summaryrefslogtreecommitdiffstats
path: root/drivers/clocksource
diff options
context:
space:
mode:
authorAhmad Fatoum <a.fatoum@pengutronix.de>2021-03-22 14:39:17 +0100
committerSascha Hauer <s.hauer@pengutronix.de>2021-03-23 12:16:26 +0100
commit2ee369dcf7a5fdf75483290adc096b1128f8e727 (patch)
tree547b60bc2928aefe8ed8cdaaf8c10ae74122f46a /drivers/clocksource
parent06dd27fde7b5d398b032144ee52536278658bef6 (diff)
downloadbarebox-2ee369dcf7a5fdf75483290adc096b1128f8e727.tar.gz
barebox-2ee369dcf7a5fdf75483290adc096b1128f8e727.tar.xz
clocksource: add driver for RISC-V and CLINT timers
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@pengutronix.de> Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Diffstat (limited to 'drivers/clocksource')
-rw-r--r--drivers/clocksource/Kconfig15
-rw-r--r--drivers/clocksource/Makefile2
-rw-r--r--drivers/clocksource/timer-clint.c93
-rw-r--r--drivers/clocksource/timer-riscv.c66
4 files changed, 176 insertions, 0 deletions
diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig
index 6dfe6151ac..7bc69afd78 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
+ 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 b4607f787f..e3d243eb94 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 0000000000..b7360010bd
--- /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 0000000000..eb5ba2d8c2
--- /dev/null
+++ b/drivers/clocksource/timer-riscv.c
@@ -0,0 +1,66 @@
+// 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_sbi(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 u64 notrace riscv_timer_get_count_rdcycle(void)
+{
+ u64 ticks;
+ asm volatile("rdcycle %0" : "=r" (ticks));
+
+ return ticks;
+}
+
+static u64 notrace riscv_timer_get_count(void)
+{
+ if (IS_ENABLED(CONFIG_RISCV_SBI))
+ return riscv_timer_get_count_sbi();
+ else
+ return riscv_timer_get_count_rdcycle();
+}
+
+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);