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