diff options
author | David Brownell <david-b@pacbell.net> | 2005-09-23 01:38:16 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2005-10-28 19:47:40 -0400 |
commit | 5f827ea3c3820cd8e0a1a35e4d275c8b78ee94e1 (patch) | |
tree | 7bd8a0f12f83c219cab6da7e30e8d0e1d94df57d /drivers/usb | |
parent | f3f3253d644d36a2ea5464005a9c2cf28804d200 (diff) |
[PATCH] usbcore PCI glue updates for PM
This updates the PCI glue to address the new and simplified usbcore suspend
semantics, where CONFIG_USB_SUSPEND becomes irrelevant to HCDs because
hcd->hub_suspend() will always be called.
- Removes now-unneeded recursion support
- Go back to ignoring faults reported by the wakeup calls; we expect them
to fail sometimes, and that's just fine.
The PCI HCDs will need simple changes to catch up to this, like being able
to ignore the setting of CONFIG_USB_SUSPEND.
Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
drivers/usb/core/hcd-pci.c | 106 +++++++++++++++++++++------------------------
drivers/usb/core/hcd.h | 6 +-
2 files changed, 53 insertions(+), 59 deletions(-)
Diffstat (limited to 'drivers/usb')
-rw-r--r-- | drivers/usb/core/hcd-pci.c | 106 | ||||
-rw-r--r-- | drivers/usb/core/hcd.h | 6 |
2 files changed, 53 insertions, 59 deletions
diff --git a/drivers/usb/core/hcd-pci.c b/drivers/usb/core/hcd-pci.c index 6385d1a99b60..84d9e69329bb 100644 --- a/drivers/usb/core/hcd-pci.c +++ b/drivers/usb/core/hcd-pci.c | |||
@@ -30,6 +30,8 @@ | |||
30 | #include <asm/io.h> | 30 | #include <asm/io.h> |
31 | #include <asm/irq.h> | 31 | #include <asm/irq.h> |
32 | #include <linux/usb.h> | 32 | #include <linux/usb.h> |
33 | |||
34 | #include "usb.h" | ||
33 | #include "hcd.h" | 35 | #include "hcd.h" |
34 | 36 | ||
35 | 37 | ||
@@ -197,6 +199,26 @@ int usb_hcd_pci_suspend (struct pci_dev *dev, pm_message_t message) | |||
197 | 199 | ||
198 | hcd = pci_get_drvdata(dev); | 200 | hcd = pci_get_drvdata(dev); |
199 | 201 | ||
202 | /* Root hub suspend should have stopped all downstream traffic, | ||
203 | * and all bus master traffic. And done so for both the interface | ||
204 | * and the stub usb_device (which we check here). But maybe it | ||
205 | * didn't; writing sysfs power/state files ignores such rules... | ||
206 | * | ||
207 | * We must ignore the FREEZE vs SUSPEND distinction here, because | ||
208 | * otherwise the swsusp will save (and restore) garbage state. | ||
209 | */ | ||
210 | if (hcd->self.root_hub->dev.power.power_state.event == PM_EVENT_ON) | ||
211 | return -EBUSY; | ||
212 | |||
213 | if (hcd->driver->suspend) { | ||
214 | retval = hcd->driver->suspend(hcd, message); | ||
215 | if (retval) { | ||
216 | dev_dbg (&dev->dev, "PCI pre-suspend fail, %d\n", | ||
217 | retval); | ||
218 | goto done; | ||
219 | } | ||
220 | } | ||
221 | |||
200 | /* FIXME until the generic PM interfaces change a lot more, this | 222 | /* FIXME until the generic PM interfaces change a lot more, this |
201 | * can't use PCI D1 and D2 states. For example, the confusion | 223 | * can't use PCI D1 and D2 states. For example, the confusion |
202 | * between messages and states will need to vanish, and messages | 224 | * between messages and states will need to vanish, and messages |
@@ -215,31 +237,13 @@ int usb_hcd_pci_suspend (struct pci_dev *dev, pm_message_t message) | |||
215 | */ | 237 | */ |
216 | has_pci_pm = pci_find_capability(dev, PCI_CAP_ID_PM); | 238 | has_pci_pm = pci_find_capability(dev, PCI_CAP_ID_PM); |
217 | 239 | ||
218 | switch (hcd->state) { | 240 | /* Downstream ports from this root hub should already be quiesced, so |
219 | 241 | * there will be no DMA activity. Now we can shut down the upstream | |
220 | /* entry if root hub wasn't yet suspended ... from sysfs, | 242 | * link (except maybe for PME# resume signaling) and enter some PCI |
221 | * without autosuspend, or if USB_SUSPEND isn't configured. | 243 | * low power state, if the hardware allows. |
222 | */ | 244 | */ |
223 | case HC_STATE_RUNNING: | 245 | if (hcd->state == HC_STATE_SUSPENDED) { |
224 | hcd->state = HC_STATE_QUIESCING; | ||
225 | retval = hcd->driver->suspend (hcd, message); | ||
226 | if (retval) { | ||
227 | dev_dbg (hcd->self.controller, | ||
228 | "suspend fail, retval %d\n", | ||
229 | retval); | ||
230 | break; | ||
231 | } | ||
232 | hcd->state = HC_STATE_SUSPENDED; | ||
233 | /* FALLTHROUGH */ | ||
234 | 246 | ||
235 | /* entry with CONFIG_USB_SUSPEND, or hcds that autosuspend: the | ||
236 | * controller and/or root hub will already have been suspended, | ||
237 | * but it won't be ready for a PCI resume call. | ||
238 | * | ||
239 | * FIXME only CONFIG_USB_SUSPEND guarantees hub_suspend() will | ||
240 | * have been called, otherwise root hub timers still run ... | ||
241 | */ | ||
242 | case HC_STATE_SUSPENDED: | ||
243 | /* no DMA or IRQs except when HC is active */ | 247 | /* no DMA or IRQs except when HC is active */ |
244 | if (dev->current_state == PCI_D0) { | 248 | if (dev->current_state == PCI_D0) { |
245 | pci_save_state (dev); | 249 | pci_save_state (dev); |
@@ -248,7 +252,7 @@ int usb_hcd_pci_suspend (struct pci_dev *dev, pm_message_t message) | |||
248 | 252 | ||
249 | if (!has_pci_pm) { | 253 | if (!has_pci_pm) { |
250 | dev_dbg (hcd->self.controller, "--> PCI D0/legacy\n"); | 254 | dev_dbg (hcd->self.controller, "--> PCI D0/legacy\n"); |
251 | break; | 255 | goto done; |
252 | } | 256 | } |
253 | 257 | ||
254 | /* NOTE: dev->current_state becomes nonzero only here, and | 258 | /* NOTE: dev->current_state becomes nonzero only here, and |
@@ -259,28 +263,29 @@ int usb_hcd_pci_suspend (struct pci_dev *dev, pm_message_t message) | |||
259 | retval = pci_set_power_state (dev, PCI_D3hot); | 263 | retval = pci_set_power_state (dev, PCI_D3hot); |
260 | if (retval == 0) { | 264 | if (retval == 0) { |
261 | dev_dbg (hcd->self.controller, "--> PCI D3\n"); | 265 | dev_dbg (hcd->self.controller, "--> PCI D3\n"); |
262 | retval = pci_enable_wake (dev, PCI_D3hot, hcd->remote_wakeup); | 266 | |
263 | if (retval) | 267 | /* Ignore these return values. We rely on pci code to |
264 | break; | 268 | * reject requests the hardware can't implement, rather |
265 | retval = pci_enable_wake (dev, PCI_D3cold, hcd->remote_wakeup); | 269 | * than coding the same thing. |
266 | } else if (retval < 0) { | 270 | */ |
271 | (void) pci_enable_wake (dev, PCI_D3hot, hcd->remote_wakeup); | ||
272 | (void) pci_enable_wake (dev, PCI_D3cold, hcd->remote_wakeup); | ||
273 | } else { | ||
267 | dev_dbg (&dev->dev, "PCI D3 suspend fail, %d\n", | 274 | dev_dbg (&dev->dev, "PCI D3 suspend fail, %d\n", |
268 | retval); | 275 | retval); |
269 | (void) usb_hcd_pci_resume (dev); | 276 | (void) usb_hcd_pci_resume (dev); |
270 | break; | ||
271 | } | 277 | } |
272 | break; | 278 | |
273 | default: | 279 | } else { |
274 | dev_dbg (hcd->self.controller, "hcd state %d; not suspended\n", | 280 | dev_dbg (hcd->self.controller, "hcd state %d; not suspended\n", |
275 | hcd->state); | 281 | hcd->state); |
276 | WARN_ON(1); | 282 | WARN_ON(1); |
277 | retval = -EINVAL; | 283 | retval = -EINVAL; |
278 | break; | ||
279 | } | 284 | } |
280 | 285 | ||
281 | /* update power_state **ONLY** to make sysfs happier */ | 286 | done: |
282 | if (retval == 0) | 287 | if (retval == 0) |
283 | dev->dev.power.power_state = message; | 288 | dev->dev.power.power_state = PMSG_SUSPEND; |
284 | return retval; | 289 | return retval; |
285 | } | 290 | } |
286 | EXPORT_SYMBOL (usb_hcd_pci_suspend); | 291 | EXPORT_SYMBOL (usb_hcd_pci_suspend); |
@@ -336,20 +341,9 @@ int usb_hcd_pci_resume (struct pci_dev *dev) | |||
336 | dev->current_state); | 341 | dev->current_state); |
337 | } | 342 | } |
338 | #endif | 343 | #endif |
339 | retval = pci_enable_wake (dev, dev->current_state, 0); | 344 | /* yes, ignore these results too... */ |
340 | if (retval) { | 345 | (void) pci_enable_wake (dev, dev->current_state, 0); |
341 | dev_err(hcd->self.controller, | 346 | (void) pci_enable_wake (dev, PCI_D3cold, 0); |
342 | "can't enable_wake to %d, %d!\n", | ||
343 | dev->current_state, retval); | ||
344 | return retval; | ||
345 | } | ||
346 | retval = pci_enable_wake (dev, PCI_D3cold, 0); | ||
347 | if (retval) { | ||
348 | dev_err(hcd->self.controller, | ||
349 | "can't enable_wake to %d, %d!\n", | ||
350 | PCI_D3cold, retval); | ||
351 | return retval; | ||
352 | } | ||
353 | } else { | 347 | } else { |
354 | /* Same basic cases: clean (powered/not), dirty */ | 348 | /* Same basic cases: clean (powered/not), dirty */ |
355 | dev_dbg(hcd->self.controller, "PCI legacy resume\n"); | 349 | dev_dbg(hcd->self.controller, "PCI legacy resume\n"); |
@@ -371,17 +365,17 @@ int usb_hcd_pci_resume (struct pci_dev *dev) | |||
371 | 365 | ||
372 | dev->dev.power.power_state = PMSG_ON; | 366 | dev->dev.power.power_state = PMSG_ON; |
373 | 367 | ||
374 | hcd->state = HC_STATE_RESUMING; | ||
375 | hcd->saw_irq = 0; | 368 | hcd->saw_irq = 0; |
376 | 369 | ||
377 | retval = hcd->driver->resume (hcd); | 370 | if (hcd->driver->resume) { |
378 | if (!HC_IS_RUNNING (hcd->state)) { | 371 | retval = hcd->driver->resume(hcd); |
379 | dev_dbg (hcd->self.controller, | 372 | if (retval) { |
380 | "resume fail, retval %d\n", retval); | 373 | dev_err (hcd->self.controller, |
381 | usb_hc_died (hcd); | 374 | "PCI post-resume error %d!\n", retval); |
375 | usb_hc_died (hcd); | ||
376 | } | ||
382 | } | 377 | } |
383 | 378 | ||
384 | retval = pci_enable_device(dev); | ||
385 | return retval; | 379 | return retval; |
386 | } | 380 | } |
387 | EXPORT_SYMBOL (usb_hcd_pci_resume); | 381 | EXPORT_SYMBOL (usb_hcd_pci_resume); |
diff --git a/drivers/usb/core/hcd.h b/drivers/usb/core/hcd.h index eb21f13c5c74..74757fc1a99f 100644 --- a/drivers/usb/core/hcd.h +++ b/drivers/usb/core/hcd.h | |||
@@ -182,12 +182,12 @@ struct hc_driver { | |||
182 | int (*start) (struct usb_hcd *hcd); | 182 | int (*start) (struct usb_hcd *hcd); |
183 | 183 | ||
184 | /* NOTE: these suspend/resume calls relate to the HC as | 184 | /* NOTE: these suspend/resume calls relate to the HC as |
185 | * a whole, not just the root hub; they're for bus glue. | 185 | * a whole, not just the root hub; they're for PCI bus glue. |
186 | */ | 186 | */ |
187 | /* called after all devices were suspended */ | 187 | /* called after suspending the hub, before entering D3 etc */ |
188 | int (*suspend) (struct usb_hcd *hcd, pm_message_t message); | 188 | int (*suspend) (struct usb_hcd *hcd, pm_message_t message); |
189 | 189 | ||
190 | /* called before any devices get resumed */ | 190 | /* called after entering D0 (etc), before resuming the hub */ |
191 | int (*resume) (struct usb_hcd *hcd); | 191 | int (*resume) (struct usb_hcd *hcd); |
192 | 192 | ||
193 | /* cleanly make HCD stop writing memory and doing I/O */ | 193 | /* cleanly make HCD stop writing memory and doing I/O */ |