1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
|
/*-
* Copyright (c) 2012 Olivier Houchard <cognet@FreeBSD.org>
* Copyright (c) 2011
* Ben Gray <ben.r.gray@gmail.com>.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the company nor the name of the author may be used to
* endorse or promote products derived from this software without specific
* prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY BEN GRAY ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL BEN GRAY BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/bus.h>
#include <sys/kernel.h>
#include <sys/rman.h>
#include <sys/module.h>
#include <sys/lock.h>
#include <sys/mutex.h>
#include <machine/intr.h>
#include <vm/vm.h>
#include <vm/pmap.h>
#include <machine/pl310.h>
#include <machine/bus.h>
#include <dev/fdt/fdt_common.h>
#include <dev/ofw/openfirm.h>
#include <dev/ofw/ofw_bus.h>
#include <dev/ofw/ofw_bus_subr.h>
/**
* PL310 - L2 Cache Controller register offsets.
*
*/
#define PL310_CACHE_ID 0x000
#define PL310_CACHE_TYPE 0x004
#define PL310_CTRL 0x100
#define PL310_AUX_CTRL 0x104
#define PL310_EVENT_COUNTER_CTRL 0x200
#define PL310_EVENT_COUNTER1_CONF 0x204
#define PL310_EVENT_COUNTER0_CONF 0x208
#define PL310_EVENT_COUNTER1_VAL 0x20C
#define PL310_EVENT_COUNTER0_VAL 0x210
#define PL310_INTR_MASK 0x214
#define PL310_MASKED_INTR_STAT 0x218
#define PL310_RAW_INTR_STAT 0x21C
#define PL310_INTR_CLEAR 0x220
#define PL310_CACHE_SYNC 0x730
#define PL310_INV_LINE_PA 0x770
#define PL310_INV_WAY 0x77C
#define PL310_CLEAN_LINE_PA 0x7B0
#define PL310_CLEAN_LINE_IDX 0x7B8
#define PL310_CLEAN_WAY 0x7BC
#define PL310_CLEAN_INV_LINE_PA 0x7F0
#define PL310_CLEAN_INV_LINE_IDX 0x7F8
#define PL310_CLEAN_INV_WAY 0x7FC
#define PL310_LOCKDOWN_D_WAY(x) (0x900 + ((x) * 8))
#define PL310_LOCKDOWN_I_WAY(x) (0x904 + ((x) * 8))
#define PL310_LOCKDOWN_LINE_ENABLE 0x950
#define PL310_UNLOCK_ALL_LINES_WAY 0x954
#define PL310_ADDR_FILTER_START 0xC00
#define PL310_ADDR_FILTER_END 0xC04
#define PL310_DEBUG_CTRL 0xF40
#define PL310_AUX_CTRL_MASK 0xc0000fff
#define PL310_AUX_CTRL_ASSOCIATIVITY_SHIFT 16
#define PL310_AUX_CTRL_WAY_SIZE_SHIFT 17
#define PL310_AUX_CTRL_WAY_SIZE_MASK (0x7 << 17)
#define PL310_AUX_CTRL_SHARE_OVERRIDE_SHIFT 22
#define PL310_AUX_CTRL_NS_LOCKDOWN_SHIFT 26
#define PL310_AUX_CTRL_NS_INT_CTRL_SHIFT 27
#define PL310_AUX_CTRL_DATA_PREFETCH_SHIFT 28
#define PL310_AUX_CTRL_INSTR_PREFETCH_SHIFT 29
#define PL310_AUX_CTRL_EARLY_BRESP_SHIFT 30
void omap4_l2cache_wbinv_range(vm_paddr_t physaddr, vm_size_t size);
void omap4_l2cache_inv_range(vm_paddr_t physaddr, vm_size_t size);
void omap4_l2cache_wb_range(vm_paddr_t physaddr, vm_size_t size);
void omap4_l2cache_wbinv_all(void);
void omap4_l2cache_inv_all(void);
void omap4_l2cache_wb_all(void);
static uint32_t g_l2cache_way_mask;
static const uint32_t g_l2cache_line_size = 32;
static const uint32_t g_l2cache_align_mask = (32 - 1);
static uint32_t g_l2cache_size;
static struct pl310_softc *pl310_softc;
/**
* pl310_read4 - read a 32-bit value from the PL310 registers
* pl310_write4 - write a 32-bit value from the PL310 registers
* @off: byte offset within the register set to read from
* @val: the value to write into the register
*
*
* LOCKING:
* None
*
* RETURNS:
* nothing in case of write function, if read function returns the value read.
*/
static __inline uint32_t
pl310_read4(bus_size_t off)
{
return bus_read_4(pl310_softc->sc_mem_res, off);
}
static __inline void
pl310_write4(bus_size_t off, uint32_t val)
{
bus_write_4(pl310_softc->sc_mem_res, off, val);
}
static __inline void
pl310_wait_background_op(uint32_t off, uint32_t mask)
{
while (pl310_read4(off) & mask);
}
/**
* pl310_cache_sync - performs a cache sync operation
*
* According to the TRM:
*
* "Before writing to any other register you must perform an explicit
* Cache Sync operation. This is particularly important when the cache is
* enabled and changes to how the cache allocates new lines are to be made."
*
*
*/
static __inline void
pl310_cache_sync(void)
{
pl310_write4(PL310_CACHE_SYNC, 0);
}
static void
pl310_wbinv_all(void)
{
#if 1
pl310_write4(PL310_DEBUG_CTRL, 3);
#endif
pl310_write4(PL310_CLEAN_INV_WAY, g_l2cache_way_mask);
pl310_wait_background_op(PL310_CLEAN_INV_WAY, g_l2cache_way_mask);
pl310_cache_sync();
#if 1
pl310_write4(PL310_DEBUG_CTRL, 0);
#endif
}
static void
pl310_wbinv_range(vm_paddr_t start, vm_size_t size)
{
if (start & g_l2cache_align_mask) {
size += start & g_l2cache_align_mask;
start &= ~g_l2cache_align_mask;
}
if (size & g_l2cache_align_mask) {
size &= ~g_l2cache_align_mask;
size += g_l2cache_line_size;
}
#if 1
pl310_write4(PL310_DEBUG_CTRL, 3);
#endif
while (size > 0) {
#if 1
/*
* Errata 588369 says that clean + inv may keep the
* cache line if it was clean, the recommanded workaround
* is to clean then invalidate the cache line, with
* write-back and cache linefill disabled
*/
pl310_write4(PL310_CLEAN_LINE_PA, start);
pl310_write4(PL310_INV_LINE_PA, start);
#else
pl310_write4(PL310_CLEAN_INV_LINE_PA, start);
#endif
start += g_l2cache_line_size;
size -= g_l2cache_line_size;
}
#if 1
pl310_write4(PL310_DEBUG_CTRL, 0);
#endif
pl310_wait_background_op(PL310_CLEAN_INV_LINE_PA, 1);
pl310_cache_sync();
}
static void
pl310_wb_range(vm_paddr_t start, vm_size_t size)
{
if (start & g_l2cache_align_mask) {
size += start & g_l2cache_align_mask;
start &= ~g_l2cache_align_mask;
}
if (size & g_l2cache_align_mask) {
size &= ~g_l2cache_align_mask;
size += g_l2cache_line_size;
}
while (size > 0) {
pl310_write4(PL310_CLEAN_LINE_PA, start);
start += g_l2cache_line_size;
size -= g_l2cache_line_size;
}
pl310_cache_sync();
pl310_wait_background_op(PL310_CLEAN_LINE_PA, 1);
}
static void
pl310_inv_range(vm_paddr_t start, vm_size_t size)
{
if (start & g_l2cache_align_mask) {
size += start & g_l2cache_align_mask;
start &= ~g_l2cache_align_mask;
}
if (size & g_l2cache_align_mask) {
size &= ~g_l2cache_align_mask;
size += g_l2cache_line_size;
}
while (size > 0) {
pl310_write4(PL310_INV_LINE_PA, start);
start += g_l2cache_line_size;
size -= g_l2cache_line_size;
}
pl310_cache_sync();
pl310_wait_background_op(PL310_INV_LINE_PA, 1);
}
static int
pl310_probe(device_t dev)
{
if (!ofw_bus_is_compatible(dev, "arm,pl310"))
return (ENXIO);
device_set_desc(dev, "PL310 L2 cache controller");
return (0);
}
static int
pl310_attach(device_t dev)
{
struct pl310_softc *sc = device_get_softc(dev);
int rid = 0;
uint32_t aux_value;
uint32_t way_size;
uint32_t ways_assoc;
uint32_t ctrl_value;
sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
RF_ACTIVE);
if (sc->sc_mem_res == NULL)
panic("%s: Cannot map registers", device_get_name(dev));
pl310_softc = sc;
platform_init_pl310(sc);
aux_value = pl310_read4(PL310_AUX_CTRL);
way_size = (aux_value & PL310_AUX_CTRL_WAY_SIZE_MASK) >>
PL310_AUX_CTRL_WAY_SIZE_SHIFT;
way_size = 1 << (way_size + 13);
if (aux_value & (1 << PL310_AUX_CTRL_ASSOCIATIVITY_SHIFT))
ways_assoc = 16;
else
ways_assoc = 8;
g_l2cache_way_mask = (1 << ways_assoc) - 1;
g_l2cache_size = way_size * ways_assoc;
/* Print the information */
printf(" L2 Cache: %uKB/%dB %d ways\n", (g_l2cache_size / 1024),
g_l2cache_line_size, ways_assoc);
ctrl_value = pl310_read4(PL310_CTRL);
if (!(ctrl_value & 0x1)) {
/* Enable the L2 cache if disabled */
pl310_write4(PL310_CTRL, ctrl_value & 0x1);
}
pl310_wbinv_all();
/* Set the l2 functions in the set of cpufuncs */
cpufuncs.cf_l2cache_wbinv_all = pl310_wbinv_all;
cpufuncs.cf_l2cache_wbinv_range = pl310_wbinv_range;
cpufuncs.cf_l2cache_inv_range = pl310_inv_range;
cpufuncs.cf_l2cache_wb_range = pl310_wb_range;
return (0);
}
static device_method_t pl310_methods[] = {
DEVMETHOD(device_probe, pl310_probe),
DEVMETHOD(device_attach, pl310_attach),
{0, 0},
};
static driver_t pl310_driver = {
"l2cache",
pl310_methods,
sizeof(struct pl310_softc),
};
static devclass_t pl310_devclass;
DRIVER_MODULE(pl310, simplebus, pl310_driver, pl310_devclass, 0, 0);
|