riscv: provide native clint access for M-mode
authorChristoph Hellwig <hch@lst.de>
Mon, 28 Oct 2019 12:10:38 +0000 (13:10 +0100)
committerPaul Walmsley <paul.walmsley@sifive.com>
Sun, 17 Nov 2019 23:17:39 +0000 (15:17 -0800)
RISC-V has the concept of a cpu level interrupt controller.  The
interface for it is split between a standardized part that is exposed
as bits in the mstatus/sstatus register and the mie/mip/sie/sip
CRS.  But the bit to actually trigger IPIs is not standardized and
just mentioned as implementable using MMIO.

Add support for IPIs using MMIO using the SiFive clint layout (which
is also shared by Ariane, Kendryte and the Qemu virt platform).
Additionally the MMIO block also supports the time value and timer
compare registers, so they are also set up using the same OF node.
Support for other layouts should also be relatively easy to add in the
future.

Signed-off-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Anup Patel <anup@brainfault.org>
[paul.walmsley@sifive.com: update include guard format; fix checkpatch
 issues; minor commit message cleanup]
Signed-off-by: Paul Walmsley <paul.walmsley@sifive.com>
arch/riscv/include/asm/clint.h [new file with mode: 0644]
arch/riscv/include/asm/sbi.h
arch/riscv/kernel/Makefile
arch/riscv/kernel/clint.c [new file with mode: 0644]
arch/riscv/kernel/setup.c
arch/riscv/kernel/smp.c
arch/riscv/kernel/smpboot.c

diff --git a/arch/riscv/include/asm/clint.h b/arch/riscv/include/asm/clint.h
new file mode 100644 (file)
index 0000000..6eaa2ee
--- /dev/null
@@ -0,0 +1,39 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _ASM_RISCV_CLINT_H
+#define _ASM_RISCV_CLINT_H 1
+
+#include <linux/io.h>
+#include <linux/smp.h>
+
+#ifdef CONFIG_RISCV_M_MODE
+extern u32 __iomem *clint_ipi_base;
+
+void clint_init_boot_cpu(void);
+
+static inline void clint_send_ipi_single(unsigned long hartid)
+{
+       writel(1, clint_ipi_base + hartid);
+}
+
+static inline void clint_send_ipi_mask(const struct cpumask *hartid_mask)
+{
+       int hartid;
+
+       for_each_cpu(hartid, hartid_mask)
+               clint_send_ipi_single(hartid);
+}
+
+static inline void clint_clear_ipi(unsigned long hartid)
+{
+       writel(0, clint_ipi_base + hartid);
+}
+#else /* CONFIG_RISCV_M_MODE */
+#define clint_init_boot_cpu()  do { } while (0)
+
+/* stubs to for code is only reachable under IS_ENABLED(CONFIG_RISCV_M_MODE): */
+void clint_send_ipi_single(unsigned long hartid);
+void clint_send_ipi_mask(const struct cpumask *hartid_mask);
+void clint_clear_ipi(unsigned long hartid);
+#endif /* CONFIG_RISCV_M_MODE */
+
+#endif /* _ASM_RISCV_CLINT_H */
index 8e14d4819d0fdc6ba3e1dace976b0290f2fd2f03..2570c1e683d34db50addcb042404cce1e72aca0e 100644 (file)
@@ -97,6 +97,8 @@ static inline void sbi_remote_sfence_vma_asid(const unsigned long *hart_mask,
 #else /* CONFIG_RISCV_SBI */
 /* stubs for code that is only reachable under IS_ENABLED(CONFIG_RISCV_SBI): */
 void sbi_set_timer(uint64_t stime_value);
+void sbi_clear_ipi(void);
+void sbi_send_ipi(const unsigned long *hart_mask);
 void sbi_remote_fence_i(const unsigned long *hart_mask);
 #endif /* CONFIG_RISCV_SBI */
 #endif /* _ASM_RISCV_SBI_H */
index d8c35fa93cc6555d1b2cbb42b759aedaed5ac63b..2dca51046899723b30e9ed8159d37713eeb5be9e 100644 (file)
@@ -29,6 +29,7 @@ obj-y += vdso.o
 obj-y  += cacheinfo.o
 obj-y  += vdso/
 
+obj-$(CONFIG_RISCV_M_MODE)     += clint.o
 obj-$(CONFIG_FPU)              += fpu.o
 obj-$(CONFIG_SMP)              += smpboot.o
 obj-$(CONFIG_SMP)              += smp.o
diff --git a/arch/riscv/kernel/clint.c b/arch/riscv/kernel/clint.c
new file mode 100644 (file)
index 0000000..3647980
--- /dev/null
@@ -0,0 +1,44 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2019 Christoph Hellwig.
+ */
+
+#include <linux/io.h>
+#include <linux/of_address.h>
+#include <linux/types.h>
+#include <asm/clint.h>
+#include <asm/csr.h>
+#include <asm/timex.h>
+#include <asm/smp.h>
+
+/*
+ * This is the layout used by the SiFive clint, which is also shared by the qemu
+ * virt platform, and the Kendryte KD210 at least.
+ */
+#define CLINT_IPI_OFF          0
+#define CLINT_TIME_CMP_OFF     0x4000
+#define CLINT_TIME_VAL_OFF     0xbff8
+
+u32 __iomem *clint_ipi_base;
+
+void clint_init_boot_cpu(void)
+{
+       struct device_node *np;
+       void __iomem *base;
+
+       np = of_find_compatible_node(NULL, NULL, "riscv,clint0");
+       if (!np) {
+               panic("clint not found");
+               return;
+       }
+
+       base = of_iomap(np, 0);
+       if (!base)
+               panic("could not map CLINT");
+
+       clint_ipi_base = base + CLINT_IPI_OFF;
+       riscv_time_cmp = base + CLINT_TIME_CMP_OFF;
+       riscv_time_val = base + CLINT_TIME_VAL_OFF;
+
+       clint_clear_ipi(boot_cpu_hartid);
+}
index 845ae0e121159719115cc818e096aa0bc621970b..365ff8420bfef756b7f158ba7dc4ccff6c26900e 100644 (file)
@@ -17,6 +17,7 @@
 #include <linux/sched/task.h>
 #include <linux/swiotlb.h>
 
+#include <asm/clint.h>
 #include <asm/setup.h>
 #include <asm/sections.h>
 #include <asm/pgtable.h>
@@ -67,6 +68,7 @@ void __init setup_arch(char **cmdline_p)
        setup_bootmem();
        paging_init();
        unflatten_device_tree();
+       clint_init_boot_cpu();
 
 #ifdef CONFIG_SWIOTLB
        swiotlb_init(1);
index c0fbc04e6810cb4e275e6b449d85e949ecc9b0b3..eb878abcaaf8176e6e3e7a6c0059360d60488582 100644 (file)
@@ -16,6 +16,7 @@
 #include <linux/seq_file.h>
 #include <linux/delay.h>
 
+#include <asm/clint.h>
 #include <asm/sbi.h>
 #include <asm/tlbflush.h>
 #include <asm/cacheflush.h>
@@ -92,7 +93,10 @@ static void send_ipi_mask(const struct cpumask *mask, enum ipi_message_type op)
        smp_mb__after_atomic();
 
        riscv_cpuid_to_hartid_mask(mask, &hartid_mask);
-       sbi_send_ipi(cpumask_bits(&hartid_mask));
+       if (IS_ENABLED(CONFIG_RISCV_SBI))
+               sbi_send_ipi(cpumask_bits(&hartid_mask));
+       else
+               clint_send_ipi_mask(&hartid_mask);
 }
 
 static void send_ipi_single(int cpu, enum ipi_message_type op)
