mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-08-31 08:15:07 -04:00
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:
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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(®->dev_attr, attr->name, 0644,
|
||||
pmbus_show_samples, pmbus_set_samples);
|
||||
a = ®->attribute;
|
||||
|
||||
return pmbus_add_attribute(data, ®->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
|
||||
|
||||
Reference in New Issue
Block a user