summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRamneek Mehresh <ramneek.mehresh@freescale.com>2013-04-18 10:13:33 (GMT)
committerXie Xiaobo <xiaobo.xie@nxp.com>2017-09-25 07:25:27 (GMT)
commit42a1431068088a5a9dbeaf94d9a52ba23a5b91eb (patch)
treed818031532102100d03ac071e342bd9876282be2
parentbb40cce21a9f174d07ea82173e5fe59cfbdbaaca (diff)
downloadlinux-42a1431068088a5a9dbeaf94d9a52ba23a5b91eb.tar.xz
usb: host: Add support to add/remove usb host driver
Add workqueue to add/remove host driver (outside interrupt context) upon each id change Signed-off-by: Li Yang <leoli@freescale.com> Signed-off-by: Ramneek Mehresh <ramneek.mehresh@freescale.com> Signed-off-by: yinbo.zhu <yinbo.zhu@nxp.com>
-rw-r--r--drivers/usb/host/ehci-fsl.c140
-rw-r--r--drivers/usb/host/ehci.h3
-rw-r--r--drivers/usb/phy/phy-fsl-usb.c7
-rw-r--r--include/linux/usb.h1
4 files changed, 133 insertions, 18 deletions
diff --git a/drivers/usb/host/ehci-fsl.c b/drivers/usb/host/ehci-fsl.c
index e89a8b8..2e6b52a 100644
--- a/drivers/usb/host/ehci-fsl.c
+++ b/drivers/usb/host/ehci-fsl.c
@@ -44,6 +44,96 @@
#define DRV_NAME "ehci-fsl"
static struct hc_driver __read_mostly fsl_ehci_hc_driver;
+struct ehci_fsl {
+ /* 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
+
+struct ehci_fsl {
+ struct ehci_hcd ehci;
+
+#ifdef CONFIG_PM
+struct ehci_regs saved_regs;
+struct ccsr_usb_phy saved_phy_regs;
+/* 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 strut 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 */
@@ -148,11 +238,14 @@ static int fsl_ehci_drv_probe(struct platform_device *pdev)
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->usb_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->usb_phy);
@@ -379,15 +472,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
@@ -535,24 +619,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 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())
@@ -568,12 +660,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 3f3b74a..89e78d0 100644
--- a/drivers/usb/host/ehci.h
+++ b/drivers/usb/host/ehci.h
@@ -180,6 +180,9 @@ struct ehci_hcd { /* one per controller */
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;
diff --git a/drivers/usb/phy/phy-fsl-usb.c b/drivers/usb/phy/phy-fsl-usb.c
index 7f70135..cb3a951 100644
--- a/drivers/usb/phy/phy-fsl-usb.c
+++ b/drivers/usb/phy/phy-fsl-usb.c
@@ -463,6 +463,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->usb_phy, struct fsl_otg, phy);
@@ -486,6 +487,7 @@ int fsl_otg_start_host(struct otg_fsm *fsm, int on)
otg_reset_controller();
VDBG("host on......\n");
if (dev->driver->pm && dev->driver->pm->resume) {
+ host->is_otg = 1;
retval = dev->driver->pm->resume(dev);
if (fsm->id) {
/* default-b */
@@ -510,8 +512,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 eba1f10..c334e28 100644
--- a/include/linux/usb.h
+++ b/include/linux/usb.h
@@ -362,6 +362,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; /*