diff --git a/drivers/cache/CMakeLists.txt b/drivers/cache/CMakeLists.txt index 9881313609a..c2c8bcc1483 100644 --- a/drivers/cache/CMakeLists.txt +++ b/drivers/cache/CMakeLists.txt @@ -1 +1,2 @@ # SPDX-License-Identifier: Apache-2.0 +zephyr_sources_ifdef(CONFIG_CACHE_ASPEED cache_aspeed.c) diff --git a/drivers/cache/Kconfig b/drivers/cache/Kconfig index 52cbc049dfd..6370eea6170 100644 --- a/drivers/cache/Kconfig +++ b/drivers/cache/Kconfig @@ -16,3 +16,5 @@ config CACHE_HAS_DRIVER bool endif # CACHE + +source "drivers/cache/Kconfig.aspeed" diff --git a/drivers/cache/Kconfig.aspeed b/drivers/cache/Kconfig.aspeed new file mode 100644 index 00000000000..01cce6f8e31 --- /dev/null +++ b/drivers/cache/Kconfig.aspeed @@ -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. diff --git a/drivers/cache/cache_aspeed.c b/drivers/cache/cache_aspeed.c new file mode 100644 index 00000000000..1b6051a3d7b --- /dev/null +++ b/drivers/cache/cache_aspeed.c @@ -0,0 +1,276 @@ +/* + * Copyright (c) 2022 ASPEED Technology Inc. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include + +/* + * 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 */