diff options
author | Minda Chen <minda.chen@starfivetech.com> | 2023-11-20 09:18:17 +0300 |
---|---|---|
committer | Minda Chen <minda.chen@starfivetech.com> | 2024-05-31 12:28:57 +0300 |
commit | 454bd993b165ddb6857c677cf7c519c5d8d7f475 (patch) | |
tree | b126d6e0eec30a6e86f92a5cb4839c19a2dbbe71 | |
parent | 55523997b9176a23f79315c03dededfe26e81ed2 (diff) | |
download | linux-454bd993b165ddb6857c677cf7c519c5d8d7f475.tar.xz |
sbi: riscv: add AMP ipi support
Add AMP opensbi command support and add IPI AMP
interrupt process.
Signed-off-by: Minda Chen <minda.chen@starfivetech.com>
-rw-r--r-- | arch/riscv/Kconfig | 4 | ||||
-rw-r--r-- | arch/riscv/include/asm/irq.h | 9 | ||||
-rw-r--r-- | arch/riscv/include/asm/sbi.h | 13 | ||||
-rw-r--r-- | arch/riscv/kernel/irq.c | 13 | ||||
-rw-r--r-- | arch/riscv/kernel/sbi-ipi.c | 48 | ||||
-rw-r--r-- | arch/riscv/kernel/sbi.c | 30 | ||||
-rw-r--r-- | arch/riscv/kernel/smp.c | 46 | ||||
-rw-r--r-- | kernel/irq/ipi-mux.c | 18 |
8 files changed, 177 insertions, 4 deletions
diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig index c50df02b78fd..1da66ef1eb58 100644 --- a/arch/riscv/Kconfig +++ b/arch/riscv/Kconfig @@ -395,6 +395,10 @@ config HOTPLUG_CPU Say N if you want to disable CPU hotplug. +config RISCV_AMP + bool "support for RISCV AMP" + depends on SMP && RISCV_SBI + choice prompt "CPU Tuning" default TUNE_GENERIC diff --git a/arch/riscv/include/asm/irq.h b/arch/riscv/include/asm/irq.h index 8e10a94430a2..57352e80ce61 100644 --- a/arch/riscv/include/asm/irq.h +++ b/arch/riscv/include/asm/irq.h @@ -16,4 +16,13 @@ void riscv_set_intc_hwnode_fn(struct fwnode_handle *(*fn)(void)); struct fwnode_handle *riscv_get_intc_hwnode(void); +#ifdef CONFIG_RISCV_AMP +#define IPI_AMP 15 +void riscv_set_ipi_amp_enable(void); +int riscv_get_ipi_amp_enable(void); +void ipi_set_extra_bits(unsigned long (*func)(void)); +unsigned long riscv_clear_amp_bits(void); +void register_ipi_mailbox_handler(void (*handler)(unsigned long)); +#endif + #endif /* _ASM_RISCV_IRQ_H */ diff --git a/arch/riscv/include/asm/sbi.h b/arch/riscv/include/asm/sbi.h index b79d0228144f..2be05510f25b 100644 --- a/arch/riscv/include/asm/sbi.h +++ b/arch/riscv/include/asm/sbi.h @@ -56,6 +56,9 @@ enum sbi_ext_time_fid { enum sbi_ext_ipi_fid { SBI_EXT_IPI_SEND_IPI = 0, + SBI_EXT_IPI_SEND_EXT_DOMAIN = 0x100, + SBI_EXT_IPI_SET_AMP_DATA_ADDR = 0x101, + SBI_EXT_IPI_CLEAR_IPI = 0x102, }; enum sbi_ext_rfence_fid { @@ -253,6 +256,12 @@ enum sbi_pmu_ctr_type { #define SBI_ERR_ALREADY_STOPPED -8 extern unsigned long sbi_spec_version; +#ifdef CONFIG_RISCV_AMP +struct amp_data { + unsigned long amp_bits; +}; +#endif + struct sbiret { long error; long value; @@ -337,4 +346,8 @@ void sbi_ipi_init(void); static inline void sbi_ipi_init(void) { } #endif +#ifdef CONFIG_RISCV_AMP +int sbi_send_ipi_amp(unsigned int hartid, unsigned int msg_type); +int sbi_amp_data_init(void *riscv_amp_data); +#endif #endif /* _ASM_RISCV_SBI_H */ diff --git a/arch/riscv/kernel/irq.c b/arch/riscv/kernel/irq.c index 9cc0a7669271..4552b24f6d0c 100644 --- a/arch/riscv/kernel/irq.c +++ b/arch/riscv/kernel/irq.c @@ -17,6 +17,19 @@ static struct fwnode_handle *(*__get_intc_node)(void); +#ifdef CONFIG_RISCV_AMP +static int ipi_amp_enable; +void riscv_set_ipi_amp_enable(void) +{ + ipi_amp_enable = 1; +} + +int riscv_get_ipi_amp_enable(void) +{ + return ipi_amp_enable; +} +#endif + void riscv_set_intc_hwnode_fn(struct fwnode_handle *(*fn)(void)) { __get_intc_node = fn; diff --git a/arch/riscv/kernel/sbi-ipi.c b/arch/riscv/kernel/sbi-ipi.c index a4559695ce62..144e608f4b6b 100644 --- a/arch/riscv/kernel/sbi-ipi.c +++ b/arch/riscv/kernel/sbi-ipi.c @@ -15,6 +15,33 @@ static int sbi_ipi_virq; +#ifdef CONFIG_RISCV_AMP +static struct amp_data riscv_amp_data[NR_CPUS] __cacheline_aligned; + +static unsigned long riscv_get_extra_bits(void) +{ + int cpu_id; + unsigned long bits = 0; + + cpu_id = smp_processor_id(); + if (riscv_amp_data[cpuid_to_hartid_map(cpu_id)].amp_bits) + bits |= BIT(IPI_AMP); + + return bits; +} + +unsigned long riscv_clear_amp_bits(void) +{ + int cpu_id; + unsigned long *ops; + + /*atomic ops */ + cpu_id = smp_processor_id(); + ops = &riscv_amp_data[cpuid_to_hartid_map(cpu_id)].amp_bits; + return xchg(ops, 0); +} +#endif + static void sbi_ipi_handle(struct irq_desc *desc) { struct irq_chip *chip = irq_desc_get_chip(desc); @@ -35,13 +62,15 @@ static int sbi_ipi_starting_cpu(unsigned int cpu) void __init sbi_ipi_init(void) { - int virq; + int virq, irq_num; struct irq_domain *domain; + struct fwnode_handle *node; if (riscv_ipi_have_virq_range()) return; - domain = irq_find_matching_fwnode(riscv_get_intc_hwnode(), + node = riscv_get_intc_hwnode(); + domain = irq_find_matching_fwnode(node, DOMAIN_BUS_ANY); if (!domain) { pr_err("unable to find INTC IRQ domain\n"); @@ -53,8 +82,19 @@ void __init sbi_ipi_init(void) pr_err("unable to create INTC IRQ mapping\n"); return; } +#ifdef CONFIG_RISCV_AMP + if (fwnode_property_present(node, "enable-ipi-amp")) { + riscv_set_ipi_amp_enable(); + sbi_amp_data_init(riscv_amp_data); + ipi_set_extra_bits(riscv_get_extra_bits); + irq_num = BITS_PER_TYPE(short); + } else + irq_num = BITS_PER_BYTE; +#else + irq_num = BITS_PER_BYTE; +#endif - virq = ipi_mux_create(BITS_PER_BYTE, sbi_send_ipi); + virq = ipi_mux_create(irq_num, sbi_send_ipi); if (virq <= 0) { pr_err("unable to create muxed IPIs\n"); irq_dispose_mapping(sbi_ipi_virq); @@ -72,6 +112,6 @@ void __init sbi_ipi_init(void) "irqchip/sbi-ipi:starting", sbi_ipi_starting_cpu, NULL); - riscv_ipi_set_virq_range(virq, BITS_PER_BYTE, false); + riscv_ipi_set_virq_range(virq, irq_num, false); pr_info("providing IPIs using SBI IPI extension\n"); } diff --git a/arch/riscv/kernel/sbi.c b/arch/riscv/kernel/sbi.c index 5a62ed1da453..aa54c9549ba3 100644 --- a/arch/riscv/kernel/sbi.c +++ b/arch/riscv/kernel/sbi.c @@ -9,6 +9,7 @@ #include <linux/init.h> #include <linux/pm.h> #include <linux/reboot.h> +#include <asm/io.h> #include <asm/sbi.h> #include <asm/smp.h> #include <asm/tlbflush.h> @@ -364,6 +365,35 @@ void sbi_send_ipi(unsigned int cpu) } EXPORT_SYMBOL(sbi_send_ipi); +#ifdef CONFIG_RISCV_AMP +int sbi_send_ipi_amp(unsigned int hartid, unsigned int msg_type) +{ + struct sbiret ret = {0}; + + ret = sbi_ecall(SBI_EXT_IPI, SBI_EXT_IPI_SEND_EXT_DOMAIN, + 0, hartid, msg_type, 0, 0, 0); + + if (ret.error) + pr_err("sbi ipi amp error"); + + return ret.error; +} +EXPORT_SYMBOL_GPL(sbi_send_ipi_amp); + +int sbi_amp_data_init(void *riscv_amp_data) +{ + struct sbiret ret = {0}; + + ret = sbi_ecall(SBI_EXT_IPI, SBI_EXT_IPI_SET_AMP_DATA_ADDR, + virt_to_phys((void *)riscv_amp_data), 0, + 0, 0, 0, 0); + if (ret.error) + pr_err("set ipi data error"); + + return ret.error; +} +#endif + /** * sbi_remote_fence_i() - Execute FENCE.I instruction on given remote harts. * @cpu_mask: A cpu mask containing all the target harts. diff --git a/arch/riscv/kernel/smp.c b/arch/riscv/kernel/smp.c index 40420afbb1a0..5bd06a1460e9 100644 --- a/arch/riscv/kernel/smp.c +++ b/arch/riscv/kernel/smp.c @@ -49,6 +49,21 @@ static DEFINE_PER_CPU_READ_MOSTLY(int, ipi_dummy_dev); static int ipi_virq_base __ro_after_init; static int nr_ipi __ro_after_init = IPI_MAX; static struct irq_desc *ipi_desc[IPI_MAX] __read_mostly; +#ifdef CONFIG_RISCV_AMP +static struct irq_desc *amp_desc; +static void (*ipi_mailbox_handler)(unsigned long msg_type); +void ipi_amp_handle(unsigned long msg_type) +{ + if (ipi_mailbox_handler) + ipi_mailbox_handler(msg_type); +} + +void register_ipi_mailbox_handler(void (*handler)(unsigned long)) +{ + ipi_mailbox_handler = handler; +} +EXPORT_SYMBOL_GPL(register_ipi_mailbox_handler); +#endif int riscv_hartid_to_cpuid(unsigned long hartid) { @@ -136,6 +151,11 @@ static irqreturn_t handle_IPI(int irq, void *data) tick_receive_broadcast(); break; #endif +#ifdef CONFIG_RISCV_AMP + case IPI_AMP: + ipi_amp_handle(riscv_clear_amp_bits()); + break; +#endif default: pr_warn("CPU%d: unhandled IPI%d\n", smp_processor_id(), ipi); break; @@ -153,6 +173,10 @@ void riscv_ipi_enable(void) for (i = 0; i < nr_ipi; i++) enable_percpu_irq(ipi_virq_base + i, 0); +#ifdef CONFIG_RISCV_AMP + if (riscv_get_ipi_amp_enable()) + enable_percpu_irq(ipi_virq_base + IPI_AMP, 0); +#endif } void riscv_ipi_disable(void) @@ -164,6 +188,10 @@ void riscv_ipi_disable(void) for (i = 0; i < nr_ipi; i++) disable_percpu_irq(ipi_virq_base + i); +#ifdef CONFIG_RISCV_AMP + if (riscv_get_ipi_amp_enable()) + disable_percpu_irq(ipi_virq_base + IPI_AMP); +#endif } bool riscv_ipi_have_virq_range(void) @@ -194,7 +222,16 @@ void riscv_ipi_set_virq_range(int virq, int nr, bool use_for_rfence) ipi_desc[i] = irq_to_desc(ipi_virq_base + i); irq_set_status_flags(ipi_virq_base + i, IRQ_HIDDEN); } +#ifdef CONFIG_RISCV_AMP + if (riscv_get_ipi_amp_enable()) { + err = request_percpu_irq(ipi_virq_base + IPI_AMP, handle_IPI, + "IPI", &ipi_dummy_dev); + WARN_ON(err); + amp_desc = irq_to_desc(ipi_virq_base + IPI_AMP); + irq_set_status_flags(ipi_virq_base + IPI_AMP, IRQ_HIDDEN); + } +#endif /* Enabled IPIs for boot CPU immediately */ riscv_ipi_enable(); @@ -225,6 +262,15 @@ void show_ipi_stats(struct seq_file *p, int prec) seq_printf(p, "%10u ", irq_desc_kstat_cpu(ipi_desc[i], cpu)); seq_printf(p, " %s\n", ipi_names[i]); } +#ifdef CONFIG_RISCV_AMP + if (riscv_get_ipi_amp_enable()) { + seq_printf(p, "%*s:%s", prec - 1, "IAMP", + prec >= 4 ? " " : ""); + for_each_online_cpu(cpu) + seq_printf(p, "%10u ", irq_desc_kstat_cpu(amp_desc, cpu)); + seq_printf(p, " %s\n", "AMP rpmsg interrupts"); + } +#endif } void arch_send_call_function_ipi_mask(struct cpumask *mask) diff --git a/kernel/irq/ipi-mux.c b/kernel/irq/ipi-mux.c index fa4fc18c6131..6eb5d65932d3 100644 --- a/kernel/irq/ipi-mux.c +++ b/kernel/irq/ipi-mux.c @@ -26,6 +26,14 @@ static struct ipi_mux_cpu __percpu *ipi_mux_pcpu; static struct irq_domain *ipi_mux_domain; static void (*ipi_mux_send)(unsigned int cpu); +#ifdef CONFIG_RISCV_AMP +static unsigned long (*arch_get_extra_bits)(void); +void ipi_set_extra_bits(unsigned long (*func)(void)) +{ + arch_get_extra_bits = func; +} +#endif + static void ipi_mux_mask(struct irq_data *d) { struct ipi_mux_cpu *icpu = this_cpu_ptr(ipi_mux_pcpu); @@ -139,6 +147,16 @@ void ipi_mux_process(void) for_each_set_bit(hwirq, &ipis, BITS_PER_TYPE(int)) generic_handle_domain_irq(ipi_mux_domain, hwirq); + +#ifdef CONFIG_RISCV_AMP + unsigned long extra_ipis; + + if (arch_get_extra_bits) { + extra_ipis = arch_get_extra_bits(); + for_each_set_bit(hwirq, &extra_ipis, BITS_PER_TYPE(int)) + generic_handle_domain_irq(ipi_mux_domain, hwirq); + } +#endif } /** |