Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/arch/sh/mm/cache-sh2a.c
26424 views
1
// SPDX-License-Identifier: GPL-2.0-only
2
/*
3
* arch/sh/mm/cache-sh2a.c
4
*
5
* Copyright (C) 2008 Yoshinori Sato
6
*/
7
8
#include <linux/init.h>
9
#include <linux/mm.h>
10
11
#include <asm/cache.h>
12
#include <asm/addrspace.h>
13
#include <asm/processor.h>
14
#include <asm/cacheflush.h>
15
#include <asm/io.h>
16
17
/*
18
* The maximum number of pages we support up to when doing ranged dcache
19
* flushing. Anything exceeding this will simply flush the dcache in its
20
* entirety.
21
*/
22
#define MAX_OCACHE_PAGES 32
23
#define MAX_ICACHE_PAGES 32
24
25
#ifdef CONFIG_CACHE_WRITEBACK
26
static void sh2a_flush_oc_line(unsigned long v, int way)
27
{
28
unsigned long addr = (v & 0x000007f0) | (way << 11);
29
unsigned long data;
30
31
data = __raw_readl(CACHE_OC_ADDRESS_ARRAY | addr);
32
if ((data & CACHE_PHYSADDR_MASK) == (v & CACHE_PHYSADDR_MASK)) {
33
data &= ~SH_CACHE_UPDATED;
34
__raw_writel(data, CACHE_OC_ADDRESS_ARRAY | addr);
35
}
36
}
37
#endif
38
39
static void sh2a_invalidate_line(unsigned long cache_addr, unsigned long v)
40
{
41
/* Set associative bit to hit all ways */
42
unsigned long addr = (v & 0x000007f0) | SH_CACHE_ASSOC;
43
__raw_writel((addr & CACHE_PHYSADDR_MASK), cache_addr | addr);
44
}
45
46
/*
47
* Write back the dirty D-caches, but not invalidate them.
48
*/
49
static void sh2a__flush_wback_region(void *start, int size)
50
{
51
#ifdef CONFIG_CACHE_WRITEBACK
52
unsigned long v;
53
unsigned long begin, end;
54
unsigned long flags;
55
int nr_ways;
56
57
begin = (unsigned long)start & ~(L1_CACHE_BYTES-1);
58
end = ((unsigned long)start + size + L1_CACHE_BYTES-1)
59
& ~(L1_CACHE_BYTES-1);
60
nr_ways = current_cpu_data.dcache.ways;
61
62
local_irq_save(flags);
63
jump_to_uncached();
64
65
/* If there are too many pages then flush the entire cache */
66
if (((end - begin) >> PAGE_SHIFT) >= MAX_OCACHE_PAGES) {
67
begin = CACHE_OC_ADDRESS_ARRAY;
68
end = begin + (nr_ways * current_cpu_data.dcache.way_size);
69
70
for (v = begin; v < end; v += L1_CACHE_BYTES) {
71
unsigned long data = __raw_readl(v);
72
if (data & SH_CACHE_UPDATED)
73
__raw_writel(data & ~SH_CACHE_UPDATED, v);
74
}
75
} else {
76
int way;
77
for (way = 0; way < nr_ways; way++) {
78
for (v = begin; v < end; v += L1_CACHE_BYTES)
79
sh2a_flush_oc_line(v, way);
80
}
81
}
82
83
back_to_cached();
84
local_irq_restore(flags);
85
#endif
86
}
87
88
/*
89
* Write back the dirty D-caches and invalidate them.
90
*/
91
static void sh2a__flush_purge_region(void *start, int size)
92
{
93
unsigned long v;
94
unsigned long begin, end;
95
unsigned long flags;
96
97
begin = (unsigned long)start & ~(L1_CACHE_BYTES-1);
98
end = ((unsigned long)start + size + L1_CACHE_BYTES-1)
99
& ~(L1_CACHE_BYTES-1);
100
101
local_irq_save(flags);
102
jump_to_uncached();
103
104
for (v = begin; v < end; v+=L1_CACHE_BYTES) {
105
#ifdef CONFIG_CACHE_WRITEBACK
106
int way;
107
int nr_ways = current_cpu_data.dcache.ways;
108
for (way = 0; way < nr_ways; way++)
109
sh2a_flush_oc_line(v, way);
110
#endif
111
sh2a_invalidate_line(CACHE_OC_ADDRESS_ARRAY, v);
112
}
113
114
back_to_cached();
115
local_irq_restore(flags);
116
}
117
118
/*
119
* Invalidate the D-caches, but no write back please
120
*/
121
static void sh2a__flush_invalidate_region(void *start, int size)
122
{
123
unsigned long v;
124
unsigned long begin, end;
125
unsigned long flags;
126
127
begin = (unsigned long)start & ~(L1_CACHE_BYTES-1);
128
end = ((unsigned long)start + size + L1_CACHE_BYTES-1)
129
& ~(L1_CACHE_BYTES-1);
130
131
local_irq_save(flags);
132
jump_to_uncached();
133
134
/* If there are too many pages then just blow the cache */
135
if (((end - begin) >> PAGE_SHIFT) >= MAX_OCACHE_PAGES) {
136
__raw_writel(__raw_readl(SH_CCR) | CCR_OCACHE_INVALIDATE,
137
SH_CCR);
138
} else {
139
for (v = begin; v < end; v += L1_CACHE_BYTES)
140
sh2a_invalidate_line(CACHE_OC_ADDRESS_ARRAY, v);
141
}
142
143
back_to_cached();
144
local_irq_restore(flags);
145
}
146
147
/*
148
* Write back the range of D-cache, and purge the I-cache.
149
*/
150
static void sh2a_flush_icache_range(void *args)
151
{
152
struct flusher_data *data = args;
153
unsigned long start, end;
154
unsigned long v;
155
unsigned long flags;
156
157
start = data->addr1 & ~(L1_CACHE_BYTES-1);
158
end = (data->addr2 + L1_CACHE_BYTES-1) & ~(L1_CACHE_BYTES-1);
159
160
#ifdef CONFIG_CACHE_WRITEBACK
161
sh2a__flush_wback_region((void *)start, end-start);
162
#endif
163
164
local_irq_save(flags);
165
jump_to_uncached();
166
167
/* I-Cache invalidate */
168
/* If there are too many pages then just blow the cache */
169
if (((end - start) >> PAGE_SHIFT) >= MAX_ICACHE_PAGES) {
170
__raw_writel(__raw_readl(SH_CCR) | CCR_ICACHE_INVALIDATE,
171
SH_CCR);
172
} else {
173
for (v = start; v < end; v += L1_CACHE_BYTES)
174
sh2a_invalidate_line(CACHE_IC_ADDRESS_ARRAY, v);
175
}
176
177
back_to_cached();
178
local_irq_restore(flags);
179
}
180
181
void __init sh2a_cache_init(void)
182
{
183
local_flush_icache_range = sh2a_flush_icache_range;
184
185
__flush_wback_region = sh2a__flush_wback_region;
186
__flush_purge_region = sh2a__flush_purge_region;
187
__flush_invalidate_region = sh2a__flush_invalidate_region;
188
}
189
190