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

IRQCHIP: Add new driver for BCM7038-style level 1 interrupt controllers

This is the main peripheral IRQ controller on the BCM7xxx MIPS chips;
it has the following characteristics:

- 64 to 160+ level IRQs
- Atomic set/clear registers
- Reasonably predictable register layout (N status words, then N
mask status words, then N mask set words, then N mask clear words)
- SMP affinity supported on most systems
- Typically connected to MIPS IRQ 2,3,2,3 on CPUs 0,1,2,3

This driver registers one IRQ domain and one IRQ chip to cover all
instances of the block. Up to 4 instances of the block may appear, as
it supports 4-way IRQ affinity on BCM7435.

The same block exists on the ARM BCM7xxx chips, but typically the ARM GIC
is used instead. So this driver is primarily intended for MIPS STB chips.

Signed-off-by: Kevin Cernekee <cernekee@gmail.com>
Cc: f.fainelli@gmail.com
Cc: jaedon.shin@gmail.com
Cc: abrestic@chromium.org
Cc: tglx@linutronix.de
Cc: jason@lakedaemon.net
Cc: jogo@openwrt.org
Cc: arnd@arndb.de
Cc: computersforpeace@gmail.com
Cc: linux-mips@linux-mips.org
Cc: devicetree@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
Patchwork: https://patchwork.linux-mips.org/patch/8844/
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>

authored by

Kevin Cernekee and committed by
Ralf Baechle
5f7f0317 7b7230e7

