Commit 819a8826 authored by Lorenzo Pieralisi's avatar Lorenzo Pieralisi Committed by Catalin Marinas

ARM64: kernel: make cpu_ops hooks DT agnostic

ARM64 CPU operations such as cpu_init and cpu_init_idle take
a struct device_node pointer as a parameter, which corresponds to
the device tree node of the logical cpu on which the operation
has to be applied.

With the advent of ACPI on arm64, where MADT static table entries
are used to initialize cpus, the device tree node parameter
in cpu_ops hooks become useless when booting with ACPI, since
in that case cpu device tree nodes are not present and can not be
used for cpu initialization.

The current cpu_init hook requires a struct device_node pointer
parameter because it is called while parsing the device tree to
initialize CPUs, when the cpu_logical_map (that is used to match
a cpu node reg property to a device tree node) for a given logical
cpu id is not set up yet. This means that the cpu_init hook cannot
rely on the of_get_cpu_node function to retrieve the device tree
node corresponding to the logical cpu id passed in as parameter,
so the cpu device tree node must be passed in as a parameter to fix
this catch-22 dependency cycle.

This patch reshuffles the cpu_logical_map initialization code so
that the cpu_init cpu_ops hook can safely use the of_get_cpu_node
function to retrieve the cpu device tree node, removing the need for
the device tree node pointer parameter.

In the process, the patch removes device tree node parameters
from all cpu_ops hooks, in preparation for SMP DT/ACPI cpus
initialization consolidation.
Signed-off-by: default avatarLorenzo Pieralisi <lorenzo.pieralisi@arm.com>
Acked-by: default avatarHanjun Guo <hanjun.guo@linaro.org>
Acked-by: default avatarSudeep Holla <sudeep.holla@arm.com>
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 e2608180
...@@ -19,15 +19,15 @@ ...@@ -19,15 +19,15 @@
#include <linux/init.h> #include <linux/init.h>
#include <linux/threads.h> #include <linux/threads.h>
struct device_node;
/** /**
* struct cpu_operations - Callback operations for hotplugging CPUs. * struct cpu_operations - Callback operations for hotplugging CPUs.
* *
* @name: Name of the property as appears in a devicetree cpu node's * @name: Name of the property as appears in a devicetree cpu node's
* enable-method property. * enable-method property. On systems booting with ACPI, @name
* @cpu_init: Reads any data necessary for a specific enable-method from the * identifies the struct cpu_operations entry corresponding to
* devicetree, for a given cpu node and proposed logical id. * the boot protocol specified in the ACPI MADT table.
* @cpu_init: Reads any data necessary for a specific enable-method for a
* proposed logical id.
* @cpu_prepare: Early one-time preparation step for a cpu. If there is a * @cpu_prepare: Early one-time preparation step for a cpu. If there is a
* mechanism for doing so, tests whether it is possible to boot * mechanism for doing so, tests whether it is possible to boot
* the given CPU. * the given CPU.
...@@ -40,15 +40,15 @@ struct device_node; ...@@ -40,15 +40,15 @@ struct device_node;
* @cpu_die: Makes a cpu leave the kernel. Must not fail. Called from the * @cpu_die: Makes a cpu leave the kernel. Must not fail. Called from the
* cpu being killed. * cpu being killed.
* @cpu_kill: Ensures a cpu has left the kernel. Called from another cpu. * @cpu_kill: Ensures a cpu has left the kernel. Called from another cpu.
* @cpu_init_idle: Reads any data necessary to initialize CPU idle states from * @cpu_init_idle: Reads any data necessary to initialize CPU idle states for
* devicetree, for a given cpu node and proposed logical id. * a proposed logical id.
* @cpu_suspend: Suspends a cpu and saves the required context. May fail owing * @cpu_suspend: Suspends a cpu and saves the required context. May fail owing
* to wrong parameters or error conditions. Called from the * to wrong parameters or error conditions. Called from the
* CPU being suspended. Must be called with IRQs disabled. * CPU being suspended. Must be called with IRQs disabled.
*/ */
struct cpu_operations { struct cpu_operations {
const char *name; const char *name;
int (*cpu_init)(struct device_node *, unsigned int); int (*cpu_init)(unsigned int);
int (*cpu_prepare)(unsigned int); int (*cpu_prepare)(unsigned int);
int (*cpu_boot)(unsigned int); int (*cpu_boot)(unsigned int);
void (*cpu_postboot)(void); void (*cpu_postboot)(void);
...@@ -58,14 +58,18 @@ struct cpu_operations { ...@@ -58,14 +58,18 @@ struct cpu_operations {
int (*cpu_kill)(unsigned int cpu); int (*cpu_kill)(unsigned int cpu);
#endif #endif
#ifdef CONFIG_CPU_IDLE #ifdef CONFIG_CPU_IDLE
int (*cpu_init_idle)(struct device_node *, unsigned int); int (*cpu_init_idle)(unsigned int);
int (*cpu_suspend)(unsigned long); int (*cpu_suspend)(unsigned long);
#endif #endif
}; };
extern const struct cpu_operations *cpu_ops[NR_CPUS]; extern const struct cpu_operations *cpu_ops[NR_CPUS];
int __init cpu_read_ops(struct device_node *dn, int cpu); int __init cpu_read_ops(int cpu);
void __init cpu_read_bootcpu_ops(void);
const struct cpu_operations *cpu_get_ops(const char *name); const struct cpu_operations *cpu_get_ops(const char *name);
static inline void __init cpu_read_bootcpu_ops(void)
{
cpu_read_ops(0);
}
#endif /* ifndef __ASM_CPU_OPS_H */ #endif /* ifndef __ASM_CPU_OPS_H */
...@@ -154,7 +154,7 @@ acpi_map_gic_cpu_interface(struct acpi_madt_generic_interrupt *processor) ...@@ -154,7 +154,7 @@ acpi_map_gic_cpu_interface(struct acpi_madt_generic_interrupt *processor)
if (!cpu_ops[enabled_cpus]) if (!cpu_ops[enabled_cpus])
return; return;
if (cpu_ops[enabled_cpus]->cpu_init(NULL, enabled_cpus)) if (cpu_ops[enabled_cpus]->cpu_init(enabled_cpus))
return; return;
/* map the logical cpu id to cpu MPIDR */ /* map the logical cpu id to cpu MPIDR */
......
...@@ -52,9 +52,18 @@ const struct cpu_operations * __init cpu_get_ops(const char *name) ...@@ -52,9 +52,18 @@ const struct cpu_operations * __init cpu_get_ops(const char *name)
/* /*
* Read a cpu's enable method from the device tree and record it in cpu_ops. * Read a cpu's enable method from the device tree and record it in cpu_ops.
*/ */
int __init cpu_read_ops(struct device_node *dn, int cpu) int __init cpu_read_ops(int cpu)
{ {
const char *enable_method = of_get_property(dn, "enable-method", NULL); const char *enable_method;
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 -ENODEV;
}
enable_method = of_get_property(dn, "enable-method", NULL);
if (!enable_method) { if (!enable_method) {
/* /*
* The boot CPU may not have an enable method (e.g. when * The boot CPU may not have an enable method (e.g. when
...@@ -75,13 +84,3 @@ int __init cpu_read_ops(struct device_node *dn, int cpu) ...@@ -75,13 +84,3 @@ int __init cpu_read_ops(struct device_node *dn, int cpu)
return 0; return 0;
} }
void __init cpu_read_bootcpu_ops(void)
{
struct device_node *dn = of_get_cpu_node(0, NULL);
if (!dn) {
pr_err("Failed to find device node for boot cpu\n");
return;
}
cpu_read_ops(dn, 0);
}
...@@ -18,15 +18,10 @@ ...@@ -18,15 +18,10 @@
int arm_cpuidle_init(unsigned int cpu) int arm_cpuidle_init(unsigned int cpu)
{ {
int ret = -EOPNOTSUPP; int ret = -EOPNOTSUPP;
struct device_node *cpu_node = of_cpu_device_node_get(cpu);
if (!cpu_node)
return -ENODEV;
if (cpu_ops[cpu] && cpu_ops[cpu]->cpu_init_idle) if (cpu_ops[cpu] && cpu_ops[cpu]->cpu_init_idle)
ret = cpu_ops[cpu]->cpu_init_idle(cpu_node, cpu); ret = cpu_ops[cpu]->cpu_init_idle(cpu);
of_node_put(cpu_node);
return ret; return ret;
} }
......
...@@ -186,12 +186,15 @@ static int psci_migrate_info_type(void) ...@@ -186,12 +186,15 @@ static int psci_migrate_info_type(void)
return err; return err;
} }
static int __maybe_unused cpu_psci_cpu_init_idle(struct device_node *cpu_node, static int __maybe_unused cpu_psci_cpu_init_idle(unsigned int cpu)
unsigned int cpu)
{ {
int i, ret, count = 0; int i, ret, count = 0;
struct psci_power_state *psci_states; struct psci_power_state *psci_states;
struct device_node *state_node; struct device_node *state_node, *cpu_node;
cpu_node = of_get_cpu_node(cpu, NULL);
if (!cpu_node)
return -ENODEV;
/* /*
* If the PSCI cpu_suspend function hook has not been initialized * If the PSCI cpu_suspend function hook has not been initialized
...@@ -444,7 +447,7 @@ int __init psci_acpi_init(void) ...@@ -444,7 +447,7 @@ int __init psci_acpi_init(void)
#ifdef CONFIG_SMP #ifdef CONFIG_SMP
static int __init cpu_psci_cpu_init(struct device_node *dn, unsigned int cpu) static int __init cpu_psci_cpu_init(unsigned int cpu)
{ {
return 0; return 0;
} }
......
...@@ -318,6 +318,23 @@ void __init smp_prepare_boot_cpu(void) ...@@ -318,6 +318,23 @@ void __init smp_prepare_boot_cpu(void)
set_my_cpu_offset(per_cpu_offset(smp_processor_id())); set_my_cpu_offset(per_cpu_offset(smp_processor_id()));
} }
/*
* Initialize cpu operations for a logical cpu and
* set it in the possible mask on success
*/
static int __init smp_cpu_setup(int cpu)
{
if (cpu_read_ops(cpu))
return -ENODEV;
if (cpu_ops[cpu]->cpu_init(cpu))
return -ENODEV;
set_cpu_possible(cpu, true);
return 0;
}
/* /*
* Enumerate the possible CPU set from the device tree and build the * Enumerate the possible CPU set from the device tree and build the
* cpu logical map array containing MPIDR values related to logical * cpu logical map array containing MPIDR values related to logical
...@@ -395,12 +412,6 @@ void __init of_smp_init_cpus(void) ...@@ -395,12 +412,6 @@ void __init of_smp_init_cpus(void)
if (cpu >= NR_CPUS) if (cpu >= NR_CPUS)
goto next; goto next;
if (cpu_read_ops(dn, cpu) != 0)
goto next;
if (cpu_ops[cpu]->cpu_init(dn, cpu))
goto next;
pr_debug("cpu logical map 0x%llx\n", hwid); pr_debug("cpu logical map 0x%llx\n", hwid);
cpu_logical_map(cpu) = hwid; cpu_logical_map(cpu) = hwid;
next: next:
...@@ -418,12 +429,18 @@ void __init of_smp_init_cpus(void) ...@@ -418,12 +429,18 @@ void __init of_smp_init_cpus(void)
} }
/* /*
* All the cpus that made it to the cpu_logical_map have been * We need to set the cpu_logical_map entries before enabling
* validated so set them as possible cpus. * the cpus so that cpu processor description entries (DT cpu nodes
* and ACPI MADT entries) can be retrieved by matching the cpu hwid
* with entries in cpu_logical_map while initializing the cpus.
* If the cpu set-up fails, invalidate the cpu_logical_map entry.
*/ */
for (i = 0; i < NR_CPUS; i++) for (i = 1; i < NR_CPUS; i++) {
if (cpu_logical_map(i) != INVALID_HWID) if (cpu_logical_map(i) != INVALID_HWID) {
set_cpu_possible(i, true); if (smp_cpu_setup(i))
cpu_logical_map(i) = INVALID_HWID;
}
}
} }
void __init smp_prepare_cpus(unsigned int max_cpus) void __init smp_prepare_cpus(unsigned int max_cpus)
......
...@@ -49,8 +49,14 @@ static void write_pen_release(u64 val) ...@@ -49,8 +49,14 @@ static void write_pen_release(u64 val)
} }
static int smp_spin_table_cpu_init(struct device_node *dn, unsigned int cpu) static int smp_spin_table_cpu_init(unsigned int cpu)
{ {
struct device_node *dn;
dn = of_get_cpu_node(cpu, NULL);
if (!dn)
return -ENODEV;
/* /*
* Determine the address from which the CPU is polling. * Determine the address from which the CPU is polling.
*/ */
......
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