rk312x: add usb
authorlyz <lyz@rock-chips.com>
Wed, 30 Jul 2014 10:30:52 +0000 (18:30 +0800)
committerlyz <lyz@rock-chips.com>
Wed, 30 Jul 2014 10:31:01 +0000 (18:31 +0800)
arch/arm/boot/dts/rk3126-sdk.dts
arch/arm/boot/dts/rk312x.dtsi
drivers/usb/dwc_otg_310/Makefile
drivers/usb/dwc_otg_310/dwc_otg_driver.c
drivers/usb/dwc_otg_310/usbdev_rk.h
drivers/usb/dwc_otg_310/usbdev_rk3126.c [new file with mode: 0755]

index 24ef257c303aa2a72ccc8b5501e82b6750bee687..cb3c4b3ecaa015f0a9e4b3c5c088e80c420e0c2a 100755 (executable)
                 default-brightness-level = <128>;
                 enable-gpios = <&gpio3 GPIO_C1 GPIO_ACTIVE_HIGH>;
         };
+       usb_control {
+               compatible = "rockchip,rk3126-usb-control";
+               host_drv_gpio = <&gpio2 GPIO_B4 GPIO_ACTIVE_LOW>;
+               otg_drv_gpio = <&gpio2 GPIO_B6 GPIO_ACTIVE_LOW>;
+
+               rockchip,remote_wakeup;
+               rockchip,usb_irq_wakeup;
+       };
 };
 
 &nandc {
index ef7d1c9a7b8ecffd9f71ef7a05fc375704da02ca..1485704d82fe3551bd11704e276a5c04a3678a15 100755 (executable)
                 status = "disabled";
         };
 
+       dwc_control_usb: dwc-control-usb@20008000 {
+               compatible = "rockchip,rk3126-dwc-control-usb";
+               reg = <0x20008000 0x4>;
+               interrupts = <GIC_SPI 6 IRQ_TYPE_LEVEL_HIGH>;
+               interrupt-names = "otg_bvalid";
+               clocks = <&clk_gates9 13>;
+               clock-names = "hclk_usb_peri";
+               rockchip,remote_wakeup;
+               rockchip,usb_irq_wakeup;
+               resets = <&reset RK3128_RST_USBPOR>;
+               reset-names = "usbphy_por";
+               usb_bc{
+                       compatible = "inno,phy";
+                       regbase = &dwc_control_usb;
+               };
+       };
+
+       usb0: usb@10180000 {
+               compatible = "rockchip,rk3126_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";
+               resets = <&reset RK3128_RST_USBOTG0>, <&reset RK3128_RST_USBOTG0>,
+                               <&reset RK3128_RST_OTGC0>;
+               reset-names = "otg_ahb", "otg_phy", "otg_controller";
+               /*0 - Normal, 1 - Force Host, 2 - Force Device*/
+               rockchip,usb-mode = <0>;
+       };
+
+       usb1: usb@101c0000 {
+               compatible = "rockchip,rk3126_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";
+               resets = <&reset RK3128_RST_USBOTG1>, <&reset RK3128_RST_UTMI1>,
+                               <&reset RK3128_RST_OTGC1>;
+               reset-names = "host_ahb", "host_phy", "host_controller";
+       };
+
        fb: fb{
                compatible = "rockchip,rk-fb";
                rockchip,disp-mode = <ONE_DUAL>;
index f5dd7ae364c9ede0abe72b3fd46b63419da90eee..b1656829613ba3fcfc481040f780a8cb7a0ad0bf 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_rk3036.o usbdev_bc.o
+dwc_otg-objs   += usbdev_rk30.o usbdev_rk32.o usbdev_rk3036.o usbdev_rk3126.o usbdev_bc.o
 obj-$(CONFIG_DWC_OTG_310) := dwc_otg.o
index 96671ef802a8e8168695c84f67e0493bf2e9d734..57034a80f4ee13cd7dad13225cd7ef223e6ceb7a 100755 (executable)
@@ -90,6 +90,10 @@ static struct usb20otg_pdata_id usb20otg_pdata[] = {
         .name = "rk3036-usb20otg",
         .pdata = &usb20otg_pdata_rk3036,
         },
+       {
+        .name = "rk3126-usb20otg",
+        .pdata = &usb20otg_pdata_rk3126,
+        },
        {},
 };
 #endif
