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