xref: /onnv-gate/usr/src/uts/common/io/power.c (revision 920:5061227f5943)
1*920Sjbeloro 
2611Smyers /*
3611Smyers  * CDDL HEADER START
4611Smyers  *
5611Smyers  * The contents of this file are subject to the terms of the
6611Smyers  * Common Development and Distribution License, Version 1.0 only
7611Smyers  * (the "License").  You may not use this file except in compliance
8611Smyers  * with the License.
9611Smyers  *
10611Smyers  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
11611Smyers  * or http://www.opensolaris.org/os/licensing.
12611Smyers  * See the License for the specific language governing permissions
13611Smyers  * and limitations under the License.
14611Smyers  *
15611Smyers  * When distributing Covered Code, include this CDDL HEADER in each
16611Smyers  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
17611Smyers  * If applicable, add the following below this CDDL HEADER, with the
18611Smyers  * fields enclosed by brackets "[]" replaced with your own identifying
19611Smyers  * information: Portions Copyright [yyyy] [name of copyright owner]
20611Smyers  *
21611Smyers  * CDDL HEADER END
22611Smyers  */
23611Smyers /*
24611Smyers  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
25611Smyers  * Use is subject to license terms.
26611Smyers  */
27611Smyers 
28611Smyers #pragma ident	"%Z%%M%	%I%	%E% SMI"
29611Smyers 
30611Smyers /*
31611Smyers  *	Power Button Driver
32611Smyers  *
33611Smyers  *	This driver handles interrupt generated by the power button on
34611Smyers  *	platforms with "power" device node which has "button" property.
35611Smyers  *	Currently, these platforms are:
36611Smyers  *
37611Smyers  *		ACPI-enabled x86/x64 platforms
38611Smyers  *		Ultra-5_10, Ultra-80, Sun-Blade-100, Sun-Blade-150,
39611Smyers  *		Sun-Blade-1500, Sun-Blade-2500,
40611Smyers  *		Sun-Fire-V210, Sun-Fire-V240, Netra-240
41611Smyers  *
42611Smyers  *	Only one instance is allowed to attach.  In order to know when
43611Smyers  *	an application that has opened the device is going away, a new
44611Smyers  *	minor clone is created for each open(9E) request.  There are
45611Smyers  *	allocations for creating minor clones between 1 and 255.  The ioctl
46611Smyers  *	interface is defined by pbio(7I) and approved as part of
47611Smyers  *	PSARC/1999/393 case.
48611Smyers  */
49611Smyers 
50611Smyers #include <sys/types.h>
51611Smyers #include <sys/conf.h>
52611Smyers #include <sys/ddi.h>
53611Smyers #include <sys/sunddi.h>
54611Smyers #include <sys/ddi_impldefs.h>
55611Smyers #include <sys/cmn_err.h>
56611Smyers #include <sys/errno.h>
57611Smyers #include <sys/modctl.h>
58611Smyers #include <sys/machsystm.h>
59611Smyers #include <sys/open.h>
60611Smyers #include <sys/stat.h>
61611Smyers #include <sys/poll.h>
62611Smyers #include <sys/pbio.h>
63*920Sjbeloro 
64611Smyers #ifdef	ACPI_POWER_BUTTON
65*920Sjbeloro 
66611Smyers #include <sys/acpi/acpi.h>
67611Smyers #include <sys/acpica.h>
68*920Sjbeloro 
69*920Sjbeloro #else
70*920Sjbeloro 
71*920Sjbeloro #include <sys/epic.h>
72*920Sjbeloro /*
73*920Sjbeloro  * Some #defs that must be here as they differ for power.c
74*920Sjbeloro  * and epic.c
75*920Sjbeloro  */
76*920Sjbeloro #define	EPIC_REGS_OFFSET	0x00
77*920Sjbeloro #define	EPIC_REGS_LEN		0x82
78*920Sjbeloro 
79*920Sjbeloro 
80*920Sjbeloro /*
81*920Sjbeloro  * This flag, which is set for platforms,  that have EPIC processor
82*920Sjbeloro  * to process power button interrupt, helps in executing platform
83*920Sjbeloro  * specific code.
84*920Sjbeloro  */
85*920Sjbeloro static char 	hasEPIC = B_FALSE;
86611Smyers #endif	/* ACPI_POWER_BUTTON */
87611Smyers 
88611Smyers /*
89611Smyers  * Maximum number of clone minors that is allowed.  This value
90611Smyers  * is defined relatively low to save memory.
91611Smyers  */
92611Smyers #define	POWER_MAX_CLONE	256
93611Smyers 
94611Smyers /*
95611Smyers  * Minor number is instance << 8 + clone minor from range 1-255; clone 0
96611Smyers  * is reserved for "original" minor.
97611Smyers  */
98611Smyers #define	POWER_MINOR_TO_CLONE(minor) ((minor) & (POWER_MAX_CLONE - 1))
99611Smyers 
100611Smyers /*
101611Smyers  * Power Button Abort Delay
102611Smyers  */
103611Smyers #define	ABORT_INCREMENT_DELAY	10
104611Smyers 
105611Smyers /*
106611Smyers  * Driver global variables
107611Smyers  */
108611Smyers static void *power_state;
109611Smyers static int power_inst = -1;
110611Smyers 
111611Smyers static hrtime_t	power_button_debounce = NANOSEC/MILLISEC*10;
112611Smyers static hrtime_t power_button_abort_interval = 1.5 * NANOSEC;
113611Smyers static int	power_button_abort_presses = 3;
114611Smyers static int	power_button_abort_enable = 1;
115611Smyers static int	power_button_enable = 1;
116611Smyers 
117611Smyers static int	power_button_pressed = 0;
118611Smyers static int	power_button_cancel = 0;
119611Smyers static int	power_button_timeouts = 0;
120611Smyers static int	timeout_cancel = 0;
121611Smyers static int	additional_presses = 0;
122611Smyers 
123611Smyers /*
124611Smyers  * Function prototypes
125611Smyers  */
126611Smyers static int power_attach(dev_info_t *, ddi_attach_cmd_t);
127611Smyers static int power_detach(dev_info_t *, ddi_detach_cmd_t);
128611Smyers static int power_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
129611Smyers static int power_open(dev_t *, int, int, cred_t *);
130611Smyers static int power_close(dev_t, int, int, cred_t *);
131611Smyers static int power_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
132611Smyers static int power_chpoll(dev_t, short, int, short *, struct pollhead **);
133611Smyers #ifndef	ACPI_POWER_BUTTON
134611Smyers static uint_t power_high_intr(caddr_t);
135611Smyers #endif
136611Smyers static uint_t power_soft_intr(caddr_t);
137611Smyers static uint_t power_issue_shutdown(caddr_t);
138611Smyers static void power_timeout(caddr_t);
139611Smyers static void power_log_message(void);
140611Smyers 
141611Smyers /*
142611Smyers  * Structure used in the driver
143611Smyers  */
144611Smyers struct power_soft_state {
145611Smyers 	dev_info_t	*dip;		/* device info pointer */
146611Smyers 	kmutex_t	power_mutex;	/* mutex lock */
147611Smyers 	kmutex_t	power_intr_mutex; /* interrupt mutex lock */
148611Smyers 	ddi_iblock_cookie_t soft_iblock_cookie; /* holds interrupt cookie */
149611Smyers 	ddi_iblock_cookie_t high_iblock_cookie; /* holds interrupt cookie */
150611Smyers 	ddi_softintr_t	softintr_id;	/* soft interrupt id */
151611Smyers 	uchar_t		clones[POWER_MAX_CLONE]; /* array of minor clones */
152611Smyers 	int		monitor_on;	/* clone monitoring the button event */
153611Smyers 					/* clone 0 indicates no one is */
154611Smyers 					/* monitoring the button event */
155611Smyers 	pollhead_t	pollhd;		/* poll head struct */
156611Smyers 	int		events;		/* bit map of occured events */
157611Smyers 	int		shutdown_pending; /* system shutdown in progress */
158611Smyers #ifdef	ACPI_POWER_BUTTON
159611Smyers 	boolean_t	fixed_attached;	/* true means fixed is attached */
160611Smyers 	boolean_t	gpe_attached;	/* true means GPE is attached */
161611Smyers 	ACPI_HANDLE	button_obj;	/* handle to device power button */
162611Smyers #else
163611Smyers 	ddi_acc_handle_t power_rhandle; /* power button register handle */
164611Smyers 	uint8_t		*power_btn_reg;	/* power button register address */
165611Smyers 	uint8_t		power_btn_bit;	/* power button register bit */
166611Smyers 	boolean_t	power_regs_mapped; /* flag to tell if regs mapped */
167611Smyers 	boolean_t	power_btn_ioctl; /* flag to specify ioctl request */
168611Smyers #endif
169611Smyers };
170611Smyers 
171611Smyers #ifdef	ACPI_POWER_BUTTON
172611Smyers static int power_attach_acpi(struct power_soft_state *softsp);
173611Smyers static void power_detach_acpi(struct power_soft_state *softsp);
174611Smyers static UINT32 power_acpi_fixed_event(void *ctx);
175611Smyers #else
176611Smyers static int power_setup_regs(struct power_soft_state *softsp);
177611Smyers static void power_free_regs(struct power_soft_state *softsp);
178611Smyers #endif	/* ACPI_POWER_BUTTON */
179611Smyers 
180611Smyers /*
181611Smyers  * Configuration data structures
182611Smyers  */
183611Smyers static struct cb_ops power_cb_ops = {
184611Smyers 	power_open,		/* open */
185611Smyers 	power_close,		/* close */
186611Smyers 	nodev,			/* strategy */
187611Smyers 	nodev,			/* print */
188611Smyers 	nodev,			/* dump */
189611Smyers 	nodev,			/* read */
190611Smyers 	nodev,			/* write */
191611Smyers 	power_ioctl,		/* ioctl */
192611Smyers 	nodev,			/* devmap */
193611Smyers 	nodev,			/* mmap */
194611Smyers 	nodev,			/* segmap */
195611Smyers 	power_chpoll,		/* poll */
196611Smyers 	ddi_prop_op,		/* cb_prop_op */
197611Smyers 	NULL,			/* streamtab */
198611Smyers 	D_MP | D_NEW,		/* Driver compatibility flag */
199611Smyers 	CB_REV,			/* rev */
200611Smyers 	nodev,			/* cb_aread */
201611Smyers 	nodev			/* cb_awrite */
202611Smyers };
203611Smyers 
204611Smyers static struct dev_ops power_ops = {
205611Smyers 	DEVO_REV,		/* devo_rev, */
206611Smyers 	0,			/* refcnt */
207611Smyers 	power_getinfo,		/* getinfo */
208611Smyers 	nulldev,		/* identify */
209611Smyers 	nulldev,		/* probe */
210611Smyers 	power_attach,		/* attach */
211611Smyers 	power_detach,		/* detach */
212611Smyers 	nodev,			/* reset */
213611Smyers 	&power_cb_ops,		/* cb_ops */
214611Smyers 	(struct bus_ops *)NULL,	/* bus_ops */
215611Smyers 	NULL			/* power */
216611Smyers };
217611Smyers 
218611Smyers static struct modldrv modldrv = {
219611Smyers 	&mod_driverops,		/* Type of module.  This one is a driver */
220611Smyers 	"power button driver v%I%",	/* name of module */
221611Smyers 	&power_ops,		/* driver ops */
222611Smyers };
223611Smyers 
224611Smyers static struct modlinkage modlinkage = {
225611Smyers 	MODREV_1,
226611Smyers 	(void *)&modldrv,
227611Smyers 	NULL
228611Smyers };
229611Smyers 
230611Smyers /*
231611Smyers  * These are the module initialization routines.
232611Smyers  */
233611Smyers 
234611Smyers int
235611Smyers _init(void)
236611Smyers {
237611Smyers 	int error;
238611Smyers 
239611Smyers 	if ((error = ddi_soft_state_init(&power_state,
240611Smyers 	    sizeof (struct power_soft_state), 0)) != 0)
241611Smyers 		return (error);
242611Smyers 
243611Smyers 	if ((error = mod_install(&modlinkage)) != 0)
244611Smyers 		ddi_soft_state_fini(&power_state);
245611Smyers 
246611Smyers 	return (error);
247611Smyers }
248611Smyers 
249611Smyers int
250611Smyers _fini(void)
251611Smyers {
252611Smyers 	int error;
253611Smyers 
254611Smyers 	if ((error = mod_remove(&modlinkage)) == 0)
255611Smyers 		ddi_soft_state_fini(&power_state);
256611Smyers 
257611Smyers 	return (error);
258611Smyers }
259611Smyers 
260611Smyers int
261611Smyers _info(struct modinfo *modinfop)
262611Smyers {
263611Smyers 	return (mod_info(&modlinkage, modinfop));
264611Smyers }
265611Smyers 
266611Smyers /*ARGSUSED*/
267611Smyers static int
268611Smyers power_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg,
269611Smyers     void **result)
270611Smyers {
271611Smyers 	struct power_soft_state *softsp;
272611Smyers 
273611Smyers 	if (power_inst == -1)
274611Smyers 		return (DDI_FAILURE);
275611Smyers 
276611Smyers 	switch (infocmd) {
277611Smyers 	case DDI_INFO_DEVT2DEVINFO:
278611Smyers 		if ((softsp = ddi_get_soft_state(power_state, power_inst))
279611Smyers 		    == NULL)
280611Smyers 			return (DDI_FAILURE);
281611Smyers 		*result = (void *)softsp->dip;
282611Smyers 		return (DDI_SUCCESS);
283611Smyers 
284611Smyers 	case DDI_INFO_DEVT2INSTANCE:
285611Smyers 		*result = (void *)(uintptr_t)power_inst;
286611Smyers 		return (DDI_SUCCESS);
287611Smyers 
288611Smyers 	default:
289611Smyers 		return (DDI_FAILURE);
290611Smyers 	}
291611Smyers }
292611Smyers 
293611Smyers static int
294611Smyers power_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
295611Smyers {
296611Smyers 	struct power_soft_state *softsp;
297611Smyers 
298611Smyers 	switch (cmd) {
299611Smyers 	case DDI_ATTACH:
300611Smyers 		break;
301611Smyers 	case DDI_RESUME:
302611Smyers 		return (DDI_SUCCESS);
303611Smyers 	default:
304611Smyers 		return (DDI_FAILURE);
305611Smyers 	}
306611Smyers 
307611Smyers 	/*
308611Smyers 	 * If the power node doesn't have "button" property, quietly
309611Smyers 	 * fail to attach.
310611Smyers 	 */
311611Smyers 	if (ddi_prop_exists(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
312611Smyers 	    "button") == 0)
313611Smyers 		return (DDI_FAILURE);
314611Smyers 
315611Smyers 	if (power_inst != -1)
316611Smyers 		return (DDI_FAILURE);
317611Smyers 
318611Smyers 	power_inst = ddi_get_instance(dip);
319611Smyers 
320611Smyers 	if (ddi_soft_state_zalloc(power_state, power_inst) != DDI_SUCCESS)
321611Smyers 		return (DDI_FAILURE);
322611Smyers 
323611Smyers 	if (ddi_create_minor_node(dip, "power_button", S_IFCHR,
324611Smyers 	    (power_inst << 8) + 0, "ddi_power_button", 0) != DDI_SUCCESS)
325611Smyers 		return (DDI_FAILURE);
326611Smyers 
327611Smyers 	softsp = ddi_get_soft_state(power_state, power_inst);
328611Smyers 	softsp->dip = dip;
329611Smyers 
330611Smyers #ifdef	ACPI_POWER_BUTTON
331622Smyers 	(void) power_attach_acpi(softsp);
332611Smyers #else
333611Smyers 	if (power_setup_regs(softsp) != DDI_SUCCESS) {
334611Smyers 		cmn_err(CE_WARN, "power_attach: failed to setup registers");
335611Smyers 		goto error;
336611Smyers 	}
337611Smyers 
338611Smyers 	if (ddi_get_iblock_cookie(dip, 0,
339611Smyers 	    &softsp->high_iblock_cookie) != DDI_SUCCESS) {
340611Smyers 		cmn_err(CE_WARN, "power_attach: ddi_get_soft_iblock_cookie "
341611Smyers 		    "failed.");
342611Smyers 		goto error;
343611Smyers 	}
344611Smyers 	mutex_init(&softsp->power_intr_mutex, NULL, MUTEX_DRIVER,
345611Smyers 	    softsp->high_iblock_cookie);
346611Smyers 
347611Smyers 	if (ddi_add_intr(dip, 0, &softsp->high_iblock_cookie, NULL,
348611Smyers 	    power_high_intr, (caddr_t)softsp) != DDI_SUCCESS) {
349611Smyers 		cmn_err(CE_WARN, "power_attach: failed to add high-level "
350611Smyers 		    " interrupt handler.");
351611Smyers 		mutex_destroy(&softsp->power_intr_mutex);
352611Smyers 		goto error;
353611Smyers 	}
354611Smyers #endif	/* ACPI_POWER_BUTTON */
355611Smyers 
356611Smyers 	if (ddi_get_soft_iblock_cookie(dip, DDI_SOFTINT_LOW,
357611Smyers 	    &softsp->soft_iblock_cookie) != DDI_SUCCESS) {
358611Smyers 		cmn_err(CE_WARN, "power_attach: ddi_get_soft_iblock_cookie "
359611Smyers 		    "failed.");
360611Smyers 		mutex_destroy(&softsp->power_intr_mutex);
361611Smyers 		ddi_remove_intr(dip, 0, NULL);
362611Smyers 		goto error;
363611Smyers 	}
364611Smyers 
365611Smyers 	mutex_init(&softsp->power_mutex, NULL, MUTEX_DRIVER,
366611Smyers 	    (void *)softsp->soft_iblock_cookie);
367611Smyers 
368611Smyers 	if (ddi_add_softintr(dip, DDI_SOFTINT_LOW, &softsp->softintr_id,
369611Smyers 	    NULL, NULL, power_soft_intr, (caddr_t)softsp) != DDI_SUCCESS) {
370611Smyers 		cmn_err(CE_WARN, "power_attach: failed to add soft "
371611Smyers 		    "interrupt handler.");
372611Smyers 		mutex_destroy(&softsp->power_mutex);
373611Smyers 		mutex_destroy(&softsp->power_intr_mutex);
374611Smyers 		ddi_remove_intr(dip, 0, NULL);
375611Smyers 		goto error;
376611Smyers 	}
377611Smyers 
378611Smyers 	ddi_report_dev(dip);
379611Smyers 
380611Smyers 	return (DDI_SUCCESS);
381611Smyers 
382611Smyers error:
383611Smyers #ifdef	ACPI_POWER_BUTTON
384611Smyers 	/*
385611Smyers 	 * detach ACPI power button
386611Smyers 	 */
387611Smyers 	power_detach_acpi(softsp);
388611Smyers #else
389611Smyers 	power_free_regs(softsp);
390611Smyers #endif	/* ACPI_POWER_BUTTON */
391611Smyers 	ddi_remove_minor_node(dip, "power_button");
392611Smyers 	ddi_soft_state_free(power_state, power_inst);
393611Smyers 	return (DDI_FAILURE);
394611Smyers }
395611Smyers 
396611Smyers /*ARGSUSED*/
397611Smyers /*
398611Smyers  * This driver doesn't detach.
399611Smyers  */
400611Smyers static int
401611Smyers power_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
402611Smyers {
403611Smyers 	/*
404611Smyers 	 * Since the "power" node has "reg" property, as part of
405611Smyers 	 * the suspend operation, detach(9E) entry point is called.
406611Smyers 	 * There is no state to save, since this register is used
407611Smyers 	 * by OBP to power off the system and the state of the
408611Smyers 	 * power off is preserved by hardware.
409611Smyers 	 */
410611Smyers 	return ((cmd == DDI_SUSPEND) ? DDI_SUCCESS :
411611Smyers 	    DDI_FAILURE);
412611Smyers }
413611Smyers 
414*920Sjbeloro 
415611Smyers #ifndef	ACPI_POWER_BUTTON
416611Smyers /*
417611Smyers  * Handler for the high-level interrupt.
418611Smyers  */
419611Smyers static uint_t
420611Smyers power_high_intr(caddr_t arg)
421611Smyers {
422611Smyers 	struct power_soft_state *softsp = (struct power_soft_state *)arg;
423611Smyers 	ddi_acc_handle_t hdl = softsp->power_rhandle;
424611Smyers 	uint8_t		reg;
425611Smyers 
426611Smyers 	hrtime_t tstamp;
427611Smyers 	static hrtime_t o_tstamp = 0;
428611Smyers 	static hrtime_t power_button_tstamp = 0;
429611Smyers 	static int power_button_cnt;
430611Smyers 
431611Smyers 	if (softsp->power_regs_mapped) {
432611Smyers 		mutex_enter(&softsp->power_intr_mutex);
433*920Sjbeloro 
434*920Sjbeloro 		/* Check if power button interrupt is delivered by EPIC HW */
435*920Sjbeloro 		if (hasEPIC) {
436*920Sjbeloro 			/* read isr - first issue command */
437*920Sjbeloro 			EPIC_WR(hdl, softsp->power_btn_reg,
438*920Sjbeloro 				EPIC_ATOM_INTR_READ);
439*920Sjbeloro 			/* next, read the reg */
440*920Sjbeloro 			EPIC_RD(hdl, softsp->power_btn_reg, reg);
441*920Sjbeloro 
442*920Sjbeloro 			if (reg & EPIC_FIRE_INTERRUPT) {  /* PB pressed */
443*920Sjbeloro 				/* clear the interrupt */
444*920Sjbeloro 				EPIC_WR(hdl, softsp->power_btn_reg,
445*920Sjbeloro 					EPIC_ATOM_INTR_CLEAR);
446*920Sjbeloro 			} else {
447*920Sjbeloro 				if (!softsp->power_btn_ioctl) {
448*920Sjbeloro 					mutex_exit(&softsp->power_intr_mutex);
449*920Sjbeloro 					return (DDI_INTR_CLAIMED);
450*920Sjbeloro 				}
451*920Sjbeloro 				softsp->power_btn_ioctl = B_FALSE;
452*920Sjbeloro 			}
453611Smyers 		} else {
454*920Sjbeloro 			reg = ddi_get8(hdl, softsp->power_btn_reg);
455*920Sjbeloro 			if (reg & softsp->power_btn_bit) {
456*920Sjbeloro 				reg &= softsp->power_btn_bit;
457*920Sjbeloro 				ddi_put8(hdl, softsp->power_btn_reg, reg);
458*920Sjbeloro 				(void) ddi_get8(hdl, softsp->power_btn_reg);
459*920Sjbeloro 			} else {
460*920Sjbeloro 				if (!softsp->power_btn_ioctl) {
461*920Sjbeloro 					mutex_exit(&softsp->power_intr_mutex);
462*920Sjbeloro 					return (DDI_INTR_CLAIMED);
463*920Sjbeloro 				}
464*920Sjbeloro 				softsp->power_btn_ioctl = B_FALSE;
465611Smyers 			}
466611Smyers 		}
467611Smyers 		mutex_exit(&softsp->power_intr_mutex);
468611Smyers 	}
469611Smyers 
470611Smyers 	tstamp = gethrtime();
471611Smyers 
472611Smyers 	/* need to deal with power button debounce */
473611Smyers 	if (o_tstamp && (tstamp - o_tstamp) < power_button_debounce) {
474611Smyers 		o_tstamp = tstamp;
475611Smyers 		return (DDI_INTR_CLAIMED);
476611Smyers 	}
477611Smyers 	o_tstamp = tstamp;
478611Smyers 
479611Smyers 	power_button_cnt++;
480611Smyers 
481611Smyers 	mutex_enter(&softsp->power_intr_mutex);
482611Smyers 	power_button_pressed++;
483611Smyers 	mutex_exit(&softsp->power_intr_mutex);
484611Smyers 
485611Smyers 	/*
486611Smyers 	 * If power button abort is enabled and power button was pressed
487611Smyers 	 * power_button_abort_presses times within power_button_abort_interval
488611Smyers 	 * then call abort_sequence_enter();
489611Smyers 	 */
490611Smyers 	if (power_button_abort_enable) {
491611Smyers 		if (power_button_abort_presses == 1 ||
492611Smyers 		    tstamp < (power_button_tstamp +
493611Smyers 		    power_button_abort_interval)) {
494611Smyers 			if (power_button_cnt == power_button_abort_presses) {
495611Smyers 				mutex_enter(&softsp->power_intr_mutex);
496611Smyers 				power_button_cancel += power_button_timeouts;
497611Smyers 				power_button_pressed = 0;
498611Smyers 				mutex_exit(&softsp->power_intr_mutex);
499611Smyers 				power_button_cnt = 0;
500611Smyers 				abort_sequence_enter("Power Button Abort");
501611Smyers 				return (DDI_INTR_CLAIMED);
502611Smyers 			}
503611Smyers 		} else {
504611Smyers 			power_button_cnt = 1;
505611Smyers 			power_button_tstamp = tstamp;
506611Smyers 		}
507611Smyers 	}
508611Smyers 
509611Smyers 	if (!power_button_enable)
510611Smyers 		return (DDI_INTR_CLAIMED);
511611Smyers 
512611Smyers 	/* post softint to issue timeout for power button action */
513611Smyers 	if (softsp->softintr_id != NULL)
514611Smyers 		ddi_trigger_softintr(softsp->softintr_id);
515611Smyers 
516611Smyers 	return (DDI_INTR_CLAIMED);
517611Smyers }
518611Smyers #endif	/* ifndef ACPI_POWER_BUTTON */
519611Smyers 
520611Smyers /*
521611Smyers  * Handle the softints....
522611Smyers  *
523611Smyers  * If only one softint is posted for several button presses, record
524611Smyers  * the number of additional presses just incase this was actually not quite
525611Smyers  * an Abort sequence so that we can log this event later.
526611Smyers  *
527611Smyers  * Issue a timeout with a duration being a fraction larger than
528611Smyers  * the specified Abort interval inorder to perform a power down if required.
529611Smyers  */
530611Smyers static uint_t
531611Smyers power_soft_intr(caddr_t arg)
532611Smyers {
533611Smyers 	struct power_soft_state *softsp = (struct power_soft_state *)arg;
534611Smyers 
535611Smyers 	if (!power_button_abort_enable)
536611Smyers 		return (power_issue_shutdown(arg));
537611Smyers 
538611Smyers 	mutex_enter(&softsp->power_intr_mutex);
539611Smyers 	if (!power_button_pressed) {
540611Smyers 		mutex_exit(&softsp->power_intr_mutex);
541611Smyers 		return (DDI_INTR_CLAIMED);
542611Smyers 	}
543611Smyers 
544611Smyers 	/*
545611Smyers 	 * Schedule a timeout to do the necessary
546611Smyers 	 * work for shutdown, only one timeout for
547611Smyers 	 * n presses if power button was pressed
548611Smyers 	 * more than once before softint fired
549611Smyers 	 */
550611Smyers 	if (power_button_pressed > 1)
551611Smyers 		additional_presses += power_button_pressed - 1;
552611Smyers 
553611Smyers 	timeout_cancel = 0;
554611Smyers 	power_button_pressed = 0;
555611Smyers 	power_button_timeouts++;
556611Smyers 	mutex_exit(&softsp->power_intr_mutex);
557611Smyers 	(void) timeout((void(*)(void *))power_timeout,
558611Smyers 	    softsp, NSEC_TO_TICK(power_button_abort_interval) +
559611Smyers 	    ABORT_INCREMENT_DELAY);
560611Smyers 
561611Smyers 	return (DDI_INTR_CLAIMED);
562611Smyers }
563611Smyers 
564611Smyers /*
565611Smyers  * Upon receiving a timeout the following is determined:
566611Smyers  *
567611Smyers  * If an  Abort sequence was issued, then we cancel all outstanding timeouts
568611Smyers  * and additional presses prior to the Abort sequence.
569611Smyers  *
570611Smyers  * If we had multiple timeouts issued and the abort sequence was not met,
571611Smyers  * then we had more than one button press to power down the machine. We
572611Smyers  * were probably trying to issue an abort. So log a message indicating this
573611Smyers  * and cancel all outstanding timeouts.
574611Smyers  *
575611Smyers  * If we had just one timeout and the abort sequence was not met then
576611Smyers  * we really did want to power down the machine, so call power_issue_shutdown()
577611Smyers  * to do the work and schedule a power down
578611Smyers  */
579611Smyers static void
580611Smyers power_timeout(caddr_t arg)
581611Smyers {
582611Smyers 	struct power_soft_state *softsp = (struct power_soft_state *)arg;
583611Smyers 	static int first = 0;
584611Smyers 
585611Smyers 	/*
586611Smyers 	 * Abort was generated cancel all outstanding power
587611Smyers 	 * button timeouts
588611Smyers 	 */
589611Smyers 	mutex_enter(&softsp->power_intr_mutex);
590611Smyers 	if (power_button_cancel) {
591611Smyers 		power_button_cancel--;
592611Smyers 		power_button_timeouts--;
593611Smyers 		if (!first) {
594611Smyers 			first++;
595611Smyers 			additional_presses = 0;
596611Smyers 		}
597611Smyers 		mutex_exit(&softsp->power_intr_mutex);
598611Smyers 		return;
599611Smyers 	}
600611Smyers 	first = 0;
601611Smyers 
602611Smyers 	/*
603611Smyers 	 * We get here if the timeout(s) have fired and they were
604611Smyers 	 * not issued prior to an abort.
605611Smyers 	 *
606611Smyers 	 * If we had more than one press in the interval we were
607611Smyers 	 * probably trying to issue an abort, but didnt press the
608611Smyers 	 * required number within the interval. Hence cancel all
609611Smyers 	 * timeouts and do not continue towards shutdown.
610611Smyers 	 */
611611Smyers 	if (!timeout_cancel) {
612611Smyers 		timeout_cancel = power_button_timeouts +
613611Smyers 		    additional_presses;
614611Smyers 
615611Smyers 		power_button_timeouts--;
616611Smyers 		if (!power_button_timeouts)
617611Smyers 			additional_presses = 0;
618611Smyers 
619611Smyers 		if (timeout_cancel > 1) {
620611Smyers 			mutex_exit(&softsp->power_intr_mutex);
621611Smyers 			cmn_err(CE_NOTE, "Power Button pressed "
622611Smyers 			    "%d times, cancelling all requests",
623611Smyers 			    timeout_cancel);
624611Smyers 			return;
625611Smyers 		}
626611Smyers 		mutex_exit(&softsp->power_intr_mutex);
627611Smyers 
628611Smyers 		/* Go and do the work to request shutdown */
629611Smyers 		(void) power_issue_shutdown((caddr_t)softsp);
630611Smyers 		return;
631611Smyers 	}
632611Smyers 
633611Smyers 	power_button_timeouts--;
634611Smyers 	if (!power_button_timeouts)
635611Smyers 		additional_presses = 0;
636611Smyers 	mutex_exit(&softsp->power_intr_mutex);
637611Smyers }
638611Smyers 
639611Smyers #ifdef ACPI_POWER_BUTTON
640611Smyers static void
641611Smyers do_shutdown(void)
642611Smyers {
643611Smyers 	proc_t *initpp;
644611Smyers 
645611Smyers 	/*
646611Smyers 	 * If we're still booting and init(1) isn't set up yet, simply halt.
647611Smyers 	 */
648611Smyers 	mutex_enter(&pidlock);
649611Smyers 	initpp = prfind(P_INITPID);
650611Smyers 	mutex_exit(&pidlock);
651611Smyers 	if (initpp == NULL) {
652611Smyers 		extern void halt(char *);
653611Smyers 		halt("Power off the System");   /* just in case */
654611Smyers 	}
655611Smyers 
656611Smyers 	/*
657611Smyers 	 * else, graceful shutdown with inittab and all getting involved
658611Smyers 	 */
659611Smyers 	psignal(initpp, SIGPWR);
660611Smyers }
661611Smyers #endif
662611Smyers 
663611Smyers static uint_t
664611Smyers power_issue_shutdown(caddr_t arg)
665611Smyers {
666611Smyers 	struct power_soft_state *softsp = (struct power_soft_state *)arg;
667611Smyers 
668611Smyers 	mutex_enter(&softsp->power_mutex);
669611Smyers 	softsp->events |= PB_BUTTON_PRESS;
670611Smyers 	if (softsp->monitor_on != 0) {
671611Smyers 		mutex_exit(&softsp->power_mutex);
672611Smyers 		pollwakeup(&softsp->pollhd, POLLRDNORM);
673611Smyers 		pollwakeup(&softsp->pollhd, POLLIN);
674611Smyers 		return (DDI_INTR_CLAIMED);
675611Smyers 	}
676611Smyers 
677611Smyers 	if (!softsp->shutdown_pending) {
678611Smyers 		cmn_err(CE_WARN, "Power off requested from power button or "
679611Smyers 		    "SC, powering down the system!");
680611Smyers 		softsp->shutdown_pending = 1;
681611Smyers 		do_shutdown();
682611Smyers 
683611Smyers 		/*
684611Smyers 		 * Wait a while for "do_shutdown()" to shut down the system
685611Smyers 		 * before logging an error message.
686611Smyers 		 */
687611Smyers 		(void) timeout((void(*)(void *))power_log_message, NULL,
688611Smyers 		    100 * hz);
689611Smyers 	}
690611Smyers 	mutex_exit(&softsp->power_mutex);
691611Smyers 
692611Smyers 	return (DDI_INTR_CLAIMED);
693611Smyers }
694611Smyers 
695611Smyers /*
696611Smyers  * Open the device.
697611Smyers  */
698611Smyers /*ARGSUSED*/
699611Smyers static int
700611Smyers power_open(dev_t *devp, int openflags, int otyp, cred_t *credp)
701611Smyers {
702611Smyers 	struct power_soft_state *softsp;
703611Smyers 	int clone;
704611Smyers 
705611Smyers 	if (otyp != OTYP_CHR)
706611Smyers 		return (EINVAL);
707611Smyers 
708611Smyers 	if ((softsp = ddi_get_soft_state(power_state, power_inst)) ==
709611Smyers 	    NULL)
710611Smyers 		return (ENXIO);
711611Smyers 
712611Smyers 	mutex_enter(&softsp->power_mutex);
713611Smyers 	for (clone = 1; clone < POWER_MAX_CLONE; clone++)
714611Smyers 		if (!softsp->clones[clone])
715611Smyers 			break;
716611Smyers 
717611Smyers 	if (clone == POWER_MAX_CLONE) {
718611Smyers 		cmn_err(CE_WARN, "power_open: No more allocation left "
719611Smyers 		    "to create a clone minor.");
720611Smyers 		mutex_exit(&softsp->power_mutex);
721611Smyers 		return (ENXIO);
722611Smyers 	}
723611Smyers 
724611Smyers 	*devp = makedevice(getmajor(*devp), (power_inst << 8) + clone);
725611Smyers 	softsp->clones[clone] = 1;
726611Smyers 	mutex_exit(&softsp->power_mutex);
727611Smyers 
728611Smyers 	return (0);
729611Smyers }
730611Smyers 
731611Smyers /*
732611Smyers  * Close the device.
733611Smyers  */
734611Smyers /*ARGSUSED*/
735611Smyers static  int
736611Smyers power_close(dev_t dev, int openflags, int otyp, cred_t *credp)
737611Smyers {
738611Smyers 	struct power_soft_state *softsp;
739611Smyers 	int clone;
740611Smyers 
741611Smyers 	if (otyp != OTYP_CHR)
742611Smyers 		return (EINVAL);
743611Smyers 
744611Smyers 	if ((softsp = ddi_get_soft_state(power_state, power_inst)) ==
745611Smyers 	    NULL)
746611Smyers 		return (ENXIO);
747611Smyers 
748611Smyers 	clone = POWER_MINOR_TO_CLONE(getminor(dev));
749611Smyers 	mutex_enter(&softsp->power_mutex);
750611Smyers 	if (softsp->monitor_on == clone)
751611Smyers 		softsp->monitor_on = 0;
752611Smyers 	softsp->clones[clone] = 0;
753611Smyers 	mutex_exit(&softsp->power_mutex);
754611Smyers 
755611Smyers 	return (0);
756611Smyers }
757611Smyers 
758611Smyers /*ARGSUSED*/
759611Smyers static  int
760611Smyers power_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cred_p,
761611Smyers     int *rval_p)
762611Smyers {
763611Smyers 	struct power_soft_state *softsp;
764611Smyers 	int clone;
765611Smyers 
766611Smyers 	if ((softsp = ddi_get_soft_state(power_state, power_inst)) ==
767611Smyers 	    NULL)
768611Smyers 		return (ENXIO);
769611Smyers 
770611Smyers 	clone = POWER_MINOR_TO_CLONE(getminor(dev));
771611Smyers 	switch (cmd) {
772611Smyers 	case PB_BEGIN_MONITOR:
773611Smyers 		mutex_enter(&softsp->power_mutex);
774611Smyers 		if (softsp->monitor_on) {
775611Smyers 			mutex_exit(&softsp->power_mutex);
776611Smyers 			return (EBUSY);
777611Smyers 		}
778611Smyers 		softsp->monitor_on = clone;
779611Smyers 		mutex_exit(&softsp->power_mutex);
780611Smyers 		return (0);
781611Smyers 
782611Smyers 	case PB_END_MONITOR:
783611Smyers 		mutex_enter(&softsp->power_mutex);
784611Smyers 
785611Smyers 		/*
786611Smyers 		 * If PB_END_MONITOR is called without first
787611Smyers 		 * calling PB_BEGIN_MONITOR, an error will be
788611Smyers 		 * returned.
789611Smyers 		 */
790611Smyers 		if (!softsp->monitor_on) {
791611Smyers 			mutex_exit(&softsp->power_mutex);
792611Smyers 			return (ENXIO);
793611Smyers 		}
794611Smyers 
795611Smyers 		/*
796611Smyers 		 * This clone is not monitoring the button.
797611Smyers 		 */
798611Smyers 		if (softsp->monitor_on != clone) {
799611Smyers 			mutex_exit(&softsp->power_mutex);
800611Smyers 			return (EINVAL);
801611Smyers 		}
802611Smyers 		softsp->monitor_on = 0;
803611Smyers 		mutex_exit(&softsp->power_mutex);
804611Smyers 		return (0);
805611Smyers 
806611Smyers 	case PB_GET_EVENTS:
807611Smyers 		mutex_enter(&softsp->power_mutex);
808611Smyers 		if (ddi_copyout((void *)&softsp->events, (void *)arg,
809611Smyers 		    sizeof (int), mode) != 0) {
810611Smyers 			mutex_exit(&softsp->power_mutex);
811611Smyers 			return (EFAULT);
812611Smyers 		}
813611Smyers 
814611Smyers 		/*
815611Smyers 		 * This ioctl returned the events detected since last
816611Smyers 		 * call.  Note that any application can get the events
817611Smyers 		 * and clear the event register.
818611Smyers 		 */
819611Smyers 		softsp->events = 0;
820611Smyers 		mutex_exit(&softsp->power_mutex);
821611Smyers 		return (0);
822611Smyers 
823611Smyers 	/*
824611Smyers 	 * This ioctl is used by the test suite.
825611Smyers 	 */
826611Smyers 	case PB_CREATE_BUTTON_EVENT:
827611Smyers #ifdef	ACPI_POWER_BUTTON
828611Smyers 		(UINT32)power_acpi_fixed_event((void *)softsp);
829611Smyers #else
830611Smyers 		if (softsp->power_regs_mapped) {
831611Smyers 			mutex_enter(&softsp->power_intr_mutex);
832611Smyers 			softsp->power_btn_ioctl = B_TRUE;
833611Smyers 			mutex_exit(&softsp->power_intr_mutex);
834611Smyers 		}
835611Smyers 		(void) power_high_intr((caddr_t)softsp);
836611Smyers #endif	/* ACPI_POWER_BUTTON */
837611Smyers 		return (0);
838611Smyers 
839611Smyers 	default:
840611Smyers 		return (ENOTTY);
841611Smyers 	}
842611Smyers }
843611Smyers 
844611Smyers /*ARGSUSED*/
845611Smyers static int
846611Smyers power_chpoll(dev_t dev, short events, int anyyet,
847611Smyers     short *reventsp, struct pollhead **phpp)
848611Smyers {
849611Smyers 	struct power_soft_state *softsp;
850611Smyers 
851611Smyers 	if ((softsp = ddi_get_soft_state(power_state, power_inst)) == NULL)
852611Smyers 		return (ENXIO);
853611Smyers 
854611Smyers 	mutex_enter(&softsp->power_mutex);
855611Smyers 	*reventsp = 0;
856611Smyers 	if (softsp->events)
857611Smyers 		*reventsp = POLLRDNORM|POLLIN;
858611Smyers 	else {
859611Smyers 		if (!anyyet)
860611Smyers 			*phpp = &softsp->pollhd;
861611Smyers 	}
862611Smyers 	mutex_exit(&softsp->power_mutex);
863611Smyers 
864611Smyers 	return (0);
865611Smyers }
866611Smyers 
867611Smyers static void
868611Smyers power_log_message(void)
869611Smyers {
870611Smyers 	struct power_soft_state *softsp;
871611Smyers 
872611Smyers 	if ((softsp = ddi_get_soft_state(power_state, power_inst)) == NULL) {
873611Smyers 		cmn_err(CE_WARN, "Failed to get internal state!");
874611Smyers 		return;
875611Smyers 	}
876611Smyers 
877611Smyers 	mutex_enter(&softsp->power_mutex);
878611Smyers 	softsp->shutdown_pending = 0;
879611Smyers 	cmn_err(CE_WARN, "Failed to shut down the system!");
880611Smyers 	mutex_exit(&softsp->power_mutex);
881611Smyers }
882611Smyers 
883611Smyers #ifdef	ACPI_POWER_BUTTON
884611Smyers /*
885611Smyers  * Given a handle to a device object, locate a _PRW object
886611Smyers  * if present and fetch the GPE info for this device object
887611Smyers  */
888611Smyers static ACPI_STATUS
889611Smyers power_get_prw_gpe(ACPI_HANDLE dev, ACPI_HANDLE *gpe_dev, UINT32 *gpe_num)
890611Smyers {
891611Smyers 	ACPI_BUFFER buf;
892611Smyers 	ACPI_STATUS status;
893611Smyers 	ACPI_HANDLE prw;
894611Smyers 	ACPI_OBJECT *gpe;
895611Smyers 
896611Smyers 	/*
897611Smyers 	 * Evaluate _PRW if present
898611Smyers 	 */
899611Smyers 	status = AcpiGetHandle(dev, "_PRW", &prw);
900611Smyers 	if (status != AE_OK)
901611Smyers 		return (status);
902611Smyers 	buf.Length = ACPI_ALLOCATE_BUFFER;
903611Smyers 	status = AcpiEvaluateObjectTyped(prw, NULL, NULL, &buf,
904611Smyers 	    ACPI_TYPE_PACKAGE);
905611Smyers 	if (status != AE_OK)
906611Smyers 		return (status);
907611Smyers 
908611Smyers 	/*
909611Smyers 	 * Sanity-check the package; need at least two elements
910611Smyers 	 */
911611Smyers 	status = AE_ERROR;
912611Smyers 	if (((ACPI_OBJECT *)buf.Pointer)->Package.Count < 2)
913611Smyers 		goto done;
914611Smyers 
915611Smyers 	gpe = &((ACPI_OBJECT *)buf.Pointer)->Package.Elements[0];
916611Smyers 	if (gpe->Type == ACPI_TYPE_INTEGER) {
917611Smyers 		*gpe_dev = NULL;
918611Smyers 		*gpe_num = gpe->Integer.Value;
919611Smyers 		status = AE_OK;
920611Smyers 	} else if (gpe->Type == ACPI_TYPE_PACKAGE) {
921611Smyers 		if ((gpe->Package.Count != 2) ||
922611Smyers 		    (gpe->Package.Elements[0].Type != ACPI_TYPE_DEVICE) ||
923611Smyers 		    (gpe->Package.Elements[1].Type != ACPI_TYPE_INTEGER))
924611Smyers 			goto done;
925611Smyers 		*gpe_dev = gpe->Package.Elements[0].Reference.Handle;
926611Smyers 		*gpe_num = gpe->Package.Elements[1].Integer.Value;
927611Smyers 		status = AE_OK;
928611Smyers 	}
929611Smyers 
930611Smyers done:
931611Smyers 	AcpiOsFree(buf.Pointer);
932611Smyers 	return (status);
933611Smyers }
934611Smyers 
935611Smyers 
936611Smyers /*
937611Smyers  *
938611Smyers  */
939622Smyers /*ARGSUSED*/
940611Smyers static ACPI_STATUS
941611Smyers acpi_device(ACPI_HANDLE obj, UINT32 nesting, void *context, void **rv)
942611Smyers {
943622Smyers 
944611Smyers 	*((ACPI_HANDLE *)context) = obj;
945611Smyers 	return (AE_OK);
946611Smyers }
947611Smyers 
948611Smyers /*
949611Smyers  *
950611Smyers  */
951611Smyers static ACPI_HANDLE
952611Smyers probe_acpi_pwrbutton()
953611Smyers {
954611Smyers 	ACPI_HANDLE obj = NULL;
955611Smyers 
956622Smyers 	(void) AcpiGetDevices("PNP0C0C", acpi_device, (void *)&obj, NULL);
957611Smyers 	return (obj);
958611Smyers }
959611Smyers 
960611Smyers static UINT32
961611Smyers power_acpi_fixed_event(void *ctx)
962611Smyers {
963611Smyers 
964611Smyers 	mutex_enter(&((struct power_soft_state *)ctx)->power_intr_mutex);
965611Smyers 	power_button_pressed++;
966611Smyers 	mutex_exit(&((struct power_soft_state *)ctx)->power_intr_mutex);
967611Smyers 
968611Smyers 	/* post softint to issue timeout for power button action */
969611Smyers 	if (((struct power_soft_state *)ctx)->softintr_id != NULL)
970611Smyers 		ddi_trigger_softintr(
971611Smyers 		    ((struct power_soft_state *)ctx)->softintr_id);
972611Smyers 
973611Smyers 	return (AE_OK);
974611Smyers }
975611Smyers 
976622Smyers /*ARGSUSED*/
977611Smyers static void
978611Smyers power_acpi_notify_event(ACPI_HANDLE obj, UINT32 val, void *ctx)
979611Smyers {
980611Smyers 	if (val == 0x80)
981622Smyers 		(void) power_acpi_fixed_event(ctx);
982611Smyers }
983611Smyers 
984611Smyers /*
985611Smyers  *
986611Smyers  */
987611Smyers static int
988611Smyers power_probe_method_button(struct power_soft_state *softsp)
989611Smyers {
990611Smyers 	ACPI_HANDLE button_obj;
991611Smyers 	UINT32 gpe_num;
992611Smyers 	ACPI_HANDLE gpe_dev;
993611Smyers 
994611Smyers 	button_obj = probe_acpi_pwrbutton();
995611Smyers 	softsp->button_obj = button_obj;	/* remember obj */
996611Smyers 	if ((button_obj != NULL) &&
997611Smyers 	    (power_get_prw_gpe(button_obj, &gpe_dev, &gpe_num) == AE_OK) &&
998611Smyers 	    (AcpiSetGpeType(gpe_dev, gpe_num, ACPI_GPE_TYPE_WAKE_RUN) ==
999611Smyers 	    AE_OK) &&
1000611Smyers 	    (AcpiEnableGpe(gpe_dev, gpe_num, ACPI_NOT_ISR) == AE_OK) &&
1001611Smyers 	    (AcpiInstallNotifyHandler(button_obj, ACPI_DEVICE_NOTIFY,
1002611Smyers 	    power_acpi_notify_event, (void*)softsp) == AE_OK))
1003611Smyers 		return (1);
1004611Smyers 	return (0);
1005611Smyers }
1006611Smyers 
1007611Smyers /*
1008611Smyers  *
1009611Smyers  */
1010611Smyers static int
1011611Smyers power_probe_fixed_button(struct power_soft_state *softsp)
1012611Smyers {
1013611Smyers 	FADT_DESCRIPTOR *fadt;
1014611Smyers 
1015611Smyers 	if (AcpiGetFirmwareTable(FADT_SIG, 1, ACPI_LOGICAL_ADDRESSING,
1016611Smyers 	    (ACPI_TABLE_HEADER **) &fadt) != AE_OK)
1017611Smyers 		return (0);
1018611Smyers 
1019611Smyers 	if (!fadt->PwrButton) {
1020611Smyers 		if (AcpiInstallFixedEventHandler(ACPI_EVENT_POWER_BUTTON,
1021611Smyers 		    power_acpi_fixed_event, (void *)softsp) == AE_OK)
1022611Smyers 			return (1);
1023611Smyers 	}
1024611Smyers 	return (0);
1025611Smyers }
1026611Smyers 
1027611Smyers 
1028611Smyers /*
1029611Smyers  *
1030611Smyers  */
1031611Smyers static int
1032611Smyers power_attach_acpi(struct power_soft_state *softsp)
1033611Smyers {
1034611Smyers 
1035611Smyers 	/*
1036611Smyers 	 * If we've attached anything already, return an error
1037611Smyers 	 */
1038611Smyers 	if ((softsp->gpe_attached) || (softsp->fixed_attached))
1039611Smyers 		return (DDI_FAILURE);
1040611Smyers 
1041611Smyers 	/*
1042611Smyers 	 * attempt to attach both a fixed-event handler and a GPE
1043611Smyers 	 * handler; remember what we got
1044611Smyers 	 */
1045611Smyers 	softsp->fixed_attached = (power_probe_fixed_button(softsp) != 0);
1046611Smyers 	softsp->gpe_attached = (power_probe_method_button(softsp) != 0);
1047611Smyers 
1048611Smyers 	/*
1049611Smyers 	 * If we've attached anything now, return success
1050611Smyers 	 */
1051611Smyers 	if ((softsp->gpe_attached) || (softsp->fixed_attached))
1052611Smyers 		return (DDI_SUCCESS);
1053611Smyers 
1054611Smyers 	return (DDI_FAILURE);
1055611Smyers }
1056611Smyers 
1057611Smyers /*
1058611Smyers  *
1059611Smyers  */
1060611Smyers static void
1061611Smyers power_detach_acpi(struct power_soft_state *softsp)
1062611Smyers {
1063611Smyers 	if (softsp->gpe_attached) {
1064611Smyers 		if (AcpiRemoveNotifyHandler(softsp->button_obj,
1065611Smyers 		    ACPI_DEVICE_NOTIFY, power_acpi_notify_event) != AE_OK)
1066611Smyers 			cmn_err(CE_WARN, "!power: failed to remove Notify"
1067611Smyers 			    " handler");
1068611Smyers 	}
1069611Smyers 
1070611Smyers 	if (softsp->fixed_attached) {
1071611Smyers 		if (AcpiRemoveFixedEventHandler(ACPI_EVENT_POWER_BUTTON,
1072611Smyers 		    power_acpi_fixed_event) != AE_OK)
1073611Smyers 			cmn_err(CE_WARN, "!power: failed to remove Power"
1074611Smyers 			    " Button handler");
1075611Smyers 	}
1076611Smyers }
1077611Smyers 
1078611Smyers #else
1079611Smyers /*
1080*920Sjbeloro  * Code for platforms that have EPIC processor for processing power
1081*920Sjbeloro  * button interrupts.
1082*920Sjbeloro  */
1083*920Sjbeloro static int
1084*920Sjbeloro power_setup_epic_regs(dev_info_t *dip, struct power_soft_state *softsp)
1085*920Sjbeloro {
1086*920Sjbeloro 	ddi_device_acc_attr_t	attr;
1087*920Sjbeloro 	uint8_t *reg_base;
1088*920Sjbeloro 
1089*920Sjbeloro 	attr.devacc_attr_version = DDI_DEVICE_ATTR_V0;
1090*920Sjbeloro 	attr.devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC;
1091*920Sjbeloro 	attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
1092*920Sjbeloro 	if (ddi_regs_map_setup(dip, 0, (caddr_t *)&reg_base,
1093*920Sjbeloro 		EPIC_REGS_OFFSET, EPIC_REGS_LEN, &attr,
1094*920Sjbeloro 		&softsp->power_rhandle) != DDI_SUCCESS) {
1095*920Sjbeloro 		return (DDI_FAILURE);
1096*920Sjbeloro 	}
1097*920Sjbeloro 
1098*920Sjbeloro 	softsp->power_btn_reg = reg_base;
1099*920Sjbeloro 	softsp->power_regs_mapped = B_TRUE;
1100*920Sjbeloro 
1101*920Sjbeloro 	/* Clear power button interrupt first */
1102*920Sjbeloro 	EPIC_WR(softsp->power_rhandle, softsp->power_btn_reg,
1103*920Sjbeloro 		EPIC_ATOM_INTR_CLEAR);
1104*920Sjbeloro 
1105*920Sjbeloro 	/* Enable EPIC interrupt for power button single press event */
1106*920Sjbeloro 	EPIC_WR(softsp->power_rhandle, softsp->power_btn_reg,
1107*920Sjbeloro 		EPIC_ATOM_INTR_ENABLE);
1108*920Sjbeloro 
1109*920Sjbeloro 	/*
1110*920Sjbeloro 	 * At this point, EPIC interrupt processing is fully initialised.
1111*920Sjbeloro 	 */
1112*920Sjbeloro 	hasEPIC = B_TRUE;
1113*920Sjbeloro 	return (DDI_SUCCESS);
1114*920Sjbeloro }
1115*920Sjbeloro 
1116*920Sjbeloro /*
1117*920Sjbeloro  *
1118611Smyers  * power button register definitions for acpi register on m1535d
1119611Smyers  */
1120611Smyers #define	M1535D_PWR_BTN_REG_01		0x1
1121611Smyers #define	M1535D_PWR_BTN_EVENT_FLAG	0x1
1122611Smyers 
1123611Smyers static int
1124611Smyers power_setup_m1535_regs(dev_info_t *dip, struct power_soft_state *softsp)
1125611Smyers {
1126611Smyers 	ddi_device_acc_attr_t	attr;
1127611Smyers 	uint8_t *reg_base;
1128611Smyers 
1129611Smyers 	attr.devacc_attr_version = DDI_DEVICE_ATTR_V0;
1130611Smyers 	attr.devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC;
1131611Smyers 	attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
1132611Smyers 	if (ddi_regs_map_setup(dip, 0, (caddr_t *)&reg_base, 0, 0, &attr,
1133611Smyers 	    &softsp->power_rhandle) != DDI_SUCCESS) {
1134611Smyers 		return (DDI_FAILURE);
1135611Smyers 	}
1136611Smyers 	softsp->power_btn_reg = &reg_base[M1535D_PWR_BTN_REG_01];
1137611Smyers 	softsp->power_btn_bit = M1535D_PWR_BTN_EVENT_FLAG;
1138611Smyers 	softsp->power_regs_mapped = B_TRUE;
1139611Smyers 	return (DDI_SUCCESS);
1140611Smyers }
1141611Smyers 
1142611Smyers /*
1143*920Sjbeloro  * MBC Fire/SSI Interrupt Status Register definitions
1144*920Sjbeloro  */
1145*920Sjbeloro #define	FIRE_SSI_ISR			0x0
1146*920Sjbeloro #define	FIRE_SSI_INTR_ENA		0x8
1147*920Sjbeloro #define	FIRE_SSI_SHUTDOWN_REQ		0x4
1148*920Sjbeloro 
1149*920Sjbeloro static int
1150*920Sjbeloro power_setup_mbc_regs(dev_info_t *dip, struct power_soft_state *softsp)
1151*920Sjbeloro {
1152*920Sjbeloro 	ddi_device_acc_attr_t   attr;
1153*920Sjbeloro 	uint8_t *reg_base;
1154*920Sjbeloro 	ddi_acc_handle_t hdl;
1155*920Sjbeloro 	uint8_t reg;
1156*920Sjbeloro 
1157*920Sjbeloro 	attr.devacc_attr_version = DDI_DEVICE_ATTR_V0;
1158*920Sjbeloro 	attr.devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC;
1159*920Sjbeloro 	attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
1160*920Sjbeloro 	if (ddi_regs_map_setup(dip, 0, (caddr_t *)&reg_base, 0, 0, &attr,
1161*920Sjbeloro 		&softsp->power_rhandle) != DDI_SUCCESS) {
1162*920Sjbeloro 		return (DDI_FAILURE);
1163*920Sjbeloro 	}
1164*920Sjbeloro 	softsp->power_btn_reg = &reg_base[FIRE_SSI_ISR];
1165*920Sjbeloro 	softsp->power_btn_bit = FIRE_SSI_SHUTDOWN_REQ;
1166*920Sjbeloro 	/*
1167*920Sjbeloro 	 * Enable MBC Fire Power Button interrupt.
1168*920Sjbeloro 	 */
1169*920Sjbeloro 	hdl = softsp->power_rhandle;
1170*920Sjbeloro 	reg = ddi_get8(hdl, &reg_base[FIRE_SSI_INTR_ENA]);
1171*920Sjbeloro 	reg |= FIRE_SSI_SHUTDOWN_REQ;
1172*920Sjbeloro 	ddi_put8(hdl, &reg_base[FIRE_SSI_INTR_ENA], reg);
1173*920Sjbeloro 
1174*920Sjbeloro 	softsp->power_regs_mapped = B_TRUE;
1175*920Sjbeloro 
1176*920Sjbeloro 	return (DDI_SUCCESS);
1177*920Sjbeloro }
1178*920Sjbeloro 
1179*920Sjbeloro /*
1180611Smyers  * Setup register map for the power button
1181611Smyers  * NOTE:- we only map registers for platforms
1182611Smyers  * binding with the ali1535d+-power compatible
1183*920Sjbeloro  * property or mbc-power or epic property.
1184611Smyers  */
1185611Smyers static int
1186611Smyers power_setup_regs(struct power_soft_state *softsp)
1187611Smyers {
1188611Smyers 	char	*binding_name;
1189611Smyers 
1190611Smyers 	softsp->power_regs_mapped = B_FALSE;
1191611Smyers 	softsp->power_btn_ioctl = B_FALSE;
1192611Smyers 	binding_name = ddi_binding_name(softsp->dip);
1193*920Sjbeloro 	if (strcmp(binding_name, "mbc-power") == 0)
1194*920Sjbeloro 		return (power_setup_mbc_regs(softsp->dip, softsp));
1195*920Sjbeloro 	else if (strcmp(binding_name, "SUNW,ebus-pic18lf65j10-power") == 0)
1196*920Sjbeloro 		return (power_setup_epic_regs(softsp->dip, softsp));
1197*920Sjbeloro 	else if (strcmp(binding_name, "ali1535d+-power") == 0)
1198611Smyers 		return (power_setup_m1535_regs(softsp->dip, softsp));
1199611Smyers 
1200*920Sjbeloro 	/*
1201*920Sjbeloro 	 * If the binding name is not one of these, that means there is no
1202*920Sjbeloro 	 * additional HW and hence no extra processing is necessary. Just
1203*920Sjbeloro 	 * return SUCCESS.
1204*920Sjbeloro 	 */
1205611Smyers 	return (DDI_SUCCESS);
1206611Smyers }
1207611Smyers 
1208611Smyers static void
1209611Smyers power_free_regs(struct power_soft_state *softsp)
1210611Smyers {
1211611Smyers 	if (softsp->power_regs_mapped)
1212611Smyers 		ddi_regs_map_free(&softsp->power_rhandle);
1213611Smyers }
1214611Smyers #endif	/* ACPI_POWER_BUTTON */
1215