diff options
Diffstat (limited to 'drivers/acpi/device_pm.c')
-rw-r--r-- | drivers/acpi/device_pm.c | 100 |
1 files changed, 60 insertions, 40 deletions
diff --git a/drivers/acpi/device_pm.c b/drivers/acpi/device_pm.c index 735db11a9b00..88dbbb115285 100644 --- a/drivers/acpi/device_pm.c +++ b/drivers/acpi/device_pm.c | |||
@@ -98,17 +98,16 @@ int acpi_device_get_power(struct acpi_device *device, int *state) | |||
98 | 98 | ||
99 | /* | 99 | /* |
100 | * The power resources settings may indicate a power state | 100 | * The power resources settings may indicate a power state |
101 | * shallower than the actual power state of the device. | 101 | * shallower than the actual power state of the device, because |
102 | * the same power resources may be referenced by other devices. | ||
102 | * | 103 | * |
103 | * Moreover, on systems predating ACPI 4.0, if the device | 104 | * For systems predating ACPI 4.0 we assume that D3hot is the |
104 | * doesn't depend on any power resources and _PSC returns 3, | 105 | * deepest state that can be supported. |
105 | * that means "power off". We need to maintain compatibility | ||
106 | * with those systems. | ||
107 | */ | 106 | */ |
108 | if (psc > result && psc < ACPI_STATE_D3_COLD) | 107 | if (psc > result && psc < ACPI_STATE_D3_COLD) |
109 | result = psc; | 108 | result = psc; |
110 | else if (result == ACPI_STATE_UNKNOWN) | 109 | else if (result == ACPI_STATE_UNKNOWN) |
111 | result = psc > ACPI_STATE_D2 ? ACPI_STATE_D3_COLD : psc; | 110 | result = psc > ACPI_STATE_D2 ? ACPI_STATE_D3_HOT : psc; |
112 | } | 111 | } |
113 | 112 | ||
114 | /* | 113 | /* |
@@ -153,8 +152,8 @@ static int acpi_dev_pm_explicit_set(struct acpi_device *adev, int state) | |||
153 | */ | 152 | */ |
154 | int acpi_device_set_power(struct acpi_device *device, int state) | 153 | int acpi_device_set_power(struct acpi_device *device, int state) |
155 | { | 154 | { |
155 | int target_state = state; | ||
156 | int result = 0; | 156 | int result = 0; |
157 | bool cut_power = false; | ||
158 | 157 | ||
159 | if (!device || !device->flags.power_manageable | 158 | if (!device || !device->flags.power_manageable |
160 | || (state < ACPI_STATE_D0) || (state > ACPI_STATE_D3_COLD)) | 159 | || (state < ACPI_STATE_D0) || (state > ACPI_STATE_D3_COLD)) |
@@ -169,11 +168,21 @@ int acpi_device_set_power(struct acpi_device *device, int state) | |||
169 | return 0; | 168 | return 0; |
170 | } | 169 | } |
171 | 170 | ||
172 | if (!device->power.states[state].flags.valid) { | 171 | if (state == ACPI_STATE_D3_COLD) { |
172 | /* | ||
173 | * For transitions to D3cold we need to execute _PS3 and then | ||
174 | * possibly drop references to the power resources in use. | ||
175 | */ | ||
176 | state = ACPI_STATE_D3_HOT; | ||
177 | /* If _PR3 is not available, use D3hot as the target state. */ | ||
178 | if (!device->power.states[ACPI_STATE_D3_COLD].flags.valid) | ||
179 | target_state = state; | ||
180 | } else if (!device->power.states[state].flags.valid) { | ||
173 | dev_warn(&device->dev, "Power state %s not supported\n", | 181 | dev_warn(&device->dev, "Power state %s not supported\n", |
174 | acpi_power_state_string(state)); | 182 | acpi_power_state_string(state)); |
175 | return -ENODEV; | 183 | return -ENODEV; |
176 | } | 184 | } |
185 | |||
177 | if (!device->power.flags.ignore_parent && | 186 | if (!device->power.flags.ignore_parent && |
178 | device->parent && (state < device->parent->power.state)) { | 187 | device->parent && (state < device->parent->power.state)) { |
179 | dev_warn(&device->dev, | 188 | dev_warn(&device->dev, |
@@ -183,39 +192,38 @@ int acpi_device_set_power(struct acpi_device *device, int state) | |||
183 | return -ENODEV; | 192 | return -ENODEV; |
184 | } | 193 | } |
185 | 194 | ||
186 | /* For D3cold we should first transition into D3hot. */ | ||
187 | if (state == ACPI_STATE_D3_COLD | ||
188 | && device->power.states[ACPI_STATE_D3_COLD].flags.os_accessible) { | ||
189 | state = ACPI_STATE_D3_HOT; | ||
190 | cut_power = true; | ||
191 | } | ||
192 | |||
193 | if (state < device->power.state && state != ACPI_STATE_D0 | ||
194 | && device->power.state >= ACPI_STATE_D3_HOT) { | ||
195 | dev_warn(&device->dev, | ||
196 | "Cannot transition to non-D0 state from D3\n"); | ||
197 | return -ENODEV; | ||
198 | } | ||
199 | |||
200 | /* | 195 | /* |
201 | * Transition Power | 196 | * Transition Power |
202 | * ---------------- | 197 | * ---------------- |
203 | * In accordance with the ACPI specification first apply power (via | 198 | * In accordance with ACPI 6, _PSx is executed before manipulating power |
204 | * power resources) and then evaluate _PSx. | 199 | * resources, unless the target state is D0, in which case _PS0 is |
200 | * supposed to be executed after turning the power resources on. | ||
205 | */ | 201 | */ |
206 | if (device->power.flags.power_resources) { | 202 | if (state > ACPI_STATE_D0) { |
207 | result = acpi_power_transition(device, state); | 203 | /* |
204 | * According to ACPI 6, devices cannot go from lower-power | ||
205 | * (deeper) states to higher-power (shallower) states. | ||
206 | */ | ||
207 | if (state < device->power.state) { | ||
208 | dev_warn(&device->dev, "Cannot transition from %s to %s\n", | ||
209 | acpi_power_state_string(device->power.state), | ||
210 | acpi_power_state_string(state)); | ||
211 | return -ENODEV; | ||
212 | } | ||
213 | |||
214 | result = acpi_dev_pm_explicit_set(device, state); | ||
208 | if (result) | 215 | if (result) |
209 | goto end; | 216 | goto end; |
210 | } | ||
211 | result = acpi_dev_pm_explicit_set(device, state); | ||
212 | if (result) | ||
213 | goto end; | ||
214 | 217 | ||
215 | if (cut_power) { | 218 | if (device->power.flags.power_resources) |
216 | device->power.state = state; | 219 | result = acpi_power_transition(device, target_state); |
217 | state = ACPI_STATE_D3_COLD; | 220 | } else { |
218 | result = acpi_power_transition(device, state); | 221 | if (device->power.flags.power_resources) { |
222 | result = acpi_power_transition(device, ACPI_STATE_D0); | ||
223 | if (result) | ||
224 | goto end; | ||
225 | } | ||
226 | result = acpi_dev_pm_explicit_set(device, ACPI_STATE_D0); | ||
219 | } | 227 | } |
220 | 228 | ||
221 | end: | 229 | end: |
@@ -223,7 +231,7 @@ int acpi_device_set_power(struct acpi_device *device, int state) | |||
223 | dev_warn(&device->dev, "Failed to change power state to %s\n", | 231 | dev_warn(&device->dev, "Failed to change power state to %s\n", |
224 | acpi_power_state_string(state)); | 232 | acpi_power_state_string(state)); |
225 | } else { | 233 | } else { |
226 | device->power.state = state; | 234 | device->power.state = target_state; |
227 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, | 235 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, |
228 | "Device [%s] transitioned to %s\n", | 236 | "Device [%s] transitioned to %s\n", |
229 | device->pnp.bus_id, | 237 | device->pnp.bus_id, |
@@ -264,13 +272,24 @@ int acpi_bus_init_power(struct acpi_device *device) | |||
264 | return result; | 272 | return result; |
265 | 273 | ||
266 | if (state < ACPI_STATE_D3_COLD && device->power.flags.power_resources) { | 274 | if (state < ACPI_STATE_D3_COLD && device->power.flags.power_resources) { |
275 | /* Reference count the power resources. */ | ||
267 | result = acpi_power_on_resources(device, state); | 276 | result = acpi_power_on_resources(device, state); |
268 | if (result) | 277 | if (result) |
269 | return result; | 278 | return result; |
270 | 279 | ||
271 | result = acpi_dev_pm_explicit_set(device, state); | 280 | if (state == ACPI_STATE_D0) { |
272 | if (result) | 281 | /* |
273 | return result; | 282 | * If _PSC is not present and the state inferred from |
283 | * power resources appears to be D0, it still may be | ||
284 | * necessary to execute _PS0 at this point, because | ||
285 | * another device using the same power resources may | ||
286 | * have been put into D0 previously and that's why we | ||
287 | * see D0 here. | ||
288 | */ | ||
289 | result = acpi_dev_pm_explicit_set(device, state); | ||
290 | if (result) | ||
291 | return result; | ||
292 | } | ||
274 | } else if (state == ACPI_STATE_UNKNOWN) { | 293 | } else if (state == ACPI_STATE_UNKNOWN) { |
275 | /* | 294 | /* |
276 | * No power resources and missing _PSC? Cross fingers and make | 295 | * No power resources and missing _PSC? Cross fingers and make |
@@ -603,12 +622,12 @@ int acpi_pm_device_sleep_state(struct device *dev, int *d_min_p, int d_max_in) | |||
603 | if (d_max_in < ACPI_STATE_D0 || d_max_in > ACPI_STATE_D3_COLD) | 622 | if (d_max_in < ACPI_STATE_D0 || d_max_in > ACPI_STATE_D3_COLD) |
604 | return -EINVAL; | 623 | return -EINVAL; |
605 | 624 | ||
606 | if (d_max_in > ACPI_STATE_D3_HOT) { | 625 | if (d_max_in > ACPI_STATE_D2) { |
607 | enum pm_qos_flags_status stat; | 626 | enum pm_qos_flags_status stat; |
608 | 627 | ||
609 | stat = dev_pm_qos_flags(dev, PM_QOS_FLAG_NO_POWER_OFF); | 628 | stat = dev_pm_qos_flags(dev, PM_QOS_FLAG_NO_POWER_OFF); |
610 | if (stat == PM_QOS_FLAGS_ALL) | 629 | if (stat == PM_QOS_FLAGS_ALL) |
611 | d_max_in = ACPI_STATE_D3_HOT; | 630 | d_max_in = ACPI_STATE_D2; |
612 | } | 631 | } |
613 | 632 | ||
614 | adev = ACPI_COMPANION(dev); | 633 | adev = ACPI_COMPANION(dev); |
@@ -953,6 +972,7 @@ EXPORT_SYMBOL_GPL(acpi_subsys_prepare); | |||
953 | */ | 972 | */ |
954 | void acpi_subsys_complete(struct device *dev) | 973 | void acpi_subsys_complete(struct device *dev) |
955 | { | 974 | { |
975 | pm_generic_complete(dev); | ||
956 | /* | 976 | /* |
957 | * If the device had been runtime-suspended before the system went into | 977 | * If the device had been runtime-suspended before the system went into |
958 | * the sleep state it is going out of and it has never been resumed till | 978 | * the sleep state it is going out of and it has never been resumed till |