summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNicolas Pitre <nicolas.pitre@linaro.org>2012-11-21 20:53:27 +0400
committerNicolas Pitre <nicolas.pitre@linaro.org>2013-07-30 17:02:16 +0400
commit9797a0e95ead7bfe52260c369ee9fe6ba445afaf (patch)
tree01ed44a4f452c0e62465992066d2c517288d6d27
parentc052de2693498bee6ff2e1b5bcd32309c4f8780b (diff)
downloadlinux-9797a0e95ead7bfe52260c369ee9fe6ba445afaf.tar.xz
ARM: bL_switcher: hot-unplug half of the available CPUs
In a regular kernel configuration, all the CPUs are initially available. But the switcher execution model uses half of them at any time. Instead of hacking the DTB to remove half of the CPUs, let's remove them at run time and make sure we still have a working switcher configuration. This way, the same DTB can be used whether or not the switcher is used. Signed-off-by: Nicolas Pitre <nico@linaro.org>
-rw-r--r--arch/arm/common/bL_switcher.c78
1 files changed, 77 insertions, 1 deletions
diff --git a/arch/arm/common/bL_switcher.c b/arch/arm/common/bL_switcher.c
index 4caca71c906f..50e95d894e35 100644
--- a/arch/arm/common/bL_switcher.c
+++ b/arch/arm/common/bL_switcher.c
@@ -289,18 +289,94 @@ int bL_switch_request(unsigned int cpu, unsigned int new_cluster_id)
}
EXPORT_SYMBOL_GPL(bL_switch_request);
+/*
+ * Activation and configuration code.
+ */
+
+static cpumask_t bL_switcher_removed_logical_cpus;
+
+static void __init bL_switcher_restore_cpus(void)
+{
+ int i;
+
+ for_each_cpu(i, &bL_switcher_removed_logical_cpus)
+ cpu_up(i);
+}
+
+static int __init bL_switcher_halve_cpus(void)
+{
+ int cpu, cluster, i, ret;
+ cpumask_t cluster_mask[2], common_mask;
+
+ cpumask_clear(&bL_switcher_removed_logical_cpus);
+ cpumask_clear(&cluster_mask[0]);
+ cpumask_clear(&cluster_mask[1]);
+
+ for_each_online_cpu(i) {
+ cpu = cpu_logical_map(i) & 0xff;
+ cluster = (cpu_logical_map(i) >> 8) & 0xff;
+ if (cluster >= 2) {
+ pr_err("%s: only dual cluster systems are supported\n", __func__);
+ return -EINVAL;
+ }
+ cpumask_set_cpu(cpu, &cluster_mask[cluster]);
+ }
+
+ if (!cpumask_and(&common_mask, &cluster_mask[0], &cluster_mask[1])) {
+ pr_err("%s: no common set of CPUs\n", __func__);
+ return -EINVAL;
+ }
+
+ for_each_online_cpu(i) {
+ cpu = cpu_logical_map(i) & 0xff;
+ cluster = (cpu_logical_map(i) >> 8) & 0xff;
+
+ if (cpumask_test_cpu(cpu, &common_mask)) {
+ /*
+ * We keep only those logical CPUs which number
+ * is equal to their physical CPU number. This is
+ * not perfect but good enough for now.
+ */
+ if (cpu == i)
+ continue;
+ }
+
+ ret = cpu_down(i);
+ if (ret) {
+ bL_switcher_restore_cpus();
+ return ret;
+ }
+ cpumask_set_cpu(i, &bL_switcher_removed_logical_cpus);
+ }
+
+ return 0;
+}
+
static int __init bL_switcher_init(void)
{
- int cpu;
+ int cpu, ret;
pr_info("big.LITTLE switcher initializing\n");
+ if (MAX_NR_CLUSTERS != 2) {
+ pr_err("%s: only dual cluster systems are supported\n", __func__);
+ return -EINVAL;
+ }
+
+ cpu_hotplug_driver_lock();
+ ret = bL_switcher_halve_cpus();
+ if (ret) {
+ cpu_hotplug_driver_unlock();
+ return ret;
+ }
+
for_each_online_cpu(cpu) {
struct bL_thread *t = &bL_threads[cpu];
init_waitqueue_head(&t->wq);
t->wanted_cluster = -1;
t->task = bL_switcher_thread_create(cpu, t);
}
+ cpu_hotplug_driver_unlock();
pr_info("big.LITTLE switcher initialized\n");
return 0;