aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorArnd Bergmann <arnd@arndb.de>2017-10-30 06:23:13 -0400
committerArnd Bergmann <arnd@arndb.de>2017-10-30 06:23:13 -0400
commit682e3efa4d23c0587eba784ede6ce6fcff5acee5 (patch)
tree7e59ea9663d6d4c8cd9042cd3ef6b15b370e5bb5
parent16511ee20b1febad53720210673f82a46867a5c0 (diff)
parentdea54268f3925d05e75b9c6dec487a6515dc0ed5 (diff)
Merge tag 'amlogic-soc' of git://git.kernel.org/pub/scm/linux/kernel/git/khilman/linux-amlogic into next/soc
Pull "Amlogic SoC updates for v4.15" from Kevin Hilman: - add SMP support to Meson8/8b * tag 'amlogic-soc' of git://git.kernel.org/pub/scm/linux/kernel/git/khilman/linux-amlogic: ARM: meson: enable MESON_IRQ_GPIO in Kconfig for meson8b ARM: meson: Add SMP bringup code for Meson8 and Meson8b ARM: smp_scu: allow the platform code to read the SCU CPU status ARM: smp_scu: add a helper for powering on a specific CPU dt-bindings: Amlogic: Add Meson8 and Meson8b SMP related documentation
-rw-r--r--Documentation/devicetree/bindings/arm/amlogic/pmu.txt18
-rw-r--r--Documentation/devicetree/bindings/arm/amlogic/smp-sram.txt32
-rw-r--r--Documentation/devicetree/bindings/arm/cpus.txt2
-rw-r--r--arch/arm/Makefile1
-rw-r--r--arch/arm/include/asm/smp_scu.h12
-rw-r--r--arch/arm/kernel/smp_scu.c43
-rw-r--r--arch/arm/mach-meson/Kconfig2
-rw-r--r--arch/arm/mach-meson/Makefile1
-rw-r--r--arch/arm/mach-meson/platsmp.c440
9 files changed, 545 insertions, 6 deletions
diff --git a/Documentation/devicetree/bindings/arm/amlogic/pmu.txt b/Documentation/devicetree/bindings/arm/amlogic/pmu.txt
new file mode 100644
index 000000000000..72f8d08198b6
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/amlogic/pmu.txt
@@ -0,0 +1,18 @@
1Amlogic Meson8 and Meson8b power-management-unit:
2-------------------------------------------------
3
4The pmu is used to turn off and on different power domains of the SoCs
5This includes the power to the CPU cores.
6
7Required node properties:
8- compatible value : depending on the SoC this should be one of:
9 "amlogic,meson8-pmu"
10 "amlogic,meson8b-pmu"
11- reg : physical base address and the size of the registers window
12
13Example:
14
15 pmu@c81000e4 {
16 compatible = "amlogic,meson8b-pmu", "syscon";
17 reg = <0xc81000e0 0x18>;
18 };
diff --git a/Documentation/devicetree/bindings/arm/amlogic/smp-sram.txt b/Documentation/devicetree/bindings/arm/amlogic/smp-sram.txt
new file mode 100644
index 000000000000..3473ddaadfac
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/amlogic/smp-sram.txt
@@ -0,0 +1,32 @@
1Amlogic Meson8 and Meson8b SRAM for smp bringup:
2------------------------------------------------
3
4Amlogic's SMP-capable SoCs use part of the sram for the bringup of the cores.
5Once the core gets powered up it executes the code that is residing at a
6specific location.
7
8Therefore a reserved section sub-node has to be added to the mmio-sram
9declaration.
10
11Required sub-node properties:
12- compatible : depending on the SoC this should be one of:
13 "amlogic,meson8-smp-sram"
14 "amlogic,meson8b-smp-sram"
15
16The rest of the properties should follow the generic mmio-sram discription
17found in ../../misc/sram.txt
18
19Example:
20
21 sram: sram@d9000000 {
22 compatible = "mmio-sram";
23 reg = <0xd9000000 0x20000>;
24 #address-cells = <1>;
25 #size-cells = <1>;
26 ranges = <0 0xd9000000 0x20000>;
27
28 smp-sram@1ff80 {
29 compatible = "amlogic,meson8b-smp-sram";
30 reg = <0x1ff80 0x8>;
31 };
32 };
diff --git a/Documentation/devicetree/bindings/arm/cpus.txt b/Documentation/devicetree/bindings/arm/cpus.txt
index b92f12bd5244..a0009b72e9be 100644
--- a/Documentation/devicetree/bindings/arm/cpus.txt
+++ b/Documentation/devicetree/bindings/arm/cpus.txt
@@ -197,6 +197,8 @@ described below.
197 "actions,s500-smp" 197 "actions,s500-smp"
198 "allwinner,sun6i-a31" 198 "allwinner,sun6i-a31"
199 "allwinner,sun8i-a23" 199 "allwinner,sun8i-a23"
200 "amlogic,meson8-smp"
201 "amlogic,meson8b-smp"
200 "arm,realview-smp" 202 "arm,realview-smp"
201 "brcm,bcm11351-cpu-method" 203 "brcm,bcm11351-cpu-method"
202 "brcm,bcm23550" 204 "brcm,bcm23550"
diff --git a/arch/arm/Makefile b/arch/arm/Makefile
index 47d3a1ab08d2..82faa958ab88 100644
--- a/arch/arm/Makefile
+++ b/arch/arm/Makefile
@@ -147,6 +147,7 @@ textofs-$(CONFIG_SA1111) := 0x00208000
147endif 147endif
148textofs-$(CONFIG_ARCH_MSM8X60) := 0x00208000 148textofs-$(CONFIG_ARCH_MSM8X60) := 0x00208000
149textofs-$(CONFIG_ARCH_MSM8960) := 0x00208000 149textofs-$(CONFIG_ARCH_MSM8960) := 0x00208000
150textofs-$(CONFIG_ARCH_MESON) := 0x00208000
150textofs-$(CONFIG_ARCH_AXXIA) := 0x00308000 151textofs-$(CONFIG_ARCH_AXXIA) := 0x00308000
151 152
152# Machine directory name. This list is sorted alphanumerically 153# Machine directory name. This list is sorted alphanumerically
diff --git a/arch/arm/include/asm/smp_scu.h b/arch/arm/include/asm/smp_scu.h
index 5983f6bc62d5..1529d1ae2f8d 100644
--- a/arch/arm/include/asm/smp_scu.h
+++ b/arch/arm/include/asm/smp_scu.h
@@ -27,6 +27,8 @@ static inline unsigned long scu_a9_get_base(void)
27#ifdef CONFIG_HAVE_ARM_SCU 27#ifdef CONFIG_HAVE_ARM_SCU
28unsigned int scu_get_core_count(void __iomem *); 28unsigned int scu_get_core_count(void __iomem *);
29int scu_power_mode(void __iomem *, unsigned int); 29int scu_power_mode(void __iomem *, unsigned int);
30int scu_cpu_power_enable(void __iomem *, unsigned int);
31int scu_get_cpu_power_mode(void __iomem *scu_base, unsigned int logical_cpu);
30#else 32#else
31static inline unsigned int scu_get_core_count(void __iomem *scu_base) 33static inline unsigned int scu_get_core_count(void __iomem *scu_base)
32{ 34{
@@ -36,6 +38,16 @@ static inline int scu_power_mode(void __iomem *scu_base, unsigned int mode)
36{ 38{
37 return -EINVAL; 39 return -EINVAL;
38} 40}
41static inline int scu_cpu_power_enable(void __iomem *scu_base,
42 unsigned int mode)
43{
44 return -EINVAL;
45}
46static inline int scu_get_cpu_power_mode(void __iomem *scu_base,
47 unsigned int logical_cpu)
48{
49 return -EINVAL;
50}
39#endif 51#endif
40 52
41#if defined(CONFIG_SMP) && defined(CONFIG_HAVE_ARM_SCU) 53#if defined(CONFIG_SMP) && defined(CONFIG_HAVE_ARM_SCU)
diff --git a/arch/arm/kernel/smp_scu.c b/arch/arm/kernel/smp_scu.c
index 72f9241ad5db..c6b33074c393 100644
--- a/arch/arm/kernel/smp_scu.c
+++ b/arch/arm/kernel/smp_scu.c
@@ -21,6 +21,7 @@
21#define SCU_STANDBY_ENABLE (1 << 5) 21#define SCU_STANDBY_ENABLE (1 << 5)
22#define SCU_CONFIG 0x04 22#define SCU_CONFIG 0x04
23#define SCU_CPU_STATUS 0x08 23#define SCU_CPU_STATUS 0x08
24#define SCU_CPU_STATUS_MASK GENMASK(1, 0)
24#define SCU_INVALIDATE 0x0c 25#define SCU_INVALIDATE 0x0c
25#define SCU_FPGA_REVISION 0x10 26#define SCU_FPGA_REVISION 0x10
26 27
@@ -72,6 +73,24 @@ void scu_enable(void __iomem *scu_base)
72} 73}
73#endif 74#endif
74 75
76static int scu_set_power_mode_internal(void __iomem *scu_base,
77 unsigned int logical_cpu,
78 unsigned int mode)
79{
80 unsigned int val;
81 int cpu = MPIDR_AFFINITY_LEVEL(cpu_logical_map(logical_cpu), 0);
82
83 if (mode > 3 || mode == 1 || cpu > 3)
84 return -EINVAL;
85
86 val = readb_relaxed(scu_base + SCU_CPU_STATUS + cpu);
87 val &= ~SCU_CPU_STATUS_MASK;
88 val |= mode;
89 writeb_relaxed(val, scu_base + SCU_CPU_STATUS + cpu);
90
91 return 0;
92}
93
75/* 94/*
76 * Set the executing CPUs power mode as defined. This will be in 95 * Set the executing CPUs power mode as defined. This will be in
77 * preparation for it executing a WFI instruction. 96 * preparation for it executing a WFI instruction.
@@ -82,15 +101,27 @@ void scu_enable(void __iomem *scu_base)
82 */ 101 */
83int scu_power_mode(void __iomem *scu_base, unsigned int mode) 102int scu_power_mode(void __iomem *scu_base, unsigned int mode)
84{ 103{
104 return scu_set_power_mode_internal(scu_base, smp_processor_id(), mode);
105}
106
107/*
108 * Set the given (logical) CPU's power mode to SCU_PM_NORMAL.
109 */
110int scu_cpu_power_enable(void __iomem *scu_base, unsigned int cpu)
111{
112 return scu_set_power_mode_internal(scu_base, cpu, SCU_PM_NORMAL);
113}
114
115int scu_get_cpu_power_mode(void __iomem *scu_base, unsigned int logical_cpu)
116{
85 unsigned int val; 117 unsigned int val;
86 int cpu = MPIDR_AFFINITY_LEVEL(cpu_logical_map(smp_processor_id()), 0); 118 int cpu = MPIDR_AFFINITY_LEVEL(cpu_logical_map(logical_cpu), 0);
87 119
88 if (mode > 3 || mode == 1 || cpu > 3) 120 if (cpu > 3)
89 return -EINVAL; 121 return -EINVAL;
90 122
91 val = readb_relaxed(scu_base + SCU_CPU_STATUS + cpu) & ~0x03; 123 val = readb_relaxed(scu_base + SCU_CPU_STATUS + cpu);
92 val |= mode; 124 val &= SCU_CPU_STATUS_MASK;
93 writeb_relaxed(val, scu_base + SCU_CPU_STATUS + cpu);
94 125
95 return 0; 126 return val;
96} 127}
diff --git a/arch/arm/mach-meson/Kconfig b/arch/arm/mach-meson/Kconfig
index ee30511849ca..aff6164b2083 100644
--- a/arch/arm/mach-meson/Kconfig
+++ b/arch/arm/mach-meson/Kconfig
@@ -9,6 +9,7 @@ menuconfig ARCH_MESON
9 select PINCTRL_MESON 9 select PINCTRL_MESON
10 select COMMON_CLK 10 select COMMON_CLK
11 select COMMON_CLK_AMLOGIC 11 select COMMON_CLK_AMLOGIC
12 select HAVE_ARM_SCU if SMP
12 13
13if ARCH_MESON 14if ARCH_MESON
14 15
@@ -28,5 +29,6 @@ config MACH_MESON8B
28 default ARCH_MESON 29 default ARCH_MESON
29 select MESON6_TIMER 30 select MESON6_TIMER
30 select COMMON_CLK_MESON8B 31 select COMMON_CLK_MESON8B
32 select MESON_IRQ_GPIO
31 33
32endif 34endif
diff --git a/arch/arm/mach-meson/Makefile b/arch/arm/mach-meson/Makefile
index 9d7380eeeedd..bc26c85a7e8f 100644
--- a/arch/arm/mach-meson/Makefile
+++ b/arch/arm/mach-meson/Makefile
@@ -1 +1,2 @@
1obj-$(CONFIG_ARCH_MESON) += meson.o 1obj-$(CONFIG_ARCH_MESON) += meson.o
2obj-$(CONFIG_SMP) += platsmp.o
diff --git a/arch/arm/mach-meson/platsmp.c b/arch/arm/mach-meson/platsmp.c
new file mode 100644
index 000000000000..2555f9056a33
--- /dev/null
+++ b/arch/arm/mach-meson/platsmp.c
@@ -0,0 +1,440 @@
1/*
2 * Copyright (C) 2015 Carlo Caione <carlo@endlessm.com>
3 * Copyright (C) 2017 Martin Blumenstingl <martin.blumenstingl@googlemail.com>
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, but WITHOUT
11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
13 * more details.
14 *
15 */
16
17#include <linux/delay.h>
18#include <linux/init.h>
19#include <linux/io.h>
20#include <linux/of.h>
21#include <linux/of_address.h>
22#include <linux/regmap.h>
23#include <linux/reset.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/smp_scu.h>
30#include <asm/smp_plat.h>
31
32#define MESON_SMP_SRAM_CPU_CTRL_REG (0x00)
33#define MESON_SMP_SRAM_CPU_CTRL_ADDR_REG(c) (0x04 + ((c - 1) << 2))
34
35#define MESON_CPU_AO_RTI_PWR_A9_CNTL0 (0x00)
36#define MESON_CPU_AO_RTI_PWR_A9_CNTL1 (0x04)
37#define MESON_CPU_AO_RTI_PWR_A9_MEM_PD0 (0x14)
38
39#define MESON_CPU_PWR_A9_CNTL0_M(c) (0x03 << ((c * 2) + 16))
40#define MESON_CPU_PWR_A9_CNTL1_M(c) (0x03 << ((c + 1) << 1))
41#define MESON_CPU_PWR_A9_MEM_PD0_M(c) (0x0f << (32 - (c * 4)))
42#define MESON_CPU_PWR_A9_CNTL1_ST(c) (0x01 << (c + 16))
43
44static void __iomem *sram_base;
45static void __iomem *scu_base;
46static struct regmap *pmu;
47
48static struct reset_control *meson_smp_get_core_reset(int cpu)
49{
50 struct device_node *np = of_get_cpu_node(cpu, 0);
51
52 return of_reset_control_get_exclusive(np, NULL);
53}
54
55static void meson_smp_set_cpu_ctrl(int cpu, bool on_off)
56{
57 u32 val = readl(sram_base + MESON_SMP_SRAM_CPU_CTRL_REG);
58
59 if (on_off)
60 val |= BIT(cpu);
61 else
62 val &= ~BIT(cpu);
63
64 /* keep bit 0 always enabled */
65 val |= BIT(0);
66
67 writel(val, sram_base + MESON_SMP_SRAM_CPU_CTRL_REG);
68}
69
70static void __init meson_smp_prepare_cpus(const char *scu_compatible,
71 const char *pmu_compatible,
72 const char *sram_compatible)
73{
74 static struct device_node *node;
75
76 /* SMP SRAM */
77 node = of_find_compatible_node(NULL, NULL, sram_compatible);
78 if (!node) {
79 pr_err("Missing SRAM node\n");
80 return;
81 }
82
83 sram_base = of_iomap(node, 0);
84 if (!sram_base) {
85 pr_err("Couldn't map SRAM registers\n");
86 return;
87 }
88
89 /* PMU */
90 pmu = syscon_regmap_lookup_by_compatible(pmu_compatible);
91 if (IS_ERR(pmu)) {
92 pr_err("Couldn't map PMU registers\n");
93 return;
94 }
95
96 /* SCU */
97 node = of_find_compatible_node(NULL, NULL, scu_compatible);
98 if (!node) {
99 pr_err("Missing SCU node\n");
100 return;
101 }
102
103 scu_base = of_iomap(node, 0);
104 if (!scu_base) {
105 pr_err("Couln't map SCU registers\n");
106 return;
107 }
108
109 scu_enable(scu_base);
110}
111
112static void __init meson8b_smp_prepare_cpus(unsigned int max_cpus)
113{
114 meson_smp_prepare_cpus("arm,cortex-a5-scu", "amlogic,meson8b-pmu",
115 "amlogic,meson8b-smp-sram");
116}
117
118static void __init meson8_smp_prepare_cpus(unsigned int max_cpus)
119{
120 meson_smp_prepare_cpus("arm,cortex-a9-scu", "amlogic,meson8-pmu",
121 "amlogic,meson8-smp-sram");
122}
123
124static void meson_smp_begin_secondary_boot(unsigned int cpu)
125{
126 /*
127 * Set the entry point before powering on the CPU through the SCU. This
128 * is needed if the CPU is in "warm" state (= after rebooting the
129 * system without power-cycling, or when taking the CPU offline and
130 * then taking it online again.
131 */
132 writel(__pa_symbol(secondary_startup),
133 sram_base + MESON_SMP_SRAM_CPU_CTRL_ADDR_REG(cpu));
134
135 /*
136 * SCU Power on CPU (needs to be done before starting the CPU,
137 * otherwise the secondary CPU will not start).
138 */
139 scu_cpu_power_enable(scu_base, cpu);
140}
141
142static int meson_smp_finalize_secondary_boot(unsigned int cpu)
143{
144 unsigned long timeout;
145
146 timeout = jiffies + (10 * HZ);
147 while (readl(sram_base + MESON_SMP_SRAM_CPU_CTRL_ADDR_REG(cpu))) {
148 if (!time_before(jiffies, timeout)) {
149 pr_err("Timeout while waiting for CPU%d status\n",
150 cpu);
151 return -ETIMEDOUT;
152 }
153 }
154
155 writel(__pa_symbol(secondary_startup),
156 sram_base + MESON_SMP_SRAM_CPU_CTRL_ADDR_REG(cpu));
157
158 meson_smp_set_cpu_ctrl(cpu, true);
159
160 return 0;
161}
162
163static int meson8_smp_boot_secondary(unsigned int cpu,
164 struct task_struct *idle)
165{
166 struct reset_control *rstc;
167 int ret;
168
169 rstc = meson_smp_get_core_reset(cpu);
170 if (IS_ERR(rstc)) {
171 pr_err("Couldn't get the reset controller for CPU%d\n", cpu);
172 return PTR_ERR(rstc);
173 }
174
175 meson_smp_begin_secondary_boot(cpu);
176
177 /* Reset enable */
178 ret = reset_control_assert(rstc);
179 if (ret) {
180 pr_err("Failed to assert CPU%d reset\n", cpu);
181 goto out;
182 }
183
184 /* CPU power ON */
185 ret = regmap_update_bits(pmu, MESON_CPU_AO_RTI_PWR_A9_CNTL1,
186 MESON_CPU_PWR_A9_CNTL1_M(cpu), 0);
187 if (ret < 0) {
188 pr_err("Couldn't wake up CPU%d\n", cpu);
189 goto out;
190 }
191
192 udelay(10);
193
194 /* Isolation disable */
195 ret = regmap_update_bits(pmu, MESON_CPU_AO_RTI_PWR_A9_CNTL0, BIT(cpu),
196 0);
197 if (ret < 0) {
198 pr_err("Error when disabling isolation of CPU%d\n", cpu);
199 goto out;
200 }
201
202 /* Reset disable */
203 ret = reset_control_deassert(rstc);
204 if (ret) {
205 pr_err("Failed to de-assert CPU%d reset\n", cpu);
206 goto out;
207 }
208
209 ret = meson_smp_finalize_secondary_boot(cpu);
210 if (ret)
211 goto out;
212
213out:
214 reset_control_put(rstc);
215
216 return 0;
217}
218
219static int meson8b_smp_boot_secondary(unsigned int cpu,
220 struct task_struct *idle)
221{
222 struct reset_control *rstc;
223 int ret;
224 u32 val;
225
226 rstc = meson_smp_get_core_reset(cpu);
227 if (IS_ERR(rstc)) {
228 pr_err("Couldn't get the reset controller for CPU%d\n", cpu);
229 return PTR_ERR(rstc);
230 }
231
232 meson_smp_begin_secondary_boot(cpu);
233
234 /* CPU power UP */
235 ret = regmap_update_bits(pmu, MESON_CPU_AO_RTI_PWR_A9_CNTL0,
236 MESON_CPU_PWR_A9_CNTL0_M(cpu), 0);
237 if (ret < 0) {
238 pr_err("Couldn't power up CPU%d\n", cpu);
239 goto out;
240 }
241
242 udelay(5);
243
244 /* Reset enable */
245 ret = reset_control_assert(rstc);
246 if (ret) {
247 pr_err("Failed to assert CPU%d reset\n", cpu);
248 goto out;
249 }
250
251 /* Memory power UP */
252 ret = regmap_update_bits(pmu, MESON_CPU_AO_RTI_PWR_A9_MEM_PD0,
253 MESON_CPU_PWR_A9_MEM_PD0_M(cpu), 0);
254 if (ret < 0) {
255 pr_err("Couldn't power up the memory for CPU%d\n", cpu);
256 goto out;
257 }
258
259 /* Wake up CPU */
260 ret = regmap_update_bits(pmu, MESON_CPU_AO_RTI_PWR_A9_CNTL1,
261 MESON_CPU_PWR_A9_CNTL1_M(cpu), 0);
262 if (ret < 0) {
263 pr_err("Couldn't wake up CPU%d\n", cpu);
264 goto out;
265 }
266
267 udelay(10);
268
269 ret = regmap_read_poll_timeout(pmu, MESON_CPU_AO_RTI_PWR_A9_CNTL1, val,
270 val & MESON_CPU_PWR_A9_CNTL1_ST(cpu),
271 10, 10000);
272 if (ret) {
273 pr_err("Timeout while polling PMU for CPU%d status\n", cpu);
274 goto out;
275 }
276
277 /* Isolation disable */
278 ret = regmap_update_bits(pmu, MESON_CPU_AO_RTI_PWR_A9_CNTL0, BIT(cpu),
279 0);
280 if (ret < 0) {
281 pr_err("Error when disabling isolation of CPU%d\n", cpu);
282 goto out;
283 }
284
285 /* Reset disable */
286 ret = reset_control_deassert(rstc);
287 if (ret) {
288 pr_err("Failed to de-assert CPU%d reset\n", cpu);
289 goto out;
290 }
291
292 ret = meson_smp_finalize_secondary_boot(cpu);
293 if (ret)
294 goto out;
295
296out:
297 reset_control_put(rstc);
298
299 return 0;
300}
301
302#ifdef CONFIG_HOTPLUG_CPU
303static void meson8_smp_cpu_die(unsigned int cpu)
304{
305 meson_smp_set_cpu_ctrl(cpu, false);
306
307 v7_exit_coherency_flush(louis);
308
309 scu_power_mode(scu_base, SCU_PM_POWEROFF);
310
311 dsb();
312 wfi();
313
314 /* we should never get here */
315 WARN_ON(1);
316}
317
318static int meson8_smp_cpu_kill(unsigned int cpu)
319{
320 int ret, power_mode;
321 unsigned long timeout;
322
323 timeout = jiffies + (50 * HZ);
324 do {
325 power_mode = scu_get_cpu_power_mode(scu_base, cpu);
326
327 if (power_mode == SCU_PM_POWEROFF)
328 break;
329
330 usleep_range(10000, 15000);
331 } while (time_before(jiffies, timeout));
332
333 if (power_mode != SCU_PM_POWEROFF) {
334 pr_err("Error while waiting for SCU power-off on CPU%d\n",
335 cpu);
336 return -ETIMEDOUT;
337 }
338
339 msleep(30);
340
341 /* Isolation enable */
342 ret = regmap_update_bits(pmu, MESON_CPU_AO_RTI_PWR_A9_CNTL0, BIT(cpu),
343 0x3);
344 if (ret < 0) {
345 pr_err("Error when enabling isolation for CPU%d\n", cpu);
346 return ret;
347 }
348
349 udelay(10);
350
351 /* CPU power OFF */
352 ret = regmap_update_bits(pmu, MESON_CPU_AO_RTI_PWR_A9_CNTL1,
353 MESON_CPU_PWR_A9_CNTL1_M(cpu), 0x3);
354 if (ret < 0) {
355 pr_err("Couldn't change sleep status of CPU%d\n", cpu);
356 return ret;
357 }
358
359 return 1;
360}
361
362static int meson8b_smp_cpu_kill(unsigned int cpu)
363{
364 int ret, power_mode, count = 5000;
365
366 do {
367 power_mode = scu_get_cpu_power_mode(scu_base, cpu);
368
369 if (power_mode == SCU_PM_POWEROFF)
370 break;
371
372 udelay(10);
373 } while (++count);
374
375 if (power_mode != SCU_PM_POWEROFF) {
376 pr_err("Error while waiting for SCU power-off on CPU%d\n",
377 cpu);
378 return -ETIMEDOUT;
379 }
380
381 udelay(10);
382
383 /* CPU power DOWN */
384 ret = regmap_update_bits(pmu, MESON_CPU_AO_RTI_PWR_A9_CNTL0,
385 MESON_CPU_PWR_A9_CNTL0_M(cpu), 0x3);
386 if (ret < 0) {
387 pr_err("Couldn't power down CPU%d\n", cpu);
388 return ret;
389 }
390
391 /* Isolation enable */
392 ret = regmap_update_bits(pmu, MESON_CPU_AO_RTI_PWR_A9_CNTL0, BIT(cpu),
393 0x3);
394 if (ret < 0) {
395 pr_err("Error when enabling isolation for CPU%d\n", cpu);
396 return ret;
397 }
398
399 udelay(10);
400
401 /* Sleep status */
402 ret = regmap_update_bits(pmu, MESON_CPU_AO_RTI_PWR_A9_CNTL1,
403 MESON_CPU_PWR_A9_CNTL1_M(cpu), 0x3);
404 if (ret < 0) {
405 pr_err("Couldn't change sleep status of CPU%d\n", cpu);
406 return ret;
407 }
408
409 /* Memory power DOWN */
410 ret = regmap_update_bits(pmu, MESON_CPU_AO_RTI_PWR_A9_MEM_PD0,
411 MESON_CPU_PWR_A9_MEM_PD0_M(cpu), 0xf);
412 if (ret < 0) {
413 pr_err("Couldn't power down the memory of CPU%d\n", cpu);
414 return ret;
415 }
416
417 return 1;
418}
419#endif
420
421static struct smp_operations meson8_smp_ops __initdata = {
422 .smp_prepare_cpus = meson8_smp_prepare_cpus,
423 .smp_boot_secondary = meson8_smp_boot_secondary,
424#ifdef CONFIG_HOTPLUG_CPU
425 .cpu_die = meson8_smp_cpu_die,
426 .cpu_kill = meson8_smp_cpu_kill,
427#endif
428};
429
430static struct smp_operations meson8b_smp_ops __initdata = {
431 .smp_prepare_cpus = meson8b_smp_prepare_cpus,
432 .smp_boot_secondary = meson8b_smp_boot_secondary,
433#ifdef CONFIG_HOTPLUG_CPU
434 .cpu_die = meson8_smp_cpu_die,
435 .cpu_kill = meson8b_smp_cpu_kill,
436#endif
437};
438
439CPU_METHOD_OF_DECLARE(meson8_smp, "amlogic,meson8-smp", &meson8_smp_ops);
440CPU_METHOD_OF_DECLARE(meson8b_smp, "amlogic,meson8b-smp", &meson8b_smp_ops);