Skip to content

Commit

Permalink
Merge branches 'pm-core', 'pm-sleep', 'powercap', 'pm-domains' and 'p…
Browse files Browse the repository at this point in the history
…m-em'

Merge core device power management changes for v5.20-rc1:

 - Extend support for wakeirq to callback wrappers used during system
   suspend and resume (Ulf Hansson).

 - Defer waiting for device probe before loading a hibernation image
   till the first actual device access to avoid possible deadlocks
   reported by syzbot (Tetsuo Handa).

 - Unify device_init_wakeup() for PM_SLEEP and !PM_SLEEP (Bjorn
   Helgaas).

 - Add Raptor Lake-P to the list of processors supported by the Intel
   RAPL driver (George D Sworo).

 - Add Alder Lake-N and Raptor Lake-P to the list of processors for
   which Power Limit4 is supported in the Intel RAPL driver (Sumeet
   Pawnikar).

 - Make pm_genpd_remove() check genpd_debugfs_dir against NULL before
   attempting to remove it (Hsin-Yi Wang).

 - Change the Energy Model code to represent power in micro-Watts and
   adjust its users accordingly (Lukasz Luba).

* pm-core:
  PM: runtime: Extend support for wakeirq for force_suspend|resume

* pm-sleep:
  PM: hibernate: defer device probing when resuming from hibernation
  PM: wakeup: Unify device_init_wakeup() for PM_SLEEP and !PM_SLEEP

* powercap:
  powercap: RAPL: Add Power Limit4 support for Alder Lake-N and Raptor Lake-P
  powercap: intel_rapl: Add support for RAPTORLAKE_P

* pm-domains:
  PM: domains: Ensure genpd_debugfs_dir exists before remove

* pm-em:
  cpufreq: scmi: Support the power scale in micro-Watts in SCMI v3.1
  firmware: arm_scmi: Get detailed power scale from perf
  Documentation: EM: Switch to micro-Watts scale
  PM: EM: convert power field to micro-Watts precision and align drivers
  • Loading branch information
Rafael J. Wysocki committed Jul 29, 2022
6 parents 82b6c2e + c46a0d5 + 8386c41 + b08b95c + 37101d3 + f3ac888 commit 954a83f
Show file tree
Hide file tree
Showing 18 changed files with 179 additions and 99 deletions.
14 changes: 7 additions & 7 deletions Documentation/power/energy-model.rst
Original file line number Diff line number Diff line change
Expand Up @@ -20,20 +20,20 @@ possible source of information on its own, the EM framework intervenes as an
abstraction layer which standardizes the format of power cost tables in the
kernel, hence enabling to avoid redundant work.

The power values might be expressed in milli-Watts or in an 'abstract scale'.
The power values might be expressed in micro-Watts or in an 'abstract scale'.
Multiple subsystems might use the EM and it is up to the system integrator to
check that the requirements for the power value scale types are met. An example
can be found in the Energy-Aware Scheduler documentation
Documentation/scheduler/sched-energy.rst. For some subsystems like thermal or
powercap power values expressed in an 'abstract scale' might cause issues.
These subsystems are more interested in estimation of power used in the past,
thus the real milli-Watts might be needed. An example of these requirements can
thus the real micro-Watts might be needed. An example of these requirements can
be found in the Intelligent Power Allocation in
Documentation/driver-api/thermal/power_allocator.rst.
Kernel subsystems might implement automatic detection to check whether EM
registered devices have inconsistent scale (based on EM internal flag).
Important thing to keep in mind is that when the power values are expressed in
an 'abstract scale' deriving real energy in milli-Joules would not be possible.
an 'abstract scale' deriving real energy in micro-Joules would not be possible.

The figure below depicts an example of drivers (Arm-specific here, but the
approach is applicable to any architecture) providing power costs to the EM
Expand Down Expand Up @@ -98,18 +98,18 @@ Drivers are expected to register performance domains into the EM framework by
calling the following API::

int em_dev_register_perf_domain(struct device *dev, unsigned int nr_states,
struct em_data_callback *cb, cpumask_t *cpus, bool milliwatts);
struct em_data_callback *cb, cpumask_t *cpus, bool microwatts);

