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

ARM: bcm2835: add interrupt controller driver

The BCM2835 contains a custom interrupt controller, which supports 72
interrupt sources using a 2-level register scheme. The interrupt
controller, or the HW block containing it, is referred to occasionally
as "armctrl" in the SoC documentation, hence the symbol naming in the
code.

This patch was extracted from git://github.com/lp0/linux.git branch
rpi-split as of 2012/09/08, and modified as follows:

* s/bcm2708/bcm2835/.
* Modified device tree vendor prefix.
* Moved implementation to drivers/irchip/.
* Added devicetree documentation, and hence removed list of IRQs from
bcm2835.dtsi.
* Changed shift in MAKE_HWIRQ() and HWIRQ_BANK() from 8 to 5 to reduce
the size of the hwirq space, and pass the total size of the hwirq space
to irq_domain_add_linear(), rather than just the number of valid hwirqs;
the two are different due to the hwirq space being sparse.
* Added the interrupt controller DT node to the top-level of the DT,
rather than nesting it inside a /axi node. Hence, changed the reg value
since /axi had a ranges property. This seems simpler to me, but I'm not
sure if everyone will like this change or not.
* Don't set struct irq_domain_ops.map = irq_domain_simple_map, hence
removing the need to patch include/linux/irqdomain.h or
kernel/irq/irqdomain.c.
* Simplified armctrl_of_init() using of_iomap().
* Removed unused IS_VALID_BANK()/IS_VALID_IRQ() macros.
* Renamed armctrl_handle_irq() to prevent possible symbol clashes.
* Made armctrl_of_init() static.
* Removed comment "Each bank is registered as a separate interrupt
controller" since this is no longer true.
* Removed FSF address from license header.
* Added my name to copyright header.

Signed-off-by: Chris Boot <bootc@bootc.net>
Signed-off-by: Simon Arlott <simon@fire.lp0.eu>
Signed-off-by: Dom Cobley <popcornmix@gmail.com>
Signed-off-by: Dom Cobley <dc4@broadcom.com>
Signed-off-by: Stephen Warren <swarren@wwwdotorg.org>
Acked-by: Arnd Bergmann <arnd@arndb.de>

authored by

Simon Arlott and committed by
Stephen Warren
89214f00 ec9653b8

