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