diff mbox series

arm: kernel: add support for detecting armv8 cpu cache information

Message ID 1539843407-7439-1-git-send-email-tengfei@codeaurora.org (mailing list archive)
State New, archived
Headers show
Series arm: kernel: add support for detecting armv8 cpu cache information | expand

Commit Message

Teng Fei Fan Oct. 18, 2018, 6:16 a.m. UTC
This patch adds support for cacheinfo on 32bit ARMv8 platform.
Add support for detecting cpu cache information cpu cache information
via sysfs for 32bit armv8 platform. And export to sysfs then userspace
can get from /sys/devices/system/cpu/cpuX/cache.

Signed-off-by: Teng Fei Fan <tengfei@codeaurora.org>
Cc: Russell King <linux@arm.linux.org.uk>
Cc: Will Deacon <will.deacon@arm.com>
Cc: linux-arm-kernel@lists.infradead.org
---
 arch/arm/include/asm/cachetype.h |  38 ++++++++++++
 arch/arm/kernel/Makefile         |   3 +-
 arch/arm/kernel/cacheinfo.c      | 128 +++++++++++++++++++++++++++++++++++++++
 3 files changed, 168 insertions(+), 1 deletion(-)
 create mode 100644 arch/arm/kernel/cacheinfo.c

Comments

Russell King (Oracle) Oct. 18, 2018, 8:56 a.m. UTC | #1
On Thu, Oct 18, 2018 at 02:16:47PM +0800, Teng Fei Fan wrote:
> This patch adds support for cacheinfo on 32bit ARMv8 platform.
> Add support for detecting cpu cache information cpu cache information
> via sysfs for 32bit armv8 platform. And export to sysfs then userspace
> can get from /sys/devices/system/cpu/cpuX/cache.

You don't explain why this is needed.

We don't do this for previous 32-bit CPUs, so why should we do this
for ARMv8 running on a 32-bit kernel?
Sudeep Holla Oct. 18, 2018, 9:36 a.m. UTC | #2
On Thu, Oct 18, 2018 at 02:16:47PM +0800, Teng Fei Fan wrote:
> This patch adds support for cacheinfo on 32bit ARMv8 platform.
> Add support for detecting cpu cache information cpu cache information
> via sysfs for 32bit armv8 platform. And export to sysfs then userspace
> can get from /sys/devices/system/cpu/cpuX/cache.
>
> Signed-off-by: Teng Fei Fan <tengfei@codeaurora.org>
> Cc: Russell King <linux@arm.linux.org.uk>
> Cc: Will Deacon <will.deacon@arm.com>
> Cc: linux-arm-kernel@lists.infradead.org
> ---
>  arch/arm/include/asm/cachetype.h |  38 ++++++++++++
>  arch/arm/kernel/Makefile         |   3 +-
>  arch/arm/kernel/cacheinfo.c      | 128 +++++++++++++++++++++++++++++++++++++++
>  3 files changed, 168 insertions(+), 1 deletion(-)
>  create mode 100644 arch/arm/kernel/cacheinfo.c
>
> diff --git a/arch/arm/include/asm/cachetype.h b/arch/arm/include/asm/cachetype.h
> index 01509ae..f639c64 100644
> --- a/arch/arm/include/asm/cachetype.h
> +++ b/arch/arm/include/asm/cachetype.h
> @@ -1,6 +1,31 @@
>  #ifndef __ASM_ARM_CACHETYPE_H
>  #define __ASM_ARM_CACHETYPE_H
>
> +/*
> + * NumSets, bits[27:13] - (Number of sets in cache) - 1
> + * Associativity, bits[12:3] - (Associativity of cache) - 1
> + * LineSize, bits[2:0] - (Log2(Number of words in cache line)) - 2
> + */
> +#define CCSIDR_EL1_WRITE_THROUGH	BIT(31)
> +#define CCSIDR_EL1_WRITE_BACK		BIT(30)
> +#define CCSIDR_EL1_READ_ALLOCATE	BIT(29)
> +#define CCSIDR_EL1_WRITE_ALLOCATE	BIT(28)
> +#define CCSIDR_EL1_LINESIZE_MASK	0x7
> +#define CCSIDR_EL1_LINESIZE(x)		((x) & CCSIDR_EL1_LINESIZE_MASK)
> +#define CCSIDR_EL1_ASSOCIATIVITY_SHIFT	3
> +#define CCSIDR_EL1_ASSOCIATIVITY_MASK	0x3ff
> +#define CCSIDR_EL1_ASSOCIATIVITY(x) \
> +	(((x) >> CCSIDR_EL1_ASSOCIATIVITY_SHIFT) \
> +	& CCSIDR_EL1_ASSOCIATIVITY_MASK)
> +#define CCSIDR_EL1_NUMSETS_SHIFT	13
> +#define CCSIDR_EL1_NUMSETS_MASK		0x7fff
> +#define CCSIDR_EL1_NUMSETS(x) \
> +	(((x) >> CCSIDR_EL1_NUMSETS_SHIFT) & CCSIDR_EL1_NUMSETS_MASK)
> +
> +#define CACHE_LINESIZE(x)	(16 << CCSIDR_EL1_LINESIZE(x))
> +#define CACHE_NUMSETS(x)	(CCSIDR_EL1_NUMSETS(x) + 1)
> +#define CACHE_ASSOCIATIVITY(x)	(CCSIDR_EL1_ASSOCIATIVITY(x) + 1)
> +

