[PATCH 2/3] lib: utils/cache: Add SiFive PL2 controller

Nick Hu nick.hu at sifive.com
Thu Nov 13 19:22:46 PST 2025


SiFive Private L2(PL2) cache is a private cache owned by each hart. Add
this driver to support private cache flush operations via the MMIO
registers.

Co-developed-by: Eric Lin <eric.lin at sifive.com>
Signed-off-by: Eric Lin <eric.lin at sifive.com>
Co-developed-by: Zong Li <zong.li at sifive.com>
Signed-off-by: Zong Li <zong.li at sifive.com>
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>
Signed-off-by: Nick Hu <nick.hu at sifive.com>
---
 lib/utils/cache/Kconfig            |   4 ++
 lib/utils/cache/fdt_sifive_pl2.c   | 139 +++++++++++++++++++++++++++++++++++++
 lib/utils/cache/objects.mk         |   3 +
 platform/generic/configs/defconfig |   1 +
 4 files changed, 147 insertions(+)

diff --git a/lib/utils/cache/Kconfig b/lib/utils/cache/Kconfig
index 1c7abdc9ad93319569807b4e25623cac871c6ce3..e28262fa2e81328460c8ee9a34b841454d03d776 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_PL2
+	bool "SiFive PL2 FDT cache driver"
+	default n
+
 endif
 
 config CACHE
