.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
---|
1 | 2 | /* |
---|
2 | 3 | * drivers/acpi/device_pm.c - ACPI device power management routines. |
---|
3 | 4 | * |
---|
.. | .. |
---|
5 | 6 | * Author: Rafael J. Wysocki <rafael.j.wysocki@intel.com> |
---|
6 | 7 | * |
---|
7 | 8 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
---|
8 | | - * |
---|
9 | | - * This program is free software; you can redistribute it and/or modify |
---|
10 | | - * it under the terms of the GNU General Public License version 2 as published |
---|
11 | | - * by the Free Software Foundation. |
---|
12 | | - * |
---|
13 | | - * This program is distributed in the hope that it will be useful, but |
---|
14 | | - * WITHOUT ANY WARRANTY; without even the implied warranty of |
---|
15 | | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
---|
16 | | - * General Public License for more details. |
---|
17 | 9 | * |
---|
18 | 10 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
---|
19 | 11 | */ |
---|
.. | .. |
---|
26 | 18 | #include <linux/pm_runtime.h> |
---|
27 | 19 | #include <linux/suspend.h> |
---|
28 | 20 | |
---|
| 21 | +#include "fan.h" |
---|
29 | 22 | #include "internal.h" |
---|
30 | 23 | |
---|
31 | 24 | #define _COMPONENT ACPI_POWER_COMPONENT |
---|
.. | .. |
---|
53 | 46 | } |
---|
54 | 47 | } |
---|
55 | 48 | |
---|
| 49 | +static int acpi_dev_pm_explicit_get(struct acpi_device *device, int *state) |
---|
| 50 | +{ |
---|
| 51 | + unsigned long long psc; |
---|
| 52 | + acpi_status status; |
---|
| 53 | + |
---|
| 54 | + status = acpi_evaluate_integer(device->handle, "_PSC", NULL, &psc); |
---|
| 55 | + if (ACPI_FAILURE(status)) |
---|
| 56 | + return -ENODEV; |
---|
| 57 | + |
---|
| 58 | + *state = psc; |
---|
| 59 | + return 0; |
---|
| 60 | +} |
---|
| 61 | + |
---|
56 | 62 | /** |
---|
57 | 63 | * acpi_device_get_power - Get power state of an ACPI device. |
---|
58 | 64 | * @device: Device to get the power state of. |
---|
.. | .. |
---|
61 | 67 | * This function does not update the device's power.state field, but it may |
---|
62 | 68 | * update its parent's power.state field (when the parent's power state is |
---|
63 | 69 | * unknown and the device's power state turns out to be D0). |
---|
| 70 | + * |
---|
| 71 | + * Also, it does not update power resource reference counters to ensure that |
---|
| 72 | + * the power state returned by it will be persistent and it may return a power |
---|
| 73 | + * state shallower than previously set by acpi_device_set_power() for @device |
---|
| 74 | + * (if that power state depends on any power resources). |
---|
64 | 75 | */ |
---|
65 | 76 | int acpi_device_get_power(struct acpi_device *device, int *state) |
---|
66 | 77 | { |
---|
67 | 78 | int result = ACPI_STATE_UNKNOWN; |
---|
| 79 | + int error; |
---|
68 | 80 | |
---|
69 | 81 | if (!device || !state) |
---|
70 | 82 | return -EINVAL; |
---|
.. | .. |
---|
81 | 93 | * if available. |
---|
82 | 94 | */ |
---|
83 | 95 | if (device->power.flags.power_resources) { |
---|
84 | | - int error = acpi_power_get_inferred_state(device, &result); |
---|
| 96 | + error = acpi_power_get_inferred_state(device, &result); |
---|
85 | 97 | if (error) |
---|
86 | 98 | return error; |
---|
87 | 99 | } |
---|
88 | 100 | if (device->power.flags.explicit_get) { |
---|
89 | | - acpi_handle handle = device->handle; |
---|
90 | | - unsigned long long psc; |
---|
91 | | - acpi_status status; |
---|
| 101 | + int psc; |
---|
92 | 102 | |
---|
93 | | - status = acpi_evaluate_integer(handle, "_PSC", NULL, &psc); |
---|
94 | | - if (ACPI_FAILURE(status)) |
---|
95 | | - return -ENODEV; |
---|
| 103 | + error = acpi_dev_pm_explicit_get(device, &psc); |
---|
| 104 | + if (error) |
---|
| 105 | + return error; |
---|
96 | 106 | |
---|
97 | 107 | /* |
---|
98 | 108 | * The power resources settings may indicate a power state |
---|
.. | .. |
---|
157 | 167 | || (state < ACPI_STATE_D0) || (state > ACPI_STATE_D3_COLD)) |
---|
158 | 168 | return -EINVAL; |
---|
159 | 169 | |
---|
| 170 | + acpi_handle_debug(device->handle, "Power state change: %s -> %s\n", |
---|
| 171 | + acpi_power_state_string(device->power.state), |
---|
| 172 | + acpi_power_state_string(state)); |
---|
| 173 | + |
---|
160 | 174 | /* Make sure this is a valid target state */ |
---|
161 | 175 | |
---|
162 | | - if (state == device->power.state) { |
---|
| 176 | + /* There is a special case for D0 addressed below. */ |
---|
| 177 | + if (state > ACPI_STATE_D0 && state == device->power.state) { |
---|
163 | 178 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device [%s] already in %s\n", |
---|
164 | 179 | device->pnp.bus_id, |
---|
165 | 180 | acpi_power_state_string(state))); |
---|
.. | .. |
---|
209 | 224 | return -ENODEV; |
---|
210 | 225 | } |
---|
211 | 226 | |
---|
212 | | - result = acpi_dev_pm_explicit_set(device, state); |
---|
213 | | - if (result) |
---|
214 | | - goto end; |
---|
| 227 | + /* |
---|
| 228 | + * If the device goes from D3hot to D3cold, _PS3 has been |
---|
| 229 | + * evaluated for it already, so skip it in that case. |
---|
| 230 | + */ |
---|
| 231 | + if (device->power.state < ACPI_STATE_D3_HOT) { |
---|
| 232 | + result = acpi_dev_pm_explicit_set(device, state); |
---|
| 233 | + if (result) |
---|
| 234 | + goto end; |
---|
| 235 | + } |
---|
215 | 236 | |
---|
216 | 237 | if (device->power.flags.power_resources) |
---|
217 | 238 | result = acpi_power_transition(device, target_state); |
---|
218 | 239 | } else { |
---|
| 240 | + int cur_state = device->power.state; |
---|
| 241 | + |
---|
219 | 242 | if (device->power.flags.power_resources) { |
---|
220 | 243 | result = acpi_power_transition(device, ACPI_STATE_D0); |
---|
221 | 244 | if (result) |
---|
222 | 245 | goto end; |
---|
223 | 246 | } |
---|
| 247 | + |
---|
| 248 | + if (cur_state == ACPI_STATE_D0) { |
---|
| 249 | + int psc; |
---|
| 250 | + |
---|
| 251 | + /* Nothing to do here if _PSC is not present. */ |
---|
| 252 | + if (!device->power.flags.explicit_get) |
---|
| 253 | + return 0; |
---|
| 254 | + |
---|
| 255 | + /* |
---|
| 256 | + * The power state of the device was set to D0 last |
---|
| 257 | + * time, but that might have happened before a |
---|
| 258 | + * system-wide transition involving the platform |
---|
| 259 | + * firmware, so it may be necessary to evaluate _PS0 |
---|
| 260 | + * for the device here. However, use extra care here |
---|
| 261 | + * and evaluate _PSC to check the device's current power |
---|
| 262 | + * state, and only invoke _PS0 if the evaluation of _PSC |
---|
| 263 | + * is successful and it returns a power state different |
---|
| 264 | + * from D0. |
---|
| 265 | + */ |
---|
| 266 | + result = acpi_dev_pm_explicit_get(device, &psc); |
---|
| 267 | + if (result || psc == ACPI_STATE_D0) |
---|
| 268 | + return 0; |
---|
| 269 | + } |
---|
| 270 | + |
---|
224 | 271 | result = acpi_dev_pm_explicit_set(device, ACPI_STATE_D0); |
---|
225 | 272 | } |
---|
226 | 273 | |
---|
.. | .. |
---|
413 | 460 | if (adev->wakeup.flags.notifier_present) { |
---|
414 | 461 | pm_wakeup_ws_event(adev->wakeup.ws, 0, acpi_s2idle_wakeup()); |
---|
415 | 462 | if (adev->wakeup.context.func) { |
---|
416 | | - acpi_handle_debug(handle, "Running %pF for %s\n", |
---|
| 463 | + acpi_handle_debug(handle, "Running %pS for %s\n", |
---|
417 | 464 | adev->wakeup.context.func, |
---|
418 | 465 | dev_name(adev->wakeup.context.dev)); |
---|
419 | 466 | adev->wakeup.context.func(&adev->wakeup.context); |
---|
.. | .. |
---|
729 | 776 | goto out; |
---|
730 | 777 | } |
---|
731 | 778 | |
---|
| 779 | + acpi_handle_debug(adev->handle, "GPE%2X enabled for wakeup\n", |
---|
| 780 | + (unsigned int)wakeup->gpe_number); |
---|
| 781 | + |
---|
732 | 782 | inc: |
---|
733 | 783 | wakeup->enable_count++; |
---|
734 | 784 | |
---|
.. | .. |
---|
1018 | 1068 | { |
---|
1019 | 1069 | int ret; |
---|
1020 | 1070 | |
---|
1021 | | - if (dev_pm_smart_suspend_and_suspended(dev)) |
---|
| 1071 | + if (dev_pm_skip_suspend(dev)) |
---|
1022 | 1072 | return 0; |
---|
1023 | 1073 | |
---|
1024 | 1074 | ret = pm_generic_suspend_late(dev); |
---|
.. | .. |
---|
1034 | 1084 | { |
---|
1035 | 1085 | int ret; |
---|
1036 | 1086 | |
---|
1037 | | - if (dev_pm_smart_suspend_and_suspended(dev)) { |
---|
1038 | | - dev->power.may_skip_resume = true; |
---|
| 1087 | + if (dev_pm_skip_suspend(dev)) |
---|
1039 | 1088 | return 0; |
---|
1040 | | - } |
---|
1041 | 1089 | |
---|
1042 | 1090 | ret = pm_generic_suspend_noirq(dev); |
---|
1043 | 1091 | if (ret) |
---|
.. | .. |
---|
1050 | 1098 | * acpi_subsys_complete() to take care of fixing up the device's state |
---|
1051 | 1099 | * anyway, if need be. |
---|
1052 | 1100 | */ |
---|
1053 | | - dev->power.may_skip_resume = device_may_wakeup(dev) || |
---|
1054 | | - !device_can_wakeup(dev); |
---|
| 1101 | + if (device_can_wakeup(dev) && !device_may_wakeup(dev)) |
---|
| 1102 | + dev->power.may_skip_resume = false; |
---|
1055 | 1103 | |
---|
1056 | 1104 | return 0; |
---|
1057 | 1105 | } |
---|
.. | .. |
---|
1063 | 1111 | */ |
---|
1064 | 1112 | static int acpi_subsys_resume_noirq(struct device *dev) |
---|
1065 | 1113 | { |
---|
1066 | | - if (dev_pm_may_skip_resume(dev)) |
---|
| 1114 | + if (dev_pm_skip_resume(dev)) |
---|
1067 | 1115 | return 0; |
---|
1068 | | - |
---|
1069 | | - /* |
---|
1070 | | - * Devices with DPM_FLAG_SMART_SUSPEND may be left in runtime suspend |
---|
1071 | | - * during system suspend, so update their runtime PM status to "active" |
---|
1072 | | - * as they will be put into D0 going forward. |
---|
1073 | | - */ |
---|
1074 | | - if (dev_pm_smart_suspend_and_suspended(dev)) |
---|
1075 | | - pm_runtime_set_active(dev); |
---|
1076 | 1116 | |
---|
1077 | 1117 | return pm_generic_resume_noirq(dev); |
---|
1078 | 1118 | } |
---|
.. | .. |
---|
1087 | 1127 | */ |
---|
1088 | 1128 | static int acpi_subsys_resume_early(struct device *dev) |
---|
1089 | 1129 | { |
---|
1090 | | - int ret = acpi_dev_resume(dev); |
---|
| 1130 | + int ret; |
---|
| 1131 | + |
---|
| 1132 | + if (dev_pm_skip_resume(dev)) |
---|
| 1133 | + return 0; |
---|
| 1134 | + |
---|
| 1135 | + ret = acpi_dev_resume(dev); |
---|
1091 | 1136 | return ret ? ret : pm_generic_resume_early(dev); |
---|
1092 | 1137 | } |
---|
1093 | 1138 | |
---|
.. | .. |
---|
1152 | 1197 | { |
---|
1153 | 1198 | int ret; |
---|
1154 | 1199 | |
---|
1155 | | - if (dev_pm_smart_suspend_and_suspended(dev)) |
---|
| 1200 | + if (dev_pm_skip_suspend(dev)) |
---|
1156 | 1201 | return 0; |
---|
1157 | 1202 | |
---|
1158 | 1203 | ret = pm_generic_poweroff_late(dev); |
---|
.. | .. |
---|
1168 | 1213 | */ |
---|
1169 | 1214 | static int acpi_subsys_poweroff_noirq(struct device *dev) |
---|
1170 | 1215 | { |
---|
1171 | | - if (dev_pm_smart_suspend_and_suspended(dev)) |
---|
| 1216 | + if (dev_pm_skip_suspend(dev)) |
---|
1172 | 1217 | return 0; |
---|
1173 | 1218 | |
---|
1174 | 1219 | return pm_generic_poweroff_noirq(dev); |
---|
.. | .. |
---|
1254 | 1299 | * with the generic ACPI PM domain. |
---|
1255 | 1300 | */ |
---|
1256 | 1301 | static const struct acpi_device_id special_pm_ids[] = { |
---|
1257 | | - {"PNP0C0B", }, /* Generic ACPI fan */ |
---|
1258 | | - {"INT3404", }, /* Fan */ |
---|
| 1302 | + ACPI_FAN_DEVICE_IDS, |
---|
1259 | 1303 | {} |
---|
1260 | 1304 | }; |
---|
1261 | 1305 | struct acpi_device *adev = ACPI_COMPANION(dev); |
---|