Add workqueue to add/remove host driver (outside interrupt context)
upon each id change

Signed-off-by: Ramneek Mehresh <ramneek.mehr...@freescale.com>
---
Changes for v3:
        - use overrides for ehci_fsl_overrides
        - remove struct ehci_hcd from ehci_fsl
        - move ehci_fsl to ehci-fsl.h
 
 drivers/usb/host/ehci-fsl.c | 132 ++++++++++++++++++++++++--------------------
 drivers/usb/host/ehci-fsl.h |  20 +++++++
 drivers/usb/host/ehci-hcd.c |  10 ++++
 drivers/usb/host/ehci.h     |   6 ++
 4 files changed, 108 insertions(+), 60 deletions(-)

diff --git a/drivers/usb/host/ehci-fsl.c b/drivers/usb/host/ehci-fsl.c
index ab4eee3..b2f5949 100644
--- a/drivers/usb/host/ehci-fsl.c
+++ b/drivers/usb/host/ehci-fsl.c
@@ -33,6 +33,33 @@
 
 #include "ehci-fsl.h"
 
+#if defined(CONFIG_FSL_USB2_OTG) || defined(CONFIG_FSL_USB2_OTG_MODULE)
+static struct ehci_fsl *hcd_to_ehci_fsl(struct usb_hcd *hcd)
+{
+       return (struct ehci_fsl *)hcd_to_ehci(hcd)->priv;
+}
+
+static void do_change_hcd(struct work_struct *work)
+{
+       struct ehci_fsl *ehci_fsl = container_of(work, struct ehci_fsl,
+                                       change_hcd_work);
+       struct usb_hcd *hcd = ehci_fsl->hcd;
+       void __iomem *non_ehci = hcd->regs;
+       int retval;
+
+       if (ehci_fsl->hcd_add && !ehci_fsl->have_hcd) {
+               writel(USBMODE_CM_HOST, non_ehci + FSL_SOC_USB_USBMODE);
+               /* host, gadget and otg share same int line */
+               retval = usb_add_hcd(hcd, hcd->irq, IRQF_SHARED);
+               if (retval == 0)
+                       ehci_fsl->have_hcd = 1;
+       } else if (!ehci_fsl->hcd_add && ehci_fsl->have_hcd) {
+               usb_remove_hcd(hcd);
+               ehci_fsl->have_hcd = 0;
+       }
+}
+#endif
+
 /* configure so an HC device and id are always provided */
 /* always called with process context; sleeping is OK */
 
@@ -126,11 +153,16 @@ static int usb_hcd_fsl_probe(const struct hc_driver 
*driver,
                goto err2;
        device_wakeup_enable(hcd->self.controller);
 
-#ifdef CONFIG_USB_OTG
+#if defined(CONFIG_FSL_USB2_OTG) || defined(CONFIG_FSL_USB2_OTG_MODULE)
        if (pdata->operating_mode == FSL_USB2_DR_OTG) {
                struct ehci_hcd *ehci = hcd_to_ehci(hcd);
+               struct ehci_fsl *ehci_fsl = hcd_to_ehci_fsl(hcd);
 
+               ehci_fsl->hcd = hcd;
                hcd->usb_phy = usb_get_phy(USB_PHY_TYPE_USB2);
+
+               INIT_WORK(&ehci_fsl->change_hcd_work, do_change_hcd);
+
                dev_dbg(&pdev->dev, "hcd=0x%p  ehci=0x%p, phy=0x%p\n",
                        hcd, ehci, hcd->usb_phy);
 
@@ -376,15 +408,6 @@ static int ehci_fsl_setup(struct usb_hcd *hcd)
        return retval;
 }
 
-struct ehci_fsl {
-       struct ehci_hcd ehci;
-
-#ifdef CONFIG_PM
-       /* Saved USB PHY settings, need to restore after deep sleep. */
-       u32 usb_ctrl;
-#endif
-};
-
 #ifdef CONFIG_PM
 
 #ifdef CONFIG_PPC_MPC512x
@@ -532,24 +555,32 @@ static inline int ehci_fsl_mpc512x_drv_resume(struct 
device *dev)
 }
 #endif /* CONFIG_PPC_MPC512x */
 
-static struct ehci_fsl *hcd_to_ehci_fsl(struct usb_hcd *hcd)
-{
-       struct ehci_hcd *ehci = hcd_to_ehci(hcd);
-
-       return container_of(ehci, struct ehci_fsl, ehci);
-}
-
 static int ehci_fsl_drv_suspend(struct device *dev)
 {
        struct usb_hcd *hcd = dev_get_drvdata(dev);
-       struct ehci_fsl *ehci_fsl = hcd_to_ehci_fsl(hcd);
        void __iomem *non_ehci = hcd->regs;
+#if defined(CONFIG_FSL_USB2_OTG) || defined(CONFIG_FSL_USB2_OTG_MODULE)
+       struct ehci_fsl *ehci_fsl = hcd_to_ehci_fsl(hcd);
+       struct usb_bus host = hcd->self;
+#endif
 
        if (of_device_is_compatible(dev->parent->of_node,
                                    "fsl,mpc5121-usb2-dr")) {
                return ehci_fsl_mpc512x_drv_suspend(dev);
        }
 
+#if defined(CONFIG_FSL_USB2_OTG) || defined(CONFIG_FSL_USB2_OTG_MODULE)
+       if (host.is_otg) {
+               /*struct ehci_hcd *ehci = hcd_to_ehci(hcd);*/
+
+               /* remove hcd */
+               ehci_fsl->hcd_add = 0;
+               schedule_work(&ehci_fsl->change_hcd_work);
+               host.is_otg = 0;
+               return 0;
+       }
+#endif
+
        ehci_prepare_ports_for_controller_suspend(hcd_to_ehci(hcd),
                        device_may_wakeup(dev));
        if (!fsl_deep_sleep())
@@ -562,15 +593,29 @@ static int ehci_fsl_drv_suspend(struct device *dev)
 static int ehci_fsl_drv_resume(struct device *dev)
 {
        struct usb_hcd *hcd = dev_get_drvdata(dev);
-       struct ehci_fsl *ehci_fsl = hcd_to_ehci_fsl(hcd);
        struct ehci_hcd *ehci = hcd_to_ehci(hcd);
        void __iomem *non_ehci = hcd->regs;
+#if defined(CONFIG_FSL_USB2_OTG) || defined(CONFIG_FSL_USB2_OTG_MODULE)
+       struct ehci_fsl *ehci_fsl = hcd_to_ehci_fsl(hcd);
+       struct usb_bus host = hcd->self;
+#endif
 
        if (of_device_is_compatible(dev->parent->of_node,
                                    "fsl,mpc5121-usb2-dr")) {
                return ehci_fsl_mpc512x_drv_resume(dev);
        }
 
+#if defined(CONFIG_FSL_USB2_OTG) || defined(CONFIG_FSL_USB2_OTG_MODULE)
+       if (host.is_otg) {
+               /* add hcd */
+               ehci_fsl->hcd_add = 1;
+               schedule_work(&ehci_fsl->change_hcd_work);
+               usb_hcd_resume_root_hub(hcd);
+               host.is_otg = 0;
+               return 0;
+       }
+#endif
+
        ehci_prepare_ports_for_controller_resume(ehci);
        if (!fsl_deep_sleep())
                return 0;
@@ -636,60 +681,27 @@ static int ehci_start_port_reset(struct usb_hcd *hcd, 
unsigned port)
 #define ehci_start_port_reset  NULL
 #endif /* CONFIG_USB_OTG */
 
+static struct hc_driver __read_mostly ehci_fsl_hc_driver;
 
-static const struct hc_driver ehci_fsl_hc_driver = {
-       .description = hcd_name,
+static struct ehci_driver_overrides ehci_fsl_overrides __initdata = {
        .product_desc = "Freescale On-Chip EHCI Host Controller",
-       .hcd_priv_size = sizeof(struct ehci_fsl),
-
-       /*
-        * generic hardware linkage
-        */
-       .irq = ehci_irq,
-       .flags = HCD_USB2 | HCD_MEMORY | HCD_BH,
-
-       /*
-        * basic lifecycle operations
-        */
+       .extra_priv_size = sizeof(struct ehci_fsl),
        .reset = ehci_fsl_setup,
        .start = ehci_run,
-       .stop = ehci_stop,
-       .shutdown = ehci_shutdown,
-
-       /*
-        * managing i/o requests and associated device resources
-        */
-       .urb_enqueue = ehci_urb_enqueue,
-       .urb_dequeue = ehci_urb_dequeue,
-       .endpoint_disable = ehci_endpoint_disable,
-       .endpoint_reset = ehci_endpoint_reset,
-
-       /*
-        * scheduling support
-        */
-       .get_frame_number = ehci_get_frame,
-
-       /*
-        * root hub support
-        */
-       .hub_status_data = ehci_hub_status_data,
-       .hub_control = ehci_hub_control,
-       .bus_suspend = ehci_bus_suspend,
-       .bus_resume = ehci_bus_resume,
        .start_port_reset = ehci_start_port_reset,
-       .relinquish_port = ehci_relinquish_port,
-       .port_handed_over = ehci_port_handed_over,
-
-       .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete,
 };
 
 static int ehci_fsl_drv_probe(struct platform_device *pdev)
 {
+       struct hc_driver *driver = &ehci_fsl_hc_driver;
+
+       ehci_init_driver(driver, &ehci_fsl_overrides);
+
        if (usb_disabled())
                return -ENODEV;
 
        /* FIXME we only want one one probe() not two */
-       return usb_hcd_fsl_probe(&ehci_fsl_hc_driver, pdev);
+       return usb_hcd_fsl_probe(driver, pdev);
 }
 
 static int ehci_fsl_drv_remove(struct platform_device *pdev)
diff --git a/drivers/usb/host/ehci-fsl.h b/drivers/usb/host/ehci-fsl.h
index dbd292e..c2e38f2 100644
--- a/drivers/usb/host/ehci-fsl.h
+++ b/drivers/usb/host/ehci-fsl.h
@@ -61,5 +61,25 @@
 #define PLL_RESET               (1<<8)
 #define UTMI_PHY_EN             (1<<9)
 #define ULPI_PHY_CLK_SEL        (1<<10)
+
 #define PHY_CLK_VALID          (1<<17)
+
+struct ehci_fsl {
+#ifdef CONFIG_PM
+       /* Saved USB PHY settings, need to restore after deep sleep. */
+       u32 usb_ctrl;
+#endif
+       struct usb_hcd *hcd;
+#if defined(CONFIG_FSL_USB2_OTG) || defined(CONFIG_FSL_USB2_OTG_MODULE)
+       struct work_struct change_hcd_work;
+#endif
+       /* store current hcd state for otg;
+        * have_hcd is true when host drv al already part of otg framework,
+        * otherwise false;
+        * hcd_add is true when otg framework wants to add host
+        * drv as part of otg;flase when it wants to remove it
+        */
+       unsigned have_hcd:1;
+       unsigned hcd_add:1;
+};
 #endif                         /* _EHCI_FSL_H */
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
index 85e56d1..d29cd85 100644
--- a/drivers/usb/host/ehci-hcd.c
+++ b/drivers/usb/host/ehci-hcd.c
@@ -1236,10 +1236,20 @@ void ehci_init_driver(struct hc_driver *drv,
 
        if (over) {
                drv->hcd_priv_size += over->extra_priv_size;
+               if (over->product_desc)
+                       drv->product_desc = over->product_desc;
+               if (over->start)
+                       drv->start = over->start;
+               if (over->stop)
+                       drv->stop = over->stop;
+               if (over->shutdown)
+                       drv->shutdown = over->shutdown;
                if (over->reset)
                        drv->reset = over->reset;
                if (over->port_power)
                        drv->port_power = over->port_power;
+               if (over->start_port_reset)
+                       drv->start_port_reset = over->start_port_reset;
        }
 }
 EXPORT_SYMBOL_GPL(ehci_init_driver);
diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h
index 52ef084..f090243 100644
--- a/drivers/usb/host/ehci.h
+++ b/drivers/usb/host/ehci.h
@@ -857,10 +857,16 @@ static inline u32 hc32_to_cpup (const struct ehci_hcd 
*ehci, const __hc32 *x)
 /* Declarations of things exported for use by ehci platform drivers */
 
 struct ehci_driver_overrides {
+       const char      *product_desc;
        size_t          extra_priv_size;
        int             (*reset)(struct usb_hcd *hcd);
+       int             (*start)(struct usb_hcd *hcd);
+       void            (*stop)(struct usb_hcd *hcd);
+       void            (*shutdown)(struct usb_hcd *hcd);
        int             (*port_power)(struct usb_hcd *hcd,
                                int portnum, bool enable);
+       int             (*start_port_reset)(struct usb_hcd *,
+                               unsigned port_num);
 };
 
 extern void    ehci_init_driver(struct hc_driver *drv,
-- 
1.8.3.1

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to