18c2ecf20Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0-only */ 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Cache maintenance 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2001 Deep Blue Solutions Ltd. 68c2ecf20Sopenharmony_ci * Copyright (C) 2012 ARM Ltd. 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/errno.h> 108c2ecf20Sopenharmony_ci#include <linux/linkage.h> 118c2ecf20Sopenharmony_ci#include <linux/init.h> 128c2ecf20Sopenharmony_ci#include <asm/assembler.h> 138c2ecf20Sopenharmony_ci#include <asm/cpufeature.h> 148c2ecf20Sopenharmony_ci#include <asm/alternative.h> 158c2ecf20Sopenharmony_ci#include <asm/asm-uaccess.h> 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci/* 188c2ecf20Sopenharmony_ci * flush_icache_range(start,end) 198c2ecf20Sopenharmony_ci * 208c2ecf20Sopenharmony_ci * Ensure that the I and D caches are coherent within specified region. 218c2ecf20Sopenharmony_ci * This is typically used when code has been written to a memory region, 228c2ecf20Sopenharmony_ci * and will be executed. 238c2ecf20Sopenharmony_ci * 248c2ecf20Sopenharmony_ci * - start - virtual start address of region 258c2ecf20Sopenharmony_ci * - end - virtual end address of region 268c2ecf20Sopenharmony_ci */ 278c2ecf20Sopenharmony_ciSYM_FUNC_START(__flush_icache_range) 288c2ecf20Sopenharmony_ci /* FALLTHROUGH */ 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci/* 318c2ecf20Sopenharmony_ci * __flush_cache_user_range(start,end) 328c2ecf20Sopenharmony_ci * 338c2ecf20Sopenharmony_ci * Ensure that the I and D caches are coherent within specified region. 348c2ecf20Sopenharmony_ci * This is typically used when code has been written to a memory region, 358c2ecf20Sopenharmony_ci * and will be executed. 368c2ecf20Sopenharmony_ci * 378c2ecf20Sopenharmony_ci * - start - virtual start address of region 388c2ecf20Sopenharmony_ci * - end - virtual end address of region 398c2ecf20Sopenharmony_ci */ 408c2ecf20Sopenharmony_ciSYM_FUNC_START(__flush_cache_user_range) 418c2ecf20Sopenharmony_ci uaccess_ttbr0_enable x2, x3, x4 428c2ecf20Sopenharmony_cialternative_if ARM64_HAS_CACHE_IDC 438c2ecf20Sopenharmony_ci dsb ishst 448c2ecf20Sopenharmony_ci b 7f 458c2ecf20Sopenharmony_cialternative_else_nop_endif 468c2ecf20Sopenharmony_ci dcache_line_size x2, x3 478c2ecf20Sopenharmony_ci sub x3, x2, #1 488c2ecf20Sopenharmony_ci bic x4, x0, x3 498c2ecf20Sopenharmony_ci1: 508c2ecf20Sopenharmony_ciuser_alt 9f, "dc cvau, x4", "dc civac, x4", ARM64_WORKAROUND_CLEAN_CACHE 518c2ecf20Sopenharmony_ci add x4, x4, x2 528c2ecf20Sopenharmony_ci cmp x4, x1 538c2ecf20Sopenharmony_ci b.lo 1b 548c2ecf20Sopenharmony_ci dsb ish 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci7: 578c2ecf20Sopenharmony_cialternative_if ARM64_HAS_CACHE_DIC 588c2ecf20Sopenharmony_ci isb 598c2ecf20Sopenharmony_ci b 8f 608c2ecf20Sopenharmony_cialternative_else_nop_endif 618c2ecf20Sopenharmony_ci invalidate_icache_by_line x0, x1, x2, x3, 9f 628c2ecf20Sopenharmony_ci8: mov x0, #0 638c2ecf20Sopenharmony_ci1: 648c2ecf20Sopenharmony_ci uaccess_ttbr0_disable x1, x2 658c2ecf20Sopenharmony_ci ret 668c2ecf20Sopenharmony_ci9: 678c2ecf20Sopenharmony_ci mov x0, #-EFAULT 688c2ecf20Sopenharmony_ci b 1b 698c2ecf20Sopenharmony_ciSYM_FUNC_END(__flush_icache_range) 708c2ecf20Sopenharmony_ciSYM_FUNC_END(__flush_cache_user_range) 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci/* 738c2ecf20Sopenharmony_ci * invalidate_icache_range(start,end) 748c2ecf20Sopenharmony_ci * 758c2ecf20Sopenharmony_ci * Ensure that the I cache is invalid within specified region. 768c2ecf20Sopenharmony_ci * 778c2ecf20Sopenharmony_ci * - start - virtual start address of region 788c2ecf20Sopenharmony_ci * - end - virtual end address of region 798c2ecf20Sopenharmony_ci */ 808c2ecf20Sopenharmony_ciSYM_FUNC_START(invalidate_icache_range) 818c2ecf20Sopenharmony_cialternative_if ARM64_HAS_CACHE_DIC 828c2ecf20Sopenharmony_ci mov x0, xzr 838c2ecf20Sopenharmony_ci isb 848c2ecf20Sopenharmony_ci ret 858c2ecf20Sopenharmony_cialternative_else_nop_endif 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci uaccess_ttbr0_enable x2, x3, x4 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci invalidate_icache_by_line x0, x1, x2, x3, 2f 908c2ecf20Sopenharmony_ci mov x0, xzr 918c2ecf20Sopenharmony_ci1: 928c2ecf20Sopenharmony_ci uaccess_ttbr0_disable x1, x2 938c2ecf20Sopenharmony_ci ret 948c2ecf20Sopenharmony_ci2: 958c2ecf20Sopenharmony_ci mov x0, #-EFAULT 968c2ecf20Sopenharmony_ci b 1b 978c2ecf20Sopenharmony_ciSYM_FUNC_END(invalidate_icache_range) 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci/* 1008c2ecf20Sopenharmony_ci * __flush_dcache_area(kaddr, size) 1018c2ecf20Sopenharmony_ci * 1028c2ecf20Sopenharmony_ci * Ensure that any D-cache lines for the interval [kaddr, kaddr+size) 1038c2ecf20Sopenharmony_ci * are cleaned and invalidated to the PoC. 1048c2ecf20Sopenharmony_ci * 1058c2ecf20Sopenharmony_ci * - kaddr - kernel address 1068c2ecf20Sopenharmony_ci * - size - size in question 1078c2ecf20Sopenharmony_ci */ 1088c2ecf20Sopenharmony_ciSYM_FUNC_START_PI(__flush_dcache_area) 1098c2ecf20Sopenharmony_ci dcache_by_line_op civac, sy, x0, x1, x2, x3 1108c2ecf20Sopenharmony_ci ret 1118c2ecf20Sopenharmony_ciSYM_FUNC_END_PI(__flush_dcache_area) 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci/* 1148c2ecf20Sopenharmony_ci * __clean_dcache_area_pou(kaddr, size) 1158c2ecf20Sopenharmony_ci * 1168c2ecf20Sopenharmony_ci * Ensure that any D-cache lines for the interval [kaddr, kaddr+size) 1178c2ecf20Sopenharmony_ci * are cleaned to the PoU. 1188c2ecf20Sopenharmony_ci * 1198c2ecf20Sopenharmony_ci * - kaddr - kernel address 1208c2ecf20Sopenharmony_ci * - size - size in question 1218c2ecf20Sopenharmony_ci */ 1228c2ecf20Sopenharmony_ciSYM_FUNC_START(__clean_dcache_area_pou) 1238c2ecf20Sopenharmony_cialternative_if ARM64_HAS_CACHE_IDC 1248c2ecf20Sopenharmony_ci dsb ishst 1258c2ecf20Sopenharmony_ci ret 1268c2ecf20Sopenharmony_cialternative_else_nop_endif 1278c2ecf20Sopenharmony_ci dcache_by_line_op cvau, ish, x0, x1, x2, x3 1288c2ecf20Sopenharmony_ci ret 1298c2ecf20Sopenharmony_ciSYM_FUNC_END(__clean_dcache_area_pou) 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci/* 1328c2ecf20Sopenharmony_ci * __inval_dcache_area(kaddr, size) 1338c2ecf20Sopenharmony_ci * 1348c2ecf20Sopenharmony_ci * Ensure that any D-cache lines for the interval [kaddr, kaddr+size) 1358c2ecf20Sopenharmony_ci * are invalidated. Any partial lines at the ends of the interval are 1368c2ecf20Sopenharmony_ci * also cleaned to PoC to prevent data loss. 1378c2ecf20Sopenharmony_ci * 1388c2ecf20Sopenharmony_ci * - kaddr - kernel address 1398c2ecf20Sopenharmony_ci * - size - size in question 1408c2ecf20Sopenharmony_ci */ 1418c2ecf20Sopenharmony_ciSYM_FUNC_START_LOCAL(__dma_inv_area) 1428c2ecf20Sopenharmony_ciSYM_FUNC_START_PI(__inval_dcache_area) 1438c2ecf20Sopenharmony_ci /* FALLTHROUGH */ 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci/* 1468c2ecf20Sopenharmony_ci * __dma_inv_area(start, size) 1478c2ecf20Sopenharmony_ci * - start - virtual start address of region 1488c2ecf20Sopenharmony_ci * - size - size in question 1498c2ecf20Sopenharmony_ci */ 1508c2ecf20Sopenharmony_ci add x1, x1, x0 1518c2ecf20Sopenharmony_ci dcache_line_size x2, x3 1528c2ecf20Sopenharmony_ci sub x3, x2, #1 1538c2ecf20Sopenharmony_ci tst x1, x3 // end cache line aligned? 1548c2ecf20Sopenharmony_ci bic x1, x1, x3 1558c2ecf20Sopenharmony_ci b.eq 1f 1568c2ecf20Sopenharmony_ci dc civac, x1 // clean & invalidate D / U line 1578c2ecf20Sopenharmony_ci1: tst x0, x3 // start cache line aligned? 1588c2ecf20Sopenharmony_ci bic x0, x0, x3 1598c2ecf20Sopenharmony_ci b.eq 2f 1608c2ecf20Sopenharmony_ci dc civac, x0 // clean & invalidate D / U line 1618c2ecf20Sopenharmony_ci b 3f 1628c2ecf20Sopenharmony_ci2: dc ivac, x0 // invalidate D / U line 1638c2ecf20Sopenharmony_ci3: add x0, x0, x2 1648c2ecf20Sopenharmony_ci cmp x0, x1 1658c2ecf20Sopenharmony_ci b.lo 2b 1668c2ecf20Sopenharmony_ci dsb sy 1678c2ecf20Sopenharmony_ci ret 1688c2ecf20Sopenharmony_ciSYM_FUNC_END_PI(__inval_dcache_area) 1698c2ecf20Sopenharmony_ciSYM_FUNC_END(__dma_inv_area) 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci/* 1728c2ecf20Sopenharmony_ci * __clean_dcache_area_poc(kaddr, size) 1738c2ecf20Sopenharmony_ci * 1748c2ecf20Sopenharmony_ci * Ensure that any D-cache lines for the interval [kaddr, kaddr+size) 1758c2ecf20Sopenharmony_ci * are cleaned to the PoC. 1768c2ecf20Sopenharmony_ci * 1778c2ecf20Sopenharmony_ci * - kaddr - kernel address 1788c2ecf20Sopenharmony_ci * - size - size in question 1798c2ecf20Sopenharmony_ci */ 1808c2ecf20Sopenharmony_ciSYM_FUNC_START_LOCAL(__dma_clean_area) 1818c2ecf20Sopenharmony_ciSYM_FUNC_START_PI(__clean_dcache_area_poc) 1828c2ecf20Sopenharmony_ci /* FALLTHROUGH */ 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci/* 1858c2ecf20Sopenharmony_ci * __dma_clean_area(start, size) 1868c2ecf20Sopenharmony_ci * - start - virtual start address of region 1878c2ecf20Sopenharmony_ci * - size - size in question 1888c2ecf20Sopenharmony_ci */ 1898c2ecf20Sopenharmony_ci dcache_by_line_op cvac, sy, x0, x1, x2, x3 1908c2ecf20Sopenharmony_ci ret 1918c2ecf20Sopenharmony_ciSYM_FUNC_END_PI(__clean_dcache_area_poc) 1928c2ecf20Sopenharmony_ciSYM_FUNC_END(__dma_clean_area) 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci/* 1958c2ecf20Sopenharmony_ci * __clean_dcache_area_pop(kaddr, size) 1968c2ecf20Sopenharmony_ci * 1978c2ecf20Sopenharmony_ci * Ensure that any D-cache lines for the interval [kaddr, kaddr+size) 1988c2ecf20Sopenharmony_ci * are cleaned to the PoP. 1998c2ecf20Sopenharmony_ci * 2008c2ecf20Sopenharmony_ci * - kaddr - kernel address 2018c2ecf20Sopenharmony_ci * - size - size in question 2028c2ecf20Sopenharmony_ci */ 2038c2ecf20Sopenharmony_ciSYM_FUNC_START_PI(__clean_dcache_area_pop) 2048c2ecf20Sopenharmony_ci alternative_if_not ARM64_HAS_DCPOP 2058c2ecf20Sopenharmony_ci b __clean_dcache_area_poc 2068c2ecf20Sopenharmony_ci alternative_else_nop_endif 2078c2ecf20Sopenharmony_ci dcache_by_line_op cvap, sy, x0, x1, x2, x3 2088c2ecf20Sopenharmony_ci ret 2098c2ecf20Sopenharmony_ciSYM_FUNC_END_PI(__clean_dcache_area_pop) 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci/* 2128c2ecf20Sopenharmony_ci * __dma_flush_area(start, size) 2138c2ecf20Sopenharmony_ci * 2148c2ecf20Sopenharmony_ci * clean & invalidate D / U line 2158c2ecf20Sopenharmony_ci * 2168c2ecf20Sopenharmony_ci * - start - virtual start address of region 2178c2ecf20Sopenharmony_ci * - size - size in question 2188c2ecf20Sopenharmony_ci */ 2198c2ecf20Sopenharmony_ciSYM_FUNC_START_PI(__dma_flush_area) 2208c2ecf20Sopenharmony_ci dcache_by_line_op civac, sy, x0, x1, x2, x3 2218c2ecf20Sopenharmony_ci ret 2228c2ecf20Sopenharmony_ciSYM_FUNC_END_PI(__dma_flush_area) 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci/* 2258c2ecf20Sopenharmony_ci * __dma_map_area(start, size, dir) 2268c2ecf20Sopenharmony_ci * - start - kernel virtual start address 2278c2ecf20Sopenharmony_ci * - size - size of region 2288c2ecf20Sopenharmony_ci * - dir - DMA direction 2298c2ecf20Sopenharmony_ci */ 2308c2ecf20Sopenharmony_ciSYM_FUNC_START_PI(__dma_map_area) 2318c2ecf20Sopenharmony_ci b __dma_clean_area 2328c2ecf20Sopenharmony_ciSYM_FUNC_END_PI(__dma_map_area) 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci/* 2358c2ecf20Sopenharmony_ci * __dma_unmap_area(start, size, dir) 2368c2ecf20Sopenharmony_ci * - start - kernel virtual start address 2378c2ecf20Sopenharmony_ci * - size - size of region 2388c2ecf20Sopenharmony_ci * - dir - DMA direction 2398c2ecf20Sopenharmony_ci */ 2408c2ecf20Sopenharmony_ciSYM_FUNC_START_PI(__dma_unmap_area) 2418c2ecf20Sopenharmony_ci cmp w2, #DMA_TO_DEVICE 2428c2ecf20Sopenharmony_ci b.ne __dma_inv_area 2438c2ecf20Sopenharmony_ci ret 2448c2ecf20Sopenharmony_ciSYM_FUNC_END_PI(__dma_unmap_area) 245