[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, &reg_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