1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 /* 30 * Power Button Driver 31 * 32 * This driver handles interrupt generated by the power button on 33 * platforms with "power" device node which has "button" property. 34 * Currently, these platforms are: 35 * 36 * ACPI-enabled x86/x64 platforms 37 * Ultra-5_10, Ultra-80, Sun-Blade-100, Sun-Blade-150, 38 * Sun-Blade-1500, Sun-Blade-2500, 39 * Sun-Fire-V210, Sun-Fire-V240, Netra-240 40 * 41 * Only one instance is allowed to attach. In order to know when 42 * an application that has opened the device is going away, a new 43 * minor clone is created for each open(9E) request. There are 44 * allocations for creating minor clones between 1 and 255. The ioctl 45 * interface is defined by pbio(7I) and approved as part of 46 * PSARC/1999/393 case. 47 */ 48 49 #include <sys/types.h> 50 #include <sys/conf.h> 51 #include <sys/ddi.h> 52 #include <sys/sunddi.h> 53 #include <sys/ddi_impldefs.h> 54 #include <sys/cmn_err.h> 55 #include <sys/errno.h> 56 #include <sys/modctl.h> 57 #include <sys/machsystm.h> 58 #include <sys/open.h> 59 #include <sys/stat.h> 60 #include <sys/poll.h> 61 #include <sys/pbio.h> 62 #ifdef ACPI_POWER_BUTTON 63 #include <sys/acpi/acpi.h> 64 #include <sys/acpica.h> 65 #endif /* ACPI_POWER_BUTTON */ 66 67 /* 68 * Maximum number of clone minors that is allowed. This value 69 * is defined relatively low to save memory. 70 */ 71 #define POWER_MAX_CLONE 256 72 73 /* 74 * Minor number is instance << 8 + clone minor from range 1-255; clone 0 75 * is reserved for "original" minor. 76 */ 77 #define POWER_MINOR_TO_CLONE(minor) ((minor) & (POWER_MAX_CLONE - 1)) 78 79 /* 80 * Power Button Abort Delay 81 */ 82 #define ABORT_INCREMENT_DELAY 10 83 84 /* 85 * Driver global variables 86 */ 87 static void *power_state; 88 static int power_inst = -1; 89 90 static hrtime_t power_button_debounce = NANOSEC/MILLISEC*10; 91 static hrtime_t power_button_abort_interval = 1.5 * NANOSEC; 92 static int power_button_abort_presses = 3; 93 static int power_button_abort_enable = 1; 94 static int power_button_enable = 1; 95 96 static int power_button_pressed = 0; 97 static int power_button_cancel = 0; 98 static int power_button_timeouts = 0; 99 static int timeout_cancel = 0; 100 static int additional_presses = 0; 101 102 /* 103 * Function prototypes 104 */ 105 static int power_attach(dev_info_t *, ddi_attach_cmd_t); 106 static int power_detach(dev_info_t *, ddi_detach_cmd_t); 107 static int power_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **); 108 static int power_open(dev_t *, int, int, cred_t *); 109 static int power_close(dev_t, int, int, cred_t *); 110 static int power_ioctl(dev_t, int, intptr_t, int, cred_t *, int *); 111 static int power_chpoll(dev_t, short, int, short *, struct pollhead **); 112 #ifndef ACPI_POWER_BUTTON 113 static uint_t power_high_intr(caddr_t); 114 #endif 115 static uint_t power_soft_intr(caddr_t); 116 static uint_t power_issue_shutdown(caddr_t); 117 static void power_timeout(caddr_t); 118 static void power_log_message(void); 119 120 /* 121 * Structure used in the driver 122 */ 123 struct power_soft_state { 124 dev_info_t *dip; /* device info pointer */ 125 kmutex_t power_mutex; /* mutex lock */ 126 kmutex_t power_intr_mutex; /* interrupt mutex lock */ 127 ddi_iblock_cookie_t soft_iblock_cookie; /* holds interrupt cookie */ 128 ddi_iblock_cookie_t high_iblock_cookie; /* holds interrupt cookie */ 129 ddi_softintr_t softintr_id; /* soft interrupt id */ 130 uchar_t clones[POWER_MAX_CLONE]; /* array of minor clones */ 131 int monitor_on; /* clone monitoring the button event */ 132 /* clone 0 indicates no one is */ 133 /* monitoring the button event */ 134 pollhead_t pollhd; /* poll head struct */ 135 int events; /* bit map of occured events */ 136 int shutdown_pending; /* system shutdown in progress */ 137 #ifdef ACPI_POWER_BUTTON 138 boolean_t fixed_attached; /* true means fixed is attached */ 139 boolean_t gpe_attached; /* true means GPE is attached */ 140 ACPI_HANDLE button_obj; /* handle to device power button */ 141 #else 142 ddi_acc_handle_t power_rhandle; /* power button register handle */ 143 uint8_t *power_btn_reg; /* power button register address */ 144 uint8_t power_btn_bit; /* power button register bit */ 145 boolean_t power_regs_mapped; /* flag to tell if regs mapped */ 146 boolean_t power_btn_ioctl; /* flag to specify ioctl request */ 147 #endif 148 }; 149 150 #ifdef ACPI_POWER_BUTTON 151 static int power_attach_acpi(struct power_soft_state *softsp); 152 static void power_detach_acpi(struct power_soft_state *softsp); 153 static UINT32 power_acpi_fixed_event(void *ctx); 154 #else 155 static int power_setup_regs(struct power_soft_state *softsp); 156 static void power_free_regs(struct power_soft_state *softsp); 157 #endif /* ACPI_POWER_BUTTON */ 158 159 /* 160 * Configuration data structures 161 */ 162 static struct cb_ops power_cb_ops = { 163 power_open, /* open */ 164 power_close, /* close */ 165 nodev, /* strategy */ 166 nodev, /* print */ 167 nodev, /* dump */ 168 nodev, /* read */ 169 nodev, /* write */ 170 power_ioctl, /* ioctl */ 171 nodev, /* devmap */ 172 nodev, /* mmap */ 173 nodev, /* segmap */ 174 power_chpoll, /* poll */ 175 ddi_prop_op, /* cb_prop_op */ 176 NULL, /* streamtab */ 177 D_MP | D_NEW, /* Driver compatibility flag */ 178 CB_REV, /* rev */ 179 nodev, /* cb_aread */ 180 nodev /* cb_awrite */ 181 }; 182 183 static struct dev_ops power_ops = { 184 DEVO_REV, /* devo_rev, */ 185 0, /* refcnt */ 186 power_getinfo, /* getinfo */ 187 nulldev, /* identify */ 188 nulldev, /* probe */ 189 power_attach, /* attach */ 190 power_detach, /* detach */ 191 nodev, /* reset */ 192 &power_cb_ops, /* cb_ops */ 193 (struct bus_ops *)NULL, /* bus_ops */ 194 NULL /* power */ 195 }; 196 197 static struct modldrv modldrv = { 198 &mod_driverops, /* Type of module. This one is a driver */ 199 "power button driver v%I%", /* name of module */ 200 &power_ops, /* driver ops */ 201 }; 202 203 static struct modlinkage modlinkage = { 204 MODREV_1, 205 (void *)&modldrv, 206 NULL 207 }; 208 209 /* 210 * These are the module initialization routines. 211 */ 212 213 int 214 _init(void) 215 { 216 int error; 217 218 if ((error = ddi_soft_state_init(&power_state, 219 sizeof (struct power_soft_state), 0)) != 0) 220 return (error); 221 222 if ((error = mod_install(&modlinkage)) != 0) 223 ddi_soft_state_fini(&power_state); 224 225 return (error); 226 } 227 228 int 229 _fini(void) 230 { 231 int error; 232 233 if ((error = mod_remove(&modlinkage)) == 0) 234 ddi_soft_state_fini(&power_state); 235 236 return (error); 237 } 238 239 int 240 _info(struct modinfo *modinfop) 241 { 242 return (mod_info(&modlinkage, modinfop)); 243 } 244 245 /*ARGSUSED*/ 246 static int 247 power_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, 248 void **result) 249 { 250 struct power_soft_state *softsp; 251 252 if (power_inst == -1) 253 return (DDI_FAILURE); 254 255 switch (infocmd) { 256 case DDI_INFO_DEVT2DEVINFO: 257 if ((softsp = ddi_get_soft_state(power_state, power_inst)) 258 == NULL) 259 return (DDI_FAILURE); 260 *result = (void *)softsp->dip; 261 return (DDI_SUCCESS); 262 263 case DDI_INFO_DEVT2INSTANCE: 264 *result = (void *)(uintptr_t)power_inst; 265 return (DDI_SUCCESS); 266 267 default: 268 return (DDI_FAILURE); 269 } 270 } 271 272 static int 273 power_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 274 { 275 struct power_soft_state *softsp; 276 277 switch (cmd) { 278 case DDI_ATTACH: 279 break; 280 case DDI_RESUME: 281 return (DDI_SUCCESS); 282 default: 283 return (DDI_FAILURE); 284 } 285 286 /* 287 * If the power node doesn't have "button" property, quietly 288 * fail to attach. 289 */ 290 if (ddi_prop_exists(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 291 "button") == 0) 292 return (DDI_FAILURE); 293 294 if (power_inst != -1) 295 return (DDI_FAILURE); 296 297 power_inst = ddi_get_instance(dip); 298 299 if (ddi_soft_state_zalloc(power_state, power_inst) != DDI_SUCCESS) 300 return (DDI_FAILURE); 301 302 if (ddi_create_minor_node(dip, "power_button", S_IFCHR, 303 (power_inst << 8) + 0, "ddi_power_button", 0) != DDI_SUCCESS) 304 return (DDI_FAILURE); 305 306 softsp = ddi_get_soft_state(power_state, power_inst); 307 softsp->dip = dip; 308 309 #ifdef ACPI_POWER_BUTTON 310 (void) power_attach_acpi(softsp); 311 #else 312 if (power_setup_regs(softsp) != DDI_SUCCESS) { 313 cmn_err(CE_WARN, "power_attach: failed to setup registers"); 314 goto error; 315 } 316 317 if (ddi_get_iblock_cookie(dip, 0, 318 &softsp->high_iblock_cookie) != DDI_SUCCESS) { 319 cmn_err(CE_WARN, "power_attach: ddi_get_soft_iblock_cookie " 320 "failed."); 321 goto error; 322 } 323 mutex_init(&softsp->power_intr_mutex, NULL, MUTEX_DRIVER, 324 softsp->high_iblock_cookie); 325 326 if (ddi_add_intr(dip, 0, &softsp->high_iblock_cookie, NULL, 327 power_high_intr, (caddr_t)softsp) != DDI_SUCCESS) { 328 cmn_err(CE_WARN, "power_attach: failed to add high-level " 329 " interrupt handler."); 330 mutex_destroy(&softsp->power_intr_mutex); 331 goto error; 332 } 333 #endif /* ACPI_POWER_BUTTON */ 334 335 if (ddi_get_soft_iblock_cookie(dip, DDI_SOFTINT_LOW, 336 &softsp->soft_iblock_cookie) != DDI_SUCCESS) { 337 cmn_err(CE_WARN, "power_attach: ddi_get_soft_iblock_cookie " 338 "failed."); 339 mutex_destroy(&softsp->power_intr_mutex); 340 ddi_remove_intr(dip, 0, NULL); 341 goto error; 342 } 343 344 mutex_init(&softsp->power_mutex, NULL, MUTEX_DRIVER, 345 (void *)softsp->soft_iblock_cookie); 346 347 if (ddi_add_softintr(dip, DDI_SOFTINT_LOW, &softsp->softintr_id, 348 NULL, NULL, power_soft_intr, (caddr_t)softsp) != DDI_SUCCESS) { 349 cmn_err(CE_WARN, "power_attach: failed to add soft " 350 "interrupt handler."); 351 mutex_destroy(&softsp->power_mutex); 352 mutex_destroy(&softsp->power_intr_mutex); 353 ddi_remove_intr(dip, 0, NULL); 354 goto error; 355 } 356 357 ddi_report_dev(dip); 358 359 return (DDI_SUCCESS); 360 361 error: 362 #ifdef ACPI_POWER_BUTTON 363 /* 364 * detach ACPI power button 365 */ 366 power_detach_acpi(softsp); 367 #else 368 power_free_regs(softsp); 369 #endif /* ACPI_POWER_BUTTON */ 370 ddi_remove_minor_node(dip, "power_button"); 371 ddi_soft_state_free(power_state, power_inst); 372 return (DDI_FAILURE); 373 } 374 375 /*ARGSUSED*/ 376 /* 377 * This driver doesn't detach. 378 */ 379 static int 380 power_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 381 { 382 /* 383 * Since the "power" node has "reg" property, as part of 384 * the suspend operation, detach(9E) entry point is called. 385 * There is no state to save, since this register is used 386 * by OBP to power off the system and the state of the 387 * power off is preserved by hardware. 388 */ 389 return ((cmd == DDI_SUSPEND) ? DDI_SUCCESS : 390 DDI_FAILURE); 391 } 392 393 #ifndef ACPI_POWER_BUTTON 394 /* 395 * Handler for the high-level interrupt. 396 */ 397 static uint_t 398 power_high_intr(caddr_t arg) 399 { 400 struct power_soft_state *softsp = (struct power_soft_state *)arg; 401 ddi_acc_handle_t hdl = softsp->power_rhandle; 402 uint8_t reg; 403 404 hrtime_t tstamp; 405 static hrtime_t o_tstamp = 0; 406 static hrtime_t power_button_tstamp = 0; 407 static int power_button_cnt; 408 409 if (softsp->power_regs_mapped) { 410 mutex_enter(&softsp->power_intr_mutex); 411 reg = ddi_get8(hdl, softsp->power_btn_reg); 412 if (reg & softsp->power_btn_bit) { 413 reg &= softsp->power_btn_bit; 414 ddi_put8(hdl, softsp->power_btn_reg, reg); 415 (void) ddi_get8(hdl, softsp->power_btn_reg); 416 } else { 417 if (!softsp->power_btn_ioctl) { 418 mutex_exit(&softsp->power_intr_mutex); 419 return (DDI_INTR_CLAIMED); 420 } 421 softsp->power_btn_ioctl = B_FALSE; 422 } 423 mutex_exit(&softsp->power_intr_mutex); 424 } 425 426 tstamp = gethrtime(); 427 428 /* need to deal with power button debounce */ 429 if (o_tstamp && (tstamp - o_tstamp) < power_button_debounce) { 430 o_tstamp = tstamp; 431 return (DDI_INTR_CLAIMED); 432 } 433 o_tstamp = tstamp; 434 435 power_button_cnt++; 436 437 mutex_enter(&softsp->power_intr_mutex); 438 power_button_pressed++; 439 mutex_exit(&softsp->power_intr_mutex); 440 441 /* 442 * If power button abort is enabled and power button was pressed 443 * power_button_abort_presses times within power_button_abort_interval 444 * then call abort_sequence_enter(); 445 */ 446 if (power_button_abort_enable) { 447 if (power_button_abort_presses == 1 || 448 tstamp < (power_button_tstamp + 449 power_button_abort_interval)) { 450 if (power_button_cnt == power_button_abort_presses) { 451 mutex_enter(&softsp->power_intr_mutex); 452 power_button_cancel += power_button_timeouts; 453 power_button_pressed = 0; 454 mutex_exit(&softsp->power_intr_mutex); 455 power_button_cnt = 0; 456 abort_sequence_enter("Power Button Abort"); 457 return (DDI_INTR_CLAIMED); 458 } 459 } else { 460 power_button_cnt = 1; 461 power_button_tstamp = tstamp; 462 } 463 } 464 465 if (!power_button_enable) 466 return (DDI_INTR_CLAIMED); 467 468 /* post softint to issue timeout for power button action */ 469 if (softsp->softintr_id != NULL) 470 ddi_trigger_softintr(softsp->softintr_id); 471 472 return (DDI_INTR_CLAIMED); 473 } 474 #endif /* ifndef ACPI_POWER_BUTTON */ 475 476 /* 477 * Handle the softints.... 478 * 479 * If only one softint is posted for several button presses, record 480 * the number of additional presses just incase this was actually not quite 481 * an Abort sequence so that we can log this event later. 482 * 483 * Issue a timeout with a duration being a fraction larger than 484 * the specified Abort interval inorder to perform a power down if required. 485 */ 486 static uint_t 487 power_soft_intr(caddr_t arg) 488 { 489 struct power_soft_state *softsp = (struct power_soft_state *)arg; 490 491 if (!power_button_abort_enable) 492 return (power_issue_shutdown(arg)); 493 494 mutex_enter(&softsp->power_intr_mutex); 495 if (!power_button_pressed) { 496 mutex_exit(&softsp->power_intr_mutex); 497 return (DDI_INTR_CLAIMED); 498 } 499 500 /* 501 * Schedule a timeout to do the necessary 502 * work for shutdown, only one timeout for 503 * n presses if power button was pressed 504 * more than once before softint fired 505 */ 506 if (power_button_pressed > 1) 507 additional_presses += power_button_pressed - 1; 508 509 timeout_cancel = 0; 510 power_button_pressed = 0; 511 power_button_timeouts++; 512 mutex_exit(&softsp->power_intr_mutex); 513 (void) timeout((void(*)(void *))power_timeout, 514 softsp, NSEC_TO_TICK(power_button_abort_interval) + 515 ABORT_INCREMENT_DELAY); 516 517 return (DDI_INTR_CLAIMED); 518 } 519 520 /* 521 * Upon receiving a timeout the following is determined: 522 * 523 * If an Abort sequence was issued, then we cancel all outstanding timeouts 524 * and additional presses prior to the Abort sequence. 525 * 526 * If we had multiple timeouts issued and the abort sequence was not met, 527 * then we had more than one button press to power down the machine. We 528 * were probably trying to issue an abort. So log a message indicating this 529 * and cancel all outstanding timeouts. 530 * 531 * If we had just one timeout and the abort sequence was not met then 532 * we really did want to power down the machine, so call power_issue_shutdown() 533 * to do the work and schedule a power down 534 */ 535 static void 536 power_timeout(caddr_t arg) 537 { 538 struct power_soft_state *softsp = (struct power_soft_state *)arg; 539 static int first = 0; 540 541 /* 542 * Abort was generated cancel all outstanding power 543 * button timeouts 544 */ 545 mutex_enter(&softsp->power_intr_mutex); 546 if (power_button_cancel) { 547 power_button_cancel--; 548 power_button_timeouts--; 549 if (!first) { 550 first++; 551 additional_presses = 0; 552 } 553 mutex_exit(&softsp->power_intr_mutex); 554 return; 555 } 556 first = 0; 557 558 /* 559 * We get here if the timeout(s) have fired and they were 560 * not issued prior to an abort. 561 * 562 * If we had more than one press in the interval we were 563 * probably trying to issue an abort, but didnt press the 564 * required number within the interval. Hence cancel all 565 * timeouts and do not continue towards shutdown. 566 */ 567 if (!timeout_cancel) { 568 timeout_cancel = power_button_timeouts + 569 additional_presses; 570 571 power_button_timeouts--; 572 if (!power_button_timeouts) 573 additional_presses = 0; 574 575 if (timeout_cancel > 1) { 576 mutex_exit(&softsp->power_intr_mutex); 577 cmn_err(CE_NOTE, "Power Button pressed " 578 "%d times, cancelling all requests", 579 timeout_cancel); 580 return; 581 } 582 mutex_exit(&softsp->power_intr_mutex); 583 584 /* Go and do the work to request shutdown */ 585 (void) power_issue_shutdown((caddr_t)softsp); 586 return; 587 } 588 589 power_button_timeouts--; 590 if (!power_button_timeouts) 591 additional_presses = 0; 592 mutex_exit(&softsp->power_intr_mutex); 593 } 594 595 #ifdef ACPI_POWER_BUTTON 596 static void 597 do_shutdown(void) 598 { 599 proc_t *initpp; 600 601 /* 602 * If we're still booting and init(1) isn't set up yet, simply halt. 603 */ 604 mutex_enter(&pidlock); 605 initpp = prfind(P_INITPID); 606 mutex_exit(&pidlock); 607 if (initpp == NULL) { 608 extern void halt(char *); 609 halt("Power off the System"); /* just in case */ 610 } 611 612 /* 613 * else, graceful shutdown with inittab and all getting involved 614 */ 615 psignal(initpp, SIGPWR); 616 } 617 #endif 618 619 static uint_t 620 power_issue_shutdown(caddr_t arg) 621 { 622 struct power_soft_state *softsp = (struct power_soft_state *)arg; 623 624 mutex_enter(&softsp->power_mutex); 625 softsp->events |= PB_BUTTON_PRESS; 626 if (softsp->monitor_on != 0) { 627 mutex_exit(&softsp->power_mutex); 628 pollwakeup(&softsp->pollhd, POLLRDNORM); 629 pollwakeup(&softsp->pollhd, POLLIN); 630 return (DDI_INTR_CLAIMED); 631 } 632 633 if (!softsp->shutdown_pending) { 634 cmn_err(CE_WARN, "Power off requested from power button or " 635 "SC, powering down the system!"); 636 softsp->shutdown_pending = 1; 637 do_shutdown(); 638 639 /* 640 * Wait a while for "do_shutdown()" to shut down the system 641 * before logging an error message. 642 */ 643 (void) timeout((void(*)(void *))power_log_message, NULL, 644 100 * hz); 645 } 646 mutex_exit(&softsp->power_mutex); 647 648 return (DDI_INTR_CLAIMED); 649 } 650 651 /* 652 * Open the device. 653 */ 654 /*ARGSUSED*/ 655 static int 656 power_open(dev_t *devp, int openflags, int otyp, cred_t *credp) 657 { 658 struct power_soft_state *softsp; 659 int clone; 660 661 if (otyp != OTYP_CHR) 662 return (EINVAL); 663 664 if ((softsp = ddi_get_soft_state(power_state, power_inst)) == 665 NULL) 666 return (ENXIO); 667 668 mutex_enter(&softsp->power_mutex); 669 for (clone = 1; clone < POWER_MAX_CLONE; clone++) 670 if (!softsp->clones[clone]) 671 break; 672 673 if (clone == POWER_MAX_CLONE) { 674 cmn_err(CE_WARN, "power_open: No more allocation left " 675 "to create a clone minor."); 676 mutex_exit(&softsp->power_mutex); 677 return (ENXIO); 678 } 679 680 *devp = makedevice(getmajor(*devp), (power_inst << 8) + clone); 681 softsp->clones[clone] = 1; 682 mutex_exit(&softsp->power_mutex); 683 684 return (0); 685 } 686 687 /* 688 * Close the device. 689 */ 690 /*ARGSUSED*/ 691 static int 692 power_close(dev_t dev, int openflags, int otyp, cred_t *credp) 693 { 694 struct power_soft_state *softsp; 695 int clone; 696 697 if (otyp != OTYP_CHR) 698 return (EINVAL); 699 700 if ((softsp = ddi_get_soft_state(power_state, power_inst)) == 701 NULL) 702 return (ENXIO); 703 704 clone = POWER_MINOR_TO_CLONE(getminor(dev)); 705 mutex_enter(&softsp->power_mutex); 706 if (softsp->monitor_on == clone) 707 softsp->monitor_on = 0; 708 softsp->clones[clone] = 0; 709 mutex_exit(&softsp->power_mutex); 710 711 return (0); 712 } 713 714 /*ARGSUSED*/ 715 static int 716 power_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cred_p, 717 int *rval_p) 718 { 719 struct power_soft_state *softsp; 720 int clone; 721 722 if ((softsp = ddi_get_soft_state(power_state, power_inst)) == 723 NULL) 724 return (ENXIO); 725 726 clone = POWER_MINOR_TO_CLONE(getminor(dev)); 727 switch (cmd) { 728 case PB_BEGIN_MONITOR: 729 mutex_enter(&softsp->power_mutex); 730 if (softsp->monitor_on) { 731 mutex_exit(&softsp->power_mutex); 732 return (EBUSY); 733 } 734 softsp->monitor_on = clone; 735 mutex_exit(&softsp->power_mutex); 736 return (0); 737 738 case PB_END_MONITOR: 739 mutex_enter(&softsp->power_mutex); 740 741 /* 742 * If PB_END_MONITOR is called without first 743 * calling PB_BEGIN_MONITOR, an error will be 744 * returned. 745 */ 746 if (!softsp->monitor_on) { 747 mutex_exit(&softsp->power_mutex); 748 return (ENXIO); 749 } 750 751 /* 752 * This clone is not monitoring the button. 753 */ 754 if (softsp->monitor_on != clone) { 755 mutex_exit(&softsp->power_mutex); 756 return (EINVAL); 757 } 758 softsp->monitor_on = 0; 759 mutex_exit(&softsp->power_mutex); 760 return (0); 761 762 case PB_GET_EVENTS: 763 mutex_enter(&softsp->power_mutex); 764 if (ddi_copyout((void *)&softsp->events, (void *)arg, 765 sizeof (int), mode) != 0) { 766 mutex_exit(&softsp->power_mutex); 767 return (EFAULT); 768 } 769 770 /* 771 * This ioctl returned the events detected since last 772 * call. Note that any application can get the events 773 * and clear the event register. 774 */ 775 softsp->events = 0; 776 mutex_exit(&softsp->power_mutex); 777 return (0); 778 779 /* 780 * This ioctl is used by the test suite. 781 */ 782 case PB_CREATE_BUTTON_EVENT: 783 #ifdef ACPI_POWER_BUTTON 784 (UINT32)power_acpi_fixed_event((void *)softsp); 785 #else 786 if (softsp->power_regs_mapped) { 787 mutex_enter(&softsp->power_intr_mutex); 788 softsp->power_btn_ioctl = B_TRUE; 789 mutex_exit(&softsp->power_intr_mutex); 790 } 791 (void) power_high_intr((caddr_t)softsp); 792 #endif /* ACPI_POWER_BUTTON */ 793 return (0); 794 795 default: 796 return (ENOTTY); 797 } 798 } 799 800 /*ARGSUSED*/ 801 static int 802 power_chpoll(dev_t dev, short events, int anyyet, 803 short *reventsp, struct pollhead **phpp) 804 { 805 struct power_soft_state *softsp; 806 807 if ((softsp = ddi_get_soft_state(power_state, power_inst)) == NULL) 808 return (ENXIO); 809 810 mutex_enter(&softsp->power_mutex); 811 *reventsp = 0; 812 if (softsp->events) 813 *reventsp = POLLRDNORM|POLLIN; 814 else { 815 if (!anyyet) 816 *phpp = &softsp->pollhd; 817 } 818 mutex_exit(&softsp->power_mutex); 819 820 return (0); 821 } 822 823 static void 824 power_log_message(void) 825 { 826 struct power_soft_state *softsp; 827 828 if ((softsp = ddi_get_soft_state(power_state, power_inst)) == NULL) { 829 cmn_err(CE_WARN, "Failed to get internal state!"); 830 return; 831 } 832 833 mutex_enter(&softsp->power_mutex); 834 softsp->shutdown_pending = 0; 835 cmn_err(CE_WARN, "Failed to shut down the system!"); 836 mutex_exit(&softsp->power_mutex); 837 } 838 839 #ifdef ACPI_POWER_BUTTON 840 /* 841 * Given a handle to a device object, locate a _PRW object 842 * if present and fetch the GPE info for this device object 843 */ 844 static ACPI_STATUS 845 power_get_prw_gpe(ACPI_HANDLE dev, ACPI_HANDLE *gpe_dev, UINT32 *gpe_num) 846 { 847 ACPI_BUFFER buf; 848 ACPI_STATUS status; 849 ACPI_HANDLE prw; 850 ACPI_OBJECT *gpe; 851 852 /* 853 * Evaluate _PRW if present 854 */ 855 status = AcpiGetHandle(dev, "_PRW", &prw); 856 if (status != AE_OK) 857 return (status); 858 buf.Length = ACPI_ALLOCATE_BUFFER; 859 status = AcpiEvaluateObjectTyped(prw, NULL, NULL, &buf, 860 ACPI_TYPE_PACKAGE); 861 if (status != AE_OK) 862 return (status); 863 864 /* 865 * Sanity-check the package; need at least two elements 866 */ 867 status = AE_ERROR; 868 if (((ACPI_OBJECT *)buf.Pointer)->Package.Count < 2) 869 goto done; 870 871 gpe = &((ACPI_OBJECT *)buf.Pointer)->Package.Elements[0]; 872 if (gpe->Type == ACPI_TYPE_INTEGER) { 873 *gpe_dev = NULL; 874 *gpe_num = gpe->Integer.Value; 875 status = AE_OK; 876 } else if (gpe->Type == ACPI_TYPE_PACKAGE) { 877 if ((gpe->Package.Count != 2) || 878 (gpe->Package.Elements[0].Type != ACPI_TYPE_DEVICE) || 879 (gpe->Package.Elements[1].Type != ACPI_TYPE_INTEGER)) 880 goto done; 881 *gpe_dev = gpe->Package.Elements[0].Reference.Handle; 882 *gpe_num = gpe->Package.Elements[1].Integer.Value; 883 status = AE_OK; 884 } 885 886 done: 887 AcpiOsFree(buf.Pointer); 888 return (status); 889 } 890 891 892 /* 893 * 894 */ 895 /*ARGSUSED*/ 896 static ACPI_STATUS 897 acpi_device(ACPI_HANDLE obj, UINT32 nesting, void *context, void **rv) 898 { 899 900 *((ACPI_HANDLE *)context) = obj; 901 return (AE_OK); 902 } 903 904 /* 905 * 906 */ 907 static ACPI_HANDLE 908 probe_acpi_pwrbutton() 909 { 910 ACPI_HANDLE obj = NULL; 911 912 (void) AcpiGetDevices("PNP0C0C", acpi_device, (void *)&obj, NULL); 913 return (obj); 914 } 915 916 static UINT32 917 power_acpi_fixed_event(void *ctx) 918 { 919 920 mutex_enter(&((struct power_soft_state *)ctx)->power_intr_mutex); 921 power_button_pressed++; 922 mutex_exit(&((struct power_soft_state *)ctx)->power_intr_mutex); 923 924 /* post softint to issue timeout for power button action */ 925 if (((struct power_soft_state *)ctx)->softintr_id != NULL) 926 ddi_trigger_softintr( 927 ((struct power_soft_state *)ctx)->softintr_id); 928 929 return (AE_OK); 930 } 931 932 /*ARGSUSED*/ 933 static void 934 power_acpi_notify_event(ACPI_HANDLE obj, UINT32 val, void *ctx) 935 { 936 if (val == 0x80) 937 (void) power_acpi_fixed_event(ctx); 938 } 939 940 /* 941 * 942 */ 943 static int 944 power_probe_method_button(struct power_soft_state *softsp) 945 { 946 ACPI_HANDLE button_obj; 947 UINT32 gpe_num; 948 ACPI_HANDLE gpe_dev; 949 950 button_obj = probe_acpi_pwrbutton(); 951 softsp->button_obj = button_obj; /* remember obj */ 952 if ((button_obj != NULL) && 953 (power_get_prw_gpe(button_obj, &gpe_dev, &gpe_num) == AE_OK) && 954 (AcpiSetGpeType(gpe_dev, gpe_num, ACPI_GPE_TYPE_WAKE_RUN) == 955 AE_OK) && 956 (AcpiEnableGpe(gpe_dev, gpe_num, ACPI_NOT_ISR) == AE_OK) && 957 (AcpiInstallNotifyHandler(button_obj, ACPI_DEVICE_NOTIFY, 958 power_acpi_notify_event, (void*)softsp) == AE_OK)) 959 return (1); 960 return (0); 961 } 962 963 /* 964 * 965 */ 966 static int 967 power_probe_fixed_button(struct power_soft_state *softsp) 968 { 969 FADT_DESCRIPTOR *fadt; 970 971 if (AcpiGetFirmwareTable(FADT_SIG, 1, ACPI_LOGICAL_ADDRESSING, 972 (ACPI_TABLE_HEADER **) &fadt) != AE_OK) 973 return (0); 974 975 if (!fadt->PwrButton) { 976 if (AcpiInstallFixedEventHandler(ACPI_EVENT_POWER_BUTTON, 977 power_acpi_fixed_event, (void *)softsp) == AE_OK) 978 return (1); 979 } 980 return (0); 981 } 982 983 984 /* 985 * 986 */ 987 static int 988 power_attach_acpi(struct power_soft_state *softsp) 989 { 990 991 /* 992 * If we've attached anything already, return an error 993 */ 994 if ((softsp->gpe_attached) || (softsp->fixed_attached)) 995 return (DDI_FAILURE); 996 997 /* 998 * attempt to attach both a fixed-event handler and a GPE 999 * handler; remember what we got 1000 */ 1001 softsp->fixed_attached = (power_probe_fixed_button(softsp) != 0); 1002 softsp->gpe_attached = (power_probe_method_button(softsp) != 0); 1003 1004 /* 1005 * If we've attached anything now, return success 1006 */ 1007 if ((softsp->gpe_attached) || (softsp->fixed_attached)) 1008 return (DDI_SUCCESS); 1009 1010 return (DDI_FAILURE); 1011 } 1012 1013 /* 1014 * 1015 */ 1016 static void 1017 power_detach_acpi(struct power_soft_state *softsp) 1018 { 1019 if (softsp->gpe_attached) { 1020 if (AcpiRemoveNotifyHandler(softsp->button_obj, 1021 ACPI_DEVICE_NOTIFY, power_acpi_notify_event) != AE_OK) 1022 cmn_err(CE_WARN, "!power: failed to remove Notify" 1023 " handler"); 1024 } 1025 1026 if (softsp->fixed_attached) { 1027 if (AcpiRemoveFixedEventHandler(ACPI_EVENT_POWER_BUTTON, 1028 power_acpi_fixed_event) != AE_OK) 1029 cmn_err(CE_WARN, "!power: failed to remove Power" 1030 " Button handler"); 1031 } 1032 } 1033 1034 #else 1035 /* 1036 * power button register definitions for acpi register on m1535d 1037 */ 1038 #define M1535D_PWR_BTN_REG_01 0x1 1039 #define M1535D_PWR_BTN_EVENT_FLAG 0x1 1040 1041 static int 1042 power_setup_m1535_regs(dev_info_t *dip, struct power_soft_state *softsp) 1043 { 1044 ddi_device_acc_attr_t attr; 1045 uint8_t *reg_base; 1046 1047 attr.devacc_attr_version = DDI_DEVICE_ATTR_V0; 1048 attr.devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC; 1049 attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC; 1050 if (ddi_regs_map_setup(dip, 0, (caddr_t *)®_base, 0, 0, &attr, 1051 &softsp->power_rhandle) != DDI_SUCCESS) { 1052 return (DDI_FAILURE); 1053 } 1054 softsp->power_btn_reg = ®_base[M1535D_PWR_BTN_REG_01]; 1055 softsp->power_btn_bit = M1535D_PWR_BTN_EVENT_FLAG; 1056 softsp->power_regs_mapped = B_TRUE; 1057 return (DDI_SUCCESS); 1058 } 1059 1060 /* 1061 * Setup register map for the power button 1062 * NOTE:- we only map registers for platforms 1063 * binding with the ali1535d+-power compatible 1064 * property. 1065 */ 1066 static int 1067 power_setup_regs(struct power_soft_state *softsp) 1068 { 1069 char *binding_name; 1070 1071 softsp->power_regs_mapped = B_FALSE; 1072 softsp->power_btn_ioctl = B_FALSE; 1073 binding_name = ddi_binding_name(softsp->dip); 1074 if (strcmp(binding_name, "ali1535d+-power") == 0) 1075 return (power_setup_m1535_regs(softsp->dip, softsp)); 1076 1077 return (DDI_SUCCESS); 1078 } 1079 1080 static void 1081 power_free_regs(struct power_soft_state *softsp) 1082 { 1083 if (softsp->power_regs_mapped) 1084 ddi_regs_map_free(&softsp->power_rhandle); 1085 } 1086 #endif /* ACPI_POWER_BUTTON */ 1087