1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21/*
22 * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
23 */
24/*
25 * Copyright (c) 2009, Intel Corporation.
26 * All rights reserved.
27 */
28/*
29 * Solaris x86 ACPI CA services
30 */
31
32#include <sys/file.h>
33#include <sys/errno.h>
34#include <sys/conf.h>
35#include <sys/modctl.h>
36#include <sys/open.h>
37#include <sys/stat.h>
38#include <sys/spl.h>
39#include <sys/ddi.h>
40#include <sys/sunddi.h>
41#include <sys/esunddi.h>
42#include <sys/kstat.h>
43#include <sys/x86_archext.h>
44
45#include <sys/acpi/acpi.h>
46#include <sys/acpica.h>
47#include <sys/archsystm.h>
48
49/*
50 *
51 */
52static	struct modlmisc modlmisc = {
53	&mod_miscops,
54	"ACPI interpreter",
55};
56
57static	struct modlinkage modlinkage = {
58	MODREV_1,		/* MODREV_1 manual */
59	(void *)&modlmisc,	/* module linkage */
60	NULL,			/* list terminator */
61};
62
63/*
64 * Local prototypes
65 */
66
67static void	acpica_init_kstats(void);
68
69/*
70 * Local data
71 */
72
73static kmutex_t	acpica_module_lock;
74static kstat_t	*acpica_ksp;
75
76/*
77 * State of acpica subsystem
78 * After successful initialization, will be ACPICA_INITIALIZED
79 */
80int acpica_init_state = ACPICA_NOT_INITIALIZED;
81
82/*
83 * Following are set by acpica_process_user_options()
84 *
85 * acpica_enable = FALSE prevents initialization of ACPI CA
86 * completely
87 *
88 * acpi_init_level determines level of ACPI CA functionality
89 * enabled in acpica_init()
90 */
91int	acpica_enable;
92UINT32	acpi_init_level;
93
94/*
95 * Non-zero enables lax behavior with respect to some
96 * common ACPI BIOS issues; see ACPI CA documentation
97 * Setting this to zero causes ACPI CA to enforce strict
98 * compliance with ACPI specification
99 */
100int acpica_enable_interpreter_slack = 1;
101
102/*
103 * For non-DEBUG builds, set the ACPI CA debug level to 0
104 * to quiet chatty BIOS output into /var/adm/messages
105 * Field-patchable for diagnostic use.
106 */
107#ifdef  DEBUG
108int acpica_muzzle_debug_output = 0;
109#else
110int acpica_muzzle_debug_output = 1;
111#endif
112
113/*
114 * ACPI DDI hooks
115 */
116static int acpica_ddi_setwake(dev_info_t *dip, int level);
117
118int
119_init(void)
120{
121	int error = EBUSY;
122	int	status;
123	extern int (*acpi_fp_setwake)();
124	extern kmutex_t cpu_map_lock;
125
126	mutex_init(&acpica_module_lock, NULL, MUTEX_DRIVER, NULL);
127	mutex_init(&cpu_map_lock, NULL, MUTEX_SPIN,
128	    (ddi_iblock_cookie_t)ipltospl(DISP_LEVEL));
129
130	if ((error = mod_install(&modlinkage)) != 0) {
131		mutex_destroy(&acpica_module_lock);
132		goto load_error;
133	}
134
135	AcpiGbl_EnableInterpreterSlack = (acpica_enable_interpreter_slack != 0);
136
137	/* global ACPI CA initialization */
138	if (ACPI_FAILURE(status = AcpiInitializeSubsystem()))
139		cmn_err(CE_WARN, "!AcpiInitializeSubsystem failed: %d", status);
140
141	/* initialize table manager */
142	if (ACPI_FAILURE(status = AcpiInitializeTables(NULL, 0, 0)))
143		cmn_err(CE_WARN, "!AcpiInitializeTables failed: %d", status);
144
145	acpi_fp_setwake = acpica_ddi_setwake;
146
147load_error:
148	return (error);
149}
150
151int
152_info(struct modinfo *modinfop)
153{
154	return (mod_info(&modlinkage, modinfop));
155}
156
157int
158_fini(void)
159{
160	/*
161	 * acpica module is never unloaded at run-time; there's always
162	 * a PSM depending on it, at the very least
163	 */
164	return (EBUSY);
165}
166
167/*
168 * Install acpica-provided address-space handlers
169 */
170static int
171acpica_install_handlers()
172{
173	ACPI_STATUS	rv = AE_OK;
174
175	/*
176	 * Install ACPI CA default handlers
177	 */
178	if (AcpiInstallAddressSpaceHandler(ACPI_ROOT_OBJECT,
179	    ACPI_ADR_SPACE_SYSTEM_MEMORY,
180	    ACPI_DEFAULT_HANDLER, NULL, NULL) != AE_OK) {
181		cmn_err(CE_WARN, "!acpica: no default handler for"
182		    " system memory");
183		rv = AE_ERROR;
184	}
185
186	if (AcpiInstallAddressSpaceHandler(ACPI_ROOT_OBJECT,
187	    ACPI_ADR_SPACE_SYSTEM_IO,
188	    ACPI_DEFAULT_HANDLER, NULL, NULL) != AE_OK) {
189		cmn_err(CE_WARN, "!acpica: no default handler for"
190		    " system I/O");
191		rv = AE_ERROR;
192	}
193
194	if (AcpiInstallAddressSpaceHandler(ACPI_ROOT_OBJECT,
195	    ACPI_ADR_SPACE_PCI_CONFIG,
196	    ACPI_DEFAULT_HANDLER, NULL, NULL) != AE_OK) {
197		cmn_err(CE_WARN, "!acpica: no default handler for"
198		    " PCI Config");
199		rv = AE_ERROR;
200	}
201
202
203	return (rv);
204}
205
206/*
207 * Find the BIOS date, and return TRUE if supplied
208 * date is same or later than the BIOS date, or FALSE
209 * if the BIOS date can't be fetched for any reason
210 */
211static int
212acpica_check_bios_date(int yy, int mm, int dd)
213{
214
215	char *datep;
216	int bios_year, bios_month, bios_day;
217
218	/* If firmware has no bios, skip the check */
219	if (ddi_prop_exists(DDI_DEV_T_ANY, ddi_root_node(), DDI_PROP_DONTPASS,
220	    "bios-free"))
221		return (TRUE);
222
223	/*
224	 * PC BIOSes contain a string in the form of
225	 * "mm/dd/yy" at absolute address 0xffff5,
226	 * where mm, dd and yy are all ASCII digits.
227	 * We map the string, pluck out the values,
228	 * and accept all BIOSes from 1 Jan 1999 on
229	 * as valid.
230	 */
231
232	if ((datep = (char *)AcpiOsMapMemory(0xffff5, 8)) == NULL)
233		return (FALSE);
234
235	/* year */
236	bios_year = ((int)(*(datep + 6) - '0') * 10) + (*(datep + 7) - '0');
237	/* month */
238	bios_month = ((int)(*datep - '0') * 10) + (*(datep + 1) - '0');
239	/* day */
240	bios_day = ((int)(*(datep + 3) - '0') * 10) + (*(datep + 4) - '0');
241
242	AcpiOsUnmapMemory((void *) datep, 8);
243
244	if (bios_year < 0 || bios_year > 99 || bios_month < 0 ||
245	    bios_month > 99 || bios_day < 0 || bios_day > 99) {
246		/* non-digit chars in BIOS date */
247		return (FALSE);
248	}
249
250	/*
251	 * Adjust for 2-digit year; note to grand-children:
252	 * need a new scheme before 2080 rolls around
253	 */
254	bios_year += (bios_year >= 80 && bios_year <= 99) ?
255	    1900 : 2000;
256
257	if (bios_year < yy)
258		return (FALSE);
259	else if (bios_year > yy)
260		return (TRUE);
261
262	if (bios_month < mm)
263		return (FALSE);
264	else if (bios_month > mm)
265		return (TRUE);
266
267	if (bios_day < dd)
268		return (FALSE);
269
270	return (TRUE);
271}
272
273/*
274 * Check for Metropolis systems with BIOSes older than 10/12/04
275 * return TRUE if BIOS requires legacy mode, FALSE otherwise
276 */
277static int
278acpica_metro_old_bios()
279{
280	ACPI_TABLE_HEADER *fadt;
281
282	/* get the FADT */
283	if (AcpiGetTable(ACPI_SIG_FADT, 1, (ACPI_TABLE_HEADER **)&fadt) !=
284	    AE_OK)
285		return (FALSE);
286
287	/* compare OEM Table ID to "SUNmetro" - no match, return false */
288	if (strncmp("SUNmetro", fadt->OemTableId, 8))
289		return (FALSE);
290
291	/* On a Metro - return FALSE if later than 10/12/04 */
292	return (!acpica_check_bios_date(2004, 10, 12));
293}
294
295
296/*
297 * Process acpi-user-options property  if present
298 */
299static void
300acpica_process_user_options()
301{
302	static int processed = 0;
303	int acpi_user_options;
304	char *acpi_prop;
305
306	/*
307	 * return if acpi-user-options has already been processed
308	 */
309	if (processed)
310		return;
311	else
312		processed = 1;
313
314	/* converts acpi-user-options from type string to int, if any */
315	if (ddi_prop_lookup_string(DDI_DEV_T_ANY, ddi_root_node(),
316	    DDI_PROP_DONTPASS, "acpi-user-options", &acpi_prop) ==
317	    DDI_PROP_SUCCESS) {
318		long data;
319		int ret;
320		ret = ddi_strtol(acpi_prop, NULL, 0, &data);
321		if (ret == 0) {
322			e_ddi_prop_remove(DDI_DEV_T_NONE, ddi_root_node(),
323			    "acpi-user-options");
324			e_ddi_prop_update_int(DDI_DEV_T_NONE, ddi_root_node(),
325			    "acpi-user-options", data);
326		}
327		ddi_prop_free(acpi_prop);
328	}
329
330	/*
331	 * fetch the optional options property
332	 */
333	acpi_user_options = ddi_prop_get_int(DDI_DEV_T_ANY, ddi_root_node(),
334	    DDI_PROP_DONTPASS, "acpi-user-options", 0);
335
336	/*
337	 * Note that 'off' has precedence over 'on'
338	 * Also note - all cases of ACPI_OUSER_MASK
339	 * provided here, no default: case is present
340	 */
341	switch (acpi_user_options & ACPI_OUSER_MASK) {
342	case ACPI_OUSER_DFLT:
343		acpica_enable = acpica_check_bios_date(1999, 1, 1);
344		break;
345	case ACPI_OUSER_ON:
346		acpica_enable = TRUE;
347		break;
348	case ACPI_OUSER_OFF:
349	case ACPI_OUSER_OFF | ACPI_OUSER_ON:
350		acpica_enable = FALSE;
351		break;
352	}
353
354	acpi_init_level = ACPI_FULL_INITIALIZATION;
355
356	/*
357	 * special test here; may be generalized in the
358	 * future - test for a machines that are known to
359	 * work only in legacy mode, and set OUSER_LEGACY if
360	 * we're on one
361	 */
362	if (acpica_metro_old_bios())
363		acpi_user_options |= ACPI_OUSER_LEGACY;
364
365	/*
366	 * If legacy mode is specified, set initialization
367	 * options to avoid entering ACPI mode and hooking SCI
368	 * - basically try to act like legacy acpi_intp
369	 */
370	if ((acpi_user_options & ACPI_OUSER_LEGACY) != 0)
371		acpi_init_level |= (ACPI_NO_ACPI_ENABLE | ACPI_NO_HANDLER_INIT);
372
373	/*
374	 * modify default ACPI CA debug output level for non-DEBUG builds
375	 * (to avoid BIOS debug chatter in /var/adm/messages)
376	 */
377	if (acpica_muzzle_debug_output)
378		AcpiDbgLevel = 0;
379}
380
381/*
382 * Initialize the CA subsystem if it hasn't been done already
383 */
384int
385acpica_init()
386{
387	extern void acpica_find_ioapics(void);
388	ACPI_STATUS status;
389
390	/*
391	 * Make sure user options are processed,
392	 * then fail to initialize if ACPI CA has been
393	 * disabled
394	 */
395	acpica_process_user_options();
396	if (!acpica_enable)
397		return (AE_ERROR);
398
399	mutex_enter(&acpica_module_lock);
400	if (acpica_init_state == ACPICA_INITIALIZED) {
401		mutex_exit(&acpica_module_lock);
402		return (AE_OK);
403	}
404
405	if (ACPI_FAILURE(status = AcpiLoadTables()))
406		goto error;
407
408	if (ACPI_FAILURE(status = acpica_install_handlers()))
409		goto error;
410
411	/*
412	 * Create ACPI-to-devinfo mapping now so _INI and _STA
413	 * methods can access PCI config space when needed
414	 */
415	scan_d2a_map();
416
417	if (ACPI_FAILURE(status = AcpiEnableSubsystem(acpi_init_level)))
418		goto error;
419
420	/* do after AcpiEnableSubsystem() so GPEs are initialized */
421	acpica_ec_init();	/* initialize EC if present */
422
423	if (ACPI_FAILURE(status = AcpiInitializeObjects(0)))
424		goto error;
425
426	acpica_init_state = ACPICA_INITIALIZED;
427
428	/*
429	 * If we are running on the Xen hypervisor as dom0 we need to
430	 * find the ioapics so we can prevent ACPI from trying to
431	 * access them.
432	 */
433	if (get_hwenv() == HW_XEN_PV && is_controldom())
434		acpica_find_ioapics();
435	acpica_init_kstats();
436error:
437	if (acpica_init_state != ACPICA_INITIALIZED) {
438		cmn_err(CE_NOTE, "!failed to initialize ACPI services");
439	}
440
441	/*
442	 * Set acpi-status to 13 if acpica has been initialized successfully.
443	 * This indicates that acpica is up and running.  This variable name
444	 * and value were chosen in order to remain compatible with acpi_intp.
445	 */
446	e_ddi_prop_update_int(DDI_DEV_T_NONE, ddi_root_node(), "acpi-status",
447	    (ACPI_SUCCESS(status)) ? (ACPI_BOOT_INIT | ACPI_BOOT_ENABLE |
448	    ACPI_BOOT_BOOTCONF) : 0);
449
450	/* Mark acpica subsystem as fully initialized. */
451	if (ACPI_SUCCESS(status) &&
452	    acpi_init_level == ACPI_FULL_INITIALIZATION) {
453		acpica_set_core_feature(ACPI_FEATURE_FULL_INIT);
454	}
455
456	mutex_exit(&acpica_module_lock);
457	return (status);
458}
459
460/*
461 * SCI handling
462 */
463
464ACPI_STATUS
465acpica_get_sci(int *sci_irq, iflag_t *sci_flags)
466{
467	ACPI_SUBTABLE_HEADER		*ap;
468	ACPI_TABLE_MADT			*mat;
469	ACPI_MADT_INTERRUPT_OVERRIDE	*mio;
470	ACPI_TABLE_FADT			*fadt;
471	int			madt_seen, madt_size;
472
473
474	/*
475	 * Make sure user options are processed,
476	 * then return error if ACPI CA has been
477	 * disabled or system is not running in ACPI
478	 * and won't need/understand SCI
479	 */
480	acpica_process_user_options();
481	if ((!acpica_enable) || (acpi_init_level & ACPI_NO_ACPI_ENABLE))
482		return (AE_ERROR);
483
484	/*
485	 * according to Intel ACPI developers, SCI
486	 * conforms to PCI bus conventions; level/low
487	 * unless otherwise directed by overrides.
488	 */
489	sci_flags->intr_el = INTR_EL_LEVEL;
490	sci_flags->intr_po = INTR_PO_ACTIVE_LOW;
491	sci_flags->bustype = BUS_PCI;	/*  we *do* conform to PCI */
492
493	/* get the SCI from the FADT */
494	if (AcpiGetTable(ACPI_SIG_FADT, 1, (ACPI_TABLE_HEADER **)&fadt) !=
495	    AE_OK)
496		return (AE_ERROR);
497
498	*sci_irq = fadt->SciInterrupt;
499
500	/* search for ISOs that modify it */
501	/* if we don't find a MADT, that's OK; no ISOs then */
502	if (AcpiGetTable(ACPI_SIG_MADT, 1, (ACPI_TABLE_HEADER **) &mat) !=
503	    AE_OK)
504		return (AE_OK);
505
506	ap = (ACPI_SUBTABLE_HEADER *) (mat + 1);
507	madt_size = mat->Header.Length;
508	madt_seen = sizeof (*mat);
509
510	while (madt_seen < madt_size) {
511		switch (ap->Type) {
512		case ACPI_MADT_TYPE_INTERRUPT_OVERRIDE:
513			mio = (ACPI_MADT_INTERRUPT_OVERRIDE *) ap;
514			if (mio->SourceIrq == *sci_irq) {
515				*sci_irq = mio->GlobalIrq;
516				sci_flags->intr_el = (mio->IntiFlags &
517				    ACPI_MADT_TRIGGER_MASK) >> 2;
518				sci_flags->intr_po = mio->IntiFlags &
519				    ACPI_MADT_POLARITY_MASK;
520			}
521			break;
522		}
523
524		/* advance to next entry */
525		madt_seen += ap->Length;
526		ap = (ACPI_SUBTABLE_HEADER *)(((char *)ap) + ap->Length);
527	}
528
529	/*
530	 * One more check; if ISO said "conform", revert to default
531	 */
532	if (sci_flags->intr_el == INTR_EL_CONFORM)
533		sci_flags->intr_el = INTR_EL_LEVEL;
534	if (sci_flags->intr_po == INTR_PO_CONFORM)
535		sci_flags->intr_po = INTR_PO_ACTIVE_LOW;
536
537	return (AE_OK);
538}
539
540/*
541 * Sets ACPI wake state for device referenced by dip.
542 * If level is S0 (0), disables wake event; otherwise,
543 * enables wake event which will wake system from level.
544 */
545static int
546acpica_ddi_setwake(dev_info_t *dip, int level)
547{
548	ACPI_STATUS	status;
549	ACPI_HANDLE	devobj, gpeobj;
550	ACPI_OBJECT	*prw, *gpe;
551	ACPI_BUFFER	prw_buf;
552	ACPI_OBJECT_LIST	arglist;
553	ACPI_OBJECT		args[3];
554	int		gpebit, pwr_res_count, prw_level, rv;
555
556	/*
557	 * initialize these early so we can use a common
558	 * exit point below
559	 */
560	prw_buf.Pointer = NULL;
561	prw_buf.Length = ACPI_ALLOCATE_BUFFER;
562	rv = 0;
563
564	/*
565	 * Attempt to get a handle to a corresponding ACPI object.
566	 * If no object is found, return quietly, since not all
567	 * devices have corresponding ACPI objects.
568	 */
569	status = acpica_get_handle(dip, &devobj);
570	if (ACPI_FAILURE(status)) {
571		char pathbuf[MAXPATHLEN];
572		ddi_pathname(dip, pathbuf);
573#ifdef DEBUG
574		cmn_err(CE_NOTE, "!acpica_ddi_setwake: could not get"
575		    " handle for %s, %s:%d", pathbuf, ddi_driver_name(dip),
576		    ddi_get_instance(dip));
577#endif
578		goto done;
579	}
580
581	/*
582	 * ACPI3.0 7.2.1: only use the _PSW method if OSPM does not support
583	 * _DSW or if the _DSW method is not present.
584	 *
585	 * _DSW arguments:
586	 * args[0] - Enable/Disable
587	 * args[1] - Target system state
588	 * args[2] - Target device state
589	 */
590
591	arglist.Count = 3;
592	arglist.Pointer = args;
593	args[0].Type = ACPI_TYPE_INTEGER;
594	args[0].Integer.Value = level ? 1 : 0;
595	args[1].Type = ACPI_TYPE_INTEGER;
596	args[1].Integer.Value = level;
597	args[2].Type = ACPI_TYPE_INTEGER;
598	args[2].Integer.Value = level;
599	if (ACPI_FAILURE(status = AcpiEvaluateObject(devobj, "_DSW",
600	    &arglist, NULL))) {
601
602		if (status == AE_NOT_FOUND) {
603			arglist.Count = 1;
604			args[0].Type = ACPI_TYPE_INTEGER;
605			args[0].Integer.Value = level ? 1 : 0;
606
607			if (ACPI_FAILURE(status = AcpiEvaluateObject(devobj,
608			    "_PSW", &arglist, NULL))) {
609
610				if (status != AE_NOT_FOUND) {
611					cmn_err(CE_NOTE,
612					    "!_PSW failure %d for device %s",
613					    status, ddi_driver_name(dip));
614				}
615			}
616
617		} else {
618			cmn_err(CE_NOTE, "!_DSW failure %d for device %s",
619			    status, ddi_driver_name(dip));
620		}
621	}
622
623	/*
624	 * Attempt to evaluate _PRW object.
625	 * If no valid object is found, return quietly, since not all
626	 * devices have _PRW objects.
627	 */
628	status = AcpiEvaluateObject(devobj, "_PRW", NULL, &prw_buf);
629	prw = prw_buf.Pointer;
630	if (ACPI_FAILURE(status) || prw_buf.Length == 0 || prw == NULL ||
631	    prw->Type != ACPI_TYPE_PACKAGE || prw->Package.Count < 2 ||
632	    prw->Package.Elements[1].Type != ACPI_TYPE_INTEGER) {
633		goto done;
634	}
635
636	/* fetch the lowest wake level from the _PRW */
637	prw_level = prw->Package.Elements[1].Integer.Value;
638
639	/*
640	 * process the GPE description
641	 */
642	switch (prw->Package.Elements[0].Type) {
643	case ACPI_TYPE_INTEGER:
644		gpeobj = NULL;
645		gpebit = prw->Package.Elements[0].Integer.Value;
646		break;
647	case ACPI_TYPE_PACKAGE:
648		gpe = &prw->Package.Elements[0];
649		if (gpe->Package.Count != 2 ||
650		    gpe->Package.Elements[1].Type != ACPI_TYPE_INTEGER)
651			goto done;
652		gpeobj = gpe->Package.Elements[0].Reference.Handle;
653		gpebit = gpe->Package.Elements[1].Integer.Value;
654		if (gpeobj == NULL)
655			goto done;
656	default:
657		goto done;
658	}
659
660	rv = -1;
661	if (level == 0) {
662		if (ACPI_FAILURE(AcpiDisableGpe(gpeobj, gpebit, ACPI_NOT_ISR)))
663			goto done;
664	} else if (prw_level >= level) {
665		if (ACPI_SUCCESS(
666		    AcpiSetGpeType(gpeobj, gpebit, ACPI_GPE_TYPE_WAKE)))
667			if (ACPI_FAILURE(
668			    AcpiEnableGpe(gpeobj, gpebit, ACPI_NOT_ISR)))
669				goto done;
670	}
671	rv = 0;
672done:
673	if (prw_buf.Pointer != NULL)
674		AcpiOsFree(prw_buf.Pointer);
675	return (rv);
676}
677
678/*
679 * kstat access to a limited set of ACPI propertis
680 */
681static void
682acpica_init_kstats()
683{
684	ACPI_HANDLE	s3handle;
685	ACPI_STATUS	status;
686	ACPI_TABLE_FADT	*fadt;
687	kstat_named_t *knp;
688
689	/*
690	 * Create a small set of named kstats; just return in the rare
691	 * case of a failure, * in which case, the kstats won't be present.
692	 */
693	if ((acpica_ksp = kstat_create("acpi", 0, "acpi", "misc",
694	    KSTAT_TYPE_NAMED, 2, 0)) == NULL)
695		return;
696
697	/*
698	 * initialize kstat 'S3' to reflect the presence of \_S3 in
699	 * the ACPI namespace (1 = present, 0 = not present)
700	 */
701	knp = acpica_ksp->ks_data;
702	knp->value.l = (AcpiGetHandle(NULL, "\\_S3", &s3handle) == AE_OK);
703	kstat_named_init(knp, "S3", KSTAT_DATA_LONG);
704	knp++;		/* advance to next named kstat */
705
706	/*
707	 * initialize kstat 'preferred_pm_profile' to the value
708	 * contained in the (always present) FADT
709	 */
710	status = AcpiGetTable(ACPI_SIG_FADT, 1, (ACPI_TABLE_HEADER **)&fadt);
711	knp->value.l = (status == AE_OK) ? fadt->PreferredProfile : -1;
712	kstat_named_init(knp, "preferred_pm_profile", KSTAT_DATA_LONG);
713
714	/*
715	 * install the named kstats
716	 */
717	kstat_install(acpica_ksp);
718}
719
720/*
721 * Attempt to save the current ACPI settings (_CRS) for the device
722 * which corresponds to the supplied devinfo node.  The settings are
723 * saved as a property on the dip.  If no ACPI object is found to be
724 * associated with the devinfo node, no action is taken and no error
725 * is reported.
726 */
727void
728acpica_ddi_save_resources(dev_info_t *dip)
729{
730	ACPI_HANDLE	devobj;
731	ACPI_BUFFER	resbuf;
732	int		ret;
733
734	resbuf.Length = ACPI_ALLOCATE_BUFFER;
735	if (ACPI_FAILURE(acpica_get_handle(dip, &devobj)) ||
736	    ACPI_FAILURE(AcpiGetCurrentResources(devobj, &resbuf)))
737		return;
738
739	ret = ddi_prop_create(DDI_DEV_T_NONE, dip, DDI_PROP_CANSLEEP,
740	    "acpi-crs", resbuf.Pointer, resbuf.Length);
741
742	ASSERT(ret == DDI_PROP_SUCCESS);
743
744	AcpiOsFree(resbuf.Pointer);
745}
746
747/*
748 * If the supplied devinfo node has an ACPI settings property attached,
749 * restore them to the associated ACPI device using _SRS.  The property
750 * is deleted from the devinfo node afterward.
751 */
752void
753acpica_ddi_restore_resources(dev_info_t *dip)
754{
755	ACPI_HANDLE	devobj;
756	ACPI_BUFFER	resbuf;
757	uchar_t		*propdata;
758	uint_t		proplen;
759
760	if (ACPI_FAILURE(acpica_get_handle(dip, &devobj)))
761		return;
762
763	if (ddi_prop_lookup_byte_array(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
764	    "acpi-crs", &propdata, &proplen) != DDI_PROP_SUCCESS)
765		return;
766
767	resbuf.Pointer = propdata;
768	resbuf.Length = proplen;
769	(void) AcpiSetCurrentResources(devobj, &resbuf);
770	ddi_prop_free(propdata);
771	(void) ddi_prop_remove(DDI_DEV_T_NONE, dip, "acpi-crs");
772}
773
774void
775acpi_reset_system(void)
776{
777	ACPI_STATUS status;
778	int ten;
779
780	status = AcpiReset();
781	if (status == AE_OK) {
782		/*
783		 * Wait up to 500 milliseconds for AcpiReset() to make its
784		 * way.
785		 */
786		ten = 50000;
787		while (ten-- > 0)
788			tenmicrosec();
789	}
790}
791