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