cache-v7m.S 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451
  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 <linux/cfi_types.h>
  15. #include <asm/assembler.h>
  16. #include <asm/errno.h>
  17. #include <asm/unwind.h>
  18. #include <asm/v7m.h>
  19. #include "proc-macros.S"
  20. .arch armv7-m
  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
  27. .macro v7m_cacheop, rt, tmp, op, c = al
  28. movw\c \tmp, #:lower16:BASEADDR_V7M_SCB + \op
  29. movt\c \tmp, #:upper16:BASEADDR_V7M_SCB + \op
  30. str\c \rt, [\tmp]
  31. .endm
  32. .macro read_ccsidr, rt
  33. v7m_cache_read \rt, V7M_SCB_CCSIDR
  34. .endm
  35. .macro read_clidr, rt
  36. v7m_cache_read \rt, V7M_SCB_CLIDR
  37. .endm
  38. .macro write_csselr, rt, tmp
  39. v7m_cacheop \rt, \tmp, V7M_SCB_CSSELR
  40. .endm
  41. /*
  42. * dcisw: Invalidate data cache by set/way
  43. */
  44. .macro dcisw, rt, tmp
  45. v7m_cacheop \rt, \tmp, V7M_SCB_DCISW
  46. .endm
  47. /*
  48. * dccisw: Clean and invalidate data cache by set/way
  49. */
  50. .macro dccisw, rt, tmp
  51. v7m_cacheop \rt, \tmp, V7M_SCB_DCCISW
  52. .endm
  53. /*
  54. * dccimvac: Clean and invalidate data cache line by MVA to PoC.
  55. */
  56. .irp c,,eq,ne,cs,cc,mi,pl,vs,vc,hi,ls,ge,lt,gt,le,hs,lo
  57. .macro dccimvac\c, rt, tmp
  58. v7m_cacheop \rt, \tmp, V7M_SCB_DCCIMVAC, \c
  59. .endm
  60. .endr
  61. /*
  62. * dcimvac: 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 dcimvac\c, rt, tmp
  66. v7m_cacheop \rt, \tmp, V7M_SCB_DCIMVAC, \c
  67. .endm
  68. .endr
  69. /*
  70. * dccmvau: Clean data cache line by MVA to PoU
  71. */
  72. .macro dccmvau, rt, tmp
  73. v7m_cacheop \rt, \tmp, V7M_SCB_DCCMVAU
  74. .endm
  75. /*
  76. * dccmvac: Clean data cache line by MVA to PoC
  77. */
  78. .macro dccmvac, rt, tmp
  79. v7m_cacheop \rt, \tmp, V7M_SCB_DCCMVAC
  80. .endm
  81. /*
  82. * icimvau: Invalidate instruction caches by MVA to PoU
  83. */
  84. .macro icimvau, rt, tmp
  85. v7m_cacheop \rt, \tmp, V7M_SCB_ICIMVAU
  86. .endm
  87. /*
  88. * Invalidate the icache, inner shareable if SMP, invalidate BTB for UP.
  89. * rt data ignored by ICIALLU(IS), so can be used for the address
  90. */
  91. .macro invalidate_icache, rt
  92. v7m_cacheop \rt, \rt, V7M_SCB_ICIALLU
  93. mov \rt, #0
  94. .endm
  95. /*
  96. * Invalidate the BTB, inner shareable if SMP.
  97. * rt data ignored by BPIALL, so it can be used for the address
  98. */
  99. .macro invalidate_bp, rt
  100. v7m_cacheop \rt, \rt, V7M_SCB_BPIALL
  101. mov \rt, #0
  102. .endm
  103. ENTRY(v7m_invalidate_l1)
  104. mov r0, #0
  105. write_csselr r0, r1
  106. read_ccsidr r0
  107. movw r1, #0x7fff
  108. and r2, r1, r0, lsr #13
  109. movw r1, #0x3ff
  110. and r3, r1, r0, lsr #3 @ NumWays - 1
  111. add r2, r2, #1 @ NumSets
  112. and r0, r0, #0x7
  113. add r0, r0, #4 @ SetShift
  114. clz r1, r3 @ WayShift
  115. add r4, r3, #1 @ NumWays
  116. 1: sub r2, r2, #1 @ NumSets--
  117. mov r3, r4 @ Temp = NumWays
  118. 2: subs r3, r3, #1 @ Temp--
  119. mov r5, r3, lsl r1
  120. mov r6, r2, lsl r0
  121. orr r5, r5, r6 @ Reg = (Temp<<WayShift)|(NumSets<<SetShift)
  122. dcisw r5, r6
  123. bgt 2b
  124. cmp r2, #0
  125. bgt 1b
  126. dsb st
  127. isb
  128. ret lr
  129. ENDPROC(v7m_invalidate_l1)
  130. /*
  131. * v7m_flush_icache_all()
  132. *
  133. * Flush the whole I-cache.
  134. *
  135. * Registers:
  136. * r0 - set to 0
  137. */
  138. SYM_TYPED_FUNC_START(v7m_flush_icache_all)
  139. invalidate_icache r0
  140. ret lr
  141. SYM_FUNC_END(v7m_flush_icache_all)
  142. /*
  143. * v7m_flush_dcache_all()
  144. *
  145. * Flush the whole D-cache.
  146. *
  147. * Corrupted registers: r0-r7, r9-r11
  148. */
  149. ENTRY(v7m_flush_dcache_all)
  150. dmb @ ensure ordering with previous memory accesses
  151. read_clidr r0
  152. mov r3, r0, lsr #23 @ move LoC into position
  153. ands r3, r3, #7 << 1 @ extract LoC*2 from clidr
  154. beq finished @ if loc is 0, then no need to clean
  155. start_flush_levels:
  156. mov r10, #0 @ start clean at cache level 0
  157. flush_levels:
  158. add r2, r10, r10, lsr #1 @ work out 3x current cache level
  159. mov r1, r0, lsr r2 @ extract cache type bits from clidr
  160. and r1, r1, #7 @ mask of the bits for current cache only
  161. cmp r1, #2 @ see what cache we have at this level
  162. blt skip @ skip if no cache, or just i-cache
  163. #ifdef CONFIG_PREEMPTION
  164. save_and_disable_irqs_notrace r9 @ make cssr&csidr read atomic
  165. #endif
  166. write_csselr r10, r1 @ set current cache level
  167. isb @ isb to sych the new cssr&csidr
  168. read_ccsidr r1 @ read the new csidr
  169. #ifdef CONFIG_PREEMPTION
  170. restore_irqs_notrace r9
  171. #endif
  172. and r2, r1, #7 @ extract the length of the cache lines
  173. add r2, r2, #4 @ add 4 (line length offset)
  174. movw r4, #0x3ff
  175. ands r4, r4, r1, lsr #3 @ find maximum number on the way size
  176. clz r5, r4 @ find bit position of way size increment
  177. movw r7, #0x7fff
  178. ands r7, r7, r1, lsr #13 @ extract max number of the index size
  179. loop1:
  180. mov r9, r7 @ create working copy of max index
  181. loop2:
  182. lsl r6, r4, r5
  183. orr r11, r10, r6 @ factor way and cache number into r11
  184. lsl r6, r9, r2
  185. orr r11, r11, r6 @ factor index number into r11
  186. dccisw r11, r6 @ clean/invalidate by set/way
  187. subs r9, r9, #1 @ decrement the index
  188. bge loop2
  189. subs r4, r4, #1 @ decrement the way
  190. bge loop1
  191. skip:
  192. add r10, r10, #2 @ increment cache number
  193. cmp r3, r10
  194. bgt flush_levels
  195. finished:
  196. mov r10, #0 @ switch back to cache level 0
  197. write_csselr r10, r3 @ select current cache level in cssr
  198. dsb st
  199. isb
  200. ret lr
  201. ENDPROC(v7m_flush_dcache_all)
  202. /*
  203. * v7m_flush_cache_all()
  204. *
  205. * Flush the entire cache system.
  206. * The data cache flush is now achieved using atomic clean / invalidates
  207. * working outwards from L1 cache. This is done using Set/Way based cache
  208. * maintenance instructions.
  209. * The instruction cache can still be invalidated back to the point of
  210. * unification in a single instruction.
  211. *
  212. */
  213. SYM_TYPED_FUNC_START(v7m_flush_kern_cache_all)
  214. stmfd sp!, {r4-r7, r9-r11, lr}
  215. bl v7m_flush_dcache_all
  216. invalidate_icache r0
  217. ldmfd sp!, {r4-r7, r9-r11, lr}
  218. ret lr
  219. SYM_FUNC_END(v7m_flush_kern_cache_all)
  220. /*
  221. * v7m_flush_cache_all()
  222. *
  223. * Flush all TLB entries in a particular address space
  224. *
  225. * - mm - mm_struct describing address space
  226. */
  227. SYM_TYPED_FUNC_START(v7m_flush_user_cache_all)
  228. ret lr
  229. SYM_FUNC_END(v7m_flush_user_cache_all)
  230. /*
  231. * v7m_flush_cache_range(start, end, flags)
  232. *
  233. * Flush a range of TLB entries in the specified address space.
  234. *
  235. * - start - start address (may not be aligned)
  236. * - end - end address (exclusive, may not be aligned)
  237. * - flags - vm_area_struct flags describing address space
  238. *
  239. * It is assumed that:
  240. * - we have a VIPT cache.
  241. */
  242. SYM_TYPED_FUNC_START(v7m_flush_user_cache_range)
  243. ret lr
  244. SYM_FUNC_END(v7m_flush_user_cache_range)
  245. /*
  246. * v7m_coherent_kern_range(start,end)
  247. *
  248. * Ensure that the I and D caches are coherent within specified
  249. * region. This is typically used when code has been written to
  250. * a memory region, and will be executed.
  251. *
  252. * - start - virtual start address of region
  253. * - end - virtual end address of region
  254. *
  255. * It is assumed that:
  256. * - the Icache does not read data from the write buffer
  257. */
  258. SYM_TYPED_FUNC_START(v7m_coherent_kern_range)
  259. #ifdef CONFIG_CFI /* Fallthrough if !CFI */
  260. b v7m_coherent_user_range
  261. #endif
  262. SYM_FUNC_END(v7m_coherent_kern_range)
  263. /*
  264. * v7m_coherent_user_range(start,end)
  265. *
  266. * Ensure that the I and D caches are coherent within specified
  267. * region. This is typically used when code has been written to
  268. * a memory region, and will be executed.
  269. *
  270. * - start - virtual start address of region
  271. * - end - virtual end address of region
  272. *
  273. * It is assumed that:
  274. * - the Icache does not read data from the write buffer
  275. */
  276. SYM_TYPED_FUNC_START(v7m_coherent_user_range)
  277. UNWIND(.fnstart )
  278. dcache_line_size r2, r3
  279. sub r3, r2, #1
  280. bic r12, r0, r3
  281. 1:
  282. /*
  283. * We use open coded version of dccmvau otherwise USER() would
  284. * point at movw instruction.
  285. */
  286. dccmvau r12, r3
  287. add r12, r12, r2
  288. cmp r12, r1
  289. blo 1b
  290. dsb ishst
  291. icache_line_size r2, r3
  292. sub r3, r2, #1
  293. bic r12, r0, r3
  294. 2:
  295. icimvau r12, r3
  296. add r12, r12, r2
  297. cmp r12, r1
  298. blo 2b
  299. invalidate_bp r0
  300. dsb ishst
  301. isb
  302. ret lr
  303. UNWIND(.fnend )
  304. SYM_FUNC_END(v7m_coherent_user_range)
  305. /*
  306. * v7m_flush_kern_dcache_area(void *addr, size_t size)
  307. *
  308. * Ensure that the data held in the page kaddr is written back
  309. * to the page in question.
  310. *
  311. * - addr - kernel address
  312. * - size - region size
  313. */
  314. SYM_TYPED_FUNC_START(v7m_flush_kern_dcache_area)
  315. dcache_line_size r2, r3
  316. add r1, r0, r1
  317. sub r3, r2, #1
  318. bic r0, r0, r3
  319. 1:
  320. dccimvac r0, r3 @ clean & invalidate D line / unified line
  321. add r0, r0, r2
  322. cmp r0, r1
  323. blo 1b
  324. dsb st
  325. ret lr
  326. SYM_FUNC_END(v7m_flush_kern_dcache_area)
  327. /*
  328. * v7m_dma_inv_range(start,end)
  329. *
  330. * Invalidate the data cache within the specified region; we will
  331. * be performing a DMA operation in this region and we want to
  332. * purge old data in the cache.
  333. *
  334. * - start - virtual start address of region
  335. * - end - virtual end address of region
  336. */
  337. v7m_dma_inv_range:
  338. dcache_line_size r2, r3
  339. sub r3, r2, #1
  340. tst r0, r3
  341. bic r0, r0, r3
  342. dccimvacne r0, r3
  343. addne r0, r0, r2
  344. subne r3, r2, #1 @ restore r3, corrupted by v7m's dccimvac
  345. tst r1, r3
  346. bic r1, r1, r3
  347. dccimvacne r1, r3
  348. cmp r0, r1
  349. 1:
  350. dcimvaclo r0, r3
  351. addlo r0, r0, r2
  352. cmplo r0, r1
  353. blo 1b
  354. dsb st
  355. ret lr
  356. ENDPROC(v7m_dma_inv_range)
  357. /*
  358. * v7m_dma_clean_range(start,end)
  359. * - start - virtual start address of region
  360. * - end - virtual end address of region
  361. */
  362. v7m_dma_clean_range:
  363. dcache_line_size r2, r3
  364. sub r3, r2, #1
  365. bic r0, r0, r3
  366. 1:
  367. dccmvac r0, r3 @ clean D / U line
  368. add r0, r0, r2
  369. cmp r0, r1
  370. blo 1b
  371. dsb st
  372. ret lr
  373. ENDPROC(v7m_dma_clean_range)
  374. /*
  375. * v7m_dma_flush_range(start,end)
  376. * - start - virtual start address of region
  377. * - end - virtual end address of region
  378. */
  379. SYM_TYPED_FUNC_START(v7m_dma_flush_range)
  380. dcache_line_size r2, r3
  381. sub r3, r2, #1
  382. bic r0, r0, r3
  383. 1:
  384. dccimvac r0, r3 @ clean & invalidate D / U line
  385. add r0, r0, r2
  386. cmp r0, r1
  387. blo 1b
  388. dsb st
  389. ret lr
  390. SYM_FUNC_END(v7m_dma_flush_range)
  391. /*
  392. * dma_map_area(start, size, dir)
  393. * - start - kernel virtual start address
  394. * - size - size of region
  395. * - dir - DMA direction
  396. */
  397. SYM_TYPED_FUNC_START(v7m_dma_map_area)
  398. add r1, r1, r0
  399. teq r2, #DMA_FROM_DEVICE
  400. beq v7m_dma_inv_range
  401. b v7m_dma_clean_range
  402. SYM_FUNC_END(v7m_dma_map_area)
  403. /*
  404. * dma_unmap_area(start, size, dir)
  405. * - start - kernel virtual start address
  406. * - size - size of region
  407. * - dir - DMA direction
  408. */
  409. SYM_TYPED_FUNC_START(v7m_dma_unmap_area)
  410. add r1, r1, r0
  411. teq r2, #DMA_TO_DEVICE
  412. bne v7m_dma_inv_range
  413. ret lr
  414. SYM_FUNC_END(v7m_dma_unmap_area)