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