lib: utils/cache: Add SiFive PL2 controller

SiFive Private L2(PL2) cache is a private cache owned by each hart. Add
this driver to support private cache flush operations via the MMIO
registers.

Co-developed-by: Eric Lin <eric.lin@sifive.com>
Signed-off-by: Eric Lin <eric.lin@sifive.com>
Co-developed-by: Zong Li <zong.li@sifive.com>
Signed-off-by: Zong Li <zong.li@sifive.com>
Co-developed-by: Vincent Chen <vincent.chen@sifive.com>
Signed-off-by: Vincent Chen <vincent.chen@sifive.com>
Co-developed-by: Samuel Holland <samuel.holland@sifive.com>
Signed-off-by: Samuel Holland <samuel.holland@sifive.com>
Signed-off-by: Nick Hu <nick.hu@sifive.com>
Reviewed-by: Anup Patel <anup@brainfault.org>
Link: https://lore.kernel.org/r/20251114-sifive-cache-drivers-v1-2-8423a721924c@sifive.com
Signed-off-by: Anup Patel <anup@brainfault.org>
This commit is contained in:
Nick Hu
2025-11-14 11:22:46 +08:00
committed by Anup Patel
parent 35aece218a
commit ec51e91eaa
4 changed files with 147 additions and 0 deletions

View File

@@ -14,6 +14,10 @@ config FDT_CACHE_SIFIVE_CCACHE
bool "SiFive CCACHE FDT cache driver"
default n
config FDT_CACHE_SIFIVE_PL2
bool "SiFive PL2 FDT cache driver"
default n
endif
config CACHE

139
lib/utils/cache/fdt_sifive_pl2.c vendored Normal file
View File

@@ -0,0 +1,139 @@
/*
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2025 SiFive Inc.
*/
#include <libfdt.h>
#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>
#define FLUSH64_CMD_TARGET_ALL (0x2 << 3)
#define FLUSH64_CMD_TYPE_FLUSH 0x3ULL
#define SIFIVE_PL2CACHE_CMD_QLEN 0xff
#define SIFIVE_PL2CACHE_FLUSH64_OFF 0x200ULL
#define SIFIVE_PL2CACHE_STATUS_OFF 0x208ULL
#define SIFIVE_PL2CACHE_CONFIG1_OFF 0x1000ULL
#define SIFIVE_PL2CACHE_CONFIG0_OFF 0x1008ULL
#define FLUSH64_CMD_POS 56
#define REGIONCLOCKDISABLE_MASK BIT(3)
#define CONFIG0_ACCEPT_DIRTY_DATA_ENABLE BIT(24)
struct sifive_pl2_quirks {
bool no_dirty_fill;
};
struct sifive_pl2 {
struct cache_device dev;
void *addr;
bool no_dirty_fill;
};
#define to_pl2(_dev) container_of(_dev, struct sifive_pl2, dev)
static int sifive_pl2_flush_all(struct cache_device *dev)
{
struct sifive_pl2 *pl2_dev = to_pl2(dev);
char *addr = pl2_dev->addr;
u64 cmd = (FLUSH64_CMD_TARGET_ALL | FLUSH64_CMD_TYPE_FLUSH) << FLUSH64_CMD_POS;
u32 config0;
/*
* While flushing pl2 cache, a speculative load might causes a dirty line pull
* into PL2. It will cause the SiFive SMC0 refuse to enter the power gating.
* Disable the ACCEPT_DIRTY_DATA_ENABLE to avoid the issue.
*/
if (pl2_dev->no_dirty_fill) {
config0 = readl((void *)addr + SIFIVE_PL2CACHE_CONFIG0_OFF);
config0 &= ~CONFIG0_ACCEPT_DIRTY_DATA_ENABLE;
writel(config0, (void *)addr + SIFIVE_PL2CACHE_CONFIG0_OFF);
}
#if __riscv_xlen != 32
writeq(cmd, addr + SIFIVE_PL2CACHE_FLUSH64_OFF);
#else
writel((u32)cmd, addr + SIFIVE_PL2CACHE_FLUSH64_OFF);
writel((u32)(cmd >> 32), addr + SIFIVE_PL2CACHE_FLUSH64_OFF + sizeof(u32));
#endif
do {} while (readl(addr + SIFIVE_PL2CACHE_STATUS_OFF) & SIFIVE_PL2CACHE_CMD_QLEN);
return 0;
}
static int sifive_pl2_warm_init(struct cache_device *dev)
{
struct sifive_pl2 *pl2_dev = to_pl2(dev);
char *addr = pl2_dev->addr;
u32 val;
/* Enabling the clock gating */
val = readl(addr + SIFIVE_PL2CACHE_CONFIG1_OFF);
val &= (~REGIONCLOCKDISABLE_MASK);
writel(val, addr + SIFIVE_PL2CACHE_CONFIG1_OFF);
return 0;
}
static struct cache_ops sifive_pl2_ops = {
.warm_init = sifive_pl2_warm_init,
.cache_flush_all = sifive_pl2_flush_all,
};
static int sifive_pl2_cold_init(const void *fdt, int nodeoff, const struct fdt_match *match)
{
const struct sifive_pl2_quirks *quirk = match->data;
struct sifive_pl2 *pl2_dev;
struct cache_device *dev;
u64 reg_addr;
int rc;
/* find the pl2 control base address */
rc = fdt_get_node_addr_size(fdt, nodeoff, 0, &reg_addr, NULL);
if (rc < 0 && reg_addr)
return SBI_ENODEV;
pl2_dev = sbi_zalloc(sizeof(*pl2_dev));
if (!pl2_dev)
return SBI_ENOMEM;
dev = &pl2_dev->dev;
dev->ops = &sifive_pl2_ops;
dev->cpu_private = true;
rc = fdt_cache_add(fdt, nodeoff, dev);
if (rc)
return rc;
pl2_dev->addr = (void *)(uintptr_t)reg_addr;
if (quirk)
pl2_dev->no_dirty_fill = quirk->no_dirty_fill;
return 0;
}
static const struct sifive_pl2_quirks pl2cache2_quirks = {
.no_dirty_fill = true,
};
static const struct sifive_pl2_quirks pl2cache0_quirks = {
.no_dirty_fill = false,
};
static const struct fdt_match sifive_pl2_match[] = {
{ .compatible = "sifive,pl2cache2", .data = &pl2cache2_quirks },
{ .compatible = "sifive,pl2cache1", .data = &pl2cache0_quirks },
{ .compatible = "sifive,pl2cache0", .data = &pl2cache0_quirks },
{},
};
struct fdt_driver fdt_sifive_pl2 = {
.match_table = sifive_pl2_match,
.init = sifive_pl2_cold_init,
};

View File

@@ -11,4 +11,7 @@ libsbiutils-objs-$(CONFIG_FDT_CACHE) += cache/fdt_cmo_helper.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
carray-fdt_cache_drivers-$(CONFIG_FDT_CACHE_SIFIVE_PL2) += fdt_sifive_pl2
libsbiutils-objs-$(CONFIG_FDT_CACHE_SIFIVE_PL2) += cache/fdt_sifive_pl2.o
libsbiutils-objs-$(CONFIG_CACHE) += cache/cache.o

View File

@@ -13,6 +13,7 @@ CONFIG_PLATFORM_MIPS_P8700=y
CONFIG_PLATFORM_SPACEMIT_K1=y
CONFIG_FDT_CACHE=y
CONFIG_FDT_CACHE_SIFIVE_CCACHE=y
CONFIG_FDT_CACHE_SIFIVE_PL2=y
CONFIG_FDT_CPPC=y
CONFIG_FDT_CPPC_RPMI=y
CONFIG_FDT_GPIO=y