summaryrefslogtreecommitdiffstats
path: root/sys/arm/arm/pl310.c
blob: 7c43b311997167260dfe6eeef98d598cf8cb7f24 (plain)
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);

OpenPOWER on IntegriCloud