>From b6295cd7977ce3384dca7bd9a510e7571cc62496 Mon Sep 17 00:00:00 2001 From: Alek Du <alek.du@xxxxxxxxx> Date: Wed, 10 Jun 2009 14:38:15 +0800 Subject: [PATCH] EHCI: Add Intel Moorestown EHCI controller HOSTPCx extensions and support low power mode The Intel Moorestown EHCI controller supports non-standard HOSTPCx register extension. This register controls the LPM behaviour and controls the behaviour of each USB port. Signed-off-by: Jacob Pan <jacob.jun.pan@xxxxxxxxx> Signed-off-by: Alek Du <alek.du@xxxxxxxxx> --- drivers/usb/host/ehci-hcd.c | 4 +++ drivers/usb/host/ehci-hub.c | 59 +++++++++++++++++++++++++++++++++++++---- drivers/usb/host/ehci.h | 9 ++++++ include/linux/usb/ehci_def.h | 9 ++++++ 4 files changed, 75 insertions(+), 6 deletions(-) diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 36cedc9..a546ce6 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -249,6 +249,10 @@ static int ehci_reset (struct ehci_hcd *ehci) retval = handshake (ehci, &ehci->regs->command, CMD_RESET, 0, 250 * 1000); + if (ehci->has_hostpc) { + ehci_writel(ehci, 0x23, &ehci->regs->command + (0xc8/4)); + ehci_writel(ehci, 0x80000, &ehci->regs->command + (0x24/4)); + } if (retval) return retval; diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c index 97a53a4..51be77a 100644 --- a/drivers/usb/host/ehci-hub.c +++ b/drivers/usb/host/ehci-hub.c @@ -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 *)((unsigned char *)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,31 @@ 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) { + 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); + 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"); + } } } @@ -242,7 +262,8 @@ static int ehci_bus_resume (struct usb_hcd *hcd) (temp & PORT_SUSPEND)) { temp |= PORT_RESUME; resume_needed = 1; - } + } else if ((temp & (PORT_SUSPEND) && (temp & PORT_CONNECT))) + temp |= PORT_WKOC_E | PORT_WKDISC_E; ehci_writel(ehci, temp, &ehci->regs->port_status [i]); } @@ -563,7 +584,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; @@ -576,6 +598,9 @@ static int ehci_hub_control ( */ spin_lock_irqsave (&ehci->lock, flags); + if (ehci->has_hostpc) + hostpc_reg = (u32 __iomem *)((unsigned char *)ehci->regs + + HOSTPC0 + 4 * ((wIndex & 0xff) - 1)); switch (typeReq) { case ClearHubFeature: switch (wValue) { @@ -773,7 +798,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; @@ -831,7 +860,25 @@ static int ehci_hub_control ( if ((temp & PORT_PE) == 0 || (temp & PORT_RESET) != 0) goto error; + if (temp & PORT_CONNECT) { + temp &= ~PORT_WKCONN_E; + temp |= (PORT_WKDISC_E | PORT_WKOC_E); + } else { + temp |= (PORT_WKCONN_E | PORT_WKOC_E); + temp &= ~PORT_WKDISC_E; + } ehci_writel(ehci, temp | PORT_SUSPEND, status_reg); + /* Put phy in low power suspend with HOSTPCx reg */ + if (hostpc_reg) { + msleep(5); + 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: diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index d04d018..3cfc3cc 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -135,6 +135,7 @@ struct ehci_hcd { /* one per controller */ #define OHCI_HCCTRL_OFFSET 0x4 #define OHCI_HCCTRL_LEN 0x4 __hc32 *ohci_hcctrl_reg; + unsigned has_hostpc:1; u8 sbrn; /* packed release number */ @@ -542,6 +543,14 @@ static inline unsigned int ehci_port_speed(struct ehci_hcd *ehci, unsigned int portsc) { if (ehci_is_TDI(ehci)) { + if (ehci->has_hostpc) { + if (portsc & (1<<25)) + return 1<<USB_PORT_FEAT_LOWSPEED; + if (portsc & (1<<26)) + return 1<<USB_PORT_FEAT_HIGHSPEED; + else + return 0; /* Full speed */ + } switch ((portsc>>26)&3) { case 0: return 0; diff --git a/include/linux/usb/ehci_def.h b/include/linux/usb/ehci_def.h index 5b88e36..e3607b0 100644 --- a/include/linux/usb/ehci_def.h +++ b/include/linux/usb/ehci_def.h @@ -132,6 +132,15 @@ struct ehci_regs { #define USBMODE_CM_HC (3<<0) /* host controller mode */ #define USBMODE_CM_IDLE (0<<0) /* idle state */ +/* Moorestown has some non-standard registers, partially due to the fact that + * its EHCI controller has both TT and LPM support. HOSTPCx are extentions to + * PORTSCx + */ +#define HOSTPC0 0x84 +#define HOSTPC_PHCD (1<<22) +#define HOSTPC_PSPD (3<<25) + + /* Appendix C, Debug port ... intended for use with special "debug devices" * that can help if there's no serial console. (nonstandard enumeration.) */ -- 1.6.0.4 -- 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