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