3036: usb: add usb support
authorlyz <lyz@rock-chips.com>
Fri, 4 Jul 2014 03:56:19 +0000 (11:56 +0800)
committerlyz <lyz@rock-chips.com>
Fri, 4 Jul 2014 03:56:32 +0000 (11:56 +0800)
arch/arm/boot/dts/rk3036.dtsi [changed mode: 0644->0755]
arch/arm/mach-rockchip/rk3036.c [changed mode: 0644->0755]
drivers/usb/dwc_otg_310/Makefile
drivers/usb/dwc_otg_310/dwc_otg_driver.c
drivers/usb/dwc_otg_310/usbdev_grf_regs.h [changed mode: 0644->0755]
drivers/usb/dwc_otg_310/usbdev_rk.h
drivers/usb/dwc_otg_310/usbdev_rk3036.c [new file with mode: 0755]

old mode 100644 (file)
new mode 100755 (executable)
index a971bb2..836becc
                                                  "Mali_PP0_IRQ",
                                                  "Mali_PP0_MMU_IRQ";
          };
+       dwc_control_usb: dwc-control-usb@20008000 {
+               compatible = "rockchip,rk3188-dwc-control-usb";
+               interrupts = <GIC_SPI 6 IRQ_TYPE_LEVEL_HIGH>;
+               interrupt-names = "otg_bvalid";
+               //gpios = <&gpio0 GPIO_C0 GPIO_ACTIVE_LOW>, <&gpio3 GPIO_D5 GPIO_ACTIVE_LOW>;
+               clocks = <&clk_gates9 13>;
+               clock-names = "hclk_usb_peri";
+               rockchip,remote_wakeup;
+               rockchip,usb_irq_wakeup;
+
+               usb_bc{
+                       compatible = "rockchip,ctrl";
+                       rk_usb,bvalid   = <0x14c 8 1>;
+                       rk_usb,iddig    = <0x14c 11 1>;
+                       rk_usb,line     = <0x14c 9 2>;
+                       rk_usb,softctrl = <0x17c 0 1>;
+                       rk_usb,opmode   = <0x17c 2 2>;
+                       rk_usb,xcvrsel  = <0x17c 4 2>;
+                       rk_usb,termsel  = <0x118 6 1>; 
+               };
+       };
+       usb0: usb@10180000 {
+               compatible = "rockchip,rk3188_usb20_otg";
+               reg = <0x10180000 0x40000>;
+               interrupts = <GIC_SPI 10 IRQ_TYPE_LEVEL_HIGH>;
+               clocks = <&clk_gates1 5>, <&clk_gates5 13>;
+               clock-names = "clk_usbphy0", "hclk_usb0";
+               /*0 - Normal, 1 - Force Host, 2 - Force Device*/
+               rockchip,usb-mode = <0>;
+       };
+
+       usb1: usb@101c0000 {
+               compatible = "rockchip,rk3188_usb20_host";
+               reg = <0x101c0000 0x40000>;
+               interrupts = <GIC_SPI 11 IRQ_TYPE_LEVEL_HIGH>;
+               clocks = <&clk_gates1 6>, <&clk_gates7 3>;
+               clock-names = "clk_usbphy1", "hclk_usb1";
+       };
 
 };
old mode 100644 (file)
new mode 100755 (executable)
index 3dcd886..bba0701
@@ -86,7 +86,24 @@ static void __init rk3036_boot_mode_init(void)
 
 static void usb_uart_init(void)
 {
-    return;
+       u32 soc_status0 = readl_relaxed(RK_GRF_VIRT + RK3036_GRF_SOC_STATUS0);
+       writel_relaxed(0x34000000, RK_GRF_VIRT + RK3036_GRF_UOC1_CON4);
+#ifdef CONFIG_RK_USB_UART
+       if (!(soc_status0 & (1 << 14)) && (soc_status0 & (1 << 17))) {
+               /* software control usb phy enable */
+               writel_relaxed(0x007f0055, RK_GRF_VIRT + RK3036_GRF_UOC0_CON5);
+               writel_relaxed(0x34003000, RK_GRF_VIRT + RK3036_GRF_UOC1_CON4);
+       }
+#endif
+#ifdef RK_DEBUG_UART_VIRT
+    writel_relaxed(0x07, RK_DEBUG_UART_VIRT + 0x88);
+    writel_relaxed(0x07, RK_DEBUG_UART_VIRT + 0x88);
+    writel_relaxed(0x00, RK_DEBUG_UART_VIRT + 0x04);
+    writel_relaxed(0x83, RK_DEBUG_UART_VIRT + 0x0c);
+    writel_relaxed(0x0d, RK_DEBUG_UART_VIRT + 0x00);
+    writel_relaxed(0x00, RK_DEBUG_UART_VIRT + 0x04);
+    writel_relaxed(0x03, RK_DEBUG_UART_VIRT + 0x0c);
+#endif //end of DEBUG_UART_BASE
 }
 
 static void __init rk3036_dt_map_io(void)
index 9af238ce165b0d2a38095827d0017e15e133fbf9..f5dd7ae364c9ede0abe72b3fd46b63419da90eee 100755 (executable)
@@ -25,5 +25,5 @@ endif
 dwc_otg-objs   += common_port/dwc_common_linux.o
 
 #objs relative to RK platform
-dwc_otg-objs   += usbdev_rk30.o usbdev_rk32.o usbdev_bc.o
+dwc_otg-objs   += usbdev_rk30.o usbdev_rk32.o usbdev_rk3036.o usbdev_bc.o
 obj-$(CONFIG_DWC_OTG_310) := dwc_otg.o
index 74deafef62f1f6843076b9fa65daa1d5a420284a..5b9ceb1f94d91e344230df3d48cdfad304e9913a 100755 (executable)
@@ -86,6 +86,10 @@ static struct usb20otg_pdata_id usb20otg_pdata[] = {
         .name = "rk3288-usb20otg",
         .pdata = &usb20otg_pdata_rk3288,
         },
+       {
+        .name = "rk3036-usb20otg",
+        .pdata = &usb20otg_pdata_rk3036,
+        },
        {},
 };
 #endif
@@ -100,6 +104,10 @@ static struct usb20host_pdata_id usb20host_pdata[] = {
         .name = "rk3288-usb20host",
         .pdata = &usb20host_pdata_rk3288,
         },
+       {
+        .name = "rk3288-usb20host",
+        .pdata = &usb20host_pdata_rk3036,
+        },
        {},
 };
 #endif
@@ -972,6 +980,10 @@ static const struct of_device_id usb20_host_of_match[] = {
         .compatible = "rockchip,rk3288_usb20_host",
         .data = &usb20host_pdata[RK3288_USB_CTLR],
         },
+       {
+        .compatible = "rockchip,rk3036_usb20_host",
+        .data = &usb20host_pdata[RK3036_USB_CTLR],
+        },
        {},
 };
 
@@ -1326,6 +1338,10 @@ static const struct of_device_id usb20_otg_of_match[] = {
         .compatible = "rockchip,rk3288_usb20_otg",
         .data = &usb20otg_pdata[RK3288_USB_CTLR],
         },
+       {
+        .compatible = "rockchip,rk3036_usb20_otg",
+        .data = &usb20otg_pdata[RK3036_USB_CTLR],
+        },
        {
         },
 };
