aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBrian Norris <computersforpeace@gmail.com>2014-09-05 19:31:04 -0400
committerFlorian Fainelli <f.fainelli@gmail.com>2014-10-20 15:44:40 -0400
commit62639c2f5332a0f25b11806ddcfe1d95d3d635fb (patch)
tree20373f40cdfaab704facd0767c17df1746b026bf
parent81b43a6e2d072126df5eb016524819f6921262f3 (diff)
ARM: brcmstb: reintroduce SMP support
Support for SMP bringup of the B15 CPUs on Broadcom STB chips was added in commit 4fbe66d9903425156c193ae44c81c0f7557755c4 but was reverted in commit fc3e825fa91636a5d1b992e769b2d8279877bfad to address some late review comments. This reintroduces SMP support. Signed-off-by: Marc Carino <marc.ceeeee@gmail.com> Signed-off-by: Brian Norris <computersforpeace@gmail.com> Signed-off-by: Florian Fainelli <f.fainelli@gmail.com>
-rw-r--r--arch/arm/mach-bcm/Makefile2
-rw-r--r--arch/arm/mach-bcm/brcmstb.h19
-rw-r--r--arch/arm/mach-bcm/headsmp-brcmstb.S33
-rw-r--r--arch/arm/mach-bcm/platsmp-brcmstb.c329
4 files changed, 383 insertions, 0 deletions
diff --git a/arch/arm/mach-bcm/Makefile b/arch/arm/mach-bcm/Makefile
index 300ae4b79ae6..6710ea321220 100644
--- a/arch/arm/mach-bcm/Makefile
+++ b/arch/arm/mach-bcm/Makefile
@@ -38,5 +38,7 @@ obj-$(CONFIG_ARCH_BCM_5301X) += bcm_5301x.o
38obj-$(CONFIG_ARCH_BCM_63XX) := bcm63xx.o 38obj-$(CONFIG_ARCH_BCM_63XX) := bcm63xx.o
39 39
40ifeq ($(CONFIG_ARCH_BRCMSTB),y) 40ifeq ($(CONFIG_ARCH_BRCMSTB),y)
41CFLAGS_platsmp-brcmstb.o += -march=armv7-a
41obj-y += brcmstb.o 42obj-y += brcmstb.o
43obj-$(CONFIG_SMP) += headsmp-brcmstb.o platsmp-brcmstb.o
42endif 44endif
diff --git a/arch/arm/mach-bcm/brcmstb.h b/arch/arm/mach-bcm/brcmstb.h
new file mode 100644
index 000000000000..ec0c3d112b36
--- /dev/null
+++ b/arch/arm/mach-bcm/brcmstb.h
@@ -0,0 +1,19 @@
1/*
2 * Copyright (C) 2013-2014 Broadcom Corporation
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License as
6 * published by the Free Software Foundation version 2.
7 *
8 * This program is distributed "as is" WITHOUT ANY WARRANTY of any
9 * kind, whether express or implied; without even the implied warranty
10 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 */
13
14#ifndef __BRCMSTB_H__
15#define __BRCMSTB_H__
16
17void brcmstb_secondary_startup(void);
18
19#endif /* __BRCMSTB_H__ */
diff --git a/arch/arm/mach-bcm/headsmp-brcmstb.S b/arch/arm/mach-bcm/headsmp-brcmstb.S
new file mode 100644
index 000000000000..199c1ea58248
--- /dev/null
+++ b/arch/arm/mach-bcm/headsmp-brcmstb.S
@@ -0,0 +1,33 @@
1/*
2 * SMP boot code for secondary CPUs
3 * Based on arch/arm/mach-tegra/headsmp.S
4 *
5 * Copyright (C) 2010 NVIDIA, Inc.
6 * Copyright (C) 2013-2014 Broadcom Corporation
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License as
10 * published by the Free Software Foundation version 2.
11 *
12 * This program is distributed "as is" WITHOUT ANY WARRANTY of any
13 * kind, whether express or implied; without even the implied warranty
14 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 */
17
18#include <asm/assembler.h>
19#include <linux/linkage.h>
20#include <linux/init.h>
21
22 .section ".text.head", "ax"
23
24ENTRY(brcmstb_secondary_startup)
25 /*
26 * Ensure CPU is in a sane state by disabling all IRQs and switching
27 * into SVC mode.
28 */
29 setmode PSR_I_BIT | PSR_F_BIT | SVC_MODE, r0
30
31 bl v7_invalidate_l1
32 b secondary_startup
33ENDPROC(brcmstb_secondary_startup)
diff --git a/arch/arm/mach-bcm/platsmp-brcmstb.c b/arch/arm/mach-bcm/platsmp-brcmstb.c
new file mode 100644
index 000000000000..31c87a284a34
--- /dev/null
+++ b/arch/arm/mach-bcm/platsmp-brcmstb.c
@@ -0,0 +1,329 @@
1/*
2 * Broadcom STB CPU SMP and hotplug support for ARM
3 *
4 * Copyright (C) 2013-2014 Broadcom Corporation
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License as
8 * published by the Free Software Foundation version 2.
9 *
10 * This program is distributed "as is" WITHOUT ANY WARRANTY of any
11 * kind, whether express or implied; without even the implied warranty
12 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 */
15
16#include <linux/delay.h>
17#include <linux/errno.h>
18#include <linux/init.h>
19#include <linux/io.h>
20#include <linux/of_address.h>
21#include <linux/of_platform.h>
22#include <linux/printk.h>
23#include <linux/regmap.h>
24#include <linux/smp.h>
25#include <linux/mfd/syscon.h>
26
27#include <asm/cacheflush.h>
28#include <asm/cp15.h>
29#include <asm/mach-types.h>
30#include <asm/smp_plat.h>
31
32#include "brcmstb.h"
33
34enum {
35 ZONE_MAN_CLKEN_MASK = BIT(0),
36 ZONE_MAN_RESET_CNTL_MASK = BIT(1),
37 ZONE_MAN_MEM_PWR_MASK = BIT(4),
38 ZONE_RESERVED_1_MASK = BIT(5),
39 ZONE_MAN_ISO_CNTL_MASK = BIT(6),
40 ZONE_MANUAL_CONTROL_MASK = BIT(7),
41 ZONE_PWR_DN_REQ_MASK = BIT(9),
42 ZONE_PWR_UP_REQ_MASK = BIT(10),
43 ZONE_BLK_RST_ASSERT_MASK = BIT(12),
44 ZONE_PWR_OFF_STATE_MASK = BIT(25),
45 ZONE_PWR_ON_STATE_MASK = BIT(26),
46 ZONE_DPG_PWR_STATE_MASK = BIT(28),
47 ZONE_MEM_PWR_STATE_MASK = BIT(29),
48 ZONE_RESET_STATE_MASK = BIT(31),
49 CPU0_PWR_ZONE_CTRL_REG = 1,
50 CPU_RESET_CONFIG_REG = 2,
51};
52
53static void __iomem *cpubiuctrl_block;
54static void __iomem *hif_cont_block;
55static u32 cpu0_pwr_zone_ctrl_reg;
56static u32 cpu_rst_cfg_reg;
57static u32 hif_cont_reg;
58
59#ifdef CONFIG_HOTPLUG_CPU
60/*
61 * We must quiesce a dying CPU before it can be killed by the boot CPU. Because
62 * one or more cache may be disabled, we must flush to ensure coherency. We
63 * cannot use traditionl completion structures or spinlocks as they rely on
64 * coherency.
65 */
66static DEFINE_PER_CPU_ALIGNED(int, per_cpu_sw_state);
67
68static int per_cpu_sw_state_rd(u32 cpu)
69{
70 sync_cache_r(SHIFT_PERCPU_PTR(&per_cpu_sw_state, per_cpu_offset(cpu)));
71 return per_cpu(per_cpu_sw_state, cpu);
72}
73
74static void per_cpu_sw_state_wr(u32 cpu, int val)
75{
76 dmb();
77 per_cpu(per_cpu_sw_state, cpu) = val;
78 sync_cache_w(SHIFT_PERCPU_PTR(&per_cpu_sw_state, per_cpu_offset(cpu)));
79}
80#else
81static inline void per_cpu_sw_state_wr(u32 cpu, int val) { }
82#endif
83
84static void __iomem *pwr_ctrl_get_base(u32 cpu)
85{
86 void __iomem *base = cpubiuctrl_block + cpu0_pwr_zone_ctrl_reg;
87 base += (cpu_logical_map(cpu) * 4);
88 return base;
89}
90
91static u32 pwr_ctrl_rd(u32 cpu)
92{
93 void __iomem *base = pwr_ctrl_get_base(cpu);
94 return readl_relaxed(base);
95}
96
97static void pwr_ctrl_wr(u32 cpu, u32 val)
98{
99 void __iomem *base = pwr_ctrl_get_base(cpu);
100 writel(val, base);
101}
102
103static void cpu_rst_cfg_set(u32 cpu, int set)
104{
105 u32 val;
106 val = readl_relaxed(cpubiuctrl_block + cpu_rst_cfg_reg);
107 if (set)
108 val |= BIT(cpu_logical_map(cpu));
109 else
110 val &= ~BIT(cpu_logical_map(cpu));
111 writel_relaxed(val, cpubiuctrl_block + cpu_rst_cfg_reg);
112}
113
114static void cpu_set_boot_addr(u32 cpu, unsigned long boot_addr)
115{
116 const int reg_ofs = cpu_logical_map(cpu) * 8;
117 writel_relaxed(0, hif_cont_block + hif_cont_reg + reg_ofs);
118 writel_relaxed(boot_addr, hif_cont_block + hif_cont_reg + 4 + reg_ofs);
119}
120
121static void brcmstb_cpu_boot(u32 cpu)
122{
123 /* Mark this CPU as "up" */
124 per_cpu_sw_state_wr(cpu, 1);
125
126 /*
127 * Set the reset vector to point to the secondary_startup
128 * routine
129 */
130 cpu_set_boot_addr(cpu, virt_to_phys(brcmstb_secondary_startup));
131
132 /* Unhalt the cpu */
133 cpu_rst_cfg_set(cpu, 0);
134}
135
136static void brcmstb_cpu_power_on(u32 cpu)
137{
138 /*
139 * The secondary cores power was cut, so we must go through
140 * power-on initialization.
141 */
142 u32 tmp;
143
144 /* Request zone power up */
145 pwr_ctrl_wr(cpu, ZONE_PWR_UP_REQ_MASK);
146
147 /* Wait for the power up FSM to complete */
148 do {
149 tmp = pwr_ctrl_rd(cpu);
150 } while (!(tmp & ZONE_PWR_ON_STATE_MASK));
151}
152
153static int brcmstb_cpu_get_power_state(u32 cpu)
154{
155 int tmp = pwr_ctrl_rd(cpu);
156 return (tmp & ZONE_RESET_STATE_MASK) ? 0 : 1;
157}
158
159#ifdef CONFIG_HOTPLUG_CPU
160
161static void brcmstb_cpu_die(u32 cpu)
162{
163 v7_exit_coherency_flush(all);
164
165 per_cpu_sw_state_wr(cpu, 0);
166
167 /* Sit and wait to die */
168 wfi();
169
170 /* We should never get here... */
171 while (1)
172 ;
173}
174
175static int brcmstb_cpu_kill(u32 cpu)
176{
177 u32 tmp;
178
179 while (per_cpu_sw_state_rd(cpu))
180 ;
181
182 /* Program zone reset */
183 pwr_ctrl_wr(cpu, ZONE_RESET_STATE_MASK | ZONE_BLK_RST_ASSERT_MASK |
184 ZONE_PWR_DN_REQ_MASK);
185
186 /* Verify zone reset */
187 tmp = pwr_ctrl_rd(cpu);
188 if (!(tmp & ZONE_RESET_STATE_MASK))
189 pr_err("%s: Zone reset bit for CPU %d not asserted!\n",
190 __func__, cpu);
191
192 /* Wait for power down */
193 do {
194 tmp = pwr_ctrl_rd(cpu);
195 } while (!(tmp & ZONE_PWR_OFF_STATE_MASK));
196
197 /* Flush pipeline before resetting CPU */
198 mb();
199
200 /* Assert reset on the CPU */
201 cpu_rst_cfg_set(cpu, 1);
202
203 return 1;
204}
205
206#endif /* CONFIG_HOTPLUG_CPU */
207
208static int __init setup_hifcpubiuctrl_regs(struct device_node *np)
209{
210 int rc = 0;
211 char *name;
212 struct device_node *syscon_np = NULL;
213
214 name = "syscon-cpu";
215
216 syscon_np = of_parse_phandle(np, name, 0);
217 if (!syscon_np) {
218 pr_err("can't find phandle %s\n", name);
219 rc = -EINVAL;
220 goto cleanup;
221 }
222
223 cpubiuctrl_block = of_iomap(syscon_np, 0);
224 if (!cpubiuctrl_block) {
225 pr_err("iomap failed for cpubiuctrl_block\n");
226 rc = -EINVAL;
227 goto cleanup;
228 }
229
230 rc = of_property_read_u32_index(np, name, CPU0_PWR_ZONE_CTRL_REG,
231 &cpu0_pwr_zone_ctrl_reg);
232 if (rc) {
233 pr_err("failed to read 1st entry from %s property (%d)\n", name,
234 rc);
235 rc = -EINVAL;
236 goto cleanup;
237 }
238
239 rc = of_property_read_u32_index(np, name, CPU_RESET_CONFIG_REG,
240 &cpu_rst_cfg_reg);
241 if (rc) {
242 pr_err("failed to read 2nd entry from %s property (%d)\n", name,
243 rc);
244 rc = -EINVAL;
245 goto cleanup;
246 }
247
248cleanup:
249 of_node_put(syscon_np);
250 return rc;
251}
252
253static int __init setup_hifcont_regs(struct device_node *np)
254{
255 int rc = 0;
256 char *name;
257 struct device_node *syscon_np = NULL;
258
259 name = "syscon-cont";
260
261 syscon_np = of_parse_phandle(np, name, 0);
262 if (!syscon_np) {
263 pr_err("can't find phandle %s\n", name);
264 rc = -EINVAL;
265 goto cleanup;
266 }
267
268 hif_cont_block = of_iomap(syscon_np, 0);
269 if (!hif_cont_block) {
270 pr_err("iomap failed for hif_cont_block\n");
271 rc = -EINVAL;
272 goto cleanup;
273 }
274
275 /* Offset is at top of hif_cont_block */
276 hif_cont_reg = 0;
277
278cleanup:
279 of_node_put(syscon_np);
280 return rc;
281}
282
283static void __init brcmstb_cpu_ctrl_setup(unsigned int max_cpus)
284{
285 int rc;
286 struct device_node *np;
287 char *name;
288
289 name = "brcm,brcmstb-smpboot";
290 np = of_find_compatible_node(NULL, NULL, name);
291 if (!np) {
292 pr_err("can't find compatible node %s\n", name);
293 return;
294 }
295
296 rc = setup_hifcpubiuctrl_regs(np);
297 if (rc)
298 return;
299
300 rc = setup_hifcont_regs(np);
301 if (rc)
302 return;
303}
304
305static int brcmstb_boot_secondary(unsigned int cpu, struct task_struct *idle)
306{
307 /* Missing the brcm,brcmstb-smpboot DT node? */
308 if (!cpubiuctrl_block || !hif_cont_block)
309 return -ENODEV;
310
311 /* Bring up power to the core if necessary */
312 if (brcmstb_cpu_get_power_state(cpu) == 0)
313 brcmstb_cpu_power_on(cpu);
314
315 brcmstb_cpu_boot(cpu);
316
317 return 0;
318}
319
320static struct smp_operations brcmstb_smp_ops __initdata = {
321 .smp_prepare_cpus = brcmstb_cpu_ctrl_setup,
322 .smp_boot_secondary = brcmstb_boot_secondary,
323#ifdef CONFIG_HOTPLUG_CPU
324 .cpu_kill = brcmstb_cpu_kill,
325 .cpu_die = brcmstb_cpu_die,
326#endif
327};
328
329CPU_METHOD_OF_DECLARE(brcmstb_smp, "brcm,brahma-b15", &brcmstb_smp_ops);