]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
irqchip: Add T-HEAD C900 ACLINT SSWI driver
authorInochi Amaoto <inochiama@gmail.com>
Thu, 31 Oct 2024 06:08:58 +0000 (14:08 +0800)
committerThomas Gleixner <tglx@linutronix.de>
Wed, 6 Nov 2024 23:28:27 +0000 (00:28 +0100)
Add a driver for the T-HEAD C900 ACLINT SSWI device. This device allows
the system with T-HEAD cpus to send ipi via fast device interface.

Signed-off-by: Inochi Amaoto <inochiama@gmail.com>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Link: https://lore.kernel.org/all/20241031060859.722258-3-inochiama@gmail.com
drivers/irqchip/Kconfig
drivers/irqchip/Makefile
drivers/irqchip/irq-thead-c900-aclint-sswi.c [new file with mode: 0644]
include/linux/cpuhotplug.h

index ef0fa6975dab9502efda80cdc0c5e21c251f4abd..9cac13663871ba16e81c40cfffbbaf03c09e459e 100644 (file)
@@ -619,6 +619,18 @@ config STARFIVE_JH8100_INTC
 
          If you don't know what to do here, say Y.
 
+config THEAD_C900_ACLINT_SSWI
+       bool "THEAD C9XX ACLINT S-mode IPI Interrupt Controller"
+       depends on RISCV
+       depends on SMP
+       select IRQ_DOMAIN_HIERARCHY
+       select GENERIC_IRQ_IPI_MUX
+       help
+         This enables support for T-HEAD specific ACLINT SSWI device
+         support.
+
+         If you don't know what to do here, say Y.
+
 config EXYNOS_IRQ_COMBINER
        bool "Samsung Exynos IRQ combiner support" if COMPILE_TEST
        depends on (ARCH_EXYNOS && ARM) || COMPILE_TEST
index 15ed6e952ecd34782af6ffd4cb1490bbf2794297..25e9ad29b8c4a5729ae4114a1ea53c9fc07ff133 100644 (file)
@@ -103,6 +103,7 @@ obj-$(CONFIG_RISCV_APLIC_MSI)               += irq-riscv-aplic-msi.o
 obj-$(CONFIG_RISCV_IMSIC)              += irq-riscv-imsic-state.o irq-riscv-imsic-early.o irq-riscv-imsic-platform.o
 obj-$(CONFIG_SIFIVE_PLIC)              += irq-sifive-plic.o
 obj-$(CONFIG_STARFIVE_JH8100_INTC)     += irq-starfive-jh8100-intc.o
+obj-$(CONFIG_THEAD_C900_ACLINT_SSWI)   += irq-thead-c900-aclint-sswi.o
 obj-$(CONFIG_IMX_IRQSTEER)             += irq-imx-irqsteer.o
 obj-$(CONFIG_IMX_INTMUX)               += irq-imx-intmux.o
 obj-$(CONFIG_IMX_MU_MSI)               += irq-imx-mu-msi.o