@@ -103,12 +107,18 @@ static void send_ipi_single(int cpu, enum ipi_message_type op)
        set_bit(op, &ipi_data[cpu].bits);
        smp_mb__after_atomic();
 
-       sbi_send_ipi(cpumask_bits(cpumask_of(hartid)));
+       if (IS_ENABLED(CONFIG_RISCV_SBI))
+               sbi_send_ipi(cpumask_bits(cpumask_of(hartid)));
+       else
+               clint_send_ipi_single(hartid);
 }
 
 static inline void clear_ipi(void)
 {
-       csr_clear(CSR_IP, IE_SIE);
+       if (IS_ENABLED(CONFIG_RISCV_SBI))
+               csr_clear(CSR_IP, IE_SIE);
+       else
+               clint_clear_ipi(cpuid_to_hartid_map(smp_processor_id()));
 }
 
 void riscv_software_interrupt(void)
index 261f4087cc39e177fbc063b003dfafc57488cc18..8bc01f0ca73be2e772a4da0d1828bc179f8952c8 100644 (file)
@@ -24,6 +24,7 @@
 #include <linux/of.h>
 #include <linux/sched/task_stack.h>
 #include <linux/sched/mm.h>
+#include <asm/clint.h>
 #include <asm/irq.h>
 #include <asm/mmu_context.h>
 #include <asm/tlbflush.h>
@@ -137,6 +138,9 @@ asmlinkage __visible void __init smp_callin(void)
 {
        struct mm_struct *mm = &init_mm;
 
+       if (!IS_ENABLED(CONFIG_RISCV_SBI))
+               clint_clear_ipi(cpuid_to_hartid_map(smp_processor_id()));
+
        /* All kernel threads share the same mm context.  */
        mmgrab(mm);
        current->active_mm = mm;