xref: /onnv-gate/usr/src/uts/i86pc/io/pcplusmp/apic_introp.c (revision 0:68f95e015346)
1*0Sstevel@tonic-gate /*
2*0Sstevel@tonic-gate  * CDDL HEADER START
3*0Sstevel@tonic-gate  *
4*0Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*0Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
6*0Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
7*0Sstevel@tonic-gate  * with the License.
8*0Sstevel@tonic-gate  *
9*0Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10*0Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
11*0Sstevel@tonic-gate  * See the License for the specific language governing permissions
12*0Sstevel@tonic-gate  * and limitations under the License.
13*0Sstevel@tonic-gate  *
14*0Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
15*0Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16*0Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
17*0Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
18*0Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
19*0Sstevel@tonic-gate  *
20*0Sstevel@tonic-gate  * CDDL HEADER END
21*0Sstevel@tonic-gate  */
22*0Sstevel@tonic-gate /*
23*0Sstevel@tonic-gate  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24*0Sstevel@tonic-gate  * Use is subject to license terms.
25*0Sstevel@tonic-gate  */
26*0Sstevel@tonic-gate 
27*0Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
28*0Sstevel@tonic-gate 
29*0Sstevel@tonic-gate /*
30*0Sstevel@tonic-gate  * apic_introp.c:
31*0Sstevel@tonic-gate  *	Has code for Advanced DDI interrupt framework support.
32*0Sstevel@tonic-gate  */
33*0Sstevel@tonic-gate 
34*0Sstevel@tonic-gate #include <sys/cpuvar.h>
35*0Sstevel@tonic-gate #include <sys/psm.h>
36*0Sstevel@tonic-gate #include "apic.h"
37*0Sstevel@tonic-gate #include <sys/sunddi.h>
38*0Sstevel@tonic-gate #include <sys/ddi_impldefs.h>
39*0Sstevel@tonic-gate #include <sys/trap.h>
40*0Sstevel@tonic-gate #include <sys/pci.h>
41*0Sstevel@tonic-gate #include <sys/pci_intr_lib.h>
42*0Sstevel@tonic-gate 
43*0Sstevel@tonic-gate /*
44*0Sstevel@tonic-gate  *	Local Function Prototypes
45*0Sstevel@tonic-gate  */
46*0Sstevel@tonic-gate int		apic_pci_msi_enable_vector(dev_info_t *, int, int,
47*0Sstevel@tonic-gate 		    int, int, int);
48*0Sstevel@tonic-gate apic_irq_t	*apic_find_irq(dev_info_t *, struct intrspec *, int);
49*0Sstevel@tonic-gate static int	apic_get_pending(apic_irq_t *, int);
50*0Sstevel@tonic-gate static void	apic_clear_mask(apic_irq_t *);
51*0Sstevel@tonic-gate static void	apic_set_mask(apic_irq_t *);
52*0Sstevel@tonic-gate static uchar_t	apic_find_multi_vectors(int, int);
53*0Sstevel@tonic-gate int		apic_navail_vector(dev_info_t *, int);
54*0Sstevel@tonic-gate int		apic_alloc_vectors(dev_info_t *, int, int, int, int);
55*0Sstevel@tonic-gate void		apic_free_vectors(dev_info_t *, int, int, int, int);
56*0Sstevel@tonic-gate int		apic_intr_ops(dev_info_t *, ddi_intr_handle_impl_t *,
57*0Sstevel@tonic-gate 		    psm_intr_op_t, int *);
58*0Sstevel@tonic-gate 
59*0Sstevel@tonic-gate extern int	intr_clear(void);
60*0Sstevel@tonic-gate extern void	intr_restore(uint_t);
61*0Sstevel@tonic-gate extern uchar_t	apic_bind_intr(dev_info_t *, int, uchar_t, uchar_t);
62*0Sstevel@tonic-gate extern int	apic_allocate_irq(int);
63*0Sstevel@tonic-gate extern int	apic_introp_xlate(dev_info_t *, struct intrspec *, int);
64*0Sstevel@tonic-gate 
65*0Sstevel@tonic-gate /*
66*0Sstevel@tonic-gate  * MSI support flag:
67*0Sstevel@tonic-gate  * reflects whether MSI is supported at APIC level
68*0Sstevel@tonic-gate  * it can also be patched through /etc/system
69*0Sstevel@tonic-gate  *
70*0Sstevel@tonic-gate  *  0 = default value - don't know and need to call apic_check_msi_support()
71*0Sstevel@tonic-gate  *      to find out then set it accordingly
72*0Sstevel@tonic-gate  *  1 = supported
73*0Sstevel@tonic-gate  * -1 = not supported
74*0Sstevel@tonic-gate  */
75*0Sstevel@tonic-gate int	apic_support_msi = 0;
76*0Sstevel@tonic-gate 
77*0Sstevel@tonic-gate /* Multiple vector support for MSI */
78*0Sstevel@tonic-gate int	apic_multi_msi_enable = 1;
79*0Sstevel@tonic-gate int	apic_multi_msi_max = 2;
80*0Sstevel@tonic-gate 
81*0Sstevel@tonic-gate extern uchar_t		apic_ipltopri[MAXIPL+1];
82*0Sstevel@tonic-gate extern uchar_t		apic_vector_to_irq[APIC_MAX_VECTOR+1];
83*0Sstevel@tonic-gate extern int		apic_max_device_irq;
84*0Sstevel@tonic-gate extern int		apic_min_device_irq;
85*0Sstevel@tonic-gate extern apic_irq_t	*apic_irq_table[APIC_MAX_VECTOR+1];
86*0Sstevel@tonic-gate extern volatile uint32_t *apicadr; /* virtual addr of local APIC */
87*0Sstevel@tonic-gate extern volatile int32_t	*apicioadr[MAX_IO_APIC];
88*0Sstevel@tonic-gate extern lock_t		apic_ioapic_lock;
89*0Sstevel@tonic-gate extern kmutex_t		airq_mutex;
90*0Sstevel@tonic-gate extern apic_cpus_info_t	*apic_cpus;
91*0Sstevel@tonic-gate 
92*0Sstevel@tonic-gate 
93*0Sstevel@tonic-gate /*
94*0Sstevel@tonic-gate  * apic_pci_msi_enable_vector:
95*0Sstevel@tonic-gate  *	Set the address/data fields in the MSI/X capability structure
96*0Sstevel@tonic-gate  *	XXX: MSI-X support
97*0Sstevel@tonic-gate  */
98*0Sstevel@tonic-gate /* ARGSUSED */
99*0Sstevel@tonic-gate int
100*0Sstevel@tonic-gate apic_pci_msi_enable_vector(dev_info_t *dip, int type, int inum, int vector,
101*0Sstevel@tonic-gate     int count, int target_apic_id)
102*0Sstevel@tonic-gate {
103*0Sstevel@tonic-gate 	uint64_t	msi_addr, msi_data;
104*0Sstevel@tonic-gate 
105*0Sstevel@tonic-gate 	DDI_INTR_IMPLDBG((CE_CONT, "apic_pci_msi_enable_vector: dip=0x%p\n"
106*0Sstevel@tonic-gate 	    "\tdriver = %s, inum=0x%x vector=0x%x apicid=0x%x\n", (void *)dip,
107*0Sstevel@tonic-gate 	    ddi_driver_name(dip), inum, vector, target_apic_id));
108*0Sstevel@tonic-gate 
109*0Sstevel@tonic-gate 	/* MSI Address */
110*0Sstevel@tonic-gate 	msi_addr = (MSI_ADDR_HDR | (target_apic_id << MSI_ADDR_DEST_SHIFT));
111*0Sstevel@tonic-gate 	msi_addr |= ((MSI_ADDR_RH_FIXED << MSI_ADDR_RH_SHIFT) |
112*0Sstevel@tonic-gate 		    (MSI_ADDR_DM_PHYSICAL << MSI_ADDR_DM_SHIFT));
113*0Sstevel@tonic-gate 
114*0Sstevel@tonic-gate 	/* MSI Data: MSI is edge triggered according to spec */
115*0Sstevel@tonic-gate 	msi_data = ((MSI_DATA_TM_EDGE << MSI_DATA_TM_SHIFT) | vector);
116*0Sstevel@tonic-gate 
117*0Sstevel@tonic-gate 	DDI_INTR_IMPLDBG((CE_CONT, "apic_pci_msi_enable_vector: addr=0x%lx "
118*0Sstevel@tonic-gate 	    "data=0x%lx\n", (long)msi_addr, (long)msi_data));
119*0Sstevel@tonic-gate 
120*0Sstevel@tonic-gate 	if (pci_msi_configure(dip, type, count, inum, msi_addr, msi_data) !=
121*0Sstevel@tonic-gate 	    DDI_SUCCESS) {
122*0Sstevel@tonic-gate 		DDI_INTR_IMPLDBG((CE_CONT, "apic_pci_msi_enable_vector: "
123*0Sstevel@tonic-gate 		    "pci_msi_configure failed\n"));
124*0Sstevel@tonic-gate 		return (PSM_FAILURE);
125*0Sstevel@tonic-gate 	}
126*0Sstevel@tonic-gate 
127*0Sstevel@tonic-gate 	return (PSM_SUCCESS);
128*0Sstevel@tonic-gate }
129*0Sstevel@tonic-gate 
130*0Sstevel@tonic-gate 
131*0Sstevel@tonic-gate /*
132*0Sstevel@tonic-gate  * This function returns the no. of vectors available for the pri.
133*0Sstevel@tonic-gate  * dip is not used at this moment.  If we really don't need that,
134*0Sstevel@tonic-gate  * it will be removed.
135*0Sstevel@tonic-gate  */
136*0Sstevel@tonic-gate /*ARGSUSED*/
137*0Sstevel@tonic-gate int
138*0Sstevel@tonic-gate apic_navail_vector(dev_info_t *dip, int pri)
139*0Sstevel@tonic-gate {
140*0Sstevel@tonic-gate 	int	lowest, highest, i, navail, count;
141*0Sstevel@tonic-gate 
142*0Sstevel@tonic-gate 	DDI_INTR_IMPLDBG((CE_CONT, "apic_navail_vector: dip: %p, pri: %x\n",
143*0Sstevel@tonic-gate 	    (void *)dip, pri));
144*0Sstevel@tonic-gate 
145*0Sstevel@tonic-gate 	highest = apic_ipltopri[pri] + APIC_VECTOR_MASK;
146*0Sstevel@tonic-gate 	lowest = apic_ipltopri[pri - 1] + APIC_VECTOR_PER_IPL;
147*0Sstevel@tonic-gate 	navail = count = 0;
148*0Sstevel@tonic-gate 
149*0Sstevel@tonic-gate 	/* It has to be contiguous */
150*0Sstevel@tonic-gate 	for (i = lowest; i < highest; i++) {
151*0Sstevel@tonic-gate 		count = 0;
152*0Sstevel@tonic-gate 		while ((apic_vector_to_irq[i] == APIC_RESV_IRQ) &&
153*0Sstevel@tonic-gate 			(i < highest)) {
154*0Sstevel@tonic-gate 			if ((i == T_FASTTRAP) || (i == APIC_SPUR_INTR))
155*0Sstevel@tonic-gate 				break;
156*0Sstevel@tonic-gate 			count++;
157*0Sstevel@tonic-gate 			i++;
158*0Sstevel@tonic-gate 		}
159*0Sstevel@tonic-gate 		if (count > navail)
160*0Sstevel@tonic-gate 			navail = count;
161*0Sstevel@tonic-gate 	}
162*0Sstevel@tonic-gate 	return (navail);
163*0Sstevel@tonic-gate }
164*0Sstevel@tonic-gate 
165*0Sstevel@tonic-gate static uchar_t
166*0Sstevel@tonic-gate apic_find_multi_vectors(int pri, int count)
167*0Sstevel@tonic-gate {
168*0Sstevel@tonic-gate 	int	lowest, highest, i, navail, start;
169*0Sstevel@tonic-gate 
170*0Sstevel@tonic-gate 	DDI_INTR_IMPLDBG((CE_CONT, "apic_find_mult: pri: %x, count: %x\n",
171*0Sstevel@tonic-gate 	    pri, count));
172*0Sstevel@tonic-gate 
173*0Sstevel@tonic-gate 	highest = apic_ipltopri[pri] + APIC_VECTOR_MASK;
174*0Sstevel@tonic-gate 	lowest = apic_ipltopri[pri - 1] + APIC_VECTOR_PER_IPL;
175*0Sstevel@tonic-gate 	navail = 0;
176*0Sstevel@tonic-gate 
177*0Sstevel@tonic-gate 	/* It has to be contiguous */
178*0Sstevel@tonic-gate 	for (i = lowest; i < highest; i++) {
179*0Sstevel@tonic-gate 		navail = 0;
180*0Sstevel@tonic-gate 		start = i;
181*0Sstevel@tonic-gate 		while ((apic_vector_to_irq[i] == APIC_RESV_IRQ) &&
182*0Sstevel@tonic-gate 			(i < highest)) {
183*0Sstevel@tonic-gate 			if ((i == T_FASTTRAP) || (i == APIC_SPUR_INTR))
184*0Sstevel@tonic-gate 				break;
185*0Sstevel@tonic-gate 			navail++;
186*0Sstevel@tonic-gate 			if (navail >= count)
187*0Sstevel@tonic-gate 				return (start);
188*0Sstevel@tonic-gate 			i++;
189*0Sstevel@tonic-gate 		}
190*0Sstevel@tonic-gate 	}
191*0Sstevel@tonic-gate 	return (0);
192*0Sstevel@tonic-gate }
193*0Sstevel@tonic-gate 
194*0Sstevel@tonic-gate 
195*0Sstevel@tonic-gate /*
196*0Sstevel@tonic-gate  * It finds the apic_irq_t associates with the dip, ispec and type.
197*0Sstevel@tonic-gate  */
198*0Sstevel@tonic-gate apic_irq_t *
199*0Sstevel@tonic-gate apic_find_irq(dev_info_t *dip, struct intrspec *ispec, int type)
200*0Sstevel@tonic-gate {
201*0Sstevel@tonic-gate 	apic_irq_t	*irqp;
202*0Sstevel@tonic-gate 	int i;
203*0Sstevel@tonic-gate 
204*0Sstevel@tonic-gate 	DDI_INTR_IMPLDBG((CE_CONT, "apic_find_irq: dip=0x%p vec=0x%x "
205*0Sstevel@tonic-gate 	    "ipl=0x%x type=0x%x\n", (void *)dip, ispec->intrspec_vec,
206*0Sstevel@tonic-gate 	    ispec->intrspec_pri, type));
207*0Sstevel@tonic-gate 
208*0Sstevel@tonic-gate 	for (i = apic_min_device_irq; i <= apic_max_device_irq; i++) {
209*0Sstevel@tonic-gate 		if ((irqp = apic_irq_table[i]) == NULL)
210*0Sstevel@tonic-gate 			continue;
211*0Sstevel@tonic-gate 		if ((irqp->airq_dip == dip) &&
212*0Sstevel@tonic-gate 		    (irqp->airq_origirq == ispec->intrspec_vec) &&
213*0Sstevel@tonic-gate 		    (irqp->airq_ipl == ispec->intrspec_pri)) {
214*0Sstevel@tonic-gate 			if (DDI_INTR_IS_MSI_OR_MSIX(type)) {
215*0Sstevel@tonic-gate 				if (APIC_IS_MSI_OR_MSIX_INDEX(irqp->
216*0Sstevel@tonic-gate 				    airq_mps_intr_index))
217*0Sstevel@tonic-gate 					return (irqp);
218*0Sstevel@tonic-gate 			} else
219*0Sstevel@tonic-gate 				return (irqp);
220*0Sstevel@tonic-gate 		}
221*0Sstevel@tonic-gate 	}
222*0Sstevel@tonic-gate 	DDI_INTR_IMPLDBG((CE_CONT, "apic_find_irq: return NULL\n"));
223*0Sstevel@tonic-gate 	return (NULL);
224*0Sstevel@tonic-gate }
225*0Sstevel@tonic-gate 
226*0Sstevel@tonic-gate 
227*0Sstevel@tonic-gate /*
228*0Sstevel@tonic-gate  * This function will return the pending bit of the irqp.
229*0Sstevel@tonic-gate  * It either comes from the IRR register of the APIC or the RDT
230*0Sstevel@tonic-gate  * entry of the I/O APIC.
231*0Sstevel@tonic-gate  * For the IRR to work, it needs to be to its binding CPU
232*0Sstevel@tonic-gate  */
233*0Sstevel@tonic-gate static int
234*0Sstevel@tonic-gate apic_get_pending(apic_irq_t *irqp, int type)
235*0Sstevel@tonic-gate {
236*0Sstevel@tonic-gate 	int			bit, index, irr, pending;
237*0Sstevel@tonic-gate 	int			intin_no;
238*0Sstevel@tonic-gate 	volatile int32_t 	*ioapic;
239*0Sstevel@tonic-gate 
240*0Sstevel@tonic-gate 	DDI_INTR_IMPLDBG((CE_CONT, "apic_get_pending: irqp: %p, cpuid: %x "
241*0Sstevel@tonic-gate 	    "type: %x\n", (void *)irqp, irqp->airq_cpu & ~IRQ_USER_BOUND,
242*0Sstevel@tonic-gate 	    type));
243*0Sstevel@tonic-gate 
244*0Sstevel@tonic-gate 	/* need to get on the bound cpu */
245*0Sstevel@tonic-gate 	mutex_enter(&cpu_lock);
246*0Sstevel@tonic-gate 	affinity_set(irqp->airq_cpu & ~IRQ_USER_BOUND);
247*0Sstevel@tonic-gate 
248*0Sstevel@tonic-gate 	index = irqp->airq_vector / 32;
249*0Sstevel@tonic-gate 	bit = irqp->airq_vector % 32;
250*0Sstevel@tonic-gate 	irr = apicadr[APIC_IRR_REG + index];
251*0Sstevel@tonic-gate 
252*0Sstevel@tonic-gate 	affinity_clear();
253*0Sstevel@tonic-gate 	mutex_exit(&cpu_lock);
254*0Sstevel@tonic-gate 
255*0Sstevel@tonic-gate 	pending = (irr & (1 << bit)) ? 1 : 0;
256*0Sstevel@tonic-gate 	if (!pending && (type == DDI_INTR_TYPE_FIXED)) {
257*0Sstevel@tonic-gate 		/* check I/O APIC for fixed interrupt */
258*0Sstevel@tonic-gate 		intin_no = irqp->airq_intin_no;
259*0Sstevel@tonic-gate 		ioapic = apicioadr[irqp->airq_ioapicindex];
260*0Sstevel@tonic-gate 		pending = (READ_IOAPIC_RDT_ENTRY_LOW_DWORD(ioapic, intin_no) &
261*0Sstevel@tonic-gate 		    AV_PENDING) ? 1 : 0;
262*0Sstevel@tonic-gate 	}
263*0Sstevel@tonic-gate 	return (pending);
264*0Sstevel@tonic-gate }
265*0Sstevel@tonic-gate 
266*0Sstevel@tonic-gate 
267*0Sstevel@tonic-gate /*
268*0Sstevel@tonic-gate  * This function will clear the mask for the interrupt on the I/O APIC
269*0Sstevel@tonic-gate  */
270*0Sstevel@tonic-gate static void
271*0Sstevel@tonic-gate apic_clear_mask(apic_irq_t *irqp)
272*0Sstevel@tonic-gate {
273*0Sstevel@tonic-gate 	int			intin_no;
274*0Sstevel@tonic-gate 	int			iflag;
275*0Sstevel@tonic-gate 	int32_t			rdt_entry;
276*0Sstevel@tonic-gate 	volatile int32_t 	*ioapic;
277*0Sstevel@tonic-gate 
278*0Sstevel@tonic-gate 	DDI_INTR_IMPLDBG((CE_CONT, "apic_clear_mask: irqp: %p\n",
279*0Sstevel@tonic-gate 	    (void *)irqp));
280*0Sstevel@tonic-gate 
281*0Sstevel@tonic-gate 	intin_no = irqp->airq_intin_no;
282*0Sstevel@tonic-gate 	ioapic = apicioadr[irqp->airq_ioapicindex];
283*0Sstevel@tonic-gate 
284*0Sstevel@tonic-gate 	iflag = intr_clear();
285*0Sstevel@tonic-gate 	lock_set(&apic_ioapic_lock);
286*0Sstevel@tonic-gate 
287*0Sstevel@tonic-gate 	rdt_entry = READ_IOAPIC_RDT_ENTRY_LOW_DWORD(ioapic, intin_no);
288*0Sstevel@tonic-gate 
289*0Sstevel@tonic-gate 	/* clear mask */
290*0Sstevel@tonic-gate 	WRITE_IOAPIC_RDT_ENTRY_LOW_DWORD(ioapic, intin_no,
291*0Sstevel@tonic-gate 	    ((~AV_MASK) & rdt_entry));
292*0Sstevel@tonic-gate 
293*0Sstevel@tonic-gate 	lock_clear(&apic_ioapic_lock);
294*0Sstevel@tonic-gate 	intr_restore(iflag);
295*0Sstevel@tonic-gate }
296*0Sstevel@tonic-gate 
297*0Sstevel@tonic-gate 
298*0Sstevel@tonic-gate /*
299*0Sstevel@tonic-gate  * This function will mask the interrupt on the I/O APIC
300*0Sstevel@tonic-gate  */
301*0Sstevel@tonic-gate static void
302*0Sstevel@tonic-gate apic_set_mask(apic_irq_t *irqp)
303*0Sstevel@tonic-gate {
304*0Sstevel@tonic-gate 	int			intin_no;
305*0Sstevel@tonic-gate 	volatile int32_t 	*ioapic;
306*0Sstevel@tonic-gate 	int			iflag;
307*0Sstevel@tonic-gate 	int32_t			rdt_entry;
308*0Sstevel@tonic-gate 
309*0Sstevel@tonic-gate 	DDI_INTR_IMPLDBG((CE_CONT, "apic_set_mask: irqp: %p\n", (void *)irqp));
310*0Sstevel@tonic-gate 
311*0Sstevel@tonic-gate 	intin_no = irqp->airq_intin_no;
312*0Sstevel@tonic-gate 	ioapic = apicioadr[irqp->airq_ioapicindex];
313*0Sstevel@tonic-gate 
314*0Sstevel@tonic-gate 	iflag = intr_clear();
315*0Sstevel@tonic-gate 
316*0Sstevel@tonic-gate 	lock_set(&apic_ioapic_lock);
317*0Sstevel@tonic-gate 
318*0Sstevel@tonic-gate 	rdt_entry = READ_IOAPIC_RDT_ENTRY_LOW_DWORD(ioapic, intin_no);
319*0Sstevel@tonic-gate 
320*0Sstevel@tonic-gate 	/* mask it */
321*0Sstevel@tonic-gate 	WRITE_IOAPIC_RDT_ENTRY_LOW_DWORD(ioapic, intin_no,
322*0Sstevel@tonic-gate 	    (AV_MASK | rdt_entry));
323*0Sstevel@tonic-gate 
324*0Sstevel@tonic-gate 	lock_clear(&apic_ioapic_lock);
325*0Sstevel@tonic-gate 	intr_restore(iflag);
326*0Sstevel@tonic-gate }
327*0Sstevel@tonic-gate 
328*0Sstevel@tonic-gate 
329*0Sstevel@tonic-gate /*
330*0Sstevel@tonic-gate  * This function allocate "count" vector(s) for the given "dip/pri/type"
331*0Sstevel@tonic-gate  */
332*0Sstevel@tonic-gate int
333*0Sstevel@tonic-gate apic_alloc_vectors(dev_info_t *dip, int inum, int count, int pri, int type)
334*0Sstevel@tonic-gate {
335*0Sstevel@tonic-gate 	int	rcount, i;
336*0Sstevel@tonic-gate 	uchar_t	start, irqno, cpu;
337*0Sstevel@tonic-gate 	short	idx;
338*0Sstevel@tonic-gate 	major_t	major;
339*0Sstevel@tonic-gate 	apic_irq_t	*irqptr;
340*0Sstevel@tonic-gate 
341*0Sstevel@tonic-gate 	/* for MSI/X only */
342*0Sstevel@tonic-gate 	if (!DDI_INTR_IS_MSI_OR_MSIX(type))
343*0Sstevel@tonic-gate 		return (0);
344*0Sstevel@tonic-gate 
345*0Sstevel@tonic-gate 	DDI_INTR_IMPLDBG((CE_CONT, "apic_alloc_vectors: dip=0x%p type=%d "
346*0Sstevel@tonic-gate 	    "inum=0x%x  pri=0x%x count=0x%x\n",
347*0Sstevel@tonic-gate 	    (void *)dip, type, inum, pri, count));
348*0Sstevel@tonic-gate 
349*0Sstevel@tonic-gate 	if (count > 1) {
350*0Sstevel@tonic-gate 		if (apic_multi_msi_enable == 0)
351*0Sstevel@tonic-gate 			count = 1;
352*0Sstevel@tonic-gate 		else if (count > apic_multi_msi_max)
353*0Sstevel@tonic-gate 			count = apic_multi_msi_max;
354*0Sstevel@tonic-gate 	}
355*0Sstevel@tonic-gate 
356*0Sstevel@tonic-gate 	if ((rcount = apic_navail_vector(dip, pri)) > count)
357*0Sstevel@tonic-gate 		rcount = count;
358*0Sstevel@tonic-gate 
359*0Sstevel@tonic-gate 	mutex_enter(&airq_mutex);
360*0Sstevel@tonic-gate 
361*0Sstevel@tonic-gate 	for (start = 0; rcount > 0; rcount--) {
362*0Sstevel@tonic-gate 		if ((start = apic_find_multi_vectors(pri, rcount)) != 0)
363*0Sstevel@tonic-gate 			break;
364*0Sstevel@tonic-gate 	}
365*0Sstevel@tonic-gate 
366*0Sstevel@tonic-gate 	if (start == 0) {
367*0Sstevel@tonic-gate 		/* no vector available */
368*0Sstevel@tonic-gate 		mutex_exit(&airq_mutex);
369*0Sstevel@tonic-gate 		return (0);
370*0Sstevel@tonic-gate 	}
371*0Sstevel@tonic-gate 
372*0Sstevel@tonic-gate 	idx = (short)((type == DDI_INTR_TYPE_MSI) ? MSI_INDEX : MSIX_INDEX);
373*0Sstevel@tonic-gate 	major = (dip != NULL) ? ddi_name_to_major(ddi_get_name(dip)) : 0;
374*0Sstevel@tonic-gate 	for (i = 0; i < rcount; i++) {
375*0Sstevel@tonic-gate 		if ((irqno = apic_allocate_irq(APIC_FIRST_FREE_IRQ)) ==
376*0Sstevel@tonic-gate 		    (uchar_t)-1) {
377*0Sstevel@tonic-gate 			mutex_exit(&airq_mutex);
378*0Sstevel@tonic-gate 			DDI_INTR_IMPLDBG((CE_CONT, "apic_alloc_vectors: "
379*0Sstevel@tonic-gate 			    "apic_allocate_irq failed\n"));
380*0Sstevel@tonic-gate 			return (i);
381*0Sstevel@tonic-gate 		}
382*0Sstevel@tonic-gate 		apic_max_device_irq = max(irqno, apic_max_device_irq);
383*0Sstevel@tonic-gate 		apic_min_device_irq = min(irqno, apic_min_device_irq);
384*0Sstevel@tonic-gate 		irqptr = apic_irq_table[irqno];
385*0Sstevel@tonic-gate #ifdef	DEBUG
386*0Sstevel@tonic-gate 		if (apic_vector_to_irq[start + i] != APIC_RESV_IRQ)
387*0Sstevel@tonic-gate 			DDI_INTR_IMPLDBG((CE_CONT, "apic_alloc_vectors: "
388*0Sstevel@tonic-gate 			    "apic_vector_to_irq is not APIC_RESV_IRQ\n"));
389*0Sstevel@tonic-gate #endif
390*0Sstevel@tonic-gate 		apic_vector_to_irq[start + i] = (uchar_t)irqno;
391*0Sstevel@tonic-gate 
392*0Sstevel@tonic-gate 		irqptr->airq_vector = (uchar_t)(start + i);
393*0Sstevel@tonic-gate 		irqptr->airq_ioapicindex = (uchar_t)inum;	/* start */
394*0Sstevel@tonic-gate 		irqptr->airq_intin_no = (uchar_t)rcount;
395*0Sstevel@tonic-gate 		irqptr->airq_ipl = pri;
396*0Sstevel@tonic-gate 		irqptr->airq_vector = start + i;
397*0Sstevel@tonic-gate 		irqptr->airq_origirq = (uchar_t)(inum + i);
398*0Sstevel@tonic-gate 		irqptr->airq_share_id = 0;
399*0Sstevel@tonic-gate 		irqptr->airq_mps_intr_index = idx;
400*0Sstevel@tonic-gate 		irqptr->airq_dip = dip;
401*0Sstevel@tonic-gate 		irqptr->airq_major = major;
402*0Sstevel@tonic-gate 		if (i == 0) /* they all bound to the same cpu */
403*0Sstevel@tonic-gate 			cpu = irqptr->airq_cpu = apic_bind_intr(dip, irqno,
404*0Sstevel@tonic-gate 				0xff, 0xff);
405*0Sstevel@tonic-gate 		else
406*0Sstevel@tonic-gate 			irqptr->airq_cpu = cpu;
407*0Sstevel@tonic-gate 		DDI_INTR_IMPLDBG((CE_CONT, "apic_alloc_vectors: irq=0x%x "
408*0Sstevel@tonic-gate 		    "dip=0x%p vector=0x%x origirq=0x%x pri=0x%x\n", irqno,
409*0Sstevel@tonic-gate 		    (void *)irqptr->airq_dip, irqptr->airq_vector,
410*0Sstevel@tonic-gate 		    irqptr->airq_origirq, pri));
411*0Sstevel@tonic-gate 	}
412*0Sstevel@tonic-gate 	mutex_exit(&airq_mutex);
413*0Sstevel@tonic-gate 	return (rcount);
414*0Sstevel@tonic-gate }
415*0Sstevel@tonic-gate 
416*0Sstevel@tonic-gate 
417*0Sstevel@tonic-gate void
418*0Sstevel@tonic-gate apic_free_vectors(dev_info_t *dip, int inum, int count, int pri, int type)
419*0Sstevel@tonic-gate {
420*0Sstevel@tonic-gate 	int i;
421*0Sstevel@tonic-gate 	apic_irq_t *irqptr;
422*0Sstevel@tonic-gate 	struct intrspec ispec;
423*0Sstevel@tonic-gate 
424*0Sstevel@tonic-gate 	DDI_INTR_IMPLDBG((CE_CONT, "apic_free_vectors: dip: %p inum: %x "
425*0Sstevel@tonic-gate 	    "count: %x pri: %x type: %x\n",
426*0Sstevel@tonic-gate 	    (void *)dip, inum, count, pri, type));
427*0Sstevel@tonic-gate 
428*0Sstevel@tonic-gate 	/* for MSI/X only */
429*0Sstevel@tonic-gate 	if (!DDI_INTR_IS_MSI_OR_MSIX(type))
430*0Sstevel@tonic-gate 		return;
431*0Sstevel@tonic-gate 
432*0Sstevel@tonic-gate 	for (i = 0; i < count; i++) {
433*0Sstevel@tonic-gate 		DDI_INTR_IMPLDBG((CE_CONT, "apic_free_vectors: inum=0x%x "
434*0Sstevel@tonic-gate 		    "pri=0x%x count=0x%x\n", inum, pri, count));
435*0Sstevel@tonic-gate 		ispec.intrspec_vec = inum + i;
436*0Sstevel@tonic-gate 		ispec.intrspec_pri = pri;
437*0Sstevel@tonic-gate 		if ((irqptr = apic_find_irq(dip, &ispec, type)) == NULL) {
438*0Sstevel@tonic-gate 			DDI_INTR_IMPLDBG((CE_CONT, "apic_free_vectors: "
439*0Sstevel@tonic-gate 			    "dip=0x%p inum=0x%x pri=0x%x apic_find_irq() "
440*0Sstevel@tonic-gate 			    "failed\n", (void *)dip, inum, pri));
441*0Sstevel@tonic-gate 			continue;
442*0Sstevel@tonic-gate 		}
443*0Sstevel@tonic-gate 		irqptr->airq_mps_intr_index = FREE_INDEX;
444*0Sstevel@tonic-gate 		apic_vector_to_irq[irqptr->airq_vector] = APIC_RESV_IRQ;
445*0Sstevel@tonic-gate 	}
446*0Sstevel@tonic-gate }
447*0Sstevel@tonic-gate 
448*0Sstevel@tonic-gate 
449*0Sstevel@tonic-gate /*
450*0Sstevel@tonic-gate  * check whether the system supports MSI
451*0Sstevel@tonic-gate  *
452*0Sstevel@tonic-gate  * If PCI-E capability is found, then this must be a PCI-E system.
453*0Sstevel@tonic-gate  * Since MSI is required for PCI-E system, it returns PSM_SUCCESS
454*0Sstevel@tonic-gate  * to indicate this system supports MSI.
455*0Sstevel@tonic-gate  */
456*0Sstevel@tonic-gate int
457*0Sstevel@tonic-gate apic_check_msi_support(dev_info_t *dip)
458*0Sstevel@tonic-gate {
459*0Sstevel@tonic-gate 
460*0Sstevel@tonic-gate 	dev_info_t *rootdip;
461*0Sstevel@tonic-gate 
462*0Sstevel@tonic-gate 	DDI_INTR_IMPLDBG((CE_CONT, "apic_check_msi_support: dip: 0x%p\n",
463*0Sstevel@tonic-gate 	    (void *)dip));
464*0Sstevel@tonic-gate 
465*0Sstevel@tonic-gate 	/* check whether the device or its ancestors have PCI-E capability */
466*0Sstevel@tonic-gate 	for (rootdip = ddi_root_node(); dip != rootdip &&
467*0Sstevel@tonic-gate 	    pci_check_pciex(dip) != DDI_SUCCESS; dip = ddi_get_parent(dip));
468*0Sstevel@tonic-gate 
469*0Sstevel@tonic-gate 	/* PCI-E capability found */
470*0Sstevel@tonic-gate 	if (dip != rootdip) {
471*0Sstevel@tonic-gate 		DDI_INTR_IMPLDBG((CE_CONT, "apic_check_msi_support: "
472*0Sstevel@tonic-gate 		    "PCI-E capability found @ nodename %s driver %s%d\n",
473*0Sstevel@tonic-gate 		    ddi_node_name(dip), ddi_driver_name(dip),
474*0Sstevel@tonic-gate 		    ddi_get_instance(dip)));
475*0Sstevel@tonic-gate 		return (PSM_SUCCESS);
476*0Sstevel@tonic-gate 	}
477*0Sstevel@tonic-gate 
478*0Sstevel@tonic-gate 	/* MSI is not supported on this system */
479*0Sstevel@tonic-gate 	DDI_INTR_IMPLDBG((CE_CONT, "apic_check_msi_support: "
480*0Sstevel@tonic-gate 	    "no PCI-E capability found\n"));
481*0Sstevel@tonic-gate 	return (PSM_FAILURE);
482*0Sstevel@tonic-gate }
483*0Sstevel@tonic-gate 
484*0Sstevel@tonic-gate /*
485*0Sstevel@tonic-gate  * This function provides external interface to the nexus for all
486*0Sstevel@tonic-gate  * functionalities related to the new DDI interrupt framework.
487*0Sstevel@tonic-gate  *
488*0Sstevel@tonic-gate  * Input:
489*0Sstevel@tonic-gate  * dip     - pointer to the dev_info structure of the requested device
490*0Sstevel@tonic-gate  * hdlp    - pointer to the internal interrupt handle structure for the
491*0Sstevel@tonic-gate  *	     requested interrupt
492*0Sstevel@tonic-gate  * intr_op - opcode for this call
493*0Sstevel@tonic-gate  * result  - pointer to the integer that will hold the result to be
494*0Sstevel@tonic-gate  *	     passed back if return value is PSM_SUCCESS
495*0Sstevel@tonic-gate  *
496*0Sstevel@tonic-gate  * Output:
497*0Sstevel@tonic-gate  * return value is either PSM_SUCCESS or PSM_FAILURE
498*0Sstevel@tonic-gate  */
499*0Sstevel@tonic-gate int
500*0Sstevel@tonic-gate apic_intr_ops(dev_info_t *dip, ddi_intr_handle_impl_t *hdlp,
501*0Sstevel@tonic-gate     psm_intr_op_t intr_op, int *result)
502*0Sstevel@tonic-gate {
503*0Sstevel@tonic-gate 	int		cap;
504*0Sstevel@tonic-gate 	int		count_vec;
505*0Sstevel@tonic-gate 	int		old_priority;
506*0Sstevel@tonic-gate 	int		new_priority;
507*0Sstevel@tonic-gate 	apic_irq_t	*irqp;
508*0Sstevel@tonic-gate 	struct intrspec *ispec, intr_spec;
509*0Sstevel@tonic-gate 
510*0Sstevel@tonic-gate 	DDI_INTR_IMPLDBG((CE_CONT, "apic_intr_ops: dip: %p hdlp: %p "
511*0Sstevel@tonic-gate 	    "intr_op: %x\n", (void *)dip, (void *)hdlp, intr_op));
512*0Sstevel@tonic-gate 
513*0Sstevel@tonic-gate 	ispec = &intr_spec;
514*0Sstevel@tonic-gate 	ispec->intrspec_pri = hdlp->ih_pri;
515*0Sstevel@tonic-gate 	ispec->intrspec_vec = hdlp->ih_inum;
516*0Sstevel@tonic-gate 	ispec->intrspec_func = hdlp->ih_cb_func;
517*0Sstevel@tonic-gate 
518*0Sstevel@tonic-gate 	switch (intr_op) {
519*0Sstevel@tonic-gate 	case PSM_INTR_OP_CHECK_MSI:
520*0Sstevel@tonic-gate 		/*
521*0Sstevel@tonic-gate 		 * Check MSI/X is supported or not at APIC level and
522*0Sstevel@tonic-gate 		 * masked off the MSI/X bits in hdlp->ih_type if not
523*0Sstevel@tonic-gate 		 * supported before return.  If MSI/X is supported,
524*0Sstevel@tonic-gate 		 * leave the ih_type unchanged and return.
525*0Sstevel@tonic-gate 		 *
526*0Sstevel@tonic-gate 		 * hdlp->ih_type passed in from the nexus has all the
527*0Sstevel@tonic-gate 		 * interrupt types supported by the device.
528*0Sstevel@tonic-gate 		 */
529*0Sstevel@tonic-gate 		if (apic_support_msi == 0) {
530*0Sstevel@tonic-gate 			/*
531*0Sstevel@tonic-gate 			 * if apic_support_msi is not set, call
532*0Sstevel@tonic-gate 			 * apic_check_msi_support() to check whether msi
533*0Sstevel@tonic-gate 			 * is supported first
534*0Sstevel@tonic-gate 			 */
535*0Sstevel@tonic-gate 			if (apic_check_msi_support(dip) == PSM_SUCCESS)
536*0Sstevel@tonic-gate 				apic_support_msi = 1;
537*0Sstevel@tonic-gate 			else
538*0Sstevel@tonic-gate 				apic_support_msi = -1;
539*0Sstevel@tonic-gate 		}
540*0Sstevel@tonic-gate 		if (apic_support_msi == 1)
541*0Sstevel@tonic-gate 			*result = hdlp->ih_type;
542*0Sstevel@tonic-gate 		else
543*0Sstevel@tonic-gate 			*result = hdlp->ih_type & ~(DDI_INTR_TYPE_MSI |
544*0Sstevel@tonic-gate 			    DDI_INTR_TYPE_MSIX);
545*0Sstevel@tonic-gate 		break;
546*0Sstevel@tonic-gate 	case PSM_INTR_OP_ALLOC_VECTORS:
547*0Sstevel@tonic-gate 		*result = apic_alloc_vectors(dip, hdlp->ih_inum,
548*0Sstevel@tonic-gate 		    hdlp->ih_scratch1, hdlp->ih_pri, hdlp->ih_type);
549*0Sstevel@tonic-gate 		break;
550*0Sstevel@tonic-gate 	case PSM_INTR_OP_FREE_VECTORS:
551*0Sstevel@tonic-gate 		apic_free_vectors(dip, hdlp->ih_inum, hdlp->ih_scratch1,
552*0Sstevel@tonic-gate 		    hdlp->ih_pri, hdlp->ih_type);
553*0Sstevel@tonic-gate 		break;
554*0Sstevel@tonic-gate 	case PSM_INTR_OP_NAVAIL_VECTORS:
555*0Sstevel@tonic-gate 		*result = apic_navail_vector(dip, hdlp->ih_pri);
556*0Sstevel@tonic-gate 		break;
557*0Sstevel@tonic-gate 	case PSM_INTR_OP_XLATE_VECTOR:
558*0Sstevel@tonic-gate 		ispec = (struct intrspec *)hdlp->ih_private;
559*0Sstevel@tonic-gate 		*result = apic_introp_xlate(dip, ispec, hdlp->ih_type);
560*0Sstevel@tonic-gate 		break;
561*0Sstevel@tonic-gate 	case PSM_INTR_OP_GET_PENDING:
562*0Sstevel@tonic-gate 		if ((irqp = apic_find_irq(dip, ispec, hdlp->ih_type)) == NULL)
563*0Sstevel@tonic-gate 			return (PSM_FAILURE);
564*0Sstevel@tonic-gate 		*result = apic_get_pending(irqp, hdlp->ih_type);
565*0Sstevel@tonic-gate 		break;
566*0Sstevel@tonic-gate 	case PSM_INTR_OP_CLEAR_MASK:
567*0Sstevel@tonic-gate 		if (hdlp->ih_type != DDI_INTR_TYPE_FIXED)
568*0Sstevel@tonic-gate 			return (PSM_FAILURE);
569*0Sstevel@tonic-gate 		irqp = apic_find_irq(dip, ispec, hdlp->ih_type);
570*0Sstevel@tonic-gate 		if (irqp == NULL)
571*0Sstevel@tonic-gate 			return (PSM_FAILURE);
572*0Sstevel@tonic-gate 		apic_clear_mask(irqp);
573*0Sstevel@tonic-gate 		break;
574*0Sstevel@tonic-gate 	case PSM_INTR_OP_SET_MASK:
575*0Sstevel@tonic-gate 		if (hdlp->ih_type != DDI_INTR_TYPE_FIXED)
576*0Sstevel@tonic-gate 			return (PSM_FAILURE);
577*0Sstevel@tonic-gate 		if ((irqp = apic_find_irq(dip, ispec, hdlp->ih_type)) == NULL)
578*0Sstevel@tonic-gate 			return (PSM_FAILURE);
579*0Sstevel@tonic-gate 		apic_set_mask(irqp);
580*0Sstevel@tonic-gate 		break;
581*0Sstevel@tonic-gate 	case PSM_INTR_OP_GET_CAP:
582*0Sstevel@tonic-gate 		cap = DDI_INTR_FLAG_PENDING;
583*0Sstevel@tonic-gate 		if (hdlp->ih_type == DDI_INTR_TYPE_FIXED)
584*0Sstevel@tonic-gate 			cap |= DDI_INTR_FLAG_MASKABLE;
585*0Sstevel@tonic-gate 		*result = cap;
586*0Sstevel@tonic-gate 		break;
587*0Sstevel@tonic-gate 	case PSM_INTR_OP_GET_SHARED:
588*0Sstevel@tonic-gate 		if (hdlp->ih_type != DDI_INTR_TYPE_FIXED)
589*0Sstevel@tonic-gate 			return (PSM_FAILURE);
590*0Sstevel@tonic-gate 		if ((irqp = apic_find_irq(dip, ispec, hdlp->ih_type)) == NULL)
591*0Sstevel@tonic-gate 			return (PSM_FAILURE);
592*0Sstevel@tonic-gate 		*result = irqp->airq_share ? 1: 0;
593*0Sstevel@tonic-gate 		break;
594*0Sstevel@tonic-gate 	case PSM_INTR_OP_SET_PRI:
595*0Sstevel@tonic-gate 		old_priority = hdlp->ih_pri;	/* save old value */
596*0Sstevel@tonic-gate 		new_priority = *(int *)result;	/* try the new value */
597*0Sstevel@tonic-gate 
598*0Sstevel@tonic-gate 		/* First, check if "hdlp->ih_scratch1" vectors exist? */
599*0Sstevel@tonic-gate 		if (apic_navail_vector(dip, new_priority) < hdlp->ih_scratch1)
600*0Sstevel@tonic-gate 			return (PSM_FAILURE);
601*0Sstevel@tonic-gate 
602*0Sstevel@tonic-gate 		/* Now allocate the vectors */
603*0Sstevel@tonic-gate 		count_vec = apic_alloc_vectors(dip, hdlp->ih_inum,
604*0Sstevel@tonic-gate 		    hdlp->ih_scratch1, new_priority, hdlp->ih_type);
605*0Sstevel@tonic-gate 
606*0Sstevel@tonic-gate 		/* Did we get fewer vectors? */
607*0Sstevel@tonic-gate 		if (count_vec != hdlp->ih_scratch1) {
608*0Sstevel@tonic-gate 			apic_free_vectors(dip, hdlp->ih_inum, count_vec,
609*0Sstevel@tonic-gate 			    new_priority, hdlp->ih_type);
610*0Sstevel@tonic-gate 			return (PSM_FAILURE);
611*0Sstevel@tonic-gate 		}
612*0Sstevel@tonic-gate 
613*0Sstevel@tonic-gate 		/* Finally, free the previously allocated vectors */
614*0Sstevel@tonic-gate 		apic_free_vectors(dip, hdlp->ih_inum, count_vec,
615*0Sstevel@tonic-gate 		    old_priority, hdlp->ih_type);
616*0Sstevel@tonic-gate 		hdlp->ih_pri = new_priority; /* set the new value */
617*0Sstevel@tonic-gate 		break;
618*0Sstevel@tonic-gate 	case PSM_INTR_OP_SET_CAP:
619*0Sstevel@tonic-gate 	default:
620*0Sstevel@tonic-gate 		return (PSM_FAILURE);
621*0Sstevel@tonic-gate 	}
622*0Sstevel@tonic-gate 	return (PSM_SUCCESS);
623*0Sstevel@tonic-gate }
624