Merge tag 'hwmon-for-v7.2-rc7' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging

Pull hwmon fixes from Guenter Roeck:
 "Various fixes, most of them fixing critical or high severity bugs
  reported by Sashiko.

  ads7828:
   - Fix external VREF regulator handling

  corsair-psu:
   - Fix linear11 calculation
   - Serialize debugfs access against hwmon
   - Fix possible out-of-bounds access on missing string termination

  ltc4282:
   - Fix parsing adi,current-limit-sense-microvolt
   - Clamp negative current limits
   - Avoid overflow in maximum power calculation

  nzxt-smart2:
   - Check return value of init_device() in probe

  PMBus core:
   - Fix type confusion in notification logic
   - Avoid race condition during probe

  PMBus/lm25066:
   - Fix PMBus coefficient calculations"

* tag 'hwmon-for-v7.2-rc7' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging:
  hwmon: (corsair-psu) Fix linear11 calculation
  hwmon: (corsair-psu) serialize debugfs access against hwmon
  hwmon: (ltc4282) Fix parsing adi,current-limit-sense-microvolt
  hwmon: (ltc4282) Clamp negative current limits
  hwmon: (ltc4282) Avoid overflow in maximum power calculation
  hwmon: (ads7828) Fix external VREF regulator handling
  hwmon: (corsair-psu) fix possible out-of-bounds access on missing string termination
  hwmon: (pmbus/lm25066) Fix PMBus coefficient calculations
  hwmon: (nzxt-smart2) Check return value of init_device() in probe
  hwmon: (pmbus) Fix type confusion in notification logic
  hwmon: (pmbus/core) Avoid race condition during probe
This commit is contained in:
Linus Torvalds
2026-08-07 17:14:05 -07:00
6 changed files with 87 additions and 51 deletions

View File

