diff options
| author | Rafael J. Wysocki <rjw@sisk.pl> | 2008-07-06 21:35:26 -0400 |
|---|---|---|
| committer | Jesse Barnes <jbarnes@virtuousgeek.org> | 2008-07-07 19:26:33 -0400 |
| commit | 404cc2d8ce41ed4031958fba8e633767e8a2e028 (patch) | |
| tree | d679e1ea92d6ae8e72309c02197fd5f1480651fb | |
| parent | eb9d0fe40e313c0a74115ef456a2e43a6c8da72f (diff) | |
PCI PM: Introduce pci_prepare_to_sleep and pci_back_from_sleep
Introduce functions pci_prepare_to_sleep() and pci_back_from_sleep(),
to be used by the PCI drivers that want to place their devices into
the lowest power state appropiate for them (PCI_D3hot, if the device
is not supposed to wake up the system, or the deepest state from
which the wake-up is possible, otherwise) while the system is being
prepared to go into a sleeping state and to put them back into D0
during the subsequent transition to the working state.
Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
| -rw-r--r-- | drivers/pci/pci.c | 83 | ||||
| -rw-r--r-- | include/linux/pci.h | 2 |
2 files changed, 85 insertions, 0 deletions
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index a6b1b6f96abc..1b0ec45e9934 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c | |||
| @@ -1147,6 +1147,87 @@ int pci_enable_wake(struct pci_dev *dev, pci_power_t state, int enable) | |||
| 1147 | } | 1147 | } |
| 1148 | 1148 | ||
| 1149 | /** | 1149 | /** |
| 1150 | * pci_prepare_to_sleep - prepare PCI device for system-wide transition into | ||
| 1151 | * a sleep state | ||
| 1152 | * @dev: Device to handle. | ||
| 1153 | * | ||
| 1154 | * Choose the power state appropriate for the device depending on whether | ||
| 1155 | * it can wake up the system and/or is power manageable by the platform | ||
| 1156 | * (PCI_D3hot is the default) and put the device into that state. | ||
| 1157 | */ | ||
| 1158 | int pci_prepare_to_sleep(struct pci_dev *dev) | ||
| 1159 | { | ||
| 1160 | pci_power_t target_state = PCI_D3hot; | ||
| 1161 | int pm = pci_find_capability(dev, PCI_CAP_ID_PM); | ||
| 1162 | int error; | ||
| 1163 | |||
| 1164 | if (platform_pci_power_manageable(dev)) { | ||
| 1165 | /* | ||
| 1166 | * Call the platform to choose the target state of the device | ||
| 1167 | * and enable wake-up from this state if supported. | ||
| 1168 | */ | ||
| 1169 | pci_power_t state = platform_pci_choose_state(dev); | ||
| 1170 | |||
| 1171 | switch (state) { | ||
| 1172 | case PCI_POWER_ERROR: | ||
| 1173 | case PCI_UNKNOWN: | ||
| 1174 | break; | ||
| 1175 | case PCI_D1: | ||
| 1176 | case PCI_D2: | ||
| 1177 | if (pci_no_d1d2(dev)) | ||
| 1178 | break; | ||
| 1179 | default: | ||
| 1180 | target_state = state; | ||
| 1181 | pci_enable_wake(dev, target_state, true); | ||
| 1182 | } | ||
| 1183 | } else if (device_may_wakeup(&dev->dev)) { | ||
| 1184 | /* | ||
| 1185 | * Find the deepest state from which the device can generate | ||
| 1186 | * wake-up events, make it the target state and enable device | ||
| 1187 | * to generate PME#. | ||
| 1188 | */ | ||
| 1189 | u16 pmc; | ||
| 1190 | |||
| 1191 | if (!pm) | ||
| 1192 | return -EIO; | ||
| 1193 | |||
| 1194 | pci_read_config_word(dev, pm + PCI_PM_PMC, &pmc); | ||
| 1195 | if (pmc & PCI_PM_CAP_PME_MASK) { | ||
| 1196 | if (!(pmc & PCI_PM_CAP_PME_D3)) { | ||
| 1197 | /* Device cannot generate PME# from D3_hot */ | ||
| 1198 | if (pmc & PCI_PM_CAP_PME_D2) | ||
| 1199 | target_state = PCI_D2; | ||
| 1200 | else if (pmc & PCI_PM_CAP_PME_D1) | ||
| 1201 | target_state = PCI_D1; | ||
| 1202 | else | ||
| 1203 | target_state = PCI_D0; | ||
| 1204 | } | ||
| 1205 | pci_pme_active(dev, pm, true); | ||
| 1206 | } | ||
| 1207 | } | ||
| 1208 | |||
| 1209 | error = pci_set_power_state(dev, target_state); | ||
| 1210 | |||
| 1211 | if (error) | ||
| 1212 | pci_enable_wake(dev, target_state, false); | ||
| 1213 | |||
| 1214 | return error; | ||
| 1215 | } | ||
| 1216 | |||
| 1217 | /** | ||
| 1218 | * pci_back_from_sleep - turn PCI device on during system-wide transition into | ||
| 1219 | * the working state a sleep state | ||
| 1220 | * @dev: Device to handle. | ||
| 1221 | * | ||
| 1222 | * Disable device's sytem wake-up capability and put it into D0. | ||
| 1223 | */ | ||
| 1224 | int pci_back_from_sleep(struct pci_dev *dev) | ||
| 1225 | { | ||
| 1226 | pci_enable_wake(dev, PCI_D0, false); | ||
| 1227 | return pci_set_power_state(dev, PCI_D0); | ||
| 1228 | } | ||
| 1229 | |||
| 1230 | /** | ||
| 1150 | * pci_pm_init - Initialize PM functions of given PCI device | 1231 | * pci_pm_init - Initialize PM functions of given PCI device |
| 1151 | * @dev: PCI device to handle. | 1232 | * @dev: PCI device to handle. |
| 1152 | */ | 1233 | */ |
| @@ -1853,5 +1934,7 @@ EXPORT_SYMBOL(pci_set_power_state); | |||
| 1853 | EXPORT_SYMBOL(pci_save_state); | 1934 | EXPORT_SYMBOL(pci_save_state); |
| 1854 | EXPORT_SYMBOL(pci_restore_state); | 1935 | EXPORT_SYMBOL(pci_restore_state); |
| 1855 | EXPORT_SYMBOL(pci_enable_wake); | 1936 | EXPORT_SYMBOL(pci_enable_wake); |
| 1937 | EXPORT_SYMBOL(pci_prepare_to_sleep); | ||
| 1938 | EXPORT_SYMBOL(pci_back_from_sleep); | ||
| 1856 | EXPORT_SYMBOL_GPL(pci_set_pcie_reset_state); | 1939 | EXPORT_SYMBOL_GPL(pci_set_pcie_reset_state); |
| 1857 | 1940 | ||
diff --git a/include/linux/pci.h b/include/linux/pci.h index 4c80dc3f2990..52ac06dcce98 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h | |||
| @@ -632,6 +632,8 @@ int pci_restore_state(struct pci_dev *dev); | |||
| 632 | int pci_set_power_state(struct pci_dev *dev, pci_power_t state); | 632 | int pci_set_power_state(struct pci_dev *dev, pci_power_t state); |
| 633 | pci_power_t pci_choose_state(struct pci_dev *dev, pm_message_t state); | 633 | pci_power_t pci_choose_state(struct pci_dev *dev, pm_message_t state); |
| 634 | int pci_enable_wake(struct pci_dev *dev, pci_power_t state, int enable); | 634 | int pci_enable_wake(struct pci_dev *dev, pci_power_t state, int enable); |
| 635 | int pci_prepare_to_sleep(struct pci_dev *dev); | ||
| 636 | int pci_back_from_sleep(struct pci_dev *dev); | ||
| 635 | 637 | ||
| 636 | /* Functions for PCI Hotplug drivers to use */ | 638 | /* Functions for PCI Hotplug drivers to use */ |
| 637 | int pci_bus_find_capability(struct pci_bus *bus, unsigned int devfn, int cap); | 639 | int pci_bus_find_capability(struct pci_bus *bus, unsigned int devfn, int cap); |
