This minor refactoring patch changes the signature of all limited cache invalidation functions in coreboot and libpayload from unsigned long to void * for the address argument, since that's really what you have in 95% of the cases and I think it's ugly to have casting boilerplate all over the place. Change-Id: Ic9d3b2ea70b6aa8aea6647adae43ee2183b4e065 Signed-off-by: Julius Werner <jwerner@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/167338 (cherry picked from commit d550bec944736dfa29fcf109e30f17a94af03576) Signed-off-by: Isaac Christensen <isaac.christensen@se-eng.com> Reviewed-on: http://review.coreboot.org/6623 Tested-by: build bot (Jenkins) Reviewed-by: Ronald G. Minnich <rminnich@gmail.com>
		
			
				
	
	
		
			331 lines
		
	
	
		
			7.6 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			331 lines
		
	
	
		
			7.6 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * This file is part of the coreboot project.
 | |
|  *
 | |
|  * Copyright 2013 Google Inc.
 | |
|  *
 | |
|  * 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 author may not be used to endorse or promote products
 | |
|  *    derived from this software without specific prior written permission.
 | |
|  *
 | |
|  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 THE AUTHOR OR CONTRIBUTORS 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.
 | |
|  *
 | |
|  * cache.c: Cache maintenance routines for ARMv7-A and ARMv7-R
 | |
|  *
 | |
|  * Reference: ARM Architecture Reference Manual, ARMv7-A and ARMv7-R edition
 | |
|  */
 | |
| 
 | |
| #include <stdint.h>
 | |
| 
 | |
| #include <arch/cache.h>
 | |
| #include <arch/virtual.h>
 | |
| 
 | |
| #define bitmask(high, low) ((1UL << (high)) + \
 | |
| 			((1UL << (high)) - 1) - ((1UL << (low)) - 1))
 | |
| 
 | |
| /* Basic log2() implementation. Note: log2(0) is 0 for our purposes. */
 | |
| /* FIXME: src/include/lib.h is difficult to work with due to romcc */
 | |
| static unsigned long log2(unsigned long u)
 | |
| {
 | |
| 	int i = 0;
 | |
| 
 | |
| 	while (u >>= 1)
 | |
| 		i++;
 | |
| 
 | |
| 	return i;
 | |
| }
 | |
| 
 | |
| void tlb_invalidate_all(void)
 | |
| {
 | |
| 	/*
 | |
| 	 * FIXME: ARMv7 Architecture Ref. Manual claims that the distinction
 | |
| 	 * instruction vs. data TLBs is deprecated in ARMv7, however this does
 | |
| 	 * not seem to be the case as of Cortex-A15.
 | |
| 	 */
 | |
| 	tlbiall();
 | |
| 	dtlbiall();
 | |
| 	itlbiall();
 | |
| 	isb();
 | |
| 	dsb();
 | |
| }
 | |
| 
 | |
| void icache_invalidate_all(void)
 | |
| {
 | |
| 	/*
 | |
| 	 * icache can be entirely invalidated with one operation.
 | |
| 	 * Note: If branch predictors are architecturally-visible, ICIALLU
 | |
| 	 * also performs a BPIALL operation (B2-1283 in arch manual)
 | |
| 	 */
 | |
| 	iciallu();
 | |
| 	isb();
 | |
| }
 | |
| 
 | |
| enum dcache_op {
 | |
| 	OP_DCCSW,
 | |
| 	OP_DCCISW,
 | |
| 	OP_DCISW,
 | |
| 	OP_DCCIMVAC,
 | |
| 	OP_DCCMVAC,
 | |
| 	OP_DCIMVAC,
 | |
| };
 | |
| 
 | |
| /*
 | |
|  * Do a dcache operation on entire cache by set/way. This is done for
 | |
|  * portability because mapping of memory address to cache location is
 | |
|  * implementation defined (See note on "Requirements for operations by
 | |
|  * set/way" in arch ref. manual).
 | |
|  */
 | |
| static void dcache_op_set_way(enum dcache_op op)
 | |
