summaryrefslogtreecommitdiff
path: root/drivers/hwmon/peci-hwmon.h
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/hwmon/peci-hwmon.h')
-rw-r--r--drivers/hwmon/peci-hwmon.h645
1 files changed, 645 insertions, 0 deletions
diff --git a/drivers/hwmon/peci-hwmon.h b/drivers/hwmon/peci-hwmon.h
new file mode 100644
index 000000000000..911bd9840b7d
--- /dev/null
+++ b/drivers/hwmon/peci-hwmon.h
@@ -0,0 +1,645 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (c) 2018-2020 Intel Corporation */
+
+#ifndef __PECI_HWMON_H
+#define __PECI_HWMON_H
+
+#include <linux/peci.h>
+#include <asm/div64.h>
+
+#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 PECI_HWMON_LABEL_STR_LEN 10
+
+/**
+ * struct peci_sensor_data - PECI sensor information
+ * @valid: flag to indicate the sensor value is valid
+ * @value: sensor value in milli units
+ * @last_updated: time of the last update in jiffies
+ */
+struct peci_sensor_data {
+ uint valid;
+ union {
+ s32 value;
+ u32 uvalue;
+ };
+ ulong last_updated;
+};
+
+/**
+ * peci_sensor_need_update - check whether sensor update is needed or not
+ * @sensor: pointer to sensor data struct
+ *
+ * Return: true if update is needed, false if not.
+ */
+static inline bool peci_sensor_need_update(struct peci_sensor_data *sensor)
+{
+ return !sensor->valid ||
+ time_after(jiffies,
+ sensor->last_updated + UPDATE_INTERVAL_DEFAULT);
+}
+
+/**
+ * peci_sensor_need_update_with_time - check whether sensor update is needed
+ * or not
+ * @sensor: pointer to sensor data struct
+ * @update_interval: update interval to check
+ *
+ * Return: true if update is needed, false if not.
+ */
+static inline bool
+peci_sensor_need_update_with_time(struct peci_sensor_data *sensor,
+ ulong update_interval)
+{
+ return !sensor->valid ||
+ time_after(jiffies, sensor->last_updated + update_interval);
+}
+
+/**
+ * peci_sensor_mark_updated - mark the sensor is updated
+ * @sensor: pointer to sensor data struct
+ */
+static inline void peci_sensor_mark_updated(struct peci_sensor_data *sensor)
+{
+ sensor->valid = 1;
+ sensor->last_updated = jiffies;
+}
+
+/**
+ * peci_sensor_mark_updated_with_time - mark the sensor is updated
+ * @sensor: pointer to sensor data struct
+ * @jif: jiffies value to update with
+ */
+static inline void
+peci_sensor_mark_updated_with_time(struct peci_sensor_data *sensor, ulong jif)
+{
+ sensor->valid = 1;
+ sensor->last_updated = jif;
+}
+
+/**
+ * struct peci_sensor_conf - PECI sensor information
+ * @attribute: Sensor attribute
+ * @config: Part of channel parameters brought by single sensor
+ * @update_interval: time in jiffies needs to elapse to read sensor again
+ * @read: Read callback for data attributes. Mandatory if readable
+ * data attributes are present.
+ * Parameters are:
+ * @module_ctx: Pointer peci module context
+ * @sensor_conf: Pointer to sensor configuration object
+ * @sensor_data: Pointer to sensor data object
+ * @val: Pointer to returned value
+ * The function returns 0 on success or a negative error number.
+ * @write: Write callback for data attributes. Mandatory if writeable
+ * data attributes are present.
+ * Parameters are:
+ * @module_ctx: Pointer peci module context
+ * @sensor_conf: Pointer to sensor configuration object
+ * @sensor_data: Pointer to sensor data object
+ * @val: Value to write
+ * The function returns 0 on success or a negative error number.
+ */
+struct peci_sensor_conf {
+ const s32 attribute;
+ const u32 config;
+ const ulong update_interval;
+
+ 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);
+};
+
+/**
+ * peci_sensor_get_config - get peci sensor configuration for provided channel
+ * @sensors: Sensors list
+ * @sensor_count: Sensors count
+ *
+ * Return: sensor configuration
+ */
+static inline u32 peci_sensor_get_config(struct peci_sensor_conf sensors[],
+ u8 sensor_count)
+{
+ u32 config = 0u;
+ int iter;
+
+ for (iter = 0; iter < sensor_count; ++iter)
+ config |= sensors[iter].config;
+
+ return config;
+}
+
+/**
+ * peci_sensor_get_ctx - get peci sensor context - both configuration and data
+ * @attribute: Sensor attribute
+ * @sensor_conf_list: Sensors configuration object list
+ * @sensor_conf: Sensor configuration object found
+ * @sensor_data_list: Sensors data object list, maybe NULL in case there is no
+ * need to find sensor data object
+ * @sensor_data: Sensor data object found, maybe NULL in case there is no need
+ * to find sensor data object
+ * @sensor_count: Sensor count
+ *
+ * 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)
+{
+ int iter;
+
+ for (iter = 0; iter < sensor_count; ++iter) {
+ if (attribute == sensor_conf_list[iter].attribute) {
+ *sensor_conf = &sensor_conf_list[iter];
+ if (sensor_data_list && sensor_data)
+ *sensor_data = &sensor_data_list[iter];
+ return 0;
+ }
+ }
+
+ return -EOPNOTSUPP;
+}
+
+/* Value for the most common parameter used for PCS accessing */
+#define PECI_PCS_PARAM_ZERO 0x0000u
+
+#define PECI_PCS_REGISTER_SIZE 4u /* PCS register size in bytes */
+
+/* PPL1 value to PPL2 value conversation macro */
+#define PECI_PCS_PPL1_TO_PPL2(ppl1_value) ((((u32)(ppl1_value)) * 12uL) / 10uL)
+
+#define PECI_PCS_PPL1_TIME_WINDOW 250 /* PPL1 Time Window value in ms */
+
+#define PECI_PCS_PPL2_TIME_WINDOW 10 /* PPL2 Time Window value in ms */
+
+/**
+ * union peci_pkg_power_sku_unit - PECI Package Power Unit PCS
+ * This register coresponds to the MSR@606h - MSR_RAPL_POWER_UNIT
+ * Accessing over PECI: PCS=0x1E, Parameter=0x0000
+ * @value: PCS register value
+ * @bits: PCS register bits
+ * @pwr_unit: Bits [3:0] - Power Unit
+ * @rsvd0: Bits [7:4]
+ * @eng_unit: Bits [12:8] - Energy Unit
+ * @rsvd1: Bits [15:13]
+ * @tim_unit: Bits [19:16] - Time Unit
+ * @rsvd2: Bits [31:20]
+ */
+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;
+ } __attribute__((__packed__)) bits;
+} __attribute__((__packed__));
+
+static_assert(sizeof(union peci_pkg_power_sku_unit) == PECI_PCS_REGISTER_SIZE);
+
+/**
+ * union peci_package_power_info_low - Platform and Package Power SKU (Low) PCS
+ * This PCS coresponds to the MSR@614h - PACKAGE_POWER_SKU, bits [31:0]
+ * Accessing over PECI: PCS=0x1C, parameter=0x00FF
+ * @value: PCS register value
+ * @bits: PCS register bits
+ * @pkg_tdp: Bits [14:0] - TDP Package Power
+ * @rsvd0: Bits [15:15]
+ * @pkg_min_pwr: Bits [30:16] - Minimal Package Power
+ * @rsvd1: Bits [31:31]
+ */
+union peci_package_power_info_low {
+ u32 value;
+ struct {
+ u32 pkg_tdp : 15;
+ u32 rsvd0 : 1;
+ u32 pkg_min_pwr : 15;
+ u32 rsvd1 : 1;
+ } __attribute__((__packed__)) bits;
+} __attribute__((__packed__));
+
+static_assert(sizeof(union peci_package_power_info_low) ==
+ PECI_PCS_REGISTER_SIZE);
+
+/**
+ * union peci_package_power_limit_high - Package Power Limit 2 PCS
+ * This PCS coresponds to the MSR@610h - PACKAGE_RAPL_LIMIT, bits [63:32]
+ * Accessing over PECI: PCS=0x1B, Parameter=0x0000
+ * @value: PCS register value
+ * @bits: PCS register bits
+ * @pwr_lim_2: Bits [14:0] - Power Limit 2
+ * @pwr_lim_2_en: Bits [15:15] - Power Limit 2 Enable
+ * @pwr_clmp_lim_2:Bits [16:16] - Package Clamping Limitation 2
+ * @pwr_lim_2_time:Bits [23:17] - Power Limit 2 Time Window
+ * @rsvd0: Bits [31:24]
+ */
+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;
+ } __attribute__((__packed__)) bits;
+} __attribute__((__packed__));
+
+static_assert(sizeof(union peci_package_power_limit_high) ==
+ PECI_PCS_REGISTER_SIZE);
+
+/**
+ * union peci_package_power_limit_low - Package Power Limit 1 PCS
+ * This PCS coresponds to the MSR@610h - PACKAGE_RAPL_LIMIT, bits [31:0]
+ * Accessing over PECI: PCS=0x1A, Parameter=0x0000
+ * @value: PCS register value
+ * @bits: PCS register bits
+ * @pwr_lim_1: Bits [14:0] - Power Limit 1
+ * @pwr_lim_1_en: Bits [15:15] - Power Limit 1 Enable
+ * @pwr_clmp_lim_1:Bits [16:16] - Package Clamping Limitation 1
+ * @pwr_lim_1_time:Bits [23:17] - Power Limit 1 Time Window
+ * @rsvd0: Bits [31:24]
+ */
+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;
+ } __attribute__((__packed__)) bits;
+} __attribute__((__packed__));
+
+static_assert(sizeof(union peci_package_power_limit_low) ==
+ PECI_PCS_REGISTER_SIZE);
+
+/**
+ * union peci_dram_power_info_low - DRAM Power Info low PCS
+ * This PCS coresponds to the MSR@61Ch - MSR_DRAM_POWER_INFO, bits [31:0]
+ * Accessing over PECI: PCS=0x24, Parameter=0x0000
+ * @value: PCS register value
+ * @bits: PCS register bits
+ * @tdp: Bits [14:0] - Spec DRAM Power
+ * @rsvd0: Bits [15:15]
+ * @min_pwr: Bits [30:16] - Minimal DRAM Power
+ * @rsvd1: Bits [31:31]
+ */
+union peci_dram_power_info_low {
+ u32 value;
+ struct {
+ u32 tdp : 15;
+ u32 rsvd0 : 1;
+ u32 min_pwr : 15;
+ u32 rsvd1 : 1;
+ } __attribute__((__packed__)) bits;
+} __attribute__((__packed__));
+
+static_assert(sizeof(union peci_dram_power_info_low) == PECI_PCS_REGISTER_SIZE);
+
+/**
+ * union peci_dram_power_limit - DRAM Power Limit PCS
+ * This PCS coresponds to the MSR@618h - DRAM_PLANE_POWER_LIMIT, bits [31:0]
+ * Accessing over PECI: PCS=0x22, Parameter=0x0000
+ * @value: PCS register value
+ * @bits: PCS register bits
+ * @pp_pwr_lim: Bits [14:0] - Power Limit[0] for DDR domain,
+ * format: U11.3
+ * @pwr_lim_ctrl_en:Bits [15:15] - Power Limit[0] enable bit for
+ * DDR domain
+ * @rsvd0: Bits [16:16]
+ * @ctrl_time_win: Bits [23:17] - Power Limit[0] time window for
+ * DDR domain
+ * @rsvd1: Bits [31:24]
+ */
+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;
+ } __attribute__((__packed__)) bits;
+} __attribute__((__packed__));
+
+static_assert(sizeof(union peci_dram_power_limit) == PECI_PCS_REGISTER_SIZE);
+
+/**
+ * peci_pcs_xn_to_uunits - function converting value in units in x.N format to
+ * micro units (microjoules, microseconds, microdegrees) in regular format
+ * @x_n_value: Value in units in x.n format
+ * @n: n factor for x.n format
+
+ *
+ * Return: value in micro units (microjoules, microseconds, microdegrees)
+ * in regular format
+ */
+static inline u64 peci_pcs_xn_to_uunits(u32 x_n_value, u8 n)
+{
+ u64 mx_n_value = (u64)x_n_value * 1000000uLL;
+
+ return mx_n_value >> n;
+}
+
+/**
+ * peci_pcs_xn_to_munits - function converting value in units in x.N format to
+ * milli units (millijoules, milliseconds, millidegrees) in regular format
+ * @x_n_value: Value in units in x.n format
+ * @n: n factor for x.n format
+
+ *
+ * Return: value in milli units (millijoules, milliseconds, millidegrees)
+ * in regular format
+ */
+static inline u64 peci_pcs_xn_to_munits(u32 x_n_value, u8 n)
+{
+ u64 mx_n_value = (u64)x_n_value * 1000uLL;
+
+ return mx_n_value >> n;
+}
+
+/**
+ * peci_pcs_munits_to_xn - function converting value in milli units
+ * (millijoules,milliseconds, millidegrees) in regular format to value in units
+ * in x.n format
+ * @mu_value: Value in milli units (millijoules, milliseconds, millidegrees)
+ * @n: n factor for x.n format, assumed here maximal value for n is 32
+ *
+ * Return: value in units in x.n format
+ */
+static inline u32 peci_pcs_munits_to_xn(u32 mu_value, u8 n)
+{
+ /* Convert value in milli units (regular format) to the x.n format */
+ u64 mx_n_value = (u64)mu_value << n;
+ /* Convert milli units (x.n format) to units (x.n format) */
+ if (mx_n_value > (u64)U32_MAX) {
+ do_div(mx_n_value, 1000uL);
+ return (u32)mx_n_value;
+ } else {
+ return (u32)mx_n_value / 1000uL;
+ }
+}
+
+/**
+ * peci_pcs_read - read PCS register
+ * @peci_mgr: PECI client manager handle
+ * @index: PCS index
+ * @parameter: PCS parameter
+ * @reg: Pointer to the variable read value is going to be put
+ *
+ * Return: 0 if succeeded,
+ * -EINVAL if there are null pointers among arguments,
+ * other values in case other errors.
+ */
+static inline int peci_pcs_read(struct peci_client_manager *peci_mgr, u8 index,
+ u16 parameter, u32 *reg)
+{
+ u32 pcs_reg;
+ int ret;
+
+ if (!reg)
+ return -EINVAL;
+
+ ret = peci_client_read_package_config(peci_mgr, index, parameter,
+ (u8 *)&pcs_reg);
+ if (!ret)
+ *reg = le32_to_cpup((__le32 *)&pcs_reg);
+
+ return ret;
+}
+
+/**
+ * peci_pcs_write - write PCS register
+ * @peci_mgr: PECI client manager handle
+ * @index: PCS index
+ * @parameter: PCS parameter
+ * @reg: Variable which value is going to be written to the PCS
+ *
+ * Return: 0 if succeeded, other values in case an error.
+ */
+static inline int peci_pcs_write(struct peci_client_manager *peci_mgr, u8 index,
+ u16 parameter, u32 reg)
+{
+ u32 pcs_reg;
+ int ret;
+
+ pcs_reg = cpu_to_le32p(&reg);
+
+ ret = peci_client_write_package_config(peci_mgr, index, parameter,
+ (u8 *)&pcs_reg);
+
+ return ret;
+}
+
+/**
+ * peci_pcs_calc_pwr_from_eng - calculate power (in milliwatts) 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
+ * @power_val_in_mW: Pointer to the variable calculation result is going to
+ * be put
+ *
+ * Return: 0 if succeeded,
+ * -EINVAL if there are null pointers among arguments,
+ * -EAGAIN if calculation is skipped.
+ */
+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)
+{
+ ulong elapsed;
+ int ret;
+
+
+ elapsed = 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->value, energy->value, unit, elapsed);
+
+ /*
+ * Don't calculate average power for first counter read last counter
+ * read was more than 60 minutes ago (jiffies did not wrap and power
+ * calculation does not overflow or underflow).
+ */
+ if (prev_energy->last_updated > 0 && elapsed < (HZ * 3600) && elapsed) {
+ u32 energy_consumed;
+ u64 energy_consumed_in_mJ;
+ u64 energy_by_jiffies;
+
+ if (energy->uvalue >= prev_energy->uvalue)
+ energy_consumed = energy->uvalue - prev_energy->uvalue;
+ else
+ energy_consumed = (U32_MAX - prev_energy->uvalue) +
+ energy->uvalue + 1u;
+
+ energy_consumed_in_mJ =
+ peci_pcs_xn_to_munits(energy_consumed, unit);
+ energy_by_jiffies = energy_consumed_in_mJ * HZ;
+
+ if (energy_by_jiffies > (u64)U32_MAX) {
+ do_div(energy_by_jiffies, elapsed);
+ *power_in_mW = (long)energy_by_jiffies;
+ } else {
+ *power_in_mW = (u32)energy_by_jiffies / elapsed;
+ }
+
+ dev_dbg(dev, "raw energy consumed %u, scaled energy consumed %llumJ, scaled power %dmW\n",
+ energy_consumed, energy_consumed_in_mJ, *power_in_mW);
+
+ ret = 0;
+ } else {
+ dev_dbg(dev, "skipping calculate power, try again\n");
+ *power_in_mW = 0;
+ ret = -EAGAIN;
+ }
+
+ prev_energy->uvalue = energy->uvalue;
+ peci_sensor_mark_updated_with_time(prev_energy, energy->last_updated);
+
+ return ret;
+}
+
+/**
+ * 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
+ * from HW
+ * @valid: Flag telling cache is valid
+ *
+ * Return: 0 if succeeded
+ * -EINVAL if there are null pointers among arguments,
+ * other values in case other errors.
+ */
+static inline int peci_pcs_get_units(struct peci_client_manager *peci_mgr,
+ union peci_pkg_power_sku_unit *units,
+ bool *valid)
+{
+ int ret = 0;
+
+ if (!valid)
+ return -EINVAL;
+
+ if (!(*valid)) {
+ ret = peci_pcs_read(peci_mgr, PECI_MBX_INDEX_TDP_UNITS,
+ PECI_PCS_PARAM_ZERO, &units->value);
+ if (!ret)
+ *valid = true;
+ }
+ return ret;
+}
+
+/**
+ * peci_pcs_calc_plxy_time_window - calculate power limit time window in
+ * PCS format. To figure that value out needs to solve the following equation:
+ * time_window = (1+(x/4)) * (2 ^ y), where time_window is known value and
+ * x and y values are variables to find.
+ * Return value is about X & Y compostion according to the following:
+ * x = ret[6:5], y = ret[4:0].
+ * @pl_tim_wnd_in_xn: PPL time window in X-n format
+ *
+ * Return: Power limit time window value
+ */
+static inline u32 peci_pcs_calc_plxy_time_window(u32 pl_tim_wnd_in_xn)
+{
+ u32 x = 0u;
+ u32 y = 0u;
+
+ /* Calculate y first */
+ while (pl_tim_wnd_in_xn > 7u) {
+ pl_tim_wnd_in_xn >>= 1;
+ y++;
+ }
+
+ /* Correct y value */
+ if (pl_tim_wnd_in_xn >= 4u)
+ y += 2u;
+ else if (pl_tim_wnd_in_xn >= 2u)
+ y += 1u;
+
+ /* Calculate x then */
+ if (pl_tim_wnd_in_xn >= 4u)
+ x = pl_tim_wnd_in_xn % 4;
+ else
+ x = 0u;
+
+ return ((x & 0x3) << 5) | (y & 0x1F);
+}
+
+#endif /* __PECI_HWMON_H */