+393
+52
Documentation/devicetree/bindings/interrupt-controller/brcm,bcm7038-l1-intc.txt
··· 1 + Broadcom BCM7038-style Level 1 interrupt controller 2 + 3 + This block is a first level interrupt controller that is typically connected 4 + directly to one of the HW INT lines on each CPU. Every BCM7xxx set-top chip 5 + since BCM7038 has contained this hardware. 6 + 7 + Key elements of the hardware design include: 8 + 9 + - 64, 96, 128, or 160 incoming level IRQ lines 10 + 11 + - Most onchip peripherals are wired directly to an L1 input 12 + 13 + - A separate instance of the register set for each CPU, allowing individual 14 + peripheral IRQs to be routed to any CPU 15 + 16 + - Atomic mask/unmask operations 17 + 18 + - No polarity/level/edge settings 19 + 20 + - No FIFO or priority encoder logic; software is expected to read all 21 + 2-5 status words to determine which IRQs are pending 22 + 23 + Required properties: 24 + 25 + - compatible: should be "brcm,bcm7038-l1-intc" 26 + - reg: specifies the base physical address and size of the registers; 27 + the number of supported IRQs is inferred from the size argument 28 + - interrupt-controller: identifies the node as an interrupt controller 29 + - #interrupt-cells: specifies the number of cells needed to encode an interrupt 30 + source, should be 1. 31 + - interrupt-parent: specifies the phandle to the parent interrupt controller(s) 32 + this one is cascaded from 33 + - interrupts: specifies the interrupt line(s) in the interrupt-parent controller 34 + node; valid values depend on the type of parent interrupt controller 35 + 36 + If multiple reg ranges and interrupt-parent entries are present on an SMP 37 + system, the driver will allow IRQ SMP affinity to be set up through the 38 + /proc/irq/ interface. In the simplest possible configuration, only one 39 + reg range and one interrupt-parent is needed. 40 + 41 + Example: 42 + 43 + periph_intc: periph_intc@1041a400 { 44 + compatible = "brcm,bcm7038-l1-intc"; 45 + reg = <0x1041a400 0x30 0x1041a600 0x30>; 46 + 47 + interrupt-controller; 48 + #interrupt-cells = <1>; 49 + 50 + interrupt-parent = <&cpu_intc>; 51 + interrupts = <2>, <3>; 52 + };
+5
drivers/irqchip/Kconfig
··· 60 60 select MULTI_IRQ_HANDLER 61 61 select SPARSE_IRQ 62 62 63 + config BCM7038_L1_IRQ 64 + bool 65 + select GENERIC_IRQ_CHIP 66 + select IRQ_DOMAIN 67 + 63 68 config BCM7120_L2_IRQ 64 69 bool 65 70 select GENERIC_IRQ_CHIP
+1
drivers/irqchip/Makefile
··· 37 37 obj-$(CONFIG_XTENSA) += irq-xtensa-pic.o 38 38 obj-$(CONFIG_XTENSA_MX) += irq-xtensa-mx.o 39 39 obj-$(CONFIG_IRQ_CROSSBAR) += irq-crossbar.o 40 + obj-$(CONFIG_BCM7038_L1_IRQ) += irq-bcm7038-l1.o 40 41 obj-$(CONFIG_BCM7120_L2_IRQ) += irq-bcm7120-l2.o 41 42 obj-$(CONFIG_BRCMSTB_L2_IRQ) += irq-brcmstb-l2.o 42 43 obj-$(CONFIG_KEYSTONE_IRQ) += irq-keystone.o
+335
drivers/irqchip/irq-bcm7038-l1.c
··· 1 + /* 2 + * Broadcom BCM7038 style Level 1 interrupt controller driver 3 + * 4 + * Copyright (C) 2014 Broadcom Corporation 5 + * Author: Kevin Cernekee 6 + * 7 + * This program is free software; you can redistribute it and/or modify 8 + * it under the terms of the GNU General Public License version 2 as 9 + * published by the Free Software Foundation. 10 + */ 11 + 12 + #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 13 + 14 + #include <linux/bitops.h> 15 + #include <linux/kconfig.h> 16 + #include <linux/kernel.h> 17 + #include <linux/init.h> 18 + #include <linux/interrupt.h> 19 + #include <linux/io.h> 20 + #include <linux/ioport.h> 21 + #include <linux/irq.h> 22 + #include <linux/irqdomain.h> 23 + #include <linux/module.h> 24 + #include <linux/of.h> 25 + #include <linux/of_irq.h> 26 + #include <linux/of_address.h> 27 + #include <linux/of_platform.h> 28 + #include <linux/platform_device.h> 29 + #include <linux/slab.h> 30 + #include <linux/smp.h> 31 + #include <linux/types.h> 32 + #include <linux/irqchip/chained_irq.h> 33 + 34 + #include "irqchip.h" 35 + 36 + #define IRQS_PER_WORD 32 37 + #define REG_BYTES_PER_IRQ_WORD (sizeof(u32) * 4) 38 + #define MAX_WORDS 8 39 + 40 + struct bcm7038_l1_cpu; 41 + 42 + struct bcm7038_l1_chip { 43 + raw_spinlock_t lock; 44 + unsigned int n_words; 45 + struct irq_domain *domain; 46 + struct bcm7038_l1_cpu *cpus[NR_CPUS]; 47 + u8 affinity[MAX_WORDS * IRQS_PER_WORD]; 48 + }; 49 + 50 + struct bcm7038_l1_cpu { 51 + void __iomem *map_base; 52 + u32 mask_cache[0]; 53 + }; 54 + 55 + /* 56 + * STATUS/MASK_STATUS/MASK_SET/MASK_CLEAR are packed one right after another: 57 + * 58 + * 7038: 59 + * 0x1000_1400: W0_STATUS 60 + * 0x1000_1404: W1_STATUS 61 + * 0x1000_1408: W0_MASK_STATUS 62 + * 0x1000_140c: W1_MASK_STATUS 63 + * 0x1000_1410: W0_MASK_SET 64 + * 0x1000_1414: W1_MASK_SET 65 + * 0x1000_1418: W0_MASK_CLEAR 66 + * 0x1000_141c: W1_MASK_CLEAR 67 + * 68 + * 7445: 69 + * 0xf03e_1500: W0_STATUS 70 + * 0xf03e_1504: W1_STATUS 71 + * 0xf03e_1508: W2_STATUS 72 + * 0xf03e_150c: W3_STATUS 73 + * 0xf03e_1510: W4_STATUS 74 + * 0xf03e_1514: W0_MASK_STATUS 75 + * 0xf03e_1518: W1_MASK_STATUS 76 + * [...] 77 + */ 78 + 79 + static inline unsigned int reg_status(struct bcm7038_l1_chip *intc, 80 + unsigned int word) 81 + { 82 + return (0 * intc->n_words + word) * sizeof(u32); 83 + } 84 + 85 + static inline unsigned int reg_mask_status(struct bcm7038_l1_chip *intc, 86 + unsigned int word) 87 + { 88 + return (1 * intc->n_words + word) * sizeof(u32); 89 + } 90 + 91 + static inline unsigned int reg_mask_set(struct bcm7038_l1_chip *intc, 92 + unsigned int word) 93 + { 94 + return (2 * intc->n_words + word) * sizeof(u32); 95 + } 96 + 97 + static inline unsigned int reg_mask_clr(struct bcm7038_l1_chip *intc, 98 + unsigned int word) 99 + { 100 + return (3 * intc->n_words + word) * sizeof(u32); 101 + } 102 + 103 + static inline u32 l1_readl(void __iomem *reg) 104 + { 105 + if (IS_ENABLED(CONFIG_MIPS) && IS_ENABLED(CONFIG_CPU_BIG_ENDIAN)) 106 + return ioread32be(reg); 107 + else 108 + return readl(reg); 109 + } 110 + 111 + static inline void l1_writel(u32 val, void __iomem *reg) 112 + { 113 + if (IS_ENABLED(CONFIG_MIPS) && IS_ENABLED(CONFIG_CPU_BIG_ENDIAN)) 114 + iowrite32be(val, reg); 115 + else 116 + writel(val, reg); 117 + } 118 + 119 + static void bcm7038_l1_irq_handle(unsigned int irq, struct irq_desc *desc) 120 + { 121 + struct bcm7038_l1_chip *intc = irq_desc_get_handler_data(desc); 122 + struct bcm7038_l1_cpu *cpu; 123 + struct irq_chip *chip = irq_desc_get_chip(desc); 124 + unsigned int idx; 125 + 126 + #ifdef CONFIG_SMP 127 + cpu = intc->cpus[cpu_logical_map(smp_processor_id())]; 128 + #else 129 + cpu = intc->cpus[0]; 130 + #endif 131 + 132 + chained_irq_enter(chip, desc); 133 + 134 + for (idx = 0; idx < intc->n_words; idx++) { 135 + int base = idx * IRQS_PER_WORD; 136 + unsigned long pending, flags; 137 + int hwirq; 138 + 139 + raw_spin_lock_irqsave(&intc->lock, flags); 140 + pending = l1_readl(cpu->map_base + reg_status(intc, idx)) & 141 + ~cpu->mask_cache[idx]; 142 + raw_spin_unlock_irqrestore(&intc->lock, flags); 143 + 144 + for_each_set_bit(hwirq, &pending, IRQS_PER_WORD) { 145 + generic_handle_irq(irq_find_mapping(intc->domain, 146 + base + hwirq)); 147 + } 148 + } 149 + 150 + chained_irq_exit(chip, desc); 151 + } 152 + 153 + static void __bcm7038_l1_unmask(struct irq_data *d, unsigned int cpu_idx) 154 + { 155 + struct bcm7038_l1_chip *intc = irq_data_get_irq_chip_data(d); 156 + u32 word = d->hwirq / IRQS_PER_WORD; 157 + u32 mask = BIT(d->hwirq % IRQS_PER_WORD); 158 + 159 + intc->cpus[cpu_idx]->mask_cache[word] &= ~mask; 160 + l1_writel(mask, intc->cpus[cpu_idx]->map_base + 161 + reg_mask_clr(intc, word)); 162 + } 163 + 164 + static void __bcm7038_l1_mask(struct irq_data *d, unsigned int cpu_idx) 165 + { 166 + struct bcm7038_l1_chip *intc = irq_data_get_irq_chip_data(d); 167 + u32 word = d->hwirq / IRQS_PER_WORD; 168 + u32 mask = BIT(d->hwirq % IRQS_PER_WORD); 169 + 170 + intc->cpus[cpu_idx]->mask_cache[word] |= mask; 171 + l1_writel(mask, intc->cpus[cpu_idx]->map_base + 172 + reg_mask_set(intc, word)); 173 + } 174 + 175 + static void bcm7038_l1_unmask(struct irq_data *d) 176 + { 177 + struct bcm7038_l1_chip *intc = irq_data_get_irq_chip_data(d); 178 + unsigned long flags; 179 + 180 + raw_spin_lock_irqsave(&intc->lock, flags); 181 + __bcm7038_l1_unmask(d, intc->affinity[d->hwirq]); 182 + raw_spin_unlock_irqrestore(&intc->lock, flags); 183 + } 184 + 185 + static void bcm7038_l1_mask(struct irq_data *d) 186 + { 187 + struct bcm7038_l1_chip *intc = irq_data_get_irq_chip_data(d); 188 + unsigned long flags; 189 + 190 + raw_spin_lock_irqsave(&intc->lock, flags); 191 + __bcm7038_l1_mask(d, intc->affinity[d->hwirq]); 192 + raw_spin_unlock_irqrestore(&intc->lock, flags); 193 + } 194 + 195 + static int bcm7038_l1_set_affinity(struct irq_data *d, 196 + const struct cpumask *dest, 197 + bool force) 198 + { 199 + struct bcm7038_l1_chip *intc = irq_data_get_irq_chip_data(d); 200 + unsigned long flags; 201 + irq_hw_number_t hw = d->hwirq; 202 + u32 word = hw / IRQS_PER_WORD; 203 + u32 mask = BIT(hw % IRQS_PER_WORD); 204 + unsigned int first_cpu = cpumask_any_and(dest, cpu_online_mask); 205 + bool was_disabled; 206 + 207 + raw_spin_lock_irqsave(&intc->lock, flags); 208 + 209 + was_disabled = !!(intc->cpus[intc->affinity[hw]]->mask_cache[word] & 210 + mask); 211 + __bcm7038_l1_mask(d, intc->affinity[hw]); 212 + intc->affinity[hw] = first_cpu; 213 + if (!was_disabled) 214 + __bcm7038_l1_unmask(d, first_cpu); 215 + 216 + raw_spin_unlock_irqrestore(&intc->lock, flags); 217 + return 0; 218 + } 219 + 220 + static int __init bcm7038_l1_init_one(struct device_node *dn, 221 + unsigned int idx, 222 + struct bcm7038_l1_chip *intc) 223 + { 224 + struct resource res; 225 + resource_size_t sz; 226 + struct bcm7038_l1_cpu *cpu; 227 + unsigned int i, n_words, parent_irq; 228 + 229 + if (of_address_to_resource(dn, idx, &res)) 230 + return -EINVAL; 231 + sz = resource_size(&res); 232 + n_words = sz / REG_BYTES_PER_IRQ_WORD; 233 + 234 + if (n_words > MAX_WORDS) 235 + return -EINVAL; 236 + else if (!intc->n_words) 237 + intc->n_words = n_words; 238 + else if (intc->n_words != n_words) 239 + return -EINVAL; 240 + 241 + cpu = intc->cpus[idx] = kzalloc(sizeof(*cpu) + n_words * sizeof(u32), 242 + GFP_KERNEL); 243 + if (!cpu) 244 + return -ENOMEM; 245 + 246 + cpu->map_base = ioremap(res.start, sz); 247 + if (!cpu->map_base) 248 + return -ENOMEM; 249 + 250 + for (i = 0; i < n_words; i++) { 251 + l1_writel(0xffffffff, cpu->map_base + reg_mask_set(intc, i)); 252 + cpu->mask_cache[i] = 0xffffffff; 253 + } 254 + 255 + parent_irq = irq_of_parse_and_map(dn, idx); 256 + if (!parent_irq) { 257 + pr_err("failed to map parent interrupt %d\n", parent_irq); 258 + return -EINVAL; 259 + } 260 + irq_set_handler_data(parent_irq, intc); 261 + irq_set_chained_handler(parent_irq, bcm7038_l1_irq_handle); 262 + 263 + return 0; 264 + } 265 + 266 + static struct irq_chip bcm7038_l1_irq_chip = { 267 + .name = "bcm7038-l1", 268 + .irq_mask = bcm7038_l1_mask, 269 + .irq_unmask = bcm7038_l1_unmask, 270 + .irq_set_affinity = bcm7038_l1_set_affinity, 271 + }; 272 + 273 + static int bcm7038_l1_map(struct irq_domain *d, unsigned int virq, 274 + irq_hw_number_t hw_irq) 275 + { 276 + irq_set_chip_and_handler(virq, &bcm7038_l1_irq_chip, handle_level_irq); 277 + irq_set_chip_data(virq, d->host_data); 278 + return 0; 279 + } 280 + 281 + static const struct irq_domain_ops bcm7038_l1_domain_ops = { 282 + .xlate = irq_domain_xlate_onecell, 283 + .map = bcm7038_l1_map, 284 + }; 285 + 286 + int __init bcm7038_l1_of_init(struct device_node *dn, 287 + struct device_node *parent) 288 + { 289 + struct bcm7038_l1_chip *intc; 290 + int idx, ret; 291 + 292 + intc = kzalloc(sizeof(*intc), GFP_KERNEL); 293 + if (!intc) 294 + return -ENOMEM; 295 + 296 + raw_spin_lock_init(&intc->lock); 297 + for_each_possible_cpu(idx) { 298 + ret = bcm7038_l1_init_one(dn, idx, intc); 299 + if (ret < 0) { 300 + if (idx) 301 + break; 302 + pr_err("failed to remap intc L1 registers\n"); 303 + goto out_free; 304 + } 305 + } 306 + 307 + intc->domain = irq_domain_add_linear(dn, IRQS_PER_WORD * intc->n_words, 308 + &bcm7038_l1_domain_ops, 309 + intc); 310 + if (!intc->domain) { 311 + ret = -ENOMEM; 312 + goto out_unmap; 313 + } 314 + 315 + pr_info("registered BCM7038 L1 intc (mem: 0x%p, IRQs: %d)\n", 316 + intc->cpus[0]->map_base, IRQS_PER_WORD * intc->n_words); 317 + 318 + return 0; 319 + 320 + out_unmap: 321 + for_each_possible_cpu(idx) { 322 + struct bcm7038_l1_cpu *cpu = intc->cpus[idx]; 323 + 324 + if (cpu) { 325 + if (cpu->map_base) 326 + iounmap(cpu->map_base); 327 + kfree(cpu); 328 + } 329 + } 330 + out_free: 331 + kfree(intc); 332 + return ret; 333 + } 334 + 335 + IRQCHIP_DECLARE(bcm7038_l1, "brcm,bcm7038-l1-intc", bcm7038_l1_of_init);