1*7728819cSStefan Wahren // SPDX-License-Identifier: GPL-2.0+ 289214f00SSimon Arlott /* 389214f00SSimon Arlott * Copyright 2010 Broadcom 489214f00SSimon Arlott * Copyright 2012 Simon Arlott, Chris Boot, Stephen Warren 589214f00SSimon Arlott * 689214f00SSimon Arlott * Quirk 1: Shortcut interrupts don't set the bank 1/2 register pending bits 789214f00SSimon Arlott * 889214f00SSimon Arlott * If an interrupt fires on bank 1 that isn't in the shortcuts list, bit 8 989214f00SSimon Arlott * on bank 0 is set to signify that an interrupt in bank 1 has fired, and 1089214f00SSimon Arlott * to look in the bank 1 status register for more information. 1189214f00SSimon Arlott * 1289214f00SSimon Arlott * If an interrupt fires on bank 1 that _is_ in the shortcuts list, its 1389214f00SSimon Arlott * shortcut bit in bank 0 is set as well as its interrupt bit in the bank 1 1489214f00SSimon Arlott * status register, but bank 0 bit 8 is _not_ set. 1589214f00SSimon Arlott * 1689214f00SSimon Arlott * Quirk 2: You can't mask the register 1/2 pending interrupts 1789214f00SSimon Arlott * 1889214f00SSimon Arlott * In a proper cascaded interrupt controller, the interrupt lines with 1989214f00SSimon Arlott * cascaded interrupt controllers on them are just normal interrupt lines. 2089214f00SSimon Arlott * You can mask the interrupts and get on with things. With this controller 2189214f00SSimon Arlott * you can't do that. 2289214f00SSimon Arlott * 2389214f00SSimon Arlott * Quirk 3: The shortcut interrupts can't be (un)masked in bank 0 2489214f00SSimon Arlott * 2589214f00SSimon Arlott * Those interrupts that have shortcuts can only be masked/unmasked in 2689214f00SSimon Arlott * their respective banks' enable/disable registers. Doing so in the bank 0 2789214f00SSimon Arlott * enable/disable registers has no effect. 2889214f00SSimon Arlott * 2989214f00SSimon Arlott * The FIQ control register: 3089214f00SSimon Arlott * Bits 0-6: IRQ (index in order of interrupts from banks 1, 2, then 0) 3189214f00SSimon Arlott * Bit 7: Enable FIQ generation 3289214f00SSimon Arlott * Bits 8+: Unused 3389214f00SSimon Arlott * 3489214f00SSimon Arlott * An interrupt must be disabled before configuring it for FIQ generation 3589214f00SSimon Arlott * otherwise both handlers will fire at the same time! 3689214f00SSimon Arlott */ 3789214f00SSimon Arlott 3889214f00SSimon Arlott #include <linux/io.h> 3989214f00SSimon Arlott #include <linux/slab.h> 4089214f00SSimon Arlott #include <linux/of_address.h> 4189214f00SSimon Arlott #include <linux/of_irq.h> 4241a83e06SJoel Porquet #include <linux/irqchip.h> 4389214f00SSimon Arlott #include <linux/irqdomain.h> 4489214f00SSimon Arlott 4589214f00SSimon Arlott #include <asm/exception.h> 465702941eSAxel Lin 4789214f00SSimon Arlott /* Put the bank and irq (32 bits) into the hwirq */ 4889214f00SSimon Arlott #define MAKE_HWIRQ(b, n) ((b << 5) | (n)) 4989214f00SSimon Arlott #define HWIRQ_BANK(i) (i >> 5) 5089214f00SSimon Arlott #define HWIRQ_BIT(i) BIT(i & 0x1f) 5189214f00SSimon Arlott 5289214f00SSimon Arlott #define NR_IRQS_BANK0 8 5389214f00SSimon Arlott #define BANK0_HWIRQ_MASK 0xff 5489214f00SSimon Arlott /* Shortcuts can't be disabled so any unknown new ones need to be masked */ 5589214f00SSimon Arlott #define SHORTCUT1_MASK 0x00007c00 5689214f00SSimon Arlott #define SHORTCUT2_MASK 0x001f8000 5789214f00SSimon Arlott #define SHORTCUT_SHIFT 10 5889214f00SSimon Arlott #define BANK1_HWIRQ BIT(8) 5989214f00SSimon Arlott #define BANK2_HWIRQ BIT(9) 6089214f00SSimon Arlott #define BANK0_VALID_MASK (BANK0_HWIRQ_MASK | BANK1_HWIRQ | BANK2_HWIRQ \ 6189214f00SSimon Arlott | SHORTCUT1_MASK | SHORTCUT2_MASK) 6289214f00SSimon Arlott 6389214f00SSimon Arlott #define REG_FIQ_CONTROL 0x0c 6489214f00SSimon Arlott 6589214f00SSimon Arlott #define NR_BANKS 3 6689214f00SSimon Arlott #define IRQS_PER_BANK 32 6789214f00SSimon Arlott 68c376023bSNicolas Pitre static const int reg_pending[] __initconst = { 0x00, 0x04, 0x08 }; 69c376023bSNicolas Pitre static const int reg_enable[] __initconst = { 0x18, 0x10, 0x14 }; 70c376023bSNicolas Pitre static const int reg_disable[] __initconst = { 0x24, 0x1c, 0x20 }; 71c376023bSNicolas Pitre static const int bank_irqs[] __initconst = { 8, 32, 32 }; 7289214f00SSimon Arlott 7389214f00SSimon Arlott static const int shortcuts[] = { 7489214f00SSimon Arlott 7, 9, 10, 18, 19, /* Bank 1 */ 7589214f00SSimon Arlott 21, 22, 23, 24, 25, 30 /* Bank 2 */ 7689214f00SSimon Arlott }; 7789214f00SSimon Arlott 7889214f00SSimon Arlott struct armctrl_ic { 7989214f00SSimon Arlott void __iomem *base; 8089214f00SSimon Arlott void __iomem *pending[NR_BANKS]; 8189214f00SSimon Arlott void __iomem *enable[NR_BANKS]; 8289214f00SSimon Arlott void __iomem *disable[NR_BANKS]; 8389214f00SSimon Arlott struct irq_domain *domain; 8489214f00SSimon Arlott }; 8589214f00SSimon Arlott 8689214f00SSimon Arlott static struct armctrl_ic intc __read_mostly; 878783dd3aSStephen Boyd static void __exception_irq_entry bcm2835_handle_irq( 885702941eSAxel Lin struct pt_regs *regs); 89bd0b9ac4SThomas Gleixner static void bcm2836_chained_handle_irq(struct irq_desc *desc); 9089214f00SSimon Arlott 9189214f00SSimon Arlott static void armctrl_mask_irq(struct irq_data *d) 9289214f00SSimon Arlott { 9389214f00SSimon Arlott writel_relaxed(HWIRQ_BIT(d->hwirq), intc.disable[HWIRQ_BANK(d->hwirq)]); 9489214f00SSimon Arlott } 9589214f00SSimon Arlott 9689214f00SSimon Arlott static void armctrl_unmask_irq(struct irq_data *d) 9789214f00SSimon Arlott { 9889214f00SSimon Arlott writel_relaxed(HWIRQ_BIT(d->hwirq), intc.enable[HWIRQ_BANK(d->hwirq)]); 9989214f00SSimon Arlott } 10089214f00SSimon Arlott 10189214f00SSimon Arlott static struct irq_chip armctrl_chip = { 10289214f00SSimon Arlott .name = "ARMCTRL-level", 10389214f00SSimon Arlott .irq_mask = armctrl_mask_irq, 10489214f00SSimon Arlott .irq_unmask = armctrl_unmask_irq 10589214f00SSimon Arlott }; 10689214f00SSimon Arlott 10789214f00SSimon Arlott static int armctrl_xlate(struct irq_domain *d, struct device_node *ctrlr, 10889214f00SSimon Arlott const u32 *intspec, unsigned int intsize, 10989214f00SSimon Arlott unsigned long *out_hwirq, unsigned int *out_type) 11089214f00SSimon Arlott { 11189214f00SSimon Arlott if (WARN_ON(intsize != 2)) 11289214f00SSimon Arlott return -EINVAL; 11389214f00SSimon Arlott 11489214f00SSimon Arlott if (WARN_ON(intspec[0] >= NR_BANKS)) 11589214f00SSimon Arlott return -EINVAL; 11689214f00SSimon Arlott 11789214f00SSimon Arlott if (WARN_ON(intspec[1] >= IRQS_PER_BANK)) 11889214f00SSimon Arlott return -EINVAL; 11989214f00SSimon Arlott 12089214f00SSimon Arlott if (WARN_ON(intspec[0] == 0 && intspec[1] >= NR_IRQS_BANK0)) 12189214f00SSimon Arlott return -EINVAL; 12289214f00SSimon Arlott 12389214f00SSimon Arlott *out_hwirq = MAKE_HWIRQ(intspec[0], intspec[1]); 12489214f00SSimon Arlott *out_type = IRQ_TYPE_NONE; 12589214f00SSimon Arlott return 0; 12689214f00SSimon Arlott } 12789214f00SSimon Arlott 12896009736SKrzysztof Kozlowski static const struct irq_domain_ops armctrl_ops = { 12989214f00SSimon Arlott .xlate = armctrl_xlate 13089214f00SSimon Arlott }; 13189214f00SSimon Arlott 13289214f00SSimon Arlott static int __init armctrl_of_init(struct device_node *node, 133a493f339SEric Anholt struct device_node *parent, 134a493f339SEric Anholt bool is_2836) 13589214f00SSimon Arlott { 13689214f00SSimon Arlott void __iomem *base; 13789214f00SSimon Arlott int irq, b, i; 13889214f00SSimon Arlott 13989214f00SSimon Arlott base = of_iomap(node, 0); 14089214f00SSimon Arlott if (!base) 141e81f54c6SRob Herring panic("%pOF: unable to map IC registers\n", node); 14289214f00SSimon Arlott 14389214f00SSimon Arlott intc.domain = irq_domain_add_linear(node, MAKE_HWIRQ(NR_BANKS, 0), 14489214f00SSimon Arlott &armctrl_ops, NULL); 14589214f00SSimon Arlott if (!intc.domain) 146e81f54c6SRob Herring panic("%pOF: unable to create IRQ domain\n", node); 14789214f00SSimon Arlott 14889214f00SSimon Arlott for (b = 0; b < NR_BANKS; b++) { 14989214f00SSimon Arlott intc.pending[b] = base + reg_pending[b]; 15089214f00SSimon Arlott intc.enable[b] = base + reg_enable[b]; 15189214f00SSimon Arlott intc.disable[b] = base + reg_disable[b]; 15289214f00SSimon Arlott 15389214f00SSimon Arlott for (i = 0; i < bank_irqs[b]; i++) { 15489214f00SSimon Arlott irq = irq_create_mapping(intc.domain, MAKE_HWIRQ(b, i)); 15589214f00SSimon Arlott BUG_ON(irq <= 0); 15689214f00SSimon Arlott irq_set_chip_and_handler(irq, &armctrl_chip, 15789214f00SSimon Arlott handle_level_irq); 158d17cab44SRob Herring irq_set_probe(irq); 15989214f00SSimon Arlott } 16089214f00SSimon Arlott } 1615702941eSAxel Lin 162a493f339SEric Anholt if (is_2836) { 163a493f339SEric Anholt int parent_irq = irq_of_parse_and_map(node, 0); 164a493f339SEric Anholt 165a493f339SEric Anholt if (!parent_irq) { 166e81f54c6SRob Herring panic("%pOF: unable to get parent interrupt.\n", 167e81f54c6SRob Herring node); 168a493f339SEric Anholt } 169a493f339SEric Anholt irq_set_chained_handler(parent_irq, bcm2836_chained_handle_irq); 170a493f339SEric Anholt } else { 1715702941eSAxel Lin set_handle_irq(bcm2835_handle_irq); 172a493f339SEric Anholt } 173a493f339SEric Anholt 17489214f00SSimon Arlott return 0; 17589214f00SSimon Arlott } 17689214f00SSimon Arlott 177a493f339SEric Anholt static int __init bcm2835_armctrl_of_init(struct device_node *node, 178a493f339SEric Anholt struct device_node *parent) 179a493f339SEric Anholt { 180a493f339SEric Anholt return armctrl_of_init(node, parent, false); 181a493f339SEric Anholt } 182a493f339SEric Anholt 183a493f339SEric Anholt static int __init bcm2836_armctrl_of_init(struct device_node *node, 184a493f339SEric Anholt struct device_node *parent) 185a493f339SEric Anholt { 186a493f339SEric Anholt return armctrl_of_init(node, parent, true); 187a493f339SEric Anholt } 188a493f339SEric Anholt 189a493f339SEric Anholt 19089214f00SSimon Arlott /* 19189214f00SSimon Arlott * Handle each interrupt across the entire interrupt controller. This reads the 19289214f00SSimon Arlott * status register before handling each interrupt, which is necessary given that 19389214f00SSimon Arlott * handle_IRQ may briefly re-enable interrupts for soft IRQ handling. 19489214f00SSimon Arlott */ 19589214f00SSimon Arlott 196de58e52fSEric Anholt static u32 armctrl_translate_bank(int bank) 19789214f00SSimon Arlott { 198de58e52fSEric Anholt u32 stat = readl_relaxed(intc.pending[bank]); 19989214f00SSimon Arlott 200de58e52fSEric Anholt return MAKE_HWIRQ(bank, ffs(stat) - 1); 20189214f00SSimon Arlott } 20289214f00SSimon Arlott 203de58e52fSEric Anholt static u32 armctrl_translate_shortcut(int bank, u32 stat) 20489214f00SSimon Arlott { 205de58e52fSEric Anholt return MAKE_HWIRQ(bank, shortcuts[ffs(stat >> SHORTCUT_SHIFT) - 1]); 206de58e52fSEric Anholt } 207de58e52fSEric Anholt 208de58e52fSEric Anholt static u32 get_next_armctrl_hwirq(void) 209de58e52fSEric Anholt { 210de58e52fSEric Anholt u32 stat = readl_relaxed(intc.pending[0]) & BANK0_VALID_MASK; 211de58e52fSEric Anholt 212de58e52fSEric Anholt if (stat == 0) 213de58e52fSEric Anholt return ~0; 214de58e52fSEric Anholt else if (stat & BANK0_HWIRQ_MASK) 215de58e52fSEric Anholt return MAKE_HWIRQ(0, ffs(stat & BANK0_HWIRQ_MASK) - 1); 216de58e52fSEric Anholt else if (stat & SHORTCUT1_MASK) 217de58e52fSEric Anholt return armctrl_translate_shortcut(1, stat & SHORTCUT1_MASK); 218de58e52fSEric Anholt else if (stat & SHORTCUT2_MASK) 219de58e52fSEric Anholt return armctrl_translate_shortcut(2, stat & SHORTCUT2_MASK); 220de58e52fSEric Anholt else if (stat & BANK1_HWIRQ) 221de58e52fSEric Anholt return armctrl_translate_bank(1); 222de58e52fSEric Anholt else if (stat & BANK2_HWIRQ) 223de58e52fSEric Anholt return armctrl_translate_bank(2); 224de58e52fSEric Anholt else 225de58e52fSEric Anholt BUG(); 22689214f00SSimon Arlott } 22789214f00SSimon Arlott 2288783dd3aSStephen Boyd static void __exception_irq_entry bcm2835_handle_irq( 22989214f00SSimon Arlott struct pt_regs *regs) 23089214f00SSimon Arlott { 231de58e52fSEric Anholt u32 hwirq; 23289214f00SSimon Arlott 233de58e52fSEric Anholt while ((hwirq = get_next_armctrl_hwirq()) != ~0) 234d7e3528eSEric Anholt handle_domain_irq(intc.domain, hwirq, regs); 23589214f00SSimon Arlott } 2365702941eSAxel Lin 237bd0b9ac4SThomas Gleixner static void bcm2836_chained_handle_irq(struct irq_desc *desc) 238a493f339SEric Anholt { 239a493f339SEric Anholt u32 hwirq; 240a493f339SEric Anholt 241a493f339SEric Anholt while ((hwirq = get_next_armctrl_hwirq()) != ~0) 242a493f339SEric Anholt generic_handle_irq(irq_linear_revmap(intc.domain, hwirq)); 243a493f339SEric Anholt } 244a493f339SEric Anholt 245a493f339SEric Anholt IRQCHIP_DECLARE(bcm2835_armctrl_ic, "brcm,bcm2835-armctrl-ic", 246a493f339SEric Anholt bcm2835_armctrl_of_init); 247a493f339SEric Anholt IRQCHIP_DECLARE(bcm2836_armctrl_ic, "brcm,bcm2836-armctrl-ic", 248a493f339SEric Anholt bcm2836_armctrl_of_init); 249