]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
ARM: 9433/2: implement cacheinfo support
authorDmitry Baryshkov <dmitry.baryshkov@linaro.org>
Tue, 14 Jan 2025 11:54:52 +0000 (12:54 +0100)
committerRussell King (Oracle) <rmk+kernel@armlinux.org.uk>
Tue, 14 Jan 2025 12:29:30 +0000 (12:29 +0000)
On ARMv7 / v7m machines read CTR and CLIDR registers to provide
information regarding the cache topology. Earlier machines should
describe full cache topology in the device tree.

Note, this follows the ARM64 cacheinfo support and provides only minimal
support required to bootstrap cache info. All useful properties should
be decribed in Device Tree.

Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
arch/arm/Kconfig
arch/arm/include/asm/cache.h
arch/arm/kernel/Makefile
arch/arm/kernel/cacheinfo.c [new file with mode: 0644]
include/linux/cacheinfo.h

index 19ca8c271ffec1127b732fc1ea8ddea1bfff0c51..d7d2223d404203fc8e817248c0719d2cc205056e 100644 (file)
@@ -5,6 +5,7 @@ config ARM
        select ARCH_32BIT_OFF_T
        select ARCH_CORRECT_STACKTRACE_ON_KRETPROBE if HAVE_KRETPROBES && FRAME_POINTER && !ARM_UNWIND
        select ARCH_HAS_BINFMT_FLAT
+       select ARCH_HAS_CACHE_LINE_SIZE if OF
        select ARCH_HAS_CPU_CACHE_ALIASING
        select ARCH_HAS_CPU_FINALIZE_INIT if MMU
        select ARCH_HAS_CURRENT_STACK_POINTER
index e3ea34558ada0a52048d41d9e6eef3a1394d83c5..ecbc100d22a56c8f685ab9892c1afb9d76c14e58 100644 (file)
 
 #define __read_mostly __section(".data..read_mostly")
 
+#ifndef __ASSEMBLY__
+#ifdef CONFIG_ARCH_HAS_CACHE_LINE_SIZE
+int cache_line_size(void);
+#endif
+#endif
+
 #endif
index aaae31b8c4a5dba25b4cd362fe95e5ad2fa0df6c..b3333d070390a8ff366a306931614b1b260647bd 100644 (file)
@@ -40,6 +40,7 @@ obj-y         += entry-armv.o
 endif
 
 obj-$(CONFIG_MMU)              += bugs.o
+obj-$(CONFIG_OF)               += cacheinfo.o
 obj-$(CONFIG_CPU_IDLE)         += cpuidle.o
 obj-$(CONFIG_ISA_DMA_API)      += dma.o
 obj-$(CONFIG_FIQ)              += fiq.o fiqasm.o
