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.grzeschik@xxxxxxxxxxxxxx> --- 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 majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html