summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorOlender, Agata <agata.olender@intel.com>2021-01-27 15:40:00 +0300
committerJae Hyun Yoo <jae.hyun.yoo@intel.com>2021-07-14 20:05:11 +0300
commit9deeabf70b7dafcf3a0406e1c69c1bb314315f4c (patch)
tree6d19f4fc0289d246d49851b66ac11e98ba59cdb8
parent99258de7b430735344e0c9723931a866ee65370e (diff)
downloadlinux-9deeabf70b7dafcf3a0406e1c69c1bb314315f4c.tar.xz
hwmon: peci: cumulative energy sensor
Change energy sensor to cumulative energy sensor. Actually energy sensor implementation works as simply translation for value read from PCS register to microjoules. With this change, energy sensor will behave as accumulative sensor> After every read on energyN_input file new energy sample will be gathered, and the difference between new sample and previous one will be added to sensor value. This approach solves problems with counters overflow and interpretation of the energy sensor value. Signed-off-by: Olender, Agata <agata.olender@intel.com>
-rw-r--r--drivers/hwmon/peci-cpupower.c30
-rw-r--r--drivers/hwmon/peci-hwmon.h172
2 files changed, 133 insertions, 69 deletions
diff --git a/drivers/hwmon/peci-cpupower.c b/drivers/hwmon/peci-cpupower.c
index c8b3d15f5a07..90336d24f7a2 100644
--- a/drivers/hwmon/peci-cpupower.c
+++ b/drivers/hwmon/peci-cpupower.c
@@ -41,18 +41,10 @@ struct peci_cpupower {
energy_sensor_data_list[PECI_CPUPOWER_ENERGY_CHANNEL_COUNT]
[PECI_CPUPOWER_ENERGY_SENSOR_COUNT];
- /*
- * Not exposed to any sensor directly - just used to limit PECI
- * communication for energy/power_average which are derived from the
- * same underlying data
- */
- struct peci_sensor_data energy_cache;
-
- /*
- * Not exposed to any sensor directly - just used to store previous raw
- * energy counter value that is required to calculate average power
- */
+ /* Below structs are not exposed to any sensor directly */
+ struct peci_sensor_data energy_cache; /* used to limit PECI communication */
struct peci_sensor_data power_sensor_prev_energy;
+ struct peci_sensor_data energy_sensor_prev_energy;
union peci_pkg_power_sku_unit units;
bool units_valid;
@@ -414,14 +406,22 @@ peci_cpupower_read_energy(void *ctx, struct peci_sensor_conf *sensor_conf,
return ret;
}
- /* Energy consumed in microjoules - adjusted to 31 bits counter */
- sensor_data->value = S32_MAX & (u32)peci_pcs_xn_to_uunits(priv->energy_cache.value,
- priv->units.bits.eng_unit);
+ ret = peci_pcs_calc_acc_eng(priv->dev,
+ &priv->energy_sensor_prev_energy,
+ &priv->energy_cache,
+ priv->units.bits.eng_unit,
+ &sensor_data->uvalue);
+
+ if (ret) {
+ dev_dbg(priv->dev, "cumulative energy calculation failed\n");
+ return ret;
+ }
+
peci_sensor_mark_updated_with_time(sensor_data,
priv->energy_cache.last_updated);
dev_dbg(priv->dev, "energy %duJ, jif %lu, HZ is %d jiffies\n",
- sensor_data->value, sensor_data->last_updated, HZ);
+ sensor_data->uvalue, sensor_data->last_updated, HZ);
return 0;
}
diff --git a/drivers/hwmon/peci-hwmon.h b/drivers/hwmon/peci-hwmon.h
index 010d000db31e..63ff624d475a 100644
--- a/drivers/hwmon/peci-hwmon.h
+++ b/drivers/hwmon/peci-hwmon.h
@@ -9,8 +9,8 @@
#define TEMP_TYPE_PECI 6 /* Sensor type 6: Intel PECI */
#define UPDATE_INTERVAL_DEFAULT HZ
-#define UPDATE_INTERVAL_100MS (HZ / 10)
-#define UPDATE_INTERVAL_10S (HZ * 10)
+#define UPDATE_INTERVAL_100MS (HZ / 10)
+#define UPDATE_INTERVAL_10S (HZ * 10)
#define PECI_HWMON_LABEL_STR_LEN 10
@@ -21,8 +21,11 @@
* @last_updated: time of the last update in jiffies
*/
struct peci_sensor_data {
- uint valid;
- s32 value;
+ uint valid;
+ union {
+ s32 value;
+ u32 uvalue;
+ };
ulong last_updated;
};
@@ -35,8 +38,8 @@ struct peci_sensor_data {
static inline bool peci_sensor_need_update(struct peci_sensor_data *sensor)
{
return !sensor->valid ||
- time_after(jiffies, sensor->last_updated +
- UPDATE_INTERVAL_DEFAULT);
+ time_after(jiffies,
+ sensor->last_updated + UPDATE_INTERVAL_DEFAULT);
}
/**
@@ -71,8 +74,7 @@ static inline void peci_sensor_mark_updated(struct peci_sensor_data *sensor)
* @jif: jiffies value to update with
*/
static inline void
-peci_sensor_mark_updated_with_time(struct peci_sensor_data *sensor,
- ulong jif)
+peci_sensor_mark_updated_with_time(struct peci_sensor_data *sensor, ulong jif)
{
sensor->valid = 1;
sensor->last_updated = jif;
@@ -108,8 +110,7 @@ struct peci_sensor_conf {
int (*const read)(void *priv, struct peci_sensor_conf *sensor_conf,
struct peci_sensor_data *sensor_data);
int (*const write)(void *priv, struct peci_sensor_conf *sensor_conf,
- struct peci_sensor_data *sensor_data,
- s32 val);
+ struct peci_sensor_data *sensor_data, s32 val);
};
/**
@@ -144,14 +145,12 @@ static inline u32 peci_sensor_get_config(struct peci_sensor_conf sensors[],
*
* Return: 0 on success or -EOPNOTSUPP in case sensor attribute not found
*/
-static inline int peci_sensor_get_ctx(s32 attribute,
- struct peci_sensor_conf
- sensor_conf_list[],
- struct peci_sensor_conf **sensor_conf,
- struct peci_sensor_data
- sensor_data_list[],
- struct peci_sensor_data **sensor_data,
- const u8 sensor_count)
+static inline int
+peci_sensor_get_ctx(s32 attribute, struct peci_sensor_conf sensor_conf_list[],
+ struct peci_sensor_conf **sensor_conf,
+ struct peci_sensor_data sensor_data_list[],
+ struct peci_sensor_data **sensor_data,
+ const u8 sensor_count)
{
int iter;
@@ -195,12 +194,12 @@ static inline int peci_sensor_get_ctx(s32 attribute,
union peci_pkg_power_sku_unit {
u32 value;
struct {
- u32 pwr_unit : 4;
- u32 rsvd0 : 4;
- u32 eng_unit : 5;
- u32 rsvd1 : 3;
- u32 tim_unit : 4;
- u32 rsvd2 : 12;
+ u32 pwr_unit : 4;
+ u32 rsvd0 : 4;
+ u32 eng_unit : 5;
+ u32 rsvd1 : 3;
+ u32 tim_unit : 4;
+ u32 rsvd2 : 12;
} __attribute__((__packed__)) bits;
} __attribute__((__packed__));
@@ -220,10 +219,10 @@ static_assert(sizeof(union peci_pkg_power_sku_unit) == PECI_PCS_REGISTER_SIZE);
union peci_package_power_info_low {
u32 value;
struct {
- u32 pkg_tdp : 15;
- u32 rsvd0 : 1;
- u32 pkg_min_pwr : 15;
- u32 rsvd1 : 1;
+ u32 pkg_tdp : 15;
+ u32 rsvd0 : 1;
+ u32 pkg_min_pwr : 15;
+ u32 rsvd1 : 1;
} __attribute__((__packed__)) bits;
} __attribute__((__packed__));
@@ -245,11 +244,11 @@ static_assert(sizeof(union peci_package_power_info_low) ==
union peci_package_power_limit_high {
u32 value;
struct {
- u32 pwr_lim_2 : 15;
- u32 pwr_lim_2_en : 1;
- u32 pwr_clmp_lim_2 : 1;
- u32 pwr_lim_2_time : 7;
- u32 rsvd0 : 8;
+ u32 pwr_lim_2 : 15;
+ u32 pwr_lim_2_en : 1;
+ u32 pwr_clmp_lim_2 : 1;
+ u32 pwr_lim_2_time : 7;
+ u32 rsvd0 : 8;
} __attribute__((__packed__)) bits;
} __attribute__((__packed__));
@@ -271,11 +270,11 @@ static_assert(sizeof(union peci_package_power_limit_high) ==
union peci_package_power_limit_low {
u32 value;
struct {
- u32 pwr_lim_1 : 15;
- u32 pwr_lim_1_en : 1;
- u32 pwr_clmp_lim_1 : 1;
- u32 pwr_lim_1_time : 7;
- u32 rsvd0 : 8;
+ u32 pwr_lim_1 : 15;
+ u32 pwr_lim_1_en : 1;
+ u32 pwr_clmp_lim_1 : 1;
+ u32 pwr_lim_1_time : 7;
+ u32 rsvd0 : 8;
} __attribute__((__packed__)) bits;
} __attribute__((__packed__));
@@ -297,11 +296,11 @@ static_assert(sizeof(union peci_package_power_limit_low) ==
union peci_dram_power_info_high {
u32 value;
struct {
- u32 max_pwr : 15;
- u32 rsvd0 : 1;
- u32 max_win : 7;
- u32 rsvd1 : 8;
- u32 lock : 1;
+ u32 max_pwr : 15;
+ u32 rsvd0 : 1;
+ u32 max_win : 7;
+ u32 rsvd1 : 8;
+ u32 lock : 1;
} __attribute__((__packed__)) bits;
} __attribute__((__packed__));
@@ -322,10 +321,10 @@ static_assert(sizeof(union peci_dram_power_info_high) ==
union peci_dram_power_info_low {
u32 value;
struct {
- u32 tdp : 15;
- u32 rsvd0 : 1;
- u32 min_pwr : 15;
- u32 rsvd1 : 1;
+ u32 tdp : 15;
+ u32 rsvd0 : 1;
+ u32 min_pwr : 15;
+ u32 rsvd1 : 1;
} __attribute__((__packed__)) bits;
} __attribute__((__packed__));
@@ -349,11 +348,11 @@ static_assert(sizeof(union peci_dram_power_info_low) == PECI_PCS_REGISTER_SIZE);
union peci_dram_power_limit {
u32 value;
struct {
- u32 pp_pwr_lim : 15;
- u32 pwr_lim_ctrl_en : 1;
- u32 rsvd0 : 1;
- u32 ctrl_time_win : 7;
- u32 rsvd1 : 8;
+ u32 pp_pwr_lim : 15;
+ u32 pwr_lim_ctrl_en : 1;
+ u32 rsvd0 : 1;
+ u32 ctrl_time_win : 7;
+ u32 rsvd1 : 8;
} __attribute__((__packed__)) bits;
} __attribute__((__packed__));
@@ -483,8 +482,7 @@ static inline int peci_pcs_write(struct peci_client_manager *peci_mgr, u8 index,
static inline int peci_pcs_calc_pwr_from_eng(struct device *dev,
struct peci_sensor_data *prev_energy,
struct peci_sensor_data *energy,
- u32 unit,
- s32 *power_in_mW)
+ u32 unit, s32 *power_in_mW)
{
ulong elapsed;
int ret;
@@ -545,6 +543,72 @@ static inline int peci_pcs_calc_pwr_from_eng(struct device *dev,
}
/**
+ * peci_pcs_calc_acc_eng - calculate accumulated energy (in microjoules) based
+ * on two energy readings
+ * @dev: Device handle
+ * @prev_energy: Previous energy reading context with raw energy counter value
+ * @energy: Current energy reading context with raw energy counter value
+ * @unit: Calculation factor
+ * @acc_energy_in_uJ: Pointer to the variable with cumulative energy counter
+ *
+ * Return: 0 if succeeded,
+ * -EINVAL if there are null pointers among arguments,
+ * -EAGAIN if calculation is skipped.
+ */
+static inline int peci_pcs_calc_acc_eng(struct device *dev,
+ struct peci_sensor_data *prev_energy,
+ struct peci_sensor_data *curr_energy,
+ u32 unit, u32 *acc_energy_in_uJ)
+{
+ ulong elapsed;
+ int ret;
+
+ elapsed = curr_energy->last_updated - prev_energy->last_updated;
+
+ dev_dbg(dev, "raw energy before %u, raw energy now %u, unit %u, jiffies elapsed %lu\n",
+ prev_energy->uvalue, curr_energy->value, unit, elapsed);
+
+ /*
+ * Don't calculate cumulative energy for first counter read - last counter
+ * read was more than 17 minutes ago (jiffies and energy raw counter did not wrap
+ * and power calculation does not overflow or underflow).
+ */
+ if (prev_energy->last_updated > 0 && elapsed < (HZ * 17 * 60)) {
+ u32 energy_consumed;
+ u64 energy_consumed_in_uJ;
+
+ if (curr_energy->uvalue >= prev_energy->uvalue)
+ energy_consumed = curr_energy->uvalue -
+ prev_energy->uvalue;
+ else
+ energy_consumed = (U32_MAX - prev_energy->uvalue) +
+ curr_energy->uvalue + 1u;
+
+ energy_consumed_in_uJ =
+ peci_pcs_xn_to_uunits(energy_consumed, unit);
+ *acc_energy_in_uJ = S32_MAX &
+ (*acc_energy_in_uJ + (u32)energy_consumed_in_uJ);
+
+ dev_dbg(dev, "raw energy %u, scaled energy %llumJ, cumulative energy %dmJ\n",
+ energy_consumed, energy_consumed_in_uJ,
+ *acc_energy_in_uJ);
+
+ ret = 0;
+ } else {
+ dev_dbg(dev, "skipping calculate cumulative energy, try again\n");
+
+ *acc_energy_in_uJ = 0;
+ ret = -EAGAIN;
+ }
+
+ prev_energy->uvalue = curr_energy->uvalue;
+ peci_sensor_mark_updated_with_time(prev_energy,
+ curr_energy->last_updated);
+
+ return ret;
+}
+
+/**
* peci_pcs_get_units - read units (power, energy, time) from HW or cache
* @peci_mgr: PECI client manager handle
* @units: Pointer to the variable read value is going to be put in case reading