1a9ff9447SDmitry Baryshkov // SPDX-License-Identifier: GPL-2.0-only 2a9ff9447SDmitry Baryshkov /* 3a9ff9447SDmitry Baryshkov * ARM cacheinfo support 4a9ff9447SDmitry Baryshkov * 5a9ff9447SDmitry Baryshkov * Copyright (C) 2023 Linaro Ltd. 6a9ff9447SDmitry Baryshkov * Copyright (C) 2015 ARM Ltd. 7a9ff9447SDmitry Baryshkov * All Rights Reserved 8a9ff9447SDmitry Baryshkov */ 9a9ff9447SDmitry Baryshkov 10a9ff9447SDmitry Baryshkov #include <linux/bitfield.h> 11a9ff9447SDmitry Baryshkov #include <linux/cacheinfo.h> 12a9ff9447SDmitry Baryshkov #include <linux/of.h> 13a9ff9447SDmitry Baryshkov 14a9ff9447SDmitry Baryshkov #include <asm/cachetype.h> 15a9ff9447SDmitry Baryshkov #include <asm/cputype.h> 16a9ff9447SDmitry Baryshkov #include <asm/system_info.h> 17a9ff9447SDmitry Baryshkov 18a9ff9447SDmitry Baryshkov /* Ctypen, bits[3(n - 1) + 2 : 3(n - 1)], for n = 1 to 7 */ 19a9ff9447SDmitry Baryshkov #define CLIDR_CTYPE_SHIFT(level) (3 * (level - 1)) 20a9ff9447SDmitry Baryshkov #define CLIDR_CTYPE_MASK(level) (7 << CLIDR_CTYPE_SHIFT(level)) 21a9ff9447SDmitry Baryshkov #define CLIDR_CTYPE(clidr, level) \ 22a9ff9447SDmitry Baryshkov (((clidr) & CLIDR_CTYPE_MASK(level)) >> CLIDR_CTYPE_SHIFT(level)) 23a9ff9447SDmitry Baryshkov 24a9ff9447SDmitry Baryshkov #define MAX_CACHE_LEVEL 7 /* Max 7 level supported */ 25a9ff9447SDmitry Baryshkov 26*f520fab5SDmitry Baryshkov #define CTR_FORMAT_MASK GENMASK(31, 29) 27a9ff9447SDmitry Baryshkov #define CTR_FORMAT_ARMV6 0 28a9ff9447SDmitry Baryshkov #define CTR_FORMAT_ARMV7 4 29a9ff9447SDmitry Baryshkov #define CTR_CWG_MASK GENMASK(27, 24) 30a9ff9447SDmitry Baryshkov #define CTR_DSIZE_LEN_MASK GENMASK(13, 12) 31a9ff9447SDmitry Baryshkov #define CTR_ISIZE_LEN_MASK GENMASK(1, 0) 32a9ff9447SDmitry Baryshkov 33a9ff9447SDmitry Baryshkov /* Also valid for v7m */ 34a9ff9447SDmitry Baryshkov static inline int cache_line_size_cp15(void) 35a9ff9447SDmitry Baryshkov { 36a9ff9447SDmitry Baryshkov u32 ctr = read_cpuid_cachetype(); 37a9ff9447SDmitry Baryshkov u32 format = FIELD_GET(CTR_FORMAT_MASK, ctr); 38a9ff9447SDmitry Baryshkov 39a9ff9447SDmitry Baryshkov if (format == CTR_FORMAT_ARMV7) { 40a9ff9447SDmitry Baryshkov u32 cwg = FIELD_GET(CTR_CWG_MASK, ctr); 41a9ff9447SDmitry Baryshkov 42a9ff9447SDmitry Baryshkov return cwg ? 4 << cwg : ARCH_DMA_MINALIGN; 43a9ff9447SDmitry Baryshkov } else if (WARN_ON_ONCE(format != CTR_FORMAT_ARMV6)) { 44a9ff9447SDmitry Baryshkov return ARCH_DMA_MINALIGN; 45a9ff9447SDmitry Baryshkov } 46a9ff9447SDmitry Baryshkov 47a9ff9447SDmitry Baryshkov return 8 << max(FIELD_GET(CTR_ISIZE_LEN_MASK, ctr), 48a9ff9447SDmitry Baryshkov FIELD_GET(CTR_DSIZE_LEN_MASK, ctr)); 49a9ff9447SDmitry Baryshkov } 50a9ff9447SDmitry Baryshkov 51a9ff9447SDmitry Baryshkov int cache_line_size(void) 52a9ff9447SDmitry Baryshkov { 53a9ff9447SDmitry Baryshkov if (coherency_max_size != 0) 54a9ff9447SDmitry Baryshkov return coherency_max_size; 55a9ff9447SDmitry Baryshkov 56a9ff9447SDmitry Baryshkov /* CP15 is optional / implementation defined before ARMv6 */ 57a9ff9447SDmitry Baryshkov if (cpu_architecture() < CPU_ARCH_ARMv6) 58a9ff9447SDmitry Baryshkov return ARCH_DMA_MINALIGN; 59a9ff9447SDmitry Baryshkov 60a9ff9447SDmitry Baryshkov return cache_line_size_cp15(); 61a9ff9447SDmitry Baryshkov } 62a9ff9447SDmitry Baryshkov EXPORT_SYMBOL_GPL(cache_line_size); 63a9ff9447SDmitry Baryshkov 64a9ff9447SDmitry Baryshkov static inline enum cache_type get_cache_type(int level) 65a9ff9447SDmitry Baryshkov { 66a9ff9447SDmitry Baryshkov u32 clidr; 67a9ff9447SDmitry Baryshkov 68a9ff9447SDmitry Baryshkov if (level > MAX_CACHE_LEVEL) 69a9ff9447SDmitry Baryshkov return CACHE_TYPE_NOCACHE; 70a9ff9447SDmitry Baryshkov 71a9ff9447SDmitry Baryshkov clidr = read_clidr(); 72a9ff9447SDmitry Baryshkov 73a9ff9447SDmitry Baryshkov return CLIDR_CTYPE(clidr, level); 74a9ff9447SDmitry Baryshkov } 75a9ff9447SDmitry Baryshkov 76a9ff9447SDmitry Baryshkov static void ci_leaf_init(struct cacheinfo *this_leaf, 77a9ff9447SDmitry Baryshkov enum cache_type type, unsigned int level) 78a9ff9447SDmitry Baryshkov { 79a9ff9447SDmitry Baryshkov this_leaf->level = level; 80a9ff9447SDmitry Baryshkov this_leaf->type = type; 81a9ff9447SDmitry Baryshkov } 82a9ff9447SDmitry Baryshkov 83a9ff9447SDmitry Baryshkov static int detect_cache_level(unsigned int *level_p, unsigned int *leaves_p) 84a9ff9447SDmitry Baryshkov { 85a9ff9447SDmitry Baryshkov unsigned int ctype, level, leaves; 86a9ff9447SDmitry Baryshkov u32 ctr, format; 87a9ff9447SDmitry Baryshkov 88a9ff9447SDmitry Baryshkov /* CLIDR is not present before ARMv7/v7m */ 89a9ff9447SDmitry Baryshkov if (cpu_architecture() < CPU_ARCH_ARMv7) 90a9ff9447SDmitry Baryshkov return -EOPNOTSUPP; 91a9ff9447SDmitry Baryshkov 92a9ff9447SDmitry Baryshkov /* Don't try reading CLIDR if CTR declares old format */ 93a9ff9447SDmitry Baryshkov ctr = read_cpuid_cachetype(); 94a9ff9447SDmitry Baryshkov format = FIELD_GET(CTR_FORMAT_MASK, ctr); 95a9ff9447SDmitry Baryshkov if (format != CTR_FORMAT_ARMV7) 96a9ff9447SDmitry Baryshkov return -EOPNOTSUPP; 97a9ff9447SDmitry Baryshkov 98a9ff9447SDmitry Baryshkov for (level = 1, leaves = 0; level <= MAX_CACHE_LEVEL; level++) { 99a9ff9447SDmitry Baryshkov ctype = get_cache_type(level); 100a9ff9447SDmitry Baryshkov if (ctype == CACHE_TYPE_NOCACHE) { 101a9ff9447SDmitry Baryshkov level--; 102a9ff9447SDmitry Baryshkov break; 103a9ff9447SDmitry Baryshkov } 104a9ff9447SDmitry Baryshkov /* Separate instruction and data caches */ 105a9ff9447SDmitry Baryshkov leaves += (ctype == CACHE_TYPE_SEPARATE) ? 2 : 1; 106a9ff9447SDmitry Baryshkov } 107a9ff9447SDmitry Baryshkov 108a9ff9447SDmitry Baryshkov *level_p = level; 109a9ff9447SDmitry Baryshkov *leaves_p = leaves; 110a9ff9447SDmitry Baryshkov 111a9ff9447SDmitry Baryshkov return 0; 112a9ff9447SDmitry Baryshkov } 113a9ff9447SDmitry Baryshkov 114a9ff9447SDmitry Baryshkov int early_cache_level(unsigned int cpu) 115a9ff9447SDmitry Baryshkov { 116a9ff9447SDmitry Baryshkov struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu); 117a9ff9447SDmitry Baryshkov 118a9ff9447SDmitry Baryshkov return detect_cache_level(&this_cpu_ci->num_levels, &this_cpu_ci->num_leaves); 119a9ff9447SDmitry Baryshkov } 120a9ff9447SDmitry Baryshkov 121a9ff9447SDmitry Baryshkov int init_cache_level(unsigned int cpu) 122a9ff9447SDmitry Baryshkov { 123a9ff9447SDmitry Baryshkov unsigned int level, leaves; 124a9ff9447SDmitry Baryshkov struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu); 125a9ff9447SDmitry Baryshkov int fw_level; 126a9ff9447SDmitry Baryshkov int ret; 127a9ff9447SDmitry Baryshkov 128a9ff9447SDmitry Baryshkov ret = detect_cache_level(&level, &leaves); 129a9ff9447SDmitry Baryshkov if (ret) 130a9ff9447SDmitry Baryshkov return ret; 131a9ff9447SDmitry Baryshkov 132a9ff9447SDmitry Baryshkov fw_level = of_find_last_cache_level(cpu); 133a9ff9447SDmitry Baryshkov 134a9ff9447SDmitry Baryshkov if (level < fw_level) { 135a9ff9447SDmitry Baryshkov /* 136a9ff9447SDmitry Baryshkov * some external caches not specified in CLIDR_EL1 137a9ff9447SDmitry Baryshkov * the information may be available in the device tree 138a9ff9447SDmitry Baryshkov * only unified external caches are considered here 139a9ff9447SDmitry Baryshkov */ 140a9ff9447SDmitry Baryshkov leaves += (fw_level - level); 141a9ff9447SDmitry Baryshkov level = fw_level; 142a9ff9447SDmitry Baryshkov } 143a9ff9447SDmitry Baryshkov 144a9ff9447SDmitry Baryshkov this_cpu_ci->num_levels = level; 145a9ff9447SDmitry Baryshkov this_cpu_ci->num_leaves = leaves; 146a9ff9447SDmitry Baryshkov return 0; 147a9ff9447SDmitry Baryshkov } 148a9ff9447SDmitry Baryshkov 149a9ff9447SDmitry Baryshkov int populate_cache_leaves(unsigned int cpu) 150a9ff9447SDmitry Baryshkov { 151a9ff9447SDmitry Baryshkov unsigned int level, idx; 152a9ff9447SDmitry Baryshkov enum cache_type type; 153a9ff9447SDmitry Baryshkov struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu); 154a9ff9447SDmitry Baryshkov struct cacheinfo *this_leaf = this_cpu_ci->info_list; 155a9ff9447SDmitry Baryshkov unsigned int arch = cpu_architecture(); 156a9ff9447SDmitry Baryshkov 157a9ff9447SDmitry Baryshkov /* CLIDR is not present before ARMv7/v7m */ 158a9ff9447SDmitry Baryshkov if (arch < CPU_ARCH_ARMv7) 159a9ff9447SDmitry Baryshkov return -EOPNOTSUPP; 160a9ff9447SDmitry Baryshkov 161a9ff9447SDmitry Baryshkov for (idx = 0, level = 1; level <= this_cpu_ci->num_levels && 162a9ff9447SDmitry Baryshkov idx < this_cpu_ci->num_leaves; idx++, level++) { 163a9ff9447SDmitry Baryshkov type = get_cache_type(level); 164a9ff9447SDmitry Baryshkov if (type == CACHE_TYPE_SEPARATE) { 165a9ff9447SDmitry Baryshkov ci_leaf_init(this_leaf++, CACHE_TYPE_DATA, level); 166a9ff9447SDmitry Baryshkov ci_leaf_init(this_leaf++, CACHE_TYPE_INST, level); 167a9ff9447SDmitry Baryshkov } else { 168a9ff9447SDmitry Baryshkov ci_leaf_init(this_leaf++, type, level); 169a9ff9447SDmitry Baryshkov } 170a9ff9447SDmitry Baryshkov } 171a9ff9447SDmitry Baryshkov 172a9ff9447SDmitry Baryshkov return 0; 173a9ff9447SDmitry Baryshkov } 174