From c33efafaf949ef11fc525cd5be018ea48c40898c Mon Sep 17 00:00:00 2001 From: Sean Anderson Date: Mon, 28 Sep 2020 10:52:21 -0400 Subject: riscv: Rework riscv timer driver to only support S-mode The riscv-timer driver currently serves as a shim for several riscv timer drivers. This is not too desirable because it bypasses the usual timer selection via the driver model. There is no easy way to specify an alternate timing driver, or have the tick rate depend on the cpu's configured frequency. The timer drivers also do not have device structs, and so have to rely on storing parameters in gd_t. Lastly, there is no initialization call, so driver init is done in the same function which reads the time. This can result in confusing error messages. To a user, it looks like the driver failed when trying to read the time, whereas it may have failed while initializing. This patch removes the shim functionality from the riscv-timer driver, and has it instead implement the former rdtime.c timer driver. This is because existing u-boot users who pass in a device tree (e.g. qemu) do not create a timer device for S-mode u-boot. The existing behavior of creating the riscv-timer device in the riscv cpu driver must be kept. The actual reading of the CSRs has been redone in the style of Linux's get_cycles64. Signed-off-by: Sean Anderson Reviewed-by: Bin Meng Reviewed-by: Rick Chen --- drivers/timer/Kconfig | 4 ++-- drivers/timer/riscv_timer.c | 39 ++++++++++++++++++++------------------- 2 files changed, 22 insertions(+), 21 deletions(-) (limited to 'drivers/timer') diff --git a/drivers/timer/Kconfig b/drivers/timer/Kconfig index 637024445c1..d40d3130113 100644 --- a/drivers/timer/Kconfig +++ b/drivers/timer/Kconfig @@ -146,8 +146,8 @@ config RISCV_TIMER bool "RISC-V timer support" depends on TIMER && RISCV help - Select this to enable support for the timer as defined - by the RISC-V privileged architecture spec. + Select this to enable support for a generic RISC-V S-Mode timer + driver. config ROCKCHIP_TIMER bool "Rockchip timer support" diff --git a/drivers/timer/riscv_timer.c b/drivers/timer/riscv_timer.c index 9f9f070e0b3..449fcfcfd59 100644 --- a/drivers/timer/riscv_timer.c +++ b/drivers/timer/riscv_timer.c @@ -1,36 +1,37 @@ // SPDX-License-Identifier: GPL-2.0+ /* + * Copyright (C) 2020, Sean Anderson * Copyright (C) 2018, Bin Meng + * Copyright (C) 2018, Anup Patel + * Copyright (C) 2012 Regents of the University of California * - * RISC-V privileged architecture defined generic timer driver + * RISC-V architecturally-defined generic timer driver * - * This driver relies on RISC-V platform codes to provide the essential API - * riscv_get_time() which is supposed to return the timer counter as defined - * by the RISC-V privileged architecture spec. - * - * This driver can be used in both M-mode and S-mode U-Boot. + * This driver provides generic timer support for S-mode U-Boot. */ #include #include #include #include -#include - -/** - * riscv_get_time() - get the timer counter - * - * Platform codes should provide this API in order to make this driver function. - * - * @time: the 64-bit timer count as defined by the RISC-V privileged - * architecture spec. - * @return: 0 on success, -ve on error. - */ -extern int riscv_get_time(u64 *time); +#include static int riscv_timer_get_count(struct udevice *dev, u64 *count) { - return riscv_get_time(count); + if (IS_ENABLED(CONFIG_64BIT)) { + *count = csr_read(CSR_TIME); + } else { + u32 hi, lo; + + do { + hi = csr_read(CSR_TIMEH); + lo = csr_read(CSR_TIME); + } while (hi != csr_read(CSR_TIMEH)); + + *count = ((u64)hi << 32) | lo; + } + + return 0; } static int riscv_timer_probe(struct udevice *dev) -- cgit v1.3.1 From 3576121687965ffe580fc44f5dd1d8e9ab434c5b Mon Sep 17 00:00:00 2001 From: Sean Anderson Date: Mon, 28 Sep 2020 10:52:22 -0400 Subject: timer: Add helper for drivers using timebase fallback This function is designed to be used when a timer used to be initialized by the cpu (e.g. RISC-V timers), but now is initialized by dm_timer_init. In such a case, the timer may prefer to use the clocks and clock-frequency properties, but should be able to fall back on using the cpu's timebase-frequency. Signed-off-by: Sean Anderson Reviewed-by: Simon Glass Reviewed-by: Bin Meng Reviewed-by: Rick Chen --- drivers/timer/timer-uclass.c | 31 +++++++++++++++++++++++++++++++ include/timer.h | 15 +++++++++++++++ 2 files changed, 46 insertions(+) (limited to 'drivers/timer') diff --git a/drivers/timer/timer-uclass.c b/drivers/timer/timer-uclass.c index 14dde950a18..e9802c8b43e 100644 --- a/drivers/timer/timer-uclass.c +++ b/drivers/timer/timer-uclass.c @@ -4,6 +4,7 @@ */ #include +#include #include #include #include @@ -79,6 +80,36 @@ static int timer_post_probe(struct udevice *dev) return 0; } +/* + * TODO: should be CONFIG_IS_ENABLED(CPU), but the SPL config has _SUPPORT on + * the end... + */ +#if defined(CONFIG_CPU) || defined(CONFIG_SPL_CPU_SUPPORT) +int timer_timebase_fallback(struct udevice *dev) +{ + struct udevice *cpu; + struct cpu_platdata *cpu_plat; + struct timer_dev_priv *uc_priv = dev_get_uclass_priv(dev); + + /* Did we get our clock rate from the device tree? */ + if (uc_priv->clock_rate) + return 0; + + /* Fall back to timebase-frequency */ + dev_dbg(dev, "missing clocks or clock-frequency property; falling back on timebase-frequency\n"); + cpu = cpu_get_current_dev(); + if (!cpu) + return -ENODEV; + + cpu_plat = dev_get_parent_platdata(cpu); + if (!cpu_plat) + return -ENODEV; + + uc_priv->clock_rate = cpu_plat->timebase_freq; + return 0; +} +#endif + u64 timer_conv_64(u32 count) { /* increment tbh if tbl has rolled over */ diff --git a/include/timer.h b/include/timer.h index a49b500ce35..8b9fa51c53d 100644 --- a/include/timer.h +++ b/include/timer.h @@ -15,6 +15,21 @@ */ int dm_timer_init(void); +/** + * timer_timebase_fallback() - Helper for timers using timebase fallback + * @dev: A timer partially-probed timer device + * + * This is a helper function designed for timers which need to fall back on the + * cpu's timebase. This function is designed to be called during the driver's + * probe(). If there is a clocks or clock-frequency property in the timer's + * binding, then it will be used. Otherwise, the timebase of the current cpu + * will be used. This is initialized by the cpu driver, and usually gotten from + * ``/cpus/timebase-frequency`` or ``/cpus/cpu@X/timebase-frequency``. + * + * Return: 0 if OK, or negative error code on failure + */ +int timer_timebase_fallback(struct udevice *dev); + /* * timer_conv_64 - convert 32-bit counter value to 64-bit * -- cgit v1.3.1 From 7616e3687e447b5a838f472afb5275fe6a841f5b Mon Sep 17 00:00:00 2001 From: Sean Anderson Date: Mon, 28 Sep 2020 10:52:23 -0400 Subject: timer: Add a test for timer_timebase_fallback To test this function, sandbox CPU must set cpu_platdata.timebase_freq on bind. It also needs to expose a method to set the current cpu. I also make some most members of cpu_sandbox_ops static. On the timer side, the device tree property sandbox,timebase-frequency-fallback controls whether sandbox_timer_probe falls back to time_timebase_fallback or to SANDBOX_TIMER_RATE. Signed-off-by: Sean Anderson Reviewed-by: Simon Glass --- arch/sandbox/dts/test.dts | 9 ++++++++- arch/sandbox/include/asm/cpu.h | 11 +++++++++++ drivers/cpu/cpu_sandbox.c | 39 ++++++++++++++++++++++++++++++++------- drivers/timer/sandbox_timer.c | 4 +++- test/dm/timer.c | 27 ++++++++++++++++++++++++++- 5 files changed, 80 insertions(+), 10 deletions(-) create mode 100644 arch/sandbox/include/asm/cpu.h (limited to 'drivers/timer') diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts index 9f45c48e4e0..2f559265a50 100644 --- a/arch/sandbox/dts/test.dts +++ b/arch/sandbox/dts/test.dts @@ -533,7 +533,9 @@ }; cpus { + timebase-frequency = <2000000>; cpu-test1 { + timebase-frequency = <3000000>; compatible = "sandbox,cpu_sandbox"; u-boot,dm-pre-reloc; }; @@ -839,11 +841,16 @@ 0x58 8>; }; - timer { + timer@0 { compatible = "sandbox,timer"; clock-frequency = <1000000>; }; + timer@1 { + compatible = "sandbox,timer"; + sandbox,timebase-frequency-fallback; + }; + tpm2 { compatible = "sandbox,tpm2"; }; diff --git a/arch/sandbox/include/asm/cpu.h b/arch/sandbox/include/asm/cpu.h new file mode 100644 index 00000000000..c97ac7ba95b --- /dev/null +++ b/arch/sandbox/include/asm/cpu.h @@ -0,0 +1,11 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2020 Sean Anderson + */ + +#ifndef __SANDBOX_CPU_H +#define __SANDBOX_CPU_H + +void cpu_sandbox_set_current(const char *name); + +#endif /* __SANDBOX_CPU_H */ diff --git a/drivers/cpu/cpu_sandbox.c b/drivers/cpu/cpu_sandbox.c index caa26e50f28..4ba0d1b99ef 100644 --- a/drivers/cpu/cpu_sandbox.c +++ b/drivers/cpu/cpu_sandbox.c @@ -8,14 +8,15 @@ #include #include -int cpu_sandbox_get_desc(const struct udevice *dev, char *buf, int size) +static int cpu_sandbox_get_desc(const struct udevice *dev, char *buf, int size) { snprintf(buf, size, "LEG Inc. SuperMegaUltraTurbo CPU No. 1"); return 0; } -int cpu_sandbox_get_info(const struct udevice *dev, struct cpu_info *info) +static int cpu_sandbox_get_info(const struct udevice *dev, + struct cpu_info *info) { info->cpu_freq = 42 * 42 * 42 * 42 * 42; info->features = 0x42424242; @@ -24,21 +25,29 @@ int cpu_sandbox_get_info(const struct udevice *dev, struct cpu_info *info) return 0; } -int cpu_sandbox_get_count(const struct udevice *dev) +static int cpu_sandbox_get_count(const struct udevice *dev) { return 42; } -int cpu_sandbox_get_vendor(const struct udevice *dev, char *buf, int size) +static int cpu_sandbox_get_vendor(const struct udevice *dev, char *buf, + int size) { snprintf(buf, size, "Languid Example Garbage Inc."); return 0; } -int cpu_sandbox_is_current(struct udevice *dev) +static const char *cpu_current = "cpu-test1"; + +void cpu_sandbox_set_current(const char *name) { - if (!strcmp(dev->name, "cpu-test1")) + cpu_current = name; +} + +static int cpu_sandbox_is_current(struct udevice *dev) +{ + if (!strcmp(dev->name, cpu_current)) return 1; return 0; @@ -52,7 +61,22 @@ static const struct cpu_ops cpu_sandbox_ops = { .is_current = cpu_sandbox_is_current, }; -int cpu_sandbox_probe(struct udevice *dev) +static int cpu_sandbox_bind(struct udevice *dev) +{ + int ret; + struct cpu_platdata *plat = dev_get_parent_platdata(dev); + + /* first examine the property in current cpu node */ + ret = dev_read_u32(dev, "timebase-frequency", &plat->timebase_freq); + /* if not found, then look at the parent /cpus node */ + if (ret) + ret = dev_read_u32(dev->parent, "timebase-frequency", + &plat->timebase_freq); + + return ret; +} + +static int cpu_sandbox_probe(struct udevice *dev) { return 0; } @@ -67,5 +91,6 @@ U_BOOT_DRIVER(cpu_sandbox) = { .id = UCLASS_CPU, .ops = &cpu_sandbox_ops, .of_match = cpu_sandbox_ids, + .bind = cpu_sandbox_bind, .probe = cpu_sandbox_probe, }; diff --git a/drivers/timer/sandbox_timer.c b/drivers/timer/sandbox_timer.c index 5228486082c..6a503c2f153 100644 --- a/drivers/timer/sandbox_timer.c +++ b/drivers/timer/sandbox_timer.c @@ -40,7 +40,9 @@ static int sandbox_timer_probe(struct udevice *dev) { struct timer_dev_priv *uc_priv = dev_get_uclass_priv(dev); - if (!uc_priv->clock_rate) + if (dev_read_bool(dev, "sandbox,timebase-frequency-fallback")) + return timer_timebase_fallback(dev); + else if (!uc_priv->clock_rate) uc_priv->clock_rate = SANDBOX_TIMER_RATE; return 0; diff --git a/test/dm/timer.c b/test/dm/timer.c index 95dab976654..70043b9eeed 100644 --- a/test/dm/timer.c +++ b/test/dm/timer.c @@ -7,8 +7,10 @@ #include #include #include +#include #include #include +#include /* * Basic test of the timer uclass. @@ -17,9 +19,32 @@ static int dm_test_timer_base(struct unit_test_state *uts) { struct udevice *dev; - ut_assertok(uclass_get_device(UCLASS_TIMER, 0, &dev)); + ut_assertok(uclass_get_device_by_name(UCLASS_TIMER, "timer@0", &dev)); ut_asserteq(1000000, timer_get_rate(dev)); return 0; } DM_TEST(dm_test_timer_base, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +/* + * Test of timebase fallback + */ +static int dm_test_timer_timebase_fallback(struct unit_test_state *uts) +{ + struct udevice *dev; + + cpu_sandbox_set_current("cpu-test1"); + ut_assertok(uclass_get_device_by_name(UCLASS_TIMER, "timer@1", &dev)); + ut_asserteq(3000000, timer_get_rate(dev)); + ut_assertok(device_remove(dev, DM_REMOVE_NORMAL)); + + cpu_sandbox_set_current("cpu-test2"); + ut_assertok(uclass_get_device_by_name(UCLASS_TIMER, "timer@1", &dev)); + ut_asserteq(2000000, timer_get_rate(dev)); + + cpu_sandbox_set_current("cpu-test1"); + + return 0; +} +DM_TEST(dm_test_timer_timebase_fallback, + UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); -- cgit v1.3.1