| {
 | |
| 	uint32_t ccsidr;
 | |
| 	unsigned int associativity, num_sets, linesize_bytes;
 | |
| 	unsigned int set, way;
 | |
| 	unsigned int level;
 | |
| 
 | |
| 	level = (read_csselr() >> 1) & 0x7;
 | |
| 
 | |
| 	/*
 | |
| 	 * dcache must be invalidated by set/way for portability since virtual
 | |
| 	 * memory mapping is system-defined. The number of sets and
 | |
| 	 * associativity is given by CCSIDR. We'll use DCISW to invalidate the
 | |
| 	 * dcache.
 | |
| 	 */
 | |
| 	ccsidr = read_ccsidr();
 | |
| 
 | |
| 	/* FIXME: rounding up required here? */
 | |
| 	num_sets = ((ccsidr & bitmask(27, 13)) >> 13) + 1;
 | |
| 	associativity = ((ccsidr & bitmask(12, 3)) >> 3) + 1;
 | |
| 	/* FIXME: do we need to use CTR.DminLine here? */
 | |
| 	linesize_bytes = (1 << ((ccsidr & 0x7) + 2)) * 4;
 | |
| 
 | |
| 	dsb();
 | |
| 
 | |
| 	/*
 | |
| 	 * Set/way operations require an interesting bit packing. See section
 | |
| 	 * B4-35 in the ARMv7 Architecture Reference Manual:
 | |
| 	 *
 | |
| 	 * A: Log2(associativity)
 | |
| 	 * B: L+S
 | |
| 	 * L: Log2(linesize)
 | |
| 	 * S: Log2(num_sets)
 | |
| 	 *
 | |
| 	 * The bits are packed as follows:
 | |
| 	 *  31  31-A        B B-1    L L-1   4 3   1 0
 | |
| 	 * |---|-------------|--------|-------|-----|-|
 | |
| 	 * |Way|    zeros    |   Set  | zeros |level|0|
 | |
| 	 * |---|-------------|--------|-------|-----|-|
 | |
| 	 */
 | |
| 	for (way = 0; way < associativity; way++) {
 | |
| 		for (set = 0; set < num_sets; set++) {
 | |
| 			uint32_t val = 0;
 | |
| 			val |= way << (32 - log2(associativity));
 | |
| 			val |= set << log2(linesize_bytes);
 | |
| 			val |= level << 1;
 | |
| 			switch(op) {
 | |
| 			case OP_DCCISW:
 | |
| 				dccisw(val);
 | |
| 				break;
 | |
| 			case OP_DCISW:
 | |
| 				dcisw(val);
 | |
| 				break;
 | |
| 			case OP_DCCSW:
 | |
| 				dccsw(val);
 | |
| 				break;
 | |
| 			default:
 | |
| 				break;
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 	isb();
 | |
| }
 | |
| 
 | |
| static void dcache_foreach(enum dcache_op op)
 | |
| {
 | |
| 	uint32_t clidr;
 | |
| 	int level;
 | |
| 
 | |
| 	clidr = read_clidr();
 | |
| 	for (level = 0; level < 7; level++) {
 | |
| 		unsigned int ctype = (clidr >> (level * 3)) & 0x7;
 | |
| 		uint32_t csselr;
 | |
| 
 | |
| 		switch(ctype) {
 | |
| 		case 0x2:
 | |
| 		case 0x3:
 | |
| 		case 0x4:
 | |
| 			csselr = level << 1;
 | |
| 			write_csselr(csselr);
 | |
| 			dcache_op_set_way(op);
 | |
| 			break;
 | |
| 		default:
 | |
| 			/* no cache, icache only, or reserved */
 | |
| 			break;
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void dcache_clean_all(void)
 | |
| {
 | |
| 	dcache_foreach(OP_DCCSW);
 | |
| }
 | |
| 
 | |
| void dcache_clean_invalidate_all(void)
 | |
| {
 | |
| 	dcache_foreach(OP_DCCISW);
 | |
| }
 | |
| 
 | |
| void dcache_invalidate_all(void)
 | |
| {
 | |
| 	dcache_foreach(OP_DCISW);
 | |
| }
 | |
| 
 | |
| static unsigned int line_bytes(void)
 | |
| {
 | |
| 	uint32_t ccsidr;
 | |
| 	unsigned int size;
 | |
| 
 | |
| 	ccsidr = read_ccsidr();
 | |
| 	/* [2:0] - Indicates (Log2(number of words in cache line)) - 2 */
 | |
| 	size = 1 << ((ccsidr & 0x7) + 2);	/* words per line */
 | |
| 	size *= sizeof(unsigned int);		/* bytes per line */
 | |
| 
 | |
| 	return size;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Do a dcache operation by modified virtual address. This is useful for
 | |
|  * maintaining coherency in drivers which do DMA transfers and only need to
 | |
|  * perform cache maintenance on a particular memory range rather than the
 | |
|  * entire cache.
 | |
|  */
 | |
| static void dcache_op_mva(void const *vaddr, size_t len, enum dcache_op op)
 | |
| {
 | |
| 	unsigned long line, linesize;
 | |
| 	unsigned long paddr = virt_to_phys(vaddr);
 | |
| 
 | |
| 	linesize = line_bytes();
 | |
| 	line = paddr & ~(linesize - 1);
 | |
| 
 | |
| 	dsb();
 | |
| 	while (line < paddr + len) {
 | |
| 		switch(op) {
 | |
| 		case OP_DCCIMVAC:
 | |
| 			dccimvac(line);
 | |
| 			break;
 | |
| 		case OP_DCCMVAC:
 | |
| 			dccmvac(line);
 | |
| 			break;
 | |
| 		case OP_DCIMVAC:
 | |
| 			dcimvac(line);
 | |
| 			break;
 | |
| 		default:
 | |
| 			break;
 | |
| 		}
 | |
| 		line += linesize;
 | |
| 	}
 | |
| 	isb();
 | |
| }
 | |
| 
 | |
| void dcache_clean_by_mva(void const *addr, size_t len)
 | |
| {
 | |
| 	dcache_op_mva(addr, len, OP_DCCMVAC);
 | |
| }
 | |
| 
 | |
| void dcache_clean_invalidate_by_mva(void const *addr, size_t len)
 | |
| {
 | |
| 	dcache_op_mva(addr, len, OP_DCCIMVAC);
 | |
| }
 | |
| 
 | |
| void dcache_invalidate_by_mva(void const *addr, size_t len)
 | |
| {
 | |
| 	dcache_op_mva(addr, len, OP_DCIMVAC);
 | |
| }
 | |
| 
 | |
| void dcache_mmu_disable(void)
 | |
| {
 | |
| 	uint32_t sctlr;
 | |
| 
 | |
| 	dcache_clean_invalidate_all();
 | |
| 	sctlr = read_sctlr();
 | |
| 	sctlr &= ~(SCTLR_C | SCTLR_M);
 | |
| 	write_sctlr(sctlr);
 | |
| }
 | |
| 
 | |
| 
 | |
| void dcache_mmu_enable(void)
 | |
| {
 | |
| 	uint32_t sctlr;
 | |
| 
 | |
| 	sctlr = read_sctlr();
 | |
| 	dcache_clean_invalidate_all();
 | |
| 	sctlr |= SCTLR_C | SCTLR_M;
 | |
| 	write_sctlr(sctlr);
 | |
| }
 | |
| 
 | |
| void armv7_invalidate_caches(void)
 | |
| {
 | |
| 	uint32_t clidr;
 | |
| 	int level;
 | |
| 
 | |
| 	/* Invalidate branch predictor */
 | |
| 	bpiall();
 | |
| 
 | |
| 	/* Iterate thru each cache identified in CLIDR and invalidate */
 | |
| 	clidr = read_clidr();
 | |
| 	for (level = 0; level < 7; level++) {
 | |
| 		unsigned int ctype = (clidr >> (level * 3)) & 0x7;
 | |
| 		uint32_t csselr;
 | |
| 
 | |
| 		switch(ctype) {
 | |
| 		case 0x0:
 | |
| 			/* no cache */
 | |
| 			break;
 | |
| 		case 0x1:
 | |
| 			/* icache only */
 | |
| 			csselr = (level << 1) | 1;
 | |
| 			write_csselr(csselr);
 | |
| 			icache_invalidate_all();
 | |
| 			break;
 | |
| 		case 0x2:
 | |
| 		case 0x4:
 | |
| 			/* dcache only or unified cache */
 | |
| 			csselr = level << 1;
 | |
| 			write_csselr(csselr);
 | |
| 			dcache_invalidate_all();
 | |
| 			break;
 | |
| 		case 0x3:
 | |
| 			/* separate icache and dcache */
 | |
| 			csselr = (level << 1) | 1;
 | |
| 			write_csselr(csselr);
 | |
| 			icache_invalidate_all();
 | |
| 
 | |
| 			csselr = level << 1;
 | |
| 			write_csselr(csselr);
 | |
| 			dcache_invalidate_all();
 | |
| 			break;
 | |
| 		default:
 | |
| 			/* reserved */
 | |
| 			break;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	/* Invalidate TLB */
 | |
| 	tlb_invalidate_all();
 | |
| }
 |