Loading...
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 | /* cache.c - d-cache support for AARCH64 CPUs */
/*
* Copyright 2020-2021 NXP
*
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @file
* @brief d-cache manipulation
*
* This module contains functions for manipulation of the d-cache.
*/
#include <cache.h>
#define CTR_EL0_DMINLINE_SHIFT 16
#define CTR_EL0_DMINLINE_MASK BIT_MASK(4)
#define CTR_EL0_CWG_SHIFT 24
#define CTR_EL0_CWG_MASK BIT_MASK(4)
/* clidr_el1 */
#define CLIDR_EL1_LOC_SHIFT 24
#define CLIDR_EL1_LOC_MASK BIT_MASK(3)
#define CLIDR_EL1_CTYPE_SHIFT(level) ((level) * 3)
#define CLIDR_EL1_CTYPE_MASK BIT_MASK(3)
/* ccsidr_el1 */
#define CCSIDR_EL1_LN_SZ_SHIFT 0
#define CCSIDR_EL1_LN_SZ_MASK BIT_MASK(3)
#define CCSIDR_EL1_WAYS_SHIFT 3
#define CCSIDR_EL1_WAYS_MASK BIT_MASK(10)
#define CCSIDR_EL1_SETS_SHIFT 13
#define CCSIDR_EL1_SETS_MASK BIT_MASK(15)
#define dc_ops(op, val) \
({ \
__asm__ volatile ("dc " op ", %0" :: "r" (val) : "memory"); \
})
static size_t dcache_line_size;
size_t arch_dcache_line_size_get(void)
{
uint64_t ctr_el0;
uint32_t dminline;
if (dcache_line_size)
return dcache_line_size;
ctr_el0 = read_sysreg(CTR_EL0);
dminline = (ctr_el0 >> CTR_EL0_DMINLINE_SHIFT) & CTR_EL0_DMINLINE_MASK;
dcache_line_size = 4 << dminline;
return dcache_line_size;
}
/*
* operation for data cache by virtual address to PoC
* ops: K_CACHE_INVD: invalidate
* K_CACHE_WB: clean
* K_CACHE_WB_INVD: clean and invalidate
*/
int arch_dcache_range(void *addr, size_t size, int op)
{
size_t line_size;
uintptr_t start_addr = (uintptr_t)addr;
uintptr_t end_addr = start_addr + size;
if (op != K_CACHE_INVD && op != K_CACHE_WB && op != K_CACHE_WB_INVD)
return -ENOTSUP;
line_size = arch_dcache_line_size_get();
/* Align address to line size */
start_addr &= ~(line_size - 1);
do {
if (op == K_CACHE_INVD) {
dc_ops("ivac", start_addr);
} else if (op == K_CACHE_WB) {
dc_ops("cvac", start_addr);
} else if (op == K_CACHE_WB_INVD) {
dc_ops("civac", start_addr);
}
start_addr += line_size;
} while (start_addr < end_addr);
dsb();
return 0;
}
/*
* operation for all data cache
* ops: K_CACHE_INVD: invalidate
* K_CACHE_WB: clean
* K_CACHE_WB_INVD: clean and invalidate
*/
int arch_dcache_all(int op)
{
uint32_t clidr_el1, csselr_el1, ccsidr_el1;
uint8_t loc, ctype, cache_level, line_size, way_pos;
uint32_t max_ways, max_sets, dc_val, set, way;
if (op != K_CACHE_INVD && op != K_CACHE_WB && op != K_CACHE_WB_INVD)
return -ENOTSUP;
/* Data barrier before start */
dsb();
clidr_el1 = read_clidr_el1();
loc = (clidr_el1 >> CLIDR_EL1_LOC_SHIFT) & CLIDR_EL1_LOC_MASK;
if (!loc)
return 0;
for (cache_level = 0; cache_level < loc; cache_level++) {
ctype = (clidr_el1 >> CLIDR_EL1_CTYPE_SHIFT(cache_level))
& CLIDR_EL1_CTYPE_MASK;
/* No data cache, continue */
if (ctype < 2)
continue;
/* select cache level */
csselr_el1 = cache_level << 1;
write_csselr_el1(csselr_el1);
isb();
ccsidr_el1 = read_ccsidr_el1();
line_size = (ccsidr_el1 >> CCSIDR_EL1_LN_SZ_SHIFT
& CCSIDR_EL1_LN_SZ_MASK) + 4;
max_ways = (ccsidr_el1 >> CCSIDR_EL1_WAYS_SHIFT)
& CCSIDR_EL1_WAYS_MASK;
max_sets = (ccsidr_el1 >> CCSIDR_EL1_SETS_SHIFT)
& CCSIDR_EL1_SETS_MASK;
/* 32-log2(ways), bit position of way in DC operand */
way_pos = __builtin_clz(max_ways);
for (set = 0; set <= max_sets; set++) {
for (way = 0; way <= max_ways; way++) {
/* way number, aligned to pos in DC operand */
dc_val = way << way_pos;
/* cache level, aligned to pos in DC operand */
dc_val |= csselr_el1;
/* set number, aligned to pos in DC operand */
dc_val |= set << line_size;
if (op == K_CACHE_INVD) {
dc_ops("isw", dc_val);
} else if (op == K_CACHE_WB_INVD) {
dc_ops("cisw", dc_val);
} else if (op == K_CACHE_WB) {
dc_ops("csw", dc_val);
}
}
}
}
/* Restore csselr_el1 to level 0 */
write_csselr_el1(0);
dsb();
isb();
return 0;
}
|