[RFC PATCH 1/2] irqchip: ti-tsir: Add support for Timesync Interrupt Router

Chintan Vankar
2025-02-05T16:01:47Z

Timesync Interrupt Router is an instantiation of generic interrupt router
module. It provides a mechanism to mux M interrupt inputs to N interrupt
outputs, where all M inputs are selectable to be driven as per N output.
Timesync Interrupt Router's inputs are either from peripherals or from
Device sync Events.

Add support for Timesync Interrupt Router driver to map input received
from peripherals with the corresponding output.

Signed-off-by: Chintan Vankar <c-vankar@ti.com>
---
drivers/irqchip/Kconfig | 9 +++
drivers/irqchip/Makefile | 1 +
drivers/irqchip/ti-timesync-intr.c | 109 +++++++++++++++++++++++++++++
3 files changed, 119 insertions(+)
create mode 100644 drivers/irqchip/ti-timesync-intr.c

diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
index c11b9965c4ad..48b9d907be0f 100644
--- a/drivers/irqchip/Kconfig
+++ b/drivers/irqchip/Kconfig
@@ -557,6 +557,15 @@ config TI_SCI_INTA_IRQCHIP
If you wish to use interrupt aggregator irq resources managed by the
TI System Controller, say Y here. Otherwise, say N.

+config TI_TS_INTR
+ bool
+ select IRQ_DOMAIN_HIERARCHY
+ help
+ This enables the irqchip driver support for K3 Timesync Interrupt
+ router available on TI's SoCs.
+ To enable Timesync Interrupt Router's mapping, say Y here. Otherwise
+ say N.
+
config TI_PRUSS_INTC
tristate
depends on TI_PRUSS
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
index 25e9ad29b8c4..00c49f6d492a 100644
--- a/drivers/irqchip/Makefile
+++ b/drivers/irqchip/Makefile
@@ -129,3 +129,4 @@ obj-$(CONFIG_IRQ_IDT3243X) += irq-idt3243x.o
obj-$(CONFIG_APPLE_AIC) += irq-apple-aic.o
obj-$(CONFIG_MCHP_EIC) += irq-mchp-eic.o
obj-$(CONFIG_SUNPLUS_SP7021_INTC) += irq-sp7021-intc.o
+obj-$(CONFIG_TS_INTR) += ti-timesync-intr.o
diff --git a/drivers/irqchip/ti-timesync-intr.c b/drivers/irqchip/ti-timesync-intr.c
new file mode 100644
index 000000000000..11f26ca649d2
--- /dev/null
+++ b/drivers/irqchip/ti-timesync-intr.c
@@ -0,0 +1,109 @@
+// SPDX-License-Identifier: GPL
+/*
+ * Texas Instruments K3 Timesync Interrupt Router driver
+ *
+ * Copyright (C) 2025 Texas Instruments Incorporated - https://www.ti.com/
+ * Chintan Vankar <c-vankar@ti.com>
+ */
+
+#include <linux/irqchip.h>
+#include <linux/irqdomain.h>
+#include <linux/of_address.h>
+#include <linux/list.h>
+
+#define DRIVER_NAME "ti-tsir"
+
+#define TIMESYNC_INTRTR_ENABLE GENMASK(5, 0)
+#define TIMESYNC_INTRTR_INT_ENABLE BIT(16)
+#define TIMESYNC_INTRTR_MAX_OUTPUT_LINES 48
+
+struct tsr_chip_data {
+ void __iomem *tsr_base;
+ struct irq_domain *domain;
+ u64 flags;
+};
+
+static struct irq_chip ts_intr_irq_chip = {
+ .name = "TIMESYNC_INTRTR",
+};
+
+static u32 output_line_to_virq[TIMESYNC_INTRTR_MAX_OUTPUT_LINES];
+static struct tsr_chip_data tsr_data;
+
+static int ts_intr_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
+ unsigned int nr_irqs, void *arg)
+{
+ unsigned int output_line, input_line, output_line_offset;
+ struct irq_fwspec *fwspec = (struct irq_fwspec *)arg;
+ int ret;
+
+ irq_domain_set_hwirq_and_chip(domain, virq, output_line,
+ &ts_intr_irq_chip,
+ NULL);
+
+ /* Check for two input parameters: output line and corresponding input line */
+ if (fwspec->param_count != 2)
+ return -EINVAL;
+
+ output_line = fwspec->param[0];
+
+ /* Timesync Interrupt Router's mux-controller register starts at offset 4 from base
+ * address and each output line are at offset in multiple of 4s in Timesync INTR's
+ * register space, calculate the register offset from provided output line.
+ */
+ output_line_offset = 4 * output_line + 0x4;
+ output_line_to_virq[output_line] = virq;
+ input_line = fwspec->param[1] & TIMESYNC_INTRTR_ENABLE;
+
+ /* Map output line corresponding to input line */
+ writel(input_line, tsr_data.tsr_base + output_line_offset);
+
+ /* When interrupt enable bit is set for Timesync Interrupt Router it maps the output
+ * line with the existing input line, hence enable interrupt line after we set bits for
+ * output line.
+ */
+ input_line |= TIMESYNC_INTRTR_INT_ENABLE;
+ writel(input_line, tsr_data.tsr_base + output_line_offset);
+
+ return 0;
+}
+
+static void ts_intr_irq_domain_free(struct irq_domain *domain, unsigned int virq,
+ unsigned int nr_irqs)
+{
+ struct output_line_to_virq *node, *n;
+ unsigned int output_line_offset;
+ int i;
+
+ for (i = 0; i < TIMESYNC_INTRTR_MAX_OUTPUT_LINES; i++) {
+ if (output_line_to_virq[i] == virq) {
+ /* Calculate the register offset value from provided output line */
+ output_line_offset = 4 * i + 0x4;
+ writel(~TIMESYNC_INTRTR_INT_ENABLE, tsr_data.tsr_base + output_line_offset);
+ }
+ }
+}
+
+static const struct irq_domain_ops ts_intr_irq_domain_ops = {
+ .alloc = ts_intr_irq_domain_alloc,
+ .free = ts_intr_irq_domain_free,
+};
+
+static int tsr_init(struct device_node *node)
+{
+ tsr_data.tsr_base = of_iomap(node, 0);
+ if (IS_ERR(tsr_data.tsr_base)) {
+ pr_err("Unable to get reg\n");
+ return PTR_ERR(tsr_data.tsr_base);
+ }
+
+ tsr_data.domain = irq_domain_create_tree(&node->fwnode, &ts_intr_irq_domain_ops, &tsr_data);
+
+ return 0;
+}
+
+IRQCHIP_DECLARE(ts_intr, "ti,ts-intr", tsr_init);
+
+MODULE_AUTHOR("Chintan Vankar <c-vankar@ti.com>");
+MODULE_DESCRIPTION("Driver to configure Timesync Interrupt Router");
+MODULE_LICENSE("GPL");
--
2.34.1