1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * Architecture-specific ACPI-based support for suspend-to-idle.
4 *
5 * Author: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
6 * Author: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>
7 * Author: Shyam Sundar S K <Shyam-sundar.S-k@amd.com>
8 *
9 * On platforms supporting the Low Power S0 Idle interface there is an ACPI
10 * device object with the PNP0D80 compatible device ID (System Power Management
11 * Controller) and a specific _DSM method under it. That method, if present,
12 * can be used to indicate to the platform that the OS is transitioning into a
13 * low-power state in which certain types of activity are not desirable or that
14 * it is leaving such a state, which allows the platform to adjust its operation
15 * mode accordingly.
16 */
17
18 #include <linux/acpi.h>
19 #include <linux/device.h>
20 #include <linux/suspend.h>
21
22 #include "../sleep.h"
23
24 #ifdef CONFIG_SUSPEND
25
26 static bool sleep_no_lps0 __read_mostly;
27 module_param(sleep_no_lps0, bool, 0644);
28 MODULE_PARM_DESC(sleep_no_lps0, "Do not use the special LPS0 device interface");
29
30 static const struct acpi_device_id lps0_device_ids[] = {
31 {"PNP0D80", },
32 {"", },
33 };
34
35 /* Microsoft platform agnostic UUID */
36 #define ACPI_LPS0_DSM_UUID_MICROSOFT "11e00d56-ce64-47ce-837b-1f898f9aa461"
37
38 #define ACPI_LPS0_DSM_UUID "c4eb40a0-6cd2-11e2-bcfd-0800200c9a66"
39
40 #define ACPI_LPS0_GET_DEVICE_CONSTRAINTS 1
41 #define ACPI_LPS0_SCREEN_OFF 3
42 #define ACPI_LPS0_SCREEN_ON 4
43 #define ACPI_LPS0_ENTRY 5
44 #define ACPI_LPS0_EXIT 6
45 #define ACPI_LPS0_MS_ENTRY 7
46 #define ACPI_LPS0_MS_EXIT 8
47
48 /* AMD */
49 #define ACPI_LPS0_DSM_UUID_AMD "e3f32452-febc-43ce-9039-932122d37721"
50 #define ACPI_LPS0_ENTRY_AMD 2
51 #define ACPI_LPS0_EXIT_AMD 3
52 #define ACPI_LPS0_SCREEN_OFF_AMD 4
53 #define ACPI_LPS0_SCREEN_ON_AMD 5
54
55 static acpi_handle lps0_device_handle;
56 static guid_t lps0_dsm_guid;
57 static int lps0_dsm_func_mask;
58
59 static guid_t lps0_dsm_guid_microsoft;
60 static int lps0_dsm_func_mask_microsoft;
61
62 /* Device constraint entry structure */
63 struct lpi_device_info {
64 char *name;
65 int enabled;
66 union acpi_object *package;
67 };
68
69 /* Constraint package structure */
70 struct lpi_device_constraint {
71 int uid;
72 int min_dstate;
73 int function_states;
74 };
75
76 struct lpi_constraints {
77 acpi_handle handle;
78 int min_dstate;
79 };
80
81 /* AMD Constraint package structure */
82 struct lpi_device_constraint_amd {
83 char *name;
84 int enabled;
85 int function_states;
86 int min_dstate;
87 };
88
89 static LIST_HEAD(lps0_s2idle_devops_head);
90
91 static struct lpi_constraints *lpi_constraints_table;
92 static int lpi_constraints_table_size;
93 static int rev_id;
94
lpi_device_get_constraints_amd(void)95 static void lpi_device_get_constraints_amd(void)
96 {
97 union acpi_object *out_obj;
98 int i, j, k;
99
100 out_obj = acpi_evaluate_dsm_typed(lps0_device_handle, &lps0_dsm_guid,
101 rev_id, ACPI_LPS0_GET_DEVICE_CONSTRAINTS,
102 NULL, ACPI_TYPE_PACKAGE);
103
104 acpi_handle_debug(lps0_device_handle, "_DSM function 1 eval %s\n",
105 out_obj ? "successful" : "failed");
106
107 if (!out_obj)
108 return;
109
110 for (i = 0; i < out_obj->package.count; i++) {
111 union acpi_object *package = &out_obj->package.elements[i];
112
113 if (package->type == ACPI_TYPE_PACKAGE) {
114 if (lpi_constraints_table) {
115 acpi_handle_err(lps0_device_handle,
116 "Duplicate constraints list\n");
117 goto free_acpi_buffer;
118 }
119
120 lpi_constraints_table = kcalloc(package->package.count,
121 sizeof(*lpi_constraints_table),
122 GFP_KERNEL);
123
124 if (!lpi_constraints_table)
125 goto free_acpi_buffer;
126
127 acpi_handle_debug(lps0_device_handle,
128 "LPI: constraints list begin:\n");
129
130 for (j = 0; j < package->package.count; j++) {
131 union acpi_object *info_obj = &package->package.elements[j];
132 struct lpi_device_constraint_amd dev_info = {};
133 struct lpi_constraints *list;
134 acpi_status status;
135
136 list = &lpi_constraints_table[lpi_constraints_table_size];
137
138 for (k = 0; k < info_obj->package.count; k++) {
139 union acpi_object *obj = &info_obj->package.elements[k];
140
141 switch (k) {
142 case 0:
143 dev_info.enabled = obj->integer.value;
144 break;
145 case 1:
146 dev_info.name = obj->string.pointer;
147 break;
148 case 2:
149 dev_info.function_states = obj->integer.value;
150 break;
151 case 3:
152 dev_info.min_dstate = obj->integer.value;
153 break;
154 }
155 }
156
157 if (!dev_info.enabled || !dev_info.name ||
158 !dev_info.min_dstate)
159 continue;
160
161 status = acpi_get_handle(NULL, dev_info.name, &list->handle);
162 if (ACPI_FAILURE(status))
163 continue;
164
165 acpi_handle_debug(lps0_device_handle,
166 "Name:%s\n", dev_info.name);
167
168 list->min_dstate = dev_info.min_dstate;
169
170 lpi_constraints_table_size++;
171 }
172 }
173 }
174
175 acpi_handle_debug(lps0_device_handle, "LPI: constraints list end\n");
176
177 free_acpi_buffer:
178 ACPI_FREE(out_obj);
179 }
180
lpi_device_get_constraints(void)181 static void lpi_device_get_constraints(void)
182 {
183 union acpi_object *out_obj;
184 int i;
185
186 out_obj = acpi_evaluate_dsm_typed(lps0_device_handle, &lps0_dsm_guid,
187 1, ACPI_LPS0_GET_DEVICE_CONSTRAINTS,
188 NULL, ACPI_TYPE_PACKAGE);
189
190 acpi_handle_debug(lps0_device_handle, "_DSM function 1 eval %s\n",
191 out_obj ? "successful" : "failed");
192
193 if (!out_obj)
194 return;
195
196 lpi_constraints_table = kcalloc(out_obj->package.count,
197 sizeof(*lpi_constraints_table),
198 GFP_KERNEL);
199 if (!lpi_constraints_table)
200 goto free_acpi_buffer;
201
202 acpi_handle_debug(lps0_device_handle, "LPI: constraints list begin:\n");
203
204 for (i = 0; i < out_obj->package.count; i++) {
205 struct lpi_constraints *constraint;
206 acpi_status status;
207 union acpi_object *package = &out_obj->package.elements[i];
208 struct lpi_device_info info = { };
209 int package_count = 0, j;
210
211 if (!package)
212 continue;
213
214 for (j = 0; j < package->package.count; j++) {
215 union acpi_object *element =
216 &(package->package.elements[j]);
217
218 switch (element->type) {
219 case ACPI_TYPE_INTEGER:
220 info.enabled = element->integer.value;
221 break;
222 case ACPI_TYPE_STRING:
223 info.name = element->string.pointer;
224 break;
225 case ACPI_TYPE_PACKAGE:
226 package_count = element->package.count;
227 info.package = element->package.elements;
228 break;
229 }
230 }
231
232 if (!info.enabled || !info.package || !info.name)
233 continue;
234
235 constraint = &lpi_constraints_table[lpi_constraints_table_size];
236
237 status = acpi_get_handle(NULL, info.name, &constraint->handle);
238 if (ACPI_FAILURE(status))
239 continue;
240
241 acpi_handle_debug(lps0_device_handle,
242 "index:%d Name:%s\n", i, info.name);
243
244 constraint->min_dstate = -1;
245
246 for (j = 0; j < package_count; j++) {
247 union acpi_object *info_obj = &info.package[j];
248 union acpi_object *cnstr_pkg;
249 union acpi_object *obj;
250 struct lpi_device_constraint dev_info;
251
252 switch (info_obj->type) {
253 case ACPI_TYPE_INTEGER:
254 /* version */
255 break;
256 case ACPI_TYPE_PACKAGE:
257 if (info_obj->package.count < 2)
258 break;
259
260 cnstr_pkg = info_obj->package.elements;
261 obj = &cnstr_pkg[0];
262 dev_info.uid = obj->integer.value;
263 obj = &cnstr_pkg[1];
264 dev_info.min_dstate = obj->integer.value;
265
266 acpi_handle_debug(lps0_device_handle,
267 "uid:%d min_dstate:%s\n",
268 dev_info.uid,
269 acpi_power_state_string(dev_info.min_dstate));
270
271 constraint->min_dstate = dev_info.min_dstate;
272 break;
273 }
274 }
275
276 if (constraint->min_dstate < 0) {
277 acpi_handle_debug(lps0_device_handle,
278 "Incomplete constraint defined\n");
279 continue;
280 }
281
282 lpi_constraints_table_size++;
283 }
284
285 acpi_handle_debug(lps0_device_handle, "LPI: constraints list end\n");
286
287 free_acpi_buffer:
288 ACPI_FREE(out_obj);
289 }
290
lpi_check_constraints(void)291 static void lpi_check_constraints(void)
292 {
293 int i;
294
295 for (i = 0; i < lpi_constraints_table_size; ++i) {
296 acpi_handle handle = lpi_constraints_table[i].handle;
297 struct acpi_device *adev;
298
299 if (!handle || acpi_bus_get_device(handle, &adev))
300 continue;
301
302 acpi_handle_debug(handle,
303 "LPI: required min power state:%s current power state:%s\n",
304 acpi_power_state_string(lpi_constraints_table[i].min_dstate),
305 acpi_power_state_string(adev->power.state));
306
307 if (!adev->flags.power_manageable) {
308 acpi_handle_info(handle, "LPI: Device not power manageable\n");
309 lpi_constraints_table[i].handle = NULL;
310 continue;
311 }
312
313 if (adev->power.state < lpi_constraints_table[i].min_dstate)
314 acpi_handle_info(handle,
315 "LPI: Constraint not met; min power state:%s current power state:%s\n",
316 acpi_power_state_string(lpi_constraints_table[i].min_dstate),
317 acpi_power_state_string(adev->power.state));
318 }
319 }
320
acpi_sleep_run_lps0_dsm(unsigned int func,unsigned int func_mask,guid_t dsm_guid)321 static void acpi_sleep_run_lps0_dsm(unsigned int func, unsigned int func_mask, guid_t dsm_guid)
322 {
323 union acpi_object *out_obj;
324
325 if (!(func_mask & (1 << func)))
326 return;
327
328 out_obj = acpi_evaluate_dsm(lps0_device_handle, &dsm_guid,
329 rev_id, func, NULL);
330 ACPI_FREE(out_obj);
331
332 acpi_handle_debug(lps0_device_handle, "_DSM function %u evaluation %s\n",
333 func, out_obj ? "successful" : "failed");
334 }
335
acpi_s2idle_vendor_amd(void)336 static bool acpi_s2idle_vendor_amd(void)
337 {
338 return boot_cpu_data.x86_vendor == X86_VENDOR_AMD;
339 }
340
validate_dsm(acpi_handle handle,const char * uuid,int rev,guid_t * dsm_guid)341 static int validate_dsm(acpi_handle handle, const char *uuid, int rev, guid_t *dsm_guid)
342 {
343 union acpi_object *obj;
344 int ret = -EINVAL;
345
346 guid_parse(uuid, dsm_guid);
347 obj = acpi_evaluate_dsm(handle, dsm_guid, rev, 0, NULL);
348
349 /* Check if the _DSM is present and as expected. */
350 if (!obj || obj->type != ACPI_TYPE_BUFFER || obj->buffer.length == 0 ||
351 obj->buffer.length > sizeof(u32)) {
352 acpi_handle_debug(handle,
353 "_DSM UUID %s rev %d function 0 evaluation failed\n", uuid, rev);
354 goto out;
355 }
356
357 ret = *(int *)obj->buffer.pointer;
358 acpi_handle_debug(handle, "_DSM UUID %s rev %d function mask: 0x%x\n", uuid, rev, ret);
359
360 out:
361 ACPI_FREE(obj);
362 return ret;
363 }
364
lps0_device_attach(struct acpi_device * adev,const struct acpi_device_id * not_used)365 static int lps0_device_attach(struct acpi_device *adev,
366 const struct acpi_device_id *not_used)
367 {
368 if (lps0_device_handle)
369 return 0;
370
371 if (!(acpi_gbl_FADT.flags & ACPI_FADT_LOW_POWER_S0))
372 return 0;
373
374 if (acpi_s2idle_vendor_amd()) {
375 /* AMD0004, AMD0005, AMDI0005:
376 * - Should use rev_id 0x0
377 * - function mask > 0x3: Should use AMD method, but has off by one bug
378 * - function mask = 0x3: Should use Microsoft method
379 * AMDI0006:
380 * - should use rev_id 0x0
381 * - function mask = 0x3: Should use Microsoft method
382 */
383 const char *hid = acpi_device_hid(adev);
384 rev_id = 0;
385 lps0_dsm_func_mask = validate_dsm(adev->handle,
386 ACPI_LPS0_DSM_UUID_AMD, rev_id, &lps0_dsm_guid);
387 lps0_dsm_func_mask_microsoft = validate_dsm(adev->handle,
388 ACPI_LPS0_DSM_UUID_MICROSOFT, rev_id,
389 &lps0_dsm_guid_microsoft);
390 if (lps0_dsm_func_mask > 0x3 && (!strcmp(hid, "AMD0004") ||
391 !strcmp(hid, "AMD0005") ||
392 !strcmp(hid, "AMDI0005"))) {
393 lps0_dsm_func_mask = (lps0_dsm_func_mask << 1) | 0x1;
394 acpi_handle_debug(adev->handle, "_DSM UUID %s: Adjusted function mask: 0x%x\n",
395 ACPI_LPS0_DSM_UUID_AMD, lps0_dsm_func_mask);
396 }
397 } else {
398 rev_id = 1;
399 lps0_dsm_func_mask = validate_dsm(adev->handle,
400 ACPI_LPS0_DSM_UUID, rev_id, &lps0_dsm_guid);
401 lps0_dsm_func_mask_microsoft = -EINVAL;
402 }
403
404 if (lps0_dsm_func_mask < 0 && lps0_dsm_func_mask_microsoft < 0)
405 return 0; //function evaluation failed
406
407 lps0_device_handle = adev->handle;
408
409 if (acpi_s2idle_vendor_amd())
410 lpi_device_get_constraints_amd();
411 else
412 lpi_device_get_constraints();
413
414 /*
415 * Use suspend-to-idle by default if the default suspend mode was not
416 * set from the command line.
417 */
418 if (mem_sleep_default > PM_SUSPEND_MEM && !acpi_sleep_default_s3)
419 mem_sleep_current = PM_SUSPEND_TO_IDLE;
420
421 /*
422 * Some LPS0 systems, like ASUS Zenbook UX430UNR/i7-8550U, require the
423 * EC GPE to be enabled while suspended for certain wakeup devices to
424 * work, so mark it as wakeup-capable.
425 */
426 acpi_ec_mark_gpe_for_wake();
427
428 return 0;
429 }
430
431 static struct acpi_scan_handler lps0_handler = {
432 .ids = lps0_device_ids,
433 .attach = lps0_device_attach,
434 };
435
acpi_s2idle_prepare_late(void)436 int acpi_s2idle_prepare_late(void)
437 {
438 struct acpi_s2idle_dev_ops *handler;
439
440 if (!lps0_device_handle || sleep_no_lps0)
441 return 0;
442
443 if (pm_debug_messages_on)
444 lpi_check_constraints();
445
446 /* Screen off */
447 if (lps0_dsm_func_mask > 0)
448 acpi_sleep_run_lps0_dsm(acpi_s2idle_vendor_amd() ?
449 ACPI_LPS0_SCREEN_OFF_AMD :
450 ACPI_LPS0_SCREEN_OFF,
451 lps0_dsm_func_mask, lps0_dsm_guid);
452
453 if (lps0_dsm_func_mask_microsoft > 0)
454 acpi_sleep_run_lps0_dsm(ACPI_LPS0_SCREEN_OFF,
455 lps0_dsm_func_mask_microsoft, lps0_dsm_guid_microsoft);
456
457 /* LPS0 entry */
458 if (lps0_dsm_func_mask > 0)
459 acpi_sleep_run_lps0_dsm(acpi_s2idle_vendor_amd() ?
460 ACPI_LPS0_ENTRY_AMD :
461 ACPI_LPS0_ENTRY,
462 lps0_dsm_func_mask, lps0_dsm_guid);
463 if (lps0_dsm_func_mask_microsoft > 0) {
464 acpi_sleep_run_lps0_dsm(ACPI_LPS0_ENTRY,
465 lps0_dsm_func_mask_microsoft, lps0_dsm_guid_microsoft);
466 /* modern standby entry */
467 acpi_sleep_run_lps0_dsm(ACPI_LPS0_MS_ENTRY,
468 lps0_dsm_func_mask_microsoft, lps0_dsm_guid_microsoft);
469 }
470
471 list_for_each_entry(handler, &lps0_s2idle_devops_head, list_node) {
472 if (handler->prepare)
473 handler->prepare();
474 }
475
476 return 0;
477 }
478
acpi_s2idle_restore_early(void)479 void acpi_s2idle_restore_early(void)
480 {
481 struct acpi_s2idle_dev_ops *handler;
482
483 if (!lps0_device_handle || sleep_no_lps0)
484 return;
485
486 list_for_each_entry(handler, &lps0_s2idle_devops_head, list_node)
487 if (handler->restore)
488 handler->restore();
489
490 /* Modern standby exit */
491 if (lps0_dsm_func_mask_microsoft > 0)
492 acpi_sleep_run_lps0_dsm(ACPI_LPS0_MS_EXIT,
493 lps0_dsm_func_mask_microsoft, lps0_dsm_guid_microsoft);
494
495 /* LPS0 exit */
496 if (lps0_dsm_func_mask > 0)
497 acpi_sleep_run_lps0_dsm(acpi_s2idle_vendor_amd() ?
498 ACPI_LPS0_EXIT_AMD :
499 ACPI_LPS0_EXIT,
500 lps0_dsm_func_mask, lps0_dsm_guid);
501 if (lps0_dsm_func_mask_microsoft > 0)
502 acpi_sleep_run_lps0_dsm(ACPI_LPS0_EXIT,
503 lps0_dsm_func_mask_microsoft, lps0_dsm_guid_microsoft);
504
505 /* Screen on */
506 if (lps0_dsm_func_mask_microsoft > 0)
507 acpi_sleep_run_lps0_dsm(ACPI_LPS0_SCREEN_ON,
508 lps0_dsm_func_mask_microsoft, lps0_dsm_guid_microsoft);
509 if (lps0_dsm_func_mask > 0)
510 acpi_sleep_run_lps0_dsm(acpi_s2idle_vendor_amd() ?
511 ACPI_LPS0_SCREEN_ON_AMD :
512 ACPI_LPS0_SCREEN_ON,
513 lps0_dsm_func_mask, lps0_dsm_guid);
514 }
515
516 static const struct platform_s2idle_ops acpi_s2idle_ops_lps0 = {
517 .begin = acpi_s2idle_begin,
518 .prepare = acpi_s2idle_prepare,
519 .prepare_late = acpi_s2idle_prepare_late,
520 .wake = acpi_s2idle_wake,
521 .restore_early = acpi_s2idle_restore_early,
522 .restore = acpi_s2idle_restore,
523 .end = acpi_s2idle_end,
524 };
525
acpi_s2idle_setup(void)526 void acpi_s2idle_setup(void)
527 {
528 acpi_scan_add_handler(&lps0_handler);
529 s2idle_set_ops(&acpi_s2idle_ops_lps0);
530 }
531
acpi_register_lps0_dev(struct acpi_s2idle_dev_ops * arg)532 int acpi_register_lps0_dev(struct acpi_s2idle_dev_ops *arg)
533 {
534 if (!lps0_device_handle || sleep_no_lps0)
535 return -ENODEV;
536
537 lock_system_sleep();
538 list_add(&arg->list_node, &lps0_s2idle_devops_head);
539 unlock_system_sleep();
540
541 return 0;
542 }
543 EXPORT_SYMBOL_GPL(acpi_register_lps0_dev);
544
acpi_unregister_lps0_dev(struct acpi_s2idle_dev_ops * arg)545 void acpi_unregister_lps0_dev(struct acpi_s2idle_dev_ops *arg)
546 {
547 if (!lps0_device_handle || sleep_no_lps0)
548 return;
549
550 lock_system_sleep();
551 list_del(&arg->list_node);
552 unlock_system_sleep();
553 }
554 EXPORT_SYMBOL_GPL(acpi_unregister_lps0_dev);
555
556 #endif /* CONFIG_SUSPEND */
557