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 }