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