diff --git a/arch/arm/kernel/cacheinfo.c b/arch/arm/kernel/cacheinfo.c
new file mode 100644 (file)
index 0000000..a8eabca
--- /dev/null
@@ -0,0 +1,173 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ *  ARM cacheinfo support
+ *
+ *  Copyright (C) 2023 Linaro Ltd.
+ *  Copyright (C) 2015 ARM Ltd.
+ *  All Rights Reserved
+ */
+
+#include <linux/bitfield.h>
+#include <linux/cacheinfo.h>
+#include <linux/of.h>
+
+#include <asm/cachetype.h>
+#include <asm/cputype.h>
+#include <asm/system_info.h>
+
+/* Ctypen, bits[3(n - 1) + 2 : 3(n - 1)], for n = 1 to 7 */
+#define CLIDR_CTYPE_SHIFT(level)       (3 * (level - 1))
+#define CLIDR_CTYPE_MASK(level)                (7 << CLIDR_CTYPE_SHIFT(level))
+#define CLIDR_CTYPE(clidr, level)      \
+       (((clidr) & CLIDR_CTYPE_MASK(level)) >> CLIDR_CTYPE_SHIFT(level))
+
+#define MAX_CACHE_LEVEL                        7       /* Max 7 level supported */
+
+#define CTR_FORMAT_MASK        GENMASK(27, 24)
+#define CTR_FORMAT_ARMV6 0
+#define CTR_FORMAT_ARMV7 4
+#define CTR_CWG_MASK   GENMASK(27, 24)
+#define CTR_DSIZE_LEN_MASK GENMASK(13, 12)
+#define CTR_ISIZE_LEN_MASK GENMASK(1, 0)
+
+/* Also valid for v7m */
+static inline int cache_line_size_cp15(void)
+{
+       u32 ctr = read_cpuid_cachetype();
+       u32 format = FIELD_GET(CTR_FORMAT_MASK, ctr);
+
+       if (format == CTR_FORMAT_ARMV7) {
+               u32 cwg = FIELD_GET(CTR_CWG_MASK, ctr);
+
+               return cwg ? 4 << cwg : ARCH_DMA_MINALIGN;
+       } else if (WARN_ON_ONCE(format != CTR_FORMAT_ARMV6)) {
+               return ARCH_DMA_MINALIGN;
+       }
+
+       return 8 << max(FIELD_GET(CTR_ISIZE_LEN_MASK, ctr),
+                       FIELD_GET(CTR_DSIZE_LEN_MASK, ctr));
+}
+
+int cache_line_size(void)
+{
+       if (coherency_max_size != 0)
+               return coherency_max_size;
+
+       /* CP15 is optional / implementation defined before ARMv6 */
+       if (cpu_architecture() < CPU_ARCH_ARMv6)
+               return ARCH_DMA_MINALIGN;
+
+       return cache_line_size_cp15();
+}
+EXPORT_SYMBOL_GPL(cache_line_size);
+
+static inline enum cache_type get_cache_type(int level)
+{
+       u32 clidr;
+
+       if (level > MAX_CACHE_LEVEL)
+               return CACHE_TYPE_NOCACHE;
+
+       clidr = read_clidr();
+
+       return CLIDR_CTYPE(clidr, level);
+}
+
+static void ci_leaf_init(struct cacheinfo *this_leaf,
+                        enum cache_type type, unsigned int level)
+{
+       this_leaf->level = level;
+       this_leaf->type = type;
+}
+
+static int detect_cache_level(unsigned int *level_p, unsigned int *leaves_p)
+{
+       unsigned int ctype, level, leaves;
+       u32 ctr, format;
+
+       /* CLIDR is not present before ARMv7/v7m */
+       if (cpu_architecture() < CPU_ARCH_ARMv7)
+               return -EOPNOTSUPP;
+
+       /* Don't try reading CLIDR if CTR declares old format */
+       ctr = read_cpuid_cachetype();
+       format = FIELD_GET(CTR_FORMAT_MASK, ctr);
+       if (format != CTR_FORMAT_ARMV7)
+               return -EOPNOTSUPP;
+
+       for (level = 1, leaves = 0; level <= MAX_CACHE_LEVEL; level++) {
+               ctype = get_cache_type(level);
+               if (ctype == CACHE_TYPE_NOCACHE) {
+                       level--;
+                       break;
+               }
+               /* Separate instruction and data caches */
+               leaves += (ctype == CACHE_TYPE_SEPARATE) ? 2 : 1;
+       }
+
+       *level_p = level;
+       *leaves_p = leaves;
+
+       return 0;
+}
+
+int early_cache_level(unsigned int cpu)
+{
+       struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu);
+
+       return detect_cache_level(&this_cpu_ci->num_levels, &this_cpu_ci->num_leaves);
+}
+
+int init_cache_level(unsigned int cpu)
+{
+       unsigned int level, leaves;
+       struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu);
+       int fw_level;
+       int ret;
+
+       ret = detect_cache_level(&level, &leaves);
+       if (ret)
+               return ret;
+
+       fw_level = of_find_last_cache_level(cpu);
+
+       if (level < fw_level) {
+               /*
+                * some external caches not specified in CLIDR_EL1
+                * the information may be available in the device tree
+                * only unified external caches are considered here
+                */
+               leaves += (fw_level - level);
+               level = fw_level;
+       }
+
+       this_cpu_ci->num_levels = level;
+       this_cpu_ci->num_leaves = leaves;
+       return 0;
+}
+
+int populate_cache_leaves(unsigned int cpu)
+{
+       unsigned int level, idx;
+       enum cache_type type;
+       struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu);
+       struct cacheinfo *this_leaf = this_cpu_ci->info_list;
+       unsigned int arch = cpu_architecture();
+
+       /* CLIDR is not present before ARMv7/v7m */
+       if (arch < CPU_ARCH_ARMv7)
+               return -EOPNOTSUPP;
+
+       for (idx = 0, level = 1; level <= this_cpu_ci->num_levels &&
+            idx < this_cpu_ci->num_leaves; idx++, level++) {
+               type = get_cache_type(level);
+               if (type == CACHE_TYPE_SEPARATE) {
+                       ci_leaf_init(this_leaf++, CACHE_TYPE_DATA, level);
+                       ci_leaf_init(this_leaf++, CACHE_TYPE_INST, level);
+               } else {
+                       ci_leaf_init(this_leaf++, type, level);
+               }
+       }
+
+       return 0;
+}
index 108060612bb87566a004f98864ced3baf7922ac6..1e7061549fc74d1cde1fbfb5354f1d0f33a781f9 100644 (file)
@@ -147,7 +147,7 @@ static inline int get_cpu_cacheinfo_id(int cpu, int level)
        return ci ? ci->id : -1;
 }
 
-#ifdef CONFIG_ARM64
+#if defined(CONFIG_ARM64) || defined(CONFIG_ARM)
 #define use_arch_cache_info()  (true)
 #else
 #define use_arch_cache_info()  (false)