1611Smyers /* 2611Smyers * CDDL HEADER START 3611Smyers * 4611Smyers * The contents of this file are subject to the terms of the 51877Sgk73471 * Common Development and Distribution License (the "License"). 61877Sgk73471 * You may not use this file except in compliance with the License. 7611Smyers * 8611Smyers * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9611Smyers * or http://www.opensolaris.org/os/licensing. 10611Smyers * See the License for the specific language governing permissions 11611Smyers * and limitations under the License. 12611Smyers * 13611Smyers * When distributing Covered Code, include this CDDL HEADER in each 14611Smyers * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15611Smyers * If applicable, add the following below this CDDL HEADER, with the 16611Smyers * fields enclosed by brackets "[]" replaced with your own identifying 17611Smyers * information: Portions Copyright [yyyy] [name of copyright owner] 18611Smyers * 19611Smyers * CDDL HEADER END 20611Smyers */ 21611Smyers /* 22*6573Sphitran * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 23611Smyers * Use is subject to license terms. 24611Smyers */ 25611Smyers 26611Smyers #pragma ident "%Z%%M% %I% %E% SMI" 27611Smyers 28611Smyers /* 29611Smyers * Power Button Driver 30611Smyers * 31611Smyers * This driver handles interrupt generated by the power button on 32611Smyers * platforms with "power" device node which has "button" property. 33611Smyers * Currently, these platforms are: 34611Smyers * 35611Smyers * ACPI-enabled x86/x64 platforms 36611Smyers * Ultra-5_10, Ultra-80, Sun-Blade-100, Sun-Blade-150, 37611Smyers * Sun-Blade-1500, Sun-Blade-2500, 38611Smyers * Sun-Fire-V210, Sun-Fire-V240, Netra-240 39611Smyers * 40611Smyers * Only one instance is allowed to attach. In order to know when 41611Smyers * an application that has opened the device is going away, a new 42611Smyers * minor clone is created for each open(9E) request. There are 43611Smyers * allocations for creating minor clones between 1 and 255. The ioctl 44611Smyers * interface is defined by pbio(7I) and approved as part of 45611Smyers * PSARC/1999/393 case. 46611Smyers */ 47611Smyers 48611Smyers #include <sys/types.h> 49611Smyers #include <sys/conf.h> 50611Smyers #include <sys/ddi.h> 51611Smyers #include <sys/sunddi.h> 52611Smyers #include <sys/ddi_impldefs.h> 53611Smyers #include <sys/cmn_err.h> 54611Smyers #include <sys/errno.h> 55611Smyers #include <sys/modctl.h> 56611Smyers #include <sys/open.h> 57611Smyers #include <sys/stat.h> 58611Smyers #include <sys/poll.h> 59611Smyers #include <sys/pbio.h> 60*6573Sphitran #include <sys/sysevent/eventdefs.h> 61*6573Sphitran #include <sys/sysevent/pwrctl.h> 62920Sjbeloro 633446Smrj #if defined(__sparc) 643446Smrj #include <sys/machsystm.h> 653446Smrj #endif 663446Smrj 67611Smyers #ifdef ACPI_POWER_BUTTON 68920Sjbeloro 69611Smyers #include <sys/acpi/acpi.h> 70611Smyers #include <sys/acpica.h> 71920Sjbeloro 72920Sjbeloro #else 73920Sjbeloro 74920Sjbeloro #include <sys/epic.h> 75920Sjbeloro /* 76920Sjbeloro * Some #defs that must be here as they differ for power.c 77920Sjbeloro * and epic.c 78920Sjbeloro */ 79920Sjbeloro #define EPIC_REGS_OFFSET 0x00 80920Sjbeloro #define EPIC_REGS_LEN 0x82 81920Sjbeloro 82920Sjbeloro 83920Sjbeloro /* 84920Sjbeloro * This flag, which is set for platforms, that have EPIC processor 85920Sjbeloro * to process power button interrupt, helps in executing platform 86920Sjbeloro * specific code. 87920Sjbeloro */ 88920Sjbeloro static char hasEPIC = B_FALSE; 89611Smyers #endif /* ACPI_POWER_BUTTON */ 90611Smyers 91611Smyers /* 92611Smyers * Maximum number of clone minors that is allowed. This value 93611Smyers * is defined relatively low to save memory. 94611Smyers */ 95611Smyers #define POWER_MAX_CLONE 256 96611Smyers 97611Smyers /* 98611Smyers * Minor number is instance << 8 + clone minor from range 1-255; clone 0 99611Smyers * is reserved for "original" minor. 100611Smyers */ 101611Smyers #define POWER_MINOR_TO_CLONE(minor) ((minor) & (POWER_MAX_CLONE - 1)) 102611Smyers 103611Smyers /* 104611Smyers * Power Button Abort Delay 105611Smyers */ 106611Smyers #define ABORT_INCREMENT_DELAY 10 107611Smyers 108611Smyers /* 1091084Sjroberts * FWARC 2005/687: power device compatible property 1101084Sjroberts */ 1111084Sjroberts #define POWER_DEVICE_TYPE "power-device-type" 1121084Sjroberts 1131084Sjroberts /* 114611Smyers * Driver global variables 115611Smyers */ 116611Smyers static void *power_state; 117611Smyers static int power_inst = -1; 118611Smyers 119611Smyers static hrtime_t power_button_debounce = NANOSEC/MILLISEC*10; 120611Smyers static hrtime_t power_button_abort_interval = 1.5 * NANOSEC; 121611Smyers static int power_button_abort_presses = 3; 122611Smyers static int power_button_abort_enable = 1; 123611Smyers static int power_button_enable = 1; 124611Smyers 125611Smyers static int power_button_pressed = 0; 126611Smyers static int power_button_cancel = 0; 127611Smyers static int power_button_timeouts = 0; 128611Smyers static int timeout_cancel = 0; 129611Smyers static int additional_presses = 0; 130611Smyers 131611Smyers /* 132611Smyers * Function prototypes 133611Smyers */ 134611Smyers static int power_attach(dev_info_t *, ddi_attach_cmd_t); 135611Smyers static int power_detach(dev_info_t *, ddi_detach_cmd_t); 136611Smyers static int power_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **); 137611Smyers static int power_open(dev_t *, int, int, cred_t *); 138611Smyers static int power_close(dev_t, int, int, cred_t *); 139611Smyers static int power_ioctl(dev_t, int, intptr_t, int, cred_t *, int *); 140611Smyers static int power_chpoll(dev_t, short, int, short *, struct pollhead **); 141611Smyers #ifndef ACPI_POWER_BUTTON 142611Smyers static uint_t power_high_intr(caddr_t); 143611Smyers #endif 144611Smyers static uint_t power_soft_intr(caddr_t); 145611Smyers static uint_t power_issue_shutdown(caddr_t); 146611Smyers static void power_timeout(caddr_t); 147611Smyers static void power_log_message(void); 148611Smyers 149611Smyers /* 150611Smyers * Structure used in the driver 151611Smyers */ 152611Smyers struct power_soft_state { 153611Smyers dev_info_t *dip; /* device info pointer */ 154611Smyers kmutex_t power_mutex; /* mutex lock */ 155611Smyers kmutex_t power_intr_mutex; /* interrupt mutex lock */ 156611Smyers ddi_iblock_cookie_t soft_iblock_cookie; /* holds interrupt cookie */ 157611Smyers ddi_iblock_cookie_t high_iblock_cookie; /* holds interrupt cookie */ 158611Smyers ddi_softintr_t softintr_id; /* soft interrupt id */ 159611Smyers uchar_t clones[POWER_MAX_CLONE]; /* array of minor clones */ 160611Smyers int monitor_on; /* clone monitoring the button event */ 161611Smyers /* clone 0 indicates no one is */ 162611Smyers /* monitoring the button event */ 163611Smyers pollhead_t pollhd; /* poll head struct */ 164611Smyers int events; /* bit map of occured events */ 165611Smyers int shutdown_pending; /* system shutdown in progress */ 166611Smyers #ifdef ACPI_POWER_BUTTON 167611Smyers boolean_t fixed_attached; /* true means fixed is attached */ 168611Smyers boolean_t gpe_attached; /* true means GPE is attached */ 169611Smyers ACPI_HANDLE button_obj; /* handle to device power button */ 170611Smyers #else 171611Smyers ddi_acc_handle_t power_rhandle; /* power button register handle */ 172611Smyers uint8_t *power_btn_reg; /* power button register address */ 173611Smyers uint8_t power_btn_bit; /* power button register bit */ 174611Smyers boolean_t power_regs_mapped; /* flag to tell if regs mapped */ 175611Smyers boolean_t power_btn_ioctl; /* flag to specify ioctl request */ 176611Smyers #endif 177611Smyers }; 178611Smyers 179*6573Sphitran static void power_gen_sysevent(struct power_soft_state *); 180*6573Sphitran 181611Smyers #ifdef ACPI_POWER_BUTTON 182611Smyers static int power_attach_acpi(struct power_soft_state *softsp); 183611Smyers static void power_detach_acpi(struct power_soft_state *softsp); 184611Smyers static UINT32 power_acpi_fixed_event(void *ctx); 185611Smyers #else 186611Smyers static int power_setup_regs(struct power_soft_state *softsp); 187611Smyers static void power_free_regs(struct power_soft_state *softsp); 188611Smyers #endif /* ACPI_POWER_BUTTON */ 189611Smyers 190611Smyers /* 191611Smyers * Configuration data structures 192611Smyers */ 193611Smyers static struct cb_ops power_cb_ops = { 194611Smyers power_open, /* open */ 195611Smyers power_close, /* close */ 196611Smyers nodev, /* strategy */ 197611Smyers nodev, /* print */ 198611Smyers nodev, /* dump */ 199611Smyers nodev, /* read */ 200611Smyers nodev, /* write */ 201611Smyers power_ioctl, /* ioctl */ 202611Smyers nodev, /* devmap */ 203611Smyers nodev, /* mmap */ 204611Smyers nodev, /* segmap */ 205611Smyers power_chpoll, /* poll */ 206611Smyers ddi_prop_op, /* cb_prop_op */ 207611Smyers NULL, /* streamtab */ 208611Smyers D_MP | D_NEW, /* Driver compatibility flag */ 209611Smyers CB_REV, /* rev */ 210611Smyers nodev, /* cb_aread */ 211611Smyers nodev /* cb_awrite */ 212611Smyers }; 213611Smyers 214611Smyers static struct dev_ops power_ops = { 215611Smyers DEVO_REV, /* devo_rev, */ 216611Smyers 0, /* refcnt */ 217611Smyers power_getinfo, /* getinfo */ 218611Smyers nulldev, /* identify */ 219611Smyers nulldev, /* probe */ 220611Smyers power_attach, /* attach */ 221611Smyers power_detach, /* detach */ 222611Smyers nodev, /* reset */ 223611Smyers &power_cb_ops, /* cb_ops */ 224611Smyers (struct bus_ops *)NULL, /* bus_ops */ 225611Smyers NULL /* power */ 226611Smyers }; 227611Smyers 228611Smyers static struct modldrv modldrv = { 229611Smyers &mod_driverops, /* Type of module. This one is a driver */ 230*6573Sphitran "power button driver", /* name of module */ 231611Smyers &power_ops, /* driver ops */ 232611Smyers }; 233611Smyers 234611Smyers static struct modlinkage modlinkage = { 235611Smyers MODREV_1, 236611Smyers (void *)&modldrv, 237611Smyers NULL 238611Smyers }; 239611Smyers 240611Smyers /* 241611Smyers * These are the module initialization routines. 242611Smyers */ 243611Smyers 244611Smyers int 245611Smyers _init(void) 246611Smyers { 247611Smyers int error; 248611Smyers 249611Smyers if ((error = ddi_soft_state_init(&power_state, 250611Smyers sizeof (struct power_soft_state), 0)) != 0) 251611Smyers return (error); 252611Smyers 253611Smyers if ((error = mod_install(&modlinkage)) != 0) 254611Smyers ddi_soft_state_fini(&power_state); 255611Smyers 256611Smyers return (error); 257611Smyers } 258611Smyers 259611Smyers int 260611Smyers _fini(void) 261611Smyers { 262611Smyers int error; 263611Smyers 264611Smyers if ((error = mod_remove(&modlinkage)) == 0) 265611Smyers ddi_soft_state_fini(&power_state); 266611Smyers 267611Smyers return (error); 268611Smyers } 269611Smyers 270611Smyers int 271611Smyers _info(struct modinfo *modinfop) 272611Smyers { 273611Smyers return (mod_info(&modlinkage, modinfop)); 274611Smyers } 275611Smyers 276611Smyers /*ARGSUSED*/ 277611Smyers static int 278611Smyers power_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, 279611Smyers void **result) 280611Smyers { 281611Smyers struct power_soft_state *softsp; 282611Smyers 283611Smyers if (power_inst == -1) 284611Smyers return (DDI_FAILURE); 285611Smyers 286611Smyers switch (infocmd) { 287611Smyers case DDI_INFO_DEVT2DEVINFO: 288611Smyers if ((softsp = ddi_get_soft_state(power_state, power_inst)) 289611Smyers == NULL) 290611Smyers return (DDI_FAILURE); 291611Smyers *result = (void *)softsp->dip; 292611Smyers return (DDI_SUCCESS); 293611Smyers 294611Smyers case DDI_INFO_DEVT2INSTANCE: 295611Smyers *result = (void *)(uintptr_t)power_inst; 296611Smyers return (DDI_SUCCESS); 297611Smyers 298611Smyers default: 299611Smyers return (DDI_FAILURE); 300611Smyers } 301611Smyers } 302611Smyers 303611Smyers static int 304611Smyers power_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 305611Smyers { 306611Smyers struct power_soft_state *softsp; 307611Smyers 308611Smyers switch (cmd) { 309611Smyers case DDI_ATTACH: 310611Smyers break; 311611Smyers case DDI_RESUME: 312611Smyers return (DDI_SUCCESS); 313611Smyers default: 314611Smyers return (DDI_FAILURE); 315611Smyers } 316611Smyers 317611Smyers /* 318611Smyers * If the power node doesn't have "button" property, quietly 319611Smyers * fail to attach. 320611Smyers */ 321611Smyers if (ddi_prop_exists(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 322611Smyers "button") == 0) 323611Smyers return (DDI_FAILURE); 324611Smyers 325611Smyers if (power_inst != -1) 326611Smyers return (DDI_FAILURE); 327611Smyers 328611Smyers power_inst = ddi_get_instance(dip); 329611Smyers 330611Smyers if (ddi_soft_state_zalloc(power_state, power_inst) != DDI_SUCCESS) 331611Smyers return (DDI_FAILURE); 332611Smyers 333611Smyers if (ddi_create_minor_node(dip, "power_button", S_IFCHR, 334611Smyers (power_inst << 8) + 0, "ddi_power_button", 0) != DDI_SUCCESS) 335611Smyers return (DDI_FAILURE); 336611Smyers 337611Smyers softsp = ddi_get_soft_state(power_state, power_inst); 338611Smyers softsp->dip = dip; 339611Smyers 340611Smyers #ifdef ACPI_POWER_BUTTON 341622Smyers (void) power_attach_acpi(softsp); 342611Smyers #else 343611Smyers if (power_setup_regs(softsp) != DDI_SUCCESS) { 344611Smyers cmn_err(CE_WARN, "power_attach: failed to setup registers"); 345611Smyers goto error; 346611Smyers } 347611Smyers 348611Smyers if (ddi_get_iblock_cookie(dip, 0, 349611Smyers &softsp->high_iblock_cookie) != DDI_SUCCESS) { 350611Smyers cmn_err(CE_WARN, "power_attach: ddi_get_soft_iblock_cookie " 351611Smyers "failed."); 352611Smyers goto error; 353611Smyers } 354611Smyers mutex_init(&softsp->power_intr_mutex, NULL, MUTEX_DRIVER, 355611Smyers softsp->high_iblock_cookie); 356611Smyers 357611Smyers if (ddi_add_intr(dip, 0, &softsp->high_iblock_cookie, NULL, 358611Smyers power_high_intr, (caddr_t)softsp) != DDI_SUCCESS) { 359611Smyers cmn_err(CE_WARN, "power_attach: failed to add high-level " 360611Smyers " interrupt handler."); 361611Smyers mutex_destroy(&softsp->power_intr_mutex); 362611Smyers goto error; 363611Smyers } 364611Smyers #endif /* ACPI_POWER_BUTTON */ 365611Smyers 366611Smyers if (ddi_get_soft_iblock_cookie(dip, DDI_SOFTINT_LOW, 367611Smyers &softsp->soft_iblock_cookie) != DDI_SUCCESS) { 368611Smyers cmn_err(CE_WARN, "power_attach: ddi_get_soft_iblock_cookie " 369611Smyers "failed."); 370611Smyers mutex_destroy(&softsp->power_intr_mutex); 371611Smyers ddi_remove_intr(dip, 0, NULL); 372611Smyers goto error; 373611Smyers } 374611Smyers 375611Smyers mutex_init(&softsp->power_mutex, NULL, MUTEX_DRIVER, 376611Smyers (void *)softsp->soft_iblock_cookie); 377611Smyers 378611Smyers if (ddi_add_softintr(dip, DDI_SOFTINT_LOW, &softsp->softintr_id, 379611Smyers NULL, NULL, power_soft_intr, (caddr_t)softsp) != DDI_SUCCESS) { 380611Smyers cmn_err(CE_WARN, "power_attach: failed to add soft " 381611Smyers "interrupt handler."); 382611Smyers mutex_destroy(&softsp->power_mutex); 383611Smyers mutex_destroy(&softsp->power_intr_mutex); 384611Smyers ddi_remove_intr(dip, 0, NULL); 385611Smyers goto error; 386611Smyers } 387611Smyers 388611Smyers ddi_report_dev(dip); 389611Smyers 390611Smyers return (DDI_SUCCESS); 391611Smyers 392611Smyers error: 393611Smyers #ifdef ACPI_POWER_BUTTON 394611Smyers /* 395611Smyers * detach ACPI power button 396611Smyers */ 397611Smyers power_detach_acpi(softsp); 398611Smyers #else 399611Smyers power_free_regs(softsp); 400611Smyers #endif /* ACPI_POWER_BUTTON */ 401611Smyers ddi_remove_minor_node(dip, "power_button"); 402611Smyers ddi_soft_state_free(power_state, power_inst); 403611Smyers return (DDI_FAILURE); 404611Smyers } 405611Smyers 406611Smyers /*ARGSUSED*/ 407611Smyers /* 408611Smyers * This driver doesn't detach. 409611Smyers */ 410611Smyers static int 411611Smyers power_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 412611Smyers { 413611Smyers /* 414611Smyers * Since the "power" node has "reg" property, as part of 415611Smyers * the suspend operation, detach(9E) entry point is called. 416611Smyers * There is no state to save, since this register is used 417611Smyers * by OBP to power off the system and the state of the 418611Smyers * power off is preserved by hardware. 419611Smyers */ 420611Smyers return ((cmd == DDI_SUSPEND) ? DDI_SUCCESS : 421611Smyers DDI_FAILURE); 422611Smyers } 423611Smyers 424920Sjbeloro 425611Smyers #ifndef ACPI_POWER_BUTTON 426611Smyers /* 427611Smyers * Handler for the high-level interrupt. 428611Smyers */ 429611Smyers static uint_t 430611Smyers power_high_intr(caddr_t arg) 431611Smyers { 432611Smyers struct power_soft_state *softsp = (struct power_soft_state *)arg; 433611Smyers ddi_acc_handle_t hdl = softsp->power_rhandle; 434611Smyers uint8_t reg; 435611Smyers 436611Smyers hrtime_t tstamp; 437611Smyers static hrtime_t o_tstamp = 0; 438611Smyers static hrtime_t power_button_tstamp = 0; 439611Smyers static int power_button_cnt; 440611Smyers 441611Smyers if (softsp->power_regs_mapped) { 442611Smyers mutex_enter(&softsp->power_intr_mutex); 443920Sjbeloro 444920Sjbeloro /* Check if power button interrupt is delivered by EPIC HW */ 445920Sjbeloro if (hasEPIC) { 446920Sjbeloro /* read isr - first issue command */ 447920Sjbeloro EPIC_WR(hdl, softsp->power_btn_reg, 448*6573Sphitran EPIC_ATOM_INTR_READ); 449920Sjbeloro /* next, read the reg */ 450920Sjbeloro EPIC_RD(hdl, softsp->power_btn_reg, reg); 451920Sjbeloro 452920Sjbeloro if (reg & EPIC_FIRE_INTERRUPT) { /* PB pressed */ 453920Sjbeloro /* clear the interrupt */ 454920Sjbeloro EPIC_WR(hdl, softsp->power_btn_reg, 455*6573Sphitran EPIC_ATOM_INTR_CLEAR); 456920Sjbeloro } else { 457920Sjbeloro if (!softsp->power_btn_ioctl) { 458920Sjbeloro mutex_exit(&softsp->power_intr_mutex); 459920Sjbeloro return (DDI_INTR_CLAIMED); 460920Sjbeloro } 461920Sjbeloro softsp->power_btn_ioctl = B_FALSE; 462920Sjbeloro } 463611Smyers } else { 464920Sjbeloro reg = ddi_get8(hdl, softsp->power_btn_reg); 465920Sjbeloro if (reg & softsp->power_btn_bit) { 466920Sjbeloro reg &= softsp->power_btn_bit; 467920Sjbeloro ddi_put8(hdl, softsp->power_btn_reg, reg); 468920Sjbeloro (void) ddi_get8(hdl, softsp->power_btn_reg); 469920Sjbeloro } else { 470920Sjbeloro if (!softsp->power_btn_ioctl) { 471920Sjbeloro mutex_exit(&softsp->power_intr_mutex); 472920Sjbeloro return (DDI_INTR_CLAIMED); 473920Sjbeloro } 474920Sjbeloro softsp->power_btn_ioctl = B_FALSE; 475611Smyers } 476611Smyers } 477611Smyers mutex_exit(&softsp->power_intr_mutex); 478611Smyers } 479611Smyers 480611Smyers tstamp = gethrtime(); 481611Smyers 482611Smyers /* need to deal with power button debounce */ 483611Smyers if (o_tstamp && (tstamp - o_tstamp) < power_button_debounce) { 484611Smyers o_tstamp = tstamp; 485611Smyers return (DDI_INTR_CLAIMED); 486611Smyers } 487611Smyers o_tstamp = tstamp; 488611Smyers 489611Smyers power_button_cnt++; 490611Smyers 491611Smyers mutex_enter(&softsp->power_intr_mutex); 492611Smyers power_button_pressed++; 493611Smyers mutex_exit(&softsp->power_intr_mutex); 494611Smyers 495611Smyers /* 496611Smyers * If power button abort is enabled and power button was pressed 497611Smyers * power_button_abort_presses times within power_button_abort_interval 498611Smyers * then call abort_sequence_enter(); 499611Smyers */ 500611Smyers if (power_button_abort_enable) { 501611Smyers if (power_button_abort_presses == 1 || 502611Smyers tstamp < (power_button_tstamp + 503611Smyers power_button_abort_interval)) { 504611Smyers if (power_button_cnt == power_button_abort_presses) { 505611Smyers mutex_enter(&softsp->power_intr_mutex); 506611Smyers power_button_cancel += power_button_timeouts; 507611Smyers power_button_pressed = 0; 508611Smyers mutex_exit(&softsp->power_intr_mutex); 509611Smyers power_button_cnt = 0; 510611Smyers abort_sequence_enter("Power Button Abort"); 511611Smyers return (DDI_INTR_CLAIMED); 512611Smyers } 513611Smyers } else { 514611Smyers power_button_cnt = 1; 515611Smyers power_button_tstamp = tstamp; 516611Smyers } 517611Smyers } 518611Smyers 519611Smyers if (!power_button_enable) 520611Smyers return (DDI_INTR_CLAIMED); 521611Smyers 522611Smyers /* post softint to issue timeout for power button action */ 523611Smyers if (softsp->softintr_id != NULL) 524611Smyers ddi_trigger_softintr(softsp->softintr_id); 525611Smyers 526611Smyers return (DDI_INTR_CLAIMED); 527611Smyers } 528611Smyers #endif /* ifndef ACPI_POWER_BUTTON */ 529611Smyers 530611Smyers /* 531611Smyers * Handle the softints.... 532611Smyers * 533611Smyers * If only one softint is posted for several button presses, record 534611Smyers * the number of additional presses just incase this was actually not quite 535611Smyers * an Abort sequence so that we can log this event later. 536611Smyers * 537611Smyers * Issue a timeout with a duration being a fraction larger than 538611Smyers * the specified Abort interval inorder to perform a power down if required. 539611Smyers */ 540611Smyers static uint_t 541611Smyers power_soft_intr(caddr_t arg) 542611Smyers { 543611Smyers struct power_soft_state *softsp = (struct power_soft_state *)arg; 544611Smyers 545611Smyers if (!power_button_abort_enable) 546611Smyers return (power_issue_shutdown(arg)); 547611Smyers 548611Smyers mutex_enter(&softsp->power_intr_mutex); 549611Smyers if (!power_button_pressed) { 550611Smyers mutex_exit(&softsp->power_intr_mutex); 551611Smyers return (DDI_INTR_CLAIMED); 552611Smyers } 553611Smyers 554611Smyers /* 555611Smyers * Schedule a timeout to do the necessary 556611Smyers * work for shutdown, only one timeout for 557611Smyers * n presses if power button was pressed 558611Smyers * more than once before softint fired 559611Smyers */ 560611Smyers if (power_button_pressed > 1) 561611Smyers additional_presses += power_button_pressed - 1; 562611Smyers 563611Smyers timeout_cancel = 0; 564611Smyers power_button_pressed = 0; 565611Smyers power_button_timeouts++; 566611Smyers mutex_exit(&softsp->power_intr_mutex); 567611Smyers (void) timeout((void(*)(void *))power_timeout, 568611Smyers softsp, NSEC_TO_TICK(power_button_abort_interval) + 569611Smyers ABORT_INCREMENT_DELAY); 570611Smyers 571611Smyers return (DDI_INTR_CLAIMED); 572611Smyers } 573611Smyers 574611Smyers /* 575611Smyers * Upon receiving a timeout the following is determined: 576611Smyers * 577611Smyers * If an Abort sequence was issued, then we cancel all outstanding timeouts 578611Smyers * and additional presses prior to the Abort sequence. 579611Smyers * 580611Smyers * If we had multiple timeouts issued and the abort sequence was not met, 581611Smyers * then we had more than one button press to power down the machine. We 582611Smyers * were probably trying to issue an abort. So log a message indicating this 583611Smyers * and cancel all outstanding timeouts. 584611Smyers * 585611Smyers * If we had just one timeout and the abort sequence was not met then 586611Smyers * we really did want to power down the machine, so call power_issue_shutdown() 587611Smyers * to do the work and schedule a power down 588611Smyers */ 589611Smyers static void 590611Smyers power_timeout(caddr_t arg) 591611Smyers { 592611Smyers struct power_soft_state *softsp = (struct power_soft_state *)arg; 593611Smyers static int first = 0; 594611Smyers 595611Smyers /* 596611Smyers * Abort was generated cancel all outstanding power 597611Smyers * button timeouts 598611Smyers */ 599611Smyers mutex_enter(&softsp->power_intr_mutex); 600611Smyers if (power_button_cancel) { 601611Smyers power_button_cancel--; 602611Smyers power_button_timeouts--; 603611Smyers if (!first) { 604611Smyers first++; 605611Smyers additional_presses = 0; 606611Smyers } 607611Smyers mutex_exit(&softsp->power_intr_mutex); 608611Smyers return; 609611Smyers } 610611Smyers first = 0; 611611Smyers 612611Smyers /* 613611Smyers * We get here if the timeout(s) have fired and they were 614611Smyers * not issued prior to an abort. 615611Smyers * 616611Smyers * If we had more than one press in the interval we were 617611Smyers * probably trying to issue an abort, but didnt press the 618611Smyers * required number within the interval. Hence cancel all 619611Smyers * timeouts and do not continue towards shutdown. 620611Smyers */ 621611Smyers if (!timeout_cancel) { 622611Smyers timeout_cancel = power_button_timeouts + 623611Smyers additional_presses; 624611Smyers 625611Smyers power_button_timeouts--; 626611Smyers if (!power_button_timeouts) 627611Smyers additional_presses = 0; 628611Smyers 629611Smyers if (timeout_cancel > 1) { 630611Smyers mutex_exit(&softsp->power_intr_mutex); 631611Smyers cmn_err(CE_NOTE, "Power Button pressed " 632611Smyers "%d times, cancelling all requests", 633611Smyers timeout_cancel); 634611Smyers return; 635611Smyers } 636611Smyers mutex_exit(&softsp->power_intr_mutex); 637611Smyers 638611Smyers /* Go and do the work to request shutdown */ 639611Smyers (void) power_issue_shutdown((caddr_t)softsp); 640611Smyers return; 641611Smyers } 642611Smyers 643611Smyers power_button_timeouts--; 644611Smyers if (!power_button_timeouts) 645611Smyers additional_presses = 0; 646611Smyers mutex_exit(&softsp->power_intr_mutex); 647611Smyers } 648611Smyers 649611Smyers #ifdef ACPI_POWER_BUTTON 650611Smyers static void 651611Smyers do_shutdown(void) 652611Smyers { 653611Smyers proc_t *initpp; 654611Smyers 655611Smyers /* 656611Smyers * If we're still booting and init(1) isn't set up yet, simply halt. 657611Smyers */ 658611Smyers mutex_enter(&pidlock); 659611Smyers initpp = prfind(P_INITPID); 660611Smyers mutex_exit(&pidlock); 661611Smyers if (initpp == NULL) { 662611Smyers extern void halt(char *); 663611Smyers halt("Power off the System"); /* just in case */ 664611Smyers } 665611Smyers 666611Smyers /* 667611Smyers * else, graceful shutdown with inittab and all getting involved 668611Smyers */ 669611Smyers psignal(initpp, SIGPWR); 670611Smyers } 671611Smyers #endif 672611Smyers 673611Smyers static uint_t 674611Smyers power_issue_shutdown(caddr_t arg) 675611Smyers { 676611Smyers struct power_soft_state *softsp = (struct power_soft_state *)arg; 677611Smyers 678611Smyers mutex_enter(&softsp->power_mutex); 679611Smyers softsp->events |= PB_BUTTON_PRESS; 680611Smyers if (softsp->monitor_on != 0) { 681611Smyers mutex_exit(&softsp->power_mutex); 682611Smyers pollwakeup(&softsp->pollhd, POLLRDNORM); 683611Smyers pollwakeup(&softsp->pollhd, POLLIN); 684*6573Sphitran power_gen_sysevent(softsp); 685611Smyers return (DDI_INTR_CLAIMED); 686611Smyers } 687611Smyers 688611Smyers if (!softsp->shutdown_pending) { 689611Smyers cmn_err(CE_WARN, "Power off requested from power button or " 690611Smyers "SC, powering down the system!"); 691611Smyers softsp->shutdown_pending = 1; 692611Smyers do_shutdown(); 693611Smyers 694611Smyers /* 695611Smyers * Wait a while for "do_shutdown()" to shut down the system 696611Smyers * before logging an error message. 697611Smyers */ 698611Smyers (void) timeout((void(*)(void *))power_log_message, NULL, 699611Smyers 100 * hz); 700611Smyers } 701611Smyers mutex_exit(&softsp->power_mutex); 702611Smyers 703611Smyers return (DDI_INTR_CLAIMED); 704611Smyers } 705611Smyers 706*6573Sphitran static void 707*6573Sphitran power_gen_sysevent(struct power_soft_state *softsp) 708*6573Sphitran { 709*6573Sphitran nvlist_t *attr_list = NULL; 710*6573Sphitran int err; 711*6573Sphitran char pathname[MAXPATHLEN]; 712*6573Sphitran char hid[9] = "\0"; 713*6573Sphitran 714*6573Sphitran /* Allocate and build sysevent attribute list */ 715*6573Sphitran err = nvlist_alloc(&attr_list, NV_UNIQUE_NAME_TYPE, DDI_NOSLEEP); 716*6573Sphitran if (err != 0) { 717*6573Sphitran cmn_err(CE_WARN, 718*6573Sphitran "cannot allocate memory for sysevent attributes\n"); 719*6573Sphitran return; 720*6573Sphitran } 721*6573Sphitran 722*6573Sphitran #ifdef ACPI_POWER_BUTTON 723*6573Sphitran /* Only control method power button has HID */ 724*6573Sphitran if (softsp->gpe_attached) { 725*6573Sphitran (void) strlcpy(hid, "PNP0C0C", sizeof (hid)); 726*6573Sphitran } 727*6573Sphitran #endif 728*6573Sphitran 729*6573Sphitran err = nvlist_add_string(attr_list, PWRCTL_DEV_HID, hid); 730*6573Sphitran if (err != 0) { 731*6573Sphitran cmn_err(CE_WARN, 732*6573Sphitran "Failed to add attr [%s] for %s/%s event", 733*6573Sphitran PWRCTL_DEV_HID, EC_PWRCTL, ESC_PWRCTL_POWER_BUTTON); 734*6573Sphitran nvlist_free(attr_list); 735*6573Sphitran return; 736*6573Sphitran } 737*6573Sphitran 738*6573Sphitran (void) ddi_pathname(softsp->dip, pathname); 739*6573Sphitran err = nvlist_add_string(attr_list, PWRCTL_DEV_PHYS_PATH, pathname); 740*6573Sphitran if (err != 0) { 741*6573Sphitran cmn_err(CE_WARN, 742*6573Sphitran "Failed to add attr [%s] for %s/%s event", 743*6573Sphitran PWRCTL_DEV_PHYS_PATH, EC_PWRCTL, ESC_PWRCTL_POWER_BUTTON); 744*6573Sphitran nvlist_free(attr_list); 745*6573Sphitran return; 746*6573Sphitran } 747*6573Sphitran 748*6573Sphitran /* Generate/log sysevent */ 749*6573Sphitran err = ddi_log_sysevent(softsp->dip, DDI_VENDOR_SUNW, EC_PWRCTL, 750*6573Sphitran ESC_PWRCTL_POWER_BUTTON, attr_list, NULL, DDI_NOSLEEP); 751*6573Sphitran if (err != DDI_SUCCESS) { 752*6573Sphitran cmn_err(CE_WARN, 753*6573Sphitran "cannot log sysevent, err code %x\n", err); 754*6573Sphitran } 755*6573Sphitran 756*6573Sphitran nvlist_free(attr_list); 757*6573Sphitran } 758*6573Sphitran 759611Smyers /* 760611Smyers * Open the device. 761611Smyers */ 762611Smyers /*ARGSUSED*/ 763611Smyers static int 764611Smyers power_open(dev_t *devp, int openflags, int otyp, cred_t *credp) 765611Smyers { 766611Smyers struct power_soft_state *softsp; 767611Smyers int clone; 768611Smyers 769611Smyers if (otyp != OTYP_CHR) 770611Smyers return (EINVAL); 771611Smyers 772611Smyers if ((softsp = ddi_get_soft_state(power_state, power_inst)) == 773611Smyers NULL) 774611Smyers return (ENXIO); 775611Smyers 776611Smyers mutex_enter(&softsp->power_mutex); 777611Smyers for (clone = 1; clone < POWER_MAX_CLONE; clone++) 778611Smyers if (!softsp->clones[clone]) 779611Smyers break; 780611Smyers 781611Smyers if (clone == POWER_MAX_CLONE) { 782611Smyers cmn_err(CE_WARN, "power_open: No more allocation left " 783611Smyers "to create a clone minor."); 784611Smyers mutex_exit(&softsp->power_mutex); 785611Smyers return (ENXIO); 786611Smyers } 787611Smyers 788611Smyers *devp = makedevice(getmajor(*devp), (power_inst << 8) + clone); 789611Smyers softsp->clones[clone] = 1; 790611Smyers mutex_exit(&softsp->power_mutex); 791611Smyers 792611Smyers return (0); 793611Smyers } 794611Smyers 795611Smyers /* 796611Smyers * Close the device. 797611Smyers */ 798611Smyers /*ARGSUSED*/ 799611Smyers static int 800611Smyers power_close(dev_t dev, int openflags, int otyp, cred_t *credp) 801611Smyers { 802611Smyers struct power_soft_state *softsp; 803611Smyers int clone; 804611Smyers 805611Smyers if (otyp != OTYP_CHR) 806611Smyers return (EINVAL); 807611Smyers 808611Smyers if ((softsp = ddi_get_soft_state(power_state, power_inst)) == 809611Smyers NULL) 810611Smyers return (ENXIO); 811611Smyers 812611Smyers clone = POWER_MINOR_TO_CLONE(getminor(dev)); 813611Smyers mutex_enter(&softsp->power_mutex); 814611Smyers if (softsp->monitor_on == clone) 815611Smyers softsp->monitor_on = 0; 816611Smyers softsp->clones[clone] = 0; 817611Smyers mutex_exit(&softsp->power_mutex); 818611Smyers 819611Smyers return (0); 820611Smyers } 821611Smyers 822611Smyers /*ARGSUSED*/ 823611Smyers static int 824611Smyers power_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cred_p, 825611Smyers int *rval_p) 826611Smyers { 827611Smyers struct power_soft_state *softsp; 828611Smyers int clone; 829611Smyers 830611Smyers if ((softsp = ddi_get_soft_state(power_state, power_inst)) == 831611Smyers NULL) 832611Smyers return (ENXIO); 833611Smyers 834611Smyers clone = POWER_MINOR_TO_CLONE(getminor(dev)); 835611Smyers switch (cmd) { 836611Smyers case PB_BEGIN_MONITOR: 837611Smyers mutex_enter(&softsp->power_mutex); 838611Smyers if (softsp->monitor_on) { 839611Smyers mutex_exit(&softsp->power_mutex); 840611Smyers return (EBUSY); 841611Smyers } 842611Smyers softsp->monitor_on = clone; 843611Smyers mutex_exit(&softsp->power_mutex); 844611Smyers return (0); 845611Smyers 846611Smyers case PB_END_MONITOR: 847611Smyers mutex_enter(&softsp->power_mutex); 848611Smyers 849611Smyers /* 850611Smyers * If PB_END_MONITOR is called without first 851611Smyers * calling PB_BEGIN_MONITOR, an error will be 852611Smyers * returned. 853611Smyers */ 854611Smyers if (!softsp->monitor_on) { 855611Smyers mutex_exit(&softsp->power_mutex); 856611Smyers return (ENXIO); 857611Smyers } 858611Smyers 859611Smyers /* 860611Smyers * This clone is not monitoring the button. 861611Smyers */ 862611Smyers if (softsp->monitor_on != clone) { 863611Smyers mutex_exit(&softsp->power_mutex); 864611Smyers return (EINVAL); 865611Smyers } 866611Smyers softsp->monitor_on = 0; 867611Smyers mutex_exit(&softsp->power_mutex); 868611Smyers return (0); 869611Smyers 870611Smyers case PB_GET_EVENTS: 871611Smyers mutex_enter(&softsp->power_mutex); 872611Smyers if (ddi_copyout((void *)&softsp->events, (void *)arg, 873611Smyers sizeof (int), mode) != 0) { 874611Smyers mutex_exit(&softsp->power_mutex); 875611Smyers return (EFAULT); 876611Smyers } 877611Smyers 878611Smyers /* 879611Smyers * This ioctl returned the events detected since last 880611Smyers * call. Note that any application can get the events 881611Smyers * and clear the event register. 882611Smyers */ 883611Smyers softsp->events = 0; 884611Smyers mutex_exit(&softsp->power_mutex); 885611Smyers return (0); 886611Smyers 887611Smyers /* 888611Smyers * This ioctl is used by the test suite. 889611Smyers */ 890611Smyers case PB_CREATE_BUTTON_EVENT: 891611Smyers #ifdef ACPI_POWER_BUTTON 892611Smyers (UINT32)power_acpi_fixed_event((void *)softsp); 893611Smyers #else 894611Smyers if (softsp->power_regs_mapped) { 895611Smyers mutex_enter(&softsp->power_intr_mutex); 896611Smyers softsp->power_btn_ioctl = B_TRUE; 897611Smyers mutex_exit(&softsp->power_intr_mutex); 898611Smyers } 899611Smyers (void) power_high_intr((caddr_t)softsp); 900611Smyers #endif /* ACPI_POWER_BUTTON */ 901611Smyers return (0); 902611Smyers 903611Smyers default: 904611Smyers return (ENOTTY); 905611Smyers } 906611Smyers } 907611Smyers 908611Smyers /*ARGSUSED*/ 909611Smyers static int 910611Smyers power_chpoll(dev_t dev, short events, int anyyet, 911611Smyers short *reventsp, struct pollhead **phpp) 912611Smyers { 913611Smyers struct power_soft_state *softsp; 914611Smyers 915611Smyers if ((softsp = ddi_get_soft_state(power_state, power_inst)) == NULL) 916611Smyers return (ENXIO); 917611Smyers 918611Smyers mutex_enter(&softsp->power_mutex); 919611Smyers *reventsp = 0; 920611Smyers if (softsp->events) 921611Smyers *reventsp = POLLRDNORM|POLLIN; 922611Smyers else { 923611Smyers if (!anyyet) 924611Smyers *phpp = &softsp->pollhd; 925611Smyers } 926611Smyers mutex_exit(&softsp->power_mutex); 927611Smyers 928611Smyers return (0); 929611Smyers } 930611Smyers 931611Smyers static void 932611Smyers power_log_message(void) 933611Smyers { 934611Smyers struct power_soft_state *softsp; 935611Smyers 936611Smyers if ((softsp = ddi_get_soft_state(power_state, power_inst)) == NULL) { 937611Smyers cmn_err(CE_WARN, "Failed to get internal state!"); 938611Smyers return; 939611Smyers } 940611Smyers 941611Smyers mutex_enter(&softsp->power_mutex); 942611Smyers softsp->shutdown_pending = 0; 943611Smyers cmn_err(CE_WARN, "Failed to shut down the system!"); 944611Smyers mutex_exit(&softsp->power_mutex); 945611Smyers } 946611Smyers 947611Smyers #ifdef ACPI_POWER_BUTTON 948611Smyers /* 949611Smyers * Given a handle to a device object, locate a _PRW object 950611Smyers * if present and fetch the GPE info for this device object 951611Smyers */ 952611Smyers static ACPI_STATUS 953611Smyers power_get_prw_gpe(ACPI_HANDLE dev, ACPI_HANDLE *gpe_dev, UINT32 *gpe_num) 954611Smyers { 955611Smyers ACPI_BUFFER buf; 956611Smyers ACPI_STATUS status; 957611Smyers ACPI_HANDLE prw; 958611Smyers ACPI_OBJECT *gpe; 959611Smyers 960611Smyers /* 961611Smyers * Evaluate _PRW if present 962611Smyers */ 963611Smyers status = AcpiGetHandle(dev, "_PRW", &prw); 964611Smyers if (status != AE_OK) 965611Smyers return (status); 966611Smyers buf.Length = ACPI_ALLOCATE_BUFFER; 967611Smyers status = AcpiEvaluateObjectTyped(prw, NULL, NULL, &buf, 968611Smyers ACPI_TYPE_PACKAGE); 969611Smyers if (status != AE_OK) 970611Smyers return (status); 971611Smyers 972611Smyers /* 973611Smyers * Sanity-check the package; need at least two elements 974611Smyers */ 975611Smyers status = AE_ERROR; 976611Smyers if (((ACPI_OBJECT *)buf.Pointer)->Package.Count < 2) 977611Smyers goto done; 978611Smyers 979611Smyers gpe = &((ACPI_OBJECT *)buf.Pointer)->Package.Elements[0]; 980611Smyers if (gpe->Type == ACPI_TYPE_INTEGER) { 981611Smyers *gpe_dev = NULL; 982611Smyers *gpe_num = gpe->Integer.Value; 983611Smyers status = AE_OK; 984611Smyers } else if (gpe->Type == ACPI_TYPE_PACKAGE) { 985611Smyers if ((gpe->Package.Count != 2) || 986611Smyers (gpe->Package.Elements[0].Type != ACPI_TYPE_DEVICE) || 987611Smyers (gpe->Package.Elements[1].Type != ACPI_TYPE_INTEGER)) 988611Smyers goto done; 989611Smyers *gpe_dev = gpe->Package.Elements[0].Reference.Handle; 990611Smyers *gpe_num = gpe->Package.Elements[1].Integer.Value; 991611Smyers status = AE_OK; 992611Smyers } 993611Smyers 994611Smyers done: 995611Smyers AcpiOsFree(buf.Pointer); 996611Smyers return (status); 997611Smyers } 998611Smyers 999611Smyers 1000611Smyers /* 1001611Smyers * 1002611Smyers */ 1003622Smyers /*ARGSUSED*/ 1004611Smyers static ACPI_STATUS 1005611Smyers acpi_device(ACPI_HANDLE obj, UINT32 nesting, void *context, void **rv) 1006611Smyers { 1007622Smyers 1008611Smyers *((ACPI_HANDLE *)context) = obj; 1009611Smyers return (AE_OK); 1010611Smyers } 1011611Smyers 1012611Smyers /* 1013611Smyers * 1014611Smyers */ 1015611Smyers static ACPI_HANDLE 1016611Smyers probe_acpi_pwrbutton() 1017611Smyers { 1018611Smyers ACPI_HANDLE obj = NULL; 1019611Smyers 1020622Smyers (void) AcpiGetDevices("PNP0C0C", acpi_device, (void *)&obj, NULL); 1021611Smyers return (obj); 1022611Smyers } 1023611Smyers 1024611Smyers static UINT32 1025611Smyers power_acpi_fixed_event(void *ctx) 1026611Smyers { 1027611Smyers 1028611Smyers mutex_enter(&((struct power_soft_state *)ctx)->power_intr_mutex); 1029611Smyers power_button_pressed++; 1030611Smyers mutex_exit(&((struct power_soft_state *)ctx)->power_intr_mutex); 1031611Smyers 1032611Smyers /* post softint to issue timeout for power button action */ 1033611Smyers if (((struct power_soft_state *)ctx)->softintr_id != NULL) 1034611Smyers ddi_trigger_softintr( 1035611Smyers ((struct power_soft_state *)ctx)->softintr_id); 1036611Smyers 1037611Smyers return (AE_OK); 1038611Smyers } 1039611Smyers 1040622Smyers /*ARGSUSED*/ 1041611Smyers static void 1042611Smyers power_acpi_notify_event(ACPI_HANDLE obj, UINT32 val, void *ctx) 1043611Smyers { 1044611Smyers if (val == 0x80) 1045622Smyers (void) power_acpi_fixed_event(ctx); 1046611Smyers } 1047611Smyers 1048611Smyers /* 1049611Smyers * 1050611Smyers */ 1051611Smyers static int 1052611Smyers power_probe_method_button(struct power_soft_state *softsp) 1053611Smyers { 1054611Smyers ACPI_HANDLE button_obj; 1055611Smyers UINT32 gpe_num; 1056611Smyers ACPI_HANDLE gpe_dev; 1057611Smyers 1058611Smyers button_obj = probe_acpi_pwrbutton(); 1059611Smyers softsp->button_obj = button_obj; /* remember obj */ 1060611Smyers if ((button_obj != NULL) && 1061611Smyers (power_get_prw_gpe(button_obj, &gpe_dev, &gpe_num) == AE_OK) && 1062611Smyers (AcpiSetGpeType(gpe_dev, gpe_num, ACPI_GPE_TYPE_WAKE_RUN) == 1063611Smyers AE_OK) && 1064611Smyers (AcpiEnableGpe(gpe_dev, gpe_num, ACPI_NOT_ISR) == AE_OK) && 1065611Smyers (AcpiInstallNotifyHandler(button_obj, ACPI_DEVICE_NOTIFY, 1066611Smyers power_acpi_notify_event, (void*)softsp) == AE_OK)) 1067611Smyers return (1); 1068611Smyers return (0); 1069611Smyers } 1070611Smyers 1071611Smyers /* 1072611Smyers * 1073611Smyers */ 1074611Smyers static int 1075611Smyers power_probe_fixed_button(struct power_soft_state *softsp) 1076611Smyers { 1077611Smyers FADT_DESCRIPTOR *fadt; 1078611Smyers 1079611Smyers if (AcpiGetFirmwareTable(FADT_SIG, 1, ACPI_LOGICAL_ADDRESSING, 1080611Smyers (ACPI_TABLE_HEADER **) &fadt) != AE_OK) 1081611Smyers return (0); 1082611Smyers 1083611Smyers if (!fadt->PwrButton) { 1084611Smyers if (AcpiInstallFixedEventHandler(ACPI_EVENT_POWER_BUTTON, 1085611Smyers power_acpi_fixed_event, (void *)softsp) == AE_OK) 1086611Smyers return (1); 1087611Smyers } 1088611Smyers return (0); 1089611Smyers } 1090611Smyers 1091611Smyers 1092611Smyers /* 1093611Smyers * 1094611Smyers */ 1095611Smyers static int 1096611Smyers power_attach_acpi(struct power_soft_state *softsp) 1097611Smyers { 1098611Smyers 1099611Smyers /* 1100611Smyers * If we've attached anything already, return an error 1101611Smyers */ 1102611Smyers if ((softsp->gpe_attached) || (softsp->fixed_attached)) 1103611Smyers return (DDI_FAILURE); 1104611Smyers 1105611Smyers /* 1106611Smyers * attempt to attach both a fixed-event handler and a GPE 1107611Smyers * handler; remember what we got 1108611Smyers */ 1109611Smyers softsp->fixed_attached = (power_probe_fixed_button(softsp) != 0); 1110611Smyers softsp->gpe_attached = (power_probe_method_button(softsp) != 0); 1111611Smyers 1112611Smyers /* 1113611Smyers * If we've attached anything now, return success 1114611Smyers */ 1115611Smyers if ((softsp->gpe_attached) || (softsp->fixed_attached)) 1116611Smyers return (DDI_SUCCESS); 1117611Smyers 1118611Smyers return (DDI_FAILURE); 1119611Smyers } 1120611Smyers 1121611Smyers /* 1122611Smyers * 1123611Smyers */ 1124611Smyers static void 1125611Smyers power_detach_acpi(struct power_soft_state *softsp) 1126611Smyers { 1127611Smyers if (softsp->gpe_attached) { 1128611Smyers if (AcpiRemoveNotifyHandler(softsp->button_obj, 1129611Smyers ACPI_DEVICE_NOTIFY, power_acpi_notify_event) != AE_OK) 1130611Smyers cmn_err(CE_WARN, "!power: failed to remove Notify" 1131611Smyers " handler"); 1132611Smyers } 1133611Smyers 1134611Smyers if (softsp->fixed_attached) { 1135611Smyers if (AcpiRemoveFixedEventHandler(ACPI_EVENT_POWER_BUTTON, 1136611Smyers power_acpi_fixed_event) != AE_OK) 1137611Smyers cmn_err(CE_WARN, "!power: failed to remove Power" 1138611Smyers " Button handler"); 1139611Smyers } 1140611Smyers } 1141611Smyers 1142611Smyers #else 1143611Smyers /* 1144920Sjbeloro * Code for platforms that have EPIC processor for processing power 1145920Sjbeloro * button interrupts. 1146920Sjbeloro */ 1147920Sjbeloro static int 1148920Sjbeloro power_setup_epic_regs(dev_info_t *dip, struct power_soft_state *softsp) 1149920Sjbeloro { 1150920Sjbeloro ddi_device_acc_attr_t attr; 1151920Sjbeloro uint8_t *reg_base; 1152920Sjbeloro 1153920Sjbeloro attr.devacc_attr_version = DDI_DEVICE_ATTR_V0; 1154920Sjbeloro attr.devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC; 1155920Sjbeloro attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC; 1156920Sjbeloro if (ddi_regs_map_setup(dip, 0, (caddr_t *)®_base, 1157*6573Sphitran EPIC_REGS_OFFSET, EPIC_REGS_LEN, &attr, 1158*6573Sphitran &softsp->power_rhandle) != DDI_SUCCESS) { 1159920Sjbeloro return (DDI_FAILURE); 1160920Sjbeloro } 1161920Sjbeloro 1162920Sjbeloro softsp->power_btn_reg = reg_base; 1163920Sjbeloro softsp->power_regs_mapped = B_TRUE; 1164920Sjbeloro 1165920Sjbeloro /* Clear power button interrupt first */ 1166920Sjbeloro EPIC_WR(softsp->power_rhandle, softsp->power_btn_reg, 1167*6573Sphitran EPIC_ATOM_INTR_CLEAR); 1168920Sjbeloro 1169920Sjbeloro /* Enable EPIC interrupt for power button single press event */ 1170920Sjbeloro EPIC_WR(softsp->power_rhandle, softsp->power_btn_reg, 1171*6573Sphitran EPIC_ATOM_INTR_ENABLE); 1172920Sjbeloro 1173920Sjbeloro /* 1174920Sjbeloro * At this point, EPIC interrupt processing is fully initialised. 1175920Sjbeloro */ 1176920Sjbeloro hasEPIC = B_TRUE; 1177920Sjbeloro return (DDI_SUCCESS); 1178920Sjbeloro } 1179920Sjbeloro 1180920Sjbeloro /* 1181920Sjbeloro * 1182611Smyers * power button register definitions for acpi register on m1535d 1183611Smyers */ 1184611Smyers #define M1535D_PWR_BTN_REG_01 0x1 1185611Smyers #define M1535D_PWR_BTN_EVENT_FLAG 0x1 1186611Smyers 1187611Smyers static int 1188611Smyers power_setup_m1535_regs(dev_info_t *dip, struct power_soft_state *softsp) 1189611Smyers { 1190611Smyers ddi_device_acc_attr_t attr; 1191611Smyers uint8_t *reg_base; 1192611Smyers 1193611Smyers attr.devacc_attr_version = DDI_DEVICE_ATTR_V0; 1194611Smyers attr.devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC; 1195611Smyers attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC; 1196611Smyers if (ddi_regs_map_setup(dip, 0, (caddr_t *)®_base, 0, 0, &attr, 1197611Smyers &softsp->power_rhandle) != DDI_SUCCESS) { 1198611Smyers return (DDI_FAILURE); 1199611Smyers } 1200611Smyers softsp->power_btn_reg = ®_base[M1535D_PWR_BTN_REG_01]; 1201611Smyers softsp->power_btn_bit = M1535D_PWR_BTN_EVENT_FLAG; 1202611Smyers softsp->power_regs_mapped = B_TRUE; 1203611Smyers return (DDI_SUCCESS); 1204611Smyers } 1205611Smyers 1206611Smyers /* 1207920Sjbeloro * MBC Fire/SSI Interrupt Status Register definitions 1208920Sjbeloro */ 1209920Sjbeloro #define FIRE_SSI_ISR 0x0 1210920Sjbeloro #define FIRE_SSI_INTR_ENA 0x8 1211920Sjbeloro #define FIRE_SSI_SHUTDOWN_REQ 0x4 1212920Sjbeloro 1213920Sjbeloro static int 1214920Sjbeloro power_setup_mbc_regs(dev_info_t *dip, struct power_soft_state *softsp) 1215920Sjbeloro { 1216920Sjbeloro ddi_device_acc_attr_t attr; 1217920Sjbeloro uint8_t *reg_base; 1218920Sjbeloro ddi_acc_handle_t hdl; 1219920Sjbeloro uint8_t reg; 1220920Sjbeloro 1221920Sjbeloro attr.devacc_attr_version = DDI_DEVICE_ATTR_V0; 1222920Sjbeloro attr.devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC; 1223920Sjbeloro attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC; 1224920Sjbeloro if (ddi_regs_map_setup(dip, 0, (caddr_t *)®_base, 0, 0, &attr, 1225*6573Sphitran &softsp->power_rhandle) != DDI_SUCCESS) { 1226920Sjbeloro return (DDI_FAILURE); 1227920Sjbeloro } 1228920Sjbeloro softsp->power_btn_reg = ®_base[FIRE_SSI_ISR]; 1229920Sjbeloro softsp->power_btn_bit = FIRE_SSI_SHUTDOWN_REQ; 12301877Sgk73471 hdl = softsp->power_rhandle; 12311877Sgk73471 /* 12321877Sgk73471 * Clear MBC Fire Power Button interrupt, if set. 12331877Sgk73471 */ 12341877Sgk73471 reg = ddi_get8(hdl, softsp->power_btn_reg); 12351877Sgk73471 if (reg & softsp->power_btn_bit) { 12361877Sgk73471 reg &= softsp->power_btn_bit; 12371877Sgk73471 ddi_put8(hdl, softsp->power_btn_reg, reg); 12381877Sgk73471 (void) ddi_get8(hdl, softsp->power_btn_reg); 12391877Sgk73471 } 1240920Sjbeloro /* 1241920Sjbeloro * Enable MBC Fire Power Button interrupt. 1242920Sjbeloro */ 1243920Sjbeloro reg = ddi_get8(hdl, ®_base[FIRE_SSI_INTR_ENA]); 1244920Sjbeloro reg |= FIRE_SSI_SHUTDOWN_REQ; 1245920Sjbeloro ddi_put8(hdl, ®_base[FIRE_SSI_INTR_ENA], reg); 1246920Sjbeloro 1247920Sjbeloro softsp->power_regs_mapped = B_TRUE; 1248920Sjbeloro 1249920Sjbeloro return (DDI_SUCCESS); 1250920Sjbeloro } 1251920Sjbeloro 1252920Sjbeloro /* 1253611Smyers * Setup register map for the power button 12541084Sjroberts * NOTE:- we only map registers for platforms if 12551084Sjroberts * the OBP power device has any of the following 12561084Sjroberts * properties: 12571084Sjroberts * 12581084Sjroberts * a) Boston: power-device-type set to "SUNW,mbc" 12591084Sjroberts * b) Seattle: power-device-type set to "SUNW,pic18lf65j10" 12601084Sjroberts * c) Chalupa: compatible set to "ali1535d+-power" 12611084Sjroberts * 12621084Sjroberts * Cases (a) and (b) are defined in FWARC 2005/687. 12631084Sjroberts * If none of the above conditions are true, then we 12641084Sjroberts * do not need to map in any registers, and this 12651084Sjroberts * function can simply return DDI_SUCCESS. 1266611Smyers */ 1267611Smyers static int 1268611Smyers power_setup_regs(struct power_soft_state *softsp) 1269611Smyers { 1270611Smyers char *binding_name; 12711084Sjroberts char *power_type = NULL; 12721084Sjroberts int retval = DDI_SUCCESS; 1273611Smyers 1274611Smyers softsp->power_regs_mapped = B_FALSE; 1275611Smyers softsp->power_btn_ioctl = B_FALSE; 1276611Smyers binding_name = ddi_binding_name(softsp->dip); 12771084Sjroberts if (ddi_prop_lookup_string(DDI_DEV_T_ANY, softsp->dip, 12781084Sjroberts DDI_PROP_DONTPASS, POWER_DEVICE_TYPE, 12791084Sjroberts &power_type) == DDI_PROP_SUCCESS) { 12801084Sjroberts if (strcmp(power_type, "SUNW,mbc") == 0) { 12811084Sjroberts retval = power_setup_mbc_regs(softsp->dip, softsp); 12821084Sjroberts } else if (strcmp(power_type, "SUNW,pic18lf65j10") == 0) { 12831084Sjroberts retval = power_setup_epic_regs(softsp->dip, softsp); 12841084Sjroberts } else { 12851084Sjroberts cmn_err(CE_WARN, "unexpected power-device-type: %s\n", 12861084Sjroberts power_type); 12871084Sjroberts retval = DDI_FAILURE; 12881084Sjroberts } 12891084Sjroberts ddi_prop_free(power_type); 12901084Sjroberts } else if (strcmp(binding_name, "ali1535d+-power") == 0) { 12911084Sjroberts retval = power_setup_m1535_regs(softsp->dip, softsp); 12921084Sjroberts } 1293611Smyers 1294920Sjbeloro /* 12951084Sjroberts * If power-device-type does not exist AND the binding name is not 12961084Sjroberts * "ali1535d+-power", that means there is no additional HW and hence 12971084Sjroberts * no extra processing is necessary. In that case, retval should still 12981084Sjroberts * be set to its initial value of DDI_SUCCESS. 1299920Sjbeloro */ 13001084Sjroberts return (retval); 1301611Smyers } 1302611Smyers 1303611Smyers static void 1304611Smyers power_free_regs(struct power_soft_state *softsp) 1305611Smyers { 1306611Smyers if (softsp->power_regs_mapped) 1307611Smyers ddi_regs_map_free(&softsp->power_rhandle); 1308611Smyers } 1309611Smyers #endif /* ACPI_POWER_BUTTON */ 1310