usb: dwc_otg_310: fix usb vbus power controlled by pmic
[firefly-linux-kernel-4.4.55.git] / drivers / usb / dwc_otg_310 / usbdev_rk32.c
old mode 100755 (executable)
new mode 100644 (file)
index 9656992..53e14de
@@ -1,17 +1,25 @@
-
+#ifdef CONFIG_ARM
 #include "usbdev_rk.h"
 #include "usbdev_grf_regs.h"
 #include "dwc_otg_regs.h"
 static struct dwc_otg_control_usb *control_usb;
 
+int is_rk3288_usb(void)
+{
+       if (!control_usb)
+               return false;
+
+       return control_usb->chip_id == RK3288_USB_CTLR ? true : false;
+}
+
 #ifdef CONFIG_USB20_OTG
 static void usb20otg_hw_init(void)
 {
 #ifndef CONFIG_USB20_HOST
-       //enable soft control
-       control_usb->grf_uoc2_base->CON2 = (0x01<<2)|((0x01<<2)<<16);   
-       // enter suspend
-       control_usb->grf_uoc2_base->CON3 = 0x2A|(0x3F<<16);     
+       /* enable soft control */
+       control_usb->grf_uoc2_base->CON2 = (0x01 << 2) | ((0x01 << 2) << 16);
+       /* enter suspend */
+       control_usb->grf_uoc2_base->CON3 = 0x2A | (0x3F << 16);
 #endif
        /* usb phy config init
         * usb phy enter usb mode */
@@ -19,36 +27,57 @@ static void usb20otg_hw_init(void)
 
        /* other haredware init,include:
         * DRV_VBUS GPIO init */
-//     gpio_direction_output(control_usb->otg_gpios->gpio, 0);
-
+       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(voidpdata, int suspend)
+static void usb20otg_phy_suspend(void *pdata, int suspend)
 {
-       struct dwc_otg_platform_data *usbpdata=pdata;
-
-       if(suspend){
-               //enable soft control
-               control_usb->grf_uoc0_base->CON2 = (0x01<<2)|((0x01<<2)<<16);
-               //enter suspend
-               control_usb->grf_uoc0_base->CON3 = 0x2A|(0x3F<<16);
+       struct dwc_otg_platform_data *usbpdata = pdata;
+
+       if (suspend) {
+               /* enable soft control */
+               control_usb->grf_uoc0_base->CON2 =
+                   (0x01 << 2) | ((0x01 << 2) << 16);
+               /* enter suspend */
+               control_usb->grf_uoc0_base->CON3 = 0x2A | (0x3F << 16);
                usbpdata->phy_status = 1;
-       }else{
-               // exit suspend.
-               control_usb->grf_uoc0_base->CON2 = ((0x01<<2)<<16);
+       } else {
+               /* exit suspend */
+               control_usb->grf_uoc0_base->CON2 = ((0x01 << 2) << 16);
                usbpdata->phy_status = 0;
        }
 }
 
-static void usb20otg_soft_reset(void)
+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;
+       }
+
+       reset_control_assert(rst_otg_h);
+       reset_control_assert(rst_otg_p);
+       reset_control_assert(rst_otg_c);
+       udelay(5);
+       reset_control_deassert(rst_otg_h);
+       reset_control_deassert(rst_otg_p);
+       reset_control_deassert(rst_otg_c);
+       mdelay(2);
 }
 
-static void usb20otg_clock_init(voidpdata)
+static void usb20otg_clock_init(void *pdata)
 {
-       /*
-       struct dwc_otg_platform_data *usbpdata=pdata;
-       struct clk* ahbclk,*phyclk;
+       struct dwc_otg_platform_data *usbpdata = pdata;
+       struct clk *ahbclk, *phyclk;
 
        ahbclk = devm_clk_get(usbpdata->dev, "hclk_usb0");
        if (IS_ERR(ahbclk)) {
@@ -64,80 +93,107 @@ static void usb20otg_clock_init(void* pdata)
 
        usbpdata->phyclk = phyclk;
        usbpdata->ahbclk = ahbclk;
-       */
 }
 