@@ -106,12 +106,11 @@ static int ads7828_probe(struct i2c_client *client)
struct ads7828_data *data;
struct device *hwmon_dev;
unsigned int vref_mv = ADS7828_INT_VREF_MV;
unsigned int vref_uv;
int vref_uv;
bool diff_input = false;
bool ext_vref = false;
unsigned int regval;
enum ads7828_chips chip;
struct regulator *reg;
data = devm_kzalloc(dev, sizeof(struct ads7828_data), GFP_KERNEL);
if (!data)
@@ -125,9 +124,11 @@ static int ads7828_probe(struct i2c_client *client)
} else if (dev->of_node) {
diff_input = of_property_read_bool(dev->of_node,
"ti,differential-input");
reg = devm_regulator_get_optional(dev, "vref");
if (!IS_ERR(reg)) {
vref_uv = regulator_get_voltage(reg);
vref_uv = devm_regulator_get_enable_read_voltage(dev, "vref");
if (vref_uv < 0) {
if (vref_uv != -ENODEV)
return vref_uv;
} else {
vref_mv = DIV_ROUND_CLOSEST(vref_uv, 1000);
if (vref_mv < ADS7828_EXT_VREF_MV_MIN ||
vref_mv > ADS7828_EXT_VREF_MV_MAX)

View File

@@ -137,13 +137,18 @@ struct corsairpsu_data {
};
/* some values are SMBus LINEAR11 data which need a conversion */
static int corsairpsu_linear11_to_int(const u16 val, const int scale)
static long corsairpsu_linear11_to_long(const u16 val, const int scale)
{
const int exp = ((s16)val) >> 11;
const int mant = (((s16)(val & 0x7ff)) << 5) >> 5;
const int result = mant * scale;
const int mant = ((s16)((val & 0x7ff) << 5)) >> 5;
s64 result = mant * scale;
return (exp >= 0) ? (result << exp) : (result >> -exp);
if (exp >= 0)
result *= (int)(1UL << exp);
else
result >>= -exp;
return clamp(result, LONG_MIN, LONG_MAX);
}
/* the micro-controller uses percentage values to control pwm */
@@ -263,13 +268,13 @@ static int corsairpsu_get_value(struct corsairpsu_data *priv, u8 cmd, u8 rail, l
case PSU_CMD_RAIL_AMPS:
case PSU_CMD_TEMP0:
case PSU_CMD_TEMP1:
*val = corsairpsu_linear11_to_int(tmp & 0xFFFF, 1000);
*val = corsairpsu_linear11_to_long(tmp & 0xFFFF, 1000);
break;
case PSU_CMD_FAN:
*val = corsairpsu_linear11_to_int(tmp & 0xFFFF, 1);
*val = corsairpsu_linear11_to_long(tmp & 0xFFFF, 1);
break;
case PSU_CMD_FAN_PWM_ENABLE:
*val = corsairpsu_linear11_to_int(tmp & 0xFFFF, 1);
*val = corsairpsu_linear11_to_long(tmp & 0xFFFF, 1);
/*
* 0 = automatic mode, means the micro-controller controls the fan using a plan
* which can be modified, but changing this plan is not supported by this
@@ -283,12 +288,12 @@ static int corsairpsu_get_value(struct corsairpsu_data *priv, u8 cmd, u8 rail, l
*val = 2;
break;
case PSU_CMD_FAN_PWM:
*val = corsairpsu_linear11_to_int(tmp & 0xFFFF, 1);
*val = corsairpsu_linear11_to_long(tmp & 0xFFFF, 1);
*val = corsairpsu_dutycycle_to_pwm(*val);
break;
case PSU_CMD_RAIL_WATTS:
case PSU_CMD_TOTAL_WATTS:
*val = corsairpsu_linear11_to_int(tmp & 0xFFFF, 1000000);
*val = corsairpsu_linear11_to_long(tmp & 0xFFFF, 1000000);
break;
case PSU_CMD_TOTAL_UPTIME:
case PSU_CMD_UPTIME:
@@ -664,6 +669,8 @@ static void print_uptime(struct seq_file *seqf, u8 cmd)
long val;
int ret;
guard(hwmon_lock)(priv->hwmon_dev);
ret = corsairpsu_get_value(priv, cmd, 0, &val);
if (ret < 0) {
seq_puts(seqf, "N/A\n");
@@ -701,7 +708,7 @@ static int vendor_show(struct seq_file *seqf, void *unused)
{
struct corsairpsu_data *priv = seqf->private;
seq_printf(seqf, "%s\n", priv->vendor);
seq_printf(seqf, "%.*s\n", REPLY_SIZE, priv->vendor);
return 0;
}
@@ -711,7 +718,7 @@ static int product_show(struct seq_file *seqf, void *unused)
{
struct corsairpsu_data *priv = seqf->private;
seq_printf(seqf, "%s\n", priv->product);
seq_printf(seqf, "%.*s\n", REPLY_SIZE, priv->product);
return 0;
}
@@ -723,6 +730,8 @@ static int ocpmode_show(struct seq_file *seqf, void *unused)
long val;
int ret;
guard(hwmon_lock)(priv->hwmon_dev);
/*
* The rail mode is switchable on the fly. The RAW interface can be used for this. But it
* will not be included here, because I consider it somewhat dangerous for the health of the

View File

@@ -14,6 +14,7 @@
#include <linux/hwmon.h>
#include <linux/i2c.h>
#include <linux/math.h>
#include <linux/math64.h>
#include <linux/minmax.h>
#include <linux/module.h>
#include <linux/regmap.h>
@@ -137,7 +138,7 @@ struct ltc4282_state {
*/
struct ltc4282_cache in0_1_cache[LTC4282_CHAN_VGPIO];
u32 vsense_max;
long power_max;
s64 power_max;
u32 rsense;
u16 vdd;
u16 vfs_out;
@@ -613,13 +614,12 @@ static int ltc4282_read(struct device *dev, enum hwmon_sensor_types type,
}
static int ltc4282_write_power_byte(const struct ltc4282_state *st, u32 reg,
long val)
s64 val)
{
u32 power;
u64 temp;
if (val > st->power_max)
val = st->power_max;
val = clamp(val, 0, st->power_max);
temp = val * int_pow(U8_MAX, 2) * st->rsense;
power = DIV64_U64_ROUND_CLOSEST(temp,
@@ -629,7 +629,7 @@ static int ltc4282_write_power_byte(const struct ltc4282_state *st, u32 reg,
}
static int ltc4282_write_power_word(const struct ltc4282_state *st, u32 reg,
long val)
u64 val)
{
u64 temp = int_pow(U16_MAX, 2) * st->rsense, temp_2;
__be16 __raw;
@@ -930,8 +930,11 @@ static int ltc4282_curr_reset_hist(struct ltc4282_state *st)
static int ltc4282_write_curr(struct ltc4282_state *st, u32 attr,
long val)
{
s32 ulimit = min_t(u64, INT_MAX,
div_u64((u64)INT_MAX * DECA * MICRO, st->rsense));
u64 val64 = clamp(val, 0, ulimit);
/* need to pass it in millivolt */
u32 in = DIV_ROUND_CLOSEST_ULL((u64)val * st->rsense, DECA * MICRO);
u32 in = DIV_ROUND_CLOSEST_ULL(val64 * st->rsense, DECA * MICRO);
switch (attr) {
case hwmon_curr_max:
@@ -1222,7 +1225,8 @@ static int ltc4282_set_max_limits(struct ltc4282_state *st)
return ret;
/* Power is given by ISENSE * Vout. */
st->power_max = DIV_ROUND_CLOSEST(st->vsense_max * DECA * MILLI, st->rsense) * st->vfs_out;
st->power_max = DIV_ROUND_CLOSEST_ULL((u64)st->vsense_max * DECA * MILLI,
st->rsense) * st->vfs_out;
ret = ltc4282_write_power_byte(st, LTC4282_POWER_MAX, st->power_max);
if (ret)
return ret;
@@ -1390,7 +1394,7 @@ static int ltc4282_setup(struct ltc4282_state *st, struct device *dev)
if (!ret) {
int reg_val;
switch (val) {
switch (st->vsense_max) {
case 12500:
reg_val = 0;
break;

View File

@@ -754,7 +754,11 @@ static int nzxt_smart2_hid_probe(struct hid_device *hdev,
hid_device_io_start(hdev);
init_device(drvdata, UPDATE_INTERVAL_DEFAULT_MS);
ret = init_device(drvdata, UPDATE_INTERVAL_DEFAULT_MS);
if (ret) {
dev_err(&hdev->dev, "init_device failed: %d\n", ret);
goto out_hw_close;
}
drvdata->hwmon =
hwmon_device_register_with_info(&hdev->dev, "nzxtsmart2", drvdata,

View File

@@ -14,6 +14,7 @@
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/log2.h>
#include <linux/math.h>
#include <linux/of.h>
#include "pmbus.h"
@@ -542,8 +543,8 @@ static int lm25066_probe(struct i2c_client *client)
if (of_property_read_u32(client->dev.of_node, "shunt-resistor-micro-ohms", &shunt))
shunt = 1000;
info->m[PSC_CURRENT_IN] = info->m[PSC_CURRENT_IN] * shunt / 1000;
info->m[PSC_POWER] = info->m[PSC_POWER] * shunt / 1000;
info->m[PSC_CURRENT_IN] = DIV_ROUND_CLOSEST_ULL((u64)info->m[PSC_CURRENT_IN] * shunt, 1000);
info->m[PSC_POWER] = DIV_ROUND_CLOSEST_ULL((u64)info->m[PSC_POWER] * shunt, 1000);
#if IS_ENABLED(CONFIG_SENSORS_LM25066_REGULATOR)
/* LM25056 doesn't support OPERATION */

View File

@@ -45,7 +45,7 @@ module_param(wp, int, 0444);
struct pmbus_sensor {
struct pmbus_sensor *next;
char name[PMBUS_NAME_SIZE]; /* sysfs sensor name */
struct device_attribute attribute;
struct sensor_device_attribute attribute;
u8 page; /* page number */
u8 phase; /* phase number, 0xff for all phases */
u16 reg; /* register */
@@ -68,7 +68,7 @@ struct pmbus_boolean {
struct pmbus_label {
char name[PMBUS_NAME_SIZE]; /* sysfs label name */
struct device_attribute attribute;
struct sensor_device_attribute attribute;
char label[PMBUS_NAME_SIZE]; /* label */
};
#define to_pmbus_label(_attr) \
@@ -1241,7 +1241,8 @@ static ssize_t pmbus_show_sensor(struct device *dev,
struct device_attribute *devattr, char *buf)
{
struct i2c_client *client = to_i2c_client(dev->parent);
struct pmbus_sensor *sensor = to_pmbus_sensor(devattr);
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct pmbus_sensor *sensor = to_pmbus_sensor(attr);
struct pmbus_data *data = i2c_get_clientdata(client);
s64 val;
@@ -1261,7 +1262,8 @@ static ssize_t pmbus_set_sensor(struct device *dev,
{
struct i2c_client *client = to_i2c_client(dev->parent);
struct pmbus_data *data = i2c_get_clientdata(client);
struct pmbus_sensor *sensor = to_pmbus_sensor(devattr);
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct pmbus_sensor *sensor = to_pmbus_sensor(attr);
s64 val;
int ret;
u16 regval;
@@ -1283,7 +1285,8 @@ static ssize_t pmbus_set_sensor(struct device *dev,
static ssize_t pmbus_show_label(struct device *dev,
struct device_attribute *da, char *buf)
{
struct pmbus_label *label = to_pmbus_label(da);
struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
struct pmbus_label *label = to_pmbus_label(attr);
return sysfs_emit(buf, "%s\n", label->label);
}
@@ -1436,8 +1439,8 @@ static struct pmbus_sensor *pmbus_add_sensor(struct pmbus_data *data,
bool update, bool readonly,
bool writeonly, bool convert)
{
struct sensor_device_attribute *a;
struct pmbus_sensor *sensor;
struct device_attribute *a;
sensor = devm_kzalloc(data->dev, sizeof(*sensor), GFP_KERNEL);
if (!sensor)
@@ -1461,12 +1464,11 @@ static struct pmbus_sensor *pmbus_add_sensor(struct pmbus_data *data,
sensor->update = update;
sensor->convert = convert;
sensor->data = -ENODATA;
pmbus_dev_attr_init(a, sensor->name,
readonly ? 0444 : 0644,
writeonly ? pmbus_show_zero : pmbus_show_sensor,
pmbus_set_sensor);
pmbus_attr_init(a, sensor->name, readonly ? 0444 : 0644,
writeonly ? pmbus_show_zero : pmbus_show_sensor,
pmbus_set_sensor, -1);
if (pmbus_add_attribute(data, &a->attr))
if (pmbus_add_attribute(data, &a->dev_attr.attr))
return NULL;
sensor->next = data->sensors;
@@ -1483,8 +1485,8 @@ static int pmbus_add_label(struct pmbus_data *data,
const char *name, int seq,
const char *lstring, int index, int phase)
{
struct sensor_device_attribute *a;
struct pmbus_label *label;
struct device_attribute *a;
label = devm_kzalloc(data->dev, sizeof(*label), GFP_KERNEL);
if (!label)
@@ -1508,8 +1510,8 @@ static int pmbus_add_label(struct pmbus_data *data,
lstring, index, phase);
}
pmbus_dev_attr_init(a, label->name, 0444, pmbus_show_label, NULL);
return pmbus_add_attribute(data, &a->attr);
pmbus_attr_init(a, label->name, 0444, pmbus_show_label, NULL, -1);
return pmbus_add_attribute(data, &a->dev_attr.attr);
}
/*
@@ -2397,7 +2399,7 @@ struct pmbus_samples_attr {
struct pmbus_samples_reg {
int page;
struct pmbus_samples_attr *attr;
struct device_attribute dev_attr;
struct sensor_device_attribute attribute;
};
static struct pmbus_samples_attr pmbus_samples_registers[] = {
@@ -2419,14 +2421,15 @@ static struct pmbus_samples_attr pmbus_samples_registers[] = {
}
};
#define to_samples_reg(x) container_of(x, struct pmbus_samples_reg, dev_attr)
#define to_samples_reg(x) container_of(x, struct pmbus_samples_reg, attribute)
static ssize_t pmbus_show_samples(struct device *dev,
struct device_attribute *devattr, char *buf)
{
int val;
struct i2c_client *client = to_i2c_client(dev->parent);
struct pmbus_samples_reg *reg = to_samples_reg(devattr);
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct pmbus_samples_reg *reg = to_samples_reg(attr);
scoped_guard(pmbus_lock, client) {
val = _pmbus_read_word_data(client, reg->page, 0xff, reg->attr->reg);
@@ -2444,7 +2447,8 @@ static ssize_t pmbus_set_samples(struct device *dev,
int ret;
long val;
struct i2c_client *client = to_i2c_client(dev->parent);
struct pmbus_samples_reg *reg = to_samples_reg(devattr);
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct pmbus_samples_reg *reg = to_samples_reg(attr);
if (kstrtol(buf, 0, &val) < 0)
return -EINVAL;
@@ -2459,6 +2463,7 @@ static ssize_t pmbus_set_samples(struct device *dev,
static int pmbus_add_samples_attr(struct pmbus_data *data, int page,
struct pmbus_samples_attr *attr)
{
struct sensor_device_attribute *a;
struct pmbus_samples_reg *reg;
reg = devm_kzalloc(data->dev, sizeof(*reg), GFP_KERNEL);
@@ -2468,10 +2473,12 @@ static int pmbus_add_samples_attr(struct pmbus_data *data, int page,
reg->attr = attr;
reg->page = page;
pmbus_dev_attr_init(&reg->dev_attr, attr->name, 0644,
pmbus_show_samples, pmbus_set_samples);
a = &reg->attribute;
return pmbus_add_attribute(data, &reg->dev_attr.attr);
pmbus_attr_init(a, attr->name, 0644,
pmbus_show_samples, pmbus_set_samples, -1);
return pmbus_add_attribute(data, &a->dev_attr.attr);
}
static int pmbus_add_samples_attributes(struct i2c_client *client,
@@ -2979,9 +2986,15 @@ static void pmbus_notify(struct pmbus_data *data, int page, int reg, int flags)
struct device_attribute *da = to_dev_attr(data->group.attrs[i]);
struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
int index = attr->index;
u16 smask = pb_index_to_mask(index);
u8 spage = pb_index_to_page(index);
u16 sreg = pb_index_to_reg(index);
u16 smask, sreg;
u8 spage;
if (index == -1)
continue;
smask = pb_index_to_mask(index);
spage = pb_index_to_page(index);
sreg = pb_index_to_reg(index);
if (reg == sreg && page == spage && (smask & flags)) {
dev_dbg(data->dev, "sysfs notify: %s", da->attr.name);
@@ -3427,6 +3440,8 @@ static int pmbus_write_smbalert_mask(struct i2c_client *client, u8 page, u8 reg,
{
int ret;
guard(pmbus_lock)(client);
ret = _pmbus_write_word_data(client, page, PMBUS_SMBALERT_MASK, reg | (val << 8));
/*
@@ -3662,6 +3677,8 @@ static void pmbus_init_debugfs(struct i2c_client *client,
if (!entries)
return;
guard(pmbus_lock)(client);
/*
* Add device-specific entries.
* Please note that the PMBUS standard allows all registers to be