lib: utils/hsm: factor out ATCSMU code into an HSM driver

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>
Link: https://lore.kernel.org/r/20251229071914.1451587-2-ben717@andestech.com
Signed-off-by: Anup Patel <anup@brainfault.org>
This commit is contained in:
Ben Zong-You Xie
2025-12-29 15:19:10 +08:00
committed by Anup Patel
parent 74434f2558
commit 9ffacc8ae1
16 changed files with 302 additions and 348 deletions

View File

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

View File

@@ -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);
extern void _start_warm(void);
static int ae350_hart_start(u32 hartid, ulong saddr)
void ae350_enable_coherency_warmboot(void)
{
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);
}
}
static int ae350_final_init(bool cold_boot)
{
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;

View File

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

View File

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

View File

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

View File

@@ -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 */