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