[PATCH] RISC-V: Resurrect the MMIO timer implementation for M-mode systems
Damien Le Moal
Damien.LeMoal at wdc.com
Tue Sep 15 04:25:43 EDT 2020
On Mon, 2020-09-14 at 09:56 -0700, Palmer Dabbelt wrote:
> The K210 doesn't implement rdtime in M-mode, and since that's where Linux runs
> in the NOMMU systems that means we can't use rdtime. The K210 is the only
> system that anyone is currently running NOMMU or M-mode on, so here we're just
> inlining the timer read directly.
>
> Signed-off-by: Palmer Dabbelt <palmerdabbelt at google.com>
> ---
> I don't actually have a K210 so I haven't tested this. If nobody else has the
> time to I'll put together a QEMU that doesn't support rdtime in M-mode, but
> I've yet to mess around with the !MMU stuff so that might take a little while.
> This certainly doesn't seem worse than what's there right now, though, as
> rdtime isn't valid in M-mode on the K210 (our only M-mode platform).
> ---
> arch/riscv/include/asm/clint.h | 26 ++++++++++++++++++++++++++
> arch/riscv/include/asm/timex.h | 27 +++++++++++++++++++++++++++
> drivers/clocksource/timer-clint.c | 17 +++++++++++++++++
> 3 files changed, 70 insertions(+)
> create mode 100644 arch/riscv/include/asm/clint.h
>
> diff --git a/arch/riscv/include/asm/clint.h b/arch/riscv/include/asm/clint.h
> new file mode 100644
> index 000000000000..0789fd37b40a
> --- /dev/null
> +++ b/arch/riscv/include/asm/clint.h
> @@ -0,0 +1,26 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +/*
> + * Copyright (C) 2020 Google, Inc
> + */
> +
> +#ifndef _ASM_RISCV_CLINT_H
> +#define _ASM_RISCV_CLINT_H
> +
> +#include <linux/types.h>
> +#include <asm/mmio.h>
> +
> +#ifdef CONFIG_RISCV_M_MODE
> +/*
> + * This lives in the CLINT driver, but is accessed directly by timex.h to avoid
> + * any overhead when accessing the MMIO timer.
> + *
> + * The ISA defines mtime as a 64-bit memory-mapped register that increments at
> + * a constant frequency, but it doesn't define some other constraints we depend
> + * on (most notably ordering constraints, but also some simpler stuff like the
> + * memory layout). Thus, this is called "clint_time_val" instead of something
> + * like "riscv_mtime", to signify that these non-ISA assumptions must hold.
> + */
> +extern u64 __iomem *clint_time_val;
> +#endif
> +
> +#endif
> diff --git a/arch/riscv/include/asm/timex.h b/arch/riscv/include/asm/timex.h
> index a3fb85d505d4..7f659dda0032 100644
> --- a/arch/riscv/include/asm/timex.h
> +++ b/arch/riscv/include/asm/timex.h
> @@ -10,6 +10,31 @@
>
> typedef unsigned long cycles_t;
>
> +#ifdef CONFIG_RISCV_M_MODE
> +
> +#include <asm/clint.h>
> +
> +#ifdef CONFIG_64BIT
> +static inline cycles_t get_cycles(void)
> +{
> + return readq_relaxed(clint_time_val);
> +}
> +#else /* !CONFIG_64BIT */
> +static inline u32 get_cycles(void)
> +{
> + return readl_relaxed(((u32 *)clint_time_val));
> +}
> +#define get_cycles get_cycles
> +
> +static inline u32 get_cycles_hi(void)
> +{
> + return readl_relaxed(((u32 *)clint_time_val) + 1);
> +}
> +#define get_cycles_hi get_cycles_hi
> +#endif /* CONFIG_64BIT */
> +
> +#else /* CONFIG_RISCV_M_MODE */
> +
> static inline cycles_t get_cycles(void)
> {
> return csr_read(CSR_TIME);
> @@ -41,6 +66,8 @@ static inline u64 get_cycles64(void)
> }
> #endif /* CONFIG_64BIT */
>
> +#endif /* !CONFIG_RISCV_M_MODE */
> +
> #define ARCH_HAS_READ_CURRENT_TIMER
> static inline int read_current_timer(unsigned long *timer_val)
> {
> diff --git a/drivers/clocksource/timer-clint.c b/drivers/clocksource/timer-clint.c
> index 8eeafa82c03d..d17367dee02c 100644
> --- a/drivers/clocksource/timer-clint.c
> +++ b/drivers/clocksource/timer-clint.c
> @@ -19,6 +19,11 @@
> #include <linux/interrupt.h>
> #include <linux/of_irq.h>
> #include <linux/smp.h>
> +#include <linux/timex.h>
> +
> +#ifndef CONFIG_RISCV_M_MODE
> +#include <asm/clint.h>
> +#endif
>
> #define CLINT_IPI_OFF 0
> #define CLINT_TIMER_CMP_OFF 0x4000
> @@ -31,6 +36,10 @@ static u64 __iomem *clint_timer_val;
> static unsigned long clint_timer_freq;
> static unsigned int clint_timer_irq;
>
> +#ifdef CONFIG_RISCV_M_MODE
> +u64 __iomem *clint_time_val;
> +#endif
> +
> static void clint_send_ipi(const struct cpumask *target)
> {
> unsigned int cpu;
> @@ -184,6 +193,14 @@ static int __init clint_timer_init_dt(struct device_node *np)
> clint_timer_val = base + CLINT_TIMER_VAL_OFF;
> clint_timer_freq = riscv_timebase;
>
> +#ifdef CONFIG_RISCV_M_MODE
> + /*
> + * Yes, that's an odd naming scheme. time_val is public, but hopefully
> + * will die in favor of something cleaner.
> + */
> + clint_time_val = clint_timer_val;
> +#endif
> +
> pr_info("%pOFP: timer running at %ld Hz\n", np, clint_timer_freq);
>
> rc = clocksource_register_hz(&clint_clocksource, clint_timer_freq);
I tried this patch on the Kendryte, on top of for-next and on top of
fixes (not sure which one is your target). First of all, with for-next,
I get this compilation warning:
arch/riscv/mm/init.c:44:28: warning: ‘pt_ops’ defined but not used [-
Wunused-variable]
44 | static struct pt_alloc_ops pt_ops;
Now for the tests:
(1) With for-next, no patch applied I get a crash. Expected I guess...
[ 0.000000] Linux version 5.9.0-rc2-00022-g3a822d8ea3b3 (
damien at localhost.localdomain) (riscv64-linux-gcc.br_real (Buildroot
2020.08-368-g9148de6052-dirty) 9.3.0, GNU ld (GNU Binutils) 2.32) #93
SMP Tue Sep 15 17:13:25 JST 2020
[ 0.000000] earlycon: sifive0 at MMIO 0x0000000038000000 (options
'')
[ 0.000000] printk: bootconsole [sifive0] enabled
[ 0.000000] Zone ranges:
[ 0.000000] DMA32 [mem 0x0000000080000000-0x00000000807fffff]
[ 0.000000] Normal empty
[ 0.000000] Movable zone start for each node
[ 0.000000] Early memory node ranges
[ 0.000000] node 0: [mem 0x0000000080000000-0x00000000807fffff]
[ 0.000000] Initmem setup node 0 [mem 0x0000000080000000-
0x00000000807fffff]
[ 0.000000] riscv: ISA extensions acdfim
[ 0.000000] riscv: ELF capabilities acdfim
[ 0.000000] percpu: max_distance=0x18000 too large for vmalloc space
0x0
[ 0.000000] percpu: Embedded 12 pages/cpu s18400 r0 d30752 u49152
[ 0.000000] Built 1 zonelists, mobility grouping off. Total pages:
2020
[ 0.000000] Kernel command line: earlycon console=ttySIF0
[ 0.000000] Dentry cache hash table entries: 1024 (order: 1, 8192
bytes, linear)
[ 0.000000] Inode-cache hash table entries: 512 (order: 0, 4096
bytes, linear)
[ 0.000000] Sorting __ex_table...
[ 0.000000] mem auto-init: stack:off, heap alloc:off, heap free:off
[ 0.000000] Memory: 6192K/8192K available (1287K kernel code, 117K
rwdata, 178K rodata, 301K init, 97K bss, 2000K reserved, 0K cma-
reserved)
[ 0.000000] rcu: Hierarchical RCU implementation.
[ 0.000000] rcu: RCU calculated value of scheduler-enlistment delay
is 25 jiffies.
[ 0.000000] NR_IRQS: 64, nr_irqs: 64, preallocated irqs: 0
[ 0.000000] riscv-intc: 64 local interrupts mapped
[ 0.000000] plic: interrupt-controller at c000000: mapped 65 interrupts
with 2 handlers for 4 contexts.
[ 0.000000] Oops - illegal instruction [#1]
[ 0.000000] CPU: 0 PID: 0 Comm: swapper/0 Not tainted 5.9.0-rc2-
00022-g3a822d8ea3b3 #93
[ 0.000000] epc: 000000008000d5c8 ra : 000000008000d4fa sp :
0000000080183f40
[ 0.000000] gp : 000000008019d350 tp : 0000000080187a00 t0 :
0000000000000019
[ 0.000000] t1 : 0000000000000018 t2 : 0000000000000001 s0 :
0000000080183f80
[ 0.000000] s1 : 0000000000000200 a0 : 0000000080196070 a1 :
0000000000000000
[ 0.000000] a2 : ffffffff80183f50 a3 : 0000000000000000 a4 :
0000000000000000
[ 0.000000] a5 : 0000000080168f70 a6 : 0000000000000000 a7 :
00000000000001e4
[ 0.000000] s2 : 000000008019d0a8 s3 : 000000008019d068 s4 :
00000000807e0b00
[ 0.000000] s5 : 000000008019d038 s6 : 000000008019d030 s7 :
00000000800123a8
[ 0.000000] s8 : 0000000000000000 s9 : 0000000000000000 s10:
0000000000000000
[ 0.000000] s11: 0000000000000000 t3 : 00000000801b42a8 t4 :
0000000000000068
[ 0.000000] t5 : 000000000000004c t6 : 0000000000000033
[ 0.000000] status: 0000000000001800 badaddr: 00000000001b727c
cause: 0000000000000002
[ 0.000000] random: get_random_bytes called from 0x0000000080051df8
with crng_init=0
[ 0.000000] ---[ end trace 0000000000000000 ]---
[ 0.000000] Kernel panic - not syncing: Fatal exception
[ 0.000000] ---[ end Kernel panic - not syncing: Fatal exception ]
---
(2) With for-next, patch applied, I get a hang:
[ 0.000000] Linux version 5.9.0-rc2-00023-gacd1158869ad (
damien at localhost.localdomain) (riscv64-linux-gcc.br_real (Buildroot
2020.08-368-g9148de6052-dirty) 9.3.0, GNU ld (GNU Binutils) 2.32) #94
SMP Tue Sep 15 17:15:59 JST 2020
[ 0.000000] earlycon: sifive0 at MMIO 0x0000000038000000 (options
'')
[ 0.000000] printk: bootconsole [sifive0] enabled
[ 0.000000] Zone ranges:
[ 0.000000] DMA32 [mem 0x0000000080000000-0x00000000807fffff]
[ 0.000000] Normal empty
[ 0.000000] Movable zone start for each node
[ 0.000000] Early memory node ranges
[ 0.000000] node 0: [mem 0x0000000080000000-0x00000000807fffff]
[ 0.000000] Initmem setup node 0 [mem 0x0000000080000000-
0x00000000807fffff]
[ 0.000000] riscv: ISA extensions acdfim
[ 0.000000] riscv: ELF capabilities acdfim
[ 0.000000] percpu: max_distance=0x18000 too large for vmalloc space
0x0
[ 0.000000] percpu: Embedded 12 pages/cpu s18400 r0 d30752 u49152
[ 0.000000] Built 1 zonelists, mobility grouping off. Total pages:
2020
[ 0.000000] Kernel command line: earlycon console=ttySIF0
[ 0.000000] Dentry cache hash table entries: 1024 (order: 1, 8192
bytes, linear)
[ 0.000000] Inode-cache hash table entries: 512 (order: 0, 4096
bytes, linear)
[ 0.000000] Sorting __ex_table...
[ 0.000000] mem auto-init: stack:off, heap alloc:off, heap free:off
[ 0.000000] Memory: 6176K/8192K available (1291K kernel code, 128K
rwdata, 178K rodata, 305K init, 97K bss, 2016K reserved, 0K cma-
reserved)
[ 0.000000] rcu: Hierarchical RCU implementation.
[ 0.000000] rcu: RCU calculated value of scheduler-enlistment delay
is 25 jiffies.
[ 0.000000] NR_IRQS: 64, nr_irqs: 64, preallocated irqs: 0
[ 0.000000] riscv-intc: 64 local interrupts mapped
[ 0.000000] plic: interrupt-controller at c000000: mapped 65 interrupts
with 2 handlers for 4 contexts.
[ 0.000000] random: get_random_bytes called from 0x00000000800019b4
with crng_init=0
[ 0.000000] clint: interrupt-controller at 2000000: timer irq not found
[ 0.000000] Failed to initialize '/soc/interrupt-controller at 2000000'
: -19
[ 0.000000] timer_probe: no matching timers found
[ 0.000000] Console: colour dummy device 80x25
[ 0.000000] sched_clock: 64 bits at 250 Hz, resolution 4000000ns,
wraps every 9007199254000000ns
[ 0.000000] Calibrating delay loop (skipped), value calculated using
timer frequency.. 15.60 BogoMIPS (lpj=31200)
[ 0.000000] pid_max: default: 4096 minimum: 301
[ 0.000000] Mount-cache hash table entries: 512 (order: 0, 4096
bytes, linear)
[ 0.000000] Mountpoint-cache hash table entries: 512 (order: 0, 4096
bytes, linear)
[ 0.000000] rcu: Hierarchical SRCU implementation.
[ 0.000000] smp: Bringing up secondary CPUs ...
[ 0.000000] SMP: IPI inject method not available
In both cases, the timer does not seem to work at all (see the message
timestamps).
Exact same results with your fixes branch too: hang with the patch and
illegal instruction without it.
Something is broken.
--
Damien Le Moal
Western Digital Research
More information about the linux-riscv
mailing list