Drivers must provide a callback function returning <frequency, power> tuples
for each performance state. The callback function provided by the driver is free
to fetch data from any relevant location (DT, firmware, ...), and by any mean
deemed necessary. Only for CPU devices, drivers must specify the CPUs of the
performance domains using cpumask. For other devices than CPUs the last
argument must be set to NULL.
The last argument 'milliwatts' is important to set with correct value. Kernel
The last argument 'microwatts' is important to set with correct value. Kernel
subsystems which use EM might rely on this flag to check if all EM devices use
the same scale. If there are different scales, these subsystems might decide
to: return warning/error, stop working or panic.
to return warning/error, stop working or panic.
See Section 3. for an example of driver implementing this
callback, or Section 2.4 for further documentation on this API

Expand Down Expand Up @@ -137,7 +137,7 @@ The .get_cost() allows to provide the 'cost' values which reflect the
efficiency of the CPUs. This would allow to provide EAS information which
has different relation than what would be forced by the EM internal
formulas calculating 'cost' values. To register an EM for such platform, the
driver must set the flag 'milliwatts' to 0, provide .get_power() callback
driver must set the flag 'microwatts' to 0, provide .get_power() callback
and provide .get_cost() callback. The EM framework would handle such platform
properly during registration. A flag EM_PERF_DOMAIN_ARTIFICIAL is set for such
platform. Special care should be taken by other frameworks which are using EM
Expand Down
3 changes: 3 additions & 0 deletions drivers/base/power/domain.c
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,9 @@ static void genpd_debug_remove(struct generic_pm_domain *genpd)
{
struct dentry *d;

if (!genpd_debugfs_dir)
return;

d = debugfs_lookup(genpd->name, genpd_debugfs_dir);
debugfs_remove(d);
}
Expand Down
6 changes: 6 additions & 0 deletions drivers/base/power/runtime.c
Original file line number Diff line number Diff line change
Expand Up @@ -1862,10 +1862,13 @@ int pm_runtime_force_suspend(struct device *dev)

callback = RPM_GET_CALLBACK(dev, runtime_suspend);

dev_pm_enable_wake_irq_check(dev, true);
ret = callback ? callback(dev) : 0;
if (ret)
goto err;

dev_pm_enable_wake_irq_complete(dev);

/*
* If the device can stay in suspend after the system-wide transition
* to the working state that will follow, drop the children counter of
Expand All @@ -1882,6 +1885,7 @@ int pm_runtime_force_suspend(struct device *dev)
return 0;

err:
dev_pm_disable_wake_irq_check(dev, true);
pm_runtime_enable(dev);
return ret;
}
Expand Down Expand Up @@ -1915,9 +1919,11 @@ int pm_runtime_force_resume(struct device *dev)

callback = RPM_GET_CALLBACK(dev, runtime_resume);

dev_pm_disable_wake_irq_check(dev, false);
ret = callback ? callback(dev) : 0;
if (ret) {
pm_runtime_set_suspended(dev);
dev_pm_enable_wake_irq_check(dev, false);
goto out;
}

Expand Down
30 changes: 0 additions & 30 deletions drivers/base/power/wakeup.c
Original file line number Diff line number Diff line change
Expand Up @@ -500,36 +500,6 @@ void device_set_wakeup_capable(struct device *dev, bool capable)
}
EXPORT_SYMBOL_GPL(device_set_wakeup_capable);

/**
* device_init_wakeup - Device wakeup initialization.
* @dev: Device to handle.
* @enable: Whether or not to enable @dev as a wakeup device.
*
* By default, most devices should leave wakeup disabled. The exceptions are
* devices that everyone expects to be wakeup sources: keyboards, power buttons,
* possibly network interfaces, etc. Also, devices that don't generate their
* own wakeup requests but merely forward requests from one bus to another
* (like PCI bridges) should have wakeup enabled by default.
*/
int device_init_wakeup(struct device *dev, bool enable)
{
int ret = 0;

if (!dev)
return -EINVAL;

if (enable) {
device_set_wakeup_capable(dev, true);
ret = device_wakeup_enable(dev);
} else {
device_wakeup_disable(dev);
device_set_wakeup_capable(dev, false);
}

return ret;
}
EXPORT_SYMBOL_GPL(device_init_wakeup);

