aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHeiko Stuebner <heiko@sntech.de>2013-06-17 16:29:23 -0400
committerHeiko Stuebner <heiko@sntech.de>2014-03-01 11:21:57 -0500
commita7a2b3118b410fb3cd3a8363b157c56f4211ee05 (patch)
tree28f762f27421a638fdbf1aa4a2c7c1dfadddd700
parent46b8219c519be424ab96b5b7fabee171c1e6b4c7 (diff)
ARM: rockchip: add smp bringup code
This adds the necessary smp-operations and startup code to use additional cores on Rockchip SoCs. We currently hog the power management unit in the smp code, as it is necessary to control the power to the cpu core and nothing else is currently using it, so a generic implementation can be done later. Signed-off-by: Heiko Stuebner <heiko@sntech.de> Tested-by: Ulrich Prinz <ulrich.prinz@googlemail.com>
-rw-r--r--arch/arm/mach-rockchip/Makefile1
-rw-r--r--arch/arm/mach-rockchip/core.h22
-rw-r--r--arch/arm/mach-rockchip/headsmp.S30
-rw-r--r--arch/arm/mach-rockchip/platsmp.c184
-rw-r--r--arch/arm/mach-rockchip/rockchip.c2
5 files changed, 239 insertions, 0 deletions
diff --git a/arch/arm/mach-rockchip/Makefile b/arch/arm/mach-rockchip/Makefile
index 1547d4fc920a..4377a1436a98 100644
--- a/arch/arm/mach-rockchip/Makefile
+++ b/arch/arm/mach-rockchip/Makefile
@@ -1 +1,2 @@
1obj-$(CONFIG_ARCH_ROCKCHIP) += rockchip.o 1obj-$(CONFIG_ARCH_ROCKCHIP) += rockchip.o
2obj-$(CONFIG_SMP) += headsmp.o platsmp.o
diff --git a/arch/arm/mach-rockchip/core.h b/arch/arm/mach-rockchip/core.h
new file mode 100644
index 000000000000..e2e7c9dbb200
--- /dev/null
+++ b/arch/arm/mach-rockchip/core.h
@@ -0,0 +1,22 @@
1/*
2 * Copyright (c) 2013 MundoReader S.L.
3 * Author: Heiko Stuebner <heiko@sntech.de>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 */
15
16extern char rockchip_secondary_trampoline;
17extern char rockchip_secondary_trampoline_end;
18
19extern unsigned long rockchip_boot_fn;
20extern void rockchip_secondary_startup(void);
21
22extern struct smp_operations rockchip_smp_ops;
diff --git a/arch/arm/mach-rockchip/headsmp.S b/arch/arm/mach-rockchip/headsmp.S
new file mode 100644
index 000000000000..73206e360e31
--- /dev/null
+++ b/arch/arm/mach-rockchip/headsmp.S
@@ -0,0 +1,30 @@
1/*
2 * Copyright (c) 2013 MundoReader S.L.
3 * Author: Heiko Stuebner <heiko@sntech.de>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 */
15#include <linux/linkage.h>
16#include <linux/init.h>
17
18ENTRY(rockchip_secondary_startup)
19 bl v7_invalidate_l1
20 b secondary_startup
21ENDPROC(rockchip_secondary_startup)
22
23ENTRY(rockchip_secondary_trampoline)
24 ldr pc, 1f
25ENDPROC(rockchip_secondary_trampoline)
26 .globl rockchip_boot_fn
27rockchip_boot_fn:
281: .space 4
29
30ENTRY(rockchip_secondary_trampoline_end)
diff --git a/arch/arm/mach-rockchip/platsmp.c b/arch/arm/mach-rockchip/platsmp.c
new file mode 100644
index 000000000000..dbfa5a26cfff
--- /dev/null
+++ b/arch/arm/mach-rockchip/platsmp.c
@@ -0,0 +1,184 @@
1/*
2 * Copyright (c) 2013 MundoReader S.L.
3 * Author: Heiko Stuebner <heiko@sntech.de>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * 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/init.h>
18#include <linux/smp.h>
19#include <linux/io.h>
20#include <linux/of.h>
21#include <linux/of_address.h>
22
23#include <asm/cacheflush.h>
24#include <asm/smp_scu.h>
25#include <asm/smp_plat.h>
26#include <asm/mach/map.h>
27
28#include "core.h"
29
30static void __iomem *scu_base_addr;
31static void __iomem *sram_base_addr;
32static int ncores;
33
34#define PMU_PWRDN_CON 0x08
35#define PMU_PWRDN_ST 0x0c
36
37#define PMU_PWRDN_SCU 4
38
39static void __iomem *pmu_base_addr;
40
41static inline bool pmu_power_domain_is_on(int pd)
42{
43 return !(readl_relaxed(pmu_base_addr + PMU_PWRDN_ST) & BIT(pd));
44}
45
46static void pmu_set_power_domain(int pd, bool on)
47{
48 u32 val = readl_relaxed(pmu_base_addr + PMU_PWRDN_CON);
49 if (on)
50 val &= ~BIT(pd);
51 else
52 val |= BIT(pd);
53 writel(val, pmu_base_addr + PMU_PWRDN_CON);
54
55 while (pmu_power_domain_is_on(pd) != on) { }
56}
57
58/*
59 * Handling of CPU cores
60 */
61
62static int __cpuinit rockchip_boot_secondary(unsigned int cpu,
63 struct task_struct *idle)
64{
65 if (!sram_base_addr || !pmu_base_addr) {
66 pr_err("%s: sram or pmu missing for cpu boot\n", __func__);
67 return -ENXIO;
68 }
69
70 if (cpu >= ncores) {
71 pr_err("%s: cpu %d outside maximum number of cpus %d\n",
72 __func__, cpu, ncores);
73 return -ENXIO;
74 }
75
76 /* start the core */
77 pmu_set_power_domain(0 + cpu, true);
78
79 return 0;
80}
81
82/**
83 * rockchip_smp_prepare_sram - populate necessary sram block
84 * Starting cores execute the code residing at the start of the on-chip sram
85 * after power-on. Therefore make sure, this sram region is reserved and
86 * big enough. After this check, copy the trampoline code that directs the
87 * core to the real startup code in ram into the sram-region.
88 * @node: mmio-sram device node
89 */
90static int __init rockchip_smp_prepare_sram(struct device_node *node)
91{
92 unsigned int trampoline_sz = &rockchip_secondary_trampoline_end -
93 &rockchip_secondary_trampoline;
94 struct resource res;
95 unsigned int rsize;
96 int ret;
97
98 ret = of_address_to_resource(node, 0, &res);
99 if (ret < 0) {
100 pr_err("%s: could not get address for node %s\n",
101 __func__, node->full_name);
102 return ret;
103 }
104
105 rsize = resource_size(&res);
106 if (rsize < trampoline_sz) {
107 pr_err("%s: reserved block with size 0x%x is to small for trampoline size 0x%x\n",
108 __func__, rsize, trampoline_sz);
109 return -EINVAL;
110 }
111
112 sram_base_addr = of_iomap(node, 0);
113
114 /* set the boot function for the sram code */
115 rockchip_boot_fn = virt_to_phys(rockchip_secondary_startup);
116
117 /* copy the trampoline to sram, that runs during startup of the core */
118 memcpy(sram_base_addr, &rockchip_secondary_trampoline, trampoline_sz);
119 flush_cache_all();
120 outer_clean_range(0, trampoline_sz);
121
122 dsb_sev();
123
124 return 0;
125}
126
127static void __init rockchip_smp_prepare_cpus(unsigned int max_cpus)
128{
129 struct device_node *node;
130 unsigned int i;
131
132 node = of_find_compatible_node(NULL, NULL, "arm,cortex-a9-scu");
133 if (!node) {
134 pr_err("%s: missing scu\n", __func__);
135 return;
136 }
137
138 scu_base_addr = of_iomap(node, 0);
139 if (!scu_base_addr) {
140 pr_err("%s: could not map scu registers\n", __func__);
141 return;
142 }
143
144 node = of_find_compatible_node(NULL, NULL, "rockchip,rk3066-smp-sram");
145 if (!node) {
146 pr_err("%s: could not find sram dt node\n", __func__);
147 return;
148 }
149
150 if (rockchip_smp_prepare_sram(node))
151 return;
152
153 node = of_find_compatible_node(NULL, NULL, "rockchip,rk3066-pmu");
154 if (!node) {
155 pr_err("%s: could not find sram dt node\n", __func__);
156 return;
157 }
158
159 pmu_base_addr = of_iomap(node, 0);
160 if (!pmu_base_addr) {
161 pr_err("%s: could not map pmu registers\n", __func__);
162 return;
163 }
164
165 /* enable the SCU power domain */
166 pmu_set_power_domain(PMU_PWRDN_SCU, true);
167
168 /*
169 * While the number of cpus is gathered from dt, also get the number
170 * of cores from the scu to verify this value when booting the cores.
171 */
172 ncores = scu_get_core_count(scu_base_addr);
173
174 scu_enable(scu_base_addr);
175
176 /* Make sure that all cores except the first are really off */
177 for (i = 1; i < ncores; i++)
178 pmu_set_power_domain(0 + i, false);
179}
180
181struct smp_operations rockchip_smp_ops __initdata = {
182 .smp_prepare_cpus = rockchip_smp_prepare_cpus,
183 .smp_boot_secondary = rockchip_boot_secondary,
184};
diff --git a/arch/arm/mach-rockchip/rockchip.c b/arch/arm/mach-rockchip/rockchip.c
index 82c0b0709712..d211d6fa0d98 100644
--- a/arch/arm/mach-rockchip/rockchip.c
+++ b/arch/arm/mach-rockchip/rockchip.c
@@ -22,6 +22,7 @@
22#include <asm/mach/arch.h> 22#include <asm/mach/arch.h>
23#include <asm/mach/map.h> 23#include <asm/mach/map.h>
24#include <asm/hardware/cache-l2x0.h> 24#include <asm/hardware/cache-l2x0.h>
25#include "core.h"
25 26
26static void __init rockchip_dt_init(void) 27static void __init rockchip_dt_init(void)
27{ 28{
@@ -38,6 +39,7 @@ static const char * const rockchip_board_dt_compat[] = {
38}; 39};
39 40
40DT_MACHINE_START(ROCKCHIP_DT, "Rockchip Cortex-A9 (Device Tree)") 41DT_MACHINE_START(ROCKCHIP_DT, "Rockchip Cortex-A9 (Device Tree)")
42 .smp = smp_ops(rockchip_smp_ops),
41 .init_machine = rockchip_dt_init, 43 .init_machine = rockchip_dt_init,
42 .dt_compat = rockchip_board_dt_compat, 44 .dt_compat = rockchip_board_dt_compat,
43MACHINE_END 45MACHINE_END