usb: dwc_otg_310: fix usb vbus power controlled by pmic
[firefly-linux-kernel-4.4.55.git] / drivers / usb / dwc_otg_310 / dwc_otg_driver.c
old mode 100755 (executable)
new mode 100644 (file)
index cbd3f36..4463f1b
@@ -68,6 +68,7 @@ static const char dwc_host20_driver_name[] = "usb20_host";
 static const char dwc_otg20_driver_name[] = "usb20_otg";
 
 dwc_otg_device_t *g_otgdev;
+void *dwc_otg_dev;
 
 extern int pcd_init(struct platform_device *_dev);
 extern int otg20_hcd_init(struct platform_device *_dev);
@@ -77,8 +78,9 @@ extern void hcd_remove(struct platform_device *_dev);
 extern void dwc_otg_adp_start(dwc_otg_core_if_t *core_if, uint8_t is_host);
 
 
-
+#ifdef CONFIG_RK_USB_UART
 static u32 usb_to_uart_status;
+#endif
 /*-------------------------------------------------------------------------*/
 /* Encapsulate the module parameter settings */
 
@@ -350,13 +352,21 @@ extern void dwc_otg_hub_disconnect_device(struct usb_hub *hub);
 void dwc_otg_force_host(dwc_otg_core_if_t *core_if)
 {
        dwc_otg_device_t *otg_dev = core_if->otg_dev;
+       struct dwc_otg_platform_data *pdata = otg_dev->pldata;
        dctl_data_t dctl = {.d32 = 0 };
        unsigned long flags;
 
        if (core_if->op_state == A_HOST) {
-               printk("dwc_otg_force_host,already in A_HOST mode,everest\n");
+               dev_info(pdata->dev,
+                        "dwc_otg_force_host,already in A_HOST mode,everest\n");
+               return;
+       } else if (pdata->get_status(USB_STATUS_BVABLID) &&
+                  core_if->pmic_vbus) {
+               dev_info(pdata->dev,
+                        "Please disconnect the USB cable first, and try again!\n");
                return;
        }
+
        core_if->op_state = A_HOST;
 
        cancel_delayed_work(&otg_dev->pcd->check_vbus_work);
@@ -384,6 +394,7 @@ void dwc_otg_force_device(dwc_otg_core_if_t *core_if)
        local_irq_save(flags);
 
        if (core_if->op_state == B_PERIPHERAL) {
+               local_irq_restore(flags);
                printk
                    ("dwc_otg_force_device,already in B_PERIPHERAL,everest\n");
                return;
@@ -914,6 +925,7 @@ static int host20_driver_remove(struct platform_device *_dev)
 }
 
 static const struct of_device_id usb20_host_of_match[] = {
+#ifdef CONFIG_ARM
        {
         .compatible = "rockchip,rk3188_usb20_host",
         .data = &usb20host_pdata_rk3188,
@@ -930,6 +942,7 @@ static const struct of_device_id usb20_host_of_match[] = {
         .compatible = "rockchip,rk3126_usb20_host",
         .data = &usb20host_pdata_rk3126,
         },
+#endif
        {},
 };
 
@@ -959,15 +972,13 @@ static int host20_driver_probe(struct platform_device *_dev)
            of_match_device(of_match_ptr(usb20_host_of_match), &_dev->dev);
 
        if (match && match->data) {
-               dev->platform_data = (void *)match->data;
+               pldata = (void *)match->data;
+               pldata->dev = dev;
        } else {
-               dev_err(dev, "usb20host match failed\n");
+               dev_err(dev, "usb20otg match failed\n");
                return -EINVAL;
        }
 
-       pldata = dev->platform_data;
-       pldata->dev = dev;
-
        if (!node) {
                dev_err(dev, "device node not found\n");
                return -EINVAL;
@@ -1012,8 +1023,7 @@ static int host20_driver_probe(struct platform_device *_dev)
                retval = -ENOMEM;
                goto clk_disable;
        }
-       dev_dbg(&_dev->dev, "base=0x%08x\n",
-               (unsigned)dwc_otg_device->os_dep.base);
+       dev_dbg(&_dev->dev, "base=0x%p\n", dwc_otg_device->os_dep.base);
 
        /* Set device flags indicating whether the HCD supports DMA. */
        if (!_dev->dev.dma_mask)
@@ -1156,22 +1166,20 @@ static int dwc_otg_driver_resume(struct platform_device *_dev)
 static void dwc_otg_driver_shutdown(struct platform_device *_dev)
 {
        struct device *dev = &_dev->dev;
-       struct dwc_otg_platform_data *pldata = dev->platform_data;
        dwc_otg_device_t *otg_dev = dev->platform_data;
        dwc_otg_core_if_t *core_if = otg_dev->core_if;
+       struct dwc_otg_platform_data *pldata = otg_dev->pldata;
        dctl_data_t dctl = {.d32 = 0 };
+       dwc_otg_pcd_t *pcd = core_if->otg_dev->pcd;
 
        DWC_PRINTF("%s: disconnect USB %s mode\n", __func__,
                   dwc_otg_is_host_mode(core_if) ? "host" : "device");
 
-    if( pldata->dwc_otg_uart_mode != NULL)
-        pldata->dwc_otg_uart_mode( pldata, PHY_USB_MODE);
-    if(pldata->phy_suspend != NULL)
-        pldata->phy_suspend(pldata, USB_PHY_ENABLED);
        if (dwc_otg_is_host_mode(core_if)) {
                if (core_if->hcd_cb && core_if->hcd_cb->stop)
                        core_if->hcd_cb->stop(core_if->hcd_cb_p);
        } else {
+               cancel_delayed_work_sync(&pcd->check_vbus_work);
                /* soft disconnect */
                dctl.d32 =
                    DWC_READ_REG32(&core_if->dev_if->dev_global_regs->dctl);
@@ -1182,6 +1190,11 @@ static void dwc_otg_driver_shutdown(struct platform_device *_dev)
        /* Clear any pending interrupts */
        DWC_WRITE_REG32(&core_if->core_global_regs->gintsts, 0xFFFFFFFF);
 
+       if (pldata->dwc_otg_uart_mode != NULL)
+               pldata->dwc_otg_uart_mode(pldata, PHY_USB_MODE);
+       if (pldata->phy_suspend != NULL)
+               pldata->phy_suspend(pldata, USB_PHY_ENABLED);
+
 }
 
 /**
@@ -1287,6 +1300,7 @@ static int otg20_driver_remove(struct platform_device *_dev)
 }
 
 static const struct of_device_id usb20_otg_of_match[] = {
+#ifdef CONFIG_ARM
        {
         .compatible = "rockchip,rk3188_usb20_otg",
         .data = &usb20otg_pdata_rk3188,
@@ -1303,6 +1317,13 @@ static const struct of_device_id usb20_otg_of_match[] = {
         .compatible = "rockchip,rk3126_usb20_otg",
         .data = &usb20otg_pdata_rk3126,
         },
+#endif
+#ifdef CONFIG_ARM64
+       {
+        .compatible = "rockchip,rk3368-usb",
+        .data = &usb20otg_pdata_rk3368,
+        },
+#endif
        { },
 };
 
@@ -1332,21 +1353,19 @@ static int otg20_driver_probe(struct platform_device *_dev)
        const struct of_device_id *match =
            of_match_device(of_match_ptr(usb20_otg_of_match), &_dev->dev);
 
-       if (match) {
-               dev->platform_data = (void *)match->data;
+       if (match && match->data) {
+               pldata = (void *)match->data;
+               pldata->dev = dev;
        } else {
                dev_err(dev, "usb20otg match failed\n");
                return -EINVAL;
        }
 
-       pldata = dev->platform_data;
-       pldata->dev = dev;
-
        if (!node) {
                dev_err(dev, "device node not found\n");
                return -EINVAL;
        }
-       /*todo : move to usbdev_rk-XX.c */
+
        if (pldata->hw_init)
                pldata->hw_init();
 
@@ -1395,8 +1414,7 @@ static int otg20_driver_probe(struct platform_device *_dev)
                retval = -ENOMEM;
                goto clk_disable;
        }
-       dev_dbg(&_dev->dev, "base=0x%08x\n",
-               (unsigned)dwc_otg_device->os_dep.base);
+       dev_dbg(&_dev->dev, "base=0x%p\n", dwc_otg_device->os_dep.base);
 
        /* Set device flags indicating whether the HCD supports DMA. */
        if (!_dev->dev.dma_mask)
@@ -1411,6 +1429,7 @@ static int otg20_driver_probe(struct platform_device *_dev)
         */
 
        g_otgdev = dwc_otg_device;
+       dwc_otg_dev = (struct device *)&_dev->dev;
        pldata->privdata = dwc_otg_device;
        dwc_otg_device->pldata = pldata;
 
@@ -1493,6 +1512,10 @@ static int otg20_driver_probe(struct platform_device *_dev)
        of_property_read_u32(node, "rockchip,usb-mode", &val);
        dwc_otg_device->core_if->usb_mode = val;
 
+       /* Indicate usb vbus get from pmic (e.g. rk81x) */
+       dwc_otg_device->core_if->pmic_vbus = of_property_read_bool(node,
+                                               "rockchip,usb-pmic-vbus");
+
 #ifndef DWC_HOST_ONLY
        /*
         * Initialize the PCD
@@ -1521,7 +1544,8 @@ static int otg20_driver_probe(struct platform_device *_dev)
         * perform initial actions required for Internal ADP logic.
         */
        if (!dwc_otg_get_param_adp_enable(dwc_otg_device->core_if)) {
-               if (pldata->phy_status == USB_PHY_ENABLED) {
+               if (dwc_otg_device->core_if->usb_mode == USB_MODE_NORMAL &&
+                   pldata->phy_status == USB_PHY_ENABLED) {
                        pldata->phy_suspend(pldata, USB_PHY_SUSPEND);
                        udelay(3);
                        pldata->clock_enable(pldata, 0);
@@ -1544,10 +1568,59 @@ clk_disable:
        return retval;
 }
 
+#ifdef CONFIG_PM
+static int dwc_otg_pm_suspend(struct device *dev)
+{
+       dwc_otg_device_t *dwc_otg_device;
+       struct dwc_otg_platform_data *pdata_otg;
+
+       dwc_otg_device = dev_get_platdata(dev);
+
+       dev_dbg(dev, "dwc_otg PM suspend\n");
+
+       if (dwc_otg_device->core_if->op_state == B_PERIPHERAL)
+               return 0;
+
+       pdata_otg = dwc_otg_device->pldata;
+       pdata_otg->phy_suspend(pdata_otg, USB_PHY_SUSPEND);
+
+       return 0;
+}
+
+static int dwc_otg_pm_resume(struct device *dev)
+{
+       dwc_otg_device_t *dwc_otg_device;
+       struct dwc_otg_platform_data *pdata_otg;
+
+       dwc_otg_device = dev_get_platdata(dev);
+
+       dev_dbg(dev, "dwc_otg PM resume\n");
+
+       if (dwc_otg_device->core_if->op_state == B_PERIPHERAL)
+               return 0;
+
+       pdata_otg = dwc_otg_device->pldata;
+       pdata_otg->phy_suspend(pdata_otg, USB_PHY_ENABLED);
+
+       return 0;
+}
+#else
+#define dwc_otg_pm_suspend     NULL
+#define dwc_otg_pm_resume      NULL
+#endif
+
+static const struct dev_pm_ops dwc_otg_dev_pm_ops = {
+       .suspend = dwc_otg_pm_suspend,
+       .resume = dwc_otg_pm_resume,
+};
+
 static struct platform_driver dwc_otg_driver = {
        .driver = {
                   .name = (char *)dwc_otg20_driver_name,
                   .of_match_table = of_match_ptr(usb20_otg_of_match),
+#ifdef CONFIG_PM
+                  .pm = &dwc_otg_dev_pm_ops,
+#endif
                   },
        .probe = otg20_driver_probe,
        .remove = otg20_driver_remove,
@@ -1560,14 +1633,14 @@ static struct platform_driver dwc_otg_driver = {
 void rk_usb_power_up(void)
 {
        struct dwc_otg_platform_data *pldata_otg;
+#ifdef CONFIG_USB20_HOST
        struct dwc_otg_platform_data *pldata_host;
+#endif
+#ifdef CONFIG_USB_EHCI_RK
        struct rkehci_platform_data *pldata_ehci;
-       if (cpu_is_rk312x()) {
-               pldata_otg = &usb20otg_pdata_rk3126;
-               if (usb_to_uart_status)
-                       pldata_otg->dwc_otg_uart_mode(pldata_otg, PHY_UART_MODE);
-       }
-       if (cpu_is_rk3288()) {
+#endif
+
+       if (is_rk3288_usb()) {
 #ifdef CONFIG_RK_USB_UART
                /* enable USB bypass UART function  */
                writel_relaxed(0x00c00000 | usb_to_uart_status,
@@ -1603,21 +1676,29 @@ void rk_usb_power_up(void)
                }
 #endif
 
+       } else {
+               dwc_otg_device_t *otg_dev = g_otgdev;
+
+               if (!otg_dev)
+                       return;
+
+               pldata_otg = otg_dev->pldata;
+               if (pldata_otg && pldata_otg->phy_power_down)
+                       pldata_otg->phy_power_down(PHY_POWER_UP);
        }
 }
 
 void rk_usb_power_down(void)
 {
        struct dwc_otg_platform_data *pldata_otg;
+#ifdef CONFIG_USB20_HOST
        struct dwc_otg_platform_data *pldata_host;
+#endif
+#ifdef CONFIG_USB_EHCI_RK
        struct rkehci_platform_data *pldata_ehci;
+#endif
 
-       if (cpu_is_rk312x()) {
-               pldata_otg = &usb20otg_pdata_rk3126;
-               usb_to_uart_status = pldata_otg->get_status(USB_STATUS_UARTMODE);
-               pldata_otg->dwc_otg_uart_mode(pldata_otg, PHY_USB_MODE);
-       }
-       if (cpu_is_rk3288()) {
+       if (is_rk3288_usb()) {
 #ifdef CONFIG_RK_USB_UART
                /* disable USB bypass UART function */
                usb_to_uart_status =
@@ -1660,6 +1741,15 @@ void rk_usb_power_down(void)
                                               RK3288_GRF_UOC1_CON0);
                }
 #endif
+       } else {
+               dwc_otg_device_t *otg_dev = g_otgdev;
+
+               if (!otg_dev)
+                       return;
+
+               pldata_otg = otg_dev->pldata;
+               if (pldata_otg && pldata_otg->phy_power_down)
+                       pldata_otg->phy_power_down(PHY_POWER_DOWN);
        }
 }