usb: add usb host & device driver support for g12a
authorYue Wang <yue.wang@amlogic.com>
Thu, 1 Feb 2018 15:21:39 +0000 (23:21 +0800)
committerYixun Lan <yixun.lan@amlogic.com>
Fri, 2 Mar 2018 07:07:52 +0000 (15:07 +0800)
PD#156734: usb: add usb host & device driver support for g12a

Change-Id: Ia12b63f85fb6d980a7c7906664ae2db7c4ddb86b
Signed-off-by: Yue Wang <yue.wang@amlogic.com>
22 files changed:
MAINTAINERS
arch/arm64/boot/dts/amlogic/g12a_skt.dts
drivers/amlogic/usb/dwc_otg/310/dwc_otg_cil.c
drivers/amlogic/usb/dwc_otg/310/dwc_otg_cil.h
drivers/amlogic/usb/dwc_otg/310/dwc_otg_driver.c
drivers/amlogic/usb/dwc_otg/310/dwc_otg_driver.h
drivers/amlogic/usb/dwc_otg/310/dwc_otg_pcd_intr.c
drivers/amlogic/usb/dwc_otg/310/dwc_otg_pcd_linux.c
drivers/amlogic/usb/phy/Makefile
drivers/amlogic/usb/phy/phy-aml-new-usb-v2.c [new file with mode: 0644]
drivers/amlogic/usb/phy/phy-aml-new-usb-v2.h [new file with mode: 0644]
drivers/amlogic/usb/phy/phy-aml-new-usb2-v2.c [new file with mode: 0644]
drivers/amlogic/usb/phy/phy-aml-new-usb3-v2.c [new file with mode: 0644]
drivers/amlogic/usb/phy/usbphy.c
drivers/usb/dwc3/core.c
drivers/usb/dwc3/core.h
drivers/usb/dwc3/host.c
drivers/usb/host/xhci-hub.c
drivers/usb/host/xhci-plat.c
drivers/usb/host/xhci.h
include/linux/amlogic/usb-v2.h [new file with mode: 0644]
include/linux/amlogic/usbtype.h

index 7f0297b..7fa98f0 100644 (file)
@@ -13531,6 +13531,10 @@ F: drivers/usb/phy/phy-aml-new-usb.h
 F: drivers/usb/phy/phy-aml-new-usb.c
 F: drivers/usb/phy/phy-aml-new-usb2.c
 F: drivers/usb/phy/phy-aml-new-usb3.c
+F: drivers/usb/phy/phy-aml-new-usb-v2.h
+F: drivers/usb/phy/phy-aml-new-usb-v2.c
+F: drivers/usb/phy/phy-aml-new-usb2-v2.c
+F: drivers/usb/phy/phy-aml-new-usb3-v2.c
 F: drivers/usb/phy/phy-aml-usb.h
 F: drivers/usb/phy/phy-aml-usb.c
 F: drivers/usb/phy/phy-aml-usb2.c
@@ -13541,6 +13545,7 @@ F: include/linux/amlogic/usb-gxbbtv.h
 F: include/linux/amlogic/usb-gxl.h
 F: include/linux/amlogic/usb-gxbb.h
 F: include/linux/amlogic/usbtype.h
+F: include/linux/amlogic/usb-v2.h
 
 AMLOGIC scpi cpufreq
 M:  jianxin.pan <jianxin.pan@amlogic.com>
index 70e256c..c4106a1 100644 (file)
                pinctrl-0 = <&c_uart_pins>;
        };
 
+       dwc3: dwc3@ff500000 {
+               compatible = "synopsys, dwc3";
+               status = "okay";
+               reg = <0x0 0xff500000 0x0 0x100000>;
+               interrupts = <0 30 4>;
+               usb-phy = <&usb2_phy_v2>, <&usb3_phy_v2>;
+               cpu-type = "gxl";
+               clock-src = "usb3.0";
+               clocks = <&clkc CLKID_USB_GENERAL>;
+               clock-names = "dwc_general";
+               /*snps,super_speed_support;*/
+       };
+
+       usb2_phy_v2: usb2phy@ffe09000 {
+               compatible = "amlogic, amlogic-new-usb2-v2";
+               status = "okay";
+               portnum = <2>;
+               reg = <0x0 0xffe09000 0x0 0x80
+                               0x0 0xffd01008 0x0 0x4
+                               0x0 0xff636000 0x0 0x2000
+                               0x0 0xff63a000 0x0 0x2000>;
+       };
+
+       usb3_phy_v2: usb3phy@ffe09080 {
+               compatible = "amlogic, amlogic-new-usb3-v2";
+               status = "okay";
+               portnum = <1>;
+               reg = <0x0 0xffe09080 0x0 0x20>;
+               phy-reg = <0xff646000>;
+               phy-reg-size = <0x4>;
+               interrupts = <0 16 4>;
+               otg = <0>;
+       };
+
+       dwc2_a {
+               compatible = "amlogic, dwc2";
+               device_name = "dwc2_a";
+               reg = <0x0 0xff400000 0x0 0x40000>;
+               status = "okay";
+               interrupts = <0 31 4>;
+               pl-periph-id = <0>; /** lm name */
+               clock-src = "usb0"; /** clock src */
+               port-id = <0>;  /** ref to mach/usb.h */
+               port-type = <2>;        /** 0: otg, 1: host, 2: slave */
+               port-speed = <0>; /** 0: default, high, 1: full */
+               port-config = <0>; /** 0: default */
+               /*0:default,1:single,2:incr,3:incr4,4:incr8,5:incr16,6:disable*/
+               port-dma = <0>;
+               port-id-mode = <0>; /** 0: hardware, 1: sw_host, 2: sw_slave*/
+               usb-fifo = <728>;
+               cpu-type = "v2";
+               /** 0: normal, 1: otg+dwc3 host only, 2: otg+dwc3 device only*/
+               controller-type = <1>;
+               phy-reg = <0xffe09000>;
+               phy-reg-size = <0xa0>;
+               /** phy-interface: 0x0: amlogic phy, 0x1: synopsys phy **/
+               phy-interface = <0x0>;
+               clocks = <&clkc CLKID_USB_GENERAL
+                                       &clkc CLKID_USB1_TO_DDR>;
+               clock-names = "usb_general",
+                                       "usb1";
+       };
+
        canvas{
                compatible = "amlogic, meson, canvas";
                dev_name = "amlogic-canvas";
index 62cfdf1..126bbe5 100644 (file)
@@ -1472,6 +1472,12 @@ void dwc_otg_core_init(dwc_otg_core_if_t *core_if)
                break;
        }
 
+       /* AMLOGIC USB PHY interface */
+       if (core_if->phy_interface == 1)
+               usbcfg.b.usbtrdtim = 9;
+       else
+               usbcfg.b.usbtrdtim = 5;
+
        DWC_WRITE_REG32(&global_regs->gusbcfg, usbcfg.d32);
 
 #ifdef CONFIG_USB_DWC_OTG_LPM
index 890ec40..454dce8 100644 (file)
@@ -1057,6 +1057,8 @@ struct dwc_otg_core_if {
        uint8_t stop_adpprb;
 
        int controller_type;
+
+       uint32_t phy_interface;
 };
 
 #ifdef DEBUG
