This patch adds an irqchip driver for the main interrupt controller found on Marvell Orion SoCs (Kirkwood, Dove, Orion5x, Discovery Innovation). Corresponding device tree documentation is also added. Signed-off-by: Sebastian Hesselbarth <sebastian.hesselbarth@xxxxxxxxx> --- Note: This patch triggers a checkpatch warning for WARNING: Avoid CamelCase: <handle_IRQ> Cc: Grant Likely <grant.likely@xxxxxxxxxx> Cc: Rob Herring <rob.herring@xxxxxxxxxxx> Cc: Rob Landley <rob@xxxxxxxxxxx> Cc: Thomas Gleixner <tglx@xxxxxxxxxxxxx> Cc: Russell King <linux@xxxxxxxxxxxxxxxx> Cc: Arnd Bergmann <arnd@xxxxxxxx> Cc: Jason Cooper <jason@xxxxxxxxxxxxxx> Cc: Andrew Lunn <andrew@xxxxxxx> Cc: Thomas Petazzoni <thomas.petazzoni@xxxxxxxxxxxxxxxxxx> Cc: Gregory Clement <gregory.clement@xxxxxxxxxxxxxxxxxx> Cc: Ezequiel Garcia <ezequiel.garcia@xxxxxxxxxxxxxxxxxx> Cc: Jean-Francois Moine <moinejf@xxxxxxx> Cc: devicetree-discuss@xxxxxxxxxxxxxxxx Cc: linux-doc@xxxxxxxxxxxxxxx Cc: linux-arm-kernel@xxxxxxxxxxxxxxxxxxx Cc: linux-kernel@xxxxxxxxxxxxxxx --- .../interrupt-controller/marvell,orion-mpic.txt | 22 ++++ drivers/irqchip/Kconfig | 5 + drivers/irqchip/Makefile | 1 + drivers/irqchip/irq-orion.c | 129 ++++++++++++++++++++ include/linux/irqchip/orion.h | 18 +++ 5 files changed, 175 insertions(+), 0 deletions(-) create mode 100644 Documentation/devicetree/bindings/interrupt-controller/marvell,orion-mpic.txt create mode 100644 drivers/irqchip/irq-orion.c create mode 100644 include/linux/irqchip/orion.h diff --git a/Documentation/devicetree/bindings/interrupt-controller/marvell,orion-mpic.txt b/Documentation/devicetree/bindings/interrupt-controller/marvell,orion-mpic.txt new file mode 100644 index 0000000..3b303ec --- /dev/null +++ b/Documentation/devicetree/bindings/interrupt-controller/marvell,orion-mpic.txt @@ -0,0 +1,22 @@ +Marvell Orion SoC main interrupt controller + +Required properties: +- compatible: shall be "marvell,orion-mpic" +- reg: base address(es) of interrupt registers starting with CAUSE register +- interrupt-controller: identifies the node as an interrupt controller +- #interrupt-cells: number of cells to encode an interrupt source, shall be 1. + +The interrupt sources map to the corresponding bits in the interrupt +registers, i.e. +- 0 maps to bit 0 of first base address, +- 1 maps to bit 1 of first base address, +- 32 maps to bit 0 of second base address, and so on. + +Example: + intc: interrupt-controller { + compatible = "marvell,orion-mpic"; + interrupt-controller; + #interrupt-cells = <1>; + /* Dove has 64 primary interrupts */ + reg = <0x20200 0x10>, <0x20210 0x10>; + }; diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig index a350969..8da3559 100644 --- a/drivers/irqchip/Kconfig +++ b/drivers/irqchip/Kconfig @@ -2,6 +2,11 @@ config IRQCHIP def_bool y depends on OF_IRQ +config IRQCHIP_ORION + bool + select IRQ_DOMAIN + select MULTI_IRQ_HANDLER + config ARM_GIC bool select IRQ_DOMAIN diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile index 98e3b87..8adbd43 100644 --- a/drivers/irqchip/Makefile +++ b/drivers/irqchip/Makefile @@ -2,6 +2,7 @@ obj-$(CONFIG_IRQCHIP) += irqchip.o obj-$(CONFIG_ARCH_BCM2835) += irq-bcm2835.o obj-$(CONFIG_ARCH_EXYNOS) += exynos-combiner.o +obj-$(CONFIG_IRQCHIP_ORION) += irq-orion.o obj-$(CONFIG_METAG) += irq-metag-ext.o obj-$(CONFIG_METAG_PERFCOUNTER_IRQS) += irq-metag.o obj-$(CONFIG_ARCH_SUNXI) += irq-sunxi.o diff --git a/drivers/irqchip/irq-orion.c b/drivers/irqchip/irq-orion.c new file mode 100644 index 0000000..ea02e11 --- /dev/null +++ b/drivers/irqchip/irq-orion.c @@ -0,0 +1,129 @@ +/* + * Marvell Orion SoCs IRQ chip driver. + * + * Sebastian Hesselbarth <sebastian.hesselbarth@xxxxxxxxx> + * + * 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. + */ + +#include <linux/io.h> +#include <linux/irq.h> +#include <linux/irqchip/orion.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/of_irq.h> +#include <asm/mach/irq.h> + +/* max number of handled irq register blocks */ +#define ORION_MAX_IRQREG 2 + +#define ORION_IRQ_CAUSE 0x00 +#define ORION_IRQ_MASK 0x04 +#define ORION_IRQ_FIQ_MASK 0x08 +#define ORION_IRQ_ENDP_MASK 0x0c + +static void __iomem *orion_irq_base[ORION_MAX_IRQREG]; +static unsigned int orion_irq_regs; +static struct irq_domain *orion_irq_domain; + +asmlinkage void __exception_irq_entry orion_handle_irq(struct pt_regs *regs) +{ + int n; + for (n = 0; n < orion_irq_regs; n++) { + u32 hwirq_base = n * 32; + u32 stat = readl_relaxed(orion_irq_base[n] + ORION_IRQ_CAUSE) & + readl_relaxed(orion_irq_base[n] + ORION_IRQ_MASK); + while (stat) { + u32 hwirq = ffs(stat) - 1; + u32 irq = irq_find_mapping(orion_irq_domain, + hwirq_base + hwirq); + handle_IRQ(irq, regs); + stat &= ~(1 << hwirq); + } + } +} + +static void orion_irq_mask(struct irq_data *irqd) +{ + unsigned int irq = irqd_to_hwirq(irqd); + unsigned int irq_off = irq % 32; + int reg = irq / 32; + u32 val; + + val = readl(orion_irq_base[reg] + ORION_IRQ_MASK); + writel(val & ~(1 << irq_off), orion_irq_base[reg] + ORION_IRQ_MASK); +} + +static void orion_irq_unmask(struct irq_data *irqd) +{ + unsigned int irq = irqd_to_hwirq(irqd); + unsigned int irq_off = irq % 32; + int reg = irq / 32; + u32 val; + + val = readl(orion_irq_base[reg] + ORION_IRQ_MASK); + writel(val | (1 << irq_off), orion_irq_base[reg] + ORION_IRQ_MASK); +} + +static struct irq_chip orion_irq_chip = { + .name = "orion_irq", + .irq_mask = orion_irq_mask, + .irq_unmask = orion_irq_unmask, +}; + +static int orion_irq_map(struct irq_domain *d, unsigned int virq, + irq_hw_number_t hw) +{ + irq_set_chip_and_handler(virq, &orion_irq_chip, + handle_level_irq); + set_irq_flags(virq, IRQF_VALID | IRQF_PROBE); + + return 0; +} + +static struct irq_domain_ops orion_irq_ops = { + .map = orion_irq_map, + .xlate = irq_domain_xlate_onecell, +}; + +static int __init orion_of_init(struct device_node *np, + struct device_node *parent) +{ + int n; + + for (n = 0; n < ORION_MAX_IRQREG; n++) { + orion_irq_base[n] = of_iomap(np, n); + + if (!orion_irq_base[n]) + continue; + + /* mask all interrupts */ + writel(0, orion_irq_base[n] + ORION_IRQ_MASK); + orion_irq_regs++; + } + + /* at least one irq reg must be set */ + if (!orion_irq_regs) + panic("%s: unable to map IRQC registers\n", np->full_name); + + orion_irq_domain = irq_domain_add_linear(np, orion_irq_regs * 32, + &orion_irq_ops, NULL); + if (!orion_irq_domain) + panic("%s: unable to create IRQ domain\n", np->full_name); + + set_handle_irq(orion_handle_irq); + + return 0; +} + +static struct of_device_id orion_irq_dt_ids[] __initconst = { + { .compatible = "marvell,orion-mpic", .data = orion_of_init }, + { } +}; + +void __init orion_init_irq(void) +{ + of_irq_init(orion_irq_dt_ids); +} diff --git a/include/linux/irqchip/orion.h b/include/linux/irqchip/orion.h new file mode 100644 index 0000000..04f7bab --- /dev/null +++ b/include/linux/irqchip/orion.h @@ -0,0 +1,18 @@ +/* + * Marvell Orion SoCs IRQ chip driver header. + * + * Sebastian Hesselbarth <sebastian.hesselbarth@xxxxxxxxx> + * + * 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 __LINUX_IRQCHIP_ORION_H +#define __LINUX_IRQCHIP_ORION_H + +#include <asm/exception.h> + +extern void orion_init_irq(void); + +#endif -- 1.7.2.5 -- To unsubscribe from this list: send the line "unsubscribe linux-doc" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html