arm64: SMP: rework the SMP code to be enabling method agnostic

In order to introduce PSCI support, let the SMP code handle
multiple enabling methods. This also allow CPUs to be booted
using different methods (though this feels a bit weird...).

In the process, move the spin-table code to its own file.

Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
This commit is contained in:
Marc Zyngier 2013-01-02 15:24:22 +00:00 committed by Catalin Marinas
parent 75e424620a
commit d329de3f2a
4 changed files with 123 additions and 23 deletions

View File

@ -66,4 +66,14 @@ extern volatile unsigned long secondary_holding_pen_release;
extern void arch_send_call_function_single_ipi(int cpu);
extern void arch_send_call_function_ipi_mask(const struct cpumask *mask);
struct device_node;
struct smp_enable_ops {
const char *name;
int (*init_cpu)(struct device_node *, int);
int (*prepare_cpu)(int);
};
extern const struct smp_enable_ops smp_spin_table_ops;
#endif /* ifndef __ASM_SMP_H */

View File

@ -14,7 +14,7 @@ arm64-obj-y := cputable.o debug-monitors.o entry.o irq.o fpsimd.o \
arm64-obj-$(CONFIG_COMPAT) += sys32.o kuser32.o signal32.o \
sys_compat.o
arm64-obj-$(CONFIG_MODULES) += arm64ksyms.o module.o
arm64-obj-$(CONFIG_SMP) += smp.o
arm64-obj-$(CONFIG_SMP) += smp.o smp_spin_table.o
arm64-obj-$(CONFIG_HW_PERF_EVENTS) += perf_event.o
arm64-obj-$(CONFIG_HAVE_HW_BREAKPOINT)+= hw_breakpoint.o
arm64-obj-$(CONFIG_EARLY_PRINTK) += early_printk.o

View File

@ -233,7 +233,27 @@ void __init smp_prepare_boot_cpu(void)
}
static void (*smp_cross_call)(const struct cpumask *, unsigned int);
static phys_addr_t cpu_release_addr[NR_CPUS];
static const struct smp_enable_ops *enable_ops[] __initconst = {
&smp_spin_table_ops,
NULL,
};
static const struct smp_enable_ops *smp_enable_ops[NR_CPUS];
static const struct smp_enable_ops * __init smp_get_enable_ops(const char *name)
{
const struct smp_enable_ops *ops = enable_ops[0];
while (ops) {
if (!strcmp(name, ops->name))
return ops;
ops++;
}
return NULL;
}
/*
* Enumerate the possible CPU set from the device tree.
@ -252,21 +272,21 @@ void __init smp_init_cpus(void)
* We currently support only the "spin-table" enable-method.
*/
enable_method = of_get_property(dn, "enable-method", NULL);
if (!enable_method || strcmp(enable_method, "spin-table")) {
pr_err("CPU %d: missing or invalid enable-method property: %s\n",
if (!enable_method) {
pr_err("CPU %d: missing enable-method property\n", cpu);
goto next;
}
smp_enable_ops[cpu] = smp_get_enable_ops(enable_method);
if (!smp_enable_ops[cpu]) {
pr_err("CPU %d: invalid enable-method property: %s\n",
cpu, enable_method);
goto next;
}
/*
* Determine the address from which the CPU is polling.
*/
if (of_property_read_u64(dn, "cpu-release-addr",
&cpu_release_addr[cpu])) {
pr_err("CPU %d: missing or invalid cpu-release-addr property\n",
cpu);
if (smp_enable_ops[cpu]->init_cpu(dn, cpu))
goto next;
}
set_cpu_possible(cpu, true);
next:
@ -281,8 +301,7 @@ void __init smp_init_cpus(void)
void __init smp_prepare_cpus(unsigned int max_cpus)
{
int cpu;
void **release_addr;
int cpu, err;
unsigned int ncores = num_possible_cpus();
/*
@ -291,30 +310,35 @@ void __init smp_prepare_cpus(unsigned int max_cpus)
if (max_cpus > ncores)
max_cpus = ncores;
/* Don't bother if we're effectively UP */
if (max_cpus <= 1)
return;
/*
* Initialise the present map (which describes the set of CPUs
* actually populated at the present time) and release the
* secondaries from the bootloader.
*
* Make sure we online at most (max_cpus - 1) additional CPUs.
*/
max_cpus--;
for_each_possible_cpu(cpu) {
if (max_cpus == 0)
break;
if (!cpu_release_addr[cpu])
if (cpu == smp_processor_id())
continue;
release_addr = __va(cpu_release_addr[cpu]);
release_addr[0] = (void *)__pa(secondary_holding_pen);
__flush_dcache_area(release_addr, sizeof(release_addr[0]));
if (!smp_enable_ops[cpu])
continue;
err = smp_enable_ops[cpu]->prepare_cpu(cpu);
if (err)
continue;
set_cpu_present(cpu, true);
max_cpus--;
}
/*
* Send an event to wake up the secondaries.
*/
sev();
}

View File

@ -0,0 +1,66 @@
/*
* Spin Table SMP initialisation
*
* Copyright (C) 2013 ARM Ltd.
*
* 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 in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; 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/init.h>
#include <linux/of.h>
#include <linux/smp.h>
#include <asm/cacheflush.h>
static phys_addr_t cpu_release_addr[NR_CPUS];
static int __init smp_spin_table_init_cpu(struct device_node *dn, int cpu)
{
/*
* Determine the address from which the CPU is polling.
*/
if (of_property_read_u64(dn, "cpu-release-addr",
&cpu_release_addr[cpu])) {
pr_err("CPU %d: missing or invalid cpu-release-addr property\n",
cpu);
return -1;
}
return 0;
}
static int __init smp_spin_table_prepare_cpu(int cpu)
{
void **release_addr;
if (!cpu_release_addr[cpu])
return -ENODEV;
release_addr = __va(cpu_release_addr[cpu]);
release_addr[0] = (void *)__pa(secondary_holding_pen);
__flush_dcache_area(release_addr, sizeof(release_addr[0]));
/*
* Send an event to wake up the secondary CPU.
*/
sev();
return 0;
}
const struct smp_enable_ops smp_spin_table_ops __initconst = {
.name = "spin-table",
.init_cpu = smp_spin_table_init_cpu,
.prepare_cpu = smp_spin_table_prepare_cpu,
};