[PATCH 4/6] platform: andes/ae350: Implement hart hotplug using HSM extension
Yu Chien Peter Lin
peterlin at andestech.com
Tue Jan 3 22:29:25 PST 2023
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>
---
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
More information about the opensbi
mailing list