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 #include <sys/types.h> 30 #include <sys/param.h> 31 #include <sys/cmn_err.h> 32 #include <sys/promif.h> 33 #include <sys/acpi/acpi.h> 34 #include <sys/acpica.h> 35 #include <sys/sunddi.h> 36 #include <sys/ddi.h> 37 #include <sys/ddi_impldefs.h> 38 #include <sys/pci.h> 39 #include <sys/debug.h> 40 #include <sys/psm_common.h> 41 #include <sys/sunndi.h> 42 #include <sys/ksynch.h> 43 44 /* 45 * ACPI Poweroff patchable options in acpi_poweroff_opt for broken BIOS 46 * workarounds. 47 */ 48 #define ACPI_PO_CLRWAK 0x0001 49 #define ACPI_PO_CLRALL 0x0002 50 #define ACPI_PO_RSTGPE 0x0004 51 #define ACPI_PO_DISARB 0x0008 52 #define ACPI_PO_2NDTRY 0x0010 53 54 /* Global configurables */ 55 56 char *psm_module_name; /* used to store name of psm module */ 57 58 /* 59 * acpi_irq_check_elcr: when set elcr will also be consulted for building 60 * the reserved irq list 61 */ 62 int acpi_irq_check_elcr = 1; 63 64 /* 65 * acpi_s5_slp_typ: 66 * If >= 0 then override the \_S5 parameter return value. This is useful 67 * for systems with broken \_S5 methods which return the wrong value for 68 * the chipset in use. 69 */ 70 int acpi_s5_slp_typ = -1; 71 int acpi_s5_slp_typ2 = -1; /* second parameter (only patch if different) */ 72 int acpi_poweroff_opt = 0; /* patchable poweroff options */ 73 74 int psm_verbose = 0; 75 76 #define PSM_VERBOSE_IRQ(fmt) \ 77 if (psm_verbose & PSM_VERBOSE_IRQ_FLAG) \ 78 cmn_err fmt; 79 80 #define PSM_VERBOSE_POWEROFF(fmt) \ 81 if (psm_verbose & PSM_VERBOSE_POWEROFF_FLAG || \ 82 psm_verbose & PSM_VERBOSE_POWEROFF_PAUSE_FLAG) \ 83 prom_printf fmt; 84 85 #define PSM_VERBOSE_POWEROFF_PAUSE(fmt) \ 86 if (psm_verbose & PSM_VERBOSE_POWEROFF_FLAG || \ 87 psm_verbose & PSM_VERBOSE_POWEROFF_PAUSE_FLAG) {\ 88 prom_printf fmt; \ 89 if (psm_verbose & PSM_VERBOSE_POWEROFF_PAUSE_FLAG) \ 90 (void) goany(); \ 91 } 92 93 94 /* Local storage */ 95 static ACPI_HANDLE acpi_sbobj = NULL; 96 static kmutex_t acpi_irq_cache_mutex; 97 98 #define D2A_INITLEN 20 99 static int d2a_len = 0; 100 static int d2a_valid = 0; 101 static d2a *d2a_table; 102 103 /* 104 * irq_cache_table is a list that serves a two-key cache. It is used 105 * as a pci busid/devid/ipin <-> irq cache and also as a acpi 106 * interrupt lnk <-> irq cache. 107 */ 108 static irq_cache_t *irq_cache_table; 109 110 #define IRQ_CACHE_INITLEN 20 111 static int irq_cache_len = 0; 112 static int irq_cache_valid = 0; 113 114 static int acpi_get_gsiv(dev_info_t *dip, ACPI_HANDLE pciobj, int devno, 115 int ipin, int *pci_irqp, iflag_t *iflagp, acpi_psm_lnk_t *acpipsmlnkp); 116 117 static int acpi_eval_lnk(dev_info_t *dip, char *lnkname, 118 int *pci_irqp, iflag_t *intr_flagp, acpi_psm_lnk_t *acpipsmlnkp); 119 120 static int acpi_get_irq_lnk_cache_ent(ACPI_HANDLE lnkobj, int *pci_irqp, 121 iflag_t *intr_flagp); 122 123 extern int goany(void); 124 125 126 #define NEXT_PRT_ITEM(p) \ 127 (ACPI_PCI_ROUTING_TABLE *)(((char *)(p)) + (p)->Length) 128 129 static int 130 acpi_get_gsiv(dev_info_t *dip, ACPI_HANDLE pciobj, int devno, int ipin, 131 int *pci_irqp, iflag_t *intr_flagp, acpi_psm_lnk_t *acpipsmlnkp) 132 { 133 ACPI_BUFFER rb; 134 ACPI_PCI_ROUTING_TABLE *prtp; 135 int status; 136 int dev_adr; 137 138 /* 139 * Get the IRQ routing table 140 */ 141 rb.Pointer = NULL; 142 rb.Length = ACPI_ALLOCATE_BUFFER; 143 if (AcpiGetIrqRoutingTable(pciobj, &rb) != AE_OK) { 144 return (ACPI_PSM_FAILURE); 145 } 146 147 status = ACPI_PSM_FAILURE; 148 dev_adr = (devno << 16 | 0xffff); 149 for (prtp = rb.Pointer; prtp->Length != 0; prtp = NEXT_PRT_ITEM(prtp)) { 150 /* look until a matching dev/pin is found */ 151 if (dev_adr != prtp->Address || ipin != prtp->Pin) 152 continue; 153 154 /* NULL Source name means index is GSIV */ 155 if (*prtp->Source == 0) { 156 intr_flagp->intr_el = TRIGGER_LEVEL; 157 intr_flagp->intr_po = POLARITY_ACTIVE_LOW; 158 ASSERT(pci_irqp != NULL); 159 *pci_irqp = prtp->SourceIndex; 160 status = ACPI_PSM_SUCCESS; 161 } else 162 status = acpi_eval_lnk(dip, prtp->Source, pci_irqp, 163 intr_flagp, acpipsmlnkp); 164 165 break; 166 167 } 168 169 AcpiOsFree(rb.Pointer); 170 return (status); 171 } 172 173 /* 174 * 175 * If the interrupt link device is already configured, 176 * stores polarity and sensitivity in the structure pointed to by 177 * intr_flagp, and irqno in the value pointed to by pci_irqp. 178 * 179 * Returns: 180 * ACPI_PSM_SUCCESS if the interrupt link device is already configured. 181 * ACPI_PSM_PARTIAL if configuration is needed. 182 * ACPI_PSM_FAILURE in case of error. 183 * 184 * When two devices share the same interrupt link device, and the 185 * link device is already configured (i.e. found in the irq cache) 186 * we need to use the already configured irq instead of reconfiguring 187 * the link device. 188 */ 189 static int 190 acpi_eval_lnk(dev_info_t *dip, char *lnkname, int *pci_irqp, 191 iflag_t *intr_flagp, acpi_psm_lnk_t *acpipsmlnkp) 192 { 193 ACPI_HANDLE tmpobj; 194 ACPI_HANDLE lnkobj; 195 int status; 196 197 /* 198 * Convert the passed-in link device name to a handle 199 */ 200 if (AcpiGetHandle(NULL, lnkname, &lnkobj) != AE_OK) { 201 return (ACPI_PSM_FAILURE); 202 } 203 204 /* 205 * Assume that the link device is invalid if no _CRS method 206 * exists, since _CRS method is a required method 207 */ 208 if (AcpiGetHandle(lnkobj, "_CRS", &tmpobj) != AE_OK) { 209 return (ACPI_PSM_FAILURE); 210 } 211 212 ASSERT(acpipsmlnkp != NULL); 213 acpipsmlnkp->lnkobj = lnkobj; 214 if ((acpi_get_irq_lnk_cache_ent(lnkobj, pci_irqp, intr_flagp)) == 215 ACPI_PSM_SUCCESS) { 216 PSM_VERBOSE_IRQ((CE_CONT, "!psm: link object found from cache " 217 " for device %s, instance #%d, irq no %d\n", 218 ddi_get_name(dip), ddi_get_instance(dip), *pci_irqp)); 219 return (ACPI_PSM_SUCCESS); 220 } else { 221 if (acpica_eval_int(lnkobj, "_STA", &status) == AE_OK) { 222 acpipsmlnkp->device_status = (uchar_t)status; 223 } 224 225 return (ACPI_PSM_PARTIAL); 226 } 227 } 228 229 int 230 acpi_psm_init(char *module_name, int verbose_flags) 231 { 232 psm_module_name = module_name; 233 234 psm_verbose = verbose_flags; 235 236 if (AcpiGetHandle(NULL, "\\_SB", &acpi_sbobj) != AE_OK) { 237 cmn_err(CE_WARN, "!psm: get _SB failed"); 238 return (ACPI_PSM_FAILURE); 239 } 240 241 mutex_init(&acpi_irq_cache_mutex, NULL, MUTEX_DEFAULT, NULL); 242 243 return (ACPI_PSM_SUCCESS); 244 245 } 246 247 /* 248 * Return bus/dev/fn for PCI dip (note: not the parent "pci" node). 249 */ 250 251 int 252 get_bdf(dev_info_t *dip, int *bus, int *device, int *func) 253 { 254 pci_regspec_t *pci_rp; 255 int len; 256 257 if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 258 "reg", (int **)&pci_rp, (uint_t *)&len) != DDI_SUCCESS) 259 return (-1); 260 261 if (len < (sizeof (pci_regspec_t) / sizeof (int))) { 262 ddi_prop_free(pci_rp); 263 return (-1); 264 } 265 if (bus != NULL) 266 *bus = (int)PCI_REG_BUS_G(pci_rp->pci_phys_hi); 267 if (device != NULL) 268 *device = (int)PCI_REG_DEV_G(pci_rp->pci_phys_hi); 269 if (func != NULL) 270 *func = (int)PCI_REG_FUNC_G(pci_rp->pci_phys_hi); 271 ddi_prop_free(pci_rp); 272 return (0); 273 } 274 275 276 /* 277 * Build the reserved ISA irq list, and store it in the table pointed to by 278 * reserved_irqs_table. The caller is responsible for allocating this table 279 * with a minimum of MAX_ISA_IRQ + 1 entries. 280 * 281 * The routine looks in the device tree at the subtree rooted at /isa 282 * for each of the devices under that node, if an interrupts property 283 * is present, its values are used to "reserve" irqs so that later ACPI 284 * configuration won't choose those irqs. 285 * 286 * In addition, if acpi_irq_check_elcr is set, will use ELCR register 287 * to identify reserved IRQs. 288 */ 289 void 290 build_reserved_irqlist(uchar_t *reserved_irqs_table) 291 { 292 dev_info_t *isanode = ddi_find_devinfo("isa", -1, 0); 293 dev_info_t *isa_child = 0; 294 int i; 295 uint_t elcrval; 296 297 /* Initialize the reserved ISA IRQs: */ 298 for (i = 0; i < MAX_ISA_IRQ; i++) 299 reserved_irqs_table[i] = 0; 300 301 if (acpi_irq_check_elcr) { 302 303 elcrval = (inb(ELCR_PORT2) << 8) | (inb(ELCR_PORT1)); 304 if (ELCR_EDGE(elcrval, 0) && ELCR_EDGE(elcrval, 1) && 305 ELCR_EDGE(elcrval, 2) && ELCR_EDGE(elcrval, 8) && 306 ELCR_EDGE(elcrval, 13)) { 307 /* valid ELCR */ 308 for (i = 0; i < MAX_ISA_IRQ; i++) 309 if (!ELCR_LEVEL(elcrval, i)) 310 reserved_irqs_table[i] = 1; 311 } 312 } 313 314 /* always check the isa devinfo nodes */ 315 316 if (isanode != 0) { /* Found ISA */ 317 uint_t intcnt; /* Interrupt count */ 318 int *intrs; /* Interrupt values */ 319 320 /* Load first child: */ 321 isa_child = ddi_get_child(isanode); 322 while (isa_child != 0) { /* Iterate over /isa children */ 323 /* if child has any interrupts, save them */ 324 if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, isa_child, 325 DDI_PROP_DONTPASS, "interrupts", &intrs, &intcnt) 326 == DDI_PROP_SUCCESS) { 327 /* 328 * iterate over child interrupt list, adding 329 * them to the reserved irq list 330 */ 331 while (intcnt-- > 0) { 332 /* 333 * Each value MUST be <= MAX_ISA_IRQ 334 */ 335 336 if ((intrs[intcnt] > MAX_ISA_IRQ) || 337 (intrs[intcnt] < 0)) 338 continue; 339 340 reserved_irqs_table[intrs[intcnt]] = 1; 341 } 342 ddi_prop_free(intrs); 343 } 344 isa_child = ddi_get_next_sibling(isa_child); 345 } 346 /* The isa node was held by ddi_find_devinfo, so release it */ 347 ndi_rele_devi(isanode); 348 } 349 350 /* 351 * Reserve IRQ14 & IRQ15 for IDE. It shouldn't be hard-coded 352 * here but there's no other way to find the irqs for 353 * legacy-mode ata (since it's hard-coded in pci-ide also). 354 */ 355 reserved_irqs_table[14] = 1; 356 reserved_irqs_table[15] = 1; 357 } 358 359 /* 360 * Examine devinfo node to determine if it is a PCI-PCI bridge 361 * 362 * Returns: 363 * 0 if not a bridge or error 364 * 1 if a bridge 365 */ 366 static int 367 psm_is_pci_bridge(dev_info_t *dip) 368 { 369 ddi_acc_handle_t cfg_handle; 370 int rv = 0; 371 372 if (pci_config_setup(dip, &cfg_handle) == DDI_SUCCESS) { 373 rv = ((pci_config_get8(cfg_handle, PCI_CONF_BASCLASS) == 374 PCI_CLASS_BRIDGE) && (pci_config_get8(cfg_handle, 375 PCI_CONF_SUBCLASS) == PCI_BRIDGE_PCI)); 376 pci_config_teardown(&cfg_handle); 377 } 378 379 return (rv); 380 } 381 382 383 /* 384 * Examines ACPI node for presence of _PRT object 385 * 386 * Returns: 387 * 0 if no _PRT or error 388 * 1 if _PRT is present 389 */ 390 static int 391 psm_node_has_prt(ACPI_HANDLE *ah) 392 { 393 ACPI_HANDLE rh; 394 395 return (AcpiGetHandle(ah, "_PRT", &rh) == AE_OK); 396 } 397 398 399 /* 400 * Look first for an ACPI PCI bus node matching busid, then for a _PRT on the 401 * parent node; then drop into the bridge-chasing code (which will also 402 * look for _PRTs on the way up the tree of bridges) 403 * 404 * Stores polarity and sensitivity in the structure pointed to by 405 * intr_flagp, and irqno in the value pointed to by pci_irqp. * 406 * Returns: 407 * ACPI_PSM_SUCCESS on success. 408 * ACPI_PSM_PARTIAL to indicate need to configure the interrupt 409 * link device. 410 * ACPI_PSM_FAILURE if an error prevented the system from 411 * obtaining irq information for dip. 412 */ 413 int 414 acpi_translate_pci_irq(dev_info_t *dip, int ipin, int *pci_irqp, 415 iflag_t *intr_flagp, acpi_psm_lnk_t *acpipsmlnkp) 416 { 417 ACPI_HANDLE pciobj; 418 int status = AE_ERROR; 419 dev_info_t *curdip, *parentdip; 420 int curpin, curbus, curdev; 421 422 423 curpin = ipin; 424 curdip = dip; 425 while (curdip != ddi_root_node()) { 426 parentdip = ddi_get_parent(curdip); 427 ASSERT(parentdip != NULL); 428 429 if (get_bdf(curdip, &curbus, &curdev, NULL) != 0) { 430 break; 431 } 432 433 status = acpica_find_pciobj(parentdip, &pciobj); 434 if ((status == AE_OK) && psm_node_has_prt(pciobj)) { 435 return (acpi_get_gsiv(curdip, pciobj, curdev, curpin, 436 pci_irqp, intr_flagp, acpipsmlnkp)); 437 } 438 439 /* if we got here, we need to traverse a bridge upwards */ 440 if (!psm_is_pci_bridge(parentdip)) 441 break; 442 443 /* 444 * This is the rotating scheme that Compaq is using 445 * and documented in the PCI-PCI spec. Also, if the 446 * PCI-PCI bridge is behind another PCI-PCI bridge, 447 * then it needs to keep ascending until an interrupt 448 * entry is found or the top is reached 449 */ 450 curpin = (curdev + curpin) % PCI_INTD; 451 curdip = parentdip; 452 } 453 454 /* 455 * We should never, ever get here; didn't find a _PRT 456 */ 457 return (ACPI_PSM_FAILURE); 458 } 459 460 /* 461 * Sets the irq resource of the lnk object to the requested irq value. 462 * 463 * Returns ACPI_PSM_SUCCESS on success, ACPI_PSM_FAILURE upon failure. 464 */ 465 int 466 acpi_set_irq_resource(acpi_psm_lnk_t *acpipsmlnkp, int irq) 467 { 468 ACPI_BUFFER rsb; 469 ACPI_RESOURCE *resp; 470 ACPI_RESOURCE *srsp; 471 ACPI_HANDLE lnkobj; 472 int status; 473 474 ASSERT(acpipsmlnkp != NULL); 475 476 lnkobj = acpipsmlnkp->lnkobj; 477 478 /* 479 * Fetch the possible resources for the link 480 */ 481 482 rsb.Pointer = NULL; 483 rsb.Length = ACPI_ALLOCATE_BUFFER; 484 status = AcpiGetPossibleResources(lnkobj, &rsb); 485 if (status != AE_OK) { 486 cmn_err(CE_WARN, "!psm: set_irq: _PRS failed"); 487 return (ACPI_PSM_FAILURE); 488 } 489 490 /* 491 * Find an IRQ resource descriptor to use as template 492 */ 493 srsp = NULL; 494 for (resp = rsb.Pointer; resp->Length != 0; 495 resp = ACPI_NEXT_RESOURCE(resp)) { 496 if ((resp->Id == ACPI_RSTYPE_IRQ) || 497 (resp->Id == ACPI_RSTYPE_EXT_IRQ)) { 498 /* 499 * Mild trickery here; allocate two 500 * resource structures, and set the Length 501 * field of the second one to 0 to terminate 502 * the list. Copy the possible resource into 503 * the first one as a template. 504 */ 505 srsp = kmem_zalloc(2 * sizeof (*srsp), KM_SLEEP); 506 srsp[1].Length = 0; 507 *srsp = *resp; 508 break; /* drop out of the loop */ 509 } 510 } 511 512 /* 513 * We're done with the PRS values, toss 'em lest we forget 514 */ 515 AcpiOsFree(rsb.Pointer); 516 517 if (srsp == NULL) 518 return (ACPI_PSM_FAILURE); 519 520 /* 521 * The Interrupts[] array is always at least one entry 522 * long; see the definition of ACPI_RESOURCE. 523 */ 524 switch (srsp->Id) { 525 case ACPI_RSTYPE_IRQ: 526 srsp->Data.Irq.NumberOfInterrupts = 1; 527 srsp->Data.Irq.Interrupts[0] = irq; 528 break; 529 case ACPI_RSTYPE_EXT_IRQ: 530 srsp->Data.ExtendedIrq.NumberOfInterrupts = 1; 531 srsp->Data.ExtendedIrq.Interrupts[0] = irq; 532 break; 533 } 534 535 rsb.Pointer = srsp; 536 rsb.Length = 2 * sizeof (*srsp); 537 status = AcpiSetCurrentResources(lnkobj, &rsb); 538 kmem_free(srsp, 2 * sizeof (*srsp)); 539 if (status != AE_OK) { 540 cmn_err(CE_WARN, "!psm: set_irq: _SRS failed"); 541 return (ACPI_PSM_FAILURE); 542 } 543 544 if (acpica_eval_int(lnkobj, "_STA", &status) == AE_OK) { 545 acpipsmlnkp->device_status = (uchar_t)status; 546 return (ACPI_PSM_SUCCESS); 547 } else 548 return (ACPI_PSM_FAILURE); 549 } 550 551 552 /* 553 * 554 */ 555 static int 556 psm_acpi_edgelevel(UINT32 el) 557 { 558 switch (el) { 559 case ACPI_EDGE_SENSITIVE: 560 return (INTR_EL_EDGE); 561 case ACPI_LEVEL_SENSITIVE: 562 return (INTR_EL_LEVEL); 563 default: 564 /* el is a single bit; should never reach here */ 565 return (INTR_EL_CONFORM); 566 } 567 } 568 569 570 /* 571 * 572 */ 573 static int 574 psm_acpi_po(UINT32 po) 575 { 576 switch (po) { 577 case ACPI_ACTIVE_HIGH: 578 return (INTR_PO_ACTIVE_HIGH); 579 case ACPI_ACTIVE_LOW: 580 return (INTR_PO_ACTIVE_LOW); 581 default: 582 /* po is a single bit; should never reach here */ 583 return (INTR_PO_CONFORM); 584 } 585 } 586 587 588 /* 589 * Retrieves the current irq setting for the interrrupt link device. 590 * 591 * Stores polarity and sensitivity in the structure pointed to by 592 * intr_flagp, and irqno in the value pointed to by pci_irqp. 593 * 594 * Returns ACPI_PSM_SUCCESS on success, ACPI_PSM_FAILURE upon failure. 595 */ 596 int 597 acpi_get_current_irq_resource(acpi_psm_lnk_t *acpipsmlnkp, int *pci_irqp, 598 iflag_t *intr_flagp) 599 { 600 ACPI_HANDLE lnkobj; 601 ACPI_BUFFER rb; 602 ACPI_RESOURCE *rp; 603 int irq; 604 int status = ACPI_PSM_FAILURE; 605 606 ASSERT(acpipsmlnkp != NULL); 607 lnkobj = acpipsmlnkp->lnkobj; 608 609 if (!(acpipsmlnkp->device_status & STA_PRESENT) || 610 !(acpipsmlnkp->device_status & STA_ENABLE)) { 611 PSM_VERBOSE_IRQ((CE_WARN, "!psm: crs device either not " 612 "present or disabled, status 0x%x", 613 acpipsmlnkp->device_status)); 614 return (ACPI_PSM_FAILURE); 615 } 616 617 rb.Pointer = NULL; 618 rb.Length = ACPI_ALLOCATE_BUFFER; 619 if (AcpiGetCurrentResources(lnkobj, &rb) != AE_OK) { 620 PSM_VERBOSE_IRQ((CE_WARN, "!psm: no crs object found or" 621 " evaluation failed")); 622 return (ACPI_PSM_FAILURE); 623 } 624 625 irq = -1; 626 for (rp = rb.Pointer; rp->Length != 0; rp = ACPI_NEXT_RESOURCE(rp)) { 627 if (rp->Id == ACPI_RSTYPE_IRQ) { 628 if (irq > 0) { 629 PSM_VERBOSE_IRQ((CE_WARN, "!psm: multiple IRQ" 630 " from _CRS ")); 631 status = ACPI_PSM_FAILURE; 632 break; 633 } 634 635 if (rp->Data.Irq.NumberOfInterrupts != 1) { 636 PSM_VERBOSE_IRQ((CE_WARN, "!psm: <>1 interrupt" 637 " from _CRS ")); 638 status = ACPI_PSM_FAILURE; 639 break; 640 } 641 642 intr_flagp->intr_el = psm_acpi_edgelevel( 643 rp->Data.Irq.EdgeLevel); 644 intr_flagp->intr_po = psm_acpi_po( 645 rp->Data.Irq.ActiveHighLow); 646 irq = rp->Data.Irq.Interrupts[0]; 647 status = ACPI_PSM_SUCCESS; 648 } else if (rp->Id == ACPI_RSTYPE_EXT_IRQ) { 649 if (irq > 0) { 650 PSM_VERBOSE_IRQ((CE_WARN, "!psm: multiple IRQ" 651 " from _CRS ")); 652 status = ACPI_PSM_FAILURE; 653 break; 654 } 655 656 if (rp->Data.ExtendedIrq.NumberOfInterrupts != 1) { 657 PSM_VERBOSE_IRQ((CE_WARN, "!psm: <>1 interrupt" 658 " from _CRS ")); 659 status = ACPI_PSM_FAILURE; 660 break; 661 } 662 663 intr_flagp->intr_el = psm_acpi_edgelevel( 664 rp->Data.ExtendedIrq.EdgeLevel); 665 intr_flagp->intr_po = psm_acpi_po( 666 rp->Data.ExtendedIrq.ActiveHighLow); 667 irq = rp->Data.ExtendedIrq.Interrupts[0]; 668 status = ACPI_PSM_SUCCESS; 669 } 670 } 671 672 AcpiOsFree(rb.Pointer); 673 if (status == ACPI_PSM_SUCCESS) { 674 *pci_irqp = irq; 675 } 676 677 return (status); 678 } 679 680 /* 681 * Searches for the given IRQ in the irqlist passed in. 682 * 683 * If multiple matches exist, this returns true on the first match. 684 * Returns the interrupt flags, if a match was found, in `intr_flagp' if 685 * it's passed in non-NULL 686 */ 687 int 688 acpi_irqlist_find_irq(acpi_irqlist_t *irqlistp, int irq, iflag_t *intr_flagp) 689 { 690 int found = 0; 691 int i; 692 693 while (irqlistp != NULL && !found) { 694 for (i = 0; i < irqlistp->num_irqs; i++) { 695 if (irqlistp->irqs[i] == irq) { 696 if (intr_flagp) 697 *intr_flagp = irqlistp->intr_flags; 698 found = 1; 699 break; /* out of for() */ 700 } 701 } 702 } 703 704 return (found ? ACPI_PSM_SUCCESS : ACPI_PSM_FAILURE); 705 } 706 707 /* 708 * Frees the irqlist allocated by acpi_get_possible_irq_resource. 709 * It takes a count of number of entries in the list. 710 */ 711 void 712 acpi_free_irqlist(acpi_irqlist_t *irqlistp) 713 { 714 acpi_irqlist_t *freednode; 715 716 while (irqlistp != NULL) { 717 /* Free the irq list */ 718 kmem_free(irqlistp->irqs, irqlistp->num_irqs * 719 sizeof (int32_t)); 720 721 freednode = irqlistp; 722 irqlistp = irqlistp->next; 723 kmem_free(freednode, sizeof (acpi_irqlist_t)); 724 } 725 } 726 727 /* 728 * Creates a new entry in the given irqlist with the information passed in. 729 */ 730 static void 731 acpi_add_irqlist_entry(acpi_irqlist_t **irqlistp, uint32_t *irqlist, 732 int irqlist_len, iflag_t *intr_flagp) 733 { 734 acpi_irqlist_t *newent; 735 736 ASSERT(irqlist != NULL); 737 ASSERT(intr_flagp != NULL); 738 739 newent = kmem_alloc(sizeof (acpi_irqlist_t), KM_SLEEP); 740 newent->intr_flags = *intr_flagp; 741 newent->irqs = irqlist; 742 newent->num_irqs = irqlist_len; 743 newent->next = *irqlistp; 744 745 *irqlistp = newent; 746 } 747 748 749 /* 750 * Retrieves a list of possible interrupt settings for the interrupt link 751 * device. 752 * 753 * Stores polarity and sensitivity in the structure pointed to by intr_flagp. 754 * Updates value pointed to by irqlistp with the address of a table it 755 * allocates. where interrupt numbers are stored. Stores the number of entries 756 * in this table in the value pointed to by num_entriesp; 757 * 758 * Each element in this table is of type int32_t. The table should be later 759 * freed by caller via acpi_free_irq_list(). 760 * 761 * Returns ACPI_PSM_SUCCESS on success and ACPI_PSM_FAILURE upon failure 762 */ 763 int 764 acpi_get_possible_irq_resources(acpi_psm_lnk_t *acpipsmlnkp, 765 acpi_irqlist_t **irqlistp) 766 { 767 ACPI_HANDLE lnkobj; 768 ACPI_BUFFER rsb; 769 ACPI_RESOURCE *resp; 770 int status; 771 772 int i, el, po, irqlist_len; 773 uint32_t *irqlist, *tmplist; 774 iflag_t intr_flags; 775 776 ASSERT(acpipsmlnkp != NULL); 777 lnkobj = acpipsmlnkp->lnkobj; 778 779 rsb.Pointer = NULL; 780 rsb.Length = ACPI_ALLOCATE_BUFFER; 781 status = AcpiGetPossibleResources(lnkobj, &rsb); 782 if (status != AE_OK) { 783 cmn_err(CE_WARN, "!psm: get_irq: _PRS failed"); 784 return (ACPI_PSM_FAILURE); 785 } 786 787 /* 788 * Scan the resources looking for an interrupt resource 789 */ 790 *irqlistp = 0; 791 for (resp = rsb.Pointer; resp->Length != 0; 792 resp = ACPI_NEXT_RESOURCE(resp)) { 793 switch (resp->Id) { 794 case ACPI_RSTYPE_IRQ: 795 irqlist_len = resp->Data.Irq.NumberOfInterrupts; 796 tmplist = resp->Data.Irq.Interrupts; 797 el = resp->Data.Irq.EdgeLevel; 798 po = resp->Data.Irq.ActiveHighLow; 799 break; 800 case ACPI_RSTYPE_EXT_IRQ: 801 irqlist_len = resp->Data.ExtendedIrq.NumberOfInterrupts; 802 tmplist = resp->Data.ExtendedIrq.Interrupts; 803 el = resp->Data.ExtendedIrq.EdgeLevel; 804 po = resp->Data.ExtendedIrq.ActiveHighLow; 805 break; 806 default: 807 continue; 808 } 809 /* NEEDSWORK: move this into add_irqlist_entry someday */ 810 irqlist = kmem_zalloc(irqlist_len * sizeof (*irqlist), 811 KM_SLEEP); 812 for (i = 0; i < irqlist_len; i++) 813 irqlist[i] = tmplist[i]; 814 intr_flags.intr_el = psm_acpi_edgelevel(el); 815 intr_flags.intr_po = psm_acpi_po(po); 816 acpi_add_irqlist_entry(irqlistp, irqlist, irqlist_len, 817 &intr_flags); 818 } 819 820 AcpiOsFree(rsb.Pointer); 821 return (irqlistp == NULL ? ACPI_PSM_FAILURE : ACPI_PSM_SUCCESS); 822 } 823 824 /* 825 * Adds a new cache entry to the irq cache which maps an irq and 826 * its attributes to PCI bus/dev/ipin and optionally to its associated ACPI 827 * interrupt link device object. 828 */ 829 void 830 acpi_new_irq_cache_ent(int bus, int dev, int ipin, int pci_irq, 831 iflag_t *intr_flagp, acpi_psm_lnk_t *acpipsmlnkp) 832 { 833 int newsize; 834 irq_cache_t *new_arr, *ep; 835 836 mutex_enter(&acpi_irq_cache_mutex); 837 if (irq_cache_valid >= irq_cache_len) { 838 /* initially, or re-, allocate array */ 839 840 newsize = (irq_cache_len ? 841 irq_cache_len * 2 : IRQ_CACHE_INITLEN); 842 new_arr = kmem_zalloc(newsize * sizeof (irq_cache_t), KM_SLEEP); 843 if (irq_cache_len != 0) { 844 /* realloc: copy data, free old */ 845 bcopy(irq_cache_table, new_arr, 846 irq_cache_len * sizeof (irq_cache_t)); 847 kmem_free(irq_cache_table, 848 irq_cache_len * sizeof (irq_cache_t)); 849 } 850 irq_cache_len = newsize; 851 irq_cache_table = new_arr; 852 } 853 ep = &irq_cache_table[irq_cache_valid++]; 854 ep->bus = (uchar_t)bus; 855 ep->dev = (uchar_t)dev; 856 ep->ipin = (uchar_t)ipin; 857 ep->flags = *intr_flagp; 858 ep->irq = pci_irq; 859 ASSERT(acpipsmlnkp != NULL); 860 ep->lnkobj = acpipsmlnkp->lnkobj; 861 mutex_exit(&acpi_irq_cache_mutex); 862 } 863 864 865 /* 866 * Searches the irq caches for the given bus/dev/ipin. 867 * 868 * If info is found, stores polarity and sensitivity in the structure 869 * pointed to by intr_flagp, and irqno in the value pointed to by pci_irqp, 870 * and returns ACPI_PSM_SUCCESS. 871 * Otherwise, ACPI_PSM_FAILURE is returned. 872 */ 873 int 874 acpi_get_irq_cache_ent(uchar_t bus, uchar_t dev, int ipin, 875 int *pci_irqp, iflag_t *intr_flagp) 876 { 877 878 irq_cache_t *irqcachep; 879 int i; 880 int ret = ACPI_PSM_FAILURE; 881 882 mutex_enter(&acpi_irq_cache_mutex); 883 for (irqcachep = irq_cache_table, i = 0; i < irq_cache_valid; 884 irqcachep++, i++) 885 if ((irqcachep->bus == bus) && 886 (irqcachep->dev == dev) && 887 (irqcachep->ipin == ipin)) { 888 ASSERT(pci_irqp != NULL && intr_flagp != NULL); 889 *pci_irqp = irqcachep->irq; 890 *intr_flagp = irqcachep->flags; 891 ret = ACPI_PSM_SUCCESS; 892 break; 893 } 894 895 mutex_exit(&acpi_irq_cache_mutex); 896 return (ret); 897 } 898 899 /* 900 * Searches the irq caches for the given interrupt lnk device object. 901 * 902 * If info is found, stores polarity and sensitivity in the structure 903 * pointed to by intr_flagp, and irqno in the value pointed to by pci_irqp, 904 * and returns ACPI_PSM_SUCCESS. 905 * Otherwise, ACPI_PSM_FAILURE is returned. 906 */ 907 int 908 acpi_get_irq_lnk_cache_ent(ACPI_HANDLE lnkobj, int *pci_irqp, 909 iflag_t *intr_flagp) 910 { 911 912 irq_cache_t *irqcachep; 913 int i; 914 int ret = ACPI_PSM_FAILURE; 915 916 if (lnkobj == NULL) 917 return (ACPI_PSM_FAILURE); 918 919 mutex_enter(&acpi_irq_cache_mutex); 920 for (irqcachep = irq_cache_table, i = 0; i < irq_cache_valid; 921 irqcachep++, i++) 922 if (irqcachep->lnkobj == lnkobj) { 923 ASSERT(pci_irqp != NULL); 924 *pci_irqp = irqcachep->irq; 925 ASSERT(intr_flagp != NULL); 926 *intr_flagp = irqcachep->flags; 927 ret = ACPI_PSM_SUCCESS; 928 break; 929 } 930 mutex_exit(&acpi_irq_cache_mutex); 931 return (ret); 932 } 933 934 int 935 acpi_poweroff(void) 936 { 937 PSM_VERBOSE_POWEROFF(("acpi_poweroff: starting poweroff\n")); 938 939 if (AcpiEnterSleepStatePrep(5) != AE_OK) 940 return (1); 941 ACPI_DISABLE_IRQS(); 942 if (AcpiEnterSleepState(5) != AE_OK) { 943 ACPI_ENABLE_IRQS(); 944 return (1); 945 } 946 ACPI_ENABLE_IRQS(); 947 948 /* we should be off; if we get here it's an error */ 949 PSM_VERBOSE_POWEROFF(("acpi_poweroff: failed to actually power off\n")); 950 return (1); 951 } 952