lib: utils/suspend: Add SiFive SMC0 driver

The SiFive SMC0 controls the clock and power domain of the core complex
on the SiFive platform. The core complex enters the low power state
after the secondary cores enter the tile power gating and last core
execute the `CEASE` instruction with the corresponding SMC0
configurations. The devices that inside both tile power domain and core
complex power domain will be off, including caches and timer. Therefore
we need to flush the last level cache before entering the core complex
power gating and update the timer after waking up.

Reviewed-by: Cyan Yang <cyan.yang@sifive.com>
Reviewed-by: Anup Patel <anup@brainfault.org>
Signed-off-by: Nick Hu <nick.hu@sifive.com>
Link: https://lore.kernel.org/r/20251020-cache-upstream-v7-12-69a132447d8a@sifive.com
Signed-off-by: Anup Patel <anup@brainfault.org>
This commit is contained in:
Nick Hu
2025-10-20 14:34:14 +08:00
committed by Anup Patel
parent ab23d8a392
commit 37b72cb575
6 changed files with 397 additions and 0 deletions

View File

@@ -18,6 +18,7 @@
#include <sbi_utils/fdt/fdt_driver.h>
#include <sbi_utils/fdt/fdt_helper.h>
#include <sbi_utils/hsm/fdt_hsm_sifive_inst.h>
#include <sbi_utils/hsm/fdt_hsm_sifive_tmc0.h>
struct sifive_tmc0 {
unsigned long reg;
@@ -77,6 +78,62 @@ static unsigned long tmc0_offset;
#define SIFIVE_TMC_WAKE_MASK_WREQ BIT(31)
#define SIFIVE_TMC_WAKE_MASK_ACK BIT(30)
int sifive_tmc0_set_wakemask_enareq(u32 hartid)
{
struct sbi_scratch *scratch = sbi_hartid_to_scratch(hartid);
struct sifive_tmc0 *tmc0 = tmc0_ptr_get(scratch);
unsigned long addr;
u32 v;
if (!tmc0)
return SBI_ENODEV;
addr = tmc0->reg + SIFIVE_TMC_WAKE_MASK_OFF;
v = readl((void *)addr);
writel(v | SIFIVE_TMC_WAKE_MASK_WREQ, (void *)addr);
while (!(readl((void *)addr) & SIFIVE_TMC_WAKE_MASK_ACK));
return SBI_OK;
}
void sifive_tmc0_set_wakemask_disreq(u32 hartid)
{
struct sbi_scratch *scratch = sbi_hartid_to_scratch(hartid);
struct sifive_tmc0 *tmc0 = tmc0_ptr_get(scratch);
unsigned long addr;
u32 v;
if (!tmc0)
return;
addr = tmc0->reg + SIFIVE_TMC_WAKE_MASK_OFF;
v = readl((void *)addr);
writel(v & ~SIFIVE_TMC_WAKE_MASK_WREQ, (void *)addr);
while (readl((void *)addr) & SIFIVE_TMC_WAKE_MASK_ACK);
}
bool sifive_tmc0_is_pg(u32 hartid)
{
struct sbi_scratch *scratch = sbi_hartid_to_scratch(hartid);
struct sifive_tmc0 *tmc0 = tmc0_ptr_get(scratch);
unsigned long addr;
u32 v;
if (!tmc0)
return false;
addr = tmc0->reg + SIFIVE_TMC_PG_OFF;
v = readl((void *)addr);
if (!(v & SIFIVE_TMC_PG_ENA_ACK) ||
(v & SIFIVE_TMC_PG_ENARSP) ||
(v & SIFIVE_TMC_PG_DIS_REQ))
return false;
return true;
}
static void sifive_tmc0_set_resumepc(physical_addr_t addr)
{
struct sifive_tmc0 *tmc0 = tmc0_ptr_get(sbi_scratch_thishart_ptr());