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

irqchips/bmips: Add bcm6345-l1 interrupt controller

Add the BCM6345 interrupt controller based on the SMP-capable BCM7038
and the BCM3380 but with packed interrupt registers.

Add the BCM6345 interrupt controller to a list with the existing BCM7038
so that interrupts on CPU1 are not ignored.

Update the maintainers file list for BMIPS to include this driver.

Signed-off-by: Simon Arlott <simon@fire.lp0.eu>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: devicetree@vger.kernel.org
Cc: Ian Campbell <ijc+devicetree@hellion.org.uk>
Cc: Florian Fainelli <f.fainelli@gmail.com>
Cc: Jason Cooper <jason@lakedaemon.net>
Cc: Pawel Moll <pawel.moll@arm.com>
Cc: linux-mips@linux-mips.org
Cc: Marc Zyngier <marc.zyngier@arm.com>
Cc: Kevin Cernekee <cernekee@gmail.com>
Cc: Ralf Baechle <ralf@linux-mips.org>
Cc: Jonas Gorski <jogo@openwrt.org>
Cc: Kumar Gala <galak@codeaurora.org>
Cc: Rob Herring <robh@kernel.org>
Link: http://lkml.kernel.org/r/5651D176.6030908@simon.arlott.org.uk
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>

authored by

Simon Arlott and committed by
Thomas Gleixner
c7c42ec2 4b5ce20b