+376 -9
+110
Documentation/devicetree/bindings/interrupt-controller/brcm,bcm2835-armctrl-ic.txt
··· 1 + BCM2835 Top-Level ("ARMCTRL") Interrupt Controller 2 + 3 + The BCM2835 contains a custom top-level interrupt controller, which supports 4 + 72 interrupt sources using a 2-level register scheme. The interrupt 5 + controller, or the HW block containing it, is referred to occasionally 6 + as "armctrl" in the SoC documentation, hence naming of this binding. 7 + 8 + Required properties: 9 + 10 + - compatible : should be "brcm,bcm2835-armctrl-ic.txt" 11 + - reg : Specifies base physical address and size of the registers. 12 + - interrupt-controller : Identifies the node as an interrupt controller 13 + - #interrupt-cells : Specifies the number of cells needed to encode an 14 + interrupt source. The value shall be 2. 15 + 16 + The 1st cell is the interrupt bank; 0 for interrupts in the "IRQ basic 17 + pending" register, or 1/2 respectively for interrupts in the "IRQ pending 18 + 1/2" register. 19 + 20 + The 2nd cell contains the interrupt number within the bank. Valid values 21 + are 0..7 for bank 0, and 0..31 for bank 1. 22 + 23 + The interrupt sources are as follows: 24 + 25 + Bank 0: 26 + 0: ARM_TIMER 27 + 1: ARM_MAILBOX 28 + 2: ARM_DOORBELL_0 29 + 3: ARM_DOORBELL_1 30 + 4: VPU0_HALTED 31 + 5: VPU1_HALTED 32 + 6: ILLEGAL_TYPE0 33 + 7: ILLEGAL_TYPE1 34 + 35 + Bank 1: 36 + 0: TIMER0 37 + 1: TIMER1 38 + 2: TIMER2 39 + 3: TIMER3 40 + 4: CODEC0 41 + 5: CODEC1 42 + 6: CODEC2 43 + 7: VC_JPEG 44 + 8: ISP 45 + 9: VC_USB 46 + 10: VC_3D 47 + 11: TRANSPOSER 48 + 12: MULTICORESYNC0 49 + 13: MULTICORESYNC1 50 + 14: MULTICORESYNC2 51 + 15: MULTICORESYNC3 52 + 16: DMA0 53 + 17: DMA1 54 + 18: VC_DMA2 55 + 19: VC_DMA3 56 + 20: DMA4 57 + 21: DMA5 58 + 22: DMA6 59 + 23: DMA7 60 + 24: DMA8 61 + 25: DMA9 62 + 26: DMA10 63 + 27: DMA11 64 + 28: DMA12 65 + 29: AUX 66 + 30: ARM 67 + 31: VPUDMA 68 + 69 + Bank 2: 70 + 0: HOSTPORT 71 + 1: VIDEOSCALER 72 + 2: CCP2TX 73 + 3: SDC 74 + 4: DSI0 75 + 5: AVE 76 + 6: CAM0 77 + 7: CAM1 78 + 8: HDMI0 79 + 9: HDMI1 80 + 10: PIXELVALVE1 81 + 11: I2CSPISLV 82 + 12: DSI1 83 + 13: PWA0 84 + 14: PWA1 85 + 15: CPR 86 + 16: SMI 87 + 17: GPIO0 88 + 18: GPIO1 89 + 19: GPIO2 90 + 20: GPIO3 91 + 21: VC_I2C 92 + 22: VC_SPI 93 + 23: VC_I2SPCM 94 + 24: VC_SDIO 95 + 25: VC_UART 96 + 26: SLIMBUS 97 + 27: VEC 98 + 28: CPG 99 + 29: RNG 100 + 30: VC_ARASANSDIO 101 + 31: AVSPMON 102 + 103 + Example: 104 + 105 + intc: interrupt-controller { 106 + compatible = "brcm,bcm2835-armctrl-ic"; 107 + reg = <0x7e00b200 0x200>; 108 + interrupt-controller; 109 + #interrupt-cells = <2>; 110 + };
+8
arch/arm/boot/dts/bcm2835.dtsi
··· 3 3 / { 4 4 compatible = "brcm,bcm2835"; 5 5 model = "BCM2835"; 6 + interrupt-parent = <&intc>; 6 7 7 8 chosen { 8 9 bootargs = "earlyprintk"; ··· 14 13 #address-cells = <1>; 15 14 #size-cells = <1>; 16 15 ranges = <0x7e000000 0x20000000 0x02000000>; 16 + 17 + intc: interrupt-controller { 18 + compatible = "brcm,bcm2835-armctrl-ic"; 19 + reg = <0x7e00b200 0x200>; 20 + interrupt-controller; 21 + #interrupt-cells = <2>; 22 + }; 17 23 }; 18 24 };
+1 -9
arch/arm/mach-bcm2835/bcm2835.c
··· 13 13 */ 14 14 15 15 #include <linux/init.h> 16 + #include <linux/irqchip/bcm2835.h> 16 17 #include <linux/of_platform.h> 17 18 18 19 #include <asm/mach/arch.h> 19 20 #include <asm/mach/map.h> 20 21 #include <asm/mach/time.h> 21 - #include <asm/exception.h> 22 22 23 23 #include <mach/bcm2835_soc.h> 24 24 ··· 32 32 void __init bcm2835_map_io(void) 33 33 { 34 34 iotable_init(&io_map, 1); 35 - } 36 - 37 - void __init bcm2835_init_irq(void) 38 - { 39 - } 40 - 41 - asmlinkage void __exception_irq_entry bcm2835_handle_irq(struct pt_regs *regs) 42 - { 43 35 } 44 36 45 37 void __init bcm2835_init(void)
+2
drivers/Kconfig
··· 152 152 153 153 source "drivers/pwm/Kconfig" 154 154 155 + source "drivers/irqchip/Kconfig" 156 + 155 157 endmenu
+2
drivers/Makefile
··· 5 5 # Rewritten to use lists instead of if-statements. 6 6 # 7 7 8 + obj-y += irqchip/ 9 + 8 10 # GPIO must come after pinctrl as gpios may need to mux pins etc 9 11 obj-y += pinctrl/ 10 12 obj-y += gpio/
drivers/irqchip/Kconfig
+1
drivers/irqchip/Makefile
··· 1 + obj-$(CONFIG_ARCH_BCM2835) += irq-bcm2835.o
+223
drivers/irqchip/irq-bcm2835.c
··· 1 + /* 2 + * Copyright 2010 Broadcom 3 + * Copyright 2012 Simon Arlott, Chris Boot, Stephen Warren 4 + * 5 + * This program is free software; you can redistribute it and/or modify 6 + * it under the terms of the GNU General Public License as published by 7 + * the Free Software Foundation; either version 2 of the License, or 8 + * (at your option) any later version. 9 + * 10 + * This program is distributed in the hope that it will be useful, 11 + * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 + * GNU General Public License for more details. 14 + * 15 + * Quirk 1: Shortcut interrupts don't set the bank 1/2 register pending bits 16 + * 17 + * If an interrupt fires on bank 1 that isn't in the shortcuts list, bit 8 18 + * on bank 0 is set to signify that an interrupt in bank 1 has fired, and 19 + * to look in the bank 1 status register for more information. 20 + * 21 + * If an interrupt fires on bank 1 that _is_ in the shortcuts list, its 22 + * shortcut bit in bank 0 is set as well as its interrupt bit in the bank 1 23 + * status register, but bank 0 bit 8 is _not_ set. 24 + * 25 + * Quirk 2: You can't mask the register 1/2 pending interrupts 26 + * 27 + * In a proper cascaded interrupt controller, the interrupt lines with 28 + * cascaded interrupt controllers on them are just normal interrupt lines. 29 + * You can mask the interrupts and get on with things. With this controller 30 + * you can't do that. 31 + * 32 + * Quirk 3: The shortcut interrupts can't be (un)masked in bank 0 33 + * 34 + * Those interrupts that have shortcuts can only be masked/unmasked in 35 + * their respective banks' enable/disable registers. Doing so in the bank 0 36 + * enable/disable registers has no effect. 37 + * 38 + * The FIQ control register: 39 + * Bits 0-6: IRQ (index in order of interrupts from banks 1, 2, then 0) 40 + * Bit 7: Enable FIQ generation 41 + * Bits 8+: Unused 42 + * 43 + * An interrupt must be disabled before configuring it for FIQ generation 44 + * otherwise both handlers will fire at the same time! 45 + */ 46 + 47 + #include <linux/io.h> 48 + #include <linux/slab.h> 49 + #include <linux/of_address.h> 50 + #include <linux/of_irq.h> 51 + #include <linux/irqdomain.h> 52 + #include <linux/irqchip/bcm2835.h> 53 + 54 + #include <asm/exception.h> 55 + 56 + /* Put the bank and irq (32 bits) into the hwirq */ 57 + #define MAKE_HWIRQ(b, n) ((b << 5) | (n)) 58 + #define HWIRQ_BANK(i) (i >> 5) 59 + #define HWIRQ_BIT(i) BIT(i & 0x1f) 60 + 61 + #define NR_IRQS_BANK0 8 62 + #define BANK0_HWIRQ_MASK 0xff 63 + /* Shortcuts can't be disabled so any unknown new ones need to be masked */ 64 + #define SHORTCUT1_MASK 0x00007c00 65 + #define SHORTCUT2_MASK 0x001f8000 66 + #define SHORTCUT_SHIFT 10 67 + #define BANK1_HWIRQ BIT(8) 68 + #define BANK2_HWIRQ BIT(9) 69 + #define BANK0_VALID_MASK (BANK0_HWIRQ_MASK | BANK1_HWIRQ | BANK2_HWIRQ \ 70 + | SHORTCUT1_MASK | SHORTCUT2_MASK) 71 + 72 + #define REG_FIQ_CONTROL 0x0c 73 + 74 + #define NR_BANKS 3 75 + #define IRQS_PER_BANK 32 76 + 77 + static int reg_pending[] __initconst = { 0x00, 0x04, 0x08 }; 78 + static int reg_enable[] __initconst = { 0x18, 0x10, 0x14 }; 79 + static int reg_disable[] __initconst = { 0x24, 0x1c, 0x20 }; 80 + static int bank_irqs[] __initconst = { 8, 32, 32 }; 81 + 82 + static const int shortcuts[] = { 83 + 7, 9, 10, 18, 19, /* Bank 1 */ 84 + 21, 22, 23, 24, 25, 30 /* Bank 2 */ 85 + }; 86 + 87 + struct armctrl_ic { 88 + void __iomem *base; 89 + void __iomem *pending[NR_BANKS]; 90 + void __iomem *enable[NR_BANKS]; 91 + void __iomem *disable[NR_BANKS]; 92 + struct irq_domain *domain; 93 + }; 94 + 95 + static struct armctrl_ic intc __read_mostly; 96 + 97 + static void armctrl_mask_irq(struct irq_data *d) 98 + { 99 + writel_relaxed(HWIRQ_BIT(d->hwirq), intc.disable[HWIRQ_BANK(d->hwirq)]); 100 + } 101 + 102 + static void armctrl_unmask_irq(struct irq_data *d) 103 + { 104 + writel_relaxed(HWIRQ_BIT(d->hwirq), intc.enable[HWIRQ_BANK(d->hwirq)]); 105 + } 106 + 107 + static struct irq_chip armctrl_chip = { 108 + .name = "ARMCTRL-level", 109 + .irq_mask = armctrl_mask_irq, 110 + .irq_unmask = armctrl_unmask_irq 111 + }; 112 + 113 + static int armctrl_xlate(struct irq_domain *d, struct device_node *ctrlr, 114 + const u32 *intspec, unsigned int intsize, 115 + unsigned long *out_hwirq, unsigned int *out_type) 116 + { 117 + if (WARN_ON(intsize != 2)) 118 + return -EINVAL; 119 + 120 + if (WARN_ON(intspec[0] >= NR_BANKS)) 121 + return -EINVAL; 122 + 123 + if (WARN_ON(intspec[1] >= IRQS_PER_BANK)) 124 + return -EINVAL; 125 + 126 + if (WARN_ON(intspec[0] == 0 && intspec[1] >= NR_IRQS_BANK0)) 127 + return -EINVAL; 128 + 129 + *out_hwirq = MAKE_HWIRQ(intspec[0], intspec[1]); 130 + *out_type = IRQ_TYPE_NONE; 131 + return 0; 132 + } 133 + 134 + static struct irq_domain_ops armctrl_ops = { 135 + .xlate = armctrl_xlate 136 + }; 137 + 138 + static int __init armctrl_of_init(struct device_node *node, 139 + struct device_node *parent) 140 + { 141 + void __iomem *base; 142 + int irq, b, i; 143 + 144 + base = of_iomap(node, 0); 145 + if (!base) 146 + panic("%s: unable to map IC registers\n", 147 + node->full_name); 148 + 149 + intc.domain = irq_domain_add_linear(node, MAKE_HWIRQ(NR_BANKS, 0), 150 + &armctrl_ops, NULL); 151 + if (!intc.domain) 152 + panic("%s: unable to create IRQ domain\n", node->full_name); 153 + 154 + for (b = 0; b < NR_BANKS; b++) { 155 + intc.pending[b] = base + reg_pending[b]; 156 + intc.enable[b] = base + reg_enable[b]; 157 + intc.disable[b] = base + reg_disable[b]; 158 + 159 + for (i = 0; i < bank_irqs[b]; i++) { 160 + irq = irq_create_mapping(intc.domain, MAKE_HWIRQ(b, i)); 161 + BUG_ON(irq <= 0); 162 + irq_set_chip_and_handler(irq, &armctrl_chip, 163 + handle_level_irq); 164 + set_irq_flags(irq, IRQF_VALID | IRQF_PROBE); 165 + } 166 + } 167 + return 0; 168 + } 169 + 170 + static struct of_device_id irq_of_match[] __initconst = { 171 + { .compatible = "brcm,bcm2835-armctrl-ic", .data = armctrl_of_init } 172 + }; 173 + 174 + void __init bcm2835_init_irq(void) 175 + { 176 + of_irq_init(irq_of_match); 177 + } 178 + 179 + /* 180 + * Handle each interrupt across the entire interrupt controller. This reads the 181 + * status register before handling each interrupt, which is necessary given that 182 + * handle_IRQ may briefly re-enable interrupts for soft IRQ handling. 183 + */ 184 + 185 + static void armctrl_handle_bank(int bank, struct pt_regs *regs) 186 + { 187 + u32 stat, irq; 188 + 189 + while ((stat = readl_relaxed(intc.pending[bank]))) { 190 + irq = MAKE_HWIRQ(bank, ffs(stat) - 1); 191 + handle_IRQ(irq_linear_revmap(intc.domain, irq), regs); 192 + } 193 + } 194 + 195 + static void armctrl_handle_shortcut(int bank, struct pt_regs *regs, 196 + u32 stat) 197 + { 198 + u32 irq = MAKE_HWIRQ(bank, shortcuts[ffs(stat >> SHORTCUT_SHIFT) - 1]); 199 + handle_IRQ(irq_linear_revmap(intc.domain, irq), regs); 200 + } 201 + 202 + asmlinkage void __exception_irq_entry bcm2835_handle_irq( 203 + struct pt_regs *regs) 204 + { 205 + u32 stat, irq; 206 + 207 + while ((stat = readl_relaxed(intc.pending[0]) & BANK0_VALID_MASK)) { 208 + if (stat & BANK0_HWIRQ_MASK) { 209 + irq = MAKE_HWIRQ(0, ffs(stat & BANK0_HWIRQ_MASK) - 1); 210 + handle_IRQ(irq_linear_revmap(intc.domain, irq), regs); 211 + } else if (stat & SHORTCUT1_MASK) { 212 + armctrl_handle_shortcut(1, regs, stat & SHORTCUT1_MASK); 213 + } else if (stat & SHORTCUT2_MASK) { 214 + armctrl_handle_shortcut(2, regs, stat & SHORTCUT2_MASK); 215 + } else if (stat & BANK1_HWIRQ) { 216 + armctrl_handle_bank(1, regs); 217 + } else if (stat & BANK2_HWIRQ) { 218 + armctrl_handle_bank(2, regs); 219 + } else { 220 + BUG(); 221 + } 222 + } 223 + }
+29
include/linux/irqchip/bcm2835.h
··· 1 + /* 2 + * Copyright (C) 2010 Broadcom 3 + * 4 + * This program is free software; you can redistribute it and/or modify 5 + * it under the terms of the GNU General Public License as published by 6 + * the Free Software Foundation; either version 2 of the License, or 7 + * (at your option) any later version. 8 + * 9 + * This program is distributed in the hope that it will be useful, 10 + * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 + * GNU General Public License for more details. 13 + * 14 + * You should have received a copy of the GNU General Public License 15 + * along with this program; if not, write to the Free Software 16 + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 17 + */ 18 + 19 + #ifndef __LINUX_IRQCHIP_BCM2835_H_ 20 + #define __LINUX_IRQCHIP_BCM2835_H_ 21 + 22 + #include <asm/exception.h> 23 + 24 + extern void bcm2835_init_irq(void); 25 + 26 + extern asmlinkage void __exception_irq_entry bcm2835_handle_irq( 27 + struct pt_regs *regs); 28 + 29 + #endif