forked from torvalds/linux
-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
RISC-V: Treat IPIs as normal Linux IRQs
Currently, the RISC-V kernel provides arch specific hooks (i.e. struct riscv_ipi_ops) to register IPI handling methods. The stats gathering of IPIs is also arch specific in the RISC-V kernel. Other architectures (such as ARM, ARM64, and MIPS) have moved away from custom arch specific IPI handling methods. Currently, these architectures have Linux irqchip drivers providing a range of Linux IRQ numbers to be used as IPIs and IPI triggering is done using generic IPI APIs. This approach allows architectures to treat IPIs as normal Linux IRQs and IPI stats gathering is done by the generic Linux IRQ subsystem. We extend the RISC-V IPI handling as-per above approach so that arch specific IPI handling methods (struct riscv_ipi_ops) can be removed and the IPI handling is totally contained Linux within irqchip drivers. Signed-off-by: Anup Patel <anup.patel@wdc.com>
- Loading branch information
Showing
12 changed files
with
352 additions
and
172 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,223 @@ | ||
// SPDX-License-Identifier: GPL-2.0-only | ||
/* | ||
* SBI based IPI support. | ||
* | ||
* Copyright (c) 2021 Western Digital Corporation or its affiliates. | ||
*/ | ||
|
||
#define pr_fmt(fmt) "riscv-sbi-ipi: " fmt | ||
#include <linux/cpu.h> | ||
#include <linux/cpumask.h> | ||
#include <linux/init.h> | ||
#include <linux/irq.h> | ||
#include <linux/irqchip.h> | ||
#include <linux/irqchip/chained_irq.h> | ||
#include <linux/irqdomain.h> | ||
#include <linux/of.h> | ||
#include <linux/smp.h> | ||
#include <asm/sbi.h> | ||
|
||
static int intc_parent_irq __ro_after_init; | ||
static struct irq_domain *sbi_ipi_domain __ro_after_init; | ||
static DEFINE_PER_CPU(unsigned long, sbi_ipi_bits); | ||
|
||
static void sbi_ipi_dummy_mask_unmask(struct irq_data *d) | ||
{ | ||
} | ||
|
||
static void sbi_ipi_send_mask(struct irq_data *d, const struct cpumask *mask) | ||
{ | ||
int cpu; | ||
struct cpumask hartid_mask; | ||
|
||
/* Barrier before doing atomic bit update to IPI bits */ | ||
smp_mb__before_atomic(); | ||
for_each_cpu(cpu, mask) | ||
set_bit(d->hwirq, per_cpu_ptr(&sbi_ipi_bits, cpu)); | ||
/* Barrier after doing atomic bit update to IPI bits */ | ||
smp_mb__after_atomic(); | ||
|
||
riscv_cpuid_to_hartid_mask(mask, &hartid_mask); | ||
|
||
sbi_send_ipi(cpumask_bits(&hartid_mask)); | ||
} | ||
|
||
static struct irq_chip sbi_ipi_chip = { | ||
.name = "RISC-V SBI IPI", | ||
.irq_mask = sbi_ipi_dummy_mask_unmask, | ||
.irq_unmask = sbi_ipi_dummy_mask_unmask, | ||
.ipi_send_mask = sbi_ipi_send_mask, | ||
}; | ||
|
||
static int sbi_ipi_domain_map(struct irq_domain *d, unsigned int irq, | ||
irq_hw_number_t hwirq) | ||
{ | ||
irq_set_percpu_devid(irq); | ||
irq_domain_set_info(d, irq, hwirq, &sbi_ipi_chip, d->host_data, | ||
handle_percpu_devid_irq, NULL, NULL); | ||
|
||
return 0; | ||
} | ||
|
||
static int sbi_ipi_domain_alloc(struct irq_domain *d, unsigned int virq, | ||
unsigned int nr_irqs, void *arg) | ||
{ | ||
int i, ret; | ||
irq_hw_number_t hwirq; | ||
unsigned int type = IRQ_TYPE_NONE; | ||
struct irq_fwspec *fwspec = arg; | ||
|
||
ret = irq_domain_translate_onecell(d, fwspec, &hwirq, &type); | ||
if (ret) | ||
return ret; | ||
|
||
for (i = 0; i < nr_irqs; i++) { | ||
ret = sbi_ipi_domain_map(d, virq + i, hwirq + i); | ||
if (ret) | ||
return ret; | ||
} | ||
|
||
return 0; | ||
} | ||
|
||
static const struct irq_domain_ops sbi_ipi_domain_ops = { | ||
.translate = irq_domain_translate_onecell, | ||
.alloc = sbi_ipi_domain_alloc, | ||
.free = irq_domain_free_irqs_top, | ||
}; | ||
|
||
static void sbi_ipi_handle_irq(struct irq_desc *desc) | ||
{ | ||
int irq; | ||
struct irq_chip *chip = irq_desc_get_chip(desc); | ||
unsigned long irqs, *bits = this_cpu_ptr(&sbi_ipi_bits); | ||
irq_hw_number_t hwirq; | ||
|
||
chained_irq_enter(chip, desc); | ||
|
||
csr_clear(CSR_IP, IE_SIE); | ||
|
||
while (true) { | ||
/* Order bit clearing and data access. */ | ||
mb(); | ||
|
||
irqs = xchg(bits, 0); | ||
if (!irqs) | ||
goto done; | ||
|
||
for (hwirq = 0; hwirq < BITS_PER_LONG; hwirq++) { | ||
if (!(BIT(hwirq) & irqs)) | ||
continue; | ||
|
||
irq = irq_find_mapping(sbi_ipi_domain, hwirq); | ||
if (unlikely(irq <= 0)) | ||
pr_warn_ratelimited( | ||
"can't find mapping for hwirq %lu\n", | ||
hwirq); | ||
else | ||
generic_handle_irq(irq); | ||
} | ||
} | ||
|
||
done: | ||
chained_irq_exit(chip, desc); | ||
} | ||
|
||
static int sbi_ipi_dying_cpu(unsigned int cpu) | ||
{ | ||
disable_percpu_irq(intc_parent_irq); | ||
return 0; | ||
} | ||
|
||
static int sbi_ipi_starting_cpu(unsigned int cpu) | ||
{ | ||
enable_percpu_irq(intc_parent_irq, | ||
irq_get_trigger_type(intc_parent_irq)); | ||
return 0; | ||
} | ||
|
||
static int __init sbi_ipi_set_virq(void) | ||
{ | ||
int virq; | ||
struct irq_fwspec ipi = { | ||
.fwnode = sbi_ipi_domain->fwnode, | ||
.param_count = 1, | ||
.param[0] = 0, | ||
}; | ||
|
||
virq = __irq_domain_alloc_irqs(sbi_ipi_domain, -1, BITS_PER_LONG, | ||
NUMA_NO_NODE, &ipi, | ||
false, NULL); | ||
if (virq <= 0) { | ||
pr_err("unable to alloc IRQs from SBI IPI IRQ domain\n"); | ||
return -ENOMEM; | ||
} | ||
|
||
riscv_ipi_set_virq_range(virq, BITS_PER_LONG); | ||
|
||
return 0; | ||
} | ||
|
||
static int __init sbi_ipi_domain_init(struct device_node *node, | ||
struct irq_domain *domain) | ||
{ | ||
struct irq_fwspec swi = { | ||
.fwnode = domain->fwnode, | ||
.param_count = 1, | ||
.param[0] = RV_IRQ_SOFT, | ||
}; | ||
|
||
intc_parent_irq = __irq_domain_alloc_irqs(domain, -1, 1, | ||
NUMA_NO_NODE, &swi, | ||
false, NULL); | ||
if (intc_parent_irq <= 0) { | ||
pr_err("unable to alloc IRQ from INTC IRQ domain\n"); | ||
return -ENOMEM; | ||
} | ||
|
||
irq_set_chained_handler(intc_parent_irq, sbi_ipi_handle_irq); | ||
|
||
sbi_ipi_domain = irq_domain_add_linear(node, BITS_PER_LONG, | ||
&sbi_ipi_domain_ops, NULL); | ||
if (!sbi_ipi_domain) { | ||
pr_err("unable to add SBI IPI IRQ domain\n"); | ||
return -ENOMEM; | ||
} | ||
|
||
cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, | ||
"irqchip/riscv/sbi-ipi:starting", | ||
sbi_ipi_starting_cpu, sbi_ipi_dying_cpu); | ||
|
||
return sbi_ipi_set_virq(); | ||
} | ||
|
||
void __init sbi_ipi_init(void) | ||
{ | ||
struct irq_domain *domain = NULL; | ||
struct device_node *cpu, *child, *node = NULL; | ||
|
||
for_each_of_cpu_node(cpu) { | ||
child = of_get_compatible_child(cpu, "riscv,cpu-intc"); | ||
if (!child) { | ||
pr_err("failed to find INTC node [%pOF]\n", cpu); | ||
return; | ||
} | ||
|
||
domain = irq_find_host(child); | ||
if (domain) { | ||
node = cpu; | ||
break; | ||
} | ||
|
||
of_node_put(child); | ||
} | ||
if (!domain || !node) { | ||
pr_err("can't find INTC IRQ domain\n"); | ||
return; | ||
} | ||
|
||
if (sbi_ipi_domain_init(node, domain)) | ||
pr_err("failed to register IPI domain\n"); | ||
else | ||
pr_info("registered IPI domain\n"); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.