Commit 331ac6b2 authored by Alek Du's avatar Alek Du Committed by Greg Kroah-Hartman

USB: EHCI: Add Intel Moorestown EHCI controller HOSTPCx extensions and support phy 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: default avatarJacob Pan <jacob.jun.pan@intel.com>
Signed-off-by: default avatarAlek Du <alek.du@intel.com>
Acked-by: default avatarAlan Stern <stern@rowland.harvard.edu>
Cc: David Brownell <dbrownell@users.sourceforge.net>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent 3807e26d
...@@ -249,6 +249,12 @@ static int ehci_reset (struct ehci_hcd *ehci) ...@@ -249,6 +249,12 @@ static int ehci_reset (struct ehci_hcd *ehci)
retval = handshake (ehci, &ehci->regs->command, retval = handshake (ehci, &ehci->regs->command,
CMD_RESET, 0, 250 * 1000); CMD_RESET, 0, 250 * 1000);
if (ehci->has_hostpc) {
ehci_writel(ehci, USBMODE_EX_HC | USBMODE_EX_VBPS,
(u32 __iomem *)(((u8 *)ehci->regs) + USBMODE_EX));
ehci_writel(ehci, TXFIFO_DEFAULT,
(u32 __iomem *)(((u8 *)ehci->regs) + TXFILLTUNING));
}
if (retval) if (retval)
return retval; return retval;
......
...@@ -111,6 +111,7 @@ static int ehci_bus_suspend (struct usb_hcd *hcd) ...@@ -111,6 +111,7 @@ static int ehci_bus_suspend (struct usb_hcd *hcd)
struct ehci_hcd *ehci = hcd_to_ehci (hcd); struct ehci_hcd *ehci = hcd_to_ehci (hcd);
int port; int port;
int mask; int mask;
u32 __iomem *hostpc_reg = NULL;
ehci_dbg(ehci, "suspend root hub\n"); ehci_dbg(ehci, "suspend root hub\n");
...@@ -142,6 +143,9 @@ static int ehci_bus_suspend (struct usb_hcd *hcd) ...@@ -142,6 +143,9 @@ static int ehci_bus_suspend (struct usb_hcd *hcd)
u32 t1 = ehci_readl(ehci, reg) & ~PORT_RWC_BITS; u32 t1 = ehci_readl(ehci, reg) & ~PORT_RWC_BITS;
u32 t2 = t1; 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 */ /* keep track of which ports we suspend */
if (t1 & PORT_OWNER) if (t1 & PORT_OWNER)
set_bit(port, &ehci->owned_ports); set_bit(port, &ehci->owned_ports);
...@@ -151,15 +155,37 @@ static int ehci_bus_suspend (struct usb_hcd *hcd) ...@@ -151,15 +155,37 @@ static int ehci_bus_suspend (struct usb_hcd *hcd)
} }
/* enable remote wakeup on all ports */ /* enable remote wakeup on all ports */
if (hcd->self.root_hub->do_remote_wakeup) if (hcd->self.root_hub->do_remote_wakeup) {
t2 |= PORT_WAKE_BITS; /* only enable appropriate wake bits, otherwise the
else * 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; t2 &= ~PORT_WAKE_BITS;
if (t1 != t2) { if (t1 != t2) {
ehci_vdbg (ehci, "port %d, %08x -> %08x\n", ehci_vdbg (ehci, "port %d, %08x -> %08x\n",
port + 1, t1, t2); port + 1, t1, t2);
ehci_writel(ehci, t2, reg); 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 ( ...@@ -563,7 +589,8 @@ static int ehci_hub_control (
int ports = HCS_N_PORTS (ehci->hcs_params); int ports = HCS_N_PORTS (ehci->hcs_params);
u32 __iomem *status_reg = &ehci->regs->port_status[ u32 __iomem *status_reg = &ehci->regs->port_status[
(wIndex & 0xff) - 1]; (wIndex & 0xff) - 1];
u32 temp, status; u32 __iomem *hostpc_reg = NULL;
u32 temp, temp1, status;
unsigned long flags; unsigned long flags;
int retval = 0; int retval = 0;
unsigned selector; unsigned selector;
...@@ -575,6 +602,9 @@ static int ehci_hub_control ( ...@@ -575,6 +602,9 @@ static int ehci_hub_control (
* power, "this is the one", etc. EHCI spec supports this. * 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); spin_lock_irqsave (&ehci->lock, flags);
switch (typeReq) { switch (typeReq) {
case ClearHubFeature: case ClearHubFeature:
...@@ -773,6 +803,10 @@ static int ehci_hub_control ( ...@@ -773,6 +803,10 @@ static int ehci_hub_control (
if (temp & PORT_CONNECT) { if (temp & PORT_CONNECT) {
status |= 1 << USB_PORT_FEAT_CONNECTION; status |= 1 << USB_PORT_FEAT_CONNECTION;
// status may be from integrated TT // status may be from integrated TT
if (ehci->has_hostpc) {
temp1 = ehci_readl(ehci, hostpc_reg);
status |= ehci_port_speed(ehci, temp1);
} else
status |= ehci_port_speed(ehci, temp); status |= ehci_port_speed(ehci, temp);
} }
if (temp & PORT_PE) if (temp & PORT_PE)
...@@ -832,6 +866,24 @@ static int ehci_hub_control ( ...@@ -832,6 +866,24 @@ static int ehci_hub_control (
|| (temp & PORT_RESET) != 0) || (temp & PORT_RESET) != 0)
goto error; goto error;
ehci_writel(ehci, temp | PORT_SUSPEND, status_reg); 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); set_bit(wIndex, &ehci->suspended_ports);
break; break;
case USB_PORT_FEAT_POWER: case USB_PORT_FEAT_POWER:
......
...@@ -136,6 +136,7 @@ struct ehci_hcd { /* one per controller */ ...@@ -136,6 +136,7 @@ struct ehci_hcd { /* one per controller */
#define OHCI_HCCTRL_OFFSET 0x4 #define OHCI_HCCTRL_OFFSET 0x4
#define OHCI_HCCTRL_LEN 0x4 #define OHCI_HCCTRL_LEN 0x4
__hc32 *ohci_hcctrl_reg; __hc32 *ohci_hcctrl_reg;
unsigned has_hostpc:1;
u8 sbrn; /* packed release number */ u8 sbrn; /* packed release number */
...@@ -548,7 +549,7 @@ static inline unsigned int ...@@ -548,7 +549,7 @@ static inline unsigned int
ehci_port_speed(struct ehci_hcd *ehci, unsigned int portsc) ehci_port_speed(struct ehci_hcd *ehci, unsigned int portsc)
{ {
if (ehci_is_TDI(ehci)) { if (ehci_is_TDI(ehci)) {
switch ((portsc>>26)&3) { switch ((portsc >> (ehci->has_hostpc ? 25 : 26)) & 3) {
case 0: case 0:
return 0; return 0;
case 1: case 1:
......
...@@ -132,6 +132,19 @@ struct ehci_regs { ...@@ -132,6 +132,19 @@ struct ehci_regs {
#define USBMODE_CM_HC (3<<0) /* host controller mode */ #define USBMODE_CM_HC (3<<0) /* host controller mode */
#define USBMODE_CM_IDLE (0<<0) /* idle state */ #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 /* HOSTPC extension */
#define HOSTPC_PHCD (1<<22) /* Phy clock disable */
#define HOSTPC_PSPD (3<<25) /* Port speed detection */
#define USBMODE_EX 0xc8 /* USB Device mode extension */
#define USBMODE_EX_VBPS (1<<5) /* VBus Power Select On */
#define USBMODE_EX_HC (3<<0) /* host controller mode */
#define TXFILLTUNING 0x24 /* TX FIFO Tuning register */
#define TXFIFO_DEFAULT (8<<16) /* FIFO burst threshold 8 */
/* Appendix C, Debug port ... intended for use with special "debug devices" /* Appendix C, Debug port ... intended for use with special "debug devices"
* that can help if there's no serial console. (nonstandard enumeration.) * that can help if there's no serial console. (nonstandard enumeration.)
*/ */
......
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