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 51701Sjohnny * Common Development and Distribution License (the "License"). 61701Sjohnny * You may not use this file except in compliance with the License. 70Sstevel@tonic-gate * 80Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 90Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 100Sstevel@tonic-gate * See the License for the specific language governing permissions 110Sstevel@tonic-gate * and limitations under the License. 120Sstevel@tonic-gate * 130Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 140Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 150Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 160Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 170Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 180Sstevel@tonic-gate * 190Sstevel@tonic-gate * CDDL HEADER END 200Sstevel@tonic-gate */ 210Sstevel@tonic-gate /* 223446Smrj * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 230Sstevel@tonic-gate * Use is subject to license terms. 240Sstevel@tonic-gate */ 250Sstevel@tonic-gate 260Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 270Sstevel@tonic-gate 280Sstevel@tonic-gate /* 290Sstevel@tonic-gate * apic_introp.c: 300Sstevel@tonic-gate * Has code for Advanced DDI interrupt framework support. 310Sstevel@tonic-gate */ 320Sstevel@tonic-gate 330Sstevel@tonic-gate #include <sys/cpuvar.h> 340Sstevel@tonic-gate #include <sys/psm.h> 353446Smrj #include <sys/archsystm.h> 363446Smrj #include <sys/apic.h> 370Sstevel@tonic-gate #include <sys/sunddi.h> 380Sstevel@tonic-gate #include <sys/ddi_impldefs.h> 39916Sschwartz #include <sys/mach_intr.h> 40916Sschwartz #include <sys/sysmacros.h> 410Sstevel@tonic-gate #include <sys/trap.h> 420Sstevel@tonic-gate #include <sys/pci.h> 430Sstevel@tonic-gate #include <sys/pci_intr_lib.h> 440Sstevel@tonic-gate 45916Sschwartz extern struct av_head autovect[]; 46916Sschwartz 470Sstevel@tonic-gate /* 480Sstevel@tonic-gate * Local Function Prototypes 490Sstevel@tonic-gate */ 500Sstevel@tonic-gate int apic_pci_msi_enable_vector(dev_info_t *, int, int, 510Sstevel@tonic-gate int, int, int); 520Sstevel@tonic-gate apic_irq_t *apic_find_irq(dev_info_t *, struct intrspec *, int); 530Sstevel@tonic-gate static int apic_get_pending(apic_irq_t *, int); 540Sstevel@tonic-gate static void apic_clear_mask(apic_irq_t *); 550Sstevel@tonic-gate static void apic_set_mask(apic_irq_t *); 560Sstevel@tonic-gate 570Sstevel@tonic-gate /* 580Sstevel@tonic-gate * MSI support flag: 590Sstevel@tonic-gate * reflects whether MSI is supported at APIC level 600Sstevel@tonic-gate * it can also be patched through /etc/system 610Sstevel@tonic-gate * 620Sstevel@tonic-gate * 0 = default value - don't know and need to call apic_check_msi_support() 630Sstevel@tonic-gate * to find out then set it accordingly 640Sstevel@tonic-gate * 1 = supported 650Sstevel@tonic-gate * -1 = not supported 660Sstevel@tonic-gate */ 670Sstevel@tonic-gate int apic_support_msi = 0; 680Sstevel@tonic-gate 690Sstevel@tonic-gate /* Multiple vector support for MSI */ 700Sstevel@tonic-gate int apic_multi_msi_enable = 1; 710Sstevel@tonic-gate int apic_multi_msi_max = 2; 720Sstevel@tonic-gate 730Sstevel@tonic-gate /* 740Sstevel@tonic-gate * apic_pci_msi_enable_vector: 750Sstevel@tonic-gate * Set the address/data fields in the MSI/X capability structure 760Sstevel@tonic-gate * XXX: MSI-X support 770Sstevel@tonic-gate */ 780Sstevel@tonic-gate /* ARGSUSED */ 790Sstevel@tonic-gate int 800Sstevel@tonic-gate apic_pci_msi_enable_vector(dev_info_t *dip, int type, int inum, int vector, 810Sstevel@tonic-gate int count, int target_apic_id) 820Sstevel@tonic-gate { 831997Sanish uint64_t msi_addr, msi_data; 841997Sanish ushort_t msi_ctrl; 851997Sanish int cap_ptr = i_ddi_get_msi_msix_cap_ptr(dip); 861997Sanish ddi_acc_handle_t handle = i_ddi_get_pci_config_handle(dip); 870Sstevel@tonic-gate 880Sstevel@tonic-gate DDI_INTR_IMPLDBG((CE_CONT, "apic_pci_msi_enable_vector: dip=0x%p\n" 890Sstevel@tonic-gate "\tdriver = %s, inum=0x%x vector=0x%x apicid=0x%x\n", (void *)dip, 900Sstevel@tonic-gate ddi_driver_name(dip), inum, vector, target_apic_id)); 910Sstevel@tonic-gate 924160Sjveta if (handle == NULL || cap_ptr == 0) 931997Sanish return (PSM_FAILURE); 941997Sanish 950Sstevel@tonic-gate /* MSI Address */ 960Sstevel@tonic-gate msi_addr = (MSI_ADDR_HDR | (target_apic_id << MSI_ADDR_DEST_SHIFT)); 970Sstevel@tonic-gate msi_addr |= ((MSI_ADDR_RH_FIXED << MSI_ADDR_RH_SHIFT) | 98*4397Sschwartz (MSI_ADDR_DM_PHYSICAL << MSI_ADDR_DM_SHIFT)); 990Sstevel@tonic-gate 1000Sstevel@tonic-gate /* MSI Data: MSI is edge triggered according to spec */ 1010Sstevel@tonic-gate msi_data = ((MSI_DATA_TM_EDGE << MSI_DATA_TM_SHIFT) | vector); 1020Sstevel@tonic-gate 1030Sstevel@tonic-gate DDI_INTR_IMPLDBG((CE_CONT, "apic_pci_msi_enable_vector: addr=0x%lx " 1040Sstevel@tonic-gate "data=0x%lx\n", (long)msi_addr, (long)msi_data)); 1050Sstevel@tonic-gate 1061997Sanish if (type == DDI_INTR_TYPE_MSI) { 1071997Sanish msi_ctrl = pci_config_get16(handle, cap_ptr + PCI_MSI_CTRL); 1081997Sanish 1091997Sanish /* Set the bits to inform how many MSIs are enabled */ 1101997Sanish msi_ctrl |= ((highbit(count) -1) << PCI_MSI_MME_SHIFT); 1111997Sanish pci_config_put16(handle, cap_ptr + PCI_MSI_CTRL, msi_ctrl); 1121997Sanish 1131997Sanish pci_config_put32(handle, 1141997Sanish cap_ptr + PCI_MSI_ADDR_OFFSET, msi_addr); 1151997Sanish 1161997Sanish if (msi_ctrl & PCI_MSI_64BIT_MASK) { 1171997Sanish pci_config_put32(handle, 1181997Sanish cap_ptr + PCI_MSI_ADDR_OFFSET + 4, msi_addr >> 32); 1191997Sanish pci_config_put16(handle, 1201997Sanish cap_ptr + PCI_MSI_64BIT_DATA, msi_data); 1211997Sanish } else { 1221997Sanish pci_config_put16(handle, 1231997Sanish cap_ptr + PCI_MSI_32BIT_DATA, msi_data); 1241997Sanish } 1251997Sanish 1261997Sanish } else if (type == DDI_INTR_TYPE_MSIX) { 1271997Sanish uintptr_t off; 1281997Sanish ddi_intr_msix_t *msix_p = i_ddi_get_msix(dip); 1291997Sanish 1301997Sanish /* Offset into the "inum"th entry in the MSI-X table */ 1311997Sanish off = (uintptr_t)msix_p->msix_tbl_addr + 1321997Sanish (inum * PCI_MSIX_VECTOR_SIZE); 1331997Sanish 1341997Sanish ddi_put32(msix_p->msix_tbl_hdl, 1351997Sanish (uint32_t *)(off + PCI_MSIX_DATA_OFFSET), msi_data); 1361997Sanish ddi_put64(msix_p->msix_tbl_hdl, 1371997Sanish (uint64_t *)(off + PCI_MSIX_LOWER_ADDR_OFFSET), msi_addr); 1380Sstevel@tonic-gate } 1390Sstevel@tonic-gate 1400Sstevel@tonic-gate return (PSM_SUCCESS); 1410Sstevel@tonic-gate } 1420Sstevel@tonic-gate 1430Sstevel@tonic-gate 1440Sstevel@tonic-gate /* 1450Sstevel@tonic-gate * This function returns the no. of vectors available for the pri. 1460Sstevel@tonic-gate * dip is not used at this moment. If we really don't need that, 1470Sstevel@tonic-gate * it will be removed. 1480Sstevel@tonic-gate */ 1490Sstevel@tonic-gate /*ARGSUSED*/ 1500Sstevel@tonic-gate int 1510Sstevel@tonic-gate apic_navail_vector(dev_info_t *dip, int pri) 1520Sstevel@tonic-gate { 1530Sstevel@tonic-gate int lowest, highest, i, navail, count; 1540Sstevel@tonic-gate 1550Sstevel@tonic-gate DDI_INTR_IMPLDBG((CE_CONT, "apic_navail_vector: dip: %p, pri: %x\n", 1560Sstevel@tonic-gate (void *)dip, pri)); 1570Sstevel@tonic-gate 1580Sstevel@tonic-gate highest = apic_ipltopri[pri] + APIC_VECTOR_MASK; 1590Sstevel@tonic-gate lowest = apic_ipltopri[pri - 1] + APIC_VECTOR_PER_IPL; 1600Sstevel@tonic-gate navail = count = 0; 1610Sstevel@tonic-gate 1620Sstevel@tonic-gate /* It has to be contiguous */ 1630Sstevel@tonic-gate for (i = lowest; i < highest; i++) { 1640Sstevel@tonic-gate count = 0; 1650Sstevel@tonic-gate while ((apic_vector_to_irq[i] == APIC_RESV_IRQ) && 166*4397Sschwartz (i < highest)) { 1672335Sjohnny if (APIC_CHECK_RESERVE_VECTORS(i)) 1680Sstevel@tonic-gate break; 1690Sstevel@tonic-gate count++; 1700Sstevel@tonic-gate i++; 1710Sstevel@tonic-gate } 1720Sstevel@tonic-gate if (count > navail) 1730Sstevel@tonic-gate navail = count; 1740Sstevel@tonic-gate } 1750Sstevel@tonic-gate return (navail); 1760Sstevel@tonic-gate } 1770Sstevel@tonic-gate 1782335Sjohnny /* 1792335Sjohnny * Finds "count" contiguous MSI vectors starting at the proper alignment 1802335Sjohnny * at "pri". 1812335Sjohnny * Caller needs to make sure that count has to be power of 2 and should not 1822335Sjohnny * be < 1. 1832335Sjohnny */ 1843446Smrj uchar_t 1850Sstevel@tonic-gate apic_find_multi_vectors(int pri, int count) 1860Sstevel@tonic-gate { 1872335Sjohnny int lowest, highest, i, navail, start, msibits; 1880Sstevel@tonic-gate 1890Sstevel@tonic-gate DDI_INTR_IMPLDBG((CE_CONT, "apic_find_mult: pri: %x, count: %x\n", 1900Sstevel@tonic-gate pri, count)); 1910Sstevel@tonic-gate 1920Sstevel@tonic-gate highest = apic_ipltopri[pri] + APIC_VECTOR_MASK; 1930Sstevel@tonic-gate lowest = apic_ipltopri[pri - 1] + APIC_VECTOR_PER_IPL; 1940Sstevel@tonic-gate navail = 0; 1950Sstevel@tonic-gate 1962335Sjohnny /* 1972335Sjohnny * msibits is the no. of lower order message data bits for the 1982335Sjohnny * allocated MSI vectors and is used to calculate the aligned 1992335Sjohnny * starting vector 2002335Sjohnny */ 2012335Sjohnny msibits = count - 1; 2022335Sjohnny 2030Sstevel@tonic-gate /* It has to be contiguous */ 2040Sstevel@tonic-gate for (i = lowest; i < highest; i++) { 2050Sstevel@tonic-gate navail = 0; 2062335Sjohnny 2072335Sjohnny /* 2082335Sjohnny * starting vector has to be aligned accordingly for 2092335Sjohnny * multiple MSIs 2102335Sjohnny */ 2112335Sjohnny if (msibits) 2122335Sjohnny i = (i + msibits) & ~msibits; 2130Sstevel@tonic-gate start = i; 2140Sstevel@tonic-gate while ((apic_vector_to_irq[i] == APIC_RESV_IRQ) && 215*4397Sschwartz (i < highest)) { 2162335Sjohnny if (APIC_CHECK_RESERVE_VECTORS(i)) 2170Sstevel@tonic-gate break; 2180Sstevel@tonic-gate navail++; 2190Sstevel@tonic-gate if (navail >= count) 2200Sstevel@tonic-gate return (start); 2210Sstevel@tonic-gate i++; 2220Sstevel@tonic-gate } 2230Sstevel@tonic-gate } 2240Sstevel@tonic-gate return (0); 2250Sstevel@tonic-gate } 2260Sstevel@tonic-gate 2273446Smrj 2280Sstevel@tonic-gate /* 2290Sstevel@tonic-gate * It finds the apic_irq_t associates with the dip, ispec and type. 2300Sstevel@tonic-gate */ 2310Sstevel@tonic-gate apic_irq_t * 2320Sstevel@tonic-gate apic_find_irq(dev_info_t *dip, struct intrspec *ispec, int type) 2330Sstevel@tonic-gate { 2340Sstevel@tonic-gate apic_irq_t *irqp; 2350Sstevel@tonic-gate int i; 2360Sstevel@tonic-gate 2370Sstevel@tonic-gate DDI_INTR_IMPLDBG((CE_CONT, "apic_find_irq: dip=0x%p vec=0x%x " 2380Sstevel@tonic-gate "ipl=0x%x type=0x%x\n", (void *)dip, ispec->intrspec_vec, 2390Sstevel@tonic-gate ispec->intrspec_pri, type)); 2400Sstevel@tonic-gate 2410Sstevel@tonic-gate for (i = apic_min_device_irq; i <= apic_max_device_irq; i++) { 2420Sstevel@tonic-gate if ((irqp = apic_irq_table[i]) == NULL) 2430Sstevel@tonic-gate continue; 2440Sstevel@tonic-gate if ((irqp->airq_dip == dip) && 2450Sstevel@tonic-gate (irqp->airq_origirq == ispec->intrspec_vec) && 2460Sstevel@tonic-gate (irqp->airq_ipl == ispec->intrspec_pri)) { 2470Sstevel@tonic-gate if (DDI_INTR_IS_MSI_OR_MSIX(type)) { 2480Sstevel@tonic-gate if (APIC_IS_MSI_OR_MSIX_INDEX(irqp-> 2490Sstevel@tonic-gate airq_mps_intr_index)) 2500Sstevel@tonic-gate return (irqp); 2510Sstevel@tonic-gate } else 2520Sstevel@tonic-gate return (irqp); 2530Sstevel@tonic-gate } 2540Sstevel@tonic-gate } 2550Sstevel@tonic-gate DDI_INTR_IMPLDBG((CE_CONT, "apic_find_irq: return NULL\n")); 2560Sstevel@tonic-gate return (NULL); 2570Sstevel@tonic-gate } 2580Sstevel@tonic-gate 2590Sstevel@tonic-gate 2600Sstevel@tonic-gate /* 2610Sstevel@tonic-gate * This function will return the pending bit of the irqp. 2620Sstevel@tonic-gate * It either comes from the IRR register of the APIC or the RDT 2630Sstevel@tonic-gate * entry of the I/O APIC. 2640Sstevel@tonic-gate * For the IRR to work, it needs to be to its binding CPU 2650Sstevel@tonic-gate */ 2660Sstevel@tonic-gate static int 2670Sstevel@tonic-gate apic_get_pending(apic_irq_t *irqp, int type) 2680Sstevel@tonic-gate { 2690Sstevel@tonic-gate int bit, index, irr, pending; 2700Sstevel@tonic-gate int intin_no; 2713446Smrj int apic_ix; 2720Sstevel@tonic-gate 2730Sstevel@tonic-gate DDI_INTR_IMPLDBG((CE_CONT, "apic_get_pending: irqp: %p, cpuid: %x " 2740Sstevel@tonic-gate "type: %x\n", (void *)irqp, irqp->airq_cpu & ~IRQ_USER_BOUND, 2750Sstevel@tonic-gate type)); 2760Sstevel@tonic-gate 2770Sstevel@tonic-gate /* need to get on the bound cpu */ 2780Sstevel@tonic-gate mutex_enter(&cpu_lock); 2790Sstevel@tonic-gate affinity_set(irqp->airq_cpu & ~IRQ_USER_BOUND); 2800Sstevel@tonic-gate 2810Sstevel@tonic-gate index = irqp->airq_vector / 32; 2820Sstevel@tonic-gate bit = irqp->airq_vector % 32; 2830Sstevel@tonic-gate irr = apicadr[APIC_IRR_REG + index]; 2840Sstevel@tonic-gate 2850Sstevel@tonic-gate affinity_clear(); 2860Sstevel@tonic-gate mutex_exit(&cpu_lock); 2870Sstevel@tonic-gate 2880Sstevel@tonic-gate pending = (irr & (1 << bit)) ? 1 : 0; 2890Sstevel@tonic-gate if (!pending && (type == DDI_INTR_TYPE_FIXED)) { 2900Sstevel@tonic-gate /* check I/O APIC for fixed interrupt */ 2910Sstevel@tonic-gate intin_no = irqp->airq_intin_no; 2923446Smrj apic_ix = irqp->airq_ioapicindex; 2933446Smrj pending = (READ_IOAPIC_RDT_ENTRY_LOW_DWORD(apic_ix, intin_no) & 2940Sstevel@tonic-gate AV_PENDING) ? 1 : 0; 2950Sstevel@tonic-gate } 2960Sstevel@tonic-gate return (pending); 2970Sstevel@tonic-gate } 2980Sstevel@tonic-gate 2990Sstevel@tonic-gate 3000Sstevel@tonic-gate /* 3010Sstevel@tonic-gate * This function will clear the mask for the interrupt on the I/O APIC 3020Sstevel@tonic-gate */ 3030Sstevel@tonic-gate static void 3040Sstevel@tonic-gate apic_clear_mask(apic_irq_t *irqp) 3050Sstevel@tonic-gate { 3060Sstevel@tonic-gate int intin_no; 3073446Smrj ulong_t iflag; 3080Sstevel@tonic-gate int32_t rdt_entry; 3093446Smrj int apic_ix; 3100Sstevel@tonic-gate 3110Sstevel@tonic-gate DDI_INTR_IMPLDBG((CE_CONT, "apic_clear_mask: irqp: %p\n", 3120Sstevel@tonic-gate (void *)irqp)); 3130Sstevel@tonic-gate 3140Sstevel@tonic-gate intin_no = irqp->airq_intin_no; 3153446Smrj apic_ix = irqp->airq_ioapicindex; 3160Sstevel@tonic-gate 3170Sstevel@tonic-gate iflag = intr_clear(); 3180Sstevel@tonic-gate lock_set(&apic_ioapic_lock); 3190Sstevel@tonic-gate 3203446Smrj rdt_entry = READ_IOAPIC_RDT_ENTRY_LOW_DWORD(apic_ix, intin_no); 3210Sstevel@tonic-gate 3220Sstevel@tonic-gate /* clear mask */ 3233446Smrj WRITE_IOAPIC_RDT_ENTRY_LOW_DWORD(apic_ix, intin_no, 3240Sstevel@tonic-gate ((~AV_MASK) & rdt_entry)); 3250Sstevel@tonic-gate 3260Sstevel@tonic-gate lock_clear(&apic_ioapic_lock); 3270Sstevel@tonic-gate intr_restore(iflag); 3280Sstevel@tonic-gate } 3290Sstevel@tonic-gate 3300Sstevel@tonic-gate 3310Sstevel@tonic-gate /* 3320Sstevel@tonic-gate * This function will mask the interrupt on the I/O APIC 3330Sstevel@tonic-gate */ 3340Sstevel@tonic-gate static void 3350Sstevel@tonic-gate apic_set_mask(apic_irq_t *irqp) 3360Sstevel@tonic-gate { 3370Sstevel@tonic-gate int intin_no; 3383446Smrj int apic_ix; 3393446Smrj ulong_t iflag; 3400Sstevel@tonic-gate int32_t rdt_entry; 3410Sstevel@tonic-gate 3420Sstevel@tonic-gate DDI_INTR_IMPLDBG((CE_CONT, "apic_set_mask: irqp: %p\n", (void *)irqp)); 3430Sstevel@tonic-gate 3440Sstevel@tonic-gate intin_no = irqp->airq_intin_no; 3453446Smrj apic_ix = irqp->airq_ioapicindex; 3460Sstevel@tonic-gate 3470Sstevel@tonic-gate iflag = intr_clear(); 3480Sstevel@tonic-gate 3490Sstevel@tonic-gate lock_set(&apic_ioapic_lock); 3500Sstevel@tonic-gate 3513446Smrj rdt_entry = READ_IOAPIC_RDT_ENTRY_LOW_DWORD(apic_ix, intin_no); 3520Sstevel@tonic-gate 3530Sstevel@tonic-gate /* mask it */ 3543446Smrj WRITE_IOAPIC_RDT_ENTRY_LOW_DWORD(apic_ix, intin_no, 3550Sstevel@tonic-gate (AV_MASK | rdt_entry)); 3560Sstevel@tonic-gate 3570Sstevel@tonic-gate lock_clear(&apic_ioapic_lock); 3580Sstevel@tonic-gate intr_restore(iflag); 3590Sstevel@tonic-gate } 3600Sstevel@tonic-gate 3610Sstevel@tonic-gate 3620Sstevel@tonic-gate void 3630Sstevel@tonic-gate apic_free_vectors(dev_info_t *dip, int inum, int count, int pri, int type) 3640Sstevel@tonic-gate { 3650Sstevel@tonic-gate int i; 3660Sstevel@tonic-gate apic_irq_t *irqptr; 3670Sstevel@tonic-gate struct intrspec ispec; 3680Sstevel@tonic-gate 3690Sstevel@tonic-gate DDI_INTR_IMPLDBG((CE_CONT, "apic_free_vectors: dip: %p inum: %x " 3700Sstevel@tonic-gate "count: %x pri: %x type: %x\n", 3710Sstevel@tonic-gate (void *)dip, inum, count, pri, type)); 3720Sstevel@tonic-gate 3730Sstevel@tonic-gate /* for MSI/X only */ 3740Sstevel@tonic-gate if (!DDI_INTR_IS_MSI_OR_MSIX(type)) 3750Sstevel@tonic-gate return; 3760Sstevel@tonic-gate 3770Sstevel@tonic-gate for (i = 0; i < count; i++) { 3780Sstevel@tonic-gate DDI_INTR_IMPLDBG((CE_CONT, "apic_free_vectors: inum=0x%x " 3790Sstevel@tonic-gate "pri=0x%x count=0x%x\n", inum, pri, count)); 3800Sstevel@tonic-gate ispec.intrspec_vec = inum + i; 3810Sstevel@tonic-gate ispec.intrspec_pri = pri; 3820Sstevel@tonic-gate if ((irqptr = apic_find_irq(dip, &ispec, type)) == NULL) { 3830Sstevel@tonic-gate DDI_INTR_IMPLDBG((CE_CONT, "apic_free_vectors: " 3840Sstevel@tonic-gate "dip=0x%p inum=0x%x pri=0x%x apic_find_irq() " 3850Sstevel@tonic-gate "failed\n", (void *)dip, inum, pri)); 3860Sstevel@tonic-gate continue; 3870Sstevel@tonic-gate } 3880Sstevel@tonic-gate irqptr->airq_mps_intr_index = FREE_INDEX; 3890Sstevel@tonic-gate apic_vector_to_irq[irqptr->airq_vector] = APIC_RESV_IRQ; 3900Sstevel@tonic-gate } 3910Sstevel@tonic-gate } 3920Sstevel@tonic-gate 3930Sstevel@tonic-gate 3940Sstevel@tonic-gate /* 3950Sstevel@tonic-gate * check whether the system supports MSI 3960Sstevel@tonic-gate * 3970Sstevel@tonic-gate * If PCI-E capability is found, then this must be a PCI-E system. 3980Sstevel@tonic-gate * Since MSI is required for PCI-E system, it returns PSM_SUCCESS 3990Sstevel@tonic-gate * to indicate this system supports MSI. 4000Sstevel@tonic-gate */ 4010Sstevel@tonic-gate int 4021701Sjohnny apic_check_msi_support() 4030Sstevel@tonic-gate { 4041701Sjohnny dev_info_t *cdip; 405881Sjohnny char dev_type[16]; 406881Sjohnny int dev_len; 4070Sstevel@tonic-gate 4081701Sjohnny DDI_INTR_IMPLDBG((CE_CONT, "apic_check_msi_support:\n")); 4090Sstevel@tonic-gate 4101701Sjohnny /* 4111701Sjohnny * check whether the first level children of root_node have 4121701Sjohnny * PCI-E capability 4131701Sjohnny */ 4141701Sjohnny for (cdip = ddi_get_child(ddi_root_node()); cdip != NULL; 4151701Sjohnny cdip = ddi_get_next_sibling(cdip)) { 4161701Sjohnny 4171701Sjohnny DDI_INTR_IMPLDBG((CE_CONT, "apic_check_msi_support: cdip: 0x%p," 4181701Sjohnny " driver: %s, binding: %s, nodename: %s\n", (void *)cdip, 4191701Sjohnny ddi_driver_name(cdip), ddi_binding_name(cdip), 4201701Sjohnny ddi_node_name(cdip))); 421881Sjohnny dev_len = sizeof (dev_type); 4221701Sjohnny if (ddi_getlongprop_buf(DDI_DEV_T_ANY, cdip, DDI_PROP_DONTPASS, 423881Sjohnny "device_type", (caddr_t)dev_type, &dev_len) 424881Sjohnny != DDI_PROP_SUCCESS) 425881Sjohnny continue; 426881Sjohnny if (strcmp(dev_type, "pciex") == 0) 427881Sjohnny return (PSM_SUCCESS); 4280Sstevel@tonic-gate } 4290Sstevel@tonic-gate 4300Sstevel@tonic-gate /* MSI is not supported on this system */ 431881Sjohnny DDI_INTR_IMPLDBG((CE_CONT, "apic_check_msi_support: no 'pciex' " 432881Sjohnny "device_type found\n")); 4330Sstevel@tonic-gate return (PSM_FAILURE); 4340Sstevel@tonic-gate } 4350Sstevel@tonic-gate 4360Sstevel@tonic-gate /* 4371997Sanish * apic_pci_msi_unconfigure: 4381997Sanish * 4391997Sanish * This and next two interfaces are copied from pci_intr_lib.c 4401997Sanish * Do ensure that these two files stay in sync. 4411997Sanish * These needed to be copied over here to avoid a deadlock situation on 4421997Sanish * certain mp systems that use MSI interrupts. 4431997Sanish * 4441997Sanish * IMPORTANT regards next three interfaces: 4451997Sanish * i) are called only for MSI/X interrupts. 4461997Sanish * ii) called with interrupts disabled, and must not block 4471997Sanish */ 4481997Sanish int 4491997Sanish apic_pci_msi_unconfigure(dev_info_t *rdip, int type, int inum) 4501997Sanish { 4511997Sanish ushort_t msi_ctrl; 4521997Sanish int cap_ptr = i_ddi_get_msi_msix_cap_ptr(rdip); 4531997Sanish ddi_acc_handle_t handle = i_ddi_get_pci_config_handle(rdip); 4541997Sanish 4554160Sjveta if (handle == NULL || cap_ptr == 0) 4561997Sanish return (PSM_FAILURE); 4571997Sanish 4581997Sanish if (type == DDI_INTR_TYPE_MSI) { 4591997Sanish msi_ctrl = pci_config_get16(handle, cap_ptr + PCI_MSI_CTRL); 4601997Sanish msi_ctrl &= (~PCI_MSI_MME_MASK); 4611997Sanish pci_config_put16(handle, cap_ptr + PCI_MSI_CTRL, msi_ctrl); 4621997Sanish pci_config_put32(handle, cap_ptr + PCI_MSI_ADDR_OFFSET, 0); 4631997Sanish 4641997Sanish if (msi_ctrl & PCI_MSI_64BIT_MASK) { 4651997Sanish pci_config_put16(handle, 4661997Sanish cap_ptr + PCI_MSI_64BIT_DATA, 0); 4671997Sanish pci_config_put32(handle, 4681997Sanish cap_ptr + PCI_MSI_ADDR_OFFSET + 4, 0); 4691997Sanish } else { 4701997Sanish pci_config_put16(handle, 4711997Sanish cap_ptr + PCI_MSI_32BIT_DATA, 0); 4721997Sanish } 4731997Sanish 4741997Sanish } else if (type == DDI_INTR_TYPE_MSIX) { 4751997Sanish uintptr_t off; 4761997Sanish ddi_intr_msix_t *msix_p = i_ddi_get_msix(rdip); 4771997Sanish 4781997Sanish /* Offset into the "inum"th entry in the MSI-X table */ 4791997Sanish off = (uintptr_t)msix_p->msix_tbl_addr + 4801997Sanish (inum * PCI_MSIX_VECTOR_SIZE); 4811997Sanish 4821997Sanish /* Reset the "data" and "addr" bits */ 4831997Sanish ddi_put32(msix_p->msix_tbl_hdl, 4841997Sanish (uint32_t *)(off + PCI_MSIX_DATA_OFFSET), 0); 4851997Sanish ddi_put64(msix_p->msix_tbl_hdl, (uint64_t *)off, 0); 4861997Sanish } 4871997Sanish 4881997Sanish return (PSM_SUCCESS); 4891997Sanish } 4901997Sanish 4911997Sanish 4921997Sanish /* 4931997Sanish * apic_pci_msi_enable_mode: 4941997Sanish */ 4951997Sanish int 4961997Sanish apic_pci_msi_enable_mode(dev_info_t *rdip, int type, int inum) 4971997Sanish { 4981997Sanish ushort_t msi_ctrl; 4991997Sanish int cap_ptr = i_ddi_get_msi_msix_cap_ptr(rdip); 5001997Sanish ddi_acc_handle_t handle = i_ddi_get_pci_config_handle(rdip); 5011997Sanish 5024160Sjveta if (handle == NULL || cap_ptr == 0) 5031997Sanish return (PSM_FAILURE); 5041997Sanish 5051997Sanish if (type == DDI_INTR_TYPE_MSI) { 5061997Sanish msi_ctrl = pci_config_get16(handle, cap_ptr + PCI_MSI_CTRL); 5071997Sanish if ((msi_ctrl & PCI_MSI_ENABLE_BIT)) 5081997Sanish return (PSM_SUCCESS); 5091997Sanish 5101997Sanish msi_ctrl |= PCI_MSI_ENABLE_BIT; 5111997Sanish pci_config_put16(handle, cap_ptr + PCI_MSI_CTRL, msi_ctrl); 5121997Sanish 5131997Sanish } else if (type == DDI_INTR_TYPE_MSIX) { 5141997Sanish uintptr_t off; 5151997Sanish ddi_intr_msix_t *msix_p; 5161997Sanish 5171997Sanish msi_ctrl = pci_config_get16(handle, cap_ptr + PCI_MSIX_CTRL); 5181997Sanish 5191997Sanish if (msi_ctrl & PCI_MSIX_ENABLE_BIT) 5201997Sanish return (PSM_SUCCESS); 5211997Sanish 5221997Sanish msi_ctrl |= PCI_MSIX_ENABLE_BIT; 5231997Sanish pci_config_put16(handle, cap_ptr + PCI_MSIX_CTRL, msi_ctrl); 5241997Sanish 5251997Sanish msix_p = i_ddi_get_msix(rdip); 5261997Sanish 5271997Sanish /* Offset into "inum"th entry in the MSI-X table & clear mask */ 5281997Sanish off = (uintptr_t)msix_p->msix_tbl_addr + (inum * 5291997Sanish PCI_MSIX_VECTOR_SIZE) + PCI_MSIX_VECTOR_CTRL_OFFSET; 5301997Sanish ddi_put32(msix_p->msix_tbl_hdl, (uint32_t *)off, 0); 5311997Sanish } 5321997Sanish 5331997Sanish return (PSM_SUCCESS); 5341997Sanish } 5351997Sanish 5361997Sanish /* 5371997Sanish * apic_pci_msi_disable_mode: 5381997Sanish */ 5391997Sanish int 5401997Sanish apic_pci_msi_disable_mode(dev_info_t *rdip, int type, int inum) 5411997Sanish { 5421997Sanish ushort_t msi_ctrl; 5431997Sanish int cap_ptr = i_ddi_get_msi_msix_cap_ptr(rdip); 5441997Sanish ddi_acc_handle_t handle = i_ddi_get_pci_config_handle(rdip); 5451997Sanish 5464160Sjveta if (handle == NULL || cap_ptr == 0) 5471997Sanish return (PSM_FAILURE); 5481997Sanish 5491997Sanish if (type == DDI_INTR_TYPE_MSI) { 5501997Sanish msi_ctrl = pci_config_get16(handle, cap_ptr + PCI_MSI_CTRL); 5511997Sanish if (!(msi_ctrl & PCI_MSI_ENABLE_BIT)) 5521997Sanish return (PSM_SUCCESS); 5531997Sanish 5541997Sanish msi_ctrl &= ~PCI_MSI_ENABLE_BIT; /* MSI disable */ 5551997Sanish pci_config_put16(handle, cap_ptr + PCI_MSI_CTRL, msi_ctrl); 5561997Sanish 5571997Sanish } else if (type == DDI_INTR_TYPE_MSIX) { 5581997Sanish uintptr_t off; 5591997Sanish ddi_intr_msix_t *msix_p; 5601997Sanish 5611997Sanish msi_ctrl = pci_config_get16(handle, cap_ptr + PCI_MSIX_CTRL); 5621997Sanish 5631997Sanish if (!(msi_ctrl & PCI_MSIX_ENABLE_BIT)) 5641997Sanish return (PSM_SUCCESS); 5651997Sanish 5661997Sanish msix_p = i_ddi_get_msix(rdip); 5671997Sanish 5681997Sanish /* Offset into "inum"th entry in the MSI-X table & mask it */ 5691997Sanish off = (uintptr_t)msix_p->msix_tbl_addr + (inum * 5701997Sanish PCI_MSIX_VECTOR_SIZE) + PCI_MSIX_VECTOR_CTRL_OFFSET; 5711997Sanish ddi_put32(msix_p->msix_tbl_hdl, (uint32_t *)off, 0x1); 5721997Sanish } 5731997Sanish 5741997Sanish return (PSM_SUCCESS); 5751997Sanish } 5761997Sanish 5773446Smrj 578*4397Sschwartz static int 579*4397Sschwartz apic_set_cpu(uint32_t vector, int cpu, int *result) 580*4397Sschwartz { 581*4397Sschwartz apic_irq_t *irqp; 582*4397Sschwartz int iflag; 583*4397Sschwartz int ret; 584*4397Sschwartz 585*4397Sschwartz DDI_INTR_IMPLDBG((CE_CONT, "APIC_SET_CPU\n")); 586*4397Sschwartz 587*4397Sschwartz /* Convert the vector to the irq using vector_to_irq table. */ 588*4397Sschwartz mutex_enter(&airq_mutex); 589*4397Sschwartz irqp = apic_irq_table[apic_vector_to_irq[vector]]; 590*4397Sschwartz mutex_exit(&airq_mutex); 591*4397Sschwartz 592*4397Sschwartz if (irqp == NULL) { 593*4397Sschwartz *result = ENXIO; 594*4397Sschwartz return (PSM_FAILURE); 595*4397Sschwartz } 596*4397Sschwartz 597*4397Sschwartz /* Fail if this is an MSI intr and is part of a group. */ 598*4397Sschwartz if ((irqp->airq_mps_intr_index == MSI_INDEX) && 599*4397Sschwartz (irqp->airq_intin_no > 1)) { 600*4397Sschwartz *result = ENXIO; 601*4397Sschwartz return (PSM_FAILURE); 602*4397Sschwartz } 603*4397Sschwartz 604*4397Sschwartz iflag = intr_clear(); 605*4397Sschwartz lock_set(&apic_ioapic_lock); 606*4397Sschwartz 607*4397Sschwartz ret = apic_rebind_all(irqp, cpu); 608*4397Sschwartz 609*4397Sschwartz lock_clear(&apic_ioapic_lock); 610*4397Sschwartz intr_restore(iflag); 611*4397Sschwartz 612*4397Sschwartz if (ret) { 613*4397Sschwartz *result = EIO; 614*4397Sschwartz return (PSM_FAILURE); 615*4397Sschwartz } 616*4397Sschwartz *result = 0; 617*4397Sschwartz return (PSM_SUCCESS); 618*4397Sschwartz } 619*4397Sschwartz 620*4397Sschwartz static int 621*4397Sschwartz apic_grp_set_cpu(uint32_t vector, int new_cpu, int *result) 622*4397Sschwartz { 623*4397Sschwartz dev_info_t *orig_dip; 624*4397Sschwartz uchar_t orig_cpu; 625*4397Sschwartz int iflag; 626*4397Sschwartz apic_irq_t *irqps[PCI_MSI_MAX_INTRS]; 627*4397Sschwartz int i; 628*4397Sschwartz int cap_ptr; 629*4397Sschwartz int msi_mask_off; 630*4397Sschwartz ushort_t msi_ctrl; 631*4397Sschwartz uint32_t msi_pvm; 632*4397Sschwartz ddi_acc_handle_t handle; 633*4397Sschwartz int num_vectors = 0; 634*4397Sschwartz 635*4397Sschwartz DDI_INTR_IMPLDBG((CE_CONT, "APIC_GRP_SET_CPU\n")); 636*4397Sschwartz 637*4397Sschwartz /* 638*4397Sschwartz * Take mutex to insure that table doesn't change out from underneath 639*4397Sschwartz * us while we're playing with it. 640*4397Sschwartz */ 641*4397Sschwartz mutex_enter(&airq_mutex); 642*4397Sschwartz irqps[0] = apic_irq_table[apic_vector_to_irq[vector]]; 643*4397Sschwartz orig_cpu = irqps[0]->airq_temp_cpu; 644*4397Sschwartz orig_dip = irqps[0]->airq_dip; 645*4397Sschwartz num_vectors = irqps[0]->airq_intin_no; 646*4397Sschwartz 647*4397Sschwartz /* A "group" of 1 */ 648*4397Sschwartz if (num_vectors == 1) { 649*4397Sschwartz mutex_exit(&airq_mutex); 650*4397Sschwartz return (apic_set_cpu(vector, new_cpu, result)); 651*4397Sschwartz } 652*4397Sschwartz 653*4397Sschwartz *result = ENXIO; 654*4397Sschwartz 655*4397Sschwartz if (irqps[0]->airq_mps_intr_index != MSI_INDEX) { 656*4397Sschwartz mutex_exit(&airq_mutex); 657*4397Sschwartz DDI_INTR_IMPLDBG((CE_CONT, "set_grp: intr not MSI\n")); 658*4397Sschwartz goto set_grp_intr_done; 659*4397Sschwartz } 660*4397Sschwartz if ((num_vectors < 1) || ((num_vectors - 1) & vector)) { 661*4397Sschwartz mutex_exit(&airq_mutex); 662*4397Sschwartz DDI_INTR_IMPLDBG((CE_CONT, 663*4397Sschwartz "set_grp: base vec not part of a grp or not aligned: " 664*4397Sschwartz "vec:0x%x, num_vec:0x%x\n", vector, num_vectors)); 665*4397Sschwartz goto set_grp_intr_done; 666*4397Sschwartz } 667*4397Sschwartz DDI_INTR_IMPLDBG((CE_CONT, "set_grp: num intrs in grp: %d\n", 668*4397Sschwartz num_vectors)); 669*4397Sschwartz 670*4397Sschwartz ASSERT((num_vectors + vector) < APIC_MAX_VECTOR); 671*4397Sschwartz 672*4397Sschwartz *result = EIO; 673*4397Sschwartz 674*4397Sschwartz /* 675*4397Sschwartz * All IRQ entries in the table for the given device will be not 676*4397Sschwartz * shared. Since they are not shared, the dip in the table will 677*4397Sschwartz * be true to the device of interest. 678*4397Sschwartz */ 679*4397Sschwartz for (i = 1; i < num_vectors; i++) { 680*4397Sschwartz irqps[i] = apic_irq_table[apic_vector_to_irq[vector + i]]; 681*4397Sschwartz if (irqps[i] == NULL) { 682*4397Sschwartz mutex_exit(&airq_mutex); 683*4397Sschwartz goto set_grp_intr_done; 684*4397Sschwartz } 685*4397Sschwartz #ifdef DEBUG 686*4397Sschwartz /* Sanity check: CPU and dip is the same for all entries. */ 687*4397Sschwartz if ((irqps[i]->airq_dip != orig_dip) || 688*4397Sschwartz (irqps[i]->airq_temp_cpu != orig_cpu)) { 689*4397Sschwartz mutex_exit(&airq_mutex); 690*4397Sschwartz DDI_INTR_IMPLDBG((CE_CONT, 691*4397Sschwartz "set_grp: cpu or dip for vec 0x%x difft than for " 692*4397Sschwartz "vec 0x%x\n", vector, vector + i)); 693*4397Sschwartz DDI_INTR_IMPLDBG((CE_CONT, 694*4397Sschwartz " cpu: %d vs %d, dip: 0x%p vs 0x%p\n", orig_cpu, 695*4397Sschwartz irqps[i]->airq_temp_cpu, (void *)orig_dip, 696*4397Sschwartz (void *)irqps[i]->airq_dip)); 697*4397Sschwartz goto set_grp_intr_done; 698*4397Sschwartz } 699*4397Sschwartz #endif /* DEBUG */ 700*4397Sschwartz } 701*4397Sschwartz mutex_exit(&airq_mutex); 702*4397Sschwartz 703*4397Sschwartz cap_ptr = i_ddi_get_msi_msix_cap_ptr(orig_dip); 704*4397Sschwartz handle = i_ddi_get_pci_config_handle(orig_dip); 705*4397Sschwartz msi_ctrl = pci_config_get16(handle, cap_ptr + PCI_MSI_CTRL); 706*4397Sschwartz 707*4397Sschwartz /* MSI Per vector masking is supported. */ 708*4397Sschwartz if (msi_ctrl & PCI_MSI_PVM_MASK) { 709*4397Sschwartz if (msi_ctrl & PCI_MSI_64BIT_MASK) 710*4397Sschwartz msi_mask_off = cap_ptr + PCI_MSI_64BIT_MASKBITS; 711*4397Sschwartz else 712*4397Sschwartz msi_mask_off = cap_ptr + PCI_MSI_32BIT_MASK; 713*4397Sschwartz msi_pvm = pci_config_get32(handle, msi_mask_off); 714*4397Sschwartz pci_config_put32(handle, msi_mask_off, (uint32_t)-1); 715*4397Sschwartz DDI_INTR_IMPLDBG((CE_CONT, 716*4397Sschwartz "set_grp: pvm supported. Mask set to 0x%x\n", 717*4397Sschwartz pci_config_get32(handle, msi_mask_off))); 718*4397Sschwartz } 719*4397Sschwartz 720*4397Sschwartz iflag = intr_clear(); 721*4397Sschwartz lock_set(&apic_ioapic_lock); 722*4397Sschwartz 723*4397Sschwartz /* 724*4397Sschwartz * Do the first rebind and check for errors. Apic_rebind_all returns 725*4397Sschwartz * an error if the CPU is not accepting interrupts. If the first one 726*4397Sschwartz * succeeds they all will. 727*4397Sschwartz */ 728*4397Sschwartz if (apic_rebind_all(irqps[0], new_cpu)) 729*4397Sschwartz (void) apic_rebind_all(irqps[0], orig_cpu); 730*4397Sschwartz else { 731*4397Sschwartz for (i = 1; i < num_vectors; i++) 732*4397Sschwartz (void) apic_rebind_all(irqps[i], new_cpu); 733*4397Sschwartz *result = 0; /* SUCCESS */ 734*4397Sschwartz } 735*4397Sschwartz 736*4397Sschwartz lock_clear(&apic_ioapic_lock); 737*4397Sschwartz intr_restore(iflag); 738*4397Sschwartz 739*4397Sschwartz /* Reenable vectors if per vector masking is supported. */ 740*4397Sschwartz if (msi_ctrl & PCI_MSI_PVM_MASK) { 741*4397Sschwartz pci_config_put32(handle, msi_mask_off, msi_pvm); 742*4397Sschwartz DDI_INTR_IMPLDBG((CE_CONT, 743*4397Sschwartz "set_grp: pvm supported. Mask restored to 0x%x\n", 744*4397Sschwartz pci_config_get32(handle, msi_mask_off))); 745*4397Sschwartz } 746*4397Sschwartz 747*4397Sschwartz set_grp_intr_done: 748*4397Sschwartz if (*result != 0) 749*4397Sschwartz return (PSM_FAILURE); 750*4397Sschwartz 751*4397Sschwartz return (PSM_SUCCESS); 752*4397Sschwartz } 753*4397Sschwartz 754*4397Sschwartz static int 755*4397Sschwartz apic_get_vector_intr_info(int vecirq, apic_get_intr_t *intr_params_p) 756*4397Sschwartz { 757*4397Sschwartz struct autovec *av_dev; 758*4397Sschwartz uchar_t irqno; 759*4397Sschwartz int i; 760*4397Sschwartz apic_irq_t *irq_p; 761*4397Sschwartz 762*4397Sschwartz /* Sanity check the vector/irq argument. */ 763*4397Sschwartz ASSERT((vecirq >= 0) || (vecirq <= APIC_MAX_VECTOR)); 764*4397Sschwartz 765*4397Sschwartz mutex_enter(&airq_mutex); 766*4397Sschwartz 767*4397Sschwartz /* 768*4397Sschwartz * Convert the vecirq arg to an irq using vector_to_irq table 769*4397Sschwartz * if the arg is a vector. Pass thru if already an irq. 770*4397Sschwartz */ 771*4397Sschwartz if ((intr_params_p->avgi_req_flags & PSMGI_INTRBY_FLAGS) == 772*4397Sschwartz PSMGI_INTRBY_VEC) 773*4397Sschwartz irqno = apic_vector_to_irq[vecirq]; 774*4397Sschwartz else 775*4397Sschwartz irqno = vecirq; 776*4397Sschwartz 777*4397Sschwartz irq_p = apic_irq_table[irqno]; 778*4397Sschwartz 779*4397Sschwartz if ((irq_p == NULL) || 780*4397Sschwartz (irq_p->airq_temp_cpu == IRQ_UNBOUND) || 781*4397Sschwartz (irq_p->airq_temp_cpu == IRQ_UNINIT)) { 782*4397Sschwartz mutex_exit(&airq_mutex); 783*4397Sschwartz return (PSM_FAILURE); 784*4397Sschwartz } 785*4397Sschwartz 786*4397Sschwartz if (intr_params_p->avgi_req_flags & PSMGI_REQ_CPUID) { 787*4397Sschwartz 788*4397Sschwartz /* Get the (temp) cpu from apic_irq table, indexed by irq. */ 789*4397Sschwartz intr_params_p->avgi_cpu_id = irq_p->airq_temp_cpu; 790*4397Sschwartz 791*4397Sschwartz /* Return user bound info for intrd. */ 792*4397Sschwartz if (intr_params_p->avgi_cpu_id & IRQ_USER_BOUND) { 793*4397Sschwartz intr_params_p->avgi_cpu_id &= ~IRQ_USER_BOUND; 794*4397Sschwartz intr_params_p->avgi_cpu_id |= PSMGI_CPU_USER_BOUND; 795*4397Sschwartz } 796*4397Sschwartz } 797*4397Sschwartz 798*4397Sschwartz if (intr_params_p->avgi_req_flags & PSMGI_REQ_VECTOR) 799*4397Sschwartz intr_params_p->avgi_vector = irq_p->airq_vector; 800*4397Sschwartz 801*4397Sschwartz if (intr_params_p->avgi_req_flags & 802*4397Sschwartz (PSMGI_REQ_NUM_DEVS | PSMGI_REQ_GET_DEVS)) 803*4397Sschwartz /* Get number of devices from apic_irq table shared field. */ 804*4397Sschwartz intr_params_p->avgi_num_devs = irq_p->airq_share; 805*4397Sschwartz 806*4397Sschwartz if (intr_params_p->avgi_req_flags & PSMGI_REQ_GET_DEVS) { 807*4397Sschwartz 808*4397Sschwartz intr_params_p->avgi_req_flags |= PSMGI_REQ_NUM_DEVS; 809*4397Sschwartz 810*4397Sschwartz /* Some devices have NULL dip. Don't count these. */ 811*4397Sschwartz if (intr_params_p->avgi_num_devs > 0) { 812*4397Sschwartz for (i = 0, av_dev = autovect[irqno].avh_link; 813*4397Sschwartz av_dev; av_dev = av_dev->av_link) 814*4397Sschwartz if (av_dev->av_vector && av_dev->av_dip) 815*4397Sschwartz i++; 816*4397Sschwartz intr_params_p->avgi_num_devs = 817*4397Sschwartz MIN(intr_params_p->avgi_num_devs, i); 818*4397Sschwartz } 819*4397Sschwartz 820*4397Sschwartz /* There are no viable dips to return. */ 821*4397Sschwartz if (intr_params_p->avgi_num_devs == 0) 822*4397Sschwartz intr_params_p->avgi_dip_list = NULL; 823*4397Sschwartz 824*4397Sschwartz else { /* Return list of dips */ 825*4397Sschwartz 826*4397Sschwartz /* Allocate space in array for that number of devs. */ 827*4397Sschwartz intr_params_p->avgi_dip_list = kmem_zalloc( 828*4397Sschwartz intr_params_p->avgi_num_devs * 829*4397Sschwartz sizeof (dev_info_t *), 830*4397Sschwartz KM_SLEEP); 831*4397Sschwartz 832*4397Sschwartz /* 833*4397Sschwartz * Loop through the device list of the autovec table 834*4397Sschwartz * filling in the dip array. 835*4397Sschwartz * 836*4397Sschwartz * Note that the autovect table may have some special 837*4397Sschwartz * entries which contain NULL dips. These will be 838*4397Sschwartz * ignored. 839*4397Sschwartz */ 840*4397Sschwartz for (i = 0, av_dev = autovect[irqno].avh_link; 841*4397Sschwartz av_dev; av_dev = av_dev->av_link) 842*4397Sschwartz if (av_dev->av_vector && av_dev->av_dip) 843*4397Sschwartz intr_params_p->avgi_dip_list[i++] = 844*4397Sschwartz av_dev->av_dip; 845*4397Sschwartz } 846*4397Sschwartz } 847*4397Sschwartz 848*4397Sschwartz mutex_exit(&airq_mutex); 849*4397Sschwartz 850*4397Sschwartz return (PSM_SUCCESS); 851*4397Sschwartz } 852*4397Sschwartz 853*4397Sschwartz 8541997Sanish /* 8550Sstevel@tonic-gate * This function provides external interface to the nexus for all 8560Sstevel@tonic-gate * functionalities related to the new DDI interrupt framework. 8570Sstevel@tonic-gate * 8580Sstevel@tonic-gate * Input: 8590Sstevel@tonic-gate * dip - pointer to the dev_info structure of the requested device 8600Sstevel@tonic-gate * hdlp - pointer to the internal interrupt handle structure for the 8610Sstevel@tonic-gate * requested interrupt 8620Sstevel@tonic-gate * intr_op - opcode for this call 8630Sstevel@tonic-gate * result - pointer to the integer that will hold the result to be 8640Sstevel@tonic-gate * passed back if return value is PSM_SUCCESS 8650Sstevel@tonic-gate * 8660Sstevel@tonic-gate * Output: 8670Sstevel@tonic-gate * return value is either PSM_SUCCESS or PSM_FAILURE 8680Sstevel@tonic-gate */ 8690Sstevel@tonic-gate int 8700Sstevel@tonic-gate apic_intr_ops(dev_info_t *dip, ddi_intr_handle_impl_t *hdlp, 8710Sstevel@tonic-gate psm_intr_op_t intr_op, int *result) 8720Sstevel@tonic-gate { 873*4397Sschwartz int cap; 8740Sstevel@tonic-gate int count_vec; 8750Sstevel@tonic-gate int old_priority; 8760Sstevel@tonic-gate int new_priority; 877*4397Sschwartz int new_cpu; 8780Sstevel@tonic-gate apic_irq_t *irqp; 8790Sstevel@tonic-gate struct intrspec *ispec, intr_spec; 8800Sstevel@tonic-gate 8810Sstevel@tonic-gate DDI_INTR_IMPLDBG((CE_CONT, "apic_intr_ops: dip: %p hdlp: %p " 8820Sstevel@tonic-gate "intr_op: %x\n", (void *)dip, (void *)hdlp, intr_op)); 8830Sstevel@tonic-gate 8840Sstevel@tonic-gate ispec = &intr_spec; 8850Sstevel@tonic-gate ispec->intrspec_pri = hdlp->ih_pri; 8860Sstevel@tonic-gate ispec->intrspec_vec = hdlp->ih_inum; 8870Sstevel@tonic-gate ispec->intrspec_func = hdlp->ih_cb_func; 8880Sstevel@tonic-gate 8890Sstevel@tonic-gate switch (intr_op) { 8900Sstevel@tonic-gate case PSM_INTR_OP_CHECK_MSI: 8910Sstevel@tonic-gate /* 8920Sstevel@tonic-gate * Check MSI/X is supported or not at APIC level and 8930Sstevel@tonic-gate * masked off the MSI/X bits in hdlp->ih_type if not 8940Sstevel@tonic-gate * supported before return. If MSI/X is supported, 8950Sstevel@tonic-gate * leave the ih_type unchanged and return. 8960Sstevel@tonic-gate * 8970Sstevel@tonic-gate * hdlp->ih_type passed in from the nexus has all the 8980Sstevel@tonic-gate * interrupt types supported by the device. 8990Sstevel@tonic-gate */ 9000Sstevel@tonic-gate if (apic_support_msi == 0) { 9010Sstevel@tonic-gate /* 9020Sstevel@tonic-gate * if apic_support_msi is not set, call 9030Sstevel@tonic-gate * apic_check_msi_support() to check whether msi 9040Sstevel@tonic-gate * is supported first 9050Sstevel@tonic-gate */ 9061701Sjohnny if (apic_check_msi_support() == PSM_SUCCESS) 9070Sstevel@tonic-gate apic_support_msi = 1; 9080Sstevel@tonic-gate else 9090Sstevel@tonic-gate apic_support_msi = -1; 9100Sstevel@tonic-gate } 9110Sstevel@tonic-gate if (apic_support_msi == 1) 9120Sstevel@tonic-gate *result = hdlp->ih_type; 9130Sstevel@tonic-gate else 9140Sstevel@tonic-gate *result = hdlp->ih_type & ~(DDI_INTR_TYPE_MSI | 9150Sstevel@tonic-gate DDI_INTR_TYPE_MSIX); 9160Sstevel@tonic-gate break; 9170Sstevel@tonic-gate case PSM_INTR_OP_ALLOC_VECTORS: 9180Sstevel@tonic-gate *result = apic_alloc_vectors(dip, hdlp->ih_inum, 9192335Sjohnny hdlp->ih_scratch1, hdlp->ih_pri, hdlp->ih_type, 9202335Sjohnny (int)(uintptr_t)hdlp->ih_scratch2); 9210Sstevel@tonic-gate break; 9220Sstevel@tonic-gate case PSM_INTR_OP_FREE_VECTORS: 9230Sstevel@tonic-gate apic_free_vectors(dip, hdlp->ih_inum, hdlp->ih_scratch1, 9240Sstevel@tonic-gate hdlp->ih_pri, hdlp->ih_type); 9250Sstevel@tonic-gate break; 9260Sstevel@tonic-gate case PSM_INTR_OP_NAVAIL_VECTORS: 9270Sstevel@tonic-gate *result = apic_navail_vector(dip, hdlp->ih_pri); 9280Sstevel@tonic-gate break; 9290Sstevel@tonic-gate case PSM_INTR_OP_XLATE_VECTOR: 930916Sschwartz ispec = ((ihdl_plat_t *)hdlp->ih_private)->ip_ispecp; 9310Sstevel@tonic-gate *result = apic_introp_xlate(dip, ispec, hdlp->ih_type); 9320Sstevel@tonic-gate break; 9330Sstevel@tonic-gate case PSM_INTR_OP_GET_PENDING: 9340Sstevel@tonic-gate if ((irqp = apic_find_irq(dip, ispec, hdlp->ih_type)) == NULL) 9350Sstevel@tonic-gate return (PSM_FAILURE); 9360Sstevel@tonic-gate *result = apic_get_pending(irqp, hdlp->ih_type); 9370Sstevel@tonic-gate break; 9380Sstevel@tonic-gate case PSM_INTR_OP_CLEAR_MASK: 9390Sstevel@tonic-gate if (hdlp->ih_type != DDI_INTR_TYPE_FIXED) 9400Sstevel@tonic-gate return (PSM_FAILURE); 9410Sstevel@tonic-gate irqp = apic_find_irq(dip, ispec, hdlp->ih_type); 9420Sstevel@tonic-gate if (irqp == NULL) 9430Sstevel@tonic-gate return (PSM_FAILURE); 9440Sstevel@tonic-gate apic_clear_mask(irqp); 9450Sstevel@tonic-gate break; 9460Sstevel@tonic-gate case PSM_INTR_OP_SET_MASK: 9470Sstevel@tonic-gate if (hdlp->ih_type != DDI_INTR_TYPE_FIXED) 9480Sstevel@tonic-gate return (PSM_FAILURE); 9490Sstevel@tonic-gate if ((irqp = apic_find_irq(dip, ispec, hdlp->ih_type)) == NULL) 9500Sstevel@tonic-gate return (PSM_FAILURE); 9510Sstevel@tonic-gate apic_set_mask(irqp); 9520Sstevel@tonic-gate break; 9530Sstevel@tonic-gate case PSM_INTR_OP_GET_CAP: 9540Sstevel@tonic-gate cap = DDI_INTR_FLAG_PENDING; 9550Sstevel@tonic-gate if (hdlp->ih_type == DDI_INTR_TYPE_FIXED) 9560Sstevel@tonic-gate cap |= DDI_INTR_FLAG_MASKABLE; 9570Sstevel@tonic-gate *result = cap; 9580Sstevel@tonic-gate break; 9590Sstevel@tonic-gate case PSM_INTR_OP_GET_SHARED: 9600Sstevel@tonic-gate if (hdlp->ih_type != DDI_INTR_TYPE_FIXED) 9610Sstevel@tonic-gate return (PSM_FAILURE); 9620Sstevel@tonic-gate if ((irqp = apic_find_irq(dip, ispec, hdlp->ih_type)) == NULL) 9630Sstevel@tonic-gate return (PSM_FAILURE); 9640Sstevel@tonic-gate *result = irqp->airq_share ? 1: 0; 9650Sstevel@tonic-gate break; 9660Sstevel@tonic-gate case PSM_INTR_OP_SET_PRI: 9670Sstevel@tonic-gate old_priority = hdlp->ih_pri; /* save old value */ 9680Sstevel@tonic-gate new_priority = *(int *)result; /* try the new value */ 9690Sstevel@tonic-gate 9700Sstevel@tonic-gate /* First, check if "hdlp->ih_scratch1" vectors exist? */ 9710Sstevel@tonic-gate if (apic_navail_vector(dip, new_priority) < hdlp->ih_scratch1) 9720Sstevel@tonic-gate return (PSM_FAILURE); 9730Sstevel@tonic-gate 9740Sstevel@tonic-gate /* Now allocate the vectors */ 9750Sstevel@tonic-gate count_vec = apic_alloc_vectors(dip, hdlp->ih_inum, 9762335Sjohnny hdlp->ih_scratch1, new_priority, hdlp->ih_type, 9772335Sjohnny DDI_INTR_ALLOC_STRICT); 9780Sstevel@tonic-gate 9793446Smrj /* Did we get new vectors? */ 9802335Sjohnny if (!count_vec) 9810Sstevel@tonic-gate return (PSM_FAILURE); 9820Sstevel@tonic-gate 9830Sstevel@tonic-gate /* Finally, free the previously allocated vectors */ 9840Sstevel@tonic-gate apic_free_vectors(dip, hdlp->ih_inum, count_vec, 9850Sstevel@tonic-gate old_priority, hdlp->ih_type); 9860Sstevel@tonic-gate hdlp->ih_pri = new_priority; /* set the new value */ 9870Sstevel@tonic-gate break; 988916Sschwartz case PSM_INTR_OP_SET_CPU: 989*4397Sschwartz case PSM_INTR_OP_GRP_SET_CPU: 990916Sschwartz /* 991916Sschwartz * The interrupt handle given here has been allocated 992916Sschwartz * specifically for this command, and ih_private carries 993916Sschwartz * a CPU value. 994916Sschwartz */ 995*4397Sschwartz new_cpu = (int)(intptr_t)hdlp->ih_private; 996*4397Sschwartz if (!apic_cpu_in_range(new_cpu)) { 997*4397Sschwartz DDI_INTR_IMPLDBG((CE_CONT, 998*4397Sschwartz "[grp_]set_cpu: cpu out of range: %d\n", new_cpu)); 999916Sschwartz *result = EINVAL; 1000916Sschwartz return (PSM_FAILURE); 1001916Sschwartz } 1002*4397Sschwartz if (intr_op == PSM_INTR_OP_SET_CPU) { 1003*4397Sschwartz if (apic_set_cpu(hdlp->ih_vector, new_cpu, result) != 1004*4397Sschwartz PSM_SUCCESS) 1005*4397Sschwartz return (PSM_FAILURE); 1006*4397Sschwartz } else { 1007*4397Sschwartz if (apic_grp_set_cpu(hdlp->ih_vector, new_cpu, 1008*4397Sschwartz result) != PSM_SUCCESS) 1009*4397Sschwartz return (PSM_FAILURE); 1010916Sschwartz } 1011916Sschwartz break; 1012916Sschwartz case PSM_INTR_OP_GET_INTR: 1013916Sschwartz /* 1014916Sschwartz * The interrupt handle given here has been allocated 1015916Sschwartz * specifically for this command, and ih_private carries 1016916Sschwartz * a pointer to a apic_get_intr_t. 1017916Sschwartz */ 1018916Sschwartz if (apic_get_vector_intr_info( 1019916Sschwartz hdlp->ih_vector, hdlp->ih_private) != PSM_SUCCESS) 1020916Sschwartz return (PSM_FAILURE); 1021916Sschwartz break; 1022*4397Sschwartz case PSM_INTR_OP_APIC_TYPE: 1023*4397Sschwartz hdlp->ih_private = apic_get_apic_type(); 1024*4397Sschwartz hdlp->ih_ver = apic_get_apic_version(); 1025*4397Sschwartz break; 10260Sstevel@tonic-gate case PSM_INTR_OP_SET_CAP: 10270Sstevel@tonic-gate default: 10280Sstevel@tonic-gate return (PSM_FAILURE); 10290Sstevel@tonic-gate } 10300Sstevel@tonic-gate return (PSM_SUCCESS); 10310Sstevel@tonic-gate } 1032