sifive_cache: add flush_range func in sifive cache driver

Signed-off-by: Wei Fu <wefu@redhat.com>
This commit is contained in:
Wei Fu 2021-09-19 14:13:15 +08:00 committed by Tekkaman Ninja
parent 1aea567a29
commit e9b56e55ee
2 changed files with 81 additions and 1 deletions

View file

@ -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);
}
}

View file

@ -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;
}