diff options
Diffstat (limited to 'drivers/pci/pci.c')
-rw-r--r-- | drivers/pci/pci.c | 150 |
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 | */ |
420 | int | 420 | static int |
421 | pci_set_power_state(struct pci_dev *dev, pci_power_t state) | 421 | pci_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 | */ | ||
521 | static 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 | */ | ||
546 | int 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 |