aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb')
-rw-r--r--drivers/usb/core/hcd-pci.c200
-rw-r--r--drivers/usb/core/hcd.h4
-rw-r--r--drivers/usb/host/ehci-pci.c2
-rw-r--r--drivers/usb/host/ohci-pci.c2
-rw-r--r--drivers/usb/host/uhci-hcd.c2
5 files changed, 115 insertions, 95 deletions
diff --git a/drivers/usb/core/hcd-pci.c b/drivers/usb/core/hcd-pci.c
index 5b87ae7f0a6a..99432785f438 100644
--- a/drivers/usb/core/hcd-pci.c
+++ b/drivers/usb/core/hcd-pci.c
@@ -191,17 +191,15 @@ EXPORT_SYMBOL_GPL(usb_hcd_pci_remove);
191/** 191/**
192 * usb_hcd_pci_suspend - power management suspend of a PCI-based HCD 192 * usb_hcd_pci_suspend - power management suspend of a PCI-based HCD
193 * @dev: USB Host Controller being suspended 193 * @dev: USB Host Controller being suspended
194 * @message: semantics in flux 194 * @message: Power Management message describing this state transition
195 * 195 *
196 * Store this function in the HCD's struct pci_driver as suspend(). 196 * Store this function in the HCD's struct pci_driver as .suspend.
197 */ 197 */
198int usb_hcd_pci_suspend(struct pci_dev *dev, pm_message_t message) 198int usb_hcd_pci_suspend(struct pci_dev *dev, pm_message_t message)
199{ 199{
200 struct usb_hcd *hcd; 200 struct usb_hcd *hcd = pci_get_drvdata(dev);
201 int retval = 0; 201 int retval = 0;
202 int has_pci_pm; 202 int wake, w;
203
204 hcd = pci_get_drvdata(dev);
205 203
206 /* Root hub suspend should have stopped all downstream traffic, 204 /* Root hub suspend should have stopped all downstream traffic,
207 * and all bus master traffic. And done so for both the interface 205 * and all bus master traffic. And done so for both the interface
@@ -212,8 +210,15 @@ int usb_hcd_pci_suspend(struct pci_dev *dev, pm_message_t message)
212 * otherwise the swsusp will save (and restore) garbage state. 210 * otherwise the swsusp will save (and restore) garbage state.
213 */ 211 */
214 if (!(hcd->state == HC_STATE_SUSPENDED || 212 if (!(hcd->state == HC_STATE_SUSPENDED ||
215 hcd->state == HC_STATE_HALT)) 213 hcd->state == HC_STATE_HALT)) {
216 return -EBUSY; 214 dev_warn(&dev->dev, "Root hub is not suspended\n");
215 retval = -EBUSY;
216 goto done;
217 }
218
219 /* We might already be suspended (runtime PM -- not yet written) */
220 if (dev->current_state != PCI_D0)
221 goto done;
217 222
218 if (hcd->driver->pci_suspend) { 223 if (hcd->driver->pci_suspend) {
219 retval = hcd->driver->pci_suspend(hcd, message); 224 retval = hcd->driver->pci_suspend(hcd, message);
@@ -221,49 +226,60 @@ int usb_hcd_pci_suspend(struct pci_dev *dev, pm_message_t message)
221 if (retval) 226 if (retval)
222 goto done; 227 goto done;
223 } 228 }
224 synchronize_irq(dev->irq);
225 229
226 /* FIXME until the generic PM interfaces change a lot more, this 230 synchronize_irq(dev->irq);
227 * can't use PCI D1 and D2 states. For example, the confusion
228 * between messages and states will need to vanish, and messages
229 * will need to provide a target system state again.
230 *
231 * It'll be important to learn characteristics of the target state,
232 * especially on embedded hardware where the HCD will often be in
233 * charge of an external VBUS power supply and one or more clocks.
234 * Some target system states will leave them active; others won't.
235 * (With PCI, that's often handled by platform BIOS code.)
236 */
237 231
238 /* even when the PCI layer rejects some of the PCI calls 232 /* Don't fail on error to enable wakeup. We rely on pci code
239 * below, HCs can try global suspend and reduce DMA traffic. 233 * to reject requests the hardware can't implement, rather
240 * PM-sensitive HCDs may already have done this. 234 * than coding the same thing.
241 */ 235 */
242 has_pci_pm = pci_find_capability(dev, PCI_CAP_ID_PM); 236 wake = (hcd->state == HC_STATE_SUSPENDED &&
237 device_may_wakeup(&dev->dev));
238 w = pci_wake_from_d3(dev, wake);
239 if (w < 0)
240 wake = w;
241 dev_dbg(&dev->dev, "wakeup: %d\n", wake);
243 242
244 /* Downstream ports from this root hub should already be quiesced, so 243 /* Downstream ports from this root hub should already be quiesced, so
245 * there will be no DMA activity. Now we can shut down the upstream 244 * there will be no DMA activity. Now we can shut down the upstream
246 * link (except maybe for PME# resume signaling) and enter some PCI 245 * link (except maybe for PME# resume signaling) and enter some PCI
247 * low power state, if the hardware allows. 246 * low power state, if the hardware allows.
248 */ 247 */
249 if (hcd->state == HC_STATE_SUSPENDED) { 248 pci_disable_device(dev);
249 done:
250 return retval;
251}
252EXPORT_SYMBOL_GPL(usb_hcd_pci_suspend);
250 253
251 /* no DMA or IRQs except when HC is active */ 254/**
252 if (dev->current_state == PCI_D0) { 255 * usb_hcd_pci_suspend_late - suspend a PCI-based HCD after IRQs are disabled
253 pci_save_state(dev); 256 * @dev: USB Host Controller being suspended
254 pci_disable_device(dev); 257 * @message: Power Management message describing this state transition
255 } 258 *
259 * Store this function in the HCD's struct pci_driver as .suspend_late.
260 */
261int usb_hcd_pci_suspend_late(struct pci_dev *dev, pm_message_t message)
262{
263 int retval = 0;
264 int has_pci_pm;
256 265
257 if (message.event == PM_EVENT_FREEZE || 266 /* We might already be suspended (runtime PM -- not yet written) */
258 message.event == PM_EVENT_PRETHAW) { 267 if (dev->current_state != PCI_D0)
259 dev_dbg(hcd->self.controller, "--> no state change\n"); 268 goto done;
260 goto done;
261 }
262 269
263 if (!has_pci_pm) { 270 pci_save_state(dev);
264 dev_dbg(hcd->self.controller, "--> PCI D0/legacy\n"); 271
265 goto done; 272 /* Don't change state if we don't need to */
266 } 273 if (message.event == PM_EVENT_FREEZE ||
274 message.event == PM_EVENT_PRETHAW) {
275 dev_dbg(&dev->dev, "--> no state change\n");
276 goto done;
277 }
278
279 has_pci_pm = pci_find_capability(dev, PCI_CAP_ID_PM);
280 if (!has_pci_pm) {
281 dev_dbg(&dev->dev, "--> PCI D0 legacy\n");
282 } else {
267 283
268 /* NOTE: dev->current_state becomes nonzero only here, and 284 /* NOTE: dev->current_state becomes nonzero only here, and
269 * only for devices that support PCI PM. Also, exiting 285 * only for devices that support PCI PM. Also, exiting
@@ -273,35 +289,16 @@ int usb_hcd_pci_suspend(struct pci_dev *dev, pm_message_t message)
273 retval = pci_set_power_state(dev, PCI_D3hot); 289 retval = pci_set_power_state(dev, PCI_D3hot);
274 suspend_report_result(pci_set_power_state, retval); 290 suspend_report_result(pci_set_power_state, retval);
275 if (retval == 0) { 291 if (retval == 0) {
276 int wake = device_can_wakeup(&hcd->self.root_hub->dev); 292 dev_dbg(&dev->dev, "--> PCI D3\n");
277
278 wake = wake && device_may_wakeup(hcd->self.controller);
279
280 dev_dbg(hcd->self.controller, "--> PCI D3%s\n",
281 wake ? "/wakeup" : "");
282
283 /* Ignore these return values. We rely on pci code to
284 * reject requests the hardware can't implement, rather
285 * than coding the same thing.
286 */
287 (void) pci_enable_wake(dev, PCI_D3hot, wake);
288 (void) pci_enable_wake(dev, PCI_D3cold, wake);
289 } else { 293 } else {
290 dev_dbg(&dev->dev, "PCI D3 suspend fail, %d\n", 294 dev_dbg(&dev->dev, "PCI D3 suspend fail, %d\n",
291 retval); 295 retval);
292 (void) usb_hcd_pci_resume(dev); 296 pci_restore_state(dev);
293 } 297 }
294
295 } else if (hcd->state != HC_STATE_HALT) {
296 dev_dbg(hcd->self.controller, "hcd state %d; not suspended\n",
297 hcd->state);
298 WARN_ON(1);
299 retval = -EINVAL;
300 } 298 }
301 299
302done:
303 if (retval == 0) {
304#ifdef CONFIG_PPC_PMAC 300#ifdef CONFIG_PPC_PMAC
301 if (retval == 0) {
305 /* Disable ASIC clocks for USB */ 302 /* Disable ASIC clocks for USB */
306 if (machine_is(powermac)) { 303 if (machine_is(powermac)) {
307 struct device_node *of_node; 304 struct device_node *of_node;
@@ -311,30 +308,24 @@ done:
311 pmac_call_feature(PMAC_FTR_USB_ENABLE, 308 pmac_call_feature(PMAC_FTR_USB_ENABLE,
312 of_node, 0, 0); 309 of_node, 0, 0);
313 } 310 }
314#endif
315 } 311 }
312#endif
316 313
314 done:
317 return retval; 315 return retval;
318} 316}
319EXPORT_SYMBOL_GPL(usb_hcd_pci_suspend); 317EXPORT_SYMBOL_GPL(usb_hcd_pci_suspend_late);
320 318
321/** 319/**
322 * usb_hcd_pci_resume - power management resume of a PCI-based HCD 320 * usb_hcd_pci_resume_early - resume a PCI-based HCD before IRQs are enabled
323 * @dev: USB Host Controller being resumed 321 * @dev: USB Host Controller being resumed
324 * 322 *
325 * Store this function in the HCD's struct pci_driver as resume(). 323 * Store this function in the HCD's struct pci_driver as .resume_early.
326 */ 324 */
327int usb_hcd_pci_resume(struct pci_dev *dev) 325int usb_hcd_pci_resume_early(struct pci_dev *dev)
328{ 326{
329 struct usb_hcd *hcd; 327 int retval = 0;
330 int retval; 328 pci_power_t state = dev->current_state;
331
332 hcd = pci_get_drvdata(dev);
333 if (hcd->state != HC_STATE_SUSPENDED) {
334 dev_dbg(hcd->self.controller,
335 "can't resume, not suspended!\n");
336 return 0;
337 }
338 329
339#ifdef CONFIG_PPC_PMAC 330#ifdef CONFIG_PPC_PMAC
340 /* Reenable ASIC clocks for USB */ 331 /* Reenable ASIC clocks for USB */
@@ -352,7 +343,7 @@ int usb_hcd_pci_resume(struct pci_dev *dev)
352 * calls "standby", "suspend to RAM", and so on). There are also 343 * calls "standby", "suspend to RAM", and so on). There are also
353 * dirty cases when swsusp fakes a suspend in "shutdown" mode. 344 * dirty cases when swsusp fakes a suspend in "shutdown" mode.
354 */ 345 */
355 if (dev->current_state != PCI_D0) { 346 if (state != PCI_D0) {
356#ifdef DEBUG 347#ifdef DEBUG
357 int pci_pm; 348 int pci_pm;
358 u16 pmcr; 349 u16 pmcr;
@@ -364,8 +355,7 @@ int usb_hcd_pci_resume(struct pci_dev *dev)
364 /* Clean case: power to USB and to HC registers was 355 /* Clean case: power to USB and to HC registers was
365 * maintained; remote wakeup is easy. 356 * maintained; remote wakeup is easy.
366 */ 357 */
367 dev_dbg(hcd->self.controller, "resume from PCI D%d\n", 358 dev_dbg(&dev->dev, "resume from PCI D%d\n", pmcr);
368 pmcr);
369 } else { 359 } else {
370 /* Clean: HC lost Vcc power, D0 uninitialized 360 /* Clean: HC lost Vcc power, D0 uninitialized
371 * + Vaux may have preserved port and transceiver 361 * + Vaux may have preserved port and transceiver
@@ -376,32 +366,55 @@ int usb_hcd_pci_resume(struct pci_dev *dev)
376 * + after BIOS init 366 * + after BIOS init
377 * + after Linux init (HCD statically linked) 367 * + after Linux init (HCD statically linked)
378 */ 368 */
379 dev_dbg(hcd->self.controller, 369 dev_dbg(&dev->dev, "resume from previous PCI D%d\n",
380 "PCI D0, from previous PCI D%d\n", 370 state);
381 dev->current_state);
382 } 371 }
383#endif 372#endif
384 /* yes, ignore these results too... */ 373
385 (void) pci_enable_wake(dev, dev->current_state, 0); 374 retval = pci_set_power_state(dev, PCI_D0);
386 (void) pci_enable_wake(dev, PCI_D3cold, 0);
387 } else { 375 } else {
388 /* Same basic cases: clean (powered/not), dirty */ 376 /* Same basic cases: clean (powered/not), dirty */
389 dev_dbg(hcd->self.controller, "PCI legacy resume\n"); 377 dev_dbg(&dev->dev, "PCI legacy resume\n");
378 }
379
380 if (retval < 0)
381 dev_err(&dev->dev, "can't resume: %d\n", retval);
382 else
383 pci_restore_state(dev);
384
385 return retval;
386}
387EXPORT_SYMBOL_GPL(usb_hcd_pci_resume_early);
388
389/**
390 * usb_hcd_pci_resume - power management resume of a PCI-based HCD
391 * @dev: USB Host Controller being resumed
392 *
393 * Store this function in the HCD's struct pci_driver as .resume.
394 */
395int usb_hcd_pci_resume(struct pci_dev *dev)
396{
397 struct usb_hcd *hcd;
398 int retval;
399
400 hcd = pci_get_drvdata(dev);
401 if (hcd->state != HC_STATE_SUSPENDED) {
402 dev_dbg(hcd->self.controller,
403 "can't resume, not suspended!\n");
404 return 0;
390 } 405 }
391 406
392 /* NOTE: the PCI API itself is asymmetric here. We don't need to
393 * pci_set_power_state(PCI_D0) since that's part of re-enabling;
394 * but that won't re-enable bus mastering. Yet pci_disable_device()
395 * explicitly disables bus mastering...
396 */
397 retval = pci_enable_device(dev); 407 retval = pci_enable_device(dev);
398 if (retval < 0) { 408 if (retval < 0) {
399 dev_err(hcd->self.controller, 409 dev_err(&dev->dev, "can't re-enable after resume, %d!\n",
400 "can't re-enable after resume, %d!\n", retval); 410 retval);
401 return retval; 411 return retval;
402 } 412 }
413
403 pci_set_master(dev); 414 pci_set_master(dev);
404 pci_restore_state(dev); 415
416 /* yes, ignore this result too... */
417 (void) pci_wake_from_d3(dev, 0);
405 418
406 clear_bit(HCD_FLAG_SAW_IRQ, &hcd->flags); 419 clear_bit(HCD_FLAG_SAW_IRQ, &hcd->flags);
407 420
@@ -413,7 +426,6 @@ int usb_hcd_pci_resume(struct pci_dev *dev)
413 usb_hc_died(hcd); 426 usb_hc_died(hcd);
414 } 427 }
415 } 428 }
416
417 return retval; 429 return retval;
418} 430}
419EXPORT_SYMBOL_GPL(usb_hcd_pci_resume); 431EXPORT_SYMBOL_GPL(usb_hcd_pci_resume);
diff --git a/drivers/usb/core/hcd.h b/drivers/usb/core/hcd.h
index aa5da82d9071..572d2cf46e8d 100644
--- a/drivers/usb/core/hcd.h
+++ b/drivers/usb/core/hcd.h
@@ -256,7 +256,9 @@ extern int usb_hcd_pci_probe(struct pci_dev *dev,
256extern void usb_hcd_pci_remove(struct pci_dev *dev); 256extern void usb_hcd_pci_remove(struct pci_dev *dev);
257 257
258#ifdef CONFIG_PM 258#ifdef CONFIG_PM
259extern int usb_hcd_pci_suspend(struct pci_dev *dev, pm_message_t state); 259extern int usb_hcd_pci_suspend(struct pci_dev *dev, pm_message_t msg);
260extern int usb_hcd_pci_suspend_late(struct pci_dev *dev, pm_message_t msg);
261extern int usb_hcd_pci_resume_early(struct pci_dev *dev);
260extern int usb_hcd_pci_resume(struct pci_dev *dev); 262extern int usb_hcd_pci_resume(struct pci_dev *dev);
261#endif /* CONFIG_PM */ 263#endif /* CONFIG_PM */
262 264
diff --git a/drivers/usb/host/ehci-pci.c b/drivers/usb/host/ehci-pci.c
index 36864f958444..6af47a0937b8 100644
--- a/drivers/usb/host/ehci-pci.c
+++ b/drivers/usb/host/ehci-pci.c
@@ -428,6 +428,8 @@ static struct pci_driver ehci_pci_driver = {
428 428
429#ifdef CONFIG_PM 429#ifdef CONFIG_PM
430 .suspend = usb_hcd_pci_suspend, 430 .suspend = usb_hcd_pci_suspend,
431 .suspend_late = usb_hcd_pci_suspend_late,
432 .resume_early = usb_hcd_pci_resume_early,
431 .resume = usb_hcd_pci_resume, 433 .resume = usb_hcd_pci_resume,
432#endif 434#endif
433 .shutdown = usb_hcd_pci_shutdown, 435 .shutdown = usb_hcd_pci_shutdown,
diff --git a/drivers/usb/host/ohci-pci.c b/drivers/usb/host/ohci-pci.c
index a9c2ae36c7ad..8380cc2e961a 100644
--- a/drivers/usb/host/ohci-pci.c
+++ b/drivers/usb/host/ohci-pci.c
@@ -487,6 +487,8 @@ static struct pci_driver ohci_pci_driver = {
487 487
488#ifdef CONFIG_PM 488#ifdef CONFIG_PM
489 .suspend = usb_hcd_pci_suspend, 489 .suspend = usb_hcd_pci_suspend,
490 .suspend_late = usb_hcd_pci_suspend_late,
491 .resume_early = usb_hcd_pci_resume_early,
490 .resume = usb_hcd_pci_resume, 492 .resume = usb_hcd_pci_resume,
491#endif 493#endif
492 494
diff --git a/drivers/usb/host/uhci-hcd.c b/drivers/usb/host/uhci-hcd.c
index cf5e4cf7ea42..4e221060f58c 100644
--- a/drivers/usb/host/uhci-hcd.c
+++ b/drivers/usb/host/uhci-hcd.c
@@ -942,6 +942,8 @@ static struct pci_driver uhci_pci_driver = {
942 942
943#ifdef CONFIG_PM 943#ifdef CONFIG_PM
944 .suspend = usb_hcd_pci_suspend, 944 .suspend = usb_hcd_pci_suspend,
945 .suspend_late = usb_hcd_pci_suspend_late,
946 .resume_early = usb_hcd_pci_resume_early,
945 .resume = usb_hcd_pci_resume, 947 .resume = usb_hcd_pci_resume,
946#endif /* PM */ 948#endif /* PM */
947}; 949};