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