Commit 11a7e594 authored by Michael Grzeschik's avatar Michael Grzeschik Committed by Greg Kroah-Hartman

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: default avatarMichael Grzeschik <m.grzeschik@pengutronix.de>
Signed-off-by: default avatarPeter Chen <peter.chen@freescale.com>
Acked-off-by: default avatarAlan Stern <stern@rowland.harvard.edu>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent e28e2f2f
...@@ -311,6 +311,7 @@ static void unlink_empty_async_suspended(struct ehci_hcd *ehci); ...@@ -311,6 +311,7 @@ static void unlink_empty_async_suspended(struct ehci_hcd *ehci);
static void ehci_work(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 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 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-timer.c"
#include "ehci-hub.c" #include "ehci-hub.c"
...@@ -329,9 +330,13 @@ static void ehci_turn_off_all_ports(struct ehci_hcd *ehci) ...@@ -329,9 +330,13 @@ static void ehci_turn_off_all_ports(struct ehci_hcd *ehci)
{ {
int port = HCS_N_PORTS(ehci->hcs_params); int port = HCS_N_PORTS(ehci->hcs_params);
while (port--) while (port--) {
ehci_writel(ehci, PORT_RWC_BITS, ehci_writel(ehci, PORT_RWC_BITS,
&ehci->regs->port_status[port]); &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, ...@@ -1233,6 +1238,8 @@ void ehci_init_driver(struct hc_driver *drv,
drv->hcd_priv_size += over->extra_priv_size; drv->hcd_priv_size += over->extra_priv_size;
if (over->reset) if (over->reset)
drv->reset = over->reset; drv->reset = over->reset;
if (over->port_power)
drv->port_power = over->port_power;
} }
} }
EXPORT_SYMBOL_GPL(ehci_init_driver); EXPORT_SYMBOL_GPL(ehci_init_driver);
......
...@@ -69,10 +69,8 @@ static void ehci_handover_companion_ports(struct ehci_hcd *ehci) ...@@ -69,10 +69,8 @@ static void ehci_handover_companion_ports(struct ehci_hcd *ehci)
if (test_bit(port, &ehci->owned_ports)) { if (test_bit(port, &ehci->owned_ports)) {
reg = &ehci->regs->port_status[port]; reg = &ehci->regs->port_status[port];
status = ehci_readl(ehci, reg) & ~PORT_RWC_BITS; status = ehci_readl(ehci, reg) & ~PORT_RWC_BITS;
if (!(status & PORT_POWER)) { if (!(status & PORT_POWER))
status |= PORT_POWER; ehci_port_power(ehci, port, true);
ehci_writel(ehci, status, reg);
}
} }
} }
...@@ -952,9 +950,11 @@ int ehci_hub_control( ...@@ -952,9 +950,11 @@ int ehci_hub_control(
clear_bit(wIndex, &ehci->port_c_suspend); clear_bit(wIndex, &ehci->port_c_suspend);
break; break;
case USB_PORT_FEAT_POWER: case USB_PORT_FEAT_POWER:
if (HCS_PPC (ehci->hcs_params)) if (HCS_PPC(ehci->hcs_params)) {
ehci_writel(ehci, temp & ~PORT_POWER, spin_unlock_irqrestore(&ehci->lock, flags);
status_reg); ehci_port_power(ehci, wIndex, false);
spin_lock_irqsave(&ehci->lock, flags);
}
break; break;
case USB_PORT_FEAT_C_CONNECTION: case USB_PORT_FEAT_C_CONNECTION:
ehci_writel(ehci, temp | PORT_CSC, status_reg); ehci_writel(ehci, temp | PORT_CSC, status_reg);
...@@ -1004,9 +1004,9 @@ int ehci_hub_control( ...@@ -1004,9 +1004,9 @@ int ehci_hub_control(
*/ */
if (((temp & PORT_OC) || (ehci->need_oc_pp_cycle)) if (((temp & PORT_OC) || (ehci->need_oc_pp_cycle))
&& HCS_PPC(ehci->hcs_params)) { && HCS_PPC(ehci->hcs_params)) {
ehci_writel(ehci, spin_unlock_irqrestore(&ehci->lock, flags);
temp & ~(PORT_RWC_BITS | PORT_POWER), ehci_port_power(ehci, wIndex, false);
status_reg); spin_lock_irqsave(&ehci->lock, flags);
temp = ehci_readl(ehci, status_reg); temp = ehci_readl(ehci, status_reg);
} }
} }
...@@ -1187,9 +1187,11 @@ int ehci_hub_control( ...@@ -1187,9 +1187,11 @@ int ehci_hub_control(
set_bit(wIndex, &ehci->suspended_ports); set_bit(wIndex, &ehci->suspended_ports);
break; break;
case USB_PORT_FEAT_POWER: case USB_PORT_FEAT_POWER:
if (HCS_PPC (ehci->hcs_params)) if (HCS_PPC(ehci->hcs_params)) {
ehci_writel(ehci, temp | PORT_POWER, spin_unlock_irqrestore(&ehci->lock, flags);
status_reg); ehci_port_power(ehci, wIndex, true);
spin_lock_irqsave(&ehci->lock, flags);
}
break; break;
case USB_PORT_FEAT_RESET: case USB_PORT_FEAT_RESET:
if (temp & (PORT_SUSPEND|PORT_RESUME)) if (temp & (PORT_SUSPEND|PORT_RESUME))
...@@ -1297,3 +1299,20 @@ static int ehci_port_handed_over(struct usb_hcd *hcd, int portnum) ...@@ -1297,3 +1299,20 @@ static int ehci_port_handed_over(struct usb_hcd *hcd, int portnum)
reg = &ehci->regs->port_status[portnum - 1]; reg = &ehci->regs->port_status[portnum - 1];
return ehci_readl(ehci, reg) & PORT_OWNER; 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;
}
...@@ -859,6 +859,8 @@ static inline u32 hc32_to_cpup (const struct ehci_hcd *ehci, const __hc32 *x) ...@@ -859,6 +859,8 @@ static inline u32 hc32_to_cpup (const struct ehci_hcd *ehci, const __hc32 *x)
struct ehci_driver_overrides { struct ehci_driver_overrides {
size_t extra_priv_size; size_t extra_priv_size;
int (*reset)(struct usb_hcd *hcd); 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, extern void ehci_init_driver(struct hc_driver *drv,
......
...@@ -379,6 +379,9 @@ struct hc_driver { ...@@ -379,6 +379,9 @@ struct hc_driver {
int (*disable_usb3_lpm_timeout)(struct usb_hcd *, int (*disable_usb3_lpm_timeout)(struct usb_hcd *,
struct usb_device *, enum usb3_link_state state); struct usb_device *, enum usb3_link_state state);
int (*find_raw_port_number)(struct usb_hcd *, int); int (*find_raw_port_number)(struct usb_hcd *, int);
/* Call for power on/off the port if necessary */
int (*port_power)(struct usb_hcd *hcd, int portnum, bool enable);
}; };
static inline int hcd_giveback_urb_in_bh(struct usb_hcd *hcd) static inline int hcd_giveback_urb_in_bh(struct usb_hcd *hcd)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment