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