xref: /titanic_50/usr/src/uts/common/io/power.c (revision fd358c0d0d7fe52984c41025af9ef89a895c971a)
1*fd358c0dSmyers /*
2*fd358c0dSmyers  * CDDL HEADER START
3*fd358c0dSmyers  *
4*fd358c0dSmyers  * The contents of this file are subject to the terms of the
5*fd358c0dSmyers  * Common Development and Distribution License, Version 1.0 only
6*fd358c0dSmyers  * (the "License").  You may not use this file except in compliance
7*fd358c0dSmyers  * with the License.
8*fd358c0dSmyers  *
9*fd358c0dSmyers  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10*fd358c0dSmyers  * or http://www.opensolaris.org/os/licensing.
11*fd358c0dSmyers  * See the License for the specific language governing permissions
12*fd358c0dSmyers  * and limitations under the License.
13*fd358c0dSmyers  *
14*fd358c0dSmyers  * When distributing Covered Code, include this CDDL HEADER in each
15*fd358c0dSmyers  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16*fd358c0dSmyers  * If applicable, add the following below this CDDL HEADER, with the
17*fd358c0dSmyers  * fields enclosed by brackets "[]" replaced with your own identifying
18*fd358c0dSmyers  * information: Portions Copyright [yyyy] [name of copyright owner]
19*fd358c0dSmyers  *
20*fd358c0dSmyers  * CDDL HEADER END
21*fd358c0dSmyers  */
22*fd358c0dSmyers /*
23*fd358c0dSmyers  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24*fd358c0dSmyers  * Use is subject to license terms.
25*fd358c0dSmyers  */
26*fd358c0dSmyers 
27*fd358c0dSmyers #pragma ident	"%Z%%M%	%I%	%E% SMI"
28*fd358c0dSmyers 
29*fd358c0dSmyers /*
30*fd358c0dSmyers  *	Power Button Driver
31*fd358c0dSmyers  *
32*fd358c0dSmyers  *	This driver handles interrupt generated by the power button on
33*fd358c0dSmyers  *	platforms with "power" device node which has "button" property.
34*fd358c0dSmyers  *	Currently, these platforms are:
35*fd358c0dSmyers  *
36*fd358c0dSmyers  *		ACPI-enabled x86/x64 platforms
37*fd358c0dSmyers  *		Ultra-5_10, Ultra-80, Sun-Blade-100, Sun-Blade-150,
38*fd358c0dSmyers  *		Sun-Blade-1500, Sun-Blade-2500,
39*fd358c0dSmyers  *		Sun-Fire-V210, Sun-Fire-V240, Netra-240
40*fd358c0dSmyers  *
41*fd358c0dSmyers  *	Only one instance is allowed to attach.  In order to know when
42*fd358c0dSmyers  *	an application that has opened the device is going away, a new
43*fd358c0dSmyers  *	minor clone is created for each open(9E) request.  There are
44*fd358c0dSmyers  *	allocations for creating minor clones between 1 and 255.  The ioctl
45*fd358c0dSmyers  *	interface is defined by pbio(7I) and approved as part of
46*fd358c0dSmyers  *	PSARC/1999/393 case.
47*fd358c0dSmyers  */
48*fd358c0dSmyers 
49*fd358c0dSmyers #include <sys/types.h>
50*fd358c0dSmyers #include <sys/conf.h>
51*fd358c0dSmyers #include <sys/ddi.h>
52*fd358c0dSmyers #include <sys/sunddi.h>
53*fd358c0dSmyers #include <sys/ddi_impldefs.h>
54*fd358c0dSmyers #include <sys/cmn_err.h>
55*fd358c0dSmyers #include <sys/errno.h>
56*fd358c0dSmyers #include <sys/modctl.h>
57*fd358c0dSmyers #include <sys/machsystm.h>
58*fd358c0dSmyers #include <sys/open.h>
59*fd358c0dSmyers #include <sys/stat.h>
60*fd358c0dSmyers #include <sys/poll.h>
61*fd358c0dSmyers #include <sys/pbio.h>
62*fd358c0dSmyers #ifdef	ACPI_POWER_BUTTON
63*fd358c0dSmyers #include <sys/acpi/acpi.h>
64*fd358c0dSmyers #include <sys/acpica.h>
65*fd358c0dSmyers #endif	/* ACPI_POWER_BUTTON */
66*fd358c0dSmyers 
67*fd358c0dSmyers /*
68*fd358c0dSmyers  * Maximum number of clone minors that is allowed.  This value
69*fd358c0dSmyers  * is defined relatively low to save memory.
70*fd358c0dSmyers  */
71*fd358c0dSmyers #define	POWER_MAX_CLONE	256
72*fd358c0dSmyers 
73*fd358c0dSmyers /*
74*fd358c0dSmyers  * Minor number is instance << 8 + clone minor from range 1-255; clone 0
75*fd358c0dSmyers  * is reserved for "original" minor.
76*fd358c0dSmyers  */
77*fd358c0dSmyers #define	POWER_MINOR_TO_CLONE(minor) ((minor) & (POWER_MAX_CLONE - 1))
78*fd358c0dSmyers 
79*fd358c0dSmyers /*
80*fd358c0dSmyers  * Power Button Abort Delay
81*fd358c0dSmyers  */
82*fd358c0dSmyers #define	ABORT_INCREMENT_DELAY	10
83*fd358c0dSmyers 
84*fd358c0dSmyers /*
85*fd358c0dSmyers  * Driver global variables
86*fd358c0dSmyers  */
87*fd358c0dSmyers static void *power_state;
88*fd358c0dSmyers static int power_inst = -1;
89*fd358c0dSmyers 
90*fd358c0dSmyers static hrtime_t	power_button_debounce = NANOSEC/MILLISEC*10;
91*fd358c0dSmyers static hrtime_t power_button_abort_interval = 1.5 * NANOSEC;
92*fd358c0dSmyers static int	power_button_abort_presses = 3;
93*fd358c0dSmyers static int	power_button_abort_enable = 1;
94*fd358c0dSmyers static int	power_button_enable = 1;
95*fd358c0dSmyers 
96*fd358c0dSmyers static int	power_button_pressed = 0;
97*fd358c0dSmyers static int	power_button_cancel = 0;
98*fd358c0dSmyers static int	power_button_timeouts = 0;
99*fd358c0dSmyers static int	timeout_cancel = 0;
100*fd358c0dSmyers static int	additional_presses = 0;
101*fd358c0dSmyers 
102*fd358c0dSmyers /*
103*fd358c0dSmyers  * Function prototypes
104*fd358c0dSmyers  */
105*fd358c0dSmyers static int power_attach(dev_info_t *, ddi_attach_cmd_t);
106*fd358c0dSmyers static int power_detach(dev_info_t *, ddi_detach_cmd_t);
107*fd358c0dSmyers static int power_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
108*fd358c0dSmyers static int power_open(dev_t *, int, int, cred_t *);
109*fd358c0dSmyers static int power_close(dev_t, int, int, cred_t *);
110*fd358c0dSmyers static int power_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
111*fd358c0dSmyers static int power_chpoll(dev_t, short, int, short *, struct pollhead **);
112*fd358c0dSmyers #ifndef	ACPI_POWER_BUTTON
113*fd358c0dSmyers static uint_t power_high_intr(caddr_t);
114*fd358c0dSmyers #endif
115*fd358c0dSmyers static uint_t power_soft_intr(caddr_t);
116*fd358c0dSmyers static uint_t power_issue_shutdown(caddr_t);
117*fd358c0dSmyers static void power_timeout(caddr_t);
118*fd358c0dSmyers static void power_log_message(void);
119*fd358c0dSmyers 
120*fd358c0dSmyers /*
121*fd358c0dSmyers  * Structure used in the driver
122*fd358c0dSmyers  */
123*fd358c0dSmyers struct power_soft_state {
124*fd358c0dSmyers 	dev_info_t	*dip;		/* device info pointer */
125*fd358c0dSmyers 	kmutex_t	power_mutex;	/* mutex lock */
126*fd358c0dSmyers 	kmutex_t	power_intr_mutex; /* interrupt mutex lock */
127*fd358c0dSmyers 	ddi_iblock_cookie_t soft_iblock_cookie; /* holds interrupt cookie */
128*fd358c0dSmyers 	ddi_iblock_cookie_t high_iblock_cookie; /* holds interrupt cookie */
129*fd358c0dSmyers 	ddi_softintr_t	softintr_id;	/* soft interrupt id */
130*fd358c0dSmyers 	uchar_t		clones[POWER_MAX_CLONE]; /* array of minor clones */
131*fd358c0dSmyers 	int		monitor_on;	/* clone monitoring the button event */
132*fd358c0dSmyers 					/* clone 0 indicates no one is */
133*fd358c0dSmyers 					/* monitoring the button event */
134*fd358c0dSmyers 	pollhead_t	pollhd;		/* poll head struct */
135*fd358c0dSmyers 	int		events;		/* bit map of occured events */
136*fd358c0dSmyers 	int		shutdown_pending; /* system shutdown in progress */
137*fd358c0dSmyers #ifdef	ACPI_POWER_BUTTON
138*fd358c0dSmyers 	boolean_t	fixed_attached;	/* true means fixed is attached */
139*fd358c0dSmyers 	boolean_t	gpe_attached;	/* true means GPE is attached */
140*fd358c0dSmyers 	ACPI_HANDLE	button_obj;	/* handle to device power button */
141*fd358c0dSmyers #else
142*fd358c0dSmyers 	ddi_acc_handle_t power_rhandle; /* power button register handle */
143*fd358c0dSmyers 	uint8_t		*power_btn_reg;	/* power button register address */
144*fd358c0dSmyers 	uint8_t		power_btn_bit;	/* power button register bit */
145*fd358c0dSmyers 	boolean_t	power_regs_mapped; /* flag to tell if regs mapped */
146*fd358c0dSmyers 	boolean_t	power_btn_ioctl; /* flag to specify ioctl request */
147*fd358c0dSmyers #endif
148*fd358c0dSmyers };
149*fd358c0dSmyers 
150*fd358c0dSmyers #ifdef	ACPI_POWER_BUTTON
151*fd358c0dSmyers static int power_attach_acpi(struct power_soft_state *softsp);
152*fd358c0dSmyers static void power_detach_acpi(struct power_soft_state *softsp);
153*fd358c0dSmyers static UINT32 power_acpi_fixed_event(void *ctx);
154*fd358c0dSmyers #else
155*fd358c0dSmyers static int power_setup_regs(struct power_soft_state *softsp);
156*fd358c0dSmyers static void power_free_regs(struct power_soft_state *softsp);
157*fd358c0dSmyers #endif	/* ACPI_POWER_BUTTON */
158*fd358c0dSmyers 
159*fd358c0dSmyers /*
160*fd358c0dSmyers  * Configuration data structures
161*fd358c0dSmyers  */
162*fd358c0dSmyers static struct cb_ops power_cb_ops = {
163*fd358c0dSmyers 	power_open,		/* open */
164*fd358c0dSmyers 	power_close,		/* close */
165*fd358c0dSmyers 	nodev,			/* strategy */
166*fd358c0dSmyers 	nodev,			/* print */
167*fd358c0dSmyers 	nodev,			/* dump */
168*fd358c0dSmyers 	nodev,			/* read */
169*fd358c0dSmyers 	nodev,			/* write */
170*fd358c0dSmyers 	power_ioctl,		/* ioctl */
171*fd358c0dSmyers 	nodev,			/* devmap */
172*fd358c0dSmyers 	nodev,			/* mmap */
173*fd358c0dSmyers 	nodev,			/* segmap */
174*fd358c0dSmyers 	power_chpoll,		/* poll */
175*fd358c0dSmyers 	ddi_prop_op,		/* cb_prop_op */
176*fd358c0dSmyers 	NULL,			/* streamtab */
177*fd358c0dSmyers 	D_MP | D_NEW,		/* Driver compatibility flag */
178*fd358c0dSmyers 	CB_REV,			/* rev */
179*fd358c0dSmyers 	nodev,			/* cb_aread */
180*fd358c0dSmyers 	nodev			/* cb_awrite */
181*fd358c0dSmyers };
182*fd358c0dSmyers 
183*fd358c0dSmyers static struct dev_ops power_ops = {
184*fd358c0dSmyers 	DEVO_REV,		/* devo_rev, */
185*fd358c0dSmyers 	0,			/* refcnt */
186*fd358c0dSmyers 	power_getinfo,		/* getinfo */
187*fd358c0dSmyers 	nulldev,		/* identify */
188*fd358c0dSmyers 	nulldev,		/* probe */
189*fd358c0dSmyers 	power_attach,		/* attach */
190*fd358c0dSmyers 	power_detach,		/* detach */
191*fd358c0dSmyers 	nodev,			/* reset */
192*fd358c0dSmyers 	&power_cb_ops,		/* cb_ops */
193*fd358c0dSmyers 	(struct bus_ops *)NULL,	/* bus_ops */
194*fd358c0dSmyers 	NULL			/* power */
195*fd358c0dSmyers };
196*fd358c0dSmyers 
197*fd358c0dSmyers static struct modldrv modldrv = {
198*fd358c0dSmyers 	&mod_driverops,		/* Type of module.  This one is a driver */
199*fd358c0dSmyers 	"power button driver v%I%",	/* name of module */
200*fd358c0dSmyers 	&power_ops,		/* driver ops */
201*fd358c0dSmyers };
202*fd358c0dSmyers 
203*fd358c0dSmyers static struct modlinkage modlinkage = {
204*fd358c0dSmyers 	MODREV_1,
205*fd358c0dSmyers 	(void *)&modldrv,
206*fd358c0dSmyers 	NULL
207*fd358c0dSmyers };
208*fd358c0dSmyers 
209*fd358c0dSmyers /*
210*fd358c0dSmyers  * These are the module initialization routines.
211*fd358c0dSmyers  */
212*fd358c0dSmyers 
213*fd358c0dSmyers int
214*fd358c0dSmyers _init(void)
215*fd358c0dSmyers {
216*fd358c0dSmyers 	int error;
217*fd358c0dSmyers 
218*fd358c0dSmyers 	if ((error = ddi_soft_state_init(&power_state,
219*fd358c0dSmyers 	    sizeof (struct power_soft_state), 0)) != 0)
220*fd358c0dSmyers 		return (error);
221*fd358c0dSmyers 
222*fd358c0dSmyers 	if ((error = mod_install(&modlinkage)) != 0)
223*fd358c0dSmyers 		ddi_soft_state_fini(&power_state);
224*fd358c0dSmyers 
225*fd358c0dSmyers 	return (error);
226*fd358c0dSmyers }
227*fd358c0dSmyers 
228*fd358c0dSmyers int
229*fd358c0dSmyers _fini(void)
230*fd358c0dSmyers {
231*fd358c0dSmyers 	int error;
232*fd358c0dSmyers 
233*fd358c0dSmyers 	if ((error = mod_remove(&modlinkage)) == 0)
234*fd358c0dSmyers 		ddi_soft_state_fini(&power_state);
235*fd358c0dSmyers 
236*fd358c0dSmyers 	return (error);
237*fd358c0dSmyers }
238*fd358c0dSmyers 
239*fd358c0dSmyers int
240*fd358c0dSmyers _info(struct modinfo *modinfop)
241*fd358c0dSmyers {
242*fd358c0dSmyers 	return (mod_info(&modlinkage, modinfop));
243*fd358c0dSmyers }
244*fd358c0dSmyers 
245*fd358c0dSmyers /*ARGSUSED*/
246*fd358c0dSmyers static int
247*fd358c0dSmyers power_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg,
248*fd358c0dSmyers     void **result)
249*fd358c0dSmyers {
250*fd358c0dSmyers 	struct power_soft_state *softsp;
251*fd358c0dSmyers 
252*fd358c0dSmyers 	if (power_inst == -1)
253*fd358c0dSmyers 		return (DDI_FAILURE);
254*fd358c0dSmyers 
255*fd358c0dSmyers 	switch (infocmd) {
256*fd358c0dSmyers 	case DDI_INFO_DEVT2DEVINFO:
257*fd358c0dSmyers 		if ((softsp = ddi_get_soft_state(power_state, power_inst))
258*fd358c0dSmyers 		    == NULL)
259*fd358c0dSmyers 			return (DDI_FAILURE);
260*fd358c0dSmyers 		*result = (void *)softsp->dip;
261*fd358c0dSmyers 		return (DDI_SUCCESS);
262*fd358c0dSmyers 
263*fd358c0dSmyers 	case DDI_INFO_DEVT2INSTANCE:
264*fd358c0dSmyers 		*result = (void *)(uintptr_t)power_inst;
265*fd358c0dSmyers 		return (DDI_SUCCESS);
266*fd358c0dSmyers 
267*fd358c0dSmyers 	default:
268*fd358c0dSmyers 		return (DDI_FAILURE);
269*fd358c0dSmyers 	}
270*fd358c0dSmyers }
271*fd358c0dSmyers 
272*fd358c0dSmyers static int
273*fd358c0dSmyers power_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
274*fd358c0dSmyers {
275*fd358c0dSmyers 	struct power_soft_state *softsp;
276*fd358c0dSmyers 
277*fd358c0dSmyers 	switch (cmd) {
278*fd358c0dSmyers 	case DDI_ATTACH:
279*fd358c0dSmyers 		break;
280*fd358c0dSmyers 	case DDI_RESUME:
281*fd358c0dSmyers 		return (DDI_SUCCESS);
282*fd358c0dSmyers 	default:
283*fd358c0dSmyers 		return (DDI_FAILURE);
284*fd358c0dSmyers 	}
285*fd358c0dSmyers 
286*fd358c0dSmyers 	/*
287*fd358c0dSmyers 	 * If the power node doesn't have "button" property, quietly
288*fd358c0dSmyers 	 * fail to attach.
289*fd358c0dSmyers 	 */
290*fd358c0dSmyers 	if (ddi_prop_exists(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
291*fd358c0dSmyers 	    "button") == 0)
292*fd358c0dSmyers 		return (DDI_FAILURE);
293*fd358c0dSmyers 
294*fd358c0dSmyers 	if (power_inst != -1)
295*fd358c0dSmyers 		return (DDI_FAILURE);
296*fd358c0dSmyers 
297*fd358c0dSmyers 	power_inst = ddi_get_instance(dip);
298*fd358c0dSmyers 
299*fd358c0dSmyers 	if (ddi_soft_state_zalloc(power_state, power_inst) != DDI_SUCCESS)
300*fd358c0dSmyers 		return (DDI_FAILURE);
301*fd358c0dSmyers 
302*fd358c0dSmyers 	if (ddi_create_minor_node(dip, "power_button", S_IFCHR,
303*fd358c0dSmyers 	    (power_inst << 8) + 0, "ddi_power_button", 0) != DDI_SUCCESS)
304*fd358c0dSmyers 		return (DDI_FAILURE);
305*fd358c0dSmyers 
306*fd358c0dSmyers 	softsp = ddi_get_soft_state(power_state, power_inst);
307*fd358c0dSmyers 	softsp->dip = dip;
308*fd358c0dSmyers 
309*fd358c0dSmyers #ifdef	ACPI_POWER_BUTTON
310*fd358c0dSmyers 	power_attach_acpi(softsp);
311*fd358c0dSmyers #else
312*fd358c0dSmyers 	if (power_setup_regs(softsp) != DDI_SUCCESS) {
313*fd358c0dSmyers 		cmn_err(CE_WARN, "power_attach: failed to setup registers");
314*fd358c0dSmyers 		goto error;
315*fd358c0dSmyers 	}
316*fd358c0dSmyers 
317*fd358c0dSmyers 	if (ddi_get_iblock_cookie(dip, 0,
318*fd358c0dSmyers 	    &softsp->high_iblock_cookie) != DDI_SUCCESS) {
319*fd358c0dSmyers 		cmn_err(CE_WARN, "power_attach: ddi_get_soft_iblock_cookie "
320*fd358c0dSmyers 		    "failed.");
321*fd358c0dSmyers 		goto error;
322*fd358c0dSmyers 	}
323*fd358c0dSmyers 	mutex_init(&softsp->power_intr_mutex, NULL, MUTEX_DRIVER,
324*fd358c0dSmyers 	    softsp->high_iblock_cookie);
325*fd358c0dSmyers 
326*fd358c0dSmyers 	if (ddi_add_intr(dip, 0, &softsp->high_iblock_cookie, NULL,
327*fd358c0dSmyers 	    power_high_intr, (caddr_t)softsp) != DDI_SUCCESS) {
328*fd358c0dSmyers 		cmn_err(CE_WARN, "power_attach: failed to add high-level "
329*fd358c0dSmyers 		    " interrupt handler.");
330*fd358c0dSmyers 		mutex_destroy(&softsp->power_intr_mutex);
331*fd358c0dSmyers 		goto error;
332*fd358c0dSmyers 	}
333*fd358c0dSmyers #endif	/* ACPI_POWER_BUTTON */
334*fd358c0dSmyers 
335*fd358c0dSmyers 	if (ddi_get_soft_iblock_cookie(dip, DDI_SOFTINT_LOW,
336*fd358c0dSmyers 	    &softsp->soft_iblock_cookie) != DDI_SUCCESS) {
337*fd358c0dSmyers 		cmn_err(CE_WARN, "power_attach: ddi_get_soft_iblock_cookie "
338*fd358c0dSmyers 		    "failed.");
339*fd358c0dSmyers 		mutex_destroy(&softsp->power_intr_mutex);
340*fd358c0dSmyers 		ddi_remove_intr(dip, 0, NULL);
341*fd358c0dSmyers 		goto error;
342*fd358c0dSmyers 	}
343*fd358c0dSmyers 
344*fd358c0dSmyers 	mutex_init(&softsp->power_mutex, NULL, MUTEX_DRIVER,
345*fd358c0dSmyers 	    (void *)softsp->soft_iblock_cookie);
346*fd358c0dSmyers 
347*fd358c0dSmyers 	if (ddi_add_softintr(dip, DDI_SOFTINT_LOW, &softsp->softintr_id,
348*fd358c0dSmyers 	    NULL, NULL, power_soft_intr, (caddr_t)softsp) != DDI_SUCCESS) {
349*fd358c0dSmyers 		cmn_err(CE_WARN, "power_attach: failed to add soft "
350*fd358c0dSmyers 		    "interrupt handler.");
351*fd358c0dSmyers 		mutex_destroy(&softsp->power_mutex);
352*fd358c0dSmyers 		mutex_destroy(&softsp->power_intr_mutex);
353*fd358c0dSmyers 		ddi_remove_intr(dip, 0, NULL);
354*fd358c0dSmyers 		goto error;
355*fd358c0dSmyers 	}
356*fd358c0dSmyers 
357*fd358c0dSmyers 	ddi_report_dev(dip);
358*fd358c0dSmyers 
359*fd358c0dSmyers 	return (DDI_SUCCESS);
360*fd358c0dSmyers 
361*fd358c0dSmyers error:
362*fd358c0dSmyers #ifdef	ACPI_POWER_BUTTON
363*fd358c0dSmyers 	/*
364*fd358c0dSmyers 	 * detach ACPI power button
365*fd358c0dSmyers 	 */
366*fd358c0dSmyers 	power_detach_acpi(softsp);
367*fd358c0dSmyers #else
368*fd358c0dSmyers 	power_free_regs(softsp);
369*fd358c0dSmyers #endif	/* ACPI_POWER_BUTTON */
370*fd358c0dSmyers 	ddi_remove_minor_node(dip, "power_button");
371*fd358c0dSmyers 	ddi_soft_state_free(power_state, power_inst);
372*fd358c0dSmyers 	return (DDI_FAILURE);
373*fd358c0dSmyers }
374*fd358c0dSmyers 
375*fd358c0dSmyers /*ARGSUSED*/
376*fd358c0dSmyers /*
377*fd358c0dSmyers  * This driver doesn't detach.
378*fd358c0dSmyers  */
379*fd358c0dSmyers static int
380*fd358c0dSmyers power_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
381*fd358c0dSmyers {
382*fd358c0dSmyers 	/*
383*fd358c0dSmyers 	 * Since the "power" node has "reg" property, as part of
384*fd358c0dSmyers 	 * the suspend operation, detach(9E) entry point is called.
385*fd358c0dSmyers 	 * There is no state to save, since this register is used
386*fd358c0dSmyers 	 * by OBP to power off the system and the state of the
387*fd358c0dSmyers 	 * power off is preserved by hardware.
388*fd358c0dSmyers 	 */
389*fd358c0dSmyers 	return ((cmd == DDI_SUSPEND) ? DDI_SUCCESS :
390*fd358c0dSmyers 	    DDI_FAILURE);
391*fd358c0dSmyers }
392*fd358c0dSmyers 
393*fd358c0dSmyers #ifndef	ACPI_POWER_BUTTON
394*fd358c0dSmyers /*
395*fd358c0dSmyers  * Handler for the high-level interrupt.
396*fd358c0dSmyers  */
397*fd358c0dSmyers static uint_t
398*fd358c0dSmyers power_high_intr(caddr_t arg)
399*fd358c0dSmyers {
400*fd358c0dSmyers 	struct power_soft_state *softsp = (struct power_soft_state *)arg;
401*fd358c0dSmyers 	ddi_acc_handle_t hdl = softsp->power_rhandle;
402*fd358c0dSmyers 	uint8_t		reg;
403*fd358c0dSmyers 
404*fd358c0dSmyers 	hrtime_t tstamp;
405*fd358c0dSmyers 	static hrtime_t o_tstamp = 0;
406*fd358c0dSmyers 	static hrtime_t power_button_tstamp = 0;
407*fd358c0dSmyers 	static int power_button_cnt;
408*fd358c0dSmyers 
409*fd358c0dSmyers 	if (softsp->power_regs_mapped) {
410*fd358c0dSmyers 		mutex_enter(&softsp->power_intr_mutex);
411*fd358c0dSmyers 		reg = ddi_get8(hdl, softsp->power_btn_reg);
412*fd358c0dSmyers 		if (reg & softsp->power_btn_bit) {
413*fd358c0dSmyers 			reg &= softsp->power_btn_bit;
414*fd358c0dSmyers 			ddi_put8(hdl, softsp->power_btn_reg, reg);
415*fd358c0dSmyers 			(void) ddi_get8(hdl, softsp->power_btn_reg);
416*fd358c0dSmyers 		} else {
417*fd358c0dSmyers 			if (!softsp->power_btn_ioctl) {
418*fd358c0dSmyers 				mutex_exit(&softsp->power_intr_mutex);
419*fd358c0dSmyers 				return (DDI_INTR_CLAIMED);
420*fd358c0dSmyers 			}
421*fd358c0dSmyers 			softsp->power_btn_ioctl = B_FALSE;
422*fd358c0dSmyers 		}
423*fd358c0dSmyers 		mutex_exit(&softsp->power_intr_mutex);
424*fd358c0dSmyers 	}
425*fd358c0dSmyers 
426*fd358c0dSmyers 	tstamp = gethrtime();
427*fd358c0dSmyers 
428*fd358c0dSmyers 	/* need to deal with power button debounce */
429*fd358c0dSmyers 	if (o_tstamp && (tstamp - o_tstamp) < power_button_debounce) {
430*fd358c0dSmyers 		o_tstamp = tstamp;
431*fd358c0dSmyers 		return (DDI_INTR_CLAIMED);
432*fd358c0dSmyers 	}
433*fd358c0dSmyers 	o_tstamp = tstamp;
434*fd358c0dSmyers 
435*fd358c0dSmyers 	power_button_cnt++;
436*fd358c0dSmyers 
437*fd358c0dSmyers 	mutex_enter(&softsp->power_intr_mutex);
438*fd358c0dSmyers 	power_button_pressed++;
439*fd358c0dSmyers 	mutex_exit(&softsp->power_intr_mutex);
440*fd358c0dSmyers 
441*fd358c0dSmyers 	/*
442*fd358c0dSmyers 	 * If power button abort is enabled and power button was pressed
443*fd358c0dSmyers 	 * power_button_abort_presses times within power_button_abort_interval
444*fd358c0dSmyers 	 * then call abort_sequence_enter();
445*fd358c0dSmyers 	 */
446*fd358c0dSmyers 	if (power_button_abort_enable) {
447*fd358c0dSmyers 		if (power_button_abort_presses == 1 ||
448*fd358c0dSmyers 		    tstamp < (power_button_tstamp +
449*fd358c0dSmyers 		    power_button_abort_interval)) {
450*fd358c0dSmyers 			if (power_button_cnt == power_button_abort_presses) {
451*fd358c0dSmyers 				mutex_enter(&softsp->power_intr_mutex);
452*fd358c0dSmyers 				power_button_cancel += power_button_timeouts;
453*fd358c0dSmyers 				power_button_pressed = 0;
454*fd358c0dSmyers 				mutex_exit(&softsp->power_intr_mutex);
455*fd358c0dSmyers 				power_button_cnt = 0;
456*fd358c0dSmyers 				abort_sequence_enter("Power Button Abort");
457*fd358c0dSmyers 				return (DDI_INTR_CLAIMED);
458*fd358c0dSmyers 			}
459*fd358c0dSmyers 		} else {
460*fd358c0dSmyers 			power_button_cnt = 1;
461*fd358c0dSmyers 			power_button_tstamp = tstamp;
462*fd358c0dSmyers 		}
463*fd358c0dSmyers 	}
464*fd358c0dSmyers 
465*fd358c0dSmyers 	if (!power_button_enable)
466*fd358c0dSmyers 		return (DDI_INTR_CLAIMED);
467*fd358c0dSmyers 
468*fd358c0dSmyers 	/* post softint to issue timeout for power button action */
469*fd358c0dSmyers 	if (softsp->softintr_id != NULL)
470*fd358c0dSmyers 		ddi_trigger_softintr(softsp->softintr_id);
471*fd358c0dSmyers 
472*fd358c0dSmyers 	return (DDI_INTR_CLAIMED);
473*fd358c0dSmyers }
474*fd358c0dSmyers #endif	/* ifndef ACPI_POWER_BUTTON */
475*fd358c0dSmyers 
476*fd358c0dSmyers /*
477*fd358c0dSmyers  * Handle the softints....
478*fd358c0dSmyers  *
479*fd358c0dSmyers  * If only one softint is posted for several button presses, record
480*fd358c0dSmyers  * the number of additional presses just incase this was actually not quite
481*fd358c0dSmyers  * an Abort sequence so that we can log this event later.
482*fd358c0dSmyers  *
483*fd358c0dSmyers  * Issue a timeout with a duration being a fraction larger than
484*fd358c0dSmyers  * the specified Abort interval inorder to perform a power down if required.
485*fd358c0dSmyers  */
486*fd358c0dSmyers static uint_t
487*fd358c0dSmyers power_soft_intr(caddr_t arg)
488*fd358c0dSmyers {
489*fd358c0dSmyers 	struct power_soft_state *softsp = (struct power_soft_state *)arg;
490*fd358c0dSmyers 
491*fd358c0dSmyers 	if (!power_button_abort_enable)
492*fd358c0dSmyers 		return (power_issue_shutdown(arg));
493*fd358c0dSmyers 
494*fd358c0dSmyers 	mutex_enter(&softsp->power_intr_mutex);
495*fd358c0dSmyers 	if (!power_button_pressed) {
496*fd358c0dSmyers 		mutex_exit(&softsp->power_intr_mutex);
497*fd358c0dSmyers 		return (DDI_INTR_CLAIMED);
498*fd358c0dSmyers 	}
499*fd358c0dSmyers 
500*fd358c0dSmyers 	/*
501*fd358c0dSmyers 	 * Schedule a timeout to do the necessary
502*fd358c0dSmyers 	 * work for shutdown, only one timeout for
503*fd358c0dSmyers 	 * n presses if power button was pressed
504*fd358c0dSmyers 	 * more than once before softint fired
505*fd358c0dSmyers 	 */
506*fd358c0dSmyers 	if (power_button_pressed > 1)
507*fd358c0dSmyers 		additional_presses += power_button_pressed - 1;
508*fd358c0dSmyers 
509*fd358c0dSmyers 	timeout_cancel = 0;
510*fd358c0dSmyers 	power_button_pressed = 0;
511*fd358c0dSmyers 	power_button_timeouts++;
512*fd358c0dSmyers 	mutex_exit(&softsp->power_intr_mutex);
513*fd358c0dSmyers 	(void) timeout((void(*)(void *))power_timeout,
514*fd358c0dSmyers 	    softsp, NSEC_TO_TICK(power_button_abort_interval) +
515*fd358c0dSmyers 	    ABORT_INCREMENT_DELAY);
516*fd358c0dSmyers 
517*fd358c0dSmyers 	return (DDI_INTR_CLAIMED);
518*fd358c0dSmyers }
519*fd358c0dSmyers 
520*fd358c0dSmyers /*
521*fd358c0dSmyers  * Upon receiving a timeout the following is determined:
522*fd358c0dSmyers  *
523*fd358c0dSmyers  * If an  Abort sequence was issued, then we cancel all outstanding timeouts
524*fd358c0dSmyers  * and additional presses prior to the Abort sequence.
525*fd358c0dSmyers  *
526*fd358c0dSmyers  * If we had multiple timeouts issued and the abort sequence was not met,
527*fd358c0dSmyers  * then we had more than one button press to power down the machine. We
528*fd358c0dSmyers  * were probably trying to issue an abort. So log a message indicating this
529*fd358c0dSmyers  * and cancel all outstanding timeouts.
530*fd358c0dSmyers  *
531*fd358c0dSmyers  * If we had just one timeout and the abort sequence was not met then
532*fd358c0dSmyers  * we really did want to power down the machine, so call power_issue_shutdown()
533*fd358c0dSmyers  * to do the work and schedule a power down
534*fd358c0dSmyers  */
535*fd358c0dSmyers static void
536*fd358c0dSmyers power_timeout(caddr_t arg)
537*fd358c0dSmyers {
538*fd358c0dSmyers 	struct power_soft_state *softsp = (struct power_soft_state *)arg;
539*fd358c0dSmyers 	static int first = 0;
540*fd358c0dSmyers 
541*fd358c0dSmyers 	/*
542*fd358c0dSmyers 	 * Abort was generated cancel all outstanding power
543*fd358c0dSmyers 	 * button timeouts
544*fd358c0dSmyers 	 */
545*fd358c0dSmyers 	mutex_enter(&softsp->power_intr_mutex);
546*fd358c0dSmyers 	if (power_button_cancel) {
547*fd358c0dSmyers 		power_button_cancel--;
548*fd358c0dSmyers 		power_button_timeouts--;
549*fd358c0dSmyers 		if (!first) {
550*fd358c0dSmyers 			first++;
551*fd358c0dSmyers 			additional_presses = 0;
552*fd358c0dSmyers 		}
553*fd358c0dSmyers 		mutex_exit(&softsp->power_intr_mutex);
554*fd358c0dSmyers 		return;
555*fd358c0dSmyers 	}
556*fd358c0dSmyers 	first = 0;
557*fd358c0dSmyers 
558*fd358c0dSmyers 	/*
559*fd358c0dSmyers 	 * We get here if the timeout(s) have fired and they were
560*fd358c0dSmyers 	 * not issued prior to an abort.
561*fd358c0dSmyers 	 *
562*fd358c0dSmyers 	 * If we had more than one press in the interval we were
563*fd358c0dSmyers 	 * probably trying to issue an abort, but didnt press the
564*fd358c0dSmyers 	 * required number within the interval. Hence cancel all
565*fd358c0dSmyers 	 * timeouts and do not continue towards shutdown.
566*fd358c0dSmyers 	 */
567*fd358c0dSmyers 	if (!timeout_cancel) {
568*fd358c0dSmyers 		timeout_cancel = power_button_timeouts +
569*fd358c0dSmyers 		    additional_presses;
570*fd358c0dSmyers 
571*fd358c0dSmyers 		power_button_timeouts--;
572*fd358c0dSmyers 		if (!power_button_timeouts)
573*fd358c0dSmyers 			additional_presses = 0;
574*fd358c0dSmyers 
575*fd358c0dSmyers 		if (timeout_cancel > 1) {
576*fd358c0dSmyers 			mutex_exit(&softsp->power_intr_mutex);
577*fd358c0dSmyers 			cmn_err(CE_NOTE, "Power Button pressed "
578*fd358c0dSmyers 			    "%d times, cancelling all requests",
579*fd358c0dSmyers 			    timeout_cancel);
580*fd358c0dSmyers 			return;
581*fd358c0dSmyers 		}
582*fd358c0dSmyers 		mutex_exit(&softsp->power_intr_mutex);
583*fd358c0dSmyers 
584*fd358c0dSmyers 		/* Go and do the work to request shutdown */
585*fd358c0dSmyers 		(void) power_issue_shutdown((caddr_t)softsp);
586*fd358c0dSmyers 		return;
587*fd358c0dSmyers 	}
588*fd358c0dSmyers 
589*fd358c0dSmyers 	power_button_timeouts--;
590*fd358c0dSmyers 	if (!power_button_timeouts)
591*fd358c0dSmyers 		additional_presses = 0;
592*fd358c0dSmyers 	mutex_exit(&softsp->power_intr_mutex);
593*fd358c0dSmyers }
594*fd358c0dSmyers 
595*fd358c0dSmyers #ifdef ACPI_POWER_BUTTON
596*fd358c0dSmyers static void
597*fd358c0dSmyers do_shutdown(void)
598*fd358c0dSmyers {
599*fd358c0dSmyers 	proc_t *initpp;
600*fd358c0dSmyers 
601*fd358c0dSmyers 	/*
602*fd358c0dSmyers 	 * If we're still booting and init(1) isn't set up yet, simply halt.
603*fd358c0dSmyers 	 */
604*fd358c0dSmyers 	mutex_enter(&pidlock);
605*fd358c0dSmyers 	initpp = prfind(P_INITPID);
606*fd358c0dSmyers 	mutex_exit(&pidlock);
607*fd358c0dSmyers 	if (initpp == NULL) {
608*fd358c0dSmyers 		extern void halt(char *);
609*fd358c0dSmyers 		halt("Power off the System");   /* just in case */
610*fd358c0dSmyers 	}
611*fd358c0dSmyers 
612*fd358c0dSmyers 	/*
613*fd358c0dSmyers 	 * else, graceful shutdown with inittab and all getting involved
614*fd358c0dSmyers 	 */
615*fd358c0dSmyers 	psignal(initpp, SIGPWR);
616*fd358c0dSmyers }
617*fd358c0dSmyers #endif
618*fd358c0dSmyers 
619*fd358c0dSmyers static uint_t
620*fd358c0dSmyers power_issue_shutdown(caddr_t arg)
621*fd358c0dSmyers {
622*fd358c0dSmyers 	struct power_soft_state *softsp = (struct power_soft_state *)arg;
623*fd358c0dSmyers 
624*fd358c0dSmyers 	mutex_enter(&softsp->power_mutex);
625*fd358c0dSmyers 	softsp->events |= PB_BUTTON_PRESS;
626*fd358c0dSmyers 	if (softsp->monitor_on != 0) {
627*fd358c0dSmyers 		mutex_exit(&softsp->power_mutex);
628*fd358c0dSmyers 		pollwakeup(&softsp->pollhd, POLLRDNORM);
629*fd358c0dSmyers 		pollwakeup(&softsp->pollhd, POLLIN);
630*fd358c0dSmyers 		return (DDI_INTR_CLAIMED);
631*fd358c0dSmyers 	}
632*fd358c0dSmyers 
633*fd358c0dSmyers 	if (!softsp->shutdown_pending) {
634*fd358c0dSmyers 		cmn_err(CE_WARN, "Power off requested from power button or "
635*fd358c0dSmyers 		    "SC, powering down the system!");
636*fd358c0dSmyers 		softsp->shutdown_pending = 1;
637*fd358c0dSmyers 		do_shutdown();
638*fd358c0dSmyers 
639*fd358c0dSmyers 		/*
640*fd358c0dSmyers 		 * Wait a while for "do_shutdown()" to shut down the system
641*fd358c0dSmyers 		 * before logging an error message.
642*fd358c0dSmyers 		 */
643*fd358c0dSmyers 		(void) timeout((void(*)(void *))power_log_message, NULL,
644*fd358c0dSmyers 		    100 * hz);
645*fd358c0dSmyers 	}
646*fd358c0dSmyers 	mutex_exit(&softsp->power_mutex);
647*fd358c0dSmyers 
648*fd358c0dSmyers 	return (DDI_INTR_CLAIMED);
649*fd358c0dSmyers }
650*fd358c0dSmyers 
651*fd358c0dSmyers /*
652*fd358c0dSmyers  * Open the device.
653*fd358c0dSmyers  */
654*fd358c0dSmyers /*ARGSUSED*/
655*fd358c0dSmyers static int
656*fd358c0dSmyers power_open(dev_t *devp, int openflags, int otyp, cred_t *credp)
657*fd358c0dSmyers {
658*fd358c0dSmyers 	struct power_soft_state *softsp;
659*fd358c0dSmyers 	int clone;
660*fd358c0dSmyers 
661*fd358c0dSmyers 	if (otyp != OTYP_CHR)
662*fd358c0dSmyers 		return (EINVAL);
663*fd358c0dSmyers 
664*fd358c0dSmyers 	if ((softsp = ddi_get_soft_state(power_state, power_inst)) ==
665*fd358c0dSmyers 	    NULL)
666*fd358c0dSmyers 		return (ENXIO);
667*fd358c0dSmyers 
668*fd358c0dSmyers 	mutex_enter(&softsp->power_mutex);
669*fd358c0dSmyers 	for (clone = 1; clone < POWER_MAX_CLONE; clone++)
670*fd358c0dSmyers 		if (!softsp->clones[clone])
671*fd358c0dSmyers 			break;
672*fd358c0dSmyers 
673*fd358c0dSmyers 	if (clone == POWER_MAX_CLONE) {
674*fd358c0dSmyers 		cmn_err(CE_WARN, "power_open: No more allocation left "
675*fd358c0dSmyers 		    "to create a clone minor.");
676*fd358c0dSmyers 		mutex_exit(&softsp->power_mutex);
677*fd358c0dSmyers 		return (ENXIO);
678*fd358c0dSmyers 	}
679*fd358c0dSmyers 
680*fd358c0dSmyers 	*devp = makedevice(getmajor(*devp), (power_inst << 8) + clone);
681*fd358c0dSmyers 	softsp->clones[clone] = 1;
682*fd358c0dSmyers 	mutex_exit(&softsp->power_mutex);
683*fd358c0dSmyers 
684*fd358c0dSmyers 	return (0);
685*fd358c0dSmyers }
686*fd358c0dSmyers 
687*fd358c0dSmyers /*
688*fd358c0dSmyers  * Close the device.
689*fd358c0dSmyers  */
690*fd358c0dSmyers /*ARGSUSED*/
691*fd358c0dSmyers static  int
692*fd358c0dSmyers power_close(dev_t dev, int openflags, int otyp, cred_t *credp)
693*fd358c0dSmyers {
694*fd358c0dSmyers 	struct power_soft_state *softsp;
695*fd358c0dSmyers 	int clone;
696*fd358c0dSmyers 
697*fd358c0dSmyers 	if (otyp != OTYP_CHR)
698*fd358c0dSmyers 		return (EINVAL);
699*fd358c0dSmyers 
700*fd358c0dSmyers 	if ((softsp = ddi_get_soft_state(power_state, power_inst)) ==
701*fd358c0dSmyers 	    NULL)
702*fd358c0dSmyers 		return (ENXIO);
703*fd358c0dSmyers 
704*fd358c0dSmyers 	clone = POWER_MINOR_TO_CLONE(getminor(dev));
705*fd358c0dSmyers 	mutex_enter(&softsp->power_mutex);
706*fd358c0dSmyers 	if (softsp->monitor_on == clone)
707*fd358c0dSmyers 		softsp->monitor_on = 0;
708*fd358c0dSmyers 	softsp->clones[clone] = 0;
709*fd358c0dSmyers 	mutex_exit(&softsp->power_mutex);
710*fd358c0dSmyers 
711*fd358c0dSmyers 	return (0);
712*fd358c0dSmyers }
713*fd358c0dSmyers 
714*fd358c0dSmyers /*ARGSUSED*/
715*fd358c0dSmyers static  int
716*fd358c0dSmyers power_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cred_p,
717*fd358c0dSmyers     int *rval_p)
718*fd358c0dSmyers {
719*fd358c0dSmyers 	struct power_soft_state *softsp;
720*fd358c0dSmyers 	int clone;
721*fd358c0dSmyers 
722*fd358c0dSmyers 	if ((softsp = ddi_get_soft_state(power_state, power_inst)) ==
723*fd358c0dSmyers 	    NULL)
724*fd358c0dSmyers 		return (ENXIO);
725*fd358c0dSmyers 
726*fd358c0dSmyers 	clone = POWER_MINOR_TO_CLONE(getminor(dev));
727*fd358c0dSmyers 	switch (cmd) {
728*fd358c0dSmyers 	case PB_BEGIN_MONITOR:
729*fd358c0dSmyers 		mutex_enter(&softsp->power_mutex);
730*fd358c0dSmyers 		if (softsp->monitor_on) {
731*fd358c0dSmyers 			mutex_exit(&softsp->power_mutex);
732*fd358c0dSmyers 			return (EBUSY);
733*fd358c0dSmyers 		}
734*fd358c0dSmyers 		softsp->monitor_on = clone;
735*fd358c0dSmyers 		mutex_exit(&softsp->power_mutex);
736*fd358c0dSmyers 		return (0);
737*fd358c0dSmyers 
738*fd358c0dSmyers 	case PB_END_MONITOR:
739*fd358c0dSmyers 		mutex_enter(&softsp->power_mutex);
740*fd358c0dSmyers 
741*fd358c0dSmyers 		/*
742*fd358c0dSmyers 		 * If PB_END_MONITOR is called without first
743*fd358c0dSmyers 		 * calling PB_BEGIN_MONITOR, an error will be
744*fd358c0dSmyers 		 * returned.
745*fd358c0dSmyers 		 */
746*fd358c0dSmyers 		if (!softsp->monitor_on) {
747*fd358c0dSmyers 			mutex_exit(&softsp->power_mutex);
748*fd358c0dSmyers 			return (ENXIO);
749*fd358c0dSmyers 		}
750*fd358c0dSmyers 
751*fd358c0dSmyers 		/*
752*fd358c0dSmyers 		 * This clone is not monitoring the button.
753*fd358c0dSmyers 		 */
754*fd358c0dSmyers 		if (softsp->monitor_on != clone) {
755*fd358c0dSmyers 			mutex_exit(&softsp->power_mutex);
756*fd358c0dSmyers 			return (EINVAL);
757*fd358c0dSmyers 		}
758*fd358c0dSmyers 		softsp->monitor_on = 0;
759*fd358c0dSmyers 		mutex_exit(&softsp->power_mutex);
760*fd358c0dSmyers 		return (0);
761*fd358c0dSmyers 
762*fd358c0dSmyers 	case PB_GET_EVENTS:
763*fd358c0dSmyers 		mutex_enter(&softsp->power_mutex);
764*fd358c0dSmyers 		if (ddi_copyout((void *)&softsp->events, (void *)arg,
765*fd358c0dSmyers 		    sizeof (int), mode) != 0) {
766*fd358c0dSmyers 			mutex_exit(&softsp->power_mutex);
767*fd358c0dSmyers 			return (EFAULT);
768*fd358c0dSmyers 		}
769*fd358c0dSmyers 
770*fd358c0dSmyers 		/*
771*fd358c0dSmyers 		 * This ioctl returned the events detected since last
772*fd358c0dSmyers 		 * call.  Note that any application can get the events
773*fd358c0dSmyers 		 * and clear the event register.
774*fd358c0dSmyers 		 */
775*fd358c0dSmyers 		softsp->events = 0;
776*fd358c0dSmyers 		mutex_exit(&softsp->power_mutex);
777*fd358c0dSmyers 		return (0);
778*fd358c0dSmyers 
779*fd358c0dSmyers 	/*
780*fd358c0dSmyers 	 * This ioctl is used by the test suite.
781*fd358c0dSmyers 	 */
782*fd358c0dSmyers 	case PB_CREATE_BUTTON_EVENT:
783*fd358c0dSmyers #ifdef	ACPI_POWER_BUTTON
784*fd358c0dSmyers 		(UINT32)power_acpi_fixed_event((void *)softsp);
785*fd358c0dSmyers #else
786*fd358c0dSmyers 		if (softsp->power_regs_mapped) {
787*fd358c0dSmyers 			mutex_enter(&softsp->power_intr_mutex);
788*fd358c0dSmyers 			softsp->power_btn_ioctl = B_TRUE;
789*fd358c0dSmyers 			mutex_exit(&softsp->power_intr_mutex);
790*fd358c0dSmyers 		}
791*fd358c0dSmyers 		(void) power_high_intr((caddr_t)softsp);
792*fd358c0dSmyers #endif	/* ACPI_POWER_BUTTON */
793*fd358c0dSmyers 		return (0);
794*fd358c0dSmyers 
795*fd358c0dSmyers 	default:
796*fd358c0dSmyers 		return (ENOTTY);
797*fd358c0dSmyers 	}
798*fd358c0dSmyers }
799*fd358c0dSmyers 
800*fd358c0dSmyers /*ARGSUSED*/
801*fd358c0dSmyers static int
802*fd358c0dSmyers power_chpoll(dev_t dev, short events, int anyyet,
803*fd358c0dSmyers     short *reventsp, struct pollhead **phpp)
804*fd358c0dSmyers {
805*fd358c0dSmyers 	struct power_soft_state *softsp;
806*fd358c0dSmyers 
807*fd358c0dSmyers 	if ((softsp = ddi_get_soft_state(power_state, power_inst)) == NULL)
808*fd358c0dSmyers 		return (ENXIO);
809*fd358c0dSmyers 
810*fd358c0dSmyers 	mutex_enter(&softsp->power_mutex);
811*fd358c0dSmyers 	*reventsp = 0;
812*fd358c0dSmyers 	if (softsp->events)
813*fd358c0dSmyers 		*reventsp = POLLRDNORM|POLLIN;
814*fd358c0dSmyers 	else {
815*fd358c0dSmyers 		if (!anyyet)
816*fd358c0dSmyers 			*phpp = &softsp->pollhd;
817*fd358c0dSmyers 	}
818*fd358c0dSmyers 	mutex_exit(&softsp->power_mutex);
819*fd358c0dSmyers 
820*fd358c0dSmyers 	return (0);
821*fd358c0dSmyers }
822*fd358c0dSmyers 
823*fd358c0dSmyers static void
824*fd358c0dSmyers power_log_message(void)
825*fd358c0dSmyers {
826*fd358c0dSmyers 	struct power_soft_state *softsp;
827*fd358c0dSmyers 
828*fd358c0dSmyers 	if ((softsp = ddi_get_soft_state(power_state, power_inst)) == NULL) {
829*fd358c0dSmyers 		cmn_err(CE_WARN, "Failed to get internal state!");
830*fd358c0dSmyers 		return;
831*fd358c0dSmyers 	}
832*fd358c0dSmyers 
833*fd358c0dSmyers 	mutex_enter(&softsp->power_mutex);
834*fd358c0dSmyers 	softsp->shutdown_pending = 0;
835*fd358c0dSmyers 	cmn_err(CE_WARN, "Failed to shut down the system!");
836*fd358c0dSmyers 	mutex_exit(&softsp->power_mutex);
837*fd358c0dSmyers }
838*fd358c0dSmyers 
839*fd358c0dSmyers #ifdef	ACPI_POWER_BUTTON
840*fd358c0dSmyers /*
841*fd358c0dSmyers  * Given a handle to a device object, locate a _PRW object
842*fd358c0dSmyers  * if present and fetch the GPE info for this device object
843*fd358c0dSmyers  */
844*fd358c0dSmyers static ACPI_STATUS
845*fd358c0dSmyers power_get_prw_gpe(ACPI_HANDLE dev, ACPI_HANDLE *gpe_dev, UINT32 *gpe_num)
846*fd358c0dSmyers {
847*fd358c0dSmyers 	ACPI_BUFFER buf;
848*fd358c0dSmyers 	ACPI_STATUS status;
849*fd358c0dSmyers 	ACPI_HANDLE prw;
850*fd358c0dSmyers 	ACPI_OBJECT *gpe;
851*fd358c0dSmyers 
852*fd358c0dSmyers 	/*
853*fd358c0dSmyers 	 * Evaluate _PRW if present
854*fd358c0dSmyers 	 */
855*fd358c0dSmyers 	status = AcpiGetHandle(dev, "_PRW", &prw);
856*fd358c0dSmyers 	if (status != AE_OK)
857*fd358c0dSmyers 		return (status);
858*fd358c0dSmyers 	buf.Length = ACPI_ALLOCATE_BUFFER;
859*fd358c0dSmyers 	status = AcpiEvaluateObjectTyped(prw, NULL, NULL, &buf,
860*fd358c0dSmyers 	    ACPI_TYPE_PACKAGE);
861*fd358c0dSmyers 	if (status != AE_OK)
862*fd358c0dSmyers 		return (status);
863*fd358c0dSmyers 
864*fd358c0dSmyers 	/*
865*fd358c0dSmyers 	 * Sanity-check the package; need at least two elements
866*fd358c0dSmyers 	 */
867*fd358c0dSmyers 	status = AE_ERROR;
868*fd358c0dSmyers 	if (((ACPI_OBJECT *)buf.Pointer)->Package.Count < 2)
869*fd358c0dSmyers 		goto done;
870*fd358c0dSmyers 
871*fd358c0dSmyers 	gpe = &((ACPI_OBJECT *)buf.Pointer)->Package.Elements[0];
872*fd358c0dSmyers 	if (gpe->Type == ACPI_TYPE_INTEGER) {
873*fd358c0dSmyers 		*gpe_dev = NULL;
874*fd358c0dSmyers 		*gpe_num = gpe->Integer.Value;
875*fd358c0dSmyers 		status = AE_OK;
876*fd358c0dSmyers 	} else if (gpe->Type == ACPI_TYPE_PACKAGE) {
877*fd358c0dSmyers 		if ((gpe->Package.Count != 2) ||
878*fd358c0dSmyers 		    (gpe->Package.Elements[0].Type != ACPI_TYPE_DEVICE) ||
879*fd358c0dSmyers 		    (gpe->Package.Elements[1].Type != ACPI_TYPE_INTEGER))
880*fd358c0dSmyers 			goto done;
881*fd358c0dSmyers 		*gpe_dev = gpe->Package.Elements[0].Reference.Handle;
882*fd358c0dSmyers 		*gpe_num = gpe->Package.Elements[1].Integer.Value;
883*fd358c0dSmyers 		status = AE_OK;
884*fd358c0dSmyers 	}
885*fd358c0dSmyers 
886*fd358c0dSmyers done:
887*fd358c0dSmyers 	AcpiOsFree(buf.Pointer);
888*fd358c0dSmyers 	return (status);
889*fd358c0dSmyers }
890*fd358c0dSmyers 
891*fd358c0dSmyers 
892*fd358c0dSmyers /*
893*fd358c0dSmyers  *
894*fd358c0dSmyers  */
895*fd358c0dSmyers static ACPI_STATUS
896*fd358c0dSmyers acpi_device(ACPI_HANDLE obj, UINT32 nesting, void *context, void **rv)
897*fd358c0dSmyers {
898*fd358c0dSmyers 	*((ACPI_HANDLE *)context) = obj;
899*fd358c0dSmyers 	return (AE_OK);
900*fd358c0dSmyers }
901*fd358c0dSmyers 
902*fd358c0dSmyers /*
903*fd358c0dSmyers  *
904*fd358c0dSmyers  */
905*fd358c0dSmyers static ACPI_HANDLE
906*fd358c0dSmyers probe_acpi_pwrbutton()
907*fd358c0dSmyers {
908*fd358c0dSmyers 	ACPI_HANDLE obj = NULL;
909*fd358c0dSmyers 
910*fd358c0dSmyers 	AcpiGetDevices("PNP0C0C", acpi_device, (void *)&obj, NULL);
911*fd358c0dSmyers 	return (obj);
912*fd358c0dSmyers }
913*fd358c0dSmyers 
914*fd358c0dSmyers static UINT32
915*fd358c0dSmyers power_acpi_fixed_event(void *ctx)
916*fd358c0dSmyers {
917*fd358c0dSmyers 
918*fd358c0dSmyers 	mutex_enter(&((struct power_soft_state *)ctx)->power_intr_mutex);
919*fd358c0dSmyers 	power_button_pressed++;
920*fd358c0dSmyers 	mutex_exit(&((struct power_soft_state *)ctx)->power_intr_mutex);
921*fd358c0dSmyers 
922*fd358c0dSmyers 	/* post softint to issue timeout for power button action */
923*fd358c0dSmyers 	if (((struct power_soft_state *)ctx)->softintr_id != NULL)
924*fd358c0dSmyers 		ddi_trigger_softintr(
925*fd358c0dSmyers 		    ((struct power_soft_state *)ctx)->softintr_id);
926*fd358c0dSmyers 
927*fd358c0dSmyers 	return (AE_OK);
928*fd358c0dSmyers }
929*fd358c0dSmyers 
930*fd358c0dSmyers static void
931*fd358c0dSmyers power_acpi_notify_event(ACPI_HANDLE obj, UINT32 val, void *ctx)
932*fd358c0dSmyers {
933*fd358c0dSmyers 	if (val == 0x80)
934*fd358c0dSmyers 		power_acpi_fixed_event(ctx);
935*fd358c0dSmyers }
936*fd358c0dSmyers 
937*fd358c0dSmyers /*
938*fd358c0dSmyers  *
939*fd358c0dSmyers  */
940*fd358c0dSmyers static int
941*fd358c0dSmyers power_probe_method_button(struct power_soft_state *softsp)
942*fd358c0dSmyers {
943*fd358c0dSmyers 	ACPI_HANDLE button_obj;
944*fd358c0dSmyers 	UINT32 gpe_num;
945*fd358c0dSmyers 	ACPI_HANDLE gpe_dev;
946*fd358c0dSmyers 	ACPI_STATUS status;
947*fd358c0dSmyers 
948*fd358c0dSmyers 	button_obj = probe_acpi_pwrbutton();
949*fd358c0dSmyers 	softsp->button_obj = button_obj;	/* remember obj */
950*fd358c0dSmyers 	if ((button_obj != NULL) &&
951*fd358c0dSmyers 	    (power_get_prw_gpe(button_obj, &gpe_dev, &gpe_num) == AE_OK) &&
952*fd358c0dSmyers 	    (AcpiSetGpeType(gpe_dev, gpe_num, ACPI_GPE_TYPE_WAKE_RUN) ==
953*fd358c0dSmyers 	    AE_OK) &&
954*fd358c0dSmyers 	    (AcpiEnableGpe(gpe_dev, gpe_num, ACPI_NOT_ISR) == AE_OK) &&
955*fd358c0dSmyers 	    (AcpiInstallNotifyHandler(button_obj, ACPI_DEVICE_NOTIFY,
956*fd358c0dSmyers 	    power_acpi_notify_event, (void*)softsp) == AE_OK))
957*fd358c0dSmyers 		return (1);
958*fd358c0dSmyers 	return (0);
959*fd358c0dSmyers }
960*fd358c0dSmyers 
961*fd358c0dSmyers /*
962*fd358c0dSmyers  *
963*fd358c0dSmyers  */
964*fd358c0dSmyers static int
965*fd358c0dSmyers power_probe_fixed_button(struct power_soft_state *softsp)
966*fd358c0dSmyers {
967*fd358c0dSmyers 	FADT_DESCRIPTOR *fadt;
968*fd358c0dSmyers 
969*fd358c0dSmyers 	if (AcpiGetFirmwareTable(FADT_SIG, 1, ACPI_LOGICAL_ADDRESSING,
970*fd358c0dSmyers 	    (ACPI_TABLE_HEADER **) &fadt) != AE_OK)
971*fd358c0dSmyers 		return (0);
972*fd358c0dSmyers 
973*fd358c0dSmyers 	if (!fadt->PwrButton) {
974*fd358c0dSmyers 		if (AcpiInstallFixedEventHandler(ACPI_EVENT_POWER_BUTTON,
975*fd358c0dSmyers 		    power_acpi_fixed_event, (void *)softsp) == AE_OK)
976*fd358c0dSmyers 			return (1);
977*fd358c0dSmyers 	}
978*fd358c0dSmyers 	return (0);
979*fd358c0dSmyers }
980*fd358c0dSmyers 
981*fd358c0dSmyers 
982*fd358c0dSmyers /*
983*fd358c0dSmyers  *
984*fd358c0dSmyers  */
985*fd358c0dSmyers static int
986*fd358c0dSmyers power_attach_acpi(struct power_soft_state *softsp)
987*fd358c0dSmyers {
988*fd358c0dSmyers 
989*fd358c0dSmyers 	/*
990*fd358c0dSmyers 	 * If we've attached anything already, return an error
991*fd358c0dSmyers 	 */
992*fd358c0dSmyers 	if ((softsp->gpe_attached) || (softsp->fixed_attached))
993*fd358c0dSmyers 		return (DDI_FAILURE);
994*fd358c0dSmyers 
995*fd358c0dSmyers 	/*
996*fd358c0dSmyers 	 * attempt to attach both a fixed-event handler and a GPE
997*fd358c0dSmyers 	 * handler; remember what we got
998*fd358c0dSmyers 	 */
999*fd358c0dSmyers 	softsp->fixed_attached = (power_probe_fixed_button(softsp) != 0);
1000*fd358c0dSmyers 	softsp->gpe_attached = (power_probe_method_button(softsp) != 0);
1001*fd358c0dSmyers 
1002*fd358c0dSmyers 	/*
1003*fd358c0dSmyers 	 * If we've attached anything now, return success
1004*fd358c0dSmyers 	 */
1005*fd358c0dSmyers 	if ((softsp->gpe_attached) || (softsp->fixed_attached))
1006*fd358c0dSmyers 		return (DDI_SUCCESS);
1007*fd358c0dSmyers 
1008*fd358c0dSmyers 	return (DDI_FAILURE);
1009*fd358c0dSmyers }
1010*fd358c0dSmyers 
1011*fd358c0dSmyers /*
1012*fd358c0dSmyers  *
1013*fd358c0dSmyers  */
1014*fd358c0dSmyers static void
1015*fd358c0dSmyers power_detach_acpi(struct power_soft_state *softsp)
1016*fd358c0dSmyers {
1017*fd358c0dSmyers 	if (softsp->gpe_attached) {
1018*fd358c0dSmyers 		if (AcpiRemoveNotifyHandler(softsp->button_obj,
1019*fd358c0dSmyers 		    ACPI_DEVICE_NOTIFY, power_acpi_notify_event) != AE_OK)
1020*fd358c0dSmyers 			cmn_err(CE_WARN, "!power: failed to remove Notify"
1021*fd358c0dSmyers 			    " handler");
1022*fd358c0dSmyers 	}
1023*fd358c0dSmyers 
1024*fd358c0dSmyers 	if (softsp->fixed_attached) {
1025*fd358c0dSmyers 		if (AcpiRemoveFixedEventHandler(ACPI_EVENT_POWER_BUTTON,
1026*fd358c0dSmyers 		    power_acpi_fixed_event) != AE_OK)
1027*fd358c0dSmyers 			cmn_err(CE_WARN, "!power: failed to remove Power"
1028*fd358c0dSmyers 			    " Button handler");
1029*fd358c0dSmyers 	}
1030*fd358c0dSmyers }
1031*fd358c0dSmyers 
1032*fd358c0dSmyers #else
1033*fd358c0dSmyers /*
1034*fd358c0dSmyers  * power button register definitions for acpi register on m1535d
1035*fd358c0dSmyers  */
1036*fd358c0dSmyers #define	M1535D_PWR_BTN_REG_01		0x1
1037*fd358c0dSmyers #define	M1535D_PWR_BTN_EVENT_FLAG	0x1
1038*fd358c0dSmyers 
1039*fd358c0dSmyers static int
1040*fd358c0dSmyers power_setup_m1535_regs(dev_info_t *dip, struct power_soft_state *softsp)
1041*fd358c0dSmyers {
1042*fd358c0dSmyers 	ddi_device_acc_attr_t	attr;
1043*fd358c0dSmyers 	uint8_t *reg_base;
1044*fd358c0dSmyers 
1045*fd358c0dSmyers 	attr.devacc_attr_version = DDI_DEVICE_ATTR_V0;
1046*fd358c0dSmyers 	attr.devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC;
1047*fd358c0dSmyers 	attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
1048*fd358c0dSmyers 	if (ddi_regs_map_setup(dip, 0, (caddr_t *)&reg_base, 0, 0, &attr,
1049*fd358c0dSmyers 	    &softsp->power_rhandle) != DDI_SUCCESS) {
1050*fd358c0dSmyers 		return (DDI_FAILURE);
1051*fd358c0dSmyers 	}
1052*fd358c0dSmyers 	softsp->power_btn_reg = &reg_base[M1535D_PWR_BTN_REG_01];
1053*fd358c0dSmyers 	softsp->power_btn_bit = M1535D_PWR_BTN_EVENT_FLAG;
1054*fd358c0dSmyers 	softsp->power_regs_mapped = B_TRUE;
1055*fd358c0dSmyers 	return (DDI_SUCCESS);
1056*fd358c0dSmyers }
1057*fd358c0dSmyers 
1058*fd358c0dSmyers /*
1059*fd358c0dSmyers  * Setup register map for the power button
1060*fd358c0dSmyers  * NOTE:- we only map registers for platforms
1061*fd358c0dSmyers  * binding with the ali1535d+-power compatible
1062*fd358c0dSmyers  * property.
1063*fd358c0dSmyers  */
1064*fd358c0dSmyers static int
1065*fd358c0dSmyers power_setup_regs(struct power_soft_state *softsp)
1066*fd358c0dSmyers {
1067*fd358c0dSmyers 	char	*binding_name;
1068*fd358c0dSmyers 
1069*fd358c0dSmyers 	softsp->power_regs_mapped = B_FALSE;
1070*fd358c0dSmyers 	softsp->power_btn_ioctl = B_FALSE;
1071*fd358c0dSmyers 	binding_name = ddi_binding_name(softsp->dip);
1072*fd358c0dSmyers 	if (strcmp(binding_name, "ali1535d+-power") == 0)
1073*fd358c0dSmyers 		return (power_setup_m1535_regs(softsp->dip, softsp));
1074*fd358c0dSmyers 
1075*fd358c0dSmyers 	return (DDI_SUCCESS);
1076*fd358c0dSmyers }
1077*fd358c0dSmyers 
1078*fd358c0dSmyers static void
1079*fd358c0dSmyers power_free_regs(struct power_soft_state *softsp)
1080*fd358c0dSmyers {
1081*fd358c0dSmyers 	if (softsp->power_regs_mapped)
1082*fd358c0dSmyers 		ddi_regs_map_free(&softsp->power_rhandle);
1083*fd358c0dSmyers }
1084*fd358c0dSmyers #endif	/* ACPI_POWER_BUTTON */
1085