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