Merge commit '720b16875295d57e0e6a4e0ec32db4d47412f896'
[unleashed.git] / usr / src / uts / common / io / power.c
blobf227264f0d082873e0aab0a10312957b7a057d70
1 /*
2 * CDDL HEADER START
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.
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.
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]
19 * CDDL HEADER END
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.
29 * Power Button Driver
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:
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
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.
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>
64 #ifdef ACPI_POWER_BUTTON
66 #include <sys/acpi/acpi.h>
67 #include <sys/acpica.h>
69 #else
71 #include <sys/epic.h>
73 * Some #defs that must be here as they differ for power.c
74 * and epic.c
76 #define EPIC_REGS_OFFSET 0x00
77 #define EPIC_REGS_LEN 0x82
81 * This flag, which is set for platforms, that have EPIC processor
82 * to process power button interrupt, helps in executing platform
83 * specific code.
85 static char hasEPIC = B_FALSE;
86 #endif /* ACPI_POWER_BUTTON */
89 * Maximum number of clone minors that is allowed. This value
90 * is defined relatively low to save memory.
92 #define POWER_MAX_CLONE 256
95 * Minor number is instance << 8 + clone minor from range 1-255; clone 0
96 * is reserved for "original" minor.
98 #define POWER_MINOR_TO_CLONE(minor) ((minor) & (POWER_MAX_CLONE - 1))
101 * Power Button Abort Delay
103 #define ABORT_INCREMENT_DELAY 10
106 * FWARC 2005/687: power device compatible property
108 #define POWER_DEVICE_TYPE "power-device-type"
111 * Driver global variables
113 static void *power_state;
114 static int power_inst = -1;
116 static hrtime_t power_button_debounce = MSEC2NSEC(10);
117 static hrtime_t power_button_abort_interval = 1.5 * NANOSEC;
118 static int power_button_abort_presses = 3;
119 static int power_button_abort_enable = 1;
120 static int power_button_enable = 1;
122 static int power_button_pressed = 0;
123 static int power_button_cancel = 0;
124 static int power_button_timeouts = 0;
125 static int timeout_cancel = 0;
126 static int additional_presses = 0;
129 * Function prototypes
131 static int power_attach(dev_info_t *, ddi_attach_cmd_t);
132 static int power_detach(dev_info_t *, ddi_detach_cmd_t);
133 static int power_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
134 static int power_open(dev_t *, int, int, cred_t *);
135 static int power_close(dev_t, int, int, cred_t *);
136 static int power_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
137 static int power_chpoll(dev_t, short, int, short *, struct pollhead **);
138 #ifndef ACPI_POWER_BUTTON
139 static uint_t power_high_intr(caddr_t);
140 #endif
141 static uint_t power_soft_intr(caddr_t);
142 static uint_t power_issue_shutdown(caddr_t);
143 static void power_timeout(caddr_t);
144 static void power_log_message(void);
147 * Structure used in the driver
149 struct power_soft_state {
150 dev_info_t *dip; /* device info pointer */
151 kmutex_t power_mutex; /* mutex lock */
152 kmutex_t power_intr_mutex; /* interrupt mutex lock */
153 ddi_iblock_cookie_t soft_iblock_cookie; /* holds interrupt cookie */
154 ddi_iblock_cookie_t high_iblock_cookie; /* holds interrupt cookie */
155 ddi_softintr_t softintr_id; /* soft interrupt id */
156 uchar_t clones[POWER_MAX_CLONE]; /* array of minor clones */
157 int monitor_on; /* clone monitoring the button event */
158 /* clone 0 indicates no one is */
159 /* monitoring the button event */
160 pollhead_t pollhd; /* poll head struct */
161 int events; /* bit map of occured events */
162 int shutdown_pending; /* system shutdown in progress */
163 #ifdef ACPI_POWER_BUTTON
164 boolean_t fixed_attached; /* true means fixed is attached */
165 boolean_t gpe_attached; /* true means GPE is attached */
166 ACPI_HANDLE button_obj; /* handle to device power button */
167 #else
168 ddi_acc_handle_t power_rhandle; /* power button register handle */
169 uint8_t *power_btn_reg; /* power button register address */
170 uint8_t power_btn_bit; /* power button register bit */
171 boolean_t power_regs_mapped; /* flag to tell if regs mapped */
172 boolean_t power_btn_ioctl; /* flag to specify ioctl request */
173 #endif
176 static void power_gen_sysevent(struct power_soft_state *);
178 #ifdef ACPI_POWER_BUTTON
179 static int power_attach_acpi(struct power_soft_state *softsp);
180 static void power_detach_acpi(struct power_soft_state *softsp);
181 static UINT32 power_acpi_fixed_event(void *ctx);
182 #else
183 static int power_setup_regs(struct power_soft_state *softsp);
184 static void power_free_regs(struct power_soft_state *softsp);
185 #endif /* ACPI_POWER_BUTTON */
188 * Configuration data structures
190 static struct cb_ops power_cb_ops = {
191 power_open, /* open */
192 power_close, /* close */
193 nodev, /* strategy */
194 nodev, /* print */
195 nodev, /* dump */
196 nodev, /* read */
197 nodev, /* write */
198 power_ioctl, /* ioctl */
199 nodev, /* devmap */
200 nodev, /* mmap */
201 nodev, /* segmap */
202 power_chpoll, /* poll */
203 ddi_prop_op, /* cb_prop_op */
204 NULL, /* streamtab */
205 D_MP | D_NEW, /* Driver compatibility flag */
206 CB_REV, /* rev */
207 nodev, /* cb_aread */
208 nodev /* cb_awrite */
211 static struct dev_ops power_ops = {
212 DEVO_REV, /* devo_rev, */
213 0, /* refcnt */
214 power_getinfo, /* getinfo */
215 nulldev, /* identify */
216 nulldev, /* probe */
217 power_attach, /* attach */
218 power_detach, /* detach */
219 nodev, /* reset */
220 &power_cb_ops, /* cb_ops */
221 NULL, /* bus_ops */
222 NULL, /* power */
223 ddi_quiesce_not_supported, /* devo_quiesce */
226 static struct modldrv modldrv = {
227 &mod_driverops, /* Type of module. This one is a driver */
228 "power button driver", /* name of module */
229 &power_ops, /* driver ops */
232 static struct modlinkage modlinkage = {
233 MODREV_1,
234 (void *)&modldrv,
235 NULL
239 * These are the module initialization routines.
243 _init(void)
245 int error;
247 if ((error = ddi_soft_state_init(&power_state,
248 sizeof (struct power_soft_state), 0)) != 0)
249 return (error);
251 if ((error = mod_install(&modlinkage)) != 0)
252 ddi_soft_state_fini(&power_state);
254 return (error);
258 _fini(void)
260 int error;
262 if ((error = mod_remove(&modlinkage)) == 0)
263 ddi_soft_state_fini(&power_state);
265 return (error);
269 _info(struct modinfo *modinfop)
271 return (mod_info(&modlinkage, modinfop));
274 /*ARGSUSED*/
275 static int
276 power_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg,
277 void **result)
279 struct power_soft_state *softsp;
281 if (power_inst == -1)
282 return (DDI_FAILURE);
284 switch (infocmd) {
285 case DDI_INFO_DEVT2DEVINFO:
286 if ((softsp = ddi_get_soft_state(power_state, power_inst))
287 == NULL)
288 return (DDI_FAILURE);
289 *result = (void *)softsp->dip;
290 return (DDI_SUCCESS);
292 case DDI_INFO_DEVT2INSTANCE:
293 *result = (void *)(uintptr_t)power_inst;
294 return (DDI_SUCCESS);
296 default:
297 return (DDI_FAILURE);
301 static int
302 power_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
304 struct power_soft_state *softsp;
306 switch (cmd) {
307 case DDI_ATTACH:
308 break;
309 case DDI_RESUME:
310 return (DDI_SUCCESS);
311 default:
312 return (DDI_FAILURE);
316 * If the power node doesn't have "button" property, quietly
317 * fail to attach.
319 if (ddi_prop_exists(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
320 "button") == 0)
321 return (DDI_FAILURE);
323 if (power_inst != -1)
324 return (DDI_FAILURE);
326 power_inst = ddi_get_instance(dip);
328 if (ddi_soft_state_zalloc(power_state, power_inst) != DDI_SUCCESS)
329 return (DDI_FAILURE);
331 if (ddi_create_minor_node(dip, "power_button", S_IFCHR,
332 (power_inst << 8) + 0, "ddi_power_button", 0) != DDI_SUCCESS)
333 return (DDI_FAILURE);
335 softsp = ddi_get_soft_state(power_state, power_inst);
336 softsp->dip = dip;
338 #ifdef ACPI_POWER_BUTTON
339 (void) power_attach_acpi(softsp);
340 #else
341 if (power_setup_regs(softsp) != DDI_SUCCESS) {
342 cmn_err(CE_WARN, "power_attach: failed to setup registers");
343 goto error;
346 if (ddi_get_iblock_cookie(dip, 0,
347 &softsp->high_iblock_cookie) != DDI_SUCCESS) {
348 cmn_err(CE_WARN, "power_attach: ddi_get_soft_iblock_cookie "
349 "failed.");
350 goto error;
352 mutex_init(&softsp->power_intr_mutex, NULL, MUTEX_DRIVER,
353 softsp->high_iblock_cookie);
355 if (ddi_add_intr(dip, 0, &softsp->high_iblock_cookie, NULL,
356 power_high_intr, (caddr_t)softsp) != DDI_SUCCESS) {
357 cmn_err(CE_WARN, "power_attach: failed to add high-level "
358 " interrupt handler.");
359 mutex_destroy(&softsp->power_intr_mutex);
360 goto error;
362 #endif /* ACPI_POWER_BUTTON */
364 if (ddi_get_soft_iblock_cookie(dip, DDI_SOFTINT_LOW,
365 &softsp->soft_iblock_cookie) != DDI_SUCCESS) {
366 cmn_err(CE_WARN, "power_attach: ddi_get_soft_iblock_cookie "
367 "failed.");
368 mutex_destroy(&softsp->power_intr_mutex);
369 ddi_remove_intr(dip, 0, NULL);
370 goto error;
373 mutex_init(&softsp->power_mutex, NULL, MUTEX_DRIVER,
374 (void *)softsp->soft_iblock_cookie);
376 if (ddi_add_softintr(dip, DDI_SOFTINT_LOW, &softsp->softintr_id,
377 NULL, NULL, power_soft_intr, (caddr_t)softsp) != DDI_SUCCESS) {
378 cmn_err(CE_WARN, "power_attach: failed to add soft "
379 "interrupt handler.");
380 mutex_destroy(&softsp->power_mutex);
381 mutex_destroy(&softsp->power_intr_mutex);
382 ddi_remove_intr(dip, 0, NULL);
383 goto error;
386 ddi_report_dev(dip);
388 return (DDI_SUCCESS);
390 error:
391 #ifdef ACPI_POWER_BUTTON
393 * detach ACPI power button
395 power_detach_acpi(softsp);
396 #else
397 power_free_regs(softsp);
398 #endif /* ACPI_POWER_BUTTON */
399 ddi_remove_minor_node(dip, "power_button");
400 ddi_soft_state_free(power_state, power_inst);
401 return (DDI_FAILURE);
404 /*ARGSUSED*/
406 * This driver doesn't detach.
408 static int
409 power_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
412 * Since the "power" node has "reg" property, as part of
413 * the suspend operation, detach(9E) entry point is called.
414 * There is no state to save, since this register is used
415 * by OBP to power off the system and the state of the
416 * power off is preserved by hardware.
418 return ((cmd == DDI_SUSPEND) ? DDI_SUCCESS :
419 DDI_FAILURE);
423 #ifndef ACPI_POWER_BUTTON
425 * Handler for the high-level interrupt.
427 static uint_t
428 power_high_intr(caddr_t arg)
430 struct power_soft_state *softsp = (struct power_soft_state *)arg;
431 ddi_acc_handle_t hdl = softsp->power_rhandle;
432 uint8_t reg;
434 hrtime_t tstamp;
435 static hrtime_t o_tstamp = 0;
436 static hrtime_t power_button_tstamp = 0;
437 static int power_button_cnt;
439 if (softsp->power_regs_mapped) {
440 mutex_enter(&softsp->power_intr_mutex);
442 /* Check if power button interrupt is delivered by EPIC HW */
443 if (hasEPIC) {
444 /* read isr - first issue command */
445 EPIC_WR(hdl, softsp->power_btn_reg,
446 EPIC_ATOM_INTR_READ);
447 /* next, read the reg */
448 EPIC_RD(hdl, softsp->power_btn_reg, reg);
450 if (reg & EPIC_FIRE_INTERRUPT) { /* PB pressed */
451 /* clear the interrupt */
452 EPIC_WR(hdl, softsp->power_btn_reg,
453 EPIC_ATOM_INTR_CLEAR);
454 } else {
455 if (!softsp->power_btn_ioctl) {
456 mutex_exit(&softsp->power_intr_mutex);
457 return (DDI_INTR_CLAIMED);
459 softsp->power_btn_ioctl = B_FALSE;
461 } else {
462 reg = ddi_get8(hdl, softsp->power_btn_reg);
463 if (reg & softsp->power_btn_bit) {
464 reg &= softsp->power_btn_bit;
465 ddi_put8(hdl, softsp->power_btn_reg, reg);
466 (void) ddi_get8(hdl, softsp->power_btn_reg);
467 } else {
468 if (!softsp->power_btn_ioctl) {
469 mutex_exit(&softsp->power_intr_mutex);
470 return (DDI_INTR_CLAIMED);
472 softsp->power_btn_ioctl = B_FALSE;
475 mutex_exit(&softsp->power_intr_mutex);
478 tstamp = gethrtime();
480 /* need to deal with power button debounce */
481 if (o_tstamp && (tstamp - o_tstamp) < power_button_debounce) {
482 o_tstamp = tstamp;
483 return (DDI_INTR_CLAIMED);
485 o_tstamp = tstamp;
487 power_button_cnt++;
489 mutex_enter(&softsp->power_intr_mutex);
490 power_button_pressed++;
491 mutex_exit(&softsp->power_intr_mutex);
494 * If power button abort is enabled and power button was pressed
495 * power_button_abort_presses times within power_button_abort_interval
496 * then call abort_sequence_enter();
498 if (power_button_abort_enable) {
499 if (power_button_abort_presses == 1 ||
500 tstamp < (power_button_tstamp +
501 power_button_abort_interval)) {
502 if (power_button_cnt == power_button_abort_presses) {
503 mutex_enter(&softsp->power_intr_mutex);
504 power_button_cancel += power_button_timeouts;
505 power_button_pressed = 0;
506 mutex_exit(&softsp->power_intr_mutex);
507 power_button_cnt = 0;
508 abort_sequence_enter("Power Button Abort");
509 return (DDI_INTR_CLAIMED);
511 } else {
512 power_button_cnt = 1;
513 power_button_tstamp = tstamp;
517 if (!power_button_enable)
518 return (DDI_INTR_CLAIMED);
520 /* post softint to issue timeout for power button action */
521 if (softsp->softintr_id != NULL)
522 ddi_trigger_softintr(softsp->softintr_id);
524 return (DDI_INTR_CLAIMED);
526 #endif /* ifndef ACPI_POWER_BUTTON */
529 * Handle the softints....
531 * If only one softint is posted for several button presses, record
532 * the number of additional presses just incase this was actually not quite
533 * an Abort sequence so that we can log this event later.
535 * Issue a timeout with a duration being a fraction larger than
536 * the specified Abort interval inorder to perform a power down if required.
538 static uint_t
539 power_soft_intr(caddr_t arg)
541 struct power_soft_state *softsp = (struct power_soft_state *)arg;
543 if (!power_button_abort_enable)
544 return (power_issue_shutdown(arg));
546 mutex_enter(&softsp->power_intr_mutex);
547 if (!power_button_pressed) {
548 mutex_exit(&softsp->power_intr_mutex);
549 return (DDI_INTR_CLAIMED);
553 * Schedule a timeout to do the necessary
554 * work for shutdown, only one timeout for
555 * n presses if power button was pressed
556 * more than once before softint fired
558 if (power_button_pressed > 1)
559 additional_presses += power_button_pressed - 1;
561 timeout_cancel = 0;
562 power_button_pressed = 0;
563 power_button_timeouts++;
564 mutex_exit(&softsp->power_intr_mutex);
565 (void) timeout((void(*)(void *))power_timeout,
566 softsp, NSEC_TO_TICK(power_button_abort_interval) +
567 ABORT_INCREMENT_DELAY);
569 return (DDI_INTR_CLAIMED);
573 * Upon receiving a timeout the following is determined:
575 * If an Abort sequence was issued, then we cancel all outstanding timeouts
576 * and additional presses prior to the Abort sequence.
578 * If we had multiple timeouts issued and the abort sequence was not met,
579 * then we had more than one button press to power down the machine. We
580 * were probably trying to issue an abort. So log a message indicating this
581 * and cancel all outstanding timeouts.
583 * If we had just one timeout and the abort sequence was not met then
584 * we really did want to power down the machine, so call power_issue_shutdown()
585 * to do the work and schedule a power down
587 static void
588 power_timeout(caddr_t arg)
590 struct power_soft_state *softsp = (struct power_soft_state *)arg;
591 static int first = 0;
594 * Abort was generated cancel all outstanding power
595 * button timeouts
597 mutex_enter(&softsp->power_intr_mutex);
598 if (power_button_cancel) {
599 power_button_cancel--;
600 power_button_timeouts--;
601 if (!first) {
602 first++;
603 additional_presses = 0;
605 mutex_exit(&softsp->power_intr_mutex);
606 return;
608 first = 0;
611 * We get here if the timeout(s) have fired and they were
612 * not issued prior to an abort.
614 * If we had more than one press in the interval we were
615 * probably trying to issue an abort, but didnt press the
616 * required number within the interval. Hence cancel all
617 * timeouts and do not continue towards shutdown.
619 if (!timeout_cancel) {
620 timeout_cancel = power_button_timeouts +
621 additional_presses;
623 power_button_timeouts--;
624 if (!power_button_timeouts)
625 additional_presses = 0;
627 if (timeout_cancel > 1) {
628 mutex_exit(&softsp->power_intr_mutex);
629 cmn_err(CE_NOTE, "Power Button pressed "
630 "%d times, cancelling all requests",
631 timeout_cancel);
632 return;
634 mutex_exit(&softsp->power_intr_mutex);
636 /* Go and do the work to request shutdown */
637 (void) power_issue_shutdown((caddr_t)softsp);
638 return;
641 power_button_timeouts--;
642 if (!power_button_timeouts)
643 additional_presses = 0;
644 mutex_exit(&softsp->power_intr_mutex);
647 #ifdef ACPI_POWER_BUTTON
648 static void
649 do_shutdown(void)
651 proc_t *initpp;
654 * If we're still booting and init(1) isn't set up yet, simply halt.
656 mutex_enter(&pidlock);
657 initpp = prfind(P_INITPID);
658 mutex_exit(&pidlock);
659 if (initpp == NULL) {
660 extern void halt(char *);
661 halt("Power off the System"); /* just in case */
665 * else, graceful shutdown with inittab and all getting involved
667 psignal(initpp, SIGPWR);
669 #endif
671 static uint_t
672 power_issue_shutdown(caddr_t arg)
674 struct power_soft_state *softsp = (struct power_soft_state *)arg;
676 mutex_enter(&softsp->power_mutex);
677 softsp->events |= PB_BUTTON_PRESS;
678 if (softsp->monitor_on != 0) {
679 mutex_exit(&softsp->power_mutex);
680 pollwakeup(&softsp->pollhd, POLLRDNORM);
681 pollwakeup(&softsp->pollhd, POLLIN);
682 power_gen_sysevent(softsp);
683 return (DDI_INTR_CLAIMED);
686 if (!softsp->shutdown_pending) {
687 cmn_err(CE_WARN, "Power off requested from power button or "
688 "SC, powering down the system!");
689 softsp->shutdown_pending = 1;
690 do_shutdown();
693 * Wait a while for "do_shutdown()" to shut down the system
694 * before logging an error message.
696 (void) timeout((void(*)(void *))power_log_message, NULL,
697 100 * hz);
699 mutex_exit(&softsp->power_mutex);
701 return (DDI_INTR_CLAIMED);
704 static void
705 power_gen_sysevent(struct power_soft_state *softsp)
707 nvlist_t *attr_list = NULL;
708 int err;
709 char pathname[MAXPATHLEN];
710 char hid[9] = "\0";
712 /* Allocate and build sysevent attribute list */
713 err = nvlist_alloc(&attr_list, NV_UNIQUE_NAME_TYPE, DDI_NOSLEEP);
714 if (err != 0) {
715 cmn_err(CE_WARN,
716 "cannot allocate memory for sysevent attributes\n");
717 return;
720 #ifdef ACPI_POWER_BUTTON
721 /* Only control method power button has HID */
722 if (softsp->gpe_attached) {
723 (void) strlcpy(hid, "PNP0C0C", sizeof (hid));
725 #endif
727 err = nvlist_add_string(attr_list, PWRCTL_DEV_HID, hid);
728 if (err != 0) {
729 cmn_err(CE_WARN,
730 "Failed to add attr [%s] for %s/%s event",
731 PWRCTL_DEV_HID, EC_PWRCTL, ESC_PWRCTL_POWER_BUTTON);
732 nvlist_free(attr_list);
733 return;
736 (void) ddi_pathname(softsp->dip, pathname);
737 err = nvlist_add_string(attr_list, PWRCTL_DEV_PHYS_PATH, pathname);
738 if (err != 0) {
739 cmn_err(CE_WARN,
740 "Failed to add attr [%s] for %s/%s event",
741 PWRCTL_DEV_PHYS_PATH, EC_PWRCTL, ESC_PWRCTL_POWER_BUTTON);
742 nvlist_free(attr_list);
743 return;
746 /* Generate/log sysevent */
747 err = ddi_log_sysevent(softsp->dip, DDI_VENDOR_SUNW, EC_PWRCTL,
748 ESC_PWRCTL_POWER_BUTTON, attr_list, NULL, DDI_NOSLEEP);
749 if (err != DDI_SUCCESS) {
750 cmn_err(CE_WARN,
751 "cannot log sysevent, err code %x\n", err);
754 nvlist_free(attr_list);
758 * Open the device.
760 /*ARGSUSED*/
761 static int
762 power_open(dev_t *devp, int openflags, int otyp, cred_t *credp)
764 struct power_soft_state *softsp;
765 int clone;
767 if (otyp != OTYP_CHR)
768 return (EINVAL);
770 if ((softsp = ddi_get_soft_state(power_state, power_inst)) ==
771 NULL)
772 return (ENXIO);
774 mutex_enter(&softsp->power_mutex);
775 for (clone = 1; clone < POWER_MAX_CLONE; clone++)
776 if (!softsp->clones[clone])
777 break;
779 if (clone == POWER_MAX_CLONE) {
780 cmn_err(CE_WARN, "power_open: No more allocation left "
781 "to create a clone minor.");
782 mutex_exit(&softsp->power_mutex);
783 return (ENXIO);
786 *devp = makedevice(getmajor(*devp), (power_inst << 8) + clone);
787 softsp->clones[clone] = 1;
788 mutex_exit(&softsp->power_mutex);
790 return (0);
794 * Close the device.
796 /*ARGSUSED*/
797 static int
798 power_close(dev_t dev, int openflags, int otyp, cred_t *credp)
800 struct power_soft_state *softsp;
801 int clone;
803 if (otyp != OTYP_CHR)
804 return (EINVAL);
806 if ((softsp = ddi_get_soft_state(power_state, power_inst)) ==
807 NULL)
808 return (ENXIO);
810 clone = POWER_MINOR_TO_CLONE(getminor(dev));
811 mutex_enter(&softsp->power_mutex);
812 if (softsp->monitor_on == clone)
813 softsp->monitor_on = 0;
814 softsp->clones[clone] = 0;
815 mutex_exit(&softsp->power_mutex);
817 return (0);
820 /*ARGSUSED*/
821 static int
822 power_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cred_p,
823 int *rval_p)
825 struct power_soft_state *softsp;
826 int clone;
828 if ((softsp = ddi_get_soft_state(power_state, power_inst)) ==
829 NULL)
830 return (ENXIO);
832 clone = POWER_MINOR_TO_CLONE(getminor(dev));
833 switch (cmd) {
834 case PB_BEGIN_MONITOR:
835 mutex_enter(&softsp->power_mutex);
836 if (softsp->monitor_on) {
837 mutex_exit(&softsp->power_mutex);
838 return (EBUSY);
840 softsp->monitor_on = clone;
841 mutex_exit(&softsp->power_mutex);
842 return (0);
844 case PB_END_MONITOR:
845 mutex_enter(&softsp->power_mutex);
848 * If PB_END_MONITOR is called without first
849 * calling PB_BEGIN_MONITOR, an error will be
850 * returned.
852 if (!softsp->monitor_on) {
853 mutex_exit(&softsp->power_mutex);
854 return (ENXIO);
858 * This clone is not monitoring the button.
860 if (softsp->monitor_on != clone) {
861 mutex_exit(&softsp->power_mutex);
862 return (EINVAL);
864 softsp->monitor_on = 0;
865 mutex_exit(&softsp->power_mutex);
866 return (0);
868 case PB_GET_EVENTS:
869 mutex_enter(&softsp->power_mutex);
870 if (ddi_copyout((void *)&softsp->events, (void *)arg,
871 sizeof (int), mode) != 0) {
872 mutex_exit(&softsp->power_mutex);
873 return (EFAULT);
877 * This ioctl returned the events detected since last
878 * call. Note that any application can get the events
879 * and clear the event register.
881 softsp->events = 0;
882 mutex_exit(&softsp->power_mutex);
883 return (0);
886 * This ioctl is used by the test suite.
888 case PB_CREATE_BUTTON_EVENT:
889 #ifdef ACPI_POWER_BUTTON
890 (UINT32)power_acpi_fixed_event((void *)softsp);
891 #else
892 if (softsp->power_regs_mapped) {
893 mutex_enter(&softsp->power_intr_mutex);
894 softsp->power_btn_ioctl = B_TRUE;
895 mutex_exit(&softsp->power_intr_mutex);
897 (void) power_high_intr((caddr_t)softsp);
898 #endif /* ACPI_POWER_BUTTON */
899 return (0);
901 default:
902 return (ENOTTY);
906 /*ARGSUSED*/
907 static int
908 power_chpoll(dev_t dev, short events, int anyyet,
909 short *reventsp, struct pollhead **phpp)
911 struct power_soft_state *softsp;
913 if ((softsp = ddi_get_soft_state(power_state, power_inst)) == NULL)
914 return (ENXIO);
916 mutex_enter(&softsp->power_mutex);
917 *reventsp = 0;
918 if (softsp->events)
919 *reventsp = POLLRDNORM|POLLIN;
920 else {
921 if (!anyyet)
922 *phpp = &softsp->pollhd;
924 mutex_exit(&softsp->power_mutex);
926 return (0);
929 static void
930 power_log_message(void)
932 struct power_soft_state *softsp;
934 if ((softsp = ddi_get_soft_state(power_state, power_inst)) == NULL) {
935 cmn_err(CE_WARN, "Failed to get internal state!");
936 return;
939 mutex_enter(&softsp->power_mutex);
940 softsp->shutdown_pending = 0;
941 cmn_err(CE_WARN, "Failed to shut down the system!");
942 mutex_exit(&softsp->power_mutex);
945 #ifdef ACPI_POWER_BUTTON
949 /*ARGSUSED*/
950 static ACPI_STATUS
951 acpi_device(ACPI_HANDLE obj, UINT32 nesting, void *context, void **rv)
954 *((ACPI_HANDLE *)context) = obj;
955 return (AE_OK);
961 static ACPI_HANDLE
962 probe_acpi_pwrbutton()
964 ACPI_HANDLE obj = NULL;
966 (void) AcpiGetDevices("PNP0C0C", acpi_device, (void *)&obj, NULL);
967 return (obj);
970 static UINT32
971 power_acpi_fixed_event(void *ctx)
974 mutex_enter(&((struct power_soft_state *)ctx)->power_intr_mutex);
975 power_button_pressed++;
976 mutex_exit(&((struct power_soft_state *)ctx)->power_intr_mutex);
978 /* post softint to issue timeout for power button action */
979 if (((struct power_soft_state *)ctx)->softintr_id != NULL)
980 ddi_trigger_softintr(
981 ((struct power_soft_state *)ctx)->softintr_id);
983 return (AE_OK);
986 /*ARGSUSED*/
987 static void
988 power_acpi_notify_event(ACPI_HANDLE obj, UINT32 val, void *ctx)
990 if (val == 0x80)
991 (void) power_acpi_fixed_event(ctx);
997 static int
998 power_probe_method_button(struct power_soft_state *softsp)
1000 ACPI_HANDLE button_obj;
1002 button_obj = probe_acpi_pwrbutton();
1003 softsp->button_obj = button_obj; /* remember obj */
1004 if ((button_obj != NULL) &&
1005 (AcpiInstallNotifyHandler(button_obj, ACPI_DEVICE_NOTIFY,
1006 power_acpi_notify_event, (void*)softsp) == AE_OK))
1007 return (1);
1008 return (0);
1014 static int
1015 power_probe_fixed_button(struct power_soft_state *softsp)
1017 ACPI_TABLE_FADT *fadt;
1019 if (AcpiGetTable(ACPI_SIG_FADT, 1, (ACPI_TABLE_HEADER **) &fadt) !=
1020 AE_OK)
1021 return (0);
1023 if ((fadt->Flags & ACPI_FADT_POWER_BUTTON) == 0) {
1024 if (AcpiInstallFixedEventHandler(ACPI_EVENT_POWER_BUTTON,
1025 power_acpi_fixed_event, (void *)softsp) == AE_OK)
1026 return (1);
1028 return (0);
1035 static int
1036 power_attach_acpi(struct power_soft_state *softsp)
1040 * If we've attached anything already, return an error
1042 if ((softsp->gpe_attached) || (softsp->fixed_attached))
1043 return (DDI_FAILURE);
1046 * attempt to attach both a fixed-event handler and a GPE
1047 * handler; remember what we got
1049 softsp->fixed_attached = (power_probe_fixed_button(softsp) != 0);
1050 softsp->gpe_attached = (power_probe_method_button(softsp) != 0);
1053 * If we've attached anything now, return success
1055 if ((softsp->gpe_attached) || (softsp->fixed_attached))
1056 return (DDI_SUCCESS);
1058 return (DDI_FAILURE);
1064 static void
1065 power_detach_acpi(struct power_soft_state *softsp)
1067 if (softsp->gpe_attached) {
1068 if (AcpiRemoveNotifyHandler(softsp->button_obj,
1069 ACPI_DEVICE_NOTIFY, power_acpi_notify_event) != AE_OK)
1070 cmn_err(CE_WARN, "!power: failed to remove Notify"
1071 " handler");
1074 if (softsp->fixed_attached) {
1075 if (AcpiRemoveFixedEventHandler(ACPI_EVENT_POWER_BUTTON,
1076 power_acpi_fixed_event) != AE_OK)
1077 cmn_err(CE_WARN, "!power: failed to remove Power"
1078 " Button handler");
1082 #else
1084 * Code for platforms that have EPIC processor for processing power
1085 * button interrupts.
1087 static int
1088 power_setup_epic_regs(dev_info_t *dip, struct power_soft_state *softsp)
1090 ddi_device_acc_attr_t attr;
1091 uint8_t *reg_base;
1093 attr.devacc_attr_version = DDI_DEVICE_ATTR_V0;
1094 attr.devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC;
1095 attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
1096 if (ddi_regs_map_setup(dip, 0, (caddr_t *)&reg_base,
1097 EPIC_REGS_OFFSET, EPIC_REGS_LEN, &attr,
1098 &softsp->power_rhandle) != DDI_SUCCESS) {
1099 return (DDI_FAILURE);
1102 softsp->power_btn_reg = reg_base;
1103 softsp->power_regs_mapped = B_TRUE;
1105 /* Clear power button interrupt first */
1106 EPIC_WR(softsp->power_rhandle, softsp->power_btn_reg,
1107 EPIC_ATOM_INTR_CLEAR);
1109 /* Enable EPIC interrupt for power button single press event */
1110 EPIC_WR(softsp->power_rhandle, softsp->power_btn_reg,
1111 EPIC_ATOM_INTR_ENABLE);
1114 * At this point, EPIC interrupt processing is fully initialised.
1116 hasEPIC = B_TRUE;
1117 return (DDI_SUCCESS);
1122 * power button register definitions for acpi register on m1535d
1124 #define M1535D_PWR_BTN_REG_01 0x1
1125 #define M1535D_PWR_BTN_EVENT_FLAG 0x1
1127 static int
1128 power_setup_m1535_regs(dev_info_t *dip, struct power_soft_state *softsp)
1130 ddi_device_acc_attr_t attr;
1131 uint8_t *reg_base;
1133 attr.devacc_attr_version = DDI_DEVICE_ATTR_V0;
1134 attr.devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC;
1135 attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
1136 if (ddi_regs_map_setup(dip, 0, (caddr_t *)&reg_base, 0, 0, &attr,
1137 &softsp->power_rhandle) != DDI_SUCCESS) {
1138 return (DDI_FAILURE);
1140 softsp->power_btn_reg = &reg_base[M1535D_PWR_BTN_REG_01];
1141 softsp->power_btn_bit = M1535D_PWR_BTN_EVENT_FLAG;
1142 softsp->power_regs_mapped = B_TRUE;
1143 return (DDI_SUCCESS);
1147 * MBC Fire/SSI Interrupt Status Register definitions
1149 #define FIRE_SSI_ISR 0x0
1150 #define FIRE_SSI_INTR_ENA 0x8
1151 #define FIRE_SSI_SHUTDOWN_REQ 0x4
1153 static int
1154 power_setup_mbc_regs(dev_info_t *dip, struct power_soft_state *softsp)
1156 ddi_device_acc_attr_t attr;
1157 uint8_t *reg_base;
1158 ddi_acc_handle_t hdl;
1159 uint8_t reg;
1161 attr.devacc_attr_version = DDI_DEVICE_ATTR_V0;
1162 attr.devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC;
1163 attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
1164 if (ddi_regs_map_setup(dip, 0, (caddr_t *)&reg_base, 0, 0, &attr,
1165 &softsp->power_rhandle) != DDI_SUCCESS) {
1166 return (DDI_FAILURE);
1168 softsp->power_btn_reg = &reg_base[FIRE_SSI_ISR];
1169 softsp->power_btn_bit = FIRE_SSI_SHUTDOWN_REQ;
1170 hdl = softsp->power_rhandle;
1172 * Clear MBC Fire Power Button interrupt, if set.
1174 reg = ddi_get8(hdl, softsp->power_btn_reg);
1175 if (reg & softsp->power_btn_bit) {
1176 reg &= softsp->power_btn_bit;
1177 ddi_put8(hdl, softsp->power_btn_reg, reg);
1178 (void) ddi_get8(hdl, softsp->power_btn_reg);
1181 * Enable MBC Fire Power Button interrupt.
1183 reg = ddi_get8(hdl, &reg_base[FIRE_SSI_INTR_ENA]);
1184 reg |= FIRE_SSI_SHUTDOWN_REQ;
1185 ddi_put8(hdl, &reg_base[FIRE_SSI_INTR_ENA], reg);
1187 softsp->power_regs_mapped = B_TRUE;
1189 return (DDI_SUCCESS);
1193 * Setup register map for the power button
1194 * NOTE:- we only map registers for platforms if
1195 * the OBP power device has any of the following
1196 * properties:
1198 * a) Boston: power-device-type set to "SUNW,mbc"
1199 * b) Seattle: power-device-type set to "SUNW,pic18lf65j10"
1200 * c) Chalupa: compatible set to "ali1535d+-power"
1202 * Cases (a) and (b) are defined in FWARC 2005/687.
1203 * If none of the above conditions are true, then we
1204 * do not need to map in any registers, and this
1205 * function can simply return DDI_SUCCESS.
1207 static int
1208 power_setup_regs(struct power_soft_state *softsp)
1210 char *binding_name;
1211 char *power_type = NULL;
1212 int retval = DDI_SUCCESS;
1214 softsp->power_regs_mapped = B_FALSE;
1215 softsp->power_btn_ioctl = B_FALSE;
1216 binding_name = ddi_binding_name(softsp->dip);
1217 if (ddi_prop_lookup_string(DDI_DEV_T_ANY, softsp->dip,
1218 DDI_PROP_DONTPASS, POWER_DEVICE_TYPE,
1219 &power_type) == DDI_PROP_SUCCESS) {
1220 if (strcmp(power_type, "SUNW,mbc") == 0) {
1221 retval = power_setup_mbc_regs(softsp->dip, softsp);
1222 } else if (strcmp(power_type, "SUNW,pic18lf65j10") == 0) {
1223 retval = power_setup_epic_regs(softsp->dip, softsp);
1224 } else {
1225 cmn_err(CE_WARN, "unexpected power-device-type: %s\n",
1226 power_type);
1227 retval = DDI_FAILURE;
1229 ddi_prop_free(power_type);
1230 } else if (strcmp(binding_name, "ali1535d+-power") == 0) {
1231 retval = power_setup_m1535_regs(softsp->dip, softsp);
1235 * If power-device-type does not exist AND the binding name is not
1236 * "ali1535d+-power", that means there is no additional HW and hence
1237 * no extra processing is necessary. In that case, retval should still
1238 * be set to its initial value of DDI_SUCCESS.
1240 return (retval);
1243 static void
1244 power_free_regs(struct power_soft_state *softsp)
1246 if (softsp->power_regs_mapped)
1247 ddi_regs_map_free(&softsp->power_rhandle);
1249 #endif /* ACPI_POWER_BUTTON */