xref: /illumos-gate/usr/src/uts/i86pc/io/pcplusmp/apic_introp.c (revision 150d2c5288c645a1c1a7d2bee61199a3729406c7)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 /*
29  * apic_introp.c:
30  *	Has code for Advanced DDI interrupt framework support.
31  */
32 
33 #include <sys/cpuvar.h>
34 #include <sys/psm.h>
35 #include <sys/archsystm.h>
36 #include <sys/apic.h>
37 #include <sys/sunddi.h>
38 #include <sys/ddi_impldefs.h>
39 #include <sys/mach_intr.h>
40 #include <sys/sysmacros.h>
41 #include <sys/trap.h>
42 #include <sys/pci.h>
43 #include <sys/pci_intr_lib.h>
44 
45 extern struct av_head autovect[];
46 
47 /*
48  *	Local Function Prototypes
49  */
50 int		apic_pci_msi_enable_vector(dev_info_t *, int, int,
51 		    int, int, int);
52 apic_irq_t	*apic_find_irq(dev_info_t *, struct intrspec *, int);
53 static int	apic_get_pending(apic_irq_t *, int);
54 static void	apic_clear_mask(apic_irq_t *);
55 static void	apic_set_mask(apic_irq_t *);
56 
57 /*
58  * MSI support flag:
59  * reflects whether MSI is supported at APIC level
60  * it can also be patched through /etc/system
61  *
62  *  0 = default value - don't know and need to call apic_check_msi_support()
63  *      to find out then set it accordingly
64  *  1 = supported
65  * -1 = not supported
66  */
67 int	apic_support_msi = 0;
68 
69 /* Multiple vector support for MSI */
70 int	apic_multi_msi_enable = 1;
71 int	apic_multi_msi_max = 2;
72 
73 /*
74  * apic_pci_msi_enable_vector:
75  *	Set the address/data fields in the MSI/X capability structure
76  *	XXX: MSI-X support
77  */
78 /* ARGSUSED */
79 int
80 apic_pci_msi_enable_vector(dev_info_t *dip, int type, int inum, int vector,
81     int count, int target_apic_id)
82 {
83 	uint64_t		msi_addr, msi_data;
84 	ushort_t		msi_ctrl;
85 	int			cap_ptr = i_ddi_get_msi_msix_cap_ptr(dip);
86 	ddi_acc_handle_t	handle = i_ddi_get_pci_config_handle(dip);
87 
88 	DDI_INTR_IMPLDBG((CE_CONT, "apic_pci_msi_enable_vector: dip=0x%p\n"
89 	    "\tdriver = %s, inum=0x%x vector=0x%x apicid=0x%x\n", (void *)dip,
90 	    ddi_driver_name(dip), inum, vector, target_apic_id));
91 
92 	if (handle == NULL)
93 		return (PSM_FAILURE);
94 
95 	/* MSI Address */
96 	msi_addr = (MSI_ADDR_HDR | (target_apic_id << MSI_ADDR_DEST_SHIFT));
97 	msi_addr |= ((MSI_ADDR_RH_FIXED << MSI_ADDR_RH_SHIFT) |
98 		    (MSI_ADDR_DM_PHYSICAL << MSI_ADDR_DM_SHIFT));
99 
100 	/* MSI Data: MSI is edge triggered according to spec */
101 	msi_data = ((MSI_DATA_TM_EDGE << MSI_DATA_TM_SHIFT) | vector);
102 
103 	DDI_INTR_IMPLDBG((CE_CONT, "apic_pci_msi_enable_vector: addr=0x%lx "
104 	    "data=0x%lx\n", (long)msi_addr, (long)msi_data));
105 
106 	if (type == DDI_INTR_TYPE_MSI) {
107 		msi_ctrl = pci_config_get16(handle, cap_ptr + PCI_MSI_CTRL);
108 
109 		/* Set the bits to inform how many MSIs are enabled */
110 		msi_ctrl |= ((highbit(count) -1) << PCI_MSI_MME_SHIFT);
111 		pci_config_put16(handle, cap_ptr + PCI_MSI_CTRL, msi_ctrl);
112 
113 		pci_config_put32(handle,
114 		    cap_ptr + PCI_MSI_ADDR_OFFSET, msi_addr);
115 
116 		if (msi_ctrl &  PCI_MSI_64BIT_MASK) {
117 			pci_config_put32(handle,
118 			    cap_ptr + PCI_MSI_ADDR_OFFSET + 4, msi_addr >> 32);
119 			pci_config_put16(handle,
120 			    cap_ptr + PCI_MSI_64BIT_DATA, msi_data);
121 		} else {
122 			pci_config_put16(handle,
123 			    cap_ptr + PCI_MSI_32BIT_DATA, msi_data);
124 		}
125 
126 	} else if (type == DDI_INTR_TYPE_MSIX) {
127 		uintptr_t	off;
128 		ddi_intr_msix_t	*msix_p = i_ddi_get_msix(dip);
129 
130 		/* Offset into the "inum"th entry in the MSI-X table */
131 		off = (uintptr_t)msix_p->msix_tbl_addr +
132 		    (inum  * PCI_MSIX_VECTOR_SIZE);
133 
134 		ddi_put32(msix_p->msix_tbl_hdl,
135 		    (uint32_t *)(off + PCI_MSIX_DATA_OFFSET), msi_data);
136 		ddi_put64(msix_p->msix_tbl_hdl,
137 		    (uint64_t *)(off + PCI_MSIX_LOWER_ADDR_OFFSET), msi_addr);
138 	}
139 
140 	return (PSM_SUCCESS);
141 }
142 
143 
144 /*
145  * This function returns the no. of vectors available for the pri.
146  * dip is not used at this moment.  If we really don't need that,
147  * it will be removed.
148  */
149 /*ARGSUSED*/
150 int
151 apic_navail_vector(dev_info_t *dip, int pri)
152 {
153 	int	lowest, highest, i, navail, count;
154 
155 	DDI_INTR_IMPLDBG((CE_CONT, "apic_navail_vector: dip: %p, pri: %x\n",
156 	    (void *)dip, pri));
157 
158 	highest = apic_ipltopri[pri] + APIC_VECTOR_MASK;
159 	lowest = apic_ipltopri[pri - 1] + APIC_VECTOR_PER_IPL;
160 	navail = count = 0;
161 
162 	/* It has to be contiguous */
163 	for (i = lowest; i < highest; i++) {
164 		count = 0;
165 		while ((apic_vector_to_irq[i] == APIC_RESV_IRQ) &&
166 			(i < highest)) {
167 			if (APIC_CHECK_RESERVE_VECTORS(i))
168 				break;
169 			count++;
170 			i++;
171 		}
172 		if (count > navail)
173 			navail = count;
174 	}
175 	return (navail);
176 }
177 
178 /*
179  * Finds "count" contiguous MSI vectors starting at the proper alignment
180  * at "pri".
181  * Caller needs to make sure that count has to be power of 2 and should not
182  * be < 1.
183  */
184 uchar_t
185 apic_find_multi_vectors(int pri, int count)
186 {
187 	int	lowest, highest, i, navail, start, msibits;
188 
189 	DDI_INTR_IMPLDBG((CE_CONT, "apic_find_mult: pri: %x, count: %x\n",
190 	    pri, count));
191 
192 	highest = apic_ipltopri[pri] + APIC_VECTOR_MASK;
193 	lowest = apic_ipltopri[pri - 1] + APIC_VECTOR_PER_IPL;
194 	navail = 0;
195 
196 	/*
197 	 * msibits is the no. of lower order message data bits for the
198 	 * allocated MSI vectors and is used to calculate the aligned
199 	 * starting vector
200 	 */
201 	msibits = count - 1;
202 
203 	/* It has to be contiguous */
204 	for (i = lowest; i < highest; i++) {
205 		navail = 0;
206 
207 		/*
208 		 * starting vector has to be aligned accordingly for
209 		 * multiple MSIs
210 		 */
211 		if (msibits)
212 			i = (i + msibits) & ~msibits;
213 		start = i;
214 		while ((apic_vector_to_irq[i] == APIC_RESV_IRQ) &&
215 			(i < highest)) {
216 			if (APIC_CHECK_RESERVE_VECTORS(i))
217 				break;
218 			navail++;
219 			if (navail >= count)
220 				return (start);
221 			i++;
222 		}
223 	}
224 	return (0);
225 }
226 
227 
228 /*
229  * It finds the apic_irq_t associates with the dip, ispec and type.
230  */
231 apic_irq_t *
232 apic_find_irq(dev_info_t *dip, struct intrspec *ispec, int type)
233 {
234 	apic_irq_t	*irqp;
235 	int i;
236 
237 	DDI_INTR_IMPLDBG((CE_CONT, "apic_find_irq: dip=0x%p vec=0x%x "
238 	    "ipl=0x%x type=0x%x\n", (void *)dip, ispec->intrspec_vec,
239 	    ispec->intrspec_pri, type));
240 
241 	for (i = apic_min_device_irq; i <= apic_max_device_irq; i++) {
242 		if ((irqp = apic_irq_table[i]) == NULL)
243 			continue;
244 		if ((irqp->airq_dip == dip) &&
245 		    (irqp->airq_origirq == ispec->intrspec_vec) &&
246 		    (irqp->airq_ipl == ispec->intrspec_pri)) {
247 			if (DDI_INTR_IS_MSI_OR_MSIX(type)) {
248 				if (APIC_IS_MSI_OR_MSIX_INDEX(irqp->
249 				    airq_mps_intr_index))
250 					return (irqp);
251 			} else
252 				return (irqp);
253 		}
254 	}
255 	DDI_INTR_IMPLDBG((CE_CONT, "apic_find_irq: return NULL\n"));
256 	return (NULL);
257 }
258 
259 
260 /*
261  * This function will return the pending bit of the irqp.
262  * It either comes from the IRR register of the APIC or the RDT
263  * entry of the I/O APIC.
264  * For the IRR to work, it needs to be to its binding CPU
265  */
266 static int
267 apic_get_pending(apic_irq_t *irqp, int type)
268 {
269 	int			bit, index, irr, pending;
270 	int			intin_no;
271 	int			apic_ix;
272 
273 	DDI_INTR_IMPLDBG((CE_CONT, "apic_get_pending: irqp: %p, cpuid: %x "
274 	    "type: %x\n", (void *)irqp, irqp->airq_cpu & ~IRQ_USER_BOUND,
275 	    type));
276 
277 	/* need to get on the bound cpu */
278 	mutex_enter(&cpu_lock);
279 	affinity_set(irqp->airq_cpu & ~IRQ_USER_BOUND);
280 
281 	index = irqp->airq_vector / 32;
282 	bit = irqp->airq_vector % 32;
283 	irr = apicadr[APIC_IRR_REG + index];
284 
285 	affinity_clear();
286 	mutex_exit(&cpu_lock);
287 
288 	pending = (irr & (1 << bit)) ? 1 : 0;
289 	if (!pending && (type == DDI_INTR_TYPE_FIXED)) {
290 		/* check I/O APIC for fixed interrupt */
291 		intin_no = irqp->airq_intin_no;
292 		apic_ix = irqp->airq_ioapicindex;
293 		pending = (READ_IOAPIC_RDT_ENTRY_LOW_DWORD(apic_ix, intin_no) &
294 		    AV_PENDING) ? 1 : 0;
295 	}
296 	return (pending);
297 }
298 
299 
300 /*
301  * This function will clear the mask for the interrupt on the I/O APIC
302  */
303 static void
304 apic_clear_mask(apic_irq_t *irqp)
305 {
306 	int			intin_no;
307 	ulong_t			iflag;
308 	int32_t			rdt_entry;
309 	int 			apic_ix;
310 
311 	DDI_INTR_IMPLDBG((CE_CONT, "apic_clear_mask: irqp: %p\n",
312 	    (void *)irqp));
313 
314 	intin_no = irqp->airq_intin_no;
315 	apic_ix = irqp->airq_ioapicindex;
316 
317 	iflag = intr_clear();
318 	lock_set(&apic_ioapic_lock);
319 
320 	rdt_entry = READ_IOAPIC_RDT_ENTRY_LOW_DWORD(apic_ix, intin_no);
321 
322 	/* clear mask */
323 	WRITE_IOAPIC_RDT_ENTRY_LOW_DWORD(apic_ix, intin_no,
324 	    ((~AV_MASK) & rdt_entry));
325 
326 	lock_clear(&apic_ioapic_lock);
327 	intr_restore(iflag);
328 }
329 
330 
331 /*
332  * This function will mask the interrupt on the I/O APIC
333  */
334 static void
335 apic_set_mask(apic_irq_t *irqp)
336 {
337 	int			intin_no;
338 	int 			apic_ix;
339 	ulong_t			iflag;
340 	int32_t			rdt_entry;
341 
342 	DDI_INTR_IMPLDBG((CE_CONT, "apic_set_mask: irqp: %p\n", (void *)irqp));
343 
344 	intin_no = irqp->airq_intin_no;
345 	apic_ix = irqp->airq_ioapicindex;
346 
347 	iflag = intr_clear();
348 
349 	lock_set(&apic_ioapic_lock);
350 
351 	rdt_entry = READ_IOAPIC_RDT_ENTRY_LOW_DWORD(apic_ix, intin_no);
352 
353 	/* mask it */
354 	WRITE_IOAPIC_RDT_ENTRY_LOW_DWORD(apic_ix, intin_no,
355 	    (AV_MASK | rdt_entry));
356 
357 	lock_clear(&apic_ioapic_lock);
358 	intr_restore(iflag);
359 }
360 
361 
362 void
363 apic_free_vectors(dev_info_t *dip, int inum, int count, int pri, int type)
364 {
365 	int i;
366 	apic_irq_t *irqptr;
367 	struct intrspec ispec;
368 
369 	DDI_INTR_IMPLDBG((CE_CONT, "apic_free_vectors: dip: %p inum: %x "
370 	    "count: %x pri: %x type: %x\n",
371 	    (void *)dip, inum, count, pri, type));
372 
373 	/* for MSI/X only */
374 	if (!DDI_INTR_IS_MSI_OR_MSIX(type))
375 		return;
376 
377 	for (i = 0; i < count; i++) {
378 		DDI_INTR_IMPLDBG((CE_CONT, "apic_free_vectors: inum=0x%x "
379 		    "pri=0x%x count=0x%x\n", inum, pri, count));
380 		ispec.intrspec_vec = inum + i;
381 		ispec.intrspec_pri = pri;
382 		if ((irqptr = apic_find_irq(dip, &ispec, type)) == NULL) {
383 			DDI_INTR_IMPLDBG((CE_CONT, "apic_free_vectors: "
384 			    "dip=0x%p inum=0x%x pri=0x%x apic_find_irq() "
385 			    "failed\n", (void *)dip, inum, pri));
386 			continue;
387 		}
388 		irqptr->airq_mps_intr_index = FREE_INDEX;
389 		apic_vector_to_irq[irqptr->airq_vector] = APIC_RESV_IRQ;
390 	}
391 }
392 
393 
394 /*
395  * check whether the system supports MSI
396  *
397  * If PCI-E capability is found, then this must be a PCI-E system.
398  * Since MSI is required for PCI-E system, it returns PSM_SUCCESS
399  * to indicate this system supports MSI.
400  */
401 int
402 apic_check_msi_support()
403 {
404 	dev_info_t *cdip;
405 	char dev_type[16];
406 	int dev_len;
407 
408 	DDI_INTR_IMPLDBG((CE_CONT, "apic_check_msi_support:\n"));
409 
410 	/*
411 	 * check whether the first level children of root_node have
412 	 * PCI-E capability
413 	 */
414 	for (cdip = ddi_get_child(ddi_root_node()); cdip != NULL;
415 	    cdip = ddi_get_next_sibling(cdip)) {
416 
417 		DDI_INTR_IMPLDBG((CE_CONT, "apic_check_msi_support: cdip: 0x%p,"
418 		    " driver: %s, binding: %s, nodename: %s\n", (void *)cdip,
419 		    ddi_driver_name(cdip), ddi_binding_name(cdip),
420 		    ddi_node_name(cdip)));
421 		dev_len = sizeof (dev_type);
422 		if (ddi_getlongprop_buf(DDI_DEV_T_ANY, cdip, DDI_PROP_DONTPASS,
423 		    "device_type", (caddr_t)dev_type, &dev_len)
424 		    != DDI_PROP_SUCCESS)
425 			continue;
426 		if (strcmp(dev_type, "pciex") == 0)
427 			return (PSM_SUCCESS);
428 	}
429 
430 	/* MSI is not supported on this system */
431 	DDI_INTR_IMPLDBG((CE_CONT, "apic_check_msi_support: no 'pciex' "
432 	    "device_type found\n"));
433 	return (PSM_FAILURE);
434 }
435 
436 int
437 apic_get_vector_intr_info(int vecirq, apic_get_intr_t *intr_params_p)
438 {
439 	struct autovec *av_dev;
440 	uchar_t irqno;
441 	int i;
442 	apic_irq_t *irq_p;
443 
444 	/* Sanity check the vector/irq argument. */
445 	ASSERT((vecirq >= 0) || (vecirq <= APIC_MAX_VECTOR));
446 
447 	mutex_enter(&airq_mutex);
448 
449 	/*
450 	 * Convert the vecirq arg to an irq using vector_to_irq table
451 	 * if the arg is a vector.  Pass thru if already an irq.
452 	 */
453 	if ((intr_params_p->avgi_req_flags & PSMGI_INTRBY_FLAGS) ==
454 	    PSMGI_INTRBY_VEC)
455 		irqno = apic_vector_to_irq[vecirq];
456 	else
457 		irqno = vecirq;
458 
459 	irq_p = apic_irq_table[irqno];
460 
461 	if ((irq_p == NULL) ||
462 	    (irq_p->airq_temp_cpu == IRQ_UNBOUND) ||
463 	    (irq_p->airq_temp_cpu == IRQ_UNINIT)) {
464 		mutex_exit(&airq_mutex);
465 		return (PSM_FAILURE);
466 	}
467 
468 	if (intr_params_p->avgi_req_flags & PSMGI_REQ_CPUID) {
469 
470 		/* Get the (temp) cpu from apic_irq table, indexed by irq. */
471 		intr_params_p->avgi_cpu_id = irq_p->airq_temp_cpu;
472 
473 		/* Return user bound info for intrd. */
474 		if (intr_params_p->avgi_cpu_id & IRQ_USER_BOUND) {
475 			intr_params_p->avgi_cpu_id &= ~IRQ_USER_BOUND;
476 			intr_params_p->avgi_cpu_id |= PSMGI_CPU_USER_BOUND;
477 		}
478 	}
479 
480 	if (intr_params_p->avgi_req_flags & PSMGI_REQ_VECTOR) {
481 		intr_params_p->avgi_vector = irq_p->airq_vector;
482 	}
483 
484 	if (intr_params_p->avgi_req_flags &
485 	    (PSMGI_REQ_NUM_DEVS | PSMGI_REQ_GET_DEVS)) {
486 		/* Get number of devices from apic_irq table shared field. */
487 		intr_params_p->avgi_num_devs = irq_p->airq_share;
488 	}
489 
490 	if (intr_params_p->avgi_req_flags &  PSMGI_REQ_GET_DEVS) {
491 
492 		intr_params_p->avgi_req_flags  |= PSMGI_REQ_NUM_DEVS;
493 
494 		/* Some devices have NULL dip.  Don't count these. */
495 		if (intr_params_p->avgi_num_devs > 0) {
496 			for (i = 0, av_dev = autovect[irqno].avh_link;
497 			    av_dev; av_dev = av_dev->av_link)
498 				if (av_dev->av_vector && av_dev->av_dip)
499 					i++;
500 			intr_params_p->avgi_num_devs =
501 			    MIN(intr_params_p->avgi_num_devs, i);
502 		}
503 
504 		/* There are no viable dips to return. */
505 		if (intr_params_p->avgi_num_devs == 0)
506 			intr_params_p->avgi_dip_list = NULL;
507 
508 		else {	/* Return list of dips */
509 
510 			/* Allocate space in array for that number of devs. */
511 			intr_params_p->avgi_dip_list = kmem_zalloc(
512 			    intr_params_p->avgi_num_devs *
513 			    sizeof (dev_info_t *),
514 			    KM_SLEEP);
515 
516 			/*
517 			 * Loop through the device list of the autovec table
518 			 * filling in the dip array.
519 			 *
520 			 * Note that the autovect table may have some special
521 			 * entries which contain NULL dips.  These will be
522 			 * ignored.
523 			 */
524 			for (i = 0, av_dev = autovect[irqno].avh_link;
525 			    av_dev; av_dev = av_dev->av_link)
526 				if (av_dev->av_vector && av_dev->av_dip)
527 					intr_params_p->avgi_dip_list[i++] =
528 					    av_dev->av_dip;
529 		}
530 	}
531 
532 	mutex_exit(&airq_mutex);
533 
534 	return (PSM_SUCCESS);
535 }
536 
537 /*
538  * apic_pci_msi_unconfigure:
539  *
540  * This and next two interfaces are copied from pci_intr_lib.c
541  * Do ensure that these two files stay in sync.
542  * These needed to be copied over here to avoid a deadlock situation on
543  * certain mp systems that use MSI interrupts.
544  *
545  * IMPORTANT regards next three interfaces:
546  * i) are called only for MSI/X interrupts.
547  * ii) called with interrupts disabled, and must not block
548  */
549 int
550 apic_pci_msi_unconfigure(dev_info_t *rdip, int type, int inum)
551 {
552 	ushort_t		msi_ctrl;
553 	int			cap_ptr = i_ddi_get_msi_msix_cap_ptr(rdip);
554 	ddi_acc_handle_t	handle = i_ddi_get_pci_config_handle(rdip);
555 
556 	if (handle == NULL)
557 		return (PSM_FAILURE);
558 
559 	if (type == DDI_INTR_TYPE_MSI) {
560 		msi_ctrl = pci_config_get16(handle, cap_ptr + PCI_MSI_CTRL);
561 		msi_ctrl &= (~PCI_MSI_MME_MASK);
562 		pci_config_put16(handle, cap_ptr + PCI_MSI_CTRL, msi_ctrl);
563 		pci_config_put32(handle, cap_ptr + PCI_MSI_ADDR_OFFSET, 0);
564 
565 		if (msi_ctrl &  PCI_MSI_64BIT_MASK) {
566 			pci_config_put16(handle,
567 			    cap_ptr + PCI_MSI_64BIT_DATA, 0);
568 			pci_config_put32(handle,
569 			    cap_ptr + PCI_MSI_ADDR_OFFSET + 4, 0);
570 		} else {
571 			pci_config_put16(handle,
572 			    cap_ptr + PCI_MSI_32BIT_DATA, 0);
573 		}
574 
575 	} else if (type == DDI_INTR_TYPE_MSIX) {
576 		uintptr_t	off;
577 		ddi_intr_msix_t	*msix_p = i_ddi_get_msix(rdip);
578 
579 		/* Offset into the "inum"th entry in the MSI-X table */
580 		off = (uintptr_t)msix_p->msix_tbl_addr +
581 		    (inum * PCI_MSIX_VECTOR_SIZE);
582 
583 		/* Reset the "data" and "addr" bits */
584 		ddi_put32(msix_p->msix_tbl_hdl,
585 		    (uint32_t *)(off + PCI_MSIX_DATA_OFFSET), 0);
586 		ddi_put64(msix_p->msix_tbl_hdl, (uint64_t *)off, 0);
587 	}
588 
589 	return (PSM_SUCCESS);
590 }
591 
592 
593 /*
594  * apic_pci_msi_enable_mode:
595  */
596 int
597 apic_pci_msi_enable_mode(dev_info_t *rdip, int type, int inum)
598 {
599 	ushort_t		msi_ctrl;
600 	int			cap_ptr = i_ddi_get_msi_msix_cap_ptr(rdip);
601 	ddi_acc_handle_t	handle = i_ddi_get_pci_config_handle(rdip);
602 
603 	if (handle == NULL)
604 		return (PSM_FAILURE);
605 
606 	if (type == DDI_INTR_TYPE_MSI) {
607 		msi_ctrl = pci_config_get16(handle, cap_ptr + PCI_MSI_CTRL);
608 		if ((msi_ctrl & PCI_MSI_ENABLE_BIT))
609 			return (PSM_SUCCESS);
610 
611 		msi_ctrl |= PCI_MSI_ENABLE_BIT;
612 		pci_config_put16(handle, cap_ptr + PCI_MSI_CTRL, msi_ctrl);
613 
614 	} else if (type == DDI_INTR_TYPE_MSIX) {
615 		uintptr_t	off;
616 		ddi_intr_msix_t	*msix_p;
617 
618 		msi_ctrl = pci_config_get16(handle, cap_ptr + PCI_MSIX_CTRL);
619 
620 		if (msi_ctrl & PCI_MSIX_ENABLE_BIT)
621 			return (PSM_SUCCESS);
622 
623 		msi_ctrl |= PCI_MSIX_ENABLE_BIT;
624 		pci_config_put16(handle, cap_ptr + PCI_MSIX_CTRL, msi_ctrl);
625 
626 		msix_p = i_ddi_get_msix(rdip);
627 
628 		/* Offset into "inum"th entry in the MSI-X table & clear mask */
629 		off = (uintptr_t)msix_p->msix_tbl_addr + (inum *
630 		    PCI_MSIX_VECTOR_SIZE) + PCI_MSIX_VECTOR_CTRL_OFFSET;
631 		ddi_put32(msix_p->msix_tbl_hdl, (uint32_t *)off, 0);
632 	}
633 
634 	return (PSM_SUCCESS);
635 }
636 
637 /*
638  * apic_pci_msi_disable_mode:
639  */
640 int
641 apic_pci_msi_disable_mode(dev_info_t *rdip, int type, int inum)
642 {
643 	ushort_t		msi_ctrl;
644 	int			cap_ptr = i_ddi_get_msi_msix_cap_ptr(rdip);
645 	ddi_acc_handle_t	handle = i_ddi_get_pci_config_handle(rdip);
646 
647 	if (handle == NULL)
648 		return (PSM_FAILURE);
649 
650 	if (type == DDI_INTR_TYPE_MSI) {
651 		msi_ctrl = pci_config_get16(handle, cap_ptr + PCI_MSI_CTRL);
652 		if (!(msi_ctrl & PCI_MSI_ENABLE_BIT))
653 			return (PSM_SUCCESS);
654 
655 		msi_ctrl &= ~PCI_MSI_ENABLE_BIT;	/* MSI disable */
656 		pci_config_put16(handle, cap_ptr + PCI_MSI_CTRL, msi_ctrl);
657 
658 	} else if (type == DDI_INTR_TYPE_MSIX) {
659 		uintptr_t	off;
660 		ddi_intr_msix_t	*msix_p;
661 
662 		msi_ctrl = pci_config_get16(handle, cap_ptr + PCI_MSIX_CTRL);
663 
664 		if (!(msi_ctrl & PCI_MSIX_ENABLE_BIT))
665 			return (PSM_SUCCESS);
666 
667 		msix_p = i_ddi_get_msix(rdip);
668 
669 		/* Offset into "inum"th entry in the MSI-X table & mask it */
670 		off = (uintptr_t)msix_p->msix_tbl_addr + (inum *
671 		    PCI_MSIX_VECTOR_SIZE) + PCI_MSIX_VECTOR_CTRL_OFFSET;
672 		ddi_put32(msix_p->msix_tbl_hdl, (uint32_t *)off, 0x1);
673 	}
674 
675 	return (PSM_SUCCESS);
676 }
677 
678 
679 /*
680  * This function provides external interface to the nexus for all
681  * functionalities related to the new DDI interrupt framework.
682  *
683  * Input:
684  * dip     - pointer to the dev_info structure of the requested device
685  * hdlp    - pointer to the internal interrupt handle structure for the
686  *	     requested interrupt
687  * intr_op - opcode for this call
688  * result  - pointer to the integer that will hold the result to be
689  *	     passed back if return value is PSM_SUCCESS
690  *
691  * Output:
692  * return value is either PSM_SUCCESS or PSM_FAILURE
693  */
694 int
695 apic_intr_ops(dev_info_t *dip, ddi_intr_handle_impl_t *hdlp,
696     psm_intr_op_t intr_op, int *result)
697 {
698 	int		cap, ret;
699 	int		count_vec;
700 	int		cpu;
701 	int		old_priority;
702 	int		new_priority;
703 	int		iflag;
704 	apic_irq_t	*irqp;
705 	struct intrspec *ispec, intr_spec;
706 
707 	DDI_INTR_IMPLDBG((CE_CONT, "apic_intr_ops: dip: %p hdlp: %p "
708 	    "intr_op: %x\n", (void *)dip, (void *)hdlp, intr_op));
709 
710 	ispec = &intr_spec;
711 	ispec->intrspec_pri = hdlp->ih_pri;
712 	ispec->intrspec_vec = hdlp->ih_inum;
713 	ispec->intrspec_func = hdlp->ih_cb_func;
714 
715 	switch (intr_op) {
716 	case PSM_INTR_OP_CHECK_MSI:
717 		/*
718 		 * Check MSI/X is supported or not at APIC level and
719 		 * masked off the MSI/X bits in hdlp->ih_type if not
720 		 * supported before return.  If MSI/X is supported,
721 		 * leave the ih_type unchanged and return.
722 		 *
723 		 * hdlp->ih_type passed in from the nexus has all the
724 		 * interrupt types supported by the device.
725 		 */
726 		if (apic_support_msi == 0) {
727 			/*
728 			 * if apic_support_msi is not set, call
729 			 * apic_check_msi_support() to check whether msi
730 			 * is supported first
731 			 */
732 			if (apic_check_msi_support() == PSM_SUCCESS)
733 				apic_support_msi = 1;
734 			else
735 				apic_support_msi = -1;
736 		}
737 		if (apic_support_msi == 1)
738 			*result = hdlp->ih_type;
739 		else
740 			*result = hdlp->ih_type & ~(DDI_INTR_TYPE_MSI |
741 			    DDI_INTR_TYPE_MSIX);
742 		break;
743 	case PSM_INTR_OP_ALLOC_VECTORS:
744 		*result = apic_alloc_vectors(dip, hdlp->ih_inum,
745 		    hdlp->ih_scratch1, hdlp->ih_pri, hdlp->ih_type,
746 		    (int)(uintptr_t)hdlp->ih_scratch2);
747 		break;
748 	case PSM_INTR_OP_FREE_VECTORS:
749 		apic_free_vectors(dip, hdlp->ih_inum, hdlp->ih_scratch1,
750 		    hdlp->ih_pri, hdlp->ih_type);
751 		break;
752 	case PSM_INTR_OP_NAVAIL_VECTORS:
753 		*result = apic_navail_vector(dip, hdlp->ih_pri);
754 		break;
755 	case PSM_INTR_OP_XLATE_VECTOR:
756 		ispec = ((ihdl_plat_t *)hdlp->ih_private)->ip_ispecp;
757 		*result = apic_introp_xlate(dip, ispec, hdlp->ih_type);
758 		break;
759 	case PSM_INTR_OP_GET_PENDING:
760 		if ((irqp = apic_find_irq(dip, ispec, hdlp->ih_type)) == NULL)
761 			return (PSM_FAILURE);
762 		*result = apic_get_pending(irqp, hdlp->ih_type);
763 		break;
764 	case PSM_INTR_OP_CLEAR_MASK:
765 		if (hdlp->ih_type != DDI_INTR_TYPE_FIXED)
766 			return (PSM_FAILURE);
767 		irqp = apic_find_irq(dip, ispec, hdlp->ih_type);
768 		if (irqp == NULL)
769 			return (PSM_FAILURE);
770 		apic_clear_mask(irqp);
771 		break;
772 	case PSM_INTR_OP_SET_MASK:
773 		if (hdlp->ih_type != DDI_INTR_TYPE_FIXED)
774 			return (PSM_FAILURE);
775 		if ((irqp = apic_find_irq(dip, ispec, hdlp->ih_type)) == NULL)
776 			return (PSM_FAILURE);
777 		apic_set_mask(irqp);
778 		break;
779 	case PSM_INTR_OP_GET_CAP:
780 		cap = DDI_INTR_FLAG_PENDING;
781 		if (hdlp->ih_type == DDI_INTR_TYPE_FIXED)
782 			cap |= DDI_INTR_FLAG_MASKABLE;
783 		*result = cap;
784 		break;
785 	case PSM_INTR_OP_GET_SHARED:
786 		if (hdlp->ih_type != DDI_INTR_TYPE_FIXED)
787 			return (PSM_FAILURE);
788 		if ((irqp = apic_find_irq(dip, ispec, hdlp->ih_type)) == NULL)
789 			return (PSM_FAILURE);
790 		*result = irqp->airq_share ? 1: 0;
791 		break;
792 	case PSM_INTR_OP_SET_PRI:
793 		old_priority = hdlp->ih_pri;	/* save old value */
794 		new_priority = *(int *)result;	/* try the new value */
795 
796 		/* First, check if "hdlp->ih_scratch1" vectors exist? */
797 		if (apic_navail_vector(dip, new_priority) < hdlp->ih_scratch1)
798 			return (PSM_FAILURE);
799 
800 		/* Now allocate the vectors */
801 		count_vec = apic_alloc_vectors(dip, hdlp->ih_inum,
802 		    hdlp->ih_scratch1, new_priority, hdlp->ih_type,
803 		    DDI_INTR_ALLOC_STRICT);
804 
805 		/* Did we get new vectors? */
806 		if (!count_vec)
807 			return (PSM_FAILURE);
808 
809 		/* Finally, free the previously allocated vectors */
810 		apic_free_vectors(dip, hdlp->ih_inum, count_vec,
811 		    old_priority, hdlp->ih_type);
812 		hdlp->ih_pri = new_priority; /* set the new value */
813 		break;
814 	case PSM_INTR_OP_SET_CPU:
815 		/*
816 		 * The interrupt handle given here has been allocated
817 		 * specifically for this command, and ih_private carries
818 		 * a CPU value.
819 		 */
820 		cpu = (int)(intptr_t)hdlp->ih_private;
821 
822 		if (!apic_cpu_in_range(cpu)) {
823 			*result = EINVAL;
824 			return (PSM_FAILURE);
825 		}
826 
827 
828 		/* Convert the vector to the irq using vector_to_irq table. */
829 		mutex_enter(&airq_mutex);
830 		irqp = apic_irq_table[apic_vector_to_irq[hdlp->ih_vector]];
831 		mutex_exit(&airq_mutex);
832 
833 		if (irqp == NULL) {
834 			*result = ENXIO;
835 			return (PSM_FAILURE);
836 		}
837 
838 		iflag = intr_clear();
839 		lock_set(&apic_ioapic_lock);
840 
841 		ret = apic_rebind_all(irqp, cpu);
842 
843 		lock_clear(&apic_ioapic_lock);
844 		intr_restore(iflag);
845 
846 		if (ret) {
847 			*result = EIO;
848 			return (PSM_FAILURE);
849 		}
850 		*result = 0;
851 		break;
852 	case PSM_INTR_OP_GET_INTR:
853 		/*
854 		 * The interrupt handle given here has been allocated
855 		 * specifically for this command, and ih_private carries
856 		 * a pointer to a apic_get_intr_t.
857 		 */
858 		if (apic_get_vector_intr_info(
859 		    hdlp->ih_vector, hdlp->ih_private) != PSM_SUCCESS)
860 			return (PSM_FAILURE);
861 		break;
862 	case PSM_INTR_OP_SET_CAP:
863 	default:
864 		return (PSM_FAILURE);
865 	}
866 	return (PSM_SUCCESS);
867 }
868