aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorDavid Brownell <david-b@pacbell.net>2005-04-18 20:39:22 -0400
committerGreg K-H <gregkh@suse.de>2005-04-18 20:39:22 -0400
commitc6053ecffb895f6c0e0ec9c1d298e35cffc1f7a6 (patch)
tree9f3d796b1f41338bf276c82b58d260125541e8a0 /drivers
parent84d79cb8db2811140c911df7ce3e3354cfa018c4 (diff)
[PATCH] usb resume fixes
This has a variety of updates to the shared suspend/resume code for PCI based USB host controllers. - Cope with pm_message_t replacing the target system state. This is actually a loss of functionality; PCI D1 and D2 states will no longer be used, and it's no longer knowable that D3cold is on the way so power will be lost. - Most importantly, some of the resume paths are reworked and cleaned up. They're now an exact mirror of suspend paths, and more care is taken to ensure the hardware is reactivated before the hardware re-enables interrupts. Plus comment and diagnostic cleanups; there are some nasty cases here especially combined with swsusp, now they're somewhat commented. Signed-off-by: David Brownell <dbrownell@users.sourceforge.net> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> diff -puN drivers/usb/core/hcd-pci.c~usb-resume-fixes drivers/usb/core/hcd-pci.c
Diffstat (limited to 'drivers')
-rw-r--r--drivers/usb/core/hcd-pci.c151
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
189static 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 */
208int usb_hcd_pci_suspend (struct pci_dev *dev, u32 state) 196int 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}
299EXPORT_SYMBOL (usb_hcd_pci_suspend); 289EXPORT_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)) {