bcm53xx: use Broadcom's proposed SMP implementation
[openwrt.git] / target / linux / bcm53xx / patches-4.1 / 130-ARM-BCM-Add-SMP-support-for-Broadcom-NSP.patch
1 From a0ad1511d5805b95ac4c454d7904c670a1696055 Mon Sep 17 00:00:00 2001
2 From: Kapil Hali <kapilh@broadcom.com>
3 Date: Wed, 14 Oct 2015 13:47:00 -0400
4 Subject: [PATCH] ARM: BCM: Add SMP support for Broadcom NSP
5
6 Add SMP support for Broadcom's Northstar Plus SoC,
7 cpu enable method and pen_release procedures. This
8 changes also consolidates iProc family's - BCM NSP
9 and BCM Kona, SMP handling in a common file.
10
11 Northstar Plus SoC is based on ARM Cortex-A9
12 revision r3p0 which requires configuration for ARM
13 Errata 764369 for SMP. This change adds the needed
14 configuration option.
15
16 Signed-off-by: Kapil Hali <kapilh@broadcom.com>
17 ---
18  arch/arm/mach-bcm/Makefile   |   2 +-
19  arch/arm/mach-bcm/bcm_nsp.h  |  19 +++
20  arch/arm/mach-bcm/headsmp.S  |  37 +++++
21  arch/arm/mach-bcm/kona_smp.c | 202 ---------------------------
22  arch/arm/mach-bcm/platsmp.c  | 326 +++++++++++++++++++++++++++++++++++++++++++
23  5 files changed, 383 insertions(+), 203 deletions(-)
24  create mode 100644 arch/arm/mach-bcm/bcm_nsp.h
25  create mode 100644 arch/arm/mach-bcm/headsmp.S
26  delete mode 100644 arch/arm/mach-bcm/kona_smp.c
27  create mode 100644 arch/arm/mach-bcm/platsmp.c
28
29 --- a/arch/arm/mach-bcm/Makefile
30 +++ b/arch/arm/mach-bcm/Makefile
31 @@ -20,7 +20,7 @@ obj-$(CONFIG_ARCH_BCM_281XX)  += board_bc
32  obj-$(CONFIG_ARCH_BCM_21664)   += board_bcm21664.o
33  
34  # BCM281XX and BCM21664 SMP support
35 -obj-$(CONFIG_ARCH_BCM_MOBILE_SMP) += kona_smp.o
36 +obj-$(CONFIG_ARCH_BCM_MOBILE_SMP) += platsmp.o
37  
38  # BCM281XX and BCM21664 L2 cache control
39  obj-$(CONFIG_ARCH_BCM_MOBILE_L2_CACHE) += kona_l2_cache.o
40 --- /dev/null
41 +++ b/arch/arm/mach-bcm/bcm_nsp.h
42 @@ -0,0 +1,19 @@
43 +/*
44 + * Copyright (C) 2015 Broadcom Corporation
45 + *
46 + * This program is free software; you can redistribute it and/or
47 + * modify it under the terms of the GNU General Public License as
48 + * published by the Free Software Foundation version 2.
49 + *
50 + * This program is distributed "as is" WITHOUT ANY WARRANTY of any
51 + * kind, whether express or implied; without even the implied warranty
52 + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
53 + * GNU General Public License for more details.
54 + */
55 +
56 +#ifndef __BCM_NSP_H
57 +#define __BCM_NSP_H
58 +
59 +extern void nsp_secondary_startup(void);
60 +
61 +#endif /* __BCM_NSP_H */
62 --- /dev/null
63 +++ b/arch/arm/mach-bcm/headsmp.S
64 @@ -0,0 +1,37 @@
65 +/*
66 + * Copyright (C) 2015 Broadcom Corporation
67 + *
68 + * This program is free software; you can redistribute it and/or
69 + * modify it under the terms of the GNU General Public License as
70 + * published by the Free Software Foundation version 2.
71 + *
72 + * This program is distributed "as is" WITHOUT ANY WARRANTY of any
73 + * kind, whether express or implied; without even the implied warranty
74 + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
75 + * GNU General Public License for more details.
76 + */
77 +
78 +#include <linux/linkage.h>
79 +
80 +/*
81 + * iProc specific entry point for secondary CPUs.  This provides
82 + * a "holding pen" into which all secondary cores are held until
83 + * we are ready for them to initialise.
84 + */
85 +ENTRY(nsp_secondary_startup)
86 +       mrc     p15, 0, r0, c0, c0, 5
87 +       and     r0, r0, #15
88 +       adr     r4, 1f
89 +       ldmia   r4, {r5, r6}
90 +       sub     r4, r4, r5
91 +       add     r6, r6, r4
92 +pen:   ldr     r7, [r6]
93 +       cmp     r7, r0
94 +       bne     pen
95 +
96 +       b    secondary_startup
97 +
98 +1:     .long   .
99 +       .long   pen_release
100 +
101 +ENDPROC(nsp_secondary_startup)
102 --- a/arch/arm/mach-bcm/kona_smp.c
103 +++ /dev/null
104 @@ -1,202 +0,0 @@
105 -/*
106 - * Copyright (C) 2014 Broadcom Corporation
107 - * Copyright 2014 Linaro Limited
108 - *
109 - * This program is free software; you can redistribute it and/or
110 - * modify it under the terms of the GNU General Public License as
111 - * published by the Free Software Foundation version 2.
112 - *
113 - * This program is distributed "as is" WITHOUT ANY WARRANTY of any
114 - * kind, whether express or implied; without even the implied warranty
115 - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
116 - * GNU General Public License for more details.
117 - */
118 -
119 -#include <linux/init.h>
120 -#include <linux/errno.h>
121 -#include <linux/io.h>
122 -#include <linux/of.h>
123 -#include <linux/sched.h>
124 -
125 -#include <asm/smp.h>
126 -#include <asm/smp_plat.h>
127 -#include <asm/smp_scu.h>
128 -
129 -/* Size of mapped Cortex A9 SCU address space */
130 -#define CORTEX_A9_SCU_SIZE     0x58
131 -
132 -#define SECONDARY_TIMEOUT_NS   NSEC_PER_MSEC   /* 1 msec (in nanoseconds) */
133 -#define BOOT_ADDR_CPUID_MASK   0x3
134 -
135 -/* Name of device node property defining secondary boot register location */
136 -#define OF_SECONDARY_BOOT      "secondary-boot-reg"
137 -
138 -/* I/O address of register used to coordinate secondary core startup */
139 -static u32     secondary_boot;
140 -
141 -/*
142 - * Enable the Cortex A9 Snoop Control Unit
143 - *
144 - * By the time this is called we already know there are multiple
145 - * cores present.  We assume we're running on a Cortex A9 processor,
146 - * so any trouble getting the base address register or getting the
147 - * SCU base is a problem.
148 - *
149 - * Return 0 if successful or an error code otherwise.
150 - */
151 -static int __init scu_a9_enable(void)
152 -{
153 -       unsigned long config_base;
154 -       void __iomem *scu_base;
155 -
156 -       if (!scu_a9_has_base()) {
157 -               pr_err("no configuration base address register!\n");
158 -               return -ENXIO;
159 -       }
160 -
161 -       /* Config base address register value is zero for uniprocessor */
162 -       config_base = scu_a9_get_base();
163 -       if (!config_base) {
164 -               pr_err("hardware reports only one core\n");
165 -               return -ENOENT;
166 -       }
167 -
168 -       scu_base = ioremap((phys_addr_t)config_base, CORTEX_A9_SCU_SIZE);
169 -       if (!scu_base) {
170 -               pr_err("failed to remap config base (%lu/%u) for SCU\n",
171 -                       config_base, CORTEX_A9_SCU_SIZE);
172 -               return -ENOMEM;
173 -       }
174 -
175 -       scu_enable(scu_base);
176 -
177 -       iounmap(scu_base);      /* That's the last we'll need of this */
178 -
179 -       return 0;
180 -}
181 -
182 -static void __init bcm_smp_prepare_cpus(unsigned int max_cpus)
183 -{
184 -       static cpumask_t only_cpu_0 = { CPU_BITS_CPU0 };
185 -       struct device_node *node;
186 -       int ret;
187 -
188 -       BUG_ON(secondary_boot);         /* We're called only once */
189 -
190 -       /*
191 -        * This function is only called via smp_ops->smp_prepare_cpu().
192 -        * That only happens if a "/cpus" device tree node exists
193 -        * and has an "enable-method" property that selects the SMP
194 -        * operations defined herein.
195 -        */
196 -       node = of_find_node_by_path("/cpus");
197 -       BUG_ON(!node);
198 -
199 -       /*
200 -        * Our secondary enable method requires a "secondary-boot-reg"
201 -        * property to specify a register address used to request the
202 -        * ROM code boot a secondary code.  If we have any trouble
203 -        * getting this we fall back to uniprocessor mode.
204 -        */
205 -       if (of_property_read_u32(node, OF_SECONDARY_BOOT, &secondary_boot)) {
206 -               pr_err("%s: missing/invalid " OF_SECONDARY_BOOT " property\n",
207 -                       node->name);
208 -               ret = -ENOENT;          /* Arrange to disable SMP */
209 -               goto out;
210 -       }
211 -
212 -       /*
213 -        * Enable the SCU on Cortex A9 based SoCs.  If -ENOENT is
214 -        * returned, the SoC reported a uniprocessor configuration.
215 -        * We bail on any other error.
216 -        */
217 -       ret = scu_a9_enable();
218 -out:
219 -       of_node_put(node);
220 -       if (ret) {
221 -               /* Update the CPU present map to reflect uniprocessor mode */
222 -               BUG_ON(ret != -ENOENT);
223 -               pr_warn("disabling SMP\n");
224 -               init_cpu_present(&only_cpu_0);
225 -       }
226 -}
227 -
228 -/*
229 - * The ROM code has the secondary cores looping, waiting for an event.
230 - * When an event occurs each core examines the bottom two bits of the
231 - * secondary boot register.  When a core finds those bits contain its
232 - * own core id, it performs initialization, including computing its boot
233 - * address by clearing the boot register value's bottom two bits.  The
234 - * core signals that it is beginning its execution by writing its boot
235 - * address back to the secondary boot register, and finally jumps to
236 - * that address.
237 - *
238 - * So to start a core executing we need to:
239 - * - Encode the (hardware) CPU id with the bottom bits of the secondary
240 - *   start address.
241 - * - Write that value into the secondary boot register.
242 - * - Generate an event to wake up the secondary CPU(s).
243 - * - Wait for the secondary boot register to be re-written, which
244 - *   indicates the secondary core has started.
245 - */
246 -static int bcm_boot_secondary(unsigned int cpu, struct task_struct *idle)
247 -{
248 -       void __iomem *boot_reg;
249 -       phys_addr_t boot_func;
250 -       u64 start_clock;
251 -       u32 cpu_id;
252 -       u32 boot_val;
253 -       bool timeout = false;
254 -
255 -       cpu_id = cpu_logical_map(cpu);
256 -       if (cpu_id & ~BOOT_ADDR_CPUID_MASK) {
257 -               pr_err("bad cpu id (%u > %u)\n", cpu_id, BOOT_ADDR_CPUID_MASK);
258 -               return -EINVAL;
259 -       }
260 -
261 -       if (!secondary_boot) {
262 -               pr_err("required secondary boot register not specified\n");
263 -               return -EINVAL;
264 -       }
265 -
266 -       boot_reg = ioremap_nocache((phys_addr_t)secondary_boot, sizeof(u32));
267 -       if (!boot_reg) {
268 -               pr_err("unable to map boot register for cpu %u\n", cpu_id);
269 -               return -ENOSYS;
270 -       }
271 -
272 -       /*
273 -        * Secondary cores will start in secondary_startup(),
274 -        * defined in "arch/arm/kernel/head.S"
275 -        */
276 -       boot_func = virt_to_phys(secondary_startup);
277 -       BUG_ON(boot_func & BOOT_ADDR_CPUID_MASK);
278 -       BUG_ON(boot_func > (phys_addr_t)U32_MAX);
279 -
280 -       /* The core to start is encoded in the low bits */
281 -       boot_val = (u32)boot_func | cpu_id;
282 -       writel_relaxed(boot_val, boot_reg);
283 -
284 -       sev();
285 -
286 -       /* The low bits will be cleared once the core has started */
287 -       start_clock = local_clock();
288 -       while (!timeout && readl_relaxed(boot_reg) == boot_val)
289 -               timeout = local_clock() - start_clock > SECONDARY_TIMEOUT_NS;
290 -
291 -       iounmap(boot_reg);
292 -
293 -       if (!timeout)
294 -               return 0;
295 -
296 -       pr_err("timeout waiting for cpu %u to start\n", cpu_id);
297 -
298 -       return -ENOSYS;
299 -}
300 -
301 -static struct smp_operations bcm_smp_ops __initdata = {
302 -       .smp_prepare_cpus       = bcm_smp_prepare_cpus,
303 -       .smp_boot_secondary     = bcm_boot_secondary,
304 -};
305 -CPU_METHOD_OF_DECLARE(bcm_smp_bcm281xx, "brcm,bcm11351-cpu-method",
306 -                       &bcm_smp_ops);
307 --- /dev/null
308 +++ b/arch/arm/mach-bcm/platsmp.c
309 @@ -0,0 +1,326 @@
310 +/*
311 + * Copyright (C) 2014-2015 Broadcom Corporation
312 + * Copyright 2014 Linaro Limited
313 + *
314 + * This program is free software; you can redistribute it and/or
315 + * modify it under the terms of the GNU General Public License as
316 + * published by the Free Software Foundation version 2.
317 + *
318 + * This program is distributed "as is" WITHOUT ANY WARRANTY of any
319 + * kind, whether express or implied; without even the implied warranty
320 + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
321 + * GNU General Public License for more details.
322 + */
323 +
324 +#include <linux/cpumask.h>
325 +#include <linux/delay.h>
326 +#include <linux/errno.h>
327 +#include <linux/init.h>
328 +#include <linux/io.h>
329 +#include <linux/jiffies.h>
330 +#include <linux/of.h>
331 +#include <linux/sched.h>
332 +#include <linux/smp.h>
333 +
334 +#include <asm/cacheflush.h>
335 +#include <asm/smp.h>
336 +#include <asm/smp_plat.h>
337 +#include <asm/smp_scu.h>
338 +
339 +#include "bcm_nsp.h"
340 +
341 +/* Size of mapped Cortex A9 SCU address space */
342 +#define CORTEX_A9_SCU_SIZE     0x58
343 +
344 +#define SECONDARY_TIMEOUT_NS   NSEC_PER_MSEC   /* 1 msec (in nanoseconds) */
345 +#define BOOT_ADDR_CPUID_MASK   0x3
346 +
347 +/* Name of device node property defining secondary boot register location */
348 +#define OF_SECONDARY_BOOT      "secondary-boot-reg"
349 +
350 +/* I/O address of register used to coordinate secondary core startup */
351 +static u32     secondary_boot;
352 +
353 +static DEFINE_SPINLOCK(boot_lock);
354 +
355 +/*
356 + * Write pen_release in a way that is guaranteed to be visible to all
357 + * observers, irrespective of whether they're taking part in coherency
358 + * or not.  This is necessary for the hotplug code to work reliably.
359 + */
360 +static void write_pen_release(int val)
361 +{
362 +       pen_release = val;
363 +       /*
364 +        * Ensure write to pen_release is visible to the other cores,
365 +        * here - primary core
366 +        */
367 +       smp_wmb();
368 +       sync_cache_w(&pen_release);
369 +}
370 +
371 +/*
372 + * Enable the Cortex A9 Snoop Control Unit
373 + *
374 + * By the time this is called we already know there are multiple
375 + * cores present.  We assume we're running on a Cortex A9 processor,
376 + * so any trouble getting the base address register or getting the
377 + * SCU base is a problem.
378 + *
379 + * Return 0 if successful or an error code otherwise.
380 + */
381 +static int __init scu_a9_enable(void)
382 +{
383 +       unsigned long config_base;
384 +       void __iomem *scu_base;
385 +
386 +       if (!scu_a9_has_base()) {
387 +               pr_err("no configuration base address register!\n");
388 +               return -ENXIO;
389 +       }
390 +
391 +       /* Config base address register value is zero for uniprocessor */
392 +       config_base = scu_a9_get_base();
393 +       if (!config_base) {
394 +               pr_err("hardware reports only one core\n");
395 +               return -ENOENT;
396 +       }
397 +
398 +       scu_base = ioremap((phys_addr_t)config_base, CORTEX_A9_SCU_SIZE);
399 +       if (!scu_base) {
400 +               pr_err("failed to remap config base (%lu/%u) for SCU\n",
401 +                       config_base, CORTEX_A9_SCU_SIZE);
402 +               return -ENOMEM;
403 +       }
404 +
405 +       scu_enable(scu_base);
406 +
407 +       iounmap(scu_base);      /* That's the last we'll need of this */
408 +
409 +       return 0;
410 +}
411 +
412 +static int nsp_write_lut(void (*secondary_startup) (void))
413 +{
414 +       void __iomem *sku_rom_lut;
415 +       phys_addr_t secondary_startup_phy;
416 +
417 +       if (!secondary_boot) {
418 +               pr_warn("required secondary boot register not specified\n");
419 +               return -EINVAL;
420 +       }
421 +
422 +       sku_rom_lut = ioremap_nocache((phys_addr_t)secondary_boot,
423 +                                               sizeof(secondary_boot));
424 +       if (!sku_rom_lut) {
425 +               pr_warn("unable to ioremap SKU-ROM LUT register\n");
426 +               return -ENOMEM;
427 +       }
428 +
429 +       secondary_startup_phy = virt_to_phys(secondary_startup);
430 +       BUG_ON(secondary_startup_phy > (phys_addr_t)U32_MAX);
431 +
432 +       writel_relaxed(secondary_startup_phy, sku_rom_lut);
433 +       /*
434 +        * Ensure the write is visible to the secondary core.
435 +        */
436 +       smp_wmb();
437 +
438 +       iounmap(sku_rom_lut);
439 +
440 +       return 0;
441 +}
442 +
443 +static void nsp_secondary_init(unsigned int cpu)
444 +{
445 +       /*
446 +        * Let the primary cpu know we are out of holding pen.
447 +        */
448 +       write_pen_release(-1);
449 +
450 +       /*
451 +        * Synchronise with the boot thread.
452 +        */
453 +       spin_lock(&boot_lock);
454 +       spin_unlock(&boot_lock);
455 +}
456 +
457 +static void __init bcm_smp_prepare_cpus(unsigned int max_cpus)
458 +{
459 +       static cpumask_t only_cpu_0 = { CPU_BITS_CPU0 };
460 +       struct device_node *node;
461 +       int ret;
462 +
463 +       BUG_ON(secondary_boot);         /* We're called only once */
464 +
465 +       /*
466 +        * This function is only called via smp_ops->smp_prepare_cpu().
467 +        * That only happens if a "/cpus" device tree node exists
468 +        * and has an "enable-method" property that selects the SMP
469 +        * operations defined herein.
470 +        */
471 +       node = of_find_node_by_path("/cpus");
472 +       BUG_ON(!node);
473 +
474 +       /*
475 +        * Our secondary enable method requires a "secondary-boot-reg"
476 +        * property to specify a register address used to request the
477 +        * ROM code boot a secondary core.  If we have any trouble
478 +        * getting this we fall back to uniprocessor mode.
479 +        */
480 +       if (of_property_read_u32(node, OF_SECONDARY_BOOT, &secondary_boot)) {
481 +               pr_warn("%s: missing/invalid " OF_SECONDARY_BOOT " property\n",
482 +                       node->name);
483 +               ret = -ENOENT;          /* Arrange to disable SMP */
484 +               goto out;
485 +       }
486 +
487 +       /*
488 +        * Enable the SCU on Cortex A9 based SoCs.  If -ENOENT is
489 +        * returned, the SoC reported a uniprocessor configuration.
490 +        * We bail on any other error.
491 +        */
492 +       ret = scu_a9_enable();
493 +out:
494 +       of_node_put(node);
495 +       if (ret) {
496 +               /* Update the CPU present map to reflect uniprocessor mode */
497 +               pr_warn("disabling SMP\n");
498 +               init_cpu_present(&only_cpu_0);
499 +       }
500 +}
501 +
502 +/*
503 + * The ROM code has the secondary cores looping, waiting for an event.
504 + * When an event occurs each core examines the bottom two bits of the
505 + * secondary boot register.  When a core finds those bits contain its
506 + * own core id, it performs initialization, including computing its boot
507 + * address by clearing the boot register value's bottom two bits.  The
508 + * core signals that it is beginning its execution by writing its boot
509 + * address back to the secondary boot register, and finally jumps to
510 + * that address.
511 + *
512 + * So to start a core executing we need to:
513 + * - Encode the (hardware) CPU id with the bottom bits of the secondary
514 + *   start address.
515 + * - Write that value into the secondary boot register.
516 + * - Generate an event to wake up the secondary CPU(s).
517 + * - Wait for the secondary boot register to be re-written, which
518 + *   indicates the secondary core has started.
519 + */
520 +static int kona_boot_secondary(unsigned int cpu, struct task_struct *idle)
521 +{
522 +       void __iomem *boot_reg;
523 +       phys_addr_t boot_func;
524 +       u64 start_clock;
525 +       u32 cpu_id;
526 +       u32 boot_val;
527 +       bool timeout = false;
528 +
529 +       cpu_id = cpu_logical_map(cpu);
530 +       if (cpu_id & ~BOOT_ADDR_CPUID_MASK) {
531 +               pr_err("bad cpu id (%u > %u)\n", cpu_id, BOOT_ADDR_CPUID_MASK);
532 +               return -EINVAL;
533 +       }
534 +
535 +       if (!secondary_boot) {
536 +               pr_err("required secondary boot register not specified\n");
537 +               return -EINVAL;
538 +       }
539 +
540 +       boot_reg = ioremap_nocache((phys_addr_t)secondary_boot, sizeof(u32));
541 +       if (!boot_reg) {
542 +               pr_err("unable to map boot register for cpu %u\n", cpu_id);
543 +               return -ENOMEM;
544 +       }
545 +
546 +       /*
547 +        * Secondary cores will start in secondary_startup(),
548 +        * defined in "arch/arm/kernel/head.S"
549 +        */
550 +       boot_func = virt_to_phys(secondary_startup);
551 +       BUG_ON(boot_func & BOOT_ADDR_CPUID_MASK);
552 +       BUG_ON(boot_func > (phys_addr_t)U32_MAX);
553 +
554 +       /* The core to start is encoded in the low bits */
555 +       boot_val = (u32)boot_func | cpu_id;
556 +       writel_relaxed(boot_val, boot_reg);
557 +
558 +       sev();
559 +
560 +       /* The low bits will be cleared once the core has started */
561 +       start_clock = local_clock();
562 +       while (!timeout && readl_relaxed(boot_reg) == boot_val)
563 +               timeout = local_clock() - start_clock > SECONDARY_TIMEOUT_NS;
564 +
565 +       iounmap(boot_reg);
566 +
567 +       if (!timeout)
568 +               return 0;
569 +
570 +       pr_err("timeout waiting for cpu %u to start\n", cpu_id);
571 +
572 +       return -ENXIO;
573 +}
574 +
575 +static int nsp_boot_secondary(unsigned int cpu, struct task_struct *idle)
576 +{
577 +       unsigned long timeout;
578 +       int ret;
579 +
580 +       /*
581 +        * After wake up, secondary core branches to the startup
582 +        * address programmed at SKU ROM LUT location.
583 +        */
584 +       ret = nsp_write_lut(nsp_secondary_startup);
585 +       if (ret) {
586 +               pr_err("unable to write startup addr to SKU ROM LUT\n");
587 +               goto out;
588 +       }
589 +
590 +       /*
591 +        * The secondary processor is waiting to be released from
592 +        * the holding pen - release it, then wait for it to flag
593 +        * that it has been released by resetting pen_release.
594 +        */
595 +       spin_lock(&boot_lock);
596 +
597 +       write_pen_release(cpu_logical_map(cpu));
598 +       /*
599 +        * Send an Event to wake up the secondary core which is in
600 +        * WFE state. Updated pen_release should also be visible to
601 +        * the secondary core.
602 +        */
603 +       dsb_sev();
604 +
605 +       timeout = jiffies + (1 * HZ);
606 +       while (time_before(jiffies, timeout)) {
607 +               /* Make sure loads on other CPU is visible */
608 +               smp_rmb();
609 +               if (pen_release == -1)
610 +                       break;
611 +
612 +               udelay(10);
613 +       }
614 +
615 +       spin_unlock(&boot_lock);
616 +
617 +       ret = pen_release != -1 ? -ENXIO : 0;
618 +
619 +out:
620 +       return ret;
621 +}
622 +
623 +static struct smp_operations bcm_smp_ops __initdata = {
624 +       .smp_prepare_cpus       = bcm_smp_prepare_cpus,
625 +       .smp_boot_secondary     = kona_boot_secondary,
626 +};
627 +CPU_METHOD_OF_DECLARE(bcm_smp_bcm281xx, "brcm,bcm11351-cpu-method",
628 +                       &bcm_smp_ops);
629 +
630 +struct smp_operations nsp_smp_ops __initdata = {
631 +       .smp_prepare_cpus       = bcm_smp_prepare_cpus,
632 +       .smp_secondary_init     = nsp_secondary_init,
633 +       .smp_boot_secondary     = nsp_boot_secondary,
634 +};
635 +CPU_METHOD_OF_DECLARE(bcm_smp_nsp, "brcm,bcm-nsp-smp", &nsp_smp_ops);