aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlex Elder <elder@linaro.org>2014-06-30 18:15:37 -0400
committerMatt Porter <mporter@linaro.org>2014-07-28 09:42:24 -0400
commit9a5a110eb9ba137840cacb06cead746a6a238b09 (patch)
tree876556b77e083f89d1d9bae7e00b3e30a52c49b0
parent9a3c4145af32125c5ee39c0272662b47307a8323 (diff)
ARM: add SMP support for Broadcom mobile SoCs
This patch adds SMP support for BCM281XX and BCM21664 family SoCs. This feature is controlled with a distinct config option such that an SMP-enabled multi-v7 binary can be configured to run these SoCs in uniprocessor mode. Since this SMP functionality is used for multiple Broadcom mobile chip families the config option is called ARCH_BCM_MOBILE_SMP (for lack of a better name). On SoCs of this type, the secondary core is not held in reset on power-on. Instead it loops in a ROM-based holding pen. To release it, one must write into a special register a jump address whose low-order bits have been replaced with a secondary core's id, then trigger an event with SEV. On receipt of an event, the ROM code will examine the register's contents, and if the low-order bits match its cpu id, it will clear them and write the value back to the register just prior to jumping to the address specified. The location of the special register is defined in the device tree using a "secondary-boot-reg" property in a node whose "enable-method" matches. Derived from code originally provided by Ray Jui <rjui@broadcom.com> Signed-off-by: Alex Elder <elder@linaro.org> Signed-off-by: Matt Porter <mporter@linaro.org>
-rw-r--r--arch/arm/mach-bcm/Kconfig18
-rw-r--r--arch/arm/mach-bcm/Makefile3
-rw-r--r--arch/arm/mach-bcm/kona_smp.c202
3 files changed, 220 insertions, 3 deletions
diff --git a/arch/arm/mach-bcm/Kconfig b/arch/arm/mach-bcm/Kconfig
index 41c839167e87..6bafa2dbaece 100644
--- a/arch/arm/mach-bcm/Kconfig
+++ b/arch/arm/mach-bcm/Kconfig
@@ -9,7 +9,6 @@ config ARCH_BCM_MOBILE
9 bool "Broadcom Mobile SoC Support" if ARCH_MULTI_V7 9 bool "Broadcom Mobile SoC Support" if ARCH_MULTI_V7
10 select ARCH_REQUIRE_GPIOLIB 10 select ARCH_REQUIRE_GPIOLIB
11 select ARM_ERRATA_754322 11 select ARM_ERRATA_754322
12 select ARM_ERRATA_764369 if SMP
13 select ARM_ERRATA_775420 12 select ARM_ERRATA_775420
14 select ARM_GIC 13 select ARM_GIC
15 select GPIO_BCM_KONA 14 select GPIO_BCM_KONA
@@ -26,16 +25,18 @@ menu "Broadcom Mobile SoC Selection"
26config ARCH_BCM_281XX 25config ARCH_BCM_281XX
27 bool "Broadcom BCM281XX SoC family" 26 bool "Broadcom BCM281XX SoC family"
28 default y 27 default y
28 select HAVE_SMP
29 help 29 help
30 Enable support for the the BCM281XX family, which includes 30 Enable support for the BCM281XX family, which includes
31 BCM11130, BCM11140, BCM11351, BCM28145 and BCM28155 31 BCM11130, BCM11140, BCM11351, BCM28145 and BCM28155
32 variants. 32 variants.
33 33
34config ARCH_BCM_21664 34config ARCH_BCM_21664
35 bool "Broadcom BCM21664 SoC family" 35 bool "Broadcom BCM21664 SoC family"
36 default y 36 default y
37 select HAVE_SMP
37 help 38 help
38 Enable support for the the BCM21664 family, which includes 39 Enable support for the BCM21664 family, which includes
39 BCM21663 and BCM21664 variants. 40 BCM21663 and BCM21664 variants.
40 41
41config ARCH_BCM_MOBILE_L2_CACHE 42config ARCH_BCM_MOBILE_L2_CACHE
@@ -49,6 +50,17 @@ config ARCH_BCM_MOBILE_SMC
49 bool 50 bool
50 depends on ARCH_BCM_281XX || ARCH_BCM_21664 51 depends on ARCH_BCM_281XX || ARCH_BCM_21664
51 52
53config ARCH_BCM_MOBILE_SMP
54 bool "Broadcom mobile SoC SMP support"
55 depends on (ARCH_BCM_281XX || ARCH_BCM_21664) && SMP
56 default y
57 select HAVE_ARM_SCU
58 select ARM_ERRATA_764369
59 help
60 SMP support for the BCM281XX and BCM21664 SoC families.
61 Provided as an option so SMP support for SoCs of this type
62 can be disabled for an SMP-enabled kernel.
63
52endmenu 64endmenu
53 65
54endif 66endif
diff --git a/arch/arm/mach-bcm/Makefile b/arch/arm/mach-bcm/Makefile
index 731292114975..2eaafc848f4d 100644
--- a/arch/arm/mach-bcm/Makefile
+++ b/arch/arm/mach-bcm/Makefile
@@ -16,6 +16,9 @@ obj-$(CONFIG_ARCH_BCM_281XX) += board_bcm281xx.o
16# BCM21664 16# BCM21664
17obj-$(CONFIG_ARCH_BCM_21664) += board_bcm21664.o 17obj-$(CONFIG_ARCH_BCM_21664) += board_bcm21664.o
18 18
19# BCM281XX and BCM21664 SMP support
20obj-$(CONFIG_ARCH_BCM_MOBILE_SMP) += kona_smp.o
21
19# BCM281XX and BCM21664 L2 cache control 22# BCM281XX and BCM21664 L2 cache control
20obj-$(CONFIG_ARCH_BCM_MOBILE_L2_CACHE) += kona_l2_cache.o 23obj-$(CONFIG_ARCH_BCM_MOBILE_L2_CACHE) += kona_l2_cache.o
21 24
diff --git a/arch/arm/mach-bcm/kona_smp.c b/arch/arm/mach-bcm/kona_smp.c
new file mode 100644
index 000000000000..66a0465528a5
--- /dev/null
+++ b/arch/arm/mach-bcm/kona_smp.c
@@ -0,0 +1,202 @@
1/*
2 * Copyright (C) 2014 Broadcom Corporation
3 * Copyright 2014 Linaro Limited
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License as
7 * published by the Free Software Foundation version 2.
8 *
9 * This program is distributed "as is" WITHOUT ANY WARRANTY of any
10 * kind, whether express or implied; without even the implied warranty
11 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 */
14
15#include <linux/init.h>
16#include <linux/errno.h>
17#include <linux/io.h>
18#include <linux/of.h>
19#include <linux/sched.h>
20
21#include <asm/smp.h>
22#include <asm/smp_plat.h>
23#include <asm/smp_scu.h>
24
25/* Size of mapped Cortex A9 SCU address space */
26#define CORTEX_A9_SCU_SIZE 0x58
27
28#define SECONDARY_TIMEOUT_NS NSEC_PER_MSEC /* 1 msec (in nanoseconds) */
29#define BOOT_ADDR_CPUID_MASK 0x3
30
31/* Name of device node property defining secondary boot register location */
32#define OF_SECONDARY_BOOT "secondary-boot-reg"
33
34/* I/O address of register used to coordinate secondary core startup */
35static u32 secondary_boot;
36
37/*
38 * Enable the Cortex A9 Snoop Control Unit
39 *
40 * By the time this is called we already know there are multiple
41 * cores present. We assume we're running on a Cortex A9 processor,
42 * so any trouble getting the base address register or getting the
43 * SCU base is a problem.
44 *
45 * Return 0 if successful or an error code otherwise.
46 */
47static int __init scu_a9_enable(void)
48{
49 unsigned long config_base;
50 void __iomem *scu_base;
51
52 if (!scu_a9_has_base()) {
53 pr_err("no configuration base address register!\n");
54 return -ENXIO;
55 }
56
57 /* Config base address register value is zero for uniprocessor */
58 config_base = scu_a9_get_base();
59 if (!config_base) {
60 pr_err("hardware reports only one core\n");
61 return -ENOENT;
62 }
63
64 scu_base = ioremap((phys_addr_t)config_base, CORTEX_A9_SCU_SIZE);
65 if (!scu_base) {
66 pr_err("failed to remap config base (%lu/%u) for SCU\n",
67 config_base, CORTEX_A9_SCU_SIZE);
68 return -ENOMEM;
69 }
70
71 scu_enable(scu_base);
72
73 iounmap(scu_base); /* That's the last we'll need of this */
74
75 return 0;
76}
77
78static void __init bcm_smp_prepare_cpus(unsigned int max_cpus)
79{
80 static cpumask_t only_cpu_0 = { CPU_BITS_CPU0 };
81 struct device_node *node;
82 int ret;
83
84 BUG_ON(secondary_boot); /* We're called only once */
85
86 /*
87 * This function is only called via smp_ops->smp_prepare_cpu().
88 * That only happens if a "/cpus" device tree node exists
89 * and has an "enable-method" property that selects the SMP
90 * operations defined herein.
91 */
92 node = of_find_node_by_path("/cpus");
93 BUG_ON(!node);
94
95 /*
96 * Our secondary enable method requires a "secondary-boot-reg"
97 * property to specify a register address used to request the
98 * ROM code boot a secondary code. If we have any trouble
99 * getting this we fall back to uniprocessor mode.
100 */
101 if (of_property_read_u32(node, OF_SECONDARY_BOOT, &secondary_boot)) {
102 pr_err("%s: missing/invalid " OF_SECONDARY_BOOT " property\n",
103 node->name);
104 ret = -ENOENT; /* Arrange to disable SMP */
105 goto out;
106 }
107
108 /*
109 * Enable the SCU on Cortex A9 based SoCs. If -ENOENT is
110 * returned, the SoC reported a uniprocessor configuration.
111 * We bail on any other error.
112 */
113 ret = scu_a9_enable();
114out:
115 of_node_put(node);
116 if (ret) {
117 /* Update the CPU present map to reflect uniprocessor mode */
118 BUG_ON(ret != -ENOENT);
119 pr_warn("disabling SMP\n");
120 init_cpu_present(&only_cpu_0);
121 }
122}
123
124/*
125 * The ROM code has the secondary cores looping, waiting for an event.
126 * When an event occurs each core examines the bottom two bits of the
127 * secondary boot register. When a core finds those bits contain its
128 * own core id, it performs initialization, including computing its boot
129 * address by clearing the boot register value's bottom two bits. The
130 * core signals that it is beginning its execution by writing its boot
131 * address back to the secondary boot register, and finally jumps to
132 * that address.
133 *
134 * So to start a core executing we need to:
135 * - Encode the (hardware) CPU id with the bottom bits of the secondary
136 * start address.
137 * - Write that value into the secondary boot register.
138 * - Generate an event to wake up the secondary CPU(s).
139 * - Wait for the secondary boot register to be re-written, which
140 * indicates the secondary core has started.
141 */
142static int bcm_boot_secondary(unsigned int cpu, struct task_struct *idle)
143{
144 void __iomem *boot_reg;
145 phys_addr_t boot_func;
146 u64 start_clock;
147 u32 cpu_id;
148 u32 boot_val;
149 bool timeout = false;
150
151 cpu_id = cpu_logical_map(cpu);
152 if (cpu_id & ~BOOT_ADDR_CPUID_MASK) {
153 pr_err("bad cpu id (%u > %u)\n", cpu_id, BOOT_ADDR_CPUID_MASK);
154 return -EINVAL;
155 }
156
157 if (!secondary_boot) {
158 pr_err("required secondary boot register not specified\n");
159 return -EINVAL;
160 }
161
162 boot_reg = ioremap_nocache((phys_addr_t)secondary_boot, sizeof(u32));
163 if (!boot_reg) {
164 pr_err("unable to map boot register for cpu %u\n", cpu_id);
165 return -ENOSYS;
166 }
167
168 /*
169 * Secondary cores will start in secondary_startup(),
170 * defined in "arch/arm/kernel/head.S"
171 */
172 boot_func = virt_to_phys(secondary_startup);
173 BUG_ON(boot_func & BOOT_ADDR_CPUID_MASK);
174 BUG_ON(boot_func > (phys_addr_t)U32_MAX);
175
176 /* The core to start is encoded in the low bits */
177 boot_val = (u32)boot_func | cpu_id;
178 writel_relaxed(boot_val, boot_reg);
179
180 sev();
181
182 /* The low bits will be cleared once the core has started */
183 start_clock = local_clock();
184 while (!timeout && readl_relaxed(boot_reg) == boot_val)
185 timeout = local_clock() - start_clock > SECONDARY_TIMEOUT_NS;
186
187 iounmap(boot_reg);
188
189 if (!timeout)
190 return 0;
191
192 pr_err("timeout waiting for cpu %u to start\n", cpu_id);
193
194 return -ENOSYS;
195}
196
197static struct smp_operations bcm_smp_ops __initdata = {
198 .smp_prepare_cpus = bcm_smp_prepare_cpus,
199 .smp_boot_secondary = bcm_boot_secondary,
200};
201CPU_METHOD_OF_DECLARE(bcm_smp_bcm281xx, "brcm,bcm11351-cpu-method",
202 &bcm_smp_ops);