1*920Sjbeloro 2611Smyers /* 3611Smyers * CDDL HEADER START 4611Smyers * 5611Smyers * The contents of this file are subject to the terms of the 6611Smyers * Common Development and Distribution License, Version 1.0 only 7611Smyers * (the "License"). You may not use this file except in compliance 8611Smyers * with the License. 9611Smyers * 10611Smyers * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 11611Smyers * or http://www.opensolaris.org/os/licensing. 12611Smyers * See the License for the specific language governing permissions 13611Smyers * and limitations under the License. 14611Smyers * 15611Smyers * When distributing Covered Code, include this CDDL HEADER in each 16611Smyers * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 17611Smyers * If applicable, add the following below this CDDL HEADER, with the 18611Smyers * fields enclosed by brackets "[]" replaced with your own identifying 19611Smyers * information: Portions Copyright [yyyy] [name of copyright owner] 20611Smyers * 21611Smyers * CDDL HEADER END 22611Smyers */ 23611Smyers /* 24611Smyers * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 25611Smyers * Use is subject to license terms. 26611Smyers */ 27611Smyers 28611Smyers #pragma ident "%Z%%M% %I% %E% SMI" 29611Smyers 30611Smyers /* 31611Smyers * Power Button Driver 32611Smyers * 33611Smyers * This driver handles interrupt generated by the power button on 34611Smyers * platforms with "power" device node which has "button" property. 35611Smyers * Currently, these platforms are: 36611Smyers * 37611Smyers * ACPI-enabled x86/x64 platforms 38611Smyers * Ultra-5_10, Ultra-80, Sun-Blade-100, Sun-Blade-150, 39611Smyers * Sun-Blade-1500, Sun-Blade-2500, 40611Smyers * Sun-Fire-V210, Sun-Fire-V240, Netra-240 41611Smyers * 42611Smyers * Only one instance is allowed to attach. In order to know when 43611Smyers * an application that has opened the device is going away, a new 44611Smyers * minor clone is created for each open(9E) request. There are 45611Smyers * allocations for creating minor clones between 1 and 255. The ioctl 46611Smyers * interface is defined by pbio(7I) and approved as part of 47611Smyers * PSARC/1999/393 case. 48611Smyers */ 49611Smyers 50611Smyers #include <sys/types.h> 51611Smyers #include <sys/conf.h> 52611Smyers #include <sys/ddi.h> 53611Smyers #include <sys/sunddi.h> 54611Smyers #include <sys/ddi_impldefs.h> 55611Smyers #include <sys/cmn_err.h> 56611Smyers #include <sys/errno.h> 57611Smyers #include <sys/modctl.h> 58611Smyers #include <sys/machsystm.h> 59611Smyers #include <sys/open.h> 60611Smyers #include <sys/stat.h> 61611Smyers #include <sys/poll.h> 62611Smyers #include <sys/pbio.h> 63*920Sjbeloro 64611Smyers #ifdef ACPI_POWER_BUTTON 65*920Sjbeloro 66611Smyers #include <sys/acpi/acpi.h> 67611Smyers #include <sys/acpica.h> 68*920Sjbeloro 69*920Sjbeloro #else 70*920Sjbeloro 71*920Sjbeloro #include <sys/epic.h> 72*920Sjbeloro /* 73*920Sjbeloro * Some #defs that must be here as they differ for power.c 74*920Sjbeloro * and epic.c 75*920Sjbeloro */ 76*920Sjbeloro #define EPIC_REGS_OFFSET 0x00 77*920Sjbeloro #define EPIC_REGS_LEN 0x82 78*920Sjbeloro 79*920Sjbeloro 80*920Sjbeloro /* 81*920Sjbeloro * This flag, which is set for platforms, that have EPIC processor 82*920Sjbeloro * to process power button interrupt, helps in executing platform 83*920Sjbeloro * specific code. 84*920Sjbeloro */ 85*920Sjbeloro static char hasEPIC = B_FALSE; 86611Smyers #endif /* ACPI_POWER_BUTTON */ 87611Smyers 88611Smyers /* 89611Smyers * Maximum number of clone minors that is allowed. This value 90611Smyers * is defined relatively low to save memory. 91611Smyers */ 92611Smyers #define POWER_MAX_CLONE 256 93611Smyers 94611Smyers /* 95611Smyers * Minor number is instance << 8 + clone minor from range 1-255; clone 0 96611Smyers * is reserved for "original" minor. 97611Smyers */ 98611Smyers #define POWER_MINOR_TO_CLONE(minor) ((minor) & (POWER_MAX_CLONE - 1)) 99611Smyers 100611Smyers /* 101611Smyers * Power Button Abort Delay 102611Smyers */ 103611Smyers #define ABORT_INCREMENT_DELAY 10 104611Smyers 105611Smyers /* 106611Smyers * Driver global variables 107611Smyers */ 108611Smyers static void *power_state; 109611Smyers static int power_inst = -1; 110611Smyers 111611Smyers static hrtime_t power_button_debounce = NANOSEC/MILLISEC*10; 112611Smyers static hrtime_t power_button_abort_interval = 1.5 * NANOSEC; 113611Smyers static int power_button_abort_presses = 3; 114611Smyers static int power_button_abort_enable = 1; 115611Smyers static int power_button_enable = 1; 116611Smyers 117611Smyers static int power_button_pressed = 0; 118611Smyers static int power_button_cancel = 0; 119611Smyers static int power_button_timeouts = 0; 120611Smyers static int timeout_cancel = 0; 121611Smyers static int additional_presses = 0; 122611Smyers 123611Smyers /* 124611Smyers * Function prototypes 125611Smyers */ 126611Smyers static int power_attach(dev_info_t *, ddi_attach_cmd_t); 127611Smyers static int power_detach(dev_info_t *, ddi_detach_cmd_t); 128611Smyers static int power_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **); 129611Smyers static int power_open(dev_t *, int, int, cred_t *); 130611Smyers static int power_close(dev_t, int, int, cred_t *); 131611Smyers static int power_ioctl(dev_t, int, intptr_t, int, cred_t *, int *); 132611Smyers static int power_chpoll(dev_t, short, int, short *, struct pollhead **); 133611Smyers #ifndef ACPI_POWER_BUTTON 134611Smyers static uint_t power_high_intr(caddr_t); 135611Smyers #endif 136611Smyers static uint_t power_soft_intr(caddr_t); 137611Smyers static uint_t power_issue_shutdown(caddr_t); 138611Smyers static void power_timeout(caddr_t); 139611Smyers static void power_log_message(void); 140611Smyers 141611Smyers /* 142611Smyers * Structure used in the driver 143611Smyers */ 144611Smyers struct power_soft_state { 145611Smyers dev_info_t *dip; /* device info pointer */ 146611Smyers kmutex_t power_mutex; /* mutex lock */ 147611Smyers kmutex_t power_intr_mutex; /* interrupt mutex lock */ 148611Smyers ddi_iblock_cookie_t soft_iblock_cookie; /* holds interrupt cookie */ 149611Smyers ddi_iblock_cookie_t high_iblock_cookie; /* holds interrupt cookie */ 150611Smyers ddi_softintr_t softintr_id; /* soft interrupt id */ 151611Smyers uchar_t clones[POWER_MAX_CLONE]; /* array of minor clones */ 152611Smyers int monitor_on; /* clone monitoring the button event */ 153611Smyers /* clone 0 indicates no one is */ 154611Smyers /* monitoring the button event */ 155611Smyers pollhead_t pollhd; /* poll head struct */ 156611Smyers int events; /* bit map of occured events */ 157611Smyers int shutdown_pending; /* system shutdown in progress */ 158611Smyers #ifdef ACPI_POWER_BUTTON 159611Smyers boolean_t fixed_attached; /* true means fixed is attached */ 160611Smyers boolean_t gpe_attached; /* true means GPE is attached */ 161611Smyers ACPI_HANDLE button_obj; /* handle to device power button */ 162611Smyers #else 163611Smyers ddi_acc_handle_t power_rhandle; /* power button register handle */ 164611Smyers uint8_t *power_btn_reg; /* power button register address */ 165611Smyers uint8_t power_btn_bit; /* power button register bit */ 166611Smyers boolean_t power_regs_mapped; /* flag to tell if regs mapped */ 167611Smyers boolean_t power_btn_ioctl; /* flag to specify ioctl request */ 168611Smyers #endif 169611Smyers }; 170611Smyers 171611Smyers #ifdef ACPI_POWER_BUTTON 172611Smyers static int power_attach_acpi(struct power_soft_state *softsp); 173611Smyers static void power_detach_acpi(struct power_soft_state *softsp); 174611Smyers static UINT32 power_acpi_fixed_event(void *ctx); 175611Smyers #else 176611Smyers static int power_setup_regs(struct power_soft_state *softsp); 177611Smyers static void power_free_regs(struct power_soft_state *softsp); 178611Smyers #endif /* ACPI_POWER_BUTTON */ 179611Smyers 180611Smyers /* 181611Smyers * Configuration data structures 182611Smyers */ 183611Smyers static struct cb_ops power_cb_ops = { 184611Smyers power_open, /* open */ 185611Smyers power_close, /* close */ 186611Smyers nodev, /* strategy */ 187611Smyers nodev, /* print */ 188611Smyers nodev, /* dump */ 189611Smyers nodev, /* read */ 190611Smyers nodev, /* write */ 191611Smyers power_ioctl, /* ioctl */ 192611Smyers nodev, /* devmap */ 193611Smyers nodev, /* mmap */ 194611Smyers nodev, /* segmap */ 195611Smyers power_chpoll, /* poll */ 196611Smyers ddi_prop_op, /* cb_prop_op */ 197611Smyers NULL, /* streamtab */ 198611Smyers D_MP | D_NEW, /* Driver compatibility flag */ 199611Smyers CB_REV, /* rev */ 200611Smyers nodev, /* cb_aread */ 201611Smyers nodev /* cb_awrite */ 202611Smyers }; 203611Smyers 204611Smyers static struct dev_ops power_ops = { 205611Smyers DEVO_REV, /* devo_rev, */ 206611Smyers 0, /* refcnt */ 207611Smyers power_getinfo, /* getinfo */ 208611Smyers nulldev, /* identify */ 209611Smyers nulldev, /* probe */ 210611Smyers power_attach, /* attach */ 211611Smyers power_detach, /* detach */ 212611Smyers nodev, /* reset */ 213611Smyers &power_cb_ops, /* cb_ops */ 214611Smyers (struct bus_ops *)NULL, /* bus_ops */ 215611Smyers NULL /* power */ 216611Smyers }; 217611Smyers 218611Smyers static struct modldrv modldrv = { 219611Smyers &mod_driverops, /* Type of module. This one is a driver */ 220611Smyers "power button driver v%I%", /* name of module */ 221611Smyers &power_ops, /* driver ops */ 222611Smyers }; 223611Smyers 224611Smyers static struct modlinkage modlinkage = { 225611Smyers MODREV_1, 226611Smyers (void *)&modldrv, 227611Smyers NULL 228611Smyers }; 229611Smyers 230611Smyers /* 231611Smyers * These are the module initialization routines. 232611Smyers */ 233611Smyers 234611Smyers int 235611Smyers _init(void) 236611Smyers { 237611Smyers int error; 238611Smyers 239611Smyers if ((error = ddi_soft_state_init(&power_state, 240611Smyers sizeof (struct power_soft_state), 0)) != 0) 241611Smyers return (error); 242611Smyers 243611Smyers if ((error = mod_install(&modlinkage)) != 0) 244611Smyers ddi_soft_state_fini(&power_state); 245611Smyers 246611Smyers return (error); 247611Smyers } 248611Smyers 249611Smyers int 250611Smyers _fini(void) 251611Smyers { 252611Smyers int error; 253611Smyers 254611Smyers if ((error = mod_remove(&modlinkage)) == 0) 255611Smyers ddi_soft_state_fini(&power_state); 256611Smyers 257611Smyers return (error); 258611Smyers } 259611Smyers 260611Smyers int 261611Smyers _info(struct modinfo *modinfop) 262611Smyers { 263611Smyers return (mod_info(&modlinkage, modinfop)); 264611Smyers } 265611Smyers 266611Smyers /*ARGSUSED*/ 267611Smyers static int 268611Smyers power_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, 269611Smyers void **result) 270611Smyers { 271611Smyers struct power_soft_state *softsp; 272611Smyers 273611Smyers if (power_inst == -1) 274611Smyers return (DDI_FAILURE); 275611Smyers 276611Smyers switch (infocmd) { 277611Smyers case DDI_INFO_DEVT2DEVINFO: 278611Smyers if ((softsp = ddi_get_soft_state(power_state, power_inst)) 279611Smyers == NULL) 280611Smyers return (DDI_FAILURE); 281611Smyers *result = (void *)softsp->dip; 282611Smyers return (DDI_SUCCESS); 283611Smyers 284611Smyers case DDI_INFO_DEVT2INSTANCE: 285611Smyers *result = (void *)(uintptr_t)power_inst; 286611Smyers return (DDI_SUCCESS); 287611Smyers 288611Smyers default: 289611Smyers return (DDI_FAILURE); 290611Smyers } 291611Smyers } 292611Smyers 293611Smyers static int 294611Smyers power_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 295611Smyers { 296611Smyers struct power_soft_state *softsp; 297611Smyers 298611Smyers switch (cmd) { 299611Smyers case DDI_ATTACH: 300611Smyers break; 301611Smyers case DDI_RESUME: 302611Smyers return (DDI_SUCCESS); 303611Smyers default: 304611Smyers return (DDI_FAILURE); 305611Smyers } 306611Smyers 307611Smyers /* 308611Smyers * If the power node doesn't have "button" property, quietly 309611Smyers * fail to attach. 310611Smyers */ 311611Smyers if (ddi_prop_exists(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 312611Smyers "button") == 0) 313611Smyers return (DDI_FAILURE); 314611Smyers 315611Smyers if (power_inst != -1) 316611Smyers return (DDI_FAILURE); 317611Smyers 318611Smyers power_inst = ddi_get_instance(dip); 319611Smyers 320611Smyers if (ddi_soft_state_zalloc(power_state, power_inst) != DDI_SUCCESS) 321611Smyers return (DDI_FAILURE); 322611Smyers 323611Smyers if (ddi_create_minor_node(dip, "power_button", S_IFCHR, 324611Smyers (power_inst << 8) + 0, "ddi_power_button", 0) != DDI_SUCCESS) 325611Smyers return (DDI_FAILURE); 326611Smyers 327611Smyers softsp = ddi_get_soft_state(power_state, power_inst); 328611Smyers softsp->dip = dip; 329611Smyers 330611Smyers #ifdef ACPI_POWER_BUTTON 331622Smyers (void) power_attach_acpi(softsp); 332611Smyers #else 333611Smyers if (power_setup_regs(softsp) != DDI_SUCCESS) { 334611Smyers cmn_err(CE_WARN, "power_attach: failed to setup registers"); 335611Smyers goto error; 336611Smyers } 337611Smyers 338611Smyers if (ddi_get_iblock_cookie(dip, 0, 339611Smyers &softsp->high_iblock_cookie) != DDI_SUCCESS) { 340611Smyers cmn_err(CE_WARN, "power_attach: ddi_get_soft_iblock_cookie " 341611Smyers "failed."); 342611Smyers goto error; 343611Smyers } 344611Smyers mutex_init(&softsp->power_intr_mutex, NULL, MUTEX_DRIVER, 345611Smyers softsp->high_iblock_cookie); 346611Smyers 347611Smyers if (ddi_add_intr(dip, 0, &softsp->high_iblock_cookie, NULL, 348611Smyers power_high_intr, (caddr_t)softsp) != DDI_SUCCESS) { 349611Smyers cmn_err(CE_WARN, "power_attach: failed to add high-level " 350611Smyers " interrupt handler."); 351611Smyers mutex_destroy(&softsp->power_intr_mutex); 352611Smyers goto error; 353611Smyers } 354611Smyers #endif /* ACPI_POWER_BUTTON */ 355611Smyers 356611Smyers if (ddi_get_soft_iblock_cookie(dip, DDI_SOFTINT_LOW, 357611Smyers &softsp->soft_iblock_cookie) != DDI_SUCCESS) { 358611Smyers cmn_err(CE_WARN, "power_attach: ddi_get_soft_iblock_cookie " 359611Smyers "failed."); 360611Smyers mutex_destroy(&softsp->power_intr_mutex); 361611Smyers ddi_remove_intr(dip, 0, NULL); 362611Smyers goto error; 363611Smyers } 364611Smyers 365611Smyers mutex_init(&softsp->power_mutex, NULL, MUTEX_DRIVER, 366611Smyers (void *)softsp->soft_iblock_cookie); 367611Smyers 368611Smyers if (ddi_add_softintr(dip, DDI_SOFTINT_LOW, &softsp->softintr_id, 369611Smyers NULL, NULL, power_soft_intr, (caddr_t)softsp) != DDI_SUCCESS) { 370611Smyers cmn_err(CE_WARN, "power_attach: failed to add soft " 371611Smyers "interrupt handler."); 372611Smyers mutex_destroy(&softsp->power_mutex); 373611Smyers mutex_destroy(&softsp->power_intr_mutex); 374611Smyers ddi_remove_intr(dip, 0, NULL); 375611Smyers goto error; 376611Smyers } 377611Smyers 378611Smyers ddi_report_dev(dip); 379611Smyers 380611Smyers return (DDI_SUCCESS); 381611Smyers 382611Smyers error: 383611Smyers #ifdef ACPI_POWER_BUTTON 384611Smyers /* 385611Smyers * detach ACPI power button 386611Smyers */ 387611Smyers power_detach_acpi(softsp); 388611Smyers #else 389611Smyers power_free_regs(softsp); 390611Smyers #endif /* ACPI_POWER_BUTTON */ 391611Smyers ddi_remove_minor_node(dip, "power_button"); 392611Smyers ddi_soft_state_free(power_state, power_inst); 393611Smyers return (DDI_FAILURE); 394611Smyers } 395611Smyers 396611Smyers /*ARGSUSED*/ 397611Smyers /* 398611Smyers * This driver doesn't detach. 399611Smyers */ 400611Smyers static int 401611Smyers power_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 402611Smyers { 403611Smyers /* 404611Smyers * Since the "power" node has "reg" property, as part of 405611Smyers * the suspend operation, detach(9E) entry point is called. 406611Smyers * There is no state to save, since this register is used 407611Smyers * by OBP to power off the system and the state of the 408611Smyers * power off is preserved by hardware. 409611Smyers */ 410611Smyers return ((cmd == DDI_SUSPEND) ? DDI_SUCCESS : 411611Smyers DDI_FAILURE); 412611Smyers } 413611Smyers 414*920Sjbeloro 415611Smyers #ifndef ACPI_POWER_BUTTON 416611Smyers /* 417611Smyers * Handler for the high-level interrupt. 418611Smyers */ 419611Smyers static uint_t 420611Smyers power_high_intr(caddr_t arg) 421611Smyers { 422611Smyers struct power_soft_state *softsp = (struct power_soft_state *)arg; 423611Smyers ddi_acc_handle_t hdl = softsp->power_rhandle; 424611Smyers uint8_t reg; 425611Smyers 426611Smyers hrtime_t tstamp; 427611Smyers static hrtime_t o_tstamp = 0; 428611Smyers static hrtime_t power_button_tstamp = 0; 429611Smyers static int power_button_cnt; 430611Smyers 431611Smyers if (softsp->power_regs_mapped) { 432611Smyers mutex_enter(&softsp->power_intr_mutex); 433*920Sjbeloro 434*920Sjbeloro /* Check if power button interrupt is delivered by EPIC HW */ 435*920Sjbeloro if (hasEPIC) { 436*920Sjbeloro /* read isr - first issue command */ 437*920Sjbeloro EPIC_WR(hdl, softsp->power_btn_reg, 438*920Sjbeloro EPIC_ATOM_INTR_READ); 439*920Sjbeloro /* next, read the reg */ 440*920Sjbeloro EPIC_RD(hdl, softsp->power_btn_reg, reg); 441*920Sjbeloro 442*920Sjbeloro if (reg & EPIC_FIRE_INTERRUPT) { /* PB pressed */ 443*920Sjbeloro /* clear the interrupt */ 444*920Sjbeloro EPIC_WR(hdl, softsp->power_btn_reg, 445*920Sjbeloro EPIC_ATOM_INTR_CLEAR); 446*920Sjbeloro } else { 447*920Sjbeloro if (!softsp->power_btn_ioctl) { 448*920Sjbeloro mutex_exit(&softsp->power_intr_mutex); 449*920Sjbeloro return (DDI_INTR_CLAIMED); 450*920Sjbeloro } 451*920Sjbeloro softsp->power_btn_ioctl = B_FALSE; 452*920Sjbeloro } 453611Smyers } else { 454*920Sjbeloro reg = ddi_get8(hdl, softsp->power_btn_reg); 455*920Sjbeloro if (reg & softsp->power_btn_bit) { 456*920Sjbeloro reg &= softsp->power_btn_bit; 457*920Sjbeloro ddi_put8(hdl, softsp->power_btn_reg, reg); 458*920Sjbeloro (void) ddi_get8(hdl, softsp->power_btn_reg); 459*920Sjbeloro } else { 460*920Sjbeloro if (!softsp->power_btn_ioctl) { 461*920Sjbeloro mutex_exit(&softsp->power_intr_mutex); 462*920Sjbeloro return (DDI_INTR_CLAIMED); 463*920Sjbeloro } 464*920Sjbeloro softsp->power_btn_ioctl = B_FALSE; 465611Smyers } 466611Smyers } 467611Smyers mutex_exit(&softsp->power_intr_mutex); 468611Smyers } 469611Smyers 470611Smyers tstamp = gethrtime(); 471611Smyers 472611Smyers /* need to deal with power button debounce */ 473611Smyers if (o_tstamp && (tstamp - o_tstamp) < power_button_debounce) { 474611Smyers o_tstamp = tstamp; 475611Smyers return (DDI_INTR_CLAIMED); 476611Smyers } 477611Smyers o_tstamp = tstamp; 478611Smyers 479611Smyers power_button_cnt++; 480611Smyers 481611Smyers mutex_enter(&softsp->power_intr_mutex); 482611Smyers power_button_pressed++; 483611Smyers mutex_exit(&softsp->power_intr_mutex); 484611Smyers 485611Smyers /* 486611Smyers * If power button abort is enabled and power button was pressed 487611Smyers * power_button_abort_presses times within power_button_abort_interval 488611Smyers * then call abort_sequence_enter(); 489611Smyers */ 490611Smyers if (power_button_abort_enable) { 491611Smyers if (power_button_abort_presses == 1 || 492611Smyers tstamp < (power_button_tstamp + 493611Smyers power_button_abort_interval)) { 494611Smyers if (power_button_cnt == power_button_abort_presses) { 495611Smyers mutex_enter(&softsp->power_intr_mutex); 496611Smyers power_button_cancel += power_button_timeouts; 497611Smyers power_button_pressed = 0; 498611Smyers mutex_exit(&softsp->power_intr_mutex); 499611Smyers power_button_cnt = 0; 500611Smyers abort_sequence_enter("Power Button Abort"); 501611Smyers return (DDI_INTR_CLAIMED); 502611Smyers } 503611Smyers } else { 504611Smyers power_button_cnt = 1; 505611Smyers power_button_tstamp = tstamp; 506611Smyers } 507611Smyers } 508611Smyers 509611Smyers if (!power_button_enable) 510611Smyers return (DDI_INTR_CLAIMED); 511611Smyers 512611Smyers /* post softint to issue timeout for power button action */ 513611Smyers if (softsp->softintr_id != NULL) 514611Smyers ddi_trigger_softintr(softsp->softintr_id); 515611Smyers 516611Smyers return (DDI_INTR_CLAIMED); 517611Smyers } 518611Smyers #endif /* ifndef ACPI_POWER_BUTTON */ 519611Smyers 520611Smyers /* 521611Smyers * Handle the softints.... 522611Smyers * 523611Smyers * If only one softint is posted for several button presses, record 524611Smyers * the number of additional presses just incase this was actually not quite 525611Smyers * an Abort sequence so that we can log this event later. 526611Smyers * 527611Smyers * Issue a timeout with a duration being a fraction larger than 528611Smyers * the specified Abort interval inorder to perform a power down if required. 529611Smyers */ 530611Smyers static uint_t 531611Smyers power_soft_intr(caddr_t arg) 532611Smyers { 533611Smyers struct power_soft_state *softsp = (struct power_soft_state *)arg; 534611Smyers 535611Smyers if (!power_button_abort_enable) 536611Smyers return (power_issue_shutdown(arg)); 537611Smyers 538611Smyers mutex_enter(&softsp->power_intr_mutex); 539611Smyers if (!power_button_pressed) { 540611Smyers mutex_exit(&softsp->power_intr_mutex); 541611Smyers return (DDI_INTR_CLAIMED); 542611Smyers } 543611Smyers 544611Smyers /* 545611Smyers * Schedule a timeout to do the necessary 546611Smyers * work for shutdown, only one timeout for 547611Smyers * n presses if power button was pressed 548611Smyers * more than once before softint fired 549611Smyers */ 550611Smyers if (power_button_pressed > 1) 551611Smyers additional_presses += power_button_pressed - 1; 552611Smyers 553611Smyers timeout_cancel = 0; 554611Smyers power_button_pressed = 0; 555611Smyers power_button_timeouts++; 556611Smyers mutex_exit(&softsp->power_intr_mutex); 557611Smyers (void) timeout((void(*)(void *))power_timeout, 558611Smyers softsp, NSEC_TO_TICK(power_button_abort_interval) + 559611Smyers ABORT_INCREMENT_DELAY); 560611Smyers 561611Smyers return (DDI_INTR_CLAIMED); 562611Smyers } 563611Smyers 564611Smyers /* 565611Smyers * Upon receiving a timeout the following is determined: 566611Smyers * 567611Smyers * If an Abort sequence was issued, then we cancel all outstanding timeouts 568611Smyers * and additional presses prior to the Abort sequence. 569611Smyers * 570611Smyers * If we had multiple timeouts issued and the abort sequence was not met, 571611Smyers * then we had more than one button press to power down the machine. We 572611Smyers * were probably trying to issue an abort. So log a message indicating this 573611Smyers * and cancel all outstanding timeouts. 574611Smyers * 575611Smyers * If we had just one timeout and the abort sequence was not met then 576611Smyers * we really did want to power down the machine, so call power_issue_shutdown() 577611Smyers * to do the work and schedule a power down 578611Smyers */ 579611Smyers static void 580611Smyers power_timeout(caddr_t arg) 581611Smyers { 582611Smyers struct power_soft_state *softsp = (struct power_soft_state *)arg; 583611Smyers static int first = 0; 584611Smyers 585611Smyers /* 586611Smyers * Abort was generated cancel all outstanding power 587611Smyers * button timeouts 588611Smyers */ 589611Smyers mutex_enter(&softsp->power_intr_mutex); 590611Smyers if (power_button_cancel) { 591611Smyers power_button_cancel--; 592611Smyers power_button_timeouts--; 593611Smyers if (!first) { 594611Smyers first++; 595611Smyers additional_presses = 0; 596611Smyers } 597611Smyers mutex_exit(&softsp->power_intr_mutex); 598611Smyers return; 599611Smyers } 600611Smyers first = 0; 601611Smyers 602611Smyers /* 603611Smyers * We get here if the timeout(s) have fired and they were 604611Smyers * not issued prior to an abort. 605611Smyers * 606611Smyers * If we had more than one press in the interval we were 607611Smyers * probably trying to issue an abort, but didnt press the 608611Smyers * required number within the interval. Hence cancel all 609611Smyers * timeouts and do not continue towards shutdown. 610611Smyers */ 611611Smyers if (!timeout_cancel) { 612611Smyers timeout_cancel = power_button_timeouts + 613611Smyers additional_presses; 614611Smyers 615611Smyers power_button_timeouts--; 616611Smyers if (!power_button_timeouts) 617611Smyers additional_presses = 0; 618611Smyers 619611Smyers if (timeout_cancel > 1) { 620611Smyers mutex_exit(&softsp->power_intr_mutex); 621611Smyers cmn_err(CE_NOTE, "Power Button pressed " 622611Smyers "%d times, cancelling all requests", 623611Smyers timeout_cancel); 624611Smyers return; 625611Smyers } 626611Smyers mutex_exit(&softsp->power_intr_mutex); 627611Smyers 628611Smyers /* Go and do the work to request shutdown */ 629611Smyers (void) power_issue_shutdown((caddr_t)softsp); 630611Smyers return; 631611Smyers } 632611Smyers 633611Smyers power_button_timeouts--; 634611Smyers if (!power_button_timeouts) 635611Smyers additional_presses = 0; 636611Smyers mutex_exit(&softsp->power_intr_mutex); 637611Smyers } 638611Smyers 639611Smyers #ifdef ACPI_POWER_BUTTON 640611Smyers static void 641611Smyers do_shutdown(void) 642611Smyers { 643611Smyers proc_t *initpp; 644611Smyers 645611Smyers /* 646611Smyers * If we're still booting and init(1) isn't set up yet, simply halt. 647611Smyers */ 648611Smyers mutex_enter(&pidlock); 649611Smyers initpp = prfind(P_INITPID); 650611Smyers mutex_exit(&pidlock); 651611Smyers if (initpp == NULL) { 652611Smyers extern void halt(char *); 653611Smyers halt("Power off the System"); /* just in case */ 654611Smyers } 655611Smyers 656611Smyers /* 657611Smyers * else, graceful shutdown with inittab and all getting involved 658611Smyers */ 659611Smyers psignal(initpp, SIGPWR); 660611Smyers } 661611Smyers #endif 662611Smyers 663611Smyers static uint_t 664611Smyers power_issue_shutdown(caddr_t arg) 665611Smyers { 666611Smyers struct power_soft_state *softsp = (struct power_soft_state *)arg; 667611Smyers 668611Smyers mutex_enter(&softsp->power_mutex); 669611Smyers softsp->events |= PB_BUTTON_PRESS; 670611Smyers if (softsp->monitor_on != 0) { 671611Smyers mutex_exit(&softsp->power_mutex); 672611Smyers pollwakeup(&softsp->pollhd, POLLRDNORM); 673611Smyers pollwakeup(&softsp->pollhd, POLLIN); 674611Smyers return (DDI_INTR_CLAIMED); 675611Smyers } 676611Smyers 677611Smyers if (!softsp->shutdown_pending) { 678611Smyers cmn_err(CE_WARN, "Power off requested from power button or " 679611Smyers "SC, powering down the system!"); 680611Smyers softsp->shutdown_pending = 1; 681611Smyers do_shutdown(); 682611Smyers 683611Smyers /* 684611Smyers * Wait a while for "do_shutdown()" to shut down the system 685611Smyers * before logging an error message. 686611Smyers */ 687611Smyers (void) timeout((void(*)(void *))power_log_message, NULL, 688611Smyers 100 * hz); 689611Smyers } 690611Smyers mutex_exit(&softsp->power_mutex); 691611Smyers 692611Smyers return (DDI_INTR_CLAIMED); 693611Smyers } 694611Smyers 695611Smyers /* 696611Smyers * Open the device. 697611Smyers */ 698611Smyers /*ARGSUSED*/ 699611Smyers static int 700611Smyers power_open(dev_t *devp, int openflags, int otyp, cred_t *credp) 701611Smyers { 702611Smyers struct power_soft_state *softsp; 703611Smyers int clone; 704611Smyers 705611Smyers if (otyp != OTYP_CHR) 706611Smyers return (EINVAL); 707611Smyers 708611Smyers if ((softsp = ddi_get_soft_state(power_state, power_inst)) == 709611Smyers NULL) 710611Smyers return (ENXIO); 711611Smyers 712611Smyers mutex_enter(&softsp->power_mutex); 713611Smyers for (clone = 1; clone < POWER_MAX_CLONE; clone++) 714611Smyers if (!softsp->clones[clone]) 715611Smyers break; 716611Smyers 717611Smyers if (clone == POWER_MAX_CLONE) { 718611Smyers cmn_err(CE_WARN, "power_open: No more allocation left " 719611Smyers "to create a clone minor."); 720611Smyers mutex_exit(&softsp->power_mutex); 721611Smyers return (ENXIO); 722611Smyers } 723611Smyers 724611Smyers *devp = makedevice(getmajor(*devp), (power_inst << 8) + clone); 725611Smyers softsp->clones[clone] = 1; 726611Smyers mutex_exit(&softsp->power_mutex); 727611Smyers 728611Smyers return (0); 729611Smyers } 730611Smyers 731611Smyers /* 732611Smyers * Close the device. 733611Smyers */ 734611Smyers /*ARGSUSED*/ 735611Smyers static int 736611Smyers power_close(dev_t dev, int openflags, int otyp, cred_t *credp) 737611Smyers { 738611Smyers struct power_soft_state *softsp; 739611Smyers int clone; 740611Smyers 741611Smyers if (otyp != OTYP_CHR) 742611Smyers return (EINVAL); 743611Smyers 744611Smyers if ((softsp = ddi_get_soft_state(power_state, power_inst)) == 745611Smyers NULL) 746611Smyers return (ENXIO); 747611Smyers 748611Smyers clone = POWER_MINOR_TO_CLONE(getminor(dev)); 749611Smyers mutex_enter(&softsp->power_mutex); 750611Smyers if (softsp->monitor_on == clone) 751611Smyers softsp->monitor_on = 0; 752611Smyers softsp->clones[clone] = 0; 753611Smyers mutex_exit(&softsp->power_mutex); 754611Smyers 755611Smyers return (0); 756611Smyers } 757611Smyers 758611Smyers /*ARGSUSED*/ 759611Smyers static int 760611Smyers power_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cred_p, 761611Smyers int *rval_p) 762611Smyers { 763611Smyers struct power_soft_state *softsp; 764611Smyers int clone; 765611Smyers 766611Smyers if ((softsp = ddi_get_soft_state(power_state, power_inst)) == 767611Smyers NULL) 768611Smyers return (ENXIO); 769611Smyers 770611Smyers clone = POWER_MINOR_TO_CLONE(getminor(dev)); 771611Smyers switch (cmd) { 772611Smyers case PB_BEGIN_MONITOR: 773611Smyers mutex_enter(&softsp->power_mutex); 774611Smyers if (softsp->monitor_on) { 775611Smyers mutex_exit(&softsp->power_mutex); 776611Smyers return (EBUSY); 777611Smyers } 778611Smyers softsp->monitor_on = clone; 779611Smyers mutex_exit(&softsp->power_mutex); 780611Smyers return (0); 781611Smyers 782611Smyers case PB_END_MONITOR: 783611Smyers mutex_enter(&softsp->power_mutex); 784611Smyers 785611Smyers /* 786611Smyers * If PB_END_MONITOR is called without first 787611Smyers * calling PB_BEGIN_MONITOR, an error will be 788611Smyers * returned. 789611Smyers */ 790611Smyers if (!softsp->monitor_on) { 791611Smyers mutex_exit(&softsp->power_mutex); 792611Smyers return (ENXIO); 793611Smyers } 794611Smyers 795611Smyers /* 796611Smyers * This clone is not monitoring the button. 797611Smyers */ 798611Smyers if (softsp->monitor_on != clone) { 799611Smyers mutex_exit(&softsp->power_mutex); 800611Smyers return (EINVAL); 801611Smyers } 802611Smyers softsp->monitor_on = 0; 803611Smyers mutex_exit(&softsp->power_mutex); 804611Smyers return (0); 805611Smyers 806611Smyers case PB_GET_EVENTS: 807611Smyers mutex_enter(&softsp->power_mutex); 808611Smyers if (ddi_copyout((void *)&softsp->events, (void *)arg, 809611Smyers sizeof (int), mode) != 0) { 810611Smyers mutex_exit(&softsp->power_mutex); 811611Smyers return (EFAULT); 812611Smyers } 813611Smyers 814611Smyers /* 815611Smyers * This ioctl returned the events detected since last 816611Smyers * call. Note that any application can get the events 817611Smyers * and clear the event register. 818611Smyers */ 819611Smyers softsp->events = 0; 820611Smyers mutex_exit(&softsp->power_mutex); 821611Smyers return (0); 822611Smyers 823611Smyers /* 824611Smyers * This ioctl is used by the test suite. 825611Smyers */ 826611Smyers case PB_CREATE_BUTTON_EVENT: 827611Smyers #ifdef ACPI_POWER_BUTTON 828611Smyers (UINT32)power_acpi_fixed_event((void *)softsp); 829611Smyers #else 830611Smyers if (softsp->power_regs_mapped) { 831611Smyers mutex_enter(&softsp->power_intr_mutex); 832611Smyers softsp->power_btn_ioctl = B_TRUE; 833611Smyers mutex_exit(&softsp->power_intr_mutex); 834611Smyers } 835611Smyers (void) power_high_intr((caddr_t)softsp); 836611Smyers #endif /* ACPI_POWER_BUTTON */ 837611Smyers return (0); 838611Smyers 839611Smyers default: 840611Smyers return (ENOTTY); 841611Smyers } 842611Smyers } 843611Smyers 844611Smyers /*ARGSUSED*/ 845611Smyers static int 846611Smyers power_chpoll(dev_t dev, short events, int anyyet, 847611Smyers short *reventsp, struct pollhead **phpp) 848611Smyers { 849611Smyers struct power_soft_state *softsp; 850611Smyers 851611Smyers if ((softsp = ddi_get_soft_state(power_state, power_inst)) == NULL) 852611Smyers return (ENXIO); 853611Smyers 854611Smyers mutex_enter(&softsp->power_mutex); 855611Smyers *reventsp = 0; 856611Smyers if (softsp->events) 857611Smyers *reventsp = POLLRDNORM|POLLIN; 858611Smyers else { 859611Smyers if (!anyyet) 860611Smyers *phpp = &softsp->pollhd; 861611Smyers } 862611Smyers mutex_exit(&softsp->power_mutex); 863611Smyers 864611Smyers return (0); 865611Smyers } 866611Smyers 867611Smyers static void 868611Smyers power_log_message(void) 869611Smyers { 870611Smyers struct power_soft_state *softsp; 871611Smyers 872611Smyers if ((softsp = ddi_get_soft_state(power_state, power_inst)) == NULL) { 873611Smyers cmn_err(CE_WARN, "Failed to get internal state!"); 874611Smyers return; 875611Smyers } 876611Smyers 877611Smyers mutex_enter(&softsp->power_mutex); 878611Smyers softsp->shutdown_pending = 0; 879611Smyers cmn_err(CE_WARN, "Failed to shut down the system!"); 880611Smyers mutex_exit(&softsp->power_mutex); 881611Smyers } 882611Smyers 883611Smyers #ifdef ACPI_POWER_BUTTON 884611Smyers /* 885611Smyers * Given a handle to a device object, locate a _PRW object 886611Smyers * if present and fetch the GPE info for this device object 887611Smyers */ 888611Smyers static ACPI_STATUS 889611Smyers power_get_prw_gpe(ACPI_HANDLE dev, ACPI_HANDLE *gpe_dev, UINT32 *gpe_num) 890611Smyers { 891611Smyers ACPI_BUFFER buf; 892611Smyers ACPI_STATUS status; 893611Smyers ACPI_HANDLE prw; 894611Smyers ACPI_OBJECT *gpe; 895611Smyers 896611Smyers /* 897611Smyers * Evaluate _PRW if present 898611Smyers */ 899611Smyers status = AcpiGetHandle(dev, "_PRW", &prw); 900611Smyers if (status != AE_OK) 901611Smyers return (status); 902611Smyers buf.Length = ACPI_ALLOCATE_BUFFER; 903611Smyers status = AcpiEvaluateObjectTyped(prw, NULL, NULL, &buf, 904611Smyers ACPI_TYPE_PACKAGE); 905611Smyers if (status != AE_OK) 906611Smyers return (status); 907611Smyers 908611Smyers /* 909611Smyers * Sanity-check the package; need at least two elements 910611Smyers */ 911611Smyers status = AE_ERROR; 912611Smyers if (((ACPI_OBJECT *)buf.Pointer)->Package.Count < 2) 913611Smyers goto done; 914611Smyers 915611Smyers gpe = &((ACPI_OBJECT *)buf.Pointer)->Package.Elements[0]; 916611Smyers if (gpe->Type == ACPI_TYPE_INTEGER) { 917611Smyers *gpe_dev = NULL; 918611Smyers *gpe_num = gpe->Integer.Value; 919611Smyers status = AE_OK; 920611Smyers } else if (gpe->Type == ACPI_TYPE_PACKAGE) { 921611Smyers if ((gpe->Package.Count != 2) || 922611Smyers (gpe->Package.Elements[0].Type != ACPI_TYPE_DEVICE) || 923611Smyers (gpe->Package.Elements[1].Type != ACPI_TYPE_INTEGER)) 924611Smyers goto done; 925611Smyers *gpe_dev = gpe->Package.Elements[0].Reference.Handle; 926611Smyers *gpe_num = gpe->Package.Elements[1].Integer.Value; 927611Smyers status = AE_OK; 928611Smyers } 929611Smyers 930611Smyers done: 931611Smyers AcpiOsFree(buf.Pointer); 932611Smyers return (status); 933611Smyers } 934611Smyers 935611Smyers 936611Smyers /* 937611Smyers * 938611Smyers */ 939622Smyers /*ARGSUSED*/ 940611Smyers static ACPI_STATUS 941611Smyers acpi_device(ACPI_HANDLE obj, UINT32 nesting, void *context, void **rv) 942611Smyers { 943622Smyers 944611Smyers *((ACPI_HANDLE *)context) = obj; 945611Smyers return (AE_OK); 946611Smyers } 947611Smyers 948611Smyers /* 949611Smyers * 950611Smyers */ 951611Smyers static ACPI_HANDLE 952611Smyers probe_acpi_pwrbutton() 953611Smyers { 954611Smyers ACPI_HANDLE obj = NULL; 955611Smyers 956622Smyers (void) AcpiGetDevices("PNP0C0C", acpi_device, (void *)&obj, NULL); 957611Smyers return (obj); 958611Smyers } 959611Smyers 960611Smyers static UINT32 961611Smyers power_acpi_fixed_event(void *ctx) 962611Smyers { 963611Smyers 964611Smyers mutex_enter(&((struct power_soft_state *)ctx)->power_intr_mutex); 965611Smyers power_button_pressed++; 966611Smyers mutex_exit(&((struct power_soft_state *)ctx)->power_intr_mutex); 967611Smyers 968611Smyers /* post softint to issue timeout for power button action */ 969611Smyers if (((struct power_soft_state *)ctx)->softintr_id != NULL) 970611Smyers ddi_trigger_softintr( 971611Smyers ((struct power_soft_state *)ctx)->softintr_id); 972611Smyers 973611Smyers return (AE_OK); 974611Smyers } 975611Smyers 976622Smyers /*ARGSUSED*/ 977611Smyers static void 978611Smyers power_acpi_notify_event(ACPI_HANDLE obj, UINT32 val, void *ctx) 979611Smyers { 980611Smyers if (val == 0x80) 981622Smyers (void) power_acpi_fixed_event(ctx); 982611Smyers } 983611Smyers 984611Smyers /* 985611Smyers * 986611Smyers */ 987611Smyers static int 988611Smyers power_probe_method_button(struct power_soft_state *softsp) 989611Smyers { 990611Smyers ACPI_HANDLE button_obj; 991611Smyers UINT32 gpe_num; 992611Smyers ACPI_HANDLE gpe_dev; 993611Smyers 994611Smyers button_obj = probe_acpi_pwrbutton(); 995611Smyers softsp->button_obj = button_obj; /* remember obj */ 996611Smyers if ((button_obj != NULL) && 997611Smyers (power_get_prw_gpe(button_obj, &gpe_dev, &gpe_num) == AE_OK) && 998611Smyers (AcpiSetGpeType(gpe_dev, gpe_num, ACPI_GPE_TYPE_WAKE_RUN) == 999611Smyers AE_OK) && 1000611Smyers (AcpiEnableGpe(gpe_dev, gpe_num, ACPI_NOT_ISR) == AE_OK) && 1001611Smyers (AcpiInstallNotifyHandler(button_obj, ACPI_DEVICE_NOTIFY, 1002611Smyers power_acpi_notify_event, (void*)softsp) == AE_OK)) 1003611Smyers return (1); 1004611Smyers return (0); 1005611Smyers } 1006611Smyers 1007611Smyers /* 1008611Smyers * 1009611Smyers */ 1010611Smyers static int 1011611Smyers power_probe_fixed_button(struct power_soft_state *softsp) 1012611Smyers { 1013611Smyers FADT_DESCRIPTOR *fadt; 1014611Smyers 1015611Smyers if (AcpiGetFirmwareTable(FADT_SIG, 1, ACPI_LOGICAL_ADDRESSING, 1016611Smyers (ACPI_TABLE_HEADER **) &fadt) != AE_OK) 1017611Smyers return (0); 1018611Smyers 1019611Smyers if (!fadt->PwrButton) { 1020611Smyers if (AcpiInstallFixedEventHandler(ACPI_EVENT_POWER_BUTTON, 1021611Smyers power_acpi_fixed_event, (void *)softsp) == AE_OK) 1022611Smyers return (1); 1023611Smyers } 1024611Smyers return (0); 1025611Smyers } 1026611Smyers 1027611Smyers 1028611Smyers /* 1029611Smyers * 1030611Smyers */ 1031611Smyers static int 1032611Smyers power_attach_acpi(struct power_soft_state *softsp) 1033611Smyers { 1034611Smyers 1035611Smyers /* 1036611Smyers * If we've attached anything already, return an error 1037611Smyers */ 1038611Smyers if ((softsp->gpe_attached) || (softsp->fixed_attached)) 1039611Smyers return (DDI_FAILURE); 1040611Smyers 1041611Smyers /* 1042611Smyers * attempt to attach both a fixed-event handler and a GPE 1043611Smyers * handler; remember what we got 1044611Smyers */ 1045611Smyers softsp->fixed_attached = (power_probe_fixed_button(softsp) != 0); 1046611Smyers softsp->gpe_attached = (power_probe_method_button(softsp) != 0); 1047611Smyers 1048611Smyers /* 1049611Smyers * If we've attached anything now, return success 1050611Smyers */ 1051611Smyers if ((softsp->gpe_attached) || (softsp->fixed_attached)) 1052611Smyers return (DDI_SUCCESS); 1053611Smyers 1054611Smyers return (DDI_FAILURE); 1055611Smyers } 1056611Smyers 1057611Smyers /* 1058611Smyers * 1059611Smyers */ 1060611Smyers static void 1061611Smyers power_detach_acpi(struct power_soft_state *softsp) 1062611Smyers { 1063611Smyers if (softsp->gpe_attached) { 1064611Smyers if (AcpiRemoveNotifyHandler(softsp->button_obj, 1065611Smyers ACPI_DEVICE_NOTIFY, power_acpi_notify_event) != AE_OK) 1066611Smyers cmn_err(CE_WARN, "!power: failed to remove Notify" 1067611Smyers " handler"); 1068611Smyers } 1069611Smyers 1070611Smyers if (softsp->fixed_attached) { 1071611Smyers if (AcpiRemoveFixedEventHandler(ACPI_EVENT_POWER_BUTTON, 1072611Smyers power_acpi_fixed_event) != AE_OK) 1073611Smyers cmn_err(CE_WARN, "!power: failed to remove Power" 1074611Smyers " Button handler"); 1075611Smyers } 1076611Smyers } 1077611Smyers 1078611Smyers #else 1079611Smyers /* 1080*920Sjbeloro * Code for platforms that have EPIC processor for processing power 1081*920Sjbeloro * button interrupts. 1082*920Sjbeloro */ 1083*920Sjbeloro static int 1084*920Sjbeloro power_setup_epic_regs(dev_info_t *dip, struct power_soft_state *softsp) 1085*920Sjbeloro { 1086*920Sjbeloro ddi_device_acc_attr_t attr; 1087*920Sjbeloro uint8_t *reg_base; 1088*920Sjbeloro 1089*920Sjbeloro attr.devacc_attr_version = DDI_DEVICE_ATTR_V0; 1090*920Sjbeloro attr.devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC; 1091*920Sjbeloro attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC; 1092*920Sjbeloro if (ddi_regs_map_setup(dip, 0, (caddr_t *)®_base, 1093*920Sjbeloro EPIC_REGS_OFFSET, EPIC_REGS_LEN, &attr, 1094*920Sjbeloro &softsp->power_rhandle) != DDI_SUCCESS) { 1095*920Sjbeloro return (DDI_FAILURE); 1096*920Sjbeloro } 1097*920Sjbeloro 1098*920Sjbeloro softsp->power_btn_reg = reg_base; 1099*920Sjbeloro softsp->power_regs_mapped = B_TRUE; 1100*920Sjbeloro 1101*920Sjbeloro /* Clear power button interrupt first */ 1102*920Sjbeloro EPIC_WR(softsp->power_rhandle, softsp->power_btn_reg, 1103*920Sjbeloro EPIC_ATOM_INTR_CLEAR); 1104*920Sjbeloro 1105*920Sjbeloro /* Enable EPIC interrupt for power button single press event */ 1106*920Sjbeloro EPIC_WR(softsp->power_rhandle, softsp->power_btn_reg, 1107*920Sjbeloro EPIC_ATOM_INTR_ENABLE); 1108*920Sjbeloro 1109*920Sjbeloro /* 1110*920Sjbeloro * At this point, EPIC interrupt processing is fully initialised. 1111*920Sjbeloro */ 1112*920Sjbeloro hasEPIC = B_TRUE; 1113*920Sjbeloro return (DDI_SUCCESS); 1114*920Sjbeloro } 1115*920Sjbeloro 1116*920Sjbeloro /* 1117*920Sjbeloro * 1118611Smyers * power button register definitions for acpi register on m1535d 1119611Smyers */ 1120611Smyers #define M1535D_PWR_BTN_REG_01 0x1 1121611Smyers #define M1535D_PWR_BTN_EVENT_FLAG 0x1 1122611Smyers 1123611Smyers static int 1124611Smyers power_setup_m1535_regs(dev_info_t *dip, struct power_soft_state *softsp) 1125611Smyers { 1126611Smyers ddi_device_acc_attr_t attr; 1127611Smyers uint8_t *reg_base; 1128611Smyers 1129611Smyers attr.devacc_attr_version = DDI_DEVICE_ATTR_V0; 1130611Smyers attr.devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC; 1131611Smyers attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC; 1132611Smyers if (ddi_regs_map_setup(dip, 0, (caddr_t *)®_base, 0, 0, &attr, 1133611Smyers &softsp->power_rhandle) != DDI_SUCCESS) { 1134611Smyers return (DDI_FAILURE); 1135611Smyers } 1136611Smyers softsp->power_btn_reg = ®_base[M1535D_PWR_BTN_REG_01]; 1137611Smyers softsp->power_btn_bit = M1535D_PWR_BTN_EVENT_FLAG; 1138611Smyers softsp->power_regs_mapped = B_TRUE; 1139611Smyers return (DDI_SUCCESS); 1140611Smyers } 1141611Smyers 1142611Smyers /* 1143*920Sjbeloro * MBC Fire/SSI Interrupt Status Register definitions 1144*920Sjbeloro */ 1145*920Sjbeloro #define FIRE_SSI_ISR 0x0 1146*920Sjbeloro #define FIRE_SSI_INTR_ENA 0x8 1147*920Sjbeloro #define FIRE_SSI_SHUTDOWN_REQ 0x4 1148*920Sjbeloro 1149*920Sjbeloro static int 1150*920Sjbeloro power_setup_mbc_regs(dev_info_t *dip, struct power_soft_state *softsp) 1151*920Sjbeloro { 1152*920Sjbeloro ddi_device_acc_attr_t attr; 1153*920Sjbeloro uint8_t *reg_base; 1154*920Sjbeloro ddi_acc_handle_t hdl; 1155*920Sjbeloro uint8_t reg; 1156*920Sjbeloro 1157*920Sjbeloro attr.devacc_attr_version = DDI_DEVICE_ATTR_V0; 1158*920Sjbeloro attr.devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC; 1159*920Sjbeloro attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC; 1160*920Sjbeloro if (ddi_regs_map_setup(dip, 0, (caddr_t *)®_base, 0, 0, &attr, 1161*920Sjbeloro &softsp->power_rhandle) != DDI_SUCCESS) { 1162*920Sjbeloro return (DDI_FAILURE); 1163*920Sjbeloro } 1164*920Sjbeloro softsp->power_btn_reg = ®_base[FIRE_SSI_ISR]; 1165*920Sjbeloro softsp->power_btn_bit = FIRE_SSI_SHUTDOWN_REQ; 1166*920Sjbeloro /* 1167*920Sjbeloro * Enable MBC Fire Power Button interrupt. 1168*920Sjbeloro */ 1169*920Sjbeloro hdl = softsp->power_rhandle; 1170*920Sjbeloro reg = ddi_get8(hdl, ®_base[FIRE_SSI_INTR_ENA]); 1171*920Sjbeloro reg |= FIRE_SSI_SHUTDOWN_REQ; 1172*920Sjbeloro ddi_put8(hdl, ®_base[FIRE_SSI_INTR_ENA], reg); 1173*920Sjbeloro 1174*920Sjbeloro softsp->power_regs_mapped = B_TRUE; 1175*920Sjbeloro 1176*920Sjbeloro return (DDI_SUCCESS); 1177*920Sjbeloro } 1178*920Sjbeloro 1179*920Sjbeloro /* 1180611Smyers * Setup register map for the power button 1181611Smyers * NOTE:- we only map registers for platforms 1182611Smyers * binding with the ali1535d+-power compatible 1183*920Sjbeloro * property or mbc-power or epic property. 1184611Smyers */ 1185611Smyers static int 1186611Smyers power_setup_regs(struct power_soft_state *softsp) 1187611Smyers { 1188611Smyers char *binding_name; 1189611Smyers 1190611Smyers softsp->power_regs_mapped = B_FALSE; 1191611Smyers softsp->power_btn_ioctl = B_FALSE; 1192611Smyers binding_name = ddi_binding_name(softsp->dip); 1193*920Sjbeloro if (strcmp(binding_name, "mbc-power") == 0) 1194*920Sjbeloro return (power_setup_mbc_regs(softsp->dip, softsp)); 1195*920Sjbeloro else if (strcmp(binding_name, "SUNW,ebus-pic18lf65j10-power") == 0) 1196*920Sjbeloro return (power_setup_epic_regs(softsp->dip, softsp)); 1197*920Sjbeloro else if (strcmp(binding_name, "ali1535d+-power") == 0) 1198611Smyers return (power_setup_m1535_regs(softsp->dip, softsp)); 1199611Smyers 1200*920Sjbeloro /* 1201*920Sjbeloro * If the binding name is not one of these, that means there is no 1202*920Sjbeloro * additional HW and hence no extra processing is necessary. Just 1203*920Sjbeloro * return SUCCESS. 1204*920Sjbeloro */ 1205611Smyers return (DDI_SUCCESS); 1206611Smyers } 1207611Smyers 1208611Smyers static void 1209611Smyers power_free_regs(struct power_soft_state *softsp) 1210611Smyers { 1211611Smyers if (softsp->power_regs_mapped) 1212611Smyers ddi_regs_map_free(&softsp->power_rhandle); 1213611Smyers } 1214611Smyers #endif /* ACPI_POWER_BUTTON */ 1215