blob: 6f6de49df0edee6029dfccc03e74420b8e709016 [file] [log] [blame]
Tom Rini10e47792018-05-06 17:58:06 -04001// SPDX-License-Identifier: GPL-2.0+
Simon Glassa9a44262015-04-29 22:25:59 -06002/*
3 * Copyright (C) 2015 Google, Inc
4 *
Simon Glassa9a44262015-04-29 22:25:59 -06005 * Based on code from the coreboot file of the same name
6 */
7
8#include <common.h>
9#include <cpu.h>
10#include <dm.h>
11#include <errno.h>
Simon Glass0f2af882020-05-10 11:40:05 -060012#include <log.h>
Simon Glassa9a44262015-04-29 22:25:59 -060013#include <malloc.h>
Miao Yan92106272016-05-22 19:37:17 -070014#include <qfw.h>
Simon Glassa9a44262015-04-29 22:25:59 -060015#include <asm/atomic.h>
16#include <asm/cpu.h>
17#include <asm/interrupt.h>
18#include <asm/lapic.h>
Simon Glassc17d4502016-03-11 22:07:09 -070019#include <asm/microcode.h>
Simon Glassa9a44262015-04-29 22:25:59 -060020#include <asm/mp.h>
Bin Menge5d05002015-06-23 12:18:50 +080021#include <asm/msr.h>
Simon Glassa9a44262015-04-29 22:25:59 -060022#include <asm/mtrr.h>
Bin Menge5d05002015-06-23 12:18:50 +080023#include <asm/processor.h>
Simon Glassa9a44262015-04-29 22:25:59 -060024#include <asm/sipi.h>
25#include <dm/device-internal.h>
26#include <dm/uclass-internal.h>
Miao Yan35f54b22016-01-07 01:32:04 -080027#include <dm/lists.h>
28#include <dm/root.h>
Simon Glassdbd79542020-05-10 11:40:11 -060029#include <linux/delay.h>
Simon Glassa9a44262015-04-29 22:25:59 -060030#include <linux/linkage.h>
31
Simon Glassdaa93d92015-07-31 09:31:31 -060032DECLARE_GLOBAL_DATA_PTR;
33
Simon Glassa9a44262015-04-29 22:25:59 -060034/* This also needs to match the sipi.S assembly code for saved MSR encoding */
35struct saved_msr {
36 uint32_t index;
37 uint32_t lo;
38 uint32_t hi;
39} __packed;
40
Simon Glassa9a44262015-04-29 22:25:59 -060041struct mp_flight_plan {
42 int num_records;
43 struct mp_flight_record *records;
44};
45
46static struct mp_flight_plan mp_info;
47
48struct cpu_map {
49 struct udevice *dev;
50 int apic_id;
51 int err_code;
52};
53
54static inline void barrier_wait(atomic_t *b)
55{
56 while (atomic_read(b) == 0)
57 asm("pause");
58 mfence();
59}
60
61static inline void release_barrier(atomic_t *b)
62{
63 mfence();
64 atomic_set(b, 1);
65}
66
Bin Menge5d05002015-06-23 12:18:50 +080067static inline void stop_this_cpu(void)
68{
69 /* Called by an AP when it is ready to halt and wait for a new task */
70 for (;;)
71 cpu_hlt();
72}
73
Simon Glassa9a44262015-04-29 22:25:59 -060074/* Returns 1 if timeout waiting for APs. 0 if target APs found */
75static int wait_for_aps(atomic_t *val, int target, int total_delay,
76 int delay_step)
77{
78 int timeout = 0;
79 int delayed = 0;
80
81 while (atomic_read(val) != target) {
82 udelay(delay_step);
83 delayed += delay_step;
84 if (delayed >= total_delay) {
85 timeout = 1;
86 break;
87 }
88 }
89
90 return timeout;
91}
92
93static void ap_do_flight_plan(struct udevice *cpu)
94{
95 int i;
96
97 for (i = 0; i < mp_info.num_records; i++) {
98 struct mp_flight_record *rec = &mp_info.records[i];
99
100 atomic_inc(&rec->cpus_entered);
101 barrier_wait(&rec->barrier);
102
103 if (rec->ap_call != NULL)
104 rec->ap_call(cpu, rec->ap_arg);
105 }
106}
107
Miao Yan2ee10002016-01-07 01:32:02 -0800108static int find_cpu_by_apic_id(int apic_id, struct udevice **devp)
Simon Glassa9a44262015-04-29 22:25:59 -0600109{
110 struct udevice *dev;
111
112 *devp = NULL;
113 for (uclass_find_first_device(UCLASS_CPU, &dev);
114 dev;
115 uclass_find_next_device(&dev)) {
116 struct cpu_platdata *plat = dev_get_parent_platdata(dev);
117
118 if (plat->cpu_id == apic_id) {
119 *devp = dev;
120 return 0;
121 }
122 }
123
124 return -ENOENT;
125}
126
127/*
128 * By the time APs call ap_init() caching has been setup, and microcode has
129 * been loaded
130 */
131static void ap_init(unsigned int cpu_index)
132{
133 struct udevice *dev;
134 int apic_id;
135 int ret;
136
137 /* Ensure the local apic is enabled */
138 enable_lapic();
139
140 apic_id = lapicid();
Miao Yan2ee10002016-01-07 01:32:02 -0800141 ret = find_cpu_by_apic_id(apic_id, &dev);
Simon Glassa9a44262015-04-29 22:25:59 -0600142 if (ret) {
143 debug("Unknown CPU apic_id %x\n", apic_id);
144 goto done;
145 }
146
147 debug("AP: slot %d apic_id %x, dev %s\n", cpu_index, apic_id,
148 dev ? dev->name : "(apic_id not found)");
149
150 /* Walk the flight plan */
151 ap_do_flight_plan(dev);
152
153 /* Park the AP */
154 debug("parking\n");
155done:
156 stop_this_cpu();
157}
158
159static const unsigned int fixed_mtrrs[NUM_FIXED_MTRRS] = {
160 MTRR_FIX_64K_00000_MSR, MTRR_FIX_16K_80000_MSR, MTRR_FIX_16K_A0000_MSR,
161 MTRR_FIX_4K_C0000_MSR, MTRR_FIX_4K_C8000_MSR, MTRR_FIX_4K_D0000_MSR,
162 MTRR_FIX_4K_D8000_MSR, MTRR_FIX_4K_E0000_MSR, MTRR_FIX_4K_E8000_MSR,
163 MTRR_FIX_4K_F0000_MSR, MTRR_FIX_4K_F8000_MSR,
164};
165
166static inline struct saved_msr *save_msr(int index, struct saved_msr *entry)
167{
168 msr_t msr;
169
170 msr = msr_read(index);
171 entry->index = index;
172 entry->lo = msr.lo;
173 entry->hi = msr.hi;
174
175 /* Return the next entry */
176 entry++;
177 return entry;
178}
179
180static int save_bsp_msrs(char *start, int size)
181{
182 int msr_count;
183 int num_var_mtrrs;
184 struct saved_msr *msr_entry;
185 int i;
186 msr_t msr;
187
188 /* Determine number of MTRRs need to be saved */
189 msr = msr_read(MTRR_CAP_MSR);
190 num_var_mtrrs = msr.lo & 0xff;
191
192 /* 2 * num_var_mtrrs for base and mask. +1 for IA32_MTRR_DEF_TYPE */
193 msr_count = 2 * num_var_mtrrs + NUM_FIXED_MTRRS + 1;
194
195 if ((msr_count * sizeof(struct saved_msr)) > size) {
Simon Glass17dbe892016-03-06 19:28:22 -0700196 printf("Cannot mirror all %d msrs\n", msr_count);
Simon Glassa9a44262015-04-29 22:25:59 -0600197 return -ENOSPC;
198 }
199
200 msr_entry = (void *)start;
201 for (i = 0; i < NUM_FIXED_MTRRS; i++)
202 msr_entry = save_msr(fixed_mtrrs[i], msr_entry);
203
204 for (i = 0; i < num_var_mtrrs; i++) {
205 msr_entry = save_msr(MTRR_PHYS_BASE_MSR(i), msr_entry);
206 msr_entry = save_msr(MTRR_PHYS_MASK_MSR(i), msr_entry);
207 }
208
209 msr_entry = save_msr(MTRR_DEF_TYPE_MSR, msr_entry);
210
211 return msr_count;
212}
213
Miao Yan60677622016-01-07 01:32:03 -0800214static int load_sipi_vector(atomic_t **ap_countp, int num_cpus)
Simon Glassa9a44262015-04-29 22:25:59 -0600215{
216 struct sipi_params_16bit *params16;
217 struct sipi_params *params;
218 static char msr_save[512];
219 char *stack;
220 ulong addr;
221 int code_len;
222 int size;
223 int ret;
224
225 /* Copy in the code */
226 code_len = ap_start16_code_end - ap_start16;
227 debug("Copying SIPI code to %x: %d bytes\n", AP_DEFAULT_BASE,
228 code_len);
229 memcpy((void *)AP_DEFAULT_BASE, ap_start16, code_len);
230
231 addr = AP_DEFAULT_BASE + (ulong)sipi_params_16bit - (ulong)ap_start16;
232 params16 = (struct sipi_params_16bit *)addr;
233 params16->ap_start = (uint32_t)ap_start;
234 params16->gdt = (uint32_t)gd->arch.gdt;
235 params16->gdt_limit = X86_GDT_SIZE - 1;
236 debug("gdt = %x, gdt_limit = %x\n", params16->gdt, params16->gdt_limit);
237
238 params = (struct sipi_params *)sipi_params;
239 debug("SIPI 32-bit params at %p\n", params);
240 params->idt_ptr = (uint32_t)x86_get_idt();
241
242 params->stack_size = CONFIG_AP_STACK_SIZE;
Miao Yan60677622016-01-07 01:32:03 -0800243 size = params->stack_size * num_cpus;
Stephen Warren5923b592016-02-12 14:27:56 -0700244 stack = memalign(4096, size);
Simon Glassa9a44262015-04-29 22:25:59 -0600245 if (!stack)
246 return -ENOMEM;
247 params->stack_top = (u32)(stack + size);
Andy Shevchenko43b3ac52017-02-17 16:49:00 +0300248#if !defined(CONFIG_QEMU) && !defined(CONFIG_HAVE_FSP) && \
249 !defined(CONFIG_INTEL_MID)
Simon Glass8dda5872016-03-11 22:07:11 -0700250 params->microcode_ptr = ucode_base;
251 debug("Microcode at %x\n", params->microcode_ptr);
252#endif
Simon Glassa9a44262015-04-29 22:25:59 -0600253 params->msr_table_ptr = (u32)msr_save;
254 ret = save_bsp_msrs(msr_save, sizeof(msr_save));
255 if (ret < 0)
256 return ret;
257 params->msr_count = ret;
258
259 params->c_handler = (uint32_t)&ap_init;
260
261 *ap_countp = &params->ap_count;
262 atomic_set(*ap_countp, 0);
263 debug("SIPI vector is ready\n");
264
265 return 0;
266}
267
268static int check_cpu_devices(int expected_cpus)
269{
270 int i;
271
272 for (i = 0; i < expected_cpus; i++) {
273 struct udevice *dev;
274 int ret;
275
276 ret = uclass_find_device(UCLASS_CPU, i, &dev);
277 if (ret) {
278 debug("Cannot find CPU %d in device tree\n", i);
279 return ret;
280 }
281 }
282
283 return 0;
284}
285
286/* Returns 1 for timeout. 0 on success */
Simon Glass17dbe892016-03-06 19:28:22 -0700287static int apic_wait_timeout(int total_delay, const char *msg)
Simon Glassa9a44262015-04-29 22:25:59 -0600288{
289 int total = 0;
Simon Glassa9a44262015-04-29 22:25:59 -0600290
Simon Glass17dbe892016-03-06 19:28:22 -0700291 if (!(lapic_read(LAPIC_ICR) & LAPIC_ICR_BUSY))
292 return 0;
293
294 debug("Waiting for %s...", msg);
Simon Glassa9a44262015-04-29 22:25:59 -0600295 while (lapic_read(LAPIC_ICR) & LAPIC_ICR_BUSY) {
Simon Glass17dbe892016-03-06 19:28:22 -0700296 udelay(50);
297 total += 50;
Simon Glassa9a44262015-04-29 22:25:59 -0600298 if (total >= total_delay) {
Simon Glass17dbe892016-03-06 19:28:22 -0700299 debug("timed out: aborting\n");
300 return -ETIMEDOUT;
Simon Glassa9a44262015-04-29 22:25:59 -0600301 }
302 }
Simon Glass17dbe892016-03-06 19:28:22 -0700303 debug("done\n");
Simon Glassa9a44262015-04-29 22:25:59 -0600304
Simon Glass17dbe892016-03-06 19:28:22 -0700305 return 0;
Simon Glassa9a44262015-04-29 22:25:59 -0600306}
307
Simon Glassa3ee7b82020-07-17 08:48:10 -0600308/**
309 * start_aps() - Start up the APs and count how many we find
310 *
311 * This is called on the boot processor to start up all the other processors
312 * (here called APs).
313 *
314 * @num_aps: Number of APs we expect to find
315 * @ap_count: Initially zero. Incremented by this function for each AP found
316 * @return 0 if all APs were set up correctly or there are none to set up,
317 * -ENOSPC if the SIPI vector is too high in memory,
318 * -ETIMEDOUT if the ICR is busy or the second SIPI fails to complete
319 * -EIO if not all APs check in correctly
320 */
321static int start_aps(int num_aps, atomic_t *ap_count)
Simon Glassa9a44262015-04-29 22:25:59 -0600322{
323 int sipi_vector;
324 /* Max location is 4KiB below 1MiB */
325 const int max_vector_loc = ((1 << 20) - (1 << 12)) >> 12;
326
Simon Glassa3ee7b82020-07-17 08:48:10 -0600327 if (num_aps == 0)
Simon Glassa9a44262015-04-29 22:25:59 -0600328 return 0;
329
330 /* The vector is sent as a 4k aligned address in one byte */
331 sipi_vector = AP_DEFAULT_BASE >> 12;
332
333 if (sipi_vector > max_vector_loc) {
334 printf("SIPI vector too large! 0x%08x\n",
335 sipi_vector);
Simon Glassf9b58002019-04-25 21:58:41 -0600336 return -ENOSPC;
Simon Glassa9a44262015-04-29 22:25:59 -0600337 }
338
Simon Glassa3ee7b82020-07-17 08:48:10 -0600339 debug("Attempting to start %d APs\n", num_aps);
Simon Glassa9a44262015-04-29 22:25:59 -0600340
Simon Glass17dbe892016-03-06 19:28:22 -0700341 if (apic_wait_timeout(1000, "ICR not to be busy"))
342 return -ETIMEDOUT;
Simon Glassa9a44262015-04-29 22:25:59 -0600343
344 /* Send INIT IPI to all but self */
Bin Menge5d05002015-06-23 12:18:50 +0800345 lapic_write(LAPIC_ICR2, SET_LAPIC_DEST_FIELD(0));
346 lapic_write(LAPIC_ICR, LAPIC_DEST_ALLBUT | LAPIC_INT_ASSERT |
347 LAPIC_DM_INIT);
Simon Glass17dbe892016-03-06 19:28:22 -0700348 debug("Waiting for 10ms after sending INIT\n");
Simon Glassa9a44262015-04-29 22:25:59 -0600349 mdelay(10);
350
351 /* Send 1st SIPI */
Simon Glass17dbe892016-03-06 19:28:22 -0700352 if (apic_wait_timeout(1000, "ICR not to be busy"))
353 return -ETIMEDOUT;
Simon Glassa9a44262015-04-29 22:25:59 -0600354
Bin Menge5d05002015-06-23 12:18:50 +0800355 lapic_write(LAPIC_ICR2, SET_LAPIC_DEST_FIELD(0));
356 lapic_write(LAPIC_ICR, LAPIC_DEST_ALLBUT | LAPIC_INT_ASSERT |
357 LAPIC_DM_STARTUP | sipi_vector);
Simon Glass17dbe892016-03-06 19:28:22 -0700358 if (apic_wait_timeout(10000, "first SIPI to complete"))
359 return -ETIMEDOUT;
Simon Glassa9a44262015-04-29 22:25:59 -0600360
361 /* Wait for CPUs to check in up to 200 us */
Simon Glassa3ee7b82020-07-17 08:48:10 -0600362 wait_for_aps(ap_count, num_aps, 200, 15);
Simon Glassa9a44262015-04-29 22:25:59 -0600363
364 /* Send 2nd SIPI */
Simon Glass17dbe892016-03-06 19:28:22 -0700365 if (apic_wait_timeout(1000, "ICR not to be busy"))
366 return -ETIMEDOUT;
Simon Glassa9a44262015-04-29 22:25:59 -0600367
Bin Menge5d05002015-06-23 12:18:50 +0800368 lapic_write(LAPIC_ICR2, SET_LAPIC_DEST_FIELD(0));
369 lapic_write(LAPIC_ICR, LAPIC_DEST_ALLBUT | LAPIC_INT_ASSERT |
370 LAPIC_DM_STARTUP | sipi_vector);
Simon Glass17dbe892016-03-06 19:28:22 -0700371 if (apic_wait_timeout(10000, "second SIPI to complete"))
372 return -ETIMEDOUT;
Simon Glassa9a44262015-04-29 22:25:59 -0600373
374 /* Wait for CPUs to check in */
Simon Glassa3ee7b82020-07-17 08:48:10 -0600375 if (wait_for_aps(ap_count, num_aps, 10000, 50)) {
Simon Glass17dbe892016-03-06 19:28:22 -0700376 debug("Not all APs checked in: %d/%d\n",
Simon Glassa3ee7b82020-07-17 08:48:10 -0600377 atomic_read(ap_count), num_aps);
Simon Glassf9b58002019-04-25 21:58:41 -0600378 return -EIO;
Simon Glassa9a44262015-04-29 22:25:59 -0600379 }
380
381 return 0;
382}
383
Simon Glass3307d0c2020-07-17 08:48:11 -0600384/**
385 * bsp_do_flight_plan() - Do the flight plan on the BSP
386 *
387 * This runs the flight plan on the main CPU used to boot U-Boot
388 *
389 * @cpu: Device for the main CPU
390 * @plan: Flight plan to run
391 * @num_aps: Number of APs (CPUs other than the BSP)
392 * @returns 0 on success, -ETIMEDOUT if an AP failed to come up
393 */
394static int bsp_do_flight_plan(struct udevice *cpu, struct mp_flight_plan *plan,
395 int num_aps)
Simon Glassa9a44262015-04-29 22:25:59 -0600396{
397 int i;
398 int ret = 0;
399 const int timeout_us = 100000;
400 const int step_us = 100;
Simon Glassa9a44262015-04-29 22:25:59 -0600401
Simon Glasse40633d2020-07-17 08:48:08 -0600402 for (i = 0; i < plan->num_records; i++) {
403 struct mp_flight_record *rec = &plan->records[i];
Simon Glassa9a44262015-04-29 22:25:59 -0600404
405 /* Wait for APs if the record is not released */
406 if (atomic_read(&rec->barrier) == 0) {
407 /* Wait for the APs to check in */
408 if (wait_for_aps(&rec->cpus_entered, num_aps,
409 timeout_us, step_us)) {
Simon Glass17dbe892016-03-06 19:28:22 -0700410 debug("MP record %d timeout\n", i);
Simon Glassf9b58002019-04-25 21:58:41 -0600411 ret = -ETIMEDOUT;
Simon Glassa9a44262015-04-29 22:25:59 -0600412 }
413 }
414
415 if (rec->bsp_call != NULL)
416 rec->bsp_call(cpu, rec->bsp_arg);
417
418 release_barrier(&rec->barrier);
419 }
Simon Glass3307d0c2020-07-17 08:48:11 -0600420
Simon Glassa9a44262015-04-29 22:25:59 -0600421 return ret;
422}
423
424static int init_bsp(struct udevice **devp)
425{
426 char processor_name[CPU_MAX_NAME_LEN];
427 int apic_id;
428 int ret;
429
430 cpu_get_name(processor_name);
Simon Glass17dbe892016-03-06 19:28:22 -0700431 debug("CPU: %s\n", processor_name);
Simon Glassa9a44262015-04-29 22:25:59 -0600432
Simon Glassa9a44262015-04-29 22:25:59 -0600433 apic_id = lapicid();
Miao Yan2ee10002016-01-07 01:32:02 -0800434 ret = find_cpu_by_apic_id(apic_id, devp);
Simon Glassa9a44262015-04-29 22:25:59 -0600435 if (ret) {
436 printf("Cannot find boot CPU, APIC ID %d\n", apic_id);
437 return ret;
438 }
439
440 return 0;
441}
442
Simon Glass35ee0de2020-07-17 08:48:09 -0600443static int mp_init_cpu(struct udevice *cpu, void *unused)
444{
445 struct cpu_platdata *plat = dev_get_parent_platdata(cpu);
446
Simon Glass35ee0de2020-07-17 08:48:09 -0600447 plat->ucode_version = microcode_read_rev();
448 plat->device_id = gd->arch.x86_device;
449
450 return device_probe(cpu);
451}
452
453static struct mp_flight_record mp_steps[] = {
454 MP_FR_BLOCK_APS(mp_init_cpu, NULL, mp_init_cpu, NULL),
455};
456
Simon Glasse40633d2020-07-17 08:48:08 -0600457int mp_init(void)
Simon Glassa9a44262015-04-29 22:25:59 -0600458{
Simon Glass3307d0c2020-07-17 08:48:11 -0600459 int num_aps, num_cpus;
Simon Glassa9a44262015-04-29 22:25:59 -0600460 atomic_t *ap_count;
461 struct udevice *cpu;
Simon Glassa9a44262015-04-29 22:25:59 -0600462 struct uclass *uc;
Simon Glassebb239a2020-07-17 08:48:13 -0600463 int ret;
Simon Glassa9a44262015-04-29 22:25:59 -0600464
Simon Glass4c8243d2019-12-06 21:42:55 -0700465 if (IS_ENABLED(CONFIG_QFW)) {
466 ret = qemu_cpu_fixup();
467 if (ret)
468 return ret;
469 }
Miao Yan35f54b22016-01-07 01:32:04 -0800470
Simon Glassebb239a2020-07-17 08:48:13 -0600471 /*
472 * Multiple APs are brought up simultaneously and they may get the same
473 * seq num in the uclass_resolve_seq() during device_probe(). To avoid
474 * this, set req_seq to the reg number in the device tree in advance.
475 */
476 uclass_id_foreach_dev(UCLASS_CPU, cpu, uc)
477 cpu->req_seq = dev_read_u32_default(cpu, "reg", -1);
478
Simon Glassa9a44262015-04-29 22:25:59 -0600479 ret = init_bsp(&cpu);
480 if (ret) {
481 debug("Cannot init boot CPU: err=%d\n", ret);
482 return ret;
483 }
484
Bin Mengf967f9a2015-06-17 11:15:36 +0800485 num_cpus = cpu_get_count(cpu);
486 if (num_cpus < 0) {
487 debug("Cannot get number of CPUs: err=%d\n", num_cpus);
488 return num_cpus;
489 }
490
491 if (num_cpus < 2)
492 debug("Warning: Only 1 CPU is detected\n");
493
494 ret = check_cpu_devices(num_cpus);
Simon Glassa9a44262015-04-29 22:25:59 -0600495 if (ret)
496 debug("Warning: Device tree does not describe all CPUs. Extra ones will not be started correctly\n");
497
498 /* Copy needed parameters so that APs have a reference to the plan */
Simon Glasse40633d2020-07-17 08:48:08 -0600499 mp_info.num_records = ARRAY_SIZE(mp_steps);
500 mp_info.records = mp_steps;
Simon Glassa9a44262015-04-29 22:25:59 -0600501
502 /* Load the SIPI vector */
Miao Yan60677622016-01-07 01:32:03 -0800503 ret = load_sipi_vector(&ap_count, num_cpus);
Simon Glassa9a44262015-04-29 22:25:59 -0600504 if (ap_count == NULL)
Simon Glassf9b58002019-04-25 21:58:41 -0600505 return -ENOENT;
Simon Glassa9a44262015-04-29 22:25:59 -0600506
507 /*
508 * Make sure SIPI data hits RAM so the APs that come up will see
509 * the startup code even if the caches are disabled
510 */
511 wbinvd();
512
513 /* Start the APs providing number of APs and the cpus_entered field */
Bin Mengf967f9a2015-06-17 11:15:36 +0800514 num_aps = num_cpus - 1;
Simon Glassa9a44262015-04-29 22:25:59 -0600515 ret = start_aps(num_aps, ap_count);
516 if (ret) {
517 mdelay(1000);
518 debug("%d/%d eventually checked in?\n", atomic_read(ap_count),
519 num_aps);
520 return ret;
521 }
522
523 /* Walk the flight plan for the BSP */
Simon Glass3307d0c2020-07-17 08:48:11 -0600524 ret = bsp_do_flight_plan(cpu, &mp_info, num_aps);
Simon Glassa9a44262015-04-29 22:25:59 -0600525 if (ret) {
526 debug("CPU init failed: err=%d\n", ret);
527 return ret;
528 }
529
530 return 0;
531}