diff options
-rw-r--r-- | Documentation/devicetree/bindings/interrupt-controller/opencores,or1k-pic.txt | 23 | ||||
-rw-r--r-- | arch/openrisc/Kconfig | 1 | ||||
-rw-r--r-- | arch/openrisc/include/asm/irq.h | 3 | ||||
-rw-r--r-- | arch/openrisc/kernel/irq.c | 146 | ||||
-rw-r--r-- | drivers/irqchip/Kconfig | 4 | ||||
-rw-r--r-- | drivers/irqchip/Makefile | 1 | ||||
-rw-r--r-- | drivers/irqchip/irq-armada-370-xp.c | 17 | ||||
-rw-r--r-- | drivers/irqchip/irq-brcmstb-l2.c | 2 | ||||
-rw-r--r-- | drivers/irqchip/irq-nvic.c | 13 | ||||
-rw-r--r-- | drivers/irqchip/irq-or1k-pic.c | 182 | ||||
-rw-r--r-- | drivers/irqchip/spear-shirq.c | 304 | ||||
-rw-r--r-- | include/linux/irqchip/spear-shirq.h | 64 |
12 files changed, 384 insertions, 376 deletions
diff --git a/Documentation/devicetree/bindings/interrupt-controller/opencores,or1k-pic.txt b/Documentation/devicetree/bindings/interrupt-controller/opencores,or1k-pic.txt new file mode 100644 index 000000000000..55c04faa3f3f --- /dev/null +++ b/Documentation/devicetree/bindings/interrupt-controller/opencores,or1k-pic.txt @@ -0,0 +1,23 @@ +OpenRISC 1000 Programmable Interrupt Controller + +Required properties: + +- compatible : should be "opencores,or1k-pic-level" for variants with + level triggered interrupt lines, "opencores,or1k-pic-edge" for variants with + edge triggered interrupt lines or "opencores,or1200-pic" for machines + with the non-spec compliant or1200 type implementation. + + "opencores,or1k-pic" is also provided as an alias to "opencores,or1200-pic", + but this is only for backwards compatibility. + +- interrupt-controller : Identifies the node as an interrupt controller +- #interrupt-cells : Specifies the number of cells needed to encode an + interrupt source. The value shall be 1. + +Example: + +intc: interrupt-controller { + compatible = "opencores,or1k-pic-level"; + interrupt-controller; + #interrupt-cells = <1>; +}; diff --git a/arch/openrisc/Kconfig b/arch/openrisc/Kconfig index e71d712afb79..88e83368bbf5 100644 --- a/arch/openrisc/Kconfig +++ b/arch/openrisc/Kconfig @@ -22,6 +22,7 @@ config OPENRISC select GENERIC_STRNLEN_USER select MODULES_USE_ELF_RELA select HAVE_DEBUG_STACKOVERFLOW + select OR1K_PIC config MMU def_bool y diff --git a/arch/openrisc/include/asm/irq.h b/arch/openrisc/include/asm/irq.h index eb612b1865d2..b84634cc95eb 100644 --- a/arch/openrisc/include/asm/irq.h +++ b/arch/openrisc/include/asm/irq.h @@ -24,4 +24,7 @@ #define NO_IRQ (-1) +void handle_IRQ(unsigned int, struct pt_regs *); +extern void set_handle_irq(void (*handle_irq)(struct pt_regs *)); + #endif /* __ASM_OPENRISC_IRQ_H__ */ diff --git a/arch/openrisc/kernel/irq.c b/arch/openrisc/kernel/irq.c index 8ec77bc9f1e7..967eb1430203 100644 --- a/arch/openrisc/kernel/irq.c +++ b/arch/openrisc/kernel/irq.c @@ -16,11 +16,10 @@ #include <linux/interrupt.h> #include <linux/init.h> -#include <linux/of.h> #include <linux/ftrace.h> #include <linux/irq.h> +#include <linux/irqchip.h> #include <linux/export.h> -#include <linux/irqdomain.h> #include <linux/irqflags.h> /* read interrupt enabled status */ @@ -37,150 +36,31 @@ void arch_local_irq_restore(unsigned long flags) } EXPORT_SYMBOL(arch_local_irq_restore); - -/* OR1K PIC implementation */ - -/* We're a couple of cycles faster than the generic implementations with - * these 'fast' versions. - */ - -static void or1k_pic_mask(struct irq_data *data) -{ - mtspr(SPR_PICMR, mfspr(SPR_PICMR) & ~(1UL << data->hwirq)); -} - -static void or1k_pic_unmask(struct irq_data *data) -{ - mtspr(SPR_PICMR, mfspr(SPR_PICMR) | (1UL << data->hwirq)); -} - -static void or1k_pic_ack(struct irq_data *data) -{ - /* EDGE-triggered interrupts need to be ack'ed in order to clear - * the latch. - * LEVEL-triggered interrupts do not need to be ack'ed; however, - * ack'ing the interrupt has no ill-effect and is quicker than - * trying to figure out what type it is... - */ - - /* The OpenRISC 1000 spec says to write a 1 to the bit to ack the - * interrupt, but the OR1200 does this backwards and requires a 0 - * to be written... - */ - -#ifdef CONFIG_OR1K_1200 - /* There are two oddities with the OR1200 PIC implementation: - * i) LEVEL-triggered interrupts are latched and need to be cleared - * ii) the interrupt latch is cleared by writing a 0 to the bit, - * as opposed to a 1 as mandated by the spec - */ - - mtspr(SPR_PICSR, mfspr(SPR_PICSR) & ~(1UL << data->hwirq)); -#else - WARN(1, "Interrupt handling possibly broken\n"); - mtspr(SPR_PICSR, (1UL << data->hwirq)); -#endif -} - -static void or1k_pic_mask_ack(struct irq_data *data) -{ - /* Comments for pic_ack apply here, too */ - -#ifdef CONFIG_OR1K_1200 - mtspr(SPR_PICMR, mfspr(SPR_PICMR) & ~(1UL << data->hwirq)); - mtspr(SPR_PICSR, mfspr(SPR_PICSR) & ~(1UL << data->hwirq)); -#else - WARN(1, "Interrupt handling possibly broken\n"); - mtspr(SPR_PICMR, (1UL << data->hwirq)); - mtspr(SPR_PICSR, (1UL << data->hwirq)); -#endif -} - -#if 0 -static int or1k_pic_set_type(struct irq_data *data, unsigned int flow_type) -{ - /* There's nothing to do in the PIC configuration when changing - * flow type. Level and edge-triggered interrupts are both - * supported, but it's PIC-implementation specific which type - * is handled. */ - - return irq_setup_alt_chip(data, flow_type); -} -#endif - -static struct irq_chip or1k_dev = { - .name = "or1k-PIC", - .irq_unmask = or1k_pic_unmask, - .irq_mask = or1k_pic_mask, - .irq_ack = or1k_pic_ack, - .irq_mask_ack = or1k_pic_mask_ack, -}; - -static struct irq_domain *root_domain; - -static inline int pic_get_irq(int first) -{ - int hwirq; - - hwirq = ffs(mfspr(SPR_PICSR) >> first); - if (!hwirq) - return NO_IRQ; - else - hwirq = hwirq + first -1; - - return irq_find_mapping(root_domain, hwirq); -} - - -static int or1k_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hw) +void __init init_IRQ(void) { - irq_set_chip_and_handler_name(irq, &or1k_dev, - handle_level_irq, "level"); - irq_set_status_flags(irq, IRQ_LEVEL | IRQ_NOPROBE); - - return 0; + irqchip_init(); } -static const struct irq_domain_ops or1k_irq_domain_ops = { - .xlate = irq_domain_xlate_onecell, - .map = or1k_map, -}; - -/* - * This sets up the IRQ domain for the PIC built in to the OpenRISC - * 1000 CPU. This is the "root" domain as these are the interrupts - * that directly trigger an exception in the CPU. - */ -static void __init or1k_irq_init(void) -{ - struct device_node *intc = NULL; - - /* The interrupt controller device node is mandatory */ - intc = of_find_compatible_node(NULL, NULL, "opencores,or1k-pic"); - BUG_ON(!intc); - - /* Disable all interrupts until explicitly requested */ - mtspr(SPR_PICMR, (0UL)); - - root_domain = irq_domain_add_linear(intc, 32, - &or1k_irq_domain_ops, NULL); -} +static void (*handle_arch_irq)(struct pt_regs *); -void __init init_IRQ(void) +void __init set_handle_irq(void (*handle_irq)(struct pt_regs *)) { - or1k_irq_init(); + handle_arch_irq = handle_irq; } -void __irq_entry do_IRQ(struct pt_regs *regs) +void handle_IRQ(unsigned int irq, struct pt_regs *regs) { - int irq = -1; struct pt_regs *old_regs = set_irq_regs(regs); irq_enter(); - while ((irq = pic_get_irq(irq + 1)) != NO_IRQ) - generic_handle_irq(irq); + generic_handle_irq(irq); irq_exit(); set_irq_regs(old_regs); } + +void __irq_entry do_IRQ(struct pt_regs *regs) +{ + handle_arch_irq(regs); +} diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig index bbb746e35500..131f18562d7d 100644 --- a/drivers/irqchip/Kconfig +++ b/drivers/irqchip/Kconfig @@ -53,6 +53,10 @@ config CLPS711X_IRQCHIP select SPARSE_IRQ default y +config OR1K_PIC + bool + select IRQ_DOMAIN + config ORION_IRQCHIP bool select IRQ_DOMAIN diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile index 62a13e5ef98f..7fba336c4daf 100644 --- a/drivers/irqchip/Makefile +++ b/drivers/irqchip/Makefile @@ -11,6 +11,7 @@ obj-$(CONFIG_METAG) += irq-metag-ext.o obj-$(CONFIG_METAG_PERFCOUNTER_IRQS) += irq-metag.o obj-$(CONFIG_ARCH_MOXART) += irq-moxart.o obj-$(CONFIG_CLPS711X_IRQCHIP) += irq-clps711x.o +obj-$(CONFIG_OR1K_PIC) += irq-or1k-pic.o obj-$(CONFIG_ORION_IRQCHIP) += irq-orion.o obj-$(CONFIG_ARCH_SUNXI) += irq-sun4i.o obj-$(CONFIG_ARCH_SUNXI) += irq-sunxi-nmi.o diff --git a/drivers/irqchip/irq-armada-370-xp.c b/drivers/irqchip/irq-armada-370-xp.c index c887e6eebc41..574aba0eba4e 100644 --- a/drivers/irqchip/irq-armada-370-xp.c +++ b/drivers/irqchip/irq-armada-370-xp.c @@ -334,6 +334,15 @@ static void armada_mpic_send_doorbell(const struct cpumask *mask, static void armada_xp_mpic_smp_cpu_init(void) { + u32 control; + int nr_irqs, i; + + control = readl(main_int_base + ARMADA_370_XP_INT_CONTROL); + nr_irqs = (control >> 2) & 0x3ff; + + for (i = 0; i < nr_irqs; i++) + writel(i, per_cpu_int_base + ARMADA_370_XP_INT_SET_MASK_OFFS); + /* Clear pending IPIs */ writel(0, per_cpu_int_base + ARMADA_370_XP_IN_DRBEL_CAUSE_OFFS); @@ -474,7 +483,7 @@ static int __init armada_370_xp_mpic_of_init(struct device_node *node, struct device_node *parent) { struct resource main_int_res, per_cpu_int_res; - int parent_irq; + int parent_irq, nr_irqs, i; u32 control; BUG_ON(of_address_to_resource(node, 0, &main_int_res)); @@ -496,9 +505,13 @@ static int __init armada_370_xp_mpic_of_init(struct device_node *node, BUG_ON(!per_cpu_int_base); control = readl(main_int_base + ARMADA_370_XP_INT_CONTROL); + nr_irqs = (control >> 2) & 0x3ff; + + for (i = 0; i < nr_irqs; i++) + writel(i, main_int_base + ARMADA_370_XP_INT_CLEAR_ENABLE_OFFS); armada_370_xp_mpic_domain = - irq_domain_add_linear(node, (control >> 2) & 0x3ff, + irq_domain_add_linear(node, nr_irqs, &armada_370_xp_mpic_irq_ops, NULL); BUG_ON(!armada_370_xp_mpic_domain); diff --git a/drivers/irqchip/irq-brcmstb-l2.c b/drivers/irqchip/irq-brcmstb-l2.c index 8ee2a36d5840..c15c840987d2 100644 --- a/drivers/irqchip/irq-brcmstb-l2.c +++ b/drivers/irqchip/irq-brcmstb-l2.c @@ -150,7 +150,7 @@ int __init brcmstb_l2_intc_of_init(struct device_node *np, /* Allocate a single Generic IRQ chip for this node */ ret = irq_alloc_domain_generic_chips(data->domain, 32, 1, - np->full_name, handle_level_irq, clr, 0, 0); + np->full_name, handle_edge_irq, clr, 0, 0); if (ret) { pr_err("failed to allocate generic irq chip\n"); goto out_free_domain; diff --git a/drivers/irqchip/irq-nvic.c b/drivers/irqchip/irq-nvic.c index 70bdf6edb7bb..4ff0805fca01 100644 --- a/drivers/irqchip/irq-nvic.c +++ b/drivers/irqchip/irq-nvic.c @@ -49,14 +49,6 @@ nvic_handle_irq(irq_hw_number_t hwirq, struct pt_regs *regs) handle_IRQ(irq, regs); } -static void nvic_eoi(struct irq_data *d) -{ - /* - * This is a no-op as end of interrupt is signaled by the exception - * return sequence. - */ -} - static int __init nvic_of_init(struct device_node *node, struct device_node *parent) { @@ -102,7 +94,10 @@ static int __init nvic_of_init(struct device_node *node, gc->chip_types[0].regs.disable = NVIC_ICER; gc->chip_types[0].chip.irq_mask = irq_gc_mask_disable_reg; gc->chip_types[0].chip.irq_unmask = irq_gc_unmask_enable_reg; - gc->chip_types[0].chip.irq_eoi = nvic_eoi; + /* This is a no-op as end of interrupt is signaled by the + * exception return sequence. + */ + gc->chip_types[0].chip.irq_eoi = irq_gc_noop; /* disable interrupts */ writel_relaxed(~0, gc->reg_base + NVIC_ICER); diff --git a/drivers/irqchip/irq-or1k-pic.c b/drivers/irqchip/irq-or1k-pic.c new file mode 100644 index 000000000000..17ff033d9925 --- /dev/null +++ b/drivers/irqchip/irq-or1k-pic.c @@ -0,0 +1,182 @@ +/* + * Copyright (C) 2010-2011 Jonas Bonn <jonas@southpole.se> + * Copyright (C) 2014 Stefan Kristansson <stefan.kristiansson@saunalahti.fi> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include <linux/irq.h> +#include <linux/of.h> +#include <linux/of_irq.h> +#include <linux/of_address.h> + +#include "irqchip.h" + +/* OR1K PIC implementation */ + +struct or1k_pic_dev { + struct irq_chip chip; + irq_flow_handler_t handle; + unsigned long flags; +}; + +/* + * We're a couple of cycles faster than the generic implementations with + * these 'fast' versions. + */ + +static void or1k_pic_mask(struct irq_data *data) +{ + mtspr(SPR_PICMR, mfspr(SPR_PICMR) & ~(1UL << data->hwirq)); +} + +static void or1k_pic_unmask(struct irq_data *data) +{ + mtspr(SPR_PICMR, mfspr(SPR_PICMR) | (1UL << data->hwirq)); +} + +static void or1k_pic_ack(struct irq_data *data) +{ + mtspr(SPR_PICSR, (1UL << data->hwirq)); +} + +static void or1k_pic_mask_ack(struct irq_data *data) +{ + mtspr(SPR_PICMR, mfspr(SPR_PICMR) & ~(1UL << data->hwirq)); + mtspr(SPR_PICSR, (1UL << data->hwirq)); +} + +/* + * There are two oddities with the OR1200 PIC implementation: + * i) LEVEL-triggered interrupts are latched and need to be cleared + * ii) the interrupt latch is cleared by writing a 0 to the bit, + * as opposed to a 1 as mandated by the spec + */ +static void or1k_pic_or1200_ack(struct irq_data *data) +{ + mtspr(SPR_PICSR, mfspr(SPR_PICSR) & ~(1UL << data->hwirq)); +} + +static void or1k_pic_or1200_mask_ack(struct irq_data *data) +{ + mtspr(SPR_PICMR, mfspr(SPR_PICMR) & ~(1UL << data->hwirq)); + mtspr(SPR_PICSR, mfspr(SPR_PICSR) & ~(1UL << data->hwirq)); +} + +static struct or1k_pic_dev or1k_pic_level = { + .chip = { + .name = "or1k-PIC-level", + .irq_unmask = or1k_pic_unmask, + .irq_mask = or1k_pic_mask, + .irq_mask_ack = or1k_pic_mask, + }, + .handle = handle_level_irq, + .flags = IRQ_LEVEL | IRQ_NOPROBE, +}; + +static struct or1k_pic_dev or1k_pic_edge = { + .chip = { + .name = "or1k-PIC-edge", + .irq_unmask = or1k_pic_unmask, + .irq_mask = or1k_pic_mask, + .irq_ack = or1k_pic_ack, + .irq_mask_ack = or1k_pic_mask_ack, + }, + .handle = handle_edge_irq, + .flags = IRQ_LEVEL | IRQ_NOPROBE, +}; + +static struct or1k_pic_dev or1k_pic_or1200 = { + .chip = { + .name = "or1200-PIC", + .irq_unmask = or1k_pic_unmask, + .irq_mask = or1k_pic_mask, + .irq_ack = or1k_pic_or1200_ack, + .irq_mask_ack = or1k_pic_or1200_mask_ack, + }, + .handle = handle_level_irq, + .flags = IRQ_LEVEL | IRQ_NOPROBE, +}; + +static struct irq_domain *root_domain; + +static inline int pic_get_irq(int first) +{ + int hwirq; + + hwirq = ffs(mfspr(SPR_PICSR) >> first); + if (!hwirq) + return NO_IRQ; + else + hwirq = hwirq + first - 1; + + return irq_find_mapping(root_domain, hwirq); +} + +static void or1k_pic_handle_irq(struct pt_regs *regs) +{ + int irq = -1; + + while ((irq = pic_get_irq(irq + 1)) != NO_IRQ) + handle_IRQ(irq, regs); +} + +static int or1k_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hw) +{ + struct or1k_pic_dev *pic = d->host_data; + + irq_set_chip_and_handler(irq, &pic->chip, pic->handle); + irq_set_status_flags(irq, pic->flags); + + return 0; +} + +static const struct irq_domain_ops or1k_irq_domain_ops = { + .xlate = irq_domain_xlate_onecell, + .map = or1k_map, +}; + +/* + * This sets up the IRQ domain for the PIC built in to the OpenRISC + * 1000 CPU. This is the "root" domain as these are the interrupts + * that directly trigger an exception in the CPU. + */ +static int __init or1k_pic_init(struct device_node *node, + struct or1k_pic_dev *pic) +{ + /* Disable all interrupts until explicitly requested */ + mtspr(SPR_PICMR, (0UL)); + + root_domain = irq_domain_add_linear(node, 32, &or1k_irq_domain_ops, + pic); + + set_handle_irq(or1k_pic_handle_irq); + + return 0; +} + +static int __init or1k_pic_or1200_init(struct device_node *node, + struct device_node *parent) +{ + return or1k_pic_init(node, &or1k_pic_or1200); +} +IRQCHIP_DECLARE(or1k_pic_or1200, "opencores,or1200-pic", or1k_pic_or1200_init); +IRQCHIP_DECLARE(or1k_pic, "opencores,or1k-pic", or1k_pic_or1200_init); + +static int __init or1k_pic_level_init(struct device_node *node, + struct device_node *parent) +{ + return or1k_pic_init(node, &or1k_pic_level); +} +IRQCHIP_DECLARE(or1k_pic_level, "opencores,or1k-pic-level", + or1k_pic_level_init); + +static int __init or1k_pic_edge_init(struct device_node *node, + struct device_node *parent) +{ + return or1k_pic_init(node, &or1k_pic_edge); +} +IRQCHIP_DECLARE(or1k_pic_edge, "opencores,or1k-pic-edge", or1k_pic_edge_init); diff --git a/drivers/irqchip/spear-shirq.c b/drivers/irqchip/spear-shirq.c index 3fdda3a40269..9c145a7cb056 100644 --- a/drivers/irqchip/spear-shirq.c +++ b/drivers/irqchip/spear-shirq.c @@ -19,7 +19,6 @@ #include <linux/io.h> #include <linux/irq.h> #include <linux/irqdomain.h> -#include <linux/irqchip/spear-shirq.h> #include <linux/of.h> #include <linux/of_address.h> #include <linux/of_irq.h> @@ -27,20 +26,73 @@ #include "irqchip.h" -static DEFINE_SPINLOCK(lock); +/* + * struct spear_shirq: shared irq structure + * + * base: Base register address + * status_reg: Status register offset for chained interrupt handler + * mask_reg: Mask register offset for irq chip + * mask: Mask to apply to the status register + * virq_base: Base virtual interrupt number + * nr_irqs: Number of interrupts handled by this block + * offset: Bit offset of the first interrupt + * irq_chip: Interrupt controller chip used for this instance, + * if NULL group is disabled, but accounted + */ +struct spear_shirq { + void __iomem *base; + u32 status_reg; + u32 mask_reg; + u32 mask; + u32 virq_base; + u32 nr_irqs; + u32 offset; + struct irq_chip *irq_chip; +}; /* spear300 shared irq registers offsets and masks */ #define SPEAR300_INT_ENB_MASK_REG 0x54 #define SPEAR300_INT_STS_MASK_REG 0x58 +static DEFINE_RAW_SPINLOCK(shirq_lock); + +static void shirq_irq_mask(struct irq_data *d) +{ + struct spear_shirq *shirq = irq_data_get_irq_chip_data(d); + u32 val, shift = d->irq - shirq->virq_base + shirq->offset; + u32 __iomem *reg = shirq->base + shirq->mask_reg; + + raw_spin_lock(&shirq_lock); + val = readl(reg) & ~(0x1 << shift); + writel(val, reg); + raw_spin_unlock(&shirq_lock); +} + +static void shirq_irq_unmask(struct irq_data *d) +{ + struct spear_shirq *shirq = irq_data_get_irq_chip_data(d); + u32 val, shift = d->irq - shirq->virq_base + shirq->offset; + u32 __iomem *reg = shirq->base + shirq->mask_reg; + + raw_spin_lock(&shirq_lock); + val = readl(reg) | (0x1 << shift); + writel(val, reg); + raw_spin_unlock(&shirq_lock); +} + +static struct irq_chip shirq_chip = { + .name = "spear-shirq", + .irq_mask = shirq_irq_mask, + .irq_unmask = shirq_irq_unmask, +}; + static struct spear_shirq spear300_shirq_ras1 = { - .irq_nr = 9, - .irq_bit_off = 0, - .regs = { - .enb_reg = SPEAR300_INT_ENB_MASK_REG, - .status_reg = SPEAR300_INT_STS_MASK_REG, - .clear_reg = -1, - }, + .offset = 0, + .nr_irqs = 9, + .mask = ((0x1 << 9) - 1) << 0, + .irq_chip = &shirq_chip, + .status_reg = SPEAR300_INT_STS_MASK_REG, + .mask_reg = SPEAR300_INT_ENB_MASK_REG, }; static struct spear_shirq *spear300_shirq_blocks[] = { @@ -51,43 +103,35 @@ static struct spear_shirq *spear300_shirq_blocks[] = { #define SPEAR310_INT_STS_MASK_REG 0x04 static struct spear_shirq spear310_shirq_ras1 = { - .irq_nr = 8, - .irq_bit_off = 0, - .regs = { - .enb_reg = -1, - .status_reg = SPEAR310_INT_STS_MASK_REG, - .clear_reg = -1, - }, + .offset = 0, + .nr_irqs = 8, + .mask = ((0x1 << 8) - 1) << 0, + .irq_chip = &dummy_irq_chip, + .status_reg = SPEAR310_INT_STS_MASK_REG, }; static struct spear_shirq spear310_shirq_ras2 = { - .irq_nr = 5, - .irq_bit_off = 8, - .regs = { - .enb_reg = -1, - .status_reg = SPEAR310_INT_STS_MASK_REG, - .clear_reg = -1, - }, + .offset = 8, + .nr_irqs = 5, + .mask = ((0x1 << 5) - 1) << 8, + .irq_chip = &dummy_irq_chip, + .status_reg = SPEAR310_INT_STS_MASK_REG, }; static struct spear_shirq spear310_shirq_ras3 = { - .irq_nr = 1, - .irq_bit_off = 13, - .regs = { - .enb_reg = -1, - .status_reg = SPEAR310_INT_STS_MASK_REG, - .clear_reg = -1, - }, + .offset = 13, + .nr_irqs = 1, + .mask = ((0x1 << 1) - 1) << 13, + .irq_chip = &dummy_irq_chip, + .status_reg = SPEAR310_INT_STS_MASK_REG, }; static struct spear_shirq spear310_shirq_intrcomm_ras = { - .irq_nr = 3, - .irq_bit_off = 14, - .regs = { - .enb_reg = -1, - .status_reg = SPEAR310_INT_STS_MASK_REG, - .clear_reg = -1, - }, + .offset = 14, + .nr_irqs = 3, + .mask = ((0x1 << 3) - 1) << 14, + .irq_chip = &dummy_irq_chip, + .status_reg = SPEAR310_INT_STS_MASK_REG, }; static struct spear_shirq *spear310_shirq_blocks[] = { @@ -102,50 +146,34 @@ static struct spear_shirq *spear310_shirq_blocks[] = { #define SPEAR320_INT_CLR_MASK_REG 0x04 #define SPEAR320_INT_ENB_MASK_REG 0x08 -static struct spear_shirq spear320_shirq_ras1 = { - .irq_nr = 3, - .irq_bit_off = 7, - .regs = { - .enb_reg = -1, - .status_reg = SPEAR320_INT_STS_MASK_REG, - .clear_reg = SPEAR320_INT_CLR_MASK_REG, - .reset_to_clear = 1, - }, +static struct spear_shirq spear320_shirq_ras3 = { + .offset = 0, + .nr_irqs = 7, + .mask = ((0x1 << 7) - 1) << 0, }; -static struct spear_shirq spear320_shirq_ras2 = { - .irq_nr = 1, - .irq_bit_off = 10, - .regs = { - .enb_reg = -1, - .status_reg = SPEAR320_INT_STS_MASK_REG, - .clear_reg = SPEAR320_INT_CLR_MASK_REG, - .reset_to_clear = 1, - }, +static struct spear_shirq spear320_shirq_ras1 = { + .offset = 7, + .nr_irqs = 3, + .mask = ((0x1 << 3) - 1) << 7, + .irq_chip = &dummy_irq_chip, + .status_reg = SPEAR320_INT_STS_MASK_REG, }; -static struct spear_shirq spear320_shirq_ras3 = { - .irq_nr = 3, - .irq_bit_off = 0, - .invalid_irq = 1, - .regs = { - .enb_reg = SPEAR320_INT_ENB_MASK_REG, - .reset_to_enb = 1, - .status_reg = SPEAR320_INT_STS_MASK_REG, - .clear_reg = SPEAR320_INT_CLR_MASK_REG, - .reset_to_clear = 1, - }, +static struct spear_shirq spear320_shirq_ras2 = { + .offset = 10, + .nr_irqs = 1, + .mask = ((0x1 << 1) - 1) << 10, + .irq_chip = &dummy_irq_chip, + .status_reg = SPEAR320_INT_STS_MASK_REG, }; static struct spear_shirq spear320_shirq_intrcomm_ras = { - .irq_nr = 11, - .irq_bit_off = 11, - .regs = { - .enb_reg = -1, - .status_reg = SPEAR320_INT_STS_MASK_REG, - .clear_reg = SPEAR320_INT_CLR_MASK_REG, - .reset_to_clear = 1, - }, + .offset = 11, + .nr_irqs = 11, + .mask = ((0x1 << 11) - 1) << 11, + .irq_chip = &dummy_irq_chip, + .status_reg = SPEAR320_INT_STS_MASK_REG, }; static struct spear_shirq *spear320_shirq_blocks[] = { @@ -155,104 +183,46 @@ static struct spear_shirq *spear320_shirq_blocks[] = { &spear320_shirq_intrcomm_ras, }; -static void shirq_irq_mask_unmask(struct irq_data *d, bool mask) -{ - struct spear_shirq *shirq = irq_data_get_irq_chip_data(d); - u32 val, offset = d->irq - shirq->irq_base; - unsigned long flags; - - if (shirq->regs.enb_reg == -1) - return; - - spin_lock_irqsave(&lock, flags); - val = readl(shirq->base + shirq->regs.enb_reg); - - if (mask ^ shirq->regs.reset_to_enb) - val &= ~(0x1 << shirq->irq_bit_off << offset); - else - val |= 0x1 << shirq->irq_bit_off << offset; - - writel(val, shirq->base + shirq->regs.enb_reg); - spin_unlock_irqrestore(&lock, flags); - -} - -static void shirq_irq_mask(struct irq_data *d) -{ - shirq_irq_mask_unmask(d, 1); -} - -static void shirq_irq_unmask(struct irq_data *d) -{ - shirq_irq_mask_unmask(d, 0); -} - -static struct irq_chip shirq_chip = { - .name = "spear-shirq", - .irq_ack = shirq_irq_mask, - .irq_mask = shirq_irq_mask, - .irq_unmask = shirq_irq_unmask, -}; - static void shirq_handler(unsigned irq, struct irq_desc *desc) { - u32 i, j, val, mask, tmp; - struct irq_chip *chip; struct spear_shirq *shirq = irq_get_handler_data(irq); + u32 pend; - chip = irq_get_chip(irq); - chip->irq_ack(&desc->irq_data); - - mask = ((0x1 << shirq->irq_nr) - 1) << shirq->irq_bit_off; - while ((val = readl(shirq->base + shirq->regs.status_reg) & - mask)) { - - val >>= shirq->irq_bit_off; - for (i = 0, j = 1; i < shirq->irq_nr; i++, j <<= 1) { - - if (!(j & val)) - continue; + pend = readl(shirq->base + shirq->status_reg) & shirq->mask; + pend >>= shirq->offset; - generic_handle_irq(shirq->irq_base + i); + while (pend) { + int irq = __ffs(pend); - /* clear interrupt */ - if (shirq->regs.clear_reg == -1) - continue; - - tmp = readl(shirq->base + shirq->regs.clear_reg); - if (shirq->regs.reset_to_clear) - tmp &= ~(j << shirq->irq_bit_off); - else - tmp |= (j << shirq->irq_bit_off); - writel(tmp, shirq->base + shirq->regs.clear_reg); - } + pend &= ~(0x1 << irq); + generic_handle_irq(shirq->virq_base + irq); } - chip->irq_unmask(&desc->irq_data); } -static void __init spear_shirq_register(struct spear_shirq *shirq) +static void __init spear_shirq_register(struct spear_shirq *shirq, + int parent_irq) { int i; - if (shirq->invalid_irq) + if (!shirq->irq_chip) return; - irq_set_chained_handler(shirq->irq, shirq_handler); - for (i = 0; i < shirq->irq_nr; i++) { - irq_set_chip_and_handler(shirq->irq_base + i, - &shirq_chip, handle_simple_irq); - set_irq_flags(shirq->irq_base + i, IRQF_VALID); - irq_set_chip_data(shirq->irq_base + i, shirq); - } + irq_set_chained_handler(parent_irq, shirq_handler); + irq_set_handler_data(parent_irq, shirq); - irq_set_handler_data(shirq->irq, shirq); + for (i = 0; i < shirq->nr_irqs; i++) { + irq_set_chip_and_handler(shirq->virq_base + i, + shirq->irq_chip, handle_simple_irq); + set_irq_flags(shirq->virq_base + i, IRQF_VALID); + irq_set_chip_data(shirq->virq_base + i, shirq); + } } static int __init shirq_init(struct spear_shirq **shirq_blocks, int block_nr, struct device_node *np) { - int i, irq_base, hwirq = 0, irq_nr = 0; - static struct irq_domain *shirq_domain; + int i, parent_irq, virq_base, hwirq = 0, nr_irqs = 0; + struct irq_domain *shirq_domain; void __iomem *base; base = of_iomap(np, 0); @@ -262,15 +232,15 @@ static int __init shirq_init(struct spear_shirq **shirq_blocks, int block_nr, } for (i = 0; i < block_nr; i++) - irq_nr += shirq_blocks[i]->irq_nr; + nr_irqs += shirq_blocks[i]->nr_irqs; - irq_base = irq_alloc_descs(-1, 0, irq_nr, 0); - if (IS_ERR_VALUE(irq_base)) { + virq_base = irq_alloc_descs(-1, 0, nr_irqs, 0); + if (IS_ERR_VALUE(virq_base)) { pr_err("%s: irq desc alloc failed\n", __func__); goto err_unmap; } - shirq_domain = irq_domain_add_legacy(np, irq_nr, irq_base, 0, + shirq_domain = irq_domain_add_legacy(np, nr_irqs, virq_base, 0, &irq_domain_simple_ops, NULL); if (WARN_ON(!shirq_domain)) { pr_warn("%s: irq domain init failed\n", __func__); @@ -279,41 +249,41 @@ static int __init shirq_init(struct spear_shirq **shirq_blocks, int block_nr, for (i = 0; i < block_nr; i++) { shirq_blocks[i]->base = base; - shirq_blocks[i]->irq_base = irq_find_mapping(shirq_domain, + shirq_blocks[i]->virq_base = irq_find_mapping(shirq_domain, hwirq); - shirq_blocks[i]->irq = irq_of_parse_and_map(np, i); - spear_shirq_register(shirq_blocks[i]); - hwirq += shirq_blocks[i]->irq_nr; + parent_irq = irq_of_parse_and_map(np, i); + spear_shirq_register(shirq_blocks[i], parent_irq); + hwirq += shirq_blocks[i]->nr_irqs; } return 0; err_free_desc: - irq_free_descs(irq_base, irq_nr); + irq_free_descs(virq_base, nr_irqs); err_unmap: iounmap(base); return -ENXIO; } -int __init spear300_shirq_of_init(struct device_node *np, - struct device_node *parent) +static int __init spear300_shirq_of_init(struct device_node *np, + struct device_node *parent) { return shirq_init(spear300_shirq_blocks, ARRAY_SIZE(spear300_shirq_blocks), np); } IRQCHIP_DECLARE(spear300_shirq, "st,spear300-shirq", spear300_shirq_of_init); -int __init spear310_shirq_of_init(struct device_node *np, - struct device_node *parent) +static int __init spear310_shirq_of_init(struct device_node *np, + struct device_node *parent) { return shirq_init(spear310_shirq_blocks, ARRAY_SIZE(spear310_shirq_blocks), np); } IRQCHIP_DECLARE(spear310_shirq, "st,spear310-shirq", spear310_shirq_of_init); -int __init spear320_shirq_of_init(struct device_node *np, - struct device_node *parent) +static int __init spear320_shirq_of_init(struct device_node *np, + struct device_node *parent) { return shirq_init(spear320_shirq_blocks, ARRAY_SIZE(spear320_shirq_blocks), np); diff --git a/include/linux/irqchip/spear-shirq.h b/include/linux/irqchip/spear-shirq.h deleted file mode 100644 index c8be16d213a3..000000000000 --- a/include/linux/irqchip/spear-shirq.h +++ /dev/null @@ -1,64 +0,0 @@ -/* - * SPEAr platform shared irq layer header file - * - * Copyright (C) 2009-2012 ST Microelectronics - * Viresh Kumar <viresh.linux@gmail.com> - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. - */ - -#ifndef __SPEAR_SHIRQ_H -#define __SPEAR_SHIRQ_H - -#include <linux/irq.h> -#include <linux/types.h> - -/* - * struct shirq_regs: shared irq register configuration - * - * enb_reg: enable register offset - * reset_to_enb: val 1 indicates, we need to clear bit for enabling interrupt - * status_reg: status register offset - * status_reg_mask: status register valid mask - * clear_reg: clear register offset - * reset_to_clear: val 1 indicates, we need to clear bit for clearing interrupt - */ -struct shirq_regs { - u32 enb_reg; - u32 reset_to_enb; - u32 status_reg; - u32 clear_reg; - u32 reset_to_clear; -}; - -/* - * struct spear_shirq: shared irq structure - * - * irq: hardware irq number - * irq_base: base irq in linux domain - * irq_nr: no. of shared interrupts in a particular block - * irq_bit_off: starting bit offset in the status register - * invalid_irq: irq group is currently disabled - * base: base address of shared irq register - * regs: register configuration for shared irq block - */ -struct spear_shirq { - u32 irq; - u32 irq_base; - u32 irq_nr; - u32 irq_bit_off; - int invalid_irq; - void __iomem *base; - struct shirq_regs regs; -}; - -int __init spear300_shirq_of_init(struct device_node *np, - struct device_node *parent); -int __init spear310_shirq_of_init(struct device_node *np, - struct device_node *parent); -int __init spear320_shirq_of_init(struct device_node *np, - struct device_node *parent); - -#endif /* __SPEAR_SHIRQ_H */ |