aboutsummaryrefslogtreecommitdiff
path: root/lib/utils/cache
diff options
context:
space:
mode:
authorBen Zong-You Xie <ben717@andestech.com>2025-12-29 15:19:13 +0800
committerAnup Patel <anup@brainfault.org>2026-02-11 12:13:01 +0530
commit82b0961821e45768a91139b8ec5d7784586aa7f9 (patch)
tree35a635661eb8e7c35b0fb9a8f4611939766014c5 /lib/utils/cache
parent6d26b43c477a7616d1f030261430127fa6c61d27 (diff)
downloadopensbi-82b0961821e45768a91139b8ec5d7784586aa7f9.tar.gz
opensbi-82b0961821e45768a91139b8ec5d7784586aa7f9.tar.bz2
opensbi-82b0961821e45768a91139b8ec5d7784586aa7f9.zip
lib: utils/cache: add Andes last level cache controller
Introduce a FDT-based driver for the Andes Last Level Cache (LLC) controller to support cache maintenance operations. Signed-off-by: Ben Zong-You Xie <ben717@andestech.com> Link: https://lore.kernel.org/r/20251229071914.1451587-5-ben717@andestech.com Signed-off-by: Anup Patel <anup@brainfault.org>
Diffstat (limited to 'lib/utils/cache')
-rw-r--r--lib/utils/cache/Kconfig5
-rw-r--r--lib/utils/cache/fdt_andes_llcache.c166
-rw-r--r--lib/utils/cache/objects.mk3
3 files changed, 174 insertions, 0 deletions
diff --git a/lib/utils/cache/Kconfig b/lib/utils/cache/Kconfig
index be7d57c3..ea815dc4 100644
--- a/lib/utils/cache/Kconfig
+++ b/lib/utils/cache/Kconfig
@@ -10,6 +10,11 @@ config FDT_CACHE
if FDT_CACHE
+config FDT_CACHE_ANDES_LLCACHE
+ bool "Andes FDT last level cache driver"
+ depends on FDT_HSM_ANDES_ATCSMU
+ default n
+
config FDT_CACHE_SIFIVE_CCACHE
bool "SiFive CCACHE FDT cache driver"
default n
diff --git a/lib/utils/cache/fdt_andes_llcache.c b/lib/utils/cache/fdt_andes_llcache.c
new file mode 100644
index 00000000..490503ee
--- /dev/null
+++ b/lib/utils/cache/fdt_andes_llcache.c
@@ -0,0 +1,166 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2025 Andes Technology Corporation
+ */
+
+#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>
+#include <sbi_utils/hsm/fdt_hsm_andes_atcsmu.h>
+
+#define LLCACHE_REG_CFG_OFFSET 0x0
+#define LLCACHE_REG_CTRL_OFFSET 0x8
+#define LLCACHE_REG_CCTL_CMD_OFFSET 0x40
+#define LLCACHE_REG_CCTL_STATUS_OFFSET 0x80
+
+#define LLCACHE_REG_CFG_MAP_MASK BIT(20)
+#define LLCACHE_REG_CTRL_EN_MASK BIT(0)
+#define LLCACHE_REG_CTRL_INIT_MASK BIT(14)
+#define LLCACHE_REG_CCTL_STATUS_MASK GENMASK(3, 0)
+
+#define LLCACHE_WBINVAL_ALL 0x12
+
+struct andes_llcache {
+ struct cache_device dev;
+ void *base;
+ uint32_t cmd_stride;
+ uint32_t status_stride;
+ uint32_t status_core_stride;
+};
+
+#define to_llcache(_dev) container_of(_dev, struct andes_llcache, dev)
+
+static bool andes_llcache_init_done(struct andes_llcache *llcache)
+{
+ uint32_t llcache_ctrl;
+ void *ctrl_addr = (char *)llcache->base + LLCACHE_REG_CTRL_OFFSET;
+
+ llcache_ctrl = readl_relaxed(ctrl_addr);
+ return !EXTRACT_FIELD(llcache_ctrl, LLCACHE_REG_CTRL_INIT_MASK);
+}
+
+static bool andes_llcache_cctl_done(struct andes_llcache *llcache, uint32_t hartid)
+{
+ uint32_t llcache_cctl_status;
+ void *cctl_status_addr = (char *)llcache->base + LLCACHE_REG_CCTL_STATUS_OFFSET +
+ hartid * llcache->status_stride;
+
+ llcache_cctl_status = readl_relaxed(cctl_status_addr);
+ return !EXTRACT_FIELD(llcache_cctl_status,
+ LLCACHE_REG_CCTL_STATUS_MASK <<
+ hartid * llcache->status_core_stride);
+}
+
+static int andes_llcache_flush_all(struct cache_device *dev)
+{
+ uint32_t hartid = current_hartid();
+ struct andes_llcache *llcache = to_llcache(dev);
+ void *cctl_cmd_addr = (char *)llcache->base + LLCACHE_REG_CCTL_CMD_OFFSET +
+ hartid * llcache->cmd_stride;
+
+ /*
+ * Each command register corresponds to one CPU core, so each CPU core
+ * should only use its command registers to do the cache operation.
+ */
+ writel(LLCACHE_WBINVAL_ALL, cctl_cmd_addr);
+
+ /* Wait for the command completion */
+ while (!andes_llcache_cctl_done(llcache, hartid))
+ ;
+
+ return 0;
+}
+
+static int andes_llcache_enable(struct cache_device *dev, bool enable)
+{
+ struct andes_llcache *llcache = to_llcache(dev);
+ u32 llcache_ctrl;
+ void *ctrl_addr = (char *)llcache->base + LLCACHE_REG_CTRL_OFFSET;
+
+ /*
+ * To properly enable the last level cache to cache both instructions
+ * and data, apply the following sequence:
+ *
+ * - Write the control register with the desired value, except the
+ * CEN field should be set to zero. Thus, store the control register
+ * value with the CEN field being 0 when disabling the last level
+ * cache.
+ * - Write the control register again using the same value of step 1
+ * with the CEN field being 1.
+ */
+ if (enable) {
+ llcache_ctrl = atcsmu_read_scratch();
+ writel(llcache_ctrl, ctrl_addr);
+ writel(llcache_ctrl | LLCACHE_REG_CTRL_EN_MASK, ctrl_addr);
+ } else {
+ llcache_ctrl = readl(ctrl_addr);
+ atcsmu_write_scratch(llcache_ctrl & ~LLCACHE_REG_CTRL_EN_MASK);
+ writel(llcache_ctrl & ~LLCACHE_REG_CTRL_EN_MASK, ctrl_addr);
+ }
+
+ llcache_ctrl = readl(ctrl_addr);
+ return enable == EXTRACT_FIELD(llcache_ctrl, LLCACHE_REG_CTRL_EN_MASK);
+}
+
+static struct cache_ops andes_llcache_ops = {
+ .cache_flush_all = andes_llcache_flush_all,
+ .cache_enable = andes_llcache_enable,
+};
+
+static int andes_llcache_probe(const void *fdt, int nodeoff, const struct fdt_match *match)
+{
+ int rc;
+ u64 llcache_base = 0;
+ struct andes_llcache *llcache;
+ struct cache_device *dev;
+ uint32_t llcache_cfg;
+
+ rc = fdt_get_node_addr_size(fdt, nodeoff, 0, &llcache_base, NULL);
+ if (rc < 0 || !llcache_base)
+ return SBI_ENODEV;
+
+ llcache = sbi_zalloc(sizeof(*llcache));
+ if (!llcache)
+ return SBI_ENOMEM;
+
+ dev = &llcache->dev;
+ dev->ops = &andes_llcache_ops;
+ rc = fdt_cache_add(fdt, nodeoff, dev);
+ if (rc) {
+ sbi_free(llcache);
+ return rc;
+ }
+
+ llcache->base = (void *)(ulong)llcache_base;
+ llcache_cfg = readl_relaxed((char *)llcache->base + LLCACHE_REG_CFG_OFFSET);
+
+ /* Configurations for V1/V0 memory map */
+ if (EXTRACT_FIELD(llcache_cfg, LLCACHE_REG_CFG_MAP_MASK)) {
+ llcache->cmd_stride = 0x1000;
+ llcache->status_stride = 0x1000;
+ llcache->status_core_stride = 0;
+ } else {
+ llcache->cmd_stride = 0x10;
+ llcache->status_stride = 0x0;
+ llcache->status_core_stride = 4;
+ }
+
+ /* Wait for the hardware initialization done */
+ while (!andes_llcache_init_done(llcache))
+ ;
+
+ return SBI_OK;
+}
+
+static const struct fdt_match andes_llcache_match[] = {
+ { .compatible = "andestech,llcache" },
+ {},
+};
+
+const struct fdt_driver fdt_andes_llcache = {
+ .match_table = andes_llcache_match,
+ .init = andes_llcache_probe,
+};
diff --git a/lib/utils/cache/objects.mk b/lib/utils/cache/objects.mk
index aa76adc2..6c9bce84 100644
--- a/lib/utils/cache/objects.mk
+++ b/lib/utils/cache/objects.mk
@@ -8,6 +8,9 @@ libsbiutils-objs-$(CONFIG_FDT_CACHE) += cache/fdt_cache.o
libsbiutils-objs-$(CONFIG_FDT_CACHE) += cache/fdt_cache_drivers.carray.o
libsbiutils-objs-$(CONFIG_FDT_CACHE) += cache/fdt_cmo_helper.o
+carray-fdt_cache_drivers-$(CONFIG_FDT_CACHE_ANDES_LLCACHE) += fdt_andes_llcache
+libsbiutils-objs-$(CONFIG_FDT_CACHE_ANDES_LLCACHE) += cache/fdt_andes_llcache.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