create branch for barrier breaker (from trunk r41863)
[14.07/openwrt.git] / target / linux / cns3xxx / files / arch / arm / mach-cns3xxx / platsmp.c
1 /*
2  *  linux/arch/arm/mach-cns3xxx/platsmp.c
3  *
4  *  Copyright (C) 2002 ARM Ltd.
5  *  Copyright 2012 Gateworks Corporation
6  *                 Chris Lang <clang@gateworks.com>
7  *         Tim Harvey <tharvey@gateworks.com>
8  *
9  *  All Rights Reserved
10  *
11  * This program is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License version 2 as
13  * published by the Free Software Foundation.
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
23 #include <asm/cacheflush.h>
24 #include <asm/hardware/gic.h>
25 #include <asm/smp_scu.h>
26 #include <asm/unified.h>
27 #include <asm/fiq.h>
28 #include <mach/smp.h>
29 #include <mach/cns3xxx.h>
30
31 static struct fiq_handler fh = {
32         .name = "cns3xxx-fiq"
33 };
34
35 static unsigned int fiq_buffer[8];
36
37 #define FIQ_ENABLED         0x80000000
38 #define FIQ_GENERATE                            0x00010000
39 #define CNS3XXX_MAP_AREA    0x01000000
40 #define CNS3XXX_UNMAP_AREA  0x02000000
41 #define CNS3XXX_FLUSH_RANGE 0x03000000
42
43 extern void cns3xxx_secondary_startup(void);
44 extern unsigned char cns3xxx_fiq_start, cns3xxx_fiq_end;
45 extern unsigned int fiq_number[2];
46 extern struct cpu_cache_fns cpu_cache;
47 struct cpu_cache_fns cpu_cache_save;
48
49 #define SCU_CPU_STATUS 0x08
50 static void __iomem *scu_base;
51
52 static void __init cns3xxx_set_fiq_regs(void)
53 {
54         struct pt_regs FIQ_regs;
55         unsigned int cpu = smp_processor_id();
56
57         if (cpu) {
58                 FIQ_regs.ARM_ip = (unsigned int)&fiq_buffer[4];
59                 FIQ_regs.ARM_sp = (unsigned int)MISC_FIQ_CPU(0);
60         } else {
61                 FIQ_regs.ARM_ip = (unsigned int)&fiq_buffer[0];
62                 FIQ_regs.ARM_sp = (unsigned int)MISC_FIQ_CPU(1);
63         }
64         set_fiq_regs(&FIQ_regs);
65 }
66
67 static void __init cns3xxx_init_fiq(void)
68 {
69         void *fiqhandler_start;
70         unsigned int fiqhandler_length;
71         int ret;
72
73         fiqhandler_start = &cns3xxx_fiq_start;
74         fiqhandler_length = &cns3xxx_fiq_end - &cns3xxx_fiq_start;
75
76         ret = claim_fiq(&fh);
77
78         if (ret) {
79                 return;
80         }
81
82         set_fiq_handler(fiqhandler_start, fiqhandler_length);
83         fiq_buffer[0] = (unsigned int)&fiq_number[0];
84         fiq_buffer[3] = 0;
85         fiq_buffer[4] = (unsigned int)&fiq_number[1];
86         fiq_buffer[7] = 0;
87 }
88
89
90 /*
91  * Write pen_release in a way that is guaranteed to be visible to all
92  * observers, irrespective of whether they're taking part in coherency
93  * or not.  This is necessary for the hotplug code to work reliably.
94  */
95 static void __cpuinit write_pen_release(int val)
96 {
97         pen_release = val;
98         smp_wmb();
99         __cpuc_flush_dcache_area((void *)&pen_release, sizeof(pen_release));
100         outer_clean_range(__pa(&pen_release), __pa(&pen_release + 1));
101 }
102
103 static DEFINE_SPINLOCK(boot_lock);
104
105 static void __cpuinit cns3xxx_secondary_init(unsigned int cpu)
106 {
107         /*
108          * if any interrupts are already enabled for the primary
109          * core (e.g. timer irq), then they will not have been enabled
110          * for us: do so
111          */
112         gic_secondary_init(0);
113
114         /*
115          * Setup Secondary Core FIQ regs
116          */
117         cns3xxx_set_fiq_regs();
118
119         /*
120          * let the primary processor know we're out of the
121          * pen, then head off into the C entry point
122          */
123         write_pen_release(-1);
124
125         /*
126          * Fixup DMA Operations
127          *
128          */
129         cpu_cache.dma_map_area = (void *)smp_dma_map_area;
130         cpu_cache.dma_unmap_area = (void *)smp_dma_unmap_area;
131         cpu_cache.dma_flush_range = (void *)smp_dma_flush_range;
132
133         /*
134          * Synchronise with the boot thread.
135          */
136         spin_lock(&boot_lock);
137         spin_unlock(&boot_lock);
138 }
139
140 static int __cpuinit cns3xxx_boot_secondary(unsigned int cpu, struct task_struct *idle)
141 {
142         unsigned long timeout;
143
144         /*
145          * Set synchronisation state between this boot processor
146          * and the secondary one
147          */
148         spin_lock(&boot_lock);
149
150         /*
151          * The secondary processor is waiting to be released from
152          * the holding pen - release it, then wait for it to flag
153          * that it has been released by resetting pen_release.
154          *
155          * Note that "pen_release" is the hardware CPU ID, whereas
156          * "cpu" is Linux's internal ID.
157          */
158         write_pen_release(cpu);
159
160         /*
161          * Send the secondary CPU a soft interrupt, thereby causing
162          * the boot monitor to read the system wide flags register,
163          * and branch to the address found there.
164          */
165         gic_raise_softirq(cpumask_of(cpu), 1);
166
167         timeout = jiffies + (1 * HZ);
168         while (time_before(jiffies, timeout)) {
169                 smp_rmb();
170                 if (pen_release == -1)
171                         break;
172
173                 udelay(10);
174         }
175
176         /*
177          * now the secondary core is starting up let it run its
178          * calibrations, then wait for it to finish
179          */
180         spin_unlock(&boot_lock);
181
182         return pen_release != -1 ? -ENOSYS : 0;
183 }
184
185 /*
186  * Initialise the CPU possible map early - this describes the CPUs
187  * which may be present or become present in the system.
188  */
189 static void __init cns3xxx_smp_init_cpus(void)
190 {
191         unsigned int i, ncores;
192         unsigned int status;
193
194         scu_base = (void __iomem *) CNS3XXX_TC11MP_SCU_BASE_VIRT;
195
196         /* for CNS3xxx SCU_CPU_STATUS must be examined instead of SCU_CONFIGURATION
197          * used in scu_get_core_count
198          */
199         status = __raw_readl(scu_base + SCU_CPU_STATUS);
200         for (i = 0; i < NR_CPUS+1; i++) {
201                 if (((status >> (i*2)) & 0x3) == 0)
202                         set_cpu_possible(i, true);
203                 else
204                         break;
205         }
206         ncores = i;
207
208         set_smp_cross_call(gic_raise_softirq);
209 }
210
211 static void __init cns3xxx_smp_prepare_cpus(unsigned int max_cpus)
212 {
213         int i;
214
215         /*
216          * Initialise the present map, which describes the set of CPUs
217          * actually populated at the present time.
218          */
219         for (i = 0; i < max_cpus; i++) {
220                 set_cpu_present(i, true);
221         }
222
223         /*
224          * enable SCU
225          */
226         scu_enable(scu_base);
227
228         /*
229          * Write the address of secondary startup into the
230          * system-wide flags register. The boot monitor waits
231          * until it receives a soft interrupt, and then the
232          * secondary CPU branches to this address.
233          */
234         __raw_writel(virt_to_phys(cns3xxx_secondary_startup),
235                         (void __iomem *)(CNS3XXX_MISC_BASE_VIRT + 0x0600));
236
237         /*
238          * Setup FIQ's for main cpu
239          */
240         cns3xxx_init_fiq();
241         cns3xxx_set_fiq_regs();
242         memcpy((void *)&cpu_cache_save, (void *)&cpu_cache, sizeof(struct cpu_cache_fns));
243 }
244
245
246 static inline unsigned long cns3xxx_cpu_id(void)
247 {
248         unsigned long cpu;
249
250         asm volatile(
251                 " mrc p15, 0, %0, c0, c0, 5  @ cns3xxx_cpu_id\n"
252                 : "=r" (cpu) : : "memory", "cc");
253         return (cpu & 0xf);
254 }
255
256 void smp_dma_map_area(const void *addr, size_t size, int dir)
257 {
258         unsigned int cpu;
259         unsigned long flags;
260         raw_local_irq_save(flags);
261         cpu = cns3xxx_cpu_id();
262         if (cpu) {
263                 fiq_buffer[1] = (unsigned int)addr;
264                 fiq_buffer[2] = size;
265                 fiq_buffer[3] = dir | CNS3XXX_MAP_AREA | FIQ_ENABLED;
266                 smp_mb();
267                 __raw_writel(FIQ_GENERATE, MISC_FIQ_CPU(1));
268
269                 cpu_cache_save.dma_map_area(addr, size, dir);
270                 while ((fiq_buffer[3]) & FIQ_ENABLED) { barrier(); }
271         } else {
272
273                 fiq_buffer[5] = (unsigned int)addr;
274                 fiq_buffer[6] = size;
275                 fiq_buffer[7] = dir | CNS3XXX_MAP_AREA | FIQ_ENABLED;
276                 smp_mb();
277                 __raw_writel(FIQ_GENERATE, MISC_FIQ_CPU(0));
278
279                 cpu_cache_save.dma_map_area(addr, size, dir);
280                 while ((fiq_buffer[7]) & FIQ_ENABLED) { barrier(); }
281         }
282         raw_local_irq_restore(flags);
283 }
284
285 void smp_dma_unmap_area(const void *addr, size_t size, int dir)
286 {
287         unsigned int cpu;
288         unsigned long flags;
289
290         raw_local_irq_save(flags);
291         cpu = cns3xxx_cpu_id();
292         if (cpu) {
293
294                 fiq_buffer[1] = (unsigned int)addr;
295                 fiq_buffer[2] = size;
296                 fiq_buffer[3] = dir | CNS3XXX_UNMAP_AREA | FIQ_ENABLED;
297                 smp_mb();
298                 __raw_writel(FIQ_GENERATE, MISC_FIQ_CPU(1));
299
300                 cpu_cache_save.dma_unmap_area(addr, size, dir);
301                 while ((fiq_buffer[3]) & FIQ_ENABLED) { barrier(); }
302         } else {
303
304                 fiq_buffer[5] = (unsigned int)addr;
305                 fiq_buffer[6] = size;
306                 fiq_buffer[7] = dir | CNS3XXX_UNMAP_AREA | FIQ_ENABLED;
307                 smp_mb();
308                 __raw_writel(FIQ_GENERATE, MISC_FIQ_CPU(0));
309
310                 cpu_cache_save.dma_unmap_area(addr, size, dir);
311                 while ((fiq_buffer[7]) & FIQ_ENABLED) { barrier(); }
312         }
313         raw_local_irq_restore(flags);
314 }
315
316 void smp_dma_flush_range(const void *start, const void *end)
317 {
318         unsigned int cpu;
319         unsigned long flags;
320         raw_local_irq_save(flags);
321         cpu = cns3xxx_cpu_id();
322         if (cpu) {
323
324                 fiq_buffer[1] = (unsigned int)start;
325                 fiq_buffer[2] = (unsigned int)end;
326                 fiq_buffer[3] = CNS3XXX_FLUSH_RANGE | FIQ_ENABLED;
327                 smp_mb();
328                 __raw_writel(FIQ_GENERATE, MISC_FIQ_CPU(1));
329
330                 cpu_cache_save.dma_flush_range(start, end);
331                 while ((fiq_buffer[3]) & FIQ_ENABLED) { barrier(); }
332         } else {
333
334                 fiq_buffer[5] = (unsigned int)start;
335                 fiq_buffer[6] = (unsigned int)end;
336                 fiq_buffer[7] = CNS3XXX_FLUSH_RANGE | FIQ_ENABLED;
337                 smp_mb();
338                 __raw_writel(FIQ_GENERATE, MISC_FIQ_CPU(0));
339
340                 cpu_cache_save.dma_flush_range(start, end);
341                 while ((fiq_buffer[7]) & FIQ_ENABLED) { barrier(); }
342         }
343         raw_local_irq_restore(flags);
344 }
345
346 struct smp_operations cns3xxx_smp_ops __initdata = {
347         .smp_init_cpus      = cns3xxx_smp_init_cpus,
348         .smp_prepare_cpus   = cns3xxx_smp_prepare_cpus,
349         .smp_secondary_init = cns3xxx_secondary_init,
350         .smp_boot_secondary = cns3xxx_boot_secondary,
351 };