summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2026-08-07 17:14:05 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2026-08-07 17:14:05 -0700
commit5d78d199bec44519a7d63c7485d44243498744bf (patch)
treeb07b633cf78b36802fbfab9b38612229a834d14e /drivers
parent9a143525f62bd6a871a49a61a5b3e07f5d54f654 (diff)
parentc6c4234928d2eb4f61fecb61067e612d9bdbd2ff (diff)
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
Diffstat (limited to 'drivers')
-rw-r--r--drivers/hwmon/ads7828.c11
-rw-r--r--drivers/hwmon/corsair-psu.c31
-rw-r--r--drivers/hwmon/ltc4282.c20
-rw-r--r--drivers/hwmon/nzxt-smart2.c6
-rw-r--r--drivers/hwmon/pmbus/lm25066.c5
-rw-r--r--drivers/hwmon/pmbus/pmbus_core.c65
6 files changed, 87 insertions, 51 deletions
diff --git a/drivers/hwmon/ads7828.c b/drivers/hwmon/ads7828.c
index 149cfcec78dc..f8f2f621dfea 100644
--- a/drivers/hwmon/ads7828.c
+++ b/drivers/hwmon/ads7828.c
@@ -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)
diff --git a/drivers/hwmon/corsair-psu.c b/drivers/hwmon/corsair-psu.c
index ce958cdaef58..0cf072f86acb 100644
--- a/drivers/hwmon/corsair-psu.c
+++ b/drivers/hwmon/corsair-psu.c
@@ -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
diff --git a/drivers/hwmon/ltc4282.c b/drivers/hwmon/ltc4282.c
index cc698803f8bf..b1675dc5b3c7 100644
--- a/drivers/hwmon/ltc4282.c
+++ b/drivers/hwmon/ltc4282.c
@@ -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;
diff --git a/drivers/hwmon/nzxt-smart2.c b/drivers/hwmon/nzxt-smart2.c
index ff0c0bee0e83..efc78a8f58c8 100644
--- a/drivers/hwmon/nzxt-smart2.c
+++ b/drivers/hwmon/nzxt-smart2.c
@@ -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,
diff --git a/drivers/hwmon/pmbus/lm25066.c b/drivers/hwmon/pmbus/lm25066.c
index 2be50cf1bbde..c797e2e8258f 100644
--- a/drivers/hwmon/pmbus/lm25066.c
+++ b/drivers/hwmon/pmbus/lm25066.c
@@ -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 */
diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c
index 5567d37f13fe..3b219ed5386f 100644
--- a/drivers/hwmon/pmbus/pmbus_core.c
+++ b/drivers/hwmon/pmbus/pmbus_core.c
@@ -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;
+
+ pmbus_attr_init(a, attr->name, 0644,
+ pmbus_show_samples, pmbus_set_samples, -1);
- return pmbus_add_attribute(data, &reg->dev_attr.attr);
+ 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