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