-static void usb20otg_clock_enable(voidpdata, int enable)
+static void usb20otg_clock_enable(void *pdata, int enable)
 {
-       /*
-       struct dwc_otg_platform_data *usbpdata=pdata;
+       struct dwc_otg_platform_data *usbpdata = pdata;
 
-       if(enable){
+       if (enable) {
                clk_prepare_enable(usbpdata->ahbclk);
                clk_prepare_enable(usbpdata->phyclk);
-       }else{
+       } else {
                clk_disable_unprepare(usbpdata->ahbclk);
                clk_disable_unprepare(usbpdata->phyclk);
-       }*/
+       }
 }
 
 static int usb20otg_get_status(int id)
 {
        int ret = -1;
 
-       switch(id){
-               case USB_STATUS_BVABLID:
-                       // bvalid in grf
-                       ret = control_usb->grf_soc_status2_rk3288->otg_bvalid;
-                       break;
-               case USB_STATUS_DPDM:
-                       // dpdm in grf
-                       ret = control_usb->grf_soc_status2_rk3288->otg_linestate;
-                       break;
-               case USB_STATUS_ID:
-                       // id in grf
-                       ret = control_usb->grf_soc_status2_rk3288->otg_iddig;
-                       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;
+       switch (id) {
+       case USB_STATUS_BVABLID:
+               /* bvalid in grf */
+               ret = control_usb->grf_soc_status2_rk3288->otg_bvalid;
+               break;
+       case USB_STATUS_DPDM:
+               /* dpdm in grf */
+               ret = control_usb->grf_soc_status2_rk3288->otg_linestate;
+               break;
+       case USB_STATUS_ID:
+               /* id in grf */
+               ret = control_usb->grf_soc_status2_rk3288->otg_iddig;
+               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;
 }
 
 #ifdef CONFIG_RK_USB_UART
-static void dwc_otg_uart_mode(void* pdata, int enter_usb_uart_mode)
+/**
+ *  dwc_otg_uart_enabled - check if a usb-uart bypass func is enabled in DT
+ *
+ *  Returns true if the status property of node "usb_uart" is set to "okay"
+ *  or "ok", if this property is absent it will use the default status "ok"
+ *  0 otherwise
+ */
+static bool dwc_otg_uart_enabled(void)
 {
-       if(1 == enter_usb_uart_mode){
-               /* bypass dm, enter uart mode*/
+       struct device_node *np;
+
+       np = of_find_node_by_name(NULL, "usb_uart");
+       if (np && of_device_is_available(np))
+               return true;
+
+       return false;
+}
+
+static void dwc_otg_uart_mode(void *pdata, int enter_usb_uart_mode)
+{
+       if ((1 == enter_usb_uart_mode) && dwc_otg_uart_enabled()) {
+               /* bypass dm, enter uart mode */
                control_usb->grf_uoc0_base->CON3 = (0x00c0 | (0x00c0 << 16));
-       
-       }else if(0 == enter_usb_uart_mode){
+
+       } else if (0 == enter_usb_uart_mode) {
                /* enter usb mode */
                control_usb->grf_uoc0_base->CON3 = (0x00c0 << 16);
        }
 }
+#else
+static void dwc_otg_uart_mode(void *pdata, int enter_usb_uart_mode)
+{
+}
 #endif
 
 static void usb20otg_power_enable(int enable)
-{      /*
-       if(0 == enable){//disable otg_drv power
-               gpio_set_value(control_usb->otg_gpios->gpio, 0);
-       }else if(1 == enable){//enable otg_drv power
-               gpio_set_value(control_usb->otg_gpios->gpio, 1);
-       }*/
+{
+       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 (usb20otg_get_status(USB_STATUS_BVABLID))
+                       rk_battery_charger_detect_cb(USB_OTG_POWER_OFF);
+       } 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);
+               else if (!usb20otg_get_status(USB_STATUS_BVABLID))
+                       rk_battery_charger_detect_cb(USB_OTG_POWER_ON);
+       }
 }
 
-
 struct dwc_otg_platform_data usb20otg_pdata_rk3288 = {
        .phyclk = NULL,
        .ahbclk = NULL,
@@ -150,10 +206,8 @@ struct dwc_otg_platform_data usb20otg_pdata_rk3288 = {
        .clock_enable = usb20otg_clock_enable,
        .get_status = usb20otg_get_status,
        .power_enable = usb20otg_power_enable,
-#ifdef CONFIG_RK_USB_UART
        .dwc_otg_uart_mode = dwc_otg_uart_mode,
-#endif
-    .bc_detect_cb=usb20otg_battery_charger_detect_cb,
+       .bc_detect_cb = rk_battery_charger_detect_cb,
 };
 
 #endif
@@ -162,39 +216,65 @@ struct dwc_otg_platform_data usb20otg_pdata_rk3288 = {
 
 static void usb20host_hw_init(void)
 {
-       /* usb phy config init */
+       /* usb phy config init
+        * set common_on = 0, in suspend mode, host1 PLL blocks remain powered.
+        * for RK3288, ehci1 and other modules use host1 (DWC_OTG) 480M phy clk.
+        */
+       control_usb->grf_uoc2_base->CON0 = (1 << 16) | 0;
 
        /* other haredware init,include:
         * DRV_VBUS GPIO init */
-//     gpio_direction_output(control_usb->host_gpios->gpio, 1);
+       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(voidpdata, int suspend)
+static void usb20host_phy_suspend(void *pdata, int suspend)
 {
-       struct dwc_otg_platform_data *usbpdata=pdata;
-
-       if(suspend){
-               // enable soft control
-               control_usb->grf_uoc2_base->CON2 = (0x01<<2)|((0x01<<2)<<16);
-               // enter suspend
-               control_usb->grf_uoc2_base->CON3 = 0x2A|(0x3F<<16); 
+       struct dwc_otg_platform_data *usbpdata = pdata;
+
+       if (suspend) {
+               /* enable soft control */
+               control_usb->grf_uoc2_base->CON2 =
+                   (0x01 << 2) | ((0x01 << 2) << 16);
+               /* enter suspend */
+               control_usb->grf_uoc2_base->CON3 = 0x2A | (0x3F << 16);
                usbpdata->phy_status = 1;
-       }else{
-               //exit suspend.
-               control_usb->grf_uoc2_base->CON2 = ((0x01<<2)<<16);
+       } else {
+               /* exit suspend */
+               control_usb->grf_uoc2_base->CON2 = ((0x01 << 2) << 16);
                usbpdata->phy_status = 0;
        }
 }
 
-static void usb20host_soft_reset(void)
+static void usb20host_soft_reset(void *pdata, enum rkusb_rst_flag rst_type)
 {
+       struct dwc_otg_platform_data *usbpdata = pdata;
+       struct reset_control *rst_host1_h, *rst_host1_p, *rst_host1_c;
+
+       rst_host1_h = devm_reset_control_get(usbpdata->dev, "host1_ahb");
+       rst_host1_p = devm_reset_control_get(usbpdata->dev, "host1_phy");
+       rst_host1_c = devm_reset_control_get(usbpdata->dev, "host1_controller");
+       if (IS_ERR(rst_host1_h) || IS_ERR(rst_host1_p) || IS_ERR(rst_host1_c)) {
+               dev_err(usbpdata->dev, "Fail to get reset control from dts\n");
+               return;
+       }
+
+       reset_control_assert(rst_host1_h);
+       reset_control_assert(rst_host1_p);
+       reset_control_assert(rst_host1_c);
+       udelay(5);
+       reset_control_deassert(rst_host1_h);
+       reset_control_deassert(rst_host1_p);
+       reset_control_deassert(rst_host1_c);
+       mdelay(2);
 }
 
-static void usb20host_clock_init(voidpdata)
+static void usb20host_clock_init(void *pdata)
 {
-       /*
-       struct dwc_otg_platform_data *usbpdata=pdata;
-       struct clk* ahbclk,*phyclk;
+       struct dwc_otg_platform_data *usbpdata = pdata;
+       struct clk *ahbclk, *phyclk, *phyclk_480m;
 
        ahbclk = devm_clk_get(usbpdata->dev, "hclk_usb1");
        if (IS_ERR(ahbclk)) {
@@ -208,69 +288,76 @@ static void usb20host_clock_init(void* pdata)
                return;
        }
 
+       phyclk_480m = devm_clk_get(usbpdata->dev, "usbphy_480m");
+       if (IS_ERR(phyclk_480m)) {
+               dev_err(usbpdata->dev, "Failed to get usbphy_480m\n");
+               return;
+       }
+
        usbpdata->phyclk = phyclk;
        usbpdata->ahbclk = ahbclk;
-       */
+       usbpdata->phyclk_480m = phyclk_480m;
 }
 
-static void usb20host_clock_enable(voidpdata, int enable)
+static void usb20host_clock_enable(void *pdata, int enable)
 {
-       /*
-       struct dwc_otg_platform_data *usbpdata=pdata;
+       struct dwc_otg_platform_data *usbpdata = pdata;
 
-       if(enable){
+       if (enable) {
                clk_prepare_enable(usbpdata->ahbclk);
                clk_prepare_enable(usbpdata->phyclk);
-       }else{
+       } else {
                clk_disable_unprepare(usbpdata->ahbclk);
                clk_disable_unprepare(usbpdata->phyclk);
        }
-       */
 }
 
 static int usb20host_get_status(int id)
 {
        int ret = -1;
 
-       switch(id){
-               case USB_STATUS_BVABLID:
-                       // bvalid in grf
-                       ret = control_usb->grf_soc_status2_rk3288->host1_bvalid;        
-                       break;
-               case USB_STATUS_DPDM:
-                       // dpdm in grf
-                       ret = control_usb->grf_soc_status2_rk3288->host1_linestate;
-                       break;
-               case USB_STATUS_ID:
-                       // id in grf
-                       ret = control_usb->grf_soc_status2_rk3288->host1_iddig;
-                       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;
+       switch (id) {
+       case USB_STATUS_BVABLID:
+               /* bvalid in grf */
+               ret = control_usb->grf_soc_status2_rk3288->host1_bvalid;
+               break;
+       case USB_STATUS_DPDM:
+               /* dpdm in grf */
+               ret = control_usb->grf_soc_status2_rk3288->host1_linestate;
+               break;
+       case USB_STATUS_ID:
+               /* id in grf */
+               ret = control_usb->grf_soc_status2_rk3288->host1_iddig;
+               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
-               gpio_set_value(control_usb->host_gpios->gpio, 1);
-       }*/
+{
+       if (0 == enable) {
+               /* disable host_drv power */
+               if (gpio_is_valid(control_usb->host_gpios->gpio))
+                       gpio_set_value(control_usb->host_gpios->gpio, 0);
+       } 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_rk3288 = {
        .phyclk = NULL,
        .ahbclk = NULL,
@@ -287,17 +374,19 @@ struct dwc_otg_platform_data usb20host_pdata_rk3288 = {
 
 #endif
 
-#ifdef CONFIG_USB_EHCI_RKHSIC
-static void rk_hsic_hw_init(void)
+#ifdef CONFIG_USB_EHCI1_RK
+static void rk_ehci1_hw_init(void)
 {
-       // usb phy config init
-       // hsic phy config init, set hsicphy_txsrtune
-       control_usb->grf_uoc3_base->CON0 = ((0xf<<6)<<16)|(0xf<<6);
+       /* usb phy config init
+        * ehci1 phy config init, set ehci1phy_txsrtune */
+       control_usb->grf_uoc3_base->CON0 = ((0xf << 6) << 16) | (0xf << 6);
 
        /* other haredware init
-        * set common_on, in suspend mode, otg/host PLL blocks remain powered
+        * set common_on = 0, in suspend mode,
+        * otg/host PLL blocks remain powered
+        * for RK3288, use host1 (DWC_OTG) 480M phy clk
         */
-
+       control_usb->grf_uoc2_base->CON0 = (1 << 16) | 0;
 
        /* change INCR to INCR16 or INCR8(beats less than 16)
         * or INCR4(beats less than 8) or SINGLE(beats less than 4)
@@ -305,131 +394,280 @@ static void rk_hsic_hw_init(void)
        control_usb->grf_uoc4_base->CON0 = 0x00ff00bc;
 }
 
-static void rk_hsic_clock_init(void* pdata)
+static void rk_ehci1_clock_init(void *pdata)
 {
-       /* By default, hsicphy_480m's parent is otg phy 480MHz clk
+       /* By default, ehci1phy_480m's parent is otg phy 480MHz clk
         * rk3188 must use host phy 480MHz clk, because if otg bypass
         * to uart mode, otg phy 480MHz clk will be closed automatically
         */
-       /*
-       struct rkehci_platform_data *usbpdata=pdata;
-       struct clk *ahbclk, *phyclk480m_hsic, *phyclk12m_hsic, *phyclk_usbphy1;
+       struct rkehci_platform_data *usbpdata = pdata;
+       struct clk *ahbclk, *phyclk480m_ehci1, *phyclk12m_ehci1;
 
-       phyclk480m_hsic = devm_clk_get(usbpdata->dev, "hsicphy_480m");
-       if (IS_ERR(phyclk480m_hsic)) {
-               dev_err(usbpdata->dev, "Failed to get hsicphy_480m\n");
+       phyclk480m_ehci1 = devm_clk_get(usbpdata->dev, "ehci1phy_480m");
+       if (IS_ERR(phyclk480m_ehci1)) {
+               dev_err(usbpdata->dev, "Failed to get ehci1phy_480m\n");
                return;
        }
 
-       phyclk12m_hsic = devm_clk_get(usbpdata->dev, "hsicphy_12m");
-       if (IS_ERR(phyclk12m_hsic)) {
-               dev_err(usbpdata->dev, "Failed to get hsicphy_12m\n");
+       phyclk12m_ehci1 = devm_clk_get(usbpdata->dev, "ehci1phy_12m");
+       if (IS_ERR(phyclk12m_ehci1)) {
+               dev_err(usbpdata->dev, "Failed to get ehci1phy_12m\n");
                return;
        }
 
-       phyclk_usbphy1 = devm_clk_get(usbpdata->dev, "hsic_usbphy1");
-       if (IS_ERR(phyclk_usbphy1)) {
-               dev_err(usbpdata->dev, "Failed to get hsic_usbphy1\n");
-               return;
-       }
-
-       ahbclk = devm_clk_get(usbpdata->dev, "hclk_hsic");
+       ahbclk = devm_clk_get(usbpdata->dev, "hclk_ehci1");
        if (IS_ERR(ahbclk)) {
-               dev_err(usbpdata->dev, "Failed to get hclk_hsic\n");
+               dev_err(usbpdata->dev, "Failed to get hclk_ehci1\n");
                return;
        }
 
-       clk_set_parent(phyclk480m_hsic, phyclk_usbphy1);
-       
-       usbpdata->hclk_hsic = ahbclk;
-       usbpdata->hsic_phy_480m = phyclk480m_hsic;
-       usbpdata->hsic_phy_12m = phyclk12m_hsic;
-       */
+       usbpdata->hclk_ehci = ahbclk;
+       usbpdata->ehci_phy_480m = phyclk480m_ehci1;
+       usbpdata->ehci_phy_12m = phyclk12m_ehci1;
 }
 
-static void rk_hsic_clock_enable(void* pdata, int enable)
+static void rk_ehci1_clock_enable(void *pdata, int enable)
 {
-       /*
-       struct rkehci_platform_data *usbpdata=pdata;
+       struct rkehci_platform_data *usbpdata = pdata;
 
-       if(enable == usbpdata->clk_status)
+       if (enable == usbpdata->clk_status)
                return;
-       if(enable){
-               clk_prepare_enable(usbpdata->hclk_hsic);
-               clk_prepare_enable(usbpdata->hsic_phy_480m);
-               clk_prepare_enable(usbpdata->hsic_phy_12m);
+       if (enable) {
+               clk_prepare_enable(usbpdata->hclk_ehci);
+               clk_prepare_enable(usbpdata->ehci_phy_480m);
+               clk_prepare_enable(usbpdata->ehci_phy_12m);
                usbpdata->clk_status = 1;
-       }else{
-               clk_disable_unprepare(usbpdata->hclk_hsic);
-               clk_disable_unprepare(usbpdata->hsic_phy_480m);
-               clk_disable_unprepare(usbpdata->hsic_phy_12m);
+       } else {
+               clk_disable_unprepare(usbpdata->hclk_ehci);
+               clk_disable_unprepare(usbpdata->ehci_phy_480m);
+               clk_disable_unprepare(usbpdata->ehci_phy_12m);
                usbpdata->clk_status = 0;
        }
-       */
 }
 
-static void rk_hsic_soft_reset(void)
+static void rk_ehci1_soft_reset(void *pdata, enum rkusb_rst_flag rst_type)
 {
+       struct rkehci_platform_data *usbpdata = pdata;
+       struct reset_control *rst_ehci1_h, *rst_ehci1_a, *rst_ehci1_p;
+
+       rst_ehci1_h = devm_reset_control_get(usbpdata->dev, "ehci1_ahb");
+       rst_ehci1_a = devm_reset_control_get(usbpdata->dev, "ehci1_aux");
+       rst_ehci1_p = devm_reset_control_get(usbpdata->dev, "ehci1_phy");
+
+       reset_control_assert(rst_ehci1_h);
+       reset_control_assert(rst_ehci1_a);
+       reset_control_assert(rst_ehci1_p);
+       udelay(5);
+       reset_control_deassert(rst_ehci1_h);
+       reset_control_deassert(rst_ehci1_a);
+       reset_control_deassert(rst_ehci1_p);
+       mdelay(2);
+
+       /* EHCI1 per-port reset */
+       control_usb->grf_uoc3_base->CON0 = ((1 << 10) << 16) | (1 << 10);
+       udelay(2);
+       control_usb->grf_uoc3_base->CON0 = ((1 << 10) << 16) | (0 << 10);
+       udelay(2);
 }
 
-struct rkehci_platform_data rkhsic_pdata_rk3288 = {
-       .hclk_hsic = NULL,
-       .hsic_phy_12m = NULL,
-       .hsic_phy_480m = NULL,
+struct rkehci_platform_data rkehci1_pdata_rk3288 = {
+       .hclk_ehci = NULL,
+       .ehci_phy_12m = NULL,
+       .ehci_phy_480m = NULL,
        .clk_status = -1,
-       .hw_init = rk_hsic_hw_init,
-       .clock_init = rk_hsic_clock_init,
-       .clock_enable = rk_hsic_clock_enable,
-       .soft_reset = rk_hsic_soft_reset,
+       .hw_init = rk_ehci1_hw_init,
+       .clock_init = rk_ehci1_clock_init,
+       .clock_enable = rk_ehci1_clock_enable,
+       .soft_reset = rk_ehci1_soft_reset,
 };
 #endif
 
 #ifdef CONFIG_USB_EHCI_RK
 static void rk_ehci_hw_init(void)
 {
+       /* usb phy config init */
+
+       /* 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 rk_ehci_phy_suspend(void *pdata, int suspend)
+{
+       struct rkehci_platform_data *usbpdata = pdata;
+
+       if (suspend) {
+               /* enable soft control */
+               control_usb->grf_uoc1_base->CON2 =
+                   (0x01 << 2) | ((0x01 << 2) << 16);
+               /* enter suspend */
+               control_usb->grf_uoc1_base->CON3 = 0x2A | (0x3F << 16);
+               usbpdata->phy_status = 1;
+       } else {
+               /* exit suspend */
+               control_usb->grf_uoc1_base->CON2 = ((0x01 << 2) << 16);
+               usbpdata->phy_status = 0;
+       }
+}
+
+static void rk_ehci_clock_init(void *pdata)
+{
+       struct rkehci_platform_data *usbpdata = pdata;
+       struct clk *ahbclk, *phyclk;
+
+       ahbclk = devm_clk_get(usbpdata->dev, "hclk_usb2");
+       if (IS_ERR(ahbclk)) {
+               dev_err(usbpdata->dev, "Failed to get hclk_usb2\n");
+               return;
+       }
+
+       phyclk = devm_clk_get(usbpdata->dev, "clk_usbphy2");
+       if (IS_ERR(phyclk)) {
+               dev_err(usbpdata->dev, "Failed to get clk_usbphy2\n");
+               return;
+       }
 
+       usbpdata->phyclk = phyclk;
+       usbpdata->ahbclk = ahbclk;
 }
 
-static void rk_ehci_clock_init(void* pdata)
+static void rk_ehci_clock_enable(void *pdata, int enable)
 {
+       struct rkehci_platform_data *usbpdata = pdata;
 
+       if (enable == usbpdata->clk_status)
+               return;
+       if (enable) {
+               clk_prepare_enable(usbpdata->ahbclk);
+               clk_prepare_enable(usbpdata->phyclk);
+               usbpdata->clk_status = 1;
+       } else {
+               clk_disable_unprepare(usbpdata->ahbclk);
+               clk_disable_unprepare(usbpdata->phyclk);
+               usbpdata->clk_status = 0;
+       }
 }
 
-static void rk_ehci_clock_enable(void* pdata, int enable)
+static void rk_ehci_soft_reset(void *pdata, enum rkusb_rst_flag rst_type)
 {
+       struct rkehci_platform_data *usbpdata = pdata;
+       struct reset_control *rst_host0_h, *rst_host0_p,
+                            *rst_host0_c , *rst_host0;
+
+       rst_host0_h = devm_reset_control_get(usbpdata->dev, "ehci_ahb");
+       rst_host0_p = devm_reset_control_get(usbpdata->dev, "ehci_phy");
+       rst_host0_c = devm_reset_control_get(usbpdata->dev, "ehci_controller");
+       rst_host0 = devm_reset_control_get(usbpdata->dev, "ehci");
+       if (IS_ERR(rst_host0_h) || IS_ERR(rst_host0_p) ||
+           IS_ERR(rst_host0_c) || IS_ERR(rst_host0)) {
+               dev_err(usbpdata->dev, "Fail to get reset control from dts\n");
+               return;
+       }
 
+       reset_control_assert(rst_host0_h);
+       reset_control_assert(rst_host0_p);
+       reset_control_assert(rst_host0_c);
+       reset_control_assert(rst_host0);
+       udelay(5);
+       reset_control_deassert(rst_host0_h);
+       reset_control_deassert(rst_host0_p);
+       reset_control_deassert(rst_host0_c);
+       reset_control_deassert(rst_host0);
+       mdelay(2);
 }
 
-static void rk_ehci_soft_reset(void)
+static int rk_ehci_get_status(int id)
 {
+       int ret = -1;
 
+       switch (id) {
+       case USB_STATUS_DPDM:
+               /* dpdm in grf */
+               ret = control_usb->grf_soc_status2_rk3288->host0_linestate;
+               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;
 }
 
 struct rkehci_platform_data rkehci_pdata_rk3288 = {
        .phyclk = NULL,
        .ahbclk = NULL,
        .clk_status = -1,
+       .phy_status = 0,
        .hw_init = rk_ehci_hw_init,
+       .phy_suspend = rk_ehci_phy_suspend,
        .clock_init = rk_ehci_clock_init,
        .clock_enable = rk_ehci_clock_enable,
        .soft_reset = rk_ehci_soft_reset,
+       .get_status = rk_ehci_get_status,
 };
 #endif
 
 #ifdef CONFIG_USB_OHCI_HCD_RK
 static void rk_ohci_hw_init(void)
 {
+       /* usb phy config init */
+
+       /* 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 rk_ohci_clock_init(voidpdata)
+static void rk_ohci_clock_init(void *pdata)
 {
+       struct rkehci_platform_data *usbpdata = pdata;
+       struct clk *ahbclk, *phyclk;
+
+       ahbclk = devm_clk_get(usbpdata->dev, "hclk_usb3");
+       if (IS_ERR(ahbclk)) {
+               dev_err(usbpdata->dev, "Failed to get hclk_usb3\n");
+               return;
+       }
+
+       phyclk = devm_clk_get(usbpdata->dev, "clk_usbphy3");
+       if (IS_ERR(phyclk)) {
+               dev_err(usbpdata->dev, "Failed to get clk_usbphy3\n");
+               return;
+       }
+
+       usbpdata->phyclk = phyclk;
+       usbpdata->ahbclk = ahbclk;
 }
 
-static void rk_ohci_clock_enable(voidpdata, int enable)
+static void rk_ohci_clock_enable(void *pdata, int enable)
 {
+       struct rkehci_platform_data *usbpdata = pdata;
+
+       if (enable == usbpdata->clk_status)
+               return;
+       if (enable) {
+               clk_prepare_enable(usbpdata->ahbclk);
+               clk_prepare_enable(usbpdata->phyclk);
+               usbpdata->clk_status = 1;
+       } else {
+               clk_disable_unprepare(usbpdata->ahbclk);
+               clk_disable_unprepare(usbpdata->phyclk);
+               usbpdata->clk_status = 0;
+       }
 }
 
-static void rk_ohci_soft_reset(void)
+static void rk_ohci_soft_reset(void *pdata, enum rkusb_rst_flag rst_type)
 {
 }
 
@@ -445,13 +683,19 @@ struct rkehci_platform_data rkohci_pdata_rk3288 = {
 #endif
 
 /*********************************************************************
-                        rk3288 usb detections 
+                       rk3288 usb detections
 *********************************************************************/
 
 #define WAKE_LOCK_TIMEOUT (HZ * 10)
-inline static void do_wakeup(struct work_struct *work)
+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)
 {
-//      rk28_send_wakeup_key();
+       rk_battery_charger_detect_cb(usb_battery_charger_detect(1));
 }
 
 /********** handler for bvalid irq **********/
@@ -460,161 +704,163 @@ static irqreturn_t bvalid_irq_handler(int irq, void *dev_id)
        /* clear irq */
        control_usb->grf_uoc0_base->CON4 = (0x0008 | (0x0008 << 16));
 
-#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);
+       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);
        }
+       schedule_delayed_work(&control_usb->usb_charger_det_work, HZ / 10);
+
        return IRQ_HANDLED;
 }
 
 /***** handler for otg id rise and fall edge *****/
 static irqreturn_t id_irq_handler(int irq, void *dev_id)
 {
-    unsigned int uoc_con;
-
-     /* clear irq */
-    uoc_con = control_usb->grf_uoc0_base->CON4;
-    
-    if(uoc_con & (1<<5))//id rise 
-    {
-        control_usb->grf_uoc0_base->CON4 = ((1<<5)|(1<<21)); //clear id rise irq pandding
-    }
-    
-    if(uoc_con & (1<<7))//id fall
-    { 
-#ifdef CONFIG_RK_USB_UART
-        /* usb otg dp/dm switch to usb phy */
-        dwc_otg_uart_mode(NULL, PHY_USB_MODE);
-#endif
-        control_usb->grf_uoc0_base->CON4 = ((1<<7)|(1<<23));//clear id fall irq pandding
-    }
+       unsigned int uoc_con;
+
+       /* clear irq */
+       uoc_con = control_usb->grf_uoc0_base->CON4;
+
+       /* id rise */
+       if (uoc_con & (1 << 5)) {
+               /* clear id rise irq pandding */
+               control_usb->grf_uoc0_base->CON4 = ((1 << 5) | (1 << 21));
+       }
+
+       /* id fall */
+       if (uoc_con & (1 << 7)) {
+               /* usb otg dp/dm switch to usb phy */
+               dwc_otg_uart_mode(NULL, PHY_USB_MODE);
+               /* clear id fall irq pandding */
+               control_usb->grf_uoc0_base->CON4 = ((1 << 7) | (1 << 23));
+       }
 
-       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);
+       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);
        }
 
-    return IRQ_HANDLED;
+       return IRQ_HANDLED;
 }
 
-/***** handler for otg line status change *****/
+#ifdef USB_LINESTATE_IRQ
+/***** handler for usb line status change *****/
 
 static irqreturn_t line_irq_handler(int irq, void *dev_id)
 {
-    unsigned int uoc_con;
-    /* clear irq */
-    
-    if(control_usb->grf_uoc0_base->CON0 & 1<<15){
-        control_usb->grf_uoc0_base->CON0 = (1<<15 | 1<<31);
-    }  
-    
-    if(control_usb->grf_uoc1_base->CON0 & 1<<15){
-        control_usb->grf_uoc1_base->CON0 = (1<<15 | 1<<31);
-    }    
-    
-    if(control_usb->grf_uoc2_base->CON0 & 1<<15){
-        control_usb->grf_uoc2_base->CON0 = (1<<15 | 1<<31);
-    }
+       /* clear irq */
+
+       if (control_usb->grf_uoc0_base->CON0 & 1 << 15)
+               control_usb->grf_uoc0_base->CON0 = (1 << 15 | 1 << 31);
 
-       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);
+       if (control_usb->grf_uoc1_base->CON0 & 1 << 15)
+               control_usb->grf_uoc1_base->CON0 = (1 << 15 | 1 << 31);
+
+       if (control_usb->grf_uoc2_base->CON0 & 1 << 15)
+               control_usb->grf_uoc2_base->CON0 = (1 << 15 | 1 << 31);
+
+       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);
        }
 
-    return IRQ_HANDLED;
+       return IRQ_HANDLED;
 }
+#endif
 
 /************* 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");
+       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*/
+       /*register otg_bvalid irq */
        irq = platform_get_irq_byname(pdev, "otg_bvalid");
-       if (irq > 0) {
-               ret = request_irq(irq, bvalid_irq_handler, 0, "otg_bvalid", NULL);
-               if(ret < 0){
+       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);
-                       return ret;
-               }else{
-                   control_usb->grf_uoc0_base->CON4 = 0x000c000c;// enable bvalid irq 
-                       if(control_usb->usb_irq_wakeup){
-                                       enable_irq_wake(irq);
-                       }
+               } else {
+                       /* enable bvalid irq  */
+                       control_usb->grf_uoc0_base->CON4 = 0x000c000c;
                }
        }
 
-    /*register otg_id irq*/
-    irq = platform_get_irq_byname(pdev, "otg_id");
-    if(irq > 0){
-        ret = request_irq(irq, id_irq_handler, 0, "otg_id", NULL);
-        if(ret < 0){
+       /*register otg_id irq */
+       irq = platform_get_irq_byname(pdev, "otg_id");
+       if ((irq > 0) && control_usb->usb_irq_wakeup) {
+               ret = request_irq(irq, id_irq_handler, 0, "otg_id", NULL);
+               if (ret < 0) {
                        dev_err(&pdev->dev, "request_irq %d failed!\n", irq);
-                       return ret;
-               }else{
-                   control_usb->grf_uoc0_base->CON4 = 0x00f000f0;
-                       if(control_usb->usb_irq_wakeup){
-                                       enable_irq_wake(irq);
-                       }
+               } else {
+                       /* enable otg_id irq */
+                       control_usb->grf_uoc0_base->CON4 = 0x00f000f0;
                }
-    }
-
-    /*register otg_linestate irq*/
-    irq = platform_get_irq_byname(pdev, "otg_linestate");
-    if(irq > 0){
-        ret = request_irq(irq, line_irq_handler, 0, "otg_linestate", NULL);
-        if(ret < 0){
+       }
+#if 0
+       /*register otg_linestate irq */
+       irq = platform_get_irq_byname(pdev, "otg_linestate");
+       if (irq > 0) {
+               ret =
+                   request_irq(irq, line_irq_handler, 0, "otg_linestate",
+                               NULL);
+               if (ret < 0) {
                        dev_err(&pdev->dev, "request_irq %d failed!\n", irq);
                        return ret;
-               }else{
-                   control_usb->grf_uoc0_base->CON0 = 0xc000c000;
-                       if(control_usb->usb_irq_wakeup){
-                                       enable_irq_wake(irq);
-                       }
+               } else {
+                       control_usb->grf_uoc0_base->CON0 = 0xc000c000;
+                       if (control_usb->usb_irq_wakeup)
+                               enable_irq_wake(irq);
                }
-    }
-    
-    /*register host0_linestate irq*/
-    irq = platform_get_irq_byname(pdev, "host0_linestate");
-    if(irq > 0){
-        ret = request_irq(irq, line_irq_handler, 0, "host0_linestate", NULL);
-        if(ret < 0){
+       }
+
+       /*register host0_linestate irq */
+       irq = platform_get_irq_byname(pdev, "host0_linestate");
+       if (irq > 0) {
+               ret =
+                   request_irq(irq, line_irq_handler, 0, "host0_linestate",
+                               NULL);
+               if (ret < 0) {
                        dev_err(&pdev->dev, "request_irq %d failed!\n", irq);
                        return ret;
-               }else{
-                   control_usb->grf_uoc1_base->CON0 = 0xc000c000;
-                       if(control_usb->usb_irq_wakeup){
-                                       enable_irq_wake(irq);
-                       }
+               } else {
+                       control_usb->grf_uoc1_base->CON0 = 0xc000c000;
+                       if (control_usb->usb_irq_wakeup)
+                               enable_irq_wake(irq);
                }
-    }
-    
-    /*register host1_linestate irq*/
-    irq = platform_get_irq_byname(pdev, "host1_linestate");
-    if(irq > 0){
-        ret = request_irq(irq, line_irq_handler, 0, "host1_linestate", NULL);
-        if(ret < 0){
+       }
+
+       /*register host1_linestate irq */
+       irq = platform_get_irq_byname(pdev, "host1_linestate");
+       if (irq > 0) {
+               ret =
+                   request_irq(irq, line_irq_handler, 0, "host1_linestate",
+                               NULL);
+               if (ret < 0) {
                        dev_err(&pdev->dev, "request_irq %d failed!\n", irq);
                        return ret;
-               }else{
-                   control_usb->grf_uoc2_base->CON0 = 0xc000c000;
-                       if(control_usb->usb_irq_wakeup){
-                                       enable_irq_wake(irq);
-                       }
+               } else {
+                       control_usb->grf_uoc2_base->CON0 = 0xc000c000;
+                       if (control_usb->usb_irq_wakeup)
+                               enable_irq_wake(irq);
                }
-    }
-       
+       }
+#endif
        return ret;
 }
 
@@ -635,207 +881,243 @@ static int usb_grf_ioremap(struct platform_device *pdev)
        void *grf_uoc4_base;
 
        res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
-                                               "GRF_SOC_STATUS1");
+                                          "GRF_SOC_STATUS1");
        grf_soc_status1 = devm_ioremap_resource(&pdev->dev, res);
-       if (IS_ERR(grf_soc_status1)){
+       if (IS_ERR(grf_soc_status1)) {
                ret = PTR_ERR(grf_soc_status1);
                return ret;
        }
-       control_usb->grf_soc_status1_rk3288 = (pGRF_SOC_STATUS1_RK3288)grf_soc_status1;
+       control_usb->grf_soc_status1_rk3288 =
+           (pGRF_SOC_STATUS1_RK3288) grf_soc_status1;
 
        res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
-                                               "GRF_SOC_STATUS2");
+                                          "GRF_SOC_STATUS2");
        grf_soc_status2 = devm_ioremap_resource(&pdev->dev, res);
-       if (IS_ERR(grf_soc_status2)){
+       if (IS_ERR(grf_soc_status2)) {
                ret = PTR_ERR(grf_soc_status2);
                return ret;
        }
-       control_usb->grf_soc_status2_rk3288 = (pGRF_SOC_STATUS2_RK3288)grf_soc_status2;
+       control_usb->grf_soc_status2_rk3288 =
+           (pGRF_SOC_STATUS2_RK3288) grf_soc_status2;
 
        res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
-                                               "GRF_SOC_STATUS19");
+                                          "GRF_SOC_STATUS19");
        grf_soc_status19 = devm_ioremap_resource(&pdev->dev, res);