@@ -108,6 +112,10 @@ static struct usb20host_pdata_id usb20host_pdata[] = {
         .name = "rk3036-usb20host",
         .pdata = &usb20host_pdata_rk3036,
         },
+       {
+        .name = "rk3126-usb20host",
+        .pdata = &usb20host_pdata_rk3126,
+        },
        {},
 };
 #endif
@@ -984,6 +992,10 @@ static const struct of_device_id usb20_host_of_match[] = {
         .compatible = "rockchip,rk3036_usb20_host",
         .data = &usb20host_pdata[RK3036_USB_CTLR],
         },
+       {
+        .compatible = "rockchip,rk3126_usb20_host",
+        .data = &usb20host_pdata[RK3126_USB_CTLR],
+        },
        {},
 };
 
@@ -1342,6 +1354,10 @@ static const struct of_device_id usb20_otg_of_match[] = {
         .compatible = "rockchip,rk3036_usb20_otg",
         .data = &usb20otg_pdata[RK3036_USB_CTLR],
         },
+       {
+        .compatible = "rockchip,rk3126_usb20_otg",
+        .data = &usb20otg_pdata[RK3126_USB_CTLR],
+        },
        {
         },
 };
index c6d9e76d26942e8c70b594af036e5beceb532a65..06584dd516ca443b3e981a9a18a02af220870b0d 100755 (executable)
@@ -67,6 +67,10 @@ 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;
+/* rk3126 platform data */
+extern struct dwc_otg_platform_data usb20otg_pdata_rk3126;
+extern struct dwc_otg_platform_data usb20host_pdata_rk3126;
+
 
 struct dwc_otg_platform_data {
        void *privdata;
@@ -131,6 +135,7 @@ enum {
        RK3188_USB_CTLR = 0,    /* rk3188 chip usb */
        RK3288_USB_CTLR,        /* rk3288 chip usb */
        RK3036_USB_CTLR,        /* rk3036 chip usb */
+       RK3126_USB_CTLR,
 };
 
 struct usb20otg_pdata_id {
diff --git a/drivers/usb/dwc_otg_310/usbdev_rk3126.c b/drivers/usb/dwc_otg_310/usbdev_rk3126.c
new file mode 100755 (executable)
index 0000000..0c69143
--- /dev/null
@@ -0,0 +1,608 @@
+#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 + RK312X_GRF_UOC0_CON0);
+               usbpdata->phy_status = 1;
+       } else {
+               /* exit suspend */
+               writel(UOC_HIWORD_UPDATE(0x0, 0x1, 0),
+                      RK_GRF_VIRT + RK312X_GRF_UOC0_CON0);
+               usbpdata->phy_status = 0;
+       }
+}
+
+static void usb20otg_soft_reset(void *pdata, enum rkusb_rst_flag rst_type)
+{
+       struct dwc_otg_platform_data *usbpdata = pdata;
+       struct reset_control *rst_otg_h, *rst_otg_p, *rst_otg_c;
+
+       rst_otg_h = devm_reset_control_get(usbpdata->dev, "otg_ahb");
+       rst_otg_p = devm_reset_control_get(usbpdata->dev, "otg_phy");
+       rst_otg_c = devm_reset_control_get(usbpdata->dev, "otg_controller");
+       if (IS_ERR(rst_otg_h) || IS_ERR(rst_otg_p) || IS_ERR(rst_otg_c)) {
+               dev_err(usbpdata->dev, "Fail to get reset control from dts\n");
+               return;
+       }
+
+       switch(rst_type) {
+       case RST_POR:
+               /* PHY reset */
+               writel(UOC_HIWORD_UPDATE(0x1, 0x3, 0),
+                          RK_GRF_VIRT + RK312X_GRF_UOC0_CON0);
+               reset_control_assert(rst_otg_p);
+               udelay(15);
+               writel(UOC_HIWORD_UPDATE(0x2, 0x3, 0),
+                          RK_GRF_VIRT + RK312X_GRF_UOC0_CON0);
+               udelay(1500);
+               reset_control_deassert(rst_otg_p);
+               udelay(2);
+
+               /* Controller reset */
+               reset_control_assert(rst_otg_c);
+               reset_control_assert(rst_otg_h);
+
+               udelay(2);
+
+               reset_control_deassert(rst_otg_c);
+               reset_control_deassert(rst_otg_h);
+               break;
+
+       default:
+               break;
+       }
+}
+
+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 + RK312X_GRF_SOC_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 + RK312X_GRF_UOC1_CON4);
+       } else if (0 == enter_usb_uart_mode) {
+               /* enter usb mode */
+               writel(UOC_HIWORD_UPDATE(0x0, 0x3, 12),
+                      RK_GRF_VIRT + RK312X_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_rk3126 = {
+       .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 + RK312X_GRF_UOC1_CON5);
+               usbpdata->phy_status = 1;
+       } else {
+               /* exit suspend */
+               writel(UOC_HIWORD_UPDATE(0x0, 0x1, 0),
+                      RK_GRF_VIRT + RK312X_GRF_UOC1_CON5);
+               usbpdata->phy_status = 0;
+       }
+}
+
+static void usb20host_soft_reset(void *pdata, enum rkusb_rst_flag rst_type)
+{
+       struct dwc_otg_platform_data *usbpdata = pdata;
+       struct reset_control *rst_host_h, *rst_host_p, *rst_host_c;
+
+       rst_host_h = devm_reset_control_get(usbpdata->dev, "host_ahb");
+       rst_host_p = devm_reset_control_get(usbpdata->dev, "host_phy");
+       rst_host_c = devm_reset_control_get(usbpdata->dev, "host_controller");
+       if (IS_ERR(rst_host_h) || IS_ERR(rst_host_p) || IS_ERR(rst_host_c)) {
+               dev_err(usbpdata->dev, "Fail to get reset control from dts\n");
+               return;
+       }
+
+       switch(rst_type) {
+       case RST_POR:
+               /* PHY reset */
+               writel(UOC_HIWORD_UPDATE(0x1, 0x3, 0),
+                          RK_GRF_VIRT + RK312X_GRF_UOC1_CON5);
+               reset_control_assert(rst_host_p);
+               udelay(15);
+               writel(UOC_HIWORD_UPDATE(0x2, 0x3, 0),
+                          RK_GRF_VIRT + RK312X_GRF_UOC1_CON5);
+
+               udelay(1500);
+               reset_control_deassert(rst_host_p);
+
+               /* Controller reset */
+               reset_control_assert(rst_host_c);
+               reset_control_assert(rst_host_h);
+
+               udelay(5);
+
+               reset_control_deassert(rst_host_c);
+               reset_control_deassert(rst_host_h);
+               break;
+
+       default:
+               break;
+       }
+}
+
+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 + RK312X_GRF_SOC_STATUS0);
+
+       switch (id) {
+       case USB_STATUS_BVABLID:
+               /* bvalid in grf */
+               ret = soc_status0 & (0x1 << 10);
+               break;
+       case USB_STATUS_DPDM:
+               /* dpdm in grf */
+               ret = soc_status0 & (0x3 << 11);
+               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_rk3126 = {
+       .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,rk3126-usb-control",
+        },
+       {},
+};
+#endif
+/*********************************************************************
+                       rk3126 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 + RK312X_GRF_UOC0_CON0);
+#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 + RK312X_GRF_UOC0_CON0);
+               }
+       }
+       return ret;
+}
+
+/********** end of rk3126 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 = RK3126_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 = "rk3126-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,rk3126-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 = "rk3126-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");
+