mirror of
https://github.com/armbian/linux-cix.git
synced 2026-01-06 12:30:45 -08:00
Merge tag 'irq-core-2023-04-24' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
Pull interrupt updates from Thomas Gleixner:
"Core:
- Add tracepoints for tasklet callbacks which makes it possible to
analyze individual tasklet functions instead of guess working from
the overall duration of tasklet processing
- Ensure that secondary interrupt threads have their affinity
adjusted correctly
Drivers:
- A large rework of the RISC-V IPI management to prepare for a new
RISC-V interrupt architecture
- Small fixes and enhancements all over the place
- Removal of support for various obsolete hardware platforms and the
related code"
* tag 'irq-core-2023-04-24' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: (21 commits)
irqchip/st: Remove stih415/stih416 and stid127 platforms support
irqchip/gic-v3: Add Rockchip 3588001 erratum workaround
genirq: Update affinity of secondary threads
softirq: Add trace points for tasklet entry/exit
irqchip/loongson-pch-pic: Fix pch_pic_acpi_init calling
irqchip/loongson-pch-pic: Fix registration of syscore_ops
irqchip/loongson-eiointc: Fix registration of syscore_ops
irqchip/loongson-eiointc: Fix incorrect use of acpi_get_vec_parent
irqchip/loongson-eiointc: Fix returned value on parsing MADT
irqchip/riscv-intc: Add empty irq_eoi() for chained irq handlers
RISC-V: Use IPIs for remote icache flush when possible
RISC-V: Use IPIs for remote TLB flush when possible
RISC-V: Allow marking IPIs as suitable for remote FENCEs
RISC-V: Treat IPIs as normal Linux IRQs
irqchip/riscv-intc: Allow drivers to directly discover INTC hwnode
RISC-V: Clear SIP bit only when using SBI IPI operations
irqchip/irq-sifive-plic: Add syscore callbacks for hibernation
irqchip: Use of_property_read_bool() for boolean properties
irqchip/bcm-6345-l1: Request memory region
irqchip/gicv3: Workaround for NVIDIA erratum T241-FABRIC-4
...
This commit is contained in:
@@ -172,6 +172,8 @@ stable kernels.
|
||||
+----------------+-----------------+-----------------+-----------------------------+
|
||||
| NVIDIA | Carmel Core | N/A | NVIDIA_CARMEL_CNP_ERRATUM |
|
||||
+----------------+-----------------+-----------------+-----------------------------+
|
||||
| NVIDIA | T241 GICv3/4.x | T241-FABRIC-4 | N/A |
|
||||
+----------------+-----------------+-----------------+-----------------------------+
|
||||
+----------------+-----------------+-----------------+-----------------------------+
|
||||
| Freescale/NXP | LS2080A/LS1043A | A-008585 | FSL_ERRATUM_A008585 |
|
||||
+----------------+-----------------+-----------------+-----------------------------+
|
||||
@@ -205,6 +207,9 @@ stable kernels.
|
||||
+----------------+-----------------+-----------------+-----------------------------+
|
||||
| Qualcomm Tech. | Kryo4xx Gold | N/A | ARM64_ERRATUM_1286807 |
|
||||
+----------------+-----------------+-----------------+-----------------------------+
|
||||
+----------------+-----------------+-----------------+-----------------------------+
|
||||
| Rockchip | RK3588 | #3588001 | ROCKCHIP_ERRATUM_3588001 |
|
||||
+----------------+-----------------+-----------------+-----------------------------+
|
||||
|
||||
+----------------+-----------------+-----------------+-----------------------------+
|
||||
| Fujitsu | A64FX | E#010001 | FUJITSU_ERRATUM_010001 |
|
||||
|
||||
@@ -1150,6 +1150,16 @@ config NVIDIA_CARMEL_CNP_ERRATUM
|
||||
|
||||
If unsure, say Y.
|
||||
|
||||
config ROCKCHIP_ERRATUM_3588001
|
||||
bool "Rockchip 3588001: GIC600 can not support shareability attributes"
|
||||
default y
|
||||
help
|
||||
The Rockchip RK3588 GIC600 SoC integration does not support ACE/ACE-lite.
|
||||
This means, that its sharability feature may not be used, even though it
|
||||
is supported by the IP itself.
|
||||
|
||||
If unsure, say Y.
|
||||
|
||||
config SOCIONEXT_SYNQUACER_PREITS
|
||||
bool "Socionext Synquacer: Workaround for GICv3 pre-ITS"
|
||||
default y
|
||||
|
||||
@@ -63,6 +63,8 @@ config RISCV
|
||||
select GENERIC_GETTIMEOFDAY if HAVE_GENERIC_VDSO
|
||||
select GENERIC_IDLE_POLL_SETUP
|
||||
select GENERIC_IOREMAP if MMU
|
||||
select GENERIC_IRQ_IPI if SMP
|
||||
select GENERIC_IRQ_IPI_MUX if SMP
|
||||
select GENERIC_IRQ_MULTI_HANDLER
|
||||
select GENERIC_IRQ_SHOW
|
||||
select GENERIC_IRQ_SHOW_LEVEL
|
||||
|
||||
@@ -12,6 +12,10 @@
|
||||
|
||||
#include <asm-generic/irq.h>
|
||||
|
||||
void riscv_set_intc_hwnode_fn(struct fwnode_handle *(*fn)(void));
|
||||
|
||||
struct fwnode_handle *riscv_get_intc_hwnode(void);
|
||||
|
||||
extern void __init init_IRQ(void);
|
||||
|
||||
#endif /* _ASM_RISCV_IRQ_H */
|
||||
|
||||
@@ -271,8 +271,7 @@ long sbi_get_marchid(void);
|
||||
long sbi_get_mimpid(void);
|
||||
void sbi_set_timer(uint64_t stime_value);
|
||||
void sbi_shutdown(void);
|
||||
void sbi_clear_ipi(void);
|
||||
int sbi_send_ipi(const struct cpumask *cpu_mask);
|
||||
void sbi_send_ipi(unsigned int cpu);
|
||||
int sbi_remote_fence_i(const struct cpumask *cpu_mask);
|
||||
int sbi_remote_sfence_vma(const struct cpumask *cpu_mask,
|
||||
unsigned long start,
|
||||
@@ -335,4 +334,10 @@ unsigned long riscv_cached_mvendorid(unsigned int cpu_id);
|
||||
unsigned long riscv_cached_marchid(unsigned int cpu_id);
|
||||
unsigned long riscv_cached_mimpid(unsigned int cpu_id);
|
||||
|
||||
#if IS_ENABLED(CONFIG_SMP) && IS_ENABLED(CONFIG_RISCV_SBI)
|
||||
void sbi_ipi_init(void);
|
||||
#else
|
||||
static inline void sbi_ipi_init(void) { }
|
||||
#endif
|
||||
|
||||
#endif /* _ASM_RISCV_SBI_H */
|
||||
|
||||
@@ -15,12 +15,10 @@
|
||||
struct seq_file;
|
||||
extern unsigned long boot_cpu_hartid;
|
||||
|
||||
struct riscv_ipi_ops {
|
||||
void (*ipi_inject)(const struct cpumask *target);
|
||||
void (*ipi_clear)(void);
|
||||
};
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
|
||||
#include <linux/jump_label.h>
|
||||
|
||||
/*
|
||||
* Mapping between linux logical cpu index and hartid.
|
||||
*/
|
||||
@@ -33,9 +31,6 @@ void show_ipi_stats(struct seq_file *p, int prec);
|
||||
/* SMP initialization hook for setup_arch */
|
||||
void __init setup_smp(void);
|
||||
|
||||
/* Called from C code, this handles an IPI. */
|
||||
void handle_IPI(struct pt_regs *regs);
|
||||
|
||||
/* Hook for the generic smp_call_function_many() routine. */
|
||||
void arch_send_call_function_ipi_mask(struct cpumask *mask);
|
||||
|
||||
@@ -44,11 +39,22 @@ void arch_send_call_function_single_ipi(int cpu);
|
||||
|
||||
int riscv_hartid_to_cpuid(unsigned long hartid);
|
||||
|
||||
/* Set custom IPI operations */
|
||||
void riscv_set_ipi_ops(const struct riscv_ipi_ops *ops);
|
||||
/* Enable IPI for CPU hotplug */
|
||||
void riscv_ipi_enable(void);
|
||||
|
||||
/* Clear IPI for current CPU */
|
||||
void riscv_clear_ipi(void);
|
||||
/* Disable IPI for CPU hotplug */
|
||||
void riscv_ipi_disable(void);
|
||||
|
||||
/* Check if IPI interrupt numbers are available */
|
||||
bool riscv_ipi_have_virq_range(void);
|
||||
|
||||
/* Set the IPI interrupt numbers for arch (called by irqchip drivers) */
|
||||
void riscv_ipi_set_virq_range(int virq, int nr, bool use_for_rfence);
|
||||
|
||||
/* Check if we can use IPIs for remote FENCEs */
|
||||
DECLARE_STATIC_KEY_FALSE(riscv_ipi_for_rfence);
|
||||
#define riscv_use_ipi_for_rfence() \
|
||||
static_branch_unlikely(&riscv_ipi_for_rfence)
|
||||
|
||||
/* Check other CPUs stop or not */
|
||||
bool smp_crash_stop_failed(void);
|
||||
@@ -85,14 +91,29 @@ static inline unsigned long cpuid_to_hartid_map(int cpu)
|
||||
return boot_cpu_hartid;
|
||||
}
|
||||
|
||||
static inline void riscv_set_ipi_ops(const struct riscv_ipi_ops *ops)
|
||||
static inline void riscv_ipi_enable(void)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void riscv_clear_ipi(void)
|
||||
static inline void riscv_ipi_disable(void)
|
||||
{
|
||||
}
|
||||
|
||||
static inline bool riscv_ipi_have_virq_range(void)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline void riscv_ipi_set_virq_range(int virq, int nr,
|
||||
bool use_for_rfence)
|
||||
{
|
||||
}
|
||||
|
||||
static inline bool riscv_use_ipi_for_rfence(void)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_SMP */
|
||||
|
||||
#if defined(CONFIG_HOTPLUG_CPU) && (CONFIG_SMP)
|
||||
|
||||
@@ -74,6 +74,7 @@ obj-$(CONFIG_PERF_EVENTS) += perf_callchain.o
|
||||
obj-$(CONFIG_HAVE_PERF_REGS) += perf_regs.o
|
||||
obj-$(CONFIG_RISCV_SBI) += sbi.o
|
||||
ifeq ($(CONFIG_RISCV_SBI), y)
|
||||
obj-$(CONFIG_SMP) += sbi-ipi.o
|
||||
obj-$(CONFIG_SMP) += cpu_ops_sbi.o
|
||||
endif
|
||||
obj-$(CONFIG_HOTPLUG_CPU) += cpu-hotplug.o
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
#include <asm/irq.h>
|
||||
#include <asm/cpu_ops.h>
|
||||
#include <asm/numa.h>
|
||||
#include <asm/sbi.h>
|
||||
#include <asm/smp.h>
|
||||
|
||||
bool cpu_has_hotplug(unsigned int cpu)
|
||||
{
|
||||
@@ -43,6 +43,7 @@ int __cpu_disable(void)
|
||||
remove_cpu_topology(cpu);
|
||||
numa_remove_cpu(cpu);
|
||||
set_cpu_online(cpu, false);
|
||||
riscv_ipi_disable();
|
||||
irq_migrate_all_off_this_cpu();
|
||||
|
||||
return ret;
|
||||
|
||||
@@ -7,8 +7,26 @@
|
||||
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irqchip.h>
|
||||
#include <linux/irqdomain.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <asm/smp.h>
|
||||
#include <asm/sbi.h>
|
||||
|
||||
static struct fwnode_handle *(*__get_intc_node)(void);
|
||||
|
||||
void riscv_set_intc_hwnode_fn(struct fwnode_handle *(*fn)(void))
|
||||
{
|
||||
__get_intc_node = fn;
|
||||
}
|
||||
|
||||
struct fwnode_handle *riscv_get_intc_hwnode(void)
|
||||
{
|
||||
if (__get_intc_node)
|
||||
return __get_intc_node();
|
||||
|
||||
return NULL;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(riscv_get_intc_hwnode);
|
||||
|
||||
int arch_show_interrupts(struct seq_file *p, int prec)
|
||||
{
|
||||
@@ -21,4 +39,5 @@ void __init init_IRQ(void)
|
||||
irqchip_init();
|
||||
if (!handle_arch_irq)
|
||||
panic("No interrupt controller found.");
|
||||
sbi_ipi_init();
|
||||
}
|
||||
|
||||
77
arch/riscv/kernel/sbi-ipi.c
Normal file
77
arch/riscv/kernel/sbi-ipi.c
Normal file
@@ -0,0 +1,77 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Multiplex several IPIs over a single HW IPI.
|
||||
*
|
||||
* Copyright (c) 2022 Ventana Micro Systems Inc.
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) "riscv: " fmt
|
||||
#include <linux/cpu.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/irqchip/chained_irq.h>
|
||||
#include <linux/irqdomain.h>
|
||||
#include <asm/sbi.h>
|
||||
|
||||
static int sbi_ipi_virq;
|
||||
|
||||
static void sbi_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);
|
||||
ipi_mux_process();
|
||||
|
||||
chained_irq_exit(chip, desc);
|
||||
}
|
||||
|
||||
static int sbi_ipi_starting_cpu(unsigned int cpu)
|
||||
{
|
||||
enable_percpu_irq(sbi_ipi_virq, irq_get_trigger_type(sbi_ipi_virq));
|
||||
return 0;
|
||||
}
|
||||
|
||||
void __init sbi_ipi_init(void)
|
||||
{
|
||||
int virq;
|
||||
struct irq_domain *domain;
|
||||
|
||||
if (riscv_ipi_have_virq_range())
|
||||
return;
|
||||
|
||||
domain = irq_find_matching_fwnode(riscv_get_intc_hwnode(),
|
||||
DOMAIN_BUS_ANY);
|
||||
if (!domain) {
|
||||
pr_err("unable to find INTC IRQ domain\n");
|
||||
return;
|
||||
}
|
||||
|
||||
sbi_ipi_virq = irq_create_mapping(domain, RV_IRQ_SOFT);
|
||||
if (!sbi_ipi_virq) {
|
||||
pr_err("unable to create INTC IRQ mapping\n");
|
||||
return;
|
||||
}
|
||||
|
||||
virq = ipi_mux_create(BITS_PER_BYTE, sbi_send_ipi);
|
||||
if (virq <= 0) {
|
||||
pr_err("unable to create muxed IPIs\n");
|
||||
irq_dispose_mapping(sbi_ipi_virq);
|
||||
return;
|
||||
}
|
||||
|
||||
irq_set_chained_handler(sbi_ipi_virq, sbi_ipi_handle);
|
||||
|
||||
/*
|
||||
* Don't disable IPI when CPU goes offline because
|
||||
* the masking/unmasking of virtual IPIs is done
|
||||
* via generic IPI-Mux
|
||||
*/
|
||||
cpuhp_setup_state(CPUHP_AP_ONLINE_DYN,
|
||||
"irqchip/sbi-ipi:starting",
|
||||
sbi_ipi_starting_cpu, NULL);
|
||||
|
||||
riscv_ipi_set_virq_range(virq, BITS_PER_BYTE, false);
|
||||
pr_info("providing IPIs using SBI IPI extension\n");
|
||||
}
|
||||
@@ -17,7 +17,7 @@ unsigned long sbi_spec_version __ro_after_init = SBI_SPEC_VERSION_DEFAULT;
|
||||
EXPORT_SYMBOL(sbi_spec_version);
|
||||
|
||||
static void (*__sbi_set_timer)(uint64_t stime) __ro_after_init;
|
||||
static int (*__sbi_send_ipi)(const struct cpumask *cpu_mask) __ro_after_init;
|
||||
static void (*__sbi_send_ipi)(unsigned int cpu) __ro_after_init;
|
||||
static int (*__sbi_rfence)(int fid, const struct cpumask *cpu_mask,
|
||||
unsigned long start, unsigned long size,
|
||||
unsigned long arg4, unsigned long arg5) __ro_after_init;
|
||||
@@ -130,17 +130,6 @@ void sbi_shutdown(void)
|
||||
}
|
||||
EXPORT_SYMBOL(sbi_shutdown);
|
||||
|
||||
/**
|
||||
* sbi_clear_ipi() - Clear any pending IPIs for the calling hart.
|
||||
*
|
||||
* Return: None
|
||||
*/
|
||||
void sbi_clear_ipi(void)
|
||||
{
|
||||
sbi_ecall(SBI_EXT_0_1_CLEAR_IPI, 0, 0, 0, 0, 0, 0, 0);
|
||||
}
|
||||
EXPORT_SYMBOL(sbi_clear_ipi);
|
||||
|
||||
/**
|
||||
* __sbi_set_timer_v01() - Program the timer for next timer event.
|
||||
* @stime_value: The value after which next timer event should fire.
|
||||
@@ -157,17 +146,12 @@ static void __sbi_set_timer_v01(uint64_t stime_value)
|
||||
#endif
|
||||
}
|
||||
|
||||
static int __sbi_send_ipi_v01(const struct cpumask *cpu_mask)
|
||||
static void __sbi_send_ipi_v01(unsigned int cpu)
|
||||
{
|
||||
unsigned long hart_mask;
|
||||
|
||||
if (!cpu_mask || cpumask_empty(cpu_mask))
|
||||
cpu_mask = cpu_online_mask;
|
||||
hart_mask = __sbi_v01_cpumask_to_hartmask(cpu_mask);
|
||||
|
||||
unsigned long hart_mask =
|
||||
__sbi_v01_cpumask_to_hartmask(cpumask_of(cpu));
|
||||
sbi_ecall(SBI_EXT_0_1_SEND_IPI, 0, (unsigned long)(&hart_mask),
|
||||
0, 0, 0, 0, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __sbi_rfence_v01(int fid, const struct cpumask *cpu_mask,
|
||||
@@ -216,12 +200,10 @@ static void __sbi_set_timer_v01(uint64_t stime_value)
|
||||
sbi_major_version(), sbi_minor_version());
|
||||
}
|
||||
|
||||
static int __sbi_send_ipi_v01(const struct cpumask *cpu_mask)
|
||||
static void __sbi_send_ipi_v01(unsigned int cpu)
|
||||
{
|
||||
pr_warn("IPI extension is not available in SBI v%lu.%lu\n",
|
||||
sbi_major_version(), sbi_minor_version());
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __sbi_rfence_v01(int fid, const struct cpumask *cpu_mask,
|
||||
@@ -248,55 +230,18 @@ static void __sbi_set_timer_v02(uint64_t stime_value)
|
||||
#endif
|
||||
}
|
||||
|
||||
static int __sbi_send_ipi_v02(const struct cpumask *cpu_mask)
|
||||
static void __sbi_send_ipi_v02(unsigned int cpu)
|
||||
{
|
||||
unsigned long hartid, cpuid, hmask = 0, hbase = 0, htop = 0;
|
||||
struct sbiret ret = {0};
|
||||
int result;
|
||||
struct sbiret ret = {0};
|
||||
|
||||
if (!cpu_mask || cpumask_empty(cpu_mask))
|
||||
cpu_mask = cpu_online_mask;
|
||||
|
||||
for_each_cpu(cpuid, cpu_mask) {
|
||||
hartid = cpuid_to_hartid_map(cpuid);
|
||||
if (hmask) {
|
||||
if (hartid + BITS_PER_LONG <= htop ||
|
||||
hbase + BITS_PER_LONG <= hartid) {
|
||||
ret = sbi_ecall(SBI_EXT_IPI,
|
||||
SBI_EXT_IPI_SEND_IPI, hmask,
|
||||
hbase, 0, 0, 0, 0);
|
||||
if (ret.error)
|
||||
goto ecall_failed;
|
||||
hmask = 0;
|
||||
} else if (hartid < hbase) {
|
||||
/* shift the mask to fit lower hartid */
|
||||
hmask <<= hbase - hartid;
|
||||
hbase = hartid;
|
||||
}
|
||||
}
|
||||
if (!hmask) {
|
||||
hbase = hartid;
|
||||
htop = hartid;
|
||||
} else if (hartid > htop) {
|
||||
htop = hartid;
|
||||
}
|
||||
hmask |= BIT(hartid - hbase);
|
||||
ret = sbi_ecall(SBI_EXT_IPI, SBI_EXT_IPI_SEND_IPI,
|
||||
1UL, cpuid_to_hartid_map(cpu), 0, 0, 0, 0);
|
||||
if (ret.error) {
|
||||
result = sbi_err_map_linux_errno(ret.error);
|
||||
pr_err("%s: hbase = [%lu] failed (error [%d])\n",
|
||||
__func__, cpuid_to_hartid_map(cpu), result);
|
||||
}
|
||||
|
||||
if (hmask) {
|
||||
ret = sbi_ecall(SBI_EXT_IPI, SBI_EXT_IPI_SEND_IPI,
|
||||
hmask, hbase, 0, 0, 0, 0);
|
||||
if (ret.error)
|
||||
goto ecall_failed;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
ecall_failed:
|
||||
result = sbi_err_map_linux_errno(ret.error);
|
||||
pr_err("%s: hbase = [%lu] hmask = [0x%lx] failed (error [%d])\n",
|
||||
__func__, hbase, hmask, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
static int __sbi_rfence_v02_call(unsigned long fid, unsigned long hmask,
|
||||
@@ -410,13 +355,11 @@ void sbi_set_timer(uint64_t stime_value)
|
||||
|
||||
/**
|
||||
* sbi_send_ipi() - Send an IPI to any hart.
|
||||
* @cpu_mask: A cpu mask containing all the target harts.
|
||||
*
|
||||
* Return: 0 on success, appropriate linux error code otherwise.
|
||||
* @cpu: Logical id of the target CPU.
|
||||
*/
|
||||
int sbi_send_ipi(const struct cpumask *cpu_mask)
|
||||
void sbi_send_ipi(unsigned int cpu)
|
||||
{
|
||||
return __sbi_send_ipi(cpu_mask);
|
||||
__sbi_send_ipi(cpu);
|
||||
}
|
||||
EXPORT_SYMBOL(sbi_send_ipi);
|
||||
|
||||
@@ -641,15 +584,6 @@ long sbi_get_mimpid(void)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(sbi_get_mimpid);
|
||||
|
||||
static void sbi_send_cpumask_ipi(const struct cpumask *target)
|
||||
{
|
||||
sbi_send_ipi(target);
|
||||
}
|
||||
|
||||
static const struct riscv_ipi_ops sbi_ipi_ops = {
|
||||
.ipi_inject = sbi_send_cpumask_ipi
|
||||
};
|
||||
|
||||
void __init sbi_init(void)
|
||||
{
|
||||
int ret;
|
||||
@@ -696,6 +630,4 @@ void __init sbi_init(void)
|
||||
__sbi_send_ipi = __sbi_send_ipi_v01;
|
||||
__sbi_rfence = __sbi_rfence_v01;
|
||||
}
|
||||
|
||||
riscv_set_ipi_ops(&sbi_ipi_ops);
|
||||
}
|
||||
|
||||
@@ -13,14 +13,15 @@
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/kexec.h>
|
||||
#include <linux/percpu.h>
|
||||
#include <linux/profile.h>
|
||||
#include <linux/smp.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/irq_work.h>
|
||||
|
||||
#include <asm/sbi.h>
|
||||
#include <asm/tlbflush.h>
|
||||
#include <asm/cacheflush.h>
|
||||
#include <asm/cpu_ops.h>
|
||||
@@ -44,11 +45,10 @@ void __init smp_setup_processor_id(void)
|
||||
cpuid_to_hartid_map(0) = boot_cpu_hartid;
|
||||
}
|
||||
|
||||
/* A collection of single bit ipi messages. */
|
||||
static struct {
|
||||
unsigned long stats[IPI_MAX] ____cacheline_aligned;
|
||||
unsigned long bits ____cacheline_aligned;
|
||||
} ipi_data[NR_CPUS] __cacheline_aligned;
|
||||
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;
|
||||
|
||||
int riscv_hartid_to_cpuid(unsigned long hartid)
|
||||
{
|
||||
@@ -100,48 +100,14 @@ static inline void ipi_cpu_crash_stop(unsigned int cpu, struct pt_regs *regs)
|
||||
}
|
||||
#endif
|
||||
|
||||
static const struct riscv_ipi_ops *ipi_ops __ro_after_init;
|
||||
|
||||
void riscv_set_ipi_ops(const struct riscv_ipi_ops *ops)
|
||||
{
|
||||
ipi_ops = ops;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(riscv_set_ipi_ops);
|
||||
|
||||
void riscv_clear_ipi(void)
|
||||
{
|
||||
if (ipi_ops && ipi_ops->ipi_clear)
|
||||
ipi_ops->ipi_clear();
|
||||
|
||||
csr_clear(CSR_IP, IE_SIE);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(riscv_clear_ipi);
|
||||
|
||||
static void send_ipi_mask(const struct cpumask *mask, enum ipi_message_type op)
|
||||
{
|
||||
int cpu;
|
||||
|
||||
smp_mb__before_atomic();
|
||||
for_each_cpu(cpu, mask)
|
||||
set_bit(op, &ipi_data[cpu].bits);
|
||||
smp_mb__after_atomic();
|
||||
|
||||
if (ipi_ops && ipi_ops->ipi_inject)
|
||||
ipi_ops->ipi_inject(mask);
|
||||
else
|
||||
pr_warn("SMP: IPI inject method not available\n");
|
||||
__ipi_send_mask(ipi_desc[op], mask);
|
||||
}
|
||||
|
||||
static void send_ipi_single(int cpu, enum ipi_message_type op)
|
||||
{
|
||||
smp_mb__before_atomic();
|
||||
set_bit(op, &ipi_data[cpu].bits);
|
||||
smp_mb__after_atomic();
|
||||
|
||||
if (ipi_ops && ipi_ops->ipi_inject)
|
||||
ipi_ops->ipi_inject(cpumask_of(cpu));
|
||||
else
|
||||
pr_warn("SMP: IPI inject method not available\n");
|
||||
__ipi_send_mask(ipi_desc[op], cpumask_of(cpu));
|
||||
}
|
||||
|
||||
#ifdef CONFIG_IRQ_WORK
|
||||
@@ -151,59 +117,98 @@ void arch_irq_work_raise(void)
|
||||
}
|
||||
#endif
|
||||
|
||||
void handle_IPI(struct pt_regs *regs)
|
||||
static irqreturn_t handle_IPI(int irq, void *data)
|
||||
{
|
||||
unsigned int cpu = smp_processor_id();
|
||||
unsigned long *pending_ipis = &ipi_data[cpu].bits;
|
||||
unsigned long *stats = ipi_data[cpu].stats;
|
||||
|
||||
riscv_clear_ipi();
|
||||
|
||||
while (true) {
|
||||
unsigned long ops;
|
||||
|
||||
/* Order bit clearing and data access. */
|
||||
mb();
|
||||
|
||||
ops = xchg(pending_ipis, 0);
|
||||
if (ops == 0)
|
||||
return;
|
||||
|
||||
if (ops & (1 << IPI_RESCHEDULE)) {
|
||||
stats[IPI_RESCHEDULE]++;
|
||||
scheduler_ipi();
|
||||
}
|
||||
|
||||
if (ops & (1 << IPI_CALL_FUNC)) {
|
||||
stats[IPI_CALL_FUNC]++;
|
||||
generic_smp_call_function_interrupt();
|
||||
}
|
||||
|
||||
if (ops & (1 << IPI_CPU_STOP)) {
|
||||
stats[IPI_CPU_STOP]++;
|
||||
ipi_stop();
|
||||
}
|
||||
|
||||
if (ops & (1 << IPI_CPU_CRASH_STOP)) {
|
||||
ipi_cpu_crash_stop(cpu, get_irq_regs());
|
||||
}
|
||||
|
||||
if (ops & (1 << IPI_IRQ_WORK)) {
|
||||
stats[IPI_IRQ_WORK]++;
|
||||
irq_work_run();
|
||||
}
|
||||
int ipi = irq - ipi_virq_base;
|
||||
|
||||
switch (ipi) {
|
||||
case IPI_RESCHEDULE:
|
||||
scheduler_ipi();
|
||||
break;
|
||||
case IPI_CALL_FUNC:
|
||||
generic_smp_call_function_interrupt();
|
||||
break;
|
||||
case IPI_CPU_STOP:
|
||||
ipi_stop();
|
||||
break;
|
||||
case IPI_CPU_CRASH_STOP:
|
||||
ipi_cpu_crash_stop(smp_processor_id(), get_irq_regs());
|
||||
break;
|
||||
case IPI_IRQ_WORK:
|
||||
irq_work_run();
|
||||
break;
|
||||
#ifdef CONFIG_GENERIC_CLOCKEVENTS_BROADCAST
|
||||
if (ops & (1 << IPI_TIMER)) {
|
||||
stats[IPI_TIMER]++;
|
||||
tick_receive_broadcast();
|
||||
}
|
||||
case IPI_TIMER:
|
||||
tick_receive_broadcast();
|
||||
break;
|
||||
#endif
|
||||
BUG_ON((ops >> IPI_MAX) != 0);
|
||||
|
||||
/* Order data access and bit testing. */
|
||||
mb();
|
||||
default:
|
||||
pr_warn("CPU%d: unhandled IPI%d\n", smp_processor_id(), ipi);
|
||||
break;
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
void riscv_ipi_enable(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (WARN_ON_ONCE(!ipi_virq_base))
|
||||
return;
|
||||
|
||||
for (i = 0; i < nr_ipi; i++)
|
||||
enable_percpu_irq(ipi_virq_base + i, 0);
|
||||
}
|
||||
|
||||
void riscv_ipi_disable(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (WARN_ON_ONCE(!ipi_virq_base))
|
||||
return;
|
||||
|
||||
for (i = 0; i < nr_ipi; i++)
|
||||
disable_percpu_irq(ipi_virq_base + i);
|
||||
}
|
||||
|
||||
bool riscv_ipi_have_virq_range(void)
|
||||
{
|
||||
return (ipi_virq_base) ? true : false;
|
||||
}
|
||||
|
||||
DEFINE_STATIC_KEY_FALSE(riscv_ipi_for_rfence);
|
||||
EXPORT_SYMBOL_GPL(riscv_ipi_for_rfence);
|
||||
|
||||
void riscv_ipi_set_virq_range(int virq, int nr, bool use_for_rfence)
|
||||
{
|
||||
int i, err;
|
||||
|
||||
if (WARN_ON(ipi_virq_base))
|
||||
return;
|
||||
|
||||
WARN_ON(nr < IPI_MAX);
|
||||
nr_ipi = min(nr, IPI_MAX);
|
||||
ipi_virq_base = virq;
|
||||
|
||||
/* Request IPIs */
|
||||
for (i = 0; i < nr_ipi; i++) {
|
||||
err = request_percpu_irq(ipi_virq_base + i, handle_IPI,
|
||||
"IPI", &ipi_dummy_dev);
|
||||
WARN_ON(err);
|
||||
|
||||
ipi_desc[i] = irq_to_desc(ipi_virq_base + i);
|
||||
irq_set_status_flags(ipi_virq_base + i, IRQ_HIDDEN);
|
||||
}
|
||||
|
||||
/* Enabled IPIs for boot CPU immediately */
|
||||
riscv_ipi_enable();
|
||||
|
||||
/* Update RFENCE static key */
|
||||
if (use_for_rfence)
|
||||
static_branch_enable(&riscv_ipi_for_rfence);
|
||||
else
|
||||
static_branch_disable(&riscv_ipi_for_rfence);
|
||||
}
|
||||
|
||||
static const char * const ipi_names[] = {
|
||||
@@ -223,7 +228,7 @@ void show_ipi_stats(struct seq_file *p, int prec)
|
||||
seq_printf(p, "%*s%u:%s", prec - 1, "IPI", i,
|
||||
prec >= 4 ? " " : "");
|
||||
for_each_online_cpu(cpu)
|
||||
seq_printf(p, "%10lu ", ipi_data[cpu].stats[i]);
|
||||
seq_printf(p, "%10u ", irq_desc_kstat_cpu(ipi_desc[i], cpu));
|
||||
seq_printf(p, " %s\n", ipi_names[i]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,7 +30,6 @@
|
||||
#include <asm/numa.h>
|
||||
#include <asm/tlbflush.h>
|
||||
#include <asm/sections.h>
|
||||
#include <asm/sbi.h>
|
||||
#include <asm/smp.h>
|
||||
|
||||
#include "head.h"
|
||||
@@ -158,12 +157,12 @@ asmlinkage __visible void smp_callin(void)
|
||||
struct mm_struct *mm = &init_mm;
|
||||
unsigned int curr_cpuid = smp_processor_id();
|
||||
|
||||
riscv_clear_ipi();
|
||||
|
||||
/* All kernel threads share the same mm context. */
|
||||
mmgrab(mm);
|
||||
current->active_mm = mm;
|
||||
|
||||
riscv_ipi_enable();
|
||||
|
||||
store_cpu_topology(curr_cpuid);
|
||||
notify_cpu_starting(curr_cpuid);
|
||||
numa_add_cpu(curr_cpuid);
|
||||
|
||||
@@ -19,7 +19,7 @@ void flush_icache_all(void)
|
||||
{
|
||||
local_flush_icache_all();
|
||||
|
||||
if (IS_ENABLED(CONFIG_RISCV_SBI))
|
||||
if (IS_ENABLED(CONFIG_RISCV_SBI) && !riscv_use_ipi_for_rfence())
|
||||
sbi_remote_fence_i(NULL);
|
||||
else
|
||||
on_each_cpu(ipi_remote_fence_i, NULL, 1);
|
||||
@@ -67,7 +67,8 @@ void flush_icache_mm(struct mm_struct *mm, bool local)
|
||||
* with flush_icache_deferred().
|
||||
*/
|
||||
smp_mb();
|
||||
} else if (IS_ENABLED(CONFIG_RISCV_SBI)) {
|
||||
} else if (IS_ENABLED(CONFIG_RISCV_SBI) &&
|
||||
!riscv_use_ipi_for_rfence()) {
|
||||
sbi_remote_fence_i(&others);
|
||||
} else {
|
||||
on_each_cpu_mask(&others, ipi_remote_fence_i, NULL, 1);
|
||||
|
||||
@@ -23,14 +23,62 @@ static inline void local_flush_tlb_page_asid(unsigned long addr,
|
||||
: "memory");
|
||||
}
|
||||
|
||||
void flush_tlb_all(void)
|
||||
static inline void local_flush_tlb_range(unsigned long start,
|
||||
unsigned long size, unsigned long stride)
|
||||
{
|
||||
sbi_remote_sfence_vma(NULL, 0, -1);
|
||||
if (size <= stride)
|
||||
local_flush_tlb_page(start);
|
||||
else
|
||||
local_flush_tlb_all();
|
||||
}
|
||||
|
||||
static void __sbi_tlb_flush_range(struct mm_struct *mm, unsigned long start,
|
||||
unsigned long size, unsigned long stride)
|
||||
static inline void local_flush_tlb_range_asid(unsigned long start,
|
||||
unsigned long size, unsigned long stride, unsigned long asid)
|
||||
{
|
||||
if (size <= stride)
|
||||
local_flush_tlb_page_asid(start, asid);
|
||||
else
|
||||
local_flush_tlb_all_asid(asid);
|
||||
}
|
||||
|
||||
static void __ipi_flush_tlb_all(void *info)
|
||||
{
|
||||
local_flush_tlb_all();
|
||||
}
|
||||
|
||||
void flush_tlb_all(void)
|
||||
{
|
||||
if (riscv_use_ipi_for_rfence())
|
||||
on_each_cpu(__ipi_flush_tlb_all, NULL, 1);
|
||||
else
|
||||
sbi_remote_sfence_vma(NULL, 0, -1);
|
||||
}
|
||||
|
||||
struct flush_tlb_range_data {
|
||||
unsigned long asid;
|
||||
unsigned long start;
|
||||
unsigned long size;
|
||||
unsigned long stride;
|
||||
};
|
||||
|
||||
static void __ipi_flush_tlb_range_asid(void *info)
|
||||
{
|
||||
struct flush_tlb_range_data *d = info;
|
||||
|
||||
local_flush_tlb_range_asid(d->start, d->size, d->stride, d->asid);
|
||||
}
|
||||
|
||||
static void __ipi_flush_tlb_range(void *info)
|
||||
{
|
||||
struct flush_tlb_range_data *d = info;
|
||||
|
||||
local_flush_tlb_range(d->start, d->size, d->stride);
|
||||
}
|
||||
|
||||
static void __flush_tlb_range(struct mm_struct *mm, unsigned long start,
|
||||
unsigned long size, unsigned long stride)
|
||||
{
|
||||
struct flush_tlb_range_data ftd;
|
||||
struct cpumask *cmask = mm_cpumask(mm);
|
||||
unsigned int cpuid;
|
||||
bool broadcast;
|
||||
@@ -45,19 +93,34 @@ static void __sbi_tlb_flush_range(struct mm_struct *mm, unsigned long start,
|
||||
unsigned long asid = atomic_long_read(&mm->context.id) & asid_mask;
|
||||
|
||||
if (broadcast) {
|
||||
sbi_remote_sfence_vma_asid(cmask, start, size, asid);
|
||||
} else if (size <= stride) {
|
||||
local_flush_tlb_page_asid(start, asid);
|
||||
if (riscv_use_ipi_for_rfence()) {
|
||||
ftd.asid = asid;
|
||||
ftd.start = start;
|
||||
ftd.size = size;
|
||||
ftd.stride = stride;
|
||||
on_each_cpu_mask(cmask,
|
||||
__ipi_flush_tlb_range_asid,
|
||||
&ftd, 1);
|
||||
} else
|
||||
sbi_remote_sfence_vma_asid(cmask,
|
||||
start, size, asid);
|
||||
} else {
|
||||
local_flush_tlb_all_asid(asid);
|
||||
local_flush_tlb_range_asid(start, size, stride, asid);
|
||||
}
|
||||
} else {
|
||||
if (broadcast) {
|
||||
sbi_remote_sfence_vma(cmask, start, size);
|
||||
} else if (size <= stride) {
|
||||
local_flush_tlb_page(start);
|
||||
if (riscv_use_ipi_for_rfence()) {
|
||||
ftd.asid = 0;
|
||||
ftd.start = start;
|
||||
ftd.size = size;
|
||||
ftd.stride = stride;
|
||||
on_each_cpu_mask(cmask,
|
||||
__ipi_flush_tlb_range,
|
||||
&ftd, 1);
|
||||
} else
|
||||
sbi_remote_sfence_vma(cmask, start, size);
|
||||
} else {
|
||||
local_flush_tlb_all();
|
||||
local_flush_tlb_range(start, size, stride);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,23 +129,23 @@ static void __sbi_tlb_flush_range(struct mm_struct *mm, unsigned long start,
|
||||
|
||||
void flush_tlb_mm(struct mm_struct *mm)
|
||||
{
|
||||
__sbi_tlb_flush_range(mm, 0, -1, PAGE_SIZE);
|
||||
__flush_tlb_range(mm, 0, -1, PAGE_SIZE);
|
||||
}
|
||||
|
||||
void flush_tlb_page(struct vm_area_struct *vma, unsigned long addr)
|
||||
{
|
||||
__sbi_tlb_flush_range(vma->vm_mm, addr, PAGE_SIZE, PAGE_SIZE);
|
||||
__flush_tlb_range(vma->vm_mm, addr, PAGE_SIZE, PAGE_SIZE);
|
||||
}
|
||||
|
||||
void flush_tlb_range(struct vm_area_struct *vma, unsigned long start,
|
||||
unsigned long end)
|
||||
{
|
||||
__sbi_tlb_flush_range(vma->vm_mm, start, end - start, PAGE_SIZE);
|
||||
__flush_tlb_range(vma->vm_mm, start, end - start, PAGE_SIZE);
|
||||
}
|
||||
#ifdef CONFIG_TRANSPARENT_HUGEPAGE
|
||||
void flush_pmd_tlb_range(struct vm_area_struct *vma, unsigned long start,
|
||||
unsigned long end)
|
||||
{
|
||||
__sbi_tlb_flush_range(vma->vm_mm, start, end - start, PMD_SIZE);
|
||||
__flush_tlb_range(vma->vm_mm, start, end - start, PMD_SIZE);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -17,6 +17,9 @@
|
||||
#include <linux/sched_clock.h>
|
||||
#include <linux/io-64-nonatomic-lo-hi.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/irqchip/chained_irq.h>
|
||||
#include <linux/irqdomain.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/smp.h>
|
||||
#include <linux/timex.h>
|
||||
@@ -31,6 +34,7 @@
|
||||
|
||||
/* CLINT manages IPI and Timer for RISC-V M-mode */
|
||||
static u32 __iomem *clint_ipi_base;
|
||||
static unsigned int clint_ipi_irq;
|
||||
static u64 __iomem *clint_timer_cmp;
|
||||
static u64 __iomem *clint_timer_val;
|
||||
static unsigned long clint_timer_freq;
|
||||
@@ -41,12 +45,10 @@ u64 __iomem *clint_time_val;
|
||||
EXPORT_SYMBOL(clint_time_val);
|
||||
#endif
|
||||
|
||||
static void clint_send_ipi(const struct cpumask *target)
|
||||
#ifdef CONFIG_SMP
|
||||
static void clint_send_ipi(unsigned int cpu)
|
||||
{
|
||||
unsigned int cpu;
|
||||
|
||||
for_each_cpu(cpu, target)
|
||||
writel(1, clint_ipi_base + cpuid_to_hartid_map(cpu));
|
||||
writel(1, clint_ipi_base + cpuid_to_hartid_map(cpu));
|
||||
}
|
||||
|
||||
static void clint_clear_ipi(void)
|
||||
@@ -54,10 +56,18 @@ static void clint_clear_ipi(void)
|
||||
writel(0, clint_ipi_base + cpuid_to_hartid_map(smp_processor_id()));
|
||||
}
|
||||
|
||||
static struct riscv_ipi_ops clint_ipi_ops = {
|
||||
.ipi_inject = clint_send_ipi,
|
||||
.ipi_clear = clint_clear_ipi,
|
||||
};
|
||||
static void clint_ipi_interrupt(struct irq_desc *desc)
|
||||
{
|
||||
struct irq_chip *chip = irq_desc_get_chip(desc);
|
||||
|
||||
chained_irq_enter(chip, desc);
|
||||
|
||||
clint_clear_ipi();
|
||||
ipi_mux_process();
|
||||
|
||||
chained_irq_exit(chip, desc);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_64BIT
|
||||
#define clint_get_cycles() readq_relaxed(clint_timer_val)
|
||||
@@ -125,12 +135,19 @@ static int clint_timer_starting_cpu(unsigned int cpu)
|
||||
|
||||
enable_percpu_irq(clint_timer_irq,
|
||||
irq_get_trigger_type(clint_timer_irq));
|
||||
enable_percpu_irq(clint_ipi_irq,
|
||||
irq_get_trigger_type(clint_ipi_irq));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int clint_timer_dying_cpu(unsigned int cpu)
|
||||
{
|
||||
disable_percpu_irq(clint_timer_irq);
|
||||
/*
|
||||
* Don't disable IPI when CPU goes offline because
|
||||
* the masking/unmasking of virtual IPIs is done
|
||||
* via generic IPI-Mux
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -170,6 +187,12 @@ static int __init clint_timer_init_dt(struct device_node *np)
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* Find parent irq domain and map ipi irq */
|
||||
if (!clint_ipi_irq &&
|
||||
oirq.args[0] == RV_IRQ_SOFT &&
|
||||
irq_find_host(oirq.np))
|
||||
clint_ipi_irq = irq_of_parse_and_map(np, i);
|
||||
|
||||
/* Find parent irq domain and map timer irq */
|
||||
if (!clint_timer_irq &&
|
||||
oirq.args[0] == RV_IRQ_TIMER &&
|
||||
@@ -177,9 +200,9 @@ static int __init clint_timer_init_dt(struct device_node *np)
|
||||
clint_timer_irq = irq_of_parse_and_map(np, i);
|
||||
}
|
||||
|
||||
/* If CLINT timer irq not found then fail */
|
||||
if (!clint_timer_irq) {
|
||||
pr_err("%pOFP: timer irq not found\n", np);
|
||||
/* If CLINT ipi or timer irq not found then fail */
|
||||
if (!clint_ipi_irq || !clint_timer_irq) {
|
||||
pr_err("%pOFP: ipi/timer irq not found\n", np);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
@@ -219,6 +242,19 @@ static int __init clint_timer_init_dt(struct device_node *np)
|
||||
goto fail_iounmap;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
rc = ipi_mux_create(BITS_PER_BYTE, clint_send_ipi);
|
||||
if (rc <= 0) {
|
||||
pr_err("unable to create muxed IPIs\n");
|
||||
rc = (rc < 0) ? rc : -ENODEV;
|
||||
goto fail_free_irq;
|
||||
}
|
||||
|
||||
irq_set_chained_handler(clint_ipi_irq, clint_ipi_interrupt);
|
||||
riscv_ipi_set_virq_range(rc, BITS_PER_BYTE, true);
|
||||
clint_clear_ipi();
|
||||
#endif
|
||||
|
||||
rc = cpuhp_setup_state(CPUHP_AP_CLINT_TIMER_STARTING,
|
||||
"clockevents/clint/timer:starting",
|
||||
clint_timer_starting_cpu,
|
||||
@@ -228,13 +264,10 @@ static int __init clint_timer_init_dt(struct device_node *np)
|
||||
goto fail_free_irq;
|
||||
}
|
||||
|
||||
riscv_set_ipi_ops(&clint_ipi_ops);
|
||||
clint_clear_ipi();
|
||||
|
||||
return 0;
|
||||
|
||||
fail_free_irq:
|
||||
free_irq(clint_timer_irq, &clint_clock_event);
|
||||
free_percpu_irq(clint_timer_irq, &clint_clock_event);
|
||||
fail_iounmap:
|
||||
iounmap(base);
|
||||
return rc;
|
||||
|
||||
@@ -17,9 +17,13 @@ static enum arm_smccc_conduit smccc_conduit = SMCCC_CONDUIT_NONE;
|
||||
|
||||
bool __ro_after_init smccc_trng_available = false;
|
||||
u64 __ro_after_init smccc_has_sve_hint = false;
|
||||
s32 __ro_after_init smccc_soc_id_version = SMCCC_RET_NOT_SUPPORTED;
|
||||
s32 __ro_after_init smccc_soc_id_revision = SMCCC_RET_NOT_SUPPORTED;
|
||||
|
||||
void __init arm_smccc_version_init(u32 version, enum arm_smccc_conduit conduit)
|
||||
{
|
||||
struct arm_smccc_res res;
|
||||
|
||||
smccc_version = version;
|
||||
smccc_conduit = conduit;
|
||||
|
||||
@@ -27,6 +31,18 @@ void __init arm_smccc_version_init(u32 version, enum arm_smccc_conduit conduit)
|
||||
if (IS_ENABLED(CONFIG_ARM64_SVE) &&
|
||||
smccc_version >= ARM_SMCCC_VERSION_1_3)
|
||||
smccc_has_sve_hint = true;
|
||||
|
||||
if ((smccc_version >= ARM_SMCCC_VERSION_1_2) &&
|
||||
(smccc_conduit != SMCCC_CONDUIT_NONE)) {
|
||||
arm_smccc_1_1_invoke(ARM_SMCCC_ARCH_FEATURES_FUNC_ID,
|
||||
ARM_SMCCC_ARCH_SOC_ID, &res);
|
||||
if ((s32)res.a0 >= 0) {
|
||||
arm_smccc_1_1_invoke(ARM_SMCCC_ARCH_SOC_ID, 0, &res);
|
||||
smccc_soc_id_version = (s32)res.a0;
|
||||
arm_smccc_1_1_invoke(ARM_SMCCC_ARCH_SOC_ID, 1, &res);
|
||||
smccc_soc_id_revision = (s32)res.a0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum arm_smccc_conduit arm_smccc_1_1_get_conduit(void)
|
||||
@@ -44,6 +60,16 @@ u32 arm_smccc_get_version(void)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(arm_smccc_get_version);
|
||||
|
||||
s32 arm_smccc_get_soc_id_version(void)
|
||||
{
|
||||
return smccc_soc_id_version;
|
||||
}
|
||||
|
||||
s32 arm_smccc_get_soc_id_revision(void)
|
||||
{
|
||||
return smccc_soc_id_revision;
|
||||
}
|
||||
|
||||
static int __init smccc_devices_init(void)
|
||||
{
|
||||
struct platform_device *pdev;
|
||||
|
||||
@@ -42,41 +42,23 @@ static int __init smccc_soc_init(void)
|
||||
if (arm_smccc_get_version() < ARM_SMCCC_VERSION_1_2)
|
||||
return 0;
|
||||
|
||||
if (arm_smccc_1_1_get_conduit() == SMCCC_CONDUIT_NONE) {
|
||||
pr_err("%s: invalid SMCCC conduit\n", __func__);
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
arm_smccc_1_1_invoke(ARM_SMCCC_ARCH_FEATURES_FUNC_ID,
|
||||
ARM_SMCCC_ARCH_SOC_ID, &res);
|
||||
|
||||
if ((int)res.a0 == SMCCC_RET_NOT_SUPPORTED) {
|
||||
soc_id_version = arm_smccc_get_soc_id_version();
|
||||
if (soc_id_version == SMCCC_RET_NOT_SUPPORTED) {
|
||||
pr_info("ARCH_SOC_ID not implemented, skipping ....\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ((int)res.a0 < 0) {
|
||||
pr_info("ARCH_FEATURES(ARCH_SOC_ID) returned error: %lx\n",
|
||||
res.a0);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
arm_smccc_1_1_invoke(ARM_SMCCC_ARCH_SOC_ID, 0, &res);
|
||||
if ((int)res.a0 < 0) {
|
||||
if (soc_id_version < 0) {
|
||||
pr_err("ARCH_SOC_ID(0) returned error: %lx\n", res.a0);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
soc_id_version = res.a0;
|
||||
|
||||
arm_smccc_1_1_invoke(ARM_SMCCC_ARCH_SOC_ID, 1, &res);
|
||||
if ((int)res.a0 < 0) {
|
||||
soc_id_rev = arm_smccc_get_soc_id_revision();
|
||||
if (soc_id_rev < 0) {
|
||||
pr_err("ARCH_SOC_ID(1) returned error: %lx\n", res.a0);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
soc_id_rev = res.a0;
|
||||
|
||||
soc_dev_attr = kzalloc(sizeof(*soc_dev_attr), GFP_KERNEL);
|
||||
if (!soc_dev_attr)
|
||||
return -ENOMEM;
|
||||
|
||||
@@ -7,6 +7,7 @@ config IRQCHIP
|
||||
|
||||
config ARM_GIC
|
||||
bool
|
||||
depends on OF
|
||||
select IRQ_DOMAIN_HIERARCHY
|
||||
select GENERIC_IRQ_EFFECTIVE_AFF_MASK if SMP
|
||||
|
||||
@@ -35,6 +36,7 @@ config ARM_GIC_V3
|
||||
select IRQ_DOMAIN_HIERARCHY
|
||||
select PARTITION_PERCPU
|
||||
select GENERIC_IRQ_EFFECTIVE_AFF_MASK if SMP
|
||||
select HAVE_ARM_SMCCC_DISCOVERY
|
||||
|
||||
config ARM_GIC_V3_ITS
|
||||
bool
|
||||
@@ -535,6 +537,7 @@ config TI_PRUSS_INTC
|
||||
config RISCV_INTC
|
||||
bool
|
||||
depends on RISCV
|
||||
select IRQ_DOMAIN_HIERARCHY
|
||||
|
||||
config SIFIVE_PLIC
|
||||
bool
|
||||
|
||||
@@ -257,6 +257,9 @@ static int __init bcm6345_l1_init_one(struct device_node *dn,
|
||||
if (!cpu->map_base)
|
||||
return -ENOMEM;
|
||||
|
||||
if (!request_mem_region(res.start, sz, res.name))
|
||||
pr_err("failed to request intc memory");
|
||||
|
||||
for (i = 0; i < n_words; i++) {
|
||||
cpu->enable_cache[i] = 0;
|
||||
__raw_writel(0, cpu->map_base + reg_enable(intc, i));
|
||||
@@ -335,8 +338,7 @@ static int __init bcm6345_l1_of_init(struct device_node *dn,
|
||||
for_each_cpu(idx, &intc->cpumask) {
|
||||
struct bcm6345_l1_cpu *cpu = intc->cpus[idx];
|
||||
|
||||
pr_info(" CPU%u at MMIO 0x%p (irq = %d)\n", idx,
|
||||
cpu->map_base, cpu->parent_irq);
|
||||
pr_info(" CPU%u (irq = %d)\n", idx, cpu->parent_irq);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user