aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/usb/core/hcd-pci.c242
-rw-r--r--drivers/usb/core/hcd.h9
-rw-r--r--drivers/usb/host/ehci-pci.c9
-rw-r--r--drivers/usb/host/ohci-pci.c11
-rw-r--r--drivers/usb/host/uhci-hcd.c9
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}
186EXPORT_SYMBOL_GPL(usb_hcd_pci_remove); 186EXPORT_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 */
198int usb_hcd_pci_suspend(struct pci_dev *dev, pm_message_t message) 192void 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}
203EXPORT_SYMBOL_GPL(usb_hcd_pci_shutdown);
204
205#ifdef CONFIG_PM_SLEEP
206
207static 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
220static 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
257static 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}
298EXPORT_SYMBOL_GPL(usb_hcd_pci_suspend);
299 303
300/** 304static 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 */
306int 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
325static 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}
358EXPORT_SYMBOL_GPL(usb_hcd_pci_resume);
359 355
360#endif /* CONFIG_PM */ 356static 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 */
366void 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) 361static int hcd_pci_restore(struct device *dev)
375 hcd->driver->shutdown(hcd); 362{
363 return resume_common(dev, true);
376} 364}
377EXPORT_SYMBOL_GPL(usb_hcd_pci_shutdown);
378 365
366struct 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};
380EXPORT_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;
261extern int usb_hcd_pci_probe(struct pci_dev *dev, 261extern int usb_hcd_pci_probe(struct pci_dev *dev,
262 const struct pci_device_id *id); 262 const struct pci_device_id *id);
263extern void usb_hcd_pci_remove(struct pci_dev *dev); 263extern void usb_hcd_pci_remove(struct pci_dev *dev);
264
265#ifdef CONFIG_PM
266extern int usb_hcd_pci_suspend(struct pci_dev *dev, pm_message_t msg);
267extern int usb_hcd_pci_resume(struct pci_dev *dev);
268#endif /* CONFIG_PM */
269
270extern void usb_hcd_pci_shutdown(struct pci_dev *dev); 264extern void usb_hcd_pci_shutdown(struct pci_dev *dev);
271 265
266#ifdef CONFIG_PM_SLEEP
267extern 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
949static int __init uhci_hcd_init(void) 950static int __init uhci_hcd_init(void)