-       if (IS_ERR(grf_soc_status19)){
+       if (IS_ERR(grf_soc_status19)) {
                ret = PTR_ERR(grf_soc_status19);
                return ret;
        }
-       control_usb->grf_soc_status19_rk3288 = (pGRF_SOC_STATUS19_RK3288)grf_soc_status19;
+       control_usb->grf_soc_status19_rk3288 =
+           (pGRF_SOC_STATUS19_RK3288) grf_soc_status19;
 
        res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
-                                               "GRF_SOC_STATUS21");
+                                          "GRF_SOC_STATUS21");
        grf_soc_status21 = devm_ioremap_resource(&pdev->dev, res);
-       if (IS_ERR(grf_soc_status21)){
+       if (IS_ERR(grf_soc_status21)) {
                ret = PTR_ERR(grf_soc_status21);
                return ret;
        }
-       control_usb->grf_soc_status21_rk3288 = (pGRF_SOC_STATUS21_RK3288)grf_soc_status21;
+       control_usb->grf_soc_status21_rk3288 =
+           (pGRF_SOC_STATUS21_RK3288) grf_soc_status21;
 
        res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
-                                               "GRF_UOC0_BASE");
+                                          "GRF_UOC0_BASE");
        grf_uoc0_base = devm_ioremap_resource(&pdev->dev, res);
