blob: 088ec077b58ed023a4ff109e4ca5958defbe2f7b [file] [log] [blame]
/*
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2022 Andes Technology Corporation
*
* Authors:
* Yu Chien Peter Lin <peterlin@andestech.com>
*/
#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/andes45.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)
{
/*
* Don't send wakeup command when:
* 1) boot-time
* 2) the target hart is non-sleepable 25-series hart0
*/
if (!sbi_init_count(hartid) || (is_andes(25) && hartid == 0))
return sbi_ipi_raw_send(sbi_hartid_to_hartindex(hartid));
/* 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(void)
{
int rc;
void *fdt;
fdt = fdt_get_address();
rc = fdt_parse_compat_addr(fdt, (uint64_t *)&smu.addr,
"andestech,atcsmu");
if (!rc) {
sbi_hsm_set_device(&andes_smu);
}
}
static int ae350_final_init(bool cold_boot, const struct fdt_match *match)
{
if (cold_boot)
ae350_hsm_device_init();
return 0;
}
static const struct fdt_match andes_ae350_match[] = {
{ .compatible = "andestech,ae350" },
{ },
};
const struct platform_override andes_ae350 = {
.match_table = andes_ae350_match,
.final_init = ae350_final_init,
.extensions_init = andes_pmu_extensions_init,
.pmu_init = andes_pmu_init,
};