diff options
Diffstat (limited to 'meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0113-hwmon-peci-PCS-utils.patch')
-rw-r--r-- | meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0113-hwmon-peci-PCS-utils.patch | 706 |
1 files changed, 706 insertions, 0 deletions
diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0113-hwmon-peci-PCS-utils.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0113-hwmon-peci-PCS-utils.patch new file mode 100644 index 000000000..6f6aab922 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0113-hwmon-peci-PCS-utils.patch @@ -0,0 +1,706 @@ +From 44842b3fc1ceb7da68171b6b6c2f24a3b70405ed Mon Sep 17 00:00:00 2001 +From: Zbigniew Lukwinski <zbigniew.lukwinski@linux.intel.com> +Date: Tue, 16 Jun 2020 11:34:28 +0200 +Subject: [PATCH] hwmon: peci: PCS utils + +1. Helpers for reading/writing PCS registers added. +2. PECI sensor configuration structure definition and helpers added. +3. New PECI PCS index and parameters definitions added. + +Tested: + * on WilsonCity platform + * hwmon/peci modules work as before the change + +Signed-off-by: Zbigniew Lukwinski <zbigniew.lukwinski@linux.intel.com> +--- + drivers/hwmon/peci-cpupower.c | 4 +- + drivers/hwmon/peci-hwmon.h | 557 +++++++++++++++++++++++++++++++++- + include/linux/mfd/intel-peci-client.h | 34 ++- + include/uapi/linux/peci-ioctl.h | 5 +- + 4 files changed, 592 insertions(+), 8 deletions(-) + +diff --git a/drivers/hwmon/peci-cpupower.c b/drivers/hwmon/peci-cpupower.c +index 6907696..a3a8fc1 100644 +--- a/drivers/hwmon/peci-cpupower.c ++++ b/drivers/hwmon/peci-cpupower.c +@@ -49,7 +49,7 @@ static int get_average_power(struct peci_cpupower *priv) + + ret = peci_client_read_package_config(priv->mgr, + PECI_MBX_INDEX_TDP_UNITS, +- PECI_PKG_ID_PKG_ENERGY_STATUS, ++ PECI_PKG_ID_CPU_PACKAGE, + pkg_cfg); + + u32 power_unit = ((le32_to_cpup((__le32 *)pkg_cfg)) & 0x1f00) >> 8; +@@ -59,7 +59,7 @@ static int get_average_power(struct peci_cpupower *priv) + + ret = peci_client_read_package_config(priv->mgr, + PECI_MBX_INDEX_ENERGY_COUNTER, +- PECI_PKG_ID_PKG_ENERGY_STATUS, ++ PECI_PKG_ID_CPU_PACKAGE, + pkg_cfg); + if (!ret) { + u32 energy_cnt = le32_to_cpup((__le32 *)pkg_cfg); +diff --git a/drivers/hwmon/peci-hwmon.h b/drivers/hwmon/peci-hwmon.h +index 4d78c52..b8949ae 100644 +--- a/drivers/hwmon/peci-hwmon.h ++++ b/drivers/hwmon/peci-hwmon.h +@@ -1,20 +1,23 @@ + /* SPDX-License-Identifier: GPL-2.0 */ +-/* Copyright (c) 2018-2019 Intel Corporation */ ++/* 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 HZ ++#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 millidegree Celsius ++ * @value: sensor value in milli units + * @last_updated: time of the last update in jiffies + */ + struct peci_sensor_data { +@@ -32,7 +35,24 @@ 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); ++ 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); + } + + /** +@@ -45,4 +65,533 @@ static inline void peci_sensor_mark_updated(struct peci_sensor_data *sensor) + 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, ++ s32 *val); ++ 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_high - DRAM Power Info high PCS ++ * This PCS coresponds to the MSR@61Ch - MSR_DRAM_POWER_INFO, bits [63:32] ++ * Accessing over PECI: PCS=0x23, Parameter=0x0000 ++ * @value: PCS register value ++ * @bits: PCS register bits ++ * @max_pwr: Bits [14:0] - Maximal DRAM Power ++ * @rsvd0: Bits [15:15] ++ * @max_win: Bits [22:16] - Maximal Time Window ++ * @rsvd1: Bits [30:23] ++ * @lock: Bits [31:31] - Locking bit ++ */ ++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; ++ } __attribute__((__packed__)) bits; ++} __attribute__((__packed__)); ++ ++static_assert(sizeof(union peci_dram_power_info_high) == ++ 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_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) ++{ ++ /* Convert value in units in x.n format to milli units in x.n format */ ++ u64 mx_n_value = (u64)x_n_value * 1000uLL; ++ /* Convert x.n format to regular format */ ++ 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(®); ++ ++ 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 ++ * energy reading ++ * @dev: Device handle ++ * @energy: Energy reading context ++ * @energy_cnt: Raw energy reading ++ * @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 *sensor, ++ u32 energy_cnt, u32 unit, ++ s32 *power_in_mW) ++{ ++ ulong jif = jiffies; ++ ulong elapsed; ++ int ret; ++ ++ if (!dev || !sensor || !power_in_mW) ++ return -EINVAL; ++ ++ elapsed = jif - sensor->last_updated; ++ ++ dev_dbg(dev, "raw energy before %u, raw energy now %u, unit %u, jiffies elapsed %lu\n", ++ sensor->value, energy_cnt, 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 (sensor->last_updated > 0 && elapsed < (HZ * 3600)) { ++ u32 energy_consumed; ++ u64 energy_consumed_in_mJ; ++ u64 energy_by_jiffies; ++ ++ /* Take care here about energy counter rollover */ ++ if (energy_cnt >= (u32)(sensor->value)) ++ energy_consumed = energy_cnt - (u32)(sensor->value); ++ else ++ energy_consumed = (U32_MAX - (u32)(sensor->value)) + ++ energy_cnt + 1u; ++ ++ /* Calculate the energy here */ ++ energy_consumed_in_mJ = ++ peci_pcs_xn_to_munits(energy_consumed, unit); ++ energy_by_jiffies = energy_consumed_in_mJ * HZ; ++ ++ /* Calculate the power */ ++ 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; ++ } ++ ++ /* Update sensor context */ ++ sensor->value = energy_cnt; ++ ++ 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 */ +diff --git a/include/linux/mfd/intel-peci-client.h b/include/linux/mfd/intel-peci-client.h +index dbf0670..ba9c03d 100644 +--- a/include/linux/mfd/intel-peci-client.h ++++ b/include/linux/mfd/intel-peci-client.h +@@ -1,5 +1,5 @@ + /* SPDX-License-Identifier: GPL-2.0 */ +-/* Copyright (c) 2018-2019 Intel Corporation */ ++/* Copyright (c) 2018-2020 Intel Corporation */ + + #ifndef __LINUX_MFD_INTEL_PECI_CLIENT_H + #define __LINUX_MFD_INTEL_PECI_CLIENT_H +@@ -131,4 +131,36 @@ peci_client_read_package_config(struct peci_client_manager *priv, + return 0; + } + ++/** ++ * peci_client_write_package_config - write to the Package Configuration Space ++ * @priv: driver private data structure ++ * @index: encoding index for the requested service ++ * @param: parameter to specify the exact data being requested ++ * @data: data buffer with values to write ++ * Context: can sleep ++ * ++ * Return: zero on success, else a negative error code. ++ */ ++static inline int ++peci_client_write_package_config(struct peci_client_manager *priv, ++ u8 index, u16 param, u8 *data) ++{ ++ struct peci_rd_pkg_cfg_msg msg; ++ int ret; ++ ++ msg.addr = priv->client->addr; ++ msg.index = index; ++ msg.param = param; ++ msg.rx_len = 4u; ++ memcpy(msg.pkg_config, data, msg.rx_len); ++ ++ ret = peci_command(priv->client->adapter, PECI_CMD_WR_PKG_CFG, &msg); ++ if (!ret) { ++ if (msg.cc != PECI_DEV_CC_SUCCESS) ++ ret = -EAGAIN; ++ } ++ ++ return ret; ++} ++ + #endif /* __LINUX_MFD_INTEL_PECI_CLIENT_H */ +diff --git a/include/uapi/linux/peci-ioctl.h b/include/uapi/linux/peci-ioctl.h +index c0f0bb3..1c76c7f 100644 +--- a/include/uapi/linux/peci-ioctl.h ++++ b/include/uapi/linux/peci-ioctl.h +@@ -226,12 +226,15 @@ struct peci_rd_pkg_cfg_msg { + __u16 param; + /* When index is PECI_MBX_INDEX_CPU_ID */ + #define PECI_PKG_ID_CPU_ID 0x0000 /* CPUID Info */ ++#define PECI_PKG_POWER_SKU_UNIT 0x0000 /* Time, Energy, Power units */ + #define PECI_PKG_ID_PLATFORM_ID 0x0001 /* Platform ID */ + #define PECI_PKG_ID_UNCORE_ID 0x0002 /* Uncore Device ID */ + #define PECI_PKG_ID_MAX_THREAD_ID 0x0003 /* Max Thread ID */ + #define PECI_PKG_ID_MICROCODE_REV 0x0004 /* CPU Microcode Update Revision */ + #define PECI_PKG_ID_MACHINE_CHECK_STATUS 0x0005 /* Machine Check Status */ +-#define PECI_PKG_ID_PKG_ENERGY_STATUS 0x00ff /* Average Energy */ ++#define PECI_PKG_ID_CPU_PACKAGE 0x00ff /* CPU package ID*/ ++#define PECI_PKG_ID_DIMM 0x00ff /* DIMM ID*/ ++#define PECI_PKG_ID_PLATFORM 0x00fe /* Entire platform ID */ + + __u8 rx_len; + __u8 cc; +-- +2.7.4 + |