index b6dab6a..042b89f 100644 (file)
@@ -611,8 +611,12 @@ static void amlogic_device_detect_work(struct work_struct *work)
        int ret;
 
        if (USB_OTG == dwc_otg_device->core_if->controller_type) {
-               ret = device_status((unsigned long)dwc_otg_device->
-                               core_if->usb_peri_reg);
+               if (dwc_otg_device->core_if->phy_interface == 1)
+                       ret = device_status((unsigned long)dwc_otg_device->
+                                       core_if->usb_peri_reg);
+               else
+                       ret = device_status_v2((unsigned long)dwc_otg_device->
+                                       core_if->usb_peri_reg);
                if (!ret) {
                        DWC_PRINTF("usb device plug out, stop pcd!!!\n");
                        if (dwc_otg_device->pcd->core_if->pcd_cb->stop)
@@ -952,6 +956,7 @@ static int dwc_otg_driver_probe(struct platform_device *pdev)
        unsigned int p_phy_reg_addr = 0;
        unsigned int p_ctrl_reg_addr = 0;
        unsigned int phy_reg_addr_size = 0;
+       unsigned int phy_interface = 1;
        const char *s_clock_name = NULL;
        const char *cpu_type = NULL;
        const char *gpio_name = NULL;
@@ -1049,6 +1054,10 @@ static int dwc_otg_driver_probe(struct platform_device *pdev)
                        if (retval < 0)
                                return -EINVAL;
 
+                       prop = of_get_property(of_node, "phy-interface", NULL);
+                       if (prop)
+                               phy_interface = of_read_ulong(prop, 1);
+
                        dwc_otg_module_params.host_rx_fifo_size = dwc_otg_module_params.data_fifo_size / 2;
                        DWC_PRINTF("dwc_otg: %s: type: %d speed: %d, ",
                                s_clock_name, port_type, port_speed);
@@ -1143,6 +1152,7 @@ static int dwc_otg_driver_probe(struct platform_device *pdev)
 
        dwc_otg_device->core_if->usb_peri_reg = (usb_peri_reg_t *)phy_reg_addr;
        dwc_otg_device->core_if->controller_type = controller_type;
+       dwc_otg_device->core_if->phy_interface = phy_interface;
        /*
        * Attempt to ensure this device is really a DWC_otg Controller.
        * Read and verify the SNPSID register contents. The value should be
@@ -1387,8 +1397,12 @@ static int dwc_otg_driver_probe(struct platform_device *pdev)
 #endif
 
 #ifdef CONFIG_AMLOGIC_USB3PHY
-       if (USB_OTG == dwc_otg_device->core_if->controller_type)
-               aml_new_usb_init();
+       if (dwc_otg_device->core_if->controller_type == USB_OTG) {
+               if (dwc_otg_device->core_if->phy_interface == 1)
+                       aml_new_usb_init();
+               else
+                       aml_new_usb_v2_init();
+       }
 #endif
 
        return 0;
index 5c9b338..707b206 100644 (file)
@@ -46,6 +46,7 @@
 
 #ifdef CONFIG_AMLOGIC_USB3PHY
 extern void aml_new_usb_init(void);
+extern void aml_new_usb_v2_init(void);
 #endif
 
 /* Type declarations */
index 2c4f27f..d3b9808 100644 (file)
@@ -1127,6 +1127,12 @@ int32_t dwc_otg_pcd_handle_enum_done_intr(dwc_otg_pcd_t *pcd)
                /* Full or low speed */
                gusbcfg.b.usbtrdtim = 9;
        }
+
+       /* AMLOGIC USB PHY interface */
+       if (GET_CORE_IF(pcd)->phy_interface == 1)
+               gusbcfg.b.usbtrdtim = 9;
+       else
+               gusbcfg.b.usbtrdtim = 5;
        DWC_WRITE_REG32(&global_regs->gusbcfg, gusbcfg.d32);
 
        /* Clear interrupt */
index 22a6a9b..f4cb084 100644 (file)
@@ -62,6 +62,7 @@
 #include <linux/platform_device.h>
 #include <linux/usb/gadget.h>
 #include <linux/amlogic/usb-gxl.h>
+#include <linux/amlogic/usb-v2.h>
 #include <linux/of_device.h>
 
 static struct gadget_wrapper {
@@ -1293,7 +1294,10 @@ int pcd_init(struct platform_device *pdev)
        }
 
 #ifdef CONFIG_AMLOGIC_USB3PHY
-       aml_new_usb_register_notifier(&otg_dev->nb);
+       if (otg_dev->core_if->phy_interface == 1)
+               aml_new_usb_register_notifier(&otg_dev->nb);
+       else
+               aml_new_usb_v2_register_notifier(&otg_dev->nb);
        otg_dev->nb.notifier_call = dwc_usb_change;
 #endif
 
@@ -1361,7 +1365,10 @@ void pcd_remove(struct platform_device *pdev)
        free_wrapper(gadget_wrapper);
        dwc_otg_pcd_remove(otg_dev->pcd);
 #ifdef CONFIG_AMLOGIC_USB3PHY
-       aml_new_usb_unregister_notifier(&otg_dev->nb);
+       if (otg_dev->core_if->phy_interface == 1)
+               aml_new_usb_unregister_notifier(&otg_dev->nb);
+       else
+               aml_new_usb_v2_unregister_notifier(&otg_dev->nb);
 #endif
        otg_dev->pcd = 0;
 }
index da8178d..22433e1 100644 (file)
@@ -5,3 +5,6 @@ obj-$(CONFIG_AMLOGIC_USB3PHY)           += phy-aml-usb3.o
 obj-$(CONFIG_AMLOGIC_USBPHY)           += phy-aml-new-usb.o
 obj-$(CONFIG_AMLOGIC_USB2PHY)          += phy-aml-new-usb2.o
 obj-$(CONFIG_AMLOGIC_USB3PHY)          += phy-aml-new-usb3.o
