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