diff options
author | Rafael J. Wysocki <rjw@sisk.pl> | 2008-07-06 21:34:48 -0400 |
---|---|---|
committer | Jesse Barnes <jbarnes@virtuousgeek.org> | 2008-07-07 19:26:28 -0400 |
commit | eb9d0fe40e313c0a74115ef456a2e43a6c8da72f (patch) | |
tree | 7a90a68b8dc152d49a38469fd6a6a7840954bac2 /drivers/pci/pci.c | |
parent | 0af4b8c4fb31193dc666f4893107a18fef82baab (diff) |
PCI ACPI: Rework PCI handling of wake-up
* Introduce function acpi_pm_device_sleep_wake() for enabling and
disabling the system wake-up capability of devices that are power
manageable by ACPI.
* Introduce function acpi_bus_can_wakeup() allowing other (dependent)
subsystems to check if ACPI is able to enable the system wake-up
capability of given device.
* Introduce callback .sleep_wake() in struct pci_platform_pm_ops and
for the ACPI PCI 'driver' make it use acpi_pm_device_sleep_wake().
* Introduce callback .can_wakeup() in struct pci_platform_pm_ops and
for the ACPI 'driver' make it use acpi_bus_can_wakeup().
* Move the PME# handlig code out of pci_enable_wake() and split it
into two functions, pci_pme_capable() and pci_pme_active(),
allowing the caller to check if given device is capable of
generating PME# from given power state and to enable/disable the
device's PME# functionality, respectively.
* Modify pci_enable_wake() to use the new ACPI callbacks and the new
PME#-related functions.
* Drop the generic .platform_enable_wakeup() callback that is not
used any more.
* Introduce device_set_wakeup_capable() that will set the
power.can_wakeup flag of given device.
* Rework PCI device PM initialization so that, if given device is
capable of generating wake-up events, either natively through the
PME# mechanism, or with the help of the platform, its
power.can_wakeup flag is set and its power.should_wakeup flag is
unset as appropriate.
* Make ACPI set the power.can_wakeup flag for devices found to be
wake-up capable by it.
* Make the ACPI wake-up code enable/disable GPEs for devices that
have the wakeup.flags.prepared flag set (which means that their
wake-up power has been enabled).
Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
Diffstat (limited to 'drivers/pci/pci.c')
-rw-r--r-- | drivers/pci/pci.c | 169 |
1 files changed, 124 insertions, 45 deletions
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 20e28077b96d..a6b1b6f96abc 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c | |||
@@ -380,7 +380,8 @@ static struct pci_platform_pm_ops *pci_platform_pm; | |||
380 | 380 | ||
381 | int pci_set_platform_pm(struct pci_platform_pm_ops *ops) | 381 | int pci_set_platform_pm(struct pci_platform_pm_ops *ops) |
382 | { | 382 | { |
383 | if (!ops->is_manageable || !ops->set_state || !ops->choose_state) | 383 | if (!ops->is_manageable || !ops->set_state || !ops->choose_state |
384 | || !ops->sleep_wake || !ops->can_wakeup) | ||
384 | return -EINVAL; | 385 | return -EINVAL; |
385 | pci_platform_pm = ops; | 386 | pci_platform_pm = ops; |
386 | return 0; | 387 | return 0; |
@@ -403,6 +404,17 @@ static inline pci_power_t platform_pci_choose_state(struct pci_dev *dev) | |||
403 | pci_platform_pm->choose_state(dev) : PCI_POWER_ERROR; | 404 | pci_platform_pm->choose_state(dev) : PCI_POWER_ERROR; |
404 | } | 405 | } |
405 | 406 | ||
407 | static inline bool platform_pci_can_wakeup(struct pci_dev *dev) | ||
408 | { | ||
409 | return pci_platform_pm ? pci_platform_pm->can_wakeup(dev) : false; | ||
410 | } | ||
411 | |||
412 | static inline int platform_pci_sleep_wake(struct pci_dev *dev, bool enable) | ||
413 | { | ||
414 | return pci_platform_pm ? | ||
415 | pci_platform_pm->sleep_wake(dev, enable) : -ENODEV; | ||
416 | } | ||
417 | |||
406 | /** | 418 | /** |
407 | * pci_raw_set_power_state - Use PCI PM registers to set the power state of | 419 | * pci_raw_set_power_state - Use PCI PM registers to set the power state of |
408 | * given PCI device | 420 | * given PCI device |
@@ -1036,6 +1048,56 @@ int pci_set_pcie_reset_state(struct pci_dev *dev, enum pcie_reset_state state) | |||
1036 | } | 1048 | } |
1037 | 1049 | ||
1038 | /** | 1050 | /** |
1051 | * pci_pme_capable - check the capability of PCI device to generate PME# | ||
1052 | * @dev: PCI device to handle. | ||
1053 | * @pm: PCI PM capability offset of the device. | ||
1054 | * @state: PCI state from which device will issue PME#. | ||
1055 | */ | ||
1056 | static bool pci_pme_capable(struct pci_dev *dev, int pm, pci_power_t state) | ||
1057 | { | ||
1058 | u16 pmc; | ||
1059 | |||
1060 | if (!pm) | ||
1061 | return false; | ||
1062 | |||
1063 | /* Check device's ability to generate PME# from given state */ | ||
1064 | pci_read_config_word(dev, pm + PCI_PM_PMC, &pmc); | ||
1065 | |||
1066 | pmc &= PCI_PM_CAP_PME_MASK; | ||
1067 | pmc >>= ffs(PCI_PM_CAP_PME_MASK) - 1; /* First bit of mask */ | ||
1068 | |||
1069 | return !!(pmc & (1 << state)); | ||
1070 | } | ||
1071 | |||
1072 | /** | ||
1073 | * pci_pme_active - enable or disable PCI device's PME# function | ||
1074 | * @dev: PCI device to handle. | ||
1075 | * @pm: PCI PM capability offset of the device. | ||
1076 | * @enable: 'true' to enable PME# generation; 'false' to disable it. | ||
1077 | * | ||
1078 | * The caller must verify that the device is capable of generating PME# before | ||
1079 | * calling this function with @enable equal to 'true'. | ||
1080 | */ | ||
1081 | static void pci_pme_active(struct pci_dev *dev, int pm, bool enable) | ||
1082 | { | ||
1083 | u16 pmcsr; | ||
1084 | |||
1085 | if (!pm) | ||
1086 | return; | ||
1087 | |||
1088 | pci_read_config_word(dev, pm + PCI_PM_CTRL, &pmcsr); | ||
1089 | /* Clear PME_Status by writing 1 to it and enable PME# */ | ||
1090 | pmcsr |= PCI_PM_CTRL_PME_STATUS | PCI_PM_CTRL_PME_ENABLE; | ||
1091 | if (!enable) | ||
1092 | pmcsr &= ~PCI_PM_CTRL_PME_ENABLE; | ||
1093 | |||
1094 | pci_write_config_word(dev, pm + PCI_PM_CTRL, pmcsr); | ||
1095 | |||
1096 | dev_printk(KERN_INFO, &dev->dev, "PME# %s\n", | ||
1097 | enable ? "enabled" : "disabled"); | ||
1098 | } | ||
1099 | |||
1100 | /** | ||
1039 | * pci_enable_wake - enable PCI device as wakeup event source | 1101 | * pci_enable_wake - enable PCI device as wakeup event source |
1040 | * @dev: PCI device affected | 1102 | * @dev: PCI device affected |
1041 | * @state: PCI state from which device will issue wakeup events | 1103 | * @state: PCI state from which device will issue wakeup events |
@@ -1046,66 +1108,83 @@ int pci_set_pcie_reset_state(struct pci_dev *dev, enum pcie_reset_state state) | |||
1046 | * called automatically by this routine. | 1108 | * called automatically by this routine. |
1047 | * | 1109 | * |
1048 | * Devices with legacy power management (no standard PCI PM capabilities) | 1110 | * Devices with legacy power management (no standard PCI PM capabilities) |
1049 | * always require such platform hooks. Depending on the platform, devices | 1111 | * always require such platform hooks. |
1050 | * supporting the standard PCI PME# signal may require such platform hooks; | ||
1051 | * they always update bits in config space to allow PME# generation. | ||
1052 | * | 1112 | * |
1053 | * -EIO is returned if the device can't ever be a wakeup event source. | 1113 | * RETURN VALUE: |
1054 | * -EINVAL is returned if the device can't generate wakeup events from | 1114 | * 0 is returned on success |
1055 | * the specified PCI state. Returns zero if the operation is successful. | 1115 | * -EINVAL is returned if device is not supposed to wake up the system |
1116 | * Error code depending on the platform is returned if both the platform and | ||
1117 | * the native mechanism fail to enable the generation of wake-up events | ||
1056 | */ | 1118 | */ |
1057 | int pci_enable_wake(struct pci_dev *dev, pci_power_t state, int enable) | 1119 | int pci_enable_wake(struct pci_dev *dev, pci_power_t state, int enable) |
1058 | { | 1120 | { |
1059 | int pm; | 1121 | int pm; |
1060 | int status; | 1122 | int error = 0; |
1061 | u16 value; | 1123 | bool pme_done = false; |
1062 | |||
1063 | /* Note that drivers should verify device_may_wakeup(&dev->dev) | ||
1064 | * before calling this function. Platform code should report | ||
1065 | * errors when drivers try to enable wakeup on devices that | ||
1066 | * can't issue wakeups, or on which wakeups were disabled by | ||
1067 | * userspace updating the /sys/devices.../power/wakeup file. | ||
1068 | */ | ||
1069 | 1124 | ||
1070 | status = call_platform_enable_wakeup(&dev->dev, enable); | 1125 | if (!device_may_wakeup(&dev->dev)) |
1071 | 1126 | return -EINVAL; | |
1072 | /* find PCI PM capability in list */ | ||
1073 | pm = pci_find_capability(dev, PCI_CAP_ID_PM); | ||
1074 | 1127 | ||
1075 | /* If device doesn't support PM Capabilities, but caller wants to | 1128 | /* |
1076 | * disable wake events, it's a NOP. Otherwise fail unless the | 1129 | * According to "PCI System Architecture" 4th ed. by Tom Shanley & Don |
1077 | * platform hooks handled this legacy device already. | 1130 | * Anderson we should be doing PME# wake enable followed by ACPI wake |
1131 | * enable. To disable wake-up we call the platform first, for symmetry. | ||
1078 | */ | 1132 | */ |
1079 | if (!pm) | ||
1080 | return enable ? status : 0; | ||
1081 | 1133 | ||
1082 | /* Check device's ability to generate PME# */ | 1134 | if (!enable && platform_pci_can_wakeup(dev)) |
1083 | pci_read_config_word(dev,pm+PCI_PM_PMC,&value); | 1135 | error = platform_pci_sleep_wake(dev, false); |
1084 | 1136 | ||
1085 | value &= PCI_PM_CAP_PME_MASK; | 1137 | pm = pci_find_capability(dev, PCI_CAP_ID_PM); |
1086 | value >>= ffs(PCI_PM_CAP_PME_MASK) - 1; /* First bit of mask */ | 1138 | if (!enable || pci_pme_capable(dev, pm, state)) { |
1087 | 1139 | pci_pme_active(dev, pm, enable); | |
1088 | /* Check if it can generate PME# from requested state. */ | 1140 | pme_done = true; |
1089 | if (!value || !(value & (1 << state))) { | ||
1090 | /* if it can't, revert what the platform hook changed, | ||
1091 | * always reporting the base "EINVAL, can't PME#" error | ||
1092 | */ | ||
1093 | if (enable) | ||
1094 | call_platform_enable_wakeup(&dev->dev, 0); | ||
1095 | return enable ? -EINVAL : 0; | ||
1096 | } | 1141 | } |
1097 | 1142 | ||
1098 | pci_read_config_word(dev, pm + PCI_PM_CTRL, &value); | 1143 | if (enable && platform_pci_can_wakeup(dev)) |
1144 | error = platform_pci_sleep_wake(dev, true); | ||
1099 | 1145 | ||
1100 | /* Clear PME_Status by writing 1 to it and enable PME# */ | 1146 | return pme_done ? 0 : error; |
1101 | value |= PCI_PM_CTRL_PME_STATUS | PCI_PM_CTRL_PME_ENABLE; | 1147 | } |
1102 | 1148 | ||
1103 | if (!enable) | 1149 | /** |
1104 | value &= ~PCI_PM_CTRL_PME_ENABLE; | 1150 | * pci_pm_init - Initialize PM functions of given PCI device |
1151 | * @dev: PCI device to handle. | ||
1152 | */ | ||
1153 | void pci_pm_init(struct pci_dev *dev) | ||
1154 | { | ||
1155 | int pm; | ||
1156 | u16 pmc; | ||
1105 | 1157 | ||
1106 | pci_write_config_word(dev, pm + PCI_PM_CTRL, value); | 1158 | /* find PCI PM capability in list */ |
1159 | pm = pci_find_capability(dev, PCI_CAP_ID_PM); | ||
1160 | if (!pm) | ||
1161 | return; | ||
1162 | /* Check device's ability to generate PME# */ | ||
1163 | pci_read_config_word(dev, pm + PCI_PM_PMC, &pmc); | ||
1107 | 1164 | ||
1108 | return 0; | 1165 | if ((pmc & PCI_PM_CAP_VER_MASK) > 3) { |
1166 | dev_err(&dev->dev, "unsupported PM cap regs version (%u)\n", | ||
1167 | pmc & PCI_PM_CAP_VER_MASK); | ||
1168 | return; | ||
1169 | } | ||
1170 | |||
1171 | if (pmc & PCI_PM_CAP_PME_MASK) { | ||
1172 | dev_printk(KERN_INFO, &dev->dev, | ||
1173 | "PME# supported from%s%s%s%s%s\n", | ||
1174 | (pmc & PCI_PM_CAP_PME_D0) ? " D0" : "", | ||
1175 | (pmc & PCI_PM_CAP_PME_D1) ? " D1" : "", | ||
1176 | (pmc & PCI_PM_CAP_PME_D2) ? " D2" : "", | ||
1177 | (pmc & PCI_PM_CAP_PME_D3) ? " D3hot" : "", | ||
1178 | (pmc & PCI_PM_CAP_PME_D3cold) ? " D3cold" : ""); | ||
1179 | /* | ||
1180 | * Make device's PM flags reflect the wake-up capability, but | ||
1181 | * let the user space enable it to wake up the system as needed. | ||
1182 | */ | ||
1183 | device_set_wakeup_capable(&dev->dev, true); | ||
1184 | device_set_wakeup_enable(&dev->dev, false); | ||
1185 | /* Disable the PME# generation functionality */ | ||
1186 | pci_pme_active(dev, pm, false); | ||
1187 | } | ||
1109 | } | 1188 | } |
1110 | 1189 | ||
1111 | int | 1190 | int |