[PATCH 3/3] lib: utils/cache: Add SiFive Extensible Cache (EC) driver
Nick Hu
nick.hu at sifive.com
Thu Nov 13 19:22:47 PST 2025
Add support for SiFive Extensible Cache (EC) controller with multi-slice
architecture. The driver implements cache maintenance operations through
MMIO register interface.
Co-developed-by: Vincent Chen <vincent.chen at sifive.com>
Signed-off-by: Vincent Chen <vincent.chen at sifive.com>
Co-developed-by: Samuel Holland <samuel.holland at sifive.com>
Signed-off-by: Samuel Holland <samuel.holland at sifive.com>
Co-developed-by: Yong-Xuan Wang <yongxuan.wang at sifive.com>
Signed-off-by: Yong-Xuan Wang <yongxuan.wang at sifive.com>
Signed-off-by: Nick Hu <nick.hu at sifive.com>
---
lib/utils/cache/Kconfig | 4 +
lib/utils/cache/fdt_sifive_ec.c | 194 +++++++++++++++++++++++++++++++++++++
lib/utils/cache/objects.mk | 3 +
platform/generic/configs/defconfig | 1 +
4 files changed, 202 insertions(+)
diff --git a/lib/utils/cache/Kconfig b/lib/utils/cache/Kconfig
index e28262fa2e81328460c8ee9a34b841454d03d776..be7d57c37d2477e60fb793d42bd6cf2cd830b846 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 0000000000000000000000000000000000000000..53ef6edb24aee316652cbaf83fd34c7c159aba59
--- /dev/null
+++ b/lib/utils/cache/fdt_sifive_ec.c
@@ -0,0 +1,194 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2025 SiFive Inc.
+ */
+
+#include <libfdt.h>
+#include <sbi/riscv_io.h>
+#include <sbi/sbi_heap.h>
+#include <sbi/sbi_platform.h>
+#include <sbi_utils/cache/fdt_cache.h>
+#include <sbi_utils/fdt/fdt_driver.h>
+
+#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 37d250d1001bc647e3ae05304b2278e5dc608cae..aa76adc2e3bc5efbaa32b4ead9849d8b62bb7747 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 1bb14c78b9f0fc98493961023703a1ed2a24eea8..d0e2ec26bd60ad9394f631dd4d2870f5f2b90fd0 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
--
2.34.1
More information about the opensbi
mailing list