소스 검색

sifive_cache: add flush_range func in sifive cache driver

Signed-off-by: Wei Fu <wefu@redhat.com>
Wei Fu 2 년 전
부모
커밋
e9b56e55ee
2개의 변경된 파일81개의 추가작업 그리고 1개의 파일을 삭제
  1. 21 0
      arch/riscv/lib/sifive_cache.c
  2. 60 1
      drivers/cache/cache-sifive-ccache.c

+ 21 - 0
arch/riscv/lib/sifive_cache.c

@@ -25,3 +25,24 @@ void enable_caches(void)
 			log_debug("ccache enable failed");
 	}
 }
+
+
+void flush_dcache_range(unsigned long start, unsigned long end)
+{
+	struct udevice *dev;
+	int ret;
+
+	/* Enable ways of ccache */
+	ret = uclass_get_device_by_driver(UCLASS_CACHE,
+					  DM_DRIVER_GET(sifive_ccache),
+					  &dev);
+	if (ret) {
+		log_debug("Cannot flush dcache in %p - %p",
+			  (void *)start, (void *)end);
+	} else {
+		ret = flush_range(dev, start, end);
+		if (ret)
+			log_debug("ccache flush failed in %p - %p",
+				  (void *)start, (void *)end);
+	}
+}

+ 60 - 1
drivers/cache/cache-sifive-ccache.c

@@ -7,16 +7,22 @@
 #include <cache.h>
 #include <dm.h>
 #include <asm/io.h>
-#include <dm/device.h>
 #include <linux/bitfield.h>
+#include <fdt_support.h>
 
 #define SIFIVE_CCACHE_CONFIG		0x000
 #define SIFIVE_CCACHE_CONFIG_WAYS	GENMASK(15, 8)
 
 #define SIFIVE_CCACHE_WAY_ENABLE	0x008
+#define SIFIVE_CCACHE_FLUSH64		0x200
+
+static bool range_check = false;
 
 struct sifive_ccache {
 	void __iomem *base;
+	int cache_line_size;
+	phys_addr_t flush_start; /* Start physical address of flush range limit. */
+	phys_addr_t flush_end; /* End physical address of flush range limit. */
 };
 
 static int sifive_ccache_enable(struct udevice *dev)
@@ -43,19 +49,72 @@ static int sifive_ccache_get_info(struct udevice *dev, struct cache_info *info)
 	return 0;
 }
 
+static int sifive_ccache_flush_range(struct udevice *dev,
+				     unsigned long start, unsigned long end)
+{
+	unsigned long line;
+	volatile unsigned long *flush64;
+	struct sifive_ccache *priv = dev_get_priv(dev);
+
+	if (range_check) {
+		/* make sure the address is in the range */
+		if(start > end ||
+		   start < priv->flush_start || end > priv->flush_end)
+			return -EINVAL;
+	} else {
+		pr_warn("skip checking range.");
+	}
+
+	/* make sure we won't get into infinite loop below */
+	if (!priv->cache_line_size) {
+		pr_warn("missing cache_line_size, skip flush.");
+		return -EINVAL;
+	}
+
+	flush64 = (volatile unsigned long *)(priv->base + SIFIVE_CCACHE_FLUSH64);
+
+	/* memory barrier */
+	mb();
+	for (line = start; line < end; line += priv->cache_line_size)
+		(*flush64) = line;
+	/* memory barrier */
+	mb();
+
+	return 0;
+}
+
 static const struct cache_ops sifive_ccache_ops = {
 	.enable = sifive_ccache_enable,
 	.get_info = sifive_ccache_get_info,
+	.flush_range = sifive_ccache_flush_range,
 };
 
 static int sifive_ccache_probe(struct udevice *dev)
 {
+	int ret;
+	uint64_t addr, len;
 	struct sifive_ccache *priv = dev_get_priv(dev);
 
 	priv->base = dev_read_addr_ptr(dev);
 	if (!priv->base)
 		return -EINVAL;
 
+	priv->cache_line_size =
+		dev_read_u32_default(dev, "cache-line-size",
+				     CONFIG_SYS_CACHELINE_SIZE);
+
+	/* only read range index 0 */
+	ret = fdt_read_range((void *)gd->fdt_blob, dev_of_offset(dev), 0,
+			     NULL, &addr, &len);
+	if (ret) {
+		pr_warn("missing flush range, ignore range check.");
+		return 0;
+	}
+
+	range_check = true;
+	priv->flush_start = addr;
+	priv->flush_end = addr + len - 1;
+
 	return 0;
 }