diff options
Diffstat (limited to 'drivers/usb/host/ehci-hub.c')
-rw-r--r-- | drivers/usb/host/ehci-hub.c | 173 |
1 files changed, 125 insertions, 48 deletions
diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c index ef956220f854..e7d3d8def282 100644 --- a/drivers/usb/host/ehci-hub.c +++ b/drivers/usb/host/ehci-hub.c | |||
@@ -106,12 +106,75 @@ static void ehci_handover_companion_ports(struct ehci_hcd *ehci) | |||
106 | ehci->owned_ports = 0; | 106 | ehci->owned_ports = 0; |
107 | } | 107 | } |
108 | 108 | ||
109 | static void ehci_adjust_port_wakeup_flags(struct ehci_hcd *ehci, | ||
110 | bool suspending) | ||
111 | { | ||
112 | int port; | ||
113 | u32 temp; | ||
114 | |||
115 | /* If remote wakeup is enabled for the root hub but disabled | ||
116 | * for the controller, we must adjust all the port wakeup flags | ||
117 | * when the controller is suspended or resumed. In all other | ||
118 | * cases they don't need to be changed. | ||
119 | */ | ||
120 | if (!ehci_to_hcd(ehci)->self.root_hub->do_remote_wakeup || | ||
121 | device_may_wakeup(ehci_to_hcd(ehci)->self.controller)) | ||
122 | return; | ||
123 | |||
124 | /* clear phy low-power mode before changing wakeup flags */ | ||
125 | if (ehci->has_hostpc) { | ||
126 | port = HCS_N_PORTS(ehci->hcs_params); | ||
127 | while (port--) { | ||
128 | u32 __iomem *hostpc_reg; | ||
129 | |||
130 | hostpc_reg = (u32 __iomem *)((u8 *) ehci->regs | ||
131 | + HOSTPC0 + 4 * port); | ||
132 | temp = ehci_readl(ehci, hostpc_reg); | ||
133 | ehci_writel(ehci, temp & ~HOSTPC_PHCD, hostpc_reg); | ||
134 | } | ||
135 | msleep(5); | ||
136 | } | ||
137 | |||
138 | port = HCS_N_PORTS(ehci->hcs_params); | ||
139 | while (port--) { | ||
140 | u32 __iomem *reg = &ehci->regs->port_status[port]; | ||
141 | u32 t1 = ehci_readl(ehci, reg) & ~PORT_RWC_BITS; | ||
142 | u32 t2 = t1 & ~PORT_WAKE_BITS; | ||
143 | |||
144 | /* If we are suspending the controller, clear the flags. | ||
145 | * If we are resuming the controller, set the wakeup flags. | ||
146 | */ | ||
147 | if (!suspending) { | ||
148 | if (t1 & PORT_CONNECT) | ||
149 | t2 |= PORT_WKOC_E | PORT_WKDISC_E; | ||
150 | else | ||
151 | t2 |= PORT_WKOC_E | PORT_WKCONN_E; | ||
152 | } | ||
153 | ehci_vdbg(ehci, "port %d, %08x -> %08x\n", | ||
154 | port + 1, t1, t2); | ||
155 | ehci_writel(ehci, t2, reg); | ||
156 | } | ||
157 | |||
158 | /* enter phy low-power mode again */ | ||
159 | if (ehci->has_hostpc) { | ||
160 | port = HCS_N_PORTS(ehci->hcs_params); | ||
161 | while (port--) { | ||
162 | u32 __iomem *hostpc_reg; | ||
163 | |||
164 | hostpc_reg = (u32 __iomem *)((u8 *) ehci->regs | ||
165 | + HOSTPC0 + 4 * port); | ||
166 | temp = ehci_readl(ehci, hostpc_reg); | ||
167 | ehci_writel(ehci, temp | HOSTPC_PHCD, hostpc_reg); | ||
168 | } | ||
169 | } | ||
170 | } | ||
171 | |||
109 | static int ehci_bus_suspend (struct usb_hcd *hcd) | 172 | static int ehci_bus_suspend (struct usb_hcd *hcd) |
110 | { | 173 | { |
111 | struct ehci_hcd *ehci = hcd_to_ehci (hcd); | 174 | struct ehci_hcd *ehci = hcd_to_ehci (hcd); |
112 | int port; | 175 | int port; |
113 | int mask; | 176 | int mask; |
114 | u32 __iomem *hostpc_reg = NULL; | 177 | int changed; |
115 | 178 | ||
116 | ehci_dbg(ehci, "suspend root hub\n"); | 179 | ehci_dbg(ehci, "suspend root hub\n"); |
117 | 180 | ||
@@ -155,15 +218,13 @@ static int ehci_bus_suspend (struct usb_hcd *hcd) | |||
155 | */ | 218 | */ |
156 | ehci->bus_suspended = 0; | 219 | ehci->bus_suspended = 0; |
157 | ehci->owned_ports = 0; | 220 | ehci->owned_ports = 0; |
221 | changed = 0; | ||
158 | port = HCS_N_PORTS(ehci->hcs_params); | 222 | port = HCS_N_PORTS(ehci->hcs_params); |
159 | while (port--) { | 223 | while (port--) { |
160 | u32 __iomem *reg = &ehci->regs->port_status [port]; | 224 | u32 __iomem *reg = &ehci->regs->port_status [port]; |
161 | u32 t1 = ehci_readl(ehci, reg) & ~PORT_RWC_BITS; | 225 | u32 t1 = ehci_readl(ehci, reg) & ~PORT_RWC_BITS; |
162 | u32 t2 = t1; | 226 | u32 t2 = t1 & ~PORT_WAKE_BITS; |
163 | 227 | ||
164 | if (ehci->has_hostpc) | ||
165 | hostpc_reg = (u32 __iomem *)((u8 *)ehci->regs | ||
166 | + HOSTPC0 + 4 * (port & 0xff)); | ||
167 | /* keep track of which ports we suspend */ | 228 | /* keep track of which ports we suspend */ |
168 | if (t1 & PORT_OWNER) | 229 | if (t1 & PORT_OWNER) |
169 | set_bit(port, &ehci->owned_ports); | 230 | set_bit(port, &ehci->owned_ports); |
@@ -172,40 +233,45 @@ static int ehci_bus_suspend (struct usb_hcd *hcd) | |||
172 | set_bit(port, &ehci->bus_suspended); | 233 | set_bit(port, &ehci->bus_suspended); |
173 | } | 234 | } |
174 | 235 | ||
175 | /* enable remote wakeup on all ports */ | 236 | /* enable remote wakeup on all ports, if told to do so */ |
176 | if (hcd->self.root_hub->do_remote_wakeup) { | 237 | if (hcd->self.root_hub->do_remote_wakeup) { |
177 | /* only enable appropriate wake bits, otherwise the | 238 | /* only enable appropriate wake bits, otherwise the |
178 | * hardware can not go phy low power mode. If a race | 239 | * hardware can not go phy low power mode. If a race |
179 | * condition happens here(connection change during bits | 240 | * condition happens here(connection change during bits |
180 | * set), the port change detection will finally fix it. | 241 | * set), the port change detection will finally fix it. |
181 | */ | 242 | */ |
182 | if (t1 & PORT_CONNECT) { | 243 | if (t1 & PORT_CONNECT) |
183 | t2 |= PORT_WKOC_E | PORT_WKDISC_E; | 244 | t2 |= PORT_WKOC_E | PORT_WKDISC_E; |
184 | t2 &= ~PORT_WKCONN_E; | 245 | else |
185 | } else { | ||
186 | t2 |= PORT_WKOC_E | PORT_WKCONN_E; | 246 | t2 |= PORT_WKOC_E | PORT_WKCONN_E; |
187 | t2 &= ~PORT_WKDISC_E; | 247 | } |
188 | } | ||
189 | } else | ||
190 | t2 &= ~PORT_WAKE_BITS; | ||
191 | 248 | ||
192 | if (t1 != t2) { | 249 | if (t1 != t2) { |
193 | ehci_vdbg (ehci, "port %d, %08x -> %08x\n", | 250 | ehci_vdbg (ehci, "port %d, %08x -> %08x\n", |
194 | port + 1, t1, t2); | 251 | port + 1, t1, t2); |
195 | ehci_writel(ehci, t2, reg); | 252 | ehci_writel(ehci, t2, reg); |
196 | if (hostpc_reg) { | 253 | changed = 1; |
197 | u32 t3; | 254 | } |
255 | } | ||
198 | 256 | ||
199 | spin_unlock_irq(&ehci->lock); | 257 | if (changed && ehci->has_hostpc) { |
200 | msleep(5);/* 5ms for HCD enter low pwr mode */ | 258 | spin_unlock_irq(&ehci->lock); |
201 | spin_lock_irq(&ehci->lock); | 259 | msleep(5); /* 5 ms for HCD to enter low-power mode */ |
202 | t3 = ehci_readl(ehci, hostpc_reg); | 260 | spin_lock_irq(&ehci->lock); |
203 | ehci_writel(ehci, t3 | HOSTPC_PHCD, hostpc_reg); | 261 | |
204 | t3 = ehci_readl(ehci, hostpc_reg); | 262 | port = HCS_N_PORTS(ehci->hcs_params); |
205 | ehci_dbg(ehci, "Port%d phy low pwr mode %s\n", | 263 | while (port--) { |
264 | u32 __iomem *hostpc_reg; | ||
265 | u32 t3; | ||
266 | |||
267 | hostpc_reg = (u32 __iomem *)((u8 *) ehci->regs | ||
268 | + HOSTPC0 + 4 * port); | ||
269 | t3 = ehci_readl(ehci, hostpc_reg); | ||
270 | ehci_writel(ehci, t3 | HOSTPC_PHCD, hostpc_reg); | ||
271 | t3 = ehci_readl(ehci, hostpc_reg); | ||
272 | ehci_dbg(ehci, "Port %d phy low-power mode %s\n", | ||
206 | port, (t3 & HOSTPC_PHCD) ? | 273 | port, (t3 & HOSTPC_PHCD) ? |
207 | "succeeded" : "failed"); | 274 | "succeeded" : "failed"); |
208 | } | ||
209 | } | 275 | } |
210 | } | 276 | } |
211 | 277 | ||
@@ -291,19 +357,28 @@ static int ehci_bus_resume (struct usb_hcd *hcd) | |||
291 | msleep(8); | 357 | msleep(8); |
292 | spin_lock_irq(&ehci->lock); | 358 | spin_lock_irq(&ehci->lock); |
293 | 359 | ||
360 | /* clear phy low-power mode before resume */ | ||
361 | if (ehci->bus_suspended && ehci->has_hostpc) { | ||
362 | i = HCS_N_PORTS(ehci->hcs_params); | ||
363 | while (i--) { | ||
364 | if (test_bit(i, &ehci->bus_suspended)) { | ||
365 | u32 __iomem *hostpc_reg; | ||
366 | |||
367 | hostpc_reg = (u32 __iomem *)((u8 *) ehci->regs | ||
368 | + HOSTPC0 + 4 * i); | ||
369 | temp = ehci_readl(ehci, hostpc_reg); | ||
370 | ehci_writel(ehci, temp & ~HOSTPC_PHCD, | ||
371 | hostpc_reg); | ||
372 | } | ||
373 | } | ||
374 | spin_unlock_irq(&ehci->lock); | ||
375 | msleep(5); | ||
376 | spin_lock_irq(&ehci->lock); | ||
377 | } | ||
378 | |||
294 | /* manually resume the ports we suspended during bus_suspend() */ | 379 | /* manually resume the ports we suspended during bus_suspend() */ |
295 | i = HCS_N_PORTS (ehci->hcs_params); | 380 | i = HCS_N_PORTS (ehci->hcs_params); |
296 | while (i--) { | 381 | while (i--) { |
297 | /* clear phy low power mode before resume */ | ||
298 | if (ehci->has_hostpc) { | ||
299 | u32 __iomem *hostpc_reg = | ||
300 | (u32 __iomem *)((u8 *)ehci->regs | ||
301 | + HOSTPC0 + 4 * (i & 0xff)); | ||
302 | temp = ehci_readl(ehci, hostpc_reg); | ||
303 | ehci_writel(ehci, temp & ~HOSTPC_PHCD, | ||
304 | hostpc_reg); | ||
305 | mdelay(5); | ||
306 | } | ||
307 | temp = ehci_readl(ehci, &ehci->regs->port_status [i]); | 382 | temp = ehci_readl(ehci, &ehci->regs->port_status [i]); |
308 | temp &= ~(PORT_RWC_BITS | PORT_WAKE_BITS); | 383 | temp &= ~(PORT_RWC_BITS | PORT_WAKE_BITS); |
309 | if (test_bit(i, &ehci->bus_suspended) && | 384 | if (test_bit(i, &ehci->bus_suspended) && |
@@ -685,23 +760,25 @@ static int ehci_hub_control ( | |||
685 | goto error; | 760 | goto error; |
686 | if (ehci->no_selective_suspend) | 761 | if (ehci->no_selective_suspend) |
687 | break; | 762 | break; |
688 | if (temp & PORT_SUSPEND) { | 763 | if (!(temp & PORT_SUSPEND)) |
689 | if ((temp & PORT_PE) == 0) | 764 | break; |
690 | goto error; | 765 | if ((temp & PORT_PE) == 0) |
691 | /* clear phy low power mode before resume */ | 766 | goto error; |
692 | if (hostpc_reg) { | 767 | |
693 | temp1 = ehci_readl(ehci, hostpc_reg); | 768 | /* clear phy low-power mode before resume */ |
694 | ehci_writel(ehci, temp1 & ~HOSTPC_PHCD, | 769 | if (hostpc_reg) { |
770 | temp1 = ehci_readl(ehci, hostpc_reg); | ||
771 | ehci_writel(ehci, temp1 & ~HOSTPC_PHCD, | ||
695 | hostpc_reg); | 772 | hostpc_reg); |
696 | mdelay(5); | 773 | spin_unlock_irqrestore(&ehci->lock, flags); |
697 | } | 774 | msleep(5);/* wait to leave low-power mode */ |
698 | /* resume signaling for 20 msec */ | 775 | spin_lock_irqsave(&ehci->lock, flags); |
699 | temp &= ~(PORT_RWC_BITS | PORT_WAKE_BITS); | ||
700 | ehci_writel(ehci, temp | PORT_RESUME, | ||
701 | status_reg); | ||
702 | ehci->reset_done [wIndex] = jiffies | ||
703 | + msecs_to_jiffies (20); | ||
704 | } | 776 | } |
777 | /* resume signaling for 20 msec */ | ||
778 | temp &= ~(PORT_RWC_BITS | PORT_WAKE_BITS); | ||
779 | ehci_writel(ehci, temp | PORT_RESUME, status_reg); | ||
780 | ehci->reset_done[wIndex] = jiffies | ||
781 | + msecs_to_jiffies(20); | ||
705 | break; | 782 | break; |
706 | case USB_PORT_FEAT_C_SUSPEND: | 783 | case USB_PORT_FEAT_C_SUSPEND: |
707 | clear_bit(wIndex, &ehci->port_c_suspend); | 784 | clear_bit(wIndex, &ehci->port_c_suspend); |