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

Signed-off-by: Li Yang <le...@freescale.com>
Signed-off-by: Ramneek Mehresh <ramneek.mehr...@freescale.com>
Reviewed-by: Fleming Andrew-AFLEMING <aflem...@freescale.com>
Tested-by: Fleming Andrew-AFLEMING <aflem...@freescale.com>
---
 drivers/usb/host/ehci-fsl.c   | 97 +++++++++++++++++++++++++++++++++++--------
 drivers/usb/host/ehci.h       |  5 ++-
 drivers/usb/phy/phy-fsl-usb.c |  7 +++-
 include/linux/usb.h           |  1 +
 4 files changed, 90 insertions(+), 20 deletions(-)

diff --git a/drivers/usb/host/ehci-fsl.c b/drivers/usb/host/ehci-fsl.c
index cf2734b..b026e7e 100644
--- a/drivers/usb/host/ehci-fsl.c
+++ b/drivers/usb/host/ehci-fsl.c
@@ -33,6 +33,54 @@
 
 #include "ehci-fsl.h"
 
+struct ehci_fsl {
+       struct ehci_hcd ehci;
+
+#ifdef CONFIG_PM
+       /* Saved USB PHY settings, need to restore after deep sleep. */
+       u32 usb_ctrl;
+#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;
+};
+
+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);
+}
+
+#if defined(CONFIG_FSL_USB2_OTG) || defined(CONFIG_FSL_USB2_OTG_MODULE)
+static void do_change_hcd(struct work_struct *work)
+{
+       struct ehci_hcd *ehci = container_of(work, struct ehci_hcd,
+                                       change_hcd_work);
+       struct usb_hcd *hcd = ehci_to_hcd(ehci);
+       struct ehci_fsl *ehci_fsl = hcd_to_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 */
 
@@ -132,11 +180,14 @@ 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);
 
                hcd->phy = usb_get_phy(USB_PHY_TYPE_USB2);
+
+               INIT_WORK(&ehci->change_hcd_work, do_change_hcd);
+
                dev_dbg(&pdev->dev, "hcd=0x%p  ehci=0x%p, phy=0x%p\n",
                        hcd, ehci, hcd->phy);
 
@@ -382,15 +433,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
@@ -538,24 +580,31 @@ 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 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->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())
@@ -571,12 +620,26 @@ static int ehci_fsl_drv_resume(struct device *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 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->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;
diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h
index eee228a..1f7ffd2 100644
--- a/drivers/usb/host/ehci.h
+++ b/drivers/usb/host/ehci.h
@@ -176,8 +176,9 @@ struct ehci_hcd {                   /* one per controller */
        unsigned                isoc_count;     /* isoc activity count */
        unsigned                periodic_count; /* periodic activity count */
        unsigned                uframe_periodic_max; /* max periodic time per 
uframe */
-
-
+#if defined(CONFIG_FSL_USB2_OTG) || defined(CONFIG_FSL_USB2_OTG_MODULE)
+       struct work_struct change_hcd_work;
+#endif
        /* list of itds & sitds completed while now_frame was still active */
        struct list_head        cached_itd_list;
        struct ehci_itd         *last_itd_to_free;
diff --git a/drivers/usb/phy/phy-fsl-usb.c b/drivers/usb/phy/phy-fsl-usb.c
index dcffeae..e8fa8c3 100644
--- a/drivers/usb/phy/phy-fsl-usb.c
+++ b/drivers/usb/phy/phy-fsl-usb.c
@@ -498,6 +498,7 @@ void otg_reset_controller(void)
 int fsl_otg_start_host(struct otg_fsm *fsm, int on)
 {
        struct usb_otg *otg = fsm->otg;
+       struct usb_bus *host = otg->host;
        struct device *dev;
        struct fsl_otg *otg_dev = container_of(otg->phy, struct fsl_otg, phy);
        u32 retval = 0;
@@ -519,6 +520,7 @@ int fsl_otg_start_host(struct otg_fsm *fsm, int on)
                else {
                        otg_reset_controller();
                        VDBG("host on......\n");
+                       host->is_otg = 1;
                        if (dev->driver->pm && dev->driver->pm->resume) {
                                retval = dev->driver->pm->resume(dev);
                                if (fsm->id) {
@@ -544,8 +546,11 @@ int fsl_otg_start_host(struct otg_fsm *fsm, int on)
                else {
                        VDBG("host off......\n");
                        if (dev && dev->driver) {
-                               if (dev->driver->pm && dev->driver->pm->suspend)
+                               if (dev->driver->pm &&
+                                               dev->driver->pm->suspend) {
+                                       host->is_otg = 1;
                                        retval = dev->driver->pm->suspend(dev);
+                               }
                                if (fsm->id)
                                        /* default-b */
                                        fsl_otg_drv_vbus(fsm, 0);
diff --git a/include/linux/usb.h b/include/linux/usb.h
index d2465bc..b98b2b7 100644
--- a/include/linux/usb.h
+++ b/include/linux/usb.h
@@ -334,6 +334,7 @@ struct usb_bus {
                                         * for control transfers?
                                         */
        u8 otg_port;                    /* 0, or number of OTG/HNP port */
+        unsigned is_otg:1;             /* true when host is also otg */
        unsigned is_b_host:1;           /* true during some HNP roleswitches */
        unsigned b_hnp_enable:1;        /* OTG: did A-Host enable HNP? */
        unsigned no_stop_on_short:1;    /*
-- 
1.8.3.1

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

Reply via email to