Linux 5.7.6
[linux/fpc-iii.git] / arch / arm / mm / cache-v7m.S
blob1bc3a0a507539d0e71e365ee4f0c476cea387654
1 /* SPDX-License-Identifier: GPL-2.0-only */
2 /*
3  *  linux/arch/arm/mm/cache-v7m.S
4  *
5  *  Based on linux/arch/arm/mm/cache-v7.S
6  *
7  *  Copyright (C) 2001 Deep Blue Solutions Ltd.
8  *  Copyright (C) 2005 ARM Ltd.
9  *
10  *  This is the "shell" of the ARMv7M processor support.
11  */
12 #include <linux/linkage.h>
13 #include <linux/init.h>
14 #include <asm/assembler.h>
15 #include <asm/errno.h>
16 #include <asm/unwind.h>
17 #include <asm/v7m.h>
19 #include "proc-macros.S"
21 /* Generic V7M read/write macros for memory mapped cache operations */
22 .macro v7m_cache_read, rt, reg
23         movw    \rt, #:lower16:BASEADDR_V7M_SCB + \reg
24         movt    \rt, #:upper16:BASEADDR_V7M_SCB + \reg
25         ldr     \rt, [\rt]
26 .endm
28 .macro v7m_cacheop, rt, tmp, op, c = al
29         movw\c  \tmp, #:lower16:BASEADDR_V7M_SCB + \op
30         movt\c  \tmp, #:upper16:BASEADDR_V7M_SCB + \op
31         str\c   \rt, [\tmp]
32 .endm
35 .macro  read_ccsidr, rt
36         v7m_cache_read \rt, V7M_SCB_CCSIDR
37 .endm
39 .macro read_clidr, rt
40         v7m_cache_read \rt, V7M_SCB_CLIDR
41 .endm
43 .macro  write_csselr, rt, tmp
44         v7m_cacheop \rt, \tmp, V7M_SCB_CSSELR
45 .endm
48  * dcisw: Invalidate data cache by set/way
49  */
50 .macro dcisw, rt, tmp
51         v7m_cacheop \rt, \tmp, V7M_SCB_DCISW
52 .endm
55  * dccisw: Clean and invalidate data cache by set/way
56  */
57 .macro dccisw, rt, tmp
58         v7m_cacheop \rt, \tmp, V7M_SCB_DCCISW
59 .endm
62  * dccimvac: Clean and invalidate data cache line by MVA to PoC.
63  */
64 .irp    c,,eq,ne,cs,cc,mi,pl,vs,vc,hi,ls,ge,lt,gt,le,hs,lo
65 .macro dccimvac\c, rt, tmp
66         v7m_cacheop \rt, \tmp, V7M_SCB_DCCIMVAC, \c
67 .endm
68 .endr
71  * dcimvac: Invalidate data cache line by MVA to PoC
72  */
73 .irp    c,,eq,ne,cs,cc,mi,pl,vs,vc,hi,ls,ge,lt,gt,le,hs,lo
74 .macro dcimvac\c, rt, tmp
75         v7m_cacheop \rt, \tmp, V7M_SCB_DCIMVAC, \c
76 .endm
77 .endr
80  * dccmvau: Clean data cache line by MVA to PoU
81  */
82 .macro dccmvau, rt, tmp
83         v7m_cacheop \rt, \tmp, V7M_SCB_DCCMVAU
84 .endm
87  * dccmvac: Clean data cache line by MVA to PoC
88  */
89 .macro dccmvac,  rt, tmp
90         v7m_cacheop \rt, \tmp, V7M_SCB_DCCMVAC
91 .endm
94  * icimvau: Invalidate instruction caches by MVA to PoU
95  */
96 .macro icimvau, rt, tmp
97         v7m_cacheop \rt, \tmp, V7M_SCB_ICIMVAU
98 .endm
101  * Invalidate the icache, inner shareable if SMP, invalidate BTB for UP.
102  * rt data ignored by ICIALLU(IS), so can be used for the address
103  */
104 .macro invalidate_icache, rt
105         v7m_cacheop \rt, \rt, V7M_SCB_ICIALLU
106         mov \rt, #0
107 .endm
110  * Invalidate the BTB, inner shareable if SMP.
111  * rt data ignored by BPIALL, so it can be used for the address
112  */
113 .macro invalidate_bp, rt
114         v7m_cacheop \rt, \rt, V7M_SCB_BPIALL
115         mov \rt, #0
116 .endm
118 ENTRY(v7m_invalidate_l1)
119         mov     r0, #0
121         write_csselr r0, r1
122         read_ccsidr r0
124         movw    r1, #0x7fff
125         and     r2, r1, r0, lsr #13
127         movw    r1, #0x3ff
129         and     r3, r1, r0, lsr #3      @ NumWays - 1
130         add     r2, r2, #1              @ NumSets
132         and     r0, r0, #0x7
133         add     r0, r0, #4      @ SetShift
135         clz     r1, r3          @ WayShift
136         add     r4, r3, #1      @ NumWays
137 1:      sub     r2, r2, #1      @ NumSets--
138         mov     r3, r4          @ Temp = NumWays
139 2:      subs    r3, r3, #1      @ Temp--
140         mov     r5, r3, lsl r1
141         mov     r6, r2, lsl r0
142         orr     r5, r5, r6      @ Reg = (Temp<<WayShift)|(NumSets<<SetShift)
143         dcisw   r5, r6
144         bgt     2b
145         cmp     r2, #0
146         bgt     1b
147         dsb     st
148         isb
149         ret     lr
150 ENDPROC(v7m_invalidate_l1)
153  *      v7m_flush_icache_all()
155  *      Flush the whole I-cache.
157  *      Registers:
158  *      r0 - set to 0
159  */
160 ENTRY(v7m_flush_icache_all)
161         invalidate_icache r0
162         ret     lr
163 ENDPROC(v7m_flush_icache_all)
166  *      v7m_flush_dcache_all()
168  *      Flush the whole D-cache.
170  *      Corrupted registers: r0-r7, r9-r11
171  */
172 ENTRY(v7m_flush_dcache_all)
173         dmb                                     @ ensure ordering with previous memory accesses
174         read_clidr r0
175         mov     r3, r0, lsr #23                 @ move LoC into position
176         ands    r3, r3, #7 << 1                 @ extract LoC*2 from clidr
177         beq     finished                        @ if loc is 0, then no need to clean
178 start_flush_levels:
179         mov     r10, #0                         @ start clean at cache level 0
180 flush_levels:
181         add     r2, r10, r10, lsr #1            @ work out 3x current cache level
182         mov     r1, r0, lsr r2                  @ extract cache type bits from clidr
183         and     r1, r1, #7                      @ mask of the bits for current cache only
184         cmp     r1, #2                          @ see what cache we have at this level
185         blt     skip                            @ skip if no cache, or just i-cache
186 #ifdef CONFIG_PREEMPTION
187         save_and_disable_irqs_notrace r9        @ make cssr&csidr read atomic
188 #endif
189         write_csselr r10, r1                    @ set current cache level
190         isb                                     @ isb to sych the new cssr&csidr
191         read_ccsidr r1                          @ read the new csidr
192 #ifdef CONFIG_PREEMPTION
193         restore_irqs_notrace r9
194 #endif
195         and     r2, r1, #7                      @ extract the length of the cache lines
196         add     r2, r2, #4                      @ add 4 (line length offset)
197         movw    r4, #0x3ff
198         ands    r4, r4, r1, lsr #3              @ find maximum number on the way size
199         clz     r5, r4                          @ find bit position of way size increment
200         movw    r7, #0x7fff
201         ands    r7, r7, r1, lsr #13             @ extract max number of the index size
202 loop1:
203         mov     r9, r7                          @ create working copy of max index
204 loop2:
205         lsl     r6, r4, r5
206         orr     r11, r10, r6                    @ factor way and cache number into r11
207         lsl     r6, r9, r2
208         orr     r11, r11, r6                    @ factor index number into r11
209         dccisw  r11, r6                         @ clean/invalidate by set/way
210         subs    r9, r9, #1                      @ decrement the index
211         bge     loop2
212         subs    r4, r4, #1                      @ decrement the way
213         bge     loop1
214 skip:
215         add     r10, r10, #2                    @ increment cache number
216         cmp     r3, r10
217         bgt     flush_levels
218 finished:
219         mov     r10, #0                         @ switch back to cache level 0
220         write_csselr r10, r3                    @ select current cache level in cssr
221         dsb     st
222         isb
223         ret     lr
224 ENDPROC(v7m_flush_dcache_all)
227  *      v7m_flush_cache_all()
229  *      Flush the entire cache system.
230  *  The data cache flush is now achieved using atomic clean / invalidates
231  *  working outwards from L1 cache. This is done using Set/Way based cache
232  *  maintenance instructions.
233  *  The instruction cache can still be invalidated back to the point of
234  *  unification in a single instruction.
236  */
237 ENTRY(v7m_flush_kern_cache_all)
238         stmfd   sp!, {r4-r7, r9-r11, lr}
239         bl      v7m_flush_dcache_all
240         invalidate_icache r0
241         ldmfd   sp!, {r4-r7, r9-r11, lr}
242         ret     lr
243 ENDPROC(v7m_flush_kern_cache_all)
246  *      v7m_flush_cache_all()
248  *      Flush all TLB entries in a particular address space
250  *      - mm    - mm_struct describing address space
251  */
252 ENTRY(v7m_flush_user_cache_all)
253         /*FALLTHROUGH*/
256  *      v7m_flush_cache_range(start, end, flags)
258  *      Flush a range of TLB entries in the specified address space.
260  *      - start - start address (may not be aligned)
261  *      - end   - end address (exclusive, may not be aligned)
262  *      - flags - vm_area_struct flags describing address space
264  *      It is assumed that:
265  *      - we have a VIPT cache.
266  */
267 ENTRY(v7m_flush_user_cache_range)
268         ret     lr
269 ENDPROC(v7m_flush_user_cache_all)
270 ENDPROC(v7m_flush_user_cache_range)
273  *      v7m_coherent_kern_range(start,end)
275  *      Ensure that the I and D caches are coherent within specified
276  *      region.  This is typically used when code has been written to
277  *      a memory region, and will be executed.
279  *      - start   - virtual start address of region
280  *      - end     - virtual end address of region
282  *      It is assumed that:
283  *      - the Icache does not read data from the write buffer
284  */
285 ENTRY(v7m_coherent_kern_range)
286         /* FALLTHROUGH */
289  *      v7m_coherent_user_range(start,end)
291  *      Ensure that the I and D caches are coherent within specified
292  *      region.  This is typically used when code has been written to
293  *      a memory region, and will be executed.
295  *      - start   - virtual start address of region
296  *      - end     - virtual end address of region
298  *      It is assumed that:
299  *      - the Icache does not read data from the write buffer
300  */
301 ENTRY(v7m_coherent_user_range)
302  UNWIND(.fnstart                )
303         dcache_line_size r2, r3
304         sub     r3, r2, #1
305         bic     r12, r0, r3
308  * We use open coded version of dccmvau otherwise USER() would
309  * point at movw instruction.
310  */
311         dccmvau r12, r3
312         add     r12, r12, r2
313         cmp     r12, r1
314         blo     1b
315         dsb     ishst
316         icache_line_size r2, r3
317         sub     r3, r2, #1
318         bic     r12, r0, r3
320         icimvau r12, r3
321         add     r12, r12, r2
322         cmp     r12, r1
323         blo     2b
324         invalidate_bp r0
325         dsb     ishst
326         isb
327         ret     lr
328  UNWIND(.fnend          )
329 ENDPROC(v7m_coherent_kern_range)
330 ENDPROC(v7m_coherent_user_range)
333  *      v7m_flush_kern_dcache_area(void *addr, size_t size)
335  *      Ensure that the data held in the page kaddr is written back
336  *      to the page in question.
338  *      - addr  - kernel address
339  *      - size  - region size
340  */
341 ENTRY(v7m_flush_kern_dcache_area)
342         dcache_line_size r2, r3
343         add     r1, r0, r1
344         sub     r3, r2, #1
345         bic     r0, r0, r3
347         dccimvac r0, r3         @ clean & invalidate D line / unified line
348         add     r0, r0, r2
349         cmp     r0, r1
350         blo     1b
351         dsb     st
352         ret     lr
353 ENDPROC(v7m_flush_kern_dcache_area)
356  *      v7m_dma_inv_range(start,end)
358  *      Invalidate the data cache within the specified region; we will
359  *      be performing a DMA operation in this region and we want to
360  *      purge old data in the cache.
362  *      - start   - virtual start address of region
363  *      - end     - virtual end address of region
364  */
365 v7m_dma_inv_range:
366         dcache_line_size r2, r3
367         sub     r3, r2, #1
368         tst     r0, r3
369         bic     r0, r0, r3
370         dccimvacne r0, r3
371         addne   r0, r0, r2
372         subne   r3, r2, #1      @ restore r3, corrupted by v7m's dccimvac
373         tst     r1, r3
374         bic     r1, r1, r3
375         dccimvacne r1, r3
376         cmp     r0, r1
378         dcimvaclo r0, r3
379         addlo   r0, r0, r2
380         cmplo   r0, r1
381         blo     1b
382         dsb     st
383         ret     lr
384 ENDPROC(v7m_dma_inv_range)
387  *      v7m_dma_clean_range(start,end)
388  *      - start   - virtual start address of region
389  *      - end     - virtual end address of region
390  */
391 v7m_dma_clean_range:
392         dcache_line_size r2, r3
393         sub     r3, r2, #1
394         bic     r0, r0, r3
396         dccmvac r0, r3                  @ clean D / U line
397         add     r0, r0, r2
398         cmp     r0, r1
399         blo     1b
400         dsb     st
401         ret     lr
402 ENDPROC(v7m_dma_clean_range)
405  *      v7m_dma_flush_range(start,end)
406  *      - start   - virtual start address of region
407  *      - end     - virtual end address of region
408  */
409 ENTRY(v7m_dma_flush_range)
410         dcache_line_size r2, r3
411         sub     r3, r2, #1
412         bic     r0, r0, r3
414         dccimvac r0, r3                  @ clean & invalidate D / U line
415         add     r0, r0, r2
416         cmp     r0, r1
417         blo     1b
418         dsb     st
419         ret     lr
420 ENDPROC(v7m_dma_flush_range)
423  *      dma_map_area(start, size, dir)
424  *      - start - kernel virtual start address
425  *      - size  - size of region
426  *      - dir   - DMA direction
427  */
428 ENTRY(v7m_dma_map_area)
429         add     r1, r1, r0
430         teq     r2, #DMA_FROM_DEVICE
431         beq     v7m_dma_inv_range
432         b       v7m_dma_clean_range
433 ENDPROC(v7m_dma_map_area)
436  *      dma_unmap_area(start, size, dir)
437  *      - start - kernel virtual start address
438  *      - size  - size of region
439  *      - dir   - DMA direction
440  */
441 ENTRY(v7m_dma_unmap_area)
442         add     r1, r1, r0
443         teq     r2, #DMA_TO_DEVICE
444         bne     v7m_dma_inv_range
445         ret     lr
446 ENDPROC(v7m_dma_unmap_area)
448         .globl  v7m_flush_kern_cache_louis
449         .equ    v7m_flush_kern_cache_louis, v7m_flush_kern_cache_all
451         __INITDATA
453         @ define struct cpu_cache_fns (see <asm/cacheflush.h> and proc-macros.S)
454         define_cache_functions v7m