* [PATCH 0/5] Add hart state management and system suspend support for AE350
@ 2025-12-29 7:19 Ben Zong-You Xie
2025-12-29 7:19 ` [PATCH 1/5] lib: utils/hsm: factor out ATCSMU code into an HSM driver Ben Zong-You Xie
` (5 more replies)
0 siblings, 6 replies; 9+ messages in thread
From: Ben Zong-You Xie @ 2025-12-29 7:19 UTC (permalink / raw)
To: opensbi
This patch series introduces support for hart state management and
system suspend functionalities on the Andes AE350 platform. For the hart
state management, AE350 platform supports CPU hotplug. For the
system suspend, there are two platform-specific low-power modes,
including deep sleep (suspend to RAM) and light sleep (suspend to standby).
Below is the patch summary:
[1/5]: The Andes System Management Unit (ATCSMU) logic is factored out into
a dedicated FDT-based HSM driver, improving code reuse and
maintainability.
[2/5]: Implement the functions to save and restore platform-specific CSRs
which are lost during CPU hotplug or deep sleep.
[3/5]: Add the functions to enable/disable the cache.
[4/5]: A new Last Level Cache (LLC) driver is introduced to handle required
cache operations for deep sleep.
[5/5]: An ATCSMU-based suspend driver is implemented to support Andes deep
sleep and light sleep, providing the OS with access to standardized
SBI system suspend extensions.
Ben Zong-You Xie (5):
lib: utils/hsm: factor out ATCSMU code into an HSM driver
platform: generic/andes: add CSR save and restore functions for AE350
platform
lib: utils/cache: add cache enable function
lib: utils/cache: add Andes last level cache controller
lib: utils/suspend: add Andes ATCSMU suspend driver
include/sbi_utils/cache/cache.h | 11 +
include/sbi_utils/cache/fdt_cmo_helper.h | 13 ++
include/sbi_utils/hsm/fdt_hsm_andes_atcsmu.h | 65 ++++++
include/sbi_utils/sys/atcsmu.h | 59 ------
lib/utils/cache/Kconfig | 5 +
lib/utils/cache/cache.c | 11 +
lib/utils/cache/fdt_andes_llcache.c | 166 +++++++++++++++
lib/utils/cache/fdt_cmo_helper.c | 23 ++
lib/utils/cache/objects.mk | 3 +
lib/utils/hsm/Kconfig | 4 +
lib/utils/hsm/fdt_hsm_andes_atcsmu.c | 210 +++++++++++++++++++
lib/utils/hsm/objects.mk | 5 +-
lib/utils/reset/Kconfig | 2 +-
lib/utils/reset/fdt_reset_atcwdt200.c | 22 +-
lib/utils/suspend/Kconfig | 5 +
lib/utils/suspend/fdt_suspend_andes_atcsmu.c | 119 +++++++++++
lib/utils/suspend/objects.mk | 3 +
lib/utils/sys/Kconfig | 4 -
lib/utils/sys/atcsmu.c | 89 --------
lib/utils/sys/objects.mk | 1 -
platform/generic/Kconfig | 1 -
platform/generic/andes/ae350.c | 147 +++++--------
platform/generic/andes/objects.mk | 2 +-
platform/generic/andes/sleep.S | 70 -------
platform/generic/configs/defconfig | 3 +
platform/generic/include/andes/andes.h | 84 +++++++-
26 files changed, 791 insertions(+), 336 deletions(-)
create mode 100644 include/sbi_utils/hsm/fdt_hsm_andes_atcsmu.h
delete mode 100644 include/sbi_utils/sys/atcsmu.h
create mode 100644 lib/utils/cache/fdt_andes_llcache.c
create mode 100644 lib/utils/hsm/fdt_hsm_andes_atcsmu.c
create mode 100644 lib/utils/suspend/fdt_suspend_andes_atcsmu.c
delete mode 100644 lib/utils/sys/atcsmu.c
delete mode 100644 platform/generic/andes/sleep.S
--
2.34.1
--
opensbi mailing list
opensbi@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/opensbi
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH 1/5] lib: utils/hsm: factor out ATCSMU code into an HSM driver
2025-12-29 7:19 [PATCH 0/5] Add hart state management and system suspend support for AE350 Ben Zong-You Xie
@ 2025-12-29 7:19 ` Ben Zong-You Xie
2025-12-29 7:19 ` [PATCH 2/5] platform: generic/andes: add CSR save and restore functions for AE350 platform Ben Zong-You Xie
` (4 subsequent siblings)
5 siblings, 0 replies; 9+ messages in thread
From: Ben Zong-You Xie @ 2025-12-29 7:19 UTC (permalink / raw)
To: opensbi; +Cc: Leo Yu-Chi Liang
Refactor ATCSMU (System Management Unit) support by moving it from a
system utility into a dedicated FDT-based HSM driver.
Key changes include:
- Moving the functions in lib/utils/sys/atcsmu.c into the new HSM driver
- Moving hart start and stop operations on AE350 platform into the new
HSM driver
- Converting the assembly-based functions in sleep.S to C code for the
readability
- Updating the ATCWDT200 driver
Signed-off-by: Ben Zong-You Xie <ben717@andestech.com>
Signed-off-by: Leo Yu-Chi Liang <ycliang@andestech.com>
---
include/sbi_utils/hsm/fdt_hsm_andes_atcsmu.h | 49 ++++++
include/sbi_utils/sys/atcsmu.h | 59 -------
lib/utils/hsm/Kconfig | 4 +
lib/utils/hsm/fdt_hsm_andes_atcsmu.c | 166 +++++++++++++++++++
lib/utils/hsm/objects.mk | 5 +-
lib/utils/reset/Kconfig | 2 +-
lib/utils/reset/fdt_reset_atcwdt200.c | 22 +--
lib/utils/sys/Kconfig | 4 -
lib/utils/sys/atcsmu.c | 89 ----------
lib/utils/sys/objects.mk | 1 -
platform/generic/Kconfig | 1 -
platform/generic/andes/ae350.c | 112 +------------
platform/generic/andes/objects.mk | 2 +-
platform/generic/andes/sleep.S | 70 --------
platform/generic/configs/defconfig | 1 +
platform/generic/include/andes/andes.h | 62 +++++++
16 files changed, 301 insertions(+), 348 deletions(-)
create mode 100644 include/sbi_utils/hsm/fdt_hsm_andes_atcsmu.h
delete mode 100644 include/sbi_utils/sys/atcsmu.h
create mode 100644 lib/utils/hsm/fdt_hsm_andes_atcsmu.c
delete mode 100644 lib/utils/sys/atcsmu.c
delete mode 100644 platform/generic/andes/sleep.S
diff --git a/include/sbi_utils/hsm/fdt_hsm_andes_atcsmu.h b/include/sbi_utils/hsm/fdt_hsm_andes_atcsmu.h
new file mode 100644
index 000000000000..881d8b215060
--- /dev/null
+++ b/include/sbi_utils/hsm/fdt_hsm_andes_atcsmu.h
@@ -0,0 +1,49 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2025 Andes Technology Corporation
+ */
+
+#ifndef __FDT_HSM_ANDES_ATCSMU_H__
+#define __FDT_HSM_ANDES_ATCSMU_H__
+
+#include <sbi/sbi_types.h>
+
+/* clang-format off */
+
+#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 PCS0_CFG_OFFSET 0x80
+#define PCSm_CFG_OFFSET(i) ((i + 3) * 0x20 + PCS0_CFG_OFFSET)
+#define PCS_CFG_LIGHT_SLEEP BIT(2)
+#define PCS_CFG_DEEP_SLEEP BIT(3)
+
+#define PCS0_SCRATCH_OFFSET 0x84
+#define PCSm_SCRATCH_OFFSET(i) ((i + 3) * 0x20 + PCS0_SCRATCH_OFFSET)
+
+#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 LIGHT_SLEEP_CMD 0x3
+#define WAKEUP_CMD 0x8
+#define DEEP_SLEEP_CMD 0xb
+
+/* clang-format on */
+
+void atcsmu_set_wakeup_events(u32 events, u32 hartid);
+bool atcsmu_support_sleep_mode(u32 sleep_type, u32 hartid);
+void atcsmu_set_command(u32 pcs_ctl, u32 hartid);
+int atcsmu_set_reset_vector(u64 wakeup_addr, u32 hartid);
+u32 atcsmu_get_sleep_type(u32 hartid);
+
+#endif
diff --git a/include/sbi_utils/sys/atcsmu.h b/include/sbi_utils/sys/atcsmu.h
deleted file mode 100644
index 07be22eef626..000000000000
--- a/include/sbi_utils/sys/atcsmu.h
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * SPDX-License-Identifier: BSD-3-Clause
- *
- * Copyright (c) 2023 Andes Technology Corporation
- */
-
-#ifndef _SYS_ATCSMU_H
-#define _SYS_ATCSMU_H
-
-#include <sbi/sbi_types.h>
-
-/* clang-format off */
-
-#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
-
-/* clang-format on */
-
-struct smu_data {
- unsigned long addr;
-};
-
-int smu_set_wakeup_events(struct smu_data *smu, u32 events, u32 hartid);
-bool smu_support_sleep_mode(struct smu_data *smu, u32 sleep_mode, u32 hartid);
-int smu_set_command(struct smu_data *smu, u32 pcs_ctl, u32 hartid);
-int smu_set_reset_vector(struct smu_data *smu, ulong wakeup_addr, u32 hartid);
-
-#endif /* _SYS_ATCSMU_H */
diff --git a/lib/utils/hsm/Kconfig b/lib/utils/hsm/Kconfig
index 94973c8fd7bd..1dfb243e3109 100644
--- a/lib/utils/hsm/Kconfig
+++ b/lib/utils/hsm/Kconfig
@@ -9,6 +9,10 @@ config FDT_HSM
if FDT_HSM
+config FDT_HSM_ANDES_ATCSMU
+ bool "FDT Andes ATCSMU driver"
+ default n
+
config FDT_HSM_RPMI
bool "FDT RPMI HSM driver"
depends on FDT_MAILBOX && RPMI_MAILBOX
diff --git a/lib/utils/hsm/fdt_hsm_andes_atcsmu.c b/lib/utils/hsm/fdt_hsm_andes_atcsmu.c
new file mode 100644
index 000000000000..ee7873dc6d00
--- /dev/null
+++ b/lib/utils/hsm/fdt_hsm_andes_atcsmu.c
@@ -0,0 +1,166 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2025 Andes Technology Corporation
+ */
+
+#include <andes/andes.h>
+#include <libfdt.h>
+#include <sbi/riscv_io.h>
+#include <sbi/sbi_console.h>
+#include <sbi/sbi_ecall_interface.h>
+#include <sbi/sbi_error.h>
+#include <sbi/sbi_hart.h>
+#include <sbi/sbi_hsm.h>
+#include <sbi/sbi_init.h>
+#include <sbi/sbi_ipi.h>
+#include <sbi_utils/fdt/fdt_driver.h>
+#include <sbi_utils/fdt/fdt_helper.h>
+#include <sbi_utils/hsm/fdt_hsm_andes_atcsmu.h>
+
+static uint64_t atcsmu_base;
+
+void atcsmu_set_wakeup_events(u32 events, u32 hartid)
+{
+ writel_relaxed(events, (char *)atcsmu_base + PCSm_WE_OFFSET(hartid));
+}
+
+bool atcsmu_support_sleep_mode(u32 sleep_type, u32 hartid)
+{
+ u32 pcs_cfg;
+ u32 mask;
+ const char *sleep_mode;
+
+ pcs_cfg = readl_relaxed((char *)atcsmu_base + PCSm_CFG_OFFSET(hartid));
+ switch (sleep_type) {
+ case SBI_SUSP_AE350_LIGHT_SLEEP:
+ mask = PCS_CFG_LIGHT_SLEEP;
+ sleep_mode = "light sleep";
+ break;
+ case SBI_SUSP_SLEEP_TYPE_SUSPEND:
+ mask = PCS_CFG_DEEP_SLEEP;
+ sleep_mode = "deep sleep";
+ break;
+ default:
+ return false;
+ }
+
+ if (!EXTRACT_FIELD(pcs_cfg, mask)) {
+ sbi_printf("ATCSMU: hart%d (PCS%d) does not support %s mode\n",
+ hartid, hartid + 3, sleep_mode);
+ return false;
+ }
+
+ return true;
+}
+
+void atcsmu_set_command(u32 pcs_ctl, u32 hartid)
+{
+ writel_relaxed(pcs_ctl, (char *)atcsmu_base + PCSm_CTL_OFFSET(hartid));
+}
+
+int atcsmu_set_reset_vector(u64 wakeup_addr, u32 hartid)
+{
+ u32 vec_lo;
+ u32 vec_hi;
+ u64 reset_vector;
+
+ writel((u32)wakeup_addr, (char *)atcsmu_base + HARTn_RESET_VEC_LO(hartid));
+ writel((u32)(wakeup_addr >> 32), (char *)atcsmu_base + HARTn_RESET_VEC_HI(hartid));
+ vec_lo = readl((char *)atcsmu_base + HARTn_RESET_VEC_LO(hartid));
+ vec_hi = readl((char *)atcsmu_base + HARTn_RESET_VEC_HI(hartid));
+ reset_vector = (u64)vec_hi << 32 | vec_lo;
+ if (reset_vector != wakeup_addr) {
+ sbi_printf("ATCSMU: hart%d (PCS%d): failed to program the reset vector\n",
+ hartid, hartid + 3);
+ return SBI_EFAIL;
+ }
+
+ return SBI_OK;
+}
+
+u32 atcsmu_get_sleep_type(u32 hartid)
+{
+ return readl_relaxed((char *)atcsmu_base + PCSm_SCRATCH_OFFSET(hartid));
+}
+
+static int ae350_hart_start(u32 hartid, ulong saddr)
+{
+ u32 hartindex = sbi_hartid_to_hartindex(hartid);
+
+ /*
+ * Don't send wakeup command when:
+ * 1) boot time
+ * 2) the target hart is non-sleepable 25-series hart0
+ */
+ if (!sbi_init_count(hartindex) || (is_andes(25) && hartid == 0))
+ return sbi_ipi_raw_send(hartindex, false);
+
+ atcsmu_set_command(WAKEUP_CMD, hartid);
+ return 0;
+}
+
+static int ae350_hart_stop(void)
+{
+ u32 hartid = current_hartid();
+ u32 sleep_type = atcsmu_get_sleep_type(hartid);
+ int rc;
+
+ /*
+ * For Andes AX25MP, the hart0 shares power domain with the last level
+ * cache. Instead of turning it off, it should fall through and jump to
+ * warmboot_addr.
+ */
+ if (is_andes(25) && hartid == 0)
+ return SBI_ENOTSUPP;
+
+ if (!atcsmu_support_sleep_mode(sleep_type, hartid))
+ return SBI_ENOTSUPP;
+
+ /* Prevent the core leaving the WFI mode unexpectedly */
+ csr_write(CSR_MIE, 0);
+
+ atcsmu_set_wakeup_events(0x0, hartid);
+ atcsmu_set_command(DEEP_SLEEP_CMD, hartid);
+ rc = atcsmu_set_reset_vector((u64)ae350_enable_coherency_warmboot, hartid);
+ if (rc)
+ return SBI_EFAIL;
+
+ ae350_disable_coherency();
+ wfi();
+ return 0;
+}
+
+static const struct sbi_hsm_device hsm_andes_atcsmu = {
+ .name = "andes_atcsmu",
+ .hart_start = ae350_hart_start,
+ .hart_stop = ae350_hart_stop,
+};
+
+static int hsm_andes_atcsmu_probe(const void *fdt, int nodeoff, const struct fdt_match *match)
+{
+ int poff;
+ int rc;
+
+ /* Need to find the parent for the address property */
+ poff = fdt_parent_offset(fdt, nodeoff);
+ if (poff < 0)
+ return SBI_EINVAL;
+
+ rc = fdt_get_node_addr_size(fdt, poff, 0, &atcsmu_base, NULL);
+ if (rc < 0 || !atcsmu_base)
+ return SBI_ENODEV;
+
+ sbi_hsm_set_device(&hsm_andes_atcsmu);
+ return 0;
+}
+
+static const struct fdt_match hsm_andes_atcsmu_match[] = {
+ { .compatible = "andestech,atcsmu-hsm" },
+ { },
+};
+
+const struct fdt_driver fdt_hsm_andes_atcsmu = {
+ .match_table = hsm_andes_atcsmu_match,
+ .init = hsm_andes_atcsmu_probe,
+};
diff --git a/lib/utils/hsm/objects.mk b/lib/utils/hsm/objects.mk
index 0d0054494f18..f76af03c5c0a 100644
--- a/lib/utils/hsm/objects.mk
+++ b/lib/utils/hsm/objects.mk
@@ -7,6 +7,9 @@
# Anup Patel <apatel@ventanamicro.com>
#
+carray-fdt_early_drivers-$(CONFIG_FDT_HSM_ANDES_ATCSMU) += fdt_hsm_andes_atcsmu
+libsbiutils-objs-$(CONFIG_FDT_HSM_ANDES_ATCSMU) += hsm/fdt_hsm_andes_atcsmu.o
+
carray-fdt_early_drivers-$(CONFIG_FDT_HSM_RPMI) += fdt_hsm_rpmi
libsbiutils-objs-$(CONFIG_FDT_HSM_RPMI) += hsm/fdt_hsm_rpmi.o
@@ -14,4 +17,4 @@ carray-fdt_early_drivers-$(CONFIG_FDT_HSM_SPACEMIT) += fdt_hsm_spacemit
libsbiutils-objs-$(CONFIG_FDT_HSM_SPACEMIT) += hsm/fdt_hsm_spacemit.o
carray-fdt_early_drivers-$(CONFIG_FDT_HSM_SIFIVE_TMC0) += fdt_hsm_sifive_tmc0
-libsbiutils-objs-$(CONFIG_FDT_HSM_SIFIVE_TMC0) += hsm/fdt_hsm_sifive_tmc0.o
\ No newline at end of file
+libsbiutils-objs-$(CONFIG_FDT_HSM_SIFIVE_TMC0) += hsm/fdt_hsm_sifive_tmc0.o
diff --git a/lib/utils/reset/Kconfig b/lib/utils/reset/Kconfig
index 68e667163b5d..4835921f4b90 100644
--- a/lib/utils/reset/Kconfig
+++ b/lib/utils/reset/Kconfig
@@ -11,7 +11,7 @@ if FDT_RESET
config FDT_RESET_ATCWDT200
bool "Andes WDT FDT reset driver"
- depends on SYS_ATCSMU
+ depends on FDT_HSM_ANDES_ATCSMU
default n
config FDT_RESET_GPIO
diff --git a/lib/utils/reset/fdt_reset_atcwdt200.c b/lib/utils/reset/fdt_reset_atcwdt200.c
index 2304582a9513..5dad8ac96d48 100644
--- a/lib/utils/reset/fdt_reset_atcwdt200.c
+++ b/lib/utils/reset/fdt_reset_atcwdt200.c
@@ -1,10 +1,7 @@
/*
* SPDX-License-Identifier: BSD-2-Clause
*
- * Copyright (c) 2022 Andes Technology Corporation
- *
- * Authors:
- * Yu Chien Peter Lin <peterlin@andestech.com>
+ * Copyright (c) 2025 Andes Technology Corporation
*/
#include <libfdt.h>
@@ -15,7 +12,7 @@
#include <sbi/sbi_system.h>
#include <sbi_utils/fdt/fdt_driver.h>
#include <sbi_utils/fdt/fdt_helper.h>
-#include <sbi_utils/sys/atcsmu.h>
+#include <sbi_utils/hsm/fdt_hsm_andes_atcsmu.h>
#define ATCWDT200_WP_NUM 0x5aa5
#define WREN_REG 0x18
@@ -41,8 +38,9 @@
#define CLK_PCLK (1 << 1)
#define WDT_EN (1 << 0)
+#define AE350_FLASH_BASE 0x80000000
+
static volatile char *wdt_addr = NULL;
-static struct smu_data smu = { 0 };
static int ae350_system_reset_check(u32 type, u32 reason)
{
@@ -59,7 +57,7 @@ static int ae350_system_reset_check(u32 type, u32 reason)
static void ae350_system_reset(u32 type, u32 reason)
{
sbi_for_each_hartindex(i)
- if (smu_set_reset_vector(&smu, FLASH_BASE, i))
+ if (atcsmu_set_reset_vector(AE350_FLASH_BASE, i))
goto fail;
/* Program WDT control register */
@@ -88,16 +86,6 @@ static int atcwdt200_reset_init(const void *fdt, int nodeoff,
return SBI_ENODEV;
wdt_addr = (volatile char *)(unsigned long)reg_addr;
-
- /*
- * The reset device requires smu to program the reset
- * vector for each hart.
- */
- if (fdt_parse_compat_addr(fdt, ®_addr, "andestech,atcsmu"))
- return SBI_ENODEV;
-
- smu.addr = (unsigned long)reg_addr;
-
sbi_system_reset_add_device(&atcwdt200_reset);
return 0;
diff --git a/lib/utils/sys/Kconfig b/lib/utils/sys/Kconfig
index a22191cd7c26..fc388665524d 100644
--- a/lib/utils/sys/Kconfig
+++ b/lib/utils/sys/Kconfig
@@ -2,10 +2,6 @@
menu "System Device Support"
-config SYS_ATCSMU
- bool "Andes System Management Unit (SMU) support"
- default n
-
config SYS_HTIF
bool "Host transfere interface (HTIF) support"
default n
diff --git a/lib/utils/sys/atcsmu.c b/lib/utils/sys/atcsmu.c
deleted file mode 100644
index 2cba0eb73e7e..000000000000
--- a/lib/utils/sys/atcsmu.c
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * SPDX-License-Identifier: BSD-3-Clause
- *
- * Copyright (c) 2023 Andes Technology Corporation
- *
- * Authors:
- * Yu Chien Peter Lin <peterlin@andestech.com>
- */
-
-#include <sbi_utils/sys/atcsmu.h>
-#include <sbi/riscv_io.h>
-#include <sbi/sbi_console.h>
-#include <sbi/sbi_error.h>
-#include <sbi/sbi_bitops.h>
-
-inline int smu_set_wakeup_events(struct smu_data *smu, u32 events, u32 hartid)
-{
- if (smu) {
- writel(events, (void *)(smu->addr + PCSm_WE_OFFSET(hartid)));
- return 0;
- } else
- return SBI_EINVAL;
-}
-
-inline bool smu_support_sleep_mode(struct smu_data *smu, u32 sleep_mode,
- u32 hartid)
-{
- u32 pcs_cfg;
-
- if (!smu) {
- sbi_printf("%s(): Failed to access smu_data\n", __func__);
- return false;
- }
-
- 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;
- }
- break;
- 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;
- }
- break;
- }
-
- return true;
-}
-
-inline int smu_set_command(struct smu_data *smu, u32 pcs_ctl, u32 hartid)
-{
- if (smu) {
- writel(pcs_ctl, (void *)(smu->addr + PCSm_CTL_OFFSET(hartid)));
- return 0;
- } else
- return SBI_EINVAL;
-}
-
-inline int smu_set_reset_vector(struct smu_data *smu, ulong wakeup_addr,
- u32 hartid)
-{
- u32 vec_lo, vec_hi;
- u64 reset_vector;
-
- if (!smu)
- return SBI_EINVAL;
-
- writel(wakeup_addr, (void *)(smu->addr + HARTn_RESET_VEC_LO(hartid)));
- writel((u64)wakeup_addr >> 32,
- (void *)(smu->addr + HARTn_RESET_VEC_HI(hartid)));
-
- vec_lo = readl((void *)(smu->addr + HARTn_RESET_VEC_LO(hartid)));
- vec_hi = readl((void *)(smu->addr + HARTn_RESET_VEC_HI(hartid)));
- reset_vector = ((u64)vec_hi << 32) | vec_lo;
-
- if (reset_vector != (u64)wakeup_addr) {
- sbi_printf("hart%d (PCS%d): Failed to program the reset vector.\n",
- hartid, hartid + 3);
- return SBI_EFAIL;
- } else
- return 0;
-}
diff --git a/lib/utils/sys/objects.mk b/lib/utils/sys/objects.mk
index 409d7e8c7ef6..d9c67077a3a6 100644
--- a/lib/utils/sys/objects.mk
+++ b/lib/utils/sys/objects.mk
@@ -8,4 +8,3 @@
#
libsbiutils-objs-$(CONFIG_SYS_HTIF) += sys/htif.o
-libsbiutils-objs-$(CONFIG_SYS_ATCSMU) += sys/atcsmu.o
diff --git a/platform/generic/Kconfig b/platform/generic/Kconfig
index 716fab42a7b7..0c11fbd2c29f 100644
--- a/platform/generic/Kconfig
+++ b/platform/generic/Kconfig
@@ -36,7 +36,6 @@ config PLATFORM_ALLWINNER_D1
config PLATFORM_ANDES_AE350
bool "Andes AE350 support"
- select SYS_ATCSMU
select ANDES_PMU
select ANDES_PMA
default n
diff --git a/platform/generic/andes/ae350.c b/platform/generic/andes/ae350.c
index 0808065745af..fa3f03685bba 100644
--- a/platform/generic/andes/ae350.c
+++ b/platform/generic/andes/ae350.c
@@ -1,121 +1,25 @@
/*
* SPDX-License-Identifier: BSD-2-Clause
*
- * Copyright (c) 2022 Andes Technology Corporation
- *
- * Authors:
- * Yu Chien Peter Lin <peterlin@andestech.com>
+ * Copyright (c) 2025 Andes Technology Corporation
*/
-#include <platform_override.h>
-#include <andes/andes_pmu.h>
-#include <sbi_utils/fdt/fdt_helper.h>
-#include <sbi_utils/fdt/fdt_fixup.h>
-#include <sbi_utils/sys/atcsmu.h>
-#include <sbi/riscv_asm.h>
-#include <sbi/sbi_bitops.h>
-#include <sbi/sbi_error.h>
-#include <sbi/sbi_hsm.h>
-#include <sbi/sbi_ipi.h>
-#include <sbi/sbi_init.h>
#include <andes/andes.h>
+#include <andes/andes_pmu.h>
#include <andes/andes_sbi.h>
+#include <platform_override.h>
+#include <sbi_utils/fdt/fdt_helper.h>
-static struct smu_data smu = { 0 };
-extern void __ae350_enable_coherency_warmboot(void);
-extern void __ae350_disable_coherency(void);
-
-static int ae350_hart_start(u32 hartid, ulong saddr)
-{
- u32 hartindex = sbi_hartid_to_hartindex(hartid);
-
- /*
- * Don't send wakeup command when:
- * 1) boot-time
- * 2) the target hart is non-sleepable 25-series hart0
- */
- if (!sbi_init_count(hartindex) || (is_andes(25) && hartid == 0))
- return sbi_ipi_raw_send(hartindex, false);
-
- /* Write wakeup command to the sleep hart */
- smu_set_command(&smu, WAKEUP_CMD, hartid);
-
- return 0;
-}
-
-static int ae350_hart_stop(void)
-{
- int rc;
- u32 hartid = current_hartid();
-
- /**
- * For Andes AX25MP, the hart0 shares power domain with
- * L2-cache, instead of turning it off, it should fall
- * through and jump to warmboot_addr.
- */
- if (is_andes(25) && hartid == 0)
- return SBI_ENOTSUPP;
-
- if (!smu_support_sleep_mode(&smu, 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(&smu, 0x0, hartid);
- smu_set_command(&smu, DEEP_SLEEP_CMD, hartid);
-
- rc = smu_set_reset_vector(&smu,
- (ulong)__ae350_enable_coherency_warmboot,
- hartid);
- if (rc)
- goto fail;
-
- __ae350_disable_coherency();
-
- wfi();
-
-fail:
- /* 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(const void *fdt)
-{
- int rc;
-
- rc = fdt_parse_compat_addr(fdt, (uint64_t *)&smu.addr,
- "andestech,atcsmu");
-
- if (!rc) {
- sbi_hsm_set_device(&andes_smu);
- }
-}
+extern void _start_warm(void);
-static int ae350_final_init(bool cold_boot)
+void ae350_enable_coherency_warmboot(void)
{
- if (cold_boot) {
- const void *fdt = fdt_get_address();
-
- ae350_hsm_device_init(fdt);
- }
-
- return generic_final_init(cold_boot);
+ ae350_enable_coherency();
+ _start_warm();
}
static int ae350_platform_init(const void *fdt, int nodeoff, const struct fdt_match *match)
{
- generic_platform_ops.final_init = ae350_final_init;
generic_platform_ops.extensions_init = andes_pmu_extensions_init;
generic_platform_ops.pmu_init = andes_pmu_init;
generic_platform_ops.vendor_ext_provider = andes_sbi_vendor_ext_provider;
diff --git a/platform/generic/andes/objects.mk b/platform/generic/andes/objects.mk
index f85ad48162b2..660546c312fe 100644
--- a/platform/generic/andes/objects.mk
+++ b/platform/generic/andes/objects.mk
@@ -3,7 +3,7 @@
#
carray-platform_override_modules-$(CONFIG_PLATFORM_ANDES_AE350) += andes_ae350
-platform-objs-$(CONFIG_PLATFORM_ANDES_AE350) += andes/ae350.o andes/sleep.o
+platform-objs-$(CONFIG_PLATFORM_ANDES_AE350) += andes/ae350.o
carray-platform_override_modules-$(CONFIG_PLATFORM_ANDES_QILAI) += andes_qilai
platform-objs-$(CONFIG_PLATFORM_ANDES_QILAI) += andes/qilai.o
diff --git a/platform/generic/andes/sleep.S b/platform/generic/andes/sleep.S
deleted file mode 100644
index 361aff3a6024..000000000000
--- a/platform/generic/andes/sleep.S
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * SPDX-License-Identifier: BSD-2-Clause
- *
- * Copyright (c) 2023 Andes Technology Corporation
- *
- * Authors:
- * Yu Chien Peter Lin <peterlin@andestech.com>
- */
-
-#include <sbi/riscv_encoding.h>
-#include <sbi/riscv_asm.h>
-#include <andes/andes.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_MCACHE_CTL, 0x3
- /* disable d-cache coherency */
- lui t1, 0x80
- csrc CSR_MCACHE_CTL, t1
- /*
- * wait for mcache_ctl.DC_COHSTA to be cleared,
- * the bit is hard-wired 0 on platforms w/o CM
- * (Coherence Manager)
- */
-check_cm_disabled:
- csrr t1, CSR_MCACHE_CTL
- 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_MCACHE_CTL, t1
- /*
- * mcache_ctl.DC_COHEN is hard-wired 0 on platforms
- * w/o CM support
- */
- csrr t1, CSR_MCACHE_CTL
- srli t1, t1, 19
- andi t1, t1, 0x1
- beqz t1, enable_L1_cache
- /* wait for mcache_ctl.DC_COHSTA to be set */
-check_cm_enabled:
- csrr t1, CSR_MCACHE_CTL
- srli t1, t1, 20
- andi t1, t1, 0x1
- beqz t1, check_cm_enabled
-enable_L1_cache:
- /* enable i/d-cache */
- csrs CSR_MCACHE_CTL, 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/configs/defconfig b/platform/generic/configs/defconfig
index aab1560f2399..d85da930edbc 100644
--- a/platform/generic/configs/defconfig
+++ b/platform/generic/configs/defconfig
@@ -23,6 +23,7 @@ CONFIG_FDT_GPIO_DESIGNWARE=y
CONFIG_FDT_GPIO_SIFIVE=y
CONFIG_FDT_GPIO_STARFIVE=y
CONFIG_FDT_HSM=y
+CONFIG_FDT_HSM_ANDES_ATCSMU=y
CONFIG_FDT_HSM_RPMI=y
CONFIG_FDT_HSM_SIFIVE_TMC0=y
CONFIG_FDT_I2C=y
diff --git a/platform/generic/include/andes/andes.h b/platform/generic/include/andes/andes.h
index bfedf034c902..1b5893925c10 100644
--- a/platform/generic/include/andes/andes.h
+++ b/platform/generic/include/andes/andes.h
@@ -6,6 +6,9 @@
#ifndef _RISCV_ANDES_H
#define _RISCV_ANDES_H
+#include <sbi/sbi_bitops.h>
+#include <sbi/sbi_scratch.h>
+
/* Memory and Miscellaneous Registers */
#define CSR_MCACHE_CTL 0x7ca
#define CSR_MCCTLCOMMAND 0x7cc
@@ -43,13 +46,23 @@
#define MMSC_IOCP_OFFSET 47
#define MMSC_IOCP_MASK (1ULL << MMSC_IOCP_OFFSET)
+#define MCACHE_CTL_IC_EN_MASK BIT(0)
+#define MCACHE_CTL_DC_EN_MASK BIT(1)
#define MCACHE_CTL_CCTL_SUEN_OFFSET 8
#define MCACHE_CTL_CCTL_SUEN_MASK (1 << MCACHE_CTL_CCTL_SUEN_OFFSET)
+#define MCACHE_CTL_DC_COHEN_MASK BIT(19)
+#define MCACHE_CTL_DC_COHSTA_MASK BIT(20)
/* Performance monitor */
#define MMSC_CFG_PMNDS_MASK (1 << 15)
#define MIP_PMOVI (1 << 18)
+/* Cache control commands */
+#define MCCTLCOMMAND_L1D_WBINVAL_ALL 6
+
+/* AE350 platform specific sleep types */
+#define SBI_SUSP_AE350_LIGHT_SLEEP SBI_SUSP_PLATFORM_SLEEP_START
+
#ifndef __ASSEMBLER__
#define is_andes(series) \
@@ -67,4 +80,53 @@
#endif /* __ASSEMBLER__ */
+void ae350_enable_coherency_warmboot(void);
+
+/*
+ * On Andes 4X-series CPUs, disabling the L1 data cache causes the CPU to fetch
+ * data directly from RAM. However, L1 cache flushes write data back to the
+ * Last Level Cache (LLC). This discrepancy can lead to return address
+ * corruption on the stack. To prevent this, the following functions must
+ * be inlined.
+ */
+static inline void ae350_disable_coherency(void)
+{
+ /*
+ * To disable cache coherency of a core in AE350 platform, follow below steps:
+ *
+ * 1) Disable I/D-Cache
+ * 2) Write back and invalidate D-Cache
+ * 3) Disable D-Cache coherency
+ * 4) Wait for D-Cache disengaged from the coherence management
+ */
+ csr_clear(CSR_MCACHE_CTL, MCACHE_CTL_IC_EN_MASK | MCACHE_CTL_DC_EN_MASK);
+ csr_write(CSR_MCCTLCOMMAND, MCCTLCOMMAND_L1D_WBINVAL_ALL);
+ csr_clear(CSR_MCACHE_CTL, MCACHE_CTL_DC_COHEN_MASK);
+ while (csr_read(CSR_MCACHE_CTL) & MCACHE_CTL_DC_COHSTA_MASK)
+ ;
+}
+
+static inline void ae350_enable_coherency(void)
+{
+ /*
+ * To enable cache coherency of a core in AE350 platform, follow below steps:
+ *
+ * 1) Enable D-Cache coherency
+ * 2) Wait for D-Cache engaging in the coherence management
+ * 3) Enable I/D-Cache
+ */
+ csr_set(CSR_MCACHE_CTL, MCACHE_CTL_DC_COHEN_MASK);
+
+ /*
+ * mcache_ctl.DC_COHEN is hardwired to 0 if there is no coherence
+ * manager. In such situation, just enable the I/D-Cache to prevent
+ * permanently being stuck in the while loop.
+ */
+ if (csr_read(CSR_MCACHE_CTL) & MCACHE_CTL_DC_COHEN_MASK)
+ while (!(csr_read(CSR_MCACHE_CTL) & MCACHE_CTL_DC_COHSTA_MASK))
+ ;
+
+ csr_set(CSR_MCACHE_CTL, MCACHE_CTL_IC_EN_MASK | MCACHE_CTL_DC_EN_MASK);
+}
+
#endif /* _RISCV_ANDES_H */
--
2.34.1
--
opensbi mailing list
opensbi@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/opensbi
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH 2/5] platform: generic/andes: add CSR save and restore functions for AE350 platform
2025-12-29 7:19 [PATCH 0/5] Add hart state management and system suspend support for AE350 Ben Zong-You Xie
2025-12-29 7:19 ` [PATCH 1/5] lib: utils/hsm: factor out ATCSMU code into an HSM driver Ben Zong-You Xie
@ 2025-12-29 7:19 ` Ben Zong-You Xie
2025-12-29 7:19 ` [PATCH 3/5] lib: utils/cache: add cache enable function Ben Zong-You Xie
` (3 subsequent siblings)
5 siblings, 0 replies; 9+ messages in thread
From: Ben Zong-You Xie @ 2025-12-29 7:19 UTC (permalink / raw)
To: opensbi; +Cc: Leo Yu-Chi Liang
Implement a save and restore mechanism for Andes-specific CSRs to support
hardware power-saving modes, such as CPU hotplug or suspend to RAM.
Signed-off-by: Ben Zong-You Xie <ben717@andestech.com>
Signed-off-by: Leo Yu-Chi Liang <ycliang@andestech.com>
---
lib/utils/hsm/fdt_hsm_andes_atcsmu.c | 1 +
platform/generic/andes/ae350.c | 55 ++++++++++++++++++++++++++
platform/generic/include/andes/andes.h | 22 ++++++++++-
3 files changed, 77 insertions(+), 1 deletion(-)
diff --git a/lib/utils/hsm/fdt_hsm_andes_atcsmu.c b/lib/utils/hsm/fdt_hsm_andes_atcsmu.c
index ee7873dc6d00..3fe931aa1816 100644
--- a/lib/utils/hsm/fdt_hsm_andes_atcsmu.c
+++ b/lib/utils/hsm/fdt_hsm_andes_atcsmu.c
@@ -126,6 +126,7 @@ static int ae350_hart_stop(void)
if (rc)
return SBI_EFAIL;
+ ae350_non_ret_save(sbi_scratch_thishart_ptr());
ae350_disable_coherency();
wfi();
return 0;
diff --git a/platform/generic/andes/ae350.c b/platform/generic/andes/ae350.c
index fa3f03685bba..9bd1554394b7 100644
--- a/platform/generic/andes/ae350.c
+++ b/platform/generic/andes/ae350.c
@@ -8,18 +8,73 @@
#include <andes/andes_pmu.h>
#include <andes/andes_sbi.h>
#include <platform_override.h>
+#include <sbi/sbi_init.h>
+#include <sbi/sbi_scratch.h>
#include <sbi_utils/fdt/fdt_helper.h>
+static unsigned long andes_hart_data_offset;
extern void _start_warm(void);
+void ae350_non_ret_save(struct sbi_scratch *scratch)
+{
+ struct andes_hart_data *andes_hdata = sbi_scratch_offset_ptr(scratch,
+ andes_hart_data_offset);
+
+ andes_hdata->mcache_ctl = csr_read(CSR_MCACHE_CTL);
+ andes_hdata->mmisc_ctl = csr_read(CSR_MMISC_CTL);
+ andes_hdata->mpft_ctl = csr_read(CSR_MPFT_CTL);
+ andes_hdata->mslideleg = csr_read(CSR_MSLIDELEG);
+ andes_hdata->mxstatus = csr_read(CSR_MXSTATUS);
+ andes_hdata->slie = csr_read(CSR_SLIE);
+ andes_hdata->slip = csr_read(CSR_SLIP);
+ andes_hdata->pmacfg0 = csr_read(CSR_PMACFG0);
+ andes_hdata->pmacfg2 = csr_read_num(CSR_PMACFG0 + 2);
+ for (int i = 0; i < 16; i++)
+ andes_hdata->pmaaddrX[i] = csr_read_num(CSR_PMAADDR0 + i);
+}
+
+void ae350_non_ret_restore(struct sbi_scratch *scratch)
+{
+ struct andes_hart_data *andes_hdata = sbi_scratch_offset_ptr(scratch,
+ andes_hart_data_offset);
+
+ csr_write(CSR_MCACHE_CTL, andes_hdata->mcache_ctl);
+ csr_write(CSR_MMISC_CTL, andes_hdata->mmisc_ctl);
+ csr_write(CSR_MPFT_CTL, andes_hdata->mpft_ctl);
+ csr_write(CSR_MSLIDELEG, andes_hdata->mslideleg);
+ csr_write(CSR_MXSTATUS, andes_hdata->mxstatus);
+ csr_write(CSR_SLIE, andes_hdata->slie);
+ csr_write(CSR_SLIP, andes_hdata->slip);
+ csr_write(CSR_PMACFG0, andes_hdata->pmacfg0);
+ csr_write_num(CSR_PMACFG0 + 2, andes_hdata->pmacfg2);
+ for (int i = 0; i < 16; i++)
+ csr_write_num(CSR_PMAADDR0 + i, andes_hdata->pmaaddrX[i]);
+}
+
void ae350_enable_coherency_warmboot(void)
{
ae350_enable_coherency();
_start_warm();
}
+static int ae350_early_init(bool cold_boot)
+{
+ if (cold_boot) {
+ andes_hart_data_offset = sbi_scratch_alloc_offset(sizeof(struct andes_hart_data));
+ if (!andes_hart_data_offset)
+ return SBI_ENOMEM;
+ }
+
+ /* Don't restore Andes CSRs during boot */
+ if (sbi_init_count(current_hartindex()))
+ ae350_non_ret_restore(sbi_scratch_thishart_ptr());
+
+ return generic_early_init(cold_boot);
+}
+
static int ae350_platform_init(const void *fdt, int nodeoff, const struct fdt_match *match)
{
+ generic_platform_ops.early_init = ae350_early_init;
generic_platform_ops.extensions_init = andes_pmu_extensions_init;
generic_platform_ops.pmu_init = andes_pmu_init;
generic_platform_ops.vendor_ext_provider = andes_sbi_vendor_ext_provider;
diff --git a/platform/generic/include/andes/andes.h b/platform/generic/include/andes/andes.h
index 1b5893925c10..dd2451718d00 100644
--- a/platform/generic/include/andes/andes.h
+++ b/platform/generic/include/andes/andes.h
@@ -10,16 +10,21 @@
#include <sbi/sbi_scratch.h>
/* Memory and Miscellaneous Registers */
+#define CSR_MPFT_CTL 0x7c5
#define CSR_MCACHE_CTL 0x7ca
#define CSR_MCCTLCOMMAND 0x7cc
+#define CSR_MMISC_CTL 0x7d0
/* Configuration Control & Status Registers */
#define CSR_MICM_CFG 0xfc0
#define CSR_MDCM_CFG 0xfc1
#define CSR_MMSC_CFG 0xfc2
-/* Machine Trap Related Registers */
+/* Trap Related Registers */
+#define CSR_MXSTATUS 0x7c4
#define CSR_MSLIDELEG 0x7d5
+#define CSR_SLIE 0x9c4
+#define CSR_SLIP 0x9c5
/* Counter Related Registers */
#define CSR_MCOUNTERWEN 0x7ce
@@ -80,6 +85,21 @@
#endif /* __ASSEMBLER__ */
+struct andes_hart_data {
+ unsigned long mcache_ctl;
+ unsigned long mmisc_ctl;
+ unsigned long mpft_ctl;
+ unsigned long mslideleg;
+ unsigned long mxstatus;
+ unsigned long slie;
+ unsigned long slip;
+ unsigned long pmacfg0;
+ unsigned long pmacfg2;
+ unsigned long pmaaddrX[16];
+};
+
+void ae350_non_ret_save(struct sbi_scratch *scratch);
+void ae350_non_ret_restore(struct sbi_scratch *scratch);
void ae350_enable_coherency_warmboot(void);
/*
--
2.34.1
--
opensbi mailing list
opensbi@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/opensbi
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH 3/5] lib: utils/cache: add cache enable function
2025-12-29 7:19 [PATCH 0/5] Add hart state management and system suspend support for AE350 Ben Zong-You Xie
2025-12-29 7:19 ` [PATCH 1/5] lib: utils/hsm: factor out ATCSMU code into an HSM driver Ben Zong-You Xie
2025-12-29 7:19 ` [PATCH 2/5] platform: generic/andes: add CSR save and restore functions for AE350 platform Ben Zong-You Xie
@ 2025-12-29 7:19 ` Ben Zong-You Xie
2025-12-29 7:19 ` [PATCH 4/5] lib: utils/cache: add Andes last level cache controller Ben Zong-You Xie
` (2 subsequent siblings)
5 siblings, 0 replies; 9+ messages in thread
From: Ben Zong-You Xie @ 2025-12-29 7:19 UTC (permalink / raw)
To: opensbi
Add functions to enable/disable the cache.
Signed-off-by: Ben Zong-You Xie <ben717@andestech.com>
---
include/sbi_utils/cache/cache.h | 11 +++++++++++
include/sbi_utils/cache/fdt_cmo_helper.h | 13 +++++++++++++
lib/utils/cache/cache.c | 11 +++++++++++
lib/utils/cache/fdt_cmo_helper.c | 23 +++++++++++++++++++++++
4 files changed, 58 insertions(+)
diff --git a/include/sbi_utils/cache/cache.h b/include/sbi_utils/cache/cache.h
index 70d9286f9a0a..de6cf50d1da6 100644
--- a/include/sbi_utils/cache/cache.h
+++ b/include/sbi_utils/cache/cache.h
@@ -19,6 +19,8 @@ struct cache_ops {
int (*warm_init)(struct cache_device *dev);
/** Flush entire cache **/
int (*cache_flush_all)(struct cache_device *dev);
+ /** Enable/Disable cache **/
+ int (*cache_enable)(struct cache_device *dev, bool enable);
};
struct cache_device {
@@ -66,4 +68,13 @@ int cache_add(struct cache_device *dev);
*/
int cache_flush_all(struct cache_device *dev);
+/**
+ * Enable/Disable the cache
+ *
+ * @param dev the cache to enable/disable
+ *
+ * @return 0 on success, or a negative error code on failure
+ */
+int cache_enable(struct cache_device *dev, bool enable);
+
#endif
diff --git a/include/sbi_utils/cache/fdt_cmo_helper.h b/include/sbi_utils/cache/fdt_cmo_helper.h
index a6a28db9ede2..8b3ec89b9c15 100644
--- a/include/sbi_utils/cache/fdt_cmo_helper.h
+++ b/include/sbi_utils/cache/fdt_cmo_helper.h
@@ -22,6 +22,19 @@ int fdt_cmo_private_flc_flush_all(void);
*/
int fdt_cmo_llc_flush_all(void);
+/**
+ * Enable/Disable the private first level cache of the current hart
+ *
+ * @return 0 on success, or a negative error code on failure
+ */
+int fdt_cmo_private_flc_enable(bool enable);
+
+/**
+ * Enable/Disable the last level cache of the current hart
+ *
+ * @return 0 on success, or a negative error code on failure
+ */
+int fdt_cmo_llc_enable(bool enable);
/**
* Initialize the cache devices for each hart
*
diff --git a/lib/utils/cache/cache.c b/lib/utils/cache/cache.c
index 6bc3d10e9347..2810d5e9570c 100644
--- a/lib/utils/cache/cache.c
+++ b/lib/utils/cache/cache.c
@@ -44,3 +44,14 @@ int cache_flush_all(struct cache_device *dev)
return dev->ops->cache_flush_all(dev);
}
+
+int cache_enable(struct cache_device *dev, bool enable)
+{
+ if (!dev)
+ return SBI_ENODEV;
+
+ if (!dev->ops || !dev->ops->cache_enable)
+ return SBI_ENOTSUPP;
+
+ return dev->ops->cache_enable(dev, enable);
+}
diff --git a/lib/utils/cache/fdt_cmo_helper.c b/lib/utils/cache/fdt_cmo_helper.c
index d87bab76ca00..3ab8abe667ba 100644
--- a/lib/utils/cache/fdt_cmo_helper.c
+++ b/lib/utils/cache/fdt_cmo_helper.c
@@ -41,6 +41,29 @@ int fdt_cmo_llc_flush_all(void)
return cache_flush_all(llc);
}
+int fdt_cmo_private_flc_enable(bool enable)
+{
+ struct cache_device *flc = get_hart_flc(sbi_scratch_thishart_ptr());
+
+ if (!flc || !flc->cpu_private)
+ return SBI_ENODEV;
+
+ return cache_enable(flc, enable);
+}
+
+int fdt_cmo_llc_enable(bool enable)
+{
+ struct cache_device *llc = get_hart_flc(sbi_scratch_thishart_ptr());
+
+ if (!llc)
+ return SBI_ENODEV;
+
+ while (llc->next)
+ llc = llc->next;
+
+ return cache_enable(llc, enable);
+}
+
static int fdt_cmo_cold_init(const void *fdt)
{
struct sbi_scratch *scratch;
--
2.34.1
--
opensbi mailing list
opensbi@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/opensbi
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH 4/5] lib: utils/cache: add Andes last level cache controller
2025-12-29 7:19 [PATCH 0/5] Add hart state management and system suspend support for AE350 Ben Zong-You Xie
` (2 preceding siblings ...)
2025-12-29 7:19 ` [PATCH 3/5] lib: utils/cache: add cache enable function Ben Zong-You Xie
@ 2025-12-29 7:19 ` Ben Zong-You Xie
2025-12-29 7:19 ` [PATCH 5/5] lib: utils/suspend: add Andes ATCSMU suspend driver Ben Zong-You Xie
2026-02-11 6:24 ` [PATCH 0/5] Add hart state management and system suspend support for AE350 Anup Patel
5 siblings, 0 replies; 9+ messages in thread
From: Ben Zong-You Xie @ 2025-12-29 7:19 UTC (permalink / raw)
To: opensbi
Introduce a FDT-based driver for the Andes Last Level Cache (LLC)
controller to support cache maintenance operations.
Signed-off-by: Ben Zong-You Xie <ben717@andestech.com>
---
include/sbi_utils/hsm/fdt_hsm_andes_atcsmu.h | 15 ++
lib/utils/cache/Kconfig | 5 +
lib/utils/cache/fdt_andes_llcache.c | 166 +++++++++++++++++++
lib/utils/cache/objects.mk | 3 +
lib/utils/hsm/fdt_hsm_andes_atcsmu.c | 10 ++
platform/generic/configs/defconfig | 1 +
6 files changed, 200 insertions(+)
create mode 100644 lib/utils/cache/fdt_andes_llcache.c
diff --git a/include/sbi_utils/hsm/fdt_hsm_andes_atcsmu.h b/include/sbi_utils/hsm/fdt_hsm_andes_atcsmu.h
index 881d8b215060..ef851b7501b5 100644
--- a/include/sbi_utils/hsm/fdt_hsm_andes_atcsmu.h
+++ b/include/sbi_utils/hsm/fdt_hsm_andes_atcsmu.h
@@ -11,6 +11,8 @@
/* clang-format off */
+#define SCRATCH_PAD_OFFSET 0x40
+
#define RESET_VEC_LO_OFFSET 0x50
#define RESET_VEC_HI_OFFSET 0x60
#define RESET_VEC_8CORE_OFFSET 0x1a0
@@ -31,6 +33,9 @@
#define PCS0_WE_OFFSET 0x90
#define PCSm_WE_OFFSET(i) ((i + 3) * 0x20 + PCS0_WE_OFFSET)
+#define PCS_WAKEUP_RTC_ALARM_MASK BIT(2)
+#define PCS_WAKEUP_UART2_MASK BIT(9)
+#define PCS_WAKEUP_MSIP_MASK BIT(29)
#define PCS0_CTL_OFFSET 0x94
#define PCSm_CTL_OFFSET(i) ((i + 3) * 0x20 + PCS0_CTL_OFFSET)
@@ -38,6 +43,14 @@
#define WAKEUP_CMD 0x8
#define DEEP_SLEEP_CMD 0xb
+#define PCS0_STATUS_OFFSET 0x98
+#define PCSm_STATUS_OFFSET(i) ((i + 3) * 0x20 + PCS0_STATUS_OFFSET)
+#define PD_TYPE_MASK GENMASK(2, 0)
+#define PD_TYPE_SLEEP 2
+#define PD_STATUS_MASK GENMASK(7, 3)
+#define PD_STATUS_LIGHT_SLEEP 0
+#define PD_STATUS_DEEP_SLEEP 0x10
+
/* clang-format on */
void atcsmu_set_wakeup_events(u32 events, u32 hartid);
@@ -45,5 +58,7 @@ bool atcsmu_support_sleep_mode(u32 sleep_type, u32 hartid);
void atcsmu_set_command(u32 pcs_ctl, u32 hartid);
int atcsmu_set_reset_vector(u64 wakeup_addr, u32 hartid);
u32 atcsmu_get_sleep_type(u32 hartid);
+void atcsmu_write_scratch(u32 value);
+u32 atcsmu_read_scratch(void);
#endif
diff --git a/lib/utils/cache/Kconfig b/lib/utils/cache/Kconfig
index be7d57c37d24..ea815dc4ef03 100644
--- a/lib/utils/cache/Kconfig
+++ b/lib/utils/cache/Kconfig
@@ -10,6 +10,11 @@ config FDT_CACHE
if FDT_CACHE
+config FDT_CACHE_ANDES_LLCACHE
+ bool "Andes FDT last level cache driver"
+ depends on FDT_HSM_ANDES_ATCSMU
+ default n
+
config FDT_CACHE_SIFIVE_CCACHE
bool "SiFive CCACHE FDT cache driver"
default n
diff --git a/lib/utils/cache/fdt_andes_llcache.c b/lib/utils/cache/fdt_andes_llcache.c
new file mode 100644
index 000000000000..b3619c8fd83d
--- /dev/null
+++ b/lib/utils/cache/fdt_andes_llcache.c
@@ -0,0 +1,166 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2025 Andes Technology Corporation
+ */
+
+#include <sbi/riscv_io.h>
+#include <sbi/sbi_error.h>
+#include <sbi/sbi_heap.h>
+#include <sbi_utils/cache/fdt_cache.h>
+#include <sbi_utils/fdt/fdt_driver.h>
+#include <sbi_utils/hsm/fdt_hsm_andes_atcsmu.h>
+
+#define LLCACHE_REG_CFG_OFFSET 0x0
+#define LLCACHE_REG_CTRL_OFFSET 0x8
+#define LLCACHE_REG_CCTL_CMD_OFFSET 0x40
+#define LLCACHE_REG_CCTL_STATUS_OFFSET 0x80
+
+#define LLCACHE_REG_CFG_MAP_MASK BIT(20)
+#define LLCACHE_REG_CTRL_EN_MASK BIT(0)
+#define LLCACHE_REG_CTRL_INIT_MASK BIT(14)
+#define LLCACHE_REG_CCTL_STATUS_MASK GENMASK(3, 0)
+
+#define LLCACHE_WBINVAL_ALL 0x12
+
+struct andes_llcache {
+ struct cache_device dev;
+ void *base;
+ uint32_t cmd_stride;
+ uint32_t status_stride;
+ uint32_t status_core_stride;
+};
+
+#define to_llcache(_dev) container_of(_dev, struct andes_llcache, dev)
+
+static bool andes_llcache_init_done(struct andes_llcache *llcache)
+{
+ uint32_t llcache_ctrl;
+ void *ctrl_addr = (char *)llcache->base + LLCACHE_REG_CTRL_OFFSET;
+
+ llcache_ctrl = readl_relaxed(ctrl_addr);
+ return !EXTRACT_FIELD(llcache_ctrl, LLCACHE_REG_CTRL_INIT_MASK);
+}
+
+static bool andes_llcache_cctl_done(struct andes_llcache *llcache, uint32_t hartid)
+{
+ uint32_t llcache_cctl_status;
+ void *cctl_status_addr = (char *)llcache->base + LLCACHE_REG_CCTL_STATUS_OFFSET +
+ hartid * llcache->status_stride;
+
+ llcache_cctl_status = readl_relaxed(cctl_status_addr);
+ return !EXTRACT_FIELD(llcache_cctl_status,
+ LLCACHE_REG_CCTL_STATUS_MASK <<
+ hartid * llcache->status_core_stride);
+}
+
+static int andes_llcache_flush_all(struct cache_device *dev)
+{
+ uint32_t hartid = current_hartid();
+ struct andes_llcache *llcache = to_llcache(dev);
+ void *cctl_cmd_addr = (char *)llcache->base + LLCACHE_REG_CCTL_CMD_OFFSET +
+ hartid * llcache->cmd_stride;
+
+ /*
+ * Each command register corresponds to one CPU core, so each CPU core
+ * should only use its command registers to do the cache operation.
+ */
+ writel(LLCACHE_WBINVAL_ALL, cctl_cmd_addr);
+
+ /* Wait for the command completion */
+ while (!andes_llcache_cctl_done(llcache, hartid))
+ ;
+
+ return 0;
+}
+
+static int andes_llcache_enable(struct cache_device *dev, bool enable)
+{
+ struct andes_llcache *llcache = to_llcache(dev);
+ u32 llcache_ctrl;
+ void *ctrl_addr = (char *)llcache->base + LLCACHE_REG_CTRL_OFFSET;
+
+ /*
+ * To properly enable the last level cache to cache both instructions
+ * and data, apply the following sequence:
+ *
+ * - Write the control register with the desired value, except the
+ * CEN field should be set to zero. Thus, store the control register
+ * value with the CEN field being 0 when disabling the last level
+ * cache.
+ * - Write the control register again using the same value of step 1
+ * with the CEN field being 1.
+ */
+ if (enable) {
+ llcache_ctrl = atcsmu_read_scratch();
+ writel(llcache_ctrl, ctrl_addr);
+ writel(llcache_ctrl | LLCACHE_REG_CTRL_EN_MASK, ctrl_addr);
+ } else {
+ llcache_ctrl = readl(ctrl_addr);
+ atcsmu_write_scratch(llcache_ctrl & ~LLCACHE_REG_CTRL_EN_MASK);
+ writel(llcache_ctrl & ~LLCACHE_REG_CTRL_EN_MASK, ctrl_addr);
+ }
+
+ llcache_ctrl = readl(ctrl_addr);
+ return enable == EXTRACT_FIELD(llcache_ctrl, LLCACHE_REG_CTRL_EN_MASK);
+}
+
+static struct cache_ops andes_llcache_ops = {
+ .cache_flush_all = andes_llcache_flush_all,
+ .cache_enable = andes_llcache_enable,
+};
+
+static int andes_llcache_probe(const void *fdt, int nodeoff, const struct fdt_match *match)
+{
+ int rc;
+ u64 llcache_base = 0;
+ struct andes_llcache *llcache;
+ struct cache_device *dev;
+ uint32_t llcache_cfg;
+
+ rc = fdt_get_node_addr_size(fdt, nodeoff, 0, &llcache_base, NULL);
+ if (rc < 0 || !llcache_base)
+ return SBI_ENODEV;
+
+ llcache = sbi_zalloc(sizeof(*llcache));
+ if (!llcache)
+ return SBI_ENOMEM;
+
+ dev = &llcache->dev;
+ dev->ops = &andes_llcache_ops;
+ rc = fdt_cache_add(fdt, nodeoff, dev);
+ if (rc) {
+ sbi_free(llcache);
+ return rc;
+ }
+
+ llcache->base = (void *)llcache_base;
+ llcache_cfg = readl_relaxed((char *)llcache->base + LLCACHE_REG_CFG_OFFSET);
+
+ /* Configurations for V1/V0 memory map */
+ if (EXTRACT_FIELD(llcache_cfg, LLCACHE_REG_CFG_MAP_MASK)) {
+ llcache->cmd_stride = 0x1000;
+ llcache->status_stride = 0x1000;
+ llcache->status_core_stride = 0;
+ } else {
+ llcache->cmd_stride = 0x10;
+ llcache->status_stride = 0x0;
+ llcache->status_core_stride = 4;
+ }
+
+ /* Wait for the hardware initialization done */
+ while (!andes_llcache_init_done(llcache))
+ ;
+
+ return SBI_OK;
+}
+
+static const struct fdt_match andes_llcache_match[] = {
+ { .compatible = "andestech,llcache" },
+ {},
+};
+
+const struct fdt_driver fdt_andes_llcache = {
+ .match_table = andes_llcache_match,
+ .init = andes_llcache_probe,
+};
diff --git a/lib/utils/cache/objects.mk b/lib/utils/cache/objects.mk
index aa76adc2e3bc..6c9bce84903e 100644
--- a/lib/utils/cache/objects.mk
+++ b/lib/utils/cache/objects.mk
@@ -8,6 +8,9 @@ libsbiutils-objs-$(CONFIG_FDT_CACHE) += cache/fdt_cache.o
libsbiutils-objs-$(CONFIG_FDT_CACHE) += cache/fdt_cache_drivers.carray.o
libsbiutils-objs-$(CONFIG_FDT_CACHE) += cache/fdt_cmo_helper.o
+carray-fdt_cache_drivers-$(CONFIG_FDT_CACHE_ANDES_LLCACHE) += fdt_andes_llcache
+libsbiutils-objs-$(CONFIG_FDT_CACHE_ANDES_LLCACHE) += cache/fdt_andes_llcache.o
+
carray-fdt_cache_drivers-$(CONFIG_FDT_CACHE_SIFIVE_CCACHE) += fdt_sifive_ccache
libsbiutils-objs-$(CONFIG_FDT_CACHE_SIFIVE_CCACHE) += cache/fdt_sifive_ccache.o
diff --git a/lib/utils/hsm/fdt_hsm_andes_atcsmu.c b/lib/utils/hsm/fdt_hsm_andes_atcsmu.c
index 3fe931aa1816..32f6fef808c6 100644
--- a/lib/utils/hsm/fdt_hsm_andes_atcsmu.c
+++ b/lib/utils/hsm/fdt_hsm_andes_atcsmu.c
@@ -84,6 +84,16 @@ u32 atcsmu_get_sleep_type(u32 hartid)
return readl_relaxed((char *)atcsmu_base + PCSm_SCRATCH_OFFSET(hartid));
}
+void atcsmu_write_scratch(u32 value)
+{
+ writel_relaxed(value, (char *)atcsmu_base + SCRATCH_PAD_OFFSET);
+}
+
+u32 atcsmu_read_scratch(void)
+{
+ return readl_relaxed((char *)atcsmu_base + SCRATCH_PAD_OFFSET);
+}
+
static int ae350_hart_start(u32 hartid, ulong saddr)
{
u32 hartindex = sbi_hartid_to_hartindex(hartid);
diff --git a/platform/generic/configs/defconfig b/platform/generic/configs/defconfig
index d85da930edbc..4b7615d6c51b 100644
--- a/platform/generic/configs/defconfig
+++ b/platform/generic/configs/defconfig
@@ -13,6 +13,7 @@ CONFIG_PLATFORM_THEAD=y
CONFIG_PLATFORM_MIPS_P8700=y
CONFIG_PLATFORM_SPACEMIT_K1=y
CONFIG_FDT_CACHE=y
+CONFIG_FDT_CACHE_ANDES_LLCACHE=y
CONFIG_FDT_CACHE_SIFIVE_CCACHE=y
CONFIG_FDT_CACHE_SIFIVE_EC=y
CONFIG_FDT_CACHE_SIFIVE_PL2=y
--
2.34.1
--
opensbi mailing list
opensbi@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/opensbi
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH 5/5] lib: utils/suspend: add Andes ATCSMU suspend driver
2025-12-29 7:19 [PATCH 0/5] Add hart state management and system suspend support for AE350 Ben Zong-You Xie
` (3 preceding siblings ...)
2025-12-29 7:19 ` [PATCH 4/5] lib: utils/cache: add Andes last level cache controller Ben Zong-You Xie
@ 2025-12-29 7:19 ` Ben Zong-You Xie
2026-02-11 15:59 ` Andrew Jones
2026-02-11 6:24 ` [PATCH 0/5] Add hart state management and system suspend support for AE350 Anup Patel
5 siblings, 1 reply; 9+ messages in thread
From: Ben Zong-You Xie @ 2025-12-29 7:19 UTC (permalink / raw)
To: opensbi; +Cc: Leo Yu-Chi Liang
Implement a system-wide suspend driver for the Andes AE350 platform.
This driver supports Andes-specific deep sleep (suspend to RAM) and
light sleep (suspend to standby) functionalities via the ATCSMU.
The major differences between deep sleep and light sleep are:
- Power Domain and Resume Path: Deep sleep powers down the core domain.
Consequently, harts waking from deep sleep resume from the reset
vector. Light sleep utilizes clock gating to the core domain; harts
maintain state and resume execution at the instruction immediately
following the WFI instruction.
- Primary Hart Wakeup: In both modes, the primary hart is woken by
UART or RTC alarm interrupts. In deep sleep, the primary hart is
additionally responsible for re-enabling the Last Level Cache (LLC)
and restoring Andes-specific CSRs.
- Secondary Hart Wakeup: In light sleep, secondary harts are woken
by an IPI sent from the primary hart. In deep sleep, they are
woken by an ATCSMU hardware wake-up command. Furthermore,
secondary harts must restore Andes-specific CSRs when returning
from deep sleep.
Signed-off-by: Ben Zong-You Xie <ben717@andestech.com>
Signed-off-by: Leo Yu-Chi Liang <ycliang@andestech.com>
---
include/sbi_utils/hsm/fdt_hsm_andes_atcsmu.h | 1 +
lib/utils/hsm/fdt_hsm_andes_atcsmu.c | 49 ++++++--
lib/utils/suspend/Kconfig | 5 +
lib/utils/suspend/fdt_suspend_andes_atcsmu.c | 119 +++++++++++++++++++
lib/utils/suspend/objects.mk | 3 +
platform/generic/andes/ae350.c | 10 +-
platform/generic/configs/defconfig | 1 +
7 files changed, 178 insertions(+), 10 deletions(-)
create mode 100644 lib/utils/suspend/fdt_suspend_andes_atcsmu.c
diff --git a/include/sbi_utils/hsm/fdt_hsm_andes_atcsmu.h b/include/sbi_utils/hsm/fdt_hsm_andes_atcsmu.h
index ef851b7501b5..5e71fab7c69e 100644
--- a/include/sbi_utils/hsm/fdt_hsm_andes_atcsmu.h
+++ b/include/sbi_utils/hsm/fdt_hsm_andes_atcsmu.h
@@ -60,5 +60,6 @@ int atcsmu_set_reset_vector(u64 wakeup_addr, u32 hartid);
u32 atcsmu_get_sleep_type(u32 hartid);
void atcsmu_write_scratch(u32 value);
u32 atcsmu_read_scratch(void);
+bool atcsmu_pcs_is_sleep(u32 hartid, bool deep_sleep);
#endif
diff --git a/lib/utils/hsm/fdt_hsm_andes_atcsmu.c b/lib/utils/hsm/fdt_hsm_andes_atcsmu.c
index 32f6fef808c6..0874d365cb34 100644
--- a/lib/utils/hsm/fdt_hsm_andes_atcsmu.c
+++ b/lib/utils/hsm/fdt_hsm_andes_atcsmu.c
@@ -94,16 +94,38 @@ u32 atcsmu_read_scratch(void)
return readl_relaxed((char *)atcsmu_base + SCRATCH_PAD_OFFSET);
}
+bool atcsmu_pcs_is_sleep(u32 hartid, bool deep_sleep)
+{
+ u32 pcs_status = readl_relaxed((char *)atcsmu_base + PCSm_STATUS_OFFSET(hartid));
+ u32 pd_status = deep_sleep ? PD_STATUS_DEEP_SLEEP : PD_STATUS_LIGHT_SLEEP;
+
+ if (EXTRACT_FIELD(pcs_status, PD_TYPE_MASK) != PD_TYPE_SLEEP) {
+ sbi_printf("ATCSMU: hart%d (PCS%d): failed to sleep\n", hartid, hartid + 3);
+ return false;
+ }
+
+ if (EXTRACT_FIELD(pcs_status, PD_STATUS_MASK) != pd_status) {
+ sbi_printf("ATCSMU: hart%d (PCS%d): failed to enter %s sleep\n",
+ hartid, hartid + 3, deep_sleep ? "deep" : "light");
+ return false;
+ }
+
+ return true;
+}
+
static int ae350_hart_start(u32 hartid, ulong saddr)
{
u32 hartindex = sbi_hartid_to_hartindex(hartid);
+ u32 sleep_type = atcsmu_get_sleep_type(hartid);
/*
* Don't send wakeup command when:
* 1) boot time
* 2) the target hart is non-sleepable 25-series hart0
+ * 3) light sleep
*/
- if (!sbi_init_count(hartindex) || (is_andes(25) && hartid == 0))
+ if (!sbi_init_count(hartindex) || (is_andes(25) && hartid == 0) ||
+ sleep_type == SBI_SUSP_AE350_LIGHT_SLEEP)
return sbi_ipi_raw_send(hartindex, false);
atcsmu_set_command(WAKEUP_CMD, hartid);
@@ -130,16 +152,27 @@ static int ae350_hart_stop(void)
/* Prevent the core leaving the WFI mode unexpectedly */
csr_write(CSR_MIE, 0);
- atcsmu_set_wakeup_events(0x0, hartid);
- atcsmu_set_command(DEEP_SLEEP_CMD, hartid);
- rc = atcsmu_set_reset_vector((u64)ae350_enable_coherency_warmboot, hartid);
- if (rc)
- return SBI_EFAIL;
+ if (sleep_type == SBI_SUSP_AE350_LIGHT_SLEEP) {
+ csr_write(CSR_MIE, MIP_MSIP);
+ atcsmu_set_wakeup_events(PCS_WAKEUP_MSIP_MASK, hartid);
+ atcsmu_set_command(LIGHT_SLEEP_CMD, hartid);
+ } else if (sleep_type == SBI_SUSP_SLEEP_TYPE_SUSPEND) {
+ atcsmu_set_wakeup_events(0x0, hartid);
+ atcsmu_set_command(DEEP_SLEEP_CMD, hartid);
+ rc = atcsmu_set_reset_vector((u64)ae350_enable_coherency_warmboot, hartid);
+ if (rc)
+ return SBI_EFAIL;
+
+ ae350_non_ret_save(sbi_scratch_thishart_ptr());
+ }
- ae350_non_ret_save(sbi_scratch_thishart_ptr());
ae350_disable_coherency();
wfi();
- return 0;
+
+ /* Light sleep resumes here */
+ ae350_enable_coherency();
+
+ return SBI_ENOTSUPP;
}
static const struct sbi_hsm_device hsm_andes_atcsmu = {
diff --git a/lib/utils/suspend/Kconfig b/lib/utils/suspend/Kconfig
index 6c09d95253d2..8d05aff67ace 100644
--- a/lib/utils/suspend/Kconfig
+++ b/lib/utils/suspend/Kconfig
@@ -9,6 +9,11 @@ config FDT_SUSPEND
if FDT_SUSPEND
+config FDT_SUSPEND_ANDES_ATCSMU
+ bool "FDT Andes ATCSMU suspend driver"
+ depends on FDT_HSM_ANDES_ATCSMU && FDT_CACHE_ANDES_LLCACHE
+ default n
+
config FDT_SUSPEND_RPMI
bool "FDT RPMI suspend driver"
depends on FDT_MAILBOX && RPMI_MAILBOX
diff --git a/lib/utils/suspend/fdt_suspend_andes_atcsmu.c b/lib/utils/suspend/fdt_suspend_andes_atcsmu.c
new file mode 100644
index 000000000000..05e67e47eebe
--- /dev/null
+++ b/lib/utils/suspend/fdt_suspend_andes_atcsmu.c
@@ -0,0 +1,119 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2025 Andes Technology Corporation
+ */
+
+#include <andes/andes.h>
+#include <sbi/riscv_asm.h>
+#include <sbi/sbi_console.h>
+#include <sbi/sbi_domain.h>
+#include <sbi/sbi_error.h>
+#include <sbi/sbi_ecall_interface.h>
+#include <sbi/sbi_hart.h>
+#include <sbi/sbi_system.h>
+#include <sbi_utils/cache/fdt_cmo_helper.h>
+#include <sbi_utils/fdt/fdt_driver.h>
+#include <sbi_utils/fdt/fdt_helper.h>
+#include <sbi_utils/hsm/fdt_hsm_andes_atcsmu.h>
+
+static int check_secondary_harts_sleep(u32 hartid, bool deep_sleep)
+{
+ const struct sbi_domain *dom = &root;
+ unsigned long i;
+ u32 target;
+
+ /* Ensure the secondary harts entering the corresponding sleep state */
+ sbi_hartmask_for_each_hartindex(i, dom->possible_harts) {
+ target = sbi_hartindex_to_hartid(i);
+ if (target != hartid && !atcsmu_pcs_is_sleep(target, deep_sleep))
+ return SBI_EFAIL;
+ }
+
+ return SBI_OK;
+}
+
+static int ae350_system_suspend_check(u32 sleep_type)
+{
+ return (sleep_type == SBI_SUSP_SLEEP_TYPE_SUSPEND ||
+ sleep_type == SBI_SUSP_AE350_LIGHT_SLEEP) ? SBI_OK : SBI_EINVAL;
+}
+
+static int ae350_system_suspend(u32 sleep_type, unsigned long addr)
+{
+ u32 hartid = current_hartid();
+ int rc;
+
+ /* Prevent the core leaving the WFI mode unexpectedly */
+ csr_write(CSR_MIE, 0);
+
+ /*
+ * Only allow the S-mode external interrupts (UART2 and RTC alarm) to
+ * wake up the primary hart
+ */
+ csr_set(CSR_SIE, MIP_SEIP);
+ atcsmu_set_wakeup_events(PCS_WAKEUP_RTC_ALARM_MASK | PCS_WAKEUP_UART2_MASK, hartid);
+
+ if (sleep_type == SBI_SUSP_AE350_LIGHT_SLEEP) {
+ rc = check_secondary_harts_sleep(hartid, false);
+ if (rc)
+ return rc;
+
+ atcsmu_set_command(LIGHT_SLEEP_CMD, hartid);
+ } else if (sleep_type == SBI_SUSP_SLEEP_TYPE_SUSPEND) {
+ rc = check_secondary_harts_sleep(hartid, true);
+ if (rc)
+ return rc;
+
+ atcsmu_set_command(DEEP_SLEEP_CMD, hartid);
+ rc = atcsmu_set_reset_vector((u64)ae350_enable_coherency_warmboot, hartid);
+ if (rc)
+ return rc;
+
+ ae350_non_ret_save(sbi_scratch_thishart_ptr());
+ fdt_cmo_llc_enable(false);
+ rc = fdt_cmo_llc_flush_all();
+ if (rc)
+ return rc;
+ }
+
+ ae350_disable_coherency();
+ wfi();
+
+ /* Light sleep resumes here */
+ ae350_enable_coherency();
+
+ return SBI_OK;
+}
+
+static void ae350_system_resume(void)
+{
+ u32 hartid = current_hartid();
+ u32 sleep_type = atcsmu_get_sleep_type(hartid);
+
+ if (sleep_type == SBI_SUSP_SLEEP_TYPE_SUSPEND)
+ fdt_cmo_llc_enable(true);
+}
+
+static struct sbi_system_suspend_device suspend_andes_atcsmu = {
+ .name = "andes_atcsmu",
+ .system_suspend_check = ae350_system_suspend_check,
+ .system_suspend = ae350_system_suspend,
+ .system_resume = ae350_system_resume,
+};
+
+static int suspend_andes_atcsmu_probe(const void *fdt, int nodeoff, const struct fdt_match *match)
+{
+ sbi_system_suspend_set_device(&suspend_andes_atcsmu);
+ return 0;
+}
+
+static const struct fdt_match suspend_andes_atcsmu_match[] = {
+ { .compatible = "andestech,atcsmu-sys" },
+ { },
+};
+
+const struct fdt_driver fdt_suspend_andes_atcsmu = {
+ .match_table = suspend_andes_atcsmu_match,
+ .init = suspend_andes_atcsmu_probe,
+};
diff --git a/lib/utils/suspend/objects.mk b/lib/utils/suspend/objects.mk
index 1fb29b5e7941..fb1ad9ae34c7 100644
--- a/lib/utils/suspend/objects.mk
+++ b/lib/utils/suspend/objects.mk
@@ -7,6 +7,9 @@
# Anup Patel <apatel@ventanamicro.com>
#
+carray-fdt_early_drivers-$(CONFIG_FDT_SUSPEND_ANDES_ATCSMU) += fdt_suspend_andes_atcsmu
+libsbiutils-objs-$(CONFIG_FDT_SUSPEND_ANDES_ATCSMU) += suspend/fdt_suspend_andes_atcsmu.o
+
carray-fdt_early_drivers-$(CONFIG_FDT_SUSPEND_RPMI) += fdt_suspend_rpmi
libsbiutils-objs-$(CONFIG_FDT_SUSPEND_RPMI) += suspend/fdt_suspend_rpmi.o
diff --git a/platform/generic/andes/ae350.c b/platform/generic/andes/ae350.c
index 9bd1554394b7..4cf7e26def54 100644
--- a/platform/generic/andes/ae350.c
+++ b/platform/generic/andes/ae350.c
@@ -8,9 +8,12 @@
#include <andes/andes_pmu.h>
#include <andes/andes_sbi.h>
#include <platform_override.h>
+#include <sbi/sbi_ecall_interface.h>
+#include <sbi/riscv_asm.h>
#include <sbi/sbi_init.h>
#include <sbi/sbi_scratch.h>
#include <sbi_utils/fdt/fdt_helper.h>
+#include <sbi_utils/hsm/fdt_hsm_andes_atcsmu.h>
static unsigned long andes_hart_data_offset;
extern void _start_warm(void);
@@ -59,14 +62,17 @@ void ae350_enable_coherency_warmboot(void)
static int ae350_early_init(bool cold_boot)
{
+ u32 hartid = current_hartid();
+ u32 sleep_type = atcsmu_get_sleep_type(hartid);
+
if (cold_boot) {
andes_hart_data_offset = sbi_scratch_alloc_offset(sizeof(struct andes_hart_data));
if (!andes_hart_data_offset)
return SBI_ENOMEM;
}
- /* Don't restore Andes CSRs during boot */
- if (sbi_init_count(current_hartindex()))
+ /* Don't restore Andes CSRs during boot or wake up from light sleep */
+ if (sbi_init_count(current_hartindex()) && sleep_type == SBI_SUSP_SLEEP_TYPE_SUSPEND)
ae350_non_ret_restore(sbi_scratch_thishart_ptr());
return generic_early_init(cold_boot);
diff --git a/platform/generic/configs/defconfig b/platform/generic/configs/defconfig
index 4b7615d6c51b..19a73a247765 100644
--- a/platform/generic/configs/defconfig
+++ b/platform/generic/configs/defconfig
@@ -62,6 +62,7 @@ CONFIG_FDT_SERIAL_UART8250=y
CONFIG_FDT_SERIAL_XILINX_UARTLITE=y
CONFIG_SERIAL_SEMIHOSTING=y
CONFIG_FDT_SUSPEND=y
+CONFIG_FDT_SUSPEND_ANDES_ATCSMU=y
CONFIG_FDT_SUSPEND_RPMI=y
CONFIG_FDT_SUSPEND_SIFIVE_SMC0=y
CONFIG_FDT_TIMER=y
--
2.34.1
--
opensbi mailing list
opensbi@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/opensbi
^ permalink raw reply related [flat|nested] 9+ messages in thread
* Re: [PATCH 0/5] Add hart state management and system suspend support for AE350
2025-12-29 7:19 [PATCH 0/5] Add hart state management and system suspend support for AE350 Ben Zong-You Xie
` (4 preceding siblings ...)
2025-12-29 7:19 ` [PATCH 5/5] lib: utils/suspend: add Andes ATCSMU suspend driver Ben Zong-You Xie
@ 2026-02-11 6:24 ` Anup Patel
5 siblings, 0 replies; 9+ messages in thread
From: Anup Patel @ 2026-02-11 6:24 UTC (permalink / raw)
To: Ben Zong-You Xie; +Cc: opensbi
On Mon, Dec 29, 2025 at 12:49 PM Ben Zong-You Xie <ben717@andestech.com> wrote:
>
> This patch series introduces support for hart state management and
> system suspend functionalities on the Andes AE350 platform. For the hart
> state management, AE350 platform supports CPU hotplug. For the
> system suspend, there are two platform-specific low-power modes,
> including deep sleep (suspend to RAM) and light sleep (suspend to standby).
>
> Below is the patch summary:
> [1/5]: The Andes System Management Unit (ATCSMU) logic is factored out into
> a dedicated FDT-based HSM driver, improving code reuse and
> maintainability.
> [2/5]: Implement the functions to save and restore platform-specific CSRs
> which are lost during CPU hotplug or deep sleep.
> [3/5]: Add the functions to enable/disable the cache.
> [4/5]: A new Last Level Cache (LLC) driver is introduced to handle required
> cache operations for deep sleep.
> [5/5]: An ATCSMU-based suspend driver is implemented to support Andes deep
> sleep and light sleep, providing the OS with access to standardized
> SBI system suspend extensions.
>
> Ben Zong-You Xie (5):
> lib: utils/hsm: factor out ATCSMU code into an HSM driver
> platform: generic/andes: add CSR save and restore functions for AE350
> platform
> lib: utils/cache: add cache enable function
> lib: utils/cache: add Andes last level cache controller
> lib: utils/suspend: add Andes ATCSMU suspend driver
Applied this series to the riscv/opensbi repo.
Thanks,
Anup
--
opensbi mailing list
opensbi@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/opensbi
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH 5/5] lib: utils/suspend: add Andes ATCSMU suspend driver
2025-12-29 7:19 ` [PATCH 5/5] lib: utils/suspend: add Andes ATCSMU suspend driver Ben Zong-You Xie
@ 2026-02-11 15:59 ` Andrew Jones
2026-02-23 3:53 ` Ben Zong-You Xie
0 siblings, 1 reply; 9+ messages in thread
From: Andrew Jones @ 2026-02-11 15:59 UTC (permalink / raw)
To: Ben Zong-You Xie; +Cc: opensbi, Leo Yu-Chi Liang
On Mon, Dec 29, 2025 at 03:19:14PM +0800, Ben Zong-You Xie wrote:
...
> @@ -130,16 +152,27 @@ static int ae350_hart_stop(void)
> /* Prevent the core leaving the WFI mode unexpectedly */
> csr_write(CSR_MIE, 0);
>
> - atcsmu_set_wakeup_events(0x0, hartid);
> - atcsmu_set_command(DEEP_SLEEP_CMD, hartid);
> - rc = atcsmu_set_reset_vector((u64)ae350_enable_coherency_warmboot, hartid);
> - if (rc)
> - return SBI_EFAIL;
> + if (sleep_type == SBI_SUSP_AE350_LIGHT_SLEEP) {
> + csr_write(CSR_MIE, MIP_MSIP);
> + atcsmu_set_wakeup_events(PCS_WAKEUP_MSIP_MASK, hartid);
> + atcsmu_set_command(LIGHT_SLEEP_CMD, hartid);
> + } else if (sleep_type == SBI_SUSP_SLEEP_TYPE_SUSPEND) {
> + atcsmu_set_wakeup_events(0x0, hartid);
> + atcsmu_set_command(DEEP_SLEEP_CMD, hartid);
> + rc = atcsmu_set_reset_vector((u64)ae350_enable_coherency_warmboot, hartid);
> + if (rc)
> + return SBI_EFAIL;
> +
> + ae350_non_ret_save(sbi_scratch_thishart_ptr());
> + }
>
> - ae350_non_ret_save(sbi_scratch_thishart_ptr());
> ae350_disable_coherency();
> wfi();
> - return 0;
> +
> + /* Light sleep resumes here */
> + ae350_enable_coherency();
> +
> + return SBI_ENOTSUPP;
I just skimmed this patch really fast, so I probably missed something,
but it seems odd to return SBI_ENOTSUPP here.
Thanks,
drew
--
opensbi mailing list
opensbi@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/opensbi
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH 5/5] lib: utils/suspend: add Andes ATCSMU suspend driver
2026-02-11 15:59 ` Andrew Jones
@ 2026-02-23 3:53 ` Ben Zong-You Xie
0 siblings, 0 replies; 9+ messages in thread
From: Ben Zong-You Xie @ 2026-02-23 3:53 UTC (permalink / raw)
To: Andrew Jones; +Cc: opensbi, Leo Yu-Chi Liang
> >
> > - ae350_non_ret_save(sbi_scratch_thishart_ptr());
> > ae350_disable_coherency();
> > wfi();
> > - return 0;
> > +
> > + /* Light sleep resumes here */
> > + ae350_enable_coherency();
> > +
> > + return SBI_ENOTSUPP;
>
> I just skimmed this patch really fast, so I probably missed something,
> but it seems odd to return SBI_ENOTSUPP here.
>
> Thanks,
> drew
Hi Drew,
The reason for returning SBI_ENOTSUPP here is to satisfy the logic in
sbi_hsm_exit(). In sbi_hsm_exit(), there is a specific check:
if (hsm_device_has_hart_hotplug()) {
if (hsm_device_hart_stop() != SBI_ENOTSUPP)
goto fail_exit;
}
/**
* As platform is lacking support for hotplug, directly jump to warmboot
* and wait for interrupts in warmboot. We do it preemptively in order
* preserve the hart states and reuse the code path for hotplug.
*/
jump_warmboot();
If we return 0 (SBI_OK), the condition (0 != SBI_ENOTSUPP) evaluates to
true, and the code jumps to fail_exit, hanging the hart.
Returning SBI_ENOTSUPP allows the execution to fall through to the
jump_warmboot() path, which is the intended behavior for 'light sleep'
on AE350 platform.
--
opensbi mailing list
opensbi@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/opensbi
^ permalink raw reply [flat|nested] 9+ messages in thread
end of thread, other threads:[~2026-02-23 3:54 UTC | newest]
Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-12-29 7:19 [PATCH 0/5] Add hart state management and system suspend support for AE350 Ben Zong-You Xie
2025-12-29 7:19 ` [PATCH 1/5] lib: utils/hsm: factor out ATCSMU code into an HSM driver Ben Zong-You Xie
2025-12-29 7:19 ` [PATCH 2/5] platform: generic/andes: add CSR save and restore functions for AE350 platform Ben Zong-You Xie
2025-12-29 7:19 ` [PATCH 3/5] lib: utils/cache: add cache enable function Ben Zong-You Xie
2025-12-29 7:19 ` [PATCH 4/5] lib: utils/cache: add Andes last level cache controller Ben Zong-You Xie
2025-12-29 7:19 ` [PATCH 5/5] lib: utils/suspend: add Andes ATCSMU suspend driver Ben Zong-You Xie
2026-02-11 15:59 ` Andrew Jones
2026-02-23 3:53 ` Ben Zong-You Xie
2026-02-11 6:24 ` [PATCH 0/5] Add hart state management and system suspend support for AE350 Anup Patel
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox