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