• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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