023-ARM-BCM-Add-SMP-support-for-Broadcom-NSP.patch 17 KB

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