mirror of
https://github.com/riscv-software-src/opensbi.git
synced 2026-02-27 18:01:45 +00:00
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>
This commit is contained in:
committed by
Anup Patel
parent
6d26b43c47
commit
82b0961821
@@ -11,6 +11,8 @@
|
||||
|
||||
/* clang-format off */
|
||||
|
||||
#define SCRATCH_PAD_OFFSET 0x40
|
||||
|
||||
#define RESET_VEC_LO_OFFSET 0x50
|
||||
#define RESET_VEC_HI_OFFSET 0x60
|
||||
#define RESET_VEC_8CORE_OFFSET 0x1a0
|
||||
@@ -31,6 +33,9 @@
|
||||
|
||||
#define PCS0_WE_OFFSET 0x90
|
||||
#define PCSm_WE_OFFSET(i) ((i + 3) * 0x20 + PCS0_WE_OFFSET)
|
||||
#define PCS_WAKEUP_RTC_ALARM_MASK BIT(2)
|
||||
#define PCS_WAKEUP_UART2_MASK BIT(9)
|
||||
#define PCS_WAKEUP_MSIP_MASK BIT(29)
|
||||
|
||||
#define PCS0_CTL_OFFSET 0x94
|
||||
#define PCSm_CTL_OFFSET(i) ((i + 3) * 0x20 + PCS0_CTL_OFFSET)
|
||||
@@ -38,6 +43,14 @@
|
||||
#define WAKEUP_CMD 0x8
|
||||
#define DEEP_SLEEP_CMD 0xb
|
||||
|
||||
#define PCS0_STATUS_OFFSET 0x98
|
||||
#define PCSm_STATUS_OFFSET(i) ((i + 3) * 0x20 + PCS0_STATUS_OFFSET)
|
||||
#define PD_TYPE_MASK GENMASK(2, 0)
|
||||
#define PD_TYPE_SLEEP 2
|
||||
#define PD_STATUS_MASK GENMASK(7, 3)
|
||||
#define PD_STATUS_LIGHT_SLEEP 0
|
||||
#define PD_STATUS_DEEP_SLEEP 0x10
|
||||
|
||||
/* clang-format on */
|
||||
|
||||
void atcsmu_set_wakeup_events(u32 events, u32 hartid);
|
||||
@@ -45,5 +58,7 @@ bool atcsmu_support_sleep_mode(u32 sleep_type, u32 hartid);
|
||||
void atcsmu_set_command(u32 pcs_ctl, u32 hartid);
|
||||
int atcsmu_set_reset_vector(u64 wakeup_addr, u32 hartid);
|
||||
u32 atcsmu_get_sleep_type(u32 hartid);
|
||||
void atcsmu_write_scratch(u32 value);
|
||||
u32 atcsmu_read_scratch(void);
|
||||
|
||||
#endif
|
||||
|
||||
5
lib/utils/cache/Kconfig
vendored
5
lib/utils/cache/Kconfig
vendored
@@ -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
|
||||
|
||||
166
lib/utils/cache/fdt_andes_llcache.c
vendored
Normal file
166
lib/utils/cache/fdt_andes_llcache.c
vendored
Normal file
@@ -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,
|
||||
};
|
||||
3
lib/utils/cache/objects.mk
vendored
3
lib/utils/cache/objects.mk
vendored
@@ -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
|
||||
|
||||
|
||||
@@ -84,6 +84,16 @@ u32 atcsmu_get_sleep_type(u32 hartid)
|
||||
return readl_relaxed((char *)atcsmu_base + PCSm_SCRATCH_OFFSET(hartid));
|
||||
}
|
||||
|
||||
void atcsmu_write_scratch(u32 value)
|
||||
{
|
||||
writel_relaxed(value, (char *)atcsmu_base + SCRATCH_PAD_OFFSET);
|
||||
}
|
||||
|
||||
u32 atcsmu_read_scratch(void)
|
||||
{
|
||||
return readl_relaxed((char *)atcsmu_base + SCRATCH_PAD_OFFSET);
|
||||
}
|
||||
|
||||
static int ae350_hart_start(u32 hartid, ulong saddr)
|
||||
{
|
||||
u32 hartindex = sbi_hartid_to_hartindex(hartid);
|
||||
|
||||
@@ -13,6 +13,7 @@ CONFIG_PLATFORM_THEAD=y
|
||||
CONFIG_PLATFORM_MIPS_P8700=y
|
||||
CONFIG_PLATFORM_SPACEMIT_K1=y
|
||||
CONFIG_FDT_CACHE=y
|
||||
CONFIG_FDT_CACHE_ANDES_LLCACHE=y
|
||||
CONFIG_FDT_CACHE_SIFIVE_CCACHE=y
|
||||
CONFIG_FDT_CACHE_SIFIVE_EC=y
|
||||
CONFIG_FDT_CACHE_SIFIVE_PL2=y
|
||||
|
||||
Reference in New Issue
Block a user