OSDN Git Service

ARM: EXYNOS: Remove unneeded __ref annotation for cpu_die function
[android-x86/kernel.git] / arch / arm / mach-exynos / platsmp.c
1  /*
2  * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.
3  *              http://www.samsung.com
4  *
5  * Cloned from linux/arch/arm/mach-vexpress/platsmp.c
6  *
7  *  Copyright (C) 2002 ARM Ltd.
8  *  All Rights Reserved
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License version 2 as
12  * published by the Free Software Foundation.
13 */
14
15 #include <linux/init.h>
16 #include <linux/errno.h>
17 #include <linux/delay.h>
18 #include <linux/device.h>
19 #include <linux/jiffies.h>
20 #include <linux/smp.h>
21 #include <linux/io.h>
22 #include <linux/of_address.h>
23
24 #include <asm/cacheflush.h>
25 #include <asm/cp15.h>
26 #include <asm/smp_plat.h>
27 #include <asm/smp_scu.h>
28 #include <asm/firmware.h>
29
30 #include <mach/map.h>
31
32 #include "common.h"
33 #include "regs-pmu.h"
34
35 extern void exynos4_secondary_startup(void);
36
37 #ifdef CONFIG_HOTPLUG_CPU
38 static inline void cpu_leave_lowpower(void)
39 {
40         unsigned int v;
41
42         asm volatile(
43         "mrc    p15, 0, %0, c1, c0, 0\n"
44         "       orr     %0, %0, %1\n"
45         "       mcr     p15, 0, %0, c1, c0, 0\n"
46         "       mrc     p15, 0, %0, c1, c0, 1\n"
47         "       orr     %0, %0, %2\n"
48         "       mcr     p15, 0, %0, c1, c0, 1\n"
49           : "=&r" (v)
50           : "Ir" (CR_C), "Ir" (0x40)
51           : "cc");
52 }
53
54 static inline void platform_do_lowpower(unsigned int cpu, int *spurious)
55 {
56         u32 mpidr = cpu_logical_map(cpu);
57         u32 core_id = MPIDR_AFFINITY_LEVEL(mpidr, 0);
58
59         for (;;) {
60
61                 /* Turn the CPU off on next WFI instruction. */
62                 exynos_cpu_power_down(core_id);
63
64                 wfi();
65
66                 if (pen_release == core_id) {
67                         /*
68                          * OK, proper wakeup, we're done
69                          */
70                         break;
71                 }
72
73                 /*
74                  * Getting here, means that we have come out of WFI without
75                  * having been woken up - this shouldn't happen
76                  *
77                  * Just note it happening - when we're woken, we can report
78                  * its occurrence.
79                  */
80                 (*spurious)++;
81         }
82 }
83 #endif /* CONFIG_HOTPLUG_CPU */
84
85 /**
86  * exynos_core_power_down : power down the specified cpu
87  * @cpu : the cpu to power down
88  *
89  * Power down the specified cpu. The sequence must be finished by a
90  * call to cpu_do_idle()
91  *
92  */
93 void exynos_cpu_power_down(int cpu)
94 {
95         pmu_raw_writel(0, EXYNOS_ARM_CORE_CONFIGURATION(cpu));
96 }
97
98 /**
99  * exynos_cpu_power_up : power up the specified cpu
100  * @cpu : the cpu to power up
101  *
102  * Power up the specified cpu
103  */
104 void exynos_cpu_power_up(int cpu)
105 {
106         pmu_raw_writel(S5P_CORE_LOCAL_PWR_EN,
107                         EXYNOS_ARM_CORE_CONFIGURATION(cpu));
108 }
109
110 /**
111  * exynos_cpu_power_state : returns the power state of the cpu
112  * @cpu : the cpu to retrieve the power state from
113  *
114  */
115 int exynos_cpu_power_state(int cpu)
116 {
117         return (pmu_raw_readl(EXYNOS_ARM_CORE_STATUS(cpu)) &
118                         S5P_CORE_LOCAL_PWR_EN);
119 }
120
121 /**
122  * exynos_cluster_power_down : power down the specified cluster
123  * @cluster : the cluster to power down
124  */
125 void exynos_cluster_power_down(int cluster)
126 {
127         pmu_raw_writel(0, EXYNOS_COMMON_CONFIGURATION(cluster));
128 }
129
130 /**
131  * exynos_cluster_power_up : power up the specified cluster
132  * @cluster : the cluster to power up
133  */
134 void exynos_cluster_power_up(int cluster)
135 {
136         pmu_raw_writel(S5P_CORE_LOCAL_PWR_EN,
137                         EXYNOS_COMMON_CONFIGURATION(cluster));
138 }
139
140 /**
141  * exynos_cluster_power_state : returns the power state of the cluster
142  * @cluster : the cluster to retrieve the power state from
143  *
144  */
145 int exynos_cluster_power_state(int cluster)
146 {
147         return (pmu_raw_readl(EXYNOS_COMMON_STATUS(cluster)) &
148                 S5P_CORE_LOCAL_PWR_EN);
149 }
150
151 static inline void __iomem *cpu_boot_reg_base(void)
152 {
153         if (soc_is_exynos4210() && samsung_rev() == EXYNOS4210_REV_1_1)
154                 return pmu_base_addr + S5P_INFORM5;
155         return sysram_base_addr;
156 }
157
158 static inline void __iomem *cpu_boot_reg(int cpu)
159 {
160         void __iomem *boot_reg;
161
162         boot_reg = cpu_boot_reg_base();
163         if (!boot_reg)
164                 return ERR_PTR(-ENODEV);
165         if (soc_is_exynos4412())
166                 boot_reg += 4*cpu;
167         else if (soc_is_exynos5420() || soc_is_exynos5800())
168                 boot_reg += 4;
169         return boot_reg;
170 }
171
172 /*
173  * Write pen_release in a way that is guaranteed to be visible to all
174  * observers, irrespective of whether they're taking part in coherency
175  * or not.  This is necessary for the hotplug code to work reliably.
176  */
177 static void write_pen_release(int val)
178 {
179         pen_release = val;
180         smp_wmb();
181         sync_cache_w(&pen_release);
182 }
183
184 static void __iomem *scu_base_addr(void)
185 {
186         return (void __iomem *)(S5P_VA_SCU);
187 }
188
189 static DEFINE_SPINLOCK(boot_lock);
190
191 static void exynos_secondary_init(unsigned int cpu)
192 {
193         /*
194          * let the primary processor know we're out of the
195          * pen, then head off into the C entry point
196          */
197         write_pen_release(-1);
198
199         /*
200          * Synchronise with the boot thread.
201          */
202         spin_lock(&boot_lock);
203         spin_unlock(&boot_lock);
204 }
205
206 static int exynos_boot_secondary(unsigned int cpu, struct task_struct *idle)
207 {
208         unsigned long timeout;
209         u32 mpidr = cpu_logical_map(cpu);
210         u32 core_id = MPIDR_AFFINITY_LEVEL(mpidr, 0);
211         int ret = -ENOSYS;
212
213         /*
214          * Set synchronisation state between this boot processor
215          * and the secondary one
216          */
217         spin_lock(&boot_lock);
218
219         /*
220          * The secondary processor is waiting to be released from
221          * the holding pen - release it, then wait for it to flag
222          * that it has been released by resetting pen_release.
223          *
224          * Note that "pen_release" is the hardware CPU core ID, whereas
225          * "cpu" is Linux's internal ID.
226          */
227         write_pen_release(core_id);
228
229         if (!exynos_cpu_power_state(core_id)) {
230                 exynos_cpu_power_up(core_id);
231                 timeout = 10;
232
233                 /* wait max 10 ms until cpu1 is on */
234                 while (exynos_cpu_power_state(core_id)
235                        != S5P_CORE_LOCAL_PWR_EN) {
236                         if (timeout-- == 0)
237                                 break;
238
239                         mdelay(1);
240                 }
241
242                 if (timeout == 0) {
243                         printk(KERN_ERR "cpu1 power enable failed");
244                         spin_unlock(&boot_lock);
245                         return -ETIMEDOUT;
246                 }
247         }
248         /*
249          * Send the secondary CPU a soft interrupt, thereby causing
250          * the boot monitor to read the system wide flags register,
251          * and branch to the address found there.
252          */
253
254         timeout = jiffies + (1 * HZ);
255         while (time_before(jiffies, timeout)) {
256                 unsigned long boot_addr;
257
258                 smp_rmb();
259
260                 boot_addr = virt_to_phys(exynos4_secondary_startup);
261
262                 /*
263                  * Try to set boot address using firmware first
264                  * and fall back to boot register if it fails.
265                  */
266                 ret = call_firmware_op(set_cpu_boot_addr, core_id, boot_addr);
267                 if (ret && ret != -ENOSYS)
268                         goto fail;
269                 if (ret == -ENOSYS) {
270                         void __iomem *boot_reg = cpu_boot_reg(core_id);
271
272                         if (IS_ERR(boot_reg)) {
273                                 ret = PTR_ERR(boot_reg);
274                                 goto fail;
275                         }
276                         __raw_writel(boot_addr, boot_reg);
277                 }
278
279                 call_firmware_op(cpu_boot, core_id);
280
281                 arch_send_wakeup_ipi_mask(cpumask_of(cpu));
282
283                 if (pen_release == -1)
284                         break;
285
286                 udelay(10);
287         }
288
289         /*
290          * now the secondary core is starting up let it run its
291          * calibrations, then wait for it to finish
292          */
293 fail:
294         spin_unlock(&boot_lock);
295
296         return pen_release != -1 ? ret : 0;
297 }
298
299 /*
300  * Initialise the CPU possible map early - this describes the CPUs
301  * which may be present or become present in the system.
302  */
303
304 static void __init exynos_smp_init_cpus(void)
305 {
306         void __iomem *scu_base = scu_base_addr();
307         unsigned int i, ncores;
308
309         if (read_cpuid_part() == ARM_CPU_PART_CORTEX_A9)
310                 ncores = scu_base ? scu_get_core_count(scu_base) : 1;
311         else
312                 /*
313                  * CPU Nodes are passed thru DT and set_cpu_possible
314                  * is set by "arm_dt_init_cpu_maps".
315                  */
316                 return;
317
318         /* sanity check */
319         if (ncores > nr_cpu_ids) {
320                 pr_warn("SMP: %u cores greater than maximum (%u), clipping\n",
321                         ncores, nr_cpu_ids);
322                 ncores = nr_cpu_ids;
323         }
324
325         for (i = 0; i < ncores; i++)
326                 set_cpu_possible(i, true);
327 }
328
329 static void __init exynos_smp_prepare_cpus(unsigned int max_cpus)
330 {
331         int i;
332
333         exynos_sysram_init();
334
335         if (read_cpuid_part() == ARM_CPU_PART_CORTEX_A9)
336                 scu_enable(scu_base_addr());
337
338         /*
339          * Write the address of secondary startup into the
340          * system-wide flags register. The boot monitor waits
341          * until it receives a soft interrupt, and then the
342          * secondary CPU branches to this address.
343          *
344          * Try using firmware operation first and fall back to
345          * boot register if it fails.
346          */
347         for (i = 1; i < max_cpus; ++i) {
348                 unsigned long boot_addr;
349                 u32 mpidr;
350                 u32 core_id;
351                 int ret;
352
353                 mpidr = cpu_logical_map(i);
354                 core_id = MPIDR_AFFINITY_LEVEL(mpidr, 0);
355                 boot_addr = virt_to_phys(exynos4_secondary_startup);
356
357                 ret = call_firmware_op(set_cpu_boot_addr, core_id, boot_addr);
358                 if (ret && ret != -ENOSYS)
359                         break;
360                 if (ret == -ENOSYS) {
361                         void __iomem *boot_reg = cpu_boot_reg(core_id);
362
363                         if (IS_ERR(boot_reg))
364                                 break;
365                         __raw_writel(boot_addr, boot_reg);
366                 }
367         }
368 }
369
370 #ifdef CONFIG_HOTPLUG_CPU
371 /*
372  * platform-specific code to shutdown a CPU
373  *
374  * Called with IRQs disabled
375  */
376 static void exynos_cpu_die(unsigned int cpu)
377 {
378         int spurious = 0;
379
380         v7_exit_coherency_flush(louis);
381
382         platform_do_lowpower(cpu, &spurious);
383
384         /*
385          * bring this CPU back into the world of cache
386          * coherency, and then restore interrupts
387          */
388         cpu_leave_lowpower();
389
390         if (spurious)
391                 pr_warn("CPU%u: %u spurious wakeup calls\n", cpu, spurious);
392 }
393 #endif /* CONFIG_HOTPLUG_CPU */
394
395 struct smp_operations exynos_smp_ops __initdata = {
396         .smp_init_cpus          = exynos_smp_init_cpus,
397         .smp_prepare_cpus       = exynos_smp_prepare_cpus,
398         .smp_secondary_init     = exynos_secondary_init,
399         .smp_boot_secondary     = exynos_boot_secondary,
400 #ifdef CONFIG_HOTPLUG_CPU
401         .cpu_die                = exynos_cpu_die,
402 #endif
403 };