From 933b9bc8bb2b9c348fbb2105e461780b8f89953e Mon Sep 17 00:00:00 2001 From: Wensheng Wang Date: Sun, 28 Sep 2025 17:28:44 +0800 Subject: [PATCH 001/110] dt-bindings: hwmon: Add MPS mp2925 and mp2929 Add support for MPS mp2925 and mp2929 controller. Acked-by: Krzysztof Kozlowski Signed-off-by: Wensheng Wang Link: https://lore.kernel.org/r/20250928092845.1394718-1-wenswang@yeah.net Signed-off-by: Guenter Roeck --- Documentation/devicetree/bindings/trivial-devices.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Documentation/devicetree/bindings/trivial-devices.yaml b/Documentation/devicetree/bindings/trivial-devices.yaml index 58ff948d93c9..61116b95b427 100644 --- a/Documentation/devicetree/bindings/trivial-devices.yaml +++ b/Documentation/devicetree/bindings/trivial-devices.yaml @@ -297,6 +297,10 @@ properties: - mps,mp2888 # Monolithic Power Systems Inc. multi-phase controller mp2891 - mps,mp2891 + # Monolithic Power Systems Inc. multi-phase controller mp2925 + - mps,mp2925 + # Monolithic Power Systems Inc. multi-phase controller mp2929 + - mps,mp2929 # Monolithic Power Systems Inc. multi-phase controller mp29502 - mps,mp29502 # Monolithic Power Systems Inc. multi-phase controller mp29608 From a79472e30be48e6693f83d298836880b56afc5a7 Mon Sep 17 00:00:00 2001 From: Wensheng Wang Date: Sun, 28 Sep 2025 17:28:45 +0800 Subject: [PATCH 002/110] hwmon: Add MP2925 and MP2929 driver Add support for MPS VR mp2925 and mp2929 controller. This driver exposes telemetry and limit value readings and writtings. Signed-off-by: Wensheng Wang Link: https://lore.kernel.org/r/20250928092845.1394718-2-wenswang@yeah.net Signed-off-by: Guenter Roeck --- Documentation/hwmon/index.rst | 1 + Documentation/hwmon/mp2925.rst | 151 ++++++++++++++++ MAINTAINERS | 7 + drivers/hwmon/pmbus/Kconfig | 9 + drivers/hwmon/pmbus/Makefile | 1 + drivers/hwmon/pmbus/mp2925.c | 316 +++++++++++++++++++++++++++++++++ 6 files changed, 485 insertions(+) create mode 100644 Documentation/hwmon/mp2925.rst create mode 100644 drivers/hwmon/pmbus/mp2925.c diff --git a/Documentation/hwmon/index.rst b/Documentation/hwmon/index.rst index 51a5bdf75b08..cb3e61ddd311 100644 --- a/Documentation/hwmon/index.rst +++ b/Documentation/hwmon/index.rst @@ -177,6 +177,7 @@ Hardware Monitoring Kernel Drivers mp2869 mp2888 mp2891 + mp2925 mp29502 mp2975 mp2993 diff --git a/Documentation/hwmon/mp2925.rst b/Documentation/hwmon/mp2925.rst new file mode 100644 index 000000000000..63eda215b6cb --- /dev/null +++ b/Documentation/hwmon/mp2925.rst @@ -0,0 +1,151 @@ +.. SPDX-License-Identifier: GPL-2.0 + +Kernel driver mp2925 +==================== + +Supported chips: + + * MPS mp2925 + + Prefix: 'mp2925' + + * MPS mp2929 + + Prefix: 'mp2929' + +Author: + + Wensheng Wang + +Description +----------- + +This driver implements support for Monolithic Power Systems, Inc. (MPS) +MP2925 Dual Loop Digital Multi-phase Controller. + +Device compliant with: + +- PMBus rev 1.3 interface. + +The driver exports the following attributes via the 'sysfs' files +for input voltage: + +**in1_input** + +**in1_label** + +**in1_crit** + +**in1_crit_alarm** + +**in1_lcrit** + +**in1_lcrit_alarm** + +**in1_max** + +**in1_max_alarm** + +**in1_min** + +**in1_min_alarm** + +The driver provides the following attributes for output voltage: + +**in2_input** + +**in2_label** + +**in2_crit** + +**in2_crit_alarm** + +**in2_lcrit** + +**in2_lcrit_alarm** + +**in3_input** + +**in3_label** + +**in3_crit** + +**in3_crit_alarm** + +**in3_lcrit** + +**in3_lcrit_alarm** + +The driver provides the following attributes for input current: + +**curr1_input** + +**curr1_label** + +The driver provides the following attributes for output current: + +**curr2_input** + +**curr2_label** + +**curr2_crit** + +**curr2_crit_alarm** + +**curr2_max** + +**curr2_max_alarm** + +**curr3_input** + +**curr3_label** + +**curr3_crit** + +**curr3_crit_alarm** + +**curr3_max** + +**curr3_max_alarm** + +The driver provides the following attributes for input power: + +**power1_input** + +**power1_label** + +**power2_input** + +**power2_label** + +The driver provides the following attributes for output power: + +**power3_input** + +**power3_label** + +**power4_input** + +**power4_label** + +The driver provides the following attributes for temperature: + +**temp1_input** + +**temp1_crit** + +**temp1_crit_alarm** + +**temp1_max** + +**temp1_max_alarm** + +**temp2_input** + +**temp2_crit** + +**temp2_crit_alarm** + +**temp2_max** + +**temp2_max_alarm** diff --git a/MAINTAINERS b/MAINTAINERS index 46126ce2f968..cb6e7e8322e8 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -17462,6 +17462,13 @@ S: Maintained F: Documentation/hwmon/mp2891.rst F: drivers/hwmon/pmbus/mp2891.c +MPS MP2925 DRIVER +M: Noah Wang +L: linux-hwmon@vger.kernel.org +S: Maintained +F: Documentation/hwmon/mp2925.rst +F: drivers/hwmon/pmbus/mp2925.c + MPS MP29502 DRIVER M: Wensheng Wang L: linux-hwmon@vger.kernel.org diff --git a/drivers/hwmon/pmbus/Kconfig b/drivers/hwmon/pmbus/Kconfig index da04ff6df28b..e03b6271d487 100644 --- a/drivers/hwmon/pmbus/Kconfig +++ b/drivers/hwmon/pmbus/Kconfig @@ -401,6 +401,15 @@ config SENSORS_MP2891 This driver can also be built as a module. If so, the module will be called mp2891. +config SENSORS_MP2925 + tristate "MPS MP2925" + help + If you say yes here you get hardware monitoring support for MPS + MP2925 Dual Loop Digital Multi-Phase Controller. + + This driver can also be built as a module. If so, the module will + be called mp2925. + config SENSORS_MP29502 tristate "MPS MP29502" help diff --git a/drivers/hwmon/pmbus/Makefile b/drivers/hwmon/pmbus/Makefile index 4c5ff3f32c5e..8f8f305b4d4d 100644 --- a/drivers/hwmon/pmbus/Makefile +++ b/drivers/hwmon/pmbus/Makefile @@ -40,6 +40,7 @@ obj-$(CONFIG_SENSORS_MP2856) += mp2856.o obj-$(CONFIG_SENSORS_MP2869) += mp2869.o obj-$(CONFIG_SENSORS_MP2888) += mp2888.o obj-$(CONFIG_SENSORS_MP2891) += mp2891.o +obj-$(CONFIG_SENSORS_MP2925) += mp2925.o obj-$(CONFIG_SENSORS_MP29502) += mp29502.o obj-$(CONFIG_SENSORS_MP2975) += mp2975.o obj-$(CONFIG_SENSORS_MP2993) += mp2993.o diff --git a/drivers/hwmon/pmbus/mp2925.c b/drivers/hwmon/pmbus/mp2925.c new file mode 100644 index 000000000000..6bebd6023021 --- /dev/null +++ b/drivers/hwmon/pmbus/mp2925.c @@ -0,0 +1,316 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Hardware monitoring driver for MPS Multi-phase Digital VR Controllers(MP2925) + */ + +#include +#include +#include +#include +#include "pmbus.h" + +/* + * Vender specific register MFR_VR_MULTI_CONFIG(0x08). + * This register is used to obtain vid scale. + */ +#define MFR_VR_MULTI_CONFIG 0x08 + +#define MP2925_VOUT_DIV 512 +#define MP2925_VOUT_OVUV_UINT 195 +#define MP2925_VOUT_OVUV_DIV 100 + +#define MP2925_PAGE_NUM 2 + +#define MP2925_RAIL1_FUNC (PMBUS_HAVE_VIN | PMBUS_HAVE_PIN | \ + PMBUS_HAVE_VOUT | PMBUS_HAVE_IOUT | \ + PMBUS_HAVE_POUT | PMBUS_HAVE_TEMP | \ + PMBUS_HAVE_STATUS_VOUT | \ + PMBUS_HAVE_STATUS_IOUT | \ + PMBUS_HAVE_STATUS_TEMP | \ + PMBUS_HAVE_STATUS_INPUT) + +#define MP2925_RAIL2_FUNC (PMBUS_HAVE_PIN | PMBUS_HAVE_VOUT | \ + PMBUS_HAVE_IOUT | PMBUS_HAVE_POUT | \ + PMBUS_HAVE_TEMP | PMBUS_HAVE_IIN | \ + PMBUS_HAVE_STATUS_VOUT | \ + PMBUS_HAVE_STATUS_IOUT | \ + PMBUS_HAVE_STATUS_TEMP | \ + PMBUS_HAVE_STATUS_INPUT) + +struct mp2925_data { + struct pmbus_driver_info info; + int vout_scale[MP2925_PAGE_NUM]; +}; + +#define to_mp2925_data(x) container_of(x, struct mp2925_data, info) + +static u16 mp2925_linear_exp_transfer(u16 word, u16 expect_exponent) +{ + s16 exponent, mantissa, target_exponent; + + exponent = ((s16)word) >> 11; + mantissa = ((s16)((word & 0x7ff) << 5)) >> 5; + target_exponent = (s16)((expect_exponent & 0x1f) << 11) >> 11; + + if (exponent > target_exponent) + mantissa = mantissa << (exponent - target_exponent); + else + mantissa = mantissa >> (target_exponent - exponent); + + return (mantissa & 0x7ff) | ((expect_exponent << 11) & 0xf800); +} + +static int mp2925_read_byte_data(struct i2c_client *client, int page, int reg) +{ + int ret; + + switch (reg) { + case PMBUS_VOUT_MODE: + /* + * The MP2925 does not follow standard PMBus protocol completely, + * and the calculation of vout in this driver is based on direct + * format. As a result, the format of vout is enforced to direct. + */ + ret = PB_VOUT_MODE_DIRECT; + break; + default: + ret = -ENODATA; + break; + } + + return ret; +} + +static int mp2925_read_word_data(struct i2c_client *client, int page, int phase, + int reg) +{ + const struct pmbus_driver_info *info = pmbus_get_driver_info(client); + struct mp2925_data *data = to_mp2925_data(info); + int ret; + + switch (reg) { + case PMBUS_READ_VOUT: + ret = pmbus_read_word_data(client, page, phase, reg); + if (ret < 0) + return ret; + + ret = DIV_ROUND_CLOSEST((ret & GENMASK(11, 0)) * data->vout_scale[page], + MP2925_VOUT_DIV); + break; + case PMBUS_VOUT_OV_FAULT_LIMIT: + case PMBUS_VOUT_UV_FAULT_LIMIT: + ret = pmbus_read_word_data(client, page, phase, reg); + if (ret < 0) + return ret; + + ret = DIV_ROUND_CLOSEST((ret & GENMASK(11, 0)) * MP2925_VOUT_OVUV_UINT, + MP2925_VOUT_OVUV_DIV); + break; + case PMBUS_STATUS_WORD: + case PMBUS_READ_VIN: + case PMBUS_READ_IOUT: + case PMBUS_READ_POUT: + case PMBUS_READ_PIN: + case PMBUS_READ_IIN: + case PMBUS_READ_TEMPERATURE_1: + case PMBUS_VIN_OV_FAULT_LIMIT: + case PMBUS_VIN_OV_WARN_LIMIT: + case PMBUS_VIN_UV_WARN_LIMIT: + case PMBUS_VIN_UV_FAULT_LIMIT: + case PMBUS_IOUT_OC_FAULT_LIMIT: + case PMBUS_IOUT_OC_WARN_LIMIT: + case PMBUS_OT_FAULT_LIMIT: + case PMBUS_OT_WARN_LIMIT: + ret = -ENODATA; + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static int mp2925_write_word_data(struct i2c_client *client, int page, int reg, + u16 word) +{ + int ret; + + switch (reg) { + case PMBUS_VIN_OV_FAULT_LIMIT: + case PMBUS_VIN_OV_WARN_LIMIT: + case PMBUS_VIN_UV_WARN_LIMIT: + case PMBUS_VIN_UV_FAULT_LIMIT: + /* + * The PMBUS_VIN_OV_FAULT_LIMIT, PMBUS_VIN_OV_WARN_LIMIT, + * PMBUS_VIN_UV_WARN_LIMIT and PMBUS_VIN_UV_FAULT_LIMIT + * of MP2925 is linear11 format, and the exponent is a + * constant value(5'b11100), so the exponent of word + * parameter should be converted to 5'b11100(0x1C). + */ + ret = pmbus_write_word_data(client, page, reg, + mp2925_linear_exp_transfer(word, 0x1C)); + break; + case PMBUS_VOUT_OV_FAULT_LIMIT: + case PMBUS_VOUT_UV_FAULT_LIMIT: + /* + * The bit0-bit11 is the limit value, and bit12-bit15 + * should not be changed. + */ + ret = pmbus_read_word_data(client, page, 0xff, reg); + if (ret < 0) + return ret; + + ret = pmbus_write_word_data(client, page, reg, + (ret & ~GENMASK(11, 0)) | + FIELD_PREP(GENMASK(11, 0), + DIV_ROUND_CLOSEST(word * MP2925_VOUT_OVUV_DIV, + MP2925_VOUT_OVUV_UINT))); + break; + case PMBUS_OT_FAULT_LIMIT: + case PMBUS_OT_WARN_LIMIT: + /* + * The PMBUS_OT_FAULT_LIMIT and PMBUS_OT_WARN_LIMIT of + * MP2925 is linear11 format, and the exponent is a + * constant value(5'b00000), so the exponent of word + * parameter should be converted to 5'b00000. + */ + ret = pmbus_write_word_data(client, page, reg, + mp2925_linear_exp_transfer(word, 0x00)); + break; + case PMBUS_IOUT_OC_FAULT_LIMIT: + case PMBUS_IOUT_OC_WARN_LIMIT: + /* + * The PMBUS_IOUT_OC_FAULT_LIMIT and PMBUS_IOUT_OC_WARN_LIMIT + * of MP2925 is linear11 format, and the exponent can not be + * changed. + */ + ret = pmbus_read_word_data(client, page, 0xff, reg); + if (ret < 0) + return ret; + + ret = pmbus_write_word_data(client, page, reg, + mp2925_linear_exp_transfer(word, + FIELD_GET(GENMASK(15, 11), + ret))); + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static int +mp2925_identify_vout_scale(struct i2c_client *client, struct pmbus_driver_info *info, + int page) +{ + struct mp2925_data *data = to_mp2925_data(info); + int ret; + + ret = i2c_smbus_write_byte_data(client, PMBUS_PAGE, page); + if (ret < 0) + return ret; + + ret = i2c_smbus_read_byte_data(client, PMBUS_VOUT_MODE); + if (ret < 0) + return ret; + + if (FIELD_GET(GENMASK(5, 5), ret)) { + ret = i2c_smbus_write_byte_data(client, PMBUS_PAGE, + page == 0 ? 3 : 4); + if (ret < 0) + return ret; + + ret = i2c_smbus_read_word_data(client, MFR_VR_MULTI_CONFIG); + if (ret < 0) + return ret; + + if (FIELD_GET(GENMASK(5, 5), ret)) + data->vout_scale[page] = 2560; + else + data->vout_scale[page] = 5120; + } else if (FIELD_GET(GENMASK(4, 4), ret)) { + data->vout_scale[page] = 1; + } else { + data->vout_scale[page] = 512; + } + + return 0; +} + +static int mp2925_identify(struct i2c_client *client, struct pmbus_driver_info *info) +{ + int ret; + + ret = mp2925_identify_vout_scale(client, info, 0); + if (ret < 0) + return ret; + + return mp2925_identify_vout_scale(client, info, 1); +} + +static const struct pmbus_driver_info mp2925_info = { + .pages = MP2925_PAGE_NUM, + .format[PSC_VOLTAGE_IN] = linear, + .format[PSC_CURRENT_IN] = linear, + .format[PSC_CURRENT_OUT] = linear, + .format[PSC_POWER] = linear, + .format[PSC_TEMPERATURE] = linear, + .format[PSC_VOLTAGE_OUT] = direct, + + .m[PSC_VOLTAGE_OUT] = 1, + .R[PSC_VOLTAGE_OUT] = 3, + .b[PSC_VOLTAGE_OUT] = 0, + + .func[0] = MP2925_RAIL1_FUNC, + .func[1] = MP2925_RAIL2_FUNC, + .read_word_data = mp2925_read_word_data, + .read_byte_data = mp2925_read_byte_data, + .write_word_data = mp2925_write_word_data, + .identify = mp2925_identify, +}; + +static int mp2925_probe(struct i2c_client *client) +{ + struct mp2925_data *data; + + data = devm_kzalloc(&client->dev, sizeof(struct mp2925_data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + memcpy(&data->info, &mp2925_info, sizeof(mp2925_info)); + + return pmbus_do_probe(client, &data->info); +} + +static const struct i2c_device_id mp2925_id[] = { + {"mp2925"}, + {"mp2929"}, + {} +}; +MODULE_DEVICE_TABLE(i2c, mp2925_id); + +static const struct of_device_id __maybe_unused mp2925_of_match[] = { + {.compatible = "mps,mp2925"}, + {.compatible = "mps,mp2929"}, + {} +}; +MODULE_DEVICE_TABLE(of, mp2925_of_match); + +static struct i2c_driver mp2925_driver = { + .driver = { + .name = "mp2925", + .of_match_table = mp2925_of_match, + }, + .probe = mp2925_probe, + .id_table = mp2925_id, +}; + +module_i2c_driver(mp2925_driver); + +MODULE_AUTHOR("Wensheng Wang "); +MODULE_DESCRIPTION("PMBus driver for MPS MP2925"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("PMBUS"); From 1288ea95ed172905e36f19ecbafaa85326fa6725 Mon Sep 17 00:00:00 2001 From: Kim Seer Paller Date: Tue, 30 Sep 2025 13:02:20 +0800 Subject: [PATCH 003/110] dt-bindings: hwmon: pmbus: add max17616 Add device tree documentation for MAX17616/MAX17616A current-limiter with overvoltage/surge, undervoltage, reverse polarity, loss of ground protection with PMBus interface. Signed-off-by: Kim Seer Paller Acked-by: Conor Dooley Link: https://lore.kernel.org/r/20250930-upstream-max17616-v1-1-1525a85f126c@analog.com Signed-off-by: Guenter Roeck --- .../bindings/hwmon/pmbus/adi,max17616.yaml | 48 +++++++++++++++++++ MAINTAINERS | 7 +++ 2 files changed, 55 insertions(+) create mode 100644 Documentation/devicetree/bindings/hwmon/pmbus/adi,max17616.yaml diff --git a/Documentation/devicetree/bindings/hwmon/pmbus/adi,max17616.yaml b/Documentation/devicetree/bindings/hwmon/pmbus/adi,max17616.yaml new file mode 100644 index 000000000000..4680d354af0e --- /dev/null +++ b/Documentation/devicetree/bindings/hwmon/pmbus/adi,max17616.yaml @@ -0,0 +1,48 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/hwmon/pmbus/adi,max17616.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Analog Devices MAX17616/MAX17616A Current-Limiter with PMBus Interface + +maintainers: + - Kim Seer Paller + +description: | + The MAX17616/MAX17616A is a 3V to 80V, 7A current-limiter with overvoltage, + surge, undervoltage, reverse polarity, and loss of ground protection. It allows + monitoring of input/output voltage, output current and temperature through the + PMBus serial interface. + Datasheet: + https://www.analog.com/en/products/max17616.html + +properties: + compatible: + const: adi,max17616 + + reg: + maxItems: 1 + + vcc-supply: true + +required: + - compatible + - reg + - vcc-supply + +additionalProperties: false + +examples: + - | + i2c { + #address-cells = <1>; + #size-cells = <0>; + + hwmon@16 { + compatible = "adi,max17616"; + reg = <0x16>; + vcc-supply = <&vcc>; + }; + }; +... diff --git a/MAINTAINERS b/MAINTAINERS index cb6e7e8322e8..bb0f771236cc 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -15165,6 +15165,13 @@ S: Maintained F: Documentation/hwmon/max15301.rst F: drivers/hwmon/pmbus/max15301.c +MAX17616 HARDWARE MONITOR DRIVER +M: Kim Seer Paller +L: linux-hwmon@vger.kernel.org +S: Supported +W: https://ez.analog.com/linux-software-drivers +F: Documentation/devicetree/bindings/hwmon/pmbus/adi,max17616.yaml + MAX2175 SDR TUNER DRIVER M: Ramesh Shanmugasundaram L: linux-media@vger.kernel.org From 77ed12d163c7bfa100bb08c421775d9849f18919 Mon Sep 17 00:00:00 2001 From: Kim Seer Paller Date: Tue, 30 Sep 2025 13:02:21 +0800 Subject: [PATCH 004/110] hwmon: (pmbus/max17616) add driver for max17616 Add support for MAX17616/MAX17616A current-limiter with overvoltage/surge, undervoltage, reverse polarity, loss of ground protection with PMBus interface. The PMBus interface allows monitoring of input/output voltages, output current and temperature. Signed-off-by: Kim Seer Paller Link: https://lore.kernel.org/r/20250930-upstream-max17616-v1-2-1525a85f126c@analog.com [groeck: Fixed htmldocs 'WARNING: Title underline too short' as reported by Kriish Sharma ] Signed-off-by: Guenter Roeck --- Documentation/hwmon/index.rst | 1 + Documentation/hwmon/max17616.rst | 62 +++++++++++++++++++++++++++ MAINTAINERS | 2 + drivers/hwmon/pmbus/Kconfig | 9 ++++ drivers/hwmon/pmbus/Makefile | 1 + drivers/hwmon/pmbus/max17616.c | 73 ++++++++++++++++++++++++++++++++ 6 files changed, 148 insertions(+) create mode 100644 Documentation/hwmon/max17616.rst create mode 100644 drivers/hwmon/pmbus/max17616.c diff --git a/Documentation/hwmon/index.rst b/Documentation/hwmon/index.rst index cb3e61ddd311..bebe0e731be7 100644 --- a/Documentation/hwmon/index.rst +++ b/Documentation/hwmon/index.rst @@ -151,6 +151,7 @@ Hardware Monitoring Kernel Drivers max1619 max16601 max1668 + max17616 max197 max20730 max20751 diff --git a/Documentation/hwmon/max17616.rst b/Documentation/hwmon/max17616.rst new file mode 100644 index 000000000000..a3dc429048ae --- /dev/null +++ b/Documentation/hwmon/max17616.rst @@ -0,0 +1,62 @@ +.. SPDX-License-Identifier: GPL-2.0 + +Kernel driver max17616 +====================== + +Supported chips: + + * Analog Devices MAX17616/MAX17616A + + Prefix: 'max17616' + + Addresses scanned: - + + Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/max17616-max17616a.pdf + +Author: + + - Kim Seer Paller + + +Description +----------- + +This driver supports hardware monitoring for Analog Devices MAX17616/MAX17616A +Current-Limiter with OV/Surge, UV, Reverse Polarity, Loss of Ground Protection +with PMBus Interface. + +The MAX17616/MAX17616A is a 3V to 80V, 7A current-limiter with overvoltage, +surge, undervoltage, reverse polarity, and loss of ground protection. Through +the PMBus interface, the device can monitor input/output voltages, output current +and temperature. + +The driver is a client driver to the core PMBus driver. Please see +Documentation/hwmon/pmbus.rst for details on PMBus client drivers. + +Usage Notes +----------- + +This driver does not auto-detect devices. You will have to instantiate +the devices explicitly. Please see Documentation/i2c/instantiating-devices.rst +for details. + +Platform data support +--------------------- + +The driver supports standard PMBus driver platform data. + +Sysfs entries +------------- + +================= ======================================== +in1_label "vin" +in1_input Measured input voltage +in1_alarm Input voltage alarm +in2_label "vout1" +in2_input Measured output voltage +curr1_label "iout1" +curr1_input Measured output current. +curr1_alarm Output current alarm +temp1_input Measured temperature +temp1_alarm Chip temperature alarm +================= ======================================== diff --git a/MAINTAINERS b/MAINTAINERS index bb0f771236cc..953bf1d29a81 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -15171,6 +15171,8 @@ L: linux-hwmon@vger.kernel.org S: Supported W: https://ez.analog.com/linux-software-drivers F: Documentation/devicetree/bindings/hwmon/pmbus/adi,max17616.yaml +F: Documentation/hwmon/max17616.rst +F: drivers/hwmon/pmbus/max17616.c MAX2175 SDR TUNER DRIVER M: Ramesh Shanmugasundaram diff --git a/drivers/hwmon/pmbus/Kconfig b/drivers/hwmon/pmbus/Kconfig index e03b6271d487..408492d2230f 100644 --- a/drivers/hwmon/pmbus/Kconfig +++ b/drivers/hwmon/pmbus/Kconfig @@ -320,6 +320,15 @@ config SENSORS_MAX16601 This driver can also be built as a module. If so, the module will be called max16601. +config SENSORS_MAX17616 + tristate "Analog Devices MAX17616/MAX17616A" + help + If you say yes here you get hardware monitoring support for Analog + Devices MAX17616/MAX17616A. + + This driver can also be built as a module. If so, the module will + be called max17616. + config SENSORS_MAX20730 tristate "Maxim MAX20710, MAX20730, MAX20734, MAX20743" help diff --git a/drivers/hwmon/pmbus/Makefile b/drivers/hwmon/pmbus/Makefile index 8f8f305b4d4d..43fbc546fb5d 100644 --- a/drivers/hwmon/pmbus/Makefile +++ b/drivers/hwmon/pmbus/Makefile @@ -31,6 +31,7 @@ obj-$(CONFIG_SENSORS_LTC4286) += ltc4286.o obj-$(CONFIG_SENSORS_MAX15301) += max15301.o obj-$(CONFIG_SENSORS_MAX16064) += max16064.o obj-$(CONFIG_SENSORS_MAX16601) += max16601.o +obj-$(CONFIG_SENSORS_MAX17616) += max17616.o obj-$(CONFIG_SENSORS_MAX20730) += max20730.o obj-$(CONFIG_SENSORS_MAX20751) += max20751.o obj-$(CONFIG_SENSORS_MAX31785) += max31785.o diff --git a/drivers/hwmon/pmbus/max17616.c b/drivers/hwmon/pmbus/max17616.c new file mode 100644 index 000000000000..1d4a0ddb95bb --- /dev/null +++ b/drivers/hwmon/pmbus/max17616.c @@ -0,0 +1,73 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Hardware monitoring driver for Analog Devices MAX17616/MAX17616A + * + * Copyright (C) 2025 Analog Devices, Inc. + */ + +#include +#include +#include +#include + +#include "pmbus.h" + +static struct pmbus_driver_info max17616_info = { + .pages = 1, + .format[PSC_VOLTAGE_IN] = direct, + .m[PSC_VOLTAGE_IN] = 512, + .b[PSC_VOLTAGE_IN] = -18, + .R[PSC_VOLTAGE_IN] = -1, + + .format[PSC_VOLTAGE_OUT] = direct, + .m[PSC_VOLTAGE_OUT] = 512, + .b[PSC_VOLTAGE_OUT] = -18, + .R[PSC_VOLTAGE_OUT] = -1, + + .format[PSC_CURRENT_OUT] = direct, + .m[PSC_CURRENT_OUT] = 5845, + .b[PSC_CURRENT_OUT] = 80, + .R[PSC_CURRENT_OUT] = -1, + + .format[PSC_TEMPERATURE] = direct, + .m[PSC_TEMPERATURE] = 71, + .b[PSC_TEMPERATURE] = 19653, + .R[PSC_TEMPERATURE] = -1, + + .func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | PMBUS_HAVE_IOUT | + PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_VOUT | + PMBUS_HAVE_STATUS_IOUT | PMBUS_HAVE_STATUS_INPUT | + PMBUS_HAVE_STATUS_TEMP, +}; + +static int max17616_probe(struct i2c_client *client) +{ + return pmbus_do_probe(client, &max17616_info); +} + +static const struct i2c_device_id max17616_id[] = { + { "max17616" }, + { } +}; +MODULE_DEVICE_TABLE(i2c, max17616_id); + +static const struct of_device_id max17616_of_match[] = { + { .compatible = "adi,max17616" }, + { } +}; +MODULE_DEVICE_TABLE(of, max17616_of_match); + +static struct i2c_driver max17616_driver = { + .driver = { + .name = "max17616", + .of_match_table = max17616_of_match, + }, + .probe = max17616_probe, + .id_table = max17616_id, +}; +module_i2c_driver(max17616_driver); + +MODULE_AUTHOR("Kim Seer Paller "); +MODULE_DESCRIPTION("PMBus driver for Analog Devices MAX17616/MAX17616A"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("PMBUS"); From 8264528aab4f6e8c4ddd3022d9d890368461c23f Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Wed, 1 Oct 2025 13:45:28 +0200 Subject: [PATCH 005/110] dt-bindings: hwmon: ntc-thermistor: Add Murata ncp18wm474 NTC Add Murata ncp18wm474 [1] NTC to the ntc-thermistor binding. [1] https://www.murata.com/en-eu/api/pdfdownloadapi?cate=&partno=NCP18WM474E03RB Signed-off-by: Sascha Hauer Acked-by: Conor Dooley Link: https://lore.kernel.org/r/20251001-ntc-thermistor-ncp18wm474-v1-2-2c64b9b509ff@pengutronix.de Signed-off-by: Guenter Roeck --- Documentation/devicetree/bindings/hwmon/ntc-thermistor.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/hwmon/ntc-thermistor.yaml b/Documentation/devicetree/bindings/hwmon/ntc-thermistor.yaml index b8e500e6cd9f..dc8bc4c6df34 100644 --- a/Documentation/devicetree/bindings/hwmon/ntc-thermistor.yaml +++ b/Documentation/devicetree/bindings/hwmon/ntc-thermistor.yaml @@ -75,6 +75,7 @@ properties: - const: murata,ncp15wl333 - const: murata,ncp03wf104 - const: murata,ncp15xh103 + - const: murata,ncp18wm474 - const: samsung,1404-001221 # Deprecated "ntc," compatible strings - const: ntc,ncp15wb473 From 13e6612fd333f73dd26ee683b5c5c776ebeb5215 Mon Sep 17 00:00:00 2001 From: Emil Dahl Juhl Date: Wed, 1 Oct 2025 13:45:27 +0200 Subject: [PATCH 006/110] hwmon: (ntc-thermistor) Add Murata ncp18wm474 Add support for the Murata NCP18WM474 NTC. Compensation table has been constructed by linear interpolation between well defined points[1] on the resistance vs. temperature graph in the datasheet[2]. The readouts of the graph has been done to the best of my abilities, but the compensation table will be subject to inaccuracies nonetheless. [1] -40, -25, 0, 25, 50, 75, 100, 125 degrees [2] https://www.murata.com/en-eu/api/pdfdownloadapi?cate=&partno=NCP18WM474E03RB Signed-off-by: Emil Dahl Juhl Signed-off-by: Sascha Hauer Link: https://lore.kernel.org/r/20251001-ntc-thermistor-ncp18wm474-v1-1-2c64b9b509ff@pengutronix.de Signed-off-by: Guenter Roeck --- drivers/hwmon/ntc_thermistor.c | 43 ++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/drivers/hwmon/ntc_thermistor.c b/drivers/hwmon/ntc_thermistor.c index d21f7266c411..d6b48178343d 100644 --- a/drivers/hwmon/ntc_thermistor.c +++ b/drivers/hwmon/ntc_thermistor.c @@ -24,6 +24,7 @@ enum ntc_thermistor_type { TYPE_NCPXXWF104, TYPE_NCPXXWL333, TYPE_NCPXXXH103, + TYPE_NCPXXWM474, }; struct ntc_compensation { @@ -46,6 +47,7 @@ enum { NTC_NCP18WB473, NTC_NCP21WB473, NTC_SSG1404001221, + NTC_NCP18WM474, NTC_LAST, }; @@ -60,6 +62,7 @@ static const struct platform_device_id ntc_thermistor_id[] = { [NTC_NCP18WB473] = { "ncp18wb473", TYPE_NCPXXWB473 }, [NTC_NCP21WB473] = { "ncp21wb473", TYPE_NCPXXWB473 }, [NTC_SSG1404001221] = { "ssg1404_001221", TYPE_NCPXXWB473 }, + [NTC_NCP18WM474] = { "ncp18wm474", TYPE_NCPXXWM474 }, [NTC_LAST] = { }, }; MODULE_DEVICE_TABLE(platform, ntc_thermistor_id); @@ -217,6 +220,43 @@ static const struct ntc_compensation ncpXXxh103[] = { { .temp_c = 125, .ohm = 531 }, }; +static const struct ntc_compensation ncpXXwm474[] = { + { .temp_c = -40, .ohm = 10900000 }, + { .temp_c = -35, .ohm = 9600000 }, + { .temp_c = -30, .ohm = 8300000 }, + { .temp_c = -25, .ohm = 7000000 }, + { .temp_c = -20, .ohm = 5980000 }, + { .temp_c = -15, .ohm = 4960000 }, + { .temp_c = -10, .ohm = 3940000 }, + { .temp_c = -5, .ohm = 2920000 }, + { .temp_c = 0, .ohm = 1900000 }, + { .temp_c = 5, .ohm = 1614000 }, + { .temp_c = 10, .ohm = 1328000 }, + { .temp_c = 15, .ohm = 1042000 }, + { .temp_c = 20, .ohm = 756000 }, + { .temp_c = 25, .ohm = 470000 }, + { .temp_c = 30, .ohm = 404000 }, + { .temp_c = 35, .ohm = 338000 }, + { .temp_c = 40, .ohm = 272000 }, + { .temp_c = 45, .ohm = 206000 }, + { .temp_c = 50, .ohm = 140000 }, + { .temp_c = 55, .ohm = 122000 }, + { .temp_c = 60, .ohm = 104000 }, + { .temp_c = 65, .ohm = 86000 }, + { .temp_c = 70, .ohm = 68000 }, + { .temp_c = 75, .ohm = 50000 }, + { .temp_c = 80, .ohm = 44200 }, + { .temp_c = 85, .ohm = 38400 }, + { .temp_c = 90, .ohm = 32600 }, + { .temp_c = 95, .ohm = 26800 }, + { .temp_c = 100, .ohm = 21000 }, + { .temp_c = 105, .ohm = 18600 }, + { .temp_c = 110, .ohm = 16200 }, + { .temp_c = 115, .ohm = 13800 }, + { .temp_c = 120, .ohm = 11400 }, + { .temp_c = 125, .ohm = 9000 }, +}; + /* * The following compensation tables are from the specifications in EPCOS NTC * Thermistors Datasheets @@ -319,6 +359,7 @@ static const struct ntc_type ntc_type[] = { NTC_TYPE(TYPE_NCPXXWF104, ncpXXwf104), NTC_TYPE(TYPE_NCPXXWL333, ncpXXwl333), NTC_TYPE(TYPE_NCPXXXH103, ncpXXxh103), + NTC_TYPE(TYPE_NCPXXWM474, ncpXXwm474), }; /* @@ -675,6 +716,8 @@ static const struct of_device_id ntc_match[] = { .data = &ntc_thermistor_id[NTC_NCP21WB473] }, { .compatible = "samsung,1404-001221", .data = &ntc_thermistor_id[NTC_SSG1404001221] }, + { .compatible = "murata,ncp18wm474", + .data = &ntc_thermistor_id[NTC_NCP18WM474] }, /* Usage of vendor name "ntc" is deprecated */ { .compatible = "ntc,ncp03wb473", From 1a254111998c417abd3cc783f690bddc8c7aedac Mon Sep 17 00:00:00 2001 From: Eddie James Date: Wed, 1 Oct 2025 09:44:38 -0500 Subject: [PATCH 007/110] dt-bindings: hwmon: Move max31785 compatibles to max31790 document The binding for the max31785 is the same as the max31790, so just add some compatible strings for the max31785 chip. Signed-off-by: Eddie James Reviewed-by: Rob Herring (Arm) Link: https://lore.kernel.org/r/20251001144441.310950-5-eajames@linux.ibm.com Signed-off-by: Guenter Roeck --- .../devicetree/bindings/hwmon/max31785.txt | 22 ------------------- .../bindings/hwmon/maxim,max31790.yaml | 6 ++++- 2 files changed, 5 insertions(+), 23 deletions(-) delete mode 100644 Documentation/devicetree/bindings/hwmon/max31785.txt diff --git a/Documentation/devicetree/bindings/hwmon/max31785.txt b/Documentation/devicetree/bindings/hwmon/max31785.txt deleted file mode 100644 index 106e08c56aaa..000000000000 --- a/Documentation/devicetree/bindings/hwmon/max31785.txt +++ /dev/null @@ -1,22 +0,0 @@ -Bindings for the Maxim MAX31785 Intelligent Fan Controller -========================================================== - -Reference: - -https://datasheets.maximintegrated.com/en/ds/MAX31785.pdf - -The Maxim MAX31785 is a PMBus device providing closed-loop, multi-channel fan -management with temperature and remote voltage sensing. Various fan control -features are provided, including PWM frequency control, temperature hysteresis, -dual tachometer measurements, and fan health monitoring. - -Required properties: -- compatible : One of "maxim,max31785" or "maxim,max31785a" -- reg : I2C address, one of 0x52, 0x53, 0x54, 0x55. - -Example: - - fans@52 { - compatible = "maxim,max31785"; - reg = <0x52>; - }; diff --git a/Documentation/devicetree/bindings/hwmon/maxim,max31790.yaml b/Documentation/devicetree/bindings/hwmon/maxim,max31790.yaml index b1ff496f87f9..b2dc813b1ab4 100644 --- a/Documentation/devicetree/bindings/hwmon/maxim,max31790.yaml +++ b/Documentation/devicetree/bindings/hwmon/maxim,max31790.yaml @@ -20,7 +20,11 @@ description: > properties: compatible: - const: maxim,max31790 + enum: + - maxim,max31785 + - maxim,max31785a + - maxim,max31785b + - maxim,max31790 reg: maxItems: 1 From f1ba3c5f559bb35758e30a4cba3d5b53cbf327b4 Mon Sep 17 00:00:00 2001 From: Eddie James Date: Wed, 1 Oct 2025 09:44:39 -0500 Subject: [PATCH 008/110] dt-bindings: hwmon: max31790: Use addressed fan nodes Since fan properties can include reg, the fan controller should be able to specify address-cells and size-cells properties and use an addressed fan child node. Signed-off-by: Eddie James Reviewed-by: Rob Herring (Arm) Link: https://lore.kernel.org/r/20251001144441.310950-6-eajames@linux.ibm.com Signed-off-by: Guenter Roeck --- .../bindings/hwmon/maxim,max31790.yaml | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/Documentation/devicetree/bindings/hwmon/maxim,max31790.yaml b/Documentation/devicetree/bindings/hwmon/maxim,max31790.yaml index b2dc813b1ab4..558cbd251b0f 100644 --- a/Documentation/devicetree/bindings/hwmon/maxim,max31790.yaml +++ b/Documentation/devicetree/bindings/hwmon/maxim,max31790.yaml @@ -35,11 +35,17 @@ properties: resets: maxItems: 1 + '#address-cells': + const: 1 + + '#size-cells': + const: 0 + "#pwm-cells": const: 1 patternProperties: - "^fan-[0-9]+$": + "^fan@[0-9]+$": $ref: fan-common.yaml# unevaluatedProperties: false @@ -60,13 +66,17 @@ examples: reg = <0x20>; clocks = <&sys_clk>; resets = <&reset 0>; + #address-cells = <1>; #pwm-cells = <1>; + #size-cells = <0>; - fan-0 { + fan@0 { + reg = <0x0>; pwms = <&pwm_provider 1>; }; - fan-1 { + fan@1 { + reg = <0x1>; pwms = <&pwm_provider 2>; }; }; From 03897f9baf3eeea509b2c749bab435072f5e637a Mon Sep 17 00:00:00 2001 From: Maximilian Luz Date: Thu, 2 Oct 2025 20:49:56 +0200 Subject: [PATCH 009/110] hwmon: (nct6775) Add ASUS ROG STRIX X870E-H GAMING WIFI7 The ASUS ROG STRIX X870E-H GAMING WIFI7 has a NCT6799D compatible chip, which is also accessed via ACPI. Signed-off-by: Maximilian Luz Link: https://lore.kernel.org/r/20251002184958.359744-1-luzmaximilian@gmail.com Signed-off-by: Guenter Roeck --- drivers/hwmon/nct6775-platform.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/hwmon/nct6775-platform.c b/drivers/hwmon/nct6775-platform.c index 407945d2cd6a..c3a719aef1ac 100644 --- a/drivers/hwmon/nct6775-platform.c +++ b/drivers/hwmon/nct6775-platform.c @@ -1403,6 +1403,7 @@ static const char * const asus_msi_boards[] = { "ROG STRIX X670E-E GAMING WIFI", "ROG STRIX X670E-F GAMING WIFI", "ROG STRIX X670E-I GAMING WIFI", + "ROG STRIX X870E-H GAMING WIFI7", "ROG STRIX Z590-A GAMING WIFI", "ROG STRIX Z590-A GAMING WIFI II", "ROG STRIX Z590-E GAMING WIFI", From 2e0b52f1ae88ded4115b195f8e00e28121b26c06 Mon Sep 17 00:00:00 2001 From: Alexis Czezar Torreno Date: Wed, 1 Oct 2025 08:37:08 +0800 Subject: [PATCH 010/110] hwmon: (pmbus/max34440): add support adpm12200 ADPM12200 is a quarter brick DC/DC Power Module. It is a high power non-isolated converter capable of delivering regulated 12V with continuous power level of 2000W. Uses PMBus. Signed-off-by: Alexis Czezar Torreno Link: https://lore.kernel.org/r/20251001-hwmon-next-v1-2-f8ca6a648203@analog.com Signed-off-by: Guenter Roeck --- Documentation/hwmon/max34440.rst | 25 +++++++++++++----- drivers/hwmon/pmbus/Kconfig | 1 + drivers/hwmon/pmbus/max34440.c | 44 +++++++++++++++++++++++++++++--- 3 files changed, 60 insertions(+), 10 deletions(-) diff --git a/Documentation/hwmon/max34440.rst b/Documentation/hwmon/max34440.rst index 8591a7152ce5..34a6e0e1b963 100644 --- a/Documentation/hwmon/max34440.rst +++ b/Documentation/hwmon/max34440.rst @@ -11,6 +11,14 @@ Supported chips: Datasheet: - + * ADI ADPM12200 + + Prefixes: 'adpm12200' + + Addresses scanned: - + + Datasheet: - + * Maxim MAX34440 Prefixes: 'max34440' @@ -79,10 +87,11 @@ This driver supports multiple devices: hardware monitoring for Maxim MAX34440 PMBus 6-Channel Power-Supply Manager, MAX34441 PMBus 5-Channel Power-Supply Manager and Intelligent Fan Controller, and MAX34446 PMBus Power-Supply Data Logger; PMBus Voltage Monitor and Sequencers for MAX34451, MAX34460, and -MAX34461; PMBus DC/DC Power Module ADPM12160. The MAX34451 supports monitoring -voltage or current of 12 channels based on GIN pins. The MAX34460 supports 12 -voltage channels, and the MAX34461 supports 16 voltage channels. The ADPM1260 -also monitors both input and output of voltage and current. +MAX34461; PMBus DC/DC Power Module ADPM12160, and ADPM12200. The MAX34451 +supports monitoring voltage or current of 12 channels based on GIN pins. The +MAX34460 supports 12 voltage channels, and the MAX34461 supports 16 voltage +channels. The ADPM12160, and ADPM12200 also monitors both input and output +of voltage and current. The driver is a client driver to the core PMBus driver. Please see Documentation/hwmon/pmbus.rst for details on PMBus client drivers. @@ -140,7 +149,8 @@ in[1-6]_reset_history Write any value to reset history. .. note:: - MAX34446 only supports in[1-4]. - - ADPM12160 only supports in[1-2]. Label is "vin1" and "vout1" respectively. + - ADPM12160, and ADPM12200 only supports in[1-2]. Label is "vin1" + and "vout1" respectively. Curr ~~~~ @@ -162,7 +172,8 @@ curr[1-6]_reset_history Write any value to reset history. - in6 and curr6 attributes only exist for MAX34440. - MAX34446 only supports curr[1-4]. - - For ADPM12160, curr[1] is "iin1" and curr[2-6] are "iout[1-5]. + - For ADPM12160, and ADPM12200, curr[1] is "iin1" and curr[2-6] + are "iout[1-5]". Power ~~~~~ @@ -198,7 +209,7 @@ temp[1-8]_reset_history Write any value to reset history. .. note:: - temp7 and temp8 attributes only exist for MAX34440. - MAX34446 only supports temp[1-3]. - - ADPM12160 only supports temp[1]. + - ADPM12160, and ADPM12200 only supports temp[1]. .. note:: diff --git a/drivers/hwmon/pmbus/Kconfig b/drivers/hwmon/pmbus/Kconfig index 408492d2230f..fdcef5dfa5a8 100644 --- a/drivers/hwmon/pmbus/Kconfig +++ b/drivers/hwmon/pmbus/Kconfig @@ -361,6 +361,7 @@ config SENSORS_MAX34440 help If you say yes here you get hardware monitoring support for Maxim MAX34440, MAX34441, MAX34446, MAX34451, MAX34460, and MAX34461. + Other compatible are ADPM12160, and ADPM12200. This driver can also be built as a module. If so, the module will be called max34440. diff --git a/drivers/hwmon/pmbus/max34440.c b/drivers/hwmon/pmbus/max34440.c index 56834d26f8ef..70d644fe14d3 100644 --- a/drivers/hwmon/pmbus/max34440.c +++ b/drivers/hwmon/pmbus/max34440.c @@ -17,6 +17,7 @@ enum chips { adpm12160, + adpm12200, max34440, max34441, max34446, @@ -98,7 +99,7 @@ static int max34440_read_word_data(struct i2c_client *client, int page, break; case PMBUS_VIRT_READ_IOUT_AVG: if (data->id != max34446 && data->id != max34451 && - data->id != adpm12160) + data->id != adpm12160 && data->id != adpm12200) return -ENXIO; ret = pmbus_read_word_data(client, page, phase, MAX34446_MFR_IOUT_AVG); @@ -183,7 +184,7 @@ static int max34440_write_word_data(struct i2c_client *client, int page, ret = pmbus_write_word_data(client, page, MAX34440_MFR_IOUT_PEAK, 0); if (!ret && (data->id == max34446 || data->id == max34451 || - data->id == adpm12160)) + data->id == adpm12160 || data->id == adpm12200)) ret = pmbus_write_word_data(client, page, MAX34446_MFR_IOUT_AVG, 0); @@ -364,6 +365,42 @@ static struct pmbus_driver_info max34440_info[] = { .read_word_data = max34440_read_word_data, .write_word_data = max34440_write_word_data, }, + [adpm12200] = { + .pages = 19, + .format[PSC_VOLTAGE_IN] = direct, + .format[PSC_VOLTAGE_OUT] = direct, + .format[PSC_CURRENT_IN] = direct, + .format[PSC_CURRENT_OUT] = direct, + .format[PSC_TEMPERATURE] = direct, + .m[PSC_VOLTAGE_IN] = 125, + .b[PSC_VOLTAGE_IN] = 0, + .R[PSC_VOLTAGE_IN] = 0, + .m[PSC_VOLTAGE_OUT] = 125, + .b[PSC_VOLTAGE_OUT] = 0, + .R[PSC_VOLTAGE_OUT] = 0, + .m[PSC_CURRENT_IN] = 250, + .b[PSC_CURRENT_IN] = 0, + .R[PSC_CURRENT_IN] = -1, + .m[PSC_CURRENT_OUT] = 250, + .b[PSC_CURRENT_OUT] = 0, + .R[PSC_CURRENT_OUT] = -1, + .m[PSC_TEMPERATURE] = 1, + .b[PSC_TEMPERATURE] = 0, + .R[PSC_TEMPERATURE] = 2, + /* absent func below [18] are not for monitoring */ + .func[2] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT, + .func[4] = PMBUS_HAVE_STATUS_IOUT, + .func[5] = PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT, + .func[6] = PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT, + .func[7] = PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT, + .func[8] = PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT, + .func[9] = PMBUS_HAVE_VIN | PMBUS_HAVE_STATUS_INPUT, + .func[10] = PMBUS_HAVE_IIN | PMBUS_HAVE_STATUS_INPUT, + .func[14] = PMBUS_HAVE_IOUT, + .func[18] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP, + .read_word_data = max34440_read_word_data, + .write_word_data = max34440_write_word_data, + }, [max34440] = { .pages = 14, .format[PSC_VOLTAGE_IN] = direct, @@ -600,7 +637,7 @@ static int max34440_probe(struct i2c_client *client) rv = max34451_set_supported_funcs(client, data); if (rv) return rv; - } else if (data->id == adpm12160) { + } else if (data->id == adpm12160 || data->id == adpm12200) { data->iout_oc_fault_limit = PMBUS_IOUT_OC_FAULT_LIMIT; data->iout_oc_warn_limit = PMBUS_IOUT_OC_WARN_LIMIT; } @@ -610,6 +647,7 @@ static int max34440_probe(struct i2c_client *client) static const struct i2c_device_id max34440_id[] = { {"adpm12160", adpm12160}, + {"adpm12200", adpm12200}, {"max34440", max34440}, {"max34441", max34441}, {"max34446", max34446}, From 4bb9cf5d413137924527123c0754144033728d1a Mon Sep 17 00:00:00 2001 From: Paul Heneghan Date: Fri, 3 Oct 2025 10:07:56 +0200 Subject: [PATCH 011/110] hwmon: (asus-ec-sensors) add Pro WS TRX50-SAGE WIFI Added support for Pro WS TRX50-SAGE WIFI motherboard. Signed-off-by: Paul Heneghan Signed-off-by: Eugene Shalygin Link: https://lore.kernel.org/r/20251003081002.1013313-2-eugene.shalygin@gmail.com Signed-off-by: Guenter Roeck --- Documentation/hwmon/asus_ec_sensors.rst | 1 + drivers/hwmon/asus-ec-sensors.c | 22 ++++++++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/Documentation/hwmon/asus_ec_sensors.rst b/Documentation/hwmon/asus_ec_sensors.rst index a5a58c00c322..ab0f255d9653 100644 --- a/Documentation/hwmon/asus_ec_sensors.rst +++ b/Documentation/hwmon/asus_ec_sensors.rst @@ -9,6 +9,7 @@ Supported boards: * PRIME X570-PRO * PRIME X670E-PRO WIFI * PRIME Z270-A + * Pro WS TRX50-SAGE WIFI * Pro WS X570-ACE * Pro WS WRX90E-SAGE SE * ProArt X570-CREATOR WIFI diff --git a/drivers/hwmon/asus-ec-sensors.c b/drivers/hwmon/asus-ec-sensors.c index 34a8f6b834c9..d969c5dc4b0f 100644 --- a/drivers/hwmon/asus-ec-sensors.c +++ b/drivers/hwmon/asus-ec-sensors.c @@ -182,6 +182,7 @@ enum board_family { family_amd_500_series, family_amd_600_series, family_amd_800_series, + family_amd_trx_50, family_amd_wrx_90, family_intel_200_series, family_intel_300_series, @@ -294,6 +295,15 @@ static const struct ec_sensor_info sensors_family_amd_800[] = { EC_SENSOR("CPU_Opt", hwmon_fan, 2, 0x00, 0xb0), }; +static const struct ec_sensor_info sensors_family_amd_trx_50[] = { + [ec_sensor_fan_vrmw_hs] = + EC_SENSOR("VRMW HS", hwmon_fan, 2, 0x00, 0xb4), + [ec_sensor_fan_vrme_hs] = + EC_SENSOR("VRME HS", hwmon_fan, 2, 0x00, 0xbc), + [ec_sensor_temp_t_sensor] = + EC_SENSOR("T_Sensor", hwmon_temp, 1, 0x01, 0x04), +}; + static const struct ec_sensor_info sensors_family_amd_wrx_90[] = { [ec_sensor_temp_cpu_package] = EC_SENSOR("CPU Package", hwmon_temp, 1, 0x00, 0x31), @@ -533,6 +543,13 @@ static const struct ec_board_info board_info_pro_art_x870E_creator_wifi = { .family = family_amd_800_series, }; +static const struct ec_board_info board_info_pro_ws_trx50_sage_wifi = { + /* Board also has a nct6798 */ + .sensors = SENSOR_TEMP_T_SENSOR | SENSOR_FAN_VRME_HS | SENSOR_FAN_VRMW_HS, + .mutex_path = ASUS_HW_ACCESS_MUTEX_RMTW_ASMX, + .family = family_amd_trx_50, +}; + static const struct ec_board_info board_info_pro_ws_wrx90e_sage_se = { /* Board also has a nct6798 with 7 more fans and temperatures */ .sensors = SENSOR_TEMP_CPU_PACKAGE | SENSOR_TEMP_T_SENSOR | @@ -739,6 +756,8 @@ static const struct dmi_system_id dmi_table[] = { &board_info_pro_art_x670E_creator_wifi), DMI_EXACT_MATCH_ASUS_BOARD_NAME("ProArt X870E-CREATOR WIFI", &board_info_pro_art_x870E_creator_wifi), + DMI_EXACT_MATCH_ASUS_BOARD_NAME("Pro WS TRX50-SAGE WIFI", + &board_info_pro_ws_trx50_sage_wifi), DMI_EXACT_MATCH_ASUS_BOARD_NAME("Pro WS WRX90E-SAGE SE", &board_info_pro_ws_wrx90e_sage_se), DMI_EXACT_MATCH_ASUS_BOARD_NAME("Pro WS X570-ACE", @@ -1274,6 +1293,9 @@ static int asus_ec_probe(struct platform_device *pdev) case family_amd_800_series: ec_data->sensors_info = sensors_family_amd_800; break; + case family_amd_trx_50: + ec_data->sensors_info = sensors_family_amd_trx_50; + break; case family_amd_wrx_90: ec_data->sensors_info = sensors_family_amd_wrx_90; break; From cbb213e07c01784ce1f41ba04995da3808cc5a47 Mon Sep 17 00:00:00 2001 From: Maximilian Luz Date: Fri, 3 Oct 2025 10:07:57 +0200 Subject: [PATCH 012/110] hwmon: (asus-ec-sensors) add ROG STRIX X870E-H GAMING WIFI7 The board has a similar sensor configuration as the ROG STRIX X870E-E GAMING WIFI, with an additional temperature sensor header. Signed-off-by: Maximilian Luz Signed-off-by: Eugene Shalygin Link: https://lore.kernel.org/r/20251003081002.1013313-3-eugene.shalygin@gmail.com Signed-off-by: Guenter Roeck --- Documentation/hwmon/asus_ec_sensors.rst | 1 + drivers/hwmon/asus-ec-sensors.c | 10 ++++++++++ 2 files changed, 11 insertions(+) diff --git a/Documentation/hwmon/asus_ec_sensors.rst b/Documentation/hwmon/asus_ec_sensors.rst index ab0f255d9653..2666ed3a8ea3 100644 --- a/Documentation/hwmon/asus_ec_sensors.rst +++ b/Documentation/hwmon/asus_ec_sensors.rst @@ -38,6 +38,7 @@ Supported boards: * ROG STRIX X670E-I GAMING WIFI * ROG STRIX X870-I GAMING WIFI * ROG STRIX X870E-E GAMING WIFI + * ROG STRIX X870E-H GAMING WIFI7 * ROG STRIX Z390-F GAMING * ROG STRIX Z490-F GAMING * ROG STRIX Z690-A GAMING WIFI D4 diff --git a/drivers/hwmon/asus-ec-sensors.c b/drivers/hwmon/asus-ec-sensors.c index d969c5dc4b0f..7f35eade3a9b 100644 --- a/drivers/hwmon/asus-ec-sensors.c +++ b/drivers/hwmon/asus-ec-sensors.c @@ -660,6 +660,14 @@ static const struct ec_board_info board_info_strix_x870e_e_gaming_wifi = { .family = family_amd_800_series, }; +static const struct ec_board_info board_info_strix_x870e_h_gaming_wifi7 = { + .sensors = SENSOR_TEMP_CPU | SENSOR_TEMP_CPU_PACKAGE | + SENSOR_TEMP_MB | SENSOR_TEMP_VRM | SENSOR_TEMP_T_SENSOR | + SENSOR_FAN_CPU_OPT, + .mutex_path = ASUS_HW_ACCESS_MUTEX_SB_PCI0_SBRG_SIO1_MUT0, + .family = family_amd_800_series, +}; + static const struct ec_board_info board_info_strix_z390_f_gaming = { .sensors = SENSOR_TEMP_CHIPSET | SENSOR_TEMP_VRM | SENSOR_TEMP_T_SENSOR | @@ -806,6 +814,8 @@ static const struct dmi_system_id dmi_table[] = { &board_info_strix_x870_i_gaming_wifi), DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX X870E-E GAMING WIFI", &board_info_strix_x870e_e_gaming_wifi), + DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX X870E-H GAMING WIFI7", + &board_info_strix_x870e_h_gaming_wifi7), DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX Z390-F GAMING", &board_info_strix_z390_f_gaming), DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX Z490-F GAMING", From 906f25050add51f1a412ea37e618d8748f75e23a Mon Sep 17 00:00:00 2001 From: Ivan Mikhaylov Date: Tue, 7 Oct 2025 00:53:19 +0300 Subject: [PATCH 013/110] peci: cpu: add Intel Emerald Rapids support Add support for detection of Intel Emerald Rapids processor based on CPU model. Emerald Rapids Xeon processors with the model set to INTEL_EMERALDRAPIDS_X. The data field for this entry is "emr". Tested the patch series with AST2600 BMC with 5S Intel Emerald Rapids processors & verified by reading cpu & dimm temperature which matches host sensor values from lmsensors. Signed-off-by: Ivan Mikhaylov Reviewed-by: Paul Menzel Link: https://lore.kernel.org/r/20251006215321.5036-2-fr0st61te@gmail.com Signed-off-by: Guenter Roeck --- drivers/peci/cpu.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/peci/cpu.c b/drivers/peci/cpu.c index 2dac8ba82787..fbccc1d1b637 100644 --- a/drivers/peci/cpu.c +++ b/drivers/peci/cpu.c @@ -321,6 +321,10 @@ static const struct peci_device_id peci_cpu_device_ids[] = { .x86_vfm = INTEL_SAPPHIRERAPIDS_X, .data = "spr", }, + { /* Emerald Rapids Xeon */ + .x86_vfm = INTEL_EMERALDRAPIDS_X, + .data = "emr", + }, { } }; MODULE_DEVICE_TABLE(peci, peci_cpu_device_ids); From 03c5ecc276fdc696ec469ee3a784726b809ecf26 Mon Sep 17 00:00:00 2001 From: Ivan Mikhaylov Date: Tue, 7 Oct 2025 00:53:20 +0300 Subject: [PATCH 014/110] hwmon: (peci/dimmtemp) add Intel Emerald Rapids platform support Extend the functionality of hwmon (peci/dimmtemp) for Emerald Rapids platform. The patch has been tested on a 5S system with 16 DIMMs installed. Verified read of DIMM temperature thresholds & temperature. Using Sapphire's callbacks about getting thresholds because it's same platform/socket. Signed-off-by: Ivan Mikhaylov Reviewed-by: Paul Menzel Link: https://lore.kernel.org/r/20251006215321.5036-3-fr0st61te@gmail.com Signed-off-by: Guenter Roeck --- drivers/hwmon/peci/dimmtemp.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/drivers/hwmon/peci/dimmtemp.c b/drivers/hwmon/peci/dimmtemp.c index fbe82d9852e0..a281476c7a31 100644 --- a/drivers/hwmon/peci/dimmtemp.c +++ b/drivers/hwmon/peci/dimmtemp.c @@ -32,6 +32,8 @@ #define DIMM_IDX_MAX_ON_ICXD 2 #define CHAN_RANK_MAX_ON_SPR 8 #define DIMM_IDX_MAX_ON_SPR 2 +#define CHAN_RANK_MAX_ON_EMR 8 +#define DIMM_IDX_MAX_ON_EMR 2 #define CHAN_RANK_MAX CHAN_RANK_MAX_ON_HSX #define DIMM_IDX_MAX DIMM_IDX_MAX_ON_HSX @@ -571,6 +573,12 @@ read_thresholds_spr(struct peci_dimmtemp *priv, int dimm_order, int chan_rank, u return 0; } +static int read_thresholds_emr(struct peci_dimmtemp *priv, int dimm_order, + int chan_rank, u32 *data) +{ + return read_thresholds_spr(priv, dimm_order, chan_rank, data); +} + static const struct dimm_info dimm_hsx = { .chan_rank_max = CHAN_RANK_MAX_ON_HSX, .dimm_idx_max = DIMM_IDX_MAX_ON_HSX, @@ -620,6 +628,13 @@ static const struct dimm_info dimm_spr = { .read_thresholds = &read_thresholds_spr, }; +static const struct dimm_info dimm_emr = { + .chan_rank_max = CHAN_RANK_MAX_ON_EMR, + .dimm_idx_max = DIMM_IDX_MAX_ON_EMR, + .min_peci_revision = 0x40, + .read_thresholds = &read_thresholds_emr, +}; + static const struct auxiliary_device_id peci_dimmtemp_ids[] = { { .name = "peci_cpu.dimmtemp.hsx", @@ -649,6 +664,10 @@ static const struct auxiliary_device_id peci_dimmtemp_ids[] = { .name = "peci_cpu.dimmtemp.spr", .driver_data = (kernel_ulong_t)&dimm_spr, }, + { + .name = "peci_cpu.dimmtemp.emr", + .driver_data = (kernel_ulong_t)&dimm_emr, + }, { } }; MODULE_DEVICE_TABLE(auxiliary, peci_dimmtemp_ids); From a45b3ae40451542e3d6b37b8fba04e280cc8efa7 Mon Sep 17 00:00:00 2001 From: Ivan Mikhaylov Date: Tue, 7 Oct 2025 00:53:21 +0300 Subject: [PATCH 015/110] hwmon: (peci/cputemp) add Intel Emerald Rapids support Add support to read DTS for reading Intel Emerald Rapids platform. Signed-off-by: Ivan Mikhaylov Reviewed-by: Paul Menzel Link: https://lore.kernel.org/r/20251006215321.5036-4-fr0st61te@gmail.com Signed-off-by: Guenter Roeck --- drivers/hwmon/peci/cputemp.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/drivers/hwmon/peci/cputemp.c b/drivers/hwmon/peci/cputemp.c index c7112dbf008b..b350c9a76894 100644 --- a/drivers/hwmon/peci/cputemp.c +++ b/drivers/hwmon/peci/cputemp.c @@ -364,6 +364,7 @@ static int init_core_mask(struct peci_cputemp *priv) case INTEL_ICELAKE_X: case INTEL_ICELAKE_D: case INTEL_SAPPHIRERAPIDS_X: + case INTEL_EMERALDRAPIDS_X: ret = peci_ep_pci_local_read(peci_dev, 0, reg->bus, reg->dev, reg->func, reg->offset + 4, &data); if (ret) @@ -539,6 +540,13 @@ static struct resolved_cores_reg resolved_cores_reg_spr = { .offset = 0x80, }; +static struct resolved_cores_reg resolved_cores_reg_emr = { + .bus = 31, + .dev = 30, + .func = 6, + .offset = 0x80, +}; + static const struct cpu_info cpu_hsx = { .reg = &resolved_cores_reg_hsx, .min_peci_revision = 0x33, @@ -563,6 +571,12 @@ static const struct cpu_info cpu_spr = { .thermal_margin_to_millidegree = &dts_ten_dot_six_to_millidegree, }; +static const struct cpu_info cpu_emr = { + .reg = &resolved_cores_reg_emr, + .min_peci_revision = 0x40, + .thermal_margin_to_millidegree = &dts_ten_dot_six_to_millidegree, +}; + static const struct auxiliary_device_id peci_cputemp_ids[] = { { .name = "peci_cpu.cputemp.hsx", @@ -592,6 +606,10 @@ static const struct auxiliary_device_id peci_cputemp_ids[] = { .name = "peci_cpu.cputemp.spr", .driver_data = (kernel_ulong_t)&cpu_spr, }, + { + .name = "peci_cpu.cputemp.emr", + .driver_data = (kernel_ulong_t)&cpu_emr, + }, { } }; MODULE_DEVICE_TABLE(auxiliary, peci_cputemp_ids); From 2272f61d7241f8dd8c44d5c25ef5e67e61062111 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sun, 12 Oct 2025 14:16:24 -0700 Subject: [PATCH 016/110] hwmon: (ltc2947) Use the energy64 attribute type to report the energy MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use the energy64 attribute type instead of a locally defined sysfs attribute to report the accumulated energy. Signed-off-by: Guenter Roeck Reviewed-by: Nuno Sá Link: https://lore.kernel.org/r/20251012211625.533791-1-linux@roeck-us.net Signed-off-by: Guenter Roeck --- drivers/hwmon/ltc2947-core.c | 60 ++++++++++++++++-------------------- 1 file changed, 26 insertions(+), 34 deletions(-) diff --git a/drivers/hwmon/ltc2947-core.c b/drivers/hwmon/ltc2947-core.c index 244839167e51..90f70f732b41 100644 --- a/drivers/hwmon/ltc2947-core.c +++ b/drivers/hwmon/ltc2947-core.c @@ -9,8 +9,8 @@ #include #include #include -#include #include +#include #include #include #include @@ -319,24 +319,6 @@ unlock: return ret; } -static ssize_t ltc2947_show_value(struct device *dev, - struct device_attribute *da, char *buf) -{ - struct ltc2947_data *st = dev_get_drvdata(dev); - struct sensor_device_attribute *attr = to_sensor_dev_attr(da); - int ret; - s64 val = 0; - - ret = ltc2947_val_read(st, attr->index, LTC2947_PAGE0, 6, &val); - if (ret) - return ret; - - /* value in microJoule. st->lsb_energy was multiplied by 10E9 */ - val = div_s64(val * st->lsb_energy, 1000); - - return sprintf(buf, "%lld\n", val); -} - static int ltc2947_read_temp(struct device *dev, const u32 attr, long *val, const int channel) { @@ -588,6 +570,23 @@ static int ltc2947_read_in(struct device *dev, const u32 attr, long *val, return 0; } +static int ltc2947_read_energy(struct device *dev, s64 *val, const int channel) +{ + int reg = channel ? LTC2947_REG_ENERGY2 : LTC2947_REG_ENERGY1; + struct ltc2947_data *st = dev_get_drvdata(dev); + s64 __val = 0; + int ret; + + ret = ltc2947_val_read(st, reg, LTC2947_PAGE0, 6, &__val); + if (ret) + return ret; + + /* value in microJoule. st->lsb_energy was multiplied by 10E9 */ + *val = DIV_S64_ROUND_CLOSEST(__val * st->lsb_energy, 1000); + + return 0; +} + static int ltc2947_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel, long *val) { @@ -600,6 +599,8 @@ static int ltc2947_read(struct device *dev, enum hwmon_sensor_types type, return ltc2947_read_power(dev, attr, val); case hwmon_temp: return ltc2947_read_temp(dev, attr, val, channel); + case hwmon_energy64: + return ltc2947_read_energy(dev, (s64 *)val, channel); default: return -ENOTSUPP; } @@ -897,6 +898,8 @@ static umode_t ltc2947_is_visible(const void *data, return ltc2947_power_is_visible(attr); case hwmon_temp: return ltc2947_temp_is_visible(attr); + case hwmon_energy64: + return 0444; default: return 0; } @@ -929,6 +932,9 @@ static const struct hwmon_channel_info * const ltc2947_info[] = { HWMON_T_LABEL, HWMON_T_MAX_ALARM | HWMON_T_MIN_ALARM | HWMON_T_MAX | HWMON_T_MIN | HWMON_T_LABEL), + HWMON_CHANNEL_INFO(energy64, + HWMON_E_INPUT, + HWMON_E_INPUT), NULL }; @@ -944,19 +950,6 @@ static const struct hwmon_chip_info ltc2947_chip_info = { .info = ltc2947_info, }; -/* energy attributes are 6bytes wide so we need u64 */ -static SENSOR_DEVICE_ATTR(energy1_input, 0444, ltc2947_show_value, NULL, - LTC2947_REG_ENERGY1); -static SENSOR_DEVICE_ATTR(energy2_input, 0444, ltc2947_show_value, NULL, - LTC2947_REG_ENERGY2); - -static struct attribute *ltc2947_attrs[] = { - &sensor_dev_attr_energy1_input.dev_attr.attr, - &sensor_dev_attr_energy2_input.dev_attr.attr, - NULL, -}; -ATTRIBUTE_GROUPS(ltc2947); - static int ltc2947_setup(struct ltc2947_data *st) { int ret; @@ -1114,8 +1107,7 @@ int ltc2947_core_probe(struct regmap *map, const char *name) return ret; hwmon = devm_hwmon_device_register_with_info(dev, name, st, - <c2947_chip_info, - ltc2947_groups); + <c2947_chip_info, NULL); return PTR_ERR_OR_ZERO(hwmon); } EXPORT_SYMBOL_GPL(ltc2947_core_probe); From 9d501496f094c71a1630e4d72dedbb5fa98bca0d Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sun, 12 Oct 2025 14:16:25 -0700 Subject: [PATCH 017/110] hwmon: (ltc4282) Use the energy64 attribute type to report the energy MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use the energy64 attribute type instead of a locally defined sysfs attribute to report the accumulated energy. Signed-off-by: Guenter Roeck Reviewed-by: Nuno Sá Link: https://lore.kernel.org/r/20251012211625.533791-2-linux@roeck-us.net Signed-off-by: Guenter Roeck --- drivers/hwmon/ltc4282.c | 44 ++++++++++++----------------------------- 1 file changed, 13 insertions(+), 31 deletions(-) diff --git a/drivers/hwmon/ltc4282.c b/drivers/hwmon/ltc4282.c index 1d664a2d7b3c..44102879694a 100644 --- a/drivers/hwmon/ltc4282.c +++ b/drivers/hwmon/ltc4282.c @@ -12,7 +12,6 @@ #include #include #include -#include #include #include #include @@ -541,7 +540,7 @@ static int ltc4282_read_power_byte(const struct ltc4282_state *st, u32 reg, return 0; } -static int ltc4282_read_energy(const struct ltc4282_state *st, u64 *val) +static int ltc4282_read_energy(const struct ltc4282_state *st, s64 *val) { u64 temp, energy; __be64 raw; @@ -617,6 +616,12 @@ static int ltc4282_read(struct device *dev, enum hwmon_sensor_types type, *val = st->energy_en; } return 0; + case hwmon_energy64: + scoped_guard(mutex, &st->lock) { + if (st->energy_en) + return ltc4282_read_energy(st, (s64 *)val); + } + return -ENODATA; default: return -EOPNOTSUPP; } @@ -1078,6 +1083,9 @@ static umode_t ltc4282_is_visible(const void *data, case hwmon_energy: /* hwmon_energy_enable */ return 0644; + case hwmon_energy64: + /* hwmon_energy_input */ + return 0444; default: return 0; } @@ -1106,24 +1114,6 @@ static int ltc4282_read_labels(struct device *dev, } } -static ssize_t ltc4282_energy_show(struct device *dev, - struct device_attribute *da, char *buf) -{ - struct ltc4282_state *st = dev_get_drvdata(dev); - u64 energy; - int ret; - - guard(mutex)(&st->lock); - if (!st->energy_en) - return -ENODATA; - - ret = ltc4282_read_energy(st, &energy); - if (ret < 0) - return ret; - - return sysfs_emit(buf, "%llu\n", energy); -} - static const struct clk_ops ltc4282_ops = { .recalc_rate = ltc4282_recalc_rate, .determine_rate = ltc4282_determine_rate, @@ -1588,6 +1578,8 @@ static const struct hwmon_channel_info * const ltc4282_info[] = { HWMON_P_RESET_HISTORY | HWMON_P_LABEL), HWMON_CHANNEL_INFO(energy, HWMON_E_ENABLE), + HWMON_CHANNEL_INFO(energy64, + HWMON_E_INPUT), NULL }; @@ -1603,15 +1595,6 @@ static const struct hwmon_chip_info ltc4282_chip_info = { .info = ltc4282_info, }; -/* energy attributes are 6bytes wide so we need u64 */ -static SENSOR_DEVICE_ATTR_RO(energy1_input, ltc4282_energy, 0); - -static struct attribute *ltc4282_attrs[] = { - &sensor_dev_attr_energy1_input.dev_attr.attr, - NULL -}; -ATTRIBUTE_GROUPS(ltc4282); - static int ltc4282_show_fault_log(void *arg, u64 *val, u32 mask) { struct ltc4282_state *st = arg; @@ -1718,8 +1701,7 @@ static int ltc4282_probe(struct i2c_client *i2c) mutex_init(&st->lock); hwmon = devm_hwmon_device_register_with_info(dev, "ltc4282", st, - <c4282_chip_info, - ltc4282_groups); + <c4282_chip_info, NULL); if (IS_ERR(hwmon)) return PTR_ERR(hwmon); From d3f849c7318b95bca2695fe5fca13249e64632bb Mon Sep 17 00:00:00 2001 From: Kim Seer Paller Date: Mon, 13 Oct 2025 13:00:47 +0800 Subject: [PATCH 018/110] dt-bindings: hwmon: pmbus/max17616: Add SMBALERT interrupt property Add interrupt property to document the SMBALERT pin functionality for fault condition signal. Suggested-by: Marcelo Schmitt Signed-off-by: Kim Seer Paller Acked-by: Conor Dooley Link: https://lore.kernel.org/r/20251013-upstream-max17616-v1-1-0e15002479c3@analog.com Signed-off-by: Guenter Roeck --- .../devicetree/bindings/hwmon/pmbus/adi,max17616.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Documentation/devicetree/bindings/hwmon/pmbus/adi,max17616.yaml b/Documentation/devicetree/bindings/hwmon/pmbus/adi,max17616.yaml index 4680d354af0e..fa48af81e083 100644 --- a/Documentation/devicetree/bindings/hwmon/pmbus/adi,max17616.yaml +++ b/Documentation/devicetree/bindings/hwmon/pmbus/adi,max17616.yaml @@ -26,6 +26,10 @@ properties: vcc-supply: true + interrupts: + description: Fault condition signal provided on SMBALERT pin. + maxItems: 1 + required: - compatible - reg From 81a34b59330ac9ecf064a0c050f876bdaba9aa48 Mon Sep 17 00:00:00 2001 From: "Rob Herring (Arm)" Date: Mon, 13 Oct 2025 16:31:26 -0500 Subject: [PATCH 019/110] dt-bindings: hwmon: Convert apm,xgene-slimpro-hwmon to DT schema Convert APM X-Gene slimpro-hwmon binding to DT schema format. It's a straight-forward conversion. Signed-off-by: Rob Herring (Arm) Link: https://lore.kernel.org/r/20251013213127.692373-1-robh@kernel.org Signed-off-by: Guenter Roeck --- .../hwmon/apm,xgene-slimpro-hwmon.yaml | 30 +++++++++++++++++++ .../bindings/hwmon/apm-xgene-hwmon.txt | 14 --------- 2 files changed, 30 insertions(+), 14 deletions(-) create mode 100644 Documentation/devicetree/bindings/hwmon/apm,xgene-slimpro-hwmon.yaml delete mode 100644 Documentation/devicetree/bindings/hwmon/apm-xgene-hwmon.txt diff --git a/Documentation/devicetree/bindings/hwmon/apm,xgene-slimpro-hwmon.yaml b/Documentation/devicetree/bindings/hwmon/apm,xgene-slimpro-hwmon.yaml new file mode 100644 index 000000000000..58c51626a9ce --- /dev/null +++ b/Documentation/devicetree/bindings/hwmon/apm,xgene-slimpro-hwmon.yaml @@ -0,0 +1,30 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/hwmon/apm,xgene-slimpro-hwmon.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: APM X-Gene SLIMpro hwmon + +maintainers: + - Khuong Dinh + +properties: + compatible: + const: apm,xgene-slimpro-hwmon + + mboxes: + maxItems: 1 + +required: + - compatible + - mboxes + +additionalProperties: false + +examples: + - | + hwmon { + compatible = "apm,xgene-slimpro-hwmon"; + mboxes = <&mailbox 7>; + }; diff --git a/Documentation/devicetree/bindings/hwmon/apm-xgene-hwmon.txt b/Documentation/devicetree/bindings/hwmon/apm-xgene-hwmon.txt deleted file mode 100644 index 59b38557f1bb..000000000000 --- a/Documentation/devicetree/bindings/hwmon/apm-xgene-hwmon.txt +++ /dev/null @@ -1,14 +0,0 @@ -APM X-Gene hwmon driver - -APM X-Gene SOC sensors are accessed over the "SLIMpro" mailbox. - -Required properties : - - compatible : should be "apm,xgene-slimpro-hwmon" - - mboxes : use the label reference for the mailbox as the first parameter. - The second parameter is the channel number. - -Example : - hwmonslimpro { - compatible = "apm,xgene-slimpro-hwmon"; - mboxes = <&mailbox 7>; - }; From 6e11e29d22c7523d4da097bbe544cc80e22a2f44 Mon Sep 17 00:00:00 2001 From: Cosmo Chou Date: Fri, 10 Oct 2025 04:54:57 +0800 Subject: [PATCH 020/110] dt-bindings: trivial-devices: add mps,mp9945 Add dt-bindings for MPS mp9945 controller. Acked-by: Conor Dooley Signed-off-by: Cosmo Chou Link: https://lore.kernel.org/r/20251009205458.396368-1-chou.cosmo@gmail.com 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 61116b95b427..deccb119493e 100644 --- a/Documentation/devicetree/bindings/trivial-devices.yaml +++ b/Documentation/devicetree/bindings/trivial-devices.yaml @@ -321,6 +321,8 @@ properties: - mps,mp5998 # Monolithic Power Systems Inc. digital step-down converter mp9941 - mps,mp9941 + # Monolithic Power Systems Inc. digital step-down converter mp9945 + - mps,mp9945 # Temperature sensor with integrated fan control - national,lm63 # Temperature sensor with integrated fan control From 6923e2827d58ff0927ce8912fda65c837af090c7 Mon Sep 17 00:00:00 2001 From: Cosmo Chou Date: Fri, 10 Oct 2025 04:54:58 +0800 Subject: [PATCH 021/110] hwmon: (pmbus) add driver for MPS MP9945 Add support for mp9945 device from Monolithic Power Systems, Inc. (MPS) vendor. This is a single phase digital step down converter. Signed-off-by: Cosmo Chou Link: https://lore.kernel.org/r/20251009205458.396368-2-chou.cosmo@gmail.com Signed-off-by: Guenter Roeck --- Documentation/hwmon/index.rst | 1 + Documentation/hwmon/mp9945.rst | 117 ++++++++++++++++ MAINTAINERS | 7 + drivers/hwmon/pmbus/Kconfig | 9 ++ drivers/hwmon/pmbus/Makefile | 1 + drivers/hwmon/pmbus/mp9945.c | 243 +++++++++++++++++++++++++++++++++ 6 files changed, 378 insertions(+) create mode 100644 Documentation/hwmon/mp9945.rst create mode 100644 drivers/hwmon/pmbus/mp9945.c diff --git a/Documentation/hwmon/index.rst b/Documentation/hwmon/index.rst index bebe0e731be7..45a26927e64f 100644 --- a/Documentation/hwmon/index.rst +++ b/Documentation/hwmon/index.rst @@ -186,6 +186,7 @@ Hardware Monitoring Kernel Drivers mp5920 mp5990 mp9941 + mp9945 mpq8785 nct6683 nct6775 diff --git a/Documentation/hwmon/mp9945.rst b/Documentation/hwmon/mp9945.rst new file mode 100644 index 000000000000..f406f96efcf9 --- /dev/null +++ b/Documentation/hwmon/mp9945.rst @@ -0,0 +1,117 @@ +.. SPDX-License-Identifier: GPL-2.0 + +Kernel driver mp9945 +===================== + +Supported chips: + + * MPS mp9945 + + Prefix: 'mp9945' + +Author: + + Cosmo Chou + +Description +----------- + +This driver implements support for Monolithic Power Systems, Inc. (MPS) +MP9945 Digital Single-phase Controller. + +Device compliant with: + +- PMBus rev 1.3 interface. + +The driver exports the following attributes via the 'sysfs' files +for input voltage: + +**in1_input** + +**in1_label** + +**in1_crit** + +**in1_crit_alarm** + +**in1_lcrit** + +**in1_lcrit_alarm** + +**in1_max** + +**in1_max_alarm** + +**in1_min** + +**in1_min_alarm** + +The driver provides the following attributes for output voltage: + +**in2_input** + +**in2_label** + +**in2_crit** + +**in2_crit_alarm** + +**in2_lcrit** + +**in2_lcrit_alarm** + +**in2_min** + +**in2_min_alarm** + +The driver provides the following attributes for input current: + +**curr1_input** + +**curr1_label** + +**curr1_max** + +**curr1_max_alarm** + +The driver provides the following attributes for output current: + +**curr2_input** + +**curr2_label** + +**curr2_crit** + +**curr2_crit_alarm** + +**curr2_max** + +**curr2_max_alarm** + +The driver provides the following attributes for input power: + +**power1_input** + +**power1_label** + +The driver provides the following attributes for output power: + +**power2_input** + +**power2_label** + +**power2_max** + +**power2_max_alarm** + +The driver provides the following attributes for temperature: + +**temp1_input** + +**temp1_crit** + +**temp1_crit_alarm** + +**temp1_max** + +**temp1_max_alarm** diff --git a/MAINTAINERS b/MAINTAINERS index 953bf1d29a81..4a6e00df8a38 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -17499,6 +17499,13 @@ S: Maintained F: Documentation/hwmon/mp9941.rst F: drivers/hwmon/pmbus/mp9941.c +MPS MP9945 DRIVER +M: Cosmo Chou +L: linux-hwmon@vger.kernel.org +S: Maintained +F: Documentation/hwmon/mp9945.rst +F: drivers/hwmon/pmbus/mp9945.c + MR800 AVERMEDIA USB FM RADIO DRIVER M: Alexey Klimov L: linux-media@vger.kernel.org diff --git a/drivers/hwmon/pmbus/Kconfig b/drivers/hwmon/pmbus/Kconfig index fdcef5dfa5a8..f3fb94cebf1a 100644 --- a/drivers/hwmon/pmbus/Kconfig +++ b/drivers/hwmon/pmbus/Kconfig @@ -490,6 +490,15 @@ config SENSORS_MP9941 This driver can also be built as a module. If so, the module will be called mp9941. +config SENSORS_MP9945 + tristate "MPS MP9945" + help + If you say yes here you get hardware monitoring support for MPS + MP9945. + + This driver can also be built as a module. If so, the module will + be called mp9945. + config SENSORS_MPQ7932_REGULATOR bool "Regulator support for MPQ7932" depends on SENSORS_MPQ7932 && REGULATOR diff --git a/drivers/hwmon/pmbus/Makefile b/drivers/hwmon/pmbus/Makefile index 43fbc546fb5d..349a89b6d92e 100644 --- a/drivers/hwmon/pmbus/Makefile +++ b/drivers/hwmon/pmbus/Makefile @@ -49,6 +49,7 @@ obj-$(CONFIG_SENSORS_MP5023) += mp5023.o obj-$(CONFIG_SENSORS_MP5920) += mp5920.o obj-$(CONFIG_SENSORS_MP5990) += mp5990.o obj-$(CONFIG_SENSORS_MP9941) += mp9941.o +obj-$(CONFIG_SENSORS_MP9945) += mp9945.o obj-$(CONFIG_SENSORS_MPQ7932) += mpq7932.o obj-$(CONFIG_SENSORS_MPQ8785) += mpq8785.o obj-$(CONFIG_SENSORS_PLI1209BC) += pli1209bc.o diff --git a/drivers/hwmon/pmbus/mp9945.c b/drivers/hwmon/pmbus/mp9945.c new file mode 100644 index 000000000000..34822e0de812 --- /dev/null +++ b/drivers/hwmon/pmbus/mp9945.c @@ -0,0 +1,243 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Hardware monitoring driver for MPS Single-phase Digital VR Controllers(MP9945) + */ + +#include +#include +#include +#include +#include "pmbus.h" + +#define MFR_VR_MULTI_CONFIG_R1 0x08 +#define MFR_SVID_CFG_R1 0xBD + +/* VOUT_MODE register values */ +#define VOUT_MODE_LINEAR16 0x17 +#define VOUT_MODE_VID 0x21 +#define VOUT_MODE_DIRECT 0x40 + +#define MP9945_PAGE_NUM 1 + +#define MP9945_RAIL1_FUNC (PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | \ + PMBUS_HAVE_IIN | PMBUS_HAVE_IOUT | \ + PMBUS_HAVE_PIN | PMBUS_HAVE_POUT | \ + PMBUS_HAVE_TEMP | \ + PMBUS_HAVE_STATUS_VOUT | \ + PMBUS_HAVE_STATUS_IOUT | \ + PMBUS_HAVE_STATUS_TEMP | \ + PMBUS_HAVE_STATUS_INPUT) + +enum mp9945_vout_mode { + MP9945_VOUT_MODE_VID, + MP9945_VOUT_MODE_DIRECT, + MP9945_VOUT_MODE_LINEAR16, +}; + +struct mp9945_data { + struct pmbus_driver_info info; + enum mp9945_vout_mode vout_mode; + int vid_resolution; + int vid_offset; +}; + +#define to_mp9945_data(x) container_of(x, struct mp9945_data, info) + +static int mp9945_read_vout(struct i2c_client *client, struct mp9945_data *data) +{ + int ret; + + ret = i2c_smbus_read_word_data(client, PMBUS_READ_VOUT); + if (ret < 0) + return ret; + + ret &= GENMASK(11, 0); + + switch (data->vout_mode) { + case MP9945_VOUT_MODE_VID: + if (ret > 0) + ret = (ret + data->vid_offset) * data->vid_resolution; + break; + case MP9945_VOUT_MODE_DIRECT: + break; + case MP9945_VOUT_MODE_LINEAR16: + /* LSB: 1000 * 2^-9 (mV) */ + ret = DIV_ROUND_CLOSEST(ret * 125, 64); + break; + default: + return -ENODEV; + } + + return ret; +} + +static int mp9945_read_byte_data(struct i2c_client *client, int page, int reg) +{ + int ret; + + ret = i2c_smbus_write_byte_data(client, PMBUS_PAGE, 0); + if (ret < 0) + return ret; + + switch (reg) { + case PMBUS_VOUT_MODE: + /* + * Override VOUT_MODE to DIRECT as the driver handles custom + * VOUT format conversions internally. + */ + return PB_VOUT_MODE_DIRECT; + default: + return -ENODATA; + } +} + +static int mp9945_read_word_data(struct i2c_client *client, int page, int phase, + int reg) +{ + const struct pmbus_driver_info *info = pmbus_get_driver_info(client); + struct mp9945_data *data = to_mp9945_data(info); + int ret; + + ret = i2c_smbus_write_byte_data(client, PMBUS_PAGE, 0); + if (ret < 0) + return ret; + + switch (reg) { + case PMBUS_READ_VOUT: + ret = mp9945_read_vout(client, data); + break; + case PMBUS_VOUT_OV_FAULT_LIMIT: + case PMBUS_VOUT_UV_FAULT_LIMIT: + ret = i2c_smbus_read_word_data(client, reg); + if (ret < 0) + return ret; + + /* LSB: 1.95 (mV) */ + ret = DIV_ROUND_CLOSEST((ret & GENMASK(11, 0)) * 39, 20); + break; + case PMBUS_VOUT_UV_WARN_LIMIT: + ret = i2c_smbus_read_word_data(client, reg); + if (ret < 0) + return ret; + + ret &= GENMASK(9, 0); + if (ret > 0) + ret = (ret + data->vid_offset) * data->vid_resolution; + break; + default: + ret = -ENODATA; + break; + } + + return ret; +} + +static int mp9945_identify(struct i2c_client *client, + struct pmbus_driver_info *info) +{ + struct mp9945_data *data = to_mp9945_data(info); + int ret; + + ret = i2c_smbus_read_byte_data(client, PMBUS_VOUT_MODE); + if (ret < 0) + return ret; + + switch (ret) { + case VOUT_MODE_LINEAR16: + data->vout_mode = MP9945_VOUT_MODE_LINEAR16; + break; + case VOUT_MODE_VID: + data->vout_mode = MP9945_VOUT_MODE_VID; + break; + case VOUT_MODE_DIRECT: + data->vout_mode = MP9945_VOUT_MODE_DIRECT; + break; + default: + return -ENODEV; + } + + ret = i2c_smbus_write_byte_data(client, PMBUS_PAGE, 3); + if (ret < 0) + return ret; + + ret = i2c_smbus_read_word_data(client, MFR_VR_MULTI_CONFIG_R1); + if (ret < 0) + return ret; + + data->vid_resolution = (FIELD_GET(BIT(2), ret)) ? 5 : 10; + + ret = i2c_smbus_read_word_data(client, MFR_SVID_CFG_R1); + if (ret < 0) + return ret; + + data->vid_offset = (FIELD_GET(BIT(15), ret)) ? 19 : 49; + + return i2c_smbus_write_byte_data(client, PMBUS_PAGE, 0); +} + +static struct pmbus_driver_info mp9945_info = { + .pages = MP9945_PAGE_NUM, + .format[PSC_VOLTAGE_IN] = linear, + .format[PSC_VOLTAGE_OUT] = direct, + .format[PSC_CURRENT_IN] = linear, + .format[PSC_CURRENT_OUT] = linear, + .format[PSC_POWER] = linear, + .format[PSC_TEMPERATURE] = linear, + .m[PSC_VOLTAGE_OUT] = 1, + .R[PSC_VOLTAGE_OUT] = 3, + .b[PSC_VOLTAGE_OUT] = 0, + .func[0] = MP9945_RAIL1_FUNC, + .read_word_data = mp9945_read_word_data, + .read_byte_data = mp9945_read_byte_data, + .identify = mp9945_identify, +}; + +static int mp9945_probe(struct i2c_client *client) +{ + struct mp9945_data *data; + int ret; + + data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + memcpy(&data->info, &mp9945_info, sizeof(mp9945_info)); + + /* + * Set page 0 before probe. The core reads paged registers which are + * only on page 0 for this device. + */ + ret = i2c_smbus_write_byte_data(client, PMBUS_PAGE, 0); + if (ret < 0) + return ret; + + return pmbus_do_probe(client, &data->info); +} + +static const struct i2c_device_id mp9945_id[] = { + {"mp9945"}, + {} +}; +MODULE_DEVICE_TABLE(i2c, mp9945_id); + +static const struct of_device_id __maybe_unused mp9945_of_match[] = { + {.compatible = "mps,mp9945"}, + {} +}; +MODULE_DEVICE_TABLE(of, mp9945_of_match); + +static struct i2c_driver mp9945_driver = { + .driver = { + .name = "mp9945", + .of_match_table = of_match_ptr(mp9945_of_match), + }, + .probe = mp9945_probe, + .id_table = mp9945_id, +}; + +module_i2c_driver(mp9945_driver); + +MODULE_AUTHOR("Cosmo Chou "); +MODULE_DESCRIPTION("PMBus driver for MPS MP9945"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("PMBUS"); From 2e2334fa431af7c7e98abdf8e546a01a28bc00d4 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Thu, 5 Jun 2025 16:41:36 -0700 Subject: [PATCH 022/110] hwmon: (jc42) Rely on subsystem locking Attribute access is now serialized in the hardware monitoring core, so locking in the driver code is no longer necessary. Drop it. Signed-off-by: Guenter Roeck --- drivers/hwmon/jc42.c | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/drivers/hwmon/jc42.c b/drivers/hwmon/jc42.c index 06f0ab2f52fa..6549dc543781 100644 --- a/drivers/hwmon/jc42.c +++ b/drivers/hwmon/jc42.c @@ -19,7 +19,6 @@ #include #include #include -#include #include /* Addresses to scan */ @@ -179,7 +178,6 @@ static struct jc42_chips jc42_chips[] = { /* Each client has this additional data */ struct jc42_data { - struct mutex update_lock; /* protect register access */ struct regmap *regmap; bool extended; /* true if extended range supported */ bool valid; @@ -216,8 +214,6 @@ static int jc42_read(struct device *dev, enum hwmon_sensor_types type, unsigned int regval; int ret, temp, hyst; - mutex_lock(&data->update_lock); - switch (attr) { case hwmon_temp_input: ret = regmap_read(data->regmap, JC42_REG_TEMP, ®val); @@ -295,8 +291,6 @@ static int jc42_read(struct device *dev, enum hwmon_sensor_types type, break; } - mutex_unlock(&data->update_lock); - return ret; } @@ -308,8 +302,6 @@ static int jc42_write(struct device *dev, enum hwmon_sensor_types type, int diff, hyst; int ret; - mutex_lock(&data->update_lock); - switch (attr) { case hwmon_temp_min: ret = regmap_write(data->regmap, JC42_REG_TEMP_LOWER, @@ -356,8 +348,6 @@ static int jc42_write(struct device *dev, enum hwmon_sensor_types type, break; } - mutex_unlock(&data->update_lock); - return ret; } @@ -498,7 +488,6 @@ static int jc42_probe(struct i2c_client *client) return PTR_ERR(data->regmap); i2c_set_clientdata(client, data); - mutex_init(&data->update_lock); ret = regmap_read(data->regmap, JC42_REG_CAP, &cap); if (ret) From e9b64fae44be190ea21c1092e96f316f90deb663 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Thu, 5 Jun 2025 17:29:55 -0700 Subject: [PATCH 023/110] hwmon: (lm90) Rely on subsystem locking Attribute access is now serialized in the hardware monitoring core, so locking n the driver code is no longer necessary. Drop it. Signed-off-by: Guenter Roeck --- drivers/hwmon/lm90.c | 25 ++++--------------------- 1 file changed, 4 insertions(+), 21 deletions(-) diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c index c1f528e292f3..3c10a5066b53 100644 --- a/drivers/hwmon/lm90.c +++ b/drivers/hwmon/lm90.c @@ -108,7 +108,6 @@ #include #include #include -#include #include #include #include @@ -735,7 +734,6 @@ struct lm90_data { struct hwmon_channel_info temp_info; const struct hwmon_channel_info *info[3]; 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 */ @@ -1226,9 +1224,9 @@ static int lm90_update_alarms(struct lm90_data *data, bool force) { int err; - mutex_lock(&data->update_lock); + hwmon_lock(data->hwmon_dev); err = lm90_update_alarms_locked(data, force); - mutex_unlock(&data->update_lock); + hwmon_unlock(data->hwmon_dev); return err; } @@ -1519,9 +1517,7 @@ static int lm90_temp_read(struct device *dev, u32 attr, int channel, long *val) int err; u16 bit; - mutex_lock(&data->update_lock); err = lm90_update_device(dev); - mutex_unlock(&data->update_lock); if (err) return err; @@ -1590,11 +1586,9 @@ static int lm90_temp_write(struct device *dev, u32 attr, int channel, long val) struct lm90_data *data = dev_get_drvdata(dev); int err; - mutex_lock(&data->update_lock); - err = lm90_update_device(dev); if (err) - goto error; + return err; switch (attr) { case hwmon_temp_min: @@ -1624,9 +1618,6 @@ static int lm90_temp_write(struct device *dev, u32 attr, int channel, long val) err = -EOPNOTSUPP; break; } -error: - mutex_unlock(&data->update_lock); - return err; } @@ -1662,9 +1653,7 @@ static int lm90_chip_read(struct device *dev, u32 attr, int channel, long *val) struct lm90_data *data = dev_get_drvdata(dev); int err; - mutex_lock(&data->update_lock); err = lm90_update_device(dev); - mutex_unlock(&data->update_lock); if (err) return err; @@ -1710,11 +1699,9 @@ static int lm90_chip_write(struct device *dev, u32 attr, int channel, long val) struct i2c_client *client = data->client; int err; - mutex_lock(&data->update_lock); - err = lm90_update_device(dev); if (err) - goto error; + return err; switch (attr) { case hwmon_chip_update_interval: @@ -1728,9 +1715,6 @@ static int lm90_chip_write(struct device *dev, u32 attr, int channel, long val) err = -EOPNOTSUPP; break; } -error: - mutex_unlock(&data->update_lock); - return err; } @@ -2793,7 +2777,6 @@ 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); INIT_WORK(&data->report_work, lm90_report_alarms); From 5dd26b6924784ee6cf6ce1b27a900e8c9b932741 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Thu, 5 Jun 2025 17:31:20 -0700 Subject: [PATCH 024/110] hwmon: (adm9240) Rely on subsystem locking Attribute access is now serialized in the hardware monitoring core, so locking in the driver code is no longer necessary. Drop it. Signed-off-by: Guenter Roeck --- drivers/hwmon/adm9240.c | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/drivers/hwmon/adm9240.c b/drivers/hwmon/adm9240.c index 6dfbeb6acf00..86f6044b5bd0 100644 --- a/drivers/hwmon/adm9240.c +++ b/drivers/hwmon/adm9240.c @@ -37,7 +37,6 @@ #include #include #include -#include #include /* Addresses to scan */ @@ -125,7 +124,6 @@ static inline unsigned int AOUT_FROM_REG(u8 reg) struct adm9240_data { struct device *dev; struct regmap *regmap; - struct mutex update_lock; u8 fan_div[2]; /* rw fan1_div, read-only accessor */ u8 vrm; /* -- vrm set on startup, no accessor */ @@ -170,8 +168,6 @@ static int adm9240_fan_min_write(struct adm9240_data *data, int channel, long va u8 fan_min; int err; - mutex_lock(&data->update_lock); - if (!val) { fan_min = 255; new_div = data->fan_div[channel]; @@ -206,8 +202,6 @@ static int adm9240_fan_min_write(struct adm9240_data *data, int channel, long va } err = regmap_write(data->regmap, ADM9240_REG_FAN_MIN(channel), fan_min); - mutex_unlock(&data->update_lock); - return err; } @@ -501,23 +495,17 @@ static int adm9240_fan_read(struct device *dev, u32 attr, int channel, long *val switch (attr) { case hwmon_fan_input: - mutex_lock(&data->update_lock); err = regmap_read(data->regmap, ADM9240_REG_FAN(channel), ®val); - if (err < 0) { - mutex_unlock(&data->update_lock); + if (err < 0) return err; - } if (regval == 255 && data->fan_div[channel] < 3) { /* adjust fan clock divider on overflow */ err = adm9240_write_fan_div(data, channel, ++data->fan_div[channel]); - if (err) { - mutex_unlock(&data->update_lock); + if (err) return err; - } } *val = FAN_FROM_REG(regval, BIT(data->fan_div[channel])); - mutex_unlock(&data->update_lock); break; case hwmon_fan_div: *val = BIT(data->fan_div[channel]); @@ -791,7 +779,6 @@ static int adm9240_probe(struct i2c_client *client) return -ENOMEM; data->dev = dev; - mutex_init(&data->update_lock); data->regmap = devm_regmap_init_i2c(client, &adm9240_regmap_config); if (IS_ERR(data->regmap)) return PTR_ERR(data->regmap); From 61e9b50ffa2390a2927051d335b2ba04b8f791ce Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Thu, 5 Jun 2025 17:35:04 -0700 Subject: [PATCH 025/110] hwmon: (emc1403) Rely on subsystem locking Attribute access is now serialized in the hardware monitoring core, so locking in the driver code is no longer necessary. Drop it. Signed-off-by: Guenter Roeck --- drivers/hwmon/emc1403.c | 46 +++++++++-------------------------------- 1 file changed, 10 insertions(+), 36 deletions(-) diff --git a/drivers/hwmon/emc1403.c b/drivers/hwmon/emc1403.c index eca33220d34a..ccce948a4306 100644 --- a/drivers/hwmon/emc1403.c +++ b/drivers/hwmon/emc1403.c @@ -17,7 +17,6 @@ #include #include #include -#include #include #include @@ -30,7 +29,6 @@ enum emc1403_chip { emc1402, emc1403, emc1404, emc1428 }; struct thermal_data { enum emc1403_chip chip; struct regmap *regmap; - struct mutex mutex; }; static ssize_t power_state_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -268,8 +266,8 @@ static s8 emc1403_temp_regs_low[][4] = { }, }; -static int __emc1403_get_temp(struct thermal_data *data, int channel, - enum emc1403_reg_map map, long *val) +static int emc1403_get_temp(struct thermal_data *data, int channel, + enum emc1403_reg_map map, long *val) { unsigned int regvalh; unsigned int regvall = 0; @@ -295,38 +293,23 @@ static int __emc1403_get_temp(struct thermal_data *data, int channel, return 0; } -static int emc1403_get_temp(struct thermal_data *data, int channel, - enum emc1403_reg_map map, long *val) -{ - int ret; - - mutex_lock(&data->mutex); - ret = __emc1403_get_temp(data, channel, map, val); - mutex_unlock(&data->mutex); - - return ret; -} - static int emc1403_get_hyst(struct thermal_data *data, int channel, enum emc1403_reg_map map, long *val) { int hyst, ret; long limit; - mutex_lock(&data->mutex); - ret = __emc1403_get_temp(data, channel, map, &limit); + ret = emc1403_get_temp(data, channel, map, &limit); if (ret < 0) - goto unlock; + return ret; ret = regmap_read(data->regmap, 0x21, &hyst); if (ret < 0) - goto unlock; + return ret; if (map == temp_min) *val = limit + hyst * 1000; else *val = limit - hyst * 1000; -unlock: - mutex_unlock(&data->mutex); - return ret; + return 0; } static int emc1403_temp_read(struct thermal_data *data, u32 attr, int channel, long *val) @@ -451,20 +434,16 @@ static int emc1403_set_hyst(struct thermal_data *data, long val) else val = clamp_val(val, 0, 255000); - mutex_lock(&data->mutex); - ret = __emc1403_get_temp(data, 0, temp_crit, &limit); + ret = emc1403_get_temp(data, 0, temp_crit, &limit); if (ret < 0) - goto unlock; + return ret; hyst = limit - val; if (data->chip == emc1428) hyst = clamp_val(DIV_ROUND_CLOSEST(hyst, 1000), 0, 127); else hyst = clamp_val(DIV_ROUND_CLOSEST(hyst, 1000), 0, 255); - ret = regmap_write(data->regmap, 0x21, hyst); -unlock: - mutex_unlock(&data->mutex); - return ret; + return regmap_write(data->regmap, 0x21, hyst); } static int emc1403_set_temp(struct thermal_data *data, int channel, @@ -478,7 +457,6 @@ static int emc1403_set_temp(struct thermal_data *data, int channel, regh = emc1403_temp_regs[channel][map]; regl = emc1403_temp_regs_low[channel][map]; - mutex_lock(&data->mutex); if (regl >= 0) { if (data->chip == emc1428) val = clamp_val(val, -128000, 127875); @@ -487,7 +465,7 @@ static int emc1403_set_temp(struct thermal_data *data, int channel, regval = DIV_ROUND_CLOSEST(val, 125); ret = regmap_write(data->regmap, regh, (regval >> 3) & 0xff); if (ret < 0) - goto unlock; + return ret; ret = regmap_write(data->regmap, regl, (regval & 0x07) << 5); } else { if (data->chip == emc1428) @@ -497,8 +475,6 @@ static int emc1403_set_temp(struct thermal_data *data, int channel, regval = DIV_ROUND_CLOSEST(val, 1000); ret = regmap_write(data->regmap, regh, regval); } -unlock: - mutex_unlock(&data->mutex); return ret; } @@ -695,8 +671,6 @@ static int emc1403_probe(struct i2c_client *client) if (IS_ERR(data->regmap)) return PTR_ERR(data->regmap); - mutex_init(&data->mutex); - hwmon_dev = devm_hwmon_device_register_with_info(&client->dev, client->name, data, &emc1403_chip_info, From 965e7381ffcbcc7fef3ceb4a35fbc295f7ad709a Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sat, 7 Jun 2025 20:56:22 -0700 Subject: [PATCH 026/110] hwmon: (tmp464) Rely on subsystem locking Attribute access is now serialized in the hardware monitoring core, so locking in the driver is no longer necessary. Drop it. Signed-off-by: Guenter Roeck --- drivers/hwmon/tmp464.c | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/drivers/hwmon/tmp464.c b/drivers/hwmon/tmp464.c index 0f629c6d7695..98f2576d94c6 100644 --- a/drivers/hwmon/tmp464.c +++ b/drivers/hwmon/tmp464.c @@ -13,7 +13,6 @@ #include #include #include -#include #include #include #include @@ -92,7 +91,6 @@ struct tmp464_channel { struct tmp464_data { struct regmap *regmap; - struct mutex update_lock; int channels; s16 config_orig; u16 open_reg; @@ -172,19 +170,16 @@ static int tmp464_temp_read(struct device *dev, u32 attr, int channel, long *val * complete. That means we have to cache the value internally * for one measurement cycle and report the cached value. */ - mutex_lock(&data->update_lock); if (!data->valid || time_after(jiffies, data->last_updated + msecs_to_jiffies(data->update_interval))) { err = regmap_read(regmap, TMP464_REMOTE_OPEN_REG, ®val); if (err < 0) - goto unlock; + break; data->open_reg = regval; data->last_updated = jiffies; data->valid = true; } *val = !!(data->open_reg & BIT(channel + 7)); -unlock: - mutex_unlock(&data->update_lock); break; case hwmon_temp_max_hyst: regs[0] = TMP464_THERM_LIMIT[channel]; @@ -345,8 +340,6 @@ static int tmp464_write(struct device *dev, enum hwmon_sensor_types type, struct tmp464_data *data = dev_get_drvdata(dev); int err; - mutex_lock(&data->update_lock); - switch (type) { case hwmon_chip: err = tmp464_chip_write(data, attr, channel, val); @@ -359,8 +352,6 @@ static int tmp464_write(struct device *dev, enum hwmon_sensor_types type, break; } - mutex_unlock(&data->update_lock); - return err; } @@ -658,8 +649,6 @@ static int tmp464_probe(struct i2c_client *client) if (!data) return -ENOMEM; - mutex_init(&data->update_lock); - data->channels = (int)(unsigned long)i2c_get_match_data(client); data->regmap = devm_regmap_init_i2c(client, &tmp464_regmap_config); From dc11f797b19c39d3d05471253b2fc651ab3ac03a Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sat, 7 Jun 2025 21:00:41 -0700 Subject: [PATCH 027/110] hwmon: (tmp421) Rely on subsystem locking Attribute access is now serialized in the hardware monitoring core, so locking in the driver code is no longer necessary. Drop it. Signed-off-by: Guenter Roeck --- drivers/hwmon/tmp421.c | 21 ++++----------------- 1 file changed, 4 insertions(+), 17 deletions(-) diff --git a/drivers/hwmon/tmp421.c b/drivers/hwmon/tmp421.c index 9537727aad9a..2e43ce8408d6 100644 --- a/drivers/hwmon/tmp421.c +++ b/drivers/hwmon/tmp421.c @@ -19,7 +19,6 @@ #include #include #include -#include #include #include @@ -99,7 +98,6 @@ struct tmp421_channel { struct tmp421_data { struct i2c_client *client; - struct mutex update_lock; u32 temp_config[MAX_CHANNELS + 1]; struct hwmon_channel_info temp_info; const struct hwmon_channel_info *info[2]; @@ -130,38 +128,28 @@ static int tmp421_update_device(struct tmp421_data *data) int ret = 0; int i; - mutex_lock(&data->update_lock); - if (time_after(jiffies, data->last_updated + (HZ / 2)) || !data->valid) { + data->valid = false; ret = i2c_smbus_read_byte_data(client, TMP421_CONFIG_REG_1); if (ret < 0) - goto exit; + return ret; data->config = ret; for (i = 0; i < data->channels; i++) { ret = i2c_smbus_read_byte_data(client, TMP421_TEMP_MSB[i]); if (ret < 0) - goto exit; + return ret; data->channel[i].temp = ret << 8; ret = i2c_smbus_read_byte_data(client, TMP421_TEMP_LSB[i]); if (ret < 0) - goto exit; + return ret; data->channel[i].temp |= ret; } data->last_updated = jiffies; data->valid = true; } - -exit: - mutex_unlock(&data->update_lock); - - if (ret < 0) { - data->valid = false; - return ret; - } - return 0; } @@ -442,7 +430,6 @@ static int tmp421_probe(struct i2c_client *client) if (!data) return -ENOMEM; - mutex_init(&data->update_lock); data->channels = (unsigned long)i2c_get_match_data(client); data->client = client; From 3c1aefb3656f0fcef2644e714223595f016e57c4 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sat, 7 Jun 2025 21:01:11 -0700 Subject: [PATCH 028/110] hwmon: (tmp401) Rely on subsystem locking Attribute access is now serialized in the hardware monitoring core, so locking in the driver code is no longer necessary. Drop it. Signed-off-by: Guenter Roeck --- drivers/hwmon/tmp401.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/drivers/hwmon/tmp401.c b/drivers/hwmon/tmp401.c index 02c5a3bb1071..fbaa34973694 100644 --- a/drivers/hwmon/tmp401.c +++ b/drivers/hwmon/tmp401.c @@ -24,7 +24,6 @@ #include #include #include -#include #include #include @@ -107,7 +106,6 @@ MODULE_DEVICE_TABLE(i2c, tmp401_id); struct tmp401_data { struct i2c_client *client; struct regmap *regmap; - struct mutex update_lock; enum chips kind; bool extended_range; @@ -357,7 +355,6 @@ static int tmp401_temp_write(struct device *dev, u32 attr, int channel, unsigned int regval; int reg, ret, temp; - mutex_lock(&data->update_lock); switch (attr) { case hwmon_temp_min: case hwmon_temp_max: @@ -386,7 +383,6 @@ static int tmp401_temp_write(struct device *dev, u32 attr, int channel, ret = -EOPNOTSUPP; break; } - mutex_unlock(&data->update_lock); return ret; } @@ -436,7 +432,6 @@ static int tmp401_chip_write(struct device *dev, u32 attr, int channel, long val struct regmap *regmap = data->regmap; int err; - mutex_lock(&data->update_lock); switch (attr) { case hwmon_chip_update_interval: err = tmp401_set_convrate(regmap, val); @@ -456,8 +451,6 @@ static int tmp401_chip_write(struct device *dev, u32 attr, int channel, long val err = -EOPNOTSUPP; break; } - mutex_unlock(&data->update_lock); - return err; } @@ -685,7 +678,6 @@ static int tmp401_probe(struct i2c_client *client) return -ENOMEM; data->client = client; - mutex_init(&data->update_lock); data->kind = (uintptr_t)i2c_get_match_data(client); data->regmap = devm_regmap_init(dev, NULL, data, &tmp401_regmap_config); From 9a4bbd20a879b66dddb563698345b0ae24d810a6 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sat, 7 Jun 2025 21:02:15 -0700 Subject: [PATCH 029/110] hwmon: (tmp108) Drop mutex.h include The driver does not perform any locking, so including mutex.h is not necessary. Drop it. Signed-off-by: Guenter Roeck --- drivers/hwmon/tmp108.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/hwmon/tmp108.c b/drivers/hwmon/tmp108.c index a971ff628435..60a237cbedbc 100644 --- a/drivers/hwmon/tmp108.c +++ b/drivers/hwmon/tmp108.c @@ -10,7 +10,6 @@ #include #include #include -#include #include #include #include From 1cb5eca6f2261ab93e9cca115eec1442af066a0c Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sat, 7 Jun 2025 21:03:55 -0700 Subject: [PATCH 030/110] hwmon: (drivetemp) Rely on subsystem locking Attribute access is now serialized in the hardware monitoring core, so locking in the driver code is no longer necessary. Drop it. Signed-off-by: Guenter Roeck --- drivers/hwmon/drivetemp.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/drivers/hwmon/drivetemp.c b/drivers/hwmon/drivetemp.c index 291d91f68646..9c5b021aab86 100644 --- a/drivers/hwmon/drivetemp.c +++ b/drivers/hwmon/drivetemp.c @@ -102,7 +102,6 @@ #include #include #include -#include #include #include #include @@ -110,7 +109,6 @@ struct drivetemp_data { struct list_head list; /* list of instantiated devices */ - struct mutex lock; /* protect data buffer accesses */ struct scsi_device *sdev; /* SCSI device */ struct device *dev; /* instantiating device */ struct device *hwdev; /* hardware monitoring device */ @@ -462,9 +460,7 @@ static int drivetemp_read(struct device *dev, enum hwmon_sensor_types type, case hwmon_temp_input: case hwmon_temp_lowest: case hwmon_temp_highest: - mutex_lock(&st->lock); err = st->get_temp(st, attr, val); - mutex_unlock(&st->lock); break; case hwmon_temp_lcrit: *val = st->temp_lcrit; @@ -566,7 +562,6 @@ static int drivetemp_add(struct device *dev) st->sdev = sdev; st->dev = dev; - mutex_init(&st->lock); if (drivetemp_identify(st)) { err = -ENODEV; From 658bc40ba207ea03341c540e0632eeda7450c151 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sat, 7 Jun 2025 21:07:16 -0700 Subject: [PATCH 031/110] hwmon: (max6697) Rely on subsystem locking Attribute access is now serialized in the hardware monitoring core, so locking in the driver code is no longer necessary. Drop it. Signed-off-by: Guenter Roeck --- drivers/hwmon/max6697.c | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/drivers/hwmon/max6697.c b/drivers/hwmon/max6697.c index 0735a1d2c20f..6b4a52838818 100644 --- a/drivers/hwmon/max6697.c +++ b/drivers/hwmon/max6697.c @@ -13,7 +13,6 @@ #include #include #include -#include #include #include #include @@ -91,8 +90,6 @@ struct max6697_data { int temp_offset; /* in degrees C */ - struct mutex update_lock; - #define MAX6697_TEMP_INPUT 0 #define MAX6697_TEMP_EXT 1 #define MAX6697_TEMP_MAX 2 @@ -302,7 +299,6 @@ static int max6697_write(struct device *dev, enum hwmon_sensor_types type, val = clamp_val(val, 0, 255); return regmap_write(regmap, MAX6697_REG_MIN, val); case hwmon_temp_offset: - mutex_lock(&data->update_lock); val = clamp_val(val, MAX6581_OFFSET_MIN, MAX6581_OFFSET_MAX); val = DIV_ROUND_CLOSEST(val, 250); if (!val) { /* disable this (and only this) channel */ @@ -313,11 +309,9 @@ static int max6697_write(struct device *dev, enum hwmon_sensor_types type, ret = regmap_set_bits(regmap, MAX6581_REG_OFFSET_SELECT, BIT(channel - 1)); if (ret) - goto unlock; + return ret; ret = regmap_write(regmap, MAX6581_REG_OFFSET, val); } -unlock: - mutex_unlock(&data->update_lock); return ret; default: return -EOPNOTSUPP; @@ -559,7 +553,6 @@ static int max6697_probe(struct i2c_client *client) data->regmap = regmap; data->type = (uintptr_t)i2c_get_match_data(client); data->chip = &max6697_chip_data[data->type]; - mutex_init(&data->update_lock); err = max6697_init_chip(client->dev.of_node, data); if (err) From 8ee6dc15e2d19dd75c8ce01567d8255b8c4f7d4f Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sat, 7 Jun 2025 21:08:40 -0700 Subject: [PATCH 032/110] hwmon: (ltc4245) Rely on subsystem locking Attribute access is now serialized in the hardware monitoring core, so locking in the driver code is no longer necessary. Drop it. While at it, drop the unnecessary include of hwmon-sysfs.h. Signed-off-by: Guenter Roeck --- drivers/hwmon/ltc4245.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/drivers/hwmon/ltc4245.c b/drivers/hwmon/ltc4245.c index 14593bc81e85..e8131a48bda7 100644 --- a/drivers/hwmon/ltc4245.c +++ b/drivers/hwmon/ltc4245.c @@ -18,7 +18,6 @@ #include #include #include -#include #include #include @@ -51,7 +50,6 @@ enum ltc4245_cmd { struct ltc4245_data { struct i2c_client *client; - struct mutex update_lock; bool valid; unsigned long last_updated; /* in jiffies */ @@ -132,10 +130,7 @@ static struct ltc4245_data *ltc4245_update_device(struct device *dev) s32 val; int i; - mutex_lock(&data->update_lock); - if (time_after(jiffies, data->last_updated + HZ) || !data->valid) { - /* Read control registers -- 0x00 to 0x07 */ for (i = 0; i < ARRAY_SIZE(data->cregs); i++) { val = i2c_smbus_read_byte_data(client, i); @@ -161,8 +156,6 @@ static struct ltc4245_data *ltc4245_update_device(struct device *dev) data->valid = true; } - mutex_unlock(&data->update_lock); - return data; } @@ -454,7 +447,6 @@ static int ltc4245_probe(struct i2c_client *client) return -ENOMEM; data->client = client; - mutex_init(&data->update_lock); data->use_extra_gpios = ltc4245_use_extra_gpios(client); /* Initialize the LTC4245 chip */ From b37a3b983a8f8007062e7ca0ceb8343186c3f56f Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sat, 7 Jun 2025 21:10:12 -0700 Subject: [PATCH 033/110] hwmon: (lm95245) Rely on subsystem locking Attribute access is now serialized in the hardware monitoring core, so locking in the driver code is no longer necessary. Drop it. Signed-off-by: Guenter Roeck --- drivers/hwmon/lm95245.c | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/drivers/hwmon/lm95245.c b/drivers/hwmon/lm95245.c index 3bdc30530847..9ed300c6b5f7 100644 --- a/drivers/hwmon/lm95245.c +++ b/drivers/hwmon/lm95245.c @@ -13,7 +13,6 @@ #include #include #include -#include #include #include @@ -86,7 +85,6 @@ static const unsigned short normal_i2c[] = { /* Client data (each client gets its own) */ struct lm95245_data { struct regmap *regmap; - struct mutex update_lock; int interval; /* in msecs */ }; @@ -279,20 +277,16 @@ static int lm95245_write_temp(struct device *dev, u32 attr, int channel, ret = regmap_write(regmap, reg, val); return ret; case hwmon_temp_crit_hyst: - mutex_lock(&data->update_lock); ret = regmap_read(regmap, LM95245_REG_RW_LOCAL_OS_TCRIT_LIMIT, ®val); - if (ret < 0) { - mutex_unlock(&data->update_lock); + if (ret < 0) return ret; - } /* Clamp to reasonable range to prevent overflow */ val = clamp_val(val, -1000000, 1000000); val = regval - val / 1000; val = clamp_val(val, 0, 31); ret = regmap_write(regmap, LM95245_REG_RW_COMMON_HYSTERESIS, val); - mutex_unlock(&data->update_lock); return ret; case hwmon_temp_offset: val = clamp_val(val, -128000, 127875); @@ -332,14 +326,10 @@ static int lm95245_write_chip(struct device *dev, u32 attr, int channel, long val) { struct lm95245_data *data = dev_get_drvdata(dev); - int ret; switch (attr) { case hwmon_chip_update_interval: - mutex_lock(&data->update_lock); - ret = lm95245_set_conversion_rate(data, val); - mutex_unlock(&data->update_lock); - return ret; + return lm95245_set_conversion_rate(data, val); default: return -EOPNOTSUPP; } @@ -542,8 +532,6 @@ static int lm95245_probe(struct i2c_client *client) if (IS_ERR(data->regmap)) return PTR_ERR(data->regmap); - mutex_init(&data->update_lock); - /* Initialize the LM95245 chip */ ret = lm95245_init_client(data); if (ret < 0) From 44bd91bca9d95339a7791b1eaa0186f4fad9e857 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Mon, 9 Jun 2025 06:49:07 -0700 Subject: [PATCH 034/110] hwmon: (tmp103) Drop unnecessary include files The driver does not perform any locking, does not sleep or check the time, and does not create sysfs attributes. Drop the unnecessary include files. Signed-off-by: Guenter Roeck --- drivers/hwmon/tmp103.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/hwmon/tmp103.c b/drivers/hwmon/tmp103.c index f271a03e05ae..221bba8a215d 100644 --- a/drivers/hwmon/tmp103.c +++ b/drivers/hwmon/tmp103.c @@ -14,11 +14,8 @@ #include #include #include -#include #include -#include #include -#include #include #define TMP103_TEMP_REG 0x00 From 04e965717f5d45e59912800c7ee587e043b075ac Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Mon, 9 Jun 2025 06:51:14 -0700 Subject: [PATCH 035/110] hwmon: (tmp102) Drop unnecessary include files The driver does not perform any locking and does not create sysfs attributes. Drop the unnecessary include files. Signed-off-by: Guenter Roeck --- drivers/hwmon/tmp102.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/hwmon/tmp102.c b/drivers/hwmon/tmp102.c index 376e0eac8cc1..5b10c395a84d 100644 --- a/drivers/hwmon/tmp102.c +++ b/drivers/hwmon/tmp102.c @@ -10,9 +10,7 @@ #include #include #include -#include #include -#include #include #include #include From a07eb5f6f942f26460feb1e92b9c58198f69d4e8 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Mon, 9 Jun 2025 11:08:07 -0700 Subject: [PATCH 036/110] hwmon: (max6639) Rely on subsystem locking Attribute access is now serialized in the hardware monitoring core, so locking in the driver code is no longer necessary. Drop it. While at it, drop unnecessary include of hwmon-sysfs.c. Signed-off-by: Guenter Roeck --- drivers/hwmon/max6639.c | 23 ++++------------------- 1 file changed, 4 insertions(+), 19 deletions(-) diff --git a/drivers/hwmon/max6639.c b/drivers/hwmon/max6639.c index a06346496e1d..99140a2ca995 100644 --- a/drivers/hwmon/max6639.c +++ b/drivers/hwmon/max6639.c @@ -16,9 +16,7 @@ #include #include #include -#include #include -#include #include #include @@ -75,7 +73,6 @@ static const unsigned int freq_table[] = { 20, 33, 50, 100, 5000, 8333, 12500, */ struct max6639_data { struct regmap *regmap; - struct mutex update_lock; /* Register values initialized only once */ u8 ppr[MAX6639_NUM_CHANNELS]; /* Pulses per rotation 0..3 for 1..4 ppr */ @@ -249,16 +246,11 @@ static int max6639_write_fan(struct device *dev, u32 attr, int channel, if (val <= 0 || val > 4) return -EINVAL; - mutex_lock(&data->update_lock); /* Set Fan pulse per revolution */ err = max6639_set_ppr(data, channel, val); - if (err < 0) { - mutex_unlock(&data->update_lock); + if (err < 0) return err; - } data->ppr[channel] = val; - - mutex_unlock(&data->update_lock); return 0; default: return -EOPNOTSUPP; @@ -320,21 +312,17 @@ static int max6639_write_pwm(struct device *dev, u32 attr, int channel, case hwmon_pwm_input: if (val < 0 || val > 255) return -EINVAL; - err = regmap_write(data->regmap, MAX6639_REG_TARGTDUTY(channel), - val * 120 / 255); - return err; + return regmap_write(data->regmap, MAX6639_REG_TARGTDUTY(channel), + val * 120 / 255); case hwmon_pwm_freq: val = clamp_val(val, 0, 25000); i = find_closest(val, freq_table, ARRAY_SIZE(freq_table)); - mutex_lock(&data->update_lock); err = regmap_update_bits(data->regmap, MAX6639_REG_FAN_CONFIG3(channel), MAX6639_FAN_CONFIG3_FREQ_MASK, i); - if (err < 0) { - mutex_unlock(&data->update_lock); + if (err < 0) return err; - } if (i >> 2) err = regmap_set_bits(data->regmap, MAX6639_REG_GCONFIG, @@ -343,7 +331,6 @@ static int max6639_write_pwm(struct device *dev, u32 attr, int channel, err = regmap_clear_bits(data->regmap, MAX6639_REG_GCONFIG, MAX6639_GCONFIG_PWM_FREQ_HI); - mutex_unlock(&data->update_lock); return err; default: return -EOPNOTSUPP; @@ -753,8 +740,6 @@ static int max6639_probe(struct i2c_client *client) } } - mutex_init(&data->update_lock); - /* Initialize the max6639 chip */ err = max6639_init_client(client, data); if (err < 0) From e1c4d5db9a87a437f44ddc87032ef043e6511577 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Mon, 9 Jun 2025 11:14:16 -0700 Subject: [PATCH 037/110] hwmon: (max31827) Rely on subsystem locking Attribute access is now serialized in the hardware monitoring core, so locking in the driver code is no longer necessary. Drop it. Signed-off-by: Guenter Roeck --- drivers/hwmon/max31827.c | 60 ++++++++++------------------------------ 1 file changed, 14 insertions(+), 46 deletions(-) diff --git a/drivers/hwmon/max31827.c b/drivers/hwmon/max31827.c index a31c7b655da1..9b2e56c040df 100644 --- a/drivers/hwmon/max31827.c +++ b/drivers/hwmon/max31827.c @@ -10,7 +10,6 @@ #include #include #include -#include #include #include #include @@ -99,7 +98,6 @@ struct max31827_state { /* * Prevent simultaneous access to the i2c client. */ - struct mutex lock; struct regmap *regmap; bool enable; unsigned int resolution; @@ -123,30 +121,23 @@ static int shutdown_write(struct max31827_state *st, unsigned int reg, * Before the Temperature Threshold Alarm, Alarm Hysteresis Threshold * and Resolution bits from Configuration register are changed over I2C, * the part must be in shutdown mode. - * - * Mutex is used to ensure, that some other process doesn't change the - * configuration register. */ - mutex_lock(&st->lock); - if (!st->enable) { if (!mask) - ret = regmap_write(st->regmap, reg, val); - else - ret = regmap_update_bits(st->regmap, reg, mask, val); - goto unlock; + return regmap_write(st->regmap, reg, val); + return regmap_update_bits(st->regmap, reg, mask, val); } ret = regmap_read(st->regmap, MAX31827_CONFIGURATION_REG, &cfg); if (ret) - goto unlock; + return ret; cnv_rate = MAX31827_CONFIGURATION_CNV_RATE_MASK & cfg; cfg = cfg & ~(MAX31827_CONFIGURATION_1SHOT_MASK | MAX31827_CONFIGURATION_CNV_RATE_MASK); ret = regmap_write(st->regmap, MAX31827_CONFIGURATION_REG, cfg); if (ret) - goto unlock; + return ret; if (!mask) ret = regmap_write(st->regmap, reg, val); @@ -154,15 +145,11 @@ static int shutdown_write(struct max31827_state *st, unsigned int reg, ret = regmap_update_bits(st->regmap, reg, mask, val); if (ret) - goto unlock; + return ret; - ret = regmap_update_bits(st->regmap, MAX31827_CONFIGURATION_REG, - MAX31827_CONFIGURATION_CNV_RATE_MASK, - cnv_rate); - -unlock: - mutex_unlock(&st->lock); - return ret; + return regmap_update_bits(st->regmap, MAX31827_CONFIGURATION_REG, + MAX31827_CONFIGURATION_CNV_RATE_MASK, + cnv_rate); } static int write_alarm_val(struct max31827_state *st, unsigned int reg, @@ -223,23 +210,13 @@ static int max31827_read(struct device *dev, enum hwmon_sensor_types type, break; case hwmon_temp_input: - mutex_lock(&st->lock); - if (!st->enable) { - /* - * This operation requires mutex protection, - * because the chip configuration should not - * be changed during the conversion process. - */ - ret = regmap_update_bits(st->regmap, MAX31827_CONFIGURATION_REG, MAX31827_CONFIGURATION_1SHOT_MASK, 1); - if (ret) { - mutex_unlock(&st->lock); + if (ret) return ret; - } msleep(max31827_conv_times[st->resolution]); } @@ -254,8 +231,6 @@ static int max31827_read(struct device *dev, enum hwmon_sensor_types type, ret = regmap_read(st->regmap, MAX31827_T_REG, &uval); - mutex_unlock(&st->lock); - if (ret) break; @@ -352,7 +327,6 @@ static int max31827_write(struct device *dev, enum hwmon_sensor_types type, if (val >> 1) return -EINVAL; - mutex_lock(&st->lock); /** * The chip should not be enabled while a conversion is * performed. Neither should the chip be enabled when @@ -361,15 +335,11 @@ static int max31827_write(struct device *dev, enum hwmon_sensor_types type, st->enable = val; - ret = regmap_update_bits(st->regmap, - MAX31827_CONFIGURATION_REG, - MAX31827_CONFIGURATION_1SHOT_MASK | - MAX31827_CONFIGURATION_CNV_RATE_MASK, - MAX31827_DEVICE_ENABLE(val)); - - mutex_unlock(&st->lock); - - return ret; + return regmap_update_bits(st->regmap, + MAX31827_CONFIGURATION_REG, + MAX31827_CONFIGURATION_1SHOT_MASK | + MAX31827_CONFIGURATION_CNV_RATE_MASK, + MAX31827_DEVICE_ENABLE(val)); case hwmon_temp_max: return write_alarm_val(st, MAX31827_TH_REG, val); @@ -623,8 +593,6 @@ static int max31827_probe(struct i2c_client *client) if (!st) return -ENOMEM; - mutex_init(&st->lock); - st->regmap = devm_regmap_init_i2c(client, &max31827_regmap); if (IS_ERR(st->regmap)) return dev_err_probe(dev, PTR_ERR(st->regmap), From 9ad270bf4dcc0a8d8a0c13dda308c4df5cdd109b Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sun, 7 Sep 2025 13:50:21 -0700 Subject: [PATCH 038/110] hwmon: (nct7904) Rely on subsystem locking Attribute access is now serialized in the hardware monitoring core, so locking in the driver code is no longer necessary. Drop it. Signed-off-by: Guenter Roeck --- drivers/hwmon/nct7904.c | 63 ++++++++++++++++------------------------- 1 file changed, 24 insertions(+), 39 deletions(-) diff --git a/drivers/hwmon/nct7904.c b/drivers/hwmon/nct7904.c index f1e6eda949ba..2fa091720c79 100644 --- a/drivers/hwmon/nct7904.c +++ b/drivers/hwmon/nct7904.c @@ -21,7 +21,6 @@ #include #include #include -#include #include #include @@ -128,7 +127,6 @@ static const unsigned short normal_i2c[] = { struct nct7904_data { struct i2c_client *client; struct watchdog_device wdt; - struct mutex bank_lock; int bank_sel; u32 fanin_mask; u32 vsen_mask; @@ -142,24 +140,19 @@ struct nct7904_data { }; /* Access functions */ -static int nct7904_bank_lock(struct nct7904_data *data, unsigned int bank) +static int nct7904_bank_select(struct nct7904_data *data, unsigned int bank) { int ret; - mutex_lock(&data->bank_lock); if (data->bank_sel == bank) return 0; ret = i2c_smbus_write_byte_data(data->client, BANK_SEL_REG, bank); - if (ret == 0) - data->bank_sel = bank; - else + if (ret < 0) { data->bank_sel = -1; - return ret; -} - -static inline void nct7904_bank_release(struct nct7904_data *data) -{ - mutex_unlock(&data->bank_lock); + return ret; + } + data->bank_sel = bank; + return 0; } /* Read 1-byte register. Returns unsigned reg or -ERRNO on error. */ @@ -169,12 +162,10 @@ static int nct7904_read_reg(struct nct7904_data *data, struct i2c_client *client = data->client; int ret; - ret = nct7904_bank_lock(data, bank); - if (ret == 0) - ret = i2c_smbus_read_byte_data(client, reg); - - nct7904_bank_release(data); - return ret; + ret = nct7904_bank_select(data, bank); + if (ret < 0) + return ret; + return i2c_smbus_read_byte_data(client, reg); } /* @@ -187,19 +178,16 @@ static int nct7904_read_reg16(struct nct7904_data *data, struct i2c_client *client = data->client; int ret, hi; - ret = nct7904_bank_lock(data, bank); - if (ret == 0) { - ret = i2c_smbus_read_byte_data(client, reg); - if (ret >= 0) { - hi = ret; - ret = i2c_smbus_read_byte_data(client, reg + 1); - if (ret >= 0) - ret |= hi << 8; - } - } - - nct7904_bank_release(data); - return ret; + ret = nct7904_bank_select(data, bank); + if (ret < 0) + return ret; + hi = i2c_smbus_read_byte_data(client, reg); + if (hi < 0) + return hi; + ret = i2c_smbus_read_byte_data(client, reg + 1); + if (ret < 0) + return ret; + return ret | (hi << 8); } /* Write 1-byte register. Returns 0 or -ERRNO on error. */ @@ -209,12 +197,10 @@ static int nct7904_write_reg(struct nct7904_data *data, struct i2c_client *client = data->client; int ret; - ret = nct7904_bank_lock(data, bank); - if (ret == 0) - ret = i2c_smbus_write_byte_data(client, reg, val); - - nct7904_bank_release(data); - return ret; + ret = nct7904_bank_select(data, bank); + if (ret < 0) + return ret; + return i2c_smbus_write_byte_data(client, reg, val); } static int nct7904_read_fan(struct device *dev, u32 attr, int channel, @@ -1023,7 +1009,6 @@ static int nct7904_probe(struct i2c_client *client) return -ENOMEM; data->client = client; - mutex_init(&data->bank_lock); data->bank_sel = -1; /* Setup sensor groups. */ From bc003fbc77a04059527ef403effb363764ddf38c Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sun, 7 Sep 2025 13:51:50 -0700 Subject: [PATCH 039/110] hwmon: (nct7363) Drop unnecessary include files The driver does not perform any locking and does not create sysfs attributes. Drop the unnecessary include files. Signed-off-by: Guenter Roeck --- drivers/hwmon/nct7363.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/hwmon/nct7363.c b/drivers/hwmon/nct7363.c index e13ab918b1ab..71cef794835d 100644 --- a/drivers/hwmon/nct7363.c +++ b/drivers/hwmon/nct7363.c @@ -7,10 +7,8 @@ #include #include #include -#include #include #include -#include #include #include From 06c38a58786a77a4fb9b9a774ec53487548a19c4 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sun, 7 Sep 2025 14:00:54 -0700 Subject: [PATCH 040/110] hwmon: (max6620) Rely on subsystem locking Attribute access is now serialized in the hardware monitoring core, so locking in the driver code is no longer necessary. Drop it. Signed-off-by: Guenter Roeck --- drivers/hwmon/max6620.c | 43 ++++++++++++----------------------------- 1 file changed, 12 insertions(+), 31 deletions(-) diff --git a/drivers/hwmon/max6620.c b/drivers/hwmon/max6620.c index 13201fb755c9..4316dcdd03fc 100644 --- a/drivers/hwmon/max6620.c +++ b/drivers/hwmon/max6620.c @@ -130,7 +130,6 @@ static const u8 target_reg[] = { struct max6620_data { struct i2c_client *client; - struct mutex update_lock; bool valid; /* false until following fields are valid */ unsigned long last_updated; /* in jiffies */ @@ -161,39 +160,36 @@ static int max6620_update_device(struct device *dev) { struct max6620_data *data = dev_get_drvdata(dev); struct i2c_client *client = data->client; - int i; - int ret = 0; - - mutex_lock(&data->update_lock); + int i, ret; if (time_after(jiffies, data->last_updated + HZ) || !data->valid) { for (i = 0; i < 4; i++) { ret = i2c_smbus_read_byte_data(client, config_reg[i]); if (ret < 0) - goto error; + return ret; data->fancfg[i] = ret; ret = i2c_smbus_read_byte_data(client, dyn_reg[i]); if (ret < 0) - goto error; + return ret; data->fandyn[i] = ret; ret = i2c_smbus_read_byte_data(client, tach_reg[i]); if (ret < 0) - goto error; + return ret; data->tach[i] = (ret << 3) & 0x7f8; ret = i2c_smbus_read_byte_data(client, tach_reg[i] + 1); if (ret < 0) - goto error; + return ret; data->tach[i] |= (ret >> 5) & 0x7; ret = i2c_smbus_read_byte_data(client, target_reg[i]); if (ret < 0) - goto error; + return ret; data->target[i] = (ret << 3) & 0x7f8; ret = i2c_smbus_read_byte_data(client, target_reg[i] + 1); if (ret < 0) - goto error; + return ret; data->target[i] |= (ret >> 5) & 0x7; } @@ -204,16 +200,13 @@ static int max6620_update_device(struct device *dev) */ ret = i2c_smbus_read_byte_data(client, MAX6620_REG_FAULT); if (ret < 0) - goto error; + return ret; data->fault |= (ret >> 4) & (ret & 0x0F); data->last_updated = jiffies; data->valid = true; } - -error: - mutex_unlock(&data->update_lock); - return ret; + return 0; } static umode_t @@ -261,7 +254,6 @@ max6620_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, case hwmon_fan: switch (attr) { case hwmon_fan_alarm: - mutex_lock(&data->update_lock); *val = !!(data->fault & BIT(channel)); /* Setting TACH count to re-enable fan fault detection */ @@ -270,21 +262,15 @@ max6620_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, val2 = (data->target[channel] << 5) & 0xe0; ret = i2c_smbus_write_byte_data(client, target_reg[channel], val1); - if (ret < 0) { - mutex_unlock(&data->update_lock); + if (ret < 0) return ret; - } ret = i2c_smbus_write_byte_data(client, target_reg[channel] + 1, val2); - if (ret < 0) { - mutex_unlock(&data->update_lock); + if (ret < 0) return ret; - } data->fault &= ~BIT(channel); } - mutex_unlock(&data->update_lock); - break; case hwmon_fan_div: *val = max6620_fan_div_from_reg(data->fandyn[channel]); @@ -334,7 +320,6 @@ max6620_write(struct device *dev, enum hwmon_sensor_types type, u32 attr, return ret; data = dev_get_drvdata(dev); client = data->client; - mutex_lock(&data->update_lock); switch (type) { case hwmon_fan: @@ -360,8 +345,7 @@ max6620_write(struct device *dev, enum hwmon_sensor_types type, u32 attr, div = 5; break; default: - ret = -EINVAL; - goto error; + return -EINVAL; } data->fandyn[channel] &= 0x1F; data->fandyn[channel] |= div << 5; @@ -396,8 +380,6 @@ max6620_write(struct device *dev, enum hwmon_sensor_types type, u32 attr, break; } -error: - mutex_unlock(&data->update_lock); return ret; } @@ -478,7 +460,6 @@ static int max6620_probe(struct i2c_client *client) return -ENOMEM; data->client = client; - mutex_init(&data->update_lock); err = max6620_init_client(data); if (err) From 578652bef0e8aaeb6a69efdfce67e255985b04ff Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sun, 7 Sep 2025 14:05:28 -0700 Subject: [PATCH 041/110] hwmon: (max31790) Rely on subsystem locking Attribute access is now serialized in the hardware monitoring core, so locking in the driver code is no longer necessary. Drop it. Signed-off-by: Guenter Roeck --- drivers/hwmon/max31790.c | 48 +++++++++------------------------------- 1 file changed, 11 insertions(+), 37 deletions(-) diff --git a/drivers/hwmon/max31790.c b/drivers/hwmon/max31790.c index f56913327004..4f6171a17d9f 100644 --- a/drivers/hwmon/max31790.c +++ b/drivers/hwmon/max31790.c @@ -57,7 +57,6 @@ */ struct max31790_data { struct i2c_client *client; - struct mutex update_lock; bool valid; /* zero until following fields are valid */ unsigned long last_updated; /* in jiffies */ @@ -74,30 +73,27 @@ static struct max31790_data *max31790_update_device(struct device *dev) { struct max31790_data *data = dev_get_drvdata(dev); struct i2c_client *client = data->client; - struct max31790_data *ret = data; - int i; - int rv; - - mutex_lock(&data->update_lock); + int i, rv; if (time_after(jiffies, data->last_updated + HZ) || !data->valid) { + data->valid = false; rv = i2c_smbus_read_byte_data(client, MAX31790_REG_FAN_FAULT_STATUS1); if (rv < 0) - goto abort; + return ERR_PTR(rv); data->fault_status |= rv & 0x3F; rv = i2c_smbus_read_byte_data(client, MAX31790_REG_FAN_FAULT_STATUS2); if (rv < 0) - goto abort; + return ERR_PTR(rv); data->fault_status |= (rv & 0x3F) << 6; for (i = 0; i < NR_CHANNEL; i++) { rv = i2c_smbus_read_word_swapped(client, MAX31790_REG_TACH_COUNT(i)); if (rv < 0) - goto abort; + return ERR_PTR(rv); data->tach[i] = rv; if (data->fan_config[i] @@ -106,19 +102,19 @@ static struct max31790_data *max31790_update_device(struct device *dev) MAX31790_REG_TACH_COUNT(NR_CHANNEL + i)); if (rv < 0) - goto abort; + return ERR_PTR(rv); data->tach[NR_CHANNEL + i] = rv; } else { rv = i2c_smbus_read_word_swapped(client, MAX31790_REG_PWM_DUTY_CYCLE(i)); if (rv < 0) - goto abort; + return ERR_PTR(rv); data->pwm[i] = rv; rv = i2c_smbus_read_word_swapped(client, MAX31790_REG_TARGET_COUNT(i)); if (rv < 0) - goto abort; + return ERR_PTR(rv); data->target_count[i] = rv; } } @@ -126,16 +122,7 @@ static struct max31790_data *max31790_update_device(struct device *dev) data->last_updated = jiffies; data->valid = true; } - goto done; - -abort: - data->valid = false; - ret = ERR_PTR(rv); - -done: - mutex_unlock(&data->update_lock); - - return ret; + return data; } static const u8 tach_period[8] = { 1, 2, 4, 8, 16, 32, 32, 32 }; @@ -189,7 +176,6 @@ static int max31790_read_fan(struct device *dev, u32 attr, int channel, *val = rpm; return 0; case hwmon_fan_fault: - mutex_lock(&data->update_lock); *val = !!(data->fault_status & (1 << channel)); data->fault_status &= ~(1 << channel); /* @@ -200,10 +186,9 @@ static int max31790_read_fan(struct device *dev, u32 attr, int channel, if (*val) { int reg = MAX31790_REG_TARGET_COUNT(channel % NR_CHANNEL); - i2c_smbus_write_byte_data(data->client, reg, - data->target_count[channel % NR_CHANNEL] >> 8); + return i2c_smbus_write_byte_data(data->client, reg, + data->target_count[channel % NR_CHANNEL] >> 8); } - mutex_unlock(&data->update_lock); return 0; case hwmon_fan_enable: *val = !!(data->fan_config[channel] & MAX31790_FAN_CFG_TACH_INPUT_EN); @@ -223,8 +208,6 @@ static int max31790_write_fan(struct device *dev, u32 attr, int channel, u8 bits, fan_config; int sr; - mutex_lock(&data->update_lock); - switch (attr) { case hwmon_fan_target: val = clamp_val(val, FAN_RPM_MIN, FAN_RPM_MAX); @@ -270,9 +253,6 @@ static int max31790_write_fan(struct device *dev, u32 attr, int channel, err = -EOPNOTSUPP; break; } - - mutex_unlock(&data->update_lock); - return err; } @@ -338,8 +318,6 @@ static int max31790_write_pwm(struct device *dev, u32 attr, int channel, u8 fan_config; int err = 0; - mutex_lock(&data->update_lock); - switch (attr) { case hwmon_pwm_input: if (val < 0 || val > 255) { @@ -389,9 +367,6 @@ static int max31790_write_pwm(struct device *dev, u32 attr, int channel, err = -EOPNOTSUPP; break; } - - mutex_unlock(&data->update_lock); - return err; } @@ -525,7 +500,6 @@ static int max31790_probe(struct i2c_client *client) return -ENOMEM; data->client = client; - mutex_init(&data->update_lock); /* * Initialize the max31790 chip From 72804d1631b3688014e06065b9f95885ba3e08b6 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Wed, 15 Oct 2025 13:49:28 +0200 Subject: [PATCH 042/110] hwmon: (scmi) Enable sensors to assure they can be read The SCMI specification states that SENSOR_CONFIG_SET is used to enable/disable the sensors. The sensors can be disabled by default, which is the case at least on NXP i.MX95. Explicitly trigger the SENSOR_CONFIG_SET to enable the sensors, otherwise sensor read may fail and won't return valid data. Signed-off-by: Marek Vasut Link: https://lore.kernel.org/r/20251015114953.148706-1-marek.vasut@mailbox.org Signed-off-by: Guenter Roeck --- drivers/hwmon/scmi-hwmon.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/hwmon/scmi-hwmon.c b/drivers/hwmon/scmi-hwmon.c index 364199b332c0..eec223d174c0 100644 --- a/drivers/hwmon/scmi-hwmon.c +++ b/drivers/hwmon/scmi-hwmon.c @@ -240,6 +240,8 @@ static int scmi_hwmon_probe(struct scmi_device *sdev) const struct hwmon_channel_info **ptr_scmi_ci; const struct scmi_handle *handle = sdev->handle; struct scmi_protocol_handle *ph; + u32 sensor_config = FIELD_PREP(SCMI_SENS_CFG_SENSOR_ENABLED_MASK, + SCMI_SENS_CFG_SENSOR_ENABLE); if (!handle) return -ENODEV; @@ -339,6 +341,13 @@ static int scmi_hwmon_probe(struct scmi_device *sdev) if (!sensor) continue; + ret = sensor_ops->config_set(ph, i, sensor_config); + if (ret) { + dev_err(dev, "Error enabling sensor %s. err=%d\n", + sensor->name, ret); + continue; + } + /* * Warn on any misconfiguration related to thermal zones but * bail out of probing only on memory errors. From d10b16d02cc7d4c5fedd030ef389adf9c87198dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nuno=20S=C3=A1?= Date: Wed, 15 Oct 2025 15:52:19 +0100 Subject: [PATCH 043/110] dt-bindings: trivial-devices: add ADT7410, ADT7420 and ADT7422 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add Analog Devices high accuracy digital temperature sensors to trivial devices. Signed-off-by: Nuno Sá Acked-by: Conor Dooley Link: https://lore.kernel.org/r/20251015-dev-add-adt7422-v1-1-7cf72d3253ad@analog.com Signed-off-by: Guenter Roeck --- Documentation/devicetree/bindings/trivial-devices.yaml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Documentation/devicetree/bindings/trivial-devices.yaml b/Documentation/devicetree/bindings/trivial-devices.yaml index deccb119493e..884a071a1f14 100644 --- a/Documentation/devicetree/bindings/trivial-devices.yaml +++ b/Documentation/devicetree/bindings/trivial-devices.yaml @@ -43,8 +43,14 @@ properties: - adi,ad5110 # Temperature sensor with integrated fan control - adi,adm1027 + # Analog Devices ADT7410 High Accuracy Digital Temperature Sensor + - adi,adt7410 # Analog Devices ADT7411 Temperature Sensor and 8-channel ADC - adi,adt7411 + # Analog Devices ADT7420 High Accuracy Digital Temperature Sensor + - adi,adt7420 + # Analog Devices ADT7422 High Accuracy Digital Temperature Sensor + - adi,adt7422 # Temperature sensor with integrated fan control - adi,adt7463 # Temperature sensor with integrated fan control From 7b2a5a84694ea01e752411c24721d298cd07bc90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nuno=20S=C3=A1?= Date: Wed, 15 Oct 2025 15:52:20 +0100 Subject: [PATCH 044/110] hwmon: (adt7410): Add OF match table MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a struct of_device_id match table to the driver. While at it, make sure to properly include mod_devicetable.h which is also needed for struct i2c_device_id. Signed-off-by: Nuno Sá Link: https://lore.kernel.org/r/20251015-dev-add-adt7422-v1-2-7cf72d3253ad@analog.com Signed-off-by: Guenter Roeck --- drivers/hwmon/adt7410.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/hwmon/adt7410.c b/drivers/hwmon/adt7410.c index 3bf0e0a0882c..79952866a4db 100644 --- a/drivers/hwmon/adt7410.c +++ b/drivers/hwmon/adt7410.c @@ -7,6 +7,7 @@ */ #include +#include #include #include #include @@ -94,10 +95,18 @@ static const struct i2c_device_id adt7410_ids[] = { }; MODULE_DEVICE_TABLE(i2c, adt7410_ids); +static const struct of_device_id adt7410_of_match[] = { + { .compatible = "adi,adt7410" }, + { .compatible = "adi,adt7420" }, + { } +}; +MODULE_DEVICE_TABLE(of, adt7410_of_match); + static struct i2c_driver adt7410_driver = { .driver = { .name = "adt7410", .pm = pm_sleep_ptr(&adt7x10_dev_pm_ops), + .of_match_table = adt7410_of_match, }, .probe = adt7410_i2c_probe, .id_table = adt7410_ids, From ad1519d58713f835682b25b48dee6206977c8dec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nuno=20S=C3=A1?= Date: Wed, 15 Oct 2025 15:52:21 +0100 Subject: [PATCH 045/110] hwmon: (adt7410): Support adt7422 chip MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add support for the ADT7422 high accuracy digital temperature sensor. It's identical to the other chips supported in the driver so we just need to add it to the ID tables. Co-developed-by: Cosmin Tanislav Signed-off-by: Cosmin Tanislav Signed-off-by: Nuno Sá Link: https://lore.kernel.org/r/20251015-dev-add-adt7422-v1-3-7cf72d3253ad@analog.com Signed-off-by: Guenter Roeck --- drivers/hwmon/Kconfig | 6 +++--- drivers/hwmon/adt7410.c | 2 ++ 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 2760feb9f83b..aa783be9b73d 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -175,7 +175,7 @@ config SENSORS_ADT7X10 select REGMAP help This module contains common code shared by the ADT7310/ADT7320 and - ADT7410/ADT7420 temperature monitoring chip drivers. + ADT7410/ADT7420/ADT7422 temperature monitoring chip drivers. If built as a module, the module will be called adt7x10. @@ -191,12 +191,12 @@ config SENSORS_ADT7310 will be called adt7310. config SENSORS_ADT7410 - tristate "Analog Devices ADT7410/ADT7420" + tristate "Analog Devices ADT7410/ADT7420/ADT7422" depends on I2C select SENSORS_ADT7X10 help If you say yes here you get support for the Analog Devices - ADT7410 and ADT7420 temperature monitoring chips. + ADT7410, ADT7420 and ADT7422 temperature monitoring chips. This driver can also be built as a module. If so, the module will be called adt7410. diff --git a/drivers/hwmon/adt7410.c b/drivers/hwmon/adt7410.c index 79952866a4db..73b196a78f3a 100644 --- a/drivers/hwmon/adt7410.c +++ b/drivers/hwmon/adt7410.c @@ -91,6 +91,7 @@ static int adt7410_i2c_probe(struct i2c_client *client) static const struct i2c_device_id adt7410_ids[] = { { "adt7410" }, { "adt7420" }, + { "adt7422" }, {} }; MODULE_DEVICE_TABLE(i2c, adt7410_ids); @@ -98,6 +99,7 @@ MODULE_DEVICE_TABLE(i2c, adt7410_ids); static const struct of_device_id adt7410_of_match[] = { { .compatible = "adi,adt7410" }, { .compatible = "adi,adt7420" }, + { .compatible = "adi,adt7422" }, { } }; MODULE_DEVICE_TABLE(of, adt7410_of_match); From 1b1d2fae36544083227086f9705e3bb35f6fe15c Mon Sep 17 00:00:00 2001 From: Ankan Biswas Date: Thu, 16 Oct 2025 16:00:47 +0530 Subject: [PATCH 046/110] docs/hwmon: Fix broken links warnings in lm90.rst This patch fixes warnings in Documentation/hwmon/lm90.rst reported when running 'make linkcheckdocs'. On 2011-09-23 National Semiconductors became part of Texas Instruments and national.com is no longer accessible. The datasheet resources for the chips are now available at Texas Instruments website and have been updated for lm90. In 2021, Maxim Integrated was acquired by Analog Devices and maxim-ic.com redirects to analog.com. The chip datasheets are now available at Analog Devices and have been updated accordingly. Some additional broken or unavailable links have been replaced with equivalents hosted by DigiKey. Signed-off-by: Ankan Biswas Link: https://lore.kernel.org/r/20251016103049.2573-1-spyjetfayed@gmail.com Signed-off-by: Guenter Roeck --- Documentation/hwmon/lm90.rst | 127 ++++++++++++++++++++--------------- 1 file changed, 73 insertions(+), 54 deletions(-) diff --git a/Documentation/hwmon/lm90.rst b/Documentation/hwmon/lm90.rst index 98452eed16d5..79c023521d39 100644 --- a/Documentation/hwmon/lm90.rst +++ b/Documentation/hwmon/lm90.rst @@ -9,7 +9,9 @@ Supported chips: Addresses scanned: I2C 0x18 - 0x1a, 0x29 - 0x2b, 0x4c - 0x4e - Datasheet: Publicly available at the National Semiconductor website + Datasheet: Publicly available at the TI website + + https://www.ti.com/lit/ds/symlink/lm84.pdf * National Semiconductor LM90 @@ -17,9 +19,9 @@ Supported chips: Addresses scanned: I2C 0x4c - Datasheet: Publicly available at the National Semiconductor website + Datasheet: Publicly available at the TI website - http://www.national.com/pf/LM/LM90.html + https://www.ti.com/lit/ds/symlink/lm90.pdf * National Semiconductor LM89 @@ -27,9 +29,9 @@ Supported chips: Addresses scanned: I2C 0x4c and 0x4d - Datasheet: Publicly available at the National Semiconductor website + Datasheet: Publicly available at the TI website - http://www.national.com/mpf/LM/LM89.html + https://www.ti.com/lit/ds/symlink/lm89.pdf * National Semiconductor LM99 @@ -37,9 +39,9 @@ Supported chips: Addresses scanned: I2C 0x4c and 0x4d - Datasheet: Publicly available at the National Semiconductor website + Datasheet: Publicly available at the TI website - http://www.national.com/pf/LM/LM99.html + https://www.ti.com/lit/ds/symlink/lm99.pdf * National Semiconductor LM86 @@ -47,9 +49,9 @@ Supported chips: Addresses scanned: I2C 0x4c - Datasheet: Publicly available at the National Semiconductor website + Datasheet: Publicly available at the TI website - http://www.national.com/mpf/LM/LM86.html + https://www.ti.com/lit/ds/symlink/lm86.pdf * Analog Devices ADM1020 @@ -57,7 +59,9 @@ Supported chips: Addresses scanned: I2C 0x4c - 0x4e - Datasheet: Publicly available at the Analog Devices website + Datasheet: Publicly available at the DigiKey website + + https://media.digikey.com/pdf/Data%20Sheets/Analog%20Devices%20PDFs/ADM1020.pdf * Analog Devices ADM1021 @@ -65,7 +69,9 @@ Supported chips: Addresses scanned: I2C 0x18 - 0x1a, 0x29 - 0x2b, 0x4c - 0x4e - Datasheet: Publicly available at the Analog Devices website + Datasheet: Publicly available at the DigiKey website + + https://media.digikey.com/pdf/Data%20Sheets/Analog%20Devices%20PDFs/ADM1021.pdf * Analog Devices ADM1021A/ADM1023 @@ -75,15 +81,18 @@ Supported chips: Datasheet: Publicly available at the Analog Devices website + https://media.digikey.com/pdf/Data%20Sheets/Analog%20Devices%20PDFs/ADM1021A.pdf + https://media.digikey.com/pdf/Data%20Sheets/Analog%20Devices%20PDFs/ADM1023.pdf + * Analog Devices ADM1032 Prefix: 'adm1032' Addresses scanned: I2C 0x4c and 0x4d - Datasheet: Publicly available at the ON Semiconductor website + Datasheet: Publicly available at the DigiKey website - https://www.onsemi.com/PowerSolutions/product.do?id=ADM1032 + https://www.digikey.com/htmldatasheets/production/53140/0/0/1/ADM1032.pdf * Analog Devices ADT7461 @@ -111,9 +120,9 @@ Supported chips: Addresses scanned: I2C 0x4b and 0x4c - Datasheet: Publicly available at the ON Semiconductor website + Datasheet: Publicly available at the DigiKey website - https://www.onsemi.com/PowerSolutions/product.do?id=ADT7481 + https://www.digikey.com/htmldatasheets/production/234607/0/0/1/ADT7481.pdf * Analog Devices ADT7482 @@ -191,7 +200,9 @@ Supported chips: Addresses scanned: I2C 0x18 - 0x1a, 0x29 - 0x2b, 0x4c - 0x4e - Datasheet: Publicly available at the Maxim website + Datasheet: Publicly available at the Analog Devices website + + https://www.analog.com/media/en/technical-documentation/data-sheets/MAX1617.pdf * Maxim MAX1617A @@ -199,7 +210,9 @@ Supported chips: Addresses scanned: I2C 0x18 - 0x1a, 0x29 - 0x2b, 0x4c - 0x4e - Datasheet: Publicly available at the Maxim website + Datasheet: Publicly available at the Analog Devices website + + https://www.analog.com/media/en/technical-documentation/data-sheets/MAX1617A.pdf * Maxim MAX6642 @@ -207,9 +220,9 @@ Supported chips: Addresses scanned: I2C 0x48-0x4f - Datasheet: Publicly available at the Maxim website + Datasheet: Publicly available at the Analog Devices website - http://datasheets.maxim-ic.com/en/ds/MAX6642.pdf + https://www.analog.com/media/en/technical-documentation/data-sheets/MAX6642.pdf * Maxim MAX6646 @@ -217,9 +230,9 @@ Supported chips: Addresses scanned: I2C 0x4d - Datasheet: Publicly available at the Maxim website + Datasheet: Publicly available at the Analog Devices website - http://www.maxim-ic.com/quick_view2.cfm/qv_pk/3497 + https://www.analog.com/media/en/technical-documentation/data-sheets/MAX6646-MAX6649.pdf * Maxim MAX6647 @@ -227,9 +240,9 @@ Supported chips: Addresses scanned: I2C 0x4e - Datasheet: Publicly available at the Maxim website + Datasheet: Publicly available at the Analog Devices website - http://www.maxim-ic.com/quick_view2.cfm/qv_pk/3497 + https://www.analog.com/media/en/technical-documentation/data-sheets/MAX6646-MAX6649.pdf * Maxim MAX6648 @@ -237,9 +250,9 @@ Supported chips: Addresses scanned: I2C 0x4c - Datasheet: Publicly available at the Maxim website + Datasheet: Publicly available at the Analog Devices website - http://www.maxim-ic.com/quick_view2.cfm/qv_pk/3500 + https://www.analog.com/media/en/technical-documentation/data-sheets/MAX6648-MAX6692.pdf * Maxim MAX6649 @@ -247,9 +260,9 @@ Supported chips: Addresses scanned: I2C 0x4c - Datasheet: Publicly available at the Maxim website + Datasheet: Publicly available at the Analog Devices website - http://www.maxim-ic.com/quick_view2.cfm/qv_pk/3497 + https://www.analog.com/media/en/technical-documentation/data-sheets/MAX1617.pdf * Maxim MAX6654 @@ -259,9 +272,9 @@ Supported chips: 0x4c, 0x4d and 0x4e - Datasheet: Publicly available at the Maxim website + Datasheet: Publicly available at the Analog Devices website - https://www.maximintegrated.com/en/products/sensors/MAX6654.html + https://www.analog.com/media/en/technical-documentation/data-sheets/MAX6654.pdf * Maxim MAX6657 @@ -269,9 +282,9 @@ Supported chips: Addresses scanned: I2C 0x4c - Datasheet: Publicly available at the Maxim website + Datasheet: Publicly available at the Analog Devices website - http://www.maxim-ic.com/quick_view2.cfm/qv_pk/2578 + https://www.analog.com/media/en/technical-documentation/data-sheets/MAX6657-MAX6659.pdf * Maxim MAX6658 @@ -279,9 +292,9 @@ Supported chips: Addresses scanned: I2C 0x4c - Datasheet: Publicly available at the Maxim website + Datasheet: Publicly available at the Analog Devices website - http://www.maxim-ic.com/quick_view2.cfm/qv_pk/2578 + https://www.analog.com/media/en/technical-documentation/data-sheets/MAX6657-MAX6659.pdf * Maxim MAX6659 @@ -289,9 +302,9 @@ Supported chips: Addresses scanned: I2C 0x4c, 0x4d, 0x4e - Datasheet: Publicly available at the Maxim website + Datasheet: Publicly available at the Analog Devices website - http://www.maxim-ic.com/quick_view2.cfm/qv_pk/2578 + https://www.analog.com/media/en/technical-documentation/data-sheets/MAX6657-MAX6659.pdf * Maxim MAX6680 @@ -301,9 +314,9 @@ Supported chips: 0x4c, 0x4d and 0x4e - Datasheet: Publicly available at the Maxim website + Datasheet: Publicly available at the Analog Devices website - http://www.maxim-ic.com/quick_view2.cfm/qv_pk/3370 + https://www.analog.com/media/en/technical-documentation/data-sheets/MAX6680-MAX6681.pdf * Maxim MAX6681 @@ -313,9 +326,9 @@ Supported chips: 0x4c, 0x4d and 0x4e - Datasheet: Publicly available at the Maxim website + Datasheet: Publicly available at the Analog Devices website - http://www.maxim-ic.com/quick_view2.cfm/qv_pk/3370 + https://www.analog.com/media/en/technical-documentation/data-sheets/MAX6680-MAX6681.pdf * Maxim MAX6692 @@ -323,9 +336,9 @@ Supported chips: Addresses scanned: I2C 0x4c - Datasheet: Publicly available at the Maxim website + Datasheet: Publicly available at the Analog Devices website - http://www.maxim-ic.com/quick_view2.cfm/qv_pk/3500 + https://www.analog.com/media/en/technical-documentation/data-sheets/MAX6648-MAX6692.pdf * Maxim MAX6695 @@ -333,9 +346,9 @@ Supported chips: Addresses scanned: I2C 0x18 - Datasheet: Publicly available at the Maxim website + Datasheet: Publicly available at the Analog Devices website - http://www.maxim-ic.com/datasheet/index.mvp/id/4199 + https://www.analog.com/media/en/technical-documentation/data-sheets/MAX6695-MAX6696.pdf * Maxim MAX6696 @@ -345,9 +358,9 @@ Supported chips: 0x4c, 0x4d and 0x4e - Datasheet: Publicly available at the Maxim website + Datasheet: Publicly available at the Analog Devices website - http://www.maxim-ic.com/datasheet/index.mvp/id/4199 + https://www.analog.com/media/en/technical-documentation/data-sheets/MAX6695-MAX6696.pdf * Winbond/Nuvoton W83L771W/G @@ -355,7 +368,9 @@ Supported chips: Addresses scanned: I2C 0x4c - Datasheet: No longer available + Datasheet: Publicly available at the DigiKey website + + https://mm.digikey.com/Volume0/opasdata/d220001/medias/docus/1128/W83L771W%2CW83L771G.pdf * Winbond/Nuvoton W83L771AWG/ASG @@ -381,7 +396,7 @@ Supported chips: Datasheet: Publicly available at Nuvoton website - https://www.nuvoton.com/resource-files/Nuvoton_NCT7717U_Datasheet_V111.pdf + https://www.nuvoton.com/resource-files/Nuvoton_NCT7717U_Datasheet_V111.pdf * Nuvoton NCT7718 @@ -391,7 +406,7 @@ Supported chips: Datasheet: Publicly available at Nuvoton website - https://www.nuvoton.com/resource-files/Nuvoton_NCT7718W_Datasheet_V11.pdf + https://www.nuvoton.com/resource-files/Nuvoton_NCT7718W_Datasheet_V11.pdf * Philips/NXP SA56004X @@ -401,7 +416,7 @@ Supported chips: Datasheet: Publicly available at NXP website - http://ics.nxp.com/products/interface/datasheet/sa56004x.pdf + https://www.nxp.com/docs/en/data-sheet/SA56004X.pdf * GMT G781 @@ -437,7 +452,9 @@ Supported chips: Addresses scanned: I2C 0x18 - 0x1a, 0x29 - 0x2b, 0x4c - 0x4e - Datasheets: Publicly available at the Philips website + Datasheets: Publicly available at the DigiKey website + + https://www.digikey.com/htmldatasheets/production/97606/0/0/1/ne1617.pdf * Philips NE1618 @@ -445,7 +462,9 @@ Supported chips: Addresses scanned: I2C 0x18 - 0x1a, 0x29 - 0x2b, 0x4c - 0x4e - Datasheets: Publicly available at the Philips website + Datasheets: Publicly available at the DigiKey website + + https://media.digikey.com/pdf/Data%20Sheets/NXP%20PDFs/NE1618.pdf * Genesys Logic GL523SM @@ -453,7 +472,7 @@ Supported chips: Addresses scanned: I2C 0x18 - 0x1a, 0x29 - 0x2b, 0x4c - 0x4e - Datasheet: + Datasheet: No longer available at Genesys Logic website * TI THMC10 @@ -461,7 +480,7 @@ Supported chips: Addresses scanned: I2C 0x18 - 0x1a, 0x29 - 0x2b, 0x4c - 0x4e - Datasheet: Publicly available at the TI website + Datasheet: No longer available at the TI website * Onsemi MC1066 @@ -469,7 +488,7 @@ Supported chips: Addresses scanned: I2C 0x18 - 0x1a, 0x29 - 0x2b, 0x4c - 0x4e - Datasheet: Publicly available at the Onsemi website + Datasheet: No longer available at the Onsemi website Author: Jean Delvare From 59526e85427155d55074ddffef7f9d3a6a44d70e Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sun, 7 Sep 2025 14:07:08 -0700 Subject: [PATCH 047/110] hwmon: (max127) Rely on subsystem locking Attribute access is now serialized in the hardware monitoring core, so locking in the driver code is no longer necessary. Drop it. Signed-off-by: Guenter Roeck --- drivers/hwmon/max127.c | 23 +++-------------------- 1 file changed, 3 insertions(+), 20 deletions(-) diff --git a/drivers/hwmon/max127.c b/drivers/hwmon/max127.c index a9aab8862f5e..5102d86d2619 100644 --- a/drivers/hwmon/max127.c +++ b/drivers/hwmon/max127.c @@ -45,7 +45,6 @@ #define MAX127_SIGN_BIT BIT(11) struct max127_data { - struct mutex lock; struct i2c_client *client; u8 ctrl_byte[MAX127_NUM_CHANNELS]; }; @@ -121,21 +120,16 @@ static int max127_read_input(struct max127_data *data, int channel, long *val) struct i2c_client *client = data->client; u8 ctrl_byte = data->ctrl_byte[channel]; - mutex_lock(&data->lock); - status = max127_select_channel(client, ctrl_byte); if (status) - goto exit; + return status; status = max127_read_channel(client, &raw); if (status) - goto exit; + return status; *val = max127_process_raw(ctrl_byte, raw); - -exit: - mutex_unlock(&data->lock); - return status; + return 0; } static int max127_read_min(struct max127_data *data, int channel, long *val) @@ -170,8 +164,6 @@ static int max127_write_min(struct max127_data *data, int channel, long val) { u8 ctrl; - mutex_lock(&data->lock); - ctrl = data->ctrl_byte[channel]; if (val <= -MAX127_FULL_RANGE) { ctrl |= (MAX127_CTRL_RNG | MAX127_CTRL_BIP); @@ -182,23 +174,15 @@ static int max127_write_min(struct max127_data *data, int channel, long val) ctrl &= ~MAX127_CTRL_BIP; } data->ctrl_byte[channel] = ctrl; - - mutex_unlock(&data->lock); - return 0; } static int max127_write_max(struct max127_data *data, int channel, long val) { - mutex_lock(&data->lock); - if (val >= MAX127_FULL_RANGE) data->ctrl_byte[channel] |= MAX127_CTRL_RNG; else data->ctrl_byte[channel] &= ~MAX127_CTRL_RNG; - - mutex_unlock(&data->lock); - return 0; } @@ -315,7 +299,6 @@ static int max127_probe(struct i2c_client *client) return -ENOMEM; data->client = client; - mutex_init(&data->lock); for (i = 0; i < ARRAY_SIZE(data->ctrl_byte); i++) data->ctrl_byte[i] = (MAX127_CTRL_START | MAX127_SET_CHANNEL(i)); From 4e94552a57099752efaea682826dbc1c31077337 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sun, 7 Sep 2025 14:09:50 -0700 Subject: [PATCH 048/110] hwmon: (lm95234) Rely on subsystem locking Attribute access is now serialized in the hardware monitoring core, so locking in the driver code is no longer necessary. Drop it. Signed-off-by: Guenter Roeck --- drivers/hwmon/lm95234.c | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/drivers/hwmon/lm95234.c b/drivers/hwmon/lm95234.c index 7da6c8f07332..387b3ba81dbf 100644 --- a/drivers/hwmon/lm95234.c +++ b/drivers/hwmon/lm95234.c @@ -14,7 +14,6 @@ #include #include #include -#include #include #include @@ -54,7 +53,6 @@ static const unsigned short normal_i2c[] = { /* Client data (each client gets its own) */ struct lm95234_data { struct regmap *regmap; - struct mutex update_lock; enum chips type; }; @@ -107,19 +105,14 @@ static ssize_t lm95234_hyst_set(struct lm95234_data *data, long val) u32 tcrit; int ret; - mutex_lock(&data->update_lock); - ret = regmap_read(data->regmap, LM95234_REG_TCRIT1(0), &tcrit); if (ret) - goto unlock; + return ret; val = DIV_ROUND_CLOSEST(clamp_val(val, -255000, 255000), 1000); val = clamp_val((int)tcrit - val, 0, 31); - ret = regmap_write(data->regmap, LM95234_REG_TCRIT_HYST, val); -unlock: - mutex_unlock(&data->update_lock); - return ret; + return regmap_write(data->regmap, LM95234_REG_TCRIT_HYST, val); } static int lm95234_crit_reg(int channel) @@ -526,7 +519,6 @@ static int lm95234_probe(struct i2c_client *client) return PTR_ERR(regmap); data->regmap = regmap; - mutex_init(&data->update_lock); /* Initialize the LM95234 chip */ err = lm95234_init_client(dev, regmap); From 3f5b5795b4f797bcb6fad32cf32a431c1198f112 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sun, 7 Sep 2025 14:11:08 -0700 Subject: [PATCH 049/110] hwmon: (lm92) Rely on subsystem locking Attribute access is now serialized in the hardware monitoring core, so locking in the driver code is no longer necessary. Drop it. Signed-off-by: Guenter Roeck --- drivers/hwmon/lm92.c | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/drivers/hwmon/lm92.c b/drivers/hwmon/lm92.c index 0be439b38ee1..91a6b7525bb6 100644 --- a/drivers/hwmon/lm92.c +++ b/drivers/hwmon/lm92.c @@ -32,7 +32,6 @@ #include #include #include -#include #include #include @@ -78,7 +77,6 @@ static inline u8 ALARMS_FROM_REG(s16 reg) /* Client data (each client gets its own) */ struct lm92_data { struct regmap *regmap; - struct mutex update_lock; int resolution; }; @@ -199,15 +197,11 @@ static int lm92_temp_write(struct lm92_data *data, u32 attr, long val) break; case hwmon_temp_crit_hyst: val = clamp_val(val, -120000, 220000); - mutex_lock(&data->update_lock); err = regmap_read(regmap, LM92_REG_TEMP_CRIT, &temp); if (err) - goto unlock; + return err; val = TEMP_TO_REG(TEMP_FROM_REG(temp) - val, data->resolution); - err = regmap_write(regmap, LM92_REG_TEMP_HYST, val); -unlock: - mutex_unlock(&data->update_lock); - return err; + return regmap_write(regmap, LM92_REG_TEMP_HYST, val); default: return -EOPNOTSUPP; } @@ -396,7 +390,6 @@ static int lm92_probe(struct i2c_client *client) data->regmap = regmap; data->resolution = (unsigned long)i2c_get_match_data(client); - mutex_init(&data->update_lock); /* Initialize the chipset */ err = lm92_init_client(regmap); From 0f192ec0014e6d7bf7b713719ac6a4a031ee6964 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sun, 7 Sep 2025 14:30:30 -0700 Subject: [PATCH 050/110] hwmon: (hs3001) Rely on subsystem locking Attribute access is now serialized in the hardware monitoring core, so locking in the driver code is no longer necessary. Drop it. Signed-off-by: Guenter Roeck --- drivers/hwmon/hs3001.c | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/drivers/hwmon/hs3001.c b/drivers/hwmon/hs3001.c index 24ed3fb9a43a..50c6c15f8b18 100644 --- a/drivers/hwmon/hs3001.c +++ b/drivers/hwmon/hs3001.c @@ -42,7 +42,6 @@ struct hs3001_data { struct i2c_client *client; - struct mutex i2c_lock; /* lock for sending i2c commands */ u32 wait_time; /* in us */ int temperature; /* in milli degree */ u32 humidity; /* in milli % */ @@ -112,12 +111,9 @@ static int hs3001_read(struct device *dev, enum hwmon_sensor_types type, struct i2c_client *client = data->client; int ret; - mutex_lock(&data->i2c_lock); ret = i2c_master_send(client, NULL, 0); - if (ret < 0) { - mutex_unlock(&data->i2c_lock); + if (ret < 0) return ret; - } /* * Sensor needs some time to process measurement depending on @@ -126,8 +122,6 @@ static int hs3001_read(struct device *dev, enum hwmon_sensor_types type, fsleep(data->wait_time); ret = hs3001_data_fetch_command(client, data); - mutex_unlock(&data->i2c_lock); - if (ret < 0) return ret; @@ -211,8 +205,6 @@ static int hs3001_probe(struct i2c_client *client) data->wait_time = (HS3001_WAKEUP_TIME + HS3001_14BIT_RESOLUTION + HS3001_14BIT_RESOLUTION); - mutex_init(&data->i2c_lock); - hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, data, From 5395aa802e69817cdbdfdf159abcbecc5b1a1abd Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sun, 7 Sep 2025 14:32:38 -0700 Subject: [PATCH 051/110] hwmon: (sbtsi_temp) Rely on subsystem locking Attribute access is now serialized in the hardware monitoring core, so locking in the driver code is no longer necessary. Drop it. Signed-off-by: Guenter Roeck --- drivers/hwmon/sbtsi_temp.c | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/drivers/hwmon/sbtsi_temp.c b/drivers/hwmon/sbtsi_temp.c index a6c439e376ff..c5b2488c4c7f 100644 --- a/drivers/hwmon/sbtsi_temp.c +++ b/drivers/hwmon/sbtsi_temp.c @@ -12,7 +12,6 @@ #include #include #include -#include #include #include @@ -52,7 +51,6 @@ /* Each client has this additional data */ struct sbtsi_data { struct i2c_client *client; - struct mutex lock; bool ext_range_mode; bool read_order; }; @@ -94,7 +92,6 @@ static int sbtsi_read(struct device *dev, enum hwmon_sensor_types type, switch (attr) { case hwmon_temp_input: - mutex_lock(&data->lock); if (data->read_order) { temp_dec = i2c_smbus_read_byte_data(data->client, SBTSI_REG_TEMP_DEC); temp_int = i2c_smbus_read_byte_data(data->client, SBTSI_REG_TEMP_INT); @@ -102,19 +99,14 @@ static int sbtsi_read(struct device *dev, enum hwmon_sensor_types type, temp_int = i2c_smbus_read_byte_data(data->client, SBTSI_REG_TEMP_INT); temp_dec = i2c_smbus_read_byte_data(data->client, SBTSI_REG_TEMP_DEC); } - mutex_unlock(&data->lock); break; case hwmon_temp_max: - mutex_lock(&data->lock); temp_int = i2c_smbus_read_byte_data(data->client, SBTSI_REG_TEMP_HIGH_INT); temp_dec = i2c_smbus_read_byte_data(data->client, SBTSI_REG_TEMP_HIGH_DEC); - mutex_unlock(&data->lock); break; case hwmon_temp_min: - mutex_lock(&data->lock); temp_int = i2c_smbus_read_byte_data(data->client, SBTSI_REG_TEMP_LOW_INT); temp_dec = i2c_smbus_read_byte_data(data->client, SBTSI_REG_TEMP_LOW_DEC); - mutex_unlock(&data->lock); break; default: return -EINVAL; @@ -158,15 +150,11 @@ static int sbtsi_write(struct device *dev, enum hwmon_sensor_types type, val = clamp_val(val, SBTSI_TEMP_MIN, SBTSI_TEMP_MAX); sbtsi_mc_to_reg(val, &temp_int, &temp_dec); - mutex_lock(&data->lock); err = i2c_smbus_write_byte_data(data->client, reg_int, temp_int); if (err) - goto exit; + return err; - err = i2c_smbus_write_byte_data(data->client, reg_dec, temp_dec); -exit: - mutex_unlock(&data->lock); - return err; + return i2c_smbus_write_byte_data(data->client, reg_dec, temp_dec); } static umode_t sbtsi_is_visible(const void *data, @@ -219,7 +207,6 @@ static int sbtsi_probe(struct i2c_client *client) return -ENOMEM; data->client = client; - mutex_init(&data->lock); err = i2c_smbus_read_byte_data(data->client, SBTSI_REG_CONFIG); if (err < 0) From caff6fba5166b6da14ed00c64ad8acf376676e54 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sun, 7 Sep 2025 15:08:30 -0700 Subject: [PATCH 052/110] hwmon: (ina2xx) Rely on subsystem locking Attribute access is now serialized in the hardware monitoring core, so locking in the driver code is no longer necessary. Drop it. Signed-off-by: Guenter Roeck --- drivers/hwmon/ina2xx.c | 28 ++++++++++------------------ 1 file changed, 10 insertions(+), 18 deletions(-) diff --git a/drivers/hwmon/ina2xx.c b/drivers/hwmon/ina2xx.c index bc3c1f7314b3..69ac0468dee4 100644 --- a/drivers/hwmon/ina2xx.c +++ b/drivers/hwmon/ina2xx.c @@ -156,7 +156,6 @@ struct ina2xx_data { long rshunt; long current_lsb_uA; long power_lsb_uW; - struct mutex config_lock; struct regmap *regmap; struct i2c_client *client; }; @@ -390,22 +389,19 @@ static int ina226_alert_limit_read(struct ina2xx_data *data, u32 mask, int reg, int regval; int ret; - mutex_lock(&data->config_lock); ret = regmap_read(regmap, INA226_MASK_ENABLE, ®val); if (ret) - goto abort; + return ret; if (regval & mask) { ret = regmap_read(regmap, INA226_ALERT_LIMIT, ®val); if (ret) - goto abort; + return ret; *val = ina2xx_get_value(data, reg, regval); } else { *val = 0; } -abort: - mutex_unlock(&data->config_lock); - return ret; + return 0; } static int ina226_alert_limit_write(struct ina2xx_data *data, u32 mask, int reg, long val) @@ -421,23 +417,20 @@ static int ina226_alert_limit_write(struct ina2xx_data *data, u32 mask, int reg, * due to register write sequence. Then, only enable the alert * if the value is non-zero. */ - mutex_lock(&data->config_lock); ret = regmap_update_bits(regmap, INA226_MASK_ENABLE, INA226_ALERT_CONFIG_MASK, 0); if (ret < 0) - goto abort; + return ret; ret = regmap_write(regmap, INA226_ALERT_LIMIT, ina226_alert_to_reg(data, reg, val)); if (ret < 0) - goto abort; + return ret; if (val) - ret = regmap_update_bits(regmap, INA226_MASK_ENABLE, - INA226_ALERT_CONFIG_MASK, mask); -abort: - mutex_unlock(&data->config_lock); - return ret; + return regmap_update_bits(regmap, INA226_MASK_ENABLE, + INA226_ALERT_CONFIG_MASK, mask); + return 0; } static int ina2xx_chip_read(struct device *dev, u32 attr, long *val) @@ -859,9 +852,9 @@ static ssize_t shunt_resistor_store(struct device *dev, if (status < 0) return status; - mutex_lock(&data->config_lock); + hwmon_lock(dev); status = ina2xx_set_shunt(data, val); - mutex_unlock(&data->config_lock); + hwmon_unlock(dev); if (status < 0) return status; return count; @@ -951,7 +944,6 @@ static int ina2xx_probe(struct i2c_client *client) data->client = client; data->config = &ina2xx_config[chip]; data->chip = chip; - mutex_init(&data->config_lock); data->regmap = devm_regmap_init_i2c(client, &ina2xx_regmap_config); if (IS_ERR(data->regmap)) { From 53dfa12299c15f256e6199db1328e8bccd52ffd9 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sun, 7 Sep 2025 15:12:01 -0700 Subject: [PATCH 053/110] hwmon: (sht4x) Rely on subsystem locking Attribute access is now serialized in the hardware monitoring core, so locking in the driver code is no longer necessary. Drop it. Signed-off-by: Guenter Roeck --- drivers/hwmon/sht4x.c | 40 +++++++++++----------------------------- 1 file changed, 11 insertions(+), 29 deletions(-) diff --git a/drivers/hwmon/sht4x.c b/drivers/hwmon/sht4x.c index 6c9b776237c2..5abe1227e109 100644 --- a/drivers/hwmon/sht4x.c +++ b/drivers/hwmon/sht4x.c @@ -55,7 +55,6 @@ DECLARE_CRC8_TABLE(sht4x_crc8_table); /** * struct sht4x_data - All the data required to operate an SHT4X chip * @client: the i2c client associated with the SHT4X - * @lock: a mutex that is used to prevent parallel access to the i2c client * @heating_complete: the time that the last heating finished * @data_pending: true if and only if there are measurements to retrieve after heating * @heater_power: the power at which the heater will be started @@ -68,7 +67,6 @@ DECLARE_CRC8_TABLE(sht4x_crc8_table); */ struct sht4x_data { struct i2c_client *client; - struct mutex lock; /* atomic read data updates */ unsigned long heating_complete; /* in jiffies */ bool data_pending; u32 heater_power; /* in milli-watts */ @@ -87,7 +85,7 @@ struct sht4x_data { */ static int sht4x_read_values(struct sht4x_data *data) { - int ret = 0; + int ret; u16 t_ticks, rh_ticks; unsigned long next_update; struct i2c_client *client = data->client; @@ -96,8 +94,6 @@ static int sht4x_read_values(struct sht4x_data *data) u8 raw_data[SHT4X_RESPONSE_LENGTH]; unsigned long curr_jiffies; - mutex_lock(&data->lock); - curr_jiffies = jiffies; if (time_before(curr_jiffies, data->heating_complete)) msleep(jiffies_to_msecs(data->heating_complete - curr_jiffies)); @@ -110,11 +106,11 @@ static int sht4x_read_values(struct sht4x_data *data) msecs_to_jiffies(data->update_interval); if (data->valid && time_before_eq(jiffies, next_update)) - goto unlock; + return 0; ret = i2c_master_send(client, cmd, SHT4X_CMD_LEN); if (ret < 0) - goto unlock; + return ret; usleep_range(SHT4X_MEAS_DELAY_HPM, SHT4X_MEAS_DELAY_HPM + SHT4X_DELAY_EXTRA); } @@ -123,7 +119,7 @@ static int sht4x_read_values(struct sht4x_data *data) if (ret != SHT4X_RESPONSE_LENGTH) { if (ret >= 0) ret = -ENODATA; - goto unlock; + return ret; } t_ticks = raw_data[0] << 8 | raw_data[1]; @@ -132,26 +128,20 @@ static int sht4x_read_values(struct sht4x_data *data) crc = crc8(sht4x_crc8_table, &raw_data[0], SHT4X_WORD_LEN, CRC8_INIT_VALUE); if (crc != raw_data[2]) { dev_err(&client->dev, "data integrity check failed\n"); - ret = -EIO; - goto unlock; + return -EIO; } crc = crc8(sht4x_crc8_table, &raw_data[3], SHT4X_WORD_LEN, CRC8_INIT_VALUE); if (crc != raw_data[5]) { dev_err(&client->dev, "data integrity check failed\n"); - ret = -EIO; - goto unlock; + return -EIO; } data->temperature = ((21875 * (int32_t)t_ticks) >> 13) - 45000; data->humidity = ((15625 * (int32_t)rh_ticks) >> 13) - 6000; data->last_updated = jiffies; data->valid = true; - ret = 0; - -unlock: - mutex_unlock(&data->lock); - return ret; + return 0; } static ssize_t sht4x_interval_write(struct sht4x_data *data, long val) @@ -287,22 +277,16 @@ static ssize_t heater_enable_store(struct device *dev, heating_time_bound = 1100; } - mutex_lock(&data->lock); - - if (time_before(jiffies, data->heating_complete)) { - ret = -EBUSY; - goto unlock; - } + if (time_before(jiffies, data->heating_complete)) + return -EBUSY; ret = i2c_master_send(data->client, &cmd, SHT4X_CMD_LEN); if (ret < 0) - goto unlock; + return ret; data->heating_complete = jiffies + msecs_to_jiffies(heating_time_bound); data->data_pending = true; -unlock: - mutex_unlock(&data->lock); - return ret; + return 0; } static ssize_t heater_power_show(struct device *dev, @@ -422,8 +406,6 @@ static int sht4x_probe(struct i2c_client *client) data->heater_time = 1000; data->heating_complete = jiffies; - mutex_init(&data->lock); - crc8_populate_msb(sht4x_crc8_table, SHT4X_CRC8_POLYNOMIAL); ret = i2c_master_send(client, cmd, SHT4X_CMD_LEN); From 3ed9a9274724b3d36ed5348feecf54316497b3ee Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sun, 7 Sep 2025 15:14:20 -0700 Subject: [PATCH 054/110] hwmon: (ina3221) Rely on subsystem locking Attribute access is now serialized in the hardware monitoring core, so locking in the driver code is no longer necessary. Drop it. Signed-off-by: Guenter Roeck --- drivers/hwmon/ina3221.c | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/drivers/hwmon/ina3221.c b/drivers/hwmon/ina3221.c index ce0e3f214f5b..5ecc68dcf169 100644 --- a/drivers/hwmon/ina3221.c +++ b/drivers/hwmon/ina3221.c @@ -11,7 +11,6 @@ #include #include #include -#include #include #include #include @@ -115,7 +114,6 @@ struct ina3221_input { * @regmap: Register map of the device * @fields: Register fields of the device * @inputs: Array of channel input source specific structures - * @lock: mutex lock to serialize sysfs attribute accesses * @reg_config: Register value of INA3221_CONFIG * @summation_shunt_resistor: equivalent shunt resistor value for summation * @summation_channel_control: Value written to SCC field in INA3221_MASK_ENABLE @@ -126,7 +124,6 @@ struct ina3221_data { struct regmap *regmap; struct regmap_field *fields[F_MAX_FIELDS]; struct ina3221_input inputs[INA3221_NUM_CHANNELS]; - struct mutex lock; u32 reg_config; int summation_shunt_resistor; u32 summation_channel_control; @@ -530,11 +527,8 @@ fail: static int ina3221_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel, long *val) { - struct ina3221_data *ina = dev_get_drvdata(dev); int ret; - mutex_lock(&ina->lock); - switch (type) { case hwmon_chip: ret = ina3221_read_chip(dev, attr, val); @@ -550,20 +544,14 @@ static int ina3221_read(struct device *dev, enum hwmon_sensor_types type, ret = -EOPNOTSUPP; break; } - - mutex_unlock(&ina->lock); - return ret; } static int ina3221_write(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel, long val) { - struct ina3221_data *ina = dev_get_drvdata(dev); int ret; - mutex_lock(&ina->lock); - switch (type) { case hwmon_chip: ret = ina3221_write_chip(dev, attr, val); @@ -579,9 +567,6 @@ static int ina3221_write(struct device *dev, enum hwmon_sensor_types type, ret = -EOPNOTSUPP; break; } - - mutex_unlock(&ina->lock); - return ret; } @@ -886,7 +871,6 @@ static int ina3221_probe(struct i2c_client *client) } ina->pm_dev = dev; - mutex_init(&ina->lock); dev_set_drvdata(dev, ina); /* Enable PM runtime -- status is suspended by default */ @@ -925,7 +909,6 @@ fail: /* pm_runtime_put_noidle() will decrease the PM refcount until 0 */ for (i = 0; i < INA3221_NUM_CHANNELS; i++) pm_runtime_put_noidle(ina->pm_dev); - mutex_destroy(&ina->lock); return ret; } @@ -941,8 +924,6 @@ static void ina3221_remove(struct i2c_client *client) /* pm_runtime_put_noidle() will decrease the PM refcount until 0 */ for (i = 0; i < INA3221_NUM_CHANNELS; i++) pm_runtime_put_noidle(ina->pm_dev); - - mutex_destroy(&ina->lock); } static int ina3221_suspend(struct device *dev) From 8b932c6af4430a3c48aa291f0df63f28298a4af2 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sun, 7 Sep 2025 15:36:01 -0700 Subject: [PATCH 055/110] hwmon: (k10temp) Rely on subsystem locking Attribute access is now serialized in the hardware monitoring core, so locking in the driver code is no longer necessary. Drop it. Signed-off-by: Guenter Roeck --- drivers/hwmon/k10temp.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/drivers/hwmon/k10temp.c b/drivers/hwmon/k10temp.c index b98d5ec72c4f..7fc1d97a9971 100644 --- a/drivers/hwmon/k10temp.c +++ b/drivers/hwmon/k10temp.c @@ -31,9 +31,6 @@ static bool force; module_param(force, bool, 0444); MODULE_PARM_DESC(force, "force loading on processors with erratum 319"); -/* Provide lock for writing to NB_SMU_IND_ADDR */ -static DEFINE_MUTEX(nb_smu_ind_mutex); - #ifndef PCI_DEVICE_ID_AMD_15H_M70H_NB_F3 #define PCI_DEVICE_ID_AMD_15H_M70H_NB_F3 0x15b3 #endif @@ -137,12 +134,10 @@ static void read_tempreg_pci(struct pci_dev *pdev, u32 *regval) static void amd_nb_index_read(struct pci_dev *pdev, unsigned int devfn, unsigned int base, int offset, u32 *val) { - mutex_lock(&nb_smu_ind_mutex); pci_bus_write_config_dword(pdev->bus, devfn, base, offset); pci_bus_read_config_dword(pdev->bus, devfn, base + 4, val); - mutex_unlock(&nb_smu_ind_mutex); } static void read_htcreg_nb_f15(struct pci_dev *pdev, u32 *regval) From 3e9c967f30a6dc57e4386c85ee5b75caee7246e8 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sun, 7 Sep 2025 20:41:27 -0700 Subject: [PATCH 056/110] hwmon: (mr75203) Drop unnecessary include file The driver does not perform any locking and thus does not need to include mutex.h. Drop the unnecessary include file. Signed-off-by: Guenter Roeck --- drivers/hwmon/mr75203.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/hwmon/mr75203.c b/drivers/hwmon/mr75203.c index 7848198f8996..32c1e42e1278 100644 --- a/drivers/hwmon/mr75203.c +++ b/drivers/hwmon/mr75203.c @@ -14,7 +14,6 @@ #include #include #include -#include #include #include #include From 8c1750a5645a4a9e382501acae14917658eb2fd0 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sun, 7 Sep 2025 20:45:35 -0700 Subject: [PATCH 057/110] hwmon: (powr1220) Rely on subsystem locking Attribute access is now serialized in the hardware monitoring core, so locking in the driver code is no longer necessary. Drop it. Signed-off-by: Guenter Roeck --- drivers/hwmon/powr1220.c | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/drivers/hwmon/powr1220.c b/drivers/hwmon/powr1220.c index 5f9ca6543530..06a2c56016d1 100644 --- a/drivers/hwmon/powr1220.c +++ b/drivers/hwmon/powr1220.c @@ -16,7 +16,6 @@ #include #include #include -#include #include #define ADC_STEP_MV 2 @@ -75,7 +74,6 @@ enum powr1220_adc_values { struct powr1220_data { struct i2c_client *client; - struct mutex update_lock; u8 max_channels; bool adc_valid[MAX_POWR1220_ADC_VALUES]; /* the next value is in jiffies */ @@ -111,8 +109,6 @@ static int powr1220_read_adc(struct device *dev, int ch_num) int result; int adc_range = 0; - mutex_lock(&data->update_lock); - if (time_after(jiffies, data->adc_last_updated[ch_num] + HZ) || !data->adc_valid[ch_num]) { /* @@ -128,8 +124,8 @@ static int powr1220_read_adc(struct device *dev, int ch_num) /* set the attenuator and mux */ result = i2c_smbus_write_byte_data(data->client, ADC_MUX, adc_range | ch_num); - if (result) - goto exit; + if (result < 0) + return result; /* * wait at least Tconvert time (200 us) for the @@ -140,14 +136,14 @@ static int powr1220_read_adc(struct device *dev, int ch_num) /* get the ADC reading */ result = i2c_smbus_read_byte_data(data->client, ADC_VALUE_LOW); if (result < 0) - goto exit; + return result; reading = result >> 4; /* get the upper half of the reading */ result = i2c_smbus_read_byte_data(data->client, ADC_VALUE_HIGH); if (result < 0) - goto exit; + return result; reading |= result << 4; @@ -163,10 +159,6 @@ static int powr1220_read_adc(struct device *dev, int ch_num) } else { result = data->adc_values[ch_num]; } - -exit: - mutex_unlock(&data->update_lock); - return result; } @@ -302,7 +294,6 @@ static int powr1220_probe(struct i2c_client *client) break; } - mutex_init(&data->update_lock); data->client = client; hwmon_dev = devm_hwmon_device_register_with_info(&client->dev, From fa035d98dbaeb45bf3057b171813dc45d0b40c86 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sun, 7 Sep 2025 20:51:28 -0700 Subject: [PATCH 058/110] hwmon: (ftsteutates) Rely on subsystem locking Attribute access is now serialized in the hardware monitoring core, so locking in the driver code is no longer necessary. Drop it. Signed-off-by: Guenter Roeck --- drivers/hwmon/ftsteutates.c | 84 +++++++++++-------------------------- 1 file changed, 25 insertions(+), 59 deletions(-) diff --git a/drivers/hwmon/ftsteutates.c b/drivers/hwmon/ftsteutates.c index 8aeec16a7a90..08dcc6a7fb62 100644 --- a/drivers/hwmon/ftsteutates.c +++ b/drivers/hwmon/ftsteutates.c @@ -12,7 +12,6 @@ #include #include #include -#include #include #include @@ -62,10 +61,6 @@ enum WATCHDOG_RESOLUTION { struct fts_data { struct i2c_client *client; - /* update sensor data lock */ - struct mutex update_lock; - /* read/write register lock */ - struct mutex access_lock; unsigned long last_updated; /* in jiffies */ struct watchdog_device wdd; enum WATCHDOG_RESOLUTION resolution; @@ -98,21 +93,15 @@ static int fts_read_byte(struct i2c_client *client, unsigned short reg) { int ret; unsigned char page = reg >> 8; - struct fts_data *data = dev_get_drvdata(&client->dev); - - mutex_lock(&data->access_lock); dev_dbg(&client->dev, "page select - page: 0x%.02x\n", page); ret = i2c_smbus_write_byte_data(client, FTS_PAGE_SELECT_REG, page); if (ret < 0) - goto error; + return ret; reg &= 0xFF; ret = i2c_smbus_read_byte_data(client, reg); dev_dbg(&client->dev, "read - reg: 0x%.02x: val: 0x%.02x\n", reg, ret); - -error: - mutex_unlock(&data->access_lock); return ret; } @@ -121,22 +110,16 @@ static int fts_write_byte(struct i2c_client *client, unsigned short reg, { int ret; unsigned char page = reg >> 8; - struct fts_data *data = dev_get_drvdata(&client->dev); - - mutex_lock(&data->access_lock); dev_dbg(&client->dev, "page select - page: 0x%.02x\n", page); ret = i2c_smbus_write_byte_data(client, FTS_PAGE_SELECT_REG, page); if (ret < 0) - goto error; + return ret; reg &= 0xFF; dev_dbg(&client->dev, "write - reg: 0x%.02x: val: 0x%.02x\n", reg, value); ret = i2c_smbus_write_byte_data(client, reg, value); - -error: - mutex_unlock(&data->access_lock); return ret; } @@ -145,44 +128,40 @@ error: /*****************************************************************************/ static int fts_update_device(struct fts_data *data) { - int i; - int err = 0; + int i, err; - mutex_lock(&data->update_lock); if (!time_after(jiffies, data->last_updated + 2 * HZ) && data->valid) - goto exit; + return 0; err = fts_read_byte(data->client, FTS_DEVICE_STATUS_REG); if (err < 0) - goto exit; + return err; data->valid = !!(err & 0x02); /* Data not ready yet */ - if (unlikely(!data->valid)) { - err = -EAGAIN; - goto exit; - } + if (unlikely(!data->valid)) + return -EAGAIN; err = fts_read_byte(data->client, FTS_FAN_PRESENT_REG); if (err < 0) - goto exit; + return err; data->fan_present = err; err = fts_read_byte(data->client, FTS_FAN_EVENT_REG); if (err < 0) - goto exit; + return err; data->fan_alarm = err; for (i = 0; i < FTS_NO_FAN_SENSORS; i++) { if (data->fan_present & BIT(i)) { err = fts_read_byte(data->client, FTS_REG_FAN_INPUT(i)); if (err < 0) - goto exit; + return err; data->fan_input[i] = err; err = fts_read_byte(data->client, FTS_REG_FAN_SOURCE(i)); if (err < 0) - goto exit; + return err; data->fan_source[i] = err; } else { data->fan_input[i] = 0; @@ -192,27 +171,24 @@ static int fts_update_device(struct fts_data *data) err = fts_read_byte(data->client, FTS_SENSOR_EVENT_REG); if (err < 0) - goto exit; + return err; data->temp_alarm = err; for (i = 0; i < FTS_NO_TEMP_SENSORS; i++) { err = fts_read_byte(data->client, FTS_REG_TEMP_INPUT(i)); if (err < 0) - goto exit; + return err; data->temp_input[i] = err; } for (i = 0; i < FTS_NO_VOLT_SENSORS; i++) { err = fts_read_byte(data->client, FTS_REG_VOLT(i)); if (err < 0) - goto exit; + return err; data->volt[i] = err; } data->last_updated = jiffies; - err = 0; -exit: - mutex_unlock(&data->update_lock); - return err; + return 0; } /*****************************************************************************/ @@ -470,18 +446,14 @@ static int fts_write(struct device *dev, enum hwmon_sensor_types type, u32 attr, if (val) return -EINVAL; - mutex_lock(&data->update_lock); ret = fts_read_byte(data->client, FTS_REG_TEMP_CONTROL(channel)); - if (ret >= 0) - ret = fts_write_byte(data->client, FTS_REG_TEMP_CONTROL(channel), - ret | 0x1); - if (ret >= 0) - data->valid = false; - - mutex_unlock(&data->update_lock); if (ret < 0) return ret; - + ret = fts_write_byte(data->client, FTS_REG_TEMP_CONTROL(channel), + ret | 0x1); + if (ret < 0) + return ret; + data->valid = false; return 0; default: break; @@ -493,18 +465,14 @@ static int fts_write(struct device *dev, enum hwmon_sensor_types type, u32 attr, if (val) return -EINVAL; - mutex_lock(&data->update_lock); ret = fts_read_byte(data->client, FTS_REG_FAN_CONTROL(channel)); - if (ret >= 0) - ret = fts_write_byte(data->client, FTS_REG_FAN_CONTROL(channel), - ret | 0x1); - if (ret >= 0) - data->valid = false; - - mutex_unlock(&data->update_lock); if (ret < 0) return ret; - + ret = fts_write_byte(data->client, FTS_REG_FAN_CONTROL(channel), + ret | 0x1); + if (ret < 0) + return ret; + data->valid = false; return 0; default: break; @@ -648,8 +616,6 @@ static int fts_probe(struct i2c_client *client) if (!data) return -ENOMEM; - mutex_init(&data->update_lock); - mutex_init(&data->access_lock); data->client = client; dev_set_drvdata(&client->dev, data); From 3da03e4e0e16674dd6be57eda2cc5c2ba5543035 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sun, 7 Sep 2025 11:44:37 -0700 Subject: [PATCH 059/110] hwmon: (ina238) Rely on subsystem locking Attribute access is now serialized in the hardware monitoring core, so locking in the driver code is no longer necessary. Drop it. Signed-off-by: Guenter Roeck --- drivers/hwmon/ina238.c | 26 +++++--------------------- 1 file changed, 5 insertions(+), 21 deletions(-) diff --git a/drivers/hwmon/ina238.c b/drivers/hwmon/ina238.c index 356d19b7675c..ff67b03189f7 100644 --- a/drivers/hwmon/ina238.c +++ b/drivers/hwmon/ina238.c @@ -117,7 +117,6 @@ struct ina238_config { struct ina238_data { const struct ina238_config *config; struct i2c_client *client; - struct mutex config_lock; struct regmap *regmap; u32 rshunt; int gain; @@ -607,31 +606,18 @@ static int ina238_read(struct device *dev, enum hwmon_sensor_types type, static int ina238_write(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel, long val) { - struct ina238_data *data = dev_get_drvdata(dev); - int err; - - mutex_lock(&data->config_lock); - switch (type) { case hwmon_in: - err = ina238_write_in(dev, attr, channel, val); - break; + return ina238_write_in(dev, attr, channel, val); case hwmon_curr: - err = ina238_write_curr(dev, attr, val); - break; + return ina238_write_curr(dev, attr, val); case hwmon_power: - err = ina238_write_power_max(dev, val); - break; + return ina238_write_power_max(dev, val); case hwmon_temp: - err = ina238_write_temp_max(dev, val); - break; + return ina238_write_temp_max(dev, val); default: - err = -EOPNOTSUPP; - break; + return -EOPNOTSUPP; } - - mutex_unlock(&data->config_lock); - return err; } static umode_t ina238_is_visible(const void *drvdata, @@ -757,8 +743,6 @@ static int ina238_probe(struct i2c_client *client) /* set the device type */ data->config = &ina238_config[chip]; - mutex_init(&data->config_lock); - data->regmap = devm_regmap_init_i2c(client, &ina238_regmap_config); if (IS_ERR(data->regmap)) { dev_err(dev, "failed to allocate register map\n"); From a640a80bf02dd2c10c62bbad9ccfb85829b03b5d Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sun, 7 Sep 2025 21:36:47 -0700 Subject: [PATCH 060/110] hwmon: (lm95241) Rely on subsystem locking Attribute access is now serialized in the hardware monitoring core, so locking in the driver code is no longer necessary. Drop it. Signed-off-by: Guenter Roeck --- drivers/hwmon/lm95241.c | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/drivers/hwmon/lm95241.c b/drivers/hwmon/lm95241.c index cad0a0ff8416..456381b0938e 100644 --- a/drivers/hwmon/lm95241.c +++ b/drivers/hwmon/lm95241.c @@ -15,7 +15,6 @@ #include #include #include -#include #include #define DEVNAME "lm95241" @@ -75,7 +74,6 @@ static const u8 lm95241_reg_address[] = { /* Client data (each client gets its own) */ struct lm95241_data { struct i2c_client *client; - struct mutex update_lock; unsigned long last_updated; /* in jiffies */ unsigned long interval; /* in milli-seconds */ bool valid; /* false until following fields are valid */ @@ -102,8 +100,6 @@ static struct lm95241_data *lm95241_update_device(struct device *dev) struct lm95241_data *data = dev_get_drvdata(dev); struct i2c_client *client = data->client; - mutex_lock(&data->update_lock); - if (time_after(jiffies, data->last_updated + msecs_to_jiffies(data->interval)) || !data->valid) { @@ -120,9 +116,6 @@ static struct lm95241_data *lm95241_update_device(struct device *dev) data->last_updated = jiffies; data->valid = true; } - - mutex_unlock(&data->update_lock); - return data; } @@ -204,8 +197,6 @@ static int lm95241_write_chip(struct device *dev, u32 attr, int channel, u8 config; int ret; - mutex_lock(&data->update_lock); - switch (attr) { case hwmon_chip_update_interval: config = data->config & ~CFG_CRMASK; @@ -231,7 +222,6 @@ static int lm95241_write_chip(struct device *dev, u32 attr, int channel, ret = -EOPNOTSUPP; break; } - mutex_unlock(&data->update_lock); return ret; } @@ -242,8 +232,6 @@ static int lm95241_write_temp(struct device *dev, u32 attr, int channel, struct i2c_client *client = data->client; int ret; - mutex_lock(&data->update_lock); - switch (attr) { case hwmon_temp_min: if (channel == 1) { @@ -313,9 +301,6 @@ static int lm95241_write_temp(struct device *dev, u32 attr, int channel, ret = -EOPNOTSUPP; break; } - - mutex_unlock(&data->update_lock); - return ret; } @@ -443,7 +428,6 @@ static int lm95241_probe(struct i2c_client *client) return -ENOMEM; data->client = client; - mutex_init(&data->update_lock); /* Initialize the LM95241 chip */ lm95241_init_client(client, data); From 1cfad0931e7b495245fd5092fc95bc4cc0ea1b3f Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sun, 7 Sep 2025 21:38:32 -0700 Subject: [PATCH 061/110] hwmon: (aht10) Rely on subsystem locking Attribute access is now serialized in the hardware monitoring core, so locking in the driver code is no longer necessary. Drop it. Signed-off-by: Guenter Roeck --- drivers/hwmon/aht10.c | 24 +++--------------------- 1 file changed, 3 insertions(+), 21 deletions(-) diff --git a/drivers/hwmon/aht10.c b/drivers/hwmon/aht10.c index d1c55e2eb479..8b90b661c393 100644 --- a/drivers/hwmon/aht10.c +++ b/drivers/hwmon/aht10.c @@ -60,8 +60,6 @@ MODULE_DEVICE_TABLE(i2c, aht10_id); /** * struct aht10_data - All the data required to operate an AHT10/AHT20 chip * @client: the i2c client associated with the AHT10/AHT20 - * @lock: a mutex that is used to prevent parallel access to the - * i2c client * @min_poll_interval: the minimum poll interval * While the poll rate limit is not 100% necessary, * the datasheet recommends that a measurement @@ -81,11 +79,6 @@ MODULE_DEVICE_TABLE(i2c, aht10_id); struct aht10_data { struct i2c_client *client; - /* - * Prevent simultaneous access to the i2c - * client and previous_poll_time - */ - struct mutex lock; ktime_t min_poll_interval; ktime_t previous_poll_time; int temperature; @@ -168,32 +161,24 @@ static int aht10_read_values(struct aht10_data *data) u8 raw_data[AHT20_MEAS_SIZE]; struct i2c_client *client = data->client; - mutex_lock(&data->lock); - if (!aht10_polltime_expired(data)) { - mutex_unlock(&data->lock); + if (!aht10_polltime_expired(data)) return 0; - } res = i2c_master_send(client, cmd_meas, sizeof(cmd_meas)); - if (res < 0) { - mutex_unlock(&data->lock); + if (res < 0) return res; - } usleep_range(AHT10_MEAS_DELAY, AHT10_MEAS_DELAY + AHT10_DELAY_EXTRA); res = i2c_master_recv(client, raw_data, data->meas_size); if (res != data->meas_size) { - mutex_unlock(&data->lock); if (res >= 0) return -ENODATA; return res; } - if (data->crc8 && crc8_check(raw_data, data->meas_size)) { - mutex_unlock(&data->lock); + if (data->crc8 && crc8_check(raw_data, data->meas_size)) return -EIO; - } hum = ((u32)raw_data[1] << 12u) | ((u32)raw_data[2] << 4u) | @@ -210,7 +195,6 @@ static int aht10_read_values(struct aht10_data *data) data->humidity = hum; data->previous_poll_time = ktime_get_boottime(); - mutex_unlock(&data->lock); return 0; } @@ -358,8 +342,6 @@ static int aht10_probe(struct i2c_client *client) break; } - mutex_init(&data->lock); - res = aht10_init(data); if (res < 0) return res; From ed2bee47de004df200915c596267450b683ef7a1 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Mon, 8 Sep 2025 13:14:25 -0700 Subject: [PATCH 062/110] hwmon: (adt7411) Rely on subsystem locking MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Attribute access is now serialized in the hardware monitoring core, so locking in the driver code is no longer necessary. Drop it. Signed-off-by: Guenter Roeck Reviewed-by: Nuno Sá --- drivers/hwmon/adt7411.c | 59 +++++++++++------------------------------ 1 file changed, 16 insertions(+), 43 deletions(-) diff --git a/drivers/hwmon/adt7411.c b/drivers/hwmon/adt7411.c index 08d0effd97f7..b9991a69e6c6 100644 --- a/drivers/hwmon/adt7411.c +++ b/drivers/hwmon/adt7411.c @@ -11,7 +11,6 @@ #include #include #include -#include #include #include #include @@ -99,8 +98,6 @@ static const u8 adt7411_in_alarm_bits[] = { }; struct adt7411_data { - struct mutex device_lock; /* for "atomic" device accesses */ - struct mutex update_lock; unsigned long next_update; long vref_cached; struct i2c_client *client; @@ -110,55 +107,41 @@ struct adt7411_data { /* * When reading a register containing (up to 4) lsb, all associated * msb-registers get locked by the hardware. After _one_ of those msb is read, - * _all_ are unlocked. In order to use this locking correctly, reading lsb/msb - * is protected here with a mutex, too. + * _all_ are unlocked. */ static int adt7411_read_10_bit(struct i2c_client *client, u8 lsb_reg, - u8 msb_reg, u8 lsb_shift) + u8 msb_reg, u8 lsb_shift) { - struct adt7411_data *data = i2c_get_clientdata(client); int val, tmp; - mutex_lock(&data->device_lock); - val = i2c_smbus_read_byte_data(client, lsb_reg); if (val < 0) - goto exit_unlock; + return val; tmp = (val >> lsb_shift) & 3; val = i2c_smbus_read_byte_data(client, msb_reg); + if (val < 0) + return val; - if (val >= 0) - val = (val << 2) | tmp; - - exit_unlock: - mutex_unlock(&data->device_lock); - + val = (val << 2) | tmp; return val; } static int adt7411_modify_bit(struct i2c_client *client, u8 reg, u8 bit, - bool flag) + bool flag) { - struct adt7411_data *data = i2c_get_clientdata(client); int ret, val; - mutex_lock(&data->device_lock); - ret = i2c_smbus_read_byte_data(client, reg); if (ret < 0) - goto exit_unlock; + return ret; if (flag) val = ret | bit; else val = ret & ~bit; - ret = i2c_smbus_write_byte_data(client, reg, val); - - exit_unlock: - mutex_unlock(&data->device_lock); - return ret; + return i2c_smbus_write_byte_data(client, reg, val); } static ssize_t adt7411_show_bit(struct device *dev, @@ -186,12 +169,11 @@ static ssize_t adt7411_set_bit(struct device *dev, if (ret || flag > 1) return -EINVAL; + hwmon_lock(dev); ret = adt7411_modify_bit(client, s_attr2->index, s_attr2->nr, flag); - /* force update */ - mutex_lock(&data->update_lock); data->next_update = jiffies; - mutex_unlock(&data->update_lock); + hwmon_unlock(dev); return ret < 0 ? ret : count; } @@ -294,10 +276,9 @@ static int adt7411_read_in_chan(struct device *dev, u32 attr, int channel, int reg, lsb_reg, lsb_shift; int nr = channel - 1; - mutex_lock(&data->update_lock); ret = adt7411_update_vref(dev); if (ret < 0) - goto exit_unlock; + return ret; switch (attr) { case hwmon_in_input: @@ -307,7 +288,7 @@ static int adt7411_read_in_chan(struct device *dev, u32 attr, int channel, ADT7411_REG_EXT_TEMP_AIN1_MSB + nr, lsb_shift); if (ret < 0) - goto exit_unlock; + return ret; *val = ret * data->vref_cached / 1024; ret = 0; break; @@ -318,7 +299,7 @@ static int adt7411_read_in_chan(struct device *dev, u32 attr, int channel, : ADT7411_REG_IN_HIGH(channel); ret = i2c_smbus_read_byte_data(client, reg); if (ret < 0) - goto exit_unlock; + return ret; *val = ret * data->vref_cached / 256; ret = 0; break; @@ -329,8 +310,6 @@ static int adt7411_read_in_chan(struct device *dev, u32 attr, int channel, ret = -EOPNOTSUPP; break; } - exit_unlock: - mutex_unlock(&data->update_lock); return ret; } @@ -457,10 +436,9 @@ static int adt7411_write_in_chan(struct device *dev, u32 attr, int channel, struct i2c_client *client = data->client; int ret, reg; - mutex_lock(&data->update_lock); ret = adt7411_update_vref(dev); if (ret < 0) - goto exit_unlock; + return ret; val = clamp_val(val, 0, 255 * data->vref_cached / 256); val = DIV_ROUND_CLOSEST(val * 256, data->vref_cached); @@ -472,13 +450,10 @@ static int adt7411_write_in_chan(struct device *dev, u32 attr, int channel, reg = ADT7411_REG_IN_HIGH(channel); break; default: - ret = -EOPNOTSUPP; - goto exit_unlock; + return -EOPNOTSUPP; } ret = i2c_smbus_write_byte_data(client, reg, val); - exit_unlock: - mutex_unlock(&data->update_lock); return ret; } @@ -679,8 +654,6 @@ static int adt7411_probe(struct i2c_client *client) i2c_set_clientdata(client, data); data->client = client; - mutex_init(&data->device_lock); - mutex_init(&data->update_lock); ret = adt7411_init_device(data); if (ret < 0) From fd0443a92480420c163e708406a92752138c9b4b Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Mon, 8 Sep 2025 13:40:55 -0700 Subject: [PATCH 063/110] hwmon: (ltc2947-core) Rely on subsystem locking MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Attribute access is now serialized in the hardware monitoring core, so locking in the driver code is no longer necessary. Drop it. Signed-off-by: Guenter Roeck Reviewed-by: Nuno Sá --- drivers/hwmon/ltc2947-core.c | 32 +++++--------------------------- 1 file changed, 5 insertions(+), 27 deletions(-) diff --git a/drivers/hwmon/ltc2947-core.c b/drivers/hwmon/ltc2947-core.c index 90f70f732b41..ad7120d1e469 100644 --- a/drivers/hwmon/ltc2947-core.c +++ b/drivers/hwmon/ltc2947-core.c @@ -120,12 +120,6 @@ struct ltc2947_data { struct regmap *map; struct device *dev; - /* - * The mutex is needed because the device has 2 memory pages. When - * reading/writing the correct page needs to be set so that, the - * complete sequence select_page->read/write needs to be protected. - */ - struct mutex lock; u32 lsb_energy; bool gpio_out; }; @@ -181,13 +175,9 @@ static int ltc2947_val_read(struct ltc2947_data *st, const u8 reg, int ret; u64 __val = 0; - mutex_lock(&st->lock); - ret = regmap_write(st->map, LTC2947_REG_PAGE_CTRL, page); - if (ret) { - mutex_unlock(&st->lock); + if (ret) return ret; - } dev_dbg(st->dev, "Read val, reg:%02X, p:%d sz:%zu\n", reg, page, size); @@ -207,8 +197,6 @@ static int ltc2947_val_read(struct ltc2947_data *st, const u8 reg, break; } - mutex_unlock(&st->lock); - if (ret) return ret; @@ -242,13 +230,10 @@ static int ltc2947_val_write(struct ltc2947_data *st, const u8 reg, { int ret; - mutex_lock(&st->lock); /* set device on correct page */ ret = regmap_write(st->map, LTC2947_REG_PAGE_CTRL, page); - if (ret) { - mutex_unlock(&st->lock); + if (ret) return ret; - } dev_dbg(st->dev, "Write val, r:%02X, p:%d, sz:%zu, val:%016llX\n", reg, page, size, val); @@ -265,8 +250,6 @@ static int ltc2947_val_write(struct ltc2947_data *st, const u8 reg, break; } - mutex_unlock(&st->lock); - return ret; } @@ -295,11 +278,9 @@ static int ltc2947_alarm_read(struct ltc2947_data *st, const u8 reg, memset(alarms, 0, sizeof(alarms)); - mutex_lock(&st->lock); - ret = regmap_write(st->map, LTC2947_REG_PAGE_CTRL, LTC2947_PAGE0); if (ret) - goto unlock; + return ret; dev_dbg(st->dev, "Read alarm, reg:%02X, mask:%02X\n", reg, mask); /* @@ -310,13 +291,11 @@ static int ltc2947_alarm_read(struct ltc2947_data *st, const u8 reg, ret = regmap_bulk_read(st->map, LTC2947_REG_STATUS, alarms, sizeof(alarms)); if (ret) - goto unlock; + return ret; /* get the alarm */ *val = !!(alarms[offset] & mask); -unlock: - mutex_unlock(&st->lock); - return ret; + return 0; } static int ltc2947_read_temp(struct device *dev, const u32 attr, long *val, @@ -1100,7 +1079,6 @@ int ltc2947_core_probe(struct regmap *map, const char *name) st->map = map; st->dev = dev; dev_set_drvdata(dev, st); - mutex_init(&st->lock); ret = ltc2947_setup(st); if (ret) From 1ba1fd1f6057ad5e1b4c55762b5de3d18dea4cde Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Mon, 8 Sep 2025 16:22:27 -0700 Subject: [PATCH 064/110] hwmon: (peci) Rely on subsystem locking Attribute access is now serialized in the hardware monitoring core, so locking in the driver code is no longer necessary. Drop it. Signed-off-by: Guenter Roeck --- drivers/hwmon/peci/common.h | 3 -- drivers/hwmon/peci/cputemp.c | 72 +++++++++-------------------------- drivers/hwmon/peci/dimmtemp.c | 17 ++------- 3 files changed, 23 insertions(+), 69 deletions(-) diff --git a/drivers/hwmon/peci/common.h b/drivers/hwmon/peci/common.h index 734506b0eca2..92a7ee1925bc 100644 --- a/drivers/hwmon/peci/common.h +++ b/drivers/hwmon/peci/common.h @@ -1,7 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* Copyright (c) 2021 Intel Corporation */ -#include #include #ifndef __PECI_HWMON_COMMON_H @@ -13,12 +12,10 @@ * struct peci_sensor_state - PECI state information * @valid: flag to indicate the sensor value is valid * @last_updated: time of the last update in jiffies - * @lock: mutex to protect sensor access */ struct peci_sensor_state { bool valid; unsigned long last_updated; - struct mutex lock; /* protect sensor access */ }; /** diff --git a/drivers/hwmon/peci/cputemp.c b/drivers/hwmon/peci/cputemp.c index b350c9a76894..b2fc936851e1 100644 --- a/drivers/hwmon/peci/cputemp.c +++ b/drivers/hwmon/peci/cputemp.c @@ -116,11 +116,9 @@ static int get_temp_target(struct peci_cputemp *priv, enum peci_temp_target_type { int ret; - mutex_lock(&priv->temp.target.state.lock); - ret = update_temp_target(priv); if (ret) - goto unlock; + return ret; switch (type) { case tcontrol_type: @@ -139,9 +137,6 @@ static int get_temp_target(struct peci_cputemp *priv, enum peci_temp_target_type ret = -EOPNOTSUPP; break; } -unlock: - mutex_unlock(&priv->temp.target.state.lock); - return ret; } @@ -177,26 +172,23 @@ static s32 dts_eight_dot_eight_to_millidegree(u16 val) static int get_die_temp(struct peci_cputemp *priv, long *val) { - int ret = 0; long tjmax; u16 temp; + int ret; - mutex_lock(&priv->temp.die.state.lock); if (!peci_sensor_need_update(&priv->temp.die.state)) goto skip_update; ret = peci_temp_read(priv->peci_dev, &temp); if (ret) - goto err_unlock; + return ret; - if (!dts_valid(temp)) { - ret = -EIO; - goto err_unlock; - } + if (!dts_valid(temp)) + return -EIO; ret = get_temp_target(priv, tjmax_type, &tjmax); if (ret) - goto err_unlock; + return ret; priv->temp.die.value = (s32)tjmax + dts_ten_dot_six_to_millidegree(temp); @@ -204,35 +196,30 @@ static int get_die_temp(struct peci_cputemp *priv, long *val) skip_update: *val = priv->temp.die.value; -err_unlock: - mutex_unlock(&priv->temp.die.state.lock); - return ret; + return 0; } static int get_dts(struct peci_cputemp *priv, long *val) { - int ret = 0; u16 thermal_margin; long tcontrol; u32 pcs; + int ret; - mutex_lock(&priv->temp.dts.state.lock); if (!peci_sensor_need_update(&priv->temp.dts.state)) goto skip_update; ret = peci_pcs_read(priv->peci_dev, PECI_PCS_THERMAL_MARGIN, 0, &pcs); if (ret) - goto err_unlock; + return ret; thermal_margin = FIELD_GET(DTS_MARGIN_MASK, pcs); - if (!dts_valid(thermal_margin)) { - ret = -EIO; - goto err_unlock; - } + if (!dts_valid(thermal_margin)) + return -EIO; ret = get_temp_target(priv, tcontrol_type, &tcontrol); if (ret) - goto err_unlock; + return ret; /* Note that the tcontrol should be available before calling it */ priv->temp.dts.value = @@ -242,35 +229,30 @@ static int get_dts(struct peci_cputemp *priv, long *val) skip_update: *val = priv->temp.dts.value; -err_unlock: - mutex_unlock(&priv->temp.dts.state.lock); - return ret; + return 0; } static int get_core_temp(struct peci_cputemp *priv, int core_index, long *val) { - int ret = 0; u16 core_dts_margin; long tjmax; u32 pcs; + int ret; - mutex_lock(&priv->temp.core[core_index].state.lock); if (!peci_sensor_need_update(&priv->temp.core[core_index].state)) goto skip_update; ret = peci_pcs_read(priv->peci_dev, PECI_PCS_MODULE_TEMP, core_index, &pcs); if (ret) - goto err_unlock; + return ret; core_dts_margin = FIELD_GET(PCS_MODULE_TEMP_MASK, pcs); - if (!dts_valid(core_dts_margin)) { - ret = -EIO; - goto err_unlock; - } + if (!dts_valid(core_dts_margin)) + return -EIO; ret = get_temp_target(priv, tjmax_type, &tjmax); if (ret) - goto err_unlock; + return ret; /* Note that the tjmax should be available before calling it */ priv->temp.core[core_index].value = @@ -280,9 +262,7 @@ static int get_core_temp(struct peci_cputemp *priv, int core_index, long *val) skip_update: *val = priv->temp.core[core_index].value; -err_unlock: - mutex_unlock(&priv->temp.core[core_index].state.lock); - return ret; + return 0; } static int cputemp_read_string(struct device *dev, enum hwmon_sensor_types type, @@ -431,18 +411,6 @@ static void check_resolved_cores(struct peci_cputemp *priv) bitmap_zero(priv->core_mask, CORE_NUMS_MAX); } -static void sensor_init(struct peci_cputemp *priv) -{ - int i; - - mutex_init(&priv->temp.target.state.lock); - mutex_init(&priv->temp.die.state.lock); - mutex_init(&priv->temp.dts.state.lock); - - for_each_set_bit(i, priv->core_mask, CORE_NUMS_MAX) - mutex_init(&priv->temp.core[i].state.lock); -} - static const struct hwmon_ops peci_cputemp_ops = { .is_visible = cputemp_is_visible, .read_string = cputemp_read_string, @@ -507,8 +475,6 @@ static int peci_cputemp_probe(struct auxiliary_device *adev, check_resolved_cores(priv); - sensor_init(priv); - hwmon_dev = devm_hwmon_device_register_with_info(priv->dev, priv->name, priv, &peci_cputemp_chip_info, NULL); diff --git a/drivers/hwmon/peci/dimmtemp.c b/drivers/hwmon/peci/dimmtemp.c index a281476c7a31..bd3e8715dfec 100644 --- a/drivers/hwmon/peci/dimmtemp.c +++ b/drivers/hwmon/peci/dimmtemp.c @@ -96,16 +96,15 @@ static int get_dimm_temp(struct peci_dimmtemp *priv, int dimm_no, long *val) { int dimm_order = dimm_no % priv->gen_info->dimm_idx_max; int chan_rank = dimm_no / priv->gen_info->dimm_idx_max; - int ret = 0; u32 data; + int ret; - mutex_lock(&priv->dimm[dimm_no].temp.state.lock); if (!peci_sensor_need_update(&priv->dimm[dimm_no].temp.state)) goto skip_update; ret = peci_pcs_read(priv->peci_dev, PECI_PCS_DDR_DIMM_TEMP, chan_rank, &data); if (ret) - goto unlock; + return ret; priv->dimm[dimm_no].temp.value = __dimm_temp(data, dimm_order) * MILLIDEGREE_PER_DEGREE; @@ -113,9 +112,7 @@ static int get_dimm_temp(struct peci_dimmtemp *priv, int dimm_no, long *val) skip_update: *val = priv->dimm[dimm_no].temp.value; -unlock: - mutex_unlock(&priv->dimm[dimm_no].temp.state.lock); - return ret; + return 0; } static int update_thresholds(struct peci_dimmtemp *priv, int dimm_no) @@ -145,10 +142,9 @@ static int get_dimm_thresholds(struct peci_dimmtemp *priv, enum peci_dimm_thresh { int ret; - mutex_lock(&priv->dimm[dimm_no].thresholds.state.lock); ret = update_thresholds(priv, dimm_no); if (ret) - goto unlock; + return ret; switch (type) { case temp_max_type: @@ -161,9 +157,6 @@ static int get_dimm_thresholds(struct peci_dimmtemp *priv, enum peci_dimm_thresh ret = -EOPNOTSUPP; break; } -unlock: - mutex_unlock(&priv->dimm[dimm_no].thresholds.state.lock); - return ret; } @@ -349,8 +342,6 @@ static int create_dimm_temp_info(struct peci_dimmtemp *priv) ret = create_dimm_temp_label(priv, i); if (ret) return ret; - mutex_init(&priv->dimm[i].thresholds.state.lock); - mutex_init(&priv->dimm[i].temp.state.lock); } dev = devm_hwmon_device_register_with_info(priv->dev, priv->name, priv, From c3fc3c63c9e3da3abd5927bb86e110288cae0932 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Mon, 8 Sep 2025 16:28:15 -0700 Subject: [PATCH 065/110] hwmon: (adt7x10) Rely on subsystem locking MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Attribute access is now serialized in the hardware monitoring core, so locking in the driver code is no longer necessary. Drop it. Signed-off-by: Guenter Roeck Reviewed-by: Nuno Sá --- drivers/hwmon/adt7x10.c | 27 +++++---------------------- 1 file changed, 5 insertions(+), 22 deletions(-) diff --git a/drivers/hwmon/adt7x10.c b/drivers/hwmon/adt7x10.c index 2d329391ed3f..d003ee3ebf06 100644 --- a/drivers/hwmon/adt7x10.c +++ b/drivers/hwmon/adt7x10.c @@ -15,7 +15,6 @@ #include #include #include -#include #include #include #include @@ -55,7 +54,6 @@ /* Each client has this additional data */ struct adt7x10_data { struct regmap *regmap; - struct mutex update_lock; u8 config; u8 oldconfig; bool valid; /* true if temperature valid */ @@ -137,17 +135,13 @@ static int adt7x10_temp_read(struct adt7x10_data *data, int index, long *val) unsigned int regval; int ret; - mutex_lock(&data->update_lock); if (index == adt7x10_temperature && !data->valid) { /* wait for valid temperature */ ret = adt7x10_temp_ready(data->regmap); - if (ret) { - mutex_unlock(&data->update_lock); + if (ret) return ret; - } data->valid = true; } - mutex_unlock(&data->update_lock); ret = regmap_read(data->regmap, ADT7X10_REG_TEMP[index], ®val); if (ret) @@ -159,13 +153,8 @@ static int adt7x10_temp_read(struct adt7x10_data *data, int index, long *val) static int adt7x10_temp_write(struct adt7x10_data *data, int index, long temp) { - int ret; - - mutex_lock(&data->update_lock); - ret = regmap_write(data->regmap, ADT7X10_REG_TEMP[index], - ADT7X10_TEMP_TO_REG(temp)); - mutex_unlock(&data->update_lock); - return ret; + return regmap_write(data->regmap, ADT7X10_REG_TEMP[index], + ADT7X10_TEMP_TO_REG(temp)); } static int adt7x10_hyst_read(struct adt7x10_data *data, int index, long *val) @@ -197,22 +186,17 @@ static int adt7x10_hyst_write(struct adt7x10_data *data, long hyst) unsigned int regval; int limit, ret; - mutex_lock(&data->update_lock); - /* convert absolute hysteresis value to a 4 bit delta value */ ret = regmap_read(data->regmap, ADT7X10_T_ALARM_HIGH, ®val); if (ret < 0) - goto abort; + return ret; limit = ADT7X10_REG_TO_TEMP(data, regval); hyst = clamp_val(hyst, ADT7X10_TEMP_MIN, ADT7X10_TEMP_MAX); regval = clamp_val(DIV_ROUND_CLOSEST(limit - hyst, 1000), 0, ADT7X10_T_HYST_MASK); - ret = regmap_write(data->regmap, ADT7X10_T_HYST, regval); -abort: - mutex_unlock(&data->update_lock); - return ret; + return regmap_write(data->regmap, ADT7X10_T_HYST, regval); } static int adt7x10_alarm_read(struct adt7x10_data *data, int index, long *val) @@ -344,7 +328,6 @@ int adt7x10_probe(struct device *dev, const char *name, int irq, data->regmap = regmap; dev_set_drvdata(dev, data); - mutex_init(&data->update_lock); /* configure as specified */ ret = regmap_read(regmap, ADT7X10_CONFIG, &config); From 00148a0a234e1c5b1266f3d81e27a15e68fb60f2 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Mon, 8 Sep 2025 16:29:03 -0700 Subject: [PATCH 066/110] hwmon: (sfctemp) Rely on subsystem locking Attribute access is now serialized in the hardware monitoring core, so locking in the driver code is no longer necessary. Drop it. Signed-off-by: Guenter Roeck --- drivers/hwmon/sfctemp.c | 36 ++++++++---------------------------- 1 file changed, 8 insertions(+), 28 deletions(-) diff --git a/drivers/hwmon/sfctemp.c b/drivers/hwmon/sfctemp.c index fb1da93383d7..b78b2c099a12 100644 --- a/drivers/hwmon/sfctemp.c +++ b/drivers/hwmon/sfctemp.c @@ -10,7 +10,6 @@ #include #include #include -#include #include #include #include @@ -49,8 +48,6 @@ #define SFCTEMP_K1000 81100L struct sfctemp { - /* serialize access to hardware register and enabled below */ - struct mutex lock; void __iomem *regs; struct clk *clk_sense; struct clk *clk_bus; @@ -92,15 +89,14 @@ static void sfctemp_stop(struct sfctemp *sfctemp) static int sfctemp_enable(struct sfctemp *sfctemp) { - int ret = 0; + int ret; - mutex_lock(&sfctemp->lock); if (sfctemp->enabled) - goto done; + return 0; ret = clk_prepare_enable(sfctemp->clk_bus); if (ret) - goto err; + return ret; ret = reset_control_deassert(sfctemp->rst_bus); if (ret) goto err_disable_bus; @@ -115,9 +111,7 @@ static int sfctemp_enable(struct sfctemp *sfctemp) sfctemp_power_up(sfctemp); sfctemp_run(sfctemp); sfctemp->enabled = true; -done: - mutex_unlock(&sfctemp->lock); - return ret; + return 0; err_disable_sense: clk_disable_unprepare(sfctemp->clk_sense); @@ -125,16 +119,13 @@ err_assert_bus: reset_control_assert(sfctemp->rst_bus); err_disable_bus: clk_disable_unprepare(sfctemp->clk_bus); -err: - mutex_unlock(&sfctemp->lock); return ret; } static int sfctemp_disable(struct sfctemp *sfctemp) { - mutex_lock(&sfctemp->lock); if (!sfctemp->enabled) - goto done; + return 0; sfctemp_stop(sfctemp); sfctemp_power_down(sfctemp); @@ -143,8 +134,6 @@ static int sfctemp_disable(struct sfctemp *sfctemp) reset_control_assert(sfctemp->rst_bus); clk_disable_unprepare(sfctemp->clk_bus); sfctemp->enabled = false; -done: - mutex_unlock(&sfctemp->lock); return 0; } @@ -155,22 +144,14 @@ static void sfctemp_disable_action(void *data) static int sfctemp_convert(struct sfctemp *sfctemp, long *val) { - int ret; - - mutex_lock(&sfctemp->lock); - if (!sfctemp->enabled) { - ret = -ENODATA; - goto out; - } + if (!sfctemp->enabled) + return -ENODATA; /* calculate temperature in milli Celcius */ *val = (long)((readl(sfctemp->regs) & SFCTEMP_DOUT_MSK) >> SFCTEMP_DOUT_POS) * SFCTEMP_Y1000 / SFCTEMP_Z - SFCTEMP_K1000; - ret = 0; -out: - mutex_unlock(&sfctemp->lock); - return ret; + return 0; } static umode_t sfctemp_is_visible(const void *data, enum hwmon_sensor_types type, @@ -263,7 +244,6 @@ static int sfctemp_probe(struct platform_device *pdev) return -ENOMEM; dev_set_drvdata(dev, sfctemp); - mutex_init(&sfctemp->lock); sfctemp->regs = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(sfctemp->regs)) From bf9a27c86e4b982ed9fe462d86608894aa7cb9b9 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Mon, 8 Sep 2025 17:33:39 -0700 Subject: [PATCH 067/110] hwmon: (lochnagar-hwmon) Rely on subsystem locking Attribute access is now serialized in the hardware monitoring core, so locking in the driver code is no longer necessary. Drop it. Signed-off-by: Guenter Roeck --- drivers/hwmon/lochnagar-hwmon.c | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/drivers/hwmon/lochnagar-hwmon.c b/drivers/hwmon/lochnagar-hwmon.c index 5202dddfd61e..c1ba72f6132e 100644 --- a/drivers/hwmon/lochnagar-hwmon.c +++ b/drivers/hwmon/lochnagar-hwmon.c @@ -10,7 +10,6 @@ #include #include -#include #include #include #include @@ -42,9 +41,6 @@ struct lochnagar_hwmon { struct regmap *regmap; long power_nsamples[ARRAY_SIZE(lochnagar_chan_names)]; - - /* Lock to ensure only a single sensor is read at a time */ - struct mutex sensor_lock; }; enum lochnagar_measure_mode { @@ -178,26 +174,20 @@ static int read_sensor(struct device *dev, int chan, u32 data; int ret; - mutex_lock(&priv->sensor_lock); - ret = do_measurement(regmap, chan, mode, nsamples); if (ret < 0) { dev_err(dev, "Failed to perform measurement: %d\n", ret); - goto error; + return ret; } ret = request_data(regmap, chan, &data); if (ret < 0) { dev_err(dev, "Failed to read measurement: %d\n", ret); - goto error; + return ret; } *val = float_to_long(data, precision); - -error: - mutex_unlock(&priv->sensor_lock); - - return ret; + return 0; } static int read_power(struct device *dev, int chan, long *val) @@ -378,8 +368,6 @@ static int lochnagar_hwmon_probe(struct platform_device *pdev) if (!priv) return -ENOMEM; - mutex_init(&priv->sensor_lock); - priv->regmap = dev_get_regmap(dev->parent, NULL); if (!priv->regmap) { dev_err(dev, "No register map found\n"); From d4469d53a9fbb9c3e17ab09424017a5918c894b5 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Tue, 9 Sep 2025 05:50:35 -0700 Subject: [PATCH 068/110] hwmon: (ltc4282) Rely on subsystem locking MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Attribute access is now serialized in the hardware monitoring core, so locking in the driver code is no longer necessary. Drop it. Signed-off-by: Guenter Roeck Reviewed-by: Nuno Sá --- drivers/hwmon/ltc4282.c | 32 ++++---------------------------- 1 file changed, 4 insertions(+), 28 deletions(-) diff --git a/drivers/hwmon/ltc4282.c b/drivers/hwmon/ltc4282.c index 44102879694a..b9cad89f2cd9 100644 --- a/drivers/hwmon/ltc4282.c +++ b/drivers/hwmon/ltc4282.c @@ -17,7 +17,6 @@ #include #include #include -#include #include #include #include @@ -131,8 +130,6 @@ struct ltc4282_cache { struct ltc4282_state { struct regmap *map; - /* Protect against multiple accesses to the device registers */ - struct mutex lock; struct clk_hw clk_hw; /* * Used to cache values for VDD/VSOURCE depending which will be used @@ -281,14 +278,12 @@ static int __ltc4282_read_alarm(struct ltc4282_state *st, u32 reg, u32 mask, static int ltc4282_read_alarm(struct ltc4282_state *st, u32 reg, u32 mask, long *val) { - guard(mutex)(&st->lock); return __ltc4282_read_alarm(st, reg, mask, val); } static int ltc4282_vdd_source_read_in(struct ltc4282_state *st, u32 channel, long *val) { - guard(mutex)(&st->lock); if (!st->in0_1_cache[channel].en) return -ENODATA; @@ -300,7 +295,6 @@ static int ltc4282_vdd_source_read_hist(struct ltc4282_state *st, u32 reg, { int ret; - guard(mutex)(&st->lock); if (!st->in0_1_cache[channel].en) { *val = *cached; return 0; @@ -317,7 +311,6 @@ static int ltc4282_vdd_source_read_hist(struct ltc4282_state *st, u32 reg, static int ltc4282_vdd_source_read_lim(struct ltc4282_state *st, u32 reg, u32 channel, u32 *cached, long *val) { - guard(mutex)(&st->lock); if (!st->in0_1_cache[channel].en) return ltc4282_read_voltage_byte_cached(st, reg, st->vfs_out, val, cached); @@ -328,7 +321,6 @@ static int ltc4282_vdd_source_read_lim(struct ltc4282_state *st, u32 reg, static int ltc4282_vdd_source_read_alm(struct ltc4282_state *st, u32 mask, u32 channel, long *val) { - guard(mutex)(&st->lock); if (!st->in0_1_cache[channel].en) { /* * Do this otherwise alarms can get confused because we clear @@ -412,9 +404,7 @@ static int ltc4282_read_in(struct ltc4282_state *st, u32 attr, long *val, channel, &st->in0_1_cache[channel].in_min_raw, val); case hwmon_in_enable: - scoped_guard(mutex, &st->lock) { - *val = st->in0_1_cache[channel].en; - } + *val = st->in0_1_cache[channel].en; return 0; case hwmon_in_fault: /* @@ -612,15 +602,11 @@ static int ltc4282_read(struct device *dev, enum hwmon_sensor_types type, case hwmon_power: return ltc4282_read_power(st, attr, val); case hwmon_energy: - scoped_guard(mutex, &st->lock) { - *val = st->energy_en; - } + *val = st->energy_en; return 0; case hwmon_energy64: - scoped_guard(mutex, &st->lock) { - if (st->energy_en) - return ltc4282_read_energy(st, (s64 *)val); - } + if (st->energy_en) + return ltc4282_read_energy(st, (s64 *)val); return -ENODATA; default: return -EOPNOTSUPP; @@ -688,7 +674,6 @@ static int __ltc4282_in_write_history(const struct ltc4282_state *st, u32 reg, static int ltc4282_in_write_history(struct ltc4282_state *st, u32 reg, long lowest, long highest, u32 fs) { - guard(mutex)(&st->lock); return __ltc4282_in_write_history(st, reg, lowest, highest, fs); } @@ -696,8 +681,6 @@ static int ltc4282_power_reset_hist(struct ltc4282_state *st) { int ret; - guard(mutex)(&st->lock); - ret = ltc4282_write_power_word(st, LTC4282_POWER_LOWEST, st->power_max); if (ret) @@ -803,7 +786,6 @@ static int ltc4282_vdd_source_write_lim(struct ltc4282_state *st, u32 reg, { int ret; - guard(mutex)(&st->lock); if (st->in0_1_cache[channel].en) ret = ltc4282_write_voltage_byte(st, reg, st->vfs_out, val); else @@ -821,7 +803,6 @@ static int ltc4282_vdd_source_reset_hist(struct ltc4282_state *st, int channel) if (channel == LTC4282_CHAN_VDD) lowest = st->vdd; - guard(mutex)(&st->lock); if (st->in0_1_cache[channel].en) { ret = __ltc4282_in_write_history(st, LTC4282_VSOURCE_LOWEST, lowest, 0, st->vfs_out); @@ -861,7 +842,6 @@ static int ltc4282_vdd_source_enable(struct ltc4282_state *st, int channel, int ret, other_chan = ~channel & 0x1; u8 __val = val; - guard(mutex)(&st->lock); if (st->in0_1_cache[channel].en == !!val) return 0; @@ -938,8 +918,6 @@ static int ltc4282_curr_reset_hist(struct ltc4282_state *st) { int ret; - guard(mutex)(&st->lock); - ret = __ltc4282_in_write_history(st, LTC4282_VSENSE_LOWEST, st->vsense_max, 0, 40 * MILLI); if (ret) @@ -974,7 +952,6 @@ static int ltc4282_energy_enable_set(struct ltc4282_state *st, long val) { int ret; - guard(mutex)(&st->lock); /* setting the bit halts the meter */ ret = regmap_update_bits(st->map, LTC4282_ADC_CTRL, LTC4282_METER_HALT_MASK, @@ -1699,7 +1676,6 @@ static int ltc4282_probe(struct i2c_client *i2c) if (ret) return ret; - mutex_init(&st->lock); hwmon = devm_hwmon_device_register_with_info(dev, "ltc4282", st, <c4282_chip_info, NULL); if (IS_ERR(hwmon)) From ca2363f8a294de31c78b05f7e97852eacdde02e6 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Tue, 9 Sep 2025 05:54:26 -0700 Subject: [PATCH 069/110] hwmon: (aquacomputer_d5next) Rely on subsystem locking Attribute access is now serialized in the hardware monitoring core, so locking in the driver code is no longer necessary. Drop it. Signed-off-by: Guenter Roeck --- drivers/hwmon/aquacomputer_d5next.c | 37 +++++------------------------ 1 file changed, 6 insertions(+), 31 deletions(-) diff --git a/drivers/hwmon/aquacomputer_d5next.c b/drivers/hwmon/aquacomputer_d5next.c index 0dcb8a3a691d..1ca70e726298 100644 --- a/drivers/hwmon/aquacomputer_d5next.c +++ b/drivers/hwmon/aquacomputer_d5next.c @@ -20,7 +20,6 @@ #include #include #include -#include #include #include @@ -551,7 +550,6 @@ struct aqc_data { struct hid_device *hdev; struct device *hwmon_dev; struct dentry *debugfs; - struct mutex mutex; /* Used for locking access when reading and writing PWM values */ enum kinds kind; const char *name; @@ -662,7 +660,6 @@ static void aqc_delay_ctrl_report(struct aqc_data *priv) } } -/* Expects the mutex to be locked */ static int aqc_get_ctrl_data(struct aqc_data *priv) { int ret; @@ -680,7 +677,6 @@ static int aqc_get_ctrl_data(struct aqc_data *priv) return ret; } -/* Expects the mutex to be locked */ static int aqc_send_ctrl_data(struct aqc_data *priv) { int ret; @@ -721,11 +717,9 @@ static int aqc_get_ctrl_val(struct aqc_data *priv, int offset, long *val, int ty { int ret; - mutex_lock(&priv->mutex); - ret = aqc_get_ctrl_data(priv); if (ret < 0) - goto unlock_and_return; + return ret; switch (type) { case AQC_BE16: @@ -737,9 +731,6 @@ static int aqc_get_ctrl_val(struct aqc_data *priv, int offset, long *val, int ty default: ret = -EINVAL; } - -unlock_and_return: - mutex_unlock(&priv->mutex); return ret; } @@ -747,11 +738,9 @@ static int aqc_set_ctrl_vals(struct aqc_data *priv, int *offsets, long *vals, in { int ret, i; - mutex_lock(&priv->mutex); - ret = aqc_get_ctrl_data(priv); if (ret < 0) - goto unlock_and_return; + return ret; for (i = 0; i < len; i++) { switch (types[i]) { @@ -762,18 +751,11 @@ static int aqc_set_ctrl_vals(struct aqc_data *priv, int *offsets, long *vals, in priv->buffer[offsets[i]] = (u8)vals[i]; break; default: - ret = -EINVAL; + return -EINVAL; } } - if (ret < 0) - goto unlock_and_return; - - ret = aqc_send_ctrl_data(priv); - -unlock_and_return: - mutex_unlock(&priv->mutex); - return ret; + return aqc_send_ctrl_data(priv); } static int aqc_set_ctrl_val(struct aqc_data *priv, int offset, long val, int type) @@ -953,13 +935,11 @@ static int aqc_legacy_read(struct aqc_data *priv) { int ret, i, sensor_value; - mutex_lock(&priv->mutex); - memset(priv->buffer, 0x00, priv->buffer_size); ret = hid_hw_raw_request(priv->hdev, priv->status_report_id, priv->buffer, priv->buffer_size, HID_FEATURE_REPORT, HID_REQ_GET_REPORT); if (ret < 0) - goto unlock_and_return; + return ret; /* Temperature sensor readings */ for (i = 0; i < priv->num_temp_sensors; i++) { @@ -1020,10 +1000,7 @@ static int aqc_legacy_read(struct aqc_data *priv) } priv->updated = jiffies; - -unlock_and_return: - mutex_unlock(&priv->mutex); - return ret; + return 0; } static int aqc_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, @@ -1870,8 +1847,6 @@ static int aqc_probe(struct hid_device *hdev, const struct hid_device_id *id) goto fail_and_close; } - mutex_init(&priv->mutex); - priv->hwmon_dev = hwmon_device_register_with_info(&hdev->dev, priv->name, priv, &aqc_chip_info, NULL); From 0517a5c70c6ebdc1d8945c8c0126d501fa4a4616 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Tue, 9 Sep 2025 05:59:24 -0700 Subject: [PATCH 070/110] hwmon: (gpd-fan) Rely on subsystem locking Attribute access is now serialized in the hardware monitoring core, so locking in the driver code is no longer necessary. Drop it. Signed-off-by: Guenter Roeck --- drivers/hwmon/gpd-fan.c | 56 ++++++++++------------------------------- 1 file changed, 13 insertions(+), 43 deletions(-) diff --git a/drivers/hwmon/gpd-fan.c b/drivers/hwmon/gpd-fan.c index 644dc3ca9df7..57caae9a23eb 100644 --- a/drivers/hwmon/gpd-fan.c +++ b/drivers/hwmon/gpd-fan.c @@ -26,9 +26,6 @@ static char *gpd_fan_board = ""; module_param(gpd_fan_board, charp, 0444); -// EC read/write locker, protecting a sequence of EC operations -static DEFINE_MUTEX(gpd_fan_sequence_lock); - enum gpd_board { win_mini, win4_6800u, @@ -507,87 +504,60 @@ static int gpd_fan_hwmon_read(__always_unused struct device *dev, { int ret; - ret = mutex_lock_interruptible(&gpd_fan_sequence_lock); - if (ret) - return ret; - if (type == hwmon_fan) { if (attr == hwmon_fan_input) { ret = gpd_read_rpm(); if (ret < 0) - goto OUT; + return ret; *val = ret; - ret = 0; - goto OUT; + return 0; } } else if (type == hwmon_pwm) { switch (attr) { case hwmon_pwm_enable: *val = gpd_driver_priv.pwm_enable; - ret = 0; - goto OUT; + return 0; case hwmon_pwm_input: ret = gpd_read_pwm(); if (ret < 0) - goto OUT; + return ret; *val = ret; - ret = 0; - goto OUT; + return 0; } } - ret = -EOPNOTSUPP; - -OUT: - mutex_unlock(&gpd_fan_sequence_lock); - return ret; + return -EOPNOTSUPP; } static int gpd_fan_hwmon_write(__always_unused struct device *dev, enum hwmon_sensor_types type, u32 attr, __always_unused int channel, long val) { - int ret; - - ret = mutex_lock_interruptible(&gpd_fan_sequence_lock); - if (ret) - return ret; - if (type == hwmon_pwm) { switch (attr) { case hwmon_pwm_enable: - if (!in_range(val, 0, 3)) { - ret = -EINVAL; - goto OUT; - } + if (!in_range(val, 0, 3)) + return -EINVAL; gpd_driver_priv.pwm_enable = val; gpd_set_pwm_enable(gpd_driver_priv.pwm_enable); - ret = 0; - goto OUT; + return 0; case hwmon_pwm_input: - if (!in_range(val, 0, 256)) { - ret = -ERANGE; - goto OUT; - } + if (!in_range(val, 0, 256)) + return -EINVAL; gpd_driver_priv.pwm_value = val; - ret = gpd_write_pwm(val); - goto OUT; + return gpd_write_pwm(val); } } - ret = -EOPNOTSUPP; - -OUT: - mutex_unlock(&gpd_fan_sequence_lock); - return ret; + return -EOPNOTSUPP; } static const struct hwmon_ops gpd_fan_ops = { From 75616264e0cca5ead432df7236653056b35cb0fc Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Tue, 9 Sep 2025 06:01:41 -0700 Subject: [PATCH 071/110] hwmon: (i5500_temp) Drop unnecessary include files The driver does not perform any locking, does not execute or use any sleep related functionality, and does not allocate memory. Drop the unnecessary include files. Signed-off-by: Guenter Roeck --- drivers/hwmon/i5500_temp.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/hwmon/i5500_temp.c b/drivers/hwmon/i5500_temp.c index 2a530da21949..bf006cb272b1 100644 --- a/drivers/hwmon/i5500_temp.c +++ b/drivers/hwmon/i5500_temp.c @@ -8,13 +8,10 @@ #include #include #include -#include -#include #include #include #include #include -#include /* Register definitions from datasheet */ #define REG_TSTHRCATA 0xE2 From b4306c0c8e24f9e031185fb87de45391bc8c7fe9 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Tue, 9 Sep 2025 06:09:01 -0700 Subject: [PATCH 072/110] hwmon: (asus_rog_ryujin) Rely on subsystem locking Attribute access is now serialized in the hardware monitoring core, so locking in the driver code is no longer necessary. Drop it. Signed-off-by: Guenter Roeck --- drivers/hwmon/asus_rog_ryujin.c | 48 +++++++-------------------------- 1 file changed, 9 insertions(+), 39 deletions(-) diff --git a/drivers/hwmon/asus_rog_ryujin.c b/drivers/hwmon/asus_rog_ryujin.c index e5e93a20723c..10a1f5aca988 100644 --- a/drivers/hwmon/asus_rog_ryujin.c +++ b/drivers/hwmon/asus_rog_ryujin.c @@ -81,10 +81,6 @@ static const char *const rog_ryujin_speed_label[] = { struct rog_ryujin_data { struct hid_device *hdev; struct device *hwmon_dev; - /* For locking access to buffer */ - struct mutex buffer_lock; - /* For queueing multiple readers */ - struct mutex status_report_request_mutex; /* For reinitializing the completions below */ spinlock_t status_report_request_lock; struct completion cooler_status_received; @@ -153,18 +149,10 @@ static umode_t rog_ryujin_is_visible(const void *data, /* Writes the command to the device with the rest of the report filled with zeroes */ static int rog_ryujin_write_expanded(struct rog_ryujin_data *priv, const u8 *cmd, int cmd_length) { - int ret; - - mutex_lock(&priv->buffer_lock); - memcpy_and_pad(priv->buffer, MAX_REPORT_LENGTH, cmd, cmd_length, 0x00); - ret = hid_hw_output_report(priv->hdev, priv->buffer, MAX_REPORT_LENGTH); - - mutex_unlock(&priv->buffer_lock); - return ret; + return hid_hw_output_report(priv->hdev, priv->buffer, MAX_REPORT_LENGTH); } -/* Assumes priv->status_report_request_mutex is locked */ static int rog_ryujin_execute_cmd(struct rog_ryujin_data *priv, const u8 *cmd, int cmd_length, struct completion *status_completion) { @@ -196,14 +184,11 @@ static int rog_ryujin_execute_cmd(struct rog_ryujin_data *priv, const u8 *cmd, i static int rog_ryujin_get_status(struct rog_ryujin_data *priv) { - int ret = mutex_lock_interruptible(&priv->status_report_request_mutex); - - if (ret < 0) - return ret; + int ret; if (!time_after(jiffies, priv->updated + msecs_to_jiffies(STATUS_VALIDITY))) { /* Data is up to date */ - goto unlock_and_return; + return 0; } /* Retrieve cooler status */ @@ -211,36 +196,30 @@ static int rog_ryujin_get_status(struct rog_ryujin_data *priv) rog_ryujin_execute_cmd(priv, get_cooler_status_cmd, GET_CMD_LENGTH, &priv->cooler_status_received); if (ret < 0) - goto unlock_and_return; + return ret; /* Retrieve controller status (speeds) */ ret = rog_ryujin_execute_cmd(priv, get_controller_speed_cmd, GET_CMD_LENGTH, &priv->controller_status_received); if (ret < 0) - goto unlock_and_return; + return ret; /* Retrieve cooler duty */ ret = rog_ryujin_execute_cmd(priv, get_cooler_duty_cmd, GET_CMD_LENGTH, &priv->cooler_duty_received); if (ret < 0) - goto unlock_and_return; + return ret; /* Retrieve controller duty */ ret = rog_ryujin_execute_cmd(priv, get_controller_duty_cmd, GET_CMD_LENGTH, &priv->controller_duty_received); - if (ret < 0) - goto unlock_and_return; - - priv->updated = jiffies; - -unlock_and_return: - mutex_unlock(&priv->status_report_request_mutex); if (ret < 0) return ret; + priv->updated = jiffies; return 0; } @@ -303,14 +282,11 @@ static int rog_ryujin_write_fixed_duty(struct rog_ryujin_data *priv, int channel * Retrieve cooler duty since both pump and internal fan are set * together, then write back with one of them modified. */ - ret = mutex_lock_interruptible(&priv->status_report_request_mutex); - if (ret < 0) - return ret; ret = rog_ryujin_execute_cmd(priv, get_cooler_duty_cmd, GET_CMD_LENGTH, &priv->cooler_duty_received); if (ret < 0) - goto unlock_and_return; + return ret; memcpy(set_cmd, set_cooler_duty_cmd, SET_CMD_LENGTH); @@ -329,11 +305,7 @@ static int rog_ryujin_write_fixed_duty(struct rog_ryujin_data *priv, int channel set_cmd[RYUJIN_SET_COOLER_FAN_DUTY_OFFSET] = val; } - ret = rog_ryujin_execute_cmd(priv, set_cmd, SET_CMD_LENGTH, &priv->cooler_duty_set); -unlock_and_return: - mutex_unlock(&priv->status_report_request_mutex); - if (ret < 0) - return ret; + return rog_ryujin_execute_cmd(priv, set_cmd, SET_CMD_LENGTH, &priv->cooler_duty_set); } else { /* * Controller fan duty (channel == 2). No need to retrieve current @@ -538,8 +510,6 @@ static int rog_ryujin_probe(struct hid_device *hdev, const struct hid_device_id goto fail_and_close; } - mutex_init(&priv->status_report_request_mutex); - mutex_init(&priv->buffer_lock); spin_lock_init(&priv->status_report_request_lock); init_completion(&priv->cooler_status_received); init_completion(&priv->controller_status_received); From 4c8d758d4ebff3780d09599f2c8db5d6af3ca2af Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Tue, 9 Sep 2025 06:10:07 -0700 Subject: [PATCH 073/110] hwmon: (chipcap2) Drop unnecessary include files The driver does not perform any locking, does not execute or use any sleep related functionality, and does not allocate memory. Drop the unnecessary include files. Signed-off-by: Guenter Roeck --- drivers/hwmon/chipcap2.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/drivers/hwmon/chipcap2.c b/drivers/hwmon/chipcap2.c index 9d071f7ca9d2..645b8c2e704e 100644 --- a/drivers/hwmon/chipcap2.c +++ b/drivers/hwmon/chipcap2.c @@ -81,7 +81,6 @@ struct cc2_data { struct completion complete; struct device *hwmon; struct i2c_client *client; - struct mutex dev_access_lock; /* device access lock */ struct regulator *regulator; const char *name; int irq_ready; @@ -558,8 +557,6 @@ static int cc2_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, { struct cc2_data *data = dev_get_drvdata(dev); - guard(mutex)(&data->dev_access_lock); - switch (type) { case hwmon_temp: return cc2_measurement(data, type, val); @@ -600,8 +597,6 @@ static int cc2_write(struct device *dev, enum hwmon_sensor_types type, u32 attr, if (val < 0 || val > CC2_RH_MAX) return -EINVAL; - guard(mutex)(&data->dev_access_lock); - switch (attr) { case hwmon_humidity_min: cmd = CC2_W_ALARM_L_ON; @@ -708,8 +703,6 @@ static int cc2_probe(struct i2c_client *client) i2c_set_clientdata(client, data); - mutex_init(&data->dev_access_lock); - data->client = client; data->regulator = devm_regulator_get_exclusive(dev, "vdd"); From 4207069edbf01ae859946fe7f51e3fa03a3bb2f8 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Tue, 9 Sep 2025 06:13:13 -0700 Subject: [PATCH 074/110] hwmon: (corsair-psu) Rely on subsystem locking Attribute access is now serialized in the hardware monitoring core, so locking in the driver code is no longer necessary. Drop it. Signed-off-by: Guenter Roeck --- drivers/hwmon/corsair-psu.c | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/drivers/hwmon/corsair-psu.c b/drivers/hwmon/corsair-psu.c index 6b5c8f200780..dddbd2463f8d 100644 --- a/drivers/hwmon/corsair-psu.c +++ b/drivers/hwmon/corsair-psu.c @@ -9,11 +9,9 @@ #include #include #include -#include #include #include #include -#include #include #include @@ -124,7 +122,6 @@ struct corsairpsu_data { struct device *hwmon_dev; struct dentry *debugfs; struct completion wait_completion; - struct mutex lock; /* for locking access to cmd_buffer */ u8 *cmd_buffer; char vendor[REPLY_SIZE]; char product[REPLY_SIZE]; @@ -220,7 +217,6 @@ static int corsairpsu_request(struct corsairpsu_data *priv, u8 cmd, u8 rail, voi { int ret; - mutex_lock(&priv->lock); switch (cmd) { case PSU_CMD_RAIL_VOLTS_HCRIT: case PSU_CMD_RAIL_VOLTS_LCRIT: @@ -230,17 +226,13 @@ static int corsairpsu_request(struct corsairpsu_data *priv, u8 cmd, u8 rail, voi case PSU_CMD_RAIL_WATTS: ret = corsairpsu_usb_cmd(priv, 2, PSU_CMD_SELECT_RAIL, rail, NULL); if (ret < 0) - goto cmd_fail; + return ret; break; default: break; } - ret = corsairpsu_usb_cmd(priv, 3, cmd, 0, data); - -cmd_fail: - mutex_unlock(&priv->lock); - return ret; + return corsairpsu_usb_cmd(priv, 3, cmd, 0, data); } static int corsairpsu_get_value(struct corsairpsu_data *priv, u8 cmd, u8 rail, long *val) @@ -797,7 +789,6 @@ static int corsairpsu_probe(struct hid_device *hdev, const struct hid_device_id priv->hdev = hdev; hid_set_drvdata(hdev, priv); - mutex_init(&priv->lock); init_completion(&priv->wait_completion); hid_device_io_start(hdev); From abfb05085177df84125cd93f357e48376a4a68e6 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Tue, 9 Sep 2025 06:16:45 -0700 Subject: [PATCH 075/110] hwmon: (corsair-psu) Rely on subsystem locking Attribute access is now serialized in the hardware monitoring core, so locking in the driver code is no longer necessary. Drop it. Signed-off-by: Guenter Roeck --- drivers/hwmon/npcm750-pwm-fan.c | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/drivers/hwmon/npcm750-pwm-fan.c b/drivers/hwmon/npcm750-pwm-fan.c index 802c73def428..c8f5e695fb6d 100644 --- a/drivers/hwmon/npcm750-pwm-fan.c +++ b/drivers/hwmon/npcm750-pwm-fan.c @@ -4,7 +4,6 @@ #include #include #include -#include #include #include #include @@ -198,7 +197,6 @@ struct npcm7xx_pwm_fan_data { int pwm_modules; struct clk *pwm_clk; struct clk *fan_clk; - struct mutex pwm_lock[NPCM7XX_PWM_MAX_MODULES]; spinlock_t fan_lock[NPCM7XX_FAN_MAX_MODULE]; int fan_irq[NPCM7XX_FAN_MAX_MODULE]; bool pwm_present[NPCM7XX_PWM_MAX_CHN_NUM]; @@ -221,7 +219,6 @@ static int npcm7xx_pwm_config_set(struct npcm7xx_pwm_fan_data *data, /* * Config PWM Comparator register for setting duty cycle */ - mutex_lock(&data->pwm_lock[module]); /* write new CMR value */ iowrite32(val, NPCM7XX_PWM_REG_CMRx(data->pwm_base, module, pwm_ch)); @@ -245,7 +242,6 @@ static int npcm7xx_pwm_config_set(struct npcm7xx_pwm_fan_data *data, env_bit = NPCM7XX_PWM_CTRL_CH3_INV_BIT; break; default: - mutex_unlock(&data->pwm_lock[module]); return -ENODEV; } @@ -260,8 +256,6 @@ static int npcm7xx_pwm_config_set(struct npcm7xx_pwm_fan_data *data, } iowrite32(tmp_buf, NPCM7XX_PWM_REG_CR(data->pwm_base, module)); - mutex_unlock(&data->pwm_lock[module]); - return 0; } @@ -932,8 +926,8 @@ static int npcm7xx_pwm_fan_probe(struct platform_device *pdev) struct resource *res; struct device *hwmon; char name[20]; - int ret, cnt; u32 output_freq; + int ret; u32 i; np = dev->of_node; @@ -985,9 +979,6 @@ static int npcm7xx_pwm_fan_probe(struct platform_device *pdev) output_freq = npcm7xx_pwm_init(data); npcm7xx_fan_init(data); - for (cnt = 0; cnt < data->pwm_modules; cnt++) - mutex_init(&data->pwm_lock[cnt]); - for (i = 0; i < NPCM7XX_FAN_MAX_MODULE; i++) { spin_lock_init(&data->fan_lock[i]); From 0da5b278f665282583776715ce2296cd6b7a9a10 Mon Sep 17 00:00:00 2001 From: Ankan Biswas Date: Fri, 17 Oct 2025 21:17:47 +0530 Subject: [PATCH 076/110] docs/hwmon: Fix broken maxim-ic.com links to analog.com In 2021, Maxim Integrated was acquired by Analog Devices. maxim-ic.com & maximintegrated.com links redirect to analog.com. Broken redirects fixed to their proper analog.com links. Fixes warnings in 'make linkcheckdocs'. Signed-off-by: Ankan Biswas Link: https://lore.kernel.org/r/20251017154755.32105-1-spyjetfayed@gmail.com Signed-off-by: Guenter Roeck --- Documentation/hwmon/jc42.rst | 2 +- Documentation/hwmon/max16064.rst | 2 +- Documentation/hwmon/max16065.rst | 8 ++++---- Documentation/hwmon/max1619.rst | 4 ++-- Documentation/hwmon/max31790.rst | 2 +- Documentation/hwmon/max6650.rst | 4 ++-- Documentation/hwmon/max8688.rst | 2 +- 7 files changed, 12 insertions(+), 12 deletions(-) diff --git a/Documentation/hwmon/jc42.rst b/Documentation/hwmon/jc42.rst index 19d10512f6c0..3736e63db2a8 100644 --- a/Documentation/hwmon/jc42.rst +++ b/Documentation/hwmon/jc42.rst @@ -33,7 +33,7 @@ Supported chips: Datasheets: - http://datasheets.maxim-ic.com/en/ds/MAX6604.pdf + https://www.analog.com/media/en/technical-documentation/data-sheets/MAX6604.pdf * Microchip MCP9804, MCP9805, MCP9808, MCP98242, MCP98243, MCP98244, MCP9843 diff --git a/Documentation/hwmon/max16064.rst b/Documentation/hwmon/max16064.rst index c06249292557..2a8a76d7b230 100644 --- a/Documentation/hwmon/max16064.rst +++ b/Documentation/hwmon/max16064.rst @@ -9,7 +9,7 @@ Supported chips: Addresses scanned: - - Datasheet: http://datasheets.maxim-ic.com/en/ds/MAX16064.pdf + Datasheet: https://www.digikey.com/en/htmldatasheets/production/701833/0/0/1/max16064 Author: Guenter Roeck diff --git a/Documentation/hwmon/max16065.rst b/Documentation/hwmon/max16065.rst index 45f69f334f25..ac3dc6f023dd 100644 --- a/Documentation/hwmon/max16065.rst +++ b/Documentation/hwmon/max16065.rst @@ -12,7 +12,7 @@ Supported chips: Datasheet: - http://datasheets.maxim-ic.com/en/ds/MAX16065-MAX16066.pdf + https://www.analog.com/media/en/technical-documentation/data-sheets/max16065-max16066.pdf * Maxim MAX16067 @@ -22,7 +22,7 @@ Supported chips: Datasheet: - http://datasheets.maxim-ic.com/en/ds/MAX16067.pdf + https://www.analog.com/media/en/technical-documentation/data-sheets/max16067.pdf * Maxim MAX16068 @@ -32,7 +32,7 @@ Supported chips: Datasheet: - http://datasheets.maxim-ic.com/en/ds/MAX16068.pdf + https://www.analog.com/media/en/technical-documentation/data-sheets/max16068.pdf * Maxim MAX16070/MAX16071 @@ -42,7 +42,7 @@ Supported chips: Datasheet: - http://datasheets.maxim-ic.com/en/ds/MAX16070-MAX16071.pdf + https://www.analog.com/media/en/technical-documentation/data-sheets/max16070-max16071.pdf Author: Guenter Roeck diff --git a/Documentation/hwmon/max1619.rst b/Documentation/hwmon/max1619.rst index b5fc175ae18d..f134d0fa9bfd 100644 --- a/Documentation/hwmon/max1619.rst +++ b/Documentation/hwmon/max1619.rst @@ -9,9 +9,9 @@ Supported chips: Addresses scanned: I2C 0x18-0x1a, 0x29-0x2b, 0x4c-0x4e - Datasheet: Publicly available at the Maxim website + Datasheet: - http://pdfserv.maxim-ic.com/en/ds/MAX1619.pdf + https://www.analog.com/media/en/technical-documentation/data-sheets/MAX1619.pdf Authors: - Oleksij Rempel , diff --git a/Documentation/hwmon/max31790.rst b/Documentation/hwmon/max31790.rst index 33c5c7330efc..b8af2d907b6e 100644 --- a/Documentation/hwmon/max31790.rst +++ b/Documentation/hwmon/max31790.rst @@ -9,7 +9,7 @@ Supported chips: Addresses scanned: - - Datasheet: https://pdfserv.maximintegrated.com/en/ds/MAX31790.pdf + Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/MAX31790.pdf Author: Il Han diff --git a/Documentation/hwmon/max6650.rst b/Documentation/hwmon/max6650.rst index 7952b6ecaa2d..427f353c5e9c 100644 --- a/Documentation/hwmon/max6650.rst +++ b/Documentation/hwmon/max6650.rst @@ -9,7 +9,7 @@ Supported chips: Addresses scanned: none - Datasheet: http://pdfserv.maxim-ic.com/en/ds/MAX6650-MAX6651.pdf + Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/MAX6650-MAX6651.pdf * Maxim MAX6651 @@ -17,7 +17,7 @@ Supported chips: Addresses scanned: none - Datasheet: http://pdfserv.maxim-ic.com/en/ds/MAX6650-MAX6651.pdf + Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/MAX6650-MAX6651.pdf Authors: - Hans J. Koch diff --git a/Documentation/hwmon/max8688.rst b/Documentation/hwmon/max8688.rst index 71e7f2cbe2e2..71b6b9ee90ab 100644 --- a/Documentation/hwmon/max8688.rst +++ b/Documentation/hwmon/max8688.rst @@ -9,7 +9,7 @@ Supported chips: Addresses scanned: - - Datasheet: http://datasheets.maxim-ic.com/en/ds/MAX8688.pdf + Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/max8688.pdf Author: Guenter Roeck From 17ede362a6f202bc0cb0e359a3da08728fa5de3d Mon Sep 17 00:00:00 2001 From: Ankan Biswas Date: Fri, 17 Oct 2025 21:38:05 +0530 Subject: [PATCH 077/110] docs/hwmon: Add missing datasheet links for Maxim chips In 2021, Maxim Integrated was acquired by Analog Devices. maxim-ic.com & maximintegrated.com links redirect to analog.com. Missing datasheets now available at Analog Devices added. Signed-off-by: Ankan Biswas Link: https://lore.kernel.org/r/20251017161422.4404-1-spyjetfayed@gmail.com Signed-off-by: Guenter Roeck --- Documentation/hwmon/ds1621.rst | 10 +++++----- Documentation/hwmon/lm75.rst | 13 ++++++++----- Documentation/hwmon/max15301.rst | 2 +- Documentation/hwmon/max31827.rst | 6 +++--- Documentation/hwmon/max77705.rst | 4 +++- Documentation/hwmon/pmbus.rst | 2 +- 6 files changed, 21 insertions(+), 16 deletions(-) diff --git a/Documentation/hwmon/ds1621.rst b/Documentation/hwmon/ds1621.rst index 552b37e9dd34..d0808720aa07 100644 --- a/Documentation/hwmon/ds1621.rst +++ b/Documentation/hwmon/ds1621.rst @@ -9,7 +9,7 @@ Supported chips: Addresses scanned: none - Datasheet: Publicly available from www.maximintegrated.com + Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/DS1621.pdf * Dallas Semiconductor DS1625 @@ -17,7 +17,7 @@ Supported chips: Addresses scanned: none - Datasheet: Publicly available from www.datasheetarchive.com + Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/DS1620.pdf * Maxim Integrated DS1631 @@ -25,7 +25,7 @@ Supported chips: Addresses scanned: none - Datasheet: Publicly available from www.maximintegrated.com + Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/DS1631-DS1731.pdf * Maxim Integrated DS1721 @@ -33,7 +33,7 @@ Supported chips: Addresses scanned: none - Datasheet: Publicly available from www.maximintegrated.com + Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/DS1721.pdf * Maxim Integrated DS1731 @@ -41,7 +41,7 @@ Supported chips: Addresses scanned: none - Datasheet: Publicly available from www.maximintegrated.com + Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/DS1631-DS1731.pdf Authors: - Christian W. Zuckschwerdt diff --git a/Documentation/hwmon/lm75.rst b/Documentation/hwmon/lm75.rst index 908b3a9df06e..4269da04508e 100644 --- a/Documentation/hwmon/lm75.rst +++ b/Documentation/hwmon/lm75.rst @@ -23,15 +23,17 @@ Supported chips: http://www.national.com/ - * Dallas Semiconductor (now Maxim) DS75, DS1775, DS7505 + * Dallas Semiconductor (now Analog Devices) DS75, DS1775, DS7505 Prefixes: 'ds75', 'ds1775', 'ds7505' Addresses scanned: none - Datasheet: Publicly available at the Maxim website + Datasheets: - https://www.maximintegrated.com/ + https://www.analog.com/media/en/technical-documentation/data-sheets/DS75.pdf + https://www.analog.com/media/en/technical-documentation/data-sheets/DS1775.pdf + https://www.analog.com/media/en/technical-documentation/data-sheets/DS7505.pdf * Maxim MAX6625, MAX6626, MAX31725, MAX31726 @@ -39,9 +41,10 @@ Supported chips: Addresses scanned: none - Datasheet: Publicly available at the Maxim website + Datasheets: - http://www.maxim-ic.com/ + https://www.analog.com/media/en/technical-documentation/data-sheets/MAX6625-MAX6626.pdf + https://www.analog.com/media/en/technical-documentation/data-sheets/MAX31725-MAX31726.pdf * Microchip (TelCom) TCN75 diff --git a/Documentation/hwmon/max15301.rst b/Documentation/hwmon/max15301.rst index e2222e98304f..a0a993195cd1 100644 --- a/Documentation/hwmon/max15301.rst +++ b/Documentation/hwmon/max15301.rst @@ -11,7 +11,7 @@ Supported chips: Addresses scanned: - - Datasheet: https://datasheets.maximintegrated.com/en/ds/MAX15301.pdf + Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/max15301.pdf * Maxim MAX15303 diff --git a/Documentation/hwmon/max31827.rst b/Documentation/hwmon/max31827.rst index 6cc5088b26b7..ddd039529077 100644 --- a/Documentation/hwmon/max31827.rst +++ b/Documentation/hwmon/max31827.rst @@ -11,7 +11,7 @@ Supported chips: Addresses scanned: I2C 0x40 - 0x5f - Datasheet: Publicly available at the Analog Devices website + Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/MAX31827-MAX31829.pdf * Maxim MAX31828 @@ -19,7 +19,7 @@ Supported chips: Addresses scanned: I2C 0x40 - 0x5f - Datasheet: Publicly available at the Analog Devices website + Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/MAX31827-MAX31829.pdf * Maxim MAX31829 @@ -27,7 +27,7 @@ Supported chips: Addresses scanned: I2C 0x40 - 0x5f - Datasheet: Publicly available at the Analog Devices website + Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/MAX31827-MAX31829.pdf Authors: diff --git a/Documentation/hwmon/max77705.rst b/Documentation/hwmon/max77705.rst index 4a7680a340e1..5202de614647 100644 --- a/Documentation/hwmon/max77705.rst +++ b/Documentation/hwmon/max77705.rst @@ -11,7 +11,9 @@ Supported chips: Addresses scanned: none - Datasheet: Not available + Datasheet: + + https://www.analog.com/media/en/technical-documentation/data-sheets/max77505.pdf Authors: - Dzmitry Sankouski diff --git a/Documentation/hwmon/pmbus.rst b/Documentation/hwmon/pmbus.rst index d477124cf67f..a8e01a5b96da 100644 --- a/Documentation/hwmon/pmbus.rst +++ b/Documentation/hwmon/pmbus.rst @@ -74,7 +74,7 @@ Supported chips: Datasheet: - Not published + https://www.analog.com/media/en/technical-documentation/data-sheets/MAX20796.pdf * Generic PMBus devices From 1f7161fa0d4e8dcaee86a9cf91ecabcb691d0500 Mon Sep 17 00:00:00 2001 From: Ankan Biswas Date: Fri, 17 Oct 2025 22:04:31 +0530 Subject: [PATCH 078/110] docs/hwmon: Update maxim-ic.com links to analog.com In 2021, Maxim Integrated was acquired by Analog Devices. maxim-ic.com & maximintegrated.com links redirect to analog.com. Update maxim-ic.com & maximintegrated.com links to analog.com links. Signed-off-by: Ankan Biswas Link: https://lore.kernel.org/r/20251017163501.11285-1-spyjetfayed@gmail.com Signed-off-by: Guenter Roeck --- Documentation/hwmon/max127.rst | 2 +- Documentation/hwmon/max16601.rst | 2 +- Documentation/hwmon/max1668.rst | 2 +- Documentation/hwmon/max197.rst | 4 ++-- Documentation/hwmon/max20730.rst | 8 ++++---- Documentation/hwmon/max31722.rst | 4 ++-- Documentation/hwmon/max31730.rst | 2 +- Documentation/hwmon/max31785.rst | 2 +- Documentation/hwmon/max34440.rst | 12 ++++++------ Documentation/hwmon/max6639.rst | 2 +- Documentation/hwmon/max6697.rst | 20 ++++++++++---------- 11 files changed, 30 insertions(+), 30 deletions(-) diff --git a/Documentation/hwmon/max127.rst b/Documentation/hwmon/max127.rst index dc192dd9c37c..09204b45f27b 100644 --- a/Documentation/hwmon/max127.rst +++ b/Documentation/hwmon/max127.rst @@ -13,7 +13,7 @@ Supported chips: Prefix: 'max127' - Datasheet: https://datasheets.maximintegrated.com/en/ds/MAX127-MAX128.pdf + Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/max127-max128.pdf Description ----------- diff --git a/Documentation/hwmon/max16601.rst b/Documentation/hwmon/max16601.rst index c8c63a053e40..3b1392bf547e 100644 --- a/Documentation/hwmon/max16601.rst +++ b/Documentation/hwmon/max16601.rst @@ -35,7 +35,7 @@ Supported chips: Addresses scanned: - - Datasheet: https://datasheets.maximintegrated.com/en/ds/MAX16602.pdf + Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/max16602.pdf Author: Guenter Roeck diff --git a/Documentation/hwmon/max1668.rst b/Documentation/hwmon/max1668.rst index 417f17d750e6..e2b8a4056abe 100644 --- a/Documentation/hwmon/max1668.rst +++ b/Documentation/hwmon/max1668.rst @@ -9,7 +9,7 @@ Supported chips: Addresses scanned: I2C 0x18, 0x19, 0x1a, 0x29, 0x2a, 0x2b, 0x4c, 0x4d, 0x4e - Datasheet: http://datasheets.maxim-ic.com/en/ds/MAX1668-MAX1989.pdf + Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/MAX1668-MAX1989.pdf Author: diff --git a/Documentation/hwmon/max197.rst b/Documentation/hwmon/max197.rst index 02fe19bc3428..00e16056823f 100644 --- a/Documentation/hwmon/max197.rst +++ b/Documentation/hwmon/max197.rst @@ -11,13 +11,13 @@ Supported chips: Prefix: 'max197' - Datasheet: http://datasheets.maxim-ic.com/en/ds/MAX197.pdf + Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/MAX197.pdf * Maxim MAX199 Prefix: 'max199' - Datasheet: http://datasheets.maxim-ic.com/en/ds/MAX199.pdf + Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/MAX199.pdf Description ----------- diff --git a/Documentation/hwmon/max20730.rst b/Documentation/hwmon/max20730.rst index cb0c95b2b1f6..0ce473bca889 100644 --- a/Documentation/hwmon/max20730.rst +++ b/Documentation/hwmon/max20730.rst @@ -11,7 +11,7 @@ Supported chips: Addresses scanned: - - Datasheet: https://datasheets.maximintegrated.com/en/ds/MAX20710.pdf + Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/max20710.pdf * Maxim MAX20730 @@ -19,7 +19,7 @@ Supported chips: Addresses scanned: - - Datasheet: https://datasheets.maximintegrated.com/en/ds/MAX20730.pdf + Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/max20730.pdf * Maxim MAX20734 @@ -27,7 +27,7 @@ Supported chips: Addresses scanned: - - Datasheet: https://datasheets.maximintegrated.com/en/ds/MAX20734.pdf + Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/max20734.pdf * Maxim MAX20743 @@ -35,7 +35,7 @@ Supported chips: Addresses scanned: - - Datasheet: https://datasheets.maximintegrated.com/en/ds/MAX20743.pdf + Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/max20743.pdf Author: Guenter Roeck diff --git a/Documentation/hwmon/max31722.rst b/Documentation/hwmon/max31722.rst index 0ab15c00b226..b9d176ee7a69 100644 --- a/Documentation/hwmon/max31722.rst +++ b/Documentation/hwmon/max31722.rst @@ -11,7 +11,7 @@ Supported chips: Addresses scanned: - - Datasheet: https://datasheets.maximintegrated.com/en/ds/MAX31722-MAX31723.pdf + Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/max31722-max31723.pdf * Maxim Integrated MAX31723 @@ -21,7 +21,7 @@ Supported chips: Addresses scanned: - - Datasheet: https://datasheets.maximintegrated.com/en/ds/MAX31722-MAX31723.pdf + Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/max31722-max31723.pdf Author: Tiberiu Breana diff --git a/Documentation/hwmon/max31730.rst b/Documentation/hwmon/max31730.rst index def0de19dbd2..1c5a32b64187 100644 --- a/Documentation/hwmon/max31730.rst +++ b/Documentation/hwmon/max31730.rst @@ -9,7 +9,7 @@ Supported chips: Addresses scanned: 0x1c, 0x1d, 0x1e, 0x1f, 0x4c, 0x4d, 0x4e, 0x4f - Datasheet: https://datasheets.maximintegrated.com/en/ds/MAX31730.pdf + Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/max31730.pdf Author: Guenter Roeck diff --git a/Documentation/hwmon/max31785.rst b/Documentation/hwmon/max31785.rst index c8c6756d0ee1..92817436759e 100644 --- a/Documentation/hwmon/max31785.rst +++ b/Documentation/hwmon/max31785.rst @@ -9,7 +9,7 @@ Supported chips: Addresses scanned: - - Datasheet: https://datasheets.maximintegrated.com/en/ds/MAX31785.pdf + Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/max31785.pdf Author: Andrew Jeffery diff --git a/Documentation/hwmon/max34440.rst b/Documentation/hwmon/max34440.rst index 34a6e0e1b963..d6d4fbc863d9 100644 --- a/Documentation/hwmon/max34440.rst +++ b/Documentation/hwmon/max34440.rst @@ -25,7 +25,7 @@ Supported chips: Addresses scanned: - - Datasheet: https://datasheets.maximintegrated.com/en/ds/MAX34440.pdf + Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/max34440.pdf * Maxim MAX34441 @@ -35,7 +35,7 @@ Supported chips: Addresses scanned: - - Datasheet: https://datasheets.maximintegrated.com/en/ds/MAX34441.pdf + Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/max34441.pdf * Maxim MAX34446 @@ -45,7 +45,7 @@ Supported chips: Addresses scanned: - - Datasheet: https://datasheets.maximintegrated.com/en/ds/MAX34446.pdf + Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/max34446.pdf * Maxim MAX34451 @@ -55,7 +55,7 @@ Supported chips: Addresses scanned: - - Datasheet: https://datasheets.maximintegrated.com/en/ds/MAX34451.pdf + Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/max34451.pdf * Maxim MAX34460 @@ -65,7 +65,7 @@ Supported chips: Addresses scanned: - - Datasheet: https://datasheets.maximintegrated.com/en/ds/MAX34460.pdf + Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/max34460.pdf * Maxim MAX34461 @@ -75,7 +75,7 @@ Supported chips: Addresses scanned: - - Datasheet: https://datasheets.maximintegrated.com/en/ds/MAX34461.pdf + Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/max34461.pdf Author: Guenter Roeck diff --git a/Documentation/hwmon/max6639.rst b/Documentation/hwmon/max6639.rst index c85d285a3489..492c13a5880d 100644 --- a/Documentation/hwmon/max6639.rst +++ b/Documentation/hwmon/max6639.rst @@ -9,7 +9,7 @@ Supported chips: Addresses scanned: I2C 0x2c, 0x2e, 0x2f - Datasheet: https://datasheets.maximintegrated.com/en/ds/MAX6639-MAX6639F.pdf + Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/max6639-max6639f.pdf Authors: - He Changqing diff --git a/Documentation/hwmon/max6697.rst b/Documentation/hwmon/max6697.rst index 90ca224c446a..5b37ff08ff44 100644 --- a/Documentation/hwmon/max6697.rst +++ b/Documentation/hwmon/max6697.rst @@ -7,61 +7,61 @@ Supported chips: Prefix: 'max6581' - Datasheet: http://datasheets.maximintegrated.com/en/ds/MAX6581.pdf + Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/max6581.pdf * Maxim MAX6602 Prefix: 'max6602' - Datasheet: http://datasheets.maximintegrated.com/en/ds/MAX6602.pdf + Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/max6602.pdf * Maxim MAX6622 Prefix: 'max6622' - Datasheet: http://datasheets.maximintegrated.com/en/ds/MAX6622.pdf + Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/max6622.pdf * Maxim MAX6636 Prefix: 'max6636' - Datasheet: http://datasheets.maximintegrated.com/en/ds/MAX6636.pdf + Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/max6636.pdf * Maxim MAX6689 Prefix: 'max6689' - Datasheet: http://datasheets.maximintegrated.com/en/ds/MAX6689.pdf + Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/max6689.pdf * Maxim MAX6693 Prefix: 'max6693' - Datasheet: http://datasheets.maximintegrated.com/en/ds/MAX6693.pdf + Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/max6693.pdf * Maxim MAX6694 Prefix: 'max6694' - Datasheet: http://datasheets.maximintegrated.com/en/ds/MAX6694.pdf + Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/max6694.pdf * Maxim MAX6697 Prefix: 'max6697' - Datasheet: http://datasheets.maximintegrated.com/en/ds/MAX6697.pdf + Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/max6697.pdf * Maxim MAX6698 Prefix: 'max6698' - Datasheet: http://datasheets.maximintegrated.com/en/ds/MAX6698.pdf + Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/max6698.pdf * Maxim MAX6699 Prefix: 'max6699' - Datasheet: http://datasheets.maximintegrated.com/en/ds/MAX6699.pdf + Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/max6699.pdf Author: From 2ea255d35bc3dc06b156563a9241ef2ada921b38 Mon Sep 17 00:00:00 2001 From: Eugene Shalygin Date: Sun, 2 Nov 2025 12:40:28 +0100 Subject: [PATCH 079/110] hwmon: (asus-ec-sensors) add ROG STRIX X870-F GAMING WIFI Add support for the ROG STRIX X870-F GAMING WIFI board. Testing done by the board owner [1]. [1] https://github.com/zeule/asus-ec-sensors/issues/83 Signed-off-by: Eugene Shalygin Link: https://lore.kernel.org/r/20251102114038.283396-1-eugene.shalygin@gmail.com Signed-off-by: Guenter Roeck --- Documentation/hwmon/asus_ec_sensors.rst | 1 + drivers/hwmon/asus-ec-sensors.c | 9 +++++++++ 2 files changed, 10 insertions(+) diff --git a/Documentation/hwmon/asus_ec_sensors.rst b/Documentation/hwmon/asus_ec_sensors.rst index 2666ed3a8ea3..f0a92ce30a02 100644 --- a/Documentation/hwmon/asus_ec_sensors.rst +++ b/Documentation/hwmon/asus_ec_sensors.rst @@ -36,6 +36,7 @@ Supported boards: * ROG STRIX X570-I GAMING * ROG STRIX X670E-E GAMING WIFI * ROG STRIX X670E-I GAMING WIFI + * ROG STRIX X870-F GAMING WIFI * ROG STRIX X870-I GAMING WIFI * ROG STRIX X870E-E GAMING WIFI * ROG STRIX X870E-H GAMING WIFI7 diff --git a/drivers/hwmon/asus-ec-sensors.c b/drivers/hwmon/asus-ec-sensors.c index 7f35eade3a9b..8c5eadd8786d 100644 --- a/drivers/hwmon/asus-ec-sensors.c +++ b/drivers/hwmon/asus-ec-sensors.c @@ -645,6 +645,13 @@ static const struct ec_board_info board_info_strix_x670e_i_gaming_wifi = { .family = family_amd_600_series, }; +static const struct ec_board_info board_info_strix_x870_f_gaming_wifi = { + .sensors = SENSOR_TEMP_CPU | SENSOR_TEMP_CPU_PACKAGE | + SENSOR_TEMP_MB | SENSOR_TEMP_VRM | SENSOR_TEMP_T_SENSOR, + .mutex_path = ASUS_HW_ACCESS_MUTEX_SB_PCI0_SBRG_SIO1_MUT0, + .family = family_amd_800_series, +}; + static const struct ec_board_info board_info_strix_x870_i_gaming_wifi = { .sensors = SENSOR_TEMP_CPU | SENSOR_TEMP_CPU_PACKAGE | SENSOR_TEMP_MB | SENSOR_TEMP_VRM, @@ -810,6 +817,8 @@ static const struct dmi_system_id dmi_table[] = { &board_info_strix_x670e_e_gaming_wifi), DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX X670E-I GAMING WIFI", &board_info_strix_x670e_i_gaming_wifi), + DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX X870-F GAMING WIFI", + &board_info_strix_x870_f_gaming_wifi), DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX X870-I GAMING WIFI", &board_info_strix_x870_i_gaming_wifi), DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX X870E-E GAMING WIFI", From 3eaf1b631506e8de2cb37c278d5bc042521e82c1 Mon Sep 17 00:00:00 2001 From: Akhilesh Patil Date: Sun, 2 Nov 2025 15:13:20 +0530 Subject: [PATCH 080/110] hwmon: (aht10) Add support for dht20 Add support for dht20 temperature and humidity sensor from Aosong. Modify aht10 driver to handle different init command for dht20 sensor by adding init_cmd entry in the driver data. dht20 sensor is compatible with aht10 hwmon driver with this change. Tested on TI am62x SK board with dht20 sensor connected at i2c-2 port. Signed-off-by: Akhilesh Patil Link: https://lore.kernel.org/r/2025112-94320-906858@bhairav-test.ee.iitb.ac.in Signed-off-by: Guenter Roeck --- Documentation/hwmon/aht10.rst | 10 +++++++++- drivers/hwmon/Kconfig | 6 +++--- drivers/hwmon/aht10.c | 19 ++++++++++++++++--- 3 files changed, 28 insertions(+), 7 deletions(-) diff --git a/Documentation/hwmon/aht10.rst b/Documentation/hwmon/aht10.rst index 213644b4ecba..7903b6434326 100644 --- a/Documentation/hwmon/aht10.rst +++ b/Documentation/hwmon/aht10.rst @@ -20,6 +20,14 @@ Supported chips: English: http://www.aosong.com/userfiles/files/media/Data%20Sheet%20AHT20.pdf + * Aosong DHT20 + + Prefix: 'dht20' + + Addresses scanned: None + + Datasheet: https://www.digikey.co.nz/en/htmldatasheets/production/9184855/0/0/1/101020932 + Author: Johannes Cornelis Draaijer @@ -33,7 +41,7 @@ The address of this i2c device may only be 0x38 Special Features ---------------- -AHT20 has additional CRC8 support which is sent as the last byte of the sensor +AHT20, DHT20 has additional CRC8 support which is sent as the last byte of the sensor values. Usage Notes diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index aa783be9b73d..adf957540050 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -245,12 +245,12 @@ config SENSORS_ADT7475 will be called adt7475. config SENSORS_AHT10 - tristate "Aosong AHT10, AHT20" + tristate "Aosong AHT10, AHT20, DHT20" depends on I2C select CRC8 help - If you say yes here, you get support for the Aosong AHT10 and AHT20 - temperature and humidity sensors + If you say yes here, you get support for the Aosong AHT10, AHT20 and + DHT20 temperature and humidity sensors This driver can also be built as a module. If so, the module will be called aht10. diff --git a/drivers/hwmon/aht10.c b/drivers/hwmon/aht10.c index 8b90b661c393..007befdba977 100644 --- a/drivers/hwmon/aht10.c +++ b/drivers/hwmon/aht10.c @@ -37,6 +37,8 @@ #define AHT10_CMD_MEAS 0b10101100 #define AHT10_CMD_RST 0b10111010 +#define DHT20_CMD_INIT 0x71 + /* * Flags in the answer byte/command */ @@ -48,11 +50,12 @@ #define AHT10_MAX_POLL_INTERVAL_LEN 30 -enum aht10_variant { aht10, aht20 }; +enum aht10_variant { aht10, aht20, dht20}; static const struct i2c_device_id aht10_id[] = { { "aht10", aht10 }, { "aht20", aht20 }, + { "dht20", dht20 }, { }, }; MODULE_DEVICE_TABLE(i2c, aht10_id); @@ -75,6 +78,7 @@ MODULE_DEVICE_TABLE(i2c, aht10_id); * AHT10/AHT20 * @crc8: crc8 support flag * @meas_size: measurements data size + * @init_cmd: Initialization command */ struct aht10_data { @@ -85,6 +89,7 @@ struct aht10_data { int humidity; bool crc8; unsigned int meas_size; + u8 init_cmd; }; /* @@ -94,13 +99,13 @@ struct aht10_data { */ static int aht10_init(struct aht10_data *data) { - const u8 cmd_init[] = {AHT10_CMD_INIT, AHT10_CAL_ENABLED | AHT10_MODE_CYC, + const u8 cmd_init[] = {data->init_cmd, AHT10_CAL_ENABLED | AHT10_MODE_CYC, 0x00}; int res; u8 status; struct i2c_client *client = data->client; - res = i2c_master_send(client, cmd_init, 3); + res = i2c_master_send(client, cmd_init, sizeof(cmd_init)); if (res < 0) return res; @@ -336,9 +341,17 @@ static int aht10_probe(struct i2c_client *client) data->meas_size = AHT20_MEAS_SIZE; data->crc8 = true; crc8_populate_msb(crc8_table, AHT20_CRC8_POLY); + data->init_cmd = AHT10_CMD_INIT; + break; + case dht20: + data->meas_size = AHT20_MEAS_SIZE; + data->crc8 = true; + crc8_populate_msb(crc8_table, AHT20_CRC8_POLY); + data->init_cmd = DHT20_CMD_INIT; break; default: data->meas_size = AHT10_MEAS_SIZE; + data->init_cmd = AHT10_CMD_INIT; break; } From 68c2a8b59d231b83284cfda6c3c2147f4e0fa4cf Mon Sep 17 00:00:00 2001 From: Andreas Kemnade Date: Mon, 27 Oct 2025 21:28:47 +0100 Subject: [PATCH 081/110] hwmon: (sy7636a) Fix sensor description The temperature is not from the die itself but from an NTC. That was verified with an IR camera. Fix that. Signed-off-by: Andreas Kemnade Link: https://lore.kernel.org/r/20251027202847.119707-1-akemnade@kernel.org Signed-off-by: Guenter Roeck --- Documentation/hwmon/sy7636a-hwmon.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/hwmon/sy7636a-hwmon.rst b/Documentation/hwmon/sy7636a-hwmon.rst index c85db7b32941..0143ce0e5db7 100644 --- a/Documentation/hwmon/sy7636a-hwmon.rst +++ b/Documentation/hwmon/sy7636a-hwmon.rst @@ -17,10 +17,10 @@ the Silergy SY7636A PMIC. The following sensors are supported * Temperature - - SoC on-die temperature in milli-degree C + - Temperature of external NTC in milli-degree C sysfs-Interface --------------- temp0_input - - SoC on-die temperature (milli-degree C) + - Temperature of external NTC (milli-degree C) From b340412a3b22b60b5e19cce8726940c7b5b14439 Mon Sep 17 00:00:00 2001 From: James Calligeros Date: Sat, 25 Oct 2025 10:24:36 +1000 Subject: [PATCH 082/110] mfd: macsmc: Add new __SMC_KEY macro When using the _SMC_KEY macro in switch/case statements, GCC 15.2.1 errors out with 'case label does not reduce to an integer constant'. Introduce a new __SMC_KEY macro that can be used instead. Signed-off-by: James Calligeros Link: https://patch.msgid.link/20251025-macsmc-subdevs-v4-5-374d5c9eba0e@gmail.com Signed-off-by: Lee Jones --- include/linux/mfd/macsmc.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/linux/mfd/macsmc.h b/include/linux/mfd/macsmc.h index 6b13f01a8592..f6f80c33b5cf 100644 --- a/include/linux/mfd/macsmc.h +++ b/include/linux/mfd/macsmc.h @@ -41,6 +41,7 @@ typedef u32 smc_key; */ #define SMC_KEY(s) (smc_key)(_SMC_KEY(#s)) #define _SMC_KEY(s) (((s)[0] << 24) | ((s)[1] << 16) | ((s)[2] << 8) | (s)[3]) +#define __SMC_KEY(a, b, c, d) (((u32)(a) << 24) | ((u32)(b) << 16) | ((u32)(c) << 8) | ((u32)(d))) #define APPLE_SMC_READABLE BIT(7) #define APPLE_SMC_WRITABLE BIT(6) From 96cee719d222f5a0da51099256bc97311e396b31 Mon Sep 17 00:00:00 2001 From: Igor Reznichenko Date: Wed, 5 Nov 2025 12:14:05 -0800 Subject: [PATCH 083/110] dt-bindings: hwmon: ST TSC1641 power monitor Add binding for the TSC1641 I2C power monitor. Signed-off-by: Igor Reznichenko Reviewed-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20251105201406.1210856-2-igor@reznichenko.net Signed-off-by: Guenter Roeck --- .../devicetree/bindings/hwmon/st,tsc1641.yaml | 63 +++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 Documentation/devicetree/bindings/hwmon/st,tsc1641.yaml diff --git a/Documentation/devicetree/bindings/hwmon/st,tsc1641.yaml b/Documentation/devicetree/bindings/hwmon/st,tsc1641.yaml new file mode 100644 index 000000000000..aaf244790663 --- /dev/null +++ b/Documentation/devicetree/bindings/hwmon/st,tsc1641.yaml @@ -0,0 +1,63 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/hwmon/st,tsc1641.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: ST Microelectronics TSC1641 I2C power monitor + +maintainers: + - Igor Reznichenko + +description: | + TSC1641 is a 60 V, 16-bit high-precision power monitor with I2C and + MIPI I3C interface + + Datasheets: + https://www.st.com/resource/en/datasheet/tsc1641.pdf + +properties: + compatible: + const: st,tsc1641 + + reg: + maxItems: 1 + + interrupts: + description: Optional alert interrupt. + maxItems: 1 + + shunt-resistor-micro-ohms: + description: Shunt resistor value in micro-ohms. Since device has internal + 16-bit RSHUNT register with 10 uOhm LSB, the maximum value is capped at + 655.35 mOhm. + minimum: 100 + default: 1000 + maximum: 655350 + + st,alert-polarity-active-high: + $ref: /schemas/types.yaml#/definitions/flag + description: Default value is 0 which configures the normal polarity of the + ALERT pin, being active low open-drain. Setting this to 1 configures the + polarity of the ALERT pin to be inverted and active high open-drain. + Specify this property to set the alert polarity to active-high. + +required: + - compatible + - reg + +additionalProperties: false + +examples: + - | + i2c { + #address-cells = <1>; + #size-cells = <0>; + + power-sensor@40 { + compatible = "st,tsc1641"; + reg = <0x40>; + shunt-resistor-micro-ohms = <1000>; + st,alert-polarity-active-high; + }; + }; From 0c7d530a04b4cada1042ecaf49e12dafe90ce30e Mon Sep 17 00:00:00 2001 From: Igor Reznichenko Date: Wed, 5 Nov 2025 12:14:06 -0800 Subject: [PATCH 084/110] hwmon: Add TSC1641 I2C power monitor driver Add a driver for the ST Microelectronics TSC1641 16-bit high-precision power monitor. The driver supports reading bus voltage, current, power, and temperature. Sysfs attributes are exposed for shunt resistor and update interval. The driver integrates with the hwmon subsystem and supports optional ALERT pin polarity configuration. Signed-off-by: Igor Reznichenko Link: https://lore.kernel.org/r/20251105201406.1210856-3-igor@reznichenko.net Signed-off-by: Guenter Roeck --- Documentation/hwmon/index.rst | 1 + Documentation/hwmon/tsc1641.rst | 87 ++++ drivers/hwmon/Kconfig | 12 + drivers/hwmon/Makefile | 1 + drivers/hwmon/tsc1641.c | 748 ++++++++++++++++++++++++++++++++ 5 files changed, 849 insertions(+) create mode 100644 Documentation/hwmon/tsc1641.rst create mode 100644 drivers/hwmon/tsc1641.c diff --git a/Documentation/hwmon/index.rst b/Documentation/hwmon/index.rst index 45a26927e64f..d1b80bfd0fd1 100644 --- a/Documentation/hwmon/index.rst +++ b/Documentation/hwmon/index.rst @@ -256,6 +256,7 @@ Hardware Monitoring Kernel Drivers tps40422 tps53679 tps546d24 + tsc1641 twl4030-madc-hwmon ucd9000 ucd9200 diff --git a/Documentation/hwmon/tsc1641.rst b/Documentation/hwmon/tsc1641.rst new file mode 100644 index 000000000000..425e25f7a7d1 --- /dev/null +++ b/Documentation/hwmon/tsc1641.rst @@ -0,0 +1,87 @@ +.. SPDX-License-Identifier: GPL-2.0-only + +Kernel driver tsc1641 +===================== + +Supported chips: + + * ST TSC1641 + + Prefix: 'tsc1641' + + Addresses scanned: - + + Datasheet: + https://www.st.com/resource/en/datasheet/tsc1641.pdf + +Author: + - Igor Reznichenko + + +Description +----------- + +The TSC1641 is a high-precision current, voltage, power, and temperature +monitoring analog front-end (AFE). It monitors bidirectional current into a +shunt resistor and load voltage up to 60 V in a synchronized way. Digital bus +interface is I2C/SMbus. The TSC1641 allows the assertion of several alerts +regarding the voltage, current, power and temperature. + +Usage Notes +----------- + +The TSC1641 driver requires the value of the external shunt resistor to +correctly compute current and power measurements. The resistor value, in +micro-ohms, should be provided either through the device tree property +"shunt-resistor-micro-ohms" or via writable sysfs attribute "shunt_resistor". +Please refer to the Documentation/devicetree/bindings/hwmon/st,tsc1641.yaml +for bindings if the device tree is used. + +Supported range of shunt resistor values is from 100 uOhm to 655.35 mOhm, in +10 uOhm steps. +When selecting the value keep in mind device maximum DC power measurement is +1600W. See datasheet p.22 for ST recommendations on selecting shunt value. + +If the shunt resistor value is not specified in the device tree, the driver +initializes it to 1000 uOhm by default. Users may configure the correct shunt +resistor value at runtime by writing to the "shunt_resistor" sysfs attribute. + +The driver only supports continuous operating mode. +Measurement ranges: + +================ =============================================================== +Current Bidirectional, dependent on shunt +Bus voltage 0-60V +Maximum DC power 1600W +Temperature -40C to +125C +================ =============================================================== + +Sysfs entries +------------- + +==================== =========================================================== +in0_input bus voltage (mV) +in0_max bus voltage max alarm limit (mV) +in0_max_alarm bus voltage max alarm limit exceeded +in0_min bus voltage min alarm limit (mV) +in0_min_alarm bus voltage min alarm limit exceeded + +curr1_input current measurement (mA) +curr1_max current max alarm limit (mA) +curr1_max_alarm current max alarm limit exceeded +curr1_min current min alarm limit (mA) +curr1_min_alarm current min alarm limit exceeded + +power1_input power measurement (uW) +power1_max power max alarm limit (uW) +power1_max_alarm power max alarm limit exceeded + +shunt_resistor shunt resistor value (uOhms) + +temp1_input temperature measurement (mdegC) +temp1_max temperature max alarm limit (mdegC) +temp1_max_alarm temperature max alarm limit exceeded + +update_interval data conversion time (1 - 33ms), longer conversion time + corresponds to higher effective resolution in bits +==================== =========================================================== \ No newline at end of file diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index adf957540050..8c852bcac26f 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -2434,6 +2434,18 @@ config SENSORS_TMP513 This driver can also be built as a module. If so, the module will be called tmp513. +config SENSORS_TSC1641 + tristate "ST Microelectronics TSC1641 Power Monitor" + depends on I2C + select REGMAP_I2C + help + If you say yes here you get support for TSC1641 power monitor chip. + The TSC1641 driver is configured for the default configuration of + the part except temperature is enabled by default. + + This driver can also be built as a module. If so, the module + will be called tsc1641. + config SENSORS_VEXPRESS tristate "Versatile Express" depends on VEXPRESS_CONFIG diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index 73b2abdcc6dd..a8de5bc69f2a 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -233,6 +233,7 @@ obj-$(CONFIG_SENSORS_TMP401) += tmp401.o obj-$(CONFIG_SENSORS_TMP421) += tmp421.o obj-$(CONFIG_SENSORS_TMP464) += tmp464.o obj-$(CONFIG_SENSORS_TMP513) += tmp513.o +obj-$(CONFIG_SENSORS_TSC1641) += tsc1641.o obj-$(CONFIG_SENSORS_VEXPRESS) += vexpress-hwmon.o obj-$(CONFIG_SENSORS_VIA_CPUTEMP)+= via-cputemp.o obj-$(CONFIG_SENSORS_VIA686A) += via686a.o diff --git a/drivers/hwmon/tsc1641.c b/drivers/hwmon/tsc1641.c new file mode 100644 index 000000000000..2b5d34bab146 --- /dev/null +++ b/drivers/hwmon/tsc1641.c @@ -0,0 +1,748 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Driver for ST Microelectronics TSC1641 I2C power monitor + * + * 60 V, 16-bit high-precision power monitor with I2C and MIPI I3C interface + * Datasheet: https://www.st.com/resource/en/datasheet/tsc1641.pdf + * + * Copyright (C) 2025 Igor Reznichenko + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* I2C registers */ +#define TSC1641_CONFIG 0x00 +#define TSC1641_SHUNT_VOLTAGE 0x01 +#define TSC1641_LOAD_VOLTAGE 0x02 +#define TSC1641_POWER 0x03 +#define TSC1641_CURRENT 0x04 +#define TSC1641_TEMP 0x05 +#define TSC1641_MASK 0x06 +#define TSC1641_FLAG 0x07 +#define TSC1641_RSHUNT 0x08 /* Shunt resistance */ +#define TSC1641_SOL 0x09 +#define TSC1641_SUL 0x0A +#define TSC1641_LOL 0x0B +#define TSC1641_LUL 0x0C +#define TSC1641_POL 0x0D +#define TSC1641_TOL 0x0E +#define TSC1641_MANUF_ID 0xFE /* 0x0006 */ +#define TSC1641_DIE_ID 0xFF /* 0x1000 */ +#define TSC1641_MAX_REG 0xFF + +#define TSC1641_RSHUNT_DEFAULT 1000 /* 1mOhm */ +#define TSC1641_CONFIG_DEFAULT 0x003F /* Default mode and temperature sensor */ +#define TSC1641_MASK_DEFAULT 0xFC00 /* Unmask all alerts */ + +/* Bit mask for conversion time in the configuration register */ +#define TSC1641_CONV_TIME_MASK GENMASK(7, 4) + +#define TSC1641_CONV_TIME_DEFAULT 1024 +#define TSC1641_MIN_UPDATE_INTERVAL 1024 + +/* LSB value of different registers */ +#define TSC1641_VLOAD_LSB_MVOLT 2 +#define TSC1641_POWER_LSB_UWATT 25000 +#define TSC1641_VSHUNT_LSB_NVOLT 2500 /* Use nanovolts to make it integer */ +#define TSC1641_RSHUNT_LSB_UOHM 10 +#define TSC1641_TEMP_LSB_MDEGC 500 + +/* Limits based on datasheet */ +#define TSC1641_RSHUNT_MIN_UOHM 100 +#define TSC1641_RSHUNT_MAX_UOHM 655350 +#define TSC1641_CURR_ABS_MAX_MAMP 819200 /* Max current at 100uOhm*/ + +#define TSC1641_ALERT_POL_MASK BIT(1) +#define TSC1641_ALERT_LATCH_EN_MASK BIT(0) + +/* Flags indicating alerts in TSC1641_FLAG register*/ +#define TSC1641_SAT_FLAG BIT(13) +#define TSC1641_SHUNT_OV_FLAG BIT(6) +#define TSC1641_SHUNT_UV_FLAG BIT(5) +#define TSC1641_LOAD_OV_FLAG BIT(4) +#define TSC1641_LOAD_UV_FLAG BIT(3) +#define TSC1641_POWER_OVER_FLAG BIT(2) +#define TSC1641_TEMP_OVER_FLAG BIT(1) + +static bool tsc1641_writeable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case TSC1641_CONFIG: + case TSC1641_MASK: + case TSC1641_RSHUNT: + case TSC1641_SOL: + case TSC1641_SUL: + case TSC1641_LOL: + case TSC1641_LUL: + case TSC1641_POL: + case TSC1641_TOL: + return true; + default: + return false; + } +} + +static bool tsc1641_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case TSC1641_SHUNT_VOLTAGE: + case TSC1641_LOAD_VOLTAGE: + case TSC1641_POWER: + case TSC1641_CURRENT: + case TSC1641_TEMP: + case TSC1641_FLAG: + case TSC1641_MANUF_ID: + case TSC1641_DIE_ID: + return true; + default: + return false; + } +} + +static const struct regmap_config tsc1641_regmap_config = { + .reg_bits = 8, + .val_bits = 16, + .use_single_write = true, + .use_single_read = true, + .max_register = TSC1641_MAX_REG, + .cache_type = REGCACHE_MAPLE, + .volatile_reg = tsc1641_volatile_reg, + .writeable_reg = tsc1641_writeable_reg, +}; + +struct tsc1641_data { + long rshunt_uohm; + long current_lsb_ua; + struct regmap *regmap; +}; + +/* + * Upper limit due to chip 16-bit shunt register, lower limit to + * prevent current and power registers overflow + */ +static inline int tsc1641_validate_shunt(u32 val) +{ + if (val < TSC1641_RSHUNT_MIN_UOHM || val > TSC1641_RSHUNT_MAX_UOHM) + return -EINVAL; + return 0; +} + +static int tsc1641_set_shunt(struct tsc1641_data *data, u32 val) +{ + struct regmap *regmap = data->regmap; + long rshunt_reg; + + /* RSHUNT register LSB is 10uOhm so need to divide further */ + rshunt_reg = DIV_ROUND_CLOSEST(val, TSC1641_RSHUNT_LSB_UOHM); + /* + * Clamp value to the nearest multiple of TSC1641_RSHUNT_LSB_UOHM + * in case shunt value provided was not a multiple + */ + data->rshunt_uohm = rshunt_reg * TSC1641_RSHUNT_LSB_UOHM; + data->current_lsb_ua = DIV_ROUND_CLOSEST(TSC1641_VSHUNT_LSB_NVOLT * 1000, + data->rshunt_uohm); + + return regmap_write(regmap, TSC1641_RSHUNT, rshunt_reg); +} + +/* + * Conversion times in uS, value in CONFIG[CT3:CT0] corresponds to index in this array + * See "Table 14. CT3 to CT0: conversion time" in: + * https://www.st.com/resource/en/datasheet/tsc1641.pdf + */ +static const int tsc1641_conv_times[] = { 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768 }; + +static int tsc1641_reg_to_upd_interval(u16 config) +{ + int idx = FIELD_GET(TSC1641_CONV_TIME_MASK, config); + + idx = clamp_val(idx, 0, ARRAY_SIZE(tsc1641_conv_times) - 1); + int conv_time = tsc1641_conv_times[idx]; + + /* Don't support sub-millisecond update interval as it's not supported in hwmon */ + conv_time = max(conv_time, TSC1641_MIN_UPDATE_INTERVAL); + /* Return nearest value in milliseconds */ + return DIV_ROUND_CLOSEST(conv_time, 1000); +} + +static u16 tsc1641_upd_interval_to_reg(long interval) +{ + /* Supported interval is 1ms - 33ms */ + interval = clamp_val(interval, 1, 33); + + int conv = interval * 1000; + int conv_bits = find_closest(conv, tsc1641_conv_times, + ARRAY_SIZE(tsc1641_conv_times)); + + return FIELD_PREP(TSC1641_CONV_TIME_MASK, conv_bits); +} + +static int tsc1641_chip_write(struct device *dev, u32 attr, long val) +{ + struct tsc1641_data *data = dev_get_drvdata(dev); + + switch (attr) { + case hwmon_chip_update_interval: + return regmap_update_bits(data->regmap, TSC1641_CONFIG, + TSC1641_CONV_TIME_MASK, + tsc1641_upd_interval_to_reg(val)); + default: + return -EOPNOTSUPP; + } +} + +static int tsc1641_chip_read(struct device *dev, u32 attr, long *val) +{ + struct tsc1641_data *data = dev_get_drvdata(dev); + u32 regval; + int ret; + + switch (attr) { + case hwmon_chip_update_interval: + ret = regmap_read(data->regmap, TSC1641_CONFIG, ®val); + if (ret) + return ret; + + *val = tsc1641_reg_to_upd_interval(regval); + return 0; + default: + return -EOPNOTSUPP; + } +} + +static int tsc1641_flag_read(struct regmap *regmap, u32 flag, long *val) +{ + unsigned int regval; + int ret; + + ret = regmap_read_bypassed(regmap, TSC1641_FLAG, ®val); + if (ret) + return ret; + + *val = !!(regval & flag); + return 0; +} + +static int tsc1641_in_read(struct device *dev, u32 attr, long *val) +{ + struct tsc1641_data *data = dev_get_drvdata(dev); + struct regmap *regmap = data->regmap; + unsigned int regval; + int ret, reg; + long sat_flag; + + switch (attr) { + case hwmon_in_input: + reg = TSC1641_LOAD_VOLTAGE; + break; + case hwmon_in_min: + reg = TSC1641_LUL; + break; + case hwmon_in_max: + reg = TSC1641_LOL; + break; + case hwmon_in_min_alarm: + return tsc1641_flag_read(regmap, TSC1641_LOAD_UV_FLAG, val); + case hwmon_in_max_alarm: + return tsc1641_flag_read(regmap, TSC1641_LOAD_OV_FLAG, val); + default: + return -EOPNOTSUPP; + } + + ret = regmap_read(regmap, reg, ®val); + if (ret) + return ret; + + /* Check if load voltage is out of range */ + if (reg == TSC1641_LOAD_VOLTAGE) { + /* Register is 15-bit max */ + if (regval & 0x8000) + return -ENODATA; + + ret = tsc1641_flag_read(regmap, TSC1641_SAT_FLAG, &sat_flag); + if (ret) + return ret; + /* Out of range conditions per datasheet */ + if (sat_flag && (regval == 0x7FFF || !regval)) + return -ENODATA; + } + + *val = regval * TSC1641_VLOAD_LSB_MVOLT; + return 0; +} + +/* Chip supports bidirectional (positive or negative) current */ +static int tsc1641_curr_read(struct device *dev, u32 attr, long *val) +{ + struct tsc1641_data *data = dev_get_drvdata(dev); + struct regmap *regmap = data->regmap; + int regval; + int ret, reg; + long sat_flag; + + /* Current limits are the shunt under/over voltage limits */ + switch (attr) { + case hwmon_curr_input: + reg = TSC1641_CURRENT; + break; + case hwmon_curr_min: + reg = TSC1641_SUL; + break; + case hwmon_curr_max: + reg = TSC1641_SOL; + break; + case hwmon_curr_min_alarm: + return tsc1641_flag_read(regmap, TSC1641_SHUNT_UV_FLAG, val); + case hwmon_curr_max_alarm: + return tsc1641_flag_read(regmap, TSC1641_SHUNT_OV_FLAG, val); + default: + return -EOPNOTSUPP; + } + /* + * Current uses shunt voltage, so check if it's out of range. + * We report current register in sysfs to stay consistent with internal + * power calculations which use current register values + */ + if (reg == TSC1641_CURRENT) { + ret = regmap_read(regmap, TSC1641_SHUNT_VOLTAGE, ®val); + if (ret) + return ret; + + ret = tsc1641_flag_read(regmap, TSC1641_SAT_FLAG, &sat_flag); + if (ret) + return ret; + + if (sat_flag && (regval == 0x7FFF || regval == 0x8000)) + return -ENODATA; + } + + ret = regmap_read(regmap, reg, ®val); + if (ret) + return ret; + + /* Current in milliamps, signed */ + *val = DIV_ROUND_CLOSEST((s16)regval * data->current_lsb_ua, 1000); + return 0; +} + +static int tsc1641_power_read(struct device *dev, u32 attr, long *val) +{ + struct tsc1641_data *data = dev_get_drvdata(dev); + struct regmap *regmap = data->regmap; + unsigned int regval; + int ret, reg; + + switch (attr) { + case hwmon_power_input: + reg = TSC1641_POWER; + break; + case hwmon_power_max: + reg = TSC1641_POL; + break; + case hwmon_power_max_alarm: + return tsc1641_flag_read(regmap, TSC1641_POWER_OVER_FLAG, val); + default: + return -EOPNOTSUPP; + } + + ret = regmap_read(regmap, reg, ®val); + if (ret) + return ret; + + *val = regval * TSC1641_POWER_LSB_UWATT; + return 0; +} + +static int tsc1641_temp_read(struct device *dev, u32 attr, long *val) +{ + struct tsc1641_data *data = dev_get_drvdata(dev); + struct regmap *regmap = data->regmap; + unsigned int regval; + int ret, reg; + + switch (attr) { + case hwmon_temp_input: + reg = TSC1641_TEMP; + break; + case hwmon_temp_max: + reg = TSC1641_TOL; + break; + case hwmon_temp_max_alarm: + return tsc1641_flag_read(regmap, TSC1641_TEMP_OVER_FLAG, val); + default: + return -EOPNOTSUPP; + } + + ret = regmap_read(regmap, reg, ®val); + if (ret) + return ret; + + /* 0x8000 means that TEMP measurement not enabled */ + if (reg == TSC1641_TEMP && regval == 0x8000) + return -ENODATA; + + /* Both temperature and limit registers are signed */ + *val = (s16)regval * TSC1641_TEMP_LSB_MDEGC; + return 0; +} + +static int tsc1641_in_write(struct device *dev, u32 attr, long val) +{ + struct tsc1641_data *data = dev_get_drvdata(dev); + struct regmap *regmap = data->regmap; + unsigned int regval; + int reg; + + switch (attr) { + case hwmon_in_min: + reg = TSC1641_LUL; + break; + case hwmon_in_max: + reg = TSC1641_LOL; + break; + default: + return -EOPNOTSUPP; + } + /* Clamp to full register range */ + val = clamp_val(val, 0, TSC1641_VLOAD_LSB_MVOLT * USHRT_MAX); + regval = DIV_ROUND_CLOSEST(val, TSC1641_VLOAD_LSB_MVOLT); + + return regmap_write(regmap, reg, regval); +} + +static int tsc1641_curr_write(struct device *dev, u32 attr, long val) +{ + struct tsc1641_data *data = dev_get_drvdata(dev); + struct regmap *regmap = data->regmap; + int reg, regval; + + switch (attr) { + case hwmon_curr_min: + reg = TSC1641_SUL; + break; + case hwmon_curr_max: + reg = TSC1641_SOL; + break; + default: + return -EOPNOTSUPP; + } + + /* Clamp to prevent over/underflow below */ + val = clamp_val(val, -TSC1641_CURR_ABS_MAX_MAMP, TSC1641_CURR_ABS_MAX_MAMP); + /* Convert val in milliamps to register */ + regval = DIV_ROUND_CLOSEST(val * 1000, data->current_lsb_ua); + /* + * Prevent signed 16-bit overflow. + * Integer arithmetic and shunt scaling can quantize values near 0x7FFF/0x8000, + * so reading and writing back may not preserve the exact original register value. + */ + regval = clamp_val(regval, SHRT_MIN, SHRT_MAX); + /* SUL and SOL registers are signed */ + return regmap_write(regmap, reg, regval & 0xFFFF); +} + +static int tsc1641_power_write(struct device *dev, u32 attr, long val) +{ + struct tsc1641_data *data = dev_get_drvdata(dev); + struct regmap *regmap = data->regmap; + unsigned int regval; + + switch (attr) { + case hwmon_power_max: + /* Clamp to full register range */ + val = clamp_val(val, 0, TSC1641_POWER_LSB_UWATT * USHRT_MAX); + regval = DIV_ROUND_CLOSEST(val, TSC1641_POWER_LSB_UWATT); + return regmap_write(regmap, TSC1641_POL, regval); + default: + return -EOPNOTSUPP; + } +} + +static int tsc1641_temp_write(struct device *dev, u32 attr, long val) +{ + struct tsc1641_data *data = dev_get_drvdata(dev); + struct regmap *regmap = data->regmap; + int regval; + + switch (attr) { + case hwmon_temp_max: + /* Clamp to full register range */ + val = clamp_val(val, TSC1641_TEMP_LSB_MDEGC * SHRT_MIN, + TSC1641_TEMP_LSB_MDEGC * SHRT_MAX); + regval = DIV_ROUND_CLOSEST(val, TSC1641_TEMP_LSB_MDEGC); + /* TOL register is signed */ + return regmap_write(regmap, TSC1641_TOL, regval & 0xFFFF); + default: + return -EOPNOTSUPP; + } +} + +static umode_t tsc1641_is_visible(const void *data, enum hwmon_sensor_types type, + u32 attr, int channel) +{ + switch (type) { + case hwmon_chip: + switch (attr) { + case hwmon_chip_update_interval: + return 0644; + default: + break; + } + break; + case hwmon_in: + switch (attr) { + case hwmon_in_input: + return 0444; + case hwmon_in_min: + case hwmon_in_max: + return 0644; + case hwmon_in_min_alarm: + case hwmon_in_max_alarm: + return 0444; + default: + break; + } + break; + case hwmon_curr: + switch (attr) { + case hwmon_curr_input: + return 0444; + case hwmon_curr_min: + case hwmon_curr_max: + return 0644; + case hwmon_curr_min_alarm: + case hwmon_curr_max_alarm: + return 0444; + default: + break; + } + break; + case hwmon_power: + switch (attr) { + case hwmon_power_input: + return 0444; + case hwmon_power_max: + return 0644; + case hwmon_power_max_alarm: + return 0444; + default: + break; + } + break; + case hwmon_temp: + switch (attr) { + case hwmon_temp_input: + return 0444; + case hwmon_temp_max: + return 0644; + case hwmon_temp_max_alarm: + return 0444; + default: + break; + } + break; + default: + break; + } + return 0; +} + +static int tsc1641_read(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long *val) +{ + switch (type) { + case hwmon_chip: + return tsc1641_chip_read(dev, attr, val); + case hwmon_in: + return tsc1641_in_read(dev, attr, val); + case hwmon_curr: + return tsc1641_curr_read(dev, attr, val); + case hwmon_power: + return tsc1641_power_read(dev, attr, val); + case hwmon_temp: + return tsc1641_temp_read(dev, attr, val); + default: + return -EOPNOTSUPP; + } +} + +static int tsc1641_write(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long val) +{ + switch (type) { + case hwmon_chip: + return tsc1641_chip_write(dev, attr, val); + case hwmon_in: + return tsc1641_in_write(dev, attr, val); + case hwmon_curr: + return tsc1641_curr_write(dev, attr, val); + case hwmon_power: + return tsc1641_power_write(dev, attr, val); + case hwmon_temp: + return tsc1641_temp_write(dev, attr, val); + default: + return -EOPNOTSUPP; + } +} + +static const struct hwmon_channel_info * const tsc1641_info[] = { + HWMON_CHANNEL_INFO(chip, + HWMON_C_UPDATE_INTERVAL), + HWMON_CHANNEL_INFO(in, + HWMON_I_INPUT | HWMON_I_MAX | HWMON_I_MAX_ALARM | + HWMON_I_MIN | HWMON_I_MIN_ALARM), + HWMON_CHANNEL_INFO(curr, + HWMON_C_INPUT | HWMON_C_MAX | HWMON_C_MAX_ALARM | + HWMON_C_MIN | HWMON_C_MIN_ALARM), + HWMON_CHANNEL_INFO(power, + HWMON_P_INPUT | HWMON_P_MAX | HWMON_P_MAX_ALARM), + HWMON_CHANNEL_INFO(temp, + HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MAX_ALARM), + NULL +}; + +static ssize_t shunt_resistor_show(struct device *dev, + struct device_attribute *da, char *buf) +{ + struct tsc1641_data *data = dev_get_drvdata(dev); + + return sysfs_emit(buf, "%li\n", data->rshunt_uohm); +} + +static ssize_t shunt_resistor_store(struct device *dev, + struct device_attribute *da, + const char *buf, size_t count) +{ + struct tsc1641_data *data = dev_get_drvdata(dev); + unsigned int val; + int ret; + + ret = kstrtouint(buf, 10, &val); + if (ret < 0) + return ret; + + ret = tsc1641_validate_shunt(val); + if (ret < 0) + return ret; + + ret = tsc1641_set_shunt(data, val); + if (ret < 0) + return ret; + return count; +} + +static const struct hwmon_ops tsc1641_hwmon_ops = { + .is_visible = tsc1641_is_visible, + .read = tsc1641_read, + .write = tsc1641_write, +}; + +static const struct hwmon_chip_info tsc1641_chip_info = { + .ops = &tsc1641_hwmon_ops, + .info = tsc1641_info, +}; + +static DEVICE_ATTR_RW(shunt_resistor); + +/* Shunt resistor value is exposed via sysfs attribute */ +static struct attribute *tsc1641_attrs[] = { + &dev_attr_shunt_resistor.attr, + NULL, +}; +ATTRIBUTE_GROUPS(tsc1641); + +static int tsc1641_init(struct device *dev, struct tsc1641_data *data) +{ + struct regmap *regmap = data->regmap; + bool active_high; + u32 shunt; + int ret; + + if (device_property_read_u32(dev, "shunt-resistor-micro-ohms", &shunt) < 0) + shunt = TSC1641_RSHUNT_DEFAULT; + + if (tsc1641_validate_shunt(shunt) < 0) { + dev_err(dev, "invalid shunt resistor value %u\n", shunt); + return -EINVAL; + } + + ret = tsc1641_set_shunt(data, shunt); + if (ret < 0) + return ret; + + ret = regmap_write(regmap, TSC1641_CONFIG, TSC1641_CONFIG_DEFAULT); + if (ret < 0) + return ret; + + active_high = device_property_read_bool(dev, "st,alert-polarity-active-high"); + + return regmap_write(regmap, TSC1641_MASK, TSC1641_MASK_DEFAULT | + FIELD_PREP(TSC1641_ALERT_POL_MASK, active_high)); +} + +static int tsc1641_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct tsc1641_data *data; + struct device *hwmon_dev; + int ret; + + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->regmap = devm_regmap_init_i2c(client, &tsc1641_regmap_config); + if (IS_ERR(data->regmap)) + return dev_err_probe(dev, PTR_ERR(data->regmap), + "failed to allocate register map\n"); + + ret = tsc1641_init(dev, data); + if (ret < 0) + return dev_err_probe(dev, ret, "failed to configure device\n"); + + hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, + data, &tsc1641_chip_info, tsc1641_groups); + if (IS_ERR(hwmon_dev)) + return PTR_ERR(hwmon_dev); + + dev_info(dev, "power monitor %s (Rshunt = %li uOhm)\n", + client->name, data->rshunt_uohm); + + return 0; +} + +static const struct i2c_device_id tsc1641_id[] = { + { "tsc1641", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, tsc1641_id); + +static const struct of_device_id __maybe_unused tsc1641_of_match[] = { + { .compatible = "st,tsc1641" }, + { }, +}; +MODULE_DEVICE_TABLE(of, tsc1641_of_match); + +static struct i2c_driver tsc1641_driver = { + .driver = { + .name = "tsc1641", + .of_match_table = of_match_ptr(tsc1641_of_match), + }, + .probe = tsc1641_probe, + .id_table = tsc1641_id, +}; + +module_i2c_driver(tsc1641_driver); + +MODULE_AUTHOR("Igor Reznichenko "); +MODULE_DESCRIPTION("tsc1641 driver"); +MODULE_LICENSE("GPL"); From 71a117d28f87884893f0398fedfdc989d76314ce Mon Sep 17 00:00:00 2001 From: Jeff Lin Date: Thu, 6 Nov 2025 18:45:19 +0800 Subject: [PATCH 085/110] hwmon/pmbus: (isl68137) Add support for raa229141 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The RAA229141A is a digital dual output multiphase (X+Y ≤ 12) PWM controller designed to be compliant with Intel VR13, VR13.HC, VR14 and VR14.Cloud specifications, targeting VCORE and auxiliary rails. The RAA229141A supports the Intel SVID interface along with PMBus V1.3 specifications, making it ideal for controlling the microprocessor core and system rails in Intel VR13, VR13.HC, VR14 and VR14.Cloud platforms. Signed-off-by: Jeff Lin Link: https://lore.kernel.org/r/20251106104519.2014853-1-jefflin994697@gmail.com Signed-off-by: Guenter Roeck --- Documentation/hwmon/isl68137.rst | 10 ++++++++++ drivers/hwmon/pmbus/isl68137.c | 14 ++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/Documentation/hwmon/isl68137.rst b/Documentation/hwmon/isl68137.rst index 5bc029c98383..e77f582c2850 100644 --- a/Documentation/hwmon/isl68137.rst +++ b/Documentation/hwmon/isl68137.rst @@ -414,6 +414,16 @@ Supported chips: Publicly available (after August 2020 launch) at the Renesas website + * Renesas RAA229141 + + Prefix: 'raa229141' + + Addresses scanned: - + + Datasheet: + + Provided by Renesas upon request and NDA + Authors: - Maxim Sloyko - Robert Lippert diff --git a/drivers/hwmon/pmbus/isl68137.c b/drivers/hwmon/pmbus/isl68137.c index 52cf62e45a86..f95088fd4dd2 100644 --- a/drivers/hwmon/pmbus/isl68137.c +++ b/drivers/hwmon/pmbus/isl68137.c @@ -65,6 +65,7 @@ enum chips { raa228246, raa229001, raa229004, + raa229141, raa229621, }; @@ -73,6 +74,7 @@ enum variants { raa_dmpvr2_1rail, raa_dmpvr2_2rail, raa_dmpvr2_2rail_nontc, + raa_dmpvr2_2rail_pmbus, raa_dmpvr2_3rail, raa_dmpvr2_hv, }; @@ -400,6 +402,17 @@ static int isl68137_probe(struct i2c_client *client) info->read_word_data = raa_dmpvr2_read_word_data; info->write_word_data = raa_dmpvr2_write_word_data; break; + case raa_dmpvr2_2rail_pmbus: + info->format[PSC_VOLTAGE_IN] = linear, + info->format[PSC_VOLTAGE_OUT] = linear, + info->format[PSC_CURRENT_IN] = linear; + info->format[PSC_CURRENT_OUT] = linear; + info->format[PSC_POWER] = linear; + info->format[PSC_TEMPERATURE] = linear; + info->pages = 2; + info->read_word_data = raa_dmpvr2_read_word_data; + info->write_word_data = raa_dmpvr2_write_word_data; + break; case raa_dmpvr2_3rail: info->read_word_data = raa_dmpvr2_read_word_data; info->write_word_data = raa_dmpvr2_write_word_data; @@ -470,6 +483,7 @@ static const struct i2c_device_id raa_dmpvr_id[] = { {"raa228246", raa_dmpvr2_2rail_nontc}, {"raa229001", raa_dmpvr2_2rail}, {"raa229004", raa_dmpvr2_2rail}, + {"raa229141", raa_dmpvr2_2rail_pmbus}, {"raa229621", raa_dmpvr2_2rail}, {} }; From 638196d43bd3c8548c6576d26475e071c023a9b3 Mon Sep 17 00:00:00 2001 From: Bruno Thomsen Date: Tue, 11 Nov 2025 15:44:05 +0100 Subject: [PATCH 086/110] hwmon: (tmp421) Check error when loading label from dt Add error checking when loading temperature channel label defined in device tree. Handling of error from of_property_read_string() is inspired by lm90 driver and therefor contain same error string. Signed-off-by: Bruno Thomsen Link: https://lore.kernel.org/r/20251111144406.7489-1-bruno.thomsen@gmail.com Signed-off-by: Guenter Roeck --- drivers/hwmon/tmp421.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/hwmon/tmp421.c b/drivers/hwmon/tmp421.c index 2e43ce8408d6..5d6babdadf89 100644 --- a/drivers/hwmon/tmp421.c +++ b/drivers/hwmon/tmp421.c @@ -369,7 +369,11 @@ static int tmp421_probe_child_from_dt(struct i2c_client *client, return -EINVAL; } - of_property_read_string(child, "label", &data->channel[i].label); + err = of_property_read_string(child, "label", &data->channel[i].label); + if (err == -ENODATA || err == -EILSEQ) { + dev_err(dev, "invalid label property in %pOFn\n", child); + return err; + } if (data->channel[i].label) data->temp_config[i] |= HWMON_T_LABEL; From 006f661e024f2f0e89f3b458a3db0b10b657afa5 Mon Sep 17 00:00:00 2001 From: Bruno Thomsen Date: Tue, 11 Nov 2025 15:44:06 +0100 Subject: [PATCH 087/110] hwmon: (tmp421) Remove duplicate return in switch-case Use single read permission return in switch-case that handles attributes in tmp421_is_visible(). Signed-off-by: Bruno Thomsen Link: https://lore.kernel.org/r/20251111144406.7489-2-bruno.thomsen@gmail.com Signed-off-by: Guenter Roeck --- drivers/hwmon/tmp421.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/hwmon/tmp421.c b/drivers/hwmon/tmp421.c index 5d6babdadf89..2ea9d3e9553d 100644 --- a/drivers/hwmon/tmp421.c +++ b/drivers/hwmon/tmp421.c @@ -250,7 +250,6 @@ static umode_t tmp421_is_visible(const void *data, enum hwmon_sensor_types type, switch (attr) { case hwmon_temp_fault: case hwmon_temp_input: - return 0444; case hwmon_temp_label: return 0444; case hwmon_temp_enable: From 6ae5101d6f3a8fbecfc7a50af076e1ce890fd62c Mon Sep 17 00:00:00 2001 From: Marius Zachmann Date: Thu, 13 Nov 2025 11:00:23 +0100 Subject: [PATCH 088/110] hwmon: (corsair-cpro) Read temperature as a signed value Convert temperature to s16 to correctly read negative temperatures. Signed-off-by: Marius Zachmann Link: https://lore.kernel.org/r/20251113100024.11103-2-mail@mariuszachmann.de [groeck: Updated subject, coding style] Signed-off-by: Guenter Roeck --- drivers/hwmon/corsair-cpro.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/hwmon/corsair-cpro.c b/drivers/hwmon/corsair-cpro.c index b7b911f8359c..f011d26eb0ef 100644 --- a/drivers/hwmon/corsair-cpro.c +++ b/drivers/hwmon/corsair-cpro.c @@ -40,7 +40,7 @@ #define CTL_GET_TMP 0x11 /* * send: byte 1 is channel, rest zero * rcv: returns temp for channel in centi-degree celsius - * in bytes 1 and 2 + * in bytes 1 and 2 as a two's complement value * returns 0x11 in byte 0 if no sensor is connected */ #define CTL_GET_VOLT 0x12 /* @@ -258,7 +258,7 @@ static int ccp_read(struct device *dev, enum hwmon_sensor_types type, ret = get_data(ccp, CTL_GET_TMP, channel, true); if (ret < 0) return ret; - *val = ret * 10; + *val = (s16)ret * 10; return 0; default: break; From 3c48eb382e57a9e2d2817f22f11f6c754e601dc8 Mon Sep 17 00:00:00 2001 From: Marius Zachmann Date: Thu, 13 Nov 2025 11:00:24 +0100 Subject: [PATCH 089/110] hwmon: (corsair-cpro) Replace magic values with constants Replace two magic values in ccp_device with existing constants. Signed-off-by: Marius Zachmann Link: https://lore.kernel.org/r/20251113100024.11103-3-mail@mariuszachmann.de [groeck: Updated subject to include subsystem/driver] Signed-off-by: Guenter Roeck --- drivers/hwmon/corsair-cpro.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/hwmon/corsair-cpro.c b/drivers/hwmon/corsair-cpro.c index f011d26eb0ef..b6e508e43fa1 100644 --- a/drivers/hwmon/corsair-cpro.c +++ b/drivers/hwmon/corsair-cpro.c @@ -90,10 +90,10 @@ struct ccp_device { u8 *cmd_buffer; u8 *buffer; int buffer_recv_size; /* number of received bytes in buffer */ - int target[6]; + int target[NUM_FANS]; DECLARE_BITMAP(temp_cnct, NUM_TEMP_SENSORS); DECLARE_BITMAP(fan_cnct, NUM_FANS); - char fan_label[6][LABEL_LENGTH]; + char fan_label[NUM_FANS][LABEL_LENGTH]; u8 firmware_ver[3]; u8 bootloader_ver[2]; }; From 93a8cb5b69c92e3ad173ed6dba3796bb2976ccbe Mon Sep 17 00:00:00 2001 From: Igor Reznichenko Date: Fri, 14 Nov 2025 23:31:34 -0800 Subject: [PATCH 090/110] MAINTAINERS: Add entry for ST TSC1641 driver Add myself as maintainer for the TSC1641 driver Signed-off-by: Igor Reznichenko Link: https://lore.kernel.org/r/20251115073134.1345535-1-igor@reznichenko.net Signed-off-by: Guenter Roeck --- MAINTAINERS | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 4a6e00df8a38..dac82e76f908 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -24356,6 +24356,14 @@ S: Maintained F: Documentation/hwmon/stpddc60.rst F: drivers/hwmon/pmbus/stpddc60.c +ST TSC1641 DRIVER +M: Igor Reznichenko +L: linux-hwmon@vger.kernel.org +S: Maintained +F: Documentation/devicetree/bindings/hwmon/st,tsc1641.yaml +F: Documentation/hwmon/tsc1641.rst +F: drivers/hwmon/tsc1641.c + ST VD55G1 DRIVER M: Benjamin Mugnier M: Sylvain Petinot From 862fa23acc678343986ab39e7f6c2ceaf14f34c2 Mon Sep 17 00:00:00 2001 From: Igor Reznichenko Date: Sat, 15 Nov 2025 10:29:08 -0800 Subject: [PATCH 091/110] Documentation/hwmon: Fix broken datasheet links for zl6100 Some of the datasheet links contained .pdf suffix which resulted in 404. This patch updates the links to the correct URLs on Renesas website. Signed-off-by: Igor Reznichenko Link: https://lore.kernel.org/r/20251115182908.1357793-1-igor@reznichenko.net Signed-off-by: Guenter Roeck --- Documentation/hwmon/zl6100.rst | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Documentation/hwmon/zl6100.rst b/Documentation/hwmon/zl6100.rst index d42ed9d3ac69..1513c9d2d461 100644 --- a/Documentation/hwmon/zl6100.rst +++ b/Documentation/hwmon/zl6100.rst @@ -9,7 +9,7 @@ Supported chips: Addresses scanned: - - Datasheet: https://www.renesas.com/us/en/document/dst/zl2004-datasheet.pdf + Datasheet: https://www.renesas.com/us/en/document/dst/zl2004-datasheet * Renesas / Intersil / Zilker Labs ZL2005 @@ -17,7 +17,7 @@ Supported chips: Addresses scanned: - - Datasheet: https://www.renesas.com/us/en/document/dst/zl2005-datasheet.pdf + Datasheet: https://www.renesas.com/us/en/document/dst/zl2005-datasheet * Renesas / Intersil / Zilker Labs ZL2006 @@ -25,7 +25,7 @@ Supported chips: Addresses scanned: - - Datasheet: https://www.renesas.com/us/en/document/dst/zl2006-datasheet.pdf + Datasheet: https://www.renesas.com/us/en/document/dst/zl2006-datasheet * Renesas / Intersil / Zilker Labs ZL2008 @@ -33,7 +33,7 @@ Supported chips: Addresses scanned: - - Datasheet: https://www.renesas.com/us/en/document/dst/zl2008-datasheet.pdf + Datasheet: https://www.renesas.com/us/en/document/dst/zl2008-datasheet * Renesas / Intersil / Zilker Labs ZL2105 @@ -41,7 +41,7 @@ Supported chips: Addresses scanned: - - Datasheet: https://www.renesas.com/us/en/document/dst/zl2105-datasheet.pdf + Datasheet: https://www.renesas.com/us/en/document/dst/zl2105-datasheet * Renesas / Intersil / Zilker Labs ZL2106 @@ -49,7 +49,7 @@ Supported chips: Addresses scanned: - - Datasheet: https://www.renesas.com/us/en/document/dst/zl2106-datasheet.pdf + Datasheet: https://www.renesas.com/us/en/document/dst/zl2106-datasheet * Renesas / Intersil / Zilker Labs ZL6100 @@ -57,7 +57,7 @@ Supported chips: Addresses scanned: - - Datasheet: https://www.renesas.com/us/en/document/dst/zl6100-datasheet.pdf + Datasheet: https://www.renesas.com/us/en/document/dst/zl6100-datasheet * Renesas / Intersil / Zilker Labs ZL6105 @@ -65,7 +65,7 @@ Supported chips: Addresses scanned: - - Datasheet: https://www.renesas.com/us/en/document/dst/zl6105-datasheet.pdf + Datasheet: https://www.renesas.com/us/en/document/dst/zl6105-datasheet * Renesas / Intersil / Zilker Labs ZL8802 From 785205fd81399bd8e49065aee3362a99d5702321 Mon Sep 17 00:00:00 2001 From: James Calligeros Date: Wed, 12 Nov 2025 21:16:52 +1000 Subject: [PATCH 092/110] hwmon: Add Apple Silicon SMC hwmon driver The System Management Controller on Apple Silicon devices is responsible for integrating and exposing the data reported by the vast array of hardware monitoring sensors present on these devices. It is also responsible for fan control, and allows users to manually set fan speeds if they so desire. Add a hwmon driver to expose current, power, temperature, and voltage monitoring sensors, as well as fan speed monitoring and control via the SMC on Apple Silicon devices. The SMC firmware has no consistency between devices, even when they share an SoC. The FourCC keys used to access sensors are almost random. An M1 Mac mini will have different FourCCs for its CPU core temperature sensors to an M1 MacBook Pro, for example. For this reason, the valid sensors for a given device are specified in a child of the SMC Devicetree node. The driver uses this information to determine which sensors to make available at runtime. Reviewed-by: Neal Gompa Acked-by: Guenter Roeck Co-developed-by: Janne Grunau Signed-off-by: Janne Grunau Signed-off-by: James Calligeros Link: https://lore.kernel.org/r/20251112-macsmc-subdevs-v5-6-728e4b91fe81@gmail.com [groeck: Added Documentation to index] Signed-off-by: Guenter Roeck --- Documentation/hwmon/index.rst | 1 + Documentation/hwmon/macsmc-hwmon.rst | 71 +++ MAINTAINERS | 2 + drivers/hwmon/Kconfig | 12 + drivers/hwmon/Makefile | 1 + drivers/hwmon/macsmc-hwmon.c | 851 +++++++++++++++++++++++++++ 6 files changed, 938 insertions(+) create mode 100644 Documentation/hwmon/macsmc-hwmon.rst create mode 100644 drivers/hwmon/macsmc-hwmon.c diff --git a/Documentation/hwmon/index.rst b/Documentation/hwmon/index.rst index d1b80bfd0fd1..85d7a686883e 100644 --- a/Documentation/hwmon/index.rst +++ b/Documentation/hwmon/index.rst @@ -144,6 +144,7 @@ Hardware Monitoring Kernel Drivers ltc4261 ltc4282 ltc4286 + macsmc-hwmon max127 max15301 max16064 diff --git a/Documentation/hwmon/macsmc-hwmon.rst b/Documentation/hwmon/macsmc-hwmon.rst new file mode 100644 index 000000000000..6903f76df62b --- /dev/null +++ b/Documentation/hwmon/macsmc-hwmon.rst @@ -0,0 +1,71 @@ +.. SPDX-License-Identifier: GPL-2.0-only + +Kernel driver macsmc-hwmon +========================== + +Supported hardware + + * Apple Silicon Macs (M1 and up) + +Author: James Calligeros + +Description +----------- + +macsmc-hwmon exposes the Apple System Management controller's +temperature, voltage, current and power sensors, as well as +fan speed and control capabilities, via hwmon. + +Because each Apple Silicon Mac exposes a different set of sensors +(e.g. the MacBooks expose battery telemetry that is not present on +the desktop Macs), sensors present on any given machine are described +via Devicetree. The driver picks these up and registers them with +hwmon when probed. + +Manual fan speed is supported via the fan_control module parameter. This +is disabled by default and marked as unsafe, as it cannot be proven that +the system will fail safe if overheating due to manual fan control being +used. + +sysfs interface +--------------- + +currX_input + Ammeter value + +currX_label + Ammeter label + +fanX_input + Current fan speed + +fanX_label + Fan label + +fanX_min + Minimum possible fan speed + +fanX_max + Maximum possible fan speed + +fanX_target + Current fan setpoint + +inX_input + Voltmeter value + +inX_label + Voltmeter label + +powerX_input + Power meter value + +powerX_label + Power meter label + +tempX_input + Temperature sensor value + +tempX_label + Temperature sensor label + diff --git a/MAINTAINERS b/MAINTAINERS index dac82e76f908..d032c598b1d5 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2438,12 +2438,14 @@ F: Documentation/devicetree/bindings/pwm/apple,s5l-fpwm.yaml F: Documentation/devicetree/bindings/spi/apple,spi.yaml F: Documentation/devicetree/bindings/spmi/apple,spmi.yaml F: Documentation/devicetree/bindings/watchdog/apple,wdt.yaml +F: Documentation/hwmon/macsmc-hwmon.rst F: arch/arm64/boot/dts/apple/ F: drivers/bluetooth/hci_bcm4377.c F: drivers/clk/clk-apple-nco.c F: drivers/cpufreq/apple-soc-cpufreq.c F: drivers/dma/apple-admac.c F: drivers/gpio/gpio-macsmc.c +F: drivers/hwmon/macsmc-hwmon.c F: drivers/pmdomain/apple/ F: drivers/i2c/busses/i2c-pasemi-core.c F: drivers/i2c/busses/i2c-pasemi-platform.c diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 8c852bcac26f..157678b821fc 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -1174,6 +1174,18 @@ config SENSORS_LTQ_CPUTEMP If you say yes here you get support for the temperature sensor inside your CPU. +config SENSORS_MACSMC_HWMON + tristate "Apple SMC (Apple Silicon)" + depends on MFD_MACSMC && OF + help + This driver enables hwmon support for current, power, temperature, + and voltage sensors, as well as fan speed reporting and control + on Apple Silicon devices. Say Y here if you have an Apple Silicon + device. + + This driver can also be built as a module. If so, the module will + be called macsmc-hwmon. + config SENSORS_MAX1111 tristate "Maxim MAX1111 Serial 8-bit ADC chip and compatibles" depends on SPI_MASTER diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index a8de5bc69f2a..eade8e3b1bde 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -148,6 +148,7 @@ obj-$(CONFIG_SENSORS_LTC4260) += ltc4260.o obj-$(CONFIG_SENSORS_LTC4261) += ltc4261.o obj-$(CONFIG_SENSORS_LTC4282) += ltc4282.o obj-$(CONFIG_SENSORS_LTQ_CPUTEMP) += ltq-cputemp.o +obj-$(CONFIG_SENSORS_MACSMC_HWMON) += macsmc-hwmon.o obj-$(CONFIG_SENSORS_MAX1111) += max1111.o obj-$(CONFIG_SENSORS_MAX127) += max127.o obj-$(CONFIG_SENSORS_MAX16065) += max16065.o diff --git a/drivers/hwmon/macsmc-hwmon.c b/drivers/hwmon/macsmc-hwmon.c new file mode 100644 index 000000000000..1c0bbec7e8eb --- /dev/null +++ b/drivers/hwmon/macsmc-hwmon.c @@ -0,0 +1,851 @@ +// SPDX-License-Identifier: GPL-2.0-only OR MIT +/* + * Apple SMC hwmon driver for Apple Silicon platforms + * + * The System Management Controller on Apple Silicon devices is responsible for + * measuring data from sensors across the SoC and machine. These include power, + * temperature, voltage and current sensors. Some "sensors" actually expose + * derived values. An example of this is the key PHPC, which is an estimate + * of the heat energy being dissipated by the SoC. + * + * While each SoC only has one SMC variant, each platform exposes a different + * set of sensors. For example, M1 MacBooks expose battery telemetry sensors + * which are not present on the M1 Mac mini. For this reason, the available + * sensors for a given platform are described in the device tree in a child + * node of the SMC device. We must walk this list of available sensors and + * populate the required hwmon data structures at runtime. + * + * Originally based on a concept by Jean-Francois Bortolotti + * + * Copyright The Asahi Linux Contributors + */ + +#include +#include +#include +#include +#include +#include + +#define MAX_LABEL_LENGTH 32 + +/* Temperature, voltage, current, power, fan(s) */ +#define NUM_SENSOR_TYPES 5 + +#define FLT_EXP_BIAS 127 +#define FLT_EXP_MASK GENMASK(30, 23) +#define FLT_MANT_BIAS 23 +#define FLT_MANT_MASK GENMASK(22, 0) +#define FLT_SIGN_MASK BIT(31) + +static bool fan_control; +module_param_unsafe(fan_control, bool, 0644); +MODULE_PARM_DESC(fan_control, + "Override the SMC to set your own fan speeds on supported machines"); + +struct macsmc_hwmon_sensor { + struct apple_smc_key_info info; + smc_key macsmc_key; + char label[MAX_LABEL_LENGTH]; + u32 attrs; +}; + +struct macsmc_hwmon_fan { + struct macsmc_hwmon_sensor now; + struct macsmc_hwmon_sensor min; + struct macsmc_hwmon_sensor max; + struct macsmc_hwmon_sensor set; + struct macsmc_hwmon_sensor mode; + char label[MAX_LABEL_LENGTH]; + u32 attrs; + bool manual; +}; + +struct macsmc_hwmon_sensors { + struct hwmon_channel_info channel_info; + struct macsmc_hwmon_sensor *sensors; + u32 count; +}; + +struct macsmc_hwmon_fans { + struct hwmon_channel_info channel_info; + struct macsmc_hwmon_fan *fans; + u32 count; +}; + +struct macsmc_hwmon { + struct device *dev; + struct apple_smc *smc; + struct device *hwmon_dev; + struct hwmon_chip_info chip_info; + /* Chip + sensor types + NULL */ + const struct hwmon_channel_info *channel_infos[1 + NUM_SENSOR_TYPES + 1]; + struct macsmc_hwmon_sensors temp; + struct macsmc_hwmon_sensors volt; + struct macsmc_hwmon_sensors curr; + struct macsmc_hwmon_sensors power; + struct macsmc_hwmon_fans fan; +}; + +static int macsmc_hwmon_read_label(struct device *dev, + enum hwmon_sensor_types type, u32 attr, + int channel, const char **str) +{ + struct macsmc_hwmon *hwmon = dev_get_drvdata(dev); + + switch (type) { + case hwmon_temp: + *str = hwmon->temp.sensors[channel].label; + break; + case hwmon_in: + *str = hwmon->volt.sensors[channel].label; + break; + case hwmon_curr: + *str = hwmon->curr.sensors[channel].label; + break; + case hwmon_power: + *str = hwmon->power.sensors[channel].label; + break; + case hwmon_fan: + *str = hwmon->fan.fans[channel].label; + break; + default: + return -EOPNOTSUPP; + } + + return 0; +} + +/* + * A number of sensors report data in a 48.16 fixed-point decimal format that is + * not used by any other function of the SMC. + */ +static int macsmc_hwmon_read_ioft_scaled(struct apple_smc *smc, smc_key key, + u64 *p, int scale) +{ + u64 val; + int ret; + + ret = apple_smc_read_u64(smc, key, &val); + if (ret < 0) + return ret; + + *p = mult_frac(val, scale, 65536); + + return 0; +} + +/* + * Many sensors report their data as IEEE-754 floats. No other SMC function uses + * them. + */ +static int macsmc_hwmon_read_f32_scaled(struct apple_smc *smc, smc_key key, + int *p, int scale) +{ + u32 fval; + u64 val; + int ret, exp; + + ret = apple_smc_read_u32(smc, key, &fval); + if (ret < 0) + return ret; + + val = ((u64)((fval & FLT_MANT_MASK) | BIT(23))); + exp = ((fval >> 23) & 0xff) - FLT_EXP_BIAS - FLT_MANT_BIAS; + + /* We never have negatively scaled SMC floats */ + val *= scale; + + if (exp > 63) + val = U64_MAX; + else if (exp < -63) + val = 0; + else if (exp < 0) + val >>= -exp; + else if (exp != 0 && (val & ~((1UL << (64 - exp)) - 1))) /* overflow */ + val = U64_MAX; + else + val <<= exp; + + if (fval & FLT_SIGN_MASK) { + if (val > (-(s64)INT_MIN)) + *p = INT_MIN; + else + *p = -val; + } else { + if (val > INT_MAX) + *p = INT_MAX; + else + *p = val; + } + + return 0; +} + +/* + * The SMC has keys of multiple types, denoted by a FourCC of the same format + * as the key ID. We don't know what data type a key encodes until we poke at it. + */ +static int macsmc_hwmon_read_key(struct apple_smc *smc, + struct macsmc_hwmon_sensor *sensor, int scale, + long *val) +{ + int ret; + + switch (sensor->info.type_code) { + /* 32-bit IEEE 754 float */ + case __SMC_KEY('f', 'l', 't', ' '): { + u32 flt_ = 0; + + ret = macsmc_hwmon_read_f32_scaled(smc, sensor->macsmc_key, + &flt_, scale); + if (ret) + return ret; + + *val = flt_; + break; + } + /* 48.16 fixed point decimal */ + case __SMC_KEY('i', 'o', 'f', 't'): { + u64 ioft = 0; + + ret = macsmc_hwmon_read_ioft_scaled(smc, sensor->macsmc_key, + &ioft, scale); + if (ret) + return ret; + + *val = (long)ioft; + break; + } + default: + return -EOPNOTSUPP; + } + + return 0; +} + +static int macsmc_hwmon_write_f32(struct apple_smc *smc, smc_key key, int value) +{ + u64 val; + u32 fval = 0; + int exp = 0, neg; + + val = abs(value); + neg = val != value; + + if (val) { + int msb = __fls(val) - exp; + + if (msb > 23) { + val >>= msb - FLT_MANT_BIAS; + exp -= msb - FLT_MANT_BIAS; + } else if (msb < 23) { + val <<= FLT_MANT_BIAS - msb; + exp += msb; + } + + fval = FIELD_PREP(FLT_SIGN_MASK, neg) | + FIELD_PREP(FLT_EXP_MASK, exp + FLT_EXP_BIAS) | + FIELD_PREP(FLT_MANT_MASK, val); + } + + return apple_smc_write_u32(smc, key, fval); +} + +static int macsmc_hwmon_write_key(struct apple_smc *smc, + struct macsmc_hwmon_sensor *sensor, long val) +{ + switch (sensor->info.type_code) { + /* 32-bit IEEE 754 float */ + case __SMC_KEY('f', 'l', 't', ' '): + return macsmc_hwmon_write_f32(smc, sensor->macsmc_key, val); + /* unsigned 8-bit integer */ + case __SMC_KEY('u', 'i', '8', ' '): + return apple_smc_write_u8(smc, sensor->macsmc_key, val); + default: + return -EOPNOTSUPP; + } +} + +static int macsmc_hwmon_read_fan(struct macsmc_hwmon *hwmon, u32 attr, int chan, + long *val) +{ + switch (attr) { + case hwmon_fan_input: + return macsmc_hwmon_read_key(hwmon->smc, + &hwmon->fan.fans[chan].now, 1, val); + case hwmon_fan_min: + return macsmc_hwmon_read_key(hwmon->smc, + &hwmon->fan.fans[chan].min, 1, val); + case hwmon_fan_max: + return macsmc_hwmon_read_key(hwmon->smc, + &hwmon->fan.fans[chan].max, 1, val); + case hwmon_fan_target: + return macsmc_hwmon_read_key(hwmon->smc, + &hwmon->fan.fans[chan].set, 1, val); + default: + return -EOPNOTSUPP; + } +} + +static int macsmc_hwmon_write_fan(struct device *dev, u32 attr, int channel, + long val) +{ + struct macsmc_hwmon *hwmon = dev_get_drvdata(dev); + long min, max; + int ret; + + if (!fan_control || hwmon->fan.fans[channel].mode.macsmc_key == 0) + return -EOPNOTSUPP; + + /* + * The SMC does no sanity checks on requested fan speeds, so we need to. + */ + ret = macsmc_hwmon_read_key(hwmon->smc, &hwmon->fan.fans[channel].min, + 1, &min); + if (ret) + return ret; + + ret = macsmc_hwmon_read_key(hwmon->smc, &hwmon->fan.fans[channel].max, + 1, &max); + if (ret) + return ret; + + if (val >= min && val <= max) { + if (!hwmon->fan.fans[channel].manual) { + /* Write 1 to mode key for manual control */ + ret = macsmc_hwmon_write_key(hwmon->smc, + &hwmon->fan.fans[channel].mode, 1); + if (ret < 0) + return ret; + + hwmon->fan.fans[channel].manual = true; + } + return macsmc_hwmon_write_key(hwmon->smc, + &hwmon->fan.fans[channel].set, val); + } else if (!val) { + if (hwmon->fan.fans[channel].manual) { + ret = macsmc_hwmon_write_key(hwmon->smc, + &hwmon->fan.fans[channel].mode, 0); + if (ret < 0) + return ret; + + hwmon->fan.fans[channel].manual = false; + } + } else { + return -EINVAL; + } + + return 0; +} + +static int macsmc_hwmon_read(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long *val) +{ + struct macsmc_hwmon *hwmon = dev_get_drvdata(dev); + int ret = 0; + + switch (type) { + case hwmon_temp: + ret = macsmc_hwmon_read_key(hwmon->smc, + &hwmon->temp.sensors[channel], 1000, val); + break; + case hwmon_in: + ret = macsmc_hwmon_read_key(hwmon->smc, + &hwmon->volt.sensors[channel], 1000, val); + break; + case hwmon_curr: + ret = macsmc_hwmon_read_key(hwmon->smc, + &hwmon->curr.sensors[channel], 1000, val); + break; + case hwmon_power: + /* SMC returns power in Watts with acceptable precision to scale to uW */ + ret = macsmc_hwmon_read_key(hwmon->smc, + &hwmon->power.sensors[channel], + 1000000, val); + break; + case hwmon_fan: + ret = macsmc_hwmon_read_fan(hwmon, attr, channel, val); + break; + default: + return -EOPNOTSUPP; + } + + return ret; +} + +static int macsmc_hwmon_write(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long val) +{ + switch (type) { + case hwmon_fan: + return macsmc_hwmon_write_fan(dev, attr, channel, val); + default: + return -EOPNOTSUPP; + } +} + +static umode_t macsmc_hwmon_fan_is_visible(const struct macsmc_hwmon_fan *fan, + u32 attr) +{ + if (fan->attrs & BIT(attr)) { + if (attr == hwmon_fan_target && fan_control && fan->mode.macsmc_key) + return 0644; + + return 0444; + } + + return 0; +} + +static umode_t macsmc_hwmon_is_visible(const void *data, + enum hwmon_sensor_types type, u32 attr, + int channel) +{ + const struct macsmc_hwmon *hwmon = data; + struct macsmc_hwmon_sensor *sensor; + + switch (type) { + case hwmon_in: + sensor = &hwmon->volt.sensors[channel]; + break; + case hwmon_curr: + sensor = &hwmon->curr.sensors[channel]; + break; + case hwmon_power: + sensor = &hwmon->power.sensors[channel]; + break; + case hwmon_temp: + sensor = &hwmon->temp.sensors[channel]; + break; + case hwmon_fan: + return macsmc_hwmon_fan_is_visible(&hwmon->fan.fans[channel], attr); + default: + return 0; + } + + /* Sensors only register ro attributes */ + if (sensor->attrs & BIT(attr)) + return 0444; + + return 0; +} + +static const struct hwmon_ops macsmc_hwmon_ops = { + .is_visible = macsmc_hwmon_is_visible, + .read = macsmc_hwmon_read, + .read_string = macsmc_hwmon_read_label, + .write = macsmc_hwmon_write, +}; + +/* + * Get the key metadata, including key data type, from the SMC. + */ +static int macsmc_hwmon_parse_key(struct device *dev, struct apple_smc *smc, + struct macsmc_hwmon_sensor *sensor, + const char *key) +{ + int ret; + + ret = apple_smc_get_key_info(smc, _SMC_KEY(key), &sensor->info); + if (ret) { + dev_dbg(dev, "Failed to retrieve key info for %s\n", key); + return ret; + } + + sensor->macsmc_key = _SMC_KEY(key); + + return 0; +} + +/* + * A sensor is a single key-value pair as made available by the SMC. + * The devicetree gives us the SMC key ID and a friendly name where the + * purpose of the sensor is known. + */ +static int macsmc_hwmon_create_sensor(struct device *dev, struct apple_smc *smc, + struct device_node *sensor_node, + struct macsmc_hwmon_sensor *sensor) +{ + const char *key, *label; + int ret; + + ret = of_property_read_string(sensor_node, "apple,key-id", &key); + if (ret) { + dev_dbg(dev, "Could not find apple,key-id in sensor node\n"); + return ret; + } + + ret = macsmc_hwmon_parse_key(dev, smc, sensor, key); + if (ret) + return ret; + + ret = of_property_read_string(sensor_node, "label", &label); + if (ret) + dev_dbg(dev, "No label found for sensor %s\n", key); + else + strscpy_pad(sensor->label, label, sizeof(sensor->label)); + + return 0; +} + +/* + * Fan data is exposed by the SMC as multiple sensors. + * + * The devicetree schema reuses apple,key-id for the actual fan speed sensor. + * Min, max and target keys do not need labels, so we can reuse label + * for naming the entire fan. + */ +static int macsmc_hwmon_create_fan(struct device *dev, struct apple_smc *smc, + struct device_node *fan_node, + struct macsmc_hwmon_fan *fan) +{ + const char *label, *now, *min, *max, *set, *mode; + int ret; + + ret = of_property_read_string(fan_node, "apple,key-id", &now); + if (ret) { + dev_err(dev, "apple,key-id not found in fan node!\n"); + return ret; + } + + ret = macsmc_hwmon_parse_key(dev, smc, &fan->now, now); + if (ret) + return ret; + + fan->attrs = HWMON_F_INPUT; + + ret = of_property_read_string(fan_node, "label", &label); + if (ret) { + dev_dbg(dev, "No label found for fan %s\n", now); + } else { + strscpy_pad(fan->label, label, sizeof(fan->label)); + fan->attrs |= HWMON_F_LABEL; + } + + /* The following keys are not required to simply monitor fan speed */ + if (!of_property_read_string(fan_node, "apple,fan-minimum", &min)) { + ret = macsmc_hwmon_parse_key(dev, smc, &fan->min, min); + if (ret) + return ret; + + fan->attrs |= HWMON_F_MIN; + } + + if (!of_property_read_string(fan_node, "apple,fan-maximum", &max)) { + ret = macsmc_hwmon_parse_key(dev, smc, &fan->max, max); + if (ret) + return ret; + + fan->attrs |= HWMON_F_MAX; + } + + if (!of_property_read_string(fan_node, "apple,fan-target", &set)) { + ret = macsmc_hwmon_parse_key(dev, smc, &fan->set, set); + if (ret) + return ret; + + fan->attrs |= HWMON_F_TARGET; + } + + if (!of_property_read_string(fan_node, "apple,fan-mode", &mode)) { + ret = macsmc_hwmon_parse_key(dev, smc, &fan->mode, mode); + if (ret) + return ret; + } + + /* Initialise fan control mode to automatic */ + fan->manual = false; + + return 0; +} + +static int macsmc_hwmon_populate_sensors(struct macsmc_hwmon *hwmon, + struct device_node *hwmon_node) +{ + struct device_node *key_node __maybe_unused; + struct macsmc_hwmon_sensor *sensor; + u32 n_current = 0, n_fan = 0, n_power = 0, n_temperature = 0, n_voltage = 0; + + for_each_child_of_node_with_prefix(hwmon_node, key_node, "current-") { + n_current++; + } + + if (n_current) { + hwmon->curr.sensors = devm_kcalloc(hwmon->dev, n_current, + sizeof(struct macsmc_hwmon_sensor), GFP_KERNEL); + if (!hwmon->curr.sensors) + return -ENOMEM; + + for_each_child_of_node_with_prefix(hwmon_node, key_node, "current-") { + sensor = &hwmon->curr.sensors[hwmon->curr.count]; + if (!macsmc_hwmon_create_sensor(hwmon->dev, hwmon->smc, key_node, sensor)) { + sensor->attrs = HWMON_C_INPUT; + + if (*sensor->label) + sensor->attrs |= HWMON_C_LABEL; + + hwmon->curr.count++; + } + } + } + + for_each_child_of_node_with_prefix(hwmon_node, key_node, "fan-") { + n_fan++; + } + + if (n_fan) { + hwmon->fan.fans = devm_kcalloc(hwmon->dev, n_fan, + sizeof(struct macsmc_hwmon_fan), GFP_KERNEL); + if (!hwmon->fan.fans) + return -ENOMEM; + + for_each_child_of_node_with_prefix(hwmon_node, key_node, "fan-") { + if (!macsmc_hwmon_create_fan(hwmon->dev, hwmon->smc, key_node, + &hwmon->fan.fans[hwmon->fan.count])) + hwmon->fan.count++; + } + } + + for_each_child_of_node_with_prefix(hwmon_node, key_node, "power-") { + n_power++; + } + + if (n_power) { + hwmon->power.sensors = devm_kcalloc(hwmon->dev, n_power, + sizeof(struct macsmc_hwmon_sensor), GFP_KERNEL); + if (!hwmon->power.sensors) + return -ENOMEM; + + for_each_child_of_node_with_prefix(hwmon_node, key_node, "power-") { + sensor = &hwmon->power.sensors[hwmon->power.count]; + if (!macsmc_hwmon_create_sensor(hwmon->dev, hwmon->smc, key_node, sensor)) { + sensor->attrs = HWMON_P_INPUT; + + if (*sensor->label) + sensor->attrs |= HWMON_P_LABEL; + + hwmon->power.count++; + } + } + } + + for_each_child_of_node_with_prefix(hwmon_node, key_node, "temperature-") { + n_temperature++; + } + + if (n_temperature) { + hwmon->temp.sensors = devm_kcalloc(hwmon->dev, n_temperature, + sizeof(struct macsmc_hwmon_sensor), GFP_KERNEL); + if (!hwmon->temp.sensors) + return -ENOMEM; + + for_each_child_of_node_with_prefix(hwmon_node, key_node, "temperature-") { + sensor = &hwmon->temp.sensors[hwmon->temp.count]; + if (!macsmc_hwmon_create_sensor(hwmon->dev, hwmon->smc, key_node, sensor)) { + sensor->attrs = HWMON_T_INPUT; + + if (*sensor->label) + sensor->attrs |= HWMON_T_LABEL; + + hwmon->temp.count++; + } + } + } + + for_each_child_of_node_with_prefix(hwmon_node, key_node, "voltage-") { + n_voltage++; + } + + if (n_voltage) { + hwmon->volt.sensors = devm_kcalloc(hwmon->dev, n_voltage, + sizeof(struct macsmc_hwmon_sensor), GFP_KERNEL); + if (!hwmon->volt.sensors) + return -ENOMEM; + + for_each_child_of_node_with_prefix(hwmon_node, key_node, "volt-") { + sensor = &hwmon->temp.sensors[hwmon->temp.count]; + if (!macsmc_hwmon_create_sensor(hwmon->dev, hwmon->smc, key_node, sensor)) { + sensor->attrs = HWMON_I_INPUT; + + if (*sensor->label) + sensor->attrs |= HWMON_I_LABEL; + + hwmon->volt.count++; + } + } + } + + return 0; +} + +/* Create NULL-terminated config arrays */ +static void macsmc_hwmon_populate_configs(u32 *configs, const struct macsmc_hwmon_sensors *sensors) +{ + int idx; + + for (idx = 0; idx < sensors->count; idx++) + configs[idx] = sensors->sensors[idx].attrs; +} + +static void macsmc_hwmon_populate_fan_configs(u32 *configs, const struct macsmc_hwmon_fans *fans) +{ + int idx; + + for (idx = 0; idx < fans->count; idx++) + configs[idx] = fans->fans[idx].attrs; +} + +static const struct hwmon_channel_info *const macsmc_chip_channel_info = + HWMON_CHANNEL_INFO(chip, HWMON_C_REGISTER_TZ); + +static int macsmc_hwmon_create_infos(struct macsmc_hwmon *hwmon) +{ + struct hwmon_channel_info *channel_info; + int i = 0; + + /* chip */ + hwmon->channel_infos[i++] = macsmc_chip_channel_info; + + if (hwmon->curr.count) { + channel_info = &hwmon->curr.channel_info; + channel_info->type = hwmon_curr; + channel_info->config = devm_kcalloc(hwmon->dev, hwmon->curr.count + 1, + sizeof(u32), GFP_KERNEL); + if (!channel_info->config) + return -ENOMEM; + + macsmc_hwmon_populate_configs((u32 *)channel_info->config, &hwmon->curr); + hwmon->channel_infos[i++] = channel_info; + } + + if (hwmon->fan.count) { + channel_info = &hwmon->fan.channel_info; + channel_info->type = hwmon_fan; + channel_info->config = devm_kcalloc(hwmon->dev, hwmon->fan.count + 1, + sizeof(u32), GFP_KERNEL); + if (!channel_info->config) + return -ENOMEM; + + macsmc_hwmon_populate_fan_configs((u32 *)channel_info->config, &hwmon->fan); + hwmon->channel_infos[i++] = channel_info; + } + + if (hwmon->power.count) { + channel_info = &hwmon->power.channel_info; + channel_info->type = hwmon_power; + channel_info->config = devm_kcalloc(hwmon->dev, hwmon->power.count + 1, + sizeof(u32), GFP_KERNEL); + if (!channel_info->config) + return -ENOMEM; + + macsmc_hwmon_populate_configs((u32 *)channel_info->config, &hwmon->power); + hwmon->channel_infos[i++] = channel_info; + } + + if (hwmon->temp.count) { + channel_info = &hwmon->temp.channel_info; + channel_info->type = hwmon_temp; + channel_info->config = devm_kcalloc(hwmon->dev, hwmon->temp.count + 1, + sizeof(u32), GFP_KERNEL); + if (!channel_info->config) + return -ENOMEM; + + macsmc_hwmon_populate_configs((u32 *)channel_info->config, &hwmon->temp); + hwmon->channel_infos[i++] = channel_info; + } + + if (hwmon->volt.count) { + channel_info = &hwmon->volt.channel_info; + channel_info->type = hwmon_in; + channel_info->config = devm_kcalloc(hwmon->dev, hwmon->volt.count + 1, + sizeof(u32), GFP_KERNEL); + if (!channel_info->config) + return -ENOMEM; + + macsmc_hwmon_populate_configs((u32 *)channel_info->config, &hwmon->volt); + hwmon->channel_infos[i++] = channel_info; + } + + return 0; +} + +static int macsmc_hwmon_probe(struct platform_device *pdev) +{ + struct apple_smc *smc = dev_get_drvdata(pdev->dev.parent); + struct macsmc_hwmon *hwmon; + int ret; + + /* + * The MFD driver will try to probe us unconditionally. Some devices + * with the SMC do not have hwmon capabilities. Only probe if we have + * a hwmon node. + */ + if (!pdev->dev.of_node) + return -ENODEV; + + hwmon = devm_kzalloc(&pdev->dev, sizeof(*hwmon), + GFP_KERNEL); + if (!hwmon) + return -ENOMEM; + + hwmon->dev = &pdev->dev; + hwmon->smc = smc; + + ret = macsmc_hwmon_populate_sensors(hwmon, hwmon->dev->of_node); + if (ret) { + dev_err(hwmon->dev, "Could not parse sensors\n"); + return ret; + } + + if (!hwmon->curr.count && !hwmon->fan.count && + !hwmon->power.count && !hwmon->temp.count && + !hwmon->volt.count) { + dev_err(hwmon->dev, + "No valid sensors found of any supported type\n"); + return -ENODEV; + } + + ret = macsmc_hwmon_create_infos(hwmon); + if (ret) + return ret; + + hwmon->chip_info.ops = &macsmc_hwmon_ops; + hwmon->chip_info.info = + (const struct hwmon_channel_info *const *)&hwmon->channel_infos; + + hwmon->hwmon_dev = devm_hwmon_device_register_with_info(&pdev->dev, + "macsmc_hwmon", hwmon, + &hwmon->chip_info, NULL); + if (IS_ERR(hwmon->hwmon_dev)) + return dev_err_probe(hwmon->dev, PTR_ERR(hwmon->hwmon_dev), + "Probing SMC hwmon device failed\n"); + + dev_dbg(hwmon->dev, "Registered SMC hwmon device. Sensors:\n"); + dev_dbg(hwmon->dev, + "Current: %d, Fans: %d, Power: %d, Temperature: %d, Voltage: %d", + hwmon->curr.count, hwmon->fan.count, + hwmon->power.count, hwmon->temp.count, + hwmon->volt.count); + + return 0; +} + +static const struct of_device_id macsmc_hwmon_of_table[] = { + { .compatible = "apple,smc-hwmon" }, + {} +}; +MODULE_DEVICE_TABLE(of, macsmc_hwmon_of_table); + +static struct platform_driver macsmc_hwmon_driver = { + .probe = macsmc_hwmon_probe, + .driver = { + .name = "macsmc-hwmon", + .of_match_table = macsmc_hwmon_of_table, + }, +}; +module_platform_driver(macsmc_hwmon_driver); + +MODULE_DESCRIPTION("Apple Silicon SMC hwmon driver"); +MODULE_AUTHOR("James Calligeros "); +MODULE_LICENSE("Dual MIT/GPL"); From d56933e74d0d691dcd642876feda0b30f397e2ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Rebe?= Date: Mon, 17 Nov 2025 19:50:48 +0100 Subject: [PATCH 093/110] hwmon: (k10temp) Add AMD Steam Deck APU ID MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add AMD Custom APU 0405 PCI ID as used in the Valve Steam Deck to k10temp. Signed-off-by: René Rebe Link: https://lore.kernel.org/r/20251117.195048.691713748773231900.rene@exactco.de Signed-off-by: Guenter Roeck --- drivers/hwmon/k10temp.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/hwmon/k10temp.c b/drivers/hwmon/k10temp.c index 7fc1d97a9971..a5d8f45b7881 100644 --- a/drivers/hwmon/k10temp.c +++ b/drivers/hwmon/k10temp.c @@ -81,6 +81,12 @@ MODULE_PARM_DESC(force, "force loading on processors with erratum 319"); */ #define AMD_I3255_STR "3255" +/* + * PCI Device IDs for AMD's Family 17h-based SOCs. + * Defining locally as IDs are not shared. + */ +#define PCI_DEVICE_ID_AMD_17H_M90H_DF_F3 0x1663 + /* * PCI Device IDs for AMD's Family 1Ah-based SOCs. * Defining locally as IDs are not shared. @@ -548,6 +554,7 @@ static const struct pci_device_id k10temp_id_table[] = { { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_17H_M40H_DF_F3) }, { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_17H_M60H_DF_F3) }, { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_17H_M70H_DF_F3) }, + { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_17H_M90H_DF_F3) }, { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_17H_MA0H_DF_F3) }, { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_19H_DF_F3) }, { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_19H_M10H_DF_F3) }, From 96710819d0ce6d1e42be751f6bbed18b6e9ea6d1 Mon Sep 17 00:00:00 2001 From: Gui-Dong Han Date: Thu, 20 Nov 2025 12:13:31 +0800 Subject: [PATCH 094/110] hwmon: (vt1211) Convert macros to functions to avoid TOCTOU The macros IN_FROM_REG, TEMP_FROM_REG, and RPM_FROM_REG evaluate their arguments multiple times. These macros are used in lockless show functions involving shared driver data, leading to Time-of-Check to Time-of-Use race conditions. For example, RPM_FROM_REG checks if a value is 0 or 255, and then uses it in a division. If the value is modified by another thread to 0 after the check but before the division, it causes a divide-by-zero error. Convert these macros to static functions. This guarantees that arguments are evaluated only once (pass-by-value), fixing the race conditions. Adhere to the principle of minimal changes by only converting the specific macros involved in these lockless contexts. Link: https://lore.kernel.org/all/CALbr=LYJ_ehtp53HXEVkSpYoub+XYSTU8Rg=o1xxMJ8=5z8B-g@mail.gmail.com/ Signed-off-by: Gui-Dong Han Link: https://lore.kernel.org/r/20251120041331.1917570-1-hanguidong02@gmail.com Signed-off-by: Guenter Roeck --- drivers/hwmon/vt1211.c | 53 ++++++++++++++++++++++++++++-------------- 1 file changed, 35 insertions(+), 18 deletions(-) diff --git a/drivers/hwmon/vt1211.c b/drivers/hwmon/vt1211.c index 386edea6b69e..1e52cabd6e24 100644 --- a/drivers/hwmon/vt1211.c +++ b/drivers/hwmon/vt1211.c @@ -142,9 +142,15 @@ struct vt1211_data { * in5 (ix = 5) is special. It's the internal 3.3V so it's scaled in the * driver according to the VT1211 BIOS porting guide */ -#define IN_FROM_REG(ix, reg) ((reg) < 3 ? 0 : (ix) == 5 ? \ - (((reg) - 3) * 15882 + 479) / 958 : \ - (((reg) - 3) * 10000 + 479) / 958) +static int in_from_reg(int ix, int reg) +{ + if (reg < 3) + return 0; + if (ix == 5) + return ((reg - 3) * 15882 + 479) / 958; + return ((reg - 3) * 10000 + 479) / 958; +} + #define IN_TO_REG(ix, val) (clamp_val((ix) == 5 ? \ ((val) * 958 + 7941) / 15882 + 3 : \ ((val) * 958 + 5000) / 10000 + 3, 0, 255)) @@ -156,10 +162,15 @@ struct vt1211_data { * temp3-7 are thermistor based so the driver returns the voltage measured at * the pin (range 0V - 2.2V). */ -#define TEMP_FROM_REG(ix, reg) ((ix) == 0 ? (reg) * 1000 : \ - (ix) == 1 ? (reg) < 51 ? 0 : \ - ((reg) - 51) * 1000 : \ - ((253 - (reg)) * 2200 + 105) / 210) +static int temp_from_reg(int ix, int reg) +{ + if (ix == 0) + return reg * 1000; + if (ix == 1) + return reg < 51 ? 0 : (reg - 51) * 1000; + return ((253 - reg) * 2200 + 105) / 210; +} + #define TEMP_TO_REG(ix, val) clamp_val( \ ((ix) == 0 ? ((val) + 500) / 1000 : \ (ix) == 1 ? ((val) + 500) / 1000 + 51 : \ @@ -167,8 +178,14 @@ struct vt1211_data { #define DIV_FROM_REG(reg) (1 << (reg)) -#define RPM_FROM_REG(reg, div) (((reg) == 0) || ((reg) == 255) ? 0 : \ - 1310720 / (reg) / DIV_FROM_REG(div)) +static int rpm_from_reg(int reg, int div) +{ + if (reg == 0 || reg == 255) + return 0; + + return 1310720 / reg / DIV_FROM_REG(div); +} + #define RPM_TO_REG(val, div) ((val) == 0 ? 255 : \ clamp_val((1310720 / (val) / \ DIV_FROM_REG(div)), 1, 254)) @@ -343,13 +360,13 @@ static ssize_t show_in(struct device *dev, struct device_attribute *attr, switch (fn) { case SHOW_IN_INPUT: - res = IN_FROM_REG(ix, data->in[ix]); + res = in_from_reg(ix, data->in[ix]); break; case SHOW_SET_IN_MIN: - res = IN_FROM_REG(ix, data->in_min[ix]); + res = in_from_reg(ix, data->in_min[ix]); break; case SHOW_SET_IN_MAX: - res = IN_FROM_REG(ix, data->in_max[ix]); + res = in_from_reg(ix, data->in_max[ix]); break; case SHOW_IN_ALARM: res = (data->alarms >> bitalarmin[ix]) & 1; @@ -417,13 +434,13 @@ static ssize_t show_temp(struct device *dev, struct device_attribute *attr, switch (fn) { case SHOW_TEMP_INPUT: - res = TEMP_FROM_REG(ix, data->temp[ix]); + res = temp_from_reg(ix, data->temp[ix]); break; case SHOW_SET_TEMP_MAX: - res = TEMP_FROM_REG(ix, data->temp_max[ix]); + res = temp_from_reg(ix, data->temp_max[ix]); break; case SHOW_SET_TEMP_MAX_HYST: - res = TEMP_FROM_REG(ix, data->temp_hyst[ix]); + res = temp_from_reg(ix, data->temp_hyst[ix]); break; case SHOW_TEMP_ALARM: res = (data->alarms >> bitalarmtemp[ix]) & 1; @@ -493,10 +510,10 @@ static ssize_t show_fan(struct device *dev, struct device_attribute *attr, switch (fn) { case SHOW_FAN_INPUT: - res = RPM_FROM_REG(data->fan[ix], data->fan_div[ix]); + res = rpm_from_reg(data->fan[ix], data->fan_div[ix]); break; case SHOW_SET_FAN_MIN: - res = RPM_FROM_REG(data->fan_min[ix], data->fan_div[ix]); + res = rpm_from_reg(data->fan_min[ix], data->fan_div[ix]); break; case SHOW_SET_FAN_DIV: res = DIV_FROM_REG(data->fan_div[ix]); @@ -751,7 +768,7 @@ static ssize_t show_pwm_auto_point_temp(struct device *dev, int ix = sensor_attr_2->index; int ap = sensor_attr_2->nr; - return sprintf(buf, "%d\n", TEMP_FROM_REG(data->pwm_ctl[ix] & 7, + return sprintf(buf, "%d\n", temp_from_reg(data->pwm_ctl[ix] & 7, data->pwm_auto_temp[ap])); } From 48593957a016cf32274e935f4346d0580271a61f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Rebe?= Date: Thu, 20 Nov 2025 14:00:09 +0100 Subject: [PATCH 095/110] hwmon: (asus-ec-sensors) add ROG STRIX X470-I GAMING MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add support for ROG STRIX X470-I GAMING Signed-off-by: René Rebe Reviewed-by: Eugene Shalygin Link: https://lore.kernel.org/r/20251120.140009.210830394703243387.rene@exactco.de Signed-off-by: Guenter Roeck --- Documentation/hwmon/asus_ec_sensors.rst | 1 + drivers/hwmon/asus-ec-sensors.c | 10 ++++++++++ 2 files changed, 11 insertions(+) diff --git a/Documentation/hwmon/asus_ec_sensors.rst b/Documentation/hwmon/asus_ec_sensors.rst index f0a92ce30a02..232885f24430 100644 --- a/Documentation/hwmon/asus_ec_sensors.rst +++ b/Documentation/hwmon/asus_ec_sensors.rst @@ -30,6 +30,7 @@ Supported boards: * ROG STRIX B550-I GAMING * ROG STRIX B650E-I GAMING WIFI * ROG STRIX B850-I GAMING WIFI + * ROG STRIX X470-I GAMING * ROG STRIX X570-E GAMING * ROG STRIX X570-E GAMING WIFI II * ROG STRIX X570-F GAMING diff --git a/drivers/hwmon/asus-ec-sensors.c b/drivers/hwmon/asus-ec-sensors.c index 8c5eadd8786d..b56a5a63cc61 100644 --- a/drivers/hwmon/asus-ec-sensors.c +++ b/drivers/hwmon/asus-ec-sensors.c @@ -598,6 +598,14 @@ static const struct ec_board_info board_info_strix_b850_i_gaming_wifi = { .family = family_amd_800_series, }; +static const struct ec_board_info board_info_strix_x470_i_gaming = { + .sensors = SENSOR_SET_TEMP_CHIPSET_CPU_MB | + SENSOR_TEMP_T_SENSOR | SENSOR_TEMP_VRM | + SENSOR_CURR_CPU | SENSOR_IN_CPU_CORE, + .mutex_path = ASUS_HW_ACCESS_MUTEX_ASMX, + .family = family_amd_400_series, +}; + static const struct ec_board_info board_info_strix_x570_e_gaming = { .sensors = SENSOR_SET_TEMP_CHIPSET_CPU_MB | SENSOR_TEMP_T_SENSOR | @@ -805,6 +813,8 @@ static const struct dmi_system_id dmi_table[] = { &board_info_strix_b650e_i_gaming), DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX B850-I GAMING WIFI", &board_info_strix_b850_i_gaming_wifi), + DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX X470-I GAMING", + &board_info_strix_x470_i_gaming), DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX X570-E GAMING", &board_info_strix_x570_e_gaming), DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX X570-E GAMING WIFI II", From cefb89592e3217884138d4450ad14bc3415e1d76 Mon Sep 17 00:00:00 2001 From: Billy Tsai Date: Thu, 20 Nov 2025 19:12:31 +0800 Subject: [PATCH 096/110] dt-bindings: hwmon: Add AST2700 compatible Adds support for the AST2700 PWM/Tach controller by extending the compatible string enumeration in the device tree binding. The AST2700 PWM/Tach hardware is compatible with the existing binding schema and requires no additional properties or modifications beyond the new compatible string. Signed-off-by: Billy Tsai Reviewed-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20251120-upstream_pwm_tach-v3-1-eaa2f9b300a2@aspeedtech.com Signed-off-by: Guenter Roeck --- .../devicetree/bindings/hwmon/aspeed,g6-pwm-tach.yaml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/hwmon/aspeed,g6-pwm-tach.yaml b/Documentation/devicetree/bindings/hwmon/aspeed,g6-pwm-tach.yaml index 9e5ed901ae54..851fb16ec7fa 100644 --- a/Documentation/devicetree/bindings/hwmon/aspeed,g6-pwm-tach.yaml +++ b/Documentation/devicetree/bindings/hwmon/aspeed,g6-pwm-tach.yaml @@ -18,8 +18,11 @@ description: | properties: compatible: - enum: - - aspeed,ast2600-pwm-tach + oneOf: + - items: + - const: aspeed,ast2700-pwm-tach + - const: aspeed,ast2600-pwm-tach + - const: aspeed,ast2600-pwm-tach reg: maxItems: 1 From edbce49ea6535a56aac3a5e211cf7af873d7221d Mon Sep 17 00:00:00 2001 From: Billy Tsai Date: Thu, 20 Nov 2025 19:12:32 +0800 Subject: [PATCH 097/110] hwmon: (aspeed-g6-pwm-tach): Add AST2700 compatible string Extends device tree support to include the AST2700 chip variant by adding its compatible string to the device match table. The AST2700 PWM/TACH hardware is compatible with the existing driver implementation used for AST2600. Signed-off-by: Billy Tsai Link: https://lore.kernel.org/r/20251120-upstream_pwm_tach-v3-2-eaa2f9b300a2@aspeedtech.com Signed-off-by: Guenter Roeck --- drivers/hwmon/aspeed-g6-pwm-tach.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/hwmon/aspeed-g6-pwm-tach.c b/drivers/hwmon/aspeed-g6-pwm-tach.c index 4174b129d1fc..44e1ecba205d 100644 --- a/drivers/hwmon/aspeed-g6-pwm-tach.c +++ b/drivers/hwmon/aspeed-g6-pwm-tach.c @@ -528,6 +528,9 @@ static const struct of_device_id aspeed_pwm_tach_match[] = { { .compatible = "aspeed,ast2600-pwm-tach", }, + { + .compatible = "aspeed,ast2700-pwm-tach", + }, {}, }; MODULE_DEVICE_TABLE(of, aspeed_pwm_tach_match); From 4faaa77d6b32347ea6af74d5fd2f34c3922aeee6 Mon Sep 17 00:00:00 2001 From: Gui-Dong Han Date: Tue, 25 Nov 2025 00:55:08 +0800 Subject: [PATCH 098/110] hwmon: (emc2103) Add locking to avoid TOCTOU The functions fan1_input_show and fan1_target_show check shared data for zero before using it as a divisor. These accesses are currently lockless. If the data changes to zero between the check and the division, it causes a divide-by-zero error. Explicitly acquire the update lock around these checks and calculations to ensure the data remains stable, preventing Time-of-Check to Time-of-Use (TOCTOU) race conditions. Link: https://lore.kernel.org/all/CALbr=LYJ_ehtp53HXEVkSpYoub+XYSTU8Rg=o1xxMJ8=5z8B-g@mail.gmail.com/ Signed-off-by: Gui-Dong Han Link: https://lore.kernel.org/r/20251124165508.4667-1-hanguidong02@gmail.com Signed-off-by: Guenter Roeck --- drivers/hwmon/emc2103.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/hwmon/emc2103.c b/drivers/hwmon/emc2103.c index 60eddc7b0270..9b8e925af030 100644 --- a/drivers/hwmon/emc2103.c +++ b/drivers/hwmon/emc2103.c @@ -277,8 +277,10 @@ fan1_input_show(struct device *dev, struct device_attribute *da, char *buf) { struct emc2103_data *data = emc2103_update_device(dev); int rpm = 0; + mutex_lock(&data->update_lock); if (data->fan_tach != 0) rpm = (FAN_RPM_FACTOR * data->fan_multiplier) / data->fan_tach; + mutex_unlock(&data->update_lock); return sprintf(buf, "%d\n", rpm); } @@ -363,10 +365,12 @@ fan1_target_show(struct device *dev, struct device_attribute *da, char *buf) struct emc2103_data *data = emc2103_update_device(dev); int rpm = 0; + mutex_lock(&data->update_lock); /* high byte of 0xff indicates disabled so return 0 */ if ((data->fan_target != 0) && ((data->fan_target & 0x1fe0) != 0x1fe0)) rpm = (FAN_RPM_FACTOR * data->fan_multiplier) / data->fan_target; + mutex_unlock(&data->update_lock); return sprintf(buf, "%d\n", rpm); } From fe598ab37e472514e769d3eeb0456cddb3b8ba9f Mon Sep 17 00:00:00 2001 From: Gui-Dong Han Date: Tue, 25 Nov 2025 00:59:00 +0800 Subject: [PATCH 099/110] hwmon: (vt8231) Convert macros to functions to avoid TOCTOU The macro FAN_FROM_REG evaluates its arguments multiple times. When used with shared driver data, this leads to Time-of-Check to Time-of-Use (TOCTOU) race conditions, potentially causing divide-by-zero errors. Convert the macro to a static function to ensure arguments are evaluated only once. Additionally, in fan_div_store, move the reading of the old register value and the calculation of the minimum limit inside the update lock. This ensures that the read-modify-write sequence operates on consistent data, preventing race conditions during fan divider updates. Link: https://lore.kernel.org/all/CALbr=LYJ_ehtp53HXEVkSpYoub+XYSTU8Rg=o1xxMJ8=5z8B-g@mail.gmail.com/ Signed-off-by: Gui-Dong Han Link: https://lore.kernel.org/r/20251124165900.4713-1-hanguidong02@gmail.com [groeck: Dropped unnecessary line split] Signed-off-by: Guenter Roeck --- drivers/hwmon/vt8231.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/drivers/hwmon/vt8231.c b/drivers/hwmon/vt8231.c index 3bf27c21845b..5757a0979f3f 100644 --- a/drivers/hwmon/vt8231.c +++ b/drivers/hwmon/vt8231.c @@ -138,7 +138,12 @@ static inline u8 FAN_TO_REG(long rpm, int div) return clamp_val(1310720 / (rpm * div), 1, 255); } -#define FAN_FROM_REG(val, div) ((val) == 0 ? 0 : 1310720 / ((val) * (div))) +static int fan_from_reg(int val, int div) +{ + if (val == 0) + return 0; + return 1310720 / (val * div); +} struct vt8231_data { unsigned short addr; @@ -561,7 +566,7 @@ static ssize_t fan_show(struct device *dev, struct device_attribute *attr, struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); int nr = sensor_attr->index; struct vt8231_data *data = vt8231_update_device(dev); - return sprintf(buf, "%d\n", FAN_FROM_REG(data->fan[nr], + return sprintf(buf, "%d\n", fan_from_reg(data->fan[nr], DIV_FROM_REG(data->fan_div[nr]))); } @@ -571,7 +576,7 @@ static ssize_t fan_min_show(struct device *dev, struct device_attribute *attr, struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); int nr = sensor_attr->index; struct vt8231_data *data = vt8231_update_device(dev); - return sprintf(buf, "%d\n", FAN_FROM_REG(data->fan_min[nr], + return sprintf(buf, "%d\n", fan_from_reg(data->fan_min[nr], DIV_FROM_REG(data->fan_div[nr]))); } @@ -613,9 +618,8 @@ static ssize_t fan_div_store(struct device *dev, struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); unsigned long val; int nr = sensor_attr->index; - int old = vt8231_read_value(data, VT8231_REG_FANDIV); - long min = FAN_FROM_REG(data->fan_min[nr], - DIV_FROM_REG(data->fan_div[nr])); + int old; + long min; int err; err = kstrtoul(buf, 10, &val); @@ -623,6 +627,8 @@ static ssize_t fan_div_store(struct device *dev, return err; mutex_lock(&data->update_lock); + old = vt8231_read_value(data, VT8231_REG_FANDIV); + min = fan_from_reg(data->fan_min[nr], DIV_FROM_REG(data->fan_div[nr])); switch (val) { case 1: data->fan_div[nr] = 0; From fe5dbe3110c559a2e6d13f114238621d89cb24f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B0=8F=E5=A4=AA?= Date: Tue, 25 Nov 2025 05:01:26 +0100 Subject: [PATCH 100/110] hwmon: (asus-ec-sensors) correct Pro WS TRX50-SAGE WIFI entry MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add missing temperature and fan sensors to Pro WS TRX50-SAGE WIFI Also: - Format VRM names to match the BIOS - Fix swapped VRM_E and VRM_W entries Signed-off-by: 小太 Signed-off-by: Eugene Shalygin Link: https://lore.kernel.org/r/20251125040140.277756-1-eugene.shalygin@gmail.com Signed-off-by: Guenter Roeck --- drivers/hwmon/asus-ec-sensors.c | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/drivers/hwmon/asus-ec-sensors.c b/drivers/hwmon/asus-ec-sensors.c index b56a5a63cc61..61b18b88ee8f 100644 --- a/drivers/hwmon/asus-ec-sensors.c +++ b/drivers/hwmon/asus-ec-sensors.c @@ -113,15 +113,19 @@ enum ec_sensors { ec_sensor_temp_t_sensor, /* VRM temperature [℃] */ ec_sensor_temp_vrm, + /* VRM east (right) temperature [℃] */ + ec_sensor_temp_vrme, + /* VRM west (left) temperature [℃] */ + ec_sensor_temp_vrmw, /* CPU Core voltage [mV] */ ec_sensor_in_cpu_core, /* CPU_Opt fan [RPM] */ ec_sensor_fan_cpu_opt, /* VRM heat sink fan [RPM] */ ec_sensor_fan_vrm_hs, - /* VRM east heat sink fan [RPM] */ + /* VRM east (right) heat sink fan [RPM] */ ec_sensor_fan_vrme_hs, - /* VRM west heat sink fan [RPM] */ + /* VRM west (left) heat sink fan [RPM] */ ec_sensor_fan_vrmw_hs, /* Chipset fan [RPM] */ ec_sensor_fan_chipset, @@ -157,6 +161,8 @@ enum ec_sensors { #define SENSOR_TEMP_MB BIT(ec_sensor_temp_mb) #define SENSOR_TEMP_T_SENSOR BIT(ec_sensor_temp_t_sensor) #define SENSOR_TEMP_VRM BIT(ec_sensor_temp_vrm) +#define SENSOR_TEMP_VRME BIT(ec_sensor_temp_vrme) +#define SENSOR_TEMP_VRMW BIT(ec_sensor_temp_vrmw) #define SENSOR_IN_CPU_CORE BIT(ec_sensor_in_cpu_core) #define SENSOR_FAN_CPU_OPT BIT(ec_sensor_fan_cpu_opt) #define SENSOR_FAN_VRM_HS BIT(ec_sensor_fan_vrm_hs) @@ -296,10 +302,14 @@ static const struct ec_sensor_info sensors_family_amd_800[] = { }; static const struct ec_sensor_info sensors_family_amd_trx_50[] = { - [ec_sensor_fan_vrmw_hs] = - EC_SENSOR("VRMW HS", hwmon_fan, 2, 0x00, 0xb4), - [ec_sensor_fan_vrme_hs] = - EC_SENSOR("VRME HS", hwmon_fan, 2, 0x00, 0xbc), + [ec_sensor_temp_cpu] = EC_SENSOR("CPU", hwmon_temp, 1, 0x00, 0x30), + [ec_sensor_temp_cpu_package] = + EC_SENSOR("CPU Package", hwmon_temp, 1, 0x00, 0x31), + [ec_sensor_temp_vrme] = EC_SENSOR("VRM_E", hwmon_temp, 1, 0x00, 0x33), + [ec_sensor_temp_vrmw] = EC_SENSOR("VRM_W", hwmon_temp, 1, 0x00, 0x34), + [ec_sensor_fan_cpu_opt] = EC_SENSOR("CPU_Opt", hwmon_fan, 2, 0x00, 0xb0), + [ec_sensor_fan_vrmw_hs] = EC_SENSOR("VRM_E HS", hwmon_fan, 2, 0x00, 0xb4), + [ec_sensor_fan_vrme_hs] = EC_SENSOR("VRM_W HS", hwmon_fan, 2, 0x00, 0xbc), [ec_sensor_temp_t_sensor] = EC_SENSOR("T_Sensor", hwmon_temp, 1, 0x01, 0x04), }; @@ -545,7 +555,9 @@ static const struct ec_board_info board_info_pro_art_x870E_creator_wifi = { static const struct ec_board_info board_info_pro_ws_trx50_sage_wifi = { /* Board also has a nct6798 */ - .sensors = SENSOR_TEMP_T_SENSOR | SENSOR_FAN_VRME_HS | SENSOR_FAN_VRMW_HS, + .sensors = SENSOR_TEMP_CPU | SENSOR_TEMP_CPU_PACKAGE | SENSOR_TEMP_VRME | + SENSOR_TEMP_VRMW | SENSOR_FAN_CPU_OPT | SENSOR_FAN_VRME_HS | + SENSOR_FAN_VRMW_HS | SENSOR_TEMP_T_SENSOR, .mutex_path = ASUS_HW_ACCESS_MUTEX_RMTW_ASMX, .family = family_amd_trx_50, }; From be89cf786d36f44d7765c572296fc9299afa428c Mon Sep 17 00:00:00 2001 From: Gui-Dong Han Date: Wed, 26 Nov 2025 19:35:42 +0800 Subject: [PATCH 101/110] hwmon: (lm87) Convert macros to functions to avoid TOCTOU The macro FAN_FROM_REG evaluates its arguments multiple times. When used in lockless contexts involving shared driver data, this causes Time-of-Check to Time-of-Use (TOCTOU) race conditions. Convert the macro to a static function. This guarantees that arguments are evaluated only once (pass-by-value), preventing the race conditions. Adhere to the principle of minimal changes by only converting macros that evaluate arguments multiple times and are used in lockless contexts. Link: https://lore.kernel.org/all/CALbr=LYJ_ehtp53HXEVkSpYoub+XYSTU8Rg=o1xxMJ8=5z8B-g@mail.gmail.com/ Signed-off-by: Gui-Dong Han Link: https://lore.kernel.org/r/20251126113542.9968-1-hanguidong02@gmail.com Signed-off-by: Guenter Roeck --- drivers/hwmon/lm87.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/drivers/hwmon/lm87.c b/drivers/hwmon/lm87.c index d2d970e73c61..37bf2d1d3d09 100644 --- a/drivers/hwmon/lm87.c +++ b/drivers/hwmon/lm87.c @@ -116,8 +116,14 @@ static u8 LM87_REG_TEMP_LOW[3] = { 0x3A, 0x38, 0x2C }; (((val) < 0 ? (val) - 500 : \ (val) + 500) / 1000)) -#define FAN_FROM_REG(reg, div) ((reg) == 255 || (reg) == 0 ? 0 : \ - (1350000 + (reg)*(div) / 2) / ((reg) * (div))) +static int fan_from_reg(int reg, int div) +{ + if (reg == 255 || reg == 0) + return 0; + + return (1350000 + reg * div / 2) / (reg * div); +} + #define FAN_TO_REG(val, div) ((val) * (div) * 255 <= 1350000 ? 255 : \ (1350000 + (val)*(div) / 2) / ((val) * (div))) @@ -465,7 +471,7 @@ static ssize_t fan_input_show(struct device *dev, struct lm87_data *data = lm87_update_device(dev); int nr = to_sensor_dev_attr(attr)->index; - return sprintf(buf, "%d\n", FAN_FROM_REG(data->fan[nr], + return sprintf(buf, "%d\n", fan_from_reg(data->fan[nr], FAN_DIV_FROM_REG(data->fan_div[nr]))); } @@ -475,7 +481,7 @@ static ssize_t fan_min_show(struct device *dev, struct device_attribute *attr, struct lm87_data *data = lm87_update_device(dev); int nr = to_sensor_dev_attr(attr)->index; - return sprintf(buf, "%d\n", FAN_FROM_REG(data->fan_min[nr], + return sprintf(buf, "%d\n", fan_from_reg(data->fan_min[nr], FAN_DIV_FROM_REG(data->fan_div[nr]))); } @@ -534,7 +540,7 @@ static ssize_t fan_div_store(struct device *dev, return err; mutex_lock(&data->update_lock); - min = FAN_FROM_REG(data->fan_min[nr], + min = fan_from_reg(data->fan_min[nr], FAN_DIV_FROM_REG(data->fan_div[nr])); switch (val) { From 9eb4fb93069a889aae955c2fbf78f9eb34ac4562 Mon Sep 17 00:00:00 2001 From: Gui-Dong Han Date: Wed, 26 Nov 2025 19:40:46 +0800 Subject: [PATCH 102/110] hwmon: (adm1029) Add locking to avoid TOCTOU The function fan_show checks shared data for zero or invalid values before using it as a divisor. These accesses are currently lockless. If the data changes to zero between the check and the division, it causes a divide-by-zero error. Explicitly acquire the update lock around these checks and calculations to ensure the data remains stable, preventing Time-of-Check to Time-of-Use (TOCTOU) race conditions. Link: https://lore.kernel.org/all/CALbr=LYJ_ehtp53HXEVkSpYoub+XYSTU8Rg=o1xxMJ8=5z8B-g@mail.gmail.com/ Signed-off-by: Gui-Dong Han Link: https://lore.kernel.org/r/20251126114047.10039-1-hanguidong02@gmail.com Signed-off-by: Guenter Roeck --- drivers/hwmon/adm1029.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/hwmon/adm1029.c b/drivers/hwmon/adm1029.c index 761c13092488..71eea8ae51b9 100644 --- a/drivers/hwmon/adm1029.c +++ b/drivers/hwmon/adm1029.c @@ -171,14 +171,17 @@ fan_show(struct device *dev, struct device_attribute *devattr, char *buf) struct adm1029_data *data = adm1029_update_device(dev); u16 val; + mutex_lock(&data->update_lock); if (data->fan[attr->index] == 0 || (data->fan_div[attr->index] & 0xC0) == 0 || data->fan[attr->index] == 255) { + mutex_unlock(&data->update_lock); return sprintf(buf, "0\n"); } val = 1880 * 120 / DIV_FROM_REG(data->fan_div[attr->index]) / data->fan[attr->index]; + mutex_unlock(&data->update_lock); return sprintf(buf, "%d\n", val); } From a4d01f3b02d0afb5e4497a1ec5d48da2a326d468 Mon Sep 17 00:00:00 2001 From: Gui-Dong Han Date: Wed, 26 Nov 2025 19:38:28 +0800 Subject: [PATCH 103/110] hwmon: (adm1026) Convert macros to functions to avoid TOCTOU The macro FAN_FROM_REG evaluates its arguments multiple times. When used in lockless contexts involving shared driver data, this causes Time-of-Check to Time-of-Use (TOCTOU) race conditions. Convert the macro to a static function. This guarantees that arguments are evaluated only once (pass-by-value), preventing the race conditions. Adhere to the principle of minimal changes by only converting macros that evaluate arguments multiple times and are used in lockless contexts. Link: https://lore.kernel.org/all/CALbr=LYJ_ehtp53HXEVkSpYoub+XYSTU8Rg=o1xxMJ8=5z8B-g@mail.gmail.com/ Signed-off-by: Gui-Dong Han Link: https://lore.kernel.org/r/20251126113828.10003-1-hanguidong02@gmail.com Signed-off-by: Guenter Roeck --- drivers/hwmon/adm1026.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/drivers/hwmon/adm1026.c b/drivers/hwmon/adm1026.c index 80d09b017d3b..c38c932e5d2a 100644 --- a/drivers/hwmon/adm1026.c +++ b/drivers/hwmon/adm1026.c @@ -197,8 +197,16 @@ static int adm1026_scaling[] = { /* .001 Volts */ #define FAN_TO_REG(val, div) ((val) <= 0 ? 0xff : \ clamp_val(1350000 / ((val) * (div)), \ 1, 254)) -#define FAN_FROM_REG(val, div) ((val) == 0 ? -1 : (val) == 0xff ? 0 : \ - 1350000 / ((val) * (div))) + +static int fan_from_reg(int val, int div) +{ + if (val == 0) + return -1; + if (val == 0xff) + return 0; + return 1350000 / (val * div); +} + #define DIV_FROM_REG(val) (1 << (val)) #define DIV_TO_REG(val) ((val) >= 8 ? 3 : (val) >= 4 ? 2 : (val) >= 2 ? 1 : 0) @@ -656,7 +664,7 @@ static ssize_t fan_show(struct device *dev, struct device_attribute *attr, struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); int nr = sensor_attr->index; struct adm1026_data *data = adm1026_update_device(dev); - return sprintf(buf, "%d\n", FAN_FROM_REG(data->fan[nr], + return sprintf(buf, "%d\n", fan_from_reg(data->fan[nr], data->fan_div[nr])); } static ssize_t fan_min_show(struct device *dev, struct device_attribute *attr, @@ -665,7 +673,7 @@ static ssize_t fan_min_show(struct device *dev, struct device_attribute *attr, struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); int nr = sensor_attr->index; struct adm1026_data *data = adm1026_update_device(dev); - return sprintf(buf, "%d\n", FAN_FROM_REG(data->fan_min[nr], + return sprintf(buf, "%d\n", fan_from_reg(data->fan_min[nr], data->fan_div[nr])); } static ssize_t fan_min_store(struct device *dev, From 2f88425ef590b7fcc2324334b342e048edc144a9 Mon Sep 17 00:00:00 2001 From: Haotian Zhang Date: Thu, 27 Nov 2025 00:26:02 +0800 Subject: [PATCH 104/110] hwmon: sy7636a: Fix regulator_enable resource leak on error path In sy7636a_sensor_probe(), regulator_enable() is called but if devm_hwmon_device_register_with_info() fails, the function returns without calling regulator_disable(), leaving the regulator enabled and leaking the reference count. Switch to devm_regulator_get_enable() to automatically manage the regulator resource. Fixes: de34a4053250 ("hwmon: sy7636a: Add temperature driver for sy7636a") Suggested-by: Guenter Roeck Signed-off-by: Haotian Zhang Link: https://lore.kernel.org/r/20251126162602.2086-1-vulab@iscas.ac.cn Signed-off-by: Guenter Roeck --- drivers/hwmon/sy7636a-hwmon.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/drivers/hwmon/sy7636a-hwmon.c b/drivers/hwmon/sy7636a-hwmon.c index a12fc0ce70e7..d51daaf63d63 100644 --- a/drivers/hwmon/sy7636a-hwmon.c +++ b/drivers/hwmon/sy7636a-hwmon.c @@ -66,18 +66,13 @@ static const struct hwmon_chip_info sy7636a_chip_info = { static int sy7636a_sensor_probe(struct platform_device *pdev) { struct regmap *regmap = dev_get_regmap(pdev->dev.parent, NULL); - struct regulator *regulator; struct device *hwmon_dev; int err; if (!regmap) return -EPROBE_DEFER; - regulator = devm_regulator_get(&pdev->dev, "vcom"); - if (IS_ERR(regulator)) - return PTR_ERR(regulator); - - err = regulator_enable(regulator); + err = devm_regulator_get_enable(&pdev->dev, "vcom"); if (err) return err; From 8ac2a19d7063231348da8d485fb7564ca120ddcc Mon Sep 17 00:00:00 2001 From: Juergen Gross Date: Wed, 26 Nov 2025 17:20:15 +0100 Subject: [PATCH 105/110] hwmon/lm78: Drop REALLY_SLOW_IO setting In lm78_isa_found() there is REALLY_SLOW_IO defined around some port accesses, probably in order to wait between multiple accesses. Unfortunately this isn't making any difference compared to not having this define since more than a decade, as REALLY_SLOW_IO needs to be defined while "#include " is called to have an effect. As there seem not to be any outstanding issues in spite of this having no effect, just drop the "#define" and add a remark to the related comment. Signed-off-by: Juergen Gross Link: https://lore.kernel.org/r/20251126162018.5676-3-jgross@suse.com Signed-off-by: Guenter Roeck --- drivers/hwmon/lm78.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/hwmon/lm78.c b/drivers/hwmon/lm78.c index 8b53bb312069..9378a47bf5af 100644 --- a/drivers/hwmon/lm78.c +++ b/drivers/hwmon/lm78.c @@ -843,17 +843,18 @@ static int __init lm78_isa_found(unsigned short address) } } -#define REALLY_SLOW_IO /* * We need the timeouts for at least some LM78-like * chips. But only if we read 'undefined' registers. + * There used to be a "#define REALLY_SLOW_IO" to enforce that, but + * this has been without any effect since more than a decade, so it + * has been dropped. */ val = inb_p(address + 1); if (inb_p(address + 2) != val || inb_p(address + 3) != val || inb_p(address + 7) != val) goto release; -#undef REALLY_SLOW_IO /* * We should be able to change the 7 LSB of the address port. The From 72e5c0d70d32fa3c6e86bf22e03d4318b4023978 Mon Sep 17 00:00:00 2001 From: Juergen Gross Date: Wed, 26 Nov 2025 17:20:16 +0100 Subject: [PATCH 106/110] hwmon/w83781d: Drop REALLY_SLOW_IO setting In w83781d_isa_found() there is REALLY_SLOW_IO defined around some port accesses, probably in order to wait between multiple accesses. Unfortunately this isn't making any difference compared to not having this define since more than a decade, as REALLY_SLOW_IO needs to be defined while "#include " is called to have an effect. As there seem not to be any outstanding issues in spite of this having no effect, just drop the "#define" and add a remark to the related comment. Signed-off-by: Juergen Gross Link: https://lore.kernel.org/r/20251126162018.5676-4-jgross@suse.com Signed-off-by: Guenter Roeck --- drivers/hwmon/w83781d.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/hwmon/w83781d.c b/drivers/hwmon/w83781d.c index 076200ed2ec9..f664c2152a6d 100644 --- a/drivers/hwmon/w83781d.c +++ b/drivers/hwmon/w83781d.c @@ -1850,10 +1850,12 @@ w83781d_isa_found(unsigned short address) } } -#define REALLY_SLOW_IO /* * We need the timeouts for at least some W83781D-like * chips. But only if we read 'undefined' registers. + * There used to be a "#define REALLY_SLOW_IO" to enforce that, but + * this has been without any effect since more than a decade, so it + * has been dropped. */ val = inb_p(address + 1); if (inb_p(address + 2) != val @@ -1862,7 +1864,6 @@ w83781d_isa_found(unsigned short address) pr_debug("Detection failed at step %d\n", 1); goto release; } -#undef REALLY_SLOW_IO /* * We should be able to change the 7 LSB of the address port. The From 02f0ad8e8de8cf5344f8f0fa26d9529b8339da47 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 27 Nov 2025 14:43:51 +0100 Subject: [PATCH 107/110] hwmon: (max6697) fix regmap leak on probe failure The i2c regmap allocated during probe is never freed. Switch to using the device managed allocator so that the regmap is released on probe failures (e.g. probe deferral) and on driver unbind. Fixes: 3a2a8cc3fe24 ("hwmon: (max6697) Convert to use regmap") Cc: stable@vger.kernel.org # 6.12 Cc: Guenter Roeck Signed-off-by: Johan Hovold Link: https://lore.kernel.org/r/20251127134351.1585-1-johan@kernel.org Signed-off-by: Guenter Roeck --- drivers/hwmon/max6697.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hwmon/max6697.c b/drivers/hwmon/max6697.c index 6b4a52838818..dd906cf491ca 100644 --- a/drivers/hwmon/max6697.c +++ b/drivers/hwmon/max6697.c @@ -542,7 +542,7 @@ static int max6697_probe(struct i2c_client *client) struct regmap *regmap; int err; - regmap = regmap_init_i2c(client, &max6697_regmap_config); + regmap = devm_regmap_init_i2c(client, &max6697_regmap_config); if (IS_ERR(regmap)) return PTR_ERR(regmap); From 07272e883fc61574b8367d44de48917f622cdd83 Mon Sep 17 00:00:00 2001 From: Gui-Dong Han Date: Fri, 28 Nov 2025 20:38:16 +0800 Subject: [PATCH 108/110] hwmon: (w83l786ng) Convert macros to functions to avoid TOCTOU The macros FAN_FROM_REG and TEMP_FROM_REG evaluate their arguments multiple times. When used in lockless contexts involving shared driver data, this causes Time-of-Check to Time-of-Use (TOCTOU) race conditions. Convert the macros to static functions. This guarantees that arguments are evaluated only once (pass-by-value), preventing the race conditions. Adhere to the principle of minimal changes by only converting macros that evaluate arguments multiple times and are used in lockless contexts. Link: https://lore.kernel.org/all/CALbr=LYJ_ehtp53HXEVkSpYoub+XYSTU8Rg=o1xxMJ8=5z8B-g@mail.gmail.com/ Fixes: 85f03bccd6e0 ("hwmon: Add support for Winbond W83L786NG/NR") Cc: stable@vger.kernel.org Signed-off-by: Gui-Dong Han Link: https://lore.kernel.org/r/20251128123816.3670-1-hanguidong02@gmail.com Signed-off-by: Guenter Roeck --- drivers/hwmon/w83l786ng.c | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/drivers/hwmon/w83l786ng.c b/drivers/hwmon/w83l786ng.c index 9b81bd406e05..1d9109ca1585 100644 --- a/drivers/hwmon/w83l786ng.c +++ b/drivers/hwmon/w83l786ng.c @@ -76,15 +76,25 @@ FAN_TO_REG(long rpm, int div) return clamp_val((1350000 + rpm * div / 2) / (rpm * div), 1, 254); } -#define FAN_FROM_REG(val, div) ((val) == 0 ? -1 : \ - ((val) == 255 ? 0 : \ - 1350000 / ((val) * (div)))) +static int fan_from_reg(int val, int div) +{ + if (val == 0) + return -1; + if (val == 255) + return 0; + return 1350000 / (val * div); +} /* for temp */ #define TEMP_TO_REG(val) (clamp_val(((val) < 0 ? (val) + 0x100 * 1000 \ : (val)) / 1000, 0, 0xff)) -#define TEMP_FROM_REG(val) (((val) & 0x80 ? \ - (val) - 0x100 : (val)) * 1000) + +static int temp_from_reg(int val) +{ + if (val & 0x80) + return (val - 0x100) * 1000; + return val * 1000; +} /* * The analog voltage inputs have 8mV LSB. Since the sysfs output is @@ -280,7 +290,7 @@ static ssize_t show_##reg(struct device *dev, struct device_attribute *attr, \ int nr = to_sensor_dev_attr(attr)->index; \ struct w83l786ng_data *data = w83l786ng_update_device(dev); \ return sprintf(buf, "%d\n", \ - FAN_FROM_REG(data->reg[nr], DIV_FROM_REG(data->fan_div[nr]))); \ + fan_from_reg(data->reg[nr], DIV_FROM_REG(data->fan_div[nr]))); \ } show_fan_reg(fan); @@ -347,7 +357,7 @@ store_fan_div(struct device *dev, struct device_attribute *attr, /* Save fan_min */ mutex_lock(&data->update_lock); - min = FAN_FROM_REG(data->fan_min[nr], DIV_FROM_REG(data->fan_div[nr])); + min = fan_from_reg(data->fan_min[nr], DIV_FROM_REG(data->fan_div[nr])); data->fan_div[nr] = DIV_TO_REG(val); @@ -409,7 +419,7 @@ show_temp(struct device *dev, struct device_attribute *attr, char *buf) int nr = sensor_attr->nr; int index = sensor_attr->index; struct w83l786ng_data *data = w83l786ng_update_device(dev); - return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp[nr][index])); + return sprintf(buf, "%d\n", temp_from_reg(data->temp[nr][index])); } static ssize_t From b8d5acdcf525f44e521ca4ef51dce4dac403dab4 Mon Sep 17 00:00:00 2001 From: Gui-Dong Han Date: Fri, 28 Nov 2025 20:47:09 +0800 Subject: [PATCH 109/110] hwmon: (max16065) Use local variable to avoid TOCTOU In max16065_current_show, data->curr_sense is read twice: once for the error check and again for the calculation. Since i2c_smbus_read_byte_data returns negative error codes on failure, if the data changes to an error code between the check and the use, ADC_TO_CURR results in an incorrect calculation. Read data->curr_sense into a local variable to ensure consistency. Note that data->curr_gain is constant and safe to access directly. This aligns max16065_current_show with max16065_input_show, which already uses a local variable for the same reason. Link: https://lore.kernel.org/all/CALbr=LYJ_ehtp53HXEVkSpYoub+XYSTU8Rg=o1xxMJ8=5z8B-g@mail.gmail.com/ Fixes: f5bae2642e3d ("hwmon: Driver for MAX16065 System Manager and compatibles") Cc: stable@vger.kernel.org Signed-off-by: Gui-Dong Han Link: https://lore.kernel.org/r/20251128124709.3876-1-hanguidong02@gmail.com Signed-off-by: Guenter Roeck --- drivers/hwmon/max16065.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/hwmon/max16065.c b/drivers/hwmon/max16065.c index 0ccb5eb596fc..4c9e7892a73c 100644 --- a/drivers/hwmon/max16065.c +++ b/drivers/hwmon/max16065.c @@ -216,12 +216,13 @@ static ssize_t max16065_current_show(struct device *dev, struct device_attribute *da, char *buf) { struct max16065_data *data = max16065_update_device(dev); + int curr_sense = data->curr_sense; - if (unlikely(data->curr_sense < 0)) - return data->curr_sense; + if (unlikely(curr_sense < 0)) + return curr_sense; return sysfs_emit(buf, "%d\n", - ADC_TO_CURR(data->curr_sense, data->curr_gain)); + ADC_TO_CURR(curr_sense, data->curr_gain)); } static ssize_t max16065_limit_store(struct device *dev, From 30ca0e049f507001c6377e28482a636689351f64 Mon Sep 17 00:00:00 2001 From: Gabriel Marcano Date: Fri, 28 Nov 2025 11:16:44 -0800 Subject: [PATCH 110/110] hwmon: (dell-smm) Add Dell G5 5505 to fan control whitelist MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Allow manual PWM control on Dell G5 5505 (and SE). Signed-off-by: Gabriel Marcano Link: https://lore.kernel.org/r/20251128191650.6191-1-gabemarcano@yahoo.com Acked-by: Pali Rohár Signed-off-by: Guenter Roeck --- drivers/hwmon/dell-smm-hwmon.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/hwmon/dell-smm-hwmon.c b/drivers/hwmon/dell-smm-hwmon.c index cbe1a74a3dee..683baf361c4c 100644 --- a/drivers/hwmon/dell-smm-hwmon.c +++ b/drivers/hwmon/dell-smm-hwmon.c @@ -1533,6 +1533,15 @@ static const struct i8k_fan_control_data i8k_fan_control_data[] __initconst = { }; static const struct dmi_system_id i8k_whitelist_fan_control[] __initconst = { + { + .ident = "Dell G5 5505", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "G5 5505"), + + }, + .driver_data = (void *)&i8k_fan_control_data[I8K_FAN_34A3_35A3], + }, { .ident = "Dell Latitude 5480", .matches = {