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
421	/* Check if the _DSM is present and as expected. */
422	obj = acpi_evaluate_dsm_typed(handle, dsm_guid, rev, 0, NULL, ACPI_TYPE_BUFFER);
423	if (!obj || obj->buffer.length == 0 || obj->buffer.length > sizeof(u32)) {
424		acpi_handle_debug(handle,
425				"_DSM UUID %s rev %d function 0 evaluation failed\n", uuid, rev);
426		goto out;
427	}
428
429	ret = *(int *)obj->buffer.pointer;
430	acpi_handle_debug(handle, "_DSM UUID %s rev %d function mask: 0x%x\n", uuid, rev, ret);
431
432out:
433	ACPI_FREE(obj);
434	return ret;
435}
436
437struct amd_lps0_hid_device_data {
438	const bool check_off_by_one;
439};
440
441static const struct amd_lps0_hid_device_data amd_picasso = {
442	.check_off_by_one = true,
443};
444
445static const struct amd_lps0_hid_device_data amd_cezanne = {
446	.check_off_by_one = false,
447};
448
449static const struct acpi_device_id amd_hid_ids[] = {
450	{"AMD0004",	(kernel_ulong_t)&amd_picasso,	},
451	{"AMD0005",	(kernel_ulong_t)&amd_picasso,	},
452	{"AMDI0005",	(kernel_ulong_t)&amd_picasso,	},
453	{"AMDI0006",	(kernel_ulong_t)&amd_cezanne,	},
454	{}
455};
456
457static int lps0_device_attach(struct acpi_device *adev,
458			      const struct acpi_device_id *not_used)
459{
460	if (lps0_device_handle)
461		return 0;
462
463	lps0_dsm_func_mask_microsoft = validate_dsm(adev->handle,
464						    ACPI_LPS0_DSM_UUID_MICROSOFT, 0,
465						    &lps0_dsm_guid_microsoft);
466	if (acpi_s2idle_vendor_amd()) {
467		static const struct acpi_device_id *dev_id;
468		const struct amd_lps0_hid_device_data *data;
469
470		for (dev_id = &amd_hid_ids[0]; dev_id->id[0]; dev_id++)
471			if (acpi_dev_hid_uid_match(adev, dev_id->id, NULL))
472				break;
473		if (dev_id->id[0])
474			data = (const struct amd_lps0_hid_device_data *) dev_id->driver_data;
475		else
476			data = &amd_cezanne;
477		lps0_dsm_func_mask = validate_dsm(adev->handle,
478					ACPI_LPS0_DSM_UUID_AMD, rev_id, &lps0_dsm_guid);
479		if (lps0_dsm_func_mask > 0x3 && data->check_off_by_one) {
480			lps0_dsm_func_mask = (lps0_dsm_func_mask << 1) | 0x1;
481			acpi_handle_debug(adev->handle, "_DSM UUID %s: Adjusted function mask: 0x%x\n",
482					  ACPI_LPS0_DSM_UUID_AMD, lps0_dsm_func_mask);
483		} else if (lps0_dsm_func_mask_microsoft > 0 && rev_id) {
484			lps0_dsm_func_mask_microsoft = -EINVAL;
485			acpi_handle_debug(adev->handle, "_DSM Using AMD method\n");
486		}
487	} else {
488		rev_id = 1;
489		lps0_dsm_func_mask = validate_dsm(adev->handle,
490					ACPI_LPS0_DSM_UUID, rev_id, &lps0_dsm_guid);
491		if (lps0_dsm_func_mask > 0 && lps0_dsm_func_mask_microsoft > 0) {
492			unsigned int func_mask;
493
494			/*
495			 * Log a message if the _DSM function sets for two
496			 * different UUIDs overlap.
497			 */
498			func_mask = lps0_dsm_func_mask & lps0_dsm_func_mask_microsoft;
499			if (func_mask)
500				acpi_handle_info(adev->handle,
501						 "Duplicate LPS0 _DSM functions (mask: 0x%x)\n",
502						 func_mask);
503		}
504	}
505
506	if (lps0_dsm_func_mask < 0 && lps0_dsm_func_mask_microsoft < 0)
507		return 0; //function evaluation failed
508
509	lps0_device_handle = adev->handle;
510
511	if (acpi_s2idle_vendor_amd())
512		lpi_device_get_constraints_amd();
513	else
514		lpi_device_get_constraints();
515
516	/*
517	 * Use suspend-to-idle by default if ACPI_FADT_LOW_POWER_S0 is set in
518	 * the FADT and the default suspend mode was not set from the command
519	 * line.
520	 */
521	if ((acpi_gbl_FADT.flags & ACPI_FADT_LOW_POWER_S0) &&
522	    mem_sleep_default > PM_SUSPEND_MEM && !acpi_sleep_default_s3) {
523		mem_sleep_current = PM_SUSPEND_TO_IDLE;
524		pr_info("Low-power S0 idle used by default for system suspend\n");
525	}
526
527	/*
528	 * Some LPS0 systems, like ASUS Zenbook UX430UNR/i7-8550U, require the
529	 * EC GPE to be enabled while suspended for certain wakeup devices to
530	 * work, so mark it as wakeup-capable.
531	 */
532	acpi_ec_mark_gpe_for_wake();
533
534	return 0;
535}
536
537static struct acpi_scan_handler lps0_handler = {
538	.ids = lps0_device_ids,
539	.attach = lps0_device_attach,
540};
541
542int acpi_s2idle_prepare_late(void)
543{
544	struct acpi_s2idle_dev_ops *handler;
545
546	if (!lps0_device_handle || sleep_no_lps0)
547		return 0;
548
549	if (pm_debug_messages_on)
550		lpi_check_constraints();
551
552	/* Screen off */
553	if (lps0_dsm_func_mask > 0)
554		acpi_sleep_run_lps0_dsm(acpi_s2idle_vendor_amd() ?
555					ACPI_LPS0_SCREEN_OFF_AMD :
556					ACPI_LPS0_SCREEN_OFF,
557					lps0_dsm_func_mask, lps0_dsm_guid);
558
559	if (lps0_dsm_func_mask_microsoft > 0)
560		acpi_sleep_run_lps0_dsm(ACPI_LPS0_SCREEN_OFF,
561				lps0_dsm_func_mask_microsoft, lps0_dsm_guid_microsoft);
562
563	/* LPS0 entry */
564	if (lps0_dsm_func_mask > 0 && acpi_s2idle_vendor_amd())
565		acpi_sleep_run_lps0_dsm(ACPI_LPS0_ENTRY_AMD,
566					lps0_dsm_func_mask, lps0_dsm_guid);
567
568	if (lps0_dsm_func_mask_microsoft > 0) {
569		/* Modern Standby entry */
570		acpi_sleep_run_lps0_dsm(ACPI_LPS0_MS_ENTRY,
571				lps0_dsm_func_mask_microsoft, lps0_dsm_guid_microsoft);
572		acpi_sleep_run_lps0_dsm(ACPI_LPS0_ENTRY,
573				lps0_dsm_func_mask_microsoft, lps0_dsm_guid_microsoft);
574	}
575
576	if (lps0_dsm_func_mask > 0 && !acpi_s2idle_vendor_amd())
577		acpi_sleep_run_lps0_dsm(ACPI_LPS0_ENTRY,
578					lps0_dsm_func_mask, lps0_dsm_guid);
579
580	list_for_each_entry(handler, &lps0_s2idle_devops_head, list_node) {
581		if (handler->prepare)
582			handler->prepare();
583	}
584
585	return 0;
586}
587
588void acpi_s2idle_check(void)
589{
590	struct acpi_s2idle_dev_ops *handler;
591
592	if (!lps0_device_handle || sleep_no_lps0)
593		return;
594
595	list_for_each_entry(handler, &lps0_s2idle_devops_head, list_node) {
596		if (handler->check)
597			handler->check();
598	}
599}
600
601void acpi_s2idle_restore_early(void)
602{
603	struct acpi_s2idle_dev_ops *handler;
604
605	if (!lps0_device_handle || sleep_no_lps0)
606		return;
607
608	list_for_each_entry(handler, &lps0_s2idle_devops_head, list_node)
609		if (handler->restore)
610			handler->restore();
611
612	/* LPS0 exit */
613	if (lps0_dsm_func_mask > 0)
614		acpi_sleep_run_lps0_dsm(acpi_s2idle_vendor_amd() ?
615					ACPI_LPS0_EXIT_AMD :
616					ACPI_LPS0_EXIT,
617					lps0_dsm_func_mask, lps0_dsm_guid);
618
619	if (lps0_dsm_func_mask_microsoft > 0) {
620		acpi_sleep_run_lps0_dsm(ACPI_LPS0_EXIT,
621				lps0_dsm_func_mask_microsoft, lps0_dsm_guid_microsoft);
622		/* Modern Standby exit */
623		acpi_sleep_run_lps0_dsm(ACPI_LPS0_MS_EXIT,
624				lps0_dsm_func_mask_microsoft, lps0_dsm_guid_microsoft);
625	}
626
627	/* Screen on */
628	if (lps0_dsm_func_mask_microsoft > 0)
629		acpi_sleep_run_lps0_dsm(ACPI_LPS0_SCREEN_ON,
630				lps0_dsm_func_mask_microsoft, lps0_dsm_guid_microsoft);
631	if (lps0_dsm_func_mask > 0)
632		acpi_sleep_run_lps0_dsm(acpi_s2idle_vendor_amd() ?
633					ACPI_LPS0_SCREEN_ON_AMD :
634					ACPI_LPS0_SCREEN_ON,
635					lps0_dsm_func_mask, lps0_dsm_guid);
636}
637
638static const struct platform_s2idle_ops acpi_s2idle_ops_lps0 = {
639	.begin = acpi_s2idle_begin,
640	.prepare = acpi_s2idle_prepare,
641	.prepare_late = acpi_s2idle_prepare_late,
642	.check = acpi_s2idle_check,
643	.wake = acpi_s2idle_wake,
644	.restore_early = acpi_s2idle_restore_early,
645	.restore = acpi_s2idle_restore,
646	.end = acpi_s2idle_end,
647};
648
649void __init acpi_s2idle_setup(void)
650{
651	acpi_scan_add_handler(&lps0_handler);
652	s2idle_set_ops(&acpi_s2idle_ops_lps0);
653}
654
655int acpi_register_lps0_dev(struct acpi_s2idle_dev_ops *arg)
656{
657	unsigned int sleep_flags;
658
659	if (!lps0_device_handle || sleep_no_lps0)
660		return -ENODEV;
661
662	sleep_flags = lock_system_sleep();
663	list_add(&arg->list_node, &lps0_s2idle_devops_head);
664	unlock_system_sleep(sleep_flags);
665
666	return 0;
667}
668EXPORT_SYMBOL_GPL(acpi_register_lps0_dev);
669
670void acpi_unregister_lps0_dev(struct acpi_s2idle_dev_ops *arg)
671{
672	unsigned int sleep_flags;
673
674	if (!lps0_device_handle || sleep_no_lps0)
675		return;
676
677	sleep_flags = lock_system_sleep();
678	list_del(&arg->list_node);
679	unlock_system_sleep(sleep_flags);
680}
681EXPORT_SYMBOL_GPL(acpi_unregister_lps0_dev);
682
683#endif /* CONFIG_SUSPEND */
684