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