aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm/mach-hisi
diff options
context:
space:
mode:
authorHaojian Zhuang <haojian.zhuang@linaro.org>2014-05-09 03:59:29 -0400
committerWei Xu <xuwei5@hisilicon.com>2014-09-03 09:10:12 -0400
commit9cdc99919a95e8b54c1998b65bb1bfdabd47d27b (patch)
tree472e2963fdec26c8f2fd86bf6c047ce96e23990e /arch/arm/mach-hisi
parentebf4a5c5b4027b682ed8877a938e6d1d92f37745 (diff)
ARM: hisi: enable MCPM implementation
Multiple CPU clusters are used in Hisilicon HiP04 SoC. Now use MCPM framework to manage power on HiP04 SoC. Changelog: v20: * Disable L2 prefetch when the whole cluster is down. * Move disabling snoop filter into power_down() after L2 prefetch disabled. * Remove delay in wait_for_power_down() after L2 prefetch disabled. * Add the sleep polling in wait_for_power_down() again since we need to wait L2 when the cluster is down. v19: * Add comments on those delay hacks. * Update on checking core enabled counts in wait_for_power_down(). v18: * Fix to release resource in probe(). * Check whether cpu is already up in the process of making cpu down. * Add udelay in power up/down sequence. * Optimize on setting relocation entry. * Optimize on polling status in wait_for_power_down(). * Add mcpm critical operations. v17: * Parse bootwrapper parameters in DTS file. * Fix to use msleep() in spinlock region. v16: * Parse bootwrapper parameters in command line instead. v13: * Restore power down operation in MCPM. * Fix disabling snoop filter issue in MCPM. v12: * Use wfi as power down state in MCPM. * Remove wait_for_powerdown() in MCPM because wfi is used now. Signed-off-by: Haojian Zhuang <haojian.zhuang@linaro.org> Reviewed-by: Nicolas Pitre <nico@linaro.org> Signed-off-by: Wei Xu <xuwei5@hisilicon.com>
Diffstat (limited to 'arch/arm/mach-hisi')
-rw-r--r--arch/arm/mach-hisi/Makefile1
-rw-r--r--arch/arm/mach-hisi/platmcpm.c386
2 files changed, 387 insertions, 0 deletions
diff --git a/arch/arm/mach-hisi/Makefile b/arch/arm/mach-hisi/Makefile
index ee2506b9cde3..d64831e6a782 100644
--- a/arch/arm/mach-hisi/Makefile
+++ b/arch/arm/mach-hisi/Makefile
@@ -3,4 +3,5 @@
3# 3#
4 4
5obj-y += hisilicon.o 5obj-y += hisilicon.o
6obj-$(CONFIG_MCPM) += platmcpm.o
6obj-$(CONFIG_SMP) += platsmp.o hotplug.o headsmp.o 7obj-$(CONFIG_SMP) += platsmp.o hotplug.o headsmp.o
diff --git a/arch/arm/mach-hisi/platmcpm.c b/arch/arm/mach-hisi/platmcpm.c
new file mode 100644
index 000000000000..280f3f14f77c
--- /dev/null
+++ b/arch/arm/mach-hisi/platmcpm.c
@@ -0,0 +1,386 @@
1/*
2 * Copyright (c) 2013-2014 Linaro Ltd.
3 * Copyright (c) 2013-2014 Hisilicon Limited.
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms and conditions of the GNU General Public License,
7 * version 2, as published by the Free Software Foundation.
8 */
9#include <linux/delay.h>
10#include <linux/io.h>
11#include <linux/memblock.h>
12#include <linux/of_address.h>
13
14#include <asm/cputype.h>
15#include <asm/cp15.h>
16#include <asm/mcpm.h>
17
18#include "core.h"
19
20/* bits definition in SC_CPU_RESET_REQ[x]/SC_CPU_RESET_DREQ[x]
21 * 1 -- unreset; 0 -- reset
22 */
23#define CORE_RESET_BIT(x) (1 << x)
24#define NEON_RESET_BIT(x) (1 << (x + 4))
25#define CORE_DEBUG_RESET_BIT(x) (1 << (x + 9))
26#define CLUSTER_L2_RESET_BIT (1 << 8)
27#define CLUSTER_DEBUG_RESET_BIT (1 << 13)
28
29/*
30 * bits definition in SC_CPU_RESET_STATUS[x]
31 * 1 -- reset status; 0 -- unreset status
32 */
33#define CORE_RESET_STATUS(x) (1 << x)
34#define NEON_RESET_STATUS(x) (1 << (x + 4))
35#define CORE_DEBUG_RESET_STATUS(x) (1 << (x + 9))
36#define CLUSTER_L2_RESET_STATUS (1 << 8)
37#define CLUSTER_DEBUG_RESET_STATUS (1 << 13)
38#define CORE_WFI_STATUS(x) (1 << (x + 16))
39#define CORE_WFE_STATUS(x) (1 << (x + 20))
40#define CORE_DEBUG_ACK(x) (1 << (x + 24))
41
42#define SC_CPU_RESET_REQ(x) (0x520 + (x << 3)) /* reset */
43#define SC_CPU_RESET_DREQ(x) (0x524 + (x << 3)) /* unreset */
44#define SC_CPU_RESET_STATUS(x) (0x1520 + (x << 3))
45
46#define FAB_SF_MODE 0x0c
47#define FAB_SF_INVLD 0x10
48
49/* bits definition in FB_SF_INVLD */
50#define FB_SF_INVLD_START (1 << 8)
51
52#define HIP04_MAX_CLUSTERS 4
53#define HIP04_MAX_CPUS_PER_CLUSTER 4
54
55#define POLL_MSEC 10
56#define TIMEOUT_MSEC 1000
57
58static void __iomem *sysctrl, *fabric;
59static int hip04_cpu_table[HIP04_MAX_CLUSTERS][HIP04_MAX_CPUS_PER_CLUSTER];
60static DEFINE_SPINLOCK(boot_lock);
61static u32 fabric_phys_addr;
62/*
63 * [0]: bootwrapper physical address
64 * [1]: bootwrapper size
65 * [2]: relocation address
66 * [3]: relocation size
67 */
68static u32 hip04_boot_method[4];
69
70static bool hip04_cluster_is_down(unsigned int cluster)
71{
72 int i;
73
74 for (i = 0; i < HIP04_MAX_CPUS_PER_CLUSTER; i++)
75 if (hip04_cpu_table[cluster][i])
76 return false;
77 return true;
78}
79
80static void hip04_set_snoop_filter(unsigned int cluster, unsigned int on)
81{
82 unsigned long data;
83
84 if (!fabric)
85 BUG();
86 data = readl_relaxed(fabric + FAB_SF_MODE);
87 if (on)
88 data |= 1 << cluster;
89 else
90 data &= ~(1 << cluster);
91 writel_relaxed(data, fabric + FAB_SF_MODE);
92 do {
93 cpu_relax();
94 } while (data != readl_relaxed(fabric + FAB_SF_MODE));
95}
96
97static int hip04_mcpm_power_up(unsigned int cpu, unsigned int cluster)
98{
99 unsigned long data;
100 void __iomem *sys_dreq, *sys_status;
101
102 if (!sysctrl)
103 return -ENODEV;
104 if (cluster >= HIP04_MAX_CLUSTERS || cpu >= HIP04_MAX_CPUS_PER_CLUSTER)
105 return -EINVAL;
106
107 spin_lock_irq(&boot_lock);
108
109 if (hip04_cpu_table[cluster][cpu])
110 goto out;
111
112 sys_dreq = sysctrl + SC_CPU_RESET_DREQ(cluster);
113 sys_status = sysctrl + SC_CPU_RESET_STATUS(cluster);
114 if (hip04_cluster_is_down(cluster)) {
115 data = CLUSTER_DEBUG_RESET_BIT;
116 writel_relaxed(data, sys_dreq);
117 do {
118 cpu_relax();
119 data = readl_relaxed(sys_status);
120 } while (data & CLUSTER_DEBUG_RESET_STATUS);
121 }
122
123 data = CORE_RESET_BIT(cpu) | NEON_RESET_BIT(cpu) | \
124 CORE_DEBUG_RESET_BIT(cpu);
125 writel_relaxed(data, sys_dreq);
126 do {
127 cpu_relax();
128 } while (data == readl_relaxed(sys_status));
129 /*
130 * We may fail to power up core again without this delay.
131 * It's not mentioned in document. It's found by test.
132 */
133 udelay(20);
134out:
135 hip04_cpu_table[cluster][cpu]++;
136 spin_unlock_irq(&boot_lock);
137
138 return 0;
139}
140
141static void hip04_mcpm_power_down(void)
142{
143 unsigned int mpidr, cpu, cluster;
144 bool skip_wfi = false, last_man = false;
145
146 mpidr = read_cpuid_mpidr();
147 cpu = MPIDR_AFFINITY_LEVEL(mpidr, 0);
148 cluster = MPIDR_AFFINITY_LEVEL(mpidr, 1);
149
150 __mcpm_cpu_going_down(cpu, cluster);
151
152 spin_lock(&boot_lock);
153 BUG_ON(__mcpm_cluster_state(cluster) != CLUSTER_UP);
154 hip04_cpu_table[cluster][cpu]--;
155 if (hip04_cpu_table[cluster][cpu] == 1) {
156 /* A power_up request went ahead of us. */
157 skip_wfi = true;
158 } else if (hip04_cpu_table[cluster][cpu] > 1) {
159 pr_err("Cluster %d CPU%d boots multiple times\n", cluster, cpu);
160 BUG();
161 }
162
163 last_man = hip04_cluster_is_down(cluster);
164 if (last_man && __mcpm_outbound_enter_critical(cpu, cluster)) {
165 spin_unlock(&boot_lock);
166 /* Since it's Cortex A15, disable L2 prefetching. */
167 asm volatile(
168 "mcr p15, 1, %0, c15, c0, 3 \n\t"
169 "isb \n\t"
170 "dsb "
171 : : "r" (0x400) );
172 v7_exit_coherency_flush(all);
173 hip04_set_snoop_filter(cluster, 0);
174 __mcpm_outbound_leave_critical(cluster, CLUSTER_DOWN);
175 } else {
176 spin_unlock(&boot_lock);
177 v7_exit_coherency_flush(louis);
178 }
179
180 __mcpm_cpu_down(cpu, cluster);
181
182 if (!skip_wfi)
183 wfi();
184}
185
186static int hip04_mcpm_wait_for_powerdown(unsigned int cpu, unsigned int cluster)
187{
188 unsigned int data, tries, count;
189 int ret = -ETIMEDOUT;
190
191 BUG_ON(cluster >= HIP04_MAX_CLUSTERS ||
192 cpu >= HIP04_MAX_CPUS_PER_CLUSTER);
193
194 count = TIMEOUT_MSEC / POLL_MSEC;
195 spin_lock_irq(&boot_lock);
196 for (tries = 0; tries < count; tries++) {
197 if (hip04_cpu_table[cluster][cpu]) {
198 ret = -EBUSY;
199 goto err;
200 }
201 cpu_relax();
202 data = readl_relaxed(sysctrl + SC_CPU_RESET_STATUS(cluster));
203 if (data & CORE_WFI_STATUS(cpu))
204 break;
205 spin_unlock_irq(&boot_lock);
206 /* Wait for clean L2 when the whole cluster is down. */
207 msleep(POLL_MSEC);
208 spin_lock_irq(&boot_lock);
209 }
210 if (tries >= count)
211 goto err;
212 data = CORE_RESET_BIT(cpu) | NEON_RESET_BIT(cpu) | \
213 CORE_DEBUG_RESET_BIT(cpu);
214 writel_relaxed(data, sysctrl + SC_CPU_RESET_REQ(cluster));
215 for (tries = 0; tries < count; tries++) {
216 cpu_relax();
217 data = readl_relaxed(sysctrl + SC_CPU_RESET_STATUS(cluster));
218 if (data & CORE_RESET_STATUS(cpu))
219 break;
220 }
221 if (tries >= count)
222 goto err;
223 spin_unlock_irq(&boot_lock);
224 return 0;
225err:
226 spin_unlock_irq(&boot_lock);
227 return ret;
228}
229
230static void hip04_mcpm_powered_up(void)
231{
232 unsigned int mpidr, cpu, cluster;
233
234 mpidr = read_cpuid_mpidr();
235 cpu = MPIDR_AFFINITY_LEVEL(mpidr, 0);
236 cluster = MPIDR_AFFINITY_LEVEL(mpidr, 1);
237
238 spin_lock(&boot_lock);
239 if (!hip04_cpu_table[cluster][cpu])
240 hip04_cpu_table[cluster][cpu] = 1;
241 spin_unlock(&boot_lock);
242}
243
244static void __naked hip04_mcpm_power_up_setup(unsigned int affinity_level)
245{
246 asm volatile (" \n"
247" cmp r0, #0 \n"
248" bxeq lr \n"
249 /* calculate fabric phys address */
250" adr r2, 2f \n"
251" ldmia r2, {r1, r3} \n"
252" sub r0, r2, r1 \n"
253" ldr r2, [r0, r3] \n"
254 /* get cluster id from MPIDR */
255" mrc p15, 0, r0, c0, c0, 5 \n"
256" ubfx r1, r0, #8, #8 \n"
257 /* 1 << cluster id */
258" mov r0, #1 \n"
259" mov r3, r0, lsl r1 \n"
260" ldr r0, [r2, #"__stringify(FAB_SF_MODE)"] \n"
261" tst r0, r3 \n"
262" bxne lr \n"
263" orr r1, r0, r3 \n"
264" str r1, [r2, #"__stringify(FAB_SF_MODE)"] \n"
265"1: ldr r0, [r2, #"__stringify(FAB_SF_MODE)"] \n"
266" tst r0, r3 \n"
267" beq 1b \n"
268" bx lr \n"
269
270" .align 2 \n"
271"2: .word . \n"
272" .word fabric_phys_addr \n"
273 );
274}
275
276static const struct mcpm_platform_ops hip04_mcpm_ops = {
277 .power_up = hip04_mcpm_power_up,
278 .power_down = hip04_mcpm_power_down,
279 .wait_for_powerdown = hip04_mcpm_wait_for_powerdown,
280 .powered_up = hip04_mcpm_powered_up,
281};
282
283static bool __init hip04_cpu_table_init(void)
284{
285 unsigned int mpidr, cpu, cluster;
286
287 mpidr = read_cpuid_mpidr();
288 cpu = MPIDR_AFFINITY_LEVEL(mpidr, 0);
289 cluster = MPIDR_AFFINITY_LEVEL(mpidr, 1);
290
291 if (cluster >= HIP04_MAX_CLUSTERS ||
292 cpu >= HIP04_MAX_CPUS_PER_CLUSTER) {
293 pr_err("%s: boot CPU is out of bound!\n", __func__);
294 return false;
295 }
296 hip04_set_snoop_filter(cluster, 1);
297 hip04_cpu_table[cluster][cpu] = 1;
298 return true;
299}
300
301static int __init hip04_mcpm_init(void)
302{
303 struct device_node *np, *np_sctl, *np_fab;
304 struct resource fab_res;
305 void __iomem *relocation;
306 int ret = -ENODEV;
307
308 np = of_find_compatible_node(NULL, NULL, "hisilicon,hip04-bootwrapper");
309 if (!np)
310 goto err;
311 ret = of_property_read_u32_array(np, "boot-method",
312 &hip04_boot_method[0], 4);
313 if (ret)
314 goto err;
315 np_sctl = of_find_compatible_node(NULL, NULL, "hisilicon,sysctrl");
316 if (!np_sctl)
317 goto err;
318 np_fab = of_find_compatible_node(NULL, NULL, "hisilicon,hip04-fabric");
319 if (!np_fab)
320 goto err;
321
322 ret = memblock_reserve(hip04_boot_method[0], hip04_boot_method[1]);
323 if (ret)
324 goto err;
325
326 relocation = ioremap(hip04_boot_method[2], hip04_boot_method[3]);
327 if (!relocation) {
328 pr_err("failed to map relocation space\n");
329 ret = -ENOMEM;
330 goto err_reloc;
331 }
332 sysctrl = of_iomap(np_sctl, 0);
333 if (!sysctrl) {
334 pr_err("failed to get sysctrl base\n");
335 ret = -ENOMEM;
336 goto err_sysctrl;
337 }
338 ret = of_address_to_resource(np_fab, 0, &fab_res);
339 if (ret) {
340 pr_err("failed to get fabric base phys\n");
341 goto err_fabric;
342 }
343 fabric_phys_addr = fab_res.start;
344 sync_cache_w(&fabric_phys_addr);
345 fabric = of_iomap(np_fab, 0);
346 if (!fabric) {
347 pr_err("failed to get fabric base\n");
348 ret = -ENOMEM;
349 goto err_fabric;
350 }
351
352 if (!hip04_cpu_table_init()) {
353 ret = -EINVAL;
354 goto err_table;
355 }
356 ret = mcpm_platform_register(&hip04_mcpm_ops);
357 if (ret) {
358 goto err_table;
359 }
360
361 /*
362 * Fill the instruction address that is used after secondary core
363 * out of reset.
364 */
365 writel_relaxed(hip04_boot_method[0], relocation);
366 writel_relaxed(0xa5a5a5a5, relocation + 4); /* magic number */
367 writel_relaxed(virt_to_phys(mcpm_entry_point), relocation + 8);
368 writel_relaxed(0, relocation + 12);
369 iounmap(relocation);
370
371 mcpm_sync_init(hip04_mcpm_power_up_setup);
372 mcpm_smp_set_ops();
373 pr_info("HiP04 MCPM initialized\n");
374 return ret;
375err_table:
376 iounmap(fabric);
377err_fabric:
378 iounmap(sysctrl);
379err_sysctrl:
380 iounmap(relocation);
381err_reloc:
382 memblock_free(hip04_boot_method[0], hip04_boot_method[1]);
383err:
384 return ret;
385}
386early_initcall(hip04_mcpm_init);