This was dropped from arm64 via the commit
a8d4636f96ad ("arm64: cacheinfo: Remove CCSIDR-based cache information probing")

So it makes no sense to add CCSIDR based cacheinfo back. If we add this
support, it should be entirely based on DT.

--
Regards,
Sudeep
Teng Fei Fan Nov. 5, 2018, 8:21 a.m. UTC | #3
Hi Russell,

     our team team was trying to check L1, L2 cache size with adb 
commands help on 32bit ARMv8 platform, so we submit this patch.

Thanks,
Tengfei Fan

On 2018-10-18 16:56, Russell King - ARM Linux wrote:
> On Thu, Oct 18, 2018 at 02:16:47PM +0800, Teng Fei Fan wrote:
>> This patch adds support for cacheinfo on 32bit ARMv8 platform.
>> Add support for detecting cpu cache information cpu cache information
>> via sysfs for 32bit armv8 platform. And export to sysfs then userspace
>> can get from /sys/devices/system/cpu/cpuX/cache.
> 
> You don't explain why this is needed.
> 
> We don't do this for previous 32-bit CPUs, so why should we do this
> for ARMv8 running on a 32-bit kernel?
diff mbox series

Patch

diff --git a/arch/arm/include/asm/cachetype.h b/arch/arm/include/asm/cachetype.h
index 01509ae..f639c64 100644
--- a/arch/arm/include/asm/cachetype.h
+++ b/arch/arm/include/asm/cachetype.h
@@ -1,6 +1,31 @@ 
 #ifndef __ASM_ARM_CACHETYPE_H
 #define __ASM_ARM_CACHETYPE_H
 
+/*
+ * NumSets, bits[27:13] - (Number of sets in cache) - 1
+ * Associativity, bits[12:3] - (Associativity of cache) - 1
+ * LineSize, bits[2:0] - (Log2(Number of words in cache line)) - 2
+ */
+#define CCSIDR_EL1_WRITE_THROUGH	BIT(31)
+#define CCSIDR_EL1_WRITE_BACK		BIT(30)
+#define CCSIDR_EL1_READ_ALLOCATE	BIT(29)
+#define CCSIDR_EL1_WRITE_ALLOCATE	BIT(28)
+#define CCSIDR_EL1_LINESIZE_MASK	0x7
+#define CCSIDR_EL1_LINESIZE(x)		((x) & CCSIDR_EL1_LINESIZE_MASK)
+#define CCSIDR_EL1_ASSOCIATIVITY_SHIFT	3
+#define CCSIDR_EL1_ASSOCIATIVITY_MASK	0x3ff
+#define CCSIDR_EL1_ASSOCIATIVITY(x) \
+	(((x) >> CCSIDR_EL1_ASSOCIATIVITY_SHIFT) \
+	& CCSIDR_EL1_ASSOCIATIVITY_MASK)
+#define CCSIDR_EL1_NUMSETS_SHIFT	13
+#define CCSIDR_EL1_NUMSETS_MASK		0x7fff
+#define CCSIDR_EL1_NUMSETS(x) \
+	(((x) >> CCSIDR_EL1_NUMSETS_SHIFT) & CCSIDR_EL1_NUMSETS_MASK)
+
+#define CACHE_LINESIZE(x)	(16 << CCSIDR_EL1_LINESIZE(x))
+#define CACHE_NUMSETS(x)	(CCSIDR_EL1_NUMSETS(x) + 1)
+#define CACHE_ASSOCIATIVITY(x)	(CCSIDR_EL1_ASSOCIATIVITY(x) + 1)
+
 #define CACHEID_VIVT			(1 << 0)
 #define CACHEID_VIPT_NONALIASING	(1 << 1)
 #define CACHEID_VIPT_ALIASING		(1 << 2)
@@ -80,6 +105,14 @@  static inline unsigned int read_ccsidr(void)
 	asm volatile("mrc p15, 1, %0, c0, c0, 0" : "=r" (val));
 	return val;
 }
+
+static inline unsigned int read_clidr(void)
+{
+	unsigned int val;
+
+	asm volatile("mrc	p15, 1, %0, c0, c0, 1" : "=r" (val));
+	return val;
+}
 #else /* CONFIG_CPU_V7M */
 #include <linux/io.h>
 #include "asm/v7m.h"
