opensbi/lib/utils/cache/fdt_sifive_ccache.c
Vincent Chen 8ea972838c utils: cache: Add SiFive ccache controller
SiFive Composable cache is a L3 share cache of the core complex. Add this
driver to support the share cache maintenance operations via the MMIO
registers.

Co-developed-by: Samuel Holland <samuel.holland@sifive.com>
Signed-off-by: Samuel Holland <samuel.holland@sifive.com>
Signed-off-by: Vincent Chen <vincent.chen@sifive.com>
Co-developed-by: Nick Hu <nick.hu@sifive.com>
Signed-off-by: Nick Hu <nick.hu@sifive.com>
Reviewed-by: Samuel Holland <samuel.holland@sifive.com>
Reviewed-by: Anup Patel <anup@brainfault.org>
Link: https://lore.kernel.org/r/20251020-cache-upstream-v7-3-69a132447d8a@sifive.com
Signed-off-by: Anup Patel <anup@brainfault.org>
2025-10-28 11:27:51 +05:30

176 lines
5.2 KiB
C

/*
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2025 SiFive Inc.
*/
#include <libfdt.h>
#include <sbi/riscv_barrier.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 CCACHE_CFG_CSR 0
#define CCACHE_CMD_CSR 0x280
#define CCACHE_STATUS_CSR 0x288
#define CFG_CSR_BANK_MASK 0xff
#define CFG_CSR_WAY_MASK 0xff00
#define CFG_CSR_WAY_OFFSET 8
#define CFG_CSR_SET_MASK 0xff0000
#define CFG_CSR_SET_OFFSET 16
#define CMD_CSR_CMD_OFFSET 56
#define CMD_CSR_BANK_OFFSET 6
#define CMD_OPCODE_SETWAY 0x1ULL
#define CMD_OPCODE_OFFSET 0x2ULL
#define CFLUSH_SETWAY_CLEANINV ((CMD_OPCODE_SETWAY << CMD_OPCODE_OFFSET) | 0x3)
#define CCACHE_CMD_QLEN 0xff
#define ccache_mb_b() RISCV_FENCE(rw, o)
#define ccache_mb_a() RISCV_FENCE(o, rw)
#define CCACHE_ALL_OP_REQ_BATCH_NUM 0x10
#define CCACHE_ALL_OP_REQ_BATCH_MASK (CCACHE_CMD_QLEN + 1 - CCACHE_ALL_OP_REQ_BATCH_NUM)
struct sifive_ccache {
struct cache_device dev;
void *addr;
u64 total_lines;
};
#define to_ccache(_dev) container_of(_dev, struct sifive_ccache, dev)
static inline unsigned int sifive_ccache_read_status(void *status_addr)
{
return readl_relaxed(status_addr);
}
static inline void sifive_ccache_write_cmd(u64 cmd, void *cmd_csr_addr)
{
#if __riscv_xlen != 32
writeq_relaxed(cmd, cmd_csr_addr);
#else
/*
* The cache maintenance request is only generated when the "command"
* field (part of the high word) is written.
*/
writel_relaxed(cmd, cmd_csr_addr);
writel(cmd >> 32, cmd_csr_addr + 4);
#endif
}
static int sifive_ccache_flush_all(struct cache_device *dev)
{
struct sifive_ccache *ccache = to_ccache(dev);
void *status_addr = (char *)ccache->addr + CCACHE_STATUS_CSR;
void *cmd_csr_addr = (char *)ccache->addr + CCACHE_CMD_CSR;
u64 total_cnt = ccache->total_lines;
u64 cmd = CFLUSH_SETWAY_CLEANINV << CMD_CSR_CMD_OFFSET;
int loop_cnt = CCACHE_CMD_QLEN & CCACHE_ALL_OP_REQ_BATCH_MASK;
ccache_mb_b();
send_cmd:
total_cnt -= loop_cnt;
while (loop_cnt > 0) {
sifive_ccache_write_cmd(cmd + (0 << CMD_CSR_BANK_OFFSET), cmd_csr_addr);
sifive_ccache_write_cmd(cmd + (1 << CMD_CSR_BANK_OFFSET), cmd_csr_addr);
sifive_ccache_write_cmd(cmd + (2 << CMD_CSR_BANK_OFFSET), cmd_csr_addr);
sifive_ccache_write_cmd(cmd + (3 << CMD_CSR_BANK_OFFSET), cmd_csr_addr);
sifive_ccache_write_cmd(cmd + (4 << CMD_CSR_BANK_OFFSET), cmd_csr_addr);
sifive_ccache_write_cmd(cmd + (5 << CMD_CSR_BANK_OFFSET), cmd_csr_addr);
sifive_ccache_write_cmd(cmd + (6 << CMD_CSR_BANK_OFFSET), cmd_csr_addr);
sifive_ccache_write_cmd(cmd + (7 << CMD_CSR_BANK_OFFSET), cmd_csr_addr);
sifive_ccache_write_cmd(cmd + (8 << CMD_CSR_BANK_OFFSET), cmd_csr_addr);
sifive_ccache_write_cmd(cmd + (9 << CMD_CSR_BANK_OFFSET), cmd_csr_addr);
sifive_ccache_write_cmd(cmd + (10 << CMD_CSR_BANK_OFFSET), cmd_csr_addr);
sifive_ccache_write_cmd(cmd + (11 << CMD_CSR_BANK_OFFSET), cmd_csr_addr);
sifive_ccache_write_cmd(cmd + (12 << CMD_CSR_BANK_OFFSET), cmd_csr_addr);
sifive_ccache_write_cmd(cmd + (13 << CMD_CSR_BANK_OFFSET), cmd_csr_addr);
sifive_ccache_write_cmd(cmd + (14 << CMD_CSR_BANK_OFFSET), cmd_csr_addr);
sifive_ccache_write_cmd(cmd + (15 << CMD_CSR_BANK_OFFSET), cmd_csr_addr);
cmd += CCACHE_ALL_OP_REQ_BATCH_NUM << CMD_CSR_BANK_OFFSET;
loop_cnt -= CCACHE_ALL_OP_REQ_BATCH_NUM;
}
if (!total_cnt)
goto done;
/* Ensure the ccache is able receive more than 16 requests */
do {
loop_cnt = (CCACHE_CMD_QLEN - sifive_ccache_read_status(status_addr));
} while (loop_cnt < CCACHE_ALL_OP_REQ_BATCH_NUM);
loop_cnt &= CCACHE_ALL_OP_REQ_BATCH_MASK;
if (total_cnt < loop_cnt) {
loop_cnt = (total_cnt + CCACHE_ALL_OP_REQ_BATCH_NUM) & CCACHE_ALL_OP_REQ_BATCH_MASK;
cmd -= ((loop_cnt - total_cnt) << CMD_CSR_BANK_OFFSET);
total_cnt = loop_cnt;
}
goto send_cmd;
done:
do {} while (sifive_ccache_read_status(status_addr));
ccache_mb_a();
return 0;
}
static struct cache_ops sifive_ccache_ops = {
.cache_flush_all = sifive_ccache_flush_all,
};
static int sifive_ccache_cold_init(const void *fdt, int nodeoff, const struct fdt_match *match)
{
struct sifive_ccache *ccache;
struct cache_device *dev;
u64 reg_addr = 0;
u32 config_csr, banks, sets, ways;
int rc;
/* find the ccache base control address */
rc = fdt_get_node_addr_size(fdt, nodeoff, 0, &reg_addr, NULL);
if (rc < 0 && reg_addr)
return SBI_ENODEV;
ccache = sbi_zalloc(sizeof(*ccache));
if (!ccache)
return SBI_ENOMEM;
dev = &ccache->dev;
dev->ops = &sifive_ccache_ops;
rc = fdt_cache_add(fdt, nodeoff, dev);
if (rc) {
sbi_free(ccache);
return rc;
}
ccache->addr = (void *)(uintptr_t)reg_addr;
/* get the info of ccache from config CSR */
config_csr = readl(ccache->addr + CCACHE_CFG_CSR);
banks = config_csr & CFG_CSR_BANK_MASK;
sets = (config_csr & CFG_CSR_SET_MASK) >> CFG_CSR_SET_OFFSET;
sets = (1 << sets);
ways = (config_csr & CFG_CSR_WAY_MASK) >> CFG_CSR_WAY_OFFSET;
ccache->total_lines = sets * ways * banks;
return SBI_OK;
}
static const struct fdt_match sifive_ccache_match[] = {
{ .compatible = "sifive,ccache2" },
{},
};
const struct fdt_driver fdt_sifive_ccache = {
.match_table = sifive_ccache_match,
.init = sifive_ccache_cold_init,
};