old mode 100644 (file)
new mode 100755 (executable)
index 319581b..8b4a727
@@ -8,6 +8,7 @@ typedef volatile struct tag_grf_uoc0_reg {
        u32 CON2;
        u32 CON3;
        u32 CON4;
+       u32 CON5;
 } GRF_UOC0_REG, *pGRF_UOC0_REG;
 
 typedef volatile struct tag_grf_uoc1_reg {
@@ -20,6 +21,7 @@ typedef volatile struct tag_grf_uoc1_reg {
        u32 CON2;
        u32 CON3;
        u32 CON4;
+       u32 CON5;
 } GRF_UOC1_REG, *pGRF_UOC1_REG;
 
 typedef volatile struct tag_grf_uoc2_reg {
index f79adac1563f15b9af8ccec7c6307c03e5d8ff0f..8f4389e10a856b71fc4627e1b768b550cf2c9337 100755 (executable)
@@ -21,6 +21,7 @@
 #include <linux/rockchip/cru.h>
 #include <linux/rockchip/grf.h>
 #include <linux/rockchip/cpu.h>
+#include <linux/rockchip/iomap.h>
 
 #include "usbdev_grf_regs.h"
 #include "usbdev_bc.h"
@@ -40,6 +41,9 @@
 #define USB_REMOTE_WAKEUP     (6)
 #define USB_IRQ_WAKEUP        (7)
 
+#define UOC_HIWORD_UPDATE(val, mask, shift) \
+               ((val) << (shift) | (mask) << ((shift) + 16))
+
 extern int rk_usb_charger_status;
 extern void rk_send_wakeup_key(void);
 /* rk3188 platform data */
@@ -52,6 +56,9 @@ extern struct dwc_otg_platform_data usb20host_pdata_rk3288;
 extern struct rkehci_platform_data rkhsic_pdata_rk3288;
 extern struct rkehci_platform_data rkehci_pdata_rk3288;
 extern struct rkehci_platform_data rkohci_pdata_rk3288;
+/* rk3036 platform data */
+extern struct dwc_otg_platform_data usb20otg_pdata_rk3036;
+extern struct dwc_otg_platform_data usb20host_pdata_rk3036;
 
 struct dwc_otg_platform_data {
        void *privdata;
@@ -61,15 +68,16 @@ struct dwc_otg_platform_data {
        struct clk *busclk;
        struct clk *phyclk_480m;
        int phy_status;
-       void (*hw_init) (void);
-       void (*phy_suspend) (void *pdata, int suspend);
-       void (*soft_reset) (void);
-       void (*clock_init) (void *pdata);
-       void (*clock_enable) (void *pdata, int enable);
-       void (*power_enable) (int enable);
-       void (*dwc_otg_uart_mode) (void *pdata, int enter_usb_uart_mode);
-       void (*bc_detect_cb) (int bc_type);
-       int (*get_status) (int id);
+
+       void (*hw_init)(void);
+       void (*phy_suspend)(void *pdata, int suspend);
+       void (*soft_reset)(void);
+       void (*clock_init)(void *pdata);
+       void (*clock_enable)(void *pdata, int enable);
+       void (*power_enable)(int enable);
+       void (*dwc_otg_uart_mode)(void *pdata, int enter_usb_uart_mode);
+       void (*bc_detect_cb)(int bc_type);
+       int (*get_status)(int id);
 };
 
 struct rkehci_platform_data {
@@ -79,12 +87,13 @@ struct rkehci_platform_data {
        struct clk *hsic_phy_12m;
        struct clk *phyclk;
        struct clk *ahbclk;
-       void (*hw_init) (void);
-       void (*clock_init) (void *pdata);
-       void (*clock_enable) (void *pdata, int enable);
-       void (*phy_suspend) (void *pdata, int suspend);
-       void (*soft_reset) (void);
-       int (*get_status) (int id);
+
+       void (*hw_init)(void);
+       void (*clock_init)(void *pdata);
+       void (*clock_enable)(void *pdata, int enable);
+       void (*phy_suspend)(void *pdata, int suspend);
+       void (*soft_reset)(void);
+       int (*get_status)(int id);
        int clk_status;
        int phy_status;
 };
@@ -100,6 +109,7 @@ struct dwc_otg_control_usb {
        pGRF_SOC_STATUS2_RK3288 grf_soc_status2_rk3288;
        pGRF_SOC_STATUS19_RK3288 grf_soc_status19_rk3288;
        pGRF_SOC_STATUS21_RK3288 grf_soc_status21_rk3288;
+
        struct gpio *host_gpios;
        struct gpio *otg_gpios;
        struct clk *hclk_usb_peri;
@@ -114,6 +124,7 @@ struct dwc_otg_control_usb {
 enum {
        RK3188_USB_CTLR = 0,    /* rk3188 chip usb */
        RK3288_USB_CTLR,        /* rk3288 chip usb */
+       RK3036_USB_CTLR,        /* rk3036 chip usb */
 };
 
 struct usb20otg_pdata_id {
diff --git a/drivers/usb/dwc_otg_310/usbdev_rk3036.c b/drivers/usb/dwc_otg_310/usbdev_rk3036.c
new file mode 100755 (executable)
index 0000000..d94e8d2
--- /dev/null
@@ -0,0 +1,598 @@
+#include "usbdev_rk.h"
+#include "usbdev_grf_regs.h"
+#include "dwc_otg_regs.h"
+static struct dwc_otg_control_usb *control_usb;
+
+#ifdef CONFIG_USB20_OTG
+static void usb20otg_hw_init(void)
+{
+       /* other haredware init,include:
+        * DRV_VBUS GPIO init */
+       if (gpio_is_valid(control_usb->otg_gpios->gpio)) {
+               if (gpio_get_value(control_usb->otg_gpios->gpio))
+                       gpio_set_value(control_usb->otg_gpios->gpio, 0);
+       }
+}
+
+static void usb20otg_phy_suspend(void *pdata, int suspend)
+{
+       struct dwc_otg_platform_data *usbpdata = pdata;
+
+       if (suspend) {
+               /* enable soft control */
+               writel(UOC_HIWORD_UPDATE(0x55, 0x7f, 0),
+                      RK_GRF_VIRT + RK3036_GRF_UOC0_CON5);
+               usbpdata->phy_status = 1;
+       } else {
+               /* exit suspend */
+               writel(UOC_HIWORD_UPDATE(0x0, 0x1, 0),
+                      RK_GRF_VIRT + RK3036_GRF_UOC0_CON5);
+               usbpdata->phy_status = 0;
+       }
+}
+
+static void usb20otg_soft_reset(void)
+{
+       writel(UOC_HIWORD_UPDATE(0x1, 0x3, 0),
+              RK_GRF_VIRT + RK3036_GRF_UOC0_CON5);
+       writel(UOC_HIWORD_UPDATE(0x1, 0x3, 0),
+              RK_GRF_VIRT + RK3036_GRF_UOC1_CON5);
+       /* cru_set_soft_reset(SOFT_RST_USBPOR, true); */
+       writel(UOC_HIWORD_UPDATE(0x1, 0x1, 9),
+              RK_CRU_VIRT + RK3036_CRU_SOFTRST6_CON);
+       /* cru_set_soft_reset(SOFT_RST_UTMI0, true); */
+       writel(UOC_HIWORD_UPDATE(0x1, 0x1, 7),
+              RK_CRU_VIRT + RK3036_CRU_SOFTRST6_CON);
+       /* cru_set_soft_reset(SOFT_RST_UTMI1, true); */
+       writel(UOC_HIWORD_UPDATE(0x1, 0x1, 8),
+              RK_CRU_VIRT + RK3036_CRU_SOFTRST6_CON);
+
+       udelay(15);
+
+       writel(UOC_HIWORD_UPDATE(0x2, 0x3, 0),
+              RK_GRF_VIRT + RK3036_GRF_UOC0_CON5);
+       writel(UOC_HIWORD_UPDATE(0x2, 0x3, 0),
+              RK_GRF_VIRT + RK3036_GRF_UOC1_CON5);
+
+       udelay(1500);
+       /* cru_set_soft_reset(SOFT_RST_USBPOR, false); */
+       writel(UOC_HIWORD_UPDATE(0x0, 0x1, 9),
+              RK_CRU_VIRT + RK3036_CRU_SOFTRST6_CON);
+       udelay(2);
+       /* cru_set_soft_reset(SOFT_RST_UTMI0, false); */
+       writel(UOC_HIWORD_UPDATE(0x0, 0x1, 7),
+              RK_CRU_VIRT + RK3036_CRU_SOFTRST6_CON);
+       /* cru_set_soft_reset(SOFT_RST_UTMI1, false); */
+       writel(UOC_HIWORD_UPDATE(0x0, 0x1, 8),
+              RK_CRU_VIRT + RK3036_CRU_SOFTRST6_CON);
+
+       /* ctrler reset */
+       /* cru_set_soft_reset(SOFT_RST_OTGC0, true); */
+       writel(UOC_HIWORD_UPDATE(0x1, 0x1, 7),
+              RK_CRU_VIRT + RK3036_CRU_SOFTRST4_CON);
+       /* cru_set_soft_reset(SOFT_RST_OTGC1, true); */
+       writel(UOC_HIWORD_UPDATE(0x1, 0x1, 10),
+              RK_CRU_VIRT + RK3036_CRU_SOFTRST4_CON);
+       udelay(2);
+
+       /* cru_set_soft_reset(SOFT_RST_USBOTG0, true); */
+       writel(UOC_HIWORD_UPDATE(0x1, 0x1, 5),
+              RK_CRU_VIRT + RK3036_CRU_SOFTRST4_CON);
+       /* cru_set_soft_reset(SOFT_RST_USBOTG1, true); */
+       writel(UOC_HIWORD_UPDATE(0x1, 0x1, 8),
+              RK_CRU_VIRT + RK3036_CRU_SOFTRST4_CON);
+       udelay(2);
+       /*
+          cru_set_soft_reset(SOFT_RST_OTGC0,false);
+          cru_set_soft_reset(SOFT_RST_OTGC1,false);
+          ru_set_soft_reset(SOFT_RST_USBOTG0,false);
+          cru_set_soft_reset(SOFT_RST_USBOTG1,false);
+        */
+       writel(UOC_HIWORD_UPDATE(0x0, 0x1, 7),
+              RK_CRU_VIRT + RK3036_CRU_SOFTRST4_CON);
+       writel(UOC_HIWORD_UPDATE(0x0, 0x1, 10),
+              RK_CRU_VIRT + RK3036_CRU_SOFTRST4_CON);
+       writel(UOC_HIWORD_UPDATE(0x0, 0x1, 5),
+              RK_CRU_VIRT + RK3036_CRU_SOFTRST4_CON);
+       writel(UOC_HIWORD_UPDATE(0x0, 0x1, 8),
+              RK_CRU_VIRT + RK3036_CRU_SOFTRST4_CON);
+}
+
+static void usb20otg_clock_init(void *pdata)
+{
+       struct dwc_otg_platform_data *usbpdata = pdata;
+       struct clk *ahbclk, *phyclk;
+
+       ahbclk = devm_clk_get(usbpdata->dev, "hclk_usb0");
+       if (IS_ERR(ahbclk)) {
+               dev_err(usbpdata->dev, "Failed to get hclk_usb0\n");
+               return;
+       }
+
+       phyclk = devm_clk_get(usbpdata->dev, "clk_usbphy0");
+       if (IS_ERR(phyclk)) {
+               dev_err(usbpdata->dev, "Failed to get clk_usbphy0\n");
+               return;
+       }
+
+       usbpdata->phyclk = phyclk;
+       usbpdata->ahbclk = ahbclk;
+}
+
+static void usb20otg_clock_enable(void *pdata, int enable)
+{
+       struct dwc_otg_platform_data *usbpdata = pdata;
+
+       if (enable) {
+               clk_prepare_enable(usbpdata->ahbclk);
+               clk_prepare_enable(usbpdata->phyclk);
+       } else {
+               clk_disable_unprepare(usbpdata->ahbclk);
+               /*
+                  clk_disable_unprepare(usbpdata->phyclk);
+                */
+       }
+}
+
+static int usb20otg_get_status(int id)
+{
+       int ret = -1;
+       u32 soc_status0 = readl(RK_GRF_VIRT + RK3036_GRF_CPU_STATUS0);
+
+       switch (id) {
+       case USB_STATUS_BVABLID:
+               /* bvalid in grf */
+               ret = soc_status0 & (0x1 << 8);
+               break;
+       case USB_STATUS_DPDM:
+               /* dpdm in grf */
+               ret = soc_status0 & (0x3 << 9);
+               break;
+       case USB_STATUS_ID:
+               /* id in grf */
+               ret = soc_status0 & (0x1 << 11);
+               break;
+       case USB_CHIP_ID:
+               ret = control_usb->chip_id;
+               break;
+       case USB_REMOTE_WAKEUP:
+               ret = control_usb->remote_wakeup;
+               break;
+       case USB_IRQ_WAKEUP:
+               ret = control_usb->usb_irq_wakeup;
+               break;
+       default:
+               break;
+       }
+       return ret;
+}
+
+static void dwc_otg_uart_mode(void *pdata, int enter_usb_uart_mode)
+{
+#ifdef CONFIG_RK_USB_UART
+       if (1 == enter_usb_uart_mode) {
+               /* bypass dm, enter uart mode */
+               writel(UOC_HIWORD_UPDATE(0x3, 0x3, 12),
+                      RK_GRF_VIRT + RK3036_GRF_UOC1_CON4);
+       } else if (0 == enter_usb_uart_mode) {
+               /* enter usb mode */
+               writel(UOC_HIWORD_UPDATE(0x0, 0x3, 12),
+                      RK_GRF_VIRT + RK3036_GRF_UOC1_CON4);
+       }
+#endif
+}
+
+static void usb20otg_power_enable(int enable)
+{
+       if (0 == enable) {
+               /* disable otg_drv power */
+               if (gpio_is_valid(control_usb->otg_gpios->gpio))
+                       gpio_set_value(control_usb->otg_gpios->gpio, 0);
+       } else if (1 == enable) {
+               /* enable otg_drv power */
+               if (gpio_is_valid(control_usb->otg_gpios->gpio))
+                       gpio_set_value(control_usb->otg_gpios->gpio, 1);
+       }
+}
+
+struct dwc_otg_platform_data usb20otg_pdata_rk3036 = {
+       .phyclk = NULL,
+       .ahbclk = NULL,
+       .busclk = NULL,
+       .phy_status = 0,
+       .hw_init = usb20otg_hw_init,
+       .phy_suspend = usb20otg_phy_suspend,
+       .soft_reset = usb20otg_soft_reset,
+       .clock_init = usb20otg_clock_init,
+       .clock_enable = usb20otg_clock_enable,
+       .get_status = usb20otg_get_status,
+       .power_enable = usb20otg_power_enable,
+       .dwc_otg_uart_mode = dwc_otg_uart_mode,
+       .bc_detect_cb = usb20otg_battery_charger_detect_cb,
+};
+#endif
+
+#ifdef CONFIG_USB20_HOST
+static void usb20host_hw_init(void)
+{
+       /* other haredware init,include:
+        * DRV_VBUS GPIO init */
+       if (gpio_is_valid(control_usb->host_gpios->gpio)) {
+               if (!gpio_get_value(control_usb->host_gpios->gpio))
+                       gpio_set_value(control_usb->host_gpios->gpio, 1);
+       }
+}
+
+static void usb20host_phy_suspend(void *pdata, int suspend)
+{
+       struct dwc_otg_platform_data *usbpdata = pdata;
+
+       if (suspend) {
+               /* enable soft control */
+               writel(UOC_HIWORD_UPDATE(0x1d5, 0x1ff, 0),
+                      RK_GRF_VIRT + RK3036_GRF_UOC1_CON5);
+               usbpdata->phy_status = 1;
+       } else {
+               /* exit suspend */
+               writel(UOC_HIWORD_UPDATE(0x0, 0x1, 0),
+                      RK_GRF_VIRT + RK3036_GRF_UOC1_CON5);
+               usbpdata->phy_status = 0;
+       }
+}
+
+static void usb20host_soft_reset(void)
+{
+       ;
+}
+
+static void usb20host_clock_init(void *pdata)
+{
+       struct dwc_otg_platform_data *usbpdata = pdata;
+       struct clk *ahbclk, *phyclk;
+
+       ahbclk = devm_clk_get(usbpdata->dev, "hclk_usb1");
+       if (IS_ERR(ahbclk)) {
+               dev_err(usbpdata->dev, "Failed to get hclk_usb1\n");
+               return;
+       }
+
+       phyclk = devm_clk_get(usbpdata->dev, "clk_usbphy1");
+       if (IS_ERR(phyclk)) {
+               dev_err(usbpdata->dev, "Failed to get clk_usbphy1\n");
+               return;
+       }
+
+       usbpdata->phyclk = phyclk;
+       usbpdata->ahbclk = ahbclk;
+}
+
+static void usb20host_clock_enable(void *pdata, int enable)
+{
+       struct dwc_otg_platform_data *usbpdata = pdata;
+
+       if (enable) {
+               clk_prepare_enable(usbpdata->ahbclk);
+               clk_prepare_enable(usbpdata->phyclk);
+       } else {
+               clk_disable_unprepare(usbpdata->ahbclk);
+               clk_disable_unprepare(usbpdata->phyclk);
+       }
+}
+
+static int usb20host_get_status(int id)
+{
+       int ret = -1;
+       u32 soc_status0 = readl(RK_GRF_VIRT + RK3036_GRF_CPU_STATUS0);
+
+       switch (id) {
+       case USB_STATUS_BVABLID:
+               /* bvalid in grf */
+               ret = soc_status0 & (0x1 << 13);
+               break;
+       case USB_STATUS_DPDM:
+               /* dpdm in grf */
+               ret = soc_status0 & (0x3 << 14);
+               break;
+       case USB_STATUS_ID:
+               /* id in grf */
+               ret = 0;
+               break;
+       case USB_CHIP_ID:
+               ret = control_usb->chip_id;
+               break;
+       case USB_REMOTE_WAKEUP:
+               ret = control_usb->remote_wakeup;
+               break;
+       case USB_IRQ_WAKEUP:
+               ret = control_usb->usb_irq_wakeup;
+               break;
+       default:
+               break;
+       }
+       return ret;
+}
+
+static void usb20host_power_enable(int enable)
+{
+       if (0 == enable) {
+               /* disable host_drv power */
+               /* do not disable power in default */
+       } else if (1 == enable) {
+               /* enable host_drv power */
+               if (gpio_is_valid(control_usb->host_gpios->gpio))
+                       gpio_set_value(control_usb->host_gpios->gpio, 1);
+       }
+}
+
+struct dwc_otg_platform_data usb20host_pdata_rk3036 = {
+       .phyclk = NULL,
+       .ahbclk = NULL,
+       .busclk = NULL,
+       .phy_status = 0,
+       .hw_init = usb20host_hw_init,
+       .phy_suspend = usb20host_phy_suspend,
+       .soft_reset = usb20host_soft_reset,
+       .clock_init = usb20host_clock_init,
+       .clock_enable = usb20host_clock_enable,
+       .get_status = usb20host_get_status,
+       .power_enable = usb20host_power_enable,
+};
+#endif
+
+#ifdef CONFIG_OF
+static const struct of_device_id rk_usb_control_id_table[] = {
+       {
+        .compatible = "rockchip,rk3036-usb-control",
+        },
+       {},
+};
+#endif
+/*********************************************************************
+                       rk3036 usb detections
+*********************************************************************/
+
+#define WAKE_LOCK_TIMEOUT (HZ * 10)
+static inline void do_wakeup(struct work_struct *work)
+{
+       /* wake up the system */
+       rk_send_wakeup_key();
+}
+
+static void usb_battery_charger_detect_work(struct work_struct *work)
+{
+       rk_usb_charger_status = usb_battery_charger_detect(0);
+}
+
+/********** handler for bvalid irq **********/
+static irqreturn_t bvalid_irq_handler(int irq, void *dev_id)
+{
+       /* clear irq */
+       writel(UOC_HIWORD_UPDATE(0x1, 0x1, 15),
+              RK_GRF_VIRT + RK3036_GRF_UOC0_CON5);
+#ifdef CONFIG_RK_USB_UART
+       /* usb otg dp/dm switch to usb phy */
+       dwc_otg_uart_mode(NULL, PHY_USB_MODE);
+#endif
+
+       if (control_usb->usb_irq_wakeup) {
+               wake_lock_timeout(&control_usb->usb_wakelock,
+                                 WAKE_LOCK_TIMEOUT);
+               schedule_delayed_work(&control_usb->usb_det_wakeup_work,
+                                     HZ / 10);
+       }
+
+       rk_usb_charger_status = USB_BC_TYPE_SDP;
+       schedule_delayed_work(&control_usb->usb_charger_det_work, HZ / 10);
+
+       return IRQ_HANDLED;
+}
+
+/************* register usb detection irqs **************/
+static int otg_irq_detect_init(struct platform_device *pdev)
+{
+       int ret = 0;
+       int irq = 0;
+
+       if (control_usb->usb_irq_wakeup) {
+               wake_lock_init(&control_usb->usb_wakelock, WAKE_LOCK_SUSPEND,
+                              "usb_detect");
+               INIT_DELAYED_WORK(&control_usb->usb_det_wakeup_work, do_wakeup);
+       }
+
+       /*register otg_bvalid irq */
+       irq = platform_get_irq_byname(pdev, "otg_bvalid");
+       if ((irq > 0) && control_usb->usb_irq_wakeup) {
+               ret = request_irq(irq, bvalid_irq_handler,
+                                 0, "otg_bvalid", NULL);
+               if (ret < 0) {
+                       dev_err(&pdev->dev, "request_irq %d failed!\n", irq);
+               } else {
+                       /* enable bvalid irq  */
+                       writel(UOC_HIWORD_UPDATE(0x1, 0x1, 14),
+                              RK_GRF_VIRT + RK3036_GRF_UOC0_CON5);
+               }
+       }
+       return ret;
+}
+
+/********** end of rk3036 usb detections **********/
+static int rk_usb_control_probe(struct platform_device *pdev)
+{
+       int gpio, err;
+       struct device_node *np = pdev->dev.of_node;
+       int ret = 0;
+
+       control_usb =
+           devm_kzalloc(&pdev->dev, sizeof(*control_usb), GFP_KERNEL);
+       if (!control_usb) {
+               dev_err(&pdev->dev, "unable to alloc memory for control usb\n");
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       control_usb->chip_id = RK3036_USB_CTLR;
+       control_usb->remote_wakeup = of_property_read_bool(np,
+                                                          "rockchip,remote_wakeup");
+       control_usb->usb_irq_wakeup = of_property_read_bool(np,
+                                                           "rockchip,usb_irq_wakeup");
+
+       INIT_DELAYED_WORK(&control_usb->usb_charger_det_work,
+                         usb_battery_charger_detect_work);
+
+       control_usb->host_gpios =
+           devm_kzalloc(&pdev->dev, sizeof(struct gpio), GFP_KERNEL);
+       if (!control_usb->host_gpios) {
+               dev_err(&pdev->dev, "unable to alloc memory for host_gpios\n");
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       gpio = of_get_named_gpio(np, "host_drv_gpio", 0);
+       control_usb->host_gpios->gpio = gpio;
+
+       if (!gpio_is_valid(gpio)) {
+               dev_err(&pdev->dev, "invalid host gpio%d\n", gpio);
+       } else {
+               err = devm_gpio_request(&pdev->dev, gpio, "host_drv_gpio");
+               if (err) {
+                       dev_err(&pdev->dev,
+                               "failed to request GPIO%d for host_drv\n",
+                               gpio);
+                       ret = err;
+                       goto out;
+               }
+               gpio_direction_output(control_usb->host_gpios->gpio, 1);
+       }
+
+       control_usb->otg_gpios =
+           devm_kzalloc(&pdev->dev, sizeof(struct gpio), GFP_KERNEL);
+       if (!control_usb->otg_gpios) {
+               dev_err(&pdev->dev, "unable to alloc memory for otg_gpios\n");
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       gpio = of_get_named_gpio(np, "otg_drv_gpio", 0);
+       control_usb->otg_gpios->gpio = gpio;
+
+       if (!gpio_is_valid(gpio)) {
+               dev_err(&pdev->dev, "invalid otg gpio%d\n", gpio);
+       } else {
+               err = devm_gpio_request(&pdev->dev, gpio, "otg_drv_gpio");
+               if (err) {
+                       dev_err(&pdev->dev,
+                               "failed to request GPIO%d for otg_drv\n", gpio);
+                       ret = err;
+                       goto out;
+               }
+               gpio_direction_output(control_usb->otg_gpios->gpio, 0);
+       }
+
+out:
+       return ret;
+}
+
+static int rk_usb_control_remove(struct platform_device *pdev)
+{
+       return 0;
+}
+
+static struct platform_driver rk_usb_control_driver = {
+       .probe = rk_usb_control_probe,
+       .remove = rk_usb_control_remove,
+       .driver = {
+                  .name = "rk3036-usb-control",
+                  .owner = THIS_MODULE,
+                  .of_match_table = of_match_ptr(rk_usb_control_id_table),
+                  },
+};
+
+#ifdef CONFIG_OF
+
+static const struct of_device_id dwc_otg_control_usb_id_table[] = {
+       {
+        .compatible = "rockchip,rk3036-dwc-control-usb",
+        },
+       {},
+};
+
+#endif
+static int dwc_otg_control_usb_probe(struct platform_device *pdev)
+{
+       struct clk *hclk_usb_peri;
+       int ret = 0;
+
+       if (!control_usb) {
+               dev_err(&pdev->dev, "unable to alloc memory for control usb\n");
+               ret = -ENOMEM;
+               goto err1;
+       }
+
+       hclk_usb_peri = devm_clk_get(&pdev->dev, "hclk_usb_peri");
+       if (IS_ERR(hclk_usb_peri)) {
+               dev_err(&pdev->dev, "Failed to get hclk_usb_peri\n");
+               ret = -EINVAL;
+               goto err1;
+       }
+
+       control_usb->hclk_usb_peri = hclk_usb_peri;
+       clk_prepare_enable(hclk_usb_peri);
+
+#ifdef CONFIG_USB20_OTG
+       if (usb20otg_get_status(USB_STATUS_BVABLID)) {
+               rk_usb_charger_status = USB_BC_TYPE_SDP;
+               schedule_delayed_work(&control_usb->usb_charger_det_work,
+                                     HZ / 10);
+       }
+#endif
+
+       ret = otg_irq_detect_init(pdev);
+       if (ret < 0)
+               goto err2;
+
+       return 0;
+
+err2:
+       clk_disable_unprepare(hclk_usb_peri);
+err1:
+       return ret;
+}
+
+static int dwc_otg_control_usb_remove(struct platform_device *pdev)
+{
+       clk_disable_unprepare(control_usb->hclk_usb_peri);
+       return 0;
+}
+
+static struct platform_driver dwc_otg_control_usb_driver = {
+       .probe = dwc_otg_control_usb_probe,
+       .remove = dwc_otg_control_usb_remove,
+       .driver = {
+                  .name = "rk3036-dwc-control-usb",
+                  .owner = THIS_MODULE,
+                  .of_match_table = of_match_ptr(dwc_otg_control_usb_id_table),
+                  },
+};
+
+static int __init dwc_otg_control_usb_init(void)
+{
+       int retval = 0;
+
+       retval |= platform_driver_register(&rk_usb_control_driver);
+       retval |= platform_driver_register(&dwc_otg_control_usb_driver);
+       return retval;
+}
+
+subsys_initcall(dwc_otg_control_usb_init);
+
+static void __exit dwc_otg_control_usb_exit(void)
+{
+       platform_driver_unregister(&rk_usb_control_driver);
+       platform_driver_unregister(&dwc_otg_control_usb_driver);
+}
+
+module_exit(dwc_otg_control_usb_exit);
+MODULE_ALIAS("platform: dwc_control_usb");
+MODULE_AUTHOR("RockChip Inc.");
+MODULE_DESCRIPTION("RockChip Control Module USB Driver");
+MODULE_LICENSE("GPL v2");