18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public 38c2ecf20Sopenharmony_ci * License. See the file "COPYING" in the main directory of this archive 48c2ecf20Sopenharmony_ci * for more details. 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Copyright (C) 2005-2009, 2010 Cavium Networks 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci#include <linux/kernel.h> 98c2ecf20Sopenharmony_ci#include <linux/init.h> 108c2ecf20Sopenharmony_ci#include <linux/msi.h> 118c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 128c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include <asm/octeon/octeon.h> 158c2ecf20Sopenharmony_ci#include <asm/octeon/cvmx-npi-defs.h> 168c2ecf20Sopenharmony_ci#include <asm/octeon/cvmx-pci-defs.h> 178c2ecf20Sopenharmony_ci#include <asm/octeon/cvmx-npei-defs.h> 188c2ecf20Sopenharmony_ci#include <asm/octeon/cvmx-sli-defs.h> 198c2ecf20Sopenharmony_ci#include <asm/octeon/cvmx-pexp-defs.h> 208c2ecf20Sopenharmony_ci#include <asm/octeon/pci-octeon.h> 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci/* 238c2ecf20Sopenharmony_ci * Each bit in msi_free_irq_bitmask represents a MSI interrupt that is 248c2ecf20Sopenharmony_ci * in use. 258c2ecf20Sopenharmony_ci */ 268c2ecf20Sopenharmony_cistatic u64 msi_free_irq_bitmask[4]; 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci/* 298c2ecf20Sopenharmony_ci * Each bit in msi_multiple_irq_bitmask tells that the device using 308c2ecf20Sopenharmony_ci * this bit in msi_free_irq_bitmask is also using the next bit. This 318c2ecf20Sopenharmony_ci * is used so we can disable all of the MSI interrupts when a device 328c2ecf20Sopenharmony_ci * uses multiple. 338c2ecf20Sopenharmony_ci */ 348c2ecf20Sopenharmony_cistatic u64 msi_multiple_irq_bitmask[4]; 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci/* 378c2ecf20Sopenharmony_ci * This lock controls updates to msi_free_irq_bitmask and 388c2ecf20Sopenharmony_ci * msi_multiple_irq_bitmask. 398c2ecf20Sopenharmony_ci */ 408c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(msi_free_irq_bitmask_lock); 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci/* 438c2ecf20Sopenharmony_ci * Number of MSI IRQs used. This variable is set up in 448c2ecf20Sopenharmony_ci * the module init time. 458c2ecf20Sopenharmony_ci */ 468c2ecf20Sopenharmony_cistatic int msi_irq_size; 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci/** 498c2ecf20Sopenharmony_ci * Called when a driver request MSI interrupts instead of the 508c2ecf20Sopenharmony_ci * legacy INT A-D. This routine will allocate multiple interrupts 518c2ecf20Sopenharmony_ci * for MSI devices that support them. A device can override this by 528c2ecf20Sopenharmony_ci * programming the MSI control bits [6:4] before calling 538c2ecf20Sopenharmony_ci * pci_enable_msi(). 548c2ecf20Sopenharmony_ci * 558c2ecf20Sopenharmony_ci * @dev: Device requesting MSI interrupts 568c2ecf20Sopenharmony_ci * @desc: MSI descriptor 578c2ecf20Sopenharmony_ci * 588c2ecf20Sopenharmony_ci * Returns 0 on success. 598c2ecf20Sopenharmony_ci */ 608c2ecf20Sopenharmony_ciint arch_setup_msi_irq(struct pci_dev *dev, struct msi_desc *desc) 618c2ecf20Sopenharmony_ci{ 628c2ecf20Sopenharmony_ci struct msi_msg msg; 638c2ecf20Sopenharmony_ci u16 control; 648c2ecf20Sopenharmony_ci int configured_private_bits; 658c2ecf20Sopenharmony_ci int request_private_bits; 668c2ecf20Sopenharmony_ci int irq = 0; 678c2ecf20Sopenharmony_ci int irq_step; 688c2ecf20Sopenharmony_ci u64 search_mask; 698c2ecf20Sopenharmony_ci int index; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci /* 728c2ecf20Sopenharmony_ci * Read the MSI config to figure out how many IRQs this device 738c2ecf20Sopenharmony_ci * wants. Most devices only want 1, which will give 748c2ecf20Sopenharmony_ci * configured_private_bits and request_private_bits equal 0. 758c2ecf20Sopenharmony_ci */ 768c2ecf20Sopenharmony_ci pci_read_config_word(dev, dev->msi_cap + PCI_MSI_FLAGS, &control); 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci /* 798c2ecf20Sopenharmony_ci * If the number of private bits has been configured then use 808c2ecf20Sopenharmony_ci * that value instead of the requested number. This gives the 818c2ecf20Sopenharmony_ci * driver the chance to override the number of interrupts 828c2ecf20Sopenharmony_ci * before calling pci_enable_msi(). 838c2ecf20Sopenharmony_ci */ 848c2ecf20Sopenharmony_ci configured_private_bits = (control & PCI_MSI_FLAGS_QSIZE) >> 4; 858c2ecf20Sopenharmony_ci if (configured_private_bits == 0) { 868c2ecf20Sopenharmony_ci /* Nothing is configured, so use the hardware requested size */ 878c2ecf20Sopenharmony_ci request_private_bits = (control & PCI_MSI_FLAGS_QMASK) >> 1; 888c2ecf20Sopenharmony_ci } else { 898c2ecf20Sopenharmony_ci /* 908c2ecf20Sopenharmony_ci * Use the number of configured bits, assuming the 918c2ecf20Sopenharmony_ci * driver wanted to override the hardware request 928c2ecf20Sopenharmony_ci * value. 938c2ecf20Sopenharmony_ci */ 948c2ecf20Sopenharmony_ci request_private_bits = configured_private_bits; 958c2ecf20Sopenharmony_ci } 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci /* 988c2ecf20Sopenharmony_ci * The PCI 2.3 spec mandates that there are at most 32 998c2ecf20Sopenharmony_ci * interrupts. If this device asks for more, only give it one. 1008c2ecf20Sopenharmony_ci */ 1018c2ecf20Sopenharmony_ci if (request_private_bits > 5) 1028c2ecf20Sopenharmony_ci request_private_bits = 0; 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_citry_only_one: 1058c2ecf20Sopenharmony_ci /* 1068c2ecf20Sopenharmony_ci * The IRQs have to be aligned on a power of two based on the 1078c2ecf20Sopenharmony_ci * number being requested. 1088c2ecf20Sopenharmony_ci */ 1098c2ecf20Sopenharmony_ci irq_step = 1 << request_private_bits; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci /* Mask with one bit for each IRQ */ 1128c2ecf20Sopenharmony_ci search_mask = (1 << irq_step) - 1; 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci /* 1158c2ecf20Sopenharmony_ci * We're going to search msi_free_irq_bitmask_lock for zero 1168c2ecf20Sopenharmony_ci * bits. This represents an MSI interrupt number that isn't in 1178c2ecf20Sopenharmony_ci * use. 1188c2ecf20Sopenharmony_ci */ 1198c2ecf20Sopenharmony_ci spin_lock(&msi_free_irq_bitmask_lock); 1208c2ecf20Sopenharmony_ci for (index = 0; index < msi_irq_size/64; index++) { 1218c2ecf20Sopenharmony_ci for (irq = 0; irq < 64; irq += irq_step) { 1228c2ecf20Sopenharmony_ci if ((msi_free_irq_bitmask[index] & (search_mask << irq)) == 0) { 1238c2ecf20Sopenharmony_ci msi_free_irq_bitmask[index] |= search_mask << irq; 1248c2ecf20Sopenharmony_ci msi_multiple_irq_bitmask[index] |= (search_mask >> 1) << irq; 1258c2ecf20Sopenharmony_ci goto msi_irq_allocated; 1268c2ecf20Sopenharmony_ci } 1278c2ecf20Sopenharmony_ci } 1288c2ecf20Sopenharmony_ci } 1298c2ecf20Sopenharmony_cimsi_irq_allocated: 1308c2ecf20Sopenharmony_ci spin_unlock(&msi_free_irq_bitmask_lock); 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci /* Make sure the search for available interrupts didn't fail */ 1338c2ecf20Sopenharmony_ci if (irq >= 64) { 1348c2ecf20Sopenharmony_ci if (request_private_bits) { 1358c2ecf20Sopenharmony_ci pr_err("arch_setup_msi_irq: Unable to find %d free interrupts, trying just one", 1368c2ecf20Sopenharmony_ci 1 << request_private_bits); 1378c2ecf20Sopenharmony_ci request_private_bits = 0; 1388c2ecf20Sopenharmony_ci goto try_only_one; 1398c2ecf20Sopenharmony_ci } else 1408c2ecf20Sopenharmony_ci panic("arch_setup_msi_irq: Unable to find a free MSI interrupt"); 1418c2ecf20Sopenharmony_ci } 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci /* MSI interrupts start at logical IRQ OCTEON_IRQ_MSI_BIT0 */ 1448c2ecf20Sopenharmony_ci irq += index*64; 1458c2ecf20Sopenharmony_ci irq += OCTEON_IRQ_MSI_BIT0; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci switch (octeon_dma_bar_type) { 1488c2ecf20Sopenharmony_ci case OCTEON_DMA_BAR_TYPE_SMALL: 1498c2ecf20Sopenharmony_ci /* When not using big bar, Bar 0 is based at 128MB */ 1508c2ecf20Sopenharmony_ci msg.address_lo = 1518c2ecf20Sopenharmony_ci ((128ul << 20) + CVMX_PCI_MSI_RCV) & 0xffffffff; 1528c2ecf20Sopenharmony_ci msg.address_hi = ((128ul << 20) + CVMX_PCI_MSI_RCV) >> 32; 1538c2ecf20Sopenharmony_ci break; 1548c2ecf20Sopenharmony_ci case OCTEON_DMA_BAR_TYPE_BIG: 1558c2ecf20Sopenharmony_ci /* When using big bar, Bar 0 is based at 0 */ 1568c2ecf20Sopenharmony_ci msg.address_lo = (0 + CVMX_PCI_MSI_RCV) & 0xffffffff; 1578c2ecf20Sopenharmony_ci msg.address_hi = (0 + CVMX_PCI_MSI_RCV) >> 32; 1588c2ecf20Sopenharmony_ci break; 1598c2ecf20Sopenharmony_ci case OCTEON_DMA_BAR_TYPE_PCIE: 1608c2ecf20Sopenharmony_ci /* When using PCIe, Bar 0 is based at 0 */ 1618c2ecf20Sopenharmony_ci /* FIXME CVMX_NPEI_MSI_RCV* other than 0? */ 1628c2ecf20Sopenharmony_ci msg.address_lo = (0 + CVMX_NPEI_PCIE_MSI_RCV) & 0xffffffff; 1638c2ecf20Sopenharmony_ci msg.address_hi = (0 + CVMX_NPEI_PCIE_MSI_RCV) >> 32; 1648c2ecf20Sopenharmony_ci break; 1658c2ecf20Sopenharmony_ci case OCTEON_DMA_BAR_TYPE_PCIE2: 1668c2ecf20Sopenharmony_ci /* When using PCIe2, Bar 0 is based at 0 */ 1678c2ecf20Sopenharmony_ci msg.address_lo = (0 + CVMX_SLI_PCIE_MSI_RCV) & 0xffffffff; 1688c2ecf20Sopenharmony_ci msg.address_hi = (0 + CVMX_SLI_PCIE_MSI_RCV) >> 32; 1698c2ecf20Sopenharmony_ci break; 1708c2ecf20Sopenharmony_ci default: 1718c2ecf20Sopenharmony_ci panic("arch_setup_msi_irq: Invalid octeon_dma_bar_type"); 1728c2ecf20Sopenharmony_ci } 1738c2ecf20Sopenharmony_ci msg.data = irq - OCTEON_IRQ_MSI_BIT0; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci /* Update the number of IRQs the device has available to it */ 1768c2ecf20Sopenharmony_ci control &= ~PCI_MSI_FLAGS_QSIZE; 1778c2ecf20Sopenharmony_ci control |= request_private_bits << 4; 1788c2ecf20Sopenharmony_ci pci_write_config_word(dev, dev->msi_cap + PCI_MSI_FLAGS, control); 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci irq_set_msi_desc(irq, desc); 1818c2ecf20Sopenharmony_ci pci_write_msi_msg(irq, &msg); 1828c2ecf20Sopenharmony_ci return 0; 1838c2ecf20Sopenharmony_ci} 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ciint arch_setup_msi_irqs(struct pci_dev *dev, int nvec, int type) 1868c2ecf20Sopenharmony_ci{ 1878c2ecf20Sopenharmony_ci struct msi_desc *entry; 1888c2ecf20Sopenharmony_ci int ret; 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci /* 1918c2ecf20Sopenharmony_ci * MSI-X is not supported. 1928c2ecf20Sopenharmony_ci */ 1938c2ecf20Sopenharmony_ci if (type == PCI_CAP_ID_MSIX) 1948c2ecf20Sopenharmony_ci return -EINVAL; 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci /* 1978c2ecf20Sopenharmony_ci * If an architecture wants to support multiple MSI, it needs to 1988c2ecf20Sopenharmony_ci * override arch_setup_msi_irqs() 1998c2ecf20Sopenharmony_ci */ 2008c2ecf20Sopenharmony_ci if (type == PCI_CAP_ID_MSI && nvec > 1) 2018c2ecf20Sopenharmony_ci return 1; 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci for_each_pci_msi_entry(entry, dev) { 2048c2ecf20Sopenharmony_ci ret = arch_setup_msi_irq(dev, entry); 2058c2ecf20Sopenharmony_ci if (ret < 0) 2068c2ecf20Sopenharmony_ci return ret; 2078c2ecf20Sopenharmony_ci if (ret > 0) 2088c2ecf20Sopenharmony_ci return -ENOSPC; 2098c2ecf20Sopenharmony_ci } 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci return 0; 2128c2ecf20Sopenharmony_ci} 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci/** 2158c2ecf20Sopenharmony_ci * Called when a device no longer needs its MSI interrupts. All 2168c2ecf20Sopenharmony_ci * MSI interrupts for the device are freed. 2178c2ecf20Sopenharmony_ci * 2188c2ecf20Sopenharmony_ci * @irq: The devices first irq number. There may be multple in sequence. 2198c2ecf20Sopenharmony_ci */ 2208c2ecf20Sopenharmony_civoid arch_teardown_msi_irq(unsigned int irq) 2218c2ecf20Sopenharmony_ci{ 2228c2ecf20Sopenharmony_ci int number_irqs; 2238c2ecf20Sopenharmony_ci u64 bitmask; 2248c2ecf20Sopenharmony_ci int index = 0; 2258c2ecf20Sopenharmony_ci int irq0; 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci if ((irq < OCTEON_IRQ_MSI_BIT0) 2288c2ecf20Sopenharmony_ci || (irq > msi_irq_size + OCTEON_IRQ_MSI_BIT0)) 2298c2ecf20Sopenharmony_ci panic("arch_teardown_msi_irq: Attempted to teardown illegal " 2308c2ecf20Sopenharmony_ci "MSI interrupt (%d)", irq); 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci irq -= OCTEON_IRQ_MSI_BIT0; 2338c2ecf20Sopenharmony_ci index = irq / 64; 2348c2ecf20Sopenharmony_ci irq0 = irq % 64; 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci /* 2378c2ecf20Sopenharmony_ci * Count the number of IRQs we need to free by looking at the 2388c2ecf20Sopenharmony_ci * msi_multiple_irq_bitmask. Each bit set means that the next 2398c2ecf20Sopenharmony_ci * IRQ is also owned by this device. 2408c2ecf20Sopenharmony_ci */ 2418c2ecf20Sopenharmony_ci number_irqs = 0; 2428c2ecf20Sopenharmony_ci while ((irq0 + number_irqs < 64) && 2438c2ecf20Sopenharmony_ci (msi_multiple_irq_bitmask[index] 2448c2ecf20Sopenharmony_ci & (1ull << (irq0 + number_irqs)))) 2458c2ecf20Sopenharmony_ci number_irqs++; 2468c2ecf20Sopenharmony_ci number_irqs++; 2478c2ecf20Sopenharmony_ci /* Mask with one bit for each IRQ */ 2488c2ecf20Sopenharmony_ci bitmask = (1 << number_irqs) - 1; 2498c2ecf20Sopenharmony_ci /* Shift the mask to the correct bit location */ 2508c2ecf20Sopenharmony_ci bitmask <<= irq0; 2518c2ecf20Sopenharmony_ci if ((msi_free_irq_bitmask[index] & bitmask) != bitmask) 2528c2ecf20Sopenharmony_ci panic("arch_teardown_msi_irq: Attempted to teardown MSI " 2538c2ecf20Sopenharmony_ci "interrupt (%d) not in use", irq); 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci /* Checks are done, update the in use bitmask */ 2568c2ecf20Sopenharmony_ci spin_lock(&msi_free_irq_bitmask_lock); 2578c2ecf20Sopenharmony_ci msi_free_irq_bitmask[index] &= ~bitmask; 2588c2ecf20Sopenharmony_ci msi_multiple_irq_bitmask[index] &= ~bitmask; 2598c2ecf20Sopenharmony_ci spin_unlock(&msi_free_irq_bitmask_lock); 2608c2ecf20Sopenharmony_ci} 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_cistatic DEFINE_RAW_SPINLOCK(octeon_irq_msi_lock); 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_cistatic u64 msi_rcv_reg[4]; 2658c2ecf20Sopenharmony_cistatic u64 mis_ena_reg[4]; 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_cistatic void octeon_irq_msi_enable_pcie(struct irq_data *data) 2688c2ecf20Sopenharmony_ci{ 2698c2ecf20Sopenharmony_ci u64 en; 2708c2ecf20Sopenharmony_ci unsigned long flags; 2718c2ecf20Sopenharmony_ci int msi_number = data->irq - OCTEON_IRQ_MSI_BIT0; 2728c2ecf20Sopenharmony_ci int irq_index = msi_number >> 6; 2738c2ecf20Sopenharmony_ci int irq_bit = msi_number & 0x3f; 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci raw_spin_lock_irqsave(&octeon_irq_msi_lock, flags); 2768c2ecf20Sopenharmony_ci en = cvmx_read_csr(mis_ena_reg[irq_index]); 2778c2ecf20Sopenharmony_ci en |= 1ull << irq_bit; 2788c2ecf20Sopenharmony_ci cvmx_write_csr(mis_ena_reg[irq_index], en); 2798c2ecf20Sopenharmony_ci cvmx_read_csr(mis_ena_reg[irq_index]); 2808c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&octeon_irq_msi_lock, flags); 2818c2ecf20Sopenharmony_ci} 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_cistatic void octeon_irq_msi_disable_pcie(struct irq_data *data) 2848c2ecf20Sopenharmony_ci{ 2858c2ecf20Sopenharmony_ci u64 en; 2868c2ecf20Sopenharmony_ci unsigned long flags; 2878c2ecf20Sopenharmony_ci int msi_number = data->irq - OCTEON_IRQ_MSI_BIT0; 2888c2ecf20Sopenharmony_ci int irq_index = msi_number >> 6; 2898c2ecf20Sopenharmony_ci int irq_bit = msi_number & 0x3f; 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci raw_spin_lock_irqsave(&octeon_irq_msi_lock, flags); 2928c2ecf20Sopenharmony_ci en = cvmx_read_csr(mis_ena_reg[irq_index]); 2938c2ecf20Sopenharmony_ci en &= ~(1ull << irq_bit); 2948c2ecf20Sopenharmony_ci cvmx_write_csr(mis_ena_reg[irq_index], en); 2958c2ecf20Sopenharmony_ci cvmx_read_csr(mis_ena_reg[irq_index]); 2968c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&octeon_irq_msi_lock, flags); 2978c2ecf20Sopenharmony_ci} 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_cistatic struct irq_chip octeon_irq_chip_msi_pcie = { 3008c2ecf20Sopenharmony_ci .name = "MSI", 3018c2ecf20Sopenharmony_ci .irq_enable = octeon_irq_msi_enable_pcie, 3028c2ecf20Sopenharmony_ci .irq_disable = octeon_irq_msi_disable_pcie, 3038c2ecf20Sopenharmony_ci}; 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_cistatic void octeon_irq_msi_enable_pci(struct irq_data *data) 3068c2ecf20Sopenharmony_ci{ 3078c2ecf20Sopenharmony_ci /* 3088c2ecf20Sopenharmony_ci * Octeon PCI doesn't have the ability to mask/unmask MSI 3098c2ecf20Sopenharmony_ci * interrupts individually. Instead of masking/unmasking them 3108c2ecf20Sopenharmony_ci * in groups of 16, we simple assume MSI devices are well 3118c2ecf20Sopenharmony_ci * behaved. MSI interrupts are always enable and the ACK is 3128c2ecf20Sopenharmony_ci * assumed to be enough 3138c2ecf20Sopenharmony_ci */ 3148c2ecf20Sopenharmony_ci} 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_cistatic void octeon_irq_msi_disable_pci(struct irq_data *data) 3178c2ecf20Sopenharmony_ci{ 3188c2ecf20Sopenharmony_ci /* See comment in enable */ 3198c2ecf20Sopenharmony_ci} 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_cistatic struct irq_chip octeon_irq_chip_msi_pci = { 3228c2ecf20Sopenharmony_ci .name = "MSI", 3238c2ecf20Sopenharmony_ci .irq_enable = octeon_irq_msi_enable_pci, 3248c2ecf20Sopenharmony_ci .irq_disable = octeon_irq_msi_disable_pci, 3258c2ecf20Sopenharmony_ci}; 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci/* 3288c2ecf20Sopenharmony_ci * Called by the interrupt handling code when an MSI interrupt 3298c2ecf20Sopenharmony_ci * occurs. 3308c2ecf20Sopenharmony_ci */ 3318c2ecf20Sopenharmony_cistatic irqreturn_t __octeon_msi_do_interrupt(int index, u64 msi_bits) 3328c2ecf20Sopenharmony_ci{ 3338c2ecf20Sopenharmony_ci int irq; 3348c2ecf20Sopenharmony_ci int bit; 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci bit = fls64(msi_bits); 3378c2ecf20Sopenharmony_ci if (bit) { 3388c2ecf20Sopenharmony_ci bit--; 3398c2ecf20Sopenharmony_ci /* Acknowledge it first. */ 3408c2ecf20Sopenharmony_ci cvmx_write_csr(msi_rcv_reg[index], 1ull << bit); 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci irq = bit + OCTEON_IRQ_MSI_BIT0 + 64 * index; 3438c2ecf20Sopenharmony_ci do_IRQ(irq); 3448c2ecf20Sopenharmony_ci return IRQ_HANDLED; 3458c2ecf20Sopenharmony_ci } 3468c2ecf20Sopenharmony_ci return IRQ_NONE; 3478c2ecf20Sopenharmony_ci} 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci#define OCTEON_MSI_INT_HANDLER_X(x) \ 3508c2ecf20Sopenharmony_cistatic irqreturn_t octeon_msi_interrupt##x(int cpl, void *dev_id) \ 3518c2ecf20Sopenharmony_ci{ \ 3528c2ecf20Sopenharmony_ci u64 msi_bits = cvmx_read_csr(msi_rcv_reg[(x)]); \ 3538c2ecf20Sopenharmony_ci return __octeon_msi_do_interrupt((x), msi_bits); \ 3548c2ecf20Sopenharmony_ci} 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci/* 3578c2ecf20Sopenharmony_ci * Create octeon_msi_interrupt{0-3} function body 3588c2ecf20Sopenharmony_ci */ 3598c2ecf20Sopenharmony_ciOCTEON_MSI_INT_HANDLER_X(0); 3608c2ecf20Sopenharmony_ciOCTEON_MSI_INT_HANDLER_X(1); 3618c2ecf20Sopenharmony_ciOCTEON_MSI_INT_HANDLER_X(2); 3628c2ecf20Sopenharmony_ciOCTEON_MSI_INT_HANDLER_X(3); 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci/* 3658c2ecf20Sopenharmony_ci * Initializes the MSI interrupt handling code 3668c2ecf20Sopenharmony_ci */ 3678c2ecf20Sopenharmony_ciint __init octeon_msi_initialize(void) 3688c2ecf20Sopenharmony_ci{ 3698c2ecf20Sopenharmony_ci int irq; 3708c2ecf20Sopenharmony_ci struct irq_chip *msi; 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci if (octeon_dma_bar_type == OCTEON_DMA_BAR_TYPE_INVALID) { 3738c2ecf20Sopenharmony_ci return 0; 3748c2ecf20Sopenharmony_ci } else if (octeon_dma_bar_type == OCTEON_DMA_BAR_TYPE_PCIE) { 3758c2ecf20Sopenharmony_ci msi_rcv_reg[0] = CVMX_PEXP_NPEI_MSI_RCV0; 3768c2ecf20Sopenharmony_ci msi_rcv_reg[1] = CVMX_PEXP_NPEI_MSI_RCV1; 3778c2ecf20Sopenharmony_ci msi_rcv_reg[2] = CVMX_PEXP_NPEI_MSI_RCV2; 3788c2ecf20Sopenharmony_ci msi_rcv_reg[3] = CVMX_PEXP_NPEI_MSI_RCV3; 3798c2ecf20Sopenharmony_ci mis_ena_reg[0] = CVMX_PEXP_NPEI_MSI_ENB0; 3808c2ecf20Sopenharmony_ci mis_ena_reg[1] = CVMX_PEXP_NPEI_MSI_ENB1; 3818c2ecf20Sopenharmony_ci mis_ena_reg[2] = CVMX_PEXP_NPEI_MSI_ENB2; 3828c2ecf20Sopenharmony_ci mis_ena_reg[3] = CVMX_PEXP_NPEI_MSI_ENB3; 3838c2ecf20Sopenharmony_ci msi = &octeon_irq_chip_msi_pcie; 3848c2ecf20Sopenharmony_ci } else { 3858c2ecf20Sopenharmony_ci msi_rcv_reg[0] = CVMX_NPI_NPI_MSI_RCV; 3868c2ecf20Sopenharmony_ci#define INVALID_GENERATE_ADE 0x8700000000000000ULL; 3878c2ecf20Sopenharmony_ci msi_rcv_reg[1] = INVALID_GENERATE_ADE; 3888c2ecf20Sopenharmony_ci msi_rcv_reg[2] = INVALID_GENERATE_ADE; 3898c2ecf20Sopenharmony_ci msi_rcv_reg[3] = INVALID_GENERATE_ADE; 3908c2ecf20Sopenharmony_ci mis_ena_reg[0] = INVALID_GENERATE_ADE; 3918c2ecf20Sopenharmony_ci mis_ena_reg[1] = INVALID_GENERATE_ADE; 3928c2ecf20Sopenharmony_ci mis_ena_reg[2] = INVALID_GENERATE_ADE; 3938c2ecf20Sopenharmony_ci mis_ena_reg[3] = INVALID_GENERATE_ADE; 3948c2ecf20Sopenharmony_ci msi = &octeon_irq_chip_msi_pci; 3958c2ecf20Sopenharmony_ci } 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci for (irq = OCTEON_IRQ_MSI_BIT0; irq <= OCTEON_IRQ_MSI_LAST; irq++) 3988c2ecf20Sopenharmony_ci irq_set_chip_and_handler(irq, msi, handle_simple_irq); 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci if (octeon_has_feature(OCTEON_FEATURE_PCIE)) { 4018c2ecf20Sopenharmony_ci if (request_irq(OCTEON_IRQ_PCI_MSI0, octeon_msi_interrupt0, 4028c2ecf20Sopenharmony_ci 0, "MSI[0:63]", octeon_msi_interrupt0)) 4038c2ecf20Sopenharmony_ci panic("request_irq(OCTEON_IRQ_PCI_MSI0) failed"); 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci if (request_irq(OCTEON_IRQ_PCI_MSI1, octeon_msi_interrupt1, 4068c2ecf20Sopenharmony_ci 0, "MSI[64:127]", octeon_msi_interrupt1)) 4078c2ecf20Sopenharmony_ci panic("request_irq(OCTEON_IRQ_PCI_MSI1) failed"); 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci if (request_irq(OCTEON_IRQ_PCI_MSI2, octeon_msi_interrupt2, 4108c2ecf20Sopenharmony_ci 0, "MSI[127:191]", octeon_msi_interrupt2)) 4118c2ecf20Sopenharmony_ci panic("request_irq(OCTEON_IRQ_PCI_MSI2) failed"); 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci if (request_irq(OCTEON_IRQ_PCI_MSI3, octeon_msi_interrupt3, 4148c2ecf20Sopenharmony_ci 0, "MSI[192:255]", octeon_msi_interrupt3)) 4158c2ecf20Sopenharmony_ci panic("request_irq(OCTEON_IRQ_PCI_MSI3) failed"); 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci msi_irq_size = 256; 4188c2ecf20Sopenharmony_ci } else if (octeon_is_pci_host()) { 4198c2ecf20Sopenharmony_ci if (request_irq(OCTEON_IRQ_PCI_MSI0, octeon_msi_interrupt0, 4208c2ecf20Sopenharmony_ci 0, "MSI[0:15]", octeon_msi_interrupt0)) 4218c2ecf20Sopenharmony_ci panic("request_irq(OCTEON_IRQ_PCI_MSI0) failed"); 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci if (request_irq(OCTEON_IRQ_PCI_MSI1, octeon_msi_interrupt0, 4248c2ecf20Sopenharmony_ci 0, "MSI[16:31]", octeon_msi_interrupt0)) 4258c2ecf20Sopenharmony_ci panic("request_irq(OCTEON_IRQ_PCI_MSI1) failed"); 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci if (request_irq(OCTEON_IRQ_PCI_MSI2, octeon_msi_interrupt0, 4288c2ecf20Sopenharmony_ci 0, "MSI[32:47]", octeon_msi_interrupt0)) 4298c2ecf20Sopenharmony_ci panic("request_irq(OCTEON_IRQ_PCI_MSI2) failed"); 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci if (request_irq(OCTEON_IRQ_PCI_MSI3, octeon_msi_interrupt0, 4328c2ecf20Sopenharmony_ci 0, "MSI[48:63]", octeon_msi_interrupt0)) 4338c2ecf20Sopenharmony_ci panic("request_irq(OCTEON_IRQ_PCI_MSI3) failed"); 4348c2ecf20Sopenharmony_ci msi_irq_size = 64; 4358c2ecf20Sopenharmony_ci } 4368c2ecf20Sopenharmony_ci return 0; 4378c2ecf20Sopenharmony_ci} 4388c2ecf20Sopenharmony_cisubsys_initcall(octeon_msi_initialize); 439