File Index
Files and directories
Sign in
FILE

irq-aclint-sswi.c

File preview with syntax highlighting.

linux-6.19.14/drivers/irqchip/irq-aclint-sswi.c C
  1// SPDX-License-Identifier: GPL-2.0
  2/*
  3 * Copyright (C) 2024 Inochi Amaoto <inochiama@gmail.com>
  4 */
  5
  6#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  7
  8#include <linux/cpu.h>
  9#include <linux/interrupt.h>
 10#include <linux/irqchip.h>
 11#include <linux/irqchip/chained_irq.h>
 12#include <linux/of_address.h>
 13#include <linux/spinlock.h>
 14#include <linux/smp.h>
 15#include <linux/string_choices.h>
 16#include <asm/sbi.h>
 17#include <asm/vendorid_list.h>
 18
 19static int sswi_ipi_virq __ro_after_init;
 20static DEFINE_PER_CPU(void __iomem *, sswi_cpu_regs);
 21
 22static void aclint_sswi_ipi_send(unsigned int cpu)
 23{
 24	writel(0x1, per_cpu(sswi_cpu_regs, cpu));
 25}
 26
 27static void aclint_sswi_ipi_clear(void)
 28{
 29	writel_relaxed(0x0, this_cpu_read(sswi_cpu_regs));
 30}
 31
 32static void aclint_sswi_ipi_handle(struct irq_desc *desc)
 33{
 34	struct irq_chip *chip = irq_desc_get_chip(desc);
 35
 36	chained_irq_enter(chip, desc);
 37
 38	csr_clear(CSR_IP, IE_SIE);
 39	aclint_sswi_ipi_clear();
 40
 41	ipi_mux_process();
 42
 43	chained_irq_exit(chip, desc);
 44}
 45
 46static int aclint_sswi_starting_cpu(unsigned int cpu)
 47{
 48	enable_percpu_irq(sswi_ipi_virq, irq_get_trigger_type(sswi_ipi_virq));
 49
 50	return 0;
 51}
 52
 53static int aclint_sswi_dying_cpu(unsigned int cpu)
 54{
 55	aclint_sswi_ipi_clear();
 56
 57	disable_percpu_irq(sswi_ipi_virq);
 58
 59	return 0;
 60}
 61
 62static int __init aclint_sswi_parse_irq(struct fwnode_handle *fwnode, void __iomem *reg)
 63{
 64	u32 contexts = of_irq_count(to_of_node(fwnode));
 65
 66	if (!(contexts)) {
 67		pr_err("%pfwP: no ACLINT SSWI context available\n", fwnode);
 68		return -EINVAL;
 69	}
 70
 71	for (u32 i = 0; i < contexts; i++) {
 72		struct of_phandle_args parent;
 73		unsigned long hartid;
 74		u32 hart_index;
 75		int rc, cpu;
 76
 77		rc = of_irq_parse_one(to_of_node(fwnode), i, &parent);
 78		if (rc)
 79			return rc;
 80
 81		rc = riscv_of_parent_hartid(parent.np, &hartid);
 82		if (rc)
 83			return rc;
 84
 85		if (parent.args[0] != RV_IRQ_SOFT)
 86			return -ENOTSUPP;
 87
 88		cpu = riscv_hartid_to_cpuid(hartid);
 89
 90		rc = riscv_get_hart_index(fwnode, i, &hart_index);
 91		if (rc) {
 92			pr_warn("%pfwP: hart index [%d] not found\n", fwnode, i);
 93			return -EINVAL;
 94		}
 95		per_cpu(sswi_cpu_regs, cpu) = reg + hart_index * 4;
 96	}
 97
 98	pr_info("%pfwP: register %u CPU%s\n", fwnode, contexts, str_plural(contexts));
 99
100	return 0;
101}
102
103static int __init aclint_sswi_probe(struct fwnode_handle *fwnode)
104{
105	struct irq_domain *domain;
106	void __iomem *reg;
107	int virq, rc;
108
109	if (!is_of_node(fwnode))
110		return -EINVAL;
111
112	reg = of_iomap(to_of_node(fwnode), 0);
113	if (!reg)
114		return -ENOMEM;
115
116	/* Parse SSWI setting */
117	rc = aclint_sswi_parse_irq(fwnode, reg);
118	if (rc < 0)
119		return rc;
120
121	/* If mulitple SSWI devices are present, do not register irq again */
122	if (sswi_ipi_virq)
123		return 0;
124
125	/* Find riscv intc domain and create IPI irq mapping */
126	domain = irq_find_matching_fwnode(riscv_get_intc_hwnode(), DOMAIN_BUS_ANY);
127	if (!domain) {
128		pr_err("%pfwP: Failed to find INTC domain\n", fwnode);
129		return -ENOENT;
130	}
131
132	sswi_ipi_virq = irq_create_mapping(domain, RV_IRQ_SOFT);
133	if (!sswi_ipi_virq) {
134		pr_err("unable to create ACLINT SSWI IRQ mapping\n");
135		return -ENOMEM;
136	}
137
138	/* Register SSWI irq and handler */
139	virq = ipi_mux_create(BITS_PER_BYTE, aclint_sswi_ipi_send);
140	if (virq <= 0) {
141		pr_err("unable to create muxed IPIs\n");
142		irq_dispose_mapping(sswi_ipi_virq);
143		return virq < 0 ? virq : -ENOMEM;
144	}
145
146	irq_set_chained_handler(sswi_ipi_virq, aclint_sswi_ipi_handle);
147
148	cpuhp_setup_state(CPUHP_AP_IRQ_ACLINT_SSWI_STARTING,
149			  "irqchip/aclint-sswi:starting",
150			  aclint_sswi_starting_cpu,
151			  aclint_sswi_dying_cpu);
152
153	riscv_ipi_set_virq_range(virq, BITS_PER_BYTE);
154
155	return 0;
156}
157
158/* generic/MIPS variant */
159static int __init generic_aclint_sswi_probe(struct fwnode_handle *fwnode)
160{
161	int rc;
162
163	rc = aclint_sswi_probe(fwnode);
164	if (rc)
165		return rc;
166
167	/* Announce that SSWI is providing IPIs */
168	pr_info("providing IPIs using ACLINT SSWI\n");
169
170	return 0;
171}
172
173static int __init generic_aclint_sswi_early_probe(struct device_node *node,
174						  struct device_node *parent)
175{
176	return generic_aclint_sswi_probe(&node->fwnode);
177}
178IRQCHIP_DECLARE(mips_p8700_sswi, "mips,p8700-aclint-sswi", generic_aclint_sswi_early_probe);
179IRQCHIP_DECLARE(nuclei_ux900_sswi, "nuclei,ux900-aclint-sswi", generic_aclint_sswi_early_probe);
180
181/* THEAD variant */
182#define THEAD_C9XX_CSR_SXSTATUS			0x5c0
183#define THEAD_C9XX_SXSTATUS_CLINTEE		BIT(17)
184
185static int __init thead_aclint_sswi_probe(struct fwnode_handle *fwnode)
186{
187	int rc;
188
189	/* If it is T-HEAD CPU, check whether SSWI is enabled */
190	if (riscv_cached_mvendorid(0) == THEAD_VENDOR_ID &&
191	    !(csr_read(THEAD_C9XX_CSR_SXSTATUS) & THEAD_C9XX_SXSTATUS_CLINTEE))
192		return -ENOTSUPP;
193
194	rc = aclint_sswi_probe(fwnode);
195	if (rc)
196		return rc;
197
198	/* Announce that SSWI is providing IPIs */
199	pr_info("providing IPIs using THEAD ACLINT SSWI\n");
200
201	return 0;
202}
203
204static int __init thead_aclint_sswi_early_probe(struct device_node *node,
205						struct device_node *parent)
206{
207	return thead_aclint_sswi_probe(&node->fwnode);
208}
209IRQCHIP_DECLARE(thead_aclint_sswi, "thead,c900-aclint-sswi", thead_aclint_sswi_early_probe);
~/ Read-only file preview.