diff options
Diffstat (limited to 'drivers/base/cacheinfo.c')
| -rw-r--r-- | drivers/base/cacheinfo.c | 195 | 
1 files changed, 149 insertions, 46 deletions
diff --git a/drivers/base/cacheinfo.c b/drivers/base/cacheinfo.c index e9fd32e91668..1e3903d0d994 100644 --- a/drivers/base/cacheinfo.c +++ b/drivers/base/cacheinfo.c @@ -16,6 +16,9 @@   * You should have received a copy of the GNU General Public License   * along with this program.  If not, see <http://www.gnu.org/licenses/>.   */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/acpi.h>  #include <linux/bitops.h>  #include <linux/cacheinfo.h>  #include <linux/compiler.h> @@ -85,7 +88,120 @@ static inline bool cache_leaves_are_shared(struct cacheinfo *this_leaf,  {  	return sib_leaf->of_node == this_leaf->of_node;  } + +/* OF properties to query for a given cache type */ +struct cache_type_info { +	const char *size_prop; +	const char *line_size_props[2]; +	const char *nr_sets_prop; +}; + +static const struct cache_type_info cache_type_info[] = { +	{ +		.size_prop       = "cache-size", +		.line_size_props = { "cache-line-size", +				     "cache-block-size", }, +		.nr_sets_prop    = "cache-sets", +	}, { +		.size_prop       = "i-cache-size", +		.line_size_props = { "i-cache-line-size", +				     "i-cache-block-size", }, +		.nr_sets_prop    = "i-cache-sets", +	}, { +		.size_prop       = "d-cache-size", +		.line_size_props = { "d-cache-line-size", +				     "d-cache-block-size", }, +		.nr_sets_prop    = "d-cache-sets", +	}, +}; + +static inline int get_cacheinfo_idx(enum cache_type type) +{ +	if (type == CACHE_TYPE_UNIFIED) +		return 0; +	return type; +} + +static void cache_size(struct cacheinfo *this_leaf) +{ +	const char *propname; +	const __be32 *cache_size; +	int ct_idx; + +	ct_idx = get_cacheinfo_idx(this_leaf->type); +	propname = cache_type_info[ct_idx].size_prop; + +	cache_size = of_get_property(this_leaf->of_node, propname, NULL); +	if (cache_size) +		this_leaf->size = of_read_number(cache_size, 1); +} + +/* not cache_line_size() because that's a macro in include/linux/cache.h */ +static void cache_get_line_size(struct cacheinfo *this_leaf) +{ +	const __be32 *line_size; +	int i, lim, ct_idx; + +	ct_idx = get_cacheinfo_idx(this_leaf->type); +	lim = ARRAY_SIZE(cache_type_info[ct_idx].line_size_props); + +	for (i = 0; i < lim; i++) { +		const char *propname; + +		propname = cache_type_info[ct_idx].line_size_props[i]; +		line_size = of_get_property(this_leaf->of_node, propname, NULL); +		if (line_size) +			break; +	} + +	if (line_size) +		this_leaf->coherency_line_size = of_read_number(line_size, 1); +} + +static void cache_nr_sets(struct cacheinfo *this_leaf) +{ +	const char *propname; +	const __be32 *nr_sets; +	int ct_idx; + +	ct_idx = get_cacheinfo_idx(this_leaf->type); +	propname = cache_type_info[ct_idx].nr_sets_prop; + +	nr_sets = of_get_property(this_leaf->of_node, propname, NULL); +	if (nr_sets) +		this_leaf->number_of_sets = of_read_number(nr_sets, 1); +} + +static void cache_associativity(struct cacheinfo *this_leaf) +{ +	unsigned int line_size = this_leaf->coherency_line_size; +	unsigned int nr_sets = this_leaf->number_of_sets; +	unsigned int size = this_leaf->size; + +	/* +	 * If the cache is fully associative, there is no need to +	 * check the other properties. +	 */ +	if (!(nr_sets == 1) && (nr_sets > 0 && size > 0 && line_size > 0)) +		this_leaf->ways_of_associativity = (size / nr_sets) / line_size; +} + +static void cache_of_override_properties(unsigned int cpu) +{ +	int index; +	struct cacheinfo *this_leaf; +	struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu); + +	for (index = 0; index < cache_leaves(cpu); index++) { +		this_leaf = this_cpu_ci->info_list + index; +		cache_size(this_leaf); +		cache_get_line_size(this_leaf); +		cache_nr_sets(this_leaf); +		cache_associativity(this_leaf); +	} +}  #else +static void cache_of_override_properties(unsigned int cpu) { }  static inline int cache_setup_of_node(unsigned int cpu) { return 0; }  static inline bool cache_leaves_are_shared(struct cacheinfo *this_leaf,  					   struct cacheinfo *sib_leaf) @@ -104,9 +220,16 @@ static int cache_shared_cpu_map_setup(unsigned int cpu)  	struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu);  	struct cacheinfo *this_leaf, *sib_leaf;  	unsigned int index; -	int ret; +	int ret = 0; + +	if (this_cpu_ci->cpu_map_populated) +		return 0; -	ret = cache_setup_of_node(cpu); +	if (of_have_populated_dt()) +		ret = cache_setup_of_node(cpu); +	else if (!acpi_disabled) +		/* No cache property/hierarchy support yet in ACPI */ +		ret = -ENOTSUPP;  	if (ret)  		return ret; @@ -161,6 +284,12 @@ static void cache_shared_cpu_map_remove(unsigned int cpu)  	}  } +static void cache_override_properties(unsigned int cpu) +{ +	if (of_have_populated_dt()) +		return cache_of_override_properties(cpu); +} +  static void free_cache_attributes(unsigned int cpu)  {  	if (!per_cpu_cacheinfo(cpu)) @@ -203,10 +332,11 @@ static int detect_cache_attributes(unsigned int cpu)  	 */  	ret = cache_shared_cpu_map_setup(cpu);  	if (ret) { -		pr_warn("Unable to detect cache hierarchy from DT for CPU %d\n", -			cpu); +		pr_warn("Unable to detect cache hierarchy for CPU %d\n", cpu);  		goto free_ci;  	} + +	cache_override_properties(cpu);  	return 0;  free_ci: @@ -498,57 +628,30 @@ err:  	return rc;  } -static void cache_remove_dev(unsigned int cpu) +static int cacheinfo_cpu_online(unsigned int cpu)  { -	if (!cpumask_test_cpu(cpu, &cache_dev_map)) -		return; -	cpumask_clear_cpu(cpu, &cache_dev_map); +	int rc = detect_cache_attributes(cpu); -	cpu_cache_sysfs_exit(cpu); +	if (rc) +		return rc; +	rc = cache_add_dev(cpu); +	if (rc) +		free_cache_attributes(cpu); +	return rc;  } -static int cacheinfo_cpu_callback(struct notifier_block *nfb, -				  unsigned long action, void *hcpu) +static int cacheinfo_cpu_pre_down(unsigned int cpu)  { -	unsigned int cpu = (unsigned long)hcpu; -	int rc = 0; +	if (cpumask_test_and_clear_cpu(cpu, &cache_dev_map)) +		cpu_cache_sysfs_exit(cpu); -	switch (action & ~CPU_TASKS_FROZEN) { -	case CPU_ONLINE: -		rc = detect_cache_attributes(cpu); -		if (!rc) -			rc = cache_add_dev(cpu); -		break; -	case CPU_DEAD: -		cache_remove_dev(cpu); -		free_cache_attributes(cpu); -		break; -	} -	return notifier_from_errno(rc); +	free_cache_attributes(cpu); +	return 0;  }  static int __init cacheinfo_sysfs_init(void)  { -	int cpu, rc = 0; - -	cpu_notifier_register_begin(); - -	for_each_online_cpu(cpu) { -		rc = detect_cache_attributes(cpu); -		if (rc) -			goto out; -		rc = cache_add_dev(cpu); -		if (rc) { -			free_cache_attributes(cpu); -			pr_err("error populating cacheinfo..cpu%d\n", cpu); -			goto out; -		} -	} -	__hotcpu_notifier(cacheinfo_cpu_callback, 0); - -out: -	cpu_notifier_register_done(); -	return rc; +	return cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "base/cacheinfo:online", +				 cacheinfo_cpu_online, cacheinfo_cpu_pre_down);  } -  device_initcall(cacheinfo_sysfs_init);  |