162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci// Copyright (c) 2018-2021 Intel Corporation 362306a36Sopenharmony_ci 462306a36Sopenharmony_ci#include <linux/auxiliary_bus.h> 562306a36Sopenharmony_ci#include <linux/bitfield.h> 662306a36Sopenharmony_ci#include <linux/bitops.h> 762306a36Sopenharmony_ci#include <linux/hwmon.h> 862306a36Sopenharmony_ci#include <linux/jiffies.h> 962306a36Sopenharmony_ci#include <linux/module.h> 1062306a36Sopenharmony_ci#include <linux/peci.h> 1162306a36Sopenharmony_ci#include <linux/peci-cpu.h> 1262306a36Sopenharmony_ci#include <linux/units.h> 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include "common.h" 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#define CORE_NUMS_MAX 64 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#define BASE_CHANNEL_NUMS 5 1962306a36Sopenharmony_ci#define CPUTEMP_CHANNEL_NUMS (BASE_CHANNEL_NUMS + CORE_NUMS_MAX) 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#define TEMP_TARGET_FAN_TEMP_MASK GENMASK(15, 8) 2262306a36Sopenharmony_ci#define TEMP_TARGET_REF_TEMP_MASK GENMASK(23, 16) 2362306a36Sopenharmony_ci#define TEMP_TARGET_TJ_OFFSET_MASK GENMASK(29, 24) 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#define DTS_MARGIN_MASK GENMASK(15, 0) 2662306a36Sopenharmony_ci#define PCS_MODULE_TEMP_MASK GENMASK(15, 0) 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_cistruct resolved_cores_reg { 2962306a36Sopenharmony_ci u8 bus; 3062306a36Sopenharmony_ci u8 dev; 3162306a36Sopenharmony_ci u8 func; 3262306a36Sopenharmony_ci u8 offset; 3362306a36Sopenharmony_ci}; 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_cistruct cpu_info { 3662306a36Sopenharmony_ci struct resolved_cores_reg *reg; 3762306a36Sopenharmony_ci u8 min_peci_revision; 3862306a36Sopenharmony_ci s32 (*thermal_margin_to_millidegree)(u16 val); 3962306a36Sopenharmony_ci}; 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_cistruct peci_temp_target { 4262306a36Sopenharmony_ci s32 tcontrol; 4362306a36Sopenharmony_ci s32 tthrottle; 4462306a36Sopenharmony_ci s32 tjmax; 4562306a36Sopenharmony_ci struct peci_sensor_state state; 4662306a36Sopenharmony_ci}; 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_cienum peci_temp_target_type { 4962306a36Sopenharmony_ci tcontrol_type, 5062306a36Sopenharmony_ci tthrottle_type, 5162306a36Sopenharmony_ci tjmax_type, 5262306a36Sopenharmony_ci crit_hyst_type, 5362306a36Sopenharmony_ci}; 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_cistruct peci_cputemp { 5662306a36Sopenharmony_ci struct peci_device *peci_dev; 5762306a36Sopenharmony_ci struct device *dev; 5862306a36Sopenharmony_ci const char *name; 5962306a36Sopenharmony_ci const struct cpu_info *gen_info; 6062306a36Sopenharmony_ci struct { 6162306a36Sopenharmony_ci struct peci_temp_target target; 6262306a36Sopenharmony_ci struct peci_sensor_data die; 6362306a36Sopenharmony_ci struct peci_sensor_data dts; 6462306a36Sopenharmony_ci struct peci_sensor_data core[CORE_NUMS_MAX]; 6562306a36Sopenharmony_ci } temp; 6662306a36Sopenharmony_ci const char **coretemp_label; 6762306a36Sopenharmony_ci DECLARE_BITMAP(core_mask, CORE_NUMS_MAX); 6862306a36Sopenharmony_ci}; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_cienum cputemp_channels { 7162306a36Sopenharmony_ci channel_die, 7262306a36Sopenharmony_ci channel_dts, 7362306a36Sopenharmony_ci channel_tcontrol, 7462306a36Sopenharmony_ci channel_tthrottle, 7562306a36Sopenharmony_ci channel_tjmax, 7662306a36Sopenharmony_ci channel_core, 7762306a36Sopenharmony_ci}; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_cistatic const char * const cputemp_label[BASE_CHANNEL_NUMS] = { 8062306a36Sopenharmony_ci "Die", 8162306a36Sopenharmony_ci "DTS", 8262306a36Sopenharmony_ci "Tcontrol", 8362306a36Sopenharmony_ci "Tthrottle", 8462306a36Sopenharmony_ci "Tjmax", 8562306a36Sopenharmony_ci}; 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_cistatic int update_temp_target(struct peci_cputemp *priv) 8862306a36Sopenharmony_ci{ 8962306a36Sopenharmony_ci s32 tthrottle_offset, tcontrol_margin; 9062306a36Sopenharmony_ci u32 pcs; 9162306a36Sopenharmony_ci int ret; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci if (!peci_sensor_need_update(&priv->temp.target.state)) 9462306a36Sopenharmony_ci return 0; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci ret = peci_pcs_read(priv->peci_dev, PECI_PCS_TEMP_TARGET, 0, &pcs); 9762306a36Sopenharmony_ci if (ret) 9862306a36Sopenharmony_ci return ret; 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci priv->temp.target.tjmax = 10162306a36Sopenharmony_ci FIELD_GET(TEMP_TARGET_REF_TEMP_MASK, pcs) * MILLIDEGREE_PER_DEGREE; 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci tcontrol_margin = FIELD_GET(TEMP_TARGET_FAN_TEMP_MASK, pcs); 10462306a36Sopenharmony_ci tcontrol_margin = sign_extend32(tcontrol_margin, 7) * MILLIDEGREE_PER_DEGREE; 10562306a36Sopenharmony_ci priv->temp.target.tcontrol = priv->temp.target.tjmax - tcontrol_margin; 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci tthrottle_offset = FIELD_GET(TEMP_TARGET_TJ_OFFSET_MASK, pcs) * MILLIDEGREE_PER_DEGREE; 10862306a36Sopenharmony_ci priv->temp.target.tthrottle = priv->temp.target.tjmax - tthrottle_offset; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci peci_sensor_mark_updated(&priv->temp.target.state); 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci return 0; 11362306a36Sopenharmony_ci} 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_cistatic int get_temp_target(struct peci_cputemp *priv, enum peci_temp_target_type type, long *val) 11662306a36Sopenharmony_ci{ 11762306a36Sopenharmony_ci int ret; 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci mutex_lock(&priv->temp.target.state.lock); 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci ret = update_temp_target(priv); 12262306a36Sopenharmony_ci if (ret) 12362306a36Sopenharmony_ci goto unlock; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci switch (type) { 12662306a36Sopenharmony_ci case tcontrol_type: 12762306a36Sopenharmony_ci *val = priv->temp.target.tcontrol; 12862306a36Sopenharmony_ci break; 12962306a36Sopenharmony_ci case tthrottle_type: 13062306a36Sopenharmony_ci *val = priv->temp.target.tthrottle; 13162306a36Sopenharmony_ci break; 13262306a36Sopenharmony_ci case tjmax_type: 13362306a36Sopenharmony_ci *val = priv->temp.target.tjmax; 13462306a36Sopenharmony_ci break; 13562306a36Sopenharmony_ci case crit_hyst_type: 13662306a36Sopenharmony_ci *val = priv->temp.target.tjmax - priv->temp.target.tcontrol; 13762306a36Sopenharmony_ci break; 13862306a36Sopenharmony_ci default: 13962306a36Sopenharmony_ci ret = -EOPNOTSUPP; 14062306a36Sopenharmony_ci break; 14162306a36Sopenharmony_ci } 14262306a36Sopenharmony_ciunlock: 14362306a36Sopenharmony_ci mutex_unlock(&priv->temp.target.state.lock); 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci return ret; 14662306a36Sopenharmony_ci} 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci/* 14962306a36Sopenharmony_ci * Error codes: 15062306a36Sopenharmony_ci * 0x8000: General sensor error 15162306a36Sopenharmony_ci * 0x8001: Reserved 15262306a36Sopenharmony_ci * 0x8002: Underflow on reading value 15362306a36Sopenharmony_ci * 0x8003-0x81ff: Reserved 15462306a36Sopenharmony_ci */ 15562306a36Sopenharmony_cistatic bool dts_valid(u16 val) 15662306a36Sopenharmony_ci{ 15762306a36Sopenharmony_ci return val < 0x8000 || val > 0x81ff; 15862306a36Sopenharmony_ci} 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci/* 16162306a36Sopenharmony_ci * Processors return a value of DTS reading in S10.6 fixed point format 16262306a36Sopenharmony_ci * (16 bits: 10-bit signed magnitude, 6-bit fraction). 16362306a36Sopenharmony_ci */ 16462306a36Sopenharmony_cistatic s32 dts_ten_dot_six_to_millidegree(u16 val) 16562306a36Sopenharmony_ci{ 16662306a36Sopenharmony_ci return sign_extend32(val, 15) * MILLIDEGREE_PER_DEGREE / 64; 16762306a36Sopenharmony_ci} 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci/* 17062306a36Sopenharmony_ci * For older processors, thermal margin reading is returned in S8.8 fixed 17162306a36Sopenharmony_ci * point format (16 bits: 8-bit signed magnitude, 8-bit fraction). 17262306a36Sopenharmony_ci */ 17362306a36Sopenharmony_cistatic s32 dts_eight_dot_eight_to_millidegree(u16 val) 17462306a36Sopenharmony_ci{ 17562306a36Sopenharmony_ci return sign_extend32(val, 15) * MILLIDEGREE_PER_DEGREE / 256; 17662306a36Sopenharmony_ci} 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_cistatic int get_die_temp(struct peci_cputemp *priv, long *val) 17962306a36Sopenharmony_ci{ 18062306a36Sopenharmony_ci int ret = 0; 18162306a36Sopenharmony_ci long tjmax; 18262306a36Sopenharmony_ci u16 temp; 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci mutex_lock(&priv->temp.die.state.lock); 18562306a36Sopenharmony_ci if (!peci_sensor_need_update(&priv->temp.die.state)) 18662306a36Sopenharmony_ci goto skip_update; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci ret = peci_temp_read(priv->peci_dev, &temp); 18962306a36Sopenharmony_ci if (ret) 19062306a36Sopenharmony_ci goto err_unlock; 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci if (!dts_valid(temp)) { 19362306a36Sopenharmony_ci ret = -EIO; 19462306a36Sopenharmony_ci goto err_unlock; 19562306a36Sopenharmony_ci } 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci ret = get_temp_target(priv, tjmax_type, &tjmax); 19862306a36Sopenharmony_ci if (ret) 19962306a36Sopenharmony_ci goto err_unlock; 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci priv->temp.die.value = (s32)tjmax + dts_ten_dot_six_to_millidegree(temp); 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci peci_sensor_mark_updated(&priv->temp.die.state); 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ciskip_update: 20662306a36Sopenharmony_ci *val = priv->temp.die.value; 20762306a36Sopenharmony_cierr_unlock: 20862306a36Sopenharmony_ci mutex_unlock(&priv->temp.die.state.lock); 20962306a36Sopenharmony_ci return ret; 21062306a36Sopenharmony_ci} 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_cistatic int get_dts(struct peci_cputemp *priv, long *val) 21362306a36Sopenharmony_ci{ 21462306a36Sopenharmony_ci int ret = 0; 21562306a36Sopenharmony_ci u16 thermal_margin; 21662306a36Sopenharmony_ci long tcontrol; 21762306a36Sopenharmony_ci u32 pcs; 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci mutex_lock(&priv->temp.dts.state.lock); 22062306a36Sopenharmony_ci if (!peci_sensor_need_update(&priv->temp.dts.state)) 22162306a36Sopenharmony_ci goto skip_update; 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci ret = peci_pcs_read(priv->peci_dev, PECI_PCS_THERMAL_MARGIN, 0, &pcs); 22462306a36Sopenharmony_ci if (ret) 22562306a36Sopenharmony_ci goto err_unlock; 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci thermal_margin = FIELD_GET(DTS_MARGIN_MASK, pcs); 22862306a36Sopenharmony_ci if (!dts_valid(thermal_margin)) { 22962306a36Sopenharmony_ci ret = -EIO; 23062306a36Sopenharmony_ci goto err_unlock; 23162306a36Sopenharmony_ci } 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci ret = get_temp_target(priv, tcontrol_type, &tcontrol); 23462306a36Sopenharmony_ci if (ret) 23562306a36Sopenharmony_ci goto err_unlock; 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci /* Note that the tcontrol should be available before calling it */ 23862306a36Sopenharmony_ci priv->temp.dts.value = 23962306a36Sopenharmony_ci (s32)tcontrol - priv->gen_info->thermal_margin_to_millidegree(thermal_margin); 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci peci_sensor_mark_updated(&priv->temp.dts.state); 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ciskip_update: 24462306a36Sopenharmony_ci *val = priv->temp.dts.value; 24562306a36Sopenharmony_cierr_unlock: 24662306a36Sopenharmony_ci mutex_unlock(&priv->temp.dts.state.lock); 24762306a36Sopenharmony_ci return ret; 24862306a36Sopenharmony_ci} 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_cistatic int get_core_temp(struct peci_cputemp *priv, int core_index, long *val) 25162306a36Sopenharmony_ci{ 25262306a36Sopenharmony_ci int ret = 0; 25362306a36Sopenharmony_ci u16 core_dts_margin; 25462306a36Sopenharmony_ci long tjmax; 25562306a36Sopenharmony_ci u32 pcs; 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci mutex_lock(&priv->temp.core[core_index].state.lock); 25862306a36Sopenharmony_ci if (!peci_sensor_need_update(&priv->temp.core[core_index].state)) 25962306a36Sopenharmony_ci goto skip_update; 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci ret = peci_pcs_read(priv->peci_dev, PECI_PCS_MODULE_TEMP, core_index, &pcs); 26262306a36Sopenharmony_ci if (ret) 26362306a36Sopenharmony_ci goto err_unlock; 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci core_dts_margin = FIELD_GET(PCS_MODULE_TEMP_MASK, pcs); 26662306a36Sopenharmony_ci if (!dts_valid(core_dts_margin)) { 26762306a36Sopenharmony_ci ret = -EIO; 26862306a36Sopenharmony_ci goto err_unlock; 26962306a36Sopenharmony_ci } 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci ret = get_temp_target(priv, tjmax_type, &tjmax); 27262306a36Sopenharmony_ci if (ret) 27362306a36Sopenharmony_ci goto err_unlock; 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci /* Note that the tjmax should be available before calling it */ 27662306a36Sopenharmony_ci priv->temp.core[core_index].value = 27762306a36Sopenharmony_ci (s32)tjmax + dts_ten_dot_six_to_millidegree(core_dts_margin); 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci peci_sensor_mark_updated(&priv->temp.core[core_index].state); 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ciskip_update: 28262306a36Sopenharmony_ci *val = priv->temp.core[core_index].value; 28362306a36Sopenharmony_cierr_unlock: 28462306a36Sopenharmony_ci mutex_unlock(&priv->temp.core[core_index].state.lock); 28562306a36Sopenharmony_ci return ret; 28662306a36Sopenharmony_ci} 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_cistatic int cputemp_read_string(struct device *dev, enum hwmon_sensor_types type, 28962306a36Sopenharmony_ci u32 attr, int channel, const char **str) 29062306a36Sopenharmony_ci{ 29162306a36Sopenharmony_ci struct peci_cputemp *priv = dev_get_drvdata(dev); 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci if (attr != hwmon_temp_label) 29462306a36Sopenharmony_ci return -EOPNOTSUPP; 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci *str = channel < channel_core ? 29762306a36Sopenharmony_ci cputemp_label[channel] : priv->coretemp_label[channel - channel_core]; 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci return 0; 30062306a36Sopenharmony_ci} 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_cistatic int cputemp_read(struct device *dev, enum hwmon_sensor_types type, 30362306a36Sopenharmony_ci u32 attr, int channel, long *val) 30462306a36Sopenharmony_ci{ 30562306a36Sopenharmony_ci struct peci_cputemp *priv = dev_get_drvdata(dev); 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci switch (attr) { 30862306a36Sopenharmony_ci case hwmon_temp_input: 30962306a36Sopenharmony_ci switch (channel) { 31062306a36Sopenharmony_ci case channel_die: 31162306a36Sopenharmony_ci return get_die_temp(priv, val); 31262306a36Sopenharmony_ci case channel_dts: 31362306a36Sopenharmony_ci return get_dts(priv, val); 31462306a36Sopenharmony_ci case channel_tcontrol: 31562306a36Sopenharmony_ci return get_temp_target(priv, tcontrol_type, val); 31662306a36Sopenharmony_ci case channel_tthrottle: 31762306a36Sopenharmony_ci return get_temp_target(priv, tthrottle_type, val); 31862306a36Sopenharmony_ci case channel_tjmax: 31962306a36Sopenharmony_ci return get_temp_target(priv, tjmax_type, val); 32062306a36Sopenharmony_ci default: 32162306a36Sopenharmony_ci return get_core_temp(priv, channel - channel_core, val); 32262306a36Sopenharmony_ci } 32362306a36Sopenharmony_ci break; 32462306a36Sopenharmony_ci case hwmon_temp_max: 32562306a36Sopenharmony_ci return get_temp_target(priv, tcontrol_type, val); 32662306a36Sopenharmony_ci case hwmon_temp_crit: 32762306a36Sopenharmony_ci return get_temp_target(priv, tjmax_type, val); 32862306a36Sopenharmony_ci case hwmon_temp_crit_hyst: 32962306a36Sopenharmony_ci return get_temp_target(priv, crit_hyst_type, val); 33062306a36Sopenharmony_ci default: 33162306a36Sopenharmony_ci return -EOPNOTSUPP; 33262306a36Sopenharmony_ci } 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci return 0; 33562306a36Sopenharmony_ci} 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_cistatic umode_t cputemp_is_visible(const void *data, enum hwmon_sensor_types type, 33862306a36Sopenharmony_ci u32 attr, int channel) 33962306a36Sopenharmony_ci{ 34062306a36Sopenharmony_ci const struct peci_cputemp *priv = data; 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci if (channel > CPUTEMP_CHANNEL_NUMS) 34362306a36Sopenharmony_ci return 0; 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci if (channel < channel_core) 34662306a36Sopenharmony_ci return 0444; 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci if (test_bit(channel - channel_core, priv->core_mask)) 34962306a36Sopenharmony_ci return 0444; 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci return 0; 35262306a36Sopenharmony_ci} 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_cistatic int init_core_mask(struct peci_cputemp *priv) 35562306a36Sopenharmony_ci{ 35662306a36Sopenharmony_ci struct peci_device *peci_dev = priv->peci_dev; 35762306a36Sopenharmony_ci struct resolved_cores_reg *reg = priv->gen_info->reg; 35862306a36Sopenharmony_ci u64 core_mask; 35962306a36Sopenharmony_ci u32 data; 36062306a36Sopenharmony_ci int ret; 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci /* Get the RESOLVED_CORES register value */ 36362306a36Sopenharmony_ci switch (peci_dev->info.model) { 36462306a36Sopenharmony_ci case INTEL_FAM6_ICELAKE_X: 36562306a36Sopenharmony_ci case INTEL_FAM6_ICELAKE_D: 36662306a36Sopenharmony_ci case INTEL_FAM6_SAPPHIRERAPIDS_X: 36762306a36Sopenharmony_ci ret = peci_ep_pci_local_read(peci_dev, 0, reg->bus, reg->dev, 36862306a36Sopenharmony_ci reg->func, reg->offset + 4, &data); 36962306a36Sopenharmony_ci if (ret) 37062306a36Sopenharmony_ci return ret; 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci core_mask = (u64)data << 32; 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci ret = peci_ep_pci_local_read(peci_dev, 0, reg->bus, reg->dev, 37562306a36Sopenharmony_ci reg->func, reg->offset, &data); 37662306a36Sopenharmony_ci if (ret) 37762306a36Sopenharmony_ci return ret; 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci core_mask |= data; 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci break; 38262306a36Sopenharmony_ci default: 38362306a36Sopenharmony_ci ret = peci_pci_local_read(peci_dev, reg->bus, reg->dev, 38462306a36Sopenharmony_ci reg->func, reg->offset, &data); 38562306a36Sopenharmony_ci if (ret) 38662306a36Sopenharmony_ci return ret; 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci core_mask = data; 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci break; 39162306a36Sopenharmony_ci } 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci if (!core_mask) 39462306a36Sopenharmony_ci return -EIO; 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci bitmap_from_u64(priv->core_mask, core_mask); 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci return 0; 39962306a36Sopenharmony_ci} 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_cistatic int create_temp_label(struct peci_cputemp *priv) 40262306a36Sopenharmony_ci{ 40362306a36Sopenharmony_ci unsigned long core_max = find_last_bit(priv->core_mask, CORE_NUMS_MAX); 40462306a36Sopenharmony_ci int i; 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci priv->coretemp_label = devm_kzalloc(priv->dev, (core_max + 1) * sizeof(char *), GFP_KERNEL); 40762306a36Sopenharmony_ci if (!priv->coretemp_label) 40862306a36Sopenharmony_ci return -ENOMEM; 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci for_each_set_bit(i, priv->core_mask, CORE_NUMS_MAX) { 41162306a36Sopenharmony_ci priv->coretemp_label[i] = devm_kasprintf(priv->dev, GFP_KERNEL, "Core %d", i); 41262306a36Sopenharmony_ci if (!priv->coretemp_label[i]) 41362306a36Sopenharmony_ci return -ENOMEM; 41462306a36Sopenharmony_ci } 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci return 0; 41762306a36Sopenharmony_ci} 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_cistatic void check_resolved_cores(struct peci_cputemp *priv) 42062306a36Sopenharmony_ci{ 42162306a36Sopenharmony_ci /* 42262306a36Sopenharmony_ci * Failure to resolve cores is non-critical, we're still able to 42362306a36Sopenharmony_ci * provide other sensor data. 42462306a36Sopenharmony_ci */ 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci if (init_core_mask(priv)) 42762306a36Sopenharmony_ci return; 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci if (create_temp_label(priv)) 43062306a36Sopenharmony_ci bitmap_zero(priv->core_mask, CORE_NUMS_MAX); 43162306a36Sopenharmony_ci} 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_cistatic void sensor_init(struct peci_cputemp *priv) 43462306a36Sopenharmony_ci{ 43562306a36Sopenharmony_ci int i; 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci mutex_init(&priv->temp.target.state.lock); 43862306a36Sopenharmony_ci mutex_init(&priv->temp.die.state.lock); 43962306a36Sopenharmony_ci mutex_init(&priv->temp.dts.state.lock); 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci for_each_set_bit(i, priv->core_mask, CORE_NUMS_MAX) 44262306a36Sopenharmony_ci mutex_init(&priv->temp.core[i].state.lock); 44362306a36Sopenharmony_ci} 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_cistatic const struct hwmon_ops peci_cputemp_ops = { 44662306a36Sopenharmony_ci .is_visible = cputemp_is_visible, 44762306a36Sopenharmony_ci .read_string = cputemp_read_string, 44862306a36Sopenharmony_ci .read = cputemp_read, 44962306a36Sopenharmony_ci}; 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_cistatic const struct hwmon_channel_info * const peci_cputemp_info[] = { 45262306a36Sopenharmony_ci HWMON_CHANNEL_INFO(temp, 45362306a36Sopenharmony_ci /* Die temperature */ 45462306a36Sopenharmony_ci HWMON_T_LABEL | HWMON_T_INPUT | HWMON_T_MAX | 45562306a36Sopenharmony_ci HWMON_T_CRIT | HWMON_T_CRIT_HYST, 45662306a36Sopenharmony_ci /* DTS margin */ 45762306a36Sopenharmony_ci HWMON_T_LABEL | HWMON_T_INPUT | HWMON_T_MAX | 45862306a36Sopenharmony_ci HWMON_T_CRIT | HWMON_T_CRIT_HYST, 45962306a36Sopenharmony_ci /* Tcontrol temperature */ 46062306a36Sopenharmony_ci HWMON_T_LABEL | HWMON_T_INPUT | HWMON_T_CRIT, 46162306a36Sopenharmony_ci /* Tthrottle temperature */ 46262306a36Sopenharmony_ci HWMON_T_LABEL | HWMON_T_INPUT, 46362306a36Sopenharmony_ci /* Tjmax temperature */ 46462306a36Sopenharmony_ci HWMON_T_LABEL | HWMON_T_INPUT, 46562306a36Sopenharmony_ci /* Core temperature - for all core channels */ 46662306a36Sopenharmony_ci [channel_core ... CPUTEMP_CHANNEL_NUMS - 1] = 46762306a36Sopenharmony_ci HWMON_T_LABEL | HWMON_T_INPUT), 46862306a36Sopenharmony_ci NULL 46962306a36Sopenharmony_ci}; 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_cistatic const struct hwmon_chip_info peci_cputemp_chip_info = { 47262306a36Sopenharmony_ci .ops = &peci_cputemp_ops, 47362306a36Sopenharmony_ci .info = peci_cputemp_info, 47462306a36Sopenharmony_ci}; 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_cistatic int peci_cputemp_probe(struct auxiliary_device *adev, 47762306a36Sopenharmony_ci const struct auxiliary_device_id *id) 47862306a36Sopenharmony_ci{ 47962306a36Sopenharmony_ci struct device *dev = &adev->dev; 48062306a36Sopenharmony_ci struct peci_device *peci_dev = to_peci_device(dev->parent); 48162306a36Sopenharmony_ci struct peci_cputemp *priv; 48262306a36Sopenharmony_ci struct device *hwmon_dev; 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 48562306a36Sopenharmony_ci if (!priv) 48662306a36Sopenharmony_ci return -ENOMEM; 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci priv->name = devm_kasprintf(dev, GFP_KERNEL, "peci_cputemp.cpu%d", 48962306a36Sopenharmony_ci peci_dev->info.socket_id); 49062306a36Sopenharmony_ci if (!priv->name) 49162306a36Sopenharmony_ci return -ENOMEM; 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci priv->dev = dev; 49462306a36Sopenharmony_ci priv->peci_dev = peci_dev; 49562306a36Sopenharmony_ci priv->gen_info = (const struct cpu_info *)id->driver_data; 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci /* 49862306a36Sopenharmony_ci * This is just a sanity check. Since we're using commands that are 49962306a36Sopenharmony_ci * guaranteed to be supported on a given platform, we should never see 50062306a36Sopenharmony_ci * revision lower than expected. 50162306a36Sopenharmony_ci */ 50262306a36Sopenharmony_ci if (peci_dev->info.peci_revision < priv->gen_info->min_peci_revision) 50362306a36Sopenharmony_ci dev_warn(priv->dev, 50462306a36Sopenharmony_ci "Unexpected PECI revision %#x, some features may be unavailable\n", 50562306a36Sopenharmony_ci peci_dev->info.peci_revision); 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci check_resolved_cores(priv); 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci sensor_init(priv); 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci hwmon_dev = devm_hwmon_device_register_with_info(priv->dev, priv->name, 51262306a36Sopenharmony_ci priv, &peci_cputemp_chip_info, NULL); 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci return PTR_ERR_OR_ZERO(hwmon_dev); 51562306a36Sopenharmony_ci} 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci/* 51862306a36Sopenharmony_ci * RESOLVED_CORES PCI configuration register may have different location on 51962306a36Sopenharmony_ci * different platforms. 52062306a36Sopenharmony_ci */ 52162306a36Sopenharmony_cistatic struct resolved_cores_reg resolved_cores_reg_hsx = { 52262306a36Sopenharmony_ci .bus = 1, 52362306a36Sopenharmony_ci .dev = 30, 52462306a36Sopenharmony_ci .func = 3, 52562306a36Sopenharmony_ci .offset = 0xb4, 52662306a36Sopenharmony_ci}; 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_cistatic struct resolved_cores_reg resolved_cores_reg_icx = { 52962306a36Sopenharmony_ci .bus = 14, 53062306a36Sopenharmony_ci .dev = 30, 53162306a36Sopenharmony_ci .func = 3, 53262306a36Sopenharmony_ci .offset = 0xd0, 53362306a36Sopenharmony_ci}; 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_cistatic struct resolved_cores_reg resolved_cores_reg_spr = { 53662306a36Sopenharmony_ci .bus = 31, 53762306a36Sopenharmony_ci .dev = 30, 53862306a36Sopenharmony_ci .func = 6, 53962306a36Sopenharmony_ci .offset = 0x80, 54062306a36Sopenharmony_ci}; 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_cistatic const struct cpu_info cpu_hsx = { 54362306a36Sopenharmony_ci .reg = &resolved_cores_reg_hsx, 54462306a36Sopenharmony_ci .min_peci_revision = 0x33, 54562306a36Sopenharmony_ci .thermal_margin_to_millidegree = &dts_eight_dot_eight_to_millidegree, 54662306a36Sopenharmony_ci}; 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_cistatic const struct cpu_info cpu_skx = { 54962306a36Sopenharmony_ci .reg = &resolved_cores_reg_hsx, 55062306a36Sopenharmony_ci .min_peci_revision = 0x33, 55162306a36Sopenharmony_ci .thermal_margin_to_millidegree = &dts_ten_dot_six_to_millidegree, 55262306a36Sopenharmony_ci}; 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_cistatic const struct cpu_info cpu_icx = { 55562306a36Sopenharmony_ci .reg = &resolved_cores_reg_icx, 55662306a36Sopenharmony_ci .min_peci_revision = 0x40, 55762306a36Sopenharmony_ci .thermal_margin_to_millidegree = &dts_ten_dot_six_to_millidegree, 55862306a36Sopenharmony_ci}; 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_cistatic const struct cpu_info cpu_spr = { 56162306a36Sopenharmony_ci .reg = &resolved_cores_reg_spr, 56262306a36Sopenharmony_ci .min_peci_revision = 0x40, 56362306a36Sopenharmony_ci .thermal_margin_to_millidegree = &dts_ten_dot_six_to_millidegree, 56462306a36Sopenharmony_ci}; 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_cistatic const struct auxiliary_device_id peci_cputemp_ids[] = { 56762306a36Sopenharmony_ci { 56862306a36Sopenharmony_ci .name = "peci_cpu.cputemp.hsx", 56962306a36Sopenharmony_ci .driver_data = (kernel_ulong_t)&cpu_hsx, 57062306a36Sopenharmony_ci }, 57162306a36Sopenharmony_ci { 57262306a36Sopenharmony_ci .name = "peci_cpu.cputemp.bdx", 57362306a36Sopenharmony_ci .driver_data = (kernel_ulong_t)&cpu_hsx, 57462306a36Sopenharmony_ci }, 57562306a36Sopenharmony_ci { 57662306a36Sopenharmony_ci .name = "peci_cpu.cputemp.bdxd", 57762306a36Sopenharmony_ci .driver_data = (kernel_ulong_t)&cpu_hsx, 57862306a36Sopenharmony_ci }, 57962306a36Sopenharmony_ci { 58062306a36Sopenharmony_ci .name = "peci_cpu.cputemp.skx", 58162306a36Sopenharmony_ci .driver_data = (kernel_ulong_t)&cpu_skx, 58262306a36Sopenharmony_ci }, 58362306a36Sopenharmony_ci { 58462306a36Sopenharmony_ci .name = "peci_cpu.cputemp.icx", 58562306a36Sopenharmony_ci .driver_data = (kernel_ulong_t)&cpu_icx, 58662306a36Sopenharmony_ci }, 58762306a36Sopenharmony_ci { 58862306a36Sopenharmony_ci .name = "peci_cpu.cputemp.icxd", 58962306a36Sopenharmony_ci .driver_data = (kernel_ulong_t)&cpu_icx, 59062306a36Sopenharmony_ci }, 59162306a36Sopenharmony_ci { 59262306a36Sopenharmony_ci .name = "peci_cpu.cputemp.spr", 59362306a36Sopenharmony_ci .driver_data = (kernel_ulong_t)&cpu_spr, 59462306a36Sopenharmony_ci }, 59562306a36Sopenharmony_ci { } 59662306a36Sopenharmony_ci}; 59762306a36Sopenharmony_ciMODULE_DEVICE_TABLE(auxiliary, peci_cputemp_ids); 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_cistatic struct auxiliary_driver peci_cputemp_driver = { 60062306a36Sopenharmony_ci .probe = peci_cputemp_probe, 60162306a36Sopenharmony_ci .id_table = peci_cputemp_ids, 60262306a36Sopenharmony_ci}; 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_cimodule_auxiliary_driver(peci_cputemp_driver); 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ciMODULE_AUTHOR("Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>"); 60762306a36Sopenharmony_ciMODULE_AUTHOR("Iwona Winiarska <iwona.winiarska@intel.com>"); 60862306a36Sopenharmony_ciMODULE_DESCRIPTION("PECI cputemp driver"); 60962306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 61062306a36Sopenharmony_ciMODULE_IMPORT_NS(PECI_CPU); 611