From f30ce040a704f30f7ac6d3ec4c3f5390ccaabfe0 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Fri, 20 May 2022 11:47:21 -0700 Subject: [PATCH 01/85] hwmon: (pmbus) Move pec attribute to I2C device Enabling and disabling PEC for PMBus devices is currently only supported with a debugfs attribute, which requires debugfs to be enabled and is thus less than perfect. Take the lm90 driver as example and add a 'pec' attribute to the I2C device if both the I2C adapter and the PMBus device support it. Remove the now obsolete 'pec' attribute from debugfs. Cc: Andrew Jeffery Signed-off-by: Guenter Roeck --- Documentation/ABI/testing/sysfs-class-hwmon | 9 +++ Documentation/hwmon/pmbus-core.rst | 9 +++ drivers/hwmon/pmbus/pmbus_core.c | 89 ++++++++++++--------- 3 files changed, 68 insertions(+), 39 deletions(-) diff --git a/Documentation/ABI/testing/sysfs-class-hwmon b/Documentation/ABI/testing/sysfs-class-hwmon index 653d4c75eddb..7271781a23b2 100644 --- a/Documentation/ABI/testing/sysfs-class-hwmon +++ b/Documentation/ABI/testing/sysfs-class-hwmon @@ -938,3 +938,12 @@ Description: - 1: enable RW + +What: /sys/class/hwmon/hwmonX/device/pec +Description: + PEC support on I2C devices + + - 0, off, n: disable + - 1, on, y: enable + + RW diff --git a/Documentation/hwmon/pmbus-core.rst b/Documentation/hwmon/pmbus-core.rst index e7e0c9ef10be..84c5a4e40c40 100644 --- a/Documentation/hwmon/pmbus-core.rst +++ b/Documentation/hwmon/pmbus-core.rst @@ -121,6 +121,15 @@ Specifically, it provides the following information. non-standard PMBus commands to standard commands, or to augment standard command return values with device specific information. +PEC Support +=========== + +Many PMBus devices support SMBus PEC (Packet Error Checking). If supported +by both the I2C adapter and by the PMBus chip, it is by default enabled. +If PEC is supported, the PMBus core driver adds an attribute named 'pec' to +the I2C device. This attribute can be used to control PEC support in the +communication with the PMBus chip. + API functions ============= diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c index 02912022853d..e670b868e74b 100644 --- a/drivers/hwmon/pmbus/pmbus_core.c +++ b/drivers/hwmon/pmbus/pmbus_core.c @@ -2388,6 +2388,42 @@ static int pmbus_read_status_word(struct i2c_client *client, int page) return _pmbus_read_word_data(client, page, 0xff, PMBUS_STATUS_WORD); } +/* PEC attribute support */ + +static ssize_t pec_show(struct device *dev, struct device_attribute *dummy, + char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + + return sysfs_emit(buf, "%d\n", !!(client->flags & I2C_CLIENT_PEC)); +} + +static ssize_t pec_store(struct device *dev, struct device_attribute *dummy, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + bool enable; + int err; + + err = kstrtobool(buf, &enable); + if (err < 0) + return err; + + if (enable) + client->flags |= I2C_CLIENT_PEC; + else + client->flags &= ~I2C_CLIENT_PEC; + + return count; +} + +static DEVICE_ATTR_RW(pec); + +static void pmbus_remove_pec(void *dev) +{ + device_remove_file(dev, &dev_attr_pec); +} + static int pmbus_init_common(struct i2c_client *client, struct pmbus_data *data, struct pmbus_driver_info *info) { @@ -2474,6 +2510,20 @@ static int pmbus_init_common(struct i2c_client *client, struct pmbus_data *data, return ret; } + if (client->flags & I2C_CLIENT_PEC) { + /* + * If I2C_CLIENT_PEC is set here, both the I2C adapter and the + * chip support PEC. Add 'pec' attribute to client device to let + * the user control it. + */ + ret = device_create_file(dev, &dev_attr_pec); + if (ret) + return ret; + ret = devm_add_action_or_reset(dev, pmbus_remove_pec, dev); + if (ret) + return ret; + } + return 0; } @@ -2782,42 +2832,6 @@ static int pmbus_debugfs_get_status(void *data, u64 *val) DEFINE_DEBUGFS_ATTRIBUTE(pmbus_debugfs_ops_status, pmbus_debugfs_get_status, NULL, "0x%04llx\n"); -static int pmbus_debugfs_get_pec(void *data, u64 *val) -{ - struct i2c_client *client = data; - - *val = !!(client->flags & I2C_CLIENT_PEC); - - return 0; -} - -static int pmbus_debugfs_set_pec(void *data, u64 val) -{ - int rc; - struct i2c_client *client = data; - - if (!val) { - client->flags &= ~I2C_CLIENT_PEC; - return 0; - } - - if (val != 1) - return -EINVAL; - - rc = i2c_smbus_read_byte_data(client, PMBUS_CAPABILITY); - if (rc < 0) - return rc; - - if (!(rc & PB_CAPABILITY_ERROR_CHECK)) - return -EOPNOTSUPP; - - client->flags |= I2C_CLIENT_PEC; - - return 0; -} -DEFINE_DEBUGFS_ATTRIBUTE(pmbus_debugfs_ops_pec, pmbus_debugfs_get_pec, - pmbus_debugfs_set_pec, "%llu\n"); - static void pmbus_remove_debugfs(void *data) { struct dentry *entry = data; @@ -2853,9 +2867,6 @@ static int pmbus_init_debugfs(struct i2c_client *client, if (!entries) return -ENOMEM; - debugfs_create_file("pec", 0664, data->debugfs, client, - &pmbus_debugfs_ops_pec); - for (i = 0; i < data->info->pages; ++i) { /* Check accessibility of status register if it's not page 0 */ if (!i || pmbus_check_status_register(client, i)) { From eaf87c006f03266d4203f0a62e8c8a1cde417152 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Tue, 11 Jan 2022 03:01:56 -0800 Subject: [PATCH 02/85] hwmon: (lm90) Generate sysfs and udev events for all alarms So far the driver only generated sysfs and udev events for minimum and maximum alarms. Also generate events for critical and emergency alarms. Signed-off-by: Guenter Roeck --- drivers/hwmon/lm90.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c index 3820f0e61510..83d027c134be 100644 --- a/drivers/hwmon/lm90.c +++ b/drivers/hwmon/lm90.c @@ -1829,6 +1829,26 @@ static bool lm90_is_tripped(struct i2c_client *client, u16 *status) hwmon_notify_event(data->hwmon_dev, hwmon_temp, hwmon_temp_max_alarm, 2); + if (st & LM90_STATUS_LTHRM) + hwmon_notify_event(hwmon_dev, hwmon_temp, + hwmon_temp_crit_alarm, 0); + if (st & LM90_STATUS_RTHRM) + hwmon_notify_event(hwmon_dev, hwmon_temp, + hwmon_temp_crit_alarm, 1); + if (st2 & MAX6696_STATUS2_R2THRM) + hwmon_notify_event(hwmon_dev, hwmon_temp, + hwmon_temp_crit_alarm, 2); + + if (st2 & MAX6696_STATUS2_LOT2) + hwmon_notify_event(hwmon_dev, hwmon_temp, + hwmon_temp_emergency_alarm, 0); + if (st2 & MAX6696_STATUS2_ROT2) + hwmon_notify_event(hwmon_dev, hwmon_temp, + hwmon_temp_emergency_alarm, 1); + if (st2 & MAX6696_STATUS2_R2OT2) + hwmon_notify_event(hwmon_dev, hwmon_temp, + hwmon_temp_emergency_alarm, 2); + return true; } From f6d0775119fb905fb02eafa98d575cf8ee792d46 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sun, 9 Jan 2022 14:19:01 -0800 Subject: [PATCH 03/85] hwmon: (lm90) Rework alarm/status handling Many chips supported by this driver clear status registers after it is read and update it in the next measurement cycle. Normally this falls under the radar because all registers are only read once per measurement cycle. However, there is an exception: Status registers are always read during interrupt and laert handling. This can result in invalid status reports if userspace reads an alarm attribute immediately afterwards. Rework alarm/status handling by keeping a shadow register with 'current' alarms, and by ensuring that the register is either only updated once per measurement cycle or not cleared. A second problem is related to alert handling: Alert handling is disabled for chips with broken alert after an alert was reported, but only re-enabled if attributes are read by the user. This means that alert conditions may appear and disappear unnoticed. Remedy the situation by introducing a worker to periodically read the status register(s) while alert handling is disabled, and re-enable alerts after the alert condition clears. Yet another problem is that sysfs and udev events are currently only reported to userspace if an alarm is raised, but not if an alarm condition clears. Use the new worker to detect that situation and also generate sysfs and udev events in that case. Signed-off-by: Guenter Roeck --- drivers/hwmon/lm90.c | 314 +++++++++++++++++++++++++++---------------- 1 file changed, 199 insertions(+), 115 deletions(-) diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c index 83d027c134be..63ada2d0d839 100644 --- a/drivers/hwmon/lm90.c +++ b/drivers/hwmon/lm90.c @@ -92,6 +92,7 @@ #include #include #include +#include /* * Addresses to scan @@ -499,8 +500,11 @@ struct lm90_data { const struct hwmon_channel_info *info[3]; struct hwmon_chip_info chip; struct mutex update_lock; + struct delayed_work alert_work; bool valid; /* true if register values are valid */ + bool alarms_valid; /* true if status register values are valid */ unsigned long last_updated; /* in jiffies */ + unsigned long alarms_updated; /* in jiffies */ int kind; u32 flags; @@ -518,7 +522,9 @@ struct lm90_data { s8 temp8[TEMP8_REG_NUM]; s16 temp11[TEMP11_REG_NUM]; u8 temp_hyst; - u16 alarms; /* bitvector (upper 8 bits for max6695/96) */ + u16 reported_alarms; /* alarms reported as sysfs/udev events */ + u16 current_alarms; /* current alarms, reported by chip */ + u16 alarms; /* alarms not yet reported to user */ }; /* @@ -771,6 +777,158 @@ static int lm90_update_limits(struct device *dev) return 0; } +static void lm90_report_alarms(struct device *dev, struct lm90_data *data) +{ + u16 cleared_alarms = data->reported_alarms & ~data->current_alarms; + u16 new_alarms = data->current_alarms & ~data->reported_alarms; + struct device *hwmon_dev = data->hwmon_dev; + int st, st2; + + if (!cleared_alarms && !new_alarms) + return; + + st = new_alarms & 0xff; + st2 = new_alarms >> 8; + + if ((st & (LM90_STATUS_LLOW | LM90_STATUS_LHIGH | LM90_STATUS_LTHRM)) || + (st2 & MAX6696_STATUS2_LOT2)) + dev_dbg(dev, "temp%d out of range, please check!\n", 1); + if ((st & (LM90_STATUS_RLOW | LM90_STATUS_RHIGH | LM90_STATUS_RTHRM)) || + (st2 & MAX6696_STATUS2_ROT2)) + dev_dbg(dev, "temp%d out of range, please check!\n", 2); + if (st & LM90_STATUS_ROPEN) + dev_dbg(dev, "temp%d diode open, please check!\n", 2); + if (st2 & (MAX6696_STATUS2_R2LOW | MAX6696_STATUS2_R2HIGH | + MAX6696_STATUS2_R2THRM | MAX6696_STATUS2_R2OT2)) + dev_dbg(dev, "temp%d out of range, please check!\n", 3); + if (st2 & MAX6696_STATUS2_R2OPEN) + dev_dbg(dev, "temp%d diode open, please check!\n", 3); + + st |= cleared_alarms & 0xff; + st2 |= cleared_alarms >> 8; + + if (st & LM90_STATUS_LLOW) + hwmon_notify_event(hwmon_dev, hwmon_temp, hwmon_temp_min_alarm, 0); + if (st & LM90_STATUS_RLOW) + hwmon_notify_event(hwmon_dev, hwmon_temp, hwmon_temp_min_alarm, 1); + if (st2 & MAX6696_STATUS2_R2LOW) + hwmon_notify_event(hwmon_dev, hwmon_temp, hwmon_temp_min_alarm, 2); + + if (st & LM90_STATUS_LHIGH) + hwmon_notify_event(hwmon_dev, hwmon_temp, hwmon_temp_max_alarm, 0); + if (st & LM90_STATUS_RHIGH) + hwmon_notify_event(hwmon_dev, hwmon_temp, hwmon_temp_max_alarm, 1); + if (st2 & MAX6696_STATUS2_R2HIGH) + hwmon_notify_event(hwmon_dev, hwmon_temp, hwmon_temp_max_alarm, 2); + + if (st & LM90_STATUS_LTHRM) + hwmon_notify_event(hwmon_dev, hwmon_temp, hwmon_temp_crit_alarm, 0); + if (st & LM90_STATUS_RTHRM) + hwmon_notify_event(hwmon_dev, hwmon_temp, hwmon_temp_crit_alarm, 1); + if (st2 & MAX6696_STATUS2_R2THRM) + hwmon_notify_event(hwmon_dev, hwmon_temp, hwmon_temp_crit_alarm, 2); + + if (st2 & MAX6696_STATUS2_LOT2) + hwmon_notify_event(hwmon_dev, hwmon_temp, hwmon_temp_emergency_alarm, 0); + if (st2 & MAX6696_STATUS2_ROT2) + hwmon_notify_event(hwmon_dev, hwmon_temp, hwmon_temp_emergency_alarm, 1); + if (st2 & MAX6696_STATUS2_R2OT2) + hwmon_notify_event(hwmon_dev, hwmon_temp, hwmon_temp_emergency_alarm, 2); + + data->reported_alarms = data->current_alarms; +} + +static int lm90_update_alarms_locked(struct lm90_data *data, bool force) +{ + if (force || !data->alarms_valid || + time_after(jiffies, data->alarms_updated + msecs_to_jiffies(data->update_interval))) { + struct i2c_client *client = data->client; + bool check_enable; + u16 alarms; + int val; + + data->alarms_valid = false; + + val = lm90_read_reg(client, LM90_REG_R_STATUS); + if (val < 0) + return val; + alarms = val & ~LM90_STATUS_BUSY; + + if (data->kind == max6696) { + val = lm90_read_reg(client, MAX6696_REG_R_STATUS2); + if (val < 0) + return val; + alarms |= val << 8; + } + /* + * If the update is forced (called from interrupt or alert + * handler) and alarm data is valid, the alarms may have been + * updated after the last update interval, and the status + * register may still be cleared. Only add additional alarms + * in this case. Alarms will be cleared later if appropriate. + */ + if (force && data->alarms_valid) + data->current_alarms |= alarms; + else + data->current_alarms = alarms; + data->alarms |= alarms; + + check_enable = (client->irq || !(data->config_orig & 0x80)) && + (data->config & 0x80); + + if (force || check_enable) + lm90_report_alarms(&client->dev, data); + + /* + * Re-enable ALERT# output if it was originally enabled, relevant + * alarms are all clear, and alerts are currently disabled. + * Otherwise (re)schedule worker if needed. + */ + if (check_enable) { + if (!(data->current_alarms & data->alert_alarms)) { + dev_dbg(&client->dev, "Re-enabling ALERT#\n"); + lm90_update_confreg(data, data->config & ~0x80); + /* + * We may have been called from the update handler. + * If so, the worker, if scheduled, is no longer + * needed. Cancel it. Don't synchronize because + * it may already be running. + */ + cancel_delayed_work(&data->alert_work); + } else { + schedule_delayed_work(&data->alert_work, + max_t(int, HZ, msecs_to_jiffies(data->update_interval))); + } + } + data->alarms_updated = jiffies; + data->alarms_valid = true; + } + return 0; +} + +static int lm90_update_alarms(struct lm90_data *data, bool force) +{ + int err; + + mutex_lock(&data->update_lock); + err = lm90_update_alarms_locked(data, force); + mutex_unlock(&data->update_lock); + + return err; +} + +static void lm90_alert_work(struct work_struct *__work) +{ + struct delayed_work *delayed_work = container_of(__work, struct delayed_work, work); + struct lm90_data *data = container_of(delayed_work, struct lm90_data, alert_work); + + /* Nothing to do if alerts are enabled */ + if (!(data->config & 0x80)) + return; + + lm90_update_alarms(data, true); +} + static int lm90_update_device(struct device *dev) { struct lm90_data *data = dev_get_drvdata(dev); @@ -819,11 +977,6 @@ static int lm90_update_device(struct device *dev) return val; data->temp11[REMOTE_TEMP] = val; - val = lm90_read_reg(client, LM90_REG_R_STATUS); - if (val < 0) - return val; - data->alarms = val & ~LM90_STATUS_BUSY; - if (data->kind == max6696) { val = lm90_select_remote_channel(data, 1); if (val < 0) @@ -838,24 +991,11 @@ static int lm90_update_device(struct device *dev) data->temp11[REMOTE2_TEMP] = val; lm90_select_remote_channel(data, 0); - - val = lm90_read_reg(client, MAX6696_REG_R_STATUS2); - if (val < 0) - return val; - data->alarms |= val << 8; } - /* - * Re-enable ALERT# output if it was originally enabled and - * relevant alarms are all clear - */ - if ((client->irq || !(data->config_orig & 0x80)) && - !(data->alarms & data->alert_alarms)) { - if (data->config & 0x80) { - dev_dbg(&client->dev, "Re-enabling ALERT#\n"); - lm90_update_confreg(data, data->config & ~0x80); - } - } + val = lm90_update_alarms_locked(data, false); + if (val < 0) + return val; data->last_updated = jiffies; data->valid = true; @@ -1212,7 +1352,7 @@ static const u8 lm90_fault_bits[3] = { 0, 2, 10 }; static int lm90_temp_read(struct device *dev, u32 attr, int channel, long *val) { struct lm90_data *data = dev_get_drvdata(dev); - int err; + int err, bit; mutex_lock(&data->update_lock); err = lm90_update_device(dev); @@ -1225,22 +1365,33 @@ static int lm90_temp_read(struct device *dev, u32 attr, int channel, long *val) *val = lm90_get_temp11(data, lm90_temp_index[channel]); break; case hwmon_temp_min_alarm: - *val = (data->alarms >> lm90_min_alarm_bits[channel]) & 1; - break; case hwmon_temp_max_alarm: - *val = (data->alarms >> lm90_max_alarm_bits[channel]) & 1; - break; case hwmon_temp_crit_alarm: - if (data->flags & LM90_HAVE_CRIT_ALRM_SWP) - *val = (data->alarms >> lm90_crit_alarm_bits_swapped[channel]) & 1; - else - *val = (data->alarms >> lm90_crit_alarm_bits[channel]) & 1; - break; case hwmon_temp_emergency_alarm: - *val = (data->alarms >> lm90_emergency_alarm_bits[channel]) & 1; - break; case hwmon_temp_fault: - *val = (data->alarms >> lm90_fault_bits[channel]) & 1; + switch (attr) { + case hwmon_temp_min_alarm: + bit = BIT(lm90_min_alarm_bits[channel]); + break; + case hwmon_temp_max_alarm: + bit = BIT(lm90_max_alarm_bits[channel]); + break; + case hwmon_temp_crit_alarm: + if (data->flags & LM90_HAVE_CRIT_ALRM_SWP) + bit = BIT(lm90_crit_alarm_bits_swapped[channel]); + else + bit = BIT(lm90_crit_alarm_bits[channel]); + break; + case hwmon_temp_emergency_alarm: + bit = BIT(lm90_emergency_alarm_bits[channel]); + break; + case hwmon_temp_fault: + bit = BIT(lm90_fault_bits[channel]); + break; + } + *val = !!(data->alarms & bit); + data->alarms &= ~bit; + data->alarms |= data->current_alarms; break; case hwmon_temp_min: if (channel == 0) @@ -1699,6 +1850,8 @@ static void lm90_restore_conf(void *_data) struct lm90_data *data = _data; struct i2c_client *client = data->client; + cancel_delayed_work_sync(&data->alert_work); + /* Restore initial configuration */ lm90_write_convrate(data, data->convrate_orig); i2c_smbus_write_byte_data(client, LM90_REG_W_CONFIG1, @@ -1771,93 +1924,23 @@ static int lm90_init_client(struct i2c_client *client, struct lm90_data *data) return devm_add_action_or_reset(&client->dev, lm90_restore_conf, data); } -static bool lm90_is_tripped(struct i2c_client *client, u16 *status) +static bool lm90_is_tripped(struct i2c_client *client) { struct lm90_data *data = i2c_get_clientdata(client); - int st, st2 = 0; + int ret; - st = lm90_read_reg(client, LM90_REG_R_STATUS); - if (st < 0) + ret = lm90_update_alarms(data, true); + if (ret < 0) return false; - if (data->kind == max6696) { - st2 = lm90_read_reg(client, MAX6696_REG_R_STATUS2); - if (st2 < 0) - return false; - } - - *status = st | (st2 << 8); - - if ((st & 0x7f) == 0 && (st2 & 0xfe) == 0) - return false; - - if ((st & (LM90_STATUS_LLOW | LM90_STATUS_LHIGH | LM90_STATUS_LTHRM)) || - (st2 & MAX6696_STATUS2_LOT2)) - dev_dbg(&client->dev, - "temp%d out of range, please check!\n", 1); - if ((st & (LM90_STATUS_RLOW | LM90_STATUS_RHIGH | LM90_STATUS_RTHRM)) || - (st2 & MAX6696_STATUS2_ROT2)) - dev_dbg(&client->dev, - "temp%d out of range, please check!\n", 2); - if (st & LM90_STATUS_ROPEN) - dev_dbg(&client->dev, - "temp%d diode open, please check!\n", 2); - if (st2 & (MAX6696_STATUS2_R2LOW | MAX6696_STATUS2_R2HIGH | - MAX6696_STATUS2_R2THRM | MAX6696_STATUS2_R2OT2)) - dev_dbg(&client->dev, - "temp%d out of range, please check!\n", 3); - if (st2 & MAX6696_STATUS2_R2OPEN) - dev_dbg(&client->dev, - "temp%d diode open, please check!\n", 3); - - if (st & LM90_STATUS_LLOW) - hwmon_notify_event(data->hwmon_dev, hwmon_temp, - hwmon_temp_min_alarm, 0); - if (st & LM90_STATUS_RLOW) - hwmon_notify_event(data->hwmon_dev, hwmon_temp, - hwmon_temp_min_alarm, 1); - if (st2 & MAX6696_STATUS2_R2LOW) - hwmon_notify_event(data->hwmon_dev, hwmon_temp, - hwmon_temp_min_alarm, 2); - if (st & LM90_STATUS_LHIGH) - hwmon_notify_event(data->hwmon_dev, hwmon_temp, - hwmon_temp_max_alarm, 0); - if (st & LM90_STATUS_RHIGH) - hwmon_notify_event(data->hwmon_dev, hwmon_temp, - hwmon_temp_max_alarm, 1); - if (st2 & MAX6696_STATUS2_R2HIGH) - hwmon_notify_event(data->hwmon_dev, hwmon_temp, - hwmon_temp_max_alarm, 2); - - if (st & LM90_STATUS_LTHRM) - hwmon_notify_event(hwmon_dev, hwmon_temp, - hwmon_temp_crit_alarm, 0); - if (st & LM90_STATUS_RTHRM) - hwmon_notify_event(hwmon_dev, hwmon_temp, - hwmon_temp_crit_alarm, 1); - if (st2 & MAX6696_STATUS2_R2THRM) - hwmon_notify_event(hwmon_dev, hwmon_temp, - hwmon_temp_crit_alarm, 2); - - if (st2 & MAX6696_STATUS2_LOT2) - hwmon_notify_event(hwmon_dev, hwmon_temp, - hwmon_temp_emergency_alarm, 0); - if (st2 & MAX6696_STATUS2_ROT2) - hwmon_notify_event(hwmon_dev, hwmon_temp, - hwmon_temp_emergency_alarm, 1); - if (st2 & MAX6696_STATUS2_R2OT2) - hwmon_notify_event(hwmon_dev, hwmon_temp, - hwmon_temp_emergency_alarm, 2); - - return true; + return !!data->current_alarms; } static irqreturn_t lm90_irq_thread(int irq, void *dev_id) { struct i2c_client *client = dev_id; - u16 status; - if (lm90_is_tripped(client, &status)) + if (lm90_is_tripped(client)) return IRQ_HANDLED; else return IRQ_NONE; @@ -1911,6 +1994,7 @@ static int lm90_probe(struct i2c_client *client) data->client = client; i2c_set_clientdata(client, data); mutex_init(&data->update_lock); + INIT_DELAYED_WORK(&data->alert_work, lm90_alert_work); /* Set the device type */ if (client->dev.of_node) @@ -2027,12 +2111,10 @@ static int lm90_probe(struct i2c_client *client) static void lm90_alert(struct i2c_client *client, enum i2c_alert_protocol type, unsigned int flag) { - u16 alarms; - if (type != I2C_PROTOCOL_SMBUS_ALERT) return; - if (lm90_is_tripped(client, &alarms)) { + if (lm90_is_tripped(client)) { /* * Disable ALERT# output, because these chips don't implement * SMBus alert correctly; they should only hold the alert line @@ -2041,9 +2123,11 @@ static void lm90_alert(struct i2c_client *client, enum i2c_alert_protocol type, struct lm90_data *data = i2c_get_clientdata(client); if ((data->flags & LM90_HAVE_BROKEN_ALERT) && - (alarms & data->alert_alarms)) { + (data->current_alarms & data->alert_alarms)) { dev_dbg(&client->dev, "Disabling ALERT#\n"); lm90_update_confreg(data, data->config | 0x80); + schedule_delayed_work(&data->alert_work, + max_t(int, HZ, msecs_to_jiffies(data->update_interval))); } } else { dev_dbg(&client->dev, "Everything OK\n"); From 479f21d41d30c25cc9060828dd8819f655227931 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sat, 30 Oct 2021 09:02:56 -0700 Subject: [PATCH 04/85] hwmon: (lm90) Reorder include files in alphabetical order Reorder include files in alphabetical order to reduce the chance of duplicates and to make it clear where new include files should be added. Drop the unnecessary include of linux/sysfs.h. Include linux/device.h instead because that is what is actually used. No functional change. Signed-off-by: Guenter Roeck --- drivers/hwmon/lm90.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c index 63ada2d0d839..b7f5b743c9f5 100644 --- a/drivers/hwmon/lm90.c +++ b/drivers/hwmon/lm90.c @@ -80,18 +80,18 @@ * concern all supported chipsets, unless mentioned otherwise. */ -#include -#include -#include -#include -#include -#include +#include #include +#include +#include +#include +#include +#include +#include #include #include -#include -#include #include +#include #include /* From ff8f0a652d2fbc3e552a32ad738928f7c23751e4 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sat, 30 Oct 2021 09:05:45 -0700 Subject: [PATCH 05/85] hwmon: (lm90) Reorder chip enumeration to be in alphabetical order Reorder chip enumeration in alphabetical order to make it easier to see which chips are supported, and to clarify where to add support new chip types. No functional change. Signed-off-by: Guenter Roeck --- drivers/hwmon/lm90.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c index b7f5b743c9f5..6728520a21ca 100644 --- a/drivers/hwmon/lm90.c +++ b/drivers/hwmon/lm90.c @@ -113,8 +113,10 @@ static const unsigned short normal_i2c[] = { 0x18, 0x19, 0x1a, 0x29, 0x2a, 0x2b, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, I2C_CLIENT_END }; -enum chips { lm90, adm1032, lm99, lm86, max6657, max6659, adt7461, max6680, - max6646, w83l771, max6696, sa56004, g781, tmp451, tmp461, max6654 }; +enum chips { adm1032, adt7461, g781, lm86, lm90, lm99, + max6646, max6654, max6657, max6659, max6680, max6696, + sa56004, tmp451, tmp461, w83l771, +}; /* * The LM90 registers From ddf2a6093c56546e17c7038ac6275ef779682de9 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sat, 30 Oct 2021 09:11:20 -0700 Subject: [PATCH 06/85] hwmon: (lm90) Use BIT macro Use BIT macro instead of shift operation to improve readability. No functional change. Signed-off-by: Guenter Roeck --- drivers/hwmon/lm90.c | 53 ++++++++++++++++++++++---------------------- 1 file changed, 27 insertions(+), 26 deletions(-) diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c index 6728520a21ca..0f3fadc1631c 100644 --- a/drivers/hwmon/lm90.c +++ b/drivers/hwmon/lm90.c @@ -80,6 +80,7 @@ * concern all supported chipsets, unless mentioned otherwise. */ +#include #include #include #include @@ -182,36 +183,36 @@ enum chips { adm1032, adt7461, g781, lm86, lm90, lm99, /* * Device flags */ -#define LM90_FLAG_ADT7461_EXT (1 << 0) /* ADT7461 extended mode */ +#define LM90_FLAG_ADT7461_EXT BIT(0) /* ADT7461 extended mode */ /* Device features */ -#define LM90_HAVE_OFFSET (1 << 1) /* temperature offset register */ -#define LM90_HAVE_REM_LIMIT_EXT (1 << 3) /* extended remote limit */ -#define LM90_HAVE_EMERGENCY (1 << 4) /* 3rd upper (emergency) limit */ -#define LM90_HAVE_EMERGENCY_ALARM (1 << 5)/* emergency alarm */ -#define LM90_HAVE_TEMP3 (1 << 6) /* 3rd temperature sensor */ -#define LM90_HAVE_BROKEN_ALERT (1 << 7) /* Broken alert */ -#define LM90_HAVE_EXTENDED_TEMP (1 << 8) /* extended temperature support*/ -#define LM90_PAUSE_FOR_CONFIG (1 << 9) /* Pause conversion for config */ -#define LM90_HAVE_CRIT (1 << 10)/* Chip supports CRIT/OVERT register */ -#define LM90_HAVE_CRIT_ALRM_SWP (1 << 11)/* critical alarm bits swapped */ +#define LM90_HAVE_OFFSET BIT(1) /* temperature offset register */ +#define LM90_HAVE_REM_LIMIT_EXT BIT(3) /* extended remote limit */ +#define LM90_HAVE_EMERGENCY BIT(4) /* 3rd upper (emergency) limit */ +#define LM90_HAVE_EMERGENCY_ALARM BIT(5)/* emergency alarm */ +#define LM90_HAVE_TEMP3 BIT(6) /* 3rd temperature sensor */ +#define LM90_HAVE_BROKEN_ALERT BIT(7) /* Broken alert */ +#define LM90_HAVE_EXTENDED_TEMP BIT(8) /* extended temperature support */ +#define LM90_PAUSE_FOR_CONFIG BIT(9) /* Pause conversion for config */ +#define LM90_HAVE_CRIT BIT(10) /* Chip supports CRIT/OVERT register */ +#define LM90_HAVE_CRIT_ALRM_SWP BIT(11) /* critical alarm bits swapped */ /* LM90 status */ -#define LM90_STATUS_LTHRM (1 << 0) /* local THERM limit tripped */ -#define LM90_STATUS_RTHRM (1 << 1) /* remote THERM limit tripped */ -#define LM90_STATUS_ROPEN (1 << 2) /* remote is an open circuit */ -#define LM90_STATUS_RLOW (1 << 3) /* remote low temp limit tripped */ -#define LM90_STATUS_RHIGH (1 << 4) /* remote high temp limit tripped */ -#define LM90_STATUS_LLOW (1 << 5) /* local low temp limit tripped */ -#define LM90_STATUS_LHIGH (1 << 6) /* local high temp limit tripped */ -#define LM90_STATUS_BUSY (1 << 7) /* conversion is ongoing */ +#define LM90_STATUS_LTHRM BIT(0) /* local THERM limit tripped */ +#define LM90_STATUS_RTHRM BIT(1) /* remote THERM limit tripped */ +#define LM90_STATUS_ROPEN BIT(2) /* remote is an open circuit */ +#define LM90_STATUS_RLOW BIT(3) /* remote low temp limit tripped */ +#define LM90_STATUS_RHIGH BIT(4) /* remote high temp limit tripped */ +#define LM90_STATUS_LLOW BIT(5) /* local low temp limit tripped */ +#define LM90_STATUS_LHIGH BIT(6) /* local high temp limit tripped */ +#define LM90_STATUS_BUSY BIT(7) /* conversion is ongoing */ -#define MAX6696_STATUS2_R2THRM (1 << 1) /* remote2 THERM limit tripped */ -#define MAX6696_STATUS2_R2OPEN (1 << 2) /* remote2 is an open circuit */ -#define MAX6696_STATUS2_R2LOW (1 << 3) /* remote2 low temp limit tripped */ -#define MAX6696_STATUS2_R2HIGH (1 << 4) /* remote2 high temp limit tripped */ -#define MAX6696_STATUS2_ROT2 (1 << 5) /* remote emergency limit tripped */ -#define MAX6696_STATUS2_R2OT2 (1 << 6) /* remote2 emergency limit tripped */ -#define MAX6696_STATUS2_LOT2 (1 << 7) /* local emergency limit tripped */ +#define MAX6696_STATUS2_R2THRM BIT(1) /* remote2 THERM limit tripped */ +#define MAX6696_STATUS2_R2OPEN BIT(2) /* remote2 is an open circuit */ +#define MAX6696_STATUS2_R2LOW BIT(3) /* remote2 low temp limit tripped */ +#define MAX6696_STATUS2_R2HIGH BIT(4) /* remote2 high temp limit tripped */ +#define MAX6696_STATUS2_ROT2 BIT(5) /* remote emergency limit tripped */ +#define MAX6696_STATUS2_R2OT2 BIT(6) /* remote2 emergency limit tripped */ +#define MAX6696_STATUS2_LOT2 BIT(7) /* local emergency limit tripped */ /* * Driver data (common to all clients) From ca7b9b14a022b2d255133b7bce7536ac45441f5e Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Mon, 29 Nov 2021 12:17:41 -0800 Subject: [PATCH 07/85] hwmon: (lm90) Move status register bit shifts to compile time Handling bit shifts necessary to extract status bits during compile time reduces code and data size by almost 5% when building for x86_64. Signed-off-by: Guenter Roeck --- drivers/hwmon/lm90.c | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c index 0f3fadc1631c..cc26dd08fbff 100644 --- a/drivers/hwmon/lm90.c +++ b/drivers/hwmon/lm90.c @@ -1345,17 +1345,18 @@ static const u8 lm90_temp_emerg_index[3] = { LOCAL_EMERG, REMOTE_EMERG, REMOTE2_EMERG }; -static const u8 lm90_min_alarm_bits[3] = { 5, 3, 11 }; -static const u8 lm90_max_alarm_bits[3] = { 6, 4, 12 }; -static const u8 lm90_crit_alarm_bits[3] = { 0, 1, 9 }; -static const u8 lm90_crit_alarm_bits_swapped[3] = { 1, 0, 9 }; -static const u8 lm90_emergency_alarm_bits[3] = { 15, 13, 14 }; -static const u8 lm90_fault_bits[3] = { 0, 2, 10 }; +static const u16 lm90_min_alarm_bits[3] = { BIT(5), BIT(3), BIT(11) }; +static const u16 lm90_max_alarm_bits[3] = { BIT(6), BIT(4), BIT(12) }; +static const u16 lm90_crit_alarm_bits[3] = { BIT(0), BIT(1), BIT(9) }; +static const u16 lm90_crit_alarm_bits_swapped[3] = { BIT(1), BIT(0), BIT(9) }; +static const u16 lm90_emergency_alarm_bits[3] = { BIT(15), BIT(13), BIT(14) }; +static const u16 lm90_fault_bits[3] = { BIT(0), BIT(2), BIT(10) }; static int lm90_temp_read(struct device *dev, u32 attr, int channel, long *val) { struct lm90_data *data = dev_get_drvdata(dev); - int err, bit; + int err; + u16 bit; mutex_lock(&data->update_lock); err = lm90_update_device(dev); @@ -1374,22 +1375,22 @@ static int lm90_temp_read(struct device *dev, u32 attr, int channel, long *val) case hwmon_temp_fault: switch (attr) { case hwmon_temp_min_alarm: - bit = BIT(lm90_min_alarm_bits[channel]); + bit = lm90_min_alarm_bits[channel]; break; case hwmon_temp_max_alarm: - bit = BIT(lm90_max_alarm_bits[channel]); + bit = lm90_max_alarm_bits[channel]; break; case hwmon_temp_crit_alarm: if (data->flags & LM90_HAVE_CRIT_ALRM_SWP) - bit = BIT(lm90_crit_alarm_bits_swapped[channel]); + bit = lm90_crit_alarm_bits_swapped[channel]; else - bit = BIT(lm90_crit_alarm_bits[channel]); + bit = lm90_crit_alarm_bits[channel]; break; case hwmon_temp_emergency_alarm: - bit = BIT(lm90_emergency_alarm_bits[channel]); + bit = lm90_emergency_alarm_bits[channel]; break; case hwmon_temp_fault: - bit = BIT(lm90_fault_bits[channel]); + bit = lm90_fault_bits[channel]; break; } *val = !!(data->alarms & bit); From f68480cc32750eecf44dd657322040d41a6a557c Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sun, 31 Oct 2021 02:01:41 -0700 Subject: [PATCH 08/85] hwmon: (lm90) Stop using R_/W_ register prefix The register write address either matches the read address, or it is the read address plus 6. Simplify the code and resolve the write address programmatically instead of having two defines for each register. Signed-off-by: Guenter Roeck --- drivers/hwmon/lm90.c | 234 +++++++++++++++++++++---------------------- 1 file changed, 117 insertions(+), 117 deletions(-) diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c index cc26dd08fbff..995b27a248e6 100644 --- a/drivers/hwmon/lm90.c +++ b/drivers/hwmon/lm90.c @@ -123,58 +123,42 @@ enum chips { adm1032, adt7461, g781, lm86, lm90, lm99, * The LM90 registers */ -#define LM90_REG_R_MAN_ID 0xFE -#define LM90_REG_R_CHIP_ID 0xFF -#define LM90_REG_R_CONFIG1 0x03 -#define LM90_REG_W_CONFIG1 0x09 -#define LM90_REG_R_CONFIG2 0xBF -#define LM90_REG_W_CONFIG2 0xBF -#define LM90_REG_R_CONVRATE 0x04 -#define LM90_REG_W_CONVRATE 0x0A -#define LM90_REG_R_STATUS 0x02 -#define LM90_REG_R_LOCAL_TEMP 0x00 -#define LM90_REG_R_LOCAL_HIGH 0x05 -#define LM90_REG_W_LOCAL_HIGH 0x0B -#define LM90_REG_R_LOCAL_LOW 0x06 -#define LM90_REG_W_LOCAL_LOW 0x0C -#define LM90_REG_R_LOCAL_CRIT 0x20 -#define LM90_REG_W_LOCAL_CRIT 0x20 -#define LM90_REG_R_REMOTE_TEMPH 0x01 -#define LM90_REG_R_REMOTE_TEMPL 0x10 -#define LM90_REG_R_REMOTE_OFFSH 0x11 -#define LM90_REG_W_REMOTE_OFFSH 0x11 -#define LM90_REG_R_REMOTE_OFFSL 0x12 -#define LM90_REG_W_REMOTE_OFFSL 0x12 -#define LM90_REG_R_REMOTE_HIGHH 0x07 -#define LM90_REG_W_REMOTE_HIGHH 0x0D -#define LM90_REG_R_REMOTE_HIGHL 0x13 -#define LM90_REG_W_REMOTE_HIGHL 0x13 -#define LM90_REG_R_REMOTE_LOWH 0x08 -#define LM90_REG_W_REMOTE_LOWH 0x0E -#define LM90_REG_R_REMOTE_LOWL 0x14 -#define LM90_REG_W_REMOTE_LOWL 0x14 -#define LM90_REG_R_REMOTE_CRIT 0x19 -#define LM90_REG_W_REMOTE_CRIT 0x19 -#define LM90_REG_R_TCRIT_HYST 0x21 -#define LM90_REG_W_TCRIT_HYST 0x21 +#define LM90_REG_MAN_ID 0xFE +#define LM90_REG_CHIP_ID 0xFF +#define LM90_REG_CONFIG1 0x03 +#define LM90_REG_CONFIG2 0xBF +#define LM90_REG_CONVRATE 0x04 +#define LM90_REG_STATUS 0x02 +#define LM90_REG_LOCAL_TEMP 0x00 +#define LM90_REG_LOCAL_HIGH 0x05 +#define LM90_REG_LOCAL_LOW 0x06 +#define LM90_REG_LOCAL_CRIT 0x20 +#define LM90_REG_REMOTE_TEMPH 0x01 +#define LM90_REG_REMOTE_TEMPL 0x10 +#define LM90_REG_REMOTE_OFFSH 0x11 +#define LM90_REG_REMOTE_OFFSL 0x12 +#define LM90_REG_REMOTE_HIGHH 0x07 +#define LM90_REG_REMOTE_HIGHL 0x13 +#define LM90_REG_REMOTE_LOWH 0x08 +#define LM90_REG_REMOTE_LOWL 0x14 +#define LM90_REG_REMOTE_CRIT 0x19 +#define LM90_REG_TCRIT_HYST 0x21 /* MAX6646/6647/6649/6654/6657/6658/6659/6695/6696 registers */ -#define MAX6657_REG_R_LOCAL_TEMPL 0x11 -#define MAX6696_REG_R_STATUS2 0x12 -#define MAX6659_REG_R_REMOTE_EMERG 0x16 -#define MAX6659_REG_W_REMOTE_EMERG 0x16 -#define MAX6659_REG_R_LOCAL_EMERG 0x17 -#define MAX6659_REG_W_LOCAL_EMERG 0x17 +#define MAX6657_REG_LOCAL_TEMPL 0x11 +#define MAX6696_REG_STATUS2 0x12 +#define MAX6659_REG_REMOTE_EMERG 0x16 +#define MAX6659_REG_LOCAL_EMERG 0x17 /* SA56004 registers */ -#define SA56004_REG_R_LOCAL_TEMPL 0x22 +#define SA56004_REG_LOCAL_TEMPL 0x22 #define LM90_MAX_CONVRATE_MS 16000 /* Maximum conversion rate in ms */ /* TMP451/TMP461 registers */ -#define TMP451_REG_R_LOCAL_TEMPL 0x15 +#define TMP451_REG_LOCAL_TEMPL 0x15 #define TMP451_REG_CONALERT 0x22 #define TMP461_REG_CHEN 0x16 @@ -401,25 +385,25 @@ static const struct lm90_params lm90_params[] = { .flags = LM90_HAVE_CRIT | LM90_HAVE_BROKEN_ALERT, .alert_alarms = 0x7c, .max_convrate = 6, - .reg_local_ext = MAX6657_REG_R_LOCAL_TEMPL, + .reg_local_ext = MAX6657_REG_LOCAL_TEMPL, }, [max6654] = { .flags = LM90_HAVE_BROKEN_ALERT, .alert_alarms = 0x7c, .max_convrate = 7, - .reg_local_ext = MAX6657_REG_R_LOCAL_TEMPL, + .reg_local_ext = MAX6657_REG_LOCAL_TEMPL, }, [max6657] = { .flags = LM90_PAUSE_FOR_CONFIG | LM90_HAVE_CRIT, .alert_alarms = 0x7c, .max_convrate = 8, - .reg_local_ext = MAX6657_REG_R_LOCAL_TEMPL, + .reg_local_ext = MAX6657_REG_LOCAL_TEMPL, }, [max6659] = { .flags = LM90_HAVE_EMERGENCY | LM90_HAVE_CRIT, .alert_alarms = 0x7c, .max_convrate = 8, - .reg_local_ext = MAX6657_REG_R_LOCAL_TEMPL, + .reg_local_ext = MAX6657_REG_LOCAL_TEMPL, }, [max6680] = { .flags = LM90_HAVE_OFFSET | LM90_HAVE_CRIT @@ -432,7 +416,7 @@ static const struct lm90_params lm90_params[] = { | LM90_HAVE_EMERGENCY_ALARM | LM90_HAVE_TEMP3 | LM90_HAVE_CRIT, .alert_alarms = 0x1c7c, .max_convrate = 6, - .reg_local_ext = MAX6657_REG_R_LOCAL_TEMPL, + .reg_local_ext = MAX6657_REG_LOCAL_TEMPL, }, [w83l771] = { .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT | LM90_HAVE_CRIT, @@ -443,21 +427,21 @@ static const struct lm90_params lm90_params[] = { .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT | LM90_HAVE_CRIT, .alert_alarms = 0x7b, .max_convrate = 9, - .reg_local_ext = SA56004_REG_R_LOCAL_TEMPL, + .reg_local_ext = SA56004_REG_LOCAL_TEMPL, }, [tmp451] = { .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT | LM90_HAVE_BROKEN_ALERT | LM90_HAVE_EXTENDED_TEMP | LM90_HAVE_CRIT, .alert_alarms = 0x7c, .max_convrate = 9, - .reg_local_ext = TMP451_REG_R_LOCAL_TEMPL, + .reg_local_ext = TMP451_REG_LOCAL_TEMPL, }, [tmp461] = { .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT | LM90_HAVE_BROKEN_ALERT | LM90_HAVE_EXTENDED_TEMP | LM90_HAVE_CRIT, .alert_alarms = 0x7c, .max_convrate = 9, - .reg_local_ext = TMP451_REG_R_LOCAL_TEMPL, + .reg_local_ext = TMP451_REG_LOCAL_TEMPL, }, }; @@ -565,6 +549,29 @@ static int lm90_read_reg(struct i2c_client *client, u8 reg) return err; } +/* + * Return register write address + * + * The write address for registers 0x03 .. 0x08 is the read address plus 6. + * For other registers the write address matches the read address. + */ +static u8 lm90_write_reg_addr(u8 reg) +{ + if (reg >= LM90_REG_CONFIG1 && reg <= LM90_REG_REMOTE_LOWH) + return reg + 6; + return reg; +} + +/* + * Write into LM90 register. + * Convert register address to write address if needed, then execute the + * operation. + */ +static int lm90_write_reg(struct i2c_client *client, u8 reg, u8 val) +{ + return i2c_smbus_write_byte_data(client, lm90_write_reg_addr(reg), val); +} + static int lm90_read16(struct i2c_client *client, u8 regh, u8 regl) { int oldh, newh, l; @@ -604,9 +611,7 @@ static int lm90_update_confreg(struct lm90_data *data, u8 config) if (data->config != config) { int err; - err = i2c_smbus_write_byte_data(data->client, - LM90_REG_W_CONFIG1, - config); + err = lm90_write_reg(data->client, LM90_REG_CONFIG1, config); if (err) return err; data->config = config; @@ -649,7 +654,7 @@ static int lm90_write_convrate(struct lm90_data *data, int val) } /* Set conv rate */ - err = i2c_smbus_write_byte_data(data->client, LM90_REG_W_CONVRATE, val); + err = lm90_write_reg(data->client, LM90_REG_CONVRATE, val); /* Revert change to config */ lm90_update_confreg(data, config); @@ -689,61 +694,61 @@ static int lm90_update_limits(struct device *dev) int val; if (data->flags & LM90_HAVE_CRIT) { - val = lm90_read_reg(client, LM90_REG_R_LOCAL_CRIT); + val = lm90_read_reg(client, LM90_REG_LOCAL_CRIT); if (val < 0) return val; data->temp8[LOCAL_CRIT] = val; - val = lm90_read_reg(client, LM90_REG_R_REMOTE_CRIT); + val = lm90_read_reg(client, LM90_REG_REMOTE_CRIT); if (val < 0) return val; data->temp8[REMOTE_CRIT] = val; - val = lm90_read_reg(client, LM90_REG_R_TCRIT_HYST); + val = lm90_read_reg(client, LM90_REG_TCRIT_HYST); if (val < 0) return val; data->temp_hyst = val; } - val = lm90_read_reg(client, LM90_REG_R_REMOTE_LOWH); + val = lm90_read_reg(client, LM90_REG_REMOTE_LOWH); if (val < 0) return val; data->temp11[REMOTE_LOW] = val << 8; if (data->flags & LM90_HAVE_REM_LIMIT_EXT) { - val = lm90_read_reg(client, LM90_REG_R_REMOTE_LOWL); + val = lm90_read_reg(client, LM90_REG_REMOTE_LOWL); if (val < 0) return val; data->temp11[REMOTE_LOW] |= val; } - val = lm90_read_reg(client, LM90_REG_R_REMOTE_HIGHH); + val = lm90_read_reg(client, LM90_REG_REMOTE_HIGHH); if (val < 0) return val; data->temp11[REMOTE_HIGH] = val << 8; if (data->flags & LM90_HAVE_REM_LIMIT_EXT) { - val = lm90_read_reg(client, LM90_REG_R_REMOTE_HIGHL); + val = lm90_read_reg(client, LM90_REG_REMOTE_HIGHL); if (val < 0) return val; data->temp11[REMOTE_HIGH] |= val; } if (data->flags & LM90_HAVE_OFFSET) { - val = lm90_read16(client, LM90_REG_R_REMOTE_OFFSH, - LM90_REG_R_REMOTE_OFFSL); + val = lm90_read16(client, LM90_REG_REMOTE_OFFSH, + LM90_REG_REMOTE_OFFSL); if (val < 0) return val; data->temp11[REMOTE_OFFSET] = val; } if (data->flags & LM90_HAVE_EMERGENCY) { - val = lm90_read_reg(client, MAX6659_REG_R_LOCAL_EMERG); + val = lm90_read_reg(client, MAX6659_REG_LOCAL_EMERG); if (val < 0) return val; data->temp8[LOCAL_EMERG] = val; - val = lm90_read_reg(client, MAX6659_REG_R_REMOTE_EMERG); + val = lm90_read_reg(client, MAX6659_REG_REMOTE_EMERG); if (val < 0) return val; data->temp8[REMOTE_EMERG] = val; @@ -754,22 +759,22 @@ static int lm90_update_limits(struct device *dev) if (val < 0) return val; - val = lm90_read_reg(client, LM90_REG_R_REMOTE_CRIT); + val = lm90_read_reg(client, LM90_REG_REMOTE_CRIT); if (val < 0) return val; data->temp8[REMOTE2_CRIT] = val; - val = lm90_read_reg(client, MAX6659_REG_R_REMOTE_EMERG); + val = lm90_read_reg(client, MAX6659_REG_REMOTE_EMERG); if (val < 0) return val; data->temp8[REMOTE2_EMERG] = val; - val = lm90_read_reg(client, LM90_REG_R_REMOTE_LOWH); + val = lm90_read_reg(client, LM90_REG_REMOTE_LOWH); if (val < 0) return val; data->temp11[REMOTE2_LOW] = val << 8; - val = lm90_read_reg(client, LM90_REG_R_REMOTE_HIGHH); + val = lm90_read_reg(client, LM90_REG_REMOTE_HIGHH); if (val < 0) return val; data->temp11[REMOTE2_HIGH] = val << 8; @@ -852,13 +857,13 @@ static int lm90_update_alarms_locked(struct lm90_data *data, bool force) data->alarms_valid = false; - val = lm90_read_reg(client, LM90_REG_R_STATUS); + val = lm90_read_reg(client, LM90_REG_STATUS); if (val < 0) return val; alarms = val & ~LM90_STATUS_BUSY; if (data->kind == max6696) { - val = lm90_read_reg(client, MAX6696_REG_R_STATUS2); + val = lm90_read_reg(client, MAX6696_REG_STATUS2); if (val < 0) return val; alarms |= val << 8; @@ -952,30 +957,30 @@ static int lm90_update_device(struct device *dev) data->valid = false; - val = lm90_read_reg(client, LM90_REG_R_LOCAL_LOW); + val = lm90_read_reg(client, LM90_REG_LOCAL_LOW); if (val < 0) return val; data->temp8[LOCAL_LOW] = val; - val = lm90_read_reg(client, LM90_REG_R_LOCAL_HIGH); + val = lm90_read_reg(client, LM90_REG_LOCAL_HIGH); if (val < 0) return val; data->temp8[LOCAL_HIGH] = val; if (data->reg_local_ext) { - val = lm90_read16(client, LM90_REG_R_LOCAL_TEMP, + val = lm90_read16(client, LM90_REG_LOCAL_TEMP, data->reg_local_ext); if (val < 0) return val; data->temp11[LOCAL_TEMP] = val; } else { - val = lm90_read_reg(client, LM90_REG_R_LOCAL_TEMP); + val = lm90_read_reg(client, LM90_REG_LOCAL_TEMP); if (val < 0) return val; data->temp11[LOCAL_TEMP] = val << 8; } - val = lm90_read16(client, LM90_REG_R_REMOTE_TEMPH, - LM90_REG_R_REMOTE_TEMPL); + val = lm90_read16(client, LM90_REG_REMOTE_TEMPH, + LM90_REG_REMOTE_TEMPL); if (val < 0) return val; data->temp11[REMOTE_TEMP] = val; @@ -985,8 +990,8 @@ static int lm90_update_device(struct device *dev) if (val < 0) return val; - val = lm90_read16(client, LM90_REG_R_REMOTE_TEMPH, - LM90_REG_R_REMOTE_TEMPL); + val = lm90_read16(client, LM90_REG_REMOTE_TEMPH, + LM90_REG_REMOTE_TEMPL); if (val < 0) { lm90_select_remote_channel(data, 0); return val; @@ -1191,11 +1196,11 @@ static int lm90_set_temp11(struct lm90_data *data, int index, long val) u8 high; u8 low; } reg[] = { - [REMOTE_LOW] = { LM90_REG_W_REMOTE_LOWH, LM90_REG_W_REMOTE_LOWL }, - [REMOTE_HIGH] = { LM90_REG_W_REMOTE_HIGHH, LM90_REG_W_REMOTE_HIGHL }, - [REMOTE_OFFSET] = { LM90_REG_W_REMOTE_OFFSH, LM90_REG_W_REMOTE_OFFSL }, - [REMOTE2_LOW] = { LM90_REG_W_REMOTE_LOWH, LM90_REG_W_REMOTE_LOWL }, - [REMOTE2_HIGH] = { LM90_REG_W_REMOTE_HIGHH, LM90_REG_W_REMOTE_HIGHL } + [REMOTE_LOW] = { LM90_REG_REMOTE_LOWH, LM90_REG_REMOTE_LOWL }, + [REMOTE_HIGH] = { LM90_REG_REMOTE_HIGHH, LM90_REG_REMOTE_HIGHL }, + [REMOTE_OFFSET] = { LM90_REG_REMOTE_OFFSH, LM90_REG_REMOTE_OFFSL }, + [REMOTE2_LOW] = { LM90_REG_REMOTE_LOWH, LM90_REG_REMOTE_LOWL }, + [REMOTE2_HIGH] = { LM90_REG_REMOTE_HIGHH, LM90_REG_REMOTE_HIGHL } }; struct i2c_client *client = data->client; struct reg *regp = ®[index]; @@ -1218,13 +1223,12 @@ static int lm90_set_temp11(struct lm90_data *data, int index, long val) data->temp11[index] = temp_to_s8(val) << 8; lm90_select_remote_channel(data, index >= 3); - err = i2c_smbus_write_byte_data(client, regp->high, - data->temp11[index] >> 8); + err = lm90_write_reg(client, regp->high, data->temp11[index] >> 8); if (err < 0) return err; if (data->flags & LM90_HAVE_REM_LIMIT_EXT) - err = i2c_smbus_write_byte_data(client, regp->low, - data->temp11[index] & 0xff); + err = lm90_write_reg(client, regp->low, + data->temp11[index] & 0xff); lm90_select_remote_channel(data, 0); return err; @@ -1252,14 +1256,14 @@ static int lm90_get_temp8(struct lm90_data *data, int index) static int lm90_set_temp8(struct lm90_data *data, int index, long val) { static const u8 reg[TEMP8_REG_NUM] = { - LM90_REG_W_LOCAL_LOW, - LM90_REG_W_LOCAL_HIGH, - LM90_REG_W_LOCAL_CRIT, - LM90_REG_W_REMOTE_CRIT, - MAX6659_REG_W_LOCAL_EMERG, - MAX6659_REG_W_REMOTE_EMERG, - LM90_REG_W_REMOTE_CRIT, - MAX6659_REG_W_REMOTE_EMERG, + LM90_REG_LOCAL_LOW, + LM90_REG_LOCAL_HIGH, + LM90_REG_LOCAL_CRIT, + LM90_REG_REMOTE_CRIT, + MAX6659_REG_LOCAL_EMERG, + MAX6659_REG_REMOTE_EMERG, + LM90_REG_REMOTE_CRIT, + MAX6659_REG_REMOTE_EMERG, }; struct i2c_client *client = data->client; int err; @@ -1279,7 +1283,7 @@ static int lm90_set_temp8(struct lm90_data *data, int index, long val) data->temp8[index] = temp_to_s8(val); lm90_select_remote_channel(data, index >= 6); - err = i2c_smbus_write_byte_data(client, reg[index], data->temp8[index]); + err = lm90_write_reg(client, reg[index], data->temp8[index]); lm90_select_remote_channel(data, 0); return err; @@ -1307,7 +1311,6 @@ static int lm90_set_temphyst(struct lm90_data *data, long val) { struct i2c_client *client = data->client; int temp; - int err; if (data->flags & LM90_HAVE_EXTENDED_TEMP) temp = temp_from_u8_adt7461(data, data->temp8[LOCAL_CRIT]); @@ -1320,9 +1323,7 @@ static int lm90_set_temphyst(struct lm90_data *data, long val) val = clamp_val(val, -128000l, 255000l); data->temp_hyst = hyst_to_reg(temp - val); - err = i2c_smbus_write_byte_data(client, LM90_REG_W_TCRIT_HYST, - data->temp_hyst); - return err; + return lm90_write_reg(client, LM90_REG_TCRIT_HYST, data->temp_hyst); } static const u8 lm90_temp_index[3] = { @@ -1630,15 +1631,15 @@ static int lm90_detect(struct i2c_client *client, return -ENODEV; /* detection and identification */ - man_id = i2c_smbus_read_byte_data(client, LM90_REG_R_MAN_ID); - chip_id = i2c_smbus_read_byte_data(client, LM90_REG_R_CHIP_ID); - config1 = i2c_smbus_read_byte_data(client, LM90_REG_R_CONFIG1); - convrate = i2c_smbus_read_byte_data(client, LM90_REG_R_CONVRATE); + man_id = i2c_smbus_read_byte_data(client, LM90_REG_MAN_ID); + chip_id = i2c_smbus_read_byte_data(client, LM90_REG_CHIP_ID); + config1 = i2c_smbus_read_byte_data(client, LM90_REG_CONFIG1); + convrate = i2c_smbus_read_byte_data(client, LM90_REG_CONVRATE); if (man_id < 0 || chip_id < 0 || config1 < 0 || convrate < 0) return -ENODEV; if (man_id == 0x01 || man_id == 0x5C || man_id == 0xA1) { - config2 = i2c_smbus_read_byte_data(client, LM90_REG_R_CONFIG2); + config2 = i2c_smbus_read_byte_data(client, LM90_REG_CONFIG2); if (config2 < 0) return -ENODEV; } @@ -1697,19 +1698,19 @@ static int lm90_detect(struct i2c_client *client, int emerg, emerg2, status2; /* - * We read MAX6659_REG_R_REMOTE_EMERG twice, and re-read - * LM90_REG_R_MAN_ID in between. If MAX6659_REG_R_REMOTE_EMERG + * We read MAX6659_REG_REMOTE_EMERG twice, and re-read + * LM90_REG_MAN_ID in between. If MAX6659_REG_REMOTE_EMERG * exists, both readings will reflect the same value. Otherwise, * the readings will be different. */ emerg = i2c_smbus_read_byte_data(client, - MAX6659_REG_R_REMOTE_EMERG); + MAX6659_REG_REMOTE_EMERG); man_id = i2c_smbus_read_byte_data(client, - LM90_REG_R_MAN_ID); + LM90_REG_MAN_ID); emerg2 = i2c_smbus_read_byte_data(client, - MAX6659_REG_R_REMOTE_EMERG); + MAX6659_REG_REMOTE_EMERG); status2 = i2c_smbus_read_byte_data(client, - MAX6696_REG_R_STATUS2); + MAX6696_REG_STATUS2); if (emerg < 0 || man_id < 0 || emerg2 < 0 || status2 < 0) return -ENODEV; @@ -1820,7 +1821,7 @@ static int lm90_detect(struct i2c_client *client, int local_ext, conalert, chen, dfc; local_ext = i2c_smbus_read_byte_data(client, - TMP451_REG_R_LOCAL_TEMPL); + TMP451_REG_LOCAL_TEMPL); conalert = i2c_smbus_read_byte_data(client, TMP451_REG_CONALERT); chen = i2c_smbus_read_byte_data(client, TMP461_REG_CHEN); @@ -1858,8 +1859,7 @@ static void lm90_restore_conf(void *_data) /* Restore initial configuration */ lm90_write_convrate(data, data->convrate_orig); - i2c_smbus_write_byte_data(client, LM90_REG_W_CONFIG1, - data->config_orig); + lm90_write_reg(client, LM90_REG_CONFIG1, data->config_orig); } static int lm90_init_client(struct i2c_client *client, struct lm90_data *data) @@ -1867,7 +1867,7 @@ static int lm90_init_client(struct i2c_client *client, struct lm90_data *data) struct device_node *np = client->dev.of_node; int config, convrate; - convrate = lm90_read_reg(client, LM90_REG_R_CONVRATE); + convrate = lm90_read_reg(client, LM90_REG_CONVRATE); if (convrate < 0) return convrate; data->convrate_orig = convrate; @@ -1875,7 +1875,7 @@ static int lm90_init_client(struct i2c_client *client, struct lm90_data *data) /* * Start the conversions. */ - config = lm90_read_reg(client, LM90_REG_R_CONFIG1); + config = lm90_read_reg(client, LM90_REG_CONFIG1); if (config < 0) return config; data->config_orig = config; From 3b0982ff93aab8751f2c5df456a68abed11f84da Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sun, 31 Oct 2021 09:34:33 -0700 Subject: [PATCH 09/85] hwmon: (lm90) Improve PEC support PEC (packet error checking) support for ADM1032 is currently only enabled if the chip was auto-detected, but not if a chip is instantiated explicitly. Always enable PEC support by introducing a chip feature flag indicating partial PEC support. Also, for consistency, disable PEC support by default to match existing functionality if the chip was not auto- detected. At the same time, introduce generic support for PEC with a separate feature flag. This will be used when support for chips with full PEC functionality is added. Signed-off-by: Guenter Roeck --- Documentation/hwmon/lm90.rst | 6 ++-- drivers/hwmon/lm90.c | 56 +++++++++++++++++++----------------- 2 files changed, 32 insertions(+), 30 deletions(-) diff --git a/Documentation/hwmon/lm90.rst b/Documentation/hwmon/lm90.rst index 05391fb4042d..f107d4a159fa 100644 --- a/Documentation/hwmon/lm90.rst +++ b/Documentation/hwmon/lm90.rst @@ -423,6 +423,6 @@ two transactions will typically mean twice as much delay waiting for transaction completion, effectively doubling the register cache refresh time. I guess reliability comes at a price, but it's quite expensive this time. -So, as not everyone might enjoy the slowdown, PEC can be disabled through -sysfs. Just write 0 to the "pec" file and PEC will be disabled. Write 1 -to that file to enable PEC again. +So, as not everyone might enjoy the slowdown, PEC is disabled by default and +can be enabled through sysfs. Just write 1 to the "pec" file and PEC will be +enabled. Write 0 to that file to disable PEC again. diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c index 995b27a248e6..8ba95ea06f0c 100644 --- a/drivers/hwmon/lm90.c +++ b/drivers/hwmon/lm90.c @@ -179,6 +179,8 @@ enum chips { adm1032, adt7461, g781, lm86, lm90, lm99, #define LM90_PAUSE_FOR_CONFIG BIT(9) /* Pause conversion for config */ #define LM90_HAVE_CRIT BIT(10) /* Chip supports CRIT/OVERT register */ #define LM90_HAVE_CRIT_ALRM_SWP BIT(11) /* critical alarm bits swapped */ +#define LM90_HAVE_PEC BIT(12) /* Chip supports PEC */ +#define LM90_HAVE_PARTIAL_PEC BIT(13) /* Partial PEC support (adm1032)*/ /* LM90 status */ #define LM90_STATUS_LTHRM BIT(0) /* local THERM limit tripped */ @@ -346,7 +348,8 @@ struct lm90_params { static const struct lm90_params lm90_params[] = { [adm1032] = { .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT - | LM90_HAVE_BROKEN_ALERT | LM90_HAVE_CRIT, + | LM90_HAVE_BROKEN_ALERT | LM90_HAVE_CRIT + | LM90_HAVE_PARTIAL_PEC, .alert_alarms = 0x7c, .max_convrate = 10, }, @@ -519,10 +522,10 @@ struct lm90_data { */ /* - * The ADM1032 supports PEC but not on write byte transactions, so we need + * If the chip supports PEC but not on write byte transactions, we need * to explicitly ask for a transaction without PEC. */ -static inline s32 adm1032_write_byte(struct i2c_client *client, u8 value) +static inline s32 lm90_write_no_pec(struct i2c_client *client, u8 value) { return i2c_smbus_xfer(client->adapter, client->addr, client->flags & ~I2C_CLIENT_PEC, @@ -531,22 +534,24 @@ static inline s32 adm1032_write_byte(struct i2c_client *client, u8 value) /* * It is assumed that client->update_lock is held (unless we are in - * detection or initialization steps). This matters when PEC is enabled, - * because we don't want the address pointer to change between the write - * byte and the read byte transactions. + * detection or initialization steps). This matters when PEC is enabled + * for chips with partial PEC support, because we don't want the address + * pointer to change between the write byte and the read byte transactions. */ static int lm90_read_reg(struct i2c_client *client, u8 reg) { + struct lm90_data *data = i2c_get_clientdata(client); + bool partial_pec = (client->flags & I2C_CLIENT_PEC) && + (data->flags & LM90_HAVE_PARTIAL_PEC); int err; - if (client->flags & I2C_CLIENT_PEC) { - err = adm1032_write_byte(client, reg); - if (err >= 0) - err = i2c_smbus_read_byte(client); - } else - err = i2c_smbus_read_byte_data(client, reg); - - return err; + if (partial_pec) { + err = lm90_write_no_pec(client, reg); + if (err) + return err; + return i2c_smbus_read_byte(client); + } + return i2c_smbus_read_byte_data(client, reg); } /* @@ -1135,7 +1140,7 @@ static u16 temp_to_u16_adt7461(struct lm90_data *data, long val) return (val + 125) / 250 * 64; } -/* pec used for ADM1032 only */ +/* pec used for devices with PEC support */ static ssize_t pec_show(struct device *dev, struct device_attribute *dummy, char *buf) { @@ -1675,13 +1680,6 @@ static int lm90_detect(struct i2c_client *client, && (config1 & 0x3F) == 0x00 && convrate <= 0x0A) { name = "adm1032"; - /* - * The ADM1032 supports PEC, but only if combined - * transactions are not used. - */ - if (i2c_check_functionality(adapter, - I2C_FUNC_SMBUS_BYTE)) - info->flags |= I2C_CLIENT_PEC; } else if (chip_id == 0x51 /* ADT7461 */ && (config1 & 0x1B) == 0x00 @@ -2005,10 +2003,6 @@ static int lm90_probe(struct i2c_client *client) data->kind = (enum chips)of_device_get_match_data(&client->dev); else data->kind = i2c_match_id(lm90_id, client)->driver_data; - if (data->kind == adm1032) { - if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE)) - client->flags &= ~I2C_CLIENT_PEC; - } /* * Different devices have different alarm bits triggering the @@ -2019,6 +2013,14 @@ static int lm90_probe(struct i2c_client *client) /* Set chip capabilities */ data->flags = lm90_params[data->kind].flags; + if ((data->flags & (LM90_HAVE_PEC | LM90_HAVE_PARTIAL_PEC)) && + !i2c_check_functionality(adapter, I2C_FUNC_SMBUS_PEC)) + data->flags &= ~(LM90_HAVE_PEC | LM90_HAVE_PARTIAL_PEC); + + if ((data->flags & LM90_HAVE_PARTIAL_PEC) && + !i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE)) + data->flags &= ~LM90_HAVE_PARTIAL_PEC; + data->chip.ops = &lm90_ops; data->chip.info = data->info; @@ -2081,7 +2083,7 @@ static int lm90_probe(struct i2c_client *client) * The 'pec' attribute is attached to the i2c device and thus created * separately. */ - if (client->flags & I2C_CLIENT_PEC) { + if (data->flags & (LM90_HAVE_PEC | LM90_HAVE_PARTIAL_PEC)) { err = device_create_file(dev, &dev_attr_pec); if (err) return err; From 425f5b5d15c2dfed7193ad2517a082bc99964722 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Mon, 8 Nov 2021 17:45:05 -0800 Subject: [PATCH 10/85] hwmon: (lm90) Add partial PEC support for ADT7461 Revision 0 of the ADT7461 datasheet suggests that the chip supports PEC (packet error checking). This information is gone in later versions of the datasheet. Experiments show that PEC support on ADT7461 is similar to PEC support in ADM1032, ie it is only supported for read operations. Add support for it to the driver. Signed-off-by: Guenter Roeck --- Documentation/hwmon/lm90.rst | 1 + drivers/hwmon/lm90.c | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Documentation/hwmon/lm90.rst b/Documentation/hwmon/lm90.rst index f107d4a159fa..9886a298797f 100644 --- a/Documentation/hwmon/lm90.rst +++ b/Documentation/hwmon/lm90.rst @@ -322,6 +322,7 @@ ADM1032: ADT7461, ADT7461A, NCT1008: * Extended temperature range (breaks compatibility) * Lower resolution for remote temperature + * SMBus PEC support for Write Byte and Receive Byte transactions. MAX6654: * Better local resolution diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c index 8ba95ea06f0c..6c79422da420 100644 --- a/drivers/hwmon/lm90.c +++ b/drivers/hwmon/lm90.c @@ -356,7 +356,7 @@ static const struct lm90_params lm90_params[] = { [adt7461] = { .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT | LM90_HAVE_BROKEN_ALERT | LM90_HAVE_EXTENDED_TEMP - | LM90_HAVE_CRIT, + | LM90_HAVE_CRIT | LM90_HAVE_PARTIAL_PEC, .alert_alarms = 0x7c, .max_convrate = 10, }, From d70fa73d84337e7197336e137e221d93792844f7 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Fri, 10 Dec 2021 21:18:27 -0800 Subject: [PATCH 11/85] hwmon: (lm90) Enable full PEC support for ADT7461A Experiments show that ADT7461A and NCT1008 support PEC, even though it is not documented. Enable support for it in the driver. Since ADT7461 only supports partial PEC, this means that the configuration for ADT7461A needs to be separated from ADT7461. Signed-off-by: Guenter Roeck --- drivers/hwmon/lm90.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c index 6c79422da420..42e72702b9a9 100644 --- a/drivers/hwmon/lm90.c +++ b/drivers/hwmon/lm90.c @@ -114,7 +114,7 @@ static const unsigned short normal_i2c[] = { 0x18, 0x19, 0x1a, 0x29, 0x2a, 0x2b, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, I2C_CLIENT_END }; -enum chips { adm1032, adt7461, g781, lm86, lm90, lm99, +enum chips { adm1032, adt7461, adt7461a, g781, lm86, lm90, lm99, max6646, max6654, max6657, max6659, max6680, max6696, sa56004, tmp451, tmp461, w83l771, }; @@ -207,7 +207,7 @@ enum chips { adm1032, adt7461, g781, lm86, lm90, lm99, static const struct i2c_device_id lm90_id[] = { { "adm1032", adm1032 }, { "adt7461", adt7461 }, - { "adt7461a", adt7461 }, + { "adt7461a", adt7461a }, { "g781", g781 }, { "lm90", lm90 }, { "lm86", lm86 }, @@ -224,7 +224,7 @@ static const struct i2c_device_id lm90_id[] = { { "max6681", max6680 }, { "max6695", max6696 }, { "max6696", max6696 }, - { "nct1008", adt7461 }, + { "nct1008", adt7461a }, { "w83l771", w83l771 }, { "sa56004", sa56004 }, { "tmp451", tmp451 }, @@ -244,7 +244,7 @@ static const struct of_device_id __maybe_unused lm90_of_match[] = { }, { .compatible = "adi,adt7461a", - .data = (void *)adt7461 + .data = (void *)adt7461a }, { .compatible = "gmt,g781", @@ -312,7 +312,7 @@ static const struct of_device_id __maybe_unused lm90_of_match[] = { }, { .compatible = "onnn,nct1008", - .data = (void *)adt7461 + .data = (void *)adt7461a }, { .compatible = "winbond,w83l771", @@ -360,6 +360,13 @@ static const struct lm90_params lm90_params[] = { .alert_alarms = 0x7c, .max_convrate = 10, }, + [adt7461a] = { + .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT + | LM90_HAVE_BROKEN_ALERT | LM90_HAVE_EXTENDED_TEMP + | LM90_HAVE_CRIT | LM90_HAVE_PEC, + .alert_alarms = 0x7c, + .max_convrate = 10, + }, [g781] = { .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT | LM90_HAVE_BROKEN_ALERT | LM90_HAVE_CRIT, From b2644494a4c7145a05af01bee1df8549a71efc20 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sat, 4 Dec 2021 07:53:00 -0800 Subject: [PATCH 12/85] hwmon: (lm90) Add support for unsigned and signed temperatures ADT7461 and TMP451 temperature sensors support extended temperature ranges. If standard temperature range is selected, the temperature range is unsigned and limited to 0 .. 127 degrees C. For TMP461, the standard temperature range is -128000 ... 127000 degrees C. Distinguish between the two chips by introducing a feature flag indicating if the standard temperature range is signed or unsigned. Use the same flag for MAX6646/ MAX6647 as well since those chips also support unsigned temperatures. Note that while the datasheet for ADT7461 suggests that the default temperature range is unsigned, tests with a real chip suggest that this is not the case: If the temperature offset is set to a value << 0, the temperature register does report negative values. Tests with real chips show that MAX6680/MAX6681 and SA56004 report temperatures of 128 degrees C and higher as negative temperatures. Add respective comments to the code. Also use clamp_val() and DIV_ROUND_CLOSEST where appropriate in calculations. Signed-off-by: Guenter Roeck --- drivers/hwmon/lm90.c | 70 ++++++++++++++++++++++++++------------------ 1 file changed, 42 insertions(+), 28 deletions(-) diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c index 42e72702b9a9..acb9ca3b99b0 100644 --- a/drivers/hwmon/lm90.c +++ b/drivers/hwmon/lm90.c @@ -170,6 +170,7 @@ enum chips { adm1032, adt7461, adt7461a, g781, lm86, lm90, lm99, #define LM90_FLAG_ADT7461_EXT BIT(0) /* ADT7461 extended mode */ /* Device features */ #define LM90_HAVE_OFFSET BIT(1) /* temperature offset register */ +#define LM90_HAVE_UNSIGNED_TEMP BIT(2) /* temperatures are unsigned */ #define LM90_HAVE_REM_LIMIT_EXT BIT(3) /* extended remote limit */ #define LM90_HAVE_EMERGENCY BIT(4) /* 3rd upper (emergency) limit */ #define LM90_HAVE_EMERGENCY_ALARM BIT(5)/* emergency alarm */ @@ -354,6 +355,11 @@ static const struct lm90_params lm90_params[] = { .max_convrate = 10, }, [adt7461] = { + /* + * Standard temperature range is supposed to be unsigned, + * but that does not match reality. Negative temperatures + * are always reported. + */ .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT | LM90_HAVE_BROKEN_ALERT | LM90_HAVE_EXTENDED_TEMP | LM90_HAVE_CRIT | LM90_HAVE_PARTIAL_PEC, @@ -392,7 +398,8 @@ static const struct lm90_params lm90_params[] = { .max_convrate = 9, }, [max6646] = { - .flags = LM90_HAVE_CRIT | LM90_HAVE_BROKEN_ALERT, + .flags = LM90_HAVE_CRIT | LM90_HAVE_BROKEN_ALERT + | LM90_HAVE_UNSIGNED_TEMP, .alert_alarms = 0x7c, .max_convrate = 6, .reg_local_ext = MAX6657_REG_LOCAL_TEMPL, @@ -416,6 +423,11 @@ static const struct lm90_params lm90_params[] = { .reg_local_ext = MAX6657_REG_LOCAL_TEMPL, }, [max6680] = { + /* + * Apparent temperatures of 128 degrees C or higher are reported + * and treated as negative temperatures (meaning min_alarm will + * be set). + */ .flags = LM90_HAVE_OFFSET | LM90_HAVE_CRIT | LM90_HAVE_CRIT_ALRM_SWP | LM90_HAVE_BROKEN_ALERT, .alert_alarms = 0x7c, @@ -434,6 +446,11 @@ static const struct lm90_params lm90_params[] = { .max_convrate = 8, }, [sa56004] = { + /* + * Apparent temperatures of 128 degrees C or higher are reported + * and treated as negative temperatures (meaning min_alarm will + * be set). + */ .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT | LM90_HAVE_CRIT, .alert_alarms = 0x7b, .max_convrate = 9, @@ -441,7 +458,8 @@ static const struct lm90_params lm90_params[] = { }, [tmp451] = { .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT - | LM90_HAVE_BROKEN_ALERT | LM90_HAVE_EXTENDED_TEMP | LM90_HAVE_CRIT, + | LM90_HAVE_BROKEN_ALERT | LM90_HAVE_EXTENDED_TEMP | LM90_HAVE_CRIT + | LM90_HAVE_UNSIGNED_TEMP, .alert_alarms = 0x7c, .max_convrate = 9, .reg_local_ext = TMP451_REG_LOCAL_TEMPL, @@ -1118,33 +1136,27 @@ static inline int temp_from_u16_adt7461(struct lm90_data *data, u16 val) static u8 temp_to_u8_adt7461(struct lm90_data *data, long val) { if (data->flags & LM90_FLAG_ADT7461_EXT) { - if (val <= -64000) - return 0; - if (val >= 191000) - return 0xFF; - return (val + 500 + 64000) / 1000; + val = clamp_val(val, -64000, 191000); + val += 64000; + } else if (data->flags & LM90_HAVE_UNSIGNED_TEMP) { + val = clamp_val(val, 0, 127000); + } else { + val = clamp_val(val, -128000, 127000); } - if (val <= 0) - return 0; - if (val >= 127000) - return 127; - return (val + 500) / 1000; + return DIV_ROUND_CLOSEST(val, 1000); } static u16 temp_to_u16_adt7461(struct lm90_data *data, long val) { if (data->flags & LM90_FLAG_ADT7461_EXT) { - if (val <= -64000) - return 0; - if (val >= 191750) - return 0xFFC0; - return (val + 64000 + 125) / 250 * 64; + val = clamp_val(val, -64000, 191000); + val += 64000; + } else if (data->flags & LM90_HAVE_UNSIGNED_TEMP) { + val = clamp_val(val, 0, 127000); + } else { + val = clamp_val(val, -128000, 127000); } - if (val <= 0) - return 0; - if (val >= 127750) - return 0x7FC0; - return (val + 125) / 250 * 64; + return DIV_ROUND_CLOSEST(val, 1000) & 0xfff0; } /* pec used for devices with PEC support */ @@ -1190,7 +1202,7 @@ static int lm90_get_temp11(struct lm90_data *data, int index) if (data->flags & LM90_HAVE_EXTENDED_TEMP) temp = temp_from_u16_adt7461(data, temp11); - else if (data->kind == max6646) + else if (data->flags & LM90_HAVE_UNSIGNED_TEMP) temp = temp_from_u16(temp11); else temp = temp_from_s16(temp11); @@ -1227,7 +1239,7 @@ static int lm90_set_temp11(struct lm90_data *data, int index, long val) if (data->flags & LM90_HAVE_EXTENDED_TEMP) data->temp11[index] = temp_to_u16_adt7461(data, val); - else if (data->kind == max6646) + else if (data->flags & LM90_HAVE_UNSIGNED_TEMP) data->temp11[index] = temp_to_u8(val) << 8; else if (data->flags & LM90_HAVE_REM_LIMIT_EXT) data->temp11[index] = temp_to_s16(val); @@ -1253,7 +1265,7 @@ static int lm90_get_temp8(struct lm90_data *data, int index) if (data->flags & LM90_HAVE_EXTENDED_TEMP) temp = temp_from_u8_adt7461(data, temp8); - else if (data->kind == max6646) + else if (data->flags & LM90_HAVE_UNSIGNED_TEMP) temp = temp_from_u8(temp8); else temp = temp_from_s8(temp8); @@ -1289,7 +1301,7 @@ static int lm90_set_temp8(struct lm90_data *data, int index, long val) if (data->flags & LM90_HAVE_EXTENDED_TEMP) data->temp8[index] = temp_to_u8_adt7461(data, val); - else if (data->kind == max6646) + else if (data->flags & LM90_HAVE_UNSIGNED_TEMP) data->temp8[index] = temp_to_u8(val); else data->temp8[index] = temp_to_s8(val); @@ -1307,7 +1319,7 @@ static int lm90_get_temphyst(struct lm90_data *data, int index) if (data->flags & LM90_HAVE_EXTENDED_TEMP) temp = temp_from_u8_adt7461(data, data->temp8[index]); - else if (data->kind == max6646) + else if (data->flags & LM90_HAVE_UNSIGNED_TEMP) temp = temp_from_u8(data->temp8[index]); else temp = temp_from_s8(data->temp8[index]); @@ -1326,7 +1338,7 @@ static int lm90_set_temphyst(struct lm90_data *data, long val) if (data->flags & LM90_HAVE_EXTENDED_TEMP) temp = temp_from_u8_adt7461(data, data->temp8[LOCAL_CRIT]); - else if (data->kind == max6646) + else if (data->flags & LM90_HAVE_UNSIGNED_TEMP) temp = temp_from_u8(data->temp8[LOCAL_CRIT]); else temp = temp_from_s8(data->temp8[LOCAL_CRIT]); @@ -1901,6 +1913,8 @@ static int lm90_init_client(struct i2c_client *client, struct lm90_data *data) * Put MAX6680/MAX8881 into extended resolution (bit 0x10, * 0.125 degree resolution) and range (0x08, extend range * to -64 degree) mode for the remote temperature sensor. + * Note that expeciments with an actual chip do not show a difference + * if bit 3 is set or not. */ if (data->kind == max6680) config |= 0x18; From 8f19501d87e0e79a50b60ead7e114ed5a8eea34e Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Fri, 5 Nov 2021 16:04:52 -0700 Subject: [PATCH 13/85] hwmon: (lm90) Only re-read registers if volatile When reading 16-bit volatile registers, the code uses a trick to determine if a temperature is consistent: It reads the high part of the register twice. If the values are the same, the code assumes that the reading is consistent. If the value differs, the code re-reads the second register as well and assumes that it now has correct values. This is only necessary for volatile registers. Add a parameter to lm90_read16() to indicate if the register is volatile to avoid the extra overhead for non-volatile registers. Signed-off-by: Guenter Roeck --- drivers/hwmon/lm90.c | 41 +++++++++++++++++++++++------------------ 1 file changed, 23 insertions(+), 18 deletions(-) diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c index acb9ca3b99b0..b20be0cb28b5 100644 --- a/drivers/hwmon/lm90.c +++ b/drivers/hwmon/lm90.c @@ -602,29 +602,34 @@ static int lm90_write_reg(struct i2c_client *client, u8 reg, u8 val) return i2c_smbus_write_byte_data(client, lm90_write_reg_addr(reg), val); } -static int lm90_read16(struct i2c_client *client, u8 regh, u8 regl) +static int lm90_read16(struct i2c_client *client, u8 regh, u8 regl, + bool is_volatile) { int oldh, newh, l; - /* - * There is a trick here. We have to read two registers to have the - * sensor temperature, but we have to beware a conversion could occur - * between the readings. The datasheet says we should either use - * the one-shot conversion register, which we don't want to do - * (disables hardware monitoring) or monitor the busy bit, which is - * impossible (we can't read the values and monitor that bit at the - * exact same time). So the solution used here is to read the high - * byte once, then the low byte, then the high byte again. If the new - * high byte matches the old one, then we have a valid reading. Else - * we have to read the low byte again, and now we believe we have a - * correct reading. - */ oldh = lm90_read_reg(client, regh); if (oldh < 0) return oldh; l = lm90_read_reg(client, regl); if (l < 0) return l; + + if (!is_volatile) + return (oldh << 8) | l; + + /* + * For volatile registers we have to use a trick. + * We have to read two registers to have the sensor temperature, + * but we have to beware a conversion could occur between the + * readings. The datasheet says we should either use + * the one-shot conversion register, which we don't want to do + * (disables hardware monitoring) or monitor the busy bit, which is + * impossible (we can't read the values and monitor that bit at the + * exact same time). So the solution used here is to read the high + * the high byte again. If the new high byte matches the old one, + * then we have a valid reading. Otherwise we have to read the low + * byte again, and now we believe we have a correct reading. + */ newh = lm90_read_reg(client, regh); if (newh < 0) return newh; @@ -766,7 +771,7 @@ static int lm90_update_limits(struct device *dev) if (data->flags & LM90_HAVE_OFFSET) { val = lm90_read16(client, LM90_REG_REMOTE_OFFSH, - LM90_REG_REMOTE_OFFSL); + LM90_REG_REMOTE_OFFSL, false); if (val < 0) return val; data->temp11[REMOTE_OFFSET] = val; @@ -999,7 +1004,7 @@ static int lm90_update_device(struct device *dev) if (data->reg_local_ext) { val = lm90_read16(client, LM90_REG_LOCAL_TEMP, - data->reg_local_ext); + data->reg_local_ext, true); if (val < 0) return val; data->temp11[LOCAL_TEMP] = val; @@ -1010,7 +1015,7 @@ static int lm90_update_device(struct device *dev) data->temp11[LOCAL_TEMP] = val << 8; } val = lm90_read16(client, LM90_REG_REMOTE_TEMPH, - LM90_REG_REMOTE_TEMPL); + LM90_REG_REMOTE_TEMPL, true); if (val < 0) return val; data->temp11[REMOTE_TEMP] = val; @@ -1021,7 +1026,7 @@ static int lm90_update_device(struct device *dev) return val; val = lm90_read16(client, LM90_REG_REMOTE_TEMPH, - LM90_REG_REMOTE_TEMPL); + LM90_REG_REMOTE_TEMPL, true); if (val < 0) { lm90_select_remote_channel(data, 0); return val; From a8ddcc5750a94527ceead3de59f1f6ebc3e58115 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Tue, 2 Nov 2021 22:38:59 -0700 Subject: [PATCH 14/85] hwmon: (lm90) Support multiple temperature resolutions While most LM90 compatible chips support a temperature sensor resolution of 11 bit, this is not the case for all chips. ADT7461 only supports a resolution of 10 bit, and TMP451/TMP461 support a resolution of 12 bit. Add support for various temperature sensor resolutions. To do this, model all temperature sensors as 16 bit sensors, and use unified temperature conversion functions which take the sensor resolution as parameter. While enhancing functionality, this has the positive side effect of reducing code size by about 5%. Signed-off-by: Guenter Roeck --- drivers/hwmon/lm90.c | 464 +++++++++++++++---------------------------- 1 file changed, 161 insertions(+), 303 deletions(-) diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c index b20be0cb28b5..f162391eafc2 100644 --- a/drivers/hwmon/lm90.c +++ b/drivers/hwmon/lm90.c @@ -343,6 +343,7 @@ struct lm90_params { u16 alert_alarms; /* Which alarm bits trigger ALERT# */ /* Upper 8 bits for max6695/96 */ u8 max_convrate; /* Maximum conversion rate register value */ + u8 resolution; /* 16-bit resolution (default 11 bit) */ u8 reg_local_ext; /* Extended local temp register (optional) */ }; @@ -365,6 +366,7 @@ static const struct lm90_params lm90_params[] = { | LM90_HAVE_CRIT | LM90_HAVE_PARTIAL_PEC, .alert_alarms = 0x7c, .max_convrate = 10, + .resolution = 10, }, [adt7461a] = { .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT @@ -462,6 +464,7 @@ static const struct lm90_params lm90_params[] = { | LM90_HAVE_UNSIGNED_TEMP, .alert_alarms = 0x7c, .max_convrate = 9, + .resolution = 12, .reg_local_ext = TMP451_REG_LOCAL_TEMPL, }, [tmp461] = { @@ -469,14 +472,15 @@ static const struct lm90_params lm90_params[] = { | LM90_HAVE_BROKEN_ALERT | LM90_HAVE_EXTENDED_TEMP | LM90_HAVE_CRIT, .alert_alarms = 0x7c, .max_convrate = 9, + .resolution = 12, .reg_local_ext = TMP451_REG_LOCAL_TEMPL, }, }; /* - * TEMP8 register index + * temperature register index */ -enum lm90_temp8_reg_index { +enum lm90_temp_reg_index { LOCAL_LOW = 0, LOCAL_HIGH, LOCAL_CRIT, @@ -485,14 +489,8 @@ enum lm90_temp8_reg_index { REMOTE_EMERG, /* max6659 and max6695/96 */ REMOTE2_CRIT, /* max6695/96 only */ REMOTE2_EMERG, /* max6695/96 only */ - TEMP8_REG_NUM -}; -/* - * TEMP11 register index - */ -enum lm90_temp11_reg_index { - REMOTE_TEMP = 0, + REMOTE_TEMP, REMOTE_LOW, REMOTE_HIGH, REMOTE_OFFSET, /* except max6646, max6657/58/59, and max6695/96 */ @@ -500,7 +498,8 @@ enum lm90_temp11_reg_index { REMOTE2_TEMP, /* max6695/96 only */ REMOTE2_LOW, /* max6695/96 only */ REMOTE2_HIGH, /* max6695/96 only */ - TEMP11_REG_NUM + + TEMP_REG_NUM }; /* @@ -528,14 +527,14 @@ struct lm90_data { u8 config; /* Current configuration register value */ u8 config_orig; /* Original configuration register value */ u8 convrate_orig; /* Original conversion rate register value */ + u8 resolution; /* temperature resolution in bit */ u16 alert_alarms; /* Which alarm bits trigger ALERT# */ /* Upper 8 bits for max6695/96 */ u8 max_convrate; /* Maximum conversion rate */ u8 reg_local_ext; /* local extension register offset */ /* registers values */ - s8 temp8[TEMP8_REG_NUM]; - s16 temp11[TEMP11_REG_NUM]; + u16 temp[TEMP_REG_NUM]; u8 temp_hyst; u16 reported_alarms; /* alarms reported as sysfs/udev events */ u16 current_alarms; /* current alarms, reported by chip */ @@ -732,12 +731,12 @@ static int lm90_update_limits(struct device *dev) val = lm90_read_reg(client, LM90_REG_LOCAL_CRIT); if (val < 0) return val; - data->temp8[LOCAL_CRIT] = val; + data->temp[LOCAL_CRIT] = val << 8; val = lm90_read_reg(client, LM90_REG_REMOTE_CRIT); if (val < 0) return val; - data->temp8[REMOTE_CRIT] = val; + data->temp[REMOTE_CRIT] = val << 8; val = lm90_read_reg(client, LM90_REG_TCRIT_HYST); if (val < 0) @@ -748,25 +747,25 @@ static int lm90_update_limits(struct device *dev) val = lm90_read_reg(client, LM90_REG_REMOTE_LOWH); if (val < 0) return val; - data->temp11[REMOTE_LOW] = val << 8; + data->temp[REMOTE_LOW] = val << 8; if (data->flags & LM90_HAVE_REM_LIMIT_EXT) { val = lm90_read_reg(client, LM90_REG_REMOTE_LOWL); if (val < 0) return val; - data->temp11[REMOTE_LOW] |= val; + data->temp[REMOTE_LOW] |= val; } val = lm90_read_reg(client, LM90_REG_REMOTE_HIGHH); if (val < 0) return val; - data->temp11[REMOTE_HIGH] = val << 8; + data->temp[REMOTE_HIGH] = val << 8; if (data->flags & LM90_HAVE_REM_LIMIT_EXT) { val = lm90_read_reg(client, LM90_REG_REMOTE_HIGHL); if (val < 0) return val; - data->temp11[REMOTE_HIGH] |= val; + data->temp[REMOTE_HIGH] |= val; } if (data->flags & LM90_HAVE_OFFSET) { @@ -774,19 +773,19 @@ static int lm90_update_limits(struct device *dev) LM90_REG_REMOTE_OFFSL, false); if (val < 0) return val; - data->temp11[REMOTE_OFFSET] = val; + data->temp[REMOTE_OFFSET] = val; } if (data->flags & LM90_HAVE_EMERGENCY) { val = lm90_read_reg(client, MAX6659_REG_LOCAL_EMERG); if (val < 0) return val; - data->temp8[LOCAL_EMERG] = val; + data->temp[LOCAL_EMERG] = val << 8; val = lm90_read_reg(client, MAX6659_REG_REMOTE_EMERG); if (val < 0) return val; - data->temp8[REMOTE_EMERG] = val; + data->temp[REMOTE_EMERG] = val << 8; } if (data->kind == max6696) { @@ -797,22 +796,22 @@ static int lm90_update_limits(struct device *dev) val = lm90_read_reg(client, LM90_REG_REMOTE_CRIT); if (val < 0) return val; - data->temp8[REMOTE2_CRIT] = val; + data->temp[REMOTE2_CRIT] = val << 8; val = lm90_read_reg(client, MAX6659_REG_REMOTE_EMERG); if (val < 0) return val; - data->temp8[REMOTE2_EMERG] = val; + data->temp[REMOTE2_EMERG] = val << 8; val = lm90_read_reg(client, LM90_REG_REMOTE_LOWH); if (val < 0) return val; - data->temp11[REMOTE2_LOW] = val << 8; + data->temp[REMOTE2_LOW] = val << 8; val = lm90_read_reg(client, LM90_REG_REMOTE_HIGHH); if (val < 0) return val; - data->temp11[REMOTE2_HIGH] = val << 8; + data->temp[REMOTE2_HIGH] = val << 8; lm90_select_remote_channel(data, 0); } @@ -995,30 +994,30 @@ static int lm90_update_device(struct device *dev) val = lm90_read_reg(client, LM90_REG_LOCAL_LOW); if (val < 0) return val; - data->temp8[LOCAL_LOW] = val; + data->temp[LOCAL_LOW] = val << 8; val = lm90_read_reg(client, LM90_REG_LOCAL_HIGH); if (val < 0) return val; - data->temp8[LOCAL_HIGH] = val; + data->temp[LOCAL_HIGH] = val << 8; if (data->reg_local_ext) { val = lm90_read16(client, LM90_REG_LOCAL_TEMP, data->reg_local_ext, true); if (val < 0) return val; - data->temp11[LOCAL_TEMP] = val; + data->temp[LOCAL_TEMP] = val; } else { val = lm90_read_reg(client, LM90_REG_LOCAL_TEMP); if (val < 0) return val; - data->temp11[LOCAL_TEMP] = val << 8; + data->temp[LOCAL_TEMP] = val << 8; } val = lm90_read16(client, LM90_REG_REMOTE_TEMPH, LM90_REG_REMOTE_TEMPL, true); if (val < 0) return val; - data->temp11[REMOTE_TEMP] = val; + data->temp[REMOTE_TEMP] = val; if (data->kind == max6696) { val = lm90_select_remote_channel(data, 1); @@ -1031,7 +1030,7 @@ static int lm90_update_device(struct device *dev) lm90_select_remote_channel(data, 0); return val; } - data->temp11[REMOTE2_TEMP] = val; + data->temp[REMOTE2_TEMP] = val; lm90_select_remote_channel(data, 0); } @@ -1047,123 +1046,6 @@ static int lm90_update_device(struct device *dev) return 0; } -/* - * Conversions - * For local temperatures and limits, critical limits and the hysteresis - * value, the LM90 uses signed 8-bit values with LSB = 1 degree Celsius. - * For remote temperatures and limits, it uses signed 11-bit values with - * LSB = 0.125 degree Celsius, left-justified in 16-bit registers. Some - * Maxim chips use unsigned values. - */ - -static inline int temp_from_s8(s8 val) -{ - return val * 1000; -} - -static inline int temp_from_u8(u8 val) -{ - return val * 1000; -} - -static inline int temp_from_s16(s16 val) -{ - return val / 32 * 125; -} - -static inline int temp_from_u16(u16 val) -{ - return val / 32 * 125; -} - -static s8 temp_to_s8(long val) -{ - if (val <= -128000) - return -128; - if (val >= 127000) - return 127; - if (val < 0) - return (val - 500) / 1000; - return (val + 500) / 1000; -} - -static u8 temp_to_u8(long val) -{ - if (val <= 0) - return 0; - if (val >= 255000) - return 255; - return (val + 500) / 1000; -} - -static s16 temp_to_s16(long val) -{ - if (val <= -128000) - return 0x8000; - if (val >= 127875) - return 0x7FE0; - if (val < 0) - return (val - 62) / 125 * 32; - return (val + 62) / 125 * 32; -} - -static u8 hyst_to_reg(long val) -{ - if (val <= 0) - return 0; - if (val >= 30500) - return 31; - return (val + 500) / 1000; -} - -/* - * ADT7461 in compatibility mode is almost identical to LM90 except that - * attempts to write values that are outside the range 0 < temp < 127 are - * treated as the boundary value. - * - * ADT7461 in "extended mode" operation uses unsigned integers offset by - * 64 (e.g., 0 -> -64 degC). The range is restricted to -64..191 degC. - */ -static inline int temp_from_u8_adt7461(struct lm90_data *data, u8 val) -{ - if (data->flags & LM90_FLAG_ADT7461_EXT) - return (val - 64) * 1000; - return temp_from_s8(val); -} - -static inline int temp_from_u16_adt7461(struct lm90_data *data, u16 val) -{ - if (data->flags & LM90_FLAG_ADT7461_EXT) - return (val - 0x4000) / 64 * 250; - return temp_from_s16(val); -} - -static u8 temp_to_u8_adt7461(struct lm90_data *data, long val) -{ - if (data->flags & LM90_FLAG_ADT7461_EXT) { - val = clamp_val(val, -64000, 191000); - val += 64000; - } else if (data->flags & LM90_HAVE_UNSIGNED_TEMP) { - val = clamp_val(val, 0, 127000); - } else { - val = clamp_val(val, -128000, 127000); - } - return DIV_ROUND_CLOSEST(val, 1000); -} - -static u16 temp_to_u16_adt7461(struct lm90_data *data, long val) -{ - if (data->flags & LM90_FLAG_ADT7461_EXT) { - val = clamp_val(val, -64000, 191000); - val += 64000; - } else if (data->flags & LM90_HAVE_UNSIGNED_TEMP) { - val = clamp_val(val, 0, 127000); - } else { - val = clamp_val(val, -128000, 127000); - } - return DIV_ROUND_CLOSEST(val, 1000) & 0xfff0; -} - /* pec used for devices with PEC support */ static ssize_t pec_show(struct device *dev, struct device_attribute *dummy, char *buf) @@ -1200,159 +1082,141 @@ static ssize_t pec_store(struct device *dev, struct device_attribute *dummy, static DEVICE_ATTR_RW(pec); -static int lm90_get_temp11(struct lm90_data *data, int index) +static int lm90_temp_get_resolution(struct lm90_data *data, int index) { - s16 temp11 = data->temp11[index]; - int temp; + switch (index) { + case REMOTE_TEMP: + case REMOTE_OFFSET: + case REMOTE2_TEMP: + return data->resolution; + case LOCAL_TEMP: + if (data->reg_local_ext) + return data->resolution; + return 8; + case REMOTE_LOW: + case REMOTE_HIGH: + case REMOTE2_LOW: + case REMOTE2_HIGH: + if (data->flags & LM90_HAVE_REM_LIMIT_EXT) + return data->resolution; + return 8; + default: + return 8; + } +} - if (data->flags & LM90_HAVE_EXTENDED_TEMP) - temp = temp_from_u16_adt7461(data, temp11); - else if (data->flags & LM90_HAVE_UNSIGNED_TEMP) - temp = temp_from_u16(temp11); +static int lm90_temp_from_reg(u32 flags, u16 regval, u8 resolution) +{ + int val; + + if (flags & LM90_FLAG_ADT7461_EXT) + val = regval - 0x4000; + else if (flags & LM90_HAVE_UNSIGNED_TEMP) + val = regval; else - temp = temp_from_s16(temp11); + val = (s16)regval; - /* +16 degrees offset for temp2 for the LM99 */ - if (data->kind == lm99 && index <= 2) + return ((val >> (16 - resolution)) * 1000) >> (resolution - 8); +} + +static int lm90_get_temp(struct lm90_data *data, int index, int channel) +{ + int temp = lm90_temp_from_reg(data->flags, data->temp[index], + lm90_temp_get_resolution(data, index)); + + /* +16 degrees offset for remote temperature on LM99 */ + if (data->kind == lm99 && channel) temp += 16000; return temp; } -static int lm90_set_temp11(struct lm90_data *data, int index, long val) +static u16 lm90_temp_to_reg(u32 flags, long val, u8 resolution) { - static struct reg { - u8 high; - u8 low; - } reg[] = { - [REMOTE_LOW] = { LM90_REG_REMOTE_LOWH, LM90_REG_REMOTE_LOWL }, - [REMOTE_HIGH] = { LM90_REG_REMOTE_HIGHH, LM90_REG_REMOTE_HIGHL }, - [REMOTE_OFFSET] = { LM90_REG_REMOTE_OFFSH, LM90_REG_REMOTE_OFFSL }, - [REMOTE2_LOW] = { LM90_REG_REMOTE_LOWH, LM90_REG_REMOTE_LOWL }, - [REMOTE2_HIGH] = { LM90_REG_REMOTE_HIGHH, LM90_REG_REMOTE_HIGHL } + int fraction = resolution > 8 ? + 1000 - DIV_ROUND_CLOSEST(1000, BIT(resolution - 8)) : 0; + + if (flags & LM90_FLAG_ADT7461_EXT) { + val = clamp_val(val, -64000, 191000 + fraction); + val += 64000; + } else if (flags & LM90_HAVE_UNSIGNED_TEMP) { + val = clamp_val(val, 0, 127000 + fraction); + } else { + val = clamp_val(val, -128000, 127000 + fraction); + } + + return DIV_ROUND_CLOSEST(val << (resolution - 8), 1000) << (16 - resolution); +} + +static int lm90_set_temp(struct lm90_data *data, int index, int channel, long val) +{ + static const u8 regs[] = { + [LOCAL_LOW] = LM90_REG_LOCAL_LOW, + [LOCAL_HIGH] = LM90_REG_LOCAL_HIGH, + [LOCAL_CRIT] = LM90_REG_LOCAL_CRIT, + [REMOTE_CRIT] = LM90_REG_REMOTE_CRIT, + [LOCAL_EMERG] = MAX6659_REG_LOCAL_EMERG, + [REMOTE_EMERG] = MAX6659_REG_REMOTE_EMERG, + [REMOTE2_CRIT] = LM90_REG_REMOTE_CRIT, + [REMOTE2_EMERG] = MAX6659_REG_REMOTE_EMERG, + [REMOTE_LOW] = LM90_REG_REMOTE_LOWH, + [REMOTE_HIGH] = LM90_REG_REMOTE_HIGHH, + [REMOTE2_LOW] = LM90_REG_REMOTE_LOWH, + [REMOTE2_HIGH] = LM90_REG_REMOTE_HIGHH, }; struct i2c_client *client = data->client; - struct reg *regp = ®[index]; + u8 regh = regs[index]; + u8 regl = 0; int err; - /* +16 degrees offset for temp2 for the LM99 */ - if (data->kind == lm99 && index <= 2) { + if (channel && (data->flags & LM90_HAVE_REM_LIMIT_EXT)) { + if (index == REMOTE_LOW || index == REMOTE2_LOW) + regl = LM90_REG_REMOTE_LOWL; + else if (index == REMOTE_HIGH || index == REMOTE2_HIGH) + regl = LM90_REG_REMOTE_HIGHL; + } + + /* +16 degrees offset for remote temperature on LM99 */ + if (data->kind == lm99 && channel) { /* prevent integer underflow */ val = max(val, -128000l); val -= 16000; } - if (data->flags & LM90_HAVE_EXTENDED_TEMP) - data->temp11[index] = temp_to_u16_adt7461(data, val); - else if (data->flags & LM90_HAVE_UNSIGNED_TEMP) - data->temp11[index] = temp_to_u8(val) << 8; - else if (data->flags & LM90_HAVE_REM_LIMIT_EXT) - data->temp11[index] = temp_to_s16(val); - else - data->temp11[index] = temp_to_s8(val) << 8; + data->temp[index] = lm90_temp_to_reg(data->flags, val, + lm90_temp_get_resolution(data, index)); - lm90_select_remote_channel(data, index >= 3); - err = lm90_write_reg(client, regp->high, data->temp11[index] >> 8); + if (channel > 1) + lm90_select_remote_channel(data, 1); + + err = lm90_write_reg(client, regh, data->temp[index] >> 8); if (err < 0) - return err; - if (data->flags & LM90_HAVE_REM_LIMIT_EXT) - err = lm90_write_reg(client, regp->low, - data->temp11[index] & 0xff); - - lm90_select_remote_channel(data, 0); - return err; -} - -static int lm90_get_temp8(struct lm90_data *data, int index) -{ - s8 temp8 = data->temp8[index]; - int temp; - - if (data->flags & LM90_HAVE_EXTENDED_TEMP) - temp = temp_from_u8_adt7461(data, temp8); - else if (data->flags & LM90_HAVE_UNSIGNED_TEMP) - temp = temp_from_u8(temp8); - else - temp = temp_from_s8(temp8); - - /* +16 degrees offset for temp2 for the LM99 */ - if (data->kind == lm99 && index == 3) - temp += 16000; - - return temp; -} - -static int lm90_set_temp8(struct lm90_data *data, int index, long val) -{ - static const u8 reg[TEMP8_REG_NUM] = { - LM90_REG_LOCAL_LOW, - LM90_REG_LOCAL_HIGH, - LM90_REG_LOCAL_CRIT, - LM90_REG_REMOTE_CRIT, - MAX6659_REG_LOCAL_EMERG, - MAX6659_REG_REMOTE_EMERG, - LM90_REG_REMOTE_CRIT, - MAX6659_REG_REMOTE_EMERG, - }; - struct i2c_client *client = data->client; - int err; - - /* +16 degrees offset for temp2 for the LM99 */ - if (data->kind == lm99 && index == 3) { - /* prevent integer underflow */ - val = max(val, -128000l); - val -= 16000; - } - - if (data->flags & LM90_HAVE_EXTENDED_TEMP) - data->temp8[index] = temp_to_u8_adt7461(data, val); - else if (data->flags & LM90_HAVE_UNSIGNED_TEMP) - data->temp8[index] = temp_to_u8(val); - else - data->temp8[index] = temp_to_s8(val); - - lm90_select_remote_channel(data, index >= 6); - err = lm90_write_reg(client, reg[index], data->temp8[index]); - lm90_select_remote_channel(data, 0); + goto deselect; + if (regl) + err = lm90_write_reg(client, regl, data->temp[index] & 0xff); +deselect: + if (channel > 1) + lm90_select_remote_channel(data, 0); return err; } -static int lm90_get_temphyst(struct lm90_data *data, int index) +static int lm90_get_temphyst(struct lm90_data *data, int index, int channel) { - int temp; + int temp = lm90_get_temp(data, index, channel); - if (data->flags & LM90_HAVE_EXTENDED_TEMP) - temp = temp_from_u8_adt7461(data, data->temp8[index]); - else if (data->flags & LM90_HAVE_UNSIGNED_TEMP) - temp = temp_from_u8(data->temp8[index]); - else - temp = temp_from_s8(data->temp8[index]); - - /* +16 degrees offset for temp2 for the LM99 */ - if (data->kind == lm99 && index == 3) - temp += 16000; - - return temp - temp_from_s8(data->temp_hyst); + return temp - data->temp_hyst * 1000; } static int lm90_set_temphyst(struct lm90_data *data, long val) { - struct i2c_client *client = data->client; - int temp; - - if (data->flags & LM90_HAVE_EXTENDED_TEMP) - temp = temp_from_u8_adt7461(data, data->temp8[LOCAL_CRIT]); - else if (data->flags & LM90_HAVE_UNSIGNED_TEMP) - temp = temp_from_u8(data->temp8[LOCAL_CRIT]); - else - temp = temp_from_s8(data->temp8[LOCAL_CRIT]); + int temp = lm90_get_temp(data, LOCAL_CRIT, 0); /* prevent integer overflow/underflow */ val = clamp_val(val, -128000l, 255000l); + data->temp_hyst = clamp_val(DIV_ROUND_CLOSEST(temp - val, 1000), 0, 31); - data->temp_hyst = hyst_to_reg(temp - val); - return lm90_write_reg(client, LM90_REG_TCRIT_HYST, data->temp_hyst); + return lm90_write_reg(data->client, LM90_REG_TCRIT_HYST, data->temp_hyst); } static const u8 lm90_temp_index[3] = { @@ -1396,7 +1260,7 @@ static int lm90_temp_read(struct device *dev, u32 attr, int channel, long *val) switch (attr) { case hwmon_temp_input: - *val = lm90_get_temp11(data, lm90_temp_index[channel]); + *val = lm90_get_temp(data, lm90_temp_index[channel], channel); break; case hwmon_temp_min_alarm: case hwmon_temp_max_alarm: @@ -1428,35 +1292,26 @@ static int lm90_temp_read(struct device *dev, u32 attr, int channel, long *val) data->alarms |= data->current_alarms; break; case hwmon_temp_min: - if (channel == 0) - *val = lm90_get_temp8(data, - lm90_temp_min_index[channel]); - else - *val = lm90_get_temp11(data, - lm90_temp_min_index[channel]); + *val = lm90_get_temp(data, lm90_temp_min_index[channel], channel); break; case hwmon_temp_max: - if (channel == 0) - *val = lm90_get_temp8(data, - lm90_temp_max_index[channel]); - else - *val = lm90_get_temp11(data, - lm90_temp_max_index[channel]); + *val = lm90_get_temp(data, lm90_temp_max_index[channel], channel); break; case hwmon_temp_crit: - *val = lm90_get_temp8(data, lm90_temp_crit_index[channel]); + *val = lm90_get_temp(data, lm90_temp_crit_index[channel], channel); break; case hwmon_temp_crit_hyst: - *val = lm90_get_temphyst(data, lm90_temp_crit_index[channel]); + *val = lm90_get_temphyst(data, lm90_temp_crit_index[channel], channel); break; case hwmon_temp_emergency: - *val = lm90_get_temp8(data, lm90_temp_emerg_index[channel]); + *val = lm90_get_temp(data, lm90_temp_emerg_index[channel], channel); break; case hwmon_temp_emergency_hyst: - *val = lm90_get_temphyst(data, lm90_temp_emerg_index[channel]); + *val = lm90_get_temphyst(data, lm90_temp_emerg_index[channel], channel); break; case hwmon_temp_offset: - *val = lm90_get_temp11(data, REMOTE_OFFSET); + *val = lm90_temp_from_reg(0, data->temp[REMOTE_OFFSET], + lm90_temp_get_resolution(data, REMOTE_OFFSET)); break; default: return -EOPNOTSUPP; @@ -1477,36 +1332,38 @@ static int lm90_temp_write(struct device *dev, u32 attr, int channel, long val) switch (attr) { case hwmon_temp_min: - if (channel == 0) - err = lm90_set_temp8(data, - lm90_temp_min_index[channel], - val); - else - err = lm90_set_temp11(data, - lm90_temp_min_index[channel], - val); + err = lm90_set_temp(data, lm90_temp_min_index[channel], + channel, val); break; case hwmon_temp_max: - if (channel == 0) - err = lm90_set_temp8(data, - lm90_temp_max_index[channel], - val); - else - err = lm90_set_temp11(data, - lm90_temp_max_index[channel], - val); + err = lm90_set_temp(data, lm90_temp_max_index[channel], + channel, val); break; case hwmon_temp_crit: - err = lm90_set_temp8(data, lm90_temp_crit_index[channel], val); + err = lm90_set_temp(data, lm90_temp_crit_index[channel], + channel, val); break; case hwmon_temp_crit_hyst: err = lm90_set_temphyst(data, val); break; case hwmon_temp_emergency: - err = lm90_set_temp8(data, lm90_temp_emerg_index[channel], val); + err = lm90_set_temp(data, lm90_temp_emerg_index[channel], + channel, val); break; case hwmon_temp_offset: - err = lm90_set_temp11(data, REMOTE_OFFSET, val); + val = lm90_temp_to_reg(0, val, + lm90_temp_get_resolution(data, REMOTE_OFFSET)); + err = i2c_smbus_write_byte_data(data->client, + LM90_REG_REMOTE_OFFSH, + val >> 8); + if (err) + break; + err = i2c_smbus_write_byte_data(data->client, + LM90_REG_REMOTE_OFFSL, + val & 0xff); + if (err) + break; + data->temp[REMOTE_OFFSET] = val; break; default: err = -EOPNOTSUPP; @@ -2035,6 +1892,7 @@ static int lm90_probe(struct i2c_client *client) * ALERT# output */ data->alert_alarms = lm90_params[data->kind].alert_alarms; + data->resolution = lm90_params[data->kind].resolution ? : 11; /* Set chip capabilities */ data->flags = lm90_params[data->kind].flags; From b977ed27c4dbbb65eb6c00a3bd89e5907d4c2042 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Mon, 22 Nov 2021 08:13:58 -0800 Subject: [PATCH 15/85] hwmon: (lm90) Use single flag to indicate extended temperature support Since temperature conversion functions are now unified, there is no need to keep "the chip supports a configurable extended temperature range" and "the chip has extended temperature range enabled" flags separate. Use a single flag instead to reflect both. Signed-off-by: Guenter Roeck --- drivers/hwmon/lm90.c | 25 ++++++++++--------------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c index f162391eafc2..9c0ad8217dd1 100644 --- a/drivers/hwmon/lm90.c +++ b/drivers/hwmon/lm90.c @@ -164,11 +164,8 @@ enum chips { adm1032, adt7461, adt7461a, g781, lm86, lm90, lm99, #define TMP461_REG_CHEN 0x16 #define TMP461_REG_DFC 0x24 -/* - * Device flags - */ -#define LM90_FLAG_ADT7461_EXT BIT(0) /* ADT7461 extended mode */ /* Device features */ +#define LM90_HAVE_EXTENDED_TEMP BIT(0) /* extended temperature support */ #define LM90_HAVE_OFFSET BIT(1) /* temperature offset register */ #define LM90_HAVE_UNSIGNED_TEMP BIT(2) /* temperatures are unsigned */ #define LM90_HAVE_REM_LIMIT_EXT BIT(3) /* extended remote limit */ @@ -176,12 +173,11 @@ enum chips { adm1032, adt7461, adt7461a, g781, lm86, lm90, lm99, #define LM90_HAVE_EMERGENCY_ALARM BIT(5)/* emergency alarm */ #define LM90_HAVE_TEMP3 BIT(6) /* 3rd temperature sensor */ #define LM90_HAVE_BROKEN_ALERT BIT(7) /* Broken alert */ -#define LM90_HAVE_EXTENDED_TEMP BIT(8) /* extended temperature support */ -#define LM90_PAUSE_FOR_CONFIG BIT(9) /* Pause conversion for config */ -#define LM90_HAVE_CRIT BIT(10) /* Chip supports CRIT/OVERT register */ -#define LM90_HAVE_CRIT_ALRM_SWP BIT(11) /* critical alarm bits swapped */ -#define LM90_HAVE_PEC BIT(12) /* Chip supports PEC */ -#define LM90_HAVE_PARTIAL_PEC BIT(13) /* Partial PEC support (adm1032)*/ +#define LM90_PAUSE_FOR_CONFIG BIT(8) /* Pause conversion for config */ +#define LM90_HAVE_CRIT BIT(9) /* Chip supports CRIT/OVERT register */ +#define LM90_HAVE_CRIT_ALRM_SWP BIT(10) /* critical alarm bits swapped */ +#define LM90_HAVE_PEC BIT(11) /* Chip supports PEC */ +#define LM90_HAVE_PARTIAL_PEC BIT(12) /* Partial PEC support (adm1032)*/ /* LM90 status */ #define LM90_STATUS_LTHRM BIT(0) /* local THERM limit tripped */ @@ -1109,7 +1105,7 @@ static int lm90_temp_from_reg(u32 flags, u16 regval, u8 resolution) { int val; - if (flags & LM90_FLAG_ADT7461_EXT) + if (flags & LM90_HAVE_EXTENDED_TEMP) val = regval - 0x4000; else if (flags & LM90_HAVE_UNSIGNED_TEMP) val = regval; @@ -1136,7 +1132,7 @@ static u16 lm90_temp_to_reg(u32 flags, long val, u8 resolution) int fraction = resolution > 8 ? 1000 - DIV_ROUND_CLOSEST(1000, BIT(resolution - 8)) : 0; - if (flags & LM90_FLAG_ADT7461_EXT) { + if (flags & LM90_HAVE_EXTENDED_TEMP) { val = clamp_val(val, -64000, 191000 + fraction); val += 64000; } else if (flags & LM90_HAVE_UNSIGNED_TEMP) { @@ -1766,9 +1762,8 @@ static int lm90_init_client(struct i2c_client *client, struct lm90_data *data) if (data->flags & LM90_HAVE_EXTENDED_TEMP) { if (of_property_read_bool(np, "ti,extended-range-enable")) config |= 0x04; - - if (config & 0x04) - data->flags |= LM90_FLAG_ADT7461_EXT; + if (!(config & 0x04)) + data->flags &= ~LM90_HAVE_EXTENDED_TEMP; } /* From c7cebce984a24cb2f6d5acfd404bd4629a2ca5fa Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Tue, 9 Nov 2021 20:38:22 -0800 Subject: [PATCH 16/85] hwmon: (lm90) Rework detect function The detect function is getting larger and larger and difficult to understand or review. Split it into per-manufacturer detect functions to improve readability. Signed-off-by: Guenter Roeck --- drivers/hwmon/lm90.c | 385 +++++++++++++++++++++++++++---------------- 1 file changed, 239 insertions(+), 146 deletions(-) diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c index 9c0ad8217dd1..efeb6bc0f76d 100644 --- a/drivers/hwmon/lm90.c +++ b/drivers/hwmon/lm90.c @@ -1500,78 +1500,88 @@ static umode_t lm90_is_visible(const void *data, enum hwmon_sensor_types type, } } -/* Return 0 if detection is successful, -ENODEV otherwise */ -static int lm90_detect(struct i2c_client *client, - struct i2c_board_info *info) +/* + * Per-manufacturer chip detect functions. + * Functions are expected to return a pointer to the chip name or NULL + * if detection was not successful. + */ + +static const char *lm90_detect_national(struct i2c_client *client, int chip_id, + int config1, int convrate) { - struct i2c_adapter *adapter = client->adapter; + int config2 = i2c_smbus_read_byte_data(client, LM90_REG_CONFIG2); int address = client->addr; const char *name = NULL; - int man_id, chip_id, config1, config2, convrate; - if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) - return -ENODEV; + if (config2 < 0) + return NULL; - /* detection and identification */ - man_id = i2c_smbus_read_byte_data(client, LM90_REG_MAN_ID); - chip_id = i2c_smbus_read_byte_data(client, LM90_REG_CHIP_ID); - config1 = i2c_smbus_read_byte_data(client, LM90_REG_CONFIG1); - convrate = i2c_smbus_read_byte_data(client, LM90_REG_CONVRATE); - if (man_id < 0 || chip_id < 0 || config1 < 0 || convrate < 0) - return -ENODEV; + if ((config1 & 0x2a) || (config2 & 0xf8) || convrate > 0x09) + return NULL; - if (man_id == 0x01 || man_id == 0x5C || man_id == 0xA1) { - config2 = i2c_smbus_read_byte_data(client, LM90_REG_CONFIG2); - if (config2 < 0) - return -ENODEV; + if (address != 0x4c && address != 0x4d) + return NULL; + + switch (chip_id & 0xf0) { + case 0x10: /* LM86 */ + if (address == 0x4c) + name = "lm86"; + break; + case 0x20: /* LM90 */ + if (address == 0x4c) + name = "lm90"; + break; + case 0x30: /* LM89/LM99 */ + name = "lm99"; /* detect LM89 as LM99 */ + break; + default: + break; } - if ((address == 0x4C || address == 0x4D) - && man_id == 0x01) { /* National Semiconductor */ - if ((config1 & 0x2A) == 0x00 - && (config2 & 0xF8) == 0x00 - && convrate <= 0x09) { - if (address == 0x4C - && (chip_id & 0xF0) == 0x20) { /* LM90 */ - name = "lm90"; - } else - if ((chip_id & 0xF0) == 0x30) { /* LM89/LM99 */ - name = "lm99"; - dev_info(&adapter->dev, - "Assuming LM99 chip at 0x%02x\n", - address); - dev_info(&adapter->dev, - "If it is an LM89, instantiate it " - "with the new_device sysfs " - "interface\n"); - } else - if (address == 0x4C - && (chip_id & 0xF0) == 0x10) { /* LM86 */ - name = "lm86"; - } - } - } else - if ((address == 0x4C || address == 0x4D) - && man_id == 0x41) { /* Analog Devices */ - if ((chip_id & 0xF0) == 0x40 /* ADM1032 */ - && (config1 & 0x3F) == 0x00 - && convrate <= 0x0A) { - name = "adm1032"; - } else - if (chip_id == 0x51 /* ADT7461 */ - && (config1 & 0x1B) == 0x00 - && convrate <= 0x0A) { - name = "adt7461"; - } else - if (chip_id == 0x57 /* ADT7461A, NCT1008 */ - && (config1 & 0x1B) == 0x00 - && convrate <= 0x0A) { - name = "adt7461a"; - } - } else - if (man_id == 0x4D) { /* Maxim */ - int emerg, emerg2, status2; + return name; +} +static const char *lm90_detect_analog(struct i2c_client *client, int chip_id, + int config1, int convrate) +{ + int address = client->addr; + const char *name = NULL; + + switch (chip_id) { + case 0x40 ... 0x4f: /* ADM1032 */ + if ((address == 0x4c || address == 0x4d) && !(config1 & 0x3f) && + convrate <= 0x0a) + name = "adm1032"; + break; + case 0x51: /* ADT7461 */ + if ((address == 0x4c || address == 0x4d) && !(config1 & 0x1b) && + convrate <= 0x0a) + name = "adt7461"; + break; + case 0x57: /* ADT7461A, NCT1008 */ + if ((address == 0x4c || address == 0x4d) && !(config1 & 0x1b) && + convrate <= 0x0a) + name = "adt7461a"; + break; + default: + break; + } + + return name; +} + +static const char *lm90_detect_maxim(struct i2c_client *client, int chip_id, + int config1, int convrate) +{ + int man_id, emerg, emerg2, status2; + int address = client->addr; + const char *name = NULL; + + if ((address >= 0x48 && address <= 0x4b) || address == 0x4f) + return NULL; + + switch (chip_id) { + case 0x01: /* * We read MAX6659_REG_REMOTE_EMERG twice, and re-read * LM90_REG_MAN_ID in between. If MAX6659_REG_REMOTE_EMERG @@ -1587,30 +1597,8 @@ static int lm90_detect(struct i2c_client *client, status2 = i2c_smbus_read_byte_data(client, MAX6696_REG_STATUS2); if (emerg < 0 || man_id < 0 || emerg2 < 0 || status2 < 0) - return -ENODEV; + return NULL; - /* - * The MAX6657, MAX6658 and MAX6659 do NOT have a chip_id - * register. Reading from that address will return the last - * read value, which in our case is those of the man_id - * register. Likewise, the config1 register seems to lack a - * low nibble, so the value will be those of the previous - * read, so in our case those of the man_id register. - * MAX6659 has a third set of upper temperature limit registers. - * Those registers also return values on MAX6657 and MAX6658, - * thus the only way to detect MAX6659 is by its address. - * For this reason it will be mis-detected as MAX6657 if its - * address is 0x4C. - */ - if (chip_id == man_id - && (address == 0x4C || address == 0x4D || address == 0x4E) - && (config1 & 0x1F) == (man_id & 0x0F) - && convrate <= 0x09) { - if (address == 0x4C) - name = "max6657"; - else - name = "max6659"; - } else /* * Even though MAX6695 and MAX6696 do not have a chip ID * register, reading it returns 0x01. Bit 4 of the config1 @@ -1622,77 +1610,137 @@ static int lm90_detect(struct i2c_client *client, * limit registers. We can detect those chips by checking if * one of those registers exists. */ - if (chip_id == 0x01 - && (config1 & 0x10) == 0x00 - && (status2 & 0x01) == 0x00 - && emerg == emerg2 - && convrate <= 0x07) { + if (!(config1 & 0x10) && !(status2 & 0x01) && emerg == emerg2 && + convrate <= 0x07) name = "max6696"; - } else /* * The chip_id register of the MAX6680 and MAX6681 holds the * revision of the chip. The lowest bit of the config1 register * is unused and should return zero when read, so should the * second to last bit of config1 (software reset). */ - if (chip_id == 0x01 - && (config1 & 0x03) == 0x00 - && convrate <= 0x07) { + else if (!(config1 & 0x03) && convrate <= 0x07) name = "max6680"; - } else - /* - * The chip_id register of the MAX6646/6647/6649 holds the - * revision of the chip. The lowest 6 bits of the config1 - * register are unused and should return zero when read. - */ - if (chip_id == 0x59 - && (config1 & 0x3f) == 0x00 - && convrate <= 0x07) { - name = "max6646"; - } else + break; + case 0x08: /* * The chip_id of the MAX6654 holds the revision of the chip. * The lowest 3 bits of the config1 register are unused and * should return zero when read. */ - if (chip_id == 0x08 - && (config1 & 0x07) == 0x00 - && convrate <= 0x07) { + if (!(config1 & 0x07) && convrate <= 0x07) name = "max6654"; + break; + case 0x4d: + /* + * The MAX6657, MAX6658 and MAX6659 do NOT have a chip_id + * register. Reading from that address will return the last + * read value, which in our case is those of the man_id + * register, or 0x4d. Likewise, the config1 register seems to + * lack a low nibble, so the value will be those of the previous + * read, so in our case again those of the man_id register. + * MAX6659 has a third set of upper temperature limit registers. + * Those registers also return values on MAX6657 and MAX6658, + * thus the only way to detect MAX6659 is by its address. + * For this reason it will be mis-detected as MAX6657 if its + * address is 0x4c. + */ + if ((address == 0x4c || address == 0x4d || address == 0x4e) && + (config1 & 0x1f) == 0x0d && convrate <= 0x09) { + if (address == 0x4c) + name = "max6657"; + else + name = "max6659"; } - } else - if (address == 0x4C - && man_id == 0x5C) { /* Winbond/Nuvoton */ - if ((config1 & 0x2A) == 0x00 - && (config2 & 0xF8) == 0x00) { - if (chip_id == 0x01 /* W83L771W/G */ - && convrate <= 0x09) { - name = "w83l771"; - } else - if ((chip_id & 0xFE) == 0x10 /* W83L771AWG/ASG */ - && convrate <= 0x08) { - name = "w83l771"; + break; + case 0x59: + /* + * The chip_id register of the MAX6646/6647/6649 holds the + * revision of the chip. The lowest 6 bits of the config1 + * register are unused and should return zero when read. + */ + if (!(config1 & 0x3f) && convrate <= 0x07) { + switch (address) { + case 0x4c: + name = "max6649"; + break; + case 0x4d: + name = "max6646"; + break; + case 0x4e: + name = "max6647"; + break; + default: + break; } } - } else - if (address >= 0x48 && address <= 0x4F - && man_id == 0xA1) { /* NXP Semiconductor/Philips */ - if (chip_id == 0x00 - && (config1 & 0x2A) == 0x00 - && (config2 & 0xFE) == 0x00 - && convrate <= 0x09) { - name = "sa56004"; + break; + default: + break; + } + + return name; +} + +static const char *lm90_detect_nuvoton(struct i2c_client *client, int chip_id, + int config1, int convrate) +{ + int config2 = i2c_smbus_read_byte_data(client, LM90_REG_CONFIG2); + int address = client->addr; + const char *name = NULL; + + if (config2 < 0) + return ERR_PTR(-ENODEV); + + if (address == 0x4c && !(config1 & 0x2a) && !(config2 & 0xf8)) { + if (chip_id == 0x01 && convrate <= 0x09) { + /* W83L771W/G */ + name = "w83l771"; + } else if ((chip_id & 0xfe) == 0x10 && convrate <= 0x08) { + /* W83L771AWG/ASG */ + name = "w83l771"; } - } else - if ((address == 0x4C || address == 0x4D) - && man_id == 0x47) { /* GMT */ - if (chip_id == 0x01 /* G781 */ - && (config1 & 0x3F) == 0x00 - && convrate <= 0x08) - name = "g781"; - } else - if (man_id == 0x55 && chip_id == 0x00 && - (config1 & 0x1B) == 0x00 && convrate <= 0x09) { + } + return name; +} + +static const char *lm90_detect_nxp(struct i2c_client *client, int chip_id, + int config1, int convrate) +{ + int config2 = i2c_smbus_read_byte_data(client, LM90_REG_CONFIG2); + int address = client->addr; + const char *name = NULL; + + if (config2 < 0) + return NULL; + + if (address >= 0x48 && address <= 0x4f && chip_id == 0x00 && + !(config1 & 0x2a) && !(config2 & 0xfe) && convrate <= 0x09) + name = "sa56004"; + + return name; +} + +static const char *lm90_detect_gmt(struct i2c_client *client, int chip_id, + int config1, int convrate) +{ + int address = client->addr; + const char *name = NULL; + + if ((address == 0x4c || address == 0x4d) && chip_id == 0x01 && + !(config1 & 0x3f) && convrate <= 0x08) + name = "g781"; + + return name; +} + +static const char *lm90_detect_ti(struct i2c_client *client, int chip_id, + int config1, int convrate) +{ + int address = client->addr; + const char *name = NULL; + + if (chip_id == 0x00 && !(config1 & 0x1b) && convrate <= 0x09) { int local_ext, conalert, chen, dfc; local_ext = i2c_smbus_read_byte_data(client, @@ -1702,10 +1750,8 @@ static int lm90_detect(struct i2c_client *client, chen = i2c_smbus_read_byte_data(client, TMP461_REG_CHEN); dfc = i2c_smbus_read_byte_data(client, TMP461_REG_DFC); - if ((local_ext & 0x0F) == 0x00 && - (conalert & 0xf1) == 0x01 && - (chen & 0xfc) == 0x00 && - (dfc & 0xfc) == 0x00) { + if (!(local_ext & 0x0f) && (conalert & 0xf1) == 0x01 && + (chen & 0xfc) == 0x00 && (dfc & 0xfc) == 0x00) { if (address == 0x4c && !(chen & 0x03)) name = "tmp451"; else if (address >= 0x48 && address <= 0x4f) @@ -1713,10 +1759,57 @@ static int lm90_detect(struct i2c_client *client, } } - if (!name) { /* identification failed */ + return name; +} + +/* Return 0 if detection is successful, -ENODEV otherwise */ +static int lm90_detect(struct i2c_client *client, struct i2c_board_info *info) +{ + struct i2c_adapter *adapter = client->adapter; + int man_id, chip_id, config1, convrate; + const char *name = NULL; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -ENODEV; + + /* detection and identification */ + man_id = i2c_smbus_read_byte_data(client, LM90_REG_MAN_ID); + chip_id = i2c_smbus_read_byte_data(client, LM90_REG_CHIP_ID); + config1 = i2c_smbus_read_byte_data(client, LM90_REG_CONFIG1); + convrate = i2c_smbus_read_byte_data(client, LM90_REG_CONVRATE); + if (man_id < 0 || chip_id < 0 || config1 < 0 || convrate < 0) + return -ENODEV; + + switch (man_id) { + case 0x01: /* National Semiconductor */ + name = lm90_detect_national(client, chip_id, config1, convrate); + break; + case 0x41: /* Analog Devices */ + name = lm90_detect_analog(client, chip_id, config1, convrate); + break; + case 0x47: /* GMT */ + name = lm90_detect_gmt(client, chip_id, config1, convrate); + break; + case 0x4d: /* Maxim Integrated */ + name = lm90_detect_maxim(client, chip_id, config1, convrate); + break; + case 0x55: /* TI */ + name = lm90_detect_ti(client, chip_id, config1, convrate); + break; + case 0x5c: /* Winbond/Nuvoton */ + name = lm90_detect_nuvoton(client, chip_id, config1, convrate); + break; + case 0xa1: /* NXP Semiconductor/Philips */ + name = lm90_detect_nxp(client, chip_id, config1, convrate); + break; + default: + break; + } + + if (!name) { /* identification failed */ dev_dbg(&adapter->dev, - "Unsupported chip at 0x%02x (man_id=0x%02X, " - "chip_id=0x%02X)\n", address, man_id, chip_id); + "Unsupported chip at 0x%02x (man_id=0x%02X, chip_id=0x%02X)\n", + client->addr, man_id, chip_id); return -ENODEV; } From d277fbd53d9d0cd6fc35a8d9003b2e2176fb4948 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Mon, 20 Dec 2021 17:02:36 -0800 Subject: [PATCH 17/85] hwmon: (lm90) Add support for additional chip revision of NCT1008 The NCT1008 datasheet, Revision 3, states that its chip revision is 0x57. This matches the ADT7461A chip revision, and NCT1008 is therefore detected as ADT7461A. In revision 6 of the datasheet, the chip revision register is no longer documented. Multiple samples of NCT1008 were found to report a chip revision of 0x54. As it turns out, one of the patches submitted to add NCT1008 support to the lm90 driver already included a check for chip revision 0x54. Unfortunately, that patch never made it into the kernel. Remedy the situation and explicitly detect chips with revision 0x54 as NCT1008. Signed-off-by: Guenter Roeck --- drivers/hwmon/lm90.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c index efeb6bc0f76d..c3ccf1fb2758 100644 --- a/drivers/hwmon/lm90.c +++ b/drivers/hwmon/lm90.c @@ -1558,7 +1558,12 @@ static const char *lm90_detect_analog(struct i2c_client *client, int chip_id, convrate <= 0x0a) name = "adt7461"; break; - case 0x57: /* ADT7461A, NCT1008 */ + case 0x54: /* NCT1008 */ + if ((address == 0x4c || address == 0x4d) && !(config1 & 0x1b) && + convrate <= 0x0a) + name = "nct1008"; + break; + case 0x57: /* ADT7461A, NCT1008 (datasheet rev. 3) */ if ((address == 0x4c || address == 0x4d) && !(config1 & 0x1b) && convrate <= 0x0a) name = "adt7461a"; From 0707dda69d85a3c6f5155e5c33e92b49f389e552 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Thu, 2 Dec 2021 16:19:27 -0800 Subject: [PATCH 18/85] hwmon: (lm90) Fix/Add detection of G781-1 When support for G781 was added, chips with ID 0x01 were found at I2C addresses 0x4c and 0x4d. The G781 datasheet (version 1.3 from October 2003) says that the device ID for G781-1 is 0x03, not 0x01. Also, the datasheet states that the chip at I2C address is G781 and the chip at I2C address 0x4d is G781-1. A G781-1 at I2C address 0x4d was now found to have a chip ID of 0x03 as suggested by the datasheet. Accept both 0x01 and 0x03 chip IDs at both addresses to ensure that all variants of G781 are detected properly. While at it, improve chip detection accuracy by reading two additional registers and ensuring that only expected bits are set in those registers. Signed-off-by: Guenter Roeck --- drivers/hwmon/lm90.c | 39 ++++++++++++++++++++++++++++++++++----- 1 file changed, 34 insertions(+), 5 deletions(-) diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c index c3ccf1fb2758..f9bc20cf24c3 100644 --- a/drivers/hwmon/lm90.c +++ b/drivers/hwmon/lm90.c @@ -1730,13 +1730,42 @@ static const char *lm90_detect_gmt(struct i2c_client *client, int chip_id, int config1, int convrate) { int address = client->addr; - const char *name = NULL; - if ((address == 0x4c || address == 0x4d) && chip_id == 0x01 && - !(config1 & 0x3f) && convrate <= 0x08) - name = "g781"; + /* + * According to the datasheet, G781 is supposed to be at I2C Address + * 0x4c and have a chip ID of 0x01. G781-1 is supposed to be at I2C + * address 0x4d and have a chip ID of 0x03. However, when support + * for G781 was added, chips at 0x4c and 0x4d were found to have a + * chip ID of 0x01. A G781-1 at I2C address 0x4d was now found with + * chip ID 0x03. + * To avoid detection failures, accept chip ID 0x01 and 0x03 at both + * addresses. + * G784 reports manufacturer ID 0x47 and chip ID 0x01. A public + * datasheet is not available. Extensive testing suggests that + * the chip appears to be fully compatible with G781. + * Available register dumps show that G751 also reports manufacturer + * ID 0x47 and chip ID 0x01 even though that chip does not officially + * support those registers. This makes chip detection somewhat + * vulnerable. To improve detection quality, read the offset low byte + * and alert fault queue registers and verify that only expected bits + * are set. + */ + if ((chip_id == 0x01 || chip_id == 0x03) && + (address == 0x4c || address == 0x4d) && + !(config1 & 0x3f) && convrate <= 0x08) { + int reg; - return name; + reg = i2c_smbus_read_byte_data(client, LM90_REG_REMOTE_OFFSL); + if (reg < 0 || reg & 0x1f) + return NULL; + reg = i2c_smbus_read_byte_data(client, TMP451_REG_CONALERT); + if (reg < 0 || reg & 0xf1) + return NULL; + + return "g781"; + } + + return NULL; } static const char *lm90_detect_ti(struct i2c_client *client, int chip_id, From e9684fdbdc525950eb006cfad195aaaf46cf5310 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Wed, 17 Nov 2021 11:57:57 -0800 Subject: [PATCH 19/85] hwmon: (lm90) Add flag to indicate 'alarms' attribute support We don't want to support the obsolete 'alarms' attribute for new chips supported by this driver. Add flag to indicate 'alarms' attribute support and use it for existing chips. This flag will not be set for additional chips supported by this driver in the future. Signed-off-by: Guenter Roeck --- drivers/hwmon/lm90.c | 57 +++++++++++++++++++++++++++++--------------- 1 file changed, 38 insertions(+), 19 deletions(-) diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c index f9bc20cf24c3..e6a9c6832fc6 100644 --- a/drivers/hwmon/lm90.c +++ b/drivers/hwmon/lm90.c @@ -178,6 +178,7 @@ enum chips { adm1032, adt7461, adt7461a, g781, lm86, lm90, lm99, #define LM90_HAVE_CRIT_ALRM_SWP BIT(10) /* critical alarm bits swapped */ #define LM90_HAVE_PEC BIT(11) /* Chip supports PEC */ #define LM90_HAVE_PARTIAL_PEC BIT(12) /* Partial PEC support (adm1032)*/ +#define LM90_HAVE_ALARMS BIT(13) /* Create 'alarms' attribute */ /* LM90 status */ #define LM90_STATUS_LTHRM BIT(0) /* local THERM limit tripped */ @@ -347,7 +348,7 @@ static const struct lm90_params lm90_params[] = { [adm1032] = { .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT | LM90_HAVE_BROKEN_ALERT | LM90_HAVE_CRIT - | LM90_HAVE_PARTIAL_PEC, + | LM90_HAVE_PARTIAL_PEC | LM90_HAVE_ALARMS, .alert_alarms = 0x7c, .max_convrate = 10, }, @@ -359,7 +360,8 @@ static const struct lm90_params lm90_params[] = { */ .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT | LM90_HAVE_BROKEN_ALERT | LM90_HAVE_EXTENDED_TEMP - | LM90_HAVE_CRIT | LM90_HAVE_PARTIAL_PEC, + | LM90_HAVE_CRIT | LM90_HAVE_PARTIAL_PEC + | LM90_HAVE_ALARMS, .alert_alarms = 0x7c, .max_convrate = 10, .resolution = 10, @@ -367,55 +369,58 @@ static const struct lm90_params lm90_params[] = { [adt7461a] = { .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT | LM90_HAVE_BROKEN_ALERT | LM90_HAVE_EXTENDED_TEMP - | LM90_HAVE_CRIT | LM90_HAVE_PEC, + | LM90_HAVE_CRIT | LM90_HAVE_PEC | LM90_HAVE_ALARMS, .alert_alarms = 0x7c, .max_convrate = 10, }, [g781] = { .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT - | LM90_HAVE_BROKEN_ALERT | LM90_HAVE_CRIT, + | LM90_HAVE_BROKEN_ALERT | LM90_HAVE_CRIT + | LM90_HAVE_ALARMS, .alert_alarms = 0x7c, .max_convrate = 7, }, [lm86] = { .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT - | LM90_HAVE_CRIT, + | LM90_HAVE_CRIT | LM90_HAVE_ALARMS, .alert_alarms = 0x7b, .max_convrate = 9, }, [lm90] = { .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT - | LM90_HAVE_CRIT, + | LM90_HAVE_CRIT | LM90_HAVE_ALARMS, .alert_alarms = 0x7b, .max_convrate = 9, }, [lm99] = { .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT - | LM90_HAVE_CRIT, + | LM90_HAVE_CRIT | LM90_HAVE_ALARMS, .alert_alarms = 0x7b, .max_convrate = 9, }, [max6646] = { .flags = LM90_HAVE_CRIT | LM90_HAVE_BROKEN_ALERT - | LM90_HAVE_UNSIGNED_TEMP, + | LM90_HAVE_UNSIGNED_TEMP | LM90_HAVE_ALARMS, .alert_alarms = 0x7c, .max_convrate = 6, .reg_local_ext = MAX6657_REG_LOCAL_TEMPL, }, [max6654] = { - .flags = LM90_HAVE_BROKEN_ALERT, + .flags = LM90_HAVE_BROKEN_ALERT | LM90_HAVE_ALARMS, .alert_alarms = 0x7c, .max_convrate = 7, .reg_local_ext = MAX6657_REG_LOCAL_TEMPL, }, [max6657] = { - .flags = LM90_PAUSE_FOR_CONFIG | LM90_HAVE_CRIT, + .flags = LM90_PAUSE_FOR_CONFIG | LM90_HAVE_CRIT + | LM90_HAVE_ALARMS, .alert_alarms = 0x7c, .max_convrate = 8, .reg_local_ext = MAX6657_REG_LOCAL_TEMPL, }, [max6659] = { - .flags = LM90_HAVE_EMERGENCY | LM90_HAVE_CRIT, + .flags = LM90_HAVE_EMERGENCY | LM90_HAVE_CRIT + | LM90_HAVE_ALARMS, .alert_alarms = 0x7c, .max_convrate = 8, .reg_local_ext = MAX6657_REG_LOCAL_TEMPL, @@ -427,19 +432,22 @@ static const struct lm90_params lm90_params[] = { * be set). */ .flags = LM90_HAVE_OFFSET | LM90_HAVE_CRIT - | LM90_HAVE_CRIT_ALRM_SWP | LM90_HAVE_BROKEN_ALERT, + | LM90_HAVE_CRIT_ALRM_SWP | LM90_HAVE_BROKEN_ALERT + | LM90_HAVE_ALARMS, .alert_alarms = 0x7c, .max_convrate = 7, }, [max6696] = { .flags = LM90_HAVE_EMERGENCY - | LM90_HAVE_EMERGENCY_ALARM | LM90_HAVE_TEMP3 | LM90_HAVE_CRIT, + | LM90_HAVE_EMERGENCY_ALARM | LM90_HAVE_TEMP3 | LM90_HAVE_CRIT + | LM90_HAVE_ALARMS, .alert_alarms = 0x1c7c, .max_convrate = 6, .reg_local_ext = MAX6657_REG_LOCAL_TEMPL, }, [w83l771] = { - .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT | LM90_HAVE_CRIT, + .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT | LM90_HAVE_CRIT + | LM90_HAVE_ALARMS, .alert_alarms = 0x7c, .max_convrate = 8, }, @@ -449,7 +457,8 @@ static const struct lm90_params lm90_params[] = { * and treated as negative temperatures (meaning min_alarm will * be set). */ - .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT | LM90_HAVE_CRIT, + .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT | LM90_HAVE_CRIT + | LM90_HAVE_ALARMS, .alert_alarms = 0x7b, .max_convrate = 9, .reg_local_ext = SA56004_REG_LOCAL_TEMPL, @@ -457,7 +466,7 @@ static const struct lm90_params lm90_params[] = { [tmp451] = { .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT | LM90_HAVE_BROKEN_ALERT | LM90_HAVE_EXTENDED_TEMP | LM90_HAVE_CRIT - | LM90_HAVE_UNSIGNED_TEMP, + | LM90_HAVE_UNSIGNED_TEMP | LM90_HAVE_ALARMS, .alert_alarms = 0x7c, .max_convrate = 9, .resolution = 12, @@ -465,7 +474,8 @@ static const struct lm90_params lm90_params[] = { }, [tmp461] = { .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT - | LM90_HAVE_BROKEN_ALERT | LM90_HAVE_EXTENDED_TEMP | LM90_HAVE_CRIT, + | LM90_HAVE_BROKEN_ALERT | LM90_HAVE_EXTENDED_TEMP | LM90_HAVE_CRIT + | LM90_HAVE_ALARMS, .alert_alarms = 0x7c, .max_convrate = 9, .resolution = 12, @@ -505,7 +515,9 @@ enum lm90_temp_reg_index { struct lm90_data { struct i2c_client *client; struct device *hwmon_dev; + u32 chip_config[2]; u32 channel_config[4]; + struct hwmon_channel_info chip_info; struct hwmon_channel_info temp_info; const struct hwmon_channel_info *info[3]; struct hwmon_chip_info chip; @@ -2030,8 +2042,15 @@ static int lm90_probe(struct i2c_client *client) data->chip.ops = &lm90_ops; data->chip.info = data->info; - data->info[0] = HWMON_CHANNEL_INFO(chip, - HWMON_C_REGISTER_TZ | HWMON_C_UPDATE_INTERVAL | HWMON_C_ALARMS); + data->info[0] = &data->chip_info; + info = &data->chip_info; + info->type = hwmon_chip; + info->config = data->chip_config; + + data->chip_config[0] = HWMON_C_REGISTER_TZ | HWMON_C_UPDATE_INTERVAL; + if (data->flags & LM90_HAVE_ALARMS) + data->chip_config[0] |= HWMON_C_ALARMS; + data->info[1] = &data->temp_info; info = &data->temp_info; From 904a6fe689e28abff09c6b039a3dacec5a586c91 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Fri, 5 Nov 2021 21:58:51 -0700 Subject: [PATCH 20/85] hwmon: (lm90) Add explicit support for MAX6648/MAX6692 Unlike MAX6646/MAX6647/MAX6649, MAX6648 and MAX6692 only support a temperature range of 0..127 degrees C. Separate support for the two sets of chips to be able to support maximum temperature ranges correctly for all chips. Introduce new feature flag to indicate temperature support up to 255 degrees C. Since the chips are almost identical except for the supported temperature range, automatic chip detection is limited. Effectively this means that MAX6648 may be mis-detected as MAX6649 when auto-detected, but there is nothing we can do about that. Devicetree nodes are not added for the added chips since it is quite unlikely that such old chips will ever be used in a devicetree based system. They can be added later if needed. Signed-off-by: Guenter Roeck --- Documentation/hwmon/lm90.rst | 12 +++++++++-- drivers/hwmon/lm90.c | 41 ++++++++++++++++++++++++++++++++---- 2 files changed, 47 insertions(+), 6 deletions(-) diff --git a/Documentation/hwmon/lm90.rst b/Documentation/hwmon/lm90.rst index 9886a298797f..e947e609990b 100644 --- a/Documentation/hwmon/lm90.rst +++ b/Documentation/hwmon/lm90.rst @@ -105,7 +105,7 @@ Supported chips: * Maxim MAX6648 - Prefix: 'max6646' + Prefix: 'max6648' Addresses scanned: I2C 0x4c @@ -191,7 +191,7 @@ Supported chips: * Maxim MAX6692 - Prefix: 'max6646' + Prefix: 'max6648' Addresses scanned: I2C 0x4c @@ -324,6 +324,14 @@ ADT7461, ADT7461A, NCT1008: * Lower resolution for remote temperature * SMBus PEC support for Write Byte and Receive Byte transactions. +MAX6646, MAX6647, MAX6649: + * Better local resolution + * Extended range unsigned external temperature + +MAX6648, MAX6692: + * Better local resolution + * Unsigned temperature + MAX6654: * Better local resolution * Selectable address diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c index e6a9c6832fc6..fded11b0003e 100644 --- a/drivers/hwmon/lm90.c +++ b/drivers/hwmon/lm90.c @@ -115,7 +115,7 @@ static const unsigned short normal_i2c[] = { 0x4d, 0x4e, 0x4f, I2C_CLIENT_END }; enum chips { adm1032, adt7461, adt7461a, g781, lm86, lm90, lm99, - max6646, max6654, max6657, max6659, max6680, max6696, + max6646, max6648, max6654, max6657, max6659, max6680, max6696, sa56004, tmp451, tmp461, w83l771, }; @@ -179,6 +179,7 @@ enum chips { adm1032, adt7461, adt7461a, g781, lm86, lm90, lm99, #define LM90_HAVE_PEC BIT(11) /* Chip supports PEC */ #define LM90_HAVE_PARTIAL_PEC BIT(12) /* Partial PEC support (adm1032)*/ #define LM90_HAVE_ALARMS BIT(13) /* Create 'alarms' attribute */ +#define LM90_HAVE_EXT_UNSIGNED BIT(14) /* extended unsigned temperature*/ /* LM90 status */ #define LM90_STATUS_LTHRM BIT(0) /* local THERM limit tripped */ @@ -213,6 +214,7 @@ static const struct i2c_device_id lm90_id[] = { { "lm99", lm99 }, { "max6646", max6646 }, { "max6647", max6646 }, + { "max6648", max6648 }, { "max6649", max6646 }, { "max6654", max6654 }, { "max6657", max6657 }, @@ -220,6 +222,7 @@ static const struct i2c_device_id lm90_id[] = { { "max6659", max6659 }, { "max6680", max6680 }, { "max6681", max6680 }, + { "max6692", max6648 }, { "max6695", max6696 }, { "max6696", max6696 }, { "nct1008", adt7461a }, @@ -400,7 +403,14 @@ static const struct lm90_params lm90_params[] = { }, [max6646] = { .flags = LM90_HAVE_CRIT | LM90_HAVE_BROKEN_ALERT - | LM90_HAVE_UNSIGNED_TEMP | LM90_HAVE_ALARMS, + | LM90_HAVE_EXT_UNSIGNED | LM90_HAVE_ALARMS, + .alert_alarms = 0x7c, + .max_convrate = 6, + .reg_local_ext = MAX6657_REG_LOCAL_TEMPL, + }, + [max6648] = { + .flags = LM90_HAVE_UNSIGNED_TEMP | LM90_HAVE_CRIT + | LM90_HAVE_BROKEN_ALERT, .alert_alarms = 0x7c, .max_convrate = 6, .reg_local_ext = MAX6657_REG_LOCAL_TEMPL, @@ -1119,7 +1129,7 @@ static int lm90_temp_from_reg(u32 flags, u16 regval, u8 resolution) if (flags & LM90_HAVE_EXTENDED_TEMP) val = regval - 0x4000; - else if (flags & LM90_HAVE_UNSIGNED_TEMP) + else if (flags & (LM90_HAVE_UNSIGNED_TEMP | LM90_HAVE_EXT_UNSIGNED)) val = regval; else val = (s16)regval; @@ -1147,6 +1157,8 @@ static u16 lm90_temp_to_reg(u32 flags, long val, u8 resolution) if (flags & LM90_HAVE_EXTENDED_TEMP) { val = clamp_val(val, -64000, 191000 + fraction); val += 64000; + } else if (flags & LM90_HAVE_EXT_UNSIGNED) { + val = clamp_val(val, 0, 255000 + fraction); } else if (flags & LM90_HAVE_UNSIGNED_TEMP) { val = clamp_val(val, 0, 127000 + fraction); } else { @@ -1675,11 +1687,32 @@ static const char *lm90_detect_maxim(struct i2c_client *client, int chip_id, * The chip_id register of the MAX6646/6647/6649 holds the * revision of the chip. The lowest 6 bits of the config1 * register are unused and should return zero when read. + * The I2C address of MAX6648/6692 is fixed at 0x4c. + * MAX6646 is at address 0x4d, MAX6647 is at address 0x4e, + * and MAX6649 is at address 0x4c. A slight difference between + * the two sets of chips is that the remote temperature register + * reports different values if the DXP pin is open or shorted. + * We can use that information to help distinguish between the + * chips. MAX6648 will be mis-detected as MAX6649 if the remote + * diode is connected, but there isn't really anything we can + * do about that. */ if (!(config1 & 0x3f) && convrate <= 0x07) { + int temp; + switch (address) { case 0x4c: - name = "max6649"; + /* + * MAX6649 reports an external temperature + * value of 0xff if DXP is open or shorted. + * MAX6648 reports 0x80 in that case. + */ + temp = i2c_smbus_read_byte_data(client, + LM90_REG_REMOTE_TEMPH); + if (temp == 0x80) + name = "max6648"; + else + name = "max6649"; break; case 0x4d: name = "max6646"; From a9f3d3a80a405249e18f9bf85271e4cee5c4160f Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sat, 6 Nov 2021 09:59:26 -0700 Subject: [PATCH 21/85] hwmon: (lm90) Add support for ADT7481, ADT7482, and ADT7483 ADT7481, ADT7482, and ADT7483 are similar to ADT7461, but support two external temperature sensors, similar to MAX6695/6696. They support an extended temperature range similar to ADT7461. Registers for the second external channel can be accessed directly or by using the same method as used by MAX6695/6696. For simplicity, the access method implemented for MAX6695/6696 is used. The chips support PEC (packet error checking). Set the PEC feature flag and let the user decide if it should be enabled or not (it is by default disabled). Even though it is only documented for ADT7483, all three chips support a secondary manufacturer ID register at 0x3e and a chip ID register at 0x3f. Use the contents of those registers register for improved chip detection accuracy. Add the same check to the ADT7461A detection code since this chip also supports the same (undocumented) registers. Devicetree nodes are not added for the added chips since it is quite unlikely that such old chips will ever be used in a devicetree based system. They can be added later if needed. Reviewed-by: Slawomir Stepien Signed-off-by: Guenter Roeck --- Documentation/hwmon/lm90.rst | 38 +++++++++++ drivers/hwmon/Kconfig | 3 +- drivers/hwmon/lm90.c | 119 ++++++++++++++++++++++++++--------- 3 files changed, 129 insertions(+), 31 deletions(-) diff --git a/Documentation/hwmon/lm90.rst b/Documentation/hwmon/lm90.rst index e947e609990b..53429f79b819 100644 --- a/Documentation/hwmon/lm90.rst +++ b/Documentation/hwmon/lm90.rst @@ -73,6 +73,36 @@ Supported chips: https://www.onsemi.com/PowerSolutions/product.do?id=ADT7461A + * Analog Devices ADT7481 + + Prefix: 'adt7481' + + Addresses scanned: I2C 0x4b and 0x4c + + Datasheet: Publicly available at the ON Semiconductor website + + https://www.onsemi.com/PowerSolutions/product.do?id=ADT7481 + + * Analog Devices ADT7482 + + Prefix: 'adt7482' + + Addresses scanned: I2C 0x4c + + Datasheet: Publicly available at the ON Semiconductor website + + https://www.onsemi.com/PowerSolutions/product.do?id=ADT7482 + + * Analog Devices ADT7483A + + Prefix: 'adt7483a' + + Addresses scanned: I2C 0x18, 0x19, 0x1a, 0x29, 0x2a, 0x2b, 0x4c, 0x4d, 0x4e + + Datasheet: Publicly available at the ON Semiconductor website + + https://www.onsemi.com/PowerSolutions/product.do?id=ADT7483A + * ON Semiconductor NCT1008 Prefix: 'nct1008' @@ -323,6 +353,14 @@ ADT7461, ADT7461A, NCT1008: * Extended temperature range (breaks compatibility) * Lower resolution for remote temperature * SMBus PEC support for Write Byte and Receive Byte transactions. + * 10 bit temperature resolution + +ADT7481, ADT7482, ADT7483: + * Temperature offset register + * SMBus PEC support + * 10 bit temperature resolution for external sensors + * Two remote sensors + * Selectable address (ADT7483) MAX6646, MAX6647, MAX6649: * Better local resolution diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 590d3d550acb..df54628bd36b 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -1358,7 +1358,8 @@ config SENSORS_LM90 depends on I2C help If you say yes here you get support for National Semiconductor LM90, - LM86, LM89 and LM99, Analog Devices ADM1032, ADT7461, and ADT7461A, + LM86, LM89 and LM99, Analog Devices ADM1032, ADT7461, ADT7461A, + ADT7481, ADT7482, and ADT7483A, Maxim MAX6646, MAX6647, MAX6648, MAX6649, MAX6654, MAX6657, MAX6658, MAX6659, MAX6680, MAX6681, MAX6692, MAX6695, MAX6696, ON Semiconductor NCT1008, Winbond/Nuvoton W83L771W/G/AWG/ASG, diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c index fded11b0003e..a915d0356d57 100644 --- a/drivers/hwmon/lm90.c +++ b/drivers/hwmon/lm90.c @@ -64,6 +64,10 @@ * and extended mode. They are mostly compatible with LM90 except for a data * format difference for the temperature value registers. * + * This driver also supports ADT7481, ADT7482, and ADT7483 from Analog Devices + * / ON Semiconductor. The chips are similar to ADT7461 but support two external + * temperature sensors. + * * This driver also supports the SA56004 from Philips. This device is * pin-compatible with the LM86, the ED/EDP parts are also address-compatible. * @@ -114,7 +118,7 @@ static const unsigned short normal_i2c[] = { 0x18, 0x19, 0x1a, 0x29, 0x2a, 0x2b, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, I2C_CLIENT_END }; -enum chips { adm1032, adt7461, adt7461a, g781, lm86, lm90, lm99, +enum chips { adm1032, adt7461, adt7461a, adt7481, g781, lm86, lm90, lm99, max6646, max6648, max6654, max6657, max6659, max6680, max6696, sa56004, tmp451, tmp461, w83l771, }; @@ -164,6 +168,13 @@ enum chips { adm1032, adt7461, adt7461a, g781, lm86, lm90, lm99, #define TMP461_REG_CHEN 0x16 #define TMP461_REG_DFC 0x24 +/* ADT7481 registers */ +#define ADT7481_REG_STATUS2 0x23 +#define ADT7481_REG_CONFIG2 0x24 + +#define ADT7481_REG_MAN_ID 0x3e +#define ADT7481_REG_CHIP_ID 0x3d + /* Device features */ #define LM90_HAVE_EXTENDED_TEMP BIT(0) /* extended temperature support */ #define LM90_HAVE_OFFSET BIT(1) /* temperature offset register */ @@ -191,6 +202,7 @@ enum chips { adm1032, adt7461, adt7461a, g781, lm86, lm90, lm99, #define LM90_STATUS_LHIGH BIT(6) /* local high temp limit tripped */ #define LM90_STATUS_BUSY BIT(7) /* conversion is ongoing */ +/* MAX6695/6696 and ADT7481 2nd status register */ #define MAX6696_STATUS2_R2THRM BIT(1) /* remote2 THERM limit tripped */ #define MAX6696_STATUS2_R2OPEN BIT(2) /* remote2 is an open circuit */ #define MAX6696_STATUS2_R2LOW BIT(3) /* remote2 low temp limit tripped */ @@ -207,6 +219,9 @@ static const struct i2c_device_id lm90_id[] = { { "adm1032", adm1032 }, { "adt7461", adt7461 }, { "adt7461a", adt7461a }, + { "adt7481", adt7481 }, + { "adt7482", adt7481 }, + { "adt7483a", adt7481 }, { "g781", g781 }, { "lm90", lm90 }, { "lm86", lm86 }, @@ -344,6 +359,7 @@ struct lm90_params { /* Upper 8 bits for max6695/96 */ u8 max_convrate; /* Maximum conversion rate register value */ u8 resolution; /* 16-bit resolution (default 11 bit) */ + u8 reg_status2; /* 2nd status register (optional) */ u8 reg_local_ext; /* Extended local temp register (optional) */ }; @@ -376,6 +392,16 @@ static const struct lm90_params lm90_params[] = { .alert_alarms = 0x7c, .max_convrate = 10, }, + [adt7481] = { + .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT + | LM90_HAVE_BROKEN_ALERT | LM90_HAVE_EXTENDED_TEMP + | LM90_HAVE_UNSIGNED_TEMP | LM90_HAVE_PEC + | LM90_HAVE_TEMP3 | LM90_HAVE_CRIT, + .alert_alarms = 0x1c7c, + .max_convrate = 11, + .resolution = 10, + .reg_status2 = ADT7481_REG_STATUS2, + }, [g781] = { .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT | LM90_HAVE_BROKEN_ALERT | LM90_HAVE_CRIT @@ -453,6 +479,7 @@ static const struct lm90_params lm90_params[] = { | LM90_HAVE_ALARMS, .alert_alarms = 0x1c7c, .max_convrate = 6, + .reg_status2 = MAX6696_REG_STATUS2, .reg_local_ext = MAX6657_REG_LOCAL_TEMPL, }, [w83l771] = { @@ -549,6 +576,7 @@ struct lm90_data { u16 alert_alarms; /* Which alarm bits trigger ALERT# */ /* Upper 8 bits for max6695/96 */ u8 max_convrate; /* Maximum conversion rate */ + u8 reg_status2; /* 2nd status register (optional) */ u8 reg_local_ext; /* local extension register offset */ /* registers values */ @@ -679,18 +707,14 @@ static int lm90_update_confreg(struct lm90_data *data, u8 config) * various registers have different meanings as a result of selecting a * non-default remote channel. */ -static int lm90_select_remote_channel(struct lm90_data *data, int channel) +static int lm90_select_remote_channel(struct lm90_data *data, bool second) { - int err = 0; + u8 config = data->config & ~0x08; - if (data->kind == max6696) { - u8 config = data->config & ~0x08; + if (second) + config |= 0x08; - if (channel) - config |= 0x08; - err = lm90_update_confreg(data, config); - } - return err; + return lm90_update_confreg(data, config); } static int lm90_write_convrate(struct lm90_data *data, int val) @@ -806,8 +830,8 @@ static int lm90_update_limits(struct device *dev) data->temp[REMOTE_EMERG] = val << 8; } - if (data->kind == max6696) { - val = lm90_select_remote_channel(data, 1); + if (data->flags & LM90_HAVE_TEMP3) { + val = lm90_select_remote_channel(data, true); if (val < 0) return val; @@ -816,10 +840,12 @@ static int lm90_update_limits(struct device *dev) return val; data->temp[REMOTE2_CRIT] = val << 8; - val = lm90_read_reg(client, MAX6659_REG_REMOTE_EMERG); - if (val < 0) - return val; - data->temp[REMOTE2_EMERG] = val << 8; + if (data->flags & LM90_HAVE_EMERGENCY) { + val = lm90_read_reg(client, MAX6659_REG_REMOTE_EMERG); + if (val < 0) + return val; + data->temp[REMOTE2_EMERG] = val << 8; + } val = lm90_read_reg(client, LM90_REG_REMOTE_LOWH); if (val < 0) @@ -831,7 +857,7 @@ static int lm90_update_limits(struct device *dev) return val; data->temp[REMOTE2_HIGH] = val << 8; - lm90_select_remote_channel(data, 0); + lm90_select_remote_channel(data, false); } return 0; @@ -914,8 +940,8 @@ static int lm90_update_alarms_locked(struct lm90_data *data, bool force) return val; alarms = val & ~LM90_STATUS_BUSY; - if (data->kind == max6696) { - val = lm90_read_reg(client, MAX6696_REG_STATUS2); + if (data->reg_status2) { + val = lm90_read_reg(client, data->reg_status2); if (val < 0) return val; alarms |= val << 8; @@ -1037,20 +1063,20 @@ static int lm90_update_device(struct device *dev) return val; data->temp[REMOTE_TEMP] = val; - if (data->kind == max6696) { - val = lm90_select_remote_channel(data, 1); + if (data->flags & LM90_HAVE_TEMP3) { + val = lm90_select_remote_channel(data, true); if (val < 0) return val; val = lm90_read16(client, LM90_REG_REMOTE_TEMPH, LM90_REG_REMOTE_TEMPL, true); if (val < 0) { - lm90_select_remote_channel(data, 0); + lm90_select_remote_channel(data, false); return val; } data->temp[REMOTE2_TEMP] = val; - lm90_select_remote_channel(data, 0); + lm90_select_remote_channel(data, false); } val = lm90_update_alarms_locked(data, false); @@ -1207,7 +1233,7 @@ static int lm90_set_temp(struct lm90_data *data, int index, int channel, long va lm90_temp_get_resolution(data, index)); if (channel > 1) - lm90_select_remote_channel(data, 1); + lm90_select_remote_channel(data, true); err = lm90_write_reg(client, regh, data->temp[index] >> 8); if (err < 0) @@ -1216,7 +1242,7 @@ static int lm90_set_temp(struct lm90_data *data, int index, int channel, long va err = lm90_write_reg(client, regl, data->temp[index] & 0xff); deselect: if (channel > 1) - lm90_select_remote_channel(data, 0); + lm90_select_remote_channel(data, false); return err; } @@ -1568,9 +1594,15 @@ static const char *lm90_detect_national(struct i2c_client *client, int chip_id, static const char *lm90_detect_analog(struct i2c_client *client, int chip_id, int config1, int convrate) { + int config2 = i2c_smbus_read_byte_data(client, ADT7481_REG_CONFIG2); + int man_id2 = i2c_smbus_read_byte_data(client, ADT7481_REG_MAN_ID); + int chip_id2 = i2c_smbus_read_byte_data(client, ADT7481_REG_CHIP_ID); int address = client->addr; const char *name = NULL; + if (config2 < 0 || man_id2 < 0 || chip_id2 < 0) + return NULL; + switch (chip_id) { case 0x40 ... 0x4f: /* ADM1032 */ if ((address == 0x4c || address == 0x4d) && !(config1 & 0x3f) && @@ -1592,6 +1624,28 @@ static const char *lm90_detect_analog(struct i2c_client *client, int chip_id, convrate <= 0x0a) name = "adt7461a"; break; + case 0x62: /* ADT7481, undocumented */ + if (man_id2 == 0x41 && chip_id2 == 0x81 && + (address == 0x4b || address == 0x4c) && !(config1 & 0x10) && + !(config2 & 0x7f) && (convrate & 0x0f) <= 0x0b) { + name = "adt7481"; + } + break; + case 0x65: /* ADT7482, datasheet */ + case 0x75: /* ADT7482, real chip */ + if (man_id2 == 0x41 && chip_id2 == 0x82 && + address == 0x4c && !(config1 & 0x10) && !(config2 & 0x7f) && + convrate <= 0x0a) + name = "adt7482"; + break; + case 0x94: /* ADT7483 */ + if (man_id2 == 0x41 && chip_id2 == 0x83 && + ((address >= 0x18 && address <= 0x1a) || + (address >= 0x29 && address <= 0x2b) || + (address >= 0x4c && address <= 0x4e)) && + !(config1 & 0x10) && !(config2 & 0x7f) && convrate <= 0x0a) + name = "adt7483a"; + break; default: break; } @@ -1958,9 +2012,9 @@ static int lm90_init_client(struct i2c_client *client, struct lm90_data *data) config |= 0x20; /* - * Select external channel 0 for max6695/96 + * Select external channel 0 for devices with three sensors */ - if (data->kind == max6696) + if (data->flags & LM90_HAVE_TEMP3) config &= ~0x08; /* @@ -2119,13 +2173,18 @@ static int lm90_probe(struct i2c_client *client) data->channel_config[2] = HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX | HWMON_T_CRIT | HWMON_T_CRIT_HYST | - HWMON_T_EMERGENCY | HWMON_T_EMERGENCY_HYST | HWMON_T_MIN_ALARM | HWMON_T_MAX_ALARM | - HWMON_T_CRIT_ALARM | HWMON_T_EMERGENCY_ALARM | - HWMON_T_FAULT; + HWMON_T_CRIT_ALARM | HWMON_T_FAULT; + if (data->flags & LM90_HAVE_EMERGENCY) { + data->channel_config[2] |= HWMON_T_EMERGENCY | + HWMON_T_EMERGENCY_HYST; + } + if (data->flags & LM90_HAVE_EMERGENCY_ALARM) + data->channel_config[2] |= HWMON_T_EMERGENCY_ALARM; } data->reg_local_ext = lm90_params[data->kind].reg_local_ext; + data->reg_status2 = lm90_params[data->kind].reg_status2; /* Set maximum conversion rate */ data->max_convrate = lm90_params[data->kind].max_convrate; From 9888775be8c07e134c1c5b281ee0a4e01773aed1 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Fri, 10 Dec 2021 19:35:20 -0800 Subject: [PATCH 22/85] hwmon: (lm90) Strengthen chip detection for ADM1032, ADT7461(A), and NCT1008 ADT7461A and NCT1008 support the undocumented manufacturer and chip ID registers at 0x3e and 0x3f, and return 0x61 as chip ID. ADM1032 and ADT7461 do not support those registers but return 0 when reading them. Use this information to improve the accuracy of the chip detection code. Signed-off-by: Guenter Roeck --- drivers/hwmon/lm90.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c index a915d0356d57..66d706d665bd 100644 --- a/drivers/hwmon/lm90.c +++ b/drivers/hwmon/lm90.c @@ -1605,22 +1605,26 @@ static const char *lm90_detect_analog(struct i2c_client *client, int chip_id, switch (chip_id) { case 0x40 ... 0x4f: /* ADM1032 */ - if ((address == 0x4c || address == 0x4d) && !(config1 & 0x3f) && + if (man_id2 == 0x00 && chip_id2 == 0x00 && + (address == 0x4c || address == 0x4d) && !(config1 & 0x3f) && convrate <= 0x0a) name = "adm1032"; break; case 0x51: /* ADT7461 */ - if ((address == 0x4c || address == 0x4d) && !(config1 & 0x1b) && + if (man_id2 == 0x00 && chip_id2 == 0x00 && + (address == 0x4c || address == 0x4d) && !(config1 & 0x1b) && convrate <= 0x0a) name = "adt7461"; break; case 0x54: /* NCT1008 */ - if ((address == 0x4c || address == 0x4d) && !(config1 & 0x1b) && + if (man_id2 == 0x41 && chip_id2 == 0x61 && + (address == 0x4c || address == 0x4d) && !(config1 & 0x1b) && convrate <= 0x0a) name = "nct1008"; break; case 0x57: /* ADT7461A, NCT1008 (datasheet rev. 3) */ - if ((address == 0x4c || address == 0x4d) && !(config1 & 0x1b) && + if (man_id2 == 0x41 && chip_id2 == 0x61 && + (address == 0x4c || address == 0x4d) && !(config1 & 0x1b) && convrate <= 0x0a) name = "adt7461a"; break; From 399a8a0046cfc878203b0a1c990f81c8153757f3 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Fri, 12 Nov 2021 22:03:31 -0800 Subject: [PATCH 23/85] hwmon: (lm90) Add support for MAX6690 MAX6690 is all but identical to MAX6654. Revision 1 of its datasheet lists the same chip ID as MAX6654, and a chip labeled MAX6654 was found to have the chip ID listed as MAX6690 chip ID in Revision 2 of its datasheet. A devicetree node is not added for this chip since it is quite unlikely that such an old chip will ever be used in a devicetree based system. It can be added later if needed. Signed-off-by: Guenter Roeck --- Documentation/hwmon/lm90.rst | 2 +- drivers/hwmon/lm90.c | 17 ++++++++++++++++- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/Documentation/hwmon/lm90.rst b/Documentation/hwmon/lm90.rst index 53429f79b819..45bc333a1219 100644 --- a/Documentation/hwmon/lm90.rst +++ b/Documentation/hwmon/lm90.rst @@ -370,7 +370,7 @@ MAX6648, MAX6692: * Better local resolution * Unsigned temperature -MAX6654: +MAX6654, MAX6690: * Better local resolution * Selectable address * Remote sensor type selection diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c index 66d706d665bd..1f5e2dd765d8 100644 --- a/drivers/hwmon/lm90.c +++ b/drivers/hwmon/lm90.c @@ -42,7 +42,8 @@ * accordingly, and is done during initialization. Extended precision is only * available at conversion rates of 1 Hz and slower. Note that extended * precision is not enabled by default, as this driver initializes all chips - * to 2 Hz by design. + * to 2 Hz by design. The driver also supports MAX6690, which is practically + * identical to MAX6654. * * This driver also supports the MAX6646, MAX6647, MAX6648, MAX6649 and * MAX6692 chips made by Maxim. These are again similar to the LM86, @@ -237,6 +238,7 @@ static const struct i2c_device_id lm90_id[] = { { "max6659", max6659 }, { "max6680", max6680 }, { "max6681", max6680 }, + { "max6690", max6654 }, { "max6692", max6648 }, { "max6695", max6696 }, { "max6696", max6696 }, @@ -1718,6 +1720,19 @@ static const char *lm90_detect_maxim(struct i2c_client *client, int chip_id, if (!(config1 & 0x07) && convrate <= 0x07) name = "max6654"; break; + case 0x09: + /* + * The chip_id of the MAX6690 holds the revision of the chip. + * The lowest 3 bits of the config1 register are unused and + * should return zero when read. + * Note that MAX6654 and MAX6690 are practically the same chips. + * The only diference is the rated accuracy. Rev. 1 of the + * MAX6690 datasheet lists a chip ID of 0x08, and a chip labeled + * MAX6654 was observed to have a chip ID of 0x09. + */ + if (!(config1 & 0x07) && convrate <= 0x07) + name = "max6690"; + break; case 0x4d: /* * The MAX6657, MAX6658 and MAX6659 do NOT have a chip_id From 2cb8d9d831306757b1de393f1c5b8a98b715bf15 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Mon, 15 Nov 2021 17:48:13 -0800 Subject: [PATCH 24/85] hwmon: (lm90) Add flag to indicate support for minimum temperature limits A flag indicating support for minimum temperature limits doesn't cost much and will enable us to add support for MAX6642 to the lm90 driver. Signed-off-by: Guenter Roeck --- drivers/hwmon/lm90.c | 54 +++++++++++++++++++++++++------------------- 1 file changed, 31 insertions(+), 23 deletions(-) diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c index 1f5e2dd765d8..991a7f6aae93 100644 --- a/drivers/hwmon/lm90.c +++ b/drivers/hwmon/lm90.c @@ -192,6 +192,7 @@ enum chips { adm1032, adt7461, adt7461a, adt7481, g781, lm86, lm90, lm99, #define LM90_HAVE_PARTIAL_PEC BIT(12) /* Partial PEC support (adm1032)*/ #define LM90_HAVE_ALARMS BIT(13) /* Create 'alarms' attribute */ #define LM90_HAVE_EXT_UNSIGNED BIT(14) /* extended unsigned temperature*/ +#define LM90_HAVE_LOW BIT(15) /* low limits */ /* LM90 status */ #define LM90_STATUS_LTHRM BIT(0) /* local THERM limit tripped */ @@ -369,7 +370,8 @@ static const struct lm90_params lm90_params[] = { [adm1032] = { .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT | LM90_HAVE_BROKEN_ALERT | LM90_HAVE_CRIT - | LM90_HAVE_PARTIAL_PEC | LM90_HAVE_ALARMS, + | LM90_HAVE_PARTIAL_PEC | LM90_HAVE_ALARMS + | LM90_HAVE_LOW, .alert_alarms = 0x7c, .max_convrate = 10, }, @@ -382,7 +384,7 @@ static const struct lm90_params lm90_params[] = { .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT | LM90_HAVE_BROKEN_ALERT | LM90_HAVE_EXTENDED_TEMP | LM90_HAVE_CRIT | LM90_HAVE_PARTIAL_PEC - | LM90_HAVE_ALARMS, + | LM90_HAVE_ALARMS | LM90_HAVE_LOW, .alert_alarms = 0x7c, .max_convrate = 10, .resolution = 10, @@ -390,7 +392,8 @@ static const struct lm90_params lm90_params[] = { [adt7461a] = { .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT | LM90_HAVE_BROKEN_ALERT | LM90_HAVE_EXTENDED_TEMP - | LM90_HAVE_CRIT | LM90_HAVE_PEC | LM90_HAVE_ALARMS, + | LM90_HAVE_CRIT | LM90_HAVE_PEC | LM90_HAVE_ALARMS + | LM90_HAVE_LOW, .alert_alarms = 0x7c, .max_convrate = 10, }, @@ -398,7 +401,7 @@ static const struct lm90_params lm90_params[] = { .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT | LM90_HAVE_BROKEN_ALERT | LM90_HAVE_EXTENDED_TEMP | LM90_HAVE_UNSIGNED_TEMP | LM90_HAVE_PEC - | LM90_HAVE_TEMP3 | LM90_HAVE_CRIT, + | LM90_HAVE_TEMP3 | LM90_HAVE_CRIT | LM90_HAVE_LOW, .alert_alarms = 0x1c7c, .max_convrate = 11, .resolution = 10, @@ -407,58 +410,58 @@ static const struct lm90_params lm90_params[] = { [g781] = { .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT | LM90_HAVE_BROKEN_ALERT | LM90_HAVE_CRIT - | LM90_HAVE_ALARMS, + | LM90_HAVE_ALARMS | LM90_HAVE_LOW, .alert_alarms = 0x7c, .max_convrate = 7, }, [lm86] = { .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT - | LM90_HAVE_CRIT | LM90_HAVE_ALARMS, + | LM90_HAVE_CRIT | LM90_HAVE_ALARMS | LM90_HAVE_LOW, .alert_alarms = 0x7b, .max_convrate = 9, }, [lm90] = { .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT - | LM90_HAVE_CRIT | LM90_HAVE_ALARMS, + | LM90_HAVE_CRIT | LM90_HAVE_ALARMS | LM90_HAVE_LOW, .alert_alarms = 0x7b, .max_convrate = 9, }, [lm99] = { .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT - | LM90_HAVE_CRIT | LM90_HAVE_ALARMS, + | LM90_HAVE_CRIT | LM90_HAVE_ALARMS | LM90_HAVE_LOW, .alert_alarms = 0x7b, .max_convrate = 9, }, [max6646] = { .flags = LM90_HAVE_CRIT | LM90_HAVE_BROKEN_ALERT - | LM90_HAVE_EXT_UNSIGNED | LM90_HAVE_ALARMS, + | LM90_HAVE_EXT_UNSIGNED | LM90_HAVE_ALARMS | LM90_HAVE_LOW, .alert_alarms = 0x7c, .max_convrate = 6, .reg_local_ext = MAX6657_REG_LOCAL_TEMPL, }, [max6648] = { .flags = LM90_HAVE_UNSIGNED_TEMP | LM90_HAVE_CRIT - | LM90_HAVE_BROKEN_ALERT, + | LM90_HAVE_BROKEN_ALERT | LM90_HAVE_LOW, .alert_alarms = 0x7c, .max_convrate = 6, .reg_local_ext = MAX6657_REG_LOCAL_TEMPL, }, [max6654] = { - .flags = LM90_HAVE_BROKEN_ALERT | LM90_HAVE_ALARMS, + .flags = LM90_HAVE_BROKEN_ALERT | LM90_HAVE_ALARMS | LM90_HAVE_LOW, .alert_alarms = 0x7c, .max_convrate = 7, .reg_local_ext = MAX6657_REG_LOCAL_TEMPL, }, [max6657] = { .flags = LM90_PAUSE_FOR_CONFIG | LM90_HAVE_CRIT - | LM90_HAVE_ALARMS, + | LM90_HAVE_ALARMS | LM90_HAVE_LOW, .alert_alarms = 0x7c, .max_convrate = 8, .reg_local_ext = MAX6657_REG_LOCAL_TEMPL, }, [max6659] = { .flags = LM90_HAVE_EMERGENCY | LM90_HAVE_CRIT - | LM90_HAVE_ALARMS, + | LM90_HAVE_ALARMS | LM90_HAVE_LOW, .alert_alarms = 0x7c, .max_convrate = 8, .reg_local_ext = MAX6657_REG_LOCAL_TEMPL, @@ -471,14 +474,14 @@ static const struct lm90_params lm90_params[] = { */ .flags = LM90_HAVE_OFFSET | LM90_HAVE_CRIT | LM90_HAVE_CRIT_ALRM_SWP | LM90_HAVE_BROKEN_ALERT - | LM90_HAVE_ALARMS, + | LM90_HAVE_ALARMS | LM90_HAVE_LOW, .alert_alarms = 0x7c, .max_convrate = 7, }, [max6696] = { .flags = LM90_HAVE_EMERGENCY | LM90_HAVE_EMERGENCY_ALARM | LM90_HAVE_TEMP3 | LM90_HAVE_CRIT - | LM90_HAVE_ALARMS, + | LM90_HAVE_ALARMS | LM90_HAVE_LOW, .alert_alarms = 0x1c7c, .max_convrate = 6, .reg_status2 = MAX6696_REG_STATUS2, @@ -486,7 +489,7 @@ static const struct lm90_params lm90_params[] = { }, [w83l771] = { .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT | LM90_HAVE_CRIT - | LM90_HAVE_ALARMS, + | LM90_HAVE_ALARMS | LM90_HAVE_LOW, .alert_alarms = 0x7c, .max_convrate = 8, }, @@ -497,7 +500,7 @@ static const struct lm90_params lm90_params[] = { * be set). */ .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT | LM90_HAVE_CRIT - | LM90_HAVE_ALARMS, + | LM90_HAVE_ALARMS | LM90_HAVE_LOW, .alert_alarms = 0x7b, .max_convrate = 9, .reg_local_ext = SA56004_REG_LOCAL_TEMPL, @@ -505,7 +508,7 @@ static const struct lm90_params lm90_params[] = { [tmp451] = { .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT | LM90_HAVE_BROKEN_ALERT | LM90_HAVE_EXTENDED_TEMP | LM90_HAVE_CRIT - | LM90_HAVE_UNSIGNED_TEMP | LM90_HAVE_ALARMS, + | LM90_HAVE_UNSIGNED_TEMP | LM90_HAVE_ALARMS | LM90_HAVE_LOW, .alert_alarms = 0x7c, .max_convrate = 9, .resolution = 12, @@ -514,7 +517,7 @@ static const struct lm90_params lm90_params[] = { [tmp461] = { .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT | LM90_HAVE_BROKEN_ALERT | LM90_HAVE_EXTENDED_TEMP | LM90_HAVE_CRIT - | LM90_HAVE_ALARMS, + | LM90_HAVE_ALARMS | LM90_HAVE_LOW, .alert_alarms = 0x7c, .max_convrate = 9, .resolution = 12, @@ -2163,10 +2166,15 @@ static int lm90_probe(struct i2c_client *client) info->type = hwmon_temp; info->config = data->channel_config; - data->channel_config[0] = HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX | - HWMON_T_MIN_ALARM | HWMON_T_MAX_ALARM; - data->channel_config[1] = HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX | - HWMON_T_MIN_ALARM | HWMON_T_MAX_ALARM | HWMON_T_FAULT; + data->channel_config[0] = HWMON_T_INPUT | HWMON_T_MAX | + HWMON_T_MAX_ALARM; + data->channel_config[1] = HWMON_T_INPUT | HWMON_T_MAX | + HWMON_T_MAX_ALARM | HWMON_T_FAULT; + + if (data->flags & LM90_HAVE_LOW) { + data->channel_config[0] |= HWMON_T_MIN | HWMON_T_MIN_ALARM; + data->channel_config[1] |= HWMON_T_MIN | HWMON_T_MIN_ALARM; + } if (data->flags & LM90_HAVE_CRIT) { data->channel_config[0] |= HWMON_T_CRIT | HWMON_T_CRIT_ALARM | HWMON_T_CRIT_HYST; From ca6bfa3b7325518a4b41c81c89a970976771f2ea Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Mon, 15 Nov 2021 18:14:35 -0800 Subject: [PATCH 25/85] hwmon: (lm90) Add flag to indicate conversion rate support A flag indicating support for setting the conversion rate doesn't cost much and will enable us to add support for MAX6642 to the lm90 driver. Signed-off-by: Guenter Roeck --- drivers/hwmon/lm90.c | 66 ++++++++++++++++++++++++++------------------ 1 file changed, 39 insertions(+), 27 deletions(-) diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c index 991a7f6aae93..ae14b0f60100 100644 --- a/drivers/hwmon/lm90.c +++ b/drivers/hwmon/lm90.c @@ -193,6 +193,7 @@ enum chips { adm1032, adt7461, adt7461a, adt7481, g781, lm86, lm90, lm99, #define LM90_HAVE_ALARMS BIT(13) /* Create 'alarms' attribute */ #define LM90_HAVE_EXT_UNSIGNED BIT(14) /* extended unsigned temperature*/ #define LM90_HAVE_LOW BIT(15) /* low limits */ +#define LM90_HAVE_CONVRATE BIT(16) /* conversion rate */ /* LM90 status */ #define LM90_STATUS_LTHRM BIT(0) /* local THERM limit tripped */ @@ -371,7 +372,7 @@ static const struct lm90_params lm90_params[] = { .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT | LM90_HAVE_BROKEN_ALERT | LM90_HAVE_CRIT | LM90_HAVE_PARTIAL_PEC | LM90_HAVE_ALARMS - | LM90_HAVE_LOW, + | LM90_HAVE_LOW | LM90_HAVE_CONVRATE, .alert_alarms = 0x7c, .max_convrate = 10, }, @@ -384,7 +385,7 @@ static const struct lm90_params lm90_params[] = { .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT | LM90_HAVE_BROKEN_ALERT | LM90_HAVE_EXTENDED_TEMP | LM90_HAVE_CRIT | LM90_HAVE_PARTIAL_PEC - | LM90_HAVE_ALARMS | LM90_HAVE_LOW, + | LM90_HAVE_ALARMS | LM90_HAVE_LOW | LM90_HAVE_CONVRATE, .alert_alarms = 0x7c, .max_convrate = 10, .resolution = 10, @@ -393,7 +394,7 @@ static const struct lm90_params lm90_params[] = { .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT | LM90_HAVE_BROKEN_ALERT | LM90_HAVE_EXTENDED_TEMP | LM90_HAVE_CRIT | LM90_HAVE_PEC | LM90_HAVE_ALARMS - | LM90_HAVE_LOW, + | LM90_HAVE_LOW | LM90_HAVE_CONVRATE, .alert_alarms = 0x7c, .max_convrate = 10, }, @@ -401,7 +402,8 @@ static const struct lm90_params lm90_params[] = { .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT | LM90_HAVE_BROKEN_ALERT | LM90_HAVE_EXTENDED_TEMP | LM90_HAVE_UNSIGNED_TEMP | LM90_HAVE_PEC - | LM90_HAVE_TEMP3 | LM90_HAVE_CRIT | LM90_HAVE_LOW, + | LM90_HAVE_TEMP3 | LM90_HAVE_CRIT | LM90_HAVE_LOW + | LM90_HAVE_CONVRATE, .alert_alarms = 0x1c7c, .max_convrate = 11, .resolution = 10, @@ -410,58 +412,61 @@ static const struct lm90_params lm90_params[] = { [g781] = { .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT | LM90_HAVE_BROKEN_ALERT | LM90_HAVE_CRIT - | LM90_HAVE_ALARMS | LM90_HAVE_LOW, + | LM90_HAVE_ALARMS | LM90_HAVE_LOW | LM90_HAVE_CONVRATE, .alert_alarms = 0x7c, .max_convrate = 7, }, [lm86] = { .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT - | LM90_HAVE_CRIT | LM90_HAVE_ALARMS | LM90_HAVE_LOW, + | LM90_HAVE_CRIT | LM90_HAVE_ALARMS | LM90_HAVE_LOW | LM90_HAVE_CONVRATE, .alert_alarms = 0x7b, .max_convrate = 9, }, [lm90] = { .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT - | LM90_HAVE_CRIT | LM90_HAVE_ALARMS | LM90_HAVE_LOW, + | LM90_HAVE_CRIT | LM90_HAVE_ALARMS | LM90_HAVE_LOW | LM90_HAVE_CONVRATE, .alert_alarms = 0x7b, .max_convrate = 9, }, [lm99] = { .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT - | LM90_HAVE_CRIT | LM90_HAVE_ALARMS | LM90_HAVE_LOW, + | LM90_HAVE_CRIT | LM90_HAVE_ALARMS | LM90_HAVE_LOW | LM90_HAVE_CONVRATE, .alert_alarms = 0x7b, .max_convrate = 9, }, [max6646] = { .flags = LM90_HAVE_CRIT | LM90_HAVE_BROKEN_ALERT - | LM90_HAVE_EXT_UNSIGNED | LM90_HAVE_ALARMS | LM90_HAVE_LOW, + | LM90_HAVE_EXT_UNSIGNED | LM90_HAVE_ALARMS | LM90_HAVE_LOW + | LM90_HAVE_CONVRATE, .alert_alarms = 0x7c, .max_convrate = 6, .reg_local_ext = MAX6657_REG_LOCAL_TEMPL, }, [max6648] = { .flags = LM90_HAVE_UNSIGNED_TEMP | LM90_HAVE_CRIT - | LM90_HAVE_BROKEN_ALERT | LM90_HAVE_LOW, + | LM90_HAVE_BROKEN_ALERT | LM90_HAVE_LOW + | LM90_HAVE_CONVRATE, .alert_alarms = 0x7c, .max_convrate = 6, .reg_local_ext = MAX6657_REG_LOCAL_TEMPL, }, [max6654] = { - .flags = LM90_HAVE_BROKEN_ALERT | LM90_HAVE_ALARMS | LM90_HAVE_LOW, + .flags = LM90_HAVE_BROKEN_ALERT | LM90_HAVE_ALARMS | LM90_HAVE_LOW + | LM90_HAVE_CONVRATE, .alert_alarms = 0x7c, .max_convrate = 7, .reg_local_ext = MAX6657_REG_LOCAL_TEMPL, }, [max6657] = { .flags = LM90_PAUSE_FOR_CONFIG | LM90_HAVE_CRIT - | LM90_HAVE_ALARMS | LM90_HAVE_LOW, + | LM90_HAVE_ALARMS | LM90_HAVE_LOW | LM90_HAVE_CONVRATE, .alert_alarms = 0x7c, .max_convrate = 8, .reg_local_ext = MAX6657_REG_LOCAL_TEMPL, }, [max6659] = { .flags = LM90_HAVE_EMERGENCY | LM90_HAVE_CRIT - | LM90_HAVE_ALARMS | LM90_HAVE_LOW, + | LM90_HAVE_ALARMS | LM90_HAVE_LOW | LM90_HAVE_CONVRATE, .alert_alarms = 0x7c, .max_convrate = 8, .reg_local_ext = MAX6657_REG_LOCAL_TEMPL, @@ -474,14 +479,14 @@ static const struct lm90_params lm90_params[] = { */ .flags = LM90_HAVE_OFFSET | LM90_HAVE_CRIT | LM90_HAVE_CRIT_ALRM_SWP | LM90_HAVE_BROKEN_ALERT - | LM90_HAVE_ALARMS | LM90_HAVE_LOW, + | LM90_HAVE_ALARMS | LM90_HAVE_LOW | LM90_HAVE_CONVRATE, .alert_alarms = 0x7c, .max_convrate = 7, }, [max6696] = { .flags = LM90_HAVE_EMERGENCY | LM90_HAVE_EMERGENCY_ALARM | LM90_HAVE_TEMP3 | LM90_HAVE_CRIT - | LM90_HAVE_ALARMS | LM90_HAVE_LOW, + | LM90_HAVE_ALARMS | LM90_HAVE_LOW | LM90_HAVE_CONVRATE, .alert_alarms = 0x1c7c, .max_convrate = 6, .reg_status2 = MAX6696_REG_STATUS2, @@ -489,7 +494,7 @@ static const struct lm90_params lm90_params[] = { }, [w83l771] = { .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT | LM90_HAVE_CRIT - | LM90_HAVE_ALARMS | LM90_HAVE_LOW, + | LM90_HAVE_ALARMS | LM90_HAVE_LOW | LM90_HAVE_CONVRATE, .alert_alarms = 0x7c, .max_convrate = 8, }, @@ -500,7 +505,7 @@ static const struct lm90_params lm90_params[] = { * be set). */ .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT | LM90_HAVE_CRIT - | LM90_HAVE_ALARMS | LM90_HAVE_LOW, + | LM90_HAVE_ALARMS | LM90_HAVE_LOW | LM90_HAVE_CONVRATE, .alert_alarms = 0x7b, .max_convrate = 9, .reg_local_ext = SA56004_REG_LOCAL_TEMPL, @@ -508,7 +513,8 @@ static const struct lm90_params lm90_params[] = { [tmp451] = { .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT | LM90_HAVE_BROKEN_ALERT | LM90_HAVE_EXTENDED_TEMP | LM90_HAVE_CRIT - | LM90_HAVE_UNSIGNED_TEMP | LM90_HAVE_ALARMS | LM90_HAVE_LOW, + | LM90_HAVE_UNSIGNED_TEMP | LM90_HAVE_ALARMS | LM90_HAVE_LOW + | LM90_HAVE_CONVRATE, .alert_alarms = 0x7c, .max_convrate = 9, .resolution = 12, @@ -517,7 +523,7 @@ static const struct lm90_params lm90_params[] = { [tmp461] = { .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT | LM90_HAVE_BROKEN_ALERT | LM90_HAVE_EXTENDED_TEMP | LM90_HAVE_CRIT - | LM90_HAVE_ALARMS | LM90_HAVE_LOW, + | LM90_HAVE_ALARMS | LM90_HAVE_LOW | LM90_HAVE_CONVRATE, .alert_alarms = 0x7c, .max_convrate = 9, .resolution = 12, @@ -1981,7 +1987,8 @@ static void lm90_restore_conf(void *_data) cancel_delayed_work_sync(&data->alert_work); /* Restore initial configuration */ - lm90_write_convrate(data, data->convrate_orig); + if (data->flags & LM90_HAVE_CONVRATE) + lm90_write_convrate(data, data->convrate_orig); lm90_write_reg(client, LM90_REG_CONFIG1, data->config_orig); } @@ -1990,10 +1997,15 @@ static int lm90_init_client(struct i2c_client *client, struct lm90_data *data) struct device_node *np = client->dev.of_node; int config, convrate; - convrate = lm90_read_reg(client, LM90_REG_CONVRATE); - if (convrate < 0) - return convrate; - data->convrate_orig = convrate; + if (data->flags & LM90_HAVE_CONVRATE) { + convrate = lm90_read_reg(client, LM90_REG_CONVRATE); + if (convrate < 0) + return convrate; + data->convrate_orig = convrate; + lm90_set_convrate(client, data, 500); /* 500ms; 2Hz conversion rate */ + } else { + data->update_interval = 500; + } /* * Start the conversions. @@ -2004,8 +2016,6 @@ static int lm90_init_client(struct i2c_client *client, struct lm90_data *data) data->config_orig = config; data->config = config; - lm90_set_convrate(client, data, 500); /* 500ms; 2Hz conversion rate */ - /* Check Temperature Range Select */ if (data->flags & LM90_HAVE_EXTENDED_TEMP) { if (of_property_read_bool(np, "ti,extended-range-enable")) @@ -2156,9 +2166,11 @@ static int lm90_probe(struct i2c_client *client) info->type = hwmon_chip; info->config = data->chip_config; - data->chip_config[0] = HWMON_C_REGISTER_TZ | HWMON_C_UPDATE_INTERVAL; + data->chip_config[0] = HWMON_C_REGISTER_TZ; if (data->flags & LM90_HAVE_ALARMS) data->chip_config[0] |= HWMON_C_ALARMS; + if (data->flags & LM90_HAVE_CONVRATE) + data->chip_config[0] |= HWMON_C_UPDATE_INTERVAL; data->info[1] = &data->temp_info; From 3c1ecccbbc1389773c32fc56910cf23512fccfcd Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Mon, 15 Nov 2021 18:10:11 -0800 Subject: [PATCH 26/85] hwmon: (lm90) Add support for MAX6642 MAX6642 is a reduced version of LM90 with no low limits and no conversion rate register. Its alert functionality is broken, similar to many other chips supported by the lm90 driver. After this change, the stand-alone max6642 driver is only needed if the lm90 driver is disabled. Make it dependent on SENSORS_LM90=n to show that it is not needed if the lm90 driver is enabled. A devicetree node is not added for this chip since it is quite unlikely that such an old chip will ever be used in a devicetree based system. It can be added later if needed. Signed-off-by: Guenter Roeck --- Documentation/hwmon/lm90.rst | 16 ++++++++ drivers/hwmon/Kconfig | 5 ++- drivers/hwmon/lm90.c | 78 +++++++++++++++++++++++++++++------- 3 files changed, 83 insertions(+), 16 deletions(-) diff --git a/Documentation/hwmon/lm90.rst b/Documentation/hwmon/lm90.rst index 45bc333a1219..313b18f6531d 100644 --- a/Documentation/hwmon/lm90.rst +++ b/Documentation/hwmon/lm90.rst @@ -113,6 +113,16 @@ Supported chips: https://www.onsemi.com/PowerSolutions/product.do?id=NCT1008 + * Maxim MAX6642 + + Prefix: 'max6642' + + Addresses scanned: I2C 0x48-0x4f + + Datasheet: Publicly available at the Maxim website + + http://datasheets.maxim-ic.com/en/ds/MAX6642.pdf + * Maxim MAX6646 Prefix: 'max6646' @@ -362,6 +372,12 @@ ADT7481, ADT7482, ADT7483: * Two remote sensors * Selectable address (ADT7483) +MAX6642: + * No critical limit register + * Conversion rate not configurable + * Better local resolution (10 bit) + * 10 bit external sensor resolution + MAX6646, MAX6647, MAX6649: * Better local resolution * Extended range unsigned external temperature diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index df54628bd36b..39ce1b2ccbb3 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -1102,6 +1102,7 @@ config SENSORS_MAX6639 config SENSORS_MAX6642 tristate "Maxim MAX6642 sensor chip" depends on I2C + depends on SENSORS_LM90=n help If you say yes here you get support for MAX6642 sensor chip. MAX6642 is a SMBus-Compatible Remote/Local Temperature Sensor @@ -1360,8 +1361,8 @@ config SENSORS_LM90 If you say yes here you get support for National Semiconductor LM90, LM86, LM89 and LM99, Analog Devices ADM1032, ADT7461, ADT7461A, ADT7481, ADT7482, and ADT7483A, - Maxim MAX6646, MAX6647, MAX6648, MAX6649, MAX6654, MAX6657, MAX6658, - MAX6659, MAX6680, MAX6681, MAX6692, MAX6695, MAX6696, + Maxim MAX6642, MAX6646, MAX6647, MAX6648, MAX6649, MAX6654, MAX6657, + MAX6658, MAX6659, MAX6680, MAX6681, MAX6692, MAX6695, MAX6696, ON Semiconductor NCT1008, Winbond/Nuvoton W83L771W/G/AWG/ASG, Philips SA56004, GMT G781, Texas Instruments TMP451 and TMP461 sensor chips. diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c index ae14b0f60100..181fc1efaa47 100644 --- a/drivers/hwmon/lm90.c +++ b/drivers/hwmon/lm90.c @@ -120,7 +120,7 @@ static const unsigned short normal_i2c[] = { 0x4d, 0x4e, 0x4f, I2C_CLIENT_END }; enum chips { adm1032, adt7461, adt7461a, adt7481, g781, lm86, lm90, lm99, - max6646, max6648, max6654, max6657, max6659, max6680, max6696, + max6642, max6646, max6648, max6654, max6657, max6659, max6680, max6696, sa56004, tmp451, tmp461, w83l771, }; @@ -230,6 +230,7 @@ static const struct i2c_device_id lm90_id[] = { { "lm86", lm86 }, { "lm89", lm86 }, { "lm99", lm99 }, + { "max6642", max6642 }, { "max6646", max6646 }, { "max6647", max6646 }, { "max6648", max6648 }, @@ -434,6 +435,12 @@ static const struct lm90_params lm90_params[] = { .alert_alarms = 0x7b, .max_convrate = 9, }, + [max6642] = { + .flags = LM90_HAVE_BROKEN_ALERT | LM90_HAVE_EXT_UNSIGNED, + .alert_alarms = 0x50, + .reg_local_ext = MAX6657_REG_LOCAL_TEMPL, + .resolution = 10, + }, [max6646] = { .flags = LM90_HAVE_CRIT | LM90_HAVE_BROKEN_ALERT | LM90_HAVE_EXT_UNSIGNED | LM90_HAVE_ALARMS | LM90_HAVE_LOW @@ -1668,18 +1675,18 @@ static const char *lm90_detect_analog(struct i2c_client *client, int chip_id, return name; } -static const char *lm90_detect_maxim(struct i2c_client *client, int chip_id, - int config1, int convrate) +static const char *lm90_detect_maxim(struct i2c_client *client, bool common_address, + int chip_id, int config1, int convrate) { int man_id, emerg, emerg2, status2; int address = client->addr; const char *name = NULL; - if ((address >= 0x48 && address <= 0x4b) || address == 0x4f) - return NULL; - switch (chip_id) { case 0x01: + if (!common_address) + break; + /* * We read MAX6659_REG_REMOTE_EMERG twice, and re-read * LM90_REG_MAN_ID in between. If MAX6659_REG_REMOTE_EMERG @@ -1726,7 +1733,7 @@ static const char *lm90_detect_maxim(struct i2c_client *client, int chip_id, * The lowest 3 bits of the config1 register are unused and * should return zero when read. */ - if (!(config1 & 0x07) && convrate <= 0x07) + if (common_address && !(config1 & 0x07) && convrate <= 0x07) name = "max6654"; break; case 0x09: @@ -1739,16 +1746,21 @@ static const char *lm90_detect_maxim(struct i2c_client *client, int chip_id, * MAX6690 datasheet lists a chip ID of 0x08, and a chip labeled * MAX6654 was observed to have a chip ID of 0x09. */ - if (!(config1 & 0x07) && convrate <= 0x07) + if (common_address && !(config1 & 0x07) && convrate <= 0x07) name = "max6690"; break; case 0x4d: /* - * The MAX6657, MAX6658 and MAX6659 do NOT have a chip_id + * MAX6642, MAX6657, MAX6658 and MAX6659 do NOT have a chip_id * register. Reading from that address will return the last * read value, which in our case is those of the man_id - * register, or 0x4d. Likewise, the config1 register seems to - * lack a low nibble, so the value will be those of the previous + * register, or 0x4d. + * MAX6642 does not have a conversion rate register, nor low + * limit registers. Reading from those registers returns the + * last read value. + * + * For MAX6657, MAX6658 and MAX6659, the config1 register lacks + * a low nibble, so the value will be those of the previous * read, so in our case again those of the man_id register. * MAX6659 has a third set of upper temperature limit registers. * Those registers also return values on MAX6657 and MAX6658, @@ -1756,8 +1768,40 @@ static const char *lm90_detect_maxim(struct i2c_client *client, int chip_id, * For this reason it will be mis-detected as MAX6657 if its * address is 0x4c. */ - if ((address == 0x4c || address == 0x4d || address == 0x4e) && - (config1 & 0x1f) == 0x0d && convrate <= 0x09) { + if (address >= 0x48 && address <= 0x4f && config1 == convrate && + !(config1 & 0x0f)) { + int regval; + + /* + * We know that this is not a MAX6657/58/59 because its + * configuration register has the wrong value and it does + * not appear to have a conversion rate register. + */ + + /* re-read manufacturer ID to have a good baseline */ + if (i2c_smbus_read_byte_data(client, LM90_REG_MAN_ID) != 0x4d) + break; + + /* check various non-existing registers */ + if (i2c_smbus_read_byte_data(client, LM90_REG_CONVRATE) != 0x4d || + i2c_smbus_read_byte_data(client, LM90_REG_LOCAL_LOW) != 0x4d || + i2c_smbus_read_byte_data(client, LM90_REG_REMOTE_LOWH) != 0x4d) + break; + + /* check for unused status register bits */ + regval = i2c_smbus_read_byte_data(client, LM90_REG_STATUS); + if (regval < 0 || (regval & 0x2b)) + break; + + /* re-check unsupported registers */ + if (i2c_smbus_read_byte_data(client, LM90_REG_CONVRATE) != regval || + i2c_smbus_read_byte_data(client, LM90_REG_LOCAL_LOW) != regval || + i2c_smbus_read_byte_data(client, LM90_REG_REMOTE_LOWH) != regval) + break; + + name = "max6642"; + } else if ((address == 0x4c || address == 0x4d || address == 0x4e) && + (config1 & 0x1f) == 0x0d && convrate <= 0x09) { if (address == 0x4c) name = "max6657"; else @@ -1929,6 +1973,11 @@ static int lm90_detect(struct i2c_client *client, struct i2c_board_info *info) struct i2c_adapter *adapter = client->adapter; int man_id, chip_id, config1, convrate; const char *name = NULL; + int address = client->addr; + bool common_address = + (address >= 0x18 && address <= 0x1a) || + (address >= 0x29 && address <= 0x2b) || + (address >= 0x4c && address <= 0x4e); if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) return -ENODEV; @@ -1952,7 +2001,8 @@ static int lm90_detect(struct i2c_client *client, struct i2c_board_info *info) name = lm90_detect_gmt(client, chip_id, config1, convrate); break; case 0x4d: /* Maxim Integrated */ - name = lm90_detect_maxim(client, chip_id, config1, convrate); + name = lm90_detect_maxim(client, common_address, chip_id, + config1, convrate); break; case 0x55: /* TI */ name = lm90_detect_ti(client, chip_id, config1, convrate); From c9933a4439b57f404d7809445e6e43c8f1bbf7e4 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Fri, 19 Nov 2021 14:24:18 -0800 Subject: [PATCH 27/85] hwmon: (lm90) Let lm90_read16() handle 8-bit read operations Simplify the code a bit by handling single-register read operations in lm90_read16(). All we need to do is to skip the low-byte read operation if the register address is 0. Signed-off-by: Guenter Roeck --- drivers/hwmon/lm90.c | 47 ++++++++++++++++---------------------------- 1 file changed, 17 insertions(+), 30 deletions(-) diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c index 181fc1efaa47..4050706a1f32 100644 --- a/drivers/hwmon/lm90.c +++ b/drivers/hwmon/lm90.c @@ -673,6 +673,10 @@ static int lm90_read16(struct i2c_client *client, u8 regh, u8 regl, oldh = lm90_read_reg(client, regh); if (oldh < 0) return oldh; + + if (!regl) + return oldh << 8; + l = lm90_read_reg(client, regl); if (l < 0) return l; @@ -804,29 +808,19 @@ static int lm90_update_limits(struct device *dev) data->temp_hyst = val; } - val = lm90_read_reg(client, LM90_REG_REMOTE_LOWH); + val = lm90_read16(client, LM90_REG_REMOTE_LOWH, + (data->flags & LM90_HAVE_REM_LIMIT_EXT) ? LM90_REG_REMOTE_LOWL : 0, + false); if (val < 0) return val; - data->temp[REMOTE_LOW] = val << 8; + data->temp[REMOTE_LOW] = val; - if (data->flags & LM90_HAVE_REM_LIMIT_EXT) { - val = lm90_read_reg(client, LM90_REG_REMOTE_LOWL); - if (val < 0) - return val; - data->temp[REMOTE_LOW] |= val; - } - - val = lm90_read_reg(client, LM90_REG_REMOTE_HIGHH); + val = lm90_read16(client, LM90_REG_REMOTE_HIGHH, + (data->flags & LM90_HAVE_REM_LIMIT_EXT) ? LM90_REG_REMOTE_HIGHL : 0, + false); if (val < 0) return val; - data->temp[REMOTE_HIGH] = val << 8; - - if (data->flags & LM90_HAVE_REM_LIMIT_EXT) { - val = lm90_read_reg(client, LM90_REG_REMOTE_HIGHL); - if (val < 0) - return val; - data->temp[REMOTE_HIGH] |= val; - } + data->temp[REMOTE_HIGH] = val; if (data->flags & LM90_HAVE_OFFSET) { val = lm90_read16(client, LM90_REG_REMOTE_OFFSH, @@ -1063,18 +1057,11 @@ static int lm90_update_device(struct device *dev) return val; data->temp[LOCAL_HIGH] = val << 8; - if (data->reg_local_ext) { - val = lm90_read16(client, LM90_REG_LOCAL_TEMP, - data->reg_local_ext, true); - if (val < 0) - return val; - data->temp[LOCAL_TEMP] = val; - } else { - val = lm90_read_reg(client, LM90_REG_LOCAL_TEMP); - if (val < 0) - return val; - data->temp[LOCAL_TEMP] = val << 8; - } + val = lm90_read16(client, LM90_REG_LOCAL_TEMP, + data->reg_local_ext, true); + if (val < 0) + return val; + data->temp[LOCAL_TEMP] = val; val = lm90_read16(client, LM90_REG_REMOTE_TEMPH, LM90_REG_REMOTE_TEMPL, true); if (val < 0) From 6be4b1a430824852f7c67f510f4ab88eada2d838 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sat, 20 Nov 2021 08:12:17 -0800 Subject: [PATCH 28/85] hwmon: (lm90) Introduce 16-bit register write function Introduce 16-bit register write function to simplify the code in some places. Signed-off-by: Guenter Roeck --- drivers/hwmon/lm90.c | 33 +++++++++++++++++++-------------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c index 4050706a1f32..75ea3a993920 100644 --- a/drivers/hwmon/lm90.c +++ b/drivers/hwmon/lm90.c @@ -665,6 +665,21 @@ static int lm90_write_reg(struct i2c_client *client, u8 reg, u8 val) return i2c_smbus_write_byte_data(client, lm90_write_reg_addr(reg), val); } +/* + * Write into 16-bit LM90 register. + * Convert register addresses to write address if needed, then execute the + * operation. + */ +static int lm90_write16(struct i2c_client *client, u8 regh, u8 regl, u16 val) +{ + int ret; + + ret = lm90_write_reg(client, regh, val >> 8); + if (ret < 0 || !regl) + return ret; + return lm90_write_reg(client, regl, val & 0xff); +} + static int lm90_read16(struct i2c_client *client, u8 regh, u8 regl, bool is_volatile) { @@ -1240,12 +1255,8 @@ static int lm90_set_temp(struct lm90_data *data, int index, int channel, long va if (channel > 1) lm90_select_remote_channel(data, true); - err = lm90_write_reg(client, regh, data->temp[index] >> 8); - if (err < 0) - goto deselect; - if (regl) - err = lm90_write_reg(client, regl, data->temp[index] & 0xff); -deselect: + err = lm90_write16(client, regh, regl, data->temp[index]); + if (channel > 1) lm90_select_remote_channel(data, false); @@ -1404,14 +1415,8 @@ static int lm90_temp_write(struct device *dev, u32 attr, int channel, long val) case hwmon_temp_offset: val = lm90_temp_to_reg(0, val, lm90_temp_get_resolution(data, REMOTE_OFFSET)); - err = i2c_smbus_write_byte_data(data->client, - LM90_REG_REMOTE_OFFSH, - val >> 8); - if (err) - break; - err = i2c_smbus_write_byte_data(data->client, - LM90_REG_REMOTE_OFFSL, - val & 0xff); + err = lm90_write16(data->client, LM90_REG_REMOTE_OFFSH, + LM90_REG_REMOTE_OFFSL, val); if (err) break; data->temp[REMOTE_OFFSET] = val; From c09472fcf9e08b650453906ec9ab70662fe57fea Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Fri, 19 Nov 2021 13:55:47 -0800 Subject: [PATCH 29/85] hwmon: (lm90) Support MAX1617 and LM84 MAX1617 and LM84 are stripped-down versions of LM90, so they can easily be supported by the LM90 driver. The most difficult part is chip detection, since those old chips do not support manufacturer ID or chip ID registers. The "alarms" attribute is enabled for both chips to match the functionality of the adm1021 driver. Chip detection was improved and is less prone to misdetection than the chip detection in the adm1021 driver. Devicetree nodes are not added for the added chips since it is quite unlikely that such old chips will ever be used in a devicetree based system. They can be added later if needed. Signed-off-by: Guenter Roeck --- Documentation/hwmon/lm90.rst | 45 +++++- drivers/hwmon/Kconfig | 9 +- drivers/hwmon/lm90.c | 264 ++++++++++++++++++++++++++++++----- 3 files changed, 275 insertions(+), 43 deletions(-) diff --git a/Documentation/hwmon/lm90.rst b/Documentation/hwmon/lm90.rst index 313b18f6531d..e2ee0f2e1b7b 100644 --- a/Documentation/hwmon/lm90.rst +++ b/Documentation/hwmon/lm90.rst @@ -3,6 +3,14 @@ Kernel driver lm90 Supported chips: + * National Semiconductor LM84 + + Prefix: 'lm84' + + Addresses scanned: I2C 0x18 - 0x1a, 0x29 - 0x2b, 0x4c - 0x4e + + Datasheet: Publicly available at the National Semiconductor website + * National Semiconductor LM90 Prefix: 'lm90' @@ -113,6 +121,22 @@ Supported chips: https://www.onsemi.com/PowerSolutions/product.do?id=NCT1008 + * Maxim MAX1617 + + Prefix: 'max1617' + + Addresses scanned: I2C 0x18 - 0x1a, 0x29 - 0x2b, 0x4c - 0x4e + + Datasheet: Publicly available at the Maxim website + + * Maxim MAX1617A + + Prefix: 'max1617a' + + Addresses scanned: I2C 0x18 - 0x1a, 0x29 - 0x2b, 0x4c - 0x4e + + Datasheet: Publicly available at the Maxim website + * Maxim MAX6642 Prefix: 'max6642' @@ -325,6 +349,12 @@ The LM90 is a digital temperature sensor. It senses its own temperature as well as the temperature of up to one external diode. It is compatible with many other devices, many of which are supported by this driver. +The family of chips supported by this driver is derived from MAX1617. +This chip as well as various compatible chips support a local and a remote +temperature sensor with 8 bit accuracy. Later chips provide improved accuracy +and other additional features such as hysteresis and temperature offset +registers. + Note that there is no easy way to differentiate between the MAX6657, MAX6658 and MAX6659 variants. The extra features of the MAX6659 are only supported by this driver if the chip is located at address 0x4d or 0x4e, @@ -332,15 +362,22 @@ or if the chip type is explicitly selected as max6659. The MAX6680 and MAX6681 only differ in their pinout, therefore they obviously can't (and don't need to) be distinguished. -The specificity of this family of chipsets over the ADM1021/LM84 -family is that it features critical limits with hysteresis, and an -increased resolution of the remote temperature measurement. - The different chipsets of the family are not strictly identical, although very similar. For reference, here comes a non-exhaustive list of specific features: +LM84: + * 8 bit sensor resolution + +MAX1617: + * 8 bit sensor resolution + * Low temperature limits + LM90: + * 11 bit resolution for remote temperature sensor + * Temperature offset register for remote temperature sensor + * Low and critical temperature limits + * Configurable conversion rate * Filter and alert configuration register at 0xBF. * ALERT is triggered by temperatures over critical limits. diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 39ce1b2ccbb3..1dd812cf15bb 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -1358,11 +1358,12 @@ config SENSORS_LM90 tristate "National Semiconductor LM90 and compatibles" depends on I2C help - If you say yes here you get support for National Semiconductor LM90, - LM86, LM89 and LM99, Analog Devices ADM1032, ADT7461, ADT7461A, + If you say yes here you get support for National Semiconductor LM84, + LM90, LM86, LM89 and LM99, Analog Devices ADM1032, ADT7461, ADT7461A, ADT7481, ADT7482, and ADT7483A, - Maxim MAX6642, MAX6646, MAX6647, MAX6648, MAX6649, MAX6654, MAX6657, - MAX6658, MAX6659, MAX6680, MAX6681, MAX6692, MAX6695, MAX6696, + Maxim MAX1617, MAX6642, MAX6646, MAX6647, MAX6648, MAX6649, MAX6654, + MAX6657, MAX6658, MAX6659, MAX6680, MAX6681, MAX6692, MAX6695, + MAX6696, ON Semiconductor NCT1008, Winbond/Nuvoton W83L771W/G/AWG/ASG, Philips SA56004, GMT G781, Texas Instruments TMP451 and TMP461 sensor chips. diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c index 75ea3a993920..2e85dbc4def7 100644 --- a/drivers/hwmon/lm90.c +++ b/drivers/hwmon/lm90.c @@ -80,6 +80,9 @@ * They are mostly compatible with ADT7461 except for local temperature * low byte register and max conversion rate. * + * This driver also supports MAX1617 and various clones such as G767 + * and NE1617. Such clones will be detected as MAX1617. + * * Since the LM90 was the first chipset supported by this driver, most * comments will refer to this chipset, but are actually general and * concern all supported chipsets, unless mentioned otherwise. @@ -119,8 +122,8 @@ static const unsigned short normal_i2c[] = { 0x18, 0x19, 0x1a, 0x29, 0x2a, 0x2b, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, I2C_CLIENT_END }; -enum chips { adm1032, adt7461, adt7461a, adt7481, g781, lm86, lm90, lm99, - max6642, max6646, max6648, max6654, max6657, max6659, max6680, max6696, +enum chips { adm1032, adt7461, adt7461a, adt7481, g781, lm84, lm86, lm90, lm99, + max1617, max6642, max6646, max6648, max6654, max6657, max6659, max6680, max6696, sa56004, tmp451, tmp461, w83l771, }; @@ -194,6 +197,7 @@ enum chips { adm1032, adt7461, adt7461a, adt7481, g781, lm86, lm90, lm99, #define LM90_HAVE_EXT_UNSIGNED BIT(14) /* extended unsigned temperature*/ #define LM90_HAVE_LOW BIT(15) /* low limits */ #define LM90_HAVE_CONVRATE BIT(16) /* conversion rate */ +#define LM90_HAVE_REMOTE_EXT BIT(17) /* extended remote temperature */ /* LM90 status */ #define LM90_STATUS_LTHRM BIT(0) /* local THERM limit tripped */ @@ -226,10 +230,12 @@ static const struct i2c_device_id lm90_id[] = { { "adt7482", adt7481 }, { "adt7483a", adt7481 }, { "g781", g781 }, - { "lm90", lm90 }, + { "lm84", lm84 }, { "lm86", lm86 }, { "lm89", lm86 }, + { "lm90", lm90 }, { "lm99", lm99 }, + { "max1617", max1617 }, { "max6642", max6642 }, { "max6646", max6646 }, { "max6647", max6646 }, @@ -373,7 +379,7 @@ static const struct lm90_params lm90_params[] = { .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT | LM90_HAVE_BROKEN_ALERT | LM90_HAVE_CRIT | LM90_HAVE_PARTIAL_PEC | LM90_HAVE_ALARMS - | LM90_HAVE_LOW | LM90_HAVE_CONVRATE, + | LM90_HAVE_LOW | LM90_HAVE_CONVRATE | LM90_HAVE_REMOTE_EXT, .alert_alarms = 0x7c, .max_convrate = 10, }, @@ -386,7 +392,8 @@ static const struct lm90_params lm90_params[] = { .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT | LM90_HAVE_BROKEN_ALERT | LM90_HAVE_EXTENDED_TEMP | LM90_HAVE_CRIT | LM90_HAVE_PARTIAL_PEC - | LM90_HAVE_ALARMS | LM90_HAVE_LOW | LM90_HAVE_CONVRATE, + | LM90_HAVE_ALARMS | LM90_HAVE_LOW | LM90_HAVE_CONVRATE + | LM90_HAVE_REMOTE_EXT, .alert_alarms = 0x7c, .max_convrate = 10, .resolution = 10, @@ -395,7 +402,7 @@ static const struct lm90_params lm90_params[] = { .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT | LM90_HAVE_BROKEN_ALERT | LM90_HAVE_EXTENDED_TEMP | LM90_HAVE_CRIT | LM90_HAVE_PEC | LM90_HAVE_ALARMS - | LM90_HAVE_LOW | LM90_HAVE_CONVRATE, + | LM90_HAVE_LOW | LM90_HAVE_CONVRATE | LM90_HAVE_REMOTE_EXT, .alert_alarms = 0x7c, .max_convrate = 10, }, @@ -404,7 +411,7 @@ static const struct lm90_params lm90_params[] = { | LM90_HAVE_BROKEN_ALERT | LM90_HAVE_EXTENDED_TEMP | LM90_HAVE_UNSIGNED_TEMP | LM90_HAVE_PEC | LM90_HAVE_TEMP3 | LM90_HAVE_CRIT | LM90_HAVE_LOW - | LM90_HAVE_CONVRATE, + | LM90_HAVE_CONVRATE | LM90_HAVE_REMOTE_EXT, .alert_alarms = 0x1c7c, .max_convrate = 11, .resolution = 10, @@ -413,38 +420,54 @@ static const struct lm90_params lm90_params[] = { [g781] = { .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT | LM90_HAVE_BROKEN_ALERT | LM90_HAVE_CRIT - | LM90_HAVE_ALARMS | LM90_HAVE_LOW | LM90_HAVE_CONVRATE, + | LM90_HAVE_ALARMS | LM90_HAVE_LOW | LM90_HAVE_CONVRATE + | LM90_HAVE_REMOTE_EXT, .alert_alarms = 0x7c, .max_convrate = 7, }, + [lm84] = { + .flags = LM90_HAVE_ALARMS, + .resolution = 8, + }, [lm86] = { .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT - | LM90_HAVE_CRIT | LM90_HAVE_ALARMS | LM90_HAVE_LOW | LM90_HAVE_CONVRATE, + | LM90_HAVE_CRIT | LM90_HAVE_ALARMS | LM90_HAVE_LOW + | LM90_HAVE_CONVRATE | LM90_HAVE_REMOTE_EXT, .alert_alarms = 0x7b, .max_convrate = 9, }, [lm90] = { .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT - | LM90_HAVE_CRIT | LM90_HAVE_ALARMS | LM90_HAVE_LOW | LM90_HAVE_CONVRATE, + | LM90_HAVE_CRIT | LM90_HAVE_ALARMS | LM90_HAVE_LOW + | LM90_HAVE_CONVRATE | LM90_HAVE_REMOTE_EXT, .alert_alarms = 0x7b, .max_convrate = 9, }, [lm99] = { .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT - | LM90_HAVE_CRIT | LM90_HAVE_ALARMS | LM90_HAVE_LOW | LM90_HAVE_CONVRATE, + | LM90_HAVE_CRIT | LM90_HAVE_ALARMS | LM90_HAVE_LOW + | LM90_HAVE_CONVRATE | LM90_HAVE_REMOTE_EXT, .alert_alarms = 0x7b, .max_convrate = 9, }, + [max1617] = { + .flags = LM90_HAVE_CONVRATE | LM90_HAVE_BROKEN_ALERT | + LM90_HAVE_LOW | LM90_HAVE_ALARMS, + .alert_alarms = 0x78, + .resolution = 8, + .max_convrate = 7, + }, [max6642] = { - .flags = LM90_HAVE_BROKEN_ALERT | LM90_HAVE_EXT_UNSIGNED, + .flags = LM90_HAVE_BROKEN_ALERT | LM90_HAVE_EXT_UNSIGNED + | LM90_HAVE_REMOTE_EXT, .alert_alarms = 0x50, - .reg_local_ext = MAX6657_REG_LOCAL_TEMPL, .resolution = 10, + .reg_local_ext = MAX6657_REG_LOCAL_TEMPL, }, [max6646] = { .flags = LM90_HAVE_CRIT | LM90_HAVE_BROKEN_ALERT | LM90_HAVE_EXT_UNSIGNED | LM90_HAVE_ALARMS | LM90_HAVE_LOW - | LM90_HAVE_CONVRATE, + | LM90_HAVE_CONVRATE | LM90_HAVE_REMOTE_EXT, .alert_alarms = 0x7c, .max_convrate = 6, .reg_local_ext = MAX6657_REG_LOCAL_TEMPL, @@ -452,28 +475,30 @@ static const struct lm90_params lm90_params[] = { [max6648] = { .flags = LM90_HAVE_UNSIGNED_TEMP | LM90_HAVE_CRIT | LM90_HAVE_BROKEN_ALERT | LM90_HAVE_LOW - | LM90_HAVE_CONVRATE, + | LM90_HAVE_CONVRATE | LM90_HAVE_REMOTE_EXT, .alert_alarms = 0x7c, .max_convrate = 6, .reg_local_ext = MAX6657_REG_LOCAL_TEMPL, }, [max6654] = { .flags = LM90_HAVE_BROKEN_ALERT | LM90_HAVE_ALARMS | LM90_HAVE_LOW - | LM90_HAVE_CONVRATE, + | LM90_HAVE_CONVRATE | LM90_HAVE_REMOTE_EXT, .alert_alarms = 0x7c, .max_convrate = 7, .reg_local_ext = MAX6657_REG_LOCAL_TEMPL, }, [max6657] = { .flags = LM90_PAUSE_FOR_CONFIG | LM90_HAVE_CRIT - | LM90_HAVE_ALARMS | LM90_HAVE_LOW | LM90_HAVE_CONVRATE, + | LM90_HAVE_ALARMS | LM90_HAVE_LOW | LM90_HAVE_CONVRATE + | LM90_HAVE_REMOTE_EXT, .alert_alarms = 0x7c, .max_convrate = 8, .reg_local_ext = MAX6657_REG_LOCAL_TEMPL, }, [max6659] = { .flags = LM90_HAVE_EMERGENCY | LM90_HAVE_CRIT - | LM90_HAVE_ALARMS | LM90_HAVE_LOW | LM90_HAVE_CONVRATE, + | LM90_HAVE_ALARMS | LM90_HAVE_LOW | LM90_HAVE_CONVRATE + | LM90_HAVE_REMOTE_EXT, .alert_alarms = 0x7c, .max_convrate = 8, .reg_local_ext = MAX6657_REG_LOCAL_TEMPL, @@ -486,14 +511,16 @@ static const struct lm90_params lm90_params[] = { */ .flags = LM90_HAVE_OFFSET | LM90_HAVE_CRIT | LM90_HAVE_CRIT_ALRM_SWP | LM90_HAVE_BROKEN_ALERT - | LM90_HAVE_ALARMS | LM90_HAVE_LOW | LM90_HAVE_CONVRATE, + | LM90_HAVE_ALARMS | LM90_HAVE_LOW | LM90_HAVE_CONVRATE + | LM90_HAVE_REMOTE_EXT, .alert_alarms = 0x7c, .max_convrate = 7, }, [max6696] = { .flags = LM90_HAVE_EMERGENCY | LM90_HAVE_EMERGENCY_ALARM | LM90_HAVE_TEMP3 | LM90_HAVE_CRIT - | LM90_HAVE_ALARMS | LM90_HAVE_LOW | LM90_HAVE_CONVRATE, + | LM90_HAVE_ALARMS | LM90_HAVE_LOW | LM90_HAVE_CONVRATE + | LM90_HAVE_REMOTE_EXT, .alert_alarms = 0x1c7c, .max_convrate = 6, .reg_status2 = MAX6696_REG_STATUS2, @@ -501,7 +528,8 @@ static const struct lm90_params lm90_params[] = { }, [w83l771] = { .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT | LM90_HAVE_CRIT - | LM90_HAVE_ALARMS | LM90_HAVE_LOW | LM90_HAVE_CONVRATE, + | LM90_HAVE_ALARMS | LM90_HAVE_LOW | LM90_HAVE_CONVRATE + | LM90_HAVE_REMOTE_EXT, .alert_alarms = 0x7c, .max_convrate = 8, }, @@ -512,7 +540,8 @@ static const struct lm90_params lm90_params[] = { * be set). */ .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT | LM90_HAVE_CRIT - | LM90_HAVE_ALARMS | LM90_HAVE_LOW | LM90_HAVE_CONVRATE, + | LM90_HAVE_ALARMS | LM90_HAVE_LOW | LM90_HAVE_CONVRATE + | LM90_HAVE_REMOTE_EXT, .alert_alarms = 0x7b, .max_convrate = 9, .reg_local_ext = SA56004_REG_LOCAL_TEMPL, @@ -521,7 +550,7 @@ static const struct lm90_params lm90_params[] = { .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT | LM90_HAVE_BROKEN_ALERT | LM90_HAVE_EXTENDED_TEMP | LM90_HAVE_CRIT | LM90_HAVE_UNSIGNED_TEMP | LM90_HAVE_ALARMS | LM90_HAVE_LOW - | LM90_HAVE_CONVRATE, + | LM90_HAVE_CONVRATE | LM90_HAVE_REMOTE_EXT, .alert_alarms = 0x7c, .max_convrate = 9, .resolution = 12, @@ -530,7 +559,8 @@ static const struct lm90_params lm90_params[] = { [tmp461] = { .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT | LM90_HAVE_BROKEN_ALERT | LM90_HAVE_EXTENDED_TEMP | LM90_HAVE_CRIT - | LM90_HAVE_ALARMS | LM90_HAVE_LOW | LM90_HAVE_CONVRATE, + | LM90_HAVE_ALARMS | LM90_HAVE_LOW | LM90_HAVE_CONVRATE + | LM90_HAVE_REMOTE_EXT, .alert_alarms = 0x7c, .max_convrate = 9, .resolution = 12, @@ -596,6 +626,7 @@ struct lm90_data { u8 max_convrate; /* Maximum conversion rate */ u8 reg_status2; /* 2nd status register (optional) */ u8 reg_local_ext; /* local extension register offset */ + u8 reg_remote_ext; /* remote temperature low byte */ /* registers values */ u16 temp[TEMP_REG_NUM]; @@ -1078,7 +1109,7 @@ static int lm90_update_device(struct device *dev) return val; data->temp[LOCAL_TEMP] = val; val = lm90_read16(client, LM90_REG_REMOTE_TEMPH, - LM90_REG_REMOTE_TEMPL, true); + data->reg_remote_ext, true); if (val < 0) return val; data->temp[REMOTE_TEMP] = val; @@ -1089,7 +1120,7 @@ static int lm90_update_device(struct device *dev) return val; val = lm90_read16(client, LM90_REG_REMOTE_TEMPH, - LM90_REG_REMOTE_TEMPL, true); + data->reg_remote_ext, true); if (val < 0) { lm90_select_remote_channel(data, false); return val; @@ -1150,6 +1181,9 @@ static int lm90_temp_get_resolution(struct lm90_data *data, int index) { switch (index) { case REMOTE_TEMP: + if (data->reg_remote_ext) + return data->resolution; + return 8; case REMOTE_OFFSET: case REMOTE2_TEMP: return data->resolution; @@ -1560,11 +1594,118 @@ static umode_t lm90_is_visible(const void *data, enum hwmon_sensor_types type, } } -/* - * Per-manufacturer chip detect functions. - * Functions are expected to return a pointer to the chip name or NULL - * if detection was not successful. - */ +static const char *lm90_detect_lm84(struct i2c_client *client) +{ + static const u8 regs[] = { + LM90_REG_STATUS, LM90_REG_LOCAL_TEMP, LM90_REG_LOCAL_HIGH, + LM90_REG_REMOTE_TEMPH, LM90_REG_REMOTE_HIGHH + }; + int status = i2c_smbus_read_byte_data(client, LM90_REG_STATUS); + int reg1, reg2, reg3, reg4; + bool nonzero = false; + u8 ff = 0xff; + int i; + + if (status < 0 || (status & 0xab)) + return NULL; + + /* + * For LM84, undefined registers return the most recent value. + * Repeat several times, each time checking against a different + * (presumably) existing register. + */ + for (i = 0; i < ARRAY_SIZE(regs); i++) { + reg1 = i2c_smbus_read_byte_data(client, regs[i]); + reg2 = i2c_smbus_read_byte_data(client, LM90_REG_REMOTE_TEMPL); + reg3 = i2c_smbus_read_byte_data(client, LM90_REG_LOCAL_LOW); + reg4 = i2c_smbus_read_byte_data(client, LM90_REG_REMOTE_LOWH); + + if (reg1 < 0) + return NULL; + + /* If any register has a different value, this is not an LM84 */ + if (reg2 != reg1 || reg3 != reg1 || reg4 != reg1) + return NULL; + + nonzero |= reg1 || reg2 || reg3 || reg4; + ff &= reg1; + } + /* + * If all registers always returned 0 or 0xff, all bets are off, + * and we can not make any predictions about the chip type. + */ + return nonzero && ff != 0xff ? "lm84" : NULL; +} + +static const char *lm90_detect_max1617(struct i2c_client *client, int config1) +{ + int status = i2c_smbus_read_byte_data(client, LM90_REG_STATUS); + int llo, rlo, lhi, rhi; + + if (status < 0 || (status & 0x03)) + return NULL; + + if (config1 & 0x3f) + return NULL; + + /* + * Fail if unsupported registers return anything but 0xff. + * The calling code already checked man_id and chip_id. + * A byte read operation repeats the most recent read operation + * and should also return 0xff. + */ + if (i2c_smbus_read_byte_data(client, LM90_REG_REMOTE_TEMPL) != 0xff || + i2c_smbus_read_byte_data(client, MAX6657_REG_LOCAL_TEMPL) != 0xff || + i2c_smbus_read_byte_data(client, LM90_REG_REMOTE_LOWL) != 0xff || + i2c_smbus_read_byte(client) != 0xff) + return NULL; + + llo = i2c_smbus_read_byte_data(client, LM90_REG_LOCAL_LOW); + rlo = i2c_smbus_read_byte_data(client, LM90_REG_REMOTE_LOWH); + + lhi = i2c_smbus_read_byte_data(client, LM90_REG_LOCAL_HIGH); + rhi = i2c_smbus_read_byte_data(client, LM90_REG_REMOTE_HIGHH); + + if (llo < 0 || rlo < 0) + return NULL; + + /* + * A byte read operation repeats the most recent read and should + * return the same value. + */ + if (i2c_smbus_read_byte(client) != rhi) + return NULL; + + /* + * The following two checks are marginal since the checked values + * are strictly speaking valid. + */ + + /* fail for negative high limits; this also catches read errors */ + if ((s8)lhi < 0 || (s8)rhi < 0) + return NULL; + + /* fail if low limits are larger than or equal to high limits */ + if ((s8)llo >= lhi || (s8)rlo >= rhi) + return NULL; + + if (i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WORD_DATA)) { + /* + * Word read operations return 0xff in second byte + */ + if (i2c_smbus_read_word_data(client, LM90_REG_REMOTE_TEMPL) != + 0xffff) + return NULL; + if (i2c_smbus_read_word_data(client, LM90_REG_CONFIG1) != + (config1 | 0xff00)) + return NULL; + if (i2c_smbus_read_word_data(client, LM90_REG_LOCAL_HIGH) != + (lhi | 0xff00)) + return NULL; + } + + return "max1617"; +} static const char *lm90_detect_national(struct i2c_client *client, int chip_id, int config1, int convrate) @@ -1714,10 +1855,29 @@ static const char *lm90_detect_maxim(struct i2c_client *client, bool common_addr * The chip_id register of the MAX6680 and MAX6681 holds the * revision of the chip. The lowest bit of the config1 register * is unused and should return zero when read, so should the - * second to last bit of config1 (software reset). + * second to last bit of config1 (software reset). Register + * address 0x12 (LM90_REG_REMOTE_OFFSL) exists for this chip and + * should differ from emerg2, and emerg2 should match man_id + * since it does not exist. */ - else if (!(config1 & 0x03) && convrate <= 0x07) + else if (!(config1 & 0x03) && convrate <= 0x07 && + emerg2 == man_id && emerg2 != status2) name = "max6680"; + /* + * MAX1617A does not have any extended registers (register + * address 0x10 or higher) except for manufacturer and + * device ID registers. Unlike other chips of this series, + * unsupported registers were observed to return a fixed value + * of 0x01. + * Note: Multiple chips with different markings labeled as + * "MAX1617" (no "A") were observed to report manufacturer ID + * 0x4d and device ID 0x01. It is unknown if other variants of + * MAX1617/MAX617A with different behavior exist. The detection + * code below works for those chips. + */ + else if (!(config1 & 0x03f) && convrate <= 0x07 && + emerg == 0x01 && emerg2 == 0x01 && status2 == 0x01) + name = "max1617"; break; case 0x08: /* @@ -1963,7 +2123,7 @@ static const char *lm90_detect_ti(struct i2c_client *client, int chip_id, static int lm90_detect(struct i2c_client *client, struct i2c_board_info *info) { struct i2c_adapter *adapter = client->adapter; - int man_id, chip_id, config1, convrate; + int man_id, chip_id, config1, convrate, lhigh; const char *name = NULL; int address = client->addr; bool common_address = @@ -1974,15 +2134,43 @@ static int lm90_detect(struct i2c_client *client, struct i2c_board_info *info) if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) return -ENODEV; + /* + * Get well defined register value for chips with neither man_id nor + * chip_id registers. + */ + lhigh = i2c_smbus_read_byte_data(client, LM90_REG_LOCAL_HIGH); + /* detection and identification */ man_id = i2c_smbus_read_byte_data(client, LM90_REG_MAN_ID); chip_id = i2c_smbus_read_byte_data(client, LM90_REG_CHIP_ID); config1 = i2c_smbus_read_byte_data(client, LM90_REG_CONFIG1); convrate = i2c_smbus_read_byte_data(client, LM90_REG_CONVRATE); - if (man_id < 0 || chip_id < 0 || config1 < 0 || convrate < 0) + if (man_id < 0 || chip_id < 0 || config1 < 0 || convrate < 0 || lhigh < 0) return -ENODEV; + /* Bail out immediately if all register report the same value */ + if (lhigh == man_id && lhigh == chip_id && lhigh == config1 && lhigh == convrate) + return -ENODEV; + + /* + * If reading man_id and chip_id both return the same value as lhigh, + * the chip may not support those registers and return the most recent read + * value. Check again with a different register and handle accordingly. + */ + if (man_id == lhigh && chip_id == lhigh) { + convrate = i2c_smbus_read_byte_data(client, LM90_REG_CONVRATE); + man_id = i2c_smbus_read_byte_data(client, LM90_REG_MAN_ID); + chip_id = i2c_smbus_read_byte_data(client, LM90_REG_CHIP_ID); + if (convrate < 0 || man_id < 0 || chip_id < 0) + return -ENODEV; + if (man_id == convrate && chip_id == convrate) + man_id = -1; + } switch (man_id) { + case -1: /* Chip does not support man_id / chip_id */ + if (common_address && !convrate && !(config1 & 0x7f)) + name = lm90_detect_lm84(client); + break; case 0x01: /* National Semiconductor */ name = lm90_detect_national(client, chip_id, config1, convrate); break; @@ -2005,6 +2193,10 @@ static int lm90_detect(struct i2c_client *client, struct i2c_board_info *info) case 0xa1: /* NXP Semiconductor/Philips */ name = lm90_detect_nxp(client, chip_id, config1, convrate); break; + case 0xff: /* MAX1617, G767, NE1617 */ + if (common_address && chip_id == 0xff && convrate < 8) + name = lm90_detect_max1617(client, config1); + break; default: break; } @@ -2265,6 +2457,8 @@ static int lm90_probe(struct i2c_client *client) } data->reg_local_ext = lm90_params[data->kind].reg_local_ext; + if (data->flags & LM90_HAVE_REMOTE_EXT) + data->reg_remote_ext = LM90_REG_REMOTE_TEMPL; data->reg_status2 = lm90_params[data->kind].reg_status2; /* Set maximum conversion rate */ From 0c6bffd4a625319d83f40a40090a5335a5bd8a8d Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Fri, 19 Nov 2021 16:21:37 -0800 Subject: [PATCH 30/85] hwmon: (lm90) Add support for ADM1021, ADM1021A, and ADM1023 Both chips are quite similar to other chips of this series, so add support for them to the lm90 driver. Also mention ON Semiconductor NCT210, which is pin and register compatible to ADM1021A. None of the chips support the secondary manufacturer and chip ID registers at 0x3e and 0x3f, but return 0 when reading from those registers. Use that information to improve the accuracy of chip detection code. Devicetree nodes are not added for the added chips since it is quite unlikely that such old chips will ever be used in a devicetree based system. They can be added later if needed. Signed-off-by: Guenter Roeck --- Documentation/hwmon/lm90.rst | 23 +++++++++++++++++- drivers/hwmon/Kconfig | 4 +-- drivers/hwmon/lm90.c | 47 ++++++++++++++++++++++++++++++++---- 3 files changed, 66 insertions(+), 8 deletions(-) diff --git a/Documentation/hwmon/lm90.rst b/Documentation/hwmon/lm90.rst index e2ee0f2e1b7b..075be136210b 100644 --- a/Documentation/hwmon/lm90.rst +++ b/Documentation/hwmon/lm90.rst @@ -51,6 +51,22 @@ Supported chips: http://www.national.com/mpf/LM/LM86.html + * Analog Devices ADM1021 + + Prefix: 'adm1021' + + Addresses scanned: I2C 0x18 - 0x1a, 0x29 - 0x2b, 0x4c - 0x4e + + Datasheet: Publicly available at the Analog Devices website + + * Analog Devices ADM1021A/ADM1023 + + Prefix: 'adm1023' + + Addresses scanned: I2C 0x18 - 0x1a, 0x29 - 0x2b, 0x4c - 0x4e + + Datasheet: Publicly available at the Analog Devices website + * Analog Devices ADM1032 Prefix: 'adm1032' @@ -369,10 +385,15 @@ features: LM84: * 8 bit sensor resolution -MAX1617: +ADM1021, MAX1617: * 8 bit sensor resolution * Low temperature limits +ADM1021A, ADM1023: + * Temperature offset register for remote temperature sensor + * 11 bit resolution for remote temperature sensor + * Low temperature limits + LM90: * 11 bit resolution for remote temperature sensor * Temperature offset register for remote temperature sensor diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 1dd812cf15bb..50fa255b1e3c 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -1359,8 +1359,8 @@ config SENSORS_LM90 depends on I2C help If you say yes here you get support for National Semiconductor LM84, - LM90, LM86, LM89 and LM99, Analog Devices ADM1032, ADT7461, ADT7461A, - ADT7481, ADT7482, and ADT7483A, + LM90, LM86, LM89 and LM99, Analog Devices ADM2021, ADM1021A, ADM1023, + ADM1032, ADT7461, ADT7461A, ADT7481, ADT7482, and ADT7483A, Maxim MAX1617, MAX6642, MAX6646, MAX6647, MAX6648, MAX6649, MAX6654, MAX6657, MAX6658, MAX6659, MAX6680, MAX6681, MAX6692, MAX6695, MAX6696, diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c index 2e85dbc4def7..2c4a66b93fde 100644 --- a/drivers/hwmon/lm90.c +++ b/drivers/hwmon/lm90.c @@ -122,7 +122,8 @@ static const unsigned short normal_i2c[] = { 0x18, 0x19, 0x1a, 0x29, 0x2a, 0x2b, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, I2C_CLIENT_END }; -enum chips { adm1032, adt7461, adt7461a, adt7481, g781, lm84, lm86, lm90, lm99, +enum chips { adm1023, adm1032, adt7461, adt7461a, adt7481, + g781, lm84, lm86, lm90, lm99, max1617, max6642, max6646, max6648, max6654, max6657, max6659, max6680, max6696, sa56004, tmp451, tmp461, w83l771, }; @@ -223,6 +224,8 @@ enum chips { adm1032, adt7461, adt7461a, adt7481, g781, lm84, lm86, lm90, lm99, */ static const struct i2c_device_id lm90_id[] = { + { "adm1021", max1617 }, + { "adm1023", adm1023 }, { "adm1032", adm1032 }, { "adt7461", adt7461 }, { "adt7461a", adt7461a }, @@ -375,6 +378,14 @@ struct lm90_params { }; static const struct lm90_params lm90_params[] = { + [adm1023] = { + .flags = LM90_HAVE_ALARMS | LM90_HAVE_OFFSET | LM90_HAVE_BROKEN_ALERT + | LM90_HAVE_REM_LIMIT_EXT | LM90_HAVE_LOW | LM90_HAVE_CONVRATE + | LM90_HAVE_REMOTE_EXT, + .alert_alarms = 0x7c, + .resolution = 8, + .max_convrate = 7, + }, [adm1032] = { .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT | LM90_HAVE_BROKEN_ALERT | LM90_HAVE_CRIT @@ -1742,19 +1753,43 @@ static const char *lm90_detect_national(struct i2c_client *client, int chip_id, return name; } -static const char *lm90_detect_analog(struct i2c_client *client, int chip_id, - int config1, int convrate) +static const char *lm90_detect_analog(struct i2c_client *client, bool common_address, + int chip_id, int config1, int convrate) { + int status = i2c_smbus_read_byte_data(client, LM90_REG_STATUS); int config2 = i2c_smbus_read_byte_data(client, ADT7481_REG_CONFIG2); int man_id2 = i2c_smbus_read_byte_data(client, ADT7481_REG_MAN_ID); int chip_id2 = i2c_smbus_read_byte_data(client, ADT7481_REG_CHIP_ID); int address = client->addr; const char *name = NULL; - if (config2 < 0 || man_id2 < 0 || chip_id2 < 0) + if (status < 0 || config2 < 0 || man_id2 < 0 || chip_id2 < 0) return NULL; switch (chip_id) { + case 0x00 ... 0x0f: /* ADM1021, undocumented */ + if (man_id2 == 0x00 && chip_id2 == 0x00 && common_address && + !(status & 0x03) && !(config1 & 0x3f) && !(convrate & 0xf8)) + name = "adm1021"; + break; + case 0x30 ... 0x3f: /* ADM1021A, ADM1023 */ + /* + * ADM1021A and compatible chips will be mis-detected as + * ADM1023. Chips labeled 'ADM1021A' and 'ADM1023' were both + * found to have a Chip ID of 0x3c. + * ADM1021A does not officially support low byte registers + * (0x12 .. 0x14), but a chip labeled ADM1021A does support it. + * Official support for the temperature offset high byte + * register (0x11) was added to revision F of the ADM1021A + * datasheet. + * It is currently unknown if there is a means to distinguish + * ADM1021A from ADM1023, and/or if revisions of ADM1021A exist + * which differ in functionality from ADM1023. + */ + if (man_id2 == 0x00 && chip_id2 == 0x00 && common_address && + !(status & 0x03) && !(config1 & 0x3f) && !(convrate & 0xf8)) + name = "adm1023"; + break; case 0x40 ... 0x4f: /* ADM1032 */ if (man_id2 == 0x00 && chip_id2 == 0x00 && (address == 0x4c || address == 0x4d) && !(config1 & 0x3f) && @@ -1795,6 +1830,7 @@ static const char *lm90_detect_analog(struct i2c_client *client, int chip_id, break; case 0x94: /* ADT7483 */ if (man_id2 == 0x41 && chip_id2 == 0x83 && + common_address && ((address >= 0x18 && address <= 0x1a) || (address >= 0x29 && address <= 0x2b) || (address >= 0x4c && address <= 0x4e)) && @@ -2175,7 +2211,8 @@ static int lm90_detect(struct i2c_client *client, struct i2c_board_info *info) name = lm90_detect_national(client, chip_id, config1, convrate); break; case 0x41: /* Analog Devices */ - name = lm90_detect_analog(client, chip_id, config1, convrate); + name = lm90_detect_analog(client, common_address, chip_id, config1, + convrate); break; case 0x47: /* GMT */ name = lm90_detect_gmt(client, chip_id, config1, convrate); From 37d1dc8d48cf266ffcb83b223d2dd0667a73e830 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Mon, 22 Nov 2021 13:00:34 -0800 Subject: [PATCH 31/85] hwmon: (lm90) Add remaining chips supported by adm1021 driver All chips supported by the ADM1021 driver are also supported by the LM90 driver. Make that support official. After this change, the adm1021 driver is only needed if the lm90 driver is disabled. Also, the adm1021 driver misdetects a variety of chips as MAX1617A, which is unwanted if any of those chips is in the system. For this reason. make the adm1021 driver dependent on !SENSORS_LM90 to show that it is not needed if the lm90 driver is enabled, and to avoid misdetection if a chip supported by the lm90 driver is in the system. Devicetree nodes are not added for the added chips since it is quite unlikely that such old chips will ever be used in a devicetree based system. They can be added later if needed. Signed-off-by: Guenter Roeck --- Documentation/hwmon/lm90.rst | 34 +++++++++++++++++++++++++++++++++- drivers/hwmon/Kconfig | 1 + drivers/hwmon/lm90.c | 26 ++++++++++++++++++++++++++ 3 files changed, 60 insertions(+), 1 deletion(-) diff --git a/Documentation/hwmon/lm90.rst b/Documentation/hwmon/lm90.rst index 075be136210b..94dcc79168d3 100644 --- a/Documentation/hwmon/lm90.rst +++ b/Documentation/hwmon/lm90.rst @@ -355,6 +355,38 @@ Supported chips: https://www.ti.com/lit/gpn/tmp461 + * Philips NE1617, NE1617A + + Prefix: 'max1617' (probably detected as a max1617) + + Addresses scanned: I2C 0x18 - 0x1a, 0x29 - 0x2b, 0x4c - 0x4e + + Datasheets: Publicly available at the Philips website + + * Genesys Logic GL523SM + + Prefix: 'gl523sm' + + Addresses scanned: I2C 0x18 - 0x1a, 0x29 - 0x2b, 0x4c - 0x4e + + Datasheet: + + * TI THMC10 + + Prefix: 'thmc10' + + Addresses scanned: I2C 0x18 - 0x1a, 0x29 - 0x2b, 0x4c - 0x4e + + Datasheet: Publicly available at the TI website + + * Onsemi MC1066 + + Prefix: 'mc1066' + + Addresses scanned: I2C 0x18 - 0x1a, 0x29 - 0x2b, 0x4c - 0x4e + + Datasheet: Publicly available at the Onsemi website + Author: Jean Delvare @@ -385,7 +417,7 @@ features: LM84: * 8 bit sensor resolution -ADM1021, MAX1617: +ADM1021, GL523SM, MAX1617, NE1617, NE1617A, THMC10: * 8 bit sensor resolution * Low temperature limits diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 50fa255b1e3c..bb952287fcee 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -100,6 +100,7 @@ config SENSORS_AD7418 config SENSORS_ADM1021 tristate "Analog Devices ADM1021 and compatibles" depends on I2C + depends on SENSORS_LM90=n help If you say yes here you get support for Analog Devices ADM1021 and ADM1023 sensor chips and clones: Maxim MAX1617 and MAX1617A, diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c index 2c4a66b93fde..d33faa725f52 100644 --- a/drivers/hwmon/lm90.c +++ b/drivers/hwmon/lm90.c @@ -233,6 +233,7 @@ static const struct i2c_device_id lm90_id[] = { { "adt7482", adt7481 }, { "adt7483a", adt7481 }, { "g781", g781 }, + { "gl523sm", max1617 }, { "lm84", lm84 }, { "lm86", lm86 }, { "lm89", lm86 }, @@ -254,9 +255,11 @@ static const struct i2c_device_id lm90_id[] = { { "max6692", max6648 }, { "max6695", max6696 }, { "max6696", max6696 }, + { "mc1066", max1617 }, { "nct1008", adt7461a }, { "w83l771", w83l771 }, { "sa56004", sa56004 }, + { "thmc10", max1617 }, { "tmp451", tmp451 }, { "tmp461", tmp461 }, { } @@ -2127,6 +2130,18 @@ static const char *lm90_detect_gmt(struct i2c_client *client, int chip_id, return NULL; } +static const char *lm90_detect_ti49(struct i2c_client *client, bool common_address, + int chip_id, int config1, int convrate) +{ + if (common_address && chip_id == 0x00 && !(config1 & 0x3f) && !(convrate & 0xf8)) { + /* THMC10: Unsupported registers return 0xff */ + if (i2c_smbus_read_byte_data(client, LM90_REG_REMOTE_TEMPL) == 0xff && + i2c_smbus_read_byte_data(client, LM90_REG_REMOTE_CRIT) == 0xff) + return "thmc10"; + } + return NULL; +} + static const char *lm90_detect_ti(struct i2c_client *client, int chip_id, int config1, int convrate) { @@ -2210,6 +2225,10 @@ static int lm90_detect(struct i2c_client *client, struct i2c_board_info *info) case 0x01: /* National Semiconductor */ name = lm90_detect_national(client, chip_id, config1, convrate); break; + case 0x23: /* Genesys Logic */ + if (common_address && !(config1 & 0x3f) && !(convrate & 0xf8)) + name = "gl523sm"; + break; case 0x41: /* Analog Devices */ name = lm90_detect_analog(client, common_address, chip_id, config1, convrate); @@ -2217,10 +2236,17 @@ static int lm90_detect(struct i2c_client *client, struct i2c_board_info *info) case 0x47: /* GMT */ name = lm90_detect_gmt(client, chip_id, config1, convrate); break; + case 0x49: /* TI */ + name = lm90_detect_ti49(client, common_address, chip_id, config1, convrate); + break; case 0x4d: /* Maxim Integrated */ name = lm90_detect_maxim(client, common_address, chip_id, config1, convrate); break; + case 0x54: /* ON MC1066, Microchip TC1068, TCM1617 (originally TelCom) */ + if (common_address && !(config1 & 0x3f) && !(convrate & 0xf8)) + name = "mc1066"; + break; case 0x55: /* TI */ name = lm90_detect_ti(client, chip_id, config1, convrate); break; From df18fccd99e3d1b59d44fc04e7e48ccbef283c0e Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Mon, 27 Dec 2021 12:10:36 -0800 Subject: [PATCH 32/85] hwmon: (lm90) Combine lm86 and lm90 configuration LM86 and LM90 support exactly the same features, so there is no need to keep their configuration options separate. Combine to reduce data size. Signed-off-by: Guenter Roeck --- drivers/hwmon/lm90.c | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c index d33faa725f52..2384fa7dd5b2 100644 --- a/drivers/hwmon/lm90.c +++ b/drivers/hwmon/lm90.c @@ -123,7 +123,7 @@ static const unsigned short normal_i2c[] = { 0x4d, 0x4e, 0x4f, I2C_CLIENT_END }; enum chips { adm1023, adm1032, adt7461, adt7461a, adt7481, - g781, lm84, lm86, lm90, lm99, + g781, lm84, lm90, lm99, max1617, max6642, max6646, max6648, max6654, max6657, max6659, max6680, max6696, sa56004, tmp451, tmp461, w83l771, }; @@ -235,8 +235,8 @@ static const struct i2c_device_id lm90_id[] = { { "g781", g781 }, { "gl523sm", max1617 }, { "lm84", lm84 }, - { "lm86", lm86 }, - { "lm89", lm86 }, + { "lm86", lm90 }, + { "lm89", lm90 }, { "lm90", lm90 }, { "lm99", lm99 }, { "max1617", max1617 }, @@ -289,11 +289,11 @@ static const struct of_device_id __maybe_unused lm90_of_match[] = { }, { .compatible = "national,lm86", - .data = (void *)lm86 + .data = (void *)lm90 }, { .compatible = "national,lm89", - .data = (void *)lm86 + .data = (void *)lm90 }, { .compatible = "national,lm99", @@ -443,13 +443,6 @@ static const struct lm90_params lm90_params[] = { .flags = LM90_HAVE_ALARMS, .resolution = 8, }, - [lm86] = { - .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT - | LM90_HAVE_CRIT | LM90_HAVE_ALARMS | LM90_HAVE_LOW - | LM90_HAVE_CONVRATE | LM90_HAVE_REMOTE_EXT, - .alert_alarms = 0x7b, - .max_convrate = 9, - }, [lm90] = { .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT | LM90_HAVE_CRIT | LM90_HAVE_ALARMS | LM90_HAVE_LOW From af4540b112c48ac523f964f5014b48291490ac18 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Thu, 6 Jan 2022 12:12:31 -0800 Subject: [PATCH 33/85] hwmon: (lm90) Add explicit support for NCT210 Unlike ADM1023 and compatible chips, NCT210 does not support a temperature offset register. A real chip was found to have a chip revision of 0x3f. Use it to detect NCT210 explicitly. Signed-off-by: Guenter Roeck --- Documentation/hwmon/lm90.rst | 14 ++++++++++++++ drivers/hwmon/Kconfig | 2 +- drivers/hwmon/lm90.c | 18 ++++++++++++++++-- 3 files changed, 31 insertions(+), 3 deletions(-) diff --git a/Documentation/hwmon/lm90.rst b/Documentation/hwmon/lm90.rst index 94dcc79168d3..ca8368ac1cf2 100644 --- a/Documentation/hwmon/lm90.rst +++ b/Documentation/hwmon/lm90.rst @@ -137,6 +137,16 @@ Supported chips: https://www.onsemi.com/PowerSolutions/product.do?id=NCT1008 + * ON Semiconductor NCT210 + + Prefix: 'adm1021' + + Addresses scanned: I2C 0x18 - 0x1a, 0x29 - 0x2b, 0x4c - 0x4e + + Datasheet: Publicly available at the ON Semiconductor website + + https://www.onsemi.com/PowerSolutions/product.do?id=NCT210 + * Maxim MAX1617 Prefix: 'max1617' @@ -421,6 +431,10 @@ ADM1021, GL523SM, MAX1617, NE1617, NE1617A, THMC10: * 8 bit sensor resolution * Low temperature limits +NCT210: + * 11 bit sensor resolution for remote temperature sensor + * Low temperature limits + ADM1021A, ADM1023: * Temperature offset register for remote temperature sensor * 11 bit resolution for remote temperature sensor diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index bb952287fcee..d30ea2fea3e2 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -1365,7 +1365,7 @@ config SENSORS_LM90 Maxim MAX1617, MAX6642, MAX6646, MAX6647, MAX6648, MAX6649, MAX6654, MAX6657, MAX6658, MAX6659, MAX6680, MAX6681, MAX6692, MAX6695, MAX6696, - ON Semiconductor NCT1008, Winbond/Nuvoton W83L771W/G/AWG/ASG, + ON Semiconductor NCT1008, NCT210, Winbond/Nuvoton W83L771W/G/AWG/ASG, Philips SA56004, GMT G781, Texas Instruments TMP451 and TMP461 sensor chips. diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c index 2384fa7dd5b2..9939a77ac00a 100644 --- a/drivers/hwmon/lm90.c +++ b/drivers/hwmon/lm90.c @@ -125,7 +125,7 @@ static const unsigned short normal_i2c[] = { enum chips { adm1023, adm1032, adt7461, adt7461a, adt7481, g781, lm84, lm90, lm99, max1617, max6642, max6646, max6648, max6654, max6657, max6659, max6680, max6696, - sa56004, tmp451, tmp461, w83l771, + nct210, sa56004, tmp451, tmp461, w83l771, }; /* @@ -257,6 +257,7 @@ static const struct i2c_device_id lm90_id[] = { { "max6696", max6696 }, { "mc1066", max1617 }, { "nct1008", adt7461a }, + { "nct210", nct210 }, { "w83l771", w83l771 }, { "sa56004", sa56004 }, { "thmc10", max1617 }, @@ -533,6 +534,14 @@ static const struct lm90_params lm90_params[] = { .reg_status2 = MAX6696_REG_STATUS2, .reg_local_ext = MAX6657_REG_LOCAL_TEMPL, }, + [nct210] = { + .flags = LM90_HAVE_ALARMS | LM90_HAVE_BROKEN_ALERT + | LM90_HAVE_REM_LIMIT_EXT | LM90_HAVE_LOW | LM90_HAVE_CONVRATE + | LM90_HAVE_REMOTE_EXT, + .alert_alarms = 0x7c, + .resolution = 11, + .max_convrate = 7, + }, [w83l771] = { .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT | LM90_HAVE_CRIT | LM90_HAVE_ALARMS | LM90_HAVE_LOW | LM90_HAVE_CONVRATE @@ -1768,7 +1777,7 @@ static const char *lm90_detect_analog(struct i2c_client *client, bool common_add !(status & 0x03) && !(config1 & 0x3f) && !(convrate & 0xf8)) name = "adm1021"; break; - case 0x30 ... 0x3f: /* ADM1021A, ADM1023 */ + case 0x30 ... 0x3e: /* ADM1021A, ADM1023 */ /* * ADM1021A and compatible chips will be mis-detected as * ADM1023. Chips labeled 'ADM1021A' and 'ADM1023' were both @@ -1786,6 +1795,11 @@ static const char *lm90_detect_analog(struct i2c_client *client, bool common_add !(status & 0x03) && !(config1 & 0x3f) && !(convrate & 0xf8)) name = "adm1023"; break; + case 0x3f: /* NCT210 */ + if (man_id2 == 0x00 && chip_id2 == 0x00 && common_address && + !(status & 0x03) && !(config1 & 0x3f) && !(convrate & 0xf8)) + name = "nct210"; + break; case 0x40 ... 0x4f: /* ADM1032 */ if (man_id2 == 0x00 && chip_id2 == 0x00 && (address == 0x4c || address == 0x4d) && !(config1 & 0x3f) && From 2c6cb6c557858672cafb6a5dc89a1df993420831 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Thu, 2 Dec 2021 21:58:25 -0800 Subject: [PATCH 34/85] hwmon: (lm90) Add support for ON Semiconductor NCT214 and NCT72 NCT214 and NCT72 are compatible to ADT7461/ADT7461A but have full PEC (packet error checking) support. PEC support is undocumented. Both chips support the undocumented secondary chip and manufacturer ID registers at 0x3e and 0x3f, and return 0x61 as chip ID. Use this information to improve the accuracy of chip detection code. Signed-off-by: Guenter Roeck --- Documentation/hwmon/lm90.rst | 20 ++++++++++++++++++++ drivers/hwmon/Kconfig | 3 ++- drivers/hwmon/lm90.c | 35 ++++++++++++++++++++++++++++++++++- 3 files changed, 56 insertions(+), 2 deletions(-) diff --git a/Documentation/hwmon/lm90.rst b/Documentation/hwmon/lm90.rst index ca8368ac1cf2..91947825e734 100644 --- a/Documentation/hwmon/lm90.rst +++ b/Documentation/hwmon/lm90.rst @@ -147,6 +147,26 @@ Supported chips: https://www.onsemi.com/PowerSolutions/product.do?id=NCT210 + * ON Semiconductor NCT214 + + Prefix: 'nct214' + + Addresses scanned: I2C 0x18 - 0x1a, 0x29 - 0x2b, 0x4c - 0x4e + + Datasheet: Publicly available at the ON Semiconductor website + + https://www.onsemi.com/PowerSolutions/product.do?id=NCT214 + + * ON Semiconductor NCT72 + + Prefix: 'nct72' + + Addresses scanned: I2C 0x4c - 0x4d + + Datasheet: Publicly available at the ON Semiconductor website + + https://www.onsemi.com/PowerSolutions/product.do?id=NCT72 + * Maxim MAX1617 Prefix: 'max1617' diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index d30ea2fea3e2..9353d207f254 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -1365,7 +1365,8 @@ config SENSORS_LM90 Maxim MAX1617, MAX6642, MAX6646, MAX6647, MAX6648, MAX6649, MAX6654, MAX6657, MAX6658, MAX6659, MAX6680, MAX6681, MAX6692, MAX6695, MAX6696, - ON Semiconductor NCT1008, NCT210, Winbond/Nuvoton W83L771W/G/AWG/ASG, + ON Semiconductor NCT1008, NCT210, NCT72, NCT214, + Winbond/Nuvoton W83L771W/G/AWG/ASG, Philips SA56004, GMT G781, Texas Instruments TMP451 and TMP461 sensor chips. diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c index 9939a77ac00a..63bd7c3dcc6b 100644 --- a/drivers/hwmon/lm90.c +++ b/drivers/hwmon/lm90.c @@ -69,6 +69,9 @@ * / ON Semiconductor. The chips are similar to ADT7461 but support two external * temperature sensors. * + * This driver also supports NCT72 and NCT214 from ON Semiconductor. The chips + * are similar to ADT7461/ADT7461A but have full PEC support (undocumented). + * * This driver also supports the SA56004 from Philips. This device is * pin-compatible with the LM86, the ED/EDP parts are also address-compatible. * @@ -125,7 +128,7 @@ static const unsigned short normal_i2c[] = { enum chips { adm1023, adm1032, adt7461, adt7461a, adt7481, g781, lm84, lm90, lm99, max1617, max6642, max6646, max6648, max6654, max6657, max6659, max6680, max6696, - nct210, sa56004, tmp451, tmp461, w83l771, + nct210, nct72, sa56004, tmp451, tmp461, w83l771, }; /* @@ -258,6 +261,8 @@ static const struct i2c_device_id lm90_id[] = { { "mc1066", max1617 }, { "nct1008", adt7461a }, { "nct210", nct210 }, + { "nct214", nct72 }, + { "nct72", nct72 }, { "w83l771", w83l771 }, { "sa56004", sa56004 }, { "thmc10", max1617 }, @@ -348,6 +353,14 @@ static const struct of_device_id __maybe_unused lm90_of_match[] = { .compatible = "onnn,nct1008", .data = (void *)adt7461a }, + { + .compatible = "onnn,nct214", + .data = (void *)nct72 + }, + { + .compatible = "onnn,nct72", + .data = (void *)nct72 + }, { .compatible = "winbond,w83l771", .data = (void *)w83l771 @@ -534,6 +547,15 @@ static const struct lm90_params lm90_params[] = { .reg_status2 = MAX6696_REG_STATUS2, .reg_local_ext = MAX6657_REG_LOCAL_TEMPL, }, + [nct72] = { + .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT + | LM90_HAVE_BROKEN_ALERT | LM90_HAVE_EXTENDED_TEMP + | LM90_HAVE_CRIT | LM90_HAVE_PEC | LM90_HAVE_UNSIGNED_TEMP + | LM90_HAVE_LOW | LM90_HAVE_CONVRATE | LM90_HAVE_REMOTE_EXT, + .alert_alarms = 0x7c, + .max_convrate = 10, + .resolution = 10, + }, [nct210] = { .flags = LM90_HAVE_ALARMS | LM90_HAVE_BROKEN_ALERT | LM90_HAVE_REM_LIMIT_EXT | LM90_HAVE_LOW | LM90_HAVE_CONVRATE @@ -1818,12 +1840,23 @@ static const char *lm90_detect_analog(struct i2c_client *client, bool common_add convrate <= 0x0a) name = "nct1008"; break; + case 0x55: /* NCT72 */ + if (man_id2 == 0x41 && chip_id2 == 0x61 && + (address == 0x4c || address == 0x4d) && !(config1 & 0x1b) && + convrate <= 0x0a) + name = "nct72"; + break; case 0x57: /* ADT7461A, NCT1008 (datasheet rev. 3) */ if (man_id2 == 0x41 && chip_id2 == 0x61 && (address == 0x4c || address == 0x4d) && !(config1 & 0x1b) && convrate <= 0x0a) name = "adt7461a"; break; + case 0x5a: /* NCT214 */ + if (man_id2 == 0x41 && chip_id2 == 0x61 && + common_address && !(config1 & 0x1b) && convrate <= 0x0a) + name = "nct214"; + break; case 0x62: /* ADT7481, undocumented */ if (man_id2 == 0x41 && chip_id2 == 0x81 && (address == 0x4b || address == 0x4c) && !(config1 & 0x10) && From d8521f82dfb67442777a3f7ec2fdc1237384195e Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Mon, 13 Dec 2021 11:26:54 -0800 Subject: [PATCH 35/85] hwmon: (lm90) Add support for ON Semiconductor NCT218 NCT218 is compatible to NCT72 and NCT214. It also supports PEC (packet error checking). Similar to NCT72 and NCT214, PEC support is undocumented. Unlike NCT214 and NCT72, NCT218 does not support the undocumented secondary chip and manufacturer ID registers at 0x3e and 0x3f and returns 0x00 when reading those registers. The value for the chip revision register is not documented but was observed to be 0xca. Use that information to improve chip detection accuracy. Signed-off-by: Guenter Roeck --- Documentation/hwmon/lm90.rst | 10 ++++++++++ drivers/hwmon/Kconfig | 2 +- drivers/hwmon/lm90.c | 31 +++++++++++++++++++++++++++++-- 3 files changed, 40 insertions(+), 3 deletions(-) diff --git a/Documentation/hwmon/lm90.rst b/Documentation/hwmon/lm90.rst index 91947825e734..ef8b55c17118 100644 --- a/Documentation/hwmon/lm90.rst +++ b/Documentation/hwmon/lm90.rst @@ -157,6 +157,16 @@ Supported chips: https://www.onsemi.com/PowerSolutions/product.do?id=NCT214 + * ON Semiconductor NCT218 + + Prefix: 'nct218' + + Addresses scanned: I2C 0x4c - 0x4d + + Datasheet: Publicly available at the ON Semiconductor website + + https://www.onsemi.com/PowerSolutions/product.do?id=NCT218 + * ON Semiconductor NCT72 Prefix: 'nct72' diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 9353d207f254..32c605eaec7e 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -1365,7 +1365,7 @@ config SENSORS_LM90 Maxim MAX1617, MAX6642, MAX6646, MAX6647, MAX6648, MAX6649, MAX6654, MAX6657, MAX6658, MAX6659, MAX6680, MAX6681, MAX6692, MAX6695, MAX6696, - ON Semiconductor NCT1008, NCT210, NCT72, NCT214, + ON Semiconductor NCT1008, NCT210, NCT72, NCT214, NCT218, Winbond/Nuvoton W83L771W/G/AWG/ASG, Philips SA56004, GMT G781, Texas Instruments TMP451 and TMP461 sensor chips. diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c index 63bd7c3dcc6b..a553c611624b 100644 --- a/drivers/hwmon/lm90.c +++ b/drivers/hwmon/lm90.c @@ -69,8 +69,9 @@ * / ON Semiconductor. The chips are similar to ADT7461 but support two external * temperature sensors. * - * This driver also supports NCT72 and NCT214 from ON Semiconductor. The chips - * are similar to ADT7461/ADT7461A but have full PEC support (undocumented). + * This driver also supports NCT72, NCT214, and NCT218 from ON Semiconductor. + * The chips are similar to ADT7461/ADT7461A but have full PEC support + * (undocumented). * * This driver also supports the SA56004 from Philips. This device is * pin-compatible with the LM86, the ED/EDP parts are also address-compatible. @@ -262,6 +263,7 @@ static const struct i2c_device_id lm90_id[] = { { "nct1008", adt7461a }, { "nct210", nct210 }, { "nct214", nct72 }, + { "nct218", nct72 }, { "nct72", nct72 }, { "w83l771", w83l771 }, { "sa56004", sa56004 }, @@ -357,6 +359,10 @@ static const struct of_device_id __maybe_unused lm90_of_match[] = { .compatible = "onnn,nct214", .data = (void *)nct72 }, + { + .compatible = "onnn,nct218", + .data = (void *)nct72 + }, { .compatible = "onnn,nct72", .data = (void *)nct72 @@ -1780,6 +1786,24 @@ static const char *lm90_detect_national(struct i2c_client *client, int chip_id, return name; } +static const char *lm90_detect_on(struct i2c_client *client, int chip_id, int config1, + int convrate) +{ + int address = client->addr; + const char *name = NULL; + + switch (chip_id) { + case 0xca: /* NCT218 */ + if ((address == 0x4c || address == 0x4d) && !(config1 & 0x1b) && + convrate <= 0x0a) + name = "nct218"; + break; + default: + break; + } + return name; +} + static const char *lm90_detect_analog(struct i2c_client *client, bool common_address, int chip_id, int config1, int convrate) { @@ -2265,6 +2289,9 @@ static int lm90_detect(struct i2c_client *client, struct i2c_board_info *info) case 0x01: /* National Semiconductor */ name = lm90_detect_national(client, chip_id, config1, convrate); break; + case 0x1a: /* ON */ + name = lm90_detect_on(client, chip_id, config1, convrate); + break; case 0x23: /* Genesys Logic */ if (common_address && !(config1 & 0x3f) && !(convrate & 0xf8)) name = "gl523sm"; From 41e6d7215d79f98758def6ef60161fd891bbac0d Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Wed, 15 Dec 2021 19:58:01 -0800 Subject: [PATCH 36/85] hwmon: (lm90) Add support for ADT7421 ADT7421 is similar to ADT7461A but supports configurable Beta Compensation. Packet Error Checking (PEC) is supported but undocumented. A devicetree node is not added for the added chip since it is quite unlikely that such an old chip will ever be used in a devicetree based system. It can be added later if needed. Signed-off-by: Guenter Roeck --- drivers/hwmon/lm90.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c index a553c611624b..c7bf288c8401 100644 --- a/drivers/hwmon/lm90.c +++ b/drivers/hwmon/lm90.c @@ -231,6 +231,7 @@ static const struct i2c_device_id lm90_id[] = { { "adm1021", max1617 }, { "adm1023", adm1023 }, { "adm1032", adm1032 }, + { "adt7421", adt7461a }, { "adt7461", adt7461 }, { "adt7461a", adt7461a }, { "adt7481", adt7481 }, @@ -1818,11 +1819,18 @@ static const char *lm90_detect_analog(struct i2c_client *client, bool common_add return NULL; switch (chip_id) { - case 0x00 ... 0x0f: /* ADM1021, undocumented */ + case 0x00 ... 0x03: /* ADM1021 */ + case 0x05 ... 0x0f: if (man_id2 == 0x00 && chip_id2 == 0x00 && common_address && !(status & 0x03) && !(config1 & 0x3f) && !(convrate & 0xf8)) name = "adm1021"; break; + case 0x04: /* ADT7421 (undocumented) */ + if (man_id2 == 0x41 && chip_id2 == 0x21 && + (address == 0x4c || address == 0x4d) && + (config1 & 0x0b) == 0x08 && convrate <= 0x0a) + name = "adt7421"; + break; case 0x30 ... 0x3e: /* ADM1021A, ADM1023 */ /* * ADM1021A and compatible chips will be mis-detected as From b1526b38e363bd87840c0e2c22a16e6fd42fea56 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Tue, 28 Dec 2021 07:59:26 -0800 Subject: [PATCH 37/85] hwmon: (lm90) Only disable alerts if not already disabled It was observed that the alert handler may be called from the i2c core even after alerts have already been disabled. Only disable alerts if they have not already been disabled. Signed-off-by: Guenter Roeck --- drivers/hwmon/lm90.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c index c7bf288c8401..3f49b2e5e9c4 100644 --- a/drivers/hwmon/lm90.c +++ b/drivers/hwmon/lm90.c @@ -2660,8 +2660,10 @@ static void lm90_alert(struct i2c_client *client, enum i2c_alert_protocol type, if ((data->flags & LM90_HAVE_BROKEN_ALERT) && (data->current_alarms & data->alert_alarms)) { - dev_dbg(&client->dev, "Disabling ALERT#\n"); - lm90_update_confreg(data, data->config | 0x80); + if (!(data->config & 0x80)) { + dev_dbg(&client->dev, "Disabling ALERT#\n"); + lm90_update_confreg(data, data->config | 0x80); + } schedule_delayed_work(&data->alert_work, max_t(int, HZ, msecs_to_jiffies(data->update_interval))); } From f63f6cce28cd90f7caa68b4a3690896523d142a4 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Thu, 6 Jan 2022 15:07:48 -0800 Subject: [PATCH 38/85] hwmon: (lm90) Add explicit support for ADM1020 ADM1020 is compatible with ADM1021 but has a separate chip revision and a limited I2C address range. Signed-off-by: Guenter Roeck --- Documentation/hwmon/lm90.rst | 10 +++++++++- drivers/hwmon/Kconfig | 4 ++-- drivers/hwmon/lm90.c | 10 +++++++++- 3 files changed, 20 insertions(+), 4 deletions(-) diff --git a/Documentation/hwmon/lm90.rst b/Documentation/hwmon/lm90.rst index ef8b55c17118..f8dd2de9a41c 100644 --- a/Documentation/hwmon/lm90.rst +++ b/Documentation/hwmon/lm90.rst @@ -51,6 +51,14 @@ Supported chips: http://www.national.com/mpf/LM/LM86.html + * Analog Devices ADM1020 + + Prefix: 'adm1020' + + Addresses scanned: I2C 0x4c - 0x4e + + Datasheet: Publicly available at the Analog Devices website + * Analog Devices ADM1021 Prefix: 'adm1021' @@ -457,7 +465,7 @@ features: LM84: * 8 bit sensor resolution -ADM1021, GL523SM, MAX1617, NE1617, NE1617A, THMC10: +ADM1020, ADM1021, GL523SM, MAX1617, NE1617, NE1617A, THMC10: * 8 bit sensor resolution * Low temperature limits diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 32c605eaec7e..494539e4be3d 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -1360,8 +1360,8 @@ config SENSORS_LM90 depends on I2C help If you say yes here you get support for National Semiconductor LM84, - LM90, LM86, LM89 and LM99, Analog Devices ADM2021, ADM1021A, ADM1023, - ADM1032, ADT7461, ADT7461A, ADT7481, ADT7482, and ADT7483A, + LM90, LM86, LM89 and LM99, Analog Devices ADM1020, ADM2021, ADM1021A, + ADM1023, ADM1032, ADT7461, ADT7461A, ADT7481, ADT7482, and ADT7483A, Maxim MAX1617, MAX6642, MAX6646, MAX6647, MAX6648, MAX6649, MAX6654, MAX6657, MAX6658, MAX6659, MAX6680, MAX6681, MAX6692, MAX6695, MAX6696, diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c index 3f49b2e5e9c4..42f6d9d6677b 100644 --- a/drivers/hwmon/lm90.c +++ b/drivers/hwmon/lm90.c @@ -228,6 +228,7 @@ enum chips { adm1023, adm1032, adt7461, adt7461a, adt7481, */ static const struct i2c_device_id lm90_id[] = { + { "adm1020", max1617 }, { "adm1021", max1617 }, { "adm1023", adm1023 }, { "adm1032", adm1032 }, @@ -1831,7 +1832,8 @@ static const char *lm90_detect_analog(struct i2c_client *client, bool common_add (config1 & 0x0b) == 0x08 && convrate <= 0x0a) name = "adt7421"; break; - case 0x30 ... 0x3e: /* ADM1021A, ADM1023 */ + case 0x30 ... 0x38: /* ADM1021A, ADM1023 */ + case 0x3a ... 0x3e: /* * ADM1021A and compatible chips will be mis-detected as * ADM1023. Chips labeled 'ADM1021A' and 'ADM1023' were both @@ -1849,6 +1851,12 @@ static const char *lm90_detect_analog(struct i2c_client *client, bool common_add !(status & 0x03) && !(config1 & 0x3f) && !(convrate & 0xf8)) name = "adm1023"; break; + case 0x39: /* ADM1020 (undocumented) */ + if (man_id2 == 0x00 && chip_id2 == 0x00 && + (address == 0x4c || address == 0x4d || address == 0x4e) && + !(status & 0x03) && !(config1 & 0x3f) && !(convrate & 0xf8)) + name = "adm1020"; + break; case 0x3f: /* NCT210 */ if (man_id2 == 0x00 && chip_id2 == 0x00 && common_address && !(status & 0x03) && !(config1 & 0x3f) && !(convrate & 0xf8)) From 9a1986637021f4578937d18f4bc0fa2062b992b5 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Thu, 6 Jan 2022 19:52:36 -0800 Subject: [PATCH 39/85] hwmon: (lm90) Add support and detection of Philips/NXP NE1618 NE1618 is similar to NE1617 but supports manufacturer and chip ID registers as well as 11 bit external temperature resolution. Signed-off-by: Guenter Roeck --- Documentation/hwmon/lm90.rst | 10 +++++++- drivers/hwmon/Kconfig | 2 +- drivers/hwmon/lm90.c | 44 ++++++++++++++++++++++++++---------- 3 files changed, 42 insertions(+), 14 deletions(-) diff --git a/Documentation/hwmon/lm90.rst b/Documentation/hwmon/lm90.rst index f8dd2de9a41c..23af17a0ab44 100644 --- a/Documentation/hwmon/lm90.rst +++ b/Documentation/hwmon/lm90.rst @@ -411,6 +411,14 @@ Supported chips: Datasheets: Publicly available at the Philips website + * Philips NE1618 + + Prefix: 'ne1618' + + Addresses scanned: I2C 0x18 - 0x1a, 0x29 - 0x2b, 0x4c - 0x4e + + Datasheets: Publicly available at the Philips website + * Genesys Logic GL523SM Prefix: 'gl523sm' @@ -469,7 +477,7 @@ ADM1020, ADM1021, GL523SM, MAX1617, NE1617, NE1617A, THMC10: * 8 bit sensor resolution * Low temperature limits -NCT210: +NCT210, NE1618: * 11 bit sensor resolution for remote temperature sensor * Low temperature limits diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 494539e4be3d..981df62bd6ab 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -1367,7 +1367,7 @@ config SENSORS_LM90 MAX6696, ON Semiconductor NCT1008, NCT210, NCT72, NCT214, NCT218, Winbond/Nuvoton W83L771W/G/AWG/ASG, - Philips SA56004, GMT G781, Texas Instruments TMP451 and TMP461 + Philips NE1618, SA56004, GMT G781, Texas Instruments TMP451 and TMP461 sensor chips. This driver can also be built as a module. If so, the module diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c index 42f6d9d6677b..4694807fa698 100644 --- a/drivers/hwmon/lm90.c +++ b/drivers/hwmon/lm90.c @@ -87,6 +87,9 @@ * This driver also supports MAX1617 and various clones such as G767 * and NE1617. Such clones will be detected as MAX1617. * + * This driver also supports NE1618 from Philips. It is similar to NE1617 + * but supports 11 bit external temperature values. + * * Since the LM90 was the first chipset supported by this driver, most * comments will refer to this chipset, but are actually general and * concern all supported chipsets, unless mentioned otherwise. @@ -129,7 +132,7 @@ static const unsigned short normal_i2c[] = { enum chips { adm1023, adm1032, adt7461, adt7461a, adt7481, g781, lm84, lm90, lm99, max1617, max6642, max6646, max6648, max6654, max6657, max6659, max6680, max6696, - nct210, nct72, sa56004, tmp451, tmp461, w83l771, + nct210, nct72, ne1618, sa56004, tmp451, tmp461, w83l771, }; /* @@ -267,6 +270,7 @@ static const struct i2c_device_id lm90_id[] = { { "nct214", nct72 }, { "nct218", nct72 }, { "nct72", nct72 }, + { "ne1618", ne1618 }, { "w83l771", w83l771 }, { "sa56004", sa56004 }, { "thmc10", max1617 }, @@ -572,6 +576,13 @@ static const struct lm90_params lm90_params[] = { .resolution = 11, .max_convrate = 7, }, + [ne1618] = { + .flags = LM90_PAUSE_FOR_CONFIG | LM90_HAVE_BROKEN_ALERT + | LM90_HAVE_LOW | LM90_HAVE_CONVRATE | LM90_HAVE_REMOTE_EXT, + .alert_alarms = 0x7c, + .resolution = 11, + .max_convrate = 7, + }, [w83l771] = { .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT | LM90_HAVE_CRIT | LM90_HAVE_ALARMS | LM90_HAVE_LOW | LM90_HAVE_CONVRATE @@ -2151,20 +2162,29 @@ static const char *lm90_detect_nuvoton(struct i2c_client *client, int chip_id, return name; } -static const char *lm90_detect_nxp(struct i2c_client *client, int chip_id, - int config1, int convrate) +static const char *lm90_detect_nxp(struct i2c_client *client, bool common_address, + int chip_id, int config1, int convrate) { - int config2 = i2c_smbus_read_byte_data(client, LM90_REG_CONFIG2); int address = client->addr; const char *name = NULL; + int config2; - if (config2 < 0) - return NULL; - - if (address >= 0x48 && address <= 0x4f && chip_id == 0x00 && - !(config1 & 0x2a) && !(config2 & 0xfe) && convrate <= 0x09) - name = "sa56004"; - + switch (chip_id) { + case 0x00: + config2 = i2c_smbus_read_byte_data(client, LM90_REG_CONFIG2); + if (config2 < 0) + return NULL; + if (address >= 0x48 && address <= 0x4f && + !(config1 & 0x2a) && !(config2 & 0xfe) && convrate <= 0x09) + name = "sa56004"; + break; + case 0x80: + if (common_address && !(config1 & 0x3f) && convrate <= 0x07) + name = "ne1618"; + break; + default: + break; + } return name; } @@ -2337,7 +2357,7 @@ static int lm90_detect(struct i2c_client *client, struct i2c_board_info *info) name = lm90_detect_nuvoton(client, chip_id, config1, convrate); break; case 0xa1: /* NXP Semiconductor/Philips */ - name = lm90_detect_nxp(client, chip_id, config1, convrate); + name = lm90_detect_nxp(client, common_address, chip_id, config1, convrate); break; case 0xff: /* MAX1617, G767, NE1617 */ if (common_address && chip_id == 0xff && convrate < 8) From 018b82877046c2644acd43d5a03c0d3aded5caf2 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Thu, 16 Dec 2021 20:05:32 -0800 Subject: [PATCH 40/85] hwmon: (lm90) Add table with supported Analog/ONSEMI devices Add table with device names and known register values for supported devices from Analog / ON Semiconductor. Signed-off-by: Guenter Roeck --- drivers/hwmon/lm90.c | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c index 4694807fa698..c766e548801b 100644 --- a/drivers/hwmon/lm90.c +++ b/drivers/hwmon/lm90.c @@ -1830,6 +1830,42 @@ static const char *lm90_detect_analog(struct i2c_client *client, bool common_add if (status < 0 || config2 < 0 || man_id2 < 0 || chip_id2 < 0) return NULL; + /* + * The following chips should be detected by this function. Known + * register values are listed. Registers 0x3d .. 0x3e are undocumented + * for most of the chips, yet appear to return a well defined value. + * Register 0xff is undocumented for some of the chips. Register 0x3f + * is undocumented for all chips, but also returns a well defined value. + * Values are as reported from real chips unless mentioned otherwise. + * The code below checks values for registers 0x3d, 0x3e, and 0xff, + * but not for register 0x3f. + * + * Chip Register + * 3d 3e 3f fe ff Notes + * ---------------------------------------------------------- + * adm1020 00 00 00 41 39 + * adm1021 00 00 00 41 03 + * adm1021a 00 00 00 41 3c + * adm1023 00 00 00 41 3c same as adm1021a + * adm1032 00 00 00 41 42 + * + * adt7421 21 41 04 41 04 + * adt7461 00 00 00 41 51 + * adt7461a 61 41 05 41 57 + * adt7481 81 41 02 41 62 + * adt7482 - - - 41 65 datasheet + * 82 41 05 41 75 real chip + * adt7483 83 41 04 41 94 + * + * nct72 61 41 07 41 55 + * nct210 00 00 00 41 3f + * nct214 61 41 08 41 5a + * nct1008 - - - 41 57 datasheet rev. 3 + * 61 41 06 41 54 real chip + * + * nvt210 - - - 41 - datasheet + * nvt211 - - - 41 - datasheet + */ switch (chip_id) { case 0x00 ... 0x03: /* ADM1021 */ case 0x05 ... 0x0f: From ca99633ae40937adfe9acf3d01e5c74fbd940927 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sun, 16 Jan 2022 08:55:49 -0800 Subject: [PATCH 41/85] hwmon: (lm90) Support temp_samples attribute Several of the chips supported by this driver support configuring the number of samples (or the fault queue depth) necessary before a fault or alarm is reported. This is done either with a bit in the configuration register or with a separate "consecutive alert" register. Support this functionality with the temp_samples attribute. Signed-off-by: Guenter Roeck --- drivers/hwmon/lm90.c | 105 +++++++++++++++++++++++++++++++++++++------ 1 file changed, 91 insertions(+), 14 deletions(-) diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c index c766e548801b..f54227affe09 100644 --- a/drivers/hwmon/lm90.c +++ b/drivers/hwmon/lm90.c @@ -206,6 +206,7 @@ enum chips { adm1023, adm1032, adt7461, adt7461a, adt7481, #define LM90_HAVE_LOW BIT(15) /* low limits */ #define LM90_HAVE_CONVRATE BIT(16) /* conversion rate */ #define LM90_HAVE_REMOTE_EXT BIT(17) /* extended remote temperature */ +#define LM90_HAVE_FAULTQUEUE BIT(18) /* configurable samples count */ /* LM90 status */ #define LM90_STATUS_LTHRM BIT(0) /* local THERM limit tripped */ @@ -404,6 +405,8 @@ struct lm90_params { u8 resolution; /* 16-bit resolution (default 11 bit) */ u8 reg_status2; /* 2nd status register (optional) */ u8 reg_local_ext; /* Extended local temp register (optional) */ + u8 faultqueue_mask; /* fault queue bit mask */ + u8 faultqueue_depth; /* fault queue depth if mask is used */ }; static const struct lm90_params lm90_params[] = { @@ -419,7 +422,8 @@ static const struct lm90_params lm90_params[] = { .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT | LM90_HAVE_BROKEN_ALERT | LM90_HAVE_CRIT | LM90_HAVE_PARTIAL_PEC | LM90_HAVE_ALARMS - | LM90_HAVE_LOW | LM90_HAVE_CONVRATE | LM90_HAVE_REMOTE_EXT, + | LM90_HAVE_LOW | LM90_HAVE_CONVRATE | LM90_HAVE_REMOTE_EXT + | LM90_HAVE_FAULTQUEUE, .alert_alarms = 0x7c, .max_convrate = 10, }, @@ -433,7 +437,7 @@ static const struct lm90_params lm90_params[] = { | LM90_HAVE_BROKEN_ALERT | LM90_HAVE_EXTENDED_TEMP | LM90_HAVE_CRIT | LM90_HAVE_PARTIAL_PEC | LM90_HAVE_ALARMS | LM90_HAVE_LOW | LM90_HAVE_CONVRATE - | LM90_HAVE_REMOTE_EXT, + | LM90_HAVE_REMOTE_EXT | LM90_HAVE_FAULTQUEUE, .alert_alarms = 0x7c, .max_convrate = 10, .resolution = 10, @@ -442,7 +446,8 @@ static const struct lm90_params lm90_params[] = { .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT | LM90_HAVE_BROKEN_ALERT | LM90_HAVE_EXTENDED_TEMP | LM90_HAVE_CRIT | LM90_HAVE_PEC | LM90_HAVE_ALARMS - | LM90_HAVE_LOW | LM90_HAVE_CONVRATE | LM90_HAVE_REMOTE_EXT, + | LM90_HAVE_LOW | LM90_HAVE_CONVRATE | LM90_HAVE_REMOTE_EXT + | LM90_HAVE_FAULTQUEUE, .alert_alarms = 0x7c, .max_convrate = 10, }, @@ -451,7 +456,8 @@ static const struct lm90_params lm90_params[] = { | LM90_HAVE_BROKEN_ALERT | LM90_HAVE_EXTENDED_TEMP | LM90_HAVE_UNSIGNED_TEMP | LM90_HAVE_PEC | LM90_HAVE_TEMP3 | LM90_HAVE_CRIT | LM90_HAVE_LOW - | LM90_HAVE_CONVRATE | LM90_HAVE_REMOTE_EXT, + | LM90_HAVE_CONVRATE | LM90_HAVE_REMOTE_EXT + | LM90_HAVE_FAULTQUEUE, .alert_alarms = 0x1c7c, .max_convrate = 11, .resolution = 10, @@ -461,7 +467,7 @@ static const struct lm90_params lm90_params[] = { .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT | LM90_HAVE_BROKEN_ALERT | LM90_HAVE_CRIT | LM90_HAVE_ALARMS | LM90_HAVE_LOW | LM90_HAVE_CONVRATE - | LM90_HAVE_REMOTE_EXT, + | LM90_HAVE_REMOTE_EXT | LM90_HAVE_FAULTQUEUE, .alert_alarms = 0x7c, .max_convrate = 7, }, @@ -472,16 +478,22 @@ static const struct lm90_params lm90_params[] = { [lm90] = { .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT | LM90_HAVE_CRIT | LM90_HAVE_ALARMS | LM90_HAVE_LOW - | LM90_HAVE_CONVRATE | LM90_HAVE_REMOTE_EXT, + | LM90_HAVE_CONVRATE | LM90_HAVE_REMOTE_EXT + | LM90_HAVE_FAULTQUEUE, .alert_alarms = 0x7b, .max_convrate = 9, + .faultqueue_mask = BIT(0), + .faultqueue_depth = 3, }, [lm99] = { .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT | LM90_HAVE_CRIT | LM90_HAVE_ALARMS | LM90_HAVE_LOW - | LM90_HAVE_CONVRATE | LM90_HAVE_REMOTE_EXT, + | LM90_HAVE_CONVRATE | LM90_HAVE_REMOTE_EXT + | LM90_HAVE_FAULTQUEUE, .alert_alarms = 0x7b, .max_convrate = 9, + .faultqueue_mask = BIT(0), + .faultqueue_depth = 3, }, [max1617] = { .flags = LM90_HAVE_CONVRATE | LM90_HAVE_BROKEN_ALERT | @@ -492,10 +504,12 @@ static const struct lm90_params lm90_params[] = { }, [max6642] = { .flags = LM90_HAVE_BROKEN_ALERT | LM90_HAVE_EXT_UNSIGNED - | LM90_HAVE_REMOTE_EXT, + | LM90_HAVE_REMOTE_EXT | LM90_HAVE_FAULTQUEUE, .alert_alarms = 0x50, .resolution = 10, .reg_local_ext = MAX6657_REG_LOCAL_TEMPL, + .faultqueue_mask = BIT(4), + .faultqueue_depth = 2, }, [max6646] = { .flags = LM90_HAVE_CRIT | LM90_HAVE_BROKEN_ALERT @@ -553,17 +567,20 @@ static const struct lm90_params lm90_params[] = { .flags = LM90_HAVE_EMERGENCY | LM90_HAVE_EMERGENCY_ALARM | LM90_HAVE_TEMP3 | LM90_HAVE_CRIT | LM90_HAVE_ALARMS | LM90_HAVE_LOW | LM90_HAVE_CONVRATE - | LM90_HAVE_REMOTE_EXT, + | LM90_HAVE_REMOTE_EXT | LM90_HAVE_FAULTQUEUE, .alert_alarms = 0x1c7c, .max_convrate = 6, .reg_status2 = MAX6696_REG_STATUS2, .reg_local_ext = MAX6657_REG_LOCAL_TEMPL, + .faultqueue_mask = BIT(5), + .faultqueue_depth = 4, }, [nct72] = { .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT | LM90_HAVE_BROKEN_ALERT | LM90_HAVE_EXTENDED_TEMP | LM90_HAVE_CRIT | LM90_HAVE_PEC | LM90_HAVE_UNSIGNED_TEMP - | LM90_HAVE_LOW | LM90_HAVE_CONVRATE | LM90_HAVE_REMOTE_EXT, + | LM90_HAVE_LOW | LM90_HAVE_CONVRATE | LM90_HAVE_REMOTE_EXT + | LM90_HAVE_FAULTQUEUE, .alert_alarms = 0x7c, .max_convrate = 10, .resolution = 10, @@ -598,16 +615,18 @@ static const struct lm90_params lm90_params[] = { */ .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT | LM90_HAVE_CRIT | LM90_HAVE_ALARMS | LM90_HAVE_LOW | LM90_HAVE_CONVRATE - | LM90_HAVE_REMOTE_EXT, + | LM90_HAVE_REMOTE_EXT | LM90_HAVE_FAULTQUEUE, .alert_alarms = 0x7b, .max_convrate = 9, .reg_local_ext = SA56004_REG_LOCAL_TEMPL, + .faultqueue_mask = BIT(0), + .faultqueue_depth = 3, }, [tmp451] = { .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT | LM90_HAVE_BROKEN_ALERT | LM90_HAVE_EXTENDED_TEMP | LM90_HAVE_CRIT | LM90_HAVE_UNSIGNED_TEMP | LM90_HAVE_ALARMS | LM90_HAVE_LOW - | LM90_HAVE_CONVRATE | LM90_HAVE_REMOTE_EXT, + | LM90_HAVE_CONVRATE | LM90_HAVE_REMOTE_EXT | LM90_HAVE_FAULTQUEUE, .alert_alarms = 0x7c, .max_convrate = 9, .resolution = 12, @@ -617,7 +636,7 @@ static const struct lm90_params lm90_params[] = { .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT | LM90_HAVE_BROKEN_ALERT | LM90_HAVE_EXTENDED_TEMP | LM90_HAVE_CRIT | LM90_HAVE_ALARMS | LM90_HAVE_LOW | LM90_HAVE_CONVRATE - | LM90_HAVE_REMOTE_EXT, + | LM90_HAVE_REMOTE_EXT | LM90_HAVE_FAULTQUEUE, .alert_alarms = 0x7c, .max_convrate = 9, .resolution = 12, @@ -684,10 +703,13 @@ struct lm90_data { u8 reg_status2; /* 2nd status register (optional) */ u8 reg_local_ext; /* local extension register offset */ u8 reg_remote_ext; /* remote temperature low byte */ + u8 faultqueue_mask; /* fault queue mask */ + u8 faultqueue_depth; /* fault queue mask */ /* registers values */ u16 temp[TEMP_REG_NUM]; u8 temp_hyst; + u8 conalert; u16 reported_alarms; /* alarms reported as sysfs/udev events */ u16 current_alarms; /* current alarms, reported by chip */ u16 alarms; /* alarms not yet reported to user */ @@ -888,6 +910,26 @@ static int lm90_set_convrate(struct i2c_client *client, struct lm90_data *data, return err; } +static int lm90_set_faultqueue(struct i2c_client *client, + struct lm90_data *data, int val) +{ + int err; + + if (data->faultqueue_mask) { + err = lm90_update_confreg(data, val <= data->faultqueue_depth / 2 ? + data->config & ~data->faultqueue_mask : + data->config | data->faultqueue_mask); + } else { + static const u8 values[4] = {0, 2, 6, 0x0e}; + + data->conalert = (data->conalert & 0xf1) | values[val - 1]; + err = lm90_write_reg(data->client, TMP451_REG_CONALERT, + data->conalert); + } + + return err; +} + static int lm90_update_limits(struct device *dev) { struct lm90_data *data = dev_get_drvdata(dev); @@ -910,6 +952,12 @@ static int lm90_update_limits(struct device *dev) return val; data->temp_hyst = val; } + if ((data->flags & LM90_HAVE_FAULTQUEUE) && !data->faultqueue_mask) { + val = lm90_read_reg(client, TMP451_REG_CONALERT); + if (val < 0) + return val; + data->conalert = val; + } val = lm90_read16(client, LM90_REG_REMOTE_LOWH, (data->flags & LM90_HAVE_REM_LIMIT_EXT) ? LM90_REG_REMOTE_LOWL : 0, @@ -1566,6 +1614,28 @@ static int lm90_chip_read(struct device *dev, u32 attr, int channel, long *val) case hwmon_chip_alarms: *val = data->alarms; break; + case hwmon_chip_temp_samples: + if (data->faultqueue_mask) { + *val = (data->config & data->faultqueue_mask) ? + data->faultqueue_depth : 1; + } else { + switch (data->conalert & 0x0e) { + case 0x0: + default: + *val = 1; + break; + case 0x2: + *val = 2; + break; + case 0x6: + *val = 3; + break; + case 0xe: + *val = 4; + break; + } + } + break; default: return -EOPNOTSUPP; } @@ -1590,6 +1660,9 @@ static int lm90_chip_write(struct device *dev, u32 attr, int channel, long val) err = lm90_set_convrate(client, data, clamp_val(val, 0, 100000)); break; + case hwmon_chip_temp_samples: + err = lm90_set_faultqueue(client, data, clamp_val(val, 1, 4)); + break; default: err = -EOPNOTSUPP; break; @@ -1604,6 +1677,7 @@ static umode_t lm90_chip_is_visible(const void *data, u32 attr, int channel) { switch (attr) { case hwmon_chip_update_interval: + case hwmon_chip_temp_samples: return 0644; case hwmon_chip_alarms: return 0444; @@ -2607,7 +2681,8 @@ static int lm90_probe(struct i2c_client *client) data->chip_config[0] |= HWMON_C_ALARMS; if (data->flags & LM90_HAVE_CONVRATE) data->chip_config[0] |= HWMON_C_UPDATE_INTERVAL; - + if (data->flags & LM90_HAVE_FAULTQUEUE) + data->chip_config[0] |= HWMON_C_TEMP_SAMPLES; data->info[1] = &data->temp_info; info = &data->temp_info; @@ -2658,6 +2733,8 @@ static int lm90_probe(struct i2c_client *client) data->channel_config[2] |= HWMON_T_EMERGENCY_ALARM; } + data->faultqueue_mask = lm90_params[data->kind].faultqueue_mask; + data->faultqueue_depth = lm90_params[data->kind].faultqueue_depth; data->reg_local_ext = lm90_params[data->kind].reg_local_ext; if (data->flags & LM90_HAVE_REMOTE_EXT) data->reg_remote_ext = LM90_REG_REMOTE_TEMPL; From 4036a48e20e84930645f20bb817a04dc70286695 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Wed, 13 Mar 2019 14:36:28 -0700 Subject: [PATCH 42/85] hwmon: (pmbus) Add IEEE 754 half precision support to PMBus core Add support for the IEEE 754 half precision data format as specified in PMBus v1.3.1. Signed-off-by: Guenter Roeck --- drivers/hwmon/pmbus/pmbus.h | 2 +- drivers/hwmon/pmbus/pmbus_core.c | 142 ++++++++++++++++++++++++++++++- 2 files changed, 139 insertions(+), 5 deletions(-) diff --git a/drivers/hwmon/pmbus/pmbus.h b/drivers/hwmon/pmbus/pmbus.h index c031a9700ace..c708b60c1b48 100644 --- a/drivers/hwmon/pmbus/pmbus.h +++ b/drivers/hwmon/pmbus/pmbus.h @@ -406,7 +406,7 @@ enum pmbus_sensor_classes { #define PMBUS_PHASE_VIRTUAL BIT(30) /* Phases on this page are virtual */ #define PMBUS_PAGE_VIRTUAL BIT(31) /* Page is virtual */ -enum pmbus_data_format { linear = 0, direct, vid }; +enum pmbus_data_format { linear = 0, ieee754, direct, vid }; enum vrm_version { vr11 = 0, vr12, vr13, imvp9, amd625mv }; struct pmbus_driver_info { diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c index e670b868e74b..bb21f1e79289 100644 --- a/drivers/hwmon/pmbus/pmbus_core.c +++ b/drivers/hwmon/pmbus/pmbus_core.c @@ -611,6 +611,66 @@ static void pmbus_update_sensor_data(struct i2c_client *client, struct pmbus_sen sensor->phase, sensor->reg); } +/* + * Convert ieee754 sensor values to milli- or micro-units + * depending on sensor type. + * + * ieee754 data format: + * bit 15: sign + * bit 10..14: exponent + * bit 0..9: mantissa + * exponent=0: + * v=(−1)^signbit * 2^(−14) * 0.significantbits + * exponent=1..30: + * v=(−1)^signbit * 2^(exponent - 15) * 1.significantbits + * exponent=31: + * v=NaN + * + * Add the number mantissa bits into the calculations for simplicity. + * To do that, add '10' to the exponent. By doing that, we can just add + * 0x400 to normal values and get the expected result. + */ +static long pmbus_reg2data_ieee754(struct pmbus_data *data, + struct pmbus_sensor *sensor) +{ + int exponent; + bool sign; + long val; + + /* only support half precision for now */ + sign = sensor->data & 0x8000; + exponent = (sensor->data >> 10) & 0x1f; + val = sensor->data & 0x3ff; + + if (exponent == 0) { /* subnormal */ + exponent = -(14 + 10); + } else if (exponent == 0x1f) { /* NaN, convert to min/max */ + exponent = 0; + val = 65504; + } else { + exponent -= (15 + 10); /* normal */ + val |= 0x400; + } + + /* scale result to milli-units for all sensors except fans */ + if (sensor->class != PSC_FAN) + val = val * 1000L; + + /* scale result to micro-units for power sensors */ + if (sensor->class == PSC_POWER) + val = val * 1000L; + + if (exponent >= 0) + val <<= exponent; + else + val >>= -exponent; + + if (sign) + val = -val; + + return val; +} + /* * Convert linear sensor values to milli- or micro-units * depending on sensor type. @@ -741,6 +801,9 @@ static s64 pmbus_reg2data(struct pmbus_data *data, struct pmbus_sensor *sensor) case vid: val = pmbus_reg2data_vid(data, sensor); break; + case ieee754: + val = pmbus_reg2data_ieee754(data, sensor); + break; case linear: default: val = pmbus_reg2data_linear(data, sensor); @@ -749,8 +812,72 @@ static s64 pmbus_reg2data(struct pmbus_data *data, struct pmbus_sensor *sensor) return val; } -#define MAX_MANTISSA (1023 * 1000) -#define MIN_MANTISSA (511 * 1000) +#define MAX_IEEE_MANTISSA (0x7ff * 1000) +#define MIN_IEEE_MANTISSA (0x400 * 1000) + +static u16 pmbus_data2reg_ieee754(struct pmbus_data *data, + struct pmbus_sensor *sensor, long val) +{ + u16 exponent = (15 + 10); + long mantissa; + u16 sign = 0; + + /* simple case */ + if (val == 0) + return 0; + + if (val < 0) { + sign = 0x8000; + val = -val; + } + + /* Power is in uW. Convert to mW before converting. */ + if (sensor->class == PSC_POWER) + val = DIV_ROUND_CLOSEST(val, 1000L); + + /* + * For simplicity, convert fan data to milli-units + * before calculating the exponent. + */ + if (sensor->class == PSC_FAN) + val = val * 1000; + + /* Reduce large mantissa until it fits into 10 bit */ + while (val > MAX_IEEE_MANTISSA && exponent < 30) { + exponent++; + val >>= 1; + } + /* + * Increase small mantissa to generate valid 'normal' + * number + */ + while (val < MIN_IEEE_MANTISSA && exponent > 1) { + exponent--; + val <<= 1; + } + + /* Convert mantissa from milli-units to units */ + mantissa = DIV_ROUND_CLOSEST(val, 1000); + + /* + * Ensure that the resulting number is within range. + * Valid range is 0x400..0x7ff, where bit 10 reflects + * the implied high bit in normalized ieee754 numbers. + * Set the range to 0x400..0x7ff to reflect this. + * The upper bit is then removed by the mask against + * 0x3ff in the final assignment. + */ + if (mantissa > 0x7ff) + mantissa = 0x7ff; + else if (mantissa < 0x400) + mantissa = 0x400; + + /* Convert to sign, 5 bit exponent, 10 bit mantissa */ + return sign | (mantissa & 0x3ff) | ((exponent << 10) & 0x7c00); +} + +#define MAX_LIN_MANTISSA (1023 * 1000) +#define MIN_LIN_MANTISSA (511 * 1000) static u16 pmbus_data2reg_linear(struct pmbus_data *data, struct pmbus_sensor *sensor, s64 val) @@ -796,12 +923,12 @@ static u16 pmbus_data2reg_linear(struct pmbus_data *data, val = val * 1000LL; /* Reduce large mantissa until it fits into 10 bit */ - while (val >= MAX_MANTISSA && exponent < 15) { + while (val >= MAX_LIN_MANTISSA && exponent < 15) { exponent++; val >>= 1; } /* Increase small mantissa to improve precision */ - while (val < MIN_MANTISSA && exponent > -15) { + while (val < MIN_LIN_MANTISSA && exponent > -15) { exponent--; val <<= 1; } @@ -875,6 +1002,9 @@ static u16 pmbus_data2reg(struct pmbus_data *data, case vid: regval = pmbus_data2reg_vid(data, sensor, val); break; + case ieee754: + regval = pmbus_data2reg_ieee754(data, sensor, val); + break; case linear: default: regval = pmbus_data2reg_linear(data, sensor, val); @@ -2369,6 +2499,10 @@ static int pmbus_identify_common(struct i2c_client *client, if (data->info->format[PSC_VOLTAGE_OUT] != direct) return -ENODEV; break; + case 3: /* ieee 754 half precision */ + if (data->info->format[PSC_VOLTAGE_OUT] != ieee754) + return -ENODEV; + break; default: return -ENODEV; } From 5e9caf86b835a4d4fe6b9ddfe4b75a2d68a0be45 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Wed, 18 May 2022 11:44:01 -0700 Subject: [PATCH 43/85] dt-bindings: trivial-devices: Add lt7182s Add Analog Devices LT7182S Dual Channel Step-Down Switcher. Acked-by: Krzysztof Kozlowski Signed-off-by: Guenter Roeck --- Documentation/devicetree/bindings/trivial-devices.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/devicetree/bindings/trivial-devices.yaml b/Documentation/devicetree/bindings/trivial-devices.yaml index 6aafa71806a3..5d87b8426ff4 100644 --- a/Documentation/devicetree/bindings/trivial-devices.yaml +++ b/Documentation/devicetree/bindings/trivial-devices.yaml @@ -41,6 +41,8 @@ properties: - adi,adp5585-02 # Analog Devices ADP5589 Keypad Decoder and I/O Expansion - adi,adp5589 + # Analog Devices LT7182S Dual Channel 6A, 20V PolyPhase Step-Down Silent Switcher + - adi,lt7182s # AMS iAQ-Core VOC Sensor - ams,iaq-core # i2c serial eeprom (24cxx) From e10d9e4ca1d9d7b28907c3850828ae214c3adf67 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Wed, 18 May 2022 00:35:34 -0700 Subject: [PATCH 44/85] hwmon: (pmbus) Add support for Analog Devices LT7182S Add support for Analog Devices LT7182S Dual Channel 6A, 20V PolyPhase Step-Down Silent Switcher with Digital Power System Management. Signed-off-by: Guenter Roeck --- Documentation/hwmon/index.rst | 1 + Documentation/hwmon/lt7182s.rst | 92 +++++++++++++++ drivers/hwmon/pmbus/Kconfig | 9 ++ drivers/hwmon/pmbus/Makefile | 1 + drivers/hwmon/pmbus/lt7182s.c | 195 ++++++++++++++++++++++++++++++++ 5 files changed, 298 insertions(+) create mode 100644 Documentation/hwmon/lt7182s.rst create mode 100644 drivers/hwmon/pmbus/lt7182s.c diff --git a/Documentation/hwmon/index.rst b/Documentation/hwmon/index.rst index a72c16872ec2..f7113b0f8b2a 100644 --- a/Documentation/hwmon/index.rst +++ b/Documentation/hwmon/index.rst @@ -109,6 +109,7 @@ Hardware Monitoring Kernel Drivers lm95234 lm95245 lochnagar + lt7182s ltc2992 ltc2945 ltc2947 diff --git a/Documentation/hwmon/lt7182s.rst b/Documentation/hwmon/lt7182s.rst new file mode 100644 index 000000000000..f7268311b191 --- /dev/null +++ b/Documentation/hwmon/lt7182s.rst @@ -0,0 +1,92 @@ +.. SPDX-License-Identifier: GPL-2.0 + +Kernel driver lt7182s +===================== + +Supported chips: + + * ADI LT7182S + + Prefix: 'lt7182s' + + Addresses scanned: - + + Datasheet: https://www.analog.com/en/products/lt7182s.html + +Author: Guenter Roeck + + +Description +----------- + +LT7182S is a Dual Channel 6A, 20V PolyPhase Step-Down Silent Switcher with +Digital Power System Management support. + + +Usage Notes +----------- + +This driver does not probe for PMBus devices. You will have to instantiate +devices explicitly. + +Example: the following commands will load the driver for a LT7182S +at address 0x4f on I2C bus #4:: + + # modprobe lt7182s + # echo lt7182s 0x4f > /sys/bus/i2c/devices/i2c-4/new_device + +It can also be instantiated by declaring an entry in device tree. + + +Sysfs attributes +---------------- + +======================= ==================================== +curr[1-2]_label "iin[12]" +curr[1-2]_input Measured input current +curr[1-2]_max Maximum input current +curr[1-2]_max_alarm Current high alarm + +curr[3-4]_label "iout[1-2]" +curr[3-4]_input Measured output current +curr[3-4]_highest Highest measured output current +curr[3-4]_max Maximum output current +curr[3-4]_max_alarm Output current high alarm + +in[1-2]_label "vin[12]" +in[1-2]_input Measured input voltage +in[1-2]_highest Highest measured input voltage +in[1-2]_crit Critical maximum input voltage +in[1-2]_crit_alarm Input voltage critical high alarm +in[1-2]_min Minimum input voltage +in[1-2]_min_alarm Input voltage low alarm +in[1-2]_rated_min Rated minimum input voltage +in[1-2]_rated_max Rated maximum input voltage +in1_reset_history Write to reset history for all attributes + +in[3-5]_label "vmon[1-3]" +in[3-5]_input Measured voltage on ITH1/ITH2/EXTVCC pins + Only available if enabled with MFR_ADC_CONTROL_LT7182S + command. + +in[3-4|6-7]_label "vout[1-2]" +in[3-4|6-7]_input Measured output voltage +in[3-4|6-7]_highest Highest measured output voltage +in[3-4|6-7]_lcrit Critical minimum output voltage +in[3-4|6-7]_lcrit_alarm Output voltage critical low alarm +in[3-4|6-7]_min Minimum output voltage +in[3-4|6-7]_max_alarm Output voltage low alarm +in[3-4|6-7]_max Maximum output voltage +in[3-4|6-7]_max_alarm Output voltage high alarm +in[3-4|6-7]_crit Critical maximum output voltage +in[3-4|6-7]_crit_alarm Output voltage critical high alarm + +power[1-2]_label "pout[1-2]" +power[1-2]_input Measured output power + +temp1_input Measured temperature +temp1_crit Critical high temperature +temp1_crit_alarm Chip temperature critical high alarm +temp1_max Maximum temperature +temp1_max_alarm Chip temperature high alarm +======================= ==================================== diff --git a/drivers/hwmon/pmbus/Kconfig b/drivers/hwmon/pmbus/Kconfig index dfae76db65ae..951e4a9ff2d6 100644 --- a/drivers/hwmon/pmbus/Kconfig +++ b/drivers/hwmon/pmbus/Kconfig @@ -181,6 +181,15 @@ config SENSORS_LM25066_REGULATOR If you say yes here you get regulator support for National Semiconductor LM25066, LM5064, and LM5066. +config SENSORS_LT7182S + tristate "Analog Devices LT7182S" + help + If you say yes here you get hardware monitoring support for Analog + Devices LT7182S. + + This driver can also be built as a module. If so, the module will + be called lt7182s. + config SENSORS_LTC2978 tristate "Linear Technologies LTC2978 and compatibles" help diff --git a/drivers/hwmon/pmbus/Makefile b/drivers/hwmon/pmbus/Makefile index 4678fba5012c..e2fe86f98965 100644 --- a/drivers/hwmon/pmbus/Makefile +++ b/drivers/hwmon/pmbus/Makefile @@ -20,6 +20,7 @@ obj-$(CONFIG_SENSORS_IR38064) += ir38064.o obj-$(CONFIG_SENSORS_IRPS5401) += irps5401.o obj-$(CONFIG_SENSORS_ISL68137) += isl68137.o obj-$(CONFIG_SENSORS_LM25066) += lm25066.o +obj-$(CONFIG_SENSORS_LT7182S) += lt7182s.o obj-$(CONFIG_SENSORS_LTC2978) += ltc2978.o obj-$(CONFIG_SENSORS_LTC3815) += ltc3815.o obj-$(CONFIG_SENSORS_MAX15301) += max15301.o diff --git a/drivers/hwmon/pmbus/lt7182s.c b/drivers/hwmon/pmbus/lt7182s.c new file mode 100644 index 000000000000..4cfe476fc92d --- /dev/null +++ b/drivers/hwmon/pmbus/lt7182s.c @@ -0,0 +1,195 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Hardware monitoring driver for Analog Devices LT7182S + * + * Copyright (c) 2022 Guenter Roeck + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include "pmbus.h" + +#define LT7182S_NUM_PAGES 2 + +#define MFR_READ_EXTVCC 0xcd +#define MFR_READ_ITH 0xce +#define MFR_CONFIG_ALL_LT7182S 0xd1 +#define MFR_IOUT_PEAK 0xd7 +#define MFR_ADC_CONTROL_LT7182S 0xd8 + +#define MFR_DEBUG_TELEMETRY BIT(0) + +#define MFR_VOUT_PEAK 0xdd +#define MFR_VIN_PEAK 0xde +#define MFR_TEMPERATURE_1_PEAK 0xdf +#define MFR_CLEAR_PEAKS 0xe3 + +#define MFR_CONFIG_IEEE BIT(8) + +static int lt7182s_read_word_data(struct i2c_client *client, int page, int phase, int reg) +{ + int ret; + + switch (reg) { + case PMBUS_VIRT_READ_VMON: + if (page == 0 || page == 1) + ret = pmbus_read_word_data(client, page, phase, MFR_READ_ITH); + else + ret = pmbus_read_word_data(client, 0, phase, MFR_READ_EXTVCC); + break; + case PMBUS_VIRT_READ_IOUT_MAX: + ret = pmbus_read_word_data(client, page, phase, MFR_IOUT_PEAK); + break; + case PMBUS_VIRT_READ_VOUT_MAX: + ret = pmbus_read_word_data(client, page, phase, MFR_VOUT_PEAK); + break; + case PMBUS_VIRT_READ_VIN_MAX: + ret = pmbus_read_word_data(client, page, phase, MFR_VIN_PEAK); + break; + case PMBUS_VIRT_READ_TEMP_MAX: + ret = pmbus_read_word_data(client, page, phase, MFR_TEMPERATURE_1_PEAK); + break; + case PMBUS_VIRT_RESET_VIN_HISTORY: + ret = (page == 0) ? 0 : -ENODATA; + break; + default: + ret = -ENODATA; + break; + } + return ret; +} + +static int lt7182s_write_word_data(struct i2c_client *client, int page, int reg, u16 word) +{ + int ret; + + switch (reg) { + case PMBUS_VIRT_RESET_VIN_HISTORY: + ret = pmbus_write_byte(client, 0, MFR_CLEAR_PEAKS); + break; + default: + ret = -ENODATA; + break; + } + return ret; +} + +static struct pmbus_driver_info lt7182s_info = { + .pages = LT7182S_NUM_PAGES, + .format[PSC_VOLTAGE_IN] = linear, + .format[PSC_VOLTAGE_OUT] = linear, + .format[PSC_CURRENT_IN] = linear, + .format[PSC_CURRENT_OUT] = linear, + .format[PSC_TEMPERATURE] = linear, + .format[PSC_POWER] = linear, + .func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | + PMBUS_HAVE_IIN | PMBUS_HAVE_IOUT | PMBUS_HAVE_POUT | + PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_STATUS_IOUT | + PMBUS_HAVE_STATUS_INPUT | PMBUS_HAVE_STATUS_TEMP, + .func[1] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | + PMBUS_HAVE_IIN | PMBUS_HAVE_IOUT | PMBUS_HAVE_POUT | + PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_STATUS_IOUT | + PMBUS_HAVE_STATUS_INPUT, + .read_word_data = lt7182s_read_word_data, + .write_word_data = lt7182s_write_word_data, +}; + +static int lt7182s_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct pmbus_driver_info *info; + u8 buf[I2C_SMBUS_BLOCK_MAX + 1]; + int ret; + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_READ_BYTE_DATA | + I2C_FUNC_SMBUS_READ_WORD_DATA | + I2C_FUNC_SMBUS_READ_BLOCK_DATA)) + return -ENODEV; + + ret = i2c_smbus_read_block_data(client, PMBUS_MFR_ID, buf); + if (ret < 0) { + dev_err(dev, "Failed to read PMBUS_MFR_ID\n"); + return ret; + } + if (ret != 3 || strncmp(buf, "ADI", 3)) { + buf[ret] = '\0'; + dev_err(dev, "Manufacturer '%s' not supported\n", buf); + return -ENODEV; + } + + ret = i2c_smbus_read_block_data(client, PMBUS_MFR_MODEL, buf); + if (ret < 0) { + dev_err(dev, "Failed to read PMBUS_MFR_MODEL\n"); + return ret; + } + if (ret != 7 || strncmp(buf, "LT7182S", 7)) { + buf[ret] = '\0'; + dev_err(dev, "Model '%s' not supported\n", buf); + return -ENODEV; + } + + info = devm_kmemdup(dev, <7182s_info, + sizeof(struct pmbus_driver_info), GFP_KERNEL); + if (!info) + return -ENOMEM; + + /* Set data format to IEEE754 if configured */ + ret = i2c_smbus_read_word_data(client, MFR_CONFIG_ALL_LT7182S); + if (ret < 0) + return ret; + if (ret & MFR_CONFIG_IEEE) { + info->format[PSC_VOLTAGE_IN] = ieee754; + info->format[PSC_VOLTAGE_OUT] = ieee754; + info->format[PSC_CURRENT_IN] = ieee754; + info->format[PSC_CURRENT_OUT] = ieee754; + info->format[PSC_TEMPERATURE] = ieee754; + info->format[PSC_POWER] = ieee754; + } + + /* Enable VMON output if configured */ + ret = i2c_smbus_read_byte_data(client, MFR_ADC_CONTROL_LT7182S); + if (ret < 0) + return ret; + if (ret & MFR_DEBUG_TELEMETRY) { + info->pages = 3; + info->func[0] |= PMBUS_HAVE_VMON; + info->func[1] |= PMBUS_HAVE_VMON; + info->func[2] = PMBUS_HAVE_VMON; + } + + return pmbus_do_probe(client, info); +} + +static const struct i2c_device_id lt7182s_id[] = { + { "lt7182s", 0 }, + {} +}; +MODULE_DEVICE_TABLE(i2c, lt7182s_id); + +static const struct of_device_id __maybe_unused lt7182s_of_match[] = { + { .compatible = "adi,lt7182s" }, + {} +}; + +static struct i2c_driver lt7182s_driver = { + .driver = { + .name = "lt7182s", + .of_match_table = of_match_ptr(lt7182s_of_match), + }, + .probe_new = lt7182s_probe, + .id_table = lt7182s_id, +}; + +module_i2c_driver(lt7182s_driver); + +MODULE_AUTHOR("Guenter Roeck "); +MODULE_DESCRIPTION("PMBus driver for Analog Devices LT7182S"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(PMBUS); From 739743ec78c9d70954845609093dc0ffb22f3ab8 Mon Sep 17 00:00:00 2001 From: Aleksander Mazur Date: Sun, 5 Jun 2022 01:21:14 +0200 Subject: [PATCH 45/85] hwmon: (f71882fg) Add support for F71858AD (0x0903) Treat F71858AD like F71858FG. Tested on Igel D220. Signed-off-by: Aleksander Mazur Link: https://lore.kernel.org/r/20220605012114.3d85a75a@mocarz Signed-off-by: Guenter Roeck --- drivers/hwmon/f71882fg.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/hwmon/f71882fg.c b/drivers/hwmon/f71882fg.c index 6830e029995d..19b6c643059a 100644 --- a/drivers/hwmon/f71882fg.c +++ b/drivers/hwmon/f71882fg.c @@ -49,6 +49,7 @@ #define SIO_F81768D_ID 0x1210 /* Chipset ID */ #define SIO_F81865_ID 0x0704 /* Chipset ID */ #define SIO_F81866_ID 0x1010 /* Chipset ID */ +#define SIO_F71858AD_ID 0x0903 /* Chipset ID */ #define SIO_F81966_ID 0x1502 /* Chipset ID */ #define REGION_LENGTH 8 @@ -2638,6 +2639,7 @@ static int __init f71882fg_find(int sioaddr, struct f71882fg_sio_data *sio_data) sio_data->type = f71808a; break; case SIO_F71858_ID: + case SIO_F71858AD_ID: sio_data->type = f71858fg; break; case SIO_F71862_ID: From 39397ba8a7ec46b2b2553886056acabae1ef7a40 Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Fri, 3 Jun 2022 22:51:46 +0200 Subject: [PATCH 46/85] hwmon: (lm75) Replace kernel.h with the necessary inclusions When kernel.h is used in the headers it adds a lot into dependency hell, especially when there are circular dependencies are involved. Replace kernel.h inclusion with the list of what is really being used. Signed-off-by: Christophe JAILLET Link: https://lore.kernel.org/r/4e07ed43274ad912d4efcfc04f673f25e8f89fdc.1654289489.git.christophe.jaillet@wanadoo.fr Signed-off-by: Guenter Roeck --- drivers/hwmon/lm75.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/hwmon/lm75.h b/drivers/hwmon/lm75.h index a398171162a8..b803ada5e3c9 100644 --- a/drivers/hwmon/lm75.h +++ b/drivers/hwmon/lm75.h @@ -11,7 +11,8 @@ * which contains this code, we don't worry about the wasted space. */ -#include +#include +#include /* straight from the datasheet */ #define LM75_TEMP_MIN (-55000) From fd2d53c367ae9983c2100ac733a834e0c79d7537 Mon Sep 17 00:00:00 2001 From: Armin Wolf Date: Sun, 5 Jun 2022 00:02:00 +0200 Subject: [PATCH 47/85] hwmon: (sch56xx-common) Add DMI override table Some devices like the Fujitsu Celsius W380 do contain a working sch56xx hardware monitoring device, but do not contain the necessary DMI onboard device. Do not check for the presence of an suitable onboard device on these machines. The list of affected machines was created using data collected by the Linux Hardware Project. Tested on a Fujitsu Esprimo P720, but sadly not on a affected machine. Fixes: 393935baa45e (hwmon: (sch56xx-common) Add automatic module loading on supported devices) Signed-off-by: Armin Wolf Reviewed-by: Hans de Goede Link: https://lore.kernel.org/r/20220604220200.2567-1-W_Armin@gmx.de Signed-off-by: Guenter Roeck --- drivers/hwmon/sch56xx-common.c | 44 ++++++++++++++++++++++++++-------- 1 file changed, 34 insertions(+), 10 deletions(-) diff --git a/drivers/hwmon/sch56xx-common.c b/drivers/hwmon/sch56xx-common.c index 3ece53adabd6..de3a0886c2f7 100644 --- a/drivers/hwmon/sch56xx-common.c +++ b/drivers/hwmon/sch56xx-common.c @@ -523,6 +523,28 @@ static int __init sch56xx_device_add(int address, const char *name) return PTR_ERR_OR_ZERO(sch56xx_pdev); } +static const struct dmi_system_id sch56xx_dmi_override_table[] __initconst = { + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), + DMI_MATCH(DMI_PRODUCT_NAME, "CELSIUS W380"), + }, + }, + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), + DMI_MATCH(DMI_PRODUCT_NAME, "ESPRIMO P710"), + }, + }, + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), + DMI_MATCH(DMI_PRODUCT_NAME, "ESPRIMO E9900"), + }, + }, + { } +}; + /* For autoloading only */ static const struct dmi_system_id sch56xx_dmi_table[] __initconst = { { @@ -543,16 +565,18 @@ static int __init sch56xx_init(void) if (!dmi_check_system(sch56xx_dmi_table)) return -ENODEV; - /* - * Some machines like the Esprimo P720 and Esprimo C700 have - * onboard devices named " Antiope"/" Theseus" instead of - * "Antiope"/"Theseus", so we need to check for both. - */ - if (!dmi_find_device(DMI_DEV_TYPE_OTHER, "Antiope", NULL) && - !dmi_find_device(DMI_DEV_TYPE_OTHER, " Antiope", NULL) && - !dmi_find_device(DMI_DEV_TYPE_OTHER, "Theseus", NULL) && - !dmi_find_device(DMI_DEV_TYPE_OTHER, " Theseus", NULL)) - return -ENODEV; + if (!dmi_check_system(sch56xx_dmi_override_table)) { + /* + * Some machines like the Esprimo P720 and Esprimo C700 have + * onboard devices named " Antiope"/" Theseus" instead of + * "Antiope"/"Theseus", so we need to check for both. + */ + if (!dmi_find_device(DMI_DEV_TYPE_OTHER, "Antiope", NULL) && + !dmi_find_device(DMI_DEV_TYPE_OTHER, " Antiope", NULL) && + !dmi_find_device(DMI_DEV_TYPE_OTHER, "Theseus", NULL) && + !dmi_find_device(DMI_DEV_TYPE_OTHER, " Theseus", NULL)) + return -ENODEV; + } } /* From bae26b801f98bc902ab4a43c96947f3a0ce4f3a0 Mon Sep 17 00:00:00 2001 From: Shady Nawara Date: Fri, 3 Jun 2022 14:27:58 +0200 Subject: [PATCH 48/85] hwmon: (asus-ec-sensors) add support for Strix Z690-a D4 adds T_Sensor and VRM Temp sensors for the Asus Strix z690-a D4 motherboard Signed-off-by: Shady Nawara Signed-off-by: Eugene Shalygin Link: https://lore.kernel.org/r/20220603122758.1561064-1-eugene.shalygin@gmail.com Signed-off-by: Guenter Roeck --- Documentation/hwmon/asus_ec_sensors.rst | 1 + drivers/hwmon/asus-ec-sensors.c | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/Documentation/hwmon/asus_ec_sensors.rst b/Documentation/hwmon/asus_ec_sensors.rst index 78ca69eda877..00d8c46ef9e0 100644 --- a/Documentation/hwmon/asus_ec_sensors.rst +++ b/Documentation/hwmon/asus_ec_sensors.rst @@ -19,6 +19,7 @@ Supported boards: * ROG STRIX X570-E GAMING WIFI II * ROG STRIX X570-F GAMING * ROG STRIX X570-I GAMING + * ROG STRIX Z690-A GAMING WIFI D4 Authors: - Eugene Shalygin diff --git a/drivers/hwmon/asus-ec-sensors.c b/drivers/hwmon/asus-ec-sensors.c index 3633ab691662..19d3ca71b360 100644 --- a/drivers/hwmon/asus-ec-sensors.c +++ b/drivers/hwmon/asus-ec-sensors.c @@ -54,6 +54,8 @@ static char *mutex_path_override; /* ACPI mutex for locking access to the EC for the firmware */ #define ASUS_HW_ACCESS_MUTEX_ASMX "\\AMW0.ASMX" +#define ASUS_HW_ACCESS_MUTEX_RMTW_ASMX "\\RMTW.ASMX" + #define MAX_IDENTICAL_BOARD_VARIATIONS 3 /* Moniker for the ACPI global lock (':' is not allowed in ASL identifiers) */ @@ -139,6 +141,7 @@ enum board_family { family_unknown, family_amd_400_series, family_amd_500_series, + family_intel_600_series }; /* All the known sensors for ASUS EC controllers */ @@ -197,6 +200,12 @@ static const struct ec_sensor_info sensors_family_amd_500[] = { EC_SENSOR("Water_Out", hwmon_temp, 1, 0x01, 0x01), }; +static const struct ec_sensor_info sensors_family_intel_600[] = { + [ec_sensor_temp_t_sensor] = + EC_SENSOR("T_Sensor", hwmon_temp, 1, 0x00, 0x3d), + [ec_sensor_temp_vrm] = EC_SENSOR("VRM", hwmon_temp, 1, 0x00, 0x3e), +}; + /* Shortcuts for common combinations */ #define SENSOR_SET_TEMP_CHIPSET_CPU_MB \ (SENSOR_TEMP_CHIPSET | SENSOR_TEMP_CPU | SENSOR_TEMP_MB) @@ -330,6 +339,12 @@ static const struct ec_board_info board_info[] = { .mutex_path = ASUS_HW_ACCESS_MUTEX_ASMX, .family = family_amd_500_series, }, + { + .board_names = {"ROG STRIX Z690-A GAMING WIFI D4"}, + .sensors = SENSOR_TEMP_T_SENSOR | SENSOR_TEMP_VRM, + .mutex_path = ASUS_HW_ACCESS_MUTEX_RMTW_ASMX, + .family = family_intel_600_series, + }, {} }; @@ -799,6 +814,9 @@ static int __init asus_ec_probe(struct platform_device *pdev) case family_amd_500_series: ec_data->sensors_info = sensors_family_amd_500; break; + case family_intel_600_series: + ec_data->sensors_info = sensors_family_intel_600; + break; default: dev_err(dev, "Unknown board family: %d", ec_data->board_info->family); From 8a85007c8386ed8378ed3ee9f53e995e3c45fa1c Mon Sep 17 00:00:00 2001 From: Adam Wujek Date: Wed, 1 Jun 2022 01:33:03 +0000 Subject: [PATCH 49/85] hwmon: (pmbus) add a function to check the presence of a block register Other functions (like pmbus_check_byte_register) cannot be used to check the presence of a block register, because it will generate error when PEC is used. Signed-off-by: Adam Wujek Link: https://lore.kernel.org/r/20220601013232.801133-1-dev_public@wujek.eu Signed-off-by: Guenter Roeck --- drivers/hwmon/pmbus/pmbus_core.c | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c index bb21f1e79289..156d22ad2ad0 100644 --- a/drivers/hwmon/pmbus/pmbus_core.c +++ b/drivers/hwmon/pmbus/pmbus_core.c @@ -441,6 +441,18 @@ int pmbus_update_byte_data(struct i2c_client *client, int page, u8 reg, } EXPORT_SYMBOL_NS_GPL(pmbus_update_byte_data, PMBUS); +static int pmbus_read_block_data(struct i2c_client *client, int page, u8 reg, + char *data_buf) +{ + int rv; + + rv = pmbus_set_page(client, page, 0xff); + if (rv < 0) + return rv; + + return i2c_smbus_read_block_data(client, reg, data_buf); +} + static struct pmbus_sensor *pmbus_find_sensor(struct pmbus_data *data, int page, int reg) { @@ -578,6 +590,22 @@ bool pmbus_check_word_register(struct i2c_client *client, int page, int reg) } EXPORT_SYMBOL_NS_GPL(pmbus_check_word_register, PMBUS); +static bool pmbus_check_block_register(struct i2c_client *client, int page, + int reg) +{ + int rv; + struct pmbus_data *data = i2c_get_clientdata(client); + char data_buf[I2C_SMBUS_BLOCK_MAX + 2]; + + rv = pmbus_read_block_data(client, page, reg, data_buf); + if (rv >= 0 && !(data->flags & PMBUS_SKIP_STATUS_CHECK)) + rv = pmbus_check_status_cml(client); + if (rv < 0 && (data->flags & PMBUS_READ_STATUS_AFTER_FAILED_CHECK)) + data->read_status(client, -1); + pmbus_clear_fault_page(client, -1); + return rv >= 0; +} + const struct pmbus_driver_info *pmbus_get_driver_info(struct i2c_client *client) { struct pmbus_data *data = i2c_get_clientdata(client); From 6fd584230a85aefb22f291884af790664c2eddae Mon Sep 17 00:00:00 2001 From: Adam Wujek Date: Wed, 1 Jun 2022 01:33:17 +0000 Subject: [PATCH 50/85] hwmon: (pmbus) add MFR_* registers to debugfs Add registers to debugfs: PMBUS_MFR_ID PMBUS_MFR_MODEL PMBUS_MFR_REVISION PMBUS_MFR_LOCATION PMBUS_MFR_DATE PMBUS_MFR_SERIAL To reduce the number of debugfs entries, only values from page 0 are reported. It is assumed that values of these registers are the same for all pages. Please note that the PMBUS standard allows added registers to be page-specific. Signed-off-by: Adam Wujek Link: https://lore.kernel.org/r/20220601013232.801133-2-dev_public@wujek.eu Signed-off-by: Guenter Roeck --- drivers/hwmon/pmbus/pmbus_core.c | 99 +++++++++++++++++++++++++++++++- 1 file changed, 97 insertions(+), 2 deletions(-) diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c index 156d22ad2ad0..809e666cb52b 100644 --- a/drivers/hwmon/pmbus/pmbus_core.c +++ b/drivers/hwmon/pmbus/pmbus_core.c @@ -2994,6 +2994,34 @@ static int pmbus_debugfs_get_status(void *data, u64 *val) DEFINE_DEBUGFS_ATTRIBUTE(pmbus_debugfs_ops_status, pmbus_debugfs_get_status, NULL, "0x%04llx\n"); +static ssize_t pmbus_debugfs_mfr_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + int rc; + struct pmbus_debugfs_entry *entry = file->private_data; + char data[I2C_SMBUS_BLOCK_MAX + 2] = { 0 }; + + rc = pmbus_read_block_data(entry->client, entry->page, entry->reg, + data); + if (rc < 0) + return rc; + + /* Add newline at the end of a read data */ + data[rc] = '\n'; + + /* Include newline into the length */ + rc += 1; + + return simple_read_from_buffer(buf, count, ppos, data, rc); +} + +static const struct file_operations pmbus_debugfs_ops_mfr = { + .llseek = noop_llseek, + .read = pmbus_debugfs_mfr_read, + .write = NULL, + .open = simple_open, +}; + static void pmbus_remove_debugfs(void *data) { struct dentry *entry = data; @@ -3022,13 +3050,80 @@ static int pmbus_init_debugfs(struct i2c_client *client, return -ENODEV; } - /* Allocate the max possible entries we need. */ + /* + * Allocate the max possible entries we need. + * 6 entries device-specific + * 10 entries page-specific + */ entries = devm_kcalloc(data->dev, - data->info->pages * 10, sizeof(*entries), + 6 + data->info->pages * 10, sizeof(*entries), GFP_KERNEL); if (!entries) return -ENOMEM; + /* + * Add device-specific entries. + * Please note that the PMBUS standard allows all registers to be + * page-specific. + * To reduce the number of debugfs entries for devices with many pages + * assume that values of the following registers are the same for all + * pages and report values only for page 0. + */ + if (pmbus_check_block_register(client, 0, PMBUS_MFR_ID)) { + entries[idx].client = client; + entries[idx].page = 0; + entries[idx].reg = PMBUS_MFR_ID; + debugfs_create_file("mfr_id", 0444, data->debugfs, + &entries[idx++], + &pmbus_debugfs_ops_mfr); + } + + if (pmbus_check_block_register(client, 0, PMBUS_MFR_MODEL)) { + entries[idx].client = client; + entries[idx].page = 0; + entries[idx].reg = PMBUS_MFR_MODEL; + debugfs_create_file("mfr_model", 0444, data->debugfs, + &entries[idx++], + &pmbus_debugfs_ops_mfr); + } + + if (pmbus_check_block_register(client, 0, PMBUS_MFR_REVISION)) { + entries[idx].client = client; + entries[idx].page = 0; + entries[idx].reg = PMBUS_MFR_REVISION; + debugfs_create_file("mfr_revision", 0444, data->debugfs, + &entries[idx++], + &pmbus_debugfs_ops_mfr); + } + + if (pmbus_check_block_register(client, 0, PMBUS_MFR_LOCATION)) { + entries[idx].client = client; + entries[idx].page = 0; + entries[idx].reg = PMBUS_MFR_LOCATION; + debugfs_create_file("mfr_location", 0444, data->debugfs, + &entries[idx++], + &pmbus_debugfs_ops_mfr); + } + + if (pmbus_check_block_register(client, 0, PMBUS_MFR_DATE)) { + entries[idx].client = client; + entries[idx].page = 0; + entries[idx].reg = PMBUS_MFR_DATE; + debugfs_create_file("mfr_date", 0444, data->debugfs, + &entries[idx++], + &pmbus_debugfs_ops_mfr); + } + + if (pmbus_check_block_register(client, 0, PMBUS_MFR_SERIAL)) { + entries[idx].client = client; + entries[idx].page = 0; + entries[idx].reg = PMBUS_MFR_SERIAL; + debugfs_create_file("mfr_serial", 0444, data->debugfs, + &entries[idx++], + &pmbus_debugfs_ops_mfr); + } + + /* Add page specific entries */ for (i = 0; i < data->info->pages; ++i) { /* Check accessibility of status register if it's not page 0 */ if (!i || pmbus_check_status_register(client, i)) { From b97adb596399581df42a5f94e14235359f150373 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Fri, 27 May 2022 17:34:45 +0200 Subject: [PATCH 51/85] hwmon: Allow to compile ASB100 and FSCHMD on !X86 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The two drivers compile just fine on ARCH=arm. Allow to select these drivers if COMPILE_TEST is enabled. Signed-off-by: Uwe Kleine-König Reviewed-by: Jean Delvare Link: https://lore.kernel.org/r/20220527153445.1871086-1-u.kleine-koenig@pengutronix.de Signed-off-by: Guenter Roeck --- drivers/hwmon/Kconfig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 981df62bd6ab..fd2446cf343b 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -382,7 +382,7 @@ config SENSORS_ARM_SCPI config SENSORS_ASB100 tristate "Asus ASB100 Bach" - depends on X86 && I2C + depends on (X86 || COMPILE_TEST) && I2C select HWMON_VID help If you say yes here you get support for the ASB100 Bach sensor @@ -627,7 +627,7 @@ config SENSORS_MC13783_ADC config SENSORS_FSCHMD tristate "Fujitsu Siemens Computers sensor chips" - depends on X86 && I2C + depends on (X86 || COMPILE_TEST) && I2C help If you say yes here you get support for the following Fujitsu Siemens Computers (FSC) sensor chips: Poseidon, Scylla, Hermes, From d8d5879a444297f819c70fbffd7b6a2790eea571 Mon Sep 17 00:00:00 2001 From: Slawomir Stepien Date: Wed, 25 May 2022 09:36:51 +0200 Subject: [PATCH 52/85] dt-bindings: hwmon: Add compatible string for ADT7481 in lm90 This will allow binding the driver with the device from the device tree. This device can work in extended temperature measurement mode, so add it also to the list of devices that support 'ti,extended-range-enable'. Signed-off-by: Slawomir Stepien Acked-by: Rob Herring Link: https://lore.kernel.org/r/20220525073657.573327-2-sst@poczta.fm Signed-off-by: Guenter Roeck --- Documentation/devicetree/bindings/hwmon/national,lm90.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/devicetree/bindings/hwmon/national,lm90.yaml b/Documentation/devicetree/bindings/hwmon/national,lm90.yaml index b04657849852..82fce96498c7 100644 --- a/Documentation/devicetree/bindings/hwmon/national,lm90.yaml +++ b/Documentation/devicetree/bindings/hwmon/national,lm90.yaml @@ -16,6 +16,7 @@ properties: - adi,adm1032 - adi,adt7461 - adi,adt7461a + - adi,adt7481 - dallas,max6646 - dallas,max6647 - dallas,max6649 @@ -70,6 +71,7 @@ allOf: enum: - adi,adt7461 - adi,adt7461a + - adi,adt7481 - ti,tmp451 - ti,tmp461 then: From f07691219cff2521e6eace2cb6d3e3d3c589b376 Mon Sep 17 00:00:00 2001 From: Slawomir Stepien Date: Wed, 25 May 2022 09:36:52 +0200 Subject: [PATCH 53/85] dt-bindings: hwmon: Allow specifying channels for lm90 Add binding description for temperature channels. Currently, support for label and temperature-offset-millicelsius is implemented. Signed-off-by: Slawomir Stepien Reviewed-by: Rob Herring Link: https://lore.kernel.org/r/20220525073657.573327-3-sst@poczta.fm Signed-off-by: Guenter Roeck --- .../bindings/hwmon/national,lm90.yaml | 129 ++++++++++++++++++ 1 file changed, 129 insertions(+) diff --git a/Documentation/devicetree/bindings/hwmon/national,lm90.yaml b/Documentation/devicetree/bindings/hwmon/national,lm90.yaml index 82fce96498c7..e1719839faf0 100644 --- a/Documentation/devicetree/bindings/hwmon/national,lm90.yaml +++ b/Documentation/devicetree/bindings/hwmon/national,lm90.yaml @@ -51,6 +51,12 @@ properties: "#thermal-sensor-cells": const: 1 + '#address-cells': + const: 1 + + '#size-cells': + const: 0 + vcc-supply: description: phandle to the regulator that provides the +VCC supply @@ -62,6 +68,29 @@ required: - compatible - reg +patternProperties: + "^channel@([0-2])$": + type: object + description: Represents channels of the device and their specific configuration. + + properties: + reg: + description: The channel number. 0 is local channel, 1-2 are remote channels. + items: + minimum: 0 + maximum: 2 + + label: + description: A descriptive name for this channel, like "ambient" or "psu". + + temperature-offset-millicelsius: + description: Temperature offset to be added to or subtracted from remote temperature measurements. + + required: + - reg + + additionalProperties: false + allOf: - if: not: @@ -78,6 +107,77 @@ allOf: properties: ti,extended-range-enable: false + - if: + properties: + compatible: + contains: + enum: + - dallas,max6646 + - dallas,max6647 + - dallas,max6649 + - dallas,max6657 + - dallas,max6658 + - dallas,max6659 + - dallas,max6695 + - dallas,max6696 + then: + patternProperties: + "^channel@([0-2])$": + properties: + temperature-offset-millicelsius: false + + - if: + properties: + compatible: + contains: + enum: + - adi,adt7461 + - adi,adt7461a + - adi,adt7481 + - onnn,nct1008 + then: + patternProperties: + "^channel@([0-2])$": + properties: + temperature-offset-millicelsius: + maximum: 127750 + + - if: + properties: + compatible: + contains: + enum: + - adi,adm1032 + - dallas,max6680 + - dallas,max6681 + - gmt,g781 + - national,lm86 + - national,lm89 + - national,lm90 + - national,lm99 + - nxp,sa56004 + - winbond,w83l771 + then: + patternProperties: + "^channel@([0-2])$": + properties: + temperature-offset-millicelsius: + maximum: 127875 + + - if: + properties: + compatible: + contains: + enum: + - ti,tmp451 + - ti,tmp461 + then: + patternProperties: + "^channel@([0-2])$": + properties: + temperature-offset-millicelsius: + maximum: 127937 + additionalProperties: false examples: @@ -96,3 +196,32 @@ examples: #thermal-sensor-cells = <1>; }; }; + - | + i2c { + #address-cells = <1>; + #size-cells = <0>; + + sensor@4c { + compatible = "adi,adt7481"; + reg = <0x4c>; + #address-cells = <1>; + #size-cells = <0>; + + channel@0 { + reg = <0x0>; + label = "local"; + }; + + channel@1 { + reg = <0x1>; + label = "front"; + temperature-offset-millicelsius = <4000>; + }; + + channel@2 { + reg = <0x2>; + label = "back"; + temperature-offset-millicelsius = <750>; + }; + }; + }; From b9e7dfdedbc5309eb516fb11cef4a67543f20fc2 Mon Sep 17 00:00:00 2001 From: Slawomir Stepien Date: Wed, 25 May 2022 09:36:53 +0200 Subject: [PATCH 54/85] hwmon: (lm90) Add compatible entry for adt7481 This will allow binding the driver with the device from the device tree. Signed-off-by: Slawomir Stepien Link: https://lore.kernel.org/r/20220525073657.573327-4-sst@poczta.fm Signed-off-by: Guenter Roeck --- drivers/hwmon/lm90.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c index f54227affe09..85d35ccb7c44 100644 --- a/drivers/hwmon/lm90.c +++ b/drivers/hwmon/lm90.c @@ -294,6 +294,10 @@ static const struct of_device_id __maybe_unused lm90_of_match[] = { .compatible = "adi,adt7461a", .data = (void *)adt7461a }, + { + .compatible = "adi,adt7481", + .data = (void *)adt7481 + }, { .compatible = "gmt,g781", .data = (void *)g781 From 27f0438972fc045a575d4f95458415e62c1d24e5 Mon Sep 17 00:00:00 2001 From: Slawomir Stepien Date: Wed, 25 May 2022 09:36:55 +0200 Subject: [PATCH 55/85] hwmon: (lm90) Define maximum number of channels that are supported Use this define in all the places where literal '3' was used in this context. Signed-off-by: Slawomir Stepien Link: https://lore.kernel.org/r/20220525073657.573327-6-sst@poczta.fm Signed-off-by: Guenter Roeck --- drivers/hwmon/lm90.c | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c index 85d35ccb7c44..fd887a748fd4 100644 --- a/drivers/hwmon/lm90.c +++ b/drivers/hwmon/lm90.c @@ -110,6 +110,9 @@ #include #include +/* The maximum number of channels currently supported */ +#define MAX_CHANNELS 3 + /* * Addresses to scan * Address is fully defined internally and cannot be changed except for @@ -681,7 +684,7 @@ struct lm90_data { struct i2c_client *client; struct device *hwmon_dev; u32 chip_config[2]; - u32 channel_config[4]; + u32 channel_config[MAX_CHANNELS + 1]; struct hwmon_channel_info chip_info; struct hwmon_channel_info temp_info; const struct hwmon_channel_info *info[3]; @@ -1424,32 +1427,32 @@ static int lm90_set_temphyst(struct lm90_data *data, long val) return lm90_write_reg(data->client, LM90_REG_TCRIT_HYST, data->temp_hyst); } -static const u8 lm90_temp_index[3] = { +static const u8 lm90_temp_index[MAX_CHANNELS] = { LOCAL_TEMP, REMOTE_TEMP, REMOTE2_TEMP }; -static const u8 lm90_temp_min_index[3] = { +static const u8 lm90_temp_min_index[MAX_CHANNELS] = { LOCAL_LOW, REMOTE_LOW, REMOTE2_LOW }; -static const u8 lm90_temp_max_index[3] = { +static const u8 lm90_temp_max_index[MAX_CHANNELS] = { LOCAL_HIGH, REMOTE_HIGH, REMOTE2_HIGH }; -static const u8 lm90_temp_crit_index[3] = { +static const u8 lm90_temp_crit_index[MAX_CHANNELS] = { LOCAL_CRIT, REMOTE_CRIT, REMOTE2_CRIT }; -static const u8 lm90_temp_emerg_index[3] = { +static const u8 lm90_temp_emerg_index[MAX_CHANNELS] = { LOCAL_EMERG, REMOTE_EMERG, REMOTE2_EMERG }; -static const u16 lm90_min_alarm_bits[3] = { BIT(5), BIT(3), BIT(11) }; -static const u16 lm90_max_alarm_bits[3] = { BIT(6), BIT(4), BIT(12) }; -static const u16 lm90_crit_alarm_bits[3] = { BIT(0), BIT(1), BIT(9) }; -static const u16 lm90_crit_alarm_bits_swapped[3] = { BIT(1), BIT(0), BIT(9) }; -static const u16 lm90_emergency_alarm_bits[3] = { BIT(15), BIT(13), BIT(14) }; -static const u16 lm90_fault_bits[3] = { BIT(0), BIT(2), BIT(10) }; +static const u16 lm90_min_alarm_bits[MAX_CHANNELS] = { BIT(5), BIT(3), BIT(11) }; +static const u16 lm90_max_alarm_bits[MAX_CHANNELS] = { BIT(6), BIT(4), BIT(12) }; +static const u16 lm90_crit_alarm_bits[MAX_CHANNELS] = { BIT(0), BIT(1), BIT(9) }; +static const u16 lm90_crit_alarm_bits_swapped[MAX_CHANNELS] = { BIT(1), BIT(0), BIT(9) }; +static const u16 lm90_emergency_alarm_bits[MAX_CHANNELS] = { BIT(15), BIT(13), BIT(14) }; +static const u16 lm90_fault_bits[MAX_CHANNELS] = { BIT(0), BIT(2), BIT(10) }; static int lm90_temp_read(struct device *dev, u32 attr, int channel, long *val) { From f9938eeb97fc786148809be6ca752c0c6baebf49 Mon Sep 17 00:00:00 2001 From: Slawomir Stepien Date: Wed, 25 May 2022 09:36:56 +0200 Subject: [PATCH 56/85] hwmon: (lm90) Read the channel's label from device-tree Try to read the channel's label from device-tree. Having label in device-tree node is not mandatory. Signed-off-by: Slawomir Stepien Link: https://lore.kernel.org/r/20220525073657.573327-7-sst@poczta.fm Signed-off-by: Guenter Roeck --- drivers/hwmon/lm90.c | 72 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c index fd887a748fd4..a18aed5f96d1 100644 --- a/drivers/hwmon/lm90.c +++ b/drivers/hwmon/lm90.c @@ -685,6 +685,7 @@ struct lm90_data { struct device *hwmon_dev; u32 chip_config[2]; u32 channel_config[MAX_CHANNELS + 1]; + const char *channel_label[MAX_CHANNELS]; struct hwmon_channel_info chip_info; struct hwmon_channel_info temp_info; const struct hwmon_channel_info *info[3]; @@ -1587,6 +1588,7 @@ static umode_t lm90_temp_is_visible(const void *data, u32 attr, int channel) case hwmon_temp_emergency_alarm: case hwmon_temp_emergency_hyst: case hwmon_temp_fault: + case hwmon_temp_label: return 0444; case hwmon_temp_min: case hwmon_temp_max: @@ -1706,6 +1708,16 @@ static int lm90_read(struct device *dev, enum hwmon_sensor_types type, } } +static int lm90_read_string(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, const char **str) +{ + struct lm90_data *data = dev_get_drvdata(dev); + + *str = data->channel_label[channel]; + + return 0; +} + static int lm90_write(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel, long val) { @@ -2611,10 +2623,63 @@ static void lm90_regulator_disable(void *regulator) regulator_disable(regulator); } +static int lm90_probe_channel_from_dt(struct i2c_client *client, + struct device_node *child, + struct lm90_data *data) +{ + u32 id; + int err; + struct device *dev = &client->dev; + + err = of_property_read_u32(child, "reg", &id); + if (err) { + dev_err(dev, "missing reg property of %pOFn\n", child); + return err; + } + + if (id >= MAX_CHANNELS) { + dev_err(dev, "invalid reg property value %d in %pOFn\n", id, child); + return -EINVAL; + } + + err = of_property_read_string(child, "label", &data->channel_label[id]); + if (err == -ENODATA || err == -EILSEQ) { + dev_err(dev, "invalid label property in %pOFn\n", child); + return err; + } + + if (data->channel_label[id]) + data->channel_config[id] |= HWMON_T_LABEL; + + return 0; +} + +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) { + if (strcmp(child->name, "channel")) + continue; + + err = lm90_probe_channel_from_dt(client, child, data); + if (err) { + of_node_put(child); + return err; + } + } + + return 0; +} static const struct hwmon_ops lm90_ops = { .is_visible = lm90_is_visible, .read = lm90_read, + .read_string = lm90_read_string, .write = lm90_write, }; @@ -2750,6 +2815,13 @@ static int lm90_probe(struct i2c_client *client) /* Set maximum conversion rate */ data->max_convrate = lm90_params[data->kind].max_convrate; + /* Parse device-tree channel information */ + if (client->dev.of_node) { + err = lm90_parse_dt_channel_info(client, data); + if (err) + return err; + } + /* Initialize the LM90 chip */ err = lm90_init_client(client, data); if (err < 0) { From 9b00a5f50934b74c61982dcd30b2c21e07d0d317 Mon Sep 17 00:00:00 2001 From: Ziyang Xuan Date: Mon, 6 Jun 2022 21:14:01 +0800 Subject: [PATCH 57/85] hwmon: (occ) Delete unnecessary NULL check kvfree(NULL) is safe. NULL check before kvfree() is not needed. Delete them to simplify the code. Generated by coccinelle script: scripts/coccinelle/free/ifnullfree.cocci Signed-off-by: Ziyang Xuan Reviewed-by: Eddie James Link: https://lore.kernel.org/r/20220606131401.4053036-1-william.xuanziyang@huawei.com Signed-off-by: Guenter Roeck --- drivers/hwmon/occ/p9_sbe.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/hwmon/occ/p9_sbe.c b/drivers/hwmon/occ/p9_sbe.c index a91937e28e12..c1e0a1d96cd4 100644 --- a/drivers/hwmon/occ/p9_sbe.c +++ b/drivers/hwmon/occ/p9_sbe.c @@ -55,8 +55,7 @@ static bool p9_sbe_occ_save_ffdc(struct p9_sbe_occ *ctx, const void *resp, mutex_lock(&ctx->sbe_error_lock); if (!ctx->sbe_error) { if (resp_len > ctx->ffdc_size) { - if (ctx->ffdc) - kvfree(ctx->ffdc); + kvfree(ctx->ffdc); ctx->ffdc = kvmalloc(resp_len, GFP_KERNEL); if (!ctx->ffdc) { ctx->ffdc_len = 0; @@ -170,8 +169,7 @@ static int p9_sbe_occ_remove(struct platform_device *pdev) ctx->sbe = NULL; occ_shutdown(occ); - if (ctx->ffdc) - kvfree(ctx->ffdc); + kvfree(ctx->ffdc); return 0; } From 07845f559d325ce7b227b478dd33b8c524a065ad Mon Sep 17 00:00:00 2001 From: Slawomir Stepien Date: Tue, 7 Jun 2022 08:35:03 +0200 Subject: [PATCH 58/85] hwmon: (lm90) Add support for 2nd remote channel's offset register The ADT7481 have LM90_HAVE_TEMP3 and LM90_HAVE_OFFSET flags, but the support of second remote channel's offset is missing. Add that implementation. Signed-off-by: Slawomir Stepien Link: https://lore.kernel.org/r/20220607063504.1287855-2-sst@poczta.fm Signed-off-by: Guenter Roeck --- drivers/hwmon/lm90.c | 58 +++++++++++++++++++++++++++++++++++++------- 1 file changed, 49 insertions(+), 9 deletions(-) diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c index a18aed5f96d1..ec885cb3ab92 100644 --- a/drivers/hwmon/lm90.c +++ b/drivers/hwmon/lm90.c @@ -672,6 +672,7 @@ enum lm90_temp_reg_index { REMOTE2_TEMP, /* max6695/96 only */ REMOTE2_LOW, /* max6695/96 only */ REMOTE2_HIGH, /* max6695/96 only */ + REMOTE2_OFFSET, TEMP_REG_NUM }; @@ -1028,6 +1029,14 @@ static int lm90_update_limits(struct device *dev) return val; data->temp[REMOTE2_HIGH] = val << 8; + if (data->flags & LM90_HAVE_OFFSET) { + val = lm90_read16(client, LM90_REG_REMOTE_OFFSH, + LM90_REG_REMOTE_OFFSL, false); + if (val < 0) + return val; + data->temp[REMOTE2_OFFSET] = val; + } + lm90_select_remote_channel(data, false); } @@ -1298,6 +1307,7 @@ static int lm90_temp_get_resolution(struct lm90_data *data, int index) return data->resolution; return 8; case REMOTE_OFFSET: + case REMOTE2_OFFSET: case REMOTE2_TEMP: return data->resolution; case LOCAL_TEMP: @@ -1428,6 +1438,36 @@ static int lm90_set_temphyst(struct lm90_data *data, long val) return lm90_write_reg(data->client, LM90_REG_TCRIT_HYST, data->temp_hyst); } +static int lm90_get_temp_offset(struct lm90_data *data, int index) +{ + int res = lm90_temp_get_resolution(data, index); + + return lm90_temp_from_reg(0, data->temp[index], res); +} + +static int lm90_set_temp_offset(struct lm90_data *data, int index, int channel, long val) +{ + int err; + + val = lm90_temp_to_reg(0, val, lm90_temp_get_resolution(data, index)); + + /* For ADT7481 we can use the same registers for remote channel 1 and 2 */ + if (channel > 1) + lm90_select_remote_channel(data, true); + + err = lm90_write16(data->client, LM90_REG_REMOTE_OFFSH, LM90_REG_REMOTE_OFFSL, val); + + if (channel > 1) + lm90_select_remote_channel(data, false); + + if (err) + return err; + + data->temp[index] = val; + + return 0; +} + static const u8 lm90_temp_index[MAX_CHANNELS] = { LOCAL_TEMP, REMOTE_TEMP, REMOTE2_TEMP }; @@ -1448,6 +1488,10 @@ static const u8 lm90_temp_emerg_index[MAX_CHANNELS] = { LOCAL_EMERG, REMOTE_EMERG, REMOTE2_EMERG }; +static const s8 lm90_temp_offset_index[MAX_CHANNELS] = { + -1, REMOTE_OFFSET, REMOTE2_OFFSET +}; + static const u16 lm90_min_alarm_bits[MAX_CHANNELS] = { BIT(5), BIT(3), BIT(11) }; static const u16 lm90_max_alarm_bits[MAX_CHANNELS] = { BIT(6), BIT(4), BIT(12) }; static const u16 lm90_crit_alarm_bits[MAX_CHANNELS] = { BIT(0), BIT(1), BIT(9) }; @@ -1519,8 +1563,7 @@ static int lm90_temp_read(struct device *dev, u32 attr, int channel, long *val) *val = lm90_get_temphyst(data, lm90_temp_emerg_index[channel], channel); break; case hwmon_temp_offset: - *val = lm90_temp_from_reg(0, data->temp[REMOTE_OFFSET], - lm90_temp_get_resolution(data, REMOTE_OFFSET)); + *val = lm90_get_temp_offset(data, lm90_temp_offset_index[channel]); break; default: return -EOPNOTSUPP; @@ -1560,13 +1603,8 @@ static int lm90_temp_write(struct device *dev, u32 attr, int channel, long val) channel, val); break; case hwmon_temp_offset: - val = lm90_temp_to_reg(0, val, - lm90_temp_get_resolution(data, REMOTE_OFFSET)); - err = lm90_write16(data->client, LM90_REG_REMOTE_OFFSH, - LM90_REG_REMOTE_OFFSL, val); - if (err) - break; - data->temp[REMOTE_OFFSET] = val; + err = lm90_set_temp_offset(data, lm90_temp_offset_index[channel], + channel, val); break; default: err = -EOPNOTSUPP; @@ -2803,6 +2841,8 @@ static int lm90_probe(struct i2c_client *client) } if (data->flags & LM90_HAVE_EMERGENCY_ALARM) data->channel_config[2] |= HWMON_T_EMERGENCY_ALARM; + if (data->flags & LM90_HAVE_OFFSET) + data->channel_config[2] |= HWMON_T_OFFSET; } data->faultqueue_mask = lm90_params[data->kind].faultqueue_mask; From 00dc6452bee557c7d34a6064472817f6f6b3f185 Mon Sep 17 00:00:00 2001 From: Slawomir Stepien Date: Tue, 7 Jun 2022 08:35:04 +0200 Subject: [PATCH 59/85] hwmon: (lm90) Read the channel's temperature offset from device-tree Try to read the channel's temperature offset from device-tree. Having offset in device-tree node is not mandatory. The offset can only be set for remote channels. Signed-off-by: Slawomir Stepien Link: https://lore.kernel.org/r/20220607063504.1287855-3-sst@poczta.fm Signed-off-by: Guenter Roeck --- drivers/hwmon/lm90.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c index ec885cb3ab92..9d878163a1f2 100644 --- a/drivers/hwmon/lm90.c +++ b/drivers/hwmon/lm90.c @@ -2666,6 +2666,7 @@ static int lm90_probe_channel_from_dt(struct i2c_client *client, struct lm90_data *data) { u32 id; + s32 val; int err; struct device *dev = &client->dev; @@ -2689,6 +2690,21 @@ static int lm90_probe_channel_from_dt(struct i2c_client *client, if (data->channel_label[id]) data->channel_config[id] |= HWMON_T_LABEL; + err = of_property_read_s32(child, "temperature-offset-millicelsius", &val); + if (!err) { + if (id == 0) { + dev_err(dev, "temperature-offset-millicelsius can't be set for internal channel\n"); + return -EINVAL; + } + + err = lm90_set_temp_offset(data, lm90_temp_offset_index[id], id, val); + if (err) { + dev_err(dev, "can't set temperature offset %d for channel %d (%d)\n", + val, id, err); + return err; + } + } + return 0; } From 0356d778aa1e14d80db40a9dae02192d6b47a5ac Mon Sep 17 00:00:00 2001 From: Ren Zhijie Date: Thu, 9 Jun 2022 20:04:48 +0800 Subject: [PATCH 60/85] hwmon: (pmbus) fix build error unused-function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If CONFIG_PMBUS is y and CONFIG_DEBUG_FS is not set. make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu-, will be failed, like this: drivers/hwmon/pmbus/pmbus_core.c:593:13: error: ‘pmbus_check_block_register’ defined but not used [-Werror=unused-function] static bool pmbus_check_block_register(struct i2c_client *client, int page, ^~~~~~~~~~~~~~~~~~~~~~~~~~ cc1: all warnings being treated as errors make[3]: *** [drivers/hwmon/pmbus/pmbus_core.o] Error 1 make[2]: *** [drivers/hwmon/pmbus] Error 2 make[2]: *** Waiting for unfinished jobs.... make[1]: *** [drivers/hwmon] Error 2 make[1]: *** Waiting for unfinished jobs.... make: *** [drivers] Error 2 To fix building warning, use __maybe_unused to attach this func. Reported-by: Hulk Robot Fixes: c3ffc3a1ff83("hwmon: (pmbus) add a function to check the presence of a block register") Signed-off-by: Ren Zhijie Link: https://lore.kernel.org/r/20220609120448.139907-1-renzhijie2@huawei.com [groeck: Fixed continuation line alignment] Signed-off-by: Guenter Roeck --- drivers/hwmon/pmbus/pmbus_core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c index 809e666cb52b..4187dc20d2aa 100644 --- a/drivers/hwmon/pmbus/pmbus_core.c +++ b/drivers/hwmon/pmbus/pmbus_core.c @@ -590,8 +590,8 @@ bool pmbus_check_word_register(struct i2c_client *client, int page, int reg) } EXPORT_SYMBOL_NS_GPL(pmbus_check_word_register, PMBUS); -static bool pmbus_check_block_register(struct i2c_client *client, int page, - int reg) +static bool __maybe_unused pmbus_check_block_register(struct i2c_client *client, + int page, int reg) { int rv; struct pmbus_data *data = i2c_get_clientdata(client); From 452d5e2976941c24c4fe9e6d782bfbac3cabd54f Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 10 Jun 2022 13:33:24 +0300 Subject: [PATCH 61/85] hwmon: (nct6775) Drop duplicate NULL check in ->init() and ->exit() Since platform_device_unregister() is NULL-aware, we don't need to duplicate this check. Remove it and fold the rest of the code. Signed-off-by: Andy Shevchenko Reviewed-by: Zev Weiss Link: https://lore.kernel.org/r/20220610103324.87483-1-andriy.shevchenko@linux.intel.com Signed-off-by: Guenter Roeck --- drivers/hwmon/nct6775-platform.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/drivers/hwmon/nct6775-platform.c b/drivers/hwmon/nct6775-platform.c index 6d46c9401898..3a48627419ba 100644 --- a/drivers/hwmon/nct6775-platform.c +++ b/drivers/hwmon/nct6775-platform.c @@ -1200,10 +1200,8 @@ static int __init sensors_nct6775_platform_init(void) exit_device_put: platform_device_put(pdev[i]); exit_device_unregister: - while (--i >= 0) { - if (pdev[i]) - platform_device_unregister(pdev[i]); - } + while (i--) + platform_device_unregister(pdev[i]); exit_unregister: platform_driver_unregister(&nct6775_driver); return err; @@ -1213,10 +1211,8 @@ static void __exit sensors_nct6775_platform_exit(void) { int i; - for (i = 0; i < ARRAY_SIZE(pdev); i++) { - if (pdev[i]) - platform_device_unregister(pdev[i]); - } + for (i = 0; i < ARRAY_SIZE(pdev); i++) + platform_device_unregister(pdev[i]); platform_driver_unregister(&nct6775_driver); } From 385e5f57053ff293282fea84c1c27186d53f66e1 Mon Sep 17 00:00:00 2001 From: Armin Wolf Date: Sun, 12 Jun 2022 06:18:06 +0200 Subject: [PATCH 62/85] hwmon: (dell-smm) Add Dell XPS 13 7390 to fan control whitelist MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A user reported that the program dell-bios-fan-control worked on his Dell XPS 13 7390 to switch off automatic fan control. Since it uses the same mechanism as the dell_smm_hwmon module, add this model to the fan control whitelist. Compile-tested only. Signed-off-by: Armin Wolf Acked-by: Pali Rohár Link: https://lore.kernel.org/r/20220612041806.11367-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 071aa6f4e109..16c10ac84a91 100644 --- a/drivers/hwmon/dell-smm-hwmon.c +++ b/drivers/hwmon/dell-smm-hwmon.c @@ -1365,6 +1365,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 XPS 13 7390", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "XPS 13 7390"), + }, + .driver_data = (void *)&i8k_fan_control_data[I8K_FAN_34A3_35A3], + }, { } }; From 489dd8f05a3e97a28bc240be29b78bf8aba77969 Mon Sep 17 00:00:00 2001 From: Armin Wolf Date: Mon, 13 Jun 2022 01:22:08 +0200 Subject: [PATCH 63/85] hwmon: (dell-smm) Add Dell G5 5590 to DMI table MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit According to Bug 215983 at bugzilla.kernel.org, the Dell G5 5590 supports the SMM interface and can thus be loaded with ignore_dmi being set. Add the model the DMI table to allow for automatic loadig on this model. Compile-tested only. Signed-off-by: Armin Wolf Acked-by: Pali Rohár Link: https://lore.kernel.org/r/20220612232208.27901-1-W_Armin@gmx.de Signed-off-by: Guenter Roeck --- drivers/hwmon/dell-smm-hwmon.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/hwmon/dell-smm-hwmon.c b/drivers/hwmon/dell-smm-hwmon.c index 16c10ac84a91..d419885dd771 100644 --- a/drivers/hwmon/dell-smm-hwmon.c +++ b/drivers/hwmon/dell-smm-hwmon.c @@ -1131,6 +1131,13 @@ static const struct i8k_config_data i8k_config_data[] __initconst = { }; static const struct dmi_system_id i8k_dmi_table[] __initconst = { + { + .ident = "Dell G5 5590", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "G5 5590"), + }, + }, { .ident = "Dell Inspiron", .matches = { From b674bcb13f418e0580878c21afbe14c4bdfa3138 Mon Sep 17 00:00:00 2001 From: Liang He Date: Thu, 16 Jun 2022 19:40:24 +0800 Subject: [PATCH 64/85] hwmon: (gsc-hwmon) Add missing of_node_put() In gsc_hwmon_get_devtree_pdata(), of_find_compatible_node() will return a node pointer with refcount incremented. We should use of_node_put() in fail path or when it is not used anymore. Signed-off-by: Liang He Link: https://lore.kernel.org/r/20220616114024.3985770-1-windhl@126.com Signed-off-by: Guenter Roeck --- drivers/hwmon/gsc-hwmon.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/hwmon/gsc-hwmon.c b/drivers/hwmon/gsc-hwmon.c index 1fe37418ff46..d64be48f1ef6 100644 --- a/drivers/hwmon/gsc-hwmon.c +++ b/drivers/hwmon/gsc-hwmon.c @@ -269,10 +269,13 @@ gsc_hwmon_get_devtree_pdata(struct device *dev) /* fan controller base address */ fan = of_find_compatible_node(dev->parent->of_node, NULL, "gw,gsc-fan"); if (fan && of_property_read_u32(fan, "reg", &pdata->fan_base)) { + of_node_put(fan); dev_err(dev, "fan node without base\n"); return ERR_PTR(-EINVAL); } + of_node_put(fan); + /* allocate structures for channels and count instances of each type */ device_for_each_child_node(dev, child) { if (fwnode_property_read_string(child, "label", &ch->name)) { From 07fb76273db89d9340fa1fea2997d73fa3768ab9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A5rten=20Lindahl?= Date: Tue, 14 Jun 2022 11:38:54 +0200 Subject: [PATCH 65/85] hwmon: (pmbus) Introduce and use cached vout margins MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When setting a new voltage the voltage boundaries are read every time to check that the new voltage is within the proper range. Checking these voltage boundaries consists of reading one of PMBUS_MFR_VOUT_MIN/ PMBUS_VOUT_MARGIN_LOW registers and then PMBUS_MFR_VOUT_MAX/ PMBUS_VOUT_MARGIN_HIGH together with writing the PMBUS_CLEAR_FAULTS register. Since these boundaries are never being changed, it can be cached and thus saving unnecessary smbus transmissions. Signed-off-by: Mårten Lindahl Link: https://lore.kernel.org/r/20220614093856.3470672-2-marten.lindahl@axis.com Signed-off-by: Guenter Roeck --- drivers/hwmon/pmbus/pmbus_core.c | 78 +++++++++++++++++++++++++------- 1 file changed, 61 insertions(+), 17 deletions(-) diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c index 4187dc20d2aa..8c6f44988ba5 100644 --- a/drivers/hwmon/pmbus/pmbus_core.c +++ b/drivers/hwmon/pmbus/pmbus_core.c @@ -104,6 +104,9 @@ struct pmbus_data { s16 currpage; /* current page, -1 for unknown/unset */ s16 currphase; /* current phase, 0xff for all, -1 for unknown/unset */ + + int vout_low[PMBUS_PAGES]; /* voltage low margin */ + int vout_high[PMBUS_PAGES]; /* voltage high margin */ }; struct pmbus_debugfs_entry { @@ -2848,6 +2851,58 @@ static int pmbus_regulator_get_error_flags(struct regulator_dev *rdev, unsigned return 0; } +static int pmbus_regulator_get_low_margin(struct i2c_client *client, int page) +{ + struct pmbus_data *data = i2c_get_clientdata(client); + struct pmbus_sensor s = { + .page = page, + .class = PSC_VOLTAGE_OUT, + .convert = true, + .data = -1, + }; + + if (!data->vout_low[page]) { + if (pmbus_check_word_register(client, page, PMBUS_MFR_VOUT_MIN)) + s.data = _pmbus_read_word_data(client, page, 0xff, + PMBUS_MFR_VOUT_MIN); + if (s.data < 0) { + s.data = _pmbus_read_word_data(client, page, 0xff, + PMBUS_VOUT_MARGIN_LOW); + if (s.data < 0) + return s.data; + } + data->vout_low[page] = pmbus_reg2data(data, &s); + } + + return data->vout_low[page]; +} + +static int pmbus_regulator_get_high_margin(struct i2c_client *client, int page) +{ + struct pmbus_data *data = i2c_get_clientdata(client); + struct pmbus_sensor s = { + .page = page, + .class = PSC_VOLTAGE_OUT, + .convert = true, + .data = -1, + }; + + if (!data->vout_high[page]) { + if (pmbus_check_word_register(client, page, PMBUS_MFR_VOUT_MAX)) + s.data = _pmbus_read_word_data(client, page, 0xff, + PMBUS_MFR_VOUT_MAX); + if (s.data < 0) { + s.data = _pmbus_read_word_data(client, page, 0xff, + PMBUS_VOUT_MARGIN_HIGH); + if (s.data < 0) + return s.data; + } + data->vout_high[page] = pmbus_reg2data(data, &s); + } + + return data->vout_high[page]; +} + static int pmbus_regulator_get_voltage(struct regulator_dev *rdev) { struct device *dev = rdev_get_dev(rdev); @@ -2883,24 +2938,13 @@ static int pmbus_regulator_set_voltage(struct regulator_dev *rdev, int min_uv, *selector = 0; - if (pmbus_check_word_register(client, s.page, PMBUS_MFR_VOUT_MIN)) - s.data = _pmbus_read_word_data(client, s.page, 0xff, PMBUS_MFR_VOUT_MIN); - if (s.data < 0) { - s.data = _pmbus_read_word_data(client, s.page, 0xff, PMBUS_VOUT_MARGIN_LOW); - if (s.data < 0) - return s.data; - } - low = pmbus_reg2data(data, &s); + low = pmbus_regulator_get_low_margin(client, s.page); + if (low < 0) + return low; - s.data = -1; - if (pmbus_check_word_register(client, s.page, PMBUS_MFR_VOUT_MAX)) - s.data = _pmbus_read_word_data(client, s.page, 0xff, PMBUS_MFR_VOUT_MAX); - if (s.data < 0) { - s.data = _pmbus_read_word_data(client, s.page, 0xff, PMBUS_VOUT_MARGIN_HIGH); - if (s.data < 0) - return s.data; - } - high = pmbus_reg2data(data, &s); + high = pmbus_regulator_get_high_margin(client, s.page); + if (high < 0) + return high; /* Make sure we are within margins */ if (low > val) From 2a20db9bfc42b71fa311ace70265ba16f5a3ab80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A5rten=20Lindahl?= Date: Tue, 14 Jun 2022 11:38:55 +0200 Subject: [PATCH 66/85] hwmon: (pmbus) Add list_voltage to pmbus ops MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When checking if a regulator supports a voltage range, the regulator needs to have a list_voltage callback set to the regulator_ops or else -EINVAL will be returned. This support does not exist for the pmbus regulators, so this patch adds pmbus_regulator_list_voltage to the pmbus_regulator_ops. Signed-off-by: Mårten Lindahl Link: https://lore.kernel.org/r/20220614093856.3470672-3-marten.lindahl@axis.com Signed-off-by: Guenter Roeck --- drivers/hwmon/pmbus/pmbus_core.c | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c index 8c6f44988ba5..f10bac8860fc 100644 --- a/drivers/hwmon/pmbus/pmbus_core.c +++ b/drivers/hwmon/pmbus/pmbus_core.c @@ -2957,6 +2957,35 @@ static int pmbus_regulator_set_voltage(struct regulator_dev *rdev, int min_uv, return _pmbus_write_word_data(client, s.page, PMBUS_VOUT_COMMAND, (u16)val); } +static int pmbus_regulator_list_voltage(struct regulator_dev *rdev, + unsigned int selector) +{ + struct device *dev = rdev_get_dev(rdev); + struct i2c_client *client = to_i2c_client(dev->parent); + int val, low, high; + + if (selector >= rdev->desc->n_voltages || + selector < rdev->desc->linear_min_sel) + return -EINVAL; + + selector -= rdev->desc->linear_min_sel; + val = DIV_ROUND_CLOSEST(rdev->desc->min_uV + + (rdev->desc->uV_step * selector), 1000); /* convert to mV */ + + low = pmbus_regulator_get_low_margin(client, rdev_get_id(rdev)); + if (low < 0) + return low; + + high = pmbus_regulator_get_high_margin(client, rdev_get_id(rdev)); + if (high < 0) + return high; + + if (val >= low && val <= high) + return val * 1000; /* unit is uV */ + + return 0; +} + const struct regulator_ops pmbus_regulator_ops = { .enable = pmbus_regulator_enable, .disable = pmbus_regulator_disable, @@ -2964,6 +2993,7 @@ const struct regulator_ops pmbus_regulator_ops = { .get_error_flags = pmbus_regulator_get_error_flags, .get_voltage = pmbus_regulator_get_voltage, .set_voltage = pmbus_regulator_set_voltage, + .list_voltage = pmbus_regulator_list_voltage, }; EXPORT_SYMBOL_NS_GPL(pmbus_regulator_ops, PMBUS); From 4a235369dae5aa366c007fa46f670375cd845374 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A5rten=20Lindahl?= Date: Tue, 14 Jun 2022 11:51:44 +0200 Subject: [PATCH 67/85] hwmon: (pmbus/ltc2978) Set voltage resolution MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The LTC2977 regulator does not set the regulator_desc .n_voltages value which is needed in order to let the regulator core list the regulator voltage range. This patch defines a regulator_desc with a voltage range, and uses it for defining voltage resolution for regulators LTC2972/LTC2974/LTC2975/ LTC2977/LTC2978/LTC2979/LTC2980/LTM2987 based on that they all have a 16 bit ADC with the same stepwise 122.07uV resolution. It also scales the resolution to a 1mV resolution which is easier to handle. Signed-off-by: Mårten Lindahl Link: https://lore.kernel.org/r/20220614095144.3472305-1-marten.lindahl@axis.com Signed-off-by: Guenter Roeck --- drivers/hwmon/pmbus/ltc2978.c | 44 +++++++++++++++++++++++++++++++---- drivers/hwmon/pmbus/pmbus.h | 8 +++++-- 2 files changed, 46 insertions(+), 6 deletions(-) diff --git a/drivers/hwmon/pmbus/ltc2978.c b/drivers/hwmon/pmbus/ltc2978.c index 531aa674a928..6d2592731ba3 100644 --- a/drivers/hwmon/pmbus/ltc2978.c +++ b/drivers/hwmon/pmbus/ltc2978.c @@ -562,7 +562,24 @@ static const struct i2c_device_id ltc2978_id[] = { MODULE_DEVICE_TABLE(i2c, ltc2978_id); #if IS_ENABLED(CONFIG_SENSORS_LTC2978_REGULATOR) +#define LTC2978_ADC_RES 0xFFFF +#define LTC2978_N_ADC 122 +#define LTC2978_MAX_UV (LTC2978_ADC_RES * LTC2978_N_ADC) +#define LTC2978_UV_STEP 1000 +#define LTC2978_N_VOLTAGES ((LTC2978_MAX_UV / LTC2978_UV_STEP) + 1) + static const struct regulator_desc ltc2978_reg_desc[] = { + PMBUS_REGULATOR_STEP("vout", 0, LTC2978_N_VOLTAGES, LTC2978_UV_STEP), + PMBUS_REGULATOR_STEP("vout", 1, LTC2978_N_VOLTAGES, LTC2978_UV_STEP), + PMBUS_REGULATOR_STEP("vout", 2, LTC2978_N_VOLTAGES, LTC2978_UV_STEP), + PMBUS_REGULATOR_STEP("vout", 3, LTC2978_N_VOLTAGES, LTC2978_UV_STEP), + PMBUS_REGULATOR_STEP("vout", 4, LTC2978_N_VOLTAGES, LTC2978_UV_STEP), + PMBUS_REGULATOR_STEP("vout", 5, LTC2978_N_VOLTAGES, LTC2978_UV_STEP), + PMBUS_REGULATOR_STEP("vout", 6, LTC2978_N_VOLTAGES, LTC2978_UV_STEP), + PMBUS_REGULATOR_STEP("vout", 7, LTC2978_N_VOLTAGES, LTC2978_UV_STEP), +}; + +static const struct regulator_desc ltc2978_reg_desc_default[] = { PMBUS_REGULATOR("vout", 0), PMBUS_REGULATOR("vout", 1), PMBUS_REGULATOR("vout", 2), @@ -839,10 +856,29 @@ static int ltc2978_probe(struct i2c_client *client) #if IS_ENABLED(CONFIG_SENSORS_LTC2978_REGULATOR) info->num_regulators = info->pages; - info->reg_desc = ltc2978_reg_desc; - if (info->num_regulators > ARRAY_SIZE(ltc2978_reg_desc)) { - dev_err(&client->dev, "num_regulators too large!"); - info->num_regulators = ARRAY_SIZE(ltc2978_reg_desc); + switch (data->id) { + case ltc2972: + case ltc2974: + case ltc2975: + case ltc2977: + case ltc2978: + case ltc2979: + case ltc2980: + case ltm2987: + info->reg_desc = ltc2978_reg_desc; + if (info->num_regulators > ARRAY_SIZE(ltc2978_reg_desc)) { + dev_warn(&client->dev, "num_regulators too large!"); + info->num_regulators = ARRAY_SIZE(ltc2978_reg_desc); + } + break; + default: + info->reg_desc = ltc2978_reg_desc_default; + if (info->num_regulators > ARRAY_SIZE(ltc2978_reg_desc_default)) { + dev_warn(&client->dev, "num_regulators too large!"); + info->num_regulators = + ARRAY_SIZE(ltc2978_reg_desc_default); + } + break; } #endif diff --git a/drivers/hwmon/pmbus/pmbus.h b/drivers/hwmon/pmbus/pmbus.h index c708b60c1b48..7daaf0caf4d3 100644 --- a/drivers/hwmon/pmbus/pmbus.h +++ b/drivers/hwmon/pmbus/pmbus.h @@ -463,8 +463,8 @@ struct pmbus_driver_info { extern const struct regulator_ops pmbus_regulator_ops; -/* Macro for filling in array of struct regulator_desc */ -#define PMBUS_REGULATOR(_name, _id) \ +/* Macros for filling in array of struct regulator_desc */ +#define PMBUS_REGULATOR_STEP(_name, _id, _voltages, _step) \ [_id] = { \ .name = (_name # _id), \ .supply_name = "vin", \ @@ -474,8 +474,12 @@ extern const struct regulator_ops pmbus_regulator_ops; .ops = &pmbus_regulator_ops, \ .type = REGULATOR_VOLTAGE, \ .owner = THIS_MODULE, \ + .n_voltages = _voltages, \ + .uV_step = _step, \ } +#define PMBUS_REGULATOR(_name, _id) PMBUS_REGULATOR_STEP(_name, _id, 0, 0) + /* Function declarations */ void pmbus_clear_cache(struct i2c_client *client); From c10d52d6eda346e2c714d0d534ac8078d684511e Mon Sep 17 00:00:00 2001 From: Armin Wolf Date: Sun, 20 Feb 2022 20:08:51 +0100 Subject: [PATCH 68/85] hwmon: (dell-smm) Improve assembly code The new assembly code works on both 32 bit and 64 bit cpus and allows for more compiler optimisations. Since clang runs out of registers on 32 bit x86 when using CC_OUT, we need to execute "setc" ourself. Also modify the debug message so we can still see the result (eax) when the carry flag was set. Tested with 32 bit and 64 bit kernels on a Dell Inspiron 3505. Signed-off-by: Armin Wolf Link: https://lore.kernel.org/r/20220220190851.17965-1-W_Armin@gmx.de [groeck: Rebased to v5.19-rc3] Signed-off-by: Guenter Roeck --- drivers/hwmon/dell-smm-hwmon.c | 78 ++++++++-------------------------- 1 file changed, 18 insertions(+), 60 deletions(-) diff --git a/drivers/hwmon/dell-smm-hwmon.c b/drivers/hwmon/dell-smm-hwmon.c index d419885dd771..7f8d95dd2717 100644 --- a/drivers/hwmon/dell-smm-hwmon.c +++ b/drivers/hwmon/dell-smm-hwmon.c @@ -130,7 +130,7 @@ struct smm_regs { unsigned int edx; unsigned int esi; unsigned int edi; -} __packed; +}; static const char * const temp_labels[] = { "CPU", @@ -175,77 +175,35 @@ static int i8k_smm_func(void *par) struct smm_regs *regs = par; int eax = regs->eax; int ebx = regs->ebx; + unsigned char carry; long long duration; - int rc; /* SMM requires CPU 0 */ if (smp_processor_id() != 0) return -EBUSY; -#if defined(CONFIG_X86_64) - asm volatile("pushq %%rax\n\t" - "movl 0(%%rax),%%edx\n\t" - "pushq %%rdx\n\t" - "movl 4(%%rax),%%ebx\n\t" - "movl 8(%%rax),%%ecx\n\t" - "movl 12(%%rax),%%edx\n\t" - "movl 16(%%rax),%%esi\n\t" - "movl 20(%%rax),%%edi\n\t" - "popq %%rax\n\t" - "out %%al,$0xb2\n\t" - "out %%al,$0x84\n\t" - "xchgq %%rax,(%%rsp)\n\t" - "movl %%ebx,4(%%rax)\n\t" - "movl %%ecx,8(%%rax)\n\t" - "movl %%edx,12(%%rax)\n\t" - "movl %%esi,16(%%rax)\n\t" - "movl %%edi,20(%%rax)\n\t" - "popq %%rdx\n\t" - "movl %%edx,0(%%rax)\n\t" - "pushfq\n\t" - "popq %%rax\n\t" - "andl $1,%%eax\n" - : "=a"(rc) - : "a"(regs) - : "%ebx", "%ecx", "%edx", "%esi", "%edi", "memory"); -#else - asm volatile("pushl %%eax\n\t" - "movl 0(%%eax),%%edx\n\t" - "push %%edx\n\t" - "movl 4(%%eax),%%ebx\n\t" - "movl 8(%%eax),%%ecx\n\t" - "movl 12(%%eax),%%edx\n\t" - "movl 16(%%eax),%%esi\n\t" - "movl 20(%%eax),%%edi\n\t" - "popl %%eax\n\t" - "out %%al,$0xb2\n\t" - "out %%al,$0x84\n\t" - "xchgl %%eax,(%%esp)\n\t" - "movl %%ebx,4(%%eax)\n\t" - "movl %%ecx,8(%%eax)\n\t" - "movl %%edx,12(%%eax)\n\t" - "movl %%esi,16(%%eax)\n\t" - "movl %%edi,20(%%eax)\n\t" - "popl %%edx\n\t" - "movl %%edx,0(%%eax)\n\t" - "lahf\n\t" - "shrl $8,%%eax\n\t" - "andl $1,%%eax\n" - : "=a"(rc) - : "a"(regs) - : "%ebx", "%ecx", "%edx", "%esi", "%edi", "memory"); -#endif - if (rc != 0 || (regs->eax & 0xffff) == 0xffff || regs->eax == eax) - rc = -EINVAL; + asm volatile("out %%al,$0xb2\n\t" + "out %%al,$0x84\n\t" + "setc %0\n" + : "=mr" (carry), + "+a" (regs->eax), + "+b" (regs->ebx), + "+c" (regs->ecx), + "+d" (regs->edx), + "+S" (regs->esi), + "+D" (regs->edi)); duration = ktime_us_delta(ktime_get(), calltime); - pr_debug("smm(0x%.4x 0x%.4x) = 0x%.4x (took %7lld usecs)\n", eax, ebx, - (rc ? 0xffff : regs->eax & 0xffff), duration); + pr_debug("smm(0x%.4x 0x%.4x) = 0x%.4x carry: %d (took %7lld usecs)\n", + eax, ebx, regs->eax & 0xffff, carry, duration); if (duration > DELL_SMM_MAX_DURATION) pr_warn_once("SMM call took %lld usecs!\n", duration); - return rc; + if (carry || (regs->eax & 0xffff) == 0xffff || regs->eax == eax) + return -EINVAL; + + return 0; } /* From 8f9eb10ff71d8e3beeee3f8d19050223600faf85 Mon Sep 17 00:00:00 2001 From: Michael Carns Date: Tue, 28 Jun 2022 00:54:36 +0200 Subject: [PATCH 69/85] hwmon: (asus-ec-sensors) add support for Maximus XI Hero Add definitions for ROG MAXIMUS XI HERO and ROG MAXIMUS XI HERO (WI-FI) boards. Signed-off-by: Michael Carns Signed-off-by: Eugene Shalygin Link: https://lore.kernel.org/r/20220627225437.87462-1-eugene.shalygin@gmail.com Signed-off-by: Guenter Roeck --- Documentation/hwmon/asus_ec_sensors.rst | 2 ++ drivers/hwmon/asus-ec-sensors.c | 36 +++++++++++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/Documentation/hwmon/asus_ec_sensors.rst b/Documentation/hwmon/asus_ec_sensors.rst index 00d8c46ef9e0..1e40c123db77 100644 --- a/Documentation/hwmon/asus_ec_sensors.rst +++ b/Documentation/hwmon/asus_ec_sensors.rst @@ -13,6 +13,8 @@ Supported boards: * ROG CROSSHAIR VIII FORMULA * ROG CROSSHAIR VIII HERO * ROG CROSSHAIR VIII IMPACT + * ROG MAXIMUS XI HERO + * ROG MAXIMUS XI HERO (WI-FI) * ROG STRIX B550-E GAMING * ROG STRIX B550-I GAMING * ROG STRIX X570-E GAMING diff --git a/drivers/hwmon/asus-ec-sensors.c b/drivers/hwmon/asus-ec-sensors.c index 19d3ca71b360..625c2baa35ec 100644 --- a/drivers/hwmon/asus-ec-sensors.c +++ b/drivers/hwmon/asus-ec-sensors.c @@ -141,6 +141,7 @@ enum board_family { family_unknown, family_amd_400_series, family_amd_500_series, + family_intel_300_series, family_intel_600_series }; @@ -200,6 +201,26 @@ static const struct ec_sensor_info sensors_family_amd_500[] = { EC_SENSOR("Water_Out", hwmon_temp, 1, 0x01, 0x01), }; +static const struct ec_sensor_info sensors_family_intel_300[] = { + [ec_sensor_temp_chipset] = + EC_SENSOR("Chipset", hwmon_temp, 1, 0x00, 0x3a), + [ec_sensor_temp_cpu] = EC_SENSOR("CPU", hwmon_temp, 1, 0x00, 0x3b), + [ec_sensor_temp_mb] = + EC_SENSOR("Motherboard", hwmon_temp, 1, 0x00, 0x3c), + [ec_sensor_temp_t_sensor] = + EC_SENSOR("T_Sensor", hwmon_temp, 1, 0x00, 0x3d), + [ec_sensor_temp_vrm] = EC_SENSOR("VRM", hwmon_temp, 1, 0x00, 0x3e), + [ec_sensor_fan_cpu_opt] = + EC_SENSOR("CPU_Opt", hwmon_fan, 2, 0x00, 0xb0), + [ec_sensor_fan_vrm_hs] = EC_SENSOR("VRM HS", hwmon_fan, 2, 0x00, 0xb2), + [ec_sensor_fan_water_flow] = + EC_SENSOR("Water_Flow", hwmon_fan, 2, 0x00, 0xbc), + [ec_sensor_temp_water_in] = + EC_SENSOR("Water_In", hwmon_temp, 1, 0x01, 0x00), + [ec_sensor_temp_water_out] = + EC_SENSOR("Water_Out", hwmon_temp, 1, 0x01, 0x01), +}; + static const struct ec_sensor_info sensors_family_intel_600[] = { [ec_sensor_temp_t_sensor] = EC_SENSOR("T_Sensor", hwmon_temp, 1, 0x00, 0x3d), @@ -281,6 +302,18 @@ static const struct ec_board_info board_info[] = { .mutex_path = ASUS_HW_ACCESS_MUTEX_ASMX, .family = family_amd_500_series, }, + { + .board_names = { + "ROG MAXIMUS XI HERO", + "ROG MAXIMUS XI HERO (WI-FI)", + }, + .sensors = SENSOR_SET_TEMP_CHIPSET_CPU_MB | + SENSOR_TEMP_T_SENSOR | + SENSOR_TEMP_VRM | SENSOR_SET_TEMP_WATER | + SENSOR_FAN_CPU_OPT | SENSOR_FAN_WATER_FLOW, + .mutex_path = ASUS_HW_ACCESS_MUTEX_ASMX, + .family = family_intel_300_series, + }, { .board_names = {"ROG CROSSHAIR VIII IMPACT"}, .sensors = SENSOR_SET_TEMP_CHIPSET_CPU_MB | @@ -814,6 +847,9 @@ static int __init asus_ec_probe(struct platform_device *pdev) case family_amd_500_series: ec_data->sensors_info = sensors_family_amd_500; break; + case family_intel_300_series: + ec_data->sensors_info = sensors_family_intel_300; + break; case family_intel_600_series: ec_data->sensors_info = sensors_family_intel_600; break; From 5993b9887ac3b80fba78e1a488ac748d6e4429c1 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Wed, 29 Jun 2022 08:26:20 -0700 Subject: [PATCH 70/85] hwmon: (lm90) Use worker for alarm notifications Reporting alarms using hwmon_notify_event() may result in a callback from the thermal subsystem. This means that such notifications must not hold the update lock to avoid a deadlock. To avoid this situation, use a worker to handle notifications. Reported-by: Dmitry Osipenko Tested-by: Dmitry Osipenko Fixes: f6d0775119fb ("hwmon: (lm90) Rework alarm/status handling") Signed-off-by: Guenter Roeck --- drivers/hwmon/lm90.c | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c index 9d878163a1f2..03d07da8c2dc 100644 --- a/drivers/hwmon/lm90.c +++ b/drivers/hwmon/lm90.c @@ -693,6 +693,7 @@ struct lm90_data { struct hwmon_chip_info chip; struct mutex update_lock; struct delayed_work alert_work; + struct work_struct report_work; bool valid; /* true if register values are valid */ bool alarms_valid; /* true if status register values are valid */ unsigned long last_updated; /* in jiffies */ @@ -1043,13 +1044,18 @@ static int lm90_update_limits(struct device *dev) return 0; } -static void lm90_report_alarms(struct device *dev, struct lm90_data *data) +static void lm90_report_alarms(struct work_struct *work) { - u16 cleared_alarms = data->reported_alarms & ~data->current_alarms; - u16 new_alarms = data->current_alarms & ~data->reported_alarms; + struct lm90_data *data = container_of(work, struct lm90_data, report_work); + u16 cleared_alarms, new_alarms, current_alarms; struct device *hwmon_dev = data->hwmon_dev; + struct device *dev = &data->client->dev; int st, st2; + current_alarms = data->current_alarms; + cleared_alarms = data->reported_alarms & ~current_alarms; + new_alarms = current_alarms & ~data->reported_alarms; + if (!cleared_alarms && !new_alarms) return; @@ -1101,7 +1107,7 @@ static void lm90_report_alarms(struct device *dev, struct lm90_data *data) if (st2 & MAX6696_STATUS2_R2OT2) hwmon_notify_event(hwmon_dev, hwmon_temp, hwmon_temp_emergency_alarm, 2); - data->reported_alarms = data->current_alarms; + data->reported_alarms = current_alarms; } static int lm90_update_alarms_locked(struct lm90_data *data, bool force) @@ -1143,7 +1149,7 @@ static int lm90_update_alarms_locked(struct lm90_data *data, bool force) (data->config & 0x80); if (force || check_enable) - lm90_report_alarms(&client->dev, data); + schedule_work(&data->report_work); /* * Re-enable ALERT# output if it was originally enabled, relevant @@ -2552,6 +2558,7 @@ static void lm90_restore_conf(void *_data) struct i2c_client *client = data->client; cancel_delayed_work_sync(&data->alert_work); + cancel_work_sync(&data->report_work); /* Restore initial configuration */ if (data->flags & LM90_HAVE_CONVRATE) @@ -2769,6 +2776,7 @@ static int lm90_probe(struct i2c_client *client) i2c_set_clientdata(client, data); mutex_init(&data->update_lock); INIT_DELAYED_WORK(&data->alert_work, lm90_alert_work); + INIT_WORK(&data->report_work, lm90_report_alarms); /* Set the device type */ if (client->dev.of_node) From 8e1187fe7a7fbac4df1a95abe0fffa3a41d345aa Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Sat, 2 Jul 2022 08:05:54 +0200 Subject: [PATCH 71/85] hwmon: (asus_wmi_sensors) Save a few bytes of memory The first 'for' loop of asus_wmi_configure_sensor_setup() only computes the number and type of sensors that exist in the system. Here, the 'temp_sensor' structure is only used to store the data collected by asus_wmi_sensor_info(). There is no point in using a devm_ variant for this allocation. This wastes some memory for no good reason. Use the stack instead. Signed-off-by: Christophe JAILLET Link: https://lore.kernel.org/r/e23cea6c489fabb109a61e8a33d146a6b74c0529.1656741926.git.christophe.jaillet@wanadoo.fr Signed-off-by: Guenter Roeck --- drivers/hwmon/asus_wmi_sensors.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/drivers/hwmon/asus_wmi_sensors.c b/drivers/hwmon/asus_wmi_sensors.c index 9e935e34c998..6e8a908171f0 100644 --- a/drivers/hwmon/asus_wmi_sensors.c +++ b/drivers/hwmon/asus_wmi_sensors.c @@ -514,22 +514,20 @@ static int asus_wmi_configure_sensor_setup(struct device *dev, int i, idx; int err; - temp_sensor = devm_kcalloc(dev, 1, sizeof(*temp_sensor), GFP_KERNEL); - if (!temp_sensor) - return -ENOMEM; - for (i = 0; i < sensor_data->wmi.sensor_count; i++) { - err = asus_wmi_sensor_info(i, temp_sensor); + struct asus_wmi_sensor_info sensor; + + err = asus_wmi_sensor_info(i, &sensor); if (err) return err; - switch (temp_sensor->data_type) { + switch (sensor.data_type) { case TEMPERATURE_C: case VOLTAGE: case CURRENT: case FAN_RPM: case WATER_FLOW: - type = asus_data_types[temp_sensor->data_type]; + type = asus_data_types[sensor.data_type]; if (!nr_count[type]) nr_types++; nr_count[type]++; From 5918036cfa8ded7aa8094db70295011ce2275447 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Tue, 12 Jul 2022 23:46:24 +0200 Subject: [PATCH 72/85] hwmon: (drivetemp) Add module alias Adding a MODULE_ALIAS() to drivetemp will make the driver easier for modprobe to autoprobe. Signed-off-by: Linus Walleij Link: https://lore.kernel.org/r/20220712214624.1845158-1-linus.walleij@linaro.org Fixes: 5b46903d8bf3 ("hwmon: Driver for disk and solid state drives with temperature sensors") Signed-off-by: Guenter Roeck --- drivers/hwmon/drivetemp.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/hwmon/drivetemp.c b/drivers/hwmon/drivetemp.c index 1eb37106a220..5bac2b0fc7bb 100644 --- a/drivers/hwmon/drivetemp.c +++ b/drivers/hwmon/drivetemp.c @@ -621,3 +621,4 @@ module_exit(drivetemp_exit); MODULE_AUTHOR("Guenter Roeck "); MODULE_DESCRIPTION("Hard drive temperature monitor"); MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:drivetemp"); From 1c4e4f4a0e8d9ebe8be1c838fec4fb7053a989d9 Mon Sep 17 00:00:00 2001 From: Eugene Shalygin Date: Sun, 10 Jul 2022 10:55:38 +0200 Subject: [PATCH 73/85] hwmon: (asus-ec-sensors) add missing sensors for X570-I GAMING VRM and chipset temperature for ROG STRIX X570-I GAMING were missing according to a user contribution to the LHM project [1]. [1] https://github.com/LibreHardwareMonitor/LibreHardwareMonitor/pull/767 Signed-off-by: Eugene Shalygin Link: https://lore.kernel.org/r/20220710085539.1682869-1-eugene.shalygin@gmail.com Signed-off-by: Guenter Roeck --- drivers/hwmon/asus-ec-sensors.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/hwmon/asus-ec-sensors.c b/drivers/hwmon/asus-ec-sensors.c index 625c2baa35ec..0749cd023a32 100644 --- a/drivers/hwmon/asus-ec-sensors.c +++ b/drivers/hwmon/asus-ec-sensors.c @@ -366,9 +366,10 @@ static const struct ec_board_info board_info[] = { }, { .board_names = {"ROG STRIX X570-I GAMING"}, - .sensors = SENSOR_TEMP_T_SENSOR | SENSOR_FAN_VRM_HS | - SENSOR_FAN_CHIPSET | SENSOR_CURR_CPU | - SENSOR_IN_CPU_CORE, + .sensors = SENSOR_TEMP_CHIPSET | SENSOR_TEMP_VRM | + SENSOR_TEMP_T_SENSOR | + SENSOR_FAN_VRM_HS | SENSOR_FAN_CHIPSET | + SENSOR_CURR_CPU | SENSOR_IN_CPU_CORE, .mutex_path = ASUS_HW_ACCESS_MUTEX_ASMX, .family = family_amd_500_series, }, From 654c97351baf5e2f00e417291cc9e9caf1d07f4b Mon Sep 17 00:00:00 2001 From: Aleksa Savic Date: Thu, 7 Jul 2022 13:50:50 +0200 Subject: [PATCH 74/85] hwmon: (aquacomputer_d5next) Move device-specific data into struct aqc_data As preparation for adding support for more devices in upcoming patches, move device-specific data, such as number of fans, temperature sensors, register offsets etc. to struct aqc_data. This is made possible by the fact that the supported Aquacomputer devices share the same layouts of sensor substructures. This allows aqc_raw_event() and others to stay general and not be cluttered with similar loops for each device. Signed-off-by: Jack Doan Signed-off-by: Aleksa Savic Link: https://lore.kernel.org/r/20220707115050.90021-1-savicaleksa83@gmail.com Signed-off-by: Guenter Roeck --- drivers/hwmon/aquacomputer_d5next.c | 246 +++++++++++----------------- 1 file changed, 93 insertions(+), 153 deletions(-) diff --git a/drivers/hwmon/aquacomputer_d5next.c b/drivers/hwmon/aquacomputer_d5next.c index a0e69f7ece36..0e56cc711a26 100644 --- a/drivers/hwmon/aquacomputer_d5next.c +++ b/drivers/hwmon/aquacomputer_d5next.c @@ -54,56 +54,40 @@ static u8 secondary_ctrl_report[] = { 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x34, 0xC6 }; +/* Register offsets for all Aquacomputer devices */ +#define AQC_TEMP_SENSOR_SIZE 0x02 +#define AQC_TEMP_SENSOR_DISCONNECTED 0x7FFF +#define AQC_FAN_PERCENT_OFFSET 0x00 +#define AQC_FAN_VOLTAGE_OFFSET 0x02 +#define AQC_FAN_CURRENT_OFFSET 0x04 +#define AQC_FAN_POWER_OFFSET 0x06 +#define AQC_FAN_SPEED_OFFSET 0x08 + /* Register offsets for the D5 Next pump */ -#define D5NEXT_POWER_CYCLES 24 - -#define D5NEXT_COOLANT_TEMP 87 - -#define D5NEXT_PUMP_SPEED 116 -#define D5NEXT_FAN_SPEED 103 - -#define D5NEXT_PUMP_POWER 114 -#define D5NEXT_FAN_POWER 101 - -#define D5NEXT_PUMP_VOLTAGE 110 -#define D5NEXT_FAN_VOLTAGE 97 -#define D5NEXT_5V_VOLTAGE 57 - -#define D5NEXT_PUMP_CURRENT 112 -#define D5NEXT_FAN_CURRENT 99 +#define D5NEXT_POWER_CYCLES 0x18 +#define D5NEXT_COOLANT_TEMP 0x57 +#define D5NEXT_NUM_FANS 2 +#define D5NEXT_NUM_SENSORS 1 +#define D5NEXT_PUMP_OFFSET 0x6c +#define D5NEXT_FAN_OFFSET 0x5f +#define D5NEXT_5V_VOLTAGE 0x39 +static u8 d5next_sensor_fan_offsets[] = { D5NEXT_PUMP_OFFSET, D5NEXT_FAN_OFFSET }; /* Register offsets for the Farbwerk RGB controller */ #define FARBWERK_NUM_SENSORS 4 #define FARBWERK_SENSOR_START 0x2f -#define FARBWERK_SENSOR_SIZE 0x02 -#define FARBWERK_SENSOR_DISCONNECTED 0x7FFF /* Register offsets for the Farbwerk 360 RGB controller */ #define FARBWERK360_NUM_SENSORS 4 #define FARBWERK360_SENSOR_START 0x32 -#define FARBWERK360_SENSOR_SIZE 0x02 -#define FARBWERK360_SENSOR_DISCONNECTED 0x7FFF /* Register offsets for the Octo fan controller */ #define OCTO_POWER_CYCLES 0x18 #define OCTO_NUM_FANS 8 -#define OCTO_FAN_PERCENT_OFFSET 0x00 -#define OCTO_FAN_VOLTAGE_OFFSET 0x02 -#define OCTO_FAN_CURRENT_OFFSET 0x04 -#define OCTO_FAN_POWER_OFFSET 0x06 -#define OCTO_FAN_SPEED_OFFSET 0x08 - -static u8 octo_sensor_fan_offsets[] = { 0x7D, 0x8A, 0x97, 0xA4, 0xB1, 0xBE, 0xCB, 0xD8 }; - #define OCTO_NUM_SENSORS 4 #define OCTO_SENSOR_START 0x3D -#define OCTO_SENSOR_SIZE 0x02 -#define OCTO_SENSOR_DISCONNECTED 0x7FFF - -#define OCTO_CTRL_REPORT_SIZE 0x65F -#define OCTO_CTRL_REPORT_CHECKSUM_OFFSET 0x65D -#define OCTO_CTRL_REPORT_CHECKSUM_START 0x01 -#define OCTO_CTRL_REPORT_CHECKSUM_LENGTH 0x65C +#define OCTO_CTRL_REPORT_SIZE 0x65F +static u8 octo_sensor_fan_offsets[] = { 0x7D, 0x8A, 0x97, 0xA4, 0xB1, 0xBE, 0xCB, 0xD8 }; /* Fan speed registers in Octo control report (from 0-100%) */ static u16 octo_ctrl_fan_offsets[] = { 0x5B, 0xB0, 0x105, 0x15A, 0x1AF, 0x204, 0x259, 0x2AE }; @@ -201,11 +185,18 @@ struct aqc_data { int checksum_length; int checksum_offset; + int num_fans; + u8 *fan_sensor_offsets; + u16 *fan_ctrl_offsets; + int num_temp_sensors; + int temp_sensor_start_offset; + u16 power_cycle_count_offset; + /* General info, same across all devices */ u32 serial_number[2]; u16 firmware_version; - /* How many times the device was powered on */ + /* How many times the device was powered on, if available */ u32 power_cycles; /* Sensor values */ @@ -323,56 +314,35 @@ static umode_t aqc_is_visible(const void *data, enum hwmon_sensor_types type, u3 switch (type) { case hwmon_temp: - switch (priv->kind) { - case d5next: - if (channel == 0) - return 0444; - break; - case farbwerk: - case farbwerk360: - case octo: + if (channel < priv->num_temp_sensors) return 0444; - default: - break; - } break; case hwmon_pwm: - switch (priv->kind) { - case octo: + if (priv->fan_ctrl_offsets && channel < priv->num_fans) { switch (attr) { case hwmon_pwm_input: return 0644; default: break; } - break; - default: - break; } break; case hwmon_fan: case hwmon_power: case hwmon_curr: - switch (priv->kind) { - case d5next: - if (channel < 2) - return 0444; - break; - case octo: + if (channel < priv->num_fans) return 0444; - default: - break; - } break; case hwmon_in: switch (priv->kind) { case d5next: - if (channel < 3) + /* Special case to support voltage sensor */ + if (channel < priv->num_fans + 1) return 0444; break; - case octo: - return 0444; default: + if (channel < priv->num_fans) + return 0444; break; } break; @@ -406,16 +376,12 @@ static int aqc_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, *val = priv->power_input[channel]; break; case hwmon_pwm: - switch (priv->kind) { - case octo: - ret = aqc_get_ctrl_val(priv, octo_ctrl_fan_offsets[channel]); + if (priv->fan_ctrl_offsets) { + ret = aqc_get_ctrl_val(priv, priv->fan_ctrl_offsets[channel]); if (ret < 0) return ret; *val = aqc_percent_to_pwm(ret); - break; - default: - break; } break; case hwmon_in: @@ -469,19 +435,15 @@ static int aqc_write(struct device *dev, enum hwmon_sensor_types type, u32 attr, case hwmon_pwm: switch (attr) { case hwmon_pwm_input: - switch (priv->kind) { - case octo: + if (priv->fan_ctrl_offsets) { pwm_value = aqc_pwm_to_percent(val); if (pwm_value < 0) return pwm_value; - ret = aqc_set_ctrl_val(priv, octo_ctrl_fan_offsets[channel], + ret = aqc_set_ctrl_val(priv, priv->fan_ctrl_offsets[channel], pwm_value); if (ret < 0) return ret; - break; - default: - break; } break; default: @@ -576,76 +538,38 @@ static int aqc_raw_event(struct hid_device *hdev, struct hid_report *report, u8 priv->serial_number[1] = get_unaligned_be16(data + SERIAL_SECOND_PART); priv->firmware_version = get_unaligned_be16(data + FIRMWARE_VERSION); - /* Sensor readings */ + /* Temperature sensor readings */ + for (i = 0; i < priv->num_temp_sensors; i++) { + sensor_value = get_unaligned_be16(data + + priv->temp_sensor_start_offset + + i * AQC_TEMP_SENSOR_SIZE); + if (sensor_value == AQC_TEMP_SENSOR_DISCONNECTED) + priv->temp_input[i] = -ENODATA; + else + priv->temp_input[i] = sensor_value * 10; + } + + /* Fan speed and related readings */ + for (i = 0; i < priv->num_fans; i++) { + priv->speed_input[i] = + get_unaligned_be16(data + priv->fan_sensor_offsets[i] + AQC_FAN_SPEED_OFFSET); + priv->power_input[i] = + get_unaligned_be16(data + priv->fan_sensor_offsets[i] + + AQC_FAN_POWER_OFFSET) * 10000; + priv->voltage_input[i] = + get_unaligned_be16(data + priv->fan_sensor_offsets[i] + + AQC_FAN_VOLTAGE_OFFSET) * 10; + priv->current_input[i] = + get_unaligned_be16(data + priv->fan_sensor_offsets[i] + AQC_FAN_CURRENT_OFFSET); + } + + if (priv->power_cycle_count_offset != 0) + priv->power_cycles = get_unaligned_be32(data + priv->power_cycle_count_offset); + + /* Special-case sensor readings */ switch (priv->kind) { case d5next: - priv->power_cycles = get_unaligned_be32(data + D5NEXT_POWER_CYCLES); - - priv->temp_input[0] = get_unaligned_be16(data + D5NEXT_COOLANT_TEMP) * 10; - - priv->speed_input[0] = get_unaligned_be16(data + D5NEXT_PUMP_SPEED); - priv->speed_input[1] = get_unaligned_be16(data + D5NEXT_FAN_SPEED); - - priv->power_input[0] = get_unaligned_be16(data + D5NEXT_PUMP_POWER) * 10000; - priv->power_input[1] = get_unaligned_be16(data + D5NEXT_FAN_POWER) * 10000; - - priv->voltage_input[0] = get_unaligned_be16(data + D5NEXT_PUMP_VOLTAGE) * 10; - priv->voltage_input[1] = get_unaligned_be16(data + D5NEXT_FAN_VOLTAGE) * 10; priv->voltage_input[2] = get_unaligned_be16(data + D5NEXT_5V_VOLTAGE) * 10; - - priv->current_input[0] = get_unaligned_be16(data + D5NEXT_PUMP_CURRENT); - priv->current_input[1] = get_unaligned_be16(data + D5NEXT_FAN_CURRENT); - break; - case farbwerk: - /* Temperature sensor readings */ - for (i = 0; i < FARBWERK_NUM_SENSORS; i++) { - sensor_value = get_unaligned_be16(data + FARBWERK_SENSOR_START + - i * FARBWERK_SENSOR_SIZE); - if (sensor_value == FARBWERK_SENSOR_DISCONNECTED) - priv->temp_input[i] = -ENODATA; - else - priv->temp_input[i] = sensor_value * 10; - } - break; - case farbwerk360: - /* Temperature sensor readings */ - for (i = 0; i < FARBWERK360_NUM_SENSORS; i++) { - sensor_value = get_unaligned_be16(data + FARBWERK360_SENSOR_START + - i * FARBWERK360_SENSOR_SIZE); - if (sensor_value == FARBWERK360_SENSOR_DISCONNECTED) - priv->temp_input[i] = -ENODATA; - else - priv->temp_input[i] = sensor_value * 10; - } - break; - case octo: - priv->power_cycles = get_unaligned_be32(data + OCTO_POWER_CYCLES); - - /* Fan speed and related readings */ - for (i = 0; i < OCTO_NUM_FANS; i++) { - priv->speed_input[i] = - get_unaligned_be16(data + octo_sensor_fan_offsets[i] + - OCTO_FAN_SPEED_OFFSET); - priv->power_input[i] = - get_unaligned_be16(data + octo_sensor_fan_offsets[i] + - OCTO_FAN_POWER_OFFSET) * 10000; - priv->voltage_input[i] = - get_unaligned_be16(data + octo_sensor_fan_offsets[i] + - OCTO_FAN_VOLTAGE_OFFSET) * 10; - priv->current_input[i] = - get_unaligned_be16(data + octo_sensor_fan_offsets[i] + - OCTO_FAN_CURRENT_OFFSET); - } - - /* Temperature sensor readings */ - for (i = 0; i < OCTO_NUM_SENSORS; i++) { - sensor_value = get_unaligned_be16(data + OCTO_SENSOR_START + - i * OCTO_SENSOR_SIZE); - if (sensor_value == OCTO_SENSOR_DISCONNECTED) - priv->temp_input[i] = -ENODATA; - else - priv->temp_input[i] = sensor_value * 10; - } break; default: break; @@ -699,14 +623,8 @@ static void aqc_debugfs_init(struct aqc_data *priv) debugfs_create_file("serial_number", 0444, priv->debugfs, priv, &serial_number_fops); debugfs_create_file("firmware_version", 0444, priv->debugfs, priv, &firmware_version_fops); - switch (priv->kind) { - case d5next: - case octo: + if (priv->power_cycle_count_offset != 0) debugfs_create_file("power_cycles", 0444, priv->debugfs, priv, &power_cycles_fops); - break; - default: - break; - } } #else @@ -747,6 +665,12 @@ static int aqc_probe(struct hid_device *hdev, const struct hid_device_id *id) case USB_PRODUCT_ID_D5NEXT: priv->kind = d5next; + priv->num_fans = D5NEXT_NUM_FANS; + priv->fan_sensor_offsets = d5next_sensor_fan_offsets; + priv->num_temp_sensors = D5NEXT_NUM_SENSORS; + priv->temp_sensor_start_offset = D5NEXT_COOLANT_TEMP; + priv->power_cycle_count_offset = D5NEXT_POWER_CYCLES; + priv->temp_label = label_d5next_temp; priv->speed_label = label_d5next_speeds; priv->power_label = label_d5next_power; @@ -756,19 +680,29 @@ static int aqc_probe(struct hid_device *hdev, const struct hid_device_id *id) case USB_PRODUCT_ID_FARBWERK: priv->kind = farbwerk; + priv->num_fans = 0; + priv->num_temp_sensors = FARBWERK_NUM_SENSORS; + priv->temp_sensor_start_offset = FARBWERK_SENSOR_START; priv->temp_label = label_temp_sensors; break; case USB_PRODUCT_ID_FARBWERK360: priv->kind = farbwerk360; + priv->num_fans = 0; + priv->num_temp_sensors = FARBWERK360_NUM_SENSORS; + priv->temp_sensor_start_offset = FARBWERK360_SENSOR_START; priv->temp_label = label_temp_sensors; break; case USB_PRODUCT_ID_OCTO: priv->kind = octo; + + priv->num_fans = OCTO_NUM_FANS; + priv->fan_sensor_offsets = octo_sensor_fan_offsets; + priv->fan_ctrl_offsets = octo_ctrl_fan_offsets; + priv->num_temp_sensors = OCTO_NUM_SENSORS; + priv->temp_sensor_start_offset = OCTO_SENSOR_START; + priv->power_cycle_count_offset = OCTO_POWER_CYCLES; priv->buffer_size = OCTO_CTRL_REPORT_SIZE; - priv->checksum_start = OCTO_CTRL_REPORT_CHECKSUM_START; - priv->checksum_length = OCTO_CTRL_REPORT_CHECKSUM_LENGTH; - priv->checksum_offset = OCTO_CTRL_REPORT_CHECKSUM_OFFSET; priv->temp_label = label_temp_sensors; priv->speed_label = label_fan_speed; @@ -780,6 +714,12 @@ static int aqc_probe(struct hid_device *hdev, const struct hid_device_id *id) break; } + if (priv->buffer_size != 0) { + priv->checksum_start = 0x01; + priv->checksum_length = priv->buffer_size - 3; + priv->checksum_offset = priv->buffer_size - 2; + } + priv->name = aqc_device_names[priv->kind]; priv->buffer = devm_kzalloc(&hdev->dev, priv->buffer_size, GFP_KERNEL); From 9992b19d756ab8f0889fcaf3e71ff93852e74694 Mon Sep 17 00:00:00 2001 From: Urs Schroffenegger Date: Sun, 10 Jul 2022 22:26:39 +0200 Subject: [PATCH 75/85] hwmon: (asus-ec-sensors) add definitions for ROG ZENITH II EXTREME Add definitions for ROG ZENITH II EXTREME and some unknown yet temperature sensors in the second EC bank. Details are available at [1, 2]. [1] https://github.com/zeule/asus-ec-sensors/pull/26 [2] https://github.com/zeule/asus-ec-sensors/issues/16 Signed-off-by: Urs Schroffenegger Signed-off-by: Eugene Shalygin Link: https://lore.kernel.org/r/20220710202639.1812058-2-eugene.shalygin@gmail.com Signed-off-by: Guenter Roeck --- Documentation/hwmon/asus_ec_sensors.rst | 1 + drivers/hwmon/asus-ec-sensors.c | 47 +++++++++++++++++++++++++ 2 files changed, 48 insertions(+) diff --git a/Documentation/hwmon/asus_ec_sensors.rst b/Documentation/hwmon/asus_ec_sensors.rst index 1e40c123db77..02f4ad314a1e 100644 --- a/Documentation/hwmon/asus_ec_sensors.rst +++ b/Documentation/hwmon/asus_ec_sensors.rst @@ -22,6 +22,7 @@ Supported boards: * ROG STRIX X570-F GAMING * ROG STRIX X570-I GAMING * ROG STRIX Z690-A GAMING WIFI D4 + * ROG ZENITH II EXTREME Authors: - Eugene Shalygin diff --git a/drivers/hwmon/asus-ec-sensors.c b/drivers/hwmon/asus-ec-sensors.c index 0749cd023a32..61a4684fc020 100644 --- a/drivers/hwmon/asus-ec-sensors.c +++ b/drivers/hwmon/asus-ec-sensors.c @@ -56,6 +56,8 @@ static char *mutex_path_override; #define ASUS_HW_ACCESS_MUTEX_RMTW_ASMX "\\RMTW.ASMX" +#define ASUS_HW_ACCESS_MUTEX_SB_PCI0_SBRG_SIO1_MUT0 "\\_SB_.PCI0.SBRG.SIO1.MUT0" + #define MAX_IDENTICAL_BOARD_VARIATIONS 3 /* Moniker for the ACPI global lock (':' is not allowed in ASL identifiers) */ @@ -121,6 +123,18 @@ enum ec_sensors { ec_sensor_temp_water_in, /* "Water_Out" temperature sensor reading [℃] */ ec_sensor_temp_water_out, + /* "Water_Block_In" temperature sensor reading [℃] */ + ec_sensor_temp_water_block_in, + /* "Water_Block_Out" temperature sensor reading [℃] */ + ec_sensor_temp_water_block_out, + /* "T_sensor_2" temperature sensor reading [℃] */ + ec_sensor_temp_t_sensor_2, + /* "Extra_1" temperature sensor reading [℃] */ + ec_sensor_temp_sensor_extra_1, + /* "Extra_2" temperature sensor reading [℃] */ + ec_sensor_temp_sensor_extra_2, + /* "Extra_3" temperature sensor reading [℃] */ + ec_sensor_temp_sensor_extra_3, }; #define SENSOR_TEMP_CHIPSET BIT(ec_sensor_temp_chipset) @@ -136,6 +150,12 @@ enum ec_sensors { #define SENSOR_CURR_CPU BIT(ec_sensor_curr_cpu) #define SENSOR_TEMP_WATER_IN BIT(ec_sensor_temp_water_in) #define SENSOR_TEMP_WATER_OUT BIT(ec_sensor_temp_water_out) +#define SENSOR_TEMP_WATER_BLOCK_IN BIT(ec_sensor_temp_water_block_in) +#define SENSOR_TEMP_WATER_BLOCK_OUT BIT(ec_sensor_temp_water_block_out) +#define SENSOR_TEMP_T_SENSOR_2 BIT(ec_sensor_temp_t_sensor_2) +#define SENSOR_TEMP_SENSOR_EXTRA_1 BIT(ec_sensor_temp_sensor_extra_1) +#define SENSOR_TEMP_SENSOR_EXTRA_2 BIT(ec_sensor_temp_sensor_extra_2) +#define SENSOR_TEMP_SENSOR_EXTRA_3 BIT(ec_sensor_temp_sensor_extra_3) enum board_family { family_unknown, @@ -199,6 +219,18 @@ static const struct ec_sensor_info sensors_family_amd_500[] = { EC_SENSOR("Water_In", hwmon_temp, 1, 0x01, 0x00), [ec_sensor_temp_water_out] = EC_SENSOR("Water_Out", hwmon_temp, 1, 0x01, 0x01), + [ec_sensor_temp_water_block_in] = + EC_SENSOR("Water_Block_In", hwmon_temp, 1, 0x01, 0x02), + [ec_sensor_temp_water_block_out] = + EC_SENSOR("Water_Block_Out", hwmon_temp, 1, 0x01, 0x03), + [ec_sensor_temp_sensor_extra_1] = + EC_SENSOR("Extra_1", hwmon_temp, 1, 0x01, 0x09), + [ec_sensor_temp_t_sensor_2] = + EC_SENSOR("T_sensor_2", hwmon_temp, 1, 0x01, 0x0a), + [ec_sensor_temp_sensor_extra_2] = + EC_SENSOR("Extra_2", hwmon_temp, 1, 0x01, 0x0b), + [ec_sensor_temp_sensor_extra_3] = + EC_SENSOR("Extra_3", hwmon_temp, 1, 0x01, 0x0c), }; static const struct ec_sensor_info sensors_family_intel_300[] = { @@ -231,6 +263,9 @@ static const struct ec_sensor_info sensors_family_intel_600[] = { #define SENSOR_SET_TEMP_CHIPSET_CPU_MB \ (SENSOR_TEMP_CHIPSET | SENSOR_TEMP_CPU | SENSOR_TEMP_MB) #define SENSOR_SET_TEMP_WATER (SENSOR_TEMP_WATER_IN | SENSOR_TEMP_WATER_OUT) +#define SENSOR_SET_WATER_BLOCK \ + (SENSOR_TEMP_WATER_BLOCK_IN | SENSOR_TEMP_WATER_BLOCK_OUT) + struct ec_board_info { const char *board_names[MAX_IDENTICAL_BOARD_VARIATIONS]; @@ -379,6 +414,18 @@ static const struct ec_board_info board_info[] = { .mutex_path = ASUS_HW_ACCESS_MUTEX_RMTW_ASMX, .family = family_intel_600_series, }, + { + .board_names = {"ROG ZENITH II EXTREME"}, + .sensors = SENSOR_SET_TEMP_CHIPSET_CPU_MB | SENSOR_TEMP_T_SENSOR | + SENSOR_TEMP_VRM | SENSOR_SET_TEMP_WATER | + SENSOR_FAN_CPU_OPT | SENSOR_FAN_CHIPSET | SENSOR_FAN_VRM_HS | + SENSOR_FAN_WATER_FLOW | SENSOR_CURR_CPU | SENSOR_IN_CPU_CORE | + SENSOR_SET_WATER_BLOCK | + SENSOR_TEMP_T_SENSOR_2 | SENSOR_TEMP_SENSOR_EXTRA_1 | + SENSOR_TEMP_SENSOR_EXTRA_2 | SENSOR_TEMP_SENSOR_EXTRA_3, + .mutex_path = ASUS_HW_ACCESS_MUTEX_SB_PCI0_SBRG_SIO1_MUT0, + .family = family_amd_500_series, + }, {} }; From 03508eea538557b4f61bf1df2e842d252cb9a6e6 Mon Sep 17 00:00:00 2001 From: Marc Ferland Date: Tue, 12 Jul 2022 15:35:04 -0400 Subject: [PATCH 76/85] hwmon: (mcp3021) improve driver support for newer hwmon interface This driver is currently broken, it does not show the in0_input sysfs file and also returns the following message on startup: hwmon_device_register() is deprecated. Please convert the driver to use hwmon_device_register_with_info(). This patch converts the driver and also cleans up the 'read' function. Signed-off-by: Marc Ferland Link: https://lore.kernel.org/r/20220712193504.1374656-1-ferlandm@amotus.ca Signed-off-by: Guenter Roeck --- drivers/hwmon/mcp3021.c | 101 ++++++++++++++++++++-------------------- 1 file changed, 50 insertions(+), 51 deletions(-) diff --git a/drivers/hwmon/mcp3021.c b/drivers/hwmon/mcp3021.c index ce2780768074..e093b1998296 100644 --- a/drivers/hwmon/mcp3021.c +++ b/drivers/hwmon/mcp3021.c @@ -7,7 +7,7 @@ * Reworked by Sven Schuchmann * DT support added by Clemens Gruber * - * This driver export the value of analog input voltage to sysfs, the + * This driver exports the value of analog input voltage to sysfs, the * voltage unit is mV. Through the sysfs interface, lm-sensors tool * can also display the input voltage. */ @@ -45,19 +45,29 @@ enum chips { * Client data (each client gets its own) */ struct mcp3021_data { - struct device *hwmon_dev; + struct i2c_client *client; u32 vdd; /* supply and reference voltage in millivolt */ u16 sar_shift; u16 sar_mask; u8 output_res; }; -static int mcp3021_read16(struct i2c_client *client) +static inline u16 volts_from_reg(struct mcp3021_data *data, u16 val) { - struct mcp3021_data *data = i2c_get_clientdata(client); - int ret; - u16 reg; + return DIV_ROUND_CLOSEST(data->vdd * val, 1 << data->output_res); +} + +static int mcp3021_read(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long *val) +{ + struct mcp3021_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; __be16 buf; + u16 reg; + int ret; + + if (type != hwmon_in) + return -EOPNOTSUPP; ret = i2c_master_recv(client, (char *)&buf, 2); if (ret < 0) @@ -74,39 +84,46 @@ static int mcp3021_read16(struct i2c_client *client) */ reg = (reg >> data->sar_shift) & data->sar_mask; - return reg; + *val = volts_from_reg(data, reg); + + return 0; } -static inline u16 volts_from_reg(struct mcp3021_data *data, u16 val) +static umode_t mcp3021_is_visible(const void *_data, + enum hwmon_sensor_types type, + u32 attr, int channel) { - return DIV_ROUND_CLOSEST(data->vdd * val, 1 << data->output_res); + if (type != hwmon_in) + return 0; + + if (attr != hwmon_in_input) + return 0; + + return 0444; } -static ssize_t in0_input_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct i2c_client *client = to_i2c_client(dev); - struct mcp3021_data *data = i2c_get_clientdata(client); - int reg, in_input; +static const struct hwmon_channel_info *mcp3021_info[] = { + HWMON_CHANNEL_INFO(in, HWMON_I_INPUT), + NULL +}; - reg = mcp3021_read16(client); - if (reg < 0) - return reg; +static const struct hwmon_ops mcp3021_hwmon_ops = { + .is_visible = mcp3021_is_visible, + .read = mcp3021_read, +}; - in_input = volts_from_reg(data, reg); - - return sprintf(buf, "%d\n", in_input); -} - -static DEVICE_ATTR_RO(in0_input); +static const struct hwmon_chip_info mcp3021_chip_info = { + .ops = &mcp3021_hwmon_ops, + .info = mcp3021_info, +}; static const struct i2c_device_id mcp3021_id[]; static int mcp3021_probe(struct i2c_client *client) { - int err; struct mcp3021_data *data = NULL; struct device_node *np = client->dev.of_node; + struct device *hwmon_dev; if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) return -ENODEV; @@ -147,34 +164,17 @@ static int mcp3021_probe(struct i2c_client *client) break; } + data->client = client; + if (data->vdd > MCP3021_VDD_REF_MAX || data->vdd < MCP3021_VDD_REF_MIN) return -EINVAL; - err = sysfs_create_file(&client->dev.kobj, &dev_attr_in0_input.attr); - if (err) - return err; - - data->hwmon_dev = hwmon_device_register(&client->dev); - if (IS_ERR(data->hwmon_dev)) { - err = PTR_ERR(data->hwmon_dev); - goto exit_remove; - } - - return 0; - -exit_remove: - sysfs_remove_file(&client->dev.kobj, &dev_attr_in0_input.attr); - return err; -} - -static int mcp3021_remove(struct i2c_client *client) -{ - struct mcp3021_data *data = i2c_get_clientdata(client); - - hwmon_device_unregister(data->hwmon_dev); - sysfs_remove_file(&client->dev.kobj, &dev_attr_in0_input.attr); - - return 0; + hwmon_dev = devm_hwmon_device_register_with_info(&client->dev, + client->name, + data, + &mcp3021_chip_info, + NULL); + return PTR_ERR_OR_ZERO(hwmon_dev); } static const struct i2c_device_id mcp3021_id[] = { @@ -199,7 +199,6 @@ static struct i2c_driver mcp3021_driver = { .of_match_table = of_match_ptr(of_mcp3021_match), }, .probe_new = mcp3021_probe, - .remove = mcp3021_remove, .id_table = mcp3021_id, }; From 09e893092e005a191c1103c00d04806b5fd2e886 Mon Sep 17 00:00:00 2001 From: Aleksa Savic Date: Sun, 17 Jul 2022 19:14:12 +0200 Subject: [PATCH 77/85] hwmon: (aquacomputer_d5next) Add D5 Next fan control support Define pump and fan speed register offsets in D5 Next control report, as well as its size, to expose PWM fan control. Originally-from: Jack Doan Signed-off-by: Aleksa Savic Link: https://lore.kernel.org/r/20220717171412.11142-1-savicaleksa83@gmail.com Signed-off-by: Guenter Roeck --- drivers/hwmon/aquacomputer_d5next.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/hwmon/aquacomputer_d5next.c b/drivers/hwmon/aquacomputer_d5next.c index 0e56cc711a26..997d72ea6182 100644 --- a/drivers/hwmon/aquacomputer_d5next.c +++ b/drivers/hwmon/aquacomputer_d5next.c @@ -71,8 +71,12 @@ static u8 secondary_ctrl_report[] = { #define D5NEXT_PUMP_OFFSET 0x6c #define D5NEXT_FAN_OFFSET 0x5f #define D5NEXT_5V_VOLTAGE 0x39 +#define D5NEXT_CTRL_REPORT_SIZE 0x329 static u8 d5next_sensor_fan_offsets[] = { D5NEXT_PUMP_OFFSET, D5NEXT_FAN_OFFSET }; +/* Pump and fan speed registers in D5 Next control report (from 0-100%) */ +static u16 d5next_ctrl_fan_offsets[] = { 0x97, 0x42 }; + /* Register offsets for the Farbwerk RGB controller */ #define FARBWERK_NUM_SENSORS 4 #define FARBWERK_SENSOR_START 0x2f @@ -667,9 +671,11 @@ static int aqc_probe(struct hid_device *hdev, const struct hid_device_id *id) priv->num_fans = D5NEXT_NUM_FANS; priv->fan_sensor_offsets = d5next_sensor_fan_offsets; + priv->fan_ctrl_offsets = d5next_ctrl_fan_offsets; priv->num_temp_sensors = D5NEXT_NUM_SENSORS; priv->temp_sensor_start_offset = D5NEXT_COOLANT_TEMP; priv->power_cycle_count_offset = D5NEXT_POWER_CYCLES; + priv->buffer_size = D5NEXT_CTRL_REPORT_SIZE; priv->temp_label = label_d5next_temp; priv->speed_label = label_d5next_speeds; From 3b1ea71faccba3abbf71f9a2510c212e1783b6e1 Mon Sep 17 00:00:00 2001 From: Paul Fertser Date: Thu, 14 Jul 2022 17:23:44 +0300 Subject: [PATCH 78/85] hwmon: (aspeed-pwm-tacho) increase fan tach period (again) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The old value allows measuring fan speeds down to about 970 RPM and gives timeout for anything less than that. It is problematic because it can also be used as an indicator for fan failure or absence. Despite having read the relevant section of "ASPEED AST2500/AST2520 A2 Datasheet – V1.7" multiple times I wasn't able to figure out what exactly "fan tach period" and "fan tach falling point of period" mean (both are set by the driver from the constant this patch is amending). Experimentation with a Tioga Pass OCP board (AST2500 BMC) showed that value of 0x0108 gives time outs for speeds below 1500 RPM and the value offered by the patch is good for at least 750 RPM (the fans can't spin any slower so the lower bound is unknown). Measuring with the fans spinning takes about 30 ms, sometimes down to 18 ms, so about the same as with the previous value. This constant was last changed with commit 762b1e888013 ("hwmon: (aspeed-pwm-tacho) increase fan tach period") Signed-off-by: Paul Fertser Link: https://lore.kernel.org/r/20220714142344.27071-1-fercerpav@gmail.com Signed-off-by: Guenter Roeck --- drivers/hwmon/aspeed-pwm-tacho.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hwmon/aspeed-pwm-tacho.c b/drivers/hwmon/aspeed-pwm-tacho.c index 3cb88d6fbec0..d11f674e3dc3 100644 --- a/drivers/hwmon/aspeed-pwm-tacho.c +++ b/drivers/hwmon/aspeed-pwm-tacho.c @@ -159,7 +159,7 @@ * 11: reserved. */ #define M_TACH_MODE 0x02 /* 10b */ -#define M_TACH_UNIT 0x0210 +#define M_TACH_UNIT 0x0420 #define INIT_FAN_CTRL 0xFF /* How long we sleep in us while waiting for an RPM result. */ From 0eabb1396656f215a5333a9444158b17b0fd3247 Mon Sep 17 00:00:00 2001 From: Alexandru Gagniuc Date: Wed, 20 Jul 2022 22:22:55 -0500 Subject: [PATCH 79/85] hwmon: (tps23861) fix byte order in current and voltage registers Trying to use this driver on a big-endian machine results in garbage values for voltage and current. The tps23861 registers are little- endian, and regmap_read_bulk() does not do byte order conversion. Thus on BE machines, the most significant bytes got modified, and were trimmed by the VOLTAGE_CURRENT_MASK. To resolve this use uint16_t values, and convert them to host byte order using le16_to_cpu(). This results in correct readings on MIPS. Signed-off-by: Alexandru Gagniuc Link: https://lore.kernel.org/r/20220721032255.2850647-1-mr.nuke.me@gmail.com [groeck: Use __le16 instead of uint16_t] Signed-off-by: Guenter Roeck --- drivers/hwmon/tps23861.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/drivers/hwmon/tps23861.c b/drivers/hwmon/tps23861.c index 8bd6435c13e8..42762e87b014 100644 --- a/drivers/hwmon/tps23861.c +++ b/drivers/hwmon/tps23861.c @@ -140,7 +140,8 @@ static int tps23861_read_temp(struct tps23861_data *data, long *val) static int tps23861_read_voltage(struct tps23861_data *data, int channel, long *val) { - unsigned int regval; + __le16 regval; + long raw_val; int err; if (channel < TPS23861_NUM_PORTS) { @@ -155,7 +156,8 @@ static int tps23861_read_voltage(struct tps23861_data *data, int channel, if (err < 0) return err; - *val = (FIELD_GET(VOLTAGE_CURRENT_MASK, regval) * VOLTAGE_LSB) / 1000; + raw_val = le16_to_cpu(regval); + *val = (FIELD_GET(VOLTAGE_CURRENT_MASK, raw_val) * VOLTAGE_LSB) / 1000; return 0; } @@ -163,8 +165,9 @@ static int tps23861_read_voltage(struct tps23861_data *data, int channel, static int tps23861_read_current(struct tps23861_data *data, int channel, long *val) { - unsigned int current_lsb; - unsigned int regval; + long raw_val, current_lsb; + __le16 regval; + int err; if (data->shunt_resistor == SHUNT_RESISTOR_DEFAULT) @@ -178,7 +181,8 @@ static int tps23861_read_current(struct tps23861_data *data, int channel, if (err < 0) return err; - *val = (FIELD_GET(VOLTAGE_CURRENT_MASK, regval) * current_lsb) / 1000000; + raw_val = le16_to_cpu(regval); + *val = (FIELD_GET(VOLTAGE_CURRENT_MASK, raw_val) * current_lsb) / 1000000; return 0; } From f4caa26216411040e3c7cb80bf351104e3cd3488 Mon Sep 17 00:00:00 2001 From: Aleksa Savic Date: Tue, 26 Jul 2022 14:02:03 +0200 Subject: [PATCH 80/85] hwmon: (aquacomputer_d5next) Add support for reading the +12V voltage sensor on D5 Next Add support for reading the +12V voltage that the D5 Next pump receives. Signed-off-by: Aleksa Savic Link: https://lore.kernel.org/r/20220726120203.33773-1-savicaleksa83@gmail.com Signed-off-by: Guenter Roeck --- drivers/hwmon/aquacomputer_d5next.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/hwmon/aquacomputer_d5next.c b/drivers/hwmon/aquacomputer_d5next.c index 997d72ea6182..738a1df8eae6 100644 --- a/drivers/hwmon/aquacomputer_d5next.c +++ b/drivers/hwmon/aquacomputer_d5next.c @@ -71,6 +71,7 @@ static u8 secondary_ctrl_report[] = { #define D5NEXT_PUMP_OFFSET 0x6c #define D5NEXT_FAN_OFFSET 0x5f #define D5NEXT_5V_VOLTAGE 0x39 +#define D5NEXT_12V_VOLTAGE 0x37 #define D5NEXT_CTRL_REPORT_SIZE 0x329 static u8 d5next_sensor_fan_offsets[] = { D5NEXT_PUMP_OFFSET, D5NEXT_FAN_OFFSET }; @@ -114,7 +115,8 @@ static const char *const label_d5next_power[] = { static const char *const label_d5next_voltages[] = { "Pump voltage", "Fan voltage", - "+5V voltage" + "+5V voltage", + "+12V voltage" }; static const char *const label_d5next_current[] = { @@ -340,8 +342,8 @@ static umode_t aqc_is_visible(const void *data, enum hwmon_sensor_types type, u3 case hwmon_in: switch (priv->kind) { case d5next: - /* Special case to support voltage sensor */ - if (channel < priv->num_fans + 1) + /* Special case to support +5V and +12V voltage sensors */ + if (channel < priv->num_fans + 2) return 0444; break; default: @@ -574,6 +576,7 @@ static int aqc_raw_event(struct hid_device *hdev, struct hid_report *report, u8 switch (priv->kind) { case d5next: priv->voltage_input[2] = get_unaligned_be16(data + D5NEXT_5V_VOLTAGE) * 10; + priv->voltage_input[3] = get_unaligned_be16(data + D5NEXT_12V_VOLTAGE) * 10; break; default: break; From 7d4edccc9bbfe1dcdff641343f7b0c6763fbe774 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Mon, 25 Jul 2022 21:43:44 +0200 Subject: [PATCH 81/85] hwmon: (sht15) Fix wrong assumptions in device remove callback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Taking a lock at the beginning of .remove() doesn't prevent new readers. With the existing approach it can happen, that a read occurs just when the lock was taken blocking the reader until the lock is released at the end of the remove callback which then accessed *data that is already freed then. To actually fix this problem the hwmon core needs some adaption. Until this is implemented take the optimistic approach of assuming that all readers are gone after hwmon_device_unregister() and sysfs_remove_group() as most other drivers do. (And once the core implements that, taking the lock would deadlock.) So drop the lock, move the reset to after device unregistration to keep the device in a workable state until it's deregistered. Also add a error message in case the reset fails and return 0 anyhow. (Returning an error code, doesn't stop the platform device unregistration and only results in a little helpful error message before the devm cleanup handlers are called.) Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20220725194344.150098-1-u.kleine-koenig@pengutronix.de Signed-off-by: Guenter Roeck --- drivers/hwmon/sht15.c | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/drivers/hwmon/sht15.c b/drivers/hwmon/sht15.c index 7f4a63959730..ae4d14257a11 100644 --- a/drivers/hwmon/sht15.c +++ b/drivers/hwmon/sht15.c @@ -1020,25 +1020,20 @@ static int sht15_probe(struct platform_device *pdev) static int sht15_remove(struct platform_device *pdev) { struct sht15_data *data = platform_get_drvdata(pdev); + int ret; - /* - * Make sure any reads from the device are done and - * prevent new ones beginning - */ - mutex_lock(&data->read_lock); - if (sht15_soft_reset(data)) { - mutex_unlock(&data->read_lock); - return -EFAULT; - } hwmon_device_unregister(data->hwmon_dev); sysfs_remove_group(&pdev->dev.kobj, &sht15_attr_group); + + ret = sht15_soft_reset(data); + if (ret) + dev_err(&pdev->dev, "Failed to reset device (%pe)\n", ERR_PTR(ret)); + if (!IS_ERR(data->reg)) { regulator_unregister_notifier(data->reg, &data->nb); regulator_disable(data->reg); } - mutex_unlock(&data->read_lock); - return 0; } From 03009a605f2ae18c0cb77a78b329f79821a4c545 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 26 Jul 2022 17:31:10 +0300 Subject: [PATCH 82/85] hwmon: (occ) Replace open-coded variant of %*phN specifier printf()-like functions in the kernel have extensions, such as %*phN to dump small pieces of memory as hex bytes. Replace custom approach with the direct use of %*phN. Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20220726143110.4809-1-andriy.shevchenko@linux.intel.com Signed-off-by: Guenter Roeck --- drivers/hwmon/occ/common.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/drivers/hwmon/occ/common.c b/drivers/hwmon/occ/common.c index 157b73a3da29..45407b12db4b 100644 --- a/drivers/hwmon/occ/common.c +++ b/drivers/hwmon/occ/common.c @@ -729,18 +729,14 @@ static ssize_t occ_show_extended(struct device *dev, rc = sysfs_emit(buf, "%u", get_unaligned_be32(&extn->sensor_id)); } else { - rc = sysfs_emit(buf, "%02x%02x%02x%02x\n", - extn->name[0], extn->name[1], - extn->name[2], extn->name[3]); + rc = sysfs_emit(buf, "%4phN\n", extn->name); } break; case 1: rc = sysfs_emit(buf, "%02x\n", extn->flags); break; case 2: - rc = sysfs_emit(buf, "%02x%02x%02x%02x%02x%02x\n", - extn->data[0], extn->data[1], extn->data[2], - extn->data[3], extn->data[4], extn->data[5]); + rc = sysfs_emit(buf, "%6phN\n", extn->data); break; default: return -EINVAL; From 38ac173b0ef85a66bfd6d03ce74d4afe37f7f00c Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Tue, 26 Jul 2022 23:16:17 +0200 Subject: [PATCH 83/85] hwmon: (nct6775) add ASUS TUF GAMING B550-PLUS WIFI II Add ASUS TUF GAMING B550-PLUS WIFI II to the WMI monitoring list to enable support for HW monitoring on that board. Signed-off-by: Robert Schmidt Link: https://lore.kernel.org/r/YuBZodZHOnDll5zy@hydra 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 3a48627419ba..ab30437221ce 100644 --- a/drivers/hwmon/nct6775-platform.c +++ b/drivers/hwmon/nct6775-platform.c @@ -1083,6 +1083,7 @@ static const char * const asus_wmi_boards[] = { "TUF GAMING B550M-PLUS", "TUF GAMING B550M-PLUS (WI-FI)", "TUF GAMING B550-PLUS", + "TUF GAMING B550-PLUS WIFI II", "TUF GAMING B550-PRO", "TUF GAMING X570-PLUS", "TUF GAMING X570-PLUS (WI-FI)", From 41cd4373274239a79a5a3e1127607225908a0fbb Mon Sep 17 00:00:00 2001 From: Armin Wolf Date: Wed, 27 Jul 2022 10:30:04 +0200 Subject: [PATCH 84/85] hwmon: (dell-smm) Improve documentation Notify users of this driver that each pwmX attribute controls fan number X, meaning that probing of pwm channels is unnecessary. Signed-off-by: Armin Wolf Link: https://lore.kernel.org/r/20220727083004.5684-1-W_Armin@gmx.de Signed-off-by: Guenter Roeck --- Documentation/hwmon/dell-smm-hwmon.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Documentation/hwmon/dell-smm-hwmon.rst b/Documentation/hwmon/dell-smm-hwmon.rst index e5d85e40972c..d8f1d6859b96 100644 --- a/Documentation/hwmon/dell-smm-hwmon.rst +++ b/Documentation/hwmon/dell-smm-hwmon.rst @@ -46,6 +46,9 @@ temp[1-10]_input RO Temperature reading in milli-degrees temp[1-10]_label RO Temperature sensor label. =============================== ======= ======================================= +Due to the nature of the SMM interface, each pwmX attribute controls +fan number X. + Disabling automatic BIOS fan control ------------------------------------ From cdbe34da01e32024e56fff5c6854a263a012d7ff Mon Sep 17 00:00:00 2001 From: Aleksa Savic Date: Wed, 27 Jul 2022 12:06:05 +0200 Subject: [PATCH 85/85] hwmon: (aquacomputer_d5next) Add support for Aquacomputer Quadro fan controller Extend aquacomputer_d5next driver to expose hardware temperature sensors and fans of the Aquacomputer Quadro fan controller, which communicates through a proprietary USB HID protocol. Implemented by Jack Doan [1]. Four temperature sensors and PWM controllable fans are available. The liquid flow sensor is also exposed, implemented by Leonard Anderweit [2]. Additionally, serial number, firmware version and power-on count are exposed through debugfs. This driver has been tested on x86_64. [1] https://github.com/aleksamagicka/aquacomputer_d5next-hwmon/pull/5 [2] https://github.com/aleksamagicka/aquacomputer_d5next-hwmon/pull/9 Originally-from: Jack Doan Originally-from: Leonard Anderweit Signed-off-by: Aleksa Savic Link: https://lore.kernel.org/r/20220727100606.9328-1-savicaleksa83@gmail.com Signed-off-by: Guenter Roeck --- Documentation/hwmon/aquacomputer_d5next.rst | 17 +++-- drivers/hwmon/Kconfig | 6 +- drivers/hwmon/aquacomputer_d5next.c | 69 +++++++++++++++++++-- 3 files changed, 78 insertions(+), 14 deletions(-) diff --git a/Documentation/hwmon/aquacomputer_d5next.rst b/Documentation/hwmon/aquacomputer_d5next.rst index 717e28226cde..33649a1e3a05 100644 --- a/Documentation/hwmon/aquacomputer_d5next.rst +++ b/Documentation/hwmon/aquacomputer_d5next.rst @@ -9,6 +9,7 @@ Supported devices: * Aquacomputer Farbwerk RGB controller * Aquacomputer Farbwerk 360 RGB controller * Aquacomputer Octo fan controller +* Aquacomputer Quadro fan controller Author: Aleksa Savic @@ -33,6 +34,9 @@ better suited for userspace tools. The Octo exposes four temperature sensors and eight PWM controllable fans, along with their speed (in RPM), power, voltage and current. +The Quadro exposes four temperature sensors, a flow sensor and four PWM controllable +fans, along with their speed (in RPM), power, voltage and current. + The Farbwerk and Farbwerk 360 expose four temperature sensors. Depending on the device, not all sysfs and debugfs entries will be available. @@ -45,13 +49,14 @@ the kernel and supports hotswapping. Sysfs entries ------------- -================ ============================================= +================ ============================================== temp[1-4]_input Temperature sensors (in millidegrees Celsius) -fan[1-2]_input Pump/fan speed (in RPM) -power[1-2]_input Pump/fan power (in micro Watts) -in[0-2]_input Pump/fan voltage (in milli Volts) -curr[1-2]_input Pump/fan current (in milli Amperes) -================ ============================================= +fan[1-8]_input Pump/fan speed (in RPM) / Flow speed (in dL/h) +power[1-8]_input Pump/fan power (in micro Watts) +in[0-7]_input Pump/fan voltage (in milli Volts) +curr[1-8]_input Pump/fan current (in milli Amperes) +pwm[1-8] Fan PWM (0 - 255) +================ ============================================== Debugfs entries --------------- diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index fd2446cf343b..e70d9614bec2 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -257,13 +257,13 @@ config SENSORS_AHT10 will be called aht10. config SENSORS_AQUACOMPUTER_D5NEXT - tristate "Aquacomputer D5 Next, Octo, Farbwerk, and Farbwerk 360" + tristate "Aquacomputer D5 Next, Octo, Quadro, Farbwerk, and Farbwerk 360" depends on USB_HID select CRC16 help If you say yes here you get support for sensors and fans of - the Aquacomputer D5 Next watercooling pump, Octo fan - controller, Farbwerk and Farbwerk 360 RGB controllers, where + the Aquacomputer D5 Next watercooling pump, Octo and Quadro fan + controllers, Farbwerk and Farbwerk 360 RGB controllers, where available. This driver can also be built as a module. If so, the module diff --git a/drivers/hwmon/aquacomputer_d5next.c b/drivers/hwmon/aquacomputer_d5next.c index 738a1df8eae6..66430553cc45 100644 --- a/drivers/hwmon/aquacomputer_d5next.c +++ b/drivers/hwmon/aquacomputer_d5next.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0+ /* - * hwmon driver for Aquacomputer devices (D5 Next, Farbwerk, Farbwerk 360, Octo) + * hwmon driver for Aquacomputer devices (D5 Next, Farbwerk, Farbwerk 360, Octo, + * Quadro) * * Aquacomputer devices send HID reports (with ID 0x01) every second to report * sensor values. @@ -21,17 +22,19 @@ #define USB_VENDOR_ID_AQUACOMPUTER 0x0c70 #define USB_PRODUCT_ID_FARBWERK 0xf00a +#define USB_PRODUCT_ID_QUADRO 0xf00d #define USB_PRODUCT_ID_D5NEXT 0xf00e #define USB_PRODUCT_ID_FARBWERK360 0xf010 #define USB_PRODUCT_ID_OCTO 0xf011 -enum kinds { d5next, farbwerk, farbwerk360, octo }; +enum kinds { d5next, farbwerk, farbwerk360, octo, quadro }; static const char *const aqc_device_names[] = { [d5next] = "d5next", [farbwerk] = "farbwerk", [farbwerk360] = "farbwerk360", - [octo] = "octo" + [octo] = "octo", + [quadro] = "quadro" }; #define DRIVER_NAME "aquacomputer_d5next" @@ -97,6 +100,18 @@ static u8 octo_sensor_fan_offsets[] = { 0x7D, 0x8A, 0x97, 0xA4, 0xB1, 0xBE, 0xCB /* Fan speed registers in Octo control report (from 0-100%) */ static u16 octo_ctrl_fan_offsets[] = { 0x5B, 0xB0, 0x105, 0x15A, 0x1AF, 0x204, 0x259, 0x2AE }; +/* Register offsets for the Quadro fan controller */ +#define QUADRO_POWER_CYCLES 0x18 +#define QUADRO_NUM_FANS 4 +#define QUADRO_NUM_SENSORS 4 +#define QUADRO_SENSOR_START 0x34 +#define QUADRO_CTRL_REPORT_SIZE 0x3c1 +#define QUADRO_FLOW_SENSOR_OFFSET 0x6e +static u8 quadro_sensor_fan_offsets[] = { 0x70, 0x7D, 0x8A, 0x97 }; + +/* Fan speed registers in Quadro control report (from 0-100%) */ +static u16 quadro_ctrl_fan_offsets[] = { 0x36, 0x8b, 0xe0, 0x135 }; + /* Labels for D5 Next */ static const char *const label_d5next_temp[] = { "Coolant temp" @@ -124,7 +139,7 @@ static const char *const label_d5next_current[] = { "Fan current" }; -/* Labels for Farbwerk, Farbwerk 360 and Octo temperature sensors */ +/* Labels for Farbwerk, Farbwerk 360 and Octo and Quadro temperature sensors */ static const char *const label_temp_sensors[] = { "Sensor 1", "Sensor 2", @@ -132,7 +147,7 @@ static const char *const label_temp_sensors[] = { "Sensor 4" }; -/* Labels for Octo */ +/* Labels for Octo and Quadro (except speed) */ static const char *const label_fan_speed[] = { "Fan 1 speed", "Fan 2 speed", @@ -177,6 +192,15 @@ static const char *const label_fan_current[] = { "Fan 8 current" }; +/* Labels for Quadro fan speeds */ +static const char *const label_quadro_speeds[] = { + "Fan 1 speed", + "Fan 2 speed", + "Fan 3 speed", + "Fan 4 speed", + "Flow speed [dL/h]" +}; + struct aqc_data { struct hid_device *hdev; struct device *hwmon_dev; @@ -197,6 +221,7 @@ struct aqc_data { int num_temp_sensors; int temp_sensor_start_offset; u16 power_cycle_count_offset; + u8 flow_sensor_offset; /* General info, same across all devices */ u32 serial_number[2]; @@ -334,6 +359,18 @@ static umode_t aqc_is_visible(const void *data, enum hwmon_sensor_types type, u3 } break; case hwmon_fan: + switch (priv->kind) { + case quadro: + /* Special case to support flow sensor */ + if (channel < priv->num_fans + 1) + return 0444; + break; + default: + if (channel < priv->num_fans) + return 0444; + break; + } + break; case hwmon_power: case hwmon_curr: if (channel < priv->num_fans) @@ -578,6 +615,9 @@ static int aqc_raw_event(struct hid_device *hdev, struct hid_report *report, u8 priv->voltage_input[2] = get_unaligned_be16(data + D5NEXT_5V_VOLTAGE) * 10; priv->voltage_input[3] = get_unaligned_be16(data + D5NEXT_12V_VOLTAGE) * 10; break; + case quadro: + priv->speed_input[4] = get_unaligned_be16(data + priv->flow_sensor_offset); + break; default: break; } @@ -719,6 +759,24 @@ static int aqc_probe(struct hid_device *hdev, const struct hid_device_id *id) priv->voltage_label = label_fan_voltage; priv->current_label = label_fan_current; break; + case USB_PRODUCT_ID_QUADRO: + priv->kind = quadro; + + priv->num_fans = QUADRO_NUM_FANS; + priv->fan_sensor_offsets = quadro_sensor_fan_offsets; + priv->fan_ctrl_offsets = quadro_ctrl_fan_offsets; + priv->num_temp_sensors = QUADRO_NUM_SENSORS; + priv->temp_sensor_start_offset = QUADRO_SENSOR_START; + priv->power_cycle_count_offset = QUADRO_POWER_CYCLES; + priv->buffer_size = QUADRO_CTRL_REPORT_SIZE; + priv->flow_sensor_offset = QUADRO_FLOW_SENSOR_OFFSET; + + priv->temp_label = label_temp_sensors; + priv->speed_label = label_quadro_speeds; + priv->power_label = label_fan_power; + priv->voltage_label = label_fan_voltage; + priv->current_label = label_fan_current; + break; default: break; } @@ -774,6 +832,7 @@ static const struct hid_device_id aqc_table[] = { { HID_USB_DEVICE(USB_VENDOR_ID_AQUACOMPUTER, USB_PRODUCT_ID_FARBWERK) }, { HID_USB_DEVICE(USB_VENDOR_ID_AQUACOMPUTER, USB_PRODUCT_ID_FARBWERK360) }, { HID_USB_DEVICE(USB_VENDOR_ID_AQUACOMPUTER, USB_PRODUCT_ID_OCTO) }, + { HID_USB_DEVICE(USB_VENDOR_ID_AQUACOMPUTER, USB_PRODUCT_ID_QUADRO) }, { } };