10Sstevel@tonic-gate /* 20Sstevel@tonic-gate * CDDL HEADER START 30Sstevel@tonic-gate * 40Sstevel@tonic-gate * The contents of this file are subject to the terms of the 51456Sdmick * Common Development and Distribution License (the "License"). 61456Sdmick * You may not use this file except in compliance with the License. 70Sstevel@tonic-gate * 80Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 90Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 100Sstevel@tonic-gate * See the License for the specific language governing permissions 110Sstevel@tonic-gate * and limitations under the License. 120Sstevel@tonic-gate * 130Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 140Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 150Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 160Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 170Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 180Sstevel@tonic-gate * 190Sstevel@tonic-gate * CDDL HEADER END 200Sstevel@tonic-gate */ 210Sstevel@tonic-gate /* 223472Smyers * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 230Sstevel@tonic-gate * Use is subject to license terms. 240Sstevel@tonic-gate */ 250Sstevel@tonic-gate 260Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 270Sstevel@tonic-gate 280Sstevel@tonic-gate #include <sys/types.h> 290Sstevel@tonic-gate #include <sys/param.h> 300Sstevel@tonic-gate #include <sys/cmn_err.h> 310Sstevel@tonic-gate #include <sys/promif.h> 320Sstevel@tonic-gate #include <sys/acpi/acpi.h> 330Sstevel@tonic-gate #include <sys/acpica.h> 340Sstevel@tonic-gate #include <sys/sunddi.h> 350Sstevel@tonic-gate #include <sys/ddi.h> 360Sstevel@tonic-gate #include <sys/ddi_impldefs.h> 370Sstevel@tonic-gate #include <sys/pci.h> 380Sstevel@tonic-gate #include <sys/debug.h> 390Sstevel@tonic-gate #include <sys/psm_common.h> 400Sstevel@tonic-gate #include <sys/sunndi.h> 410Sstevel@tonic-gate #include <sys/ksynch.h> 420Sstevel@tonic-gate 430Sstevel@tonic-gate /* Global configurables */ 440Sstevel@tonic-gate 450Sstevel@tonic-gate char *psm_module_name; /* used to store name of psm module */ 460Sstevel@tonic-gate 470Sstevel@tonic-gate /* 480Sstevel@tonic-gate * acpi_irq_check_elcr: when set elcr will also be consulted for building 49347Smyers * the reserved irq list. When 0 (false), the existing state of the ELCR 50347Smyers * is ignored when selecting a vector during IRQ translation, and the ELCR 51347Smyers * is programmed to the proper setting for the type of bus (level-triggered 52347Smyers * for PCI, edge-triggered for non-PCI). When non-zero (true), vectors 53347Smyers * set to edge-mode will not be used when in PIC-mode. The default value 54347Smyers * is 0 (false). Note that ACPI's SCI vector is always set to conform to 55347Smyers * ACPI-specification regardless of this. 56347Smyers * 570Sstevel@tonic-gate */ 58347Smyers int acpi_irq_check_elcr = 0; 590Sstevel@tonic-gate 600Sstevel@tonic-gate int psm_verbose = 0; 610Sstevel@tonic-gate 620Sstevel@tonic-gate #define PSM_VERBOSE_IRQ(fmt) \ 630Sstevel@tonic-gate if (psm_verbose & PSM_VERBOSE_IRQ_FLAG) \ 640Sstevel@tonic-gate cmn_err fmt; 650Sstevel@tonic-gate 660Sstevel@tonic-gate #define PSM_VERBOSE_POWEROFF(fmt) \ 670Sstevel@tonic-gate if (psm_verbose & PSM_VERBOSE_POWEROFF_FLAG || \ 680Sstevel@tonic-gate psm_verbose & PSM_VERBOSE_POWEROFF_PAUSE_FLAG) \ 690Sstevel@tonic-gate prom_printf fmt; 700Sstevel@tonic-gate 710Sstevel@tonic-gate #define PSM_VERBOSE_POWEROFF_PAUSE(fmt) \ 720Sstevel@tonic-gate if (psm_verbose & PSM_VERBOSE_POWEROFF_FLAG || \ 730Sstevel@tonic-gate psm_verbose & PSM_VERBOSE_POWEROFF_PAUSE_FLAG) {\ 740Sstevel@tonic-gate prom_printf fmt; \ 750Sstevel@tonic-gate if (psm_verbose & PSM_VERBOSE_POWEROFF_PAUSE_FLAG) \ 760Sstevel@tonic-gate (void) goany(); \ 770Sstevel@tonic-gate } 780Sstevel@tonic-gate 790Sstevel@tonic-gate 800Sstevel@tonic-gate /* Local storage */ 810Sstevel@tonic-gate static ACPI_HANDLE acpi_sbobj = NULL; 820Sstevel@tonic-gate static kmutex_t acpi_irq_cache_mutex; 830Sstevel@tonic-gate 840Sstevel@tonic-gate /* 850Sstevel@tonic-gate * irq_cache_table is a list that serves a two-key cache. It is used 860Sstevel@tonic-gate * as a pci busid/devid/ipin <-> irq cache and also as a acpi 870Sstevel@tonic-gate * interrupt lnk <-> irq cache. 880Sstevel@tonic-gate */ 890Sstevel@tonic-gate static irq_cache_t *irq_cache_table; 900Sstevel@tonic-gate 910Sstevel@tonic-gate #define IRQ_CACHE_INITLEN 20 920Sstevel@tonic-gate static int irq_cache_len = 0; 930Sstevel@tonic-gate static int irq_cache_valid = 0; 940Sstevel@tonic-gate 950Sstevel@tonic-gate static int acpi_get_gsiv(dev_info_t *dip, ACPI_HANDLE pciobj, int devno, 960Sstevel@tonic-gate int ipin, int *pci_irqp, iflag_t *iflagp, acpi_psm_lnk_t *acpipsmlnkp); 970Sstevel@tonic-gate 980Sstevel@tonic-gate static int acpi_eval_lnk(dev_info_t *dip, char *lnkname, 990Sstevel@tonic-gate int *pci_irqp, iflag_t *intr_flagp, acpi_psm_lnk_t *acpipsmlnkp); 1000Sstevel@tonic-gate 1010Sstevel@tonic-gate static int acpi_get_irq_lnk_cache_ent(ACPI_HANDLE lnkobj, int *pci_irqp, 1020Sstevel@tonic-gate iflag_t *intr_flagp); 1030Sstevel@tonic-gate 1040Sstevel@tonic-gate extern int goany(void); 1050Sstevel@tonic-gate 1060Sstevel@tonic-gate 1070Sstevel@tonic-gate #define NEXT_PRT_ITEM(p) \ 1080Sstevel@tonic-gate (ACPI_PCI_ROUTING_TABLE *)(((char *)(p)) + (p)->Length) 1090Sstevel@tonic-gate 1100Sstevel@tonic-gate static int 1110Sstevel@tonic-gate acpi_get_gsiv(dev_info_t *dip, ACPI_HANDLE pciobj, int devno, int ipin, 1120Sstevel@tonic-gate int *pci_irqp, iflag_t *intr_flagp, acpi_psm_lnk_t *acpipsmlnkp) 1130Sstevel@tonic-gate { 1140Sstevel@tonic-gate ACPI_BUFFER rb; 1150Sstevel@tonic-gate ACPI_PCI_ROUTING_TABLE *prtp; 1160Sstevel@tonic-gate int status; 1170Sstevel@tonic-gate int dev_adr; 1180Sstevel@tonic-gate 1190Sstevel@tonic-gate /* 1200Sstevel@tonic-gate * Get the IRQ routing table 1210Sstevel@tonic-gate */ 1220Sstevel@tonic-gate rb.Pointer = NULL; 1230Sstevel@tonic-gate rb.Length = ACPI_ALLOCATE_BUFFER; 1240Sstevel@tonic-gate if (AcpiGetIrqRoutingTable(pciobj, &rb) != AE_OK) { 1250Sstevel@tonic-gate return (ACPI_PSM_FAILURE); 1260Sstevel@tonic-gate } 1270Sstevel@tonic-gate 1280Sstevel@tonic-gate status = ACPI_PSM_FAILURE; 1290Sstevel@tonic-gate dev_adr = (devno << 16 | 0xffff); 1300Sstevel@tonic-gate for (prtp = rb.Pointer; prtp->Length != 0; prtp = NEXT_PRT_ITEM(prtp)) { 1310Sstevel@tonic-gate /* look until a matching dev/pin is found */ 1320Sstevel@tonic-gate if (dev_adr != prtp->Address || ipin != prtp->Pin) 1330Sstevel@tonic-gate continue; 1340Sstevel@tonic-gate 1350Sstevel@tonic-gate /* NULL Source name means index is GSIV */ 1360Sstevel@tonic-gate if (*prtp->Source == 0) { 1370Sstevel@tonic-gate intr_flagp->intr_el = TRIGGER_LEVEL; 1380Sstevel@tonic-gate intr_flagp->intr_po = POLARITY_ACTIVE_LOW; 1390Sstevel@tonic-gate ASSERT(pci_irqp != NULL); 1400Sstevel@tonic-gate *pci_irqp = prtp->SourceIndex; 1410Sstevel@tonic-gate status = ACPI_PSM_SUCCESS; 1420Sstevel@tonic-gate } else 1430Sstevel@tonic-gate status = acpi_eval_lnk(dip, prtp->Source, pci_irqp, 1440Sstevel@tonic-gate intr_flagp, acpipsmlnkp); 1450Sstevel@tonic-gate 1460Sstevel@tonic-gate break; 1470Sstevel@tonic-gate 1480Sstevel@tonic-gate } 1490Sstevel@tonic-gate 1500Sstevel@tonic-gate AcpiOsFree(rb.Pointer); 1510Sstevel@tonic-gate return (status); 1520Sstevel@tonic-gate } 1530Sstevel@tonic-gate 1540Sstevel@tonic-gate /* 1550Sstevel@tonic-gate * 1560Sstevel@tonic-gate * If the interrupt link device is already configured, 1570Sstevel@tonic-gate * stores polarity and sensitivity in the structure pointed to by 1580Sstevel@tonic-gate * intr_flagp, and irqno in the value pointed to by pci_irqp. 1590Sstevel@tonic-gate * 1600Sstevel@tonic-gate * Returns: 1610Sstevel@tonic-gate * ACPI_PSM_SUCCESS if the interrupt link device is already configured. 1620Sstevel@tonic-gate * ACPI_PSM_PARTIAL if configuration is needed. 1630Sstevel@tonic-gate * ACPI_PSM_FAILURE in case of error. 1640Sstevel@tonic-gate * 1650Sstevel@tonic-gate * When two devices share the same interrupt link device, and the 1660Sstevel@tonic-gate * link device is already configured (i.e. found in the irq cache) 1670Sstevel@tonic-gate * we need to use the already configured irq instead of reconfiguring 1680Sstevel@tonic-gate * the link device. 1690Sstevel@tonic-gate */ 1700Sstevel@tonic-gate static int 1710Sstevel@tonic-gate acpi_eval_lnk(dev_info_t *dip, char *lnkname, int *pci_irqp, 1720Sstevel@tonic-gate iflag_t *intr_flagp, acpi_psm_lnk_t *acpipsmlnkp) 1730Sstevel@tonic-gate { 1740Sstevel@tonic-gate ACPI_HANDLE tmpobj; 1750Sstevel@tonic-gate ACPI_HANDLE lnkobj; 1760Sstevel@tonic-gate int status; 1770Sstevel@tonic-gate 1780Sstevel@tonic-gate /* 1790Sstevel@tonic-gate * Convert the passed-in link device name to a handle 1800Sstevel@tonic-gate */ 1810Sstevel@tonic-gate if (AcpiGetHandle(NULL, lnkname, &lnkobj) != AE_OK) { 1820Sstevel@tonic-gate return (ACPI_PSM_FAILURE); 1830Sstevel@tonic-gate } 1840Sstevel@tonic-gate 1850Sstevel@tonic-gate /* 1860Sstevel@tonic-gate * Assume that the link device is invalid if no _CRS method 1870Sstevel@tonic-gate * exists, since _CRS method is a required method 1880Sstevel@tonic-gate */ 1890Sstevel@tonic-gate if (AcpiGetHandle(lnkobj, "_CRS", &tmpobj) != AE_OK) { 1900Sstevel@tonic-gate return (ACPI_PSM_FAILURE); 1910Sstevel@tonic-gate } 1920Sstevel@tonic-gate 1930Sstevel@tonic-gate ASSERT(acpipsmlnkp != NULL); 1940Sstevel@tonic-gate acpipsmlnkp->lnkobj = lnkobj; 1950Sstevel@tonic-gate if ((acpi_get_irq_lnk_cache_ent(lnkobj, pci_irqp, intr_flagp)) == 1960Sstevel@tonic-gate ACPI_PSM_SUCCESS) { 1970Sstevel@tonic-gate PSM_VERBOSE_IRQ((CE_CONT, "!psm: link object found from cache " 1980Sstevel@tonic-gate " for device %s, instance #%d, irq no %d\n", 1990Sstevel@tonic-gate ddi_get_name(dip), ddi_get_instance(dip), *pci_irqp)); 2000Sstevel@tonic-gate return (ACPI_PSM_SUCCESS); 2010Sstevel@tonic-gate } else { 2020Sstevel@tonic-gate if (acpica_eval_int(lnkobj, "_STA", &status) == AE_OK) { 2030Sstevel@tonic-gate acpipsmlnkp->device_status = (uchar_t)status; 2040Sstevel@tonic-gate } 2050Sstevel@tonic-gate 2060Sstevel@tonic-gate return (ACPI_PSM_PARTIAL); 2070Sstevel@tonic-gate } 2080Sstevel@tonic-gate } 2090Sstevel@tonic-gate 2100Sstevel@tonic-gate int 2110Sstevel@tonic-gate acpi_psm_init(char *module_name, int verbose_flags) 2120Sstevel@tonic-gate { 2130Sstevel@tonic-gate psm_module_name = module_name; 2140Sstevel@tonic-gate 2150Sstevel@tonic-gate psm_verbose = verbose_flags; 2160Sstevel@tonic-gate 2170Sstevel@tonic-gate if (AcpiGetHandle(NULL, "\\_SB", &acpi_sbobj) != AE_OK) { 2180Sstevel@tonic-gate cmn_err(CE_WARN, "!psm: get _SB failed"); 2190Sstevel@tonic-gate return (ACPI_PSM_FAILURE); 2200Sstevel@tonic-gate } 2210Sstevel@tonic-gate 2220Sstevel@tonic-gate mutex_init(&acpi_irq_cache_mutex, NULL, MUTEX_DEFAULT, NULL); 2230Sstevel@tonic-gate 2240Sstevel@tonic-gate return (ACPI_PSM_SUCCESS); 2250Sstevel@tonic-gate 2260Sstevel@tonic-gate } 2270Sstevel@tonic-gate 2280Sstevel@tonic-gate /* 2290Sstevel@tonic-gate * Return bus/dev/fn for PCI dip (note: not the parent "pci" node). 2300Sstevel@tonic-gate */ 2310Sstevel@tonic-gate 2320Sstevel@tonic-gate int 2330Sstevel@tonic-gate get_bdf(dev_info_t *dip, int *bus, int *device, int *func) 2340Sstevel@tonic-gate { 2350Sstevel@tonic-gate pci_regspec_t *pci_rp; 2360Sstevel@tonic-gate int len; 2370Sstevel@tonic-gate 2380Sstevel@tonic-gate if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 2390Sstevel@tonic-gate "reg", (int **)&pci_rp, (uint_t *)&len) != DDI_SUCCESS) 2400Sstevel@tonic-gate return (-1); 2410Sstevel@tonic-gate 2420Sstevel@tonic-gate if (len < (sizeof (pci_regspec_t) / sizeof (int))) { 2430Sstevel@tonic-gate ddi_prop_free(pci_rp); 2440Sstevel@tonic-gate return (-1); 2450Sstevel@tonic-gate } 2460Sstevel@tonic-gate if (bus != NULL) 2470Sstevel@tonic-gate *bus = (int)PCI_REG_BUS_G(pci_rp->pci_phys_hi); 2480Sstevel@tonic-gate if (device != NULL) 2490Sstevel@tonic-gate *device = (int)PCI_REG_DEV_G(pci_rp->pci_phys_hi); 2500Sstevel@tonic-gate if (func != NULL) 2510Sstevel@tonic-gate *func = (int)PCI_REG_FUNC_G(pci_rp->pci_phys_hi); 2520Sstevel@tonic-gate ddi_prop_free(pci_rp); 2530Sstevel@tonic-gate return (0); 2540Sstevel@tonic-gate } 2550Sstevel@tonic-gate 2560Sstevel@tonic-gate 2570Sstevel@tonic-gate /* 2580Sstevel@tonic-gate * Build the reserved ISA irq list, and store it in the table pointed to by 2590Sstevel@tonic-gate * reserved_irqs_table. The caller is responsible for allocating this table 2600Sstevel@tonic-gate * with a minimum of MAX_ISA_IRQ + 1 entries. 2610Sstevel@tonic-gate * 2620Sstevel@tonic-gate * The routine looks in the device tree at the subtree rooted at /isa 2630Sstevel@tonic-gate * for each of the devices under that node, if an interrupts property 2640Sstevel@tonic-gate * is present, its values are used to "reserve" irqs so that later ACPI 2650Sstevel@tonic-gate * configuration won't choose those irqs. 2660Sstevel@tonic-gate * 2670Sstevel@tonic-gate * In addition, if acpi_irq_check_elcr is set, will use ELCR register 2680Sstevel@tonic-gate * to identify reserved IRQs. 2690Sstevel@tonic-gate */ 2700Sstevel@tonic-gate void 2710Sstevel@tonic-gate build_reserved_irqlist(uchar_t *reserved_irqs_table) 2720Sstevel@tonic-gate { 2730Sstevel@tonic-gate dev_info_t *isanode = ddi_find_devinfo("isa", -1, 0); 2740Sstevel@tonic-gate dev_info_t *isa_child = 0; 2750Sstevel@tonic-gate int i; 2760Sstevel@tonic-gate uint_t elcrval; 2770Sstevel@tonic-gate 2780Sstevel@tonic-gate /* Initialize the reserved ISA IRQs: */ 2791456Sdmick for (i = 0; i <= MAX_ISA_IRQ; i++) 2800Sstevel@tonic-gate reserved_irqs_table[i] = 0; 2810Sstevel@tonic-gate 2820Sstevel@tonic-gate if (acpi_irq_check_elcr) { 2830Sstevel@tonic-gate 2840Sstevel@tonic-gate elcrval = (inb(ELCR_PORT2) << 8) | (inb(ELCR_PORT1)); 2850Sstevel@tonic-gate if (ELCR_EDGE(elcrval, 0) && ELCR_EDGE(elcrval, 1) && 2860Sstevel@tonic-gate ELCR_EDGE(elcrval, 2) && ELCR_EDGE(elcrval, 8) && 2870Sstevel@tonic-gate ELCR_EDGE(elcrval, 13)) { 2880Sstevel@tonic-gate /* valid ELCR */ 2891456Sdmick for (i = 0; i <= MAX_ISA_IRQ; i++) 2900Sstevel@tonic-gate if (!ELCR_LEVEL(elcrval, i)) 2910Sstevel@tonic-gate reserved_irqs_table[i] = 1; 2920Sstevel@tonic-gate } 2930Sstevel@tonic-gate } 2940Sstevel@tonic-gate 2950Sstevel@tonic-gate /* always check the isa devinfo nodes */ 2960Sstevel@tonic-gate 2970Sstevel@tonic-gate if (isanode != 0) { /* Found ISA */ 2980Sstevel@tonic-gate uint_t intcnt; /* Interrupt count */ 2990Sstevel@tonic-gate int *intrs; /* Interrupt values */ 3000Sstevel@tonic-gate 3010Sstevel@tonic-gate /* Load first child: */ 3020Sstevel@tonic-gate isa_child = ddi_get_child(isanode); 3030Sstevel@tonic-gate while (isa_child != 0) { /* Iterate over /isa children */ 3040Sstevel@tonic-gate /* if child has any interrupts, save them */ 3050Sstevel@tonic-gate if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, isa_child, 3060Sstevel@tonic-gate DDI_PROP_DONTPASS, "interrupts", &intrs, &intcnt) 3070Sstevel@tonic-gate == DDI_PROP_SUCCESS) { 3080Sstevel@tonic-gate /* 3090Sstevel@tonic-gate * iterate over child interrupt list, adding 3100Sstevel@tonic-gate * them to the reserved irq list 3110Sstevel@tonic-gate */ 3120Sstevel@tonic-gate while (intcnt-- > 0) { 3130Sstevel@tonic-gate /* 3140Sstevel@tonic-gate * Each value MUST be <= MAX_ISA_IRQ 3150Sstevel@tonic-gate */ 3160Sstevel@tonic-gate 3170Sstevel@tonic-gate if ((intrs[intcnt] > MAX_ISA_IRQ) || 3180Sstevel@tonic-gate (intrs[intcnt] < 0)) 3190Sstevel@tonic-gate continue; 3200Sstevel@tonic-gate 3210Sstevel@tonic-gate reserved_irqs_table[intrs[intcnt]] = 1; 3220Sstevel@tonic-gate } 3230Sstevel@tonic-gate ddi_prop_free(intrs); 3240Sstevel@tonic-gate } 3250Sstevel@tonic-gate isa_child = ddi_get_next_sibling(isa_child); 3260Sstevel@tonic-gate } 3270Sstevel@tonic-gate /* The isa node was held by ddi_find_devinfo, so release it */ 3280Sstevel@tonic-gate ndi_rele_devi(isanode); 3290Sstevel@tonic-gate } 3300Sstevel@tonic-gate 3310Sstevel@tonic-gate /* 3320Sstevel@tonic-gate * Reserve IRQ14 & IRQ15 for IDE. It shouldn't be hard-coded 3330Sstevel@tonic-gate * here but there's no other way to find the irqs for 3340Sstevel@tonic-gate * legacy-mode ata (since it's hard-coded in pci-ide also). 3350Sstevel@tonic-gate */ 3360Sstevel@tonic-gate reserved_irqs_table[14] = 1; 3370Sstevel@tonic-gate reserved_irqs_table[15] = 1; 3380Sstevel@tonic-gate } 3390Sstevel@tonic-gate 3400Sstevel@tonic-gate /* 3410Sstevel@tonic-gate * Examine devinfo node to determine if it is a PCI-PCI bridge 3420Sstevel@tonic-gate * 3430Sstevel@tonic-gate * Returns: 3440Sstevel@tonic-gate * 0 if not a bridge or error 3450Sstevel@tonic-gate * 1 if a bridge 3460Sstevel@tonic-gate */ 3470Sstevel@tonic-gate static int 3480Sstevel@tonic-gate psm_is_pci_bridge(dev_info_t *dip) 3490Sstevel@tonic-gate { 3500Sstevel@tonic-gate ddi_acc_handle_t cfg_handle; 3510Sstevel@tonic-gate int rv = 0; 3520Sstevel@tonic-gate 3530Sstevel@tonic-gate if (pci_config_setup(dip, &cfg_handle) == DDI_SUCCESS) { 3540Sstevel@tonic-gate rv = ((pci_config_get8(cfg_handle, PCI_CONF_BASCLASS) == 3550Sstevel@tonic-gate PCI_CLASS_BRIDGE) && (pci_config_get8(cfg_handle, 3560Sstevel@tonic-gate PCI_CONF_SUBCLASS) == PCI_BRIDGE_PCI)); 3570Sstevel@tonic-gate pci_config_teardown(&cfg_handle); 3580Sstevel@tonic-gate } 3590Sstevel@tonic-gate 3600Sstevel@tonic-gate return (rv); 3610Sstevel@tonic-gate } 3620Sstevel@tonic-gate 3630Sstevel@tonic-gate 3640Sstevel@tonic-gate /* 3650Sstevel@tonic-gate * Examines ACPI node for presence of _PRT object 3660Sstevel@tonic-gate * 3670Sstevel@tonic-gate * Returns: 3680Sstevel@tonic-gate * 0 if no _PRT or error 3690Sstevel@tonic-gate * 1 if _PRT is present 3700Sstevel@tonic-gate */ 3710Sstevel@tonic-gate static int 3720Sstevel@tonic-gate psm_node_has_prt(ACPI_HANDLE *ah) 3730Sstevel@tonic-gate { 3740Sstevel@tonic-gate ACPI_HANDLE rh; 3750Sstevel@tonic-gate 3760Sstevel@tonic-gate return (AcpiGetHandle(ah, "_PRT", &rh) == AE_OK); 3770Sstevel@tonic-gate } 3780Sstevel@tonic-gate 3790Sstevel@tonic-gate 3800Sstevel@tonic-gate /* 3810Sstevel@tonic-gate * Look first for an ACPI PCI bus node matching busid, then for a _PRT on the 3820Sstevel@tonic-gate * parent node; then drop into the bridge-chasing code (which will also 3830Sstevel@tonic-gate * look for _PRTs on the way up the tree of bridges) 3840Sstevel@tonic-gate * 3850Sstevel@tonic-gate * Stores polarity and sensitivity in the structure pointed to by 3860Sstevel@tonic-gate * intr_flagp, and irqno in the value pointed to by pci_irqp. * 3870Sstevel@tonic-gate * Returns: 3880Sstevel@tonic-gate * ACPI_PSM_SUCCESS on success. 3890Sstevel@tonic-gate * ACPI_PSM_PARTIAL to indicate need to configure the interrupt 3900Sstevel@tonic-gate * link device. 3910Sstevel@tonic-gate * ACPI_PSM_FAILURE if an error prevented the system from 3920Sstevel@tonic-gate * obtaining irq information for dip. 3930Sstevel@tonic-gate */ 3940Sstevel@tonic-gate int 3950Sstevel@tonic-gate acpi_translate_pci_irq(dev_info_t *dip, int ipin, int *pci_irqp, 3960Sstevel@tonic-gate iflag_t *intr_flagp, acpi_psm_lnk_t *acpipsmlnkp) 3970Sstevel@tonic-gate { 3980Sstevel@tonic-gate ACPI_HANDLE pciobj; 3990Sstevel@tonic-gate int status = AE_ERROR; 4000Sstevel@tonic-gate dev_info_t *curdip, *parentdip; 4010Sstevel@tonic-gate int curpin, curbus, curdev; 4020Sstevel@tonic-gate 4030Sstevel@tonic-gate 4040Sstevel@tonic-gate curpin = ipin; 4050Sstevel@tonic-gate curdip = dip; 4060Sstevel@tonic-gate while (curdip != ddi_root_node()) { 4070Sstevel@tonic-gate parentdip = ddi_get_parent(curdip); 4080Sstevel@tonic-gate ASSERT(parentdip != NULL); 4090Sstevel@tonic-gate 4100Sstevel@tonic-gate if (get_bdf(curdip, &curbus, &curdev, NULL) != 0) { 4110Sstevel@tonic-gate break; 4120Sstevel@tonic-gate } 4130Sstevel@tonic-gate 4144667Smh27603 status = acpica_get_handle(parentdip, &pciobj); 4150Sstevel@tonic-gate if ((status == AE_OK) && psm_node_has_prt(pciobj)) { 4160Sstevel@tonic-gate return (acpi_get_gsiv(curdip, pciobj, curdev, curpin, 4170Sstevel@tonic-gate pci_irqp, intr_flagp, acpipsmlnkp)); 4180Sstevel@tonic-gate } 4190Sstevel@tonic-gate 4200Sstevel@tonic-gate /* if we got here, we need to traverse a bridge upwards */ 4210Sstevel@tonic-gate if (!psm_is_pci_bridge(parentdip)) 4220Sstevel@tonic-gate break; 4230Sstevel@tonic-gate 4240Sstevel@tonic-gate /* 4250Sstevel@tonic-gate * This is the rotating scheme that Compaq is using 4260Sstevel@tonic-gate * and documented in the PCI-PCI spec. Also, if the 4270Sstevel@tonic-gate * PCI-PCI bridge is behind another PCI-PCI bridge, 4280Sstevel@tonic-gate * then it needs to keep ascending until an interrupt 4290Sstevel@tonic-gate * entry is found or the top is reached 4300Sstevel@tonic-gate */ 4310Sstevel@tonic-gate curpin = (curdev + curpin) % PCI_INTD; 4320Sstevel@tonic-gate curdip = parentdip; 4330Sstevel@tonic-gate } 4340Sstevel@tonic-gate 4350Sstevel@tonic-gate /* 4360Sstevel@tonic-gate * We should never, ever get here; didn't find a _PRT 4370Sstevel@tonic-gate */ 4380Sstevel@tonic-gate return (ACPI_PSM_FAILURE); 4390Sstevel@tonic-gate } 4400Sstevel@tonic-gate 4410Sstevel@tonic-gate /* 4420Sstevel@tonic-gate * Sets the irq resource of the lnk object to the requested irq value. 4430Sstevel@tonic-gate * 4440Sstevel@tonic-gate * Returns ACPI_PSM_SUCCESS on success, ACPI_PSM_FAILURE upon failure. 4450Sstevel@tonic-gate */ 4460Sstevel@tonic-gate int 4470Sstevel@tonic-gate acpi_set_irq_resource(acpi_psm_lnk_t *acpipsmlnkp, int irq) 4480Sstevel@tonic-gate { 4490Sstevel@tonic-gate ACPI_BUFFER rsb; 4500Sstevel@tonic-gate ACPI_RESOURCE *resp; 4510Sstevel@tonic-gate ACPI_RESOURCE *srsp; 4520Sstevel@tonic-gate ACPI_HANDLE lnkobj; 453941Smyers int srs_len, status; 4540Sstevel@tonic-gate 4550Sstevel@tonic-gate ASSERT(acpipsmlnkp != NULL); 4560Sstevel@tonic-gate 4570Sstevel@tonic-gate lnkobj = acpipsmlnkp->lnkobj; 4580Sstevel@tonic-gate 4590Sstevel@tonic-gate /* 4600Sstevel@tonic-gate * Fetch the possible resources for the link 4610Sstevel@tonic-gate */ 4620Sstevel@tonic-gate 4630Sstevel@tonic-gate rsb.Pointer = NULL; 4640Sstevel@tonic-gate rsb.Length = ACPI_ALLOCATE_BUFFER; 4650Sstevel@tonic-gate status = AcpiGetPossibleResources(lnkobj, &rsb); 4660Sstevel@tonic-gate if (status != AE_OK) { 4670Sstevel@tonic-gate cmn_err(CE_WARN, "!psm: set_irq: _PRS failed"); 4680Sstevel@tonic-gate return (ACPI_PSM_FAILURE); 4690Sstevel@tonic-gate } 4700Sstevel@tonic-gate 4710Sstevel@tonic-gate /* 4720Sstevel@tonic-gate * Find an IRQ resource descriptor to use as template 4730Sstevel@tonic-gate */ 4740Sstevel@tonic-gate srsp = NULL; 475941Smyers for (resp = rsb.Pointer; resp->Type != ACPI_RESOURCE_TYPE_END_TAG; 4760Sstevel@tonic-gate resp = ACPI_NEXT_RESOURCE(resp)) { 477941Smyers if ((resp->Type == ACPI_RESOURCE_TYPE_IRQ) || 478941Smyers (resp->Type == ACPI_RESOURCE_TYPE_EXTENDED_IRQ)) { 479941Smyers ACPI_RESOURCE *endtag; 4800Sstevel@tonic-gate /* 481941Smyers * Allocate enough room for this resource entry 482941Smyers * and one end tag following it 4830Sstevel@tonic-gate */ 484941Smyers srs_len = resp->Length + sizeof (*endtag); 485941Smyers srsp = kmem_zalloc(srs_len, KM_SLEEP); 486941Smyers bcopy(resp, srsp, resp->Length); 487941Smyers endtag = ACPI_NEXT_RESOURCE(srsp); 488941Smyers endtag->Type = ACPI_RESOURCE_TYPE_END_TAG; 489941Smyers endtag->Length = 0; 4900Sstevel@tonic-gate break; /* drop out of the loop */ 4910Sstevel@tonic-gate } 4920Sstevel@tonic-gate } 4930Sstevel@tonic-gate 4940Sstevel@tonic-gate /* 4950Sstevel@tonic-gate * We're done with the PRS values, toss 'em lest we forget 4960Sstevel@tonic-gate */ 4970Sstevel@tonic-gate AcpiOsFree(rsb.Pointer); 4980Sstevel@tonic-gate 4990Sstevel@tonic-gate if (srsp == NULL) 5000Sstevel@tonic-gate return (ACPI_PSM_FAILURE); 5010Sstevel@tonic-gate 5020Sstevel@tonic-gate /* 5030Sstevel@tonic-gate * The Interrupts[] array is always at least one entry 5040Sstevel@tonic-gate * long; see the definition of ACPI_RESOURCE. 5050Sstevel@tonic-gate */ 506941Smyers switch (srsp->Type) { 507941Smyers case ACPI_RESOURCE_TYPE_IRQ: 508941Smyers srsp->Data.Irq.InterruptCount = 1; 5090Sstevel@tonic-gate srsp->Data.Irq.Interrupts[0] = irq; 5100Sstevel@tonic-gate break; 511941Smyers case ACPI_RESOURCE_TYPE_EXTENDED_IRQ: 512941Smyers srsp->Data.ExtendedIrq.InterruptCount = 1; 5130Sstevel@tonic-gate srsp->Data.ExtendedIrq.Interrupts[0] = irq; 5140Sstevel@tonic-gate break; 5150Sstevel@tonic-gate } 5160Sstevel@tonic-gate 5170Sstevel@tonic-gate rsb.Pointer = srsp; 518941Smyers rsb.Length = srs_len; 5190Sstevel@tonic-gate status = AcpiSetCurrentResources(lnkobj, &rsb); 520941Smyers kmem_free(srsp, srs_len); 5210Sstevel@tonic-gate if (status != AE_OK) { 5220Sstevel@tonic-gate cmn_err(CE_WARN, "!psm: set_irq: _SRS failed"); 5230Sstevel@tonic-gate return (ACPI_PSM_FAILURE); 5240Sstevel@tonic-gate } 5250Sstevel@tonic-gate 5260Sstevel@tonic-gate if (acpica_eval_int(lnkobj, "_STA", &status) == AE_OK) { 5270Sstevel@tonic-gate acpipsmlnkp->device_status = (uchar_t)status; 5280Sstevel@tonic-gate return (ACPI_PSM_SUCCESS); 5290Sstevel@tonic-gate } else 5300Sstevel@tonic-gate return (ACPI_PSM_FAILURE); 5310Sstevel@tonic-gate } 5320Sstevel@tonic-gate 5330Sstevel@tonic-gate 5340Sstevel@tonic-gate /* 5350Sstevel@tonic-gate * 5360Sstevel@tonic-gate */ 5370Sstevel@tonic-gate static int 5380Sstevel@tonic-gate psm_acpi_edgelevel(UINT32 el) 5390Sstevel@tonic-gate { 5400Sstevel@tonic-gate switch (el) { 5410Sstevel@tonic-gate case ACPI_EDGE_SENSITIVE: 5420Sstevel@tonic-gate return (INTR_EL_EDGE); 5430Sstevel@tonic-gate case ACPI_LEVEL_SENSITIVE: 5440Sstevel@tonic-gate return (INTR_EL_LEVEL); 5450Sstevel@tonic-gate default: 5460Sstevel@tonic-gate /* el is a single bit; should never reach here */ 5470Sstevel@tonic-gate return (INTR_EL_CONFORM); 5480Sstevel@tonic-gate } 5490Sstevel@tonic-gate } 5500Sstevel@tonic-gate 5510Sstevel@tonic-gate 5520Sstevel@tonic-gate /* 5530Sstevel@tonic-gate * 5540Sstevel@tonic-gate */ 5550Sstevel@tonic-gate static int 5560Sstevel@tonic-gate psm_acpi_po(UINT32 po) 5570Sstevel@tonic-gate { 5580Sstevel@tonic-gate switch (po) { 5590Sstevel@tonic-gate case ACPI_ACTIVE_HIGH: 5600Sstevel@tonic-gate return (INTR_PO_ACTIVE_HIGH); 5610Sstevel@tonic-gate case ACPI_ACTIVE_LOW: 5620Sstevel@tonic-gate return (INTR_PO_ACTIVE_LOW); 5630Sstevel@tonic-gate default: 5640Sstevel@tonic-gate /* po is a single bit; should never reach here */ 5650Sstevel@tonic-gate return (INTR_PO_CONFORM); 5660Sstevel@tonic-gate } 5670Sstevel@tonic-gate } 5680Sstevel@tonic-gate 5690Sstevel@tonic-gate 5700Sstevel@tonic-gate /* 5710Sstevel@tonic-gate * Retrieves the current irq setting for the interrrupt link device. 5720Sstevel@tonic-gate * 5730Sstevel@tonic-gate * Stores polarity and sensitivity in the structure pointed to by 5740Sstevel@tonic-gate * intr_flagp, and irqno in the value pointed to by pci_irqp. 5750Sstevel@tonic-gate * 5760Sstevel@tonic-gate * Returns ACPI_PSM_SUCCESS on success, ACPI_PSM_FAILURE upon failure. 5770Sstevel@tonic-gate */ 5780Sstevel@tonic-gate int 5790Sstevel@tonic-gate acpi_get_current_irq_resource(acpi_psm_lnk_t *acpipsmlnkp, int *pci_irqp, 5800Sstevel@tonic-gate iflag_t *intr_flagp) 5810Sstevel@tonic-gate { 5820Sstevel@tonic-gate ACPI_HANDLE lnkobj; 5830Sstevel@tonic-gate ACPI_BUFFER rb; 5840Sstevel@tonic-gate ACPI_RESOURCE *rp; 5850Sstevel@tonic-gate int irq; 5860Sstevel@tonic-gate int status = ACPI_PSM_FAILURE; 5870Sstevel@tonic-gate 5880Sstevel@tonic-gate ASSERT(acpipsmlnkp != NULL); 5890Sstevel@tonic-gate lnkobj = acpipsmlnkp->lnkobj; 5900Sstevel@tonic-gate 5910Sstevel@tonic-gate if (!(acpipsmlnkp->device_status & STA_PRESENT) || 5920Sstevel@tonic-gate !(acpipsmlnkp->device_status & STA_ENABLE)) { 5930Sstevel@tonic-gate PSM_VERBOSE_IRQ((CE_WARN, "!psm: crs device either not " 5940Sstevel@tonic-gate "present or disabled, status 0x%x", 5950Sstevel@tonic-gate acpipsmlnkp->device_status)); 5960Sstevel@tonic-gate return (ACPI_PSM_FAILURE); 5970Sstevel@tonic-gate } 5980Sstevel@tonic-gate 5990Sstevel@tonic-gate rb.Pointer = NULL; 6000Sstevel@tonic-gate rb.Length = ACPI_ALLOCATE_BUFFER; 6010Sstevel@tonic-gate if (AcpiGetCurrentResources(lnkobj, &rb) != AE_OK) { 6020Sstevel@tonic-gate PSM_VERBOSE_IRQ((CE_WARN, "!psm: no crs object found or" 6030Sstevel@tonic-gate " evaluation failed")); 6040Sstevel@tonic-gate return (ACPI_PSM_FAILURE); 6050Sstevel@tonic-gate } 6060Sstevel@tonic-gate 6070Sstevel@tonic-gate irq = -1; 608941Smyers for (rp = rb.Pointer; rp->Type != ACPI_RESOURCE_TYPE_END_TAG; 609941Smyers rp = ACPI_NEXT_RESOURCE(rp)) { 610941Smyers if (rp->Type == ACPI_RESOURCE_TYPE_IRQ) { 6110Sstevel@tonic-gate if (irq > 0) { 6120Sstevel@tonic-gate PSM_VERBOSE_IRQ((CE_WARN, "!psm: multiple IRQ" 6130Sstevel@tonic-gate " from _CRS ")); 6140Sstevel@tonic-gate status = ACPI_PSM_FAILURE; 6150Sstevel@tonic-gate break; 6160Sstevel@tonic-gate } 6170Sstevel@tonic-gate 618941Smyers if (rp->Data.Irq.InterruptCount != 1) { 6190Sstevel@tonic-gate PSM_VERBOSE_IRQ((CE_WARN, "!psm: <>1 interrupt" 6200Sstevel@tonic-gate " from _CRS ")); 6210Sstevel@tonic-gate status = ACPI_PSM_FAILURE; 6220Sstevel@tonic-gate break; 6230Sstevel@tonic-gate } 6240Sstevel@tonic-gate 6250Sstevel@tonic-gate intr_flagp->intr_el = psm_acpi_edgelevel( 626*5295Srandyf rp->Data.Irq.Triggering); 6270Sstevel@tonic-gate intr_flagp->intr_po = psm_acpi_po( 628*5295Srandyf rp->Data.Irq.Polarity); 6290Sstevel@tonic-gate irq = rp->Data.Irq.Interrupts[0]; 6300Sstevel@tonic-gate status = ACPI_PSM_SUCCESS; 631941Smyers } else if (rp->Type == ACPI_RESOURCE_TYPE_EXTENDED_IRQ) { 6320Sstevel@tonic-gate if (irq > 0) { 6330Sstevel@tonic-gate PSM_VERBOSE_IRQ((CE_WARN, "!psm: multiple IRQ" 6340Sstevel@tonic-gate " from _CRS ")); 6350Sstevel@tonic-gate status = ACPI_PSM_FAILURE; 6360Sstevel@tonic-gate break; 6370Sstevel@tonic-gate } 6380Sstevel@tonic-gate 639941Smyers if (rp->Data.ExtendedIrq.InterruptCount != 1) { 6400Sstevel@tonic-gate PSM_VERBOSE_IRQ((CE_WARN, "!psm: <>1 interrupt" 6410Sstevel@tonic-gate " from _CRS ")); 6420Sstevel@tonic-gate status = ACPI_PSM_FAILURE; 6430Sstevel@tonic-gate break; 6440Sstevel@tonic-gate } 6450Sstevel@tonic-gate 6460Sstevel@tonic-gate intr_flagp->intr_el = psm_acpi_edgelevel( 647*5295Srandyf rp->Data.ExtendedIrq.Triggering); 6480Sstevel@tonic-gate intr_flagp->intr_po = psm_acpi_po( 649*5295Srandyf rp->Data.ExtendedIrq.Polarity); 6500Sstevel@tonic-gate irq = rp->Data.ExtendedIrq.Interrupts[0]; 6510Sstevel@tonic-gate status = ACPI_PSM_SUCCESS; 6520Sstevel@tonic-gate } 6530Sstevel@tonic-gate } 6540Sstevel@tonic-gate 6550Sstevel@tonic-gate AcpiOsFree(rb.Pointer); 6560Sstevel@tonic-gate if (status == ACPI_PSM_SUCCESS) { 6570Sstevel@tonic-gate *pci_irqp = irq; 6580Sstevel@tonic-gate } 6590Sstevel@tonic-gate 6600Sstevel@tonic-gate return (status); 6610Sstevel@tonic-gate } 6620Sstevel@tonic-gate 6630Sstevel@tonic-gate /* 6640Sstevel@tonic-gate * Searches for the given IRQ in the irqlist passed in. 6650Sstevel@tonic-gate * 6660Sstevel@tonic-gate * If multiple matches exist, this returns true on the first match. 6670Sstevel@tonic-gate * Returns the interrupt flags, if a match was found, in `intr_flagp' if 6680Sstevel@tonic-gate * it's passed in non-NULL 6690Sstevel@tonic-gate */ 6700Sstevel@tonic-gate int 6710Sstevel@tonic-gate acpi_irqlist_find_irq(acpi_irqlist_t *irqlistp, int irq, iflag_t *intr_flagp) 6720Sstevel@tonic-gate { 6730Sstevel@tonic-gate int found = 0; 6740Sstevel@tonic-gate int i; 6750Sstevel@tonic-gate 6760Sstevel@tonic-gate while (irqlistp != NULL && !found) { 6770Sstevel@tonic-gate for (i = 0; i < irqlistp->num_irqs; i++) { 6780Sstevel@tonic-gate if (irqlistp->irqs[i] == irq) { 6790Sstevel@tonic-gate if (intr_flagp) 6800Sstevel@tonic-gate *intr_flagp = irqlistp->intr_flags; 6810Sstevel@tonic-gate found = 1; 6820Sstevel@tonic-gate break; /* out of for() */ 6830Sstevel@tonic-gate } 6840Sstevel@tonic-gate } 6850Sstevel@tonic-gate } 6860Sstevel@tonic-gate 6870Sstevel@tonic-gate return (found ? ACPI_PSM_SUCCESS : ACPI_PSM_FAILURE); 6880Sstevel@tonic-gate } 6890Sstevel@tonic-gate 6900Sstevel@tonic-gate /* 6910Sstevel@tonic-gate * Frees the irqlist allocated by acpi_get_possible_irq_resource. 6920Sstevel@tonic-gate * It takes a count of number of entries in the list. 6930Sstevel@tonic-gate */ 6940Sstevel@tonic-gate void 6950Sstevel@tonic-gate acpi_free_irqlist(acpi_irqlist_t *irqlistp) 6960Sstevel@tonic-gate { 6970Sstevel@tonic-gate acpi_irqlist_t *freednode; 6980Sstevel@tonic-gate 6990Sstevel@tonic-gate while (irqlistp != NULL) { 7000Sstevel@tonic-gate /* Free the irq list */ 7010Sstevel@tonic-gate kmem_free(irqlistp->irqs, irqlistp->num_irqs * 7020Sstevel@tonic-gate sizeof (int32_t)); 7030Sstevel@tonic-gate 7040Sstevel@tonic-gate freednode = irqlistp; 7050Sstevel@tonic-gate irqlistp = irqlistp->next; 7060Sstevel@tonic-gate kmem_free(freednode, sizeof (acpi_irqlist_t)); 7070Sstevel@tonic-gate } 7080Sstevel@tonic-gate } 7090Sstevel@tonic-gate 7100Sstevel@tonic-gate /* 7110Sstevel@tonic-gate * Creates a new entry in the given irqlist with the information passed in. 7120Sstevel@tonic-gate */ 7130Sstevel@tonic-gate static void 7140Sstevel@tonic-gate acpi_add_irqlist_entry(acpi_irqlist_t **irqlistp, uint32_t *irqlist, 7150Sstevel@tonic-gate int irqlist_len, iflag_t *intr_flagp) 7160Sstevel@tonic-gate { 7170Sstevel@tonic-gate acpi_irqlist_t *newent; 7180Sstevel@tonic-gate 7190Sstevel@tonic-gate ASSERT(irqlist != NULL); 7200Sstevel@tonic-gate ASSERT(intr_flagp != NULL); 7210Sstevel@tonic-gate 7220Sstevel@tonic-gate newent = kmem_alloc(sizeof (acpi_irqlist_t), KM_SLEEP); 7230Sstevel@tonic-gate newent->intr_flags = *intr_flagp; 7240Sstevel@tonic-gate newent->irqs = irqlist; 7250Sstevel@tonic-gate newent->num_irqs = irqlist_len; 7260Sstevel@tonic-gate newent->next = *irqlistp; 7270Sstevel@tonic-gate 7280Sstevel@tonic-gate *irqlistp = newent; 7290Sstevel@tonic-gate } 7300Sstevel@tonic-gate 7310Sstevel@tonic-gate 7320Sstevel@tonic-gate /* 7330Sstevel@tonic-gate * Retrieves a list of possible interrupt settings for the interrupt link 7340Sstevel@tonic-gate * device. 7350Sstevel@tonic-gate * 7360Sstevel@tonic-gate * Stores polarity and sensitivity in the structure pointed to by intr_flagp. 7370Sstevel@tonic-gate * Updates value pointed to by irqlistp with the address of a table it 7380Sstevel@tonic-gate * allocates. where interrupt numbers are stored. Stores the number of entries 7390Sstevel@tonic-gate * in this table in the value pointed to by num_entriesp; 7400Sstevel@tonic-gate * 7410Sstevel@tonic-gate * Each element in this table is of type int32_t. The table should be later 7420Sstevel@tonic-gate * freed by caller via acpi_free_irq_list(). 7430Sstevel@tonic-gate * 7440Sstevel@tonic-gate * Returns ACPI_PSM_SUCCESS on success and ACPI_PSM_FAILURE upon failure 7450Sstevel@tonic-gate */ 7460Sstevel@tonic-gate int 7470Sstevel@tonic-gate acpi_get_possible_irq_resources(acpi_psm_lnk_t *acpipsmlnkp, 7480Sstevel@tonic-gate acpi_irqlist_t **irqlistp) 7490Sstevel@tonic-gate { 7500Sstevel@tonic-gate ACPI_HANDLE lnkobj; 7510Sstevel@tonic-gate ACPI_BUFFER rsb; 7520Sstevel@tonic-gate ACPI_RESOURCE *resp; 7530Sstevel@tonic-gate int status; 7540Sstevel@tonic-gate 7550Sstevel@tonic-gate int i, el, po, irqlist_len; 7561560Smyers uint32_t *irqlist; 7571560Smyers void *tmplist; 7580Sstevel@tonic-gate iflag_t intr_flags; 7590Sstevel@tonic-gate 7600Sstevel@tonic-gate ASSERT(acpipsmlnkp != NULL); 7610Sstevel@tonic-gate lnkobj = acpipsmlnkp->lnkobj; 7620Sstevel@tonic-gate 7630Sstevel@tonic-gate rsb.Pointer = NULL; 7640Sstevel@tonic-gate rsb.Length = ACPI_ALLOCATE_BUFFER; 7650Sstevel@tonic-gate status = AcpiGetPossibleResources(lnkobj, &rsb); 7660Sstevel@tonic-gate if (status != AE_OK) { 7670Sstevel@tonic-gate cmn_err(CE_WARN, "!psm: get_irq: _PRS failed"); 7680Sstevel@tonic-gate return (ACPI_PSM_FAILURE); 7690Sstevel@tonic-gate } 7700Sstevel@tonic-gate 7710Sstevel@tonic-gate /* 7720Sstevel@tonic-gate * Scan the resources looking for an interrupt resource 7730Sstevel@tonic-gate */ 7740Sstevel@tonic-gate *irqlistp = 0; 775941Smyers for (resp = rsb.Pointer; resp->Type != ACPI_RESOURCE_TYPE_END_TAG; 7760Sstevel@tonic-gate resp = ACPI_NEXT_RESOURCE(resp)) { 777941Smyers switch (resp->Type) { 778941Smyers case ACPI_RESOURCE_TYPE_IRQ: 779941Smyers irqlist_len = resp->Data.Irq.InterruptCount; 7800Sstevel@tonic-gate tmplist = resp->Data.Irq.Interrupts; 781941Smyers el = resp->Data.Irq.Triggering; 782941Smyers po = resp->Data.Irq.Polarity; 7830Sstevel@tonic-gate break; 784941Smyers case ACPI_RESOURCE_TYPE_EXTENDED_IRQ: 785941Smyers irqlist_len = resp->Data.ExtendedIrq.InterruptCount; 7860Sstevel@tonic-gate tmplist = resp->Data.ExtendedIrq.Interrupts; 787941Smyers el = resp->Data.ExtendedIrq.Triggering; 788941Smyers po = resp->Data.ExtendedIrq.Polarity; 7890Sstevel@tonic-gate break; 7900Sstevel@tonic-gate default: 7910Sstevel@tonic-gate continue; 7920Sstevel@tonic-gate } 7931560Smyers 7941560Smyers if (resp->Type != ACPI_RESOURCE_TYPE_IRQ && 7951560Smyers resp->Type != ACPI_RESOURCE_TYPE_EXTENDED_IRQ) { 7961560Smyers cmn_err(CE_WARN, "!psm: get_irq: no IRQ resource"); 7971560Smyers return (ACPI_PSM_FAILURE); 7981560Smyers } 7991560Smyers 8000Sstevel@tonic-gate /* NEEDSWORK: move this into add_irqlist_entry someday */ 8010Sstevel@tonic-gate irqlist = kmem_zalloc(irqlist_len * sizeof (*irqlist), 802*5295Srandyf KM_SLEEP); 8030Sstevel@tonic-gate for (i = 0; i < irqlist_len; i++) 8041560Smyers if (resp->Type == ACPI_RESOURCE_TYPE_IRQ) 8051560Smyers irqlist[i] = ((uint8_t *)tmplist)[i]; 8061560Smyers else 8071560Smyers irqlist[i] = ((uint32_t *)tmplist)[i]; 8080Sstevel@tonic-gate intr_flags.intr_el = psm_acpi_edgelevel(el); 8090Sstevel@tonic-gate intr_flags.intr_po = psm_acpi_po(po); 8100Sstevel@tonic-gate acpi_add_irqlist_entry(irqlistp, irqlist, irqlist_len, 811*5295Srandyf &intr_flags); 8120Sstevel@tonic-gate } 8130Sstevel@tonic-gate 8140Sstevel@tonic-gate AcpiOsFree(rsb.Pointer); 8150Sstevel@tonic-gate return (irqlistp == NULL ? ACPI_PSM_FAILURE : ACPI_PSM_SUCCESS); 8160Sstevel@tonic-gate } 8170Sstevel@tonic-gate 8180Sstevel@tonic-gate /* 8190Sstevel@tonic-gate * Adds a new cache entry to the irq cache which maps an irq and 8200Sstevel@tonic-gate * its attributes to PCI bus/dev/ipin and optionally to its associated ACPI 8210Sstevel@tonic-gate * interrupt link device object. 8220Sstevel@tonic-gate */ 8230Sstevel@tonic-gate void 8240Sstevel@tonic-gate acpi_new_irq_cache_ent(int bus, int dev, int ipin, int pci_irq, 8250Sstevel@tonic-gate iflag_t *intr_flagp, acpi_psm_lnk_t *acpipsmlnkp) 8260Sstevel@tonic-gate { 8270Sstevel@tonic-gate int newsize; 8280Sstevel@tonic-gate irq_cache_t *new_arr, *ep; 8290Sstevel@tonic-gate 8300Sstevel@tonic-gate mutex_enter(&acpi_irq_cache_mutex); 8310Sstevel@tonic-gate if (irq_cache_valid >= irq_cache_len) { 8320Sstevel@tonic-gate /* initially, or re-, allocate array */ 8330Sstevel@tonic-gate 8340Sstevel@tonic-gate newsize = (irq_cache_len ? 8350Sstevel@tonic-gate irq_cache_len * 2 : IRQ_CACHE_INITLEN); 8360Sstevel@tonic-gate new_arr = kmem_zalloc(newsize * sizeof (irq_cache_t), KM_SLEEP); 8370Sstevel@tonic-gate if (irq_cache_len != 0) { 8380Sstevel@tonic-gate /* realloc: copy data, free old */ 8390Sstevel@tonic-gate bcopy(irq_cache_table, new_arr, 8400Sstevel@tonic-gate irq_cache_len * sizeof (irq_cache_t)); 8410Sstevel@tonic-gate kmem_free(irq_cache_table, 8420Sstevel@tonic-gate irq_cache_len * sizeof (irq_cache_t)); 8430Sstevel@tonic-gate } 8440Sstevel@tonic-gate irq_cache_len = newsize; 8450Sstevel@tonic-gate irq_cache_table = new_arr; 8460Sstevel@tonic-gate } 8470Sstevel@tonic-gate ep = &irq_cache_table[irq_cache_valid++]; 8480Sstevel@tonic-gate ep->bus = (uchar_t)bus; 8490Sstevel@tonic-gate ep->dev = (uchar_t)dev; 8500Sstevel@tonic-gate ep->ipin = (uchar_t)ipin; 8510Sstevel@tonic-gate ep->flags = *intr_flagp; 8520Sstevel@tonic-gate ep->irq = pci_irq; 8530Sstevel@tonic-gate ASSERT(acpipsmlnkp != NULL); 8540Sstevel@tonic-gate ep->lnkobj = acpipsmlnkp->lnkobj; 8550Sstevel@tonic-gate mutex_exit(&acpi_irq_cache_mutex); 8560Sstevel@tonic-gate } 8570Sstevel@tonic-gate 8580Sstevel@tonic-gate 8590Sstevel@tonic-gate /* 8600Sstevel@tonic-gate * Searches the irq caches for the given bus/dev/ipin. 8610Sstevel@tonic-gate * 8620Sstevel@tonic-gate * If info is found, stores polarity and sensitivity in the structure 8630Sstevel@tonic-gate * pointed to by intr_flagp, and irqno in the value pointed to by pci_irqp, 8640Sstevel@tonic-gate * and returns ACPI_PSM_SUCCESS. 8650Sstevel@tonic-gate * Otherwise, ACPI_PSM_FAILURE is returned. 8660Sstevel@tonic-gate */ 8670Sstevel@tonic-gate int 8680Sstevel@tonic-gate acpi_get_irq_cache_ent(uchar_t bus, uchar_t dev, int ipin, 8690Sstevel@tonic-gate int *pci_irqp, iflag_t *intr_flagp) 8700Sstevel@tonic-gate { 8710Sstevel@tonic-gate 8720Sstevel@tonic-gate irq_cache_t *irqcachep; 8730Sstevel@tonic-gate int i; 8740Sstevel@tonic-gate int ret = ACPI_PSM_FAILURE; 8750Sstevel@tonic-gate 8760Sstevel@tonic-gate mutex_enter(&acpi_irq_cache_mutex); 8770Sstevel@tonic-gate for (irqcachep = irq_cache_table, i = 0; i < irq_cache_valid; 8780Sstevel@tonic-gate irqcachep++, i++) 8790Sstevel@tonic-gate if ((irqcachep->bus == bus) && 8800Sstevel@tonic-gate (irqcachep->dev == dev) && 8810Sstevel@tonic-gate (irqcachep->ipin == ipin)) { 8820Sstevel@tonic-gate ASSERT(pci_irqp != NULL && intr_flagp != NULL); 8830Sstevel@tonic-gate *pci_irqp = irqcachep->irq; 8840Sstevel@tonic-gate *intr_flagp = irqcachep->flags; 8850Sstevel@tonic-gate ret = ACPI_PSM_SUCCESS; 8860Sstevel@tonic-gate break; 8870Sstevel@tonic-gate } 8880Sstevel@tonic-gate 8890Sstevel@tonic-gate mutex_exit(&acpi_irq_cache_mutex); 8900Sstevel@tonic-gate return (ret); 8910Sstevel@tonic-gate } 8920Sstevel@tonic-gate 8930Sstevel@tonic-gate /* 8940Sstevel@tonic-gate * Searches the irq caches for the given interrupt lnk device object. 8950Sstevel@tonic-gate * 8960Sstevel@tonic-gate * If info is found, stores polarity and sensitivity in the structure 8970Sstevel@tonic-gate * pointed to by intr_flagp, and irqno in the value pointed to by pci_irqp, 8980Sstevel@tonic-gate * and returns ACPI_PSM_SUCCESS. 8990Sstevel@tonic-gate * Otherwise, ACPI_PSM_FAILURE is returned. 9000Sstevel@tonic-gate */ 9010Sstevel@tonic-gate int 9020Sstevel@tonic-gate acpi_get_irq_lnk_cache_ent(ACPI_HANDLE lnkobj, int *pci_irqp, 9030Sstevel@tonic-gate iflag_t *intr_flagp) 9040Sstevel@tonic-gate { 9050Sstevel@tonic-gate 9060Sstevel@tonic-gate irq_cache_t *irqcachep; 9070Sstevel@tonic-gate int i; 9080Sstevel@tonic-gate int ret = ACPI_PSM_FAILURE; 9090Sstevel@tonic-gate 9100Sstevel@tonic-gate if (lnkobj == NULL) 9110Sstevel@tonic-gate return (ACPI_PSM_FAILURE); 9120Sstevel@tonic-gate 9130Sstevel@tonic-gate mutex_enter(&acpi_irq_cache_mutex); 9140Sstevel@tonic-gate for (irqcachep = irq_cache_table, i = 0; i < irq_cache_valid; 9150Sstevel@tonic-gate irqcachep++, i++) 9160Sstevel@tonic-gate if (irqcachep->lnkobj == lnkobj) { 9170Sstevel@tonic-gate ASSERT(pci_irqp != NULL); 9180Sstevel@tonic-gate *pci_irqp = irqcachep->irq; 9190Sstevel@tonic-gate ASSERT(intr_flagp != NULL); 9200Sstevel@tonic-gate *intr_flagp = irqcachep->flags; 9210Sstevel@tonic-gate ret = ACPI_PSM_SUCCESS; 9220Sstevel@tonic-gate break; 9230Sstevel@tonic-gate } 9240Sstevel@tonic-gate mutex_exit(&acpi_irq_cache_mutex); 9250Sstevel@tonic-gate return (ret); 9260Sstevel@tonic-gate } 9270Sstevel@tonic-gate 928*5295Srandyf /* 929*5295Srandyf * Walk the irq_cache_table and re-configure the link device to 930*5295Srandyf * the saved state. 931*5295Srandyf */ 932*5295Srandyf void 933*5295Srandyf acpi_restore_link_devices(void) 934*5295Srandyf { 935*5295Srandyf irq_cache_t *irqcachep; 936*5295Srandyf acpi_psm_lnk_t psmlnk; 937*5295Srandyf int i, status; 938*5295Srandyf 939*5295Srandyf /* XXX: may not need to hold this mutex */ 940*5295Srandyf mutex_enter(&acpi_irq_cache_mutex); 941*5295Srandyf for (irqcachep = irq_cache_table, i = 0; i < irq_cache_valid; 942*5295Srandyf irqcachep++, i++) { 943*5295Srandyf /* only field used from psmlnk in set_irq is lnkobj */ 944*5295Srandyf psmlnk.lnkobj = irqcachep->lnkobj; 945*5295Srandyf status = acpi_set_irq_resource(&psmlnk, irqcachep->irq); 946*5295Srandyf /* warn if set_irq failed; soldier on */ 947*5295Srandyf if (status != ACPI_PSM_SUCCESS) 948*5295Srandyf cmn_err(CE_WARN, "restore_link failed for IRQ 0x%x\n", 949*5295Srandyf irqcachep->irq); 950*5295Srandyf } 951*5295Srandyf mutex_exit(&acpi_irq_cache_mutex); 952*5295Srandyf } 953*5295Srandyf 9540Sstevel@tonic-gate int 9550Sstevel@tonic-gate acpi_poweroff(void) 9560Sstevel@tonic-gate { 9572025Smyers extern int acpica_powering_off; 9582025Smyers 9590Sstevel@tonic-gate PSM_VERBOSE_POWEROFF(("acpi_poweroff: starting poweroff\n")); 9600Sstevel@tonic-gate 9612025Smyers acpica_powering_off = 1; 9622025Smyers 9630Sstevel@tonic-gate if (AcpiEnterSleepStatePrep(5) != AE_OK) 9640Sstevel@tonic-gate return (1); 9650Sstevel@tonic-gate ACPI_DISABLE_IRQS(); 9660Sstevel@tonic-gate if (AcpiEnterSleepState(5) != AE_OK) { 9670Sstevel@tonic-gate ACPI_ENABLE_IRQS(); 9680Sstevel@tonic-gate return (1); 9690Sstevel@tonic-gate } 9700Sstevel@tonic-gate ACPI_ENABLE_IRQS(); 9710Sstevel@tonic-gate 9720Sstevel@tonic-gate /* we should be off; if we get here it's an error */ 9730Sstevel@tonic-gate PSM_VERBOSE_POWEROFF(("acpi_poweroff: failed to actually power off\n")); 9740Sstevel@tonic-gate return (1); 9750Sstevel@tonic-gate } 976347Smyers 977347Smyers 978347Smyers /* 979347Smyers * psm_set_elcr() sets ELCR bit for specified vector 980347Smyers */ 981347Smyers void 982347Smyers psm_set_elcr(int vecno, int val) 983347Smyers { 984347Smyers int elcr_port = ELCR_PORT1 + (vecno >> 3); 985347Smyers int elcr_bit = 1 << (vecno & 0x07); 986347Smyers 987347Smyers ASSERT((vecno >= 0) && (vecno < 16)); 988347Smyers 989347Smyers if (val) { 990347Smyers /* set bit to force level-triggered mode */ 991347Smyers outb(elcr_port, inb(elcr_port) | elcr_bit); 992347Smyers } else { 993347Smyers /* clear bit to force edge-triggered mode */ 994347Smyers outb(elcr_port, inb(elcr_port) & ~elcr_bit); 995347Smyers } 996347Smyers } 997347Smyers 998347Smyers /* 999347Smyers * psm_get_elcr() returns status of ELCR bit for specific vector 1000347Smyers */ 1001347Smyers int 1002347Smyers psm_get_elcr(int vecno) 1003347Smyers { 1004347Smyers int elcr_port = ELCR_PORT1 + (vecno >> 3); 1005347Smyers int elcr_bit = 1 << (vecno & 0x07); 1006347Smyers 1007347Smyers ASSERT((vecno >= 0) && (vecno < 16)); 1008347Smyers 1009347Smyers return ((inb(elcr_port) & elcr_bit) ? 1 : 0); 1010347Smyers } 1011