From 381cfd63050ba601326f89519dff3114f894f590 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Tue, 9 Jul 2024 07:16:43 -0700 Subject: [PATCH 01/96] hwmon: (adt7470) Use multi-byte regmap operations Use multi-byte regmap operations where possible to reduce code size and the need for mutex protection. No functional change. Signed-off-by: Guenter Roeck --- drivers/hwmon/adt7470.c | 22 +++++++--------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c index 517248d2994e..dbee6926fa05 100644 --- a/drivers/hwmon/adt7470.c +++ b/drivers/hwmon/adt7470.c @@ -728,30 +728,22 @@ static const int adt7470_freq_map[] = { static int pwm1_freq_get(struct device *dev) { struct adt7470_data *data = dev_get_drvdata(dev); - unsigned int cfg_reg_1, cfg_reg_2; + unsigned int regs[2] = {ADT7470_REG_CFG, ADT7470_REG_CFG_2}; + u8 cfg_reg[2]; int index; int err; - mutex_lock(&data->lock); - err = regmap_read(data->regmap, ADT7470_REG_CFG, &cfg_reg_1); - if (err < 0) - goto out; - err = regmap_read(data->regmap, ADT7470_REG_CFG_2, &cfg_reg_2); - if (err < 0) - goto out; - mutex_unlock(&data->lock); + err = regmap_multi_reg_read(data->regmap, regs, cfg_reg, 2); + if (err) + return err; - index = (cfg_reg_2 & ADT7470_FREQ_MASK) >> ADT7470_FREQ_SHIFT; - if (!(cfg_reg_1 & ADT7470_CFG_LF)) + index = (cfg_reg[1] & ADT7470_FREQ_MASK) >> ADT7470_FREQ_SHIFT; + if (!(cfg_reg[0] & ADT7470_CFG_LF)) index += 8; if (index >= ARRAY_SIZE(adt7470_freq_map)) index = ARRAY_SIZE(adt7470_freq_map) - 1; return adt7470_freq_map[index]; - -out: - mutex_unlock(&data->lock); - return err; } static int adt7470_pwm_read(struct device *dev, u32 attr, int channel, long *val) From a0a16d960f64f88d72f5b7a1155e9828d498b64f Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Tue, 9 Jul 2024 13:53:37 -0700 Subject: [PATCH 02/96] hwmon: (tmp401) Use multi-byte regmap operations Use multi-byte regmap operations where possible to reduce code size and the need for mutex protection. No functional change. Signed-off-by: Guenter Roeck --- drivers/hwmon/tmp401.c | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/drivers/hwmon/tmp401.c b/drivers/hwmon/tmp401.c index 853dbe708ff5..02c5a3bb1071 100644 --- a/drivers/hwmon/tmp401.c +++ b/drivers/hwmon/tmp401.c @@ -308,7 +308,9 @@ static int tmp401_temp_read(struct device *dev, u32 attr, int channel, long *val { struct tmp401_data *data = dev_get_drvdata(dev); struct regmap *regmap = data->regmap; + unsigned int regs[2] = { TMP401_TEMP_MSB[3][channel], TMP401_TEMP_CRIT_HYST }; unsigned int regval; + u16 regvals[2]; int reg, ret; switch (attr) { @@ -325,20 +327,11 @@ static int tmp401_temp_read(struct device *dev, u32 attr, int channel, long *val *val = tmp401_register_to_temp(regval, data->extended_range); break; case hwmon_temp_crit_hyst: - mutex_lock(&data->update_lock); - reg = TMP401_TEMP_MSB[3][channel]; - ret = regmap_read(regmap, reg, ®val); - if (ret < 0) - goto unlock; - *val = tmp401_register_to_temp(regval, data->extended_range); - ret = regmap_read(regmap, TMP401_TEMP_CRIT_HYST, ®val); - if (ret < 0) - goto unlock; - *val -= regval * 1000; -unlock: - mutex_unlock(&data->update_lock); + ret = regmap_multi_reg_read(regmap, regs, regvals, 2); if (ret < 0) return ret; + *val = tmp401_register_to_temp(regvals[0], data->extended_range) - + (regvals[1] * 1000); break; case hwmon_temp_fault: case hwmon_temp_min_alarm: From 717c04cf43d2d005db098bb794bf3d558cad98cf Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Tue, 9 Jul 2024 07:21:19 -0700 Subject: [PATCH 03/96] hwmon: (lm95245) Use multi-byte regmap operations Use multi-byte regmap operations where possible to reduce code size and the need for mutex protection. No functional change. Reviewed-by: Tzung-Bi Shih Signed-off-by: Guenter Roeck --- drivers/hwmon/lm95245.c | 110 ++++++++++++++++------------------------ 1 file changed, 45 insertions(+), 65 deletions(-) diff --git a/drivers/hwmon/lm95245.c b/drivers/hwmon/lm95245.c index d293b4f15dc1..3bdc30530847 100644 --- a/drivers/hwmon/lm95245.c +++ b/drivers/hwmon/lm95245.c @@ -161,18 +161,18 @@ static int lm95245_read_temp(struct device *dev, u32 attr, int channel, { struct lm95245_data *data = dev_get_drvdata(dev); struct regmap *regmap = data->regmap; - int ret, regl, regh, regvall, regvalh; + unsigned int regs[2]; + unsigned int regval; + u8 regvals[2]; + int ret; switch (attr) { case hwmon_temp_input: - regl = channel ? LM95245_REG_R_REMOTE_TEMPL_S : - LM95245_REG_R_LOCAL_TEMPL_S; - regh = channel ? LM95245_REG_R_REMOTE_TEMPH_S : - LM95245_REG_R_LOCAL_TEMPH_S; - ret = regmap_read(regmap, regl, ®vall); - if (ret < 0) - return ret; - ret = regmap_read(regmap, regh, ®valh); + regs[0] = channel ? LM95245_REG_R_REMOTE_TEMPL_S : + LM95245_REG_R_LOCAL_TEMPL_S; + regs[1] = channel ? LM95245_REG_R_REMOTE_TEMPH_S : + LM95245_REG_R_LOCAL_TEMPH_S; + ret = regmap_multi_reg_read(regmap, regs, regvals, 2); if (ret < 0) return ret; /* @@ -181,92 +181,77 @@ static int lm95245_read_temp(struct device *dev, u32 attr, int channel, * Use signed calculation for remote if signed bit is set * or if reported temperature is below signed limit. */ - if (!channel || (regvalh & 0x80) || regvalh < 0x7f) { - *val = temp_from_reg_signed(regvalh, regvall); + if (!channel || (regvals[1] & 0x80) || regvals[1] < 0x7f) { + *val = temp_from_reg_signed(regvals[1], regvals[0]); return 0; } - ret = regmap_read(regmap, LM95245_REG_R_REMOTE_TEMPL_U, - ®vall); - if (ret < 0) + ret = regmap_bulk_read(regmap, LM95245_REG_R_REMOTE_TEMPH_U, regvals, 2); + if (ret) return ret; - ret = regmap_read(regmap, LM95245_REG_R_REMOTE_TEMPH_U, - ®valh); - if (ret < 0) - return ret; - *val = temp_from_reg_unsigned(regvalh, regvall); + *val = temp_from_reg_unsigned(regvals[0], regvals[1]); return 0; case hwmon_temp_max: ret = regmap_read(regmap, LM95245_REG_RW_REMOTE_OS_LIMIT, - ®valh); + ®val); if (ret < 0) return ret; - *val = regvalh * 1000; + *val = regval * 1000; return 0; case hwmon_temp_crit: - regh = channel ? LM95245_REG_RW_REMOTE_TCRIT_LIMIT : - LM95245_REG_RW_LOCAL_OS_TCRIT_LIMIT; - ret = regmap_read(regmap, regh, ®valh); + regs[0] = channel ? LM95245_REG_RW_REMOTE_TCRIT_LIMIT : + LM95245_REG_RW_LOCAL_OS_TCRIT_LIMIT; + ret = regmap_read(regmap, regs[0], ®val); if (ret < 0) return ret; - *val = regvalh * 1000; + *val = regval * 1000; return 0; case hwmon_temp_max_hyst: - ret = regmap_read(regmap, LM95245_REG_RW_REMOTE_OS_LIMIT, - ®valh); + regs[0] = LM95245_REG_RW_REMOTE_OS_LIMIT; + regs[1] = LM95245_REG_RW_COMMON_HYSTERESIS; + ret = regmap_multi_reg_read(regmap, regs, regvals, 2); if (ret < 0) return ret; - ret = regmap_read(regmap, LM95245_REG_RW_COMMON_HYSTERESIS, - ®vall); - if (ret < 0) - return ret; - *val = (regvalh - regvall) * 1000; + *val = (regvals[0] - regvals[1]) * 1000; return 0; case hwmon_temp_crit_hyst: - regh = channel ? LM95245_REG_RW_REMOTE_TCRIT_LIMIT : - LM95245_REG_RW_LOCAL_OS_TCRIT_LIMIT; - ret = regmap_read(regmap, regh, ®valh); + regs[0] = channel ? LM95245_REG_RW_REMOTE_TCRIT_LIMIT : + LM95245_REG_RW_LOCAL_OS_TCRIT_LIMIT; + regs[1] = LM95245_REG_RW_COMMON_HYSTERESIS; + + ret = regmap_multi_reg_read(regmap, regs, regvals, 2); if (ret < 0) return ret; - ret = regmap_read(regmap, LM95245_REG_RW_COMMON_HYSTERESIS, - ®vall); - if (ret < 0) - return ret; - *val = (regvalh - regvall) * 1000; + *val = (regvals[0] - regvals[1]) * 1000; return 0; case hwmon_temp_type: - ret = regmap_read(regmap, LM95245_REG_RW_CONFIG2, ®valh); + ret = regmap_read(regmap, LM95245_REG_RW_CONFIG2, ®val); if (ret < 0) return ret; - *val = (regvalh & CFG2_REMOTE_TT) ? 1 : 2; + *val = (regval & CFG2_REMOTE_TT) ? 1 : 2; return 0; case hwmon_temp_offset: - ret = regmap_read(regmap, LM95245_REG_RW_REMOTE_OFFL, - ®vall); + ret = regmap_bulk_read(regmap, LM95245_REG_RW_REMOTE_OFFH, regvals, 2); if (ret < 0) return ret; - ret = regmap_read(regmap, LM95245_REG_RW_REMOTE_OFFH, - ®valh); - if (ret < 0) - return ret; - *val = temp_from_reg_signed(regvalh, regvall); + *val = temp_from_reg_signed(regvals[0], regvals[1]); return 0; case hwmon_temp_max_alarm: - ret = regmap_read(regmap, LM95245_REG_R_STATUS1, ®valh); + ret = regmap_read(regmap, LM95245_REG_R_STATUS1, ®val); if (ret < 0) return ret; - *val = !!(regvalh & STATUS1_ROS); + *val = !!(regval & STATUS1_ROS); return 0; case hwmon_temp_crit_alarm: - ret = regmap_read(regmap, LM95245_REG_R_STATUS1, ®valh); + ret = regmap_read(regmap, LM95245_REG_R_STATUS1, ®val); if (ret < 0) return ret; - *val = !!(regvalh & (channel ? STATUS1_RTCRIT : STATUS1_LOC)); + *val = !!(regval & (channel ? STATUS1_RTCRIT : STATUS1_LOC)); return 0; case hwmon_temp_fault: - ret = regmap_read(regmap, LM95245_REG_R_STATUS1, ®valh); + ret = regmap_read(regmap, LM95245_REG_R_STATUS1, ®val); if (ret < 0) return ret; - *val = !!(regvalh & STATUS1_DIODE_FAULT); + *val = !!(regval & STATUS1_DIODE_FAULT); return 0; default: return -EOPNOTSUPP; @@ -279,6 +264,7 @@ static int lm95245_write_temp(struct device *dev, u32 attr, int channel, struct lm95245_data *data = dev_get_drvdata(dev); struct regmap *regmap = data->regmap; unsigned int regval; + u8 regvals[2]; int ret, reg; switch (attr) { @@ -311,16 +297,10 @@ static int lm95245_write_temp(struct device *dev, u32 attr, int channel, case hwmon_temp_offset: val = clamp_val(val, -128000, 127875); val = val * 256 / 1000; - mutex_lock(&data->update_lock); - ret = regmap_write(regmap, LM95245_REG_RW_REMOTE_OFFL, - val & 0xe0); - if (ret < 0) { - mutex_unlock(&data->update_lock); - return ret; - } - ret = regmap_write(regmap, LM95245_REG_RW_REMOTE_OFFH, - (val >> 8) & 0xff); - mutex_unlock(&data->update_lock); + regvals[0] = val >> 8; + regvals[1] = val & 0xe0; + + ret = regmap_bulk_write(regmap, LM95245_REG_RW_REMOTE_OFFH, regvals, 2); return ret; case hwmon_temp_type: if (val != 1 && val != 2) From 7b4882d9b80cc9723ab097510732a7cc28f56c69 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Tue, 9 Jul 2024 14:03:06 -0700 Subject: [PATCH 04/96] hwmon: (nct7802) Use multi-byte regmap operations Use multi-byte regmap operations where possible to reduce code size and the need for mutex protection. No functional changes. Reviewed-by: Tzung-Bi Shih Signed-off-by: Guenter Roeck --- drivers/hwmon/nct7802.c | 62 +++++++++++++++-------------------------- 1 file changed, 23 insertions(+), 39 deletions(-) diff --git a/drivers/hwmon/nct7802.c b/drivers/hwmon/nct7802.c index 97e8c6424403..5e83504800a3 100644 --- a/drivers/hwmon/nct7802.c +++ b/drivers/hwmon/nct7802.c @@ -229,41 +229,34 @@ abort: static int nct7802_read_fan(struct nct7802_data *data, u8 reg_fan) { - unsigned int f1, f2; + unsigned int regs[2] = {reg_fan, REG_FANCOUNT_LOW}; + u8 f[2]; int ret; - mutex_lock(&data->access_lock); - ret = regmap_read(data->regmap, reg_fan, &f1); - if (ret < 0) - goto abort; - ret = regmap_read(data->regmap, REG_FANCOUNT_LOW, &f2); - if (ret < 0) - goto abort; - ret = (f1 << 5) | (f2 >> 3); + ret = regmap_multi_reg_read(data->regmap, regs, f, 2); + if (ret) + return ret; + ret = (f[0] << 5) | (f[1] >> 3); /* convert fan count to rpm */ if (ret == 0x1fff) /* maximum value, assume fan is stopped */ ret = 0; else if (ret) ret = DIV_ROUND_CLOSEST(1350000U, ret); -abort: - mutex_unlock(&data->access_lock); return ret; } static int nct7802_read_fan_min(struct nct7802_data *data, u8 reg_fan_low, u8 reg_fan_high) { - unsigned int f1, f2; + unsigned int regs[2] = {reg_fan_low, reg_fan_high}; + u8 f[2]; int ret; - mutex_lock(&data->access_lock); - ret = regmap_read(data->regmap, reg_fan_low, &f1); + ret = regmap_multi_reg_read(data->regmap, regs, f, 2); if (ret < 0) - goto abort; - ret = regmap_read(data->regmap, reg_fan_high, &f2); - if (ret < 0) - goto abort; - ret = f1 | ((f2 & 0xf8) << 5); + return ret; + + ret = f[0] | ((f[1] & 0xf8) << 5); /* convert fan count to rpm */ if (ret == 0x1fff) /* maximum value, assume no limit */ ret = 0; @@ -271,8 +264,6 @@ static int nct7802_read_fan_min(struct nct7802_data *data, u8 reg_fan_low, ret = DIV_ROUND_CLOSEST(1350000U, ret); else ret = 1350000U; -abort: - mutex_unlock(&data->access_lock); return ret; } @@ -302,33 +293,26 @@ static u8 nct7802_vmul[] = { 4, 2, 2, 2, 2 }; static int nct7802_read_voltage(struct nct7802_data *data, int nr, int index) { - unsigned int v1, v2; + u8 v[2]; int ret; - mutex_lock(&data->access_lock); if (index == 0) { /* voltage */ - ret = regmap_read(data->regmap, REG_VOLTAGE[nr], &v1); + unsigned int regs[2] = {REG_VOLTAGE[nr], REG_VOLTAGE_LOW}; + + ret = regmap_multi_reg_read(data->regmap, regs, v, 2); if (ret < 0) - goto abort; - ret = regmap_read(data->regmap, REG_VOLTAGE_LOW, &v2); - if (ret < 0) - goto abort; - ret = ((v1 << 2) | (v2 >> 6)) * nct7802_vmul[nr]; + return ret; + ret = ((v[0] << 2) | (v[1] >> 6)) * nct7802_vmul[nr]; } else { /* limit */ int shift = 8 - REG_VOLTAGE_LIMIT_MSB_SHIFT[index - 1][nr]; + unsigned int regs[2] = {REG_VOLTAGE_LIMIT_LSB[index - 1][nr], + REG_VOLTAGE_LIMIT_MSB[nr]}; - ret = regmap_read(data->regmap, - REG_VOLTAGE_LIMIT_LSB[index - 1][nr], &v1); + ret = regmap_multi_reg_read(data->regmap, regs, v, 2); if (ret < 0) - goto abort; - ret = regmap_read(data->regmap, REG_VOLTAGE_LIMIT_MSB[nr], - &v2); - if (ret < 0) - goto abort; - ret = (v1 | ((v2 << shift) & 0x300)) * nct7802_vmul[nr]; + return ret; + ret = (v[0] | ((v[1] << shift) & 0x300)) * nct7802_vmul[nr]; } -abort: - mutex_unlock(&data->access_lock); return ret; } From 4ce612b16e9e1d93cabde798750c0fcfd2477586 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Tue, 9 Jul 2024 14:06:17 -0700 Subject: [PATCH 05/96] hwmon: (adt7x10) Use multi-byte regmap operations Use multi-byte regmap operations where possible to reduce code size and the need for mutex protection. No functional changes. Reviewed-by: Tzung-Bi Shih Signed-off-by: Guenter Roeck --- drivers/hwmon/adt7x10.c | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/drivers/hwmon/adt7x10.c b/drivers/hwmon/adt7x10.c index 6701920de17f..2d329391ed3f 100644 --- a/drivers/hwmon/adt7x10.c +++ b/drivers/hwmon/adt7x10.c @@ -170,21 +170,15 @@ static int adt7x10_temp_write(struct adt7x10_data *data, int index, long temp) static int adt7x10_hyst_read(struct adt7x10_data *data, int index, long *val) { - int hyst, temp, ret; + unsigned int regs[2] = {ADT7X10_T_HYST, ADT7X10_REG_TEMP[index]}; + int hyst, ret; + u16 regdata[2]; - mutex_lock(&data->update_lock); - ret = regmap_read(data->regmap, ADT7X10_T_HYST, &hyst); - if (ret) { - mutex_unlock(&data->update_lock); - return ret; - } - - ret = regmap_read(data->regmap, ADT7X10_REG_TEMP[index], &temp); - mutex_unlock(&data->update_lock); + ret = regmap_multi_reg_read(data->regmap, regs, regdata, 2); if (ret) return ret; - hyst = (hyst & ADT7X10_T_HYST_MASK) * 1000; + hyst = (regdata[0] & ADT7X10_T_HYST_MASK) * 1000; /* * hysteresis is stored as a 4 bit offset in the device, convert it @@ -194,7 +188,7 @@ static int adt7x10_hyst_read(struct adt7x10_data *data, int index, long *val) if (index == adt7x10_t_alarm_low) hyst = -hyst; - *val = ADT7X10_REG_TO_TEMP(data, temp) - hyst; + *val = ADT7X10_REG_TO_TEMP(data, regdata[1]) - hyst; return 0; } From ad231314b6f8e83799b64dd59e6fea98e3b8d36f Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Tue, 9 Jul 2024 14:07:19 -0700 Subject: [PATCH 06/96] hwmon: (tmp464) Use multi-byte regmap operations Use multi-byte regmap operations where possible to reduce code size and the need for mutex protection. No functional changes. Reviewed-by: Tzung-Bi Shih Signed-off-by: Guenter Roeck --- drivers/hwmon/tmp464.c | 33 +++++++++++++++------------------ 1 file changed, 15 insertions(+), 18 deletions(-) diff --git a/drivers/hwmon/tmp464.c b/drivers/hwmon/tmp464.c index 3ee1137533d6..0a7c0448835b 100644 --- a/drivers/hwmon/tmp464.c +++ b/drivers/hwmon/tmp464.c @@ -147,11 +147,11 @@ static int tmp464_temp_read(struct device *dev, u32 attr, int channel, long *val { struct tmp464_data *data = dev_get_drvdata(dev); struct regmap *regmap = data->regmap; - unsigned int regval, regval2; + unsigned int regs[2]; + unsigned int regval; + u16 regvals[2]; int err = 0; - mutex_lock(&data->update_lock); - switch (attr) { case hwmon_temp_max_alarm: err = regmap_read(regmap, TMP464_THERM_STATUS_REG, ®val); @@ -172,26 +172,27 @@ static int tmp464_temp_read(struct device *dev, u32 attr, int channel, long *val * complete. That means we have to cache the value internally * for one measurement cycle and report the cached value. */ + mutex_lock(&data->update_lock); if (!data->valid || time_after(jiffies, data->last_updated + msecs_to_jiffies(data->update_interval))) { err = regmap_read(regmap, TMP464_REMOTE_OPEN_REG, ®val); if (err < 0) - break; + goto unlock; data->open_reg = regval; data->last_updated = jiffies; data->valid = true; } *val = !!(data->open_reg & BIT(channel + 7)); +unlock: + mutex_unlock(&data->update_lock); break; case hwmon_temp_max_hyst: - err = regmap_read(regmap, TMP464_THERM_LIMIT[channel], ®val); + regs[0] = TMP464_THERM_LIMIT[channel]; + regs[1] = TMP464_TEMP_HYST_REG; + err = regmap_multi_reg_read(regmap, regs, regvals, 2); if (err < 0) break; - err = regmap_read(regmap, TMP464_TEMP_HYST_REG, ®val2); - if (err < 0) - break; - regval -= regval2; - *val = temp_from_reg(regval); + *val = temp_from_reg(regvals[0] - regvals[1]); break; case hwmon_temp_max: err = regmap_read(regmap, TMP464_THERM_LIMIT[channel], ®val); @@ -200,14 +201,12 @@ static int tmp464_temp_read(struct device *dev, u32 attr, int channel, long *val *val = temp_from_reg(regval); break; case hwmon_temp_crit_hyst: - err = regmap_read(regmap, TMP464_THERM2_LIMIT[channel], ®val); + regs[0] = TMP464_THERM2_LIMIT[channel]; + regs[1] = TMP464_TEMP_HYST_REG; + err = regmap_multi_reg_read(regmap, regs, regvals, 2); if (err < 0) break; - err = regmap_read(regmap, TMP464_TEMP_HYST_REG, ®val2); - if (err < 0) - break; - regval -= regval2; - *val = temp_from_reg(regval); + *val = temp_from_reg(regvals[0] - regvals[1]); break; case hwmon_temp_crit: err = regmap_read(regmap, TMP464_THERM2_LIMIT[channel], ®val); @@ -239,8 +238,6 @@ static int tmp464_temp_read(struct device *dev, u32 attr, int channel, long *val break; } - mutex_unlock(&data->update_lock); - return err; } From 21a93a9e314f911d5022e988ab0b82a2f65a3507 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Tue, 16 Jul 2024 12:14:12 -0700 Subject: [PATCH 07/96] hwmon: (max6639) Use multi-byte regmap operations Use multi-byte regmap operations where possible to reduce code size and the need for mutex protection. No functional changes. Reviewed-by: Tzung-Bi Shih Signed-off-by: Guenter Roeck --- drivers/hwmon/max6639.c | 40 ++++++++++------------------------------ 1 file changed, 10 insertions(+), 30 deletions(-) diff --git a/drivers/hwmon/max6639.c b/drivers/hwmon/max6639.c index f54720d3d2ce..c955b0f3a8d3 100644 --- a/drivers/hwmon/max6639.c +++ b/drivers/hwmon/max6639.c @@ -88,25 +88,16 @@ struct max6639_data { static int max6639_temp_read_input(struct device *dev, int channel, long *temp) { + u32 regs[2] = { MAX6639_REG_TEMP_EXT(channel), MAX6639_REG_TEMP(channel) }; struct max6639_data *data = dev_get_drvdata(dev); - unsigned int val; + u8 regvals[2]; int res; - /* - * Lock isn't needed as MAX6639_REG_TEMP wpnt change for at least 250ms after reading - * MAX6639_REG_TEMP_EXT - */ - res = regmap_read(data->regmap, MAX6639_REG_TEMP_EXT(channel), &val); + res = regmap_multi_reg_read(data->regmap, regs, regvals, 2); if (res < 0) return res; - *temp = val >> 5; - res = regmap_read(data->regmap, MAX6639_REG_TEMP(channel), &val); - if (res < 0) - return res; - - *temp |= val << 3; - *temp *= 125; + *temp = ((regvals[0] >> 5) | (regvals[1] << 3)) * 125; return 0; } @@ -290,8 +281,10 @@ static umode_t max6639_fan_is_visible(const void *_data, u32 attr, int channel) static int max6639_read_pwm(struct device *dev, u32 attr, int channel, long *pwm_val) { + u32 regs[2] = { MAX6639_REG_FAN_CONFIG3(channel), MAX6639_REG_GCONFIG }; struct max6639_data *data = dev_get_drvdata(dev); unsigned int val; + u8 regvals[2]; int res; u8 i; @@ -303,26 +296,13 @@ static int max6639_read_pwm(struct device *dev, u32 attr, int channel, *pwm_val = val * 255 / 120; return 0; case hwmon_pwm_freq: - mutex_lock(&data->update_lock); - res = regmap_read(data->regmap, MAX6639_REG_FAN_CONFIG3(channel), &val); - if (res < 0) { - mutex_unlock(&data->update_lock); + res = regmap_multi_reg_read(data->regmap, regs, regvals, 2); + if (res < 0) return res; - } - i = val & MAX6639_FAN_CONFIG3_FREQ_MASK; - - res = regmap_read(data->regmap, MAX6639_REG_GCONFIG, &val); - if (res < 0) { - mutex_unlock(&data->update_lock); - return res; - } - - if (val & MAX6639_GCONFIG_PWM_FREQ_HI) + i = regvals[0] & MAX6639_FAN_CONFIG3_FREQ_MASK; + if (regvals[1] & MAX6639_GCONFIG_PWM_FREQ_HI) i |= 0x4; - i &= 0x7; *pwm_val = freq_table[i]; - - mutex_unlock(&data->update_lock); return 0; default: return -EOPNOTSUPP; From c37d0f08f8897f8a280566908545d13d450ba6ef Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Tue, 16 Jul 2024 12:01:21 -0700 Subject: [PATCH 08/96] hwmon: (amc6821) Use multi-byte regmap operations Use multi-byte regmap operations where possible to reduce code size. No functional changes. Reviewed-by: Tzung-Bi Shih Signed-off-by: Guenter Roeck --- drivers/hwmon/amc6821.c | 30 +++++++++++++----------------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/drivers/hwmon/amc6821.c b/drivers/hwmon/amc6821.c index ec94392fcb65..ac64b407ed0e 100644 --- a/drivers/hwmon/amc6821.c +++ b/drivers/hwmon/amc6821.c @@ -136,29 +136,25 @@ struct amc6821_data { */ static int amc6821_get_auto_point_temps(struct regmap *regmap, int channel, u8 *temps) { - u32 pwm, regval; + u32 regs[] = { + AMC6821_REG_DCY_LOW_TEMP, + AMC6821_REG_PSV_TEMP, + channel ? AMC6821_REG_RTEMP_FAN_CTRL : AMC6821_REG_LTEMP_FAN_CTRL + }; + u8 regvals[3]; + int slope; int err; - err = regmap_read(regmap, AMC6821_REG_DCY_LOW_TEMP, &pwm); + err = regmap_multi_reg_read(regmap, regs, regvals, 3); if (err) return err; - - err = regmap_read(regmap, AMC6821_REG_PSV_TEMP, ®val); - if (err) - return err; - temps[0] = regval; - - err = regmap_read(regmap, - channel ? AMC6821_REG_RTEMP_FAN_CTRL : AMC6821_REG_LTEMP_FAN_CTRL, - ®val); - if (err) - return err; - temps[1] = FIELD_GET(AMC6821_TEMP_LIMIT_MASK, regval) * 4; + temps[0] = regvals[1]; + temps[1] = FIELD_GET(AMC6821_TEMP_LIMIT_MASK, regvals[2]) * 4; /* slope is 32 >> in °C */ - regval = 32 >> FIELD_GET(AMC6821_TEMP_SLOPE_MASK, regval); - if (regval) - temps[2] = temps[1] + DIV_ROUND_CLOSEST(255 - pwm, regval); + slope = 32 >> FIELD_GET(AMC6821_TEMP_SLOPE_MASK, regvals[2]); + if (slope) + temps[2] = temps[1] + DIV_ROUND_CLOSEST(255 - regvals[0], slope); else temps[2] = 255; From 8abff91c6173b175bda3381bdb14981249502434 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sun, 7 Jul 2024 07:49:51 -0700 Subject: [PATCH 09/96] hwmon: (lm95234) Reorder include files to be in alphabetic order Alphabetic include file order simplifies maintenance and makes it easier to add or remove files. No functional change. Reviewed-by: Tzung-Bi Shih Signed-off-by: Guenter Roeck --- drivers/hwmon/lm95234.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/hwmon/lm95234.c b/drivers/hwmon/lm95234.c index 9a7afdb49895..0c509eed6a01 100644 --- a/drivers/hwmon/lm95234.c +++ b/drivers/hwmon/lm95234.c @@ -8,14 +8,14 @@ * Copyright (C) 2008, 2010 Davide Rizzo */ -#include -#include -#include -#include -#include +#include #include #include -#include +#include +#include +#include +#include +#include #include #include From e38b05f0a2fd5b92341d366c9f4f74301cd41ac6 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Tue, 9 Jul 2024 21:23:30 -0700 Subject: [PATCH 10/96] hwmon: (lm95234) Use find_closest to find matching update interval Use find_closest() instead of manually coding it to find best update interval. Since find_closest() uses rounding to find the best match, the resulting update interval will now reflect the update interval that is closest to the requested value, not the value that is lower or equal to the requested value. Reviewed-by: Tzung-Bi Shih Signed-off-by: Guenter Roeck --- drivers/hwmon/lm95234.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/hwmon/lm95234.c b/drivers/hwmon/lm95234.c index 0c509eed6a01..a36fa7824da8 100644 --- a/drivers/hwmon/lm95234.c +++ b/drivers/hwmon/lm95234.c @@ -18,6 +18,7 @@ #include #include #include +#include #define DRVNAME "lm95234" @@ -471,10 +472,7 @@ static ssize_t update_interval_store(struct device *dev, if (ret < 0) return ret; - for (regval = 0; regval < 3; regval++) { - if (val <= update_intervals[regval]) - break; - } + regval = find_closest(val, update_intervals, ARRAY_SIZE(update_intervals)); mutex_lock(&data->update_lock); data->interval = msecs_to_jiffies(update_intervals[regval]); From 963f9bc976e27bf073b449497fcce404ef223f91 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sun, 7 Jul 2024 16:43:04 -0700 Subject: [PATCH 11/96] hwmon: (lm95234) Convert to use regmap Use regmap to replace local caching and to be able to use regmap API functions. No functional change. Reviewed-by: Tzung-Bi Shih Signed-off-by: Guenter Roeck --- drivers/hwmon/Kconfig | 1 + drivers/hwmon/lm95234.c | 437 +++++++++++++++++----------------------- 2 files changed, 186 insertions(+), 252 deletions(-) diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index b60fe2e58ad6..e838a55bb3cb 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -1532,6 +1532,7 @@ config SENSORS_LM93 config SENSORS_LM95234 tristate "National Semiconductor LM95234 and compatibles" depends on I2C + select REGMAP_I2C help If you say yes here you get support for the LM95233 and LM95234 temperature sensor chips. diff --git a/drivers/hwmon/lm95234.c b/drivers/hwmon/lm95234.c index a36fa7824da8..7a3aff1d183a 100644 --- a/drivers/hwmon/lm95234.c +++ b/drivers/hwmon/lm95234.c @@ -13,10 +13,10 @@ #include #include #include -#include #include #include #include +#include #include #include @@ -33,6 +33,8 @@ static const unsigned short normal_i2c[] = { #define LM95234_REG_STATUS 0x02 #define LM95234_REG_CONFIG 0x03 #define LM95234_REG_CONVRATE 0x04 +#define LM95234_REG_ENABLE 0x05 +#define LM95234_REG_FILTER 0x06 #define LM95234_REG_STS_FAULT 0x07 #define LM95234_REG_STS_TCRIT1 0x08 #define LM95234_REG_STS_TCRIT2 0x09 @@ -53,181 +55,72 @@ static const unsigned short normal_i2c[] = { /* Client data (each client gets its own) */ struct lm95234_data { - struct i2c_client *client; + struct regmap *regmap; const struct attribute_group *groups[3]; struct mutex update_lock; - unsigned long last_updated, interval; /* in jiffies */ - bool valid; /* false until following fields are valid */ - /* registers values */ - int temp[5]; /* temperature (signed) */ - u32 status; /* fault/alarm status */ - u8 tcrit1[5]; /* critical temperature limit */ - u8 tcrit2[2]; /* high temperature limit */ - s8 toffset[4]; /* remote temperature offset */ - u8 thyst; /* common hysteresis */ - - u8 sensor_type; /* temperature sensor type */ }; -static int lm95234_read_temp(struct i2c_client *client, int index, int *t) +static int lm95234_read_temp(struct regmap *regmap, int index, int *t) { - int val; - u16 temp = 0; + int temp = 0, ret; + u32 val; if (index) { - val = i2c_smbus_read_byte_data(client, - LM95234_REG_UTEMPH(index - 1)); - if (val < 0) - return val; + ret = regmap_read(regmap, LM95234_REG_UTEMPH(index - 1), &val); + if (ret) + return ret; temp = val << 8; - val = i2c_smbus_read_byte_data(client, - LM95234_REG_UTEMPL(index - 1)); - if (val < 0) - return val; + ret = regmap_read(regmap, LM95234_REG_UTEMPL(index - 1), &val); + if (ret) + return ret; temp |= val; - *t = temp; } /* * Read signed temperature if unsigned temperature is 0, * or if this is the local sensor. */ if (!temp) { - val = i2c_smbus_read_byte_data(client, - LM95234_REG_TEMPH(index)); - if (val < 0) - return val; + ret = regmap_read(regmap, LM95234_REG_TEMPH(index), &val); + if (ret) + return ret; temp = val << 8; - val = i2c_smbus_read_byte_data(client, - LM95234_REG_TEMPL(index)); - if (val < 0) - return val; - temp |= val; - *t = (s16)temp; + ret = regmap_read(regmap, LM95234_REG_TEMPL(index), &val); + if (ret) + return ret; + temp = sign_extend32(temp | val, 15); } + *t = DIV_ROUND_CLOSEST(temp * 125, 32); return 0; } -static u16 update_intervals[] = { 143, 364, 1000, 2500 }; - -/* Fill value cache. Must be called with update lock held. */ - -static int lm95234_fill_cache(struct lm95234_data *data, - struct i2c_client *client) -{ - int i, ret; - - ret = i2c_smbus_read_byte_data(client, LM95234_REG_CONVRATE); - if (ret < 0) - return ret; - - data->interval = msecs_to_jiffies(update_intervals[ret & 0x03]); - - for (i = 0; i < ARRAY_SIZE(data->tcrit1); i++) { - ret = i2c_smbus_read_byte_data(client, LM95234_REG_TCRIT1(i)); - if (ret < 0) - return ret; - data->tcrit1[i] = ret; - } - for (i = 0; i < ARRAY_SIZE(data->tcrit2); i++) { - ret = i2c_smbus_read_byte_data(client, LM95234_REG_TCRIT2(i)); - if (ret < 0) - return ret; - data->tcrit2[i] = ret; - } - for (i = 0; i < ARRAY_SIZE(data->toffset); i++) { - ret = i2c_smbus_read_byte_data(client, LM95234_REG_OFFSET(i)); - if (ret < 0) - return ret; - data->toffset[i] = ret; - } - - ret = i2c_smbus_read_byte_data(client, LM95234_REG_TCRIT_HYST); - if (ret < 0) - return ret; - data->thyst = ret; - - ret = i2c_smbus_read_byte_data(client, LM95234_REG_REM_MODEL); - if (ret < 0) - return ret; - data->sensor_type = ret; - - return 0; -} - -static int lm95234_update_device(struct lm95234_data *data) -{ - struct i2c_client *client = data->client; - int ret; - - mutex_lock(&data->update_lock); - - if (time_after(jiffies, data->last_updated + data->interval) || - !data->valid) { - int i; - - if (!data->valid) { - ret = lm95234_fill_cache(data, client); - if (ret < 0) - goto abort; - } - - data->valid = false; - for (i = 0; i < ARRAY_SIZE(data->temp); i++) { - ret = lm95234_read_temp(client, i, &data->temp[i]); - if (ret < 0) - goto abort; - } - - ret = i2c_smbus_read_byte_data(client, LM95234_REG_STS_FAULT); - if (ret < 0) - goto abort; - data->status = ret; - - ret = i2c_smbus_read_byte_data(client, LM95234_REG_STS_TCRIT1); - if (ret < 0) - goto abort; - data->status |= ret << 8; - - ret = i2c_smbus_read_byte_data(client, LM95234_REG_STS_TCRIT2); - if (ret < 0) - goto abort; - data->status |= ret << 16; - - data->last_updated = jiffies; - data->valid = true; - } - ret = 0; -abort: - mutex_unlock(&data->update_lock); - - return ret; -} - static ssize_t temp_show(struct device *dev, struct device_attribute *attr, char *buf) { struct lm95234_data *data = dev_get_drvdata(dev); int index = to_sensor_dev_attr(attr)->index; - int ret = lm95234_update_device(data); + int ret, temp; + ret = lm95234_read_temp(data->regmap, index, &temp); if (ret) return ret; - return sprintf(buf, "%d\n", - DIV_ROUND_CLOSEST(data->temp[index] * 125, 32)); + return sysfs_emit(buf, "%d\n", temp); } static ssize_t alarm_show(struct device *dev, struct device_attribute *attr, char *buf) { struct lm95234_data *data = dev_get_drvdata(dev); - u32 mask = to_sensor_dev_attr(attr)->index; - int ret = lm95234_update_device(data); + u8 mask = to_sensor_dev_attr_2(attr)->index; + u8 reg = to_sensor_dev_attr_2(attr)->nr; + int ret; + u32 val; + ret = regmap_read(data->regmap, reg, &val); if (ret) return ret; - return sprintf(buf, "%u", !!(data->status & mask)); + return sysfs_emit(buf, "%u\n", !!(val & mask)); } static ssize_t type_show(struct device *dev, struct device_attribute *attr, @@ -235,24 +128,23 @@ static ssize_t type_show(struct device *dev, struct device_attribute *attr, { struct lm95234_data *data = dev_get_drvdata(dev); u8 mask = to_sensor_dev_attr(attr)->index; - int ret = lm95234_update_device(data); + u32 val; + int ret; + ret = regmap_read(data->regmap, LM95234_REG_REM_MODEL, &val); if (ret) return ret; - return sprintf(buf, data->sensor_type & mask ? "1\n" : "2\n"); + return sysfs_emit(buf, "%s\n", val & mask ? "1" : "2"); } static ssize_t type_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct lm95234_data *data = dev_get_drvdata(dev); - unsigned long val; u8 mask = to_sensor_dev_attr(attr)->index; - int ret = lm95234_update_device(data); - - if (ret) - return ret; + unsigned long val; + int ret; ret = kstrtoul(buf, 10, &val); if (ret < 0) @@ -261,16 +153,10 @@ static ssize_t type_store(struct device *dev, struct device_attribute *attr, if (val != 1 && val != 2) return -EINVAL; - mutex_lock(&data->update_lock); - if (val == 1) - data->sensor_type |= mask; - else - data->sensor_type &= ~mask; - data->valid = false; - i2c_smbus_write_byte_data(data->client, LM95234_REG_REM_MODEL, - data->sensor_type); - mutex_unlock(&data->update_lock); - + ret = regmap_update_bits(data->regmap, LM95234_REG_REM_MODEL, + mask, val == 1 ? mask : 0); + if (ret) + return ret; return count; } @@ -279,12 +165,14 @@ static ssize_t tcrit2_show(struct device *dev, struct device_attribute *attr, { struct lm95234_data *data = dev_get_drvdata(dev); int index = to_sensor_dev_attr(attr)->index; - int ret = lm95234_update_device(data); + int ret; + u32 tcrit2; + ret = regmap_read(data->regmap, LM95234_REG_TCRIT2(index), &tcrit2); if (ret) return ret; - return sprintf(buf, "%u", data->tcrit2[index] * 1000); + return sysfs_emit(buf, "%u\n", tcrit2 * 1000); } static ssize_t tcrit2_store(struct device *dev, struct device_attribute *attr, @@ -293,10 +181,7 @@ static ssize_t tcrit2_store(struct device *dev, struct device_attribute *attr, struct lm95234_data *data = dev_get_drvdata(dev); int index = to_sensor_dev_attr(attr)->index; long val; - int ret = lm95234_update_device(data); - - if (ret) - return ret; + int ret; ret = kstrtol(buf, 10, &val); if (ret < 0) @@ -305,27 +190,38 @@ static ssize_t tcrit2_store(struct device *dev, struct device_attribute *attr, val = DIV_ROUND_CLOSEST(clamp_val(val, 0, (index ? 255 : 127) * 1000), 1000); - mutex_lock(&data->update_lock); - data->tcrit2[index] = val; - i2c_smbus_write_byte_data(data->client, LM95234_REG_TCRIT2(index), val); - mutex_unlock(&data->update_lock); - + ret = regmap_write(data->regmap, LM95234_REG_TCRIT2(index), val); + if (ret) + return ret; return count; } +static ssize_t tcrit_hyst_show(struct lm95234_data *data, char *buf, int reg) +{ + u32 thyst, tcrit; + int ret; + + mutex_lock(&data->update_lock); + ret = regmap_read(data->regmap, reg, &tcrit); + if (ret) + goto unlock; + ret = regmap_read(data->regmap, LM95234_REG_TCRIT_HYST, &thyst); +unlock: + mutex_unlock(&data->update_lock); + if (ret) + return ret; + + /* Result can be negative, so be careful with unsigned operands */ + return sysfs_emit(buf, "%d\n", ((int)tcrit - (int)thyst) * 1000); +} + static ssize_t tcrit2_hyst_show(struct device *dev, struct device_attribute *attr, char *buf) { struct lm95234_data *data = dev_get_drvdata(dev); int index = to_sensor_dev_attr(attr)->index; - int ret = lm95234_update_device(data); - if (ret) - return ret; - - /* Result can be negative, so be careful with unsigned operands */ - return sprintf(buf, "%d", - ((int)data->tcrit2[index] - (int)data->thyst) * 1000); + return tcrit_hyst_show(data, buf, LM95234_REG_TCRIT2(index)); } static ssize_t tcrit1_show(struct device *dev, struct device_attribute *attr, @@ -333,8 +229,14 @@ static ssize_t tcrit1_show(struct device *dev, struct device_attribute *attr, { struct lm95234_data *data = dev_get_drvdata(dev); int index = to_sensor_dev_attr(attr)->index; + int ret; + u32 val; - return sprintf(buf, "%u", data->tcrit1[index] * 1000); + ret = regmap_read(data->regmap, LM95234_REG_TCRIT1(index), &val); + if (ret) + return ret; + + return sysfs_emit(buf, "%u\n", val * 1000); } static ssize_t tcrit1_store(struct device *dev, struct device_attribute *attr, @@ -342,11 +244,8 @@ static ssize_t tcrit1_store(struct device *dev, struct device_attribute *attr, { struct lm95234_data *data = dev_get_drvdata(dev); int index = to_sensor_dev_attr(attr)->index; - int ret = lm95234_update_device(data); long val; - - if (ret) - return ret; + int ret; ret = kstrtol(buf, 10, &val); if (ret < 0) @@ -354,10 +253,9 @@ static ssize_t tcrit1_store(struct device *dev, struct device_attribute *attr, val = DIV_ROUND_CLOSEST(clamp_val(val, 0, 255000), 1000); - mutex_lock(&data->update_lock); - data->tcrit1[index] = val; - i2c_smbus_write_byte_data(data->client, LM95234_REG_TCRIT1(index), val); - mutex_unlock(&data->update_lock); + ret = regmap_write(data->regmap, LM95234_REG_TCRIT1(index), val); + if (ret) + return ret; return count; } @@ -367,14 +265,8 @@ static ssize_t tcrit1_hyst_show(struct device *dev, { struct lm95234_data *data = dev_get_drvdata(dev); int index = to_sensor_dev_attr(attr)->index; - int ret = lm95234_update_device(data); - if (ret) - return ret; - - /* Result can be negative, so be careful with unsigned operands */ - return sprintf(buf, "%d", - ((int)data->tcrit1[index] - (int)data->thyst) * 1000); + return tcrit_hyst_show(data, buf, LM95234_REG_TCRIT1(index)); } static ssize_t tcrit1_hyst_store(struct device *dev, @@ -383,23 +275,28 @@ static ssize_t tcrit1_hyst_store(struct device *dev, { struct lm95234_data *data = dev_get_drvdata(dev); int index = to_sensor_dev_attr(attr)->index; - int ret = lm95234_update_device(data); + u32 tcrit; long val; - - if (ret) - return ret; + int ret; ret = kstrtol(buf, 10, &val); if (ret < 0) return ret; - val = DIV_ROUND_CLOSEST(clamp_val(val, -255000, 255000), 1000); - val = clamp_val((int)data->tcrit1[index] - val, 0, 31); - mutex_lock(&data->update_lock); - data->thyst = val; - i2c_smbus_write_byte_data(data->client, LM95234_REG_TCRIT_HYST, val); + + ret = regmap_read(data->regmap, LM95234_REG_TCRIT1(index), &tcrit); + if (ret) + goto unlock; + + val = DIV_ROUND_CLOSEST(clamp_val(val, -255000, 255000), 1000); + val = clamp_val((int)tcrit - val, 0, 31); + + ret = regmap_write(data->regmap, LM95234_REG_TCRIT_HYST, val); +unlock: mutex_unlock(&data->update_lock); + if (ret) + return ret; return count; } @@ -409,12 +306,14 @@ static ssize_t offset_show(struct device *dev, struct device_attribute *attr, { struct lm95234_data *data = dev_get_drvdata(dev); int index = to_sensor_dev_attr(attr)->index; - int ret = lm95234_update_device(data); + u32 offset; + int ret; + ret = regmap_read(data->regmap, LM95234_REG_OFFSET(index), &offset); if (ret) return ret; - return sprintf(buf, "%d", data->toffset[index] * 500); + return sysfs_emit(buf, "%d\n", sign_extend32(offset, 7) * 500); } static ssize_t offset_store(struct device *dev, struct device_attribute *attr, @@ -422,11 +321,8 @@ static ssize_t offset_store(struct device *dev, struct device_attribute *attr, { struct lm95234_data *data = dev_get_drvdata(dev); int index = to_sensor_dev_attr(attr)->index; - int ret = lm95234_update_device(data); long val; - - if (ret) - return ret; + int ret; ret = kstrtol(buf, 10, &val); if (ret < 0) @@ -435,25 +331,27 @@ static ssize_t offset_store(struct device *dev, struct device_attribute *attr, /* Accuracy is 1/2 degrees C */ val = DIV_ROUND_CLOSEST(clamp_val(val, -64000, 63500), 500); - mutex_lock(&data->update_lock); - data->toffset[index] = val; - i2c_smbus_write_byte_data(data->client, LM95234_REG_OFFSET(index), val); - mutex_unlock(&data->update_lock); + ret = regmap_write(data->regmap, LM95234_REG_OFFSET(index), val); + if (ret < 0) + return ret; return count; } +static u16 update_intervals[] = { 143, 364, 1000, 2500 }; + static ssize_t update_interval_show(struct device *dev, struct device_attribute *attr, char *buf) { struct lm95234_data *data = dev_get_drvdata(dev); - int ret = lm95234_update_device(data); + u32 convrate; + int ret; + ret = regmap_read(data->regmap, LM95234_REG_CONVRATE, &convrate); if (ret) return ret; - return sprintf(buf, "%lu\n", - DIV_ROUND_CLOSEST(data->interval * 1000, HZ)); + return sysfs_emit(buf, "%u\n", update_intervals[convrate & 0x03]); } static ssize_t update_interval_store(struct device *dev, @@ -461,23 +359,17 @@ static ssize_t update_interval_store(struct device *dev, const char *buf, size_t count) { struct lm95234_data *data = dev_get_drvdata(dev); - int ret = lm95234_update_device(data); unsigned long val; - u8 regval; - - if (ret) - return ret; + int ret; ret = kstrtoul(buf, 10, &val); if (ret < 0) return ret; - regval = find_closest(val, update_intervals, ARRAY_SIZE(update_intervals)); - - mutex_lock(&data->update_lock); - data->interval = msecs_to_jiffies(update_intervals[regval]); - i2c_smbus_write_byte_data(data->client, LM95234_REG_CONVRATE, regval); - mutex_unlock(&data->update_lock); + val = find_closest(val, update_intervals, ARRAY_SIZE(update_intervals)); + ret = regmap_write(data->regmap, LM95234_REG_CONVRATE, val); + if (ret) + return ret; return count; } @@ -488,10 +380,10 @@ static SENSOR_DEVICE_ATTR_RO(temp3_input, temp, 2); static SENSOR_DEVICE_ATTR_RO(temp4_input, temp, 3); static SENSOR_DEVICE_ATTR_RO(temp5_input, temp, 4); -static SENSOR_DEVICE_ATTR_RO(temp2_fault, alarm, BIT(0) | BIT(1)); -static SENSOR_DEVICE_ATTR_RO(temp3_fault, alarm, BIT(2) | BIT(3)); -static SENSOR_DEVICE_ATTR_RO(temp4_fault, alarm, BIT(4) | BIT(5)); -static SENSOR_DEVICE_ATTR_RO(temp5_fault, alarm, BIT(6) | BIT(7)); +static SENSOR_DEVICE_ATTR_2_RO(temp2_fault, alarm, LM95234_REG_STS_FAULT, BIT(0) | BIT(1)); +static SENSOR_DEVICE_ATTR_2_RO(temp3_fault, alarm, LM95234_REG_STS_FAULT, BIT(2) | BIT(3)); +static SENSOR_DEVICE_ATTR_2_RO(temp4_fault, alarm, LM95234_REG_STS_FAULT, BIT(4) | BIT(5)); +static SENSOR_DEVICE_ATTR_2_RO(temp5_fault, alarm, LM95234_REG_STS_FAULT, BIT(6) | BIT(7)); static SENSOR_DEVICE_ATTR_RW(temp2_type, type, BIT(1)); static SENSOR_DEVICE_ATTR_RW(temp3_type, type, BIT(2)); @@ -510,11 +402,11 @@ static SENSOR_DEVICE_ATTR_RO(temp3_max_hyst, tcrit2_hyst, 1); static SENSOR_DEVICE_ATTR_RO(temp4_max_hyst, tcrit1_hyst, 3); static SENSOR_DEVICE_ATTR_RO(temp5_max_hyst, tcrit1_hyst, 4); -static SENSOR_DEVICE_ATTR_RO(temp1_max_alarm, alarm, BIT(0 + 8)); -static SENSOR_DEVICE_ATTR_RO(temp2_max_alarm, alarm, BIT(1 + 16)); -static SENSOR_DEVICE_ATTR_RO(temp3_max_alarm, alarm, BIT(2 + 16)); -static SENSOR_DEVICE_ATTR_RO(temp4_max_alarm, alarm, BIT(3 + 8)); -static SENSOR_DEVICE_ATTR_RO(temp5_max_alarm, alarm, BIT(4 + 8)); +static SENSOR_DEVICE_ATTR_2_RO(temp1_max_alarm, alarm, LM95234_REG_STS_TCRIT1, BIT(0)); +static SENSOR_DEVICE_ATTR_2_RO(temp2_max_alarm, alarm, LM95234_REG_STS_TCRIT2, BIT(1)); +static SENSOR_DEVICE_ATTR_2_RO(temp3_max_alarm, alarm, LM95234_REG_STS_TCRIT2, BIT(2)); +static SENSOR_DEVICE_ATTR_2_RO(temp4_max_alarm, alarm, LM95234_REG_STS_TCRIT1, BIT(3)); +static SENSOR_DEVICE_ATTR_2_RO(temp5_max_alarm, alarm, LM95234_REG_STS_TCRIT1, BIT(4)); static SENSOR_DEVICE_ATTR_RW(temp2_crit, tcrit1, 1); static SENSOR_DEVICE_ATTR_RW(temp3_crit, tcrit1, 2); @@ -522,8 +414,8 @@ static SENSOR_DEVICE_ATTR_RW(temp3_crit, tcrit1, 2); static SENSOR_DEVICE_ATTR_RO(temp2_crit_hyst, tcrit1_hyst, 1); static SENSOR_DEVICE_ATTR_RO(temp3_crit_hyst, tcrit1_hyst, 2); -static SENSOR_DEVICE_ATTR_RO(temp2_crit_alarm, alarm, BIT(1 + 8)); -static SENSOR_DEVICE_ATTR_RO(temp3_crit_alarm, alarm, BIT(2 + 8)); +static SENSOR_DEVICE_ATTR_2_RO(temp2_crit_alarm, alarm, LM95234_REG_STS_TCRIT1, BIT(1)); +static SENSOR_DEVICE_ATTR_2_RO(temp3_crit_alarm, alarm, LM95234_REG_STS_TCRIT1, BIT(2)); static SENSOR_DEVICE_ATTR_RW(temp2_offset, offset, 0); static SENSOR_DEVICE_ATTR_RW(temp3_offset, offset, 1); @@ -587,6 +479,45 @@ static const struct attribute_group lm95234_group = { .attrs = lm95234_attrs, }; +static bool lm95234_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case LM95234_REG_TEMPH(0) ... LM95234_REG_TEMPH(4): + case LM95234_REG_TEMPL(0) ... LM95234_REG_TEMPL(4): + case LM95234_REG_UTEMPH(0) ... LM95234_REG_UTEMPH(3): + case LM95234_REG_UTEMPL(0) ... LM95234_REG_UTEMPL(3): + case LM95234_REG_STS_FAULT: + case LM95234_REG_STS_TCRIT1: + case LM95234_REG_STS_TCRIT2: + case LM95234_REG_REM_MODEL_STS: + return true; + default: + return false; + } +} + +static bool lm95234_writeable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case LM95234_REG_CONFIG ... LM95234_REG_FILTER: + case LM95234_REG_REM_MODEL ... LM95234_REG_OFFSET(3): + case LM95234_REG_TCRIT1(0) ... LM95234_REG_TCRIT1(4): + case LM95234_REG_TCRIT2(0) ... LM95234_REG_TCRIT2(1): + case LM95234_REG_TCRIT_HYST: + return true; + default: + return false; + } +} + +static const struct regmap_config lm95234_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .writeable_reg = lm95234_writeable_reg, + .volatile_reg = lm95234_volatile_reg, + .cache_type = REGCACHE_MAPLE, +}; + static int lm95234_detect(struct i2c_client *client, struct i2c_board_info *info) { @@ -647,33 +578,30 @@ static int lm95234_detect(struct i2c_client *client, return 0; } -static int lm95234_init_client(struct i2c_client *client) +static int lm95234_init_client(struct device *dev, struct regmap *regmap) { - int val, model; + u32 val, model; + int ret; /* start conversion if necessary */ - val = i2c_smbus_read_byte_data(client, LM95234_REG_CONFIG); - if (val < 0) - return val; - if (val & 0x40) - i2c_smbus_write_byte_data(client, LM95234_REG_CONFIG, - val & ~0x40); + ret = regmap_clear_bits(regmap, LM95234_REG_CONFIG, 0x40); + if (ret) + return ret; /* If diode type status reports an error, try to fix it */ - val = i2c_smbus_read_byte_data(client, LM95234_REG_REM_MODEL_STS); - if (val < 0) - return val; - model = i2c_smbus_read_byte_data(client, LM95234_REG_REM_MODEL); - if (model < 0) - return model; + ret = regmap_read(regmap, LM95234_REG_REM_MODEL_STS, &val); + if (ret < 0) + return ret; + ret = regmap_read(regmap, LM95234_REG_REM_MODEL, &model); + if (ret < 0) + return ret; if (model & val) { - dev_notice(&client->dev, + dev_notice(dev, "Fixing remote diode type misconfiguration (0x%x)\n", val); - i2c_smbus_write_byte_data(client, LM95234_REG_REM_MODEL, - model & ~val); + ret = regmap_write(regmap, LM95234_REG_REM_MODEL, model & ~val); } - return 0; + return ret; } static int lm95234_probe(struct i2c_client *client) @@ -682,17 +610,22 @@ static int lm95234_probe(struct i2c_client *client) struct device *dev = &client->dev; struct lm95234_data *data; struct device *hwmon_dev; + struct regmap *regmap; int err; data = devm_kzalloc(dev, sizeof(struct lm95234_data), GFP_KERNEL); if (!data) return -ENOMEM; - data->client = client; + regmap = devm_regmap_init_i2c(client, &lm95234_regmap_config); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + data->regmap = regmap; mutex_init(&data->update_lock); /* Initialize the LM95234 chip */ - err = lm95234_init_client(client); + err = lm95234_init_client(dev, regmap); if (err < 0) return err; From f27ab9f05c8633d63e50d4c412e7425c2dbad176 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Wed, 10 Jul 2024 07:11:12 -0700 Subject: [PATCH 12/96] hwmon: (lm95234) Convert to with_info hwmon API Convert to with_info API to simplify the code and reduce its size. This patch reduces the object file size by about 30%. No functional change. Reviewed-by: Tzung-Bi Shih Signed-off-by: Guenter Roeck --- drivers/hwmon/lm95234.c | 557 ++++++++++++++++------------------------ 1 file changed, 227 insertions(+), 330 deletions(-) diff --git a/drivers/hwmon/lm95234.c b/drivers/hwmon/lm95234.c index 7a3aff1d183a..85350e9c9c4c 100644 --- a/drivers/hwmon/lm95234.c +++ b/drivers/hwmon/lm95234.c @@ -10,14 +10,12 @@ #include #include -#include #include #include #include #include #include #include -#include #include #define DRVNAME "lm95234" @@ -56,11 +54,11 @@ static const unsigned short normal_i2c[] = { /* Client data (each client gets its own) */ struct lm95234_data { struct regmap *regmap; - const struct attribute_group *groups[3]; struct mutex update_lock; + enum chips type; }; -static int lm95234_read_temp(struct regmap *regmap, int index, int *t) +static int lm95234_read_temp(struct regmap *regmap, int index, long *t) { int temp = 0, ret; u32 val; @@ -93,110 +91,7 @@ static int lm95234_read_temp(struct regmap *regmap, int index, int *t) return 0; } -static ssize_t temp_show(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct lm95234_data *data = dev_get_drvdata(dev); - int index = to_sensor_dev_attr(attr)->index; - int ret, temp; - - ret = lm95234_read_temp(data->regmap, index, &temp); - if (ret) - return ret; - - return sysfs_emit(buf, "%d\n", temp); -} - -static ssize_t alarm_show(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct lm95234_data *data = dev_get_drvdata(dev); - u8 mask = to_sensor_dev_attr_2(attr)->index; - u8 reg = to_sensor_dev_attr_2(attr)->nr; - int ret; - u32 val; - - ret = regmap_read(data->regmap, reg, &val); - if (ret) - return ret; - - return sysfs_emit(buf, "%u\n", !!(val & mask)); -} - -static ssize_t type_show(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct lm95234_data *data = dev_get_drvdata(dev); - u8 mask = to_sensor_dev_attr(attr)->index; - u32 val; - int ret; - - ret = regmap_read(data->regmap, LM95234_REG_REM_MODEL, &val); - if (ret) - return ret; - - return sysfs_emit(buf, "%s\n", val & mask ? "1" : "2"); -} - -static ssize_t type_store(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct lm95234_data *data = dev_get_drvdata(dev); - u8 mask = to_sensor_dev_attr(attr)->index; - unsigned long val; - int ret; - - ret = kstrtoul(buf, 10, &val); - if (ret < 0) - return ret; - - if (val != 1 && val != 2) - return -EINVAL; - - ret = regmap_update_bits(data->regmap, LM95234_REG_REM_MODEL, - mask, val == 1 ? mask : 0); - if (ret) - return ret; - return count; -} - -static ssize_t tcrit2_show(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct lm95234_data *data = dev_get_drvdata(dev); - int index = to_sensor_dev_attr(attr)->index; - int ret; - u32 tcrit2; - - ret = regmap_read(data->regmap, LM95234_REG_TCRIT2(index), &tcrit2); - if (ret) - return ret; - - return sysfs_emit(buf, "%u\n", tcrit2 * 1000); -} - -static ssize_t tcrit2_store(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct lm95234_data *data = dev_get_drvdata(dev); - int index = to_sensor_dev_attr(attr)->index; - long val; - int ret; - - ret = kstrtol(buf, 10, &val); - if (ret < 0) - return ret; - - val = DIV_ROUND_CLOSEST(clamp_val(val, 0, (index ? 255 : 127) * 1000), - 1000); - - ret = regmap_write(data->regmap, LM95234_REG_TCRIT2(index), val); - if (ret) - return ret; - return count; -} - -static ssize_t tcrit_hyst_show(struct lm95234_data *data, char *buf, int reg) +static int lm95234_hyst_get(struct lm95234_data *data, int reg, long *val) { u32 thyst, tcrit; int ret; @@ -212,80 +107,18 @@ unlock: return ret; /* Result can be negative, so be careful with unsigned operands */ - return sysfs_emit(buf, "%d\n", ((int)tcrit - (int)thyst) * 1000); + *val = ((int)tcrit - (int)thyst) * 1000; + return 0; } -static ssize_t tcrit2_hyst_show(struct device *dev, - struct device_attribute *attr, char *buf) +static ssize_t lm95234_hyst_set(struct lm95234_data *data, long val) { - struct lm95234_data *data = dev_get_drvdata(dev); - int index = to_sensor_dev_attr(attr)->index; - - return tcrit_hyst_show(data, buf, LM95234_REG_TCRIT2(index)); -} - -static ssize_t tcrit1_show(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct lm95234_data *data = dev_get_drvdata(dev); - int index = to_sensor_dev_attr(attr)->index; - int ret; - u32 val; - - ret = regmap_read(data->regmap, LM95234_REG_TCRIT1(index), &val); - if (ret) - return ret; - - return sysfs_emit(buf, "%u\n", val * 1000); -} - -static ssize_t tcrit1_store(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct lm95234_data *data = dev_get_drvdata(dev); - int index = to_sensor_dev_attr(attr)->index; - long val; - int ret; - - ret = kstrtol(buf, 10, &val); - if (ret < 0) - return ret; - - val = DIV_ROUND_CLOSEST(clamp_val(val, 0, 255000), 1000); - - ret = regmap_write(data->regmap, LM95234_REG_TCRIT1(index), val); - if (ret) - return ret; - - return count; -} - -static ssize_t tcrit1_hyst_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct lm95234_data *data = dev_get_drvdata(dev); - int index = to_sensor_dev_attr(attr)->index; - - return tcrit_hyst_show(data, buf, LM95234_REG_TCRIT1(index)); -} - -static ssize_t tcrit1_hyst_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - struct lm95234_data *data = dev_get_drvdata(dev); - int index = to_sensor_dev_attr(attr)->index; u32 tcrit; - long val; int ret; - ret = kstrtol(buf, 10, &val); - if (ret < 0) - return ret; - mutex_lock(&data->update_lock); - ret = regmap_read(data->regmap, LM95234_REG_TCRIT1(index), &tcrit); + ret = regmap_read(data->regmap, LM95234_REG_TCRIT1(0), &tcrit); if (ret) goto unlock; @@ -295,188 +128,255 @@ static ssize_t tcrit1_hyst_store(struct device *dev, ret = regmap_write(data->regmap, LM95234_REG_TCRIT_HYST, val); unlock: mutex_unlock(&data->update_lock); - if (ret) - return ret; - - return count; + return ret; } -static ssize_t offset_show(struct device *dev, struct device_attribute *attr, - char *buf) +static int lm95234_crit_reg(int channel) { - struct lm95234_data *data = dev_get_drvdata(dev); - int index = to_sensor_dev_attr(attr)->index; - u32 offset; - int ret; - - ret = regmap_read(data->regmap, LM95234_REG_OFFSET(index), &offset); - if (ret) - return ret; - - return sysfs_emit(buf, "%d\n", sign_extend32(offset, 7) * 500); + if (channel == 1 || channel == 2) + return LM95234_REG_TCRIT2(channel - 1); + return LM95234_REG_TCRIT1(channel); } -static ssize_t offset_store(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) +static int lm95234_temp_write(struct device *dev, u32 attr, int channel, long val) { struct lm95234_data *data = dev_get_drvdata(dev); - int index = to_sensor_dev_attr(attr)->index; - long val; + struct regmap *regmap = data->regmap; + + switch (attr) { + case hwmon_temp_type: + if (val != 1 && val != 2) + return -EINVAL; + return regmap_update_bits(regmap, LM95234_REG_REM_MODEL, + BIT(channel), + val == 1 ? BIT(channel) : 0); + case hwmon_temp_offset: + val = DIV_ROUND_CLOSEST(clamp_val(val, -64000, 63500), 500); + return regmap_write(regmap, LM95234_REG_OFFSET(channel - 1), val); + case hwmon_temp_max: + val = clamp_val(val, 0, channel == 1 ? 127000 : 255000); + val = DIV_ROUND_CLOSEST(val, 1000); + return regmap_write(regmap, lm95234_crit_reg(channel), val); + case hwmon_temp_max_hyst: + return lm95234_hyst_set(data, val); + case hwmon_temp_crit: + val = DIV_ROUND_CLOSEST(clamp_val(val, 0, 255000), 1000); + return regmap_write(regmap, LM95234_REG_TCRIT1(channel), val); + default: + return -EOPNOTSUPP; + } + return 0; +} + +static int lm95234_alarm_reg(int channel) +{ + if (channel == 1 || channel == 2) + return LM95234_REG_STS_TCRIT2; + return LM95234_REG_STS_TCRIT1; +} + +static int lm95234_temp_read(struct device *dev, u32 attr, int channel, long *val) +{ + struct lm95234_data *data = dev_get_drvdata(dev); + struct regmap *regmap = data->regmap; + u32 regval, mask; int ret; - ret = kstrtol(buf, 10, &val); - if (ret < 0) - return ret; - - /* Accuracy is 1/2 degrees C */ - val = DIV_ROUND_CLOSEST(clamp_val(val, -64000, 63500), 500); - - ret = regmap_write(data->regmap, LM95234_REG_OFFSET(index), val); - if (ret < 0) - return ret; - - return count; + switch (attr) { + case hwmon_temp_input: + return lm95234_read_temp(regmap, channel, val); + case hwmon_temp_max_alarm: + ret = regmap_read(regmap, lm95234_alarm_reg(channel), ®val); + if (ret) + return ret; + *val = !!(regval & BIT(channel)); + break; + case hwmon_temp_crit_alarm: + ret = regmap_read(regmap, LM95234_REG_STS_TCRIT1, ®val); + if (ret) + return ret; + *val = !!(regval & BIT(channel)); + break; + case hwmon_temp_crit_hyst: + return lm95234_hyst_get(data, LM95234_REG_TCRIT1(channel), val); + case hwmon_temp_type: + ret = regmap_read(regmap, LM95234_REG_REM_MODEL, ®val); + if (ret) + return ret; + *val = (regval & BIT(channel)) ? 1 : 2; + break; + case hwmon_temp_offset: + ret = regmap_read(regmap, LM95234_REG_OFFSET(channel - 1), ®val); + if (ret) + return ret; + *val = sign_extend32(regval, 7) * 500; + break; + case hwmon_temp_fault: + ret = regmap_read(regmap, LM95234_REG_STS_FAULT, ®val); + if (ret) + return ret; + mask = (BIT(0) | BIT(1)) << ((channel - 1) << 1); + *val = !!(regval & mask); + break; + case hwmon_temp_max: + ret = regmap_read(regmap, lm95234_crit_reg(channel), ®val); + if (ret) + return ret; + *val = regval * 1000; + break; + case hwmon_temp_max_hyst: + return lm95234_hyst_get(data, lm95234_crit_reg(channel), val); + case hwmon_temp_crit: + ret = regmap_read(regmap, LM95234_REG_TCRIT1(channel), ®val); + if (ret) + return ret; + *val = regval * 1000; + break; + default: + return -EOPNOTSUPP; + } + return 0; } static u16 update_intervals[] = { 143, 364, 1000, 2500 }; -static ssize_t update_interval_show(struct device *dev, - struct device_attribute *attr, char *buf) +static int lm95234_chip_write(struct device *dev, u32 attr, long val) +{ + struct lm95234_data *data = dev_get_drvdata(dev); + + switch (attr) { + case hwmon_chip_update_interval: + val = find_closest(val, update_intervals, ARRAY_SIZE(update_intervals)); + return regmap_write(data->regmap, LM95234_REG_CONVRATE, val); + default: + return -EOPNOTSUPP; + } + return 0; +} + +static int lm95234_chip_read(struct device *dev, u32 attr, long *val) { struct lm95234_data *data = dev_get_drvdata(dev); u32 convrate; int ret; - ret = regmap_read(data->regmap, LM95234_REG_CONVRATE, &convrate); - if (ret) - return ret; + switch (attr) { + case hwmon_chip_update_interval: + ret = regmap_read(data->regmap, LM95234_REG_CONVRATE, &convrate); + if (ret) + return ret; - return sysfs_emit(buf, "%u\n", update_intervals[convrate & 0x03]); + *val = update_intervals[convrate & 0x03]; + break; + default: + return -EOPNOTSUPP; + } + return 0; } -static ssize_t update_interval_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) +static int lm95234_write(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long val) { - struct lm95234_data *data = dev_get_drvdata(dev); - unsigned long val; - int ret; - - ret = kstrtoul(buf, 10, &val); - if (ret < 0) - return ret; - - val = find_closest(val, update_intervals, ARRAY_SIZE(update_intervals)); - ret = regmap_write(data->regmap, LM95234_REG_CONVRATE, val); - if (ret) - return ret; - - return count; + switch (type) { + case hwmon_chip: + return lm95234_chip_write(dev, attr, val); + case hwmon_temp: + return lm95234_temp_write(dev, attr, channel, val); + default: + return -EOPNOTSUPP; + } } -static SENSOR_DEVICE_ATTR_RO(temp1_input, temp, 0); -static SENSOR_DEVICE_ATTR_RO(temp2_input, temp, 1); -static SENSOR_DEVICE_ATTR_RO(temp3_input, temp, 2); -static SENSOR_DEVICE_ATTR_RO(temp4_input, temp, 3); -static SENSOR_DEVICE_ATTR_RO(temp5_input, temp, 4); +static int lm95234_read(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long *val) +{ + switch (type) { + case hwmon_chip: + return lm95234_chip_read(dev, attr, val); + case hwmon_temp: + return lm95234_temp_read(dev, attr, channel, val); + default: + return -EOPNOTSUPP; + } +} -static SENSOR_DEVICE_ATTR_2_RO(temp2_fault, alarm, LM95234_REG_STS_FAULT, BIT(0) | BIT(1)); -static SENSOR_DEVICE_ATTR_2_RO(temp3_fault, alarm, LM95234_REG_STS_FAULT, BIT(2) | BIT(3)); -static SENSOR_DEVICE_ATTR_2_RO(temp4_fault, alarm, LM95234_REG_STS_FAULT, BIT(4) | BIT(5)); -static SENSOR_DEVICE_ATTR_2_RO(temp5_fault, alarm, LM95234_REG_STS_FAULT, BIT(6) | BIT(7)); +static umode_t lm95234_is_visible(const void *_data, enum hwmon_sensor_types type, + u32 attr, int channel) +{ + const struct lm95234_data *data = _data; -static SENSOR_DEVICE_ATTR_RW(temp2_type, type, BIT(1)); -static SENSOR_DEVICE_ATTR_RW(temp3_type, type, BIT(2)); -static SENSOR_DEVICE_ATTR_RW(temp4_type, type, BIT(3)); -static SENSOR_DEVICE_ATTR_RW(temp5_type, type, BIT(4)); + if (data->type == lm95233 && channel > 2) + return 0; -static SENSOR_DEVICE_ATTR_RW(temp1_max, tcrit1, 0); -static SENSOR_DEVICE_ATTR_RW(temp2_max, tcrit2, 0); -static SENSOR_DEVICE_ATTR_RW(temp3_max, tcrit2, 1); -static SENSOR_DEVICE_ATTR_RW(temp4_max, tcrit1, 3); -static SENSOR_DEVICE_ATTR_RW(temp5_max, tcrit1, 4); + switch (type) { + case hwmon_chip: + switch (attr) { + case hwmon_chip_update_interval: + return 0644; + default: + break; + } + break; + case hwmon_temp: + switch (attr) { + case hwmon_temp_input: + case hwmon_temp_max_alarm: + return 0444; + case hwmon_temp_crit_alarm: + case hwmon_temp_crit_hyst: + return (channel && channel < 3) ? 0444 : 0; + case hwmon_temp_type: + case hwmon_temp_offset: + return channel ? 0644 : 0; + case hwmon_temp_fault: + return channel ? 0444 : 0; + case hwmon_temp_max: + return 0644; + case hwmon_temp_max_hyst: + return channel ? 0444 : 0644; + case hwmon_temp_crit: + return (channel && channel < 3) ? 0644 : 0; + default: + break; + } + break; + default: + break; + } + return 0; +} -static SENSOR_DEVICE_ATTR_RW(temp1_max_hyst, tcrit1_hyst, 0); -static SENSOR_DEVICE_ATTR_RO(temp2_max_hyst, tcrit2_hyst, 0); -static SENSOR_DEVICE_ATTR_RO(temp3_max_hyst, tcrit2_hyst, 1); -static SENSOR_DEVICE_ATTR_RO(temp4_max_hyst, tcrit1_hyst, 3); -static SENSOR_DEVICE_ATTR_RO(temp5_max_hyst, tcrit1_hyst, 4); - -static SENSOR_DEVICE_ATTR_2_RO(temp1_max_alarm, alarm, LM95234_REG_STS_TCRIT1, BIT(0)); -static SENSOR_DEVICE_ATTR_2_RO(temp2_max_alarm, alarm, LM95234_REG_STS_TCRIT2, BIT(1)); -static SENSOR_DEVICE_ATTR_2_RO(temp3_max_alarm, alarm, LM95234_REG_STS_TCRIT2, BIT(2)); -static SENSOR_DEVICE_ATTR_2_RO(temp4_max_alarm, alarm, LM95234_REG_STS_TCRIT1, BIT(3)); -static SENSOR_DEVICE_ATTR_2_RO(temp5_max_alarm, alarm, LM95234_REG_STS_TCRIT1, BIT(4)); - -static SENSOR_DEVICE_ATTR_RW(temp2_crit, tcrit1, 1); -static SENSOR_DEVICE_ATTR_RW(temp3_crit, tcrit1, 2); - -static SENSOR_DEVICE_ATTR_RO(temp2_crit_hyst, tcrit1_hyst, 1); -static SENSOR_DEVICE_ATTR_RO(temp3_crit_hyst, tcrit1_hyst, 2); - -static SENSOR_DEVICE_ATTR_2_RO(temp2_crit_alarm, alarm, LM95234_REG_STS_TCRIT1, BIT(1)); -static SENSOR_DEVICE_ATTR_2_RO(temp3_crit_alarm, alarm, LM95234_REG_STS_TCRIT1, BIT(2)); - -static SENSOR_DEVICE_ATTR_RW(temp2_offset, offset, 0); -static SENSOR_DEVICE_ATTR_RW(temp3_offset, offset, 1); -static SENSOR_DEVICE_ATTR_RW(temp4_offset, offset, 2); -static SENSOR_DEVICE_ATTR_RW(temp5_offset, offset, 3); - -static DEVICE_ATTR_RW(update_interval); - -static struct attribute *lm95234_common_attrs[] = { - &sensor_dev_attr_temp1_input.dev_attr.attr, - &sensor_dev_attr_temp2_input.dev_attr.attr, - &sensor_dev_attr_temp3_input.dev_attr.attr, - &sensor_dev_attr_temp2_fault.dev_attr.attr, - &sensor_dev_attr_temp3_fault.dev_attr.attr, - &sensor_dev_attr_temp2_type.dev_attr.attr, - &sensor_dev_attr_temp3_type.dev_attr.attr, - &sensor_dev_attr_temp1_max.dev_attr.attr, - &sensor_dev_attr_temp2_max.dev_attr.attr, - &sensor_dev_attr_temp3_max.dev_attr.attr, - &sensor_dev_attr_temp1_max_hyst.dev_attr.attr, - &sensor_dev_attr_temp2_max_hyst.dev_attr.attr, - &sensor_dev_attr_temp3_max_hyst.dev_attr.attr, - &sensor_dev_attr_temp1_max_alarm.dev_attr.attr, - &sensor_dev_attr_temp2_max_alarm.dev_attr.attr, - &sensor_dev_attr_temp3_max_alarm.dev_attr.attr, - &sensor_dev_attr_temp2_crit.dev_attr.attr, - &sensor_dev_attr_temp3_crit.dev_attr.attr, - &sensor_dev_attr_temp2_crit_hyst.dev_attr.attr, - &sensor_dev_attr_temp3_crit_hyst.dev_attr.attr, - &sensor_dev_attr_temp2_crit_alarm.dev_attr.attr, - &sensor_dev_attr_temp3_crit_alarm.dev_attr.attr, - &sensor_dev_attr_temp2_offset.dev_attr.attr, - &sensor_dev_attr_temp3_offset.dev_attr.attr, - &dev_attr_update_interval.attr, +static const struct hwmon_channel_info * const lm95234_info[] = { + HWMON_CHANNEL_INFO(chip, HWMON_C_UPDATE_INTERVAL), + HWMON_CHANNEL_INFO(temp, + HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MAX_HYST | + HWMON_T_MAX_ALARM, + HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MAX_HYST | + HWMON_T_MAX_ALARM | HWMON_T_FAULT | HWMON_T_TYPE | + HWMON_T_CRIT | HWMON_T_CRIT_HYST | + HWMON_T_CRIT_ALARM | HWMON_T_OFFSET, + HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MAX_HYST | + HWMON_T_MAX_ALARM | HWMON_T_FAULT | HWMON_T_TYPE | + HWMON_T_CRIT | HWMON_T_CRIT_HYST | + HWMON_T_CRIT_ALARM | HWMON_T_OFFSET, + HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MAX_HYST | + HWMON_T_MAX_ALARM | HWMON_T_FAULT | HWMON_T_TYPE | + HWMON_T_OFFSET, + HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MAX_HYST | + HWMON_T_MAX_ALARM | HWMON_T_FAULT | HWMON_T_TYPE | + HWMON_T_OFFSET), NULL }; -static const struct attribute_group lm95234_common_group = { - .attrs = lm95234_common_attrs, +static const struct hwmon_ops lm95234_hwmon_ops = { + .is_visible = lm95234_is_visible, + .read = lm95234_read, + .write = lm95234_write, }; -static struct attribute *lm95234_attrs[] = { - &sensor_dev_attr_temp4_input.dev_attr.attr, - &sensor_dev_attr_temp5_input.dev_attr.attr, - &sensor_dev_attr_temp4_fault.dev_attr.attr, - &sensor_dev_attr_temp5_fault.dev_attr.attr, - &sensor_dev_attr_temp4_type.dev_attr.attr, - &sensor_dev_attr_temp5_type.dev_attr.attr, - &sensor_dev_attr_temp4_max.dev_attr.attr, - &sensor_dev_attr_temp5_max.dev_attr.attr, - &sensor_dev_attr_temp4_max_hyst.dev_attr.attr, - &sensor_dev_attr_temp5_max_hyst.dev_attr.attr, - &sensor_dev_attr_temp4_max_alarm.dev_attr.attr, - &sensor_dev_attr_temp5_max_alarm.dev_attr.attr, - &sensor_dev_attr_temp4_offset.dev_attr.attr, - &sensor_dev_attr_temp5_offset.dev_attr.attr, - NULL -}; - -static const struct attribute_group lm95234_group = { - .attrs = lm95234_attrs, +static const struct hwmon_chip_info lm95234_chip_info = { + .ops = &lm95234_hwmon_ops, + .info = lm95234_info, }; static bool lm95234_volatile_reg(struct device *dev, unsigned int reg) @@ -606,7 +506,6 @@ static int lm95234_init_client(struct device *dev, struct regmap *regmap) static int lm95234_probe(struct i2c_client *client) { - enum chips type = (uintptr_t)i2c_get_match_data(client); struct device *dev = &client->dev; struct lm95234_data *data; struct device *hwmon_dev; @@ -617,6 +516,8 @@ static int lm95234_probe(struct i2c_client *client) if (!data) return -ENOMEM; + data->type = (uintptr_t)i2c_get_match_data(client); + regmap = devm_regmap_init_i2c(client, &lm95234_regmap_config); if (IS_ERR(regmap)) return PTR_ERR(regmap); @@ -629,12 +530,8 @@ static int lm95234_probe(struct i2c_client *client) if (err < 0) return err; - data->groups[0] = &lm95234_common_group; - if (type == lm95234) - data->groups[1] = &lm95234_group; - - hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name, - data, data->groups); + hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, + data, &lm95234_chip_info, NULL); return PTR_ERR_OR_ZERO(hwmon_dev); } From 280220236bf53a6eaa3c53653b649366228c4689 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Wed, 10 Jul 2024 15:57:18 -0700 Subject: [PATCH 13/96] hwmon: (lm95234) Add support for tempX_enable attribute LM95233/LM95234 support enabling temperature channels one by one. Add support for tempX_enable attribute to be able to use that functionality. Reviewed-by: Tzung-Bi Shih Signed-off-by: Guenter Roeck --- drivers/hwmon/lm95234.c | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/drivers/hwmon/lm95234.c b/drivers/hwmon/lm95234.c index 85350e9c9c4c..d3d4b1d82899 100644 --- a/drivers/hwmon/lm95234.c +++ b/drivers/hwmon/lm95234.c @@ -144,6 +144,11 @@ static int lm95234_temp_write(struct device *dev, u32 attr, int channel, long va struct regmap *regmap = data->regmap; switch (attr) { + case hwmon_temp_enable: + if (val && val != 1) + return -EINVAL; + return regmap_update_bits(regmap, LM95234_REG_ENABLE, + BIT(channel), val ? BIT(channel) : 0); case hwmon_temp_type: if (val != 1 && val != 2) return -EINVAL; @@ -183,6 +188,12 @@ static int lm95234_temp_read(struct device *dev, u32 attr, int channel, long *va int ret; switch (attr) { + case hwmon_temp_enable: + ret = regmap_read(regmap, LM95234_REG_ENABLE, ®val); + if (ret) + return ret; + *val = !!(regval & BIT(channel)); + break; case hwmon_temp_input: return lm95234_read_temp(regmap, channel, val); case hwmon_temp_max_alarm: @@ -331,6 +342,7 @@ static umode_t lm95234_is_visible(const void *_data, enum hwmon_sensor_types typ case hwmon_temp_fault: return channel ? 0444 : 0; case hwmon_temp_max: + case hwmon_temp_enable: return 0644; case hwmon_temp_max_hyst: return channel ? 0444 : 0644; @@ -350,21 +362,21 @@ static const struct hwmon_channel_info * const lm95234_info[] = { HWMON_CHANNEL_INFO(chip, HWMON_C_UPDATE_INTERVAL), HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MAX_HYST | - HWMON_T_MAX_ALARM, + HWMON_T_MAX_ALARM | HWMON_T_ENABLE, HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MAX_HYST | HWMON_T_MAX_ALARM | HWMON_T_FAULT | HWMON_T_TYPE | HWMON_T_CRIT | HWMON_T_CRIT_HYST | - HWMON_T_CRIT_ALARM | HWMON_T_OFFSET, + HWMON_T_CRIT_ALARM | HWMON_T_OFFSET | HWMON_T_ENABLE, HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MAX_HYST | HWMON_T_MAX_ALARM | HWMON_T_FAULT | HWMON_T_TYPE | HWMON_T_CRIT | HWMON_T_CRIT_HYST | - HWMON_T_CRIT_ALARM | HWMON_T_OFFSET, + HWMON_T_CRIT_ALARM | HWMON_T_OFFSET | HWMON_T_ENABLE, HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MAX_HYST | HWMON_T_MAX_ALARM | HWMON_T_FAULT | HWMON_T_TYPE | - HWMON_T_OFFSET, + HWMON_T_OFFSET | HWMON_T_ENABLE, HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MAX_HYST | HWMON_T_MAX_ALARM | HWMON_T_FAULT | HWMON_T_TYPE | - HWMON_T_OFFSET), + HWMON_T_OFFSET | HWMON_T_ENABLE), NULL }; From 3ec621cd6fcac82c1d8b3a40f774cd984a7fe55b Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Wed, 10 Jul 2024 17:37:54 -0700 Subject: [PATCH 14/96] hwmon: (lm95234) Use multi-byte regmap operations Use multi-byte regmap operations to simplify the code and to reduce dependency on locking. No functional change. Reviewed-by: Tzung-Bi Shih Signed-off-by: Guenter Roeck --- drivers/hwmon/lm95234.c | 45 +++++++++++++++++------------------------ 1 file changed, 18 insertions(+), 27 deletions(-) diff --git a/drivers/hwmon/lm95234.c b/drivers/hwmon/lm95234.c index d3d4b1d82899..7da6c8f07332 100644 --- a/drivers/hwmon/lm95234.c +++ b/drivers/hwmon/lm95234.c @@ -60,54 +60,45 @@ struct lm95234_data { static int lm95234_read_temp(struct regmap *regmap, int index, long *t) { + unsigned int regs[2]; int temp = 0, ret; - u32 val; + u8 regvals[2]; if (index) { - ret = regmap_read(regmap, LM95234_REG_UTEMPH(index - 1), &val); + regs[0] = LM95234_REG_UTEMPH(index - 1); + regs[1] = LM95234_REG_UTEMPL(index - 1); + ret = regmap_multi_reg_read(regmap, regs, regvals, 2); if (ret) return ret; - temp = val << 8; - ret = regmap_read(regmap, LM95234_REG_UTEMPL(index - 1), &val); - if (ret) - return ret; - temp |= val; + temp = (regvals[0] << 8) | regvals[1]; } /* * Read signed temperature if unsigned temperature is 0, * or if this is the local sensor. */ if (!temp) { - ret = regmap_read(regmap, LM95234_REG_TEMPH(index), &val); + regs[0] = LM95234_REG_TEMPH(index); + regs[1] = LM95234_REG_TEMPL(index); + ret = regmap_multi_reg_read(regmap, regs, regvals, 2); if (ret) return ret; - temp = val << 8; - ret = regmap_read(regmap, LM95234_REG_TEMPL(index), &val); - if (ret) - return ret; - temp = sign_extend32(temp | val, 15); + temp = (regvals[0] << 8) | regvals[1]; + temp = sign_extend32(temp, 15); } *t = DIV_ROUND_CLOSEST(temp * 125, 32); return 0; } -static int lm95234_hyst_get(struct lm95234_data *data, int reg, long *val) +static int lm95234_hyst_get(struct regmap *regmap, int reg, long *val) { - u32 thyst, tcrit; + unsigned int regs[2] = {reg, LM95234_REG_TCRIT_HYST}; + u8 regvals[2]; int ret; - mutex_lock(&data->update_lock); - ret = regmap_read(data->regmap, reg, &tcrit); - if (ret) - goto unlock; - ret = regmap_read(data->regmap, LM95234_REG_TCRIT_HYST, &thyst); -unlock: - mutex_unlock(&data->update_lock); + ret = regmap_multi_reg_read(regmap, regs, regvals, 2); if (ret) return ret; - - /* Result can be negative, so be careful with unsigned operands */ - *val = ((int)tcrit - (int)thyst) * 1000; + *val = (regvals[0] - regvals[1]) * 1000; return 0; } @@ -209,7 +200,7 @@ static int lm95234_temp_read(struct device *dev, u32 attr, int channel, long *va *val = !!(regval & BIT(channel)); break; case hwmon_temp_crit_hyst: - return lm95234_hyst_get(data, LM95234_REG_TCRIT1(channel), val); + return lm95234_hyst_get(regmap, LM95234_REG_TCRIT1(channel), val); case hwmon_temp_type: ret = regmap_read(regmap, LM95234_REG_REM_MODEL, ®val); if (ret) @@ -236,7 +227,7 @@ static int lm95234_temp_read(struct device *dev, u32 attr, int channel, long *va *val = regval * 1000; break; case hwmon_temp_max_hyst: - return lm95234_hyst_get(data, lm95234_crit_reg(channel), val); + return lm95234_hyst_get(regmap, lm95234_crit_reg(channel), val); case hwmon_temp_crit: ret = regmap_read(regmap, LM95234_REG_TCRIT1(channel), ®val); if (ret) From b4be51302d6873c23b4813676f851b5fbfa0f0fc Mon Sep 17 00:00:00 2001 From: Armin Wolf Date: Fri, 19 Jul 2024 22:37:06 +0200 Subject: [PATCH 15/96] hwmon: (dell-smm) Add Dell Latitude 7320 to fan control whitelist MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add the Dell Latitude 7320 to the fan control whitelist to allow for manual fan control. Closes: https://github.com/Wer-Wolf/i8kutils/issues/8 Signed-off-by: Armin Wolf Acked-by: Pali Rohár Link: https://lore.kernel.org/r/20240719203706.19466-1-W_Armin@gmx.de Signed-off-by: Guenter Roeck --- drivers/hwmon/dell-smm-hwmon.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/hwmon/dell-smm-hwmon.c b/drivers/hwmon/dell-smm-hwmon.c index 0362a13f6525..f9b3a3030f13 100644 --- a/drivers/hwmon/dell-smm-hwmon.c +++ b/drivers/hwmon/dell-smm-hwmon.c @@ -1488,6 +1488,14 @@ static const struct dmi_system_id i8k_whitelist_fan_control[] __initconst = { }, .driver_data = (void *)&i8k_fan_control_data[I8K_FAN_34A3_35A3], }, + { + .ident = "Dell Latitude 7320", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Latitude 7320"), + }, + .driver_data = (void *)&i8k_fan_control_data[I8K_FAN_30A3_31A3], + }, { .ident = "Dell Latitude E6440", .matches = { From 97adb1aacef8f3f3ec3363ac1f8b72db432f55f0 Mon Sep 17 00:00:00 2001 From: Javier Carrasco Date: Sun, 21 Jul 2024 17:19:02 +0200 Subject: [PATCH 16/96] hwmon: (ltc2992) use device_for_each_child_node_scoped() to access child nodes The iterated nodes are direct children of the device node, and the `device_for_each_child_node()` macro accounts for child node availability. `fwnode_for_each_available_child_node()` is meant to access the child nodes of an fwnode, and therefore not direct child nodes of the device node. In this case, the child nodes are not required outside the loop, and the scoped version of the macro can be used to remove the repetitive `goto put` pattern. Use `device_for_each_child_node_scoped_scoped()` to indicate device's direct child nodes. Reviewed-by: Jonathan Cameron Signed-off-by: Javier Carrasco Link: https://lore.kernel.org/r/20240721-device_for_each_child_node-available-v2-2-f33748fd8b2d@gmail.com Signed-off-by: Guenter Roeck --- drivers/hwmon/ltc2992.c | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/drivers/hwmon/ltc2992.c b/drivers/hwmon/ltc2992.c index d4a93223cd3b..541fa09dc6e7 100644 --- a/drivers/hwmon/ltc2992.c +++ b/drivers/hwmon/ltc2992.c @@ -854,33 +854,24 @@ static const struct regmap_config ltc2992_regmap_config = { static int ltc2992_parse_dt(struct ltc2992_state *st) { - struct fwnode_handle *fwnode; - struct fwnode_handle *child; u32 addr; u32 val; int ret; - fwnode = dev_fwnode(&st->client->dev); - - fwnode_for_each_available_child_node(fwnode, child) { + device_for_each_child_node_scoped(&st->client->dev, child) { ret = fwnode_property_read_u32(child, "reg", &addr); - if (ret < 0) { - fwnode_handle_put(child); + if (ret < 0) return ret; - } - if (addr > 1) { - fwnode_handle_put(child); + if (addr > 1) return -EINVAL; - } ret = fwnode_property_read_u32(child, "shunt-resistor-micro-ohms", &val); if (!ret) { - if (!val) { - fwnode_handle_put(child); + if (!val) return dev_err_probe(&st->client->dev, -EINVAL, "shunt resistor value cannot be zero\n"); - } + st->r_sense_uohm[addr] = val; } } From 744ec4477b11c42e2c8de9eb8364675ae7a0bd81 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Thu, 18 Jul 2024 09:52:01 -0700 Subject: [PATCH 17/96] hwmon: (max16065) Fix overflows seen when writing limits Writing large limits resulted in overflows as reported by module tests. in0_lcrit: Suspected overflow: [max=5538, read 0, written 2147483647] in0_crit: Suspected overflow: [max=5538, read 0, written 2147483647] in0_min: Suspected overflow: [max=5538, read 0, written 2147483647] Fix the problem by clamping prior to multiplications and the use of DIV_ROUND_CLOSEST, and by using consistent variable types. Reviewed-by: Tzung-Bi Shih Fixes: f5bae2642e3d ("hwmon: Driver for MAX16065 System Manager and compatibles") Signed-off-by: Guenter Roeck --- drivers/hwmon/max16065.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/hwmon/max16065.c b/drivers/hwmon/max16065.c index 7ce9a89f93a0..5b2a174c6bad 100644 --- a/drivers/hwmon/max16065.c +++ b/drivers/hwmon/max16065.c @@ -114,9 +114,10 @@ static inline int LIMIT_TO_MV(int limit, int range) return limit * range / 256; } -static inline int MV_TO_LIMIT(int mv, int range) +static inline int MV_TO_LIMIT(unsigned long mv, int range) { - return clamp_val(DIV_ROUND_CLOSEST(mv * 256, range), 0, 255); + mv = clamp_val(mv, 0, ULONG_MAX / 256); + return DIV_ROUND_CLOSEST(clamp_val(mv * 256, 0, range * 255), range); } static inline int ADC_TO_CURR(int adc, int gain) From 119abf7d1815f098f7f91ae7abc84324a19943d7 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sun, 21 Jul 2024 06:41:17 -0700 Subject: [PATCH 18/96] hwmon: (max16065) Fix alarm attributes Chips reporting overcurrent alarms report it in the second alarm register. That means the second alarm register has to be read, even if the chip only supports 8 or fewer ADC channels. MAX16067 and MAX16068 report undervoltage and overvoltage alarms in separate registers. Fold register contents together to report both with the existing alarm attribute. This requires actually storing the chip type in struct max16065_data. Rename the variable 'chip' to match the variable name used in the probe function. Reviewed-by: Tzung-Bi Shih Fixes: f5bae2642e3d ("hwmon: Driver for MAX16065 System Manager and compatibles") Signed-off-by: Guenter Roeck --- drivers/hwmon/max16065.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/drivers/hwmon/max16065.c b/drivers/hwmon/max16065.c index 5b2a174c6bad..0ccb5eb596fc 100644 --- a/drivers/hwmon/max16065.c +++ b/drivers/hwmon/max16065.c @@ -79,7 +79,7 @@ static const bool max16065_have_current[] = { }; struct max16065_data { - enum chips type; + enum chips chip; struct i2c_client *client; const struct attribute_group *groups[4]; struct mutex update_lock; @@ -162,10 +162,17 @@ static struct max16065_data *max16065_update_device(struct device *dev) MAX16065_CURR_SENSE); } - for (i = 0; i < DIV_ROUND_UP(data->num_adc, 8); i++) + for (i = 0; i < 2; i++) data->fault[i] = i2c_smbus_read_byte_data(client, MAX16065_FAULT(i)); + /* + * MAX16067 and MAX16068 have separate undervoltage and + * overvoltage alarm bits. Squash them together. + */ + if (data->chip == max16067 || data->chip == max16068) + data->fault[0] |= data->fault[1]; + data->last_updated = jiffies; data->valid = true; } @@ -514,6 +521,7 @@ static int max16065_probe(struct i2c_client *client) if (unlikely(!data)) return -ENOMEM; + data->chip = chip; data->client = client; mutex_init(&data->update_lock); From 351047fc5947a9549bae237104b800a89923a04d Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Fri, 12 Jul 2024 13:06:30 -0700 Subject: [PATCH 19/96] hwmon: (max6697) Reorder include files Reorder include files to alphabetic order to improve maintainability. Reviewed-by: Tzung-Bi Shih Signed-off-by: Guenter Roeck --- drivers/hwmon/max6697.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/hwmon/max6697.c b/drivers/hwmon/max6697.c index 20981f9443dd..9a2a21230c7d 100644 --- a/drivers/hwmon/max6697.c +++ b/drivers/hwmon/max6697.c @@ -6,16 +6,16 @@ * Copyright (c) 2011 David George */ -#include -#include -#include -#include -#include +#include #include #include -#include +#include +#include +#include +#include #include #include +#include #include From f81489a136ac2c18a7b9bb22cf37c0a6f6d58545 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Fri, 12 Jul 2024 13:57:13 -0700 Subject: [PATCH 20/96] hwmon: (max6697) Drop platform data support Platform data is not used anywhere in the upstram kernel. Drop support for it to simplify code maintenance. Reviewed-by: Tzung-Bi Shih Signed-off-by: Guenter Roeck --- drivers/hwmon/max6697.c | 174 ++++++++++++-------------- include/linux/platform_data/max6697.h | 33 ----- 2 files changed, 81 insertions(+), 126 deletions(-) delete mode 100644 include/linux/platform_data/max6697.h diff --git a/drivers/hwmon/max6697.c b/drivers/hwmon/max6697.c index 9a2a21230c7d..b9766533d876 100644 --- a/drivers/hwmon/max6697.c +++ b/drivers/hwmon/max6697.c @@ -17,8 +17,6 @@ #include #include -#include - enum chips { max6581, max6602, max6622, max6636, max6689, max6693, max6694, max6697, max6698, max6699 }; @@ -558,54 +556,97 @@ static const struct attribute_group max6697_group = { }; __ATTRIBUTE_GROUPS(max6697); -static void max6697_get_config_of(struct device_node *node, - struct max6697_platform_data *pdata) +static int max6697_config_of(struct max6697_data *data, struct i2c_client *client) { - int len; - const __be32 *prop; + const struct max6697_chip_data *chip = data->chip; + struct device_node *node = client->dev.of_node; + int ret, confreg; + int factor = 0; + u32 vals[2]; - pdata->smbus_timeout_disable = - of_property_read_bool(node, "smbus-timeout-disable"); - pdata->extended_range_enable = - of_property_read_bool(node, "extended-range-enable"); - pdata->beta_compensation = - of_property_read_bool(node, "beta-compensation-enable"); + confreg = 0; + if (of_property_read_bool(node, "smbus-timeout-disable") && + (chip->valid_conf & MAX6697_CONF_TIMEOUT)) { + confreg |= MAX6697_CONF_TIMEOUT; + } + if (of_property_read_bool(node, "extended-range-enable") && + (chip->valid_conf & MAX6581_CONF_EXTENDED)) { + confreg |= MAX6581_CONF_EXTENDED; + data->temp_offset = 64; + } + if (of_property_read_bool(node, "beta-compensation-enable") && + (chip->valid_conf & MAX6693_CONF_BETA)) { + confreg |= MAX6693_CONF_BETA; + } - prop = of_get_property(node, "alert-mask", &len); - if (prop && len == sizeof(u32)) - pdata->alert_mask = be32_to_cpu(prop[0]); - prop = of_get_property(node, "over-temperature-mask", &len); - if (prop && len == sizeof(u32)) - pdata->over_temperature_mask = be32_to_cpu(prop[0]); - prop = of_get_property(node, "resistance-cancellation", &len); - if (prop) { - if (len == sizeof(u32)) - pdata->resistance_cancellation = be32_to_cpu(prop[0]); - else - pdata->resistance_cancellation = 0xfe; - } - prop = of_get_property(node, "transistor-ideality", &len); - if (prop && len == 2 * sizeof(u32)) { - pdata->ideality_mask = be32_to_cpu(prop[0]); - pdata->ideality_value = be32_to_cpu(prop[1]); + if (of_property_read_u32(node, "alert-mask", vals)) + vals[0] = 0; + ret = i2c_smbus_write_byte_data(client, MAX6697_REG_ALERT_MASK, + MAX6697_ALERT_MAP_BITS(vals[0])); + if (ret) + return ret; + + if (of_property_read_u32(node, "over-temperature-mask", vals)) + vals[0] = 0; + ret = i2c_smbus_write_byte_data(client, MAX6697_REG_OVERT_MASK, + MAX6697_OVERT_MAP_BITS(vals[0])); + if (ret) + return ret; + + if (data->type != max6581) { + if (of_property_read_bool(node, "resistance-cancellation") && + chip->valid_conf & MAX6697_CONF_RESISTANCE) { + confreg |= MAX6697_CONF_RESISTANCE; + factor = 1; + } + } else { + if (of_property_read_u32(node, "resistance-cancellation", &vals[0])) { + if (of_property_read_bool(node, "resistance-cancellation")) + vals[0] = 0xfe; + else + vals[0] = 0; + } + + vals[0] &= 0xfe; + factor = hweight8(vals[0]); + ret = i2c_smbus_write_byte_data(client, MAX6581_REG_RESISTANCE, + vals[0] >> 1); + if (ret < 0) + return ret; + + if (of_property_read_u32_array(node, "transistor-ideality", vals, 2)) { + vals[0] = 0; + vals[1] = 0; + } + + ret = i2c_smbus_write_byte_data(client, MAX6581_REG_IDEALITY, + vals[1]); + if (ret < 0) + return ret; + ret = i2c_smbus_write_byte_data(client, + MAX6581_REG_IDEALITY_SELECT, + (vals[0] & 0xfe) >> 1); + if (ret < 0) + return ret; } + ret = i2c_smbus_write_byte_data(client, MAX6697_REG_CONFIG, confreg); + if (ret < 0) + return ret; + return factor; } static int max6697_init_chip(struct max6697_data *data, struct i2c_client *client) { - struct max6697_platform_data *pdata = dev_get_platdata(&client->dev); - struct max6697_platform_data p; const struct max6697_chip_data *chip = data->chip; int factor = chip->channels; int ret, reg; /* - * Don't touch configuration if neither platform data nor OF - * configuration was specified. If that is the case, use the - * current chip configuration. + * Don't touch configuration if there is no devicetree configuration. + * If that is the case, use the current chip configuration. */ - if (!pdata && !client->dev.of_node) { + if (!client->dev.of_node) { reg = i2c_smbus_read_byte_data(client, MAX6697_REG_CONFIG); if (reg < 0) return reg; @@ -621,67 +662,14 @@ static int max6697_init_chip(struct max6697_data *data, if (reg & MAX6697_CONF_RESISTANCE) factor++; } - goto done; - } - - if (client->dev.of_node) { - memset(&p, 0, sizeof(p)); - max6697_get_config_of(client->dev.of_node, &p); - pdata = &p; - } - - reg = 0; - if (pdata->smbus_timeout_disable && - (chip->valid_conf & MAX6697_CONF_TIMEOUT)) { - reg |= MAX6697_CONF_TIMEOUT; - } - if (pdata->extended_range_enable && - (chip->valid_conf & MAX6581_CONF_EXTENDED)) { - reg |= MAX6581_CONF_EXTENDED; - data->temp_offset = 64; - } - if (pdata->resistance_cancellation && - (chip->valid_conf & MAX6697_CONF_RESISTANCE)) { - reg |= MAX6697_CONF_RESISTANCE; - factor++; - } - if (pdata->beta_compensation && - (chip->valid_conf & MAX6693_CONF_BETA)) { - reg |= MAX6693_CONF_BETA; - } - - ret = i2c_smbus_write_byte_data(client, MAX6697_REG_CONFIG, reg); - if (ret < 0) - return ret; - - ret = i2c_smbus_write_byte_data(client, MAX6697_REG_ALERT_MASK, - MAX6697_ALERT_MAP_BITS(pdata->alert_mask)); - if (ret < 0) - return ret; - - ret = i2c_smbus_write_byte_data(client, MAX6697_REG_OVERT_MASK, - MAX6697_OVERT_MAP_BITS(pdata->over_temperature_mask)); - if (ret < 0) - return ret; - - if (data->type == max6581) { - factor += hweight8(pdata->resistance_cancellation >> 1); - ret = i2c_smbus_write_byte_data(client, MAX6581_REG_RESISTANCE, - pdata->resistance_cancellation >> 1); - if (ret < 0) - return ret; - ret = i2c_smbus_write_byte_data(client, MAX6581_REG_IDEALITY, - pdata->ideality_value); - if (ret < 0) - return ret; - ret = i2c_smbus_write_byte_data(client, - MAX6581_REG_IDEALITY_SELECT, - pdata->ideality_mask >> 1); + data->update_interval = factor * MAX6697_CONV_TIME; + } else { + ret = max6697_config_of(data, client); if (ret < 0) return ret; + data->update_interval = (factor + ret) * MAX6697_CONV_TIME; } -done: - data->update_interval = factor * MAX6697_CONV_TIME; + return 0; } diff --git a/include/linux/platform_data/max6697.h b/include/linux/platform_data/max6697.h deleted file mode 100644 index 6fbb70005541..000000000000 --- a/include/linux/platform_data/max6697.h +++ /dev/null @@ -1,33 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * max6697.h - * Copyright (c) 2012 Guenter Roeck - */ - -#ifndef MAX6697_H -#define MAX6697_H - -#include - -/* - * For all bit masks: - * bit 0: local temperature - * bit 1..7: remote temperatures - */ -struct max6697_platform_data { - bool smbus_timeout_disable; /* set to disable SMBus timeouts */ - bool extended_range_enable; /* set to enable extended temp range */ - bool beta_compensation; /* set to enable beta compensation */ - u8 alert_mask; /* set bit to 1 to disable alert */ - u8 over_temperature_mask; /* set bit to 1 to disable */ - u8 resistance_cancellation; /* set bit to 0 to disable - * bit mask for MAX6581, - * boolean for other chips - */ - u8 ideality_mask; /* set bit to 0 to disable */ - u8 ideality_value; /* transistor ideality as per - * MAX6581 datasheet - */ -}; - -#endif /* MAX6697_H */ From c02b25dcf64b07990a75292a83486b5348f3561e Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Fri, 12 Jul 2024 14:46:19 -0700 Subject: [PATCH 21/96] hwmon: (max6697) Use bit operations where possible Use bit operations to improve code maintainability. Reviewed-by: Tzung-Bi Shih Signed-off-by: Guenter Roeck --- drivers/hwmon/max6697.c | 43 +++++++++++++++++++++++++++-------------- 1 file changed, 28 insertions(+), 15 deletions(-) diff --git a/drivers/hwmon/max6697.c b/drivers/hwmon/max6697.c index b9766533d876..74bae3586fa6 100644 --- a/drivers/hwmon/max6697.c +++ b/drivers/hwmon/max6697.c @@ -6,6 +6,8 @@ * Copyright (c) 2011 David George */ +#include +#include #include #include #include @@ -32,20 +34,31 @@ static const u8 MAX6697_REG_CRIT[] = { 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27 }; /* - * Map device tree / platform data register bit map to chip bit map. + * Map device tree / internal register bit map to chip bit map. * Applies to alert register and over-temperature register. */ + +#define MAX6697_EXTERNAL_MASK_DT GENMASK(7, 1) +#define MAX6697_LOCAL_MASK_DT BIT(0) +#define MAX6697_EXTERNAL_MASK_CHIP GENMASK(6, 0) +#define MAX6697_LOCAL_MASK_CHIP BIT(7) + +/* alert - local channel is in bit 6 */ #define MAX6697_ALERT_MAP_BITS(reg) ((((reg) & 0x7e) >> 1) | \ (((reg) & 0x01) << 6) | ((reg) & 0x80)) -#define MAX6697_OVERT_MAP_BITS(reg) (((reg) >> 1) | (((reg) & 0x01) << 7)) + +/* over-temperature - local channel is in bit 7 */ +#define MAX6697_OVERT_MAP_BITS(reg) \ + (FIELD_PREP(MAX6697_EXTERNAL_MASK_CHIP, FIELD_GET(MAX6697_EXTERNAL_MASK_DT, reg)) | \ + FIELD_PREP(MAX6697_LOCAL_MASK_CHIP, FIELD_GET(MAX6697_LOCAL_MASK_DT, reg))) #define MAX6697_REG_STAT(n) (0x44 + (n)) #define MAX6697_REG_CONFIG 0x41 -#define MAX6581_CONF_EXTENDED (1 << 1) -#define MAX6693_CONF_BETA (1 << 2) -#define MAX6697_CONF_RESISTANCE (1 << 3) -#define MAX6697_CONF_TIMEOUT (1 << 5) +#define MAX6581_CONF_EXTENDED BIT(1) +#define MAX6693_CONF_BETA BIT(2) +#define MAX6697_CONF_RESISTANCE BIT(3) +#define MAX6697_CONF_TIMEOUT BIT(5) #define MAX6697_REG_ALERT_MASK 0x42 #define MAX6697_REG_OVERT_MASK 0x43 @@ -193,7 +206,7 @@ static struct max6697_data *max6697_update_device(struct device *dev) goto abort; for (i = 0; i < data->chip->channels; i++) { - if (data->chip->have_ext & (1 << i)) { + if (data->chip->have_ext & BIT(i)) { val = i2c_smbus_read_byte_data(client, MAX6697_REG_TEMP_EXT[i]); if (unlikely(val < 0)) { @@ -217,7 +230,7 @@ static struct max6697_data *max6697_update_device(struct device *dev) } data->temp[i][MAX6697_TEMP_MAX] = val; - if (data->chip->have_crit & (1 << i)) { + if (data->chip->have_crit & BIT(i)) { val = i2c_smbus_read_byte_data(client, MAX6697_REG_CRIT[i]); if (unlikely(val < 0)) { @@ -291,7 +304,7 @@ static ssize_t alarm_show(struct device *dev, struct device_attribute *attr, if (data->chip->alarm_map) index = data->chip->alarm_map[index]; - return sprintf(buf, "%u\n", (data->alarms >> index) & 0x1); + return sprintf(buf, "%u\n", !!(data->alarms & BIT(index))); } static ssize_t temp_store(struct device *dev, @@ -342,20 +355,20 @@ static ssize_t offset_store(struct device *dev, struct device_attribute *devattr ret = select; goto abort; } - channel_enabled = (select & (1 << (index - 1))); + channel_enabled = (select & BIT(index - 1)); temp = clamp_val(temp, MAX6581_OFFSET_MIN, MAX6581_OFFSET_MAX); val = DIV_ROUND_CLOSEST(temp, 250); /* disable the offset for channel if the new offset is 0 */ if (val == 0) { if (channel_enabled) ret = i2c_smbus_write_byte_data(data->client, MAX6581_REG_OFFSET_SELECT, - select & ~(1 << (index - 1))); + select & ~BIT(index - 1)); ret = ret < 0 ? ret : count; goto abort; } if (!channel_enabled) { ret = i2c_smbus_write_byte_data(data->client, MAX6581_REG_OFFSET_SELECT, - select | (1 << (index - 1))); + select | BIT(index - 1)); if (ret < 0) goto abort; } @@ -378,7 +391,7 @@ static ssize_t offset_show(struct device *dev, struct device_attribute *devattr, select = i2c_smbus_read_byte_data(data->client, MAX6581_REG_OFFSET_SELECT); if (select < 0) ret = select; - else if (select & (1 << (index - 1))) + else if (select & BIT(index - 1)) ret = i2c_smbus_read_byte_data(data->client, MAX6581_REG_OFFSET); else ret = 0; @@ -467,9 +480,9 @@ static umode_t max6697_is_visible(struct kobject *kobj, struct attribute *attr, if (channel >= chip->channels) return 0; - if ((nr == 3 || nr == 4) && !(chip->have_crit & (1 << channel))) + if ((nr == 3 || nr == 4) && !(chip->have_crit & BIT(channel))) return 0; - if (nr == 5 && !(chip->have_fault & (1 << channel))) + if (nr == 5 && !(chip->have_fault & BIT(channel))) return 0; /* offset reg is only supported on max6581 remote channels */ if (nr == 6) From 3a2a8cc3fe24ec48a3d222f5b9db68a3404ffab3 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Fri, 12 Jul 2024 15:45:38 -0700 Subject: [PATCH 22/96] hwmon: (max6697) Convert to use regmap Use regmap for register caching, and use regmap API for bit operations to simplify the code. This patch reduces object file size by approximately 10%. Reviewed-by: Tzung-Bi Shih Signed-off-by: Guenter Roeck --- drivers/hwmon/max6697.c | 317 +++++++++++++++------------------------- 1 file changed, 121 insertions(+), 196 deletions(-) diff --git a/drivers/hwmon/max6697.c b/drivers/hwmon/max6697.c index 74bae3586fa6..90cb203c66e2 100644 --- a/drivers/hwmon/max6697.c +++ b/drivers/hwmon/max6697.c @@ -13,10 +13,10 @@ #include #include #include -#include #include #include #include +#include #include enum chips { max6581, max6602, max6622, max6636, max6689, max6693, max6694, @@ -82,20 +82,15 @@ struct max6697_chip_data { }; struct max6697_data { - struct i2c_client *client; + struct regmap *regmap; enum chips type; const struct max6697_chip_data *chip; - int update_interval; /* in milli-seconds */ int temp_offset; /* in degrees C */ struct mutex update_lock; - unsigned long last_updated; /* In jiffies */ - bool valid; /* true if following fields are valid */ - /* 1x local and up to 7x remote */ - u8 temp[8][4]; /* [nr][0]=temp [1]=ext [2]=max [3]=crit */ #define MAX6697_TEMP_INPUT 0 #define MAX6697_TEMP_EXT 1 #define MAX6697_TEMP_MAX 2 @@ -189,88 +184,22 @@ static inline int max6581_offset_to_millic(int val) return sign_extend32(val, 7) * 250; } -static struct max6697_data *max6697_update_device(struct device *dev) -{ - struct max6697_data *data = dev_get_drvdata(dev); - struct i2c_client *client = data->client; - struct max6697_data *ret = data; - int val; - int i; - u32 alarms; - - mutex_lock(&data->update_lock); - - if (data->valid && - !time_after(jiffies, data->last_updated - + msecs_to_jiffies(data->update_interval))) - goto abort; - - for (i = 0; i < data->chip->channels; i++) { - if (data->chip->have_ext & BIT(i)) { - val = i2c_smbus_read_byte_data(client, - MAX6697_REG_TEMP_EXT[i]); - if (unlikely(val < 0)) { - ret = ERR_PTR(val); - goto abort; - } - data->temp[i][MAX6697_TEMP_EXT] = val; - } - - val = i2c_smbus_read_byte_data(client, MAX6697_REG_TEMP[i]); - if (unlikely(val < 0)) { - ret = ERR_PTR(val); - goto abort; - } - data->temp[i][MAX6697_TEMP_INPUT] = val; - - val = i2c_smbus_read_byte_data(client, MAX6697_REG_MAX[i]); - if (unlikely(val < 0)) { - ret = ERR_PTR(val); - goto abort; - } - data->temp[i][MAX6697_TEMP_MAX] = val; - - if (data->chip->have_crit & BIT(i)) { - val = i2c_smbus_read_byte_data(client, - MAX6697_REG_CRIT[i]); - if (unlikely(val < 0)) { - ret = ERR_PTR(val); - goto abort; - } - data->temp[i][MAX6697_TEMP_CRIT] = val; - } - } - - alarms = 0; - for (i = 0; i < 3; i++) { - val = i2c_smbus_read_byte_data(client, MAX6697_REG_STAT(i)); - if (unlikely(val < 0)) { - ret = ERR_PTR(val); - goto abort; - } - alarms = (alarms << 8) | val; - } - data->alarms = alarms; - data->last_updated = jiffies; - data->valid = true; -abort: - mutex_unlock(&data->update_lock); - - return ret; -} - static ssize_t temp_input_show(struct device *dev, struct device_attribute *devattr, char *buf) { + struct max6697_data *data = dev_get_drvdata(dev); int index = to_sensor_dev_attr(devattr)->index; - struct max6697_data *data = max6697_update_device(dev); - int temp; + unsigned int regs[2] = { MAX6697_REG_TEMP[index], + MAX6697_REG_TEMP_EXT[index] }; + u8 regdata[2] = { }; + int temp, ret; - if (IS_ERR(data)) - return PTR_ERR(data); + ret = regmap_multi_reg_read(data->regmap, regs, regdata, + data->chip->have_ext & BIT(index) ? 2 : 1); + if (ret) + return ret; - temp = (data->temp[index][MAX6697_TEMP_INPUT] - data->temp_offset) << 3; - temp |= data->temp[index][MAX6697_TEMP_EXT] >> 5; + temp = ((regdata[0] - data->temp_offset) << 3) | (regdata[1] >> 5); return sprintf(buf, "%d\n", temp * 125); } @@ -278,33 +207,41 @@ static ssize_t temp_input_show(struct device *dev, static ssize_t temp_show(struct device *dev, struct device_attribute *devattr, char *buf) { - int nr = to_sensor_dev_attr_2(devattr)->nr; + struct max6697_data *data = dev_get_drvdata(dev); int index = to_sensor_dev_attr_2(devattr)->index; - struct max6697_data *data = max6697_update_device(dev); - int temp; + int nr = to_sensor_dev_attr_2(devattr)->nr; + unsigned int temp; + int reg, ret; - if (IS_ERR(data)) - return PTR_ERR(data); + if (index == MAX6697_TEMP_MAX) + reg = MAX6697_REG_MAX[nr]; + else + reg = MAX6697_REG_CRIT[nr]; - temp = data->temp[nr][index]; - temp -= data->temp_offset; + ret = regmap_read(data->regmap, reg, &temp); + if (ret) + return ret; - return sprintf(buf, "%d\n", temp * 1000); + return sprintf(buf, "%d\n", ((int)temp - data->temp_offset) * 1000); } static ssize_t alarm_show(struct device *dev, struct device_attribute *attr, char *buf) { + struct max6697_data *data = dev_get_drvdata(dev); int index = to_sensor_dev_attr(attr)->index; - struct max6697_data *data = max6697_update_device(dev); - - if (IS_ERR(data)) - return PTR_ERR(data); + unsigned int alarms; + int reg, ret; if (data->chip->alarm_map) index = data->chip->alarm_map[index]; - return sprintf(buf, "%u\n", !!(data->alarms & BIT(index))); + reg = MAX6697_REG_STAT(2 - (index / 8)); + ret = regmap_read(data->regmap, reg, &alarms); + if (ret) + return ret; + + return sprintf(buf, "%u\n", !!(alarms & BIT(index & 7))); } static ssize_t temp_store(struct device *dev, @@ -321,82 +258,63 @@ static ssize_t temp_store(struct device *dev, if (ret < 0) return ret; - mutex_lock(&data->update_lock); temp = clamp_val(temp, -1000000, 1000000); /* prevent underflow */ temp = DIV_ROUND_CLOSEST(temp, 1000) + data->temp_offset; temp = clamp_val(temp, 0, data->type == max6581 ? 255 : 127); - data->temp[nr][index] = temp; - ret = i2c_smbus_write_byte_data(data->client, - index == 2 ? MAX6697_REG_MAX[nr] - : MAX6697_REG_CRIT[nr], - temp); - mutex_unlock(&data->update_lock); + ret = regmap_write(data->regmap, + index == 2 ? MAX6697_REG_MAX[nr] + : MAX6697_REG_CRIT[nr], + temp); - return ret < 0 ? ret : count; + return ret ? : count; } static ssize_t offset_store(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) { - int val, ret, index, select; - struct max6697_data *data; - bool channel_enabled; + struct max6697_data *data = dev_get_drvdata(dev); + int index = to_sensor_dev_attr(devattr)->index; + struct regmap *regmap = data->regmap; long temp; + int ret; - index = to_sensor_dev_attr(devattr)->index; - data = dev_get_drvdata(dev); ret = kstrtol(buf, 10, &temp); if (ret < 0) return ret; mutex_lock(&data->update_lock); - select = i2c_smbus_read_byte_data(data->client, MAX6581_REG_OFFSET_SELECT); - if (select < 0) { - ret = select; - goto abort; - } - channel_enabled = (select & BIT(index - 1)); temp = clamp_val(temp, MAX6581_OFFSET_MIN, MAX6581_OFFSET_MAX); - val = DIV_ROUND_CLOSEST(temp, 250); - /* disable the offset for channel if the new offset is 0 */ - if (val == 0) { - if (channel_enabled) - ret = i2c_smbus_write_byte_data(data->client, MAX6581_REG_OFFSET_SELECT, - select & ~BIT(index - 1)); - ret = ret < 0 ? ret : count; - goto abort; + temp = DIV_ROUND_CLOSEST(temp, 250); + if (!temp) { /* disable this (and only this) channel */ + ret = regmap_clear_bits(regmap, MAX6581_REG_OFFSET_SELECT, BIT(index - 1)); + goto unlock; } - if (!channel_enabled) { - ret = i2c_smbus_write_byte_data(data->client, MAX6581_REG_OFFSET_SELECT, - select | BIT(index - 1)); - if (ret < 0) - goto abort; - } - ret = i2c_smbus_write_byte_data(data->client, MAX6581_REG_OFFSET, val); - ret = ret < 0 ? ret : count; - -abort: + /* enable channel, and update offset */ + ret = regmap_set_bits(regmap, MAX6581_REG_OFFSET_SELECT, BIT(index - 1)); + if (ret) + goto unlock; + ret = regmap_write(regmap, MAX6581_REG_OFFSET, temp); +unlock: mutex_unlock(&data->update_lock); - return ret; + return ret ? : count; } static ssize_t offset_show(struct device *dev, struct device_attribute *devattr, char *buf) { - struct max6697_data *data; - int select, ret, index; + unsigned int regs[2] = { MAX6581_REG_OFFSET_SELECT, MAX6581_REG_OFFSET }; + struct max6697_data *data = dev_get_drvdata(dev); + int index = to_sensor_dev_attr(devattr)->index; + u8 regdata[2]; + int ret; - index = to_sensor_dev_attr(devattr)->index; - data = dev_get_drvdata(dev); - mutex_lock(&data->update_lock); - select = i2c_smbus_read_byte_data(data->client, MAX6581_REG_OFFSET_SELECT); - if (select < 0) - ret = select; - else if (select & BIT(index - 1)) - ret = i2c_smbus_read_byte_data(data->client, MAX6581_REG_OFFSET); - else - ret = 0; - mutex_unlock(&data->update_lock); - return ret < 0 ? ret : sprintf(buf, "%d\n", max6581_offset_to_millic(ret)); + ret = regmap_multi_reg_read(data->regmap, regs, regdata, 2); + if (ret) + return ret; + + if (!(regdata[0] & BIT(index - 1))) + regdata[1] = 0; + + return sprintf(buf, "%d\n", max6581_offset_to_millic(regdata[1])); } static SENSOR_DEVICE_ATTR_RO(temp1_input, temp_input, 0); @@ -569,12 +487,11 @@ static const struct attribute_group max6697_group = { }; __ATTRIBUTE_GROUPS(max6697); -static int max6697_config_of(struct max6697_data *data, struct i2c_client *client) +static int max6697_config_of(struct device_node *node, struct max6697_data *data) { const struct max6697_chip_data *chip = data->chip; - struct device_node *node = client->dev.of_node; + struct regmap *regmap = data->regmap; int ret, confreg; - int factor = 0; u32 vals[2]; confreg = 0; @@ -594,15 +511,15 @@ static int max6697_config_of(struct max6697_data *data, struct i2c_client *clien if (of_property_read_u32(node, "alert-mask", vals)) vals[0] = 0; - ret = i2c_smbus_write_byte_data(client, MAX6697_REG_ALERT_MASK, - MAX6697_ALERT_MAP_BITS(vals[0])); + ret = regmap_write(regmap, MAX6697_REG_ALERT_MASK, + MAX6697_ALERT_MAP_BITS(vals[0])); if (ret) return ret; if (of_property_read_u32(node, "over-temperature-mask", vals)) vals[0] = 0; - ret = i2c_smbus_write_byte_data(client, MAX6697_REG_OVERT_MASK, - MAX6697_OVERT_MAP_BITS(vals[0])); + ret = regmap_write(regmap, MAX6697_REG_OVERT_MASK, + MAX6697_OVERT_MAP_BITS(vals[0])); if (ret) return ret; @@ -610,7 +527,6 @@ static int max6697_config_of(struct max6697_data *data, struct i2c_client *clien if (of_property_read_bool(node, "resistance-cancellation") && chip->valid_conf & MAX6697_CONF_RESISTANCE) { confreg |= MAX6697_CONF_RESISTANCE; - factor = 1; } } else { if (of_property_read_u32(node, "resistance-cancellation", &vals[0])) { @@ -621,9 +537,7 @@ static int max6697_config_of(struct max6697_data *data, struct i2c_client *clien } vals[0] &= 0xfe; - factor = hweight8(vals[0]); - ret = i2c_smbus_write_byte_data(client, MAX6581_REG_RESISTANCE, - vals[0] >> 1); + ret = regmap_write(regmap, MAX6581_REG_RESISTANCE, vals[0] >> 1); if (ret < 0) return ret; @@ -632,81 +546,92 @@ static int max6697_config_of(struct max6697_data *data, struct i2c_client *clien vals[1] = 0; } - ret = i2c_smbus_write_byte_data(client, MAX6581_REG_IDEALITY, - vals[1]); + ret = regmap_write(regmap, MAX6581_REG_IDEALITY, vals[1]); if (ret < 0) return ret; - ret = i2c_smbus_write_byte_data(client, - MAX6581_REG_IDEALITY_SELECT, - (vals[0] & 0xfe) >> 1); + ret = regmap_write(regmap, MAX6581_REG_IDEALITY_SELECT, + (vals[0] & 0xfe) >> 1); if (ret < 0) return ret; } - ret = i2c_smbus_write_byte_data(client, MAX6697_REG_CONFIG, confreg); - if (ret < 0) - return ret; - return factor; + return regmap_write(regmap, MAX6697_REG_CONFIG, confreg); } -static int max6697_init_chip(struct max6697_data *data, - struct i2c_client *client) +static int max6697_init_chip(struct device_node *np, struct max6697_data *data) { - const struct max6697_chip_data *chip = data->chip; - int factor = chip->channels; - int ret, reg; + unsigned int reg; + int ret; /* * Don't touch configuration if there is no devicetree configuration. * If that is the case, use the current chip configuration. */ - if (!client->dev.of_node) { - reg = i2c_smbus_read_byte_data(client, MAX6697_REG_CONFIG); - if (reg < 0) - return reg; + if (!np) { + struct regmap *regmap = data->regmap; + + ret = regmap_read(regmap, MAX6697_REG_CONFIG, ®); + if (ret < 0) + return ret; if (data->type == max6581) { if (reg & MAX6581_CONF_EXTENDED) data->temp_offset = 64; - reg = i2c_smbus_read_byte_data(client, - MAX6581_REG_RESISTANCE); - if (reg < 0) - return reg; - factor += hweight8(reg); - } else { - if (reg & MAX6697_CONF_RESISTANCE) - factor++; + ret = regmap_read(regmap, MAX6581_REG_RESISTANCE, ®); } - data->update_interval = factor * MAX6697_CONV_TIME; } else { - ret = max6697_config_of(data, client); - if (ret < 0) - return ret; - data->update_interval = (factor + ret) * MAX6697_CONV_TIME; + ret = max6697_config_of(np, data); } - return 0; + return ret; } +static bool max6697_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case 0x00 ... 0x09: /* temperature high bytes */ + case 0x44 ... 0x47: /* status */ + case 0x51 ... 0x58: /* temperature low bytes */ + return true; + default: + return false; + } +} + +static bool max6697_writeable_reg(struct device *dev, unsigned int reg) +{ + return reg != 0x0a && reg != 0x0f && !max6697_volatile_reg(dev, reg); +} + +static const struct regmap_config max6697_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = 0x58, + .writeable_reg = max6697_writeable_reg, + .volatile_reg = max6697_volatile_reg, + .cache_type = REGCACHE_MAPLE, +}; + static int max6697_probe(struct i2c_client *client) { - struct i2c_adapter *adapter = client->adapter; struct device *dev = &client->dev; struct max6697_data *data; struct device *hwmon_dev; + struct regmap *regmap; int err; - if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) - return -ENODEV; + regmap = regmap_init_i2c(client, &max6697_regmap_config); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); data = devm_kzalloc(dev, sizeof(struct max6697_data), GFP_KERNEL); if (!data) return -ENOMEM; + data->regmap = regmap; data->type = (uintptr_t)i2c_get_match_data(client); data->chip = &max6697_chip_data[data->type]; - data->client = client; mutex_init(&data->update_lock); - err = max6697_init_chip(data, client); + err = max6697_init_chip(client->dev.of_node, data); if (err) return err; From 678c2a4582023a093d0c20def5ead7dd93f9004a Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sat, 13 Jul 2024 07:44:20 -0700 Subject: [PATCH 23/96] hwmon: (max6697) Convert to with_info hwmon API Convert to with_info hwmon API to simplify the code and reduce its size. This patch reduces object file size by approximately 25%. Reviewed-by: Tzung-Bi Shih Signed-off-by: Guenter Roeck --- drivers/hwmon/max6697.c | 464 +++++++++++++++------------------------- 1 file changed, 173 insertions(+), 291 deletions(-) diff --git a/drivers/hwmon/max6697.c b/drivers/hwmon/max6697.c index 90cb203c66e2..bfbaf1b64c1e 100644 --- a/drivers/hwmon/max6697.c +++ b/drivers/hwmon/max6697.c @@ -10,7 +10,6 @@ #include #include #include -#include #include #include #include @@ -52,7 +51,9 @@ static const u8 MAX6697_REG_CRIT[] = { (FIELD_PREP(MAX6697_EXTERNAL_MASK_CHIP, FIELD_GET(MAX6697_EXTERNAL_MASK_DT, reg)) | \ FIELD_PREP(MAX6697_LOCAL_MASK_CHIP, FIELD_GET(MAX6697_LOCAL_MASK_DT, reg))) -#define MAX6697_REG_STAT(n) (0x44 + (n)) +#define MAX6697_REG_STAT_ALARM 0x44 +#define MAX6697_REG_STAT_CRIT 0x45 +#define MAX6697_REG_STAT_FAULT 0x46 #define MAX6697_REG_CONFIG 0x41 #define MAX6581_CONF_EXTENDED BIT(1) @@ -78,7 +79,6 @@ struct max6697_chip_data { u32 have_crit; u32 have_fault; u8 valid_conf; - const u8 *alarm_map; }; struct max6697_data { @@ -98,11 +98,6 @@ struct max6697_data { u32 alarms; }; -/* Diode fault status bits on MAX6581 are right shifted by one bit */ -static const u8 max6581_alarm_map[] = { - 0, 0, 1, 2, 3, 4, 5, 6, 8, 9, 10, 11, 12, 13, 14, 15, - 16, 17, 18, 19, 20, 21, 22, 23 }; - static const struct max6697_chip_data max6697_chip_data[] = { [max6581] = { .channels = 8, @@ -110,7 +105,6 @@ static const struct max6697_chip_data max6697_chip_data[] = { .have_ext = 0x7f, .have_fault = 0xfe, .valid_conf = MAX6581_CONF_EXTENDED | MAX6697_CONF_TIMEOUT, - .alarm_map = max6581_alarm_map, }, [max6602] = { .channels = 5, @@ -179,313 +173,202 @@ static const struct max6697_chip_data max6697_chip_data[] = { }, }; -static inline int max6581_offset_to_millic(int val) -{ - return sign_extend32(val, 7) * 250; -} - -static ssize_t temp_input_show(struct device *dev, - struct device_attribute *devattr, char *buf) +static int max6697_read(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long *val) { + unsigned int offset_regs[2] = { MAX6581_REG_OFFSET_SELECT, MAX6581_REG_OFFSET }; + unsigned int temp_regs[2] = { MAX6697_REG_TEMP[channel], + MAX6697_REG_TEMP_EXT[channel] }; struct max6697_data *data = dev_get_drvdata(dev); - int index = to_sensor_dev_attr(devattr)->index; - unsigned int regs[2] = { MAX6697_REG_TEMP[index], - MAX6697_REG_TEMP_EXT[index] }; - u8 regdata[2] = { }; - int temp, ret; - - ret = regmap_multi_reg_read(data->regmap, regs, regdata, - data->chip->have_ext & BIT(index) ? 2 : 1); - if (ret) - return ret; - - temp = ((regdata[0] - data->temp_offset) << 3) | (regdata[1] >> 5); - - return sprintf(buf, "%d\n", temp * 125); -} - -static ssize_t temp_show(struct device *dev, struct device_attribute *devattr, - char *buf) -{ - struct max6697_data *data = dev_get_drvdata(dev); - int index = to_sensor_dev_attr_2(devattr)->index; - int nr = to_sensor_dev_attr_2(devattr)->nr; - unsigned int temp; - int reg, ret; - - if (index == MAX6697_TEMP_MAX) - reg = MAX6697_REG_MAX[nr]; - else - reg = MAX6697_REG_CRIT[nr]; - - ret = regmap_read(data->regmap, reg, &temp); - if (ret) - return ret; - - return sprintf(buf, "%d\n", ((int)temp - data->temp_offset) * 1000); -} - -static ssize_t alarm_show(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct max6697_data *data = dev_get_drvdata(dev); - int index = to_sensor_dev_attr(attr)->index; - unsigned int alarms; - int reg, ret; - - if (data->chip->alarm_map) - index = data->chip->alarm_map[index]; - - reg = MAX6697_REG_STAT(2 - (index / 8)); - ret = regmap_read(data->regmap, reg, &alarms); - if (ret) - return ret; - - return sprintf(buf, "%u\n", !!(alarms & BIT(index & 7))); -} - -static ssize_t temp_store(struct device *dev, - struct device_attribute *devattr, const char *buf, - size_t count) -{ - int nr = to_sensor_dev_attr_2(devattr)->nr; - int index = to_sensor_dev_attr_2(devattr)->index; - struct max6697_data *data = dev_get_drvdata(dev); - long temp; - int ret; - - ret = kstrtol(buf, 10, &temp); - if (ret < 0) - return ret; - - temp = clamp_val(temp, -1000000, 1000000); /* prevent underflow */ - temp = DIV_ROUND_CLOSEST(temp, 1000) + data->temp_offset; - temp = clamp_val(temp, 0, data->type == max6581 ? 255 : 127); - ret = regmap_write(data->regmap, - index == 2 ? MAX6697_REG_MAX[nr] - : MAX6697_REG_CRIT[nr], - temp); - - return ret ? : count; -} - -static ssize_t offset_store(struct device *dev, struct device_attribute *devattr, const char *buf, - size_t count) -{ - struct max6697_data *data = dev_get_drvdata(dev); - int index = to_sensor_dev_attr(devattr)->index; struct regmap *regmap = data->regmap; - long temp; + u8 regdata[2] = { }; + u32 regval; int ret; - ret = kstrtol(buf, 10, &temp); - if (ret < 0) - return ret; + switch (attr) { + case hwmon_temp_input: + ret = regmap_multi_reg_read(regmap, temp_regs, regdata, + data->chip->have_ext & BIT(channel) ? 2 : 1); + if (ret) + return ret; + *val = (((regdata[0] - data->temp_offset) << 3) | (regdata[1] >> 5)) * 125; + break; + case hwmon_temp_max: + ret = regmap_read(regmap, MAX6697_REG_MAX[channel], ®val); + if (ret) + return ret; + *val = ((int)regval - data->temp_offset) * 1000; + break; + case hwmon_temp_crit: + ret = regmap_read(regmap, MAX6697_REG_CRIT[channel], ®val); + if (ret) + return ret; + *val = ((int)regval - data->temp_offset) * 1000; + break; + case hwmon_temp_offset: + ret = regmap_multi_reg_read(regmap, offset_regs, regdata, 2); + if (ret) + return ret; - mutex_lock(&data->update_lock); - temp = clamp_val(temp, MAX6581_OFFSET_MIN, MAX6581_OFFSET_MAX); - temp = DIV_ROUND_CLOSEST(temp, 250); - if (!temp) { /* disable this (and only this) channel */ - ret = regmap_clear_bits(regmap, MAX6581_REG_OFFSET_SELECT, BIT(index - 1)); - goto unlock; + if (!(regdata[0] & BIT(channel - 1))) + regdata[1] = 0; + + *val = sign_extend32(regdata[1], 7) * 250; + break; + case hwmon_temp_fault: + ret = regmap_read(regmap, MAX6697_REG_STAT_FAULT, ®val); + if (ret) + return ret; + if (data->type == max6581) + *val = !!(regval & BIT(channel - 1)); + else + *val = !!(regval & BIT(channel)); + break; + case hwmon_temp_crit_alarm: + ret = regmap_read(regmap, MAX6697_REG_STAT_CRIT, ®val); + if (ret) + return ret; + *val = !!(regval & BIT(channel ? channel - 1 : 7)); + break; + case hwmon_temp_max_alarm: + ret = regmap_read(regmap, MAX6697_REG_STAT_ALARM, ®val); + if (ret) + return ret; + switch (channel) { + case 0: + *val = !!(regval & BIT(6)); + break; + case 7: + *val = !!(regval & BIT(7)); + break; + default: + *val = !!(regval & BIT(channel - 1)); + break; + } + break; + default: + return -EOPNOTSUPP; } - /* enable channel, and update offset */ - ret = regmap_set_bits(regmap, MAX6581_REG_OFFSET_SELECT, BIT(index - 1)); - if (ret) - goto unlock; - ret = regmap_write(regmap, MAX6581_REG_OFFSET, temp); -unlock: - mutex_unlock(&data->update_lock); - return ret ? : count; + return 0; } -static ssize_t offset_show(struct device *dev, struct device_attribute *devattr, char *buf) +static int max6697_write(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long val) { - unsigned int regs[2] = { MAX6581_REG_OFFSET_SELECT, MAX6581_REG_OFFSET }; struct max6697_data *data = dev_get_drvdata(dev); - int index = to_sensor_dev_attr(devattr)->index; - u8 regdata[2]; + struct regmap *regmap = data->regmap; int ret; - ret = regmap_multi_reg_read(data->regmap, regs, regdata, 2); - if (ret) + switch (attr) { + case hwmon_temp_max: + val = clamp_val(val, -1000000, 1000000); /* prevent underflow */ + val = DIV_ROUND_CLOSEST(val, 1000) + data->temp_offset; + val = clamp_val(val, 0, data->type == max6581 ? 255 : 127); + return regmap_write(regmap, MAX6697_REG_MAX[channel], val); + case hwmon_temp_crit: + val = clamp_val(val, -1000000, 1000000); /* prevent underflow */ + val = DIV_ROUND_CLOSEST(val, 1000) + data->temp_offset; + val = clamp_val(val, 0, data->type == max6581 ? 255 : 127); + return regmap_write(regmap, MAX6697_REG_CRIT[channel], val); + case hwmon_temp_offset: + mutex_lock(&data->update_lock); + val = clamp_val(val, MAX6581_OFFSET_MIN, MAX6581_OFFSET_MAX); + val = DIV_ROUND_CLOSEST(val, 250); + if (!val) { /* disable this (and only this) channel */ + ret = regmap_clear_bits(regmap, MAX6581_REG_OFFSET_SELECT, + BIT(channel - 1)); + } else { + /* enable channel and update offset */ + ret = regmap_set_bits(regmap, MAX6581_REG_OFFSET_SELECT, + BIT(channel - 1)); + if (ret) + goto unlock; + ret = regmap_write(regmap, MAX6581_REG_OFFSET, val); + } +unlock: + mutex_unlock(&data->update_lock); return ret; - - if (!(regdata[0] & BIT(index - 1))) - regdata[1] = 0; - - return sprintf(buf, "%d\n", max6581_offset_to_millic(regdata[1])); + default: + return -EOPNOTSUPP; + } } -static SENSOR_DEVICE_ATTR_RO(temp1_input, temp_input, 0); -static SENSOR_DEVICE_ATTR_2_RW(temp1_max, temp, 0, MAX6697_TEMP_MAX); -static SENSOR_DEVICE_ATTR_2_RW(temp1_crit, temp, 0, MAX6697_TEMP_CRIT); - -static SENSOR_DEVICE_ATTR_RO(temp2_input, temp_input, 1); -static SENSOR_DEVICE_ATTR_2_RW(temp2_max, temp, 1, MAX6697_TEMP_MAX); -static SENSOR_DEVICE_ATTR_2_RW(temp2_crit, temp, 1, MAX6697_TEMP_CRIT); - -static SENSOR_DEVICE_ATTR_RO(temp3_input, temp_input, 2); -static SENSOR_DEVICE_ATTR_2_RW(temp3_max, temp, 2, MAX6697_TEMP_MAX); -static SENSOR_DEVICE_ATTR_2_RW(temp3_crit, temp, 2, MAX6697_TEMP_CRIT); - -static SENSOR_DEVICE_ATTR_RO(temp4_input, temp_input, 3); -static SENSOR_DEVICE_ATTR_2_RW(temp4_max, temp, 3, MAX6697_TEMP_MAX); -static SENSOR_DEVICE_ATTR_2_RW(temp4_crit, temp, 3, MAX6697_TEMP_CRIT); - -static SENSOR_DEVICE_ATTR_RO(temp5_input, temp_input, 4); -static SENSOR_DEVICE_ATTR_2_RW(temp5_max, temp, 4, MAX6697_TEMP_MAX); -static SENSOR_DEVICE_ATTR_2_RW(temp5_crit, temp, 4, MAX6697_TEMP_CRIT); - -static SENSOR_DEVICE_ATTR_RO(temp6_input, temp_input, 5); -static SENSOR_DEVICE_ATTR_2_RW(temp6_max, temp, 5, MAX6697_TEMP_MAX); -static SENSOR_DEVICE_ATTR_2_RW(temp6_crit, temp, 5, MAX6697_TEMP_CRIT); - -static SENSOR_DEVICE_ATTR_RO(temp7_input, temp_input, 6); -static SENSOR_DEVICE_ATTR_2_RW(temp7_max, temp, 6, MAX6697_TEMP_MAX); -static SENSOR_DEVICE_ATTR_2_RW(temp7_crit, temp, 6, MAX6697_TEMP_CRIT); - -static SENSOR_DEVICE_ATTR_RO(temp8_input, temp_input, 7); -static SENSOR_DEVICE_ATTR_2_RW(temp8_max, temp, 7, MAX6697_TEMP_MAX); -static SENSOR_DEVICE_ATTR_2_RW(temp8_crit, temp, 7, MAX6697_TEMP_CRIT); - -static SENSOR_DEVICE_ATTR_RO(temp1_max_alarm, alarm, 22); -static SENSOR_DEVICE_ATTR_RO(temp2_max_alarm, alarm, 16); -static SENSOR_DEVICE_ATTR_RO(temp3_max_alarm, alarm, 17); -static SENSOR_DEVICE_ATTR_RO(temp4_max_alarm, alarm, 18); -static SENSOR_DEVICE_ATTR_RO(temp5_max_alarm, alarm, 19); -static SENSOR_DEVICE_ATTR_RO(temp6_max_alarm, alarm, 20); -static SENSOR_DEVICE_ATTR_RO(temp7_max_alarm, alarm, 21); -static SENSOR_DEVICE_ATTR_RO(temp8_max_alarm, alarm, 23); - -static SENSOR_DEVICE_ATTR_RO(temp1_crit_alarm, alarm, 15); -static SENSOR_DEVICE_ATTR_RO(temp2_crit_alarm, alarm, 8); -static SENSOR_DEVICE_ATTR_RO(temp3_crit_alarm, alarm, 9); -static SENSOR_DEVICE_ATTR_RO(temp4_crit_alarm, alarm, 10); -static SENSOR_DEVICE_ATTR_RO(temp5_crit_alarm, alarm, 11); -static SENSOR_DEVICE_ATTR_RO(temp6_crit_alarm, alarm, 12); -static SENSOR_DEVICE_ATTR_RO(temp7_crit_alarm, alarm, 13); -static SENSOR_DEVICE_ATTR_RO(temp8_crit_alarm, alarm, 14); - -static SENSOR_DEVICE_ATTR_RO(temp2_fault, alarm, 1); -static SENSOR_DEVICE_ATTR_RO(temp3_fault, alarm, 2); -static SENSOR_DEVICE_ATTR_RO(temp4_fault, alarm, 3); -static SENSOR_DEVICE_ATTR_RO(temp5_fault, alarm, 4); -static SENSOR_DEVICE_ATTR_RO(temp6_fault, alarm, 5); -static SENSOR_DEVICE_ATTR_RO(temp7_fault, alarm, 6); -static SENSOR_DEVICE_ATTR_RO(temp8_fault, alarm, 7); - -/* There is no offset for local temperature so starting from temp2 */ -static SENSOR_DEVICE_ATTR_RW(temp2_offset, offset, 1); -static SENSOR_DEVICE_ATTR_RW(temp3_offset, offset, 2); -static SENSOR_DEVICE_ATTR_RW(temp4_offset, offset, 3); -static SENSOR_DEVICE_ATTR_RW(temp5_offset, offset, 4); -static SENSOR_DEVICE_ATTR_RW(temp6_offset, offset, 5); -static SENSOR_DEVICE_ATTR_RW(temp7_offset, offset, 6); -static SENSOR_DEVICE_ATTR_RW(temp8_offset, offset, 7); - -static DEVICE_ATTR(dummy, 0, NULL, NULL); - -static umode_t max6697_is_visible(struct kobject *kobj, struct attribute *attr, - int index) +static umode_t max6697_is_visible(const void *_data, enum hwmon_sensor_types type, + u32 attr, int channel) { - struct device *dev = kobj_to_dev(kobj); - struct max6697_data *data = dev_get_drvdata(dev); + const struct max6697_data *data = _data; const struct max6697_chip_data *chip = data->chip; - int channel = index / 7; /* channel number */ - int nr = index % 7; /* attribute index within channel */ if (channel >= chip->channels) return 0; - if ((nr == 3 || nr == 4) && !(chip->have_crit & BIT(channel))) - return 0; - if (nr == 5 && !(chip->have_fault & BIT(channel))) - return 0; - /* offset reg is only supported on max6581 remote channels */ - if (nr == 6) - if (data->type != max6581 || channel == 0) - return 0; - - return attr->mode; + switch (attr) { + case hwmon_temp_max: + return 0644; + case hwmon_temp_input: + case hwmon_temp_max_alarm: + return 0444; + case hwmon_temp_crit: + if (chip->have_crit & BIT(channel)) + return 0644; + break; + case hwmon_temp_crit_alarm: + if (chip->have_crit & BIT(channel)) + return 0444; + break; + case hwmon_temp_fault: + if (chip->have_fault & BIT(channel)) + return 0444; + break; + case hwmon_temp_offset: + if (data->type == max6581 && channel) + return 0644; + break; + default: + break; + } + return 0; } -/* - * max6697_is_visible uses the index into the following array to determine - * if attributes should be created or not. Any change in order or content - * must be matched in max6697_is_visible. - */ -static struct attribute *max6697_attributes[] = { - &sensor_dev_attr_temp1_input.dev_attr.attr, - &sensor_dev_attr_temp1_max.dev_attr.attr, - &sensor_dev_attr_temp1_max_alarm.dev_attr.attr, - &sensor_dev_attr_temp1_crit.dev_attr.attr, - &sensor_dev_attr_temp1_crit_alarm.dev_attr.attr, - &dev_attr_dummy.attr, - &dev_attr_dummy.attr, - - &sensor_dev_attr_temp2_input.dev_attr.attr, - &sensor_dev_attr_temp2_max.dev_attr.attr, - &sensor_dev_attr_temp2_max_alarm.dev_attr.attr, - &sensor_dev_attr_temp2_crit.dev_attr.attr, - &sensor_dev_attr_temp2_crit_alarm.dev_attr.attr, - &sensor_dev_attr_temp2_fault.dev_attr.attr, - &sensor_dev_attr_temp2_offset.dev_attr.attr, - - &sensor_dev_attr_temp3_input.dev_attr.attr, - &sensor_dev_attr_temp3_max.dev_attr.attr, - &sensor_dev_attr_temp3_max_alarm.dev_attr.attr, - &sensor_dev_attr_temp3_crit.dev_attr.attr, - &sensor_dev_attr_temp3_crit_alarm.dev_attr.attr, - &sensor_dev_attr_temp3_fault.dev_attr.attr, - &sensor_dev_attr_temp3_offset.dev_attr.attr, - - &sensor_dev_attr_temp4_input.dev_attr.attr, - &sensor_dev_attr_temp4_max.dev_attr.attr, - &sensor_dev_attr_temp4_max_alarm.dev_attr.attr, - &sensor_dev_attr_temp4_crit.dev_attr.attr, - &sensor_dev_attr_temp4_crit_alarm.dev_attr.attr, - &sensor_dev_attr_temp4_fault.dev_attr.attr, - &sensor_dev_attr_temp4_offset.dev_attr.attr, - - &sensor_dev_attr_temp5_input.dev_attr.attr, - &sensor_dev_attr_temp5_max.dev_attr.attr, - &sensor_dev_attr_temp5_max_alarm.dev_attr.attr, - &sensor_dev_attr_temp5_crit.dev_attr.attr, - &sensor_dev_attr_temp5_crit_alarm.dev_attr.attr, - &sensor_dev_attr_temp5_fault.dev_attr.attr, - &sensor_dev_attr_temp5_offset.dev_attr.attr, - - &sensor_dev_attr_temp6_input.dev_attr.attr, - &sensor_dev_attr_temp6_max.dev_attr.attr, - &sensor_dev_attr_temp6_max_alarm.dev_attr.attr, - &sensor_dev_attr_temp6_crit.dev_attr.attr, - &sensor_dev_attr_temp6_crit_alarm.dev_attr.attr, - &sensor_dev_attr_temp6_fault.dev_attr.attr, - &sensor_dev_attr_temp6_offset.dev_attr.attr, - - &sensor_dev_attr_temp7_input.dev_attr.attr, - &sensor_dev_attr_temp7_max.dev_attr.attr, - &sensor_dev_attr_temp7_max_alarm.dev_attr.attr, - &sensor_dev_attr_temp7_crit.dev_attr.attr, - &sensor_dev_attr_temp7_crit_alarm.dev_attr.attr, - &sensor_dev_attr_temp7_fault.dev_attr.attr, - &sensor_dev_attr_temp7_offset.dev_attr.attr, - - &sensor_dev_attr_temp8_input.dev_attr.attr, - &sensor_dev_attr_temp8_max.dev_attr.attr, - &sensor_dev_attr_temp8_max_alarm.dev_attr.attr, - &sensor_dev_attr_temp8_crit.dev_attr.attr, - &sensor_dev_attr_temp8_crit_alarm.dev_attr.attr, - &sensor_dev_attr_temp8_fault.dev_attr.attr, - &sensor_dev_attr_temp8_offset.dev_attr.attr, +/* Return 0 if detection is successful, -ENODEV otherwise */ +static const struct hwmon_channel_info * const max6697_info[] = { + HWMON_CHANNEL_INFO(temp, + HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT | + HWMON_T_MAX_ALARM | HWMON_T_CRIT_ALARM, + HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT | + HWMON_T_MAX_ALARM | HWMON_T_CRIT_ALARM | + HWMON_T_FAULT | HWMON_T_OFFSET, + HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT | + HWMON_T_MAX_ALARM | HWMON_T_CRIT_ALARM | + HWMON_T_FAULT | HWMON_T_OFFSET, + HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT | + HWMON_T_MAX_ALARM | HWMON_T_CRIT_ALARM | + HWMON_T_FAULT | HWMON_T_OFFSET, + HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT | + HWMON_T_MAX_ALARM | HWMON_T_CRIT_ALARM | + HWMON_T_FAULT | HWMON_T_OFFSET, + HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT | + HWMON_T_MAX_ALARM | HWMON_T_CRIT_ALARM | + HWMON_T_FAULT | HWMON_T_OFFSET, + HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT | + HWMON_T_MAX_ALARM | HWMON_T_CRIT_ALARM | + HWMON_T_FAULT | HWMON_T_OFFSET, + HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT | + HWMON_T_MAX_ALARM | HWMON_T_CRIT_ALARM | + HWMON_T_FAULT | HWMON_T_OFFSET), NULL }; -static const struct attribute_group max6697_group = { - .attrs = max6697_attributes, .is_visible = max6697_is_visible, +static const struct hwmon_ops max6697_hwmon_ops = { + .is_visible = max6697_is_visible, + .read = max6697_read, + .write = max6697_write, +}; + +static const struct hwmon_chip_info max6697_chip_info = { + .ops = &max6697_hwmon_ops, + .info = max6697_info, }; -__ATTRIBUTE_GROUPS(max6697); static int max6697_config_of(struct device_node *node, struct max6697_data *data) { @@ -635,9 +518,8 @@ static int max6697_probe(struct i2c_client *client) if (err) return err; - hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name, - data, - max6697_groups); + hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, data, + &max6697_chip_info, NULL); return PTR_ERR_OR_ZERO(hwmon_dev); } From 1e56afb1e648556111b51561cfa1940077a251c5 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sat, 13 Jul 2024 14:20:34 -0700 Subject: [PATCH 24/96] hwmon: (max6697) Add support for tempX_min and tempX_min_alarm MAX6581 supports setting the minimum temperature as well as minimum temperature alarms. Add support for it. Reviewed-by: Tzung-Bi Shih Signed-off-by: Guenter Roeck --- drivers/hwmon/max6697.c | 59 +++++++++++++++++++++++++++++++++-------- 1 file changed, 48 insertions(+), 11 deletions(-) diff --git a/drivers/hwmon/max6697.c b/drivers/hwmon/max6697.c index bfbaf1b64c1e..0c34c0c81232 100644 --- a/drivers/hwmon/max6697.c +++ b/drivers/hwmon/max6697.c @@ -32,6 +32,7 @@ static const u8 MAX6697_REG_MAX[] = { static const u8 MAX6697_REG_CRIT[] = { 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27 }; +#define MAX6697_REG_MIN 0x30 /* * Map device tree / internal register bit map to chip bit map. * Applies to alert register and over-temperature register. @@ -54,6 +55,7 @@ static const u8 MAX6697_REG_CRIT[] = { #define MAX6697_REG_STAT_ALARM 0x44 #define MAX6697_REG_STAT_CRIT 0x45 #define MAX6697_REG_STAT_FAULT 0x46 +#define MAX6697_REG_STAT_MIN_ALARM 0x47 #define MAX6697_REG_CONFIG 0x41 #define MAX6581_CONF_EXTENDED BIT(1) @@ -173,6 +175,18 @@ static const struct max6697_chip_data max6697_chip_data[] = { }, }; +static int max6697_alarm_channel_map(int channel) +{ + switch (channel) { + case 0: + return 6; + case 7: + return 7; + default: + return channel - 1; + } +} + static int max6697_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel, long *val) { @@ -205,6 +219,12 @@ static int max6697_read(struct device *dev, enum hwmon_sensor_types type, return ret; *val = ((int)regval - data->temp_offset) * 1000; break; + case hwmon_temp_min: + ret = regmap_read(regmap, MAX6697_REG_MIN, ®val); + if (ret) + return ret; + *val = ((int)regval - data->temp_offset) * 1000; + break; case hwmon_temp_offset: ret = regmap_multi_reg_read(regmap, offset_regs, regdata, 2); if (ret) @@ -234,17 +254,13 @@ static int max6697_read(struct device *dev, enum hwmon_sensor_types type, ret = regmap_read(regmap, MAX6697_REG_STAT_ALARM, ®val); if (ret) return ret; - switch (channel) { - case 0: - *val = !!(regval & BIT(6)); - break; - case 7: - *val = !!(regval & BIT(7)); - break; - default: - *val = !!(regval & BIT(channel - 1)); - break; - } + *val = !!(regval & BIT(max6697_alarm_channel_map(channel))); + break; + case hwmon_temp_min_alarm: + ret = regmap_read(regmap, MAX6697_REG_STAT_MIN_ALARM, ®val); + if (ret) + return ret; + *val = !!(regval & BIT(max6697_alarm_channel_map(channel))); break; default: return -EOPNOTSUPP; @@ -270,6 +286,11 @@ static int max6697_write(struct device *dev, enum hwmon_sensor_types type, val = DIV_ROUND_CLOSEST(val, 1000) + data->temp_offset; val = clamp_val(val, 0, data->type == max6581 ? 255 : 127); return regmap_write(regmap, MAX6697_REG_CRIT[channel], val); + case hwmon_temp_min: + val = clamp_val(val, -1000000, 1000000); /* prevent underflow */ + val = DIV_ROUND_CLOSEST(val, 1000) + data->temp_offset; + val = clamp_val(val, 0, 255); + return regmap_write(regmap, MAX6697_REG_MIN, val); case hwmon_temp_offset: mutex_lock(&data->update_lock); val = clamp_val(val, MAX6581_OFFSET_MIN, MAX6581_OFFSET_MAX); @@ -308,6 +329,14 @@ static umode_t max6697_is_visible(const void *_data, enum hwmon_sensor_types typ case hwmon_temp_input: case hwmon_temp_max_alarm: return 0444; + case hwmon_temp_min: + if (data->type == max6581) + return channel ? 0444 : 0644; + break; + case hwmon_temp_min_alarm: + if (data->type == max6581) + return 0444; + break; case hwmon_temp_crit: if (chip->have_crit & BIT(channel)) return 0644; @@ -334,27 +363,35 @@ static umode_t max6697_is_visible(const void *_data, enum hwmon_sensor_types typ static const struct hwmon_channel_info * const max6697_info[] = { HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT | + HWMON_T_MIN | HWMON_T_MIN_ALARM | HWMON_T_MAX_ALARM | HWMON_T_CRIT_ALARM, HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT | HWMON_T_MAX_ALARM | HWMON_T_CRIT_ALARM | + HWMON_T_MIN | HWMON_T_MIN_ALARM | HWMON_T_FAULT | HWMON_T_OFFSET, HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT | HWMON_T_MAX_ALARM | HWMON_T_CRIT_ALARM | + HWMON_T_MIN | HWMON_T_MIN_ALARM | HWMON_T_FAULT | HWMON_T_OFFSET, HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT | HWMON_T_MAX_ALARM | HWMON_T_CRIT_ALARM | + HWMON_T_MIN | HWMON_T_MIN_ALARM | HWMON_T_FAULT | HWMON_T_OFFSET, HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT | HWMON_T_MAX_ALARM | HWMON_T_CRIT_ALARM | + HWMON_T_MIN | HWMON_T_MIN_ALARM | HWMON_T_FAULT | HWMON_T_OFFSET, HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT | HWMON_T_MAX_ALARM | HWMON_T_CRIT_ALARM | + HWMON_T_MIN | HWMON_T_MIN_ALARM | HWMON_T_FAULT | HWMON_T_OFFSET, HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT | HWMON_T_MAX_ALARM | HWMON_T_CRIT_ALARM | + HWMON_T_MIN | HWMON_T_MIN_ALARM | HWMON_T_FAULT | HWMON_T_OFFSET, HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT | HWMON_T_MAX_ALARM | HWMON_T_CRIT_ALARM | + HWMON_T_MIN | HWMON_T_MIN_ALARM | HWMON_T_FAULT | HWMON_T_OFFSET), NULL }; From 6b1bb53579b0a46c7a2467fffcd23e54e844ff41 Mon Sep 17 00:00:00 2001 From: Chris Packham Date: Tue, 23 Jul 2024 10:17:35 +1200 Subject: [PATCH 25/96] dt-bindings: hwmon: Add adt7475 fan/pwm properties Add fan child nodes that allow describing the connections for the ADT7475 to the fans it controls. This also allows setting some initial values for the pwm duty cycle and frequency. Signed-off-by: Chris Packham Reviewed-by: Rob Herring (Arm) Link: https://lore.kernel.org/r/20240722221737.3407958-2-chris.packham@alliedtelesis.co.nz Signed-off-by: Guenter Roeck --- .../devicetree/bindings/hwmon/adt7475.yaml | 35 ++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/hwmon/adt7475.yaml b/Documentation/devicetree/bindings/hwmon/adt7475.yaml index 051c976ab711..df2b5b889e4d 100644 --- a/Documentation/devicetree/bindings/hwmon/adt7475.yaml +++ b/Documentation/devicetree/bindings/hwmon/adt7475.yaml @@ -51,6 +51,24 @@ properties: enum: [0, 1] default: 1 + "#pwm-cells": + const: 4 + description: | + Number of cells in a PWM specifier. + - 0: The PWM channel + - 1: The PWM period in nanoseconds + - 90909091 (11 Hz) + - 71428571 (14 Hz) + - 45454545 (22 Hz) + - 34482759 (29 Hz) + - 28571429 (35 Hz) + - 22727273 (44 Hz) + - 17241379 (58 Hz) + - 11363636 (88 Hz) + - 44444 (22 kHz) + - 2: PWM flags 0 or PWM_POLARITY_INVERTED + - 3: The default PWM duty cycle in nanoseconds + patternProperties: "^adi,bypass-attenuator-in[0-4]$": description: | @@ -81,6 +99,10 @@ patternProperties: - smbalert# - gpio + "^fan-[0-9]+$": + $ref: fan-common.yaml# + unevaluatedProperties: false + required: - compatible - reg @@ -89,11 +111,12 @@ additionalProperties: false examples: - | + #include i2c { #address-cells = <1>; #size-cells = <0>; - hwmon@2e { + pwm: hwmon@2e { compatible = "adi,adt7476"; reg = <0x2e>; adi,bypass-attenuator-in0 = <1>; @@ -101,5 +124,15 @@ examples: adi,pwm-active-state = <1 0 1>; adi,pin10-function = "smbalert#"; adi,pin14-function = "tach4"; + #pwm-cells = <4>; + + /* PWMs at 22.5 kHz frequency, 50% duty*/ + fan-0 { + pwms = <&pwm 0 44444 0 22222>; + }; + + fan-1 { + pwms = <&pwm 2 44444 0 22222>; + }; }; }; From 20705629555ad3a86eb2d5bf5244e6ae261664e3 Mon Sep 17 00:00:00 2001 From: Chris Packham Date: Tue, 23 Jul 2024 10:17:36 +1200 Subject: [PATCH 26/96] dt-bindings: hwmon: adt7475: Deprecate adi,pwm-active-state Now that we have fan child nodes that can specify flags for the PWM outputs we no longer need the adi,pwm-active-state property. Signed-off-by: Chris Packham Acked-by: Rob Herring (Arm) Link: https://lore.kernel.org/r/20240722221737.3407958-3-chris.packham@alliedtelesis.co.nz Signed-off-by: Guenter Roeck --- Documentation/devicetree/bindings/hwmon/adt7475.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/hwmon/adt7475.yaml b/Documentation/devicetree/bindings/hwmon/adt7475.yaml index df2b5b889e4d..79e8d62fa3b3 100644 --- a/Documentation/devicetree/bindings/hwmon/adt7475.yaml +++ b/Documentation/devicetree/bindings/hwmon/adt7475.yaml @@ -45,6 +45,7 @@ properties: the pwm uses a logic low output for 100% duty cycle. If set to 1 the pwm uses a logic high output for 100% duty cycle. $ref: /schemas/types.yaml#/definitions/uint32-array + deprecated: true minItems: 3 maxItems: 3 items: @@ -121,7 +122,6 @@ examples: reg = <0x2e>; adi,bypass-attenuator-in0 = <1>; adi,bypass-attenuator-in1 = <0>; - adi,pwm-active-state = <1 0 1>; adi,pin10-function = "smbalert#"; adi,pin14-function = "tach4"; #pwm-cells = <4>; From 777c97ff08d02ef2d19326b8ca9ef9f82b709007 Mon Sep 17 00:00:00 2001 From: Chris Packham Date: Tue, 23 Jul 2024 10:17:37 +1200 Subject: [PATCH 27/96] hwmon: (adt7475) Add support for configuring initial PWM state By default the PWM duty cycle in hardware is 100%. On some systems this can cause unwanted fan noise. Add the ability to specify the fan connections and initial state of the PWMs via device properties. Signed-off-by: Chris Packham Link: https://lore.kernel.org/r/20240722221737.3407958-4-chris.packham@alliedtelesis.co.nz [groeck: Cleaned up formatting] Signed-off-by: Guenter Roeck --- drivers/hwmon/adt7475.c | 130 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 130 insertions(+) diff --git a/drivers/hwmon/adt7475.c b/drivers/hwmon/adt7475.c index bc186c61a2c0..05c85dc6b142 100644 --- a/drivers/hwmon/adt7475.c +++ b/drivers/hwmon/adt7475.c @@ -21,6 +21,8 @@ #include #include +#include + /* Indexes for the sysfs hooks */ #define INPUT 0 @@ -1662,6 +1664,130 @@ static int adt7475_set_pwm_polarity(struct i2c_client *client) return 0; } +struct adt7475_pwm_config { + int index; + int freq; + int flags; + int duty; +}; + +static int _adt7475_pwm_properties_parse_args(u32 args[4], struct adt7475_pwm_config *cfg) +{ + int freq_hz; + int duty; + + if (args[1] == 0) + return -EINVAL; + + freq_hz = 1000000000UL / args[1]; + if (args[3] >= args[1]) + duty = 255; + else + duty = div_u64(255ULL * args[3], args[1]); + + cfg->index = args[0]; + cfg->freq = find_closest(freq_hz, pwmfreq_table, ARRAY_SIZE(pwmfreq_table)); + cfg->flags = args[2]; + cfg->duty = duty; + + return 0; +} + +static int adt7475_pwm_properties_parse_reference_args(struct fwnode_handle *fwnode, + struct adt7475_pwm_config *cfg) +{ + int ret, i; + struct fwnode_reference_args rargs = {}; + u32 args[4] = {}; + + ret = fwnode_property_get_reference_args(fwnode, "pwms", "#pwm-cells", 0, 0, &rargs); + if (ret) + return ret; + + if (rargs.nargs != 4) { + fwnode_handle_put(rargs.fwnode); + return -EINVAL; + } + + for (i = 0; i < 4; i++) + args[i] = rargs.args[i]; + + ret = _adt7475_pwm_properties_parse_args(args, cfg); + + fwnode_handle_put(rargs.fwnode); + + return ret; +} + +static int adt7475_pwm_properties_parse_args(struct fwnode_handle *fwnode, + struct adt7475_pwm_config *cfg) +{ + int ret; + u32 args[4] = {}; + + ret = fwnode_property_read_u32_array(fwnode, "pwms", args, ARRAY_SIZE(args)); + if (ret) + return ret; + + return _adt7475_pwm_properties_parse_args(args, cfg); +} + +static int adt7475_fan_pwm_config(struct i2c_client *client) +{ + struct adt7475_data *data = i2c_get_clientdata(client); + struct fwnode_handle *child; + struct adt7475_pwm_config cfg = {}; + int ret; + + device_for_each_child_node(&client->dev, child) { + if (!fwnode_property_present(child, "pwms")) + continue; + + if (is_of_node(child)) + ret = adt7475_pwm_properties_parse_reference_args(child, &cfg); + else + ret = adt7475_pwm_properties_parse_args(child, &cfg); + + if (cfg.index >= ADT7475_PWM_COUNT) + return -EINVAL; + + ret = adt7475_read(PWM_CONFIG_REG(cfg.index)); + if (ret < 0) + return ret; + data->pwm[CONTROL][cfg.index] = ret; + if (cfg.flags & PWM_POLARITY_INVERTED) + data->pwm[CONTROL][cfg.index] |= BIT(4); + else + data->pwm[CONTROL][cfg.index] &= ~BIT(4); + + /* Force to manual mode so PWM values take effect */ + data->pwm[CONTROL][cfg.index] &= ~0xE0; + data->pwm[CONTROL][cfg.index] |= 0x07 << 5; + + ret = i2c_smbus_write_byte_data(client, PWM_CONFIG_REG(cfg.index), + data->pwm[CONTROL][cfg.index]); + if (ret) + return ret; + + data->pwm[INPUT][cfg.index] = cfg.duty; + ret = i2c_smbus_write_byte_data(client, PWM_REG(cfg.index), + data->pwm[INPUT][cfg.index]); + if (ret) + return ret; + + data->range[cfg.index] = adt7475_read(TEMP_TRANGE_REG(cfg.index)); + data->range[cfg.index] &= ~0xf; + data->range[cfg.index] |= cfg.freq; + + ret = i2c_smbus_write_byte_data(client, TEMP_TRANGE_REG(cfg.index), + data->range[cfg.index]); + if (ret) + return ret; + } + + return 0; +} + static int adt7475_probe(struct i2c_client *client) { enum chips chip; @@ -1774,6 +1900,10 @@ static int adt7475_probe(struct i2c_client *client) if (ret && ret != -EINVAL) dev_warn(&client->dev, "Error configuring pwm polarity\n"); + ret = adt7475_fan_pwm_config(client); + if (ret) + dev_warn(&client->dev, "Error %d configuring fan/pwm\n", ret); + /* Start monitoring */ switch (chip) { case adt7475: From e21d6d4c7acb77fafc71586a0c94db928219c78e Mon Sep 17 00:00:00 2001 From: Nuno Sa Date: Fri, 26 Jul 2024 14:07:33 +0200 Subject: [PATCH 28/96] hwmon: (ltc2947) Move to firmware agnostic API Use the device property APIs so the driver does not depend on OF to work. While at it, properly include mod_devicetable for struct of_device_id and fix some parameter alignment in the changed places. Signed-off-by: Nuno Sa Link: https://lore.kernel.org/r/20240726-dev-hwmon-ltc6947-fw-agnostic-v1-1-f7d6cab7d438@analog.com Signed-off-by: Guenter Roeck --- drivers/hwmon/ltc2947-core.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/hwmon/ltc2947-core.c b/drivers/hwmon/ltc2947-core.c index d2ff6e700770..244839167e51 100644 --- a/drivers/hwmon/ltc2947-core.c +++ b/drivers/hwmon/ltc2947-core.c @@ -11,7 +11,8 @@ #include #include #include -#include +#include +#include #include #include "ltc2947.h" @@ -1034,9 +1035,8 @@ static int ltc2947_setup(struct ltc2947_data *st) /* 19.89E-6 * 10E9 */ st->lsb_energy = 19890; } - ret = of_property_read_u32_array(st->dev->of_node, - "adi,accumulator-ctl-pol", accum, - ARRAY_SIZE(accum)); + ret = device_property_read_u32_array(st->dev, "adi,accumulator-ctl-pol", + accum, ARRAY_SIZE(accum)); if (!ret) { u32 accum_reg = LTC2947_ACCUM_POL_1(accum[0]) | LTC2947_ACCUM_POL_2(accum[1]); @@ -1045,9 +1045,9 @@ static int ltc2947_setup(struct ltc2947_data *st) if (ret) return ret; } - ret = of_property_read_u32(st->dev->of_node, - "adi,accumulation-deadband-microamp", - &deadband); + ret = device_property_read_u32(st->dev, + "adi,accumulation-deadband-microamp", + &deadband); if (!ret) { /* the LSB is the same as the current, so 3mA */ ret = regmap_write(st->map, LTC2947_REG_ACCUM_DEADBAND, @@ -1056,7 +1056,7 @@ static int ltc2947_setup(struct ltc2947_data *st) return ret; } /* check gpio cfg */ - ret = of_property_read_u32(st->dev->of_node, "adi,gpio-out-pol", &pol); + ret = device_property_read_u32(st->dev, "adi,gpio-out-pol", &pol); if (!ret) { /* setup GPIO as output */ u32 gpio_ctl = LTC2947_GPIO_EN(1) | LTC2947_GPIO_FAN_EN(1) | @@ -1067,8 +1067,8 @@ static int ltc2947_setup(struct ltc2947_data *st) if (ret) return ret; } - ret = of_property_read_u32_array(st->dev->of_node, "adi,gpio-in-accum", - accum, ARRAY_SIZE(accum)); + ret = device_property_read_u32_array(st->dev, "adi,gpio-in-accum", + accum, ARRAY_SIZE(accum)); if (!ret) { /* * Setup the accum options. The gpioctl is already defined as From 34ea1f9867219e2810210dcfe3392a75a74dc6c3 Mon Sep 17 00:00:00 2001 From: Cryolitia PukNgae Date: Fri, 26 Jul 2024 09:21:14 +0800 Subject: [PATCH 29/96] hwmon: (oxp-sensors) Fix typo in driver documentation RMP -> RPM Signed-off-by: Cryolitia PukNgae Link: https://lore.kernel.org/r/20240726-typo-v1-1-3ca3f07f93e9@gmail.com Signed-off-by: Guenter Roeck --- Documentation/hwmon/oxp-sensors.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/hwmon/oxp-sensors.rst b/Documentation/hwmon/oxp-sensors.rst index 55b1ef61625e..50618f064379 100644 --- a/Documentation/hwmon/oxp-sensors.rst +++ b/Documentation/hwmon/oxp-sensors.rst @@ -52,7 +52,7 @@ Sysfs entries The following attributes are supported: fan1_input - Read Only. Reads current fan RMP. + Read Only. Reads current fan RPM. pwm1_enable Read Write. Enable manual fan control. Write "1" to set to manual, write "0" From 74b6451beb8b2cda63b19aff680d4517a0955242 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Mon, 15 Jul 2024 07:20:35 -0700 Subject: [PATCH 30/96] hwmon: (max1668) Reorder include files to alphabetic order Reorder include files to alphabetic order to simplify driver maintenance. Reviewed-by: Tzung-Bi Shih Signed-off-by: Guenter Roeck --- drivers/hwmon/max1668.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/hwmon/max1668.c b/drivers/hwmon/max1668.c index 9fc583ebb11b..f5b5cc29da17 100644 --- a/drivers/hwmon/max1668.c +++ b/drivers/hwmon/max1668.c @@ -6,15 +6,15 @@ * some credit to Christoph Scheurer, but largely a rewrite */ -#include -#include -#include -#include -#include +#include #include #include -#include +#include +#include +#include +#include #include +#include /* Addresses to scan */ static const unsigned short max1668_addr_list[] = { From 696dbe45fb1739eee853b1a110b5001b898031c8 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Mon, 15 Jul 2024 07:22:44 -0700 Subject: [PATCH 31/96] hwmon: (max1668) Use BIT macro Use bit macro to make the code easier to understand and reduce duplication. Reviewed-by: Tzung-Bi Shih Signed-off-by: Guenter Roeck --- drivers/hwmon/max1668.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/hwmon/max1668.c b/drivers/hwmon/max1668.c index f5b5cc29da17..83085ed0ae7e 100644 --- a/drivers/hwmon/max1668.c +++ b/drivers/hwmon/max1668.c @@ -6,6 +6,7 @@ * some credit to Christoph Scheurer, but largely a rewrite */ +#include #include #include #include @@ -172,7 +173,7 @@ static ssize_t show_alarm(struct device *dev, struct device_attribute *attr, if (IS_ERR(data)) return PTR_ERR(data); - return sprintf(buf, "%u\n", (data->alarms >> index) & 0x1); + return sprintf(buf, "%u\n", !!(data->alarms & BIT(index))); } static ssize_t show_fault(struct device *dev, @@ -185,7 +186,7 @@ static ssize_t show_fault(struct device *dev, return PTR_ERR(data); return sprintf(buf, "%u\n", - (data->alarms & (1 << 12)) && data->temp[index] == 127); + (data->alarms & BIT(12)) && data->temp[index] == 127); } static ssize_t set_temp_max(struct device *dev, From 021730acbca603b6bf8575f2e5f4b62c71869656 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Mon, 15 Jul 2024 07:18:57 -0700 Subject: [PATCH 32/96] hwmon: (max1668) Convert to use regmap Use regmap for caching to simplify the code and to hide read/write register address differences. Reviewed-by: Tzung-Bi Shih Signed-off-by: Guenter Roeck --- drivers/hwmon/max1668.c | 215 ++++++++++++++++++---------------------- 1 file changed, 99 insertions(+), 116 deletions(-) diff --git a/drivers/hwmon/max1668.c b/drivers/hwmon/max1668.c index 83085ed0ae7e..c7eae28c0b01 100644 --- a/drivers/hwmon/max1668.c +++ b/drivers/hwmon/max1668.c @@ -6,15 +6,15 @@ * some credit to Christoph Scheurer, but largely a rewrite */ +#include #include #include #include #include #include #include -#include #include -#include +#include #include /* Addresses to scan */ @@ -31,14 +31,10 @@ static const unsigned short max1668_addr_list[] = { /* limits */ -/* write high limits */ -#define MAX1668_REG_LIMH_WR(nr) (0x13 + 2 * (nr)) -/* write low limits */ -#define MAX1668_REG_LIML_WR(nr) (0x14 + 2 * (nr)) -/* read high limits */ -#define MAX1668_REG_LIMH_RD(nr) (0x08 + 2 * (nr)) +/* high limits */ +#define MAX1668_REG_LIMH(nr) (0x08 + 2 * (nr)) /* read low limits */ -#define MAX1668_REG_LIML_RD(nr) (0x09 + 2 * (nr)) +#define MAX1668_REG_LIML(nr) (0x09 + 2 * (nr)) /* manufacturer and device ID Constants */ #define MAN_ID_MAXIM 0x4d @@ -54,139 +50,91 @@ MODULE_PARM_DESC(read_only, "Don't set any values, read only mode"); enum chips { max1668, max1805, max1989 }; struct max1668_data { - struct i2c_client *client; + struct regmap *regmap; const struct attribute_group *groups[3]; enum chips type; - - struct mutex update_lock; - bool valid; /* true if following fields are valid */ - unsigned long last_updated; /* In jiffies */ - - /* 1x local and 4x remote */ - s8 temp_max[5]; - s8 temp_min[5]; - s8 temp[5]; - u16 alarms; }; -static struct max1668_data *max1668_update_device(struct device *dev) -{ - struct max1668_data *data = dev_get_drvdata(dev); - struct i2c_client *client = data->client; - struct max1668_data *ret = data; - s32 val; - int i; - - mutex_lock(&data->update_lock); - - if (data->valid && !time_after(jiffies, - data->last_updated + HZ + HZ / 2)) - goto abort; - - for (i = 0; i < 5; i++) { - val = i2c_smbus_read_byte_data(client, MAX1668_REG_TEMP(i)); - if (unlikely(val < 0)) { - ret = ERR_PTR(val); - goto abort; - } - data->temp[i] = (s8) val; - - val = i2c_smbus_read_byte_data(client, MAX1668_REG_LIMH_RD(i)); - if (unlikely(val < 0)) { - ret = ERR_PTR(val); - goto abort; - } - data->temp_max[i] = (s8) val; - - val = i2c_smbus_read_byte_data(client, MAX1668_REG_LIML_RD(i)); - if (unlikely(val < 0)) { - ret = ERR_PTR(val); - goto abort; - } - data->temp_min[i] = (s8) val; - } - - val = i2c_smbus_read_byte_data(client, MAX1668_REG_STAT1); - if (unlikely(val < 0)) { - ret = ERR_PTR(val); - goto abort; - } - data->alarms = val << 8; - - val = i2c_smbus_read_byte_data(client, MAX1668_REG_STAT2); - if (unlikely(val < 0)) { - ret = ERR_PTR(val); - goto abort; - } - data->alarms |= val; - - data->last_updated = jiffies; - data->valid = true; -abort: - mutex_unlock(&data->update_lock); - - return ret; -} - static ssize_t show_temp(struct device *dev, struct device_attribute *devattr, char *buf) { int index = to_sensor_dev_attr(devattr)->index; - struct max1668_data *data = max1668_update_device(dev); + struct max1668_data *data = dev_get_drvdata(dev); + u32 temp; + int ret; - if (IS_ERR(data)) - return PTR_ERR(data); + ret = regmap_read(data->regmap, MAX1668_REG_TEMP(index), &temp); + if (ret) + return ret; - return sprintf(buf, "%d\n", data->temp[index] * 1000); + return sprintf(buf, "%d\n", sign_extend32(temp, 7) * 1000); } static ssize_t show_temp_max(struct device *dev, struct device_attribute *devattr, char *buf) { int index = to_sensor_dev_attr(devattr)->index; - struct max1668_data *data = max1668_update_device(dev); + struct max1668_data *data = dev_get_drvdata(dev); + u32 temp; + int ret; - if (IS_ERR(data)) - return PTR_ERR(data); + ret = regmap_read(data->regmap, MAX1668_REG_LIMH(index), &temp); + if (ret) + return ret; - return sprintf(buf, "%d\n", data->temp_max[index] * 1000); + return sprintf(buf, "%d\n", sign_extend32(temp, 7) * 1000); } static ssize_t show_temp_min(struct device *dev, struct device_attribute *devattr, char *buf) { int index = to_sensor_dev_attr(devattr)->index; - struct max1668_data *data = max1668_update_device(dev); + struct max1668_data *data = dev_get_drvdata(dev); + u32 temp; + int ret; - if (IS_ERR(data)) - return PTR_ERR(data); + ret = regmap_read(data->regmap, MAX1668_REG_LIML(index), &temp); + if (ret) + return ret; - return sprintf(buf, "%d\n", data->temp_min[index] * 1000); + return sprintf(buf, "%d\n", sign_extend32(temp, 7) * 1000); } static ssize_t show_alarm(struct device *dev, struct device_attribute *attr, char *buf) { int index = to_sensor_dev_attr(attr)->index; - struct max1668_data *data = max1668_update_device(dev); + struct max1668_data *data = dev_get_drvdata(dev); + u32 alarm; + int ret; - if (IS_ERR(data)) - return PTR_ERR(data); + ret = regmap_read(data->regmap, + index >= 8 ? MAX1668_REG_STAT1 : MAX1668_REG_STAT2, + &alarm); + if (ret) + return ret; - return sprintf(buf, "%u\n", !!(data->alarms & BIT(index))); + return sprintf(buf, "%u\n", !!(alarm & BIT(index & 7))); } static ssize_t show_fault(struct device *dev, struct device_attribute *devattr, char *buf) { int index = to_sensor_dev_attr(devattr)->index; - struct max1668_data *data = max1668_update_device(dev); + struct max1668_data *data = dev_get_drvdata(dev); + struct regmap *regmap = data->regmap; + u32 alarm, temp; + int ret; - if (IS_ERR(data)) - return PTR_ERR(data); + ret = regmap_read(regmap, MAX1668_REG_STAT1, &alarm); + if (ret) + return ret; - return sprintf(buf, "%u\n", - (data->alarms & BIT(12)) && data->temp[index] == 127); + ret = regmap_read(regmap, MAX1668_REG_TEMP(index), &temp); + if (ret) + return ret; + + return sprintf(buf, "%u\n", (alarm & BIT(4)) && temp == 127); } static ssize_t set_temp_max(struct device *dev, @@ -195,7 +143,6 @@ static ssize_t set_temp_max(struct device *dev, { int index = to_sensor_dev_attr(devattr)->index; struct max1668_data *data = dev_get_drvdata(dev); - struct i2c_client *client = data->client; long temp; int ret; @@ -203,14 +150,10 @@ static ssize_t set_temp_max(struct device *dev, if (ret < 0) return ret; - mutex_lock(&data->update_lock); - data->temp_max[index] = clamp_val(temp/1000, -128, 127); - ret = i2c_smbus_write_byte_data(client, - MAX1668_REG_LIMH_WR(index), - data->temp_max[index]); + temp = clamp_val(temp / 1000, -128, 127); + ret = regmap_write(data->regmap, MAX1668_REG_LIMH(index), temp); if (ret < 0) count = ret; - mutex_unlock(&data->update_lock); return count; } @@ -221,7 +164,6 @@ static ssize_t set_temp_min(struct device *dev, { int index = to_sensor_dev_attr(devattr)->index; struct max1668_data *data = dev_get_drvdata(dev); - struct i2c_client *client = data->client; long temp; int ret; @@ -229,14 +171,10 @@ static ssize_t set_temp_min(struct device *dev, if (ret < 0) return ret; - mutex_lock(&data->update_lock); - data->temp_min[index] = clamp_val(temp/1000, -128, 127); - ret = i2c_smbus_write_byte_data(client, - MAX1668_REG_LIML_WR(index), - data->temp_min[index]); + temp = clamp_val(temp / 1000, -128, 127); + ret = regmap_write(data->regmap, MAX1668_REG_LIML(index), temp); if (ret < 0) count = ret; - mutex_unlock(&data->update_lock); return count; } @@ -392,6 +330,48 @@ static int max1668_detect(struct i2c_client *client, return 0; } +/* regmap */ + +static int max1668_reg_read(void *context, unsigned int reg, unsigned int *val) +{ + int ret; + + ret = i2c_smbus_read_byte_data(context, reg); + if (ret < 0) + return ret; + + *val = ret; + return 0; +} + +static int max1668_reg_write(void *context, unsigned int reg, unsigned int val) +{ + return i2c_smbus_write_byte_data(context, reg + 11, val); +} + +static bool max1668_regmap_is_volatile(struct device *dev, unsigned int reg) +{ + return reg <= MAX1668_REG_STAT2; +} + +static bool max1668_regmap_is_writeable(struct device *dev, unsigned int reg) +{ + return reg > MAX1668_REG_STAT2 && reg <= MAX1668_REG_LIML(4); +} + +static const struct regmap_config max1668_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .cache_type = REGCACHE_MAPLE, + .volatile_reg = max1668_regmap_is_volatile, + .writeable_reg = max1668_regmap_is_writeable, +}; + +static const struct regmap_bus max1668_regmap_bus = { + .reg_write = max1668_reg_write, + .reg_read = max1668_reg_read, +}; + static int max1668_probe(struct i2c_client *client) { struct i2c_adapter *adapter = client->adapter; @@ -406,9 +386,12 @@ static int max1668_probe(struct i2c_client *client) if (!data) return -ENOMEM; - data->client = client; + data->regmap = devm_regmap_init(dev, &max1668_regmap_bus, client, + &max1668_regmap_config); + if (IS_ERR(data->regmap)) + return PTR_ERR(data->regmap); + data->type = (uintptr_t)i2c_get_match_data(client); - mutex_init(&data->update_lock); /* sysfs hooks */ data->groups[0] = &max1668_group_common; From 355c529077b61aadf68910dc103dd54c30ec24d5 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Mon, 15 Jul 2024 07:25:38 -0700 Subject: [PATCH 33/96] hwmon: (max1668) Replace chip type with number of channels The only difference between supported chips is the number of channels. Drop enum chips and list the number of channels in struct i2c_device_id directly. Reviewed-by: Tzung-Bi Shih Signed-off-by: Guenter Roeck --- drivers/hwmon/max1668.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/drivers/hwmon/max1668.c b/drivers/hwmon/max1668.c index c7eae28c0b01..f8180a8597c0 100644 --- a/drivers/hwmon/max1668.c +++ b/drivers/hwmon/max1668.c @@ -47,12 +47,10 @@ static bool read_only; module_param(read_only, bool, 0); MODULE_PARM_DESC(read_only, "Don't set any values, read only mode"); -enum chips { max1668, max1805, max1989 }; - struct max1668_data { struct regmap *regmap; const struct attribute_group *groups[3]; - enum chips type; + int channels; }; static ssize_t show_temp(struct device *dev, @@ -391,11 +389,11 @@ static int max1668_probe(struct i2c_client *client) if (IS_ERR(data->regmap)) return PTR_ERR(data->regmap); - data->type = (uintptr_t)i2c_get_match_data(client); + data->channels = (uintptr_t)i2c_get_match_data(client); /* sysfs hooks */ data->groups[0] = &max1668_group_common; - if (data->type == max1668 || data->type == max1989) + if (data->channels == 5) data->groups[1] = &max1668_group_unique; hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name, @@ -404,9 +402,9 @@ static int max1668_probe(struct i2c_client *client) } static const struct i2c_device_id max1668_id[] = { - { "max1668", max1668 }, - { "max1805", max1805 }, - { "max1989", max1989 }, + { "max1668", 5 }, + { "max1805", 3 }, + { "max1989", 5 }, { } }; MODULE_DEVICE_TABLE(i2c, max1668_id); From c1b93b5414b48054600c96e28c325ad5920a47e6 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Mon, 15 Jul 2024 07:48:24 -0700 Subject: [PATCH 34/96] hwmon: (max1668) Convert to use with_info hwmon API Convert to use with_info API to simplify the code and to reduce its size. This patch reduces object file size by approximately 25%. Reviewed-by: Tzung-Bi Shih Signed-off-by: Guenter Roeck --- drivers/hwmon/max1668.c | 337 ++++++++++++++-------------------------- 1 file changed, 114 insertions(+), 223 deletions(-) diff --git a/drivers/hwmon/max1668.c b/drivers/hwmon/max1668.c index f8180a8597c0..a8197a86f559 100644 --- a/drivers/hwmon/max1668.c +++ b/drivers/hwmon/max1668.c @@ -10,7 +10,6 @@ #include #include #include -#include #include #include #include @@ -49,247 +48,144 @@ MODULE_PARM_DESC(read_only, "Don't set any values, read only mode"); struct max1668_data { struct regmap *regmap; - const struct attribute_group *groups[3]; int channels; }; -static ssize_t show_temp(struct device *dev, - struct device_attribute *devattr, char *buf) +static int max1668_read(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long *val) { - int index = to_sensor_dev_attr(devattr)->index; - struct max1668_data *data = dev_get_drvdata(dev); - u32 temp; - int ret; - - ret = regmap_read(data->regmap, MAX1668_REG_TEMP(index), &temp); - if (ret) - return ret; - - return sprintf(buf, "%d\n", sign_extend32(temp, 7) * 1000); -} - -static ssize_t show_temp_max(struct device *dev, - struct device_attribute *devattr, char *buf) -{ - int index = to_sensor_dev_attr(devattr)->index; - struct max1668_data *data = dev_get_drvdata(dev); - u32 temp; - int ret; - - ret = regmap_read(data->regmap, MAX1668_REG_LIMH(index), &temp); - if (ret) - return ret; - - return sprintf(buf, "%d\n", sign_extend32(temp, 7) * 1000); -} - -static ssize_t show_temp_min(struct device *dev, - struct device_attribute *devattr, char *buf) -{ - int index = to_sensor_dev_attr(devattr)->index; - struct max1668_data *data = dev_get_drvdata(dev); - u32 temp; - int ret; - - ret = regmap_read(data->regmap, MAX1668_REG_LIML(index), &temp); - if (ret) - return ret; - - return sprintf(buf, "%d\n", sign_extend32(temp, 7) * 1000); -} - -static ssize_t show_alarm(struct device *dev, struct device_attribute *attr, - char *buf) -{ - int index = to_sensor_dev_attr(attr)->index; - struct max1668_data *data = dev_get_drvdata(dev); - u32 alarm; - int ret; - - ret = regmap_read(data->regmap, - index >= 8 ? MAX1668_REG_STAT1 : MAX1668_REG_STAT2, - &alarm); - if (ret) - return ret; - - return sprintf(buf, "%u\n", !!(alarm & BIT(index & 7))); -} - -static ssize_t show_fault(struct device *dev, - struct device_attribute *devattr, char *buf) -{ - int index = to_sensor_dev_attr(devattr)->index; struct max1668_data *data = dev_get_drvdata(dev); struct regmap *regmap = data->regmap; - u32 alarm, temp; + u32 regs[2] = { MAX1668_REG_STAT1, MAX1668_REG_TEMP(channel) }; + u8 regvals[2]; + u32 regval; int ret; - ret = regmap_read(regmap, MAX1668_REG_STAT1, &alarm); - if (ret) - return ret; - - ret = regmap_read(regmap, MAX1668_REG_TEMP(index), &temp); - if (ret) - return ret; - - return sprintf(buf, "%u\n", (alarm & BIT(4)) && temp == 127); + switch (attr) { + case hwmon_temp_input: + ret = regmap_read(regmap, MAX1668_REG_TEMP(channel), ®val); + if (ret) + return ret; + *val = sign_extend32(regval, 7) * 1000; + break; + case hwmon_temp_min: + ret = regmap_read(regmap, MAX1668_REG_LIML(channel), ®val); + if (ret) + return ret; + *val = sign_extend32(regval, 7) * 1000; + break; + case hwmon_temp_max: + ret = regmap_read(regmap, MAX1668_REG_LIMH(channel), ®val); + if (ret) + return ret; + *val = sign_extend32(regval, 7) * 1000; + break; + case hwmon_temp_min_alarm: + ret = regmap_read(regmap, + channel ? MAX1668_REG_STAT2 : MAX1668_REG_STAT1, + ®val); + if (ret) + return ret; + if (channel) + *val = !!(regval & BIT(9 - channel * 2)); + else + *val = !!(regval & BIT(5)); + break; + case hwmon_temp_max_alarm: + ret = regmap_read(regmap, + channel ? MAX1668_REG_STAT2 : MAX1668_REG_STAT1, + ®val); + if (ret) + return ret; + if (channel) + *val = !!(regval & BIT(8 - channel * 2)); + else + *val = !!(regval & BIT(6)); + break; + case hwmon_temp_fault: + ret = regmap_multi_reg_read(regmap, regs, regvals, 2); + if (ret) + return ret; + *val = !!((regvals[0] & BIT(4)) && regvals[1] == 127); + break; + default: + return -EOPNOTSUPP; + } + return 0; } -static ssize_t set_temp_max(struct device *dev, - struct device_attribute *devattr, - const char *buf, size_t count) +static int max1668_write(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long val) { - int index = to_sensor_dev_attr(devattr)->index; struct max1668_data *data = dev_get_drvdata(dev); - long temp; - int ret; + struct regmap *regmap = data->regmap; - ret = kstrtol(buf, 10, &temp); - if (ret < 0) - return ret; + val = clamp_val(val / 1000, -128, 127); - temp = clamp_val(temp / 1000, -128, 127); - ret = regmap_write(data->regmap, MAX1668_REG_LIMH(index), temp); - if (ret < 0) - count = ret; - - return count; + switch (attr) { + case hwmon_temp_min: + return regmap_write(regmap, MAX1668_REG_LIML(channel), val); + case hwmon_temp_max: + return regmap_write(regmap, MAX1668_REG_LIMH(channel), val); + default: + return -EOPNOTSUPP; + } } -static ssize_t set_temp_min(struct device *dev, - struct device_attribute *devattr, - const char *buf, size_t count) +static umode_t max1668_is_visible(const void *_data, enum hwmon_sensor_types type, + u32 attr, int channel) { - int index = to_sensor_dev_attr(devattr)->index; - struct max1668_data *data = dev_get_drvdata(dev); - long temp; - int ret; + const struct max1668_data *data = _data; - ret = kstrtol(buf, 10, &temp); - if (ret < 0) - return ret; + if (channel >= data->channels) + return 0; - temp = clamp_val(temp / 1000, -128, 127); - ret = regmap_write(data->regmap, MAX1668_REG_LIML(index), temp); - if (ret < 0) - count = ret; - - return count; + switch (attr) { + case hwmon_temp_min: + case hwmon_temp_max: + return read_only ? 0444 : 0644; + case hwmon_temp_input: + case hwmon_temp_min_alarm: + case hwmon_temp_max_alarm: + return 0444; + case hwmon_temp_fault: + if (channel) + return 0444; + break; + default: + break; + } + return 0; } -static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL, 0); -static SENSOR_DEVICE_ATTR(temp1_max, S_IRUGO, show_temp_max, - set_temp_max, 0); -static SENSOR_DEVICE_ATTR(temp1_min, S_IRUGO, show_temp_min, - set_temp_min, 0); -static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_temp, NULL, 1); -static SENSOR_DEVICE_ATTR(temp2_max, S_IRUGO, show_temp_max, - set_temp_max, 1); -static SENSOR_DEVICE_ATTR(temp2_min, S_IRUGO, show_temp_min, - set_temp_min, 1); -static SENSOR_DEVICE_ATTR(temp3_input, S_IRUGO, show_temp, NULL, 2); -static SENSOR_DEVICE_ATTR(temp3_max, S_IRUGO, show_temp_max, - set_temp_max, 2); -static SENSOR_DEVICE_ATTR(temp3_min, S_IRUGO, show_temp_min, - set_temp_min, 2); -static SENSOR_DEVICE_ATTR(temp4_input, S_IRUGO, show_temp, NULL, 3); -static SENSOR_DEVICE_ATTR(temp4_max, S_IRUGO, show_temp_max, - set_temp_max, 3); -static SENSOR_DEVICE_ATTR(temp4_min, S_IRUGO, show_temp_min, - set_temp_min, 3); -static SENSOR_DEVICE_ATTR(temp5_input, S_IRUGO, show_temp, NULL, 4); -static SENSOR_DEVICE_ATTR(temp5_max, S_IRUGO, show_temp_max, - set_temp_max, 4); -static SENSOR_DEVICE_ATTR(temp5_min, S_IRUGO, show_temp_min, - set_temp_min, 4); - -static SENSOR_DEVICE_ATTR(temp1_max_alarm, S_IRUGO, show_alarm, NULL, 14); -static SENSOR_DEVICE_ATTR(temp1_min_alarm, S_IRUGO, show_alarm, NULL, 13); -static SENSOR_DEVICE_ATTR(temp2_min_alarm, S_IRUGO, show_alarm, NULL, 7); -static SENSOR_DEVICE_ATTR(temp2_max_alarm, S_IRUGO, show_alarm, NULL, 6); -static SENSOR_DEVICE_ATTR(temp3_min_alarm, S_IRUGO, show_alarm, NULL, 5); -static SENSOR_DEVICE_ATTR(temp3_max_alarm, S_IRUGO, show_alarm, NULL, 4); -static SENSOR_DEVICE_ATTR(temp4_min_alarm, S_IRUGO, show_alarm, NULL, 3); -static SENSOR_DEVICE_ATTR(temp4_max_alarm, S_IRUGO, show_alarm, NULL, 2); -static SENSOR_DEVICE_ATTR(temp5_min_alarm, S_IRUGO, show_alarm, NULL, 1); -static SENSOR_DEVICE_ATTR(temp5_max_alarm, S_IRUGO, show_alarm, NULL, 0); - -static SENSOR_DEVICE_ATTR(temp2_fault, S_IRUGO, show_fault, NULL, 1); -static SENSOR_DEVICE_ATTR(temp3_fault, S_IRUGO, show_fault, NULL, 2); -static SENSOR_DEVICE_ATTR(temp4_fault, S_IRUGO, show_fault, NULL, 3); -static SENSOR_DEVICE_ATTR(temp5_fault, S_IRUGO, show_fault, NULL, 4); - -/* Attributes common to MAX1668, MAX1989 and MAX1805 */ -static struct attribute *max1668_attribute_common[] = { - &sensor_dev_attr_temp1_max.dev_attr.attr, - &sensor_dev_attr_temp1_min.dev_attr.attr, - &sensor_dev_attr_temp1_input.dev_attr.attr, - &sensor_dev_attr_temp2_max.dev_attr.attr, - &sensor_dev_attr_temp2_min.dev_attr.attr, - &sensor_dev_attr_temp2_input.dev_attr.attr, - &sensor_dev_attr_temp3_max.dev_attr.attr, - &sensor_dev_attr_temp3_min.dev_attr.attr, - &sensor_dev_attr_temp3_input.dev_attr.attr, - - &sensor_dev_attr_temp1_max_alarm.dev_attr.attr, - &sensor_dev_attr_temp1_min_alarm.dev_attr.attr, - &sensor_dev_attr_temp2_max_alarm.dev_attr.attr, - &sensor_dev_attr_temp2_min_alarm.dev_attr.attr, - &sensor_dev_attr_temp3_max_alarm.dev_attr.attr, - &sensor_dev_attr_temp3_min_alarm.dev_attr.attr, - - &sensor_dev_attr_temp2_fault.dev_attr.attr, - &sensor_dev_attr_temp3_fault.dev_attr.attr, +static const struct hwmon_channel_info * const max1668_info[] = { + HWMON_CHANNEL_INFO(temp, + HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX | + HWMON_T_MIN_ALARM | HWMON_T_MAX_ALARM, + HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX | + HWMON_T_MIN_ALARM | HWMON_T_MAX_ALARM | + HWMON_T_FAULT, + HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX | + HWMON_T_MIN_ALARM | HWMON_T_MAX_ALARM | + HWMON_T_FAULT, + HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX | + HWMON_T_MIN_ALARM | HWMON_T_MAX_ALARM | + HWMON_T_FAULT, + HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX | + HWMON_T_MIN_ALARM | HWMON_T_MAX_ALARM | + HWMON_T_FAULT), NULL }; -/* Attributes not present on MAX1805 */ -static struct attribute *max1668_attribute_unique[] = { - &sensor_dev_attr_temp4_max.dev_attr.attr, - &sensor_dev_attr_temp4_min.dev_attr.attr, - &sensor_dev_attr_temp4_input.dev_attr.attr, - &sensor_dev_attr_temp5_max.dev_attr.attr, - &sensor_dev_attr_temp5_min.dev_attr.attr, - &sensor_dev_attr_temp5_input.dev_attr.attr, - - &sensor_dev_attr_temp4_max_alarm.dev_attr.attr, - &sensor_dev_attr_temp4_min_alarm.dev_attr.attr, - &sensor_dev_attr_temp5_max_alarm.dev_attr.attr, - &sensor_dev_attr_temp5_min_alarm.dev_attr.attr, - - &sensor_dev_attr_temp4_fault.dev_attr.attr, - &sensor_dev_attr_temp5_fault.dev_attr.attr, - NULL +static const struct hwmon_ops max1668_hwmon_ops = { + .is_visible = max1668_is_visible, + .read = max1668_read, + .write = max1668_write, }; -static umode_t max1668_attribute_mode(struct kobject *kobj, - struct attribute *attr, int index) -{ - umode_t ret = S_IRUGO; - if (read_only) - return ret; - if (attr == &sensor_dev_attr_temp1_max.dev_attr.attr || - attr == &sensor_dev_attr_temp2_max.dev_attr.attr || - attr == &sensor_dev_attr_temp3_max.dev_attr.attr || - attr == &sensor_dev_attr_temp4_max.dev_attr.attr || - attr == &sensor_dev_attr_temp5_max.dev_attr.attr || - attr == &sensor_dev_attr_temp1_min.dev_attr.attr || - attr == &sensor_dev_attr_temp2_min.dev_attr.attr || - attr == &sensor_dev_attr_temp3_min.dev_attr.attr || - attr == &sensor_dev_attr_temp4_min.dev_attr.attr || - attr == &sensor_dev_attr_temp5_min.dev_attr.attr) - ret |= S_IWUSR; - return ret; -} - -static const struct attribute_group max1668_group_common = { - .attrs = max1668_attribute_common, - .is_visible = max1668_attribute_mode -}; - -static const struct attribute_group max1668_group_unique = { - .attrs = max1668_attribute_unique, - .is_visible = max1668_attribute_mode +static const struct hwmon_chip_info max1668_chip_info = { + .ops = &max1668_hwmon_ops, + .info = max1668_info, }; /* Return 0 if detection is successful, -ENODEV otherwise */ @@ -391,13 +287,8 @@ static int max1668_probe(struct i2c_client *client) data->channels = (uintptr_t)i2c_get_match_data(client); - /* sysfs hooks */ - data->groups[0] = &max1668_group_common; - if (data->channels == 5) - data->groups[1] = &max1668_group_unique; - - hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name, - data, data->groups); + hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, data, + &max1668_chip_info, NULL); return PTR_ERR_OR_ZERO(hwmon_dev); } From 3adc2857bb6a7c6cb669627b87e9998a3a28231d Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sat, 27 Jul 2024 12:06:04 -0700 Subject: [PATCH 35/96] hwmon: (max6697) Document discrepancy in overtemperature status bit values In the MAX6581 datasheet Revision 0 to 3, the local channel overtemperature status is reported in bit 6 of register 0x45, and the overtemperature status for remote channel 7 is reported in bit 7. In Revision 4 and later, the local channel overtemperature status is reported in bit 7, and the remote channel 7 overtemperature status is reported in bit 6. A real chip was found to match the functionality documented in Revision 4 and later. The code was fixed with commit 1ea3fd1eb986 ("hwmon: (max6697) Fix swapped temp{1,8} critical alarms"). At that time it looked like this was an original bug. It only turned out later that the problem was the result of incorrect information in the chip datasheet. Document the discrepancy to avoid confusion caused by old versions of the datasheet. Cc: Tzung-Bi Shih Reviewed-by: Tzung-Bi Shih Signed-off-by: Guenter Roeck --- drivers/hwmon/max6697.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/hwmon/max6697.c b/drivers/hwmon/max6697.c index 0c34c0c81232..0735a1d2c20f 100644 --- a/drivers/hwmon/max6697.c +++ b/drivers/hwmon/max6697.c @@ -248,6 +248,16 @@ static int max6697_read(struct device *dev, enum hwmon_sensor_types type, ret = regmap_read(regmap, MAX6697_REG_STAT_CRIT, ®val); if (ret) return ret; + /* + * In the MAX6581 datasheet revision 0 to 3, the local channel + * overtemperature status is reported in bit 6 of register 0x45, + * and the overtemperature status for remote channel 7 is + * reported in bit 7. In Revision 4 and later, the local channel + * overtemperature status is reported in bit 7, and the remote + * channel 7 overtemperature status is reported in bit 6. A real + * chip was found to match the functionality documented in + * Revision 4 and later. + */ *val = !!(regval & BIT(channel ? channel - 1 : 7)); break; case hwmon_temp_max_alarm: From 88c0f840dfe36e8c2fd9ddc7d6698e485f0ca1ad Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Mon, 15 Jul 2024 22:36:35 -0700 Subject: [PATCH 36/96] hwmon: (max1619) Clamp temperature range when writing limits Module test code reports underflows when writing sensor limits. temp2_min: Suspected underflow: [min=-77000, read 101000, written -2147483648] temp2_max: Suspected underflow: [min=-77000, read 101000, written -2147483648] temp2_crit: Suspected underflow: [min=-77000, read 101000, written -2147483648] Clamp temperature ranges when writing limits to fix the problem. While at it, use sign_extend32() when reading temperatures to make the code easier to understand. Reviewed-by: Tzung-Bi Shih Signed-off-by: Guenter Roeck --- drivers/hwmon/max1619.c | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/drivers/hwmon/max1619.c b/drivers/hwmon/max1619.c index a89a519cf5d9..464f4c838394 100644 --- a/drivers/hwmon/max1619.c +++ b/drivers/hwmon/max1619.c @@ -52,16 +52,6 @@ static const unsigned short normal_i2c[] = { * Conversions */ -static int temp_from_reg(int val) -{ - return (val & 0x80 ? val-0x100 : val) * 1000; -} - -static int temp_to_reg(int val) -{ - return (val < 0 ? val+0x100*1000 : val) / 1000; -} - enum temp_index { t_input1 = 0, t_input2, @@ -142,7 +132,7 @@ static ssize_t temp_show(struct device *dev, struct device_attribute *devattr, struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct max1619_data *data = max1619_update_device(dev); - return sprintf(buf, "%d\n", temp_from_reg(data->temp[attr->index])); + return sprintf(buf, "%d\n", sign_extend(data->temp[attr->index], 7) * 1000); } static ssize_t temp_store(struct device *dev, @@ -158,7 +148,7 @@ static ssize_t temp_store(struct device *dev, return err; mutex_lock(&data->update_lock); - data->temp[attr->index] = temp_to_reg(val); + data->temp[attr->index] = DIV_ROUND_CLOSEST(clamp_val(val, -128000, 127000), 1000); i2c_smbus_write_byte_data(client, regs_write[attr->index], data->temp[attr->index]); mutex_unlock(&data->update_lock); From 216147e624638fbcd64ed1014916138733b01653 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Mon, 15 Jul 2024 20:28:58 -0700 Subject: [PATCH 37/96] hwmon: (max1619) Reorder include files to alphabetic order Simplify maintenance by reordering include files to alphabetic order. Reviewed-by: Tzung-Bi Shih Signed-off-by: Guenter Roeck --- drivers/hwmon/max1619.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/hwmon/max1619.c b/drivers/hwmon/max1619.c index 464f4c838394..8eb7d04bd2f5 100644 --- a/drivers/hwmon/max1619.c +++ b/drivers/hwmon/max1619.c @@ -12,15 +12,15 @@ * http://pdfserv.maxim-ic.com/en/ds/MAX1619.pdf */ -#include -#include -#include -#include -#include +#include #include #include -#include +#include +#include +#include +#include #include +#include #include static const unsigned short normal_i2c[] = { From 02c3c4e7026b307a220132b59758432b6e7a0cac Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sun, 28 Jul 2024 07:09:54 -0700 Subject: [PATCH 38/96] hwmon: (max1619) Mask valid alarm bits Bit 0, 5, and 6 in the status register are reserved and, if set, do not indicate an alarm. Bit 7 is the 'busy' bit and also does not indicate an alarm. Mask the non-alarm bits to avoid reporting them to userspace. Cc: Tzung-Bi Shih Reviewed-by: Tzung-Bi Shih Signed-off-by: Guenter Roeck --- drivers/hwmon/max1619.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/hwmon/max1619.c b/drivers/hwmon/max1619.c index 8eb7d04bd2f5..5edc9bbbe299 100644 --- a/drivers/hwmon/max1619.c +++ b/drivers/hwmon/max1619.c @@ -112,6 +112,7 @@ static struct max1619_data *max1619_update_device(struct device *dev) config = i2c_smbus_read_byte_data(client, MAX1619_REG_R_CONFIG); if (!(config & 0x20)) data->alarms ^= 0x02; + data->alarms &= 0x1e; data->last_updated = jiffies; data->valid = true; From f8016132ce49dc4acf5c476cebfc93c27fb493cc Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Mon, 15 Jul 2024 22:26:22 -0700 Subject: [PATCH 39/96] hwmon: (max1619) Convert to use regmap Use regmap for local caching, to hide register read/write address differences, and for multi-byte operations. With this change, the driver specific lock is no longer necessary. While at it, check errors seen when initializing the chip and bail out if chip initialization fails. Reviewed-by: Tzung-Bi Shih Signed-off-by: Guenter Roeck --- Documentation/hwmon/max1619.rst | 4 - drivers/hwmon/max1619.c | 259 +++++++++++++++++--------------- 2 files changed, 137 insertions(+), 126 deletions(-) diff --git a/Documentation/hwmon/max1619.rst b/Documentation/hwmon/max1619.rst index e25956e70f73..b5fc175ae18d 100644 --- a/Documentation/hwmon/max1619.rst +++ b/Documentation/hwmon/max1619.rst @@ -27,7 +27,3 @@ All temperature values are given in degrees Celsius. Resolution is 1.0 degree for the local temperature and for the remote temperature. Only the external sensor has high and low limits. - -The max1619 driver will not update its values more frequently than every -other second; reading them more often will do no harm, but will return -'old' values. diff --git a/drivers/hwmon/max1619.c b/drivers/hwmon/max1619.c index 5edc9bbbe299..76ccc3e94b4c 100644 --- a/drivers/hwmon/max1619.c +++ b/drivers/hwmon/max1619.c @@ -17,10 +17,8 @@ #include #include #include -#include #include -#include -#include +#include #include static const unsigned short normal_i2c[] = { @@ -30,27 +28,17 @@ static const unsigned short normal_i2c[] = { * The MAX1619 registers */ -#define MAX1619_REG_R_MAN_ID 0xFE -#define MAX1619_REG_R_CHIP_ID 0xFF -#define MAX1619_REG_R_CONFIG 0x03 -#define MAX1619_REG_W_CONFIG 0x09 -#define MAX1619_REG_R_CONVRATE 0x04 -#define MAX1619_REG_W_CONVRATE 0x0A -#define MAX1619_REG_R_STATUS 0x02 -#define MAX1619_REG_R_LOCAL_TEMP 0x00 -#define MAX1619_REG_R_REMOTE_TEMP 0x01 -#define MAX1619_REG_R_REMOTE_HIGH 0x07 -#define MAX1619_REG_W_REMOTE_HIGH 0x0D -#define MAX1619_REG_R_REMOTE_LOW 0x08 -#define MAX1619_REG_W_REMOTE_LOW 0x0E -#define MAX1619_REG_R_REMOTE_CRIT 0x10 -#define MAX1619_REG_W_REMOTE_CRIT 0x12 -#define MAX1619_REG_R_TCRIT_HYST 0x11 -#define MAX1619_REG_W_TCRIT_HYST 0x13 - -/* - * Conversions - */ +#define MAX1619_REG_LOCAL_TEMP 0x00 +#define MAX1619_REG_REMOTE_TEMP 0x01 +#define MAX1619_REG_STATUS 0x02 +#define MAX1619_REG_CONFIG 0x03 +#define MAX1619_REG_CONVRATE 0x04 +#define MAX1619_REG_REMOTE_HIGH 0x07 +#define MAX1619_REG_REMOTE_LOW 0x08 +#define MAX1619_REG_REMOTE_CRIT 0x10 +#define MAX1619_REG_REMOTE_CRIT_HYST 0x11 +#define MAX1619_REG_MAN_ID 0xFE +#define MAX1619_REG_CHIP_ID 0xFF enum temp_index { t_input1 = 0, @@ -66,63 +54,15 @@ enum temp_index { * Client data (each client gets its own) */ -struct max1619_data { - struct i2c_client *client; - struct mutex update_lock; - bool valid; /* false until following fields are valid */ - unsigned long last_updated; /* in jiffies */ - - /* registers values */ - u8 temp[t_num_regs]; /* index with enum temp_index */ - u8 alarms; +static const u8 regs[t_num_regs] = { + [t_input1] = MAX1619_REG_LOCAL_TEMP, + [t_input2] = MAX1619_REG_REMOTE_TEMP, + [t_low2] = MAX1619_REG_REMOTE_LOW, + [t_high2] = MAX1619_REG_REMOTE_HIGH, + [t_crit2] = MAX1619_REG_REMOTE_CRIT, + [t_hyst2] = MAX1619_REG_REMOTE_CRIT_HYST, }; -static const u8 regs_read[t_num_regs] = { - [t_input1] = MAX1619_REG_R_LOCAL_TEMP, - [t_input2] = MAX1619_REG_R_REMOTE_TEMP, - [t_low2] = MAX1619_REG_R_REMOTE_LOW, - [t_high2] = MAX1619_REG_R_REMOTE_HIGH, - [t_crit2] = MAX1619_REG_R_REMOTE_CRIT, - [t_hyst2] = MAX1619_REG_R_TCRIT_HYST, -}; - -static const u8 regs_write[t_num_regs] = { - [t_low2] = MAX1619_REG_W_REMOTE_LOW, - [t_high2] = MAX1619_REG_W_REMOTE_HIGH, - [t_crit2] = MAX1619_REG_W_REMOTE_CRIT, - [t_hyst2] = MAX1619_REG_W_TCRIT_HYST, -}; - -static struct max1619_data *max1619_update_device(struct device *dev) -{ - struct max1619_data *data = dev_get_drvdata(dev); - struct i2c_client *client = data->client; - int config, i; - - mutex_lock(&data->update_lock); - - if (time_after(jiffies, data->last_updated + HZ * 2) || !data->valid) { - dev_dbg(&client->dev, "Updating max1619 data.\n"); - for (i = 0; i < t_num_regs; i++) - data->temp[i] = i2c_smbus_read_byte_data(client, - regs_read[i]); - data->alarms = i2c_smbus_read_byte_data(client, - MAX1619_REG_R_STATUS); - /* If OVERT polarity is low, reverse alarm bit */ - config = i2c_smbus_read_byte_data(client, MAX1619_REG_R_CONFIG); - if (!(config & 0x20)) - data->alarms ^= 0x02; - data->alarms &= 0x1e; - - data->last_updated = jiffies; - data->valid = true; - } - - mutex_unlock(&data->update_lock); - - return data; -} - /* * Sysfs stuff */ @@ -131,9 +71,15 @@ static ssize_t temp_show(struct device *dev, struct device_attribute *devattr, char *buf) { struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct max1619_data *data = max1619_update_device(dev); + struct regmap *regmap = dev_get_drvdata(dev); + u32 temp; + int ret; - return sprintf(buf, "%d\n", sign_extend(data->temp[attr->index], 7) * 1000); + ret = regmap_read(regmap, regs[attr->index], &temp); + if (ret < 0) + return ret; + + return sprintf(buf, "%d\n", sign_extend32(temp, 7) * 1000); } static ssize_t temp_store(struct device *dev, @@ -141,34 +87,61 @@ static ssize_t temp_store(struct device *dev, size_t count) { struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct max1619_data *data = dev_get_drvdata(dev); - struct i2c_client *client = data->client; + struct regmap *regmap = dev_get_drvdata(dev); long val; int err = kstrtol(buf, 10, &val); if (err) return err; - mutex_lock(&data->update_lock); - data->temp[attr->index] = DIV_ROUND_CLOSEST(clamp_val(val, -128000, 127000), 1000); - i2c_smbus_write_byte_data(client, regs_write[attr->index], - data->temp[attr->index]); - mutex_unlock(&data->update_lock); + val = DIV_ROUND_CLOSEST(clamp_val(val, -128000, 127000), 1000); + err = regmap_write(regmap, regs[attr->index], val); + if (err < 0) + return err; return count; } +static int get_alarms(struct regmap *regmap) +{ + static u32 regs[2] = { MAX1619_REG_STATUS, MAX1619_REG_CONFIG }; + u8 regdata[2]; + int ret; + + ret = regmap_multi_reg_read(regmap, regs, regdata, 2); + if (ret) + return ret; + + /* OVERT status bit may be reversed */ + if (!(regdata[1] & 0x20)) + regdata[0] ^= 0x02; + + return regdata[0] & 0x1e; +} + static ssize_t alarms_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct max1619_data *data = max1619_update_device(dev); - return sprintf(buf, "%d\n", data->alarms); + struct regmap *regmap = dev_get_drvdata(dev); + int alarms; + + alarms = get_alarms(regmap); + if (alarms < 0) + return alarms; + + return sprintf(buf, "%d\n", alarms); } static ssize_t alarm_show(struct device *dev, struct device_attribute *attr, char *buf) { int bitnr = to_sensor_dev_attr(attr)->index; - struct max1619_data *data = max1619_update_device(dev); - return sprintf(buf, "%d\n", (data->alarms >> bitnr) & 1); + struct regmap *regmap = dev_get_drvdata(dev); + int alarms; + + alarms = get_alarms(regmap); + if (alarms < 0) + return alarms; + + return sprintf(buf, "%d\n", (alarms >> bitnr) & 1); } static SENSOR_DEVICE_ATTR_RO(temp1_input, temp, t_input1); @@ -212,9 +185,9 @@ static int max1619_detect(struct i2c_client *client, return -ENODEV; /* detection */ - reg_config = i2c_smbus_read_byte_data(client, MAX1619_REG_R_CONFIG); - reg_convrate = i2c_smbus_read_byte_data(client, MAX1619_REG_R_CONVRATE); - reg_status = i2c_smbus_read_byte_data(client, MAX1619_REG_R_STATUS); + reg_config = i2c_smbus_read_byte_data(client, MAX1619_REG_CONFIG); + reg_convrate = i2c_smbus_read_byte_data(client, MAX1619_REG_CONVRATE); + reg_status = i2c_smbus_read_byte_data(client, MAX1619_REG_STATUS); if ((reg_config & 0x03) != 0x00 || reg_convrate > 0x07 || (reg_status & 0x61) != 0x00) { dev_dbg(&adapter->dev, "MAX1619 detection failed at 0x%02x\n", @@ -223,8 +196,8 @@ static int max1619_detect(struct i2c_client *client, } /* identification */ - man_id = i2c_smbus_read_byte_data(client, MAX1619_REG_R_MAN_ID); - chip_id = i2c_smbus_read_byte_data(client, MAX1619_REG_R_CHIP_ID); + man_id = i2c_smbus_read_byte_data(client, MAX1619_REG_MAN_ID); + chip_id = i2c_smbus_read_byte_data(client, MAX1619_REG_CHIP_ID); if (man_id != 0x4D || chip_id != 0x04) { dev_info(&adapter->dev, "Unsupported chip (man_id=0x%02X, chip_id=0x%02X).\n", @@ -237,40 +210,82 @@ static int max1619_detect(struct i2c_client *client, return 0; } -static void max1619_init_client(struct i2c_client *client) +static int max1619_init_chip(struct regmap *regmap) { - u8 config; + int ret; - /* - * Start the conversions. - */ - i2c_smbus_write_byte_data(client, MAX1619_REG_W_CONVRATE, - 5); /* 2 Hz */ - config = i2c_smbus_read_byte_data(client, MAX1619_REG_R_CONFIG); - if (config & 0x40) - i2c_smbus_write_byte_data(client, MAX1619_REG_W_CONFIG, - config & 0xBF); /* run */ + ret = regmap_write(regmap, MAX1619_REG_CONVRATE, 5); /* 2 Hz */ + if (ret) + return ret; + + /* Start conversions */ + return regmap_clear_bits(regmap, MAX1619_REG_CONFIG, 0x40); } -static int max1619_probe(struct i2c_client *new_client) +/* regmap */ + +static int max1619_reg_read(void *context, unsigned int reg, unsigned int *val) { - struct max1619_data *data; + int ret; + + ret = i2c_smbus_read_byte_data(context, reg); + if (ret < 0) + return ret; + + *val = ret; + return 0; +} + +static int max1619_reg_write(void *context, unsigned int reg, unsigned int val) +{ + int offset = reg < MAX1619_REG_REMOTE_CRIT ? 6 : 2; + + return i2c_smbus_write_byte_data(context, reg + offset, val); +} + +static bool max1619_regmap_is_volatile(struct device *dev, unsigned int reg) +{ + return reg <= MAX1619_REG_STATUS; +} + +static bool max1619_regmap_is_writeable(struct device *dev, unsigned int reg) +{ + return reg > MAX1619_REG_STATUS && reg <= MAX1619_REG_REMOTE_CRIT_HYST; +} + +static const struct regmap_config max1619_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = MAX1619_REG_REMOTE_CRIT_HYST, + .cache_type = REGCACHE_MAPLE, + .volatile_reg = max1619_regmap_is_volatile, + .writeable_reg = max1619_regmap_is_writeable, +}; + +static const struct regmap_bus max1619_regmap_bus = { + .reg_write = max1619_reg_write, + .reg_read = max1619_reg_read, +}; + +static int max1619_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; struct device *hwmon_dev; + struct regmap *regmap; + int ret; - data = devm_kzalloc(&new_client->dev, sizeof(struct max1619_data), - GFP_KERNEL); - if (!data) - return -ENOMEM; + regmap = devm_regmap_init(dev, &max1619_regmap_bus, client, + &max1619_regmap_config); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); - data->client = new_client; - mutex_init(&data->update_lock); + ret = max1619_init_chip(regmap); + if (ret) + return ret; - /* Initialize the MAX1619 chip */ - max1619_init_client(new_client); - - hwmon_dev = devm_hwmon_device_register_with_groups(&new_client->dev, - new_client->name, - data, + hwmon_dev = devm_hwmon_device_register_with_groups(dev, + client->name, + regmap, max1619_groups); return PTR_ERR_OR_ZERO(hwmon_dev); } From f0b1f1f468f5c9bde6269e73c9a3eb57cb7e3293 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Tue, 16 Jul 2024 08:24:28 -0700 Subject: [PATCH 40/96] hwmon: (max1619) Convert to with_info API Convert driver to with_info hwmon API to simplify the code and with it its maintainability. Reviewed-by: Tzung-Bi Shih Signed-off-by: Guenter Roeck --- drivers/hwmon/max1619.c | 269 +++++++++++++++++++++++----------------- 1 file changed, 157 insertions(+), 112 deletions(-) diff --git a/drivers/hwmon/max1619.c b/drivers/hwmon/max1619.c index 76ccc3e94b4c..5159ff96ff37 100644 --- a/drivers/hwmon/max1619.c +++ b/drivers/hwmon/max1619.c @@ -14,20 +14,14 @@ #include #include -#include #include #include #include #include -#include static const unsigned short normal_i2c[] = { 0x18, 0x19, 0x1a, 0x29, 0x2a, 0x2b, 0x4c, 0x4d, 0x4e, I2C_CLIENT_END }; -/* - * The MAX1619 registers - */ - #define MAX1619_REG_LOCAL_TEMP 0x00 #define MAX1619_REG_REMOTE_TEMP 0x01 #define MAX1619_REG_STATUS 0x02 @@ -40,66 +34,6 @@ static const unsigned short normal_i2c[] = { #define MAX1619_REG_MAN_ID 0xFE #define MAX1619_REG_CHIP_ID 0xFF -enum temp_index { - t_input1 = 0, - t_input2, - t_low2, - t_high2, - t_crit2, - t_hyst2, - t_num_regs -}; - -/* - * Client data (each client gets its own) - */ - -static const u8 regs[t_num_regs] = { - [t_input1] = MAX1619_REG_LOCAL_TEMP, - [t_input2] = MAX1619_REG_REMOTE_TEMP, - [t_low2] = MAX1619_REG_REMOTE_LOW, - [t_high2] = MAX1619_REG_REMOTE_HIGH, - [t_crit2] = MAX1619_REG_REMOTE_CRIT, - [t_hyst2] = MAX1619_REG_REMOTE_CRIT_HYST, -}; - -/* - * Sysfs stuff - */ - -static ssize_t temp_show(struct device *dev, struct device_attribute *devattr, - char *buf) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct regmap *regmap = dev_get_drvdata(dev); - u32 temp; - int ret; - - ret = regmap_read(regmap, regs[attr->index], &temp); - if (ret < 0) - return ret; - - return sprintf(buf, "%d\n", sign_extend32(temp, 7) * 1000); -} - -static ssize_t temp_store(struct device *dev, - struct device_attribute *devattr, const char *buf, - size_t count) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct regmap *regmap = dev_get_drvdata(dev); - long val; - int err = kstrtol(buf, 10, &val); - if (err) - return err; - - val = DIV_ROUND_CLOSEST(clamp_val(val, -128000, 127000), 1000); - err = regmap_write(regmap, regs[attr->index], val); - if (err < 0) - return err; - return count; -} - static int get_alarms(struct regmap *regmap) { static u32 regs[2] = { MAX1619_REG_STATUS, MAX1619_REG_CONFIG }; @@ -117,62 +51,175 @@ static int get_alarms(struct regmap *regmap) return regdata[0] & 0x1e; } -static ssize_t alarms_show(struct device *dev, struct device_attribute *attr, - char *buf) +static int max1619_temp_read(struct regmap *regmap, u32 attr, int channel, long *val) { - struct regmap *regmap = dev_get_drvdata(dev); - int alarms; + int reg = -1, alarm_bit = 0; + u32 temp; + int ret; - alarms = get_alarms(regmap); - if (alarms < 0) - return alarms; - - return sprintf(buf, "%d\n", alarms); + switch (attr) { + case hwmon_temp_input: + reg = channel ? MAX1619_REG_REMOTE_TEMP : MAX1619_REG_LOCAL_TEMP; + break; + case hwmon_temp_min: + reg = MAX1619_REG_REMOTE_LOW; + break; + case hwmon_temp_max: + reg = MAX1619_REG_REMOTE_HIGH; + break; + case hwmon_temp_crit: + reg = MAX1619_REG_REMOTE_CRIT; + break; + case hwmon_temp_crit_hyst: + reg = MAX1619_REG_REMOTE_CRIT_HYST; + break; + case hwmon_temp_min_alarm: + alarm_bit = 3; + break; + case hwmon_temp_max_alarm: + alarm_bit = 4; + break; + case hwmon_temp_crit_alarm: + alarm_bit = 1; + break; + case hwmon_temp_fault: + alarm_bit = 2; + break; + default: + return -EOPNOTSUPP; + } + if (reg >= 0) { + ret = regmap_read(regmap, reg, &temp); + if (ret < 0) + return ret; + *val = sign_extend32(temp, 7) * 1000; + } else { + ret = get_alarms(regmap); + if (ret < 0) + return ret; + *val = !!(ret & BIT(alarm_bit)); + } + return 0; } -static ssize_t alarm_show(struct device *dev, struct device_attribute *attr, - char *buf) +static int max1619_chip_read(struct regmap *regmap, u32 attr, long *val) { - int bitnr = to_sensor_dev_attr(attr)->index; - struct regmap *regmap = dev_get_drvdata(dev); int alarms; - alarms = get_alarms(regmap); - if (alarms < 0) - return alarms; - - return sprintf(buf, "%d\n", (alarms >> bitnr) & 1); + switch (attr) { + case hwmon_chip_alarms: + alarms = get_alarms(regmap); + if (alarms < 0) + return alarms; + *val = alarms; + break; + default: + return -EOPNOTSUPP; + } + return 0; } -static SENSOR_DEVICE_ATTR_RO(temp1_input, temp, t_input1); -static SENSOR_DEVICE_ATTR_RO(temp2_input, temp, t_input2); -static SENSOR_DEVICE_ATTR_RW(temp2_min, temp, t_low2); -static SENSOR_DEVICE_ATTR_RW(temp2_max, temp, t_high2); -static SENSOR_DEVICE_ATTR_RW(temp2_crit, temp, t_crit2); -static SENSOR_DEVICE_ATTR_RW(temp2_crit_hyst, temp, t_hyst2); +static int max1619_read(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long *val) +{ + struct regmap *regmap = dev_get_drvdata(dev); -static DEVICE_ATTR_RO(alarms); -static SENSOR_DEVICE_ATTR_RO(temp2_crit_alarm, alarm, 1); -static SENSOR_DEVICE_ATTR_RO(temp2_fault, alarm, 2); -static SENSOR_DEVICE_ATTR_RO(temp2_min_alarm, alarm, 3); -static SENSOR_DEVICE_ATTR_RO(temp2_max_alarm, alarm, 4); + switch (type) { + case hwmon_chip: + return max1619_chip_read(regmap, attr, val); + case hwmon_temp: + return max1619_temp_read(regmap, attr, channel, val); + default: + return -EOPNOTSUPP; + } +} -static struct attribute *max1619_attrs[] = { - &sensor_dev_attr_temp1_input.dev_attr.attr, - &sensor_dev_attr_temp2_input.dev_attr.attr, - &sensor_dev_attr_temp2_min.dev_attr.attr, - &sensor_dev_attr_temp2_max.dev_attr.attr, - &sensor_dev_attr_temp2_crit.dev_attr.attr, - &sensor_dev_attr_temp2_crit_hyst.dev_attr.attr, +static int max1619_write(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long val) +{ + struct regmap *regmap = dev_get_drvdata(dev); + int reg; - &dev_attr_alarms.attr, - &sensor_dev_attr_temp2_crit_alarm.dev_attr.attr, - &sensor_dev_attr_temp2_fault.dev_attr.attr, - &sensor_dev_attr_temp2_min_alarm.dev_attr.attr, - &sensor_dev_attr_temp2_max_alarm.dev_attr.attr, + if (type != hwmon_temp) + return -EOPNOTSUPP; + + switch (attr) { + case hwmon_temp_min: + reg = MAX1619_REG_REMOTE_LOW; + break; + case hwmon_temp_max: + reg = MAX1619_REG_REMOTE_HIGH; + break; + case hwmon_temp_crit: + reg = MAX1619_REG_REMOTE_CRIT; + break; + case hwmon_temp_crit_hyst: + reg = MAX1619_REG_REMOTE_CRIT_HYST; + break; + default: + return -EOPNOTSUPP; + } + val = DIV_ROUND_CLOSEST(clamp_val(val, -128000, 127000), 1000); + return regmap_write(regmap, reg, val); +} + +static umode_t max1619_is_visible(const void *_data, enum hwmon_sensor_types type, + u32 attr, int channel) +{ + switch (type) { + case hwmon_chip: + switch (attr) { + case hwmon_chip_alarms: + return 0444; + default: + break; + } + break; + case hwmon_temp: + switch (attr) { + case hwmon_temp_input: + return 0444; + case hwmon_temp_min: + case hwmon_temp_max: + case hwmon_temp_crit: + case hwmon_temp_crit_hyst: + return 0644; + case hwmon_temp_min_alarm: + case hwmon_temp_max_alarm: + case hwmon_temp_crit_alarm: + case hwmon_temp_fault: + return 0444; + default: + break; + } + break; + default: + break; + } + return 0; +} + +static const struct hwmon_channel_info * const max1619_info[] = { + HWMON_CHANNEL_INFO(chip, HWMON_C_ALARMS), + HWMON_CHANNEL_INFO(temp, + HWMON_T_INPUT, + HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX | + HWMON_T_CRIT | HWMON_T_CRIT_HYST | + HWMON_T_MIN_ALARM | HWMON_T_MAX_ALARM | + HWMON_T_CRIT_ALARM | HWMON_T_FAULT), NULL }; -ATTRIBUTE_GROUPS(max1619); + +static const struct hwmon_ops max1619_hwmon_ops = { + .is_visible = max1619_is_visible, + .read = max1619_read, + .write = max1619_write, +}; + +static const struct hwmon_chip_info max1619_chip_info = { + .ops = &max1619_hwmon_ops, + .info = max1619_info, +}; /* Return 0 if detection is successful, -ENODEV otherwise */ static int max1619_detect(struct i2c_client *client, @@ -283,10 +330,8 @@ static int max1619_probe(struct i2c_client *client) if (ret) return ret; - hwmon_dev = devm_hwmon_device_register_with_groups(dev, - client->name, - regmap, - max1619_groups); + hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, + regmap, &max1619_chip_info, NULL); return PTR_ERR_OR_ZERO(hwmon_dev); } From 13047ebe63a8b4ffabbb97ec6edc8c7dcdb96cee Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Tue, 16 Jul 2024 09:01:27 -0700 Subject: [PATCH 41/96] hwmon: (max1619) Add support for update_interval attribute The chip supports reading and writing the conversion rate. Add support for the update_interval sysfs attribute. Reviewed-by: Tzung-Bi Shih Signed-off-by: Guenter Roeck --- drivers/hwmon/max1619.c | 50 ++++++++++++++++++++++++++++++++++------- 1 file changed, 42 insertions(+), 8 deletions(-) diff --git a/drivers/hwmon/max1619.c b/drivers/hwmon/max1619.c index 5159ff96ff37..72e17afb22f0 100644 --- a/drivers/hwmon/max1619.c +++ b/drivers/hwmon/max1619.c @@ -18,6 +18,7 @@ #include #include #include +#include static const unsigned short normal_i2c[] = { 0x18, 0x19, 0x1a, 0x29, 0x2a, 0x2b, 0x4c, 0x4d, 0x4e, I2C_CLIENT_END }; @@ -102,11 +103,20 @@ static int max1619_temp_read(struct regmap *regmap, u32 attr, int channel, long return 0; } +static u16 update_intervals[] = { 16000, 8000, 4000, 2000, 1000, 500, 250, 125 }; + static int max1619_chip_read(struct regmap *regmap, u32 attr, long *val) { - int alarms; + int alarms, ret; + u32 regval; switch (attr) { + case hwmon_chip_update_interval: + ret = regmap_read(regmap, MAX1619_REG_CONVRATE, ®val); + if (ret < 0) + return ret; + *val = update_intervals[regval & 7]; + break; case hwmon_chip_alarms: alarms = get_alarms(regmap); if (alarms < 0) @@ -134,14 +144,21 @@ static int max1619_read(struct device *dev, enum hwmon_sensor_types type, } } -static int max1619_write(struct device *dev, enum hwmon_sensor_types type, - u32 attr, int channel, long val) +static int max1619_chip_write(struct regmap *regmap, u32 attr, long val) { - struct regmap *regmap = dev_get_drvdata(dev); - int reg; - - if (type != hwmon_temp) + switch (attr) { + case hwmon_chip_update_interval: + val = find_closest_descending(val, update_intervals, ARRAY_SIZE(update_intervals)); + return regmap_write(regmap, MAX1619_REG_CONVRATE, val); + default: return -EOPNOTSUPP; + } +} + +static int max1619_temp_write(struct regmap *regmap, + u32 attr, int channel, long val) +{ + int reg; switch (attr) { case hwmon_temp_min: @@ -163,12 +180,29 @@ static int max1619_write(struct device *dev, enum hwmon_sensor_types type, return regmap_write(regmap, reg, val); } +static int max1619_write(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long val) +{ + struct regmap *regmap = dev_get_drvdata(dev); + + switch (type) { + case hwmon_chip: + return max1619_chip_write(regmap, attr, val); + case hwmon_temp: + return max1619_temp_write(regmap, attr, channel, val); + default: + return -EOPNOTSUPP; + } +} + static umode_t max1619_is_visible(const void *_data, enum hwmon_sensor_types type, u32 attr, int channel) { switch (type) { case hwmon_chip: switch (attr) { + case hwmon_chip_update_interval: + return 0644; case hwmon_chip_alarms: return 0444; default: @@ -200,7 +234,7 @@ static umode_t max1619_is_visible(const void *_data, enum hwmon_sensor_types typ } static const struct hwmon_channel_info * const max1619_info[] = { - HWMON_CHANNEL_INFO(chip, HWMON_C_ALARMS), + HWMON_CHANNEL_INFO(chip, HWMON_C_ALARMS | HWMON_C_UPDATE_INTERVAL), HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT, HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX | From 90368fa3978fee3a5df5cbee3312e3fe51f7a8c5 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Fri, 26 Jul 2024 15:29:40 -0700 Subject: [PATCH 42/96] hwmon: (max1619) Improve chip detection code Bail out immediately if reading any of the registers used for chip detection fails, or if it returns an unexpected value. Drop all log messages from detection code. Reviewed-by: Tzung-Bi Shih Signed-off-by: Guenter Roeck --- drivers/hwmon/max1619.c | 32 ++++++++++++++------------------ 1 file changed, 14 insertions(+), 18 deletions(-) diff --git a/drivers/hwmon/max1619.c b/drivers/hwmon/max1619.c index 72e17afb22f0..9b6d03cff4df 100644 --- a/drivers/hwmon/max1619.c +++ b/drivers/hwmon/max1619.c @@ -260,31 +260,27 @@ static int max1619_detect(struct i2c_client *client, struct i2c_board_info *info) { struct i2c_adapter *adapter = client->adapter; - u8 reg_config, reg_convrate, reg_status, man_id, chip_id; + int regval; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) return -ENODEV; - /* detection */ - reg_config = i2c_smbus_read_byte_data(client, MAX1619_REG_CONFIG); - reg_convrate = i2c_smbus_read_byte_data(client, MAX1619_REG_CONVRATE); - reg_status = i2c_smbus_read_byte_data(client, MAX1619_REG_STATUS); - if ((reg_config & 0x03) != 0x00 - || reg_convrate > 0x07 || (reg_status & 0x61) != 0x00) { - dev_dbg(&adapter->dev, "MAX1619 detection failed at 0x%02x\n", - client->addr); + regval = i2c_smbus_read_byte_data(client, MAX1619_REG_CONFIG); + if (regval < 0 || (regval & 0x03)) + return -ENODEV; + regval = i2c_smbus_read_byte_data(client, MAX1619_REG_CONVRATE); + if (regval < 0 || regval > 0x07) + return -ENODEV; + regval = i2c_smbus_read_byte_data(client, MAX1619_REG_STATUS); + if (regval < 0 || (regval & 0x61)) return -ENODEV; - } - /* identification */ - man_id = i2c_smbus_read_byte_data(client, MAX1619_REG_MAN_ID); - chip_id = i2c_smbus_read_byte_data(client, MAX1619_REG_CHIP_ID); - if (man_id != 0x4D || chip_id != 0x04) { - dev_info(&adapter->dev, - "Unsupported chip (man_id=0x%02X, chip_id=0x%02X).\n", - man_id, chip_id); + regval = i2c_smbus_read_byte_data(client, MAX1619_REG_MAN_ID); + if (regval != 0x4d) + return -ENODEV; + regval = i2c_smbus_read_byte_data(client, MAX1619_REG_CHIP_ID); + if (regval != 0x04) return -ENODEV; - } strscpy(info->type, "max1619", I2C_NAME_SIZE); From 85f72ffe0f302c0837dc9dad5ba75dfcd5b35acd Mon Sep 17 00:00:00 2001 From: "Rob Herring (Arm)" Date: Wed, 31 Jul 2024 13:12:51 -0600 Subject: [PATCH 43/96] hwmon: (vexpress) Use of_property_present() Use of_property_present() to test for property presence rather than of_get_property(). This is part of a larger effort to remove callers of of_get_property() and similar functions. of_get_property() leaks the DT property data pointer which is a problem for dynamically allocated nodes which may be freed. Signed-off-by: Rob Herring (Arm) Reviewed-by: Sudeep Holla Link: https://lore.kernel.org/r/20240731191312.1710417-13-robh@kernel.org Signed-off-by: Guenter Roeck --- drivers/hwmon/vexpress-hwmon.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hwmon/vexpress-hwmon.c b/drivers/hwmon/vexpress-hwmon.c index d82a3b454d0e..a2e350f52a9e 100644 --- a/drivers/hwmon/vexpress-hwmon.c +++ b/drivers/hwmon/vexpress-hwmon.c @@ -72,7 +72,7 @@ static umode_t vexpress_hwmon_attr_is_visible(struct kobject *kobj, struct device_attribute, attr); if (dev_attr->show == vexpress_hwmon_label_show && - !of_get_property(dev->of_node, "label", NULL)) + !of_property_present(dev->of_node, "label")) return 0; return attr->mode; From 3a3dbff8a71ae7c8c6fbf900864ab79fd001bd29 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Wed, 17 Jul 2024 12:13:12 -0700 Subject: [PATCH 44/96] hwmon: (lm92) Improve auto-detection accuracy Checking three configuration register bits and the manufacturer ID register to auto-detect LM92 is a bit vague. Repeat twice on replicated register addresses to improve detection accuracy. Check the manufacturer ID first and bail out immediately without reading the other register if there is a mismatch to reduce the number of i2c transfers needed in that case. Also explicitly test for an error from reading the configuration register to avoid potential situations where the returned error masked against 0xe0 is 0. While at it, drop "lm92: Found National Semiconductor LM92 chip" detection noise. Reviewed-by: Tzung-Bi Shih Signed-off-by: Guenter Roeck --- drivers/hwmon/lm92.c | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/drivers/hwmon/lm92.c b/drivers/hwmon/lm92.c index 46579a3e1715..e2d8c8afcbfa 100644 --- a/drivers/hwmon/lm92.c +++ b/drivers/hwmon/lm92.c @@ -272,20 +272,28 @@ static int lm92_detect(struct i2c_client *new_client, struct i2c_board_info *info) { struct i2c_adapter *adapter = new_client->adapter; - u8 config; - u16 man_id; + u8 config_addr = LM92_REG_CONFIG; + u8 man_id_addr = LM92_REG_MAN_ID; + int i, regval; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA)) return -ENODEV; - config = i2c_smbus_read_byte_data(new_client, LM92_REG_CONFIG); - man_id = i2c_smbus_read_word_data(new_client, LM92_REG_MAN_ID); - - if ((config & 0xe0) == 0x00 && man_id == 0x0180) - pr_info("lm92: Found National Semiconductor LM92 chip\n"); - else - return -ENODEV; + /* + * Register values repeat with multiples of 8. + * Read twice to improve detection accuracy. + */ + for (i = 0; i < 2; i++) { + regval = i2c_smbus_read_word_data(new_client, man_id_addr); + if (regval != 0x0180) + return -ENODEV; + regval = i2c_smbus_read_byte_data(new_client, config_addr); + if (regval < 0 || (regval & 0xe0)) + return -ENODEV; + config_addr += 8; + man_id_addr += 8; + } strscpy(info->type, "lm92", I2C_NAME_SIZE); From 82efdeff3ce722c74af7e95b7c8c060e272c4dd0 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Tue, 16 Jul 2024 20:57:19 -0700 Subject: [PATCH 45/96] hwmon: (lm92) Reorder include files to alphabetic order Simplify driver maintenance by reordering files to alphabetic order. No functional change. Reviewed-by: Tzung-Bi Shih Signed-off-by: Guenter Roeck --- drivers/hwmon/lm92.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/hwmon/lm92.c b/drivers/hwmon/lm92.c index e2d8c8afcbfa..b0179d72b779 100644 --- a/drivers/hwmon/lm92.c +++ b/drivers/hwmon/lm92.c @@ -27,15 +27,15 @@ * with the LM92. */ -#include -#include -#include -#include +#include #include #include -#include -#include +#include +#include #include +#include +#include +#include /* * The LM92 and MAX6635 have 2 two-state pins for address selection, From fcb49571b4d7a993190d4dc40331ac5c7350d2dd Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Tue, 16 Jul 2024 21:48:07 -0700 Subject: [PATCH 46/96] hwmon: (lm92) Replace chip IDs with limit register resolution The chip IDs are not used by the driver. Drop them. Use driver data to store the limit register resolution instead, and use this information when writing temperature limits to improve chip specific rounding and to avoid writing into unused register bits. Reviewed-by: Tzung-Bi Shih Signed-off-by: Guenter Roeck --- drivers/hwmon/lm92.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/drivers/hwmon/lm92.c b/drivers/hwmon/lm92.c index b0179d72b779..5ff45a0033f1 100644 --- a/drivers/hwmon/lm92.c +++ b/drivers/hwmon/lm92.c @@ -43,8 +43,6 @@ */ static const unsigned short normal_i2c[] = { 0x48, 0x49, 0x4a, 0x4b, I2C_CLIENT_END }; -enum chips { lm92, max6635 }; - /* The LM92 registers */ #define LM92_REG_CONFIG 0x01 /* 8-bit, RW */ #define LM92_REG_TEMP 0x00 /* 16-bit, RO */ @@ -66,10 +64,10 @@ static inline int TEMP_FROM_REG(s16 reg) return reg / 8 * 625 / 10; } -static inline s16 TEMP_TO_REG(long val) +static inline s16 TEMP_TO_REG(long val, int resolution) { val = clamp_val(val, -60000, 160000); - return val * 10 / 625 * 8; + return DIV_ROUND_CLOSEST(val << (resolution - 9), 1000) << (16 - resolution); } /* Alarm flags are stored in the 3 LSB of the temperature register */ @@ -99,6 +97,7 @@ static const u8 regs[t_num_regs] = { struct lm92_data { struct i2c_client *client; struct mutex update_lock; + int resolution; bool valid; /* false until following fields are valid */ unsigned long last_updated; /* in jiffies */ @@ -159,7 +158,7 @@ static ssize_t temp_store(struct device *dev, return err; mutex_lock(&data->update_lock); - data->temp[nr] = TEMP_TO_REG(val); + data->temp[nr] = TEMP_TO_REG(val, data->resolution); i2c_smbus_write_word_swapped(client, regs[nr], data->temp[nr]); mutex_unlock(&data->update_lock); return count; @@ -201,7 +200,8 @@ static ssize_t temp_hyst_store(struct device *dev, val = clamp_val(val, -120000, 220000); mutex_lock(&data->update_lock); data->temp[t_hyst] = - TEMP_TO_REG(TEMP_FROM_REG(data->temp[attr->index]) - val); + TEMP_TO_REG(TEMP_FROM_REG(data->temp[attr->index]) - val, + data->resolution); i2c_smbus_write_word_swapped(client, LM92_REG_TEMP_HYST, data->temp[t_hyst]); mutex_unlock(&data->update_lock); @@ -311,6 +311,7 @@ static int lm92_probe(struct i2c_client *new_client) return -ENOMEM; data->client = new_client; + data->resolution = (unsigned long)i2c_get_match_data(new_client); mutex_init(&data->update_lock); /* Initialize the chipset */ @@ -326,9 +327,10 @@ static int lm92_probe(struct i2c_client *new_client) * Module and driver stuff */ +/* .driver_data is limit register resolution */ static const struct i2c_device_id lm92_id[] = { - { "lm92", lm92 }, - { "max6635", max6635 }, + { "lm92", 13 }, + { "max6635", 9 }, { } }; MODULE_DEVICE_TABLE(i2c, lm92_id); From 1359590a5c48d1d28cd11808178931f146a539e2 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Tue, 16 Jul 2024 17:54:25 -0700 Subject: [PATCH 47/96] hwmon: (lm92) Convert to use regmap Use regmap for local caching and for multi-byte operations to be able to use regmap API functions and to reduce the need for locking in the driver. Reviewed-by: Tzung-Bi Shih Signed-off-by: Guenter Roeck --- drivers/hwmon/Kconfig | 1 + drivers/hwmon/lm92.c | 204 +++++++++++++++++++++++++++--------------- 2 files changed, 131 insertions(+), 74 deletions(-) diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index e838a55bb3cb..437334d10d3b 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -1511,6 +1511,7 @@ config SENSORS_LM90 config SENSORS_LM92 tristate "National Semiconductor LM92 and compatibles" depends on I2C + select REGMAP_I2C help If you say yes here you get support for National Semiconductor LM92 and Maxim MAX6635 sensor chips. diff --git a/drivers/hwmon/lm92.c b/drivers/hwmon/lm92.c index 5ff45a0033f1..89befc7f59a5 100644 --- a/drivers/hwmon/lm92.c +++ b/drivers/hwmon/lm92.c @@ -32,9 +32,9 @@ #include #include #include -#include #include #include +#include #include /* @@ -81,65 +81,40 @@ enum temp_index { t_crit, t_min, t_max, - t_hyst, t_num_regs }; -static const u8 regs[t_num_regs] = { +static const u8 lm92_regs[t_num_regs] = { [t_input] = LM92_REG_TEMP, [t_crit] = LM92_REG_TEMP_CRIT, [t_min] = LM92_REG_TEMP_LOW, [t_max] = LM92_REG_TEMP_HIGH, - [t_hyst] = LM92_REG_TEMP_HYST, }; /* Client data (each client gets its own) */ struct lm92_data { - struct i2c_client *client; + struct regmap *regmap; struct mutex update_lock; int resolution; - bool valid; /* false until following fields are valid */ - unsigned long last_updated; /* in jiffies */ - - /* registers values */ - s16 temp[t_num_regs]; /* index with enum temp_index */ }; /* * Sysfs attributes and callback functions */ -static struct lm92_data *lm92_update_device(struct device *dev) -{ - struct lm92_data *data = dev_get_drvdata(dev); - struct i2c_client *client = data->client; - int i; - - mutex_lock(&data->update_lock); - - if (time_after(jiffies, data->last_updated + HZ) || - !data->valid) { - dev_dbg(&client->dev, "Updating lm92 data\n"); - for (i = 0; i < t_num_regs; i++) { - data->temp[i] = - i2c_smbus_read_word_swapped(client, regs[i]); - } - data->last_updated = jiffies; - data->valid = true; - } - - mutex_unlock(&data->update_lock); - - return data; -} - static ssize_t temp_show(struct device *dev, struct device_attribute *devattr, char *buf) { struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct lm92_data *data = lm92_update_device(dev); + struct lm92_data *data = dev_get_drvdata(dev); + u32 temp; + int err; - return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp[attr->index])); + err = regmap_read(data->regmap, lm92_regs[attr->index], &temp); + if (err) + return err; + + return sprintf(buf, "%d\n", TEMP_FROM_REG(temp)); } static ssize_t temp_store(struct device *dev, @@ -148,7 +123,7 @@ static ssize_t temp_store(struct device *dev, { struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct lm92_data *data = dev_get_drvdata(dev); - struct i2c_client *client = data->client; + struct regmap *regmap = data->regmap; int nr = attr->index; long val; int err; @@ -157,10 +132,9 @@ static ssize_t temp_store(struct device *dev, if (err) return err; - mutex_lock(&data->update_lock); - data->temp[nr] = TEMP_TO_REG(val, data->resolution); - i2c_smbus_write_word_swapped(client, regs[nr], data->temp[nr]); - mutex_unlock(&data->update_lock); + err = regmap_write(regmap, lm92_regs[nr], TEMP_TO_REG(val, data->resolution)); + if (err) + return err; return count; } @@ -168,28 +142,42 @@ static ssize_t temp_hyst_show(struct device *dev, struct device_attribute *devattr, char *buf) { struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct lm92_data *data = lm92_update_device(dev); + u32 regs[2] = { lm92_regs[attr->index], LM92_REG_TEMP_HYST }; + struct lm92_data *data = dev_get_drvdata(dev); + u16 regvals[2]; + int err; - return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp[attr->index]) - - TEMP_FROM_REG(data->temp[t_hyst])); + err = regmap_multi_reg_read(data->regmap, regs, regvals, 2); + if (err) + return err; + + return sprintf(buf, "%d\n", + TEMP_FROM_REG(regvals[0]) - TEMP_FROM_REG(regvals[1])); } static ssize_t temp1_min_hyst_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct lm92_data *data = lm92_update_device(dev); + static u32 regs[2] = { LM92_REG_TEMP_LOW, LM92_REG_TEMP_HYST }; + struct lm92_data *data = dev_get_drvdata(dev); + u16 regvals[2]; + int err; - return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp[t_min]) - + TEMP_FROM_REG(data->temp[t_hyst])); + err = regmap_multi_reg_read(data->regmap, regs, regvals, 2); + if (err) + return err; + + return sprintf(buf, "%d\n", + TEMP_FROM_REG(regvals[0]) + TEMP_FROM_REG(regvals[1])); } static ssize_t temp_hyst_store(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) { - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct lm92_data *data = dev_get_drvdata(dev); - struct i2c_client *client = data->client; + struct regmap *regmap = data->regmap; + u32 temp; long val; int err; @@ -199,29 +187,45 @@ static ssize_t temp_hyst_store(struct device *dev, val = clamp_val(val, -120000, 220000); mutex_lock(&data->update_lock); - data->temp[t_hyst] = - TEMP_TO_REG(TEMP_FROM_REG(data->temp[attr->index]) - val, - data->resolution); - i2c_smbus_write_word_swapped(client, LM92_REG_TEMP_HYST, - data->temp[t_hyst]); + err = regmap_read(regmap, LM92_REG_TEMP_CRIT, &temp); + if (err) + goto unlock; + val = TEMP_TO_REG(TEMP_FROM_REG(temp) - val, data->resolution); + err = regmap_write(regmap, LM92_REG_TEMP_HYST, val); +unlock: mutex_unlock(&data->update_lock); + if (err) + return err; return count; } static ssize_t alarms_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct lm92_data *data = lm92_update_device(dev); + struct lm92_data *data = dev_get_drvdata(dev); + u32 temp; + int err; - return sprintf(buf, "%d\n", ALARMS_FROM_REG(data->temp[t_input])); + err = regmap_read(data->regmap, LM92_REG_TEMP, &temp); + if (err) + return err; + + return sprintf(buf, "%d\n", ALARMS_FROM_REG(temp)); } static ssize_t alarm_show(struct device *dev, struct device_attribute *attr, char *buf) { + struct lm92_data *data = dev_get_drvdata(dev); int bitnr = to_sensor_dev_attr(attr)->index; - struct lm92_data *data = lm92_update_device(dev); - return sprintf(buf, "%d\n", (data->temp[t_input] >> bitnr) & 1); + u32 temp; + int err; + + err = regmap_read(data->regmap, LM92_REG_TEMP, &temp); + if (err) + return err; + + return sprintf(buf, "%d\n", (temp >> bitnr) & 1); } static SENSOR_DEVICE_ATTR_RO(temp1_input, temp, t_input); @@ -240,15 +244,9 @@ static SENSOR_DEVICE_ATTR_RO(temp1_max_alarm, alarm, 1); * Detection and registration */ -static void lm92_init_client(struct i2c_client *client) +static int lm92_init_client(struct regmap *regmap) { - u8 config; - - /* Start the conversions if needed */ - config = i2c_smbus_read_byte_data(client, LM92_REG_CONFIG); - if (config & 0x01) - i2c_smbus_write_byte_data(client, LM92_REG_CONFIG, - config & 0xFE); + return regmap_clear_bits(regmap, LM92_REG_CONFIG, 0x01); } static struct attribute *lm92_attrs[] = { @@ -300,25 +298,83 @@ static int lm92_detect(struct i2c_client *new_client, return 0; } -static int lm92_probe(struct i2c_client *new_client) +/* regmap */ + +static int lm92_reg_read(void *context, unsigned int reg, unsigned int *val) { + int ret; + + if (reg == LM92_REG_CONFIG) + ret = i2c_smbus_read_byte_data(context, reg); + else + ret = i2c_smbus_read_word_swapped(context, reg); + if (ret < 0) + return ret; + + *val = ret; + return 0; +} + +static int lm92_reg_write(void *context, unsigned int reg, unsigned int val) +{ + if (reg == LM92_REG_CONFIG) + return i2c_smbus_write_byte_data(context, LM92_REG_CONFIG, val); + + return i2c_smbus_write_word_swapped(context, reg, val); +} + +static bool lm92_regmap_is_volatile(struct device *dev, unsigned int reg) +{ + return reg == LM92_REG_TEMP; +} + +static bool lm92_regmap_is_writeable(struct device *dev, unsigned int reg) +{ + return reg >= LM92_REG_CONFIG; +} + +static const struct regmap_config lm92_regmap_config = { + .reg_bits = 8, + .val_bits = 16, + .max_register = LM92_REG_TEMP_HIGH, + .cache_type = REGCACHE_MAPLE, + .volatile_reg = lm92_regmap_is_volatile, + .writeable_reg = lm92_regmap_is_writeable, +}; + +static const struct regmap_bus lm92_regmap_bus = { + .reg_write = lm92_reg_write, + .reg_read = lm92_reg_read, +}; + +static int lm92_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; struct device *hwmon_dev; struct lm92_data *data; + struct regmap *regmap; + int err; - data = devm_kzalloc(&new_client->dev, sizeof(struct lm92_data), - GFP_KERNEL); + regmap = devm_regmap_init(dev, &lm92_regmap_bus, client, + &lm92_regmap_config); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + data = devm_kzalloc(dev, sizeof(struct lm92_data), GFP_KERNEL); if (!data) return -ENOMEM; - data->client = new_client; - data->resolution = (unsigned long)i2c_get_match_data(new_client); + data->regmap = regmap; + data->resolution = (unsigned long)i2c_get_match_data(client); mutex_init(&data->update_lock); /* Initialize the chipset */ - lm92_init_client(new_client); + err = lm92_init_client(regmap); + if (err) + return err; - hwmon_dev = devm_hwmon_device_register_with_groups(&new_client->dev, - new_client->name, + hwmon_dev = devm_hwmon_device_register_with_groups(dev, + client->name, data, lm92_groups); return PTR_ERR_OR_ZERO(hwmon_dev); } From c21eced1157d8b78745332b2585e47eb459d6a40 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Tue, 16 Jul 2024 21:46:43 -0700 Subject: [PATCH 48/96] hwmon: (lm92) Convert to with_info hwmon API Convert driver to with_info hwmon API to simplify the code and to reduce its size. No functional change. Reviewed-by: Tzung-Bi Shih Signed-off-by: Guenter Roeck --- drivers/hwmon/lm92.c | 347 +++++++++++++++++++++++-------------------- 1 file changed, 188 insertions(+), 159 deletions(-) diff --git a/drivers/hwmon/lm92.c b/drivers/hwmon/lm92.c index 89befc7f59a5..0be439b38ee1 100644 --- a/drivers/hwmon/lm92.c +++ b/drivers/hwmon/lm92.c @@ -29,7 +29,6 @@ #include #include -#include #include #include #include @@ -76,21 +75,6 @@ static inline u8 ALARMS_FROM_REG(s16 reg) return reg & 0x0007; } -enum temp_index { - t_input, - t_crit, - t_min, - t_max, - t_num_regs -}; - -static const u8 lm92_regs[t_num_regs] = { - [t_input] = LM92_REG_TEMP, - [t_crit] = LM92_REG_TEMP_CRIT, - [t_min] = LM92_REG_TEMP_LOW, - [t_max] = LM92_REG_TEMP_HIGH, -}; - /* Client data (each client gets its own) */ struct lm92_data { struct regmap *regmap; @@ -98,147 +82,209 @@ struct lm92_data { int resolution; }; -/* - * Sysfs attributes and callback functions - */ - -static ssize_t temp_show(struct device *dev, struct device_attribute *devattr, - char *buf) +static int lm92_temp_read(struct lm92_data *data, u32 attr, int channel, long *val) { - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct lm92_data *data = dev_get_drvdata(dev); - u32 temp; - int err; - - err = regmap_read(data->regmap, lm92_regs[attr->index], &temp); - if (err) - return err; - - return sprintf(buf, "%d\n", TEMP_FROM_REG(temp)); -} - -static ssize_t temp_store(struct device *dev, - struct device_attribute *devattr, const char *buf, - size_t count) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct lm92_data *data = dev_get_drvdata(dev); - struct regmap *regmap = data->regmap; - int nr = attr->index; - long val; - int err; - - err = kstrtol(buf, 10, &val); - if (err) - return err; - - err = regmap_write(regmap, lm92_regs[nr], TEMP_TO_REG(val, data->resolution)); - if (err) - return err; - return count; -} - -static ssize_t temp_hyst_show(struct device *dev, - struct device_attribute *devattr, char *buf) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - u32 regs[2] = { lm92_regs[attr->index], LM92_REG_TEMP_HYST }; - struct lm92_data *data = dev_get_drvdata(dev); - u16 regvals[2]; - int err; - - err = regmap_multi_reg_read(data->regmap, regs, regvals, 2); - if (err) - return err; - - return sprintf(buf, "%d\n", - TEMP_FROM_REG(regvals[0]) - TEMP_FROM_REG(regvals[1])); -} - -static ssize_t temp1_min_hyst_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - static u32 regs[2] = { LM92_REG_TEMP_LOW, LM92_REG_TEMP_HYST }; - struct lm92_data *data = dev_get_drvdata(dev); - u16 regvals[2]; - int err; - - err = regmap_multi_reg_read(data->regmap, regs, regvals, 2); - if (err) - return err; - - return sprintf(buf, "%d\n", - TEMP_FROM_REG(regvals[0]) + TEMP_FROM_REG(regvals[1])); -} - -static ssize_t temp_hyst_store(struct device *dev, - struct device_attribute *devattr, - const char *buf, size_t count) -{ - struct lm92_data *data = dev_get_drvdata(dev); + int reg = -1, hyst_reg = -1, alarm_bit = 0; struct regmap *regmap = data->regmap; u32 temp; - long val; - int err; + int ret; - err = kstrtol(buf, 10, &val); - if (err) - return err; + switch (attr) { + case hwmon_temp_input: + reg = LM92_REG_TEMP; + break; + case hwmon_temp_min: + reg = LM92_REG_TEMP_LOW; + break; + case hwmon_temp_max: + reg = LM92_REG_TEMP_HIGH; + break; + case hwmon_temp_crit: + reg = LM92_REG_TEMP_CRIT; + break; + case hwmon_temp_min_hyst: + hyst_reg = LM92_REG_TEMP_LOW; + break; + case hwmon_temp_max_hyst: + hyst_reg = LM92_REG_TEMP_HIGH; + break; + case hwmon_temp_crit_hyst: + hyst_reg = LM92_REG_TEMP_CRIT; + break; + case hwmon_temp_min_alarm: + alarm_bit = 0; + break; + case hwmon_temp_max_alarm: + alarm_bit = 1; + break; + case hwmon_temp_crit_alarm: + alarm_bit = 2; + break; + default: + return -EOPNOTSUPP; + } + if (reg >= 0) { + ret = regmap_read(regmap, reg, &temp); + if (ret < 0) + return ret; + *val = TEMP_FROM_REG(temp); + } else if (hyst_reg >= 0) { + u32 regs[2] = { hyst_reg, LM92_REG_TEMP_HYST }; + u16 regvals[2]; - val = clamp_val(val, -120000, 220000); - mutex_lock(&data->update_lock); - err = regmap_read(regmap, LM92_REG_TEMP_CRIT, &temp); - if (err) - goto unlock; - val = TEMP_TO_REG(TEMP_FROM_REG(temp) - val, data->resolution); - err = regmap_write(regmap, LM92_REG_TEMP_HYST, val); + ret = regmap_multi_reg_read(regmap, regs, regvals, 2); + if (ret) + return ret; + if (attr == hwmon_temp_min_hyst) + *val = TEMP_FROM_REG(regvals[0]) + TEMP_FROM_REG(regvals[1]); + else + *val = TEMP_FROM_REG(regvals[0]) - TEMP_FROM_REG(regvals[1]); + } else { + ret = regmap_read(regmap, LM92_REG_TEMP, &temp); + if (ret) + return ret; + *val = !!(temp & BIT(alarm_bit)); + } + return 0; +} + +static int lm92_chip_read(struct lm92_data *data, u32 attr, long *val) +{ + u32 temp; + int ret; + + switch (attr) { + case hwmon_chip_alarms: + ret = regmap_read(data->regmap, LM92_REG_TEMP, &temp); + if (ret) + return ret; + *val = ALARMS_FROM_REG(temp); + break; + default: + return -EOPNOTSUPP; + } + return 0; +} + +static int lm92_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, + int channel, long *val) +{ + struct lm92_data *data = dev_get_drvdata(dev); + + switch (type) { + case hwmon_chip: + return lm92_chip_read(data, attr, val); + case hwmon_temp: + return lm92_temp_read(data, attr, channel, val); + default: + return -EOPNOTSUPP; + } +} + +static int lm92_temp_write(struct lm92_data *data, u32 attr, long val) +{ + struct regmap *regmap = data->regmap; + int reg, err; + u32 temp; + + switch (attr) { + case hwmon_temp_min: + reg = LM92_REG_TEMP_LOW; + break; + case hwmon_temp_max: + reg = LM92_REG_TEMP_HIGH; + break; + case hwmon_temp_crit: + reg = LM92_REG_TEMP_CRIT; + break; + case hwmon_temp_crit_hyst: + val = clamp_val(val, -120000, 220000); + mutex_lock(&data->update_lock); + err = regmap_read(regmap, LM92_REG_TEMP_CRIT, &temp); + if (err) + goto unlock; + val = TEMP_TO_REG(TEMP_FROM_REG(temp) - val, data->resolution); + err = regmap_write(regmap, LM92_REG_TEMP_HYST, val); unlock: - mutex_unlock(&data->update_lock); - if (err) + mutex_unlock(&data->update_lock); return err; - return count; + default: + return -EOPNOTSUPP; + } + return regmap_write(regmap, reg, TEMP_TO_REG(val, data->resolution)); } -static ssize_t alarms_show(struct device *dev, struct device_attribute *attr, - char *buf) +static int lm92_write(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long val) { struct lm92_data *data = dev_get_drvdata(dev); - u32 temp; - int err; - err = regmap_read(data->regmap, LM92_REG_TEMP, &temp); - if (err) - return err; - - return sprintf(buf, "%d\n", ALARMS_FROM_REG(temp)); + switch (type) { + case hwmon_temp: + return lm92_temp_write(data, attr, val); + default: + return -EOPNOTSUPP; + } } -static ssize_t alarm_show(struct device *dev, struct device_attribute *attr, - char *buf) +static umode_t lm92_is_visible(const void *_data, enum hwmon_sensor_types type, + u32 attr, int channel) { - struct lm92_data *data = dev_get_drvdata(dev); - int bitnr = to_sensor_dev_attr(attr)->index; - u32 temp; - int err; - - err = regmap_read(data->regmap, LM92_REG_TEMP, &temp); - if (err) - return err; - - return sprintf(buf, "%d\n", (temp >> bitnr) & 1); + switch (type) { + case hwmon_chip: + switch (attr) { + case hwmon_chip_alarms: + return 0444; + default: + break; + } + break; + case hwmon_temp: + switch (attr) { + case hwmon_temp_min: + case hwmon_temp_max: + case hwmon_temp_crit: + case hwmon_temp_crit_hyst: + return 0644; + case hwmon_temp_input: + case hwmon_temp_min_hyst: + case hwmon_temp_max_hyst: + case hwmon_temp_min_alarm: + case hwmon_temp_max_alarm: + case hwmon_temp_crit_alarm: + return 0444; + default: + break; + } + break; + default: + break; + } + return 0; } -static SENSOR_DEVICE_ATTR_RO(temp1_input, temp, t_input); -static SENSOR_DEVICE_ATTR_RW(temp1_crit, temp, t_crit); -static SENSOR_DEVICE_ATTR_RW(temp1_crit_hyst, temp_hyst, t_crit); -static SENSOR_DEVICE_ATTR_RW(temp1_min, temp, t_min); -static DEVICE_ATTR_RO(temp1_min_hyst); -static SENSOR_DEVICE_ATTR_RW(temp1_max, temp, t_max); -static SENSOR_DEVICE_ATTR_RO(temp1_max_hyst, temp_hyst, t_max); -static DEVICE_ATTR_RO(alarms); -static SENSOR_DEVICE_ATTR_RO(temp1_crit_alarm, alarm, 2); -static SENSOR_DEVICE_ATTR_RO(temp1_min_alarm, alarm, 0); -static SENSOR_DEVICE_ATTR_RO(temp1_max_alarm, alarm, 1); +static const struct hwmon_channel_info * const lm92_info[] = { + HWMON_CHANNEL_INFO(chip, HWMON_C_ALARMS), + HWMON_CHANNEL_INFO(temp, + HWMON_T_INPUT | + HWMON_T_MIN | HWMON_T_MIN_HYST | + HWMON_T_MAX | HWMON_T_MAX_HYST | + HWMON_T_CRIT | HWMON_T_CRIT_HYST | + HWMON_T_MIN_ALARM | HWMON_T_MAX_ALARM | + HWMON_T_CRIT_ALARM), + NULL +}; + +static const struct hwmon_ops lm92_hwmon_ops = { + .is_visible = lm92_is_visible, + .read = lm92_read, + .write = lm92_write, +}; + +static const struct hwmon_chip_info lm92_chip_info = { + .ops = &lm92_hwmon_ops, + .info = lm92_info, +}; /* * Detection and registration @@ -249,22 +295,6 @@ static int lm92_init_client(struct regmap *regmap) return regmap_clear_bits(regmap, LM92_REG_CONFIG, 0x01); } -static struct attribute *lm92_attrs[] = { - &sensor_dev_attr_temp1_input.dev_attr.attr, - &sensor_dev_attr_temp1_crit.dev_attr.attr, - &sensor_dev_attr_temp1_crit_hyst.dev_attr.attr, - &sensor_dev_attr_temp1_min.dev_attr.attr, - &dev_attr_temp1_min_hyst.attr, - &sensor_dev_attr_temp1_max.dev_attr.attr, - &sensor_dev_attr_temp1_max_hyst.dev_attr.attr, - &dev_attr_alarms.attr, - &sensor_dev_attr_temp1_crit_alarm.dev_attr.attr, - &sensor_dev_attr_temp1_min_alarm.dev_attr.attr, - &sensor_dev_attr_temp1_max_alarm.dev_attr.attr, - NULL -}; -ATTRIBUTE_GROUPS(lm92); - /* Return 0 if detection is successful, -ENODEV otherwise */ static int lm92_detect(struct i2c_client *new_client, struct i2c_board_info *info) @@ -373,9 +403,8 @@ static int lm92_probe(struct i2c_client *client) if (err) return err; - hwmon_dev = devm_hwmon_device_register_with_groups(dev, - client->name, - data, lm92_groups); + hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, data, + &lm92_chip_info, NULL); return PTR_ERR_OR_ZERO(hwmon_dev); } From 05aa8cbeabc07b07ff7f035e9b848aa008b831ee Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sun, 28 Jul 2024 15:52:25 -0700 Subject: [PATCH 49/96] hwmon: (lm92) Update documentation Update datasheet references. Replace misleading 'force parameter needed' with 'must be instantiated explicitly'. Explain the reason for the missing auto-detection. Mention all supported chips in Kconfig. Reviewed-by: Tzung-Bi Shih Signed-off-by: Guenter Roeck --- Documentation/hwmon/lm92.rst | 26 +++++++++++++------------- drivers/hwmon/Kconfig | 2 +- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/Documentation/hwmon/lm92.rst b/Documentation/hwmon/lm92.rst index c131b923ed36..d71cdb2af339 100644 --- a/Documentation/hwmon/lm92.rst +++ b/Documentation/hwmon/lm92.rst @@ -3,29 +3,29 @@ Kernel driver lm92 Supported chips: - * National Semiconductor LM92 + * National Semiconductor / Texas Instruments LM92 Prefix: 'lm92' Addresses scanned: I2C 0x48 - 0x4b - Datasheet: http://www.national.com/pf/LM/LM92.html + Datasheet: https://www.ti.com/lit/gpn/LM92 - * National Semiconductor LM76 + * National Semiconductor / Texas Instruments LM76 Prefix: 'lm92' - Addresses scanned: none, force parameter needed + Addresses scanned: none, must be instantiated explicitly - Datasheet: http://www.national.com/pf/LM/LM76.html + Datasheet: https://www.ti.com/lit/gpn/LM76 - * Maxim MAX6633/MAX6634/MAX6635 + * Maxim /Analog Devices MAX6633/MAX6634/MAX6635 Prefix: 'max6635' - Addresses scanned: none, force parameter needed + Addresses scanned: none, must be instantiated explicitly - Datasheet: http://www.maxim-ic.com/quick_view2.cfm/qv_pk/3074 + Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/max6633-max6635.pdf Authors: @@ -36,13 +36,13 @@ Authors: Description ----------- -This driver implements support for the National Semiconductor LM92 -temperature sensor. +This driver implements support for the National Semiconductor / Texas +Instruments LM92 temperature sensor. Each LM92 temperature sensor supports a single temperature sensor. There are alarms for high, low, and critical thresholds. There's also an hysteresis to control the thresholds for resetting alarms. -Support was added later for the LM76 and Maxim MAX6633/MAX6634/MAX6635, -which are mostly compatible. They have not all been tested, so you -may need to use the force parameter. +The driver also supports LM76 and Maxim MAX6633/MAX6634/MAX6635, which are +mostly compatible but do not have a vendor ID register and therefore must be +instantiated explicitly. diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 437334d10d3b..810c7d907235 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -1514,7 +1514,7 @@ config SENSORS_LM92 select REGMAP_I2C help If you say yes here you get support for National Semiconductor LM92 - and Maxim MAX6635 sensor chips. + and LM76 as well as Maxim MAX6633/6634/6635 sensor chips. This driver can also be built as a module. If so, the module will be called lm92. From f775f6d17035408be4a4b30c55d812fb7e6aab57 Mon Sep 17 00:00:00 2001 From: Javier Carrasco Date: Tue, 13 Aug 2024 00:59:53 +0200 Subject: [PATCH 50/96] hwmon: (chipcap2) Drop cc2_disable() in the probe and return dev_err_probe() There is no need to actively disable a regulator that has not been enabled by the driver, which makes the call to cc2_disable() in the probe function meaningless, because the probe function never enables the device's dedicated regulator. Once the call to cc2_disable() is dropped, the error paths can directly return dev_err_probe() in all cases. Signed-off-by: Javier Carrasco Message-ID: <20240813-chipcap2-probe-improvements-v2-1-e9a2932a8a00@gmail.com> Signed-off-by: Guenter Roeck --- drivers/hwmon/chipcap2.c | 33 +++++++++++---------------------- 1 file changed, 11 insertions(+), 22 deletions(-) diff --git a/drivers/hwmon/chipcap2.c b/drivers/hwmon/chipcap2.c index 6ccceae21f70..edf454474f11 100644 --- a/drivers/hwmon/chipcap2.c +++ b/drivers/hwmon/chipcap2.c @@ -740,37 +740,26 @@ static int cc2_probe(struct i2c_client *client) data->client = client; data->regulator = devm_regulator_get_exclusive(dev, "vdd"); - if (IS_ERR(data->regulator)) { - dev_err_probe(dev, PTR_ERR(data->regulator), - "Failed to get regulator\n"); - return PTR_ERR(data->regulator); - } + if (IS_ERR(data->regulator)) + return dev_err_probe(dev, PTR_ERR(data->regulator), + "Failed to get regulator\n"); ret = cc2_request_ready_irq(data, dev); - if (ret) { - dev_err_probe(dev, ret, "Failed to request ready irq\n"); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, "Failed to request ready irq\n"); ret = cc2_request_alarm_irqs(data, dev); - if (ret) { - dev_err_probe(dev, ret, "Failed to request alarm irqs\n"); - goto disable; - } + if (ret) + return dev_err_probe(dev, ret, "Failed to request alarm irqs\n"); data->hwmon = devm_hwmon_device_register_with_info(dev, client->name, data, &cc2_chip_info, NULL); - if (IS_ERR(data->hwmon)) { - dev_err_probe(dev, PTR_ERR(data->hwmon), - "Failed to register hwmon device\n"); - ret = PTR_ERR(data->hwmon); - } + if (IS_ERR(data->hwmon)) + return dev_err_probe(dev, PTR_ERR(data->hwmon), + "Failed to register hwmon device\n"); -disable: - cc2_disable(data); - - return ret; + return 0; } static void cc2_remove(struct i2c_client *client) From 1f432e4cf1dd3ecfec5ed80051b4611632a0fd51 Mon Sep 17 00:00:00 2001 From: Denis Pauk Date: Mon, 12 Aug 2024 18:26:38 +0300 Subject: [PATCH 51/96] hwmon: (nct6775) add G15CF to ASUS WMI monitoring list Boards G15CF has got a nct6775 chip, but by default there's no use of it because of resource conflict with WMI method. Add the board to the WMI monitoring list. Link: https://bugzilla.kernel.org/show_bug.cgi?id=204807 Signed-off-by: Denis Pauk Tested-by: Attila Message-ID: <20240812152652.1303-1-pauk.denis@gmail.com> Signed-off-by: Guenter Roeck --- drivers/hwmon/nct6775-platform.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/hwmon/nct6775-platform.c b/drivers/hwmon/nct6775-platform.c index 9aa4dcf4a6f3..096f1daa8f2b 100644 --- a/drivers/hwmon/nct6775-platform.c +++ b/drivers/hwmon/nct6775-platform.c @@ -1269,6 +1269,7 @@ static const char * const asus_msi_boards[] = { "EX-B760M-V5 D4", "EX-H510M-V3", "EX-H610M-V3 D4", + "G15CF", "PRIME A620M-A", "PRIME B560-PLUS", "PRIME B560-PLUS AC-HES", From 63be321e5a096746b396fc43c7d25135a61617c9 Mon Sep 17 00:00:00 2001 From: Maximilian Luz Date: Sun, 11 Aug 2024 02:14:41 +0200 Subject: [PATCH 52/96] hwmon: Add thermal sensor driver for Surface Aggregator Module Some of the newer Microsoft Surface devices (such as the Surface Book 3 and Pro 9) have thermal sensors connected via the Surface Aggregator Module (the embedded controller on those devices). Add a basic driver to read out the temperature values of those sensors. The EC can have up to 16 thermal sensors connected via a single sub-device, each providing temperature readings and a label string. Link: https://github.com/linux-surface/surface-aggregator-module/issues/59 Reviewed-by: Hans de Goede Co-developed-by: Ivor Wanders Signed-off-by: Ivor Wanders Signed-off-by: Maximilian Luz Message-ID: <20240811001503.753728-1-luzmaximilian@gmail.com> Signed-off-by: Guenter Roeck --- MAINTAINERS | 6 + drivers/hwmon/Kconfig | 11 ++ drivers/hwmon/Makefile | 1 + drivers/hwmon/surface_temp.c | 235 +++++++++++++++++++++++++++++++++++ 4 files changed, 253 insertions(+) create mode 100644 drivers/hwmon/surface_temp.c diff --git a/MAINTAINERS b/MAINTAINERS index 42decde38320..39c61db0169c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -15200,6 +15200,12 @@ S: Maintained F: Documentation/hwmon/surface_fan.rst F: drivers/hwmon/surface_fan.c +MICROSOFT SURFACE SENSOR THERMAL DRIVER +M: Maximilian Luz +L: linux-hwmon@vger.kernel.org +S: Maintained +F: drivers/hwmon/surface_temp.c + MICROSOFT SURFACE GPE LID SUPPORT DRIVER M: Maximilian Luz L: platform-driver-x86@vger.kernel.org diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 810c7d907235..1db349ecb1a4 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -2082,6 +2082,17 @@ config SENSORS_SURFACE_FAN Select M or Y here, if you want to be able to read the fan's speed. +config SENSORS_SURFACE_TEMP + tristate "Microsoft Surface Thermal Sensor Driver" + depends on SURFACE_AGGREGATOR + depends on SURFACE_AGGREGATOR_BUS + help + Driver for monitoring thermal sensors connected via the Surface + Aggregator Module (embedded controller) on Microsoft Surface devices. + + This driver can also be built as a module. If so, the module + will be called surface_temp. + config SENSORS_ADC128D818 tristate "Texas Instruments ADC128D818" depends on I2C diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index b1c7056c37db..3ce8d6a9202e 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -209,6 +209,7 @@ obj-$(CONFIG_SENSORS_SPARX5) += sparx5-temp.o obj-$(CONFIG_SENSORS_SPD5118) += spd5118.o obj-$(CONFIG_SENSORS_STTS751) += stts751.o obj-$(CONFIG_SENSORS_SURFACE_FAN)+= surface_fan.o +obj-$(CONFIG_SENSORS_SURFACE_TEMP)+= surface_temp.o obj-$(CONFIG_SENSORS_SY7636A) += sy7636a-hwmon.o obj-$(CONFIG_SENSORS_AMC6821) += amc6821.o obj-$(CONFIG_SENSORS_TC74) += tc74.o diff --git a/drivers/hwmon/surface_temp.c b/drivers/hwmon/surface_temp.c new file mode 100644 index 000000000000..cd21f331f157 --- /dev/null +++ b/drivers/hwmon/surface_temp.c @@ -0,0 +1,235 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Thermal sensor subsystem driver for Surface System Aggregator Module (SSAM). + * + * Copyright (C) 2022-2023 Maximilian Luz + */ + +#include +#include +#include +#include +#include + +#include +#include + +/* -- SAM interface. -------------------------------------------------------- */ + +/* + * Available sensors are indicated by a 16-bit bitfield, where a 1 marks the + * presence of a sensor. So we have at most 16 possible sensors/channels. + */ +#define SSAM_TMP_SENSOR_MAX_COUNT 16 + +/* + * All names observed so far are 6 characters long, but there's only + * zeros after the name, so perhaps they can be longer. This number reflects + * the maximum zero-padded space observed in the returned buffer. + */ +#define SSAM_TMP_SENSOR_NAME_LENGTH 18 + +struct ssam_tmp_get_name_rsp { + __le16 unknown1; + char unknown2; + char name[SSAM_TMP_SENSOR_NAME_LENGTH]; +} __packed; + +static_assert(sizeof(struct ssam_tmp_get_name_rsp) == 21); + +SSAM_DEFINE_SYNC_REQUEST_CL_R(__ssam_tmp_get_available_sensors, __le16, { + .target_category = SSAM_SSH_TC_TMP, + .command_id = 0x04, +}); + +SSAM_DEFINE_SYNC_REQUEST_MD_R(__ssam_tmp_get_temperature, __le16, { + .target_category = SSAM_SSH_TC_TMP, + .command_id = 0x01, +}); + +SSAM_DEFINE_SYNC_REQUEST_MD_R(__ssam_tmp_get_name, struct ssam_tmp_get_name_rsp, { + .target_category = SSAM_SSH_TC_TMP, + .command_id = 0x0e, +}); + +static int ssam_tmp_get_available_sensors(struct ssam_device *sdev, s16 *sensors) +{ + __le16 sensors_le; + int status; + + status = __ssam_tmp_get_available_sensors(sdev, &sensors_le); + if (status) + return status; + + *sensors = le16_to_cpu(sensors_le); + return 0; +} + +static int ssam_tmp_get_temperature(struct ssam_device *sdev, u8 iid, long *temperature) +{ + __le16 temp_le; + int status; + + status = __ssam_tmp_get_temperature(sdev->ctrl, sdev->uid.target, iid, &temp_le); + if (status) + return status; + + /* Convert 1/10 °K to 1/1000 °C */ + *temperature = (le16_to_cpu(temp_le) - 2731) * 100L; + return 0; +} + +static int ssam_tmp_get_name(struct ssam_device *sdev, u8 iid, char *buf, size_t buf_len) +{ + struct ssam_tmp_get_name_rsp name_rsp; + int status; + + status = __ssam_tmp_get_name(sdev->ctrl, sdev->uid.target, iid, &name_rsp); + if (status) + return status; + + /* + * This should not fail unless the name in the returned struct is not + * null-terminated or someone changed something in the struct + * definitions above, since our buffer and struct have the same + * capacity by design. So if this fails, log an error message. Since + * the more likely cause is that the returned string isn't + * null-terminated, we might have received garbage (as opposed to just + * an incomplete string), so also fail the function. + */ + status = strscpy(buf, name_rsp.name, buf_len); + if (status < 0) { + dev_err(&sdev->dev, "received non-null-terminated sensor name string\n"); + return status; + } + + return 0; +} + +/* -- Driver.---------------------------------------------------------------- */ + +struct ssam_temp { + struct ssam_device *sdev; + s16 sensors; + char names[SSAM_TMP_SENSOR_MAX_COUNT][SSAM_TMP_SENSOR_NAME_LENGTH]; +}; + +static umode_t ssam_temp_hwmon_is_visible(const void *data, + enum hwmon_sensor_types type, + u32 attr, int channel) +{ + const struct ssam_temp *ssam_temp = data; + + if (!(ssam_temp->sensors & BIT(channel))) + return 0; + + return 0444; +} + +static int ssam_temp_hwmon_read(struct device *dev, + enum hwmon_sensor_types type, + u32 attr, int channel, long *value) +{ + const struct ssam_temp *ssam_temp = dev_get_drvdata(dev); + + return ssam_tmp_get_temperature(ssam_temp->sdev, channel + 1, value); +} + +static int ssam_temp_hwmon_read_string(struct device *dev, + enum hwmon_sensor_types type, + u32 attr, int channel, const char **str) +{ + const struct ssam_temp *ssam_temp = dev_get_drvdata(dev); + + *str = ssam_temp->names[channel]; + return 0; +} + +static const struct hwmon_channel_info * const ssam_temp_hwmon_info[] = { + HWMON_CHANNEL_INFO(chip, + HWMON_C_REGISTER_TZ), + HWMON_CHANNEL_INFO(temp, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL), + NULL +}; + +static const struct hwmon_ops ssam_temp_hwmon_ops = { + .is_visible = ssam_temp_hwmon_is_visible, + .read = ssam_temp_hwmon_read, + .read_string = ssam_temp_hwmon_read_string, +}; + +static const struct hwmon_chip_info ssam_temp_hwmon_chip_info = { + .ops = &ssam_temp_hwmon_ops, + .info = ssam_temp_hwmon_info, +}; + +static int ssam_temp_probe(struct ssam_device *sdev) +{ + struct ssam_temp *ssam_temp; + struct device *hwmon_dev; + s16 sensors; + int channel; + int status; + + status = ssam_tmp_get_available_sensors(sdev, &sensors); + if (status) + return status; + + ssam_temp = devm_kzalloc(&sdev->dev, sizeof(*ssam_temp), GFP_KERNEL); + if (!ssam_temp) + return -ENOMEM; + + ssam_temp->sdev = sdev; + ssam_temp->sensors = sensors; + + /* Retrieve the name for each available sensor. */ + for (channel = 0; channel < SSAM_TMP_SENSOR_MAX_COUNT; channel++) { + if (!(sensors & BIT(channel))) + continue; + + status = ssam_tmp_get_name(sdev, channel + 1, ssam_temp->names[channel], + SSAM_TMP_SENSOR_NAME_LENGTH); + if (status) + return status; + } + + hwmon_dev = devm_hwmon_device_register_with_info(&sdev->dev, "surface_thermal", ssam_temp, + &ssam_temp_hwmon_chip_info, NULL); + return PTR_ERR_OR_ZERO(hwmon_dev); +} + +static const struct ssam_device_id ssam_temp_match[] = { + { SSAM_SDEV(TMP, SAM, 0x00, 0x02) }, + { }, +}; +MODULE_DEVICE_TABLE(ssam, ssam_temp_match); + +static struct ssam_device_driver ssam_temp = { + .probe = ssam_temp_probe, + .match_table = ssam_temp_match, + .driver = { + .name = "surface_temp", + .probe_type = PROBE_PREFER_ASYNCHRONOUS, + }, +}; +module_ssam_device_driver(ssam_temp); + +MODULE_AUTHOR("Maximilian Luz "); +MODULE_DESCRIPTION("Thermal sensor subsystem driver for Surface System Aggregator Module"); +MODULE_LICENSE("GPL"); From fa8df3cbd3c0a1b62b4127d779fbf51eb17e22f1 Mon Sep 17 00:00:00 2001 From: Liao Chen Date: Wed, 14 Aug 2024 02:45:55 +0000 Subject: [PATCH 53/96] hwmon: (gsc-hwmon) fix module autoloading Add MODULE_DEVICE_TABLE(), so modules could be properly autoloaded based on the alias from of_device_id table. Signed-off-by: Liao Chen Message-ID: <20240814024555.3875387-1-liaochen4@huawei.com> Signed-off-by: Guenter Roeck --- drivers/hwmon/gsc-hwmon.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/hwmon/gsc-hwmon.c b/drivers/hwmon/gsc-hwmon.c index cb2f01dc4326..4514f3ed90cc 100644 --- a/drivers/hwmon/gsc-hwmon.c +++ b/drivers/hwmon/gsc-hwmon.c @@ -400,6 +400,7 @@ static const struct of_device_id gsc_hwmon_of_match[] = { { .compatible = "gw,gsc-adc", }, {} }; +MODULE_DEVICE_TABLE(of, gsc_hwmon_of_match); static struct platform_driver gsc_hwmon_driver = { .driver = { From 720c741c22d18b03eb017683f001a645a23a2e65 Mon Sep 17 00:00:00 2001 From: Yue Haibing Date: Fri, 16 Aug 2024 17:57:40 +0800 Subject: [PATCH 54/96] hwmon: (sch5627) Remove unused declaration sch56xx_watchdog_unregister() Commit 2be5f0d75325 ("hwmon: (sch56xx) Use devres functions for watchdog") removed the implementation but leave declaration. Signed-off-by: Yue Haibing Message-ID: <20240816095740.877729-1-yuehaibing@huawei.com> Signed-off-by: Guenter Roeck --- drivers/hwmon/sch56xx-common.h | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/hwmon/sch56xx-common.h b/drivers/hwmon/sch56xx-common.h index 7479a549a026..601987c6b4cd 100644 --- a/drivers/hwmon/sch56xx-common.h +++ b/drivers/hwmon/sch56xx-common.h @@ -22,4 +22,3 @@ int sch56xx_read_virtual_reg12(u16 addr, u16 msb_reg, u16 lsn_reg, void sch56xx_watchdog_register(struct device *parent, u16 addr, u32 revision, struct mutex *io_lock, int check_enabled); -void sch56xx_watchdog_unregister(struct sch56xx_watchdog_data *data); From b6964d66a07a9003868e428a956949e17ab44d7e Mon Sep 17 00:00:00 2001 From: Yuntao Liu Date: Thu, 15 Aug 2024 08:30:21 +0000 Subject: [PATCH 55/96] hwmon: (ntc_thermistor) fix module autoloading Add MODULE_DEVICE_TABLE(), so modules could be properly autoloaded based on the alias from of_device_id table. Fixes: 9e8269de100d ("hwmon: (ntc_thermistor) Add DT with IIO support to NTC thermistor driver") Signed-off-by: Yuntao Liu Message-ID: <20240815083021.756134-1-liuyuntao12@huawei.com> Signed-off-by: Guenter Roeck --- drivers/hwmon/ntc_thermistor.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/hwmon/ntc_thermistor.c b/drivers/hwmon/ntc_thermistor.c index ef75b63f5894..b5352900463f 100644 --- a/drivers/hwmon/ntc_thermistor.c +++ b/drivers/hwmon/ntc_thermistor.c @@ -62,6 +62,7 @@ static const struct platform_device_id ntc_thermistor_id[] = { [NTC_SSG1404001221] = { "ssg1404_001221", TYPE_NCPXXWB473 }, [NTC_LAST] = { }, }; +MODULE_DEVICE_TABLE(platform, ntc_thermistor_id); /* * A compensation table should be sorted by the values of .ohm From 0830d3bbdabc4fb6ee0318142c2e81398e4815d9 Mon Sep 17 00:00:00 2001 From: "Rob Herring (Arm)" Date: Mon, 26 Aug 2024 14:18:11 -0500 Subject: [PATCH 56/96] hwmon: (stts751) Add "st" vendor prefix to "stts751" compatible string The documented compatible string is "st,stts751", not "stts751". Even if "stts751" was in use, there's no need to list "stts751" in the DT match table. The I2C core will strip any vendor prefix and match against the i2c_device_id table which has an "stts751" entry. Signed-off-by: Rob Herring (Arm) Message-ID: <20240826191811.1416011-1-robh@kernel.org> Signed-off-by: Guenter Roeck --- drivers/hwmon/stts751.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hwmon/stts751.c b/drivers/hwmon/stts751.c index e7632081a1d1..f9e8b2869164 100644 --- a/drivers/hwmon/stts751.c +++ b/drivers/hwmon/stts751.c @@ -77,7 +77,7 @@ static const struct i2c_device_id stts751_id[] = { }; static const struct of_device_id __maybe_unused stts751_of_match[] = { - { .compatible = "stts751" }, + { .compatible = "st,stts751" }, { }, }; MODULE_DEVICE_TABLE(of, stts751_of_match); From b82b38a49926b7d8e17d79db7959586313a99582 Mon Sep 17 00:00:00 2001 From: "Derek J. Clark" Date: Thu, 22 Aug 2024 11:35:25 -0700 Subject: [PATCH 57/96] hwmon: (oxp-sensors) Add support for multiple new devices. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add support for the OrangePi NEO-01. It uses different registers for PWM manual mode, set PWM, and read fan speed than previous devices. Valid PWM input and duty cycle is 1-244, we scale this from 1-255 to maintain compatibility with the existing interface. Add OneXPlayer 2 series, OneXFly, and X1 series models. The 2/X1 series use new registers for turbo button takeover and read fan speed. X1 has an Intel variant so change the CPU detection at init to only check for the affected devices. While at it, adjust formatting of some constants and reorder all cases alphabetically for consistency. Rename OXP_OLD constants to OXP_MINI for disambiguation. Update code comments for clarity. Add support for AYANEO models 2S, AIR 1S, Flip series, GEEK 1S, and KUN. Signed-off-by: Derek J. Clark Tested-by: Kevin Greenberg Tested-by: Joshua Tam Tested-by: Parth Menon Tested-by: Philip Müller Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202408160329.TLNbIwRC-lkp@intel.com/ Message-ID: <20240822183525.27289-2-derekjohn.clark@gmail.com> Signed-off-by: Guenter Roeck --- Documentation/hwmon/oxp-sensors.rst | 54 +++-- drivers/hwmon/oxp-sensors.c | 299 +++++++++++++++++++++++----- 2 files changed, 290 insertions(+), 63 deletions(-) diff --git a/Documentation/hwmon/oxp-sensors.rst b/Documentation/hwmon/oxp-sensors.rst index 50618f064379..581c4dafbfa1 100644 --- a/Documentation/hwmon/oxp-sensors.rst +++ b/Documentation/hwmon/oxp-sensors.rst @@ -10,41 +10,59 @@ Authors: Description: ------------ -Handheld devices from One Netbook and Aya Neo provide fan readings and fan -control through their embedded controllers. +Handheld devices from OneNetbook, AOKZOE, AYANEO, And OrangePi provide fan +readings and fan control through their embedded controllers. -Currently only supports AMD boards from One X Player, AOK ZOE, and some Aya -Neo devices. One X Player Intel boards could be supported if we could figure -out the EC registers and values to write to since the EC layout and model is -different. Aya Neo devices preceding the AIR may not be supportable as the EC -model is different and do not appear to have manual control capabilities. +Currently supports OneXPlayer devices, AOKZOE, AYANEO, and OrangePi +handheld devices. AYANEO devices preceding the AIR and OneXPlayer devices +preceding the Mini A07 are not supportable as the EC model is different +and do not have manual control capabilities. -Some models have a toggle for changing the behaviour of the "Turbo/Silent" -button of the device. It will change the key event that it triggers with -a flip of the `tt_toggle` attribute. See below for boards that support this -function. +Some OneXPlayer and AOKZOE models have a toggle for changing the behaviour +of the "Turbo/Silent" button of the device. It will change the key event +that it triggers with a flip of the `tt_toggle` attribute. See below for +boards that support this function. Supported devices ----------------- Currently the driver supports the following handhelds: - - AOK ZOE A1 - - AOK ZOE A1 PRO - - Aya Neo 2 - - Aya Neo AIR - - Aya Neo AIR Plus (Mendocino) - - Aya Neo AIR Pro - - Aya Neo Geek + - AOKZOE A1 + - AOKZOE A1 PRO + - AYANEO 2 + - AYANEO 2S + - AYANEO AIR + - AYANEO AIR 1S + - AYANEO AIR Plus (Mendocino) + - AYANEO AIR Pro + - AYANEO Flip DS + - AYANEO Flip KB + - AYANEO Geek + - AYANEO Geek 1S + - AYANEO KUN + - OneXPlayer 2 + - OneXPlayer 2 Pro - OneXPlayer AMD - OneXPlayer mini AMD - OneXPlayer mini AMD PRO + - OneXPlayer OneXFly + - OneXPlayer X1 A + - OneXPlayer X1 i + - OneXPlayer X1 mini + - OrangePi NEO-01 "Turbo/Silent" button behaviour toggle is only supported on: - AOK ZOE A1 - AOK ZOE A1 PRO + - OneXPlayer 2 + - OneXPlayer 2 Pro - OneXPlayer mini AMD (only with updated alpha BIOS) - OneXPlayer mini AMD PRO + - OneXPlayer OneXFly + - OneXPlayer X1 A + - OneXPlayer X1 i + - OneXPlayer X1 mini Sysfs entries ------------- diff --git a/drivers/hwmon/oxp-sensors.c b/drivers/hwmon/oxp-sensors.c index 8d3b0f86cc57..b6d06370469d 100644 --- a/drivers/hwmon/oxp-sensors.c +++ b/drivers/hwmon/oxp-sensors.c @@ -1,18 +1,21 @@ // SPDX-License-Identifier: GPL-2.0+ /* - * Platform driver for OneXPlayer, AOK ZOE, and Aya Neo Handhelds that expose - * fan reading and control via hwmon sysfs. + * Platform driver for OneXPlayer, AOKZOE, AYANEO, and OrangePi Handhelds + * that expose fan reading and control via hwmon sysfs. * * Old OXP boards have the same DMI strings and they are told apart by - * the boot cpu vendor (Intel/AMD). Currently only AMD boards are - * supported but the code is made to be simple to add other handheld - * boards in the future. + * the boot cpu vendor (Intel/AMD). Of these older models only AMD is + * supported. + * * Fan control is provided via pwm interface in the range [0-255]. * Old AMD boards use [0-100] as range in the EC, the written value is * scaled to accommodate for that. Newer boards like the mini PRO and - * AOK ZOE are not scaled but have the same EC layout. + * AOKZOE are not scaled but have the same EC layout. Newer models + * like the 2 and X1 are [0-184] and are scaled to 0-255. OrangePi + * are [1-244] and scaled to 0-255. * * Copyright (C) 2022 Joaquín I. Aramendía + * Copyright (C) 2024 Derek J. Clark */ #include @@ -43,32 +46,48 @@ enum oxp_board { aok_zoe_a1 = 1, aya_neo_2, aya_neo_air, + aya_neo_air_1s, aya_neo_air_plus_mendo, aya_neo_air_pro, + aya_neo_flip, aya_neo_geek, + aya_neo_kun, + orange_pi_neo, + oxp_2, + oxp_fly, oxp_mini_amd, oxp_mini_amd_a07, oxp_mini_amd_pro, + oxp_x1, }; static enum oxp_board board; /* Fan reading and PWM */ -#define OXP_SENSOR_FAN_REG 0x76 /* Fan reading is 2 registers long */ -#define OXP_SENSOR_PWM_ENABLE_REG 0x4A /* PWM enable is 1 register long */ -#define OXP_SENSOR_PWM_REG 0x4B /* PWM reading is 1 register long */ +#define OXP_SENSOR_FAN_REG 0x76 /* Fan reading is 2 registers long */ +#define OXP_2_SENSOR_FAN_REG 0x58 /* Fan reading is 2 registers long */ +#define OXP_SENSOR_PWM_ENABLE_REG 0x4A /* PWM enable is 1 register long */ +#define OXP_SENSOR_PWM_REG 0x4B /* PWM reading is 1 register long */ +#define PWM_MODE_AUTO 0x00 +#define PWM_MODE_MANUAL 0x01 + +/* OrangePi fan reading and PWM */ +#define ORANGEPI_SENSOR_FAN_REG 0x78 /* Fan reading is 2 registers long */ +#define ORANGEPI_SENSOR_PWM_ENABLE_REG 0x40 /* PWM enable is 1 register long */ +#define ORANGEPI_SENSOR_PWM_REG 0x38 /* PWM reading is 1 register long */ /* Turbo button takeover function - * Older boards have different values and EC registers + * Different boards have different values and EC registers * for the same function */ -#define OXP_OLD_TURBO_SWITCH_REG 0x1E -#define OXP_OLD_TURBO_TAKE_VAL 0x01 -#define OXP_OLD_TURBO_RETURN_VAL 0x00 +#define OXP_TURBO_SWITCH_REG 0xF1 /* Mini Pro, OneXFly, AOKZOE */ +#define OXP_2_TURBO_SWITCH_REG 0xEB /* OXP2 and X1 */ +#define OXP_MINI_TURBO_SWITCH_REG 0x1E /* Mini AO7 */ -#define OXP_TURBO_SWITCH_REG 0xF1 -#define OXP_TURBO_TAKE_VAL 0x40 -#define OXP_TURBO_RETURN_VAL 0x00 +#define OXP_MINI_TURBO_TAKE_VAL 0x01 /* Mini AO7 */ +#define OXP_TURBO_TAKE_VAL 0x40 /* All other models */ + +#define OXP_TURBO_RETURN_VAL 0x00 /* Common return val */ static const struct dmi_system_id dmi_table[] = { { @@ -88,7 +107,7 @@ static const struct dmi_system_id dmi_table[] = { { .matches = { DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"), - DMI_EXACT_MATCH(DMI_BOARD_NAME, "AYANEO 2"), + DMI_MATCH(DMI_BOARD_NAME, "AYANEO 2"), }, .driver_data = (void *)aya_neo_2, }, @@ -99,6 +118,13 @@ static const struct dmi_system_id dmi_table[] = { }, .driver_data = (void *)aya_neo_air, }, + { + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"), + DMI_EXACT_MATCH(DMI_BOARD_NAME, "AIR 1S"), + }, + .driver_data = (void *)aya_neo_air_1s, + }, { .matches = { DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"), @@ -116,10 +142,31 @@ static const struct dmi_system_id dmi_table[] = { { .matches = { DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"), - DMI_EXACT_MATCH(DMI_BOARD_NAME, "GEEK"), + DMI_MATCH(DMI_BOARD_NAME, "FLIP"), + }, + .driver_data = (void *)aya_neo_flip, + }, + { + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"), + DMI_MATCH(DMI_BOARD_NAME, "GEEK"), }, .driver_data = (void *)aya_neo_geek, }, + { + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"), + DMI_EXACT_MATCH(DMI_BOARD_NAME, "KUN"), + }, + .driver_data = (void *)aya_neo_kun, + }, + { + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "OrangePi"), + DMI_EXACT_MATCH(DMI_BOARD_NAME, "NEO-01"), + }, + .driver_data = (void *)orange_pi_neo, + }, { .matches = { DMI_MATCH(DMI_BOARD_VENDOR, "ONE-NETBOOK"), @@ -127,6 +174,20 @@ static const struct dmi_system_id dmi_table[] = { }, .driver_data = (void *)oxp_mini_amd, }, + { + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "ONE-NETBOOK"), + DMI_MATCH(DMI_BOARD_NAME, "ONEXPLAYER 2"), + }, + .driver_data = (void *)oxp_2, + }, + { + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "ONE-NETBOOK"), + DMI_EXACT_MATCH(DMI_BOARD_NAME, "ONEXPLAYER F1"), + }, + .driver_data = (void *)oxp_fly, + }, { .matches = { DMI_MATCH(DMI_BOARD_VENDOR, "ONE-NETBOOK"), @@ -141,6 +202,13 @@ static const struct dmi_system_id dmi_table[] = { }, .driver_data = (void *)oxp_mini_amd_pro, }, + { + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "ONE-NETBOOK"), + DMI_MATCH(DMI_BOARD_NAME, "ONEXPLAYER X1"), + }, + .driver_data = (void *)oxp_x1, + }, {}, }; @@ -192,14 +260,20 @@ static int tt_toggle_enable(void) switch (board) { case oxp_mini_amd_a07: - reg = OXP_OLD_TURBO_SWITCH_REG; - val = OXP_OLD_TURBO_TAKE_VAL; + reg = OXP_MINI_TURBO_SWITCH_REG; + val = OXP_MINI_TURBO_TAKE_VAL; break; - case oxp_mini_amd_pro: case aok_zoe_a1: + case oxp_fly: + case oxp_mini_amd_pro: reg = OXP_TURBO_SWITCH_REG; val = OXP_TURBO_TAKE_VAL; break; + case oxp_2: + case oxp_x1: + reg = OXP_2_TURBO_SWITCH_REG; + val = OXP_TURBO_TAKE_VAL; + break; default: return -EINVAL; } @@ -213,14 +287,20 @@ static int tt_toggle_disable(void) switch (board) { case oxp_mini_amd_a07: - reg = OXP_OLD_TURBO_SWITCH_REG; - val = OXP_OLD_TURBO_RETURN_VAL; + reg = OXP_MINI_TURBO_SWITCH_REG; + val = OXP_TURBO_RETURN_VAL; break; - case oxp_mini_amd_pro: case aok_zoe_a1: + case oxp_fly: + case oxp_mini_amd_pro: reg = OXP_TURBO_SWITCH_REG; val = OXP_TURBO_RETURN_VAL; break; + case oxp_2: + case oxp_x1: + reg = OXP_2_TURBO_SWITCH_REG; + val = OXP_TURBO_RETURN_VAL; + break; default: return -EINVAL; } @@ -233,8 +313,11 @@ static umode_t tt_toggle_is_visible(struct kobject *kobj, { switch (board) { case aok_zoe_a1: + case oxp_2: + case oxp_fly: case oxp_mini_amd_a07: case oxp_mini_amd_pro: + case oxp_x1: return attr->mode; default: break; @@ -273,12 +356,17 @@ static ssize_t tt_toggle_show(struct device *dev, switch (board) { case oxp_mini_amd_a07: - reg = OXP_OLD_TURBO_SWITCH_REG; + reg = OXP_MINI_TURBO_SWITCH_REG; break; - case oxp_mini_amd_pro: case aok_zoe_a1: + case oxp_fly: + case oxp_mini_amd_pro: reg = OXP_TURBO_SWITCH_REG; break; + case oxp_2: + case oxp_x1: + reg = OXP_2_TURBO_SWITCH_REG; + break; default: return -EINVAL; } @@ -295,12 +383,53 @@ static DEVICE_ATTR_RW(tt_toggle); /* PWM enable/disable functions */ static int oxp_pwm_enable(void) { - return write_to_ec(OXP_SENSOR_PWM_ENABLE_REG, 0x01); + switch (board) { + case orange_pi_neo: + return write_to_ec(ORANGEPI_SENSOR_PWM_ENABLE_REG, PWM_MODE_MANUAL); + case aok_zoe_a1: + case aya_neo_2: + case aya_neo_air: + case aya_neo_air_plus_mendo: + case aya_neo_air_pro: + case aya_neo_flip: + case aya_neo_geek: + case aya_neo_kun: + case oxp_2: + case oxp_fly: + case oxp_mini_amd: + case oxp_mini_amd_a07: + case oxp_mini_amd_pro: + case oxp_x1: + return write_to_ec(OXP_SENSOR_PWM_ENABLE_REG, PWM_MODE_MANUAL); + default: + return -EINVAL; + } } static int oxp_pwm_disable(void) { - return write_to_ec(OXP_SENSOR_PWM_ENABLE_REG, 0x00); + switch (board) { + case orange_pi_neo: + return write_to_ec(ORANGEPI_SENSOR_PWM_ENABLE_REG, PWM_MODE_AUTO); + case aok_zoe_a1: + case aya_neo_2: + case aya_neo_air: + case aya_neo_air_1s: + case aya_neo_air_plus_mendo: + case aya_neo_air_pro: + case aya_neo_flip: + case aya_neo_geek: + case aya_neo_kun: + case oxp_2: + case oxp_fly: + case oxp_mini_amd: + case oxp_mini_amd_a07: + case oxp_mini_amd_pro: + case oxp_x1: + return write_to_ec(OXP_SENSOR_PWM_ENABLE_REG, PWM_MODE_AUTO); + default: + return -EINVAL; + } } /* Callbacks for hwmon interface */ @@ -326,7 +455,29 @@ static int oxp_platform_read(struct device *dev, enum hwmon_sensor_types type, case hwmon_fan: switch (attr) { case hwmon_fan_input: - return read_from_ec(OXP_SENSOR_FAN_REG, 2, val); + switch (board) { + case orange_pi_neo: + return read_from_ec(ORANGEPI_SENSOR_FAN_REG, 2, val); + case oxp_2: + case oxp_x1: + return read_from_ec(OXP_2_SENSOR_FAN_REG, 2, val); + case aok_zoe_a1: + case aya_neo_2: + case aya_neo_air: + case aya_neo_air_1s: + case aya_neo_air_plus_mendo: + case aya_neo_air_pro: + case aya_neo_flip: + case aya_neo_geek: + case aya_neo_kun: + case oxp_fly: + case oxp_mini_amd: + case oxp_mini_amd_a07: + case oxp_mini_amd_pro: + return read_from_ec(OXP_SENSOR_FAN_REG, 2, val); + default: + break; + } default: break; } @@ -334,31 +485,74 @@ static int oxp_platform_read(struct device *dev, enum hwmon_sensor_types type, case hwmon_pwm: switch (attr) { case hwmon_pwm_input: - ret = read_from_ec(OXP_SENSOR_PWM_REG, 1, val); - if (ret) - return ret; switch (board) { + case orange_pi_neo: + ret = read_from_ec(ORANGEPI_SENSOR_PWM_REG, 1, val); + if (ret) + return ret; + /* scale from range [1-244] */ + *val = ((*val - 1) * 254 / 243) + 1; + break; + case oxp_2: + case oxp_x1: + ret = read_from_ec(OXP_SENSOR_PWM_REG, 1, val); + if (ret) + return ret; + /* scale from range [0-184] */ + *val = (*val * 255) / 184; + break; case aya_neo_2: case aya_neo_air: + case aya_neo_air_1s: case aya_neo_air_plus_mendo: case aya_neo_air_pro: + case aya_neo_flip: case aya_neo_geek: + case aya_neo_kun: case oxp_mini_amd: case oxp_mini_amd_a07: + ret = read_from_ec(OXP_SENSOR_PWM_REG, 1, val); + if (ret) + return ret; + /* scale from range [0-100] */ *val = (*val * 255) / 100; break; - case oxp_mini_amd_pro: case aok_zoe_a1: + case oxp_fly: + case oxp_mini_amd_pro: default: + ret = read_from_ec(OXP_SENSOR_PWM_REG, 1, val); + if (ret) + return ret; break; } return 0; case hwmon_pwm_enable: - return read_from_ec(OXP_SENSOR_PWM_ENABLE_REG, 1, val); + switch (board) { + case orange_pi_neo: + return read_from_ec(ORANGEPI_SENSOR_PWM_ENABLE_REG, 1, val); + case aok_zoe_a1: + case aya_neo_2: + case aya_neo_air: + case aya_neo_air_1s: + case aya_neo_air_plus_mendo: + case aya_neo_air_pro: + case aya_neo_flip: + case aya_neo_geek: + case aya_neo_kun: + case oxp_2: + case oxp_fly: + case oxp_mini_amd: + case oxp_mini_amd_a07: + case oxp_mini_amd_pro: + case oxp_x1: + return read_from_ec(OXP_SENSOR_PWM_ENABLE_REG, 1, val); + default: + break; + } default: break; } - break; default: break; } @@ -381,21 +575,35 @@ static int oxp_platform_write(struct device *dev, enum hwmon_sensor_types type, if (val < 0 || val > 255) return -EINVAL; switch (board) { + case orange_pi_neo: + /* scale to range [1-244] */ + val = ((val - 1) * 243 / 254) + 1; + return write_to_ec(ORANGEPI_SENSOR_PWM_REG, val); + case oxp_2: + case oxp_x1: + /* scale to range [0-184] */ + val = (val * 184) / 255; + return write_to_ec(OXP_SENSOR_PWM_REG, val); case aya_neo_2: case aya_neo_air: + case aya_neo_air_1s: case aya_neo_air_plus_mendo: case aya_neo_air_pro: + case aya_neo_flip: case aya_neo_geek: + case aya_neo_kun: case oxp_mini_amd: case oxp_mini_amd_a07: + /* scale to range [0-100] */ val = (val * 100) / 255; - break; + return write_to_ec(OXP_SENSOR_PWM_REG, val); case aok_zoe_a1: + case oxp_fly: case oxp_mini_amd_pro: + return write_to_ec(OXP_SENSOR_PWM_REG, val); default: break; } - return write_to_ec(OXP_SENSOR_PWM_REG, val); default: break; } @@ -467,19 +675,20 @@ static int __init oxp_platform_init(void) { const struct dmi_system_id *dmi_entry; - /* - * Have to check for AMD processor here because DMI strings are the - * same between Intel and AMD boards, the only way to tell them apart - * is the CPU. - * Intel boards seem to have different EC registers and values to - * read/write. - */ dmi_entry = dmi_first_match(dmi_table); - if (!dmi_entry || boot_cpu_data.x86_vendor != X86_VENDOR_AMD) + if (!dmi_entry) return -ENODEV; board = (enum oxp_board)(unsigned long)dmi_entry->driver_data; + /* + * Have to check for AMD processor here because DMI strings are the same + * between Intel and AMD boards on older OneXPlayer devices, the only way + * to tell them apart is the CPU. Old Intel boards have an unsupported EC. + */ + if (board == oxp_mini_amd && boot_cpu_data.x86_vendor != X86_VENDOR_AMD) + return -ENODEV; + oxp_platform_device = platform_create_bundle(&oxp_platform_driver, oxp_platform_probe, NULL, 0, NULL, 0); From 0050c167016b599c9a3fe79a8f021898a6211d83 Mon Sep 17 00:00:00 2001 From: Chanh Nguyen Date: Thu, 22 Aug 2024 08:48:08 +0000 Subject: [PATCH 58/96] dt-bindings: hwmon: Add maxim max31790 Add device tree bindings and an example for max31790 device. Signed-off-by: Chanh Nguyen Reviewed-by: Krzysztof Kozlowski Message-ID: <20240822084808.299884-1-chanh@os.amperecomputing.com> Signed-off-by: Guenter Roeck --- .../bindings/hwmon/maxim,max31790.yaml | 70 +++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 Documentation/devicetree/bindings/hwmon/maxim,max31790.yaml diff --git a/Documentation/devicetree/bindings/hwmon/maxim,max31790.yaml b/Documentation/devicetree/bindings/hwmon/maxim,max31790.yaml new file mode 100644 index 000000000000..b1ff496f87f9 --- /dev/null +++ b/Documentation/devicetree/bindings/hwmon/maxim,max31790.yaml @@ -0,0 +1,70 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/hwmon/maxim,max31790.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: The Maxim MAX31790 Fan Controller + +maintainers: + - Guenter Roeck + - Chanh Nguyen + +description: > + The MAX31790 controls the speeds of up to six fans using six + independent PWM outputs. The desired fan speeds (or PWM duty cycles) + are written through the I2C interface. + + Datasheets: + https://datasheets.maximintegrated.com/en/ds/MAX31790.pdf + +properties: + compatible: + const: maxim,max31790 + + reg: + maxItems: 1 + + clocks: + maxItems: 1 + + resets: + maxItems: 1 + + "#pwm-cells": + const: 1 + +patternProperties: + "^fan-[0-9]+$": + $ref: fan-common.yaml# + unevaluatedProperties: false + +required: + - compatible + - reg + +additionalProperties: false + +examples: + - | + i2c { + #address-cells = <1>; + #size-cells = <0>; + + pwm_provider: fan-controller@20 { + compatible = "maxim,max31790"; + reg = <0x20>; + clocks = <&sys_clk>; + resets = <&reset 0>; + #pwm-cells = <1>; + + fan-0 { + pwms = <&pwm_provider 1>; + }; + + fan-1 { + pwms = <&pwm_provider 2>; + }; + }; + }; + From 4599510091a1d6d4759f1302dff32b24481eec15 Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Tue, 20 Aug 2024 00:35:57 -0500 Subject: [PATCH 59/96] hwmon: (k10temp): Use cpu_feature_enabled() for detecting zen This removes some boilerplate from the code and will allow adding future CPUs by just device IDs. Signed-off-by: Mario Limonciello Message-ID: <20240820053558.1052853-1-superm1@kernel.org> Signed-off-by: Guenter Roeck --- drivers/hwmon/k10temp.c | 26 ++++++++++---------------- 1 file changed, 10 insertions(+), 16 deletions(-) diff --git a/drivers/hwmon/k10temp.c b/drivers/hwmon/k10temp.c index 543526bac042..85a7632f3b50 100644 --- a/drivers/hwmon/k10temp.c +++ b/drivers/hwmon/k10temp.c @@ -438,16 +438,21 @@ static int k10temp_probe(struct pci_dev *pdev, const struct pci_device_id *id) data->disp_negative = true; } - if (boot_cpu_data.x86 == 0x15 && + data->is_zen = cpu_feature_enabled(X86_FEATURE_ZEN); + if (data->is_zen) { + data->temp_adjust_mask = ZEN_CUR_TEMP_RANGE_SEL_MASK; + data->read_tempreg = read_tempreg_nb_zen; + } else if (boot_cpu_data.x86 == 0x15 && ((boot_cpu_data.x86_model & 0xf0) == 0x60 || (boot_cpu_data.x86_model & 0xf0) == 0x70)) { data->read_htcreg = read_htcreg_nb_f15; data->read_tempreg = read_tempreg_nb_f15; - } else if (boot_cpu_data.x86 == 0x17 || boot_cpu_data.x86 == 0x18) { - data->temp_adjust_mask = ZEN_CUR_TEMP_RANGE_SEL_MASK; - data->read_tempreg = read_tempreg_nb_zen; - data->is_zen = true; + } else { + data->read_htcreg = read_htcreg_pci; + data->read_tempreg = read_tempreg_pci; + } + if (boot_cpu_data.x86 == 0x17 || boot_cpu_data.x86 == 0x18) { switch (boot_cpu_data.x86_model) { case 0x1: /* Zen */ case 0x8: /* Zen+ */ @@ -469,10 +474,6 @@ static int k10temp_probe(struct pci_dev *pdev, const struct pci_device_id *id) break; } } else if (boot_cpu_data.x86 == 0x19) { - data->temp_adjust_mask = ZEN_CUR_TEMP_RANGE_SEL_MASK; - data->read_tempreg = read_tempreg_nb_zen; - data->is_zen = true; - switch (boot_cpu_data.x86_model) { case 0x0 ... 0x1: /* Zen3 SP3/TR */ case 0x8: /* Zen3 TR Chagall */ @@ -496,13 +497,6 @@ static int k10temp_probe(struct pci_dev *pdev, const struct pci_device_id *id) k10temp_get_ccd_support(data, 12); break; } - } else if (boot_cpu_data.x86 == 0x1a) { - data->temp_adjust_mask = ZEN_CUR_TEMP_RANGE_SEL_MASK; - data->read_tempreg = read_tempreg_nb_zen; - data->is_zen = true; - } else { - data->read_htcreg = read_htcreg_pci; - data->read_tempreg = read_tempreg_pci; } for (i = 0; i < ARRAY_SIZE(tctl_offset_table); i++) { From 24b070d7d8d84f6af7de6057fdf0d80079c7e929 Mon Sep 17 00:00:00 2001 From: Jinjie Ruan Date: Thu, 22 Aug 2024 14:29:49 +0800 Subject: [PATCH 60/96] hwmon: (aspeed-g6-pwm-tacho): Simplify with scoped for each OF child loop Use scoped for_each_child_of_node_scoped() when iterating over device nodes to make code a bit simpler. Signed-off-by: Jinjie Ruan Reviewed-by: Andrew Jeffery Message-ID: <20240822062956.3490387-2-ruanjinjie@huawei.com> Signed-off-by: Guenter Roeck --- drivers/hwmon/aspeed-g6-pwm-tach.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/hwmon/aspeed-g6-pwm-tach.c b/drivers/hwmon/aspeed-g6-pwm-tach.c index 08a2ded95e45..75eadda738ab 100644 --- a/drivers/hwmon/aspeed-g6-pwm-tach.c +++ b/drivers/hwmon/aspeed-g6-pwm-tach.c @@ -456,7 +456,6 @@ static int aspeed_pwm_tach_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev, *hwmon; int ret; - struct device_node *child; struct aspeed_pwm_tach_data *priv; struct pwm_chip *chip; @@ -498,10 +497,9 @@ static int aspeed_pwm_tach_probe(struct platform_device *pdev) if (ret) return dev_err_probe(dev, ret, "Failed to add PWM chip\n"); - for_each_child_of_node(dev->of_node, child) { + for_each_child_of_node_scoped(dev->of_node, child) { ret = aspeed_create_fan_monitor(dev, child, priv); if (ret) { - of_node_put(child); dev_warn(dev, "Failed to create fan %d", ret); return 0; } From d5733a9387c894a277e0d3d4111e968b917a7375 Mon Sep 17 00:00:00 2001 From: Jinjie Ruan Date: Thu, 22 Aug 2024 14:29:50 +0800 Subject: [PATCH 61/96] hwmon: (aspeed-pwm-tacho): Simplify with scoped for each OF child loop Use scoped for_each_child_of_node_scoped() when iterating over device nodes to make code a bit simpler. Signed-off-by: Jinjie Ruan Reviewed-by: Andrew Jeffery Message-ID: <20240822062956.3490387-3-ruanjinjie@huawei.com> Signed-off-by: Guenter Roeck --- drivers/hwmon/aspeed-pwm-tacho.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/drivers/hwmon/aspeed-pwm-tacho.c b/drivers/hwmon/aspeed-pwm-tacho.c index 4acc1858d8ac..aa159bf158a3 100644 --- a/drivers/hwmon/aspeed-pwm-tacho.c +++ b/drivers/hwmon/aspeed-pwm-tacho.c @@ -907,7 +907,7 @@ static void aspeed_pwm_tacho_remove(void *data) static int aspeed_pwm_tacho_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; - struct device_node *np, *child; + struct device_node *np; struct aspeed_pwm_tacho_data *priv; void __iomem *regs; struct device *hwmon; @@ -951,12 +951,10 @@ static int aspeed_pwm_tacho_probe(struct platform_device *pdev) aspeed_create_type(priv); - for_each_child_of_node(np, child) { + for_each_child_of_node_scoped(np, child) { ret = aspeed_create_fan(dev, child, priv); - if (ret) { - of_node_put(child); + if (ret) return ret; - } } priv->groups[0] = &pwm_dev_group; From 3fd38c6434ce54e09d8c3fbd2daf67b9672a65d6 Mon Sep 17 00:00:00 2001 From: Jinjie Ruan Date: Thu, 22 Aug 2024 14:29:51 +0800 Subject: [PATCH 62/96] hwmon: (ina3221): Simplify with scoped for each OF child loop Use scoped for_each_child_of_node_scoped() when iterating over device nodes to make code a bit simpler. Signed-off-by: Jinjie Ruan Message-ID: <20240822062956.3490387-4-ruanjinjie@huawei.com> Signed-off-by: Guenter Roeck --- drivers/hwmon/ina3221.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/drivers/hwmon/ina3221.c b/drivers/hwmon/ina3221.c index f0053f87e3e6..1bf479a0f793 100644 --- a/drivers/hwmon/ina3221.c +++ b/drivers/hwmon/ina3221.c @@ -813,7 +813,6 @@ static int ina3221_probe_child_from_dt(struct device *dev, static int ina3221_probe_from_dt(struct device *dev, struct ina3221_data *ina) { const struct device_node *np = dev->of_node; - struct device_node *child; int ret; /* Compatible with non-DT platforms */ @@ -822,12 +821,10 @@ static int ina3221_probe_from_dt(struct device *dev, struct ina3221_data *ina) ina->single_shot = of_property_read_bool(np, "ti,single-shot"); - for_each_child_of_node(np, child) { + for_each_child_of_node_scoped(np, child) { ret = ina3221_probe_child_from_dt(dev, child, ina); - if (ret) { - of_node_put(child); + if (ret) return ret; - } } return 0; From bf0b61f0aa99933f29b0424854f18de9435ed5a0 Mon Sep 17 00:00:00 2001 From: Jinjie Ruan Date: Thu, 22 Aug 2024 14:29:52 +0800 Subject: [PATCH 63/96] hwmon: (lm90): Simplify with scoped for each OF child loop Use scoped for_each_child_of_node_scoped() when iterating over device nodes to make code a bit simpler. Signed-off-by: Jinjie Ruan Message-ID: <20240822062956.3490387-5-ruanjinjie@huawei.com> Signed-off-by: Guenter Roeck --- drivers/hwmon/lm90.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c index ca5c52b38c0f..511d95a0efb3 100644 --- a/drivers/hwmon/lm90.c +++ b/drivers/hwmon/lm90.c @@ -2674,19 +2674,16 @@ static int lm90_parse_dt_channel_info(struct i2c_client *client, struct lm90_data *data) { int err; - struct device_node *child; struct device *dev = &client->dev; const struct device_node *np = dev->of_node; - for_each_child_of_node(np, child) { + for_each_child_of_node_scoped(np, child) { if (strcmp(child->name, "channel")) continue; err = lm90_probe_channel_from_dt(client, child, data); - if (err) { - of_node_put(child); + if (err) return err; - } } return 0; From 1d20db6b544a54ff5c8e8ff3237905cb47dc38c6 Mon Sep 17 00:00:00 2001 From: Jinjie Ruan Date: Thu, 22 Aug 2024 14:29:53 +0800 Subject: [PATCH 64/96] hwmon: (nct7802): Simplify with scoped for each OF child loop Use scoped for_each_child_of_node_scoped() when iterating over device nodes to make code a bit simpler. Signed-off-by: Jinjie Ruan Message-ID: <20240822062956.3490387-6-ruanjinjie@huawei.com> Signed-off-by: Guenter Roeck --- drivers/hwmon/nct7802.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/drivers/hwmon/nct7802.c b/drivers/hwmon/nct7802.c index 5e83504800a3..8c9351da12c6 100644 --- a/drivers/hwmon/nct7802.c +++ b/drivers/hwmon/nct7802.c @@ -1129,17 +1129,14 @@ static int nct7802_configure_channels(struct device *dev, { /* Enable local temperature sensor by default */ u8 mode_mask = MODE_LTD_EN, mode_val = MODE_LTD_EN; - struct device_node *node; int err; if (dev->of_node) { - for_each_child_of_node(dev->of_node, node) { + for_each_child_of_node_scoped(dev->of_node, node) { err = nct7802_get_channel_config(dev, node, &mode_mask, &mode_val); - if (err) { - of_node_put(node); + if (err) return err; - } } } From 1b79bcace44b1f4aea798db589cb6052e902dc66 Mon Sep 17 00:00:00 2001 From: Jinjie Ruan Date: Thu, 22 Aug 2024 14:29:54 +0800 Subject: [PATCH 65/96] hwmon: (npcm750-pwm-fan): Simplify with scoped for each OF child loop Use scoped for_each_child_of_node_scoped() when iterating over device nodes to make code a bit simpler. Signed-off-by: Jinjie Ruan Message-ID: <20240822062956.3490387-7-ruanjinjie@huawei.com> Signed-off-by: Guenter Roeck --- drivers/hwmon/npcm750-pwm-fan.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/hwmon/npcm750-pwm-fan.c b/drivers/hwmon/npcm750-pwm-fan.c index bc8db1dc595d..db3b551828eb 100644 --- a/drivers/hwmon/npcm750-pwm-fan.c +++ b/drivers/hwmon/npcm750-pwm-fan.c @@ -927,7 +927,7 @@ static int npcm7xx_en_pwm_fan(struct device *dev, static int npcm7xx_pwm_fan_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; - struct device_node *np, *child; + struct device_node *np; struct npcm7xx_pwm_fan_data *data; struct resource *res; struct device *hwmon; @@ -1004,11 +1004,10 @@ static int npcm7xx_pwm_fan_probe(struct platform_device *pdev) } } - for_each_child_of_node(np, child) { + for_each_child_of_node_scoped(np, child) { ret = npcm7xx_en_pwm_fan(dev, child, data); if (ret) { dev_err(dev, "enable pwm and fan failed\n"); - of_node_put(child); return ret; } } From a6e0a54aa7ad58c6b6ff0d4194b985e15fe1c1b2 Mon Sep 17 00:00:00 2001 From: Jinjie Ruan Date: Thu, 22 Aug 2024 14:29:55 +0800 Subject: [PATCH 66/96] hwmon: (tmp421): Simplify with scoped for each OF child loop Use scoped for_each_child_of_node_scoped() when iterating over device nodes to make code a bit simpler. Signed-off-by: Jinjie Ruan Message-ID: <20240822062956.3490387-8-ruanjinjie@huawei.com> Signed-off-by: Guenter Roeck --- drivers/hwmon/tmp421.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/drivers/hwmon/tmp421.c b/drivers/hwmon/tmp421.c index 7a6f9532e594..9537727aad9a 100644 --- a/drivers/hwmon/tmp421.c +++ b/drivers/hwmon/tmp421.c @@ -410,18 +410,15 @@ static int tmp421_probe_from_dt(struct i2c_client *client, struct tmp421_data *d { struct device *dev = &client->dev; const struct device_node *np = dev->of_node; - struct device_node *child; int err; - for_each_child_of_node(np, child) { + for_each_child_of_node_scoped(np, child) { if (strcmp(child->name, "channel")) continue; err = tmp421_probe_child_from_dt(client, child, data); - if (err) { - of_node_put(child); + if (err) return err; - } } return 0; From d5b07232868b69414d1e540e7b96a60fbcab3409 Mon Sep 17 00:00:00 2001 From: Jinjie Ruan Date: Thu, 22 Aug 2024 14:29:56 +0800 Subject: [PATCH 67/96] hwmon: (tmp464): Simplify with scoped for each OF child loop Use scoped for_each_child_of_node_scoped() when iterating over device nodes to make code a bit simpler. Signed-off-by: Jinjie Ruan Message-ID: <20240822062956.3490387-9-ruanjinjie@huawei.com> Signed-off-by: Guenter Roeck --- drivers/hwmon/tmp464.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/drivers/hwmon/tmp464.c b/drivers/hwmon/tmp464.c index 0a7c0448835b..0f629c6d7695 100644 --- a/drivers/hwmon/tmp464.c +++ b/drivers/hwmon/tmp464.c @@ -562,18 +562,15 @@ static int tmp464_probe_child_from_dt(struct device *dev, static int tmp464_probe_from_dt(struct device *dev, struct tmp464_data *data) { const struct device_node *np = dev->of_node; - struct device_node *child; int err; - for_each_child_of_node(np, child) { + for_each_child_of_node_scoped(np, child) { if (strcmp(child->name, "channel")) continue; err = tmp464_probe_child_from_dt(dev, child, data); - if (err) { - of_node_put(child); + if (err) return err; - } } return 0; From 87798d9f700b9a9b4afd4193b4874b7d9c36ee1f Mon Sep 17 00:00:00 2001 From: Inochi Amaoto Date: Sat, 17 Aug 2024 10:22:56 +0800 Subject: [PATCH 68/96] dt-bindings: hwmon: Add Sophgo SG2042 external hardware monitor support Due to the design, Sophgo SG2042 use an external MCU to provide hardware information, thermal information and reset control. Add bindings for this monitor device. Signed-off-by: Inochi Amaoto Reviewed-by: Conor Dooley Tested-by: Chen Wang Reviewed-by: Chen Wang Message-ID: Signed-off-by: Guenter Roeck --- .../hwmon/sophgo,sg2042-hwmon-mcu.yaml | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 Documentation/devicetree/bindings/hwmon/sophgo,sg2042-hwmon-mcu.yaml diff --git a/Documentation/devicetree/bindings/hwmon/sophgo,sg2042-hwmon-mcu.yaml b/Documentation/devicetree/bindings/hwmon/sophgo,sg2042-hwmon-mcu.yaml new file mode 100644 index 000000000000..f0667ac41d75 --- /dev/null +++ b/Documentation/devicetree/bindings/hwmon/sophgo,sg2042-hwmon-mcu.yaml @@ -0,0 +1,43 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/hwmon/sophgo,sg2042-hwmon-mcu.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Sophgo SG2042 onboard MCU support + +maintainers: + - Inochi Amaoto + +properties: + compatible: + const: sophgo,sg2042-hwmon-mcu + + reg: + maxItems: 1 + + "#thermal-sensor-cells": + const: 1 + +required: + - compatible + - reg + - "#thermal-sensor-cells" + +allOf: + - $ref: /schemas/thermal/thermal-sensor.yaml# + +unevaluatedProperties: false + +examples: + - | + i2c { + #address-cells = <1>; + #size-cells = <0>; + + hwmon@17 { + compatible = "sophgo,sg2042-hwmon-mcu"; + reg = <0x17>; + #thermal-sensor-cells = <1>; + }; + }; From 758b62e562f2fdffd26a84dbeafbe6888a7e130c Mon Sep 17 00:00:00 2001 From: Inochi Amaoto Date: Sat, 17 Aug 2024 10:22:57 +0800 Subject: [PATCH 69/96] hwmon: Add sophgo SG2042 external hardware monitor support SG2042 use an external MCU to provide basic hardware information and thermal sensors. Add driver support for the onboard MCU of SG2042. Signed-off-by: Inochi Amaoto Message-ID: Signed-off-by: Guenter Roeck --- Documentation/hwmon/index.rst | 1 + Documentation/hwmon/sg2042-mcu.rst | 78 ++++++ drivers/hwmon/Kconfig | 11 + drivers/hwmon/Makefile | 1 + drivers/hwmon/sg2042-mcu.c | 388 +++++++++++++++++++++++++++++ 5 files changed, 479 insertions(+) create mode 100644 Documentation/hwmon/sg2042-mcu.rst create mode 100644 drivers/hwmon/sg2042-mcu.c diff --git a/Documentation/hwmon/index.rst b/Documentation/hwmon/index.rst index 913c11390a45..ea3b5be8fe4f 100644 --- a/Documentation/hwmon/index.rst +++ b/Documentation/hwmon/index.rst @@ -206,6 +206,7 @@ Hardware Monitoring Kernel Drivers sch5636 scpi-hwmon sfctemp + sg2042-mcu sht15 sht21 sht3x diff --git a/Documentation/hwmon/sg2042-mcu.rst b/Documentation/hwmon/sg2042-mcu.rst new file mode 100644 index 000000000000..077e79841d2e --- /dev/null +++ b/Documentation/hwmon/sg2042-mcu.rst @@ -0,0 +1,78 @@ +.. SPDX-License-Identifier: GPL-2.0 + +Kernel driver sg2042-mcu +======================== + +Supported chips: + + * Onboard MCU for sg2042 + + Addresses scanned: - + + Prefix: 'sg2042-mcu' + +Authors: + + - Inochi Amaoto + +Description +----------- + +This driver supprts hardware monitoring for onboard MCU with +i2c interface. + +Usage Notes +----------- + +This driver does not auto-detect devices. You will have to instantiate +the devices explicitly. +Please see Documentation/i2c/instantiating-devices.rst for details. + +Sysfs Attributes +---------------- + +The following table shows the standard entries support by the driver: + +================= ===================================================== +Name Description +================= ===================================================== +temp1_input Measured temperature of SoC +temp1_crit Critical high temperature +temp1_crit_hyst hysteresis temperature restore from Critical +temp2_input Measured temperature of the base board +================= ===================================================== + +The following table shows the extra entries support by the driver +(the MCU device is in i2c subsystem): + +================= ======= ============================================= +Name Perm Description +================= ======= ============================================= +reset_count RO Reset count of the SoC +uptime RO Seconds after the MCU is powered +reset_reason RO Reset reason for the last reset +repower_policy RW Execution policy when triggering repower +================= ======= ============================================= + +``repower_policy`` + The repower is triggered when the temperature of the SoC falls below + the hysteresis temperature after triggering a shutdown due to + reaching the critical temperature. + The valid values for this entry are "repower" and "keep". "keep" will + leave the SoC down when the triggering repower, and "repower" will + boot the SoC. + +Debugfs Interfaces +------------------ + +If debugfs is available, this driver exposes some hardware specific +data in ``/sys/kernel/debug/sg2042-mcu/*/``. + +================= ======= ============================================= +Name Format Description +================= ======= ============================================= +firmware_version 0x%02x firmware version of the MCU +pcb_version 0x%02x version number of the base board +board_type 0x%02x identifiers for the base board +mcu_type %d type of the MCU: 0 is STM32, 1 is GD32 +================= ======= ============================================= diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 1db349ecb1a4..65ea92529406 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -2068,6 +2068,17 @@ config SENSORS_SFCTEMP This driver can also be built as a module. If so, the module will be called sfctemp. +config SENSORS_SG2042_MCU + tristate "Sophgo onboard MCU support" + depends on I2C + depends on ARCH_SOPHGO || COMPILE_TEST + help + Support for onboard MCU of Sophgo SG2042 SoCs. This mcu provides + power control and some basic information. + + This driver can be built as a module. If so, the module + will be called sg2042-mcu. + config SENSORS_SURFACE_FAN tristate "Surface Fan Driver" depends on SURFACE_AGGREGATOR diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index 3ce8d6a9202e..9554d2fdcf7b 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -194,6 +194,7 @@ obj-$(CONFIG_SENSORS_SCH56XX_COMMON)+= sch56xx-common.o obj-$(CONFIG_SENSORS_SCH5627) += sch5627.o obj-$(CONFIG_SENSORS_SCH5636) += sch5636.o obj-$(CONFIG_SENSORS_SFCTEMP) += sfctemp.o +obj-$(CONFIG_SENSORS_SG2042_MCU) += sg2042-mcu.o obj-$(CONFIG_SENSORS_SL28CPLD) += sl28cpld-hwmon.o obj-$(CONFIG_SENSORS_SHT15) += sht15.o obj-$(CONFIG_SENSORS_SHT21) += sht21.o diff --git a/drivers/hwmon/sg2042-mcu.c b/drivers/hwmon/sg2042-mcu.c new file mode 100644 index 000000000000..141045769354 --- /dev/null +++ b/drivers/hwmon/sg2042-mcu.c @@ -0,0 +1,388 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2024 Inochi Amaoto + * + * Sophgo power control mcu for SG2042 + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* fixed MCU registers */ +#define REG_BOARD_TYPE 0x00 +#define REG_MCU_FIRMWARE_VERSION 0x01 +#define REG_PCB_VERSION 0x02 +#define REG_PWR_CTRL 0x03 +#define REG_SOC_TEMP 0x04 +#define REG_BOARD_TEMP 0x05 +#define REG_RST_COUNT 0x0a +#define REG_UPTIME 0x0b +#define REG_RESET_REASON 0x0d +#define REG_MCU_TYPE 0x18 +#define REG_REPOWER_POLICY 0x65 +#define REG_CRITICAL_TEMP 0x66 +#define REG_REPOWER_TEMP 0x67 + +#define REPOWER_POLICY_REBOOT 1 +#define REPOWER_POLICY_KEEP_OFF 2 + +#define MCU_POWER_MAX 0xff + +#define DEFINE_MCU_DEBUG_ATTR(_name, _reg, _format) \ + static int _name##_show(struct seq_file *seqf, \ + void *unused) \ + { \ + struct sg2042_mcu_data *mcu = seqf->private; \ + int ret; \ + ret = i2c_smbus_read_byte_data(mcu->client, (_reg)); \ + if (ret < 0) \ + return ret; \ + seq_printf(seqf, _format "\n", ret); \ + return 0; \ + } \ + DEFINE_SHOW_ATTRIBUTE(_name) \ + +struct sg2042_mcu_data { + struct i2c_client *client; + struct dentry *debugfs; + struct mutex mutex; +}; + +static struct dentry *sgmcu_debugfs; + +static ssize_t reset_count_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct sg2042_mcu_data *mcu = dev_get_drvdata(dev); + int ret; + + ret = i2c_smbus_read_byte_data(mcu->client, REG_RST_COUNT); + if (ret < 0) + return ret; + + return sprintf(buf, "%d\n", ret); +} + +static ssize_t uptime_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct sg2042_mcu_data *mcu = dev_get_drvdata(dev); + u8 time_val[2]; + int ret; + + ret = i2c_smbus_read_i2c_block_data(mcu->client, REG_UPTIME, + sizeof(time_val), time_val); + if (ret < 0) + return ret; + + return sprintf(buf, "%d\n", + (time_val[0]) | (time_val[1] << 8)); +} + +static ssize_t reset_reason_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct sg2042_mcu_data *mcu = dev_get_drvdata(dev); + int ret; + + ret = i2c_smbus_read_byte_data(mcu->client, REG_RESET_REASON); + if (ret < 0) + return ret; + + return sprintf(buf, "0x%02x\n", ret); +} + +static ssize_t repower_policy_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct sg2042_mcu_data *mcu = dev_get_drvdata(dev); + int ret; + const char *action; + + ret = i2c_smbus_read_byte_data(mcu->client, REG_REPOWER_POLICY); + if (ret < 0) + return ret; + + if (ret == REPOWER_POLICY_REBOOT) + action = "repower"; + else if (ret == REPOWER_POLICY_KEEP_OFF) + action = "keep"; + else + action = "unknown"; + + return sprintf(buf, "%s\n", action); +} + +static ssize_t repower_policy_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct sg2042_mcu_data *mcu = dev_get_drvdata(dev); + u8 value; + int ret; + + if (sysfs_streq("repower", buf)) + value = REPOWER_POLICY_REBOOT; + else if (sysfs_streq("keep", buf)) + value = REPOWER_POLICY_KEEP_OFF; + else + return -EINVAL; + + ret = i2c_smbus_write_byte_data(mcu->client, + REG_REPOWER_POLICY, value); + if (ret < 0) + return ret; + + return count; +} + +static DEVICE_ATTR_RO(reset_count); +static DEVICE_ATTR_RO(uptime); +static DEVICE_ATTR_RO(reset_reason); +static DEVICE_ATTR_RW(repower_policy); + +DEFINE_MCU_DEBUG_ATTR(firmware_version, REG_MCU_FIRMWARE_VERSION, "0x%02x"); +DEFINE_MCU_DEBUG_ATTR(pcb_version, REG_PCB_VERSION, "0x%02x"); +DEFINE_MCU_DEBUG_ATTR(board_type, REG_BOARD_TYPE, "0x%02x"); +DEFINE_MCU_DEBUG_ATTR(mcu_type, REG_MCU_TYPE, "%d"); + +static struct attribute *sg2042_mcu_attrs[] = { + &dev_attr_reset_count.attr, + &dev_attr_uptime.attr, + &dev_attr_reset_reason.attr, + &dev_attr_repower_policy.attr, + NULL +}; + +static const struct attribute_group sg2042_mcu_attr_group = { + .attrs = sg2042_mcu_attrs, +}; + +static const struct attribute_group *sg2042_mcu_groups[] = { + &sg2042_mcu_attr_group, + NULL +}; + +static const struct hwmon_channel_info * const sg2042_mcu_info[] = { + HWMON_CHANNEL_INFO(chip, HWMON_C_REGISTER_TZ), + HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT | HWMON_T_CRIT | + HWMON_T_CRIT_HYST, + HWMON_T_INPUT), + NULL +}; + +static int sg2042_mcu_read(struct device *dev, + enum hwmon_sensor_types type, + u32 attr, int channel, long *val) +{ + struct sg2042_mcu_data *mcu = dev_get_drvdata(dev); + int tmp; + u8 reg; + + switch (attr) { + case hwmon_temp_input: + reg = channel ? REG_BOARD_TEMP : REG_SOC_TEMP; + break; + case hwmon_temp_crit: + reg = REG_CRITICAL_TEMP; + break; + case hwmon_temp_crit_hyst: + reg = REG_REPOWER_TEMP; + break; + default: + return -EOPNOTSUPP; + } + + tmp = i2c_smbus_read_byte_data(mcu->client, reg); + if (tmp < 0) + return tmp; + *val = tmp * 1000; + + return 0; +} + +static int sg2042_mcu_write(struct device *dev, + enum hwmon_sensor_types type, + u32 attr, int channel, long val) +{ + struct sg2042_mcu_data *mcu = dev_get_drvdata(dev); + int temp = val / 1000; + int hyst_temp, crit_temp; + u8 reg; + + temp = clamp_val(temp, 0, MCU_POWER_MAX); + + guard(mutex)(&mcu->mutex); + + switch (attr) { + case hwmon_temp_crit: + hyst_temp = i2c_smbus_read_byte_data(mcu->client, + REG_REPOWER_TEMP); + if (hyst_temp < 0) + return hyst_temp; + + crit_temp = temp; + reg = REG_CRITICAL_TEMP; + break; + case hwmon_temp_crit_hyst: + crit_temp = i2c_smbus_read_byte_data(mcu->client, + REG_CRITICAL_TEMP); + if (crit_temp < 0) + return crit_temp; + + hyst_temp = temp; + reg = REG_REPOWER_TEMP; + break; + default: + return -EOPNOTSUPP; + } + + /* + * ensure hyst_temp is smaller to avoid MCU from + * keeping triggering repower event. + */ + if (crit_temp < hyst_temp) + return -EINVAL; + + return i2c_smbus_write_byte_data(mcu->client, reg, temp); +} + +static umode_t sg2042_mcu_is_visible(const void *_data, + enum hwmon_sensor_types type, + u32 attr, int channel) +{ + switch (type) { + case hwmon_temp: + switch (attr) { + case hwmon_temp_input: + return 0444; + case hwmon_temp_crit: + case hwmon_temp_crit_hyst: + if (channel == 0) + return 0644; + break; + default: + break; + } + break; + default: + break; + } + return 0; +} + +static const struct hwmon_ops sg2042_mcu_ops = { + .is_visible = sg2042_mcu_is_visible, + .read = sg2042_mcu_read, + .write = sg2042_mcu_write, +}; + +static const struct hwmon_chip_info sg2042_mcu_chip_info = { + .ops = &sg2042_mcu_ops, + .info = sg2042_mcu_info, +}; + +static void sg2042_mcu_debugfs_init(struct sg2042_mcu_data *mcu, + struct device *dev) +{ + mcu->debugfs = debugfs_create_dir(dev_name(dev), sgmcu_debugfs); + + debugfs_create_file("firmware_version", 0444, mcu->debugfs, + mcu, &firmware_version_fops); + debugfs_create_file("pcb_version", 0444, mcu->debugfs, mcu, + &pcb_version_fops); + debugfs_create_file("mcu_type", 0444, mcu->debugfs, mcu, + &mcu_type_fops); + debugfs_create_file("board_type", 0444, mcu->debugfs, mcu, + &board_type_fops); +} + +static int sg2042_mcu_i2c_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct sg2042_mcu_data *mcu; + struct device *hwmon_dev; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA | + I2C_FUNC_SMBUS_BLOCK_DATA)) + return -ENODEV; + + mcu = devm_kmalloc(dev, sizeof(*mcu), GFP_KERNEL); + if (!mcu) + return -ENOMEM; + + mutex_init(&mcu->mutex); + mcu->client = client; + + i2c_set_clientdata(client, mcu); + + hwmon_dev = devm_hwmon_device_register_with_info(dev, "sg2042_mcu", + mcu, + &sg2042_mcu_chip_info, + NULL); + if (IS_ERR(hwmon_dev)) + return PTR_ERR(hwmon_dev); + + sg2042_mcu_debugfs_init(mcu, dev); + + return 0; +} + +static void sg2042_mcu_i2c_remove(struct i2c_client *client) +{ + struct sg2042_mcu_data *mcu = i2c_get_clientdata(client); + + debugfs_remove_recursive(mcu->debugfs); +} + +static const struct i2c_device_id sg2042_mcu_id[] = { + { "sg2042-hwmon-mcu", 0 }, + {}, +}; +MODULE_DEVICE_TABLE(i2c, sg2042_mcu_id); + +static const struct of_device_id sg2042_mcu_of_id[] = { + { .compatible = "sophgo,sg2042-hwmon-mcu" }, + {}, +}; +MODULE_DEVICE_TABLE(of, sg2042_mcu_of_id); + +static struct i2c_driver sg2042_mcu_driver = { + .driver = { + .name = "sg2042-mcu", + .of_match_table = sg2042_mcu_of_id, + .dev_groups = sg2042_mcu_groups, + }, + .probe = sg2042_mcu_i2c_probe, + .remove = sg2042_mcu_i2c_remove, + .id_table = sg2042_mcu_id, +}; + +static int __init sg2042_mcu_init(void) +{ + sgmcu_debugfs = debugfs_create_dir("sg2042-mcu", NULL); + return i2c_add_driver(&sg2042_mcu_driver); +} + +static void __exit sg2042_mcu_exit(void) +{ + debugfs_remove_recursive(sgmcu_debugfs); + i2c_del_driver(&sg2042_mcu_driver); +} + +module_init(sg2042_mcu_init); +module_exit(sg2042_mcu_exit); + +MODULE_AUTHOR("Inochi Amaoto "); +MODULE_DESCRIPTION("MCU I2C driver for SG2042 soc platform"); +MODULE_LICENSE("GPL"); From ac9cca7a6a17c6d8bd849be669005fac0420d4b5 Mon Sep 17 00:00:00 2001 From: Shen Lichuan Date: Tue, 27 Aug 2024 15:04:42 +0800 Subject: [PATCH 70/96] hwmon: (pc87360) Use min() macro Use the min() macro to simplify the pc87360_init_device() function and improve its readability. Signed-off-by: Shen Lichuan Message-ID: <20240827070442.40667-1-shenlichuan@vivo.com> Signed-off-by: Guenter Roeck --- drivers/hwmon/pc87360.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hwmon/pc87360.c b/drivers/hwmon/pc87360.c index 9e9681b2e8c5..788b5d58f77e 100644 --- a/drivers/hwmon/pc87360.c +++ b/drivers/hwmon/pc87360.c @@ -1315,7 +1315,7 @@ static void pc87360_init_device(struct platform_device *pdev, (reg & 0xC0) | 0x11); } - nr = data->innr < 11 ? data->innr : 11; + nr = min(data->innr, 11); for (i = 0; i < nr; i++) { reg = pc87360_read_value(data, LD_IN, i, PC87365_REG_IN_STATUS); From ebb75a3c5265ceeebd2b0708d013735e5eafbb69 Mon Sep 17 00:00:00 2001 From: Johannes Kirchmair Date: Tue, 27 Aug 2024 07:44:54 +0200 Subject: [PATCH 71/96] hwmon: (pwmfan) Do not force disable pwm controller The pwm1_enable attribute of the pwmfan driver influences the mode of operation, especially in case of a requested pwm1 duty cycle of zero. Especially setting pwm1_enable to two, should keep the pwm controller enabled even if the duty cycle is set to zero [1]. This is not the case at the moment, as the pwm controller is disabled always if pwm1 is set to zero. This commit tries to fix this behavior. [1] https://docs.kernel.org/hwmon/pwm-fan.html Signed-off-by: Johannes Kirchmair Message-ID: <20240827054454.521494-1-mailinglist1@johanneskirchmair.de> Signed-off-by: Guenter Roeck --- drivers/hwmon/pwm-fan.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/drivers/hwmon/pwm-fan.c b/drivers/hwmon/pwm-fan.c index a1712649b07e..c434db4656e7 100644 --- a/drivers/hwmon/pwm-fan.c +++ b/drivers/hwmon/pwm-fan.c @@ -167,7 +167,7 @@ disable_regulator: return ret; } -static int pwm_fan_power_off(struct pwm_fan_ctx *ctx) +static int pwm_fan_power_off(struct pwm_fan_ctx *ctx, bool force_disable) { struct pwm_state *state = &ctx->pwm_state; bool enable_regulator = false; @@ -180,7 +180,8 @@ static int pwm_fan_power_off(struct pwm_fan_ctx *ctx) state, &enable_regulator); - state->enabled = false; + if (force_disable) + state->enabled = false; state->duty_cycle = 0; ret = pwm_apply_might_sleep(ctx->pwm, state); if (ret) { @@ -213,7 +214,7 @@ static int __set_pwm(struct pwm_fan_ctx *ctx, unsigned long pwm) return ret; ret = pwm_fan_power_on(ctx); } else { - ret = pwm_fan_power_off(ctx); + ret = pwm_fan_power_off(ctx, false); } if (!ret) ctx->pwm_value = pwm; @@ -468,7 +469,7 @@ static void pwm_fan_cleanup(void *__ctx) del_timer_sync(&ctx->rpm_timer); /* Switch off everything */ ctx->enable_mode = pwm_disable_reg_disable; - pwm_fan_power_off(ctx); + pwm_fan_power_off(ctx, true); } static int pwm_fan_probe(struct platform_device *pdev) @@ -661,7 +662,7 @@ static int pwm_fan_suspend(struct device *dev) { struct pwm_fan_ctx *ctx = dev_get_drvdata(dev); - return pwm_fan_power_off(ctx); + return pwm_fan_power_off(ctx, true); } static int pwm_fan_resume(struct device *dev) From 98845e7753902937da24b9053537b7936c916abd Mon Sep 17 00:00:00 2001 From: Nathan Chancellor Date: Wed, 28 Aug 2024 11:05:35 -0700 Subject: [PATCH 72/96] hwmon: (oxp-sensors) Add missing breaks to fix -Wimplicit-fallthrough with clang clang warns (or errors due to CONFIG_WERROR): drivers/hwmon/oxp-sensors.c:481:3: error: unannotated fall-through between switch labels [-Werror,-Wimplicit-fallthrough] drivers/hwmon/oxp-sensors.c:553:3: error: unannotated fall-through between switch labels [-Werror,-Wimplicit-fallthrough] drivers/hwmon/oxp-sensors.c:556:2: error: unannotated fall-through between switch labels [-Werror,-Wimplicit-fallthrough] drivers/hwmon/oxp-sensors.c:607:3: error: unannotated fall-through between switch labels [-Werror,-Wimplicit-fallthrough] Clang is a little more pedantic than GCC, which does not warn when falling through to a case that is just break or return. Clang's version is more in line with the kernel's own stance in deprecated.rst, which states that all switch/case blocks must end in either break, fallthrough, continue, goto, or return. Add the missing breaks to silence the warnings. Fixes: b82b38a49926 ("hwmon: (oxp-sensors) Add support for multiple new devices.") Signed-off-by: Nathan Chancellor Message-ID: <20240828-hwmon-oxp-sensors-fix-clang-implicit-fallthrough-v1-1-dc48496ac67a@kernel.org> Signed-off-by: Guenter Roeck --- drivers/hwmon/oxp-sensors.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/hwmon/oxp-sensors.c b/drivers/hwmon/oxp-sensors.c index b6d06370469d..83730d931824 100644 --- a/drivers/hwmon/oxp-sensors.c +++ b/drivers/hwmon/oxp-sensors.c @@ -478,6 +478,7 @@ static int oxp_platform_read(struct device *dev, enum hwmon_sensor_types type, default: break; } + break; default: break; } @@ -550,9 +551,11 @@ static int oxp_platform_read(struct device *dev, enum hwmon_sensor_types type, default: break; } + break; default: break; } + break; default: break; } @@ -604,6 +607,7 @@ static int oxp_platform_write(struct device *dev, enum hwmon_sensor_types type, default: break; } + break; default: break; } From 2f0d9872beca2def687de76a7c052db36d6ba78d Mon Sep 17 00:00:00 2001 From: Frank Li Date: Thu, 29 Aug 2024 11:06:41 -0400 Subject: [PATCH 73/96] dt-bindings: hwmon: Convert ltc2978.txt to yaml Convert binding doc ltc2978.txt to yaml format. Additional change: - add i2c node. Fix below warning: arch/arm64/boot/dts/freescale/fsl-lx2160a-clearfog-cx.dtb: /soc/i2c@2000000/i2c-mux@77/i2c@2/regulator@5c: failed to match any schema with compatible: ['lltc,ltc3882'] Signed-off-by: Frank Li Reviewed-by: Rob Herring (Arm) Message-ID: <20240829150641.1307906-1-Frank.Li@nxp.com> Signed-off-by: Guenter Roeck --- .../bindings/hwmon/lltc,ltc2978.yaml | 94 +++++++++++++++++++ .../devicetree/bindings/hwmon/ltc2978.txt | 62 ------------ 2 files changed, 94 insertions(+), 62 deletions(-) create mode 100644 Documentation/devicetree/bindings/hwmon/lltc,ltc2978.yaml delete mode 100644 Documentation/devicetree/bindings/hwmon/ltc2978.txt diff --git a/Documentation/devicetree/bindings/hwmon/lltc,ltc2978.yaml b/Documentation/devicetree/bindings/hwmon/lltc,ltc2978.yaml new file mode 100644 index 000000000000..1f98da32f3fe --- /dev/null +++ b/Documentation/devicetree/bindings/hwmon/lltc,ltc2978.yaml @@ -0,0 +1,94 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/hwmon/lltc,ltc2978.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Octal Digital Power-supply monitor/supervisor/sequencer/margin controller. + +maintainers: + - Frank Li + +properties: + compatible: + enum: + - lltc,ltc2972 + - lltc,ltc2974 + - lltc,ltc2975 + - lltc,ltc2977 + - lltc,ltc2978 + - lltc,ltc2979 + - lltc,ltc2980 + - lltc,ltc3880 + - lltc,ltc3882 + - lltc,ltc3883 + - lltc,ltc3884 + - lltc,ltc3886 + - lltc,ltc3887 + - lltc,ltc3889 + - lltc,ltc7880 + - lltc,ltm2987 + - lltc,ltm4664 + - lltc,ltm4675 + - lltc,ltm4676 + - lltc,ltm4677 + - lltc,ltm4678 + - lltc,ltm4680 + - lltc,ltm4686 + - lltc,ltm4700 + + reg: + maxItems: 1 + + regulators: + type: object + description: | + list of regulators provided by this controller. + Valid names of regulators depend on number of supplies supported per device: + * ltc2972 vout0 - vout1 + * ltc2974, ltc2975 : vout0 - vout3 + * ltc2977, ltc2979, ltc2980, ltm2987 : vout0 - vout7 + * ltc2978 : vout0 - vout7 + * ltc3880, ltc3882, ltc3884, ltc3886, ltc3887, ltc3889 : vout0 - vout1 + * ltc7880 : vout0 - vout1 + * ltc3883 : vout0 + * ltm4664 : vout0 - vout1 + * ltm4675, ltm4676, ltm4677, ltm4678 : vout0 - vout1 + * ltm4680, ltm4686 : vout0 - vout1 + * ltm4700 : vout0 - vout1 + + patternProperties: + "^vout[0-7]$": + $ref: /schemas/regulator/regulator.yaml# + type: object + unevaluatedProperties: false + + additionalProperties: false + +required: + - compatible + - reg + +additionalProperties: false + +examples: + - | + i2c { + #address-cells = <1>; + #size-cells = <0>; + + regulator@5e { + compatible = "lltc,ltc2978"; + reg = <0x5e>; + + regulators { + vout0 { + regulator-name = "FPGA-2.5V"; + }; + vout2 { + regulator-name = "FPGA-1.5V"; + }; + }; + }; + }; + diff --git a/Documentation/devicetree/bindings/hwmon/ltc2978.txt b/Documentation/devicetree/bindings/hwmon/ltc2978.txt deleted file mode 100644 index 4e7f6215a453..000000000000 --- a/Documentation/devicetree/bindings/hwmon/ltc2978.txt +++ /dev/null @@ -1,62 +0,0 @@ -ltc2978 - -Required properties: -- compatible: should contain one of: - * "lltc,ltc2972" - * "lltc,ltc2974" - * "lltc,ltc2975" - * "lltc,ltc2977" - * "lltc,ltc2978" - * "lltc,ltc2979" - * "lltc,ltc2980" - * "lltc,ltc3880" - * "lltc,ltc3882" - * "lltc,ltc3883" - * "lltc,ltc3884" - * "lltc,ltc3886" - * "lltc,ltc3887" - * "lltc,ltc3889" - * "lltc,ltc7880" - * "lltc,ltm2987" - * "lltc,ltm4664" - * "lltc,ltm4675" - * "lltc,ltm4676" - * "lltc,ltm4677" - * "lltc,ltm4678" - * "lltc,ltm4680" - * "lltc,ltm4686" - * "lltc,ltm4700" -- reg: I2C slave address - -Optional properties: -- regulators: A node that houses a sub-node for each regulator controlled by - the device. Each sub-node is identified using the node's name, with valid - values listed below. The content of each sub-node is defined by the - standard binding for regulators; see regulator.txt. - -Valid names of regulators depend on number of supplies supported per device: - * ltc2972 vout0 - vout1 - * ltc2974, ltc2975 : vout0 - vout3 - * ltc2977, ltc2979, ltc2980, ltm2987 : vout0 - vout7 - * ltc2978 : vout0 - vout7 - * ltc3880, ltc3882, ltc3884, ltc3886, ltc3887, ltc3889 : vout0 - vout1 - * ltc7880 : vout0 - vout1 - * ltc3883 : vout0 - * ltm4664 : vout0 - vout1 - * ltm4675, ltm4676, ltm4677, ltm4678 : vout0 - vout1 - * ltm4680, ltm4686 : vout0 - vout1 - * ltm4700 : vout0 - vout1 - -Example: -ltc2978@5e { - compatible = "lltc,ltc2978"; - reg = <0x5e>; - regulators { - vout0 { - regulator-name = "FPGA-2.5V"; - }; - vout2 { - regulator-name = "FPGA-1.5V"; - }; - }; -}; From 9e60bb811f582a13a1f8346675f270db7e48404c Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Tue, 23 Jul 2024 13:21:12 -0700 Subject: [PATCH 74/96] hwmon: (ina2xx) Reorder include files to alphabetic order Simplify driver maintenance by reordering include files to alphabetic order. Whule at it, drop unnecessary / unused jiffies.h. No functional change. Reviewed-by: Tzung-Bi Shih Signed-off-by: Guenter Roeck --- drivers/hwmon/ina2xx.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/drivers/hwmon/ina2xx.c b/drivers/hwmon/ina2xx.c index 9ab4205622e2..a6a619a85eb6 100644 --- a/drivers/hwmon/ina2xx.c +++ b/drivers/hwmon/ina2xx.c @@ -22,19 +22,18 @@ * Thanks to Jan Volkering */ -#include -#include -#include +#include #include -#include -#include #include #include -#include +#include +#include +#include +#include #include -#include -#include #include +#include +#include #include From 61a4a8414e1c6331a5a2fcfd235e6252c985f9d7 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Tue, 23 Jul 2024 13:23:30 -0700 Subject: [PATCH 75/96] hwmon: (ina2xx) Replace platform data with device properties There are no in-tree users of ina2xx platform data. Drop it and support device properties instead as alternative if it should ever be needed. Reviewed-by: Tzung-Bi Shih Signed-off-by: Guenter Roeck --- drivers/hwmon/ina2xx.c | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/drivers/hwmon/ina2xx.c b/drivers/hwmon/ina2xx.c index a6a619a85eb6..897657f8d685 100644 --- a/drivers/hwmon/ina2xx.c +++ b/drivers/hwmon/ina2xx.c @@ -30,13 +30,11 @@ #include #include #include -#include +#include #include #include #include -#include - /* common register definitions */ #define INA2XX_CONFIG 0x00 #define INA2XX_SHUNT_VOLTAGE 0x01 /* readonly */ @@ -643,14 +641,8 @@ static int ina2xx_probe(struct i2c_client *client) data->config = &ina2xx_config[chip]; mutex_init(&data->config_lock); - if (of_property_read_u32(dev->of_node, "shunt-resistor", &val) < 0) { - struct ina2xx_platform_data *pdata = dev_get_platdata(dev); - - if (pdata) - val = pdata->shunt_uohms; - else - val = INA2XX_RSHUNT_DEFAULT; - } + if (device_property_read_u32(dev, "shunt-resistor", &val) < 0) + val = INA2XX_RSHUNT_DEFAULT; ina2xx_set_shunt(data, val); @@ -667,7 +659,7 @@ static int ina2xx_probe(struct i2c_client *client) return dev_err_probe(dev, ret, "failed to enable vs regulator\n"); if (chip == ina226) { - if (of_property_read_bool(dev->of_node, "ti,alert-polarity-active-high")) { + if (device_property_read_bool(dev, "ti,alert-polarity-active-high")) { ret = ina2xx_set_alert_polarity(data, INA226_ALERT_POL_HIGH); if (ret < 0) { From 232177a37b90d76d3616f28fb47636d81c02fe8b Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Tue, 23 Jul 2024 13:25:31 -0700 Subject: [PATCH 76/96] hwmon: (ina2xx) Use bit operations Use bit operations where possible to make the code more generic and to align it with other drivers. Also use compile time conversion from bit to mask to reduce runtime overhead. Reviewed-by: Tzung-Bi Shih Signed-off-by: Guenter Roeck --- drivers/hwmon/ina2xx.c | 84 +++++++++++++++++++++--------------------- 1 file changed, 42 insertions(+), 42 deletions(-) diff --git a/drivers/hwmon/ina2xx.c b/drivers/hwmon/ina2xx.c index 897657f8d685..1b4170d02c94 100644 --- a/drivers/hwmon/ina2xx.c +++ b/drivers/hwmon/ina2xx.c @@ -22,6 +22,8 @@ * Thanks to Jan Volkering */ +#include +#include #include #include #include @@ -65,25 +67,23 @@ #define INA2XX_RSHUNT_DEFAULT 10000 /* bit mask for reading the averaging setting in the configuration register */ -#define INA226_AVG_RD_MASK 0x0E00 +#define INA226_AVG_RD_MASK GENMASK(11, 9) -#define INA226_READ_AVG(reg) (((reg) & INA226_AVG_RD_MASK) >> 9) -#define INA226_SHIFT_AVG(val) ((val) << 9) +#define INA226_READ_AVG(reg) FIELD_GET(INA226_AVG_RD_MASK, reg) -#define INA226_ALERT_POLARITY_MASK 0x0002 -#define INA226_SHIFT_ALERT_POLARITY(val) ((val) << 1) -#define INA226_ALERT_POL_LOW 0 -#define INA226_ALERT_POL_HIGH 1 +#define INA226_ALERT_POLARITY_MASK BIT(1) +#define INA226_ALERT_POL_LOW 0 +#define INA226_ALERT_POL_HIGH 1 /* bit number of alert functions in Mask/Enable Register */ -#define INA226_SHUNT_OVER_VOLTAGE_BIT 15 -#define INA226_SHUNT_UNDER_VOLTAGE_BIT 14 -#define INA226_BUS_OVER_VOLTAGE_BIT 13 -#define INA226_BUS_UNDER_VOLTAGE_BIT 12 -#define INA226_POWER_OVER_LIMIT_BIT 11 +#define INA226_SHUNT_OVER_VOLTAGE_MASK BIT(15) +#define INA226_SHUNT_UNDER_VOLTAGE_MASK BIT(14) +#define INA226_BUS_OVER_VOLTAGE_MASK BIT(13) +#define INA226_BUS_UNDER_VOLTAGE_MASK BIT(12) +#define INA226_POWER_OVER_LIMIT_MASK BIT(11) /* bit mask for alert config bits of Mask/Enable Register */ -#define INA226_ALERT_CONFIG_MASK 0xFC00 +#define INA226_ALERT_CONFIG_MASK GENMASK(15, 10) #define INA226_ALERT_FUNCTION_FLAG BIT(4) /* common attrs, ina226 attrs and NULL */ @@ -177,7 +177,7 @@ static u16 ina226_interval_to_reg(int interval) avg_bits = find_closest(avg, ina226_avg_tab, ARRAY_SIZE(ina226_avg_tab)); - return INA226_SHIFT_AVG(avg_bits); + return FIELD_PREP(INA226_AVG_RD_MASK, avg_bits); } static int ina2xx_set_alert_polarity(struct ina2xx_data *data, @@ -185,7 +185,7 @@ static int ina2xx_set_alert_polarity(struct ina2xx_data *data, { return regmap_update_bits(data->regmap, INA226_MASK_ENABLE, INA226_ALERT_POLARITY_MASK, - INA226_SHIFT_ALERT_POLARITY(val)); + FIELD_PREP(INA226_ALERT_POLARITY_MASK, val)); } /* @@ -322,20 +322,20 @@ static ssize_t ina2xx_value_show(struct device *dev, return sysfs_emit(buf, "%d\n", ina2xx_get_value(data, attr->index, regval)); } -static int ina226_reg_to_alert(struct ina2xx_data *data, u8 bit, u16 regval) +static int ina226_reg_to_alert(struct ina2xx_data *data, u32 mask, u16 regval) { int reg; - switch (bit) { - case INA226_SHUNT_OVER_VOLTAGE_BIT: - case INA226_SHUNT_UNDER_VOLTAGE_BIT: + switch (mask) { + case INA226_SHUNT_OVER_VOLTAGE_MASK: + case INA226_SHUNT_UNDER_VOLTAGE_MASK: reg = INA2XX_SHUNT_VOLTAGE; break; - case INA226_BUS_OVER_VOLTAGE_BIT: - case INA226_BUS_UNDER_VOLTAGE_BIT: + case INA226_BUS_OVER_VOLTAGE_MASK: + case INA226_BUS_UNDER_VOLTAGE_MASK: reg = INA2XX_BUS_VOLTAGE; break; - case INA226_POWER_OVER_LIMIT_BIT: + case INA226_POWER_OVER_LIMIT_MASK: reg = INA2XX_POWER; break; default: @@ -351,19 +351,19 @@ static int ina226_reg_to_alert(struct ina2xx_data *data, u8 bit, u16 regval) * Turns alert limit values into register values. * Opposite of the formula in ina2xx_get_value(). */ -static s16 ina226_alert_to_reg(struct ina2xx_data *data, u8 bit, int val) +static s16 ina226_alert_to_reg(struct ina2xx_data *data, u32 mask, int val) { - switch (bit) { - case INA226_SHUNT_OVER_VOLTAGE_BIT: - case INA226_SHUNT_UNDER_VOLTAGE_BIT: + switch (mask) { + case INA226_SHUNT_OVER_VOLTAGE_MASK: + case INA226_SHUNT_UNDER_VOLTAGE_MASK: val *= data->config->shunt_div; return clamp_val(val, SHRT_MIN, SHRT_MAX); - case INA226_BUS_OVER_VOLTAGE_BIT: - case INA226_BUS_UNDER_VOLTAGE_BIT: + case INA226_BUS_OVER_VOLTAGE_MASK: + case INA226_BUS_UNDER_VOLTAGE_MASK: val = (val * 1000) << data->config->bus_voltage_shift; val = DIV_ROUND_CLOSEST(val, data->config->bus_voltage_lsb); return clamp_val(val, 0, SHRT_MAX); - case INA226_POWER_OVER_LIMIT_BIT: + case INA226_POWER_OVER_LIMIT_MASK: val = DIV_ROUND_CLOSEST(val, data->power_lsb_uW); return clamp_val(val, 0, USHRT_MAX); default: @@ -387,7 +387,7 @@ static ssize_t ina226_alert_show(struct device *dev, if (ret) goto abort; - if (regval & BIT(attr->index)) { + if (regval & attr->index) { ret = regmap_read(data->regmap, INA226_ALERT_LIMIT, ®val); if (ret) goto abort; @@ -432,7 +432,7 @@ static ssize_t ina226_alert_store(struct device *dev, if (val != 0) { ret = regmap_update_bits(data->regmap, INA226_MASK_ENABLE, INA226_ALERT_CONFIG_MASK, - BIT(attr->index)); + attr->index); if (ret < 0) goto abort; } @@ -456,7 +456,7 @@ static ssize_t ina226_alarm_show(struct device *dev, if (ret) return ret; - alarm = (regval & BIT(attr->index)) && + alarm = (regval & attr->index) && (regval & INA226_ALERT_FUNCTION_FLAG); return sysfs_emit(buf, "%d\n", alarm); } @@ -552,25 +552,25 @@ static ssize_t ina226_interval_show(struct device *dev, static SENSOR_DEVICE_ATTR_RO(in0_input, ina2xx_value, INA2XX_SHUNT_VOLTAGE); /* shunt voltage over/under voltage alert setting and alarm */ static SENSOR_DEVICE_ATTR_RW(in0_crit, ina226_alert, - INA226_SHUNT_OVER_VOLTAGE_BIT); + INA226_SHUNT_OVER_VOLTAGE_MASK); static SENSOR_DEVICE_ATTR_RW(in0_lcrit, ina226_alert, - INA226_SHUNT_UNDER_VOLTAGE_BIT); + INA226_SHUNT_UNDER_VOLTAGE_MASK); static SENSOR_DEVICE_ATTR_RO(in0_crit_alarm, ina226_alarm, - INA226_SHUNT_OVER_VOLTAGE_BIT); + INA226_SHUNT_OVER_VOLTAGE_MASK); static SENSOR_DEVICE_ATTR_RO(in0_lcrit_alarm, ina226_alarm, - INA226_SHUNT_UNDER_VOLTAGE_BIT); + INA226_SHUNT_UNDER_VOLTAGE_MASK); /* bus voltage */ static SENSOR_DEVICE_ATTR_RO(in1_input, ina2xx_value, INA2XX_BUS_VOLTAGE); /* bus voltage over/under voltage alert setting and alarm */ static SENSOR_DEVICE_ATTR_RW(in1_crit, ina226_alert, - INA226_BUS_OVER_VOLTAGE_BIT); + INA226_BUS_OVER_VOLTAGE_MASK); static SENSOR_DEVICE_ATTR_RW(in1_lcrit, ina226_alert, - INA226_BUS_UNDER_VOLTAGE_BIT); + INA226_BUS_UNDER_VOLTAGE_MASK); static SENSOR_DEVICE_ATTR_RO(in1_crit_alarm, ina226_alarm, - INA226_BUS_OVER_VOLTAGE_BIT); + INA226_BUS_OVER_VOLTAGE_MASK); static SENSOR_DEVICE_ATTR_RO(in1_lcrit_alarm, ina226_alarm, - INA226_BUS_UNDER_VOLTAGE_BIT); + INA226_BUS_UNDER_VOLTAGE_MASK); /* calculated current */ static SENSOR_DEVICE_ATTR_RO(curr1_input, ina2xx_value, INA2XX_CURRENT); @@ -579,9 +579,9 @@ static SENSOR_DEVICE_ATTR_RO(curr1_input, ina2xx_value, INA2XX_CURRENT); static SENSOR_DEVICE_ATTR_RO(power1_input, ina2xx_value, INA2XX_POWER); /* over-limit power alert setting and alarm */ static SENSOR_DEVICE_ATTR_RW(power1_crit, ina226_alert, - INA226_POWER_OVER_LIMIT_BIT); + INA226_POWER_OVER_LIMIT_MASK); static SENSOR_DEVICE_ATTR_RO(power1_crit_alarm, ina226_alarm, - INA226_POWER_OVER_LIMIT_BIT); + INA226_POWER_OVER_LIMIT_MASK); /* shunt resistance */ static SENSOR_DEVICE_ATTR_RW(shunt_resistor, ina2xx_shunt, INA2XX_CALIBRATION); From 2bb476524c61f43e6e89aeacaee6599ce5dd3505 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Thu, 1 Aug 2024 16:43:03 -0700 Subject: [PATCH 77/96] hwmon: (ina2xx) Mark regmap_config as const Recent versions of checkpatch complain that struct regmap_config should be declared as const. WARNING: struct regmap_config should normally be const Doing so reveals a potential problem in the driver: If both supported chips are present in a single system, the maximum number of registers may race when devices are instantiated since max_registers is updated in the probe function. Solve the problem by setting .max_registers to the maximum register address of all supported chips. This does not make a practical difference while fixing the potential race condition and reducing code complexity. Reviewed-by: Tzung-Bi Shih Signed-off-by: Guenter Roeck --- drivers/hwmon/ina2xx.c | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/drivers/hwmon/ina2xx.c b/drivers/hwmon/ina2xx.c index 1b4170d02c94..9d93190874d7 100644 --- a/drivers/hwmon/ina2xx.c +++ b/drivers/hwmon/ina2xx.c @@ -50,10 +50,6 @@ #define INA226_ALERT_LIMIT 0x07 #define INA226_DIE_ID 0xFF -/* register count */ -#define INA219_REGISTERS 6 -#define INA226_REGISTERS 8 - #define INA2XX_MAX_REGISTERS 8 /* settings - depend on use case */ @@ -95,9 +91,10 @@ */ #define INA226_TOTAL_CONV_TIME_DEFAULT 2200 -static struct regmap_config ina2xx_regmap_config = { +static const struct regmap_config ina2xx_regmap_config = { .reg_bits = 8, .val_bits = 16, + .max_register = INA2XX_MAX_REGISTERS, }; enum ina2xx_ids { ina219, ina226 }; @@ -105,7 +102,6 @@ enum ina2xx_ids { ina219, ina226 }; struct ina2xx_config { u16 config_default; int calibration_value; - int registers; int shunt_div; int bus_voltage_shift; int bus_voltage_lsb; /* uV */ @@ -128,7 +124,6 @@ static const struct ina2xx_config ina2xx_config[] = { [ina219] = { .config_default = INA219_CONFIG_DEFAULT, .calibration_value = 4096, - .registers = INA219_REGISTERS, .shunt_div = 100, .bus_voltage_shift = 3, .bus_voltage_lsb = 4000, @@ -137,7 +132,6 @@ static const struct ina2xx_config ina2xx_config[] = { [ina226] = { .config_default = INA226_CONFIG_DEFAULT, .calibration_value = 2048, - .registers = INA226_REGISTERS, .shunt_div = 400, .bus_voltage_shift = 0, .bus_voltage_lsb = 1250, @@ -646,8 +640,6 @@ static int ina2xx_probe(struct i2c_client *client) ina2xx_set_shunt(data, val); - ina2xx_regmap_config.max_register = data->config->registers; - data->regmap = devm_regmap_init_i2c(client, &ina2xx_regmap_config); if (IS_ERR(data->regmap)) { dev_err(dev, "failed to allocate register map\n"); From d491e781b0600487be9f85977deb5f833d15db56 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Thu, 1 Aug 2024 14:57:39 -0700 Subject: [PATCH 78/96] hwmon: (ina2xx) Use local regmap pointer if used more than once If regmap is accessed more than once in a function, declare and used local regmap variable. While at it, drop low value debug messages. Reviewed-by: Tzung-Bi Shih Signed-off-by: Guenter Roeck --- drivers/hwmon/ina2xx.c | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/drivers/hwmon/ina2xx.c b/drivers/hwmon/ina2xx.c index 9d93190874d7..ed8764a29d3f 100644 --- a/drivers/hwmon/ina2xx.c +++ b/drivers/hwmon/ina2xx.c @@ -210,18 +210,14 @@ static int ina2xx_init(struct ina2xx_data *data) static int ina2xx_read_reg(struct device *dev, int reg, unsigned int *regval) { struct ina2xx_data *data = dev_get_drvdata(dev); + struct regmap *regmap = data->regmap; int ret, retry; - dev_dbg(dev, "Starting register %d read\n", reg); - for (retry = 5; retry; retry--) { - - ret = regmap_read(data->regmap, reg, regval); + ret = regmap_read(regmap, reg, regval); if (ret < 0) return ret; - dev_dbg(dev, "read %d, val = 0x%04x\n", reg, *regval); - /* * If the current value in the calibration register is 0, the * power and current registers will also remain at 0. In case @@ -233,8 +229,7 @@ static int ina2xx_read_reg(struct device *dev, int reg, unsigned int *regval) if (*regval == 0) { unsigned int cal; - ret = regmap_read(data->regmap, INA2XX_CALIBRATION, - &cal); + ret = regmap_read(regmap, INA2XX_CALIBRATION, &cal); if (ret < 0) return ret; @@ -372,17 +367,18 @@ static ssize_t ina226_alert_show(struct device *dev, { struct sensor_device_attribute *attr = to_sensor_dev_attr(da); struct ina2xx_data *data = dev_get_drvdata(dev); + struct regmap *regmap = data->regmap; int regval; int val = 0; int ret; mutex_lock(&data->config_lock); - ret = regmap_read(data->regmap, INA226_MASK_ENABLE, ®val); + ret = regmap_read(regmap, INA226_MASK_ENABLE, ®val); if (ret) goto abort; if (regval & attr->index) { - ret = regmap_read(data->regmap, INA226_ALERT_LIMIT, ®val); + ret = regmap_read(regmap, INA226_ALERT_LIMIT, ®val); if (ret) goto abort; val = ina226_reg_to_alert(data, attr->index, regval); @@ -400,6 +396,7 @@ static ssize_t ina226_alert_store(struct device *dev, { struct sensor_device_attribute *attr = to_sensor_dev_attr(da); struct ina2xx_data *data = dev_get_drvdata(dev); + struct regmap *regmap = data->regmap; unsigned long val; int ret; @@ -413,18 +410,18 @@ static ssize_t ina226_alert_store(struct device *dev, * if the value is non-zero. */ mutex_lock(&data->config_lock); - ret = regmap_update_bits(data->regmap, INA226_MASK_ENABLE, + ret = regmap_update_bits(regmap, INA226_MASK_ENABLE, INA226_ALERT_CONFIG_MASK, 0); if (ret < 0) goto abort; - ret = regmap_write(data->regmap, INA226_ALERT_LIMIT, + ret = regmap_write(regmap, INA226_ALERT_LIMIT, ina226_alert_to_reg(data, attr->index, val)); if (ret < 0) goto abort; if (val != 0) { - ret = regmap_update_bits(data->regmap, INA226_MASK_ENABLE, + ret = regmap_update_bits(regmap, INA226_MASK_ENABLE, INA226_ALERT_CONFIG_MASK, attr->index); if (ret < 0) From bb25cdc2bff408ee3be1f20bd2cee4ea8b79c2d6 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Tue, 23 Jul 2024 16:04:29 -0700 Subject: [PATCH 79/96] hwmon: (ina2xx) Re-initialize chip using regmap functions If it is necessary to re-initialize the chip, for example because it has been power cycled, use regmap functions to update register contents. This ensures that all registers, including the configuration register and alert registers, are updated to previously configured values without having to locally cache everything. For this to work, volatile registers have to be marked as volatile. Also, the cache needs to be bypassed when reading the calibration and mask_enable registers. While the calibration register is not volatile, it will be reset to 0 if the chip has been power cycled. Most of the bits in the mask_enable register are configuration bits, except for bit 4 which reports if an alert has been observed. Both registers need to be marked as non-volatile to be updated after a power cycle, but it is necessary to bypass the cache when reading them to detect if the chip has been power cycled and to read the alert status. The chip does not support register auto-increments. It is therefore necessary to configure regmap to use single register read/write operations. Otherwise regmap tries to write all registers in a single operation when synchronizing register contents with the hardware, and the synchronization fails. Another necessary change is to declare ina226_alert_to_reg() as u16. So far it returned an s16 which is sign extended to a large negative value which is then sent to regmap as unsigned int, causing an -EINVAL error return. Reviewed-by: Tzung-Bi Shih Signed-off-by: Guenter Roeck --- drivers/hwmon/ina2xx.c | 50 ++++++++++++++++++++++++++++++++++-------- 1 file changed, 41 insertions(+), 9 deletions(-) diff --git a/drivers/hwmon/ina2xx.c b/drivers/hwmon/ina2xx.c index ed8764a29d3f..db6432523e59 100644 --- a/drivers/hwmon/ina2xx.c +++ b/drivers/hwmon/ina2xx.c @@ -91,10 +91,41 @@ */ #define INA226_TOTAL_CONV_TIME_DEFAULT 2200 +static bool ina2xx_writeable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case INA2XX_CONFIG: + case INA2XX_CALIBRATION: + case INA226_MASK_ENABLE: + case INA226_ALERT_LIMIT: + return true; + default: + return false; + } +} + +static bool ina2xx_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case INA2XX_SHUNT_VOLTAGE: + case INA2XX_BUS_VOLTAGE: + case INA2XX_POWER: + case INA2XX_CURRENT: + return true; + default: + return false; + } +} + static const struct regmap_config ina2xx_regmap_config = { .reg_bits = 8, .val_bits = 16, + .use_single_write = true, + .use_single_read = true, .max_register = INA2XX_MAX_REGISTERS, + .cache_type = REGCACHE_MAPLE, + .volatile_reg = ina2xx_volatile_reg, + .writeable_reg = ina2xx_writeable_reg, }; enum ina2xx_ids { ina219, ina226 }; @@ -229,16 +260,16 @@ static int ina2xx_read_reg(struct device *dev, int reg, unsigned int *regval) if (*regval == 0) { unsigned int cal; - ret = regmap_read(regmap, INA2XX_CALIBRATION, &cal); + ret = regmap_read_bypassed(regmap, INA2XX_CALIBRATION, &cal); if (ret < 0) return ret; if (cal == 0) { dev_warn(dev, "chip not calibrated, reinitializing\n"); - ret = ina2xx_init(data); - if (ret < 0) - return ret; + regcache_mark_dirty(regmap); + regcache_sync(regmap); + /* * Let's make sure the power and current * registers have been updated before trying @@ -340,7 +371,7 @@ static int ina226_reg_to_alert(struct ina2xx_data *data, u32 mask, u16 regval) * Turns alert limit values into register values. * Opposite of the formula in ina2xx_get_value(). */ -static s16 ina226_alert_to_reg(struct ina2xx_data *data, u32 mask, int val) +static u16 ina226_alert_to_reg(struct ina2xx_data *data, u32 mask, int val) { switch (mask) { case INA226_SHUNT_OVER_VOLTAGE_MASK: @@ -439,16 +470,17 @@ static ssize_t ina226_alarm_show(struct device *dev, { struct sensor_device_attribute *attr = to_sensor_dev_attr(da); struct ina2xx_data *data = dev_get_drvdata(dev); - int regval; + unsigned int mask; int alarm = 0; int ret; - ret = regmap_read(data->regmap, INA226_MASK_ENABLE, ®val); + ret = regmap_read_bypassed(data->regmap, INA226_MASK_ENABLE, &mask); if (ret) return ret; - alarm = (regval & attr->index) && - (regval & INA226_ALERT_FUNCTION_FLAG); + alarm = (mask & attr->index) && + (mask & INA226_ALERT_FUNCTION_FLAG); + return sysfs_emit(buf, "%d\n", alarm); } From ab7fbee452beca56b7c570d49190e679863362d5 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Wed, 24 Jul 2024 09:42:16 -0700 Subject: [PATCH 80/96] hwmon: (ina2xx) Fix various overflow issues Module tests show various overflow problems when writing limits and other attributes. in0_crit: Suspected overflow: [max=82, read 0, written 2147483648] in0_lcrit: Suspected overflow: [max=82, read 0, written 2147483648] in1_crit: Suspected overflow: [max=40959, read 0, written 2147483647] in1_lcrit: Suspected overflow: [max=40959, read 0, written 2147483647] power1_crit: Suspected overflow: [max=134218750, read 0, written 2147483648] update_interval: Suspected overflow: [max=2253, read 2, written 2147483647] Implement missing clamping on attribute write operations to avoid those problems. While at it, check in the probe function if the shunt resistor value passed from devicetree is valid, and bail out if it isn't. Also limit mutex use to the code calling ina2xx_set_shunt() since it isn't needed when called from the probe function. Reviewed-by: Tzung-Bi Shih Signed-off-by: Guenter Roeck --- drivers/hwmon/ina2xx.c | 32 ++++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/drivers/hwmon/ina2xx.c b/drivers/hwmon/ina2xx.c index db6432523e59..2aea461b8c6d 100644 --- a/drivers/hwmon/ina2xx.c +++ b/drivers/hwmon/ina2xx.c @@ -193,10 +193,16 @@ static int ina226_reg_to_interval(u16 config) * Return the new, shifted AVG field value of CONFIG register, * to use with regmap_update_bits */ -static u16 ina226_interval_to_reg(int interval) +static u16 ina226_interval_to_reg(unsigned long interval) { int avg, avg_bits; + /* + * The maximum supported interval is 1,024 * (2 * 8.244ms) ~= 16.8s. + * Clamp to 32 seconds before calculations to avoid overflows. + */ + interval = clamp_val(interval, 0, 32000); + avg = DIV_ROUND_CLOSEST(interval * 1000, INA226_TOTAL_CONV_TIME_DEFAULT); avg_bits = find_closest(avg, ina226_avg_tab, @@ -371,19 +377,22 @@ static int ina226_reg_to_alert(struct ina2xx_data *data, u32 mask, u16 regval) * Turns alert limit values into register values. * Opposite of the formula in ina2xx_get_value(). */ -static u16 ina226_alert_to_reg(struct ina2xx_data *data, u32 mask, int val) +static u16 ina226_alert_to_reg(struct ina2xx_data *data, u32 mask, unsigned long val) { switch (mask) { case INA226_SHUNT_OVER_VOLTAGE_MASK: case INA226_SHUNT_UNDER_VOLTAGE_MASK: + val = clamp_val(val, 0, SHRT_MAX * data->config->shunt_div); val *= data->config->shunt_div; - return clamp_val(val, SHRT_MIN, SHRT_MAX); + return clamp_val(val, 0, SHRT_MAX); case INA226_BUS_OVER_VOLTAGE_MASK: case INA226_BUS_UNDER_VOLTAGE_MASK: + val = clamp_val(val, 0, 200000); val = (val * 1000) << data->config->bus_voltage_shift; val = DIV_ROUND_CLOSEST(val, data->config->bus_voltage_lsb); - return clamp_val(val, 0, SHRT_MAX); + return clamp_val(val, 0, USHRT_MAX); case INA226_POWER_OVER_LIMIT_MASK: + val = clamp_val(val, 0, UINT_MAX - data->power_lsb_uW); val = DIV_ROUND_CLOSEST(val, data->power_lsb_uW); return clamp_val(val, 0, USHRT_MAX); default: @@ -490,19 +499,17 @@ static ssize_t ina226_alarm_show(struct device *dev, * to shunt_voltage_lsb = 1 / shunt_div multiplied by 10^9 in order * to keep the scale. */ -static int ina2xx_set_shunt(struct ina2xx_data *data, long val) +static int ina2xx_set_shunt(struct ina2xx_data *data, unsigned long val) { unsigned int dividend = DIV_ROUND_CLOSEST(1000000000, data->config->shunt_div); - if (val <= 0 || val > dividend) + if (!val || val > dividend) return -EINVAL; - mutex_lock(&data->config_lock); data->rshunt = val; data->current_lsb_uA = DIV_ROUND_CLOSEST(dividend, val); data->power_lsb_uW = data->config->power_lsb_factor * data->current_lsb_uA; - mutex_unlock(&data->config_lock); return 0; } @@ -527,7 +534,9 @@ static ssize_t ina2xx_shunt_store(struct device *dev, if (status < 0) return status; + mutex_lock(&data->config_lock); status = ina2xx_set_shunt(data, val); + mutex_unlock(&data->config_lock); if (status < 0) return status; return count; @@ -545,9 +554,6 @@ static ssize_t ina226_interval_store(struct device *dev, if (status < 0) return status; - if (val > INT_MAX || val == 0) - return -EINVAL; - status = regmap_update_bits(data->regmap, INA2XX_CONFIG, INA226_AVG_RD_MASK, ina226_interval_to_reg(val)); @@ -667,7 +673,9 @@ static int ina2xx_probe(struct i2c_client *client) if (device_property_read_u32(dev, "shunt-resistor", &val) < 0) val = INA2XX_RSHUNT_DEFAULT; - ina2xx_set_shunt(data, val); + ret = ina2xx_set_shunt(data, val); + if (ret < 0) + return dev_err_probe(dev, ret, "Invalid shunt resistor value\n"); data->regmap = devm_regmap_init_i2c(client, &ina2xx_regmap_config); if (IS_ERR(data->regmap)) { From 51c6fa3246cd6f12e3194795b0a934c1aa8f9d4f Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Wed, 24 Jul 2024 09:31:08 -0700 Subject: [PATCH 81/96] hwmon: (ina2xx) Consolidate chip initialization code Move all chip initialization code into a single function. No functional change. Reviewed-by: Tzung-Bi Shih Signed-off-by: Guenter Roeck --- drivers/hwmon/ina2xx.c | 111 ++++++++++++++++------------------------- 1 file changed, 43 insertions(+), 68 deletions(-) diff --git a/drivers/hwmon/ina2xx.c b/drivers/hwmon/ina2xx.c index 2aea461b8c6d..a9e3b23445b4 100644 --- a/drivers/hwmon/ina2xx.c +++ b/drivers/hwmon/ina2xx.c @@ -67,9 +67,7 @@ #define INA226_READ_AVG(reg) FIELD_GET(INA226_AVG_RD_MASK, reg) -#define INA226_ALERT_POLARITY_MASK BIT(1) -#define INA226_ALERT_POL_LOW 0 -#define INA226_ALERT_POL_HIGH 1 +#define INA226_ALERT_POLARITY BIT(1) /* bit number of alert functions in Mask/Enable Register */ #define INA226_SHUNT_OVER_VOLTAGE_MASK BIT(15) @@ -141,6 +139,7 @@ struct ina2xx_config { struct ina2xx_data { const struct ina2xx_config *config; + enum ina2xx_ids chip; long rshunt; long current_lsb_uA; @@ -211,39 +210,6 @@ static u16 ina226_interval_to_reg(unsigned long interval) return FIELD_PREP(INA226_AVG_RD_MASK, avg_bits); } -static int ina2xx_set_alert_polarity(struct ina2xx_data *data, - unsigned long val) -{ - return regmap_update_bits(data->regmap, INA226_MASK_ENABLE, - INA226_ALERT_POLARITY_MASK, - FIELD_PREP(INA226_ALERT_POLARITY_MASK, val)); -} - -/* - * Calibration register is set to the best value, which eliminates - * truncation errors on calculating current register in hardware. - * According to datasheet (eq. 3) the best values are 2048 for - * ina226 and 4096 for ina219. They are hardcoded as calibration_value. - */ -static int ina2xx_calibrate(struct ina2xx_data *data) -{ - return regmap_write(data->regmap, INA2XX_CALIBRATION, - data->config->calibration_value); -} - -/* - * Initialize the configuration and calibration registers. - */ -static int ina2xx_init(struct ina2xx_data *data) -{ - int ret = regmap_write(data->regmap, INA2XX_CONFIG, - data->config->config_default); - if (ret < 0) - return ret; - - return ina2xx_calibrate(data); -} - static int ina2xx_read_reg(struct device *dev, int reg, unsigned int *regval) { struct ina2xx_data *data = dev_get_drvdata(dev); @@ -651,12 +617,48 @@ static const struct attribute_group ina226_group = { .attrs = ina226_attrs, }; +/* + * Initialize chip + */ +static int ina2xx_init(struct device *dev, struct ina2xx_data *data) +{ + struct regmap *regmap = data->regmap; + u32 shunt; + int ret; + + if (device_property_read_u32(dev, "shunt-resistor", &shunt) < 0) + shunt = INA2XX_RSHUNT_DEFAULT; + + ret = ina2xx_set_shunt(data, shunt); + if (ret < 0) + return ret; + + ret = regmap_write(regmap, INA2XX_CONFIG, data->config->config_default); + if (ret < 0) + return ret; + + if (data->chip == ina226) { + bool active_high = device_property_read_bool(dev, "ti,alert-polarity-active-high"); + + regmap_update_bits(regmap, INA226_MASK_ENABLE, INA226_ALERT_POLARITY, + FIELD_PREP(INA226_ALERT_POLARITY, active_high)); + } + + /* + * Calibration register is set to the best value, which eliminates + * truncation errors on calculating current register in hardware. + * According to datasheet (eq. 3) the best values are 2048 for + * ina226 and 4096 for ina219. They are hardcoded as calibration_value. + */ + return regmap_write(regmap, INA2XX_CALIBRATION, + data->config->calibration_value); +} + static int ina2xx_probe(struct i2c_client *client) { struct device *dev = &client->dev; struct ina2xx_data *data; struct device *hwmon_dev; - u32 val; int ret, group = 0; enum ina2xx_ids chip; @@ -668,15 +670,9 @@ static int ina2xx_probe(struct i2c_client *client) /* set the device type */ data->config = &ina2xx_config[chip]; + data->chip = chip; mutex_init(&data->config_lock); - if (device_property_read_u32(dev, "shunt-resistor", &val) < 0) - val = INA2XX_RSHUNT_DEFAULT; - - ret = ina2xx_set_shunt(data, val); - if (ret < 0) - return dev_err_probe(dev, ret, "Invalid shunt resistor value\n"); - data->regmap = devm_regmap_init_i2c(client, &ina2xx_regmap_config); if (IS_ERR(data->regmap)) { dev_err(dev, "failed to allocate register map\n"); @@ -687,30 +683,9 @@ static int ina2xx_probe(struct i2c_client *client) if (ret) return dev_err_probe(dev, ret, "failed to enable vs regulator\n"); - if (chip == ina226) { - if (device_property_read_bool(dev, "ti,alert-polarity-active-high")) { - ret = ina2xx_set_alert_polarity(data, - INA226_ALERT_POL_HIGH); - if (ret < 0) { - return dev_err_probe(dev, ret, - "failed to set alert polarity active high\n"); - } - } else { - /* Set default value i.e active low */ - ret = ina2xx_set_alert_polarity(data, - INA226_ALERT_POL_LOW); - if (ret < 0) { - return dev_err_probe(dev, ret, - "failed to set alert polarity active low\n"); - } - } - } - - ret = ina2xx_init(data); - if (ret < 0) { - dev_err(dev, "error configuring the device: %d\n", ret); - return -ENODEV; - } + ret = ina2xx_init(dev, data); + if (ret < 0) + return dev_err_probe(dev, ret, "failed to configure device\n"); data->groups[group++] = &ina2xx_group; if (chip == ina226) From aa7d17636640233062075c22afbb3e25fc5e5a91 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Tue, 23 Jul 2024 16:17:02 -0700 Subject: [PATCH 82/96] hwmon: (ina2xx) Set alert latch Alerts should only be cleared after reported, not immediately after the alert condition has been cleared. Set the latch enable bit to keep alerts latched until the alert register has been read from the chip. Reviewed-by: Tzung-Bi Shih Signed-off-by: Guenter Roeck --- drivers/hwmon/ina2xx.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/hwmon/ina2xx.c b/drivers/hwmon/ina2xx.c index a9e3b23445b4..98338b7e7437 100644 --- a/drivers/hwmon/ina2xx.c +++ b/drivers/hwmon/ina2xx.c @@ -67,6 +67,7 @@ #define INA226_READ_AVG(reg) FIELD_GET(INA226_AVG_RD_MASK, reg) +#define INA226_ALERT_LATCH_ENABLE BIT(0) #define INA226_ALERT_POLARITY BIT(1) /* bit number of alert functions in Mask/Enable Register */ @@ -640,8 +641,10 @@ static int ina2xx_init(struct device *dev, struct ina2xx_data *data) if (data->chip == ina226) { bool active_high = device_property_read_bool(dev, "ti,alert-polarity-active-high"); - regmap_update_bits(regmap, INA226_MASK_ENABLE, INA226_ALERT_POLARITY, - FIELD_PREP(INA226_ALERT_POLARITY, active_high)); + regmap_update_bits(regmap, INA226_MASK_ENABLE, + INA226_ALERT_LATCH_ENABLE | INA226_ALERT_POLARITY, + INA226_ALERT_LATCH_ENABLE | + FIELD_PREP(INA226_ALERT_POLARITY, active_high)); } /* From c263d9166765567433f759b6435a3255cfdd4620 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Thu, 1 Aug 2024 15:34:48 -0700 Subject: [PATCH 83/96] hwmon: (ina2xx) Move ina2xx_get_value() ina2xx_get_value() will be needed earlier in the next patch, so move it. No functional change. Reviewed-by: Tzung-Bi Shih Signed-off-by: Guenter Roeck --- drivers/hwmon/ina2xx.c | 72 +++++++++++++++++++++--------------------- 1 file changed, 36 insertions(+), 36 deletions(-) diff --git a/drivers/hwmon/ina2xx.c b/drivers/hwmon/ina2xx.c index 98338b7e7437..fe850ee06024 100644 --- a/drivers/hwmon/ina2xx.c +++ b/drivers/hwmon/ina2xx.c @@ -211,6 +211,42 @@ static u16 ina226_interval_to_reg(unsigned long interval) return FIELD_PREP(INA226_AVG_RD_MASK, avg_bits); } +static int ina2xx_get_value(struct ina2xx_data *data, u8 reg, + unsigned int regval) +{ + int val; + + switch (reg) { + case INA2XX_SHUNT_VOLTAGE: + /* signed register */ + val = DIV_ROUND_CLOSEST((s16)regval, data->config->shunt_div); + break; + case INA2XX_BUS_VOLTAGE: + val = (regval >> data->config->bus_voltage_shift) * + data->config->bus_voltage_lsb; + val = DIV_ROUND_CLOSEST(val, 1000); + break; + case INA2XX_POWER: + val = regval * data->power_lsb_uW; + break; + case INA2XX_CURRENT: + /* signed register, result in mA */ + val = (s16)regval * data->current_lsb_uA; + val = DIV_ROUND_CLOSEST(val, 1000); + break; + case INA2XX_CALIBRATION: + val = regval; + break; + default: + /* programmer goofed */ + WARN_ON_ONCE(1); + val = 0; + break; + } + + return val; +} + static int ina2xx_read_reg(struct device *dev, int reg, unsigned int *regval) { struct ina2xx_data *data = dev_get_drvdata(dev); @@ -264,42 +300,6 @@ static int ina2xx_read_reg(struct device *dev, int reg, unsigned int *regval) return -ENODEV; } -static int ina2xx_get_value(struct ina2xx_data *data, u8 reg, - unsigned int regval) -{ - int val; - - switch (reg) { - case INA2XX_SHUNT_VOLTAGE: - /* signed register */ - val = DIV_ROUND_CLOSEST((s16)regval, data->config->shunt_div); - break; - case INA2XX_BUS_VOLTAGE: - val = (regval >> data->config->bus_voltage_shift) - * data->config->bus_voltage_lsb; - val = DIV_ROUND_CLOSEST(val, 1000); - break; - case INA2XX_POWER: - val = regval * data->power_lsb_uW; - break; - case INA2XX_CURRENT: - /* signed register, result in mA */ - val = (s16)regval * data->current_lsb_uA; - val = DIV_ROUND_CLOSEST(val, 1000); - break; - case INA2XX_CALIBRATION: - val = regval; - break; - default: - /* programmer goofed */ - WARN_ON_ONCE(1); - val = 0; - break; - } - - return val; -} - static ssize_t ina2xx_value_show(struct device *dev, struct device_attribute *da, char *buf) { From 814db9f1b8ec1cad9fa707c52c695550cbb66b80 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Wed, 24 Jul 2024 22:41:16 -0700 Subject: [PATCH 84/96] hwmon: (ina2xx) Convert to use with_info hwmon API Convert driver to use the with_info hardware monitoring API to reduce its dependency on sysfs attribute functions. Reviewed-by: Tzung-Bi Shih Signed-off-by: Guenter Roeck --- drivers/hwmon/ina2xx.c | 495 ++++++++++++++++++++++++----------------- 1 file changed, 293 insertions(+), 202 deletions(-) diff --git a/drivers/hwmon/ina2xx.c b/drivers/hwmon/ina2xx.c index fe850ee06024..339d41dfa10e 100644 --- a/drivers/hwmon/ina2xx.c +++ b/drivers/hwmon/ina2xx.c @@ -25,9 +25,9 @@ #include #include #include +#include #include #include -#include #include #include #include @@ -35,6 +35,7 @@ #include #include #include +#include #include /* common register definitions */ @@ -81,9 +82,6 @@ #define INA226_ALERT_CONFIG_MASK GENMASK(15, 10) #define INA226_ALERT_FUNCTION_FLAG BIT(4) -/* common attrs, ina226 attrs and NULL */ -#define INA2XX_MAX_ATTRIBUTE_GROUPS 3 - /* * Both bus voltage and shunt voltage conversion times for ina226 are set * to 0b0100 on POR, which translates to 2200 microseconds in total. @@ -147,8 +145,6 @@ struct ina2xx_data { long power_lsb_uW; struct mutex config_lock; struct regmap *regmap; - - const struct attribute_group *groups[INA2XX_MAX_ATTRIBUTE_GROUPS]; }; static const struct ina2xx_config ina2xx_config[] = { @@ -193,7 +189,7 @@ static int ina226_reg_to_interval(u16 config) * Return the new, shifted AVG field value of CONFIG register, * to use with regmap_update_bits */ -static u16 ina226_interval_to_reg(unsigned long interval) +static u16 ina226_interval_to_reg(long interval) { int avg, avg_bits; @@ -247,14 +243,19 @@ static int ina2xx_get_value(struct ina2xx_data *data, u8 reg, return val; } -static int ina2xx_read_reg(struct device *dev, int reg, unsigned int *regval) +/* + * Read and convert register value from chip. If the register value is 0, + * check if the chip has been power cycled or reset. If so, re-initialize it. + */ +static int ina2xx_read_init(struct device *dev, int reg, long *val) { struct ina2xx_data *data = dev_get_drvdata(dev); struct regmap *regmap = data->regmap; + unsigned int regval; int ret, retry; for (retry = 5; retry; retry--) { - ret = regmap_read(regmap, reg, regval); + ret = regmap_read(regmap, reg, ®val); if (ret < 0) return ret; @@ -266,7 +267,7 @@ static int ina2xx_read_reg(struct device *dev, int reg, unsigned int *regval) * We do that extra read of the calibration register if there * is some hint of a chip reset. */ - if (*regval == 0) { + if (regval == 0) { unsigned int cal; ret = regmap_read_bypassed(regmap, INA2XX_CALIBRATION, &cal); @@ -288,6 +289,7 @@ static int ina2xx_read_reg(struct device *dev, int reg, unsigned int *regval) continue; } } + *val = ina2xx_get_value(data, reg, regval); return 0; } @@ -300,46 +302,6 @@ static int ina2xx_read_reg(struct device *dev, int reg, unsigned int *regval) return -ENODEV; } -static ssize_t ina2xx_value_show(struct device *dev, - struct device_attribute *da, char *buf) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(da); - struct ina2xx_data *data = dev_get_drvdata(dev); - unsigned int regval; - - int err = ina2xx_read_reg(dev, attr->index, ®val); - - if (err < 0) - return err; - - return sysfs_emit(buf, "%d\n", ina2xx_get_value(data, attr->index, regval)); -} - -static int ina226_reg_to_alert(struct ina2xx_data *data, u32 mask, u16 regval) -{ - int reg; - - switch (mask) { - case INA226_SHUNT_OVER_VOLTAGE_MASK: - case INA226_SHUNT_UNDER_VOLTAGE_MASK: - reg = INA2XX_SHUNT_VOLTAGE; - break; - case INA226_BUS_OVER_VOLTAGE_MASK: - case INA226_BUS_UNDER_VOLTAGE_MASK: - reg = INA2XX_BUS_VOLTAGE; - break; - case INA226_POWER_OVER_LIMIT_MASK: - reg = INA2XX_POWER; - break; - default: - /* programmer goofed */ - WARN_ON_ONCE(1); - return 0; - } - - return ina2xx_get_value(data, reg, regval); -} - /* * Turns alert limit values into register values. * Opposite of the formula in ina2xx_get_value(). @@ -369,14 +331,10 @@ static u16 ina226_alert_to_reg(struct ina2xx_data *data, u32 mask, unsigned long } } -static ssize_t ina226_alert_show(struct device *dev, - struct device_attribute *da, char *buf) +static int ina226_alert_limit_read(struct ina2xx_data *data, u32 mask, int reg, long *val) { - struct sensor_device_attribute *attr = to_sensor_dev_attr(da); - struct ina2xx_data *data = dev_get_drvdata(dev); struct regmap *regmap = data->regmap; int regval; - int val = 0; int ret; mutex_lock(&data->config_lock); @@ -384,32 +342,26 @@ static ssize_t ina226_alert_show(struct device *dev, if (ret) goto abort; - if (regval & attr->index) { + if (regval & mask) { ret = regmap_read(regmap, INA226_ALERT_LIMIT, ®val); if (ret) goto abort; - val = ina226_reg_to_alert(data, attr->index, regval); + *val = ina2xx_get_value(data, reg, regval); + } else { + *val = 0; } - - ret = sysfs_emit(buf, "%d\n", val); abort: mutex_unlock(&data->config_lock); return ret; } -static ssize_t ina226_alert_store(struct device *dev, - struct device_attribute *da, - const char *buf, size_t count) +static int ina226_alert_limit_write(struct ina2xx_data *data, u32 mask, long val) { - struct sensor_device_attribute *attr = to_sensor_dev_attr(da); - struct ina2xx_data *data = dev_get_drvdata(dev); struct regmap *regmap = data->regmap; - unsigned long val; int ret; - ret = kstrtoul(buf, 10, &val); - if (ret < 0) - return ret; + if (val < 0) + return -EINVAL; /* * Clear all alerts first to avoid accidentally triggering ALERT pin @@ -423,43 +375,285 @@ static ssize_t ina226_alert_store(struct device *dev, goto abort; ret = regmap_write(regmap, INA226_ALERT_LIMIT, - ina226_alert_to_reg(data, attr->index, val)); + ina226_alert_to_reg(data, mask, val)); if (ret < 0) goto abort; - if (val != 0) { + if (val) ret = regmap_update_bits(regmap, INA226_MASK_ENABLE, - INA226_ALERT_CONFIG_MASK, - attr->index); - if (ret < 0) - goto abort; - } - - ret = count; + INA226_ALERT_CONFIG_MASK, mask); abort: mutex_unlock(&data->config_lock); return ret; } -static ssize_t ina226_alarm_show(struct device *dev, - struct device_attribute *da, char *buf) +static int ina2xx_chip_read(struct device *dev, u32 attr, long *val) { - struct sensor_device_attribute *attr = to_sensor_dev_attr(da); struct ina2xx_data *data = dev_get_drvdata(dev); - unsigned int mask; - int alarm = 0; + u32 regval; int ret; - ret = regmap_read_bypassed(data->regmap, INA226_MASK_ENABLE, &mask); + switch (attr) { + case hwmon_chip_update_interval: + ret = regmap_read(data->regmap, INA2XX_CONFIG, ®val); + if (ret) + return ret; + + *val = ina226_reg_to_interval(regval); + break; + default: + return -EOPNOTSUPP; + } + return 0; +} + +static int ina226_alert_read(struct regmap *regmap, u32 mask, long *val) +{ + unsigned int regval; + int ret; + + ret = regmap_read_bypassed(regmap, INA226_MASK_ENABLE, ®val); if (ret) return ret; - alarm = (mask & attr->index) && - (mask & INA226_ALERT_FUNCTION_FLAG); + *val = (regval & mask) && (regval & INA226_ALERT_FUNCTION_FLAG); - return sysfs_emit(buf, "%d\n", alarm); + return 0; } +static int ina2xx_in_read(struct device *dev, u32 attr, int channel, long *val) +{ + int voltage_reg = channel ? INA2XX_BUS_VOLTAGE : INA2XX_SHUNT_VOLTAGE; + u32 under_voltage_mask = channel ? INA226_BUS_UNDER_VOLTAGE_MASK + : INA226_SHUNT_UNDER_VOLTAGE_MASK; + u32 over_voltage_mask = channel ? INA226_BUS_OVER_VOLTAGE_MASK + : INA226_SHUNT_OVER_VOLTAGE_MASK; + struct ina2xx_data *data = dev_get_drvdata(dev); + struct regmap *regmap = data->regmap; + unsigned int regval; + int ret; + + switch (attr) { + case hwmon_in_input: + ret = regmap_read(regmap, voltage_reg, ®val); + if (ret) + return ret; + *val = ina2xx_get_value(data, voltage_reg, regval); + break; + case hwmon_in_lcrit: + return ina226_alert_limit_read(data, under_voltage_mask, + voltage_reg, val); + case hwmon_in_crit: + return ina226_alert_limit_read(data, over_voltage_mask, + voltage_reg, val); + case hwmon_in_lcrit_alarm: + return ina226_alert_read(regmap, under_voltage_mask, val); + case hwmon_in_crit_alarm: + return ina226_alert_read(regmap, over_voltage_mask, val); + default: + return -EOPNOTSUPP; + } + return 0; +} + +static int ina2xx_power_read(struct device *dev, u32 attr, long *val) +{ + struct ina2xx_data *data = dev_get_drvdata(dev); + + switch (attr) { + case hwmon_power_input: + return ina2xx_read_init(dev, INA2XX_POWER, val); + case hwmon_power_crit: + return ina226_alert_limit_read(data, INA226_POWER_OVER_LIMIT_MASK, + INA2XX_POWER, val); + case hwmon_power_crit_alarm: + return ina226_alert_read(data->regmap, INA226_POWER_OVER_LIMIT_MASK, val); + default: + return -EOPNOTSUPP; + } +} + +static int ina2xx_curr_read(struct device *dev, u32 attr, long *val) +{ + switch (attr) { + case hwmon_curr_input: + return ina2xx_read_init(dev, INA2XX_CURRENT, val); + default: + return -EOPNOTSUPP; + } +} + +static int ina2xx_read(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long *val) +{ + switch (type) { + case hwmon_chip: + return ina2xx_chip_read(dev, attr, val); + case hwmon_in: + return ina2xx_in_read(dev, attr, channel, val); + case hwmon_power: + return ina2xx_power_read(dev, attr, val); + case hwmon_curr: + return ina2xx_curr_read(dev, attr, val); + default: + return -EOPNOTSUPP; + } +} + +static int ina2xx_chip_write(struct device *dev, u32 attr, long val) +{ + struct ina2xx_data *data = dev_get_drvdata(dev); + + switch (attr) { + case hwmon_chip_update_interval: + return regmap_update_bits(data->regmap, INA2XX_CONFIG, + INA226_AVG_RD_MASK, + ina226_interval_to_reg(val)); + default: + return -EOPNOTSUPP; + } +} + +static int ina2xx_in_write(struct device *dev, u32 attr, int channel, long val) +{ + struct ina2xx_data *data = dev_get_drvdata(dev); + + switch (attr) { + case hwmon_in_lcrit: + return ina226_alert_limit_write(data, + channel ? INA226_BUS_UNDER_VOLTAGE_MASK : INA226_SHUNT_UNDER_VOLTAGE_MASK, + val); + case hwmon_in_crit: + return ina226_alert_limit_write(data, + channel ? INA226_BUS_OVER_VOLTAGE_MASK : INA226_SHUNT_OVER_VOLTAGE_MASK, + val); + default: + return -EOPNOTSUPP; + } + return 0; +} + +static int ina2xx_power_write(struct device *dev, u32 attr, long val) +{ + struct ina2xx_data *data = dev_get_drvdata(dev); + + switch (attr) { + case hwmon_power_crit: + return ina226_alert_limit_write(data, INA226_POWER_OVER_LIMIT_MASK, val); + default: + return -EOPNOTSUPP; + } + return 0; +} + +static int ina2xx_write(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long val) +{ + switch (type) { + case hwmon_chip: + return ina2xx_chip_write(dev, attr, val); + case hwmon_in: + return ina2xx_in_write(dev, attr, channel, val); + case hwmon_power: + return ina2xx_power_write(dev, attr, val); + default: + return -EOPNOTSUPP; + } +} + +static umode_t ina2xx_is_visible(const void *_data, enum hwmon_sensor_types type, + u32 attr, int channel) +{ + const struct ina2xx_data *data = _data; + enum ina2xx_ids chip = data->chip; + + switch (type) { + case hwmon_in: + switch (attr) { + case hwmon_in_input: + return 0444; + case hwmon_in_lcrit: + case hwmon_in_crit: + if (chip == ina226) + return 0644; + break; + case hwmon_in_lcrit_alarm: + case hwmon_in_crit_alarm: + if (chip == ina226) + return 0444; + break; + default: + break; + } + break; + case hwmon_curr: + switch (attr) { + case hwmon_curr_input: + return 0444; + default: + break; + } + break; + case hwmon_power: + switch (attr) { + case hwmon_power_input: + return 0444; + case hwmon_power_crit: + if (chip == ina226) + return 0644; + break; + case hwmon_power_crit_alarm: + if (chip == ina226) + return 0444; + break; + default: + break; + } + break; + case hwmon_chip: + switch (attr) { + case hwmon_chip_update_interval: + if (chip == ina226) + return 0644; + break; + default: + break; + } + break; + default: + break; + } + return 0; +} + +static const struct hwmon_channel_info * const ina2xx_info[] = { + HWMON_CHANNEL_INFO(chip, + HWMON_C_UPDATE_INTERVAL), + HWMON_CHANNEL_INFO(in, + HWMON_I_INPUT | HWMON_I_CRIT | HWMON_I_CRIT_ALARM | + HWMON_I_LCRIT | HWMON_I_LCRIT_ALARM, + HWMON_I_INPUT | HWMON_I_CRIT | HWMON_I_CRIT_ALARM | + HWMON_I_LCRIT | HWMON_I_LCRIT_ALARM + ), + HWMON_CHANNEL_INFO(curr, HWMON_C_INPUT), + HWMON_CHANNEL_INFO(power, + HWMON_P_INPUT | HWMON_P_CRIT | HWMON_P_CRIT_ALARM), + NULL +}; + +static const struct hwmon_ops ina2xx_hwmon_ops = { + .is_visible = ina2xx_is_visible, + .read = ina2xx_read, + .write = ina2xx_write, +}; + +static const struct hwmon_chip_info ina2xx_chip_info = { + .ops = &ina2xx_hwmon_ops, + .info = ina2xx_info, +}; + +/* shunt resistance */ + /* * In order to keep calibration register value fixed, the product * of current_lsb and shunt_resistor should also be fixed and equal @@ -481,21 +675,21 @@ static int ina2xx_set_shunt(struct ina2xx_data *data, unsigned long val) return 0; } -static ssize_t ina2xx_shunt_show(struct device *dev, - struct device_attribute *da, char *buf) +static ssize_t shunt_resistor_show(struct device *dev, + struct device_attribute *da, char *buf) { struct ina2xx_data *data = dev_get_drvdata(dev); return sysfs_emit(buf, "%li\n", data->rshunt); } -static ssize_t ina2xx_shunt_store(struct device *dev, - struct device_attribute *da, - const char *buf, size_t count) +static ssize_t shunt_resistor_store(struct device *dev, + struct device_attribute *da, + const char *buf, size_t count) { + struct ina2xx_data *data = dev_get_drvdata(dev); unsigned long val; int status; - struct ina2xx_data *data = dev_get_drvdata(dev); status = kstrtoul(buf, 10, &val); if (status < 0) @@ -509,114 +703,14 @@ static ssize_t ina2xx_shunt_store(struct device *dev, return count; } -static ssize_t ina226_interval_store(struct device *dev, - struct device_attribute *da, - const char *buf, size_t count) -{ - struct ina2xx_data *data = dev_get_drvdata(dev); - unsigned long val; - int status; - - status = kstrtoul(buf, 10, &val); - if (status < 0) - return status; - - status = regmap_update_bits(data->regmap, INA2XX_CONFIG, - INA226_AVG_RD_MASK, - ina226_interval_to_reg(val)); - if (status < 0) - return status; - - return count; -} - -static ssize_t ina226_interval_show(struct device *dev, - struct device_attribute *da, char *buf) -{ - struct ina2xx_data *data = dev_get_drvdata(dev); - int status; - unsigned int regval; - - status = regmap_read(data->regmap, INA2XX_CONFIG, ®val); - if (status) - return status; - - return sysfs_emit(buf, "%d\n", ina226_reg_to_interval(regval)); -} - -/* shunt voltage */ -static SENSOR_DEVICE_ATTR_RO(in0_input, ina2xx_value, INA2XX_SHUNT_VOLTAGE); -/* shunt voltage over/under voltage alert setting and alarm */ -static SENSOR_DEVICE_ATTR_RW(in0_crit, ina226_alert, - INA226_SHUNT_OVER_VOLTAGE_MASK); -static SENSOR_DEVICE_ATTR_RW(in0_lcrit, ina226_alert, - INA226_SHUNT_UNDER_VOLTAGE_MASK); -static SENSOR_DEVICE_ATTR_RO(in0_crit_alarm, ina226_alarm, - INA226_SHUNT_OVER_VOLTAGE_MASK); -static SENSOR_DEVICE_ATTR_RO(in0_lcrit_alarm, ina226_alarm, - INA226_SHUNT_UNDER_VOLTAGE_MASK); - -/* bus voltage */ -static SENSOR_DEVICE_ATTR_RO(in1_input, ina2xx_value, INA2XX_BUS_VOLTAGE); -/* bus voltage over/under voltage alert setting and alarm */ -static SENSOR_DEVICE_ATTR_RW(in1_crit, ina226_alert, - INA226_BUS_OVER_VOLTAGE_MASK); -static SENSOR_DEVICE_ATTR_RW(in1_lcrit, ina226_alert, - INA226_BUS_UNDER_VOLTAGE_MASK); -static SENSOR_DEVICE_ATTR_RO(in1_crit_alarm, ina226_alarm, - INA226_BUS_OVER_VOLTAGE_MASK); -static SENSOR_DEVICE_ATTR_RO(in1_lcrit_alarm, ina226_alarm, - INA226_BUS_UNDER_VOLTAGE_MASK); - -/* calculated current */ -static SENSOR_DEVICE_ATTR_RO(curr1_input, ina2xx_value, INA2XX_CURRENT); - -/* calculated power */ -static SENSOR_DEVICE_ATTR_RO(power1_input, ina2xx_value, INA2XX_POWER); -/* over-limit power alert setting and alarm */ -static SENSOR_DEVICE_ATTR_RW(power1_crit, ina226_alert, - INA226_POWER_OVER_LIMIT_MASK); -static SENSOR_DEVICE_ATTR_RO(power1_crit_alarm, ina226_alarm, - INA226_POWER_OVER_LIMIT_MASK); - -/* shunt resistance */ -static SENSOR_DEVICE_ATTR_RW(shunt_resistor, ina2xx_shunt, INA2XX_CALIBRATION); - -/* update interval (ina226 only) */ -static SENSOR_DEVICE_ATTR_RW(update_interval, ina226_interval, 0); +static DEVICE_ATTR_RW(shunt_resistor); /* pointers to created device attributes */ static struct attribute *ina2xx_attrs[] = { - &sensor_dev_attr_in0_input.dev_attr.attr, - &sensor_dev_attr_in1_input.dev_attr.attr, - &sensor_dev_attr_curr1_input.dev_attr.attr, - &sensor_dev_attr_power1_input.dev_attr.attr, - &sensor_dev_attr_shunt_resistor.dev_attr.attr, + &dev_attr_shunt_resistor.attr, NULL, }; - -static const struct attribute_group ina2xx_group = { - .attrs = ina2xx_attrs, -}; - -static struct attribute *ina226_attrs[] = { - &sensor_dev_attr_in0_crit.dev_attr.attr, - &sensor_dev_attr_in0_lcrit.dev_attr.attr, - &sensor_dev_attr_in0_crit_alarm.dev_attr.attr, - &sensor_dev_attr_in0_lcrit_alarm.dev_attr.attr, - &sensor_dev_attr_in1_crit.dev_attr.attr, - &sensor_dev_attr_in1_lcrit.dev_attr.attr, - &sensor_dev_attr_in1_crit_alarm.dev_attr.attr, - &sensor_dev_attr_in1_lcrit_alarm.dev_attr.attr, - &sensor_dev_attr_power1_crit.dev_attr.attr, - &sensor_dev_attr_power1_crit_alarm.dev_attr.attr, - &sensor_dev_attr_update_interval.dev_attr.attr, - NULL, -}; - -static const struct attribute_group ina226_group = { - .attrs = ina226_attrs, -}; +ATTRIBUTE_GROUPS(ina2xx); /* * Initialize chip @@ -662,8 +756,8 @@ static int ina2xx_probe(struct i2c_client *client) struct device *dev = &client->dev; struct ina2xx_data *data; struct device *hwmon_dev; - int ret, group = 0; enum ina2xx_ids chip; + int ret; chip = (uintptr_t)i2c_get_match_data(client); @@ -690,12 +784,9 @@ static int ina2xx_probe(struct i2c_client *client) if (ret < 0) return dev_err_probe(dev, ret, "failed to configure device\n"); - data->groups[group++] = &ina2xx_group; - if (chip == ina226) - data->groups[group++] = &ina226_group; - - hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name, - data, data->groups); + hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, + data, &ina2xx_chip_info, + ina2xx_groups); if (IS_ERR(hwmon_dev)) return PTR_ERR(hwmon_dev); From 9965ebd1836fb75c7a80f20ca65469f5df0d6063 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Wed, 28 Aug 2024 15:18:51 -0700 Subject: [PATCH 85/96] hwmon: (ina2xx) Pass register to alert limit write functions Pass the to-be-limited register to alert functions and use it to determine conversion from limit to register value. Reviewed-by: Tzung-Bi Shih Signed-off-by: Guenter Roeck --- drivers/hwmon/ina2xx.c | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/drivers/hwmon/ina2xx.c b/drivers/hwmon/ina2xx.c index 339d41dfa10e..1cd6fffb1495 100644 --- a/drivers/hwmon/ina2xx.c +++ b/drivers/hwmon/ina2xx.c @@ -306,21 +306,19 @@ static int ina2xx_read_init(struct device *dev, int reg, long *val) * Turns alert limit values into register values. * Opposite of the formula in ina2xx_get_value(). */ -static u16 ina226_alert_to_reg(struct ina2xx_data *data, u32 mask, unsigned long val) +static u16 ina226_alert_to_reg(struct ina2xx_data *data, int reg, unsigned long val) { - switch (mask) { - case INA226_SHUNT_OVER_VOLTAGE_MASK: - case INA226_SHUNT_UNDER_VOLTAGE_MASK: + switch (reg) { + case INA2XX_SHUNT_VOLTAGE: val = clamp_val(val, 0, SHRT_MAX * data->config->shunt_div); val *= data->config->shunt_div; return clamp_val(val, 0, SHRT_MAX); - case INA226_BUS_OVER_VOLTAGE_MASK: - case INA226_BUS_UNDER_VOLTAGE_MASK: + case INA2XX_BUS_VOLTAGE: val = clamp_val(val, 0, 200000); val = (val * 1000) << data->config->bus_voltage_shift; val = DIV_ROUND_CLOSEST(val, data->config->bus_voltage_lsb); return clamp_val(val, 0, USHRT_MAX); - case INA226_POWER_OVER_LIMIT_MASK: + case INA2XX_POWER: val = clamp_val(val, 0, UINT_MAX - data->power_lsb_uW); val = DIV_ROUND_CLOSEST(val, data->power_lsb_uW); return clamp_val(val, 0, USHRT_MAX); @@ -355,7 +353,7 @@ abort: return ret; } -static int ina226_alert_limit_write(struct ina2xx_data *data, u32 mask, long val) +static int ina226_alert_limit_write(struct ina2xx_data *data, u32 mask, int reg, long val) { struct regmap *regmap = data->regmap; int ret; @@ -375,7 +373,7 @@ static int ina226_alert_limit_write(struct ina2xx_data *data, u32 mask, long val goto abort; ret = regmap_write(regmap, INA226_ALERT_LIMIT, - ina226_alert_to_reg(data, mask, val)); + ina226_alert_to_reg(data, reg, val)); if (ret < 0) goto abort; @@ -522,10 +520,12 @@ static int ina2xx_in_write(struct device *dev, u32 attr, int channel, long val) case hwmon_in_lcrit: return ina226_alert_limit_write(data, channel ? INA226_BUS_UNDER_VOLTAGE_MASK : INA226_SHUNT_UNDER_VOLTAGE_MASK, + channel ? INA2XX_BUS_VOLTAGE : INA2XX_SHUNT_VOLTAGE, val); case hwmon_in_crit: return ina226_alert_limit_write(data, channel ? INA226_BUS_OVER_VOLTAGE_MASK : INA226_SHUNT_OVER_VOLTAGE_MASK, + channel ? INA2XX_BUS_VOLTAGE : INA2XX_SHUNT_VOLTAGE, val); default: return -EOPNOTSUPP; @@ -539,7 +539,8 @@ static int ina2xx_power_write(struct device *dev, u32 attr, long val) switch (attr) { case hwmon_power_crit: - return ina226_alert_limit_write(data, INA226_POWER_OVER_LIMIT_MASK, val); + return ina226_alert_limit_write(data, INA226_POWER_OVER_LIMIT_MASK, + INA2XX_POWER, val); default: return -EOPNOTSUPP; } From 4d5c2d986757e4d6f56761af8ab689218a2bc432 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Wed, 28 Aug 2024 15:23:53 -0700 Subject: [PATCH 86/96] hwmon: (ina2xx) Add support for current limits While the chips supported by this driver do not directly support current limits, they do support setting shunt voltage limits. The shunt voltage divided by the shunt resistor value is the current. On top of that, calibration values are set such that in the shunt voltage register and the current register report the same values. That means we can report and configure current limits based on shunt voltage limits, and we can do so with much better accuracy than by setting shunt voltage limits. Reviewed-by: Tzung-Bi Shih Signed-off-by: Guenter Roeck --- Documentation/hwmon/ina2xx.rst | 4 +++ drivers/hwmon/ina2xx.c | 61 ++++++++++++++++++++++++++++++++-- 2 files changed, 63 insertions(+), 2 deletions(-) diff --git a/Documentation/hwmon/ina2xx.rst b/Documentation/hwmon/ina2xx.rst index 27d2e39bc8ac..7f1939b40f74 100644 --- a/Documentation/hwmon/ina2xx.rst +++ b/Documentation/hwmon/ina2xx.rst @@ -99,6 +99,10 @@ Sysfs entries for ina226, ina230 and ina231 only ------------------------------------------------ ======================= ==================================================== +curr1_lcrit Critical low current +curr1_crit Critical high current +curr1_lcrit_alarm Current critical low alarm +curr1_crit_alarm Current critical high alarm in0_lcrit Critical low shunt voltage in0_crit Critical high shunt voltage in0_lcrit_alarm Shunt voltage critical low alarm diff --git a/drivers/hwmon/ina2xx.c b/drivers/hwmon/ina2xx.c index 1cd6fffb1495..10c8c475c634 100644 --- a/drivers/hwmon/ina2xx.c +++ b/drivers/hwmon/ina2xx.c @@ -306,7 +306,7 @@ static int ina2xx_read_init(struct device *dev, int reg, long *val) * Turns alert limit values into register values. * Opposite of the formula in ina2xx_get_value(). */ -static u16 ina226_alert_to_reg(struct ina2xx_data *data, int reg, unsigned long val) +static u16 ina226_alert_to_reg(struct ina2xx_data *data, int reg, long val) { switch (reg) { case INA2XX_SHUNT_VOLTAGE: @@ -322,6 +322,11 @@ static u16 ina226_alert_to_reg(struct ina2xx_data *data, int reg, unsigned long val = clamp_val(val, 0, UINT_MAX - data->power_lsb_uW); val = DIV_ROUND_CLOSEST(val, data->power_lsb_uW); return clamp_val(val, 0, USHRT_MAX); + case INA2XX_CURRENT: + val = clamp_val(val, INT_MIN / 1000, INT_MAX / 1000); + /* signed register, result in mA */ + val = DIV_ROUND_CLOSEST(val * 1000, data->current_lsb_uA); + return clamp_val(val, SHRT_MIN, SHRT_MAX); default: /* programmer goofed */ WARN_ON_ONCE(1); @@ -473,9 +478,31 @@ static int ina2xx_power_read(struct device *dev, u32 attr, long *val) static int ina2xx_curr_read(struct device *dev, u32 attr, long *val) { + struct ina2xx_data *data = dev_get_drvdata(dev); + struct regmap *regmap = data->regmap; + + /* + * While the chips supported by this driver do not directly support + * current limits, they do support setting shunt voltage limits. + * The shunt voltage divided by the shunt resistor value is the current. + * On top of that, calibration values are set such that in the shunt + * voltage register and the current register report the same values. + * That means we can report and configure current limits based on shunt + * voltage limits. + */ switch (attr) { case hwmon_curr_input: return ina2xx_read_init(dev, INA2XX_CURRENT, val); + case hwmon_curr_lcrit: + return ina226_alert_limit_read(data, INA226_SHUNT_UNDER_VOLTAGE_MASK, + INA2XX_CURRENT, val); + case hwmon_curr_crit: + return ina226_alert_limit_read(data, INA226_SHUNT_OVER_VOLTAGE_MASK, + INA2XX_CURRENT, val); + case hwmon_curr_lcrit_alarm: + return ina226_alert_read(regmap, INA226_SHUNT_UNDER_VOLTAGE_MASK, val); + case hwmon_curr_crit_alarm: + return ina226_alert_read(regmap, INA226_SHUNT_OVER_VOLTAGE_MASK, val); default: return -EOPNOTSUPP; } @@ -547,6 +574,23 @@ static int ina2xx_power_write(struct device *dev, u32 attr, long val) return 0; } +static int ina2xx_curr_write(struct device *dev, u32 attr, long val) +{ + struct ina2xx_data *data = dev_get_drvdata(dev); + + switch (attr) { + case hwmon_curr_lcrit: + return ina226_alert_limit_write(data, INA226_SHUNT_UNDER_VOLTAGE_MASK, + INA2XX_CURRENT, val); + case hwmon_curr_crit: + return ina226_alert_limit_write(data, INA226_SHUNT_OVER_VOLTAGE_MASK, + INA2XX_CURRENT, val); + default: + return -EOPNOTSUPP; + } + return 0; +} + static int ina2xx_write(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel, long val) { @@ -557,6 +601,8 @@ static int ina2xx_write(struct device *dev, enum hwmon_sensor_types type, return ina2xx_in_write(dev, attr, channel, val); case hwmon_power: return ina2xx_power_write(dev, attr, val); + case hwmon_curr: + return ina2xx_curr_write(dev, attr, val); default: return -EOPNOTSUPP; } @@ -591,6 +637,16 @@ static umode_t ina2xx_is_visible(const void *_data, enum hwmon_sensor_types type switch (attr) { case hwmon_curr_input: return 0444; + case hwmon_curr_lcrit: + case hwmon_curr_crit: + if (chip == ina226) + return 0644; + break; + case hwmon_curr_lcrit_alarm: + case hwmon_curr_crit_alarm: + if (chip == ina226) + return 0444; + break; default: break; } @@ -636,7 +692,8 @@ static const struct hwmon_channel_info * const ina2xx_info[] = { HWMON_I_INPUT | HWMON_I_CRIT | HWMON_I_CRIT_ALARM | HWMON_I_LCRIT | HWMON_I_LCRIT_ALARM ), - HWMON_CHANNEL_INFO(curr, HWMON_C_INPUT), + HWMON_CHANNEL_INFO(curr, HWMON_C_INPUT | HWMON_C_CRIT | HWMON_C_CRIT_ALARM | + HWMON_C_LCRIT | HWMON_C_LCRIT_ALARM), HWMON_CHANNEL_INFO(power, HWMON_P_INPUT | HWMON_P_CRIT | HWMON_P_CRIT_ALARM), NULL From 63fb21afc1f5b2af746f332710491c61e2ad6d74 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Wed, 28 Aug 2024 17:21:41 -0700 Subject: [PATCH 87/96] hwmon: (ina2xx) Use shunt voltage to calculate current Since the shunt voltage and the current register report the same values when the chip is calibrated, we can calculate the current directly from the shunt voltage without relying on chip calibration. With this change, the current register is no longer accessed. Its register address is only used to indicate if reading or writing current or shunt voltage is desired when accessing registers. Reviewed-by: Tzung-Bi Shih Signed-off-by: Guenter Roeck --- drivers/hwmon/ina2xx.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/drivers/hwmon/ina2xx.c b/drivers/hwmon/ina2xx.c index 10c8c475c634..f0fa6d073627 100644 --- a/drivers/hwmon/ina2xx.c +++ b/drivers/hwmon/ina2xx.c @@ -480,6 +480,8 @@ static int ina2xx_curr_read(struct device *dev, u32 attr, long *val) { struct ina2xx_data *data = dev_get_drvdata(dev); struct regmap *regmap = data->regmap; + unsigned int regval; + int ret; /* * While the chips supported by this driver do not directly support @@ -492,7 +494,17 @@ static int ina2xx_curr_read(struct device *dev, u32 attr, long *val) */ switch (attr) { case hwmon_curr_input: - return ina2xx_read_init(dev, INA2XX_CURRENT, val); + /* + * Since the shunt voltage and the current register report the + * same values when the chip is calibrated, we can calculate + * the current directly from the shunt voltage without relying + * on chip calibration. + */ + ret = regmap_read(regmap, INA2XX_SHUNT_VOLTAGE, ®val); + if (ret) + return ret; + *val = ina2xx_get_value(data, INA2XX_CURRENT, regval); + return 0; case hwmon_curr_lcrit: return ina226_alert_limit_read(data, INA226_SHUNT_UNDER_VOLTAGE_MASK, INA2XX_CURRENT, val); From 21d9e6013134998334e162af905322d7da1e5314 Mon Sep 17 00:00:00 2001 From: Patrick Rudolph Date: Mon, 2 Sep 2024 09:53:12 +0200 Subject: [PATCH 88/96] hwmon: pmbus: Implement generic bus access delay Some drivers, like the max15301 or zl6100, are intentionally delaying SMBus communications, to prevent transmission errors. As this is necessary on additional PMBus compatible devices, implement a generic delay mechanism in the pmbus core. Introduces two delay settings in the pmbus_driver_info struct, one applies to every SMBus transaction and the other is for write transaction only. Once set by the driver the SMBus traffic, using the generic pmbus access helpers, is automatically delayed when necessary. The two settings are: access_delay: - Unit in microseconds - Stores the accessed timestamp after every SMBus access - Delays when necessary before the next SMBus access write_delay: - Unit in microseconds - Stores the written timestamp after a write SMBus access - Delays when necessary before the next SMBus access This allows to drop the custom delay code from the drivers and easily introduce this feature in additional pmbus drivers. Signed-off-by: Patrick Rudolph Message-ID: <20240902075319.585656-1-patrick.rudolph@9elements.com> Signed-off-by: Guenter Roeck --- drivers/hwmon/pmbus/pmbus.h | 10 ++++ drivers/hwmon/pmbus/pmbus_core.c | 92 +++++++++++++++++++++++++++++--- 2 files changed, 96 insertions(+), 6 deletions(-) diff --git a/drivers/hwmon/pmbus/pmbus.h b/drivers/hwmon/pmbus/pmbus.h index fb442fae7b3e..5d5dc774187b 100644 --- a/drivers/hwmon/pmbus/pmbus.h +++ b/drivers/hwmon/pmbus/pmbus.h @@ -466,6 +466,16 @@ struct pmbus_driver_info { /* custom attributes */ const struct attribute_group **groups; + + /* + * Some chips need a little delay between SMBus communication. When + * set, the generic PMBus helper functions will wait if necessary + * to meet this requirement. The access delay is honored after + * every SMBus operation. The write delay is only honored after + * SMBus write operations. + */ + int access_delay; /* in microseconds */ + int write_delay; /* in microseconds */ }; /* Regulator ops */ diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c index cb4c65a7f288..0ea6fe7eb17c 100644 --- a/drivers/hwmon/pmbus/pmbus_core.c +++ b/drivers/hwmon/pmbus/pmbus_core.c @@ -7,6 +7,7 @@ */ #include +#include #include #include #include @@ -108,6 +109,8 @@ struct pmbus_data { int vout_low[PMBUS_PAGES]; /* voltage low margin */ int vout_high[PMBUS_PAGES]; /* voltage high margin */ + ktime_t write_time; /* Last SMBUS write timestamp */ + ktime_t access_time; /* Last SMBUS access timestamp */ }; struct pmbus_debugfs_entry { @@ -158,6 +161,39 @@ void pmbus_set_update(struct i2c_client *client, u8 reg, bool update) } EXPORT_SYMBOL_NS_GPL(pmbus_set_update, PMBUS); +/* Some chips need a delay between accesses. */ +static void pmbus_wait(struct i2c_client *client) +{ + struct pmbus_data *data = i2c_get_clientdata(client); + const struct pmbus_driver_info *info = data->info; + s64 delta; + + if (info->access_delay) { + delta = ktime_us_delta(ktime_get(), data->access_time); + + if (delta < info->access_delay) + fsleep(info->access_delay - delta); + } else if (info->write_delay) { + delta = ktime_us_delta(ktime_get(), data->write_time); + + if (delta < info->write_delay) + fsleep(info->write_delay - delta); + } +} + +/* Sets the last accessed timestamp for pmbus_wait */ +static void pmbus_update_ts(struct i2c_client *client, bool write_op) +{ + struct pmbus_data *data = i2c_get_clientdata(client); + const struct pmbus_driver_info *info = data->info; + + if (info->access_delay) { + data->access_time = ktime_get(); + } else if (info->write_delay && write_op) { + data->write_time = ktime_get(); + } +} + int pmbus_set_page(struct i2c_client *client, int page, int phase) { struct pmbus_data *data = i2c_get_clientdata(client); @@ -168,11 +204,15 @@ int pmbus_set_page(struct i2c_client *client, int page, int phase) if (!(data->info->func[page] & PMBUS_PAGE_VIRTUAL) && data->info->pages > 1 && page != data->currpage) { + pmbus_wait(client); rv = i2c_smbus_write_byte_data(client, PMBUS_PAGE, page); + pmbus_update_ts(client, true); if (rv < 0) return rv; + pmbus_wait(client); rv = i2c_smbus_read_byte_data(client, PMBUS_PAGE); + pmbus_update_ts(client, false); if (rv < 0) return rv; @@ -183,8 +223,10 @@ int pmbus_set_page(struct i2c_client *client, int page, int phase) if (data->info->phases[page] && data->currphase != phase && !(data->info->func[page] & PMBUS_PHASE_VIRTUAL)) { + pmbus_wait(client); rv = i2c_smbus_write_byte_data(client, PMBUS_PHASE, phase); + pmbus_update_ts(client, true); if (rv) return rv; } @@ -202,7 +244,11 @@ int pmbus_write_byte(struct i2c_client *client, int page, u8 value) if (rv < 0) return rv; - return i2c_smbus_write_byte(client, value); + pmbus_wait(client); + rv = i2c_smbus_write_byte(client, value); + pmbus_update_ts(client, true); + + return rv; } EXPORT_SYMBOL_NS_GPL(pmbus_write_byte, PMBUS); @@ -233,7 +279,11 @@ int pmbus_write_word_data(struct i2c_client *client, int page, u8 reg, if (rv < 0) return rv; - return i2c_smbus_write_word_data(client, reg, word); + pmbus_wait(client); + rv = i2c_smbus_write_word_data(client, reg, word); + pmbus_update_ts(client, true); + + return rv; } EXPORT_SYMBOL_NS_GPL(pmbus_write_word_data, PMBUS); @@ -351,7 +401,11 @@ int pmbus_read_word_data(struct i2c_client *client, int page, int phase, u8 reg) if (rv < 0) return rv; - return i2c_smbus_read_word_data(client, reg); + pmbus_wait(client); + rv = i2c_smbus_read_word_data(client, reg); + pmbus_update_ts(client, false); + + return rv; } EXPORT_SYMBOL_NS_GPL(pmbus_read_word_data, PMBUS); @@ -410,7 +464,11 @@ int pmbus_read_byte_data(struct i2c_client *client, int page, u8 reg) if (rv < 0) return rv; - return i2c_smbus_read_byte_data(client, reg); + pmbus_wait(client); + rv = i2c_smbus_read_byte_data(client, reg); + pmbus_update_ts(client, false); + + return rv; } EXPORT_SYMBOL_NS_GPL(pmbus_read_byte_data, PMBUS); @@ -422,7 +480,11 @@ int pmbus_write_byte_data(struct i2c_client *client, int page, u8 reg, u8 value) if (rv < 0) return rv; - return i2c_smbus_write_byte_data(client, reg, value); + pmbus_wait(client); + rv = i2c_smbus_write_byte_data(client, reg, value); + pmbus_update_ts(client, true); + + return rv; } EXPORT_SYMBOL_NS_GPL(pmbus_write_byte_data, PMBUS); @@ -454,7 +516,11 @@ static int pmbus_read_block_data(struct i2c_client *client, int page, u8 reg, if (rv < 0) return rv; - return i2c_smbus_read_block_data(client, reg, data_buf); + pmbus_wait(client); + rv = i2c_smbus_read_block_data(client, reg, data_buf); + pmbus_update_ts(client, false); + + return rv; } static struct pmbus_sensor *pmbus_find_sensor(struct pmbus_data *data, int page, @@ -2450,9 +2516,11 @@ static int pmbus_read_coefficients(struct i2c_client *client, data.block[1] = attr->reg; data.block[2] = 0x01; + pmbus_wait(client); rv = i2c_smbus_xfer(client->adapter, client->addr, client->flags, I2C_SMBUS_WRITE, PMBUS_COEFFICIENTS, I2C_SMBUS_BLOCK_PROC_CALL, &data); + pmbus_update_ts(client, true); if (rv < 0) return rv; @@ -2604,7 +2672,10 @@ static int pmbus_init_common(struct i2c_client *client, struct pmbus_data *data, /* Enable PEC if the controller and bus supports it */ if (!(data->flags & PMBUS_NO_CAPABILITY)) { + pmbus_wait(client); ret = i2c_smbus_read_byte_data(client, PMBUS_CAPABILITY); + pmbus_update_ts(client, false); + if (ret >= 0 && (ret & PB_CAPABILITY_ERROR_CHECK)) { if (i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_PEC)) client->flags |= I2C_CLIENT_PEC; @@ -2617,10 +2688,16 @@ static int pmbus_init_common(struct i2c_client *client, struct pmbus_data *data, * Bail out if both registers are not supported. */ data->read_status = pmbus_read_status_word; + pmbus_wait(client); ret = i2c_smbus_read_word_data(client, PMBUS_STATUS_WORD); + pmbus_update_ts(client, false); + if (ret < 0 || ret == 0xffff) { data->read_status = pmbus_read_status_byte; + pmbus_wait(client); ret = i2c_smbus_read_byte_data(client, PMBUS_STATUS_BYTE); + pmbus_update_ts(client, false); + if (ret < 0 || ret == 0xff) { dev_err(dev, "PMBus status register not found\n"); return -ENODEV; @@ -2635,7 +2712,10 @@ static int pmbus_init_common(struct i2c_client *client, struct pmbus_data *data, * limit registers need to be disabled. */ if (!(data->flags & PMBUS_NO_WRITE_PROTECT)) { + pmbus_wait(client); ret = i2c_smbus_read_byte_data(client, PMBUS_WRITE_PROTECT); + pmbus_update_ts(client, false); + if (ret > 0 && (ret & PB_WP_ANY)) data->flags |= PMBUS_WRITE_PROTECTED | PMBUS_SKIP_STATUS_CHECK; } From d83219e9fcbd2361ac15fe331480b2f74410cbef Mon Sep 17 00:00:00 2001 From: Patrick Rudolph Date: Mon, 2 Sep 2024 09:53:13 +0200 Subject: [PATCH 89/96] hwmon: pmbus: max15301: Use generic code Use the generic pmbus bus access delay. Signed-off-by: Patrick Rudolph Message-ID: <20240902075319.585656-2-patrick.rudolph@9elements.com> Signed-off-by: Guenter Roeck --- drivers/hwmon/pmbus/max15301.c | 92 +--------------------------------- 1 file changed, 1 insertion(+), 91 deletions(-) diff --git a/drivers/hwmon/pmbus/max15301.c b/drivers/hwmon/pmbus/max15301.c index 986404fe6a31..f5367a7bc0f5 100644 --- a/drivers/hwmon/pmbus/max15301.c +++ b/drivers/hwmon/pmbus/max15301.c @@ -31,8 +31,6 @@ MODULE_DEVICE_TABLE(i2c, max15301_id); struct max15301_data { int id; - ktime_t access; /* Chip access time */ - int delay; /* Delay between chip accesses in us */ struct pmbus_driver_info info; }; @@ -55,89 +53,6 @@ static struct max15301_data max15301_data = { } }; -/* This chip needs a delay between accesses */ -static inline void max15301_wait(const struct max15301_data *data) -{ - if (data->delay) { - s64 delta = ktime_us_delta(ktime_get(), data->access); - - if (delta < data->delay) - udelay(data->delay - delta); - } -} - -static int max15301_read_word_data(struct i2c_client *client, int page, - int phase, int reg) -{ - const struct pmbus_driver_info *info = pmbus_get_driver_info(client); - struct max15301_data *data = to_max15301_data(info); - int ret; - - if (page > 0) - return -ENXIO; - - if (reg >= PMBUS_VIRT_BASE) - return -ENXIO; - - max15301_wait(data); - ret = pmbus_read_word_data(client, page, phase, reg); - data->access = ktime_get(); - - return ret; -} - -static int max15301_read_byte_data(struct i2c_client *client, int page, int reg) -{ - const struct pmbus_driver_info *info = pmbus_get_driver_info(client); - struct max15301_data *data = to_max15301_data(info); - int ret; - - if (page > 0) - return -ENXIO; - - max15301_wait(data); - ret = pmbus_read_byte_data(client, page, reg); - data->access = ktime_get(); - - return ret; -} - -static int max15301_write_word_data(struct i2c_client *client, int page, int reg, - u16 word) -{ - const struct pmbus_driver_info *info = pmbus_get_driver_info(client); - struct max15301_data *data = to_max15301_data(info); - int ret; - - if (page > 0) - return -ENXIO; - - if (reg >= PMBUS_VIRT_BASE) - return -ENXIO; - - max15301_wait(data); - ret = pmbus_write_word_data(client, page, reg, word); - data->access = ktime_get(); - - return ret; -} - -static int max15301_write_byte(struct i2c_client *client, int page, u8 value) -{ - const struct pmbus_driver_info *info = pmbus_get_driver_info(client); - struct max15301_data *data = to_max15301_data(info); - int ret; - - if (page > 0) - return -ENXIO; - - max15301_wait(data); - ret = pmbus_write_byte(client, page, value); - data->access = ktime_get(); - - return ret; -} - static int max15301_probe(struct i2c_client *client) { int status; @@ -164,12 +79,7 @@ static int max15301_probe(struct i2c_client *client) return -ENODEV; } - max15301_data.delay = delay; - - info->read_byte_data = max15301_read_byte_data; - info->read_word_data = max15301_read_word_data; - info->write_byte = max15301_write_byte; - info->write_word_data = max15301_write_word_data; + info->access_delay = delay; return pmbus_do_probe(client, info); } From 106cfea5fa78eea671a6a7900da83238c4c124a5 Mon Sep 17 00:00:00 2001 From: Patrick Rudolph Date: Mon, 2 Sep 2024 09:53:14 +0200 Subject: [PATCH 90/96] hwmon: pmbus: ucd9000: Use generic code Use generic pmbus bus write access delay. Signed-off-by: Patrick Rudolph Message-ID: <20240902075319.585656-3-patrick.rudolph@9elements.com> Signed-off-by: Guenter Roeck --- drivers/hwmon/pmbus/ucd9000.c | 64 ++--------------------------------- 1 file changed, 2 insertions(+), 62 deletions(-) diff --git a/drivers/hwmon/pmbus/ucd9000.c b/drivers/hwmon/pmbus/ucd9000.c index d817c719b90b..5d3d1773bf52 100644 --- a/drivers/hwmon/pmbus/ucd9000.c +++ b/drivers/hwmon/pmbus/ucd9000.c @@ -67,7 +67,6 @@ struct ucd9000_data { struct gpio_chip gpio; #endif struct dentry *debugfs; - ktime_t write_time; }; #define to_ucd9000_data(_info) container_of(_info, struct ucd9000_data, info) @@ -86,63 +85,6 @@ struct ucd9000_debugfs_entry { */ #define UCD90320_WAIT_DELAY_US 500 -static inline void ucd90320_wait(const struct ucd9000_data *data) -{ - s64 delta = ktime_us_delta(ktime_get(), data->write_time); - - if (delta < UCD90320_WAIT_DELAY_US) - udelay(UCD90320_WAIT_DELAY_US - delta); -} - -static int ucd90320_read_word_data(struct i2c_client *client, int page, - int phase, int reg) -{ - const struct pmbus_driver_info *info = pmbus_get_driver_info(client); - struct ucd9000_data *data = to_ucd9000_data(info); - - if (reg >= PMBUS_VIRT_BASE) - return -ENXIO; - - ucd90320_wait(data); - return pmbus_read_word_data(client, page, phase, reg); -} - -static int ucd90320_read_byte_data(struct i2c_client *client, int page, int reg) -{ - const struct pmbus_driver_info *info = pmbus_get_driver_info(client); - struct ucd9000_data *data = to_ucd9000_data(info); - - ucd90320_wait(data); - return pmbus_read_byte_data(client, page, reg); -} - -static int ucd90320_write_word_data(struct i2c_client *client, int page, - int reg, u16 word) -{ - const struct pmbus_driver_info *info = pmbus_get_driver_info(client); - struct ucd9000_data *data = to_ucd9000_data(info); - int ret; - - ucd90320_wait(data); - ret = pmbus_write_word_data(client, page, reg, word); - data->write_time = ktime_get(); - - return ret; -} - -static int ucd90320_write_byte(struct i2c_client *client, int page, u8 value) -{ - const struct pmbus_driver_info *info = pmbus_get_driver_info(client); - struct ucd9000_data *data = to_ucd9000_data(info); - int ret; - - ucd90320_wait(data); - ret = pmbus_write_byte(client, page, value); - data->write_time = ktime_get(); - - return ret; -} - static int ucd9000_get_fan_config(struct i2c_client *client, int fan) { int fan_config = 0; @@ -667,10 +609,8 @@ static int ucd9000_probe(struct i2c_client *client) info->func[0] |= PMBUS_HAVE_FAN12 | PMBUS_HAVE_STATUS_FAN12 | PMBUS_HAVE_FAN34 | PMBUS_HAVE_STATUS_FAN34; } else if (mid->driver_data == ucd90320) { - info->read_byte_data = ucd90320_read_byte_data; - info->read_word_data = ucd90320_read_word_data; - info->write_byte = ucd90320_write_byte; - info->write_word_data = ucd90320_write_word_data; + /* Delay SMBus operations after a write */ + info->write_delay = UCD90320_WAIT_DELAY_US; } ucd9000_probe_gpio(client, mid, data); From 9c4e67320f39c793e4abccef6bd5d66d31e4ec0c Mon Sep 17 00:00:00 2001 From: Patrick Rudolph Date: Mon, 2 Sep 2024 09:53:15 +0200 Subject: [PATCH 91/96] hwmon: pmbus: zl6100: Use generic code Use generic pmbus bus access delay. Signed-off-by: Patrick Rudolph Message-ID: <20240902075319.585656-4-patrick.rudolph@9elements.com> Signed-off-by: Guenter Roeck --- drivers/hwmon/pmbus/zl6100.c | 66 +++++------------------------------- 1 file changed, 8 insertions(+), 58 deletions(-) diff --git a/drivers/hwmon/pmbus/zl6100.c b/drivers/hwmon/pmbus/zl6100.c index 83458df0d0cf..7920a16203e1 100644 --- a/drivers/hwmon/pmbus/zl6100.c +++ b/drivers/hwmon/pmbus/zl6100.c @@ -22,8 +22,6 @@ enum chips { zl2004, zl2005, zl2006, zl2008, zl2105, zl2106, zl6100, zl6105, struct zl6100_data { int id; - ktime_t access; /* chip access time */ - int delay; /* Delay between chip accesses in uS */ struct pmbus_driver_info info; }; @@ -122,16 +120,6 @@ static u16 zl6100_d2l(long val) return (mantissa & 0x7ff) | ((exponent << 11) & 0xf800); } -/* Some chips need a delay between accesses */ -static inline void zl6100_wait(const struct zl6100_data *data) -{ - if (data->delay) { - s64 delta = ktime_us_delta(ktime_get(), data->access); - if (delta < data->delay) - udelay(data->delay - delta); - } -} - static int zl6100_read_word_data(struct i2c_client *client, int page, int phase, int reg) { @@ -174,9 +162,7 @@ static int zl6100_read_word_data(struct i2c_client *client, int page, break; } - zl6100_wait(data); ret = pmbus_read_word_data(client, page, phase, vreg); - data->access = ktime_get(); if (ret < 0) return ret; @@ -195,14 +181,11 @@ static int zl6100_read_word_data(struct i2c_client *client, int page, static int zl6100_read_byte_data(struct i2c_client *client, int page, int reg) { const struct pmbus_driver_info *info = pmbus_get_driver_info(client); - struct zl6100_data *data = to_zl6100_data(info); int ret, status; if (page >= info->pages) return -ENXIO; - zl6100_wait(data); - switch (reg) { case PMBUS_VIRT_STATUS_VMON: ret = pmbus_read_byte_data(client, 0, @@ -225,7 +208,6 @@ static int zl6100_read_byte_data(struct i2c_client *client, int page, int reg) ret = pmbus_read_byte_data(client, page, reg); break; } - data->access = ktime_get(); return ret; } @@ -234,8 +216,7 @@ static int zl6100_write_word_data(struct i2c_client *client, int page, int reg, u16 word) { const struct pmbus_driver_info *info = pmbus_get_driver_info(client); - struct zl6100_data *data = to_zl6100_data(info); - int ret, vreg; + int vreg; if (page >= info->pages) return -ENXIO; @@ -265,27 +246,7 @@ static int zl6100_write_word_data(struct i2c_client *client, int page, int reg, vreg = reg; } - zl6100_wait(data); - ret = pmbus_write_word_data(client, page, vreg, word); - data->access = ktime_get(); - - return ret; -} - -static int zl6100_write_byte(struct i2c_client *client, int page, u8 value) -{ - const struct pmbus_driver_info *info = pmbus_get_driver_info(client); - struct zl6100_data *data = to_zl6100_data(info); - int ret; - - if (page >= info->pages) - return -ENXIO; - - zl6100_wait(data); - ret = pmbus_write_byte(client, page, value); - data->access = ktime_get(); - - return ret; + return pmbus_write_word_data(client, page, vreg, word); } static const struct i2c_device_id zl6100_id[] = { @@ -363,14 +324,7 @@ static int zl6100_probe(struct i2c_client *client) * supported chips are known to require a wait time between I2C * accesses. */ - data->delay = delay; - - /* - * Since there was a direct I2C device access above, wait before - * accessing the chip again. - */ - data->access = ktime_get(); - zl6100_wait(data); + udelay(delay); info = &data->info; @@ -404,8 +358,7 @@ static int zl6100_probe(struct i2c_client *client) if (ret < 0) return ret; - data->access = ktime_get(); - zl6100_wait(data); + udelay(delay); if (ret & ZL8802_MFR_PHASES_MASK) info->func[1] |= PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT; @@ -418,8 +371,7 @@ static int zl6100_probe(struct i2c_client *client) if (ret < 0) return ret; - data->access = ktime_get(); - zl6100_wait(data); + udelay(delay); ret = i2c_smbus_read_word_data(client, ZL8802_MFR_USER_CONFIG); if (ret < 0) @@ -428,8 +380,7 @@ static int zl6100_probe(struct i2c_client *client) if (ret & ZL8802_MFR_XTEMP_ENABLE_2) info->func[i] |= PMBUS_HAVE_TEMP2; - data->access = ktime_get(); - zl6100_wait(data); + udelay(delay); } ret = i2c_smbus_read_word_data(client, ZL8802_MFR_USER_GLOBAL_CONFIG); if (ret < 0) @@ -446,13 +397,12 @@ static int zl6100_probe(struct i2c_client *client) info->func[0] |= PMBUS_HAVE_TEMP2; } - data->access = ktime_get(); - zl6100_wait(data); + udelay(delay); + info->access_delay = delay; info->read_word_data = zl6100_read_word_data; info->read_byte_data = zl6100_read_byte_data; info->write_word_data = zl6100_write_word_data; - info->write_byte = zl6100_write_byte; return pmbus_do_probe(client, info); } From 3cd1ef26b01ea66f1f1c1f088fb41cf1476cb017 Mon Sep 17 00:00:00 2001 From: Patrick Rudolph Date: Mon, 2 Sep 2024 09:53:16 +0200 Subject: [PATCH 92/96] hwmon: pmbus: pli12096bc: Add write delay Tests on PLI12096bc showed that sometimes a small delay is necessary after a write operation before a new operation can be processed. If not respected the device will probably NACK the data phase of the SMBus transaction. Tests showed that the probability to observe transaction errors can be raised by either reading sensor data or toggling the regulator enable. Further tests showed that 250 microseconds, as used previously for the CLEAR_FAULTS workaround, is sufficient. Signed-off-by: Patrick Rudolph Message-ID: <20240902075319.585656-5-patrick.rudolph@9elements.com> Signed-off-by: Guenter Roeck --- drivers/hwmon/pmbus/pli1209bc.c | 26 +------------------------- 1 file changed, 1 insertion(+), 25 deletions(-) diff --git a/drivers/hwmon/pmbus/pli1209bc.c b/drivers/hwmon/pmbus/pli1209bc.c index 2c6c9ec2a652..178e0cdb7887 100644 --- a/drivers/hwmon/pmbus/pli1209bc.c +++ b/drivers/hwmon/pmbus/pli1209bc.c @@ -54,30 +54,6 @@ static int pli1209bc_read_word_data(struct i2c_client *client, int page, } } -static int pli1209bc_write_byte(struct i2c_client *client, int page, u8 reg) -{ - int ret; - - switch (reg) { - case PMBUS_CLEAR_FAULTS: - ret = pmbus_write_byte(client, page, reg); - /* - * PLI1209 takes 230 usec to execute the CLEAR_FAULTS command. - * During that time it's busy and NACKs all requests on the - * SMBUS interface. It also NACKs reads on PMBUS_STATUS_BYTE - * making it impossible to poll the BUSY flag. - * - * Just wait for not BUSY unconditionally. - */ - usleep_range(250, 300); - break; - default: - ret = -ENODATA; - break; - } - return ret; -} - #if IS_ENABLED(CONFIG_SENSORS_PLI1209BC_REGULATOR) static const struct regulator_desc pli1209bc_reg_desc = { .name = "vout2", @@ -127,7 +103,7 @@ static struct pmbus_driver_info pli1209bc_info = { | PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP | PMBUS_HAVE_STATUS_IOUT | PMBUS_HAVE_STATUS_INPUT, .read_word_data = pli1209bc_read_word_data, - .write_byte = pli1209bc_write_byte, + .write_delay = 250, #if IS_ENABLED(CONFIG_SENSORS_PLI1209BC_REGULATOR) .num_regulators = 1, .reg_desc = &pli1209bc_reg_desc, From d22bd451d5606411895ef55cb105277e4f4f6e54 Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Sun, 8 Sep 2024 10:32:38 +0200 Subject: [PATCH 93/96] hwmon: (pmbus/mpq7932) Constify struct regulator_desc 'struct regulator_desc' is not modified in this driver. Constifying this structure moves some data to a read-only section, so increase overall security, especially when the structure holds some function pointers. This also makes mpq7932_regulators_desc consistent with mpq7932_regulators_desc_one which is already a "static const struct regulator_desc". On a x86_64, with allmodconfig: Before: ====== text data bss dec hex filename 3516 2264 0 5780 1694 drivers/hwmon/pmbus/mpq7932.o After: ===== text data bss dec hex filename 5396 384 0 5780 1694 drivers/hwmon/pmbus/mpq7932.o Signed-off-by: Christophe JAILLET Message-ID: Signed-off-by: Guenter Roeck --- drivers/hwmon/pmbus/mpq7932.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hwmon/pmbus/mpq7932.c b/drivers/hwmon/pmbus/mpq7932.c index 67487867c70f..2dcb6da853bd 100644 --- a/drivers/hwmon/pmbus/mpq7932.c +++ b/drivers/hwmon/pmbus/mpq7932.c @@ -35,7 +35,7 @@ struct mpq7932_data { }; #if IS_ENABLED(CONFIG_SENSORS_MPQ7932_REGULATOR) -static struct regulator_desc mpq7932_regulators_desc[] = { +static const struct regulator_desc mpq7932_regulators_desc[] = { PMBUS_REGULATOR_STEP("buck", 0, MPQ7932_N_VOLTAGES, MPQ7932_UV_STEP, MPQ7932_BUCK_UV_MIN), PMBUS_REGULATOR_STEP("buck", 1, MPQ7932_N_VOLTAGES, From 38f9fa39afe119f6b2771c9065e638414226c86d Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 11 Sep 2024 22:46:27 +0300 Subject: [PATCH 94/96] hwmon: (sht21) Use %*ph to print small buffer Use %*ph format to print small buffer as hex string. Signed-off-by: Andy Shevchenko Message-ID: <20240911194627.2885506-1-andriy.shevchenko@linux.intel.com> Signed-off-by: Guenter Roeck --- drivers/hwmon/sht21.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/hwmon/sht21.c b/drivers/hwmon/sht21.c index ad1b827ea782..97327313529b 100644 --- a/drivers/hwmon/sht21.c +++ b/drivers/hwmon/sht21.c @@ -199,10 +199,7 @@ static ssize_t eic_read(struct sht21 *sht21) eic[6] = rx[0]; eic[7] = rx[1]; - ret = snprintf(sht21->eic, sizeof(sht21->eic), - "%02x%02x%02x%02x%02x%02x%02x%02x\n", - eic[0], eic[1], eic[2], eic[3], - eic[4], eic[5], eic[6], eic[7]); + ret = snprintf(sht21->eic, sizeof(sht21->eic), "%8phN\n", eic); out: if (ret < 0) sht21->eic[0] = 0; From 3017d28d6c0fe995703a1f34275e1cade424dc35 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 11 Sep 2024 23:19:03 +0300 Subject: [PATCH 95/96] hwmon: (sch5636) Print unknown ID in error string via %*pE Instead of custom approach this allows to print escaped strings via %*pE extension. With this the unknown ID will be printed as a string. Nonetheless, leave hex values to be printed as well. Signed-off-by: Andy Shevchenko Message-ID: <20240911201903.2886874-1-andriy.shevchenko@linux.intel.com> Signed-off-by: Guenter Roeck --- drivers/hwmon/sch5636.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/hwmon/sch5636.c b/drivers/hwmon/sch5636.c index 6e6d54158474..a4b05ebb0546 100644 --- a/drivers/hwmon/sch5636.c +++ b/drivers/hwmon/sch5636.c @@ -416,8 +416,7 @@ static int sch5636_probe(struct platform_device *pdev) id[i] = '\0'; if (strcmp(id, "THS")) { - pr_err("Unknown Fujitsu id: %02x%02x%02x\n", - id[0], id[1], id[2]); + pr_err("Unknown Fujitsu id: %3pE (%3ph)\n", id, id); err = -ENODEV; goto error; } From 2cb4acf2140be8a4f299c0b887cc314845ef6ec8 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Fri, 13 Sep 2024 07:11:42 -0700 Subject: [PATCH 96/96] hwmon: Remove devm_hwmon_device_unregister() API function devm_hwmon_device_unregister() has no in-tree user, and its implementation is wrong since it does not pass the to-be-removed hardware monitoring device as parameter. I do not envision a valid use for it; drivers needing it should not have called devm_hwmon_device_register_with_info() in the first place. Remove it. Reported-by: Matthew Sanders Closes: https://lore.kernel.org/linux-hwmon/488b3bdf870ea76c4b943dbe5fd15ac8113019dc.camel@kernel.org/ Signed-off-by: Guenter Roeck --- Documentation/hwmon/hwmon-kernel-api.rst | 7 ------- drivers/hwmon/hwmon.c | 18 ------------------ include/linux/hwmon.h | 1 - 3 files changed, 26 deletions(-) diff --git a/Documentation/hwmon/hwmon-kernel-api.rst b/Documentation/hwmon/hwmon-kernel-api.rst index 6cacf7daf25c..8297acfa3a2d 100644 --- a/Documentation/hwmon/hwmon-kernel-api.rst +++ b/Documentation/hwmon/hwmon-kernel-api.rst @@ -38,8 +38,6 @@ register/unregister functions:: void hwmon_device_unregister(struct device *dev); - void devm_hwmon_device_unregister(struct device *dev); - char *hwmon_sanitize_name(const char *name); char *devm_hwmon_sanitize_name(struct device *dev, const char *name); @@ -64,11 +62,6 @@ monitoring device structure. This function must be called from the driver remove function if the hardware monitoring device was registered with hwmon_device_register_with_info. -devm_hwmon_device_unregister does not normally have to be called. It is only -needed for error handling, and only needed if the driver probe fails after -the call to devm_hwmon_device_register_with_info and if the automatic (device -managed) removal would be too late. - All supported hwmon device registration functions only accept valid device names. Device names including invalid characters (whitespace, '*', or '-') will be rejected. The 'name' parameter is mandatory. diff --git a/drivers/hwmon/hwmon.c b/drivers/hwmon/hwmon.c index a362080d41fa..9c35c4d0369d 100644 --- a/drivers/hwmon/hwmon.c +++ b/drivers/hwmon/hwmon.c @@ -1188,24 +1188,6 @@ error: } EXPORT_SYMBOL_GPL(devm_hwmon_device_register_with_info); -static int devm_hwmon_match(struct device *dev, void *res, void *data) -{ - struct device **hwdev = res; - - return *hwdev == data; -} - -/** - * devm_hwmon_device_unregister - removes a previously registered hwmon device - * - * @dev: the parent device of the device to unregister - */ -void devm_hwmon_device_unregister(struct device *dev) -{ - WARN_ON(devres_release(dev, devm_hwmon_release, devm_hwmon_match, dev)); -} -EXPORT_SYMBOL_GPL(devm_hwmon_device_unregister); - static char *__hwmon_sanitize_name(struct device *dev, const char *old_name) { char *name, *p; diff --git a/include/linux/hwmon.h b/include/linux/hwmon.h index e94314760aab..5c6a421ad580 100644 --- a/include/linux/hwmon.h +++ b/include/linux/hwmon.h @@ -481,7 +481,6 @@ devm_hwmon_device_register_with_info(struct device *dev, const struct attribute_group **extra_groups); void hwmon_device_unregister(struct device *dev); -void devm_hwmon_device_unregister(struct device *dev); int hwmon_notify_event(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel);