cache: Add Aspeed AST10x0 external cache driver
The CPU in Aspeed AST10x0 SOC is a ARM Cortex-M4 which doesn't internal cache memory. Aspeed implements an integrated system level cache to accelerate instruction and data memory accesses. Signed-off-by: Dylan Hung <dylan_hung@aspeedtech.com>
This commit is contained in:
parent
46ff553b68
commit
c880db1725
1
drivers/cache/CMakeLists.txt
vendored
1
drivers/cache/CMakeLists.txt
vendored
|
@ -1 +1,2 @@
|
|||
# SPDX-License-Identifier: Apache-2.0
|
||||
zephyr_sources_ifdef(CONFIG_CACHE_ASPEED cache_aspeed.c)
|
||||
|
|
2
drivers/cache/Kconfig
vendored
2
drivers/cache/Kconfig
vendored
|
@ -16,3 +16,5 @@ config CACHE_HAS_DRIVER
|
|||
bool
|
||||
|
||||
endif # CACHE
|
||||
|
||||
source "drivers/cache/Kconfig.aspeed"
|
||||
|
|
11
drivers/cache/Kconfig.aspeed
vendored
Normal file
11
drivers/cache/Kconfig.aspeed
vendored
Normal file
|
@ -0,0 +1,11 @@
|
|||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
# Copyright (c) 2022 ASPEED Technology Inc.
|
||||
|
||||
config CACHE_ASPEED
|
||||
bool "ASPEED external cache driver"
|
||||
depends on SOC_SERIES_AST10X0
|
||||
depends on SYSCON
|
||||
select CACHE_HAS_DRIVER
|
||||
help
|
||||
This option enables the CACHE driver for ASPEED AST10X0 series SOC.
|
276
drivers/cache/cache_aspeed.c
vendored
Normal file
276
drivers/cache/cache_aspeed.c
vendored
Normal file
|
@ -0,0 +1,276 @@
|
|||
/*
|
||||
* Copyright (c) 2022 ASPEED Technology Inc.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <zephyr/kernel.h>
|
||||
#include <arch/arm/aarch32/cortex_m/cmsis.h>
|
||||
#include <zephyr/drivers/syscon.h>
|
||||
|
||||
/*
|
||||
* cache area control: each bit controls 32KB cache area
|
||||
* 1: cacheable
|
||||
* 0: no-cache
|
||||
*
|
||||
* bit[0]: 1st 32KB from 0x0000_0000 to 0x0000_7fff
|
||||
* bit[1]: 2nd 32KB from 0x0000_8000 to 0x0000_ffff
|
||||
* ...
|
||||
* bit[22]: 23th 32KB from 0x000a_8000 to 0x000a_ffff
|
||||
* bit[23]: 24th 32KB from 0x000b_0000 to 0x000b_ffff
|
||||
*/
|
||||
#define CACHE_AREA_CTRL_REG 0xa50
|
||||
#define CACHE_INVALID_REG 0xa54
|
||||
#define CACHE_FUNC_CTRL_REG 0xa58
|
||||
|
||||
#define CACHED_SRAM_ADDR CONFIG_SRAM_BASE_ADDRESS
|
||||
#define CACHED_SRAM_SIZE KB(CONFIG_SRAM_SIZE)
|
||||
#define CACHED_SRAM_END (CACHED_SRAM_ADDR + CACHED_SRAM_SIZE - 1)
|
||||
|
||||
#define CACHE_AREA_SIZE_LOG2 15
|
||||
#define CACHE_AREA_SIZE (1 << CACHE_AREA_SIZE_LOG2)
|
||||
|
||||
#define DCACHE_INVALID(addr) (BIT(31) | ((addr & GENMASK(10, 0)) << 16))
|
||||
#define ICACHE_INVALID(addr) (BIT(15) | ((addr & GENMASK(10, 0)) << 0))
|
||||
|
||||
#define ICACHE_CLEAN BIT(2)
|
||||
#define DCACHE_CLEAN BIT(1)
|
||||
#define CACHE_EANABLE BIT(0)
|
||||
|
||||
/* cache size = 32B * 128 = 4KB */
|
||||
#define CACHE_LINE_SIZE_LOG2 5
|
||||
#define CACHE_LINE_SIZE (1 << CACHE_LINE_SIZE_LOG2)
|
||||
#define N_CACHE_LINE 128
|
||||
#define CACHE_ALIGNED_ADDR(addr) \
|
||||
((addr >> CACHE_LINE_SIZE_LOG2) << CACHE_LINE_SIZE_LOG2)
|
||||
|
||||
/* prefetch buffer */
|
||||
#define PREFETCH_BUF_SIZE CACHE_LINE_SIZE
|
||||
|
||||
static void aspeed_cache_init(void)
|
||||
{
|
||||
const struct device *dev = DEVICE_DT_GET(DT_NODELABEL(syscon));
|
||||
uint32_t start_bit, end_bit, max_bit;
|
||||
|
||||
/* set all cache areas to no-cache by default */
|
||||
syscon_write_reg(dev, CACHE_FUNC_CTRL_REG, 0);
|
||||
|
||||
/* calculate how many areas need to be set */
|
||||
max_bit = 8 * sizeof(uint32_t) - 1;
|
||||
start_bit = MIN(max_bit, CACHED_SRAM_ADDR >> CACHE_AREA_SIZE_LOG2);
|
||||
end_bit = MIN(max_bit, CACHED_SRAM_END >> CACHE_AREA_SIZE_LOG2);
|
||||
syscon_write_reg(dev, CACHE_AREA_CTRL_REG, GENMASK(end_bit, start_bit));
|
||||
|
||||
/* enable cache */
|
||||
syscon_write_reg(dev, CACHE_FUNC_CTRL_REG, CACHE_EANABLE);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief get aligned address and the number of cachline to be invalied
|
||||
* @param [IN] addr - start address to be invalidated
|
||||
* @param [IN] size - size in byte
|
||||
* @param [OUT] p_aligned_addr - pointer to the cacheline aligned address variable
|
||||
* @return number of cacheline to be invalidated
|
||||
*
|
||||
* * addr
|
||||
* |--------size-------------|
|
||||
* |-----|-----|-----|-----|-----|
|
||||
* \ \
|
||||
* head tail
|
||||
*
|
||||
* example 1:
|
||||
* addr = 0x100 (cacheline aligned), size = 64
|
||||
* then head = 0x100, number of cache line to be invalidated = 64 / 32 = 2
|
||||
* which means range [0x100, 0x140) will be invalidated
|
||||
*
|
||||
* example 2:
|
||||
* addr = 0x104 (cacheline unaligned), size = 64
|
||||
* then head = 0x100, number of cache line to be invalidated = 1 + 64 / 32 = 3
|
||||
* which means range [0x100, 0x160) will be invalidated
|
||||
*/
|
||||
static uint32_t get_n_cacheline(uint32_t addr, uint32_t size, uint32_t *p_head)
|
||||
{
|
||||
uint32_t n = 0;
|
||||
uint32_t tail;
|
||||
|
||||
/* head */
|
||||
*p_head = CACHE_ALIGNED_ADDR(addr);
|
||||
|
||||
/* roundup the tail address */
|
||||
tail = addr + size + (CACHE_LINE_SIZE - 1);
|
||||
tail = CACHE_ALIGNED_ADDR(tail);
|
||||
|
||||
n = (tail - *p_head) >> CACHE_LINE_SIZE_LOG2;
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
void cache_data_enable(void)
|
||||
{
|
||||
aspeed_cache_init();
|
||||
}
|
||||
|
||||
void cache_data_disable(void)
|
||||
{
|
||||
const struct device *dev = DEVICE_DT_GET(DT_NODELABEL(syscon));
|
||||
|
||||
syscon_write_reg(dev, CACHE_FUNC_CTRL_REG, 0);
|
||||
}
|
||||
|
||||
void cache_instr_enable(void)
|
||||
{
|
||||
aspeed_cache_init();
|
||||
}
|
||||
|
||||
void cache_instr_disable(void)
|
||||
{
|
||||
const struct device *dev = DEVICE_DT_GET(DT_NODELABEL(syscon));
|
||||
|
||||
syscon_write_reg(dev, CACHE_FUNC_CTRL_REG, 0);
|
||||
}
|
||||
|
||||
int cache_data_all(int op)
|
||||
{
|
||||
const struct device *dev = DEVICE_DT_GET(DT_NODELABEL(syscon));
|
||||
uint32_t ctrl;
|
||||
unsigned int key = 0;
|
||||
|
||||
ARG_UNUSED(op);
|
||||
syscon_read_reg(dev, CACHE_FUNC_CTRL_REG, &ctrl);
|
||||
|
||||
/* enter critical section */
|
||||
if (!k_is_in_isr())
|
||||
key = irq_lock();
|
||||
|
||||
ctrl &= ~DCACHE_CLEAN;
|
||||
syscon_write_reg(dev, CACHE_FUNC_CTRL_REG, ctrl);
|
||||
|
||||
__DSB();
|
||||
ctrl |= DCACHE_CLEAN;
|
||||
syscon_write_reg(dev, CACHE_FUNC_CTRL_REG, ctrl);
|
||||
__DSB();
|
||||
|
||||
/* exit critical section */
|
||||
if (!k_is_in_isr())
|
||||
irq_unlock(key);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cache_data_range(void *addr, size_t size, int op)
|
||||
{
|
||||
uint32_t aligned_addr, i, n;
|
||||
const struct device *dev = DEVICE_DT_GET(DT_NODELABEL(syscon));
|
||||
unsigned int key = 0;
|
||||
|
||||
ARG_UNUSED(op);
|
||||
|
||||
if (((uint32_t)addr < CACHED_SRAM_ADDR) ||
|
||||
((uint32_t)addr > CACHED_SRAM_END)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* enter critical section */
|
||||
if (!k_is_in_isr())
|
||||
key = irq_lock();
|
||||
|
||||
n = get_n_cacheline((uint32_t)addr, size, &aligned_addr);
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
syscon_write_reg(dev, CACHE_INVALID_REG, 0);
|
||||
syscon_write_reg(dev, CACHE_INVALID_REG, DCACHE_INVALID(aligned_addr));
|
||||
aligned_addr += CACHE_LINE_SIZE;
|
||||
}
|
||||
__DSB();
|
||||
|
||||
/* exit critical section */
|
||||
if (!k_is_in_isr())
|
||||
irq_unlock(key);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cache_instr_all(int op)
|
||||
{
|
||||
const struct device *dev = DEVICE_DT_GET(DT_NODELABEL(syscon));
|
||||
uint32_t ctrl;
|
||||
unsigned int key = 0;
|
||||
|
||||
ARG_UNUSED(op);
|
||||
|
||||
syscon_read_reg(dev, CACHE_FUNC_CTRL_REG, &ctrl);
|
||||
|
||||
/* enter critical section */
|
||||
if (!k_is_in_isr())
|
||||
key = irq_lock();
|
||||
|
||||
ctrl &= ~ICACHE_CLEAN;
|
||||
syscon_write_reg(dev, CACHE_FUNC_CTRL_REG, ctrl);
|
||||
__ISB();
|
||||
ctrl |= ICACHE_CLEAN;
|
||||
syscon_write_reg(dev, CACHE_FUNC_CTRL_REG, ctrl);
|
||||
__ISB();
|
||||
|
||||
/* exit critical section */
|
||||
if (!k_is_in_isr())
|
||||
irq_unlock(key);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cache_instr_range(void *addr, size_t size, int op)
|
||||
{
|
||||
uint32_t aligned_addr, i, n;
|
||||
const struct device *dev = DEVICE_DT_GET(DT_NODELABEL(syscon));
|
||||
unsigned int key = 0;
|
||||
|
||||
ARG_UNUSED(op);
|
||||
|
||||
if (((uint32_t)addr < CACHED_SRAM_ADDR) ||
|
||||
((uint32_t)addr > CACHED_SRAM_END)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
n = get_n_cacheline((uint32_t)addr, size, &aligned_addr);
|
||||
|
||||
/* enter critical section */
|
||||
if (!k_is_in_isr())
|
||||
key = irq_lock();
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
syscon_write_reg(dev, CACHE_INVALID_REG, 0);
|
||||
syscon_write_reg(dev, CACHE_INVALID_REG, ICACHE_INVALID(aligned_addr));
|
||||
aligned_addr += CACHE_LINE_SIZE;
|
||||
}
|
||||
__DSB();
|
||||
|
||||
/* exit critical section */
|
||||
if (!k_is_in_isr())
|
||||
irq_unlock(key);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_DCACHE_LINE_SIZE_DETECT
|
||||
size_t cache_data_line_size_get(void)
|
||||
{
|
||||
const struct device *dev = DEVICE_DT_GET(DT_NODELABEL(syscon));
|
||||
uint32_t ctrl;
|
||||
|
||||
syscon_read_reg(dev, CACHE_FUNC_CTRL_REG, &ctrl);
|
||||
|
||||
return (ctrl & CACHE_EANABLE) ? CACHE_LINE_SIZE : 0;
|
||||
}
|
||||
#endif /* CONFIG_DCACHE_LINE_SIZE_DETECT */
|
||||
|
||||
#ifdef CONFIG_ICACHE_LINE_SIZE_DETECT
|
||||
size_t cache_instr_line_size_get(void)
|
||||
{
|
||||
const struct device *dev = DEVICE_DT_GET(DT_NODELABEL(syscon));
|
||||
uint32_t ctrl;
|
||||
|
||||
syscon_read_reg(dev, CACHE_FUNC_CTRL_REG, &ctrl);
|
||||
|
||||
return (ctrl & CACHE_EANABLE) ? CACHE_LINE_SIZE : 0;
|
||||
}
|
||||
#endif /* CONFIG_ICACHE_LINE_SIZE_DETECT */
|
Loading…
Reference in a new issue