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
4 changed files with 78 additions and 6 deletions
|
@ -249,6 +249,12 @@ 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, 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)
|
||||
return retval;
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -136,6 +136,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 */
|
||||
|
||||
|
@ -548,7 +549,7 @@ static inline unsigned int
|
|||
ehci_port_speed(struct ehci_hcd *ehci, unsigned int portsc)
|
||||
{
|
||||
if (ehci_is_TDI(ehci)) {
|
||||
switch ((portsc>>26)&3) {
|
||||
switch ((portsc >> (ehci->has_hostpc ? 25 : 26)) & 3) {
|
||||
case 0:
|
||||
return 0;
|
||||
case 1:
|
||||
|
|
|
@ -132,6 +132,19 @@ 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 /* 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"
|
||||
* that can help if there's no serial console. (nonstandard enumeration.)
|
||||
*/
|
||||
|
|
Loading…
Reference in a new issue