From: Linus Walleij Date: Wed, 31 Oct 2012 21:04:31 +0000 (+0100) Subject: ARM: plat-versatile: move FPGA irq driver to drivers/irqchip X-Git-Url: http://git.cdn.openwrt.org/?a=commitdiff_plain;h=2389d5014342e9535aad212d0c68d439aaf534ba;p=openwrt%2Fstaging%2Fblogic.git ARM: plat-versatile: move FPGA irq driver to drivers/irqchip This moves the Versatile FPGA interrupt controller driver, used in the Integrator/AP, Integrator/CP and some Versatile boards, out of arch/arm/plat-versatile and down to drivers/irqchip where we have consensus that such drivers belong. The header file is consequently moved to . Signed-off-by: Linus Walleij --- diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 73067efd4845..2205e3eb55bf 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -284,8 +284,8 @@ config ARCH_INTEGRATOR select MULTI_IRQ_HANDLER select NEED_MACH_MEMORY_H select PLAT_VERSATILE - select PLAT_VERSATILE_FPGA_IRQ select SPARSE_IRQ + select VERSATILE_FPGA_IRQ help Support for ARM's Integrator platform. @@ -318,7 +318,7 @@ config ARCH_VERSATILE select PLAT_VERSATILE select PLAT_VERSATILE_CLCD select PLAT_VERSATILE_CLOCK - select PLAT_VERSATILE_FPGA_IRQ + select VERSATILE_FPGA_IRQ help This enables support for ARM Ltd Versatile board. diff --git a/arch/arm/mach-integrator/integrator_ap.c b/arch/arm/mach-integrator/integrator_ap.c index 4f13bc57e5a4..e67a9fe18d1b 100644 --- a/arch/arm/mach-integrator/integrator_ap.c +++ b/arch/arm/mach-integrator/integrator_ap.c @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -56,8 +57,6 @@ #include #include -#include - #include "common.h" /* diff --git a/arch/arm/mach-integrator/integrator_cp.c b/arch/arm/mach-integrator/integrator_cp.c index 4423bc8e84c5..acecf04f50f7 100644 --- a/arch/arm/mach-integrator/integrator_cp.c +++ b/arch/arm/mach-integrator/integrator_cp.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -46,7 +47,6 @@ #include #include -#include #include #include "common.h" diff --git a/arch/arm/mach-versatile/core.c b/arch/arm/mach-versatile/core.c index 5b5c1eeb5b5c..5d5929450366 100644 --- a/arch/arm/mach-versatile/core.c +++ b/arch/arm/mach-versatile/core.c @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include @@ -51,7 +52,6 @@ #include #include -#include #include #include "core.h" diff --git a/arch/arm/plat-versatile/Kconfig b/arch/arm/plat-versatile/Kconfig index 2a4ae8a6a081..619f0fa0f06c 100644 --- a/arch/arm/plat-versatile/Kconfig +++ b/arch/arm/plat-versatile/Kconfig @@ -6,15 +6,6 @@ config PLAT_VERSATILE_CLOCK config PLAT_VERSATILE_CLCD bool -config PLAT_VERSATILE_FPGA_IRQ - bool - select IRQ_DOMAIN - -config PLAT_VERSATILE_FPGA_IRQ_NR - int - default 4 - depends on PLAT_VERSATILE_FPGA_IRQ - config PLAT_VERSATILE_LEDS def_bool y if NEW_LEDS depends on ARCH_REALVIEW || ARCH_VERSATILE diff --git a/arch/arm/plat-versatile/Makefile b/arch/arm/plat-versatile/Makefile index 74cfd94cbf80..f88d448b629c 100644 --- a/arch/arm/plat-versatile/Makefile +++ b/arch/arm/plat-versatile/Makefile @@ -2,7 +2,6 @@ ccflags-$(CONFIG_ARCH_MULTIPLATFORM) := -I$(srctree)/$(src)/include obj-$(CONFIG_PLAT_VERSATILE_CLOCK) += clock.o obj-$(CONFIG_PLAT_VERSATILE_CLCD) += clcd.o -obj-$(CONFIG_PLAT_VERSATILE_FPGA_IRQ) += fpga-irq.o obj-$(CONFIG_PLAT_VERSATILE_LEDS) += leds.o obj-$(CONFIG_PLAT_VERSATILE_SCHED_CLOCK) += sched-clock.o obj-$(CONFIG_SMP) += headsmp.o platsmp.o diff --git a/arch/arm/plat-versatile/fpga-irq.c b/arch/arm/plat-versatile/fpga-irq.c deleted file mode 100644 index dfe317c20354..000000000000 --- a/arch/arm/plat-versatile/fpga-irq.c +++ /dev/null @@ -1,204 +0,0 @@ -/* - * Support for Versatile FPGA-based IRQ controllers - */ -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#define IRQ_STATUS 0x00 -#define IRQ_RAW_STATUS 0x04 -#define IRQ_ENABLE_SET 0x08 -#define IRQ_ENABLE_CLEAR 0x0c -#define INT_SOFT_SET 0x10 -#define INT_SOFT_CLEAR 0x14 -#define FIQ_STATUS 0x20 -#define FIQ_RAW_STATUS 0x24 -#define FIQ_ENABLE 0x28 -#define FIQ_ENABLE_SET 0x28 -#define FIQ_ENABLE_CLEAR 0x2C - -/** - * struct fpga_irq_data - irq data container for the FPGA IRQ controller - * @base: memory offset in virtual memory - * @chip: chip container for this instance - * @domain: IRQ domain for this instance - * @valid: mask for valid IRQs on this controller - * @used_irqs: number of active IRQs on this controller - */ -struct fpga_irq_data { - void __iomem *base; - struct irq_chip chip; - u32 valid; - struct irq_domain *domain; - u8 used_irqs; -}; - -/* we cannot allocate memory when the controllers are initially registered */ -static struct fpga_irq_data fpga_irq_devices[CONFIG_PLAT_VERSATILE_FPGA_IRQ_NR]; -static int fpga_irq_id; - -static void fpga_irq_mask(struct irq_data *d) -{ - struct fpga_irq_data *f = irq_data_get_irq_chip_data(d); - u32 mask = 1 << d->hwirq; - - writel(mask, f->base + IRQ_ENABLE_CLEAR); -} - -static void fpga_irq_unmask(struct irq_data *d) -{ - struct fpga_irq_data *f = irq_data_get_irq_chip_data(d); - u32 mask = 1 << d->hwirq; - - writel(mask, f->base + IRQ_ENABLE_SET); -} - -static void fpga_irq_handle(unsigned int irq, struct irq_desc *desc) -{ - struct fpga_irq_data *f = irq_desc_get_handler_data(desc); - u32 status = readl(f->base + IRQ_STATUS); - - if (status == 0) { - do_bad_IRQ(irq, desc); - return; - } - - do { - irq = ffs(status) - 1; - status &= ~(1 << irq); - generic_handle_irq(irq_find_mapping(f->domain, irq)); - } while (status); -} - -/* - * Handle each interrupt in a single FPGA IRQ controller. Returns non-zero - * if we've handled at least one interrupt. This does a single read of the - * status register and handles all interrupts in order from LSB first. - */ -static int handle_one_fpga(struct fpga_irq_data *f, struct pt_regs *regs) -{ - int handled = 0; - int irq; - u32 status; - - while ((status = readl(f->base + IRQ_STATUS))) { - irq = ffs(status) - 1; - handle_IRQ(irq_find_mapping(f->domain, irq), regs); - handled = 1; - } - - return handled; -} - -/* - * Keep iterating over all registered FPGA IRQ controllers until there are - * no pending interrupts. - */ -asmlinkage void __exception_irq_entry fpga_handle_irq(struct pt_regs *regs) -{ - int i, handled; - - do { - for (i = 0, handled = 0; i < fpga_irq_id; ++i) - handled |= handle_one_fpga(&fpga_irq_devices[i], regs); - } while (handled); -} - -static int fpga_irqdomain_map(struct irq_domain *d, unsigned int irq, - irq_hw_number_t hwirq) -{ - struct fpga_irq_data *f = d->host_data; - - /* Skip invalid IRQs, only register handlers for the real ones */ - if (!(f->valid & BIT(hwirq))) - return -ENOTSUPP; - irq_set_chip_data(irq, f); - irq_set_chip_and_handler(irq, &f->chip, - handle_level_irq); - set_irq_flags(irq, IRQF_VALID | IRQF_PROBE); - return 0; -} - -static struct irq_domain_ops fpga_irqdomain_ops = { - .map = fpga_irqdomain_map, - .xlate = irq_domain_xlate_onetwocell, -}; - -void __init fpga_irq_init(void __iomem *base, const char *name, int irq_start, - int parent_irq, u32 valid, struct device_node *node) -{ - struct fpga_irq_data *f; - int i; - - if (fpga_irq_id >= ARRAY_SIZE(fpga_irq_devices)) { - pr_err("%s: too few FPGA IRQ controllers, increase CONFIG_PLAT_VERSATILE_FPGA_IRQ_NR\n", __func__); - return; - } - f = &fpga_irq_devices[fpga_irq_id]; - f->base = base; - f->chip.name = name; - f->chip.irq_ack = fpga_irq_mask; - f->chip.irq_mask = fpga_irq_mask; - f->chip.irq_unmask = fpga_irq_unmask; - f->valid = valid; - - if (parent_irq != -1) { - irq_set_handler_data(parent_irq, f); - irq_set_chained_handler(parent_irq, fpga_irq_handle); - } - - /* This will also allocate irq descriptors */ - f->domain = irq_domain_add_simple(node, fls(valid), irq_start, - &fpga_irqdomain_ops, f); - - /* This will allocate all valid descriptors in the linear case */ - for (i = 0; i < fls(valid); i++) - if (valid & BIT(i)) { - if (!irq_start) - irq_create_mapping(f->domain, i); - f->used_irqs++; - } - - pr_info("FPGA IRQ chip %d \"%s\" @ %p, %u irqs\n", - fpga_irq_id, name, base, f->used_irqs); - - fpga_irq_id++; -} - -#ifdef CONFIG_OF -int __init fpga_irq_of_init(struct device_node *node, - struct device_node *parent) -{ - struct fpga_irq_data *f; - void __iomem *base; - u32 clear_mask; - u32 valid_mask; - - if (WARN_ON(!node)) - return -ENODEV; - - base = of_iomap(node, 0); - WARN(!base, "unable to map fpga irq registers\n"); - - if (of_property_read_u32(node, "clear-mask", &clear_mask)) - clear_mask = 0; - - if (of_property_read_u32(node, "valid-mask", &valid_mask)) - valid_mask = 0; - - fpga_irq_init(base, node->name, 0, -1, valid_mask, node); - - writel(clear_mask, base + IRQ_ENABLE_CLEAR); - writel(clear_mask, base + FIQ_ENABLE_CLEAR); - - return 0; -} -#endif diff --git a/arch/arm/plat-versatile/include/plat/fpga-irq.h b/arch/arm/plat-versatile/include/plat/fpga-irq.h deleted file mode 100644 index 1fac9651d3ca..000000000000 --- a/arch/arm/plat-versatile/include/plat/fpga-irq.h +++ /dev/null @@ -1,13 +0,0 @@ -#ifndef PLAT_FPGA_IRQ_H -#define PLAT_FPGA_IRQ_H - -struct device_node; -struct pt_regs; - -void fpga_handle_irq(struct pt_regs *regs); -void fpga_irq_init(void __iomem *, const char *, int, int, u32, - struct device_node *node); -int fpga_irq_of_init(struct device_node *node, - struct device_node *parent); - -#endif diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig index 1bb8bf6d7fd4..62ca575701d3 100644 --- a/drivers/irqchip/Kconfig +++ b/drivers/irqchip/Kconfig @@ -1 +1,8 @@ -# empty +config VERSATILE_FPGA_IRQ + bool + select IRQ_DOMAIN + +config VERSATILE_FPGA_IRQ_NR + int + default 4 + depends on VERSATILE_FPGA_IRQ diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile index 054321db4350..e2e6eb5d32f4 100644 --- a/drivers/irqchip/Makefile +++ b/drivers/irqchip/Makefile @@ -1 +1,2 @@ obj-$(CONFIG_ARCH_BCM2835) += irq-bcm2835.o +obj-$(CONFIG_VERSATILE_FPGA_IRQ) += irq-versatile-fpga.o diff --git a/drivers/irqchip/irq-versatile-fpga.c b/drivers/irqchip/irq-versatile-fpga.c new file mode 100644 index 000000000000..789b3e526930 --- /dev/null +++ b/drivers/irqchip/irq-versatile-fpga.c @@ -0,0 +1,204 @@ +/* + * Support for Versatile FPGA-based IRQ controllers + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define IRQ_STATUS 0x00 +#define IRQ_RAW_STATUS 0x04 +#define IRQ_ENABLE_SET 0x08 +#define IRQ_ENABLE_CLEAR 0x0c +#define INT_SOFT_SET 0x10 +#define INT_SOFT_CLEAR 0x14 +#define FIQ_STATUS 0x20 +#define FIQ_RAW_STATUS 0x24 +#define FIQ_ENABLE 0x28 +#define FIQ_ENABLE_SET 0x28 +#define FIQ_ENABLE_CLEAR 0x2C + +/** + * struct fpga_irq_data - irq data container for the FPGA IRQ controller + * @base: memory offset in virtual memory + * @chip: chip container for this instance + * @domain: IRQ domain for this instance + * @valid: mask for valid IRQs on this controller + * @used_irqs: number of active IRQs on this controller + */ +struct fpga_irq_data { + void __iomem *base; + struct irq_chip chip; + u32 valid; + struct irq_domain *domain; + u8 used_irqs; +}; + +/* we cannot allocate memory when the controllers are initially registered */ +static struct fpga_irq_data fpga_irq_devices[CONFIG_VERSATILE_FPGA_IRQ_NR]; +static int fpga_irq_id; + +static void fpga_irq_mask(struct irq_data *d) +{ + struct fpga_irq_data *f = irq_data_get_irq_chip_data(d); + u32 mask = 1 << d->hwirq; + + writel(mask, f->base + IRQ_ENABLE_CLEAR); +} + +static void fpga_irq_unmask(struct irq_data *d) +{ + struct fpga_irq_data *f = irq_data_get_irq_chip_data(d); + u32 mask = 1 << d->hwirq; + + writel(mask, f->base + IRQ_ENABLE_SET); +} + +static void fpga_irq_handle(unsigned int irq, struct irq_desc *desc) +{ + struct fpga_irq_data *f = irq_desc_get_handler_data(desc); + u32 status = readl(f->base + IRQ_STATUS); + + if (status == 0) { + do_bad_IRQ(irq, desc); + return; + } + + do { + irq = ffs(status) - 1; + status &= ~(1 << irq); + generic_handle_irq(irq_find_mapping(f->domain, irq)); + } while (status); +} + +/* + * Handle each interrupt in a single FPGA IRQ controller. Returns non-zero + * if we've handled at least one interrupt. This does a single read of the + * status register and handles all interrupts in order from LSB first. + */ +static int handle_one_fpga(struct fpga_irq_data *f, struct pt_regs *regs) +{ + int handled = 0; + int irq; + u32 status; + + while ((status = readl(f->base + IRQ_STATUS))) { + irq = ffs(status) - 1; + handle_IRQ(irq_find_mapping(f->domain, irq), regs); + handled = 1; + } + + return handled; +} + +/* + * Keep iterating over all registered FPGA IRQ controllers until there are + * no pending interrupts. + */ +asmlinkage void __exception_irq_entry fpga_handle_irq(struct pt_regs *regs) +{ + int i, handled; + + do { + for (i = 0, handled = 0; i < fpga_irq_id; ++i) + handled |= handle_one_fpga(&fpga_irq_devices[i], regs); + } while (handled); +} + +static int fpga_irqdomain_map(struct irq_domain *d, unsigned int irq, + irq_hw_number_t hwirq) +{ + struct fpga_irq_data *f = d->host_data; + + /* Skip invalid IRQs, only register handlers for the real ones */ + if (!(f->valid & BIT(hwirq))) + return -ENOTSUPP; + irq_set_chip_data(irq, f); + irq_set_chip_and_handler(irq, &f->chip, + handle_level_irq); + set_irq_flags(irq, IRQF_VALID | IRQF_PROBE); + return 0; +} + +static struct irq_domain_ops fpga_irqdomain_ops = { + .map = fpga_irqdomain_map, + .xlate = irq_domain_xlate_onetwocell, +}; + +void __init fpga_irq_init(void __iomem *base, const char *name, int irq_start, + int parent_irq, u32 valid, struct device_node *node) +{ + struct fpga_irq_data *f; + int i; + + if (fpga_irq_id >= ARRAY_SIZE(fpga_irq_devices)) { + pr_err("%s: too few FPGA IRQ controllers, increase CONFIG_PLAT_VERSATILE_FPGA_IRQ_NR\n", __func__); + return; + } + f = &fpga_irq_devices[fpga_irq_id]; + f->base = base; + f->chip.name = name; + f->chip.irq_ack = fpga_irq_mask; + f->chip.irq_mask = fpga_irq_mask; + f->chip.irq_unmask = fpga_irq_unmask; + f->valid = valid; + + if (parent_irq != -1) { + irq_set_handler_data(parent_irq, f); + irq_set_chained_handler(parent_irq, fpga_irq_handle); + } + + /* This will also allocate irq descriptors */ + f->domain = irq_domain_add_simple(node, fls(valid), irq_start, + &fpga_irqdomain_ops, f); + + /* This will allocate all valid descriptors in the linear case */ + for (i = 0; i < fls(valid); i++) + if (valid & BIT(i)) { + if (!irq_start) + irq_create_mapping(f->domain, i); + f->used_irqs++; + } + + pr_info("FPGA IRQ chip %d \"%s\" @ %p, %u irqs\n", + fpga_irq_id, name, base, f->used_irqs); + + fpga_irq_id++; +} + +#ifdef CONFIG_OF +int __init fpga_irq_of_init(struct device_node *node, + struct device_node *parent) +{ + struct fpga_irq_data *f; + void __iomem *base; + u32 clear_mask; + u32 valid_mask; + + if (WARN_ON(!node)) + return -ENODEV; + + base = of_iomap(node, 0); + WARN(!base, "unable to map fpga irq registers\n"); + + if (of_property_read_u32(node, "clear-mask", &clear_mask)) + clear_mask = 0; + + if (of_property_read_u32(node, "valid-mask", &valid_mask)) + valid_mask = 0; + + fpga_irq_init(base, node->name, 0, -1, valid_mask, node); + + writel(clear_mask, base + IRQ_ENABLE_CLEAR); + writel(clear_mask, base + FIQ_ENABLE_CLEAR); + + return 0; +} +#endif diff --git a/include/linux/irqchip/versatile-fpga.h b/include/linux/irqchip/versatile-fpga.h new file mode 100644 index 000000000000..1fac9651d3ca --- /dev/null +++ b/include/linux/irqchip/versatile-fpga.h @@ -0,0 +1,13 @@ +#ifndef PLAT_FPGA_IRQ_H +#define PLAT_FPGA_IRQ_H + +struct device_node; +struct pt_regs; + +void fpga_handle_irq(struct pt_regs *regs); +void fpga_irq_init(void __iomem *, const char *, int, int, u32, + struct device_node *node); +int fpga_irq_of_init(struct device_node *node, + struct device_node *parent); + +#endif