/**
* device_set_wakeup_enable - Enable or disable a device to wake up the system.
* @dev: Device to handle.
Expand Down
7 changes: 4 additions & 3 deletions drivers/cpufreq/mediatek-cpufreq-hw.c
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ static const u16 cpufreq_mtk_offsets[REG_ARRAY_SIZE] = {
};

static int __maybe_unused
mtk_cpufreq_get_cpu_power(struct device *cpu_dev, unsigned long *mW,
mtk_cpufreq_get_cpu_power(struct device *cpu_dev, unsigned long *uW,
unsigned long *KHz)
{
struct mtk_cpufreq_data *data;
Expand All @@ -71,8 +71,9 @@ mtk_cpufreq_get_cpu_power(struct device *cpu_dev, unsigned long *mW,
i--;

*KHz = data->table[i].frequency;
*mW = readl_relaxed(data->reg_bases[REG_EM_POWER_TBL] +
i * LUT_ROW_SIZE) / 1000;
/* Provide micro-Watts value to the Energy Model */
*uW = readl_relaxed(data->reg_bases[REG_EM_POWER_TBL] +
i * LUT_ROW_SIZE);

return 0;
}
Expand Down
15 changes: 13 additions & 2 deletions drivers/cpufreq/scmi-cpufreq.c
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include <linux/slab.h>
#include <linux/scmi_protocol.h>
#include <linux/types.h>
#include <linux/units.h>

