Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

irqchip: armada-370-xp: implement MSI support

This commit introduces the support for the MSI interrupts in the
armada-370-xp interrupt controller driver. It registers an MSI chip to
the MSI chip registry, which will be used by the Marvell PCIe host
controller driver.

The MSI interrupts use the 16 high doorbells, and are therefore
notified using IRQ1 of the main interrupt controller.

Signed-off-by: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
Acked-by: Gregory CLEMENT <gregory.clement@free-electrons.com>
Signed-off-by: Jason Cooper <jason@lakedaemon.net>

authored by

Thomas Petazzoni and committed by
Jason Cooper
31f614ed 627dfcc2

+184 -1
+3
Documentation/devicetree/bindings/arm/armada-370-xp-mpic.txt
··· 4 4 Required properties: 5 5 - compatible: Should be "marvell,mpic" 6 6 - interrupt-controller: Identifies the node as an interrupt controller. 7 + - msi-controller: Identifies the node as an PCI Message Signaled 8 + Interrupt controller. 7 9 - #interrupt-cells: The number of cells to define the interrupts. Should be 1. 8 10 The cell is the IRQ number 9 11 ··· 26 24 #address-cells = <1>; 27 25 #size-cells = <1>; 28 26 interrupt-controller; 27 + msi-controller; 29 28 reg = <0xd0020a00 0x1d0>, 30 29 <0xd0021070 0x58>; 31 30 };
+181 -1
drivers/irqchip/irq-armada-370-xp.c
··· 21 21 #include <linux/io.h> 22 22 #include <linux/of_address.h> 23 23 #include <linux/of_irq.h> 24 + #include <linux/of_pci.h> 24 25 #include <linux/irqdomain.h> 26 + #include <linux/slab.h> 27 + #include <linux/msi.h> 25 28 #include <asm/mach/arch.h> 26 29 #include <asm/exception.h> 27 30 #include <asm/smp_plat.h> ··· 54 51 #define IPI_DOORBELL_START (0) 55 52 #define IPI_DOORBELL_END (8) 56 53 #define IPI_DOORBELL_MASK 0xFF 54 + #define PCI_MSI_DOORBELL_START (16) 55 + #define PCI_MSI_DOORBELL_NR (16) 56 + #define PCI_MSI_DOORBELL_END (32) 57 + #define PCI_MSI_DOORBELL_MASK 0xFFFF0000 57 58 58 59 static DEFINE_RAW_SPINLOCK(irq_controller_lock); 59 60 60 61 static void __iomem *per_cpu_int_base; 61 62 static void __iomem *main_int_base; 62 63 static struct irq_domain *armada_370_xp_mpic_domain; 64 + #ifdef CONFIG_PCI_MSI 65 + static struct irq_domain *armada_370_xp_msi_domain; 66 + static DECLARE_BITMAP(msi_used, PCI_MSI_DOORBELL_NR); 67 + static DEFINE_MUTEX(msi_used_lock); 68 + static phys_addr_t msi_doorbell_addr; 69 + #endif 63 70 64 71 /* 65 72 * In SMP mode: ··· 99 86 writel(hwirq, per_cpu_int_base + 100 87 ARMADA_370_XP_INT_CLEAR_MASK_OFFS); 101 88 } 89 + 90 + #ifdef CONFIG_PCI_MSI 91 + 92 + static int armada_370_xp_alloc_msi(void) 93 + { 94 + int hwirq; 95 + 96 + mutex_lock(&msi_used_lock); 97 + hwirq = find_first_zero_bit(&msi_used, PCI_MSI_DOORBELL_NR); 98 + if (hwirq >= PCI_MSI_DOORBELL_NR) 99 + hwirq = -ENOSPC; 100 + else 101 + set_bit(hwirq, msi_used); 102 + mutex_unlock(&msi_used_lock); 103 + 104 + return hwirq; 105 + } 106 + 107 + static void armada_370_xp_free_msi(int hwirq) 108 + { 109 + mutex_lock(&msi_used_lock); 110 + if (!test_bit(hwirq, msi_used)) 111 + pr_err("trying to free unused MSI#%d\n", hwirq); 112 + else 113 + clear_bit(hwirq, msi_used); 114 + mutex_unlock(&msi_used_lock); 115 + } 116 + 117 + static int armada_370_xp_setup_msi_irq(struct msi_chip *chip, 118 + struct pci_dev *pdev, 119 + struct msi_desc *desc) 120 + { 121 + struct msi_msg msg; 122 + irq_hw_number_t hwirq; 123 + int virq; 124 + 125 + hwirq = armada_370_xp_alloc_msi(); 126 + if (hwirq < 0) 127 + return hwirq; 128 + 129 + virq = irq_create_mapping(armada_370_xp_msi_domain, hwirq); 130 + if (!virq) { 131 + armada_370_xp_free_msi(hwirq); 132 + return -EINVAL; 133 + } 134 + 135 + irq_set_msi_desc(virq, desc); 136 + 137 + msg.address_lo = msi_doorbell_addr; 138 + msg.address_hi = 0; 139 + msg.data = 0xf00 | (hwirq + 16); 140 + 141 + write_msi_msg(virq, &msg); 142 + return 0; 143 + } 144 + 145 + static void armada_370_xp_teardown_msi_irq(struct msi_chip *chip, 146 + unsigned int irq) 147 + { 148 + struct irq_data *d = irq_get_irq_data(irq); 149 + irq_dispose_mapping(irq); 150 + armada_370_xp_free_msi(d->hwirq); 151 + } 152 + 153 + static struct irq_chip armada_370_xp_msi_irq_chip = { 154 + .name = "armada_370_xp_msi_irq", 155 + .irq_enable = unmask_msi_irq, 156 + .irq_disable = mask_msi_irq, 157 + .irq_mask = mask_msi_irq, 158 + .irq_unmask = unmask_msi_irq, 159 + }; 160 + 161 + static int armada_370_xp_msi_map(struct irq_domain *domain, unsigned int virq, 162 + irq_hw_number_t hw) 163 + { 164 + irq_set_chip_and_handler(virq, &armada_370_xp_msi_irq_chip, 165 + handle_simple_irq); 166 + set_irq_flags(virq, IRQF_VALID); 167 + 168 + return 0; 169 + } 170 + 171 + static const struct irq_domain_ops armada_370_xp_msi_irq_ops = { 172 + .map = armada_370_xp_msi_map, 173 + }; 174 + 175 + static int armada_370_xp_msi_init(struct device_node *node, 176 + phys_addr_t main_int_phys_base) 177 + { 178 + struct msi_chip *msi_chip; 179 + u32 reg; 180 + int ret; 181 + 182 + msi_doorbell_addr = main_int_phys_base + 183 + ARMADA_370_XP_SW_TRIG_INT_OFFS; 184 + 185 + msi_chip = kzalloc(sizeof(*msi_chip), GFP_KERNEL); 186 + if (!msi_chip) 187 + return -ENOMEM; 188 + 189 + msi_chip->setup_irq = armada_370_xp_setup_msi_irq; 190 + msi_chip->teardown_irq = armada_370_xp_teardown_msi_irq; 191 + msi_chip->of_node = node; 192 + 193 + armada_370_xp_msi_domain = 194 + irq_domain_add_linear(NULL, PCI_MSI_DOORBELL_NR, 195 + &armada_370_xp_msi_irq_ops, 196 + NULL); 197 + if (!armada_370_xp_msi_domain) { 198 + kfree(msi_chip); 199 + return -ENOMEM; 200 + } 201 + 202 + ret = of_pci_msi_chip_add(msi_chip); 203 + if (ret < 0) { 204 + irq_domain_remove(armada_370_xp_msi_domain); 205 + kfree(msi_chip); 206 + return ret; 207 + } 208 + 209 + reg = readl(per_cpu_int_base + ARMADA_370_XP_IN_DRBEL_MSK_OFFS) 210 + | PCI_MSI_DOORBELL_MASK; 211 + 212 + writel(reg, per_cpu_int_base + 213 + ARMADA_370_XP_IN_DRBEL_MSK_OFFS); 214 + 215 + /* Unmask IPI interrupt */ 216 + writel(1, per_cpu_int_base + ARMADA_370_XP_INT_CLEAR_MASK_OFFS); 217 + 218 + return 0; 219 + } 220 + #else 221 + static inline int armada_370_xp_msi_init(struct device_node *node, 222 + phys_addr_t main_int_phys_base) 223 + { 224 + return 0; 225 + } 226 + #endif 102 227 103 228 #ifdef CONFIG_SMP 104 229 static int armada_xp_set_affinity(struct irq_data *d, ··· 365 214 if (irqnr > 1022) 366 215 break; 367 216 368 - if (irqnr > 0) { 217 + if (irqnr > 1) { 369 218 irqnr = irq_find_mapping(armada_370_xp_mpic_domain, 370 219 irqnr); 371 220 handle_IRQ(irqnr, regs); 372 221 continue; 373 222 } 223 + 224 + #ifdef CONFIG_PCI_MSI 225 + /* MSI handling */ 226 + if (irqnr == 1) { 227 + u32 msimask, msinr; 228 + 229 + msimask = readl_relaxed(per_cpu_int_base + 230 + ARMADA_370_XP_IN_DRBEL_CAUSE_OFFS) 231 + & PCI_MSI_DOORBELL_MASK; 232 + 233 + writel(~PCI_MSI_DOORBELL_MASK, per_cpu_int_base + 234 + ARMADA_370_XP_IN_DRBEL_CAUSE_OFFS); 235 + 236 + for (msinr = PCI_MSI_DOORBELL_START; 237 + msinr < PCI_MSI_DOORBELL_END; msinr++) { 238 + int irq; 239 + 240 + if (!(msimask & BIT(msinr))) 241 + continue; 242 + 243 + irq = irq_find_mapping(armada_370_xp_msi_domain, 244 + msinr - 16); 245 + handle_IRQ(irq, regs); 246 + } 247 + } 248 + #endif 249 + 374 250 #ifdef CONFIG_SMP 375 251 /* IPI Handling */ 376 252 if (irqnr == 0) { ··· 469 291 cpumask_set_cpu(smp_processor_id(), irq_default_affinity); 470 292 471 293 #endif 294 + 295 + armada_370_xp_msi_init(node, main_int_res.start); 472 296 473 297 set_handle_irq(armada_370_xp_handle_irq); 474 298