2019-05-29 07:12:26 -07:00
// SPDX-License-Identifier: GPL-2.0-only
2015-01-08 10:42:34 +00:00
/*
* ARM64 cacheinfo support
*
* Copyright ( C ) 2015 ARM Ltd .
* All Rights Reserved
*/
2018-05-11 18:58:03 -05:00
# include <linux/acpi.h>
2015-01-08 10:42:34 +00:00
# include <linux/cacheinfo.h>
# include <linux/of.h>
# define MAX_CACHE_LEVEL 7 /* Max 7 level supported */
2019-05-28 10:16:54 +08:00
int cache_line_size ( void )
{
if ( coherency_max_size ! = 0 )
return coherency_max_size ;
2019-06-14 09:11:41 -04:00
return cache_line_size_of_cpu ( ) ;
2019-05-28 10:16:54 +08:00
}
EXPORT_SYMBOL_GPL ( cache_line_size ) ;
2015-01-08 10:42:34 +00:00
static inline enum cache_type get_cache_type ( int level )
{
u64 clidr ;
if ( level > MAX_CACHE_LEVEL )
return CACHE_TYPE_NOCACHE ;
2016-09-08 13:55:38 +01:00
clidr = read_sysreg ( clidr_el1 ) ;
2015-01-08 10:42:34 +00:00
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 ;
}
2023-04-12 14:57:58 -04:00
static void detect_cache_level ( unsigned int * level_p , unsigned int * leaves_p )
2015-01-08 10:42:34 +00:00
{
2022-08-08 09:46:40 +01:00
unsigned int ctype , level , leaves ;
2015-01-08 10:42:34 +00:00
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 ;
}
2023-04-12 14:57:58 -04:00
* level_p = level ;
* leaves_p = leaves ;
}
int early_cache_level ( unsigned int cpu )
{
struct cpu_cacheinfo * this_cpu_ci = get_cpu_cacheinfo ( cpu ) ;
detect_cache_level ( & this_cpu_ci - > num_levels , & this_cpu_ci - > num_leaves ) ;
return 0 ;
}
int init_cache_level ( unsigned int cpu )
{
unsigned int level , leaves ;
int fw_level , ret ;
struct cpu_cacheinfo * this_cpu_ci = get_cpu_cacheinfo ( cpu ) ;
detect_cache_level ( & level , & leaves ) ;
2023-01-04 19:30:28 +01:00
if ( acpi_disabled ) {
2018-05-11 18:58:03 -05:00
fw_level = of_find_last_cache_level ( cpu ) ;
2023-01-04 19:30:28 +01:00
} else {
ret = acpi_get_cache_info ( cpu , & fw_level , NULL ) ;
if ( ret < 0 )
2023-01-24 16:40:47 +01:00
fw_level = 0 ;
2023-01-04 19:30:28 +01:00
}
2018-05-11 18:58:03 -05:00
if ( level < fw_level ) {
2017-01-16 10:40:44 +00:00
/*
* some external caches not specified in CLIDR_EL1
* the information may be available in the device tree
* only unified external caches are considered here
*/
2018-05-11 18:58:03 -05:00
leaves + = ( fw_level - level ) ;
level = fw_level ;
2017-01-16 10:40:44 +00:00
}
2015-01-08 10:42:34 +00:00
this_cpu_ci - > num_levels = level ;
this_cpu_ci - > num_leaves = leaves ;
return 0 ;
}
2021-08-31 13:48:34 +02:00
int populate_cache_leaves ( unsigned int cpu )
2015-01-08 10:42:34 +00:00
{
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 ;
}