aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/pci/pci.c
diff options
context:
space:
mode:
authorRafael J. Wysocki <rjw@sisk.pl>2008-07-06 21:32:52 -0400
committerJesse Barnes <jbarnes@virtuousgeek.org>2008-07-07 19:25:43 -0400
commit44e4e66eeae5338b3ca0b28f8352e60bf18d5ba8 (patch)
tree27c32f382f7af839733cbdc5dedf4fc979708a05 /drivers/pci/pci.c
parent961d9120fa6f078492a1c762dd91f2c097e56c83 (diff)
PCI: rework pci_set_power_state function to call platform first
Rework pci_set_power_state() so that the platform callback is invoked before the native mechanism, if necessary. Also, make the function check if the device is power manageable by the platform before invoking the platform callback. This may matter if the device dependent on additional power resources controlled by the platform is being put into D0, in which case those power resources must be turned on before we attempt to handle the device itself. Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl> Acked-by: Pavel Machek <pavel@suse.cz> Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
Diffstat (limited to 'drivers/pci/pci.c')
-rw-r--r--drivers/pci/pci.c150
1 files changed, 104 insertions, 46 deletions
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index f8074525267c..20e28077b96d 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -404,67 +404,56 @@ static inline pci_power_t platform_pci_choose_state(struct pci_dev *dev)
404} 404}
405 405
406/** 406/**
407 * pci_set_power_state - Set the power state of a PCI device 407 * pci_raw_set_power_state - Use PCI PM registers to set the power state of
408 * @dev: PCI device to be suspended 408 * given PCI device
409 * @state: PCI power state (D0, D1, D2, D3hot, D3cold) we're entering 409 * @dev: PCI device to handle.
410 * @pm: PCI PM capability offset of the device.
411 * @state: PCI power state (D0, D1, D2, D3hot) to put the device into.
410 * 412 *
411 * Transition a device to a new power state, using the Power Management 413 * RETURN VALUE:
412 * Capabilities in the device's config space. 414 * -EINVAL if the requested state is invalid.
413 * 415 * -EIO if device does not support PCI PM or its PM capabilities register has a
414 * RETURN VALUE: 416 * wrong version, or device doesn't support the requested state.
415 * -EINVAL if trying to enter a lower state than we're already in. 417 * 0 if device already is in the requested state.
416 * 0 if we're already in the requested state. 418 * 0 if device's power state has been successfully changed.
417 * -EIO if device does not support PCI PM.
418 * 0 if we can successfully change the power state.
419 */ 419 */
420int 420static int
421pci_set_power_state(struct pci_dev *dev, pci_power_t state) 421pci_raw_set_power_state(struct pci_dev *dev, int pm, pci_power_t state)
422{ 422{
423 int pm, need_restore = 0;
424 u16 pmcsr, pmc; 423 u16 pmcsr, pmc;
424 bool need_restore = false;
425 425
426 /* bound the state we're entering */
427 if (state > PCI_D3hot)
428 state = PCI_D3hot;
429
430 /*
431 * If the device or the parent bridge can't support PCI PM, ignore
432 * the request if we're doing anything besides putting it into D0
433 * (which would only happen on boot).
434 */
435 if ((state == PCI_D1 || state == PCI_D2) && pci_no_d1d2(dev))
436 return 0;
437
438 /* find PCI PM capability in list */
439 pm = pci_find_capability(dev, PCI_CAP_ID_PM);
440
441 /* abort if the device doesn't support PM capabilities */
442 if (!pm) 426 if (!pm)
443 return -EIO; 427 return -EIO;
444 428
429 if (state < PCI_D0 || state > PCI_D3hot)
430 return -EINVAL;
431
445 /* Validate current state: 432 /* Validate current state:
446 * Can enter D0 from any state, but if we can only go deeper 433 * Can enter D0 from any state, but if we can only go deeper
447 * to sleep if we're already in a low power state 434 * to sleep if we're already in a low power state
448 */ 435 */
449 if (state != PCI_D0 && dev->current_state > state) { 436 if (dev->current_state == state) {
437 /* we're already there */
438 return 0;
439 } else if (state != PCI_D0 && dev->current_state <= PCI_D3cold
440 && dev->current_state > state) {
450 dev_err(&dev->dev, "invalid power transition " 441 dev_err(&dev->dev, "invalid power transition "
451 "(from state %d to %d)\n", dev->current_state, state); 442 "(from state %d to %d)\n", dev->current_state, state);
452 return -EINVAL; 443 return -EINVAL;
453 } else if (dev->current_state == state) 444 }
454 return 0; /* we're already there */
455 445
446 pci_read_config_word(dev, pm + PCI_PM_PMC, &pmc);
456 447
457 pci_read_config_word(dev,pm + PCI_PM_PMC,&pmc);
458 if ((pmc & PCI_PM_CAP_VER_MASK) > 3) { 448 if ((pmc & PCI_PM_CAP_VER_MASK) > 3) {
459 dev_printk(KERN_DEBUG, &dev->dev, "unsupported PM cap regs " 449 dev_err(&dev->dev, "unsupported PM cap regs version (%u)\n",
460 "version (%u)\n", pmc & PCI_PM_CAP_VER_MASK); 450 pmc & PCI_PM_CAP_VER_MASK);
461 return -EIO; 451 return -EIO;
462 } 452 }
463 453
464 /* check if this device supports the desired state */ 454 /* check if this device supports the desired state */
465 if (state == PCI_D1 && !(pmc & PCI_PM_CAP_D1)) 455 if ((state == PCI_D1 && !(pmc & PCI_PM_CAP_D1))
466 return -EIO; 456 || (state == PCI_D2 && !(pmc & PCI_PM_CAP_D2)))
467 else if (state == PCI_D2 && !(pmc & PCI_PM_CAP_D2))
468 return -EIO; 457 return -EIO;
469 458
470 pci_read_config_word(dev, pm + PCI_PM_CTRL, &pmcsr); 459 pci_read_config_word(dev, pm + PCI_PM_CTRL, &pmcsr);
@@ -483,7 +472,7 @@ pci_set_power_state(struct pci_dev *dev, pci_power_t state)
483 case PCI_UNKNOWN: /* Boot-up */ 472 case PCI_UNKNOWN: /* Boot-up */
484 if ((pmcsr & PCI_PM_CTRL_STATE_MASK) == PCI_D3hot 473 if ((pmcsr & PCI_PM_CTRL_STATE_MASK) == PCI_D3hot
485 && !(pmcsr & PCI_PM_CTRL_NO_SOFT_RESET)) 474 && !(pmcsr & PCI_PM_CTRL_NO_SOFT_RESET))
486 need_restore = 1; 475 need_restore = true;
487 /* Fall-through: force to D0 */ 476 /* Fall-through: force to D0 */
488 default: 477 default:
489 pmcsr = 0; 478 pmcsr = 0;
@@ -500,12 +489,6 @@ pci_set_power_state(struct pci_dev *dev, pci_power_t state)
500 else if (state == PCI_D2 || dev->current_state == PCI_D2) 489 else if (state == PCI_D2 || dev->current_state == PCI_D2)
501 udelay(200); 490 udelay(200);
502 491
503 /*
504 * Give firmware a chance to be called, such as ACPI _PRx, _PSx
505 * Firmware method after native method ?
506 */
507 platform_pci_set_power_state(dev, state);
508
509 dev->current_state = state; 492 dev->current_state = state;
510 493
511 /* According to section 5.4.1 of the "PCI BUS POWER MANAGEMENT 494 /* According to section 5.4.1 of the "PCI BUS POWER MANAGEMENT
@@ -530,6 +513,81 @@ pci_set_power_state(struct pci_dev *dev, pci_power_t state)
530} 513}
531 514
532/** 515/**
516 * pci_update_current_state - Read PCI power state of given device from its
517 * PCI PM registers and cache it
518 * @dev: PCI device to handle.
519 * @pm: PCI PM capability offset of the device.
520 */
521static void pci_update_current_state(struct pci_dev *dev, int pm)
522{
523 if (pm) {
524 u16 pmcsr;
525
526 pci_read_config_word(dev, pm + PCI_PM_CTRL, &pmcsr);
527 dev->current_state = (pmcsr & PCI_PM_CTRL_STATE_MASK);
528 }
529}
530
531/**
532 * pci_set_power_state - Set the power state of a PCI device
533 * @dev: PCI device to handle.
534 * @state: PCI power state (D0, D1, D2, D3hot) to put the device into.
535 *
536 * Transition a device to a new power state, using the platform formware and/or
537 * the device's PCI PM registers.
538 *
539 * RETURN VALUE:
540 * -EINVAL if the requested state is invalid.
541 * -EIO if device does not support PCI PM or its PM capabilities register has a
542 * wrong version, or device doesn't support the requested state.
543 * 0 if device already is in the requested state.
544 * 0 if device's power state has been successfully changed.
545 */
546int pci_set_power_state(struct pci_dev *dev, pci_power_t state)
547{
548 int pm, error;
549
550 /* bound the state we're entering */
551 if (state > PCI_D3hot)
552 state = PCI_D3hot;
553 else if (state < PCI_D0)
554 state = PCI_D0;
555 else if ((state == PCI_D1 || state == PCI_D2) && pci_no_d1d2(dev))
556 /*
557 * If the device or the parent bridge do not support PCI PM,
558 * ignore the request if we're doing anything other than putting
559 * it into D0 (which would only happen on boot).
560 */
561 return 0;
562
563 /* Find PCI PM capability in the list */
564 pm = pci_find_capability(dev, PCI_CAP_ID_PM);
565
566 if (state == PCI_D0 && platform_pci_power_manageable(dev)) {
567 /*
568 * Allow the platform to change the state, for example via ACPI
569 * _PR0, _PS0 and some such, but do not trust it.
570 */
571 int ret = platform_pci_set_power_state(dev, PCI_D0);
572 if (!ret)
573 pci_update_current_state(dev, pm);
574 }
575
576 error = pci_raw_set_power_state(dev, pm, state);
577
578 if (state > PCI_D0 && platform_pci_power_manageable(dev)) {
579 /* Allow the platform to finalize the transition */
580 int ret = platform_pci_set_power_state(dev, state);
581 if (!ret) {
582 pci_update_current_state(dev, pm);
583 error = 0;
584 }
585 }
586
587 return error;
588}
589
590/**
533 * pci_choose_state - Choose the power state of a PCI device 591 * pci_choose_state - Choose the power state of a PCI device
534 * @dev: PCI device to be suspended 592 * @dev: PCI device to be suspended
535 * @state: target sleep state for the whole system. This is the value 593 * @state: target sleep state for the whole system. This is the value