+380 -2
+1
MAINTAINERS
··· 2423 2423 F: arch/mips/include/asm/mach-bmips/* 2424 2424 F: arch/mips/kernel/*bmips* 2425 2425 F: arch/mips/boot/dts/brcm/bcm*.dts* 2426 + F: drivers/irqchip/irq-bcm63* 2426 2427 F: drivers/irqchip/irq-bcm7* 2427 2428 F: drivers/irqchip/irq-brcmstb* 2428 2429 F: include/linux/bcm963xx_nvram.h
+1
arch/mips/Kconfig
··· 151 151 select CSRC_R4K 152 152 select SYNC_R4K 153 153 select COMMON_CLK 154 + select BCM6345_L1_IRQ 154 155 select BCM7038_L1_IRQ 155 156 select BCM7120_L2_IRQ 156 157 select BRCMSTB_L2_IRQ
+8 -2
arch/mips/bmips/irq.c
··· 15 15 #include <asm/irq_cpu.h> 16 16 #include <asm/time.h> 17 17 18 + static const struct of_device_id smp_intc_dt_match[] = { 19 + { .compatible = "brcm,bcm7038-l1-intc" }, 20 + { .compatible = "brcm,bcm6345-l1-intc" }, 21 + {} 22 + }; 23 + 18 24 unsigned int get_c0_compare_int(void) 19 25 { 20 26 return CP0_LEGACY_COMPARE_IRQ; ··· 30 24 { 31 25 struct device_node *dn; 32 26 33 - /* Only the STB (bcm7038) controller supports SMP IRQ affinity */ 34 - dn = of_find_compatible_node(NULL, NULL, "brcm,bcm7038-l1-intc"); 27 + /* Only these controllers support SMP IRQ affinity */ 28 + dn = of_find_matching_node(NULL, smp_intc_dt_match); 35 29 if (dn) 36 30 of_node_put(dn); 37 31 else
+5
drivers/irqchip/Kconfig
··· 78 78 bool 79 79 select IRQ_DOMAIN 80 80 81 + config BCM6345_L1_IRQ 82 + bool 83 + select GENERIC_IRQ_CHIP 84 + select IRQ_DOMAIN 85 + 81 86 config BCM7038_L1_IRQ 82 87 bool 83 88 select GENERIC_IRQ_CHIP
+1
drivers/irqchip/Makefile
··· 46 46 obj-$(CONFIG_XTENSA_MX) += irq-xtensa-mx.o 47 47 obj-$(CONFIG_IRQ_CROSSBAR) += irq-crossbar.o 48 48 obj-$(CONFIG_SOC_VF610) += irq-vf610-mscm-ir.o 49 + obj-$(CONFIG_BCM6345_L1_IRQ) += irq-bcm6345-l1.o 49 50 obj-$(CONFIG_BCM7038_L1_IRQ) += irq-bcm7038-l1.o 50 51 obj-$(CONFIG_BCM7120_L2_IRQ) += irq-bcm7120-l2.o 51 52 obj-$(CONFIG_BRCMSTB_L2_IRQ) += irq-brcmstb-l2.o
+364
drivers/irqchip/irq-bcm6345-l1.c
··· 1 + /* 2 + * Broadcom BCM6345 style Level 1 interrupt controller driver 3 + * 4 + * Copyright (C) 2014 Broadcom Corporation 5 + * Copyright 2015 Simon Arlott 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 + * This is based on the BCM7038 (which supports SMP) but with a single 12 + * enable register instead of separate mask/set/clear registers. 13 + * 14 + * The BCM3380 has a similar mask/status register layout, but each pair 15 + * of words is at separate locations (and SMP is not supported). 16 + * 17 + * ENABLE/STATUS words are packed next to each other for each CPU: 18 + * 19 + * BCM6368: 20 + * 0x1000_0020: CPU0_W0_ENABLE 21 + * 0x1000_0024: CPU0_W1_ENABLE 22 + * 0x1000_0028: CPU0_W0_STATUS IRQs 31-63 23 + * 0x1000_002c: CPU0_W1_STATUS IRQs 0-31 24 + * 0x1000_0030: CPU1_W0_ENABLE 25 + * 0x1000_0034: CPU1_W1_ENABLE 26 + * 0x1000_0038: CPU1_W0_STATUS IRQs 31-63 27 + * 0x1000_003c: CPU1_W1_STATUS IRQs 0-31 28 + * 29 + * BCM63168: 30 + * 0x1000_0020: CPU0_W0_ENABLE 31 + * 0x1000_0024: CPU0_W1_ENABLE 32 + * 0x1000_0028: CPU0_W2_ENABLE 33 + * 0x1000_002c: CPU0_W3_ENABLE 34 + * 0x1000_0030: CPU0_W0_STATUS IRQs 96-127 35 + * 0x1000_0034: CPU0_W1_STATUS IRQs 64-95 36 + * 0x1000_0038: CPU0_W2_STATUS IRQs 32-63 37 + * 0x1000_003c: CPU0_W3_STATUS IRQs 0-31 38 + * 0x1000_0040: CPU1_W0_ENABLE 39 + * 0x1000_0044: CPU1_W1_ENABLE 40 + * 0x1000_0048: CPU1_W2_ENABLE 41 + * 0x1000_004c: CPU1_W3_ENABLE 42 + * 0x1000_0050: CPU1_W0_STATUS IRQs 96-127 43 + * 0x1000_0054: CPU1_W1_STATUS IRQs 64-95 44 + * 0x1000_0058: CPU1_W2_STATUS IRQs 32-63 45 + * 0x1000_005c: CPU1_W3_STATUS IRQs 0-31 46 + * 47 + * IRQs are numbered in CPU native endian order 48 + * (which is big-endian in these examples) 49 + */ 50 + 51 + #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 52 + 53 + #include <linux/bitops.h> 54 + #include <linux/cpumask.h> 55 + #include <linux/kconfig.h> 56 + #include <linux/kernel.h> 57 + #include <linux/init.h> 58 + #include <linux/interrupt.h> 59 + #include <linux/io.h> 60 + #include <linux/ioport.h> 61 + #include <linux/irq.h> 62 + #include <linux/irqdomain.h> 63 + #include <linux/module.h> 64 + #include <linux/of.h> 65 + #include <linux/of_irq.h> 66 + #include <linux/of_address.h> 67 + #include <linux/of_platform.h> 68 + #include <linux/platform_device.h> 69 + #include <linux/slab.h> 70 + #include <linux/smp.h> 71 + #include <linux/types.h> 72 + #include <linux/irqchip.h> 73 + #include <linux/irqchip/chained_irq.h> 74 + 75 + #define IRQS_PER_WORD 32 76 + #define REG_BYTES_PER_IRQ_WORD (sizeof(u32) * 2) 77 + 78 + struct bcm6345_l1_cpu; 79 + 80 + struct bcm6345_l1_chip { 81 + raw_spinlock_t lock; 82 + unsigned int n_words; 83 + struct irq_domain *domain; 84 + struct cpumask cpumask; 85 + struct bcm6345_l1_cpu *cpus[NR_CPUS]; 86 + }; 87 + 88 + struct bcm6345_l1_cpu { 89 + void __iomem *map_base; 90 + unsigned int parent_irq; 91 + u32 enable_cache[]; 92 + }; 93 + 94 + static inline unsigned int reg_enable(struct bcm6345_l1_chip *intc, 95 + unsigned int word) 96 + { 97 + #ifdef __BIG_ENDIAN 98 + return (1 * intc->n_words - word - 1) * sizeof(u32); 99 + #else 100 + return (0 * intc->n_words + word) * sizeof(u32); 101 + #endif 102 + } 103 + 104 + static inline unsigned int reg_status(struct bcm6345_l1_chip *intc, 105 + unsigned int word) 106 + { 107 + #ifdef __BIG_ENDIAN 108 + return (2 * intc->n_words - word - 1) * sizeof(u32); 109 + #else 110 + return (1 * intc->n_words + word) * sizeof(u32); 111 + #endif 112 + } 113 + 114 + static inline unsigned int cpu_for_irq(struct bcm6345_l1_chip *intc, 115 + struct irq_data *d) 116 + { 117 + return cpumask_first_and(&intc->cpumask, irq_data_get_affinity_mask(d)); 118 + } 119 + 120 + static void bcm6345_l1_irq_handle(struct irq_desc *desc) 121 + { 122 + struct bcm6345_l1_chip *intc = irq_desc_get_handler_data(desc); 123 + struct bcm6345_l1_cpu *cpu; 124 + struct irq_chip *chip = irq_desc_get_chip(desc); 125 + unsigned int idx; 126 + 127 + #ifdef CONFIG_SMP 128 + cpu = intc->cpus[cpu_logical_map(smp_processor_id())]; 129 + #else 130 + cpu = intc->cpus[0]; 131 + #endif 132 + 133 + chained_irq_enter(chip, desc); 134 + 135 + for (idx = 0; idx < intc->n_words; idx++) { 136 + int base = idx * IRQS_PER_WORD; 137 + unsigned long pending; 138 + irq_hw_number_t hwirq; 139 + unsigned int irq; 140 + 141 + pending = __raw_readl(cpu->map_base + reg_status(intc, idx)); 142 + pending &= __raw_readl(cpu->map_base + reg_enable(intc, idx)); 143 + 144 + for_each_set_bit(hwirq, &pending, IRQS_PER_WORD) { 145 + irq = irq_linear_revmap(intc->domain, base + hwirq); 146 + if (irq) 147 + do_IRQ(irq); 148 + else 149 + spurious_interrupt(); 150 + } 151 + } 152 + 153 + chained_irq_exit(chip, desc); 154 + } 155 + 156 + static inline void __bcm6345_l1_unmask(struct irq_data *d) 157 + { 158 + struct bcm6345_l1_chip *intc = irq_data_get_irq_chip_data(d); 159 + u32 word = d->hwirq / IRQS_PER_WORD; 160 + u32 mask = BIT(d->hwirq % IRQS_PER_WORD); 161 + unsigned int cpu_idx = cpu_for_irq(intc, d); 162 + 163 + intc->cpus[cpu_idx]->enable_cache[word] |= mask; 164 + __raw_writel(intc->cpus[cpu_idx]->enable_cache[word], 165 + intc->cpus[cpu_idx]->map_base + reg_enable(intc, word)); 166 + } 167 + 168 + static inline void __bcm6345_l1_mask(struct irq_data *d) 169 + { 170 + struct bcm6345_l1_chip *intc = irq_data_get_irq_chip_data(d); 171 + u32 word = d->hwirq / IRQS_PER_WORD; 172 + u32 mask = BIT(d->hwirq % IRQS_PER_WORD); 173 + unsigned int cpu_idx = cpu_for_irq(intc, d); 174 + 175 + intc->cpus[cpu_idx]->enable_cache[word] &= ~mask; 176 + __raw_writel(intc->cpus[cpu_idx]->enable_cache[word], 177 + intc->cpus[cpu_idx]->map_base + reg_enable(intc, word)); 178 + } 179 + 180 + static void bcm6345_l1_unmask(struct irq_data *d) 181 + { 182 + struct bcm6345_l1_chip *intc = irq_data_get_irq_chip_data(d); 183 + unsigned long flags; 184 + 185 + raw_spin_lock_irqsave(&intc->lock, flags); 186 + __bcm6345_l1_unmask(d); 187 + raw_spin_unlock_irqrestore(&intc->lock, flags); 188 + } 189 + 190 + static void bcm6345_l1_mask(struct irq_data *d) 191 + { 192 + struct bcm6345_l1_chip *intc = irq_data_get_irq_chip_data(d); 193 + unsigned long flags; 194 + 195 + raw_spin_lock_irqsave(&intc->lock, flags); 196 + __bcm6345_l1_mask(d); 197 + raw_spin_unlock_irqrestore(&intc->lock, flags); 198 + } 199 + 200 + static int bcm6345_l1_set_affinity(struct irq_data *d, 201 + const struct cpumask *dest, 202 + bool force) 203 + { 204 + struct bcm6345_l1_chip *intc = irq_data_get_irq_chip_data(d); 205 + u32 word = d->hwirq / IRQS_PER_WORD; 206 + u32 mask = BIT(d->hwirq % IRQS_PER_WORD); 207 + unsigned int old_cpu = cpu_for_irq(intc, d); 208 + unsigned int new_cpu; 209 + struct cpumask valid; 210 + unsigned long flags; 211 + bool enabled; 212 + 213 + if (!cpumask_and(&valid, &intc->cpumask, dest)) 214 + return -EINVAL; 215 + 216 + new_cpu = cpumask_any_and(&valid, cpu_online_mask); 217 + if (new_cpu >= nr_cpu_ids) 218 + return -EINVAL; 219 + 220 + dest = cpumask_of(new_cpu); 221 + 222 + raw_spin_lock_irqsave(&intc->lock, flags); 223 + if (old_cpu != new_cpu) { 224 + enabled = intc->cpus[old_cpu]->enable_cache[word] & mask; 225 + if (enabled) 226 + __bcm6345_l1_mask(d); 227 + cpumask_copy(irq_data_get_affinity_mask(d), dest); 228 + if (enabled) 229 + __bcm6345_l1_unmask(d); 230 + } else { 231 + cpumask_copy(irq_data_get_affinity_mask(d), dest); 232 + } 233 + raw_spin_unlock_irqrestore(&intc->lock, flags); 234 + 235 + return IRQ_SET_MASK_OK_NOCOPY; 236 + } 237 + 238 + static int __init bcm6345_l1_init_one(struct device_node *dn, 239 + unsigned int idx, 240 + struct bcm6345_l1_chip *intc) 241 + { 242 + struct resource res; 243 + resource_size_t sz; 244 + struct bcm6345_l1_cpu *cpu; 245 + unsigned int i, n_words; 246 + 247 + if (of_address_to_resource(dn, idx, &res)) 248 + return -EINVAL; 249 + sz = resource_size(&res); 250 + n_words = sz / REG_BYTES_PER_IRQ_WORD; 251 + 252 + if (!intc->n_words) 253 + intc->n_words = n_words; 254 + else if (intc->n_words != n_words) 255 + return -EINVAL; 256 + 257 + cpu = intc->cpus[idx] = kzalloc(sizeof(*cpu) + n_words * sizeof(u32), 258 + GFP_KERNEL); 259 + if (!cpu) 260 + return -ENOMEM; 261 + 262 + cpu->map_base = ioremap(res.start, sz); 263 + if (!cpu->map_base) 264 + return -ENOMEM; 265 + 266 + for (i = 0; i < n_words; i++) { 267 + cpu->enable_cache[i] = 0; 268 + __raw_writel(0, cpu->map_base + reg_enable(intc, i)); 269 + } 270 + 271 + cpu->parent_irq = irq_of_parse_and_map(dn, idx); 272 + if (!cpu->parent_irq) { 273 + pr_err("failed to map parent interrupt %d\n", cpu->parent_irq); 274 + return -EINVAL; 275 + } 276 + irq_set_chained_handler_and_data(cpu->parent_irq, 277 + bcm6345_l1_irq_handle, intc); 278 + 279 + return 0; 280 + } 281 + 282 + static struct irq_chip bcm6345_l1_irq_chip = { 283 + .name = "bcm6345-l1", 284 + .irq_mask = bcm6345_l1_mask, 285 + .irq_unmask = bcm6345_l1_unmask, 286 + .irq_set_affinity = bcm6345_l1_set_affinity, 287 + }; 288 + 289 + static int bcm6345_l1_map(struct irq_domain *d, unsigned int virq, 290 + irq_hw_number_t hw_irq) 291 + { 292 + irq_set_chip_and_handler(virq, 293 + &bcm6345_l1_irq_chip, handle_percpu_irq); 294 + irq_set_chip_data(virq, d->host_data); 295 + return 0; 296 + } 297 + 298 + static const struct irq_domain_ops bcm6345_l1_domain_ops = { 299 + .xlate = irq_domain_xlate_onecell, 300 + .map = bcm6345_l1_map, 301 + }; 302 + 303 + static int __init bcm6345_l1_of_init(struct device_node *dn, 304 + struct device_node *parent) 305 + { 306 + struct bcm6345_l1_chip *intc; 307 + unsigned int idx; 308 + int ret; 309 + 310 + intc = kzalloc(sizeof(*intc), GFP_KERNEL); 311 + if (!intc) 312 + return -ENOMEM; 313 + 314 + for_each_possible_cpu(idx) { 315 + ret = bcm6345_l1_init_one(dn, idx, intc); 316 + if (ret) 317 + pr_err("failed to init intc L1 for cpu %d: %d\n", 318 + idx, ret); 319 + else 320 + cpumask_set_cpu(idx, &intc->cpumask); 321 + } 322 + 323 + if (!cpumask_weight(&intc->cpumask)) { 324 + ret = -ENODEV; 325 + goto out_free; 326 + } 327 + 328 + raw_spin_lock_init(&intc->lock); 329 + 330 + intc->domain = irq_domain_add_linear(dn, IRQS_PER_WORD * intc->n_words, 331 + &bcm6345_l1_domain_ops, 332 + intc); 333 + if (!intc->domain) { 334 + ret = -ENOMEM; 335 + goto out_unmap; 336 + } 337 + 338 + pr_info("registered BCM6345 L1 intc (IRQs: %d)\n", 339 + IRQS_PER_WORD * intc->n_words); 340 + for_each_cpu(idx, &intc->cpumask) { 341 + struct bcm6345_l1_cpu *cpu = intc->cpus[idx]; 342 + 343 + pr_info(" CPU%u at MMIO 0x%p (irq = %d)\n", idx, 344 + cpu->map_base, cpu->parent_irq); 345 + } 346 + 347 + return 0; 348 + 349 + out_unmap: 350 + for_each_possible_cpu(idx) { 351 + struct bcm6345_l1_cpu *cpu = intc->cpus[idx]; 352 + 353 + if (cpu) { 354 + if (cpu->map_base) 355 + iounmap(cpu->map_base); 356 + kfree(cpu); 357 + } 358 + } 359 + out_free: 360 + kfree(intc); 361 + return ret; 362 + } 363 + 364 + IRQCHIP_DECLARE(bcm6345_l1, "brcm,bcm6345-l1-intc", bcm6345_l1_of_init);