These changes are the raw update to linux-4.4.6-rt14. Kernel sources
[kvmfornfv.git] / kernel / drivers / usb / dwc2 / platform.c
index 185663e..39c1cbf 100644 (file)
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/slab.h>
+#include <linux/clk.h>
 #include <linux/device.h>
 #include <linux/dma-mapping.h>
 #include <linux/of_device.h>
 #include <linux/mutex.h>
 #include <linux/platform_device.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_data/s3c-hsotg.h>
 
 #include <linux/usb/of.h>
 
 #include "core.h"
 #include "hcd.h"
+#include "debug.h"
 
 static const char dwc2_driver_name[] = "dwc2";
 
@@ -76,6 +80,8 @@ static const struct dwc2_core_params params_bcm2835 = {
        .reload_ctl                     = 0,
        .ahbcfg                         = 0x10,
        .uframe_sched                   = 0,
+       .external_id_pin_ctl            = -1,
+       .hibernation                    = -1,
 };
 
 static const struct dwc2_core_params params_rk3066 = {
@@ -102,10 +108,177 @@ static const struct dwc2_core_params params_rk3066 = {
        .host_ls_low_power_phy_clk      = -1,
        .ts_dline                       = -1,
        .reload_ctl                     = -1,
-       .ahbcfg                         = 0x7, /* INCR16 */
+       .ahbcfg                         = GAHBCFG_HBSTLEN_INCR16 <<
+                                         GAHBCFG_HBSTLEN_SHIFT,
        .uframe_sched                   = -1,
+       .external_id_pin_ctl            = -1,
+       .hibernation                    = -1,
 };
 
+static int __dwc2_lowlevel_hw_enable(struct dwc2_hsotg *hsotg)
+{
+       struct platform_device *pdev = to_platform_device(hsotg->dev);
+       int ret;
+
+       ret = regulator_bulk_enable(ARRAY_SIZE(hsotg->supplies),
+                                   hsotg->supplies);
+       if (ret)
+               return ret;
+
+       if (hsotg->clk) {
+               ret = clk_prepare_enable(hsotg->clk);
+               if (ret)
+                       return ret;
+       }
+
+       if (hsotg->uphy)
+               ret = usb_phy_init(hsotg->uphy);
+       else if (hsotg->plat && hsotg->plat->phy_init)
+               ret = hsotg->plat->phy_init(pdev, hsotg->plat->phy_type);
+       else {
+               ret = phy_power_on(hsotg->phy);
+               if (ret == 0)
+                       ret = phy_init(hsotg->phy);
+       }
+
+       return ret;
+}
+
+/**
+ * dwc2_lowlevel_hw_enable - enable platform lowlevel hw resources
+ * @hsotg: The driver state
+ *
+ * A wrapper for platform code responsible for controlling
+ * low-level USB platform resources (phy, clock, regulators)
+ */
+int dwc2_lowlevel_hw_enable(struct dwc2_hsotg *hsotg)
+{
+       int ret = __dwc2_lowlevel_hw_enable(hsotg);
+
+       if (ret == 0)
+               hsotg->ll_hw_enabled = true;
+       return ret;
+}
+
+static int __dwc2_lowlevel_hw_disable(struct dwc2_hsotg *hsotg)
+{
+       struct platform_device *pdev = to_platform_device(hsotg->dev);
+       int ret = 0;
+
+       if (hsotg->uphy)
+               usb_phy_shutdown(hsotg->uphy);
+       else if (hsotg->plat && hsotg->plat->phy_exit)
+               ret = hsotg->plat->phy_exit(pdev, hsotg->plat->phy_type);
+       else {
+               ret = phy_exit(hsotg->phy);
+               if (ret == 0)
+                       ret = phy_power_off(hsotg->phy);
+       }
+       if (ret)
+               return ret;
+
+       if (hsotg->clk)
+               clk_disable_unprepare(hsotg->clk);
+
+       ret = regulator_bulk_disable(ARRAY_SIZE(hsotg->supplies),
+                                    hsotg->supplies);
+
+       return ret;
+}
+
+/**
+ * dwc2_lowlevel_hw_disable - disable platform lowlevel hw resources
+ * @hsotg: The driver state
+ *
+ * A wrapper for platform code responsible for controlling
+ * low-level USB platform resources (phy, clock, regulators)
+ */
+int dwc2_lowlevel_hw_disable(struct dwc2_hsotg *hsotg)
+{
+       int ret = __dwc2_lowlevel_hw_disable(hsotg);
+
+       if (ret == 0)
+               hsotg->ll_hw_enabled = false;
+       return ret;
+}
+
+static int dwc2_lowlevel_hw_init(struct dwc2_hsotg *hsotg)
+{
+       int i, ret;
+
+       /* Set default UTMI width */
+       hsotg->phyif = GUSBCFG_PHYIF16;
+
+       /*
+        * Attempt to find a generic PHY, then look for an old style
+        * USB PHY and then fall back to pdata
+        */
+       hsotg->phy = devm_phy_get(hsotg->dev, "usb2-phy");
+       if (IS_ERR(hsotg->phy)) {
+               ret = PTR_ERR(hsotg->phy);
+               switch (ret) {
+               case -ENODEV:
+               case -ENOSYS:
+                       hsotg->phy = NULL;
+                       break;
+               case -EPROBE_DEFER:
+                       return ret;
+               default:
+                       dev_err(hsotg->dev, "error getting phy %d\n", ret);
+                       return ret;
+               }
+       }
+
+       if (!hsotg->phy) {
+               hsotg->uphy = devm_usb_get_phy(hsotg->dev, USB_PHY_TYPE_USB2);
+               if (IS_ERR(hsotg->uphy)) {
+                       ret = PTR_ERR(hsotg->uphy);
+                       switch (ret) {
+                       case -ENODEV:
+                       case -ENXIO:
+                               hsotg->uphy = NULL;
+                               break;
+                       case -EPROBE_DEFER:
+                               return ret;
+                       default:
+                               dev_err(hsotg->dev, "error getting usb phy %d\n",
+                                       ret);
+                               return ret;
+                       }
+               }
+       }
+
+       hsotg->plat = dev_get_platdata(hsotg->dev);
+
+       if (hsotg->phy) {
+               /*
+                * If using the generic PHY framework, check if the PHY bus
+                * width is 8-bit and set the phyif appropriately.
+                */
+               if (phy_get_bus_width(hsotg->phy) == 8)
+                       hsotg->phyif = GUSBCFG_PHYIF8;
+       }
+
+       /* Clock */
+       hsotg->clk = devm_clk_get(hsotg->dev, "otg");
+       if (IS_ERR(hsotg->clk)) {
+               hsotg->clk = NULL;
+               dev_dbg(hsotg->dev, "cannot get otg clock\n");
+       }
+
+       /* Regulators */
+       for (i = 0; i < ARRAY_SIZE(hsotg->supplies); i++)
+               hsotg->supplies[i].supply = dwc2_hsotg_supply_names[i];
+
+       ret = devm_regulator_bulk_get(hsotg->dev, ARRAY_SIZE(hsotg->supplies),
+                                     hsotg->supplies);
+       if (ret) {
+               dev_err(hsotg->dev, "failed to request supplies: %d\n", ret);
+               return ret;
+       }
+       return 0;
+}
+
 /**
  * dwc2_driver_remove() - Called when the DWC_otg core is unregistered with the
  * DWC_otg driver
@@ -121,10 +294,14 @@ static int dwc2_driver_remove(struct platform_device *dev)
 {
        struct dwc2_hsotg *hsotg = platform_get_drvdata(dev);
 
+       dwc2_debugfs_exit(hsotg);
        if (hsotg->hcd_enabled)
                dwc2_hcd_remove(hsotg);
        if (hsotg->gadget_enabled)
-               s3c_hsotg_remove(hsotg);
+               dwc2_hsotg_remove(hsotg);
+
+       if (hsotg->ll_hw_enabled)
+               dwc2_lowlevel_hw_disable(hsotg);
 
        return 0;
 }
@@ -157,8 +334,6 @@ static int dwc2_driver_probe(struct platform_device *dev)
        struct dwc2_core_params defparams;
        struct dwc2_hsotg *hsotg;
        struct resource *res;
-       struct phy *phy;
-       struct usb_phy *uphy;
        int retval;
        int irq;
 
@@ -192,6 +367,40 @@ static int dwc2_driver_probe(struct platform_device *dev)
        if (retval)
                return retval;
 
+       res = platform_get_resource(dev, IORESOURCE_MEM, 0);
+       hsotg->regs = devm_ioremap_resource(&dev->dev, res);
+       if (IS_ERR(hsotg->regs))
+               return PTR_ERR(hsotg->regs);
+
+       dev_dbg(&dev->dev, "mapped PA %08lx to VA %p\n",
+               (unsigned long)res->start, hsotg->regs);
+
+       hsotg->dr_mode = usb_get_dr_mode(&dev->dev);
+       if (IS_ENABLED(CONFIG_USB_DWC2_HOST) &&
+                       hsotg->dr_mode != USB_DR_MODE_HOST) {
+               hsotg->dr_mode = USB_DR_MODE_HOST;
+               dev_warn(hsotg->dev,
+                       "Configuration mismatch. Forcing host mode\n");
+       } else if (IS_ENABLED(CONFIG_USB_DWC2_PERIPHERAL) &&
+                       hsotg->dr_mode != USB_DR_MODE_PERIPHERAL) {
+               hsotg->dr_mode = USB_DR_MODE_PERIPHERAL;
+               dev_warn(hsotg->dev,
+                       "Configuration mismatch. Forcing peripheral mode\n");
+       }
+
+       retval = dwc2_lowlevel_hw_init(hsotg);
+       if (retval)
+               return retval;
+
+       spin_lock_init(&hsotg->lock);
+
+       hsotg->core_params = devm_kzalloc(&dev->dev,
+                               sizeof(*hsotg->core_params), GFP_KERNEL);
+       if (!hsotg->core_params)
+               return -ENOMEM;
+
+       dwc2_set_all_params(hsotg->core_params, -1);
+
        irq = platform_get_irq(dev, 0);
        if (irq < 0) {
                dev_err(&dev->dev, "missing IRQ resource\n");
@@ -206,56 +415,47 @@ static int dwc2_driver_probe(struct platform_device *dev)
        if (retval)
                return retval;
 
-       res = platform_get_resource(dev, IORESOURCE_MEM, 0);
-       hsotg->regs = devm_ioremap_resource(&dev->dev, res);
-       if (IS_ERR(hsotg->regs))
-               return PTR_ERR(hsotg->regs);
-
-       dev_dbg(&dev->dev, "mapped PA %08lx to VA %p\n",
-               (unsigned long)res->start, hsotg->regs);
-
-       hsotg->dr_mode = of_usb_get_dr_mode(dev->dev.of_node);
+       retval = dwc2_lowlevel_hw_enable(hsotg);
+       if (retval)
+               return retval;
 
-       /*
-        * Attempt to find a generic PHY, then look for an old style
-        * USB PHY
-        */
-       phy = devm_phy_get(&dev->dev, "usb2-phy");
-       if (IS_ERR(phy)) {
-               hsotg->phy = NULL;
-               uphy = devm_usb_get_phy(&dev->dev, USB_PHY_TYPE_USB2);
-               if (IS_ERR(uphy))
-                       hsotg->uphy = NULL;
-               else
-                       hsotg->uphy = uphy;
-       } else {
-               hsotg->phy = phy;
-               phy_power_on(hsotg->phy);
-               phy_init(hsotg->phy);
-       }
+       /* Detect config values from hardware */
+       retval = dwc2_get_hwparams(hsotg);
+       if (retval)
+               goto error;
 
-       spin_lock_init(&hsotg->lock);
-       mutex_init(&hsotg->init_mutex);
+       /* Validate parameter values */
+       dwc2_set_parameters(hsotg, params);
 
        if (hsotg->dr_mode != USB_DR_MODE_HOST) {
                retval = dwc2_gadget_init(hsotg, irq);
                if (retval)
-                       return retval;
+                       goto error;
                hsotg->gadget_enabled = 1;
        }
 
        if (hsotg->dr_mode != USB_DR_MODE_PERIPHERAL) {
-               retval = dwc2_hcd_init(hsotg, irq, params);
+               retval = dwc2_hcd_init(hsotg, irq);
                if (retval) {
                        if (hsotg->gadget_enabled)
-                               s3c_hsotg_remove(hsotg);
-                       return retval;
+                               dwc2_hsotg_remove(hsotg);
+                       goto error;
                }
                hsotg->hcd_enabled = 1;
        }
 
        platform_set_drvdata(dev, hsotg);
 
+       dwc2_debugfs_init(hsotg);
+
+       /* Gadget code manages lowlevel hw on its own */
+       if (hsotg->dr_mode == USB_DR_MODE_PERIPHERAL)
+               dwc2_lowlevel_hw_disable(hsotg);
+
+       return 0;
+
+error:
+       dwc2_lowlevel_hw_disable(hsotg);
        return retval;
 }
 
