xref: /onnv-gate/usr/src/uts/i86pc/io/pcplusmp/apic_introp.c (revision 881:e6bc7f4b8a33)
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
50Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
60Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
70Sstevel@tonic-gate  * with the License.
80Sstevel@tonic-gate  *
90Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
100Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
110Sstevel@tonic-gate  * See the License for the specific language governing permissions
120Sstevel@tonic-gate  * and limitations under the License.
130Sstevel@tonic-gate  *
140Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
150Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
160Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
170Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
180Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
190Sstevel@tonic-gate  *
200Sstevel@tonic-gate  * CDDL HEADER END
210Sstevel@tonic-gate  */
220Sstevel@tonic-gate /*
230Sstevel@tonic-gate  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
240Sstevel@tonic-gate  * Use is subject to license terms.
250Sstevel@tonic-gate  */
260Sstevel@tonic-gate 
270Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
280Sstevel@tonic-gate 
290Sstevel@tonic-gate /*
300Sstevel@tonic-gate  * apic_introp.c:
310Sstevel@tonic-gate  *	Has code for Advanced DDI interrupt framework support.
320Sstevel@tonic-gate  */
330Sstevel@tonic-gate 
340Sstevel@tonic-gate #include <sys/cpuvar.h>
350Sstevel@tonic-gate #include <sys/psm.h>
360Sstevel@tonic-gate #include "apic.h"
370Sstevel@tonic-gate #include <sys/sunddi.h>
380Sstevel@tonic-gate #include <sys/ddi_impldefs.h>
390Sstevel@tonic-gate #include <sys/trap.h>
400Sstevel@tonic-gate #include <sys/pci.h>
410Sstevel@tonic-gate #include <sys/pci_intr_lib.h>
420Sstevel@tonic-gate 
430Sstevel@tonic-gate /*
440Sstevel@tonic-gate  *	Local Function Prototypes
450Sstevel@tonic-gate  */
460Sstevel@tonic-gate int		apic_pci_msi_enable_vector(dev_info_t *, int, int,
470Sstevel@tonic-gate 		    int, int, int);
480Sstevel@tonic-gate apic_irq_t	*apic_find_irq(dev_info_t *, struct intrspec *, int);
490Sstevel@tonic-gate static int	apic_get_pending(apic_irq_t *, int);
500Sstevel@tonic-gate static void	apic_clear_mask(apic_irq_t *);
510Sstevel@tonic-gate static void	apic_set_mask(apic_irq_t *);
520Sstevel@tonic-gate static uchar_t	apic_find_multi_vectors(int, int);
530Sstevel@tonic-gate int		apic_navail_vector(dev_info_t *, int);
540Sstevel@tonic-gate int		apic_alloc_vectors(dev_info_t *, int, int, int, int);
550Sstevel@tonic-gate void		apic_free_vectors(dev_info_t *, int, int, int, int);
560Sstevel@tonic-gate int		apic_intr_ops(dev_info_t *, ddi_intr_handle_impl_t *,
570Sstevel@tonic-gate 		    psm_intr_op_t, int *);
580Sstevel@tonic-gate 
590Sstevel@tonic-gate extern int	intr_clear(void);
600Sstevel@tonic-gate extern void	intr_restore(uint_t);
610Sstevel@tonic-gate extern uchar_t	apic_bind_intr(dev_info_t *, int, uchar_t, uchar_t);
620Sstevel@tonic-gate extern int	apic_allocate_irq(int);
630Sstevel@tonic-gate extern int	apic_introp_xlate(dev_info_t *, struct intrspec *, int);
640Sstevel@tonic-gate 
650Sstevel@tonic-gate /*
660Sstevel@tonic-gate  * MSI support flag:
670Sstevel@tonic-gate  * reflects whether MSI is supported at APIC level
680Sstevel@tonic-gate  * it can also be patched through /etc/system
690Sstevel@tonic-gate  *
700Sstevel@tonic-gate  *  0 = default value - don't know and need to call apic_check_msi_support()
710Sstevel@tonic-gate  *      to find out then set it accordingly
720Sstevel@tonic-gate  *  1 = supported
730Sstevel@tonic-gate  * -1 = not supported
740Sstevel@tonic-gate  */
750Sstevel@tonic-gate int	apic_support_msi = 0;
760Sstevel@tonic-gate 
770Sstevel@tonic-gate /* Multiple vector support for MSI */
780Sstevel@tonic-gate int	apic_multi_msi_enable = 1;
790Sstevel@tonic-gate int	apic_multi_msi_max = 2;
800Sstevel@tonic-gate 
810Sstevel@tonic-gate extern uchar_t		apic_ipltopri[MAXIPL+1];
820Sstevel@tonic-gate extern uchar_t		apic_vector_to_irq[APIC_MAX_VECTOR+1];
830Sstevel@tonic-gate extern int		apic_max_device_irq;
840Sstevel@tonic-gate extern int		apic_min_device_irq;
850Sstevel@tonic-gate extern apic_irq_t	*apic_irq_table[APIC_MAX_VECTOR+1];
860Sstevel@tonic-gate extern volatile uint32_t *apicadr; /* virtual addr of local APIC */
870Sstevel@tonic-gate extern volatile int32_t	*apicioadr[MAX_IO_APIC];
880Sstevel@tonic-gate extern lock_t		apic_ioapic_lock;
890Sstevel@tonic-gate extern kmutex_t		airq_mutex;
900Sstevel@tonic-gate extern apic_cpus_info_t	*apic_cpus;
91*881Sjohnny extern int apic_first_avail_irq;
920Sstevel@tonic-gate 
930Sstevel@tonic-gate 
940Sstevel@tonic-gate /*
950Sstevel@tonic-gate  * apic_pci_msi_enable_vector:
960Sstevel@tonic-gate  *	Set the address/data fields in the MSI/X capability structure
970Sstevel@tonic-gate  *	XXX: MSI-X support
980Sstevel@tonic-gate  */
990Sstevel@tonic-gate /* ARGSUSED */
1000Sstevel@tonic-gate int
1010Sstevel@tonic-gate apic_pci_msi_enable_vector(dev_info_t *dip, int type, int inum, int vector,
1020Sstevel@tonic-gate     int count, int target_apic_id)
1030Sstevel@tonic-gate {
1040Sstevel@tonic-gate 	uint64_t	msi_addr, msi_data;
1050Sstevel@tonic-gate 
1060Sstevel@tonic-gate 	DDI_INTR_IMPLDBG((CE_CONT, "apic_pci_msi_enable_vector: dip=0x%p\n"
1070Sstevel@tonic-gate 	    "\tdriver = %s, inum=0x%x vector=0x%x apicid=0x%x\n", (void *)dip,
1080Sstevel@tonic-gate 	    ddi_driver_name(dip), inum, vector, target_apic_id));
1090Sstevel@tonic-gate 
1100Sstevel@tonic-gate 	/* MSI Address */
1110Sstevel@tonic-gate 	msi_addr = (MSI_ADDR_HDR | (target_apic_id << MSI_ADDR_DEST_SHIFT));
1120Sstevel@tonic-gate 	msi_addr |= ((MSI_ADDR_RH_FIXED << MSI_ADDR_RH_SHIFT) |
1130Sstevel@tonic-gate 		    (MSI_ADDR_DM_PHYSICAL << MSI_ADDR_DM_SHIFT));
1140Sstevel@tonic-gate 
1150Sstevel@tonic-gate 	/* MSI Data: MSI is edge triggered according to spec */
1160Sstevel@tonic-gate 	msi_data = ((MSI_DATA_TM_EDGE << MSI_DATA_TM_SHIFT) | vector);
1170Sstevel@tonic-gate 
1180Sstevel@tonic-gate 	DDI_INTR_IMPLDBG((CE_CONT, "apic_pci_msi_enable_vector: addr=0x%lx "
1190Sstevel@tonic-gate 	    "data=0x%lx\n", (long)msi_addr, (long)msi_data));
1200Sstevel@tonic-gate 
1210Sstevel@tonic-gate 	if (pci_msi_configure(dip, type, count, inum, msi_addr, msi_data) !=
1220Sstevel@tonic-gate 	    DDI_SUCCESS) {
1230Sstevel@tonic-gate 		DDI_INTR_IMPLDBG((CE_CONT, "apic_pci_msi_enable_vector: "
1240Sstevel@tonic-gate 		    "pci_msi_configure failed\n"));
1250Sstevel@tonic-gate 		return (PSM_FAILURE);
1260Sstevel@tonic-gate 	}
1270Sstevel@tonic-gate 
1280Sstevel@tonic-gate 	return (PSM_SUCCESS);
1290Sstevel@tonic-gate }
1300Sstevel@tonic-gate 
1310Sstevel@tonic-gate 
1320Sstevel@tonic-gate /*
1330Sstevel@tonic-gate  * This function returns the no. of vectors available for the pri.
1340Sstevel@tonic-gate  * dip is not used at this moment.  If we really don't need that,
1350Sstevel@tonic-gate  * it will be removed.
1360Sstevel@tonic-gate  */
1370Sstevel@tonic-gate /*ARGSUSED*/
1380Sstevel@tonic-gate int
1390Sstevel@tonic-gate apic_navail_vector(dev_info_t *dip, int pri)
1400Sstevel@tonic-gate {
1410Sstevel@tonic-gate 	int	lowest, highest, i, navail, count;
1420Sstevel@tonic-gate 
1430Sstevel@tonic-gate 	DDI_INTR_IMPLDBG((CE_CONT, "apic_navail_vector: dip: %p, pri: %x\n",
1440Sstevel@tonic-gate 	    (void *)dip, pri));
1450Sstevel@tonic-gate 
1460Sstevel@tonic-gate 	highest = apic_ipltopri[pri] + APIC_VECTOR_MASK;
1470Sstevel@tonic-gate 	lowest = apic_ipltopri[pri - 1] + APIC_VECTOR_PER_IPL;
1480Sstevel@tonic-gate 	navail = count = 0;
1490Sstevel@tonic-gate 
1500Sstevel@tonic-gate 	/* It has to be contiguous */
1510Sstevel@tonic-gate 	for (i = lowest; i < highest; i++) {
1520Sstevel@tonic-gate 		count = 0;
1530Sstevel@tonic-gate 		while ((apic_vector_to_irq[i] == APIC_RESV_IRQ) &&
1540Sstevel@tonic-gate 			(i < highest)) {
1550Sstevel@tonic-gate 			if ((i == T_FASTTRAP) || (i == APIC_SPUR_INTR))
1560Sstevel@tonic-gate 				break;
1570Sstevel@tonic-gate 			count++;
1580Sstevel@tonic-gate 			i++;
1590Sstevel@tonic-gate 		}
1600Sstevel@tonic-gate 		if (count > navail)
1610Sstevel@tonic-gate 			navail = count;
1620Sstevel@tonic-gate 	}
1630Sstevel@tonic-gate 	return (navail);
1640Sstevel@tonic-gate }
1650Sstevel@tonic-gate 
1660Sstevel@tonic-gate static uchar_t
1670Sstevel@tonic-gate apic_find_multi_vectors(int pri, int count)
1680Sstevel@tonic-gate {
1690Sstevel@tonic-gate 	int	lowest, highest, i, navail, start;
1700Sstevel@tonic-gate 
1710Sstevel@tonic-gate 	DDI_INTR_IMPLDBG((CE_CONT, "apic_find_mult: pri: %x, count: %x\n",
1720Sstevel@tonic-gate 	    pri, count));
1730Sstevel@tonic-gate 
1740Sstevel@tonic-gate 	highest = apic_ipltopri[pri] + APIC_VECTOR_MASK;
1750Sstevel@tonic-gate 	lowest = apic_ipltopri[pri - 1] + APIC_VECTOR_PER_IPL;
1760Sstevel@tonic-gate 	navail = 0;
1770Sstevel@tonic-gate 
1780Sstevel@tonic-gate 	/* It has to be contiguous */
1790Sstevel@tonic-gate 	for (i = lowest; i < highest; i++) {
1800Sstevel@tonic-gate 		navail = 0;
1810Sstevel@tonic-gate 		start = i;
1820Sstevel@tonic-gate 		while ((apic_vector_to_irq[i] == APIC_RESV_IRQ) &&
1830Sstevel@tonic-gate 			(i < highest)) {
1840Sstevel@tonic-gate 			if ((i == T_FASTTRAP) || (i == APIC_SPUR_INTR))
1850Sstevel@tonic-gate 				break;
1860Sstevel@tonic-gate 			navail++;
1870Sstevel@tonic-gate 			if (navail >= count)
1880Sstevel@tonic-gate 				return (start);
1890Sstevel@tonic-gate 			i++;
1900Sstevel@tonic-gate 		}
1910Sstevel@tonic-gate 	}
1920Sstevel@tonic-gate 	return (0);
1930Sstevel@tonic-gate }
1940Sstevel@tonic-gate 
1950Sstevel@tonic-gate 
1960Sstevel@tonic-gate /*
1970Sstevel@tonic-gate  * It finds the apic_irq_t associates with the dip, ispec and type.
1980Sstevel@tonic-gate  */
1990Sstevel@tonic-gate apic_irq_t *
2000Sstevel@tonic-gate apic_find_irq(dev_info_t *dip, struct intrspec *ispec, int type)
2010Sstevel@tonic-gate {
2020Sstevel@tonic-gate 	apic_irq_t	*irqp;
2030Sstevel@tonic-gate 	int i;
2040Sstevel@tonic-gate 
2050Sstevel@tonic-gate 	DDI_INTR_IMPLDBG((CE_CONT, "apic_find_irq: dip=0x%p vec=0x%x "
2060Sstevel@tonic-gate 	    "ipl=0x%x type=0x%x\n", (void *)dip, ispec->intrspec_vec,
2070Sstevel@tonic-gate 	    ispec->intrspec_pri, type));
2080Sstevel@tonic-gate 
2090Sstevel@tonic-gate 	for (i = apic_min_device_irq; i <= apic_max_device_irq; i++) {
2100Sstevel@tonic-gate 		if ((irqp = apic_irq_table[i]) == NULL)
2110Sstevel@tonic-gate 			continue;
2120Sstevel@tonic-gate 		if ((irqp->airq_dip == dip) &&
2130Sstevel@tonic-gate 		    (irqp->airq_origirq == ispec->intrspec_vec) &&
2140Sstevel@tonic-gate 		    (irqp->airq_ipl == ispec->intrspec_pri)) {
2150Sstevel@tonic-gate 			if (DDI_INTR_IS_MSI_OR_MSIX(type)) {
2160Sstevel@tonic-gate 				if (APIC_IS_MSI_OR_MSIX_INDEX(irqp->
2170Sstevel@tonic-gate 				    airq_mps_intr_index))
2180Sstevel@tonic-gate 					return (irqp);
2190Sstevel@tonic-gate 			} else
2200Sstevel@tonic-gate 				return (irqp);
2210Sstevel@tonic-gate 		}
2220Sstevel@tonic-gate 	}
2230Sstevel@tonic-gate 	DDI_INTR_IMPLDBG((CE_CONT, "apic_find_irq: return NULL\n"));
2240Sstevel@tonic-gate 	return (NULL);
2250Sstevel@tonic-gate }
2260Sstevel@tonic-gate 
2270Sstevel@tonic-gate 
2280Sstevel@tonic-gate /*
2290Sstevel@tonic-gate  * This function will return the pending bit of the irqp.
2300Sstevel@tonic-gate  * It either comes from the IRR register of the APIC or the RDT
2310Sstevel@tonic-gate  * entry of the I/O APIC.
2320Sstevel@tonic-gate  * For the IRR to work, it needs to be to its binding CPU
2330Sstevel@tonic-gate  */
2340Sstevel@tonic-gate static int
2350Sstevel@tonic-gate apic_get_pending(apic_irq_t *irqp, int type)
2360Sstevel@tonic-gate {
2370Sstevel@tonic-gate 	int			bit, index, irr, pending;
2380Sstevel@tonic-gate 	int			intin_no;
2390Sstevel@tonic-gate 	volatile int32_t 	*ioapic;
2400Sstevel@tonic-gate 
2410Sstevel@tonic-gate 	DDI_INTR_IMPLDBG((CE_CONT, "apic_get_pending: irqp: %p, cpuid: %x "
2420Sstevel@tonic-gate 	    "type: %x\n", (void *)irqp, irqp->airq_cpu & ~IRQ_USER_BOUND,
2430Sstevel@tonic-gate 	    type));
2440Sstevel@tonic-gate 
2450Sstevel@tonic-gate 	/* need to get on the bound cpu */
2460Sstevel@tonic-gate 	mutex_enter(&cpu_lock);
2470Sstevel@tonic-gate 	affinity_set(irqp->airq_cpu & ~IRQ_USER_BOUND);
2480Sstevel@tonic-gate 
2490Sstevel@tonic-gate 	index = irqp->airq_vector / 32;
2500Sstevel@tonic-gate 	bit = irqp->airq_vector % 32;
2510Sstevel@tonic-gate 	irr = apicadr[APIC_IRR_REG + index];
2520Sstevel@tonic-gate 
2530Sstevel@tonic-gate 	affinity_clear();
2540Sstevel@tonic-gate 	mutex_exit(&cpu_lock);
2550Sstevel@tonic-gate 
2560Sstevel@tonic-gate 	pending = (irr & (1 << bit)) ? 1 : 0;
2570Sstevel@tonic-gate 	if (!pending && (type == DDI_INTR_TYPE_FIXED)) {
2580Sstevel@tonic-gate 		/* check I/O APIC for fixed interrupt */
2590Sstevel@tonic-gate 		intin_no = irqp->airq_intin_no;
2600Sstevel@tonic-gate 		ioapic = apicioadr[irqp->airq_ioapicindex];
2610Sstevel@tonic-gate 		pending = (READ_IOAPIC_RDT_ENTRY_LOW_DWORD(ioapic, intin_no) &
2620Sstevel@tonic-gate 		    AV_PENDING) ? 1 : 0;
2630Sstevel@tonic-gate 	}
2640Sstevel@tonic-gate 	return (pending);
2650Sstevel@tonic-gate }
2660Sstevel@tonic-gate 
2670Sstevel@tonic-gate 
2680Sstevel@tonic-gate /*
2690Sstevel@tonic-gate  * This function will clear the mask for the interrupt on the I/O APIC
2700Sstevel@tonic-gate  */
2710Sstevel@tonic-gate static void
2720Sstevel@tonic-gate apic_clear_mask(apic_irq_t *irqp)
2730Sstevel@tonic-gate {
2740Sstevel@tonic-gate 	int			intin_no;
2750Sstevel@tonic-gate 	int			iflag;
2760Sstevel@tonic-gate 	int32_t			rdt_entry;
2770Sstevel@tonic-gate 	volatile int32_t 	*ioapic;
2780Sstevel@tonic-gate 
2790Sstevel@tonic-gate 	DDI_INTR_IMPLDBG((CE_CONT, "apic_clear_mask: irqp: %p\n",
2800Sstevel@tonic-gate 	    (void *)irqp));
2810Sstevel@tonic-gate 
2820Sstevel@tonic-gate 	intin_no = irqp->airq_intin_no;
2830Sstevel@tonic-gate 	ioapic = apicioadr[irqp->airq_ioapicindex];
2840Sstevel@tonic-gate 
2850Sstevel@tonic-gate 	iflag = intr_clear();
2860Sstevel@tonic-gate 	lock_set(&apic_ioapic_lock);
2870Sstevel@tonic-gate 
2880Sstevel@tonic-gate 	rdt_entry = READ_IOAPIC_RDT_ENTRY_LOW_DWORD(ioapic, intin_no);
2890Sstevel@tonic-gate 
2900Sstevel@tonic-gate 	/* clear mask */
2910Sstevel@tonic-gate 	WRITE_IOAPIC_RDT_ENTRY_LOW_DWORD(ioapic, intin_no,
2920Sstevel@tonic-gate 	    ((~AV_MASK) & rdt_entry));
2930Sstevel@tonic-gate 
2940Sstevel@tonic-gate 	lock_clear(&apic_ioapic_lock);
2950Sstevel@tonic-gate 	intr_restore(iflag);
2960Sstevel@tonic-gate }
2970Sstevel@tonic-gate 
2980Sstevel@tonic-gate 
2990Sstevel@tonic-gate /*
3000Sstevel@tonic-gate  * This function will mask the interrupt on the I/O APIC
3010Sstevel@tonic-gate  */
3020Sstevel@tonic-gate static void
3030Sstevel@tonic-gate apic_set_mask(apic_irq_t *irqp)
3040Sstevel@tonic-gate {
3050Sstevel@tonic-gate 	int			intin_no;
3060Sstevel@tonic-gate 	volatile int32_t 	*ioapic;
3070Sstevel@tonic-gate 	int			iflag;
3080Sstevel@tonic-gate 	int32_t			rdt_entry;
3090Sstevel@tonic-gate 
3100Sstevel@tonic-gate 	DDI_INTR_IMPLDBG((CE_CONT, "apic_set_mask: irqp: %p\n", (void *)irqp));
3110Sstevel@tonic-gate 
3120Sstevel@tonic-gate 	intin_no = irqp->airq_intin_no;
3130Sstevel@tonic-gate 	ioapic = apicioadr[irqp->airq_ioapicindex];
3140Sstevel@tonic-gate 
3150Sstevel@tonic-gate 	iflag = intr_clear();
3160Sstevel@tonic-gate 
3170Sstevel@tonic-gate 	lock_set(&apic_ioapic_lock);
3180Sstevel@tonic-gate 
3190Sstevel@tonic-gate 	rdt_entry = READ_IOAPIC_RDT_ENTRY_LOW_DWORD(ioapic, intin_no);
3200Sstevel@tonic-gate 
3210Sstevel@tonic-gate 	/* mask it */
3220Sstevel@tonic-gate 	WRITE_IOAPIC_RDT_ENTRY_LOW_DWORD(ioapic, intin_no,
3230Sstevel@tonic-gate 	    (AV_MASK | rdt_entry));
3240Sstevel@tonic-gate 
3250Sstevel@tonic-gate 	lock_clear(&apic_ioapic_lock);
3260Sstevel@tonic-gate 	intr_restore(iflag);
3270Sstevel@tonic-gate }
3280Sstevel@tonic-gate 
3290Sstevel@tonic-gate 
3300Sstevel@tonic-gate /*
3310Sstevel@tonic-gate  * This function allocate "count" vector(s) for the given "dip/pri/type"
3320Sstevel@tonic-gate  */
3330Sstevel@tonic-gate int
3340Sstevel@tonic-gate apic_alloc_vectors(dev_info_t *dip, int inum, int count, int pri, int type)
3350Sstevel@tonic-gate {
3360Sstevel@tonic-gate 	int	rcount, i;
3370Sstevel@tonic-gate 	uchar_t	start, irqno, cpu;
3380Sstevel@tonic-gate 	short	idx;
3390Sstevel@tonic-gate 	major_t	major;
3400Sstevel@tonic-gate 	apic_irq_t	*irqptr;
3410Sstevel@tonic-gate 
3420Sstevel@tonic-gate 	/* for MSI/X only */
3430Sstevel@tonic-gate 	if (!DDI_INTR_IS_MSI_OR_MSIX(type))
3440Sstevel@tonic-gate 		return (0);
3450Sstevel@tonic-gate 
3460Sstevel@tonic-gate 	DDI_INTR_IMPLDBG((CE_CONT, "apic_alloc_vectors: dip=0x%p type=%d "
3470Sstevel@tonic-gate 	    "inum=0x%x  pri=0x%x count=0x%x\n",
3480Sstevel@tonic-gate 	    (void *)dip, type, inum, pri, count));
3490Sstevel@tonic-gate 
3500Sstevel@tonic-gate 	if (count > 1) {
3510Sstevel@tonic-gate 		if (apic_multi_msi_enable == 0)
3520Sstevel@tonic-gate 			count = 1;
3530Sstevel@tonic-gate 		else if (count > apic_multi_msi_max)
3540Sstevel@tonic-gate 			count = apic_multi_msi_max;
3550Sstevel@tonic-gate 	}
3560Sstevel@tonic-gate 
3570Sstevel@tonic-gate 	if ((rcount = apic_navail_vector(dip, pri)) > count)
3580Sstevel@tonic-gate 		rcount = count;
3590Sstevel@tonic-gate 
3600Sstevel@tonic-gate 	mutex_enter(&airq_mutex);
3610Sstevel@tonic-gate 
3620Sstevel@tonic-gate 	for (start = 0; rcount > 0; rcount--) {
3630Sstevel@tonic-gate 		if ((start = apic_find_multi_vectors(pri, rcount)) != 0)
3640Sstevel@tonic-gate 			break;
3650Sstevel@tonic-gate 	}
3660Sstevel@tonic-gate 
3670Sstevel@tonic-gate 	if (start == 0) {
3680Sstevel@tonic-gate 		/* no vector available */
3690Sstevel@tonic-gate 		mutex_exit(&airq_mutex);
3700Sstevel@tonic-gate 		return (0);
3710Sstevel@tonic-gate 	}
3720Sstevel@tonic-gate 
3730Sstevel@tonic-gate 	idx = (short)((type == DDI_INTR_TYPE_MSI) ? MSI_INDEX : MSIX_INDEX);
3740Sstevel@tonic-gate 	major = (dip != NULL) ? ddi_name_to_major(ddi_get_name(dip)) : 0;
3750Sstevel@tonic-gate 	for (i = 0; i < rcount; i++) {
376*881Sjohnny 		if ((irqno = apic_allocate_irq(apic_first_avail_irq)) ==
3770Sstevel@tonic-gate 		    (uchar_t)-1) {
3780Sstevel@tonic-gate 			mutex_exit(&airq_mutex);
3790Sstevel@tonic-gate 			DDI_INTR_IMPLDBG((CE_CONT, "apic_alloc_vectors: "
3800Sstevel@tonic-gate 			    "apic_allocate_irq failed\n"));
3810Sstevel@tonic-gate 			return (i);
3820Sstevel@tonic-gate 		}
3830Sstevel@tonic-gate 		apic_max_device_irq = max(irqno, apic_max_device_irq);
3840Sstevel@tonic-gate 		apic_min_device_irq = min(irqno, apic_min_device_irq);
3850Sstevel@tonic-gate 		irqptr = apic_irq_table[irqno];
3860Sstevel@tonic-gate #ifdef	DEBUG
3870Sstevel@tonic-gate 		if (apic_vector_to_irq[start + i] != APIC_RESV_IRQ)
3880Sstevel@tonic-gate 			DDI_INTR_IMPLDBG((CE_CONT, "apic_alloc_vectors: "
3890Sstevel@tonic-gate 			    "apic_vector_to_irq is not APIC_RESV_IRQ\n"));
3900Sstevel@tonic-gate #endif
3910Sstevel@tonic-gate 		apic_vector_to_irq[start + i] = (uchar_t)irqno;
3920Sstevel@tonic-gate 
3930Sstevel@tonic-gate 		irqptr->airq_vector = (uchar_t)(start + i);
3940Sstevel@tonic-gate 		irqptr->airq_ioapicindex = (uchar_t)inum;	/* start */
3950Sstevel@tonic-gate 		irqptr->airq_intin_no = (uchar_t)rcount;
3960Sstevel@tonic-gate 		irqptr->airq_ipl = pri;
3970Sstevel@tonic-gate 		irqptr->airq_vector = start + i;
3980Sstevel@tonic-gate 		irqptr->airq_origirq = (uchar_t)(inum + i);
3990Sstevel@tonic-gate 		irqptr->airq_share_id = 0;
4000Sstevel@tonic-gate 		irqptr->airq_mps_intr_index = idx;
4010Sstevel@tonic-gate 		irqptr->airq_dip = dip;
4020Sstevel@tonic-gate 		irqptr->airq_major = major;
4030Sstevel@tonic-gate 		if (i == 0) /* they all bound to the same cpu */
4040Sstevel@tonic-gate 			cpu = irqptr->airq_cpu = apic_bind_intr(dip, irqno,
4050Sstevel@tonic-gate 				0xff, 0xff);
4060Sstevel@tonic-gate 		else
4070Sstevel@tonic-gate 			irqptr->airq_cpu = cpu;
4080Sstevel@tonic-gate 		DDI_INTR_IMPLDBG((CE_CONT, "apic_alloc_vectors: irq=0x%x "
4090Sstevel@tonic-gate 		    "dip=0x%p vector=0x%x origirq=0x%x pri=0x%x\n", irqno,
4100Sstevel@tonic-gate 		    (void *)irqptr->airq_dip, irqptr->airq_vector,
4110Sstevel@tonic-gate 		    irqptr->airq_origirq, pri));
4120Sstevel@tonic-gate 	}
4130Sstevel@tonic-gate 	mutex_exit(&airq_mutex);
4140Sstevel@tonic-gate 	return (rcount);
4150Sstevel@tonic-gate }
4160Sstevel@tonic-gate 
4170Sstevel@tonic-gate 
4180Sstevel@tonic-gate void
4190Sstevel@tonic-gate apic_free_vectors(dev_info_t *dip, int inum, int count, int pri, int type)
4200Sstevel@tonic-gate {
4210Sstevel@tonic-gate 	int i;
4220Sstevel@tonic-gate 	apic_irq_t *irqptr;
4230Sstevel@tonic-gate 	struct intrspec ispec;
4240Sstevel@tonic-gate 
4250Sstevel@tonic-gate 	DDI_INTR_IMPLDBG((CE_CONT, "apic_free_vectors: dip: %p inum: %x "
4260Sstevel@tonic-gate 	    "count: %x pri: %x type: %x\n",
4270Sstevel@tonic-gate 	    (void *)dip, inum, count, pri, type));
4280Sstevel@tonic-gate 
4290Sstevel@tonic-gate 	/* for MSI/X only */
4300Sstevel@tonic-gate 	if (!DDI_INTR_IS_MSI_OR_MSIX(type))
4310Sstevel@tonic-gate 		return;
4320Sstevel@tonic-gate 
4330Sstevel@tonic-gate 	for (i = 0; i < count; i++) {
4340Sstevel@tonic-gate 		DDI_INTR_IMPLDBG((CE_CONT, "apic_free_vectors: inum=0x%x "
4350Sstevel@tonic-gate 		    "pri=0x%x count=0x%x\n", inum, pri, count));
4360Sstevel@tonic-gate 		ispec.intrspec_vec = inum + i;
4370Sstevel@tonic-gate 		ispec.intrspec_pri = pri;
4380Sstevel@tonic-gate 		if ((irqptr = apic_find_irq(dip, &ispec, type)) == NULL) {
4390Sstevel@tonic-gate 			DDI_INTR_IMPLDBG((CE_CONT, "apic_free_vectors: "
4400Sstevel@tonic-gate 			    "dip=0x%p inum=0x%x pri=0x%x apic_find_irq() "
4410Sstevel@tonic-gate 			    "failed\n", (void *)dip, inum, pri));
4420Sstevel@tonic-gate 			continue;
4430Sstevel@tonic-gate 		}
4440Sstevel@tonic-gate 		irqptr->airq_mps_intr_index = FREE_INDEX;
4450Sstevel@tonic-gate 		apic_vector_to_irq[irqptr->airq_vector] = APIC_RESV_IRQ;
4460Sstevel@tonic-gate 	}
4470Sstevel@tonic-gate }
4480Sstevel@tonic-gate 
4490Sstevel@tonic-gate 
4500Sstevel@tonic-gate /*
4510Sstevel@tonic-gate  * check whether the system supports MSI
4520Sstevel@tonic-gate  *
4530Sstevel@tonic-gate  * If PCI-E capability is found, then this must be a PCI-E system.
4540Sstevel@tonic-gate  * Since MSI is required for PCI-E system, it returns PSM_SUCCESS
4550Sstevel@tonic-gate  * to indicate this system supports MSI.
4560Sstevel@tonic-gate  */
4570Sstevel@tonic-gate int
4580Sstevel@tonic-gate apic_check_msi_support(dev_info_t *dip)
4590Sstevel@tonic-gate {
4600Sstevel@tonic-gate 
4610Sstevel@tonic-gate 	dev_info_t *rootdip;
462*881Sjohnny 	char dev_type[16];
463*881Sjohnny 	int dev_len;
4640Sstevel@tonic-gate 
4650Sstevel@tonic-gate 	DDI_INTR_IMPLDBG((CE_CONT, "apic_check_msi_support: dip: 0x%p\n",
4660Sstevel@tonic-gate 	    (void *)dip));
4670Sstevel@tonic-gate 
4680Sstevel@tonic-gate 	/* check whether the device or its ancestors have PCI-E capability */
469*881Sjohnny 	for (rootdip = ddi_root_node(); dip != rootdip;
470*881Sjohnny 	    dip = ddi_get_parent(dip)) {
4710Sstevel@tonic-gate 
472*881Sjohnny 		DDI_INTR_IMPLDBG((CE_CONT, "apic_check_msi_support: dip: 0x%p,"
473*881Sjohnny 		    " driver: %s, binding: %s, nodename: %s\n", (void *)dip,
474*881Sjohnny 		    ddi_driver_name(dip), ddi_binding_name(dip),
475*881Sjohnny 		    ddi_node_name(dip)));
476*881Sjohnny 		dev_len = sizeof (dev_type);
477*881Sjohnny 		if (ddi_getlongprop_buf(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
478*881Sjohnny 		    "device_type", (caddr_t)dev_type, &dev_len)
479*881Sjohnny 		    != DDI_PROP_SUCCESS)
480*881Sjohnny 			continue;
481*881Sjohnny 		if (strcmp(dev_type, "pciex") == 0)
482*881Sjohnny 			return (PSM_SUCCESS);
4830Sstevel@tonic-gate 	}
4840Sstevel@tonic-gate 
4850Sstevel@tonic-gate 	/* MSI is not supported on this system */
486*881Sjohnny 	DDI_INTR_IMPLDBG((CE_CONT, "apic_check_msi_support: no 'pciex' "
487*881Sjohnny 	    "device_type found\n"));
4880Sstevel@tonic-gate 	return (PSM_FAILURE);
4890Sstevel@tonic-gate }
4900Sstevel@tonic-gate 
4910Sstevel@tonic-gate /*
4920Sstevel@tonic-gate  * This function provides external interface to the nexus for all
4930Sstevel@tonic-gate  * functionalities related to the new DDI interrupt framework.
4940Sstevel@tonic-gate  *
4950Sstevel@tonic-gate  * Input:
4960Sstevel@tonic-gate  * dip     - pointer to the dev_info structure of the requested device
4970Sstevel@tonic-gate  * hdlp    - pointer to the internal interrupt handle structure for the
4980Sstevel@tonic-gate  *	     requested interrupt
4990Sstevel@tonic-gate  * intr_op - opcode for this call
5000Sstevel@tonic-gate  * result  - pointer to the integer that will hold the result to be
5010Sstevel@tonic-gate  *	     passed back if return value is PSM_SUCCESS
5020Sstevel@tonic-gate  *
5030Sstevel@tonic-gate  * Output:
5040Sstevel@tonic-gate  * return value is either PSM_SUCCESS or PSM_FAILURE
5050Sstevel@tonic-gate  */
5060Sstevel@tonic-gate int
5070Sstevel@tonic-gate apic_intr_ops(dev_info_t *dip, ddi_intr_handle_impl_t *hdlp,
5080Sstevel@tonic-gate     psm_intr_op_t intr_op, int *result)
5090Sstevel@tonic-gate {
5100Sstevel@tonic-gate 	int		cap;
5110Sstevel@tonic-gate 	int		count_vec;
5120Sstevel@tonic-gate 	int		old_priority;
5130Sstevel@tonic-gate 	int		new_priority;
5140Sstevel@tonic-gate 	apic_irq_t	*irqp;
5150Sstevel@tonic-gate 	struct intrspec *ispec, intr_spec;
5160Sstevel@tonic-gate 
5170Sstevel@tonic-gate 	DDI_INTR_IMPLDBG((CE_CONT, "apic_intr_ops: dip: %p hdlp: %p "
5180Sstevel@tonic-gate 	    "intr_op: %x\n", (void *)dip, (void *)hdlp, intr_op));
5190Sstevel@tonic-gate 
5200Sstevel@tonic-gate 	ispec = &intr_spec;
5210Sstevel@tonic-gate 	ispec->intrspec_pri = hdlp->ih_pri;
5220Sstevel@tonic-gate 	ispec->intrspec_vec = hdlp->ih_inum;
5230Sstevel@tonic-gate 	ispec->intrspec_func = hdlp->ih_cb_func;
5240Sstevel@tonic-gate 
5250Sstevel@tonic-gate 	switch (intr_op) {
5260Sstevel@tonic-gate 	case PSM_INTR_OP_CHECK_MSI:
5270Sstevel@tonic-gate 		/*
5280Sstevel@tonic-gate 		 * Check MSI/X is supported or not at APIC level and
5290Sstevel@tonic-gate 		 * masked off the MSI/X bits in hdlp->ih_type if not
5300Sstevel@tonic-gate 		 * supported before return.  If MSI/X is supported,
5310Sstevel@tonic-gate 		 * leave the ih_type unchanged and return.
5320Sstevel@tonic-gate 		 *
5330Sstevel@tonic-gate 		 * hdlp->ih_type passed in from the nexus has all the
5340Sstevel@tonic-gate 		 * interrupt types supported by the device.
5350Sstevel@tonic-gate 		 */
5360Sstevel@tonic-gate 		if (apic_support_msi == 0) {
5370Sstevel@tonic-gate 			/*
5380Sstevel@tonic-gate 			 * if apic_support_msi is not set, call
5390Sstevel@tonic-gate 			 * apic_check_msi_support() to check whether msi
5400Sstevel@tonic-gate 			 * is supported first
5410Sstevel@tonic-gate 			 */
5420Sstevel@tonic-gate 			if (apic_check_msi_support(dip) == PSM_SUCCESS)
5430Sstevel@tonic-gate 				apic_support_msi = 1;
5440Sstevel@tonic-gate 			else
5450Sstevel@tonic-gate 				apic_support_msi = -1;
5460Sstevel@tonic-gate 		}
5470Sstevel@tonic-gate 		if (apic_support_msi == 1)
5480Sstevel@tonic-gate 			*result = hdlp->ih_type;
5490Sstevel@tonic-gate 		else
5500Sstevel@tonic-gate 			*result = hdlp->ih_type & ~(DDI_INTR_TYPE_MSI |
5510Sstevel@tonic-gate 			    DDI_INTR_TYPE_MSIX);
5520Sstevel@tonic-gate 		break;
5530Sstevel@tonic-gate 	case PSM_INTR_OP_ALLOC_VECTORS:
5540Sstevel@tonic-gate 		*result = apic_alloc_vectors(dip, hdlp->ih_inum,
5550Sstevel@tonic-gate 		    hdlp->ih_scratch1, hdlp->ih_pri, hdlp->ih_type);
5560Sstevel@tonic-gate 		break;
5570Sstevel@tonic-gate 	case PSM_INTR_OP_FREE_VECTORS:
5580Sstevel@tonic-gate 		apic_free_vectors(dip, hdlp->ih_inum, hdlp->ih_scratch1,
5590Sstevel@tonic-gate 		    hdlp->ih_pri, hdlp->ih_type);
5600Sstevel@tonic-gate 		break;
5610Sstevel@tonic-gate 	case PSM_INTR_OP_NAVAIL_VECTORS:
5620Sstevel@tonic-gate 		*result = apic_navail_vector(dip, hdlp->ih_pri);
5630Sstevel@tonic-gate 		break;
5640Sstevel@tonic-gate 	case PSM_INTR_OP_XLATE_VECTOR:
5650Sstevel@tonic-gate 		ispec = (struct intrspec *)hdlp->ih_private;
5660Sstevel@tonic-gate 		*result = apic_introp_xlate(dip, ispec, hdlp->ih_type);
5670Sstevel@tonic-gate 		break;
5680Sstevel@tonic-gate 	case PSM_INTR_OP_GET_PENDING:
5690Sstevel@tonic-gate 		if ((irqp = apic_find_irq(dip, ispec, hdlp->ih_type)) == NULL)
5700Sstevel@tonic-gate 			return (PSM_FAILURE);
5710Sstevel@tonic-gate 		*result = apic_get_pending(irqp, hdlp->ih_type);
5720Sstevel@tonic-gate 		break;
5730Sstevel@tonic-gate 	case PSM_INTR_OP_CLEAR_MASK:
5740Sstevel@tonic-gate 		if (hdlp->ih_type != DDI_INTR_TYPE_FIXED)
5750Sstevel@tonic-gate 			return (PSM_FAILURE);
5760Sstevel@tonic-gate 		irqp = apic_find_irq(dip, ispec, hdlp->ih_type);
5770Sstevel@tonic-gate 		if (irqp == NULL)
5780Sstevel@tonic-gate 			return (PSM_FAILURE);
5790Sstevel@tonic-gate 		apic_clear_mask(irqp);
5800Sstevel@tonic-gate 		break;
5810Sstevel@tonic-gate 	case PSM_INTR_OP_SET_MASK:
5820Sstevel@tonic-gate 		if (hdlp->ih_type != DDI_INTR_TYPE_FIXED)
5830Sstevel@tonic-gate 			return (PSM_FAILURE);
5840Sstevel@tonic-gate 		if ((irqp = apic_find_irq(dip, ispec, hdlp->ih_type)) == NULL)
5850Sstevel@tonic-gate 			return (PSM_FAILURE);
5860Sstevel@tonic-gate 		apic_set_mask(irqp);
5870Sstevel@tonic-gate 		break;
5880Sstevel@tonic-gate 	case PSM_INTR_OP_GET_CAP:
5890Sstevel@tonic-gate 		cap = DDI_INTR_FLAG_PENDING;
5900Sstevel@tonic-gate 		if (hdlp->ih_type == DDI_INTR_TYPE_FIXED)
5910Sstevel@tonic-gate 			cap |= DDI_INTR_FLAG_MASKABLE;
5920Sstevel@tonic-gate 		*result = cap;
5930Sstevel@tonic-gate 		break;
5940Sstevel@tonic-gate 	case PSM_INTR_OP_GET_SHARED:
5950Sstevel@tonic-gate 		if (hdlp->ih_type != DDI_INTR_TYPE_FIXED)
5960Sstevel@tonic-gate 			return (PSM_FAILURE);
5970Sstevel@tonic-gate 		if ((irqp = apic_find_irq(dip, ispec, hdlp->ih_type)) == NULL)
5980Sstevel@tonic-gate 			return (PSM_FAILURE);
5990Sstevel@tonic-gate 		*result = irqp->airq_share ? 1: 0;
6000Sstevel@tonic-gate 		break;
6010Sstevel@tonic-gate 	case PSM_INTR_OP_SET_PRI:
6020Sstevel@tonic-gate 		old_priority = hdlp->ih_pri;	/* save old value */
6030Sstevel@tonic-gate 		new_priority = *(int *)result;	/* try the new value */
6040Sstevel@tonic-gate 
6050Sstevel@tonic-gate 		/* First, check if "hdlp->ih_scratch1" vectors exist? */
6060Sstevel@tonic-gate 		if (apic_navail_vector(dip, new_priority) < hdlp->ih_scratch1)
6070Sstevel@tonic-gate 			return (PSM_FAILURE);
6080Sstevel@tonic-gate 
6090Sstevel@tonic-gate 		/* Now allocate the vectors */
6100Sstevel@tonic-gate 		count_vec = apic_alloc_vectors(dip, hdlp->ih_inum,
6110Sstevel@tonic-gate 		    hdlp->ih_scratch1, new_priority, hdlp->ih_type);
6120Sstevel@tonic-gate 
6130Sstevel@tonic-gate 		/* Did we get fewer vectors? */
6140Sstevel@tonic-gate 		if (count_vec != hdlp->ih_scratch1) {
6150Sstevel@tonic-gate 			apic_free_vectors(dip, hdlp->ih_inum, count_vec,
6160Sstevel@tonic-gate 			    new_priority, hdlp->ih_type);
6170Sstevel@tonic-gate 			return (PSM_FAILURE);
6180Sstevel@tonic-gate 		}
6190Sstevel@tonic-gate 
6200Sstevel@tonic-gate 		/* Finally, free the previously allocated vectors */
6210Sstevel@tonic-gate 		apic_free_vectors(dip, hdlp->ih_inum, count_vec,
6220Sstevel@tonic-gate 		    old_priority, hdlp->ih_type);
6230Sstevel@tonic-gate 		hdlp->ih_pri = new_priority; /* set the new value */
6240Sstevel@tonic-gate 		break;
6250Sstevel@tonic-gate 	case PSM_INTR_OP_SET_CAP:
6260Sstevel@tonic-gate 	default:
6270Sstevel@tonic-gate 		return (PSM_FAILURE);
6280Sstevel@tonic-gate 	}
6290Sstevel@tonic-gate 	return (PSM_SUCCESS);
6300Sstevel@tonic-gate }
631