blob: 962bf837fd8c625ea68c82b2f33d441541417abb [file] [log] [blame]
/*
* Copyright (c) 2024 Andes Technology Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef ZEPHYR_DRIVERS_CACHE_CACHE_ANDES_L2_H_
#define ZEPHYR_DRIVERS_CACHE_CACHE_ANDES_L2_H_
#include <zephyr/kernel.h>
#include <zephyr/arch/cpu.h>
#include <zephyr/drivers/syscon.h>
#define L2C_BASE DT_REG_ADDR_BY_IDX(DT_INST(0, andestech_l2c), 0)
/* L2 cache Register Offset */
#define L2C_CONFIG (L2C_BASE + 0x00)
#define L2C_CTRL (L2C_BASE + 0x08)
#define L2C_CCTLCMD(hart_id) \
(L2C_BASE + 0x40 + (hart_id * l2_cache_cfg.cmd_offset))
#define L2C_CCTLACC(hart_id) \
(L2C_BASE + 0x48 + (hart_id * l2_cache_cfg.cmd_offset))
#define L2C_CCTLST(hart_id) \
(L2C_BASE + 0x80 + (hart_id * l2_cache_cfg.status_offset))
/* L2 cache config registers bitfields */
#define L2C_CONFIG_SIZE_SHIFT 7
#define L2C_CONFIG_MAP BIT(20)
#define L2C_CONFIG_VERSION_SHIFT 24
/* L2 cache control registers bitfields */
#define L2C_CTRL_CEN BIT(0)
#define L2C_CTRL_IPFDPT_3 GENMASK(4, 3)
#define L2C_CTRL_DPFDPT_8 GENMASK(7, 6)
/* L2 cache CCTL Access Line registers bitfields */
#define L2C_CCTLACC_WAY_SHIFT 28
/* L2 CCTL Command */
#define CCTL_L2_IX_INVAL 0x00
#define CCTL_L2_IX_WB 0x01
#define CCTL_L2_PA_INVAL 0x08
#define CCTL_L2_PA_WB 0x09
#define CCTL_L2_PA_WBINVAL 0x0a
#define CCTL_L2_WBINVAL_ALL 0x12
#define K_CACHE_WB BIT(0)
#define K_CACHE_INVD BIT(1)
#define K_CACHE_WB_INVD (K_CACHE_WB | K_CACHE_INVD)
struct nds_l2_cache_config {
uint32_t size;
uint32_t cmd_offset;
uint32_t status_offset;
uint16_t status_shift;
uint8_t version;
};
static struct nds_l2_cache_config l2_cache_cfg;
static ALWAYS_INLINE int nds_l2_cache_is_inclusive(void)
{
return IS_ENABLED(CONFIG_L2C_INCLUSIVE_POLICY) &&
(l2_cache_cfg.version > 15);
}
static ALWAYS_INLINE void nds_l2_cache_wait_status(uint8_t hart_id)
{
uint32_t status;
do {
status = sys_read32(L2C_CCTLST(hart_id));
status >>= hart_id * l2_cache_cfg.status_shift;
status &= BIT_MASK(4);
} while (status == 1);
}
static ALWAYS_INLINE int nds_l2_cache_all(int op)
{
/* L2 cache fixed to 64 byte cache line size and 16 way */
const unsigned long line_size = 64, ways = 16;
unsigned long sets, index, cmd;
uint8_t hart_id;
unsigned long status = csr_read(mstatus);
if (!l2_cache_cfg.size) {
return -ENOTSUP;
}
if (csr_read(NDS_MMSC_CFG) & MMSC_CFG_VCCTL_2) {
if ((status & MSTATUS_MPRV) && !(status & MSTATUS_MPP)) {
if (!nds_l2_cache_is_inclusive()) {
return -ENOTSUP;
}
}
}
switch (op) {
case K_CACHE_WB:
cmd = CCTL_L2_IX_WB;
break;
case K_CACHE_INVD:
cmd = CCTL_L2_IX_INVAL;
break;
case K_CACHE_WB_INVD:
cmd = CCTL_L2_WBINVAL_ALL;
break;
default:
return -ENOTSUP;
}
hart_id = arch_proc_id();
if (op == K_CACHE_WB_INVD) {
sys_write32(CCTL_L2_WBINVAL_ALL, L2C_CCTLCMD(hart_id));
/* Wait L2 CCTL Commands finished */
nds_l2_cache_wait_status(hart_id);
} else {
sets = l2_cache_cfg.size / (ways * line_size);
/* Invalidate all cache line by each way and each set */
for (int j = 0; j < ways; j++) {
/* Index of way */
index = j << L2C_CCTLACC_WAY_SHIFT;
for (int i = 0; i < sets; i++) {
/* Index of set */
index += line_size;
/* Invalidate each cache line */
sys_write32(index, L2C_CCTLACC(hart_id));
sys_write32(cmd, L2C_CCTLCMD(hart_id));
/* Wait L2 CCTL Commands finished */
nds_l2_cache_wait_status(hart_id);
}
}
}
return 0;
}
static ALWAYS_INLINE int nds_l2_cache_range(void *addr, size_t size, int op)
{
const unsigned long line_size = 64;
unsigned long last_byte, align_addr, cmd;
uint8_t hart_id;
if (!l2_cache_cfg.size) {
return -ENOTSUP;
}
switch (op) {
case K_CACHE_WB:
cmd = CCTL_L2_PA_WB;
break;
case K_CACHE_INVD:
cmd = CCTL_L2_PA_INVAL;
break;
case K_CACHE_WB_INVD:
cmd = CCTL_L2_PA_WBINVAL;
break;
default:
return -ENOTSUP;
}
last_byte = (unsigned long)addr + size - 1;
align_addr = ROUND_DOWN(addr, line_size);
hart_id = arch_proc_id();
while (align_addr <= last_byte) {
sys_write32(align_addr, L2C_CCTLACC(hart_id));
sys_write32(cmd, L2C_CCTLCMD(hart_id));
align_addr += line_size;
/* Wait L2 CCTL Commands finished */
nds_l2_cache_wait_status(hart_id);
}
return 0;
}
static ALWAYS_INLINE void nds_l2_cache_enable(void)
{
if (l2_cache_cfg.size) {
uint32_t l2c_ctrl = sys_read32(L2C_CTRL);
if (!(l2c_ctrl & L2C_CTRL_CEN)) {
WRITE_BIT(l2c_ctrl, 0, true);
sys_write32(l2c_ctrl, L2C_CTRL);
}
}
}
static ALWAYS_INLINE void nds_l2_cache_disable(void)
{
if (l2_cache_cfg.size) {
uint32_t l2c_ctrl = sys_read32(L2C_CTRL);
if (l2c_ctrl & L2C_CTRL_CEN) {
WRITE_BIT(l2c_ctrl, 0, false);
sys_write32(l2c_ctrl, L2C_CTRL);
}
}
}
static ALWAYS_INLINE int nds_l2_cache_init(void)
{
unsigned long line_size;
#if defined(CONFIG_SYSCON)
#if DT_NODE_HAS_COMPAT_STATUS(DT_NODELABEL(syscon), andestech_atcsmu100, okay)
uint32_t system_cfg;
const struct device *syscon_dev = DEVICE_DT_GET(DT_NODELABEL(syscon));
if (device_is_ready(syscon_dev)) {
/* Check L2 cache feature from SMU */
syscon_read_reg(syscon_dev, 0x08, &system_cfg);
/* Platform doesn't support L2 cache controller */
if (!(system_cfg & BIT(8))) {
l2_cache_cfg.size = 0;
return 0;
}
} else {
LOG_ERR("Andes cache driver should be initialized after "
"syscon driver initialization");
return 0;
}
#endif /* andestech_atcsmu100 dts node status okay */
#endif /* defined(CONFIG_SYSCON) */
uint32_t l2c_ctrl;
line_size = (sys_read32(L2C_CONFIG) >> L2C_CONFIG_SIZE_SHIFT) & BIT_MASK(7);
l2_cache_cfg.size = line_size * 128 * 1024;
if (sys_read32(L2C_CONFIG) & L2C_CONFIG_MAP) {
l2_cache_cfg.cmd_offset = 0x10;
l2_cache_cfg.status_offset = 0;
l2_cache_cfg.status_shift = 4;
} else {
l2_cache_cfg.cmd_offset = 0x1000;
l2_cache_cfg.status_offset = 0x1000;
l2_cache_cfg.status_shift = 0;
}
l2_cache_cfg.version = (sys_read32(L2C_CONFIG) >> L2C_CONFIG_VERSION_SHIFT) & BIT_MASK(8);
/* Initializing L2 cache instruction, data prefetch depth */
l2c_ctrl = sys_read32(L2C_CTRL);
l2c_ctrl |= (L2C_CTRL_IPFDPT_3 | L2C_CTRL_DPFDPT_8);
/* Writeback and invalidate all I/D-Cache before setting L2C */
__asm__ volatile ("fence.i");
sys_write32(l2c_ctrl, L2C_CTRL);
if (IS_ENABLED(CONFIG_SMP)) {
if (l2_cache_cfg.size) {
l2c_ctrl = sys_read32(L2C_CTRL);
if (!(l2c_ctrl & L2C_CTRL_CEN)) {
WRITE_BIT(l2c_ctrl, 0, true);
sys_write32(l2c_ctrl, L2C_CTRL);
}
}
}
return l2_cache_cfg.size;
}
#endif /* ZEPHYR_DRIVERS_CACHE_CACHE_ANDES_L2_H_ */