[RFC PATCH v3 22/37] irqchip: Add kvx-core-intc core interrupt controller driver

ysionneau
2024-07-22T09:45:04Z

From: Yann Sionneau <ysionneau@kalrayinc.com>

Each kvx core includes a hardware interrupt controller (core INTC)
with the following features:
- 32 independent interrupt sources
- 4-bit priotity level
- Individual interrupt enable bit
- Interrupt status bit displaying the pending interrupts
- Priority management between the 32 interrupts

Among those 32 interrupt sources, the 0-17 and 24-31 are hard-wired to hardware
sources. The remaining interrupt sources can be triggered via software
by directly writing to the ILR SFR.

The hard-wired interrupt sources are the following:
0: Timer 0
1: Timer 1
2: Watchdog
3: Performance Monitors
4: APIC GIC line 0
5: APIC GIC line 1
6: APIC GIC line 2
7: APIC GIC line 3
12: SECC error from memory system
13: Arithmetic exception (carry and IEEE 754 flags)
16: Data Asynchronous Memory Error (DAME), raised for DECC/DSYS errors
17: CLI (Cache Line Invalidation) for L1D or L1I following
DECC/DSYS/Parity errors
24-31: IPIs

The APIC GIC lines will be used to route interrupts coming from SoC
peripherals from outside the Cluster to the kvx core. Those peripherals
include USB host controller, eMMC/SD host controller, i2c, spi, PCIe,
IOMMUs etc...

Co-developed-by: Clement Leger <clement@clement-leger.fr>
Signed-off-by: Clement Leger <clement@clement-leger.fr>
Co-developed-by: Julian Vetter <jvetter@kalrayinc.com>
Signed-off-by: Julian Vetter <jvetter@kalrayinc.com>
Co-developed-by: Vincent Chardon <vincent.chardon@elsys-design.com>
Signed-off-by: Vincent Chardon <vincent.chardon@elsys-design.com>
Co-developed-by: Jules Maselbas <jmaselbas@zdiv.net>
Signed-off-by: Jules Maselbas <jmaselbas@zdiv.net>
Signed-off-by: Yann Sionneau <ysionneau@kalrayinc.com>
---

Notes:
V1 -> V2: new patch
- removed print on probe success
V2 -> V3:
- now use generic irq multi-handler
- correctly set hwirq using irq_domain_set_info()
- move core_intc DT node as CPU subnode
---
drivers/irqchip/Kconfig | 5 ++
drivers/irqchip/Makefile | 1 +
drivers/irqchip/irq-kvx-core-intc.c | 117 ++++++++++++++++++++++++++++
3 files changed, 123 insertions(+)
create mode 100644 drivers/irqchip/irq-kvx-core-intc.c

diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
index dbac5b4400e02..da1dbd79dab54 100644
--- a/drivers/irqchip/Kconfig
+++ b/drivers/irqchip/Kconfig
@@ -332,6 +332,11 @@ config MIPS_GIC
select IRQ_DOMAIN_HIERARCHY
select MIPS_CM

