xref: /kernel/linux/linux-6.6/drivers/acpi/x86/s2idle.c (revision 62306a36)
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/dmi.h>
21#include <linux/suspend.h>
22
23#include "../sleep.h"
24
25#ifdef CONFIG_SUSPEND
26
27static bool sleep_no_lps0 __read_mostly;
28module_param(sleep_no_lps0, bool, 0644);
29MODULE_PARM_DESC(sleep_no_lps0, "Do not use the special LPS0 device interface");
30
31static const struct acpi_device_id lps0_device_ids[] = {
32	{"PNP0D80", },
33	{"", },
34};
35
36/* Microsoft platform agnostic UUID */
37#define ACPI_LPS0_DSM_UUID_MICROSOFT      "11e00d56-ce64-47ce-837b-1f898f9aa461"
38
39#define ACPI_LPS0_DSM_UUID	"c4eb40a0-6cd2-11e2-bcfd-0800200c9a66"
40
41#define ACPI_LPS0_GET_DEVICE_CONSTRAINTS	1
42#define ACPI_LPS0_SCREEN_OFF	3
43#define ACPI_LPS0_SCREEN_ON	4
44#define ACPI_LPS0_ENTRY		5
45#define ACPI_LPS0_EXIT		6
46#define ACPI_LPS0_MS_ENTRY      7
47#define ACPI_LPS0_MS_EXIT       8
48
49/* AMD */
50#define ACPI_LPS0_DSM_UUID_AMD      "e3f32452-febc-43ce-9039-932122d37721"
51#define ACPI_LPS0_ENTRY_AMD         2
52#define ACPI_LPS0_EXIT_AMD          3
53#define ACPI_LPS0_SCREEN_OFF_AMD    4
54#define ACPI_LPS0_SCREEN_ON_AMD     5
55
56static acpi_handle lps0_device_handle;
57static guid_t lps0_dsm_guid;
58static int lps0_dsm_func_mask;
59
60static guid_t lps0_dsm_guid_microsoft;
61static int lps0_dsm_func_mask_microsoft;
62static int lps0_dsm_state;
63
64/* Device constraint entry structure */
65struct lpi_device_info {
66	char *name;
67	int enabled;
68	union acpi_object *package;
69};
70
71/* Constraint package structure */
72struct lpi_device_constraint {
73	int uid;
74	int min_dstate;
75	int function_states;
76};
77
78struct lpi_constraints {
79	acpi_handle handle;
80	int min_dstate;
81};
82
83/* AMD Constraint package structure */
84struct lpi_device_constraint_amd {
85	char *name;
86	int enabled;
87	int function_states;
88	int min_dstate;
89};
90
91static LIST_HEAD(lps0_s2idle_devops_head);
92
93static struct lpi_constraints *lpi_constraints_table;
94static int lpi_constraints_table_size;
95static int rev_id;
96
97#define for_each_lpi_constraint(entry)						\
98	for (int i = 0;								\
99	     entry = &lpi_constraints_table[i], i < lpi_constraints_table_size;	\
100	     i++)
101
102static void lpi_device_get_constraints_amd(void)
103{
104	union acpi_object *out_obj;
105	int i, j, k;
106
107	out_obj = acpi_evaluate_dsm_typed(lps0_device_handle, &lps0_dsm_guid,
108					  rev_id, ACPI_LPS0_GET_DEVICE_CONSTRAINTS,
109					  NULL, ACPI_TYPE_PACKAGE);
110
111	acpi_handle_debug(lps0_device_handle, "_DSM function 1 eval %s\n",
112			  out_obj ? "successful" : "failed");
113
114	if (!out_obj)
115		return;
116
117	for (i = 0; i < out_obj->package.count; i++) {
118		union acpi_object *package = &out_obj->package.elements[i];
119
120		if (package->type == ACPI_TYPE_PACKAGE) {
121			if (lpi_constraints_table) {
122				acpi_handle_err(lps0_device_handle,
123						"Duplicate constraints list\n");
124				goto free_acpi_buffer;
125			}
126
127			lpi_constraints_table = kcalloc(package->package.count,
128							sizeof(*lpi_constraints_table),
129							GFP_KERNEL);
130
131			if (!lpi_constraints_table)
132				goto free_acpi_buffer;
133
134			acpi_handle_debug(lps0_device_handle,
135					  "LPI: constraints list begin:\n");
136
137			for (j = 0; j < package->package.count; j++) {
138				union acpi_object *info_obj = &package->package.elements[j];
139				struct lpi_device_constraint_amd dev_info = {};
140				struct lpi_constraints *list;
141				acpi_status status;
142
143				list = &lpi_constraints_table[lpi_constraints_table_size];
144
145				for (k = 0; k < info_obj->package.count; k++) {
146					union acpi_object *obj = &info_obj->package.elements[k];
147
148					switch (k) {
149					case 0:
150						dev_info.enabled = obj->integer.value;
151						break;
152					case 1:
153						dev_info.name = obj->string.pointer;
154						break;
155					case 2:
156						dev_info.function_states = obj->integer.value;
157						break;
158					case 3:
159						dev_info.min_dstate = obj->integer.value;
160						break;
161					}
162				}
163
164				acpi_handle_debug(lps0_device_handle,
165						  "Name:%s, Enabled: %d, States: %d, MinDstate: %d\n",
166						  dev_info.name,
167						  dev_info.enabled,
168						  dev_info.function_states,
169						  dev_info.min_dstate);
170
171				if (!dev_info.enabled || !dev_info.name ||
172				    !dev_info.min_dstate)
173					continue;
174
175				status = acpi_get_handle(NULL, dev_info.name, &list->handle);
176				if (ACPI_FAILURE(status))
177					continue;
178
179				list->min_dstate = dev_info.min_dstate;
180
181				lpi_constraints_table_size++;
182			}
183		}
184	}
185
186	acpi_handle_debug(lps0_device_handle, "LPI: constraints list end\n");
187
188free_acpi_buffer:
189	ACPI_FREE(out_obj);
190}
191
192static void lpi_device_get_constraints(void)
193{
194	union acpi_object *out_obj;
195	int i;
196
197	out_obj = acpi_evaluate_dsm_typed(lps0_device_handle, &lps0_dsm_guid,
198					  1, ACPI_LPS0_GET_DEVICE_CONSTRAINTS,
199					  NULL, ACPI_TYPE_PACKAGE);
200
201	acpi_handle_debug(lps0_device_handle, "_DSM function 1 eval %s\n",
202			  out_obj ? "successful" : "failed");
203
204	if (!out_obj)
205		return;
206
207	lpi_constraints_table = kcalloc(out_obj->package.count,
208					sizeof(*lpi_constraints_table),
209					GFP_KERNEL);
210	if (!lpi_constraints_table)
211		goto free_acpi_buffer;
212
213	acpi_handle_debug(lps0_device_handle, "LPI: constraints list begin:\n");
214
215	for (i = 0; i < out_obj->package.count; i++) {
216		struct lpi_constraints *constraint;
217		acpi_status status;
218		union acpi_object *package = &out_obj->package.elements[i];
219		struct lpi_device_info info = { };
220		int package_count = 0, j;
221
222		if (!package)
223			continue;
224
225		for (j = 0; j < package->package.count; j++) {
226			union acpi_object *element =
227					&(package->package.elements[j]);
228
229			switch (element->type) {
230			case ACPI_TYPE_INTEGER:
231				info.enabled = element->integer.value;
232				break;
233			case ACPI_TYPE_STRING:
234				info.name = element->string.pointer;
235				break;
236			case ACPI_TYPE_PACKAGE:
237				package_count = element->package.count;
238				info.package = element->package.elements;
239				break;
240			}
241		}
242
243		if (!info.enabled || !info.package || !info.name)
244			continue;
245
246		constraint = &lpi_constraints_table[lpi_constraints_table_size];
247
248		status = acpi_get_handle(NULL, info.name, &constraint->handle);
249		if (ACPI_FAILURE(status))
250			continue;
251
252		acpi_handle_debug(lps0_device_handle,
253				  "index:%d Name:%s\n", i, info.name);
254
255		constraint->min_dstate = -1;
256
257		for (j = 0; j < package_count; j++) {
258			union acpi_object *info_obj = &info.package[j];
259			union acpi_object *cnstr_pkg;
260			union acpi_object *obj;
261			struct lpi_device_constraint dev_info;
262
263			switch (info_obj->type) {
264			case ACPI_TYPE_INTEGER:
265				/* version */
266				break;
267			case ACPI_TYPE_PACKAGE:
268				if (info_obj->package.count < 2)
269					break;
270
271				cnstr_pkg = info_obj->package.elements;
272				obj = &cnstr_pkg[0];
273				dev_info.uid = obj->integer.value;
274				obj = &cnstr_pkg[1];
275				dev_info.min_dstate = obj->integer.value;
276
277				acpi_handle_debug(lps0_device_handle,
278					"uid:%d min_dstate:%s\n",
279					dev_info.uid,
280					acpi_power_state_string(dev_info.min_dstate));
281
282				constraint->min_dstate = dev_info.min_dstate;
283				break;
284			}
285		}
286
287		if (constraint->min_dstate < 0) {
288			acpi_handle_debug(lps0_device_handle,
289					  "Incomplete constraint defined\n");
290			continue;
291		}
292
293		lpi_constraints_table_size++;
294	}
295
296	acpi_handle_debug(lps0_device_handle, "LPI: constraints list end\n");
297
298free_acpi_buffer:
299	ACPI_FREE(out_obj);
300}
301
302/**
303 * acpi_get_lps0_constraint - Get the LPS0 constraint for a device.
304 * @adev: Device to get the constraint for.
305 *
306 * The LPS0 constraint is the shallowest (minimum) power state in which the
307 * device can be so as to allow the platform as a whole to achieve additional
308 * energy conservation by utilizing a system-wide low-power state.
309 *
310 * Returns:
311 *  - ACPI power state value of the constraint for @adev on success.
312 *  - Otherwise, ACPI_STATE_UNKNOWN.
313 */
314int acpi_get_lps0_constraint(struct acpi_device *adev)
315{
316	struct lpi_constraints *entry;
317
318	for_each_lpi_constraint(entry) {
319		if (adev->handle == entry->handle)
320			return entry->min_dstate;
321	}
322
323	return ACPI_STATE_UNKNOWN;
324}
325
326static void lpi_check_constraints(void)
327{
328	struct lpi_constraints *entry;
329
330	for_each_lpi_constraint(entry) {
331		struct acpi_device *adev = acpi_fetch_acpi_dev(entry->handle);
332
333		if (!adev)
334			continue;
335
336		acpi_handle_debug(entry->handle,
337			"LPI: required min power state:%s current power state:%s\n",
338			acpi_power_state_string(entry->min_dstate),
339			acpi_power_state_string(adev->power.state));
340
341		if (!adev->flags.power_manageable) {
342			acpi_handle_info(entry->handle, "LPI: Device not power manageable\n");
343			entry->handle = NULL;
344			continue;
345		}
346
347		if (adev->power.state < entry->min_dstate)
348			acpi_handle_info(entry->handle,
349				"LPI: Constraint not met; min power state:%s current power state:%s\n",
350				acpi_power_state_string(entry->min_dstate),
351				acpi_power_state_string(adev->power.state));
352	}
353}
354
355static bool acpi_s2idle_vendor_amd(void)
356{
357	return boot_cpu_data.x86_vendor == X86_VENDOR_AMD;
358}
359
360static const char *acpi_sleep_dsm_state_to_str(unsigned int state)
361{
362	if (lps0_dsm_func_mask_microsoft || !acpi_s2idle_vendor_amd()) {
363		switch (state) {
364		case ACPI_LPS0_SCREEN_OFF:
365			return "screen off";
366		case ACPI_LPS0_SCREEN_ON:
367			return "screen on";
368		case ACPI_LPS0_ENTRY:
369			return "lps0 entry";
370		case ACPI_LPS0_EXIT:
371			return "lps0 exit";
372		case ACPI_LPS0_MS_ENTRY:
373			return "lps0 ms entry";
374		case ACPI_LPS0_MS_EXIT:
375			return "lps0 ms exit";
376		}
377	} else {
378		switch (state) {
379		case ACPI_LPS0_SCREEN_ON_AMD:
380			return "screen on";
381		case ACPI_LPS0_SCREEN_OFF_AMD:
382			return "screen off";
383		case ACPI_LPS0_ENTRY_AMD:
384			return "lps0 entry";
385		case ACPI_LPS0_EXIT_AMD:
386			return "lps0 exit";
387		}
388	}
389
390	return "unknown";
391}
392
393static void acpi_sleep_run_lps0_dsm(unsigned int func, unsigned int func_mask, guid_t dsm_guid)
394{
395	union acpi_object *out_obj;
396
397	if (!(func_mask & (1 << func)))
398		return;
399
400	out_obj = acpi_evaluate_dsm(lps0_device_handle, &dsm_guid,
401					rev_id, func, NULL);
402	ACPI_FREE(out_obj);
403
404	lps0_dsm_state = func;
405	if (pm_debug_messages_on) {
406		acpi_handle_info(lps0_device_handle,
407				"%s transitioned to state %s\n",
408				 out_obj ? "Successfully" : "Failed to",
409				 acpi_sleep_dsm_state_to_str(lps0_dsm_state));
410	}
411}
412
413
414static int validate_dsm(acpi_handle handle, const char *uuid, int rev, guid_t *dsm_guid)
415{
416	union acpi_object *obj;
417	int ret = -EINVAL;
418
419	guid_parse(uuid, dsm_guid);
420	obj = acpi_evaluate_dsm(handle, dsm_guid, rev, 0, NULL);
421
422	/* Check if the _DSM is present and as expected. */
423	if (!obj || obj->type != ACPI_TYPE_BUFFER || obj->buffer.length == 0 ||
424	    obj->buffer.length > sizeof(u32)) {
425		acpi_handle_debug(handle,
426				"_DSM UUID %s rev %d function 0 evaluation failed\n", uuid, rev);
427		goto out;
428	}
429
430	ret = *(int *)obj->buffer.pointer;
431	acpi_handle_debug(handle, "_DSM UUID %s rev %d function mask: 0x%x\n", uuid, rev, ret);
432
433out:
434	ACPI_FREE(obj);
435	return ret;
436}
437
438struct amd_lps0_hid_device_data {
439	const bool check_off_by_one;
440};
441
442static const struct amd_lps0_hid_device_data amd_picasso = {
443	.check_off_by_one = true,
444};
445
446static const struct amd_lps0_hid_device_data amd_cezanne = {
447	.check_off_by_one = false,
448};
449
450static const struct acpi_device_id amd_hid_ids[] = {
451	{"AMD0004",	(kernel_ulong_t)&amd_picasso,	},
452	{"AMD0005",	(kernel_ulong_t)&amd_picasso,	},
453	{"AMDI0005",	(kernel_ulong_t)&amd_picasso,	},
454	{"AMDI0006",	(kernel_ulong_t)&amd_cezanne,	},
455	{}
456};
457
458static int lps0_device_attach(struct acpi_device *adev,
459			      const struct acpi_device_id *not_used)
460{
461	if (lps0_device_handle)
462		return 0;
463
464	lps0_dsm_func_mask_microsoft = validate_dsm(adev->handle,
465						    ACPI_LPS0_DSM_UUID_MICROSOFT, 0,
466						    &lps0_dsm_guid_microsoft);
467	if (acpi_s2idle_vendor_amd()) {
468		static const struct acpi_device_id *dev_id;
469		const struct amd_lps0_hid_device_data *data;
470
471		for (dev_id = &amd_hid_ids[0]; dev_id->id[0]; dev_id++)
472			if (acpi_dev_hid_uid_match(adev, dev_id->id, NULL))
473				break;
474		if (dev_id->id[0])
475			data = (const struct amd_lps0_hid_device_data *) dev_id->driver_data;
476		else
477			data = &amd_cezanne;
478		lps0_dsm_func_mask = validate_dsm(adev->handle,
479					ACPI_LPS0_DSM_UUID_AMD, rev_id, &lps0_dsm_guid);
480		if (lps0_dsm_func_mask > 0x3 && data->check_off_by_one) {
481			lps0_dsm_func_mask = (lps0_dsm_func_mask << 1) | 0x1;
482			acpi_handle_debug(adev->handle, "_DSM UUID %s: Adjusted function mask: 0x%x\n",
483					  ACPI_LPS0_DSM_UUID_AMD, lps0_dsm_func_mask);
484		} else if (lps0_dsm_func_mask_microsoft > 0 && rev_id) {
485			lps0_dsm_func_mask_microsoft = -EINVAL;
486			acpi_handle_debug(adev->handle, "_DSM Using AMD method\n");
487		}
488	} else {
489		rev_id = 1;
490		lps0_dsm_func_mask = validate_dsm(adev->handle,
491					ACPI_LPS0_DSM_UUID, rev_id, &lps0_dsm_guid);
492		lps0_dsm_func_mask_microsoft = -EINVAL;
493	}
494
495	if (lps0_dsm_func_mask < 0 && lps0_dsm_func_mask_microsoft < 0)
496		return 0; //function evaluation failed
497
498	lps0_device_handle = adev->handle;
499
500	if (acpi_s2idle_vendor_amd())
501		lpi_device_get_constraints_amd();
502	else
503		lpi_device_get_constraints();
504
505	/*
506	 * Use suspend-to-idle by default if ACPI_FADT_LOW_POWER_S0 is set in
507	 * the FADT and the default suspend mode was not set from the command
508	 * line.
509	 */
510	if ((acpi_gbl_FADT.flags & ACPI_FADT_LOW_POWER_S0) &&
511	    mem_sleep_default > PM_SUSPEND_MEM && !acpi_sleep_default_s3) {
512		mem_sleep_current = PM_SUSPEND_TO_IDLE;
513		pr_info("Low-power S0 idle used by default for system suspend\n");
514	}
515
516	/*
517	 * Some LPS0 systems, like ASUS Zenbook UX430UNR/i7-8550U, require the
518	 * EC GPE to be enabled while suspended for certain wakeup devices to
519	 * work, so mark it as wakeup-capable.
520	 */
521	acpi_ec_mark_gpe_for_wake();
522
523	return 0;
524}
525
526static struct acpi_scan_handler lps0_handler = {
527	.ids = lps0_device_ids,
528	.attach = lps0_device_attach,
529};
530
531int acpi_s2idle_prepare_late(void)
532{
533	struct acpi_s2idle_dev_ops *handler;
534
535	if (!lps0_device_handle || sleep_no_lps0)
536		return 0;
537
538	if (pm_debug_messages_on)
539		lpi_check_constraints();
540
541	/* Screen off */
542	if (lps0_dsm_func_mask > 0)
543		acpi_sleep_run_lps0_dsm(acpi_s2idle_vendor_amd() ?
544					ACPI_LPS0_SCREEN_OFF_AMD :
545					ACPI_LPS0_SCREEN_OFF,
546					lps0_dsm_func_mask, lps0_dsm_guid);
547
548	if (lps0_dsm_func_mask_microsoft > 0)
549		acpi_sleep_run_lps0_dsm(ACPI_LPS0_SCREEN_OFF,
550				lps0_dsm_func_mask_microsoft, lps0_dsm_guid_microsoft);
551
552	/* LPS0 entry */
553	if (lps0_dsm_func_mask > 0)
554		acpi_sleep_run_lps0_dsm(acpi_s2idle_vendor_amd() ?
555					ACPI_LPS0_ENTRY_AMD :
556					ACPI_LPS0_ENTRY,
557					lps0_dsm_func_mask, lps0_dsm_guid);
558	if (lps0_dsm_func_mask_microsoft > 0) {
559		/* modern standby entry */
560		acpi_sleep_run_lps0_dsm(ACPI_LPS0_MS_ENTRY,
561				lps0_dsm_func_mask_microsoft, lps0_dsm_guid_microsoft);
562		acpi_sleep_run_lps0_dsm(ACPI_LPS0_ENTRY,
563				lps0_dsm_func_mask_microsoft, lps0_dsm_guid_microsoft);
564	}
565
566	list_for_each_entry(handler, &lps0_s2idle_devops_head, list_node) {
567		if (handler->prepare)
568			handler->prepare();
569	}
570
571	return 0;
572}
573
574void acpi_s2idle_check(void)
575{
576	struct acpi_s2idle_dev_ops *handler;
577
578	if (!lps0_device_handle || sleep_no_lps0)
579		return;
580
581	list_for_each_entry(handler, &lps0_s2idle_devops_head, list_node) {
582		if (handler->check)
583			handler->check();
584	}
585}
586
587void acpi_s2idle_restore_early(void)
588{
589	struct acpi_s2idle_dev_ops *handler;
590
591	if (!lps0_device_handle || sleep_no_lps0)
592		return;
593
594	list_for_each_entry(handler, &lps0_s2idle_devops_head, list_node)
595		if (handler->restore)
596			handler->restore();
597
598	/* LPS0 exit */
599	if (lps0_dsm_func_mask > 0)
600		acpi_sleep_run_lps0_dsm(acpi_s2idle_vendor_amd() ?
601					ACPI_LPS0_EXIT_AMD :
602					ACPI_LPS0_EXIT,
603					lps0_dsm_func_mask, lps0_dsm_guid);
604	if (lps0_dsm_func_mask_microsoft > 0)
605		acpi_sleep_run_lps0_dsm(ACPI_LPS0_EXIT,
606				lps0_dsm_func_mask_microsoft, lps0_dsm_guid_microsoft);
607
608	/* Modern standby exit */
609	if (lps0_dsm_func_mask_microsoft > 0)
610		acpi_sleep_run_lps0_dsm(ACPI_LPS0_MS_EXIT,
611				lps0_dsm_func_mask_microsoft, lps0_dsm_guid_microsoft);
612
613	/* Screen on */
614	if (lps0_dsm_func_mask_microsoft > 0)
615		acpi_sleep_run_lps0_dsm(ACPI_LPS0_SCREEN_ON,
616				lps0_dsm_func_mask_microsoft, lps0_dsm_guid_microsoft);
617	if (lps0_dsm_func_mask > 0)
618		acpi_sleep_run_lps0_dsm(acpi_s2idle_vendor_amd() ?
619					ACPI_LPS0_SCREEN_ON_AMD :
620					ACPI_LPS0_SCREEN_ON,
621					lps0_dsm_func_mask, lps0_dsm_guid);
622}
623
624static const struct platform_s2idle_ops acpi_s2idle_ops_lps0 = {
625	.begin = acpi_s2idle_begin,
626	.prepare = acpi_s2idle_prepare,
627	.prepare_late = acpi_s2idle_prepare_late,
628	.check = acpi_s2idle_check,
629	.wake = acpi_s2idle_wake,
630	.restore_early = acpi_s2idle_restore_early,
631	.restore = acpi_s2idle_restore,
632	.end = acpi_s2idle_end,
633};
634
635void __init acpi_s2idle_setup(void)
636{
637	acpi_scan_add_handler(&lps0_handler);
638	s2idle_set_ops(&acpi_s2idle_ops_lps0);
639}
640
641int acpi_register_lps0_dev(struct acpi_s2idle_dev_ops *arg)
642{
643	unsigned int sleep_flags;
644
645	if (!lps0_device_handle || sleep_no_lps0)
646		return -ENODEV;
647
648	sleep_flags = lock_system_sleep();
649	list_add(&arg->list_node, &lps0_s2idle_devops_head);
650	unlock_system_sleep(sleep_flags);
651
652	return 0;
653}
654EXPORT_SYMBOL_GPL(acpi_register_lps0_dev);
655
656void acpi_unregister_lps0_dev(struct acpi_s2idle_dev_ops *arg)
657{
658	unsigned int sleep_flags;
659
660	if (!lps0_device_handle || sleep_no_lps0)
661		return;
662
663	sleep_flags = lock_system_sleep();
664	list_del(&arg->list_node);
665	unlock_system_sleep(sleep_flags);
666}
667EXPORT_SYMBOL_GPL(acpi_unregister_lps0_dev);
668
669#endif /* CONFIG_SUSPEND */
670