[PATCH 4/6] platform: andes/ae350: Implement hart hotplug using HSM extension

Anup Patel anup at brainfault.org
Mon Jan 16 01:09:11 PST 2023


On Wed, Jan 4, 2023 at 12:02 PM Yu Chien Peter Lin
<peterlin at andestech.com> wrote:
>
> Add hart_start() and hart_stop() callbacks for the multi-core ae350
> platform, it utilizes the ATCSMU to put the harts into power-gated
> deep sleep mode. The programming sequence is stated as below:
>
> 1. Set the wakeup events to PCSm_WE
> 2. Set the sleep command to PCSm_CTL
> 3. Set the reset vector to HARTm_RESET_VECTOR_{LO|HI}
> 4. Write back and invalidate D-cache by executing the CCTL command L1D_WBINVAL_ALL
> 5. Disable I/D-cache by clearing mcache_ctl.{I|D}C_EN
> 6. Disable D-cache coherency by clearing mcache_ctl_.DC_COHEN
> 7. Wait for mcache_ctl.DC_COHSTA to be cleared to ensure the previous step is completed
> 8. Execute WFI
>
> Signed-off-by: Yu Chien Peter Lin <peterlin at andestech.com>

Looks good to me.

Reviewed-by: Anup Patel <anup at brainfault.org>

Regards,
Anup

