aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRussell King <rmk+kernel@arm.linux.org.uk>2013-09-17 10:13:38 -0400
committerRussell King <rmk+kernel@arm.linux.org.uk>2013-09-17 10:14:07 -0400
commit7f63037c8fb204f666367c6469f5a9b9d6888877 (patch)
treea026c999c7166aa1004e111b2e9afd7b663acdce
parent272b98c6455f00884f0350f775c5342358ebb73f (diff)
parentb22537c682671de97c932d5addb6b7d087352aa1 (diff)
Merge branch 'iks_for_rmk' of git://git.linaro.org/people/nico/linux into devel-stable
Nicolas Pitre writes: This is the first part of the patch series adding IKS (In-Kernel Switcher) support for big.LITTLE system architectures. This consists of the core patches only. Extra patches to come later will introduce various optimizations and tracing support. Those patches were posted on the list a while ago here: http://news.gmane.org/group/gmane.linux.ports.arm.kernel/thread=253942
-rw-r--r--arch/arm/Kconfig26
-rw-r--r--arch/arm/common/Makefile2
-rw-r--r--arch/arm/common/bL_switcher.c606
-rw-r--r--arch/arm/common/bL_switcher_dummy_if.c71
-rw-r--r--arch/arm/include/asm/bL_switcher.h17
-rw-r--r--arch/arm/kernel/sleep.S26
-rw-r--r--arch/arm/kernel/suspend.c8
-rw-r--r--drivers/irqchip/irq-gic.c108
-rw-r--r--include/linux/irqchip/arm-gic.h5
9 files changed, 848 insertions, 21 deletions
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 3f7714d8d2d2..b3135378ac39 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -1549,6 +1549,32 @@ config MCPM
1549 for (multi-)cluster based systems, such as big.LITTLE based 1549 for (multi-)cluster based systems, such as big.LITTLE based
1550 systems. 1550 systems.
1551 1551
1552config BIG_LITTLE
1553 bool "big.LITTLE support (Experimental)"
1554 depends on CPU_V7 && SMP
1555 select MCPM
1556 help
1557 This option enables support selections for the big.LITTLE
1558 system architecture.
1559
1560config BL_SWITCHER
1561 bool "big.LITTLE switcher support"
1562 depends on BIG_LITTLE && MCPM && HOTPLUG_CPU
1563 select CPU_PM
1564 select ARM_CPU_SUSPEND
1565 help
1566 The big.LITTLE "switcher" provides the core functionality to
1567 transparently handle transition between a cluster of A15's
1568 and a cluster of A7's in a big.LITTLE system.
1569
1570config BL_SWITCHER_DUMMY_IF
1571 tristate "Simple big.LITTLE switcher user interface"
1572 depends on BL_SWITCHER && DEBUG_KERNEL
1573 help
1574 This is a simple and dummy char dev interface to control
1575 the big.LITTLE switcher core code. It is meant for
1576 debugging purposes only.
1577
1552choice 1578choice
1553 prompt "Memory split" 1579 prompt "Memory split"
1554 default VMSPLIT_3G 1580 default VMSPLIT_3G
diff --git a/arch/arm/common/Makefile b/arch/arm/common/Makefile
index 8c60f473e976..5c8584c4944d 100644
--- a/arch/arm/common/Makefile
+++ b/arch/arm/common/Makefile
@@ -17,3 +17,5 @@ obj-$(CONFIG_MCPM) += mcpm_head.o mcpm_entry.o mcpm_platsmp.o vlock.o
17AFLAGS_mcpm_head.o := -march=armv7-a 17AFLAGS_mcpm_head.o := -march=armv7-a
18AFLAGS_vlock.o := -march=armv7-a 18AFLAGS_vlock.o := -march=armv7-a
19obj-$(CONFIG_TI_PRIV_EDMA) += edma.o 19obj-$(CONFIG_TI_PRIV_EDMA) += edma.o
20obj-$(CONFIG_BL_SWITCHER) += bL_switcher.o
21obj-$(CONFIG_BL_SWITCHER_DUMMY_IF) += bL_switcher_dummy_if.o
diff --git a/arch/arm/common/bL_switcher.c b/arch/arm/common/bL_switcher.c
new file mode 100644
index 000000000000..335ff76d4c5a
--- /dev/null
+++ b/arch/arm/common/bL_switcher.c
@@ -0,0 +1,606 @@
1/*
2 * arch/arm/common/bL_switcher.c -- big.LITTLE cluster switcher core driver
3 *
4 * Created by: Nicolas Pitre, March 2012
5 * Copyright: (C) 2012-2013 Linaro Limited
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
10 */
11
12#include <linux/init.h>
13#include <linux/kernel.h>
14#include <linux/module.h>
15#include <linux/sched.h>
16#include <linux/interrupt.h>
17#include <linux/cpu_pm.h>
18#include <linux/cpu.h>
19#include <linux/cpumask.h>
20#include <linux/kthread.h>
21#include <linux/wait.h>
22#include <linux/clockchips.h>
23#include <linux/hrtimer.h>
24#include <linux/tick.h>
25#include <linux/mm.h>
26#include <linux/string.h>
27#include <linux/sysfs.h>
28#include <linux/irqchip/arm-gic.h>
29#include <linux/moduleparam.h>
30
31#include <asm/smp_plat.h>
32#include <asm/suspend.h>
33#include <asm/mcpm.h>
34#include <asm/bL_switcher.h>
35
36
37/*
38 * Use our own MPIDR accessors as the generic ones in asm/cputype.h have
39 * __attribute_const__ and we don't want the compiler to assume any
40 * constness here as the value _does_ change along some code paths.
41 */
42
43static int read_mpidr(void)
44{
45 unsigned int id;
46 asm volatile ("mrc p15, 0, %0, c0, c0, 5" : "=r" (id));
47 return id & MPIDR_HWID_BITMASK;
48}
49
50/*
51 * bL switcher core code.
52 */
53
54static void bL_do_switch(void *_unused)
55{
56 unsigned ib_mpidr, ib_cpu, ib_cluster;
57
58 pr_debug("%s\n", __func__);
59
60 ib_mpidr = cpu_logical_map(smp_processor_id());
61 ib_cpu = MPIDR_AFFINITY_LEVEL(ib_mpidr, 0);
62 ib_cluster = MPIDR_AFFINITY_LEVEL(ib_mpidr, 1);
63
64 /*
65 * Our state has been saved at this point. Let's release our
66 * inbound CPU.
67 */
68 mcpm_set_entry_vector(ib_cpu, ib_cluster, cpu_resume);
69 sev();
70
71 /*
72 * From this point, we must assume that our counterpart CPU might
73 * have taken over in its parallel world already, as if execution
74 * just returned from cpu_suspend(). It is therefore important to
75 * be very careful not to make any change the other guy is not
76 * expecting. This is why we need stack isolation.
77 *
78 * Fancy under cover tasks could be performed here. For now
79 * we have none.
80 */
81
82 /* Let's put ourself down. */
83 mcpm_cpu_power_down();
84
85 /* should never get here */
86 BUG();
87}
88
89/*
90 * Stack isolation. To ensure 'current' remains valid, we just use another
91 * piece of our thread's stack space which should be fairly lightly used.
92 * The selected area starts just above the thread_info structure located
93 * at the very bottom of the stack, aligned to a cache line, and indexed
94 * with the cluster number.
95 */
96#define STACK_SIZE 512
97extern void call_with_stack(void (*fn)(void *), void *arg, void *sp);
98static int bL_switchpoint(unsigned long _arg)
99{
100 unsigned int mpidr = read_mpidr();
101 unsigned int clusterid = MPIDR_AFFINITY_LEVEL(mpidr, 1);
102 void *stack = current_thread_info() + 1;
103 stack = PTR_ALIGN(stack, L1_CACHE_BYTES);
104 stack += clusterid * STACK_SIZE + STACK_SIZE;
105 call_with_stack(bL_do_switch, (void *)_arg, stack);
106 BUG();
107}
108
109/*
110 * Generic switcher interface
111 */
112
113static unsigned int bL_gic_id[MAX_CPUS_PER_CLUSTER][MAX_NR_CLUSTERS];
114static int bL_switcher_cpu_pairing[NR_CPUS];
115
116/*
117 * bL_switch_to - Switch to a specific cluster for the current CPU
118 * @new_cluster_id: the ID of the cluster to switch to.
119 *
120 * This function must be called on the CPU to be switched.
121 * Returns 0 on success, else a negative status code.
122 */
123static int bL_switch_to(unsigned int new_cluster_id)
124{
125 unsigned int mpidr, this_cpu, that_cpu;
126 unsigned int ob_mpidr, ob_cpu, ob_cluster, ib_mpidr, ib_cpu, ib_cluster;
127 struct tick_device *tdev;
128 enum clock_event_mode tdev_mode;
129 int ret;
130
131 this_cpu = smp_processor_id();
132 ob_mpidr = read_mpidr();
133 ob_cpu = MPIDR_AFFINITY_LEVEL(ob_mpidr, 0);
134 ob_cluster = MPIDR_AFFINITY_LEVEL(ob_mpidr, 1);
135 BUG_ON(cpu_logical_map(this_cpu) != ob_mpidr);
136
137 if (new_cluster_id == ob_cluster)
138 return 0;
139
140 that_cpu = bL_switcher_cpu_pairing[this_cpu];
141 ib_mpidr = cpu_logical_map(that_cpu);
142 ib_cpu = MPIDR_AFFINITY_LEVEL(ib_mpidr, 0);
143 ib_cluster = MPIDR_AFFINITY_LEVEL(ib_mpidr, 1);
144
145 pr_debug("before switch: CPU %d MPIDR %#x -> %#x\n",
146 this_cpu, ob_mpidr, ib_mpidr);
147
148 /* Close the gate for our entry vectors */
149 mcpm_set_entry_vector(ob_cpu, ob_cluster, NULL);
150 mcpm_set_entry_vector(ib_cpu, ib_cluster, NULL);
151
152 /*
153 * Let's wake up the inbound CPU now in case it requires some delay
154 * to come online, but leave it gated in our entry vector code.
155 */
156 ret = mcpm_cpu_power_up(ib_cpu, ib_cluster);
157 if (ret) {
158 pr_err("%s: mcpm_cpu_power_up() returned %d\n", __func__, ret);
159 return ret;
160 }
161
162 /*
163 * From this point we are entering the switch critical zone
164 * and can't take any interrupts anymore.
165 */
166 local_irq_disable();
167 local_fiq_disable();
168
169 /* redirect GIC's SGIs to our counterpart */
170 gic_migrate_target(bL_gic_id[ib_cpu][ib_cluster]);
171
172 /*
173 * Raise a SGI on the inbound CPU to make sure it doesn't stall
174 * in a possible WFI, such as in mcpm_power_down().
175 */
176 arch_send_wakeup_ipi_mask(cpumask_of(this_cpu));
177
178 tdev = tick_get_device(this_cpu);
179 if (tdev && !cpumask_equal(tdev->evtdev->cpumask, cpumask_of(this_cpu)))
180 tdev = NULL;
181 if (tdev) {
182 tdev_mode = tdev->evtdev->mode;
183 clockevents_set_mode(tdev->evtdev, CLOCK_EVT_MODE_SHUTDOWN);
184 }
185
186 ret = cpu_pm_enter();
187
188 /* we can not tolerate errors at this point */
189 if (ret)
190 panic("%s: cpu_pm_enter() returned %d\n", __func__, ret);
191
192 /* Swap the physical CPUs in the logical map for this logical CPU. */
193 cpu_logical_map(this_cpu) = ib_mpidr;
194 cpu_logical_map(that_cpu) = ob_mpidr;
195
196 /* Let's do the actual CPU switch. */
197 ret = cpu_suspend(0, bL_switchpoint);
198 if (ret > 0)
199 panic("%s: cpu_suspend() returned %d\n", __func__, ret);
200
201 /* We are executing on the inbound CPU at this point */
202 mpidr = read_mpidr();
203 pr_debug("after switch: CPU %d MPIDR %#x\n", this_cpu, mpidr);
204 BUG_ON(mpidr != ib_mpidr);
205
206 mcpm_cpu_powered_up();
207
208 ret = cpu_pm_exit();
209
210 if (tdev) {
211 clockevents_set_mode(tdev->evtdev, tdev_mode);
212 clockevents_program_event(tdev->evtdev,
213 tdev->evtdev->next_event, 1);
214 }
215
216 local_fiq_enable();
217 local_irq_enable();
218
219 if (ret)
220 pr_err("%s exiting with error %d\n", __func__, ret);
221 return ret;
222}
223
224struct bL_thread {
225 struct task_struct *task;
226 wait_queue_head_t wq;
227 int wanted_cluster;
228 struct completion started;
229};
230
231static struct bL_thread bL_threads[NR_CPUS];
232
233static int bL_switcher_thread(void *arg)
234{
235 struct bL_thread *t = arg;
236 struct sched_param param = { .sched_priority = 1 };
237 int cluster;
238
239 sched_setscheduler_nocheck(current, SCHED_FIFO, &param);
240 complete(&t->started);
241
242 do {
243 if (signal_pending(current))
244 flush_signals(current);
245 wait_event_interruptible(t->wq,
246 t->wanted_cluster != -1 ||
247 kthread_should_stop());
248 cluster = xchg(&t->wanted_cluster, -1);
249 if (cluster != -1)
250 bL_switch_to(cluster);
251 } while (!kthread_should_stop());
252
253 return 0;
254}
255
256static struct task_struct *bL_switcher_thread_create(int cpu, void *arg)
257{
258 struct task_struct *task;
259
260 task = kthread_create_on_node(bL_switcher_thread, arg,
261 cpu_to_node(cpu), "kswitcher_%d", cpu);
262 if (!IS_ERR(task)) {
263 kthread_bind(task, cpu);
264 wake_up_process(task);
265 } else
266 pr_err("%s failed for CPU %d\n", __func__, cpu);
267 return task;
268}
269
270/*
271 * bL_switch_request - Switch to a specific cluster for the given CPU
272 *
273 * @cpu: the CPU to switch
274 * @new_cluster_id: the ID of the cluster to switch to.
275 *
276 * This function causes a cluster switch on the given CPU by waking up
277 * the appropriate switcher thread. This function may or may not return
278 * before the switch has occurred.
279 */
280int bL_switch_request(unsigned int cpu, unsigned int new_cluster_id)
281{
282 struct bL_thread *t;
283
284 if (cpu >= ARRAY_SIZE(bL_threads)) {
285 pr_err("%s: cpu %d out of bounds\n", __func__, cpu);
286 return -EINVAL;
287 }
288
289 t = &bL_threads[cpu];
290 if (IS_ERR(t->task))
291 return PTR_ERR(t->task);
292 if (!t->task)
293 return -ESRCH;
294
295 t->wanted_cluster = new_cluster_id;
296 wake_up(&t->wq);
297 return 0;
298}
299EXPORT_SYMBOL_GPL(bL_switch_request);
300
301/*
302 * Activation and configuration code.
303 */
304
305static unsigned int bL_switcher_active;
306static unsigned int bL_switcher_cpu_original_cluster[NR_CPUS];
307static cpumask_t bL_switcher_removed_logical_cpus;
308
309static void bL_switcher_restore_cpus(void)
310{
311 int i;
312
313 for_each_cpu(i, &bL_switcher_removed_logical_cpus)
314 cpu_up(i);
315}
316
317static int bL_switcher_halve_cpus(void)
318{
319 int i, j, cluster_0, gic_id, ret;
320 unsigned int cpu, cluster, mask;
321 cpumask_t available_cpus;
322
323 /* First pass to validate what we have */
324 mask = 0;
325 for_each_online_cpu(i) {
326 cpu = MPIDR_AFFINITY_LEVEL(cpu_logical_map(i), 0);
327 cluster = MPIDR_AFFINITY_LEVEL(cpu_logical_map(i), 1);
328 if (cluster >= 2) {
329 pr_err("%s: only dual cluster systems are supported\n", __func__);
330 return -EINVAL;
331 }
332 if (WARN_ON(cpu >= MAX_CPUS_PER_CLUSTER))
333 return -EINVAL;
334 mask |= (1 << cluster);
335 }
336 if (mask != 3) {
337 pr_err("%s: no CPU pairing possible\n", __func__);
338 return -EINVAL;
339 }
340
341 /*
342 * Now let's do the pairing. We match each CPU with another CPU
343 * from a different cluster. To get a uniform scheduling behavior
344 * without fiddling with CPU topology and compute capacity data,
345 * we'll use logical CPUs initially belonging to the same cluster.
346 */
347 memset(bL_switcher_cpu_pairing, -1, sizeof(bL_switcher_cpu_pairing));
348 cpumask_copy(&available_cpus, cpu_online_mask);
349 cluster_0 = -1;
350 for_each_cpu(i, &available_cpus) {
351 int match = -1;
352 cluster = MPIDR_AFFINITY_LEVEL(cpu_logical_map(i), 1);
353 if (cluster_0 == -1)
354 cluster_0 = cluster;
355 if (cluster != cluster_0)
356 continue;
357 cpumask_clear_cpu(i, &available_cpus);
358 for_each_cpu(j, &available_cpus) {
359 cluster = MPIDR_AFFINITY_LEVEL(cpu_logical_map(j), 1);
360 /*
361 * Let's remember the last match to create "odd"
362 * pairings on purpose in order for other code not
363 * to assume any relation between physical and
364 * logical CPU numbers.
365 */
366 if (cluster != cluster_0)
367 match = j;
368 }
369 if (match != -1) {
370 bL_switcher_cpu_pairing[i] = match;
371 cpumask_clear_cpu(match, &available_cpus);
372 pr_info("CPU%d paired with CPU%d\n", i, match);
373 }
374 }
375
376 /*
377 * Now we disable the unwanted CPUs i.e. everything that has no
378 * pairing information (that includes the pairing counterparts).
379 */
380 cpumask_clear(&bL_switcher_removed_logical_cpus);
381 for_each_online_cpu(i) {
382 cpu = MPIDR_AFFINITY_LEVEL(cpu_logical_map(i), 0);
383 cluster = MPIDR_AFFINITY_LEVEL(cpu_logical_map(i), 1);
384
385 /* Let's take note of the GIC ID for this CPU */
386 gic_id = gic_get_cpu_id(i);
387 if (gic_id < 0) {
388 pr_err("%s: bad GIC ID for CPU %d\n", __func__, i);
389 bL_switcher_restore_cpus();
390 return -EINVAL;
391 }
392 bL_gic_id[cpu][cluster] = gic_id;
393 pr_info("GIC ID for CPU %u cluster %u is %u\n",
394 cpu, cluster, gic_id);
395
396 if (bL_switcher_cpu_pairing[i] != -1) {
397 bL_switcher_cpu_original_cluster[i] = cluster;
398 continue;
399 }
400
401 ret = cpu_down(i);
402 if (ret) {
403 bL_switcher_restore_cpus();
404 return ret;
405 }
406 cpumask_set_cpu(i, &bL_switcher_removed_logical_cpus);
407 }
408
409 return 0;
410}
411
412static int bL_switcher_enable(void)
413{
414 int cpu, ret;
415
416 cpu_hotplug_driver_lock();
417 if (bL_switcher_active) {
418 cpu_hotplug_driver_unlock();
419 return 0;
420 }
421
422 pr_info("big.LITTLE switcher initializing\n");
423
424 ret = bL_switcher_halve_cpus();
425 if (ret) {
426 cpu_hotplug_driver_unlock();
427 return ret;
428 }
429
430 for_each_online_cpu(cpu) {
431 struct bL_thread *t = &bL_threads[cpu];
432 init_waitqueue_head(&t->wq);
433 init_completion(&t->started);
434 t->wanted_cluster = -1;
435 t->task = bL_switcher_thread_create(cpu, t);
436 }
437
438 bL_switcher_active = 1;
439 cpu_hotplug_driver_unlock();
440
441 pr_info("big.LITTLE switcher initialized\n");
442 return 0;
443}
444
445#ifdef CONFIG_SYSFS
446
447static void bL_switcher_disable(void)
448{
449 unsigned int cpu, cluster;
450 struct bL_thread *t;
451 struct task_struct *task;
452
453 cpu_hotplug_driver_lock();
454 if (!bL_switcher_active) {
455 cpu_hotplug_driver_unlock();
456 return;
457 }
458 bL_switcher_active = 0;
459
460 /*
461 * To deactivate the switcher, we must shut down the switcher
462 * threads to prevent any other requests from being accepted.
463 * Then, if the final cluster for given logical CPU is not the
464 * same as the original one, we'll recreate a switcher thread
465 * just for the purpose of switching the CPU back without any
466 * possibility for interference from external requests.
467 */
468 for_each_online_cpu(cpu) {
469 t = &bL_threads[cpu];
470 task = t->task;
471 t->task = NULL;
472 if (!task || IS_ERR(task))
473 continue;
474 kthread_stop(task);
475 /* no more switch may happen on this CPU at this point */
476 cluster = MPIDR_AFFINITY_LEVEL(cpu_logical_map(cpu), 1);
477 if (cluster == bL_switcher_cpu_original_cluster[cpu])
478 continue;
479 init_completion(&t->started);
480 t->wanted_cluster = bL_switcher_cpu_original_cluster[cpu];
481 task = bL_switcher_thread_create(cpu, t);
482 if (!IS_ERR(task)) {
483 wait_for_completion(&t->started);
484 kthread_stop(task);
485 cluster = MPIDR_AFFINITY_LEVEL(cpu_logical_map(cpu), 1);
486 if (cluster == bL_switcher_cpu_original_cluster[cpu])
487 continue;
488 }
489 /* If execution gets here, we're in trouble. */
490 pr_crit("%s: unable to restore original cluster for CPU %d\n",
491 __func__, cpu);
492 pr_crit("%s: CPU %d can't be restored\n",
493 __func__, bL_switcher_cpu_pairing[cpu]);
494 cpumask_clear_cpu(bL_switcher_cpu_pairing[cpu],
495 &bL_switcher_removed_logical_cpus);
496 }
497
498 bL_switcher_restore_cpus();
499 cpu_hotplug_driver_unlock();
500}
501
502static ssize_t bL_switcher_active_show(struct kobject *kobj,
503 struct kobj_attribute *attr, char *buf)
504{
505 return sprintf(buf, "%u\n", bL_switcher_active);
506}
507
508static ssize_t bL_switcher_active_store(struct kobject *kobj,
509 struct kobj_attribute *attr, const char *buf, size_t count)
510{
511 int ret;
512
513 switch (buf[0]) {
514 case '0':
515 bL_switcher_disable();
516 ret = 0;
517 break;
518 case '1':
519 ret = bL_switcher_enable();
520 break;
521 default:
522 ret = -EINVAL;
523 }
524
525 return (ret >= 0) ? count : ret;
526}
527
528static struct kobj_attribute bL_switcher_active_attr =
529 __ATTR(active, 0644, bL_switcher_active_show, bL_switcher_active_store);
530
531static struct attribute *bL_switcher_attrs[] = {
532 &bL_switcher_active_attr.attr,
533 NULL,
534};
535
536static struct attribute_group bL_switcher_attr_group = {
537 .attrs = bL_switcher_attrs,
538};
539
540static struct kobject *bL_switcher_kobj;
541
542static int __init bL_switcher_sysfs_init(void)
543{
544 int ret;
545
546 bL_switcher_kobj = kobject_create_and_add("bL_switcher", kernel_kobj);
547 if (!bL_switcher_kobj)
548 return -ENOMEM;
549 ret = sysfs_create_group(bL_switcher_kobj, &bL_switcher_attr_group);
550 if (ret)
551 kobject_put(bL_switcher_kobj);
552 return ret;
553}
554
555#endif /* CONFIG_SYSFS */
556
557/*
558 * Veto any CPU hotplug operation on those CPUs we've removed
559 * while the switcher is active.
560 * We're just not ready to deal with that given the trickery involved.
561 */
562static int bL_switcher_hotplug_callback(struct notifier_block *nfb,
563 unsigned long action, void *hcpu)
564{
565 if (bL_switcher_active) {
566 int pairing = bL_switcher_cpu_pairing[(unsigned long)hcpu];
567 switch (action & 0xf) {
568 case CPU_UP_PREPARE:
569 case CPU_DOWN_PREPARE:
570 if (pairing == -1)
571 return NOTIFY_BAD;
572 }
573 }
574 return NOTIFY_DONE;
575}
576
577static bool no_bL_switcher;
578core_param(no_bL_switcher, no_bL_switcher, bool, 0644);
579
580static int __init bL_switcher_init(void)
581{
582 int ret;
583
584 if (MAX_NR_CLUSTERS != 2) {
585 pr_err("%s: only dual cluster systems are supported\n", __func__);
586 return -EINVAL;
587 }
588
589 cpu_notifier(bL_switcher_hotplug_callback, 0);
590
591 if (!no_bL_switcher) {
592 ret = bL_switcher_enable();
593 if (ret)
594 return ret;
595 }
596
597#ifdef CONFIG_SYSFS
598 ret = bL_switcher_sysfs_init();
599 if (ret)
600 pr_err("%s: unable to create sysfs entry\n", __func__);
601#endif
602
603 return 0;
604}
605
606late_initcall(bL_switcher_init);
diff --git a/arch/arm/common/bL_switcher_dummy_if.c b/arch/arm/common/bL_switcher_dummy_if.c
new file mode 100644
index 000000000000..3f47f1203c6b
--- /dev/null
+++ b/arch/arm/common/bL_switcher_dummy_if.c
@@ -0,0 +1,71 @@
1/*
2 * arch/arm/common/bL_switcher_dummy_if.c -- b.L switcher dummy interface
3 *
4 * Created by: Nicolas Pitre, November 2012
5 * Copyright: (C) 2012-2013 Linaro Limited
6 *
7 * Dummy interface to user space for debugging purpose only.
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License version 2 as
11 * published by the Free Software Foundation.
12 */
13
14#include <linux/init.h>
15#include <linux/module.h>
16#include <linux/fs.h>
17#include <linux/miscdevice.h>
18#include <asm/uaccess.h>
19#include <asm/bL_switcher.h>
20
21static ssize_t bL_switcher_write(struct file *file, const char __user *buf,
22 size_t len, loff_t *pos)
23{
24 unsigned char val[3];
25 unsigned int cpu, cluster;
26 int ret;
27
28 pr_debug("%s\n", __func__);
29
30 if (len < 3)
31 return -EINVAL;
32
33 if (copy_from_user(val, buf, 3))
34 return -EFAULT;
35
36 /* format: <cpu#>,<cluster#> */
37 if (val[0] < '0' || val[0] > '9' ||
38 val[1] != ',' ||
39 val[2] < '0' || val[2] > '1')
40 return -EINVAL;
41
42 cpu = val[0] - '0';
43 cluster = val[2] - '0';
44 ret = bL_switch_request(cpu, cluster);
45
46 return ret ? : len;
47}
48
49static const struct file_operations bL_switcher_fops = {
50 .write = bL_switcher_write,
51 .owner = THIS_MODULE,
52};
53
54static struct miscdevice bL_switcher_device = {
55 MISC_DYNAMIC_MINOR,
56 "b.L_switcher",
57 &bL_switcher_fops
58};
59
60static int __init bL_switcher_dummy_if_init(void)
61{
62 return misc_register(&bL_switcher_device);
63}
64
65static void __exit bL_switcher_dummy_if_exit(void)
66{
67 misc_deregister(&bL_switcher_device);
68}
69
70module_init(bL_switcher_dummy_if_init);
71module_exit(bL_switcher_dummy_if_exit);
diff --git a/arch/arm/include/asm/bL_switcher.h b/arch/arm/include/asm/bL_switcher.h
new file mode 100644
index 000000000000..e0c0bba70bbf
--- /dev/null
+++ b/arch/arm/include/asm/bL_switcher.h
@@ -0,0 +1,17 @@
1/*
2 * arch/arm/include/asm/bL_switcher.h
3 *
4 * Created by: Nicolas Pitre, April 2012
5 * Copyright: (C) 2012-2013 Linaro Limited
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
10 */
11
12#ifndef ASM_BL_SWITCHER_H
13#define ASM_BL_SWITCHER_H
14
15int bL_switch_request(unsigned int cpu, unsigned int new_cluster_id);
16
17#endif
diff --git a/arch/arm/kernel/sleep.S b/arch/arm/kernel/sleep.S
index db1536b8b30b..622460201911 100644
--- a/arch/arm/kernel/sleep.S
+++ b/arch/arm/kernel/sleep.S
@@ -55,6 +55,7 @@
55 * specific registers and some other data for resume. 55 * specific registers and some other data for resume.
56 * r0 = suspend function arg0 56 * r0 = suspend function arg0
57 * r1 = suspend function 57 * r1 = suspend function
58 * r2 = MPIDR value the resuming CPU will use
58 */ 59 */
59ENTRY(__cpu_suspend) 60ENTRY(__cpu_suspend)
60 stmfd sp!, {r4 - r11, lr} 61 stmfd sp!, {r4 - r11, lr}
@@ -67,23 +68,18 @@ ENTRY(__cpu_suspend)
67 mov r5, sp @ current virtual SP 68 mov r5, sp @ current virtual SP
68 add r4, r4, #12 @ Space for pgd, virt sp, phys resume fn 69 add r4, r4, #12 @ Space for pgd, virt sp, phys resume fn
69 sub sp, sp, r4 @ allocate CPU state on stack 70 sub sp, sp, r4 @ allocate CPU state on stack
70 stmfd sp!, {r0, r1} @ save suspend func arg and pointer
71 add r0, sp, #8 @ save pointer to save block
72 mov r1, r4 @ size of save block
73 mov r2, r5 @ virtual SP
74 ldr r3, =sleep_save_sp 71 ldr r3, =sleep_save_sp
72 stmfd sp!, {r0, r1} @ save suspend func arg and pointer
75 ldr r3, [r3, #SLEEP_SAVE_SP_VIRT] 73 ldr r3, [r3, #SLEEP_SAVE_SP_VIRT]
76 ALT_SMP(mrc p15, 0, r9, c0, c0, 5) 74 ALT_SMP(ldr r0, =mpidr_hash)
77 ALT_UP_B(1f) 75 ALT_UP_B(1f)
78 ldr r8, =mpidr_hash 76 /* This ldmia relies on the memory layout of the mpidr_hash struct */
79 /* 77 ldmia r0, {r1, r6-r8} @ r1 = mpidr mask (r6,r7,r8) = l[0,1,2] shifts
80 * This ldmia relies on the memory layout of the mpidr_hash 78 compute_mpidr_hash r0, r6, r7, r8, r2, r1
81 * struct mpidr_hash. 79 add r3, r3, r0, lsl #2
82 */ 801: mov r2, r5 @ virtual SP
83 ldmia r8, {r4-r7} @ r4 = mpidr mask (r5,r6,r7) = l[0,1,2] shifts 81 mov r1, r4 @ size of save block
84 compute_mpidr_hash lr, r5, r6, r7, r9, r4 82 add r0, sp, #8 @ pointer to save block
85 add r3, r3, lr, lsl #2
861:
87 bl __cpu_suspend_save 83 bl __cpu_suspend_save
88 adr lr, BSYM(cpu_suspend_abort) 84 adr lr, BSYM(cpu_suspend_abort)
89 ldmfd sp!, {r0, pc} @ call suspend fn 85 ldmfd sp!, {r0, pc} @ call suspend fn
diff --git a/arch/arm/kernel/suspend.c b/arch/arm/kernel/suspend.c
index 41cf3cbf756d..2835d35234ca 100644
--- a/arch/arm/kernel/suspend.c
+++ b/arch/arm/kernel/suspend.c
@@ -10,7 +10,7 @@
10#include <asm/suspend.h> 10#include <asm/suspend.h>
11#include <asm/tlbflush.h> 11#include <asm/tlbflush.h>
12 12
13extern int __cpu_suspend(unsigned long, int (*)(unsigned long)); 13extern int __cpu_suspend(unsigned long, int (*)(unsigned long), u32 cpuid);
14extern void cpu_resume_mmu(void); 14extern void cpu_resume_mmu(void);
15 15
16#ifdef CONFIG_MMU 16#ifdef CONFIG_MMU
@@ -21,6 +21,7 @@ extern void cpu_resume_mmu(void);
21int cpu_suspend(unsigned long arg, int (*fn)(unsigned long)) 21int cpu_suspend(unsigned long arg, int (*fn)(unsigned long))
22{ 22{
23 struct mm_struct *mm = current->active_mm; 23 struct mm_struct *mm = current->active_mm;
24 u32 __mpidr = cpu_logical_map(smp_processor_id());
24 int ret; 25 int ret;
25 26
26 if (!idmap_pgd) 27 if (!idmap_pgd)
@@ -32,7 +33,7 @@ int cpu_suspend(unsigned long arg, int (*fn)(unsigned long))
32 * resume (indicated by a zero return code), we need to switch 33 * resume (indicated by a zero return code), we need to switch
33 * back to the correct page tables. 34 * back to the correct page tables.
34 */ 35 */
35 ret = __cpu_suspend(arg, fn); 36 ret = __cpu_suspend(arg, fn, __mpidr);
36 if (ret == 0) { 37 if (ret == 0) {
37 cpu_switch_mm(mm->pgd, mm); 38 cpu_switch_mm(mm->pgd, mm);
38 local_flush_bp_all(); 39 local_flush_bp_all();
@@ -44,7 +45,8 @@ int cpu_suspend(unsigned long arg, int (*fn)(unsigned long))
44#else 45#else
45int cpu_suspend(unsigned long arg, int (*fn)(unsigned long)) 46int cpu_suspend(unsigned long arg, int (*fn)(unsigned long))
46{ 47{
47 return __cpu_suspend(arg, fn); 48 u32 __mpidr = cpu_logical_map(smp_processor_id());
49 return __cpu_suspend(arg, fn, __mpidr);
48} 50}
49#define idmap_pgd NULL 51#define idmap_pgd NULL
50#endif 52#endif
diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c
index d0e948084eaf..6365b59181ee 100644
--- a/drivers/irqchip/irq-gic.c
+++ b/drivers/irqchip/irq-gic.c
@@ -253,10 +253,9 @@ static int gic_set_affinity(struct irq_data *d, const struct cpumask *mask_val,
253 if (cpu >= NR_GIC_CPU_IF || cpu >= nr_cpu_ids) 253 if (cpu >= NR_GIC_CPU_IF || cpu >= nr_cpu_ids)
254 return -EINVAL; 254 return -EINVAL;
255 255
256 raw_spin_lock(&irq_controller_lock);
256 mask = 0xff << shift; 257 mask = 0xff << shift;
257 bit = gic_cpu_map[cpu] << shift; 258 bit = gic_cpu_map[cpu] << shift;
258
259 raw_spin_lock(&irq_controller_lock);
260 val = readl_relaxed(reg) & ~mask; 259 val = readl_relaxed(reg) & ~mask;
261 writel_relaxed(val | bit, reg); 260 writel_relaxed(val | bit, reg);
262 raw_spin_unlock(&irq_controller_lock); 261 raw_spin_unlock(&irq_controller_lock);
@@ -652,7 +651,9 @@ static void __init gic_pm_init(struct gic_chip_data *gic)
652void gic_raise_softirq(const struct cpumask *mask, unsigned int irq) 651void gic_raise_softirq(const struct cpumask *mask, unsigned int irq)
653{ 652{
654 int cpu; 653 int cpu;
655 unsigned long map = 0; 654 unsigned long flags, map = 0;
655
656 raw_spin_lock_irqsave(&irq_controller_lock, flags);
656 657
657 /* Convert our logical CPU mask into a physical one. */ 658 /* Convert our logical CPU mask into a physical one. */
658 for_each_cpu(cpu, mask) 659 for_each_cpu(cpu, mask)
@@ -666,6 +667,107 @@ void gic_raise_softirq(const struct cpumask *mask, unsigned int irq)
666 667
667 /* this always happens on GIC0 */ 668 /* this always happens on GIC0 */
668 writel_relaxed(map << 16 | irq, gic_data_dist_base(&gic_data[0]) + GIC_DIST_SOFTINT); 669 writel_relaxed(map << 16 | irq, gic_data_dist_base(&gic_data[0]) + GIC_DIST_SOFTINT);
670
671 raw_spin_unlock_irqrestore(&irq_controller_lock, flags);
672}
673#endif
674
675#ifdef CONFIG_BL_SWITCHER
676/*
677 * gic_get_cpu_id - get the CPU interface ID for the specified CPU
678 *
679 * @cpu: the logical CPU number to get the GIC ID for.
680 *
681 * Return the CPU interface ID for the given logical CPU number,
682 * or -1 if the CPU number is too large or the interface ID is
683 * unknown (more than one bit set).
684 */
685int gic_get_cpu_id(unsigned int cpu)
686{
687 unsigned int cpu_bit;
688
689 if (cpu >= NR_GIC_CPU_IF)
690 return -1;
691 cpu_bit = gic_cpu_map[cpu];
692 if (cpu_bit & (cpu_bit - 1))
693 return -1;
694 return __ffs(cpu_bit);
695}
696
697/*
698 * gic_migrate_target - migrate IRQs to another CPU interface
699 *
700 * @new_cpu_id: the CPU target ID to migrate IRQs to
701 *
702 * Migrate all peripheral interrupts with a target matching the current CPU
703 * to the interface corresponding to @new_cpu_id. The CPU interface mapping
704 * is also updated. Targets to other CPU interfaces are unchanged.
705 * This must be called with IRQs locally disabled.
706 */
707void gic_migrate_target(unsigned int new_cpu_id)
708{
709 unsigned int cur_cpu_id, gic_irqs, gic_nr = 0;
710 void __iomem *dist_base;
711 int i, ror_val, cpu = smp_processor_id();
712 u32 val, cur_target_mask, active_mask;
713
714 if (gic_nr >= MAX_GIC_NR)
715 BUG();
716
717 dist_base = gic_data_dist_base(&gic_data[gic_nr]);
718 if (!dist_base)
719 return;
720 gic_irqs = gic_data[gic_nr].gic_irqs;
721
722 cur_cpu_id = __ffs(gic_cpu_map[cpu]);
723 cur_target_mask = 0x01010101 << cur_cpu_id;
724 ror_val = (cur_cpu_id - new_cpu_id) & 31;
725
726 raw_spin_lock(&irq_controller_lock);
727
728 /* Update the target interface for this logical CPU */
729 gic_cpu_map[cpu] = 1 << new_cpu_id;
730
731 /*
732 * Find all the peripheral interrupts targetting the current
733 * CPU interface and migrate them to the new CPU interface.
734 * We skip DIST_TARGET 0 to 7 as they are read-only.
735 */
736 for (i = 8; i < DIV_ROUND_UP(gic_irqs, 4); i++) {
737 val = readl_relaxed(dist_base + GIC_DIST_TARGET + i * 4);
738 active_mask = val & cur_target_mask;
739 if (active_mask) {
740 val &= ~active_mask;
741 val |= ror32(active_mask, ror_val);
742 writel_relaxed(val, dist_base + GIC_DIST_TARGET + i*4);
743 }
744 }
745
746 raw_spin_unlock(&irq_controller_lock);
747
748 /*
749 * Now let's migrate and clear any potential SGIs that might be
750 * pending for us (cur_cpu_id). Since GIC_DIST_SGI_PENDING_SET
751 * is a banked register, we can only forward the SGI using
752 * GIC_DIST_SOFTINT. The original SGI source is lost but Linux
753 * doesn't use that information anyway.
754 *
755 * For the same reason we do not adjust SGI source information
756 * for previously sent SGIs by us to other CPUs either.
757 */
758 for (i = 0; i < 16; i += 4) {
759 int j;
760 val = readl_relaxed(dist_base + GIC_DIST_SGI_PENDING_SET + i);
761 if (!val)
762 continue;
763 writel_relaxed(val, dist_base + GIC_DIST_SGI_PENDING_CLEAR + i);
764 for (j = i; j < i + 4; j++) {
765 if (val & 0xff)
766 writel_relaxed((1 << (new_cpu_id + 16)) | j,
767 dist_base + GIC_DIST_SOFTINT);
768 val >>= 8;
769 }
770 }
669} 771}
670#endif 772#endif
671 773
diff --git a/include/linux/irqchip/arm-gic.h b/include/linux/irqchip/arm-gic.h
index 0e5d9ecdb2b6..46544e381bf9 100644
--- a/include/linux/irqchip/arm-gic.h
+++ b/include/linux/irqchip/arm-gic.h
@@ -31,6 +31,8 @@
31#define GIC_DIST_TARGET 0x800 31#define GIC_DIST_TARGET 0x800
32#define GIC_DIST_CONFIG 0xc00 32#define GIC_DIST_CONFIG 0xc00
33#define GIC_DIST_SOFTINT 0xf00 33#define GIC_DIST_SOFTINT 0xf00
34#define GIC_DIST_SGI_PENDING_CLEAR 0xf10
35#define GIC_DIST_SGI_PENDING_SET 0xf20
34 36
35#define GICH_HCR 0x0 37#define GICH_HCR 0x0
36#define GICH_VTR 0x4 38#define GICH_VTR 0x4
@@ -74,6 +76,9 @@ static inline void gic_init(unsigned int nr, int start,
74 gic_init_bases(nr, start, dist, cpu, 0, NULL); 76 gic_init_bases(nr, start, dist, cpu, 0, NULL);
75} 77}
76 78
79int gic_get_cpu_id(unsigned int cpu);
80void gic_migrate_target(unsigned int new_cpu_id);
81
77#endif /* __ASSEMBLY */ 82#endif /* __ASSEMBLY */
78 83
79#endif 84#endif