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: Jacob Pan <jacob.jun.pan@intel.com> Signed-off-by: Alek Du <alek.du@intel.com> Acked-by: Alan Stern <stern@rowland.harvard.edu> Cc: David Brownell <dbrownell@users.sourceforge.net> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
This commit is contained in:
parent
3807e26d69
commit
331ac6b288
|
@ -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)
|
||||||
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)
|
||||||
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)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 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 (
|
||||||
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 (
|
||||||
* 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,7 +803,11 @@ 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
|
||||||
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)
|
if (temp & PORT_PE)
|
||||||
status |= 1 << USB_PORT_FEAT_ENABLE;
|
status |= 1 << USB_PORT_FEAT_ENABLE;
|
||||||
|
@ -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 */
|
||||||
#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
|
||||||
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 {
|
||||||
#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.)
|
||||||
*/
|
*/
|
||||||
|
|
Loading…
Reference in New Issue