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.
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:
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
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.
46 #include <sys/types.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>
58 #include <sys/sysevent/eventdefs.h>
59 #include <sys/sysevent/pwrctl.h>
62 #include <sys/machsystm.h>
65 #ifdef ACPI_POWER_BUTTON
67 #include <sys/acpi/acpi.h>
68 #include <sys/acpica.h>
74 * Some #defs that must be here as they differ for power.c
77 #define EPIC_REGS_OFFSET 0x00
78 #define EPIC_REGS_LEN 0x82
82 * This flag, which is set for platforms, that have EPIC processor
83 * to process power button interrupt, helps in executing platform
86 static char hasEPIC
= B_FALSE
;
87 #endif /* ACPI_POWER_BUTTON */
90 * Maximum number of clone minors that is allowed. This value
91 * is defined relatively low to save memory.
93 #define POWER_MAX_CLONE 256
96 * Minor number is instance << 8 + clone minor from range 1-255; clone 0
97 * is reserved for "original" minor.
99 #define POWER_MINOR_TO_CLONE(minor) ((minor) & (POWER_MAX_CLONE - 1))
102 * Power Button Abort Delay
104 #define ABORT_INCREMENT_DELAY 10
107 * FWARC 2005/687: power device compatible property
109 #define POWER_DEVICE_TYPE "power-device-type"
112 * Driver global variables
114 static void *power_state
;
115 static int power_inst
= -1;
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;
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;
130 * Function prototypes
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
);
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);
148 * Structure used in the driver
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 */
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 */
177 static void power_gen_sysevent(struct power_soft_state
*);
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
);
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 */
189 * Configuration data structures
191 static struct cb_ops power_cb_ops
= {
192 power_open
, /* open */
193 power_close
, /* close */
194 nodev
, /* strategy */
199 power_ioctl
, /* ioctl */
203 power_chpoll
, /* poll */
204 ddi_prop_op
, /* cb_prop_op */
205 NULL
, /* streamtab */
206 D_MP
| D_NEW
, /* Driver compatibility flag */
208 nodev
, /* cb_aread */
209 nodev
/* cb_awrite */
212 static struct dev_ops power_ops
= {
213 DEVO_REV
, /* devo_rev, */
215 power_getinfo
, /* getinfo */
216 nulldev
, /* identify */
218 power_attach
, /* attach */
219 power_detach
, /* detach */
221 &power_cb_ops
, /* cb_ops */
222 (struct bus_ops
*)NULL
, /* bus_ops */
224 ddi_quiesce_not_supported
, /* devo_quiesce */
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 */
233 static struct modlinkage modlinkage
= {
240 * These are the module initialization routines.
248 if ((error
= ddi_soft_state_init(&power_state
,
249 sizeof (struct power_soft_state
), 0)) != 0)
252 if ((error
= mod_install(&modlinkage
)) != 0)
253 ddi_soft_state_fini(&power_state
);
263 if ((error
= mod_remove(&modlinkage
)) == 0)
264 ddi_soft_state_fini(&power_state
);
270 _info(struct modinfo
*modinfop
)
272 return (mod_info(&modlinkage
, modinfop
));
277 power_getinfo(dev_info_t
*dip
, ddi_info_cmd_t infocmd
, void *arg
,
280 struct power_soft_state
*softsp
;
282 if (power_inst
== -1)
283 return (DDI_FAILURE
);
286 case DDI_INFO_DEVT2DEVINFO
:
287 if ((softsp
= ddi_get_soft_state(power_state
, power_inst
))
289 return (DDI_FAILURE
);
290 *result
= (void *)softsp
->dip
;
291 return (DDI_SUCCESS
);
293 case DDI_INFO_DEVT2INSTANCE
:
294 *result
= (void *)(uintptr_t)power_inst
;
295 return (DDI_SUCCESS
);
298 return (DDI_FAILURE
);
303 power_attach(dev_info_t
*dip
, ddi_attach_cmd_t cmd
)
305 struct power_soft_state
*softsp
;
311 return (DDI_SUCCESS
);
313 return (DDI_FAILURE
);
317 * If the power node doesn't have "button" property, quietly
320 if (ddi_prop_exists(DDI_DEV_T_ANY
, dip
, DDI_PROP_DONTPASS
,
322 return (DDI_FAILURE
);
324 if (power_inst
!= -1)
325 return (DDI_FAILURE
);
327 power_inst
= ddi_get_instance(dip
);
329 if (ddi_soft_state_zalloc(power_state
, power_inst
) != DDI_SUCCESS
)
330 return (DDI_FAILURE
);
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
);
336 softsp
= ddi_get_soft_state(power_state
, power_inst
);
339 #ifdef ACPI_POWER_BUTTON
340 (void) power_attach_acpi(softsp
);
342 if (power_setup_regs(softsp
) != DDI_SUCCESS
) {
343 cmn_err(CE_WARN
, "power_attach: failed to setup registers");
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 "
353 mutex_init(&softsp
->power_intr_mutex
, NULL
, MUTEX_DRIVER
,
354 softsp
->high_iblock_cookie
);
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
);
363 #endif /* ACPI_POWER_BUTTON */
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 "
369 mutex_destroy(&softsp
->power_intr_mutex
);
370 ddi_remove_intr(dip
, 0, NULL
);
374 mutex_init(&softsp
->power_mutex
, NULL
, MUTEX_DRIVER
,
375 (void *)softsp
->soft_iblock_cookie
);
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
);
389 return (DDI_SUCCESS
);
392 #ifdef ACPI_POWER_BUTTON
394 * detach ACPI power button
396 power_detach_acpi(softsp
);
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
);
407 * This driver doesn't detach.
410 power_detach(dev_info_t
*dip
, ddi_detach_cmd_t cmd
)
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.
419 return ((cmd
== DDI_SUSPEND
) ? DDI_SUCCESS
:
424 #ifndef ACPI_POWER_BUTTON
426 * Handler for the high-level interrupt.
429 power_high_intr(caddr_t arg
)
431 struct power_soft_state
*softsp
= (struct power_soft_state
*)arg
;
432 ddi_acc_handle_t hdl
= softsp
->power_rhandle
;
436 static hrtime_t o_tstamp
= 0;
437 static hrtime_t power_button_tstamp
= 0;
438 static int power_button_cnt
;
440 if (softsp
->power_regs_mapped
) {
441 mutex_enter(&softsp
->power_intr_mutex
);
443 /* Check if power button interrupt is delivered by EPIC HW */
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
);
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
);
456 if (!softsp
->power_btn_ioctl
) {
457 mutex_exit(&softsp
->power_intr_mutex
);
458 return (DDI_INTR_CLAIMED
);
460 softsp
->power_btn_ioctl
= B_FALSE
;
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
);
469 if (!softsp
->power_btn_ioctl
) {
470 mutex_exit(&softsp
->power_intr_mutex
);
471 return (DDI_INTR_CLAIMED
);
473 softsp
->power_btn_ioctl
= B_FALSE
;
476 mutex_exit(&softsp
->power_intr_mutex
);
479 tstamp
= gethrtime();
481 /* need to deal with power button debounce */
482 if (o_tstamp
&& (tstamp
- o_tstamp
) < power_button_debounce
) {
484 return (DDI_INTR_CLAIMED
);
490 mutex_enter(&softsp
->power_intr_mutex
);
491 power_button_pressed
++;
492 mutex_exit(&softsp
->power_intr_mutex
);
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();
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
);
513 power_button_cnt
= 1;
514 power_button_tstamp
= tstamp
;
518 if (!power_button_enable
)
519 return (DDI_INTR_CLAIMED
);
521 /* post softint to issue timeout for power button action */
522 if (softsp
->softintr_id
!= NULL
)
523 ddi_trigger_softintr(softsp
->softintr_id
);
525 return (DDI_INTR_CLAIMED
);
527 #endif /* ifndef ACPI_POWER_BUTTON */
530 * Handle the softints....
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.
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.
540 power_soft_intr(caddr_t arg
)
542 struct power_soft_state
*softsp
= (struct power_soft_state
*)arg
;
544 if (!power_button_abort_enable
)
545 return (power_issue_shutdown(arg
));
547 mutex_enter(&softsp
->power_intr_mutex
);
548 if (!power_button_pressed
) {
549 mutex_exit(&softsp
->power_intr_mutex
);
550 return (DDI_INTR_CLAIMED
);
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
559 if (power_button_pressed
> 1)
560 additional_presses
+= power_button_pressed
- 1;
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
);
570 return (DDI_INTR_CLAIMED
);
574 * Upon receiving a timeout the following is determined:
576 * If an Abort sequence was issued, then we cancel all outstanding timeouts
577 * and additional presses prior to the Abort sequence.
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.
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
589 power_timeout(caddr_t arg
)
591 struct power_soft_state
*softsp
= (struct power_soft_state
*)arg
;
592 static int first
= 0;
595 * Abort was generated cancel all outstanding power
598 mutex_enter(&softsp
->power_intr_mutex
);
599 if (power_button_cancel
) {
600 power_button_cancel
--;
601 power_button_timeouts
--;
604 additional_presses
= 0;
606 mutex_exit(&softsp
->power_intr_mutex
);
612 * We get here if the timeout(s) have fired and they were
613 * not issued prior to an abort.
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.
620 if (!timeout_cancel
) {
621 timeout_cancel
= power_button_timeouts
+
624 power_button_timeouts
--;
625 if (!power_button_timeouts
)
626 additional_presses
= 0;
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",
635 mutex_exit(&softsp
->power_intr_mutex
);
637 /* Go and do the work to request shutdown */
638 (void) power_issue_shutdown((caddr_t
)softsp
);
642 power_button_timeouts
--;
643 if (!power_button_timeouts
)
644 additional_presses
= 0;
645 mutex_exit(&softsp
->power_intr_mutex
);
648 #ifdef ACPI_POWER_BUTTON
655 * If we're still booting and init(1) isn't set up yet, simply halt.
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 */
666 * else, graceful shutdown with inittab and all getting involved
668 psignal(initpp
, SIGPWR
);
673 power_issue_shutdown(caddr_t arg
)
675 struct power_soft_state
*softsp
= (struct power_soft_state
*)arg
;
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
);
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;
694 * Wait a while for "do_shutdown()" to shut down the system
695 * before logging an error message.
697 (void) timeout((void(*)(void *))power_log_message
, NULL
,
700 mutex_exit(&softsp
->power_mutex
);
702 return (DDI_INTR_CLAIMED
);
706 power_gen_sysevent(struct power_soft_state
*softsp
)
708 nvlist_t
*attr_list
= NULL
;
710 char pathname
[MAXPATHLEN
];
713 /* Allocate and build sysevent attribute list */
714 err
= nvlist_alloc(&attr_list
, NV_UNIQUE_NAME_TYPE
, DDI_NOSLEEP
);
717 "cannot allocate memory for sysevent attributes\n");
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
));
728 err
= nvlist_add_string(attr_list
, PWRCTL_DEV_HID
, hid
);
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
);
737 (void) ddi_pathname(softsp
->dip
, pathname
);
738 err
= nvlist_add_string(attr_list
, PWRCTL_DEV_PHYS_PATH
, pathname
);
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
);
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
) {
752 "cannot log sysevent, err code %x\n", err
);
755 nvlist_free(attr_list
);
763 power_open(dev_t
*devp
, int openflags
, int otyp
, cred_t
*credp
)
765 struct power_soft_state
*softsp
;
768 if (otyp
!= OTYP_CHR
)
771 if ((softsp
= ddi_get_soft_state(power_state
, power_inst
)) ==
775 mutex_enter(&softsp
->power_mutex
);
776 for (clone
= 1; clone
< POWER_MAX_CLONE
; clone
++)
777 if (!softsp
->clones
[clone
])
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
);
787 *devp
= makedevice(getmajor(*devp
), (power_inst
<< 8) + clone
);
788 softsp
->clones
[clone
] = 1;
789 mutex_exit(&softsp
->power_mutex
);
799 power_close(dev_t dev
, int openflags
, int otyp
, cred_t
*credp
)
801 struct power_soft_state
*softsp
;
804 if (otyp
!= OTYP_CHR
)
807 if ((softsp
= ddi_get_soft_state(power_state
, power_inst
)) ==
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
);
823 power_ioctl(dev_t dev
, int cmd
, intptr_t arg
, int mode
, cred_t
*cred_p
,
826 struct power_soft_state
*softsp
;
829 if ((softsp
= ddi_get_soft_state(power_state
, power_inst
)) ==
833 clone
= POWER_MINOR_TO_CLONE(getminor(dev
));
835 case PB_BEGIN_MONITOR
:
836 mutex_enter(&softsp
->power_mutex
);
837 if (softsp
->monitor_on
) {
838 mutex_exit(&softsp
->power_mutex
);
841 softsp
->monitor_on
= clone
;
842 mutex_exit(&softsp
->power_mutex
);
846 mutex_enter(&softsp
->power_mutex
);
849 * If PB_END_MONITOR is called without first
850 * calling PB_BEGIN_MONITOR, an error will be
853 if (!softsp
->monitor_on
) {
854 mutex_exit(&softsp
->power_mutex
);
859 * This clone is not monitoring the button.
861 if (softsp
->monitor_on
!= clone
) {
862 mutex_exit(&softsp
->power_mutex
);
865 softsp
->monitor_on
= 0;
866 mutex_exit(&softsp
->power_mutex
);
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
);
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.
883 mutex_exit(&softsp
->power_mutex
);
887 * This ioctl is used by the test suite.
889 case PB_CREATE_BUTTON_EVENT
:
890 #ifdef ACPI_POWER_BUTTON
891 (UINT32
)power_acpi_fixed_event((void *)softsp
);
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
);
898 (void) power_high_intr((caddr_t
)softsp
);
899 #endif /* ACPI_POWER_BUTTON */
909 power_chpoll(dev_t dev
, short events
, int anyyet
,
910 short *reventsp
, struct pollhead
**phpp
)
912 struct power_soft_state
*softsp
;
914 if ((softsp
= ddi_get_soft_state(power_state
, power_inst
)) == NULL
)
917 mutex_enter(&softsp
->power_mutex
);
920 *reventsp
= POLLRDNORM
|POLLIN
;
923 *phpp
= &softsp
->pollhd
;
925 mutex_exit(&softsp
->power_mutex
);
931 power_log_message(void)
933 struct power_soft_state
*softsp
;
935 if ((softsp
= ddi_get_soft_state(power_state
, power_inst
)) == NULL
) {
936 cmn_err(CE_WARN
, "Failed to get internal state!");
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
);
946 #ifdef ACPI_POWER_BUTTON
948 * Given a handle to a device object, locate a _PRW object
949 * if present and fetch the GPE info for this device object
952 power_get_prw_gpe(ACPI_HANDLE dev
, ACPI_HANDLE
*gpe_dev
, UINT32
*gpe_num
)
960 * Evaluate _PRW if present
962 status
= AcpiGetHandle(dev
, "_PRW", &prw
);
965 buf
.Length
= ACPI_ALLOCATE_BUFFER
;
966 status
= AcpiEvaluateObjectTyped(prw
, NULL
, NULL
, &buf
,
972 * Sanity-check the package; need at least two elements
975 if (((ACPI_OBJECT
*)buf
.Pointer
)->Package
.Count
< 2)
978 gpe
= &((ACPI_OBJECT
*)buf
.Pointer
)->Package
.Elements
[0];
979 if (gpe
->Type
== ACPI_TYPE_INTEGER
) {
981 *gpe_num
= gpe
->Integer
.Value
;
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
))
988 *gpe_dev
= gpe
->Package
.Elements
[0].Reference
.Handle
;
989 *gpe_num
= gpe
->Package
.Elements
[1].Integer
.Value
;
994 AcpiOsFree(buf
.Pointer
);
1004 acpi_device(ACPI_HANDLE obj
, UINT32 nesting
, void *context
, void **rv
)
1007 *((ACPI_HANDLE
*)context
) = obj
;
1015 probe_acpi_pwrbutton()
1017 ACPI_HANDLE obj
= NULL
;
1019 (void) AcpiGetDevices("PNP0C0C", acpi_device
, (void *)&obj
, NULL
);
1024 power_acpi_fixed_event(void *ctx
)
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
);
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
);
1041 power_acpi_notify_event(ACPI_HANDLE obj
, UINT32 val
, void *ctx
)
1044 (void) power_acpi_fixed_event(ctx
);
1051 power_probe_method_button(struct power_soft_state
*softsp
)
1053 ACPI_HANDLE button_obj
;
1055 ACPI_HANDLE gpe_dev
;
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
) ==
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
))
1074 power_probe_fixed_button(struct power_soft_state
*softsp
)
1076 ACPI_TABLE_FADT
*fadt
;
1078 if (AcpiGetTable(ACPI_SIG_FADT
, 1, (ACPI_TABLE_HEADER
**) &fadt
) !=
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
)
1095 power_attach_acpi(struct power_soft_state
*softsp
)
1099 * If we've attached anything already, return an error
1101 if ((softsp
->gpe_attached
) || (softsp
->fixed_attached
))
1102 return (DDI_FAILURE
);
1105 * attempt to attach both a fixed-event handler and a GPE
1106 * handler; remember what we got
1108 softsp
->fixed_attached
= (power_probe_fixed_button(softsp
) != 0);
1109 softsp
->gpe_attached
= (power_probe_method_button(softsp
) != 0);
1112 * If we've attached anything now, return success
1114 if ((softsp
->gpe_attached
) || (softsp
->fixed_attached
))
1115 return (DDI_SUCCESS
);
1117 return (DDI_FAILURE
);
1124 power_detach_acpi(struct power_soft_state
*softsp
)
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"
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"
1143 * Code for platforms that have EPIC processor for processing power
1144 * button interrupts.
1147 power_setup_epic_regs(dev_info_t
*dip
, struct power_soft_state
*softsp
)
1149 ddi_device_acc_attr_t attr
;
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
);
1161 softsp
->power_btn_reg
= reg_base
;
1162 softsp
->power_regs_mapped
= B_TRUE
;
1164 /* Clear power button interrupt first */
1165 EPIC_WR(softsp
->power_rhandle
, softsp
->power_btn_reg
,
1166 EPIC_ATOM_INTR_CLEAR
);
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
);
1173 * At this point, EPIC interrupt processing is fully initialised.
1176 return (DDI_SUCCESS
);
1181 * power button register definitions for acpi register on m1535d
1183 #define M1535D_PWR_BTN_REG_01 0x1
1184 #define M1535D_PWR_BTN_EVENT_FLAG 0x1
1187 power_setup_m1535_regs(dev_info_t
*dip
, struct power_soft_state
*softsp
)
1189 ddi_device_acc_attr_t attr
;
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
);
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
);
1206 * MBC Fire/SSI Interrupt Status Register definitions
1208 #define FIRE_SSI_ISR 0x0
1209 #define FIRE_SSI_INTR_ENA 0x8
1210 #define FIRE_SSI_SHUTDOWN_REQ 0x4
1213 power_setup_mbc_regs(dev_info_t
*dip
, struct power_soft_state
*softsp
)
1215 ddi_device_acc_attr_t attr
;
1217 ddi_acc_handle_t hdl
;
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
);
1227 softsp
->power_btn_reg
= ®_base
[FIRE_SSI_ISR
];
1228 softsp
->power_btn_bit
= FIRE_SSI_SHUTDOWN_REQ
;
1229 hdl
= softsp
->power_rhandle
;
1231 * Clear MBC Fire Power Button interrupt, if set.
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
);
1240 * Enable MBC Fire Power Button interrupt.
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
);
1246 softsp
->power_regs_mapped
= B_TRUE
;
1248 return (DDI_SUCCESS
);
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
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"
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.
1267 power_setup_regs(struct power_soft_state
*softsp
)
1270 char *power_type
= NULL
;
1271 int retval
= DDI_SUCCESS
;
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
);
1284 cmn_err(CE_WARN
, "unexpected power-device-type: %s\n",
1286 retval
= DDI_FAILURE
;
1288 ddi_prop_free(power_type
);
1289 } else if (strcmp(binding_name
, "ali1535d+-power") == 0) {
1290 retval
= power_setup_m1535_regs(softsp
->dip
, softsp
);
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.
1303 power_free_regs(struct power_soft_state
*softsp
)
1305 if (softsp
->power_regs_mapped
)
1306 ddi_regs_map_free(&softsp
->power_rhandle
);
1308 #endif /* ACPI_POWER_BUTTON */