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 /*
2212159SSusan.Scheufele@Sun.COM * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
230Sstevel@tonic-gate */
240Sstevel@tonic-gate
250Sstevel@tonic-gate #include <sys/types.h>
260Sstevel@tonic-gate #include <sys/param.h>
270Sstevel@tonic-gate #include <sys/cmn_err.h>
280Sstevel@tonic-gate #include <sys/promif.h>
290Sstevel@tonic-gate #include <sys/acpi/acpi.h>
300Sstevel@tonic-gate #include <sys/acpica.h>
310Sstevel@tonic-gate #include <sys/sunddi.h>
320Sstevel@tonic-gate #include <sys/ddi.h>
330Sstevel@tonic-gate #include <sys/ddi_impldefs.h>
340Sstevel@tonic-gate #include <sys/pci.h>
350Sstevel@tonic-gate #include <sys/debug.h>
360Sstevel@tonic-gate #include <sys/psm_common.h>
370Sstevel@tonic-gate #include <sys/sunndi.h>
380Sstevel@tonic-gate #include <sys/ksynch.h>
390Sstevel@tonic-gate
400Sstevel@tonic-gate /* Global configurables */
410Sstevel@tonic-gate
420Sstevel@tonic-gate char *psm_module_name; /* used to store name of psm module */
430Sstevel@tonic-gate
440Sstevel@tonic-gate /*
450Sstevel@tonic-gate * acpi_irq_check_elcr: when set elcr will also be consulted for building
46347Smyers * the reserved irq list. When 0 (false), the existing state of the ELCR
47347Smyers * is ignored when selecting a vector during IRQ translation, and the ELCR
48347Smyers * is programmed to the proper setting for the type of bus (level-triggered
49347Smyers * for PCI, edge-triggered for non-PCI). When non-zero (true), vectors
50347Smyers * set to edge-mode will not be used when in PIC-mode. The default value
51347Smyers * is 0 (false). Note that ACPI's SCI vector is always set to conform to
52347Smyers * ACPI-specification regardless of this.
53347Smyers *
540Sstevel@tonic-gate */
55347Smyers int acpi_irq_check_elcr = 0;
560Sstevel@tonic-gate
570Sstevel@tonic-gate int psm_verbose = 0;
580Sstevel@tonic-gate
590Sstevel@tonic-gate #define PSM_VERBOSE_IRQ(fmt) \
600Sstevel@tonic-gate if (psm_verbose & PSM_VERBOSE_IRQ_FLAG) \
610Sstevel@tonic-gate cmn_err fmt;
620Sstevel@tonic-gate
630Sstevel@tonic-gate #define PSM_VERBOSE_POWEROFF(fmt) \
640Sstevel@tonic-gate if (psm_verbose & PSM_VERBOSE_POWEROFF_FLAG || \
650Sstevel@tonic-gate psm_verbose & PSM_VERBOSE_POWEROFF_PAUSE_FLAG) \
660Sstevel@tonic-gate prom_printf fmt;
670Sstevel@tonic-gate
680Sstevel@tonic-gate #define PSM_VERBOSE_POWEROFF_PAUSE(fmt) \
690Sstevel@tonic-gate if (psm_verbose & PSM_VERBOSE_POWEROFF_FLAG || \
700Sstevel@tonic-gate psm_verbose & PSM_VERBOSE_POWEROFF_PAUSE_FLAG) {\
710Sstevel@tonic-gate prom_printf fmt; \
720Sstevel@tonic-gate if (psm_verbose & PSM_VERBOSE_POWEROFF_PAUSE_FLAG) \
730Sstevel@tonic-gate (void) goany(); \
740Sstevel@tonic-gate }
750Sstevel@tonic-gate
760Sstevel@tonic-gate
770Sstevel@tonic-gate /* Local storage */
780Sstevel@tonic-gate static ACPI_HANDLE acpi_sbobj = NULL;
790Sstevel@tonic-gate static kmutex_t acpi_irq_cache_mutex;
800Sstevel@tonic-gate
810Sstevel@tonic-gate /*
820Sstevel@tonic-gate * irq_cache_table is a list that serves a two-key cache. It is used
830Sstevel@tonic-gate * as a pci busid/devid/ipin <-> irq cache and also as a acpi
840Sstevel@tonic-gate * interrupt lnk <-> irq cache.
850Sstevel@tonic-gate */
860Sstevel@tonic-gate static irq_cache_t *irq_cache_table;
870Sstevel@tonic-gate
880Sstevel@tonic-gate #define IRQ_CACHE_INITLEN 20
890Sstevel@tonic-gate static int irq_cache_len = 0;
900Sstevel@tonic-gate static int irq_cache_valid = 0;
910Sstevel@tonic-gate
920Sstevel@tonic-gate static int acpi_get_gsiv(dev_info_t *dip, ACPI_HANDLE pciobj, int devno,
930Sstevel@tonic-gate int ipin, int *pci_irqp, iflag_t *iflagp, acpi_psm_lnk_t *acpipsmlnkp);
940Sstevel@tonic-gate
950Sstevel@tonic-gate static int acpi_eval_lnk(dev_info_t *dip, char *lnkname,
960Sstevel@tonic-gate int *pci_irqp, iflag_t *intr_flagp, acpi_psm_lnk_t *acpipsmlnkp);
970Sstevel@tonic-gate
980Sstevel@tonic-gate static int acpi_get_irq_lnk_cache_ent(ACPI_HANDLE lnkobj, int *pci_irqp,
990Sstevel@tonic-gate iflag_t *intr_flagp);
1000Sstevel@tonic-gate
1010Sstevel@tonic-gate extern int goany(void);
1020Sstevel@tonic-gate
1030Sstevel@tonic-gate
1040Sstevel@tonic-gate #define NEXT_PRT_ITEM(p) \
105*12683SJimmy.Vetayases@oracle.com (void *)(((char *)(p)) + (p)->Length)
1060Sstevel@tonic-gate
1070Sstevel@tonic-gate static int
acpi_get_gsiv(dev_info_t * dip,ACPI_HANDLE pciobj,int devno,int ipin,int * pci_irqp,iflag_t * intr_flagp,acpi_psm_lnk_t * acpipsmlnkp)1080Sstevel@tonic-gate acpi_get_gsiv(dev_info_t *dip, ACPI_HANDLE pciobj, int devno, int ipin,
1090Sstevel@tonic-gate int *pci_irqp, iflag_t *intr_flagp, acpi_psm_lnk_t *acpipsmlnkp)
1100Sstevel@tonic-gate {
1110Sstevel@tonic-gate ACPI_BUFFER rb;
1120Sstevel@tonic-gate ACPI_PCI_ROUTING_TABLE *prtp;
1130Sstevel@tonic-gate int status;
1140Sstevel@tonic-gate int dev_adr;
1150Sstevel@tonic-gate
1160Sstevel@tonic-gate /*
1170Sstevel@tonic-gate * Get the IRQ routing table
1180Sstevel@tonic-gate */
1190Sstevel@tonic-gate rb.Pointer = NULL;
1200Sstevel@tonic-gate rb.Length = ACPI_ALLOCATE_BUFFER;
1210Sstevel@tonic-gate if (AcpiGetIrqRoutingTable(pciobj, &rb) != AE_OK) {
1220Sstevel@tonic-gate return (ACPI_PSM_FAILURE);
1230Sstevel@tonic-gate }
1240Sstevel@tonic-gate
1250Sstevel@tonic-gate status = ACPI_PSM_FAILURE;
1260Sstevel@tonic-gate dev_adr = (devno << 16 | 0xffff);
1270Sstevel@tonic-gate for (prtp = rb.Pointer; prtp->Length != 0; prtp = NEXT_PRT_ITEM(prtp)) {
1280Sstevel@tonic-gate /* look until a matching dev/pin is found */
1290Sstevel@tonic-gate if (dev_adr != prtp->Address || ipin != prtp->Pin)
1300Sstevel@tonic-gate continue;
1310Sstevel@tonic-gate
1320Sstevel@tonic-gate /* NULL Source name means index is GSIV */
1330Sstevel@tonic-gate if (*prtp->Source == 0) {
1347851SDana.Myers@Sun.COM intr_flagp->intr_el = INTR_EL_LEVEL;
1357851SDana.Myers@Sun.COM intr_flagp->intr_po = INTR_PO_ACTIVE_LOW;
1360Sstevel@tonic-gate ASSERT(pci_irqp != NULL);
1370Sstevel@tonic-gate *pci_irqp = prtp->SourceIndex;
1380Sstevel@tonic-gate status = ACPI_PSM_SUCCESS;
1390Sstevel@tonic-gate } else
1400Sstevel@tonic-gate status = acpi_eval_lnk(dip, prtp->Source, pci_irqp,
1410Sstevel@tonic-gate intr_flagp, acpipsmlnkp);
1420Sstevel@tonic-gate
1430Sstevel@tonic-gate break;
1440Sstevel@tonic-gate
1450Sstevel@tonic-gate }
1460Sstevel@tonic-gate
1470Sstevel@tonic-gate AcpiOsFree(rb.Pointer);
1480Sstevel@tonic-gate return (status);
1490Sstevel@tonic-gate }
1500Sstevel@tonic-gate
1510Sstevel@tonic-gate /*
1520Sstevel@tonic-gate *
1530Sstevel@tonic-gate * If the interrupt link device is already configured,
1540Sstevel@tonic-gate * stores polarity and sensitivity in the structure pointed to by
1550Sstevel@tonic-gate * intr_flagp, and irqno in the value pointed to by pci_irqp.
1560Sstevel@tonic-gate *
1570Sstevel@tonic-gate * Returns:
1580Sstevel@tonic-gate * ACPI_PSM_SUCCESS if the interrupt link device is already configured.
1590Sstevel@tonic-gate * ACPI_PSM_PARTIAL if configuration is needed.
1600Sstevel@tonic-gate * ACPI_PSM_FAILURE in case of error.
1610Sstevel@tonic-gate *
1620Sstevel@tonic-gate * When two devices share the same interrupt link device, and the
1630Sstevel@tonic-gate * link device is already configured (i.e. found in the irq cache)
1640Sstevel@tonic-gate * we need to use the already configured irq instead of reconfiguring
1650Sstevel@tonic-gate * the link device.
1660Sstevel@tonic-gate */
1670Sstevel@tonic-gate static int
acpi_eval_lnk(dev_info_t * dip,char * lnkname,int * pci_irqp,iflag_t * intr_flagp,acpi_psm_lnk_t * acpipsmlnkp)1680Sstevel@tonic-gate acpi_eval_lnk(dev_info_t *dip, char *lnkname, int *pci_irqp,
1690Sstevel@tonic-gate iflag_t *intr_flagp, acpi_psm_lnk_t *acpipsmlnkp)
1700Sstevel@tonic-gate {
1710Sstevel@tonic-gate ACPI_HANDLE tmpobj;
1720Sstevel@tonic-gate ACPI_HANDLE lnkobj;
1730Sstevel@tonic-gate int status;
1740Sstevel@tonic-gate
1750Sstevel@tonic-gate /*
1760Sstevel@tonic-gate * Convert the passed-in link device name to a handle
1770Sstevel@tonic-gate */
1780Sstevel@tonic-gate if (AcpiGetHandle(NULL, lnkname, &lnkobj) != AE_OK) {
1790Sstevel@tonic-gate return (ACPI_PSM_FAILURE);
1800Sstevel@tonic-gate }
1810Sstevel@tonic-gate
1820Sstevel@tonic-gate /*
1830Sstevel@tonic-gate * Assume that the link device is invalid if no _CRS method
1840Sstevel@tonic-gate * exists, since _CRS method is a required method
1850Sstevel@tonic-gate */
1860Sstevel@tonic-gate if (AcpiGetHandle(lnkobj, "_CRS", &tmpobj) != AE_OK) {
1870Sstevel@tonic-gate return (ACPI_PSM_FAILURE);
1880Sstevel@tonic-gate }
1890Sstevel@tonic-gate
1900Sstevel@tonic-gate ASSERT(acpipsmlnkp != NULL);
1910Sstevel@tonic-gate acpipsmlnkp->lnkobj = lnkobj;
1920Sstevel@tonic-gate if ((acpi_get_irq_lnk_cache_ent(lnkobj, pci_irqp, intr_flagp)) ==
1930Sstevel@tonic-gate ACPI_PSM_SUCCESS) {
1940Sstevel@tonic-gate PSM_VERBOSE_IRQ((CE_CONT, "!psm: link object found from cache "
1950Sstevel@tonic-gate " for device %s, instance #%d, irq no %d\n",
1960Sstevel@tonic-gate ddi_get_name(dip), ddi_get_instance(dip), *pci_irqp));
1970Sstevel@tonic-gate return (ACPI_PSM_SUCCESS);
1980Sstevel@tonic-gate } else {
1990Sstevel@tonic-gate if (acpica_eval_int(lnkobj, "_STA", &status) == AE_OK) {
2000Sstevel@tonic-gate acpipsmlnkp->device_status = (uchar_t)status;
2010Sstevel@tonic-gate }
2020Sstevel@tonic-gate
2030Sstevel@tonic-gate return (ACPI_PSM_PARTIAL);
2040Sstevel@tonic-gate }
2050Sstevel@tonic-gate }
2060Sstevel@tonic-gate
2070Sstevel@tonic-gate int
acpi_psm_init(char * module_name,int verbose_flags)2080Sstevel@tonic-gate acpi_psm_init(char *module_name, int verbose_flags)
2090Sstevel@tonic-gate {
2100Sstevel@tonic-gate psm_module_name = module_name;
2110Sstevel@tonic-gate
2120Sstevel@tonic-gate psm_verbose = verbose_flags;
2130Sstevel@tonic-gate
2140Sstevel@tonic-gate if (AcpiGetHandle(NULL, "\\_SB", &acpi_sbobj) != AE_OK) {
2150Sstevel@tonic-gate cmn_err(CE_WARN, "!psm: get _SB failed");
2160Sstevel@tonic-gate return (ACPI_PSM_FAILURE);
2170Sstevel@tonic-gate }
2180Sstevel@tonic-gate
2190Sstevel@tonic-gate mutex_init(&acpi_irq_cache_mutex, NULL, MUTEX_DEFAULT, NULL);
2200Sstevel@tonic-gate
2210Sstevel@tonic-gate return (ACPI_PSM_SUCCESS);
2220Sstevel@tonic-gate
2230Sstevel@tonic-gate }
2240Sstevel@tonic-gate
2250Sstevel@tonic-gate /*
2260Sstevel@tonic-gate * Return bus/dev/fn for PCI dip (note: not the parent "pci" node).
2270Sstevel@tonic-gate */
2280Sstevel@tonic-gate
2290Sstevel@tonic-gate int
get_bdf(dev_info_t * dip,int * bus,int * device,int * func)2300Sstevel@tonic-gate get_bdf(dev_info_t *dip, int *bus, int *device, int *func)
2310Sstevel@tonic-gate {
2320Sstevel@tonic-gate pci_regspec_t *pci_rp;
2330Sstevel@tonic-gate int len;
2340Sstevel@tonic-gate
2350Sstevel@tonic-gate if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
2360Sstevel@tonic-gate "reg", (int **)&pci_rp, (uint_t *)&len) != DDI_SUCCESS)
2370Sstevel@tonic-gate return (-1);
2380Sstevel@tonic-gate
2390Sstevel@tonic-gate if (len < (sizeof (pci_regspec_t) / sizeof (int))) {
2400Sstevel@tonic-gate ddi_prop_free(pci_rp);
2410Sstevel@tonic-gate return (-1);
2420Sstevel@tonic-gate }
2430Sstevel@tonic-gate if (bus != NULL)
2440Sstevel@tonic-gate *bus = (int)PCI_REG_BUS_G(pci_rp->pci_phys_hi);
2450Sstevel@tonic-gate if (device != NULL)
2460Sstevel@tonic-gate *device = (int)PCI_REG_DEV_G(pci_rp->pci_phys_hi);
2470Sstevel@tonic-gate if (func != NULL)
2480Sstevel@tonic-gate *func = (int)PCI_REG_FUNC_G(pci_rp->pci_phys_hi);
2490Sstevel@tonic-gate ddi_prop_free(pci_rp);
2500Sstevel@tonic-gate return (0);
2510Sstevel@tonic-gate }
2520Sstevel@tonic-gate
2530Sstevel@tonic-gate
2540Sstevel@tonic-gate /*
2550Sstevel@tonic-gate * Build the reserved ISA irq list, and store it in the table pointed to by
2560Sstevel@tonic-gate * reserved_irqs_table. The caller is responsible for allocating this table
2570Sstevel@tonic-gate * with a minimum of MAX_ISA_IRQ + 1 entries.
2580Sstevel@tonic-gate *
2590Sstevel@tonic-gate * The routine looks in the device tree at the subtree rooted at /isa
2600Sstevel@tonic-gate * for each of the devices under that node, if an interrupts property
2610Sstevel@tonic-gate * is present, its values are used to "reserve" irqs so that later ACPI
2620Sstevel@tonic-gate * configuration won't choose those irqs.
2630Sstevel@tonic-gate *
2640Sstevel@tonic-gate * In addition, if acpi_irq_check_elcr is set, will use ELCR register
2650Sstevel@tonic-gate * to identify reserved IRQs.
2660Sstevel@tonic-gate */
2670Sstevel@tonic-gate void
build_reserved_irqlist(uchar_t * reserved_irqs_table)2680Sstevel@tonic-gate build_reserved_irqlist(uchar_t *reserved_irqs_table)
2690Sstevel@tonic-gate {
2700Sstevel@tonic-gate dev_info_t *isanode = ddi_find_devinfo("isa", -1, 0);
2710Sstevel@tonic-gate dev_info_t *isa_child = 0;
2720Sstevel@tonic-gate int i;
2730Sstevel@tonic-gate uint_t elcrval;
2740Sstevel@tonic-gate
2750Sstevel@tonic-gate /* Initialize the reserved ISA IRQs: */
2761456Sdmick for (i = 0; i <= MAX_ISA_IRQ; i++)
2770Sstevel@tonic-gate reserved_irqs_table[i] = 0;
2780Sstevel@tonic-gate
2790Sstevel@tonic-gate if (acpi_irq_check_elcr) {
2800Sstevel@tonic-gate
2810Sstevel@tonic-gate elcrval = (inb(ELCR_PORT2) << 8) | (inb(ELCR_PORT1));
2820Sstevel@tonic-gate if (ELCR_EDGE(elcrval, 0) && ELCR_EDGE(elcrval, 1) &&
2830Sstevel@tonic-gate ELCR_EDGE(elcrval, 2) && ELCR_EDGE(elcrval, 8) &&
2840Sstevel@tonic-gate ELCR_EDGE(elcrval, 13)) {
2850Sstevel@tonic-gate /* valid ELCR */
2861456Sdmick for (i = 0; i <= MAX_ISA_IRQ; i++)
2870Sstevel@tonic-gate if (!ELCR_LEVEL(elcrval, i))
2880Sstevel@tonic-gate reserved_irqs_table[i] = 1;
2890Sstevel@tonic-gate }
2900Sstevel@tonic-gate }
2910Sstevel@tonic-gate
2920Sstevel@tonic-gate /* always check the isa devinfo nodes */
2930Sstevel@tonic-gate
2940Sstevel@tonic-gate if (isanode != 0) { /* Found ISA */
2950Sstevel@tonic-gate uint_t intcnt; /* Interrupt count */
2960Sstevel@tonic-gate int *intrs; /* Interrupt values */
2970Sstevel@tonic-gate
2980Sstevel@tonic-gate /* Load first child: */
2990Sstevel@tonic-gate isa_child = ddi_get_child(isanode);
3000Sstevel@tonic-gate while (isa_child != 0) { /* Iterate over /isa children */
3010Sstevel@tonic-gate /* if child has any interrupts, save them */
3020Sstevel@tonic-gate if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, isa_child,
3030Sstevel@tonic-gate DDI_PROP_DONTPASS, "interrupts", &intrs, &intcnt)
3040Sstevel@tonic-gate == DDI_PROP_SUCCESS) {
3050Sstevel@tonic-gate /*
3060Sstevel@tonic-gate * iterate over child interrupt list, adding
3070Sstevel@tonic-gate * them to the reserved irq list
3080Sstevel@tonic-gate */
3090Sstevel@tonic-gate while (intcnt-- > 0) {
3100Sstevel@tonic-gate /*
3110Sstevel@tonic-gate * Each value MUST be <= MAX_ISA_IRQ
3120Sstevel@tonic-gate */
3130Sstevel@tonic-gate
3140Sstevel@tonic-gate if ((intrs[intcnt] > MAX_ISA_IRQ) ||
3150Sstevel@tonic-gate (intrs[intcnt] < 0))
3160Sstevel@tonic-gate continue;
3170Sstevel@tonic-gate
3180Sstevel@tonic-gate reserved_irqs_table[intrs[intcnt]] = 1;
3190Sstevel@tonic-gate }
3200Sstevel@tonic-gate ddi_prop_free(intrs);
3210Sstevel@tonic-gate }
3220Sstevel@tonic-gate isa_child = ddi_get_next_sibling(isa_child);
3230Sstevel@tonic-gate }
3240Sstevel@tonic-gate /* The isa node was held by ddi_find_devinfo, so release it */
3250Sstevel@tonic-gate ndi_rele_devi(isanode);
3260Sstevel@tonic-gate }
3270Sstevel@tonic-gate
3280Sstevel@tonic-gate /*
3290Sstevel@tonic-gate * Reserve IRQ14 & IRQ15 for IDE. It shouldn't be hard-coded
3300Sstevel@tonic-gate * here but there's no other way to find the irqs for
3310Sstevel@tonic-gate * legacy-mode ata (since it's hard-coded in pci-ide also).
3320Sstevel@tonic-gate */
3330Sstevel@tonic-gate reserved_irqs_table[14] = 1;
3340Sstevel@tonic-gate reserved_irqs_table[15] = 1;
3350Sstevel@tonic-gate }
3360Sstevel@tonic-gate
3370Sstevel@tonic-gate /*
3380Sstevel@tonic-gate * Examine devinfo node to determine if it is a PCI-PCI bridge
3390Sstevel@tonic-gate *
3400Sstevel@tonic-gate * Returns:
3410Sstevel@tonic-gate * 0 if not a bridge or error
3420Sstevel@tonic-gate * 1 if a bridge
3430Sstevel@tonic-gate */
3440Sstevel@tonic-gate static int
psm_is_pci_bridge(dev_info_t * dip)3450Sstevel@tonic-gate psm_is_pci_bridge(dev_info_t *dip)
3460Sstevel@tonic-gate {
3470Sstevel@tonic-gate ddi_acc_handle_t cfg_handle;
3480Sstevel@tonic-gate int rv = 0;
3490Sstevel@tonic-gate
3500Sstevel@tonic-gate if (pci_config_setup(dip, &cfg_handle) == DDI_SUCCESS) {
3510Sstevel@tonic-gate rv = ((pci_config_get8(cfg_handle, PCI_CONF_BASCLASS) ==
3520Sstevel@tonic-gate PCI_CLASS_BRIDGE) && (pci_config_get8(cfg_handle,
3530Sstevel@tonic-gate PCI_CONF_SUBCLASS) == PCI_BRIDGE_PCI));
3540Sstevel@tonic-gate pci_config_teardown(&cfg_handle);
3550Sstevel@tonic-gate }
3560Sstevel@tonic-gate
3570Sstevel@tonic-gate return (rv);
3580Sstevel@tonic-gate }
3590Sstevel@tonic-gate
3600Sstevel@tonic-gate /*
3610Sstevel@tonic-gate * Examines ACPI node for presence of _PRT object
36212159SSusan.Scheufele@Sun.COM * Check _STA to make sure node is present and/or enabled
3630Sstevel@tonic-gate *
3640Sstevel@tonic-gate * Returns:
3650Sstevel@tonic-gate * 0 if no _PRT or error
3660Sstevel@tonic-gate * 1 if _PRT is present
3670Sstevel@tonic-gate */
3680Sstevel@tonic-gate static int
psm_node_has_prt(ACPI_HANDLE * ah)3690Sstevel@tonic-gate psm_node_has_prt(ACPI_HANDLE *ah)
3700Sstevel@tonic-gate {
3710Sstevel@tonic-gate ACPI_HANDLE rh;
37212159SSusan.Scheufele@Sun.COM int sta;
37312159SSusan.Scheufele@Sun.COM
37412159SSusan.Scheufele@Sun.COM /*
37512159SSusan.Scheufele@Sun.COM * Return 0 for "no _PRT" if device does not exist
37612159SSusan.Scheufele@Sun.COM * According to ACPI Spec,
37712159SSusan.Scheufele@Sun.COM * 1) setting either bit 0 or bit 3 means that device exists.
37812159SSusan.Scheufele@Sun.COM * 2) Absence of _STA method means all status bits set.
37912159SSusan.Scheufele@Sun.COM */
38012159SSusan.Scheufele@Sun.COM if (ACPI_SUCCESS(acpica_eval_int(ah, "_STA", &sta)) &&
38112159SSusan.Scheufele@Sun.COM !(sta & (ACPI_STA_DEVICE_PRESENT | ACPI_STA_DEVICE_FUNCTIONING)))
38212159SSusan.Scheufele@Sun.COM return (0);
3830Sstevel@tonic-gate
3840Sstevel@tonic-gate return (AcpiGetHandle(ah, "_PRT", &rh) == AE_OK);
3850Sstevel@tonic-gate }
3860Sstevel@tonic-gate
387*12683SJimmy.Vetayases@oracle.com
3880Sstevel@tonic-gate /*
3890Sstevel@tonic-gate * Look first for an ACPI PCI bus node matching busid, then for a _PRT on the
3900Sstevel@tonic-gate * parent node; then drop into the bridge-chasing code (which will also
3910Sstevel@tonic-gate * look for _PRTs on the way up the tree of bridges)
3920Sstevel@tonic-gate *
3930Sstevel@tonic-gate * Stores polarity and sensitivity in the structure pointed to by
3940Sstevel@tonic-gate * intr_flagp, and irqno in the value pointed to by pci_irqp. *
3950Sstevel@tonic-gate * Returns:
3960Sstevel@tonic-gate * ACPI_PSM_SUCCESS on success.
3970Sstevel@tonic-gate * ACPI_PSM_PARTIAL to indicate need to configure the interrupt
3980Sstevel@tonic-gate * link device.
3990Sstevel@tonic-gate * ACPI_PSM_FAILURE if an error prevented the system from
4000Sstevel@tonic-gate * obtaining irq information for dip.
4010Sstevel@tonic-gate */
4020Sstevel@tonic-gate int
acpi_translate_pci_irq(dev_info_t * dip,int ipin,int * pci_irqp,iflag_t * intr_flagp,acpi_psm_lnk_t * acpipsmlnkp)4030Sstevel@tonic-gate acpi_translate_pci_irq(dev_info_t *dip, int ipin, int *pci_irqp,
4040Sstevel@tonic-gate iflag_t *intr_flagp, acpi_psm_lnk_t *acpipsmlnkp)
4050Sstevel@tonic-gate {
4060Sstevel@tonic-gate ACPI_HANDLE pciobj;
4070Sstevel@tonic-gate int status = AE_ERROR;
4080Sstevel@tonic-gate dev_info_t *curdip, *parentdip;
4090Sstevel@tonic-gate int curpin, curbus, curdev;
4100Sstevel@tonic-gate
4110Sstevel@tonic-gate
4120Sstevel@tonic-gate curpin = ipin;
4130Sstevel@tonic-gate curdip = dip;
4140Sstevel@tonic-gate while (curdip != ddi_root_node()) {
4150Sstevel@tonic-gate parentdip = ddi_get_parent(curdip);
4160Sstevel@tonic-gate ASSERT(parentdip != NULL);
4170Sstevel@tonic-gate
4187851SDana.Myers@Sun.COM if (get_bdf(curdip, &curbus, &curdev, NULL) != 0)
4190Sstevel@tonic-gate break;
4200Sstevel@tonic-gate
4214667Smh27603 status = acpica_get_handle(parentdip, &pciobj);
4220Sstevel@tonic-gate if ((status == AE_OK) && psm_node_has_prt(pciobj)) {
4230Sstevel@tonic-gate return (acpi_get_gsiv(curdip, pciobj, curdev, curpin,
4240Sstevel@tonic-gate pci_irqp, intr_flagp, acpipsmlnkp));
4250Sstevel@tonic-gate }
4260Sstevel@tonic-gate
4270Sstevel@tonic-gate /* if we got here, we need to traverse a bridge upwards */
4280Sstevel@tonic-gate if (!psm_is_pci_bridge(parentdip))
4290Sstevel@tonic-gate break;
4300Sstevel@tonic-gate
4310Sstevel@tonic-gate /*
4320Sstevel@tonic-gate * This is the rotating scheme that Compaq is using
4330Sstevel@tonic-gate * and documented in the PCI-PCI spec. Also, if the
4340Sstevel@tonic-gate * PCI-PCI bridge is behind another PCI-PCI bridge,
4350Sstevel@tonic-gate * then it needs to keep ascending until an interrupt
4360Sstevel@tonic-gate * entry is found or the top is reached
4370Sstevel@tonic-gate */
4380Sstevel@tonic-gate curpin = (curdev + curpin) % PCI_INTD;
4390Sstevel@tonic-gate curdip = parentdip;
4400Sstevel@tonic-gate }
4410Sstevel@tonic-gate
4420Sstevel@tonic-gate /*
4430Sstevel@tonic-gate * We should never, ever get here; didn't find a _PRT
4440Sstevel@tonic-gate */
4450Sstevel@tonic-gate return (ACPI_PSM_FAILURE);
4460Sstevel@tonic-gate }
4470Sstevel@tonic-gate
4480Sstevel@tonic-gate /*
4490Sstevel@tonic-gate * Sets the irq resource of the lnk object to the requested irq value.
4500Sstevel@tonic-gate *
4510Sstevel@tonic-gate * Returns ACPI_PSM_SUCCESS on success, ACPI_PSM_FAILURE upon failure.
4520Sstevel@tonic-gate */
4530Sstevel@tonic-gate int
acpi_set_irq_resource(acpi_psm_lnk_t * acpipsmlnkp,int irq)4540Sstevel@tonic-gate acpi_set_irq_resource(acpi_psm_lnk_t *acpipsmlnkp, int irq)
4550Sstevel@tonic-gate {
4560Sstevel@tonic-gate ACPI_BUFFER rsb;
4570Sstevel@tonic-gate ACPI_RESOURCE *resp;
4580Sstevel@tonic-gate ACPI_RESOURCE *srsp;
4590Sstevel@tonic-gate ACPI_HANDLE lnkobj;
460941Smyers int srs_len, status;
4610Sstevel@tonic-gate
4620Sstevel@tonic-gate ASSERT(acpipsmlnkp != NULL);
4630Sstevel@tonic-gate
4640Sstevel@tonic-gate lnkobj = acpipsmlnkp->lnkobj;
4650Sstevel@tonic-gate
4660Sstevel@tonic-gate /*
4670Sstevel@tonic-gate * Fetch the possible resources for the link
4680Sstevel@tonic-gate */
4690Sstevel@tonic-gate
4700Sstevel@tonic-gate rsb.Pointer = NULL;
4710Sstevel@tonic-gate rsb.Length = ACPI_ALLOCATE_BUFFER;
4720Sstevel@tonic-gate status = AcpiGetPossibleResources(lnkobj, &rsb);
4730Sstevel@tonic-gate if (status != AE_OK) {
4740Sstevel@tonic-gate cmn_err(CE_WARN, "!psm: set_irq: _PRS failed");
4750Sstevel@tonic-gate return (ACPI_PSM_FAILURE);
4760Sstevel@tonic-gate }
4770Sstevel@tonic-gate
4780Sstevel@tonic-gate /*
4790Sstevel@tonic-gate * Find an IRQ resource descriptor to use as template
4800Sstevel@tonic-gate */
4810Sstevel@tonic-gate srsp = NULL;
482941Smyers for (resp = rsb.Pointer; resp->Type != ACPI_RESOURCE_TYPE_END_TAG;
4830Sstevel@tonic-gate resp = ACPI_NEXT_RESOURCE(resp)) {
484941Smyers if ((resp->Type == ACPI_RESOURCE_TYPE_IRQ) ||
485941Smyers (resp->Type == ACPI_RESOURCE_TYPE_EXTENDED_IRQ)) {
486941Smyers ACPI_RESOURCE *endtag;
4870Sstevel@tonic-gate /*
488941Smyers * Allocate enough room for this resource entry
489941Smyers * and one end tag following it
4900Sstevel@tonic-gate */
491941Smyers srs_len = resp->Length + sizeof (*endtag);
492941Smyers srsp = kmem_zalloc(srs_len, KM_SLEEP);
493941Smyers bcopy(resp, srsp, resp->Length);
494941Smyers endtag = ACPI_NEXT_RESOURCE(srsp);
495941Smyers endtag->Type = ACPI_RESOURCE_TYPE_END_TAG;
496941Smyers endtag->Length = 0;
4970Sstevel@tonic-gate break; /* drop out of the loop */
4980Sstevel@tonic-gate }
4990Sstevel@tonic-gate }
5000Sstevel@tonic-gate
5010Sstevel@tonic-gate /*
5020Sstevel@tonic-gate * We're done with the PRS values, toss 'em lest we forget
5030Sstevel@tonic-gate */
5040Sstevel@tonic-gate AcpiOsFree(rsb.Pointer);
5050Sstevel@tonic-gate
5060Sstevel@tonic-gate if (srsp == NULL)
5070Sstevel@tonic-gate return (ACPI_PSM_FAILURE);
5080Sstevel@tonic-gate
5090Sstevel@tonic-gate /*
5100Sstevel@tonic-gate * The Interrupts[] array is always at least one entry
5110Sstevel@tonic-gate * long; see the definition of ACPI_RESOURCE.
5120Sstevel@tonic-gate */
513941Smyers switch (srsp->Type) {
514941Smyers case ACPI_RESOURCE_TYPE_IRQ:
515941Smyers srsp->Data.Irq.InterruptCount = 1;
516*12683SJimmy.Vetayases@oracle.com srsp->Data.Irq.Interrupts[0] = (uint8_t)irq;
5170Sstevel@tonic-gate break;
518941Smyers case ACPI_RESOURCE_TYPE_EXTENDED_IRQ:
519941Smyers srsp->Data.ExtendedIrq.InterruptCount = 1;
5200Sstevel@tonic-gate srsp->Data.ExtendedIrq.Interrupts[0] = irq;
5210Sstevel@tonic-gate break;
5220Sstevel@tonic-gate }
5230Sstevel@tonic-gate
5240Sstevel@tonic-gate rsb.Pointer = srsp;
525941Smyers rsb.Length = srs_len;
5260Sstevel@tonic-gate status = AcpiSetCurrentResources(lnkobj, &rsb);
527941Smyers kmem_free(srsp, srs_len);
5280Sstevel@tonic-gate if (status != AE_OK) {
5290Sstevel@tonic-gate cmn_err(CE_WARN, "!psm: set_irq: _SRS failed");
5300Sstevel@tonic-gate return (ACPI_PSM_FAILURE);
5310Sstevel@tonic-gate }
5320Sstevel@tonic-gate
5330Sstevel@tonic-gate if (acpica_eval_int(lnkobj, "_STA", &status) == AE_OK) {
5340Sstevel@tonic-gate acpipsmlnkp->device_status = (uchar_t)status;
5350Sstevel@tonic-gate return (ACPI_PSM_SUCCESS);
5360Sstevel@tonic-gate } else
5370Sstevel@tonic-gate return (ACPI_PSM_FAILURE);
5380Sstevel@tonic-gate }
5390Sstevel@tonic-gate
5400Sstevel@tonic-gate
5410Sstevel@tonic-gate /*
5420Sstevel@tonic-gate *
5430Sstevel@tonic-gate */
5440Sstevel@tonic-gate static int
psm_acpi_edgelevel(UINT32 el)5450Sstevel@tonic-gate psm_acpi_edgelevel(UINT32 el)
5460Sstevel@tonic-gate {
5470Sstevel@tonic-gate switch (el) {
5480Sstevel@tonic-gate case ACPI_EDGE_SENSITIVE:
5490Sstevel@tonic-gate return (INTR_EL_EDGE);
5500Sstevel@tonic-gate case ACPI_LEVEL_SENSITIVE:
5510Sstevel@tonic-gate return (INTR_EL_LEVEL);
5520Sstevel@tonic-gate default:
5530Sstevel@tonic-gate /* el is a single bit; should never reach here */
5540Sstevel@tonic-gate return (INTR_EL_CONFORM);
5550Sstevel@tonic-gate }
5560Sstevel@tonic-gate }
5570Sstevel@tonic-gate
5580Sstevel@tonic-gate
5590Sstevel@tonic-gate /*
5600Sstevel@tonic-gate *
5610Sstevel@tonic-gate */
5620Sstevel@tonic-gate static int
psm_acpi_po(UINT32 po)5630Sstevel@tonic-gate psm_acpi_po(UINT32 po)
5640Sstevel@tonic-gate {
5650Sstevel@tonic-gate switch (po) {
5660Sstevel@tonic-gate case ACPI_ACTIVE_HIGH:
5670Sstevel@tonic-gate return (INTR_PO_ACTIVE_HIGH);
5680Sstevel@tonic-gate case ACPI_ACTIVE_LOW:
5690Sstevel@tonic-gate return (INTR_PO_ACTIVE_LOW);
5700Sstevel@tonic-gate default:
5710Sstevel@tonic-gate /* po is a single bit; should never reach here */
5720Sstevel@tonic-gate return (INTR_PO_CONFORM);
5730Sstevel@tonic-gate }
5740Sstevel@tonic-gate }
5750Sstevel@tonic-gate
5760Sstevel@tonic-gate
5770Sstevel@tonic-gate /*
5780Sstevel@tonic-gate * Retrieves the current irq setting for the interrrupt link device.
5790Sstevel@tonic-gate *
5800Sstevel@tonic-gate * Stores polarity and sensitivity in the structure pointed to by
5810Sstevel@tonic-gate * intr_flagp, and irqno in the value pointed to by pci_irqp.
5820Sstevel@tonic-gate *
5830Sstevel@tonic-gate * Returns ACPI_PSM_SUCCESS on success, ACPI_PSM_FAILURE upon failure.
5840Sstevel@tonic-gate */
5850Sstevel@tonic-gate int
acpi_get_current_irq_resource(acpi_psm_lnk_t * acpipsmlnkp,int * pci_irqp,iflag_t * intr_flagp)5860Sstevel@tonic-gate acpi_get_current_irq_resource(acpi_psm_lnk_t *acpipsmlnkp, int *pci_irqp,
5870Sstevel@tonic-gate iflag_t *intr_flagp)
5880Sstevel@tonic-gate {
5890Sstevel@tonic-gate ACPI_HANDLE lnkobj;
5900Sstevel@tonic-gate ACPI_BUFFER rb;
5910Sstevel@tonic-gate ACPI_RESOURCE *rp;
5920Sstevel@tonic-gate int irq;
5930Sstevel@tonic-gate int status = ACPI_PSM_FAILURE;
5940Sstevel@tonic-gate
5950Sstevel@tonic-gate ASSERT(acpipsmlnkp != NULL);
5960Sstevel@tonic-gate lnkobj = acpipsmlnkp->lnkobj;
5970Sstevel@tonic-gate
5980Sstevel@tonic-gate if (!(acpipsmlnkp->device_status & STA_PRESENT) ||
5990Sstevel@tonic-gate !(acpipsmlnkp->device_status & STA_ENABLE)) {
6000Sstevel@tonic-gate PSM_VERBOSE_IRQ((CE_WARN, "!psm: crs device either not "
6010Sstevel@tonic-gate "present or disabled, status 0x%x",
6020Sstevel@tonic-gate acpipsmlnkp->device_status));
6030Sstevel@tonic-gate return (ACPI_PSM_FAILURE);
6040Sstevel@tonic-gate }
6050Sstevel@tonic-gate
6060Sstevel@tonic-gate rb.Pointer = NULL;
6070Sstevel@tonic-gate rb.Length = ACPI_ALLOCATE_BUFFER;
6080Sstevel@tonic-gate if (AcpiGetCurrentResources(lnkobj, &rb) != AE_OK) {
6090Sstevel@tonic-gate PSM_VERBOSE_IRQ((CE_WARN, "!psm: no crs object found or"
6100Sstevel@tonic-gate " evaluation failed"));
6110Sstevel@tonic-gate return (ACPI_PSM_FAILURE);
6120Sstevel@tonic-gate }
6130Sstevel@tonic-gate
6140Sstevel@tonic-gate irq = -1;
615941Smyers for (rp = rb.Pointer; rp->Type != ACPI_RESOURCE_TYPE_END_TAG;
616941Smyers rp = ACPI_NEXT_RESOURCE(rp)) {
617941Smyers if (rp->Type == ACPI_RESOURCE_TYPE_IRQ) {
6180Sstevel@tonic-gate if (irq > 0) {
6190Sstevel@tonic-gate PSM_VERBOSE_IRQ((CE_WARN, "!psm: multiple IRQ"
6200Sstevel@tonic-gate " from _CRS "));
6210Sstevel@tonic-gate status = ACPI_PSM_FAILURE;
6220Sstevel@tonic-gate break;
6230Sstevel@tonic-gate }
6240Sstevel@tonic-gate
625941Smyers if (rp->Data.Irq.InterruptCount != 1) {
6260Sstevel@tonic-gate PSM_VERBOSE_IRQ((CE_WARN, "!psm: <>1 interrupt"
6270Sstevel@tonic-gate " from _CRS "));
6280Sstevel@tonic-gate status = ACPI_PSM_FAILURE;
6290Sstevel@tonic-gate break;
6300Sstevel@tonic-gate }
6310Sstevel@tonic-gate
6320Sstevel@tonic-gate intr_flagp->intr_el = psm_acpi_edgelevel(
6335295Srandyf rp->Data.Irq.Triggering);
6340Sstevel@tonic-gate intr_flagp->intr_po = psm_acpi_po(
6355295Srandyf rp->Data.Irq.Polarity);
6360Sstevel@tonic-gate irq = rp->Data.Irq.Interrupts[0];
6370Sstevel@tonic-gate status = ACPI_PSM_SUCCESS;
638941Smyers } else if (rp->Type == ACPI_RESOURCE_TYPE_EXTENDED_IRQ) {
6390Sstevel@tonic-gate if (irq > 0) {
6400Sstevel@tonic-gate PSM_VERBOSE_IRQ((CE_WARN, "!psm: multiple IRQ"
6410Sstevel@tonic-gate " from _CRS "));
6420Sstevel@tonic-gate status = ACPI_PSM_FAILURE;
6430Sstevel@tonic-gate break;
6440Sstevel@tonic-gate }
6450Sstevel@tonic-gate
646941Smyers if (rp->Data.ExtendedIrq.InterruptCount != 1) {
6470Sstevel@tonic-gate PSM_VERBOSE_IRQ((CE_WARN, "!psm: <>1 interrupt"
6480Sstevel@tonic-gate " from _CRS "));
6490Sstevel@tonic-gate status = ACPI_PSM_FAILURE;
6500Sstevel@tonic-gate break;
6510Sstevel@tonic-gate }
6520Sstevel@tonic-gate
6530Sstevel@tonic-gate intr_flagp->intr_el = psm_acpi_edgelevel(
6545295Srandyf rp->Data.ExtendedIrq.Triggering);
6550Sstevel@tonic-gate intr_flagp->intr_po = psm_acpi_po(
6565295Srandyf rp->Data.ExtendedIrq.Polarity);
6570Sstevel@tonic-gate irq = rp->Data.ExtendedIrq.Interrupts[0];
6580Sstevel@tonic-gate status = ACPI_PSM_SUCCESS;
6590Sstevel@tonic-gate }
6600Sstevel@tonic-gate }
6610Sstevel@tonic-gate
6620Sstevel@tonic-gate AcpiOsFree(rb.Pointer);
6630Sstevel@tonic-gate if (status == ACPI_PSM_SUCCESS) {
6640Sstevel@tonic-gate *pci_irqp = irq;
6650Sstevel@tonic-gate }
6660Sstevel@tonic-gate
6670Sstevel@tonic-gate return (status);
6680Sstevel@tonic-gate }
6690Sstevel@tonic-gate
6700Sstevel@tonic-gate /*
6710Sstevel@tonic-gate * Searches for the given IRQ in the irqlist passed in.
6720Sstevel@tonic-gate *
6730Sstevel@tonic-gate * If multiple matches exist, this returns true on the first match.
6740Sstevel@tonic-gate * Returns the interrupt flags, if a match was found, in `intr_flagp' if
6750Sstevel@tonic-gate * it's passed in non-NULL
6760Sstevel@tonic-gate */
6770Sstevel@tonic-gate int
acpi_irqlist_find_irq(acpi_irqlist_t * irqlistp,int irq,iflag_t * intr_flagp)6780Sstevel@tonic-gate acpi_irqlist_find_irq(acpi_irqlist_t *irqlistp, int irq, iflag_t *intr_flagp)
6790Sstevel@tonic-gate {
6800Sstevel@tonic-gate int found = 0;
6810Sstevel@tonic-gate int i;
6820Sstevel@tonic-gate
6830Sstevel@tonic-gate while (irqlistp != NULL && !found) {
6840Sstevel@tonic-gate for (i = 0; i < irqlistp->num_irqs; i++) {
6850Sstevel@tonic-gate if (irqlistp->irqs[i] == irq) {
6860Sstevel@tonic-gate if (intr_flagp)
6870Sstevel@tonic-gate *intr_flagp = irqlistp->intr_flags;
6880Sstevel@tonic-gate found = 1;
6890Sstevel@tonic-gate break; /* out of for() */
6900Sstevel@tonic-gate }
6910Sstevel@tonic-gate }
6920Sstevel@tonic-gate }
6930Sstevel@tonic-gate
6940Sstevel@tonic-gate return (found ? ACPI_PSM_SUCCESS : ACPI_PSM_FAILURE);
6950Sstevel@tonic-gate }
6960Sstevel@tonic-gate
6970Sstevel@tonic-gate /*
6980Sstevel@tonic-gate * Frees the irqlist allocated by acpi_get_possible_irq_resource.
6990Sstevel@tonic-gate * It takes a count of number of entries in the list.
7000Sstevel@tonic-gate */
7010Sstevel@tonic-gate void
acpi_free_irqlist(acpi_irqlist_t * irqlistp)7020Sstevel@tonic-gate acpi_free_irqlist(acpi_irqlist_t *irqlistp)
7030Sstevel@tonic-gate {
7040Sstevel@tonic-gate acpi_irqlist_t *freednode;
7050Sstevel@tonic-gate
7060Sstevel@tonic-gate while (irqlistp != NULL) {
7070Sstevel@tonic-gate /* Free the irq list */
7080Sstevel@tonic-gate kmem_free(irqlistp->irqs, irqlistp->num_irqs *
7090Sstevel@tonic-gate sizeof (int32_t));
7100Sstevel@tonic-gate
7110Sstevel@tonic-gate freednode = irqlistp;
7120Sstevel@tonic-gate irqlistp = irqlistp->next;
7130Sstevel@tonic-gate kmem_free(freednode, sizeof (acpi_irqlist_t));
7140Sstevel@tonic-gate }
7150Sstevel@tonic-gate }
7160Sstevel@tonic-gate
7170Sstevel@tonic-gate /*
7180Sstevel@tonic-gate * Creates a new entry in the given irqlist with the information passed in.
7190Sstevel@tonic-gate */
7200Sstevel@tonic-gate static void
acpi_add_irqlist_entry(acpi_irqlist_t ** irqlistp,uint32_t * irqlist,int irqlist_len,iflag_t * intr_flagp)7210Sstevel@tonic-gate acpi_add_irqlist_entry(acpi_irqlist_t **irqlistp, uint32_t *irqlist,
7220Sstevel@tonic-gate int irqlist_len, iflag_t *intr_flagp)
7230Sstevel@tonic-gate {
7240Sstevel@tonic-gate acpi_irqlist_t *newent;
7250Sstevel@tonic-gate
7260Sstevel@tonic-gate ASSERT(irqlist != NULL);
7270Sstevel@tonic-gate ASSERT(intr_flagp != NULL);
7280Sstevel@tonic-gate
7290Sstevel@tonic-gate newent = kmem_alloc(sizeof (acpi_irqlist_t), KM_SLEEP);
7300Sstevel@tonic-gate newent->intr_flags = *intr_flagp;
7310Sstevel@tonic-gate newent->irqs = irqlist;
7320Sstevel@tonic-gate newent->num_irqs = irqlist_len;
7330Sstevel@tonic-gate newent->next = *irqlistp;
7340Sstevel@tonic-gate
7350Sstevel@tonic-gate *irqlistp = newent;
7360Sstevel@tonic-gate }
7370Sstevel@tonic-gate
7380Sstevel@tonic-gate
7390Sstevel@tonic-gate /*
7400Sstevel@tonic-gate * Retrieves a list of possible interrupt settings for the interrupt link
7410Sstevel@tonic-gate * device.
7420Sstevel@tonic-gate *
7430Sstevel@tonic-gate * Stores polarity and sensitivity in the structure pointed to by intr_flagp.
7440Sstevel@tonic-gate * Updates value pointed to by irqlistp with the address of a table it
7450Sstevel@tonic-gate * allocates. where interrupt numbers are stored. Stores the number of entries
7460Sstevel@tonic-gate * in this table in the value pointed to by num_entriesp;
7470Sstevel@tonic-gate *
7480Sstevel@tonic-gate * Each element in this table is of type int32_t. The table should be later
7490Sstevel@tonic-gate * freed by caller via acpi_free_irq_list().
7500Sstevel@tonic-gate *
7510Sstevel@tonic-gate * Returns ACPI_PSM_SUCCESS on success and ACPI_PSM_FAILURE upon failure
7520Sstevel@tonic-gate */
7530Sstevel@tonic-gate int
acpi_get_possible_irq_resources(acpi_psm_lnk_t * acpipsmlnkp,acpi_irqlist_t ** irqlistp)7540Sstevel@tonic-gate acpi_get_possible_irq_resources(acpi_psm_lnk_t *acpipsmlnkp,
7550Sstevel@tonic-gate acpi_irqlist_t **irqlistp)
7560Sstevel@tonic-gate {
7570Sstevel@tonic-gate ACPI_HANDLE lnkobj;
7580Sstevel@tonic-gate ACPI_BUFFER rsb;
7590Sstevel@tonic-gate ACPI_RESOURCE *resp;
7600Sstevel@tonic-gate int status;
7610Sstevel@tonic-gate
7620Sstevel@tonic-gate int i, el, po, irqlist_len;
7631560Smyers uint32_t *irqlist;
7641560Smyers void *tmplist;
7650Sstevel@tonic-gate iflag_t intr_flags;
7660Sstevel@tonic-gate
7670Sstevel@tonic-gate ASSERT(acpipsmlnkp != NULL);
7680Sstevel@tonic-gate lnkobj = acpipsmlnkp->lnkobj;
7690Sstevel@tonic-gate
7700Sstevel@tonic-gate rsb.Pointer = NULL;
7710Sstevel@tonic-gate rsb.Length = ACPI_ALLOCATE_BUFFER;
7720Sstevel@tonic-gate status = AcpiGetPossibleResources(lnkobj, &rsb);
7730Sstevel@tonic-gate if (status != AE_OK) {
7740Sstevel@tonic-gate cmn_err(CE_WARN, "!psm: get_irq: _PRS failed");
7750Sstevel@tonic-gate return (ACPI_PSM_FAILURE);
7760Sstevel@tonic-gate }
7770Sstevel@tonic-gate
7780Sstevel@tonic-gate /*
7790Sstevel@tonic-gate * Scan the resources looking for an interrupt resource
7800Sstevel@tonic-gate */
7810Sstevel@tonic-gate *irqlistp = 0;
782941Smyers for (resp = rsb.Pointer; resp->Type != ACPI_RESOURCE_TYPE_END_TAG;
7830Sstevel@tonic-gate resp = ACPI_NEXT_RESOURCE(resp)) {
784941Smyers switch (resp->Type) {
785941Smyers case ACPI_RESOURCE_TYPE_IRQ:
786941Smyers irqlist_len = resp->Data.Irq.InterruptCount;
7870Sstevel@tonic-gate tmplist = resp->Data.Irq.Interrupts;
788941Smyers el = resp->Data.Irq.Triggering;
789941Smyers po = resp->Data.Irq.Polarity;
7900Sstevel@tonic-gate break;
791941Smyers case ACPI_RESOURCE_TYPE_EXTENDED_IRQ:
792941Smyers irqlist_len = resp->Data.ExtendedIrq.InterruptCount;
7930Sstevel@tonic-gate tmplist = resp->Data.ExtendedIrq.Interrupts;
794941Smyers el = resp->Data.ExtendedIrq.Triggering;
795941Smyers po = resp->Data.ExtendedIrq.Polarity;
7960Sstevel@tonic-gate break;
7970Sstevel@tonic-gate default:
7980Sstevel@tonic-gate continue;
7990Sstevel@tonic-gate }
8001560Smyers
8011560Smyers if (resp->Type != ACPI_RESOURCE_TYPE_IRQ &&
8021560Smyers resp->Type != ACPI_RESOURCE_TYPE_EXTENDED_IRQ) {
8031560Smyers cmn_err(CE_WARN, "!psm: get_irq: no IRQ resource");
8041560Smyers return (ACPI_PSM_FAILURE);
8051560Smyers }
8061560Smyers
8070Sstevel@tonic-gate /* NEEDSWORK: move this into add_irqlist_entry someday */
8080Sstevel@tonic-gate irqlist = kmem_zalloc(irqlist_len * sizeof (*irqlist),
8095295Srandyf KM_SLEEP);
8100Sstevel@tonic-gate for (i = 0; i < irqlist_len; i++)
8111560Smyers if (resp->Type == ACPI_RESOURCE_TYPE_IRQ)
8121560Smyers irqlist[i] = ((uint8_t *)tmplist)[i];
8131560Smyers else
8141560Smyers irqlist[i] = ((uint32_t *)tmplist)[i];
8150Sstevel@tonic-gate intr_flags.intr_el = psm_acpi_edgelevel(el);
8160Sstevel@tonic-gate intr_flags.intr_po = psm_acpi_po(po);
8170Sstevel@tonic-gate acpi_add_irqlist_entry(irqlistp, irqlist, irqlist_len,
8185295Srandyf &intr_flags);
8190Sstevel@tonic-gate }
8200Sstevel@tonic-gate
8210Sstevel@tonic-gate AcpiOsFree(rsb.Pointer);
8220Sstevel@tonic-gate return (irqlistp == NULL ? ACPI_PSM_FAILURE : ACPI_PSM_SUCCESS);
8230Sstevel@tonic-gate }
8240Sstevel@tonic-gate
8250Sstevel@tonic-gate /*
8260Sstevel@tonic-gate * Adds a new cache entry to the irq cache which maps an irq and
8270Sstevel@tonic-gate * its attributes to PCI bus/dev/ipin and optionally to its associated ACPI
8280Sstevel@tonic-gate * interrupt link device object.
8290Sstevel@tonic-gate */
8300Sstevel@tonic-gate void
acpi_new_irq_cache_ent(int bus,int dev,int ipin,int pci_irq,iflag_t * intr_flagp,acpi_psm_lnk_t * acpipsmlnkp)8310Sstevel@tonic-gate acpi_new_irq_cache_ent(int bus, int dev, int ipin, int pci_irq,
8320Sstevel@tonic-gate iflag_t *intr_flagp, acpi_psm_lnk_t *acpipsmlnkp)
8330Sstevel@tonic-gate {
8340Sstevel@tonic-gate int newsize;
8350Sstevel@tonic-gate irq_cache_t *new_arr, *ep;
8360Sstevel@tonic-gate
8370Sstevel@tonic-gate mutex_enter(&acpi_irq_cache_mutex);
8380Sstevel@tonic-gate if (irq_cache_valid >= irq_cache_len) {
8390Sstevel@tonic-gate /* initially, or re-, allocate array */
8400Sstevel@tonic-gate
8410Sstevel@tonic-gate newsize = (irq_cache_len ?
8420Sstevel@tonic-gate irq_cache_len * 2 : IRQ_CACHE_INITLEN);
8430Sstevel@tonic-gate new_arr = kmem_zalloc(newsize * sizeof (irq_cache_t), KM_SLEEP);
8440Sstevel@tonic-gate if (irq_cache_len != 0) {
8450Sstevel@tonic-gate /* realloc: copy data, free old */
8460Sstevel@tonic-gate bcopy(irq_cache_table, new_arr,
8470Sstevel@tonic-gate irq_cache_len * sizeof (irq_cache_t));
8480Sstevel@tonic-gate kmem_free(irq_cache_table,
8490Sstevel@tonic-gate irq_cache_len * sizeof (irq_cache_t));
8500Sstevel@tonic-gate }
8510Sstevel@tonic-gate irq_cache_len = newsize;
8520Sstevel@tonic-gate irq_cache_table = new_arr;
8530Sstevel@tonic-gate }
8540Sstevel@tonic-gate ep = &irq_cache_table[irq_cache_valid++];
8550Sstevel@tonic-gate ep->bus = (uchar_t)bus;
8560Sstevel@tonic-gate ep->dev = (uchar_t)dev;
8570Sstevel@tonic-gate ep->ipin = (uchar_t)ipin;
8580Sstevel@tonic-gate ep->flags = *intr_flagp;
859*12683SJimmy.Vetayases@oracle.com ep->irq = (uchar_t)pci_irq;
8600Sstevel@tonic-gate ASSERT(acpipsmlnkp != NULL);
8610Sstevel@tonic-gate ep->lnkobj = acpipsmlnkp->lnkobj;
8620Sstevel@tonic-gate mutex_exit(&acpi_irq_cache_mutex);
8630Sstevel@tonic-gate }
8640Sstevel@tonic-gate
8650Sstevel@tonic-gate
8660Sstevel@tonic-gate /*
8670Sstevel@tonic-gate * Searches the irq caches for the given bus/dev/ipin.
8680Sstevel@tonic-gate *
8690Sstevel@tonic-gate * If info is found, stores polarity and sensitivity in the structure
8700Sstevel@tonic-gate * pointed to by intr_flagp, and irqno in the value pointed to by pci_irqp,
8710Sstevel@tonic-gate * and returns ACPI_PSM_SUCCESS.
8720Sstevel@tonic-gate * Otherwise, ACPI_PSM_FAILURE is returned.
8730Sstevel@tonic-gate */
8740Sstevel@tonic-gate int
acpi_get_irq_cache_ent(uchar_t bus,uchar_t dev,int ipin,int * pci_irqp,iflag_t * intr_flagp)8750Sstevel@tonic-gate acpi_get_irq_cache_ent(uchar_t bus, uchar_t dev, int ipin,
8760Sstevel@tonic-gate int *pci_irqp, iflag_t *intr_flagp)
8770Sstevel@tonic-gate {
8780Sstevel@tonic-gate
8790Sstevel@tonic-gate irq_cache_t *irqcachep;
8800Sstevel@tonic-gate int i;
8810Sstevel@tonic-gate int ret = ACPI_PSM_FAILURE;
8820Sstevel@tonic-gate
8830Sstevel@tonic-gate mutex_enter(&acpi_irq_cache_mutex);
8840Sstevel@tonic-gate for (irqcachep = irq_cache_table, i = 0; i < irq_cache_valid;
8850Sstevel@tonic-gate irqcachep++, i++)
8860Sstevel@tonic-gate if ((irqcachep->bus == bus) &&
8870Sstevel@tonic-gate (irqcachep->dev == dev) &&
8880Sstevel@tonic-gate (irqcachep->ipin == ipin)) {
8890Sstevel@tonic-gate ASSERT(pci_irqp != NULL && intr_flagp != NULL);
8900Sstevel@tonic-gate *pci_irqp = irqcachep->irq;
8910Sstevel@tonic-gate *intr_flagp = irqcachep->flags;
8920Sstevel@tonic-gate ret = ACPI_PSM_SUCCESS;
8930Sstevel@tonic-gate break;
8940Sstevel@tonic-gate }
8950Sstevel@tonic-gate
8960Sstevel@tonic-gate mutex_exit(&acpi_irq_cache_mutex);
8970Sstevel@tonic-gate return (ret);
8980Sstevel@tonic-gate }
8990Sstevel@tonic-gate
9000Sstevel@tonic-gate /*
9010Sstevel@tonic-gate * Searches the irq caches for the given interrupt lnk device object.
9020Sstevel@tonic-gate *
9030Sstevel@tonic-gate * If info is found, stores polarity and sensitivity in the structure
9040Sstevel@tonic-gate * pointed to by intr_flagp, and irqno in the value pointed to by pci_irqp,
9050Sstevel@tonic-gate * and returns ACPI_PSM_SUCCESS.
9060Sstevel@tonic-gate * Otherwise, ACPI_PSM_FAILURE is returned.
9070Sstevel@tonic-gate */
9080Sstevel@tonic-gate int
acpi_get_irq_lnk_cache_ent(ACPI_HANDLE lnkobj,int * pci_irqp,iflag_t * intr_flagp)9090Sstevel@tonic-gate acpi_get_irq_lnk_cache_ent(ACPI_HANDLE lnkobj, int *pci_irqp,
9100Sstevel@tonic-gate iflag_t *intr_flagp)
9110Sstevel@tonic-gate {
9120Sstevel@tonic-gate
9130Sstevel@tonic-gate irq_cache_t *irqcachep;
9140Sstevel@tonic-gate int i;
9150Sstevel@tonic-gate int ret = ACPI_PSM_FAILURE;
9160Sstevel@tonic-gate
9170Sstevel@tonic-gate if (lnkobj == NULL)
9180Sstevel@tonic-gate return (ACPI_PSM_FAILURE);
9190Sstevel@tonic-gate
9200Sstevel@tonic-gate mutex_enter(&acpi_irq_cache_mutex);
9210Sstevel@tonic-gate for (irqcachep = irq_cache_table, i = 0; i < irq_cache_valid;
9220Sstevel@tonic-gate irqcachep++, i++)
9230Sstevel@tonic-gate if (irqcachep->lnkobj == lnkobj) {
9240Sstevel@tonic-gate ASSERT(pci_irqp != NULL);
9250Sstevel@tonic-gate *pci_irqp = irqcachep->irq;
9260Sstevel@tonic-gate ASSERT(intr_flagp != NULL);
9270Sstevel@tonic-gate *intr_flagp = irqcachep->flags;
9280Sstevel@tonic-gate ret = ACPI_PSM_SUCCESS;
9290Sstevel@tonic-gate break;
9300Sstevel@tonic-gate }
9310Sstevel@tonic-gate mutex_exit(&acpi_irq_cache_mutex);
9320Sstevel@tonic-gate return (ret);
9330Sstevel@tonic-gate }
9340Sstevel@tonic-gate
9355295Srandyf /*
9365295Srandyf * Walk the irq_cache_table and re-configure the link device to
9375295Srandyf * the saved state.
9385295Srandyf */
9395295Srandyf void
acpi_restore_link_devices(void)9405295Srandyf acpi_restore_link_devices(void)
9415295Srandyf {
9425295Srandyf irq_cache_t *irqcachep;
9435295Srandyf acpi_psm_lnk_t psmlnk;
9445295Srandyf int i, status;
9455295Srandyf
9465295Srandyf /* XXX: may not need to hold this mutex */
9475295Srandyf mutex_enter(&acpi_irq_cache_mutex);
9485295Srandyf for (irqcachep = irq_cache_table, i = 0; i < irq_cache_valid;
9495295Srandyf irqcachep++, i++) {
9506210Sgs150176 if (irqcachep->lnkobj != NULL) {
9516210Sgs150176 /* only field used from psmlnk in set_irq is lnkobj */
9526210Sgs150176 psmlnk.lnkobj = irqcachep->lnkobj;
9536210Sgs150176 status = acpi_set_irq_resource(&psmlnk, irqcachep->irq);
9546210Sgs150176 /* warn if set_irq failed; soldier on */
9556210Sgs150176 if (status != ACPI_PSM_SUCCESS)
9566210Sgs150176 cmn_err(CE_WARN, "Could not restore interrupt "
9576210Sgs150176 "link device for IRQ 0x%x: Devices using "
9586210Sgs150176 "this IRQ may no longer function properly."
9596210Sgs150176 "\n", irqcachep->irq);
9606210Sgs150176 }
9615295Srandyf }
9625295Srandyf mutex_exit(&acpi_irq_cache_mutex);
9635295Srandyf }
9645295Srandyf
9650Sstevel@tonic-gate int
acpi_poweroff(void)9660Sstevel@tonic-gate acpi_poweroff(void)
9670Sstevel@tonic-gate {
9688122SKerry.Shu@Sun.COM extern int acpica_use_safe_delay;
9696678Sjj204856 ACPI_STATUS status;
9702025Smyers
9710Sstevel@tonic-gate PSM_VERBOSE_POWEROFF(("acpi_poweroff: starting poweroff\n"));
9720Sstevel@tonic-gate
9738122SKerry.Shu@Sun.COM acpica_use_safe_delay = 1;
9742025Smyers
9756678Sjj204856 status = AcpiEnterSleepStatePrep(5);
9766678Sjj204856 if (status != AE_OK) {
9776678Sjj204856 PSM_VERBOSE_POWEROFF(("acpi_poweroff: failed to prepare for "
9786678Sjj204856 "poweroff, status=0x%x\n", status));
9790Sstevel@tonic-gate return (1);
9800Sstevel@tonic-gate }
9816678Sjj204856 ACPI_DISABLE_IRQS();
9826678Sjj204856 status = AcpiEnterSleepState(5);
9830Sstevel@tonic-gate ACPI_ENABLE_IRQS();
9840Sstevel@tonic-gate
9850Sstevel@tonic-gate /* we should be off; if we get here it's an error */
9866678Sjj204856 PSM_VERBOSE_POWEROFF(("acpi_poweroff: failed to actually power "
9876678Sjj204856 "off, status=0x%x\n", status));
9880Sstevel@tonic-gate return (1);
9890Sstevel@tonic-gate }
990347Smyers
991347Smyers
992347Smyers /*
993347Smyers * psm_set_elcr() sets ELCR bit for specified vector
994347Smyers */
995347Smyers void
psm_set_elcr(int vecno,int val)996347Smyers psm_set_elcr(int vecno, int val)
997347Smyers {
998347Smyers int elcr_port = ELCR_PORT1 + (vecno >> 3);
999347Smyers int elcr_bit = 1 << (vecno & 0x07);
1000347Smyers
1001347Smyers ASSERT((vecno >= 0) && (vecno < 16));
1002347Smyers
1003347Smyers if (val) {
1004347Smyers /* set bit to force level-triggered mode */
1005347Smyers outb(elcr_port, inb(elcr_port) | elcr_bit);
1006347Smyers } else {
1007347Smyers /* clear bit to force edge-triggered mode */
1008347Smyers outb(elcr_port, inb(elcr_port) & ~elcr_bit);
1009347Smyers }
1010347Smyers }
1011347Smyers
1012347Smyers /*
1013347Smyers * psm_get_elcr() returns status of ELCR bit for specific vector
1014347Smyers */
1015347Smyers int
psm_get_elcr(int vecno)1016347Smyers psm_get_elcr(int vecno)
1017347Smyers {
1018347Smyers int elcr_port = ELCR_PORT1 + (vecno >> 3);
1019347Smyers int elcr_bit = 1 << (vecno & 0x07);
1020347Smyers
1021347Smyers ASSERT((vecno >= 0) && (vecno < 16));
1022347Smyers
1023347Smyers return ((inb(elcr_port) & elcr_bit) ? 1 : 0);
1024347Smyers }
1025