| Message ID | 20250708074940.10904-11-nick.hu@sifive.com |
|---|---|
| State | Changes Requested |
| Headers | show |
| Series | Add SiFive TMC0 and SMC0 driver | expand |
On Tue, Jul 8, 2025 at 1:20 PM Nick Hu <nick.hu@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@sifive.com> > Reviewed-by: Cyan Yang <cyan.yang@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 >
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; }