[PATCH] Revert "riscv: Clean up IPI initialization code"
Sean Anderson
seanga2 at gmail.com
Wed Jul 8 12:48:45 CEST 2020
On 7/8/20 6:09 AM, Bin Meng wrote:
> Hi Sean,
>
> On Wed, Jul 8, 2020 at 5:34 PM Sean Anderson <seanga2 at gmail.com> wrote:
>>
>> On 7/8/20 1:02 AM, Bin Meng wrote:
>>> From: Bin Meng <bin.meng at 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 at 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 at 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 <rick at andestech.com>
>> + * Copyright (C) 2020, Sean Anderson <seanga2 at 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.
The IPI is not a device as far as the rest of u-boot is concerned. I
think we can just use uclass_get_device_by_driver in riscv_ipi_init.
There is a patch to add a clint driver for Linux right now [1], and it
does a similar thing. In that patch, the IPI is initialized by the timer
driver.
>>
>> 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.
By the way, what is the exact call to udelay (or something similar)
which triggers this bug? It must occur between the end of the first
riscv_cpu_bind and the rest of arch_cpu_init_dm. Before that, there is
no timer device, and after that there are clearly no delays.
>> *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.
That sounds like a good idea.
> But even considering SPL, we should also consider UP as well because
> timer is unconditionally needed regardless of UP/SMP.
Yeah, it looks like this patch will not work.
--Sean
[1] https://patchwork.kernel.org/patch/11563003/
(there is a v2 of this patch, but this one has some relevant commentary)
More information about the U-Boot
mailing list