diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/usb/core/hcd-pci.c | 151 |
1 files changed, 91 insertions, 60 deletions
diff --git a/drivers/usb/core/hcd-pci.c b/drivers/usb/core/hcd-pci.c index b9a3dae07036..71b4a8d66318 100644 --- a/drivers/usb/core/hcd-pci.c +++ b/drivers/usb/core/hcd-pci.c | |||
@@ -33,7 +33,7 @@ | |||
33 | #include "hcd.h" | 33 | #include "hcd.h" |
34 | 34 | ||
35 | 35 | ||
36 | /* PCI-based HCs are normal, but custom bus glue should be ok */ | 36 | /* PCI-based HCs are common, but plenty of non-PCI HCs are used too */ |
37 | 37 | ||
38 | 38 | ||
39 | /*-------------------------------------------------------------------------*/ | 39 | /*-------------------------------------------------------------------------*/ |
@@ -67,8 +67,8 @@ int usb_hcd_pci_probe (struct pci_dev *dev, const struct pci_device_id *id) | |||
67 | 67 | ||
68 | if (pci_enable_device (dev) < 0) | 68 | if (pci_enable_device (dev) < 0) |
69 | return -ENODEV; | 69 | return -ENODEV; |
70 | dev->current_state = 0; | 70 | dev->current_state = PCI_D0; |
71 | dev->dev.power.power_state = 0; | 71 | dev->dev.power.power_state = PMSG_ON; |
72 | 72 | ||
73 | if (!dev->irq) { | 73 | if (!dev->irq) { |
74 | dev_err (&dev->dev, | 74 | dev_err (&dev->dev, |
@@ -186,26 +186,14 @@ EXPORT_SYMBOL (usb_hcd_pci_remove); | |||
186 | 186 | ||
187 | #ifdef CONFIG_PM | 187 | #ifdef CONFIG_PM |
188 | 188 | ||
189 | static char __attribute_used__ *pci_state(u32 state) | ||
190 | { | ||
191 | switch (state) { | ||
192 | case 0: return "D0"; | ||
193 | case 1: return "D1"; | ||
194 | case 2: return "D2"; | ||
195 | case 3: return "D3hot"; | ||
196 | case 4: return "D3cold"; | ||
197 | } | ||
198 | return NULL; | ||
199 | } | ||
200 | |||
201 | /** | 189 | /** |
202 | * usb_hcd_pci_suspend - power management suspend of a PCI-based HCD | 190 | * usb_hcd_pci_suspend - power management suspend of a PCI-based HCD |
203 | * @dev: USB Host Controller being suspended | 191 | * @dev: USB Host Controller being suspended |
204 | * @state: state that the controller is going into | 192 | * @message: semantics in flux |
205 | * | 193 | * |
206 | * Store this function in the HCD's struct pci_driver as suspend(). | 194 | * Store this function in the HCD's struct pci_driver as suspend(). |
207 | */ | 195 | */ |
208 | int usb_hcd_pci_suspend (struct pci_dev *dev, u32 state) | 196 | int usb_hcd_pci_suspend (struct pci_dev *dev, pm_message_t message) |
209 | { | 197 | { |
210 | struct usb_hcd *hcd; | 198 | struct usb_hcd *hcd; |
211 | int retval = 0; | 199 | int retval = 0; |
@@ -213,13 +201,23 @@ int usb_hcd_pci_suspend (struct pci_dev *dev, u32 state) | |||
213 | 201 | ||
214 | hcd = pci_get_drvdata(dev); | 202 | hcd = pci_get_drvdata(dev); |
215 | 203 | ||
204 | /* FIXME until the generic PM interfaces change a lot more, this | ||
205 | * can't use PCI D1 and D2 states. For example, the confusion | ||
206 | * between messages and states will need to vanish, and messages | ||
207 | * will need to provide a target system state again. | ||
208 | * | ||
209 | * It'll be important to learn characteristics of the target state, | ||
210 | * especially on embedded hardware where the HCD will often be in | ||
211 | * charge of an external VBUS power supply and one or more clocks. | ||
212 | * Some target system states will leave them active; others won't. | ||
213 | * (With PCI, that's often handled by platform BIOS code.) | ||
214 | */ | ||
215 | |||
216 | /* even when the PCI layer rejects some of the PCI calls | 216 | /* even when the PCI layer rejects some of the PCI calls |
217 | * below, HCs can try global suspend and reduce DMA traffic. | 217 | * below, HCs can try global suspend and reduce DMA traffic. |
218 | * PM-sensitive HCDs may already have done this. | 218 | * PM-sensitive HCDs may already have done this. |
219 | */ | 219 | */ |
220 | has_pci_pm = pci_find_capability(dev, PCI_CAP_ID_PM); | 220 | has_pci_pm = pci_find_capability(dev, PCI_CAP_ID_PM); |
221 | if (state > 4) | ||
222 | state = 4; | ||
223 | 221 | ||
224 | switch (hcd->state) { | 222 | switch (hcd->state) { |
225 | 223 | ||
@@ -228,7 +226,7 @@ int usb_hcd_pci_suspend (struct pci_dev *dev, u32 state) | |||
228 | */ | 226 | */ |
229 | case HC_STATE_RUNNING: | 227 | case HC_STATE_RUNNING: |
230 | hcd->state = HC_STATE_QUIESCING; | 228 | hcd->state = HC_STATE_QUIESCING; |
231 | retval = hcd->driver->suspend (hcd, state); | 229 | retval = hcd->driver->suspend (hcd, message); |
232 | if (retval) { | 230 | if (retval) { |
233 | dev_dbg (hcd->self.controller, | 231 | dev_dbg (hcd->self.controller, |
234 | "suspend fail, retval %d\n", | 232 | "suspend fail, retval %d\n", |
@@ -246,14 +244,11 @@ int usb_hcd_pci_suspend (struct pci_dev *dev, u32 state) | |||
246 | * have been called, otherwise root hub timers still run ... | 244 | * have been called, otherwise root hub timers still run ... |
247 | */ | 245 | */ |
248 | case HC_STATE_SUSPENDED: | 246 | case HC_STATE_SUSPENDED: |
249 | if (state <= dev->current_state) | 247 | /* no DMA or IRQs except when HC is active */ |
250 | break; | 248 | if (dev->current_state == PCI_D0) { |
251 | 249 | free_irq (hcd->irq, hcd); | |
252 | /* no DMA or IRQs except in D0 */ | ||
253 | if (!dev->current_state) { | ||
254 | pci_save_state (dev); | 250 | pci_save_state (dev); |
255 | pci_disable_device (dev); | 251 | pci_disable_device (dev); |
256 | free_irq (hcd->irq, hcd); | ||
257 | } | 252 | } |
258 | 253 | ||
259 | if (!has_pci_pm) { | 254 | if (!has_pci_pm) { |
@@ -261,25 +256,19 @@ int usb_hcd_pci_suspend (struct pci_dev *dev, u32 state) | |||
261 | break; | 256 | break; |
262 | } | 257 | } |
263 | 258 | ||
264 | /* POLICY: ignore D1/D2/D3hot differences; | 259 | /* NOTE: dev->current_state becomes nonzero only here, and |
265 | * we know D3hot will always work. | 260 | * only for devices that support PCI PM. Also, exiting |
261 | * PCI_D3 (but not PCI_D1 or PCI_D2) is allowed to reset | ||
262 | * some device state (e.g. as part of clock reinit). | ||
266 | */ | 263 | */ |
267 | retval = pci_set_power_state (dev, state); | 264 | retval = pci_set_power_state (dev, PCI_D3hot); |
268 | if (retval < 0 && state < 3) { | ||
269 | retval = pci_set_power_state (dev, 3); | ||
270 | if (retval == 0) | ||
271 | state = 3; | ||
272 | } | ||
273 | if (retval == 0) { | 265 | if (retval == 0) { |
274 | dev_dbg (hcd->self.controller, "--> PCI %s\n", | 266 | dev_dbg (hcd->self.controller, "--> PCI D3\n"); |
275 | pci_state(dev->current_state)); | 267 | pci_enable_wake (dev, PCI_D3hot, hcd->remote_wakeup); |
276 | #ifdef CONFIG_USB_SUSPEND | 268 | pci_enable_wake (dev, PCI_D3cold, hcd->remote_wakeup); |
277 | pci_enable_wake (dev, state, hcd->remote_wakeup); | ||
278 | pci_enable_wake (dev, 4, hcd->remote_wakeup); | ||
279 | #endif | ||
280 | } else if (retval < 0) { | 269 | } else if (retval < 0) { |
281 | dev_dbg (&dev->dev, "PCI %s suspend fail, %d\n", | 270 | dev_dbg (&dev->dev, "PCI D3 suspend fail, %d\n", |
282 | pci_state(state), retval); | 271 | retval); |
283 | (void) usb_hcd_pci_resume (dev); | 272 | (void) usb_hcd_pci_resume (dev); |
284 | break; | 273 | break; |
285 | } | 274 | } |
@@ -287,13 +276,14 @@ int usb_hcd_pci_suspend (struct pci_dev *dev, u32 state) | |||
287 | default: | 276 | default: |
288 | dev_dbg (hcd->self.controller, "hcd state %d; not suspended\n", | 277 | dev_dbg (hcd->self.controller, "hcd state %d; not suspended\n", |
289 | hcd->state); | 278 | hcd->state); |
279 | WARN_ON(1); | ||
290 | retval = -EINVAL; | 280 | retval = -EINVAL; |
291 | break; | 281 | break; |
292 | } | 282 | } |
293 | 283 | ||
294 | /* update power_state **ONLY** to make sysfs happier */ | 284 | /* update power_state **ONLY** to make sysfs happier */ |
295 | if (retval == 0) | 285 | if (retval == 0) |
296 | dev->dev.power.power_state = state; | 286 | dev->dev.power.power_state = message; |
297 | return retval; | 287 | return retval; |
298 | } | 288 | } |
299 | EXPORT_SYMBOL (usb_hcd_pci_suspend); | 289 | EXPORT_SYMBOL (usb_hcd_pci_suspend); |
@@ -308,7 +298,6 @@ int usb_hcd_pci_resume (struct pci_dev *dev) | |||
308 | { | 298 | { |
309 | struct usb_hcd *hcd; | 299 | struct usb_hcd *hcd; |
310 | int retval; | 300 | int retval; |
311 | int has_pci_pm; | ||
312 | 301 | ||
313 | hcd = pci_get_drvdata(dev); | 302 | hcd = pci_get_drvdata(dev); |
314 | if (hcd->state != HC_STATE_SUSPENDED) { | 303 | if (hcd->state != HC_STATE_SUSPENDED) { |
@@ -316,31 +305,73 @@ int usb_hcd_pci_resume (struct pci_dev *dev) | |||
316 | "can't resume, not suspended!\n"); | 305 | "can't resume, not suspended!\n"); |
317 | return 0; | 306 | return 0; |
318 | } | 307 | } |
319 | has_pci_pm = pci_find_capability(dev, PCI_CAP_ID_PM); | ||
320 | 308 | ||
321 | /* D3cold resume isn't usually reported this way... */ | 309 | /* NOTE: chip docs cover clean "real suspend" cases (what Linux |
322 | dev_dbg(hcd->self.controller, "resume from PCI %s%s\n", | 310 | * calls "standby", "suspend to RAM", and so on). There are also |
323 | pci_state(dev->current_state), | 311 | * dirty cases when swsusp fakes a suspend in "shutdown" mode. |
324 | has_pci_pm ? "" : " (legacy)"); | 312 | */ |
313 | if (dev->current_state != PCI_D0) { | ||
314 | #ifdef DEBUG | ||
315 | int pci_pm; | ||
316 | u16 pmcr; | ||
317 | |||
318 | pci_pm = pci_find_capability(dev, PCI_CAP_ID_PM); | ||
319 | pci_read_config_word(dev, pci_pm + PCI_PM_CTRL, &pmcr); | ||
320 | pmcr &= PCI_PM_CTRL_STATE_MASK; | ||
321 | if (pmcr) { | ||
322 | /* Clean case: power to USB and to HC registers was | ||
323 | * maintained; remote wakeup is easy. | ||
324 | */ | ||
325 | dev_dbg(hcd->self.controller, "resume from PCI D%d\n", | ||
326 | pmcr); | ||
327 | } else { | ||
328 | /* Clean: HC lost Vcc power, D0 uninitialized | ||
329 | * + Vaux may have preserved port and transceiver | ||
330 | * state ... for remote wakeup from D3cold | ||
331 | * + or not; HCD must reinit + re-enumerate | ||
332 | * | ||
333 | * Dirty: D0 semi-initialized cases with swsusp | ||
334 | * + after BIOS init | ||
335 | * + after Linux init (HCD statically linked) | ||
336 | */ | ||
337 | dev_dbg(hcd->self.controller, | ||
338 | "PCI D0, from previous PCI D%d\n", | ||
339 | dev->current_state); | ||
340 | } | ||
341 | #endif | ||
342 | pci_enable_wake (dev, dev->current_state, 0); | ||
343 | pci_enable_wake (dev, PCI_D3cold, 0); | ||
344 | } else { | ||
345 | /* Same basic cases: clean (powered/not), dirty */ | ||
346 | dev_dbg(hcd->self.controller, "PCI legacy resume\n"); | ||
347 | } | ||
348 | |||
349 | /* NOTE: the PCI API itself is asymmetric here. We don't need to | ||
350 | * pci_set_power_state(PCI_D0) since that's part of re-enabling; | ||
351 | * but that won't re-enable bus mastering. Yet pci_disable_device() | ||
352 | * explicitly disables bus mastering... | ||
353 | */ | ||
354 | retval = pci_enable_device (dev); | ||
355 | if (retval < 0) { | ||
356 | dev_err (hcd->self.controller, | ||
357 | "can't re-enable after resume, %d!\n", retval); | ||
358 | return retval; | ||
359 | } | ||
360 | pci_set_master (dev); | ||
361 | pci_restore_state (dev); | ||
362 | |||
363 | dev->dev.power.power_state = PMSG_ON; | ||
325 | 364 | ||
326 | hcd->state = HC_STATE_RESUMING; | 365 | hcd->state = HC_STATE_RESUMING; |
327 | 366 | hcd->saw_irq = 0; | |
328 | if (has_pci_pm) | ||
329 | pci_set_power_state (dev, 0); | ||
330 | dev->dev.power.power_state = 0; | ||
331 | retval = request_irq (dev->irq, usb_hcd_irq, SA_SHIRQ, | 367 | retval = request_irq (dev->irq, usb_hcd_irq, SA_SHIRQ, |
332 | hcd->driver->description, hcd); | 368 | hcd->irq_descr, hcd); |
333 | if (retval < 0) { | 369 | if (retval < 0) { |
334 | dev_err (hcd->self.controller, | 370 | dev_err (hcd->self.controller, |
335 | "can't restore IRQ after resume!\n"); | 371 | "can't restore IRQ after resume!\n"); |
372 | usb_hc_died (hcd); | ||
336 | return retval; | 373 | return retval; |
337 | } | 374 | } |
338 | hcd->saw_irq = 0; | ||
339 | pci_restore_state (dev); | ||
340 | #ifdef CONFIG_USB_SUSPEND | ||
341 | pci_enable_wake (dev, dev->current_state, 0); | ||
342 | pci_enable_wake (dev, 4, 0); | ||
343 | #endif | ||
344 | 375 | ||
345 | retval = hcd->driver->resume (hcd); | 376 | retval = hcd->driver->resume (hcd); |
346 | if (!HC_IS_RUNNING (hcd->state)) { | 377 | if (!HC_IS_RUNNING (hcd->state)) { |