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