@@ -93,6 +126,11 @@  static inline unsigned int read_ccsidr(void)
 {
 	return readl(BASEADDR_V7M_SCB + V7M_SCB_CCSIDR);
 }
+
+static inline unsigned int read_clidr(void)
+{
+	return readl_relaxed(BASEADDR_V7M_SCB + V7M_SCB_CLIDR);
+}
 #endif
 
 #endif
diff --git a/arch/arm/kernel/Makefile b/arch/arm/kernel/Makefile
index ad325a8..3abd6aa 100644
--- a/arch/arm/kernel/Makefile
+++ b/arch/arm/kernel/Makefile
@@ -18,7 +18,8 @@  CFLAGS_REMOVE_return_address.o = -pg
 obj-y		:= elf.o entry-common.o irq.o opcodes.o \
 		   process.o ptrace.o reboot.o return_address.o \
 		   setup.o signal.o sigreturn_codes.o \
-		   stacktrace.o sys_arm.o time.o traps.o
+		   stacktrace.o sys_arm.o time.o traps.o \
+		   cacheinfo.o
 
 obj-$(CONFIG_ATAGS)		+= atags_parse.o
 obj-$(CONFIG_ATAGS_PROC)	+= atags_proc.o
diff --git a/arch/arm/kernel/cacheinfo.c b/arch/arm/kernel/cacheinfo.c
new file mode 100644
index 0000000..2880b34
--- /dev/null
+++ b/arch/arm/kernel/cacheinfo.c
@@ -0,0 +1,128 @@ 
+/*
+ *  ARM cacheinfo support
+ *
+ *  Copyright (C) 2015 ARM Ltd.
+ *  All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/bitops.h>
+#include <linux/cacheinfo.h>
+#include <linux/cpu.h>
+#include <linux/compiler.h>
+#include <linux/of.h>
+
+#include <asm/cachetype.h>
+#include <asm/processor.h>
+
+#define MAX_CACHE_LEVEL			7	/* Max 7 level supported */
+/* 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))
+
+static inline enum cache_type get_cache_type(int level)
+{
+	unsigned int clidr;
+
+	if (level > MAX_CACHE_LEVEL)
+		return CACHE_TYPE_NOCACHE;
+	clidr = read_clidr();
+
+	return CLIDR_CTYPE(clidr, level);
+}
+
+/*
+ * Cache Size Selection Register(CSSELR) selects which Cache Size ID
+ * Register(CCSIDR) is accessible by specifying the required cache
+ * level and the cache type. We need to ensure that no one else changes
+ * CSSELR by calling this in non-preemtible context
+ */
+static unsigned int __attribute_const__ cache_get_ccsidr(unsigned int csselr)
+{
+	unsigned int ccsidr;
+
+	WARN_ON(preemptible());
+
+	/* Put value into CSSELR */
+	set_csselr(csselr);
+	isb();
+	ccsidr = read_ccsidr();
+
+	return ccsidr;
+}
+
+static void ci_leaf_init(struct cacheinfo *this_leaf,
+			 enum cache_type type, unsigned int level)
+{
+	bool is_icache = type & CACHE_TYPE_INST;
+	unsigned int tmp = cache_get_ccsidr((level - 1) << 1 | is_icache);
+
+	this_leaf->level = level;
+	this_leaf->type = type;
+	this_leaf->coherency_line_size = CACHE_LINESIZE(tmp);
+	this_leaf->number_of_sets = CACHE_NUMSETS(tmp);
+	this_leaf->ways_of_associativity = CACHE_ASSOCIATIVITY(tmp);
+	this_leaf->size = this_leaf->number_of_sets *
+	this_leaf->coherency_line_size * this_leaf->ways_of_associativity;
+	this_leaf->attributes =
+		((tmp & CCSIDR_EL1_WRITE_THROUGH) ? CACHE_WRITE_THROUGH : 0) |
+		((tmp & CCSIDR_EL1_WRITE_BACK) ? CACHE_WRITE_BACK : 0) |
+		((tmp & CCSIDR_EL1_READ_ALLOCATE) ? CACHE_READ_ALLOCATE : 0) |
+		((tmp & CCSIDR_EL1_WRITE_ALLOCATE) ? CACHE_WRITE_ALLOCATE : 0);
+}
+
+static int __init_cache_level(unsigned int cpu)
+{
+	unsigned int ctype, level, leaves;
+	struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu);
+
+	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;
+	}
+
+	this_cpu_ci->num_levels = level;
+	this_cpu_ci->num_leaves = leaves;
+	return 0;
+}
+
+static 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;
+
+	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;
+}
+
+DEFINE_SMP_CALL_CACHE_FUNCTION(init_cache_level)
+DEFINE_SMP_CALL_CACHE_FUNCTION(populate_cache_leaves)