> ---
>  platform/generic/andes/ae350.c           | 138 +++++++++++++++++++++++
>  platform/generic/andes/objects.mk        |   2 +-
>  platform/generic/andes/sleep.S           |  61 ++++++++++
>  platform/generic/include/andes/andes45.h |  10 ++
>  platform/generic/include/andes/atcsmu.h  |  44 ++++++++
>  5 files changed, 254 insertions(+), 1 deletion(-)
>  create mode 100644 platform/generic/andes/sleep.S
>  create mode 100644 platform/generic/include/andes/andes45.h
>  create mode 100644 platform/generic/include/andes/atcsmu.h
>
> diff --git a/platform/generic/andes/ae350.c b/platform/generic/andes/ae350.c
> index cf7f6f2..62aba50 100644
> --- a/platform/generic/andes/ae350.c
> +++ b/platform/generic/andes/ae350.c
> @@ -10,6 +10,143 @@
>  #include <platform_override.h>
>  #include <sbi_utils/fdt/fdt_helper.h>
>  #include <sbi_utils/fdt/fdt_fixup.h>
> +#include <sbi/riscv_io.h>
> +#include <sbi/sbi_bitops.h>
> +#include <sbi/sbi_console.h>
> +#include <sbi/sbi_error.h>
> +#include <sbi/sbi_hsm.h>
> +#include <sbi/sbi_ipi.h>
> +
> +#include <andes/atcsmu.h>
> +#include <andes/andes45.h>
> +
> +struct smu_data smu;
> +extern void __ae350_enable_coherency_warmboot(void);
> +extern void __ae350_disable_coherency(void);
> +
> +static __always_inline bool is_andes25(void)
> +{
> +       ulong marchid = csr_read(CSR_MARCHID);
> +       return EXTRACT_FIELD(marchid, CSR_MARCHID_MICROID) == 0xa25;
> +}
> +
> +static void smu_set_wakeup_events(u32 events, u32 hartid)
> +{
> +       writel(events, (void *)(smu.addr + PCSm_WE_OFFSET(hartid)));
> +}
> +
> +static bool smu_support_sleep_mode(u32 sleep_mode, u32 hartid)
> +{
> +       u32 pcs_cfg;
> +
> +       pcs_cfg = readl((void *)(smu.addr + PCSm_CFG_OFFSET(hartid)));
> +
> +       switch (sleep_mode) {
> +       case LIGHTSLEEP_MODE:
> +               if (EXTRACT_FIELD(pcs_cfg, PCS_CFG_LIGHT_SLEEP) == 0) {
> +                       sbi_printf(
> +                               "SMU: hart%d (PCS%d) does not support light sleep mode\n",
> +                               hartid, hartid + 3);
> +                       return false;
> +               }
> +       case DEEPSLEEP_MODE:
> +               if (EXTRACT_FIELD(pcs_cfg, PCS_CFG_DEEP_SLEEP) == 0) {
> +                       sbi_printf(
> +                               "SMU: hart%d (PCS%d) does not support deep sleep mode\n",
> +                               hartid, hartid + 3);
> +                       return false;
> +               }
> +       };
> +
> +       return true;
> +}
> +
> +static void smu_set_command(u32 pcs_ctl, u32 hartid)
> +{
> +       writel(pcs_ctl, (void *)(smu.addr + PCSm_CTL_OFFSET(hartid)));
> +}
> +
> +static void smu_set_reset_vector(ulong wakeup_addr, u32 hartid)
> +{
> +       writel(wakeup_addr,
> +              (void *)(smu.addr + HARTn_RESET_VEC_LO(hartid)));
> +       writel((u64)wakeup_addr >> 32,
> +              (void *)(smu.addr + HARTn_RESET_VEC_HI(hartid)));
> +}
> +
> +static int ae350_hart_start(u32 hartid, ulong saddr)
> +{
> +       if (is_andes25() && hartid == 0)
> +               return sbi_ipi_raw_send(hartid);
> +
> +       /* Write wakeup command to the sleep hart */
> +       smu_set_command(WAKEUP_CMD, hartid);
> +
> +       return 0;
> +}
> +
> +static int ae350_hart_stop(void)
> +{
> +       u32 hartid = current_hartid();
> +
> +       /**
> +        * The hart0 shares power domain with L2-cache,
> +        * instead of turning it off, it falls through
> +        * and jump to warmboot_addr.
> +        */
> +       if (is_andes25() && hartid == 0)
> +               return SBI_ENOTSUPP;
> +
> +       if (!smu_support_sleep_mode(DEEPSLEEP_MODE, hartid))
> +               return SBI_ENOTSUPP;
> +
> +       /**
> +        * disable all events, the current hart will be
> +        * woken up from reset vector when other hart
> +        * writes its PCS (power control slot) control
> +        * register
> +        */
> +       smu_set_wakeup_events(0x0, hartid);
> +       smu_set_command(DEEP_SLEEP_CMD, hartid);
> +       smu_set_reset_vector((ulong)__ae350_enable_coherency_warmboot,
> +                              hartid);
> +       __ae350_disable_coherency();
> +
> +       wfi();
> +
> +       /* It should never reach here */
> +       sbi_hart_hang();
> +       return 0;
> +}
> +
> +static const struct sbi_hsm_device andes_smu = {
> +       .name         = "andes_smu",
> +       .hart_start   = ae350_hart_start,
> +       .hart_stop    = ae350_hart_stop,
> +};
> +
> +static void ae350_hsm_device_init(void)
> +{
> +       int rc;
> +       void *fdt;
> +
> +       fdt = fdt_get_address();
> +
> +       rc = fdt_parse_compat_addr(fdt, (uint64_t *)&smu.addr,
> +                                  "andestech,atcsmu");
> +
> +       if (!rc) {
> +               sbi_hsm_set_device(&andes_smu);
> +       }
> +}
> +
> +static int ae350_final_init(bool cold_boot, const struct fdt_match *match)
> +{
> +       if (cold_boot)
> +               ae350_hsm_device_init();
> +
> +       return 0;
> +}
>
>  static const struct fdt_match andes_ae350_match[] = {
>         { .compatible = "andestech,ae350" },
> @@ -18,4 +155,5 @@ static const struct fdt_match andes_ae350_match[] = {
>
>  const struct platform_override andes_ae350 = {
>         .match_table = andes_ae350_match,
> +       .final_init  = ae350_final_init,
>  };
> diff --git a/platform/generic/andes/objects.mk b/platform/generic/andes/objects.mk
> index dd6408d..28275ef 100644
> --- a/platform/generic/andes/objects.mk
> +++ b/platform/generic/andes/objects.mk
> @@ -3,4 +3,4 @@
>  #
>
>  carray-platform_override_modules-$(CONFIG_PLATFORM_ANDES_AE350) += andes_ae350
> -platform-objs-$(CONFIG_PLATFORM_ANDES_AE350) += andes/ae350.o
> +platform-objs-$(CONFIG_PLATFORM_ANDES_AE350) += andes/ae350.o andes/sleep.o
> diff --git a/platform/generic/andes/sleep.S b/platform/generic/andes/sleep.S
> new file mode 100644
> index 0000000..2171f0d
> --- /dev/null
> +++ b/platform/generic/andes/sleep.S
> @@ -0,0 +1,61 @@
> +/*
> + * SPDX-License-Identifier: BSD-2-Clause
> + */
> +
> +#include <sbi/riscv_encoding.h>
> +#include <sbi/riscv_asm.h>
> +#include <andes/andes45.h>
> +
> +       .section .text, "ax", %progbits
> +       .align 3
> +       .global __ae350_disable_coherency
> +__ae350_disable_coherency:
> +       /* flush D-cache */
> +       csrw    CSR_MCCTLCOMMAND, 0x6
> +       /* disable I/D-cache */
> +       csrc    CSR_MCACHECTL, 0x3
> +       /* disable D-cache coherency */
> +       lui     t1, 0x80
> +       csrc    CSR_MCACHECTL, t1
> +       /* 45-series: wait for mcache_ctl.DC_COHSTA to be cleared */
> +check_cm_disabled:
> +       csrr    t1, CSR_MCACHECTL
> +       srli    t1, t1, 20
> +       andi    t1, t1, 0x1
> +       bnez    t1, check_cm_disabled
> +
> +       ret
> +
> +       .section .text, "ax", %progbits
> +       .align 3
> +       .global __ae350_enable_coherency
> +__ae350_enable_coherency:
> +       /* enable D-cache coherency */
> +       lui             t1, 0x80
> +       csrs    CSR_MCACHECTL, t1
> +       /*
> +        * check CM support
> +        * 25-series: mcache_ctl.DC_COHEN is hard-wired to 0
> +        */
> +       csrr    t1, CSR_MCACHECTL
> +       srli    t1, t1, 20
> +       andi    t1, t1, 0x1
> +       beqz    t1, enable_L1_cache
> +       /* 45-series: wait for mcache_ctl.DC_COHSTA to be set */
> +check_cm_enabled:
> +       csrr    t1, CSR_MCACHECTL
> +       srli    t1, t1, 20
> +       andi    t1, t1, 0x1
> +       beqz    t1, check_cm_enabled
> +enable_L1_cache:
> +       /* enable I/D-cache */
> +       csrs    CSR_MCACHECTL, 0x3
> +
> +       ret
> +
> +       .section .text, "ax", %progbits
> +       .align 3
> +       .global __ae350_enable_coherency_warmboot
> +__ae350_enable_coherency_warmboot:
> +       call ra, __ae350_enable_coherency
> +       j _start_warm
> diff --git a/platform/generic/include/andes/andes45.h b/platform/generic/include/andes/andes45.h
> new file mode 100644
> index 0000000..aea2368
> --- /dev/null
> +++ b/platform/generic/include/andes/andes45.h
> @@ -0,0 +1,10 @@
> +#ifndef _RISCV_ANDES45_H
> +#define _RISCV_ANDES45_H
> +
> +#define CSR_MARCHID_MICROID 0xfff
> +
> +/* Memory and Miscellaneous Registers */
> +#define CSR_MCACHECTL 0x7ca
> +#define CSR_MCCTLCOMMAND 0x7cc
> +
> +#endif /* _RISCV_ANDES45_H */
> diff --git a/platform/generic/include/andes/atcsmu.h b/platform/generic/include/andes/atcsmu.h
> new file mode 100644
> index 0000000..c72600c
> --- /dev/null
> +++ b/platform/generic/include/andes/atcsmu.h
> @@ -0,0 +1,44 @@
> +#ifndef _RISCV_ATCSMU_H
> +#define _RISCV_ATCSMU_H
> +
> +#define PCS0_WE_OFFSET 0x90
> +#define PCSm_WE_OFFSET(i) ((i + 3) * 0x20 + PCS0_WE_OFFSET)
> +
> +#define PCS0_CTL_OFFSET 0x94
> +#define PCSm_CTL_OFFSET(i) ((i + 3) * 0x20 + PCS0_CTL_OFFSET)
> +#define PCS_CTL_CMD_SHIFT 0
> +#define PCS_CTL_PARAM_SHIFT 3
> +#define SLEEP_CMD 0x3
> +#define WAKEUP_CMD (0x0 | (1 << PCS_CTL_PARAM_SHIFT))
> +#define LIGHTSLEEP_MODE 0
> +#define DEEPSLEEP_MODE 1
> +#define LIGHT_SLEEP_CMD (SLEEP_CMD | (LIGHTSLEEP_MODE << PCS_CTL_PARAM_SHIFT))
> +#define DEEP_SLEEP_CMD (SLEEP_CMD | (DEEPSLEEP_MODE << PCS_CTL_PARAM_SHIFT))
> +
> +#define PCS0_CFG_OFFSET 0x80
> +#define PCSm_CFG_OFFSET(i) ((i + 3) * 0x20 + PCS0_CFG_OFFSET)
> +#define PCS_CFG_LIGHT_SLEEP_SHIFT 2
> +#define PCS_CFG_LIGHT_SLEEP (1 << PCS_CFG_LIGHT_SLEEP_SHIFT)
> +#define PCS_CFG_DEEP_SLEEP_SHIFT 3
> +#define PCS_CFG_DEEP_SLEEP (1 << PCS_CFG_DEEP_SLEEP_SHIFT)
> +#define RESET_VEC_LO_OFFSET 0x50
> +#define RESET_VEC_HI_OFFSET 0x60
> +#define RESET_VEC_8CORE_OFFSET 0x1a0
> +#define HARTn_RESET_VEC_LO(n)  \
> +       (RESET_VEC_LO_OFFSET + \
> +        ((n) < 4 ? 0 : RESET_VEC_8CORE_OFFSET) + ((n) * 0x4))
> +#define HARTn_RESET_VEC_HI(n)  \
> +       (RESET_VEC_HI_OFFSET + \
> +        ((n) < 4 ? 0 : RESET_VEC_8CORE_OFFSET) + ((n) * 0x4))
> +#define PCS_MAX_NR 8
> +#define FLASH_BASE 0x80000000ULL
> +
> +#ifndef __ASSEMBLER__
> +
> +struct smu_data {
> +       unsigned long addr;
> +};
> +
> +#endif /* __ASSEMBLER__ */
> +
> +#endif /* _RISCV_ATCSMU_H */
> --
> 2.34.1
>
>
> --
> opensbi mailing list
> opensbi at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/opensbi



More information about the opensbi mailing list