+config KVX_CORE_INTC
+ bool
+ depends on KVX
+ select IRQ_DOMAIN
+
config KVX_APIC_GIC
bool
depends on KVX
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
index 16a2f666b788d..30b69db8789f7 100644
--- a/drivers/irqchip/Makefile
+++ b/drivers/irqchip/Makefile
@@ -68,6 +68,7 @@ obj-$(CONFIG_BCM7120_L2_IRQ) += irq-bcm7120-l2.o
obj-$(CONFIG_BRCMSTB_L2_IRQ) += irq-brcmstb-l2.o
obj-$(CONFIG_KEYSTONE_IRQ) += irq-keystone.o
obj-$(CONFIG_MIPS_GIC) += irq-mips-gic.o
+obj-$(CONFIG_KVX_CORE_INTC) += irq-kvx-core-intc.o
obj-$(CONFIG_KVX_APIC_GIC) += irq-kvx-apic-gic.o
obj-$(CONFIG_KVX_ITGEN) += irq-kvx-itgen.o
obj-$(CONFIG_KVX_APIC_MAILBOX) += irq-kvx-apic-mailbox.o
diff --git a/drivers/irqchip/irq-kvx-core-intc.c b/drivers/irqchip/irq-kvx-core-intc.c
new file mode 100644
index 0000000000000..2f5be973d2b07
--- /dev/null
+++ b/drivers/irqchip/irq-kvx-core-intc.c
@@ -0,0 +1,117 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2017-2023 Kalray Inc.
+ * Author(s): Clement Leger
+ */
+
+#define pr_fmt(fmt) "kvx_core_intc: " fmt
+
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/irqchip.h>
+#include <linux/irqdomain.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/processor.h>
+
+#include <asm/irq.h>
+
+#define KVX_CORE_INTC_IRQ 32
+
+static struct irq_domain *root_domain;
+
+static void handle_kvx_irq(struct pt_regs *regs)
+{
+ u32 ilr, ile, cause, hwirq_mask;
+ u8 es_itn, hwirq;
+ unsigned long es;
+
+ ilr = kvx_sfr_get(ILR);
+ ile = kvx_sfr_get(ILE);
+ es = regs->es;
+
+ es_itn = (es & KVX_SFR_ES_ITN_MASK) >> KVX_SFR_ES_ITN_SHIFT;
+ cause = (1 << es_itn);
+
+ hwirq_mask = (ilr & ile) | cause;
+ kvx_sfr_clear_bit(ILR, hwirq_mask);
+
+ while (hwirq_mask) {
+ hwirq = __ffs(hwirq_mask);
+ generic_handle_domain_irq(root_domain, hwirq);
+ hwirq_mask &= ~BIT_ULL(hwirq);
+ }
+
+ kvx_sfr_set_field(PS, IL, 0);
+}
+
+static void kvx_irq_mask(struct irq_data *data)
+{
+ kvx_sfr_clear_bit(ILE, data->hwirq);
+}
+
+static void kvx_irq_unmask(struct irq_data *data)
+{
+ kvx_sfr_set_bit(ILE, data->hwirq);
+}
+
+static struct irq_chip kvx_irq_chip = {
+ .name = "kvx core Intc",
+ .irq_mask = kvx_irq_mask,
+ .irq_unmask = kvx_irq_unmask,
+};
+
+static int kvx_irq_map(struct irq_domain *d, unsigned int irq,
+ irq_hw_number_t hwirq)
+{
+ /* All interrupts for core are per cpu */
+ irq_set_percpu_devid(irq);
+ irq_domain_set_info(d, irq, hwirq, &kvx_irq_chip, d->host_data,
+ handle_percpu_devid_irq, NULL, NULL);
+
+ return 0;
+}
+
+static const struct irq_domain_ops kvx_irq_ops = {
+ .xlate = irq_domain_xlate_onecell,
+ .map = kvx_irq_map,
+};
+
+static int __init
+kvx_init_core_intc(struct device_node *intc, struct device_node *parent)
+{
+ uint32_t core_nr_irqs;
+ unsigned long cpuid;
+ int ret;
+
+ ret = kvx_of_parent_cpuid(intc, &cpuid);
+ if (ret)
+ panic("core intc has no CPU parent\n");
+
+ if (smp_processor_id() != cpuid) {
+ fwnode_dev_initialized(of_fwnode_handle(intc), true);
+ return 0;
+ }
+
+ if (of_property_read_u32(intc, "kalray,intc-nr-irqs", &core_nr_irqs))
+ core_nr_irqs = KVX_CORE_INTC_IRQ;
+
+ /* We only have up to 32 interrupts,
+ * linear is likely to be the best choice
+ */
+ root_domain = irq_domain_add_linear(intc, core_nr_irqs,
+ &kvx_irq_ops, NULL);
+ if (!root_domain)
+ panic("root irq domain not available\n");
+
+ /*
+ * Needed for primary domain lookup to succeed
+ * This is a primary irqchip, and can never have a parent
+ */
+ irq_set_default_host(root_domain);
+ set_handle_irq(handle_kvx_irq);
+
+ return 0;
+}
+
+IRQCHIP_DECLARE(kvx_core_intc, "kalray,kv3-1-intc", kvx_init_core_intc);
--
2.45.2