-       if (IS_ERR(grf_uoc0_base)){
+       if (IS_ERR(grf_uoc0_base)) {
                ret = PTR_ERR(grf_uoc0_base);
                return ret;
        }
-       control_usb->grf_uoc0_base = (pGRF_UOC0_REG)grf_uoc0_base;
+       control_usb->grf_uoc0_base = (pGRF_UOC0_REG) grf_uoc0_base;
 
        res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
-                                               "GRF_UOC1_BASE");
+                                          "GRF_UOC1_BASE");
        grf_uoc1_base = devm_ioremap_resource(&pdev->dev, res);
-       if (IS_ERR(grf_uoc1_base)){
+       if (IS_ERR(grf_uoc1_base)) {
                ret = PTR_ERR(grf_uoc1_base);
                return ret;
        }
-       control_usb->grf_uoc1_base = (pGRF_UOC1_REG)grf_uoc1_base;
+       control_usb->grf_uoc1_base = (pGRF_UOC1_REG) grf_uoc1_base;
 
        res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
-                                               "GRF_UOC2_BASE");
+                                          "GRF_UOC2_BASE");
        grf_uoc2_base = devm_ioremap_resource(&pdev->dev, res);
-       if (IS_ERR(grf_uoc2_base)){
+       if (IS_ERR(grf_uoc2_base)) {
                ret = PTR_ERR(grf_uoc2_base);
                return ret;
        }
-       control_usb->grf_uoc2_base = (pGRF_UOC2_REG)grf_uoc2_base;
+       control_usb->grf_uoc2_base = (pGRF_UOC2_REG) grf_uoc2_base;
 
        res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
-                                               "GRF_UOC3_BASE");
+                                          "GRF_UOC3_BASE");
        grf_uoc3_base = devm_ioremap_resource(&pdev->dev, res);
