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