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