diff --git a/lib/utils/cache/fdt_sifive_pl2.c b/lib/utils/cache/fdt_sifive_pl2.c
new file mode 100644
index 0000000000000000000000000000000000000000..afe5d6796dbf3504604642643b49cbb4ccc858c6
--- /dev/null
+++ b/lib/utils/cache/fdt_sifive_pl2.c
@@ -0,0 +1,139 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2025 SiFive Inc.
+ */
+
+#include <libfdt.h>
+#include <sbi/riscv_io.h>
+#include <sbi/sbi_error.h>
+#include <sbi/sbi_heap.h>
+#include <sbi_utils/cache/fdt_cache.h>
+#include <sbi_utils/fdt/fdt_driver.h>
+
+#define FLUSH64_CMD_TARGET_ALL		(0x2 << 3)
+#define FLUSH64_CMD_TYPE_FLUSH		0x3ULL
+
+#define SIFIVE_PL2CACHE_CMD_QLEN	0xff
+
+#define SIFIVE_PL2CACHE_FLUSH64_OFF	0x200ULL
+#define SIFIVE_PL2CACHE_STATUS_OFF	0x208ULL
+#define SIFIVE_PL2CACHE_CONFIG1_OFF	0x1000ULL
+#define SIFIVE_PL2CACHE_CONFIG0_OFF	0x1008ULL
+
+#define FLUSH64_CMD_POS			56
+#define REGIONCLOCKDISABLE_MASK		BIT(3)
+
+#define CONFIG0_ACCEPT_DIRTY_DATA_ENABLE	BIT(24)
+
+struct sifive_pl2_quirks {
+	bool no_dirty_fill;
+};
+
+struct sifive_pl2 {
+	struct cache_device dev;
+	void *addr;
+	bool no_dirty_fill;
+};
+
+#define to_pl2(_dev) container_of(_dev, struct sifive_pl2, dev)
+
+static int sifive_pl2_flush_all(struct cache_device *dev)
+{
+	struct sifive_pl2 *pl2_dev = to_pl2(dev);
+	char *addr = pl2_dev->addr;
+	u64 cmd = (FLUSH64_CMD_TARGET_ALL | FLUSH64_CMD_TYPE_FLUSH) << FLUSH64_CMD_POS;
+	u32 config0;
+
+	/*
+	 * While flushing pl2 cache, a speculative load might causes a dirty line pull into PL2. It
+	 * will cause the SiFive SMC0 refuse to enter the power gating. Disable the
+	 * ACCEPT_DIRTY_DATA_ENABLE to avoid the issue.
+	 */
+	if (pl2_dev->no_dirty_fill) {
+		config0 = readl((void *)addr + SIFIVE_PL2CACHE_CONFIG0_OFF);
+		config0 &= ~CONFIG0_ACCEPT_DIRTY_DATA_ENABLE;
+		writel(config0, (void *)addr + SIFIVE_PL2CACHE_CONFIG0_OFF);
+	}
+
+#if __riscv_xlen != 32
+	writeq(cmd, addr + SIFIVE_PL2CACHE_FLUSH64_OFF);
+#else
+	writel((u32)cmd, addr + SIFIVE_PL2CACHE_FLUSH64_OFF);
+	writel((u32)cmd >> 32, addr + SIFIVE_PL2CACHE_FLUSH64_OFF + sizeof(u32));
+#endif
+	do {} while (readl(addr + SIFIVE_PL2CACHE_STATUS_OFF) & SIFIVE_PL2CACHE_CMD_QLEN);
+
+	return 0;
+}
+
+static int sifive_pl2_warm_init(struct cache_device *dev)
+{
+	struct sifive_pl2 *pl2_dev = to_pl2(dev);
+	char *addr = pl2_dev->addr;
+	u32 val;
+
+	/* Enabling the clock gating */
+	val = readl(addr + SIFIVE_PL2CACHE_CONFIG1_OFF);
+	val &= (~REGIONCLOCKDISABLE_MASK);
+	writel(val, addr + SIFIVE_PL2CACHE_CONFIG1_OFF);
+
+	return 0;
+}
+
+static struct cache_ops sifive_pl2_ops = {
+	.warm_init = sifive_pl2_warm_init,
+	.cache_flush_all = sifive_pl2_flush_all,
+};
+
+static int sifive_pl2_cold_init(const void *fdt, int nodeoff, const struct fdt_match *match)
+{
+	const struct sifive_pl2_quirks *quirk = match->data;
+	struct sifive_pl2 *pl2_dev;
+	struct cache_device *dev;
+	u64 reg_addr;
+	int rc;
+
+	/* find the pl2 control base address */
+	rc = fdt_get_node_addr_size(fdt, nodeoff, 0, &reg_addr, NULL);
+	if (rc < 0 && reg_addr)
+		return SBI_ENODEV;
+
+	pl2_dev = sbi_zalloc(sizeof(*pl2_dev));
+	if (!pl2_dev)
+		return SBI_ENOMEM;
+
+	dev = &pl2_dev->dev;
+	dev->ops = &sifive_pl2_ops;
+	dev->cpu_private = true;
+
+	rc = fdt_cache_add(fdt, nodeoff, dev);
+	if (rc)
+		return rc;
+
+	pl2_dev->addr = (void *)(uintptr_t)reg_addr;
+	if (quirk)
+		pl2_dev->no_dirty_fill = quirk->no_dirty_fill;
+
+	return 0;
+}
+
+static const struct sifive_pl2_quirks pl2cache2_quirks = {
+	.no_dirty_fill = true,
+};
+
+static const struct sifive_pl2_quirks pl2cache0_quirks = {
+	.no_dirty_fill = false,
+};
+
+static const struct fdt_match sifive_pl2_match[] = {
+	{ .compatible = "sifive,pl2cache2", .data = &pl2cache2_quirks },
+	{ .compatible = "sifive,pl2cache1", .data = &pl2cache0_quirks },
+	{ .compatible = "sifive,pl2cache0", .data = &pl2cache0_quirks },
+	{},
+};
+
+struct fdt_driver fdt_sifive_pl2 = {
+	.match_table = sifive_pl2_match,
+	.init = sifive_pl2_cold_init,
+};
diff --git a/lib/utils/cache/objects.mk b/lib/utils/cache/objects.mk
index 6829a966784732127d93180b3eb46ed24de5bc7d..37d250d1001bc647e3ae05304b2278e5dc608cae 100644
--- a/lib/utils/cache/objects.mk
+++ b/lib/utils/cache/objects.mk
@@ -11,4 +11,7 @@ libsbiutils-objs-$(CONFIG_FDT_CACHE) += cache/fdt_cmo_helper.o
 carray-fdt_cache_drivers-$(CONFIG_FDT_CACHE_SIFIVE_CCACHE) += fdt_sifive_ccache
 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
+
 libsbiutils-objs-$(CONFIG_CACHE) += cache/cache.o
diff --git a/platform/generic/configs/defconfig b/platform/generic/configs/defconfig
index 3896b0e03268ad3520a3d1586b652e38c0b54ceb..1bb14c78b9f0fc98493961023703a1ed2a24eea8 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_PL2=y
 CONFIG_FDT_CPPC=y
 CONFIG_FDT_CPPC_RPMI=y
 CONFIG_FDT_GPIO=y

-- 
2.34.1




More information about the opensbi mailing list