* [PATCH 0/2] cpufreq support for the big.LITTLE switcher
@ 2013-10-23 3:58 Nicolas Pitre
2013-10-23 3:58 ` [PATCH 1/2] cpufreq: arm_big_little: add in-kernel switching (IKS) support Nicolas Pitre
` (2 more replies)
0 siblings, 3 replies; 6+ messages in thread
From: Nicolas Pitre @ 2013-10-23 3:58 UTC (permalink / raw)
To: linux-arm-kernel
This is the third and final set of patches towards a fully functional
and production quality switcher solution for big.LITTLE systems,
establishing a landmark to compare against for any scheduler based
solution meant to eventually surpass the switcher's power efficiency
available in the mainline kernel.
Rationale for this code: http://lwn.net/Articles/481055/
The first and second patch sets have already been merged by RMK. They
implement the core switcher mechanism. This set adds the necessary code
to drive the switcher based on cpufreq governor decisions.
drivers/cpufreq/arm_big_little.c | 418 ++++++++++++++++++++++++++++++---
drivers/cpufreq/arm_big_little.h | 5 -
2 files changed, 389 insertions(+), 34 deletions(-)
^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH 1/2] cpufreq: arm_big_little: add in-kernel switching (IKS) support
2013-10-23 3:58 [PATCH 0/2] cpufreq support for the big.LITTLE switcher Nicolas Pitre
@ 2013-10-23 3:58 ` Nicolas Pitre
2013-10-23 3:58 ` [PATCH 2/2] cpufreq: arm_big_little: reconfigure switcher behavior at run time Nicolas Pitre
2013-10-30 14:01 ` [PATCH 0/2] cpufreq support for the big.LITTLE switcher Rafael J. Wysocki
2 siblings, 0 replies; 6+ messages in thread
From: Nicolas Pitre @ 2013-10-23 3:58 UTC (permalink / raw)
To: linux-arm-kernel
From: Viresh Kumar <viresh.kumar@linaro.org>
This patch adds IKS (In Kernel Switcher) support to cpufreq driver.
This creates a combined freq table for A7-A15 CPU pairs. A7 frequencies
are virtualized and scaled down to half the actual frequencies to
approximate a linear scale across the combined A7+A15 range. When the
requested frequency change crosses the A7-A15 boundary a cluster switch
is invoked.
Based on Earlier Work from Sudeep.
Signed-off-by: Sudeep KarkadaNagesha <sudeep.karkadanagesha@arm.com>
Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org>
Signed-off-by: Nicolas Pitre <nico@linaro.org>
---
drivers/cpufreq/arm_big_little.c | 366 ++++++++++++++++++++++++++++++++++++---
drivers/cpufreq/arm_big_little.h | 5 -
2 files changed, 339 insertions(+), 32 deletions(-)
diff --git a/drivers/cpufreq/arm_big_little.c b/drivers/cpufreq/arm_big_little.c
index 501a091c80..ba73f868b6 100644
--- a/drivers/cpufreq/arm_big_little.c
+++ b/drivers/cpufreq/arm_big_little.c
@@ -24,27 +24,165 @@
#include <linux/cpufreq.h>
#include <linux/cpumask.h>
#include <linux/export.h>
+#include <linux/mutex.h>
#include <linux/of_platform.h>
#include <linux/opp.h>
#include <linux/slab.h>
#include <linux/topology.h>
#include <linux/types.h>
+#include <asm/bL_switcher.h>
#include "arm_big_little.h"
/* Currently we support only two clusters */
+#define A15_CLUSTER 0
+#define A7_CLUSTER 1
#define MAX_CLUSTERS 2
+#ifdef CONFIG_BL_SWITCHER
+#define is_bL_switching_enabled() true
+#else
+#define is_bL_switching_enabled() false
+#endif
+
+#define ACTUAL_FREQ(cluster, freq) ((cluster == A7_CLUSTER) ? freq << 1 : freq)
+#define VIRT_FREQ(cluster, freq) ((cluster == A7_CLUSTER) ? freq >> 1 : freq)
+
static struct cpufreq_arm_bL_ops *arm_bL_ops;
static struct clk *clk[MAX_CLUSTERS];
-static struct cpufreq_frequency_table *freq_table[MAX_CLUSTERS];
-static atomic_t cluster_usage[MAX_CLUSTERS] = {ATOMIC_INIT(0), ATOMIC_INIT(0)};
+static struct cpufreq_frequency_table *freq_table[MAX_CLUSTERS + 1];
+static atomic_t cluster_usage[MAX_CLUSTERS + 1];
+
+static unsigned int clk_big_min; /* (Big) clock frequencies */
+static unsigned int clk_little_max; /* Maximum clock frequency (Little) */
+
+static DEFINE_PER_CPU(unsigned int, physical_cluster);
+static DEFINE_PER_CPU(unsigned int, cpu_last_req_freq);
+
+static struct mutex cluster_lock[MAX_CLUSTERS];
+
+static inline int raw_cpu_to_cluster(int cpu)
+{
+ return topology_physical_package_id(cpu);
+}
+
+static inline int cpu_to_cluster(int cpu)
+{
+ return is_bL_switching_enabled() ?
+ MAX_CLUSTERS : raw_cpu_to_cluster(cpu);
+}
+
+static unsigned int find_cluster_maxfreq(int cluster)
+{
+ int j;
+ u32 max_freq = 0, cpu_freq;
+
+ for_each_online_cpu(j) {
+ cpu_freq = per_cpu(cpu_last_req_freq, j);
+
+ if ((cluster == per_cpu(physical_cluster, j)) &&
+ (max_freq < cpu_freq))
+ max_freq = cpu_freq;
+ }
+
+ pr_debug("%s: cluster: %d, max freq: %d\n", __func__, cluster,
+ max_freq);
+
+ return max_freq;
+}
+
+static unsigned int clk_get_cpu_rate(unsigned int cpu)
+{
+ u32 cur_cluster = per_cpu(physical_cluster, cpu);
+ u32 rate = clk_get_rate(clk[cur_cluster]) / 1000;
+
+ /* For switcher we use virtual A7 clock rates */
+ if (is_bL_switching_enabled())
+ rate = VIRT_FREQ(cur_cluster, rate);
+
+ pr_debug("%s: cpu: %d, cluster: %d, freq: %u\n", __func__, cpu,
+ cur_cluster, rate);
+
+ return rate;
+}
+
+static unsigned int bL_cpufreq_get_rate(unsigned int cpu)
+{
+ if (is_bL_switching_enabled()) {
+ pr_debug("%s: freq: %d\n", __func__, per_cpu(cpu_last_req_freq,
+ cpu));
+
+ return per_cpu(cpu_last_req_freq, cpu);
+ } else {
+ return clk_get_cpu_rate(cpu);
+ }
+}
-static unsigned int bL_cpufreq_get(unsigned int cpu)
+static unsigned int
+bL_cpufreq_set_rate(u32 cpu, u32 old_cluster, u32 new_cluster, u32 rate)
{
- u32 cur_cluster = cpu_to_cluster(cpu);
+ u32 new_rate, prev_rate;
+ int ret;
+ bool bLs = is_bL_switching_enabled();
+
+ mutex_lock(&cluster_lock[new_cluster]);
+
+ if (bLs) {
+ prev_rate = per_cpu(cpu_last_req_freq, cpu);
+ per_cpu(cpu_last_req_freq, cpu) = rate;
+ per_cpu(physical_cluster, cpu) = new_cluster;
+
+ new_rate = find_cluster_maxfreq(new_cluster);
+ new_rate = ACTUAL_FREQ(new_cluster, new_rate);
+ } else {
+ new_rate = rate;
+ }
+
+ pr_debug("%s: cpu: %d, old cluster: %d, new cluster: %d, freq: %d\n",
+ __func__, cpu, old_cluster, new_cluster, new_rate);
+
+ ret = clk_set_rate(clk[new_cluster], new_rate * 1000);
+ if (WARN_ON(ret)) {
+ pr_err("clk_set_rate failed: %d, new cluster: %d\n", ret,
+ new_cluster);
+ if (bLs) {
+ per_cpu(cpu_last_req_freq, cpu) = prev_rate;
+ per_cpu(physical_cluster, cpu) = old_cluster;
+ }
+
+ mutex_unlock(&cluster_lock[new_cluster]);
+
+ return ret;
+ }
+
+ mutex_unlock(&cluster_lock[new_cluster]);
+
+ /* Recalc freq for old cluster when switching clusters */
+ if (old_cluster != new_cluster) {
+ pr_debug("%s: cpu: %d, old cluster: %d, new cluster: %d\n",
+ __func__, cpu, old_cluster, new_cluster);
+
+ /* Switch cluster */
+ bL_switch_request(cpu, new_cluster);
+
+ mutex_lock(&cluster_lock[old_cluster]);
- return clk_get_rate(clk[cur_cluster]) / 1000;
+ /* Set freq of old cluster if there are cpus left on it */
+ new_rate = find_cluster_maxfreq(old_cluster);
+ new_rate = ACTUAL_FREQ(old_cluster, new_rate);
+
+ if (new_rate) {
+ pr_debug("%s: Updating rate of old cluster: %d, to freq: %d\n",
+ __func__, old_cluster, new_rate);
+
+ if (clk_set_rate(clk[old_cluster], new_rate * 1000))
+ pr_err("%s: clk_set_rate failed: %d, old cluster: %d\n",
+ __func__, ret, old_cluster);
+ }
+ mutex_unlock(&cluster_lock[old_cluster]);
+ }
+
+ return 0;
}
/* Validate policy frequency range */
@@ -60,12 +198,14 @@ static int bL_cpufreq_set_target(struct cpufreq_policy *policy,
unsigned int target_freq, unsigned int relation)
{
struct cpufreq_freqs freqs;
- u32 cpu = policy->cpu, freq_tab_idx, cur_cluster;
+ u32 cpu = policy->cpu, freq_tab_idx, cur_cluster, new_cluster,
+ actual_cluster;
int ret = 0;
- cur_cluster = cpu_to_cluster(policy->cpu);
+ cur_cluster = cpu_to_cluster(cpu);
+ new_cluster = actual_cluster = per_cpu(physical_cluster, cpu);
- freqs.old = bL_cpufreq_get(policy->cpu);
+ freqs.old = bL_cpufreq_get_rate(cpu);
/* Determine valid target frequency using freq_table */
cpufreq_frequency_table_target(policy, freq_table[cur_cluster],
@@ -79,51 +219,151 @@ static int bL_cpufreq_set_target(struct cpufreq_policy *policy,
if (freqs.old == freqs.new)
return 0;
+ if (is_bL_switching_enabled()) {
+ if ((actual_cluster == A15_CLUSTER) &&
+ (freqs.new < clk_big_min)) {
+ new_cluster = A7_CLUSTER;
+ } else if ((actual_cluster == A7_CLUSTER) &&
+ (freqs.new > clk_little_max)) {
+ new_cluster = A15_CLUSTER;
+ }
+ }
+
cpufreq_notify_transition(policy, &freqs, CPUFREQ_PRECHANGE);
- ret = clk_set_rate(clk[cur_cluster], freqs.new * 1000);
- if (ret) {
- pr_err("clk_set_rate failed: %d\n", ret);
+ ret = bL_cpufreq_set_rate(cpu, actual_cluster, new_cluster, freqs.new);
+ if (ret)
freqs.new = freqs.old;
- }
cpufreq_notify_transition(policy, &freqs, CPUFREQ_POSTCHANGE);
return ret;
}
+static inline u32 get_table_count(struct cpufreq_frequency_table *table)
+{
+ int count;
+
+ for (count = 0; table[count].frequency != CPUFREQ_TABLE_END; count++)
+ ;
+
+ return count;
+}
+
+/* get the minimum frequency in the cpufreq_frequency_table */
+static inline u32 get_table_min(struct cpufreq_frequency_table *table)
+{
+ int i;
+ uint32_t min_freq = ~0;
+ for (i = 0; (table[i].frequency != CPUFREQ_TABLE_END); i++)
+ if (table[i].frequency < min_freq)
+ min_freq = table[i].frequency;
+ return min_freq;
+}
+
+/* get the maximum frequency in the cpufreq_frequency_table */
+static inline u32 get_table_max(struct cpufreq_frequency_table *table)
+{
+ int i;
+ uint32_t max_freq = 0;
+ for (i = 0; (table[i].frequency != CPUFREQ_TABLE_END); i++)
+ if (table[i].frequency > max_freq)
+ max_freq = table[i].frequency;
+ return max_freq;
+}
+
+static int merge_cluster_tables(void)
+{
+ int i, j, k = 0, count = 1;
+ struct cpufreq_frequency_table *table;
+
+ for (i = 0; i < MAX_CLUSTERS; i++)
+ count += get_table_count(freq_table[i]);
+
+ table = kzalloc(sizeof(*table) * count, GFP_KERNEL);
+ if (!table)
+ return -ENOMEM;
+
+ freq_table[MAX_CLUSTERS] = table;
+
+ /* Add in reverse order to get freqs in increasing order */
+ for (i = MAX_CLUSTERS - 1; i >= 0; i--) {
+ for (j = 0; freq_table[i][j].frequency != CPUFREQ_TABLE_END;
+ j++) {
+ table[k].frequency = VIRT_FREQ(i,
+ freq_table[i][j].frequency);
+ pr_debug("%s: index: %d, freq: %d\n", __func__, k,
+ table[k].frequency);
+ k++;
+ }
+ }
+
+ table[k].driver_data = k;
+ table[k].frequency = CPUFREQ_TABLE_END;
+
+ pr_debug("%s: End, table: %p, count: %d\n", __func__, table, k);
+
+ return 0;
+}
+
+static void _put_cluster_clk_and_freq_table(struct device *cpu_dev)
+{
+ u32 cluster = raw_cpu_to_cluster(cpu_dev->id);
+
+ if (!freq_table[cluster])
+ return;
+
+ clk_put(clk[cluster]);
+ opp_free_cpufreq_table(cpu_dev, &freq_table[cluster]);
+ dev_dbg(cpu_dev, "%s: cluster: %d\n", __func__, cluster);
+}
+
static void put_cluster_clk_and_freq_table(struct device *cpu_dev)
{
u32 cluster = cpu_to_cluster(cpu_dev->id);
+ int i;
+
+ if (atomic_dec_return(&cluster_usage[cluster]))
+ return;
+
+ if (cluster < MAX_CLUSTERS)
+ return _put_cluster_clk_and_freq_table(cpu_dev);
- if (!atomic_dec_return(&cluster_usage[cluster])) {
- clk_put(clk[cluster]);
- opp_free_cpufreq_table(cpu_dev, &freq_table[cluster]);
- dev_dbg(cpu_dev, "%s: cluster: %d\n", __func__, cluster);
+ for_each_present_cpu(i) {
+ struct device *cdev = get_cpu_device(i);
+ if (!cdev) {
+ pr_err("%s: failed to get cpu%d device\n", __func__, i);
+ return;
+ }
+
+ _put_cluster_clk_and_freq_table(cdev);
}
+
+ /* free virtual table */
+ kfree(freq_table[cluster]);
}
-static int get_cluster_clk_and_freq_table(struct device *cpu_dev)
+static int _get_cluster_clk_and_freq_table(struct device *cpu_dev)
{
- u32 cluster = cpu_to_cluster(cpu_dev->id);
+ u32 cluster = raw_cpu_to_cluster(cpu_dev->id);
char name[14] = "cpu-cluster.";
int ret;
- if (atomic_inc_return(&cluster_usage[cluster]) != 1)
+ if (freq_table[cluster])
return 0;
ret = arm_bL_ops->init_opp_table(cpu_dev);
if (ret) {
dev_err(cpu_dev, "%s: init_opp_table failed, cpu: %d, err: %d\n",
__func__, cpu_dev->id, ret);
- goto atomic_dec;
+ goto out;
}
ret = opp_init_cpufreq_table(cpu_dev, &freq_table[cluster]);
if (ret) {
dev_err(cpu_dev, "%s: failed to init cpufreq table, cpu: %d, err: %d\n",
__func__, cpu_dev->id, ret);
- goto atomic_dec;
+ goto out;
}
name[12] = cluster + '0';
@@ -140,13 +380,72 @@ static int get_cluster_clk_and_freq_table(struct device *cpu_dev)
ret = PTR_ERR(clk[cluster]);
opp_free_cpufreq_table(cpu_dev, &freq_table[cluster]);
-atomic_dec:
- atomic_dec(&cluster_usage[cluster]);
+out:
dev_err(cpu_dev, "%s: Failed to get data for cluster: %d\n", __func__,
cluster);
return ret;
}
+static int get_cluster_clk_and_freq_table(struct device *cpu_dev)
+{
+ u32 cluster = cpu_to_cluster(cpu_dev->id);
+ int i, ret;
+
+ if (atomic_inc_return(&cluster_usage[cluster]) != 1)
+ return 0;
+
+ if (cluster < MAX_CLUSTERS) {
+ ret = _get_cluster_clk_and_freq_table(cpu_dev);
+ if (ret)
+ atomic_dec(&cluster_usage[cluster]);
+ return ret;
+ }
+
+ /*
+ * Get data for all clusters and fill virtual cluster with a merge of
+ * both
+ */
+ for_each_present_cpu(i) {
+ struct device *cdev = get_cpu_device(i);
+ if (!cdev) {
+ pr_err("%s: failed to get cpu%d device\n", __func__, i);
+ return -ENODEV;
+ }
+
+ ret = _get_cluster_clk_and_freq_table(cdev);
+ if (ret)
+ goto put_clusters;
+ }
+
+ ret = merge_cluster_tables();
+ if (ret)
+ goto put_clusters;
+
+ /* Assuming 2 cluster, set clk_big_min and clk_little_max */
+ clk_big_min = get_table_min(freq_table[0]);
+ clk_little_max = VIRT_FREQ(1, get_table_max(freq_table[1]));
+
+ pr_debug("%s: cluster: %d, clk_big_min: %d, clk_little_max: %d\n",
+ __func__, cluster, clk_big_min, clk_little_max);
+
+ return 0;
+
+put_clusters:
+ for_each_present_cpu(i) {
+ struct device *cdev = get_cpu_device(i);
+ if (!cdev) {
+ pr_err("%s: failed to get cpu%d device\n", __func__, i);
+ return -ENODEV;
+ }
+
+ _put_cluster_clk_and_freq_table(cdev);
+ }
+
+ atomic_dec(&cluster_usage[cluster]);
+
+ return ret;
+}
+
/* Per-CPU initialization */
static int bL_cpufreq_init(struct cpufreq_policy *policy)
{
@@ -175,15 +474,25 @@ static int bL_cpufreq_init(struct cpufreq_policy *policy)
cpufreq_frequency_table_get_attr(freq_table[cur_cluster], policy->cpu);
+ if (cur_cluster < MAX_CLUSTERS) {
+ cpumask_copy(policy->cpus, topology_core_cpumask(policy->cpu));
+
+ per_cpu(physical_cluster, policy->cpu) = cur_cluster;
+ } else {
+ /* Assumption: during init, we are always running on A15 */
+ per_cpu(physical_cluster, policy->cpu) = A15_CLUSTER;
+ }
+
if (arm_bL_ops->get_transition_latency)
policy->cpuinfo.transition_latency =
arm_bL_ops->get_transition_latency(cpu_dev);
else
policy->cpuinfo.transition_latency = CPUFREQ_ETERNAL;
- policy->cur = bL_cpufreq_get(policy->cpu);
+ policy->cur = clk_get_cpu_rate(policy->cpu);
- cpumask_copy(policy->cpus, topology_core_cpumask(policy->cpu));
+ if (is_bL_switching_enabled())
+ per_cpu(cpu_last_req_freq, policy->cpu) = policy->cur;
dev_info(cpu_dev, "%s: CPU %d initialized\n", __func__, policy->cpu);
return 0;
@@ -217,7 +526,7 @@ static struct cpufreq_driver bL_cpufreq_driver = {
.flags = CPUFREQ_STICKY,
.verify = bL_cpufreq_verify_policy,
.target = bL_cpufreq_set_target,
- .get = bL_cpufreq_get,
+ .get = bL_cpufreq_get_rate,
.init = bL_cpufreq_init,
.exit = bL_cpufreq_exit,
.have_governor_per_policy = true,
@@ -226,7 +535,7 @@ static struct cpufreq_driver bL_cpufreq_driver = {
int bL_cpufreq_register(struct cpufreq_arm_bL_ops *ops)
{
- int ret;
+ int ret, i;
if (arm_bL_ops) {
pr_debug("%s: Already registered: %s, exiting\n", __func__,
@@ -241,6 +550,9 @@ int bL_cpufreq_register(struct cpufreq_arm_bL_ops *ops)
arm_bL_ops = ops;
+ for (i = 0; i < MAX_CLUSTERS; i++)
+ mutex_init(&cluster_lock[i]);
+
ret = cpufreq_register_driver(&bL_cpufreq_driver);
if (ret) {
pr_info("%s: Failed registering platform driver: %s, err: %d\n",
diff --git a/drivers/cpufreq/arm_big_little.h b/drivers/cpufreq/arm_big_little.h
index 79b2ce1788..70f18fc12d 100644
--- a/drivers/cpufreq/arm_big_little.h
+++ b/drivers/cpufreq/arm_big_little.h
@@ -34,11 +34,6 @@ struct cpufreq_arm_bL_ops {
int (*init_opp_table)(struct device *cpu_dev);
};
-static inline int cpu_to_cluster(int cpu)
-{
- return topology_physical_package_id(cpu);
-}
-
int bL_cpufreq_register(struct cpufreq_arm_bL_ops *ops);
void bL_cpufreq_unregister(struct cpufreq_arm_bL_ops *ops);
--
1.8.4.108.g55ea5f6
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH 2/2] cpufreq: arm_big_little: reconfigure switcher behavior at run time
2013-10-23 3:58 [PATCH 0/2] cpufreq support for the big.LITTLE switcher Nicolas Pitre
2013-10-23 3:58 ` [PATCH 1/2] cpufreq: arm_big_little: add in-kernel switching (IKS) support Nicolas Pitre
@ 2013-10-23 3:58 ` Nicolas Pitre
2013-10-23 4:49 ` Viresh Kumar
2013-10-30 14:01 ` [PATCH 0/2] cpufreq support for the big.LITTLE switcher Rafael J. Wysocki
2 siblings, 1 reply; 6+ messages in thread
From: Nicolas Pitre @ 2013-10-23 3:58 UTC (permalink / raw)
To: linux-arm-kernel
The b.L switcher can be turned on/off at run time. It is therefore
necessary to change the cpufreq driver behavior accordingly.
The driver must be unregistered/registered with the cpufreq core
to reconfigure freq tables for the virtual or actual CPUs. This is
accomplished via the b.L switcher notifier callback.
Signed-off-by: Nicolas Pitre <nicolas.pitre@linaro.org>
---
drivers/cpufreq/arm_big_little.c | 54 +++++++++++++++++++++++++++++++++++++---
1 file changed, 51 insertions(+), 3 deletions(-)
diff --git a/drivers/cpufreq/arm_big_little.c b/drivers/cpufreq/arm_big_little.c
index ba73f868b6..0904f0e0e6 100644
--- a/drivers/cpufreq/arm_big_little.c
+++ b/drivers/cpufreq/arm_big_little.c
@@ -40,9 +40,12 @@
#define MAX_CLUSTERS 2
#ifdef CONFIG_BL_SWITCHER
-#define is_bL_switching_enabled() true
+static bool bL_switching_enabled;
+#define is_bL_switching_enabled() bL_switching_enabled
+#define set_switching_enabled(x) (bL_switching_enabled = (x))
#else
#define is_bL_switching_enabled() false
+#define set_switching_enabled(x) do { } while (0)
#endif
#define ACTUAL_FREQ(cluster, freq) ((cluster == A7_CLUSTER) ? freq << 1 : freq)
@@ -533,6 +536,38 @@ static struct cpufreq_driver bL_cpufreq_driver = {
.attr = bL_cpufreq_attr,
};
+static int bL_cpufreq_switcher_notifier(struct notifier_block *nfb,
+ unsigned long action, void *_arg)
+{
+ pr_debug("%s: action: %ld\n", __func__, action);
+
+ switch (action) {
+ case BL_NOTIFY_PRE_ENABLE:
+ case BL_NOTIFY_PRE_DISABLE:
+ cpufreq_unregister_driver(&bL_cpufreq_driver);
+ break;
+
+ case BL_NOTIFY_POST_ENABLE:
+ set_switching_enabled(true);
+ cpufreq_register_driver(&bL_cpufreq_driver);
+ break;
+
+ case BL_NOTIFY_POST_DISABLE:
+ set_switching_enabled(false);
+ cpufreq_register_driver(&bL_cpufreq_driver);
+ break;
+
+ default:
+ return NOTIFY_DONE;
+ }
+
+ return NOTIFY_OK;
+}
+
+static struct notifier_block bL_switcher_notifier = {
+ .notifier_call = bL_cpufreq_switcher_notifier,
+};
+
int bL_cpufreq_register(struct cpufreq_arm_bL_ops *ops)
{
int ret, i;
@@ -550,6 +585,9 @@ int bL_cpufreq_register(struct cpufreq_arm_bL_ops *ops)
arm_bL_ops = ops;
+ ret = bL_switcher_get_enabled();
+ set_switching_enabled(ret);
+
for (i = 0; i < MAX_CLUSTERS; i++)
mutex_init(&cluster_lock[i]);
@@ -559,10 +597,17 @@ int bL_cpufreq_register(struct cpufreq_arm_bL_ops *ops)
__func__, ops->name, ret);
arm_bL_ops = NULL;
} else {
- pr_info("%s: Registered platform driver: %s\n", __func__,
- ops->name);
+ ret = bL_switcher_register_notifier(&bL_switcher_notifier);
+ if (ret) {
+ cpufreq_unregister_driver(&bL_cpufreq_driver);
+ arm_bL_ops = NULL;
+ } else {
+ pr_info("%s: Registered platform driver: %s\n",
+ __func__, ops->name);
+ }
}
+ bL_switcher_put_enabled();
return ret;
}
EXPORT_SYMBOL_GPL(bL_cpufreq_register);
@@ -575,7 +620,10 @@ void bL_cpufreq_unregister(struct cpufreq_arm_bL_ops *ops)
return;
}
+ bL_switcher_get_enabled();
+ bL_switcher_unregister_notifier(&bL_switcher_notifier);
cpufreq_unregister_driver(&bL_cpufreq_driver);
+ bL_switcher_put_enabled();
pr_info("%s: Un-registered platform driver: %s\n", __func__,
arm_bL_ops->name);
arm_bL_ops = NULL;
--
1.8.4.108.g55ea5f6
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH 2/2] cpufreq: arm_big_little: reconfigure switcher behavior at run time
2013-10-23 3:58 ` [PATCH 2/2] cpufreq: arm_big_little: reconfigure switcher behavior at run time Nicolas Pitre
@ 2013-10-23 4:49 ` Viresh Kumar
0 siblings, 0 replies; 6+ messages in thread
From: Viresh Kumar @ 2013-10-23 4:49 UTC (permalink / raw)
To: linux-arm-kernel
On 23 October 2013 09:28, Nicolas Pitre <nicolas.pitre@linaro.org> wrote:
> The b.L switcher can be turned on/off at run time. It is therefore
> necessary to change the cpufreq driver behavior accordingly.
>
> The driver must be unregistered/registered with the cpufreq core
> to reconfigure freq tables for the virtual or actual CPUs. This is
> accomplished via the b.L switcher notifier callback.
>
> Signed-off-by: Nicolas Pitre <nicolas.pitre@linaro.org>
Got really confused on what should I reply with,
Ack/Tested/Reviewed/Signed-off :)
Because we have worked closely on these patches, maybe
this suits best :)
Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org>
^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH 0/2] cpufreq support for the big.LITTLE switcher
2013-10-23 3:58 [PATCH 0/2] cpufreq support for the big.LITTLE switcher Nicolas Pitre
2013-10-23 3:58 ` [PATCH 1/2] cpufreq: arm_big_little: add in-kernel switching (IKS) support Nicolas Pitre
2013-10-23 3:58 ` [PATCH 2/2] cpufreq: arm_big_little: reconfigure switcher behavior at run time Nicolas Pitre
@ 2013-10-30 14:01 ` Rafael J. Wysocki
2013-10-30 19:47 ` Nicolas Pitre
2 siblings, 1 reply; 6+ messages in thread
From: Rafael J. Wysocki @ 2013-10-30 14:01 UTC (permalink / raw)
To: linux-arm-kernel
On Tuesday, October 22, 2013 11:58:09 PM Nicolas Pitre wrote:
> This is the third and final set of patches towards a fully functional
> and production quality switcher solution for big.LITTLE systems,
> establishing a landmark to compare against for any scheduler based
> solution meant to eventually surpass the switcher's power efficiency
> available in the mainline kernel.
>
> Rationale for this code: http://lwn.net/Articles/481055/
>
> The first and second patch sets have already been merged by RMK. They
> implement the core switcher mechanism. This set adds the necessary code
> to drive the switcher based on cpufreq governor decisions.
Patch [1/2] doesn't apply any more after all of the recent changes from Viresh.
Please rebase on top of linux-pm/linux-next and resend.
Thanks!
--
I speak only for myself.
Rafael J. Wysocki, Intel Open Source Technology Center.
^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH 0/2] cpufreq support for the big.LITTLE switcher
2013-10-30 14:01 ` [PATCH 0/2] cpufreq support for the big.LITTLE switcher Rafael J. Wysocki
@ 2013-10-30 19:47 ` Nicolas Pitre
0 siblings, 0 replies; 6+ messages in thread
From: Nicolas Pitre @ 2013-10-30 19:47 UTC (permalink / raw)
To: linux-arm-kernel
On Wed, 30 Oct 2013, Rafael J. Wysocki wrote:
> On Tuesday, October 22, 2013 11:58:09 PM Nicolas Pitre wrote:
> > This is the third and final set of patches towards a fully functional
> > and production quality switcher solution for big.LITTLE systems,
> > establishing a landmark to compare against for any scheduler based
> > solution meant to eventually surpass the switcher's power efficiency
> > available in the mainline kernel.
> >
> > Rationale for this code: http://lwn.net/Articles/481055/
> >
> > The first and second patch sets have already been merged by RMK. They
> > implement the core switcher mechanism. This set adds the necessary code
> > to drive the switcher based on cpufreq governor decisions.
>
> Patch [1/2] doesn't apply any more after all of the recent changes from Viresh.
>
> Please rebase on top of linux-pm/linux-next and resend.
I just resent those patches after some testing.
Thanks!
Nicolas
^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2013-10-30 19:47 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2013-10-23 3:58 [PATCH 0/2] cpufreq support for the big.LITTLE switcher Nicolas Pitre
2013-10-23 3:58 ` [PATCH 1/2] cpufreq: arm_big_little: add in-kernel switching (IKS) support Nicolas Pitre
2013-10-23 3:58 ` [PATCH 2/2] cpufreq: arm_big_little: reconfigure switcher behavior at run time Nicolas Pitre
2013-10-23 4:49 ` Viresh Kumar
2013-10-30 14:01 ` [PATCH 0/2] cpufreq support for the big.LITTLE switcher Rafael J. Wysocki
2013-10-30 19:47 ` Nicolas Pitre
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).