* [RFC PATCH v3 10/10] arch_topology: Start Energy Aware Scheduling
From: Quentin Perret @ 2018-05-21 14:25 UTC (permalink / raw)
To: peterz, rjw, gregkh, linux-kernel, linux-pm
Cc: mingo, dietmar.eggemann, morten.rasmussen, chris.redpath,
patrick.bellasi, valentin.schneider, vincent.guittot,
thara.gopinath, viresh.kumar, tkjos, joelaf, smuckle, adharmap,
skannan, pkondeti, juri.lelli, edubezval, srinivas.pandruvada,
currojerez, javi.merino, quentin.perret
In-Reply-To: <20180521142505.6522-1-quentin.perret@arm.com>
Energy Aware Scheduling starts when the scheduling domains are built if the
Energy Model is present and all conditions are met. However, in the typical
case of Arm/Arm64 systems, the Energy Model is provided after the scheduling
domains are first built at boot time, which results in EAS staying
disabled.
This commit fixes this issue by re-building the scheduling domain from the
arch topology driver, once CPUfreq is up and running and when the capacity
of the CPUs have been updated to their final value.
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: Quentin Perret <quentin.perret@arm.com>
---
drivers/base/arch_topology.c | 19 +++++++++++++++++++
1 file changed, 19 insertions(+)
diff --git a/drivers/base/arch_topology.c b/drivers/base/arch_topology.c
index e7cb0c6ade81..7f9fa10ef940 100644
--- a/drivers/base/arch_topology.c
+++ b/drivers/base/arch_topology.c
@@ -15,6 +15,8 @@
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/sched/topology.h>
+#include <linux/energy_model.h>
+#include <linux/cpuset.h>
DEFINE_PER_CPU(unsigned long, freq_scale) = SCHED_CAPACITY_SCALE;
@@ -173,6 +175,9 @@ static cpumask_var_t cpus_to_visit;
static void parsing_done_workfn(struct work_struct *work);
static DECLARE_WORK(parsing_done_work, parsing_done_workfn);
+static void start_eas_workfn(struct work_struct *work);
+static DECLARE_WORK(start_eas_work, start_eas_workfn);
+
static int
init_cpu_capacity_callback(struct notifier_block *nb,
unsigned long val,
@@ -204,6 +209,7 @@ init_cpu_capacity_callback(struct notifier_block *nb,
free_raw_capacity();
pr_debug("cpu_capacity: parsing done\n");
schedule_work(&parsing_done_work);
+ schedule_work(&start_eas_work);
}
return 0;
@@ -249,6 +255,19 @@ static void parsing_done_workfn(struct work_struct *work)
free_cpumask_var(cpus_to_visit);
}
+static void start_eas_workfn(struct work_struct *work)
+{
+ /* Make sure the EM knows about the updated CPU capacities. */
+ rcu_read_lock();
+ em_rescale_cpu_capacity();
+ rcu_read_unlock();
+
+ /* Inform the scheduler about the EM availability. */
+ cpus_read_lock();
+ rebuild_sched_domains();
+ cpus_read_unlock();
+}
+
#else
core_initcall(free_raw_capacity);
#endif
--
2.17.0
^ permalink raw reply related
* [RFC PATCH v3 09/10] sched/fair: Select an energy-efficient CPU on task wake-up
From: Quentin Perret @ 2018-05-21 14:25 UTC (permalink / raw)
To: peterz, rjw, gregkh, linux-kernel, linux-pm
Cc: mingo, dietmar.eggemann, morten.rasmussen, chris.redpath,
patrick.bellasi, valentin.schneider, vincent.guittot,
thara.gopinath, viresh.kumar, tkjos, joelaf, smuckle, adharmap,
skannan, pkondeti, juri.lelli, edubezval, srinivas.pandruvada,
currojerez, javi.merino, quentin.perret
In-Reply-To: <20180521142505.6522-1-quentin.perret@arm.com>
If an energy model is available, and if the system isn't overutilized,
waking tasks are re-routed into a new energy-aware placement algorithm.
The selection of an energy-efficient CPU for a task is achieved by
estimating the impact on system-level active energy resulting from the
placement of the task on the CPU with the highest spare capacity in each
frequency domain. This strategy spreads tasks in a frequency domain and
avoids overly aggressive task packing. The best CPU energy-wise is then
selected if it saves a large enough amount of energy with respect to
prev_cpu.
Although it has already shown significant benefits on some existing
targets, this approach cannot scale to platforms with numerous CPUs.
This patch is an attempt to do something useful as writing a fast
heuristic that performs reasonably well on a broad spectrum of
architectures isn't an easy task. As such, the scope of usability of the
energy-aware wake-up path is restricted to systems with the
SD_ASYM_CPUCAPACITY flag set, and where the EM isn't too complex.
In addition, the energy-aware wake-up path is accessible only if
sched_energy_enabled() is true. For systems which don't meet all
dependencies for EAS (CONFIG_ENERGY_MODEL for ex.) at compile time,
sched_enegy_enabled() defaults to a constant "false" value, hence
letting the compiler remove the unused EAS code entirely.
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Signed-off-by: Quentin Perret <quentin.perret@arm.com>
---
kernel/sched/fair.c | 84 ++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 83 insertions(+), 1 deletion(-)
diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c
index 1f7029258df2..eb44829be17f 100644
--- a/kernel/sched/fair.c
+++ b/kernel/sched/fair.c
@@ -6683,6 +6683,80 @@ static long compute_energy(struct task_struct *p, int dst_cpu)
return energy;
}
+static int find_energy_efficient_cpu(struct task_struct *p, int prev_cpu)
+{
+ unsigned long cur_energy, prev_energy, best_energy, cpu_cap, task_util;
+ int cpu, best_energy_cpu = prev_cpu;
+ struct sched_energy_fd *sfd;
+ struct sched_domain *sd;
+
+ sync_entity_load_avg(&p->se);
+
+ task_util = task_util_est(p);
+ if (!task_util)
+ return prev_cpu;
+
+ /*
+ * Energy-aware wake-up happens on the lowest sched_domain starting
+ * from sd_ea spanning over this_cpu and prev_cpu.
+ */
+ sd = rcu_dereference(*this_cpu_ptr(&sd_ea));
+ while (sd && !cpumask_test_cpu(prev_cpu, sched_domain_span(sd)))
+ sd = sd->parent;
+ if (!sd)
+ return -1;
+
+ if (cpumask_test_cpu(prev_cpu, &p->cpus_allowed))
+ prev_energy = best_energy = compute_energy(p, prev_cpu);
+ else
+ prev_energy = best_energy = ULONG_MAX;
+
+ for_each_freq_domain(sfd) {
+ unsigned long spare_cap, max_spare_cap = 0;
+ int max_spare_cap_cpu = -1;
+ unsigned long util;
+
+ /* Find the CPU with the max spare cap in the freq. dom. */
+ for_each_cpu_and(cpu, freq_domain_span(sfd), sched_domain_span(sd)) {
+ if (!cpumask_test_cpu(cpu, &p->cpus_allowed))
+ continue;
+
+ if (cpu == prev_cpu)
+ continue;
+
+ /* Skip CPUs that will be overutilized */
+ util = cpu_util_wake(cpu, p) + task_util;
+ cpu_cap = capacity_of(cpu);
+ if (cpu_cap * 1024 < util * capacity_margin)
+ continue;
+
+ spare_cap = cpu_cap - util;
+ if (spare_cap > max_spare_cap) {
+ max_spare_cap = spare_cap;
+ max_spare_cap_cpu = cpu;
+ }
+ }
+
+ /* Evaluate the energy impact of using this CPU. */
+ if (max_spare_cap_cpu >= 0) {
+ cur_energy = compute_energy(p, max_spare_cap_cpu);
+ if (cur_energy < best_energy) {
+ best_energy = cur_energy;
+ best_energy_cpu = max_spare_cap_cpu;
+ }
+ }
+ }
+
+ /*
+ * We pick the best CPU only if it saves at least 1.5% of the
+ * energy used by prev_cpu.
+ */
+ if ((prev_energy - best_energy) > (prev_energy >> 6))
+ return best_energy_cpu;
+
+ return prev_cpu;
+}
+
/*
* select_task_rq_fair: Select target runqueue for the waking task in domains
* that have the 'sd_flag' flag set. In practice, this is SD_BALANCE_WAKE,
@@ -6701,16 +6775,23 @@ select_task_rq_fair(struct task_struct *p, int prev_cpu, int sd_flag, int wake_f
struct sched_domain *tmp, *sd = NULL;
int cpu = smp_processor_id();
int new_cpu = prev_cpu;
- int want_affine = 0;
+ int want_affine = 0, want_energy = 0;
int sync = (wake_flags & WF_SYNC) && !(current->flags & PF_EXITING);
if (sd_flag & SD_BALANCE_WAKE) {
record_wakee(p);
+ want_energy = sched_energy_enabled() &&
+ !READ_ONCE(cpu_rq(cpu)->rd->overutilized);
want_affine = !wake_wide(p) && !wake_cap(p, cpu, prev_cpu)
&& cpumask_test_cpu(cpu, &p->cpus_allowed);
}
rcu_read_lock();
+ if (want_energy) {
+ new_cpu = find_energy_efficient_cpu(p, prev_cpu);
+ goto unlock;
+ }
+
for_each_domain(cpu, tmp) {
if (!(tmp->flags & SD_LOAD_BALANCE))
break;
@@ -6745,6 +6826,7 @@ select_task_rq_fair(struct task_struct *p, int prev_cpu, int sd_flag, int wake_f
if (want_affine)
current->recent_used_cpu = cpu;
}
+unlock:
rcu_read_unlock();
return new_cpu;
--
2.17.0
^ permalink raw reply related
* [RFC PATCH v3 08/10] sched: Lowest energy aware balancing sched_domain level pointer
From: Quentin Perret @ 2018-05-21 14:25 UTC (permalink / raw)
To: peterz, rjw, gregkh, linux-kernel, linux-pm
Cc: mingo, dietmar.eggemann, morten.rasmussen, chris.redpath,
patrick.bellasi, valentin.schneider, vincent.guittot,
thara.gopinath, viresh.kumar, tkjos, joelaf, smuckle, adharmap,
skannan, pkondeti, juri.lelli, edubezval, srinivas.pandruvada,
currojerez, javi.merino, quentin.perret
In-Reply-To: <20180521142505.6522-1-quentin.perret@arm.com>
Add another member to the family of per-cpu sched_domain shortcut
pointers. This one, sd_ea, points to the lowest level at which energy
aware scheduling should be used.
Generally speaking, the largest opportunity to save energy via scheduling
comes from a smarter exploitation of heterogeneous platforms (i.e.
big.LITTLE). Consequently, the sd_ea shortcut is wired to the lowest
scheduling domain at which the SD_ASYM_CPUCAPACITY flag is set. For
example, it is possible to apply energy-aware scheduling within a socket
on a multi-socket system, as long as each socket has an asymmetric
topology. Cross-sockets wake-up balancing will only happen when the
system is over-utilized, or this_cpu and prev_cpu are in different
sockets.
cc: Ingo Molnar <mingo@redhat.com>
cc: Peter Zijlstra <peterz@infradead.org>
Suggested-by: Morten Rasmussen <morten.rasmussen@arm.com>
Signed-off-by: Quentin Perret <quentin.perret@arm.com>
---
kernel/sched/sched.h | 1 +
kernel/sched/topology.c | 4 ++++
2 files changed, 5 insertions(+)
diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h
index 0dd895554f78..31535198f545 100644
--- a/kernel/sched/sched.h
+++ b/kernel/sched/sched.h
@@ -1151,6 +1151,7 @@ DECLARE_PER_CPU(int, sd_llc_id);
DECLARE_PER_CPU(struct sched_domain_shared *, sd_llc_shared);
DECLARE_PER_CPU(struct sched_domain *, sd_numa);
DECLARE_PER_CPU(struct sched_domain *, sd_asym);
+DECLARE_PER_CPU(struct sched_domain *, sd_ea);
struct sched_group_capacity {
atomic_t ref;
diff --git a/kernel/sched/topology.c b/kernel/sched/topology.c
index 3e22c798f18d..8e7ee37771bb 100644
--- a/kernel/sched/topology.c
+++ b/kernel/sched/topology.c
@@ -398,6 +398,7 @@ DEFINE_PER_CPU(int, sd_llc_id);
DEFINE_PER_CPU(struct sched_domain_shared *, sd_llc_shared);
DEFINE_PER_CPU(struct sched_domain *, sd_numa);
DEFINE_PER_CPU(struct sched_domain *, sd_asym);
+DEFINE_PER_CPU(struct sched_domain *, sd_ea);
static void update_top_cache_domain(int cpu)
{
@@ -423,6 +424,9 @@ static void update_top_cache_domain(int cpu)
sd = highest_flag_domain(cpu, SD_ASYM_PACKING);
rcu_assign_pointer(per_cpu(sd_asym, cpu), sd);
+
+ sd = lowest_flag_domain(cpu, SD_ASYM_CPUCAPACITY);
+ rcu_assign_pointer(per_cpu(sd_ea, cpu), sd);
}
/*
--
2.17.0
^ permalink raw reply related
* [RFC PATCH v3 07/10] sched/fair: Introduce an energy estimation helper function
From: Quentin Perret @ 2018-05-21 14:25 UTC (permalink / raw)
To: peterz, rjw, gregkh, linux-kernel, linux-pm
Cc: mingo, dietmar.eggemann, morten.rasmussen, chris.redpath,
patrick.bellasi, valentin.schneider, vincent.guittot,
thara.gopinath, viresh.kumar, tkjos, joelaf, smuckle, adharmap,
skannan, pkondeti, juri.lelli, edubezval, srinivas.pandruvada,
currojerez, javi.merino, quentin.perret
In-Reply-To: <20180521142505.6522-1-quentin.perret@arm.com>
In preparation for the definition of an energy-aware wakeup path, a
helper function is provided to estimate the consequence on system energy
when a specific task wakes-up on a specific CPU. compute_energy()
estimates the capacity state to be reached by all frequency domains and
estimates the consumption of each online CPU according to its Energy Model
and its percentage of busy time.
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Signed-off-by: Quentin Perret <quentin.perret@arm.com>
---
kernel/sched/fair.c | 55 ++++++++++++++++++++++++++++++++++++++++++++
kernel/sched/sched.h | 2 +-
2 files changed, 56 insertions(+), 1 deletion(-)
diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c
index ec797d7ede83..1f7029258df2 100644
--- a/kernel/sched/fair.c
+++ b/kernel/sched/fair.c
@@ -6628,6 +6628,61 @@ static int wake_cap(struct task_struct *p, int cpu, int prev_cpu)
return min_cap * 1024 < task_util(p) * capacity_margin;
}
+/*
+ * Returns the util of "cpu" if "p" wakes up on "dst_cpu".
+ */
+static unsigned long cpu_util_next(int cpu, struct task_struct *p, int dst_cpu)
+{
+ unsigned long util, util_est;
+ struct cfs_rq *cfs_rq;
+
+ /* Task is where it should be, or has no impact on cpu */
+ if ((task_cpu(p) == dst_cpu) || (cpu != task_cpu(p) && cpu != dst_cpu))
+ return cpu_util(cpu);
+
+ cfs_rq = &cpu_rq(cpu)->cfs;
+ util = READ_ONCE(cfs_rq->avg.util_avg);
+
+ if (dst_cpu == cpu)
+ util += task_util(p);
+ else
+ util = max_t(long, util - task_util(p), 0);
+
+ if (sched_feat(UTIL_EST)) {
+ util_est = READ_ONCE(cfs_rq->avg.util_est.enqueued);
+ if (dst_cpu == cpu)
+ util_est += _task_util_est(p);
+ else
+ util_est = max_t(long, util_est - _task_util_est(p), 0);
+ util = max(util, util_est);
+ }
+
+ return min_t(unsigned long, util, capacity_orig_of(cpu));
+}
+
+static long compute_energy(struct task_struct *p, int dst_cpu)
+{
+ long util, max_util, sum_util, energy = 0;
+ struct sched_energy_fd *sfd;
+ int cpu;
+
+ for_each_freq_domain(sfd) {
+ max_util = sum_util = 0;
+ for_each_cpu_and(cpu, freq_domain_span(sfd), cpu_online_mask) {
+ util = cpu_util_next(cpu, p, dst_cpu);
+ util += cpu_util_dl(cpu_rq(cpu));
+ /* XXX: add RT util_avg when available. */
+
+ max_util = max(util, max_util);
+ sum_util += util;
+ }
+
+ energy += em_fd_energy(sfd->fd, max_util, sum_util);
+ }
+
+ return energy;
+}
+
/*
* select_task_rq_fair: Select target runqueue for the waking task in domains
* that have the 'sd_flag' flag set. In practice, this is SD_BALANCE_WAKE,
diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h
index ef5d4ebc205e..0dd895554f78 100644
--- a/kernel/sched/sched.h
+++ b/kernel/sched/sched.h
@@ -2148,7 +2148,7 @@ static inline void cpufreq_update_util(struct rq *rq, unsigned int flags) {}
# define arch_scale_freq_invariant() false
#endif
-#ifdef CONFIG_CPU_FREQ_GOV_SCHEDUTIL
+#ifdef CONFIG_SMP
static inline unsigned long cpu_util_dl(struct rq *rq)
{
return (rq->dl.running_bw * SCHED_CAPACITY_SCALE) >> BW_SHIFT;
--
2.17.0
^ permalink raw reply related
* [RFC PATCH v3 06/10] sched: Add over-utilization/tipping point indicator
From: Quentin Perret @ 2018-05-21 14:25 UTC (permalink / raw)
To: peterz, rjw, gregkh, linux-kernel, linux-pm
Cc: mingo, dietmar.eggemann, morten.rasmussen, chris.redpath,
patrick.bellasi, valentin.schneider, vincent.guittot,
thara.gopinath, viresh.kumar, tkjos, joelaf, smuckle, adharmap,
skannan, pkondeti, juri.lelli, edubezval, srinivas.pandruvada,
currojerez, javi.merino, quentin.perret
In-Reply-To: <20180521142505.6522-1-quentin.perret@arm.com>
From: Morten Rasmussen <morten.rasmussen@arm.com>
Energy-aware scheduling is only meant to be active while the system is
_not_ over-utilized. That is, there are spare cycles available to shift
tasks around based on their actual utilization to get a more
energy-efficient task distribution without depriving any tasks. When
above the tipping point task placement is done the traditional way based
on load_avg, spreading the tasks across as many cpus as possible based
on priority scaled load to preserve smp_nice. Below the tipping point we
want to use util_avg instead. We need to define a criteria for when we
make the switch.
The util_avg for each cpu converges towards 100% (1024) regardless of
how many task additional task we may put on it. If we define
over-utilized as:
sum_{cpus}(rq.cfs.avg.util_avg) + margin > sum_{cpus}(rq.capacity)
some individual cpus may be over-utilized running multiple tasks even
when the above condition is false. That should be okay as long as we try
to spread the tasks out to avoid per-cpu over-utilization as much as
possible and if all tasks have the _same_ priority. If the latter isn't
true, we have to consider priority to preserve smp_nice.
For example, we could have n_cpus nice=-10 util_avg=55% tasks and
n_cpus/2 nice=0 util_avg=60% tasks. Balancing based on util_avg we are
likely to end up with nice=-10 tasks sharing cpus and nice=0 tasks
getting their own as we 1.5*n_cpus tasks in total and 55%+55% is less
over-utilized than 55%+60% for those cpus that have to be shared. The
system utilization is only 85% of the system capacity, but we are
breaking smp_nice.
To be sure not to break smp_nice, we have defined over-utilization
conservatively as when any cpu in the system is fully utilized at it's
highest frequency instead:
cpu_rq(any).cfs.avg.util_avg + margin > cpu_rq(any).capacity
IOW, as soon as one cpu is (nearly) 100% utilized, we switch to load_avg
to factor in priority to preserve smp_nice.
With this definition, we can skip periodic load-balance as no cpu has an
always-running task when the system is not over-utilized. All tasks will
be periodic and we can balance them at wake-up. This conservative
condition does however mean that some scenarios that could benefit from
energy-aware decisions even if one cpu is fully utilized would not get
those benefits.
For system where some cpus might have reduced capacity on some cpus
(RT-pressure and/or big.LITTLE), we want periodic load-balance checks as
soon a just a single cpu is fully utilized as it might one of those with
reduced capacity and in that case we want to migrate it.
cc: Ingo Molnar <mingo@redhat.com>
cc: Peter Zijlstra <peterz@infradead.org>
Signed-off-by: Morten Rasmussen <morten.rasmussen@arm.com>
Signed-off-by: Quentin Perret <quentin.perret@arm.com>
---
kernel/sched/fair.c | 47 +++++++++++++++++++++++++++++++++++++++++---
kernel/sched/sched.h | 3 +++
2 files changed, 47 insertions(+), 3 deletions(-)
diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c
index 1f6a23a5b451..ec797d7ede83 100644
--- a/kernel/sched/fair.c
+++ b/kernel/sched/fair.c
@@ -5345,6 +5345,24 @@ static inline void hrtick_update(struct rq *rq)
}
#endif
+#ifdef CONFIG_SMP
+static inline unsigned long cpu_util(int cpu);
+static unsigned long capacity_of(int cpu);
+
+static inline bool cpu_overutilized(int cpu)
+{
+ return (capacity_of(cpu) * 1024) < (cpu_util(cpu) * capacity_margin);
+}
+
+static inline void update_overutilized_status(struct rq *rq)
+{
+ if (!READ_ONCE(rq->rd->overutilized) && cpu_overutilized(rq->cpu))
+ WRITE_ONCE(rq->rd->overutilized, 1);
+}
+#else
+static inline void update_overutilized_status(struct rq *rq) { }
+#endif
+
/*
* The enqueue_task method is called before nr_running is
* increased. Here we update the fair scheduling stats and
@@ -5355,6 +5373,7 @@ enqueue_task_fair(struct rq *rq, struct task_struct *p, int flags)
{
struct cfs_rq *cfs_rq;
struct sched_entity *se = &p->se;
+ int task_new = !(flags & ENQUEUE_WAKEUP);
/*
* If in_iowait is set, the code below may not trigger any cpufreq
@@ -5394,8 +5413,12 @@ enqueue_task_fair(struct rq *rq, struct task_struct *p, int flags)
update_cfs_group(se);
}
- if (!se)
+ if (!se) {
add_nr_running(rq, 1);
+ if (!task_new)
+ update_overutilized_status(rq);
+
+ }
util_est_enqueue(&rq->cfs, p);
hrtick_update(rq);
@@ -8121,11 +8144,12 @@ static bool update_nohz_stats(struct rq *rq, bool force)
* @local_group: Does group contain this_cpu.
* @sgs: variable to hold the statistics for this group.
* @overload: Indicate more than one runnable task for any CPU.
+ * @overutilized: Indicate overutilization for any CPU.
*/
static inline void update_sg_lb_stats(struct lb_env *env,
struct sched_group *group, int load_idx,
int local_group, struct sg_lb_stats *sgs,
- bool *overload)
+ bool *overload, int *overutilized)
{
unsigned long load;
int i, nr_running;
@@ -8152,6 +8176,9 @@ static inline void update_sg_lb_stats(struct lb_env *env,
if (nr_running > 1)
*overload = true;
+ if (cpu_overutilized(i))
+ *overutilized = 1;
+
#ifdef CONFIG_NUMA_BALANCING
sgs->nr_numa_running += rq->nr_numa_running;
sgs->nr_preferred_running += rq->nr_preferred_running;
@@ -8289,6 +8316,7 @@ static inline void update_sd_lb_stats(struct lb_env *env, struct sd_lb_stats *sd
struct sg_lb_stats tmp_sgs;
int load_idx, prefer_sibling = 0;
bool overload = false;
+ int overutilized = 0;
if (child && child->flags & SD_PREFER_SIBLING)
prefer_sibling = 1;
@@ -8315,7 +8343,7 @@ static inline void update_sd_lb_stats(struct lb_env *env, struct sd_lb_stats *sd
}
update_sg_lb_stats(env, sg, load_idx, local_group, sgs,
- &overload);
+ &overload, &overutilized);
if (local_group)
goto next_group;
@@ -8367,6 +8395,13 @@ static inline void update_sd_lb_stats(struct lb_env *env, struct sd_lb_stats *sd
/* update overload indicator if we are at root domain */
if (env->dst_rq->rd->overload != overload)
env->dst_rq->rd->overload = overload;
+
+ /* Update over-utilization (tipping point, U >= 0) indicator */
+ if (READ_ONCE(env->dst_rq->rd->overutilized) != overutilized)
+ WRITE_ONCE(env->dst_rq->rd->overutilized, overutilized);
+ } else {
+ if (!READ_ONCE(env->dst_rq->rd->overutilized) && overutilized)
+ WRITE_ONCE(env->dst_rq->rd->overutilized, 1);
}
}
@@ -8586,6 +8621,10 @@ static struct sched_group *find_busiest_group(struct lb_env *env)
* this level.
*/
update_sd_lb_stats(env, &sds);
+
+ if (sched_energy_enabled() && !READ_ONCE(env->dst_rq->rd->overutilized))
+ goto out_balanced;
+
local = &sds.local_stat;
busiest = &sds.busiest_stat;
@@ -9943,6 +9982,8 @@ static void task_tick_fair(struct rq *rq, struct task_struct *curr, int queued)
if (static_branch_unlikely(&sched_numa_balancing))
task_tick_numa(rq, curr);
+
+ update_overutilized_status(task_rq(curr));
}
/*
diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h
index 7c517076a74a..ef5d4ebc205e 100644
--- a/kernel/sched/sched.h
+++ b/kernel/sched/sched.h
@@ -692,6 +692,9 @@ struct root_domain {
/* Indicate more than one runnable task for any CPU */
bool overload;
+ /* Indicate one or more cpus over-utilized (tipping point) */
+ int overutilized;
+
/*
* The bit corresponding to a CPU gets set here if such CPU has more
* than one runnable -deadline task (as it is below for RT tasks).
--
2.17.0
^ permalink raw reply related
* [RFC PATCH v3 05/10] sched/topology: Reference the Energy Model of CPUs when available
From: Quentin Perret @ 2018-05-21 14:25 UTC (permalink / raw)
To: peterz, rjw, gregkh, linux-kernel, linux-pm
Cc: mingo, dietmar.eggemann, morten.rasmussen, chris.redpath,
patrick.bellasi, valentin.schneider, vincent.guittot,
thara.gopinath, viresh.kumar, tkjos, joelaf, smuckle, adharmap,
skannan, pkondeti, juri.lelli, edubezval, srinivas.pandruvada,
currojerez, javi.merino, quentin.perret
In-Reply-To: <20180521142505.6522-1-quentin.perret@arm.com>
In order to use EAS, the task scheduler has to know about the Energy
Model (EM) of the platform. This commit extends the scheduler topology
code to take references on the frequency domains objects of the EM
framework for all online CPUs. Hence, the availability of the EM for
those CPUs is guaranteed to the scheduler at runtime without further
checks in latency sensitive code paths (i.e. task wake-up).
A (RCU-protected) private list of online frequency domains is maintained
by the scheduler to enable fast iterations. Furthermore, the availability
of an EM is notified to the rest of the scheduler with a static key,
which ensures a low impact on non-EAS systems.
Energy Aware Scheduling can be started if and only if:
1. all online CPUs are covered by the EM;
2. the EM complexity is low enough to keep scheduling overheads low;
3. the platform has an asymmetric CPU capacity topology (detected by
looking for the SD_ASYM_CPUCAPACITY flag in the sched_domain
hierarchy).
The sched_energy_enabled() function which returns the status of the
static key is stubbed to false when CONFIG_ENERGY_MODEL=n, hence making
sure that all the code behind it can be compiled out by constant
propagation.
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Signed-off-by: Quentin Perret <quentin.perret@arm.com>
---
kernel/sched/sched.h | 27 ++++++++++
kernel/sched/topology.c | 113 ++++++++++++++++++++++++++++++++++++++++
2 files changed, 140 insertions(+)
diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h
index ce562d3b7526..7c517076a74a 100644
--- a/kernel/sched/sched.h
+++ b/kernel/sched/sched.h
@@ -63,6 +63,7 @@
#include <linux/syscalls.h>
#include <linux/task_work.h>
#include <linux/tsacct_kern.h>
+#include <linux/energy_model.h>
#include <asm/tlb.h>
@@ -2162,3 +2163,29 @@ static inline unsigned long cpu_util_cfs(struct rq *rq)
return util;
}
#endif
+
+struct sched_energy_fd {
+ struct em_freq_domain *fd;
+ struct list_head next;
+ struct rcu_head rcu;
+};
+
+#ifdef CONFIG_ENERGY_MODEL
+extern struct static_key_false sched_energy_present;
+static inline bool sched_energy_enabled(void)
+{
+ return static_branch_unlikely(&sched_energy_present);
+}
+
+extern struct list_head sched_energy_fd_list;
+#define for_each_freq_domain(sfd) \
+ list_for_each_entry_rcu(sfd, &sched_energy_fd_list, next)
+#define freq_domain_span(sfd) (&((sfd)->fd->cpus))
+#else
+static inline bool sched_energy_enabled(void)
+{
+ return false;
+}
+#define for_each_freq_domain(sfd) for (sfd = NULL; sfd;)
+#define freq_domain_span(sfd) NULL
+#endif
diff --git a/kernel/sched/topology.c b/kernel/sched/topology.c
index 64cc564f5255..3e22c798f18d 100644
--- a/kernel/sched/topology.c
+++ b/kernel/sched/topology.c
@@ -1500,6 +1500,116 @@ void sched_domains_numa_masks_clear(unsigned int cpu)
#endif /* CONFIG_NUMA */
+#ifdef CONFIG_ENERGY_MODEL
+
+/*
+ * The complexity of the Energy Model is defined as the product of the number
+ * of frequency domains with the sum of the number of CPUs and the total
+ * number of OPPs in all frequency domains. It is generally not a good idea
+ * to use such a model on very complex platform because of the associated
+ * scheduling overheads. The arbitrary constraint below prevents that. It
+ * makes EAS usable up to 16 CPUs with per-CPU DVFS and less than 8 OPPs each,
+ * for example.
+ */
+#define EM_MAX_COMPLEXITY 2048
+
+DEFINE_STATIC_KEY_FALSE(sched_energy_present);
+LIST_HEAD(sched_energy_fd_list);
+
+static struct sched_energy_fd *find_sched_energy_fd(int cpu)
+{
+ struct sched_energy_fd *sfd;
+
+ for_each_freq_domain(sfd) {
+ if (cpumask_test_cpu(cpu, freq_domain_span(sfd)))
+ return sfd;
+ }
+
+ return NULL;
+}
+
+static void free_sched_energy_fd(struct rcu_head *rp)
+{
+ struct sched_energy_fd *sfd;
+
+ sfd = container_of(rp, struct sched_energy_fd, rcu);
+ kfree(sfd);
+}
+
+static void build_sched_energy(void)
+{
+ struct sched_energy_fd *sfd, *tmp;
+ struct em_freq_domain *fd;
+ struct sched_domain *sd;
+ int cpu, nr_fd = 0, nr_opp = 0;
+
+ rcu_read_lock();
+
+ /* Disable EAS entirely whenever the system isn't asymmetric. */
+ cpu = cpumask_first(cpu_online_mask);
+ sd = lowest_flag_domain(cpu, SD_ASYM_CPUCAPACITY);
+ if (!sd) {
+ pr_debug("%s: no SD_ASYM_CPUCAPACITY\n", __func__);
+ goto disable;
+ }
+
+ /* Make sure to have an energy model for all CPUs. */
+ for_each_online_cpu(cpu) {
+ /* Skip CPUs with a known energy model. */
+ sfd = find_sched_energy_fd(cpu);
+ if (sfd)
+ continue;
+
+ /* Add the energy model of others. */
+ fd = em_cpu_get(cpu);
+ if (!fd)
+ goto disable;
+ sfd = kzalloc(sizeof(*sfd), GFP_NOWAIT);
+ if (!sfd)
+ goto disable;
+ sfd->fd = fd;
+ list_add_rcu(&sfd->next, &sched_energy_fd_list);
+ }
+
+ list_for_each_entry_safe(sfd, tmp, &sched_energy_fd_list, next) {
+ if (cpumask_intersects(freq_domain_span(sfd),
+ cpu_online_mask)) {
+ nr_opp += em_fd_nr_cap_states(sfd->fd);
+ nr_fd++;
+ continue;
+ }
+
+ /* Remove the unused frequency domains */
+ list_del_rcu(&sfd->next);
+ call_rcu(&sfd->rcu, free_sched_energy_fd);
+ }
+
+ /* Bail out if the Energy Model complexity is too high. */
+ if (nr_fd * (nr_opp + num_online_cpus()) > EM_MAX_COMPLEXITY) {
+ pr_warn("%s: EM complexity too high, stopping EAS", __func__);
+ goto disable;
+ }
+
+ rcu_read_unlock();
+ static_branch_enable_cpuslocked(&sched_energy_present);
+ pr_debug("%s: EAS started\n", __func__);
+ return;
+
+disable:
+ rcu_read_unlock();
+ static_branch_disable_cpuslocked(&sched_energy_present);
+
+ /* Destroy the list */
+ list_for_each_entry_safe(sfd, tmp, &sched_energy_fd_list, next) {
+ list_del_rcu(&sfd->next);
+ call_rcu(&sfd->rcu, free_sched_energy_fd);
+ }
+ pr_debug("%s: EAS stopped\n", __func__);
+}
+#else
+static void build_sched_energy(void) { }
+#endif
+
static int __sdt_alloc(const struct cpumask *cpu_map)
{
struct sched_domain_topology_level *tl;
@@ -1913,6 +2023,9 @@ void partition_sched_domains(int ndoms_new, cpumask_var_t doms_new[],
;
}
+ /* Try to start sched energy. */
+ build_sched_energy();
+
/* Remember the new sched domains: */
if (doms_cur != &fallback_doms)
free_sched_domains(doms_cur, ndoms_cur);
--
2.17.0
^ permalink raw reply related
* [RFC PATCH v3 04/10] PM / EM: Expose the Energy Model in sysfs
From: Quentin Perret @ 2018-05-21 14:24 UTC (permalink / raw)
To: peterz, rjw, gregkh, linux-kernel, linux-pm
Cc: mingo, dietmar.eggemann, morten.rasmussen, chris.redpath,
patrick.bellasi, valentin.schneider, vincent.guittot,
thara.gopinath, viresh.kumar, tkjos, joelaf, smuckle, adharmap,
skannan, pkondeti, juri.lelli, edubezval, srinivas.pandruvada,
currojerez, javi.merino, quentin.perret
In-Reply-To: <20180521142505.6522-1-quentin.perret@arm.com>
This exposes the Energy Model (read-only) of all frequency domains in
sysfs for convenience. To do so, a parent kobject is added to the CPU
subsystem under the umbrella of which a kobject for each frequency
domain is attached.
The resulting hierarchy is as follows for a platform with two frequency
domains for example:
/sys/devices/system/cpu/energy_model
├── fd0
│ ├── capacity
│ ├── cpus
│ ├── frequency
│ └── power
└── fd4
├── capacity
├── cpus
├── frequency
└── power
In this implementation, the kobject abstraction is only used as a
convenient way of exposing data to sysfs. However, it could also be
used in the future to allocate and release frequency domains in a more
dynamic way using reference counting.
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: "Rafael J. Wysocki" <rjw@rjwysocki.net>
Signed-off-by: Quentin Perret <quentin.perret@arm.com>
---
include/linux/energy_model.h | 1 +
kernel/power/energy_model.c | 94 ++++++++++++++++++++++++++++++++++++
2 files changed, 95 insertions(+)
diff --git a/include/linux/energy_model.h b/include/linux/energy_model.h
index edde888852ba..ed79903a9721 100644
--- a/include/linux/energy_model.h
+++ b/include/linux/energy_model.h
@@ -24,6 +24,7 @@ struct em_cs_table {
struct em_freq_domain {
struct em_cs_table *cs_table;
cpumask_t cpus;
+ struct kobject kobj;
};
struct em_data_callback {
diff --git a/kernel/power/energy_model.c b/kernel/power/energy_model.c
index a2eece7007a8..6ad53f1cf7e6 100644
--- a/kernel/power/energy_model.c
+++ b/kernel/power/energy_model.c
@@ -29,6 +29,86 @@ static DEFINE_RWLOCK(em_data_lock);
*/
static DEFINE_MUTEX(em_fd_mutex);
+static struct kobject *em_kobject;
+
+/* Getters for the attributes of em_freq_domain objects */
+struct em_fd_attr {
+ struct attribute attr;
+ ssize_t (*show)(struct em_freq_domain *fd, char *buf);
+ ssize_t (*store)(struct em_freq_domain *fd, const char *buf, size_t s);
+};
+
+#define EM_ATTR_LEN 13
+#define show_table_attr(_attr) \
+static ssize_t show_##_attr(struct em_freq_domain *fd, char *buf) \
+{ \
+ ssize_t cnt = 0; \
+ int i; \
+ struct em_cs_table *table; \
+ rcu_read_lock(); \
+ table = rcu_dereference(fd->cs_table);\
+ for (i = 0; i < table->nr_cap_states; i++) { \
+ if (cnt >= (ssize_t) (PAGE_SIZE / sizeof(char) \
+ - (EM_ATTR_LEN + 2))) \
+ goto out; \
+ cnt += scnprintf(&buf[cnt], EM_ATTR_LEN + 1, "%lu ", \
+ table->state[i]._attr); \
+ } \
+out: \
+ rcu_read_unlock(); \
+ cnt += sprintf(&buf[cnt], "\n"); \
+ return cnt; \
+}
+
+show_table_attr(power);
+show_table_attr(frequency);
+show_table_attr(capacity);
+
+static ssize_t show_cpus(struct em_freq_domain *fd, char *buf)
+{
+ return sprintf(buf, "%*pbl\n", cpumask_pr_args(&fd->cpus));
+}
+
+#define fd_attr(_name) em_fd_##_name##_attr
+#define define_fd_attr(_name) static struct em_fd_attr fd_attr(_name) = \
+ __ATTR(_name, 0444, show_##_name, NULL)
+
+define_fd_attr(power);
+define_fd_attr(frequency);
+define_fd_attr(capacity);
+define_fd_attr(cpus);
+
+static struct attribute *em_fd_default_attrs[] = {
+ &fd_attr(power).attr,
+ &fd_attr(frequency).attr,
+ &fd_attr(capacity).attr,
+ &fd_attr(cpus).attr,
+ NULL
+};
+
+#define to_fd(k) container_of(k, struct em_freq_domain, kobj)
+#define to_fd_attr(a) container_of(a, struct em_fd_attr, attr)
+
+static ssize_t show(struct kobject *kobj, struct attribute *attr, char *buf)
+{
+ struct em_freq_domain *fd = to_fd(kobj);
+ struct em_fd_attr *fd_attr = to_fd_attr(attr);
+ ssize_t ret;
+
+ ret = fd_attr->show(fd, buf);
+
+ return ret;
+}
+
+static const struct sysfs_ops em_fd_sysfs_ops = {
+ .show = show,
+};
+
+static struct kobj_type ktype_em_fd = {
+ .sysfs_ops = &em_fd_sysfs_ops,
+ .default_attrs = em_fd_default_attrs,
+};
+
static struct em_cs_table *alloc_cs_table(int nr_states)
{
struct em_cs_table *cs_table;
@@ -114,6 +194,11 @@ static struct em_freq_domain *em_create_fd(cpumask_t *span, int nr_states,
}
fd_update_cs_table(fd->cs_table, cpu);
+ ret = kobject_init_and_add(&fd->kobj, &ktype_em_fd, em_kobject, "fd%u",
+ cpu);
+ if (ret)
+ pr_warn("%*pbl: failed kobject init\n", cpumask_pr_args(span));
+
return fd;
free_cs_table:
@@ -221,6 +306,15 @@ int em_register_freq_domain(cpumask_t *span, unsigned int nr_states,
mutex_lock(&em_fd_mutex);
+ if (!em_kobject) {
+ em_kobject = kobject_create_and_add("energy_model",
+ &cpu_subsys.dev_root->kobj);
+ if (!em_kobject) {
+ ret = -ENODEV;
+ goto unlock;
+ }
+ }
+
/* Make sure we don't register again an existing domain. */
for_each_cpu(cpu, span) {
if (per_cpu(em_data, cpu)) {
--
2.17.0
^ permalink raw reply related
* [RFC PATCH v3 03/10] PM: Introduce an Energy Model management framework
From: Quentin Perret @ 2018-05-21 14:24 UTC (permalink / raw)
To: peterz, rjw, gregkh, linux-kernel, linux-pm
Cc: mingo, dietmar.eggemann, morten.rasmussen, chris.redpath,
patrick.bellasi, valentin.schneider, vincent.guittot,
thara.gopinath, viresh.kumar, tkjos, joelaf, smuckle, adharmap,
skannan, pkondeti, juri.lelli, edubezval, srinivas.pandruvada,
currojerez, javi.merino, quentin.perret
In-Reply-To: <20180521142505.6522-1-quentin.perret@arm.com>
Several subsystems in the kernel (scheduler and/or thermal at the time
of writing) can benefit from knowing about the energy consumed by CPUs.
Yet, this information can come from different sources (DT or firmware for
example), in different formats, hence making it hard to exploit without
a standard API.
This patch attempts to solve this issue by introducing a centralized
Energy Model (EM) framework which can be used to interface the data
providers with the client subsystems. This framework standardizes the
API to expose power costs, and to access them from multiple locations.
The current design assumes that all CPUs in a frequency domain share the
same micro-architecture. As such, the EM data is structured in a
per-frequency-domain fashion. Drivers aware of frequency domains
(typically, but not limited to, CPUFreq drivers) are expected to register
data in the EM framework using the em_register_freq_domain() API. To do
so, the drivers must provide a callback function that will be called by
the EM framework to populate the tables. As of today, only the active
power of the CPUs is considered. For each frequency domain, the EM
includes a list of <frequency, power, capacity> tuples for the capacity
states of the domain alongside a cpumask covering the involved CPUs.
The EM framework also provides an API to re-scale the capacity values
of the model asynchronously, after it has been created. This is required
for architectures where the capacity scale factor of CPUs can change at
run-time. This is the case for Arm/Arm64 for example where the
arch_topology driver recomputes the capacity scale factors of the CPUs
after the maximum frequency of all CPUs has been discovered. Although
complex, the process of creating and re-scaling the EM has to be kept in
two separate steps to fulfill the needs of the different users. The thermal
subsystem doesn't use the capacity values and shouldn't have dependencies
on subsystems providing them. On the other hand, the task scheduler needs
the capacity values, and it will benefit from seeing them up-to-date when
applicable.
Because of this need for asynchronous update, the capacity state table
of each frequency domain is protected by RCU, hence guaranteeing a safe
modification of the table and a fast access to readers in latency-sensitive
code paths.
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: "Rafael J. Wysocki" <rjw@rjwysocki.net>
Signed-off-by: Quentin Perret <quentin.perret@arm.com>
---
include/linux/energy_model.h | 122 +++++++++++++++++
kernel/power/Kconfig | 15 +++
kernel/power/Makefile | 2 +
kernel/power/energy_model.c | 249 +++++++++++++++++++++++++++++++++++
4 files changed, 388 insertions(+)
create mode 100644 include/linux/energy_model.h
create mode 100644 kernel/power/energy_model.c
diff --git a/include/linux/energy_model.h b/include/linux/energy_model.h
new file mode 100644
index 000000000000..edde888852ba
--- /dev/null
+++ b/include/linux/energy_model.h
@@ -0,0 +1,122 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _LINUX_ENERGY_MODEL_H
+#define _LINUX_ENERGY_MODEL_H
+#include <linux/types.h>
+#include <linux/cpumask.h>
+#include <linux/jump_label.h>
+#include <linux/rcupdate.h>
+#include <linux/kobject.h>
+#include <linux/sched/cpufreq.h>
+
+#ifdef CONFIG_ENERGY_MODEL
+struct em_cap_state {
+ unsigned long capacity;
+ unsigned long frequency;
+ unsigned long power;
+};
+
+struct em_cs_table {
+ struct em_cap_state *state;
+ int nr_cap_states;
+ struct rcu_head rcu;
+};
+
+struct em_freq_domain {
+ struct em_cs_table *cs_table;
+ cpumask_t cpus;
+};
+
+struct em_data_callback {
+ /**
+ * active_power() - Provide power at the next capacity state of a CPU
+ * @power : Active power at the capacity state (modified)
+ * @freq : Frequency at the capacity state (modified)
+ * @cpu : CPU for which we do this operation
+ *
+ * active_power() must find the lowest capacity state of 'cpu' above
+ * 'freq' and update 'power' and 'freq' to the matching active power
+ * and frequency.
+ *
+ * Return 0 on success.
+ */
+ int (*active_power) (unsigned long *power, unsigned long *freq, int cpu);
+};
+
+int em_register_freq_domain(cpumask_t *span, unsigned int nr_states,
+ struct em_data_callback *cb);
+void em_rescale_cpu_capacity(void);
+struct em_freq_domain *em_cpu_get(int cpu);
+
+/**
+ * em_fd_energy() - Estimates the energy consumed by the CPUs of a freq. domain
+ * @fd : frequency domain for which energy has to be estimated
+ * @max_util : highest utilization among CPUs of the domain
+ * @sum_util : sum of the utilization of all CPUs in the domain
+ *
+ * Return: the sum of the energy consumed by the CPUs of the domain assuming
+ * a capacity state satisfying the max utilization of the domain.
+ */
+static inline unsigned long em_fd_energy(struct em_freq_domain *fd,
+ unsigned long max_util, unsigned long sum_util)
+{
+ struct em_cs_table *cs_table;
+ struct em_cap_state *cs;
+ unsigned long freq;
+ int i;
+
+ cs_table = rcu_dereference(fd->cs_table);
+ if (!cs_table)
+ return 0;
+
+ /* Map the utilization value to a frequency */
+ cs = &cs_table->state[cs_table->nr_cap_states - 1];
+ freq = map_util_freq(max_util, cs->frequency, cs->capacity);
+
+ /* Find the lowest capacity state above this frequency */
+ for (i = 0; i < cs_table->nr_cap_states; i++) {
+ cs = &cs_table->state[i];
+ if (cs->frequency >= freq)
+ break;
+ }
+
+ return cs->power * sum_util / cs->capacity;
+}
+
+/**
+ * em_fd_nr_cap_states() - Get the number of capacity states of a freq. domain
+ * @fd : frequency domain for which want to do this
+ *
+ * Return: the number of capacity state in the frequency domain table
+ */
+static inline int em_fd_nr_cap_states(struct em_freq_domain *fd)
+{
+ struct em_cs_table *table = rcu_dereference(fd->cs_table);
+
+ return table->nr_cap_states;
+}
+
+#else
+struct em_freq_domain;
+struct em_data_callback;
+static inline int em_register_freq_domain(cpumask_t *span,
+ unsigned int nr_states, struct em_data_callback *cb)
+{
+ return -ENOTSUPP;
+}
+static inline struct em_freq_domain *em_cpu_get(int cpu)
+{
+ return NULL;
+}
+static inline unsigned long em_fd_energy(struct em_freq_domain *fd,
+ unsigned long max_util, unsigned long sum_util)
+{
+ return 0;
+}
+static inline int em_fd_nr_cap_states(struct em_freq_domain *fd)
+{
+ return 0;
+}
+static inline void em_rescale_cpu_capacity(void) { }
+#endif
+
+#endif
diff --git a/kernel/power/Kconfig b/kernel/power/Kconfig
index e880ca22c5a5..b9e2b92e3be1 100644
--- a/kernel/power/Kconfig
+++ b/kernel/power/Kconfig
@@ -297,3 +297,18 @@ config PM_GENERIC_DOMAINS_OF
config CPU_PM
bool
+
+config ENERGY_MODEL
+ bool "Energy Model for CPUs"
+ depends on SMP
+ depends on CPU_FREQ
+ default n
+ help
+ Several subsystems (thermal and/or the task scheduler for example)
+ can leverage information about the energy consumed by CPUs to make
+ smarter decisions. This config option enables the framework from
+ which a user can access the energy models.
+
+ The exact usage of the energy model is subsystem-dependent.
+
+ If in doubt, say N.
diff --git a/kernel/power/Makefile b/kernel/power/Makefile
index a3f79f0eef36..e7e47d9be1e5 100644
--- a/kernel/power/Makefile
+++ b/kernel/power/Makefile
@@ -15,3 +15,5 @@ obj-$(CONFIG_PM_AUTOSLEEP) += autosleep.o
obj-$(CONFIG_PM_WAKELOCKS) += wakelock.o
obj-$(CONFIG_MAGIC_SYSRQ) += poweroff.o
+
+obj-$(CONFIG_ENERGY_MODEL) += energy_model.o
diff --git a/kernel/power/energy_model.c b/kernel/power/energy_model.c
new file mode 100644
index 000000000000..a2eece7007a8
--- /dev/null
+++ b/kernel/power/energy_model.c
@@ -0,0 +1,249 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Energy Model of CPUs
+ *
+ * Copyright (c) 2018, Arm ltd.
+ * Written by: Quentin Perret, Arm ltd.
+ */
+
+#define pr_fmt(fmt) "energy_model: " fmt
+
+#include <linux/cpu.h>
+#include <linux/slab.h>
+#include <linux/cpumask.h>
+#include <linux/energy_model.h>
+#include <linux/sched/topology.h>
+
+/* Mapping of each CPU to the frequency domain to which it belongs. */
+static DEFINE_PER_CPU(struct em_freq_domain *, em_data);
+
+/*
+ * Protects the access to em_data. Readers of em_data can be in RCU-critical
+ * sections, and can't afford to sleep.
+ */
+static DEFINE_RWLOCK(em_data_lock);
+
+/*
+ * Mutex serializing the registrations of frequency domains. It allows the
+ * callbacks defined by drivers to sleep.
+ */
+static DEFINE_MUTEX(em_fd_mutex);
+
+static struct em_cs_table *alloc_cs_table(int nr_states)
+{
+ struct em_cs_table *cs_table;
+
+ cs_table = kzalloc(sizeof(*cs_table), GFP_NOWAIT);
+ if (!cs_table)
+ return NULL;
+
+ cs_table->state = kcalloc(nr_states, sizeof(*cs_table->state),
+ GFP_NOWAIT);
+ if (!cs_table->state) {
+ kfree(cs_table);
+ return NULL;
+ }
+
+ cs_table->nr_cap_states = nr_states;
+
+ return cs_table;
+}
+
+static void free_cs_table(struct em_cs_table *table)
+{
+ if (table) {
+ kfree(table->state);
+ kfree(table);
+ }
+}
+
+static void fd_update_cs_table(struct em_cs_table *cs_table, int cpu)
+{
+ unsigned long cmax = arch_scale_cpu_capacity(NULL, cpu);
+ int max_cap_state = cs_table->nr_cap_states - 1;
+ unsigned long fmax = cs_table->state[max_cap_state].frequency;
+ int i;
+
+ for (i = 0; i < cs_table->nr_cap_states; i++)
+ cs_table->state[i].capacity = cmax *
+ cs_table->state[i].frequency / fmax;
+}
+
+static struct em_freq_domain *em_create_fd(cpumask_t *span, int nr_states,
+ struct em_data_callback *cb)
+{
+ unsigned long opp_eff, prev_opp_eff = ULONG_MAX;
+ int i, ret, cpu = cpumask_first(span);
+ struct em_freq_domain *fd;
+ unsigned long power, freq;
+
+ if (!cb->active_power)
+ return NULL;
+
+ fd = kzalloc(sizeof(*fd), GFP_KERNEL);
+ if (!fd)
+ return NULL;
+
+ fd->cs_table = alloc_cs_table(nr_states);
+ if (!fd->cs_table)
+ goto free_fd;
+
+ /* Copy the span of the frequency domain */
+ cpumask_copy(&fd->cpus, span);
+
+ /* Build the list of capacity states for this freq domain */
+ for (i = 0, freq = 0; i < nr_states; i++, freq++) {
+ ret = cb->active_power(&power, &freq, cpu);
+ if (ret)
+ goto free_cs_table;
+
+ fd->cs_table->state[i].power = power;
+ fd->cs_table->state[i].frequency = freq;
+
+ /*
+ * The hertz/watts efficiency ratio should decrease as the
+ * frequency grows on sane platforms. If not, warn the user
+ * that some high OPPs are more power efficient than some
+ * of the lower ones.
+ */
+ opp_eff = freq / power;
+ if (opp_eff >= prev_opp_eff)
+ pr_warn("%*pbl: hz/watt efficiency: OPP %d >= OPP%d\n",
+ cpumask_pr_args(span), i, i - 1);
+ prev_opp_eff = opp_eff;
+ }
+ fd_update_cs_table(fd->cs_table, cpu);
+
+ return fd;
+
+free_cs_table:
+ free_cs_table(fd->cs_table);
+free_fd:
+ kfree(fd);
+
+ return NULL;
+}
+
+static void rcu_free_cs_table(struct rcu_head *rp)
+{
+ struct em_cs_table *table;
+
+ table = container_of(rp, struct em_cs_table, rcu);
+ free_cs_table(table);
+}
+
+/**
+ * em_rescale_cpu_capacity() - Re-scale capacity values of the Energy Model
+ *
+ * This re-scales the capacity values for all capacity states of all frequency
+ * domains of the Energy Model. This should be used when the capacity values
+ * of the CPUs are updated at run-time, after the EM was registered.
+ */
+void em_rescale_cpu_capacity(void)
+{
+ struct em_cs_table *old_table, *new_table;
+ struct em_freq_domain *fd;
+ unsigned long flags;
+ int nr_states, cpu;
+
+ read_lock_irqsave(&em_data_lock, flags);
+ for_each_cpu(cpu, cpu_possible_mask) {
+ fd = per_cpu(em_data, cpu);
+ if (!fd || cpu != cpumask_first(&fd->cpus))
+ continue;
+
+ /* Copy the existing table. */
+ old_table = rcu_dereference(fd->cs_table);
+ nr_states = old_table->nr_cap_states;
+ new_table = alloc_cs_table(nr_states);
+ if (!new_table) {
+ read_unlock_irqrestore(&em_data_lock, flags);
+ return;
+ }
+ memcpy(new_table->state, old_table->state,
+ nr_states * sizeof(*new_table->state));
+
+ /* Re-scale the capacity values on the copy. */
+ fd_update_cs_table(new_table, cpumask_first(&fd->cpus));
+
+ /* Replace the table with the rescaled version. */
+ rcu_assign_pointer(fd->cs_table, new_table);
+ call_rcu(&old_table->rcu, rcu_free_cs_table);
+ }
+ read_unlock_irqrestore(&em_data_lock, flags);
+ pr_debug("Re-scaled CPU capacities\n");
+}
+EXPORT_SYMBOL_GPL(em_rescale_cpu_capacity);
+
+/**
+ * em_cpu_get() - Return the frequency domain for a CPU
+ * @cpu : CPU to find the frequency domain for
+ *
+ * Return: the frequency domain to which 'cpu' belongs, or NULL if it doesn't
+ * exist.
+ */
+struct em_freq_domain *em_cpu_get(int cpu)
+{
+ struct em_freq_domain *fd;
+ unsigned long flags;
+
+ read_lock_irqsave(&em_data_lock, flags);
+ fd = per_cpu(em_data, cpu);
+ read_unlock_irqrestore(&em_data_lock, flags);
+
+ return fd;
+}
+EXPORT_SYMBOL_GPL(em_cpu_get);
+
+/**
+ * em_register_freq_domain() - Register the Energy Model of a frequency domain
+ * @span : Mask of CPUs in the frequency domain
+ * @nr_states : Number of capacity states to register
+ * @cb : Callback functions providing the data of the Energy Model
+ *
+ * Create Energy Model tables for a frequency domain using the callbacks
+ * defined in cb.
+ *
+ * If multiple clients register the same frequency domain, all but the first
+ * registration will be ignored.
+ *
+ * Return 0 on success
+ */
+int em_register_freq_domain(cpumask_t *span, unsigned int nr_states,
+ struct em_data_callback *cb)
+{
+ struct em_freq_domain *fd;
+ unsigned long flags;
+ int cpu, ret = 0;
+
+ if (!span || !nr_states || !cb)
+ return -EINVAL;
+
+ mutex_lock(&em_fd_mutex);
+
+ /* Make sure we don't register again an existing domain. */
+ for_each_cpu(cpu, span) {
+ if (per_cpu(em_data, cpu)) {
+ ret = -EEXIST;
+ goto unlock;
+ }
+ }
+
+ /* Create the frequency domain and add it to the Energy Model. */
+ fd = em_create_fd(span, nr_states, cb);
+ if (!fd) {
+ ret = -EINVAL;
+ goto unlock;
+ }
+
+ write_lock_irqsave(&em_data_lock, flags);
+ for_each_cpu(cpu, span)
+ per_cpu(em_data, cpu) = fd;
+ write_unlock_irqrestore(&em_data_lock, flags);
+
+ pr_debug("Created freq domain %*pbl\n", cpumask_pr_args(span));
+unlock:
+ mutex_unlock(&em_fd_mutex);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(em_register_freq_domain);
--
2.17.0
^ permalink raw reply related
* [RFC PATCH v3 02/10] sched/cpufreq: Factor out utilization to frequency mapping
From: Quentin Perret @ 2018-05-21 14:24 UTC (permalink / raw)
To: peterz, rjw, gregkh, linux-kernel, linux-pm
Cc: mingo, dietmar.eggemann, morten.rasmussen, chris.redpath,
patrick.bellasi, valentin.schneider, vincent.guittot,
thara.gopinath, viresh.kumar, tkjos, joelaf, smuckle, adharmap,
skannan, pkondeti, juri.lelli, edubezval, srinivas.pandruvada,
currojerez, javi.merino, quentin.perret
In-Reply-To: <20180521142505.6522-1-quentin.perret@arm.com>
The schedutil governor maps utilization values to frequencies by applying
a 25% margin. Since this sort of mapping mechanism can be needed by other
users (i.e. EAS), factor the utilization-to-frequency mapping code out
of schedutil and move it to include/linux/sched/cpufreq.h to avoid code
duplication. The new map_util_freq() function is inlined to avoid
overheads.
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Signed-off-by: Quentin Perret <quentin.perret@arm.com>
---
include/linux/sched/cpufreq.h | 6 ++++++
kernel/sched/cpufreq_schedutil.c | 3 ++-
2 files changed, 8 insertions(+), 1 deletion(-)
diff --git a/include/linux/sched/cpufreq.h b/include/linux/sched/cpufreq.h
index 59667444669f..afa940cd50dc 100644
--- a/include/linux/sched/cpufreq.h
+++ b/include/linux/sched/cpufreq.h
@@ -20,6 +20,12 @@ void cpufreq_add_update_util_hook(int cpu, struct update_util_data *data,
void (*func)(struct update_util_data *data, u64 time,
unsigned int flags));
void cpufreq_remove_update_util_hook(int cpu);
+
+static inline unsigned long map_util_freq(unsigned long util,
+ unsigned long freq, unsigned long cap)
+{
+ return (freq + (freq >> 2)) * util / cap;
+}
#endif /* CONFIG_CPU_FREQ */
#endif /* _LINUX_SCHED_CPUFREQ_H */
diff --git a/kernel/sched/cpufreq_schedutil.c b/kernel/sched/cpufreq_schedutil.c
index 63014cff76a5..4fdc6eb7c88b 100644
--- a/kernel/sched/cpufreq_schedutil.c
+++ b/kernel/sched/cpufreq_schedutil.c
@@ -13,6 +13,7 @@
#include "sched.h"
+#include <linux/sched/cpufreq.h>
#include <trace/events/power.h>
struct sugov_tunables {
@@ -163,7 +164,7 @@ static unsigned int get_next_freq(struct sugov_policy *sg_policy,
unsigned int freq = arch_scale_freq_invariant() ?
policy->cpuinfo.max_freq : policy->cur;
- freq = (freq + (freq >> 2)) * util / max;
+ freq = map_util_freq(util, freq, max);
if (freq == sg_policy->cached_raw_freq && sg_policy->next_freq != UINT_MAX)
return sg_policy->next_freq;
--
2.17.0
^ permalink raw reply related
* [RFC PATCH v3 01/10] sched: Relocate arch_scale_cpu_capacity
From: Quentin Perret @ 2018-05-21 14:24 UTC (permalink / raw)
To: peterz, rjw, gregkh, linux-kernel, linux-pm
Cc: mingo, dietmar.eggemann, morten.rasmussen, chris.redpath,
patrick.bellasi, valentin.schneider, vincent.guittot,
thara.gopinath, viresh.kumar, tkjos, joelaf, smuckle, adharmap,
skannan, pkondeti, juri.lelli, edubezval, srinivas.pandruvada,
currojerez, javi.merino, quentin.perret
In-Reply-To: <20180521142505.6522-1-quentin.perret@arm.com>
By default, arch_scale_cpu_capacity() is only visible from within the
kernel/sched folder. Relocate it to include/linux/sched/topology.h to
make it visible to other clients needing to know about the capacity of
CPUs, such as the Energy Model framework.
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Signed-off-by: Quentin Perret <quentin.perret@arm.com>
---
include/linux/sched/topology.h | 19 +++++++++++++++++++
kernel/sched/sched.h | 18 ------------------
2 files changed, 19 insertions(+), 18 deletions(-)
diff --git a/include/linux/sched/topology.h b/include/linux/sched/topology.h
index 26347741ba50..1e24e88bee6d 100644
--- a/include/linux/sched/topology.h
+++ b/include/linux/sched/topology.h
@@ -202,6 +202,17 @@ extern void set_sched_topology(struct sched_domain_topology_level *tl);
# define SD_INIT_NAME(type)
#endif
+#ifndef arch_scale_cpu_capacity
+static __always_inline
+unsigned long arch_scale_cpu_capacity(struct sched_domain *sd, int cpu)
+{
+ if (sd && (sd->flags & SD_SHARE_CPUCAPACITY) && (sd->span_weight > 1))
+ return sd->smt_gain / sd->span_weight;
+
+ return SCHED_CAPACITY_SCALE;
+}
+#endif
+
#else /* CONFIG_SMP */
struct sched_domain_attr;
@@ -217,6 +228,14 @@ static inline bool cpus_share_cache(int this_cpu, int that_cpu)
return true;
}
+#ifndef arch_scale_cpu_capacity
+static __always_inline
+unsigned long arch_scale_cpu_capacity(void __always_unused *sd, int cpu)
+{
+ return SCHED_CAPACITY_SCALE;
+}
+#endif
+
#endif /* !CONFIG_SMP */
static inline int task_node(const struct task_struct *p)
diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h
index 15750c222ca2..ce562d3b7526 100644
--- a/kernel/sched/sched.h
+++ b/kernel/sched/sched.h
@@ -1724,30 +1724,12 @@ unsigned long arch_scale_freq_capacity(int cpu)
#ifdef CONFIG_SMP
extern void sched_avg_update(struct rq *rq);
-#ifndef arch_scale_cpu_capacity
-static __always_inline
-unsigned long arch_scale_cpu_capacity(struct sched_domain *sd, int cpu)
-{
- if (sd && (sd->flags & SD_SHARE_CPUCAPACITY) && (sd->span_weight > 1))
- return sd->smt_gain / sd->span_weight;
-
- return SCHED_CAPACITY_SCALE;
-}
-#endif
-
static inline void sched_rt_avg_update(struct rq *rq, u64 rt_delta)
{
rq->rt_avg += rt_delta * arch_scale_freq_capacity(cpu_of(rq));
sched_avg_update(rq);
}
#else
-#ifndef arch_scale_cpu_capacity
-static __always_inline
-unsigned long arch_scale_cpu_capacity(void __always_unused *sd, int cpu)
-{
- return SCHED_CAPACITY_SCALE;
-}
-#endif
static inline void sched_rt_avg_update(struct rq *rq, u64 rt_delta) { }
static inline void sched_avg_update(struct rq *rq) { }
#endif
--
2.17.0
^ permalink raw reply related
* [RFC PATCH v3 00/10] Energy Aware Scheduling
From: Quentin Perret @ 2018-05-21 14:24 UTC (permalink / raw)
To: peterz, rjw, gregkh, linux-kernel, linux-pm
Cc: mingo, dietmar.eggemann, morten.rasmussen, chris.redpath,
patrick.bellasi, valentin.schneider, vincent.guittot,
thara.gopinath, viresh.kumar, tkjos, joelaf, smuckle, adharmap,
skannan, pkondeti, juri.lelli, edubezval, srinivas.pandruvada,
currojerez, javi.merino, quentin.perret
1. Overview
The Energy Aware Scheduler (EAS) based on Morten Rasmussen's posting on
LKML [1] is currently part of the AOSP Common Kernel and runs on today's
smartphones with Arm's big.LITTLE CPUs. This series implements a new and
largely simplified version of EAS based on an Energy Model (EM) of the
platform with only costs for the active states of the CPUs.
Previous versions of this patch-set (i.e. [2]) relied on the PM_OPP
framework to provide the scheduler with an Energy Model. As agreed during
the 2nd OSPM sumit, this new revision removes this dependency by
implementing a new independent EM framework. This framework aggregates
the power values provided by drivers into a table for each frequency
domain in the system. Those tables are then available to interested
clients (e.g. the task scheduler or the thermal subsystem) via
platform-agnostic APIs. The topology code of the scheduler is modified
accordingly to take a reference on the online frequency domains, hence
guaranteeing a fast access to the shared EM data structures in
latency-sensitive code paths. The modifications required to make the
thermal subsystem use the new Energy Model framework are not covered by
this patch-set.
The v2 of this patch-set used a per-scheduling domain overutilization
flag, which has been abandoned in v3 in favour of a simpler but equally
efficient system-wide implementation attached to the root domain (like
the existing overload flag). Consequently, the integration of EAS in the
wake-up path has been reworked to accommodate this change using a
scheduling domain shortcut.
The patch-set is now arranged as follows:
- Patches 1-2 refactor code from schedutil and the scheduler to
simplify the implementation of the EM framework;
- Patches 3-4 introduce the centralized EM framework;
- Patch 5 changes the scheduler topology code to make it aware of the EM;
- Patch 6 implements the overutilization mechanism;
- Patches 7-9 introduce the energy-aware wake-up path in the CFS class;
- Patch 10 starts EAS for arm/arm64 from the arch_topology driver.
2. Test results
Two fundamentally different tests were executed. Firstly the energy
test case shows the impact on energy consumption this patch-set has
using a synthetic set of tasks. Secondly the performance test case
provides the conventional hackbench metric numbers.
The tests run on two arm64 big.LITTLE platforms: Hikey960 (4xA73 +
4xA53) and Juno r0 (2xA57 + 4xA53).
Base kernel is tip/sched/core (4.17-rc3), with some Hikey960 and
Juno specific patches, the SD_ASYM_CPUCAPACITY flag set at DIE sched
domain level for arm64 and schedutil as cpufreq governor [4].
2.1 Energy test case
10 iterations of between 10 and 50 periodic rt-app tasks (16ms period,
5% duty-cycle) for 30 seconds with energy measurement. Unit is Joules.
The goal is to save energy, so lower is better.
2.1.1 Hikey960
Energy is measured with an ACME Cape on an instrumented board. Numbers
include consumption of big and little CPUs, LPDDR memory, GPU and most
of the other small components on the board. They do not include
consumption of the radio chip (turned-off anyway) and external
connectors.
+----------+-----------------+------------------------+
| | Without patches | With patches |
+----------+--------+--------+-----------------+------+
| Tasks nb | Mean | RSD* | Mean | RSD* |
+----------+--------+--------+-----------------+------+
| 10 | 33.45 | 1.2% | 28.97 (-13.39%) | 2.0% |
| 20 | 45.45 | 1.7% | 42.76 (-5.92%) | 0.8% |
| 30 | 65.06 | 0.2% | 64.85 (-0.32%) | 4.7% |
| 40 | 85.67 | 0.7% | 77.98 (-8.98%) | 2.8% |
| 50 | 110.14 | 0.9% | 99.34 (-9.81%) | 2.0% |
+----------+--------+--------+-----------------+------+
2.1.2 Juno r0
Energy is measured with the onboard energy meter. Numbers include
consumption of big and little CPUs.
+----------+-----------------+------------------------+
| | Without patches | With patches |
+----------+--------+--------+-----------------+------+
| Tasks nb | Mean | RSD* | Mean | RSD* |
+----------+--------+--------+-----------------+------+
| 10 | 10.40 | 3.0% | 7.00 (-32.69%) | 2.5% |
| 20 | 18.47 | 1.1% | 12.88 (-30.27%) | 2.4% |
| 30 | 27.97 | 2.2% | 21.26 (-23.99%) | 0.2% |
| 40 | 36.86 | 1.2% | 30.63 (-16.90%) | 0.4% |
| 50 | 46.79 | 0.5% | 45.85 ( -0.02%) | 0.7% |
+----------+--------+--------+------------------+------+
2.2 Performance test case
30 iterations of perf bench sched messaging --pipe --thread --group G
--loop L with G=[1 2 4 8] and L=50000 (Hikey960)/16000 (Juno r0).
2.2.1 Hikey960
The impact of thermal capping was mitigated thanks to a heatsink, a
fan, and a 10 sec delay between two successive executions.
+----------------+-----------------+------------------------+
| | Without patches | With patches |
+--------+-------+---------+-------+----------------+-------+
| Groups | Tasks | Mean | RSD* | Mean | RSD* |
+--------+-------+---------+-------+----------------+-------+
| 1 | 40 | 8.75 | 0.99% | 9.46 (+8.11%) | 3.34% |
| 2 | 80 | 15.64 | 0.68% | 15.96 (+2.05%) | 0.71% |
| 4 | 160 | 31.58 | 0.65% | 32.22 (+2.03%) | 0.61% |
| 8 | 320 | 65.53 | 0.37% | 66.43 (+1.37%) | 0.36% |
+--------+-------+---------+-------+----------------+-------+
2.2.2 Juno r0
+----------------+-----------------+------------------------+
| | Without patches | With patches |
+--------+-------+---------+-------+----------------+-------+
| Groups | Tasks | Mean | RSD* | Mean | RSD* |
+--------+-------+---------+-------+----------------+-------+
| 1 | 40 | 8.25 | 0.11% | 8.21 ( 0.00%) | 0.10% |
| 2 | 80 | 14.40 | 0.14% | 14.37 ( 0.00%) | 0.12% |
| 4 | 160 | 26.72 | 0.24% | 26.73 ( 0.00%) | 0.14% |
| 8 | 320 | 52.89 | 0.10% | 52.87 ( 0.00%) | 0.23% |
+--------+-------+---------+-------+----------------+-------+
*RSD: Relative Standard Deviation (std dev / mean)
3. Changes between versions
Changes v2[2]->v3:
- Removed the PM_OPP dependency by implementing a new EM framework
- Modified the scheduler topology code to take references on the EM data
structures
- Simplified the overutilization mechanism into a system-wide flag
- Reworked the integration in the wake-up path using the sd_ea shortcut
- Rebased on tip/sched/core (247f2f6f3c70 "sched/core: Don't schedule
threads on pre-empted vCPUs")
Changes v1[3]->v2:
- Reworked interface between fair.c and energy.[ch] (Remove #ifdef
CONFIG_PM_OPP from energy.c) (Greg KH)
- Fixed licence & header issue in energy.[ch] (Greg KH)
- Reordered EAS path in select_task_rq_fair() (Joel)
- Avoid prev_cpu if not allowed in select_task_rq_fair() (Morten/Joel)
- Refactored compute_energy() (Patrick)
- Account for RT/IRQ pressure in task_fits() (Patrick)
- Use UTIL_EST and DL utilization during OPP estimation (Patrick/Juri)
- Optimize selection of CPU candidates in the energy-aware wake-up path
- Rebased on top of tip/sched/core (commit b720342849fe “sched/core:
Update Preempt_notifier_key to modern API”)
[1] https://lkml.org/lkml/2015/7/7/754
[2] https://marc.info/?l=linux-kernel&m=152302902427143&w=2
[3] https://marc.info/?l=linux-kernel&m=152153905805048&w=2
[4] http://linux-arm.org/git?p=linux-qp.git;a=shortlog;h=refs/heads/upstream/eas_v3
Morten Rasmussen (1):
sched: Add over-utilization/tipping point indicator
Quentin Perret (9):
sched: Relocate arch_scale_cpu_capacity
sched/cpufreq: Factor out utilization to frequency mapping
PM: Introduce an Energy Model management framework
PM / EM: Expose the Energy Model in sysfs
sched/topology: Reference the Energy Model of CPUs when available
sched/fair: Introduce an energy estimation helper function
sched: Lowest energy aware balancing sched_domain level pointer
sched/fair: Select an energy-efficient CPU on task wake-up
arch_topology: Start Energy Aware Scheduling
drivers/base/arch_topology.c | 19 ++
include/linux/energy_model.h | 123 +++++++++++
include/linux/sched/cpufreq.h | 6 +
include/linux/sched/topology.h | 19 ++
kernel/power/Kconfig | 15 ++
kernel/power/Makefile | 2 +
kernel/power/energy_model.c | 343 +++++++++++++++++++++++++++++++
kernel/sched/cpufreq_schedutil.c | 3 +-
kernel/sched/fair.c | 186 ++++++++++++++++-
kernel/sched/sched.h | 51 +++--
kernel/sched/topology.c | 117 +++++++++++
11 files changed, 860 insertions(+), 24 deletions(-)
create mode 100644 include/linux/energy_model.h
create mode 100644 kernel/power/energy_model.c
--
2.17.0
^ permalink raw reply
* Re: OMAP serial runtime PM and autosuspend (was: Re: [PATCH 4/7] dt-bindings: gnss: add u-blox binding))
From: Johan Hovold @ 2018-05-21 13:48 UTC (permalink / raw)
To: Tony Lindgren
Cc: Johan Hovold, Sebastian Reichel, H. Nikolaus Schaller,
Andreas Kemnade, Mark Rutland, Arnd Bergmann, Pavel Machek,
linux-kernel@vger.kernel.org,
open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
Greg Kroah-Hartman, Rob Herring, linux-serial, linux-omap,
linux-pm
In-Reply-To: <20180517171038.GL98604@atomide.com>
On Thu, May 17, 2018 at 10:10:38AM -0700, Tony Lindgren wrote:
> * Johan Hovold <johan@kernel.org> [180517 10:12]:
> > [ Sorry about the late reply. ]
> >
> > On Wed, May 09, 2018 at 06:57:06AM -0700, Tony Lindgren wrote:
> > > * Johan Hovold <johan@kernel.org> [180509 13:12]:
> >
> > > > It seems we really should not be using the negative autosuspend to
> > > > configure the RPM behaviour the way these drivers do. Perhaps a new
> > > > mechanism is needed.
> > >
> > > Hmm well simply defaulting to "on" instead of "auto" and setting the
> > > autosuspend_ms to 3000 by default might be doable. I think that way
> > > we can keep use_autosuspend() in probe. Let's hope there are no
> > > existing use cases that would break with that.
> >
> > No, defaulting to "on" (i.e. calling pm_runtime_forbid()) wouldn't work
> > either as that would also prevent the device from runtime suspending
> > just as the current negative autosuspend delay does.
>
> Well in that case we should just stick with -1 value for the
> autosuspend. And just do pm_runtime_put_sync_suspend() after
> probe and on close.
That won't work either as a negative autosuspend delay prevents runtime
suspend completely (by grabbing an extra RPM reference).
> > I fail to see how we can implement this using the current toolbox. What
> > you're after here is really a mechanism for selecting between two
> > different runtime PM schemes at runtime:
> >
> > 1. normal serial RPM, where the controller is active while the
> > port is open (this should be the safe default)
>
> Agreed. And that is the case already.
Yes, but it's not really the case today as since omap-serial (and
8250-omap) sets a negative autosuspend at probe and hence does not
runtime-suspend when the port is closed. So that's the long-standing bug
which needs fixing.
> > 2. aggressive serial RPM, where the controller is allowed to
> > suspend while the port is open even though this may result in
> > lost characters when waking up on incoming data
>
> In this case it seems that the only thing needed is to just
> configure the autosuspend delay for the parent port. The use of
> -1 has been around since the start of runtime PM AFAIK, so maybe
> we should just document it. I guess we could also introduce
> pm_runtime_block_autoidle_unless_configured() :)
The implications of a negative autosuspend delay are already documented
(in Documentation/power/runtime_pm.txt); it's just the omap drivers that
gets it wrong when trying to do things which aren't currently supported
(and never have been).
So I still think we need a new mechanism for this.
> > For normal ttys, we need a user-space interface for selecting between
> > the two, and for serdev we may want a way to select the RPM scheme from
> > within the kernel.
> >
> > Note that with my serdev controller runtime PM patch, serdev core could
> > always opt for aggressive PM (as by default serdev core holds an RPM
> > reference for the controller while the port is open).
>
> So if your serdev controller was to set the parent autosuspend
> delay on open() and set it back on close() this should work?
Is it really the job of a serdev driver to set the autosuspend delay of
a parent controller? Isn't this somethings which depends on the
characteristics of the controller (possibly configurable by user space)
such as the cost of runtime suspending and resuming?
The patch I posted works with what we have today; if a parent serial
controller driver uses aggressive runtime PM by default or after having
been configured through sysfs to do so.
What I'm getting at here is that the delay should be set by the serial
driver implementing aggressive runtime PM. Then all we need is a
mechanism to determine whether an extra RPM reference should be taken at
tty open or not (configurable by user space, defaulting to yes).
Specifically, the serial drivers themselves would always use
autosuspend and not have to deal with supporting the two RPM schemes
(normal vs aggressive runtime PM).
Thanks,
Johan
^ permalink raw reply
* Re: [PATCH 2/3] thinkpad_acpi: add support for force_discharge
From: Kevin Locke @ 2018-05-21 13:31 UTC (permalink / raw)
To: Ognjen Galic
Cc: Platform Driver, Rafael J. Wysocki, Henrique de Moraes Holschuh,
Linux PM, Rafael J. Wysocki, Robert Moore, Sebastian Reichel,
ACPI Devel Maling List, Andy Shevchenko,
Christoph Böhmwalder, Darren Hart,
devel-E0kO6a4B6psdnm+yROfE0A,
ibm-acpi-devel-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f, Andy Shevchenko,
Len Brown
In-Reply-To: <20180513153000.GA5117@thinkpad>
On Sun, 2018-05-13 at 17:30 +0200, Ognjen Galic wrote:
> Lenovo ThinkPad systems have a feature that lets you
> force the battery to discharge regardless if AC is attached
> or not.
>
> This patch implements that feature and exposes it via the generic
> ACPI battery driver.
On a T430 (2342-CTO) I can confirm that both force_discharge and
inhibit_charge behave as expected, both when the battery is above and
below charge_start_threshold. Input validation also works
as-expected. The only oddity I noticed is that force_discharge has a
delay taking effect (<1 sec) transitioning from 0 to 1 (but not 1 to
0).
Tested-by: Kevin Locke <kevin-yFOG++GfaLUI8/NkjqMgMw@public.gmane.org>
Thanks for working on this!
Kevin
------------------------------------------------------------------------------
Check out the vibrant tech community on one of the world's most
engaging tech sites, Slashdot.org! http://sdm.link/slashdot
^ permalink raw reply
* Re: [PATCH v9 01/15] soc: qcom: Separate kryo l2 accessors from PMU driver
From: Will Deacon @ 2018-05-21 13:17 UTC (permalink / raw)
To: Ilia Lin
Cc: mturquette, sboyd, robh, mark.rutland, viresh.kumar, nm,
lgirdwood, broonie, andy.gross, david.brown, catalin.marinas, rjw,
linux-clk, devicetree, rnayak, linux-pm, linux-arm-msm,
linux-kernel, amit.kucheria, tfinkel, nicolas.dechesne, celster,
linux-soc, linux-arm-kernel
In-Reply-To: <1526901932-9514-2-git-send-email-ilialin@codeaurora.org>
On Mon, May 21, 2018 at 02:25:18PM +0300, Ilia Lin wrote:
> The driver provides kernel level API for other drivers
> to access the MSM8996 L2 cache registers.
> Separating the L2 access code from the PMU driver and
> making it public to allow other drivers use it.
> The accesses must be separated with a single spinlock,
> maintained in this driver.
>
> Signed-off-by: Ilia Lin <ilialin@codeaurora.org>
> ---
> drivers/perf/Kconfig | 1 +
> drivers/perf/qcom_l2_pmu.c | 90 ++++++++++--------------------------
I'm fine with the perf bits:
Acked-by: Will Deacon <will.deacon@arm.com>
Will
^ permalink raw reply
* Re: [PATCH] cpufreq: Add Kryo CPU scaling driver
From: Sudeep Holla @ 2018-05-21 13:04 UTC (permalink / raw)
To: ilialin, mturquette, sboyd, robh, mark.rutland, viresh.kumar, nm,
lgirdwood, broonie, andy.gross, david.brown, catalin.marinas,
will.deacon, rjw, linux-clk
Cc: Sudeep Holla, devicetree, linux-kernel, linux-pm, linux-arm-msm,
linux-soc, linux-arm-kernel, rnayak, amit.kucheria,
nicolas.dechesne, celster, tfinkel
In-Reply-To: <000f01d3f103$3ff78ba0$bfe6a2e0$@codeaurora.org>
On 21/05/18 13:57, ilialin@codeaurora.org wrote:
>
[...]
>>> +#include <linux/cpu.h>
>>> +#include <linux/err.h>
>>> +#include <linux/init.h>
>>> +#include <linux/kernel.h>
>>> +#include <linux/module.h>
>>> +#include <linux/nvmem-consumer.h>
>>> +#include <linux/of.h>
>>> +#include <linux/platform_device.h>
>>> +#include <linux/pm_opp.h>
>>> +#include <linux/slab.h>
>>> +#include <linux/soc/qcom/smem.h>
>>> +
>>> +#define MSM_ID_SMEM 137
>>> +#define SILVER_LEAD 0
>>> +#define GOLD_LEAD 2
>>> +
>>
>> So I gather form other emails, that these are physical cpu number(not even
>> unique identifier like MPIDR). Will this work on parts or platforms that need
>> to boot in GOLD LEAD cpus.
>
> The driver is for Kryo CPU, which (and AFAIK all multicore MSMs)
> always boots on the CPU0.
That may be true and I am not that bothered about it. But assuming
physical ordering from the logical cpu number is *incorrect* and will
break if kernel decides to change the allocation algorithm. Kernel
provides no guarantee on that, so you need to depend on some physical ID
or may be DT to achieve what your want. But the current code as it
stands is wrong.
--
Regards,
Sudeep
^ permalink raw reply
* Re: [PATCH 00/27] Add multi-channel and overheat IRQ support to Armada thermal driver
From: Zhang Rui @ 2018-05-21 13:01 UTC (permalink / raw)
To: Miquel Raynal, Gregory CLEMENT
Cc: Mark Rutland, Andrew Lunn, Jason Cooper, Nadav Haklai, devicetree,
Antoine Tenart, Catalin Marinas, linux-pm, Will Deacon,
Maxime Chevallier, Eduardo Valentin, David Sniatkiwicz,
Rob Herring, Thomas Petazzoni, linux-arm-kernel,
Sebastian Hesselbarth
In-Reply-To: <20180518114935.38d6e883@xps13>
On 五, 2018-05-18 at 11:49 +0200, Miquel Raynal wrote:
> Hi Zhang, Eduardo & Gregory,
>
> On Wed, 16 May 2018 19:28:45 +0200, Gregory CLEMENT
> <gregory.clement@bootlin.com> wrote:
>
> >
> > Hi Miquel,
> >
> > On sam., avril 21 2018, Miquel Raynal <miquel.raynal@bootlin.com>
> > wrote:
> >
> > >
> > > The only capability of the Armada thermal driver is currently
> > > just to
> > > read one sensor (the default one) per AP and one per CP.
> > >
> > > Actually, there is one sensor per core in the AP806 plus one
> > > sensor in
> > > the thermal IP itself. The CP110 just features one thermal sensor
> > > in its
> > > own thermal IP.
> > >
> > > Also, there is no need for the thermal core to poll the
> > > temperature of
> > > each sensor by software as this IP (at least for AP806 and CP110
> > > compatibles) features an hardware overheat interrupt.
> > >
> > > This series first improves the readability of this driver, then
> > > adds
> > > support for multi-channel thermal IPs, and finally adds support
> > > for the
> > > hardware overheat interrupt. The bindings and the device-trees
> > > are
> > > updated accordingly.
> > >
> > > Please note that the thermal IP raises SEI interrupts, from which
> > > the
> > > support as just been contributed and not merged yet. Applying the
> > > last
> > > DT patches referring to the 'sei' and 'icu_sei' nodes will
> > > require this
> > > feature [1] to have been accepted first.
> > >
> > > [1] http://lists.infradead.org/pipermail/linux-arm-kernel/2018-Ap
> > > ril/572852.html
> > >
> > > Thank you,
> > > Miquèl
> > >
> > >
> > > Miquel Raynal (27):
> > > thermal: armada: add a function that sanitizes the thermal zone
> > > name
> > > thermal: armada: remove useless register accesses
> > > thermal: armada: remove misleading comments
> > > thermal: armada: rename the initialization routine
> > > thermal: armada: dissociate a380 and cp110 ->init() hooks
> > > thermal: armada: average over samples to avoid glitches
> > > thermal: armada: convert driver to syscon register accesses
> > > thermal: armada: use the resource managed registration helper
> > > alternative
> > > thermal: armada: add multi-channel sensors support
> > > thermal: armada: remove sensors validity from the IP
> > > initialization
> > > thermal: armada: move validity check out of the read function
> > > thermal: armada: get rid of the ->is_valid() pointer
> > > thermal: armada: add overheat interrupt support
> > > dt-bindings: cp110: rename cp110 syscon file
> > > dt-bindings: ap806: prepare the syscon file to list other
> > > syscons
> > > nodes
> > > dt-bindings: cp110: prepare the syscon file to list other
> > > syscons
> > > nodes
> > > dt-bindings: ap806: add the thermal node in the syscon file
> > > dt-bindings: cp110: update documentation since DT de-
> > > duplication
> > > dt-bindings: cp110: add the thermal node in the syscon file
> > > dt-bindings: thermal: armada: add reference to new bindings
> > > arm64: dts: marvell: rename ap806 syscon node
> > > arm64: dts: marvell: move AP806/CP110 thermal nodes into a new
> > > syscon
> > > arm64: dts: marvell: add thermal-zone node in ap806 DTSI file
> > > arm64: dts: marvell: add macro to make distinction between node
> > > names
> > > arm64: dts: marvell: add thermal-zone node in cp110 DTSI file
> > > arm64: dts: marvell: add interrupt support to ap806 thermal
> > > node
> > > arm64: dts: marvell: add interrupt support to cp110 thermal
> > > node
> > >
> > > .../arm/marvell/ap806-system-controller.txt | 55 +-
> > > ...controller0.txt => cp110-system-controller.txt} | 66 +-
> > > .../devicetree/bindings/thermal/armada-thermal.txt | 5 +
> > > arch/arm64/boot/dts/marvell/armada-ap806.dtsi | 85 +-
> > > arch/arm64/boot/dts/marvell/armada-common.dtsi | 1 +
> > > arch/arm64/boot/dts/marvell/armada-cp110.dtsi | 45 +-
> > > drivers/thermal/armada_thermal.c | 875
> > > ++++++++++++++++++---
> > > 7 files changed, 976 insertions(+), 156 deletions(-)
> > > rename Documentation/devicetree/bindings/arm/marvell/{cp110-
> > > system-controller0.txt => cp110-system-controller.txt} (83%)
> > What is the status of this series?
> > I am especially interested in the dt part.
> > Do you expect sending a new series modifying them?
> I have not received any feedback yet on the thermal part, bindings
> have
> been partially acked by Rob (one request, I will probably add a reg
> property in the AP node) so please do not take the DTS changes of
> this iteration.
>
> Zhang, Eduardo, could you please share the status of this series?
>
hmmm, this should go through Eduardo' thermal-soc tree, thus I'd expect
Eduardo to review this patch set.
Eduardo, what's your comments for this series?
thanks,
rui
> Thanks,
> Miquèl
>
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* RE: [PATCH] cpufreq: Add Kryo CPU scaling driver
From: ilialin @ 2018-05-21 12:57 UTC (permalink / raw)
To: 'Sudeep Holla', mturquette, sboyd, robh, mark.rutland,
viresh.kumar, nm, lgirdwood, broonie, andy.gross, david.brown,
catalin.marinas, will.deacon, rjw, linux-clk
Cc: devicetree, linux-kernel, linux-pm, linux-arm-msm, linux-soc,
linux-arm-kernel, rnayak, amit.kucheria, nicolas.dechesne,
celster, tfinkel
In-Reply-To: <153cc316-dcb5-972f-5a2f-c91fe0f6348b@arm.com>
> -----Original Message-----
> From: Sudeep Holla <sudeep.holla@arm.com>
> Sent: Monday, May 21, 2018 15:50
> To: Ilia Lin <ilialin@codeaurora.org>; mturquette@baylibre.com;
> sboyd@kernel.org; robh@kernel.org; mark.rutland@arm.com;
> viresh.kumar@linaro.org; nm@ti.com; lgirdwood@gmail.com;
> broonie@kernel.org; andy.gross@linaro.org; david.brown@linaro.org;
> catalin.marinas@arm.com; will.deacon@arm.com; rjw@rjwysocki.net; linux-
> clk@vger.kernel.org
> Cc: Sudeep Holla <sudeep.holla@arm.com>; devicetree@vger.kernel.org;
> linux-kernel@vger.kernel.org; linux-pm@vger.kernel.org; linux-arm-
> msm@vger.kernel.org; linux-soc@vger.kernel.org; linux-arm-
> kernel@lists.infradead.org; rnayak@codeaurora.org;
> amit.kucheria@linaro.org; nicolas.dechesne@linaro.org;
> celster@codeaurora.org; tfinkel@codeaurora.org
> Subject: Re: [PATCH] cpufreq: Add Kryo CPU scaling driver
>
>
>
> On 19/05/18 12:35, Ilia Lin wrote:
> > In Certain QCOM SoCs like apq8096 and msm8996 that have KRYO
> > processors, the CPU frequency subset and voltage value of each OPP
> > varies based on the silicon variant in use. Qualcomm Process Voltage
> > Scaling Tables defines the voltage and frequency value based on the
> > msm-id in SMEM and speedbin blown in the efuse combination.
> > The qcom-cpufreq-kryo driver reads the msm-id and efuse value from the
> > SoC to provide the OPP framework with required information.
> > This is used to determine the voltage and frequency value for each OPP
> > of
> > operating-points-v2 table when it is parsed by the OPP framework.
> >
> > Signed-off-by: Ilia Lin <ilialin@codeaurora.org>
> > Acked-by: Viresh Kumar <viresh.kumar@linaro.org>
> > ---
> > drivers/cpufreq/Kconfig.arm | 10 +++
> > drivers/cpufreq/Makefile | 1 +
> > drivers/cpufreq/cpufreq-dt-platdev.c | 3 +
> > drivers/cpufreq/qcom-cpufreq-kryo.c | 164
> > +++++++++++++++++++++++++++++++++++
> > 4 files changed, 178 insertions(+)
> > create mode 100644 drivers/cpufreq/qcom-cpufreq-kryo.c
> >
>
> [..]
>
> > +
> > +/*
> > + * In Certain QCOM SoCs like apq8096 and msm8996 that have KRYO
> > +processors,
> > + * the CPU frequency subset and voltage value of each OPP varies
> > + * based on the silicon variant in use. Qualcomm Process Voltage
> > +Scaling Tables
> > + * defines the voltage and frequency value based on the msm-id in
> > +SMEM
> > + * and speedbin blown in the efuse combination.
> > + * The qcom-cpufreq-kryo driver reads the msm-id and efuse value from
> > +the SoC
> > + * to provide the OPP framework with required information.
> > + * This is used to determine the voltage and frequency value for each
> > +OPP of
> > + * operating-points-v2 table when it is parsed by the OPP framework.
> > + */
> > +
> > +#include <linux/cpu.h>
> > +#include <linux/err.h>
> > +#include <linux/init.h>
> > +#include <linux/kernel.h>
> > +#include <linux/module.h>
> > +#include <linux/nvmem-consumer.h>
> > +#include <linux/of.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/pm_opp.h>
> > +#include <linux/slab.h>
> > +#include <linux/soc/qcom/smem.h>
> > +
> > +#define MSM_ID_SMEM 137
> > +#define SILVER_LEAD 0
> > +#define GOLD_LEAD 2
> > +
>
> So I gather form other emails, that these are physical cpu number(not even
> unique identifier like MPIDR). Will this work on parts or platforms that need
> to boot in GOLD LEAD cpus.
The driver is for Kryo CPU, which (and AFAIK all multicore MSMs) always boots on the CPU0.
>
> [...]
>
> > +
> > +static int __init qcom_cpufreq_kryo_driver_init(void)
> > +{
> > + struct device *cpu_dev_silver, *cpu_dev_gold;
> > + struct opp_table *opp_silver, *opp_gold;
> > + enum _msm8996_version msm8996_version;
> > + struct nvmem_cell *speedbin_nvmem;
> > + struct platform_device *pdev;
> > + struct device_node *np;
> > + u8 *speedbin;
> > + u32 versions;
> > + size_t len;
> > + int ret;
> > +
> > + cpu_dev_silver = get_cpu_device(SILVER_LEAD);
> > + if (IS_ERR_OR_NULL(cpu_dev_silver))
> > + return PTR_ERR(cpu_dev_silver);
> > +
> > + cpu_dev_gold = get_cpu_device(SILVER_LEAD);
>
> s/SILVER/GOLD/ ?
Yes, you are right. This is already fixed in the respin.
>
> --
> Regards,
> Sudeep
^ permalink raw reply
* Re: [PATCH] cpufreq: Add Kryo CPU scaling driver
From: Sudeep Holla @ 2018-05-21 12:50 UTC (permalink / raw)
To: Ilia Lin, mturquette, sboyd, robh, mark.rutland, viresh.kumar, nm,
lgirdwood, broonie, andy.gross, david.brown, catalin.marinas,
will.deacon, rjw, linux-clk
Cc: Sudeep Holla, devicetree, linux-kernel, linux-pm, linux-arm-msm,
linux-soc, linux-arm-kernel, rnayak, amit.kucheria,
nicolas.dechesne, celster, tfinkel
In-Reply-To: <1526729701-8589-1-git-send-email-ilialin@codeaurora.org>
On 19/05/18 12:35, Ilia Lin wrote:
> In Certain QCOM SoCs like apq8096 and msm8996 that have KRYO processors,
> the CPU frequency subset and voltage value of each OPP varies
> based on the silicon variant in use. Qualcomm Process Voltage Scaling Tables
> defines the voltage and frequency value based on the msm-id in SMEM
> and speedbin blown in the efuse combination.
> The qcom-cpufreq-kryo driver reads the msm-id and efuse value from the SoC
> to provide the OPP framework with required information.
> This is used to determine the voltage and frequency value for each OPP of
> operating-points-v2 table when it is parsed by the OPP framework.
>
> Signed-off-by: Ilia Lin <ilialin@codeaurora.org>
> Acked-by: Viresh Kumar <viresh.kumar@linaro.org>
> ---
> drivers/cpufreq/Kconfig.arm | 10 +++
> drivers/cpufreq/Makefile | 1 +
> drivers/cpufreq/cpufreq-dt-platdev.c | 3 +
> drivers/cpufreq/qcom-cpufreq-kryo.c | 164 +++++++++++++++++++++++++++++++++++
> 4 files changed, 178 insertions(+)
> create mode 100644 drivers/cpufreq/qcom-cpufreq-kryo.c
>
[..]
> +
> +/*
> + * In Certain QCOM SoCs like apq8096 and msm8996 that have KRYO processors,
> + * the CPU frequency subset and voltage value of each OPP varies
> + * based on the silicon variant in use. Qualcomm Process Voltage Scaling Tables
> + * defines the voltage and frequency value based on the msm-id in SMEM
> + * and speedbin blown in the efuse combination.
> + * The qcom-cpufreq-kryo driver reads the msm-id and efuse value from the SoC
> + * to provide the OPP framework with required information.
> + * This is used to determine the voltage and frequency value for each OPP of
> + * operating-points-v2 table when it is parsed by the OPP framework.
> + */
> +
> +#include <linux/cpu.h>
> +#include <linux/err.h>
> +#include <linux/init.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/nvmem-consumer.h>
> +#include <linux/of.h>
> +#include <linux/platform_device.h>
> +#include <linux/pm_opp.h>
> +#include <linux/slab.h>
> +#include <linux/soc/qcom/smem.h>
> +
> +#define MSM_ID_SMEM 137
> +#define SILVER_LEAD 0
> +#define GOLD_LEAD 2
> +
So I gather form other emails, that these are physical cpu number(not
even unique identifier like MPIDR). Will this work on parts or platforms
that need to boot in GOLD LEAD cpus.
[...]
> +
> +static int __init qcom_cpufreq_kryo_driver_init(void)
> +{
> + struct device *cpu_dev_silver, *cpu_dev_gold;
> + struct opp_table *opp_silver, *opp_gold;
> + enum _msm8996_version msm8996_version;
> + struct nvmem_cell *speedbin_nvmem;
> + struct platform_device *pdev;
> + struct device_node *np;
> + u8 *speedbin;
> + u32 versions;
> + size_t len;
> + int ret;
> +
> + cpu_dev_silver = get_cpu_device(SILVER_LEAD);
> + if (IS_ERR_OR_NULL(cpu_dev_silver))
> + return PTR_ERR(cpu_dev_silver);
> +
> + cpu_dev_gold = get_cpu_device(SILVER_LEAD);
s/SILVER/GOLD/ ?
--
Regards,
Sudeep
^ permalink raw reply
* Re: [PATCH] cpufreq: Add Kryo CPU scaling driver
From: Russell King - ARM Linux @ 2018-05-21 12:41 UTC (permalink / raw)
To: ilialin
Cc: viresh.kumar, devicetree, linux-pm, linux-arm-msm, linux-kernel,
linux-soc, linux-clk, linux-arm-kernel
In-Reply-To: <000d01d3f100$294247e0$7bc6d7a0$@codeaurora.org>
On Mon, May 21, 2018 at 03:35:07PM +0300, ilialin@codeaurora.org wrote:
> There are 2 CPU clusters in the Kryo, CPU 0 and 1 are called Silver Cluster
> and CPU 2 and 3 - Gold Cluster. Each cluster has single clock. The clusters
> differ in terms of speed capabilities, computing power and power
> consumption. Therefore, I define separate OPP table for each cluster, and my
> driver will choose the appropriate OPP subset for each cluster.
> Lead refers to first CPU in the cluster.
Ah, that is really confusing. Lead can means many things. Maybe a
little more verbosity with the names such as SILVER_CLUSTER_LEAD_CPU
would help?
--
RMK's Patch system: http://www.armlinux.org.uk/developer/patches/
FTTC broadband for 0.8mile line in suburbia: sync at 8.8Mbps down 630kbps up
According to speedtest.net: 8.21Mbps down 510kbps up
^ permalink raw reply
* RE: [PATCH] cpufreq: Add Kryo CPU scaling driver
From: ilialin @ 2018-05-21 12:35 UTC (permalink / raw)
To: 'Russell King - ARM Linux'
Cc: viresh.kumar, devicetree, linux-pm, linux-arm-msm, linux-kernel,
linux-soc, linux-clk, linux-arm-kernel
In-Reply-To: <20180521121140.GO17671@n2100.armlinux.org.uk>
There are 2 CPU clusters in the Kryo, CPU 0 and 1 are called Silver Cluster
and CPU 2 and 3 - Gold Cluster. Each cluster has single clock. The clusters
differ in terms of speed capabilities, computing power and power
consumption. Therefore, I define separate OPP table for each cluster, and my
driver will choose the appropriate OPP subset for each cluster.
Lead refers to first CPU in the cluster.
> -----Original Message-----
> From: Russell King - ARM Linux <linux@armlinux.org.uk>
> Sent: Monday, May 21, 2018 15:12
> To: ilialin@codeaurora.org
> Cc: viresh.kumar@linaro.org; devicetree@vger.kernel.org; linux-
> pm@vger.kernel.org; linux-arm-msm@vger.kernel.org; linux-
> kernel@vger.kernel.org; linux-soc@vger.kernel.org; linux-
> clk@vger.kernel.org; linux-arm-kernel@lists.infradead.org
> Subject: Re: [PATCH] cpufreq: Add Kryo CPU scaling driver
>
> On Mon, May 21, 2018 at 02:05:41PM +0300, ilialin@codeaurora.org wrote:
> > You are right.
> > cpu_dev_silver != cpu_dev_gold, and I found this with my tests as well.
> > Thank you.
> >
> > > -----Original Message-----
> > > From: Russell King - ARM Linux <linux@armlinux.org.uk>
> > > Sent: Monday, May 21, 2018 13:54
> > > To: Ilia Lin <ilialin@codeaurora.org>
> > > Cc: viresh.kumar@linaro.org; devicetree@vger.kernel.org; linux-
> > > pm@vger.kernel.org; linux-arm-msm@vger.kernel.org; linux-
> > > kernel@vger.kernel.org; linux-soc@vger.kernel.org; linux-
> > > clk@vger.kernel.org; linux-arm-kernel@lists.infradead.org
> > > Subject: Re: [PATCH] cpufreq: Add Kryo CPU scaling driver
> > >
> > > On Mon, May 21, 2018 at 01:31:30PM +0300, Ilia Lin wrote:
> > > > +#define SILVER_LEAD 0
> > > > +#define GOLD_LEAD 2
> > >
> > > Okay, two different values here, but "GOLD_LEAD" appears unused.
> > >
> > > > + cpu_dev_silver = get_cpu_device(SILVER_LEAD);
> > > > + if (NULL == cpu_dev_silver)
> > > > + return -ENODEV;
> > > > +
> > > > + cpu_dev_gold = get_cpu_device(SILVER_LEAD);
> > > > + if (NULL == cpu_dev_gold)
> > > > + return -ENODEV;
> > >
> > > get_cpu_device() takes the logical CPU number. So the above gets
> > > CPU 0 each time, and so cpu_dev_silver == cpu_dev_gold here. So
> > > what's the point of the second get_cpu_device() ? If it's supposed to
be:
> > >
> > > cpu_dev_gold = get_cpu_device(GOLD_LEAD);
> > >
> > > That would get CPU 2, but in terms of these defines, it doesn't make
> > > that much sense. What exactly does "silver lead" and "gold lead"
> > > refer to in
> > these
> > > definitions?
>
> I think you still need to explain this.
>
> --
> RMK's Patch system: http://www.armlinux.org.uk/developer/patches/
> FTTC broadband for 0.8mile line in suburbia: sync at 8.8Mbps down 630kbps
> up According to speedtest.net: 8.21Mbps down 510kbps up
^ permalink raw reply
* Re: [PATCH] cpufreq: Add Kryo CPU scaling driver
From: Russell King - ARM Linux @ 2018-05-21 12:11 UTC (permalink / raw)
To: ilialin
Cc: viresh.kumar, devicetree, linux-pm, linux-arm-msm, linux-kernel,
linux-soc, linux-clk, linux-arm-kernel
In-Reply-To: <000b01d3f0f3$aa961cc0$ffc25640$@codeaurora.org>
On Mon, May 21, 2018 at 02:05:41PM +0300, ilialin@codeaurora.org wrote:
> You are right.
> cpu_dev_silver != cpu_dev_gold, and I found this with my tests as well.
> Thank you.
>
> > -----Original Message-----
> > From: Russell King - ARM Linux <linux@armlinux.org.uk>
> > Sent: Monday, May 21, 2018 13:54
> > To: Ilia Lin <ilialin@codeaurora.org>
> > Cc: viresh.kumar@linaro.org; devicetree@vger.kernel.org; linux-
> > pm@vger.kernel.org; linux-arm-msm@vger.kernel.org; linux-
> > kernel@vger.kernel.org; linux-soc@vger.kernel.org; linux-
> > clk@vger.kernel.org; linux-arm-kernel@lists.infradead.org
> > Subject: Re: [PATCH] cpufreq: Add Kryo CPU scaling driver
> >
> > On Mon, May 21, 2018 at 01:31:30PM +0300, Ilia Lin wrote:
> > > +#define SILVER_LEAD 0
> > > +#define GOLD_LEAD 2
> >
> > Okay, two different values here, but "GOLD_LEAD" appears unused.
> >
> > > + cpu_dev_silver = get_cpu_device(SILVER_LEAD);
> > > + if (NULL == cpu_dev_silver)
> > > + return -ENODEV;
> > > +
> > > + cpu_dev_gold = get_cpu_device(SILVER_LEAD);
> > > + if (NULL == cpu_dev_gold)
> > > + return -ENODEV;
> >
> > get_cpu_device() takes the logical CPU number. So the above gets CPU 0
> > each time, and so cpu_dev_silver == cpu_dev_gold here. So what's the
> > point of the second get_cpu_device() ? If it's supposed to be:
> >
> > cpu_dev_gold = get_cpu_device(GOLD_LEAD);
> >
> > That would get CPU 2, but in terms of these defines, it doesn't make that
> > much sense. What exactly does "silver lead" and "gold lead" refer to in
> these
> > definitions?
I think you still need to explain this.
--
RMK's Patch system: http://www.armlinux.org.uk/developer/patches/
FTTC broadband for 0.8mile line in suburbia: sync at 8.8Mbps down 630kbps up
According to speedtest.net: 8.21Mbps down 510kbps up
^ permalink raw reply
* [PATCH 19/33] thermal: db8500: use match_string() helper
From: Yisheng Xie @ 2018-05-21 11:57 UTC (permalink / raw)
To: linux-kernel; +Cc: Yisheng Xie, Zhang Rui, Eduardo Valentin, linux-pm
In-Reply-To: <1526903890-35761-1-git-send-email-xieyisheng1@huawei.com>
match_string() returns the index of an array for a matching string,
which can be used intead of open coded variant.
Cc: Zhang Rui <rui.zhang@intel.com>
Cc: Eduardo Valentin <edubezval@gmail.com>
Cc: linux-pm@vger.kernel.org
Signed-off-by: Yisheng Xie <xieyisheng1@huawei.com>
---
drivers/thermal/db8500_thermal.c | 8 +++-----
1 file changed, 3 insertions(+), 5 deletions(-)
diff --git a/drivers/thermal/db8500_thermal.c b/drivers/thermal/db8500_thermal.c
index f491faf..dd83614 100644
--- a/drivers/thermal/db8500_thermal.c
+++ b/drivers/thermal/db8500_thermal.c
@@ -50,12 +50,10 @@ static int db8500_thermal_match_cdev(struct thermal_cooling_device *cdev,
if (!strlen(cdev->type))
return -EINVAL;
- for (i = 0; i < COOLING_DEV_MAX; i++) {
- if (!strcmp(trip_point->cdev_name[i], cdev->type))
- return 0;
- }
+ i = match_string((const char **)trip_point->cdev_name,
+ COOLING_DEV_MAX, cdev->type);
- return -ENODEV;
+ return (i < 0) ? -ENODEV : 0;
}
/* Callback to bind cooling device to thermal zone */
--
1.7.12.4
^ permalink raw reply related
* [PATCH 18/33] power: supply: use match_string() helper
From: Yisheng Xie @ 2018-05-21 11:57 UTC (permalink / raw)
To: linux-kernel; +Cc: Yisheng Xie, Sebastian Reichel, linux-pm
In-Reply-To: <1526903890-35761-1-git-send-email-xieyisheng1@huawei.com>
match_string() returns the index of an array for a matching string,
which can be used intead of open coded variant.
Cc: Sebastian Reichel <sre@kernel.org>
Cc: linux-pm@vger.kernel.org
Signed-off-by: Yisheng Xie <xieyisheng1@huawei.com>
---
drivers/power/supply/power_supply_core.c | 16 ++++++----------
1 file changed, 6 insertions(+), 10 deletions(-)
diff --git a/drivers/power/supply/power_supply_core.c b/drivers/power/supply/power_supply_core.c
index feac7b0..84da3a2 100644
--- a/drivers/power/supply/power_supply_core.c
+++ b/drivers/power/supply/power_supply_core.c
@@ -36,8 +36,6 @@
static bool __power_supply_is_supplied_by(struct power_supply *supplier,
struct power_supply *supply)
{
- int i;
-
if (!supply->supplied_from && !supplier->supplied_to)
return false;
@@ -45,18 +43,16 @@ static bool __power_supply_is_supplied_by(struct power_supply *supplier,
if (supply->supplied_from) {
if (!supplier->desc->name)
return false;
- for (i = 0; i < supply->num_supplies; i++)
- if (!strcmp(supplier->desc->name, supply->supplied_from[i]))
- return true;
+ return match_string((const char **)supply->supplied_from,
+ supply->num_supplies,
+ supplier->desc->name) >= 0;
} else {
if (!supply->desc->name)
return false;
- for (i = 0; i < supplier->num_supplicants; i++)
- if (!strcmp(supplier->supplied_to[i], supply->desc->name))
- return true;
+ return match_string((const char **)supplier->supplied_to,
+ supplier->num_supplicants,
+ supply->desc->name) >= 0;
}
-
- return false;
}
static int __power_supply_changed_work(struct device *dev, void *data)
--
1.7.12.4
^ permalink raw reply related
* [PATCH 15/33] cpufreq: intel_pstate: use match_string() helper
From: Yisheng Xie @ 2018-05-21 11:57 UTC (permalink / raw)
To: linux-kernel
Cc: Yisheng Xie, Srinivas Pandruvada, Len Brown, Rafael J. Wysocki,
Viresh Kumar, linux-pm
In-Reply-To: <1526903890-35761-1-git-send-email-xieyisheng1@huawei.com>
match_string() returns the index of an array for a matching string,
which can be used intead of open coded variant.
Cc: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>
Cc: Len Brown <lenb@kernel.org>
Cc: "Rafael J. Wysocki" <rjw@rjwysocki.net>
Cc: Viresh Kumar <viresh.kumar@linaro.org>
Cc: linux-pm@vger.kernel.org
Signed-off-by: Yisheng Xie <xieyisheng1@huawei.com>
---
drivers/cpufreq/intel_pstate.c | 15 ++++++---------
1 file changed, 6 insertions(+), 9 deletions(-)
diff --git a/drivers/cpufreq/intel_pstate.c b/drivers/cpufreq/intel_pstate.c
index 17e566af..d701e26 100644
--- a/drivers/cpufreq/intel_pstate.c
+++ b/drivers/cpufreq/intel_pstate.c
@@ -645,21 +645,18 @@ static ssize_t store_energy_performance_preference(
{
struct cpudata *cpu_data = all_cpu_data[policy->cpu];
char str_preference[21];
- int ret, i = 0;
+ int ret;
ret = sscanf(buf, "%20s", str_preference);
if (ret != 1)
return -EINVAL;
- while (energy_perf_strings[i] != NULL) {
- if (!strcmp(str_preference, energy_perf_strings[i])) {
- intel_pstate_set_energy_pref_index(cpu_data, i);
- return count;
- }
- ++i;
- }
+ ret = match_string(energy_perf_strings, -1, str_preference);
+ if (ret < 0)
+ return ret;
- return -EINVAL;
+ intel_pstate_set_energy_pref_index(cpu_data, ret);
+ return count;
}
static ssize_t show_energy_performance_preference(
--
1.7.12.4
^ permalink raw reply related
* [PATCH v9 15/15] dt: qcom: Add SAW regulator for 8x96 CPUs
From: Ilia Lin @ 2018-05-21 11:25 UTC (permalink / raw)
To: mturquette, sboyd, robh, mark.rutland, viresh.kumar, nm,
lgirdwood, broonie, andy.gross, david.brown, catalin.marinas,
will.deacon, rjw, linux-clk
Cc: devicetree, linux-kernel, linux-pm, linux-arm-msm, linux-soc,
linux-arm-kernel, rnayak, ilialin, amit.kucheria,
nicolas.dechesne, celster, tfinkel
In-Reply-To: <1526901932-9514-1-git-send-email-ilialin@codeaurora.org>
1. Add syscon node for the SAW CPU registers
2. Add SAW regulators gang definition for s8-s11
3. Add voltages to the OPP tables
4. Add the s11 SAW regulator as CPU regulator
Signed-off-by: Ilia Lin <ilialin@codeaurora.org>
Acked-by: Viresh Kumar <viresh.kumar@linaro.org>
---
arch/arm64/boot/dts/qcom/msm8996.dtsi | 119 ++++++++++++++++++++++++++++++++++
1 file changed, 119 insertions(+)
diff --git a/arch/arm64/boot/dts/qcom/msm8996.dtsi b/arch/arm64/boot/dts/qcom/msm8996.dtsi
index d96a112..871bfac 100644
--- a/arch/arm64/boot/dts/qcom/msm8996.dtsi
+++ b/arch/arm64/boot/dts/qcom/msm8996.dtsi
@@ -8,6 +8,7 @@
#include <dt-bindings/clock/qcom,mmcc-msm8996.h>
#include <dt-bindings/clock/qcom,rpmcc.h>
#include <dt-bindings/thermal/thermal.h>
+#include <dt-bindings/spmi/spmi.h>
/ {
model = "Qualcomm Technologies, Inc. MSM8996";
@@ -92,6 +93,7 @@
reg = <0x0 0x0>;
enable-method = "psci";
clocks = <&kryocc 0>;
+ cpu-supply = <&pm8994_s11_saw>;
operating-points-v2 = <&cluster0_opp>;
#cooling-cells = <2>;
next-level-cache = <&L2_0>;
@@ -107,6 +109,7 @@
reg = <0x0 0x1>;
enable-method = "psci";
clocks = <&kryocc 0>;
+ cpu-supply = <&pm8994_s11_saw>;
operating-points-v2 = <&cluster0_opp>;
#cooling-cells = <2>;
next-level-cache = <&L2_0>;
@@ -118,6 +121,7 @@
reg = <0x0 0x100>;
enable-method = "psci";
clocks = <&kryocc 1>;
+ cpu-supply = <&pm8994_s11_saw>;
operating-points-v2 = <&cluster1_opp>;
#cooling-cells = <2>;
next-level-cache = <&L2_1>;
@@ -133,6 +137,7 @@
reg = <0x0 0x101>;
enable-method = "psci";
clocks = <&kryocc 1>;
+ cpu-supply = <&pm8994_s11_saw>;
operating-points-v2 = <&cluster1_opp>;
#cooling-cells = <2>;
next-level-cache = <&L2_1>;
@@ -169,171 +174,205 @@
opp-307200000 {
opp-hz = /bits/ 64 <307200000>;
+ opp-microvolt = <905000 905000 1140000>;
opp-supported-hw = <0x77>;
clock-latency-ns = <200000>;
};
opp-384000000 {
opp-hz = /bits/ 64 <384000000>;
+ opp-microvolt = <905000 905000 1140000>;
opp-supported-hw = <0x70>;
clock-latency-ns = <200000>;
};
opp-422400000 {
opp-hz = /bits/ 64 <422400000>;
+ opp-microvolt = <905000 905000 1140000>;
opp-supported-hw = <0x7>;
clock-latency-ns = <200000>;
};
opp-460800000 {
opp-hz = /bits/ 64 <460800000>;
+ opp-microvolt = <905000 905000 1140000>;
opp-supported-hw = <0x70>;
clock-latency-ns = <200000>;
};
opp-480000000 {
opp-hz = /bits/ 64 <480000000>;
+ opp-microvolt = <905000 905000 1140000>;
opp-supported-hw = <0x7>;
clock-latency-ns = <200000>;
};
opp-537600000 {
opp-hz = /bits/ 64 <537600000>;
+ opp-microvolt = <905000 905000 1140000>;
opp-supported-hw = <0x70>;
clock-latency-ns = <200000>;
};
opp-556800000 {
opp-hz = /bits/ 64 <556800000>;
+ opp-microvolt = <905000 905000 1140000>;
opp-supported-hw = <0x7>;
clock-latency-ns = <200000>;
};
opp-614400000 {
opp-hz = /bits/ 64 <614400000>;
+ opp-microvolt = <905000 905000 1140000>;
opp-supported-hw = <0x70>;
clock-latency-ns = <200000>;
};
opp-652800000 {
opp-hz = /bits/ 64 <652800000>;
+ opp-microvolt = <905000 905000 1140000>;
opp-supported-hw = <0x7>;
clock-latency-ns = <200000>;
};
opp-691200000 {
opp-hz = /bits/ 64 <691200000>;
+ opp-microvolt = <905000 905000 1140000>;
opp-supported-hw = <0x70>;
clock-latency-ns = <200000>;
};
opp-729600000 {
opp-hz = /bits/ 64 <729600000>;
+ opp-microvolt = <905000 905000 1140000>;
opp-supported-hw = <0x7>;
clock-latency-ns = <200000>;
};
opp-768000000 {
opp-hz = /bits/ 64 <768000000>;
+ opp-microvolt = <905000 905000 1140000>;
opp-supported-hw = <0x70>;
clock-latency-ns = <200000>;
};
opp-844800000 {
opp-hz = /bits/ 64 <844800000>;
+ opp-microvolt = <905000 905000 1140000>;
opp-supported-hw = <0x77>;
clock-latency-ns = <200000>;
};
opp-902400000 {
opp-hz = /bits/ 64 <902400000>;
+ opp-microvolt = <905000 905000 1140000>;
opp-supported-hw = <0x70>;
clock-latency-ns = <200000>;
};
opp-960000000 {
opp-hz = /bits/ 64 <960000000>;
+ opp-microvolt = <905000 905000 1140000>;
opp-supported-hw = <0x7>;
clock-latency-ns = <200000>;
};
opp-979200000 {
opp-hz = /bits/ 64 <979200000>;
+ opp-microvolt = <905000 905000 1140000>;
opp-supported-hw = <0x70>;
clock-latency-ns = <200000>;
};
opp-1036800000 {
opp-hz = /bits/ 64 <1036800000>;
+ opp-microvolt = <905000 905000 1140000>;
opp-supported-hw = <0x7>;
clock-latency-ns = <200000>;
};
opp-1056000000 {
opp-hz = /bits/ 64 <1056000000>;
+ opp-microvolt = <905000 905000 1140000>;
opp-supported-hw = <0x70>;
clock-latency-ns = <200000>;
};
opp-1113600000 {
opp-hz = /bits/ 64 <1113600000>;
+ opp-microvolt = <905000 905000 1140000>;
opp-supported-hw = <0x7>;
clock-latency-ns = <200000>;
};
opp-1132800000 {
opp-hz = /bits/ 64 <1132800000>;
+ opp-microvolt = <905000 905000 1140000>;
opp-supported-hw = <0x70>;
clock-latency-ns = <200000>;
};
opp-1190400000 {
opp-hz = /bits/ 64 <1190400000>;
+ opp-microvolt = <905000 905000 1140000>;
opp-supported-hw = <0x7>;
clock-latency-ns = <200000>;
};
opp-1209600000 {
opp-hz = /bits/ 64 <1209600000>;
+ opp-microvolt = <905000 905000 1140000>;
opp-supported-hw = <0x70>;
clock-latency-ns = <200000>;
};
opp-1228800000 {
opp-hz = /bits/ 64 <1228800000>;
+ opp-microvolt = <905000 905000 1140000>;
opp-supported-hw = <0x7>;
clock-latency-ns = <200000>;
};
opp-1286400000 {
opp-hz = /bits/ 64 <1286400000>;
+ opp-microvolt = <1140000 905000 1140000>;
opp-supported-hw = <0x70>;
clock-latency-ns = <200000>;
};
opp-1324800000 {
opp-hz = /bits/ 64 <1324800000>;
+ opp-microvolt = <1140000 905000 1140000>;
opp-supported-hw = <0x5>;
clock-latency-ns = <200000>;
};
opp-1363200000 {
opp-hz = /bits/ 64 <1363200000>;
+ opp-microvolt = <1140000 905000 1140000>;
opp-supported-hw = <0x72>;
clock-latency-ns = <200000>;
};
opp-1401600000 {
opp-hz = /bits/ 64 <1401600000>;
+ opp-microvolt = <1140000 905000 1140000>;
opp-supported-hw = <0x5>;
clock-latency-ns = <200000>;
};
opp-1440000000 {
opp-hz = /bits/ 64 <1440000000>;
+ opp-microvolt = <1140000 905000 1140000>;
opp-supported-hw = <0x70>;
clock-latency-ns = <200000>;
};
opp-1478400000 {
opp-hz = /bits/ 64 <1478400000>;
+ opp-microvolt = <1140000 905000 1140000>;
opp-supported-hw = <0x1>;
clock-latency-ns = <200000>;
};
opp-1497600000 {
opp-hz = /bits/ 64 <1497600000>;
+ opp-microvolt = <1140000 905000 1140000>;
opp-supported-hw = <0x4>;
clock-latency-ns = <200000>;
};
opp-1516800000 {
opp-hz = /bits/ 64 <1516800000>;
+ opp-microvolt = <1140000 905000 1140000>;
opp-supported-hw = <0x70>;
clock-latency-ns = <200000>;
};
opp-1593600000 {
opp-hz = /bits/ 64 <1593600000>;
+ opp-microvolt = <1140000 905000 1140000>;
opp-supported-hw = <0x71>;
clock-latency-ns = <200000>;
};
opp-1996800000 {
opp-hz = /bits/ 64 <1996800000>;
+ opp-microvolt = <1140000 905000 1140000>;
opp-supported-hw = <0x20>;
clock-latency-ns = <200000>;
};
opp-2188800000 {
opp-hz = /bits/ 64 <2188800000>;
+ opp-microvolt = <1140000 905000 1140000>;
opp-supported-hw = <0x10>;
clock-latency-ns = <200000>;
};
@@ -346,251 +385,301 @@
opp-307200000 {
opp-hz = /bits/ 64 <307200000>;
+ opp-microvolt = <905000 905000 1140000>;
opp-supported-hw = <0x77>;
clock-latency-ns = <200000>;
};
opp-384000000 {
opp-hz = /bits/ 64 <384000000>;
+ opp-microvolt = <905000 905000 1140000>;
opp-supported-hw = <0x70>;
clock-latency-ns = <200000>;
};
opp-403200000 {
opp-hz = /bits/ 64 <403200000>;
+ opp-microvolt = <905000 905000 1140000>;
opp-supported-hw = <0x7>;
clock-latency-ns = <200000>;
};
opp-460800000 {
opp-hz = /bits/ 64 <460800000>;
+ opp-microvolt = <905000 905000 1140000>;
opp-supported-hw = <0x70>;
clock-latency-ns = <200000>;
};
opp-480000000 {
opp-hz = /bits/ 64 <480000000>;
+ opp-microvolt = <905000 905000 1140000>;
opp-supported-hw = <0x7>;
clock-latency-ns = <200000>;
};
opp-537600000 {
opp-hz = /bits/ 64 <537600000>;
+ opp-microvolt = <905000 905000 1140000>;
opp-supported-hw = <0x70>;
clock-latency-ns = <200000>;
};
opp-556800000 {
opp-hz = /bits/ 64 <556800000>;
+ opp-microvolt = <905000 905000 1140000>;
opp-supported-hw = <0x7>;
clock-latency-ns = <200000>;
};
opp-614400000 {
opp-hz = /bits/ 64 <614400000>;
+ opp-microvolt = <905000 905000 1140000>;
opp-supported-hw = <0x70>;
clock-latency-ns = <200000>;
};
opp-652800000 {
opp-hz = /bits/ 64 <652800000>;
+ opp-microvolt = <905000 905000 1140000>;
opp-supported-hw = <0x7>;
clock-latency-ns = <200000>;
};
opp-691200000 {
opp-hz = /bits/ 64 <691200000>;
+ opp-microvolt = <905000 905000 1140000>;
opp-supported-hw = <0x70>;
clock-latency-ns = <200000>;
};
opp-729600000 {
opp-hz = /bits/ 64 <729600000>;
+ opp-microvolt = <905000 905000 1140000>;
opp-supported-hw = <0x7>;
clock-latency-ns = <200000>;
};
opp-748800000 {
opp-hz = /bits/ 64 <748800000>;
+ opp-microvolt = <905000 905000 1140000>;
opp-supported-hw = <0x70>;
clock-latency-ns = <200000>;
};
opp-806400000 {
opp-hz = /bits/ 64 <806400000>;
+ opp-microvolt = <905000 905000 1140000>;
opp-supported-hw = <0x7>;
clock-latency-ns = <200000>;
};
opp-825600000 {
opp-hz = /bits/ 64 <825600000>;
+ opp-microvolt = <905000 905000 1140000>;
opp-supported-hw = <0x70>;
clock-latency-ns = <200000>;
};
opp-883200000 {
opp-hz = /bits/ 64 <883200000>;
+ opp-microvolt = <905000 905000 1140000>;
opp-supported-hw = <0x7>;
clock-latency-ns = <200000>;
};
opp-902400000 {
opp-hz = /bits/ 64 <902400000>;
+ opp-microvolt = <905000 905000 1140000>;
opp-supported-hw = <0x70>;
clock-latency-ns = <200000>;
};
opp-940800000 {
opp-hz = /bits/ 64 <940800000>;
+ opp-microvolt = <905000 905000 1140000>;
opp-supported-hw = <0x7>;
clock-latency-ns = <200000>;
};
opp-979200000 {
opp-hz = /bits/ 64 <979200000>;
+ opp-microvolt = <905000 905000 1140000>;
opp-supported-hw = <0x70>;
clock-latency-ns = <200000>;
};
opp-1036800000 {
opp-hz = /bits/ 64 <1036800000>;
+ opp-microvolt = <905000 905000 1140000>;
opp-supported-hw = <0x7>;
clock-latency-ns = <200000>;
};
opp-1056000000 {
opp-hz = /bits/ 64 <1056000000>;
+ opp-microvolt = <905000 905000 1140000>;
opp-supported-hw = <0x70>;
clock-latency-ns = <200000>;
};
opp-1113600000 {
opp-hz = /bits/ 64 <1113600000>;
+ opp-microvolt = <905000 905000 1140000>;
opp-supported-hw = <0x7>;
clock-latency-ns = <200000>;
};
opp-1132800000 {
opp-hz = /bits/ 64 <1132800000>;
+ opp-microvolt = <905000 905000 1140000>;
opp-supported-hw = <0x70>;
clock-latency-ns = <200000>;
};
opp-1190400000 {
opp-hz = /bits/ 64 <1190400000>;
+ opp-microvolt = <905000 905000 1140000>;
opp-supported-hw = <0x7>;
clock-latency-ns = <200000>;
};
opp-1209600000 {
opp-hz = /bits/ 64 <1209600000>;
+ opp-microvolt = <905000 905000 1140000>;
opp-supported-hw = <0x70>;
clock-latency-ns = <200000>;
};
opp-1248000000 {
opp-hz = /bits/ 64 <1248000000>;
+ opp-microvolt = <905000 905000 1140000>;
opp-supported-hw = <0x7>;
clock-latency-ns = <200000>;
};
opp-1286400000 {
opp-hz = /bits/ 64 <1286400000>;
+ opp-microvolt = <905000 905000 1140000>;
opp-supported-hw = <0x70>;
clock-latency-ns = <200000>;
};
opp-1324800000 {
opp-hz = /bits/ 64 <1324800000>;
+ opp-microvolt = <1140000 905000 1140000>;
opp-supported-hw = <0x7>;
clock-latency-ns = <200000>;
};
opp-1363200000 {
opp-hz = /bits/ 64 <1363200000>;
+ opp-microvolt = <1140000 905000 1140000>;
opp-supported-hw = <0x70>;
clock-latency-ns = <200000>;
};
opp-1401600000 {
opp-hz = /bits/ 64 <1401600000>;
+ opp-microvolt = <1140000 905000 1140000>;
opp-supported-hw = <0x7>;
clock-latency-ns = <200000>;
};
opp-1440000000 {
opp-hz = /bits/ 64 <1440000000>;
+ opp-microvolt = <1140000 905000 1140000>;
opp-supported-hw = <0x70>;
clock-latency-ns = <200000>;
};
opp-1478400000 {
opp-hz = /bits/ 64 <1478400000>;
+ opp-microvolt = <1140000 905000 1140000>;
opp-supported-hw = <0x7>;
clock-latency-ns = <200000>;
};
opp-1516800000 {
opp-hz = /bits/ 64 <1516800000>;
+ opp-microvolt = <1140000 905000 1140000>;
opp-supported-hw = <0x70>;
clock-latency-ns = <200000>;
};
opp-1555200000 {
opp-hz = /bits/ 64 <1555200000>;
+ opp-microvolt = <1140000 905000 1140000>;
opp-supported-hw = <0x7>;
clock-latency-ns = <200000>;
};
opp-1593600000 {
opp-hz = /bits/ 64 <1593600000>;
+ opp-microvolt = <1140000 905000 1140000>;
opp-supported-hw = <0x70>;
clock-latency-ns = <200000>;
};
opp-1632000000 {
opp-hz = /bits/ 64 <1632000000>;
+ opp-microvolt = <1140000 905000 1140000>;
opp-supported-hw = <0x7>;
clock-latency-ns = <200000>;
};
opp-1670400000 {
opp-hz = /bits/ 64 <1670400000>;
+ opp-microvolt = <1140000 905000 1140000>;
opp-supported-hw = <0x70>;
clock-latency-ns = <200000>;
};
opp-1708800000 {
opp-hz = /bits/ 64 <1708800000>;
+ opp-microvolt = <1140000 905000 1140000>;
opp-supported-hw = <0x7>;
clock-latency-ns = <200000>;
};
opp-1747200000 {
opp-hz = /bits/ 64 <1747200000>;
+ opp-microvolt = <1140000 905000 1140000>;
opp-supported-hw = <0x70>;
clock-latency-ns = <200000>;
};
opp-1785600000 {
opp-hz = /bits/ 64 <1785600000>;
+ opp-microvolt = <1140000 905000 1140000>;
opp-supported-hw = <0x7>;
clock-latency-ns = <200000>;
};
opp-1804800000 {
opp-hz = /bits/ 64 <1804800000>;
+ opp-microvolt = <1140000 905000 1140000>;
opp-supported-hw = <0x6>;
clock-latency-ns = <200000>;
};
opp-1824000000 {
opp-hz = /bits/ 64 <1824000000>;
+ opp-microvolt = <1140000 905000 1140000>;
opp-supported-hw = <0x71>;
clock-latency-ns = <200000>;
};
opp-1900800000 {
opp-hz = /bits/ 64 <1900800000>;
+ opp-microvolt = <1140000 905000 1140000>;
opp-supported-hw = <0x74>;
clock-latency-ns = <200000>;
};
opp-1920000000 {
opp-hz = /bits/ 64 <1920000000>;
+ opp-microvolt = <1140000 905000 1140000>;
opp-supported-hw = <0x1>;
clock-latency-ns = <200000>;
};
opp-1977600000 {
opp-hz = /bits/ 64 <1977600000>;
+ opp-microvolt = <1140000 905000 1140000>;
opp-supported-hw = <0x30>;
clock-latency-ns = <200000>;
};
opp-1996800000 {
opp-hz = /bits/ 64 <1996800000>;
+ opp-microvolt = <1140000 905000 1140000>;
opp-supported-hw = <0x1>;
clock-latency-ns = <200000>;
};
opp-2054400000 {
opp-hz = /bits/ 64 <2054400000>;
+ opp-microvolt = <1140000 905000 1140000>;
opp-supported-hw = <0x30>;
clock-latency-ns = <200000>;
};
opp-2073600000 {
opp-hz = /bits/ 64 <2073600000>;
+ opp-microvolt = <1140000 905000 1140000>;
opp-supported-hw = <0x1>;
clock-latency-ns = <200000>;
};
opp-2150400000 {
opp-hz = /bits/ 64 <2150400000>;
+ opp-microvolt = <1140000 905000 1140000>;
opp-supported-hw = <0x31>;
clock-latency-ns = <200000>;
};
opp-2246400000 {
opp-hz = /bits/ 64 <2246400000>;
+ opp-microvolt = <1140000 905000 1140000>;
opp-supported-hw = <0x10>;
clock-latency-ns = <200000>;
};
opp-2342400000 {
opp-hz = /bits/ 64 <2342400000>;
+ opp-microvolt = <1140000 905000 1140000>;
opp-supported-hw = <0x10>;
clock-latency-ns = <200000>;
};
@@ -908,6 +997,11 @@
#mbox-cells = <1>;
};
+ saw3: syscon@9A10000 {
+ compatible = "syscon";
+ reg = <0x9A10000 0x1000>;
+ };
+
gcc: clock-controller@300000 {
compatible = "qcom,gcc-msm8996";
#clock-cells = <1>;
@@ -1134,6 +1228,31 @@
#size-cells = <0>;
interrupt-controller;
#interrupt-cells = <4>;
+ pmic@1 {
+ compatible = "qcom,pm8994", "qcom,spmi-pmic";
+ reg = <0x1 SPMI_USID>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ spm-regulators {
+ compatible = "qcom,pm8994-regulators";
+ qcom,saw-reg = <&saw3>;
+ s8 {
+ qcom,saw-slave;
+ };
+ s9 {
+ qcom,saw-slave;
+ };
+ s10 {
+ qcom,saw-slave;
+ };
+ pm8994_s11_saw: s11 {
+ qcom,saw-leader;
+ regulator-always-on;
+ regulator-min-microvolt = <905000>;
+ regulator-max-microvolt = <1140000>;
+ };
+ };
+ };
};
mmcc: clock-controller@8c0000 {
--
1.9.1
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox