cache: Add StarFive StarLink cache management
authorJoshua Yeong <joshua.yeong@starfivetech.com>
Wed, 15 May 2024 05:02:52 +0000 (13:02 +0800)
committerConor Dooley <conor.dooley@microchip.com>
Tue, 28 May 2024 11:34:11 +0000 (12:34 +0100)
Add StarFive Starlink cache management driver.
The driver enables RISC-V non-standard cache
operation on SoC that does not support Zicbom
extension instructions.

Signed-off-by: Joshua Yeong <joshua.yeong@starfivetech.com>
Signed-off-by: Conor Dooley <conor.dooley@microchip.com>
drivers/cache/Kconfig
drivers/cache/Makefile
drivers/cache/starfive_starlink_cache.c [new file with mode: 0644]

index 9345ce4976d76d7d56b39c373e1d5bf8a374c1a5..94abd8f632a7b3eb02b7e387402cee431009ee8a 100644 (file)
@@ -14,4 +14,13 @@ config SIFIVE_CCACHE
        help
          Support for the composable cache controller on SiFive platforms.
 
+config STARFIVE_STARLINK_CACHE
+       bool "StarFive StarLink Cache controller"
+       depends on RISCV
+       depends on ARCH_STARFIVE
+       select RISCV_DMA_NONCOHERENT
+       select RISCV_NONSTANDARD_CACHE_OPS
+       help
+         Support for the StarLink cache controller IP from StarFive.
+
 endmenu
index 7657cff3bd6c74e964db7c833bee79dc6abe7fee..55c5e851034da4f9918ad419928b811f46c1e39c 100644 (file)
@@ -1,4 +1,5 @@
 # SPDX-License-Identifier: GPL-2.0
 
