Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/arch/arm/kernel/devtree.c
26292 views
1
// SPDX-License-Identifier: GPL-2.0-only
2
/*
3
* linux/arch/arm/kernel/devtree.c
4
*
5
* Copyright (C) 2009 Canonical Ltd. <[email protected]>
6
*/
7
8
#include <linux/init.h>
9
#include <linux/export.h>
10
#include <linux/errno.h>
11
#include <linux/types.h>
12
#include <linux/memblock.h>
13
#include <linux/of.h>
14
#include <linux/of_fdt.h>
15
#include <linux/of_irq.h>
16
#include <linux/smp.h>
17
18
#include <asm/cputype.h>
19
#include <asm/setup.h>
20
#include <asm/page.h>
21
#include <asm/prom.h>
22
#include <asm/smp_plat.h>
23
#include <asm/mach/arch.h>
24
#include <asm/mach-types.h>
25
26
27
#ifdef CONFIG_SMP
28
extern struct of_cpu_method __cpu_method_of_table[];
29
30
static const struct of_cpu_method __cpu_method_of_table_sentinel
31
__used __section("__cpu_method_of_table_end");
32
33
34
static int __init set_smp_ops_by_method(struct device_node *node)
35
{
36
const char *method;
37
struct of_cpu_method *m = __cpu_method_of_table;
38
39
if (of_property_read_string(node, "enable-method", &method))
40
return 0;
41
42
for (; m->method; m++)
43
if (!strcmp(m->method, method)) {
44
smp_set_ops(m->ops);
45
return 1;
46
}
47
48
return 0;
49
}
50
#else
51
static inline int set_smp_ops_by_method(struct device_node *node)
52
{
53
return 1;
54
}
55
#endif
56
57
58
/*
59
* arm_dt_init_cpu_maps - Function retrieves cpu nodes from the device tree
60
* and builds the cpu logical map array containing MPIDR values related to
61
* logical cpus
62
*
63
* Updates the cpu possible mask with the number of parsed cpu nodes
64
*/
65
void __init arm_dt_init_cpu_maps(void)
66
{
67
/*
68
* Temp logical map is initialized with UINT_MAX values that are
69
* considered invalid logical map entries since the logical map must
70
* contain a list of MPIDR[23:0] values where MPIDR[31:24] must
71
* read as 0.
72
*/
73
struct device_node *cpu, *cpus;
74
int found_method = 0;
75
u32 i, j, cpuidx = 1;
76
u32 mpidr = is_smp() ? read_cpuid_mpidr() & MPIDR_HWID_BITMASK : 0;
77
78
u32 tmp_map[NR_CPUS] = { [0 ... NR_CPUS-1] = MPIDR_INVALID };
79
bool bootcpu_valid = false;
80
cpus = of_find_node_by_path("/cpus");
81
82
if (!cpus)
83
return;
84
85
for_each_of_cpu_node(cpu) {
86
u32 hwid = of_get_cpu_hwid(cpu, 0);
87
88
pr_debug(" * %pOF...\n", cpu);
89
90
/*
91
* Bits n:24 must be set to 0 in the DT since the reg property
92
* defines the MPIDR[23:0].
93
*/
94
if (hwid & ~MPIDR_HWID_BITMASK) {
95
of_node_put(cpu);
96
return;
97
}
98
99
/*
100
* Duplicate MPIDRs are a recipe for disaster.
101
* Scan all initialized entries and check for
102
* duplicates. If any is found just bail out.
103
* temp values were initialized to UINT_MAX
104
* to avoid matching valid MPIDR[23:0] values.
105
*/
106
for (j = 0; j < cpuidx; j++)
107
if (WARN(tmp_map[j] == hwid,
108
"Duplicate /cpu reg properties in the DT\n")) {
109
of_node_put(cpu);
110
return;
111
}
112
113
/*
114
* Build a stashed array of MPIDR values. Numbering scheme
115
* requires that if detected the boot CPU must be assigned
116
* logical id 0. Other CPUs get sequential indexes starting
117
* from 1. If a CPU node with a reg property matching the
118
* boot CPU MPIDR is detected, this is recorded so that the
119
* logical map built from DT is validated and can be used
120
* to override the map created in smp_setup_processor_id().
121
*/
122
if (hwid == mpidr) {
123
i = 0;
124
bootcpu_valid = true;
125
} else {
126
i = cpuidx++;
127
}
128
129
if (WARN(cpuidx > nr_cpu_ids, "DT /cpu %u nodes greater than "
130
"max cores %u, capping them\n",
131
cpuidx, nr_cpu_ids)) {
132
cpuidx = nr_cpu_ids;
133
of_node_put(cpu);
134
break;
135
}
136
137
tmp_map[i] = hwid;
138
139
if (!found_method)
140
found_method = set_smp_ops_by_method(cpu);
141
}
142
143
/*
144
* Fallback to an enable-method in the cpus node if nothing found in
145
* a cpu node.
146
*/
147
if (!found_method)
148
set_smp_ops_by_method(cpus);
149
150
if (!bootcpu_valid) {
151
pr_warn("DT missing boot CPU MPIDR[23:0], fall back to default cpu_logical_map\n");
152
return;
153
}
154
155
/*
156
* Since the boot CPU node contains proper data, and all nodes have
157
* a reg property, the DT CPU list can be considered valid and the
158
* logical map created in smp_setup_processor_id() can be overridden
159
*/
160
for (i = 0; i < cpuidx; i++) {
161
set_cpu_possible(i, true);
162
cpu_logical_map(i) = tmp_map[i];
163
pr_debug("cpu logical map 0x%x\n", cpu_logical_map(i));
164
}
165
}
166
167
bool arch_match_cpu_phys_id(int cpu, u64 phys_id)
168
{
169
return phys_id == cpu_logical_map(cpu);
170
}
171
172
static const void * __init arch_get_next_mach(const char *const **match)
173
{
174
static const struct machine_desc *mdesc = __arch_info_begin;
175
const struct machine_desc *m = mdesc;
176
177
if (m >= __arch_info_end)
178
return NULL;
179
180
mdesc++;
181
*match = m->dt_compat;
182
return m;
183
}
184
185
/**
186
* setup_machine_fdt - Machine setup when an dtb was passed to the kernel
187
* @dt_virt: virtual address of dt blob
188
*
189
* If a dtb was passed to the kernel in r2, then use it to choose the
190
* correct machine_desc and to setup the system.
191
*/
192
const struct machine_desc * __init setup_machine_fdt(void *dt_virt)
193
{
194
const struct machine_desc *mdesc, *mdesc_best = NULL;
195
196
DT_MACHINE_START(GENERIC_DT, "Generic DT based system")
197
.l2c_aux_val = 0x0,
198
.l2c_aux_mask = ~0x0,
199
MACHINE_END
200
201
mdesc_best = &__mach_desc_GENERIC_DT;
202
203
if (!dt_virt || !early_init_dt_verify(dt_virt, __pa(dt_virt)))
204
return NULL;
205
206
mdesc = of_flat_dt_match_machine(mdesc_best, arch_get_next_mach);
207
208
if (!mdesc) {
209
const char *prop;
210
int size;
211
unsigned long dt_root;
212
213
early_print("\nError: unrecognized/unsupported "
214
"device tree compatible list:\n[ ");
215
216
dt_root = of_get_flat_dt_root();
217
prop = of_get_flat_dt_prop(dt_root, "compatible", &size);
218
while (size > 0) {
219
early_print("'%s' ", prop);
220
size -= strlen(prop) + 1;
221
prop += strlen(prop) + 1;
222
}
223
early_print("]\n\n");
224
225
dump_machine_table(); /* does not return */
226
}
227
228
/* We really don't want to do this, but sometimes firmware provides buggy data */
229
if (mdesc->dt_fixup)
230
mdesc->dt_fixup();
231
232
early_init_dt_scan_nodes();
233
234
/* Change machine number to match the mdesc we're using */
235
__machine_arch_type = mdesc->nr;
236
237
return mdesc;
238
}
239
240