xref: /onnv-gate/usr/src/uts/common/io/power.c (revision 6573:7a725819f4fe)
1611Smyers /*
2611Smyers  * CDDL HEADER START
3611Smyers  *
4611Smyers  * The contents of this file are subject to the terms of the
51877Sgk73471  * Common Development and Distribution License (the "License").
61877Sgk73471  * You may not use this file except in compliance with the License.
7611Smyers  *
8611Smyers  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9611Smyers  * or http://www.opensolaris.org/os/licensing.
10611Smyers  * See the License for the specific language governing permissions
11611Smyers  * and limitations under the License.
12611Smyers  *
13611Smyers  * When distributing Covered Code, include this CDDL HEADER in each
14611Smyers  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15611Smyers  * If applicable, add the following below this CDDL HEADER, with the
16611Smyers  * fields enclosed by brackets "[]" replaced with your own identifying
17611Smyers  * information: Portions Copyright [yyyy] [name of copyright owner]
18611Smyers  *
19611Smyers  * CDDL HEADER END
20611Smyers  */
21611Smyers /*
22*6573Sphitran  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
23611Smyers  * Use is subject to license terms.
24611Smyers  */
25611Smyers 
26611Smyers #pragma ident	"%Z%%M%	%I%	%E% SMI"
27611Smyers 
28611Smyers /*
29611Smyers  *	Power Button Driver
30611Smyers  *
31611Smyers  *	This driver handles interrupt generated by the power button on
32611Smyers  *	platforms with "power" device node which has "button" property.
33611Smyers  *	Currently, these platforms are:
34611Smyers  *
35611Smyers  *		ACPI-enabled x86/x64 platforms
36611Smyers  *		Ultra-5_10, Ultra-80, Sun-Blade-100, Sun-Blade-150,
37611Smyers  *		Sun-Blade-1500, Sun-Blade-2500,
38611Smyers  *		Sun-Fire-V210, Sun-Fire-V240, Netra-240
39611Smyers  *
40611Smyers  *	Only one instance is allowed to attach.  In order to know when
41611Smyers  *	an application that has opened the device is going away, a new
42611Smyers  *	minor clone is created for each open(9E) request.  There are
43611Smyers  *	allocations for creating minor clones between 1 and 255.  The ioctl
44611Smyers  *	interface is defined by pbio(7I) and approved as part of
45611Smyers  *	PSARC/1999/393 case.
46611Smyers  */
47611Smyers 
48611Smyers #include <sys/types.h>
49611Smyers #include <sys/conf.h>
50611Smyers #include <sys/ddi.h>
51611Smyers #include <sys/sunddi.h>
52611Smyers #include <sys/ddi_impldefs.h>
53611Smyers #include <sys/cmn_err.h>
54611Smyers #include <sys/errno.h>
55611Smyers #include <sys/modctl.h>
56611Smyers #include <sys/open.h>
57611Smyers #include <sys/stat.h>
58611Smyers #include <sys/poll.h>
59611Smyers #include <sys/pbio.h>
60*6573Sphitran #include <sys/sysevent/eventdefs.h>
61*6573Sphitran #include <sys/sysevent/pwrctl.h>
62920Sjbeloro 
633446Smrj #if defined(__sparc)
643446Smrj #include <sys/machsystm.h>
653446Smrj #endif
663446Smrj 
67611Smyers #ifdef	ACPI_POWER_BUTTON
68920Sjbeloro 
69611Smyers #include <sys/acpi/acpi.h>
70611Smyers #include <sys/acpica.h>
71920Sjbeloro 
72920Sjbeloro #else
73920Sjbeloro 
74920Sjbeloro #include <sys/epic.h>
75920Sjbeloro /*
76920Sjbeloro  * Some #defs that must be here as they differ for power.c
77920Sjbeloro  * and epic.c
78920Sjbeloro  */
79920Sjbeloro #define	EPIC_REGS_OFFSET	0x00
80920Sjbeloro #define	EPIC_REGS_LEN		0x82
81920Sjbeloro 
82920Sjbeloro 
83920Sjbeloro /*
84920Sjbeloro  * This flag, which is set for platforms,  that have EPIC processor
85920Sjbeloro  * to process power button interrupt, helps in executing platform
86920Sjbeloro  * specific code.
87920Sjbeloro  */
88920Sjbeloro static char 	hasEPIC = B_FALSE;
89611Smyers #endif	/* ACPI_POWER_BUTTON */
90611Smyers 
91611Smyers /*
92611Smyers  * Maximum number of clone minors that is allowed.  This value
93611Smyers  * is defined relatively low to save memory.
94611Smyers  */
95611Smyers #define	POWER_MAX_CLONE	256
96611Smyers 
97611Smyers /*
98611Smyers  * Minor number is instance << 8 + clone minor from range 1-255; clone 0
99611Smyers  * is reserved for "original" minor.
100611Smyers  */
101611Smyers #define	POWER_MINOR_TO_CLONE(minor) ((minor) & (POWER_MAX_CLONE - 1))
102611Smyers 
103611Smyers /*
104611Smyers  * Power Button Abort Delay
105611Smyers  */
106611Smyers #define	ABORT_INCREMENT_DELAY	10
107611Smyers 
108611Smyers /*
1091084Sjroberts  * FWARC 2005/687: power device compatible property
1101084Sjroberts  */
1111084Sjroberts #define	POWER_DEVICE_TYPE "power-device-type"
1121084Sjroberts 
1131084Sjroberts /*
114611Smyers  * Driver global variables
115611Smyers  */
116611Smyers static void *power_state;
117611Smyers static int power_inst = -1;
118611Smyers 
119611Smyers static hrtime_t	power_button_debounce = NANOSEC/MILLISEC*10;
120611Smyers static hrtime_t power_button_abort_interval = 1.5 * NANOSEC;
121611Smyers static int	power_button_abort_presses = 3;
122611Smyers static int	power_button_abort_enable = 1;
123611Smyers static int	power_button_enable = 1;
124611Smyers 
125611Smyers static int	power_button_pressed = 0;
126611Smyers static int	power_button_cancel = 0;
127611Smyers static int	power_button_timeouts = 0;
128611Smyers static int	timeout_cancel = 0;
129611Smyers static int	additional_presses = 0;
130611Smyers 
131611Smyers /*
132611Smyers  * Function prototypes
133611Smyers  */
134611Smyers static int power_attach(dev_info_t *, ddi_attach_cmd_t);
135611Smyers static int power_detach(dev_info_t *, ddi_detach_cmd_t);
136611Smyers static int power_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
137611Smyers static int power_open(dev_t *, int, int, cred_t *);
138611Smyers static int power_close(dev_t, int, int, cred_t *);
139611Smyers static int power_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
140611Smyers static int power_chpoll(dev_t, short, int, short *, struct pollhead **);
141611Smyers #ifndef	ACPI_POWER_BUTTON
142611Smyers static uint_t power_high_intr(caddr_t);
143611Smyers #endif
144611Smyers static uint_t power_soft_intr(caddr_t);
145611Smyers static uint_t power_issue_shutdown(caddr_t);
146611Smyers static void power_timeout(caddr_t);
147611Smyers static void power_log_message(void);
148611Smyers 
149611Smyers /*
150611Smyers  * Structure used in the driver
151611Smyers  */
152611Smyers struct power_soft_state {
153611Smyers 	dev_info_t	*dip;		/* device info pointer */
154611Smyers 	kmutex_t	power_mutex;	/* mutex lock */
155611Smyers 	kmutex_t	power_intr_mutex; /* interrupt mutex lock */
156611Smyers 	ddi_iblock_cookie_t soft_iblock_cookie; /* holds interrupt cookie */
157611Smyers 	ddi_iblock_cookie_t high_iblock_cookie; /* holds interrupt cookie */
158611Smyers 	ddi_softintr_t	softintr_id;	/* soft interrupt id */
159611Smyers 	uchar_t		clones[POWER_MAX_CLONE]; /* array of minor clones */
160611Smyers 	int		monitor_on;	/* clone monitoring the button event */
161611Smyers 					/* clone 0 indicates no one is */
162611Smyers 					/* monitoring the button event */
163611Smyers 	pollhead_t	pollhd;		/* poll head struct */
164611Smyers 	int		events;		/* bit map of occured events */
165611Smyers 	int		shutdown_pending; /* system shutdown in progress */
166611Smyers #ifdef	ACPI_POWER_BUTTON
167611Smyers 	boolean_t	fixed_attached;	/* true means fixed is attached */
168611Smyers 	boolean_t	gpe_attached;	/* true means GPE is attached */
169611Smyers 	ACPI_HANDLE	button_obj;	/* handle to device power button */
170611Smyers #else
171611Smyers 	ddi_acc_handle_t power_rhandle; /* power button register handle */
172611Smyers 	uint8_t		*power_btn_reg;	/* power button register address */
173611Smyers 	uint8_t		power_btn_bit;	/* power button register bit */
174611Smyers 	boolean_t	power_regs_mapped; /* flag to tell if regs mapped */
175611Smyers 	boolean_t	power_btn_ioctl; /* flag to specify ioctl request */
176611Smyers #endif
177611Smyers };
178611Smyers 
179*6573Sphitran static void power_gen_sysevent(struct power_soft_state *);
180*6573Sphitran 
181611Smyers #ifdef	ACPI_POWER_BUTTON
182611Smyers static int power_attach_acpi(struct power_soft_state *softsp);
183611Smyers static void power_detach_acpi(struct power_soft_state *softsp);
184611Smyers static UINT32 power_acpi_fixed_event(void *ctx);
185611Smyers #else
186611Smyers static int power_setup_regs(struct power_soft_state *softsp);
187611Smyers static void power_free_regs(struct power_soft_state *softsp);
188611Smyers #endif	/* ACPI_POWER_BUTTON */
189611Smyers 
190611Smyers /*
191611Smyers  * Configuration data structures
192611Smyers  */
193611Smyers static struct cb_ops power_cb_ops = {
194611Smyers 	power_open,		/* open */
195611Smyers 	power_close,		/* close */
196611Smyers 	nodev,			/* strategy */
197611Smyers 	nodev,			/* print */
198611Smyers 	nodev,			/* dump */
199611Smyers 	nodev,			/* read */
200611Smyers 	nodev,			/* write */
201611Smyers 	power_ioctl,		/* ioctl */
202611Smyers 	nodev,			/* devmap */
203611Smyers 	nodev,			/* mmap */
204611Smyers 	nodev,			/* segmap */
205611Smyers 	power_chpoll,		/* poll */
206611Smyers 	ddi_prop_op,		/* cb_prop_op */
207611Smyers 	NULL,			/* streamtab */
208611Smyers 	D_MP | D_NEW,		/* Driver compatibility flag */
209611Smyers 	CB_REV,			/* rev */
210611Smyers 	nodev,			/* cb_aread */
211611Smyers 	nodev			/* cb_awrite */
212611Smyers };
213611Smyers 
214611Smyers static struct dev_ops power_ops = {
215611Smyers 	DEVO_REV,		/* devo_rev, */
216611Smyers 	0,			/* refcnt */
217611Smyers 	power_getinfo,		/* getinfo */
218611Smyers 	nulldev,		/* identify */
219611Smyers 	nulldev,		/* probe */
220611Smyers 	power_attach,		/* attach */
221611Smyers 	power_detach,		/* detach */
222611Smyers 	nodev,			/* reset */
223611Smyers 	&power_cb_ops,		/* cb_ops */
224611Smyers 	(struct bus_ops *)NULL,	/* bus_ops */
225611Smyers 	NULL			/* power */
226611Smyers };
227611Smyers 
228611Smyers static struct modldrv modldrv = {
229611Smyers 	&mod_driverops,		/* Type of module.  This one is a driver */
230*6573Sphitran 	"power button driver",	/* name of module */
231611Smyers 	&power_ops,		/* driver ops */
232611Smyers };
233611Smyers 
234611Smyers static struct modlinkage modlinkage = {
235611Smyers 	MODREV_1,
236611Smyers 	(void *)&modldrv,
237611Smyers 	NULL
238611Smyers };
239611Smyers 
240611Smyers /*
241611Smyers  * These are the module initialization routines.
242611Smyers  */
243611Smyers 
244611Smyers int
245611Smyers _init(void)
246611Smyers {
247611Smyers 	int error;
248611Smyers 
249611Smyers 	if ((error = ddi_soft_state_init(&power_state,
250611Smyers 	    sizeof (struct power_soft_state), 0)) != 0)
251611Smyers 		return (error);
252611Smyers 
253611Smyers 	if ((error = mod_install(&modlinkage)) != 0)
254611Smyers 		ddi_soft_state_fini(&power_state);
255611Smyers 
256611Smyers 	return (error);
257611Smyers }
258611Smyers 
259611Smyers int
260611Smyers _fini(void)
261611Smyers {
262611Smyers 	int error;
263611Smyers 
264611Smyers 	if ((error = mod_remove(&modlinkage)) == 0)
265611Smyers 		ddi_soft_state_fini(&power_state);
266611Smyers 
267611Smyers 	return (error);
268611Smyers }
269611Smyers 
270611Smyers int
271611Smyers _info(struct modinfo *modinfop)
272611Smyers {
273611Smyers 	return (mod_info(&modlinkage, modinfop));
274611Smyers }
275611Smyers 
276611Smyers /*ARGSUSED*/
277611Smyers static int
278611Smyers power_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg,
279611Smyers     void **result)
280611Smyers {
281611Smyers 	struct power_soft_state *softsp;
282611Smyers 
283611Smyers 	if (power_inst == -1)
284611Smyers 		return (DDI_FAILURE);
285611Smyers 
286611Smyers 	switch (infocmd) {
287611Smyers 	case DDI_INFO_DEVT2DEVINFO:
288611Smyers 		if ((softsp = ddi_get_soft_state(power_state, power_inst))
289611Smyers 		    == NULL)
290611Smyers 			return (DDI_FAILURE);
291611Smyers 		*result = (void *)softsp->dip;
292611Smyers 		return (DDI_SUCCESS);
293611Smyers 
294611Smyers 	case DDI_INFO_DEVT2INSTANCE:
295611Smyers 		*result = (void *)(uintptr_t)power_inst;
296611Smyers 		return (DDI_SUCCESS);
297611Smyers 
298611Smyers 	default:
299611Smyers 		return (DDI_FAILURE);
300611Smyers 	}
301611Smyers }
302611Smyers 
303611Smyers static int
304611Smyers power_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
305611Smyers {
306611Smyers 	struct power_soft_state *softsp;
307611Smyers 
308611Smyers 	switch (cmd) {
309611Smyers 	case DDI_ATTACH:
310611Smyers 		break;
311611Smyers 	case DDI_RESUME:
312611Smyers 		return (DDI_SUCCESS);
313611Smyers 	default:
314611Smyers 		return (DDI_FAILURE);
315611Smyers 	}
316611Smyers 
317611Smyers 	/*
318611Smyers 	 * If the power node doesn't have "button" property, quietly
319611Smyers 	 * fail to attach.
320611Smyers 	 */
321611Smyers 	if (ddi_prop_exists(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
322611Smyers 	    "button") == 0)
323611Smyers 		return (DDI_FAILURE);
324611Smyers 
325611Smyers 	if (power_inst != -1)
326611Smyers 		return (DDI_FAILURE);
327611Smyers 
328611Smyers 	power_inst = ddi_get_instance(dip);
329611Smyers 
330611Smyers 	if (ddi_soft_state_zalloc(power_state, power_inst) != DDI_SUCCESS)
331611Smyers 		return (DDI_FAILURE);
332611Smyers 
333611Smyers 	if (ddi_create_minor_node(dip, "power_button", S_IFCHR,
334611Smyers 	    (power_inst << 8) + 0, "ddi_power_button", 0) != DDI_SUCCESS)
335611Smyers 		return (DDI_FAILURE);
336611Smyers 
337611Smyers 	softsp = ddi_get_soft_state(power_state, power_inst);
338611Smyers 	softsp->dip = dip;
339611Smyers 
340611Smyers #ifdef	ACPI_POWER_BUTTON
341622Smyers 	(void) power_attach_acpi(softsp);
342611Smyers #else
343611Smyers 	if (power_setup_regs(softsp) != DDI_SUCCESS) {
344611Smyers 		cmn_err(CE_WARN, "power_attach: failed to setup registers");
345611Smyers 		goto error;
346611Smyers 	}
347611Smyers 
348611Smyers 	if (ddi_get_iblock_cookie(dip, 0,
349611Smyers 	    &softsp->high_iblock_cookie) != DDI_SUCCESS) {
350611Smyers 		cmn_err(CE_WARN, "power_attach: ddi_get_soft_iblock_cookie "
351611Smyers 		    "failed.");
352611Smyers 		goto error;
353611Smyers 	}
354611Smyers 	mutex_init(&softsp->power_intr_mutex, NULL, MUTEX_DRIVER,
355611Smyers 	    softsp->high_iblock_cookie);
356611Smyers 
357611Smyers 	if (ddi_add_intr(dip, 0, &softsp->high_iblock_cookie, NULL,
358611Smyers 	    power_high_intr, (caddr_t)softsp) != DDI_SUCCESS) {
359611Smyers 		cmn_err(CE_WARN, "power_attach: failed to add high-level "
360611Smyers 		    " interrupt handler.");
361611Smyers 		mutex_destroy(&softsp->power_intr_mutex);
362611Smyers 		goto error;
363611Smyers 	}
364611Smyers #endif	/* ACPI_POWER_BUTTON */
365611Smyers 
366611Smyers 	if (ddi_get_soft_iblock_cookie(dip, DDI_SOFTINT_LOW,
367611Smyers 	    &softsp->soft_iblock_cookie) != DDI_SUCCESS) {
368611Smyers 		cmn_err(CE_WARN, "power_attach: ddi_get_soft_iblock_cookie "
369611Smyers 		    "failed.");
370611Smyers 		mutex_destroy(&softsp->power_intr_mutex);
371611Smyers 		ddi_remove_intr(dip, 0, NULL);
372611Smyers 		goto error;
373611Smyers 	}
374611Smyers 
375611Smyers 	mutex_init(&softsp->power_mutex, NULL, MUTEX_DRIVER,
376611Smyers 	    (void *)softsp->soft_iblock_cookie);
377611Smyers 
378611Smyers 	if (ddi_add_softintr(dip, DDI_SOFTINT_LOW, &softsp->softintr_id,
379611Smyers 	    NULL, NULL, power_soft_intr, (caddr_t)softsp) != DDI_SUCCESS) {
380611Smyers 		cmn_err(CE_WARN, "power_attach: failed to add soft "
381611Smyers 		    "interrupt handler.");
382611Smyers 		mutex_destroy(&softsp->power_mutex);
383611Smyers 		mutex_destroy(&softsp->power_intr_mutex);
384611Smyers 		ddi_remove_intr(dip, 0, NULL);
385611Smyers 		goto error;
386611Smyers 	}
387611Smyers 
388611Smyers 	ddi_report_dev(dip);
389611Smyers 
390611Smyers 	return (DDI_SUCCESS);
391611Smyers 
392611Smyers error:
393611Smyers #ifdef	ACPI_POWER_BUTTON
394611Smyers 	/*
395611Smyers 	 * detach ACPI power button
396611Smyers 	 */
397611Smyers 	power_detach_acpi(softsp);
398611Smyers #else
399611Smyers 	power_free_regs(softsp);
400611Smyers #endif	/* ACPI_POWER_BUTTON */
401611Smyers 	ddi_remove_minor_node(dip, "power_button");
402611Smyers 	ddi_soft_state_free(power_state, power_inst);
403611Smyers 	return (DDI_FAILURE);
404611Smyers }
405611Smyers 
406611Smyers /*ARGSUSED*/
407611Smyers /*
408611Smyers  * This driver doesn't detach.
409611Smyers  */
410611Smyers static int
411611Smyers power_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
412611Smyers {
413611Smyers 	/*
414611Smyers 	 * Since the "power" node has "reg" property, as part of
415611Smyers 	 * the suspend operation, detach(9E) entry point is called.
416611Smyers 	 * There is no state to save, since this register is used
417611Smyers 	 * by OBP to power off the system and the state of the
418611Smyers 	 * power off is preserved by hardware.
419611Smyers 	 */
420611Smyers 	return ((cmd == DDI_SUSPEND) ? DDI_SUCCESS :
421611Smyers 	    DDI_FAILURE);
422611Smyers }
423611Smyers 
424920Sjbeloro 
425611Smyers #ifndef	ACPI_POWER_BUTTON
426611Smyers /*
427611Smyers  * Handler for the high-level interrupt.
428611Smyers  */
429611Smyers static uint_t
430611Smyers power_high_intr(caddr_t arg)
431611Smyers {
432611Smyers 	struct power_soft_state *softsp = (struct power_soft_state *)arg;
433611Smyers 	ddi_acc_handle_t hdl = softsp->power_rhandle;
434611Smyers 	uint8_t		reg;
435611Smyers 
436611Smyers 	hrtime_t tstamp;
437611Smyers 	static hrtime_t o_tstamp = 0;
438611Smyers 	static hrtime_t power_button_tstamp = 0;
439611Smyers 	static int power_button_cnt;
440611Smyers 
441611Smyers 	if (softsp->power_regs_mapped) {
442611Smyers 		mutex_enter(&softsp->power_intr_mutex);
443920Sjbeloro 
444920Sjbeloro 		/* Check if power button interrupt is delivered by EPIC HW */
445920Sjbeloro 		if (hasEPIC) {
446920Sjbeloro 			/* read isr - first issue command */
447920Sjbeloro 			EPIC_WR(hdl, softsp->power_btn_reg,
448*6573Sphitran 			    EPIC_ATOM_INTR_READ);
449920Sjbeloro 			/* next, read the reg */
450920Sjbeloro 			EPIC_RD(hdl, softsp->power_btn_reg, reg);
451920Sjbeloro 
452920Sjbeloro 			if (reg & EPIC_FIRE_INTERRUPT) {  /* PB pressed */
453920Sjbeloro 				/* clear the interrupt */
454920Sjbeloro 				EPIC_WR(hdl, softsp->power_btn_reg,
455*6573Sphitran 				    EPIC_ATOM_INTR_CLEAR);
456920Sjbeloro 			} else {
457920Sjbeloro 				if (!softsp->power_btn_ioctl) {
458920Sjbeloro 					mutex_exit(&softsp->power_intr_mutex);
459920Sjbeloro 					return (DDI_INTR_CLAIMED);
460920Sjbeloro 				}
461920Sjbeloro 				softsp->power_btn_ioctl = B_FALSE;
462920Sjbeloro 			}
463611Smyers 		} else {
464920Sjbeloro 			reg = ddi_get8(hdl, softsp->power_btn_reg);
465920Sjbeloro 			if (reg & softsp->power_btn_bit) {
466920Sjbeloro 				reg &= softsp->power_btn_bit;
467920Sjbeloro 				ddi_put8(hdl, softsp->power_btn_reg, reg);
468920Sjbeloro 				(void) ddi_get8(hdl, softsp->power_btn_reg);
469920Sjbeloro 			} else {
470920Sjbeloro 				if (!softsp->power_btn_ioctl) {
471920Sjbeloro 					mutex_exit(&softsp->power_intr_mutex);
472920Sjbeloro 					return (DDI_INTR_CLAIMED);
473920Sjbeloro 				}
474920Sjbeloro 				softsp->power_btn_ioctl = B_FALSE;
475611Smyers 			}
476611Smyers 		}
477611Smyers 		mutex_exit(&softsp->power_intr_mutex);
478611Smyers 	}
479611Smyers 
480611Smyers 	tstamp = gethrtime();
481611Smyers 
482611Smyers 	/* need to deal with power button debounce */
483611Smyers 	if (o_tstamp && (tstamp - o_tstamp) < power_button_debounce) {
484611Smyers 		o_tstamp = tstamp;
485611Smyers 		return (DDI_INTR_CLAIMED);
486611Smyers 	}
487611Smyers 	o_tstamp = tstamp;
488611Smyers 
489611Smyers 	power_button_cnt++;
490611Smyers 
491611Smyers 	mutex_enter(&softsp->power_intr_mutex);
492611Smyers 	power_button_pressed++;
493611Smyers 	mutex_exit(&softsp->power_intr_mutex);
494611Smyers 
495611Smyers 	/*
496611Smyers 	 * If power button abort is enabled and power button was pressed
497611Smyers 	 * power_button_abort_presses times within power_button_abort_interval
498611Smyers 	 * then call abort_sequence_enter();
499611Smyers 	 */
500611Smyers 	if (power_button_abort_enable) {
501611Smyers 		if (power_button_abort_presses == 1 ||
502611Smyers 		    tstamp < (power_button_tstamp +
503611Smyers 		    power_button_abort_interval)) {
504611Smyers 			if (power_button_cnt == power_button_abort_presses) {
505611Smyers 				mutex_enter(&softsp->power_intr_mutex);
506611Smyers 				power_button_cancel += power_button_timeouts;
507611Smyers 				power_button_pressed = 0;
508611Smyers 				mutex_exit(&softsp->power_intr_mutex);
509611Smyers 				power_button_cnt = 0;
510611Smyers 				abort_sequence_enter("Power Button Abort");
511611Smyers 				return (DDI_INTR_CLAIMED);
512611Smyers 			}
513611Smyers 		} else {
514611Smyers 			power_button_cnt = 1;
515611Smyers 			power_button_tstamp = tstamp;
516611Smyers 		}
517611Smyers 	}
518611Smyers 
519611Smyers 	if (!power_button_enable)
520611Smyers 		return (DDI_INTR_CLAIMED);
521611Smyers 
522611Smyers 	/* post softint to issue timeout for power button action */
523611Smyers 	if (softsp->softintr_id != NULL)
524611Smyers 		ddi_trigger_softintr(softsp->softintr_id);
525611Smyers 
526611Smyers 	return (DDI_INTR_CLAIMED);
527611Smyers }
528611Smyers #endif	/* ifndef ACPI_POWER_BUTTON */
529611Smyers 
530611Smyers /*
531611Smyers  * Handle the softints....
532611Smyers  *
533611Smyers  * If only one softint is posted for several button presses, record
534611Smyers  * the number of additional presses just incase this was actually not quite
535611Smyers  * an Abort sequence so that we can log this event later.
536611Smyers  *
537611Smyers  * Issue a timeout with a duration being a fraction larger than
538611Smyers  * the specified Abort interval inorder to perform a power down if required.
539611Smyers  */
540611Smyers static uint_t
541611Smyers power_soft_intr(caddr_t arg)
542611Smyers {
543611Smyers 	struct power_soft_state *softsp = (struct power_soft_state *)arg;
544611Smyers 
545611Smyers 	if (!power_button_abort_enable)
546611Smyers 		return (power_issue_shutdown(arg));
547611Smyers 
548611Smyers 	mutex_enter(&softsp->power_intr_mutex);
549611Smyers 	if (!power_button_pressed) {
550611Smyers 		mutex_exit(&softsp->power_intr_mutex);
551611Smyers 		return (DDI_INTR_CLAIMED);
552611Smyers 	}
553611Smyers 
554611Smyers 	/*
555611Smyers 	 * Schedule a timeout to do the necessary
556611Smyers 	 * work for shutdown, only one timeout for
557611Smyers 	 * n presses if power button was pressed
558611Smyers 	 * more than once before softint fired
559611Smyers 	 */
560611Smyers 	if (power_button_pressed > 1)
561611Smyers 		additional_presses += power_button_pressed - 1;
562611Smyers 
563611Smyers 	timeout_cancel = 0;
564611Smyers 	power_button_pressed = 0;
565611Smyers 	power_button_timeouts++;
566611Smyers 	mutex_exit(&softsp->power_intr_mutex);
567611Smyers 	(void) timeout((void(*)(void *))power_timeout,
568611Smyers 	    softsp, NSEC_TO_TICK(power_button_abort_interval) +
569611Smyers 	    ABORT_INCREMENT_DELAY);
570611Smyers 
571611Smyers 	return (DDI_INTR_CLAIMED);
572611Smyers }
573611Smyers 
574611Smyers /*
575611Smyers  * Upon receiving a timeout the following is determined:
576611Smyers  *
577611Smyers  * If an  Abort sequence was issued, then we cancel all outstanding timeouts
578611Smyers  * and additional presses prior to the Abort sequence.
579611Smyers  *
580611Smyers  * If we had multiple timeouts issued and the abort sequence was not met,
581611Smyers  * then we had more than one button press to power down the machine. We
582611Smyers  * were probably trying to issue an abort. So log a message indicating this
583611Smyers  * and cancel all outstanding timeouts.
584611Smyers  *
585611Smyers  * If we had just one timeout and the abort sequence was not met then
586611Smyers  * we really did want to power down the machine, so call power_issue_shutdown()
587611Smyers  * to do the work and schedule a power down
588611Smyers  */
589611Smyers static void
590611Smyers power_timeout(caddr_t arg)
591611Smyers {
592611Smyers 	struct power_soft_state *softsp = (struct power_soft_state *)arg;
593611Smyers 	static int first = 0;
594611Smyers 
595611Smyers 	/*
596611Smyers 	 * Abort was generated cancel all outstanding power
597611Smyers 	 * button timeouts
598611Smyers 	 */
599611Smyers 	mutex_enter(&softsp->power_intr_mutex);
600611Smyers 	if (power_button_cancel) {
601611Smyers 		power_button_cancel--;
602611Smyers 		power_button_timeouts--;
603611Smyers 		if (!first) {
604611Smyers 			first++;
605611Smyers 			additional_presses = 0;
606611Smyers 		}
607611Smyers 		mutex_exit(&softsp->power_intr_mutex);
608611Smyers 		return;
609611Smyers 	}
610611Smyers 	first = 0;
611611Smyers 
612611Smyers 	/*
613611Smyers 	 * We get here if the timeout(s) have fired and they were
614611Smyers 	 * not issued prior to an abort.
615611Smyers 	 *
616611Smyers 	 * If we had more than one press in the interval we were
617611Smyers 	 * probably trying to issue an abort, but didnt press the
618611Smyers 	 * required number within the interval. Hence cancel all
619611Smyers 	 * timeouts and do not continue towards shutdown.
620611Smyers 	 */
621611Smyers 	if (!timeout_cancel) {
622611Smyers 		timeout_cancel = power_button_timeouts +
623611Smyers 		    additional_presses;
624611Smyers 
625611Smyers 		power_button_timeouts--;
626611Smyers 		if (!power_button_timeouts)
627611Smyers 			additional_presses = 0;
628611Smyers 
629611Smyers 		if (timeout_cancel > 1) {
630611Smyers 			mutex_exit(&softsp->power_intr_mutex);
631611Smyers 			cmn_err(CE_NOTE, "Power Button pressed "
632611Smyers 			    "%d times, cancelling all requests",
633611Smyers 			    timeout_cancel);
634611Smyers 			return;
635611Smyers 		}
636611Smyers 		mutex_exit(&softsp->power_intr_mutex);
637611Smyers 
638611Smyers 		/* Go and do the work to request shutdown */
639611Smyers 		(void) power_issue_shutdown((caddr_t)softsp);
640611Smyers 		return;
641611Smyers 	}
642611Smyers 
643611Smyers 	power_button_timeouts--;
644611Smyers 	if (!power_button_timeouts)
645611Smyers 		additional_presses = 0;
646611Smyers 	mutex_exit(&softsp->power_intr_mutex);
647611Smyers }
648611Smyers 
649611Smyers #ifdef ACPI_POWER_BUTTON
650611Smyers static void
651611Smyers do_shutdown(void)
652611Smyers {
653611Smyers 	proc_t *initpp;
654611Smyers 
655611Smyers 	/*
656611Smyers 	 * If we're still booting and init(1) isn't set up yet, simply halt.
657611Smyers 	 */
658611Smyers 	mutex_enter(&pidlock);
659611Smyers 	initpp = prfind(P_INITPID);
660611Smyers 	mutex_exit(&pidlock);
661611Smyers 	if (initpp == NULL) {
662611Smyers 		extern void halt(char *);
663611Smyers 		halt("Power off the System");   /* just in case */
664611Smyers 	}
665611Smyers 
666611Smyers 	/*
667611Smyers 	 * else, graceful shutdown with inittab and all getting involved
668611Smyers 	 */
669611Smyers 	psignal(initpp, SIGPWR);
670611Smyers }
671611Smyers #endif
672611Smyers 
673611Smyers static uint_t
674611Smyers power_issue_shutdown(caddr_t arg)
675611Smyers {
676611Smyers 	struct power_soft_state *softsp = (struct power_soft_state *)arg;
677611Smyers 
678611Smyers 	mutex_enter(&softsp->power_mutex);
679611Smyers 	softsp->events |= PB_BUTTON_PRESS;
680611Smyers 	if (softsp->monitor_on != 0) {
681611Smyers 		mutex_exit(&softsp->power_mutex);
682611Smyers 		pollwakeup(&softsp->pollhd, POLLRDNORM);
683611Smyers 		pollwakeup(&softsp->pollhd, POLLIN);
684*6573Sphitran 		power_gen_sysevent(softsp);
685611Smyers 		return (DDI_INTR_CLAIMED);
686611Smyers 	}
687611Smyers 
688611Smyers 	if (!softsp->shutdown_pending) {
689611Smyers 		cmn_err(CE_WARN, "Power off requested from power button or "
690611Smyers 		    "SC, powering down the system!");
691611Smyers 		softsp->shutdown_pending = 1;
692611Smyers 		do_shutdown();
693611Smyers 
694611Smyers 		/*
695611Smyers 		 * Wait a while for "do_shutdown()" to shut down the system
696611Smyers 		 * before logging an error message.
697611Smyers 		 */
698611Smyers 		(void) timeout((void(*)(void *))power_log_message, NULL,
699611Smyers 		    100 * hz);
700611Smyers 	}
701611Smyers 	mutex_exit(&softsp->power_mutex);
702611Smyers 
703611Smyers 	return (DDI_INTR_CLAIMED);
704611Smyers }
705611Smyers 
706*6573Sphitran static void
707*6573Sphitran power_gen_sysevent(struct power_soft_state *softsp)
708*6573Sphitran {
709*6573Sphitran 	nvlist_t *attr_list = NULL;
710*6573Sphitran 	int err;
711*6573Sphitran 	char pathname[MAXPATHLEN];
712*6573Sphitran 	char hid[9] = "\0";
713*6573Sphitran 
714*6573Sphitran 	/* Allocate and build sysevent attribute list */
715*6573Sphitran 	err = nvlist_alloc(&attr_list, NV_UNIQUE_NAME_TYPE, DDI_NOSLEEP);
716*6573Sphitran 	if (err != 0) {
717*6573Sphitran 		cmn_err(CE_WARN,
718*6573Sphitran 		    "cannot allocate memory for sysevent attributes\n");
719*6573Sphitran 		return;
720*6573Sphitran 	}
721*6573Sphitran 
722*6573Sphitran #ifdef ACPI_POWER_BUTTON
723*6573Sphitran 	/* Only control method power button has HID */
724*6573Sphitran 	if (softsp->gpe_attached) {
725*6573Sphitran 		(void) strlcpy(hid, "PNP0C0C", sizeof (hid));
726*6573Sphitran 	}
727*6573Sphitran #endif
728*6573Sphitran 
729*6573Sphitran 	err = nvlist_add_string(attr_list, PWRCTL_DEV_HID, hid);
730*6573Sphitran 	if (err != 0) {
731*6573Sphitran 		cmn_err(CE_WARN,
732*6573Sphitran 		    "Failed to add attr [%s] for %s/%s event",
733*6573Sphitran 		    PWRCTL_DEV_HID, EC_PWRCTL, ESC_PWRCTL_POWER_BUTTON);
734*6573Sphitran 		nvlist_free(attr_list);
735*6573Sphitran 		return;
736*6573Sphitran 	}
737*6573Sphitran 
738*6573Sphitran 	(void) ddi_pathname(softsp->dip, pathname);
739*6573Sphitran 	err = nvlist_add_string(attr_list, PWRCTL_DEV_PHYS_PATH, pathname);
740*6573Sphitran 	if (err != 0) {
741*6573Sphitran 		cmn_err(CE_WARN,
742*6573Sphitran 		    "Failed to add attr [%s] for %s/%s event",
743*6573Sphitran 		    PWRCTL_DEV_PHYS_PATH, EC_PWRCTL, ESC_PWRCTL_POWER_BUTTON);
744*6573Sphitran 		nvlist_free(attr_list);
745*6573Sphitran 		return;
746*6573Sphitran 	}
747*6573Sphitran 
748*6573Sphitran 	/* Generate/log sysevent */
749*6573Sphitran 	err = ddi_log_sysevent(softsp->dip, DDI_VENDOR_SUNW, EC_PWRCTL,
750*6573Sphitran 	    ESC_PWRCTL_POWER_BUTTON, attr_list, NULL, DDI_NOSLEEP);
751*6573Sphitran 	if (err != DDI_SUCCESS) {
752*6573Sphitran 		cmn_err(CE_WARN,
753*6573Sphitran 		    "cannot log sysevent, err code %x\n", err);
754*6573Sphitran 	}
755*6573Sphitran 
756*6573Sphitran 	nvlist_free(attr_list);
757*6573Sphitran }
758*6573Sphitran 
759611Smyers /*
760611Smyers  * Open the device.
761611Smyers  */
762611Smyers /*ARGSUSED*/
763611Smyers static int
764611Smyers power_open(dev_t *devp, int openflags, int otyp, cred_t *credp)
765611Smyers {
766611Smyers 	struct power_soft_state *softsp;
767611Smyers 	int clone;
768611Smyers 
769611Smyers 	if (otyp != OTYP_CHR)
770611Smyers 		return (EINVAL);
771611Smyers 
772611Smyers 	if ((softsp = ddi_get_soft_state(power_state, power_inst)) ==
773611Smyers 	    NULL)
774611Smyers 		return (ENXIO);
775611Smyers 
776611Smyers 	mutex_enter(&softsp->power_mutex);
777611Smyers 	for (clone = 1; clone < POWER_MAX_CLONE; clone++)
778611Smyers 		if (!softsp->clones[clone])
779611Smyers 			break;
780611Smyers 
781611Smyers 	if (clone == POWER_MAX_CLONE) {
782611Smyers 		cmn_err(CE_WARN, "power_open: No more allocation left "
783611Smyers 		    "to create a clone minor.");
784611Smyers 		mutex_exit(&softsp->power_mutex);
785611Smyers 		return (ENXIO);
786611Smyers 	}
787611Smyers 
788611Smyers 	*devp = makedevice(getmajor(*devp), (power_inst << 8) + clone);
789611Smyers 	softsp->clones[clone] = 1;
790611Smyers 	mutex_exit(&softsp->power_mutex);
791611Smyers 
792611Smyers 	return (0);
793611Smyers }
794611Smyers 
795611Smyers /*
796611Smyers  * Close the device.
797611Smyers  */
798611Smyers /*ARGSUSED*/
799611Smyers static  int
800611Smyers power_close(dev_t dev, int openflags, int otyp, cred_t *credp)
801611Smyers {
802611Smyers 	struct power_soft_state *softsp;
803611Smyers 	int clone;
804611Smyers 
805611Smyers 	if (otyp != OTYP_CHR)
806611Smyers 		return (EINVAL);
807611Smyers 
808611Smyers 	if ((softsp = ddi_get_soft_state(power_state, power_inst)) ==
809611Smyers 	    NULL)
810611Smyers 		return (ENXIO);
811611Smyers 
812611Smyers 	clone = POWER_MINOR_TO_CLONE(getminor(dev));
813611Smyers 	mutex_enter(&softsp->power_mutex);
814611Smyers 	if (softsp->monitor_on == clone)
815611Smyers 		softsp->monitor_on = 0;
816611Smyers 	softsp->clones[clone] = 0;
817611Smyers 	mutex_exit(&softsp->power_mutex);
818611Smyers 
819611Smyers 	return (0);
820611Smyers }
821611Smyers 
822611Smyers /*ARGSUSED*/
823611Smyers static  int
824611Smyers power_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cred_p,
825611Smyers     int *rval_p)
826611Smyers {
827611Smyers 	struct power_soft_state *softsp;
828611Smyers 	int clone;
829611Smyers 
830611Smyers 	if ((softsp = ddi_get_soft_state(power_state, power_inst)) ==
831611Smyers 	    NULL)
832611Smyers 		return (ENXIO);
833611Smyers 
834611Smyers 	clone = POWER_MINOR_TO_CLONE(getminor(dev));
835611Smyers 	switch (cmd) {
836611Smyers 	case PB_BEGIN_MONITOR:
837611Smyers 		mutex_enter(&softsp->power_mutex);
838611Smyers 		if (softsp->monitor_on) {
839611Smyers 			mutex_exit(&softsp->power_mutex);
840611Smyers 			return (EBUSY);
841611Smyers 		}
842611Smyers 		softsp->monitor_on = clone;
843611Smyers 		mutex_exit(&softsp->power_mutex);
844611Smyers 		return (0);
845611Smyers 
846611Smyers 	case PB_END_MONITOR:
847611Smyers 		mutex_enter(&softsp->power_mutex);
848611Smyers 
849611Smyers 		/*
850611Smyers 		 * If PB_END_MONITOR is called without first
851611Smyers 		 * calling PB_BEGIN_MONITOR, an error will be
852611Smyers 		 * returned.
853611Smyers 		 */
854611Smyers 		if (!softsp->monitor_on) {
855611Smyers 			mutex_exit(&softsp->power_mutex);
856611Smyers 			return (ENXIO);
857611Smyers 		}
858611Smyers 
859611Smyers 		/*
860611Smyers 		 * This clone is not monitoring the button.
861611Smyers 		 */
862611Smyers 		if (softsp->monitor_on != clone) {
863611Smyers 			mutex_exit(&softsp->power_mutex);
864611Smyers 			return (EINVAL);
865611Smyers 		}
866611Smyers 		softsp->monitor_on = 0;
867611Smyers 		mutex_exit(&softsp->power_mutex);
868611Smyers 		return (0);
869611Smyers 
870611Smyers 	case PB_GET_EVENTS:
871611Smyers 		mutex_enter(&softsp->power_mutex);
872611Smyers 		if (ddi_copyout((void *)&softsp->events, (void *)arg,
873611Smyers 		    sizeof (int), mode) != 0) {
874611Smyers 			mutex_exit(&softsp->power_mutex);
875611Smyers 			return (EFAULT);
876611Smyers 		}
877611Smyers 
878611Smyers 		/*
879611Smyers 		 * This ioctl returned the events detected since last
880611Smyers 		 * call.  Note that any application can get the events
881611Smyers 		 * and clear the event register.
882611Smyers 		 */
883611Smyers 		softsp->events = 0;
884611Smyers 		mutex_exit(&softsp->power_mutex);
885611Smyers 		return (0);
886611Smyers 
887611Smyers 	/*
888611Smyers 	 * This ioctl is used by the test suite.
889611Smyers 	 */
890611Smyers 	case PB_CREATE_BUTTON_EVENT:
891611Smyers #ifdef	ACPI_POWER_BUTTON
892611Smyers 		(UINT32)power_acpi_fixed_event((void *)softsp);
893611Smyers #else
894611Smyers 		if (softsp->power_regs_mapped) {
895611Smyers 			mutex_enter(&softsp->power_intr_mutex);
896611Smyers 			softsp->power_btn_ioctl = B_TRUE;
897611Smyers 			mutex_exit(&softsp->power_intr_mutex);
898611Smyers 		}
899611Smyers 		(void) power_high_intr((caddr_t)softsp);
900611Smyers #endif	/* ACPI_POWER_BUTTON */
901611Smyers 		return (0);
902611Smyers 
903611Smyers 	default:
904611Smyers 		return (ENOTTY);
905611Smyers 	}
906611Smyers }
907611Smyers 
908611Smyers /*ARGSUSED*/
909611Smyers static int
910611Smyers power_chpoll(dev_t dev, short events, int anyyet,
911611Smyers     short *reventsp, struct pollhead **phpp)
912611Smyers {
913611Smyers 	struct power_soft_state *softsp;
914611Smyers 
915611Smyers 	if ((softsp = ddi_get_soft_state(power_state, power_inst)) == NULL)
916611Smyers 		return (ENXIO);
917611Smyers 
918611Smyers 	mutex_enter(&softsp->power_mutex);
919611Smyers 	*reventsp = 0;
920611Smyers 	if (softsp->events)
921611Smyers 		*reventsp = POLLRDNORM|POLLIN;
922611Smyers 	else {
923611Smyers 		if (!anyyet)
924611Smyers 			*phpp = &softsp->pollhd;
925611Smyers 	}
926611Smyers 	mutex_exit(&softsp->power_mutex);
927611Smyers 
928611Smyers 	return (0);
929611Smyers }
930611Smyers 
931611Smyers static void
932611Smyers power_log_message(void)
933611Smyers {
934611Smyers 	struct power_soft_state *softsp;
935611Smyers 
936611Smyers 	if ((softsp = ddi_get_soft_state(power_state, power_inst)) == NULL) {
937611Smyers 		cmn_err(CE_WARN, "Failed to get internal state!");
938611Smyers 		return;
939611Smyers 	}
940611Smyers 
941611Smyers 	mutex_enter(&softsp->power_mutex);
942611Smyers 	softsp->shutdown_pending = 0;
943611Smyers 	cmn_err(CE_WARN, "Failed to shut down the system!");
944611Smyers 	mutex_exit(&softsp->power_mutex);
945611Smyers }
946611Smyers 
947611Smyers #ifdef	ACPI_POWER_BUTTON
948611Smyers /*
949611Smyers  * Given a handle to a device object, locate a _PRW object
950611Smyers  * if present and fetch the GPE info for this device object
951611Smyers  */
952611Smyers static ACPI_STATUS
953611Smyers power_get_prw_gpe(ACPI_HANDLE dev, ACPI_HANDLE *gpe_dev, UINT32 *gpe_num)
954611Smyers {
955611Smyers 	ACPI_BUFFER buf;
956611Smyers 	ACPI_STATUS status;
957611Smyers 	ACPI_HANDLE prw;
958611Smyers 	ACPI_OBJECT *gpe;
959611Smyers 
960611Smyers 	/*
961611Smyers 	 * Evaluate _PRW if present
962611Smyers 	 */
963611Smyers 	status = AcpiGetHandle(dev, "_PRW", &prw);
964611Smyers 	if (status != AE_OK)
965611Smyers 		return (status);
966611Smyers 	buf.Length = ACPI_ALLOCATE_BUFFER;
967611Smyers 	status = AcpiEvaluateObjectTyped(prw, NULL, NULL, &buf,
968611Smyers 	    ACPI_TYPE_PACKAGE);
969611Smyers 	if (status != AE_OK)
970611Smyers 		return (status);
971611Smyers 
972611Smyers 	/*
973611Smyers 	 * Sanity-check the package; need at least two elements
974611Smyers 	 */
975611Smyers 	status = AE_ERROR;
976611Smyers 	if (((ACPI_OBJECT *)buf.Pointer)->Package.Count < 2)
977611Smyers 		goto done;
978611Smyers 
979611Smyers 	gpe = &((ACPI_OBJECT *)buf.Pointer)->Package.Elements[0];
980611Smyers 	if (gpe->Type == ACPI_TYPE_INTEGER) {
981611Smyers 		*gpe_dev = NULL;
982611Smyers 		*gpe_num = gpe->Integer.Value;
983611Smyers 		status = AE_OK;
984611Smyers 	} else if (gpe->Type == ACPI_TYPE_PACKAGE) {
985611Smyers 		if ((gpe->Package.Count != 2) ||
986611Smyers 		    (gpe->Package.Elements[0].Type != ACPI_TYPE_DEVICE) ||
987611Smyers 		    (gpe->Package.Elements[1].Type != ACPI_TYPE_INTEGER))
988611Smyers 			goto done;
989611Smyers 		*gpe_dev = gpe->Package.Elements[0].Reference.Handle;
990611Smyers 		*gpe_num = gpe->Package.Elements[1].Integer.Value;
991611Smyers 		status = AE_OK;
992611Smyers 	}
993611Smyers 
994611Smyers done:
995611Smyers 	AcpiOsFree(buf.Pointer);
996611Smyers 	return (status);
997611Smyers }
998611Smyers 
999611Smyers 
1000611Smyers /*
1001611Smyers  *
1002611Smyers  */
1003622Smyers /*ARGSUSED*/
1004611Smyers static ACPI_STATUS
1005611Smyers acpi_device(ACPI_HANDLE obj, UINT32 nesting, void *context, void **rv)
1006611Smyers {
1007622Smyers 
1008611Smyers 	*((ACPI_HANDLE *)context) = obj;
1009611Smyers 	return (AE_OK);
1010611Smyers }
1011611Smyers 
1012611Smyers /*
1013611Smyers  *
1014611Smyers  */
1015611Smyers static ACPI_HANDLE
1016611Smyers probe_acpi_pwrbutton()
1017611Smyers {
1018611Smyers 	ACPI_HANDLE obj = NULL;
1019611Smyers 
1020622Smyers 	(void) AcpiGetDevices("PNP0C0C", acpi_device, (void *)&obj, NULL);
1021611Smyers 	return (obj);
1022611Smyers }
1023611Smyers 
1024611Smyers static UINT32
1025611Smyers power_acpi_fixed_event(void *ctx)
1026611Smyers {
1027611Smyers 
1028611Smyers 	mutex_enter(&((struct power_soft_state *)ctx)->power_intr_mutex);
1029611Smyers 	power_button_pressed++;
1030611Smyers 	mutex_exit(&((struct power_soft_state *)ctx)->power_intr_mutex);
1031611Smyers 
1032611Smyers 	/* post softint to issue timeout for power button action */
1033611Smyers 	if (((struct power_soft_state *)ctx)->softintr_id != NULL)
1034611Smyers 		ddi_trigger_softintr(
1035611Smyers 		    ((struct power_soft_state *)ctx)->softintr_id);
1036611Smyers 
1037611Smyers 	return (AE_OK);
1038611Smyers }
1039611Smyers 
1040622Smyers /*ARGSUSED*/
1041611Smyers static void
1042611Smyers power_acpi_notify_event(ACPI_HANDLE obj, UINT32 val, void *ctx)
1043611Smyers {
1044611Smyers 	if (val == 0x80)
1045622Smyers 		(void) power_acpi_fixed_event(ctx);
1046611Smyers }
1047611Smyers 
1048611Smyers /*
1049611Smyers  *
1050611Smyers  */
1051611Smyers static int
1052611Smyers power_probe_method_button(struct power_soft_state *softsp)
1053611Smyers {
1054611Smyers 	ACPI_HANDLE button_obj;
1055611Smyers 	UINT32 gpe_num;
1056611Smyers 	ACPI_HANDLE gpe_dev;
1057611Smyers 
1058611Smyers 	button_obj = probe_acpi_pwrbutton();
1059611Smyers 	softsp->button_obj = button_obj;	/* remember obj */
1060611Smyers 	if ((button_obj != NULL) &&
1061611Smyers 	    (power_get_prw_gpe(button_obj, &gpe_dev, &gpe_num) == AE_OK) &&
1062611Smyers 	    (AcpiSetGpeType(gpe_dev, gpe_num, ACPI_GPE_TYPE_WAKE_RUN) ==
1063611Smyers 	    AE_OK) &&
1064611Smyers 	    (AcpiEnableGpe(gpe_dev, gpe_num, ACPI_NOT_ISR) == AE_OK) &&
1065611Smyers 	    (AcpiInstallNotifyHandler(button_obj, ACPI_DEVICE_NOTIFY,
1066611Smyers 	    power_acpi_notify_event, (void*)softsp) == AE_OK))
1067611Smyers 		return (1);
1068611Smyers 	return (0);
1069611Smyers }
1070611Smyers 
1071611Smyers /*
1072611Smyers  *
1073611Smyers  */
1074611Smyers static int
1075611Smyers power_probe_fixed_button(struct power_soft_state *softsp)
1076611Smyers {
1077611Smyers 	FADT_DESCRIPTOR *fadt;
1078611Smyers 
1079611Smyers 	if (AcpiGetFirmwareTable(FADT_SIG, 1, ACPI_LOGICAL_ADDRESSING,
1080611Smyers 	    (ACPI_TABLE_HEADER **) &fadt) != AE_OK)
1081611Smyers 		return (0);
1082611Smyers 
1083611Smyers 	if (!fadt->PwrButton) {
1084611Smyers 		if (AcpiInstallFixedEventHandler(ACPI_EVENT_POWER_BUTTON,
1085611Smyers 		    power_acpi_fixed_event, (void *)softsp) == AE_OK)
1086611Smyers 			return (1);
1087611Smyers 	}
1088611Smyers 	return (0);
1089611Smyers }
1090611Smyers 
1091611Smyers 
1092611Smyers /*
1093611Smyers  *
1094611Smyers  */
1095611Smyers static int
1096611Smyers power_attach_acpi(struct power_soft_state *softsp)
1097611Smyers {
1098611Smyers 
1099611Smyers 	/*
1100611Smyers 	 * If we've attached anything already, return an error
1101611Smyers 	 */
1102611Smyers 	if ((softsp->gpe_attached) || (softsp->fixed_attached))
1103611Smyers 		return (DDI_FAILURE);
1104611Smyers 
1105611Smyers 	/*
1106611Smyers 	 * attempt to attach both a fixed-event handler and a GPE
1107611Smyers 	 * handler; remember what we got
1108611Smyers 	 */
1109611Smyers 	softsp->fixed_attached = (power_probe_fixed_button(softsp) != 0);
1110611Smyers 	softsp->gpe_attached = (power_probe_method_button(softsp) != 0);
1111611Smyers 
1112611Smyers 	/*
1113611Smyers 	 * If we've attached anything now, return success
1114611Smyers 	 */
1115611Smyers 	if ((softsp->gpe_attached) || (softsp->fixed_attached))
1116611Smyers 		return (DDI_SUCCESS);
1117611Smyers 
1118611Smyers 	return (DDI_FAILURE);
1119611Smyers }
1120611Smyers 
1121611Smyers /*
1122611Smyers  *
1123611Smyers  */
1124611Smyers static void
1125611Smyers power_detach_acpi(struct power_soft_state *softsp)
1126611Smyers {
1127611Smyers 	if (softsp->gpe_attached) {
1128611Smyers 		if (AcpiRemoveNotifyHandler(softsp->button_obj,
1129611Smyers 		    ACPI_DEVICE_NOTIFY, power_acpi_notify_event) != AE_OK)
1130611Smyers 			cmn_err(CE_WARN, "!power: failed to remove Notify"
1131611Smyers 			    " handler");
1132611Smyers 	}
1133611Smyers 
1134611Smyers 	if (softsp->fixed_attached) {
1135611Smyers 		if (AcpiRemoveFixedEventHandler(ACPI_EVENT_POWER_BUTTON,
1136611Smyers 		    power_acpi_fixed_event) != AE_OK)
1137611Smyers 			cmn_err(CE_WARN, "!power: failed to remove Power"
1138611Smyers 			    " Button handler");
1139611Smyers 	}
1140611Smyers }
1141611Smyers 
1142611Smyers #else
1143611Smyers /*
1144920Sjbeloro  * Code for platforms that have EPIC processor for processing power
1145920Sjbeloro  * button interrupts.
1146920Sjbeloro  */
1147920Sjbeloro static int
1148920Sjbeloro power_setup_epic_regs(dev_info_t *dip, struct power_soft_state *softsp)
1149920Sjbeloro {
1150920Sjbeloro 	ddi_device_acc_attr_t	attr;
1151920Sjbeloro 	uint8_t *reg_base;
1152920Sjbeloro 
1153920Sjbeloro 	attr.devacc_attr_version = DDI_DEVICE_ATTR_V0;
1154920Sjbeloro 	attr.devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC;
1155920Sjbeloro 	attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
1156920Sjbeloro 	if (ddi_regs_map_setup(dip, 0, (caddr_t *)&reg_base,
1157*6573Sphitran 	    EPIC_REGS_OFFSET, EPIC_REGS_LEN, &attr,
1158*6573Sphitran 	    &softsp->power_rhandle) != DDI_SUCCESS) {
1159920Sjbeloro 		return (DDI_FAILURE);
1160920Sjbeloro 	}
1161920Sjbeloro 
1162920Sjbeloro 	softsp->power_btn_reg = reg_base;
1163920Sjbeloro 	softsp->power_regs_mapped = B_TRUE;
1164920Sjbeloro 
1165920Sjbeloro 	/* Clear power button interrupt first */
1166920Sjbeloro 	EPIC_WR(softsp->power_rhandle, softsp->power_btn_reg,
1167*6573Sphitran 	    EPIC_ATOM_INTR_CLEAR);
1168920Sjbeloro 
1169920Sjbeloro 	/* Enable EPIC interrupt for power button single press event */
1170920Sjbeloro 	EPIC_WR(softsp->power_rhandle, softsp->power_btn_reg,
1171*6573Sphitran 	    EPIC_ATOM_INTR_ENABLE);
1172920Sjbeloro 
1173920Sjbeloro 	/*
1174920Sjbeloro 	 * At this point, EPIC interrupt processing is fully initialised.
1175920Sjbeloro 	 */
1176920Sjbeloro 	hasEPIC = B_TRUE;
1177920Sjbeloro 	return (DDI_SUCCESS);
1178920Sjbeloro }
1179920Sjbeloro 
1180920Sjbeloro /*
1181920Sjbeloro  *
1182611Smyers  * power button register definitions for acpi register on m1535d
1183611Smyers  */
1184611Smyers #define	M1535D_PWR_BTN_REG_01		0x1
1185611Smyers #define	M1535D_PWR_BTN_EVENT_FLAG	0x1
1186611Smyers 
1187611Smyers static int
1188611Smyers power_setup_m1535_regs(dev_info_t *dip, struct power_soft_state *softsp)
1189611Smyers {
1190611Smyers 	ddi_device_acc_attr_t	attr;
1191611Smyers 	uint8_t *reg_base;
1192611Smyers 
1193611Smyers 	attr.devacc_attr_version = DDI_DEVICE_ATTR_V0;
1194611Smyers 	attr.devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC;
1195611Smyers 	attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
1196611Smyers 	if (ddi_regs_map_setup(dip, 0, (caddr_t *)&reg_base, 0, 0, &attr,
1197611Smyers 	    &softsp->power_rhandle) != DDI_SUCCESS) {
1198611Smyers 		return (DDI_FAILURE);
1199611Smyers 	}
1200611Smyers 	softsp->power_btn_reg = &reg_base[M1535D_PWR_BTN_REG_01];
1201611Smyers 	softsp->power_btn_bit = M1535D_PWR_BTN_EVENT_FLAG;
1202611Smyers 	softsp->power_regs_mapped = B_TRUE;
1203611Smyers 	return (DDI_SUCCESS);
1204611Smyers }
1205611Smyers 
1206611Smyers /*
1207920Sjbeloro  * MBC Fire/SSI Interrupt Status Register definitions
1208920Sjbeloro  */
1209920Sjbeloro #define	FIRE_SSI_ISR			0x0
1210920Sjbeloro #define	FIRE_SSI_INTR_ENA		0x8
1211920Sjbeloro #define	FIRE_SSI_SHUTDOWN_REQ		0x4
1212920Sjbeloro 
1213920Sjbeloro static int
1214920Sjbeloro power_setup_mbc_regs(dev_info_t *dip, struct power_soft_state *softsp)
1215920Sjbeloro {
1216920Sjbeloro 	ddi_device_acc_attr_t   attr;
1217920Sjbeloro 	uint8_t *reg_base;
1218920Sjbeloro 	ddi_acc_handle_t hdl;
1219920Sjbeloro 	uint8_t reg;
1220920Sjbeloro 
1221920Sjbeloro 	attr.devacc_attr_version = DDI_DEVICE_ATTR_V0;
1222920Sjbeloro 	attr.devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC;
1223920Sjbeloro 	attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
1224920Sjbeloro 	if (ddi_regs_map_setup(dip, 0, (caddr_t *)&reg_base, 0, 0, &attr,
1225*6573Sphitran 	    &softsp->power_rhandle) != DDI_SUCCESS) {
1226920Sjbeloro 		return (DDI_FAILURE);
1227920Sjbeloro 	}
1228920Sjbeloro 	softsp->power_btn_reg = &reg_base[FIRE_SSI_ISR];
1229920Sjbeloro 	softsp->power_btn_bit = FIRE_SSI_SHUTDOWN_REQ;
12301877Sgk73471 	hdl = softsp->power_rhandle;
12311877Sgk73471 	/*
12321877Sgk73471 	 * Clear MBC Fire Power Button interrupt, if set.
12331877Sgk73471 	 */
12341877Sgk73471 	reg = ddi_get8(hdl, softsp->power_btn_reg);
12351877Sgk73471 	if (reg & softsp->power_btn_bit) {
12361877Sgk73471 		reg &= softsp->power_btn_bit;
12371877Sgk73471 		ddi_put8(hdl, softsp->power_btn_reg, reg);
12381877Sgk73471 		(void) ddi_get8(hdl, softsp->power_btn_reg);
12391877Sgk73471 	}
1240920Sjbeloro 	/*
1241920Sjbeloro 	 * Enable MBC Fire Power Button interrupt.
1242920Sjbeloro 	 */
1243920Sjbeloro 	reg = ddi_get8(hdl, &reg_base[FIRE_SSI_INTR_ENA]);
1244920Sjbeloro 	reg |= FIRE_SSI_SHUTDOWN_REQ;
1245920Sjbeloro 	ddi_put8(hdl, &reg_base[FIRE_SSI_INTR_ENA], reg);
1246920Sjbeloro 
1247920Sjbeloro 	softsp->power_regs_mapped = B_TRUE;
1248920Sjbeloro 
1249920Sjbeloro 	return (DDI_SUCCESS);
1250920Sjbeloro }
1251920Sjbeloro 
1252920Sjbeloro /*
1253611Smyers  * Setup register map for the power button
12541084Sjroberts  * NOTE:- we only map registers for platforms if
12551084Sjroberts  * the OBP power device has any of the following
12561084Sjroberts  * properties:
12571084Sjroberts  *
12581084Sjroberts  * a) Boston:  power-device-type set to "SUNW,mbc"
12591084Sjroberts  * b) Seattle: power-device-type set to "SUNW,pic18lf65j10"
12601084Sjroberts  * c) Chalupa: compatible set to "ali1535d+-power"
12611084Sjroberts  *
12621084Sjroberts  * Cases (a) and (b) are defined in FWARC 2005/687.
12631084Sjroberts  * If none of the above conditions are true, then we
12641084Sjroberts  * do not need to map in any registers, and this
12651084Sjroberts  * function can simply return DDI_SUCCESS.
1266611Smyers  */
1267611Smyers static int
1268611Smyers power_setup_regs(struct power_soft_state *softsp)
1269611Smyers {
1270611Smyers 	char	*binding_name;
12711084Sjroberts 	char	*power_type = NULL;
12721084Sjroberts 	int	retval = DDI_SUCCESS;
1273611Smyers 
1274611Smyers 	softsp->power_regs_mapped = B_FALSE;
1275611Smyers 	softsp->power_btn_ioctl = B_FALSE;
1276611Smyers 	binding_name = ddi_binding_name(softsp->dip);
12771084Sjroberts 	if (ddi_prop_lookup_string(DDI_DEV_T_ANY, softsp->dip,
12781084Sjroberts 	    DDI_PROP_DONTPASS, POWER_DEVICE_TYPE,
12791084Sjroberts 	    &power_type) == DDI_PROP_SUCCESS) {
12801084Sjroberts 		if (strcmp(power_type, "SUNW,mbc") == 0) {
12811084Sjroberts 			retval = power_setup_mbc_regs(softsp->dip, softsp);
12821084Sjroberts 		} else if (strcmp(power_type, "SUNW,pic18lf65j10") == 0) {
12831084Sjroberts 			retval = power_setup_epic_regs(softsp->dip, softsp);
12841084Sjroberts 		} else {
12851084Sjroberts 			cmn_err(CE_WARN, "unexpected power-device-type: %s\n",
12861084Sjroberts 			    power_type);
12871084Sjroberts 			retval = DDI_FAILURE;
12881084Sjroberts 		}
12891084Sjroberts 		ddi_prop_free(power_type);
12901084Sjroberts 	} else if (strcmp(binding_name, "ali1535d+-power") == 0) {
12911084Sjroberts 		retval = power_setup_m1535_regs(softsp->dip, softsp);
12921084Sjroberts 	}
1293611Smyers 
1294920Sjbeloro 	/*
12951084Sjroberts 	 * If power-device-type does not exist AND the binding name is not
12961084Sjroberts 	 * "ali1535d+-power", that means there is no additional HW and hence
12971084Sjroberts 	 * no extra processing is necessary. In that case, retval should still
12981084Sjroberts 	 * be set to its initial value of DDI_SUCCESS.
1299920Sjbeloro 	 */
13001084Sjroberts 	return (retval);
1301611Smyers }
1302611Smyers 
1303611Smyers static void
1304611Smyers power_free_regs(struct power_soft_state *softsp)
1305611Smyers {
1306611Smyers 	if (softsp->power_regs_mapped)
1307611Smyers 		ddi_regs_map_free(&softsp->power_rhandle);
1308611Smyers }
1309611Smyers #endif	/* ACPI_POWER_BUTTON */
1310