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