summaryrefslogtreecommitdiff
path: root/arch
diff options
context:
space:
mode:
authorRussell King <rmk+kernel@arm.linux.org.uk>2012-11-20 20:18:51 +0000
committerRussell King <rmk+kernel@arm.linux.org.uk>2012-11-20 20:18:51 +0000
commite38eb34aab13a81b91400c75c703252473100bc4 (patch)
treea250c6071dba9bb1cdb2cff143b33906230e6439 /arch
parent2079f30e9e83887ca95fa129d0bc734b2c4b406d (diff)
parent384a290283fde63ba8dc671fca5420111cdac19a (diff)
Merge branch 'cluster-boot-protocol' of git://linux-arm.org/linux-2.6-lp into devel-stable
Diffstat (limited to 'arch')
-rw-r--r--arch/arm/common/gic.c45
-rw-r--r--arch/arm/include/asm/cputype.h13
-rw-r--r--arch/arm/include/asm/prom.h2
-rw-r--r--arch/arm/include/asm/smp_plat.h17
-rw-r--r--arch/arm/kernel/devtree.c100
-rw-r--r--arch/arm/kernel/setup.c8
-rw-r--r--arch/arm/kernel/topology.c42
7 files changed, 179 insertions, 48 deletions
diff --git a/arch/arm/common/gic.c b/arch/arm/common/gic.c
index aa5269984187..36ae03a3f5d1 100644
--- a/arch/arm/common/gic.c
+++ b/arch/arm/common/gic.c
@@ -70,6 +70,14 @@ struct gic_chip_data {
static DEFINE_RAW_SPINLOCK(irq_controller_lock);
/*
+ * The GIC mapping of CPU interfaces does not necessarily match
+ * the logical CPU numbering. Let's use a mapping as returned
+ * by the GIC itself.
+ */
+#define NR_GIC_CPU_IF 8
+static u8 gic_cpu_map[NR_GIC_CPU_IF] __read_mostly;
+
+/*
* Supported arch specific GIC irq extension.
* Default make them NULL.
*/
@@ -238,11 +246,11 @@ static int gic_set_affinity(struct irq_data *d, const struct cpumask *mask_val,
unsigned int cpu = cpumask_any_and(mask_val, cpu_online_mask);
u32 val, mask, bit;
- if (cpu >= 8 || cpu >= nr_cpu_ids)
+ if (cpu >= NR_GIC_CPU_IF || cpu >= nr_cpu_ids)
return -EINVAL;
mask = 0xff << shift;
- bit = 1 << (cpu_logical_map(cpu) + shift);
+ bit = gic_cpu_map[cpu] << shift;
raw_spin_lock(&irq_controller_lock);
val = readl_relaxed(reg) & ~mask;
@@ -349,11 +357,6 @@ static void __init gic_dist_init(struct gic_chip_data *gic)
u32 cpumask;
unsigned int gic_irqs = gic->gic_irqs;
void __iomem *base = gic_data_dist_base(gic);
- u32 cpu = cpu_logical_map(smp_processor_id());
-
- cpumask = 1 << cpu;
- cpumask |= cpumask << 8;
- cpumask |= cpumask << 16;
writel_relaxed(0, base + GIC_DIST_CTRL);
@@ -366,6 +369,7 @@ static void __init gic_dist_init(struct gic_chip_data *gic)
/*
* Set all global interrupts to this CPU only.
*/
+ cpumask = readl_relaxed(base + GIC_DIST_TARGET + 0);
for (i = 32; i < gic_irqs; i += 4)
writel_relaxed(cpumask, base + GIC_DIST_TARGET + i * 4 / 4);
@@ -389,9 +393,25 @@ static void __cpuinit gic_cpu_init(struct gic_chip_data *gic)
{
void __iomem *dist_base = gic_data_dist_base(gic);
void __iomem *base = gic_data_cpu_base(gic);
+ unsigned int cpu_mask, cpu = smp_processor_id();
int i;
/*
+ * Get what the GIC says our CPU mask is.
+ */
+ BUG_ON(cpu >= NR_GIC_CPU_IF);
+ cpu_mask = readl_relaxed(dist_base + GIC_DIST_TARGET + 0);
+ gic_cpu_map[cpu] = cpu_mask;
+
+ /*
+ * Clear our mask from the other map entries in case they're
+ * still undefined.
+ */
+ for (i = 0; i < NR_GIC_CPU_IF; i++)
+ if (i != cpu)
+ gic_cpu_map[i] &= ~cpu_mask;
+
+ /*
* Deal with the banked PPI and SGI interrupts - disable all
* PPI interrupts, ensure all SGI interrupts are enabled.
*/
@@ -646,7 +666,7 @@ void __init gic_init_bases(unsigned int gic_nr, int irq_start,
{
irq_hw_number_t hwirq_base;
struct gic_chip_data *gic;
- int gic_irqs, irq_base;
+ int gic_irqs, irq_base, i;
BUG_ON(gic_nr >= MAX_GIC_NR);
@@ -683,6 +703,13 @@ void __init gic_init_bases(unsigned int gic_nr, int irq_start,
}
/*
+ * Initialize the CPU interface map to all CPUs.
+ * It will be refined as each CPU probes its ID.
+ */
+ for (i = 0; i < NR_GIC_CPU_IF; i++)
+ gic_cpu_map[i] = 0xff;
+
+ /*
* For primary GICs, skip over SGIs.
* For secondary GICs, skip over PPIs, too.
*/
@@ -737,7 +764,7 @@ void gic_raise_softirq(const struct cpumask *mask, unsigned int irq)
/* Convert our logical CPU mask into a physical one. */
for_each_cpu(cpu, mask)
- map |= 1 << cpu_logical_map(cpu);
+ map |= gic_cpu_map[cpu];
/*
* Ensure that stores to Normal memory are visible to the
diff --git a/arch/arm/include/asm/cputype.h b/arch/arm/include/asm/cputype.h
index cb47d28cbe1f..a59dcb5ab5fc 100644
--- a/arch/arm/include/asm/cputype.h
+++ b/arch/arm/include/asm/cputype.h
@@ -25,6 +25,19 @@
#define CPUID_EXT_ISAR4 "c2, 4"
#define CPUID_EXT_ISAR5 "c2, 5"
+#define MPIDR_SMP_BITMASK (0x3 << 30)
+#define MPIDR_SMP_VALUE (0x2 << 30)
+
+#define MPIDR_MT_BITMASK (0x1 << 24)
+
+#define MPIDR_HWID_BITMASK 0xFFFFFF
+
+#define MPIDR_LEVEL_BITS 8
+#define MPIDR_LEVEL_MASK ((1 << MPIDR_LEVEL_BITS) - 1)
+
+#define MPIDR_AFFINITY_LEVEL(mpidr, level) \
+ ((mpidr >> (MPIDR_LEVEL_BITS * level)) & MPIDR_LEVEL_MASK)
+
extern unsigned int processor_id;
#ifdef CONFIG_CPU_CP15
diff --git a/arch/arm/include/asm/prom.h b/arch/arm/include/asm/prom.h
index aeae9c609df4..8dd51dc1a367 100644
--- a/arch/arm/include/asm/prom.h
+++ b/arch/arm/include/asm/prom.h
@@ -15,6 +15,7 @@
extern struct machine_desc *setup_machine_fdt(unsigned int dt_phys);
extern void arm_dt_memblock_reserve(void);
+extern void __init arm_dt_init_cpu_maps(void);
#else /* CONFIG_OF */
@@ -24,6 +25,7 @@ static inline struct machine_desc *setup_machine_fdt(unsigned int dt_phys)
}
static inline void arm_dt_memblock_reserve(void) { }
+static inline void arm_dt_init_cpu_maps(void) { }
#endif /* CONFIG_OF */
#endif /* ASMARM_PROM_H */
diff --git a/arch/arm/include/asm/smp_plat.h b/arch/arm/include/asm/smp_plat.h
index 558d6c80aca9..aaa61b6f50ff 100644
--- a/arch/arm/include/asm/smp_plat.h
+++ b/arch/arm/include/asm/smp_plat.h
@@ -5,6 +5,9 @@
#ifndef __ASMARM_SMP_PLAT_H
#define __ASMARM_SMP_PLAT_H
+#include <linux/cpumask.h>
+#include <linux/err.h>
+
#include <asm/cputype.h>
/*
@@ -48,5 +51,19 @@ static inline int cache_ops_need_broadcast(void)
*/
extern int __cpu_logical_map[];
#define cpu_logical_map(cpu) __cpu_logical_map[cpu]
+/*
+ * Retrieve logical cpu index corresponding to a given MPIDR[23:0]
+ * - mpidr: MPIDR[23:0] to be used for the look-up
+ *
+ * Returns the cpu logical index or -EINVAL on look-up error
+ */
+static inline int get_logical_index(u32 mpidr)
+{
+ int cpu;
+ for (cpu = 0; cpu < nr_cpu_ids; cpu++)
+ if (cpu_logical_map(cpu) == mpidr)
+ return cpu;
+ return -EINVAL;
+}
#endif
diff --git a/arch/arm/kernel/devtree.c b/arch/arm/kernel/devtree.c
index bee7f9d47f02..aaf9add497fe 100644
--- a/arch/arm/kernel/devtree.c
+++ b/arch/arm/kernel/devtree.c
@@ -19,8 +19,10 @@
#include <linux/of_irq.h>
#include <linux/of_platform.h>
+#include <asm/cputype.h>
#include <asm/setup.h>
#include <asm/page.h>
+#include <asm/smp_plat.h>
#include <asm/mach/arch.h>
#include <asm/mach-types.h>
@@ -61,6 +63,104 @@ void __init arm_dt_memblock_reserve(void)
}
}
+/*
+ * arm_dt_init_cpu_maps - Function retrieves cpu nodes from the device tree
+ * and builds the cpu logical map array containing MPIDR values related to
+ * logical cpus
+ *
+ * Updates the cpu possible mask with the number of parsed cpu nodes
+ */
+void __init arm_dt_init_cpu_maps(void)
+{
+ /*
+ * Temp logical map is initialized with UINT_MAX values that are
+ * considered invalid logical map entries since the logical map must
+ * contain a list of MPIDR[23:0] values where MPIDR[31:24] must
+ * read as 0.
+ */
+ struct device_node *cpu, *cpus;
+ u32 i, j, cpuidx = 1;
+ u32 mpidr = is_smp() ? read_cpuid_mpidr() & MPIDR_HWID_BITMASK : 0;
+
+ u32 tmp_map[NR_CPUS] = { [0 ... NR_CPUS-1] = UINT_MAX };
+ bool bootcpu_valid = false;
+ cpus = of_find_node_by_path("/cpus");
+
+ if (!cpus)
+ return;
+
+ for_each_child_of_node(cpus, cpu) {
+ u32 hwid;
+
+ pr_debug(" * %s...\n", cpu->full_name);
+ /*
+ * A device tree containing CPU nodes with missing "reg"
+ * properties is considered invalid to build the
+ * cpu_logical_map.
+ */
+ if (of_property_read_u32(cpu, "reg", &hwid)) {
+ pr_debug(" * %s missing reg property\n",
+ cpu->full_name);
+ return;
+ }
+
+ /*
+ * 8 MSBs must be set to 0 in the DT since the reg property
+ * defines the MPIDR[23:0].
+ */
+ if (hwid & ~MPIDR_HWID_BITMASK)
+ return;
+
+ /*
+ * Duplicate MPIDRs are a recipe for disaster.
+ * Scan all initialized entries and check for
+ * duplicates. If any is found just bail out.
+ * temp values were initialized to UINT_MAX
+ * to avoid matching valid MPIDR[23:0] values.
+ */
+ for (j = 0; j < cpuidx; j++)
+ if (WARN(tmp_map[j] == hwid, "Duplicate /cpu reg "
+ "properties in the DT\n"))
+ return;
+
+ /*
+ * Build a stashed array of MPIDR values. Numbering scheme
+ * requires that if detected the boot CPU must be assigned
+ * logical id 0. Other CPUs get sequential indexes starting
+ * from 1. If a CPU node with a reg property matching the
+ * boot CPU MPIDR is detected, this is recorded so that the
+ * logical map built from DT is validated and can be used
+ * to override the map created in smp_setup_processor_id().
+ */
+ if (hwid == mpidr) {
+ i = 0;
+ bootcpu_valid = true;
+ } else {
+ i = cpuidx++;
+ }
+
+ tmp_map[i] = hwid;
+
+ if (cpuidx > nr_cpu_ids)
+ break;
+ }
+
+ if (WARN(!bootcpu_valid, "DT missing boot CPU MPIDR[23:0], "
+ "fall back to default cpu_logical_map\n"))
+ return;
+
+ /*
+ * Since the boot CPU node contains proper data, and all nodes have
+ * a reg property, the DT CPU list can be considered valid and the
+ * logical map created in smp_setup_processor_id() can be overridden
+ */
+ for (i = 0; i < cpuidx; i++) {
+ set_cpu_possible(i, true);
+ cpu_logical_map(i) = tmp_map[i];
+ pr_debug("cpu logical map 0x%x\n", cpu_logical_map(i));
+ }
+}
+
/**
* setup_machine_fdt - Machine setup when an dtb was passed to the kernel
* @dt_phys: physical address of dt blob
diff --git a/arch/arm/kernel/setup.c b/arch/arm/kernel/setup.c
index da1d1aa20ad9..d15f1c503f3d 100644
--- a/arch/arm/kernel/setup.c
+++ b/arch/arm/kernel/setup.c
@@ -426,13 +426,14 @@ int __cpu_logical_map[NR_CPUS];
void __init smp_setup_processor_id(void)
{
int i;
- u32 cpu = is_smp() ? read_cpuid_mpidr() & 0xff : 0;
+ u32 mpidr = is_smp() ? read_cpuid_mpidr() & MPIDR_HWID_BITMASK : 0;
+ u32 cpu = MPIDR_AFFINITY_LEVEL(mpidr, 0);
cpu_logical_map(0) = cpu;
- for (i = 1; i < NR_CPUS; ++i)
+ for (i = 1; i < nr_cpu_ids; ++i)
cpu_logical_map(i) = i == cpu ? 0 : i;
- printk(KERN_INFO "Booting Linux on physical CPU %d\n", cpu);
+ printk(KERN_INFO "Booting Linux on physical CPU 0x%x\n", mpidr);
}
static void __init setup_processor(void)
@@ -758,6 +759,7 @@ void __init setup_arch(char **cmdline_p)
unflatten_device_tree();
+ arm_dt_init_cpu_maps();
#ifdef CONFIG_SMP
if (is_smp()) {
smp_set_ops(mdesc->smp);
diff --git a/arch/arm/kernel/topology.c b/arch/arm/kernel/topology.c
index 26c12c6440fc..79282ebcd939 100644
--- a/arch/arm/kernel/topology.c
+++ b/arch/arm/kernel/topology.c
@@ -196,32 +196,7 @@ static inline void parse_dt_topology(void) {}
static inline void update_cpu_power(unsigned int cpuid, unsigned int mpidr) {}
#endif
-
-/*
- * cpu topology management
- */
-
-#define MPIDR_SMP_BITMASK (0x3 << 30)
-#define MPIDR_SMP_VALUE (0x2 << 30)
-
-#define MPIDR_MT_BITMASK (0x1 << 24)
-
-/*
- * These masks reflect the current use of the affinity levels.
- * The affinity level can be up to 16 bits according to ARM ARM
- */
-#define MPIDR_HWID_BITMASK 0xFFFFFF
-
-#define MPIDR_LEVEL0_MASK 0x3
-#define MPIDR_LEVEL0_SHIFT 0
-
-#define MPIDR_LEVEL1_MASK 0xF
-#define MPIDR_LEVEL1_SHIFT 8
-
-#define MPIDR_LEVEL2_MASK 0xFF
-#define MPIDR_LEVEL2_SHIFT 16
-
-/*
+ /*
* cpu topology table
*/
struct cputopo_arm cpu_topology[NR_CPUS];
@@ -282,19 +257,14 @@ void store_cpu_topology(unsigned int cpuid)
if (mpidr & MPIDR_MT_BITMASK) {
/* core performance interdependency */
- cpuid_topo->thread_id = (mpidr >> MPIDR_LEVEL0_SHIFT)
- & MPIDR_LEVEL0_MASK;
- cpuid_topo->core_id = (mpidr >> MPIDR_LEVEL1_SHIFT)
- & MPIDR_LEVEL1_MASK;
- cpuid_topo->socket_id = (mpidr >> MPIDR_LEVEL2_SHIFT)
- & MPIDR_LEVEL2_MASK;
+ cpuid_topo->thread_id = MPIDR_AFFINITY_LEVEL(mpidr, 0);
+ cpuid_topo->core_id = MPIDR_AFFINITY_LEVEL(mpidr, 1);
+ cpuid_topo->socket_id = MPIDR_AFFINITY_LEVEL(mpidr, 2);
} else {
/* largely independent cores */
cpuid_topo->thread_id = -1;
- cpuid_topo->core_id = (mpidr >> MPIDR_LEVEL0_SHIFT)
- & MPIDR_LEVEL0_MASK;
- cpuid_topo->socket_id = (mpidr >> MPIDR_LEVEL1_SHIFT)
- & MPIDR_LEVEL1_MASK;
+ cpuid_topo->core_id = MPIDR_AFFINITY_LEVEL(mpidr, 0);
+ cpuid_topo->socket_id = MPIDR_AFFINITY_LEVEL(mpidr, 1);
}
} else {
/*