diff --git a/drivers/irqchip/irq-thead-c900-aclint-sswi.c b/drivers/irqchip/irq-thead-c900-aclint-sswi.c
new file mode 100644 (file)
index 0000000..b0e366a
--- /dev/null
@@ -0,0 +1,176 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2024 Inochi Amaoto <inochiama@gmail.com>
+ */
+
+#define pr_fmt(fmt) "thead-c900-aclint-sswi: " fmt
+#include <linux/cpu.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/irqchip.h>
+#include <linux/irqchip/chained_irq.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/pci.h>
+#include <linux/spinlock.h>
+#include <linux/smp.h>
+#include <linux/string_choices.h>
+#include <asm/sbi.h>
+#include <asm/vendorid_list.h>
+
+#define THEAD_ACLINT_xSWI_REGISTER_SIZE                4
+
+#define THEAD_C9XX_CSR_SXSTATUS                        0x5c0
+#define THEAD_C9XX_SXSTATUS_CLINTEE            BIT(17)
+
+static int sswi_ipi_virq __ro_after_init;
+static DEFINE_PER_CPU(void __iomem *, sswi_cpu_regs);
+
+static void thead_aclint_sswi_ipi_send(unsigned int cpu)
+{
+       writel_relaxed(0x1, per_cpu(sswi_cpu_regs, cpu));
+}
+
+static void thead_aclint_sswi_ipi_clear(void)
+{
+       writel_relaxed(0x0, this_cpu_read(sswi_cpu_regs));
+}
+
+static void thead_aclint_sswi_ipi_handle(struct irq_desc *desc)
+{
+       struct irq_chip *chip = irq_desc_get_chip(desc);
+
+       chained_irq_enter(chip, desc);
+
+       csr_clear(CSR_IP, IE_SIE);
+       thead_aclint_sswi_ipi_clear();
+
+       ipi_mux_process();
+
+       chained_irq_exit(chip, desc);
+}
+
+static int thead_aclint_sswi_starting_cpu(unsigned int cpu)
+{
+       enable_percpu_irq(sswi_ipi_virq, irq_get_trigger_type(sswi_ipi_virq));
+
+       return 0;
+}
+
+static int thead_aclint_sswi_dying_cpu(unsigned int cpu)
+{
+       thead_aclint_sswi_ipi_clear();
+
+       disable_percpu_irq(sswi_ipi_virq);
+
+       return 0;
+}
+
+static int __init thead_aclint_sswi_parse_irq(struct fwnode_handle *fwnode,
+                                             void __iomem *reg)
+{
+       struct of_phandle_args parent;
+       unsigned long hartid;
+       u32 contexts, i;
+       int rc, cpu;
+
+       contexts = of_irq_count(to_of_node(fwnode));
+       if (!(contexts)) {
+               pr_err("%pfwP: no ACLINT SSWI context available\n", fwnode);
+               return -EINVAL;
+       }
+
+       for (i = 0; i < contexts; i++) {
+               rc = of_irq_parse_one(to_of_node(fwnode), i, &parent);
+               if (rc)
+                       return rc;
+
+               rc = riscv_of_parent_hartid(parent.np, &hartid);
+               if (rc)
+                       return rc;
+
+               if (parent.args[0] != RV_IRQ_SOFT)
+                       return -ENOTSUPP;
+
+               cpu = riscv_hartid_to_cpuid(hartid);
+
+               per_cpu(sswi_cpu_regs, cpu) = reg + i * THEAD_ACLINT_xSWI_REGISTER_SIZE;
+       }
+
+       pr_info("%pfwP: register %u CPU%s\n", fwnode, contexts, str_plural(contexts));
+
+       return 0;
+}
+
+static int __init thead_aclint_sswi_probe(struct fwnode_handle *fwnode)
+{
+       struct irq_domain *domain;
+       void __iomem *reg;
+       int virq, rc;
+
+       /* If it is T-HEAD CPU, check whether SSWI is enabled */
+       if (riscv_cached_mvendorid(0) == THEAD_VENDOR_ID &&
+           !(csr_read(THEAD_C9XX_CSR_SXSTATUS) & THEAD_C9XX_SXSTATUS_CLINTEE))
+               return -ENOTSUPP;
+
+       if (!is_of_node(fwnode))
+               return -EINVAL;
+
+       reg = of_iomap(to_of_node(fwnode), 0);
+       if (!reg)
+               return -ENOMEM;
+
+       /* Parse SSWI setting */
+       rc = thead_aclint_sswi_parse_irq(fwnode, reg);
+       if (rc < 0)
+               return rc;
+
+       /* If mulitple SSWI devices are present, do not register irq again */
+       if (sswi_ipi_virq)
+               return 0;
+
+       /* Find riscv intc domain and create IPI irq mapping */
+       domain = irq_find_matching_fwnode(riscv_get_intc_hwnode(), DOMAIN_BUS_ANY);
+       if (!domain) {
+               pr_err("%pfwP: Failed to find INTC domain\n", fwnode);
+               return -ENOENT;
+       }
+
+       sswi_ipi_virq = irq_create_mapping(domain, RV_IRQ_SOFT);
+       if (!sswi_ipi_virq) {
+               pr_err("unable to create ACLINT SSWI IRQ mapping\n");
+               return -ENOMEM;
+       }
+
+       /* Register SSWI irq and handler */
+       virq = ipi_mux_create(BITS_PER_BYTE, thead_aclint_sswi_ipi_send);
+       if (virq <= 0) {
+               pr_err("unable to create muxed IPIs\n");
+               irq_dispose_mapping(sswi_ipi_virq);
+               return virq < 0 ? virq : -ENOMEM;
+       }
+
+       irq_set_chained_handler(sswi_ipi_virq, thead_aclint_sswi_ipi_handle);
+
+       cpuhp_setup_state(CPUHP_AP_IRQ_THEAD_ACLINT_SSWI_STARTING,
+                         "irqchip/thead-aclint-sswi:starting",
+                         thead_aclint_sswi_starting_cpu,
+                         thead_aclint_sswi_dying_cpu);
+
+       riscv_ipi_set_virq_range(virq, BITS_PER_BYTE);
+
+       /* Announce that SSWI is providing IPIs */
+       pr_info("providing IPIs using THEAD ACLINT SSWI\n");
+
+       return 0;
+}
+
+static int __init thead_aclint_sswi_early_probe(struct device_node *node,
+                                               struct device_node *parent)
+{
+       return thead_aclint_sswi_probe(&node->fwnode);
+}
+IRQCHIP_DECLARE(thead_aclint_sswi, "thead,c900-aclint-sswi", thead_aclint_sswi_early_probe);
index 2361ed4d2b1523c02a1056be75dfefef383a9a5c..799052249c7bf9dbbfd6525185e4398afe836c48 100644 (file)
@@ -147,6 +147,7 @@ enum cpuhp_state {
        CPUHP_AP_IRQ_EIOINTC_STARTING,
        CPUHP_AP_IRQ_AVECINTC_STARTING,
        CPUHP_AP_IRQ_SIFIVE_PLIC_STARTING,
+       CPUHP_AP_IRQ_THEAD_ACLINT_SSWI_STARTING,
        CPUHP_AP_IRQ_RISCV_IMSIC_STARTING,
        CPUHP_AP_IRQ_RISCV_SBI_IPI_STARTING,
        CPUHP_AP_ARM_MVEBU_COHERENCY,