mirror of
https://github.com/riscv-software-src/opensbi.git
synced 2026-02-27 18:01:45 +00:00
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:
committed by
Anup Patel
parent
74434f2558
commit
9ffacc8ae1
49
include/sbi_utils/hsm/fdt_hsm_andes_atcsmu.h
Normal file
49
include/sbi_utils/hsm/fdt_hsm_andes_atcsmu.h
Normal file
@@ -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
|
||||
@@ -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 */
|
||||
@@ -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
|
||||
|
||||
167
lib/utils/hsm/fdt_hsm_andes_atcsmu.c
Normal file
167
lib/utils/hsm/fdt_hsm_andes_atcsmu.c
Normal file
@@ -0,0 +1,167 @@
|
||||
/*
|
||||
* 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 unsigned long 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((ulong)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, rc;
|
||||
u64 addr;
|
||||
|
||||
/* 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, &addr, NULL);
|
||||
if (rc < 0 || !addr)
|
||||
return SBI_ENODEV;
|
||||
atcsmu_base = addr;
|
||||
|
||||
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,
|
||||
};
|
||||
@@ -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
|
||||
libsbiutils-objs-$(CONFIG_FDT_HSM_SIFIVE_TMC0) += hsm/fdt_hsm_sifive_tmc0.o
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -8,4 +8,3 @@
|
||||
#
|
||||
|
||||
libsbiutils-objs-$(CONFIG_SYS_HTIF) += sys/htif.o
|
||||
libsbiutils-objs-$(CONFIG_SYS_ATCSMU) += sys/atcsmu.o
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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 */
|
||||
|
||||
Reference in New Issue
Block a user