|
@@ -111,6 +111,7 @@ static int ehci_bus_suspend (struct usb_hcd *hcd)
|
|
|
struct ehci_hcd *ehci = hcd_to_ehci (hcd);
|
|
|
int port;
|
|
|
int mask;
|
|
|
+ u32 __iomem *hostpc_reg = NULL;
|
|
|
|
|
|
ehci_dbg(ehci, "suspend root hub\n");
|
|
|
|
|
@@ -142,6 +143,9 @@ static int ehci_bus_suspend (struct usb_hcd *hcd)
|
|
|
u32 t1 = ehci_readl(ehci, reg) & ~PORT_RWC_BITS;
|
|
|
u32 t2 = t1;
|
|
|
|
|
|
+ if (ehci->has_hostpc)
|
|
|
+ hostpc_reg = (u32 __iomem *)((u8 *)ehci->regs
|
|
|
+ + HOSTPC0 + 4 * (port & 0xff));
|
|
|
/* keep track of which ports we suspend */
|
|
|
if (t1 & PORT_OWNER)
|
|
|
set_bit(port, &ehci->owned_ports);
|
|
@@ -151,15 +155,37 @@ static int ehci_bus_suspend (struct usb_hcd *hcd)
|
|
|
}
|
|
|
|
|
|
/* enable remote wakeup on all ports */
|
|
|
- if (hcd->self.root_hub->do_remote_wakeup)
|
|
|
- t2 |= PORT_WAKE_BITS;
|
|
|
- else
|
|
|
+ if (hcd->self.root_hub->do_remote_wakeup) {
|
|
|
+ /* only enable appropriate wake bits, otherwise the
|
|
|
+ * hardware can not go phy low power mode. If a race
|
|
|
+ * condition happens here(connection change during bits
|
|
|
+ * set), the port change detection will finally fix it.
|
|
|
+ */
|
|
|
+ if (t1 & PORT_CONNECT) {
|
|
|
+ t2 |= PORT_WKOC_E | PORT_WKDISC_E;
|
|
|
+ t2 &= ~PORT_WKCONN_E;
|
|
|
+ } else {
|
|
|
+ t2 |= PORT_WKOC_E | PORT_WKCONN_E;
|
|
|
+ t2 &= ~PORT_WKDISC_E;
|
|
|
+ }
|
|
|
+ } else
|
|
|
t2 &= ~PORT_WAKE_BITS;
|
|
|
|
|
|
if (t1 != t2) {
|
|
|
ehci_vdbg (ehci, "port %d, %08x -> %08x\n",
|
|
|
port + 1, t1, t2);
|
|
|
ehci_writel(ehci, t2, reg);
|
|
|
+ if (hostpc_reg) {
|
|
|
+ u32 t3;
|
|
|
+
|
|
|
+ msleep(5);/* 5ms for HCD enter low pwr mode */
|
|
|
+ t3 = ehci_readl(ehci, hostpc_reg);
|
|
|
+ ehci_writel(ehci, t3 | HOSTPC_PHCD, hostpc_reg);
|
|
|
+ t3 = ehci_readl(ehci, hostpc_reg);
|
|
|
+ ehci_dbg(ehci, "Port%d phy low pwr mode %s\n",
|
|
|
+ port, (t3 & HOSTPC_PHCD) ?
|
|
|
+ "succeeded" : "failed");
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -563,7 +589,8 @@ static int ehci_hub_control (
|
|
|
int ports = HCS_N_PORTS (ehci->hcs_params);
|
|
|
u32 __iomem *status_reg = &ehci->regs->port_status[
|
|
|
(wIndex & 0xff) - 1];
|
|
|
- u32 temp, status;
|
|
|
+ u32 __iomem *hostpc_reg = NULL;
|
|
|
+ u32 temp, temp1, status;
|
|
|
unsigned long flags;
|
|
|
int retval = 0;
|
|
|
unsigned selector;
|
|
@@ -575,6 +602,9 @@ static int ehci_hub_control (
|
|
|
* power, "this is the one", etc. EHCI spec supports this.
|
|
|
*/
|
|
|
|
|
|
+ if (ehci->has_hostpc)
|
|
|
+ hostpc_reg = (u32 __iomem *)((u8 *)ehci->regs
|
|
|
+ + HOSTPC0 + 4 * ((wIndex & 0xff) - 1));
|
|
|
spin_lock_irqsave (&ehci->lock, flags);
|
|
|
switch (typeReq) {
|
|
|
case ClearHubFeature:
|
|
@@ -773,7 +803,11 @@ static int ehci_hub_control (
|
|
|
if (temp & PORT_CONNECT) {
|
|
|
status |= 1 << USB_PORT_FEAT_CONNECTION;
|
|
|
// status may be from integrated TT
|
|
|
- status |= ehci_port_speed(ehci, temp);
|
|
|
+ if (ehci->has_hostpc) {
|
|
|
+ temp1 = ehci_readl(ehci, hostpc_reg);
|
|
|
+ status |= ehci_port_speed(ehci, temp1);
|
|
|
+ } else
|
|
|
+ status |= ehci_port_speed(ehci, temp);
|
|
|
}
|
|
|
if (temp & PORT_PE)
|
|
|
status |= 1 << USB_PORT_FEAT_ENABLE;
|
|
@@ -832,6 +866,24 @@ static int ehci_hub_control (
|
|
|
|| (temp & PORT_RESET) != 0)
|
|
|
goto error;
|
|
|
ehci_writel(ehci, temp | PORT_SUSPEND, status_reg);
|
|
|
+ /* After above check the port must be connected.
|
|
|
+ * Set appropriate bit thus could put phy into low power
|
|
|
+ * mode if we have hostpc feature
|
|
|
+ */
|
|
|
+ if (hostpc_reg) {
|
|
|
+ temp &= ~PORT_WKCONN_E;
|
|
|
+ temp |= (PORT_WKDISC_E | PORT_WKOC_E);
|
|
|
+ ehci_writel(ehci, temp | PORT_SUSPEND,
|
|
|
+ status_reg);
|
|
|
+ msleep(5);/* 5ms for HCD enter low pwr mode */
|
|
|
+ temp1 = ehci_readl(ehci, hostpc_reg);
|
|
|
+ ehci_writel(ehci, temp1 | HOSTPC_PHCD,
|
|
|
+ hostpc_reg);
|
|
|
+ temp1 = ehci_readl(ehci, hostpc_reg);
|
|
|
+ ehci_dbg(ehci, "Port%d phy low pwr mode %s\n",
|
|
|
+ wIndex, (temp1 & HOSTPC_PHCD) ?
|
|
|
+ "succeeded" : "failed");
|
|
|
+ }
|
|
|
set_bit(wIndex, &ehci->suspended_ports);
|
|
|
break;
|
|
|
case USB_PORT_FEAT_POWER:
|