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]
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.
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>
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>
60 #include <sys/sysevent/eventdefs.h>
61 #include <sys/sysevent/pwrctl.h>
64 #include <sys/machsystm.h>
67 #ifdef ACPI_POWER_BUTTON
69 #include <sys/acpi/acpi.h>
70 #include <sys/acpica.h>
76 * Some #defs that must be here as they differ for power.c
79 #define EPIC_REGS_OFFSET 0x00
80 #define EPIC_REGS_LEN 0x82
84 * This flag, which is set for platforms, that have EPIC processor
85 * to process power button interrupt, helps in executing platform
88 static char hasEPIC
= B_FALSE
;
89 #endif /* ACPI_POWER_BUTTON */
92 * Maximum number of clone minors that is allowed. This value
93 * is defined relatively low to save memory.
95 #define POWER_MAX_CLONE 256
98 * Minor number is instance << 8 + clone minor from range 1-255; clone 0
99 * is reserved for "original" minor.
101 #define POWER_MINOR_TO_CLONE(minor) ((minor) & (POWER_MAX_CLONE - 1))
104 * Power Button Abort Delay
106 #define ABORT_INCREMENT_DELAY 10
109 * FWARC 2005/687: power device compatible property
111 #define POWER_DEVICE_TYPE "power-device-type"
114 * Driver global variables
116 static void *power_state
;
117 static int power_inst
= -1;
119 static hrtime_t power_button_debounce
= MSEC2NSEC(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;
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;
132 * Function prototypes
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
);
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);
150 * Structure used in the driver
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 */
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 */
179 static void power_gen_sysevent(struct power_soft_state
*);
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
);
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 */
191 * Configuration data structures
193 static struct cb_ops power_cb_ops
= {
194 power_open
, /* open */
195 power_close
, /* close */
196 nodev
, /* strategy */
201 power_ioctl
, /* ioctl */
205 power_chpoll
, /* poll */
206 ddi_prop_op
, /* cb_prop_op */
207 NULL
, /* streamtab */
208 D_MP
| D_NEW
, /* Driver compatibility flag */
210 nodev
, /* cb_aread */
211 nodev
/* cb_awrite */
214 static struct dev_ops power_ops
= {
215 DEVO_REV
, /* devo_rev, */
217 power_getinfo
, /* getinfo */
218 nulldev
, /* identify */
220 power_attach
, /* attach */
221 power_detach
, /* detach */
223 &power_cb_ops
, /* cb_ops */
224 (struct bus_ops
*)NULL
, /* bus_ops */
226 ddi_quiesce_not_supported
, /* devo_quiesce */
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 */
235 static struct modlinkage modlinkage
= {
242 * These are the module initialization routines.
250 if ((error
= ddi_soft_state_init(&power_state
,
251 sizeof (struct power_soft_state
), 0)) != 0)
254 if ((error
= mod_install(&modlinkage
)) != 0)
255 ddi_soft_state_fini(&power_state
);
265 if ((error
= mod_remove(&modlinkage
)) == 0)
266 ddi_soft_state_fini(&power_state
);
272 _info(struct modinfo
*modinfop
)
274 return (mod_info(&modlinkage
, modinfop
));
279 power_getinfo(dev_info_t
*dip
, ddi_info_cmd_t infocmd
, void *arg
,
282 struct power_soft_state
*softsp
;
284 if (power_inst
== -1)
285 return (DDI_FAILURE
);
288 case DDI_INFO_DEVT2DEVINFO
:
289 if ((softsp
= ddi_get_soft_state(power_state
, power_inst
))
291 return (DDI_FAILURE
);
292 *result
= (void *)softsp
->dip
;
293 return (DDI_SUCCESS
);
295 case DDI_INFO_DEVT2INSTANCE
:
296 *result
= (void *)(uintptr_t)power_inst
;
297 return (DDI_SUCCESS
);
300 return (DDI_FAILURE
);
305 power_attach(dev_info_t
*dip
, ddi_attach_cmd_t cmd
)
307 struct power_soft_state
*softsp
;
313 return (DDI_SUCCESS
);
315 return (DDI_FAILURE
);
319 * If the power node doesn't have "button" property, quietly
322 if (ddi_prop_exists(DDI_DEV_T_ANY
, dip
, DDI_PROP_DONTPASS
,
324 return (DDI_FAILURE
);
326 if (power_inst
!= -1)
327 return (DDI_FAILURE
);
329 power_inst
= ddi_get_instance(dip
);
331 if (ddi_soft_state_zalloc(power_state
, power_inst
) != DDI_SUCCESS
)
332 return (DDI_FAILURE
);
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
);
338 softsp
= ddi_get_soft_state(power_state
, power_inst
);
341 #ifdef ACPI_POWER_BUTTON
342 (void) power_attach_acpi(softsp
);
344 if (power_setup_regs(softsp
) != DDI_SUCCESS
) {
345 cmn_err(CE_WARN
, "power_attach: failed to setup registers");
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 "
355 mutex_init(&softsp
->power_intr_mutex
, NULL
, MUTEX_DRIVER
,
356 softsp
->high_iblock_cookie
);
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
);
365 #endif /* ACPI_POWER_BUTTON */
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 "
371 mutex_destroy(&softsp
->power_intr_mutex
);
372 ddi_remove_intr(dip
, 0, NULL
);
376 mutex_init(&softsp
->power_mutex
, NULL
, MUTEX_DRIVER
,
377 (void *)softsp
->soft_iblock_cookie
);
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
);
391 return (DDI_SUCCESS
);
394 #ifdef ACPI_POWER_BUTTON
396 * detach ACPI power button
398 power_detach_acpi(softsp
);
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
);
409 * This driver doesn't detach.
412 power_detach(dev_info_t
*dip
, ddi_detach_cmd_t cmd
)
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.
421 return ((cmd
== DDI_SUSPEND
) ? DDI_SUCCESS
:
426 #ifndef ACPI_POWER_BUTTON
428 * Handler for the high-level interrupt.
431 power_high_intr(caddr_t arg
)
433 struct power_soft_state
*softsp
= (struct power_soft_state
*)arg
;
434 ddi_acc_handle_t hdl
= softsp
->power_rhandle
;
438 static hrtime_t o_tstamp
= 0;
439 static hrtime_t power_button_tstamp
= 0;
440 static int power_button_cnt
;
442 if (softsp
->power_regs_mapped
) {
443 mutex_enter(&softsp
->power_intr_mutex
);
445 /* Check if power button interrupt is delivered by EPIC HW */
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
);
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
);
458 if (!softsp
->power_btn_ioctl
) {
459 mutex_exit(&softsp
->power_intr_mutex
);
460 return (DDI_INTR_CLAIMED
);
462 softsp
->power_btn_ioctl
= B_FALSE
;
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
);
471 if (!softsp
->power_btn_ioctl
) {
472 mutex_exit(&softsp
->power_intr_mutex
);
473 return (DDI_INTR_CLAIMED
);
475 softsp
->power_btn_ioctl
= B_FALSE
;
478 mutex_exit(&softsp
->power_intr_mutex
);
481 tstamp
= gethrtime();
483 /* need to deal with power button debounce */
484 if (o_tstamp
&& (tstamp
- o_tstamp
) < power_button_debounce
) {
486 return (DDI_INTR_CLAIMED
);
492 mutex_enter(&softsp
->power_intr_mutex
);
493 power_button_pressed
++;
494 mutex_exit(&softsp
->power_intr_mutex
);
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();
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
);
515 power_button_cnt
= 1;
516 power_button_tstamp
= tstamp
;
520 if (!power_button_enable
)
521 return (DDI_INTR_CLAIMED
);
523 /* post softint to issue timeout for power button action */
524 if (softsp
->softintr_id
!= NULL
)
525 ddi_trigger_softintr(softsp
->softintr_id
);
527 return (DDI_INTR_CLAIMED
);
529 #endif /* ifndef ACPI_POWER_BUTTON */
532 * Handle the softints....
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.
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.
542 power_soft_intr(caddr_t arg
)
544 struct power_soft_state
*softsp
= (struct power_soft_state
*)arg
;
546 if (!power_button_abort_enable
)
547 return (power_issue_shutdown(arg
));
549 mutex_enter(&softsp
->power_intr_mutex
);
550 if (!power_button_pressed
) {
551 mutex_exit(&softsp
->power_intr_mutex
);
552 return (DDI_INTR_CLAIMED
);
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
561 if (power_button_pressed
> 1)
562 additional_presses
+= power_button_pressed
- 1;
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
);
572 return (DDI_INTR_CLAIMED
);
576 * Upon receiving a timeout the following is determined:
578 * If an Abort sequence was issued, then we cancel all outstanding timeouts
579 * and additional presses prior to the Abort sequence.
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.
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
591 power_timeout(caddr_t arg
)
593 struct power_soft_state
*softsp
= (struct power_soft_state
*)arg
;
594 static int first
= 0;
597 * Abort was generated cancel all outstanding power
600 mutex_enter(&softsp
->power_intr_mutex
);
601 if (power_button_cancel
) {
602 power_button_cancel
--;
603 power_button_timeouts
--;
606 additional_presses
= 0;
608 mutex_exit(&softsp
->power_intr_mutex
);
614 * We get here if the timeout(s) have fired and they were
615 * not issued prior to an abort.
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.
622 if (!timeout_cancel
) {
623 timeout_cancel
= power_button_timeouts
+
626 power_button_timeouts
--;
627 if (!power_button_timeouts
)
628 additional_presses
= 0;
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",
637 mutex_exit(&softsp
->power_intr_mutex
);
639 /* Go and do the work to request shutdown */
640 (void) power_issue_shutdown((caddr_t
)softsp
);
644 power_button_timeouts
--;
645 if (!power_button_timeouts
)
646 additional_presses
= 0;
647 mutex_exit(&softsp
->power_intr_mutex
);
650 #ifdef ACPI_POWER_BUTTON
657 * If we're still booting and init(1) isn't set up yet, simply halt.
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 */
668 * else, graceful shutdown with inittab and all getting involved
670 psignal(initpp
, SIGPWR
);
675 power_issue_shutdown(caddr_t arg
)
677 struct power_soft_state
*softsp
= (struct power_soft_state
*)arg
;
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
);
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;
696 * Wait a while for "do_shutdown()" to shut down the system
697 * before logging an error message.
699 (void) timeout((void(*)(void *))power_log_message
, NULL
,
702 mutex_exit(&softsp
->power_mutex
);
704 return (DDI_INTR_CLAIMED
);
708 power_gen_sysevent(struct power_soft_state
*softsp
)
710 nvlist_t
*attr_list
= NULL
;
712 char pathname
[MAXPATHLEN
];
715 /* Allocate and build sysevent attribute list */
716 err
= nvlist_alloc(&attr_list
, NV_UNIQUE_NAME_TYPE
, DDI_NOSLEEP
);
719 "cannot allocate memory for sysevent attributes\n");
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
));
730 err
= nvlist_add_string(attr_list
, PWRCTL_DEV_HID
, hid
);
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
);
739 (void) ddi_pathname(softsp
->dip
, pathname
);
740 err
= nvlist_add_string(attr_list
, PWRCTL_DEV_PHYS_PATH
, pathname
);
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
);
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
) {
754 "cannot log sysevent, err code %x\n", err
);
757 nvlist_free(attr_list
);
765 power_open(dev_t
*devp
, int openflags
, int otyp
, cred_t
*credp
)
767 struct power_soft_state
*softsp
;
770 if (otyp
!= OTYP_CHR
)
773 if ((softsp
= ddi_get_soft_state(power_state
, power_inst
)) ==
777 mutex_enter(&softsp
->power_mutex
);
778 for (clone
= 1; clone
< POWER_MAX_CLONE
; clone
++)
779 if (!softsp
->clones
[clone
])
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
);
789 *devp
= makedevice(getmajor(*devp
), (power_inst
<< 8) + clone
);
790 softsp
->clones
[clone
] = 1;
791 mutex_exit(&softsp
->power_mutex
);
801 power_close(dev_t dev
, int openflags
, int otyp
, cred_t
*credp
)
803 struct power_soft_state
*softsp
;
806 if (otyp
!= OTYP_CHR
)
809 if ((softsp
= ddi_get_soft_state(power_state
, power_inst
)) ==
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
);
825 power_ioctl(dev_t dev
, int cmd
, intptr_t arg
, int mode
, cred_t
*cred_p
,
828 struct power_soft_state
*softsp
;
831 if ((softsp
= ddi_get_soft_state(power_state
, power_inst
)) ==
835 clone
= POWER_MINOR_TO_CLONE(getminor(dev
));
837 case PB_BEGIN_MONITOR
:
838 mutex_enter(&softsp
->power_mutex
);
839 if (softsp
->monitor_on
) {
840 mutex_exit(&softsp
->power_mutex
);
843 softsp
->monitor_on
= clone
;
844 mutex_exit(&softsp
->power_mutex
);
848 mutex_enter(&softsp
->power_mutex
);
851 * If PB_END_MONITOR is called without first
852 * calling PB_BEGIN_MONITOR, an error will be
855 if (!softsp
->monitor_on
) {
856 mutex_exit(&softsp
->power_mutex
);
861 * This clone is not monitoring the button.
863 if (softsp
->monitor_on
!= clone
) {
864 mutex_exit(&softsp
->power_mutex
);
867 softsp
->monitor_on
= 0;
868 mutex_exit(&softsp
->power_mutex
);
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
);
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.
885 mutex_exit(&softsp
->power_mutex
);
889 * This ioctl is used by the test suite.
891 case PB_CREATE_BUTTON_EVENT
:
892 #ifdef ACPI_POWER_BUTTON
893 (UINT32
)power_acpi_fixed_event((void *)softsp
);
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
);
900 (void) power_high_intr((caddr_t
)softsp
);
901 #endif /* ACPI_POWER_BUTTON */
911 power_chpoll(dev_t dev
, short events
, int anyyet
,
912 short *reventsp
, struct pollhead
**phpp
)
914 struct power_soft_state
*softsp
;
916 if ((softsp
= ddi_get_soft_state(power_state
, power_inst
)) == NULL
)
919 mutex_enter(&softsp
->power_mutex
);
922 *reventsp
= POLLRDNORM
|POLLIN
;
925 *phpp
= &softsp
->pollhd
;
927 mutex_exit(&softsp
->power_mutex
);
933 power_log_message(void)
935 struct power_soft_state
*softsp
;
937 if ((softsp
= ddi_get_soft_state(power_state
, power_inst
)) == NULL
) {
938 cmn_err(CE_WARN
, "Failed to get internal state!");
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
);
948 #ifdef ACPI_POWER_BUTTON
954 acpi_device(ACPI_HANDLE obj
, UINT32 nesting
, void *context
, void **rv
)
957 *((ACPI_HANDLE
*)context
) = obj
;
965 probe_acpi_pwrbutton()
967 ACPI_HANDLE obj
= NULL
;
969 (void) AcpiGetDevices("PNP0C0C", acpi_device
, (void *)&obj
, NULL
);
974 power_acpi_fixed_event(void *ctx
)
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
);
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
);
991 power_acpi_notify_event(ACPI_HANDLE obj
, UINT32 val
, void *ctx
)
994 (void) power_acpi_fixed_event(ctx
);
1001 power_probe_method_button(struct power_soft_state
*softsp
)
1003 ACPI_HANDLE button_obj
;
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
))
1018 power_probe_fixed_button(struct power_soft_state
*softsp
)
1020 ACPI_TABLE_FADT
*fadt
;
1022 if (AcpiGetTable(ACPI_SIG_FADT
, 1, (ACPI_TABLE_HEADER
**) &fadt
) !=
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
)
1039 power_attach_acpi(struct power_soft_state
*softsp
)
1043 * If we've attached anything already, return an error
1045 if ((softsp
->gpe_attached
) || (softsp
->fixed_attached
))
1046 return (DDI_FAILURE
);
1049 * attempt to attach both a fixed-event handler and a GPE
1050 * handler; remember what we got
1052 softsp
->fixed_attached
= (power_probe_fixed_button(softsp
) != 0);
1053 softsp
->gpe_attached
= (power_probe_method_button(softsp
) != 0);
1056 * If we've attached anything now, return success
1058 if ((softsp
->gpe_attached
) || (softsp
->fixed_attached
))
1059 return (DDI_SUCCESS
);
1061 return (DDI_FAILURE
);
1068 power_detach_acpi(struct power_soft_state
*softsp
)
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"
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"
1087 * Code for platforms that have EPIC processor for processing power
1088 * button interrupts.
1091 power_setup_epic_regs(dev_info_t
*dip
, struct power_soft_state
*softsp
)
1093 ddi_device_acc_attr_t attr
;
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
*)®_base
,
1100 EPIC_REGS_OFFSET
, EPIC_REGS_LEN
, &attr
,
1101 &softsp
->power_rhandle
) != DDI_SUCCESS
) {
1102 return (DDI_FAILURE
);
1105 softsp
->power_btn_reg
= reg_base
;
1106 softsp
->power_regs_mapped
= B_TRUE
;
1108 /* Clear power button interrupt first */
1109 EPIC_WR(softsp
->power_rhandle
, softsp
->power_btn_reg
,
1110 EPIC_ATOM_INTR_CLEAR
);
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
);
1117 * At this point, EPIC interrupt processing is fully initialised.
1120 return (DDI_SUCCESS
);
1125 * power button register definitions for acpi register on m1535d
1127 #define M1535D_PWR_BTN_REG_01 0x1
1128 #define M1535D_PWR_BTN_EVENT_FLAG 0x1
1131 power_setup_m1535_regs(dev_info_t
*dip
, struct power_soft_state
*softsp
)
1133 ddi_device_acc_attr_t attr
;
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
*)®_base
, 0, 0, &attr
,
1140 &softsp
->power_rhandle
) != DDI_SUCCESS
) {
1141 return (DDI_FAILURE
);
1143 softsp
->power_btn_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
);
1150 * MBC Fire/SSI Interrupt Status Register definitions
1152 #define FIRE_SSI_ISR 0x0
1153 #define FIRE_SSI_INTR_ENA 0x8
1154 #define FIRE_SSI_SHUTDOWN_REQ 0x4
1157 power_setup_mbc_regs(dev_info_t
*dip
, struct power_soft_state
*softsp
)
1159 ddi_device_acc_attr_t attr
;
1161 ddi_acc_handle_t hdl
;
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
*)®_base
, 0, 0, &attr
,
1168 &softsp
->power_rhandle
) != DDI_SUCCESS
) {
1169 return (DDI_FAILURE
);
1171 softsp
->power_btn_reg
= ®_base
[FIRE_SSI_ISR
];
1172 softsp
->power_btn_bit
= FIRE_SSI_SHUTDOWN_REQ
;
1173 hdl
= softsp
->power_rhandle
;
1175 * Clear MBC Fire Power Button interrupt, if set.
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
);
1184 * Enable MBC Fire Power Button interrupt.
1186 reg
= ddi_get8(hdl
, ®_base
[FIRE_SSI_INTR_ENA
]);
1187 reg
|= FIRE_SSI_SHUTDOWN_REQ
;
1188 ddi_put8(hdl
, ®_base
[FIRE_SSI_INTR_ENA
], reg
);
1190 softsp
->power_regs_mapped
= B_TRUE
;
1192 return (DDI_SUCCESS
);
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
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"
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.
1211 power_setup_regs(struct power_soft_state
*softsp
)
1214 char *power_type
= NULL
;
1215 int retval
= DDI_SUCCESS
;
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
);
1228 cmn_err(CE_WARN
, "unexpected power-device-type: %s\n",
1230 retval
= DDI_FAILURE
;
1232 ddi_prop_free(power_type
);
1233 } else if (strcmp(binding_name
, "ali1535d+-power") == 0) {
1234 retval
= power_setup_m1535_regs(softsp
->dip
, softsp
);
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.
1247 power_free_regs(struct power_soft_state
*softsp
)
1249 if (softsp
->power_regs_mapped
)
1250 ddi_regs_map_free(&softsp
->power_rhandle
);
1252 #endif /* ACPI_POWER_BUTTON */