diff options
-rw-r--r-- | drivers/usb/host/ehci-hcd.c | 6 | ||||
-rw-r--r-- | drivers/usb/host/ehci-hub.c | 62 | ||||
-rw-r--r-- | drivers/usb/host/ehci.h | 3 | ||||
-rw-r--r-- | include/linux/usb/ehci_def.h | 13 |
4 files changed, 78 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 | |||
548 | ehci_port_speed(struct ehci_hcd *ehci, unsigned int portsc) | 549 | ehci_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: |
diff --git a/include/linux/usb/ehci_def.h b/include/linux/usb/ehci_def.h index 5b88e36c9103..4c4b701ff265 100644 --- a/include/linux/usb/ehci_def.h +++ b/include/linux/usb/ehci_def.h | |||
@@ -132,6 +132,19 @@ struct ehci_regs { | |||
132 | #define USBMODE_CM_HC (3<<0) /* host controller mode */ | 132 | #define USBMODE_CM_HC (3<<0) /* host controller mode */ |
133 | #define USBMODE_CM_IDLE (0<<0) /* idle state */ | 133 | #define USBMODE_CM_IDLE (0<<0) /* idle state */ |
134 | 134 | ||
135 | /* Moorestown has some non-standard registers, partially due to the fact that | ||
136 | * its EHCI controller has both TT and LPM support. HOSTPCx are extentions to | ||
137 | * PORTSCx | ||
138 | */ | ||
139 | #define HOSTPC0 0x84 /* HOSTPC extension */ | ||
140 | #define HOSTPC_PHCD (1<<22) /* Phy clock disable */ | ||
141 | #define HOSTPC_PSPD (3<<25) /* Port speed detection */ | ||
142 | #define USBMODE_EX 0xc8 /* USB Device mode extension */ | ||
143 | #define USBMODE_EX_VBPS (1<<5) /* VBus Power Select On */ | ||
144 | #define USBMODE_EX_HC (3<<0) /* host controller mode */ | ||
145 | #define TXFILLTUNING 0x24 /* TX FIFO Tuning register */ | ||
146 | #define TXFIFO_DEFAULT (8<<16) /* FIFO burst threshold 8 */ | ||
147 | |||
135 | /* Appendix C, Debug port ... intended for use with special "debug devices" | 148 | /* Appendix C, Debug port ... intended for use with special "debug devices" |
136 | * that can help if there's no serial console. (nonstandard enumeration.) | 149 | * that can help if there's no serial console. (nonstandard enumeration.) |
137 | */ | 150 | */ |