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