diff options
author | Michael Grzeschik <m.grzeschik@pengutronix.de> | 2014-10-12 21:53:03 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2014-11-03 18:34:00 -0500 |
commit | 11a7e59405148c855e0a9d13588930ccec02c150 (patch) | |
tree | a8e21a87496d8ec3fee589dfea21766d2813d58f | |
parent | e28e2f2f7c42e5b9dd4c965a0245267e44a8a7ae (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>
-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 | ||||
-rw-r--r-- | include/linux/usb/hcd.h | 3 |
4 files changed, 45 insertions, 14 deletions
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 15feaf924b71..df75b8e7d157 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); | |||
311 | static void ehci_work(struct ehci_hcd *ehci); | 311 | static void ehci_work(struct ehci_hcd *ehci); |
312 | static void start_unlink_intr(struct ehci_hcd *ehci, struct ehci_qh *qh); | 312 | static void start_unlink_intr(struct ehci_hcd *ehci, struct ehci_qh *qh); |
313 | static void end_unlink_intr(struct ehci_hcd *ehci, struct ehci_qh *qh); | 313 | static void end_unlink_intr(struct ehci_hcd *ehci, struct ehci_qh *qh); |
314 | static int ehci_port_power(struct ehci_hcd *ehci, int portnum, bool enable); | ||
314 | 315 | ||
315 | #include "ehci-timer.c" | 316 | #include "ehci-timer.c" |
316 | #include "ehci-hub.c" | 317 | #include "ehci-hub.c" |
@@ -329,9 +330,13 @@ static void ehci_turn_off_all_ports(struct ehci_hcd *ehci) | |||
329 | { | 330 | { |
330 | int port = HCS_N_PORTS(ehci->hcs_params); | 331 | int port = HCS_N_PORTS(ehci->hcs_params); |
331 | 332 | ||
332 | while (port--) | 333 | while (port--) { |
333 | ehci_writel(ehci, PORT_RWC_BITS, | 334 | ehci_writel(ehci, PORT_RWC_BITS, |
334 | &ehci->regs->port_status[port]); | 335 | &ehci->regs->port_status[port]); |
336 | spin_unlock_irq(&ehci->lock); | ||
337 | ehci_port_power(ehci, port, false); | ||
338 | spin_lock_irq(&ehci->lock); | ||
339 | } | ||
335 | } | 340 | } |
336 | 341 | ||
337 | /* | 342 | /* |
@@ -1233,6 +1238,8 @@ void ehci_init_driver(struct hc_driver *drv, | |||
1233 | drv->hcd_priv_size += over->extra_priv_size; | 1238 | drv->hcd_priv_size += over->extra_priv_size; |
1234 | if (over->reset) | 1239 | if (over->reset) |
1235 | drv->reset = over->reset; | 1240 | drv->reset = over->reset; |
1241 | if (over->port_power) | ||
1242 | drv->port_power = over->port_power; | ||
1236 | } | 1243 | } |
1237 | } | 1244 | } |
1238 | EXPORT_SYMBOL_GPL(ehci_init_driver); | 1245 | EXPORT_SYMBOL_GPL(ehci_init_driver); |
diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c index 5728829cf6ef..118edb7bdca2 100644 --- a/drivers/usb/host/ehci-hub.c +++ b/drivers/usb/host/ehci-hub.c | |||
@@ -69,10 +69,8 @@ static void ehci_handover_companion_ports(struct ehci_hcd *ehci) | |||
69 | if (test_bit(port, &ehci->owned_ports)) { | 69 | if (test_bit(port, &ehci->owned_ports)) { |
70 | reg = &ehci->regs->port_status[port]; | 70 | reg = &ehci->regs->port_status[port]; |
71 | status = ehci_readl(ehci, reg) & ~PORT_RWC_BITS; | 71 | status = ehci_readl(ehci, reg) & ~PORT_RWC_BITS; |
72 | if (!(status & PORT_POWER)) { | 72 | if (!(status & PORT_POWER)) |
73 | status |= PORT_POWER; | 73 | ehci_port_power(ehci, port, true); |
74 | ehci_writel(ehci, status, reg); | ||
75 | } | ||
76 | } | 74 | } |
77 | } | 75 | } |
78 | 76 | ||
@@ -952,9 +950,11 @@ int ehci_hub_control( | |||
952 | clear_bit(wIndex, &ehci->port_c_suspend); | 950 | clear_bit(wIndex, &ehci->port_c_suspend); |
953 | break; | 951 | break; |
954 | case USB_PORT_FEAT_POWER: | 952 | case USB_PORT_FEAT_POWER: |
955 | if (HCS_PPC (ehci->hcs_params)) | 953 | if (HCS_PPC(ehci->hcs_params)) { |
956 | ehci_writel(ehci, temp & ~PORT_POWER, | 954 | spin_unlock_irqrestore(&ehci->lock, flags); |
957 | status_reg); | 955 | ehci_port_power(ehci, wIndex, false); |
956 | spin_lock_irqsave(&ehci->lock, flags); | ||
957 | } | ||
958 | break; | 958 | break; |
959 | case USB_PORT_FEAT_C_CONNECTION: | 959 | case USB_PORT_FEAT_C_CONNECTION: |
960 | ehci_writel(ehci, temp | PORT_CSC, status_reg); | 960 | ehci_writel(ehci, temp | PORT_CSC, status_reg); |
@@ -1004,9 +1004,9 @@ int ehci_hub_control( | |||
1004 | */ | 1004 | */ |
1005 | if (((temp & PORT_OC) || (ehci->need_oc_pp_cycle)) | 1005 | if (((temp & PORT_OC) || (ehci->need_oc_pp_cycle)) |
1006 | && HCS_PPC(ehci->hcs_params)) { | 1006 | && HCS_PPC(ehci->hcs_params)) { |
1007 | ehci_writel(ehci, | 1007 | spin_unlock_irqrestore(&ehci->lock, flags); |
1008 | temp & ~(PORT_RWC_BITS | PORT_POWER), | 1008 | ehci_port_power(ehci, wIndex, false); |
1009 | status_reg); | 1009 | spin_lock_irqsave(&ehci->lock, flags); |
1010 | temp = ehci_readl(ehci, status_reg); | 1010 | temp = ehci_readl(ehci, status_reg); |
1011 | } | 1011 | } |
1012 | } | 1012 | } |
@@ -1187,9 +1187,11 @@ int ehci_hub_control( | |||
1187 | set_bit(wIndex, &ehci->suspended_ports); | 1187 | set_bit(wIndex, &ehci->suspended_ports); |
1188 | break; | 1188 | break; |
1189 | case USB_PORT_FEAT_POWER: | 1189 | case USB_PORT_FEAT_POWER: |
1190 | if (HCS_PPC (ehci->hcs_params)) | 1190 | if (HCS_PPC(ehci->hcs_params)) { |
1191 | ehci_writel(ehci, temp | PORT_POWER, | 1191 | spin_unlock_irqrestore(&ehci->lock, flags); |
1192 | status_reg); | 1192 | ehci_port_power(ehci, wIndex, true); |
1193 | spin_lock_irqsave(&ehci->lock, flags); | ||
1194 | } | ||
1193 | break; | 1195 | break; |
1194 | case USB_PORT_FEAT_RESET: | 1196 | case USB_PORT_FEAT_RESET: |
1195 | if (temp & (PORT_SUSPEND|PORT_RESUME)) | 1197 | if (temp & (PORT_SUSPEND|PORT_RESUME)) |
@@ -1297,3 +1299,20 @@ static int ehci_port_handed_over(struct usb_hcd *hcd, int portnum) | |||
1297 | reg = &ehci->regs->port_status[portnum - 1]; | 1299 | reg = &ehci->regs->port_status[portnum - 1]; |
1298 | return ehci_readl(ehci, reg) & PORT_OWNER; | 1300 | return ehci_readl(ehci, reg) & PORT_OWNER; |
1299 | } | 1301 | } |
1302 | |||
1303 | static int ehci_port_power(struct ehci_hcd *ehci, int portnum, bool enable) | ||
1304 | { | ||
1305 | struct usb_hcd *hcd = ehci_to_hcd(ehci); | ||
1306 | u32 __iomem *status_reg = &ehci->regs->port_status[portnum]; | ||
1307 | u32 temp = ehci_readl(ehci, status_reg) & ~PORT_RWC_BITS; | ||
1308 | |||
1309 | if (enable) | ||
1310 | ehci_writel(ehci, temp | PORT_POWER, status_reg); | ||
1311 | else | ||
1312 | ehci_writel(ehci, temp & ~PORT_POWER, status_reg); | ||
1313 | |||
1314 | if (hcd->driver->port_power) | ||
1315 | hcd->driver->port_power(hcd, portnum, enable); | ||
1316 | |||
1317 | return 0; | ||
1318 | } | ||
diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index eee228a26a0e..6f0577b0a5ae 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) | |||
859 | struct ehci_driver_overrides { | 859 | struct ehci_driver_overrides { |
860 | size_t extra_priv_size; | 860 | size_t extra_priv_size; |
861 | int (*reset)(struct usb_hcd *hcd); | 861 | int (*reset)(struct usb_hcd *hcd); |
862 | int (*port_power)(struct usb_hcd *hcd, | ||
863 | int portnum, bool enable); | ||
862 | }; | 864 | }; |
863 | 865 | ||
864 | extern void ehci_init_driver(struct hc_driver *drv, | 866 | extern void ehci_init_driver(struct hc_driver *drv, |
diff --git a/include/linux/usb/hcd.h b/include/linux/usb/hcd.h index cd96a2bc3388..9cf7e3594609 100644 --- a/include/linux/usb/hcd.h +++ b/include/linux/usb/hcd.h | |||
@@ -379,6 +379,9 @@ struct hc_driver { | |||
379 | int (*disable_usb3_lpm_timeout)(struct usb_hcd *, | 379 | int (*disable_usb3_lpm_timeout)(struct usb_hcd *, |
380 | struct usb_device *, enum usb3_link_state state); | 380 | struct usb_device *, enum usb3_link_state state); |
381 | int (*find_raw_port_number)(struct usb_hcd *, int); | 381 | int (*find_raw_port_number)(struct usb_hcd *, int); |
382 | /* Call for power on/off the port if necessary */ | ||
383 | int (*port_power)(struct usb_hcd *hcd, int portnum, bool enable); | ||
384 | |||
382 | }; | 385 | }; |
383 | 386 | ||
384 | static inline int hcd_giveback_urb_in_bh(struct usb_hcd *hcd) | 387 | static inline int hcd_giveback_urb_in_bh(struct usb_hcd *hcd) |