+obj-$(CONFIG_AMLOGIC_USBPHY)            += phy-aml-new-usb-v2.o
+obj-$(CONFIG_AMLOGIC_USB2PHY)           += phy-aml-new-usb2-v2.o
+obj-$(CONFIG_AMLOGIC_USB3PHY)           += phy-aml-new-usb3-v2.o
diff --git a/drivers/amlogic/usb/phy/phy-aml-new-usb-v2.c b/drivers/amlogic/usb/phy/phy-aml-new-usb-v2.c
new file mode 100644 (file)
index 0000000..b16a483
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * drivers/amlogic/usb/phy/phy-aml-new-usb-v2.c
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/amlogic/usb-v2.h>
+#include <linux/amlogic/iomap.h>
+#include "phy-aml-new-usb-v2.h"
+
+int amlogic_new_usbphy_reset_v2(struct amlogic_usb_v2 *phy)
+{
+       static int      init_count;
+       int i = 0;
+
+       if (!init_count) {
+               init_count++;
+               if (!phy->reset_regs)
+                       aml_cbus_update_bits(0x1102, 0x1<<2, 0x1<<2);
+               else
+                       writel((readl(phy->reset_regs) | (0x1 << 2)),
+                               phy->reset_regs);
+               for (i = 0; i < 1000; i++)
+                       udelay(500);
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(amlogic_new_usbphy_reset_v2);
+
+int amlogic_new_usbphy_reset_phycfg_v2(struct amlogic_usb_v2 *phy, int cnt)
+{
+       int i = 0;
+
+       if (phy->reset_regs)
+               writel((readl(phy->reset_regs) | (1 << (16 + i))),
+                       phy->reset_regs);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(amlogic_new_usbphy_reset_phycfg_v2);
diff --git a/drivers/amlogic/usb/phy/phy-aml-new-usb-v2.h b/drivers/amlogic/usb/phy/phy-aml-new-usb-v2.h
new file mode 100644 (file)
index 0000000..a9c3ed1
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * drivers/amlogic/usb/phy/phy-aml-new-usb-v2.h
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#include <linux/usb/phy.h>
+#include <linux/amlogic/usb-v2.h>
+
+#define        phy_to_amlusb(x)        container_of((x), struct amlogic_usb_v2, phy)
+
+extern int amlogic_new_usbphy_reset_v2(struct amlogic_usb_v2 *phy);
+extern int amlogic_new_usbphy_reset_phycfg_v2
+       (struct amlogic_usb_v2 *phy, int cnt);
+
diff --git a/drivers/amlogic/usb/phy/phy-aml-new-usb2-v2.c b/drivers/amlogic/usb/phy/phy-aml-new-usb2-v2.c
new file mode 100644 (file)
index 0000000..77dae47
--- /dev/null
@@ -0,0 +1,268 @@
+/*
+ * drivers/amlogic/usb/phy/phy-aml-new-usb2-v2.c
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/io.h>
+#include <linux/usb/phy_companion.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/pm_runtime.h>
+#include <linux/delay.h>
+#include <linux/usb/phy.h>
+#include <linux/amlogic/usb-v2.h>
+#include "phy-aml-new-usb-v2.h"
+
+void set_usb_pll(void __iomem  *reg)
+{
+       /* TO DO set usb  PLL */
+       writel(0x39400414, reg + 0x40);
+       writel(0x927E0000, reg + 0x44);
+       writel(0xAD5B29E9, reg + 0x48);
+       udelay(100);
+       writel(0x19400414, reg + 0x40);
+}
+
+static int amlogic_new_usb2_init(struct usb_phy *x)
+{
+       int i, j, cnt;
+
+       struct amlogic_usb_v2 *phy = phy_to_amlusb(x);
+       struct u2p_aml_regs_v2 u2p_aml_regs;
+       union u2p_r0_v2 reg0;
+       union u2p_r1_v2 reg1;
+       //u32 val;
+
+       if (phy->suspend_flag) {
+               phy->suspend_flag = 0;
+               for (i = 0; i < phy->portnum; i++) {
+                       for (j = 0; j < 2; j++) {
+                               u2p_aml_regs.u2p_r_v2[j] = (void __iomem *)
+                               ((unsigned long)phy->regs + i*PHY_REGISTER_SIZE
+                                       + 4 * j);
+                       }
+               }
+               /*TO DO: set usb phy to low power mode*/
+               return 0;
+       }
+
+       amlogic_new_usbphy_reset_v2(phy);
+
+       for (i = 0; i < phy->portnum; i++) {
+               for (j = 0; j < 2; j++) {
+                       u2p_aml_regs.u2p_r_v2[j] = (void __iomem        *)
+                               ((unsigned long)phy->regs + i*PHY_REGISTER_SIZE
+                               + 4 * j);
+               }
+
+               reg0.d32 = readl(u2p_aml_regs.u2p_r_v2[0]);
+               reg0.b.POR = 1;
+               reg0.b.host_device = 1;
+               if (i == 1)
+                       reg0.b.IDPULLUP0 = 1;
+
+               writel(reg0.d32, u2p_aml_regs.u2p_r_v2[0]);
+
+               udelay(10);
+               amlogic_new_usbphy_reset_phycfg_v2(phy, i);
+               udelay(50);
+#if 0
+               /* ID DETECT: usb2_otg_aca_en set to 0 */
+               /* usb2_otg_iddet_en set to 1 */
+               writel(readl(phy->phy_cfg[i] + 0x54) & (~(1 << 2)),
+                       (phy->phy_cfg[i] + 0x54));
+               if (i == 1) {
+                       writel((readl(phy->phy_cfg[i] + 0x50) | (1 << 0)),
+                               (phy->phy_cfg[i] + 0x50));
+               }
+#endif
+               reg1.d32 = readl(u2p_aml_regs.u2p_r_v2[1]);
+               cnt = 0;
+               while (reg1.b.phy_rdy != 1) {
+                       reg1.d32 = readl(u2p_aml_regs.u2p_r_v2[1]);
+                       /*we wait phy ready max 1ms, common is 100us*/
+                       if (cnt > 200)
+                               break;
+
+                       cnt++;
+                       udelay(5);
+               }
+
+               /* step 7: pll setting */
+               set_usb_pll(phy->phy_cfg[i]);
+       }
+
+       return 0;
+}
+
+static int amlogic_new_usb2_suspend(struct usb_phy *x, int suspend)
+{
+       return 0;
+}
+
+static void amlogic_new_usb2phy_shutdown(struct usb_phy *x)
+{
+       struct amlogic_usb_v2 *phy = phy_to_amlusb(x);
+       struct u2p_aml_regs_v2 u2p_aml_regs;
+       int i, j;
+
+       phy->suspend_flag = 1;
+       for (i = phy->portnum - 1; i >= 0; i--) {
+               for (j = 0; j < 2; j++) {
+                       u2p_aml_regs.u2p_r_v2[j] = (void __iomem        *)
+                               ((unsigned long)phy->regs + i*PHY_REGISTER_SIZE
+                               + 4 * j);
+               }
+
+               /*TO DO: set usb phy to low power mode*/
+       }
+}
+
+static int amlogic_new_usb2_probe(struct platform_device *pdev)
+{
+       struct amlogic_usb_v2                   *phy;
+       struct device *dev = &pdev->dev;
+       struct resource *phy_mem;
+       struct resource *reset_mem;
+       struct resource *phy_cfg_mem[4];
+       void __iomem    *phy_base;
+       void __iomem    *reset_base = NULL;
+       void __iomem    *phy_cfg_base[4];
+       int portnum = 0;
+       const void *prop;
+       int i = 0;
+
+       prop = of_get_property(dev->of_node, "portnum", NULL);
+       if (prop)
+               portnum = of_read_ulong(prop, 1);
+
+       if (!portnum) {
+               dev_err(&pdev->dev, "This phy has no usb port\n");
+               return -ENOMEM;
+       }
+
+       phy_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       phy_base = devm_ioremap_resource(dev, phy_mem);
+       if (IS_ERR(phy_base))
+               return PTR_ERR(phy_base);
+
+       reset_mem = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+       if (reset_mem) {
+               reset_base = ioremap(reset_mem->start,
+                       resource_size(reset_mem));
+               if (IS_ERR(reset_base))
+                       return PTR_ERR(reset_base);
+       }
+
+       for (i = 0; i < portnum; i++) {
+               phy_cfg_mem[i] = platform_get_resource
+                                       (pdev, IORESOURCE_MEM, 2 + i);
+               if (phy_cfg_mem[i]) {
+                       phy_cfg_base[i] = ioremap(phy_cfg_mem[i]->start,
+                               resource_size(phy_cfg_mem[i]));
+                       if (IS_ERR(phy_cfg_base[i]))
+                               return PTR_ERR(phy_cfg_base[i]);
+               }
+       }
+
+       phy = devm_kzalloc(&pdev->dev, sizeof(*phy), GFP_KERNEL);
+       if (!phy)
+               return -ENOMEM;
+
+       dev_info(&pdev->dev, "USB2 phy probe:phy_mem:0x%lx, iomap phy_base:0x%lx\n",
+                       (unsigned long)phy_mem->start, (unsigned long)phy_base);
+
+       phy->dev                = dev;
+       phy->regs               = phy_base;
+       phy->reset_regs = reset_base;
+       phy->portnum      = portnum;
+       phy->suspend_flag = 0;
+       phy->phy.dev            = phy->dev;
+       phy->phy.label          = "amlogic-usbphy2";
+       phy->phy.init           = amlogic_new_usb2_init;
+       phy->phy.set_suspend    = amlogic_new_usb2_suspend;
+       phy->phy.shutdown       = amlogic_new_usb2phy_shutdown;
+       phy->phy.type           = USB_PHY_TYPE_USB2;
+       for (i = 0; i < portnum; i++)
+               phy->phy_cfg[i] = phy_cfg_base[i];
+
+       usb_add_phy_dev(&phy->phy);
+
+       platform_set_drvdata(pdev, phy);
+
+       pm_runtime_enable(phy->dev);
+
+       return 0;
+}
+
+static int amlogic_new_usb2_remove(struct platform_device *pdev)
+{
+       return 0;
+}
+
+#ifdef CONFIG_PM_RUNTIME
+
+static int amlogic_new_usb2_runtime_suspend(struct device *dev)
+{
+       return 0;
+}
+
+static int amlogic_new_usb2_runtime_resume(struct device *dev)
+{
+       unsigned int ret = 0;
+
+       return ret;
+}
+
+static const struct dev_pm_ops amlogic_new_usb2_pm_ops = {
+       SET_RUNTIME_PM_OPS(amlogic_new_usb2_runtime_suspend,
+               amlogic_new_usb2_runtime_resume,
+               NULL)
+};
+
+#define DEV_PM_OPS     (&amlogic_new_usb2_pm_ops)
+#else
+#define DEV_PM_OPS     NULL
+#endif
+
+#ifdef CONFIG_OF
+static const struct of_device_id amlogic_new_usb2_id_table[] = {
+       { .compatible = "amlogic, amlogic-new-usb2-v2" },
+       {}
+};
+MODULE_DEVICE_TABLE(of, amlogic_new_usb2_id_table);
+#endif
+
+static struct platform_driver amlogic_new_usb2_v2_driver = {
+       .probe          = amlogic_new_usb2_probe,
+       .remove         = amlogic_new_usb2_remove,
+       .driver         = {
+               .name   = "amlogic-new-usb2-v2",
+               .owner  = THIS_MODULE,
+               .pm     = DEV_PM_OPS,
+               .of_match_table = of_match_ptr(amlogic_new_usb2_id_table),
+       },
+};
+
+module_platform_driver(amlogic_new_usb2_v2_driver);
+
+MODULE_ALIAS("platform: amlogic_usb2_v2");
+MODULE_AUTHOR("Amlogic Inc.");
+MODULE_DESCRIPTION("amlogic USB2 v2 phy driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/amlogic/usb/phy/phy-aml-new-usb3-v2.c b/drivers/amlogic/usb/phy/phy-aml-new-usb3-v2.c
new file mode 100644 (file)
index 0000000..316f7f6
--- /dev/null
@@ -0,0 +1,420 @@
+/*
+ * drivers/amlogic/usb/phy/phy-aml-new-usb3-v2.c
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/irqreturn.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/pm_runtime.h>
+#include <linux/delay.h>
+#include <linux/usb/phy.h>
+#include <linux/amlogic/usb-v2.h>
+#include <linux/amlogic/aml_gpio_consumer.h>
+#include <linux/workqueue.h>
+#include <linux/notifier.h>
+#include "phy-aml-new-usb-v2.h"
+
+#define HOST_MODE      0
+#define DEVICE_MODE    1
+
+struct usb_aml_regs_v2 usb_new_aml_regs_v2;
+struct amlogic_usb_v2  *g_phy_v2;
+
+static void set_mode(unsigned long reg_addr, int mode);
+BLOCKING_NOTIFIER_HEAD(aml_new_usb_v2_notifier_list);
+
+int aml_new_usb_v2_register_notifier(struct notifier_block *nb)
+{
+       int ret;
+
+       ret = blocking_notifier_chain_register
+                       (&aml_new_usb_v2_notifier_list, nb);
+
+       return ret;
+}
+EXPORT_SYMBOL(aml_new_usb_v2_register_notifier);
+
+int aml_new_usb_v2_unregister_notifier(struct notifier_block *nb)
+{
+       int ret;
+
+       ret = blocking_notifier_chain_unregister
+                       (&aml_new_usb_v2_notifier_list, nb);
+
+       return ret;
+}
+EXPORT_SYMBOL(aml_new_usb_v2_unregister_notifier);
+
+static void aml_new_usb_notifier_call(unsigned long is_device_on)
+{
+       blocking_notifier_call_chain
+                       (&aml_new_usb_v2_notifier_list, is_device_on, NULL);
+}
+//EXPORT_SYMBOL(aml_new_usb_notifier_call);
+
+static void set_usb_vbus_power
+       (struct gpio_desc *usb_gd, int pin, char is_power_on)
+{
+       if (is_power_on)
+               /*set vbus on by gpio*/
+               gpiod_direction_output(usb_gd, is_power_on);
+       else
+               /*set vbus off by gpio first*/
+               gpiod_direction_output(usb_gd, is_power_on);
+}
+
+static void amlogic_new_set_vbus_power
+               (struct amlogic_usb_v2 *phy, char is_power_on)
+{
+       if (phy->vbus_power_pin != -1)
+               set_usb_vbus_power(phy->usb_gpio_desc,
+                       phy->vbus_power_pin, is_power_on);
+}
+
+static int amlogic_new_usb3_suspend(struct usb_phy *x, int suspend)
+{
+       return 0;
+}
+
+static void amlogic_new_usb3phy_shutdown(struct usb_phy *x)
+{
+       struct amlogic_usb_v2 *phy = phy_to_amlusb(x);
+
+       phy->suspend_flag = 1;
+}
+
+void aml_new_usb_v2_init(void)
+{
+       union usb_r5_v2 r5 = {.d32 = 0};
+       unsigned long reg_addr = ((unsigned long)
+               usb_new_aml_regs_v2.usb_r_v2[0] - 0x80);
+
+       r5.d32 = readl(usb_new_aml_regs_v2.usb_r_v2[5]);
+       if (r5.b.iddig_curr == 0) {
+               amlogic_new_set_vbus_power(g_phy_v2, 1);
+               aml_new_usb_notifier_call(0);
+               set_mode(reg_addr, HOST_MODE);
+       }
+}
+EXPORT_SYMBOL(aml_new_usb_v2_init);
+
+static int amlogic_new_usb3_init(struct usb_phy *x)
+{
+       struct amlogic_usb_v2 *phy = phy_to_amlusb(x);
+       union usb_r1_v2 r1 = {.d32 = 0};
+       union usb_r2_v2 r2 = {.d32 = 0};
+       union usb_r3_v2 r3 = {.d32 = 0};
+       union usb_r5_v2 r5 = {.d32 = 0};
+       int i = 0;
+
+       if (phy->suspend_flag) {
+               phy->suspend_flag = 0;
+               return 0;
+       }
+
+       /* set the phy from pcie to usb3 */
+       if (phy->portnum > 0)
+               writel((readl(phy->phy3_cfg) | (3<<5)), phy->phy3_cfg);
+
+       for (i = 0; i < 6; i++) {
+               usb_new_aml_regs_v2.usb_r_v2[i] = (void __iomem *)
+                       ((unsigned long)phy->regs + 4*i);
+       }
+
+       r1.d32 = readl(usb_new_aml_regs_v2.usb_r_v2[1]);
+       r1.b.u3h_fladj_30mhz_reg = 0x20;
+       writel(r1.d32, usb_new_aml_regs_v2.usb_r_v2[1]);
+
+       r5.d32 = readl(usb_new_aml_regs_v2.usb_r_v2[5]);
+       r5.b.iddig_en0 = 1;
+       r5.b.iddig_en1 = 1;
+       r5.b.iddig_th = 255;
+       writel(r5.d32, usb_new_aml_regs_v2.usb_r_v2[5]);
+
+       /* config usb3 phy */
+       if (phy->portnum > 0) {
+               r3.d32 = readl(usb_new_aml_regs_v2.usb_r_v2[3]);
+               r3.b.p30_ssc_en = 1;
+               r3.b.p30_ref_ssp_en = 1;
+               writel(r3.d32, usb_new_aml_regs_v2.usb_r_v2[3]);
+               udelay(2);
+               r2.d32 = readl(usb_new_aml_regs_v2.usb_r_v2[2]);
+               r2.b.p30_pcs_tx_deemph_3p5db = 0x15;
+               r2.b.p30_pcs_tx_deemph_6db = 0x20;
+               writel(r2.d32, usb_new_aml_regs_v2.usb_r_v2[2]);
+               udelay(2);
+               r1.d32 = readl(usb_new_aml_regs_v2.usb_r_v2[1]);
+               r1.b.u3h_host_port_power_control_present = 1;
+               r1.b.u3h_fladj_30mhz_reg = 32;
+               writel(r1.d32, usb_new_aml_regs_v2.usb_r_v2[1]);
+               udelay(2);
+       }
+
+       return 0;
+}
+
+static void set_mode(unsigned long reg_addr, int mode)
+{
+       struct u2p_aml_regs_v2 u2p_aml_regs;
+       struct usb_aml_regs_v2 usb_gxl_aml_regs;
+       union u2p_r0_v2 reg0;
+       union usb_r0_v2 r0 = {.d32 = 0};
+       union usb_r4_v2 r4 = {.d32 = 0};
+
+       u2p_aml_regs.u2p_r_v2[0] = (void __iomem        *)
+                               ((unsigned long)reg_addr + PHY_REGISTER_SIZE);
+
+       usb_gxl_aml_regs.usb_r_v2[0] = (void __iomem *)
+                               ((unsigned long)reg_addr + 4*PHY_REGISTER_SIZE
+                               + 4*0);
+       usb_gxl_aml_regs.usb_r_v2[1] = (void __iomem *)
+                               ((unsigned long)reg_addr + 4*PHY_REGISTER_SIZE
+                               + 4*1);
+       usb_gxl_aml_regs.usb_r_v2[4] = (void __iomem *)
+                               ((unsigned long)reg_addr + 4*PHY_REGISTER_SIZE
+                               + 4*4);
+
+       r0.d32 = readl(usb_gxl_aml_regs.usb_r_v2[0]);
+       if (mode == DEVICE_MODE) {
+               r0.b.u2d_act = 1;
+               r0.b.u2d_ss_scaledown_mode = 0;
+       } else
+               r0.b.u2d_act = 0;
+       writel(r0.d32, usb_gxl_aml_regs.usb_r_v2[0]);
+
+       r4.d32 = readl(usb_gxl_aml_regs.usb_r_v2[4]);
+       if (mode == DEVICE_MODE)
+               r4.b.p21_SLEEPM0 = 0x1;
+       else
+               r4.b.p21_SLEEPM0 = 0x0;
+       writel(r4.d32, usb_gxl_aml_regs.usb_r_v2[4]);
+
+       reg0.d32 = readl(u2p_aml_regs.u2p_r_v2[0]);
+       if (mode == DEVICE_MODE) {
+               reg0.b.host_device = 0;
+               reg0.b.POR = 0;
+       } else {
+               reg0.b.host_device = 1;
+               reg0.b.POR = 0;
+       }
+       writel(reg0.d32, u2p_aml_regs.u2p_r_v2[0]);
+
+       udelay(500);
+}
+
+static void amlogic_gxl_work(struct work_struct *work)
+{
+       struct amlogic_usb_v2 *phy =
+               container_of(work, struct amlogic_usb_v2, work.work);
+       union usb_r5_v2 r5 = {.d32 = 0};
+       unsigned long reg_addr = ((unsigned long)phy->regs - 0x80);
+
+       r5.d32 = readl(usb_new_aml_regs_v2.usb_r_v2[5]);
+       if (r5.b.iddig_curr == 0) {
+               amlogic_new_set_vbus_power(phy, 1);
+               aml_new_usb_notifier_call(0);
+               set_mode(reg_addr, HOST_MODE);
+       } else {
+               set_mode(reg_addr, DEVICE_MODE);
+               aml_new_usb_notifier_call(1);
+               amlogic_new_set_vbus_power(phy, 0);
+       }
+       r5.b.usb_iddig_irq = 0;
+       writel(r5.d32, usb_new_aml_regs_v2.usb_r_v2[5]);
+}
+
+static irqreturn_t amlogic_botg_detect_irq(int irq, void *dev)
+{
+       struct amlogic_usb_v2 *phy = (struct amlogic_usb_v2 *)dev;
+       union usb_r5_v2 r5 = {.d32 = 0};
+
+       r5.d32 = readl(usb_new_aml_regs_v2.usb_r_v2[5]);
+       r5.b.usb_iddig_irq = 0;
+       writel(r5.d32, usb_new_aml_regs_v2.usb_r_v2[5]);
+
+       schedule_delayed_work(&phy->work, msecs_to_jiffies(10));
+
+       return IRQ_HANDLED;
+}
+
+static int amlogic_new_usb3_v2_probe(struct platform_device *pdev)
+{
+       struct amlogic_usb_v2                   *phy;
+       struct device *dev = &pdev->dev;
+       struct resource *phy_mem;
+       void __iomem    *phy_base;
+       void __iomem *phy3_base;
+       unsigned int phy3_mem;
+       unsigned int phy3_mem_size = 0;
+       const char *gpio_name = NULL;
+       struct gpio_desc *usb_gd = NULL;
+       const void *prop;
+       int portnum = 0;
+       int irq;
+       int retval;
+       int gpio_vbus_power_pin = -1;
+       int otg = 0;
+
+       gpio_name = of_get_property(dev->of_node, "gpio-vbus-power", NULL);
+       if (gpio_name) {
+               gpio_vbus_power_pin = 1;
+               usb_gd = gpiod_get_index(&pdev->dev,
+                                NULL, 0, GPIOD_OUT_LOW);
+               if (IS_ERR(usb_gd))
+                       return -1;
+       }
+
+       prop = of_get_property(dev->of_node, "portnum", NULL);
+       if (prop)
+               portnum = of_read_ulong(prop, 1);
+
+       if (!portnum)
+               dev_err(&pdev->dev, "This phy has no usb port\n");
+
+       prop = of_get_property(dev->of_node, "otg", NULL);
+       if (prop)
+               otg = of_read_ulong(prop, 1);
+
+       phy_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       phy_base = devm_ioremap_resource(dev, phy_mem);
+       if (IS_ERR(phy_base))
+               return PTR_ERR(phy_base);
+
+       retval = of_property_read_u32(dev->of_node, "phy-reg", &phy3_mem);
+       if (retval < 0)
+               return -EINVAL;
+
+       retval = of_property_read_u32
+                               (dev->of_node, "phy-reg-size", &phy3_mem_size);
+       if (retval < 0)
+               return -EINVAL;
+
+       phy3_base = devm_ioremap_nocache
+                               (&(pdev->dev), (resource_size_t)phy3_mem,
+                               (unsigned long)phy3_mem_size);
+       if (!phy3_base)
+               return -ENOMEM;
+
+       phy = devm_kzalloc(&pdev->dev, sizeof(*phy), GFP_KERNEL);
+       if (!phy)
+               return -ENOMEM;
+
+       if (otg) {
+               irq = platform_get_irq(pdev, 0);
+               if (irq < 0)
+                       return -ENODEV;
+               retval = request_irq(irq, amlogic_botg_detect_irq,
+                               IRQF_SHARED | IRQ_LEVEL,
+                               "amlogic_botg_detect", phy);
+
+               if (retval) {
+                       dev_err(&pdev->dev, "request of irq%d failed\n", irq);
+                       retval = -EBUSY;
+                       return retval;
+               }
+       }
+
+       dev_info(&pdev->dev, "USB3 phy probe:phy_mem:0x%lx, iomap phy_base:0x%lx\n",
+                       (unsigned long)phy_mem->start, (unsigned long)phy_base);
+
+       phy->dev                = dev;
+       phy->regs               = phy_base;
+       phy->phy3_cfg   = phy3_base;
+       phy->portnum      = portnum;
+       phy->suspend_flag = 0;
+       phy->phy.dev            = phy->dev;
+       phy->phy.label          = "amlogic-usbphy3";
+       phy->phy.init           = amlogic_new_usb3_init;
+       phy->phy.set_suspend    = amlogic_new_usb3_suspend;
+       phy->phy.shutdown       = amlogic_new_usb3phy_shutdown;
+       phy->phy.type           = USB_PHY_TYPE_USB3;
+       phy->vbus_power_pin = gpio_vbus_power_pin;
+       phy->usb_gpio_desc = usb_gd;
+
+       INIT_DELAYED_WORK(&phy->work, amlogic_gxl_work);
+
+       usb_add_phy_dev(&phy->phy);
+
+       platform_set_drvdata(pdev, phy);
+
+       pm_runtime_enable(phy->dev);
+
+       g_phy_v2 = phy;
+
+       return 0;
+}
+
+static int amlogic_new_usb3_remove(struct platform_device *pdev)
+{
+       return 0;
+}
+
+#ifdef CONFIG_PM_RUNTIME
+
+static int amlogic_new_usb3_runtime_suspend(struct device *dev)
+{
+       return 0;
+}
+
+static int amlogic_new_usb3_runtime_resume(struct device *dev)
+{
+       u32 ret = 0;
+
+       return ret;
+}
+
+static const struct dev_pm_ops amlogic_new_usb3_pm_ops = {
+       SET_RUNTIME_PM_OPS(amlogic_new_usb3_runtime_suspend,
+               amlogic_new_usb3_runtime_resume,
+               NULL)
+};
+
+#define DEV_PM_OPS     (&amlogic_new_usb3_pm_ops)
+#else
+#define DEV_PM_OPS     NULL
+#endif
+
+#ifdef CONFIG_OF
+static const struct of_device_id amlogic_new_usb3_v2_id_table[] = {
+       { .compatible = "amlogic, amlogic-new-usb3-v2" },
+       {}
+};
+MODULE_DEVICE_TABLE(of, amlogic_new_usb3_v2_id_table);
+#endif
+
+static struct platform_driver amlogic_new_usb3_v2_driver = {
+       .probe          = amlogic_new_usb3_v2_probe,
+       .remove         = amlogic_new_usb3_remove,
+       .driver         = {
+               .name   = "amlogic-new-usb3-v2",
+               .owner  = THIS_MODULE,
+               .pm     = DEV_PM_OPS,
+               .of_match_table = of_match_ptr(amlogic_new_usb3_v2_id_table),
+       },
+};
+
+module_platform_driver(amlogic_new_usb3_v2_driver);
+
+MODULE_ALIAS("platform: amlogic_usb3_v2");
+MODULE_AUTHOR("Amlogic Inc.");
+MODULE_DESCRIPTION("amlogic USB3 v2 phy driver");
+MODULE_LICENSE("GPL v2");
index 6f14c06..a6bea85 100644 (file)
@@ -33,6 +33,7 @@
 #include <linux/platform_device.h>
 #include <linux/amlogic/usb-gxbb.h>
 #include <linux/amlogic/usb-gxbbtv.h>
+#include <linux/amlogic/usb-v2.h>
 #include <linux/amlogic/cpu_version.h>
 #include <linux/clk.h>
 #include <linux/clk-provider.h>
@@ -69,6 +70,26 @@ int device_status(unsigned long usb_peri_reg)
 }
 EXPORT_SYMBOL(device_status);
 
+/* ret 1: device plug in */
+/* ret 0: device plug out */
+int device_status_v2(unsigned long usb_peri_reg)
+{
+       struct u2p_aml_regs_v2 u2p_aml_regs;
+       union u2p_r1_v2 reg1;
+       int ret = 1;
+
+       u2p_aml_regs.u2p_r_v2[1] = (void __iomem        *)
+                               ((unsigned long)usb_peri_reg +
+                                       PHY_REGISTER_SIZE + 0x4);
+       reg1.d32 = readl(u2p_aml_regs.u2p_r_v2[1]);
+       if (!reg1.b.OTGSESSVLD0)
+               ret = 0;
+
+       return ret;
+}
+EXPORT_SYMBOL(device_status_v2);
+
+
 int clk_enable_usb_meson8(struct platform_device *pdev,
                        const char *s_clock_name, unsigned long usb_peri_reg)
 {
@@ -368,6 +389,49 @@ static void set_device_mode(struct platform_device *pdev,
        }
 }
 
+static void set_device_mode_v2(struct platform_device *pdev,
+                               unsigned long reg_addr, int controller_type)
+{
+       struct u2p_aml_regs_v2 u2p_aml_regs;
+       struct usb_aml_regs_v2 usb_aml_regs;
+       union u2p_r0_v2 reg0;
+       union usb_r0_v2 r0 = {.d32 = 0};
+       union usb_r1_v2 r1 = {.d32 = 0};
+       union usb_r4_v2 r4 = {.d32 = 0};
+
+       u2p_aml_regs.u2p_r_v2[0] = (void __iomem        *)
+                               ((unsigned long)reg_addr + PHY_REGISTER_SIZE);
+       usb_aml_regs.usb_r_v2[0] = (void __iomem *)
+                               ((unsigned long)reg_addr + 4*PHY_REGISTER_SIZE
+                               + 4*0);
+       usb_aml_regs.usb_r_v2[1] = (void __iomem *)
+                               ((unsigned long)reg_addr + 4*PHY_REGISTER_SIZE
+                               + 4*1);
+       usb_aml_regs.usb_r_v2[4] = (void __iomem *)
+                               ((unsigned long)reg_addr + 4*PHY_REGISTER_SIZE
+                               + 4*4);
+       r0.d32 = readl(usb_aml_regs.usb_r_v2[0]);
+       r0.b.u2d_act = 1;
+       r0.b.u2d_ss_scaledown_mode = 0;
+       writel(r0.d32, usb_aml_regs.usb_r_v2[0]);
+
+       r4.d32 = readl(usb_aml_regs.usb_r_v2[4]);
+       r4.b.p21_SLEEPM0 = 0x1;
+       writel(r4.d32, usb_aml_regs.usb_r_v2[4]);
+
+       if (controller_type != USB_OTG) {
+               r1.d32 = readl(usb_aml_regs.usb_r_v2[1]);
+               r1.b.u3h_host_u2_port_disable = 0x2;
+               writel(r1.d32, usb_aml_regs.usb_r_v2[1]);
+       }
+
+       reg0.d32 = readl(u2p_aml_regs.u2p_r_v2[0]);
+       reg0.b.host_device = 0;
+       reg0.b.POR = 0;
+       writel(reg0.d32, u2p_aml_regs.u2p_r_v2[0]);
+}
+
+
 int clk_enable_usb_gxbabytv(struct platform_device *pdev,
                        const char *s_clock_name, unsigned long usb_peri_reg,
                        int controller_type)
@@ -404,6 +468,23 @@ int clk_enable_usb_gxl(struct platform_device *pdev,
 }
 
 
+int clk_enable_usb_v2(struct platform_device *pdev,
+                       const char *s_clock_name, unsigned long usb_peri_reg,
+                       int controller_type)
+{
+       struct clk *usb_reset;
+
+       usb_reset = devm_clk_get(&pdev->dev, "usb_general");
+       clk_prepare_enable(usb_reset);
+       p_clk_reset[pdev->id].usb_reset_usb_general = usb_reset;
+       usb_reset = devm_clk_get(&pdev->dev, "usb1");
+       clk_prepare_enable(usb_reset);
+       p_clk_reset[pdev->id].usb_reset_usb_to_ddr = usb_reset;
+       set_device_mode_v2(pdev, usb_peri_reg, controller_type);
+       return 0;
+}
+
+
 void clk_disable_usb_gxbabytv(struct platform_device *pdev,
                                const char *s_clock_name,
                                unsigned long usb_peri_reg)
@@ -432,6 +513,19 @@ void clk_disable_usb_gxl(struct platform_device *pdev,
        return;
 }
 
+void clk_disable_usb_v2(struct platform_device *pdev,
+                               const char *s_clock_name,
+                               unsigned long usb_peri_reg)
+{
+       struct clk *usb_reset;
+
+       usb_reset = p_clk_reset[pdev->id].usb_reset_usb_general;
+       clk_disable_unprepare(usb_reset);
+       usb_reset = p_clk_reset[pdev->id].usb_reset_usb_to_ddr;
+       clk_disable_unprepare(usb_reset);
+}
+
+
 int clk_resume_usb_gxbaby(struct platform_device *pdev,
                        const char *s_clock_name,
                        unsigned long usb_peri_reg)
@@ -489,6 +583,33 @@ int clk_resume_usb_gxl(struct platform_device *pdev,
 }
 
 
+int clk_resume_usb_v2(struct platform_device *pdev,
+                       const char *s_clock_name,
+                       unsigned long usb_peri_reg)
+{
+       struct clk *usb_reset;
+
+       if (pdev->id == 0) {
+               usb_reset = p_clk_reset[pdev->id].usb_reset_usb_general;
+               clk_prepare_enable(usb_reset);
+               usb_reset = p_clk_reset[pdev->id].usb_reset_usb_to_ddr;
+               clk_prepare_enable(usb_reset);
+       } else if (pdev->id == 1) {
+               usb_reset = p_clk_reset[pdev->id].usb_reset_usb_general;
+               clk_prepare_enable(usb_reset);
+               usb_reset = p_clk_reset[pdev->id].usb_reset_usb_to_ddr;
+               clk_prepare_enable(usb_reset);
+       } else {
+               dev_err(&pdev->dev, "bad usb clk name.\n");
+               return -1;
+       }
+
+       dmb(4);
+
+       return 0;
+}
+
+
 int clk_enable_usb(struct platform_device *pdev, const char *s_clock_name,
                unsigned long usb_peri_reg, const char *cpu_type,
                int controller_type)
@@ -510,6 +631,9 @@ int clk_enable_usb(struct platform_device *pdev, const char *s_clock_name,
        else if (!strcmp(cpu_type, GXL))
                ret = clk_enable_usb_gxl(pdev,
                                s_clock_name, usb_peri_reg, controller_type);
+       else if (!strcmp(cpu_type, V2))
+               ret = clk_enable_usb_v2(pdev,
+                               s_clock_name, usb_peri_reg, controller_type);
 
        /*add other cpu type's usb clock enable*/
 
@@ -536,6 +660,9 @@ int clk_disable_usb(struct platform_device *pdev, const char *s_clock_name,
        else if (!strcmp(cpu_type, GXL))
                        clk_disable_usb_gxl(pdev,
                                s_clock_name, usb_peri_reg);
+       else if (!strcmp(cpu_type, V2))
+               clk_disable_usb_v2(pdev,
+                       s_clock_name, usb_peri_reg);
 
        dmb(4);
        return 0;
@@ -560,6 +687,9 @@ int clk_resume_usb(struct platform_device *pdev, const char *s_clock_name,
        else if (!strcmp(cpu_type, GXL))
                ret = clk_resume_usb_gxl(pdev,
                        s_clock_name, usb_peri_reg);
+       else if (!strcmp(cpu_type, V2))
+               ret = clk_resume_usb_v2(pdev,
+                       s_clock_name, usb_peri_reg);
 
        /*add other cpu type's usb clock enable*/
 
@@ -583,6 +713,9 @@ int clk_suspend_usb(struct platform_device *pdev, const char *s_clock_name,
        else if (!strcmp(cpu_type, GXL))
                        clk_disable_usb_gxl(pdev,
                                s_clock_name, usb_peri_reg);
+       else if (!strcmp(cpu_type, V2))
+               clk_disable_usb_v2(pdev,
+                       s_clock_name, usb_peri_reg);
 
        dmb(4);
        return 0;
index 8a9463c..a2926a6 100644 (file)
@@ -1141,7 +1141,10 @@ static int dwc3_probe(struct platform_device *pdev)
                                    &dwc->hsphy_interface);
        device_property_read_u32(dev, "snps,quirk-frame-length-adjustment",
                                 &dwc->fladj);
-
+#ifdef CONFIG_AMLOGIC_USB
+       dwc->super_speed_support = device_property_read_bool(dev,
+                               "snps,super_speed_support");
+#endif
        dwc->lpm_nyet_threshold = lpm_nyet_threshold;
        dwc->tx_de_emphasis = tx_de_emphasis;
 
index 4f2638f..bc851ca 100644 (file)
@@ -982,6 +982,7 @@ struct dwc3 {
        unsigned                tx_de_emphasis_quirk:1;
        unsigned                tx_de_emphasis:2;
 #ifdef CONFIG_AMLOGIC_USB
+       unsigned                super_speed_support:1;
        struct clk              *general_clk;
 #endif
 };
index 626d87d..2ea098c 100644 (file)
@@ -113,6 +113,11 @@ int dwc3_host_init(struct dwc3 *dwc)
                }
        }
 
+#ifdef CONFIG_AMLOGIC_USB
+       if (dwc->super_speed_support)
+               props[prop_idx++].name = "usb3-support";
+#endif
+
        phy_create_lookup(dwc->usb2_generic_phy, "usb2-phy",
                          dev_name(&xhci->dev));
        phy_create_lookup(dwc->usb3_generic_phy, "usb3-phy",
index 0722f75..6b7c7da 100644 (file)
@@ -255,6 +255,7 @@ static void xhci_usb3_hub_descriptor(struct usb_hcd *hcd, struct xhci_hcd *xhci,
        unsigned int i;
 
        ports = xhci->num_usb3_ports;
+
        xhci_common_hub_descriptor(xhci, desc, ports);
        desc->bDescriptorType = USB_DT_SS_HUB;
        desc->bDescLength = USB_DT_SS_HUB_SIZE;
@@ -279,12 +280,22 @@ static void xhci_usb3_hub_descriptor(struct usb_hcd *hcd, struct xhci_hcd *xhci,
 static void xhci_hub_descriptor(struct usb_hcd *hcd, struct xhci_hcd *xhci,
                struct usb_hub_descriptor *desc)
 {
-
+#ifdef CONFIG_AMLOGIC_USB
+       if (xhci->quirks & XHCI_AML_SUPER_SPEED_SUPPORT) {
+               if (hcd->speed >= HCD_USB3)
+                       xhci_usb3_hub_descriptor(hcd, xhci, desc);
+               else
+                       xhci_usb2_hub_descriptor(hcd, xhci, desc);
+       } else {
+               if (hcd->speed < HCD_USB3)
+                       xhci_usb2_hub_descriptor(hcd, xhci, desc);
+       }
+#else
        if (hcd->speed >= HCD_USB3)
                xhci_usb3_hub_descriptor(hcd, xhci, desc);
        else
                xhci_usb2_hub_descriptor(hcd, xhci, desc);
-
+#endif
 }
 
 static unsigned int xhci_port_speed(unsigned int port_status)
index 53a7037..c2722b5 100644 (file)
@@ -237,6 +237,11 @@ static int xhci_plat_probe(struct platform_device *pdev)
        if (device_property_read_bool(&pdev->dev, "quirk-broken-port-ped"))
                xhci->quirks |= XHCI_BROKEN_PORT_PED;
 
+#ifdef CONFIG_AMLOGIC_USB
+       if (device_property_read_bool(&pdev->dev, "usb3-support"))
+               xhci->quirks |= XHCI_AML_SUPER_SPEED_SUPPORT;
+#endif
+
        hcd->usb_phy = devm_usb_get_phy_by_phandle(&pdev->dev, "usb-phy", 0);
        if (IS_ERR(hcd->usb_phy)) {
                ret = PTR_ERR(hcd->usb_phy);
index 836398a..98e1616 100644 (file)
@@ -1662,7 +1662,9 @@ struct xhci_hcd {
 #define XHCI_LIMIT_ENDPOINT_INTERVAL_7 (1 << 26)
 /* Reserved. It was XHCI_U2_DISABLE_WAKE */
 #define XHCI_ASMEDIA_MODIFY_FLOWCONTROL        (1 << 28)
-
+#ifdef CONFIG_AMLOGIC_USB
+#define XHCI_AML_SUPER_SPEED_SUPPORT (1 << 29)
+#endif
        unsigned int            num_active_eps;
        unsigned int            limit_active_eps;
        /* There are two roothubs to keep track of bus suspend info for */
diff --git a/include/linux/amlogic/usb-v2.h b/include/linux/amlogic/usb-v2.h
new file mode 100644 (file)
index 0000000..464fd2e
--- /dev/null
@@ -0,0 +1,176 @@
+/*
+ * include/linux/amlogic/usb-v2.h
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#ifndef __USB_V2_HEADER_
+#define __USB_V2_HEADER_
+
+#include <linux/usb/phy.h>
+#include <linux/platform_device.h>
+#include <linux/amlogic/aml_gpio_consumer.h>
+#include <linux/workqueue.h>
+#include <linux/notifier.h>
+
+#define PHY_REGISTER_SIZE      0x20
+/* Register definitions */
+
+int aml_new_usb_v2_register_notifier(struct notifier_block *nb);
+int aml_new_usb_v2_unregister_notifier(struct notifier_block *nb);
+
+struct u2p_aml_regs_v2 {
+       void __iomem    *u2p_r_v2[2];
+};
+
+union u2p_r0_v2 {
+       /** raw register data */
+       uint32_t d32;
+       /** register bits */
+       struct {
+               unsigned host_device:1;
+               unsigned power_ok:1;
+               unsigned hast_mode:1;
+               unsigned POR:1;
+               unsigned IDPULLUP0:1;
+               unsigned DRVVBUS0:1;
+               unsigned reserved:26;
+       } b;
+};
+
+union u2p_r1_v2 {
+       /** raw register data */
+       uint32_t d32;
+       /** register bits */
+       struct {
+               unsigned phy_rdy:1;
+               unsigned IDDIG0:1;
+               unsigned OTGSESSVLD0:1;
+               unsigned VBUSVALID0:1;
+               unsigned reserved:28;
+       } b;
+};
+
+struct usb_aml_regs_v2 {
+       void __iomem    *usb_r_v2[6];
+};
+
+union usb_r0_v2 {
+       /** raw register data */
+       uint32_t d32;
+       /** register bits */
+       struct {
+               unsigned reserved:17;
+               unsigned p30_lane0_tx2rx_loopback:1;
+               unsigned p30_lane0_ext_pclk_reg:1;
+               unsigned p30_pcs_rx_los_mask_val:10;
+               unsigned u2d_ss_scaledown_mode:2;
+               unsigned u2d_act:1;
+       } b;
+};
+
+union usb_r1_v2 {
+       /** raw register data */
+       uint32_t d32;
+       /** register bits */
+       struct {
+               unsigned u3h_bigendian_gs:1;
+               unsigned u3h_pme_en:1;
+               unsigned u3h_hub_port_overcurrent:3;
+               unsigned reserved_1:2;
+               unsigned u3h_hub_port_perm_attach:3;
+               unsigned reserved_2:2;
+               unsigned u3h_host_u2_port_disable:2;
+               unsigned reserved_3:2;
+               unsigned u3h_host_u3_port_disable:1;
+               unsigned u3h_host_port_power_control_present:1;
+               unsigned u3h_host_msi_enable:1;
+               unsigned u3h_fladj_30mhz_reg:6;
+               unsigned p30_pcs_tx_swing_full:7;
+       } b;
+};
+
+union usb_r2_v2 {
+       /** raw register data */
+       uint32_t d32;
+       /** register bits */
+       struct {
+               unsigned reserved:20;
+               unsigned p30_pcs_tx_deemph_3p5db:6;
+               unsigned p30_pcs_tx_deemph_6db:6;
+       } b;
+};
+
+union usb_r3_v2 {
+       /** raw register data */
+       uint32_t d32;
+       /** register bits */
+       struct {
+               unsigned p30_ssc_en:1;
+               unsigned p30_ssc_range:3;
+               unsigned p30_ssc_ref_clk_sel:9;
+               unsigned p30_ref_ssp_en:1;
+               unsigned reserved:18;
+       } b;
+};
+
+union usb_r4_v2 {
+       /** raw register data */
+       uint32_t d32;
+       /** register bits */
+       struct {
+               unsigned p21_PORTRESET0:1;
+               unsigned p21_SLEEPM0:1;
+               unsigned mem_pd:2;
+               unsigned p21_only:1;
+               unsigned reserved:27;
+       } b;
+};
+
+union usb_r5_v2 {
+       /** raw register data */
+       uint32_t d32;
+       /** register bits */
+       struct {
+               unsigned iddig_sync:1;
+               unsigned iddig_reg:1;
+               unsigned iddig_cfg:2;
+               unsigned iddig_en0:1;
+               unsigned iddig_en1:1;
+               unsigned iddig_curr:1;
+               unsigned usb_iddig_irq:1;
+               unsigned iddig_th:8;
+               unsigned iddig_cnt:8;
+               unsigned reserved:8;
+       } b;
+};
+
+struct amlogic_usb_v2 {
+       struct usb_phy          phy;
+       struct device           *dev;
+       void __iomem    *regs;
+       void __iomem    *reset_regs;
+       void __iomem    *phy_cfg[4];
+       void __iomem    *phy3_cfg;
+       /* Set VBus Power though GPIO */
+       int vbus_power_pin;
+       int vbus_power_pin_work_mask;
+       struct delayed_work     work;
+       struct gpio_desc *usb_gpio_desc;
+
+       int portnum;
+       int suspend_flag;
+};
+
+#endif
index edb2f20..0f5db62 100644 (file)
@@ -25,6 +25,7 @@
 #define GXBABY         "gxbaby"
 #define GXBABYTV       "gxtvbaby"
 #define GXL            "gxl"
+#define V2             "v2"
 
 #define USB_NORMAL     0
 #define USB_HOST_ONLY  1
@@ -92,6 +93,7 @@ int clk_suspend_usb(struct platform_device *pdev, const char *s_clock_name,
                unsigned long usb_peri_reg, const char *cpu_type);
 
 int device_status(unsigned long usb_peri_reg);
+int device_status_v2(unsigned long usb_peri_reg);
 
 extern int dwc_otg_power_register_notifier(struct notifier_block *nb);
 extern int dwc_otg_power_unregister_notifier(struct notifier_block *nb);