From adec30ace4cebb0554bb246b52eebaf37c1545c4 Mon Sep 17 00:00:00 2001 From: Gilbert Gilb's Date: Mon, 13 Nov 2023 12:36:24 +0100 Subject: [PATCH] fix(c9xx): don't flush dcache when invalidating The data cache invalidation function for c9xx CPUs uses `dcache.cipa` instruction. According to T-Head extension specification[1] section 3.1.5, this instruction also performs a cache clean along with the invalidation. On top of being incorrect, this leads to a serious issue on the designware ethernet driver, where stalled cache may get flushed each time we handle a new received packet[2]. As a result, received packet are randomly corrupted with old cached data. This can easily be reproduced by sending an ARP request to the device during a TFTP transfer. The last TFTP block is treated as the ARP reply we just sent, which makes U-Boot hang on the block. Always use `dcache.ipa` instruction to invalidate dcache. Replace existing usages of `dcache.ipa` with our implementation. Note that this fix is slightly intrusive as it changes the cache invalidation behavior in all drivers. However, I have not noticed any side-effect during my tests. [1] https://github.com/T-head-Semi/thead-extension-spec/releases/download/2.3.0/xthead-2023-11-10-2.3.0.pdf [2] https://github.com/revyos/thead-u-boot/blob/918a8c89e056e3462031d6a498bb4fcc0c3526ce/drivers/net/designware.c#L475 --- arch/riscv/cpu/c9xx/cpu.c | 10 ---------- cmd/ddrscan.c | 1 - cmd/prbs.c | 3 +-- drivers/mmc/sdhci.c | 3 +-- drivers/usb/dwc3/ep0.c | 3 +-- 5 files changed, 3 insertions(+), 17 deletions(-) diff --git a/arch/riscv/cpu/c9xx/cpu.c b/arch/riscv/cpu/c9xx/cpu.c index e5eaed94..a93a3060 100644 --- a/arch/riscv/cpu/c9xx/cpu.c +++ b/arch/riscv/cpu/c9xx/cpu.c @@ -104,16 +104,6 @@ void invalidate_dcache_range(unsigned long start, unsigned long end) { register unsigned long i asm("a0") = start & ~(CONFIG_SYS_CACHELINE_SIZE - 1); - for (; i < end; i += CONFIG_SYS_CACHELINE_SIZE) - asm volatile(".long 0x02b5000b"); /* dcache.cipa a0 */ - - sync_is(); -} - -void invalid_dcache_range(unsigned long start, unsigned long end) -{ - register unsigned long i asm("a0") = start & ~(CONFIG_SYS_CACHELINE_SIZE - 1); - for (; i < end; i += CONFIG_SYS_CACHELINE_SIZE) asm volatile(".long 0x02a5000b"); /* dcache.ipa a0 */ diff --git a/cmd/ddrscan.c b/cmd/ddrscan.c index 0f2b78c6..c550e03d 100644 --- a/cmd/ddrscan.c +++ b/cmd/ddrscan.c @@ -73,7 +73,6 @@ extern ulong mem_test_quick(vu_long *buf, ulong start_addr, ulong end_addr, #endif extern void flush_dcache_range(unsigned long start, unsigned long end); extern void invalidate_dcache_range(unsigned long start, unsigned long end); -extern void invalid_dcache_range(unsigned long start, unsigned long end); #ifdef CONFIG_CMD_MEMTEST int test_stuck_address(ulv *bufa, ulong count); diff --git a/cmd/prbs.c b/cmd/prbs.c index 2bbb68cf..59471169 100644 --- a/cmd/prbs.c +++ b/cmd/prbs.c @@ -50,7 +50,6 @@ u64 t_end; extern void flush_dcache_range(unsigned long start, unsigned long end); extern void invalidate_dcache_range(unsigned long start, unsigned long end); -extern void invalid_dcache_range(unsigned long start, unsigned long end); extern unsigned long get_ddr_density(void); extern int riscv_get_time(u64 *time); @@ -305,7 +304,7 @@ int prbs_test(struct PRBS_ELE *prbs, unsigned int *buf, int pos, bool random_dq, // compare result // invalid cache before read mdelay(100); - invalid_dcache_range((ulong)buf, (ulong)buf+(bit_len*4*2*2)); + invalidate_dcache_range((ulong)buf, (ulong)buf+(bit_len*4*2*2)); p1 = buf; bit_cnt = 0; for (i = 0; i < bit_len; i++) { diff --git a/drivers/mmc/sdhci.c b/drivers/mmc/sdhci.c index 5cc70cda..349e2bc1 100644 --- a/drivers/mmc/sdhci.c +++ b/drivers/mmc/sdhci.c @@ -247,10 +247,9 @@ static int sdhci_transfer_data(struct sdhci_host *host, struct mmc_data *data) } } while (!(stat & SDHCI_INT_DATA_END)); #ifdef CONFIG_TARGET_LIGHT_C910 - extern void invalid_dcache_range(unsigned long start, unsigned long end); /*After read ,invalid dcache range again to avoid cache filled during read tranfer*/ if(data->flags == MMC_DATA_READ){ - invalid_dcache_range(host->start_addr,host->start_addr+ROUND(data->blocks*data->blocksize, ARCH_DMA_MINALIGN)); + invalidate_dcache_range(host->start_addr,host->start_addr+ROUND(data->blocks*data->blocksize, ARCH_DMA_MINALIGN)); } #endif return 0; diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c index ea21f36d..1e07bdf8 100644 --- a/drivers/usb/dwc3/ep0.c +++ b/drivers/usb/dwc3/ep0.c @@ -906,8 +906,7 @@ static void dwc3_ep0_xfer_complete(struct dwc3 *dwc, dep->resource_index = 0; dwc->setup_packet_pending = false; #ifdef CONFIG_TARGET_LIGHT_C910 - extern void invalid_dcache_range(unsigned long start, unsigned long end); - invalid_dcache_range((unsigned long)dwc->ctrl_req, (dmaaddr_t)dwc->ctrl_req + ROUND(sizeof(*dwc->ctrl_req), CACHELINE_SIZE)); + invalidate_dcache_range((unsigned long)dwc->ctrl_req, (dmaaddr_t)dwc->ctrl_req + ROUND(sizeof(*dwc->ctrl_req), CACHELINE_SIZE)); #endif switch (dwc->ep0state) { -- 2.34.1