162306a36Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0-only */
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *  linux/arch/arm/mm/cache-v6.S
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci *  Copyright (C) 2001 Deep Blue Solutions Ltd.
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci *  This is the "shell" of the ARMv6 processor support.
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci#include <linux/linkage.h>
1062306a36Sopenharmony_ci#include <linux/init.h>
1162306a36Sopenharmony_ci#include <asm/assembler.h>
1262306a36Sopenharmony_ci#include <asm/errno.h>
1362306a36Sopenharmony_ci#include <asm/unwind.h>
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#include "proc-macros.S"
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci#define HARVARD_CACHE
1862306a36Sopenharmony_ci#define CACHE_LINE_SIZE		32
1962306a36Sopenharmony_ci#define D_CACHE_LINE_SIZE	32
2062306a36Sopenharmony_ci#define BTB_FLUSH_SIZE		8
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci.arch armv6
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci/*
2562306a36Sopenharmony_ci *	v6_flush_icache_all()
2662306a36Sopenharmony_ci *
2762306a36Sopenharmony_ci *	Flush the whole I-cache.
2862306a36Sopenharmony_ci *
2962306a36Sopenharmony_ci *	ARM1136 erratum 411920 - Invalidate Instruction Cache operation can fail.
3062306a36Sopenharmony_ci *	This erratum is present in 1136, 1156 and 1176. It does not affect the
3162306a36Sopenharmony_ci *	MPCore.
3262306a36Sopenharmony_ci *
3362306a36Sopenharmony_ci *	Registers:
3462306a36Sopenharmony_ci *	r0 - set to 0
3562306a36Sopenharmony_ci *	r1 - corrupted
3662306a36Sopenharmony_ci */
3762306a36Sopenharmony_ciENTRY(v6_flush_icache_all)
3862306a36Sopenharmony_ci	mov	r0, #0
3962306a36Sopenharmony_ci#ifdef CONFIG_ARM_ERRATA_411920
4062306a36Sopenharmony_ci	mrs	r1, cpsr
4162306a36Sopenharmony_ci	cpsid	ifa				@ disable interrupts
4262306a36Sopenharmony_ci	mcr	p15, 0, r0, c7, c5, 0		@ invalidate entire I-cache
4362306a36Sopenharmony_ci	mcr	p15, 0, r0, c7, c5, 0		@ invalidate entire I-cache
4462306a36Sopenharmony_ci	mcr	p15, 0, r0, c7, c5, 0		@ invalidate entire I-cache
4562306a36Sopenharmony_ci	mcr	p15, 0, r0, c7, c5, 0		@ invalidate entire I-cache
4662306a36Sopenharmony_ci	msr	cpsr_cx, r1			@ restore interrupts
4762306a36Sopenharmony_ci	.rept	11				@ ARM Ltd recommends at least
4862306a36Sopenharmony_ci	nop					@ 11 NOPs
4962306a36Sopenharmony_ci	.endr
5062306a36Sopenharmony_ci#else
5162306a36Sopenharmony_ci	mcr	p15, 0, r0, c7, c5, 0		@ invalidate I-cache
5262306a36Sopenharmony_ci#endif
5362306a36Sopenharmony_ci	ret	lr
5462306a36Sopenharmony_ciENDPROC(v6_flush_icache_all)
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci/*
5762306a36Sopenharmony_ci *	v6_flush_cache_all()
5862306a36Sopenharmony_ci *
5962306a36Sopenharmony_ci *	Flush the entire cache.
6062306a36Sopenharmony_ci *
6162306a36Sopenharmony_ci *	It is assumed that:
6262306a36Sopenharmony_ci */
6362306a36Sopenharmony_ciENTRY(v6_flush_kern_cache_all)
6462306a36Sopenharmony_ci	mov	r0, #0
6562306a36Sopenharmony_ci#ifdef HARVARD_CACHE
6662306a36Sopenharmony_ci	mcr	p15, 0, r0, c7, c14, 0		@ D cache clean+invalidate
6762306a36Sopenharmony_ci#ifndef CONFIG_ARM_ERRATA_411920
6862306a36Sopenharmony_ci	mcr	p15, 0, r0, c7, c5, 0		@ I+BTB cache invalidate
6962306a36Sopenharmony_ci#else
7062306a36Sopenharmony_ci	b	v6_flush_icache_all
7162306a36Sopenharmony_ci#endif
7262306a36Sopenharmony_ci#else
7362306a36Sopenharmony_ci	mcr	p15, 0, r0, c7, c15, 0		@ Cache clean+invalidate
7462306a36Sopenharmony_ci#endif
7562306a36Sopenharmony_ci	ret	lr
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci/*
7862306a36Sopenharmony_ci *	v6_flush_cache_all()
7962306a36Sopenharmony_ci *
8062306a36Sopenharmony_ci *	Flush all TLB entries in a particular address space
8162306a36Sopenharmony_ci *
8262306a36Sopenharmony_ci *	- mm    - mm_struct describing address space
8362306a36Sopenharmony_ci */
8462306a36Sopenharmony_ciENTRY(v6_flush_user_cache_all)
8562306a36Sopenharmony_ci	/*FALLTHROUGH*/
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci/*
8862306a36Sopenharmony_ci *	v6_flush_cache_range(start, end, flags)
8962306a36Sopenharmony_ci *
9062306a36Sopenharmony_ci *	Flush a range of TLB entries in the specified address space.
9162306a36Sopenharmony_ci *
9262306a36Sopenharmony_ci *	- start - start address (may not be aligned)
9362306a36Sopenharmony_ci *	- end   - end address (exclusive, may not be aligned)
9462306a36Sopenharmony_ci *	- flags	- vm_area_struct flags describing address space
9562306a36Sopenharmony_ci *
9662306a36Sopenharmony_ci *	It is assumed that:
9762306a36Sopenharmony_ci *	- we have a VIPT cache.
9862306a36Sopenharmony_ci */
9962306a36Sopenharmony_ciENTRY(v6_flush_user_cache_range)
10062306a36Sopenharmony_ci	ret	lr
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci/*
10362306a36Sopenharmony_ci *	v6_coherent_kern_range(start,end)
10462306a36Sopenharmony_ci *
10562306a36Sopenharmony_ci *	Ensure that the I and D caches are coherent within specified
10662306a36Sopenharmony_ci *	region.  This is typically used when code has been written to
10762306a36Sopenharmony_ci *	a memory region, and will be executed.
10862306a36Sopenharmony_ci *
10962306a36Sopenharmony_ci *	- start   - virtual start address of region
11062306a36Sopenharmony_ci *	- end     - virtual end address of region
11162306a36Sopenharmony_ci *
11262306a36Sopenharmony_ci *	It is assumed that:
11362306a36Sopenharmony_ci *	- the Icache does not read data from the write buffer
11462306a36Sopenharmony_ci */
11562306a36Sopenharmony_ciENTRY(v6_coherent_kern_range)
11662306a36Sopenharmony_ci	/* FALLTHROUGH */
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci/*
11962306a36Sopenharmony_ci *	v6_coherent_user_range(start,end)
12062306a36Sopenharmony_ci *
12162306a36Sopenharmony_ci *	Ensure that the I and D caches are coherent within specified
12262306a36Sopenharmony_ci *	region.  This is typically used when code has been written to
12362306a36Sopenharmony_ci *	a memory region, and will be executed.
12462306a36Sopenharmony_ci *
12562306a36Sopenharmony_ci *	- start   - virtual start address of region
12662306a36Sopenharmony_ci *	- end     - virtual end address of region
12762306a36Sopenharmony_ci *
12862306a36Sopenharmony_ci *	It is assumed that:
12962306a36Sopenharmony_ci *	- the Icache does not read data from the write buffer
13062306a36Sopenharmony_ci */
13162306a36Sopenharmony_ciENTRY(v6_coherent_user_range)
13262306a36Sopenharmony_ci UNWIND(.fnstart		)
13362306a36Sopenharmony_ci#ifdef HARVARD_CACHE
13462306a36Sopenharmony_ci	bic	r0, r0, #CACHE_LINE_SIZE - 1
13562306a36Sopenharmony_ci1:
13662306a36Sopenharmony_ci USER(	mcr	p15, 0, r0, c7, c10, 1	)	@ clean D line
13762306a36Sopenharmony_ci	add	r0, r0, #CACHE_LINE_SIZE
13862306a36Sopenharmony_ci	cmp	r0, r1
13962306a36Sopenharmony_ci	blo	1b
14062306a36Sopenharmony_ci#endif
14162306a36Sopenharmony_ci	mov	r0, #0
14262306a36Sopenharmony_ci#ifdef HARVARD_CACHE
14362306a36Sopenharmony_ci	mcr	p15, 0, r0, c7, c10, 4		@ drain write buffer
14462306a36Sopenharmony_ci#ifndef CONFIG_ARM_ERRATA_411920
14562306a36Sopenharmony_ci	mcr	p15, 0, r0, c7, c5, 0		@ I+BTB cache invalidate
14662306a36Sopenharmony_ci#else
14762306a36Sopenharmony_ci	b	v6_flush_icache_all
14862306a36Sopenharmony_ci#endif
14962306a36Sopenharmony_ci#else
15062306a36Sopenharmony_ci	mcr	p15, 0, r0, c7, c5, 6		@ invalidate BTB
15162306a36Sopenharmony_ci#endif
15262306a36Sopenharmony_ci	ret	lr
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci/*
15562306a36Sopenharmony_ci * Fault handling for the cache operation above. If the virtual address in r0
15662306a36Sopenharmony_ci * isn't mapped, fail with -EFAULT.
15762306a36Sopenharmony_ci */
15862306a36Sopenharmony_ci9001:
15962306a36Sopenharmony_ci	mov	r0, #-EFAULT
16062306a36Sopenharmony_ci	ret	lr
16162306a36Sopenharmony_ci UNWIND(.fnend		)
16262306a36Sopenharmony_ciENDPROC(v6_coherent_user_range)
16362306a36Sopenharmony_ciENDPROC(v6_coherent_kern_range)
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci/*
16662306a36Sopenharmony_ci *	v6_flush_kern_dcache_area(void *addr, size_t size)
16762306a36Sopenharmony_ci *
16862306a36Sopenharmony_ci *	Ensure that the data held in the page kaddr is written back
16962306a36Sopenharmony_ci *	to the page in question.
17062306a36Sopenharmony_ci *
17162306a36Sopenharmony_ci *	- addr	- kernel address
17262306a36Sopenharmony_ci *	- size	- region size
17362306a36Sopenharmony_ci */
17462306a36Sopenharmony_ciENTRY(v6_flush_kern_dcache_area)
17562306a36Sopenharmony_ci	add	r1, r0, r1
17662306a36Sopenharmony_ci	bic	r0, r0, #D_CACHE_LINE_SIZE - 1
17762306a36Sopenharmony_ci1:
17862306a36Sopenharmony_ci#ifdef HARVARD_CACHE
17962306a36Sopenharmony_ci	mcr	p15, 0, r0, c7, c14, 1		@ clean & invalidate D line
18062306a36Sopenharmony_ci#else
18162306a36Sopenharmony_ci	mcr	p15, 0, r0, c7, c15, 1		@ clean & invalidate unified line
18262306a36Sopenharmony_ci#endif
18362306a36Sopenharmony_ci	add	r0, r0, #D_CACHE_LINE_SIZE
18462306a36Sopenharmony_ci	cmp	r0, r1
18562306a36Sopenharmony_ci	blo	1b
18662306a36Sopenharmony_ci#ifdef HARVARD_CACHE
18762306a36Sopenharmony_ci	mov	r0, #0
18862306a36Sopenharmony_ci	mcr	p15, 0, r0, c7, c10, 4
18962306a36Sopenharmony_ci#endif
19062306a36Sopenharmony_ci	ret	lr
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci/*
19462306a36Sopenharmony_ci *	v6_dma_inv_range(start,end)
19562306a36Sopenharmony_ci *
19662306a36Sopenharmony_ci *	Invalidate the data cache within the specified region; we will
19762306a36Sopenharmony_ci *	be performing a DMA operation in this region and we want to
19862306a36Sopenharmony_ci *	purge old data in the cache.
19962306a36Sopenharmony_ci *
20062306a36Sopenharmony_ci *	- start   - virtual start address of region
20162306a36Sopenharmony_ci *	- end     - virtual end address of region
20262306a36Sopenharmony_ci */
20362306a36Sopenharmony_civ6_dma_inv_range:
20462306a36Sopenharmony_ci#ifdef CONFIG_DMA_CACHE_RWFO
20562306a36Sopenharmony_ci	ldrb	r2, [r0]			@ read for ownership
20662306a36Sopenharmony_ci	strb	r2, [r0]			@ write for ownership
20762306a36Sopenharmony_ci#endif
20862306a36Sopenharmony_ci	tst	r0, #D_CACHE_LINE_SIZE - 1
20962306a36Sopenharmony_ci	bic	r0, r0, #D_CACHE_LINE_SIZE - 1
21062306a36Sopenharmony_ci#ifdef HARVARD_CACHE
21162306a36Sopenharmony_ci	mcrne	p15, 0, r0, c7, c10, 1		@ clean D line
21262306a36Sopenharmony_ci#else
21362306a36Sopenharmony_ci	mcrne	p15, 0, r0, c7, c11, 1		@ clean unified line
21462306a36Sopenharmony_ci#endif
21562306a36Sopenharmony_ci	tst	r1, #D_CACHE_LINE_SIZE - 1
21662306a36Sopenharmony_ci#ifdef CONFIG_DMA_CACHE_RWFO
21762306a36Sopenharmony_ci	ldrbne	r2, [r1, #-1]			@ read for ownership
21862306a36Sopenharmony_ci	strbne	r2, [r1, #-1]			@ write for ownership
21962306a36Sopenharmony_ci#endif
22062306a36Sopenharmony_ci	bic	r1, r1, #D_CACHE_LINE_SIZE - 1
22162306a36Sopenharmony_ci#ifdef HARVARD_CACHE
22262306a36Sopenharmony_ci	mcrne	p15, 0, r1, c7, c14, 1		@ clean & invalidate D line
22362306a36Sopenharmony_ci#else
22462306a36Sopenharmony_ci	mcrne	p15, 0, r1, c7, c15, 1		@ clean & invalidate unified line
22562306a36Sopenharmony_ci#endif
22662306a36Sopenharmony_ci1:
22762306a36Sopenharmony_ci#ifdef HARVARD_CACHE
22862306a36Sopenharmony_ci	mcr	p15, 0, r0, c7, c6, 1		@ invalidate D line
22962306a36Sopenharmony_ci#else
23062306a36Sopenharmony_ci	mcr	p15, 0, r0, c7, c7, 1		@ invalidate unified line
23162306a36Sopenharmony_ci#endif
23262306a36Sopenharmony_ci	add	r0, r0, #D_CACHE_LINE_SIZE
23362306a36Sopenharmony_ci	cmp	r0, r1
23462306a36Sopenharmony_ci#ifdef CONFIG_DMA_CACHE_RWFO
23562306a36Sopenharmony_ci	ldrlo	r2, [r0]			@ read for ownership
23662306a36Sopenharmony_ci	strlo	r2, [r0]			@ write for ownership
23762306a36Sopenharmony_ci#endif
23862306a36Sopenharmony_ci	blo	1b
23962306a36Sopenharmony_ci	mov	r0, #0
24062306a36Sopenharmony_ci	mcr	p15, 0, r0, c7, c10, 4		@ drain write buffer
24162306a36Sopenharmony_ci	ret	lr
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci/*
24462306a36Sopenharmony_ci *	v6_dma_clean_range(start,end)
24562306a36Sopenharmony_ci *	- start   - virtual start address of region
24662306a36Sopenharmony_ci *	- end     - virtual end address of region
24762306a36Sopenharmony_ci */
24862306a36Sopenharmony_civ6_dma_clean_range:
24962306a36Sopenharmony_ci	bic	r0, r0, #D_CACHE_LINE_SIZE - 1
25062306a36Sopenharmony_ci1:
25162306a36Sopenharmony_ci#ifdef CONFIG_DMA_CACHE_RWFO
25262306a36Sopenharmony_ci	ldr	r2, [r0]			@ read for ownership
25362306a36Sopenharmony_ci#endif
25462306a36Sopenharmony_ci#ifdef HARVARD_CACHE
25562306a36Sopenharmony_ci	mcr	p15, 0, r0, c7, c10, 1		@ clean D line
25662306a36Sopenharmony_ci#else
25762306a36Sopenharmony_ci	mcr	p15, 0, r0, c7, c11, 1		@ clean unified line
25862306a36Sopenharmony_ci#endif
25962306a36Sopenharmony_ci	add	r0, r0, #D_CACHE_LINE_SIZE
26062306a36Sopenharmony_ci	cmp	r0, r1
26162306a36Sopenharmony_ci	blo	1b
26262306a36Sopenharmony_ci	mov	r0, #0
26362306a36Sopenharmony_ci	mcr	p15, 0, r0, c7, c10, 4		@ drain write buffer
26462306a36Sopenharmony_ci	ret	lr
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci/*
26762306a36Sopenharmony_ci *	v6_dma_flush_range(start,end)
26862306a36Sopenharmony_ci *	- start   - virtual start address of region
26962306a36Sopenharmony_ci *	- end     - virtual end address of region
27062306a36Sopenharmony_ci */
27162306a36Sopenharmony_ciENTRY(v6_dma_flush_range)
27262306a36Sopenharmony_ci#ifdef CONFIG_DMA_CACHE_RWFO
27362306a36Sopenharmony_ci	ldrb	r2, [r0]		@ read for ownership
27462306a36Sopenharmony_ci	strb	r2, [r0]		@ write for ownership
27562306a36Sopenharmony_ci#endif
27662306a36Sopenharmony_ci	bic	r0, r0, #D_CACHE_LINE_SIZE - 1
27762306a36Sopenharmony_ci1:
27862306a36Sopenharmony_ci#ifdef HARVARD_CACHE
27962306a36Sopenharmony_ci	mcr	p15, 0, r0, c7, c14, 1		@ clean & invalidate D line
28062306a36Sopenharmony_ci#else
28162306a36Sopenharmony_ci	mcr	p15, 0, r0, c7, c15, 1		@ clean & invalidate line
28262306a36Sopenharmony_ci#endif
28362306a36Sopenharmony_ci	add	r0, r0, #D_CACHE_LINE_SIZE
28462306a36Sopenharmony_ci	cmp	r0, r1
28562306a36Sopenharmony_ci#ifdef CONFIG_DMA_CACHE_RWFO
28662306a36Sopenharmony_ci	ldrblo	r2, [r0]			@ read for ownership
28762306a36Sopenharmony_ci	strblo	r2, [r0]			@ write for ownership
28862306a36Sopenharmony_ci#endif
28962306a36Sopenharmony_ci	blo	1b
29062306a36Sopenharmony_ci	mov	r0, #0
29162306a36Sopenharmony_ci	mcr	p15, 0, r0, c7, c10, 4		@ drain write buffer
29262306a36Sopenharmony_ci	ret	lr
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci/*
29562306a36Sopenharmony_ci *	dma_map_area(start, size, dir)
29662306a36Sopenharmony_ci *	- start	- kernel virtual start address
29762306a36Sopenharmony_ci *	- size	- size of region
29862306a36Sopenharmony_ci *	- dir	- DMA direction
29962306a36Sopenharmony_ci */
30062306a36Sopenharmony_ciENTRY(v6_dma_map_area)
30162306a36Sopenharmony_ci	add	r1, r1, r0
30262306a36Sopenharmony_ci	teq	r2, #DMA_FROM_DEVICE
30362306a36Sopenharmony_ci	beq	v6_dma_inv_range
30462306a36Sopenharmony_ci#ifndef CONFIG_DMA_CACHE_RWFO
30562306a36Sopenharmony_ci	b	v6_dma_clean_range
30662306a36Sopenharmony_ci#else
30762306a36Sopenharmony_ci	teq	r2, #DMA_TO_DEVICE
30862306a36Sopenharmony_ci	beq	v6_dma_clean_range
30962306a36Sopenharmony_ci	b	v6_dma_flush_range
31062306a36Sopenharmony_ci#endif
31162306a36Sopenharmony_ciENDPROC(v6_dma_map_area)
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci/*
31462306a36Sopenharmony_ci *	dma_unmap_area(start, size, dir)
31562306a36Sopenharmony_ci *	- start	- kernel virtual start address
31662306a36Sopenharmony_ci *	- size	- size of region
31762306a36Sopenharmony_ci *	- dir	- DMA direction
31862306a36Sopenharmony_ci */
31962306a36Sopenharmony_ciENTRY(v6_dma_unmap_area)
32062306a36Sopenharmony_ci#ifndef CONFIG_DMA_CACHE_RWFO
32162306a36Sopenharmony_ci	add	r1, r1, r0
32262306a36Sopenharmony_ci	teq	r2, #DMA_TO_DEVICE
32362306a36Sopenharmony_ci	bne	v6_dma_inv_range
32462306a36Sopenharmony_ci#endif
32562306a36Sopenharmony_ci	ret	lr
32662306a36Sopenharmony_ciENDPROC(v6_dma_unmap_area)
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci	.globl	v6_flush_kern_cache_louis
32962306a36Sopenharmony_ci	.equ	v6_flush_kern_cache_louis, v6_flush_kern_cache_all
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_ci	__INITDATA
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci	@ define struct cpu_cache_fns (see <asm/cacheflush.h> and proc-macros.S)
33462306a36Sopenharmony_ci	define_cache_functions v6
335