diff --git a/arch/risc-v/Kconfig b/arch/risc-v/Kconfig index 9aaf3dd4340f9..d3849635da640 100644 --- a/arch/risc-v/Kconfig +++ b/arch/risc-v/Kconfig @@ -464,6 +464,7 @@ config ARCH_CHIP_EIC7700X select ARCH_HAVE_DPFPU select ARCH_HAVE_MULTICPU select ARCH_HAVE_MPU + select ARCH_DCACHE select ARCH_MMU_TYPE_SV39 select ARCH_HAVE_ADDRENV select ARCH_NEED_ADDRENV_MAPPING diff --git a/arch/risc-v/src/eic7700x/Make.defs b/arch/risc-v/src/eic7700x/Make.defs index b70ce76c27f86..b233fa3acdeb8 100644 --- a/arch/risc-v/src/eic7700x/Make.defs +++ b/arch/risc-v/src/eic7700x/Make.defs @@ -30,6 +30,7 @@ HEAD_ASRC = eic7700x_head.S CHIP_CSRCS = eic7700x_start.c eic7700x_irq_dispatch.c eic7700x_irq.c CHIP_CSRCS += eic7700x_timerisr.c eic7700x_allocateheap.c CHIP_CSRCS += eic7700x_mm_init.c eic7700x_pgalloc.c +CHIP_CSRCS += eic7700x_cache.c ifeq ($(CONFIG_EIC7700X_CLK),y) CHIP_CSRCS += eic7700x_clk.c eic7700x_clk_ops.c diff --git a/arch/risc-v/src/eic7700x/eic7700x_cache.c b/arch/risc-v/src/eic7700x/eic7700x_cache.c new file mode 100644 index 0000000000000..065e3be2f7800 --- /dev/null +++ b/arch/risc-v/src/eic7700x/eic7700x_cache.c @@ -0,0 +1,292 @@ +/**************************************************************************** + * arch/risc-v/src/eic7700x/eic7700x_cache.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/* Data cache maintenance on a SoC that is not cache coherent. + * + * No device that moves data on its own snoops the harts' caches or is + * snooped by them, so software maintains the cache around every transfer. + * The harts are coherent with each other; it is DMA that is not. + * + * These cores have no Zicbom, so maintenance is a store to the L3 + * controller carrying a block's physical address, one per sixty four byte + * block. TRM part 1 section 3.4.1; see hardware/eic7700x_l3cache.h. + * + * That store both writes back and invalidates, and is the only operation + * there is. Two rules follow, both the caller's to keep: + * + * A range being invalidated must own whole blocks, sixty four byte + * aligned at both ends, which up_invalidate_dcache() asserts. A shared + * end block is written back as well as dropped, so a neighbour dirtied + * during the transfer lands on top of what the device delivered. + * + * Cleaning has no alignment requirement: writing back more than was + * asked costs a refill and nothing else. + * + * The rules are the chip's, not any one driver's. + */ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include + +#include +#include + +#include "riscv_internal.h" +#include "hardware/eic7700x_l3cache.h" + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: dcache_flush_pa + * + * Description: + * Write back and invalidate every cache block covering a range of + * physical addresses. + * + * Input Parameters: + * pstart - physical start address of the region + * pend - physical end address of the region + 1 + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void dcache_flush_pa(uintptr_t pstart, uintptr_t pend) +{ + uintptr_t pa; + + /* Round out to whole blocks: the register takes a block address. */ + + pa = pstart & ~(uintptr_t)(EIC7700X_L3_LINESIZE - 1); + pend = (pend + EIC7700X_L3_LINESIZE - 1) & + ~(uintptr_t)(EIC7700X_L3_LINESIZE - 1); + + /* Both fences order input and output as well as memory: what follows is + * typically the store that starts a device, which a memory-only fence + * would not order. + */ + + UP_DSB(); + + for (; pa < pend; pa += EIC7700X_L3_LINESIZE) + { + putreg64(pa, EIC7700X_L3_FLUSH64); + } + + UP_DSB(); +} + +/**************************************************************************** + * Name: dcache_range + * + * Description: + * The body of all three range operations. Kernel memory is mapped + * virtual equals physical here, so translation is a range check. + * + * An address outside that window is refused: this interface does not + * say which address environment a user address belongs to, so a caller + * reaching a user buffer translates it first and passes physical + * addresses, which are valid kernel addresses here. + * + * Input Parameters: + * start - virtual start address of the region + * end - virtual end address of the region + 1 + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void dcache_range(uintptr_t start, uintptr_t end) +{ + if (start >= end) + { + return; + } + + DEBUGASSERT(start >= CONFIG_RAM_START && end <= (uintptr_t)CONFIG_RAM_END); + + if (start < CONFIG_RAM_START || end > (uintptr_t)CONFIG_RAM_END) + { + return; + } + + dcache_flush_pa(start, end); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: up_get_dcache_linesize + * Returned Value: + * The cache block size in bytes. + * + ****************************************************************************/ + +size_t up_get_dcache_linesize(void) +{ + return EIC7700X_L3_LINESIZE; +} + +/**************************************************************************** + * Name: up_get_dcache_size + * + * Description: + * The first level data cache only. A flush reaches the private L2 and + * the L3 as well, so this is not what one covers. + * + * Returned Value: + * The size of one level one data cache in bytes. + * + ****************************************************************************/ + +size_t up_get_dcache_size(void) +{ + return EIC7700X_L1_DCACHE_SIZE; +} + +/**************************************************************************** + * Name: up_enable_dcache / up_disable_dcache + * + * Description: + * Nothing to do. The caches are running before this port is entered and + * there is no supervisor accessible control that would turn them off. + * + ****************************************************************************/ + +void up_enable_dcache(void) +{ +} + +void up_disable_dcache(void) +{ +} + +/**************************************************************************** + * Name: up_clean_dcache + * + * Description: + * Write the range back so a device about to read it sees what the + * processor wrote. Safe at any alignment. + * + * Input Parameters: + * start - virtual start address of the region + * end - virtual end address of the region + 1 + * + * Returned Value: + * None + * + ****************************************************************************/ + +void up_clean_dcache(uintptr_t start, uintptr_t end) +{ + dcache_range(start, end); +} + +/**************************************************************************** + * Name: up_flush_dcache + * + * Description: + * Write back and invalidate. This is the operation the hardware has; + * the other two are it under different names. + * + * Input Parameters: + * start - virtual start address of the region + * end - virtual end address of the region + 1 + * + * Returned Value: + * None + * + ****************************************************************************/ + +void up_flush_dcache(uintptr_t start, uintptr_t end) +{ + dcache_range(start, end); +} + +/**************************************************************************** + * Name: up_invalidate_dcache + * + * Description: + * Discard the range so the next read comes from memory, where a device + * has just left something. Must be block aligned at both ends; the + * file's opening comment says why. + * + * Input Parameters: + * start - virtual start address of the region + * end - virtual end address of the region + 1 + * + * Returned Value: + * None + * + ****************************************************************************/ + +void up_invalidate_dcache(uintptr_t start, uintptr_t end) +{ + DEBUGASSERT((start & (EIC7700X_L3_LINESIZE - 1)) == 0 && + (end & (EIC7700X_L3_LINESIZE - 1)) == 0); + + dcache_range(start, end); +} + +/**************************************************************************** + * Name: up_invalidate_dcache_all / up_clean_dcache_all / up_flush_dcache_all + * + * Description: + * Order what has been written, and nothing else. + * + * The hardware has no whole-hierarchy operation, and naming every block + * in turn would be fifteen and a half million stores, on a path two of + * the three callers reach only after the system has gone wrong. + * + * Nor is one needed between harts: the L3 is a directory based coherency + * manager and the L1 data caches are coherent with each other (TRM part + * 1 section 3.4). Ordering is what is left. Only DMA is non coherent, + * and a DMA buffer is always given as a range. + * + ****************************************************************************/ + +void up_invalidate_dcache_all(void) +{ + UP_DSB(); +} + +void up_clean_dcache_all(void) +{ + UP_DSB(); +} + +void up_flush_dcache_all(void) +{ + UP_DSB(); +} diff --git a/arch/risc-v/src/eic7700x/hardware/eic7700x_l3cache.h b/arch/risc-v/src/eic7700x/hardware/eic7700x_l3cache.h new file mode 100644 index 0000000000000..dc7590477db25 --- /dev/null +++ b/arch/risc-v/src/eic7700x/hardware/eic7700x_l3cache.h @@ -0,0 +1,80 @@ +/**************************************************************************** + * arch/risc-v/src/eic7700x/hardware/eic7700x_l3cache.h + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +#ifndef __ARCH_RISCV_SRC_EIC7700X_HARDWARE_EIC7700X_L3CACHE_H +#define __ARCH_RISCV_SRC_EIC7700X_HARDWARE_EIC7700X_L3CACHE_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "hardware/eic7700x_memorymap.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* The L3 cache controller, also the coherency manager. Four kilobytes + * here, TRM part 1 table 3-40. + */ + +#define EIC7700X_L3CACHE_BASE 0x02010000ul + +/* Configuration, read only. TRM part 1 section 3.4.1 tables 3-129 and + * 3-131. Reads 0x060a1004 on this part: four banks, sixteen ways, 1024 + * sets, sixty four byte blocks. The one place the block size can be had + * from the silicon rather than the manual. + */ + +#define EIC7700X_L3_CONFIG (EIC7700X_L3CACHE_BASE + 0x000) + +/* Flush64, write only. TRM part 1 section 3.4.1 table 3-129. + * + * The value written is the physical address of a cache block, and the + * manual is emphatic that all sixty four bits have to be written in a + * single access or nothing happens at all: two thirty two bit writes are + * not a substitute. That is why this is written with putreg64(). + * + * What the write does, in the manual's words, is "a write-back and + * invalidate, meaning the contents are written to memory and L3, pL2, and + * L1 cache lines are then invalidated". The L3 is inclusive of the L1 + * data cache and back probes it, so one write per block is the whole + * maintenance operation for the whole hierarchy. There is nothing to do + * per hart and nothing to do for the private L2. + * + * It is also the only operation there is. There is no invalidate that + * does not write back and no write back that does not invalidate, and + * these cores have no Zicbom to offer an alternative. eic7700x_cache.c + * describes what that costs and what it requires of callers. + */ + +#define EIC7700X_L3_FLUSH64 (EIC7700X_L3CACHE_BASE + 0x200) + +/* Sixty four bytes at every level of the hierarchy: the thirty two + * kilobyte four way L1 data cache, the two hundred and fifty six kilobyte + * eight way private L2, and the four megabyte sixteen way L3. + */ + +#define EIC7700X_L3_LINESIZE (64) +#define EIC7700X_L1_DCACHE_SIZE (32 * 1024) + +#endif /* __ARCH_RISCV_SRC_EIC7700X_HARDWARE_EIC7700X_L3CACHE_H */