[PATCH v3 10/12] platform: sifive: Add SiFive TMC0 driver
Anup Patel
anup at brainfault.org
Mon Aug 4 01:38:56 PDT 2025
On Tue, Jul 8, 2025 at 1:20 PM Nick Hu <nick.hu at sifive.com> wrote:
>
> The SiFive TMC0 controls the tile power domains on SiFive platform. The
> CPU enters the low power state via the `CEASE` instruction after
> configuring the TMC0. Any devices that inside the tile power domain will
> be power gated, including the private cache. Therefore flushing the
> private cache before entering the low power state.
>
> Signed-off-by: Nick Hu <nick.hu at sifive.com>
> Reviewed-by: Cyan Yang <cyan.yang at sifive.com>
> ---
> platform/generic/Kconfig | 2 +
> platform/generic/include/sifive/sifive_inst.h | 20 +
> platform/generic/include/sifive/sifive_tmc0.h | 12 +
> platform/generic/sifive/Kconfig | 5 +
> platform/generic/sifive/objects.mk | 2 +
> .../pmdomain/fdt_pmdomain_sifive_tmc0.c | 371 ++++++++++++++++++
> platform/generic/sifive/sifive_dev_platform.c | 21 +
The TMC0 driver should be under the lib/utils/hsm/ directory.
Regards,
Anup
> 7 files changed, 433 insertions(+)
> create mode 100644 platform/generic/include/sifive/sifive_inst.h
> create mode 100644 platform/generic/include/sifive/sifive_tmc0.h
> create mode 100644 platform/generic/sifive/Kconfig
> create mode 100644 platform/generic/sifive/pmdomain/fdt_pmdomain_sifive_tmc0.c
>
> diff --git a/platform/generic/Kconfig b/platform/generic/Kconfig
> index 20c25a83..1d6ea3f4 100644
> --- a/platform/generic/Kconfig
> +++ b/platform/generic/Kconfig
> @@ -46,6 +46,7 @@ config PLATFORM_RENESAS_RZFIVE
> config PLATFORM_SIFIVE_DEV
> bool "SiFive development platform support"
> depends on FDT_CACHE
> + select SIFIVE_TMC0
> default n
>
> config PLATFORM_SIFIVE_FU540
> @@ -78,6 +79,7 @@ config PLATFORM_MIPS_P8700
> default n
>
> source "$(OPENSBI_SRC_DIR)/platform/generic/andes/Kconfig"
> +source "$(OPENSBI_SRC_DIR)/platform/generic/sifive/Kconfig"
> source "$(OPENSBI_SRC_DIR)/platform/generic/thead/Kconfig"
>
> endif
> diff --git a/platform/generic/include/sifive/sifive_inst.h b/platform/generic/include/sifive/sifive_inst.h
> new file mode 100644
> index 00000000..36dddf68
> --- /dev/null
> +++ b/platform/generic/include/sifive/sifive_inst.h
> @@ -0,0 +1,20 @@
> +/*
> + * SPDX-License-Identifier: BSD-2-Clause
> + *
> + * Copyright (c) 2025 SiFive Inc.
> + */
> +
> +#ifndef __SIFIVE_INST_H__
> +#define __SIFIVE_INST_H__
> +
> +static inline void sifive_cease(void)
> +{
> + __asm__ __volatile__(".word 0x30500073" ::: "memory");
> +}
> +
> +static inline void sifive_cflush(void)
> +{
> + __asm__ __volatile__(".word 0xfc000073" ::: "memory");
> +}
> +
> +#endif
> diff --git a/platform/generic/include/sifive/sifive_tmc0.h b/platform/generic/include/sifive/sifive_tmc0.h
> new file mode 100644
> index 00000000..6100e0dc
> --- /dev/null
> +++ b/platform/generic/include/sifive/sifive_tmc0.h
> @@ -0,0 +1,12 @@
> +/*
> + * SPDX-License-Identifier: BSD-2-Clause
> + *
> + * Copyright (c) 2025 SiFive Inc.
> + */
> +
> +#ifndef __SIFIVE_TMC0_H__
> +#define __SIFIVE_TMC0_H__
> +
> +int sifive_tmc0_cold_init(void);
> +
> +#endif
> diff --git a/platform/generic/sifive/Kconfig b/platform/generic/sifive/Kconfig
> new file mode 100644
> index 00000000..2cbcea08
> --- /dev/null
> +++ b/platform/generic/sifive/Kconfig
> @@ -0,0 +1,5 @@
> +# SPDX-License-Identifier: BSD-2-Clause
> +
> +config SIFIVE_TMC0
> + bool "SiFive TMC v0 driver support"
> + default n
> diff --git a/platform/generic/sifive/objects.mk b/platform/generic/sifive/objects.mk
> index d32e1273..f8938713 100644
> --- a/platform/generic/sifive/objects.mk
> +++ b/platform/generic/sifive/objects.mk
> @@ -10,3 +10,5 @@ platform-objs-$(CONFIG_PLATFORM_SIFIVE_FU540) += sifive/fu540.o
>
> carray-platform_override_modules-$(CONFIG_PLATFORM_SIFIVE_FU740) += sifive_fu740
> platform-objs-$(CONFIG_PLATFORM_SIFIVE_FU740) += sifive/fu740.o
> +
> +platform-objs-$(CONFIG_SIFIVE_TMC0) += sifive/pmdomain/fdt_pmdomain_sifive_tmc0.o
> diff --git a/platform/generic/sifive/pmdomain/fdt_pmdomain_sifive_tmc0.c b/platform/generic/sifive/pmdomain/fdt_pmdomain_sifive_tmc0.c
> new file mode 100644
> index 00000000..70d02b33
> --- /dev/null
> +++ b/platform/generic/sifive/pmdomain/fdt_pmdomain_sifive_tmc0.c
> @@ -0,0 +1,371 @@
> +/*
> + * SPDX-License-Identifier: BSD-2-Clause
> + *
> + * Copyright (c) 2025 SiFive
> + */
> +
> +#include <libfdt.h>
> +#include <sbi/riscv_asm.h>
> +#include <sbi/riscv_io.h>
> +#include <sbi/sbi_bitops.h>
> +#include <sbi/sbi_console.h>
> +#include <sbi/sbi_error.h>
> +#include <sbi/sbi_hart.h>
> +#include <sbi/sbi_heap.h>
> +#include <sbi/sbi_hsm.h>
> +#include <sbi/sbi_ipi.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/ipi/aclint_mswi.h>
> +#include <sifive/sifive_inst.h>
> +#include <sifive/sifive_tmc0.h>
> +
> +struct sifive_tmc0 {
> + unsigned long reg;
> + struct sbi_dlist node;
> + u32 id;
> +};
> +
> +static SBI_LIST_HEAD(tmc0_list);
> +static unsigned long tmc0_offset;
> +
> +#define tmc0_ptr_get(__scratch) \
> + sbi_scratch_read_type((__scratch), struct sifive_tmc0 *, tmc0_offset)
> +
> +#define tmc0_ptr_set(__scratch, __tmc0) \
> + sbi_scratch_write_type((__scratch), struct sifive_tmc0 *, tmc0_offset, (__tmc0))
> +
> +/* TMC.PGPREP */
> +#define SIFIVE_TMC_PGPREP_OFF 0x0
> +#define SIFIVE_TMC_PGPREP_ENA_REQ BIT(31)
> +#define SIFIVE_TMC_PGPREP_ENA_ACK BIT(30)
> +#define SIFIVE_TMC_PGPREP_DIS_REQ BIT(29)
> +#define SIFIVE_TMC_PGPREP_DIS_ACK BIT(28)
> +#define SIFIVE_TMC_PGPREP_CLFPNOTQ BIT(18)
> +#define SIFIVE_TMC_PGPREP_PMCENAERR BIT(17)
> +#define SIFIVE_TMC_PGPREP_PMCDENY BIT(16)
> +#define SIFIVE_TMC_PGPREP_BUSERR BIT(15)
> +#define SIFIVE_TMC_PGPREP_WAKE_DETECT BIT(12)
> +#define SIFIVE_TMC_PGPREP_INTERNAL_ABORT BIT(2)
> +#define SIFIVE_TMC_PGPREP_ENARSP (SIFIVE_TMC_PGPREP_CLFPNOTQ | \
> + SIFIVE_TMC_PGPREP_PMCENAERR | \
> + SIFIVE_TMC_PGPREP_PMCDENY | \
> + SIFIVE_TMC_PGPREP_BUSERR | \
> + SIFIVE_TMC_PGPREP_WAKE_DETECT)
> +
> +/* TMC.PG */
> +#define SIFIVE_TMC_PG_OFF 0x4
> +#define SIFIVE_TMC_PG_ENA_REQ BIT(31)
> +#define SIFIVE_TMC_PG_ENA_ACK BIT(30)
> +#define SIFIVE_TMC_PG_DIS_REQ BIT(29)
> +#define SIFIVE_TMC_PG_DIS_ACK BIT(28)
> +#define SIFIVE_TMC_PG_PMC_ENA_ERR BIT(17)
> +#define SIFIVE_TMC_PG_PMC_DENY BIT(16)
> +#define SIFIVE_TMC_PG_BUS_ERR BIT(15)
> +#define SIFIVE_TMC_PG_MASTNOTQ BIT(14)
> +#define SIFIVE_TMC_PG_WARM_RESET BIT(1)
> +#define SIFIVE_TMC_PG_ENARSP (SIFIVE_TMC_PG_PMC_ENA_ERR | \
> + SIFIVE_TMC_PG_PMC_DENY | \
> + SIFIVE_TMC_PG_BUS_ERR | \
> + SIFIVE_TMC_PG_MASTNOTQ)
> +
> +/* TMC.RESUMEPC */
> +#define SIFIVE_TMC_RESUMEPC_LO 0x10
> +#define SIFIVE_TMC_RESUMEPC_HI 0x14
> +
> +/* TMC.WAKEMASK */
> +#define SIFIVE_TMC_WAKE_MASK_OFF 0x20
> +#define SIFIVE_TMC_WAKE_MASK_WREQ BIT(31)
> +#define SIFIVE_TMC_WAKE_MASK_ACK BIT(30)
> +
> +static void sifive_tmc0_set_resumepc(physical_addr_t addr)
> +{
> + struct sifive_tmc0 *tmc0 = tmc0_ptr_get(sbi_scratch_thishart_ptr());
> +
> + writel((u32)addr, (void *)(tmc0->reg + SIFIVE_TMC_RESUMEPC_LO));
> + writel((u32)(addr >> 32), (void *)(tmc0->reg + SIFIVE_TMC_RESUMEPC_HI));
> +}
> +
> +static u32 sifive_tmc0_set_pgprep_enareq(void)
> +{
> + struct sifive_tmc0 *tmc0 = tmc0_ptr_get(sbi_scratch_thishart_ptr());
> + unsigned long reg = tmc0->reg + SIFIVE_TMC_PGPREP_OFF;
> + u32 v = readl((void *)reg);
> +
> + writel(v | SIFIVE_TMC_PGPREP_ENA_REQ, (void *)reg);
> + while (!(readl((void *)reg) & SIFIVE_TMC_PGPREP_ENA_ACK));
> +
> + v = readl((void *)reg);
> + return v & SIFIVE_TMC_PGPREP_INTERNAL_ABORT;
> +}
> +
> +static void sifive_tmc0_set_pgprep_disreq(void)
> +{
> + struct sifive_tmc0 *tmc0 = tmc0_ptr_get(sbi_scratch_thishart_ptr());
> + unsigned long reg = tmc0->reg + SIFIVE_TMC_PGPREP_OFF;
> + u32 v = readl((void *)reg);
> +
> + writel(v | SIFIVE_TMC_PGPREP_DIS_REQ, (void *)reg);
> + while (!(readl((void *)reg) & SIFIVE_TMC_PGPREP_DIS_ACK));
> +}
> +
> +static u32 sifive_tmc0_get_pgprep_enarsp(void)
> +{
> + struct sifive_tmc0 *tmc0 = tmc0_ptr_get(sbi_scratch_thishart_ptr());
> + unsigned long reg = tmc0->reg + SIFIVE_TMC_PGPREP_OFF;
> + u32 v = readl((void *)reg);
> +
> + return v & SIFIVE_TMC_PGPREP_ENARSP;
> +}
> +
> +static void sifive_tmc0_set_pg_enareq(void)
> +{
> + struct sifive_tmc0 *tmc0 = tmc0_ptr_get(sbi_scratch_thishart_ptr());
> + unsigned long reg = tmc0->reg + SIFIVE_TMC_PG_OFF;
> + u32 v = readl((void *)reg);
> +
> + writel(v | SIFIVE_TMC_PG_ENA_REQ, (void *)reg);
> +}
> +
> +static int sifive_tmc0_prep(void)
> +{
> + struct sbi_scratch *scratch = sbi_scratch_thishart_ptr();
> + u32 rc;
> +
> + if (!tmc0_ptr_get(scratch))
> + return SBI_ENODEV;
> +
> + rc = sifive_tmc0_set_pgprep_enareq();
> + if (rc) {
> + sbi_printf("TMC0 error: Internal Abort (Wake detect)\n");
> + goto fail;
> + }
> +
> + rc = sifive_tmc0_get_pgprep_enarsp();
> + if (rc) {
> + sifive_tmc0_set_pgprep_disreq();
> + sbi_printf("TMC0 error: error response code: 0x%x\n", rc);
> + goto fail;
> + }
> +
> + sifive_tmc0_set_resumepc(scratch->warmboot_addr);
> +
> + return SBI_OK;
> +
> +fail:
> + return SBI_EFAIL;
> +}
> +
> +static int sifive_tmc0_enter(void)
> +{
> + struct sbi_scratch *scratch = sbi_scratch_thishart_ptr();
> + u32 rc;
> +
> + /* Flush cache and check if there is wake detect or bus error */
> + if (fdt_cmo_private_flc_flush_all() &&
> + sbi_hart_has_extension(scratch, SBI_HART_EXT_XSF_CFLUSH_D_L1))
> + sifive_cflush();
> +
> + rc = sifive_tmc0_get_pgprep_enarsp();
> + if (rc) {
> + sbi_printf("TMC0 error: error response code: 0x%x\n", rc);
> + goto fail;
> + }
> +
> + if (sbi_hart_has_extension(scratch, SBI_HART_EXT_XSF_CEASE)) {
> + sifive_tmc0_set_pg_enareq();
> + while (1)
> + sifive_cease();
> + }
> +
> + rc = SBI_ENOTSUPP;
> +fail:
> + sifive_tmc0_set_pgprep_disreq();
> + return rc;
> +}
> +
> +static int sifive_tmc0_tile_pg(void)
> +{
> + int rc;
> +
> + rc = sifive_tmc0_prep();
> + if (rc)
> + return rc;
> +
> + return sifive_tmc0_enter();
> +}
> +
> +static struct sifive_tmc0 *sifive_tmc0_find(u32 id)
> +{
> + struct sifive_tmc0 *tmc0;
> +
> + sbi_list_for_each_entry(tmc0, &tmc0_list, node) {
> + if (tmc0->id == id)
> + return tmc0;
> + }
> +
> + return NULL;
> +}
> +
> +static int sifive_tmc0_add(struct sifive_tmc0 *tmc0)
> +{
> + if (!tmc0)
> + return SBI_ENODEV;
> +
> + if (sifive_tmc0_find(tmc0->id))
> + return SBI_EALREADY;
> +
> + sbi_list_add(&tmc0->node, &tmc0_list);
> +
> + return SBI_OK;
> +}
> +
> +static int sifive_tmc0_probe(const void *fdt, int nodeoff, const struct fdt_match *match)
> +{
> + struct sifive_tmc0 *tmc0;
> + int rc;
> + u64 addr;
> +
> + tmc0 = sbi_zalloc(sizeof(*tmc0));
> + if (!tmc0)
> + return SBI_ENOMEM;
> +
> + rc = fdt_get_node_addr_size(fdt, nodeoff, 0, &addr, NULL);
> + if (rc)
> + goto free_tmc0;
> +
> + tmc0->reg = (unsigned long)addr;
> + tmc0->id = nodeoff;
> +
> + rc = sifive_tmc0_add(tmc0);
> + if (rc)
> + goto free_tmc0;
> +
> + return SBI_OK;
> +
> +free_tmc0:
> + sbi_free(tmc0);
> + return rc;
> +}
> +
> +static const struct fdt_match sifive_tmc0_match[] = {
> + { .compatible = "sifive,tmc0" },
> + { },
> +};
> +
> +const struct fdt_driver fdt_sifive_tmc0 = {
> + .match_table = sifive_tmc0_match,
> + .init = sifive_tmc0_probe,
> +};
> +
> +static const struct fdt_driver *const sifive_tmc0_drivers[] = {
> + &fdt_sifive_tmc0,
> + NULL
> +};
> +
> +static int fdt_sifive_tmc0_get(const void *fdt, int noff, struct sifive_tmc0 **out)
> +{
> + struct sifive_tmc0 *tmc0;
> + int rc;
> +
> + noff = fdt_node_offset_by_phandle(fdt, noff);
> + if (noff < 0)
> + return noff;
> +
> + rc = fdt_driver_init_by_offset(fdt, noff, sifive_tmc0_drivers);
> + if (rc)
> + return rc;
> +
> + tmc0 = sifive_tmc0_find(noff);
> + if (!tmc0)
> + return SBI_EFAIL;
> +
> + if (out)
> + *out = tmc0;
> +
> + return SBI_OK;
> +}
> +
> +static int fdt_sifive_tmc0_cold_init(const void *fdt)
> +{
> + struct sbi_scratch *scratch;
> + struct sifive_tmc0 *tmc0;
> + const fdt32_t *val;
> + int cpus_off, cpu_off, rc;
> + u32 hartid;
> +
> + cpus_off = fdt_path_offset(fdt, "/cpus");
> + if (cpus_off < 0)
> + return SBI_ENOENT;
> +
> + fdt_for_each_subnode(cpu_off, fdt, cpus_off) {
> + rc = fdt_parse_hart_id(fdt, cpu_off, &hartid);
> + if (rc)
> + continue;
> +
> + val = fdt_getprop(fdt, cpu_off, "power-domains", NULL);
> + if (!val)
> + return SBI_ENOENT;
> +
> + rc = fdt_sifive_tmc0_get(fdt, fdt32_to_cpu(val[0]), &tmc0);
> + if (rc)
> + return rc;
> +
> + scratch = sbi_hartid_to_scratch(hartid);
> + if (!scratch)
> + continue;
> +
> + tmc0_ptr_set(scratch, tmc0);
> + }
> +
> + return SBI_OK;
> +}
> +
> +static int sifive_tmc0_start(u32 hartid, ulong saddr)
> +{
> + struct sbi_ipi_device *clint = aclint_mswi_get();
> +
> + /*
> + * In system suspend, the IMSIC will be reset in SiFive platform so
> + * we use the CLINT IPI as the wake event.
> + */
> + if (clint && clint->ipi_send)
> + clint->ipi_send(sbi_hartid_to_hartindex(hartid));
> + else
> + sbi_ipi_raw_send(sbi_hartid_to_hartindex(hartid));
> +
> + return SBI_OK;
> +}
> +
> +static int sifive_tmc0_stop(void)
> +{
> + unsigned long mie = csr_read(CSR_MIE);
> + int rc;
> + // Set IPI as wake up source
> + csr_set(CSR_MIE, MIP_MEIP | MIP_MSIP);
> +
> + rc = sifive_tmc0_tile_pg();
> + if (rc) {
> + csr_write(CSR_MIE, mie);
> + return rc;
> + }
> +
> + return SBI_OK;
> +}
> +
> +static struct sbi_hsm_device tmc0_hsm_dev = {
> + .name = "SiFive TMC0",
> + .hart_start = sifive_tmc0_start,
> + .hart_stop = sifive_tmc0_stop,
> +};
> +
> +int sifive_tmc0_cold_init(void)
> +{
> + tmc0_offset = sbi_scratch_alloc_type_offset(struct sifive_tmc0 *);
> + if (!tmc0_offset)
> + return SBI_ENOMEM;
> +
> + sbi_hsm_set_device(&tmc0_hsm_dev);
> + return fdt_sifive_tmc0_cold_init(fdt_get_address());
> +}
> diff --git a/platform/generic/sifive/sifive_dev_platform.c b/platform/generic/sifive/sifive_dev_platform.c
> index ac659868..3e433bb3 100644
> --- a/platform/generic/sifive/sifive_dev_platform.c
> +++ b/platform/generic/sifive/sifive_dev_platform.c
> @@ -5,7 +5,11 @@
> */
>
> #include <platform_override.h>
> +#include <sbi/sbi_ipi.h>
> #include <sbi_utils/cache/fdt_cmo_helper.h>
> +#include <sbi_utils/ipi/aclint_mswi.h>
> +
> +#include <sifive/sifive_tmc0.h>
>
> static int sifive_early_init(bool cold_boot)
> {
> @@ -19,13 +23,30 @@ static int sifive_early_init(bool cold_boot)
> if (rc)
> return rc;
>
> + if (cold_boot) {
> + rc = sifive_tmc0_cold_init();
> + if (rc)
> + return rc;
> + }
> +
> return 0;
> }
>
> +static int sifive_final_init(bool cold_boot)
> +{
> + struct sbi_ipi_device *clint = aclint_mswi_get();
> +
> + if (clint && clint->ipi_clear)
> + clint->ipi_clear();
> +
> + return generic_final_init(cold_boot);
> +}
> +
> static int sifive_platform_init(const void *fdt, int nodeoff,
> const struct fdt_match *match)
> {
> generic_platform_ops.early_init = sifive_early_init;
> + generic_platform_ops.final_init = sifive_final_init;
>
> return 0;
> }
> --
> 2.17.1
>
More information about the opensbi
mailing list