diff --git a/lib/utils/cache/Kconfig b/lib/utils/cache/Kconfig index e28262fa..be7d57c3 100644 --- a/lib/utils/cache/Kconfig +++ b/lib/utils/cache/Kconfig @@ -14,6 +14,10 @@ config FDT_CACHE_SIFIVE_CCACHE bool "SiFive CCACHE FDT cache driver" default n +config FDT_CACHE_SIFIVE_EC + bool "SiFive EC FDT cache driver" + default n + config FDT_CACHE_SIFIVE_PL2 bool "SiFive PL2 FDT cache driver" default n diff --git a/lib/utils/cache/fdt_sifive_ec.c b/lib/utils/cache/fdt_sifive_ec.c new file mode 100644 index 00000000..dda0d10d --- /dev/null +++ b/lib/utils/cache/fdt_sifive_ec.c @@ -0,0 +1,195 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2025 SiFive Inc. + */ + +#include +#include +#include +#include +#include +#include + +#define SIFIVE_EC_FEATURE_DISABLE_OFF 0x100UL +#define SIFIVE_EC_FLUSH_CMD_OFF 0x800UL +#define SIFIVE_EC_FLUSH_STATUS_OFF 0x808UL +#define SIFIVE_EC_FLUSH_ADDR_OFF 0x810UL +#define SIFIVE_EC_MODE_CTRL 0xa00UL + +#define SIFIVE_EC_FLUSH_COMPLETION_MASK BIT(0) + +#define SIFIVE_EC_CLEANINV_ALL_CMD 0x3 + +#define SIFIVE_EC_FEATURE_DISABLE_VAL 0 + +struct sifive_ec_quirks { + bool two_mode; + char *reg_name; +}; + +struct sifive_ec_slice { + void *addr; + bool last_slice; +}; + +struct sifive_ec { + struct cache_device dev; + struct sifive_ec_slice *slices; +}; + +#define to_ec(_dev) container_of(_dev, struct sifive_ec, dev) + +static int sifive_ec_flush_all(struct cache_device *dev) +{ + struct sifive_ec *ec_dev = to_ec(dev); + struct sifive_ec_slice *slices = ec_dev->slices; + u32 cmd = SIFIVE_EC_CLEANINV_ALL_CMD, i = 0; + void *addr; + + do { + addr = slices[i].addr; + + writel((int)-1, addr + SIFIVE_EC_FLUSH_ADDR_OFF); + writel((int)-1, addr + SIFIVE_EC_FLUSH_ADDR_OFF + sizeof(u32)); + writel(cmd, addr + SIFIVE_EC_FLUSH_CMD_OFF); + } while (!slices[i++].last_slice); + + i = 0; + do { + addr = slices[i].addr; + do {} while (!(readl(addr + SIFIVE_EC_FLUSH_STATUS_OFF) & + SIFIVE_EC_FLUSH_COMPLETION_MASK)); + } while (!slices[i++].last_slice); + + return 0; +} + +int sifive_ec_warm_init(struct cache_device *dev) +{ + struct sifive_ec *ec_dev = to_ec(dev); + struct sifive_ec_slice *slices = ec_dev->slices; + struct sbi_domain *dom = sbi_domain_thishart_ptr(); + int i = 0; + + if (dom->boot_hartid == current_hartid()) { + do { + writel(SIFIVE_EC_FEATURE_DISABLE_VAL, + slices[i].addr + SIFIVE_EC_FEATURE_DISABLE_OFF); + } while (!slices[i++].last_slice); + } + + return SBI_OK; +} + +static struct cache_ops sifive_ec_ops = { + .warm_init = sifive_ec_warm_init, + .cache_flush_all = sifive_ec_flush_all, +}; + +static int sifive_ec_slices_cold_init(const void *fdt, int nodeoff, + struct sifive_ec_slice *slices, + const struct sifive_ec_quirks *quirks) +{ + int rc, subnode, slice_idx = -1; + u64 reg_addr, size, start_addr = -1, end_addr = 0; + + fdt_for_each_subnode(subnode, fdt, nodeoff) { + rc = fdt_get_node_addr_size_by_name(fdt, subnode, quirks->reg_name, ®_addr, + &size); + if (rc < 0) + return SBI_ENODEV; + + if (reg_addr < start_addr) + start_addr = reg_addr; + + if (reg_addr + size > end_addr) + end_addr = reg_addr + size; + + slices[++slice_idx].addr = (void *)(uintptr_t)reg_addr; + } + slices[slice_idx].last_slice = true; + + /* Only enable the pmp to protect the EC m-mode region when it support two mode */ + if (quirks->two_mode) { + rc = sbi_domain_root_add_memrange((unsigned long)start_addr, + (unsigned long)(end_addr - start_addr), + BIT(12), + (SBI_DOMAIN_MEMREGION_MMIO | + SBI_DOMAIN_MEMREGION_M_READABLE | + SBI_DOMAIN_MEMREGION_M_WRITABLE)); + if (rc) + return rc; + } + + return SBI_OK; +} + +static int sifive_ec_cold_init(const void *fdt, int nodeoff, const struct fdt_match *match) +{ + const struct sifive_ec_quirks *quirks = match->data; + struct sifive_ec_slice *slices; + struct sifive_ec *ec_dev; + struct cache_device *dev; + int subnode, rc = SBI_ENOMEM; + u32 slice_count = 0; + + /* Count the number of slices */ + fdt_for_each_subnode(subnode, fdt, nodeoff) + slice_count++; + + /* Need at least one slice */ + if (!slice_count) + return SBI_EINVAL; + + ec_dev = sbi_zalloc(sizeof(*ec_dev)); + if (!ec_dev) + return SBI_ENOMEM; + + slices = sbi_zalloc(slice_count * sizeof(*slices)); + if (!slices) + goto free_ec; + + rc = sifive_ec_slices_cold_init(fdt, nodeoff, slices, quirks); + if (rc) + goto free_slice; + + dev = &ec_dev->dev; + dev->ops = &sifive_ec_ops; + rc = fdt_cache_add(fdt, nodeoff, dev); + if (rc) + goto free_slice; + + ec_dev->slices = slices; + + return SBI_OK; + +free_slice: + sbi_free(slices); +free_ec: + sbi_free(ec_dev); + return rc; +} + +static const struct sifive_ec_quirks sifive_extensiblecache0_quirks = { + .two_mode = false, + .reg_name = "control", +}; + +static const struct sifive_ec_quirks sifive_extensiblecache4_quirks = { + .two_mode = true, + .reg_name = "m_mode", +}; + +static const struct fdt_match sifive_ec_match[] = { + { .compatible = "sifive,extensiblecache4", .data = &sifive_extensiblecache4_quirks }, + { .compatible = "sifive,extensiblecache3", .data = &sifive_extensiblecache0_quirks }, + { .compatible = "sifive,extensiblecache2", .data = &sifive_extensiblecache0_quirks }, + { .compatible = "sifive,extensiblecache0", .data = &sifive_extensiblecache0_quirks }, + {}, +}; + +struct fdt_driver fdt_sifive_ec = { + .match_table = sifive_ec_match, + .init = sifive_ec_cold_init, +}; diff --git a/lib/utils/cache/objects.mk b/lib/utils/cache/objects.mk index 37d250d1..aa76adc2 100644 --- a/lib/utils/cache/objects.mk +++ b/lib/utils/cache/objects.mk @@ -14,4 +14,7 @@ libsbiutils-objs-$(CONFIG_FDT_CACHE_SIFIVE_CCACHE) += cache/fdt_sifive_ccache.o carray-fdt_cache_drivers-$(CONFIG_FDT_CACHE_SIFIVE_PL2) += fdt_sifive_pl2 libsbiutils-objs-$(CONFIG_FDT_CACHE_SIFIVE_PL2) += cache/fdt_sifive_pl2.o +carray-fdt_cache_drivers-$(CONFIG_FDT_CACHE_SIFIVE_EC) += fdt_sifive_ec +libsbiutils-objs-$(CONFIG_FDT_CACHE_SIFIVE_EC) += cache/fdt_sifive_ec.o + libsbiutils-objs-$(CONFIG_CACHE) += cache/cache.o diff --git a/platform/generic/configs/defconfig b/platform/generic/configs/defconfig index 1bb14c78..d0e2ec26 100644 --- a/platform/generic/configs/defconfig +++ b/platform/generic/configs/defconfig @@ -13,6 +13,7 @@ CONFIG_PLATFORM_MIPS_P8700=y CONFIG_PLATFORM_SPACEMIT_K1=y CONFIG_FDT_CACHE=y CONFIG_FDT_CACHE_SIFIVE_CCACHE=y +CONFIG_FDT_CACHE_SIFIVE_EC=y CONFIG_FDT_CACHE_SIFIVE_PL2=y CONFIG_FDT_CPPC=y CONFIG_FDT_CPPC_RPMI=y