Commit 0f078336 authored by Lorenzo Pieralisi's avatar Lorenzo Pieralisi Committed by Catalin Marinas

ARM64: kernel: unify ACPI and DT cpus initialization

The code that initializes cpus on arm64 is currently split in two
different code paths that carry out DT and ACPI cpus initialization.

Most of the code executing SMP initialization is common and should
be merged to reduce discrepancies between ACPI and DT initialization
and to have code initializing cpus in a single common place in the
kernel.

This patch refactors arm64 SMP cpus initialization code to merge
ACPI and DT boot paths in a common file and to create sanity
checks that can be reused by both boot methods.

Current code assumes PSCI is the only available boot method
when arm64 boots with ACPI; this can be easily extended if/when
the ACPI parking protocol is merged into the kernel.
Signed-off-by: default avatarSudeep Holla <sudeep.holla@arm.com>
Signed-off-by: default avatarLorenzo Pieralisi <lorenzo.pieralisi@arm.com>
Acked-by: default avatarHanjun Guo <hanjun.guo@linaro.org>
Acked-by: default avatarMark Rutland <mark.rutland@arm.com>
Tested-by: default avatarHanjun Guo <hanjun.guo@linaro.org>
Tested-by: Mark Rutland <mark.rutland@arm.com> [DT]
Cc: Will Deacon <will.deacon@arm.com>
Signed-off-by: default avatarCatalin Marinas <catalin.marinas@arm.com>
parent 819a8826
......@@ -93,4 +93,8 @@ static inline bool acpi_psci_use_hvc(void) { return false; }
static inline void acpi_init_cpus(void) { }
#endif /* CONFIG_ACPI */
static inline const char *acpi_get_enable_method(int cpu)
{
return acpi_psci_present() ? "psci" : NULL;
}
#endif /*_ASM_ACPI_H*/
......@@ -65,7 +65,6 @@ struct cpu_operations {
extern const struct cpu_operations *cpu_ops[NR_CPUS];
int __init cpu_read_ops(int cpu);
const struct cpu_operations *cpu_get_ops(const char *name);
static inline void __init cpu_read_bootcpu_ops(void)
{
......
......@@ -42,7 +42,7 @@ extern void handle_IPI(int ipinr, struct pt_regs *regs);
* Discover the set of possible CPUs and determine their
* SMP operations.
*/
extern void of_smp_init_cpus(void);
extern void smp_init_cpus(void);
/*
* Provide a function to raise an IPI cross call on CPUs in callmap.
......
......@@ -36,12 +36,6 @@ EXPORT_SYMBOL(acpi_disabled);
int acpi_pci_disabled = 1; /* skip ACPI PCI scan and IRQ initialization */
EXPORT_SYMBOL(acpi_pci_disabled);
/* Processors with enabled flag and sane MPIDR */
static int enabled_cpus;
/* Boot CPU is valid or not in MADT */
static bool bootcpu_valid __initdata;
static bool param_acpi_off __initdata;
static bool param_acpi_force __initdata;
......@@ -95,124 +89,6 @@ void __init __acpi_unmap_table(char *map, unsigned long size)
early_memunmap(map, size);
}
/**
* acpi_map_gic_cpu_interface - generates a logical cpu number
* and map to MPIDR represented by GICC structure
*/
static void __init
acpi_map_gic_cpu_interface(struct acpi_madt_generic_interrupt *processor)
{
int i;
u64 mpidr = processor->arm_mpidr & MPIDR_HWID_BITMASK;
bool enabled = !!(processor->flags & ACPI_MADT_ENABLED);
if (mpidr == INVALID_HWID) {
pr_info("Skip MADT cpu entry with invalid MPIDR\n");
return;
}
total_cpus++;
if (!enabled)
return;
if (enabled_cpus >= NR_CPUS) {
pr_warn("NR_CPUS limit of %d reached, Processor %d/0x%llx ignored.\n",
NR_CPUS, total_cpus, mpidr);
return;
}
/* Check if GICC structure of boot CPU is available in the MADT */
if (cpu_logical_map(0) == mpidr) {
if (bootcpu_valid) {
pr_err("Firmware bug, duplicate CPU MPIDR: 0x%llx in MADT\n",
mpidr);
return;
}
bootcpu_valid = true;
}
/*
* Duplicate MPIDRs are a recipe for disaster. Scan
* all initialized entries and check for
* duplicates. If any is found just ignore the CPU.
*/
for (i = 1; i < enabled_cpus; i++) {
if (cpu_logical_map(i) == mpidr) {
pr_err("Firmware bug, duplicate CPU MPIDR: 0x%llx in MADT\n",
mpidr);
return;
}
}
if (!acpi_psci_present())
return;
cpu_ops[enabled_cpus] = cpu_get_ops("psci");
/* CPU 0 was already initialized */
if (enabled_cpus) {
if (!cpu_ops[enabled_cpus])
return;
if (cpu_ops[enabled_cpus]->cpu_init(enabled_cpus))
return;
/* map the logical cpu id to cpu MPIDR */
cpu_logical_map(enabled_cpus) = mpidr;
}
enabled_cpus++;
}
static int __init
acpi_parse_gic_cpu_interface(struct acpi_subtable_header *header,
const unsigned long end)
{
struct acpi_madt_generic_interrupt *processor;
processor = (struct acpi_madt_generic_interrupt *)header;
if (BAD_MADT_ENTRY(processor, end))
return -EINVAL;
acpi_table_print_madt_entry(header);
acpi_map_gic_cpu_interface(processor);
return 0;
}
/* Parse GIC cpu interface entries in MADT for SMP init */
void __init acpi_init_cpus(void)
{
int count, i;
/*
* do a partial walk of MADT to determine how many CPUs
* we have including disabled CPUs, and get information
* we need for SMP init
*/
count = acpi_table_parse_madt(ACPI_MADT_TYPE_GENERIC_INTERRUPT,
acpi_parse_gic_cpu_interface, 0);
if (!count) {
pr_err("No GIC CPU interface entries present\n");
return;
} else if (count < 0) {
pr_err("Error parsing GIC CPU interface entry\n");
return;
}
if (!bootcpu_valid) {
pr_err("MADT missing boot CPU MPIDR, not enabling secondaries\n");
return;
}
for (i = 0; i < enabled_cpus; i++)
set_cpu_possible(i, true);
/* Make boot-up look pretty */
pr_info("%d CPUs enabled, %d CPUs total\n", enabled_cpus, total_cpus);
}
/*
* acpi_fadt_sanity_check() - Check FADT presence and carry out sanity
* checks on it
......
......@@ -16,11 +16,13 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <asm/cpu_ops.h>
#include <asm/smp_plat.h>
#include <linux/acpi.h>
#include <linux/errno.h>
#include <linux/of.h>
#include <linux/string.h>
#include <asm/acpi.h>
#include <asm/cpu_ops.h>
#include <asm/smp_plat.h>
extern const struct cpu_operations smp_spin_table_ops;
extern const struct cpu_operations cpu_psci_ops;
......@@ -35,7 +37,7 @@ static const struct cpu_operations *supported_cpu_ops[] __initconst = {
NULL,
};
const struct cpu_operations * __init cpu_get_ops(const char *name)
static const struct cpu_operations * __init cpu_get_ops(const char *name)
{
const struct cpu_operations **ops = supported_cpu_ops;
......@@ -49,36 +51,51 @@ const struct cpu_operations * __init cpu_get_ops(const char *name)
return NULL;
}
static const char *__init cpu_read_enable_method(int cpu)
{
const char *enable_method;
if (acpi_disabled) {
struct device_node *dn = of_get_cpu_node(cpu, NULL);
if (!dn) {
if (!cpu)
pr_err("Failed to find device node for boot cpu\n");
return NULL;
}
enable_method = of_get_property(dn, "enable-method", NULL);
if (!enable_method) {
/*
* The boot CPU may not have an enable method (e.g.
* when spin-table is used for secondaries).
* Don't warn spuriously.
*/
if (cpu != 0)
pr_err("%s: missing enable-method property\n",
dn->full_name);
}
} else {
enable_method = acpi_get_enable_method(cpu);
if (!enable_method)
pr_err("Unsupported ACPI enable-method\n");
}
return enable_method;
}
/*
* Read a cpu's enable method from the device tree and record it in cpu_ops.
* Read a cpu's enable method and record it in cpu_ops.
*/
int __init cpu_read_ops(int cpu)
{
const char *enable_method;
struct device_node *dn = of_get_cpu_node(cpu, NULL);
const char *enable_method = cpu_read_enable_method(cpu);
if (!dn) {
if (!cpu)
pr_err("Failed to find device node for boot cpu\n");
if (!enable_method)
return -ENODEV;
}
enable_method = of_get_property(dn, "enable-method", NULL);
if (!enable_method) {
/*
* The boot CPU may not have an enable method (e.g. when
* spin-table is used for secondaries). Don't warn spuriously.
*/
if (cpu != 0)
pr_err("%s: missing enable-method property\n",
dn->full_name);
return -ENOENT;
}
cpu_ops[cpu] = cpu_get_ops(enable_method);
if (!cpu_ops[cpu]) {
pr_warn("%s: unsupported enable-method property: %s\n",
dn->full_name, enable_method);
pr_warn("Unsupported enable-method: %s\n", enable_method);
return -EOPNOTSUPP;
}
......
......@@ -408,16 +408,13 @@ void __init setup_arch(char **cmdline_p)
if (acpi_disabled) {
unflatten_device_tree();
psci_dt_init();
cpu_read_bootcpu_ops();
#ifdef CONFIG_SMP
of_smp_init_cpus();
#endif
} else {
psci_acpi_init();
acpi_init_cpus();
}
cpu_read_bootcpu_ops();
#ifdef CONFIG_SMP
smp_init_cpus();
smp_build_mpidr_hash();
#endif
......
......@@ -17,6 +17,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/acpi.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/spinlock.h>
......@@ -318,6 +319,49 @@ void __init smp_prepare_boot_cpu(void)
set_my_cpu_offset(per_cpu_offset(smp_processor_id()));
}
static u64 __init of_get_cpu_mpidr(struct device_node *dn)
{
const __be32 *cell;
u64 hwid;
/*
* A cpu node with missing "reg" property is
* considered invalid to build a cpu_logical_map
* entry.
*/
cell = of_get_property(dn, "reg", NULL);
if (!cell) {
pr_err("%s: missing reg property\n", dn->full_name);
return INVALID_HWID;
}
hwid = of_read_number(cell, of_n_addr_cells(dn));
/*
* Non affinity bits must be set to 0 in the DT
*/
if (hwid & ~MPIDR_HWID_BITMASK) {
pr_err("%s: invalid reg property\n", dn->full_name);
return INVALID_HWID;
}
return hwid;
}
/*
* Duplicate MPIDRs are a recipe for disaster. Scan all initialized
* entries and check for duplicates. If any is found just ignore the
* cpu. cpu_logical_map was initialized to INVALID_HWID to avoid
* matching valid MPIDR values.
*/
static bool __init is_mpidr_duplicate(unsigned int cpu, u64 hwid)
{
unsigned int i;
for (i = 1; (i < cpu) && (i < NR_CPUS); i++)
if (cpu_logical_map(i) == hwid)
return true;
return false;
}
/*
* Initialize cpu operations for a logical cpu and
* set it in the possible mask on success
......@@ -335,56 +379,97 @@ static int __init smp_cpu_setup(int cpu)
return 0;
}
static bool bootcpu_valid __initdata;
static unsigned int cpu_count = 1;
#ifdef CONFIG_ACPI
/*
* acpi_map_gic_cpu_interface - parse processor MADT entry
*
* Carry out sanity checks on MADT processor entry and initialize
* cpu_logical_map on success
*/
static void __init
acpi_map_gic_cpu_interface(struct acpi_madt_generic_interrupt *processor)
{
u64 hwid = processor->arm_mpidr;
if (hwid & ~MPIDR_HWID_BITMASK || hwid == INVALID_HWID) {
pr_err("skipping CPU entry with invalid MPIDR 0x%llx\n", hwid);
return;
}
if (!(processor->flags & ACPI_MADT_ENABLED)) {
pr_err("skipping disabled CPU entry with 0x%llx MPIDR\n", hwid);
return;
}
if (is_mpidr_duplicate(cpu_count, hwid)) {
pr_err("duplicate CPU MPIDR 0x%llx in MADT\n", hwid);
return;
}
/* Check if GICC structure of boot CPU is available in the MADT */
if (cpu_logical_map(0) == hwid) {
if (bootcpu_valid) {
pr_err("duplicate boot CPU MPIDR: 0x%llx in MADT\n",
hwid);
return;
}
bootcpu_valid = true;
return;
}
if (cpu_count >= NR_CPUS)
return;
/* map the logical cpu id to cpu MPIDR */
cpu_logical_map(cpu_count) = hwid;
cpu_count++;
}
static int __init
acpi_parse_gic_cpu_interface(struct acpi_subtable_header *header,
const unsigned long end)
{
struct acpi_madt_generic_interrupt *processor;
processor = (struct acpi_madt_generic_interrupt *)header;
if (BAD_MADT_ENTRY(processor, end))
return -EINVAL;
acpi_table_print_madt_entry(header);
acpi_map_gic_cpu_interface(processor);
return 0;
}
#else
#define acpi_table_parse_madt(...) do { } while (0)
#endif
/*
* Enumerate the possible CPU set from the device tree and build the
* cpu logical map array containing MPIDR values related to logical
* cpus. Assumes that cpu_logical_map(0) has already been initialized.
*/
void __init of_smp_init_cpus(void)
void __init of_parse_and_init_cpus(void)
{
struct device_node *dn = NULL;
unsigned int i, cpu = 1;
bool bootcpu_valid = false;
while ((dn = of_find_node_by_type(dn, "cpu"))) {
const u32 *cell;
u64 hwid;
u64 hwid = of_get_cpu_mpidr(dn);
/*
* A cpu node with missing "reg" property is
* considered invalid to build a cpu_logical_map
* entry.
*/
cell = of_get_property(dn, "reg", NULL);
if (!cell) {
pr_err("%s: missing reg property\n", dn->full_name);
if (hwid == INVALID_HWID)
goto next;
}
hwid = of_read_number(cell, of_n_addr_cells(dn));
/*
* Non affinity bits must be set to 0 in the DT
*/
if (hwid & ~MPIDR_HWID_BITMASK) {
pr_err("%s: invalid reg property\n", dn->full_name);
if (is_mpidr_duplicate(cpu_count, hwid)) {
pr_err("%s: duplicate cpu reg properties in the DT\n",
dn->full_name);
goto next;
}
/*
* Duplicate MPIDRs are a recipe for disaster. Scan
* all initialized entries and check for
* duplicates. If any is found just ignore the cpu.
* cpu_logical_map was initialized to INVALID_HWID to
* avoid matching valid MPIDR values.
*/
for (i = 1; (i < cpu) && (i < NR_CPUS); i++) {
if (cpu_logical_map(i) == hwid) {
pr_err("%s: duplicate cpu reg properties in the DT\n",
dn->full_name);
goto next;
}
}
/*
* The numbering scheme requires that the boot CPU
* must be assigned logical id 0. Record it so that
......@@ -409,22 +494,42 @@ void __init of_smp_init_cpus(void)
continue;
}
if (cpu >= NR_CPUS)
if (cpu_count >= NR_CPUS)
goto next;
pr_debug("cpu logical map 0x%llx\n", hwid);
cpu_logical_map(cpu) = hwid;
cpu_logical_map(cpu_count) = hwid;
next:
cpu++;
cpu_count++;
}
}
/*
* Enumerate the possible CPU set from the device tree or ACPI and build the
* cpu logical map array containing MPIDR values related to logical
* cpus. Assumes that cpu_logical_map(0) has already been initialized.
*/
void __init smp_init_cpus(void)
{
int i;
if (acpi_disabled)
of_parse_and_init_cpus();
else
/*
* do a walk of MADT to determine how many CPUs
* we have including disabled CPUs, and get information
* we need for SMP init
*/
acpi_table_parse_madt(ACPI_MADT_TYPE_GENERIC_INTERRUPT,
acpi_parse_gic_cpu_interface, 0);
/* sanity check */
if (cpu > NR_CPUS)
pr_warning("no. of cores (%d) greater than configured maximum of %d - clipping\n",
cpu, NR_CPUS);
if (cpu_count > NR_CPUS)
pr_warn("no. of cores (%d) greater than configured maximum of %d - clipping\n",
cpu_count, NR_CPUS);
if (!bootcpu_valid) {
pr_err("DT missing boot CPU MPIDR, not enabling secondaries\n");
pr_err("missing boot CPU MPIDR, not enabling secondaries\n");
return;
}
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment