From cb376159800b9b44be76949c3aee89eb06d29faa Mon Sep 17 00:00:00 2001 From: John Crispin Date: Tue, 6 Mar 2018 09:55:13 +0100 Subject: [PATCH 07/27] irqchip/irq-ath79-intc: add irq cascade driver for QCA9556 SoCs Signed-off-by: John Crispin --- drivers/irqchip/Makefile | 1 + drivers/irqchip/irq-ath79-intc.c | 104 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 105 insertions(+) create mode 100644 drivers/irqchip/irq-ath79-intc.c --- a/drivers/irqchip/Makefile +++ b/drivers/irqchip/Makefile @@ -3,6 +3,7 @@ obj-$(CONFIG_IRQCHIP) += irqchip.o obj-$(CONFIG_ALPINE_MSI) += irq-alpine-msi.o obj-$(CONFIG_ATH79) += irq-ath79-cpu.o +obj-$(CONFIG_ATH79) += irq-ath79-intc.o obj-$(CONFIG_ATH79) += irq-ath79-misc.o obj-$(CONFIG_ARCH_BCM2835) += irq-bcm2835.o obj-$(CONFIG_ARCH_BCM2835) += irq-bcm2836.o --- /dev/null +++ b/drivers/irqchip/irq-ath79-intc.c @@ -0,0 +1,142 @@ +/* + * Atheros AR71xx/AR724x/AR913x specific interrupt handling + * + * Copyright (C) 2018 John Crispin + * + * 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 +#include +#include +#include +#include + +#include +#include +#include + +#define ATH79_MAX_INTC_CASCADE 3 + +struct ath79_intc { + struct irq_chip chip; + u32 irq; + u32 pending_mask; + u32 int_status; + u32 irq_mask[ATH79_MAX_INTC_CASCADE]; + u32 irq_wb_chan[ATH79_MAX_INTC_CASCADE]; +}; + +static void ath79_intc_irq_handler(struct irq_desc *desc) +{ + struct irq_domain *domain = irq_desc_get_handler_data(desc); + struct ath79_intc *intc = domain->host_data; + u32 pending; + + pending = ath79_reset_rr(intc->int_status); + pending &= intc->pending_mask; + + if (pending) { + int i; + + for (i = 0; i < domain->hwirq_max; i++) + if (pending & intc->irq_mask[i]) { + if (intc->irq_wb_chan[i] != 0xffffffff) + ath79_ddr_wb_flush(intc->irq_wb_chan[i]); + generic_handle_irq(irq_find_mapping(domain, i)); + } + } else { + spurious_interrupt(); + } +} + +static void ath79_intc_irq_enable(struct irq_data *d) +{ + struct ath79_intc *intc = d->domain->host_data; + enable_irq(intc->irq); +} + +static void ath79_intc_irq_disable(struct irq_data *d) +{ + struct ath79_intc *intc = d->domain->host_data; + disable_irq(intc->irq); +} + +static int ath79_intc_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hw) +{ + struct ath79_intc *intc = d->host_data; + + irq_set_chip_and_handler(irq, &intc->chip, handle_level_irq); + + return 0; +} + +static const struct irq_domain_ops ath79_irq_domain_ops = { + .xlate = irq_domain_xlate_onecell, + .map = ath79_intc_map, +}; + +static int __init ath79_intc_of_init( + struct device_node *node, struct device_node *parent) +{ + struct irq_domain *domain; + struct ath79_intc *intc; + int cnt, cntwb, i, err; + + cnt = of_property_count_u32_elems(node, "qca,pending-bits"); + if (cnt > ATH79_MAX_INTC_CASCADE) + panic("Too many INTC pending bits\n"); + + intc = kzalloc(sizeof(*intc), GFP_KERNEL); + if (!intc) + panic("Failed to allocate INTC memory\n"); + intc->chip = dummy_irq_chip; + intc->chip.name = "INTC"; + intc->chip.irq_disable = ath79_intc_irq_disable; + intc->chip.irq_enable = ath79_intc_irq_enable; + + if (of_property_read_u32(node, "qca,int-status-addr", &intc->int_status) < 0) { + panic("Missing address of interrupt status register\n"); + } + + of_property_read_u32_array(node, "qca,pending-bits", intc->irq_mask, cnt); + for (i = 0; i < cnt; i++) { + intc->pending_mask |= intc->irq_mask[i]; + intc->irq_wb_chan[i] = 0xffffffff; + } + + cntwb = of_count_phandle_with_args( + node, "qca,ddr-wb-channels", "#qca,ddr-wb-channel-cells"); + + for (i = 0; i < cntwb; i++) { + struct of_phandle_args args; + u32 irq = i; + + of_property_read_u32_index( + node, "qca,ddr-wb-channel-interrupts", i, &irq); + if (irq >= ATH79_MAX_INTC_CASCADE) + continue; + + err = of_parse_phandle_with_args( + node, "qca,ddr-wb-channels", + "#qca,ddr-wb-channel-cells", + i, &args); + if (err) + return err; + + intc->irq_wb_chan[irq] = args.args[0]; + } + + intc->irq = irq_of_parse_and_map(node, 0); + if (!intc->irq) + panic("Failed to get INTC IRQ"); + + domain = irq_domain_add_linear(node, cnt, &ath79_irq_domain_ops, intc); + irq_set_chained_handler_and_data(intc->irq, ath79_intc_irq_handler, domain); + + return 0; +} +IRQCHIP_DECLARE(ath79_intc, "qca,ar9340-intc", + ath79_intc_of_init);