[PATCH v3 19/21] clocksource: add driver for RISC-V and CLINT timers
Ahmad Fatoum
a.fatoum at pengutronix.de
Sun Mar 21 15:13:42 GMT 2021
CLINT is selected by Linux on nommu RISC-V machines, while the RISC-V
timer with SBI is selected on MMU enabled ones.
Both are also available on the Qemu Virt machine, but only SBI
is available on TinyEmu. As we'll add Virt support in a follow-up
commit, import both drivers now. Erizo could in theory make use
of the RISC-V timer, but even a 2GHz timer base is too slow
for it to be accurate.
Signed-off-by: Ahmad Fatoum <a.fatoum at pengutronix.de>
---
arch/riscv/Kconfig | 12 ++
arch/riscv/Makefile | 1 +
arch/riscv/cpu/Makefile | 3 +
arch/riscv/cpu/core.c | 50 +++++++
arch/riscv/cpu/time.c | 38 ++++++
arch/riscv/include/asm/csr.h | 211 ++++++++++++++++++++++++++++++
arch/riscv/include/asm/sbi.h | 153 ++++++++++++++++++++++
arch/riscv/include/asm/timer.h | 9 ++
arch/riscv/lib/dtb.c | 4 +
drivers/clocksource/Kconfig | 15 +++
drivers/clocksource/Makefile | 2 +
drivers/clocksource/timer-clint.c | 93 +++++++++++++
drivers/clocksource/timer-riscv.c | 50 +++++++
13 files changed, 641 insertions(+)
create mode 100644 arch/riscv/cpu/Makefile
create mode 100644 arch/riscv/cpu/core.c
create mode 100644 arch/riscv/cpu/time.c
create mode 100644 arch/riscv/include/asm/csr.h
create mode 100644 arch/riscv/include/asm/sbi.h
create mode 100644 arch/riscv/include/asm/timer.h
create mode 100644 drivers/clocksource/timer-clint.c
create mode 100644 drivers/clocksource/timer-riscv.c
diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig
index e630ad4ceb98..50c4d145cf54 100644
--- a/arch/riscv/Kconfig
+++ b/arch/riscv/Kconfig
@@ -13,6 +13,7 @@ config RISCV
select HAVE_IMAGE_COMPRESSION
select HAS_ARCH_SJLJ
select HAS_KALLSYMS
+ select RISCV_TIMER if RISCV_SBI
config ARCH_TEXT_BASE
hex
@@ -30,6 +31,7 @@ config MACH_ERIZO
select HAS_DEBUG_LL
select HAS_NMON
select USE_COMPRESSED_DTB
+ select RISCV_M_MODE
endchoice
@@ -113,4 +115,14 @@ config NMON_HELP
Say yes here to get the nmon commands message on
every nmon start.
+# set if we run in machine mode, cleared if we run in supervisor mode
+config RISCV_M_MODE
+ bool
+
+# set if we are running in S-mode and can use SBI calls
+config RISCV_SBI
+ bool
+ depends on !RISCV_M_MODE
+ default y
+
endmenu
diff --git a/arch/riscv/Makefile b/arch/riscv/Makefile
index c49c1b09ec8e..c0dadead42ad 100644
--- a/arch/riscv/Makefile
+++ b/arch/riscv/Makefile
@@ -47,6 +47,7 @@ endif
common-y += $(MACH)
common-y += arch/riscv/boards/
+common-y += arch/riscv/cpu/
common-y += arch/riscv/lib/
common-y += arch/riscv/boot/
diff --git a/arch/riscv/cpu/Makefile b/arch/riscv/cpu/Makefile
new file mode 100644
index 000000000000..f1312be699a1
--- /dev/null
+++ b/arch/riscv/cpu/Makefile
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0
+
+obj-y += core.o time.o
diff --git a/arch/riscv/cpu/core.c b/arch/riscv/cpu/core.c
new file mode 100644
index 000000000000..bdcd500ed748
--- /dev/null
+++ b/arch/riscv/cpu/core.c
@@ -0,0 +1,50 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2012 Regents of the University of California
+ * Copyright (C) 2017 SiFive
+ *
+ * All RISC-V systems have a timer attached to every hart. These timers can
+ * either be read from the "time" and "timeh" CSRs, and can use the SBI to
+ * setup events, or directly accessed using MMIO registers.
+ */
+#include <common.h>
+#include <init.h>
+#include <clock.h>
+#include <errno.h>
+#include <of.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <io.h>
+
+static struct device_d timer_dev;
+
+static int riscv_probe(struct device_d *parent)
+{
+ int ret;
+
+ /* Each hart has a timer, but we only need one */
+ if (IS_ENABLED(CONFIG_RISCV_TIMER) && !timer_dev.parent) {
+ timer_dev.id = DEVICE_ID_SINGLE;
+ timer_dev.device_node = parent->device_node;
+ timer_dev.parent = parent;
+ dev_set_name(&timer_dev, "riscv-timer");
+
+ ret = platform_device_register(&timer_dev);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static struct of_device_id riscv_dt_ids[] = {
+ { .compatible = "riscv", },
+ { /* sentinel */ },
+};
+
+static struct driver_d riscv_driver = {
+ .name = "riscv",
+ .probe = riscv_probe,
+ .of_compatible = riscv_dt_ids,
+};
+postcore_platform_driver(riscv_driver);
diff --git a/arch/riscv/cpu/time.c b/arch/riscv/cpu/time.c
new file mode 100644
index 000000000000..39bb6a5112f7
--- /dev/null
+++ b/arch/riscv/cpu/time.c
@@ -0,0 +1,38 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2012 Regents of the University of California
+ * Copyright (C) 2017 SiFive
+ *
+ * All RISC-V systems have a timer attached to every hart. These timers can
+ * either be read from the "time" and "timeh" CSRs, and can use the SBI to
+ * setup events, or directly accessed using MMIO registers.
+ */
+#include <common.h>
+#include <init.h>
+#include <clock.h>
+#include <errno.h>
+#include <of.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <io.h>
+#include <asm/timer.h>
+
+unsigned long riscv_timebase;
+
+int timer_init(void)
+{
+ struct device_node *cpu;
+ u32 prop;
+
+ cpu = of_find_node_by_path("/cpus");
+ if (!cpu || of_property_read_u32(cpu, "timebase-frequency", &prop)) {
+ pr_err("RISC-V system with no 'timebase-frequency' in DTS\n");
+ return -ENOENT;
+ }
+
+ riscv_timebase = prop;
+
+ of_platform_populate(cpu, NULL, NULL);
+
+ return 0;
+}
diff --git a/arch/riscv/include/asm/csr.h b/arch/riscv/include/asm/csr.h
new file mode 100644
index 000000000000..1a15089cae95
--- /dev/null
+++ b/arch/riscv/include/asm/csr.h
@@ -0,0 +1,211 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2015 Regents of the University of California
+ *
+ * Taken from Linux arch/riscv/include/asm/csr.h
+ */
+
+#ifndef _ASM_RISCV_CSR_H
+#define _ASM_RISCV_CSR_H
+
+#include <asm/asm.h>
+#include <linux/const.h>
+
+/* Status register flags */
+#define SR_SIE _AC(0x00000002, UL) /* Supervisor Interrupt Enable */
+#define SR_SPIE _AC(0x00000020, UL) /* Previous Supervisor IE */
+#define SR_SPP _AC(0x00000100, UL) /* Previously Supervisor */
+#ifdef CONFIG_RISCV_PRIV_1_9
+#define SR_PUM _AC(0x00040000, UL) /* Protect User Memory Access */
+#else
+#define SR_SUM _AC(0x00040000, UL) /* Supervisor User Memory Access */
+#endif
+
+#define SR_FS _AC(0x00006000, UL) /* Floating-point Status */
+#define SR_FS_OFF _AC(0x00000000, UL)
+#define SR_FS_INITIAL _AC(0x00002000, UL)
+#define SR_FS_CLEAN _AC(0x00004000, UL)
+#define SR_FS_DIRTY _AC(0x00006000, UL)
+
+#define SR_XS _AC(0x00018000, UL) /* Extension Status */
+#define SR_XS_OFF _AC(0x00000000, UL)
+#define SR_XS_INITIAL _AC(0x00008000, UL)
+#define SR_XS_CLEAN _AC(0x00010000, UL)
+#define SR_XS_DIRTY _AC(0x00018000, UL)
+
+#ifdef CONFIG_RISCV_PRIV_1_9
+#define SR_VM _AC(0x1F000000, UL) /* Virtualization Management */
+#define SR_VM_MODE_BARE _AC(0x00000000, UL) /* No translation or protection */
+#define SR_VM_MODE_BB _AC(0x01000000, UL) /* Single base-and-bound */
+/* Separate instruction and data base-and-bound */
+#define SR_VM_MODE_BBID _AC(0x02000000, UL)
+#ifndef CONFIG_64BIT
+#define SR_VM_MODE_32 _AC(0x08000000, UL)
+#define SR_VM_MODE SR_VM_MODE_32
+#else
+#define SR_VM_MODE_39 _AC(0x09000000, UL)
+#define SR_VM_MODE_48 _AC(0x0A000000, UL)
+#define SR_VM_MODE SR_VM_MODE_39
+#endif
+#endif
+
+#ifndef CONFIG_64BIT
+#define SR_SD _AC(0x80000000, UL) /* FS/XS dirty */
+#else
+#define SR_SD _AC(0x8000000000000000, UL) /* FS/XS dirty */
+#endif
+
+/* SATP flags */
+#ifndef CONFIG_RISCV_PRIV_1_9
+#ifndef CONFIG_64BIT
+#define SATP_PPN _AC(0x003FFFFF, UL)
+#define SATP_MODE_32 _AC(0x80000000, UL)
+#define SATP_MODE SATP_MODE_32
+#else
+#define SATP_PPN _AC(0x00000FFFFFFFFFFF, UL)
+#define SATP_MODE_39 _AC(0x8000000000000000, UL)
+#define SATP_MODE SATP_MODE_39
+#endif
+#endif
+
+/* SCAUSE */
+#define SCAUSE_IRQ_FLAG (_AC(1, UL) << (__riscv_xlen - 1))
+
+#define IRQ_U_SOFT 0
+#define IRQ_S_SOFT 1
+#define IRQ_M_SOFT 3
+#define IRQ_U_TIMER 4
+#define IRQ_S_TIMER 5
+#define IRQ_M_TIMER 7
+#define IRQ_U_EXT 8
+#define IRQ_S_EXT 9
+#define IRQ_M_EXT 11
+
+#define EXC_INST_MISALIGNED 0
+#define EXC_INST_ACCESS 1
+#define EXC_BREAKPOINT 3
+#define EXC_LOAD_ACCESS 5
+#define EXC_STORE_ACCESS 7
+#define EXC_SYSCALL 8
+#define EXC_INST_PAGE_FAULT 12
+#define EXC_LOAD_PAGE_FAULT 13
+#define EXC_STORE_PAGE_FAULT 15
+
+/* SIE (Interrupt Enable) and SIP (Interrupt Pending) flags */
+#define MIE_MSIE (_AC(0x1, UL) << IRQ_M_SOFT)
+#define SIE_SSIE (_AC(0x1, UL) << IRQ_S_SOFT)
+#define SIE_STIE (_AC(0x1, UL) << IRQ_S_TIMER)
+#define SIE_SEIE (_AC(0x1, UL) << IRQ_S_EXT)
+
+#define CSR_FCSR 0x003
+#define CSR_CYCLE 0xc00
+#define CSR_TIME 0xc01
+#define CSR_INSTRET 0xc02
+#define CSR_SSTATUS 0x100
+#define CSR_SIE 0x104
+#define CSR_STVEC 0x105
+#define CSR_SCOUNTEREN 0x106
+#define CSR_SSCRATCH 0x140
+#define CSR_SEPC 0x141
+#define CSR_SCAUSE 0x142
+#define CSR_STVAL 0x143
+#define CSR_SIP 0x144
+#ifdef CONFIG_RISCV_PRIV_1_9
+#define CSR_SPTBR 0x180
+#else
+#define CSR_SATP 0x180
+#endif
+#define CSR_MSTATUS 0x300
+#define CSR_MISA 0x301
+#define CSR_MIE 0x304
+#define CSR_MTVEC 0x305
+#ifdef CONFIG_RISCV_PRIV_1_9
+#define CSR_MUCOUNTEREN 0x320
+#define CSR_MSCOUNTEREN 0x321
+#define CSR_MHCOUNTEREN 0x322
+#else
+#define CSR_MCOUNTEREN 0x306
+#endif
+#define CSR_MSCRATCH 0x340
+#define CSR_MEPC 0x341
+#define CSR_MCAUSE 0x342
+#define CSR_MTVAL 0x343
+#define CSR_MIP 0x344
+#ifdef CONFIG_RISCV_PRIV_1_9
+#define CSR_MBASE 0x380
+#define CSR_MBOUND 0x381
+#define CSR_MIBASE 0x382
+#define CSR_MIBOUND 0x383
+#define CSR_MDBASE 0x384
+#define CSR_MDBOUND 0x385
+#endif
+#define CSR_CYCLEH 0xc80
+#define CSR_TIMEH 0xc81
+#define CSR_INSTRETH 0xc82
+#define CSR_MHARTID 0xf14
+
+#ifndef __ASSEMBLY__
+
+#define csr_swap(csr, val) \
+({ \
+ unsigned long __v = (unsigned long)(val); \
+ __asm__ __volatile__ ("csrrw %0, " __ASM_STR(csr) ", %1"\
+ : "=r" (__v) : "rK" (__v) \
+ : "memory"); \
+ __v; \
+})
+
+#define csr_read(csr) \
+({ \
+ register unsigned long __v; \
+ __asm__ __volatile__ ("csrr %0, " __ASM_STR(csr) \
+ : "=r" (__v) : \
+ : "memory"); \
+ __v; \
+})
+
+#define csr_write(csr, val) \
+({ \
+ unsigned long __v = (unsigned long)(val); \
+ __asm__ __volatile__ ("csrw " __ASM_STR(csr) ", %0" \
+ : : "rK" (__v) \
+ : "memory"); \
+})
+
+#define csr_read_set(csr, val) \
+({ \
+ unsigned long __v = (unsigned long)(val); \
+ __asm__ __volatile__ ("csrrs %0, " __ASM_STR(csr) ", %1"\
+ : "=r" (__v) : "rK" (__v) \
+ : "memory"); \
+ __v; \
+})
+
+#define csr_set(csr, val) \
+({ \
+ unsigned long __v = (unsigned long)(val); \
+ __asm__ __volatile__ ("csrs " __ASM_STR(csr) ", %0" \
+ : : "rK" (__v) \
+ : "memory"); \
+})
+
+#define csr_read_clear(csr, val) \
+({ \
+ unsigned long __v = (unsigned long)(val); \
+ __asm__ __volatile__ ("csrrc %0, " __ASM_STR(csr) ", %1"\
+ : "=r" (__v) : "rK" (__v) \
+ : "memory"); \
+ __v; \
+})
+
+#define csr_clear(csr, val) \
+({ \
+ unsigned long __v = (unsigned long)(val); \
+ __asm__ __volatile__ ("csrc " __ASM_STR(csr) ", %0" \
+ : : "rK" (__v) \
+ : "memory"); \
+})
+
+#endif /* __ASSEMBLY__ */
+
+#endif /* _ASM_RISCV_CSR_H */
diff --git a/arch/riscv/include/asm/sbi.h b/arch/riscv/include/asm/sbi.h
new file mode 100644
index 000000000000..99895d9c3bdd
--- /dev/null
+++ b/arch/riscv/include/asm/sbi.h
@@ -0,0 +1,153 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2015 Regents of the University of California
+ * Copyright (c) 2020 Western Digital Corporation or its affiliates.
+ */
+
+#ifndef _ASM_RISCV_SBI_H
+#define _ASM_RISCV_SBI_H
+
+#include <linux/types.h>
+
+#ifdef CONFIG_RISCV_SBI
+enum sbi_ext_id {
+#ifdef CONFIG_RISCV_SBI_V01
+ SBI_EXT_0_1_SET_TIMER = 0x0,
+ SBI_EXT_0_1_CONSOLE_PUTCHAR = 0x1,
+ SBI_EXT_0_1_CONSOLE_GETCHAR = 0x2,
+ SBI_EXT_0_1_CLEAR_IPI = 0x3,
+ SBI_EXT_0_1_SEND_IPI = 0x4,
+ SBI_EXT_0_1_REMOTE_FENCE_I = 0x5,
+ SBI_EXT_0_1_REMOTE_SFENCE_VMA = 0x6,
+ SBI_EXT_0_1_REMOTE_SFENCE_VMA_ASID = 0x7,
+ SBI_EXT_0_1_SHUTDOWN = 0x8,
+#endif
+ SBI_EXT_BASE = 0x10,
+ SBI_EXT_TIME = 0x54494D45,
+ SBI_EXT_IPI = 0x735049,
+ SBI_EXT_RFENCE = 0x52464E43,
+ SBI_EXT_HSM = 0x48534D,
+};
+
+enum sbi_ext_base_fid {
+ SBI_EXT_BASE_GET_SPEC_VERSION = 0,
+ SBI_EXT_BASE_GET_IMP_ID,
+ SBI_EXT_BASE_GET_IMP_VERSION,
+ SBI_EXT_BASE_PROBE_EXT,
+ SBI_EXT_BASE_GET_MVENDORID,
+ SBI_EXT_BASE_GET_MARCHID,
+ SBI_EXT_BASE_GET_MIMPID,
+};
+
+enum sbi_ext_time_fid {
+ SBI_EXT_TIME_SET_TIMER = 0,
+};
+
+enum sbi_ext_ipi_fid {
+ SBI_EXT_IPI_SEND_IPI = 0,
+};
+
+enum sbi_ext_rfence_fid {
+ SBI_EXT_RFENCE_REMOTE_FENCE_I = 0,
+ SBI_EXT_RFENCE_REMOTE_SFENCE_VMA,
+ SBI_EXT_RFENCE_REMOTE_SFENCE_VMA_ASID,
+ SBI_EXT_RFENCE_REMOTE_HFENCE_GVMA,
+ SBI_EXT_RFENCE_REMOTE_HFENCE_GVMA_VMID,
+ SBI_EXT_RFENCE_REMOTE_HFENCE_VVMA,
+ SBI_EXT_RFENCE_REMOTE_HFENCE_VVMA_ASID,
+};
+
+enum sbi_ext_hsm_fid {
+ SBI_EXT_HSM_HART_START = 0,
+ SBI_EXT_HSM_HART_STOP,
+ SBI_EXT_HSM_HART_STATUS,
+};
+
+enum sbi_hsm_hart_status {
+ SBI_HSM_HART_STATUS_STARTED = 0,
+ SBI_HSM_HART_STATUS_STOPPED,
+ SBI_HSM_HART_STATUS_START_PENDING,
+ SBI_HSM_HART_STATUS_STOP_PENDING,
+};
+
+#define SBI_SPEC_VERSION_DEFAULT 0x1
+#define SBI_SPEC_VERSION_MAJOR_SHIFT 24
+#define SBI_SPEC_VERSION_MAJOR_MASK 0x7f
+#define SBI_SPEC_VERSION_MINOR_MASK 0xffffff
+
+/* SBI return error codes */
+#define SBI_SUCCESS 0
+#define SBI_ERR_FAILURE -1
+#define SBI_ERR_NOT_SUPPORTED -2
+#define SBI_ERR_INVALID_PARAM -3
+#define SBI_ERR_DENIED -4
+#define SBI_ERR_INVALID_ADDRESS -5
+
+extern unsigned long sbi_spec_version;
+struct sbiret {
+ long error;
+ long value;
+};
+
+void sbi_init(void);
+struct sbiret sbi_ecall(int ext, int fid, unsigned long arg0,
+ unsigned long arg1, unsigned long arg2,
+ unsigned long arg3, unsigned long arg4,
+ unsigned long arg5);
+
+void sbi_console_putchar(int ch);
+int sbi_console_getchar(void);
+void sbi_set_timer(uint64_t stime_value);
+void sbi_shutdown(void);
+void sbi_clear_ipi(void);
+int sbi_send_ipi(const unsigned long *hart_mask);
+int sbi_remote_fence_i(const unsigned long *hart_mask);
+int sbi_remote_sfence_vma(const unsigned long *hart_mask,
+ unsigned long start,
+ unsigned long size);
+
+int sbi_remote_sfence_vma_asid(const unsigned long *hart_mask,
+ unsigned long start,
+ unsigned long size,
+ unsigned long asid);
+int sbi_remote_hfence_gvma(const unsigned long *hart_mask,
+ unsigned long start,
+ unsigned long size);
+int sbi_remote_hfence_gvma_vmid(const unsigned long *hart_mask,
+ unsigned long start,
+ unsigned long size,
+ unsigned long vmid);
+int sbi_remote_hfence_vvma(const unsigned long *hart_mask,
+ unsigned long start,
+ unsigned long size);
+int sbi_remote_hfence_vvma_asid(const unsigned long *hart_mask,
+ unsigned long start,
+ unsigned long size,
+ unsigned long asid);
+int sbi_probe_extension(int ext);
+
+/* Check if current SBI specification version is 0.1 or not */
+static inline int sbi_spec_is_0_1(void)
+{
+ return (sbi_spec_version == SBI_SPEC_VERSION_DEFAULT) ? 1 : 0;
+}
+
+/* Get the major version of SBI */
+static inline unsigned long sbi_major_version(void)
+{
+ return (sbi_spec_version >> SBI_SPEC_VERSION_MAJOR_SHIFT) &
+ SBI_SPEC_VERSION_MAJOR_MASK;
+}
+
+/* Get the minor version of SBI */
+static inline unsigned long sbi_minor_version(void)
+{
+ return sbi_spec_version & SBI_SPEC_VERSION_MINOR_MASK;
+}
+
+int sbi_err_map_linux_errno(int err);
+#else /* CONFIG_RISCV_SBI */
+static inline int sbi_remote_fence_i(const unsigned long *hart_mask) { return -1; }
+static inline void sbi_init(void) {}
+#endif /* CONFIG_RISCV_SBI */
+#endif /* _ASM_RISCV_SBI_H */
diff --git a/arch/riscv/include/asm/timer.h b/arch/riscv/include/asm/timer.h
new file mode 100644
index 000000000000..1f78ef4c0099
--- /dev/null
+++ b/arch/riscv/include/asm/timer.h
@@ -0,0 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#ifndef _ASM_RISCV_DELAY_H
+#define _ASM_RISCV_DELAY_H
+
+int timer_init(void);
+extern unsigned long riscv_timebase;
+
+#endif /* _ASM_RISCV_DELAY_H */
diff --git a/arch/riscv/lib/dtb.c b/arch/riscv/lib/dtb.c
index c7fa6b0c31ca..8c2f5b251883 100644
--- a/arch/riscv/lib/dtb.c
+++ b/arch/riscv/lib/dtb.c
@@ -4,6 +4,7 @@
#include <init.h>
#include <of.h>
#include <asm/barebox-riscv.h>
+#include <asm/timer.h>
static int of_riscv_init(void)
{
@@ -21,6 +22,9 @@ static int of_riscv_init(void)
barebox_register_fdt(fdt);
+ /* do it now, before clocksource drivers run postcore */
+ timer_init();
+
return 0;
}
core_initcall(of_riscv_init);
diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig
index 6dfe6151ac98..2d8f5113ad8d 100644
--- a/drivers/clocksource/Kconfig
+++ b/drivers/clocksource/Kconfig
@@ -99,4 +99,19 @@ config CLOCKSOURCE_TI_DM
config CLOCKSOURCE_TI_32K
bool
+config RISCV_TIMER
+ bool "Timer for the RISC-V platform" if COMPILE_TEST
+ depends on RISCV && RISCV_SBI
+ help
+ This enables the per-hart timer built into all RISC-V systems, which
+ is accessed via both the SBI and the rdcycle instruction. This is
+ required for all RISC-V systems.
+
+config CLINT_TIMER
+ bool "CLINT Timer for the RISC-V platform" if COMPILE_TEST
+ depends on OFDEVICE
+ help
+ This option enables the CLINT timer for RISC-V systems. The CLINT
+ driver is usually used for NoMMU RISC-V systems.
+
endmenu
diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile
index b4607f787fcf..e3d243eb9423 100644
--- a/drivers/clocksource/Makefile
+++ b/drivers/clocksource/Makefile
@@ -21,3 +21,5 @@ obj-$(CONFIG_CLOCKSOURCE_IMX_GPT) += timer-imx-gpt.o
obj-$(CONFIG_CLOCKSOURCE_DW_APB_TIMER) += dw_apb_timer.o
obj-$(CONFIG_CLOCKSOURCE_TI_DM) += timer-ti-dm.o
obj-$(CONFIG_CLOCKSOURCE_TI_32K) += timer-ti-32k.o
+obj-$(CONFIG_CLINT_TIMER) += timer-clint.o
+obj-$(CONFIG_RISCV_TIMER) += timer-riscv.o
diff --git a/drivers/clocksource/timer-clint.c b/drivers/clocksource/timer-clint.c
new file mode 100644
index 000000000000..b7360010bdb6
--- /dev/null
+++ b/drivers/clocksource/timer-clint.c
@@ -0,0 +1,93 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020 Western Digital Corporation or its affiliates.
+ *
+ * Most of the M-mode (i.e. NoMMU) RISC-V systems usually have a
+ * CLINT MMIO timer device.
+ */
+
+#define pr_fmt(fmt) "clint: " fmt
+
+#include <common.h>
+#include <init.h>
+#include <clock.h>
+#include <errno.h>
+#include <of.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <io.h>
+#include <asm/timer.h>
+
+#define CLINT_TIMER_VAL_OFF 0xbff8
+
+#ifdef CONFIG_64BIT
+#define clint_get_cycles() readq(clint_timer_val)
+#else
+#define clint_get_cycles() readl(clint_timer_val)
+#define clint_get_cycles_hi() readl(((u32 *)clint_timer_val) + 1)
+#endif
+
+static void __iomem *clint_timer_val;
+
+#ifdef CONFIG_64BIT
+static u64 notrace clint_get_cycles64(void)
+{
+ return clint_get_cycles();
+}
+#else /* CONFIG_64BIT */
+static u64 notrace clint_get_cycles64(void)
+{
+ u32 hi, lo;
+
+ do {
+ hi = clint_get_cycles_hi();
+ lo = clint_get_cycles();
+ } while (hi != clint_get_cycles_hi());
+
+ return ((u64)hi << 32) | lo;
+}
+#endif /* CONFIG_64BIT */
+
+static u64 clint_rdtime(void)
+{
+ return clint_get_cycles64();
+}
+
+static struct clocksource clint_clocksource = {
+ .read = clint_rdtime,
+ .mask = CLOCKSOURCE_MASK(64),
+ .priority = 200,
+};
+
+static int clint_timer_init_dt(struct device_d* dev)
+{
+ struct resource *iores;
+
+ /* one timer is enough */
+ if (clint_timer_val)
+ return 0;
+
+ iores = dev_request_mem_resource(dev, 0);
+ if (IS_ERR(iores))
+ return PTR_ERR(iores);
+ clint_timer_val = IOMEM(iores->start) + CLINT_TIMER_VAL_OFF;
+
+ dev_info(dev, "running at %lu Hz\n", riscv_timebase);
+
+ clint_clocksource.mult = clocksource_hz2mult(riscv_timebase, clint_clocksource.shift);
+
+ return init_clock(&clint_clocksource);
+}
+
+static struct of_device_id timer_clint_dt_ids[] = {
+ { .compatible = "riscv,clint0", },
+ { .compatible = "sifive,clint0" },
+ { /* sentinel */ },
+};
+
+static struct driver_d clint_timer_driver = {
+ .name = "clint-timer",
+ .probe = clint_timer_init_dt,
+ .of_compatible = timer_clint_dt_ids,
+};
+postcore_platform_driver(clint_timer_driver);
diff --git a/drivers/clocksource/timer-riscv.c b/drivers/clocksource/timer-riscv.c
new file mode 100644
index 000000000000..637285fd78a7
--- /dev/null
+++ b/drivers/clocksource/timer-riscv.c
@@ -0,0 +1,50 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2012 Regents of the University of California
+ * Copyright (C) 2017 SiFive
+ *
+ * All RISC-V systems have a timer attached to every hart. These timers can
+ * either be read from the "time" and "timeh" CSRs, and can use the SBI to
+ * setup events, or directly accessed using MMIO registers.
+ */
+#include <common.h>
+#include <init.h>
+#include <clock.h>
+#include <asm/timer.h>
+#include <asm/csr.h>
+
+static u64 notrace riscv_timer_get_count(void)
+{
+ __maybe_unused u32 hi, lo;
+
+ if (IS_ENABLED(CONFIG_64BIT))
+ return csr_read(CSR_TIME);
+
+ do {
+ hi = csr_read(CSR_TIMEH);
+ lo = csr_read(CSR_TIME);
+ } while (hi != csr_read(CSR_TIMEH));
+
+ return ((u64)hi << 32) | lo;
+}
+
+static struct clocksource riscv_clocksource = {
+ .read = riscv_timer_get_count,
+ .mask = CLOCKSOURCE_MASK(64),
+ .priority = 100,
+};
+
+static int riscv_timer_init(struct device_d* dev)
+{
+ dev_info(dev, "running at %lu Hz\n", riscv_timebase);
+
+ riscv_clocksource.mult = clocksource_hz2mult(riscv_timebase, riscv_clocksource.shift);
+
+ return init_clock(&riscv_clocksource);
+}
+
+static struct driver_d riscv_timer_driver = {
+ .name = "riscv-timer",
+ .probe = riscv_timer_init,
+};
+postcore_platform_driver(riscv_timer_driver);
--
2.29.2
More information about the barebox
mailing list