]> git.karo-electronics.de Git - karo-tx-linux.git/blob - arch/arm/common/bL_switcher.c
ARM: bL_switcher: move to dedicated threads rather than workqueues
[karo-tx-linux.git] / arch / arm / common / bL_switcher.c
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/irqchip/arm-gic.h>
28
29 #include <asm/smp_plat.h>
30 #include <asm/suspend.h>
31 #include <asm/mcpm.h>
32 #include <asm/bL_switcher.h>
33
34
35 /*
36  * Use our own MPIDR accessors as the generic ones in asm/cputype.h have
37  * __attribute_const__ and we don't want the compiler to assume any
38  * constness here as the value _does_ change along some code paths.
39  */
40
41 static int read_mpidr(void)
42 {
43         unsigned int id;
44         asm volatile ("mrc p15, 0, %0, c0, c0, 5" : "=r" (id));
45         return id & MPIDR_HWID_BITMASK;
46 }
47
48 /*
49  * bL switcher core code.
50  */
51
52 static void bL_do_switch(void *_unused)
53 {
54         unsigned mpidr, cpuid, clusterid, ob_cluster, ib_cluster;
55
56         /*
57          * We now have a piece of stack borrowed from the init task's.
58          * Let's also switch to init_mm right away to match it.
59          */
60         cpu_switch_mm(init_mm.pgd, &init_mm);
61
62         pr_debug("%s\n", __func__);
63
64         mpidr = read_mpidr();
65         cpuid = MPIDR_AFFINITY_LEVEL(mpidr, 0);
66         clusterid = MPIDR_AFFINITY_LEVEL(mpidr, 1);
67         ob_cluster = clusterid;
68         ib_cluster = clusterid ^ 1;
69
70         /*
71          * Our state has been saved at this point.  Let's release our
72          * inbound CPU.
73          */
74         mcpm_set_entry_vector(cpuid, ib_cluster, cpu_resume);
75         sev();
76
77         /*
78          * From this point, we must assume that our counterpart CPU might
79          * have taken over in its parallel world already, as if execution
80          * just returned from cpu_suspend().  It is therefore important to
81          * be very careful not to make any change the other guy is not
82          * expecting.  This is why we need stack isolation.
83          *
84          * Fancy under cover tasks could be performed here.  For now
85          * we have none.
86          */
87
88         /* Let's put ourself down. */
89         mcpm_cpu_power_down();
90
91         /* should never get here */
92         BUG();
93 }
94
95 /*
96  * Stack isolation.  To ensure 'current' remains valid, we just borrow
97  * a slice of the init/idle task which should be fairly lightly used.
98  * The borrowed area starts just above the thread_info structure located
99  * at the very bottom of the stack, aligned to a cache line.
100  */
101 #define STACK_SIZE 256
102 extern void call_with_stack(void (*fn)(void *), void *arg, void *sp);
103 static int bL_switchpoint(unsigned long _arg)
104 {
105         unsigned int mpidr = read_mpidr();
106         unsigned int cpuid = MPIDR_AFFINITY_LEVEL(mpidr, 0);
107         unsigned int clusterid = MPIDR_AFFINITY_LEVEL(mpidr, 1);
108         unsigned int cpu_index = cpuid + clusterid * MAX_CPUS_PER_CLUSTER;
109         void *stack = &init_thread_info + 1;
110         stack = PTR_ALIGN(stack, L1_CACHE_BYTES);
111         stack += cpu_index * STACK_SIZE + STACK_SIZE;
112         call_with_stack(bL_do_switch, (void *)_arg, stack);
113         BUG();
114 }
115
116 /*
117  * Generic switcher interface
118  */
119
120 /*
121  * bL_switch_to - Switch to a specific cluster for the current CPU
122  * @new_cluster_id: the ID of the cluster to switch to.
123  *
124  * This function must be called on the CPU to be switched.
125  * Returns 0 on success, else a negative status code.
126  */
127 static int bL_switch_to(unsigned int new_cluster_id)
128 {
129         unsigned int mpidr, cpuid, clusterid, ob_cluster, ib_cluster, this_cpu;
130         struct tick_device *tdev;
131         enum clock_event_mode tdev_mode;
132         int ret;
133
134         mpidr = read_mpidr();
135         cpuid = MPIDR_AFFINITY_LEVEL(mpidr, 0);
136         clusterid = MPIDR_AFFINITY_LEVEL(mpidr, 1);
137         ob_cluster = clusterid;
138         ib_cluster = clusterid ^ 1;
139
140         if (new_cluster_id == clusterid)
141                 return 0;
142
143         pr_debug("before switch: CPU %d in cluster %d\n", cpuid, clusterid);
144
145         /* Close the gate for our entry vectors */
146         mcpm_set_entry_vector(cpuid, ob_cluster, NULL);
147         mcpm_set_entry_vector(cpuid, ib_cluster, NULL);
148
149         /*
150          * Let's wake up the inbound CPU now in case it requires some delay
151          * to come online, but leave it gated in our entry vector code.
152          */
153         ret = mcpm_cpu_power_up(cpuid, ib_cluster);
154         if (ret) {
155                 pr_err("%s: mcpm_cpu_power_up() returned %d\n", __func__, ret);
156                 return ret;
157         }
158
159         /*
160          * From this point we are entering the switch critical zone
161          * and can't take any interrupts anymore.
162          */
163         local_irq_disable();
164         local_fiq_disable();
165
166         this_cpu = smp_processor_id();
167
168         /* redirect GIC's SGIs to our counterpart */
169         gic_migrate_target(cpuid + ib_cluster*4);
170
171         /*
172          * Raise a SGI on the inbound CPU to make sure it doesn't stall
173          * in a possible WFI, such as in mcpm_power_down().
174          */
175         arch_send_wakeup_ipi_mask(cpumask_of(this_cpu));
176
177         tdev = tick_get_device(this_cpu);
178         if (tdev && !cpumask_equal(tdev->evtdev->cpumask, cpumask_of(this_cpu)))
179                 tdev = NULL;
180         if (tdev) {
181                 tdev_mode = tdev->evtdev->mode;
182                 clockevents_set_mode(tdev->evtdev, CLOCK_EVT_MODE_SHUTDOWN);
183         }
184
185         ret = cpu_pm_enter();
186
187         /* we can not tolerate errors at this point */
188         if (ret)
189                 panic("%s: cpu_pm_enter() returned %d\n", __func__, ret);
190
191         /* Flip the cluster in the CPU logical map for this CPU. */
192         cpu_logical_map(this_cpu) ^= (1 << 8);
193
194         /* Let's do the actual CPU switch. */
195         ret = cpu_suspend(0, bL_switchpoint);
196         if (ret > 0)
197                 panic("%s: cpu_suspend() returned %d\n", __func__, ret);
198
199         /* We are executing on the inbound CPU at this point */
200         mpidr = read_mpidr();
201         cpuid = MPIDR_AFFINITY_LEVEL(mpidr, 0);
202         clusterid = MPIDR_AFFINITY_LEVEL(mpidr, 1);
203         pr_debug("after switch: CPU %d in cluster %d\n", cpuid, clusterid);
204         BUG_ON(clusterid != ib_cluster);
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
224 struct bL_thread {
225         struct task_struct *task;
226         wait_queue_head_t wq;
227         int wanted_cluster;
228 };
229
230 static struct bL_thread bL_threads[NR_CPUS];
231
232 static int bL_switcher_thread(void *arg)
233 {
234         struct bL_thread *t = arg;
235         struct sched_param param = { .sched_priority = 1 };
236         int cluster;
237
238         sched_setscheduler_nocheck(current, SCHED_FIFO, &param);
239
240         do {
241                 if (signal_pending(current))
242                         flush_signals(current);
243                 wait_event_interruptible(t->wq,
244                                 t->wanted_cluster != -1 ||
245                                 kthread_should_stop());
246                 cluster = xchg(&t->wanted_cluster, -1);
247                 if (cluster != -1)
248                         bL_switch_to(cluster);
249         } while (!kthread_should_stop());
250
251         return 0;
252 }
253
254 static struct task_struct * __init bL_switcher_thread_create(int cpu, void *arg)
255 {
256         struct task_struct *task;
257
258         task = kthread_create_on_node(bL_switcher_thread, arg,
259                                       cpu_to_node(cpu), "kswitcher_%d", cpu);
260         if (!IS_ERR(task)) {
261                 kthread_bind(task, cpu);
262                 wake_up_process(task);
263         } else
264                 pr_err("%s failed for CPU %d\n", __func__, cpu);
265         return task;
266 }
267
268 /*
269  * bL_switch_request - Switch to a specific cluster for the given CPU
270  *
271  * @cpu: the CPU to switch
272  * @new_cluster_id: the ID of the cluster to switch to.
273  *
274  * This function causes a cluster switch on the given CPU by waking up
275  * the appropriate switcher thread.  This function may or may not return
276  * before the switch has occurred.
277  */
278 int bL_switch_request(unsigned int cpu, unsigned int new_cluster_id)
279 {
280         struct bL_thread *t;
281
282         if (cpu >= ARRAY_SIZE(bL_threads)) {
283                 pr_err("%s: cpu %d out of bounds\n", __func__, cpu);
284                 return -EINVAL;
285         }
286
287         t = &bL_threads[cpu];
288         if (IS_ERR(t->task))
289                 return PTR_ERR(t->task);
290         if (!t->task)
291                 return -ESRCH;
292
293         t->wanted_cluster = new_cluster_id;
294         wake_up(&t->wq);
295         return 0;
296 }
297 EXPORT_SYMBOL_GPL(bL_switch_request);
298
299 static int __init bL_switcher_init(void)
300 {
301         int cpu;
302
303         pr_info("big.LITTLE switcher initializing\n");
304
305         for_each_online_cpu(cpu) {
306                 struct bL_thread *t = &bL_threads[cpu];
307                 init_waitqueue_head(&t->wq);
308                 t->wanted_cluster = -1;
309                 t->task = bL_switcher_thread_create(cpu, t);
310         }
311
312         pr_info("big.LITTLE switcher initialized\n");
313         return 0;
314 }
315
316 late_initcall(bL_switcher_init);