diff options
author | Michael Grzeschik <m.grzeschik@pengutronix.de> | 2014-10-13 09:53:03 +0800 |
---|---|---|
committer | Nitin Garg <nitin.garg@freescale.com> | 2015-09-17 09:20:44 -0500 |
commit | 23e79e168be356e032bd55cca69859026138c54e (patch) | |
tree | 0f47f8afdff120bce8e73257ffe839122d3104a6 /drivers/usb/host | |
parent | cde95bf50b79d6fdd4f247f6bd26c3bba3882fa2 (diff) |
usb: ehci: add ehci_port_power interface
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 just toggled by the PORT_POWER
bit, and the gpio-regulator is needed to be toggled too.
This patch defines a port power control interface ehci_port_power for
ehci core use, it toggles PORT_POWER bit as well as calls platform
defined .port_power if it is defined.
Signed-off-by: Michael Grzeschik <m.grzeschik@pengutronix.de>
Signed-off-by: Peter Chen <peter.chen@freescale.com>
Acked-off-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
(cherry picked from commit 11a7e59405148c855e0a9d13588930ccec02c150)
Diffstat (limited to 'drivers/usb/host')
-rw-r--r-- | drivers/usb/host/ehci-hcd.c | 9 | ||||
-rw-r--r-- | drivers/usb/host/ehci-hub.c | 45 | ||||
-rw-r--r-- | drivers/usb/host/ehci.h | 2 |
3 files changed, 42 insertions, 14 deletions
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 3df32fa8c8ae..0ea0633154af 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -311,6 +311,7 @@ static void unlink_empty_async_suspended(struct ehci_hcd *ehci); static void ehci_work(struct ehci_hcd *ehci); static void start_unlink_intr(struct ehci_hcd *ehci, struct ehci_qh *qh); static void end_unlink_intr(struct ehci_hcd *ehci, struct ehci_qh *qh); +static int ehci_port_power(struct ehci_hcd *ehci, int portnum, bool enable); #include "ehci-timer.c" #include "ehci-hub.c" @@ -329,9 +330,13 @@ static void ehci_turn_off_all_ports(struct ehci_hcd *ehci) { int port = HCS_N_PORTS(ehci->hcs_params); - while (port--) + while (port--) { ehci_writel(ehci, PORT_RWC_BITS, &ehci->regs->port_status[port]); + spin_unlock_irq(&ehci->lock); + ehci_port_power(ehci, port, false); + spin_lock_irq(&ehci->lock); + } } /* @@ -1233,6 +1238,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->port_power) + drv->port_power = over->port_power; } } EXPORT_SYMBOL_GPL(ehci_init_driver); diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c index 80ce6fbecc3c..37b2220a175b 100644 --- a/drivers/usb/host/ehci-hub.c +++ b/drivers/usb/host/ehci-hub.c @@ -78,10 +78,8 @@ static void ehci_handover_companion_ports(struct ehci_hcd *ehci) if (test_bit(port, &ehci->owned_ports)) { reg = &ehci->regs->port_status[port]; status = ehci_readl(ehci, reg) & ~PORT_RWC_BITS; - if (!(status & PORT_POWER)) { - status |= PORT_POWER; - ehci_writel(ehci, status, reg); - } + if (!(status & PORT_POWER)) + ehci_port_power(ehci, port, true); } } @@ -964,9 +962,11 @@ static int ehci_hub_control ( clear_bit(wIndex, &ehci->port_c_suspend); break; case USB_PORT_FEAT_POWER: - if (HCS_PPC (ehci->hcs_params)) - ehci_writel(ehci, temp & ~PORT_POWER, - status_reg); + if (HCS_PPC(ehci->hcs_params)) { + spin_unlock_irqrestore(&ehci->lock, flags); + ehci_port_power(ehci, wIndex, false); + spin_lock_irqsave(&ehci->lock, flags); + } break; case USB_PORT_FEAT_C_CONNECTION: ehci_writel(ehci, temp | PORT_CSC, status_reg); @@ -1016,9 +1016,9 @@ static int ehci_hub_control ( */ 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); + spin_unlock_irqrestore(&ehci->lock, flags); + ehci_port_power(ehci, wIndex, false); + spin_lock_irqsave(&ehci->lock, flags); temp = ehci_readl(ehci, status_reg); } } @@ -1199,9 +1199,11 @@ static int ehci_hub_control ( set_bit(wIndex, &ehci->suspended_ports); break; case USB_PORT_FEAT_POWER: - if (HCS_PPC (ehci->hcs_params)) - ehci_writel(ehci, temp | PORT_POWER, - status_reg); + if (HCS_PPC(ehci->hcs_params)) { + spin_unlock_irqrestore(&ehci->lock, flags); + ehci_port_power(ehci, wIndex, true); + spin_lock_irqsave(&ehci->lock, flags); + } break; case USB_PORT_FEAT_RESET: if (temp & (PORT_SUSPEND|PORT_RESUME)) @@ -1308,3 +1310,20 @@ static int ehci_port_handed_over(struct usb_hcd *hcd, int portnum) reg = &ehci->regs->port_status[portnum - 1]; return ehci_readl(ehci, reg) & PORT_OWNER; } + +static int ehci_port_power(struct ehci_hcd *ehci, int portnum, bool enable) +{ + struct usb_hcd *hcd = ehci_to_hcd(ehci); + u32 __iomem *status_reg = &ehci->regs->port_status[portnum]; + u32 temp = ehci_readl(ehci, status_reg) & ~PORT_RWC_BITS; + + if (enable) + ehci_writel(ehci, temp | PORT_POWER, status_reg); + else + ehci_writel(ehci, temp & ~PORT_POWER, status_reg); + + if (hcd->driver->port_power) + hcd->driver->port_power(hcd, portnum, enable); + + return 0; +} diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index 9dfc6c1394d6..2b4de3c40435 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -859,6 +859,8 @@ 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 (*port_power)(struct usb_hcd *hcd, + int portnum, bool enable); }; extern void ehci_init_driver(struct hc_driver *drv, |