diff options
Diffstat (limited to 'drivers/usb/host')
-rw-r--r-- | drivers/usb/host/ehci-au1xxx.c | 16 | ||||
-rw-r--r-- | drivers/usb/host/ehci-fsl.c | 2 | ||||
-rw-r--r-- | drivers/usb/host/ehci-hub.c | 173 | ||||
-rw-r--r-- | drivers/usb/host/ehci-pci.c | 15 | ||||
-rw-r--r-- | drivers/usb/host/ehci.h | 10 |
5 files changed, 145 insertions, 71 deletions
diff --git a/drivers/usb/host/ehci-au1xxx.c b/drivers/usb/host/ehci-au1xxx.c index 7a27b7c4ee84..faa61748db70 100644 --- a/drivers/usb/host/ehci-au1xxx.c +++ b/drivers/usb/host/ehci-au1xxx.c | |||
@@ -224,26 +224,17 @@ static int ehci_hcd_au1xxx_drv_suspend(struct device *dev) | |||
224 | msleep(10); | 224 | msleep(10); |
225 | 225 | ||
226 | /* Root hub was already suspended. Disable irq emission and | 226 | /* Root hub was already suspended. Disable irq emission and |
227 | * mark HW unaccessible, bail out if RH has been resumed. Use | 227 | * mark HW unaccessible. The PM and USB cores make sure that |
228 | * the spinlock to properly synchronize with possible pending | 228 | * the root hub is either suspended or stopped. |
229 | * RH suspend or resume activity. | ||
230 | * | ||
231 | * This is still racy as hcd->state is manipulated outside of | ||
232 | * any locks =P But that will be a different fix. | ||
233 | */ | 229 | */ |
234 | spin_lock_irqsave(&ehci->lock, flags); | 230 | spin_lock_irqsave(&ehci->lock, flags); |
235 | if (hcd->state != HC_STATE_SUSPENDED) { | 231 | ehci_prepare_ports_for_controller_suspend(ehci); |
236 | rc = -EINVAL; | ||
237 | goto bail; | ||
238 | } | ||
239 | ehci_writel(ehci, 0, &ehci->regs->intr_enable); | 232 | ehci_writel(ehci, 0, &ehci->regs->intr_enable); |
240 | (void)ehci_readl(ehci, &ehci->regs->intr_enable); | 233 | (void)ehci_readl(ehci, &ehci->regs->intr_enable); |
241 | 234 | ||
242 | clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); | 235 | clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); |
243 | 236 | ||
244 | au1xxx_stop_ehc(); | 237 | au1xxx_stop_ehc(); |
245 | |||
246 | bail: | ||
247 | spin_unlock_irqrestore(&ehci->lock, flags); | 238 | spin_unlock_irqrestore(&ehci->lock, flags); |
248 | 239 | ||
249 | // could save FLADJ in case of Vaux power loss | 240 | // could save FLADJ in case of Vaux power loss |
@@ -273,6 +264,7 @@ static int ehci_hcd_au1xxx_drv_resume(struct device *dev) | |||
273 | if (ehci_readl(ehci, &ehci->regs->configured_flag) == FLAG_CF) { | 264 | if (ehci_readl(ehci, &ehci->regs->configured_flag) == FLAG_CF) { |
274 | int mask = INTR_MASK; | 265 | int mask = INTR_MASK; |
275 | 266 | ||
267 | ehci_prepare_ports_for_controller_resume(ehci); | ||
276 | if (!hcd->self.root_hub->do_remote_wakeup) | 268 | if (!hcd->self.root_hub->do_remote_wakeup) |
277 | mask &= ~STS_PCD; | 269 | mask &= ~STS_PCD; |
278 | ehci_writel(ehci, mask, &ehci->regs->intr_enable); | 270 | ehci_writel(ehci, mask, &ehci->regs->intr_enable); |
diff --git a/drivers/usb/host/ehci-fsl.c b/drivers/usb/host/ehci-fsl.c index 0e26aa13f158..5cd967d28938 100644 --- a/drivers/usb/host/ehci-fsl.c +++ b/drivers/usb/host/ehci-fsl.c | |||
@@ -313,6 +313,7 @@ static int ehci_fsl_drv_suspend(struct device *dev) | |||
313 | struct ehci_fsl *ehci_fsl = hcd_to_ehci_fsl(hcd); | 313 | struct ehci_fsl *ehci_fsl = hcd_to_ehci_fsl(hcd); |
314 | void __iomem *non_ehci = hcd->regs; | 314 | void __iomem *non_ehci = hcd->regs; |
315 | 315 | ||
316 | ehci_prepare_ports_for_controller_suspend(hcd_to_ehci(hcd)); | ||
316 | if (!fsl_deep_sleep()) | 317 | if (!fsl_deep_sleep()) |
317 | return 0; | 318 | return 0; |
318 | 319 | ||
@@ -327,6 +328,7 @@ static int ehci_fsl_drv_resume(struct device *dev) | |||
327 | struct ehci_hcd *ehci = hcd_to_ehci(hcd); | 328 | struct ehci_hcd *ehci = hcd_to_ehci(hcd); |
328 | void __iomem *non_ehci = hcd->regs; | 329 | void __iomem *non_ehci = hcd->regs; |
329 | 330 | ||
331 | ehci_prepare_ports_for_controller_resume(ehci); | ||
330 | if (!fsl_deep_sleep()) | 332 | if (!fsl_deep_sleep()) |
331 | return 0; | 333 | return 0; |
332 | 334 | ||
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); |
diff --git a/drivers/usb/host/ehci-pci.c b/drivers/usb/host/ehci-pci.c index d120059bbbf7..d43d176161aa 100644 --- a/drivers/usb/host/ehci-pci.c +++ b/drivers/usb/host/ehci-pci.c | |||
@@ -287,23 +287,15 @@ static int ehci_pci_suspend(struct usb_hcd *hcd) | |||
287 | msleep(10); | 287 | msleep(10); |
288 | 288 | ||
289 | /* Root hub was already suspended. Disable irq emission and | 289 | /* Root hub was already suspended. Disable irq emission and |
290 | * mark HW unaccessible, bail out if RH has been resumed. Use | 290 | * mark HW unaccessible. The PM and USB cores make sure that |
291 | * the spinlock to properly synchronize with possible pending | 291 | * the root hub is either suspended or stopped. |
292 | * RH suspend or resume activity. | ||
293 | * | ||
294 | * This is still racy as hcd->state is manipulated outside of | ||
295 | * any locks =P But that will be a different fix. | ||
296 | */ | 292 | */ |
297 | spin_lock_irqsave (&ehci->lock, flags); | 293 | spin_lock_irqsave (&ehci->lock, flags); |
298 | if (hcd->state != HC_STATE_SUSPENDED) { | 294 | ehci_prepare_ports_for_controller_suspend(ehci); |
299 | rc = -EINVAL; | ||
300 | goto bail; | ||
301 | } | ||
302 | ehci_writel(ehci, 0, &ehci->regs->intr_enable); | 295 | ehci_writel(ehci, 0, &ehci->regs->intr_enable); |
303 | (void)ehci_readl(ehci, &ehci->regs->intr_enable); | 296 | (void)ehci_readl(ehci, &ehci->regs->intr_enable); |
304 | 297 | ||
305 | clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); | 298 | clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); |
306 | bail: | ||
307 | spin_unlock_irqrestore (&ehci->lock, flags); | 299 | spin_unlock_irqrestore (&ehci->lock, flags); |
308 | 300 | ||
309 | // could save FLADJ in case of Vaux power loss | 301 | // could save FLADJ in case of Vaux power loss |
@@ -333,6 +325,7 @@ static int ehci_pci_resume(struct usb_hcd *hcd, bool hibernated) | |||
333 | !hibernated) { | 325 | !hibernated) { |
334 | int mask = INTR_MASK; | 326 | int mask = INTR_MASK; |
335 | 327 | ||
328 | ehci_prepare_ports_for_controller_resume(ehci); | ||
336 | if (!hcd->self.root_hub->do_remote_wakeup) | 329 | if (!hcd->self.root_hub->do_remote_wakeup) |
337 | mask &= ~STS_PCD; | 330 | mask &= ~STS_PCD; |
338 | ehci_writel(ehci, mask, &ehci->regs->intr_enable); | 331 | ehci_writel(ehci, mask, &ehci->regs->intr_enable); |
diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index 4ebe9ad209e4..650a687f2854 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h | |||
@@ -536,6 +536,16 @@ struct ehci_fstn { | |||
536 | 536 | ||
537 | /*-------------------------------------------------------------------------*/ | 537 | /*-------------------------------------------------------------------------*/ |
538 | 538 | ||
539 | /* Prepare the PORTSC wakeup flags during controller suspend/resume */ | ||
540 | |||
541 | #define ehci_prepare_ports_for_controller_suspend(ehci) \ | ||
542 | ehci_adjust_port_wakeup_flags(ehci, true); | ||
543 | |||
544 | #define ehci_prepare_ports_for_controller_resume(ehci) \ | ||
545 | ehci_adjust_port_wakeup_flags(ehci, false); | ||
546 | |||
547 | /*-------------------------------------------------------------------------*/ | ||
548 | |||
539 | #ifdef CONFIG_USB_EHCI_ROOT_HUB_TT | 549 | #ifdef CONFIG_USB_EHCI_ROOT_HUB_TT |
540 | 550 | ||
541 | /* | 551 | /* |