struct scmi_data {
int domain_id;
Expand Down Expand Up @@ -99,6 +100,7 @@ static int __maybe_unused
scmi_get_cpu_power(struct device *cpu_dev, unsigned long *power,
unsigned long *KHz)
{
enum scmi_power_scale power_scale = perf_ops->power_scale_get(ph);
unsigned long Hz;
int ret, domain;

Expand All @@ -112,6 +114,10 @@ scmi_get_cpu_power(struct device *cpu_dev, unsigned long *power,
if (ret)
return ret;

/* Convert the power to uW if it is mW (ignore bogoW) */
if (power_scale == SCMI_POWER_MILLIWATTS)
*power *= MICROWATT_PER_MILLIWATT;

/* The EM framework specifies the frequency in KHz. */
*KHz = Hz / 1000;

Expand Down Expand Up @@ -249,8 +255,9 @@ static int scmi_cpufreq_exit(struct cpufreq_policy *policy)
static void scmi_cpufreq_register_em(struct cpufreq_policy *policy)
{
struct em_data_callback em_cb = EM_DATA_CB(scmi_get_cpu_power);
bool power_scale_mw = perf_ops->power_scale_mw_get(ph);
enum scmi_power_scale power_scale = perf_ops->power_scale_get(ph);
struct scmi_data *priv = policy->driver_data;
bool em_power_scale = false;

/*
* This callback will be called for each policy, but we don't need to
Expand All @@ -262,9 +269,13 @@ static void scmi_cpufreq_register_em(struct cpufreq_policy *policy)
if (!priv->nr_opp)
return;

if (power_scale == SCMI_POWER_MILLIWATTS
|| power_scale == SCMI_POWER_MICROWATTS)
em_power_scale = true;

em_dev_register_perf_domain(get_cpu_device(policy->cpu), priv->nr_opp,
&em_cb, priv->opp_shared_cpus,
power_scale_mw);
em_power_scale);
}

static struct cpufreq_driver scmi_cpufreq_driver = {
Expand Down
18 changes: 11 additions & 7 deletions drivers/firmware/arm_scmi/perf.c
Original file line number Diff line number Diff line change
Expand Up @@ -170,8 +170,7 @@ struct perf_dom_info {
struct scmi_perf_info {
u32 version;
int num_domains;
bool power_scale_mw;
bool power_scale_uw;
enum scmi_power_scale power_scale;
u64 stats_addr;
u32 stats_size;
struct perf_dom_info *dom_info;
Expand Down Expand Up @@ -201,9 +200,13 @@ static int scmi_perf_attributes_get(const struct scmi_protocol_handle *ph,
u16 flags = le16_to_cpu(attr->flags);

pi->num_domains = le16_to_cpu(attr->num_domains);
pi->power_scale_mw = POWER_SCALE_IN_MILLIWATT(flags);

if (POWER_SCALE_IN_MILLIWATT(flags))
pi->power_scale = SCMI_POWER_MILLIWATTS;
if (PROTOCOL_REV_MAJOR(pi->version) >= 0x3)
pi->power_scale_uw = POWER_SCALE_IN_MICROWATT(flags);
if (POWER_SCALE_IN_MICROWATT(flags))
pi->power_scale = SCMI_POWER_MICROWATTS;

pi->stats_addr = le32_to_cpu(attr->stats_addr_low) |
(u64)le32_to_cpu(attr->stats_addr_high) << 32;
pi->stats_size = le32_to_cpu(attr->stats_size);
Expand Down Expand Up @@ -792,11 +795,12 @@ static bool scmi_fast_switch_possible(const struct scmi_protocol_handle *ph,
return dom->fc_info && dom->fc_info->level_set_addr;
}

static bool scmi_power_scale_mw_get(const struct scmi_protocol_handle *ph)
static enum scmi_power_scale
scmi_power_scale_get(const struct scmi_protocol_handle *ph)
{
struct scmi_perf_info *pi = ph->get_priv(ph);

return pi->power_scale_mw;
return pi->power_scale;
}

static const struct scmi_perf_proto_ops perf_proto_ops = {
Expand All @@ -811,7 +815,7 @@ static const struct scmi_perf_proto_ops perf_proto_ops = {
.freq_get = scmi_dvfs_freq_get,
.est_power_get = scmi_dvfs_est_power_get,
.fast_switch_possible = scmi_fast_switch_possible,
.power_scale_mw_get = scmi_power_scale_mw_get,
.power_scale_get = scmi_power_scale_get,
};

static int scmi_perf_set_notify_enabled(const struct scmi_protocol_handle *ph,
Expand Down
15 changes: 8 additions & 7 deletions drivers/opp/of.c
Original file line number Diff line number Diff line change
Expand Up @@ -1443,12 +1443,12 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_get_of_node);
* It provides the power used by @dev at @kHz if it is the frequency of an
* existing OPP, or at the frequency of the first OPP above @kHz otherwise
* (see dev_pm_opp_find_freq_ceil()). This function updates @kHz to the ceiled
* frequency and @mW to the associated power.
* frequency and @uW to the associated power.
*
* Returns 0 on success or a proper -EINVAL value in case of error.
*/
static int __maybe_unused
_get_dt_power(struct device *dev, unsigned long *mW, unsigned long *kHz)
_get_dt_power(struct device *dev, unsigned long *uW, unsigned long *kHz)
{
struct dev_pm_opp *opp;
unsigned long opp_freq, opp_power;
Expand All @@ -1465,7 +1465,7 @@ _get_dt_power(struct device *dev, unsigned long *mW, unsigned long *kHz)
return -EINVAL;

*kHz = opp_freq / 1000;
*mW = opp_power / 1000;
*uW = opp_power;

return 0;
}
Expand All @@ -1475,14 +1475,14 @@ _get_dt_power(struct device *dev, unsigned long *mW, unsigned long *kHz)
* This computes the power estimated by @dev at @kHz if it is the frequency
* of an existing OPP, or at the frequency of the first OPP above @kHz otherwise
* (see dev_pm_opp_find_freq_ceil()). This function updates @kHz to the ceiled
* frequency and @mW to the associated power. The power is estimated as
* frequency and @uW to the associated power. The power is estimated as
* P = C * V^2 * f with C being the device's capacitance and V and f
* respectively the voltage and frequency of the OPP.
*
* Returns -EINVAL if the power calculation failed because of missing
* parameters, 0 otherwise.
*/
static int __maybe_unused _get_power(struct device *dev, unsigned long *mW,
static int __maybe_unused _get_power(struct device *dev, unsigned long *uW,
unsigned long *kHz)
{
struct dev_pm_opp *opp;
Expand Down Expand Up @@ -1512,9 +1512,10 @@ static int __maybe_unused _get_power(struct device *dev, unsigned long *mW,
return -EINVAL;

tmp = (u64)cap * mV * mV * (Hz / 1000000);
do_div(tmp, 1000000000);
/* Provide power in micro-Watts */
do_div(tmp, 1000000);

*mW = (unsigned long)tmp;
*uW = (unsigned long)tmp;
*kHz = Hz / 1000;

return 0;
Expand Down
5 changes: 2 additions & 3 deletions drivers/powercap/dtpm_cpu.c
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ static u64 set_pd_power_limit(struct dtpm *dtpm, u64 power_limit)

for (i = 0; i < pd->nr_perf_states; i++) {

power = pd->table[i].power * MICROWATT_PER_MILLIWATT * nr_cpus;
power = pd->table[i].power * nr_cpus;

if (power > power_limit)
break;
Expand All @@ -63,8 +63,7 @@ static u64 set_pd_power_limit(struct dtpm *dtpm, u64 power_limit)

freq_qos_update_request(&dtpm_cpu->qos_req, freq);

power_limit = pd->table[i - 1].power *
MICROWATT_PER_MILLIWATT * nr_cpus;
power_limit = pd->table[i - 1].power * nr_cpus;

return power_limit;
}
Expand Down
1 change: 1 addition & 0 deletions drivers/powercap/intel_rapl_common.c
Original file line number Diff line number Diff line change
Expand Up @@ -1109,6 +1109,7 @@ static const struct x86_cpu_id rapl_ids[] __initconst = {
X86_MATCH_INTEL_FAM6_MODEL(ALDERLAKE_L, &rapl_defaults_core),
X86_MATCH_INTEL_FAM6_MODEL(ALDERLAKE_N, &rapl_defaults_core),
X86_MATCH_INTEL_FAM6_MODEL(RAPTORLAKE, &rapl_defaults_core),
X86_MATCH_INTEL_FAM6_MODEL(RAPTORLAKE_P, &rapl_defaults_core),
X86_MATCH_INTEL_FAM6_MODEL(SAPPHIRERAPIDS_X, &rapl_defaults_spr_server),
X86_MATCH_INTEL_FAM6_MODEL(LAKEFIELD, &rapl_defaults_core),

Expand Down
2 changes: 2 additions & 0 deletions drivers/powercap/intel_rapl_msr.c
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,9 @@ static const struct x86_cpu_id pl4_support_ids[] = {
{ X86_VENDOR_INTEL, 6, INTEL_FAM6_TIGERLAKE_L, X86_FEATURE_ANY },
{ X86_VENDOR_INTEL, 6, INTEL_FAM6_ALDERLAKE, X86_FEATURE_ANY },
{ X86_VENDOR_INTEL, 6, INTEL_FAM6_ALDERLAKE_L, X86_FEATURE_ANY },
{ X86_VENDOR_INTEL, 6, INTEL_FAM6_ALDERLAKE_N, X86_FEATURE_ANY },
{ X86_VENDOR_INTEL, 6, INTEL_FAM6_RAPTORLAKE, X86_FEATURE_ANY },
{ X86_VENDOR_INTEL, 6, INTEL_FAM6_RAPTORLAKE_P, X86_FEATURE_ANY },
{}
};

Expand Down
13 changes: 11 additions & 2 deletions drivers/thermal/cpufreq_cooling.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include <linux/pm_qos.h>
#include <linux/slab.h>
#include <linux/thermal.h>
#include <linux/units.h>

#include <trace/events/thermal.h>

Expand Down Expand Up @@ -101,23 +102,31 @@ static unsigned long get_level(struct cpufreq_cooling_device *cpufreq_cdev,
static u32 cpu_freq_to_power(struct cpufreq_cooling_device *cpufreq_cdev,
u32 freq)
{
unsigned long power_mw;
int i;

for (i = cpufreq_cdev->max_level - 1; i >= 0; i--) {
if (freq > cpufreq_cdev->em->table[i].frequency)
break;
}

return cpufreq_cdev->em->table[i + 1].power;
power_mw = cpufreq_cdev->em->table[i + 1].power;
power_mw /= MICROWATT_PER_MILLIWATT;

return power_mw;
}

static u32 cpu_power_to_freq(struct cpufreq_cooling_device *cpufreq_cdev,
u32 power)
{
unsigned long em_power_mw;
int i;

for (i = cpufreq_cdev->max_level; i > 0; i--) {
if (power >= cpufreq_cdev->em->table[i].power)
/* Convert EM power to milli-Watts to make safe comparison */
em_power_mw = cpufreq_cdev->em->table[i].power;
em_power_mw /= MICROWATT_PER_MILLIWATT;
if (power >= em_power_mw)
break;
}

Expand Down
Loading

0 comments on commit 954a83f

Please sign in to comment.