aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorAlek Du <alek.du@intel.com>2009-07-13 00:41:20 -0400
committerGreg Kroah-Hartman <gregkh@suse.de>2009-09-23 09:46:29 -0400
commit331ac6b288d9f3689514ced1878041fb0df7e13c (patch)
treec2a9d0187fea9224b81a0460b3fbe7f2e4ed0c21 /drivers
parent3807e26d69b9ad3864fe03224ebebc9610d5802e (diff)
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>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/usb/host/ehci-hcd.c6
-rw-r--r--drivers/usb/host/ehci-hub.c62
-rw-r--r--drivers/usb/host/ehci.h3
3 files changed, 65 insertions, 6 deletions
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
index 7bee1638dfd7..5fbc67c5a913 100644
--- a/drivers/usb/host/ehci-hcd.c
+++ b/drivers/usb/host/ehci-hcd.c
@@ -249,6 +249,12 @@ static int ehci_reset (struct ehci_hcd *ehci)
249 retval = handshake (ehci, &ehci->regs->command, 249 retval = handshake (ehci, &ehci->regs->command,
250 CMD_RESET, 0, 250 * 1000); 250 CMD_RESET, 0, 250 * 1000);
251 251
252 if (ehci->has_hostpc) {
253 ehci_writel(ehci, USBMODE_EX_HC | USBMODE_EX_VBPS,
254 (u32 __iomem *)(((u8 *)ehci->regs) + USBMODE_EX));
255 ehci_writel(ehci, TXFIFO_DEFAULT,
256 (u32 __iomem *)(((u8 *)ehci->regs) + TXFILLTUNING));
257 }
252 if (retval) 258 if (retval)
253 return retval; 259 return retval;
254 260
diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c
index f46ad27c9a90..6fef1ee7d101 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)
111 struct ehci_hcd *ehci = hcd_to_ehci (hcd); 111 struct ehci_hcd *ehci = hcd_to_ehci (hcd);
112 int port; 112 int port;
113 int mask; 113 int mask;
114 u32 __iomem *hostpc_reg = NULL;
114 115
115 ehci_dbg(ehci, "suspend root hub\n"); 116 ehci_dbg(ehci, "suspend root hub\n");
116 117
@@ -142,6 +143,9 @@ static int ehci_bus_suspend (struct usb_hcd *hcd)
142 u32 t1 = ehci_readl(ehci, reg) & ~PORT_RWC_BITS; 143 u32 t1 = ehci_readl(ehci, reg) & ~PORT_RWC_BITS;
143 u32 t2 = t1; 144 u32 t2 = t1;
144 145
146 if (ehci->has_hostpc)
147 hostpc_reg = (u32 __iomem *)((u8 *)ehci->regs
148 + HOSTPC0 + 4 * (port & 0xff));
145 /* keep track of which ports we suspend */ 149 /* keep track of which ports we suspend */
146 if (t1 & PORT_OWNER) 150 if (t1 & PORT_OWNER)
147 set_bit(port, &ehci->owned_ports); 151 set_bit(port, &ehci->owned_ports);
@@ -151,15 +155,37 @@ static int ehci_bus_suspend (struct usb_hcd *hcd)
151 } 155 }
152 156
153 /* enable remote wakeup on all ports */ 157 /* enable remote wakeup on all ports */
154 if (hcd->self.root_hub->do_remote_wakeup) 158 if (hcd->self.root_hub->do_remote_wakeup) {
155 t2 |= PORT_WAKE_BITS; 159 /* only enable appropriate wake bits, otherwise the
156 else 160 * hardware can not go phy low power mode. If a race
161 * condition happens here(connection change during bits
162 * set), the port change detection will finally fix it.
163 */
164 if (t1 & PORT_CONNECT) {
165 t2 |= PORT_WKOC_E | PORT_WKDISC_E;
166 t2 &= ~PORT_WKCONN_E;
167 } else {
168 t2 |= PORT_WKOC_E | PORT_WKCONN_E;
169 t2 &= ~PORT_WKDISC_E;
170 }
171 } else
157 t2 &= ~PORT_WAKE_BITS; 172 t2 &= ~PORT_WAKE_BITS;
158 173
159 if (t1 != t2) { 174 if (t1 != t2) {
160 ehci_vdbg (ehci, "port %d, %08x -> %08x\n", 175 ehci_vdbg (ehci, "port %d, %08x -> %08x\n",
161 port + 1, t1, t2); 176 port + 1, t1, t2);
162 ehci_writel(ehci, t2, reg); 177 ehci_writel(ehci, t2, reg);
178 if (hostpc_reg) {
179 u32 t3;
180
181 msleep(5);/* 5ms for HCD enter low pwr mode */
182 t3 = ehci_readl(ehci, hostpc_reg);
183 ehci_writel(ehci, t3 | HOSTPC_PHCD, hostpc_reg);
184 t3 = ehci_readl(ehci, hostpc_reg);
185 ehci_dbg(ehci, "Port%d phy low pwr mode %s\n",
186 port, (t3 & HOSTPC_PHCD) ?
187 "succeeded" : "failed");
188 }
163 } 189 }
164 } 190 }
165 191
@@ -563,7 +589,8 @@ static int ehci_hub_control (
563 int ports = HCS_N_PORTS (ehci->hcs_params); 589 int ports = HCS_N_PORTS (ehci->hcs_params);
564 u32 __iomem *status_reg = &ehci->regs->port_status[ 590 u32 __iomem *status_reg = &ehci->regs->port_status[
565 (wIndex & 0xff) - 1]; 591 (wIndex & 0xff) - 1];
566 u32 temp, status; 592 u32 __iomem *hostpc_reg = NULL;
593 u32 temp, temp1, status;
567 unsigned long flags; 594 unsigned long flags;
568 int retval = 0; 595 int retval = 0;
569 unsigned selector; 596 unsigned selector;
@@ -575,6 +602,9 @@ static int ehci_hub_control (
575 * power, "this is the one", etc. EHCI spec supports this. 602 * power, "this is the one", etc. EHCI spec supports this.
576 */ 603 */
577 604
605 if (ehci->has_hostpc)
606 hostpc_reg = (u32 __iomem *)((u8 *)ehci->regs
607 + HOSTPC0 + 4 * ((wIndex & 0xff) - 1));
578 spin_lock_irqsave (&ehci->lock, flags); 608 spin_lock_irqsave (&ehci->lock, flags);
579 switch (typeReq) { 609 switch (typeReq) {
580 case ClearHubFeature: 610 case ClearHubFeature:
@@ -773,7 +803,11 @@ static int ehci_hub_control (
773 if (temp & PORT_CONNECT) { 803 if (temp & PORT_CONNECT) {
774 status |= 1 << USB_PORT_FEAT_CONNECTION; 804 status |= 1 << USB_PORT_FEAT_CONNECTION;
775 // status may be from integrated TT 805 // status may be from integrated TT
776 status |= ehci_port_speed(ehci, temp); 806 if (ehci->has_hostpc) {
807 temp1 = ehci_readl(ehci, hostpc_reg);
808 status |= ehci_port_speed(ehci, temp1);
809 } else
810 status |= ehci_port_speed(ehci, temp);
777 } 811 }
778 if (temp & PORT_PE) 812 if (temp & PORT_PE)
779 status |= 1 << USB_PORT_FEAT_ENABLE; 813 status |= 1 << USB_PORT_FEAT_ENABLE;
@@ -832,6 +866,24 @@ static int ehci_hub_control (
832 || (temp & PORT_RESET) != 0) 866 || (temp & PORT_RESET) != 0)
833 goto error; 867 goto error;
834 ehci_writel(ehci, temp | PORT_SUSPEND, status_reg); 868 ehci_writel(ehci, temp | PORT_SUSPEND, status_reg);
869 /* After above check the port must be connected.
870 * Set appropriate bit thus could put phy into low power
871 * mode if we have hostpc feature
872 */
873 if (hostpc_reg) {
874 temp &= ~PORT_WKCONN_E;
875 temp |= (PORT_WKDISC_E | PORT_WKOC_E);
876 ehci_writel(ehci, temp | PORT_SUSPEND,
877 status_reg);
878 msleep(5);/* 5ms for HCD enter low pwr mode */
879 temp1 = ehci_readl(ehci, hostpc_reg);
880 ehci_writel(ehci, temp1 | HOSTPC_PHCD,
881 hostpc_reg);
882 temp1 = ehci_readl(ehci, hostpc_reg);
883 ehci_dbg(ehci, "Port%d phy low pwr mode %s\n",
884 wIndex, (temp1 & HOSTPC_PHCD) ?
885 "succeeded" : "failed");
886 }
835 set_bit(wIndex, &ehci->suspended_ports); 887 set_bit(wIndex, &ehci->suspended_ports);
836 break; 888 break;
837 case USB_PORT_FEAT_POWER: 889 case USB_PORT_FEAT_POWER:
diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h
index fa12f20fbfe2..ec3dba6b8e48 100644
--- a/drivers/usb/host/ehci.h
+++ b/drivers/usb/host/ehci.h
@@ -136,6 +136,7 @@ struct ehci_hcd { /* one per controller */
136 #define OHCI_HCCTRL_OFFSET 0x4 136 #define OHCI_HCCTRL_OFFSET 0x4
137 #define OHCI_HCCTRL_LEN 0x4 137 #define OHCI_HCCTRL_LEN 0x4
138 __hc32 *ohci_hcctrl_reg; 138 __hc32 *ohci_hcctrl_reg;
139 unsigned has_hostpc:1;
139 140
140 u8 sbrn; /* packed release number */ 141 u8 sbrn; /* packed release number */
141 142
@@ -548,7 +549,7 @@ static inline unsigned int
548ehci_port_speed(struct ehci_hcd *ehci, unsigned int portsc) 549ehci_port_speed(struct ehci_hcd *ehci, unsigned int portsc)
549{ 550{
550 if (ehci_is_TDI(ehci)) { 551 if (ehci_is_TDI(ehci)) {
551 switch ((portsc>>26)&3) { 552 switch ((portsc >> (ehci->has_hostpc ? 25 : 26)) & 3) {
552 case 0: 553 case 0:
553 return 0; 554 return 0;
554 case 1: 555 case 1: