summaryrefslogtreecommitdiffstats
path: root/arch/arm/common/bL_switcher.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/arm/common/bL_switcher.c')
-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 4caca71c906..50e95d894e3 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;