Files
opensbi/lib/utils/suspend/fdt_suspend_andes_atcsmu.c
T
Ben Zong-You Xie 06af8bd61b lib: utils/andes: arm the SMU sleep command last
Once the sleep command is written, the next WFI puts the core to sleep,
so everything that can fail has to run before it.

Also, use writel() so the command store cannot still be in flight at the
WFI.

Signed-off-by: Ben Zong-You Xie <ben717@andestech.com>
Reviewed-by: Anup Patel <anup@brainfault.org>
Link: https://lore.kernel.org/r/20260728081041.2724668-8-ben717@andestech.com
Signed-off-by: Anup Patel <anup@brainfault.org>
2026-09-01 10:46:23 +05:30

152 lines
3.8 KiB
C

/*
* 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/sbi_timer.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>
#define HART_SLEEP_TIMEOUT_MS 1000
static int wait_secondary_harts_sleep(u32 hartid, bool deep_sleep)
{
const struct sbi_domain *dom = &root;
unsigned long i;
u32 target;
struct atcsmu_sleep_arg arg;
arg.deep_sleep = deep_sleep;
/* Wait for 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)
continue;
arg.hartid = target;
if (!sbi_timer_waitms_until(atcsmu_hart_is_sleep, &arg,
HART_SLEEP_TIMEOUT_MS)) {
sbi_printf("ATCSMU: hart%u (PCS%u): timed out waiting for %s sleep\n",
target, target + 3,
deep_sleep ? "deep" : "light");
return SBI_ETIMEOUT;
}
}
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();
unsigned long saved_mie;
int rc;
/* Prevent the core leaving the WFI mode unexpectedly */
saved_mie = csr_read(CSR_MIE);
csr_write(CSR_MIE, 0);
/* SMU wakes the primary hart on RTC alarm / UART2 */
atcsmu_set_wakeup_events(PCS_WAKEUP_RTC_ALARM_MASK | PCS_WAKEUP_UART2_MASK, hartid);
if (sleep_type == SBI_SUSP_AE350_LIGHT_SLEEP) {
rc = wait_secondary_harts_sleep(hartid, false);
if (rc)
goto err_restore_mie;
/* Clock-gated only: enable SEI to resume past the WFI */
csr_set(CSR_MIE, MIP_SEIP);
atcsmu_set_command(LIGHT_SLEEP_CMD, hartid);
} else if (sleep_type == SBI_SUSP_SLEEP_TYPE_SUSPEND) {
rc = wait_secondary_harts_sleep(hartid, true);
if (rc)
goto err_restore_mie;
rc = atcsmu_set_reset_vector((ulong)ae350_enable_coherency_warmboot, hartid);
if (rc)
goto err_restore_mie;
ae350_non_ret_save(sbi_scratch_thishart_ptr());
/* No LLC is fine; only fail on real errors */
rc = fdt_cmo_llc_enable(false);
if (rc && rc != SBI_ENODEV)
goto err_discard_save;
rc = fdt_cmo_llc_flush_all();
if (rc && rc != SBI_ENODEV)
goto err_enable_llc;
atcsmu_set_command(DEEP_SLEEP_CMD, hartid);
}
ae350_disable_coherency();
wfi();
/* Light sleep resumes here */
ae350_enable_coherency();
return SBI_OK;
err_enable_llc:
fdt_cmo_llc_enable(true);
err_discard_save:
ae350_non_ret_discard(sbi_scratch_thishart_ptr());
err_restore_mie:
csr_write(CSR_MIE, saved_mie);
return rc;
}
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);
ae350_non_ret_restore(sbi_scratch_thishart_ptr());
}
}
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,
};