diff options
| -rw-r--r-- | arch/mips/ath79/irq.c | 244 | ||||
| -rw-r--r-- | arch/mips/include/asm/mach-ath79/ath79.h | 4 | ||||
| -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 |
5 files changed, 313 insertions, 223 deletions
diff --git a/arch/mips/ath79/irq.c b/arch/mips/ath79/irq.c index 511c06560dc1..2dfff1f19004 100644 --- a/arch/mips/ath79/irq.c +++ b/arch/mips/ath79/irq.c | |||
| @@ -26,90 +26,6 @@ | |||
| 26 | #include "common.h" | 26 | #include "common.h" |
| 27 | #include "machtypes.h" | 27 | #include "machtypes.h" |
| 28 | 28 | ||
| 29 | static void __init ath79_misc_intc_domain_init( | ||
| 30 | struct device_node *node, int irq); | ||
| 31 | |||
| 32 | static void ath79_misc_irq_handler(struct irq_desc *desc) | ||
| 33 | { | ||
| 34 | struct irq_domain *domain = irq_desc_get_handler_data(desc); | ||
| 35 | void __iomem *base = domain->host_data; | ||
| 36 | u32 pending; | ||
| 37 | |||
| 38 | pending = __raw_readl(base + AR71XX_RESET_REG_MISC_INT_STATUS) & | ||
| 39 | __raw_readl(base + AR71XX_RESET_REG_MISC_INT_ENABLE); | ||
| 40 | |||
| 41 | if (!pending) { | ||
| 42 | spurious_interrupt(); | ||
| 43 | return; | ||
| 44 | } | ||
| 45 | |||
| 46 | while (pending) { | ||
| 47 | int bit = __ffs(pending); | ||
| 48 | |||
| 49 | generic_handle_irq(irq_linear_revmap(domain, bit)); | ||
| 50 | pending &= ~BIT(bit); | ||
| 51 | } | ||
| 52 | } | ||
| 53 | |||
| 54 | static void ar71xx_misc_irq_unmask(struct irq_data *d) | ||
| 55 | { | ||
| 56 | void __iomem *base = irq_data_get_irq_chip_data(d); | ||
| 57 | unsigned int irq = d->hwirq; | ||
| 58 | u32 t; | ||
| 59 | |||
| 60 | t = __raw_readl(base + AR71XX_RESET_REG_MISC_INT_ENABLE); | ||
| 61 | __raw_writel(t | (1 << irq), base + AR71XX_RESET_REG_MISC_INT_ENABLE); | ||
| 62 | |||
| 63 | /* flush write */ | ||
| 64 | __raw_readl(base + AR71XX_RESET_REG_MISC_INT_ENABLE); | ||
| 65 | } | ||
| 66 | |||
| 67 | static void ar71xx_misc_irq_mask(struct irq_data *d) | ||
| 68 | { | ||
| 69 | void __iomem *base = irq_data_get_irq_chip_data(d); | ||
| 70 | unsigned int irq = d->hwirq; | ||
| 71 | u32 t; | ||
| 72 | |||
| 73 | t = __raw_readl(base + AR71XX_RESET_REG_MISC_INT_ENABLE); | ||
| 74 | __raw_writel(t & ~(1 << irq), base + AR71XX_RESET_REG_MISC_INT_ENABLE); | ||
| 75 | |||
| 76 | /* flush write */ | ||
| 77 | __raw_readl(base + AR71XX_RESET_REG_MISC_INT_ENABLE); | ||
| 78 | } | ||
| 79 | |||
| 80 | static void ar724x_misc_irq_ack(struct irq_data *d) | ||
| 81 | { | ||
| 82 | void __iomem *base = irq_data_get_irq_chip_data(d); | ||
| 83 | unsigned int irq = d->hwirq; | ||
| 84 | u32 t; | ||
| 85 | |||
| 86 | t = __raw_readl(base + AR71XX_RESET_REG_MISC_INT_STATUS); | ||
| 87 | __raw_writel(t & ~(1 << irq), base + AR71XX_RESET_REG_MISC_INT_STATUS); | ||
| 88 | |||
| 89 | /* flush write */ | ||
| 90 | __raw_readl(base + AR71XX_RESET_REG_MISC_INT_STATUS); | ||
| 91 | } | ||
| 92 | |||
| 93 | static struct irq_chip ath79_misc_irq_chip = { | ||
| 94 | .name = "MISC", | ||
| 95 | .irq_unmask = ar71xx_misc_irq_unmask, | ||
| 96 | .irq_mask = ar71xx_misc_irq_mask, | ||
| 97 | }; | ||
| 98 | |||
| 99 | static void __init ath79_misc_irq_init(void) | ||
| 100 | { | ||
| 101 | if (soc_is_ar71xx() || soc_is_ar913x()) | ||
| 102 | ath79_misc_irq_chip.irq_mask_ack = ar71xx_misc_irq_mask; | ||
| 103 | else if (soc_is_ar724x() || | ||
| 104 | soc_is_ar933x() || | ||
| 105 | soc_is_ar934x() || | ||
| 106 | soc_is_qca955x()) | ||
| 107 | ath79_misc_irq_chip.irq_ack = ar724x_misc_irq_ack; | ||
| 108 | else | ||
| 109 | BUG(); | ||
| 110 | |||
| 111 | ath79_misc_intc_domain_init(NULL, ATH79_CPU_IRQ(6)); | ||
| 112 | } | ||
| 113 | 29 | ||
| 114 | static void ar934x_ip2_irq_dispatch(struct irq_desc *desc) | 30 | static void ar934x_ip2_irq_dispatch(struct irq_desc *desc) |
| 115 | { | 31 | { |
| @@ -212,142 +128,12 @@ static void qca955x_irq_init(void) | |||
| 212 | irq_set_chained_handler(ATH79_CPU_IRQ(3), qca955x_ip3_irq_dispatch); | 128 | irq_set_chained_handler(ATH79_CPU_IRQ(3), qca955x_ip3_irq_dispatch); |
| 213 | } | 129 | } |
| 214 | 130 | ||
| 215 | /* | ||
| 216 | * The IP2/IP3 lines are tied to a PCI/WMAC/USB device. Drivers for | ||
| 217 | * these devices typically allocate coherent DMA memory, however the | ||
| 218 | * DMA controller may still have some unsynchronized data in the FIFO. | ||
| 219 | * Issue a flush in the handlers to ensure that the driver sees | ||
| 220 | * the update. | ||
| 221 | * | ||
| 222 | * This array map the interrupt lines to the DDR write buffer channels. | ||
| 223 | */ | ||
| 224 | |||
| 225 | static unsigned irq_wb_chan[8] = { | ||
| 226 | -1, -1, -1, -1, -1, -1, -1, -1, | ||
| 227 | }; | ||
| 228 | |||
| 229 | asmlinkage void plat_irq_dispatch(void) | ||
| 230 | { | ||
| 231 | unsigned long pending; | ||
| 232 | int irq; | ||
| 233 | |||
| 234 | pending = read_c0_status() & read_c0_cause() & ST0_IM; | ||
| 235 | |||
| 236 | if (!pending) { | ||
| 237 | spurious_interrupt(); | ||
| 238 | return; | ||
| 239 | } | ||
| 240 | |||
| 241 | pending >>= CAUSEB_IP; | ||
| 242 | while (pending) { | ||
| 243 | irq = fls(pending) - 1; | ||
| 244 | if (irq < ARRAY_SIZE(irq_wb_chan) && irq_wb_chan[irq] != -1) | ||
| 245 | ath79_ddr_wb_flush(irq_wb_chan[irq]); | ||
| 246 | do_IRQ(MIPS_CPU_IRQ_BASE + irq); | ||
| 247 | pending &= ~BIT(irq); | ||
| 248 | } | ||
| 249 | } | ||
| 250 | |||
| 251 | static int misc_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hw) | ||
| 252 | { | ||
| 253 | irq_set_chip_and_handler(irq, &ath79_misc_irq_chip, handle_level_irq); | ||
| 254 | irq_set_chip_data(irq, d->host_data); | ||
| 255 | return 0; | ||
| 256 | } | ||
| 257 | |||
| 258 | static const struct irq_domain_ops misc_irq_domain_ops = { | ||
| 259 | .xlate = irq_domain_xlate_onecell, | ||
| 260 | .map = misc_map, | ||
| 261 | }; | ||
| 262 | |||
| 263 | static void __init ath79_misc_intc_domain_init( | ||
| 264 | struct device_node *node, int irq) | ||
| 265 | { | ||
| 266 | void __iomem *base = ath79_reset_base; | ||
| 267 | struct irq_domain *domain; | ||
| 268 | |||
| 269 | domain = irq_domain_add_legacy(node, ATH79_MISC_IRQ_COUNT, | ||
| 270 | ATH79_MISC_IRQ_BASE, 0, &misc_irq_domain_ops, base); | ||
| 271 | if (!domain) | ||
| 272 | panic("Failed to add MISC irqdomain"); | ||
| 273 | |||
| 274 | /* Disable and clear all interrupts */ | ||
| 275 | __raw_writel(0, base + AR71XX_RESET_REG_MISC_INT_ENABLE); | ||
| 276 | __raw_writel(0, base + AR71XX_RESET_REG_MISC_INT_STATUS); | ||
| 277 | |||
| 278 | irq_set_chained_handler_and_data(irq, ath79_misc_irq_handler, domain); | ||
| 279 | } | ||
| 280 | |||
| 281 | static int __init ath79_misc_intc_of_init( | ||
| 282 | struct device_node *node, struct device_node *parent) | ||
| 283 | { | ||
| 284 | int irq; | ||
| 285 | |||
| 286 | irq = irq_of_parse_and_map(node, 0); | ||
| 287 | if (!irq) | ||
| 288 | panic("Failed to get MISC IRQ"); | ||
| 289 | |||
| 290 | ath79_misc_intc_domain_init(node, irq); | ||
| 291 | return 0; | ||
| 292 | } | ||
| 293 | |||
| 294 | static int __init ar7100_misc_intc_of_init( | ||
| 295 | struct device_node *node, struct device_node *parent) | ||
| 296 | { | ||
| 297 | ath79_misc_irq_chip.irq_mask_ack = ar71xx_misc_irq_mask; | ||
| 298 | return ath79_misc_intc_of_init(node, parent); | ||
| 299 | } | ||
| 300 | |||
| 301 | IRQCHIP_DECLARE(ar7100_misc_intc, "qca,ar7100-misc-intc", | ||
| 302 | ar7100_misc_intc_of_init); | ||
| 303 | |||
| 304 | static int __init ar7240_misc_intc_of_init( | ||
| 305 | struct device_node *node, struct device_node *parent) | ||
| 306 | { | ||
| 307 | ath79_misc_irq_chip.irq_ack = ar724x_misc_irq_ack; | ||
| 308 | return ath79_misc_intc_of_init(node, parent); | ||
| 309 | } | ||
| 310 | |||
| 311 | IRQCHIP_DECLARE(ar7240_misc_intc, "qca,ar7240-misc-intc", | ||
| 312 | ar7240_misc_intc_of_init); | ||
| 313 | |||
| 314 | static int __init ar79_cpu_intc_of_init( | ||
| 315 | struct device_node *node, struct device_node *parent) | ||
| 316 | { | ||
| 317 | int err, i, count; | ||
| 318 | |||
| 319 | /* Fill the irq_wb_chan table */ | ||
| 320 | count = of_count_phandle_with_args( | ||
| 321 | node, "qca,ddr-wb-channels", "#qca,ddr-wb-channel-cells"); | ||
| 322 | |||
| 323 | for (i = 0; i < count; i++) { | ||
| 324 | struct of_phandle_args args; | ||
| 325 | u32 irq = i; | ||
| 326 | |||
| 327 | of_property_read_u32_index( | ||
| 328 | node, "qca,ddr-wb-channel-interrupts", i, &irq); | ||
| 329 | if (irq >= ARRAY_SIZE(irq_wb_chan)) | ||
| 330 | continue; | ||
| 331 | |||
| 332 | err = of_parse_phandle_with_args( | ||
| 333 | node, "qca,ddr-wb-channels", | ||
| 334 | "#qca,ddr-wb-channel-cells", | ||
| 335 | i, &args); | ||
| 336 | if (err) | ||
| 337 | return err; | ||
| 338 | |||
| 339 | irq_wb_chan[irq] = args.args[0]; | ||
| 340 | pr_info("IRQ: Set flush channel of IRQ%d to %d\n", | ||
| 341 | irq, args.args[0]); | ||
| 342 | } | ||
| 343 | |||
| 344 | return mips_cpu_irq_of_init(node, parent); | ||
| 345 | } | ||
| 346 | IRQCHIP_DECLARE(ar79_cpu_intc, "qca,ar7100-cpu-intc", | ||
| 347 | ar79_cpu_intc_of_init); | ||
| 348 | |||
| 349 | void __init arch_init_irq(void) | 131 | void __init arch_init_irq(void) |
| 350 | { | 132 | { |
| 133 | unsigned irq_wb_chan2 = -1; | ||
| 134 | unsigned irq_wb_chan3 = -1; | ||
| 135 | bool misc_is_ar71xx; | ||
| 136 | |||
| 351 | if (mips_machtype == ATH79_MACH_GENERIC_OF) { | 137 | if (mips_machtype == ATH79_MACH_GENERIC_OF) { |
| 352 | irqchip_init(); | 138 | irqchip_init(); |
| 353 | return; | 139 | return; |
| @@ -355,14 +141,26 @@ void __init arch_init_irq(void) | |||
| 355 | 141 | ||
| 356 | if (soc_is_ar71xx() || soc_is_ar724x() || | 142 | if (soc_is_ar71xx() || soc_is_ar724x() || |
| 357 | soc_is_ar913x() || soc_is_ar933x()) { | 143 | soc_is_ar913x() || soc_is_ar933x()) { |
| 358 | irq_wb_chan[2] = 3; | 144 | irq_wb_chan2 = 3; |
| 359 | irq_wb_chan[3] = 2; | 145 | irq_wb_chan3 = 2; |
| 360 | } else if (soc_is_ar934x()) { | 146 | } else if (soc_is_ar934x()) { |
| 361 | irq_wb_chan[3] = 2; | 147 | irq_wb_chan3 = 2; |
| 362 | } | 148 | } |
| 363 | 149 | ||
| 364 | mips_cpu_irq_init(); | 150 | ath79_cpu_irq_init(irq_wb_chan2, irq_wb_chan3); |
| 365 | ath79_misc_irq_init(); | 151 | |
| 152 | if (soc_is_ar71xx() || soc_is_ar913x()) | ||
| 153 | misc_is_ar71xx = true; | ||
| 154 | else if (soc_is_ar724x() || | ||
| 155 | soc_is_ar933x() || | ||
| 156 | soc_is_ar934x() || | ||
| 157 | soc_is_qca955x()) | ||
| 158 | misc_is_ar71xx = false; | ||
| 159 | else | ||
| 160 | BUG(); | ||
| 161 | ath79_misc_irq_init( | ||
| 162 | ath79_reset_base + AR71XX_RESET_REG_MISC_INT_STATUS, | ||
| 163 | ATH79_CPU_IRQ(6), ATH79_MISC_IRQ_BASE, misc_is_ar71xx); | ||
| 366 | 164 | ||
| 367 | if (soc_is_ar934x()) | 165 | if (soc_is_ar934x()) |
| 368 | ar934x_ip2_irq_init(); | 166 | ar934x_ip2_irq_init(); |
diff --git a/arch/mips/include/asm/mach-ath79/ath79.h b/arch/mips/include/asm/mach-ath79/ath79.h index 2b3487213d1e..441faa92c3cd 100644 --- a/arch/mips/include/asm/mach-ath79/ath79.h +++ b/arch/mips/include/asm/mach-ath79/ath79.h | |||
| @@ -144,4 +144,8 @@ static inline u32 ath79_reset_rr(unsigned reg) | |||
| 144 | void ath79_device_reset_set(u32 mask); | 144 | void ath79_device_reset_set(u32 mask); |
| 145 | void ath79_device_reset_clear(u32 mask); | 145 | void ath79_device_reset_clear(u32 mask); |
| 146 | 146 | ||
| 147 | void ath79_cpu_irq_init(unsigned irq_wb_chan2, unsigned irq_wb_chan3); | ||
| 148 | void ath79_misc_irq_init(void __iomem *regs, int irq, | ||
| 149 | int irq_base, bool is_ar71xx); | ||
| 150 | |||
| 147 | #endif /* __ASM_MACH_ATH79_H */ | 151 | #endif /* __ASM_MACH_ATH79_H */ |
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 @@ | |||
| 1 | obj-$(CONFIG_IRQCHIP) += irqchip.o | 1 | obj-$(CONFIG_IRQCHIP) += irqchip.o |
| 2 | 2 | ||
| 3 | obj-$(CONFIG_ATH79) += irq-ath79-cpu.o | ||
| 4 | obj-$(CONFIG_ATH79) += irq-ath79-misc.o | ||
| 3 | obj-$(CONFIG_ARCH_BCM2835) += irq-bcm2835.o | 5 | obj-$(CONFIG_ARCH_BCM2835) += irq-bcm2835.o |
| 4 | obj-$(CONFIG_ARCH_BCM2835) += irq-bcm2836.o | 6 | obj-$(CONFIG_ARCH_BCM2835) += irq-bcm2836.o |
| 5 | obj-$(CONFIG_ARCH_EXYNOS) += exynos-combiner.o | 7 | 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 @@ | |||
| 1 | /* | ||
| 2 | * Atheros AR71xx/AR724x/AR913x specific interrupt handling | ||
| 3 | * | ||
| 4 | * Copyright (C) 2015 Alban Bedel <albeu@free.fr> | ||
| 5 | * Copyright (C) 2010-2011 Jaiganesh Narayanan <jnarayanan@atheros.com> | ||
| 6 | * Copyright (C) 2008-2011 Gabor Juhos <juhosg@openwrt.org> | ||
| 7 | * Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org> | ||
| 8 | * | ||
| 9 | * Parts of this file are based on Atheros' 2.6.15/2.6.31 BSP | ||
| 10 | * | ||
| 11 | * This program is free software; you can redistribute it and/or modify it | ||
| 12 | * under the terms of the GNU General Public License version 2 as published | ||
| 13 | * by the Free Software Foundation. | ||
| 14 | */ | ||
| 15 | |||
| 16 | #include <linux/interrupt.h> | ||
| 17 | #include <linux/irqchip.h> | ||
| 18 | #include <linux/of.h> | ||
| 19 | |||
| 20 | #include <asm/irq_cpu.h> | ||
| 21 | #include <asm/mach-ath79/ath79.h> | ||
| 22 | |||
| 23 | /* | ||
| 24 | * The IP2/IP3 lines are tied to a PCI/WMAC/USB device. Drivers for | ||
| 25 | * these devices typically allocate coherent DMA memory, however the | ||
| 26 | * DMA controller may still have some unsynchronized data in the FIFO. | ||
| 27 | * Issue a flush in the handlers to ensure that the driver sees | ||
| 28 | * the update. | ||
| 29 | * | ||
| 30 | * This array map the interrupt lines to the DDR write buffer channels. | ||
| 31 | */ | ||
| 32 | |||
| 33 | static unsigned irq_wb_chan[8] = { | ||
| 34 | -1, -1, -1, -1, -1, -1, -1, -1, | ||
| 35 | }; | ||
| 36 | |||
| 37 | asmlinkage void plat_irq_dispatch(void) | ||
| 38 | { | ||
| 39 | unsigned long pending; | ||
| 40 | int irq; | ||
| 41 | |||
| 42 | pending = read_c0_status() & read_c0_cause() & ST0_IM; | ||
| 43 | |||
| 44 | if (!pending) { | ||
| 45 | spurious_interrupt(); | ||
| 46 | return; | ||
| 47 | } | ||
| 48 | |||
| 49 | pending >>= CAUSEB_IP; | ||
| 50 | while (pending) { | ||
| 51 | irq = fls(pending) - 1; | ||
| 52 | if (irq < ARRAY_SIZE(irq_wb_chan) && irq_wb_chan[irq] != -1) | ||
| 53 | ath79_ddr_wb_flush(irq_wb_chan[irq]); | ||
| 54 | do_IRQ(MIPS_CPU_IRQ_BASE + irq); | ||
| 55 | pending &= ~BIT(irq); | ||
| 56 | } | ||
| 57 | } | ||
| 58 | |||
| 59 | static int __init ar79_cpu_intc_of_init( | ||
| 60 | struct device_node *node, struct device_node *parent) | ||
| 61 | { | ||
| 62 | int err, i, count; | ||
| 63 | |||
| 64 | /* Fill the irq_wb_chan table */ | ||
| 65 | count = of_count_phandle_with_args( | ||
| 66 | node, "qca,ddr-wb-channels", "#qca,ddr-wb-channel-cells"); | ||
| 67 | |||
| 68 | for (i = 0; i < count; i++) { | ||
| 69 | struct of_phandle_args args; | ||
| 70 | u32 irq = i; | ||
| 71 | |||
| 72 | of_property_read_u32_index( | ||
| 73 | node, "qca,ddr-wb-channel-interrupts", i, &irq); | ||
| 74 | if (irq >= ARRAY_SIZE(irq_wb_chan)) | ||
| 75 | continue; | ||
| 76 | |||
| 77 | err = of_parse_phandle_with_args( | ||
| 78 | node, "qca,ddr-wb-channels", | ||
| 79 | "#qca,ddr-wb-channel-cells", | ||
| 80 | i, &args); | ||
| 81 | if (err) | ||
| 82 | return err; | ||
| 83 | |||
| 84 | irq_wb_chan[irq] = args.args[0]; | ||
| 85 | } | ||
| 86 | |||
| 87 | return mips_cpu_irq_of_init(node, parent); | ||
| 88 | } | ||
| 89 | IRQCHIP_DECLARE(ar79_cpu_intc, "qca,ar7100-cpu-intc", | ||
| 90 | ar79_cpu_intc_of_init); | ||
| 91 | |||
| 92 | void __init ath79_cpu_irq_init(unsigned irq_wb_chan2, unsigned irq_wb_chan3) | ||
| 93 | { | ||
| 94 | irq_wb_chan[2] = irq_wb_chan2; | ||
| 95 | irq_wb_chan[3] = irq_wb_chan3; | ||
| 96 | mips_cpu_irq_init(); | ||
| 97 | } | ||
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 @@ | |||
| 1 | /* | ||
| 2 | * Atheros AR71xx/AR724x/AR913x MISC interrupt controller | ||
| 3 | * | ||
| 4 | * Copyright (C) 2015 Alban Bedel <albeu@free.fr> | ||
| 5 | * Copyright (C) 2010-2011 Jaiganesh Narayanan <jnarayanan@atheros.com> | ||
| 6 | * Copyright (C) 2008-2011 Gabor Juhos <juhosg@openwrt.org> | ||
| 7 | * Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org> | ||
| 8 | * | ||
| 9 | * Parts of this file are based on Atheros' 2.6.15/2.6.31 BSP | ||
| 10 | * | ||
| 11 | * This program is free software; you can redistribute it and/or modify it | ||
| 12 | * under the terms of the GNU General Public License version 2 as published | ||
| 13 | * by the Free Software Foundation. | ||
| 14 | */ | ||
| 15 | |||
| 16 | #include <linux/irqchip.h> | ||
| 17 | #include <linux/irqchip/chained_irq.h> | ||
| 18 | #include <linux/of_address.h> | ||
| 19 | #include <linux/of_irq.h> | ||
| 20 | |||
| 21 | #define AR71XX_RESET_REG_MISC_INT_STATUS 0 | ||
| 22 | #define AR71XX_RESET_REG_MISC_INT_ENABLE 4 | ||
| 23 | |||
| 24 | #define ATH79_MISC_IRQ_COUNT 32 | ||
| 25 | |||
| 26 | static void ath79_misc_irq_handler(struct irq_desc *desc) | ||
| 27 | { | ||
| 28 | struct irq_domain *domain = irq_desc_get_handler_data(desc); | ||
| 29 | struct irq_chip *chip = irq_desc_get_chip(desc); | ||
| 30 | void __iomem *base = domain->host_data; | ||
| 31 | u32 pending; | ||
| 32 | |||
| 33 | chained_irq_enter(chip, desc); | ||
| 34 | |||
| 35 | pending = __raw_readl(base + AR71XX_RESET_REG_MISC_INT_STATUS) & | ||
| 36 | __raw_readl(base + AR71XX_RESET_REG_MISC_INT_ENABLE); | ||
| 37 | |||
| 38 | if (!pending) { | ||
| 39 | spurious_interrupt(); | ||
| 40 | chained_irq_exit(chip, desc); | ||
| 41 | return; | ||
| 42 | } | ||
| 43 | |||
| 44 | while (pending) { | ||
| 45 | int bit = __ffs(pending); | ||
| 46 | |||
| 47 | generic_handle_irq(irq_linear_revmap(domain, bit)); | ||
| 48 | pending &= ~BIT(bit); | ||
| 49 | } | ||
| 50 | |||
| 51 | chained_irq_exit(chip, desc); | ||
| 52 | } | ||
| 53 | |||
| 54 | static void ar71xx_misc_irq_unmask(struct irq_data *d) | ||
| 55 | { | ||
| 56 | void __iomem *base = irq_data_get_irq_chip_data(d); | ||
| 57 | unsigned int irq = d->hwirq; | ||
| 58 | u32 t; | ||
| 59 | |||
| 60 | t = __raw_readl(base + AR71XX_RESET_REG_MISC_INT_ENABLE); | ||
| 61 | __raw_writel(t | BIT(irq), base + AR71XX_RESET_REG_MISC_INT_ENABLE); | ||
| 62 | |||
| 63 | /* flush write */ | ||
| 64 | __raw_readl(base + AR71XX_RESET_REG_MISC_INT_ENABLE); | ||
| 65 | } | ||
| 66 | |||
| 67 | static void ar71xx_misc_irq_mask(struct irq_data *d) | ||
| 68 | { | ||
| 69 | void __iomem *base = irq_data_get_irq_chip_data(d); | ||
| 70 | unsigned int irq = d->hwirq; | ||
| 71 | u32 t; | ||
| 72 | |||
| 73 | t = __raw_readl(base + AR71XX_RESET_REG_MISC_INT_ENABLE); | ||
| 74 | __raw_writel(t & ~BIT(irq), base + AR71XX_RESET_REG_MISC_INT_ENABLE); | ||
| 75 | |||
| 76 | /* flush write */ | ||
| 77 | __raw_readl(base + AR71XX_RESET_REG_MISC_INT_ENABLE); | ||
| 78 | } | ||
| 79 | |||
| 80 | static void ar724x_misc_irq_ack(struct irq_data *d) | ||
| 81 | { | ||
| 82 | void __iomem *base = irq_data_get_irq_chip_data(d); | ||
| 83 | unsigned int irq = d->hwirq; | ||
| 84 | u32 t; | ||
| 85 | |||
| 86 | t = __raw_readl(base + AR71XX_RESET_REG_MISC_INT_STATUS); | ||
| 87 | __raw_writel(t & ~BIT(irq), base + AR71XX_RESET_REG_MISC_INT_STATUS); | ||
| 88 | |||
| 89 | /* flush write */ | ||
| 90 | __raw_readl(base + AR71XX_RESET_REG_MISC_INT_STATUS); | ||
| 91 | } | ||
| 92 | |||
| 93 | static struct irq_chip ath79_misc_irq_chip = { | ||
| 94 | .name = "MISC", | ||
| 95 | .irq_unmask = ar71xx_misc_irq_unmask, | ||
| 96 | .irq_mask = ar71xx_misc_irq_mask, | ||
| 97 | }; | ||
| 98 | |||
| 99 | static int misc_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hw) | ||
| 100 | { | ||
| 101 | irq_set_chip_and_handler(irq, &ath79_misc_irq_chip, handle_level_irq); | ||
| 102 | irq_set_chip_data(irq, d->host_data); | ||
| 103 | return 0; | ||
| 104 | } | ||
| 105 | |||
| 106 | static const struct irq_domain_ops misc_irq_domain_ops = { | ||
| 107 | .xlate = irq_domain_xlate_onecell, | ||
| 108 | .map = misc_map, | ||
| 109 | }; | ||
| 110 | |||
| 111 | static void __init ath79_misc_intc_domain_init( | ||
| 112 | struct irq_domain *domain, int irq) | ||
| 113 | { | ||
| 114 | void __iomem *base = domain->host_data; | ||
| 115 | |||
| 116 | /* Disable and clear all interrupts */ | ||
| 117 | __raw_writel(0, base + AR71XX_RESET_REG_MISC_INT_ENABLE); | ||
| 118 | __raw_writel(0, base + AR71XX_RESET_REG_MISC_INT_STATUS); | ||
| 119 | |||
| 120 | irq_set_chained_handler_and_data(irq, ath79_misc_irq_handler, domain); | ||
| 121 | } | ||
| 122 | |||
| 123 | static int __init ath79_misc_intc_of_init( | ||
| 124 | struct device_node *node, struct device_node *parent) | ||
| 125 | { | ||
| 126 | struct irq_domain *domain; | ||
| 127 | void __iomem *base; | ||
| 128 | int irq; | ||
| 129 | |||
| 130 | irq = irq_of_parse_and_map(node, 0); | ||
| 131 | if (!irq) { | ||
| 132 | pr_err("Failed to get MISC IRQ\n"); | ||
| 133 | return -EINVAL; | ||
| 134 | } | ||
| 135 | |||
| 136 | base = of_iomap(node, 0); | ||
| 137 | if (!base) { | ||
| 138 | pr_err("Failed to get MISC IRQ registers\n"); | ||
| 139 | return -ENOMEM; | ||
| 140 | } | ||
| 141 | |||
| 142 | domain = irq_domain_add_linear(node, ATH79_MISC_IRQ_COUNT, | ||
| 143 | &misc_irq_domain_ops, base); | ||
| 144 | if (!domain) { | ||
| 145 | pr_err("Failed to add MISC irqdomain\n"); | ||
| 146 | return -EINVAL; | ||
| 147 | } | ||
| 148 | |||
| 149 | ath79_misc_intc_domain_init(domain, irq); | ||
| 150 | return 0; | ||
| 151 | } | ||
| 152 | |||
| 153 | static int __init ar7100_misc_intc_of_init( | ||
| 154 | struct device_node *node, struct device_node *parent) | ||
| 155 | { | ||
| 156 | ath79_misc_irq_chip.irq_mask_ack = ar71xx_misc_irq_mask; | ||
| 157 | return ath79_misc_intc_of_init(node, parent); | ||
| 158 | } | ||
| 159 | |||
| 160 | IRQCHIP_DECLARE(ar7100_misc_intc, "qca,ar7100-misc-intc", | ||
| 161 | ar7100_misc_intc_of_init); | ||
| 162 | |||
| 163 | static int __init ar7240_misc_intc_of_init( | ||
| 164 | struct device_node *node, struct device_node *parent) | ||
| 165 | { | ||
| 166 | ath79_misc_irq_chip.irq_ack = ar724x_misc_irq_ack; | ||
| 167 | return ath79_misc_intc_of_init(node, parent); | ||
| 168 | } | ||
| 169 | |||
| 170 | IRQCHIP_DECLARE(ar7240_misc_intc, "qca,ar7240-misc-intc", | ||
| 171 | ar7240_misc_intc_of_init); | ||
| 172 | |||
| 173 | void __init ath79_misc_irq_init(void __iomem *regs, int irq, | ||
| 174 | int irq_base, bool is_ar71xx) | ||
| 175 | { | ||
| 176 | struct irq_domain *domain; | ||
| 177 | |||
| 178 | if (is_ar71xx) | ||
| 179 | ath79_misc_irq_chip.irq_mask_ack = ar71xx_misc_irq_mask; | ||
| 180 | else | ||
| 181 | ath79_misc_irq_chip.irq_ack = ar724x_misc_irq_ack; | ||
| 182 | |||
| 183 | domain = irq_domain_add_legacy(NULL, ATH79_MISC_IRQ_COUNT, | ||
| 184 | irq_base, 0, &misc_irq_domain_ops, regs); | ||
| 185 | if (!domain) | ||
| 186 | panic("Failed to create MISC irqdomain"); | ||
| 187 | |||
| 188 | ath79_misc_intc_domain_init(domain, irq); | ||
| 189 | } | ||
