diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2015-09-01 14:33:35 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2015-09-01 14:33:35 -0700 |
commit | 17e6b00ac422b49d44a0b8d98402a211f726282d (patch) | |
tree | c7e9143030d20625a0bd94e12ddaf9421890c375 /drivers/irqchip/irq-bcm2835.c | |
parent | 5e359bf2219d8622eb0931701e45af55db323228 (diff) | |
parent | e324c4dc4a5991d5b1171f434884a4026345e4b4 (diff) |
Merge branch 'irq-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
Pull irq updates from Thomas Gleixner:
"This updated pull request does not contain the last few GIC related
patches which were reported to cause a regression. There is a fix
available, but I let it breed for a couple of days first.
The irq departement provides:
- new infrastructure to support non PCI based MSI interrupts
- a couple of new irq chip drivers
- the usual pile of fixlets and updates to irq chip drivers
- preparatory changes for removal of the irq argument from interrupt
flow handlers
- preparatory changes to remove IRQF_VALID"
* 'irq-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: (129 commits)
irqchip/imx-gpcv2: IMX GPCv2 driver for wakeup sources
irqchip: Add bcm2836 interrupt controller for Raspberry Pi 2
irqchip: Add documentation for the bcm2836 interrupt controller
irqchip/bcm2835: Add support for being used as a second level controller
irqchip/bcm2835: Refactor handle_IRQ() calls out of MAKE_HWIRQ
PCI: xilinx: Fix typo in function name
irqchip/gic: Ensure gic_cpu_if_up/down() programs correct GIC instance
irqchip/gic: Only allow the primary GIC to set the CPU map
PCI/MSI: pci-xgene-msi: Consolidate chained IRQ handler install/remove
unicore32/irq: Prepare puv3_gpio_handler for irq argument removal
tile/pci_gx: Prepare trio_handle_level_irq for irq argument removal
m68k/irq: Prepare irq handlers for irq argument removal
C6X/megamode-pic: Prepare megamod_irq_cascade for irq argument removal
blackfin: Prepare irq handlers for irq argument removal
arc/irq: Prepare idu_cascade_isr for irq argument removal
sparc/irq: Use access helper irq_data_get_affinity_mask()
sparc/irq: Use helper irq_data_get_irq_handler_data()
parisc/irq: Use access helper irq_data_get_affinity_mask()
mn10300/irq: Use access helper irq_data_get_affinity_mask()
irqchip/i8259: Prepare i8259_irq_dispatch for irq argument removal
...
Diffstat (limited to 'drivers/irqchip/irq-bcm2835.c')
-rw-r--r-- | drivers/irqchip/irq-bcm2835.c | 111 |
1 files changed, 74 insertions, 37 deletions
diff --git a/drivers/irqchip/irq-bcm2835.c b/drivers/irqchip/irq-bcm2835.c index e68c3b60a681..ed4ca9deca70 100644 --- a/drivers/irqchip/irq-bcm2835.c +++ b/drivers/irqchip/irq-bcm2835.c @@ -48,13 +48,12 @@ #include <linux/slab.h> #include <linux/of_address.h> #include <linux/of_irq.h> +#include <linux/irqchip.h> #include <linux/irqdomain.h> #include <asm/exception.h> #include <asm/mach/irq.h> -#include "irqchip.h" - /* Put the bank and irq (32 bits) into the hwirq */ #define MAKE_HWIRQ(b, n) ((b << 5) | (n)) #define HWIRQ_BANK(i) (i >> 5) @@ -76,10 +75,10 @@ #define NR_BANKS 3 #define IRQS_PER_BANK 32 -static int reg_pending[] __initconst = { 0x00, 0x04, 0x08 }; -static int reg_enable[] __initconst = { 0x18, 0x10, 0x14 }; -static int reg_disable[] __initconst = { 0x24, 0x1c, 0x20 }; -static int bank_irqs[] __initconst = { 8, 32, 32 }; +static const int reg_pending[] __initconst = { 0x00, 0x04, 0x08 }; +static const int reg_enable[] __initconst = { 0x18, 0x10, 0x14 }; +static const int reg_disable[] __initconst = { 0x24, 0x1c, 0x20 }; +static const int bank_irqs[] __initconst = { 8, 32, 32 }; static const int shortcuts[] = { 7, 9, 10, 18, 19, /* Bank 1 */ @@ -97,6 +96,7 @@ struct armctrl_ic { static struct armctrl_ic intc __read_mostly; static void __exception_irq_entry bcm2835_handle_irq( struct pt_regs *regs); +static void bcm2836_chained_handle_irq(unsigned int irq, struct irq_desc *desc); static void armctrl_mask_irq(struct irq_data *d) { @@ -140,7 +140,8 @@ static const struct irq_domain_ops armctrl_ops = { }; static int __init armctrl_of_init(struct device_node *node, - struct device_node *parent) + struct device_node *parent, + bool is_2836) { void __iomem *base; int irq, b, i; @@ -169,54 +170,90 @@ static int __init armctrl_of_init(struct device_node *node, } } - set_handle_irq(bcm2835_handle_irq); + if (is_2836) { + int parent_irq = irq_of_parse_and_map(node, 0); + + if (!parent_irq) { + panic("%s: unable to get parent interrupt.\n", + node->full_name); + } + irq_set_chained_handler(parent_irq, bcm2836_chained_handle_irq); + } else { + set_handle_irq(bcm2835_handle_irq); + } + return 0; } +static int __init bcm2835_armctrl_of_init(struct device_node *node, + struct device_node *parent) +{ + return armctrl_of_init(node, parent, false); +} + +static int __init bcm2836_armctrl_of_init(struct device_node *node, + struct device_node *parent) +{ + return armctrl_of_init(node, parent, true); +} + + /* * Handle each interrupt across the entire interrupt controller. This reads the * status register before handling each interrupt, which is necessary given that * handle_IRQ may briefly re-enable interrupts for soft IRQ handling. */ -static void armctrl_handle_bank(int bank, struct pt_regs *regs) +static u32 armctrl_translate_bank(int bank) { - u32 stat, irq; + u32 stat = readl_relaxed(intc.pending[bank]); - while ((stat = readl_relaxed(intc.pending[bank]))) { - irq = MAKE_HWIRQ(bank, ffs(stat) - 1); - handle_IRQ(irq_linear_revmap(intc.domain, irq), regs); - } + return MAKE_HWIRQ(bank, ffs(stat) - 1); +} + +static u32 armctrl_translate_shortcut(int bank, u32 stat) +{ + return MAKE_HWIRQ(bank, shortcuts[ffs(stat >> SHORTCUT_SHIFT) - 1]); } -static void armctrl_handle_shortcut(int bank, struct pt_regs *regs, - u32 stat) +static u32 get_next_armctrl_hwirq(void) { - u32 irq = MAKE_HWIRQ(bank, shortcuts[ffs(stat >> SHORTCUT_SHIFT) - 1]); - handle_IRQ(irq_linear_revmap(intc.domain, irq), regs); + u32 stat = readl_relaxed(intc.pending[0]) & BANK0_VALID_MASK; + + if (stat == 0) + return ~0; + else if (stat & BANK0_HWIRQ_MASK) + return MAKE_HWIRQ(0, ffs(stat & BANK0_HWIRQ_MASK) - 1); + else if (stat & SHORTCUT1_MASK) + return armctrl_translate_shortcut(1, stat & SHORTCUT1_MASK); + else if (stat & SHORTCUT2_MASK) + return armctrl_translate_shortcut(2, stat & SHORTCUT2_MASK); + else if (stat & BANK1_HWIRQ) + return armctrl_translate_bank(1); + else if (stat & BANK2_HWIRQ) + return armctrl_translate_bank(2); + else + BUG(); } static void __exception_irq_entry bcm2835_handle_irq( struct pt_regs *regs) { - u32 stat, irq; - - while ((stat = readl_relaxed(intc.pending[0]) & BANK0_VALID_MASK)) { - if (stat & BANK0_HWIRQ_MASK) { - irq = MAKE_HWIRQ(0, ffs(stat & BANK0_HWIRQ_MASK) - 1); - handle_IRQ(irq_linear_revmap(intc.domain, irq), regs); - } else if (stat & SHORTCUT1_MASK) { - armctrl_handle_shortcut(1, regs, stat & SHORTCUT1_MASK); - } else if (stat & SHORTCUT2_MASK) { - armctrl_handle_shortcut(2, regs, stat & SHORTCUT2_MASK); - } else if (stat & BANK1_HWIRQ) { - armctrl_handle_bank(1, regs); - } else if (stat & BANK2_HWIRQ) { - armctrl_handle_bank(2, regs); - } else { - BUG(); - } - } + u32 hwirq; + + while ((hwirq = get_next_armctrl_hwirq()) != ~0) + handle_IRQ(irq_linear_revmap(intc.domain, hwirq), regs); +} + +static void bcm2836_chained_handle_irq(unsigned int irq, struct irq_desc *desc) +{ + u32 hwirq; + + while ((hwirq = get_next_armctrl_hwirq()) != ~0) + generic_handle_irq(irq_linear_revmap(intc.domain, hwirq)); } -IRQCHIP_DECLARE(bcm2835_armctrl_ic, "brcm,bcm2835-armctrl-ic", armctrl_of_init); +IRQCHIP_DECLARE(bcm2835_armctrl_ic, "brcm,bcm2835-armctrl-ic", + bcm2835_armctrl_of_init); +IRQCHIP_DECLARE(bcm2836_armctrl_ic, "brcm,bcm2836-armctrl-ic", + bcm2836_armctrl_of_init); |