1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * arch/sh/mm/cache-sh3.c |
4 | * |
5 | * Copyright (C) 1999, 2000 Niibe Yutaka |
6 | * Copyright (C) 2002 Paul Mundt |
7 | */ |
8 | |
9 | #include <linux/init.h> |
10 | #include <linux/mman.h> |
11 | #include <linux/mm.h> |
12 | #include <linux/threads.h> |
13 | #include <asm/addrspace.h> |
14 | #include <asm/page.h> |
15 | #include <asm/processor.h> |
16 | #include <asm/cache.h> |
17 | #include <asm/io.h> |
18 | #include <linux/uaccess.h> |
19 | #include <asm/mmu_context.h> |
20 | #include <asm/cacheflush.h> |
21 | |
22 | /* |
23 | * Write back the dirty D-caches, but not invalidate them. |
24 | * |
25 | * Is this really worth it, or should we just alias this routine |
26 | * to __flush_purge_region too? |
27 | * |
28 | * START: Virtual Address (U0, P1, or P3) |
29 | * SIZE: Size of the region. |
30 | */ |
31 | |
32 | static void sh3__flush_wback_region(void *start, int size) |
33 | { |
34 | unsigned long v, j; |
35 | unsigned long begin, end; |
36 | unsigned long flags; |
37 | |
38 | begin = (unsigned long)start & ~(L1_CACHE_BYTES-1); |
39 | end = ((unsigned long)start + size + L1_CACHE_BYTES-1) |
40 | & ~(L1_CACHE_BYTES-1); |
41 | |
42 | for (v = begin; v < end; v+=L1_CACHE_BYTES) { |
43 | unsigned long addrstart = CACHE_OC_ADDRESS_ARRAY; |
44 | for (j = 0; j < current_cpu_data.dcache.ways; j++) { |
45 | unsigned long data, addr, p; |
46 | |
47 | p = __pa(v); |
48 | addr = addrstart | (v & current_cpu_data.dcache.entry_mask); |
49 | local_irq_save(flags); |
50 | data = __raw_readl(addr); |
51 | |
52 | if ((data & CACHE_PHYSADDR_MASK) == |
53 | (p & CACHE_PHYSADDR_MASK)) { |
54 | data &= ~SH_CACHE_UPDATED; |
55 | __raw_writel(val: data, addr); |
56 | local_irq_restore(flags); |
57 | break; |
58 | } |
59 | local_irq_restore(flags); |
60 | addrstart += current_cpu_data.dcache.way_incr; |
61 | } |
62 | } |
63 | } |
64 | |
65 | /* |
66 | * Write back the dirty D-caches and invalidate them. |
67 | * |
68 | * START: Virtual Address (U0, P1, or P3) |
69 | * SIZE: Size of the region. |
70 | */ |
71 | static void sh3__flush_purge_region(void *start, int size) |
72 | { |
73 | unsigned long v; |
74 | unsigned long begin, end; |
75 | |
76 | begin = (unsigned long)start & ~(L1_CACHE_BYTES-1); |
77 | end = ((unsigned long)start + size + L1_CACHE_BYTES-1) |
78 | & ~(L1_CACHE_BYTES-1); |
79 | |
80 | for (v = begin; v < end; v+=L1_CACHE_BYTES) { |
81 | unsigned long data, addr; |
82 | |
83 | data = (v & 0xfffffc00); /* _Virtual_ address, ~U, ~V */ |
84 | addr = CACHE_OC_ADDRESS_ARRAY | |
85 | (v & current_cpu_data.dcache.entry_mask) | SH_CACHE_ASSOC; |
86 | __raw_writel(val: data, addr); |
87 | } |
88 | } |
89 | |
90 | void __init sh3_cache_init(void) |
91 | { |
92 | __flush_wback_region = sh3__flush_wback_region; |
93 | __flush_purge_region = sh3__flush_purge_region; |
94 | |
95 | /* |
96 | * No write back please |
97 | * |
98 | * Except I don't think there's any way to avoid the writeback. |
99 | * So we just alias it to sh3__flush_purge_region(). dwmw2. |
100 | */ |
101 | __flush_invalidate_region = sh3__flush_purge_region; |
102 | } |
103 | |