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