diff options
author | Alan Stern <stern@rowland.harvard.edu> | 2009-04-27 13:33:24 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2009-06-16 00:44:44 -0400 |
commit | abb306416a7ec2386678de0da6b632a6cb068af0 (patch) | |
tree | c61165e795cf3fc20a35d30034e196074e7e022b | |
parent | 00240c3839d843ccf07abd52806f421f7b87bbdc (diff) |
USB: move PCI host controllers to new PM framework
This patch (as1236) converts the USB PCI power management routines
over to the new PM framework.
Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
Acked-by: Rafael J. Wysocki <rjw@sisk.pl>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
-rw-r--r-- | drivers/usb/core/hcd-pci.c | 242 | ||||
-rw-r--r-- | drivers/usb/core/hcd.h | 9 | ||||
-rw-r--r-- | drivers/usb/host/ehci-pci.c | 9 | ||||
-rw-r--r-- | drivers/usb/host/ohci-pci.c | 11 | ||||
-rw-r--r-- | drivers/usb/host/uhci-hcd.c | 9 |
5 files changed, 141 insertions, 139 deletions
diff --git a/drivers/usb/core/hcd-pci.c b/drivers/usb/core/hcd-pci.c index a4301dc02d27..5db4d40db832 100644 --- a/drivers/usb/core/hcd-pci.c +++ b/drivers/usb/core/hcd-pci.c | |||
@@ -185,194 +185,198 @@ void usb_hcd_pci_remove(struct pci_dev *dev) | |||
185 | } | 185 | } |
186 | EXPORT_SYMBOL_GPL(usb_hcd_pci_remove); | 186 | EXPORT_SYMBOL_GPL(usb_hcd_pci_remove); |
187 | 187 | ||
188 | |||
189 | #ifdef CONFIG_PM | ||
190 | |||
191 | /** | 188 | /** |
192 | * usb_hcd_pci_suspend - power management suspend of a PCI-based HCD | 189 | * usb_hcd_pci_shutdown - shutdown host controller |
193 | * @dev: USB Host Controller being suspended | 190 | * @dev: USB Host Controller being shutdown |
194 | * @message: Power Management message describing this state transition | ||
195 | * | ||
196 | * Store this function in the HCD's struct pci_driver as .suspend. | ||
197 | */ | 191 | */ |
198 | int usb_hcd_pci_suspend(struct pci_dev *dev, pm_message_t message) | 192 | void usb_hcd_pci_shutdown(struct pci_dev *dev) |
193 | { | ||
194 | struct usb_hcd *hcd; | ||
195 | |||
196 | hcd = pci_get_drvdata(dev); | ||
197 | if (!hcd) | ||
198 | return; | ||
199 | |||
200 | if (hcd->driver->shutdown) | ||
201 | hcd->driver->shutdown(hcd); | ||
202 | } | ||
203 | EXPORT_SYMBOL_GPL(usb_hcd_pci_shutdown); | ||
204 | |||
205 | #ifdef CONFIG_PM_SLEEP | ||
206 | |||
207 | static int check_root_hub_suspended(struct device *dev) | ||
208 | { | ||
209 | struct pci_dev *pci_dev = to_pci_dev(dev); | ||
210 | struct usb_hcd *hcd = pci_get_drvdata(pci_dev); | ||
211 | |||
212 | if (!(hcd->state == HC_STATE_SUSPENDED || | ||
213 | hcd->state == HC_STATE_HALT)) { | ||
214 | dev_warn(dev, "Root hub is not suspended\n"); | ||
215 | return -EBUSY; | ||
216 | } | ||
217 | return 0; | ||
218 | } | ||
219 | |||
220 | static int hcd_pci_suspend(struct device *dev) | ||
199 | { | 221 | { |
200 | struct usb_hcd *hcd = pci_get_drvdata(dev); | 222 | struct pci_dev *pci_dev = to_pci_dev(dev); |
201 | int retval = 0; | 223 | struct usb_hcd *hcd = pci_get_drvdata(pci_dev); |
202 | int wake, w; | 224 | int retval; |
203 | int has_pci_pm; | ||
204 | 225 | ||
205 | /* Root hub suspend should have stopped all downstream traffic, | 226 | /* Root hub suspend should have stopped all downstream traffic, |
206 | * and all bus master traffic. And done so for both the interface | 227 | * and all bus master traffic. And done so for both the interface |
207 | * and the stub usb_device (which we check here). But maybe it | 228 | * and the stub usb_device (which we check here). But maybe it |
208 | * didn't; writing sysfs power/state files ignores such rules... | 229 | * didn't; writing sysfs power/state files ignores such rules... |
209 | * | ||
210 | * We must ignore the FREEZE vs SUSPEND distinction here, because | ||
211 | * otherwise the swsusp will save (and restore) garbage state. | ||
212 | */ | 230 | */ |
213 | if (!(hcd->state == HC_STATE_SUSPENDED || | 231 | retval = check_root_hub_suspended(dev); |
214 | hcd->state == HC_STATE_HALT)) { | 232 | if (retval) |
215 | dev_warn(&dev->dev, "Root hub is not suspended\n"); | 233 | return retval; |
216 | retval = -EBUSY; | ||
217 | goto done; | ||
218 | } | ||
219 | 234 | ||
220 | /* We might already be suspended (runtime PM -- not yet written) */ | 235 | /* We might already be suspended (runtime PM -- not yet written) */ |
221 | if (dev->current_state != PCI_D0) | 236 | if (pci_dev->current_state != PCI_D0) |
222 | goto done; | 237 | return retval; |
223 | 238 | ||
224 | if (hcd->driver->pci_suspend) { | 239 | if (hcd->driver->pci_suspend) { |
225 | retval = hcd->driver->pci_suspend(hcd, message); | 240 | retval = hcd->driver->pci_suspend(hcd, PMSG_SUSPEND); |
226 | suspend_report_result(hcd->driver->pci_suspend, retval); | 241 | suspend_report_result(hcd->driver->pci_suspend, retval); |
227 | if (retval) | 242 | if (retval) |
228 | goto done; | 243 | return retval; |
229 | } | 244 | } |
230 | 245 | ||
231 | synchronize_irq(dev->irq); | 246 | synchronize_irq(pci_dev->irq); |
232 | 247 | ||
233 | /* Downstream ports from this root hub should already be quiesced, so | 248 | /* Downstream ports from this root hub should already be quiesced, so |
234 | * there will be no DMA activity. Now we can shut down the upstream | 249 | * there will be no DMA activity. Now we can shut down the upstream |
235 | * link (except maybe for PME# resume signaling) and enter some PCI | 250 | * link (except maybe for PME# resume signaling). We'll enter a |
236 | * low power state, if the hardware allows. | 251 | * low power state during suspend_noirq, if the hardware allows. |
237 | */ | 252 | */ |
238 | pci_disable_device(dev); | 253 | pci_disable_device(pci_dev); |
254 | return retval; | ||
255 | } | ||
256 | |||
257 | static int hcd_pci_suspend_noirq(struct device *dev) | ||
258 | { | ||
259 | struct pci_dev *pci_dev = to_pci_dev(dev); | ||
260 | struct usb_hcd *hcd = pci_get_drvdata(pci_dev); | ||
261 | int retval; | ||
262 | |||
263 | retval = check_root_hub_suspended(dev); | ||
264 | if (retval) | ||
265 | return retval; | ||
239 | 266 | ||
240 | pci_save_state(dev); | 267 | pci_save_state(pci_dev); |
241 | 268 | ||
242 | /* Don't fail on error to enable wakeup. We rely on pci code | 269 | /* If the root hub is HALTed rather than SUSPENDed, |
243 | * to reject requests the hardware can't implement, rather | 270 | * disallow remote wakeup. |
244 | * than coding the same thing. | ||
245 | */ | 271 | */ |
246 | wake = (hcd->state == HC_STATE_SUSPENDED && | 272 | if (hcd->state == HC_STATE_HALT) |
247 | device_may_wakeup(&dev->dev)); | 273 | device_set_wakeup_enable(dev, 0); |
248 | w = pci_wake_from_d3(dev, wake); | 274 | dev_dbg(dev, "wakeup: %d\n", device_may_wakeup(dev)); |
249 | if (w < 0) | ||
250 | wake = w; | ||
251 | dev_dbg(&dev->dev, "wakeup: %d\n", wake); | ||
252 | |||
253 | /* Don't change state if we don't need to */ | ||
254 | if (message.event == PM_EVENT_FREEZE || | ||
255 | message.event == PM_EVENT_PRETHAW) { | ||
256 | dev_dbg(&dev->dev, "--> no state change\n"); | ||
257 | goto done; | ||
258 | } | ||
259 | 275 | ||
260 | has_pci_pm = pci_find_capability(dev, PCI_CAP_ID_PM); | 276 | /* Possibly enable remote wakeup, |
261 | if (!has_pci_pm) { | 277 | * choose the appropriate low-power state, and go to that state. |
262 | dev_dbg(&dev->dev, "--> PCI D0 legacy\n"); | 278 | */ |
279 | retval = pci_prepare_to_sleep(pci_dev); | ||
280 | if (retval == -EIO) { /* Low-power not supported */ | ||
281 | dev_dbg(dev, "--> PCI D0 legacy\n"); | ||
282 | retval = 0; | ||
283 | } else if (retval == 0) { | ||
284 | dev_dbg(dev, "--> PCI %s\n", | ||
285 | pci_power_name(pci_dev->current_state)); | ||
263 | } else { | 286 | } else { |
264 | 287 | suspend_report_result(pci_prepare_to_sleep, retval); | |
265 | /* NOTE: dev->current_state becomes nonzero only here, and | 288 | return retval; |
266 | * only for devices that support PCI PM. Also, exiting | ||
267 | * PCI_D3 (but not PCI_D1 or PCI_D2) is allowed to reset | ||
268 | * some device state (e.g. as part of clock reinit). | ||
269 | */ | ||
270 | retval = pci_set_power_state(dev, PCI_D3hot); | ||
271 | suspend_report_result(pci_set_power_state, retval); | ||
272 | if (retval == 0) { | ||
273 | dev_dbg(&dev->dev, "--> PCI D3\n"); | ||
274 | } else { | ||
275 | dev_dbg(&dev->dev, "PCI D3 suspend fail, %d\n", | ||
276 | retval); | ||
277 | pci_restore_state(dev); | ||
278 | } | ||
279 | } | 289 | } |
280 | 290 | ||
281 | #ifdef CONFIG_PPC_PMAC | 291 | #ifdef CONFIG_PPC_PMAC |
282 | if (retval == 0) { | 292 | /* Disable ASIC clocks for USB */ |
283 | /* Disable ASIC clocks for USB */ | 293 | if (machine_is(powermac)) { |
284 | if (machine_is(powermac)) { | 294 | struct device_node *of_node; |
285 | struct device_node *of_node; | 295 | |
286 | 296 | of_node = pci_device_to_OF_node(pci_dev); | |
287 | of_node = pci_device_to_OF_node(dev); | 297 | if (of_node) |
288 | if (of_node) | 298 | pmac_call_feature(PMAC_FTR_USB_ENABLE, of_node, 0, 0); |
289 | pmac_call_feature(PMAC_FTR_USB_ENABLE, | ||
290 | of_node, 0, 0); | ||
291 | } | ||
292 | } | 299 | } |
293 | #endif | 300 | #endif |
294 | |||
295 | done: | ||
296 | return retval; | 301 | return retval; |
297 | } | 302 | } |
298 | EXPORT_SYMBOL_GPL(usb_hcd_pci_suspend); | ||
299 | 303 | ||
300 | /** | 304 | static int hcd_pci_resume_noirq(struct device *dev) |
301 | * usb_hcd_pci_resume - power management resume of a PCI-based HCD | ||
302 | * @dev: USB Host Controller being resumed | ||
303 | * | ||
304 | * Store this function in the HCD's struct pci_driver as .resume. | ||
305 | */ | ||
306 | int usb_hcd_pci_resume(struct pci_dev *dev) | ||
307 | { | 305 | { |
308 | struct usb_hcd *hcd; | 306 | struct pci_dev *pci_dev = to_pci_dev(dev); |
309 | int retval; | ||
310 | 307 | ||
311 | #ifdef CONFIG_PPC_PMAC | 308 | #ifdef CONFIG_PPC_PMAC |
312 | /* Reenable ASIC clocks for USB */ | 309 | /* Reenable ASIC clocks for USB */ |
313 | if (machine_is(powermac)) { | 310 | if (machine_is(powermac)) { |
314 | struct device_node *of_node; | 311 | struct device_node *of_node; |
315 | 312 | ||
316 | of_node = pci_device_to_OF_node(dev); | 313 | of_node = pci_device_to_OF_node(pci_dev); |
317 | if (of_node) | 314 | if (of_node) |
318 | pmac_call_feature(PMAC_FTR_USB_ENABLE, | 315 | pmac_call_feature(PMAC_FTR_USB_ENABLE, |
319 | of_node, 0, 1); | 316 | of_node, 0, 1); |
320 | } | 317 | } |
321 | #endif | 318 | #endif |
322 | 319 | ||
323 | pci_restore_state(dev); | 320 | /* Go back to D0 and disable remote wakeup */ |
321 | pci_back_from_sleep(pci_dev); | ||
322 | return 0; | ||
323 | } | ||
324 | |||
325 | static int resume_common(struct device *dev, bool hibernated) | ||
326 | { | ||
327 | struct pci_dev *pci_dev = to_pci_dev(dev); | ||
328 | struct usb_hcd *hcd = pci_get_drvdata(pci_dev); | ||
329 | int retval; | ||
324 | 330 | ||
325 | hcd = pci_get_drvdata(dev); | ||
326 | if (hcd->state != HC_STATE_SUSPENDED) { | 331 | if (hcd->state != HC_STATE_SUSPENDED) { |
327 | dev_dbg(hcd->self.controller, | 332 | dev_dbg(dev, "can't resume, not suspended!\n"); |
328 | "can't resume, not suspended!\n"); | ||
329 | return 0; | 333 | return 0; |
330 | } | 334 | } |
331 | 335 | ||
332 | pci_enable_wake(dev, PCI_D0, false); | 336 | retval = pci_enable_device(pci_dev); |
333 | |||
334 | retval = pci_enable_device(dev); | ||
335 | if (retval < 0) { | 337 | if (retval < 0) { |
336 | dev_err(&dev->dev, "can't re-enable after resume, %d!\n", | 338 | dev_err(dev, "can't re-enable after resume, %d!\n", retval); |
337 | retval); | ||
338 | return retval; | 339 | return retval; |
339 | } | 340 | } |
340 | 341 | ||
341 | pci_set_master(dev); | 342 | pci_set_master(pci_dev); |
342 | |||
343 | /* yes, ignore this result too... */ | ||
344 | (void) pci_wake_from_d3(dev, 0); | ||
345 | 343 | ||
346 | clear_bit(HCD_FLAG_SAW_IRQ, &hcd->flags); | 344 | clear_bit(HCD_FLAG_SAW_IRQ, &hcd->flags); |
347 | 345 | ||
348 | if (hcd->driver->pci_resume) { | 346 | if (hcd->driver->pci_resume) { |
349 | retval = hcd->driver->pci_resume(hcd); | 347 | retval = hcd->driver->pci_resume(hcd); |
350 | if (retval) { | 348 | if (retval) { |
351 | dev_err(hcd->self.controller, | 349 | dev_err(dev, "PCI post-resume error %d!\n", retval); |
352 | "PCI post-resume error %d!\n", retval); | ||
353 | usb_hc_died(hcd); | 350 | usb_hc_died(hcd); |
354 | } | 351 | } |
355 | } | 352 | } |
356 | return retval; | 353 | return retval; |
357 | } | 354 | } |
358 | EXPORT_SYMBOL_GPL(usb_hcd_pci_resume); | ||
359 | 355 | ||
360 | #endif /* CONFIG_PM */ | 356 | static int hcd_pci_resume(struct device *dev) |
361 | |||
362 | /** | ||
363 | * usb_hcd_pci_shutdown - shutdown host controller | ||
364 | * @dev: USB Host Controller being shutdown | ||
365 | */ | ||
366 | void usb_hcd_pci_shutdown(struct pci_dev *dev) | ||
367 | { | 357 | { |
368 | struct usb_hcd *hcd; | 358 | return resume_common(dev, false); |
369 | 359 | } | |
370 | hcd = pci_get_drvdata(dev); | ||
371 | if (!hcd) | ||
372 | return; | ||
373 | 360 | ||
374 | if (hcd->driver->shutdown) | 361 | static int hcd_pci_restore(struct device *dev) |
375 | hcd->driver->shutdown(hcd); | 362 | { |
363 | return resume_common(dev, true); | ||
376 | } | 364 | } |
377 | EXPORT_SYMBOL_GPL(usb_hcd_pci_shutdown); | ||
378 | 365 | ||
366 | struct dev_pm_ops usb_hcd_pci_pm_ops = { | ||
367 | .suspend = hcd_pci_suspend, | ||
368 | .suspend_noirq = hcd_pci_suspend_noirq, | ||
369 | .resume_noirq = hcd_pci_resume_noirq, | ||
370 | .resume = hcd_pci_resume, | ||
371 | .freeze = check_root_hub_suspended, | ||
372 | .freeze_noirq = check_root_hub_suspended, | ||
373 | .thaw_noirq = NULL, | ||
374 | .thaw = NULL, | ||
375 | .poweroff = hcd_pci_suspend, | ||
376 | .poweroff_noirq = hcd_pci_suspend_noirq, | ||
377 | .restore_noirq = hcd_pci_resume_noirq, | ||
378 | .restore = hcd_pci_restore, | ||
379 | }; | ||
380 | EXPORT_SYMBOL_GPL(usb_hcd_pci_pm_ops); | ||
381 | |||
382 | #endif /* CONFIG_PM_SLEEP */ | ||
diff --git a/drivers/usb/core/hcd.h b/drivers/usb/core/hcd.h index e7d4479de41c..7f068d6e6940 100644 --- a/drivers/usb/core/hcd.h +++ b/drivers/usb/core/hcd.h | |||
@@ -261,14 +261,11 @@ struct pci_device_id; | |||
261 | extern int usb_hcd_pci_probe(struct pci_dev *dev, | 261 | extern int usb_hcd_pci_probe(struct pci_dev *dev, |
262 | const struct pci_device_id *id); | 262 | const struct pci_device_id *id); |
263 | extern void usb_hcd_pci_remove(struct pci_dev *dev); | 263 | extern void usb_hcd_pci_remove(struct pci_dev *dev); |
264 | |||
265 | #ifdef CONFIG_PM | ||
266 | extern int usb_hcd_pci_suspend(struct pci_dev *dev, pm_message_t msg); | ||
267 | extern int usb_hcd_pci_resume(struct pci_dev *dev); | ||
268 | #endif /* CONFIG_PM */ | ||
269 | |||
270 | extern void usb_hcd_pci_shutdown(struct pci_dev *dev); | 264 | extern void usb_hcd_pci_shutdown(struct pci_dev *dev); |
271 | 265 | ||
266 | #ifdef CONFIG_PM_SLEEP | ||
267 | extern struct dev_pm_ops usb_hcd_pci_pm_ops; | ||
268 | #endif | ||
272 | #endif /* CONFIG_PCI */ | 269 | #endif /* CONFIG_PCI */ |
273 | 270 | ||
274 | /* pci-ish (pdev null is ok) buffer alloc/mapping support */ | 271 | /* pci-ish (pdev null is ok) buffer alloc/mapping support */ |
diff --git a/drivers/usb/host/ehci-pci.c b/drivers/usb/host/ehci-pci.c index 5aa8bce90e1f..8172383e8908 100644 --- a/drivers/usb/host/ehci-pci.c +++ b/drivers/usb/host/ehci-pci.c | |||
@@ -429,10 +429,11 @@ static struct pci_driver ehci_pci_driver = { | |||
429 | 429 | ||
430 | .probe = usb_hcd_pci_probe, | 430 | .probe = usb_hcd_pci_probe, |
431 | .remove = usb_hcd_pci_remove, | 431 | .remove = usb_hcd_pci_remove, |
432 | .shutdown = usb_hcd_pci_shutdown, | ||
432 | 433 | ||
433 | #ifdef CONFIG_PM | 434 | #ifdef CONFIG_PM_SLEEP |
434 | .suspend = usb_hcd_pci_suspend, | 435 | .driver = { |
435 | .resume = usb_hcd_pci_resume, | 436 | .pm = &usb_hcd_pci_pm_ops |
437 | }, | ||
436 | #endif | 438 | #endif |
437 | .shutdown = usb_hcd_pci_shutdown, | ||
438 | }; | 439 | }; |
diff --git a/drivers/usb/host/ohci-pci.c b/drivers/usb/host/ohci-pci.c index f9961b4c0da3..ee0a68ca5fda 100644 --- a/drivers/usb/host/ohci-pci.c +++ b/drivers/usb/host/ohci-pci.c | |||
@@ -484,12 +484,11 @@ static struct pci_driver ohci_pci_driver = { | |||
484 | 484 | ||
485 | .probe = usb_hcd_pci_probe, | 485 | .probe = usb_hcd_pci_probe, |
486 | .remove = usb_hcd_pci_remove, | 486 | .remove = usb_hcd_pci_remove, |
487 | .shutdown = usb_hcd_pci_shutdown, | ||
487 | 488 | ||
488 | #ifdef CONFIG_PM | 489 | #ifdef CONFIG_PM_SLEEP |
489 | .suspend = usb_hcd_pci_suspend, | 490 | .driver = { |
490 | .resume = usb_hcd_pci_resume, | 491 | .pm = &usb_hcd_pci_pm_ops |
492 | }, | ||
491 | #endif | 493 | #endif |
492 | |||
493 | .shutdown = usb_hcd_pci_shutdown, | ||
494 | }; | 494 | }; |
495 | |||
diff --git a/drivers/usb/host/uhci-hcd.c b/drivers/usb/host/uhci-hcd.c index f2fd709fcce7..c0133211e3ec 100644 --- a/drivers/usb/host/uhci-hcd.c +++ b/drivers/usb/host/uhci-hcd.c | |||
@@ -940,10 +940,11 @@ static struct pci_driver uhci_pci_driver = { | |||
940 | .remove = usb_hcd_pci_remove, | 940 | .remove = usb_hcd_pci_remove, |
941 | .shutdown = uhci_shutdown, | 941 | .shutdown = uhci_shutdown, |
942 | 942 | ||
943 | #ifdef CONFIG_PM | 943 | #ifdef CONFIG_PM_SLEEP |
944 | .suspend = usb_hcd_pci_suspend, | 944 | .driver = { |
945 | .resume = usb_hcd_pci_resume, | 945 | .pm = &usb_hcd_pci_pm_ops |
946 | #endif /* PM */ | 946 | }, |
947 | #endif | ||
947 | }; | 948 | }; |
948 | 949 | ||
949 | static int __init uhci_hcd_init(void) | 950 | static int __init uhci_hcd_init(void) |