diff options
Diffstat (limited to 'drivers/irqchip')
-rw-r--r-- | drivers/irqchip/Makefile | 2 | ||||
-rw-r--r-- | drivers/irqchip/irq-ath79-cpu.c | 97 | ||||
-rw-r--r-- | drivers/irqchip/irq-ath79-misc.c | 189 |
3 files changed, 288 insertions, 0 deletions
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile index be6ada6eaab3..f71bbc7906ba 100644 --- a/drivers/irqchip/Makefile +++ b/drivers/irqchip/Makefile @@ -1,5 +1,7 @@ obj-$(CONFIG_IRQCHIP) += irqchip.o +obj-$(CONFIG_ATH79) += irq-ath79-cpu.o +obj-$(CONFIG_ATH79) += irq-ath79-misc.o obj-$(CONFIG_ARCH_BCM2835) += irq-bcm2835.o obj-$(CONFIG_ARCH_BCM2835) += irq-bcm2836.o obj-$(CONFIG_ARCH_EXYNOS) += exynos-combiner.o diff --git a/drivers/irqchip/irq-ath79-cpu.c b/drivers/irqchip/irq-ath79-cpu.c new file mode 100644 index 000000000000..befe93c5a51a --- /dev/null +++ b/drivers/irqchip/irq-ath79-cpu.c @@ -0,0 +1,97 @@ +/* + * Atheros AR71xx/AR724x/AR913x specific interrupt handling + * + * Copyright (C) 2015 Alban Bedel <albeu@free.fr> + * Copyright (C) 2010-2011 Jaiganesh Narayanan <jnarayanan@atheros.com> + * Copyright (C) 2008-2011 Gabor Juhos <juhosg@openwrt.org> + * Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org> + * + * Parts of this file are based on Atheros' 2.6.15/2.6.31 BSP + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + */ + +#include <linux/interrupt.h> +#include <linux/irqchip.h> +#include <linux/of.h> + +#include <asm/irq_cpu.h> +#include <asm/mach-ath79/ath79.h> + +/* + * The IP2/IP3 lines are tied to a PCI/WMAC/USB device. Drivers for + * these devices typically allocate coherent DMA memory, however the + * DMA controller may still have some unsynchronized data in the FIFO. + * Issue a flush in the handlers to ensure that the driver sees + * the update. + * + * This array map the interrupt lines to the DDR write buffer channels. + */ + +static unsigned irq_wb_chan[8] = { + -1, -1, -1, -1, -1, -1, -1, -1, +}; + +asmlinkage void plat_irq_dispatch(void) +{ + unsigned long pending; + int irq; + + pending = read_c0_status() & read_c0_cause() & ST0_IM; + + if (!pending) { + spurious_interrupt(); + return; + } + + pending >>= CAUSEB_IP; + while (pending) { + irq = fls(pending) - 1; + if (irq < ARRAY_SIZE(irq_wb_chan) && irq_wb_chan[irq] != -1) + ath79_ddr_wb_flush(irq_wb_chan[irq]); + do_IRQ(MIPS_CPU_IRQ_BASE + irq); + pending &= ~BIT(irq); + } +} + +static int __init ar79_cpu_intc_of_init( + struct device_node *node, struct device_node *parent) +{ + int err, i, count; + + /* Fill the irq_wb_chan table */ + count = of_count_phandle_with_args( + node, "qca,ddr-wb-channels", "#qca,ddr-wb-channel-cells"); + + for (i = 0; i < count; i++) { + struct of_phandle_args args; + u32 irq = i; + + of_property_read_u32_index( + node, "qca,ddr-wb-channel-interrupts", i, &irq); + if (irq >= ARRAY_SIZE(irq_wb_chan)) + continue; + + err = of_parse_phandle_with_args( + node, "qca,ddr-wb-channels", + "#qca,ddr-wb-channel-cells", + i, &args); + if (err) + return err; + + irq_wb_chan[irq] = args.args[0]; + } + + return mips_cpu_irq_of_init(node, parent); +} +IRQCHIP_DECLARE(ar79_cpu_intc, "qca,ar7100-cpu-intc", + ar79_cpu_intc_of_init); + +void __init ath79_cpu_irq_init(unsigned irq_wb_chan2, unsigned irq_wb_chan3) +{ + irq_wb_chan[2] = irq_wb_chan2; + irq_wb_chan[3] = irq_wb_chan3; + mips_cpu_irq_init(); +} diff --git a/drivers/irqchip/irq-ath79-misc.c b/drivers/irqchip/irq-ath79-misc.c new file mode 100644 index 000000000000..aa7290784636 --- /dev/null +++ b/drivers/irqchip/irq-ath79-misc.c @@ -0,0 +1,189 @@ +/* + * Atheros AR71xx/AR724x/AR913x MISC interrupt controller + * + * Copyright (C) 2015 Alban Bedel <albeu@free.fr> + * Copyright (C) 2010-2011 Jaiganesh Narayanan <jnarayanan@atheros.com> + * Copyright (C) 2008-2011 Gabor Juhos <juhosg@openwrt.org> + * Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org> + * + * Parts of this file are based on Atheros' 2.6.15/2.6.31 BSP + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + */ + +#include <linux/irqchip.h> +#include <linux/irqchip/chained_irq.h> +#include <linux/of_address.h> +#include <linux/of_irq.h> + +#define AR71XX_RESET_REG_MISC_INT_STATUS 0 +#define AR71XX_RESET_REG_MISC_INT_ENABLE 4 + +#define ATH79_MISC_IRQ_COUNT 32 + +static void ath79_misc_irq_handler(struct irq_desc *desc) +{ + struct irq_domain *domain = irq_desc_get_handler_data(desc); + struct irq_chip *chip = irq_desc_get_chip(desc); + void __iomem *base = domain->host_data; + u32 pending; + + chained_irq_enter(chip, desc); + + pending = __raw_readl(base + AR71XX_RESET_REG_MISC_INT_STATUS) & + __raw_readl(base + AR71XX_RESET_REG_MISC_INT_ENABLE); + + if (!pending) { + spurious_interrupt(); + chained_irq_exit(chip, desc); + return; + } + + while (pending) { + int bit = __ffs(pending); + + generic_handle_irq(irq_linear_revmap(domain, bit)); + pending &= ~BIT(bit); + } + + chained_irq_exit(chip, desc); +} + +static void ar71xx_misc_irq_unmask(struct irq_data *d) +{ + void __iomem *base = irq_data_get_irq_chip_data(d); + unsigned int irq = d->hwirq; + u32 t; + + t = __raw_readl(base + AR71XX_RESET_REG_MISC_INT_ENABLE); + __raw_writel(t | BIT(irq), base + AR71XX_RESET_REG_MISC_INT_ENABLE); + + /* flush write */ + __raw_readl(base + AR71XX_RESET_REG_MISC_INT_ENABLE); +} + +static void ar71xx_misc_irq_mask(struct irq_data *d) +{ + void __iomem *base = irq_data_get_irq_chip_data(d); + unsigned int irq = d->hwirq; + u32 t; + + t = __raw_readl(base + AR71XX_RESET_REG_MISC_INT_ENABLE); + __raw_writel(t & ~BIT(irq), base + AR71XX_RESET_REG_MISC_INT_ENABLE); + + /* flush write */ + __raw_readl(base + AR71XX_RESET_REG_MISC_INT_ENABLE); +} + +static void ar724x_misc_irq_ack(struct irq_data *d) +{ + void __iomem *base = irq_data_get_irq_chip_data(d); + unsigned int irq = d->hwirq; + u32 t; + + t = __raw_readl(base + AR71XX_RESET_REG_MISC_INT_STATUS); + __raw_writel(t & ~BIT(irq), base + AR71XX_RESET_REG_MISC_INT_STATUS); + + /* flush write */ + __raw_readl(base + AR71XX_RESET_REG_MISC_INT_STATUS); +} + +static struct irq_chip ath79_misc_irq_chip = { + .name = "MISC", + .irq_unmask = ar71xx_misc_irq_unmask, + .irq_mask = ar71xx_misc_irq_mask, +}; + +static int misc_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hw) +{ + irq_set_chip_and_handler(irq, &ath79_misc_irq_chip, handle_level_irq); + irq_set_chip_data(irq, d->host_data); + return 0; +} + +static const struct irq_domain_ops misc_irq_domain_ops = { + .xlate = irq_domain_xlate_onecell, + .map = misc_map, +}; + +static void __init ath79_misc_intc_domain_init( + struct irq_domain *domain, int irq) +{ + void __iomem *base = domain->host_data; + + /* Disable and clear all interrupts */ + __raw_writel(0, base + AR71XX_RESET_REG_MISC_INT_ENABLE); + __raw_writel(0, base + AR71XX_RESET_REG_MISC_INT_STATUS); + + irq_set_chained_handler_and_data(irq, ath79_misc_irq_handler, domain); +} + +static int __init ath79_misc_intc_of_init( + struct device_node *node, struct device_node *parent) +{ + struct irq_domain *domain; + void __iomem *base; + int irq; + + irq = irq_of_parse_and_map(node, 0); + if (!irq) { + pr_err("Failed to get MISC IRQ\n"); + return -EINVAL; + } + + base = of_iomap(node, 0); + if (!base) { + pr_err("Failed to get MISC IRQ registers\n"); + return -ENOMEM; + } + + domain = irq_domain_add_linear(node, ATH79_MISC_IRQ_COUNT, + &misc_irq_domain_ops, base); + if (!domain) { + pr_err("Failed to add MISC irqdomain\n"); + return -EINVAL; + } + + ath79_misc_intc_domain_init(domain, irq); + return 0; +} + +static int __init ar7100_misc_intc_of_init( + struct device_node *node, struct device_node *parent) +{ + ath79_misc_irq_chip.irq_mask_ack = ar71xx_misc_irq_mask; + return ath79_misc_intc_of_init(node, parent); +} + +IRQCHIP_DECLARE(ar7100_misc_intc, "qca,ar7100-misc-intc", + ar7100_misc_intc_of_init); + +static int __init ar7240_misc_intc_of_init( + struct device_node *node, struct device_node *parent) +{ + ath79_misc_irq_chip.irq_ack = ar724x_misc_irq_ack; + return ath79_misc_intc_of_init(node, parent); +} + +IRQCHIP_DECLARE(ar7240_misc_intc, "qca,ar7240-misc-intc", + ar7240_misc_intc_of_init); + +void __init ath79_misc_irq_init(void __iomem *regs, int irq, + int irq_base, bool is_ar71xx) +{ + struct irq_domain *domain; + + if (is_ar71xx) + ath79_misc_irq_chip.irq_mask_ack = ar71xx_misc_irq_mask; + else + ath79_misc_irq_chip.irq_ack = ar724x_misc_irq_ack; + + domain = irq_domain_add_legacy(NULL, ATH79_MISC_IRQ_COUNT, + irq_base, 0, &misc_irq_domain_ops, regs); + if (!domain) + panic("Failed to create MISC irqdomain"); + + ath79_misc_intc_domain_init(domain, irq); +} |