-obj-$(CONFIG_AX45MP_L2_CACHE)  += ax45mp_cache.o
-obj-$(CONFIG_SIFIVE_CCACHE)    += sifive_ccache.o
+obj-$(CONFIG_AX45MP_L2_CACHE)          += ax45mp_cache.o
+obj-$(CONFIG_SIFIVE_CCACHE)            += sifive_ccache.o
+obj-$(CONFIG_STARFIVE_STARLINK_CACHE)  += starfive_starlink_cache.o
diff --git a/drivers/cache/starfive_starlink_cache.c b/drivers/cache/starfive_starlink_cache.c
new file mode 100644 (file)
index 0000000..24c7d07
--- /dev/null
@@ -0,0 +1,130 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Cache Management Operations for StarFive's Starlink cache controller
+ *
+ * Copyright (C) 2024 Shanghai StarFive Technology Co., Ltd.
+ *
+ * Author: Joshua Yeong <joshua.yeong@starfivetech.com>
+ */
+
+#include <linux/bitfield.h>
+#include <linux/cacheflush.h>
+#include <linux/iopoll.h>
+#include <linux/of_address.h>
+
+#include <asm/dma-noncoherent.h>
+
+#define STARLINK_CACHE_FLUSH_START_ADDR                        0x0
+#define STARLINK_CACHE_FLUSH_END_ADDR                  0x8
+#define STARLINK_CACHE_FLUSH_CTL                       0x10
+#define STARLINK_CACHE_ALIGN                           0x40
+
+#define STARLINK_CACHE_ADDRESS_RANGE_MASK              GENMASK(39, 0)
+#define STARLINK_CACHE_FLUSH_CTL_MODE_MASK             GENMASK(2, 1)
+#define STARLINK_CACHE_FLUSH_CTL_ENABLE_MASK           BIT(0)
+
+#define STARLINK_CACHE_FLUSH_CTL_CLEAN_INVALIDATE      0
+#define STARLINK_CACHE_FLUSH_CTL_MAKE_INVALIDATE       1
+#define STARLINK_CACHE_FLUSH_CTL_CLEAN_SHARED          2
+#define STARLINK_CACHE_FLUSH_POLL_DELAY_US             1
+#define STARLINK_CACHE_FLUSH_TIMEOUT_US                        5000000
+
+static void __iomem *starlink_cache_base;
+
+static void starlink_cache_flush_complete(void)
+{
+       volatile void __iomem *ctl = starlink_cache_base + STARLINK_CACHE_FLUSH_CTL;
+       u64 v;
+       int ret;
+
+       ret = readq_poll_timeout_atomic(ctl, v, !(v & STARLINK_CACHE_FLUSH_CTL_ENABLE_MASK),
+                                       STARLINK_CACHE_FLUSH_POLL_DELAY_US,
+                                       STARLINK_CACHE_FLUSH_TIMEOUT_US);
+       if (ret)
+               WARN(1, "StarFive Starlink cache flush operation timeout\n");
+}
+
+static void starlink_cache_dma_cache_wback(phys_addr_t paddr, unsigned long size)
+{
+       writeq(FIELD_PREP(STARLINK_CACHE_ADDRESS_RANGE_MASK, paddr),
+              starlink_cache_base + STARLINK_CACHE_FLUSH_START_ADDR);
+       writeq(FIELD_PREP(STARLINK_CACHE_ADDRESS_RANGE_MASK, paddr + size),
+              starlink_cache_base + STARLINK_CACHE_FLUSH_END_ADDR);
+
+       mb();
+       writeq(FIELD_PREP(STARLINK_CACHE_FLUSH_CTL_MODE_MASK,
+                         STARLINK_CACHE_FLUSH_CTL_CLEAN_SHARED),
+              starlink_cache_base + STARLINK_CACHE_FLUSH_CTL);
+
+       starlink_cache_flush_complete();
+}
+
+static void starlink_cache_dma_cache_invalidate(phys_addr_t paddr, unsigned long size)
+{
+       writeq(FIELD_PREP(STARLINK_CACHE_ADDRESS_RANGE_MASK, paddr),
+              starlink_cache_base + STARLINK_CACHE_FLUSH_START_ADDR);
+       writeq(FIELD_PREP(STARLINK_CACHE_ADDRESS_RANGE_MASK, paddr + size),
+              starlink_cache_base + STARLINK_CACHE_FLUSH_END_ADDR);
+
+       mb();
+       writeq(FIELD_PREP(STARLINK_CACHE_FLUSH_CTL_MODE_MASK,
+                         STARLINK_CACHE_FLUSH_CTL_MAKE_INVALIDATE),
+              starlink_cache_base + STARLINK_CACHE_FLUSH_CTL);
+
+       starlink_cache_flush_complete();
+}
+
+static void starlink_cache_dma_cache_wback_inv(phys_addr_t paddr, unsigned long size)
+{
+       writeq(FIELD_PREP(STARLINK_CACHE_ADDRESS_RANGE_MASK, paddr),
+              starlink_cache_base + STARLINK_CACHE_FLUSH_START_ADDR);
+       writeq(FIELD_PREP(STARLINK_CACHE_ADDRESS_RANGE_MASK, paddr + size),
+              starlink_cache_base + STARLINK_CACHE_FLUSH_END_ADDR);
+
+       mb();
+       writeq(FIELD_PREP(STARLINK_CACHE_FLUSH_CTL_MODE_MASK,
+                         STARLINK_CACHE_FLUSH_CTL_CLEAN_INVALIDATE),
+              starlink_cache_base + STARLINK_CACHE_FLUSH_CTL);
+
+       starlink_cache_flush_complete();
+}
+
+static const struct riscv_nonstd_cache_ops starlink_cache_ops = {
+       .wback = &starlink_cache_dma_cache_wback,
+       .inv = &starlink_cache_dma_cache_invalidate,
+       .wback_inv = &starlink_cache_dma_cache_wback_inv,
+};
+
+static const struct of_device_id starlink_cache_ids[] = {
+       { .compatible = "starfive,jh8100-starlink-cache" },
+       { /* sentinel */ }
+};
+
+static int __init starlink_cache_init(void)
+{
+       struct device_node *np;
+       u32 block_size;
+       int ret;
+
+       np = of_find_matching_node(NULL, starlink_cache_ids);
+       if (!of_device_is_available(np))
+               return -ENODEV;
+
+       ret = of_property_read_u32(np, "cache-block-size", &block_size);
+       if (ret)
+               return ret;
+
+       if (block_size % STARLINK_CACHE_ALIGN)
+               return -EINVAL;
+
+       starlink_cache_base = of_iomap(np, 0);
+       if (!starlink_cache_base)
+               return -ENOMEM;
+
+       riscv_cbom_block_size = block_size;
+       riscv_noncoherent_supported();
+       riscv_noncoherent_register_cache_ops(&starlink_cache_ops);
+
+       return 0;
+}
+arch_initcall(starlink_cache_init);