-       if (IS_ERR(grf_uoc3_base)){
+       if (IS_ERR(grf_uoc3_base)) {
                ret = PTR_ERR(grf_uoc3_base);
                return ret;
        }
-       control_usb->grf_uoc3_base = (pGRF_UOC3_REG)grf_uoc3_base;
+       control_usb->grf_uoc3_base = (pGRF_UOC3_REG) grf_uoc3_base;
 
        res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
-                                               "GRF_UOC4_BASE");
+                                          "GRF_UOC4_BASE");
        grf_uoc4_base = devm_ioremap_resource(&pdev->dev, res);
-       if (IS_ERR(grf_uoc4_base)){
+       if (IS_ERR(grf_uoc4_base)) {
                ret = PTR_ERR(grf_uoc4_base);
                return ret;
        }
-       control_usb->grf_uoc4_base = (pGRF_UOC4_REG)grf_uoc4_base;
+       control_usb->grf_uoc4_base = (pGRF_UOC4_REG) grf_uoc4_base;
 
        return ret;
 }
 
-#ifdef CONFIG_OF
-
-static const struct of_device_id dwc_otg_control_usb_id_table[] = {
-       {
-               .compatible = "rockchip,rk3288-dwc-control-usb",
-       },
-       { },
-};
-
-#endif
-
-static int dwc_otg_control_usb_probe(struct platform_device *pdev)
+static int rk_usb_control_probe(struct platform_device *pdev)
 {
        int gpio, err;
        struct device_node *np = pdev->dev.of_node;
-//     struct clk* hclk_usb_peri;
        int ret = 0;
 
-       control_usb = devm_kzalloc(&pdev->dev, sizeof(*control_usb),GFP_KERNEL);
+       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 err1;
+               ret = -ENOMEM;
+               goto out;
        }
 
        control_usb->chip_id = RK3288_USB_CTLR;
        control_usb->remote_wakeup = of_property_read_bool(np,
-               "rockchip,remote_wakeup");
+                                                          "rockchip,remote_wakeup");
        control_usb->usb_irq_wakeup = of_property_read_bool(np,
-               "rockchip,usb_irq_wakeup");
+                                                           "rockchip,usb_irq_wakeup");
+
+       INIT_DELAYED_WORK(&control_usb->usb_charger_det_work,
+                         usb_battery_charger_detect_work);
 
