[PATCH 4/6] cache: andes-l2: Add writeback-invalidate operation

Leo Yu-Chi Liang ycliang at andestech.com
Thu Mar 19 09:37:11 CET 2026


Add a wbinval operation to the cache uclass that performs
writeback-invalidate without the disable/enable cycle that the
existing disable path uses. This is needed for flush_dcache_all()
to properly flush both L1 and L2 caches.

Implement andes_l2_wbinval_all() in the Andes L2 cache driver
which issues the L2_WBINVAL_ALL command while keeping the cache
enabled.

Update flush_dcache_all() in the Andes cache code to also call
L2 wbinval when CONFIG_ANDES_L2_CACHE is enabled.

Signed-off-by: Leo Yu-Chi Liang <ycliang at andestech.com>
---
 arch/riscv/cpu/andes/cache.c   |  4 ++++
 drivers/cache/cache-andes-l2.c | 24 ++++++++++++++++++++++++
 drivers/cache/cache-uclass.c   | 10 ++++++++++
 include/cache.h                | 16 ++++++++++++++++
 4 files changed, 54 insertions(+)

diff --git a/arch/riscv/cpu/andes/cache.c b/arch/riscv/cpu/andes/cache.c
index bb57498d75a..3b325d77954 100644
--- a/arch/riscv/cpu/andes/cache.c
+++ b/arch/riscv/cpu/andes/cache.c
@@ -44,6 +44,10 @@ static void cache_ops(int (*ops)(struct udevice *dev))
 void flush_dcache_all(void)
 {
 	csr_write(CSR_UCCTLCOMMAND, CCTL_L1D_WBINVAL_ALL);
+
+#ifdef CONFIG_ANDES_L2_CACHE
+	cache_ops(cache_wbinval);
+#endif
 }
 
 void flush_dcache_range(unsigned long start, unsigned long end)
diff --git a/drivers/cache/cache-andes-l2.c b/drivers/cache/cache-andes-l2.c
index 45a4f216b07..5ceccf609e5 100644
--- a/drivers/cache/cache-andes-l2.c
+++ b/drivers/cache/cache-andes-l2.c
@@ -91,6 +91,29 @@ static int andes_l2_enable(struct udevice *dev)
 	return 0;
 }
 
+static int andes_l2_wbinval_all(struct udevice *dev)
+{
+	struct andes_l2_plat *plat = dev_get_plat(dev);
+	volatile struct l2cache *regs = plat->regs;
+	u8 hart = gd->arch.boot_hart;
+
+	void __iomem *cctlcmd = (void __iomem *)CCTL_CMD_REG(regs, hart);
+	void __iomem *cctlstatus = (void __iomem *)CCTL_STATUS_REG(regs, hart);
+
+	if ((regs) && (readl(&regs->control) & L2_ENABLE)) {
+		writel(L2_WBINVAL_ALL, cctlcmd);
+
+		while ((readl(cctlstatus) & CCTL_STATUS_MSK(hart))) {
+			if ((readl(cctlstatus) & CCTL_STATUS_ILLEGAL(hart))) {
+				printf("L2 flush illegal! hanging...");
+				hang();
+			}
+		}
+	}
+
+	return 0;
+}
+
 static int andes_l2_disable(struct udevice *dev)
 {
 	struct andes_l2_plat *plat = dev_get_plat(dev);
@@ -192,6 +215,7 @@ static const struct udevice_id andes_l2_cache_ids[] = {
 static const struct cache_ops andes_l2_cache_ops = {
 	.enable		= andes_l2_enable,
 	.disable	= andes_l2_disable,
+	.wbinval	= andes_l2_wbinval_all,
 };
 
 U_BOOT_DRIVER(andes_l2_cache) = {
diff --git a/drivers/cache/cache-uclass.c b/drivers/cache/cache-uclass.c
index 300e7bc86e1..8e77c9e2f4e 100644
--- a/drivers/cache/cache-uclass.c
+++ b/drivers/cache/cache-uclass.c
@@ -38,6 +38,16 @@ int cache_disable(struct udevice *dev)
 	return ops->disable(dev);
 }
 
+int cache_wbinval(struct udevice *dev)
+{
+	struct cache_ops *ops = cache_get_ops(dev);
+
+	if (!ops->wbinval)
+		return -ENOSYS;
+
+	return ops->wbinval(dev);
+}
+
 UCLASS_DRIVER(cache) = {
 	.id		= UCLASS_CACHE,
 	.name		= "cache",
diff --git a/include/cache.h b/include/cache.h
index 296ae3c8b48..45b27ab989c 100644
--- a/include/cache.h
+++ b/include/cache.h
@@ -42,6 +42,14 @@ struct cache_ops {
 	 * @return 0 if OK, -ve on error
 	 */
 	int (*disable)(struct udevice *dev);
+
+	/**
+	 * wbinval() - Writeback and invalidate cache
+	 *
+	 * @dev:	Device to check (UCLASS_CACHE)
+	 * @return 0 if OK, -ve on error
+	 */
+	int (*wbinval)(struct udevice *dev);
 };
 
 #define cache_get_ops(dev)	((struct cache_ops *)(dev)->driver->ops)
@@ -70,4 +78,12 @@ int cache_enable(struct udevice *dev);
  * Return: 0 if OK, -ve on error
  */
 int cache_disable(struct udevice *dev);
+
+/**
+ * cache_wbinval() - Writeback and invalidate cache
+ *
+ * @dev:	Device to check (UCLASS_CACHE)
+ * Return: 0 if OK, -ve on error
+ */
+int cache_wbinval(struct udevice *dev);
 #endif
-- 
2.34.1



More information about the U-Boot mailing list