[PATCH] Revert "riscv: Clean up IPI initialization code"

Bin Meng bmeng.cn at gmail.com
Wed Jul 8 12:09:15 CEST 2020


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.

>
>  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


More information about the U-Boot mailing list