-/*     disable for debug
-       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->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;
        }
 
-       control_usb->hclk_usb_peri = hclk_usb_peri;
-       clk_prepare_enable(hclk_usb_peri);
-*/
-       ret = usb_grf_ioremap(pdev);
-       if(ret){
-               dev_err(&pdev->dev, "Failed to ioremap usb grf\n");
-               goto err2;
+       gpio = of_get_named_gpio(np, "host_drv_gpio", 0);
+       control_usb->host_gpios->gpio = gpio;
+
+       if (!gpio_is_valid(gpio)) {
+               dev_warn(&pdev->dev, "host_drv_gpio is not specified or invalid\n");
+       } 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->host_gpios = devm_kzalloc(&pdev->dev, sizeof(struct gpio), GFP_KERNEL);
 
-       gpio =  of_get_named_gpio(np, "gpios", 0);
-       if(!gpio_is_valid(gpio)){
-               dev_err(&pdev->dev, "invalid host gpio%d\n", gpio);
-               ret = -EINVAL;
-               goto err2;
+       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;
        }
-       control_usb->host_gpios->gpio = gpio;
-       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 err2;
+
+       gpio = of_get_named_gpio(np, "otg_drv_gpio", 0);
+       control_usb->otg_gpios->gpio = gpio;
+
+       if (!gpio_is_valid(gpio)) {
+               dev_warn(&pdev->dev, "otg_drv_gpio is not specified or invalid\n");
+       } 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);
        }
 
