Commit ae6ccaa6 authored by Lukasz Luba's avatar Lukasz Luba Committed by Rafael J. Wysocki
Browse files

PM: EM: convert power field to micro-Watts precision and align drivers



The milli-Watts precision causes rounding errors while calculating
efficiency cost for each OPP. This is especially visible in the 'simple'
Energy Model (EM), where the power for each OPP is provided from OPP
framework. This can cause some OPPs to be marked inefficient, while
using micro-Watts precision that might not happen.

Update all EM users which access 'power' field and assume the value is
in milli-Watts.

Solve also an issue with potential overflow in calculation of energy
estimation on 32bit machine. It's needed now since the power value
(thus the 'cost' as well) are higher.

Example calculation which shows the rounding error and impact:

power = 'dyn-power-coeff' * volt_mV * volt_mV * freq_MHz

power_a_uW = (100 * 600mW * 600mW * 500MHz) / 10^6 = 18000
power_a_mW = (100 * 600mW * 600mW * 500MHz) / 10^9 = 18

power_b_uW = (100 * 605mW * 605mW * 600MHz) / 10^6 = 21961
power_b_mW = (100 * 605mW * 605mW * 600MHz) / 10^9 = 21

max_freq = 2000MHz

cost_a_mW = 18 * 2000MHz/500MHz = 72
cost_a_uW = 18000 * 2000MHz/500MHz = 72000

cost_b_mW = 21 * 2000MHz/600MHz = 70 // <- artificially better
cost_b_uW = 21961 * 2000MHz/600MHz = 73203

The 'cost_b_mW' (which is based on old milli-Watts) is misleadingly
better that the 'cost_b_uW' (this patch uses micro-Watts) and such
would have impact on the 'inefficient OPPs' information in the Cpufreq
framework. This patch set removes the rounding issue.

Signed-off-by: default avatarLukasz Luba <lukasz.luba@arm.com>
Acked-by: default avatarDaniel Lezcano <daniel.lezcano@linaro.org>
Acked-by: default avatarViresh Kumar <viresh.kumar@linaro.org>
Signed-off-by: default avatarRafael J. Wysocki <rafael.j.wysocki@intel.com>
parent 32346491
Loading
Loading
Loading
Loading
+4 −3
Original line number Diff line number Diff line
@@ -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;
@@ -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;
}
+6 −0
Original line number Diff line number Diff line
@@ -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;
@@ -99,6 +100,7 @@ static int __maybe_unused
scmi_get_cpu_power(struct device *cpu_dev, unsigned long *power,
		   unsigned long *KHz)
{
	bool power_scale_mw = perf_ops->power_scale_mw_get(ph);
	unsigned long Hz;
	int ret, domain;

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

	/* Provide bigger resolution power to the Energy Model */
	if (power_scale_mw)
		*power *= MICROWATT_PER_MILLIWATT;

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

+8 −7
Original line number Diff line number Diff line
@@ -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;
@@ -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;
}
@@ -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;
@@ -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;
+2 −3
Original line number Diff line number Diff line
@@ -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;
@@ -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;
}
+11 −2
Original line number Diff line number Diff line
@@ -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>

@@ -101,6 +102,7 @@ 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--) {
@@ -108,16 +110,23 @@ static u32 cpu_freq_to_power(struct cpufreq_cooling_device *cpufreq_cdev,
			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;
	}

Loading