aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
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)) {