The current EHCI implementation is prepared to toggle the
PORT_POWER bit to enable or disable a USB-Port. In some
cases this port power can not be toggled by the PORT_POWER
bit but instead i.e. by an external GPIO.
This patch adds an callback to be triggered as well.
The host needs to assign a capable portpower callback.

Signed-off-by: Michael Grzeschik <m.grzesc...@pengutronix.de>
---
 drivers/usb/host/ehci-hcd.c |  2 ++
 drivers/usb/host/ehci-hub.c | 25 +++++++++++++++++++------
 drivers/usb/host/ehci.h     |  1 +
 include/linux/usb/hcd.h     |  1 +
 4 files changed, 23 insertions(+), 6 deletions(-)

diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
index 488a308..063128c 100644
--- a/drivers/usb/host/ehci-hcd.c
+++ b/drivers/usb/host/ehci-hcd.c
@@ -1233,6 +1233,8 @@ void ehci_init_driver(struct hc_driver *drv,
                drv->hcd_priv_size += over->extra_priv_size;
                if (over->reset)
                        drv->reset = over->reset;
+               if (over->portpower)
+                       drv->portpower = over->portpower;
        }
 }
 EXPORT_SYMBOL_GPL(ehci_init_driver);
diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c
index 6130b75..2bfe660 100644
--- a/drivers/usb/host/ehci-hub.c
+++ b/drivers/usb/host/ehci-hub.c
@@ -952,6 +952,11 @@ int ehci_hub_control(
                        clear_bit(wIndex, &ehci->port_c_suspend);
                        break;
                case USB_PORT_FEAT_POWER:
+
+                       /* disable port power by callback */
+                       if (hcd->driver->portpower)
+                               hcd->driver->portpower(hcd, 0);
+
                        if (HCS_PPC (ehci->hcs_params))
                                ehci_writel(ehci, temp & ~PORT_POWER,
                                                status_reg);
@@ -1002,12 +1007,16 @@ int ehci_hub_control(
                         * power switching; they're allowed to just limit the
                         * current.  khubd will turn the power back on.
                         */
-                       if (((temp & PORT_OC) || (ehci->need_oc_pp_cycle))
-                                       && HCS_PPC(ehci->hcs_params)) {
-                               ehci_writel(ehci,
-                                       temp & ~(PORT_RWC_BITS | PORT_POWER),
-                                       status_reg);
-                               temp = ehci_readl(ehci, status_reg);
+                       if ((temp & PORT_OC) || (ehci->need_oc_pp_cycle)) {
+                               /* disable port power by callback */
+                               if (hcd->driver->portpower)
+                                       hcd->driver->portpower(hcd, 0);
+
+                               if (HCS_PPC(ehci->hcs_params)) {
+                                       temp &= ~(PORT_RWC_BITS | PORT_POWER);
+                                       ehci_writel(ehci, temp, status_reg);
+                                       temp = ehci_readl(ehci, status_reg);
+                               }
                        }
                }
 
@@ -1187,6 +1196,10 @@ int ehci_hub_control(
                        set_bit(wIndex, &ehci->suspended_ports);
                        break;
                case USB_PORT_FEAT_POWER:
+                       /* enable port power by callback */
+                       if (hcd->driver->portpower)
+                               hcd->driver->portpower(hcd, 1);
+
                        if (HCS_PPC (ehci->hcs_params))
                                ehci_writel(ehci, temp | PORT_POWER,
                                                status_reg);
diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h
index eee228a..7f48048 100644
--- a/drivers/usb/host/ehci.h
+++ b/drivers/usb/host/ehci.h
@@ -859,6 +859,7 @@ static inline u32 hc32_to_cpup (const struct ehci_hcd 
*ehci, const __hc32 *x)
 struct ehci_driver_overrides {
        size_t          extra_priv_size;
        int             (*reset)(struct usb_hcd *hcd);
+       int             (*portpower)(struct usb_hcd *hcd, int enable);
 };
 
 extern void    ehci_init_driver(struct hc_driver *drv,
diff --git a/include/linux/usb/hcd.h b/include/linux/usb/hcd.h
index 485cd5e..3f60acb 100644
--- a/include/linux/usb/hcd.h
+++ b/include/linux/usb/hcd.h
@@ -241,6 +241,7 @@ struct hc_driver {
        /* called to init HCD and root hub */
        int     (*reset) (struct usb_hcd *hcd);
        int     (*start) (struct usb_hcd *hcd);
+       int     (*portpower)(struct usb_hcd *hcd, int enable);
 
        /* NOTE:  these suspend/resume calls relate to the HC as
         * a whole, not just the root hub; they're for PCI bus glue.
-- 
2.1.0

--
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