-       control_usb->otg_gpios = devm_kzalloc(&pdev->dev, sizeof(struct gpio), GFP_KERNEL);
+out:
+       return ret;
+}
 
-       gpio =  of_get_named_gpio(np, "gpios", 1);
-       if(!gpio_is_valid(gpio)){
-               dev_err(&pdev->dev, "invalid otg gpio%d\n", gpio);
-               ret = -EINVAL;
-               goto err2;
+#ifdef CONFIG_OF
+static const struct of_device_id dwc_otg_control_usb_id_table[] = {
+       {
+        .compatible = "rockchip,rk3288-dwc-control-usb",
+        },
+       {},
+};
+#endif
+
+static int dwc_otg_control_usb_probe(struct platform_device *pdev)
+{
+       int ret = 0;
+
+       ret = rk_usb_control_probe(pdev);
+       if (ret)
+               return ret;
+
+       control_usb->hclk_usb_peri = devm_clk_get(&pdev->dev, "hclk_usb_peri");
+       if (IS_ERR(control_usb->hclk_usb_peri)) {
+               dev_info(&pdev->dev, "no hclk_usb_peri specified\n");
+               control_usb->hclk_usb_peri = NULL;
        }
-       control_usb->otg_gpios->gpio = gpio;
-       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 err2;
-       }
-*/
-/* disable for debug
+
+       clk_prepare_enable(control_usb->hclk_usb_peri);
+
+       ret = usb_grf_ioremap(pdev);
+       if (ret) {
+               dev_err(&pdev->dev, "Failed to ioremap usb grf\n");
+               goto err;
+       }
+#ifdef CONFIG_USB20_OTG
+       if (usb20otg_get_status(USB_STATUS_BVABLID))
+               schedule_delayed_work(&control_usb->usb_charger_det_work,
+                                     HZ / 10);
+#endif
+
        ret = otg_irq_detect_init(pdev);
        if (ret < 0)
-               goto err2;
-*/
+               goto err;
+
        return 0;
 
-err2:
-//     disable for debug
-//     clk_disable_unprepare(hclk_usb_peri);
-err1:
+err:
+       clk_disable_unprepare(control_usb->hclk_usb_peri);
        return ret;
 }
 
 static int dwc_otg_control_usb_remove(struct platform_device *pdev)
 {
-//     disable for debug
-//     clk_disable_unprepare(control_usb->hclk_usb_peri);
+       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   = "rk3288-dwc-control-usb",
-               .owner  = THIS_MODULE,
-               .of_match_table = of_match_ptr(dwc_otg_control_usb_id_table),
-       },
+       .probe = dwc_otg_control_usb_probe,
+       .remove = dwc_otg_control_usb_remove,
+       .driver = {
+                  .name = "rk3288-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)
 {
-       return platform_driver_register(&dwc_otg_control_usb_driver);
+       int retval = 0;
+
+       retval = platform_driver_register(&dwc_otg_control_usb_driver);
+
+       if (retval < 0) {
+               printk(KERN_ERR "%s retval=%d\n", __func__, retval);
+               return retval;
+       }
+       return retval;
 }
 
 subsys_initcall(dwc_otg_control_usb_init);
@@ -850,3 +1132,4 @@ MODULE_ALIAS("platform: dwc_control_usb");
 MODULE_AUTHOR("RockChip Inc.");
 MODULE_DESCRIPTION("RockChip Control Module USB Driver");
 MODULE_LICENSE("GPL v2");
+#endif