diff options
Diffstat (limited to 'arch/arm64/kernel/topology.c')
| -rw-r--r-- | arch/arm64/kernel/topology.c | 283 | 
1 files changed, 283 insertions, 0 deletions
diff --git a/arch/arm64/kernel/topology.c b/arch/arm64/kernel/topology.c new file mode 100644 index 00000000000..43514f90591 --- /dev/null +++ b/arch/arm64/kernel/topology.c @@ -0,0 +1,283 @@ +/* + * arch/arm64/kernel/topology.c + * + * Copyright (C) 2011,2013,2014 Linaro Limited. + * + * Based on the arm32 version written by Vincent Guittot in turn based on + * arch/sh/kernel/topology.c + * + * This file is subject to the terms and conditions of the GNU General Public + * License.  See the file "COPYING" in the main directory of this archive + * for more details. + */ + +#include <linux/cpu.h> +#include <linux/cpumask.h> +#include <linux/init.h> +#include <linux/percpu.h> +#include <linux/node.h> +#include <linux/nodemask.h> +#include <linux/of.h> +#include <linux/sched.h> + +#include <asm/topology.h> + +static int __init get_cpu_for_node(struct device_node *node) +{ +	struct device_node *cpu_node; +	int cpu; + +	cpu_node = of_parse_phandle(node, "cpu", 0); +	if (!cpu_node) +		return -1; + +	for_each_possible_cpu(cpu) { +		if (of_get_cpu_node(cpu, NULL) == cpu_node) { +			of_node_put(cpu_node); +			return cpu; +		} +	} + +	pr_crit("Unable to find CPU node for %s\n", cpu_node->full_name); + +	of_node_put(cpu_node); +	return -1; +} + +static int __init parse_core(struct device_node *core, int cluster_id, +			     int core_id) +{ +	char name[10]; +	bool leaf = true; +	int i = 0; +	int cpu; +	struct device_node *t; + +	do { +		snprintf(name, sizeof(name), "thread%d", i); +		t = of_get_child_by_name(core, name); +		if (t) { +			leaf = false; +			cpu = get_cpu_for_node(t); +			if (cpu >= 0) { +				cpu_topology[cpu].cluster_id = cluster_id; +				cpu_topology[cpu].core_id = core_id; +				cpu_topology[cpu].thread_id = i; +			} else { +				pr_err("%s: Can't get CPU for thread\n", +				       t->full_name); +				of_node_put(t); +				return -EINVAL; +			} +			of_node_put(t); +		} +		i++; +	} while (t); + +	cpu = get_cpu_for_node(core); +	if (cpu >= 0) { +		if (!leaf) { +			pr_err("%s: Core has both threads and CPU\n", +			       core->full_name); +			return -EINVAL; +		} + +		cpu_topology[cpu].cluster_id = cluster_id; +		cpu_topology[cpu].core_id = core_id; +	} else if (leaf) { +		pr_err("%s: Can't get CPU for leaf core\n", core->full_name); +		return -EINVAL; +	} + +	return 0; +} + +static int __init parse_cluster(struct device_node *cluster, int depth) +{ +	char name[10]; +	bool leaf = true; +	bool has_cores = false; +	struct device_node *c; +	static int cluster_id __initdata; +	int core_id = 0; +	int i, ret; + +	/* +	 * First check for child clusters; we currently ignore any +	 * information about the nesting of clusters and present the +	 * scheduler with a flat list of them. +	 */ +	i = 0; +	do { +		snprintf(name, sizeof(name), "cluster%d", i); +		c = of_get_child_by_name(cluster, name); +		if (c) { +			leaf = false; +			ret = parse_cluster(c, depth + 1); +			of_node_put(c); +			if (ret != 0) +				return ret; +		} +		i++; +	} while (c); + +	/* Now check for cores */ +	i = 0; +	do { +		snprintf(name, sizeof(name), "core%d", i); +		c = of_get_child_by_name(cluster, name); +		if (c) { +			has_cores = true; + +			if (depth == 0) { +				pr_err("%s: cpu-map children should be clusters\n", +				       c->full_name); +				of_node_put(c); +				return -EINVAL; +			} + +			if (leaf) { +				ret = parse_core(c, cluster_id, core_id++); +			} else { +				pr_err("%s: Non-leaf cluster with core %s\n", +				       cluster->full_name, name); +				ret = -EINVAL; +			} + +			of_node_put(c); +			if (ret != 0) +				return ret; +		} +		i++; +	} while (c); + +	if (leaf && !has_cores) +		pr_warn("%s: empty cluster\n", cluster->full_name); + +	if (leaf) +		cluster_id++; + +	return 0; +} + +static int __init parse_dt_topology(void) +{ +	struct device_node *cn, *map; +	int ret = 0; +	int cpu; + +	cn = of_find_node_by_path("/cpus"); +	if (!cn) { +		pr_err("No CPU information found in DT\n"); +		return 0; +	} + +	/* +	 * When topology is provided cpu-map is essentially a root +	 * cluster with restricted subnodes. +	 */ +	map = of_get_child_by_name(cn, "cpu-map"); +	if (!map) +		goto out; + +	ret = parse_cluster(map, 0); +	if (ret != 0) +		goto out_map; + +	/* +	 * Check that all cores are in the topology; the SMP code will +	 * only mark cores described in the DT as possible. +	 */ +	for_each_possible_cpu(cpu) { +		if (cpu_topology[cpu].cluster_id == -1) { +			pr_err("CPU%d: No topology information specified\n", +			       cpu); +			ret = -EINVAL; +		} +	} + +out_map: +	of_node_put(map); +out: +	of_node_put(cn); +	return ret; +} + +/* + * cpu topology table + */ +struct cpu_topology cpu_topology[NR_CPUS]; +EXPORT_SYMBOL_GPL(cpu_topology); + +const struct cpumask *cpu_coregroup_mask(int cpu) +{ +	return &cpu_topology[cpu].core_sibling; +} + +static void update_siblings_masks(unsigned int cpuid) +{ +	struct cpu_topology *cpu_topo, *cpuid_topo = &cpu_topology[cpuid]; +	int cpu; + +	if (cpuid_topo->cluster_id == -1) { +		/* +		 * DT does not contain topology information for this cpu. +		 */ +		pr_debug("CPU%u: No topology information configured\n", cpuid); +		return; +	} + +	/* update core and thread sibling masks */ +	for_each_possible_cpu(cpu) { +		cpu_topo = &cpu_topology[cpu]; + +		if (cpuid_topo->cluster_id != cpu_topo->cluster_id) +			continue; + +		cpumask_set_cpu(cpuid, &cpu_topo->core_sibling); +		if (cpu != cpuid) +			cpumask_set_cpu(cpu, &cpuid_topo->core_sibling); + +		if (cpuid_topo->core_id != cpu_topo->core_id) +			continue; + +		cpumask_set_cpu(cpuid, &cpu_topo->thread_sibling); +		if (cpu != cpuid) +			cpumask_set_cpu(cpu, &cpuid_topo->thread_sibling); +	} +} + +void store_cpu_topology(unsigned int cpuid) +{ +	update_siblings_masks(cpuid); +} + +static void __init reset_cpu_topology(void) +{ +	unsigned int cpu; + +	for_each_possible_cpu(cpu) { +		struct cpu_topology *cpu_topo = &cpu_topology[cpu]; + +		cpu_topo->thread_id = -1; +		cpu_topo->core_id = 0; +		cpu_topo->cluster_id = -1; + +		cpumask_clear(&cpu_topo->core_sibling); +		cpumask_set_cpu(cpu, &cpu_topo->core_sibling); +		cpumask_clear(&cpu_topo->thread_sibling); +		cpumask_set_cpu(cpu, &cpu_topo->thread_sibling); +	} +} + +void __init init_cpu_topology(void) +{ +	reset_cpu_topology(); + +	/* +	 * Discard anything that was parsed if we hit an error so we +	 * don't use partial information. +	 */ +	if (parse_dt_topology()) +		reset_cpu_topology(); +}  | 