@@ -264,15 +464,12 @@ static int __maybe_unused dwc2_suspend(struct device *dev)
        struct dwc2_hsotg *dwc2 = dev_get_drvdata(dev);
        int ret = 0;
 
-       if (dwc2_is_device_mode(dwc2)) {
-               ret = s3c_hsotg_suspend(dwc2);
-       } else {
-               if (dwc2->lx_state == DWC2_L0)
-                       return 0;
-               phy_exit(dwc2->phy);
-               phy_power_off(dwc2->phy);
+       if (dwc2_is_device_mode(dwc2))
+               dwc2_hsotg_suspend(dwc2);
+
+       if (dwc2->ll_hw_enabled)
+               ret = __dwc2_lowlevel_hw_disable(dwc2);
 
-       }
        return ret;
 }
 
@@ -281,13 +478,15 @@ static int __maybe_unused dwc2_resume(struct device *dev)
        struct dwc2_hsotg *dwc2 = dev_get_drvdata(dev);
        int ret = 0;
 
-       if (dwc2_is_device_mode(dwc2)) {
-               ret = s3c_hsotg_resume(dwc2);
-       } else {
-               phy_power_on(dwc2->phy);
-               phy_init(dwc2->phy);
-
+       if (dwc2->ll_hw_enabled) {
+               ret = __dwc2_lowlevel_hw_enable(dwc2);
+               if (ret)
+                       return ret;
        }
+
+       if (dwc2_is_device_mode(dwc2))
+               ret = dwc2_hsotg_resume(dwc2);
+
        return ret;
 }