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