Hi Sean, On Wed, Jul 8, 2020 at 5:34 PM Sean Anderson <sean...@gmail.com> wrote: > > On 7/8/20 1:02 AM, Bin Meng wrote: > > From: Bin Meng <bin.m...@windriver.com> > > > > This reverts commit 40686c394e533fec765fe237936e353c84e73fff. > > > > Commit 40686c394e53 ("riscv: Clean up IPI initialization code") > > caused U-Boot failed to boot on SiFive HiFive Unleashed board. > > > > The codes inside arch_cpu_init_dm() may call U-Boot timer APIs > > before the call to riscv_init_ipi(). At that time the timer register > > base (e.g.: the SiFive CLINT device in this case) is unknown yet. > > In general, I don't think the existing implementation for timers on > riscv (storage of base address in gd_t and initialization on first use) > is necessary at all. riscv_timer_probe gets called before riscv_get_time > gets called for the first time, and any initialization can be done > there. In addition, there is already a way to select and initialize > timers in the form of the /chosen/tick-timer property. > > For example, the following (untested) patch converts the andestech timer > to a uclass timer driver. It fails to link since riscv_get_time is no > longer provided, but that function is only ever used by the riscv-timer > driver. > > --- > arch/riscv/dts/ae350_32.dts | 2 ++ > arch/riscv/dts/ae350_64.dts | 2 ++ > arch/riscv/lib/andes_plmt.c | 51 +++++++++++++++++++++---------------- > 3 files changed, 33 insertions(+), 22 deletions(-) > > diff --git a/arch/riscv/dts/ae350_32.dts b/arch/riscv/dts/ae350_32.dts > index 3f8525fe56..f8f7ec8073 100644 > --- a/arch/riscv/dts/ae350_32.dts > +++ b/arch/riscv/dts/ae350_32.dts > @@ -14,6 +14,7 @@ > chosen { > bootargs = "console=ttyS0,38400n8 debug loglevel=7"; > stdout-path = "uart0:38400n8"; > + tick-timer = "/soc/plmt0@e6000000"; > }; > > cpus { > @@ -162,6 +163,7 @@ > &CPU2_intc 7 > &CPU3_intc 7>; > reg = <0xe6000000 0x100000>; > + clock-frequency = <60000000>; > }; > }; > > diff --git a/arch/riscv/dts/ae350_64.dts b/arch/riscv/dts/ae350_64.dts > index 482c707503..f014f053aa 100644 > --- a/arch/riscv/dts/ae350_64.dts > +++ b/arch/riscv/dts/ae350_64.dts > @@ -14,6 +14,7 @@ > chosen { > bootargs = "console=ttyS0,38400n8 debug loglevel=7"; > stdout-path = "uart0:38400n8"; > + tick-timer = "/soc/plmt0@e6000000"; > }; > > cpus { > @@ -162,6 +163,7 @@ > &CPU2_intc 7 > &CPU3_intc 7>; > reg = <0x0 0xe6000000 0x0 0x100000>; > + clock-frequency = <60000000>; > }; > }; > > diff --git a/arch/riscv/lib/andes_plmt.c b/arch/riscv/lib/andes_plmt.c > index a7e90ca992..653fa55390 100644 > --- a/arch/riscv/lib/andes_plmt.c > +++ b/arch/riscv/lib/andes_plmt.c > @@ -1,6 +1,7 @@ > // SPDX-License-Identifier: GPL-2.0+ > /* > * Copyright (C) 2019, Rick Chen <r...@andestech.com> > + * Copyright (C) 2020, Sean Anderson <sean...@gmail.com> > * > * U-Boot syscon driver for Andes's Platform Level Machine Timer (PLMT). > * The PLMT block holds memory-mapped mtime register > @@ -9,46 +10,52 @@ > > #include <common.h> > #include <dm.h> > -#include <regmap.h> > -#include <syscon.h> > +#include <timer.h> > #include <asm/io.h> > -#include <asm/syscon.h> > #include <linux/err.h> > > /* mtime register */ > #define MTIME_REG(base) ((ulong)(base)) > > -DECLARE_GLOBAL_DATA_PTR; > - > -#define PLMT_BASE_GET(void) \ > - do { \ > - long *ret; \ > - \ > - if (!gd->arch.plmt) { \ > - ret = syscon_get_first_range(RISCV_SYSCON_PLMT); \ > - if (IS_ERR(ret)) \ > - return PTR_ERR(ret); \ > - gd->arch.plmt = ret; \ > - } \ > - } while (0) > - > -int riscv_get_time(u64 *time) > +static int andes_plmt_get_count(struct udevice* dev, u64 *count) > { > - PLMT_BASE_GET(); > + *count = readq((void __iomem *)MTIME_REG(dev->priv)); > > - *time = readq((void __iomem *)MTIME_REG(gd->arch.plmt)); > + return 0; > +} > + > +static const struct timer_ops andes_plmt_ops = { > + .get_count = andes_plmt_get_count, > +}; > + > +static int andes_plmt_probe(struct udevice *dev) > +{ > + int ret; > + struct timer_dev_priv *uc_priv = dev_get_uclass_priv(dev); > + u32 clock_rate; > + > + dev->priv = dev_read_addr_ptr(dev); > + if (!dev->priv) > + return -EINVAL; > + > + ret = dev_read_u32(dev, "clock-frequency", &clock_rate); > + if (ret) > + return ret; > + uc_priv->clock_rate = clock_rate; > > return 0; > }
Yes, for Andes PLMT, it should be a timer device. However for SiFive CLINT, it is a multi-function device, and this does not fit very well. > > static const struct udevice_id andes_plmt_ids[] = { > - { .compatible = "riscv,plmt0", .data = RISCV_SYSCON_PLMT }, > + { .compatible = "riscv,plmt0" }, > { } > }; > > U_BOOT_DRIVER(andes_plmt) = { > .name = "andes_plmt", > - .id = UCLASS_SYSCON, > + .id = UCLASS_TIMER, > .of_match = andes_plmt_ids, > + .ops = &andes_plmt_ops, > + .probe = andes_plmt_probe, > .flags = DM_FLAG_PRE_RELOC, > }; > -- > 2.26.2 > > > It might be the name riscv_init_ipi() that misleads people to only > > consider it is related to IPI, but in fact the timer capability is > > provided by the same SiFive CLINT device that provides the IPI. > > Timer capability is needed for both UP and SMP. > > Ideally, it *is* only related to IPIs. As outlined above, timers can be > implemented without using global data at all by leveraging existing > systems. The dependency here was unintended. > > > As the commit was a clean up attempt which did not bring in any > > other benefits than to break the SiFive HiFive Unleashed board, > > revert it. > > This refactor does have benefits. It makes the IPI code more similar to > U-boot initialization idioms. It also removes some quite (imo) ugly > macros. I think there is a much more minimal revert below which can be > used as a stopgap. > > --- > arch/riscv/lib/sifive_clint.c | 16 ++++++++++++++++ > 1 file changed, 16 insertions(+) > > diff --git a/arch/riscv/lib/sifive_clint.c b/arch/riscv/lib/sifive_clint.c > index 78fc6c868d..3dfafd9130 100644 > --- a/arch/riscv/lib/sifive_clint.c > +++ b/arch/riscv/lib/sifive_clint.c > @@ -24,8 +24,22 @@ > > DECLARE_GLOBAL_DATA_PTR; > > +#define CLINT_BASE_GET(void) \ > + do { \ > + long *ret; \ > + \ > + if (!gd->arch.clint) { \ > + ret = syscon_get_first_range(RISCV_SYSCON_CLINT); \ > + if (IS_ERR(ret)) \ > + return PTR_ERR(ret); \ > + gd->arch.clint = ret; \ > + } \ > + } while (0) > + > int riscv_get_time(u64 *time) > { > + CLINT_BASE_GET(); > + Yes, this partial revert works. > *time = readq((void __iomem *)MTIME_REG(gd->arch.clint)); > > return 0; > @@ -33,6 +47,8 @@ int riscv_get_time(u64 *time) > > int riscv_set_timecmp(int hart, u64 cmp) > { > + CLINT_BASE_GET(); > + > writeq(cmp, (void __iomem *)MTIMECMP_REG(gd->arch.clint, hart)); > > return 0; > -- > 2.26.2 > > Alternatively, the following patch would also (indirectly) work, though > it is more brittle. > > --- > arch/riscv/cpu/cpu.c | 12 ++++++------ > 1 file changed, 6 insertions(+), 6 deletions(-) > > diff --git a/arch/riscv/cpu/cpu.c b/arch/riscv/cpu/cpu.c > index bbd6c15352..1fe22d28b3 100644 > --- a/arch/riscv/cpu/cpu.c > +++ b/arch/riscv/cpu/cpu.c > @@ -76,6 +76,12 @@ int arch_cpu_init_dm(void) > { > int ret; > > +#ifdef CONFIG_SMP > + ret = riscv_init_ipi(); > + if (ret) > + return ret; > +#endif No, this one does not work. At least it should be #if CONFIG_IS_ENABLED(SMP) to consider the SPL case. But even considering SPL, we should also consider UP as well because timer is unconditionally needed regardless of UP/SMP. > + > ret = riscv_cpu_probe(); > if (ret) > return ret; > @@ -107,12 +113,6 @@ int arch_cpu_init_dm(void) > #endif > } > > -#ifdef CONFIG_SMP > - ret = riscv_init_ipi(); > - if (ret) > - return ret; > -#endif Regards, Bin