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,104 @@ +/* + * 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 irq_mask[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(QCA955X_RESET_REG_EXT_INT_STATUS); + pending &= intc->pending_mask; + + if (pending) { + int i; + + for (i = 0; i < domain->hwirq_max; i++) + if (pending & intc->irq_mask[i]) + generic_handle_irq(irq_find_mapping(domain, i)); + } else { + spurious_interrupt(); + } +} + +static void ath79_intc_irq_unmask(struct irq_data *d) +{ +} + +static void ath79_intc_irq_mask(struct irq_data *d) +{ +} + +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 qca9556_intc_of_init( + struct device_node *node, struct device_node *parent) +{ + struct irq_domain *domain; + struct ath79_intc *intc; + int cnt, i; + + cnt = of_property_count_u32_elems(node, "qcom,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.name = "INTC"; + intc->chip.irq_unmask = ath79_intc_irq_unmask, + intc->chip.irq_mask = ath79_intc_irq_mask, + + of_property_read_u32_array(node, "qcom,pending-bits", intc->irq_mask, cnt); + for (i = 0; i < cnt; i++) + intc->pending_mask |= intc->irq_mask[i]; + + 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(qca9556_intc, "qcom,qca9556-intc", + qca9556_intc_of_init);