From 32faf8b9de3e0dc61a50a9c85634d8ac1c02ee33 Mon Sep 17 00:00:00 2001 From: Wensheng Wang Date: Tue, 14 Apr 2026 17:29:20 +0800 Subject: [PATCH 01/81] dt-bindings: hwmon: Add MPS mp2985 Add support for MPS mp2985 controller. Acked-by: Krzysztof Kozlowski Signed-off-by: Wensheng Wang Link: https://lore.kernel.org/r/20260414092921.1067735-1-wenswang@yeah.net 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 23fd4513933a..44ee92f1a09f 100644 --- a/Documentation/devicetree/bindings/trivial-devices.yaml +++ b/Documentation/devicetree/bindings/trivial-devices.yaml @@ -336,6 +336,8 @@ properties: - mps,mp29612 # Monolithic Power Systems Inc. multi-phase controller mp29816 - mps,mp29816 + # Monolithic Power Systems Inc. multi-phase controller mp2985 + - mps,mp2985 # Monolithic Power Systems Inc. multi-phase controller mp2993 - mps,mp2993 # Monolithic Power Systems Inc. hot-swap protection device From e656735fe32c59cc323c41e7922e77ac7bcd9f68 Mon Sep 17 00:00:00 2001 From: Wensheng Wang Date: Tue, 14 Apr 2026 17:29:21 +0800 Subject: [PATCH 02/81] hwmon: add MP2985 driver Add support for MPS mp2985 controller. This driver exposes telemetry and limit value readings and writtings. Signed-off-by: Wensheng Wang Link: https://lore.kernel.org/r/20260414092921.1067735-2-wenswang@yeah.net Signed-off-by: Guenter Roeck --- Documentation/hwmon/index.rst | 1 + Documentation/hwmon/mp2985.rst | 147 ++++++++++++ MAINTAINERS | 7 + drivers/hwmon/pmbus/Kconfig | 9 + drivers/hwmon/pmbus/Makefile | 1 + drivers/hwmon/pmbus/mp2985.c | 402 +++++++++++++++++++++++++++++++++ 6 files changed, 567 insertions(+) create mode 100644 Documentation/hwmon/mp2985.rst create mode 100644 drivers/hwmon/pmbus/mp2985.c diff --git a/Documentation/hwmon/index.rst b/Documentation/hwmon/index.rst index 8b655e5d6b68..96612a16f93a 100644 --- a/Documentation/hwmon/index.rst +++ b/Documentation/hwmon/index.rst @@ -185,6 +185,7 @@ Hardware Monitoring Kernel Drivers mp2925 mp29502 mp2975 + mp2985 mp2993 mp5023 mp5920 diff --git a/Documentation/hwmon/mp2985.rst b/Documentation/hwmon/mp2985.rst new file mode 100644 index 000000000000..87a39c8a300c --- /dev/null +++ b/Documentation/hwmon/mp2985.rst @@ -0,0 +1,147 @@ +.. SPDX-License-Identifier: GPL-2.0 + +Kernel driver mp2985 +==================== + +Supported chips: + + * MPS mp2985 + + Prefix: 'mp2985' + +Author: + + Wensheng Wang + +Description +----------- + +This driver implements support for Monolithic Power Systems, Inc. (MPS) +MP2985 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 b539be153f6a..9c2ebcfe7b05 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -18085,6 +18085,13 @@ S: Maintained F: Documentation/hwmon/mp29502.rst F: drivers/hwmon/pmbus/mp29502.c +MPS MP2985 DRIVER +M: Wensheng Wang +L: linux-hwmon@vger.kernel.org +S: Maintained +F: Documentation/hwmon/mp2985.rst +F: drivers/hwmon/pmbus/mp2985.c + MPS MP2993 DRIVER M: Noah Wang L: linux-hwmon@vger.kernel.org diff --git a/drivers/hwmon/pmbus/Kconfig b/drivers/hwmon/pmbus/Kconfig index 8f4bff375ecb..8a2c3ea50c8d 100644 --- a/drivers/hwmon/pmbus/Kconfig +++ b/drivers/hwmon/pmbus/Kconfig @@ -456,6 +456,15 @@ config SENSORS_MP2975 This driver can also be built as a module. If so, the module will be called mp2975. +config SENSORS_MP2985 + tristate "MPS MP2985" + help + If you say yes here you get hardware monitoring support for MPS + MP2985 Dual Loop Digital Multi-Phase Controller. + + This driver can also be built as a module. If so, the module will + be called mp2985. + config SENSORS_MP2993 tristate "MPS MP2993" help diff --git a/drivers/hwmon/pmbus/Makefile b/drivers/hwmon/pmbus/Makefile index 7129b62bc00f..6022614722bd 100644 --- a/drivers/hwmon/pmbus/Makefile +++ b/drivers/hwmon/pmbus/Makefile @@ -46,6 +46,7 @@ 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_MP2985) += mp2985.o obj-$(CONFIG_SENSORS_MP2993) += mp2993.o obj-$(CONFIG_SENSORS_MP5023) += mp5023.o obj-$(CONFIG_SENSORS_MP5920) += mp5920.o diff --git a/drivers/hwmon/pmbus/mp2985.c b/drivers/hwmon/pmbus/mp2985.c new file mode 100644 index 000000000000..b0a4796ca8ce --- /dev/null +++ b/drivers/hwmon/pmbus/mp2985.c @@ -0,0 +1,402 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Hardware monitoring driver for MPS Multi-phase Digital VR Controllers(MP2985) + * + * Copyright (C) 2026 MPS + */ + +#include +#include +#include +#include +#include "pmbus.h" + +/* + * Vender specific register READ_PIN_EST(0x93), READ_IIN_EST(0x8E), + * MFR_VR_MULTI_CONFIG_R1(0x0D) and MFR_VR_MULTI_CONFIG_R2(0x1D). + * The READ_PIN_EST is used to read pin telemetry, the READ_IIN_EST + * is used to read iin telemetry and the MFR_VR_MULTI_CONFIG_R1, + * MFR_VR_MULTI_CONFIG_R2 are used to obtain vid scale. + */ +#define READ_PIN_EST 0x93 +#define READ_IIN_EST 0x8E +#define MFR_VR_MULTI_CONFIG_R1 0x0D +#define MFR_VR_MULTI_CONFIG_R2 0x1D + +#define MP2985_VOUT_DIV 64 +#define MP2985_VOUT_OVUV_UINT 125 +#define MP2985_VOUT_OVUV_DIV 64 + +#define MP2985_PAGE_NUM 2 + +#define MP2985_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 MP2985_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 mp2985_data { + struct pmbus_driver_info info; + int vout_scale[MP2985_PAGE_NUM]; + int vid_offset[MP2985_PAGE_NUM]; +}; + +#define to_mp2985_data(x) container_of(x, struct mp2985_data, info) + +static u16 mp2985_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; + + /* + * The MP2985 does not support negtive limit value, if a negtive + * limit value is written, the limit value will become to 0. And + * the maximum positive limit value is limitted to 0x3FF. + */ + if (mantissa < 0) { + mantissa = 0; + } else { + if (exponent > target_exponent) { + mantissa = (1023 >> (exponent - target_exponent)) >= mantissa ? + mantissa << (exponent - target_exponent) : + 0x3FF; + } else { + mantissa = clamp_val(mantissa >> (target_exponent - exponent), + 0, 0x3FF); + } + } + + return mantissa | ((expect_exponent << 11) & 0xf800); +} + +static int mp2985_read_byte_data(struct i2c_client *client, int page, int reg) +{ + int ret; + + switch (reg) { + case PMBUS_VOUT_MODE: + /* + * The MP2985 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 mp2985_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 mp2985_data *data = to_mp2985_data(info); + int ret; + + switch (reg) { + case PMBUS_READ_VOUT: + ret = pmbus_read_word_data(client, page, phase, reg); + if (ret < 0) + return ret; + + /* + * The MP2985 supports three vout mode, direct, linear11 and vid mode. + * In vid mode, the MP2985 vout telemetry has 49 vid step offset, but + * PMBUS_VOUT_OV_FAULT_LIMIT and PMBUS_VOUT_UV_FAULT_LIMIT do not take + * this into consideration, their resolution are 1.953125mV/LSB, as a + * result, format[PSC_VOLTAGE_OUT] can not be set to vid mode directly. + * Adding extra vid_offset variable for vout telemetry. + */ + ret = clamp_val(DIV_ROUND_CLOSEST(((ret & GENMASK(11, 0)) + + data->vid_offset[page]) * + data->vout_scale[page], MP2985_VOUT_DIV), + 0, 0x7FFF); + break; + case PMBUS_READ_IIN: + /* + * The MP2985 has standard PMBUS_READ_IIN register(0x89), but this is + * not used to read the input current of per rail. The input current + * is read through the vender redefined register READ_IIN_EST(0x8E). + */ + ret = pmbus_read_word_data(client, page, phase, READ_IIN_EST); + break; + case PMBUS_READ_PIN: + /* + * The MP2985 has standard PMBUS_READ_PIN register(0x97), but this + * is not used to read the input power of per rail. The input power + * of per rail is read through the vender redefined register + * READ_PIN_EST(0x93). + */ + ret = pmbus_read_word_data(client, page, phase, READ_PIN_EST); + 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)) * MP2985_VOUT_OVUV_UINT, + MP2985_VOUT_OVUV_DIV); + break; + case PMBUS_STATUS_WORD: + case PMBUS_READ_VIN: + case PMBUS_READ_IOUT: + case PMBUS_READ_POUT: + 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: + /* + * These register is not explicitly handled by the driver, + * as a result, return -ENODATA directly. + */ + ret = -ENODATA; + break; + default: + /* + * The MP2985 do not support other telemetry and limit value + * reading, so, return -EINVAL directly. + */ + ret = -EINVAL; + break; + } + + return ret; +} + +static int mp2985_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 MP2985 is linear11 format, and the exponent is a + * constant value(5'b11101), so the exponent of word + * parameter should be converted to 5'b11101(0x1D). + */ + ret = pmbus_write_word_data(client, page, reg, + mp2985_linear_exp_transfer(word, 0x1D)); + 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)) | + clamp_val(DIV_ROUND_CLOSEST(word * MP2985_VOUT_OVUV_DIV, + MP2985_VOUT_OVUV_UINT), 0, 0xFFF)); + break; + case PMBUS_OT_FAULT_LIMIT: + case PMBUS_OT_WARN_LIMIT: + /* + * The PMBUS_OT_FAULT_LIMIT and PMBUS_OT_WARN_LIMIT of + * MP2985 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, + mp2985_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 MP2985 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, + mp2985_linear_exp_transfer(word, + FIELD_GET(GENMASK(15, 11), + ret))); + break; + default: + /* + * The MP2985 do not support other limit value configuration, + * so, return -EINVAL directly. + */ + ret = -EINVAL; + break; + } + + return ret; +} + +static int +mp2985_identify_vout_scale(struct i2c_client *client, struct pmbus_driver_info *info, + int page) +{ + struct mp2985_data *data = to_mp2985_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; + + /* + * The MP2985 supports three vout mode. If PMBUS_VOUT_MODE + * bit5 is 1, it is vid mode. If PMBUS PMBUS_VOUT_MODE bit4 + * is 1, it is linear11 mode, the vout scale is 1.953125mv/LSB. + * If PMBUS PMBUS_VOUT_MODE bit6 is 1, it is direct mode, the + * vout scale is 1mv/LSB. In vid mode, the MP2985 vout telemetry + * has 49 vid step offset. + */ + if (FIELD_GET(BIT(5), ret)) { + ret = i2c_smbus_write_byte_data(client, PMBUS_PAGE, 2); + if (ret < 0) + return ret; + + ret = i2c_smbus_read_word_data(client, page == 0 ? + MFR_VR_MULTI_CONFIG_R1 : + MFR_VR_MULTI_CONFIG_R2); + if (ret < 0) + return ret; + + if (page == 0) { + if (FIELD_GET(BIT(4), ret)) + data->vout_scale[page] = 320; + else + data->vout_scale[page] = 640; + } else { + if (FIELD_GET(BIT(3), ret)) + data->vout_scale[page] = 320; + else + data->vout_scale[page] = 640; + } + + data->vid_offset[page] = 49; + + /* + * For vid mode, the MP2985 should be changed to page 2 + * to obtain vout scale value, this may confuse the PMBus + * core. To avoid this, switch back to the previous page + * again. + */ + ret = i2c_smbus_write_byte_data(client, PMBUS_PAGE, page); + if (ret < 0) + return ret; + } else if (FIELD_GET(BIT(4), ret)) { + data->vout_scale[page] = 125; + data->vid_offset[page] = 0; + } else { + data->vout_scale[page] = 64; + data->vid_offset[page] = 0; + } + + return 0; +} + +static int mp2985_identify(struct i2c_client *client, struct pmbus_driver_info *info) +{ + int ret; + + ret = mp2985_identify_vout_scale(client, info, 0); + if (ret < 0) + return ret; + + return mp2985_identify_vout_scale(client, info, 1); +} + +static struct pmbus_driver_info mp2985_info = { + .pages = MP2985_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] = MP2985_RAIL1_FUNC, + .func[1] = MP2985_RAIL2_FUNC, + .read_word_data = mp2985_read_word_data, + .read_byte_data = mp2985_read_byte_data, + .write_word_data = mp2985_write_word_data, + .identify = mp2985_identify, +}; + +static int mp2985_probe(struct i2c_client *client) +{ + struct mp2985_data *data; + + data = devm_kzalloc(&client->dev, sizeof(struct mp2985_data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + memcpy(&data->info, &mp2985_info, sizeof(mp2985_info)); + + return pmbus_do_probe(client, &data->info); +} + +static const struct i2c_device_id mp2985_id[] = { + {"mp2985", 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, mp2985_id); + +static const struct of_device_id __maybe_unused mp2985_of_match[] = { + {.compatible = "mps,mp2985"}, + {} +}; +MODULE_DEVICE_TABLE(of, mp2985_of_match); + +static struct i2c_driver mp2985_driver = { + .driver = { + .name = "mp2985", + .of_match_table = mp2985_of_match, + }, + .probe = mp2985_probe, + .id_table = mp2985_id, +}; + +module_i2c_driver(mp2985_driver); + +MODULE_AUTHOR("Wensheng Wang "); +MODULE_DESCRIPTION("PMBus driver for MPS MP2985 device"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("PMBUS"); From a7c5aab8105d04d0784deabaa544f2fe581cd661 Mon Sep 17 00:00:00 2001 From: Flaviu Nistor Date: Fri, 17 Apr 2026 08:45:11 +0300 Subject: [PATCH 03/81] docs: hwmon: (lm75) Add section for sysfs interface Similar to other HWMON sensors add a section in the documentation describing the sysfs attributes, their permissions and a short description. Signed-off-by: Flaviu Nistor Link: https://lore.kernel.org/r/20260417054511.5432-1-flaviu.nistor@gmail.com [groeck: Removed trailing ":" from attribute names; fixed minor typo] Signed-off-by: Guenter Roeck --- Documentation/hwmon/lm75.rst | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/Documentation/hwmon/lm75.rst b/Documentation/hwmon/lm75.rst index 4269da04508e..ca46754e028b 100644 --- a/Documentation/hwmon/lm75.rst +++ b/Documentation/hwmon/lm75.rst @@ -181,3 +181,19 @@ is supported by this driver, other specific enhancements are not. The LM77 is not supported, contrary to what we pretended for a long time. Both chips are simply not compatible, value encoding differs. + +sysfs-Interface +--------------- + +The following list includes the sysfs attributes that the driver provides, their +permissions and a short description: + +=============================== ======= =========================================== +Name Perm Description +=============================== ======= =========================================== +temp1_input RO Temperature input +temp1_label RO Descriptive name for the sensor +temp1_max RW Maximum temperature +temp1_max_hyst RW Maximum hysteresis temperature +update_interval RW Update conversions interval in milliseconds +=============================== ======= =========================================== From 42ef8bb116dae7610b0af65d7d819cd9239ad121 Mon Sep 17 00:00:00 2001 From: Flaviu Nistor Date: Fri, 17 Apr 2026 08:57:00 +0300 Subject: [PATCH 04/81] hwmon: (lm75) Add explicit default cases in lm75_is_visible() Add explicit default labels to switch statements in lm75_is_visible(). This makes the control flow explicit and improves readability, but also keeps consistency to other usage of the switch statement in the driver. Signed-off-by: Flaviu Nistor Link: https://lore.kernel.org/r/20260417055700.5739-1-flaviu.nistor@gmail.com [groeck: Reformatted description for line length] Signed-off-by: Guenter Roeck --- drivers/hwmon/lm75.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/hwmon/lm75.c b/drivers/hwmon/lm75.c index c283443e363b..a47ce32df983 100644 --- a/drivers/hwmon/lm75.c +++ b/drivers/hwmon/lm75.c @@ -539,6 +539,8 @@ static umode_t lm75_is_visible(const void *data, enum hwmon_sensor_types type, if (config_data->params->num_sample_times > 1) return 0644; return 0444; + default: + break; } break; case hwmon_temp: @@ -555,6 +557,8 @@ static umode_t lm75_is_visible(const void *data, enum hwmon_sensor_types type, if (config_data->params->alarm) return 0444; break; + default: + break; } break; default: From dca421c6efca6f4d04dfbdac6a6907781f9fd311 Mon Sep 17 00:00:00 2001 From: Victor Duicu Date: Thu, 23 Apr 2026 13:56:40 +0300 Subject: [PATCH 05/81] hwmon: (mcp9982) Add external diode fault read Add external diode fault read capability to the MCP998X/33 and MCP998XD/33D driver. Signed-off-by: Victor Duicu Link: https://lore.kernel.org/r/20260423-add-external-diode-fault-read-v2-1-4004bb5f7d55@microchip.com Signed-off-by: Guenter Roeck --- drivers/hwmon/mcp9982.c | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/drivers/hwmon/mcp9982.c b/drivers/hwmon/mcp9982.c index 26c69e3388ab..9e19e2697e25 100644 --- a/drivers/hwmon/mcp9982.c +++ b/drivers/hwmon/mcp9982.c @@ -92,19 +92,19 @@ static const struct hwmon_channel_info * const mcp9985_info[] = { HWMON_T_INPUT | HWMON_T_LABEL | HWMON_T_MIN | HWMON_T_MIN_ALARM | HWMON_T_MAX | HWMON_T_MAX_ALARM | HWMON_T_MAX_HYST | HWMON_T_CRIT | HWMON_T_CRIT_ALARM | - HWMON_T_CRIT_HYST, + HWMON_T_CRIT_HYST | HWMON_T_FAULT, HWMON_T_INPUT | HWMON_T_LABEL | HWMON_T_MIN | HWMON_T_MIN_ALARM | HWMON_T_MAX | HWMON_T_MAX_ALARM | HWMON_T_MAX_HYST | HWMON_T_CRIT | HWMON_T_CRIT_ALARM | - HWMON_T_CRIT_HYST, + HWMON_T_CRIT_HYST | HWMON_T_FAULT, HWMON_T_INPUT | HWMON_T_LABEL | HWMON_T_MIN | HWMON_T_MIN_ALARM | HWMON_T_MAX | HWMON_T_MAX_ALARM | HWMON_T_MAX_HYST | HWMON_T_CRIT | HWMON_T_CRIT_ALARM | - HWMON_T_CRIT_HYST, + HWMON_T_CRIT_HYST | HWMON_T_FAULT, HWMON_T_INPUT | HWMON_T_LABEL | HWMON_T_MIN | HWMON_T_MIN_ALARM | HWMON_T_MAX | HWMON_T_MAX_ALARM | HWMON_T_MAX_HYST | HWMON_T_CRIT | HWMON_T_CRIT_ALARM | - HWMON_T_CRIT_HYST), + HWMON_T_CRIT_HYST | HWMON_T_FAULT), HWMON_CHANNEL_INFO(chip, HWMON_C_UPDATE_INTERVAL), NULL @@ -369,7 +369,8 @@ static int mcp9982_read(struct device *dev, enum hwmon_sensor_types type, u32 at /* * In Standby State the conversion cycle must be initated manually in - * order to read fresh temperature values and the status of the alarms. + * order to read fresh temperature values, the status of the alarms and + * fault information. */ if (!priv->run_state) { switch (type) { @@ -379,6 +380,7 @@ static int mcp9982_read(struct device *dev, enum hwmon_sensor_types type, u32 at case hwmon_temp_max_alarm: case hwmon_temp_min_alarm: case hwmon_temp_crit_alarm: + case hwmon_temp_fault: ret = regmap_write(priv->regmap, MCP9982_ONE_SHOT_ADDR, 1); if (ret) return ret; @@ -402,6 +404,11 @@ static int mcp9982_read(struct device *dev, enum hwmon_sensor_types type, u32 at } switch (type) { + /* + * Because the ALERT/THERM pin is set in Therm(Comparator) mode, + * the external diode fault status, high limit status and low + * limit status registers do not clear the bits after reading. + */ case hwmon_temp: switch (attr) { case hwmon_temp_input: @@ -510,6 +517,13 @@ static int mcp9982_read(struct device *dev, enum hwmon_sensor_types type, u32 at *val -= hyst * 1000; + return 0; + case hwmon_temp_fault: + *val = regmap_test_bits(priv->regmap, MCP9982_EXT_FAULT_STATUS_ADDR, + BIT(channel)); + if (*val < 0) + return *val; + return 0; default: return -EINVAL; @@ -681,6 +695,7 @@ static umode_t mcp9982_is_visible(const void *_data, enum hwmon_sensor_types typ case hwmon_temp_max_alarm: case hwmon_temp_max_hyst: case hwmon_temp_crit_alarm: + case hwmon_temp_fault: return 0444; case hwmon_temp_min: case hwmon_temp_max: From 66b4973f9a983911f79a73b211b6e1a6e9590f5b Mon Sep 17 00:00:00 2001 From: Brian Chiang Date: Tue, 28 Apr 2026 12:19:28 +0000 Subject: [PATCH 06/81] dt-bindings: trivial: Add LX1308 support Add device tree bindings for the Luxshare LX1308, a high-efficiency 12V 860W DC/DC power module with PMBus interface. Signed-off-by: Brian Chiang Acked-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20260428-add-support-lx1308-v2-1-90f115954143@inventec.com Signed-off-by: Guenter Roeck --- Documentation/devicetree/bindings/trivial-devices.yaml | 2 ++ Documentation/devicetree/bindings/vendor-prefixes.yaml | 2 ++ 2 files changed, 4 insertions(+) diff --git a/Documentation/devicetree/bindings/trivial-devices.yaml b/Documentation/devicetree/bindings/trivial-devices.yaml index 44ee92f1a09f..6b461b4096d3 100644 --- a/Documentation/devicetree/bindings/trivial-devices.yaml +++ b/Documentation/devicetree/bindings/trivial-devices.yaml @@ -192,6 +192,8 @@ properties: - jedec,spd5118 # Linear Technology LTC2488 - lineartechnology,ltc2488 + # Regulated 12V, 860W, Digital DC/DC Power Module + - luxshare,lx1308 # 5 Bit Programmable, Pulse-Width Modulator - maxim,ds1050 # 10 kOhm digital potentiometer with I2C interface diff --git a/Documentation/devicetree/bindings/vendor-prefixes.yaml b/Documentation/devicetree/bindings/vendor-prefixes.yaml index 28784d66ae7b..d31198268fee 100644 --- a/Documentation/devicetree/bindings/vendor-prefixes.yaml +++ b/Documentation/devicetree/bindings/vendor-prefixes.yaml @@ -981,6 +981,8 @@ patternProperties: description: Shenzhen Luckfox Technology Co., Ltd. "^lunzn,.*": description: Shenzhen Lunzn Technology Co., Ltd. + "^luxshare,.*": + description: Luxshare-ICT Co., Ltd. "^luxul,.*": description: Lagrand | AV "^lwn,.*": From d71d17d347deeb674115bf2f883a26c1920e7af6 Mon Sep 17 00:00:00 2001 From: Brian Chiang Date: Tue, 28 Apr 2026 12:19:29 +0000 Subject: [PATCH 07/81] hwmon: (pmbus/lx1308) Add support for LX1308 Add support for the Luxshare LX1308, a high-efficiency 12V 860W DC/DC power module. The module operates from 40-60V input voltage. Signed-off-by: Brian Chiang Link: https://lore.kernel.org/r/20260428-add-support-lx1308-v2-2-90f115954143@inventec.com Signed-off-by: Guenter Roeck --- Documentation/hwmon/index.rst | 1 + Documentation/hwmon/lx1308.rst | 90 +++++++++++++++ drivers/hwmon/pmbus/Kconfig | 10 ++ drivers/hwmon/pmbus/Makefile | 1 + drivers/hwmon/pmbus/lx1308.c | 204 +++++++++++++++++++++++++++++++++ 5 files changed, 306 insertions(+) create mode 100644 Documentation/hwmon/lx1308.rst create mode 100644 drivers/hwmon/pmbus/lx1308.c diff --git a/Documentation/hwmon/index.rst b/Documentation/hwmon/index.rst index 96612a16f93a..2ae3316c4665 100644 --- a/Documentation/hwmon/index.rst +++ b/Documentation/hwmon/index.rst @@ -146,6 +146,7 @@ Hardware Monitoring Kernel Drivers ltc4261 ltc4282 ltc4286 + lx1308 macsmc-hwmon max127 max15301 diff --git a/Documentation/hwmon/lx1308.rst b/Documentation/hwmon/lx1308.rst new file mode 100644 index 000000000000..c1b72e1647c5 --- /dev/null +++ b/Documentation/hwmon/lx1308.rst @@ -0,0 +1,90 @@ +.. SPDX-License-Identifier: GPL-2.0-or-later + +Kernel driver lx1308 +==================== + +Supported chips: + + * Luxshare LX1308 + + Prefixes: 'lx1308' + + Addresses scanned: - + + Datasheet: Datasheet is not publicly available. + +Author: Brian Chiang + + +Description +----------- + +The LX1308 is a high-efficiency, non-isolated, regulated 12V, 860W, +digital DC/DC power module. The module operates from a 40V to 60V DC +primary bus and provides a 12V regulated output voltage. It can deliver +up to 860W continuous and 1300W in transient. + +The module has slow OCP and fast OCP. If the module output current is higher +than slow OCP set point and the lasting time is also longer than the delay, +the module will shut down and retry 3 time, if the fault still exists then +module enter latch mode. + +If the module output current is higher than fast OCP set point then it shut +down and enter latch mode. + +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. + + +Sysfs entries +------------- + +======================= ====================================================== +curr1_alarm Input current alarm +curr1_input Input current (IIN) +curr1_label "iin" +curr2_crit Output over current fault threshold (slow OCP, 60ms delay) +curr2_crit_alarm Output over current fault alarm +curr2_input Output current (IOUT) +curr2_label "iout1" +curr2_max Output over current warning threshold (slow OCP, 60ms delay) +curr2_max_alarm Output over current warning alarm +in1_crit Input over voltage fault threshold +in1_crit_alarm Input over voltage fault alarm +in1_input Input voltage (VIN) +in1_label "vin" +in1_lcrit Input under voltage fault threshold +in1_lcrit_alarm Input under voltage fault alarm +in1_max Input over voltage warning threshold +in1_max_alarm Input over voltage warning alarm +in1_min Input under voltage warning threshold +in1_min_alarm Input under voltage warning alarm +in2_crit Output over voltage fault threshold +in2_crit_alarm Output over voltage fault alarm +in2_input Output voltage (VOUT) +in2_label "vout1" +in2_lcrit Output under voltage fault threshold +in2_lcrit_alarm Output under voltage fault alarm +in2_max Output over voltage warning threshold +in2_max_alarm Output over voltage warning alarm +in2_min Output under voltage warning threshold +in2_min_alarm Output under voltage warning alarm +power1_alarm Input power alarm +power1_input Input power (PIN) +power1_label "pin" +power2_input Output power (POUT) +power2_label "pout1" +temp1_crit Over temperature fault threshold +temp1_crit_alarm Over temperature fault alarm +temp1_input Module hot spot temperature +temp1_max Over temperature warning threshold +temp1_max_alarm Over temperature warning alarm +======================= ====================================================== diff --git a/drivers/hwmon/pmbus/Kconfig b/drivers/hwmon/pmbus/Kconfig index 8a2c3ea50c8d..1f99a333abc7 100644 --- a/drivers/hwmon/pmbus/Kconfig +++ b/drivers/hwmon/pmbus/Kconfig @@ -311,6 +311,16 @@ config SENSORS_LTC4286 If you say yes here you get hardware monitoring support for Analog Devices LTC4286. +config SENSORS_LX1308 + tristate "Luxshare LX1308 DC/DC Power Module" + help + If you say yes here you get hardware monitoring support for + Luxshare LX1308, a high-efficiency 12V 860W DC/DC power module + with PMBus interface. + + This driver can also be built as a module. If so, the module will + be called lx1308. + config SENSORS_MAX15301 tristate "Maxim MAX15301" help diff --git a/drivers/hwmon/pmbus/Makefile b/drivers/hwmon/pmbus/Makefile index 6022614722bd..039848ecee7f 100644 --- a/drivers/hwmon/pmbus/Makefile +++ b/drivers/hwmon/pmbus/Makefile @@ -30,6 +30,7 @@ obj-$(CONFIG_SENSORS_LT7182S) += lt7182s.o obj-$(CONFIG_SENSORS_LTC2978) += ltc2978.o obj-$(CONFIG_SENSORS_LTC3815) += ltc3815.o obj-$(CONFIG_SENSORS_LTC4286) += ltc4286.o +obj-$(CONFIG_SENSORS_LX1308) += lx1308.o obj-$(CONFIG_SENSORS_MAX15301) += max15301.o obj-$(CONFIG_SENSORS_MAX16064) += max16064.o obj-$(CONFIG_SENSORS_MAX16601) += max16601.o diff --git a/drivers/hwmon/pmbus/lx1308.c b/drivers/hwmon/pmbus/lx1308.c new file mode 100644 index 000000000000..0c453393796b --- /dev/null +++ b/drivers/hwmon/pmbus/lx1308.c @@ -0,0 +1,204 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include +#include +#include +#include "pmbus.h" + +#define LX1308_MFR_IOUT_OCP3_FAULT 0xBE +#define LX1308_MFR_IOUT_OCP3_WARN 0xBF + +/* + * Decode a Linear11-encoded word to an integer value. + * Linear11 format: bits[15:11] = signed 5-bit exponent, + * bits[10:0] = signed 11-bit mantissa. Result = mant * 2^exp. + */ +static inline int linear11_to_int(u16 word) +{ + s16 exp = ((s16)word) >> 11; + s16 mant = ((s16)((word & 0x7ff) << 5)) >> 5; + + return (exp >= 0) ? (int)((u32)mant << exp) : (mant >> -exp); +} + +static int lx1308_read_word_data(struct i2c_client *client, int page, + int phase, int reg) +{ + int ret; + + if (page > 0) + return -ENXIO; + + switch (reg) { + /* + * The LX1308 OCP3 registers (slow OCP, 60ms delay) use a + * manufacturer-specific U8.0 format. Read the byte value N and present + * it as a Linear11 word with exponent 0. + */ + case PMBUS_IOUT_OC_FAULT_LIMIT: + ret = i2c_smbus_read_byte_data(client, LX1308_MFR_IOUT_OCP3_FAULT); + if (ret < 0) + break; + ret &= 0x7FF; + break; + + case PMBUS_IOUT_OC_WARN_LIMIT: + ret = i2c_smbus_read_byte_data(client, LX1308_MFR_IOUT_OCP3_WARN); + if (ret < 0) + break; + ret &= 0x7FF; + break; + + /* + * The following registers are not implemented by the LX1308. Return + * -ENXIO to suppress the corresponding sysfs attributes. + */ + case PMBUS_IIN_OC_WARN_LIMIT: + case PMBUS_IIN_OC_FAULT_LIMIT: + case PMBUS_IOUT_UC_FAULT_LIMIT: + case PMBUS_PIN_OP_WARN_LIMIT: + case PMBUS_POUT_OP_WARN_LIMIT: + case PMBUS_UT_WARN_LIMIT: + case PMBUS_UT_FAULT_LIMIT: + case PMBUS_MFR_IIN_MAX: + case PMBUS_MFR_IOUT_MAX: + case PMBUS_MFR_VIN_MIN: + case PMBUS_MFR_VIN_MAX: + case PMBUS_MFR_VOUT_MIN: + case PMBUS_MFR_VOUT_MAX: + case PMBUS_MFR_PIN_MAX: + case PMBUS_MFR_POUT_MAX: + case PMBUS_MFR_MAX_TEMP_1: + ret = -ENXIO; + break; + + default: + ret = -ENODATA; + break; + } + + return ret; +} + +static int lx1308_write_word_data(struct i2c_client *client, int page, + int reg, u16 word) +{ + int ret; + + if (page > 0) + return -ENXIO; + + switch (reg) { + case PMBUS_IOUT_OC_FAULT_LIMIT: + /* + * Decode Linear11 word from pmbus_core back to a plain integer + * and write as the U8.0 byte the device expects. + */ + ret = i2c_smbus_write_byte_data(client, LX1308_MFR_IOUT_OCP3_FAULT, + clamp_val(linear11_to_int(word), 0, 255)); + break; + + case PMBUS_IOUT_OC_WARN_LIMIT: + ret = i2c_smbus_write_byte_data(client, LX1308_MFR_IOUT_OCP3_WARN, + clamp_val(linear11_to_int(word), 0, 255)); + break; + + default: + ret = -ENODATA; + break; + } + + return ret; +} + +static struct pmbus_driver_info lx1308_info = { + .pages = 1, + .format[PSC_VOLTAGE_IN] = linear, + .format[PSC_VOLTAGE_OUT] = linear, + .format[PSC_CURRENT_IN] = linear, + .format[PSC_CURRENT_OUT] = linear, + .format[PSC_POWER] = linear, + .format[PSC_TEMPERATURE] = linear, + + .func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT + | PMBUS_HAVE_IIN | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT + | PMBUS_HAVE_PIN | PMBUS_HAVE_POUT + | PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP + | PMBUS_HAVE_STATUS_INPUT, + + .read_word_data = lx1308_read_word_data, + .write_word_data = lx1308_write_word_data, +}; + +static const struct of_device_id lx1308_of_match[] = { + { .compatible = "luxshare,lx1308" }, + { } +}; + +MODULE_DEVICE_TABLE(of, lx1308_of_match); + +static const struct i2c_device_id lx1308_id[] = { + { "lx1308" }, + { } +}; + +MODULE_DEVICE_TABLE(i2c, lx1308_id); + +static int lx1308_probe(struct i2c_client *client) +{ + u8 buf[I2C_SMBUS_BLOCK_MAX + 1]; + const struct i2c_device_id *mid; + int ret; + + ret = i2c_smbus_read_block_data(client, PMBUS_MFR_ID, buf); + if (ret < 0) + return dev_err_probe(&client->dev, ret, + "Failed to read manufacturer id\n"); + buf[ret] = '\0'; + + if (ret != 12 || strncmp(buf, "LUXSHARE", 8)) + return dev_err_probe(&client->dev, -ENODEV, + "Unsupported Manufacturer ID '%s'\n", buf); + + ret = i2c_smbus_read_block_data(client, PMBUS_MFR_MODEL, buf); + if (ret < 0) + return dev_err_probe(&client->dev, ret, + "Failed to read Manufacturer Model\n"); + buf[ret] = '\0'; + + for (mid = lx1308_id; mid->name[0]; mid++) { + if (!strncasecmp(mid->name, buf, strlen(mid->name))) + break; + } + if (!mid->name[0]) + return dev_err_probe(&client->dev, -ENODEV, + "Unsupported Manufacturer Model '%s'\n", buf); + + ret = i2c_smbus_read_block_data(client, PMBUS_MFR_REVISION, buf); + if (ret < 0) + return dev_err_probe(&client->dev, ret, + "Failed to read Manufacturer Revision\n"); + buf[ret] = '\0'; + + if (ret != 12 || buf[0] != 'V') + return dev_err_probe(&client->dev, -ENODEV, + "Unsupported Manufacturer Revision '%s'\n", buf); + return pmbus_do_probe(client, &lx1308_info); +} + +static struct i2c_driver lx1308_driver = { + .driver = { + .name = "lx1308", + .of_match_table = lx1308_of_match, + }, + .probe = lx1308_probe, + .id_table = lx1308_id, +}; + +module_i2c_driver(lx1308_driver); + +MODULE_AUTHOR("Brian Chiang "); +MODULE_DESCRIPTION("PMBus driver for Luxshare LX1308"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("PMBUS"); From ee672fef23722f71d7bfa7d5a7f5822b1ca6cfa3 Mon Sep 17 00:00:00 2001 From: Florin Leotescu Date: Wed, 29 Apr 2026 09:59:53 +0300 Subject: [PATCH 08/81] hwmon: (emc2305) Fix fan channel index handling The fan channel index is used to access per-channel data structures. Validate the index against the number of available channels before use to prevent out-of-bounds access if an invalid value is provided. The thermal registration path currently uses a sequential child index, which may not match the validated channel from DT. Use the DT "reg" property when registering cooling devices to ensure consistent channel handling Signed-off-by: Florin Leotescu Link: https://lore.kernel.org/r/20260429065955.2113012-2-florin.leotescu@oss.nxp.com Signed-off-by: Guenter Roeck --- drivers/hwmon/emc2305.c | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/drivers/hwmon/emc2305.c b/drivers/hwmon/emc2305.c index 64b213e1451e..f71a0e265924 100644 --- a/drivers/hwmon/emc2305.c +++ b/drivers/hwmon/emc2305.c @@ -548,6 +548,12 @@ static int emc2305_of_parse_pwm_child(struct device *dev, return ret; } + if (ch >= data->pwm_num) { + dev_err(dev, "invalid reg %u for node %pOF (valid range 0-%u)\n", ch, child, + data->pwm_num - 1); + return -EINVAL; + } + ret = of_parse_phandle_with_args(child, "pwms", "#pwm-cells", 0, &args); if (ret) @@ -612,6 +618,7 @@ static int emc2305_probe(struct i2c_client *client) int ret; int i; int pwm_childs; + u32 ch; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA)) return -ENODEV; @@ -680,12 +687,18 @@ static int emc2305_probe(struct i2c_client *client) if (IS_REACHABLE(CONFIG_THERMAL)) { /* Parse and check for the available PWM child nodes */ if (pwm_childs > 0) { - i = 0; for_each_child_of_node_scoped(dev->of_node, child) { - ret = emc2305_set_single_tz(dev, child, i); + ret = of_property_read_u32(child, "reg", &ch); + if (ret || ch >= data->pwm_num) + continue; + + /* + * emc2305_set_single_tz() uses 0 for the common cooling + * device and 1..pwm_num for individual fan channels. + */ + ret = emc2305_set_single_tz(dev, child, ch + 1); if (ret != 0) return ret; - i++; } } else { ret = emc2305_set_tz(dev); From d37276a86e40a6f6479745a8fb4e92d0dd77c82a Mon Sep 17 00:00:00 2001 From: Florin Leotescu Date: Wed, 29 Apr 2026 09:59:54 +0300 Subject: [PATCH 09/81] dt-bindings: hwmon: emc2305: Add fan-shutdown-percent property The EMC2305 fan controller supports multiple independent PWM fan outputs. Some systems require fans to enter a defined safe state during system shutdown or reboot handoff, until firmware or the next boot stage reconfigures the controller. Add an optional "fan-shutdown-percent" property to fan child nodes allowing the PWM duty cycle applied during shutdown to be configured per fan output. Signed-off-by: Florin Leotescu Acked-by: Conor Dooley Link: https://lore.kernel.org/r/20260429065955.2113012-3-florin.leotescu@oss.nxp.com Signed-off-by: Guenter Roeck --- .../devicetree/bindings/hwmon/microchip,emc2305.yaml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Documentation/devicetree/bindings/hwmon/microchip,emc2305.yaml b/Documentation/devicetree/bindings/hwmon/microchip,emc2305.yaml index d3f06ebc19fa..8c2548539d7f 100644 --- a/Documentation/devicetree/bindings/hwmon/microchip,emc2305.yaml +++ b/Documentation/devicetree/bindings/hwmon/microchip,emc2305.yaml @@ -54,6 +54,12 @@ patternProperties: The fan number used to determine the associated PWM channel. maxItems: 1 + fan-shutdown-percent: + description: + PWM duty cycle in percent applied to the fan during shutdown. + minimum: 0 + maximum: 100 + required: - reg @@ -80,12 +86,14 @@ examples: fan@0 { reg = <0x0>; pwms = <&fan_controller 26000 PWM_POLARITY_INVERTED 1>; + fan-shutdown-percent = <100>; #cooling-cells = <2>; }; fan@1 { reg = <0x1>; pwms = <&fan_controller 26000 0 1>; + fan-shutdown-percent = <50>; #cooling-cells = <2>; }; From 07668f5ca30620fb1edfd730181bfd8120aa6c55 Mon Sep 17 00:00:00 2001 From: Florin Leotescu Date: Wed, 29 Apr 2026 09:59:55 +0300 Subject: [PATCH 10/81] hwmon: emc2305: Support configurable fan PWM at shutdown Some systems require fans to enter in a defined safe state during system shutdown or reboot handoff. Add support for the optional Device Tree property "fan-shutdown-percent" to configure the shutdown PWM duty cycle per fan output. If the property is present for a fan channel, the driver converts the configured percentage value to the corresponding PWM duty cycle and applies it during driver shutdown. If the property is not present, the fan state remains unchanged. Signed-off-by: Florin Leotescu Link: https://lore.kernel.org/r/20260429065955.2113012-4-florin.leotescu@oss.nxp.com Signed-off-by: Guenter Roeck --- drivers/hwmon/emc2305.c | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/drivers/hwmon/emc2305.c b/drivers/hwmon/emc2305.c index f71a0e265924..c5a0b2146478 100644 --- a/drivers/hwmon/emc2305.c +++ b/drivers/hwmon/emc2305.c @@ -32,6 +32,7 @@ #define EMC2305_REG_DRIVE_PWM_OUT 0x2b #define EMC2305_OPEN_DRAIN 0x0 #define EMC2305_PUSH_PULL 0x1 +#define EMC2305_PWM_SHUTDOWN_UNSET -1 #define EMC2305_PWM_DUTY2STATE(duty, max_state, pwm_max) \ DIV_ROUND_CLOSEST((duty) * (max_state), (pwm_max)) @@ -104,6 +105,7 @@ struct emc2305_cdev_data { * @pwm_output_mask: PWM output mask * @pwm_polarity_mask: PWM polarity mask * @pwm_separate: separate PWM settings for every channel + * @pwm_shutdown: Set shutdown PWM. * @pwm_min: array of minimum PWM per channel * @pwm_freq: array of PWM frequency per channel * @cdev_data: array of cooling devices data @@ -116,6 +118,7 @@ struct emc2305_data { u8 pwm_output_mask; u8 pwm_polarity_mask; bool pwm_separate; + s16 pwm_shutdown[EMC2305_PWM_MAX]; u8 pwm_min[EMC2305_PWM_MAX]; u16 pwm_freq[EMC2305_PWM_MAX]; struct emc2305_cdev_data cdev_data[EMC2305_PWM_MAX]; @@ -539,6 +542,7 @@ static int emc2305_of_parse_pwm_child(struct device *dev, struct device_node *child, struct emc2305_data *data) { u32 ch; + u32 pwm_shutdown_percent; int ret; struct of_phandle_args args; @@ -585,6 +589,16 @@ static int emc2305_of_parse_pwm_child(struct device *dev, } of_node_put(args.np); + + ret = of_property_read_u32(child, "fan-shutdown-percent", + &pwm_shutdown_percent); + + if (!ret) { + pwm_shutdown_percent = clamp(pwm_shutdown_percent, 0, 100); + data->pwm_shutdown[ch] = + DIV_ROUND_CLOSEST(pwm_shutdown_percent * EMC2305_FAN_MAX, 100); + } + return 0; } @@ -638,6 +652,9 @@ static int emc2305_probe(struct i2c_client *client) if (ret) return ret; + for (i = 0; i < EMC2305_PWM_MAX; i++) + data->pwm_shutdown[i] = EMC2305_PWM_SHUTDOWN_UNSET; + pwm_childs = emc2305_probe_childs_from_dt(dev); pdata = dev_get_platdata(&client->dev); @@ -727,6 +744,23 @@ static int emc2305_probe(struct i2c_client *client) return 0; } +static void emc2305_shutdown(struct i2c_client *client) +{ + int i; + int ret; + struct emc2305_data *data = i2c_get_clientdata(client); + + for (i = 0; i < data->pwm_num; i++) { + if (data->pwm_shutdown[i] != EMC2305_PWM_SHUTDOWN_UNSET) { + ret = i2c_smbus_write_byte_data(client, EMC2305_REG_FAN_DRIVE(i), + data->pwm_shutdown[i]); + if (ret < 0) + dev_warn(&client->dev, + "Failed to set shutdown PWM for ch %d\n", i); + } + } +} + static const struct of_device_id of_emc2305_match_table[] = { { .compatible = "microchip,emc2305", }, {}, @@ -739,6 +773,7 @@ static struct i2c_driver emc2305_driver = { .of_match_table = of_emc2305_match_table, }, .probe = emc2305_probe, + .shutdown = emc2305_shutdown, .id_table = emc2305_ids, }; From a2b0986398e6dd952ab413f6dcd271ddf86ea9b8 Mon Sep 17 00:00:00 2001 From: Tabrez Ahmed Date: Sat, 2 May 2026 07:38:43 +0530 Subject: [PATCH 11/81] hwmon: (ads7871) Convert to hwmon_device_register_with_info Convert the ads7871 driver from the legacy hwmon_device_register() to the modern hwmon_device_register_with_info() API. This migration simplifies the driver by using the structured hwmon_channel_info approach and prepares the codebase for the transition to a shared DMA-safe buffer. While at it, fix checkpatch violations. Signed-off-by: Tabrez Ahmed Link: https://lore.kernel.org/r/20260502020844.110038-3-tabreztalks@gmail.com Signed-off-by: Guenter Roeck --- drivers/hwmon/ads7871.c | 75 ++++++++++++++++++++++------------------- 1 file changed, 40 insertions(+), 35 deletions(-) diff --git a/drivers/hwmon/ads7871.c b/drivers/hwmon/ads7871.c index 9ee3ce01f130..805b3657362d 100644 --- a/drivers/hwmon/ads7871.c +++ b/drivers/hwmon/ads7871.c @@ -56,7 +56,6 @@ #include #include #include -#include #include #include @@ -66,6 +65,16 @@ struct ads7871_data { struct spi_device *spi; }; +static umode_t ads7871_is_visible(const void *data, + enum hwmon_sensor_types type, + u32 attr, int channel) +{ + if (type == hwmon_in && attr == hwmon_in_input) + return 0444; + + return 0; +} + static int ads7871_read_reg8(struct spi_device *spi, int reg) { int ret; @@ -89,19 +98,20 @@ static int ads7871_read_reg16(struct spi_device *spi, int reg) static int ads7871_write_reg8(struct spi_device *spi, int reg, u8 val) { u8 tmp[2] = {reg, val}; + return spi_write(spi, tmp, sizeof(tmp)); } -static ssize_t voltage_show(struct device *dev, struct device_attribute *da, - char *buf) +static int ads7871_read(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long *val) { struct ads7871_data *pdata = dev_get_drvdata(dev); struct spi_device *spi = pdata->spi; - struct sensor_device_attribute *attr = to_sensor_dev_attr(da); - int ret, val, i = 0; - uint8_t channel, mux_cnv; + int ret, raw_val, i = 0; + u8 mux_cnv; - channel = attr->index; + if (type != hwmon_in || attr != hwmon_in_input) + return -EOPNOTSUPP; /* * TODO: add support for conversions * other than single ended with a gain of 1 @@ -131,39 +141,34 @@ static ssize_t voltage_show(struct device *dev, struct device_attribute *da, } if (mux_cnv == 0) { - val = ads7871_read_reg16(spi, REG_LS_BYTE); - if (val < 0) - return val; + raw_val = ads7871_read_reg16(spi, REG_LS_BYTE); + if (raw_val < 0) + return raw_val; + /*result in volts*10000 = (val/8192)*2.5*10000*/ - val = ((val >> 2) * 25000) / 8192; - return sysfs_emit(buf, "%d\n", val); + *val = ((raw_val >> 2) * 25000) / 8192; + return 0; } return -ETIMEDOUT; } -static SENSOR_DEVICE_ATTR_RO(in0_input, voltage, 0); -static SENSOR_DEVICE_ATTR_RO(in1_input, voltage, 1); -static SENSOR_DEVICE_ATTR_RO(in2_input, voltage, 2); -static SENSOR_DEVICE_ATTR_RO(in3_input, voltage, 3); -static SENSOR_DEVICE_ATTR_RO(in4_input, voltage, 4); -static SENSOR_DEVICE_ATTR_RO(in5_input, voltage, 5); -static SENSOR_DEVICE_ATTR_RO(in6_input, voltage, 6); -static SENSOR_DEVICE_ATTR_RO(in7_input, voltage, 7); - -static struct attribute *ads7871_attrs[] = { - &sensor_dev_attr_in0_input.dev_attr.attr, - &sensor_dev_attr_in1_input.dev_attr.attr, - &sensor_dev_attr_in2_input.dev_attr.attr, - &sensor_dev_attr_in3_input.dev_attr.attr, - &sensor_dev_attr_in4_input.dev_attr.attr, - &sensor_dev_attr_in5_input.dev_attr.attr, - &sensor_dev_attr_in6_input.dev_attr.attr, - &sensor_dev_attr_in7_input.dev_attr.attr, +static const struct hwmon_channel_info * const ads7871_info[] = { + HWMON_CHANNEL_INFO(in, + HWMON_I_INPUT, HWMON_I_INPUT, HWMON_I_INPUT, HWMON_I_INPUT, + HWMON_I_INPUT, HWMON_I_INPUT, HWMON_I_INPUT, HWMON_I_INPUT), NULL }; -ATTRIBUTE_GROUPS(ads7871); +static const struct hwmon_ops ads7871_hwmon_ops = { + .is_visible = ads7871_is_visible, + .read = ads7871_read, +}; + +static const struct hwmon_chip_info ads7871_chip_info = { + .ops = &ads7871_hwmon_ops, + .info = ads7871_info, +}; static int ads7871_probe(struct spi_device *spi) { @@ -198,10 +203,10 @@ static int ads7871_probe(struct spi_device *spi) return -ENOMEM; pdata->spi = spi; - - hwmon_dev = devm_hwmon_device_register_with_groups(dev, spi->modalias, - pdata, - ads7871_groups); + hwmon_dev = devm_hwmon_device_register_with_info(dev, spi->modalias, + pdata, + &ads7871_chip_info, + NULL); return PTR_ERR_OR_ZERO(hwmon_dev); } From b46e1a0bff3343ad8a50a5d9cdb56a3847ba69ef Mon Sep 17 00:00:00 2001 From: Tabrez Ahmed Date: Sat, 2 May 2026 07:38:44 +0530 Subject: [PATCH 12/81] hwmon: (ads7871) Use DMA-safe buffer for SPI writes The driver currently passes a stack-allocated buffer to spi_write(), which is incompatible with DMA on systems with CONFIG_VMAP_STACK enabled. Move the transfer buffer into the driver's private data structure to ensure it is DMA-safe. Since this shared buffer now requires serialization, this change depends on the previous commit which migrated the driver to the hwmon 'with_info' API. While moving the logic, also: - Corrected the sign extension for 14-bit data by casting to s16. - Scaled the output to millivolts (2500mV full scale ) to comply with the hwmon ABI. Signed-off-by: Tabrez Ahmed Link: https://lore.kernel.org/r/20260502020844.110038-4-tabreztalks@gmail.com Signed-off-by: Guenter Roeck --- drivers/hwmon/ads7871.c | 35 +++++++++++++++++++++-------------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/drivers/hwmon/ads7871.c b/drivers/hwmon/ads7871.c index 805b3657362d..d1cbff0e8572 100644 --- a/drivers/hwmon/ads7871.c +++ b/drivers/hwmon/ads7871.c @@ -63,6 +63,7 @@ struct ads7871_data { struct spi_device *spi; + u8 tx_buf[2] ____cacheline_aligned; }; static umode_t ads7871_is_visible(const void *data, @@ -95,11 +96,12 @@ static int ads7871_read_reg16(struct spi_device *spi, int reg) return le16_to_cpu((__force __le16)ret); } -static int ads7871_write_reg8(struct spi_device *spi, int reg, u8 val) +static int ads7871_write_reg8(struct ads7871_data *pdata, int reg, u8 val) { - u8 tmp[2] = {reg, val}; + pdata->tx_buf[0] = reg; + pdata->tx_buf[1] = val; - return spi_write(spi, tmp, sizeof(tmp)); + return spi_write(pdata->spi, pdata->tx_buf, 2); } static int ads7871_read(struct device *dev, enum hwmon_sensor_types type, @@ -118,7 +120,7 @@ static int ads7871_read(struct device *dev, enum hwmon_sensor_types type, */ /*MUX_M3_BM forces single ended*/ /*This is also where the gain of the PGA would be set*/ - ret = ads7871_write_reg8(spi, REG_GAIN_MUX, + ret = ads7871_write_reg8(pdata, REG_GAIN_MUX, (MUX_CNV_BM | MUX_M3_BM | channel)); if (ret < 0) return ret; @@ -126,6 +128,7 @@ static int ads7871_read(struct device *dev, enum hwmon_sensor_types type, ret = ads7871_read_reg8(spi, REG_GAIN_MUX); if (ret < 0) return ret; + mux_cnv = ((ret & MUX_CNV_BM) >> MUX_CNV_BV); /* * on 400MHz arm9 platform the conversion @@ -145,8 +148,11 @@ static int ads7871_read(struct device *dev, enum hwmon_sensor_types type, if (raw_val < 0) return raw_val; - /*result in volts*10000 = (val/8192)*2.5*10000*/ - *val = ((raw_val >> 2) * 25000) / 8192; + /* + * Use (s16) to ensure the sign bit is preserved during the shift. + * Report millivolts (2.5V = 2500mV). + */ + *val = ((s16)raw_val >> 2) * 2500 / 8192; return 0; } @@ -183,11 +189,17 @@ static int ads7871_probe(struct spi_device *spi) spi->bits_per_word = 8; spi_setup(spi); - ads7871_write_reg8(spi, REG_SER_CONTROL, 0); - ads7871_write_reg8(spi, REG_AD_CONTROL, 0); + pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return -ENOMEM; + + pdata->spi = spi; + + ads7871_write_reg8(pdata, REG_SER_CONTROL, 0); + ads7871_write_reg8(pdata, REG_AD_CONTROL, 0); val = (OSC_OSCR_BM | OSC_OSCE_BM | OSC_REFE_BM | OSC_BUFE_BM); - ads7871_write_reg8(spi, REG_OSC_CONTROL, val); + ads7871_write_reg8(pdata, REG_OSC_CONTROL, val); ret = ads7871_read_reg8(spi, REG_OSC_CONTROL); dev_dbg(dev, "REG_OSC_CONTROL write:%x, read:%x\n", val, ret); @@ -198,11 +210,6 @@ static int ads7871_probe(struct spi_device *spi) if (val != ret) return -ENODEV; - pdata = devm_kzalloc(dev, sizeof(struct ads7871_data), GFP_KERNEL); - if (!pdata) - return -ENOMEM; - - pdata->spi = spi; hwmon_dev = devm_hwmon_device_register_with_info(dev, spi->modalias, pdata, &ads7871_chip_info, From fe97565f8977be862073dc6a4faf578555108ed6 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Tue, 5 May 2026 12:29:22 +0200 Subject: [PATCH 13/81] hwmon: Move MODULE_DEVICE_TABLE next to the table itself MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit By convention MODULE_DEVICE_TABLE() immediately follows the ID table it exports, because this is easier to read and verify. It also makes more sense since #ifdef for ACPI or OF could hide both of them. Most of the privers already have this correctly placed, so adjust the missing ones. No functional impact. Signed-off-by: Krzysztof Kozlowski Acked-by: Thomas Weißschuh # cros_ec_hwmon.c Link: https://lore.kernel.org/r/20260505102923.189289-2-krzysztof.kozlowski@oss.qualcomm.com Signed-off-by: Guenter Roeck --- drivers/hwmon/applesmc.c | 2 +- drivers/hwmon/cros_ec_hwmon.c | 2 +- drivers/hwmon/intel-m10-bmc-hwmon.c | 2 +- drivers/hwmon/nsa320-hwmon.c | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/hwmon/applesmc.c b/drivers/hwmon/applesmc.c index 24f3e86d0ebf..90a14a7f2c4c 100644 --- a/drivers/hwmon/applesmc.c +++ b/drivers/hwmon/applesmc.c @@ -1305,6 +1305,7 @@ static const struct dmi_system_id applesmc_whitelist[] __initconst = { }, { .ident = NULL } }; +MODULE_DEVICE_TABLE(dmi, applesmc_whitelist); static int __init applesmc_init(void) { @@ -1416,4 +1417,3 @@ module_exit(applesmc_exit); MODULE_AUTHOR("Nicolas Boichat"); MODULE_DESCRIPTION("Apple SMC"); MODULE_LICENSE("GPL v2"); -MODULE_DEVICE_TABLE(dmi, applesmc_whitelist); diff --git a/drivers/hwmon/cros_ec_hwmon.c b/drivers/hwmon/cros_ec_hwmon.c index 6cf5ab0f4b73..7c308b0a4b9e 100644 --- a/drivers/hwmon/cros_ec_hwmon.c +++ b/drivers/hwmon/cros_ec_hwmon.c @@ -657,6 +657,7 @@ static const struct platform_device_id cros_ec_hwmon_id[] = { { DRV_NAME, 0 }, {} }; +MODULE_DEVICE_TABLE(platform, cros_ec_hwmon_id); static struct platform_driver cros_ec_hwmon_driver = { .driver.name = DRV_NAME, @@ -667,7 +668,6 @@ static struct platform_driver cros_ec_hwmon_driver = { }; module_platform_driver(cros_ec_hwmon_driver); -MODULE_DEVICE_TABLE(platform, cros_ec_hwmon_id); MODULE_DESCRIPTION("ChromeOS EC Hardware Monitoring Driver"); MODULE_AUTHOR("Thomas Weißschuh "); MODULE_AUTHOR("Adam Baker "); MODULE_DESCRIPTION("NSA320 Hardware Monitoring"); From 7c13c7517743e8eb7096d2f1575369ae7b062835 Mon Sep 17 00:00:00 2001 From: Manish Baing Date: Fri, 1 May 2026 10:21:16 +0000 Subject: [PATCH 14/81] dt-bindings: hwmon: zyxel,nsa320-mcu: convert to DT schema Convert the ZyXEL NSA320 MCU bindings from text format to YAML schema. Signed-off-by: Manish Baing Acked-by: Adam Baker Reviewed-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20260501102116.8275-1-manishbaing2789@gmail.com Signed-off-by: Guenter Roeck --- .../devicetree/bindings/hwmon/nsa320-mcu.txt | 20 ------- .../bindings/hwmon/zyxel,nsa320-mcu.yaml | 54 +++++++++++++++++++ 2 files changed, 54 insertions(+), 20 deletions(-) delete mode 100644 Documentation/devicetree/bindings/hwmon/nsa320-mcu.txt create mode 100644 Documentation/devicetree/bindings/hwmon/zyxel,nsa320-mcu.yaml diff --git a/Documentation/devicetree/bindings/hwmon/nsa320-mcu.txt b/Documentation/devicetree/bindings/hwmon/nsa320-mcu.txt deleted file mode 100644 index 0863e067c85b..000000000000 --- a/Documentation/devicetree/bindings/hwmon/nsa320-mcu.txt +++ /dev/null @@ -1,20 +0,0 @@ -Bindings for the fan / temperature monitor microcontroller used on -the Zyxel NSA 320 and several subsequent models. - -Required properties: -- compatible : "zyxel,nsa320-mcu" -- data-gpios : The GPIO pin connected to the data line on the MCU -- clk-gpios : The GPIO pin connected to the clock line on the MCU -- act-gpios : The GPIO pin connected to the active line on the MCU - -Example: - - hwmon { - compatible = "zyxel,nsa320-mcu"; - pinctrl-0 = <&pmx_mcu_data &pmx_mcu_clk &pmx_mcu_act>; - pinctrl-names = "default"; - - data-gpios = <&gpio0 14 GPIO_ACTIVE_HIGH>; - clk-gpios = <&gpio0 16 GPIO_ACTIVE_HIGH>; - act-gpios = <&gpio0 17 GPIO_ACTIVE_LOW>; - }; diff --git a/Documentation/devicetree/bindings/hwmon/zyxel,nsa320-mcu.yaml b/Documentation/devicetree/bindings/hwmon/zyxel,nsa320-mcu.yaml new file mode 100644 index 000000000000..a111f8125e09 --- /dev/null +++ b/Documentation/devicetree/bindings/hwmon/zyxel,nsa320-mcu.yaml @@ -0,0 +1,54 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/hwmon/zyxel,nsa320-mcu.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: ZyXEL NSA320 MCU + +maintainers: + - Adam Baker + - Guenter Roeck + +description: + The ZyXEL NSA320 uses a dedicated microcontroller to manage system-critical + functions like fan speed and power monitoring. It is connected to the SoC + via a GPIO-based serial protocol. + +properties: + compatible: + const: zyxel,nsa320-mcu + + data-gpios: + maxItems: 1 + description: GPIO pin connected to the data line on the MCU. + + clk-gpios: + maxItems: 1 + description: GPIO pin connected to the clock line on the MCU. + + act-gpios: + maxItems: 1 + description: GPIO pin connected to the active line on the MCU. + +required: + - compatible + - data-gpios + - clk-gpios + - act-gpios + +additionalProperties: false + +examples: + - | + #include + + hwmon { + compatible = "zyxel,nsa320-mcu"; + pinctrl-0 = <&pmx_mcu_data &pmx_mcu_clk &pmx_mcu_act>; + pinctrl-names = "default"; + + data-gpios = <&gpio0 14 GPIO_ACTIVE_HIGH>; + clk-gpios = <&gpio0 16 GPIO_ACTIVE_HIGH>; + act-gpios = <&gpio0 17 GPIO_ACTIVE_LOW>; + }; From 01dd3f682d86bcddf51e22255f2cabee66885080 Mon Sep 17 00:00:00 2001 From: Markus Stockhausen Date: Mon, 4 May 2026 17:10:19 +0200 Subject: [PATCH 15/81] dt-bindings: hwmon: lm75: Add ti,alert-polarity-active-high property The LM75 alert pin is asserted based on the value of alert polarity bit of the configuration register. The device/driver default is 0 which means alert pin is configured to be active-low. A value of 1 maps to inverted (active-high). Add an optional boolean property "ti,alert-polarity-active-high" to override the alert pin polarity. When absent, the default active-low polarity is kept. Signed-off-by: Markus Stockhausen Acked-by: Conor Dooley Link: https://lore.kernel.org/r/20260504151020.462342-2-markus.stockhausen@gmx.de [groeck: Shorten description line length] Signed-off-by: Guenter Roeck --- Documentation/devicetree/bindings/hwmon/lm75.yaml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Documentation/devicetree/bindings/hwmon/lm75.yaml b/Documentation/devicetree/bindings/hwmon/lm75.yaml index 0b9fda81e3ec..b48bf3fd721f 100644 --- a/Documentation/devicetree/bindings/hwmon/lm75.yaml +++ b/Documentation/devicetree/bindings/hwmon/lm75.yaml @@ -54,6 +54,13 @@ properties: interrupts: maxItems: 1 + ti,alert-polarity-active-high: + description: Alert pin is asserted based on the value of alert polarity + bit of configuration register. Default value is normal (0 which maps to + active-low). The other value is inverted (1 which maps to active-high). + Specify this property to set the alert polarity to active-high. + $ref: /schemas/types.yaml#/definitions/flag + required: - compatible - reg From 1be4001d9b9b0cd083ed1e3c7817ad01f29d120a Mon Sep 17 00:00:00 2001 From: Markus Stockhausen Date: Mon, 4 May 2026 17:10:20 +0200 Subject: [PATCH 16/81] hwmon: (lm75) Support active-high alert polarity LM75 devices supported by this driver support configurable active-high alert polarity. This is already documented in the devicetree description. Add support for it to the driver. Follow documentation and defensively enforce active-low if property is not set. This avoids possible inconsistencies for future devices with wrong parametrization. No API breakage as all current devices have their parameters set to active-low. Signed-off-by: Markus Stockhausen Link: https://lore.kernel.org/r/20260504151020.462342-3-markus.stockhausen@gmx.de Signed-off-by: Guenter Roeck --- drivers/hwmon/lm75.c | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/drivers/hwmon/lm75.c b/drivers/hwmon/lm75.c index a47ce32df983..845a0e09ea9f 100644 --- a/drivers/hwmon/lm75.c +++ b/drivers/hwmon/lm75.c @@ -123,7 +123,9 @@ struct lm75_data { static const u8 lm75_sample_set_masks[] = { 0 << 5, 1 << 5, 2 << 5, 3 << 5 }; -#define LM75_SAMPLE_CLEAR_MASK (3 << 5) +#define LM75_ALERT_POLARITY_HIGH_8_BIT (BIT(2)) +#define LM75_ALERT_POLARITY_HIGH_16_BIT (BIT(2) << 8) +#define LM75_SAMPLE_CLEAR_MASK (3 << 5) /* The structure below stores the configuration values of the supported devices. * In case of being supported multiple configurations, the default one must @@ -729,6 +731,7 @@ static void lm75_remove(void *data) static int lm75_generic_probe(struct device *dev, const char *name, enum lm75_type kind, int irq, struct regmap *regmap) { + u16 clr_mask, pol_mask, set_mask; struct device *hwmon_dev; struct lm75_data *data; int status, err; @@ -766,8 +769,18 @@ static int lm75_generic_probe(struct device *dev, const char *name, return err; data->orig_conf = status; - err = lm75_write_config(data, data->params->set_mask, - data->params->clr_mask); + /* Enforce polarity active-low (default) or active-high (devicetree) */ + if (!data->params->config_reg_16bits) + pol_mask = LM75_ALERT_POLARITY_HIGH_8_BIT; + else + pol_mask = LM75_ALERT_POLARITY_HIGH_16_BIT; + + clr_mask = data->params->clr_mask | pol_mask; + set_mask = data->params->set_mask & ~pol_mask; + if (device_property_read_bool(dev, "ti,alert-polarity-active-high")) + set_mask |= pol_mask; + + err = lm75_write_config(data, set_mask, clr_mask); if (err) return err; From cf5d3086a7cb6df18bf345dac0bbb7971f347da9 Mon Sep 17 00:00:00 2001 From: Alexis Czezar Torreno Date: Tue, 5 May 2026 17:25:05 +0800 Subject: [PATCH 17/81] dt-bindings: hwmon: pmbus: add max20830 Add device tree documentation for MAX20830 step-down DC-DC switching regulator with PMBus interface. Acked-by: Conor Dooley Signed-off-by: Alexis Czezar Torreno Link: https://lore.kernel.org/r/20260505-dev_max20830-v4-1-4343dcbfd7d7@analog.com Signed-off-by: Guenter Roeck --- .../bindings/hwmon/pmbus/adi,max20830.yaml | 66 +++++++++++++++++++ MAINTAINERS | 7 ++ 2 files changed, 73 insertions(+) create mode 100644 Documentation/devicetree/bindings/hwmon/pmbus/adi,max20830.yaml diff --git a/Documentation/devicetree/bindings/hwmon/pmbus/adi,max20830.yaml b/Documentation/devicetree/bindings/hwmon/pmbus/adi,max20830.yaml new file mode 100644 index 000000000000..1625dd59417f --- /dev/null +++ b/Documentation/devicetree/bindings/hwmon/pmbus/adi,max20830.yaml @@ -0,0 +1,66 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/hwmon/pmbus/adi,max20830.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Analog Devices MAX20830 Step-Down Switching Regulator with PMBus + +maintainers: + - Alexis Czezar Torreno + +description: | + The MAX20830 is a fully integrated step-down DC-DC switching regulator with + PMBus interface. It provides 2.7V to 16V input, 0.4V to 5.8V adjustable + output, and up to 30A output current. It allows monitoring of input/output + voltage, output current and temperature through the PMBus serial interface. + Datasheet: + https://www.analog.com/en/products/max20830.html + +allOf: + - $ref: /schemas/regulator/regulator.yaml# + +properties: + compatible: + const: adi,max20830 + + reg: + maxItems: 1 + + vddh-supply: + description: + Phandle to the regulator that provides the VDDH power supply. + + avdd-supply: + description: + Phandle to the regulator that provides the AVDD power supply. + + ldoin-supply: + description: + Optional 2.5V to 5.5V LDO input supply. + + pwr-good-gpios: + description: + GPIO connected to the power-good status output pin. + maxItems: 1 + +required: + - compatible + - reg + - vddh-supply + +unevaluatedProperties: false + +examples: + - | + i2c { + #address-cells = <1>; + #size-cells = <0>; + + regulator@30 { + compatible = "adi,max20830"; + reg = <0x30>; + vddh-supply = <&vddh>; + }; + }; +... diff --git a/MAINTAINERS b/MAINTAINERS index 9c2ebcfe7b05..4169c5580b8a 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -15677,6 +15677,13 @@ F: Documentation/devicetree/bindings/hwmon/pmbus/adi,max17616.yaml F: Documentation/hwmon/max17616.rst F: drivers/hwmon/pmbus/max17616.c +MAX20830 HARDWARE MONITOR DRIVER +M: Alexis Czezar Torreno +L: linux-hwmon@vger.kernel.org +S: Supported +W: https://ez.analog.com/linux-software-drivers +F: Documentation/devicetree/bindings/hwmon/pmbus/adi,max20830.yaml + MAX2175 SDR TUNER DRIVER M: Ramesh Shanmugasundaram L: linux-media@vger.kernel.org From dfd47a3c885b79dba0bc2e6a47aa17ca443c4c21 Mon Sep 17 00:00:00 2001 From: Alexis Czezar Torreno Date: Tue, 5 May 2026 17:25:06 +0800 Subject: [PATCH 18/81] hwmon: (pmbus/max20830) add driver for max20830 Add support for MAX20830 step-down DC-DC switching regulator with PMBus interface. It allows monitoring of input/output voltage, output current and temperature through the PMBus serial interface. Signed-off-by: Alexis Czezar Torreno Link: https://lore.kernel.org/r/20260505-dev_max20830-v4-2-4343dcbfd7d7@analog.com [groeck: checkpatch cleanup (space before and after '-')] Signed-off-by: Guenter Roeck --- Documentation/hwmon/index.rst | 1 + Documentation/hwmon/max20830.rst | 49 ++++++++++++++ MAINTAINERS | 2 + drivers/hwmon/pmbus/Kconfig | 9 +++ drivers/hwmon/pmbus/Makefile | 1 + drivers/hwmon/pmbus/max20830.c | 110 +++++++++++++++++++++++++++++++ 6 files changed, 172 insertions(+) create mode 100644 Documentation/hwmon/max20830.rst create mode 100644 drivers/hwmon/pmbus/max20830.c diff --git a/Documentation/hwmon/index.rst b/Documentation/hwmon/index.rst index 2ae3316c4665..595350bfc003 100644 --- a/Documentation/hwmon/index.rst +++ b/Documentation/hwmon/index.rst @@ -159,6 +159,7 @@ Hardware Monitoring Kernel Drivers max197 max20730 max20751 + max20830 max31722 max31730 max31760 diff --git a/Documentation/hwmon/max20830.rst b/Documentation/hwmon/max20830.rst new file mode 100644 index 000000000000..936e409dcc5c --- /dev/null +++ b/Documentation/hwmon/max20830.rst @@ -0,0 +1,49 @@ +.. SPDX-License-Identifier: GPL-2.0 + +Kernel driver max20830 +====================== + +Supported chips: + + * Analog Devices MAX20830 + + Prefix: 'max20830' + + Addresses scanned: - + + Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/max20830.pdf + +Author: + + - Alexis Czezar Torreno + + +Description +----------- + +This driver supports hardware monitoring for Analog Devices MAX20830 +Step-Down Switching Regulator with PMBus Interface. + +The MAX20830 is a 2.7V to 16V, 30A fully integrated step-down DC-DC switching +regulator. 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. + +Sysfs entries +------------- + +================= ======================================== +in1_label "vin" +in1_input Measured input voltage +in1_alarm Input voltage alarm +in2_label "vout1" +in2_input Measured output voltage +in2_alarm Output voltage alarm +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 4169c5580b8a..743e451760c9 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -15683,6 +15683,8 @@ L: linux-hwmon@vger.kernel.org S: Supported W: https://ez.analog.com/linux-software-drivers F: Documentation/devicetree/bindings/hwmon/pmbus/adi,max20830.yaml +F: Documentation/hwmon/max20830.rst +F: drivers/hwmon/pmbus/max20830.c MAX2175 SDR TUNER DRIVER M: Ramesh Shanmugasundaram diff --git a/drivers/hwmon/pmbus/Kconfig b/drivers/hwmon/pmbus/Kconfig index 1f99a333abc7..8708235e3c23 100644 --- a/drivers/hwmon/pmbus/Kconfig +++ b/drivers/hwmon/pmbus/Kconfig @@ -375,6 +375,15 @@ config SENSORS_MAX20751 This driver can also be built as a module. If so, the module will be called max20751. +config SENSORS_MAX20830 + tristate "Analog Devices MAX20830" + help + If you say yes here you get hardware monitoring support for Analog + Devices MAX20830. + + This driver can also be built as a module. If so, the module will + be called max20830. + config SENSORS_MAX31785 tristate "Maxim MAX31785 and compatibles" help diff --git a/drivers/hwmon/pmbus/Makefile b/drivers/hwmon/pmbus/Makefile index 039848ecee7f..2820e7442e32 100644 --- a/drivers/hwmon/pmbus/Makefile +++ b/drivers/hwmon/pmbus/Makefile @@ -37,6 +37,7 @@ 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_MAX20830) += max20830.o obj-$(CONFIG_SENSORS_MAX31785) += max31785.o obj-$(CONFIG_SENSORS_MAX34440) += max34440.o obj-$(CONFIG_SENSORS_MAX8688) += max8688.o diff --git a/drivers/hwmon/pmbus/max20830.c b/drivers/hwmon/pmbus/max20830.c new file mode 100644 index 000000000000..cb2c23672166 --- /dev/null +++ b/drivers/hwmon/pmbus/max20830.c @@ -0,0 +1,110 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Hardware monitoring driver for Analog Devices MAX20830 + * + * Copyright (C) 2026 Analog Devices, Inc. + */ + +#include +#include +#include +#include +#include +#include "pmbus.h" + +#define MAX20830_IC_DEVICE_ID_LENGTH 9 + +static struct pmbus_driver_info max20830_info = { + .pages = 1, + .format[PSC_VOLTAGE_IN] = linear, + .format[PSC_VOLTAGE_OUT] = linear, + .format[PSC_CURRENT_OUT] = linear, + .format[PSC_TEMPERATURE] = linear, + .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 max20830_probe(struct i2c_client *client) +{ + u8 buf[I2C_SMBUS_BLOCK_MAX + 1] = {}; + int ret; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_READ_BLOCK_DATA) && + !i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_READ_I2C_BLOCK)) + return -ENODEV; + + /* + * Use i2c_smbus_read_block_data() if supported, otherwise fall back + * to i2c_smbus_read_i2c_block_data() to support I2C controllers + * which do not support SMBus block reads. + */ + if (i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_READ_BLOCK_DATA)) { + /* Reads 9 Data bytes from MAX20830 */ + ret = i2c_smbus_read_block_data(client, PMBUS_IC_DEVICE_ID, buf); + if (ret < 0) + return dev_err_probe(&client->dev, ret, + "Failed to read IC_DEVICE_ID\n"); + } else { + /* Reads 1 length byte + 9 Data bytes from MAX20830 */ + ret = i2c_smbus_read_i2c_block_data(client, PMBUS_IC_DEVICE_ID, + MAX20830_IC_DEVICE_ID_LENGTH + 1, + buf); + if (ret < 0) + return dev_err_probe(&client->dev, ret, + "Failed to read IC_DEVICE_ID\n"); + /* + * Moves data forward, removing the length byte, this is to + * match the format of i2c_smbus_read_block_data(). + * Also adjust return value to reflect length byte removal. + */ + memmove(buf, buf + 1, MAX20830_IC_DEVICE_ID_LENGTH); + ret = ret - 1; + } + + /* + * MAX20830 IC_DEVICE_ID sends string data "MAX20830\0". + * Return value should at least be 9 bytes of data. + */ + if (ret < MAX20830_IC_DEVICE_ID_LENGTH) + return dev_err_probe(&client->dev, -ENODEV, + "IC_DEVICE_ID too short: expected at least 9 bytes, got %d\n", + ret); + + /* 9 bytes of data, buf[0]-buf[7] = "MAX20830", buf[8] = '\0' */ + buf[MAX20830_IC_DEVICE_ID_LENGTH - 1] = '\0'; + if (strncmp(buf, "MAX20830", MAX20830_IC_DEVICE_ID_LENGTH - 1)) + return dev_err_probe(&client->dev, -ENODEV, + "Unsupported device: '%s'\n", buf); + + return pmbus_do_probe(client, &max20830_info); +} + +static const struct i2c_device_id max20830_id[] = { + {"max20830"}, + { } +}; +MODULE_DEVICE_TABLE(i2c, max20830_id); + +static const struct of_device_id max20830_of_match[] = { + { .compatible = "adi,max20830" }, + { } +}; +MODULE_DEVICE_TABLE(of, max20830_of_match); + +static struct i2c_driver max20830_driver = { + .driver = { + .name = "max20830", + .of_match_table = max20830_of_match, + }, + .probe = max20830_probe, + .id_table = max20830_id, +}; + +module_i2c_driver(max20830_driver); + +MODULE_AUTHOR("Alexis Czezar Torreno "); +MODULE_DESCRIPTION("PMBus driver for Analog Devices MAX20830"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("PMBUS"); From 7232b794bf8d0f3d47af9fab4b0b07f2da291afa Mon Sep 17 00:00:00 2001 From: Colin Huang Date: Fri, 8 May 2026 17:44:28 +0800 Subject: [PATCH 19/81] dt-bindings: trivial-devices: Add Delta E50SN12051 Add 600W Non-isolated 1/8th Brick DC/DC Power Modules, E50SN12051. Signed-off-by: Colin Huang Acked-by: Conor Dooley Link: https://lore.kernel.org/r/20260508-add-e50sn12051-v5-1-abebdcc29665@gmail.com [groeck: Swapped comment and definition] 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 6b461b4096d3..d7940c488ab6 100644 --- a/Documentation/devicetree/bindings/trivial-devices.yaml +++ b/Documentation/devicetree/bindings/trivial-devices.yaml @@ -99,6 +99,8 @@ properties: - delta,dps800 # Delta Electronics DPS920AB 920W 54V Power Supply - delta,dps920ab + # 600W Non-isolated 1/8th Brick DC/DC Power Modules + - delta,e50sn12051 # 1/4 Brick DC/DC Regulated Power Module - delta,q54sj108a2 # 1300W 1/4 Brick DC/DC Regulated Power Module From c41bc867788aca16b6f2d27185ffad8dca8f99c3 Mon Sep 17 00:00:00 2001 From: Colin Huang Date: Fri, 8 May 2026 17:44:29 +0800 Subject: [PATCH 20/81] Documentation/hwmon: add Delta E50SN12051 documentation Document the hardware monitoring support for the Delta E50SN12051 device. The documentation describes the supported sensors exposed via the hwmon subsystem, including voltage, current, and temperature measurements. Signed-off-by: Colin Huang Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/20260508-add-e50sn12051-v5-2-abebdcc29665@gmail.com Signed-off-by: Guenter Roeck --- Documentation/hwmon/e50sn12051.rst | 81 ++++++++++++++++++++++++++++++ Documentation/hwmon/index.rst | 1 + 2 files changed, 82 insertions(+) create mode 100644 Documentation/hwmon/e50sn12051.rst diff --git a/Documentation/hwmon/e50sn12051.rst b/Documentation/hwmon/e50sn12051.rst new file mode 100644 index 000000000000..cbbfa7895d82 --- /dev/null +++ b/Documentation/hwmon/e50sn12051.rst @@ -0,0 +1,81 @@ +.. SPDX-License-Identifier: GPL-2.0-or-later + +Kernel driver e50sn12051 +======================== + +Supported chips: + + * Delta E50SN12051 + + Prefix: 'e50sn12051' + + Addresses scanned: - + + Datasheet: + + Provided by Delta upon request and NDA + +Description +----------- + +E50SN12051 is a 600W non-isolated 1/8th brick DC-DC power module. + +This driver supports the E50SN12051 PMBus compliant monitor device. + +The device provides measurements for: + +* Input voltage +* Output current +* Output voltage +* Temperature + +The driver is based on the PMBus core and exposes standard hwmon +sysfs attributes. + +Sysfs attributes +---------------- + +======================= ======================================================= +curr1_label "iout1" +curr1_crit Critical maximum current. + From IOUT_OC_FAULT_LIMIT register. +curr1_crit_alarm Current critical high alarm. + From IOUT_OC_FAULT status. +curr1_input Measured current. + From READ_IOUT register. +curr1_max Maximum current. + From IOUT_OC_WARN_LIMIT register. +curr1_max_alarm Current high alarm. + From IOUT_OC_WARN_LIMIT status. + +in1_label "vin" +in1_alarm Input voltage alarm. +in1_input Measured voltage. + From READ_VIN register. + +in2_label "vout1" +in2_crit Critical maximum voltage. + From VOUT_OV_FAULT_LIMIT register. +in2_crit_alarm Voltage critical high alarm. + From VOLTAGE_OV_FAULT status. +in2_input Measured voltage. + From READ_VOUT register. +in2_max Maximum voltage. + From VOUT_OV_WARN_LIMIT register. +in2_max_alarm Voltage high alarm. + From VOLTAGE_OV_WARNING status. + +temp1_crit Critical high temperature. + From OT_FAULT_LIMIT register. +temp1_crit_alarm Module temperature critical high alarm. + Set by comparing READ_TEMPERATURE_1 with OT_FAULT_LIMIT + if TEMP_OT_FAULT status is set. +temp1_input Measured module's hot spot temperature. + From READ_TEMPERATURE_1 register. +temp1_max Maximum temperature. + From OT_WARN_LIMIT register. +temp1_max_alarm Module temperature high alarm. + Set by comparing READ_TEMPERATURE_1 with OT_WARN_LIMIT if + TEMP_OT_WARNING status is set. +======================= ======================================================= + diff --git a/Documentation/hwmon/index.rst b/Documentation/hwmon/index.rst index 595350bfc003..f9cbdcb75128 100644 --- a/Documentation/hwmon/index.rst +++ b/Documentation/hwmon/index.rst @@ -68,6 +68,7 @@ Hardware Monitoring Kernel Drivers drivetemp ds1621 ds620 + e50sn12051 emc1403 emc2103 emc2305 From 98be2444a6453fc1e13afeb03c70f898a2fda4e9 Mon Sep 17 00:00:00 2001 From: Colin Huang Date: Fri, 8 May 2026 17:44:30 +0800 Subject: [PATCH 21/81] hwmon: (pmbus) add support for Delta E50SN12051 Add the pmbus driver for Delta E50SN12051 600W Non-isolated 1/8th Brick DCDC Power Modules. Signed-off-by: Colin Huang Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/20260508-add-e50sn12051-v5-3-abebdcc29665@gmail.com Signed-off-by: Guenter Roeck --- drivers/hwmon/pmbus/Kconfig | 9 ++++++ drivers/hwmon/pmbus/Makefile | 1 + drivers/hwmon/pmbus/e50sn12051.c | 52 ++++++++++++++++++++++++++++++++ 3 files changed, 62 insertions(+) create mode 100644 drivers/hwmon/pmbus/e50sn12051.c diff --git a/drivers/hwmon/pmbus/Kconfig b/drivers/hwmon/pmbus/Kconfig index 8708235e3c23..c3bb6df2655a 100644 --- a/drivers/hwmon/pmbus/Kconfig +++ b/drivers/hwmon/pmbus/Kconfig @@ -161,6 +161,15 @@ config SENSORS_DPS920AB This driver can also be built as a module. If so, the module will be called dps920ab. +config SENSORS_E50SN12051 + tristate "Delta E50SN12051 Power Modules" + help + If you say yes here you get hardware monitoring support for Delta + E50SN12051 Power Modules. + + This driver can also be built as a module. If so, the module will + be called e50sn12051. + config SENSORS_INA233 tristate "Texas Instruments INA233 and compatibles" help diff --git a/drivers/hwmon/pmbus/Makefile b/drivers/hwmon/pmbus/Makefile index 2820e7442e32..efd70b00ca2e 100644 --- a/drivers/hwmon/pmbus/Makefile +++ b/drivers/hwmon/pmbus/Makefile @@ -17,6 +17,7 @@ obj-$(CONFIG_SENSORS_FSP_3Y) += fsp-3y.o obj-$(CONFIG_SENSORS_HAC300S) += hac300s.o obj-$(CONFIG_SENSORS_IBM_CFFPS) += ibm-cffps.o obj-$(CONFIG_SENSORS_DPS920AB) += dps920ab.o +obj-$(CONFIG_SENSORS_E50SN12051) += e50sn12051.o obj-$(CONFIG_SENSORS_INA233) += ina233.o obj-$(CONFIG_SENSORS_INSPUR_IPSPS) += inspur-ipsps.o obj-$(CONFIG_SENSORS_IR35221) += ir35221.o diff --git a/drivers/hwmon/pmbus/e50sn12051.c b/drivers/hwmon/pmbus/e50sn12051.c new file mode 100644 index 000000000000..efb4d62b2603 --- /dev/null +++ b/drivers/hwmon/pmbus/e50sn12051.c @@ -0,0 +1,52 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Hardware monitoring driver for E50SN12051 + */ + +#include +#include +#include +#include "pmbus.h" + +static struct pmbus_driver_info e50sn12051_info = { + .pages = 1, + .format[PSC_VOLTAGE_IN] = linear, + .format[PSC_VOLTAGE_OUT] = linear, + .format[PSC_CURRENT_OUT] = linear, + .format[PSC_TEMPERATURE] = linear, + .func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_STATUS_INPUT | + PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT | + PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT | + PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP, +}; + +static const struct i2c_device_id e50sn12051_id[] = { { "e50sn12051", 0 }, {} }; +MODULE_DEVICE_TABLE(i2c, e50sn12051_id); + +static const struct of_device_id e50sn12051_of_match[] = { + { .compatible = "delta,e50sn12051" }, + {}, +}; +MODULE_DEVICE_TABLE(of, e50sn12051_of_match); + +static int e50sn12051_probe(struct i2c_client *client) +{ + return pmbus_do_probe(client, &e50sn12051_info); +} + +static struct i2c_driver e50sn12051_driver = { + .driver = { + .name = "e50sn12051", + .of_match_table = e50sn12051_of_match, + }, + .probe = e50sn12051_probe, + + .id_table = e50sn12051_id, +}; + +module_i2c_driver(e50sn12051_driver); + +MODULE_AUTHOR("Kevin Chang "); +MODULE_DESCRIPTION("PMBus driver for E50SN12051"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("PMBUS"); From e28d0c73d4d7adc9cd3747d81fdc7338217f9a0c Mon Sep 17 00:00:00 2001 From: Aureo Serrano de Souza Date: Fri, 8 May 2026 14:44:00 +0800 Subject: [PATCH 22/81] hwmon: add driver for ARCTIC Fan Controller Add hwmon driver for the ARCTIC Fan Controller, a USB HID device (VID 0x3904, PID 0xF001) with 10 fan channels. Exposes fan speed in RPM (read-only) and PWM duty cycle (0-255, read/write) via sysfs. The device pushes IN reports at ~1 Hz containing RPM readings. PWM is set via OUT reports; the device applies the new duty cycle and sends back a 2-byte ACK (Report ID 0x02). The driver waits up to 1 s for the ACK using a completion. Measured device latency: max ~563 ms over 500 iterations. PWM control is manual-only: the device never changes duty cycle autonomously. raw_event() may run in hardirq context, so fan_rpm[] is protected by a spinlock with irq-save. pwm_duty[] is also protected by this spinlock because reset_resume() clears it outside the hwmon core lock. The OUT report buffer is built and write_pending is armed under the same lock so that no reset_resume() can race with the pwm_duty[] snapshot. priv->buf is exclusively accessed by write(), which the hwmon core serializes. Signed-off-by: Aureo Serrano de Souza Link: https://lore.kernel.org/r/20260508064405.38676-1-aureo.serrano@arctic.de Signed-off-by: Guenter Roeck --- Documentation/hwmon/arctic_fan_controller.rst | 56 +++ Documentation/hwmon/index.rst | 1 + MAINTAINERS | 7 + drivers/hwmon/Kconfig | 12 + drivers/hwmon/Makefile | 1 + drivers/hwmon/arctic_fan_controller.c | 374 ++++++++++++++++++ 6 files changed, 451 insertions(+) create mode 100644 Documentation/hwmon/arctic_fan_controller.rst create mode 100644 drivers/hwmon/arctic_fan_controller.c diff --git a/Documentation/hwmon/arctic_fan_controller.rst b/Documentation/hwmon/arctic_fan_controller.rst new file mode 100644 index 000000000000..b5be88ae464d --- /dev/null +++ b/Documentation/hwmon/arctic_fan_controller.rst @@ -0,0 +1,56 @@ +.. SPDX-License-Identifier: GPL-2.0-or-later + +Kernel driver arctic_fan_controller +===================================== + +Supported devices: + +* ARCTIC Fan Controller (USB HID, VID 0x3904, PID 0xF001) + +Author: Aureo Serrano de Souza + +Description +----------- + +This driver provides hwmon support for the ARCTIC Fan Controller, a USB +Custom HID device with 10 fan channels. The device sends IN reports about +once per second containing current RPM values (bytes 11-30, 10 x uint16 LE). +Fan speed control is manual-only: the device does not change PWM +autonomously; it only applies a new duty cycle when it receives an OUT +report from the host. + +After the device applies an OUT report, it sends back a 2-byte ACK IN +report (Report ID 0x02, byte 1 = 0x00 on success) confirming the command +was applied. + +Usage notes +----------- + +Since it is a USB device, hotplug is supported. The device is autodetected. + +The device does not support GET_REPORT, so the driver cannot read back the +current hardware PWM state at probe time. The cached PWM values (readable +via pwm[1-10]) start at 0 and reflect only values that have been +successfully written. Because each OUT report carries all 10 channel values, +writing a single channel also sends the cached values for all other channels. +Users should set all channels to the desired values before relying on the +cached state. + +On system suspend, the device may lose power and reset its PWM channels to +hardware defaults. The driver clears its cached duty values on resume so +that reads reflect the unknown hardware state rather than stale pre-suspend +values. Userspace is responsible for re-applying the desired duty cycles +after resume. + +Sysfs entries +------------- + +================ ============================================================== +fan[1-10]_input Fan speed in RPM (read-only). Updated from IN reports at ~1 Hz. +pwm[1-10] PWM duty cycle (0-255). Write: sends an OUT report setting the + duty cycle (scaled from 0-255 to 0-100% for the device); + the cached value is updated only after the device ACKs the + command with a success status. Read: returns the last + successfully written value; initialized to 0 at driver load + and after resume (hardware state unknown). +================ ============================================================== diff --git a/Documentation/hwmon/index.rst b/Documentation/hwmon/index.rst index f9cbdcb75128..78c783f11e25 100644 --- a/Documentation/hwmon/index.rst +++ b/Documentation/hwmon/index.rst @@ -43,6 +43,7 @@ Hardware Monitoring Kernel Drivers amc6821 aps-379 aquacomputer_d5next + arctic_fan_controller asb100 asc7621 aspeed-g6-pwm-tach diff --git a/MAINTAINERS b/MAINTAINERS index 743e451760c9..5d4453414e77 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2070,6 +2070,13 @@ S: Maintained F: drivers/net/arcnet/ F: include/uapi/linux/if_arcnet.h +ARCTIC FAN CONTROLLER DRIVER +M: Aureo Serrano de Souza +L: linux-hwmon@vger.kernel.org +S: Maintained +F: Documentation/hwmon/arctic_fan_controller.rst +F: drivers/hwmon/arctic_fan_controller.c + ARM AND ARM64 SoC SUB-ARCHITECTURES (COMMON PARTS) M: Arnd Bergmann M: Krzysztof Kozlowski diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 14e4cea48acc..997bba56fedf 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -388,6 +388,18 @@ config SENSORS_APPLESMC Say Y here if you have an applicable laptop and want to experience the awesome power of applesmc. +config SENSORS_ARCTIC_FAN_CONTROLLER + tristate "ARCTIC Fan Controller" + depends on USB_HID + help + If you say yes here you get support for the ARCTIC Fan Controller, + a USB HID device (VID 0x3904, PID 0xF001) with 10 fan channels. + The driver exposes fan speed (RPM) and PWM control via the hwmon + sysfs interface. + + This driver can also be built as a module. If so, the module + will be called arctic_fan_controller. + config SENSORS_ARM_SCMI tristate "ARM SCMI Sensors" depends on ARM_SCMI_PROTOCOL diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index 982ee2c6f9de..efbd1cb81816 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -49,6 +49,7 @@ obj-$(CONFIG_SENSORS_ADT7475) += adt7475.o obj-$(CONFIG_SENSORS_AHT10) += aht10.o obj-$(CONFIG_SENSORS_APPLESMC) += applesmc.o obj-$(CONFIG_SENSORS_AQUACOMPUTER_D5NEXT) += aquacomputer_d5next.o +obj-$(CONFIG_SENSORS_ARCTIC_FAN_CONTROLLER) += arctic_fan_controller.o obj-$(CONFIG_SENSORS_ARM_SCMI) += scmi-hwmon.o obj-$(CONFIG_SENSORS_ARM_SCPI) += scpi-hwmon.o obj-$(CONFIG_SENSORS_AS370) += as370-hwmon.o diff --git a/drivers/hwmon/arctic_fan_controller.c b/drivers/hwmon/arctic_fan_controller.c new file mode 100644 index 000000000000..dbe84cd93c08 --- /dev/null +++ b/drivers/hwmon/arctic_fan_controller.c @@ -0,0 +1,374 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Linux hwmon driver for ARCTIC Fan Controller + * + * USB Custom HID device with 10 fan channels. + * Exposes fan RPM (input) and PWM (0-255) via hwmon. Device pushes IN reports + * at ~1 Hz; no GET_REPORT. OUT reports set PWM duty (bytes 1-10, 0-100%). + * PWM is manual-only: the device does not change duty autonomously, only + * when it receives an OUT report from the host. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define ARCTIC_VID 0x3904 +#define ARCTIC_PID 0xF001 +#define ARCTIC_NUM_FANS 10 +#define ARCTIC_OUTPUT_REPORT_ID 0x01 +#define ARCTIC_REPORT_LEN 32 +#define ARCTIC_RPM_OFFSET 11 /* bytes 11-30: 10 x uint16 LE */ +/* ACK report: device sends Report ID 0x02, 2 bytes (ID + status) after applying OUT report */ +#define ARCTIC_ACK_REPORT_ID 0x02 +#define ARCTIC_ACK_REPORT_LEN 2 +/* + * Time to wait for ACK report after send. + * Measured over 500 iterations: max ~563 ms. Keep 1 s as margin. + */ +#define ARCTIC_ACK_TIMEOUT_MS 1000 + +struct arctic_fan_data { + struct hid_device *hdev; + struct device *hwmon_dev; /* stored for explicit unregister in remove() */ + spinlock_t in_report_lock; /* protects fan_rpm, ack_status, write_pending, pwm_duty */ + struct completion in_report_received; /* ACK (ID 0x02) received in raw_event */ + int ack_status; /* 0 = OK, negative errno on device error */ + bool write_pending; /* true while an OUT report ACK is in flight */ + u32 fan_rpm[ARCTIC_NUM_FANS]; + u8 pwm_duty[ARCTIC_NUM_FANS]; /* 0-255 matching sysfs range; converted to 0-100 on send */ + /* + * OUT report buffer passed to hid_hw_output_report(). Embedded in the + * devm_kzalloc'd struct so it is heap-allocated and passes + * usb_hcd_map_urb_for_dma(). Exclusively accessed by write(), which + * the hwmon core serializes. + */ + __dma_from_device_group_begin(); + u8 buf[ARCTIC_REPORT_LEN]; + __dma_from_device_group_end(); +}; + +/* + * Parse RPM values from the periodic status report (10 x uint16 LE at rpm_off). + * pwm_duty is not updated from the report: the device is manual-only, so the + * host cache is the authoritative source for PWM. + * Called from raw_event which may run in IRQ context; must not sleep. + */ +static void arctic_fan_parse_report(struct arctic_fan_data *priv, u8 *buf, + int len, int rpm_off) +{ + unsigned long flags; + int i; + + if (len < rpm_off + 20) + return; + + spin_lock_irqsave(&priv->in_report_lock, flags); + for (i = 0; i < ARCTIC_NUM_FANS; i++) + priv->fan_rpm[i] = get_unaligned_le16(&buf[rpm_off + i * 2]); + spin_unlock_irqrestore(&priv->in_report_lock, flags); +} + +/* + * raw_event: IN reports. + * + * Status report: Report ID 0x01, 32 bytes: + * byte 0 = report ID, bytes 1-10 = PWM 0-100%, bytes 11-30 = 10 x RPM uint16 LE. + * Device pushes these at ~1 Hz; no GET_REPORT. + * + * ACK report: Report ID 0x02, 2 bytes: + * byte 0 = 0x02, byte 1 = status (0x00 = OK, 0x01 = ERROR). + * Sent once after accepting and applying an OUT report (ID 0x01). + */ +static int arctic_fan_raw_event(struct hid_device *hdev, + struct hid_report *report, u8 *data, int size) +{ + struct arctic_fan_data *priv = hid_get_drvdata(hdev); + unsigned long flags; + + hid_dbg(hdev, "arctic_fan: raw_event id=%u size=%d\n", report->id, size); + + if (report->id == ARCTIC_ACK_REPORT_ID && size == ARCTIC_ACK_REPORT_LEN) { + spin_lock_irqsave(&priv->in_report_lock, flags); + /* + * Only deliver if a write is in flight. This prevents a + * late-arriving ACK from a timed-out write from erroneously + * satisfying a subsequent write's completion wait. + */ + if (priv->write_pending) { + priv->ack_status = data[1] == 0x00 ? 0 : -EIO; + complete(&priv->in_report_received); + } + spin_unlock_irqrestore(&priv->in_report_lock, flags); + return 0; + } + + if (report->id != ARCTIC_OUTPUT_REPORT_ID || size != ARCTIC_REPORT_LEN) { + hid_dbg(hdev, "arctic_fan: raw_event id=%u size=%d ignored\n", + report->id, size); + return 0; + } + + arctic_fan_parse_report(priv, data, size, ARCTIC_RPM_OFFSET); + return 0; +} + +static umode_t arctic_fan_is_visible(const void *data, + enum hwmon_sensor_types type, + u32 attr, int channel) +{ + if (type == hwmon_fan && attr == hwmon_fan_input) + return 0444; + if (type == hwmon_pwm && attr == hwmon_pwm_input) + return 0644; + return 0; +} + +static int arctic_fan_read(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long *val) +{ + struct arctic_fan_data *priv = dev_get_drvdata(dev); + unsigned long flags; + + if (type == hwmon_fan && attr == hwmon_fan_input) { + spin_lock_irqsave(&priv->in_report_lock, flags); + *val = priv->fan_rpm[channel]; + spin_unlock_irqrestore(&priv->in_report_lock, flags); + return 0; + } + if (type == hwmon_pwm && attr == hwmon_pwm_input) { + spin_lock_irqsave(&priv->in_report_lock, flags); + *val = priv->pwm_duty[channel]; + spin_unlock_irqrestore(&priv->in_report_lock, flags); + return 0; + } + return -EINVAL; +} + +static int arctic_fan_write(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long val) +{ + struct arctic_fan_data *priv = dev_get_drvdata(dev); + u8 new_duty = (u8)clamp_val(val, 0, 255); + unsigned long flags; + unsigned long t; + int i, ret; + + /* + * Build the buffer and arm write_pending under in_report_lock so that + * reset_resume() cannot clear pwm_duty[] between the pwm_duty[] read + * and the buffer write, and raw_event() cannot deliver a stale ACK + * from a previous write into this write's completion. + * + * priv->buf is heap-allocated (embedded in the devm_kzalloc'd struct), + * satisfying usb_hcd_map_urb_for_dma(). Exclusively accessed by + * write() which the hwmon core serializes. + * + * pwm_duty[channel] is committed only after a positive device ACK so a + * failed or timed-out write does not corrupt the cached state. + * + * Residual theoretical race: if write A times out (write_pending + * cleared), write B sets write_pending = true, and a late ACK from + * write A—delayed beyond ARCTIC_ACK_TIMEOUT_MS—arrives during write + * B's pending window, it would falsely satisfy write B's completion. + * This cannot be prevented in driver code without protocol support + * (for example, a correlation ID echoed in the device ACK report). + * In testing, observed ACK latency stayed below the 1 s timeout + * (maximum ~563 ms over 500 iterations). + * + * The wait is non-interruptible so that a signal cannot cause write() + * to return early while the OUT report is already in flight; an + * interruptible early return would create the same late-ACK window + * without even the timeout guard. + * Serialized by the hwmon core: only one arctic_fan_write() at a time. + * Use irqsave to match the IRQ context in which raw_event may run. + */ + spin_lock_irqsave(&priv->in_report_lock, flags); + priv->buf[0] = ARCTIC_OUTPUT_REPORT_ID; + for (i = 0; i < ARCTIC_NUM_FANS; i++) { + u8 d = i == channel ? new_duty : priv->pwm_duty[i]; + + priv->buf[1 + i] = DIV_ROUND_CLOSEST((unsigned int)d * 100, 255); + } + priv->ack_status = -ETIMEDOUT; + priv->write_pending = true; + reinit_completion(&priv->in_report_received); + spin_unlock_irqrestore(&priv->in_report_lock, flags); + + ret = hid_hw_output_report(priv->hdev, priv->buf, ARCTIC_REPORT_LEN); + if (ret < 0) { + spin_lock_irqsave(&priv->in_report_lock, flags); + priv->write_pending = false; + spin_unlock_irqrestore(&priv->in_report_lock, flags); + return ret; + } + + t = wait_for_completion_timeout(&priv->in_report_received, + msecs_to_jiffies(ARCTIC_ACK_TIMEOUT_MS)); + spin_lock_irqsave(&priv->in_report_lock, flags); + priv->write_pending = false; + /* Commit inside the lock so reset_resume() cannot race with this write */ + if (t && priv->ack_status == 0) + priv->pwm_duty[channel] = new_duty; + spin_unlock_irqrestore(&priv->in_report_lock, flags); + + if (!t) + return -ETIMEDOUT; + return priv->ack_status; /* 0=OK, -EIO=device error */ +} + +static const struct hwmon_ops arctic_fan_ops = { + .is_visible = arctic_fan_is_visible, + .read = arctic_fan_read, + .write = arctic_fan_write, +}; + +static const struct hwmon_channel_info *arctic_fan_info[] = { + HWMON_CHANNEL_INFO(fan, + HWMON_F_INPUT, HWMON_F_INPUT, HWMON_F_INPUT, + HWMON_F_INPUT, HWMON_F_INPUT, HWMON_F_INPUT, + HWMON_F_INPUT, HWMON_F_INPUT, HWMON_F_INPUT, + HWMON_F_INPUT), + HWMON_CHANNEL_INFO(pwm, + HWMON_PWM_INPUT, HWMON_PWM_INPUT, HWMON_PWM_INPUT, + HWMON_PWM_INPUT, HWMON_PWM_INPUT, HWMON_PWM_INPUT, + HWMON_PWM_INPUT, HWMON_PWM_INPUT, HWMON_PWM_INPUT, + HWMON_PWM_INPUT), + NULL +}; + +static const struct hwmon_chip_info arctic_fan_chip_info = { + .ops = &arctic_fan_ops, + .info = arctic_fan_info, +}; + +static int arctic_fan_reset_resume(struct hid_device *hdev) +{ + struct arctic_fan_data *priv = hid_get_drvdata(hdev); + unsigned long flags; + + /* + * The device resets its PWM channels to hardware defaults on power + * loss during suspend. Clear the cached duty values so they reflect + * the unknown hardware state, consistent with probe-time behaviour + * (the device has no GET_REPORT support). Hold in_report_lock so + * this does not race with a concurrent pwm read or write callback. + */ + spin_lock_irqsave(&priv->in_report_lock, flags); + memset(priv->pwm_duty, 0, sizeof(priv->pwm_duty)); + spin_unlock_irqrestore(&priv->in_report_lock, flags); + return 0; +} + +static int arctic_fan_probe(struct hid_device *hdev, + const struct hid_device_id *id) +{ + struct arctic_fan_data *priv; + int ret; + + if (!hid_is_usb(hdev)) + return -ENODEV; + + ret = hid_parse(hdev); + if (ret) + return ret; + + priv = devm_kzalloc(&hdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->hdev = hdev; + spin_lock_init(&priv->in_report_lock); + init_completion(&priv->in_report_received); + hid_set_drvdata(hdev, priv); + + ret = hid_hw_start(hdev, HID_CONNECT_DRIVER); + if (ret) + return ret; + + ret = hid_hw_open(hdev); + if (ret) + goto out_stop; + + /* + * Start IO before registering with hwmon. If IO were started after + * hwmon registration, a sysfs write arriving in that narrow window + * would send an OUT report but the ACK could not be delivered (the HID + * core discards events until io_started), causing a spurious timeout. + */ + hid_device_io_start(hdev); + + /* + * Use the non-devm variant and store the pointer so remove() can + * call hwmon_device_unregister() before tearing down the HID + * transport. devm_hwmon_device_register_with_info() would defer + * unregistration until after remove() returns, leaving a window + * where a concurrent sysfs write could call hid_hw_output_report() + * on an already-stopped device (use-after-free). + */ + priv->hwmon_dev = hwmon_device_register_with_info(&hdev->dev, "arctic_fan", + priv, &arctic_fan_chip_info, + NULL); + if (IS_ERR(priv->hwmon_dev)) { + ret = PTR_ERR(priv->hwmon_dev); + goto out_close; + } + + return 0; + +out_close: + hid_device_io_stop(hdev); + hid_hw_close(hdev); +out_stop: + hid_hw_stop(hdev); + return ret; +} + +static void arctic_fan_remove(struct hid_device *hdev) +{ + struct arctic_fan_data *priv = hid_get_drvdata(hdev); + + /* + * Unregister hwmon before stopping the HID transport. This removes + * the sysfs files and waits for any in-progress write() callback to + * return, so no hwmon op can call hid_hw_output_report() after + * hid_hw_stop() frees the underlying USB resources. + * Matches the pattern used by nzxt-smart2 and aquacomputer_d5next. + * + * The HID core clears hdev->io_started before invoking ->remove(), + * so hid_device_io_stop() is not called here; doing so would emit + * a spurious "io already stopped" warning. + */ + hwmon_device_unregister(priv->hwmon_dev); + hid_hw_close(hdev); + hid_hw_stop(hdev); +} + +static const struct hid_device_id arctic_fan_id_table[] = { + { HID_USB_DEVICE(ARCTIC_VID, ARCTIC_PID) }, + { } +}; +MODULE_DEVICE_TABLE(hid, arctic_fan_id_table); + +static struct hid_driver arctic_fan_driver = { + .name = "arctic_fan", + .id_table = arctic_fan_id_table, + .probe = arctic_fan_probe, + .remove = arctic_fan_remove, + .raw_event = arctic_fan_raw_event, + .reset_resume = arctic_fan_reset_resume, +}; + +module_hid_driver(arctic_fan_driver); + +MODULE_AUTHOR("Aureo Serrano de Souza "); +MODULE_DESCRIPTION("HID hwmon driver for ARCTIC Fan Controller"); +MODULE_LICENSE("GPL"); From 624fce2eec59ab28fd05c235ee26d3cbf1cbef60 Mon Sep 17 00:00:00 2001 From: Flaviu Nistor Date: Sun, 10 May 2026 12:25:43 +0300 Subject: [PATCH 23/81] hwmon: (tmp102) Use device_property_read_string API Replace of_property_read_string() with the preferded device_property_read_string() in the probe function to read the device label property, improving the driver compatibility since this method is not limited to Device Tree only. Also drop the now unnecessary __maybe_unused from tmp102_of_match. Signed-off-by: Flaviu Nistor Link: https://lore.kernel.org/r/20260510092543.12352-1-flaviu.nistor@gmail.com Link: https://lore.kernel.org/r/20260518122210.10288-1-flaviu.nistor@gmail.com [groeck: Combined API change with __maybe_unused patch] Signed-off-by: Guenter Roeck --- drivers/hwmon/tmp102.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/hwmon/tmp102.c b/drivers/hwmon/tmp102.c index 3aa1a3fbeaa9..ad525c55daf0 100644 --- a/drivers/hwmon/tmp102.c +++ b/drivers/hwmon/tmp102.c @@ -15,7 +15,8 @@ #include #include #include -#include +#include +#include #define DRIVER_NAME "tmp102" @@ -313,7 +314,7 @@ static int tmp102_probe(struct i2c_client *client) if (!tmp102) return -ENOMEM; - of_property_read_string(dev->of_node, "label", &tmp102->label); + device_property_read_string(dev, "label", &tmp102->label); i2c_set_clientdata(client, tmp102); @@ -400,7 +401,7 @@ static const struct i2c_device_id tmp102_id[] = { }; MODULE_DEVICE_TABLE(i2c, tmp102_id); -static const struct of_device_id __maybe_unused tmp102_of_match[] = { +static const struct of_device_id tmp102_of_match[] = { { .compatible = "ti,tmp102" }, { }, }; @@ -408,7 +409,7 @@ MODULE_DEVICE_TABLE(of, tmp102_of_match); static struct i2c_driver tmp102_driver = { .driver.name = DRIVER_NAME, - .driver.of_match_table = of_match_ptr(tmp102_of_match), + .driver.of_match_table = tmp102_of_match, .driver.pm = pm_sleep_ptr(&tmp102_dev_pm_ops), .probe = tmp102_probe, .id_table = tmp102_id, From 352b18d2532944f3f8fce8e6bff70c6312b0c192 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig=20=28The=20Capable=20Hub=29?= Date: Tue, 12 May 2026 18:28:56 +0200 Subject: [PATCH 24/81] hwmon: (pmbus/mp2869) Remove unused driver data MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The driver doesn't make use of the i2c .driver_data and the of .data. So drop the useless and irritating assignments. While touching all these lines, use named initializers for the i2c device ids and convert to the most common usage of spacing in such arrays. Signed-off-by: Uwe Kleine-König (The Capable Hub) Link: https://lore.kernel.org/r/dcfcc82a93da77b55503998c5c7acf2a80c4a615.1778603083.git.ukleinek@kernel.org Signed-off-by: Guenter Roeck --- drivers/hwmon/pmbus/mp2869.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/hwmon/pmbus/mp2869.c b/drivers/hwmon/pmbus/mp2869.c index 4647892e5112..d345be920429 100644 --- a/drivers/hwmon/pmbus/mp2869.c +++ b/drivers/hwmon/pmbus/mp2869.c @@ -632,20 +632,20 @@ static int mp2869_probe(struct i2c_client *client) } static const struct i2c_device_id mp2869_id[] = { - {"mp2869", 0}, - {"mp29608", 1}, - {"mp29612", 2}, - {"mp29816", 3}, - {} + { .name = "mp2869" }, + { .name = "mp29608" }, + { .name = "mp29612" }, + { .name = "mp29816" }, + { } }; MODULE_DEVICE_TABLE(i2c, mp2869_id); static const struct of_device_id __maybe_unused mp2869_of_match[] = { - {.compatible = "mps,mp2869", .data = (void *)0}, - {.compatible = "mps,mp29608", .data = (void *)1}, - {.compatible = "mps,mp29612", .data = (void *)2}, - {.compatible = "mps,mp29816", .data = (void *)3}, - {} + { .compatible = "mps,mp2869" }, + { .compatible = "mps,mp29608" }, + { .compatible = "mps,mp29612" }, + { .compatible = "mps,mp29816" }, + { } }; MODULE_DEVICE_TABLE(of, mp2869_of_match); From c057f9863ee5c8ba725e815f0136dca60d657475 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig=20=28The=20Capable=20Hub=29?= Date: Tue, 12 May 2026 18:28:57 +0200 Subject: [PATCH 25/81] hwmon: (pmbus/mp2869) Drop unjustified __maybe_unused MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit mp2869_of_match is used unconditionally in mp2869_driver, so there is no need for the __maybe_unused marker. Signed-off-by: Uwe Kleine-König (The Capable Hub) Link: https://lore.kernel.org/r/3ec2ee181d4f5bfc48c3745f9ce4fdbd1e8fb01e.1778603083.git.ukleinek@kernel.org Signed-off-by: Guenter Roeck --- drivers/hwmon/pmbus/mp2869.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hwmon/pmbus/mp2869.c b/drivers/hwmon/pmbus/mp2869.c index d345be920429..76c6017507b3 100644 --- a/drivers/hwmon/pmbus/mp2869.c +++ b/drivers/hwmon/pmbus/mp2869.c @@ -640,7 +640,7 @@ static const struct i2c_device_id mp2869_id[] = { }; MODULE_DEVICE_TABLE(i2c, mp2869_id); -static const struct of_device_id __maybe_unused mp2869_of_match[] = { +static const struct of_device_id mp2869_of_match[] = { { .compatible = "mps,mp2869" }, { .compatible = "mps,mp29608" }, { .compatible = "mps,mp29612" }, From b3de407ae66ffef6290a31363205e9751db0537a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig=20=28The=20Capable=20Hub=29?= Date: Wed, 13 May 2026 18:44:57 +0200 Subject: [PATCH 26/81] hwmon: Drop unused i2c driver_data MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The four drivers all don't make use of the value that was explicitly assigned to the .driver_data member. Drop the assignment. While touching these lines also make the assignments use named initializers and drop a comma after the end-of-list marker. Signed-off-by: Uwe Kleine-König (The Capable Hub) Reviewed-by: Chris Packham # For Link: https://lore.kernel.org/r/e8ceb3931975813545a8b478cc1a71b4ede9a6c0.1778688803.git.u.kleine-koenig@baylibre.com Signed-off-by: Guenter Roeck --- drivers/hwmon/lm80.c | 4 ++-- drivers/hwmon/pmbus/aps-379.c | 4 ++-- drivers/hwmon/pmbus/lt3074.c | 4 ++-- drivers/hwmon/tsc1641.c | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/hwmon/lm80.c b/drivers/hwmon/lm80.c index 63c7831bd3e1..002c669182e1 100644 --- a/drivers/hwmon/lm80.c +++ b/drivers/hwmon/lm80.c @@ -622,8 +622,8 @@ static int lm80_probe(struct i2c_client *client) */ static const struct i2c_device_id lm80_id[] = { - { "lm80", 0 }, - { "lm96080", 1 }, + { "lm80" }, + { "lm96080" }, { } }; MODULE_DEVICE_TABLE(i2c, lm80_id); diff --git a/drivers/hwmon/pmbus/aps-379.c b/drivers/hwmon/pmbus/aps-379.c index 7d46cd647e20..3ec0940ae564 100644 --- a/drivers/hwmon/pmbus/aps-379.c +++ b/drivers/hwmon/pmbus/aps-379.c @@ -100,8 +100,8 @@ static struct pmbus_driver_info aps_379_info = { }; static const struct i2c_device_id aps_379_id[] = { - { "aps-379", 0 }, - {}, + { .name = "aps-379" }, + { } }; MODULE_DEVICE_TABLE(i2c, aps_379_id); diff --git a/drivers/hwmon/pmbus/lt3074.c b/drivers/hwmon/pmbus/lt3074.c index 3704dbe7b54a..ed932ddb4f77 100644 --- a/drivers/hwmon/pmbus/lt3074.c +++ b/drivers/hwmon/pmbus/lt3074.c @@ -95,8 +95,8 @@ static int lt3074_probe(struct i2c_client *client) } static const struct i2c_device_id lt3074_id[] = { - { "lt3074", 0 }, - {} + { .name = "lt3074" }, + { } }; MODULE_DEVICE_TABLE(i2c, lt3074_id); diff --git a/drivers/hwmon/tsc1641.c b/drivers/hwmon/tsc1641.c index 2b5d34bab146..fc53cd5bb6e0 100644 --- a/drivers/hwmon/tsc1641.c +++ b/drivers/hwmon/tsc1641.c @@ -721,7 +721,7 @@ static int tsc1641_probe(struct i2c_client *client) } static const struct i2c_device_id tsc1641_id[] = { - { "tsc1641", 0 }, + { .name = "tsc1641" }, { } }; MODULE_DEVICE_TABLE(i2c, tsc1641_id); From 4a9e6a9230c88a71916301b9fe6627413ebc2574 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig=20=28The=20Capable=20Hub=29?= Date: Wed, 13 May 2026 18:44:58 +0200 Subject: [PATCH 27/81] hwmon: Use named initializers for arrays of i2c_device_data MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While being less compact, using named initializers allows to more easily see which members of the structs are assigned which value without having to lookup the declaration of the struct. And it's also more robust against changes to the struct definition. The mentioned robustness is relevant for a planned change to struct i2c_device_id that replaces .driver_data by an anonymous union. While touching all these arrays, unify indention and usage of commas. This patch doesn't modify the compiled arrays, only their representation in source form benefits. The former was confirmed with x86 and arm64 builds. Signed-off-by: Uwe Kleine-König (The Capable Hub) Link: https://lore.kernel.org/r/65b77bcd452752c36d866069cc5790b26d2bf8dc.1778688803.git.u.kleine-koenig@baylibre.com Signed-off-by: Guenter Roeck --- drivers/hwmon/ad7414.c | 4 +- drivers/hwmon/ad7418.c | 6 +- drivers/hwmon/adc128d818.c | 2 +- drivers/hwmon/adm1025.c | 4 +- drivers/hwmon/adm1026.c | 2 +- drivers/hwmon/adm1029.c | 2 +- drivers/hwmon/adm1031.c | 4 +- drivers/hwmon/adm1177.c | 4 +- drivers/hwmon/adm9240.c | 6 +- drivers/hwmon/ads7828.c | 4 +- drivers/hwmon/adt7410.c | 8 +-- drivers/hwmon/adt7411.c | 2 +- drivers/hwmon/adt7462.c | 2 +- drivers/hwmon/adt7470.c | 2 +- drivers/hwmon/adt7475.c | 8 +-- drivers/hwmon/aht10.c | 8 +-- drivers/hwmon/amc6821.c | 2 +- drivers/hwmon/asb100.c | 2 +- drivers/hwmon/asc7621.c | 6 +- drivers/hwmon/atxp1.c | 2 +- drivers/hwmon/chipcap2.c | 16 ++--- drivers/hwmon/dme1737.c | 4 +- drivers/hwmon/ds1621.c | 10 +-- drivers/hwmon/ds620.c | 4 +- drivers/hwmon/emc1403.c | 24 +++---- drivers/hwmon/emc2103.c | 2 +- drivers/hwmon/emc2305.c | 8 +-- drivers/hwmon/emc6w201.c | 2 +- drivers/hwmon/f75375s.c | 6 +- drivers/hwmon/fschmd.c | 14 ++-- drivers/hwmon/ftsteutates.c | 2 +- drivers/hwmon/g760a.c | 2 +- drivers/hwmon/g762.c | 6 +- drivers/hwmon/gl518sm.c | 2 +- drivers/hwmon/gl520sm.c | 2 +- drivers/hwmon/hih6130.c | 2 +- drivers/hwmon/hs3001.c | 4 +- drivers/hwmon/htu31.c | 2 +- drivers/hwmon/ina209.c | 2 +- drivers/hwmon/ina238.c | 12 ++-- drivers/hwmon/ina2xx.c | 16 ++--- drivers/hwmon/ina3221.c | 2 +- drivers/hwmon/isl28022.c | 2 +- drivers/hwmon/jc42.c | 2 +- drivers/hwmon/lineage-pem.c | 4 +- drivers/hwmon/lm63.c | 6 +- drivers/hwmon/lm73.c | 2 +- drivers/hwmon/lm75.c | 62 ++++++++-------- drivers/hwmon/lm77.c | 2 +- drivers/hwmon/lm78.c | 4 +- drivers/hwmon/lm80.c | 4 +- drivers/hwmon/lm83.c | 4 +- drivers/hwmon/lm85.c | 24 +++---- drivers/hwmon/lm87.c | 4 +- drivers/hwmon/lm90.c | 96 ++++++++++++------------- drivers/hwmon/lm92.c | 6 +- drivers/hwmon/lm93.c | 4 +- drivers/hwmon/lm95234.c | 4 +- drivers/hwmon/lm95241.c | 4 +- drivers/hwmon/lm95245.c | 4 +- drivers/hwmon/ltc2945.c | 2 +- drivers/hwmon/ltc2947-i2c.c | 4 +- drivers/hwmon/ltc2990.c | 4 +- drivers/hwmon/ltc2991.c | 4 +- drivers/hwmon/ltc2992.c | 4 +- drivers/hwmon/ltc4151.c | 2 +- drivers/hwmon/ltc4215.c | 2 +- drivers/hwmon/ltc4222.c | 2 +- drivers/hwmon/ltc4245.c | 2 +- drivers/hwmon/ltc4260.c | 2 +- drivers/hwmon/ltc4261.c | 4 +- drivers/hwmon/max127.c | 2 +- drivers/hwmon/max16065.c | 12 ++-- drivers/hwmon/max1619.c | 2 +- drivers/hwmon/max1668.c | 6 +- drivers/hwmon/max31730.c | 2 +- drivers/hwmon/max31760.c | 2 +- drivers/hwmon/max31790.c | 2 +- drivers/hwmon/max31827.c | 6 +- drivers/hwmon/max6620.c | 2 +- drivers/hwmon/max6621.c | 2 +- drivers/hwmon/max6639.c | 2 +- drivers/hwmon/max6650.c | 4 +- drivers/hwmon/max6697.c | 20 +++--- drivers/hwmon/mc34vr500.c | 4 +- drivers/hwmon/mcp3021.c | 4 +- drivers/hwmon/nct6775-i2c.c | 26 +++---- drivers/hwmon/nct7802.c | 2 +- drivers/hwmon/nct7904.c | 4 +- drivers/hwmon/pcf8591.c | 2 +- drivers/hwmon/pmbus/acbel-fsg032.c | 4 +- drivers/hwmon/pmbus/adm1266.c | 2 +- drivers/hwmon/pmbus/adm1275.c | 20 +++--- drivers/hwmon/pmbus/bel-pfe.c | 6 +- drivers/hwmon/pmbus/bpa-rs600.c | 6 +- drivers/hwmon/pmbus/crps.c | 4 +- drivers/hwmon/pmbus/delta-ahe50dc-fan.c | 2 +- drivers/hwmon/pmbus/dps920ab.c | 4 +- drivers/hwmon/pmbus/fsp-3y.c | 4 +- drivers/hwmon/pmbus/hac300s.c | 4 +- drivers/hwmon/pmbus/ibm-cffps.c | 8 +-- drivers/hwmon/pmbus/ina233.c | 4 +- drivers/hwmon/pmbus/inspur-ipsps.c | 4 +- drivers/hwmon/pmbus/ir35221.c | 4 +- drivers/hwmon/pmbus/ir36021.c | 4 +- drivers/hwmon/pmbus/ir38064.c | 10 +-- drivers/hwmon/pmbus/irps5401.c | 4 +- drivers/hwmon/pmbus/isl68137.c | 92 ++++++++++++------------ drivers/hwmon/pmbus/lm25066.c | 10 +-- drivers/hwmon/pmbus/lt7182s.c | 4 +- drivers/hwmon/pmbus/ltc2978.c | 60 ++++++++-------- drivers/hwmon/pmbus/ltc3815.c | 2 +- drivers/hwmon/pmbus/max15301.c | 8 +-- drivers/hwmon/pmbus/max16064.c | 4 +- drivers/hwmon/pmbus/max16601.c | 10 +-- drivers/hwmon/pmbus/max17616.c | 2 +- drivers/hwmon/pmbus/max20730.c | 10 +-- drivers/hwmon/pmbus/max20751.c | 4 +- drivers/hwmon/pmbus/max31785.c | 8 +-- drivers/hwmon/pmbus/max34440.c | 18 ++--- drivers/hwmon/pmbus/max8688.c | 2 +- drivers/hwmon/pmbus/mp2856.c | 6 +- drivers/hwmon/pmbus/mp2888.c | 4 +- drivers/hwmon/pmbus/mp2891.c | 2 +- drivers/hwmon/pmbus/mp2925.c | 6 +- drivers/hwmon/pmbus/mp29502.c | 4 +- drivers/hwmon/pmbus/mp2975.c | 8 +-- drivers/hwmon/pmbus/mp2993.c | 2 +- drivers/hwmon/pmbus/mp5920.c | 2 +- drivers/hwmon/pmbus/mp5926.c | 4 +- drivers/hwmon/pmbus/mp5990.c | 4 +- drivers/hwmon/pmbus/mp9941.c | 2 +- drivers/hwmon/pmbus/mp9945.c | 4 +- drivers/hwmon/pmbus/mpq7932.c | 6 +- drivers/hwmon/pmbus/mpq8785.c | 10 +-- drivers/hwmon/pmbus/pim4328.c | 18 ++--- drivers/hwmon/pmbus/pli1209bc.c | 4 +- drivers/hwmon/pmbus/pm6764tr.c | 4 +- drivers/hwmon/pmbus/pmbus.c | 60 ++++++++-------- drivers/hwmon/pmbus/pxe1610.c | 8 +-- drivers/hwmon/pmbus/q54sj108a2.c | 8 +-- drivers/hwmon/pmbus/stef48h28.c | 4 +- drivers/hwmon/pmbus/stpddc60.c | 6 +- drivers/hwmon/pmbus/tda38640.c | 4 +- drivers/hwmon/pmbus/tps25990.c | 4 +- drivers/hwmon/pmbus/tps40422.c | 4 +- drivers/hwmon/pmbus/tps53679.c | 18 ++--- drivers/hwmon/pmbus/tps546d24.c | 4 +- drivers/hwmon/pmbus/ucd9000.c | 16 ++--- drivers/hwmon/pmbus/ucd9200.c | 18 ++--- drivers/hwmon/pmbus/xdp710.c | 2 +- drivers/hwmon/pmbus/xdp720.c | 4 +- drivers/hwmon/pmbus/xdpe12284.c | 8 +-- drivers/hwmon/pmbus/xdpe152c4.c | 6 +- drivers/hwmon/pmbus/xdpe1a2g7b.c | 6 +- drivers/hwmon/pmbus/zl6100.c | 44 ++++++------ drivers/hwmon/powr1220.c | 4 +- drivers/hwmon/pt5161l.c | 4 +- drivers/hwmon/sbtsi_temp.c | 4 +- drivers/hwmon/sg2042-mcu.c | 2 +- drivers/hwmon/sht21.c | 6 +- drivers/hwmon/sht3x.c | 8 +-- drivers/hwmon/sht4x.c | 4 +- drivers/hwmon/shtc1.c | 6 +- drivers/hwmon/smsc47m192.c | 2 +- drivers/hwmon/spd5118.c | 2 +- drivers/hwmon/stts751.c | 2 +- drivers/hwmon/tc654.c | 6 +- drivers/hwmon/tc74.c | 4 +- drivers/hwmon/thmc50.c | 4 +- drivers/hwmon/tmp102.c | 2 +- drivers/hwmon/tmp103.c | 2 +- drivers/hwmon/tmp108.c | 8 +-- drivers/hwmon/tmp401.c | 10 +-- drivers/hwmon/tmp421.c | 10 +-- drivers/hwmon/tmp464.c | 4 +- drivers/hwmon/tmp513.c | 4 +- drivers/hwmon/w83773g.c | 2 +- drivers/hwmon/w83781d.c | 8 +-- drivers/hwmon/w83791d.c | 2 +- drivers/hwmon/w83792d.c | 2 +- drivers/hwmon/w83793.c | 2 +- drivers/hwmon/w83795.c | 4 +- drivers/hwmon/w83l785ts.c | 2 +- drivers/hwmon/w83l786ng.c | 2 +- 185 files changed, 682 insertions(+), 682 deletions(-) diff --git a/drivers/hwmon/ad7414.c b/drivers/hwmon/ad7414.c index f0b17e59827f..c2df631ae93a 100644 --- a/drivers/hwmon/ad7414.c +++ b/drivers/hwmon/ad7414.c @@ -205,8 +205,8 @@ static int ad7414_probe(struct i2c_client *client) } static const struct i2c_device_id ad7414_id[] = { - { "ad7414" }, - {} + { .name = "ad7414" }, + { } }; MODULE_DEVICE_TABLE(i2c, ad7414_id); diff --git a/drivers/hwmon/ad7418.c b/drivers/hwmon/ad7418.c index 7a132accdf8a..a0c9cf5e12cc 100644 --- a/drivers/hwmon/ad7418.c +++ b/drivers/hwmon/ad7418.c @@ -281,9 +281,9 @@ static int ad7418_probe(struct i2c_client *client) } static const struct i2c_device_id ad7418_id[] = { - { "ad7416", ad7416 }, - { "ad7417", ad7417 }, - { "ad7418", ad7418 }, + { .name = "ad7416", .driver_data = ad7416 }, + { .name = "ad7417", .driver_data = ad7417 }, + { .name = "ad7418", .driver_data = ad7418 }, { } }; MODULE_DEVICE_TABLE(i2c, ad7418_id); diff --git a/drivers/hwmon/adc128d818.c b/drivers/hwmon/adc128d818.c index 5e805d4ee76a..e45adc0b84d1 100644 --- a/drivers/hwmon/adc128d818.c +++ b/drivers/hwmon/adc128d818.c @@ -480,7 +480,7 @@ static int adc128_probe(struct i2c_client *client) } static const struct i2c_device_id adc128_id[] = { - { "adc128d818" }, + { .name = "adc128d818" }, { } }; MODULE_DEVICE_TABLE(i2c, adc128_id); diff --git a/drivers/hwmon/adm1025.c b/drivers/hwmon/adm1025.c index 389382d54752..ccac7ba601e9 100644 --- a/drivers/hwmon/adm1025.c +++ b/drivers/hwmon/adm1025.c @@ -548,8 +548,8 @@ static int adm1025_probe(struct i2c_client *client) } static const struct i2c_device_id adm1025_id[] = { - { "adm1025", adm1025 }, - { "ne1619", ne1619 }, + { .name = "adm1025", .driver_data = adm1025 }, + { .name = "ne1619", .driver_data = ne1619 }, { } }; MODULE_DEVICE_TABLE(i2c, adm1025_id); diff --git a/drivers/hwmon/adm1026.c b/drivers/hwmon/adm1026.c index c38c932e5d2a..3ad82c01f81d 100644 --- a/drivers/hwmon/adm1026.c +++ b/drivers/hwmon/adm1026.c @@ -1857,7 +1857,7 @@ static int adm1026_probe(struct i2c_client *client) } static const struct i2c_device_id adm1026_id[] = { - { "adm1026" }, + { .name = "adm1026" }, { } }; MODULE_DEVICE_TABLE(i2c, adm1026_id); diff --git a/drivers/hwmon/adm1029.c b/drivers/hwmon/adm1029.c index 71eea8ae51b9..6cb0a238e059 100644 --- a/drivers/hwmon/adm1029.c +++ b/drivers/hwmon/adm1029.c @@ -382,7 +382,7 @@ static int adm1029_probe(struct i2c_client *client) } static const struct i2c_device_id adm1029_id[] = { - { "adm1029" }, + { .name = "adm1029" }, { } }; MODULE_DEVICE_TABLE(i2c, adm1029_id); diff --git a/drivers/hwmon/adm1031.c b/drivers/hwmon/adm1031.c index 343118532cdb..24d0d4146c87 100644 --- a/drivers/hwmon/adm1031.c +++ b/drivers/hwmon/adm1031.c @@ -1055,8 +1055,8 @@ static int adm1031_probe(struct i2c_client *client) } static const struct i2c_device_id adm1031_id[] = { - { "adm1030", adm1030 }, - { "adm1031", adm1031 }, + { .name = "adm1030", .driver_data = adm1030 }, + { .name = "adm1031", .driver_data = adm1031 }, { } }; MODULE_DEVICE_TABLE(i2c, adm1031_id); diff --git a/drivers/hwmon/adm1177.c b/drivers/hwmon/adm1177.c index 7888afe8dafd..dc4d8a214d1b 100644 --- a/drivers/hwmon/adm1177.c +++ b/drivers/hwmon/adm1177.c @@ -246,8 +246,8 @@ static int adm1177_probe(struct i2c_client *client) } static const struct i2c_device_id adm1177_id[] = { - {"adm1177"}, - {} + { .name = "adm1177" }, + { } }; MODULE_DEVICE_TABLE(i2c, adm1177_id); diff --git a/drivers/hwmon/adm9240.c b/drivers/hwmon/adm9240.c index 86f6044b5bd0..586650e8d043 100644 --- a/drivers/hwmon/adm9240.c +++ b/drivers/hwmon/adm9240.c @@ -794,9 +794,9 @@ static int adm9240_probe(struct i2c_client *client) } static const struct i2c_device_id adm9240_id[] = { - { "adm9240", adm9240 }, - { "ds1780", ds1780 }, - { "lm81", lm81 }, + { .name = "adm9240", .driver_data = adm9240 }, + { .name = "ds1780", .driver_data = ds1780 }, + { .name = "lm81", .driver_data = lm81 }, { } }; MODULE_DEVICE_TABLE(i2c, adm9240_id); diff --git a/drivers/hwmon/ads7828.c b/drivers/hwmon/ads7828.c index 7f43565ca284..149cfcec78dc 100644 --- a/drivers/hwmon/ads7828.c +++ b/drivers/hwmon/ads7828.c @@ -176,8 +176,8 @@ static int ads7828_probe(struct i2c_client *client) } static const struct i2c_device_id ads7828_device_ids[] = { - { "ads7828", ads7828 }, - { "ads7830", ads7830 }, + { .name = "ads7828", .driver_data = ads7828 }, + { .name = "ads7830", .driver_data = ads7830 }, { } }; MODULE_DEVICE_TABLE(i2c, ads7828_device_ids); diff --git a/drivers/hwmon/adt7410.c b/drivers/hwmon/adt7410.c index 73b196a78f3a..0aa7ce0a04be 100644 --- a/drivers/hwmon/adt7410.c +++ b/drivers/hwmon/adt7410.c @@ -89,10 +89,10 @@ static int adt7410_i2c_probe(struct i2c_client *client) } static const struct i2c_device_id adt7410_ids[] = { - { "adt7410" }, - { "adt7420" }, - { "adt7422" }, - {} + { .name = "adt7410" }, + { .name = "adt7420" }, + { .name = "adt7422" }, + { } }; MODULE_DEVICE_TABLE(i2c, adt7410_ids); diff --git a/drivers/hwmon/adt7411.c b/drivers/hwmon/adt7411.c index b9991a69e6c6..5dce2a701725 100644 --- a/drivers/hwmon/adt7411.c +++ b/drivers/hwmon/adt7411.c @@ -670,7 +670,7 @@ static int adt7411_probe(struct i2c_client *client) } static const struct i2c_device_id adt7411_id[] = { - { "adt7411" }, + { .name = "adt7411" }, { } }; MODULE_DEVICE_TABLE(i2c, adt7411_id); diff --git a/drivers/hwmon/adt7462.c b/drivers/hwmon/adt7462.c index 174dfee47f7a..5101d30ed9c6 100644 --- a/drivers/hwmon/adt7462.c +++ b/drivers/hwmon/adt7462.c @@ -1809,7 +1809,7 @@ static int adt7462_probe(struct i2c_client *client) } static const struct i2c_device_id adt7462_id[] = { - { "adt7462" }, + { .name = "adt7462" }, { } }; MODULE_DEVICE_TABLE(i2c, adt7462_id); diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c index dbee6926fa05..664349756dc2 100644 --- a/drivers/hwmon/adt7470.c +++ b/drivers/hwmon/adt7470.c @@ -1296,7 +1296,7 @@ static void adt7470_remove(struct i2c_client *client) } static const struct i2c_device_id adt7470_id[] = { - { "adt7470" }, + { .name = "adt7470" }, { } }; MODULE_DEVICE_TABLE(i2c, adt7470_id); diff --git a/drivers/hwmon/adt7475.c b/drivers/hwmon/adt7475.c index 8cefa14e1633..7fb96f5395fa 100644 --- a/drivers/hwmon/adt7475.c +++ b/drivers/hwmon/adt7475.c @@ -165,10 +165,10 @@ static const unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, I2C_CLIENT_END }; enum chips { adt7473, adt7475, adt7476, adt7490 }; static const struct i2c_device_id adt7475_id[] = { - { "adt7473", adt7473 }, - { "adt7475", adt7475 }, - { "adt7476", adt7476 }, - { "adt7490", adt7490 }, + { .name = "adt7473", .driver_data = adt7473 }, + { .name = "adt7475", .driver_data = adt7475 }, + { .name = "adt7476", .driver_data = adt7476 }, + { .name = "adt7490", .driver_data = adt7490 }, { } }; MODULE_DEVICE_TABLE(i2c, adt7475_id); diff --git a/drivers/hwmon/aht10.c b/drivers/hwmon/aht10.c index 66955395d058..6c263cb57766 100644 --- a/drivers/hwmon/aht10.c +++ b/drivers/hwmon/aht10.c @@ -55,10 +55,10 @@ enum aht10_variant { aht10, aht20, dht20}; static const struct i2c_device_id aht10_id[] = { - { "aht10", aht10 }, - { "aht20", aht20 }, - { "dht20", dht20 }, - { }, + { .name = "aht10", .driver_data = aht10 }, + { .name = "aht20", .driver_data = aht20 }, + { .name = "dht20", .driver_data = dht20 }, + { } }; MODULE_DEVICE_TABLE(i2c, aht10_id); diff --git a/drivers/hwmon/amc6821.c b/drivers/hwmon/amc6821.c index d5f864b360b0..bbc0542a7d38 100644 --- a/drivers/hwmon/amc6821.c +++ b/drivers/hwmon/amc6821.c @@ -1083,7 +1083,7 @@ static int amc6821_probe(struct i2c_client *client) } static const struct i2c_device_id amc6821_id[] = { - { "amc6821" }, + { .name = "amc6821" }, { } }; diff --git a/drivers/hwmon/asb100.c b/drivers/hwmon/asb100.c index 14e7737866c2..5f5c94505166 100644 --- a/drivers/hwmon/asb100.c +++ b/drivers/hwmon/asb100.c @@ -213,7 +213,7 @@ static struct asb100_data *asb100_update_device(struct device *dev); static void asb100_init_client(struct i2c_client *client); static const struct i2c_device_id asb100_id[] = { - { "asb100" }, + { .name = "asb100" }, { } }; MODULE_DEVICE_TABLE(i2c, asb100_id); diff --git a/drivers/hwmon/asc7621.c b/drivers/hwmon/asc7621.c index 87e186700849..e8145bb13ab3 100644 --- a/drivers/hwmon/asc7621.c +++ b/drivers/hwmon/asc7621.c @@ -1179,9 +1179,9 @@ static void asc7621_remove(struct i2c_client *client) } static const struct i2c_device_id asc7621_id[] = { - {"asc7621", asc7621}, - {"asc7621a", asc7621a}, - {}, + { .name = "asc7621", .driver_data = asc7621 }, + { .name = "asc7621a", .driver_data = asc7621a }, + { } }; MODULE_DEVICE_TABLE(i2c, asc7621_id); diff --git a/drivers/hwmon/atxp1.c b/drivers/hwmon/atxp1.c index 1c7e9a98b757..f5de4894f0a2 100644 --- a/drivers/hwmon/atxp1.c +++ b/drivers/hwmon/atxp1.c @@ -278,7 +278,7 @@ static int atxp1_probe(struct i2c_client *client) }; static const struct i2c_device_id atxp1_id[] = { - { "atxp1" }, + { .name = "atxp1" }, { } }; MODULE_DEVICE_TABLE(i2c, atxp1_id); diff --git a/drivers/hwmon/chipcap2.c b/drivers/hwmon/chipcap2.c index 645b8c2e704e..4aecf463180f 100644 --- a/drivers/hwmon/chipcap2.c +++ b/drivers/hwmon/chipcap2.c @@ -736,14 +736,14 @@ static void cc2_remove(struct i2c_client *client) } static const struct i2c_device_id cc2_id[] = { - { "cc2d23" }, - { "cc2d23s" }, - { "cc2d25" }, - { "cc2d25s" }, - { "cc2d33" }, - { "cc2d33s" }, - { "cc2d35" }, - { "cc2d35s" }, + { .name = "cc2d23" }, + { .name = "cc2d23s" }, + { .name = "cc2d25" }, + { .name = "cc2d25s" }, + { .name = "cc2d33" }, + { .name = "cc2d33s" }, + { .name = "cc2d35" }, + { .name = "cc2d35s" }, { } }; MODULE_DEVICE_TABLE(i2c, cc2_id); diff --git a/drivers/hwmon/dme1737.c b/drivers/hwmon/dme1737.c index 3d4057309950..7e839308e58f 100644 --- a/drivers/hwmon/dme1737.c +++ b/drivers/hwmon/dme1737.c @@ -2515,8 +2515,8 @@ static void dme1737_i2c_remove(struct i2c_client *client) } static const struct i2c_device_id dme1737_id[] = { - { "dme1737", dme1737 }, - { "sch5027", sch5027 }, + { .name = "dme1737", .driver_data = dme1737 }, + { .name = "sch5027", .driver_data = sch5027 }, { } }; MODULE_DEVICE_TABLE(i2c, dme1737_id); diff --git a/drivers/hwmon/ds1621.c b/drivers/hwmon/ds1621.c index 42ec34cb8a5f..0618f6de9679 100644 --- a/drivers/hwmon/ds1621.c +++ b/drivers/hwmon/ds1621.c @@ -367,11 +367,11 @@ static int ds1621_probe(struct i2c_client *client) } static const struct i2c_device_id ds1621_id[] = { - { "ds1621", ds1621 }, - { "ds1625", ds1625 }, - { "ds1631", ds1631 }, - { "ds1721", ds1721 }, - { "ds1731", ds1731 }, + { .name = "ds1621", .driver_data = ds1621 }, + { .name = "ds1625", .driver_data = ds1625 }, + { .name = "ds1631", .driver_data = ds1631 }, + { .name = "ds1721", .driver_data = ds1721 }, + { .name = "ds1731", .driver_data = ds1731 }, { } }; MODULE_DEVICE_TABLE(i2c, ds1621_id); diff --git a/drivers/hwmon/ds620.c b/drivers/hwmon/ds620.c index ce397042d90b..25287f0fa4c2 100644 --- a/drivers/hwmon/ds620.c +++ b/drivers/hwmon/ds620.c @@ -233,8 +233,8 @@ static int ds620_probe(struct i2c_client *client) } static const struct i2c_device_id ds620_id[] = { - {"ds620"}, - {} + { .name = "ds620" }, + { } }; MODULE_DEVICE_TABLE(i2c, ds620_id); diff --git a/drivers/hwmon/emc1403.c b/drivers/hwmon/emc1403.c index 964a8cb278f1..cd753b38709f 100644 --- a/drivers/hwmon/emc1403.c +++ b/drivers/hwmon/emc1403.c @@ -639,18 +639,18 @@ static const struct hwmon_chip_info emc1403_chip_info = { /* Last digit of chip name indicates number of channels */ static const struct i2c_device_id emc1403_idtable[] = { - { "emc1402", emc1402 }, - { "emc1403", emc1403 }, - { "emc1404", emc1404 }, - { "emc1412", emc1402 }, - { "emc1413", emc1403 }, - { "emc1414", emc1404 }, - { "emc1422", emc1402 }, - { "emc1423", emc1403 }, - { "emc1424", emc1404 }, - { "emc1428", emc1428 }, - { "emc1438", emc1428 }, - { "emc1442", emc1402 }, + { .name = "emc1402", .driver_data = emc1402 }, + { .name = "emc1403", .driver_data = emc1403 }, + { .name = "emc1404", .driver_data = emc1404 }, + { .name = "emc1412", .driver_data = emc1402 }, + { .name = "emc1413", .driver_data = emc1403 }, + { .name = "emc1414", .driver_data = emc1404 }, + { .name = "emc1422", .driver_data = emc1402 }, + { .name = "emc1423", .driver_data = emc1403 }, + { .name = "emc1424", .driver_data = emc1404 }, + { .name = "emc1428", .driver_data = emc1428 }, + { .name = "emc1438", .driver_data = emc1428 }, + { .name = "emc1442", .driver_data = emc1402 }, { } }; MODULE_DEVICE_TABLE(i2c, emc1403_idtable); diff --git a/drivers/hwmon/emc2103.c b/drivers/hwmon/emc2103.c index 9b8e925af030..27dc149a3ed9 100644 --- a/drivers/hwmon/emc2103.c +++ b/drivers/hwmon/emc2103.c @@ -624,7 +624,7 @@ emc2103_probe(struct i2c_client *client) } static const struct i2c_device_id emc2103_ids[] = { - { "emc2103" }, + { .name = "emc2103" }, { /* LIST END */ } }; MODULE_DEVICE_TABLE(i2c, emc2103_ids); diff --git a/drivers/hwmon/emc2305.c b/drivers/hwmon/emc2305.c index c5a0b2146478..cd7613e5e547 100644 --- a/drivers/hwmon/emc2305.c +++ b/drivers/hwmon/emc2305.c @@ -60,10 +60,10 @@ enum emc230x_product_id { }; static const struct i2c_device_id emc2305_ids[] = { - { "emc2305" }, - { "emc2303" }, - { "emc2302" }, - { "emc2301" }, + { .name = "emc2305" }, + { .name = "emc2303" }, + { .name = "emc2302" }, + { .name = "emc2301" }, { } }; MODULE_DEVICE_TABLE(i2c, emc2305_ids); diff --git a/drivers/hwmon/emc6w201.c b/drivers/hwmon/emc6w201.c index 1100c6e5daa7..c13ea8748683 100644 --- a/drivers/hwmon/emc6w201.c +++ b/drivers/hwmon/emc6w201.c @@ -464,7 +464,7 @@ static int emc6w201_probe(struct i2c_client *client) } static const struct i2c_device_id emc6w201_id[] = { - { "emc6w201" }, + { .name = "emc6w201" }, { } }; MODULE_DEVICE_TABLE(i2c, emc6w201_id); diff --git a/drivers/hwmon/f75375s.c b/drivers/hwmon/f75375s.c index 7e867f132420..cc3a9612f2c0 100644 --- a/drivers/hwmon/f75375s.c +++ b/drivers/hwmon/f75375s.c @@ -877,9 +877,9 @@ static int f75375_detect(struct i2c_client *client, } static const struct i2c_device_id f75375_id[] = { - { "f75373", f75373 }, - { "f75375", f75375 }, - { "f75387", f75387 }, + { .name = "f75373", .driver_data = f75373 }, + { .name = "f75375", .driver_data = f75375 }, + { .name = "f75387", .driver_data = f75387 }, { } }; MODULE_DEVICE_TABLE(i2c, f75375_id); diff --git a/drivers/hwmon/fschmd.c b/drivers/hwmon/fschmd.c index 1211fa2259e5..019fc32bf318 100644 --- a/drivers/hwmon/fschmd.c +++ b/drivers/hwmon/fschmd.c @@ -225,13 +225,13 @@ static struct fschmd_data *fschmd_update_device(struct device *dev); */ static const struct i2c_device_id fschmd_id[] = { - { "fscpos", fscpos }, - { "fscher", fscher }, - { "fscscy", fscscy }, - { "fschrc", fschrc }, - { "fschmd", fschmd }, - { "fschds", fschds }, - { "fscsyl", fscsyl }, + { .name = "fscpos", .driver_data = fscpos }, + { .name = "fscher", .driver_data = fscher }, + { .name = "fscscy", .driver_data = fscscy }, + { .name = "fschrc", .driver_data = fschrc }, + { .name = "fschmd", .driver_data = fschmd }, + { .name = "fschds", .driver_data = fschds }, + { .name = "fscsyl", .driver_data = fscsyl }, { } }; MODULE_DEVICE_TABLE(i2c, fschmd_id); diff --git a/drivers/hwmon/ftsteutates.c b/drivers/hwmon/ftsteutates.c index 08dcc6a7fb62..06be120a349d 100644 --- a/drivers/hwmon/ftsteutates.c +++ b/drivers/hwmon/ftsteutates.c @@ -49,7 +49,7 @@ static const unsigned short normal_i2c[] = { 0x73, I2C_CLIENT_END }; static const struct i2c_device_id fts_id[] = { - { "ftsteutates" }, + { .name = "ftsteutates" }, { } }; MODULE_DEVICE_TABLE(i2c, fts_id); diff --git a/drivers/hwmon/g760a.c b/drivers/hwmon/g760a.c index 39ae8f826417..e2166ee76286 100644 --- a/drivers/hwmon/g760a.c +++ b/drivers/hwmon/g760a.c @@ -197,7 +197,7 @@ static int g760a_probe(struct i2c_client *client) } static const struct i2c_device_id g760a_id[] = { - { "g760a" }, + { .name = "g760a" }, { } }; MODULE_DEVICE_TABLE(i2c, g760a_id); diff --git a/drivers/hwmon/g762.c b/drivers/hwmon/g762.c index 4fa3aa1271da..407cf7af4fdd 100644 --- a/drivers/hwmon/g762.c +++ b/drivers/hwmon/g762.c @@ -44,9 +44,9 @@ #define DRVNAME "g762" static const struct i2c_device_id g762_id[] = { - { "g761" }, - { "g762" }, - { "g763" }, + { .name = "g761" }, + { .name = "g762" }, + { .name = "g763" }, { } }; MODULE_DEVICE_TABLE(i2c, g762_id); diff --git a/drivers/hwmon/gl518sm.c b/drivers/hwmon/gl518sm.c index 9c68bc013950..742c130cec7f 100644 --- a/drivers/hwmon/gl518sm.c +++ b/drivers/hwmon/gl518sm.c @@ -642,7 +642,7 @@ static int gl518_probe(struct i2c_client *client) } static const struct i2c_device_id gl518_id[] = { - { "gl518sm" }, + { .name = "gl518sm" }, { } }; MODULE_DEVICE_TABLE(i2c, gl518_id); diff --git a/drivers/hwmon/gl520sm.c b/drivers/hwmon/gl520sm.c index 972f4f8caa2b..f4fe39912ee2 100644 --- a/drivers/hwmon/gl520sm.c +++ b/drivers/hwmon/gl520sm.c @@ -885,7 +885,7 @@ static int gl520_probe(struct i2c_client *client) } static const struct i2c_device_id gl520_id[] = { - { "gl520sm" }, + { .name = "gl520sm" }, { } }; MODULE_DEVICE_TABLE(i2c, gl520_id); diff --git a/drivers/hwmon/hih6130.c b/drivers/hwmon/hih6130.c index 85af8299150a..7984be1e706d 100644 --- a/drivers/hwmon/hih6130.c +++ b/drivers/hwmon/hih6130.c @@ -233,7 +233,7 @@ static int hih6130_probe(struct i2c_client *client) /* Device ID table */ static const struct i2c_device_id hih6130_id[] = { - { "hih6130" }, + { .name = "hih6130" }, { } }; MODULE_DEVICE_TABLE(i2c, hih6130_id); diff --git a/drivers/hwmon/hs3001.c b/drivers/hwmon/hs3001.c index 50c6c15f8b18..b263cbeca074 100644 --- a/drivers/hwmon/hs3001.c +++ b/drivers/hwmon/hs3001.c @@ -169,8 +169,8 @@ static const struct hwmon_chip_info hs3001_chip_info = { /* device ID table */ static const struct i2c_device_id hs3001_ids[] = { - { "hs3001" }, - { }, + { .name = "hs3001" }, + { } }; MODULE_DEVICE_TABLE(i2c, hs3001_ids); diff --git a/drivers/hwmon/htu31.c b/drivers/hwmon/htu31.c index 7521a371aa6c..5d2cdbdb773b 100644 --- a/drivers/hwmon/htu31.c +++ b/drivers/hwmon/htu31.c @@ -322,7 +322,7 @@ static int htu31_probe(struct i2c_client *client) } static const struct i2c_device_id htu31_id[] = { - { "htu31" }, + { .name = "htu31" }, { } }; MODULE_DEVICE_TABLE(i2c, htu31_id); diff --git a/drivers/hwmon/ina209.c b/drivers/hwmon/ina209.c index a116f1600e81..39aca2cdf27f 100644 --- a/drivers/hwmon/ina209.c +++ b/drivers/hwmon/ina209.c @@ -569,7 +569,7 @@ static void ina209_remove(struct i2c_client *client) } static const struct i2c_device_id ina209_id[] = { - { "ina209" }, + { .name = "ina209" }, { } }; MODULE_DEVICE_TABLE(i2c, ina209_id); diff --git a/drivers/hwmon/ina238.c b/drivers/hwmon/ina238.c index ff67b03189f7..c20dd164a6a6 100644 --- a/drivers/hwmon/ina238.c +++ b/drivers/hwmon/ina238.c @@ -837,12 +837,12 @@ static int ina238_probe(struct i2c_client *client) } static const struct i2c_device_id ina238_id[] = { - { "ina228", ina228 }, - { "ina237", ina237 }, - { "ina238", ina238 }, - { "ina700", ina700 }, - { "ina780", ina780 }, - { "sq52206", sq52206 }, + { .name = "ina228", .driver_data = ina228 }, + { .name = "ina237", .driver_data = ina237 }, + { .name = "ina238", .driver_data = ina238 }, + { .name = "ina700", .driver_data = ina700 }, + { .name = "ina780", .driver_data = ina780 }, + { .name = "sq52206", .driver_data = sq52206 }, { } }; MODULE_DEVICE_TABLE(i2c, ina238_id); diff --git a/drivers/hwmon/ina2xx.c b/drivers/hwmon/ina2xx.c index 613ffb622b7c..32bf4595bcb4 100644 --- a/drivers/hwmon/ina2xx.c +++ b/drivers/hwmon/ina2xx.c @@ -1000,14 +1000,14 @@ static int ina2xx_probe(struct i2c_client *client) } static const struct i2c_device_id ina2xx_id[] = { - { "ina219", ina219 }, - { "ina220", ina219 }, - { "ina226", ina226 }, - { "ina230", ina226 }, - { "ina231", ina226 }, - { "ina234", ina234 }, - { "ina260", ina260 }, - { "sy24655", sy24655 }, + { .name = "ina219", .driver_data = ina219 }, + { .name = "ina220", .driver_data = ina219 }, + { .name = "ina226", .driver_data = ina226 }, + { .name = "ina230", .driver_data = ina226 }, + { .name = "ina231", .driver_data = ina226 }, + { .name = "ina234", .driver_data = ina234 }, + { .name = "ina260", .driver_data = ina260 }, + { .name = "sy24655", .driver_data = sy24655 }, { } }; MODULE_DEVICE_TABLE(i2c, ina2xx_id); diff --git a/drivers/hwmon/ina3221.c b/drivers/hwmon/ina3221.c index 5ecc68dcf169..3ab5de3a1110 100644 --- a/drivers/hwmon/ina3221.c +++ b/drivers/hwmon/ina3221.c @@ -1002,7 +1002,7 @@ static const struct of_device_id ina3221_of_match_table[] = { MODULE_DEVICE_TABLE(of, ina3221_of_match_table); static const struct i2c_device_id ina3221_ids[] = { - { "ina3221" }, + { .name = "ina3221" }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(i2c, ina3221_ids); diff --git a/drivers/hwmon/isl28022.c b/drivers/hwmon/isl28022.c index 96fcfbfff48f..0c60d2ef28f5 100644 --- a/drivers/hwmon/isl28022.c +++ b/drivers/hwmon/isl28022.c @@ -475,7 +475,7 @@ static int isl28022_probe(struct i2c_client *client) } static const struct i2c_device_id isl28022_ids[] = { - { "isl28022" }, + { .name = "isl28022" }, { /* LIST END */ } }; MODULE_DEVICE_TABLE(i2c, isl28022_ids); diff --git a/drivers/hwmon/jc42.c b/drivers/hwmon/jc42.c index 6549dc543781..77fece680358 100644 --- a/drivers/hwmon/jc42.c +++ b/drivers/hwmon/jc42.c @@ -579,7 +579,7 @@ static const struct dev_pm_ops jc42_dev_pm_ops = { #endif /* CONFIG_PM */ static const struct i2c_device_id jc42_id[] = { - { "jc42" }, + { .name = "jc42" }, { } }; MODULE_DEVICE_TABLE(i2c, jc42_id); diff --git a/drivers/hwmon/lineage-pem.c b/drivers/hwmon/lineage-pem.c index 64a335a64a2e..2553e49f8401 100644 --- a/drivers/hwmon/lineage-pem.c +++ b/drivers/hwmon/lineage-pem.c @@ -502,8 +502,8 @@ static int pem_probe(struct i2c_client *client) } static const struct i2c_device_id pem_id[] = { - {"lineage_pem"}, - {} + { .name = "lineage_pem" }, + { } }; MODULE_DEVICE_TABLE(i2c, pem_id); diff --git a/drivers/hwmon/lm63.c b/drivers/hwmon/lm63.c index 30500b4d2221..a0d77a7386a9 100644 --- a/drivers/hwmon/lm63.c +++ b/drivers/hwmon/lm63.c @@ -1152,9 +1152,9 @@ static int lm63_probe(struct i2c_client *client) */ static const struct i2c_device_id lm63_id[] = { - { "lm63", lm63 }, - { "lm64", lm64 }, - { "lm96163", lm96163 }, + { .name = "lm63", .driver_data = lm63 }, + { .name = "lm64", .driver_data = lm64 }, + { .name = "lm96163", .driver_data = lm96163 }, { } }; MODULE_DEVICE_TABLE(i2c, lm63_id); diff --git a/drivers/hwmon/lm73.c b/drivers/hwmon/lm73.c index 581b01572e1b..63ee67481a16 100644 --- a/drivers/hwmon/lm73.c +++ b/drivers/hwmon/lm73.c @@ -220,7 +220,7 @@ lm73_probe(struct i2c_client *client) } static const struct i2c_device_id lm73_ids[] = { - { "lm73" }, + { .name = "lm73" }, { /* LIST END */ } }; MODULE_DEVICE_TABLE(i2c, lm73_ids); diff --git a/drivers/hwmon/lm75.c b/drivers/hwmon/lm75.c index 845a0e09ea9f..1e8af5805f2f 100644 --- a/drivers/hwmon/lm75.c +++ b/drivers/hwmon/lm75.c @@ -833,37 +833,37 @@ static int lm75_i2c_probe(struct i2c_client *client) } static const struct i2c_device_id lm75_i2c_ids[] = { - { "adt75", adt75, }, - { "as6200", as6200, }, - { "at30ts74", at30ts74, }, - { "ds1775", ds1775, }, - { "ds75", ds75, }, - { "ds7505", ds7505, }, - { "g751", g751, }, - { "lm75", lm75, }, - { "lm75a", lm75a, }, - { "lm75b", lm75b, }, - { "max6625", max6625, }, - { "max6626", max6626, }, - { "max31725", max31725, }, - { "max31726", max31725, }, - { "mcp980x", mcp980x, }, - { "p3t1750", p3t1750, }, - { "p3t1755", p3t1755, }, - { "pct2075", pct2075, }, - { "stds75", stds75, }, - { "stlm75", stlm75, }, - { "tcn75", tcn75, }, - { "tmp100", tmp100, }, - { "tmp101", tmp101, }, - { "tmp105", tmp105, }, - { "tmp112", tmp112, }, - { "tmp175", tmp175, }, - { "tmp275", tmp275, }, - { "tmp75", tmp75, }, - { "tmp75b", tmp75b, }, - { "tmp75c", tmp75c, }, - { "tmp1075", tmp1075, }, + { .name = "adt75", .driver_data = adt75 }, + { .name = "as6200", .driver_data = as6200 }, + { .name = "at30ts74", .driver_data = at30ts74 }, + { .name = "ds1775", .driver_data = ds1775 }, + { .name = "ds75", .driver_data = ds75 }, + { .name = "ds7505", .driver_data = ds7505 }, + { .name = "g751", .driver_data = g751 }, + { .name = "lm75", .driver_data = lm75 }, + { .name = "lm75a", .driver_data = lm75a }, + { .name = "lm75b", .driver_data = lm75b }, + { .name = "max6625", .driver_data = max6625 }, + { .name = "max6626", .driver_data = max6626 }, + { .name = "max31725", .driver_data = max31725 }, + { .name = "max31726", .driver_data = max31725 }, + { .name = "mcp980x", .driver_data = mcp980x }, + { .name = "p3t1750", .driver_data = p3t1750 }, + { .name = "p3t1755", .driver_data = p3t1755 }, + { .name = "pct2075", .driver_data = pct2075 }, + { .name = "stds75", .driver_data = stds75 }, + { .name = "stlm75", .driver_data = stlm75 }, + { .name = "tcn75", .driver_data = tcn75 }, + { .name = "tmp100", .driver_data = tmp100 }, + { .name = "tmp101", .driver_data = tmp101 }, + { .name = "tmp105", .driver_data = tmp105 }, + { .name = "tmp112", .driver_data = tmp112 }, + { .name = "tmp175", .driver_data = tmp175 }, + { .name = "tmp275", .driver_data = tmp275 }, + { .name = "tmp75", .driver_data = tmp75 }, + { .name = "tmp75b", .driver_data = tmp75b }, + { .name = "tmp75c", .driver_data = tmp75c }, + { .name = "tmp1075", .driver_data = tmp1075 }, { /* LIST END */ } }; MODULE_DEVICE_TABLE(i2c, lm75_i2c_ids); diff --git a/drivers/hwmon/lm77.c b/drivers/hwmon/lm77.c index 80f7a6a3f9a2..96c5c2584d37 100644 --- a/drivers/hwmon/lm77.c +++ b/drivers/hwmon/lm77.c @@ -337,7 +337,7 @@ static int lm77_probe(struct i2c_client *client) } static const struct i2c_device_id lm77_id[] = { - { "lm77" }, + { .name = "lm77" }, { } }; MODULE_DEVICE_TABLE(i2c, lm77_id); diff --git a/drivers/hwmon/lm78.c b/drivers/hwmon/lm78.c index 9378a47bf5af..e4834f9eca6a 100644 --- a/drivers/hwmon/lm78.c +++ b/drivers/hwmon/lm78.c @@ -649,8 +649,8 @@ static int lm78_i2c_probe(struct i2c_client *client) } static const struct i2c_device_id lm78_i2c_id[] = { - { "lm78", lm78 }, - { "lm79", lm79 }, + { .name = "lm78", .driver_data = lm78 }, + { .name = "lm79", .driver_data = lm79 }, { } }; MODULE_DEVICE_TABLE(i2c, lm78_i2c_id); diff --git a/drivers/hwmon/lm80.c b/drivers/hwmon/lm80.c index 002c669182e1..94f3dabaaa6f 100644 --- a/drivers/hwmon/lm80.c +++ b/drivers/hwmon/lm80.c @@ -622,8 +622,8 @@ static int lm80_probe(struct i2c_client *client) */ static const struct i2c_device_id lm80_id[] = { - { "lm80" }, - { "lm96080" }, + { .name = "lm80" }, + { .name = "lm96080" }, { } }; MODULE_DEVICE_TABLE(i2c, lm80_id); diff --git a/drivers/hwmon/lm83.c b/drivers/hwmon/lm83.c index f800fe2ef18b..8d49df8c9314 100644 --- a/drivers/hwmon/lm83.c +++ b/drivers/hwmon/lm83.c @@ -443,8 +443,8 @@ static int lm83_probe(struct i2c_client *client) */ static const struct i2c_device_id lm83_id[] = { - { "lm83", lm83 }, - { "lm82", lm82 }, + { .name = "lm83", .driver_data = lm83 }, + { .name = "lm82", .driver_data = lm82 }, { } }; MODULE_DEVICE_TABLE(i2c, lm83_id); diff --git a/drivers/hwmon/lm85.c b/drivers/hwmon/lm85.c index 1c244ed75122..c56e164d61c1 100644 --- a/drivers/hwmon/lm85.c +++ b/drivers/hwmon/lm85.c @@ -1618,18 +1618,18 @@ static int lm85_probe(struct i2c_client *client) } static const struct i2c_device_id lm85_id[] = { - { "adm1027", adm1027 }, - { "adt7463", adt7463 }, - { "adt7468", adt7468 }, - { "lm85", lm85 }, - { "lm85b", lm85 }, - { "lm85c", lm85 }, - { "lm96000", lm96000 }, - { "emc6d100", emc6d100 }, - { "emc6d101", emc6d100 }, - { "emc6d102", emc6d102 }, - { "emc6d103", emc6d103 }, - { "emc6d103s", emc6d103s }, + { .name = "adm1027", .driver_data = adm1027 }, + { .name = "adt7463", .driver_data = adt7463 }, + { .name = "adt7468", .driver_data = adt7468 }, + { .name = "lm85", .driver_data = lm85 }, + { .name = "lm85b", .driver_data = lm85 }, + { .name = "lm85c", .driver_data = lm85 }, + { .name = "lm96000", .driver_data = lm96000 }, + { .name = "emc6d100", .driver_data = emc6d100 }, + { .name = "emc6d101", .driver_data = emc6d100 }, + { .name = "emc6d102", .driver_data = emc6d102 }, + { .name = "emc6d103", .driver_data = emc6d103 }, + { .name = "emc6d103s", .driver_data = emc6d103s }, { } }; MODULE_DEVICE_TABLE(i2c, lm85_id); diff --git a/drivers/hwmon/lm87.c b/drivers/hwmon/lm87.c index 37bf2d1d3d09..c09074f44708 100644 --- a/drivers/hwmon/lm87.c +++ b/drivers/hwmon/lm87.c @@ -981,8 +981,8 @@ static int lm87_probe(struct i2c_client *client) */ static const struct i2c_device_id lm87_id[] = { - { "lm87" }, - { "adm1024" }, + { .name = "lm87" }, + { .name = "adm1024" }, { } }; MODULE_DEVICE_TABLE(i2c, lm87_id); diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c index 1eeb608e5903..c78c96e1bd83 100644 --- a/drivers/hwmon/lm90.c +++ b/drivers/hwmon/lm90.c @@ -243,54 +243,54 @@ enum chips { adm1023, adm1032, adt7461, adt7461a, adt7481, */ static const struct i2c_device_id lm90_id[] = { - { "adm1020", max1617 }, - { "adm1021", max1617 }, - { "adm1023", adm1023 }, - { "adm1032", adm1032 }, - { "adt7421", adt7461a }, - { "adt7461", adt7461 }, - { "adt7461a", adt7461a }, - { "adt7481", adt7481 }, - { "adt7482", adt7481 }, - { "adt7483a", adt7481 }, - { "g781", g781 }, - { "gl523sm", max1617 }, - { "lm84", lm84 }, - { "lm86", lm90 }, - { "lm89", lm90 }, - { "lm90", lm90 }, - { "lm99", lm99 }, - { "max1617", max1617 }, - { "max6642", max6642 }, - { "max6646", max6646 }, - { "max6647", max6646 }, - { "max6648", max6648 }, - { "max6649", max6646 }, - { "max6654", max6654 }, - { "max6657", max6657 }, - { "max6658", max6657 }, - { "max6659", max6659 }, - { "max6680", max6680 }, - { "max6681", max6680 }, - { "max6690", max6654 }, - { "max6692", max6648 }, - { "max6695", max6696 }, - { "max6696", max6696 }, - { "mc1066", max1617 }, - { "nct1008", adt7461a }, - { "nct210", nct210 }, - { "nct214", nct72 }, - { "nct218", nct72 }, - { "nct72", nct72 }, - { "nct7716", nct7716 }, - { "nct7717", nct7717 }, - { "nct7718", nct7718 }, - { "ne1618", ne1618 }, - { "w83l771", w83l771 }, - { "sa56004", sa56004 }, - { "thmc10", max1617 }, - { "tmp451", tmp451 }, - { "tmp461", tmp461 }, + { .name = "adm1020", .driver_data = max1617 }, + { .name = "adm1021", .driver_data = max1617 }, + { .name = "adm1023", .driver_data = adm1023 }, + { .name = "adm1032", .driver_data = adm1032 }, + { .name = "adt7421", .driver_data = adt7461a }, + { .name = "adt7461", .driver_data = adt7461 }, + { .name = "adt7461a", .driver_data = adt7461a }, + { .name = "adt7481", .driver_data = adt7481 }, + { .name = "adt7482", .driver_data = adt7481 }, + { .name = "adt7483a", .driver_data = adt7481 }, + { .name = "g781", .driver_data = g781 }, + { .name = "gl523sm", .driver_data = max1617 }, + { .name = "lm84", .driver_data = lm84 }, + { .name = "lm86", .driver_data = lm90 }, + { .name = "lm89", .driver_data = lm90 }, + { .name = "lm90", .driver_data = lm90 }, + { .name = "lm99", .driver_data = lm99 }, + { .name = "max1617", .driver_data = max1617 }, + { .name = "max6642", .driver_data = max6642 }, + { .name = "max6646", .driver_data = max6646 }, + { .name = "max6647", .driver_data = max6646 }, + { .name = "max6648", .driver_data = max6648 }, + { .name = "max6649", .driver_data = max6646 }, + { .name = "max6654", .driver_data = max6654 }, + { .name = "max6657", .driver_data = max6657 }, + { .name = "max6658", .driver_data = max6657 }, + { .name = "max6659", .driver_data = max6659 }, + { .name = "max6680", .driver_data = max6680 }, + { .name = "max6681", .driver_data = max6680 }, + { .name = "max6690", .driver_data = max6654 }, + { .name = "max6692", .driver_data = max6648 }, + { .name = "max6695", .driver_data = max6696 }, + { .name = "max6696", .driver_data = max6696 }, + { .name = "mc1066", .driver_data = max1617 }, + { .name = "nct1008", .driver_data = adt7461a }, + { .name = "nct210", .driver_data = nct210 }, + { .name = "nct214", .driver_data = nct72 }, + { .name = "nct218", .driver_data = nct72 }, + { .name = "nct72", .driver_data = nct72 }, + { .name = "nct7716", .driver_data = nct7716 }, + { .name = "nct7717", .driver_data = nct7717 }, + { .name = "nct7718", .driver_data = nct7718 }, + { .name = "ne1618", .driver_data = ne1618 }, + { .name = "w83l771", .driver_data = w83l771 }, + { .name = "sa56004", .driver_data = sa56004 }, + { .name = "thmc10", .driver_data = max1617 }, + { .name = "tmp451", .driver_data = tmp451 }, + { .name = "tmp461", .driver_data = tmp461 }, { } }; MODULE_DEVICE_TABLE(i2c, lm90_id); diff --git a/drivers/hwmon/lm92.c b/drivers/hwmon/lm92.c index 91a6b7525bb6..4aadbabc27bb 100644 --- a/drivers/hwmon/lm92.c +++ b/drivers/hwmon/lm92.c @@ -405,10 +405,10 @@ static int lm92_probe(struct i2c_client *client) * Module and driver stuff */ -/* .driver_data is limit register resolution */ +/* .driver_data is limit register resolution */ static const struct i2c_device_id lm92_id[] = { - { "lm92", 13 }, - { "max6635", 9 }, + { .name = "lm92", .driver_data = 13 }, + { .name = "max6635", .driver_data = 9 }, { } }; MODULE_DEVICE_TABLE(i2c, lm92_id); diff --git a/drivers/hwmon/lm93.c b/drivers/hwmon/lm93.c index be4853fad80f..d58a3c2a8593 100644 --- a/drivers/hwmon/lm93.c +++ b/drivers/hwmon/lm93.c @@ -2624,8 +2624,8 @@ static int lm93_probe(struct i2c_client *client) } static const struct i2c_device_id lm93_id[] = { - { "lm93" }, - { "lm94" }, + { .name = "lm93" }, + { .name = "lm94" }, { } }; MODULE_DEVICE_TABLE(i2c, lm93_id); diff --git a/drivers/hwmon/lm95234.c b/drivers/hwmon/lm95234.c index 387b3ba81dbf..74f280b90e3e 100644 --- a/drivers/hwmon/lm95234.c +++ b/drivers/hwmon/lm95234.c @@ -532,8 +532,8 @@ static int lm95234_probe(struct i2c_client *client) /* Driver data (common to all clients) */ static const struct i2c_device_id lm95234_id[] = { - { "lm95233", lm95233 }, - { "lm95234", lm95234 }, + { .name = "lm95233", .driver_data = lm95233 }, + { .name = "lm95234", .driver_data = lm95234 }, { } }; MODULE_DEVICE_TABLE(i2c, lm95234_id); diff --git a/drivers/hwmon/lm95241.c b/drivers/hwmon/lm95241.c index 456381b0938e..0cb0edb3845d 100644 --- a/drivers/hwmon/lm95241.c +++ b/drivers/hwmon/lm95241.c @@ -441,8 +441,8 @@ static int lm95241_probe(struct i2c_client *client) /* Driver data (common to all clients) */ static const struct i2c_device_id lm95241_id[] = { - { "lm95231" }, - { "lm95241" }, + { .name = "lm95231" }, + { .name = "lm95241" }, { } }; MODULE_DEVICE_TABLE(i2c, lm95241_id); diff --git a/drivers/hwmon/lm95245.c b/drivers/hwmon/lm95245.c index 9ed300c6b5f7..11553391f54b 100644 --- a/drivers/hwmon/lm95245.c +++ b/drivers/hwmon/lm95245.c @@ -546,8 +546,8 @@ static int lm95245_probe(struct i2c_client *client) /* Driver data (common to all clients) */ static const struct i2c_device_id lm95245_id[] = { - { "lm95235" }, - { "lm95245" }, + { .name = "lm95235" }, + { .name = "lm95245" }, { } }; MODULE_DEVICE_TABLE(i2c, lm95245_id); diff --git a/drivers/hwmon/ltc2945.c b/drivers/hwmon/ltc2945.c index 3e0e0e0687bd..b521100afe53 100644 --- a/drivers/hwmon/ltc2945.c +++ b/drivers/hwmon/ltc2945.c @@ -508,7 +508,7 @@ static int ltc2945_probe(struct i2c_client *client) } static const struct i2c_device_id ltc2945_id[] = { - {"ltc2945"}, + { .name = "ltc2945" }, { } }; diff --git a/drivers/hwmon/ltc2947-i2c.c b/drivers/hwmon/ltc2947-i2c.c index 176d710706dd..e07d33983d5c 100644 --- a/drivers/hwmon/ltc2947-i2c.c +++ b/drivers/hwmon/ltc2947-i2c.c @@ -27,8 +27,8 @@ static int ltc2947_probe(struct i2c_client *i2c) } static const struct i2c_device_id ltc2947_id[] = { - {"ltc2947"}, - {} + { .name = "ltc2947" }, + { } }; MODULE_DEVICE_TABLE(i2c, ltc2947_id); diff --git a/drivers/hwmon/ltc2990.c b/drivers/hwmon/ltc2990.c index f1c1933c52cf..a2725e4b2f21 100644 --- a/drivers/hwmon/ltc2990.c +++ b/drivers/hwmon/ltc2990.c @@ -259,8 +259,8 @@ static int ltc2990_i2c_probe(struct i2c_client *i2c) } static const struct i2c_device_id ltc2990_i2c_id[] = { - { "ltc2990" }, - {} + { .name = "ltc2990" }, + { } }; MODULE_DEVICE_TABLE(i2c, ltc2990_i2c_id); diff --git a/drivers/hwmon/ltc2991.c b/drivers/hwmon/ltc2991.c index 6d5d4cb846da..bc8d803e8cc9 100644 --- a/drivers/hwmon/ltc2991.c +++ b/drivers/hwmon/ltc2991.c @@ -409,8 +409,8 @@ static const struct of_device_id ltc2991_of_match[] = { MODULE_DEVICE_TABLE(of, ltc2991_of_match); static const struct i2c_device_id ltc2991_i2c_id[] = { - { "ltc2991" }, - {} + { .name = "ltc2991" }, + { } }; MODULE_DEVICE_TABLE(i2c, ltc2991_i2c_id); diff --git a/drivers/hwmon/ltc2992.c b/drivers/hwmon/ltc2992.c index 2617c4538af9..43b6029c0840 100644 --- a/drivers/hwmon/ltc2992.c +++ b/drivers/hwmon/ltc2992.c @@ -948,8 +948,8 @@ static const struct of_device_id ltc2992_of_match[] = { MODULE_DEVICE_TABLE(of, ltc2992_of_match); static const struct i2c_device_id ltc2992_i2c_id[] = { - {"ltc2992"}, - {} + { .name = "ltc2992" }, + { } }; MODULE_DEVICE_TABLE(i2c, ltc2992_i2c_id); diff --git a/drivers/hwmon/ltc4151.c b/drivers/hwmon/ltc4151.c index fa66eda78efe..fa7c57aae5f5 100644 --- a/drivers/hwmon/ltc4151.c +++ b/drivers/hwmon/ltc4151.c @@ -188,7 +188,7 @@ static int ltc4151_probe(struct i2c_client *client) } static const struct i2c_device_id ltc4151_id[] = { - { "ltc4151" }, + { .name = "ltc4151" }, { } }; MODULE_DEVICE_TABLE(i2c, ltc4151_id); diff --git a/drivers/hwmon/ltc4215.c b/drivers/hwmon/ltc4215.c index cce452711cec..3d439fbbbef9 100644 --- a/drivers/hwmon/ltc4215.c +++ b/drivers/hwmon/ltc4215.c @@ -245,7 +245,7 @@ static int ltc4215_probe(struct i2c_client *client) } static const struct i2c_device_id ltc4215_id[] = { - { "ltc4215" }, + { .name = "ltc4215" }, { } }; MODULE_DEVICE_TABLE(i2c, ltc4215_id); diff --git a/drivers/hwmon/ltc4222.c b/drivers/hwmon/ltc4222.c index f7eb007fd766..4fc69be5f2bc 100644 --- a/drivers/hwmon/ltc4222.c +++ b/drivers/hwmon/ltc4222.c @@ -200,7 +200,7 @@ static int ltc4222_probe(struct i2c_client *client) } static const struct i2c_device_id ltc4222_id[] = { - {"ltc4222"}, + { .name = "ltc4222" }, { } }; diff --git a/drivers/hwmon/ltc4245.c b/drivers/hwmon/ltc4245.c index e8131a48bda7..d87aa9c32c87 100644 --- a/drivers/hwmon/ltc4245.c +++ b/drivers/hwmon/ltc4245.c @@ -461,7 +461,7 @@ static int ltc4245_probe(struct i2c_client *client) } static const struct i2c_device_id ltc4245_id[] = { - { "ltc4245" }, + { .name = "ltc4245" }, { } }; MODULE_DEVICE_TABLE(i2c, ltc4245_id); diff --git a/drivers/hwmon/ltc4260.c b/drivers/hwmon/ltc4260.c index 9750dc9aa336..37a85125d619 100644 --- a/drivers/hwmon/ltc4260.c +++ b/drivers/hwmon/ltc4260.c @@ -163,7 +163,7 @@ static int ltc4260_probe(struct i2c_client *client) } static const struct i2c_device_id ltc4260_id[] = { - {"ltc4260"}, + { .name = "ltc4260" }, { } }; diff --git a/drivers/hwmon/ltc4261.c b/drivers/hwmon/ltc4261.c index 2cd218a6a3be..a2e52ca0b06e 100644 --- a/drivers/hwmon/ltc4261.c +++ b/drivers/hwmon/ltc4261.c @@ -222,8 +222,8 @@ static int ltc4261_probe(struct i2c_client *client) } static const struct i2c_device_id ltc4261_id[] = { - {"ltc4261"}, - {} + { .name = "ltc4261" }, + { } }; MODULE_DEVICE_TABLE(i2c, ltc4261_id); diff --git a/drivers/hwmon/max127.c b/drivers/hwmon/max127.c index 5102d86d2619..b294e86d52d1 100644 --- a/drivers/hwmon/max127.c +++ b/drivers/hwmon/max127.c @@ -312,7 +312,7 @@ static int max127_probe(struct i2c_client *client) } static const struct i2c_device_id max127_id[] = { - { "max127" }, + { .name = "max127" }, { } }; MODULE_DEVICE_TABLE(i2c, max127_id); diff --git a/drivers/hwmon/max16065.c b/drivers/hwmon/max16065.c index 43fbb9b26b10..f8b421754220 100644 --- a/drivers/hwmon/max16065.c +++ b/drivers/hwmon/max16065.c @@ -592,12 +592,12 @@ static int max16065_probe(struct i2c_client *client) } static const struct i2c_device_id max16065_id[] = { - { "max16065", max16065 }, - { "max16066", max16066 }, - { "max16067", max16067 }, - { "max16068", max16068 }, - { "max16070", max16070 }, - { "max16071", max16071 }, + { .name = "max16065", .driver_data = max16065 }, + { .name = "max16066", .driver_data = max16066 }, + { .name = "max16067", .driver_data = max16067 }, + { .name = "max16068", .driver_data = max16068 }, + { .name = "max16070", .driver_data = max16070 }, + { .name = "max16071", .driver_data = max16071 }, { } }; diff --git a/drivers/hwmon/max1619.c b/drivers/hwmon/max1619.c index 9b6d03cff4df..77ef39e8ae7e 100644 --- a/drivers/hwmon/max1619.c +++ b/drivers/hwmon/max1619.c @@ -366,7 +366,7 @@ static int max1619_probe(struct i2c_client *client) } static const struct i2c_device_id max1619_id[] = { - { "max1619" }, + { .name = "max1619" }, { } }; MODULE_DEVICE_TABLE(i2c, max1619_id); diff --git a/drivers/hwmon/max1668.c b/drivers/hwmon/max1668.c index a8197a86f559..32548f56409c 100644 --- a/drivers/hwmon/max1668.c +++ b/drivers/hwmon/max1668.c @@ -293,9 +293,9 @@ static int max1668_probe(struct i2c_client *client) } static const struct i2c_device_id max1668_id[] = { - { "max1668", 5 }, - { "max1805", 3 }, - { "max1989", 5 }, + { .name = "max1668", .driver_data = 5 }, + { .name = "max1805", .driver_data = 3 }, + { .name = "max1989", .driver_data = 5 }, { } }; MODULE_DEVICE_TABLE(i2c, max1668_id); diff --git a/drivers/hwmon/max31730.c b/drivers/hwmon/max31730.c index 2f4b419b6c9e..d132be2d6487 100644 --- a/drivers/hwmon/max31730.c +++ b/drivers/hwmon/max31730.c @@ -345,7 +345,7 @@ max31730_probe(struct i2c_client *client) } static const struct i2c_device_id max31730_ids[] = { - { "max31730" }, + { .name = "max31730" }, { } }; MODULE_DEVICE_TABLE(i2c, max31730_ids); diff --git a/drivers/hwmon/max31760.c b/drivers/hwmon/max31760.c index 127e31ca3c87..8d3be064b025 100644 --- a/drivers/hwmon/max31760.c +++ b/drivers/hwmon/max31760.c @@ -555,7 +555,7 @@ static const struct of_device_id max31760_of_match[] = { MODULE_DEVICE_TABLE(of, max31760_of_match); static const struct i2c_device_id max31760_id[] = { - {"max31760"}, + { .name = "max31760" }, { } }; MODULE_DEVICE_TABLE(i2c, max31760_id); diff --git a/drivers/hwmon/max31790.c b/drivers/hwmon/max31790.c index 4f6171a17d9f..db8a0f6f1829 100644 --- a/drivers/hwmon/max31790.c +++ b/drivers/hwmon/max31790.c @@ -517,7 +517,7 @@ static int max31790_probe(struct i2c_client *client) } static const struct i2c_device_id max31790_id[] = { - { "max31790" }, + { .name = "max31790" }, { } }; MODULE_DEVICE_TABLE(i2c, max31790_id); diff --git a/drivers/hwmon/max31827.c b/drivers/hwmon/max31827.c index 9b2e56c040df..6a7048fdd8ab 100644 --- a/drivers/hwmon/max31827.c +++ b/drivers/hwmon/max31827.c @@ -463,9 +463,9 @@ static struct attribute *max31827_attrs[] = { ATTRIBUTE_GROUPS(max31827); static const struct i2c_device_id max31827_i2c_ids[] = { - { "max31827", max31827 }, - { "max31828", max31828 }, - { "max31829", max31829 }, + { .name = "max31827", .driver_data = max31827 }, + { .name = "max31828", .driver_data = max31828 }, + { .name = "max31829", .driver_data = max31829 }, { } }; MODULE_DEVICE_TABLE(i2c, max31827_i2c_ids); diff --git a/drivers/hwmon/max6620.c b/drivers/hwmon/max6620.c index 4316dcdd03fc..100acf357b5f 100644 --- a/drivers/hwmon/max6620.c +++ b/drivers/hwmon/max6620.c @@ -474,7 +474,7 @@ static int max6620_probe(struct i2c_client *client) } static const struct i2c_device_id max6620_id[] = { - { "max6620" }, + { .name = "max6620" }, { } }; MODULE_DEVICE_TABLE(i2c, max6620_id); diff --git a/drivers/hwmon/max6621.c b/drivers/hwmon/max6621.c index a7066f3a0bb4..e86ec6d237ca 100644 --- a/drivers/hwmon/max6621.c +++ b/drivers/hwmon/max6621.c @@ -537,7 +537,7 @@ static int max6621_probe(struct i2c_client *client) } static const struct i2c_device_id max6621_id[] = { - { MAX6621_DRV_NAME }, + { .name = MAX6621_DRV_NAME }, { } }; MODULE_DEVICE_TABLE(i2c, max6621_id); diff --git a/drivers/hwmon/max6639.c b/drivers/hwmon/max6639.c index 163d31f17bd4..dd5f4b3b128d 100644 --- a/drivers/hwmon/max6639.c +++ b/drivers/hwmon/max6639.c @@ -778,7 +778,7 @@ static int max6639_resume(struct device *dev) } static const struct i2c_device_id max6639_id[] = { - {"max6639"}, + { .name = "max6639" }, { } }; diff --git a/drivers/hwmon/max6650.c b/drivers/hwmon/max6650.c index 56b8157885bb..f0140ef9c40e 100644 --- a/drivers/hwmon/max6650.c +++ b/drivers/hwmon/max6650.c @@ -807,8 +807,8 @@ static int max6650_probe(struct i2c_client *client) } static const struct i2c_device_id max6650_id[] = { - { "max6650", 1 }, - { "max6651", 4 }, + { .name = "max6650", .driver_data = 1 }, + { .name = "max6651", .driver_data = 4 }, { } }; MODULE_DEVICE_TABLE(i2c, max6650_id); diff --git a/drivers/hwmon/max6697.c b/drivers/hwmon/max6697.c index dd906cf491ca..c864093f015c 100644 --- a/drivers/hwmon/max6697.c +++ b/drivers/hwmon/max6697.c @@ -564,16 +564,16 @@ static int max6697_probe(struct i2c_client *client) } static const struct i2c_device_id max6697_id[] = { - { "max6581", max6581 }, - { "max6602", max6602 }, - { "max6622", max6622 }, - { "max6636", max6636 }, - { "max6689", max6689 }, - { "max6693", max6693 }, - { "max6694", max6694 }, - { "max6697", max6697 }, - { "max6698", max6698 }, - { "max6699", max6699 }, + { .name = "max6581", .driver_data = max6581 }, + { .name = "max6602", .driver_data = max6602 }, + { .name = "max6622", .driver_data = max6622 }, + { .name = "max6636", .driver_data = max6636 }, + { .name = "max6689", .driver_data = max6689 }, + { .name = "max6693", .driver_data = max6693 }, + { .name = "max6694", .driver_data = max6694 }, + { .name = "max6697", .driver_data = max6697 }, + { .name = "max6698", .driver_data = max6698 }, + { .name = "max6699", .driver_data = max6699 }, { } }; MODULE_DEVICE_TABLE(i2c, max6697_id); diff --git a/drivers/hwmon/mc34vr500.c b/drivers/hwmon/mc34vr500.c index 84458e4533d8..8cb9d5c09033 100644 --- a/drivers/hwmon/mc34vr500.c +++ b/drivers/hwmon/mc34vr500.c @@ -235,8 +235,8 @@ static int mc34vr500_probe(struct i2c_client *client) } static const struct i2c_device_id mc34vr500_id[] = { - { "mc34vr500" }, - { }, + { .name = "mc34vr500" }, + { } }; MODULE_DEVICE_TABLE(i2c, mc34vr500_id); diff --git a/drivers/hwmon/mcp3021.c b/drivers/hwmon/mcp3021.c index bcddf6804d3a..d6b0ebf27941 100644 --- a/drivers/hwmon/mcp3021.c +++ b/drivers/hwmon/mcp3021.c @@ -177,8 +177,8 @@ static int mcp3021_probe(struct i2c_client *client) } static const struct i2c_device_id mcp3021_id[] = { - { "mcp3021", mcp3021 }, - { "mcp3221", mcp3221 }, + { .name = "mcp3021", .driver_data = mcp3021 }, + { .name = "mcp3221", .driver_data = mcp3221 }, { } }; MODULE_DEVICE_TABLE(i2c, mcp3021_id); diff --git a/drivers/hwmon/nct6775-i2c.c b/drivers/hwmon/nct6775-i2c.c index ba71d776a291..07783910c058 100644 --- a/drivers/hwmon/nct6775-i2c.c +++ b/drivers/hwmon/nct6775-i2c.c @@ -93,19 +93,19 @@ static const struct of_device_id __maybe_unused nct6775_i2c_of_match[] = { MODULE_DEVICE_TABLE(of, nct6775_i2c_of_match); static const struct i2c_device_id nct6775_i2c_id[] = { - { "nct6106", nct6106 }, - { "nct6116", nct6116 }, - { "nct6775", nct6775 }, - { "nct6776", nct6776 }, - { "nct6779", nct6779 }, - { "nct6791", nct6791 }, - { "nct6792", nct6792 }, - { "nct6793", nct6793 }, - { "nct6795", nct6795 }, - { "nct6796", nct6796 }, - { "nct6797", nct6797 }, - { "nct6798", nct6798 }, - { "nct6799", nct6799 }, + { .name = "nct6106", .driver_data = nct6106 }, + { .name = "nct6116", .driver_data = nct6116 }, + { .name = "nct6775", .driver_data = nct6775 }, + { .name = "nct6776", .driver_data = nct6776 }, + { .name = "nct6779", .driver_data = nct6779 }, + { .name = "nct6791", .driver_data = nct6791 }, + { .name = "nct6792", .driver_data = nct6792 }, + { .name = "nct6793", .driver_data = nct6793 }, + { .name = "nct6795", .driver_data = nct6795 }, + { .name = "nct6796", .driver_data = nct6796 }, + { .name = "nct6797", .driver_data = nct6797 }, + { .name = "nct6798", .driver_data = nct6798 }, + { .name = "nct6799", .driver_data = nct6799 }, { } }; MODULE_DEVICE_TABLE(i2c, nct6775_i2c_id); diff --git a/drivers/hwmon/nct7802.c b/drivers/hwmon/nct7802.c index 8c9351da12c6..9c3e169547a9 100644 --- a/drivers/hwmon/nct7802.c +++ b/drivers/hwmon/nct7802.c @@ -1193,7 +1193,7 @@ static const unsigned short nct7802_address_list[] = { }; static const struct i2c_device_id nct7802_idtable[] = { - { "nct7802" }, + { .name = "nct7802" }, { } }; MODULE_DEVICE_TABLE(i2c, nct7802_idtable); diff --git a/drivers/hwmon/nct7904.c b/drivers/hwmon/nct7904.c index 2fa091720c79..976b8a008e44 100644 --- a/drivers/hwmon/nct7904.c +++ b/drivers/hwmon/nct7904.c @@ -1146,8 +1146,8 @@ static int nct7904_probe(struct i2c_client *client) } static const struct i2c_device_id nct7904_id[] = { - {"nct7904"}, - {} + { .name = "nct7904" }, + { } }; MODULE_DEVICE_TABLE(i2c, nct7904_id); diff --git a/drivers/hwmon/pcf8591.c b/drivers/hwmon/pcf8591.c index 167d2fe4d543..c0220ebfd4b6 100644 --- a/drivers/hwmon/pcf8591.c +++ b/drivers/hwmon/pcf8591.c @@ -285,7 +285,7 @@ static int pcf8591_read_channel(struct device *dev, int channel) } static const struct i2c_device_id pcf8591_id[] = { - { "pcf8591" }, + { .name = "pcf8591" }, { } }; MODULE_DEVICE_TABLE(i2c, pcf8591_id); diff --git a/drivers/hwmon/pmbus/acbel-fsg032.c b/drivers/hwmon/pmbus/acbel-fsg032.c index 9f07fb4abaff..d283005d92ae 100644 --- a/drivers/hwmon/pmbus/acbel-fsg032.c +++ b/drivers/hwmon/pmbus/acbel-fsg032.c @@ -49,8 +49,8 @@ static void acbel_fsg032_init_debugfs(struct i2c_client *client) } static const struct i2c_device_id acbel_fsg032_id[] = { - { "acbel_fsg032" }, - {} + { .name = "acbel_fsg032" }, + { } }; static struct pmbus_driver_info acbel_fsg032_info = { diff --git a/drivers/hwmon/pmbus/adm1266.c b/drivers/hwmon/pmbus/adm1266.c index 6f6ad7b20e9a..360a7730b807 100644 --- a/drivers/hwmon/pmbus/adm1266.c +++ b/drivers/hwmon/pmbus/adm1266.c @@ -510,7 +510,7 @@ static const struct of_device_id adm1266_of_match[] = { MODULE_DEVICE_TABLE(of, adm1266_of_match); static const struct i2c_device_id adm1266_id[] = { - { "adm1266" }, + { .name = "adm1266" }, { } }; MODULE_DEVICE_TABLE(i2c, adm1266_id); diff --git a/drivers/hwmon/pmbus/adm1275.c b/drivers/hwmon/pmbus/adm1275.c index bc2a6a07dc3e..2e5963fb1e12 100644 --- a/drivers/hwmon/pmbus/adm1275.c +++ b/drivers/hwmon/pmbus/adm1275.c @@ -478,16 +478,16 @@ static int adm1275_read_byte_data(struct i2c_client *client, int page, int reg) } static const struct i2c_device_id adm1275_id[] = { - { "adm1075", adm1075 }, - { "adm1272", adm1272 }, - { "adm1273", adm1273 }, - { "adm1275", adm1275 }, - { "adm1276", adm1276 }, - { "adm1278", adm1278 }, - { "adm1281", adm1281 }, - { "adm1293", adm1293 }, - { "adm1294", adm1294 }, - { "mc09c", sq24905c }, + { .name = "adm1075", .driver_data = adm1075 }, + { .name = "adm1272", .driver_data = adm1272 }, + { .name = "adm1273", .driver_data = adm1273 }, + { .name = "adm1275", .driver_data = adm1275 }, + { .name = "adm1276", .driver_data = adm1276 }, + { .name = "adm1278", .driver_data = adm1278 }, + { .name = "adm1281", .driver_data = adm1281 }, + { .name = "adm1293", .driver_data = adm1293 }, + { .name = "adm1294", .driver_data = adm1294 }, + { .name = "mc09c", .driver_data = sq24905c }, { } }; MODULE_DEVICE_TABLE(i2c, adm1275_id); diff --git a/drivers/hwmon/pmbus/bel-pfe.c b/drivers/hwmon/pmbus/bel-pfe.c index 6499556f735b..9e3dc9d29c56 100644 --- a/drivers/hwmon/pmbus/bel-pfe.c +++ b/drivers/hwmon/pmbus/bel-pfe.c @@ -106,9 +106,9 @@ static int pfe_pmbus_probe(struct i2c_client *client) } static const struct i2c_device_id pfe_device_id[] = { - {"pfe1100", pfe1100}, - {"pfe3000", pfe3000}, - {} + { .name = "pfe1100", .driver_data = pfe1100 }, + { .name = "pfe3000", .driver_data = pfe3000 }, + { } }; MODULE_DEVICE_TABLE(i2c, pfe_device_id); diff --git a/drivers/hwmon/pmbus/bpa-rs600.c b/drivers/hwmon/pmbus/bpa-rs600.c index 6c3875ba37a0..e364dcb59dd4 100644 --- a/drivers/hwmon/pmbus/bpa-rs600.c +++ b/drivers/hwmon/pmbus/bpa-rs600.c @@ -147,9 +147,9 @@ static struct pmbus_driver_info bpa_rs600_info = { }; static const struct i2c_device_id bpa_rs600_id[] = { - { "bpa-rs600", bpa_rs600 }, - { "bpd-rs600", bpd_rs600 }, - {}, + { .name = "bpa-rs600", .driver_data = bpa_rs600 }, + { .name = "bpd-rs600", .driver_data = bpd_rs600 }, + { } }; MODULE_DEVICE_TABLE(i2c, bpa_rs600_id); diff --git a/drivers/hwmon/pmbus/crps.c b/drivers/hwmon/pmbus/crps.c index 164b33fed312..266ec8947519 100644 --- a/drivers/hwmon/pmbus/crps.c +++ b/drivers/hwmon/pmbus/crps.c @@ -10,8 +10,8 @@ #include "pmbus.h" static const struct i2c_device_id crps_id[] = { - { "intel_crps185" }, - {} + { .name = "intel_crps185" }, + { } }; MODULE_DEVICE_TABLE(i2c, crps_id); diff --git a/drivers/hwmon/pmbus/delta-ahe50dc-fan.c b/drivers/hwmon/pmbus/delta-ahe50dc-fan.c index 3850eaea75da..2df655b20ea5 100644 --- a/drivers/hwmon/pmbus/delta-ahe50dc-fan.c +++ b/drivers/hwmon/pmbus/delta-ahe50dc-fan.c @@ -103,7 +103,7 @@ static int ahe50dc_fan_probe(struct i2c_client *client) } static const struct i2c_device_id ahe50dc_fan_id[] = { - { "ahe50dc_fan" }, + { .name = "ahe50dc_fan" }, { } }; MODULE_DEVICE_TABLE(i2c, ahe50dc_fan_id); diff --git a/drivers/hwmon/pmbus/dps920ab.c b/drivers/hwmon/pmbus/dps920ab.c index 325111a955e6..0d2901c314c2 100644 --- a/drivers/hwmon/pmbus/dps920ab.c +++ b/drivers/hwmon/pmbus/dps920ab.c @@ -191,8 +191,8 @@ static const struct of_device_id __maybe_unused dps920ab_of_match[] = { MODULE_DEVICE_TABLE(of, dps920ab_of_match); static const struct i2c_device_id dps920ab_device_id[] = { - { "dps920ab" }, - {} + { .name = "dps920ab" }, + { } }; MODULE_DEVICE_TABLE(i2c, dps920ab_device_id); diff --git a/drivers/hwmon/pmbus/fsp-3y.c b/drivers/hwmon/pmbus/fsp-3y.c index cad4d2330003..44cf2db9364b 100644 --- a/drivers/hwmon/pmbus/fsp-3y.c +++ b/drivers/hwmon/pmbus/fsp-3y.c @@ -271,8 +271,8 @@ static int fsp3y_probe(struct i2c_client *client) } static const struct i2c_device_id fsp3y_id[] = { - {"ym2151e", ym2151e}, - {"yh5151e", yh5151e}, + { .name = "ym2151e", .driver_data = ym2151e }, + { .name = "yh5151e", .driver_data = yh5151e }, { } }; MODULE_DEVICE_TABLE(i2c, fsp3y_id); diff --git a/drivers/hwmon/pmbus/hac300s.c b/drivers/hwmon/pmbus/hac300s.c index a073db1cfe2e..761e53890ebc 100644 --- a/drivers/hwmon/pmbus/hac300s.c +++ b/drivers/hwmon/pmbus/hac300s.c @@ -112,8 +112,8 @@ static const struct of_device_id hac300s_of_match[] = { MODULE_DEVICE_TABLE(of, hac300s_of_match); static const struct i2c_device_id hac300s_id[] = { - {"hac300s", 0}, - {} + { .name = "hac300s" }, + { } }; MODULE_DEVICE_TABLE(i2c, hac300s_id); diff --git a/drivers/hwmon/pmbus/ibm-cffps.c b/drivers/hwmon/pmbus/ibm-cffps.c index 6c7256d997f4..aad94bcb9ceb 100644 --- a/drivers/hwmon/pmbus/ibm-cffps.c +++ b/drivers/hwmon/pmbus/ibm-cffps.c @@ -472,10 +472,10 @@ static struct pmbus_platform_data ibm_cffps_pdata = { }; static const struct i2c_device_id ibm_cffps_id[] = { - { "ibm_cffps1", cffps1 }, - { "ibm_cffps2", cffps2 }, - { "ibm_cffps", cffps_unknown }, - {} + { .name = "ibm_cffps1", .driver_data = cffps1 }, + { .name = "ibm_cffps2", .driver_data = cffps2 }, + { .name = "ibm_cffps", .driver_data = cffps_unknown }, + { } }; MODULE_DEVICE_TABLE(i2c, ibm_cffps_id); diff --git a/drivers/hwmon/pmbus/ina233.c b/drivers/hwmon/pmbus/ina233.c index 652087589c55..c72aa258b7c6 100644 --- a/drivers/hwmon/pmbus/ina233.c +++ b/drivers/hwmon/pmbus/ina233.c @@ -168,8 +168,8 @@ static int ina233_probe(struct i2c_client *client) } static const struct i2c_device_id ina233_id[] = { - {"ina233", 0}, - {} + { .name = "ina233" }, + { } }; MODULE_DEVICE_TABLE(i2c, ina233_id); diff --git a/drivers/hwmon/pmbus/inspur-ipsps.c b/drivers/hwmon/pmbus/inspur-ipsps.c index 074e0f164ee1..57c0fc16909c 100644 --- a/drivers/hwmon/pmbus/inspur-ipsps.c +++ b/drivers/hwmon/pmbus/inspur-ipsps.c @@ -197,8 +197,8 @@ static int ipsps_probe(struct i2c_client *client) } static const struct i2c_device_id ipsps_id[] = { - { "ipsps1" }, - {} + { .name = "ipsps1" }, + { } }; MODULE_DEVICE_TABLE(i2c, ipsps_id); diff --git a/drivers/hwmon/pmbus/ir35221.c b/drivers/hwmon/pmbus/ir35221.c index 46d8f334d49a..b2120fc76f40 100644 --- a/drivers/hwmon/pmbus/ir35221.c +++ b/drivers/hwmon/pmbus/ir35221.c @@ -126,8 +126,8 @@ static int ir35221_probe(struct i2c_client *client) } static const struct i2c_device_id ir35221_id[] = { - {"ir35221"}, - {} + { .name = "ir35221" }, + { } }; MODULE_DEVICE_TABLE(i2c, ir35221_id); diff --git a/drivers/hwmon/pmbus/ir36021.c b/drivers/hwmon/pmbus/ir36021.c index 34ce15fc708b..0dce4c3f666f 100644 --- a/drivers/hwmon/pmbus/ir36021.c +++ b/drivers/hwmon/pmbus/ir36021.c @@ -51,8 +51,8 @@ static int ir36021_probe(struct i2c_client *client) } static const struct i2c_device_id ir36021_id[] = { - { "ir36021" }, - {}, + { .name = "ir36021" }, + { } }; MODULE_DEVICE_TABLE(i2c, ir36021_id); diff --git a/drivers/hwmon/pmbus/ir38064.c b/drivers/hwmon/pmbus/ir38064.c index 7b4188e8bf48..47ce88e9d13a 100644 --- a/drivers/hwmon/pmbus/ir38064.c +++ b/drivers/hwmon/pmbus/ir38064.c @@ -53,11 +53,11 @@ static int ir38064_probe(struct i2c_client *client) } static const struct i2c_device_id ir38064_id[] = { - {"ir38060"}, - {"ir38064"}, - {"ir38164"}, - {"ir38263"}, - {} + { .name = "ir38060" }, + { .name = "ir38064" }, + { .name = "ir38164" }, + { .name = "ir38263" }, + { } }; MODULE_DEVICE_TABLE(i2c, ir38064_id); diff --git a/drivers/hwmon/pmbus/irps5401.c b/drivers/hwmon/pmbus/irps5401.c index 43674c64841d..1694b96d7abf 100644 --- a/drivers/hwmon/pmbus/irps5401.c +++ b/drivers/hwmon/pmbus/irps5401.c @@ -44,8 +44,8 @@ static int irps5401_probe(struct i2c_client *client) } static const struct i2c_device_id irps5401_id[] = { - {"irps5401"}, - {} + { .name = "irps5401" }, + { } }; MODULE_DEVICE_TABLE(i2c, irps5401_id); diff --git a/drivers/hwmon/pmbus/isl68137.c b/drivers/hwmon/pmbus/isl68137.c index 21d047b577a4..2f7f825bfb69 100644 --- a/drivers/hwmon/pmbus/isl68137.c +++ b/drivers/hwmon/pmbus/isl68137.c @@ -409,54 +409,54 @@ static int isl68137_probe(struct i2c_client *client) } static const struct i2c_device_id raa_dmpvr_id[] = { - {"isl68137", raa_dmpvr1_2rail}, - {"isl68220", raa_dmpvr2_2rail}, - {"isl68221", raa_dmpvr2_3rail}, - {"isl68222", raa_dmpvr2_2rail}, - {"isl68223", raa_dmpvr2_2rail}, - {"isl68224", raa_dmpvr2_3rail}, - {"isl68225", raa_dmpvr2_2rail}, - {"isl68226", raa_dmpvr2_3rail}, - {"isl68227", raa_dmpvr2_1rail}, - {"isl68229", raa_dmpvr2_3rail}, - {"isl68233", raa_dmpvr2_2rail}, - {"isl68239", raa_dmpvr2_3rail}, + { .name = "isl68137", .driver_data = raa_dmpvr1_2rail }, + { .name = "isl68220", .driver_data = raa_dmpvr2_2rail }, + { .name = "isl68221", .driver_data = raa_dmpvr2_3rail }, + { .name = "isl68222", .driver_data = raa_dmpvr2_2rail }, + { .name = "isl68223", .driver_data = raa_dmpvr2_2rail }, + { .name = "isl68224", .driver_data = raa_dmpvr2_3rail }, + { .name = "isl68225", .driver_data = raa_dmpvr2_2rail }, + { .name = "isl68226", .driver_data = raa_dmpvr2_3rail }, + { .name = "isl68227", .driver_data = raa_dmpvr2_1rail }, + { .name = "isl68229", .driver_data = raa_dmpvr2_3rail }, + { .name = "isl68233", .driver_data = raa_dmpvr2_2rail }, + { .name = "isl68239", .driver_data = raa_dmpvr2_3rail }, - {"isl69222", raa_dmpvr2_2rail}, - {"isl69223", raa_dmpvr2_3rail}, - {"isl69224", raa_dmpvr2_2rail}, - {"isl69225", raa_dmpvr2_2rail}, - {"isl69227", raa_dmpvr2_3rail}, - {"isl69228", raa_dmpvr2_3rail}, - {"isl69234", raa_dmpvr2_2rail}, - {"isl69236", raa_dmpvr2_2rail}, - {"isl69239", raa_dmpvr2_3rail}, - {"isl69242", raa_dmpvr2_2rail}, - {"isl69243", raa_dmpvr2_1rail}, - {"isl69247", raa_dmpvr2_2rail}, - {"isl69248", raa_dmpvr2_2rail}, - {"isl69254", raa_dmpvr2_2rail}, - {"isl69255", raa_dmpvr2_2rail}, - {"isl69256", raa_dmpvr2_2rail}, - {"isl69259", raa_dmpvr2_2rail}, - {"isl69260", raa_dmpvr2_2rail}, - {"isl69268", raa_dmpvr2_2rail}, - {"isl69269", raa_dmpvr2_3rail}, - {"isl69298", raa_dmpvr2_2rail}, + { .name = "isl69222", .driver_data = raa_dmpvr2_2rail }, + { .name = "isl69223", .driver_data = raa_dmpvr2_3rail }, + { .name = "isl69224", .driver_data = raa_dmpvr2_2rail }, + { .name = "isl69225", .driver_data = raa_dmpvr2_2rail }, + { .name = "isl69227", .driver_data = raa_dmpvr2_3rail }, + { .name = "isl69228", .driver_data = raa_dmpvr2_3rail }, + { .name = "isl69234", .driver_data = raa_dmpvr2_2rail }, + { .name = "isl69236", .driver_data = raa_dmpvr2_2rail }, + { .name = "isl69239", .driver_data = raa_dmpvr2_3rail }, + { .name = "isl69242", .driver_data = raa_dmpvr2_2rail }, + { .name = "isl69243", .driver_data = raa_dmpvr2_1rail }, + { .name = "isl69247", .driver_data = raa_dmpvr2_2rail }, + { .name = "isl69248", .driver_data = raa_dmpvr2_2rail }, + { .name = "isl69254", .driver_data = raa_dmpvr2_2rail }, + { .name = "isl69255", .driver_data = raa_dmpvr2_2rail }, + { .name = "isl69256", .driver_data = raa_dmpvr2_2rail }, + { .name = "isl69259", .driver_data = raa_dmpvr2_2rail }, + { .name = "isl69260", .driver_data = raa_dmpvr2_2rail }, + { .name = "isl69268", .driver_data = raa_dmpvr2_2rail }, + { .name = "isl69269", .driver_data = raa_dmpvr2_3rail }, + { .name = "isl69298", .driver_data = raa_dmpvr2_2rail }, - {"raa228000", raa_dmpvr2_hv}, - {"raa228004", raa_dmpvr2_hv}, - {"raa228006", raa_dmpvr2_hv}, - {"raa228228", raa_dmpvr2_2rail_nontc}, - {"raa228244", raa_dmpvr2_2rail_nontc}, - {"raa228246", raa_dmpvr2_2rail_nontc}, - {"raa228942", raa_dmpvr2_2rail_nontc}, - {"raa228943", raa_dmpvr2_2rail_nontc}, - {"raa229001", raa_dmpvr2_2rail}, - {"raa229004", raa_dmpvr2_2rail}, - {"raa229141", raa_dmpvr2_2rail_pmbus}, - {"raa229621", raa_dmpvr2_2rail}, - {} + { .name = "raa228000", .driver_data = raa_dmpvr2_hv }, + { .name = "raa228004", .driver_data = raa_dmpvr2_hv }, + { .name = "raa228006", .driver_data = raa_dmpvr2_hv }, + { .name = "raa228228", .driver_data = raa_dmpvr2_2rail_nontc }, + { .name = "raa228244", .driver_data = raa_dmpvr2_2rail_nontc }, + { .name = "raa228246", .driver_data = raa_dmpvr2_2rail_nontc }, + { .name = "raa228942", .driver_data = raa_dmpvr2_2rail_nontc }, + { .name = "raa228943", .driver_data = raa_dmpvr2_2rail_nontc }, + { .name = "raa229001", .driver_data = raa_dmpvr2_2rail }, + { .name = "raa229004", .driver_data = raa_dmpvr2_2rail }, + { .name = "raa229141", .driver_data = raa_dmpvr2_2rail_pmbus }, + { .name = "raa229621", .driver_data = raa_dmpvr2_2rail }, + { } }; MODULE_DEVICE_TABLE(i2c, raa_dmpvr_id); diff --git a/drivers/hwmon/pmbus/lm25066.c b/drivers/hwmon/pmbus/lm25066.c index dd7275a67a0a..0f7df7e2c9c5 100644 --- a/drivers/hwmon/pmbus/lm25066.c +++ b/drivers/hwmon/pmbus/lm25066.c @@ -442,11 +442,11 @@ static const struct regulator_desc lm25066_reg_desc[] = { #endif static const struct i2c_device_id lm25066_id[] = { - {"lm25056", lm25056}, - {"lm25066", lm25066}, - {"lm5064", lm5064}, - {"lm5066", lm5066}, - {"lm5066i", lm5066i}, + { .name = "lm25056", .driver_data = lm25056 }, + { .name = "lm25066", .driver_data = lm25066 }, + { .name = "lm5064", .driver_data = lm5064 }, + { .name = "lm5066", .driver_data = lm5066 }, + { .name = "lm5066i", .driver_data = lm5066i }, { } }; MODULE_DEVICE_TABLE(i2c, lm25066_id); diff --git a/drivers/hwmon/pmbus/lt7182s.c b/drivers/hwmon/pmbus/lt7182s.c index 9d6d50f39bd6..f8971a786f25 100644 --- a/drivers/hwmon/pmbus/lt7182s.c +++ b/drivers/hwmon/pmbus/lt7182s.c @@ -168,8 +168,8 @@ static int lt7182s_probe(struct i2c_client *client) } static const struct i2c_device_id lt7182s_id[] = { - { "lt7182s" }, - {} + { .name = "lt7182s" }, + { } }; MODULE_DEVICE_TABLE(i2c, lt7182s_id); diff --git a/drivers/hwmon/pmbus/ltc2978.c b/drivers/hwmon/pmbus/ltc2978.c index d69a5e675e80..10877b0867fd 100644 --- a/drivers/hwmon/pmbus/ltc2978.c +++ b/drivers/hwmon/pmbus/ltc2978.c @@ -538,36 +538,36 @@ static int ltc2978_write_word_data(struct i2c_client *client, int page, } static const struct i2c_device_id ltc2978_id[] = { - {"lt7170", lt7170}, - {"lt7171", lt7171}, - {"ltc2972", ltc2972}, - {"ltc2974", ltc2974}, - {"ltc2975", ltc2975}, - {"ltc2977", ltc2977}, - {"ltc2978", ltc2978}, - {"ltc2979", ltc2979}, - {"ltc2980", ltc2980}, - {"ltc3880", ltc3880}, - {"ltc3882", ltc3882}, - {"ltc3883", ltc3883}, - {"ltc3884", ltc3884}, - {"ltc3886", ltc3886}, - {"ltc3887", ltc3887}, - {"ltc3889", ltc3889}, - {"ltc7132", ltc7132}, - {"ltc7841", ltc7841}, - {"ltc7880", ltc7880}, - {"ltm2987", ltm2987}, - {"ltm4664", ltm4664}, - {"ltm4673", ltm4673}, - {"ltm4675", ltm4675}, - {"ltm4676", ltm4676}, - {"ltm4677", ltm4677}, - {"ltm4678", ltm4678}, - {"ltm4680", ltm4680}, - {"ltm4686", ltm4686}, - {"ltm4700", ltm4700}, - {} + { .name = "lt7170", .driver_data = lt7170 }, + { .name = "lt7171", .driver_data = lt7171 }, + { .name = "ltc2972", .driver_data = ltc2972 }, + { .name = "ltc2974", .driver_data = ltc2974 }, + { .name = "ltc2975", .driver_data = ltc2975 }, + { .name = "ltc2977", .driver_data = ltc2977 }, + { .name = "ltc2978", .driver_data = ltc2978 }, + { .name = "ltc2979", .driver_data = ltc2979 }, + { .name = "ltc2980", .driver_data = ltc2980 }, + { .name = "ltc3880", .driver_data = ltc3880 }, + { .name = "ltc3882", .driver_data = ltc3882 }, + { .name = "ltc3883", .driver_data = ltc3883 }, + { .name = "ltc3884", .driver_data = ltc3884 }, + { .name = "ltc3886", .driver_data = ltc3886 }, + { .name = "ltc3887", .driver_data = ltc3887 }, + { .name = "ltc3889", .driver_data = ltc3889 }, + { .name = "ltc7132", .driver_data = ltc7132 }, + { .name = "ltc7841", .driver_data = ltc7841 }, + { .name = "ltc7880", .driver_data = ltc7880 }, + { .name = "ltm2987", .driver_data = ltm2987 }, + { .name = "ltm4664", .driver_data = ltm4664 }, + { .name = "ltm4673", .driver_data = ltm4673 }, + { .name = "ltm4675", .driver_data = ltm4675 }, + { .name = "ltm4676", .driver_data = ltm4676 }, + { .name = "ltm4677", .driver_data = ltm4677 }, + { .name = "ltm4678", .driver_data = ltm4678 }, + { .name = "ltm4680", .driver_data = ltm4680 }, + { .name = "ltm4686", .driver_data = ltm4686 }, + { .name = "ltm4700", .driver_data = ltm4700 }, + { } }; MODULE_DEVICE_TABLE(i2c, ltc2978_id); diff --git a/drivers/hwmon/pmbus/ltc3815.c b/drivers/hwmon/pmbus/ltc3815.c index 824c16a75e2c..0219d03adb03 100644 --- a/drivers/hwmon/pmbus/ltc3815.c +++ b/drivers/hwmon/pmbus/ltc3815.c @@ -143,7 +143,7 @@ static int ltc3815_write_word_data(struct i2c_client *client, int page, } static const struct i2c_device_id ltc3815_id[] = { - {"ltc3815"}, + { .name = "ltc3815" }, { } }; MODULE_DEVICE_TABLE(i2c, ltc3815_id); diff --git a/drivers/hwmon/pmbus/max15301.c b/drivers/hwmon/pmbus/max15301.c index d5810b88ea8d..4c36f1ea27ee 100644 --- a/drivers/hwmon/pmbus/max15301.c +++ b/drivers/hwmon/pmbus/max15301.c @@ -23,10 +23,10 @@ #include "pmbus.h" static const struct i2c_device_id max15301_id[] = { - { "bmr461" }, - { "max15301" }, - { "max15303" }, - {} + { .name = "bmr461" }, + { .name = "max15301" }, + { .name = "max15303" }, + { } }; MODULE_DEVICE_TABLE(i2c, max15301_id); diff --git a/drivers/hwmon/pmbus/max16064.c b/drivers/hwmon/pmbus/max16064.c index eb84915c2a83..351a1f535999 100644 --- a/drivers/hwmon/pmbus/max16064.c +++ b/drivers/hwmon/pmbus/max16064.c @@ -91,8 +91,8 @@ static int max16064_probe(struct i2c_client *client) } static const struct i2c_device_id max16064_id[] = { - {"max16064"}, - {} + { .name = "max16064" }, + { } }; MODULE_DEVICE_TABLE(i2c, max16064_id); diff --git a/drivers/hwmon/pmbus/max16601.c b/drivers/hwmon/pmbus/max16601.c index 36dc13424d92..3dd1f6fd003b 100644 --- a/drivers/hwmon/pmbus/max16601.c +++ b/drivers/hwmon/pmbus/max16601.c @@ -263,11 +263,11 @@ static void max16601_remove(void *_data) } static const struct i2c_device_id max16601_id[] = { - {"max16508", max16508}, - {"max16600", max16600}, - {"max16601", max16601}, - {"max16602", max16602}, - {} + { .name = "max16508", .driver_data = max16508 }, + { .name = "max16600", .driver_data = max16600 }, + { .name = "max16601", .driver_data = max16601 }, + { .name = "max16602", .driver_data = max16602 }, + { } }; MODULE_DEVICE_TABLE(i2c, max16601_id); diff --git a/drivers/hwmon/pmbus/max17616.c b/drivers/hwmon/pmbus/max17616.c index 1d4a0ddb95bb..744fa5aefe93 100644 --- a/drivers/hwmon/pmbus/max17616.c +++ b/drivers/hwmon/pmbus/max17616.c @@ -46,7 +46,7 @@ static int max17616_probe(struct i2c_client *client) } static const struct i2c_device_id max17616_id[] = { - { "max17616" }, + { .name = "max17616" }, { } }; MODULE_DEVICE_TABLE(i2c, max17616_id); diff --git a/drivers/hwmon/pmbus/max20730.c b/drivers/hwmon/pmbus/max20730.c index fe03164788df..4031f894e6ae 100644 --- a/drivers/hwmon/pmbus/max20730.c +++ b/drivers/hwmon/pmbus/max20730.c @@ -751,11 +751,11 @@ static int max20730_probe(struct i2c_client *client) } static const struct i2c_device_id max20730_id[] = { - { "max20710", max20710 }, - { "max20730", max20730 }, - { "max20734", max20734 }, - { "max20743", max20743 }, - { }, + { .name = "max20710", .driver_data = max20710 }, + { .name = "max20730", .driver_data = max20730 }, + { .name = "max20734", .driver_data = max20734 }, + { .name = "max20743", .driver_data = max20743 }, + { } }; MODULE_DEVICE_TABLE(i2c, max20730_id); diff --git a/drivers/hwmon/pmbus/max20751.c b/drivers/hwmon/pmbus/max20751.c index ac8c43122133..e85676433dca 100644 --- a/drivers/hwmon/pmbus/max20751.c +++ b/drivers/hwmon/pmbus/max20751.c @@ -32,8 +32,8 @@ static int max20751_probe(struct i2c_client *client) } static const struct i2c_device_id max20751_id[] = { - {"max20751"}, - {} + { .name = "max20751" }, + { } }; MODULE_DEVICE_TABLE(i2c, max20751_id); diff --git a/drivers/hwmon/pmbus/max31785.c b/drivers/hwmon/pmbus/max31785.c index 3caa76bcbeb5..bdcce810a479 100644 --- a/drivers/hwmon/pmbus/max31785.c +++ b/drivers/hwmon/pmbus/max31785.c @@ -447,10 +447,10 @@ static int max31785_probe(struct i2c_client *client) } static const struct i2c_device_id max31785_id[] = { - { "max31785" }, - { "max31785a" }, - { "max31785b" }, - { }, + { .name = "max31785" }, + { .name = "max31785a" }, + { .name = "max31785b" }, + { } }; MODULE_DEVICE_TABLE(i2c, max31785_id); diff --git a/drivers/hwmon/pmbus/max34440.c b/drivers/hwmon/pmbus/max34440.c index cc96bb22f8f5..4525b9fc5626 100644 --- a/drivers/hwmon/pmbus/max34440.c +++ b/drivers/hwmon/pmbus/max34440.c @@ -644,15 +644,15 @@ 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}, - {"max34451", max34451}, - {"max34460", max34460}, - {"max34461", max34461}, - {} + { .name = "adpm12160", .driver_data = adpm12160 }, + { .name = "adpm12200", .driver_data = adpm12200 }, + { .name = "max34440", .driver_data = max34440 }, + { .name = "max34441", .driver_data = max34441 }, + { .name = "max34446", .driver_data = max34446 }, + { .name = "max34451", .driver_data = max34451 }, + { .name = "max34460", .driver_data = max34460 }, + { .name = "max34461", .driver_data = max34461 }, + { } }; MODULE_DEVICE_TABLE(i2c, max34440_id); diff --git a/drivers/hwmon/pmbus/max8688.c b/drivers/hwmon/pmbus/max8688.c index b3a2a7492bbf..212b28fa4bf2 100644 --- a/drivers/hwmon/pmbus/max8688.c +++ b/drivers/hwmon/pmbus/max8688.c @@ -171,7 +171,7 @@ static int max8688_probe(struct i2c_client *client) } static const struct i2c_device_id max8688_id[] = { - {"max8688"}, + { .name = "max8688" }, { } }; diff --git a/drivers/hwmon/pmbus/mp2856.c b/drivers/hwmon/pmbus/mp2856.c index e83c70a3583f..3d6621e36fd3 100644 --- a/drivers/hwmon/pmbus/mp2856.c +++ b/drivers/hwmon/pmbus/mp2856.c @@ -54,9 +54,9 @@ static const int mp2856_max_phases[][MP2856_PAGE_NUM] = { }; static const struct i2c_device_id mp2856_id[] = { - {"mp2856", mp2856}, - {"mp2857", mp2857}, - {} + { .name = "mp2856", .driver_data = mp2856 }, + { .name = "mp2857", .driver_data = mp2857 }, + { } }; MODULE_DEVICE_TABLE(i2c, mp2856_id); diff --git a/drivers/hwmon/pmbus/mp2888.c b/drivers/hwmon/pmbus/mp2888.c index 772a623ca7d0..c5f35daa3fe1 100644 --- a/drivers/hwmon/pmbus/mp2888.c +++ b/drivers/hwmon/pmbus/mp2888.c @@ -378,8 +378,8 @@ static int mp2888_probe(struct i2c_client *client) } static const struct i2c_device_id mp2888_id[] = { - {"mp2888"}, - {} + { .name = "mp2888" }, + { } }; MODULE_DEVICE_TABLE(i2c, mp2888_id); diff --git a/drivers/hwmon/pmbus/mp2891.c b/drivers/hwmon/pmbus/mp2891.c index f8f4c91ec23c..316c1fc0d6cc 100644 --- a/drivers/hwmon/pmbus/mp2891.c +++ b/drivers/hwmon/pmbus/mp2891.c @@ -572,7 +572,7 @@ static int mp2891_probe(struct i2c_client *client) } static const struct i2c_device_id mp2891_id[] = { - { "mp2891" }, + { .name = "mp2891" }, { } }; MODULE_DEVICE_TABLE(i2c, mp2891_id); diff --git a/drivers/hwmon/pmbus/mp2925.c b/drivers/hwmon/pmbus/mp2925.c index ad094842cf2d..0a58b1ffd791 100644 --- a/drivers/hwmon/pmbus/mp2925.c +++ b/drivers/hwmon/pmbus/mp2925.c @@ -305,9 +305,9 @@ static int mp2925_probe(struct i2c_client *client) } static const struct i2c_device_id mp2925_id[] = { - {"mp2925"}, - {"mp2929"}, - {} + { .name = "mp2925" }, + { .name = "mp2929" }, + { } }; MODULE_DEVICE_TABLE(i2c, mp2925_id); diff --git a/drivers/hwmon/pmbus/mp29502.c b/drivers/hwmon/pmbus/mp29502.c index 7241373f1557..afc5e8c07e25 100644 --- a/drivers/hwmon/pmbus/mp29502.c +++ b/drivers/hwmon/pmbus/mp29502.c @@ -642,8 +642,8 @@ static int mp29502_probe(struct i2c_client *client) } static const struct i2c_device_id mp29502_id[] = { - {"mp29502", 0}, - {} + { .name = "mp29502", .driver_data = 0 }, + { } }; MODULE_DEVICE_TABLE(i2c, mp29502_id); diff --git a/drivers/hwmon/pmbus/mp2975.c b/drivers/hwmon/pmbus/mp2975.c index d0bc47b12cb0..dca7e2fbcb44 100644 --- a/drivers/hwmon/pmbus/mp2975.c +++ b/drivers/hwmon/pmbus/mp2975.c @@ -1082,10 +1082,10 @@ static const struct of_device_id mp2975_of_match[] = { MODULE_DEVICE_TABLE(of, mp2975_of_match); static const struct i2c_device_id mp2975_id[] = { - {"mp2971", (kernel_ulong_t)&mp2975_ddinfo[mp2971]}, - {"mp2973", (kernel_ulong_t)&mp2975_ddinfo[mp2973]}, - {"mp2975", (kernel_ulong_t)&mp2975_ddinfo[mp2975]}, - {} + { .name = "mp2971", .driver_data = (kernel_ulong_t)&mp2975_ddinfo[mp2971] }, + { .name = "mp2973", .driver_data = (kernel_ulong_t)&mp2975_ddinfo[mp2973] }, + { .name = "mp2975", .driver_data = (kernel_ulong_t)&mp2975_ddinfo[mp2975] }, + { } }; MODULE_DEVICE_TABLE(i2c, mp2975_id); diff --git a/drivers/hwmon/pmbus/mp2993.c b/drivers/hwmon/pmbus/mp2993.c index 81c84fc8ed47..3a6a6c55a1f1 100644 --- a/drivers/hwmon/pmbus/mp2993.c +++ b/drivers/hwmon/pmbus/mp2993.c @@ -233,7 +233,7 @@ static int mp2993_probe(struct i2c_client *client) } static const struct i2c_device_id mp2993_id[] = { - { "mp2993" }, + { .name = "mp2993" }, { } }; MODULE_DEVICE_TABLE(i2c, mp2993_id); diff --git a/drivers/hwmon/pmbus/mp5920.c b/drivers/hwmon/pmbus/mp5920.c index 319ae2721bcf..b803f3ddf8ea 100644 --- a/drivers/hwmon/pmbus/mp5920.c +++ b/drivers/hwmon/pmbus/mp5920.c @@ -66,7 +66,7 @@ static const struct of_device_id mp5920_of_match[] = { MODULE_DEVICE_TABLE(of, mp5920_of_match); static const struct i2c_device_id mp5920_id[] = { - { "mp5920" }, + { .name = "mp5920" }, { } }; diff --git a/drivers/hwmon/pmbus/mp5926.c b/drivers/hwmon/pmbus/mp5926.c index f0d1b30c7013..a40647472b16 100644 --- a/drivers/hwmon/pmbus/mp5926.c +++ b/drivers/hwmon/pmbus/mp5926.c @@ -157,8 +157,8 @@ static int mp5926_probe(struct i2c_client *client) } static const struct i2c_device_id mp5926_id[] = { - { "mp5926", 0 }, - {} + { .name = "mp5926", .driver_data = 0 }, + { } }; MODULE_DEVICE_TABLE(i2c, mp5926_id); diff --git a/drivers/hwmon/pmbus/mp5990.c b/drivers/hwmon/pmbus/mp5990.c index 9a4ee79712cf..f8a5d1b42ff7 100644 --- a/drivers/hwmon/pmbus/mp5990.c +++ b/drivers/hwmon/pmbus/mp5990.c @@ -148,8 +148,8 @@ static struct pmbus_driver_info mp5998_info = { }; static const struct i2c_device_id mp5990_id[] = { - {"mp5990", mp5990}, - {"mp5998", mp5998}, + { .name = "mp5990", .driver_data = mp5990 }, + { .name = "mp5998", .driver_data = mp5998 }, { } }; MODULE_DEVICE_TABLE(i2c, mp5990_id); diff --git a/drivers/hwmon/pmbus/mp9941.c b/drivers/hwmon/pmbus/mp9941.c index 42ca6748777a..d9049d326c80 100644 --- a/drivers/hwmon/pmbus/mp9941.c +++ b/drivers/hwmon/pmbus/mp9941.c @@ -291,7 +291,7 @@ static int mp9941_probe(struct i2c_client *client) } static const struct i2c_device_id mp9941_id[] = { - { "mp9941" }, + { .name = "mp9941" }, { } }; MODULE_DEVICE_TABLE(i2c, mp9941_id); diff --git a/drivers/hwmon/pmbus/mp9945.c b/drivers/hwmon/pmbus/mp9945.c index 34822e0de812..199480d85515 100644 --- a/drivers/hwmon/pmbus/mp9945.c +++ b/drivers/hwmon/pmbus/mp9945.c @@ -215,8 +215,8 @@ static int mp9945_probe(struct i2c_client *client) } static const struct i2c_device_id mp9945_id[] = { - {"mp9945"}, - {} + { .name = "mp9945" }, + { } }; MODULE_DEVICE_TABLE(i2c, mp9945_id); diff --git a/drivers/hwmon/pmbus/mpq7932.c b/drivers/hwmon/pmbus/mpq7932.c index 8f10e37a7a76..f49610adff89 100644 --- a/drivers/hwmon/pmbus/mpq7932.c +++ b/drivers/hwmon/pmbus/mpq7932.c @@ -145,9 +145,9 @@ static const struct of_device_id mpq7932_of_match[] = { MODULE_DEVICE_TABLE(of, mpq7932_of_match); static const struct i2c_device_id mpq7932_id[] = { - { "mpq2286", }, - { "mpq7932", }, - { }, + { .name = "mpq2286" }, + { .name = "mpq7932" }, + { } }; MODULE_DEVICE_TABLE(i2c, mpq7932_id); diff --git a/drivers/hwmon/pmbus/mpq8785.c b/drivers/hwmon/pmbus/mpq8785.c index 87bd039c77b9..bbde55028290 100644 --- a/drivers/hwmon/pmbus/mpq8785.c +++ b/drivers/hwmon/pmbus/mpq8785.c @@ -110,11 +110,11 @@ static struct pmbus_driver_info mpq8785_info = { }; static const struct i2c_device_id mpq8785_id[] = { - { "mpm3695", mpm3695 }, - { "mpm3695-25", mpm3695_25 }, - { "mpm82504", mpm82504 }, - { "mpq8785", mpq8785 }, - { }, + { .name = "mpm3695", .driver_data = mpm3695 }, + { .name = "mpm3695-25", .driver_data = mpm3695_25 }, + { .name = "mpm82504", .driver_data = mpm82504 }, + { .name = "mpq8785", .driver_data = mpq8785 }, + { } }; MODULE_DEVICE_TABLE(i2c, mpq8785_id); diff --git a/drivers/hwmon/pmbus/pim4328.c b/drivers/hwmon/pmbus/pim4328.c index aa98284bbdd8..9056dc387e87 100644 --- a/drivers/hwmon/pmbus/pim4328.c +++ b/drivers/hwmon/pmbus/pim4328.c @@ -39,15 +39,15 @@ struct pim4328_data { #define PIM4328_MFR_READ_STATUS 0xd0 static const struct i2c_device_id pim4328_id[] = { - {"bmr455", pim4328}, - {"pim4006", pim4006}, - {"pim4106", pim4006}, - {"pim4206", pim4006}, - {"pim4306", pim4006}, - {"pim4328", pim4328}, - {"pim4406", pim4006}, - {"pim4820", pim4820}, - {} + { .name = "bmr455", .driver_data = pim4328 }, + { .name = "pim4006", .driver_data = pim4006 }, + { .name = "pim4106", .driver_data = pim4006 }, + { .name = "pim4206", .driver_data = pim4006 }, + { .name = "pim4306", .driver_data = pim4006 }, + { .name = "pim4328", .driver_data = pim4328 }, + { .name = "pim4406", .driver_data = pim4006 }, + { .name = "pim4820", .driver_data = pim4820 }, + { } }; MODULE_DEVICE_TABLE(i2c, pim4328_id); diff --git a/drivers/hwmon/pmbus/pli1209bc.c b/drivers/hwmon/pmbus/pli1209bc.c index 569b61dc1a32..d5ab085a2273 100644 --- a/drivers/hwmon/pmbus/pli1209bc.c +++ b/drivers/hwmon/pmbus/pli1209bc.c @@ -117,8 +117,8 @@ static int pli1209bc_probe(struct i2c_client *client) } static const struct i2c_device_id pli1209bc_id[] = { - {"pli1209bc"}, - {} + { .name = "pli1209bc" }, + { } }; MODULE_DEVICE_TABLE(i2c, pli1209bc_id); diff --git a/drivers/hwmon/pmbus/pm6764tr.c b/drivers/hwmon/pmbus/pm6764tr.c index c96c0aecb920..613654b15b4a 100644 --- a/drivers/hwmon/pmbus/pm6764tr.c +++ b/drivers/hwmon/pmbus/pm6764tr.c @@ -48,8 +48,8 @@ static int pm6764tr_probe(struct i2c_client *client) } static const struct i2c_device_id pm6764tr_id[] = { - {"pm6764tr"}, - {} + { .name = "pm6764tr" }, + { } }; MODULE_DEVICE_TABLE(i2c, pm6764tr_id); diff --git a/drivers/hwmon/pmbus/pmbus.c b/drivers/hwmon/pmbus/pmbus.c index d1844c7a51ee..34945ccd3afc 100644 --- a/drivers/hwmon/pmbus/pmbus.c +++ b/drivers/hwmon/pmbus/pmbus.c @@ -213,36 +213,36 @@ static const struct pmbus_device_info pmbus_info_one_status = { * Use driver_data to set the number of pages supported by the chip. */ static const struct i2c_device_id pmbus_id[] = { - {"adp4000", (kernel_ulong_t)&pmbus_info_one}, - {"bmr310", (kernel_ulong_t)&pmbus_info_one_status}, - {"bmr453", (kernel_ulong_t)&pmbus_info_one}, - {"bmr454", (kernel_ulong_t)&pmbus_info_one}, - {"bmr456", (kernel_ulong_t)&pmbus_info_one}, - {"bmr457", (kernel_ulong_t)&pmbus_info_one}, - {"bmr458", (kernel_ulong_t)&pmbus_info_one_status}, - {"bmr480", (kernel_ulong_t)&pmbus_info_one_status}, - {"bmr490", (kernel_ulong_t)&pmbus_info_one_status}, - {"bmr491", (kernel_ulong_t)&pmbus_info_one_status}, - {"bmr492", (kernel_ulong_t)&pmbus_info_one}, - {"dps460", (kernel_ulong_t)&pmbus_info_one_skip}, - {"dps650ab", (kernel_ulong_t)&pmbus_info_one_skip}, - {"dps800", (kernel_ulong_t)&pmbus_info_one_skip}, - {"max20796", (kernel_ulong_t)&pmbus_info_one}, - {"mdt040", (kernel_ulong_t)&pmbus_info_one}, - {"ncp4200", (kernel_ulong_t)&pmbus_info_one}, - {"ncp4208", (kernel_ulong_t)&pmbus_info_one}, - {"pdt003", (kernel_ulong_t)&pmbus_info_one}, - {"pdt006", (kernel_ulong_t)&pmbus_info_one}, - {"pdt012", (kernel_ulong_t)&pmbus_info_one}, - {"pmbus", (kernel_ulong_t)&pmbus_info_zero}, - {"sgd009", (kernel_ulong_t)&pmbus_info_one_skip}, - {"tps40400", (kernel_ulong_t)&pmbus_info_one}, - {"tps544b20", (kernel_ulong_t)&pmbus_info_one}, - {"tps544b25", (kernel_ulong_t)&pmbus_info_one}, - {"tps544c20", (kernel_ulong_t)&pmbus_info_one}, - {"tps544c25", (kernel_ulong_t)&pmbus_info_one}, - {"udt020", (kernel_ulong_t)&pmbus_info_one}, - {} + { .name = "adp4000", .driver_data = (kernel_ulong_t)&pmbus_info_one }, + { .name = "bmr310", .driver_data = (kernel_ulong_t)&pmbus_info_one_status }, + { .name = "bmr453", .driver_data = (kernel_ulong_t)&pmbus_info_one }, + { .name = "bmr454", .driver_data = (kernel_ulong_t)&pmbus_info_one }, + { .name = "bmr456", .driver_data = (kernel_ulong_t)&pmbus_info_one }, + { .name = "bmr457", .driver_data = (kernel_ulong_t)&pmbus_info_one }, + { .name = "bmr458", .driver_data = (kernel_ulong_t)&pmbus_info_one_status }, + { .name = "bmr480", .driver_data = (kernel_ulong_t)&pmbus_info_one_status }, + { .name = "bmr490", .driver_data = (kernel_ulong_t)&pmbus_info_one_status }, + { .name = "bmr491", .driver_data = (kernel_ulong_t)&pmbus_info_one_status }, + { .name = "bmr492", .driver_data = (kernel_ulong_t)&pmbus_info_one }, + { .name = "dps460", .driver_data = (kernel_ulong_t)&pmbus_info_one_skip }, + { .name = "dps650ab", .driver_data = (kernel_ulong_t)&pmbus_info_one_skip }, + { .name = "dps800", .driver_data = (kernel_ulong_t)&pmbus_info_one_skip }, + { .name = "max20796", .driver_data = (kernel_ulong_t)&pmbus_info_one }, + { .name = "mdt040", .driver_data = (kernel_ulong_t)&pmbus_info_one }, + { .name = "ncp4200", .driver_data = (kernel_ulong_t)&pmbus_info_one }, + { .name = "ncp4208", .driver_data = (kernel_ulong_t)&pmbus_info_one }, + { .name = "pdt003", .driver_data = (kernel_ulong_t)&pmbus_info_one }, + { .name = "pdt006", .driver_data = (kernel_ulong_t)&pmbus_info_one }, + { .name = "pdt012", .driver_data = (kernel_ulong_t)&pmbus_info_one }, + { .name = "pmbus", .driver_data = (kernel_ulong_t)&pmbus_info_zero }, + { .name = "sgd009", .driver_data = (kernel_ulong_t)&pmbus_info_one_skip }, + { .name = "tps40400", .driver_data = (kernel_ulong_t)&pmbus_info_one }, + { .name = "tps544b20", .driver_data = (kernel_ulong_t)&pmbus_info_one }, + { .name = "tps544b25", .driver_data = (kernel_ulong_t)&pmbus_info_one }, + { .name = "tps544c20", .driver_data = (kernel_ulong_t)&pmbus_info_one }, + { .name = "tps544c25", .driver_data = (kernel_ulong_t)&pmbus_info_one }, + { .name = "udt020", .driver_data = (kernel_ulong_t)&pmbus_info_one }, + { } }; MODULE_DEVICE_TABLE(i2c, pmbus_id); diff --git a/drivers/hwmon/pmbus/pxe1610.c b/drivers/hwmon/pmbus/pxe1610.c index 24c1f961c766..6ba2b3e0e565 100644 --- a/drivers/hwmon/pmbus/pxe1610.c +++ b/drivers/hwmon/pmbus/pxe1610.c @@ -130,10 +130,10 @@ static int pxe1610_probe(struct i2c_client *client) } static const struct i2c_device_id pxe1610_id[] = { - {"pxe1610"}, - {"pxe1110"}, - {"pxm1310"}, - {} + { .name = "pxe1610" }, + { .name = "pxe1110" }, + { .name = "pxm1310" }, + { } }; MODULE_DEVICE_TABLE(i2c, pxe1610_id); diff --git a/drivers/hwmon/pmbus/q54sj108a2.c b/drivers/hwmon/pmbus/q54sj108a2.c index a368cfa9d45a..ecac405d7536 100644 --- a/drivers/hwmon/pmbus/q54sj108a2.c +++ b/drivers/hwmon/pmbus/q54sj108a2.c @@ -270,10 +270,10 @@ static const struct file_operations q54sj108a2_fops = { }; static const struct i2c_device_id q54sj108a2_id[] = { - { "q54sj108a2", q54sj108a2 }, - { "q54sn120a1", q54sj108a2 }, - { "q54sw120a7", q54sj108a2 }, - { }, + { .name = "q54sj108a2", .driver_data = q54sj108a2 }, + { .name = "q54sn120a1", .driver_data = q54sj108a2 }, + { .name = "q54sw120a7", .driver_data = q54sj108a2 }, + { } }; MODULE_DEVICE_TABLE(i2c, q54sj108a2_id); diff --git a/drivers/hwmon/pmbus/stef48h28.c b/drivers/hwmon/pmbus/stef48h28.c index 4bde2215697c..8e48dd3ba74b 100644 --- a/drivers/hwmon/pmbus/stef48h28.c +++ b/drivers/hwmon/pmbus/stef48h28.c @@ -48,8 +48,8 @@ static int stef48h28_probe(struct i2c_client *client) } static const struct i2c_device_id stef48h28_id[] = { - {"stef48h28"}, - {} + { .name = "stef48h28" }, + { } }; MODULE_DEVICE_TABLE(i2c, stef48h28_id); diff --git a/drivers/hwmon/pmbus/stpddc60.c b/drivers/hwmon/pmbus/stpddc60.c index 5cb905ed8ae5..b5ce7f975eea 100644 --- a/drivers/hwmon/pmbus/stpddc60.c +++ b/drivers/hwmon/pmbus/stpddc60.c @@ -18,9 +18,9 @@ #define STPDDC60_MFR_UV_LIMIT_OFFSET 0xe6 static const struct i2c_device_id stpddc60_id[] = { - {"stpddc60"}, - {"bmr481"}, - {} + { .name = "stpddc60" }, + { .name = "bmr481" }, + { } }; MODULE_DEVICE_TABLE(i2c, stpddc60_id); diff --git a/drivers/hwmon/pmbus/tda38640.c b/drivers/hwmon/pmbus/tda38640.c index d902d39f49f4..803926633370 100644 --- a/drivers/hwmon/pmbus/tda38640.c +++ b/drivers/hwmon/pmbus/tda38640.c @@ -195,8 +195,8 @@ static int tda38640_probe(struct i2c_client *client) } static const struct i2c_device_id tda38640_id[] = { - {"tda38640"}, - {} + { .name = "tda38640" }, + { } }; MODULE_DEVICE_TABLE(i2c, tda38640_id); diff --git a/drivers/hwmon/pmbus/tps25990.c b/drivers/hwmon/pmbus/tps25990.c index 05c6288ecafc..9d318e6509ab 100644 --- a/drivers/hwmon/pmbus/tps25990.c +++ b/drivers/hwmon/pmbus/tps25990.c @@ -387,8 +387,8 @@ static const struct pmbus_driver_info tps25990_base_info = { }; static const struct i2c_device_id tps25990_i2c_id[] = { - { "tps25990" }, - {} + { .name = "tps25990" }, + { } }; MODULE_DEVICE_TABLE(i2c, tps25990_i2c_id); diff --git a/drivers/hwmon/pmbus/tps40422.c b/drivers/hwmon/pmbus/tps40422.c index 7c9fedaa068c..f7be075dc707 100644 --- a/drivers/hwmon/pmbus/tps40422.c +++ b/drivers/hwmon/pmbus/tps40422.c @@ -31,8 +31,8 @@ static int tps40422_probe(struct i2c_client *client) } static const struct i2c_device_id tps40422_id[] = { - {"tps40422"}, - {} + { .name = "tps40422" }, + { } }; MODULE_DEVICE_TABLE(i2c, tps40422_id); diff --git a/drivers/hwmon/pmbus/tps53679.c b/drivers/hwmon/pmbus/tps53679.c index 94258e8cfd90..31e54608b3c9 100644 --- a/drivers/hwmon/pmbus/tps53679.c +++ b/drivers/hwmon/pmbus/tps53679.c @@ -291,15 +291,15 @@ static int tps53679_probe(struct i2c_client *client) } static const struct i2c_device_id tps53679_id[] = { - {"bmr474", tps53676}, - {"tps53647", tps53647}, - {"tps53667", tps53667}, - {"tps53676", tps53676}, - {"tps53679", tps53679}, - {"tps53681", tps53681}, - {"tps53685", tps53685}, - {"tps53688", tps53688}, - {} + { .name = "bmr474", .driver_data = tps53676 }, + { .name = "tps53647", .driver_data = tps53647 }, + { .name = "tps53667", .driver_data = tps53667 }, + { .name = "tps53676", .driver_data = tps53676 }, + { .name = "tps53679", .driver_data = tps53679 }, + { .name = "tps53681", .driver_data = tps53681 }, + { .name = "tps53685", .driver_data = tps53685 }, + { .name = "tps53688", .driver_data = tps53688 }, + { } }; MODULE_DEVICE_TABLE(i2c, tps53679_id); diff --git a/drivers/hwmon/pmbus/tps546d24.c b/drivers/hwmon/pmbus/tps546d24.c index 44d7a6df1dbd..4222ff355e02 100644 --- a/drivers/hwmon/pmbus/tps546d24.c +++ b/drivers/hwmon/pmbus/tps546d24.c @@ -42,8 +42,8 @@ static int tps546d24_probe(struct i2c_client *client) } static const struct i2c_device_id tps546d24_id[] = { - {"tps546d24"}, - {} + { .name = "tps546d24" }, + { } }; MODULE_DEVICE_TABLE(i2c, tps546d24_id); diff --git a/drivers/hwmon/pmbus/ucd9000.c b/drivers/hwmon/pmbus/ucd9000.c index 9b5d34a110ba..f76c2913c9ca 100644 --- a/drivers/hwmon/pmbus/ucd9000.c +++ b/drivers/hwmon/pmbus/ucd9000.c @@ -143,14 +143,14 @@ static int ucd9000_read_byte_data(struct i2c_client *client, int page, int reg) } static const struct i2c_device_id ucd9000_id[] = { - {"ucd9000", ucd9000}, - {"ucd90120", ucd90120}, - {"ucd90124", ucd90124}, - {"ucd90160", ucd90160}, - {"ucd90320", ucd90320}, - {"ucd9090", ucd9090}, - {"ucd90910", ucd90910}, - {} + { .name = "ucd9000", .driver_data = ucd9000 }, + { .name = "ucd90120", .driver_data = ucd90120 }, + { .name = "ucd90124", .driver_data = ucd90124 }, + { .name = "ucd90160", .driver_data = ucd90160 }, + { .name = "ucd90320", .driver_data = ucd90320 }, + { .name = "ucd9090", .driver_data = ucd9090 }, + { .name = "ucd90910", .driver_data = ucd90910 }, + { } }; MODULE_DEVICE_TABLE(i2c, ucd9000_id); diff --git a/drivers/hwmon/pmbus/ucd9200.c b/drivers/hwmon/pmbus/ucd9200.c index f68adaf4a110..5e07bba111a1 100644 --- a/drivers/hwmon/pmbus/ucd9200.c +++ b/drivers/hwmon/pmbus/ucd9200.c @@ -22,15 +22,15 @@ enum chips { ucd9200, ucd9220, ucd9222, ucd9224, ucd9240, ucd9244, ucd9246, ucd9248 }; static const struct i2c_device_id ucd9200_id[] = { - {"ucd9200", ucd9200}, - {"ucd9220", ucd9220}, - {"ucd9222", ucd9222}, - {"ucd9224", ucd9224}, - {"ucd9240", ucd9240}, - {"ucd9244", ucd9244}, - {"ucd9246", ucd9246}, - {"ucd9248", ucd9248}, - {} + { .name = "ucd9200", .driver_data = ucd9200 }, + { .name = "ucd9220", .driver_data = ucd9220 }, + { .name = "ucd9222", .driver_data = ucd9222 }, + { .name = "ucd9224", .driver_data = ucd9224 }, + { .name = "ucd9240", .driver_data = ucd9240 }, + { .name = "ucd9244", .driver_data = ucd9244 }, + { .name = "ucd9246", .driver_data = ucd9246 }, + { .name = "ucd9248", .driver_data = ucd9248 }, + { } }; MODULE_DEVICE_TABLE(i2c, ucd9200_id); diff --git a/drivers/hwmon/pmbus/xdp710.c b/drivers/hwmon/pmbus/xdp710.c index 660bbfe16e1e..494dbe45ebc6 100644 --- a/drivers/hwmon/pmbus/xdp710.c +++ b/drivers/hwmon/pmbus/xdp710.c @@ -110,7 +110,7 @@ static const struct of_device_id xdp710_of_match[] = { }; static const struct i2c_device_id xdp710_id[] = { - {"xdp710"}, + { .name = "xdp710" }, { } }; MODULE_DEVICE_TABLE(i2c, xdp710_id); diff --git a/drivers/hwmon/pmbus/xdp720.c b/drivers/hwmon/pmbus/xdp720.c index 8729a771f216..60491e9217bf 100644 --- a/drivers/hwmon/pmbus/xdp720.c +++ b/drivers/hwmon/pmbus/xdp720.c @@ -106,8 +106,8 @@ static const struct of_device_id xdp720_of_match[] = { MODULE_DEVICE_TABLE(of, xdp720_of_match); static const struct i2c_device_id xdp720_id[] = { - { "xdp720" }, - {} + { .name = "xdp720" }, + { } }; MODULE_DEVICE_TABLE(i2c, xdp720_id); diff --git a/drivers/hwmon/pmbus/xdpe12284.c b/drivers/hwmon/pmbus/xdpe12284.c index f3aa6339d60d..ed93e17ce8d5 100644 --- a/drivers/hwmon/pmbus/xdpe12284.c +++ b/drivers/hwmon/pmbus/xdpe12284.c @@ -164,10 +164,10 @@ static int xdpe122_probe(struct i2c_client *client) } static const struct i2c_device_id xdpe122_id[] = { - {"xdpe11280"}, - {"xdpe12254"}, - {"xdpe12284"}, - {} + { .name = "xdpe11280" }, + { .name = "xdpe12254" }, + { .name = "xdpe12284" }, + { } }; MODULE_DEVICE_TABLE(i2c, xdpe122_id); diff --git a/drivers/hwmon/pmbus/xdpe152c4.c b/drivers/hwmon/pmbus/xdpe152c4.c index 67a3d5fe1daf..b557d2971d5f 100644 --- a/drivers/hwmon/pmbus/xdpe152c4.c +++ b/drivers/hwmon/pmbus/xdpe152c4.c @@ -44,9 +44,9 @@ static int xdpe152_probe(struct i2c_client *client) } static const struct i2c_device_id xdpe152_id[] = { - {"xdpe152c4"}, - {"xdpe15284"}, - {} + { .name = "xdpe152c4" }, + { .name = "xdpe15284" }, + { } }; MODULE_DEVICE_TABLE(i2c, xdpe152_id); diff --git a/drivers/hwmon/pmbus/xdpe1a2g7b.c b/drivers/hwmon/pmbus/xdpe1a2g7b.c index 1755e3522ede..971e7b73752e 100644 --- a/drivers/hwmon/pmbus/xdpe1a2g7b.c +++ b/drivers/hwmon/pmbus/xdpe1a2g7b.c @@ -87,9 +87,9 @@ static int xdpe1a2g7b_probe(struct i2c_client *client) } static const struct i2c_device_id xdpe1a2g7b_id[] = { - { "xdpe1a2g5b" }, - { "xdpe1a2g7b" }, - {} + { .name = "xdpe1a2g5b" }, + { .name = "xdpe1a2g7b" }, + { } }; MODULE_DEVICE_TABLE(i2c, xdpe1a2g7b_id); diff --git a/drivers/hwmon/pmbus/zl6100.c b/drivers/hwmon/pmbus/zl6100.c index 97be69630cfb..5db2a7818d16 100644 --- a/drivers/hwmon/pmbus/zl6100.c +++ b/drivers/hwmon/pmbus/zl6100.c @@ -250,28 +250,28 @@ static int zl6100_write_word_data(struct i2c_client *client, int page, int reg, } static const struct i2c_device_id zl6100_id[] = { - {"bmr450", zl2005}, - {"bmr451", zl2005}, - {"bmr462", zl2008}, - {"bmr463", zl2008}, - {"bmr464", zl2008}, - {"bmr465", zls4009}, - {"bmr466", zls1003}, - {"bmr467", zls4009}, - {"bmr469", zl8802}, - {"zl2004", zl2004}, - {"zl2005", zl2005}, - {"zl2006", zl2006}, - {"zl2008", zl2008}, - {"zl2105", zl2105}, - {"zl2106", zl2106}, - {"zl6100", zl6100}, - {"zl6105", zl6105}, - {"zl8802", zl8802}, - {"zl9101", zl9101}, - {"zl9117", zl9117}, - {"zls1003", zls1003}, - {"zls4009", zls4009}, + { .name = "bmr450", .driver_data = zl2005 }, + { .name = "bmr451", .driver_data = zl2005 }, + { .name = "bmr462", .driver_data = zl2008 }, + { .name = "bmr463", .driver_data = zl2008 }, + { .name = "bmr464", .driver_data = zl2008 }, + { .name = "bmr465", .driver_data = zls4009 }, + { .name = "bmr466", .driver_data = zls1003 }, + { .name = "bmr467", .driver_data = zls4009 }, + { .name = "bmr469", .driver_data = zl8802 }, + { .name = "zl2004", .driver_data = zl2004 }, + { .name = "zl2005", .driver_data = zl2005 }, + { .name = "zl2006", .driver_data = zl2006 }, + { .name = "zl2008", .driver_data = zl2008 }, + { .name = "zl2105", .driver_data = zl2105 }, + { .name = "zl2106", .driver_data = zl2106 }, + { .name = "zl6100", .driver_data = zl6100 }, + { .name = "zl6105", .driver_data = zl6105 }, + { .name = "zl8802", .driver_data = zl8802 }, + { .name = "zl9101", .driver_data = zl9101 }, + { .name = "zl9117", .driver_data = zl9117 }, + { .name = "zls1003", .driver_data = zls1003 }, + { .name = "zls4009", .driver_data = zls4009 }, { } }; MODULE_DEVICE_TABLE(i2c, zl6100_id); diff --git a/drivers/hwmon/powr1220.c b/drivers/hwmon/powr1220.c index 06a2c56016d1..6e211601ea26 100644 --- a/drivers/hwmon/powr1220.c +++ b/drivers/hwmon/powr1220.c @@ -306,8 +306,8 @@ static int powr1220_probe(struct i2c_client *client) } static const struct i2c_device_id powr1220_ids[] = { - { "powr1014", powr1014, }, - { "powr1220", powr1220, }, + { .name = "powr1014", .driver_data = powr1014 }, + { .name = "powr1220", .driver_data = powr1220 }, { } }; diff --git a/drivers/hwmon/pt5161l.c b/drivers/hwmon/pt5161l.c index 89d4da8aa4c0..2b408a69b085 100644 --- a/drivers/hwmon/pt5161l.c +++ b/drivers/hwmon/pt5161l.c @@ -617,8 +617,8 @@ static const struct acpi_device_id __maybe_unused pt5161l_acpi_match[] = { MODULE_DEVICE_TABLE(acpi, pt5161l_acpi_match); static const struct i2c_device_id pt5161l_id[] = { - { "pt5161l" }, - {} + { .name = "pt5161l" }, + { } }; MODULE_DEVICE_TABLE(i2c, pt5161l_id); diff --git a/drivers/hwmon/sbtsi_temp.c b/drivers/hwmon/sbtsi_temp.c index c5b2488c4c7f..c28f8625cd3a 100644 --- a/drivers/hwmon/sbtsi_temp.c +++ b/drivers/hwmon/sbtsi_temp.c @@ -221,8 +221,8 @@ static int sbtsi_probe(struct i2c_client *client) } static const struct i2c_device_id sbtsi_id[] = { - {"sbtsi"}, - {} + { .name = "sbtsi" }, + { } }; MODULE_DEVICE_TABLE(i2c, sbtsi_id); diff --git a/drivers/hwmon/sg2042-mcu.c b/drivers/hwmon/sg2042-mcu.c index 105131c4acf7..591f5f572fe4 100644 --- a/drivers/hwmon/sg2042-mcu.c +++ b/drivers/hwmon/sg2042-mcu.c @@ -333,7 +333,7 @@ static int sg2042_mcu_i2c_probe(struct i2c_client *client) } static const struct i2c_device_id sg2042_mcu_id[] = { - { "sg2042-hwmon-mcu" }, + { .name = "sg2042-hwmon-mcu" }, { } }; MODULE_DEVICE_TABLE(i2c, sg2042_mcu_id); diff --git a/drivers/hwmon/sht21.c b/drivers/hwmon/sht21.c index 627d35070a42..085492669eeb 100644 --- a/drivers/hwmon/sht21.c +++ b/drivers/hwmon/sht21.c @@ -275,9 +275,9 @@ static int sht21_probe(struct i2c_client *client) /* Device ID table */ static const struct i2c_device_id sht21_id[] = { - { "sht20" }, - { "sht21" }, - { "sht25" }, + { .name = "sht20" }, + { .name = "sht21" }, + { .name = "sht25" }, { } }; MODULE_DEVICE_TABLE(i2c, sht21_id); diff --git a/drivers/hwmon/sht3x.c b/drivers/hwmon/sht3x.c index 08306ccb6d0b..c2f6b73aa7f3 100644 --- a/drivers/hwmon/sht3x.c +++ b/drivers/hwmon/sht3x.c @@ -931,10 +931,10 @@ static int sht3x_probe(struct i2c_client *client) /* device ID table */ static const struct i2c_device_id sht3x_ids[] = { - {"sht3x", sht3x}, - {"sts3x", sts3x}, - {"sht85", sht3x}, - {} + { .name = "sht3x", .driver_data = sht3x }, + { .name = "sts3x", .driver_data = sts3x }, + { .name = "sht85", .driver_data = sht3x }, + { } }; MODULE_DEVICE_TABLE(i2c, sht3x_ids); diff --git a/drivers/hwmon/sht4x.c b/drivers/hwmon/sht4x.c index 5abe1227e109..9cace0e8acda 100644 --- a/drivers/hwmon/sht4x.c +++ b/drivers/hwmon/sht4x.c @@ -424,8 +424,8 @@ static int sht4x_probe(struct i2c_client *client) } static const struct i2c_device_id sht4x_id[] = { - { "sht4x" }, - { }, + { .name = "sht4x" }, + { } }; MODULE_DEVICE_TABLE(i2c, sht4x_id); diff --git a/drivers/hwmon/shtc1.c b/drivers/hwmon/shtc1.c index 2ac906e8e173..362a73cf661b 100644 --- a/drivers/hwmon/shtc1.c +++ b/drivers/hwmon/shtc1.c @@ -257,9 +257,9 @@ static int shtc1_probe(struct i2c_client *client) /* device ID table */ static const struct i2c_device_id shtc1_id[] = { - { "shtc1", shtc1 }, - { "shtw1", shtc1 }, - { "shtc3", shtc3 }, + { .name = "shtc1", .driver_data = shtc1 }, + { .name = "shtw1", .driver_data = shtc1 }, + { .name = "shtc3", .driver_data = shtc3 }, { } }; MODULE_DEVICE_TABLE(i2c, shtc1_id); diff --git a/drivers/hwmon/smsc47m192.c b/drivers/hwmon/smsc47m192.c index 21103af4e139..1429b66e09b0 100644 --- a/drivers/hwmon/smsc47m192.c +++ b/drivers/hwmon/smsc47m192.c @@ -618,7 +618,7 @@ static int smsc47m192_probe(struct i2c_client *client) } static const struct i2c_device_id smsc47m192_id[] = { - { "smsc47m192" }, + { .name = "smsc47m192" }, { } }; MODULE_DEVICE_TABLE(i2c, smsc47m192_id); diff --git a/drivers/hwmon/spd5118.c b/drivers/hwmon/spd5118.c index 5da44571b6a0..cc40661cab21 100644 --- a/drivers/hwmon/spd5118.c +++ b/drivers/hwmon/spd5118.c @@ -746,7 +746,7 @@ static int spd5118_i2c_probe(struct i2c_client *client) } static const struct i2c_device_id spd5118_i2c_id[] = { - { "spd5118" }, + { .name = "spd5118" }, { } }; MODULE_DEVICE_TABLE(i2c, spd5118_i2c_id); diff --git a/drivers/hwmon/stts751.c b/drivers/hwmon/stts751.c index f9e8b2869164..ce23681f7981 100644 --- a/drivers/hwmon/stts751.c +++ b/drivers/hwmon/stts751.c @@ -72,7 +72,7 @@ static const int stts751_intervals[] = { }; static const struct i2c_device_id stts751_id[] = { - { "stts751" }, + { .name = "stts751" }, { } }; diff --git a/drivers/hwmon/tc654.c b/drivers/hwmon/tc654.c index 39fe5836f237..4b22e305b203 100644 --- a/drivers/hwmon/tc654.c +++ b/drivers/hwmon/tc654.c @@ -550,9 +550,9 @@ static int tc654_probe(struct i2c_client *client) } static const struct i2c_device_id tc654_id[] = { - {"tc654"}, - {"tc655"}, - {} + { .name = "tc654" }, + { .name = "tc655" }, + { } }; MODULE_DEVICE_TABLE(i2c, tc654_id); diff --git a/drivers/hwmon/tc74.c b/drivers/hwmon/tc74.c index 7fb7b50ad1ad..e9113519be53 100644 --- a/drivers/hwmon/tc74.c +++ b/drivers/hwmon/tc74.c @@ -151,8 +151,8 @@ static int tc74_probe(struct i2c_client *client) } static const struct i2c_device_id tc74_id[] = { - { "tc74" }, - {} + { .name = "tc74" }, + { } }; MODULE_DEVICE_TABLE(i2c, tc74_id); diff --git a/drivers/hwmon/thmc50.c b/drivers/hwmon/thmc50.c index 0cbdb91698b1..b385ef64af75 100644 --- a/drivers/hwmon/thmc50.c +++ b/drivers/hwmon/thmc50.c @@ -407,8 +407,8 @@ static int thmc50_probe(struct i2c_client *client) } static const struct i2c_device_id thmc50_id[] = { - { "adm1022", adm1022 }, - { "thmc50", thmc50 }, + { .name = "adm1022", .driver_data = adm1022 }, + { .name = "thmc50", .driver_data = thmc50 }, { } }; MODULE_DEVICE_TABLE(i2c, thmc50_id); diff --git a/drivers/hwmon/tmp102.c b/drivers/hwmon/tmp102.c index ad525c55daf0..6bd1bed3cdb8 100644 --- a/drivers/hwmon/tmp102.c +++ b/drivers/hwmon/tmp102.c @@ -396,7 +396,7 @@ static int tmp102_resume(struct device *dev) static DEFINE_SIMPLE_DEV_PM_OPS(tmp102_dev_pm_ops, tmp102_suspend, tmp102_resume); static const struct i2c_device_id tmp102_id[] = { - { "tmp102" }, + { .name = "tmp102" }, { } }; MODULE_DEVICE_TABLE(i2c, tmp102_id); diff --git a/drivers/hwmon/tmp103.c b/drivers/hwmon/tmp103.c index 221bba8a215d..f13d24767575 100644 --- a/drivers/hwmon/tmp103.c +++ b/drivers/hwmon/tmp103.c @@ -194,7 +194,7 @@ static int tmp103_resume(struct device *dev) static DEFINE_SIMPLE_DEV_PM_OPS(tmp103_dev_pm_ops, tmp103_suspend, tmp103_resume); static const struct i2c_device_id tmp103_id[] = { - { "tmp103" }, + { .name = "tmp103" }, { } }; MODULE_DEVICE_TABLE(i2c, tmp103_id); diff --git a/drivers/hwmon/tmp108.c b/drivers/hwmon/tmp108.c index 3ea5f6485744..1c4a58855e2d 100644 --- a/drivers/hwmon/tmp108.c +++ b/drivers/hwmon/tmp108.c @@ -537,10 +537,10 @@ static int tmp108_resume(struct device *dev) static DEFINE_SIMPLE_DEV_PM_OPS(tmp108_dev_pm_ops, tmp108_suspend, tmp108_resume); static const struct i2c_device_id tmp108_i2c_ids[] = { - { "p3t1035", (unsigned long)&p3t1035_data }, - { "p3t1085", (unsigned long)&tmp108_data }, - { "tmp108", (unsigned long)&tmp108_data }, - {} + { .name = "p3t1035", .driver_data = (unsigned long)&p3t1035_data }, + { .name = "p3t1085", .driver_data = (unsigned long)&tmp108_data }, + { .name = "tmp108", .driver_data = (unsigned long)&tmp108_data }, + { } }; MODULE_DEVICE_TABLE(i2c, tmp108_i2c_ids); diff --git a/drivers/hwmon/tmp401.c b/drivers/hwmon/tmp401.c index 07f596581c6e..a09225a87355 100644 --- a/drivers/hwmon/tmp401.c +++ b/drivers/hwmon/tmp401.c @@ -90,11 +90,11 @@ static const u8 TMP432_STATUS_REG[] = { */ static const struct i2c_device_id tmp401_id[] = { - { "tmp401", tmp401 }, - { "tmp411", tmp411 }, - { "tmp431", tmp431 }, - { "tmp432", tmp432 }, - { "tmp435", tmp435 }, + { .name = "tmp401", .driver_data = tmp401 }, + { .name = "tmp411", .driver_data = tmp411 }, + { .name = "tmp431", .driver_data = tmp431 }, + { .name = "tmp432", .driver_data = tmp432 }, + { .name = "tmp435", .driver_data = tmp435 }, { } }; MODULE_DEVICE_TABLE(i2c, tmp401_id); diff --git a/drivers/hwmon/tmp421.c b/drivers/hwmon/tmp421.c index 2ea9d3e9553d..ed00ccfdd7b3 100644 --- a/drivers/hwmon/tmp421.c +++ b/drivers/hwmon/tmp421.c @@ -56,11 +56,11 @@ static const u8 TMP421_TEMP_LSB[MAX_CHANNELS] = { 0x10, 0x11, 0x12, 0x13 }; #define TMP442_DEVICE_ID 0x42 static const struct i2c_device_id tmp421_id[] = { - { "tmp421", 2 }, - { "tmp422", 3 }, - { "tmp423", 4 }, - { "tmp441", 2 }, - { "tmp442", 3 }, + { .name = "tmp421", .driver_data = 2 }, + { .name = "tmp422", .driver_data = 3 }, + { .name = "tmp423", .driver_data = 4 }, + { .name = "tmp441", .driver_data = 2 }, + { .name = "tmp442", .driver_data = 3 }, { } }; MODULE_DEVICE_TABLE(i2c, tmp421_id); diff --git a/drivers/hwmon/tmp464.c b/drivers/hwmon/tmp464.c index 98f2576d94c6..c3e031044d1e 100644 --- a/drivers/hwmon/tmp464.c +++ b/drivers/hwmon/tmp464.c @@ -65,8 +65,8 @@ static const u8 TMP464_THERM2_LIMIT[MAX_CHANNELS] = { #define TMP468_DEVICE_ID 0x0468 static const struct i2c_device_id tmp464_id[] = { - { "tmp464", TMP464_NUM_CHANNELS }, - { "tmp468", TMP468_NUM_CHANNELS }, + { .name = "tmp464", .driver_data = TMP464_NUM_CHANNELS }, + { .name = "tmp468", .driver_data = TMP468_NUM_CHANNELS }, { } }; MODULE_DEVICE_TABLE(i2c, tmp464_id); diff --git a/drivers/hwmon/tmp513.c b/drivers/hwmon/tmp513.c index 5acbfd7d088d..b160aa045f89 100644 --- a/drivers/hwmon/tmp513.c +++ b/drivers/hwmon/tmp513.c @@ -611,8 +611,8 @@ static int tmp51x_init(struct tmp51x_data *data) } static const struct i2c_device_id tmp51x_id[] = { - { "tmp512", TMP512_MAX_CHANNELS }, - { "tmp513", TMP513_MAX_CHANNELS }, + { .name = "tmp512", .driver_data = TMP512_MAX_CHANNELS }, + { .name = "tmp513", .driver_data = TMP513_MAX_CHANNELS }, { } }; MODULE_DEVICE_TABLE(i2c, tmp51x_id); diff --git a/drivers/hwmon/w83773g.c b/drivers/hwmon/w83773g.c index 401a28f55f93..54224dac10ae 100644 --- a/drivers/hwmon/w83773g.c +++ b/drivers/hwmon/w83773g.c @@ -34,7 +34,7 @@ static const u8 W83773_OFFSET_MSB[2] = { 0x11, 0x15 }; /* this is the number of sensors in the device */ static const struct i2c_device_id w83773_id[] = { - { "w83773g" }, + { .name = "w83773g" }, { } }; diff --git a/drivers/hwmon/w83781d.c b/drivers/hwmon/w83781d.c index f664c2152a6d..c40f84e53469 100644 --- a/drivers/hwmon/w83781d.c +++ b/drivers/hwmon/w83781d.c @@ -1559,10 +1559,10 @@ static struct w83781d_data *w83781d_update_device(struct device *dev) } static const struct i2c_device_id w83781d_ids[] = { - { "w83781d", w83781d, }, - { "w83782d", w83782d, }, - { "w83783s", w83783s, }, - { "as99127f", as99127f }, + { .name = "w83781d", .driver_data = w83781d }, + { .name = "w83782d", .driver_data = w83782d }, + { .name = "w83783s", .driver_data = w83783s }, + { .name = "as99127f", .driver_data = as99127f }, { /* LIST END */ } }; MODULE_DEVICE_TABLE(i2c, w83781d_ids); diff --git a/drivers/hwmon/w83791d.c b/drivers/hwmon/w83791d.c index 996e36951f9d..4a777430af5c 100644 --- a/drivers/hwmon/w83791d.c +++ b/drivers/hwmon/w83791d.c @@ -333,7 +333,7 @@ static void w83791d_print_debug(struct w83791d_data *data, struct device *dev); static void w83791d_init_client(struct i2c_client *client); static const struct i2c_device_id w83791d_id[] = { - { "w83791d" }, + { .name = "w83791d" }, { } }; MODULE_DEVICE_TABLE(i2c, w83791d_id); diff --git a/drivers/hwmon/w83792d.c b/drivers/hwmon/w83792d.c index b0b5f60eea53..f715c79389a5 100644 --- a/drivers/hwmon/w83792d.c +++ b/drivers/hwmon/w83792d.c @@ -296,7 +296,7 @@ static void w83792d_print_debug(struct w83792d_data *data, struct device *dev); static void w83792d_init_client(struct i2c_client *client); static const struct i2c_device_id w83792d_id[] = { - { "w83792d" }, + { .name = "w83792d" }, { } }; MODULE_DEVICE_TABLE(i2c, w83792d_id); diff --git a/drivers/hwmon/w83793.c b/drivers/hwmon/w83793.c index 24772cfbecb3..b1f906f06ab4 100644 --- a/drivers/hwmon/w83793.c +++ b/drivers/hwmon/w83793.c @@ -291,7 +291,7 @@ static void w83793_update_nonvolatile(struct device *dev); static struct w83793_data *w83793_update_device(struct device *dev); static const struct i2c_device_id w83793_id[] = { - { "w83793" }, + { .name = "w83793" }, { } }; MODULE_DEVICE_TABLE(i2c, w83793_id); diff --git a/drivers/hwmon/w83795.c b/drivers/hwmon/w83795.c index 5174db69db5e..c5ce0bf1b08e 100644 --- a/drivers/hwmon/w83795.c +++ b/drivers/hwmon/w83795.c @@ -2243,8 +2243,8 @@ static void w83795_remove(struct i2c_client *client) static const struct i2c_device_id w83795_id[] = { - { "w83795g", w83795g }, - { "w83795adg", w83795adg }, + { .name = "w83795g", .driver_data = w83795g }, + { .name = "w83795adg", .driver_data = w83795adg }, { } }; MODULE_DEVICE_TABLE(i2c, w83795_id); diff --git a/drivers/hwmon/w83l785ts.c b/drivers/hwmon/w83l785ts.c index df77b53a1b2f..e42506a3bcbe 100644 --- a/drivers/hwmon/w83l785ts.c +++ b/drivers/hwmon/w83l785ts.c @@ -74,7 +74,7 @@ static struct w83l785ts_data *w83l785ts_update_device(struct device *dev); */ static const struct i2c_device_id w83l785ts_id[] = { - { "w83l785ts" }, + { .name = "w83l785ts" }, { } }; MODULE_DEVICE_TABLE(i2c, w83l785ts_id); diff --git a/drivers/hwmon/w83l786ng.c b/drivers/hwmon/w83l786ng.c index 1d9109ca1585..a72397083cc4 100644 --- a/drivers/hwmon/w83l786ng.c +++ b/drivers/hwmon/w83l786ng.c @@ -751,7 +751,7 @@ w83l786ng_probe(struct i2c_client *client) } static const struct i2c_device_id w83l786ng_id[] = { - { "w83l786ng" }, + { .name = "w83l786ng" }, { } }; MODULE_DEVICE_TABLE(i2c, w83l786ng_id); From 1000878867bdf36b768802128f51379a1d8593e6 Mon Sep 17 00:00:00 2001 From: Ronan Dalton Date: Thu, 14 May 2026 12:34:04 +1200 Subject: [PATCH 28/81] hwmon: (nct7802) Add time step attributes for tweaking responsiveness The nct7802 chip exposes two registers that allow setting the time interval between successive duty increases or decreases in Smart Fan mode. The units are intervals of 0.1 second. The default value at power on is 10, so 1 second. Add sysfs attributes for step_up_time and step_down_time to allow controlling the responsiveness of the fan speed. Values are represented as milliseconds to the user. When set, the value is clamped to the valid range of 100 to 25500 (0.1 to 25.5 seconds), and rounded to the nearest multiple of 100. Signed-off-by: Ronan Dalton Cc: linux-kernel@vger.kernel.org Cc: linux-hwmon@vger.kernel.org Cc: Guenter Roeck Cc: Chris Packham Link: https://lore.kernel.org/r/20260514003404.1548747-2-ronan.dalton@alliedtelesis.co.nz Signed-off-by: Guenter Roeck --- Documentation/hwmon/nct7802.rst | 16 ++++++ drivers/hwmon/nct7802.c | 91 +++++++++++++++++++++++++++++++++ 2 files changed, 107 insertions(+) diff --git a/Documentation/hwmon/nct7802.rst b/Documentation/hwmon/nct7802.rst index 8b7365a7cb32..366050ea595c 100644 --- a/Documentation/hwmon/nct7802.rst +++ b/Documentation/hwmon/nct7802.rst @@ -24,6 +24,22 @@ speed sensors. Smart Fan™ speed control is available via pwmX_auto_point attributes. +Sysfs Attributes +---------------- + +Sysfs attributes unique to this chip are documented below. For common +attributes, see Documentation/hwmon/sysfs-interface.rst. + +step_up_time + Time interval between successive duty cycle increases + when in Smart Fan mode. Specified in milliseconds and + rounded to intervals of 100 in the range 100-25500. + +step_down_time + Time interval between successive duty cycle decreases + when in Smart Fan mode. Specified in milliseconds and + rounded to intervals of 100 in the range 100-25500. + Tested Boards and BIOS Versions ------------------------------- diff --git a/drivers/hwmon/nct7802.c b/drivers/hwmon/nct7802.c index 9c3e169547a9..10d1a41fe211 100644 --- a/drivers/hwmon/nct7802.c +++ b/drivers/hwmon/nct7802.c @@ -47,6 +47,8 @@ static const u8 REG_VOLTAGE_LIMIT_MSB_SHIFT[2][5] = { #define REG_PWM(x) (0x60 + (x)) #define REG_SMARTFAN_EN(x) (0x64 + (x) / 2) #define SMARTFAN_EN_SHIFT(x) ((x) % 2 * 4) +#define REG_SMARTFAN_STEP_UP_TIME 0x6e +#define REG_SMARTFAN_STEP_DOWN_TIME 0x6f #define REG_VENDOR_ID 0xfd #define REG_CHIP_ID 0xfe #define REG_VERSION_ID 0xff @@ -560,6 +562,77 @@ beep_store(struct device *dev, struct device_attribute *attr, const char *buf, return err ? : count; } +static ssize_t step_time_show(struct device *dev, struct device_attribute *attr, + char *buf, bool step_up) +{ + struct nct7802_data *data = dev_get_drvdata(dev); + unsigned int reg, val; + int ret; + + if (step_up) + reg = REG_SMARTFAN_STEP_UP_TIME; + else + reg = REG_SMARTFAN_STEP_DOWN_TIME; + + ret = regmap_read(data->regmap, reg, &val); + if (ret < 0) + return ret; + + return sprintf(buf, "%u\n", val * 100); /* Convert from ds to ms */ +} + +static ssize_t step_up_time_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return step_time_show(dev, attr, buf, true); +} + +static ssize_t step_down_time_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return step_time_show(dev, attr, buf, false); +} + +static ssize_t step_time_store(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count, bool step_up) +{ + struct nct7802_data *data = dev_get_drvdata(dev); + unsigned long val; + unsigned int reg; + int ret; + + ret = kstrtoul(buf, 10, &val); + if (ret < 0) + return ret; + + /* Clamp range, and convert from ms to ds */ + val = DIV_ROUND_CLOSEST(clamp_val(val, 100, 25500), 100); + + if (step_up) + reg = REG_SMARTFAN_STEP_UP_TIME; + else + reg = REG_SMARTFAN_STEP_DOWN_TIME; + + ret = regmap_write(data->regmap, reg, val); + + return ret ? : count; +} + +static ssize_t step_up_time_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + return step_time_store(dev, attr, buf, count, true); +} + +static ssize_t step_down_time_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + return step_time_store(dev, attr, buf, count, false); +} + static SENSOR_DEVICE_ATTR_RW(temp1_type, temp_type, 0); static SENSOR_DEVICE_ATTR_2_RO(temp1_input, temp, 0x01, REG_TEMP_LSB); static SENSOR_DEVICE_ATTR_2_RW(temp1_min, temp, 0x31, 0); @@ -975,12 +1048,30 @@ static const struct attribute_group nct7802_auto_point_group = { .attrs = nct7802_auto_point_attrs, }; +/* 7.2.102 0x6E FANCTL Step Up Time Register */ +static SENSOR_DEVICE_ATTR_RW(step_up_time, step_up_time, 0); + +/* 7.2.103 0x6F FANCTL Step Down Time Register */ +static SENSOR_DEVICE_ATTR_RW(step_down_time, step_down_time, 0); + +static struct attribute *nct7802_step_time_attrs[] = { + &sensor_dev_attr_step_up_time.dev_attr.attr, + &sensor_dev_attr_step_down_time.dev_attr.attr, + + NULL +}; + +static const struct attribute_group nct7802_step_time_group = { + .attrs = nct7802_step_time_attrs, +}; + static const struct attribute_group *nct7802_groups[] = { &nct7802_temp_group, &nct7802_in_group, &nct7802_fan_group, &nct7802_pwm_group, &nct7802_auto_point_group, + &nct7802_step_time_group, NULL }; From 0a310408504ed768aab9faa694573122ac7c003d Mon Sep 17 00:00:00 2001 From: Abdurrahman Hussain Date: Tue, 12 May 2026 11:56:24 -0700 Subject: [PATCH 29/81] hwmon: (pmbus/adm1266) add firmware_revision debugfs entry The ADM1266 reports its firmware revision via the IC_DEVICE_REV manufacturer-specific block-read command (0xAE, datasheet Rev. D Table 80). The first three returned bytes are the firmware major.minor.patch fields. This is useful when correlating field behaviour against ADI release notes; expose it through debugfs alongside the existing sequencer_state entry. The standard PMBus MFR_REVISION (0x9B) register is already exposed by pmbus_core's debugfs auto-create path and reports the manufacturer revision, which is a separate thing from the firmware running on the device. Signed-off-by: Abdurrahman Hussain Link: https://lore.kernel.org/r/20260512-adm1266-v3-1-a81a479b0bb0@nexthop.ai [groeck: Squashed patch adding serialization with pmbus_lock] Signed-off-by: Guenter Roeck --- drivers/hwmon/pmbus/adm1266.c | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/drivers/hwmon/pmbus/adm1266.c b/drivers/hwmon/pmbus/adm1266.c index 360a7730b807..7f4dbc98d92a 100644 --- a/drivers/hwmon/pmbus/adm1266.c +++ b/drivers/hwmon/pmbus/adm1266.c @@ -21,6 +21,7 @@ #include #include +#define ADM1266_IC_DEVICE_REV 0xAE #define ADM1266_BLACKBOX_CONFIG 0xD3 #define ADM1266_PDIO_CONFIG 0xD4 #define ADM1266_READ_STATE 0xD9 @@ -345,6 +346,31 @@ static int adm1266_state_read(struct seq_file *s, void *pdata) return 0; } +/* + * IC_DEVICE_REV (0xAE) returns an 8-byte block (datasheet Rev. D, Table 80): + * [2:0] firmware revision major.minor.patch + * [5:3] bootloader revision major.minor.patch + * [7:6] silicon revision two ASCII characters + */ +static int adm1266_firmware_revision_read(struct seq_file *s, void *pdata) +{ + struct device *dev = s->private; + struct i2c_client *client = to_i2c_client(dev); + u8 buf[I2C_SMBUS_BLOCK_MAX]; + int ret; + + guard(pmbus_lock)(client); + ret = i2c_smbus_read_block_data(client, ADM1266_IC_DEVICE_REV, buf); + if (ret < 0) + return ret; + if (ret < 3) + return -EIO; + + seq_printf(s, "%u.%u.%u\n", buf[0], buf[1], buf[2]); + + return 0; +} + static void adm1266_init_debugfs(struct adm1266_data *data) { struct dentry *root; @@ -357,6 +383,8 @@ static void adm1266_init_debugfs(struct adm1266_data *data) debugfs_create_devm_seqfile(&data->client->dev, "sequencer_state", data->debugfs_dir, adm1266_state_read); + debugfs_create_devm_seqfile(&data->client->dev, "firmware_revision", data->debugfs_dir, + adm1266_firmware_revision_read); } static int adm1266_nvmem_read_blackbox(struct adm1266_data *data, u8 *read_buff) From 5354f06bdb20eaae1af9b84add428bc4d27280c2 Mon Sep 17 00:00:00 2001 From: Chen-Shi-Hong Date: Sat, 16 May 2026 11:52:31 +0800 Subject: [PATCH 30/81] Documentation: hwmon: adt7411: document supported sysfs attributes The adt7411 driver exposes additional standard hwmon attributes beyond the ones currently listed in Documentation/hwmon/adt7411.rst. Document voltage min/max/alarm attributes, temperature min/max and min_alarm/max_alarm attributes, and the temp2_fault attribute for the external temperature channel. Also update the documentation to clarify that analog inputs in1 and in2 are not available when the external temperature sensor is enabled, and remove the outdated statement claiming that external temperature support and limit registers are unsupported. Signed-off-by: Chen-Shi-Hong Link: https://lore.kernel.org/r/20260516035245.1604-1-eric039eric@gmail.com Signed-off-by: Guenter Roeck --- Documentation/hwmon/adt7411.rst | 37 +++++++++++++++++++++++++++------ 1 file changed, 31 insertions(+), 6 deletions(-) diff --git a/Documentation/hwmon/adt7411.rst b/Documentation/hwmon/adt7411.rst index 57ad16fb216a..28b6f3cb6b86 100644 --- a/Documentation/hwmon/adt7411.rst +++ b/Documentation/hwmon/adt7411.rst @@ -30,11 +30,36 @@ Check the datasheet for details. sysfs-Interface --------------- -================ ================= -in0_input vdd voltage input -in[1-8]_input analog 1-8 input -temp1_input temperature input -================ ================= +================ ================================= +in0_input vdd voltage input +in0_min vdd low limit +in0_max vdd high limit +in0_alarm vdd alarm +in[1-8]_input analog 1-8 input +in[1-8]_min analog input low limit +in[1-8]_max analog input high limit +in[1-8]_alarm analog input alarm +temp1_input internal temperature input +temp1_min internal temperature low limit +temp1_max internal temperature high limit +temp1_min_alarm internal temperature low alarm +temp1_max_alarm internal temperature high alarm +================ ================================= + +If the external temperature sensor is enabled, the following attributes are +also available: + +================ ================================================ +temp2_input external temperature input +temp2_min external temperature low limit +temp2_max external temperature high limit +temp2_min_alarm external temperature low alarm +temp2_max_alarm external temperature high alarm +temp2_fault external temperature sensor fault +================ ================================================ + +If the external temperature sensor is enabled, analog inputs in1 and in2 are +not available. Besides standard interfaces, this driver adds (0 = off, 1 = on): @@ -47,4 +72,4 @@ Besides standard interfaces, this driver adds (0 = off, 1 = on): Notes ----- -SPI, external temperature sensor and limit registers are not supported yet. +SPI is not supported yet. From b89e1255c92ba7bbe78a7ba6cbda57bb02da7c3d Mon Sep 17 00:00:00 2001 From: Roman Bakshansky Date: Sat, 16 May 2026 14:42:52 +0300 Subject: [PATCH 31/81] hwmon: (coretemp) replace hardcoded core count with dynamic value The hardcoded maximum of 512 cores per package was first defined by commit 34cf8c657cf0 ("hwmon: (coretemp) Enlarge per package core count limit") and later kept as a fallback with a TODO in commit 1a793caf6f69 ("hwmon: (coretemp) Use dynamic allocated memory for core temp_data") because the actual per-package core count was not reliably available at the time. Now that topology_num_cores_per_package() is stable and suitable for use, it's time to complete the TODO and allocate only the needed amount of memory for core_data. Signed-off-by: Roman Bakshansky Link: https://lore.kernel.org/r/20260516114253.5466-2-bakshansky.lists@gmail.com Signed-off-by: Guenter Roeck --- drivers/hwmon/coretemp.c | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/drivers/hwmon/coretemp.c b/drivers/hwmon/coretemp.c index 6a0d94711ead..c722b1d8e480 100644 --- a/drivers/hwmon/coretemp.c +++ b/drivers/hwmon/coretemp.c @@ -39,7 +39,6 @@ static int force_tjmax; module_param_named(tjmax, force_tjmax, int, 0444); MODULE_PARM_DESC(tjmax, "TjMax value in degrees Celsius"); -#define NUM_REAL_CORES 512 /* Number of Real cores per cpu */ #define CORETEMP_NAME_LENGTH 28 /* String Length of attrs */ enum coretemp_attr_index { @@ -485,13 +484,7 @@ init_temp_data(struct platform_data *pdata, unsigned int cpu, int pkg_flag) struct temp_data *tdata; if (!pdata->core_data) { - /* - * TODO: - * The information of actual possible cores in a package is broken for now. - * Will replace hardcoded NUM_REAL_CORES with actual per package core count - * when this information becomes available. - */ - pdata->nr_cores = NUM_REAL_CORES; + pdata->nr_cores = topology_num_cores_per_package(); pdata->core_data = kzalloc_objs(struct temp_data *, pdata->nr_cores); if (!pdata->core_data) From 4edc7d45abc4b9bb6061818bdc1b75f2cf70f019 Mon Sep 17 00:00:00 2001 From: Roman Bakshansky Date: Sat, 16 May 2026 14:42:53 +0300 Subject: [PATCH 32/81] hwmon: (coretemp) fix coding style issues Address several coding style warnings reported by checkpatch.pl: - Replace with - Add missing blank lines after declarations - Combine split quoted strings - Reorder __initconst placement No functional change. Signed-off-by: Roman Bakshansky Link: https://lore.kernel.org/r/20260516114253.5466-3-bakshansky.lists@gmail.com [groeck: Dropped false positive change. Fixed various CHECK reports.] Signed-off-by: Guenter Roeck --- drivers/hwmon/coretemp.c | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/drivers/hwmon/coretemp.c b/drivers/hwmon/coretemp.c index c722b1d8e480..02b4e46d965b 100644 --- a/drivers/hwmon/coretemp.c +++ b/drivers/hwmon/coretemp.c @@ -25,7 +25,7 @@ #include #include #include -#include +#include #include #include @@ -135,14 +135,14 @@ static const struct tjmax_model tjmax_model_table[] = { * which are covered by tjmax_table */ { INTEL_ATOM_BONNELL_MID, ANY, 90000 }, /* Atom Tunnel Creek (Exx), Lincroft (Z6xx) - * Note: TjMax for E6xxT is 110C, but CPU type - * is undetectable by software + * Note: TjMax for E6xxT is 110C, but CPU + * type is undetectable by software */ { INTEL_ATOM_SALTWELL_MID, ANY, 90000 }, /* Atom Medfield (Z2460) */ { INTEL_ATOM_SALTWELL_TABLET, ANY, 90000 }, /* Atom Clover Trail/Cloverview (Z27x0) */ { INTEL_ATOM_SALTWELL, ANY, 100000 }, /* Atom Cedar Trail/Cedarview (N2xxx, D2xxx) - * Also matches S12x0 (stepping 9), covered by - * PCI table + * Also matches S12x0 (stepping 9), covered + * by PCI table */ { INTEL_ATOM_SILVERMONT, 9, 110000 }, /* Atom Bay Trail E38xx (embedded) */ { INTEL_ATOM_SILVERMONT, ANY, 90000 }, /* Atom Bay Trail Z37xx (tablet) */ @@ -200,6 +200,7 @@ static int adjust_tjmax(struct cpuinfo_x86 *c, u32 id, struct device *dev) for (i = 0; i < ARRAY_SIZE(tjmax_model_table); i++) { const struct tjmax_model *tm = &tjmax_model_table[i]; + if (c->x86_vfm == tm->vfm && (tm->stepping_mask == ANY || tm->stepping_mask == c->x86_stepping)) @@ -222,8 +223,7 @@ static int adjust_tjmax(struct cpuinfo_x86 *c, u32 id, struct device *dev) err = rdmsr_safe_on_cpu(id, 0x17, &eax, &edx); if (err) { dev_warn(dev, - "Unable to access MSR 0x17, assuming desktop" - " CPU\n"); + "Unable to access MSR 0x17, assuming desktop CPU\n"); usemsr_ee = 0; } else if (c->x86_vfm < INTEL_CORE2_PENRYN && !(eax & 0x10000000)) { @@ -257,8 +257,7 @@ static int adjust_tjmax(struct cpuinfo_x86 *c, u32 id, struct device *dev) err = rdmsr_safe_on_cpu(id, 0xee, &eax, &edx); if (err) { dev_warn(dev, - "Unable to access MSR 0xEE, for Tjmax, left" - " at default\n"); + "Unable to access MSR 0xEE, for Tjmax, left at default\n"); } else if (eax & 0x40000000) { tjmax = tjmax_ee; } @@ -341,7 +340,7 @@ static int max_zones __read_mostly; static struct platform_device **zone_devices; static ssize_t show_label(struct device *dev, - struct device_attribute *devattr, char *buf) + struct device_attribute *devattr, char *buf) { struct platform_data *pdata = dev_get_drvdata(dev); struct temp_data *tdata = container_of(devattr, struct temp_data, sd_attrs[ATTR_LABEL]); @@ -353,7 +352,7 @@ static ssize_t show_label(struct device *dev, } static ssize_t show_crit_alarm(struct device *dev, - struct device_attribute *devattr, char *buf) + struct device_attribute *devattr, char *buf) { u32 eax, edx; struct temp_data *tdata = container_of(devattr, struct temp_data, @@ -367,7 +366,7 @@ static ssize_t show_crit_alarm(struct device *dev, } static ssize_t show_tjmax(struct device *dev, - struct device_attribute *devattr, char *buf) + struct device_attribute *devattr, char *buf) { struct temp_data *tdata = container_of(devattr, struct temp_data, sd_attrs[ATTR_TJMAX]); int tjmax; @@ -380,7 +379,7 @@ static ssize_t show_tjmax(struct device *dev, } static ssize_t show_ttarget(struct device *dev, - struct device_attribute *devattr, char *buf) + struct device_attribute *devattr, char *buf) { struct temp_data *tdata = container_of(devattr, struct temp_data, sd_attrs[ATTR_TTARGET]); int ttarget; @@ -395,7 +394,7 @@ static ssize_t show_ttarget(struct device *dev, } static ssize_t show_temp(struct device *dev, - struct device_attribute *devattr, char *buf) + struct device_attribute *devattr, char *buf) { u32 eax, edx; struct temp_data *tdata = container_of(devattr, struct temp_data, sd_attrs[ATTR_TEMP]); @@ -423,7 +422,6 @@ static ssize_t show_temp(struct device *dev, static int create_core_attrs(struct temp_data *tdata, struct device *dev) { - int i; static ssize_t (*const rd_ptr[TOTAL_ATTRS]) (struct device *dev, struct device_attribute *devattr, char *buf) = { show_label, show_crit_alarm, show_temp, show_tjmax, @@ -431,6 +429,7 @@ static int create_core_attrs(struct temp_data *tdata, struct device *dev) static const char *const suffixes[TOTAL_ATTRS] = { "label", "crit_alarm", "input", "crit", "max" }; + int i; for (i = 0; i < tdata->attr_size; i++) { /* @@ -452,7 +451,6 @@ static int create_core_attrs(struct temp_data *tdata, struct device *dev) return sysfs_create_group(&dev->kobj, &tdata->attr_group); } - static int chk_ucode_version(unsigned int cpu) { struct cpuinfo_x86 *c = &cpu_data(cpu); @@ -774,7 +772,8 @@ static int coretemp_cpu_offline(unsigned int cpu) } return 0; } -static const struct x86_cpu_id __initconst coretemp_ids[] = { + +static const struct x86_cpu_id coretemp_ids[] __initconst = { X86_MATCH_VENDOR_FEATURE(INTEL, X86_FEATURE_DTHERM, NULL), {} }; From 93fd1c2bda0dad0c95a68062cb2232a31b49d028 Mon Sep 17 00:00:00 2001 From: Abdurrahman Hussain Date: Thu, 14 May 2026 20:03:25 -0700 Subject: [PATCH 33/81] dt-bindings: trivial-devices: Add Murata D1U74T PSU The Murata D1U74T-W series are hot-pluggable 1U AC/DC front-end power supplies in the Intel CRPS-185 / OCP M-CRPS form factor. Each variant delivers a 12 V main output plus a 12 V standby output from a wide AC input (90-264 Vac) or HVDC supply, and includes an internal variable-speed cooling fan and on-board voltage, current, power, fan-speed, and temperature telemetry. The host-side digital interface is a PMBus 1.2 port on I2C. The PSU's other electrical signals (status, alert, current-share) live on the CRPS edge connector and are consumed by the chassis controller rather than the host SoC, so there are no host-described supplies, GPIOs, clocks, or interrupts. Add the compatible to trivial-devices.yaml rather than carrying a standalone binding file. Signed-off-by: Abdurrahman Hussain Acked-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20260514-d1u74t-v4-1-1f1ee7b002ec@nexthop.ai 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 d7940c488ab6..1e870bb1bb6e 100644 --- a/Documentation/devicetree/bindings/trivial-devices.yaml +++ b/Documentation/devicetree/bindings/trivial-devices.yaml @@ -358,6 +358,8 @@ properties: - mps,mp9941 # Monolithic Power Systems Inc. digital step-down converter mp9945 - mps,mp9945 + # Murata D1U74T-W power supply unit + - murata,d1u74t # Temperature sensor with integrated fan control - national,lm63 # Temperature sensor with integrated fan control From ef68b3dbbe3968b1b409e4fe5190c187d3bc39ef Mon Sep 17 00:00:00 2001 From: Abdurrahman Hussain Date: Thu, 14 May 2026 20:03:26 -0700 Subject: [PATCH 34/81] hwmon: (pmbus/d1u74t) Add Murata D1U74T PSU driver Add PMBUS driver for Murata D1U74T power supplies. Signed-off-by: Abdurrahman Hussain Link: https://lore.kernel.org/r/20260514-d1u74t-v4-2-1f1ee7b002ec@nexthop.ai [groeck: Dropped inappropriate tags; added missing include files] Signed-off-by: Guenter Roeck --- Documentation/hwmon/d1u74t.rst | 81 +++++++++++++++++++++++++++++++ Documentation/hwmon/index.rst | 1 + MAINTAINERS | 7 +++ drivers/hwmon/pmbus/Kconfig | 9 ++++ drivers/hwmon/pmbus/Makefile | 1 + drivers/hwmon/pmbus/d1u74t.c | 88 ++++++++++++++++++++++++++++++++++ 6 files changed, 187 insertions(+) create mode 100644 Documentation/hwmon/d1u74t.rst create mode 100644 drivers/hwmon/pmbus/d1u74t.c diff --git a/Documentation/hwmon/d1u74t.rst b/Documentation/hwmon/d1u74t.rst new file mode 100644 index 000000000000..3a9eedbda483 --- /dev/null +++ b/Documentation/hwmon/d1u74t.rst @@ -0,0 +1,81 @@ +.. SPDX-License-Identifier: GPL-2.0-or-later + +Kernel driver d1u74t +==================== + +Supported chips: + + * Murata D1U74T + + Prefix: 'd1u74t' + + Addresses scanned: - + + Datasheet: Publicly available at the Murata website + +Authors: + Abdurrahman Hussain + + +Description +----------- + +This driver implements support for Murata D1U74T Power Supply with +PMBus support. + +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. + + +Sysfs entries +------------- + +======================= ====================================================== +curr1_label "iin" +curr1_input Measured input current +curr1_alarm Input current alarm +curr1_rated_max Maximum rated input current + +curr2_label "iout1" +curr2_input Measured output current +curr2_max Maximum output current +curr2_max_alarm Output current high alarm +curr2_crit Critical high output current +curr2_crit_alarm Output current critical high alarm +curr2_rated_max Maximum rated output current + +in1_label "vin" +in1_input Measured input voltage +in1_alarm Input voltage alarm +in1_rated_min Minimum rated input voltage +in1_rated_max Maximum rated input voltage + +in2_label "vout1" +in2_input Measured output voltage +in2_alarm Output voltage alarm +in2_rated_min Minimum rated output voltage +in2_rated_max Maximum rated output voltage + +power1_label "pin" +power1_input Measured input power +power1_alarm Input power alarm +power1_rated_max Maximum rated input power + +temp[1-3]_input Measured temperature +temp[1-3]_max Maximum temperature +temp[1-3]_max_alarm Maximum temperature alarm +temp[1-3]_rated_max Maximum rated temperature + +fan1_alarm Fan 1 warning +fan1_fault Fan 1 fault +fan1_input Fan 1 speed in RPM +fan1_target Fan 1 target +======================= ====================================================== diff --git a/Documentation/hwmon/index.rst b/Documentation/hwmon/index.rst index 78c783f11e25..e880c6ca84f0 100644 --- a/Documentation/hwmon/index.rst +++ b/Documentation/hwmon/index.rst @@ -61,6 +61,7 @@ Hardware Monitoring Kernel Drivers corsair-psu cros_ec_hwmon crps + d1u74t da9052 da9055 dell-smm-hwmon diff --git a/MAINTAINERS b/MAINTAINERS index 5d4453414e77..d57da4881737 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -18284,6 +18284,13 @@ F: drivers/mux/ F: include/dt-bindings/mux/ F: include/linux/mux/ +MURATA D1U74T PSU DRIVER +M: Abdurrahman Hussain +L: linux-hwmon@vger.kernel.org +S: Maintained +F: Documentation/hwmon/d1u74t.rst +F: drivers/hwmon/pmbus/d1u74t.c + MUSB MULTIPOINT HIGH SPEED DUAL-ROLE CONTROLLER M: Bin Liu L: linux-usb@vger.kernel.org diff --git a/drivers/hwmon/pmbus/Kconfig b/drivers/hwmon/pmbus/Kconfig index c3bb6df2655a..64f38654f4e7 100644 --- a/drivers/hwmon/pmbus/Kconfig +++ b/drivers/hwmon/pmbus/Kconfig @@ -113,6 +113,15 @@ config SENSORS_CRPS This driver can also be built as a module. If so, the module will be called crps. +config SENSORS_D1U74T + tristate "Murata D1U74T Power Supply" + help + If you say yes here you get hardware monitoring support for the Murata + D1U74T Power Supply. + + This driver can also be built as a module. If so, the module will + be called d1u74t. + config SENSORS_DELTA_AHE50DC_FAN tristate "Delta AHE-50DC fan control module" help diff --git a/drivers/hwmon/pmbus/Makefile b/drivers/hwmon/pmbus/Makefile index efd70b00ca2e..1f2c73b71953 100644 --- a/drivers/hwmon/pmbus/Makefile +++ b/drivers/hwmon/pmbus/Makefile @@ -80,3 +80,4 @@ obj-$(CONFIG_SENSORS_XDPE1A2G7B) += xdpe1a2g7b.o obj-$(CONFIG_SENSORS_ZL6100) += zl6100.o obj-$(CONFIG_SENSORS_PIM4328) += pim4328.o obj-$(CONFIG_SENSORS_CRPS) += crps.o +obj-$(CONFIG_SENSORS_D1U74T) += d1u74t.o diff --git a/drivers/hwmon/pmbus/d1u74t.c b/drivers/hwmon/pmbus/d1u74t.c new file mode 100644 index 000000000000..752df3af9c1a --- /dev/null +++ b/drivers/hwmon/pmbus/d1u74t.c @@ -0,0 +1,88 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright 2026 Nexthop Systems. + */ + +#include +#include +#include +#include +#include + +#include "pmbus.h" + +static const struct i2c_device_id d1u74t_id[] = { + { "d1u74t" }, + {}, +}; +MODULE_DEVICE_TABLE(i2c, d1u74t_id); + +static struct pmbus_driver_info d1u74t_info = { + .pages = 1, + /* PSU uses default linear data format. */ + .func[0] = PMBUS_HAVE_PIN | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT | + PMBUS_HAVE_IIN | PMBUS_HAVE_VIN | PMBUS_HAVE_STATUS_INPUT | + PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_TEMP | + PMBUS_HAVE_TEMP2 | PMBUS_HAVE_TEMP3 | + PMBUS_HAVE_STATUS_TEMP | PMBUS_HAVE_FAN12 | + PMBUS_HAVE_STATUS_FAN12, +}; + +static int d1u74t_probe(struct i2c_client *client) +{ + char buf[I2C_SMBUS_BLOCK_MAX + 2] = { 0 }; + struct device *dev = &client->dev; + int rc; + + rc = i2c_smbus_read_block_data(client, PMBUS_MFR_ID, buf); + if (rc < 0) + return dev_err_probe(dev, rc, "Failed to read PMBUS_MFR_ID\n"); + + if (rc != 9 || strncmp(buf, "Murata-PS", 9)) { + buf[rc] = '\0'; + return dev_err_probe(dev, -ENODEV, + "Unsupported Manufacturer ID '%s'\n", + buf); + } + + rc = i2c_smbus_read_block_data(client, PMBUS_MFR_MODEL, buf); + if (rc < 0) + return dev_err_probe(dev, rc, + "Failed to read PMBUS_MFR_MODEL\n"); + + if (rc < 8 || strncmp(buf, "D1U74T-W", 8)) { + buf[rc] = '\0'; + return dev_err_probe(dev, -ENODEV, "Model '%s' not supported\n", + buf); + } + + rc = pmbus_do_probe(client, &d1u74t_info); + if (rc) + return dev_err_probe(dev, rc, "Failed to probe\n"); + + return 0; +} + +static const struct of_device_id d1u74t_of_match[] = { + { + .compatible = "murata,d1u74t", + }, + {}, +}; +MODULE_DEVICE_TABLE(of, d1u74t_of_match); + +static struct i2c_driver d1u74t_driver = { + .driver = { + .name = "d1u74t", + .of_match_table = d1u74t_of_match, + }, + .probe = d1u74t_probe, + .id_table = d1u74t_id, +}; + +module_i2c_driver(d1u74t_driver); + +MODULE_AUTHOR("Abdurrahman Hussain"); +MODULE_DESCRIPTION("PMBus driver for Murata D1U74T-W power supplies"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("PMBUS"); From 6222b8aecc39c654dab1ec7b0b004ca7929cb8d9 Mon Sep 17 00:00:00 2001 From: Hassan Maazu Date: Sat, 16 May 2026 22:09:26 +0000 Subject: [PATCH 35/81] Documentation: hwmon: fix typo in heading for max31730 Generated heading & link to driver doc for max31730 wrongly named max31790 under hwmon docs. This patch fixes typo so link to max31730 is easily identifiable without confusion with max31790. Signed-off-by: Hassan Maazu Acked-by: Randy Dunlap Link: https://lore.kernel.org/r/6-vpIVJnccYzmznZMj4zfXmOKnHhtaXdeyJqyqTm3KJwLIEj3iSiWzBVxHnBhkNZHZ8E3KfHn7pYQSt3xrfQOQeN5RCJNnBVwmgyJcaw_zM=@proton.me Signed-off-by: Guenter Roeck --- Documentation/hwmon/max31730.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/hwmon/max31730.rst b/Documentation/hwmon/max31730.rst index 1c5a32b64187..0936ba2eac24 100644 --- a/Documentation/hwmon/max31730.rst +++ b/Documentation/hwmon/max31730.rst @@ -1,4 +1,4 @@ -Kernel driver max31790 +Kernel driver max31730 ====================== Supported chips: From a91966b9fe357a22c2d02038e6e7d3236f37a785 Mon Sep 17 00:00:00 2001 From: Chen-Shi-Hong Date: Sun, 17 May 2026 20:52:21 +0800 Subject: [PATCH 36/81] docs: hwmon: htu31: document debugfs serial_number Document the debugfs serial_number file exposed by the htu31 driver. The driver creates a debugfs entry for the sensor serial number, but the documentation currently only describes the sysfs interface. Signed-off-by: Chen-Shi-Hong Link: https://lore.kernel.org/r/20260517125320.2196-1-eric039eric@gmail.com Signed-off-by: Guenter Roeck --- Documentation/hwmon/htu31.rst | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Documentation/hwmon/htu31.rst b/Documentation/hwmon/htu31.rst index ccde84264643..9ab774dcf65d 100644 --- a/Documentation/hwmon/htu31.rst +++ b/Documentation/hwmon/htu31.rst @@ -35,3 +35,10 @@ temp1_input: temperature input humidity1_input: humidity input heater_enable: heater control =================== ================= + +debugfs-Interface +----------------- + +=================== ========================================= +serial_number: unique serial number of the sensor +=================== ========================================= From 563d71205f86dff25c36e3911e3b5faebc9cf35f Mon Sep 17 00:00:00 2001 From: Roman Bakshansky Date: Sun, 17 May 2026 18:20:27 +0300 Subject: [PATCH 37/81] docs: hwmon: (coretemp) fix outdated documentation - Remove broken Intel wiki link; add Intel SDM download page link - Fix description of tempX_max to clarify it is not Core2-only - Correct tempX_label string for package temperature (changed in commit 2bc0e6d07ee5 ("hwmon: (coretemp) rearrange tjmax handing code")) Signed-off-by: Roman Bakshansky Link: https://lore.kernel.org/r/20260517152027.350356-1-bakshansky.lists@gmail.com Signed-off-by: Guenter Roeck --- Documentation/hwmon/coretemp.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Documentation/hwmon/coretemp.rst b/Documentation/hwmon/coretemp.rst index 7a5fbb37b0f3..f63b21f24d42 100644 --- a/Documentation/hwmon/coretemp.rst +++ b/Documentation/hwmon/coretemp.rst @@ -22,8 +22,7 @@ Supported chips: Intel 64 and IA-32 Architectures Software Developer's Manual Volume 3A: System Programming Guide - - http://softwarecommunity.intel.com/Wiki/Mobility/720.htm + (https://www.intel.com/content/www/us/en/developer/articles/technical/intel-sdm.html) Author: Rudolf Marek @@ -51,12 +50,13 @@ All Sysfs entries are named with their core_id (represented here by 'X'). ================= ======================================================== tempX_input Core temperature (in millidegrees Celsius). -tempX_max All cooling devices should be turned on (on Core2). +tempX_max Maximum recommended operating temperature (Tcontrol). + All cooling devices should be turned on. tempX_crit Maximum junction temperature (in millidegrees Celsius). tempX_crit_alarm Set when Out-of-spec bit is set, never clears. Correct CPU operation is no longer guaranteed. tempX_label Contains string "Core X", where X is processor - number. For Package temp, this will be "Physical id Y", + number. For Package temp, this will be "Package id Y", where Y is the package number. ================= ======================================================== From d1e720c7328e046049b792d03fae093d4d3a72e4 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Wed, 13 May 2026 07:14:33 -0700 Subject: [PATCH 38/81] hwmon: Support guard() and scoped_guard for subsystem locks Add support for guard() and scoped_guard() for the hwmon subsystem lock to simplify its use. Signed-off-by: Guenter Roeck --- Documentation/hwmon/hwmon-kernel-api.rst | 7 ++++--- include/linux/hwmon.h | 3 +++ 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/Documentation/hwmon/hwmon-kernel-api.rst b/Documentation/hwmon/hwmon-kernel-api.rst index 1d7f1397a827..9fcde32a140d 100644 --- a/Documentation/hwmon/hwmon-kernel-api.rst +++ b/Documentation/hwmon/hwmon-kernel-api.rst @@ -85,9 +85,10 @@ removal. When using ``[devm_]hwmon_device_register_with_info()`` to register the hardware monitoring device, accesses using the associated access functions are serialised by the hardware monitoring core. If a driver needs locking -for other functions such as interrupt handlers or for attributes which are -fully implemented in the driver, hwmon_lock() and hwmon_unlock() can be used -to ensure that calls to those functions are serialized. +for other functions such as interrupt handlers, attributes which are fully +implemented in the driver, or debugfs functions, hwmon_lock() and hwmon_unlock() +can be used to ensure that calls to those functions are serialized. Those +functions also support guard() and scoped_guard() variants. Using devm_hwmon_device_register_with_info() -------------------------------------------- diff --git a/include/linux/hwmon.h b/include/linux/hwmon.h index 301a83afbd66..f5330e7ae247 100644 --- a/include/linux/hwmon.h +++ b/include/linux/hwmon.h @@ -13,6 +13,7 @@ #define _HWMON_H_ #include +#include struct device; struct attribute_group; @@ -495,6 +496,8 @@ char *devm_hwmon_sanitize_name(struct device *dev, const char *name); void hwmon_lock(struct device *dev); void hwmon_unlock(struct device *dev); +DEFINE_GUARD(hwmon_lock, struct device *, hwmon_lock(_T), hwmon_unlock(_T)) + /** * hwmon_is_bad_char - Is the char invalid in a hwmon name * @ch: the char to be considered From bf3bd1f587f5a56ce60173324db27e5e50aa56b0 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Wed, 13 May 2026 07:15:54 -0700 Subject: [PATCH 39/81] hwmon: (lm90) Use guard() and scoped_guard() to acquire subsystem lock Use guard() and scoped_guard() instead of hwmon_lock() / hwmon_unlock() to acquire and release the hardware monitoring subsystem lock. Signed-off-by: Guenter Roeck --- drivers/hwmon/lm90.c | 33 ++++++++++++++------------------- 1 file changed, 14 insertions(+), 19 deletions(-) diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c index c78c96e1bd83..4b9c0ccdf260 100644 --- a/drivers/hwmon/lm90.c +++ b/drivers/hwmon/lm90.c @@ -1226,13 +1226,8 @@ static int lm90_update_alarms_locked(struct lm90_data *data, bool force) static int lm90_update_alarms(struct lm90_data *data, bool force) { - int err; - - hwmon_lock(data->hwmon_dev); - err = lm90_update_alarms_locked(data, force); - hwmon_unlock(data->hwmon_dev); - - return err; + guard(hwmon_lock)(data->hwmon_dev); + return lm90_update_alarms_locked(data, force); } static void lm90_alert_work(struct work_struct *__work) @@ -2598,9 +2593,9 @@ static void lm90_stop_work(void *_data) { struct lm90_data *data = _data; - hwmon_lock(data->hwmon_dev); - data->shutdown = true; - hwmon_unlock(data->hwmon_dev); + scoped_guard(hwmon_lock, data->hwmon_dev) { + data->shutdown = true; + } cancel_delayed_work_sync(&data->alert_work); cancel_work_sync(&data->report_work); } @@ -2946,17 +2941,17 @@ static void lm90_alert(struct i2c_client *client, enum i2c_alert_protocol type, */ struct lm90_data *data = i2c_get_clientdata(client); - hwmon_lock(data->hwmon_dev); - if (!data->shutdown && (data->flags & LM90_HAVE_BROKEN_ALERT) && - (data->current_alarms & data->alert_alarms)) { - if (!(data->config & 0x80)) { - dev_dbg(&client->dev, "Disabling ALERT#\n"); - lm90_update_confreg(data, data->config | 0x80); + scoped_guard(hwmon_lock, data->hwmon_dev) { + if (!data->shutdown && (data->flags & LM90_HAVE_BROKEN_ALERT) && + (data->current_alarms & data->alert_alarms)) { + if (!(data->config & 0x80)) { + dev_dbg(&client->dev, "Disabling ALERT#\n"); + lm90_update_confreg(data, data->config | 0x80); + } + schedule_delayed_work(&data->alert_work, + max_t(int, HZ, msecs_to_jiffies(data->update_interval))); } - schedule_delayed_work(&data->alert_work, - max_t(int, HZ, msecs_to_jiffies(data->update_interval))); } - hwmon_unlock(data->hwmon_dev); } else { dev_dbg(&client->dev, "Everything OK\n"); } From bfca8aba91dbfac0cbf84c04dc899b482c4e0b0d Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Wed, 13 May 2026 07:17:03 -0700 Subject: [PATCH 40/81] hwmon: (ina2xx) Use scoped_guard() to acquire the subsystem lock Use scoped_guard() instead of hwmon_lock() / hwmon_unlock() to acquire and release the hardware monitoring subsystem lock. Signed-off-by: Guenter Roeck --- drivers/hwmon/ina2xx.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/hwmon/ina2xx.c b/drivers/hwmon/ina2xx.c index 32bf4595bcb4..c4742e84b999 100644 --- a/drivers/hwmon/ina2xx.c +++ b/drivers/hwmon/ina2xx.c @@ -875,11 +875,11 @@ static ssize_t shunt_resistor_store(struct device *dev, if (status < 0) return status; - hwmon_lock(dev); - status = ina2xx_set_shunt(data, val); - hwmon_unlock(dev); - if (status < 0) - return status; + scoped_guard(hwmon_lock, dev) { + status = ina2xx_set_shunt(data, val); + if (status < 0) + return status; + } return count; } From 692c9f26424be53b871f9ef5eca8085824137066 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Wed, 13 May 2026 07:22:02 -0700 Subject: [PATCH 41/81] hwmon: (adt7411) Use scoped_guard() to acquire the subsystem lock Use scoped_guard() instead of hwmon_lock() / hwmon_unlock() to acquire and release the hardware monitoring subsystem lock. Signed-off-by: Guenter Roeck --- drivers/hwmon/adt7411.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/hwmon/adt7411.c b/drivers/hwmon/adt7411.c index 5dce2a701725..598f31647544 100644 --- a/drivers/hwmon/adt7411.c +++ b/drivers/hwmon/adt7411.c @@ -169,11 +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 */ - data->next_update = jiffies; - hwmon_unlock(dev); + scoped_guard(hwmon_lock, dev) { + ret = adt7411_modify_bit(client, s_attr2->index, s_attr2->nr, flag); + /* force update */ + data->next_update = jiffies; + } return ret < 0 ? ret : count; } From 582b6e87dcd77ed28b13fd489d2738ffa19b11f3 Mon Sep 17 00:00:00 2001 From: Flaviu Nistor Date: Mon, 18 May 2026 10:23:37 +0300 Subject: [PATCH 42/81] hwmon: (lm75) Add explicit header include Since device_property_read_string() is used in the probe function add explicit include for linux/mod_devicetable.h and linux/property.h rather than having implicit inclusions. Header linux/of.h can be removed and also of_match_ptr() improving non-Device Tree compatibility of the driver. Remove __maybe_unuse because it is not needed anymore. Signed-off-by: Flaviu Nistor Link: https://lore.kernel.org/r/20260518072337.4918-1-flaviu.nistor@gmail.com Signed-off-by: Guenter Roeck --- drivers/hwmon/lm75.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/hwmon/lm75.c b/drivers/hwmon/lm75.c index 1e8af5805f2f..104149a03bad 100644 --- a/drivers/hwmon/lm75.c +++ b/drivers/hwmon/lm75.c @@ -14,7 +14,8 @@ #include #include #include -#include +#include +#include #include #include #include @@ -899,7 +900,7 @@ static int lm75_i3c_probe(struct i3c_device *i3cdev) return lm75_generic_probe(dev, id_data->name, id_data->type, 0, regmap); } -static const struct of_device_id __maybe_unused lm75_of_match[] = { +static const struct of_device_id lm75_of_match[] = { { .compatible = "adi,adt75", .data = (void *)adt75 @@ -1152,7 +1153,7 @@ static struct i2c_driver lm75_i2c_driver = { .class = I2C_CLASS_HWMON, .driver = { .name = "lm75", - .of_match_table = of_match_ptr(lm75_of_match), + .of_match_table = lm75_of_match, .pm = LM75_DEV_PM_OPS, }, .probe = lm75_i2c_probe, From 3b7f59ffacb131037f592d3a4a7342c6773249a4 Mon Sep 17 00:00:00 2001 From: Shubham Chakraborty Date: Sun, 17 May 2026 13:34:43 +0530 Subject: [PATCH 43/81] soc: bcm2835: raspberrypi-firmware: Add voltage domain IDs Add Raspberry Pi firmware voltage domain identifiers for the mailbox property interface. Also add the voltage request structure used with RPI_FIRMWARE_GET_VOLTAGE so firmware clients can share the common API definition from the firmware header. Signed-off-by: Shubham Chakraborty Acked-by: Florian Fainelli Link: https://lore.kernel.org/r/20260517080445.103962-2-chakrabortyshubham66@gmail.com Signed-off-by: Guenter Roeck --- include/soc/bcm2835/raspberrypi-firmware.h | 25 ++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/include/soc/bcm2835/raspberrypi-firmware.h b/include/soc/bcm2835/raspberrypi-firmware.h index e1f87fbfe554..975bef529854 100644 --- a/include/soc/bcm2835/raspberrypi-firmware.h +++ b/include/soc/bcm2835/raspberrypi-firmware.h @@ -156,6 +156,31 @@ enum rpi_firmware_clk_id { RPI_FIRMWARE_NUM_CLK_ID, }; +enum rpi_firmware_volt_id { + RPI_FIRMWARE_VOLT_ID_CORE = 1, + RPI_FIRMWARE_VOLT_ID_SDRAM_C = 2, + RPI_FIRMWARE_VOLT_ID_SDRAM_P = 3, + RPI_FIRMWARE_VOLT_ID_SDRAM_I = 4, + RPI_FIRMWARE_NUM_VOLT_ID, +}; + +/** + * struct rpi_firmware_get_voltage_request - Firmware request for a voltage + * @id: ID of the voltage being queried + * @value: Voltage in microvolts. Set by the firmware. + * + * Used by @RPI_FIRMWARE_GET_VOLTAGE. + */ +struct rpi_firmware_get_voltage_request { + __le32 id; + __le32 value; +} __packed; + +#define RPI_FIRMWARE_GET_VOLTAGE_REQUEST(_id) \ + { \ + .id = cpu_to_le32(_id), \ + } + /** * struct rpi_firmware_clk_rate_request - Firmware Request for a rate * @id: ID of the clock being queried From 78a3e36eec06a0dc4a205c753e7754e9f9408fba Mon Sep 17 00:00:00 2001 From: Shubham Chakraborty Date: Sun, 17 May 2026 13:34:44 +0530 Subject: [PATCH 44/81] hwmon: (raspberrypi) Add voltage input support Extend the raspberrypi-hwmon driver to expose firmware-provided voltage measurements through the hwmon subsystem. The driver now exports the following voltage inputs: - in0_input (core) - in1_input (sdram_c) - in2_input (sdram_i) - in3_input (sdram_p) Voltage values returned by firmware are converted from microvolts to millivolts as expected by the hwmon subsystem. Update the documentation related to it. The existing undervoltage sticky alarm handling is preserved and associated with the first voltage channel. Tested in - - Raspberry Pi 3b+ (Linux raspberrypi 6.12.75+rpt-rpi-v8 #1 SMP PREEMPT Debian 1:6.12.75-1+rpt1 (2026-03-11) aarch64 GNU/Linux) Signed-off-by: Shubham Chakraborty Reviewed-by: Florian Fainelli Link: https://lore.kernel.org/r/20260517080445.103962-3-chakrabortyshubham66@gmail.com [groeck: Added missing empty line after declaration] Signed-off-by: Guenter Roeck --- Documentation/hwmon/raspberrypi-hwmon.rst | 15 ++- drivers/hwmon/raspberrypi-hwmon.c | 128 +++++++++++++++++++++- 2 files changed, 138 insertions(+), 5 deletions(-) diff --git a/Documentation/hwmon/raspberrypi-hwmon.rst b/Documentation/hwmon/raspberrypi-hwmon.rst index 8038ade36490..db315184b861 100644 --- a/Documentation/hwmon/raspberrypi-hwmon.rst +++ b/Documentation/hwmon/raspberrypi-hwmon.rst @@ -20,6 +20,17 @@ undervoltage conditions. Sysfs entries ------------- -======================= ================== +======================= ====================================================== +in0_input Core voltage in millivolts +in1_input SDRAM controller voltage in millivolts +in2_input SDRAM I/O voltage in millivolts +in3_input SDRAM PHY voltage in millivolts +in0_label "core" +in1_label "sdram_c" +in2_label "sdram_i" +in3_label "sdram_p" in0_lcrit_alarm Undervoltage alarm -======================= ================== +======================= ====================================================== + +The voltage inputs and labels are only exposed if the firmware reports support +for the corresponding voltage ID. diff --git a/drivers/hwmon/raspberrypi-hwmon.c b/drivers/hwmon/raspberrypi-hwmon.c index a2938881ccd2..a458a858b0ac 100644 --- a/drivers/hwmon/raspberrypi-hwmon.c +++ b/drivers/hwmon/raspberrypi-hwmon.c @@ -5,6 +5,7 @@ * Based on firmware/raspberrypi.c by Noralf Trønnes * * Copyright (C) 2018 Stefan Wahren + * Copyright (C) 2026 Shubham Chakraborty */ #include #include @@ -21,10 +22,18 @@ struct rpi_hwmon_data { struct device *hwmon_dev; struct rpi_firmware *fw; + u32 valid_inputs; u32 last_throttled; struct delayed_work get_values_poll_work; }; +static const char * const rpi_hwmon_labels[] = { + "core", + "sdram_c", + "sdram_i", + "sdram_p", +}; + static void rpi_firmware_get_throttled(struct rpi_hwmon_data *data) { u32 new_uv, old_uv, value; @@ -56,6 +65,22 @@ static void rpi_firmware_get_throttled(struct rpi_hwmon_data *data) hwmon_notify_event(data->hwmon_dev, hwmon_in, hwmon_in_lcrit_alarm, 0); } +static int rpi_firmware_get_voltage(struct rpi_hwmon_data *data, u32 id, + long *val) +{ + struct rpi_firmware_get_voltage_request packet = + RPI_FIRMWARE_GET_VOLTAGE_REQUEST(id); + int ret; + + ret = rpi_firmware_property(data->fw, RPI_FIRMWARE_GET_VOLTAGE, + &packet, sizeof(packet)); + if (ret) + return ret; + + *val = le32_to_cpu(packet.value) / 1000; + return 0; +} + static void get_values_poll(struct work_struct *work) { struct rpi_hwmon_data *data; @@ -77,19 +102,94 @@ static int rpi_read(struct device *dev, enum hwmon_sensor_types type, { struct rpi_hwmon_data *data = dev_get_drvdata(dev); - *val = !!(data->last_throttled & UNDERVOLTAGE_STICKY_BIT); + if (type == hwmon_in) { + switch (attr) { + case hwmon_in_input: + switch (channel) { + case 0: + return rpi_firmware_get_voltage(data, + RPI_FIRMWARE_VOLT_ID_CORE, + val); + case 1: + return rpi_firmware_get_voltage(data, + RPI_FIRMWARE_VOLT_ID_SDRAM_C, + val); + case 2: + return rpi_firmware_get_voltage(data, + RPI_FIRMWARE_VOLT_ID_SDRAM_I, + val); + case 3: + return rpi_firmware_get_voltage(data, + RPI_FIRMWARE_VOLT_ID_SDRAM_P, + val); + default: + return -EOPNOTSUPP; + } + case hwmon_in_lcrit_alarm: + if (channel == 0) { + *val = !!(data->last_throttled & UNDERVOLTAGE_STICKY_BIT); + return 0; + } + return -EOPNOTSUPP; + default: + return -EOPNOTSUPP; + } + } + + return -EOPNOTSUPP; +} + +static int rpi_read_string(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, const char **str) +{ + if (type == hwmon_in && attr == hwmon_in_label) { + if (channel >= ARRAY_SIZE(rpi_hwmon_labels)) + return -EOPNOTSUPP; + + *str = rpi_hwmon_labels[channel]; + return 0; + } + + return -EOPNOTSUPP; +} + +static umode_t rpi_is_visible(const void *_data, enum hwmon_sensor_types type, + u32 attr, int channel) +{ + const struct rpi_hwmon_data *data = _data; + + if (type == hwmon_in) { + switch (attr) { + case hwmon_in_input: + case hwmon_in_label: + if (!(data->valid_inputs & BIT(channel))) + return 0; + return 0444; + case hwmon_in_lcrit_alarm: + if (channel == 0) + return 0444; + return 0; + default: + return 0; + } + } + return 0; } static const struct hwmon_channel_info * const rpi_info[] = { HWMON_CHANNEL_INFO(in, - HWMON_I_LCRIT_ALARM), + HWMON_I_INPUT | HWMON_I_LABEL | HWMON_I_LCRIT_ALARM, + HWMON_I_INPUT | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_LABEL), NULL }; static const struct hwmon_ops rpi_hwmon_ops = { - .visible = 0444, + .is_visible = rpi_is_visible, .read = rpi_read, + .read_string = rpi_read_string, }; static const struct hwmon_chip_info rpi_chip_info = { @@ -101,6 +201,7 @@ static int rpi_hwmon_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct rpi_hwmon_data *data; + long voltage; int ret; data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); @@ -110,6 +211,26 @@ static int rpi_hwmon_probe(struct platform_device *pdev) /* Parent driver assure that firmware is correct */ data->fw = dev_get_drvdata(dev->parent); + ret = rpi_firmware_get_voltage(data, RPI_FIRMWARE_VOLT_ID_CORE, + &voltage); + if (!ret) + data->valid_inputs |= BIT(0); + + ret = rpi_firmware_get_voltage(data, RPI_FIRMWARE_VOLT_ID_SDRAM_C, + &voltage); + if (!ret) + data->valid_inputs |= BIT(1); + + ret = rpi_firmware_get_voltage(data, RPI_FIRMWARE_VOLT_ID_SDRAM_I, + &voltage); + if (!ret) + data->valid_inputs |= BIT(2); + + ret = rpi_firmware_get_voltage(data, RPI_FIRMWARE_VOLT_ID_SDRAM_P, + &voltage); + if (!ret) + data->valid_inputs |= BIT(3); + data->hwmon_dev = devm_hwmon_device_register_with_info(dev, "rpi_volt", data, &rpi_chip_info, @@ -159,6 +280,7 @@ static struct platform_driver rpi_hwmon_driver = { module_platform_driver(rpi_hwmon_driver); MODULE_AUTHOR("Stefan Wahren "); +MODULE_AUTHOR("Shubham Chakraborty "); MODULE_DESCRIPTION("Raspberry Pi voltage sensor driver"); MODULE_LICENSE("GPL v2"); MODULE_ALIAS("platform:raspberrypi-hwmon"); From eb0d491e2787aa359f0d930151ff6e5e5df70fb9 Mon Sep 17 00:00:00 2001 From: Shubham Chakraborty Date: Sun, 17 May 2026 13:34:45 +0530 Subject: [PATCH 45/81] hwmon: (raspberrypi) Fix delayed-work teardown race The delayed polling work rearms itself from the work function, so use explicit delayed-work setup and cleanup instead of devm_delayed_work_autocancel(). Initialize the delayed work with INIT_DELAYED_WORK() and register a devres cleanup action that calls disable_delayed_work_sync() during teardown. This addresses the concern raised during review about the polling work being able to requeue itself while the driver is being removed. Signed-off-by: Shubham Chakraborty Link: https://lore.kernel.org/r/20260517080445.103962-4-chakrabortyshubham66@gmail.com Signed-off-by: Guenter Roeck --- drivers/hwmon/raspberrypi-hwmon.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/drivers/hwmon/raspberrypi-hwmon.c b/drivers/hwmon/raspberrypi-hwmon.c index a458a858b0ac..7b1d2829f012 100644 --- a/drivers/hwmon/raspberrypi-hwmon.c +++ b/drivers/hwmon/raspberrypi-hwmon.c @@ -8,7 +8,6 @@ * Copyright (C) 2026 Shubham Chakraborty */ #include -#include #include #include #include @@ -97,6 +96,13 @@ static void get_values_poll(struct work_struct *work) schedule_delayed_work(&data->get_values_poll_work, 2 * HZ); } +static void rpi_hwmon_cancel_poll_work(void *res) +{ + struct rpi_hwmon_data *data = res; + + disable_delayed_work_sync(&data->get_values_poll_work); +} + static int rpi_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel, long *val) { @@ -238,8 +244,8 @@ static int rpi_hwmon_probe(struct platform_device *pdev) if (IS_ERR(data->hwmon_dev)) return PTR_ERR(data->hwmon_dev); - ret = devm_delayed_work_autocancel(dev, &data->get_values_poll_work, - get_values_poll); + INIT_DELAYED_WORK(&data->get_values_poll_work, get_values_poll); + ret = devm_add_action_or_reset(dev, rpi_hwmon_cancel_poll_work, data); if (ret) return ret; platform_set_drvdata(pdev, data); From e7c1f9eb6e9f1138241f3bf28f8181f4f5ccd5eb Mon Sep 17 00:00:00 2001 From: Abdurrahman Hussain Date: Wed, 20 May 2026 15:42:40 -0700 Subject: [PATCH 46/81] hwmon: (pmbus/adm1266) add clear_blackbox debugfs entry The ADM1266 blackbox can be configured in two recording modes via BLACKBOX_CONFIG[0]: cyclic, where the device overwrites the oldest record once the 32-record buffer fills, and single, where it stops recording until the buffer is cleared. Deployments that need to preserve the full record history across multiple fault episodes typically run in single mode and need a way to clear the buffer after the records have been collected. Expose a write-only debugfs file alongside sequencer_state. Writing any data to it issues the documented clear-blackbox sub-command: a 2-byte block-write to READ_BLACKBOX (0xDE) with payload {0xFE, 0x00} (datasheet Rev. D). The clear is taken under pmbus_lock because READ_BLACKBOX is also used by adm1266_nvmem_read_blackbox() to walk records one at a time; both paths run under pmbus_lock so the clear cannot interleave mid-iteration and corrupt the read sequence. Signed-off-by: Abdurrahman Hussain Assisted-by: Claude-Code:claude-opus-4-7 Assisted-by: sashiko:gemini-3.1-pro-preview Link: https://lore.kernel.org/r/20260520-adm1266-v5-1-c72ef1fac1ea@nexthop.ai Signed-off-by: Guenter Roeck --- drivers/hwmon/pmbus/adm1266.c | 36 +++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/drivers/hwmon/pmbus/adm1266.c b/drivers/hwmon/pmbus/adm1266.c index 7f4dbc98d92a..aa353b6b9c99 100644 --- a/drivers/hwmon/pmbus/adm1266.c +++ b/drivers/hwmon/pmbus/adm1266.c @@ -371,6 +371,40 @@ static int adm1266_firmware_revision_read(struct seq_file *s, void *pdata) return 0; } +/* + * Clearing the blackbox is required when the device is configured in + * single-recording mode (BLACKBOX_CONFIG[0] = 0): once the 32-record + * buffer is full the device stops recording until cleared. + * + * The clear is issued as a 2-byte block-write to READ_BLACKBOX with + * payload {0xFE, 0x00} per the datasheet. READ_BLACKBOX is also used + * by adm1266_nvmem_read_blackbox() to walk records one at a time; + * both paths run under pmbus_lock so the clear cannot interleave + * mid-iteration and corrupt the read sequence. + */ +static ssize_t adm1266_clear_blackbox_write(struct file *file, const char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct i2c_client *client = file->private_data; + u8 payload[2] = { 0xFE, 0x00 }; + int ret; + + guard(pmbus_lock)(client); + ret = i2c_smbus_write_block_data(client, ADM1266_READ_BLACKBOX, + sizeof(payload), payload); + if (ret < 0) + return ret; + + return count; +} + +static const struct file_operations adm1266_clear_blackbox_fops = { + .owner = THIS_MODULE, + .open = simple_open, + .write = adm1266_clear_blackbox_write, + .llseek = noop_llseek, +}; + static void adm1266_init_debugfs(struct adm1266_data *data) { struct dentry *root; @@ -385,6 +419,8 @@ static void adm1266_init_debugfs(struct adm1266_data *data) adm1266_state_read); debugfs_create_devm_seqfile(&data->client->dev, "firmware_revision", data->debugfs_dir, adm1266_firmware_revision_read); + debugfs_create_file("clear_blackbox", 0200, data->debugfs_dir, data->client, + &adm1266_clear_blackbox_fops); } static int adm1266_nvmem_read_blackbox(struct adm1266_data *data, u8 *read_buff) From e8d55e1f6ebf6d22727fe46ce8714906b3afe23e Mon Sep 17 00:00:00 2001 From: Abdurrahman Hussain Date: Wed, 20 May 2026 15:42:41 -0700 Subject: [PATCH 47/81] hwmon: (pmbus/adm1266) add powerup_counter debugfs entry The ADM1266 maintains a 16-bit non-volatile POWERUP_COUNTER register (0xE4, datasheet Rev. D, Table 93) that increments on every power cycle and cannot be reset by the host. Each blackbox record already embeds the counter at record time, so the standalone live value is primarily useful for matching a captured record back to the boot it came from when correlating logs. Expose it as a read-only debugfs file alongside sequencer_state. The block-read returns two payload bytes in little-endian order. Take pmbus_lock around the block-read so the access serialises with any pmbus_core sequence that sets PAGE on the device. Without it, a PAGE write from another thread could interleave between a PAGE set and a paged read elsewhere in the driver and corrupt either side's view of the device state machine. Signed-off-by: Abdurrahman Hussain Assisted-by: Claude-Code:claude-opus-4-7 Assisted-by: sashiko:gemini-3.1-pro-preview Link: https://lore.kernel.org/r/20260520-adm1266-v5-2-c72ef1fac1ea@nexthop.ai Signed-off-by: Guenter Roeck --- drivers/hwmon/pmbus/adm1266.c | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/drivers/hwmon/pmbus/adm1266.c b/drivers/hwmon/pmbus/adm1266.c index aa353b6b9c99..0ef8a6ccd919 100644 --- a/drivers/hwmon/pmbus/adm1266.c +++ b/drivers/hwmon/pmbus/adm1266.c @@ -28,6 +28,7 @@ #define ADM1266_READ_BLACKBOX 0xDE #define ADM1266_SET_RTC 0xDF #define ADM1266_GPIO_CONFIG 0xE1 +#define ADM1266_POWERUP_COUNTER 0xE4 #define ADM1266_BLACKBOX_INFO 0xE6 #define ADM1266_PDIO_STATUS 0xE9 #define ADM1266_GPIO_STATUS 0xEA @@ -371,6 +372,32 @@ static int adm1266_firmware_revision_read(struct seq_file *s, void *pdata) return 0; } +/* + * POWERUP_COUNTER (0xE4) is a 2-byte little-endian non-volatile counter + * that increments on every device power cycle (datasheet Rev. D, Table + * 93). It saturates at 65535 and cannot be reset by the host. Each + * blackbox record embeds the counter value at record time, so this live + * read is mainly useful for matching a record back to its boot. + */ +static int adm1266_powerup_counter_read(struct seq_file *s, void *pdata) +{ + struct device *dev = s->private; + struct i2c_client *client = to_i2c_client(dev); + u8 buf[I2C_SMBUS_BLOCK_MAX]; + int ret; + + guard(pmbus_lock)(client); + ret = i2c_smbus_read_block_data(client, ADM1266_POWERUP_COUNTER, buf); + if (ret < 0) + return ret; + if (ret != 2) + return -EIO; + + seq_printf(s, "%u\n", buf[0] | (buf[1] << 8)); + + return 0; +} + /* * Clearing the blackbox is required when the device is configured in * single-recording mode (BLACKBOX_CONFIG[0] = 0): once the 32-record @@ -419,6 +446,8 @@ static void adm1266_init_debugfs(struct adm1266_data *data) adm1266_state_read); debugfs_create_devm_seqfile(&data->client->dev, "firmware_revision", data->debugfs_dir, adm1266_firmware_revision_read); + debugfs_create_devm_seqfile(&data->client->dev, "powerup_counter", data->debugfs_dir, + adm1266_powerup_counter_read); debugfs_create_file("clear_blackbox", 0200, data->debugfs_dir, data->client, &adm1266_clear_blackbox_fops); } From 22c1992642ebe95f4ec9f6456cf56ae330d2571a Mon Sep 17 00:00:00 2001 From: Abdurrahman Hussain Date: Wed, 20 May 2026 15:42:42 -0700 Subject: [PATCH 48/81] hwmon: (pmbus/adm1266) add rtc debugfs entry The driver seeds the chip's SET_RTC register once at probe with ktime_get_real_seconds(). Over a long uptime the chip's internal seconds counter drifts away from the host's wall-clock time, so the timestamp embedded in each blackbox record stops being meaningful in wall-clock terms. The datasheet recommends that the host periodically resynchronise the counter to address this; today the driver has no userspace-facing knob for that. Expose SET_RTC via an rtc debugfs file alongside the other adm1266 debugfs entries: read -- returns the chip's current SET_RTC seconds counter, so userspace can observe how far the chip has drifted from host wall-clock without writing anything. write -- the kernel re-reads ktime_get_real_seconds() itself and pushes it to the chip. The write payload is ignored; userspace does not get to supply its own timestamp value, so there is no way for it to push a wrong time into the chip. A small userspace agent (chrony hook, systemd-timesyncd dispatch script, or a periodic cron job) can write to this file to keep the chip's counter aligned with wall-clock across long uptimes. Both the read and write paths take pmbus_lock to serialise against the pmbus_core's own PAGE+register sequences and against the other adm1266 debugfs accessors that already run under the same lock. While at it, drop the now-redundant adm1266_set_rtc() probe-time helper. The new adm1266_rtc_set() callback does exactly the same byte-packing and write; probe just calls adm1266_rtc_set(client, 0) (the ignored @val argument) after pmbus_do_probe() so the pmbus_lock acquired by the new helper has a live mutex to take. Signed-off-by: Abdurrahman Hussain Assisted-by: Claude-Code:claude-opus-4-7 Assisted-by: sashiko:gemini-3.1-pro-preview Link: https://lore.kernel.org/r/20260520-adm1266-v5-3-c72ef1fac1ea@nexthop.ai Signed-off-by: Guenter Roeck --- drivers/hwmon/pmbus/adm1266.c | 90 +++++++++++++++++++++++++++-------- 1 file changed, 69 insertions(+), 21 deletions(-) diff --git a/drivers/hwmon/pmbus/adm1266.c b/drivers/hwmon/pmbus/adm1266.c index 0ef8a6ccd919..22ccb721dd51 100644 --- a/drivers/hwmon/pmbus/adm1266.c +++ b/drivers/hwmon/pmbus/adm1266.c @@ -432,6 +432,69 @@ static const struct file_operations adm1266_clear_blackbox_fops = { .llseek = noop_llseek, }; +/* + * SET_RTC (0xDF) is a 6-byte block (datasheet Rev. D, Table 84): + * bytes [1:0] - fractional seconds (1/65536 s, written as zero) + * bytes [5:2] - seconds since 1970-01-01 UTC, little-endian + * + * The driver seeds it once at probe via adm1266_rtc_set(). Over a + * long uptime the chip's counter drifts away from host wall-clock, + * so expose it via debugfs: + * + * read -- returns the chip's current seconds counter, which lets + * userspace observe host-vs-chip drift. + * write -- the kernel re-reads ktime_get_real_seconds() and writes + * it to SET_RTC. The write payload is ignored; userspace + * does not get to supply its own timestamp value, so + * there is no way to push a wrong time into the chip. + * + * A small userspace agent (chrony hook, systemd-timesyncd script, + * or a periodic cron job) can write to this file to keep the + * timestamp embedded in each blackbox record aligned with + * wall-clock across long uptimes. + */ +static int adm1266_rtc_get(void *data, u64 *val) +{ + struct i2c_client *client = data; + u8 buf[I2C_SMBUS_BLOCK_MAX]; + u32 seconds = 0; + int ret, i; + + guard(pmbus_lock)(client); + ret = i2c_smbus_read_block_data(client, ADM1266_SET_RTC, buf); + if (ret < 0) + return ret; + if (ret < 6) + return -EIO; + + for (i = 0; i < 4; i++) + seconds |= (u32)buf[2 + i] << (i * 8); + + *val = seconds; + + return 0; +} + +static int adm1266_rtc_set(void *data, u64 val) +{ + struct i2c_client *client = data; + time64_t kt = ktime_get_real_seconds(); + u8 write_buf[6] = { 0 }; + int i; + + /* User-supplied @val is ignored on purpose; the kernel owns the + * time source so userspace cannot push a wrong value into the chip. + */ + for (i = 0; i < 4; i++) + write_buf[2 + i] = (kt >> (i * 8)) & 0xFF; + + guard(pmbus_lock)(client); + return i2c_smbus_write_block_data(client, ADM1266_SET_RTC, + sizeof(write_buf), write_buf); +} +DEFINE_DEBUGFS_ATTRIBUTE(adm1266_rtc_fops, + adm1266_rtc_get, adm1266_rtc_set, "%llu\n"); + static void adm1266_init_debugfs(struct adm1266_data *data) { struct dentry *root; @@ -450,6 +513,8 @@ static void adm1266_init_debugfs(struct adm1266_data *data) adm1266_powerup_counter_read); debugfs_create_file("clear_blackbox", 0200, data->debugfs_dir, data->client, &adm1266_clear_blackbox_fops); + debugfs_create_file("rtc", 0600, data->debugfs_dir, data->client, + &adm1266_rtc_fops); } static int adm1266_nvmem_read_blackbox(struct adm1266_data *data, u8 *read_buff) @@ -539,23 +604,6 @@ static int adm1266_config_nvmem(struct adm1266_data *data) return 0; } -static int adm1266_set_rtc(struct adm1266_data *data) -{ - time64_t kt; - char write_buf[6]; - int i; - - kt = ktime_get_real_seconds(); - - memset(write_buf, 0, sizeof(write_buf)); - - for (i = 0; i < 4; i++) - write_buf[2 + i] = (kt >> (i * 8)) & 0xFF; - - return i2c_smbus_write_block_data(data->client, ADM1266_SET_RTC, sizeof(write_buf), - write_buf); -} - static int adm1266_probe(struct i2c_client *client) { struct adm1266_data *data; @@ -575,14 +623,14 @@ static int adm1266_probe(struct i2c_client *client) crc8_populate_msb(pmbus_crc_table, 0x7); mutex_init(&data->buf_mutex); - ret = adm1266_set_rtc(data); - if (ret < 0) - return ret; - ret = pmbus_do_probe(client, &data->info); if (ret) return ret; + ret = adm1266_rtc_set(client, 0); + if (ret < 0) + return ret; + ret = adm1266_config_nvmem(data); if (ret < 0) return ret; From 964acc34569677e6725812172c3d1a28b3973277 Mon Sep 17 00:00:00 2001 From: Jan-Henrik Bruhn Date: Sat, 23 May 2026 15:36:17 +0200 Subject: [PATCH 49/81] hwmon: (lm63) expose PWM frequency and LUT hysteresis as writable The driver caches the PWM frequency register and the CONFIG_FAN slow-clock select bit, but never lets userspace pick a different output frequency. Add a pwm1_freq sysfs attribute that selects the closest SCS + PFR combination for the requested value in Hz, gated by manual mode like set_pwm1(). PFR is clamped to 31 so that 2*PFR fits in the chip's 6-bit PWM register (matching the existing scaling assumption in show_pwm1). The hardware LUT hysteresis register is shared by all LUT entries, so the per-point pwm1_auto_pointN_temp_hyst attributes can't be made RW without N-to-1 cross-attribute side effects. Following the max31760 precedent, expose a single chip-wide pwm1_auto_point_temp_hyst attribute holding the hysteresis amount in millidegrees; the per-point attributes stay RO and continue to show the resulting absolute trip-down temperature for each entry. This was tested on a Linksys LGS328MPC switch hardware where the fan would not spin with the default PWM Frequency, which is why this change is required. Signed-off-by: Jan-Henrik Bruhn Link: https://lore.kernel.org/r/20260523133617.3439102-1-kernel@jhbruhn.de Signed-off-by: Guenter Roeck --- drivers/hwmon/lm63.c | 133 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 133 insertions(+) diff --git a/drivers/hwmon/lm63.c b/drivers/hwmon/lm63.c index a0d77a7386a9..e2a429e579ac 100644 --- a/drivers/hwmon/lm63.c +++ b/drivers/hwmon/lm63.c @@ -92,6 +92,9 @@ static const unsigned short normal_i2c[] = { 0x18, 0x4c, 0x4e, I2C_CLIENT_END }; #define LM96163_REG_REMOTE_TEMP_U_LSB 0x32 #define LM96163_REG_CONFIG_ENHANCED 0x45 +#define LM63_PWM_BASE_FAST_HZ 180000 +#define LM63_PWM_BASE_SLOW_HZ 700 + #define LM63_MAX_CONVRATE 9 #define LM63_MAX_CONVRATE_HZ 32 @@ -455,6 +458,91 @@ static ssize_t pwm1_enable_store(struct device *dev, return count; } +static ssize_t pwm1_freq_show(struct device *dev, + struct device_attribute *dummy, char *buf) +{ + struct lm63_data *data = lm63_update_device(dev); + unsigned int base, freq; + + mutex_lock(&data->update_lock); + base = (data->config_fan & 0x08) ? + LM63_PWM_BASE_SLOW_HZ : LM63_PWM_BASE_FAST_HZ; + freq = data->pwm1_freq; + mutex_unlock(&data->update_lock); + + return sprintf(buf, "%u\n", base / freq); +} + +/* + * Pick the closest CONFIG_FAN.SCS + PFR for the requested frequency. + * PWM_FREQ writes need the LUT unlocked, same as set_pwm1(). LUT PWM + * bytes are register-relative; rewrite them after a frequency change + * if duty cycles must be preserved. + */ +static ssize_t pwm1_freq_store(struct device *dev, + struct device_attribute *dummy, + const char *buf, size_t count) +{ + struct lm63_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + unsigned long val, pfr_fast, pfr_slow, err_fast, err_slow, pfr; + bool slow_clock; + int ret; + + ret = kstrtoul(buf, 10, &val); + if (ret) + return ret; + if (val == 0) + return -EINVAL; + + pfr_fast = clamp_val(DIV_ROUND_CLOSEST((unsigned long)LM63_PWM_BASE_FAST_HZ, val), + 1UL, 31UL); + pfr_slow = clamp_val(DIV_ROUND_CLOSEST((unsigned long)LM63_PWM_BASE_SLOW_HZ, val), + 1UL, 31UL); + err_fast = abs_diff(LM63_PWM_BASE_FAST_HZ / pfr_fast, val); + err_slow = abs_diff(LM63_PWM_BASE_SLOW_HZ / pfr_slow, val); + + if (err_slow < err_fast) { + slow_clock = true; + pfr = pfr_slow; + } else { + slow_clock = false; + pfr = pfr_fast; + } + + mutex_lock(&data->update_lock); + ret = i2c_smbus_read_byte_data(client, LM63_REG_CONFIG_FAN); + if (ret < 0) { + mutex_unlock(&data->update_lock); + return ret; + } + data->config_fan = ret; + + if (!(data->config_fan & 0x20)) { /* register is read-only */ + mutex_unlock(&data->update_lock); + return -EPERM; + } + + if (data->kind == lm96163) { + ret = i2c_smbus_read_byte_data(client, LM96163_REG_CONFIG_ENHANCED); + if (ret < 0) { + mutex_unlock(&data->update_lock); + return ret; + } + data->pwm_highres = !slow_clock && pfr == 8 && (ret & 0x10); + } + + if (slow_clock) + data->config_fan |= 0x08; + else + data->config_fan &= ~0x08; + i2c_smbus_write_byte_data(client, LM63_REG_CONFIG_FAN, data->config_fan); + i2c_smbus_write_byte_data(client, LM63_REG_PWM_FREQ, pfr); + data->pwm1_freq = pfr; + mutex_unlock(&data->update_lock); + return count; +} + /* * There are 8bit registers for both local(temp1) and remote(temp2) sensor. * For remote sensor registers temp2_offset has to be considered, @@ -629,6 +717,47 @@ static ssize_t show_lut_temp_hyst(struct device *dev, return sprintf(buf, "%d\n", temp); } +/* + * The LM63 has a single hysteresis register shared by all LUT entries. + * Expose it as a chip-wide hysteresis amount in millidegrees; the + * per-point pwm1_auto_pointN_temp_hyst attributes remain read-only and + * show the resulting absolute trip-down temperature for each entry. + */ +static ssize_t pwm1_auto_point_temp_hyst_show(struct device *dev, + struct device_attribute *dummy, + char *buf) +{ + struct lm63_data *data = lm63_update_device(dev); + u8 hyst; + + mutex_lock(&data->update_lock); + hyst = data->lut_temp_hyst; + mutex_unlock(&data->update_lock); + + return sprintf(buf, "%d\n", TEMP8_FROM_REG(hyst)); +} + +static ssize_t pwm1_auto_point_temp_hyst_store(struct device *dev, + struct device_attribute *dummy, + const char *buf, size_t count) +{ + struct lm63_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err) + return err; + + mutex_lock(&data->update_lock); + data->lut_temp_hyst = HYST_TO_REG(val); + i2c_smbus_write_byte_data(client, LM63_REG_LUT_TEMP_HYST, + data->lut_temp_hyst); + mutex_unlock(&data->update_lock); + return count; +} + /* * And now the other way around, user-space provides an absolute * hysteresis value and we have to store a relative one @@ -764,6 +893,8 @@ static SENSOR_DEVICE_ATTR(fan1_min, S_IWUSR | S_IRUGO, show_fan, static SENSOR_DEVICE_ATTR(pwm1, S_IWUSR | S_IRUGO, show_pwm1, set_pwm1, 0); static DEVICE_ATTR_RW(pwm1_enable); +static DEVICE_ATTR_RW(pwm1_freq); +static DEVICE_ATTR_RW(pwm1_auto_point_temp_hyst); static SENSOR_DEVICE_ATTR(pwm1_auto_point1_pwm, S_IWUSR | S_IRUGO, show_pwm1, set_pwm1, 1); static SENSOR_DEVICE_ATTR(pwm1_auto_point1_temp, S_IWUSR | S_IRUGO, @@ -869,6 +1000,8 @@ static DEVICE_ATTR_RW(update_interval); static struct attribute *lm63_attributes[] = { &sensor_dev_attr_pwm1.dev_attr.attr, &dev_attr_pwm1_enable.attr, + &dev_attr_pwm1_freq.attr, + &dev_attr_pwm1_auto_point_temp_hyst.attr, &sensor_dev_attr_pwm1_auto_point1_pwm.dev_attr.attr, &sensor_dev_attr_pwm1_auto_point1_temp.dev_attr.attr, &sensor_dev_attr_pwm1_auto_point1_temp_hyst.dev_attr.attr, From d0154a9422eed727a37ac8efad18216e5db0757a Mon Sep 17 00:00:00 2001 From: Flaviu Nistor Date: Fri, 22 May 2026 08:23:52 +0300 Subject: [PATCH 50/81] hwmon: (adt7475) Add explicit header include Since device_property_read_string() and similar functions defined in linux/property.h are used in the driver add explicit include for linux/mod_devicetable.h and linux/property.h rather than having implicit inclusions. Removed of_match_ptr() improving non-Device Tree compatibility of the driver and drop unnecessary __maybe_unused. Header linux/of.h can't be removed yet since macro is_of_node() is used. Signed-off-by: Flaviu Nistor Link: https://lore.kernel.org/r/20260522052352.12139-1-flaviu.nistor@gmail.com Signed-off-by: Guenter Roeck --- drivers/hwmon/adt7475.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/hwmon/adt7475.c b/drivers/hwmon/adt7475.c index 7fb96f5395fa..7241fc73d21a 100644 --- a/drivers/hwmon/adt7475.c +++ b/drivers/hwmon/adt7475.c @@ -19,6 +19,8 @@ #include #include #include +#include +#include #include #include @@ -173,7 +175,7 @@ static const struct i2c_device_id adt7475_id[] = { }; MODULE_DEVICE_TABLE(i2c, adt7475_id); -static const struct of_device_id __maybe_unused adt7475_of_match[] = { +static const struct of_device_id adt7475_of_match[] = { { .compatible = "adi,adt7473", .data = (void *)adt7473 @@ -1995,7 +1997,7 @@ static struct i2c_driver adt7475_driver = { .class = I2C_CLASS_HWMON, .driver = { .name = "adt7475", - .of_match_table = of_match_ptr(adt7475_of_match), + .of_match_table = adt7475_of_match, }, .probe = adt7475_probe, .id_table = adt7475_id, From 705ec98e8a48fd0c344c02541fcc430c1e86e30f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Reiner=20Pr=C3=B6ls?= Date: Thu, 21 May 2026 23:26:11 +0200 Subject: [PATCH 51/81] hwmon: (nct6683) Add support for ASRock Z890 Pro-A MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add the ASRock Z890 Pro-A customer ID to the list of supported boards for the NCT6683 hardware monitoring driver. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Reiner Pröls Link: https://lore.kernel.org/r/20260521212632.223724-1-Reiner.Proels@gmail.com Signed-off-by: Guenter Roeck --- drivers/hwmon/nct6683.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/hwmon/nct6683.c b/drivers/hwmon/nct6683.c index 0581770380cc..e1c36c95affb 100644 --- a/drivers/hwmon/nct6683.c +++ b/drivers/hwmon/nct6683.c @@ -177,12 +177,13 @@ superio_exit(int ioreg) #define NCT6683_CUSTOMER_ID_MSI3 0x207 #define NCT6683_CUSTOMER_ID_MSI4 0x20d #define NCT6683_CUSTOMER_ID_AMD 0x162b -#define NCT6683_CUSTOMER_ID_ASROCK 0xe2c +#define NCT6683_CUSTOMER_ID_ASROCK 0xe2c #define NCT6683_CUSTOMER_ID_ASROCK2 0xe1b #define NCT6683_CUSTOMER_ID_ASROCK3 0x1631 #define NCT6683_CUSTOMER_ID_ASROCK4 0x163e #define NCT6683_CUSTOMER_ID_ASROCK5 0x1621 #define NCT6683_CUSTOMER_ID_ASROCK6 0x1633 +#define NCT6683_CUSTOMER_ID_ASROCK7 0x163d #define NCT6683_REG_BUILD_YEAR 0x604 #define NCT6683_REG_BUILD_MONTH 0x605 @@ -1248,6 +1249,8 @@ static int nct6683_probe(struct platform_device *pdev) break; case NCT6683_CUSTOMER_ID_ASROCK6: break; + case NCT6683_CUSTOMER_ID_ASROCK7: + break; default: if (!force) return -ENODEV; From e481a270ad8ffe269b7779267d40473a41fb90b3 Mon Sep 17 00:00:00 2001 From: Veronika Kossmann Date: Sun, 7 Jun 2026 13:06:10 +0200 Subject: [PATCH 52/81] hwmon: (asus-ec-sensors) add ROG STRIX B650E-E GAMING WIFI Add support for ROG STRIX B650E-E GAMING WIFI Signed-off-by: Veronika Kossmann Co-developed-by: Oleg Tsvetkov Signed-off-by: Oleg Tsvetkov Signed-off-by: Eugene Shalygin Link: https://lore.kernel.org/r/20260607110702.84599-2-eugene.shalygin@gmail.com Signed-off-by: Guenter Roeck --- Documentation/hwmon/asus_ec_sensors.rst | 1 + drivers/hwmon/asus-ec-sensors.c | 12 +++++++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/Documentation/hwmon/asus_ec_sensors.rst b/Documentation/hwmon/asus_ec_sensors.rst index 9ad3f0a57f55..e14419811aac 100644 --- a/Documentation/hwmon/asus_ec_sensors.rst +++ b/Documentation/hwmon/asus_ec_sensors.rst @@ -31,6 +31,7 @@ Supported boards: * ROG MAXIMUS Z690 FORMULA * ROG STRIX B550-E GAMING * ROG STRIX B550-I GAMING + * ROG STRIX B650E-E GAMING WIFI * ROG STRIX B650E-I GAMING WIFI * ROG STRIX B850-I GAMING WIFI * ROG STRIX X470-F GAMING diff --git a/drivers/hwmon/asus-ec-sensors.c b/drivers/hwmon/asus-ec-sensors.c index b5d97a27f80d..ef2a2f7fc48d 100644 --- a/drivers/hwmon/asus-ec-sensors.c +++ b/drivers/hwmon/asus-ec-sensors.c @@ -277,7 +277,7 @@ static const struct ec_sensor_info sensors_family_amd_600[] = { [ec_sensor_temp_cpu_package] = EC_SENSOR("CPU Package", hwmon_temp, 1, 0x00, 0x31), [ec_sensor_temp_mb] = - EC_SENSOR("Motherboard", hwmon_temp, 1, 0x00, 0x32), + EC_SENSOR("Motherboard", hwmon_temp, 1, 0x00, 0x32), [ec_sensor_temp_vrm] = EC_SENSOR("VRM", hwmon_temp, 1, 0x00, 0x33), [ec_sensor_temp_t_sensor] = @@ -621,6 +621,14 @@ static const struct ec_board_info board_info_strix_b550_i_gaming = { .family = family_amd_500_series, }; +static const struct ec_board_info board_info_strix_b650e_e_gaming = { + .sensors = SENSOR_TEMP_CPU | SENSOR_TEMP_CPU_PACKAGE | + SENSOR_TEMP_MB | SENSOR_TEMP_VRM | + SENSOR_FAN_CPU_OPT, + .mutex_path = ASUS_HW_ACCESS_MUTEX_SB_PCI0_SBRG_SIO1_MUT0, + .family = family_amd_600_series, +}; + static const struct ec_board_info board_info_strix_b650e_i_gaming = { .sensors = SENSOR_TEMP_VRM | SENSOR_TEMP_T_SENSOR | SENSOR_SET_TEMP_CHIPSET_CPU_MB | SENSOR_IN_CPU_CORE, @@ -866,6 +874,8 @@ static const struct dmi_system_id dmi_table[] = { &board_info_strix_b550_e_gaming), DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX B550-I GAMING", &board_info_strix_b550_i_gaming), + DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX B650E-E GAMING WIFI", + &board_info_strix_b650e_e_gaming), DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX B650E-I GAMING WIFI", &board_info_strix_b650e_i_gaming), DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX B850-I GAMING WIFI", From 5e9f31543515d685cafed33274433b6360c0e1e4 Mon Sep 17 00:00:00 2001 From: Eugene Shalygin Date: Sun, 7 Jun 2026 14:36:16 +0200 Subject: [PATCH 53/81] hwmon: (asus-ec-sensors) add ROG STRIX B850-E GAMING WIFI The board has a similar sensor configuration to the ROG STRIX B850-I GAMING WIFI, but includes an additional T-Sensor header. The patch was provided via GitHub [1]. [1] https://github.com/zeule/asus-ec-sensors/pull/105 Signed-off-by: Eugene Shalygin Link: https://lore.kernel.org/r/20260607123626.100630-1-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 e14419811aac..8d963347bfca 100644 --- a/Documentation/hwmon/asus_ec_sensors.rst +++ b/Documentation/hwmon/asus_ec_sensors.rst @@ -33,6 +33,7 @@ Supported boards: * ROG STRIX B550-I GAMING * ROG STRIX B650E-E GAMING WIFI * ROG STRIX B650E-I GAMING WIFI + * ROG STRIX B850-E GAMING WIFI * ROG STRIX B850-I GAMING WIFI * ROG STRIX X470-F GAMING * ROG STRIX X470-I GAMING diff --git a/drivers/hwmon/asus-ec-sensors.c b/drivers/hwmon/asus-ec-sensors.c index ef2a2f7fc48d..85badf16e304 100644 --- a/drivers/hwmon/asus-ec-sensors.c +++ b/drivers/hwmon/asus-ec-sensors.c @@ -636,6 +636,14 @@ static const struct ec_board_info board_info_strix_b650e_i_gaming = { .family = family_amd_600_series, }; +static const struct ec_board_info board_info_strix_b850_e_gaming_wifi = { + .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_b850_i_gaming_wifi = { .sensors = SENSOR_TEMP_CPU | SENSOR_TEMP_CPU_PACKAGE | SENSOR_TEMP_MB | SENSOR_TEMP_VRM, @@ -878,6 +886,8 @@ static const struct dmi_system_id dmi_table[] = { &board_info_strix_b650e_e_gaming), DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX B650E-I GAMING WIFI", &board_info_strix_b650e_i_gaming), + DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX B850-E GAMING WIFI", + &board_info_strix_b850_e_gaming_wifi), 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-F GAMING", From 7f8581c70a3bd50a932d3d2d253e99c5ec3eda74 Mon Sep 17 00:00:00 2001 From: Nikita Zhandarovich Date: Fri, 29 May 2026 17:18:36 +0300 Subject: [PATCH 54/81] hwmon: (it87) Clamp negative values to zero in set_fan() set_fan() parses user input with kstrtol() and passes the resulting value to FAN16_TO_REG() on chips with 16-bit fan support. Negative fan speeds are not meaningful and should be rejected before conversion. Worst scenario, one may be able to abuse undefined behaviour of signed overflow to possibly induce rpm * 2 == 0 in FAN16_TO_REG(), thus causing a division by zero. Instead, clamp val < 0 to zero and keep the conversion in its valid input domain, avoiding unsafe arithmetic in the register conversion path. Found by Linux Verification Center (linuxtesting.org) with static analysis tool SVACE. Fixes: 17d648bf5786 ("it87: Add support for the IT8716F") Signed-off-by: Nikita Zhandarovich Link: https://lore.kernel.org/r/20260529141839.1639287-1-n.zhandarovich@fintech.ru Signed-off-by: Guenter Roeck --- drivers/hwmon/it87.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/hwmon/it87.c b/drivers/hwmon/it87.c index 5fd310662ee4..87edb1b6048b 100644 --- a/drivers/hwmon/it87.c +++ b/drivers/hwmon/it87.c @@ -1412,6 +1412,9 @@ static ssize_t set_fan(struct device *dev, struct device_attribute *attr, if (kstrtol(buf, 10, &val) < 0) return -EINVAL; + if (val < 0) + val = 0; + err = it87_lock(data); if (err) return err; From acac4eb97a8e50bc5589fc948e2c2a7ded8f8e86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig=20=28The=20Capable=20Hub=29?= Date: Wed, 27 May 2026 17:15:52 +0200 Subject: [PATCH 55/81] hwmon: (cros_ec) Drop unused assignment of platform_device_id driver data MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The driver explicitly set the .driver_data member of struct platform_device_id to zero without relying on that value. Drop this unused assignments. While touching this array unify spacing and use named initializers for .name. Signed-off-by: Uwe Kleine-König (The Capable Hub) Reviewed-by: Tzung-Bi Shih Acked-by: Thomas Weißschuh Link: https://lore.kernel.org/r/972c9998054c7944f63266819d6fb08b36edb5c5.1779894738.git.u.kleine-koenig@baylibre.com Signed-off-by: Guenter Roeck --- drivers/hwmon/cros_ec_hwmon.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/hwmon/cros_ec_hwmon.c b/drivers/hwmon/cros_ec_hwmon.c index 7c308b0a4b9e..f6cf254395a8 100644 --- a/drivers/hwmon/cros_ec_hwmon.c +++ b/drivers/hwmon/cros_ec_hwmon.c @@ -654,8 +654,8 @@ static int cros_ec_hwmon_resume(struct platform_device *pdev) } static const struct platform_device_id cros_ec_hwmon_id[] = { - { DRV_NAME, 0 }, - {} + { .name = DRV_NAME }, + { } }; MODULE_DEVICE_TABLE(platform, cros_ec_hwmon_id); From d12bcf7d414065e0708e775b93e445fcfe423751 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig=20=28The=20Capable=20Hub=29?= Date: Wed, 27 May 2026 17:15:53 +0200 Subject: [PATCH 56/81] hwmon: Use named initializers for platform_device_id arrays MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Named initializers are better readable and more robust to changes of the struct definition. This robustness is relevant for a planned change to struct platform_device_id replacing .driver_data by an anonymous unit. While touching these arrays unify usage of commas. Signed-off-by: Uwe Kleine-König (The Capable Hub) Link: https://lore.kernel.org/r/25d38df8db42d69f33fa30267c9fd5ea058223d0.1779894738.git.u.kleine-koenig@baylibre.com Signed-off-by: Guenter Roeck --- drivers/hwmon/max197.c | 4 ++-- drivers/hwmon/ntc_thermistor.c | 24 ++++++++++++------------ drivers/hwmon/sht15.c | 10 +++++----- 3 files changed, 19 insertions(+), 19 deletions(-) diff --git a/drivers/hwmon/max197.c b/drivers/hwmon/max197.c index f0048ff37607..9b6ab050db1b 100644 --- a/drivers/hwmon/max197.c +++ b/drivers/hwmon/max197.c @@ -321,8 +321,8 @@ static void max197_remove(struct platform_device *pdev) } static const struct platform_device_id max197_device_ids[] = { - { "max197", max197 }, - { "max199", max199 }, + { .name = "max197", .driver_data = max197 }, + { .name = "max199", .driver_data = max199 }, { } }; MODULE_DEVICE_TABLE(platform, max197_device_ids); diff --git a/drivers/hwmon/ntc_thermistor.c b/drivers/hwmon/ntc_thermistor.c index d6b48178343d..6f82a6c49393 100644 --- a/drivers/hwmon/ntc_thermistor.c +++ b/drivers/hwmon/ntc_thermistor.c @@ -52,18 +52,18 @@ enum { }; static const struct platform_device_id ntc_thermistor_id[] = { - [NTC_B57330V2103] = { "b57330v2103", TYPE_B57330V2103 }, - [NTC_B57891S0103] = { "b57891s0103", TYPE_B57891S0103 }, - [NTC_NCP03WB473] = { "ncp03wb473", TYPE_NCPXXWB473 }, - [NTC_NCP03WF104] = { "ncp03wf104", TYPE_NCPXXWF104 }, - [NTC_NCP15WB473] = { "ncp15wb473", TYPE_NCPXXWB473 }, - [NTC_NCP15WL333] = { "ncp15wl333", TYPE_NCPXXWL333 }, - [NTC_NCP15XH103] = { "ncp15xh103", TYPE_NCPXXXH103 }, - [NTC_NCP18WB473] = { "ncp18wb473", TYPE_NCPXXWB473 }, - [NTC_NCP21WB473] = { "ncp21wb473", TYPE_NCPXXWB473 }, - [NTC_SSG1404001221] = { "ssg1404_001221", TYPE_NCPXXWB473 }, - [NTC_NCP18WM474] = { "ncp18wm474", TYPE_NCPXXWM474 }, - [NTC_LAST] = { }, + [NTC_B57330V2103] = { .name = "b57330v2103", .driver_data = TYPE_B57330V2103 }, + [NTC_B57891S0103] = { .name = "b57891s0103", .driver_data = TYPE_B57891S0103 }, + [NTC_NCP03WB473] = { .name = "ncp03wb473", .driver_data = TYPE_NCPXXWB473 }, + [NTC_NCP03WF104] = { .name = "ncp03wf104", .driver_data = TYPE_NCPXXWF104 }, + [NTC_NCP15WB473] = { .name = "ncp15wb473", .driver_data = TYPE_NCPXXWB473 }, + [NTC_NCP15WL333] = { .name = "ncp15wl333", .driver_data = TYPE_NCPXXWL333 }, + [NTC_NCP15XH103] = { .name = "ncp15xh103", .driver_data = TYPE_NCPXXXH103 }, + [NTC_NCP18WB473] = { .name = "ncp18wb473", .driver_data = TYPE_NCPXXWB473 }, + [NTC_NCP21WB473] = { .name = "ncp21wb473", .driver_data = TYPE_NCPXXWB473 }, + [NTC_SSG1404001221] = { .name = "ssg1404_001221", .driver_data = TYPE_NCPXXWB473 }, + [NTC_NCP18WM474] = { .name = "ncp18wm474", .driver_data = TYPE_NCPXXWM474 }, + [NTC_LAST] = { } }; MODULE_DEVICE_TABLE(platform, ntc_thermistor_id); diff --git a/drivers/hwmon/sht15.c b/drivers/hwmon/sht15.c index 3d55047e9baf..101cebbe68e4 100644 --- a/drivers/hwmon/sht15.c +++ b/drivers/hwmon/sht15.c @@ -1036,11 +1036,11 @@ static void sht15_remove(struct platform_device *pdev) } static const struct platform_device_id sht15_device_ids[] = { - { "sht10", sht10 }, - { "sht11", sht11 }, - { "sht15", sht15 }, - { "sht71", sht71 }, - { "sht75", sht75 }, + { .name = "sht10", .driver_data = sht10 }, + { .name = "sht11", .driver_data = sht11 }, + { .name = "sht15", .driver_data = sht15 }, + { .name = "sht71", .driver_data = sht71 }, + { .name = "sht75", .driver_data = sht75 }, { } }; MODULE_DEVICE_TABLE(platform, sht15_device_ids); From cd447db64f703d12647fc3a809aa3b47505533c8 Mon Sep 17 00:00:00 2001 From: Daniel Nilsson Date: Wed, 3 Jun 2026 10:57:12 +0200 Subject: [PATCH 57/81] hwmon: (pmbus) Add support for Flex BMR316, BMR321, BMR350 and BMR351 Add support for BMR316, BMR321, BMR350 and BMR351 DC/DC converter modules from Flex to the pmbus driver. Signed-off-by: Daniel Nilsson Link: https://lore.kernel.org/r/20260603085712.659432-2-linux@erq.se [groeck: Resolved conflicts (explicit struct members in pmbus_id)] Signed-off-by: Guenter Roeck --- Documentation/hwmon/pmbus.rst | 9 +++++---- drivers/hwmon/pmbus/pmbus.c | 4 ++++ 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/Documentation/hwmon/pmbus.rst b/Documentation/hwmon/pmbus.rst index a8e01a5b96da..23c42c312274 100644 --- a/Documentation/hwmon/pmbus.rst +++ b/Documentation/hwmon/pmbus.rst @@ -3,17 +3,18 @@ Kernel driver pmbus Supported chips: - * Flex BMR310, BMR453, BMR454, BMR456, BMR457, BMR458, BMR480, - BMR490, BMR491, BMR492 + * Flex BMR310, BMR316, BMR321, BMR350, BMR351, BMR453, BMR454, + BMR456, BMR457, BMR458, BMR480, BMR490, BMR491, BMR492 - Prefixes: 'bmr310', 'bmr453', 'bmr454', 'bmr456', 'bmr457', 'bmr458', 'bmr480', + Prefixes: 'bmr310', 'bmr316', 'bmr321', 'bmr350', 'bmr351', + 'bmr453', 'bmr454', 'bmr456', 'bmr457', 'bmr458', 'bmr480', 'bmr490', 'bmr491', 'bmr492' Addresses scanned: - Datasheets: - https://flexpowermodules.com/products + https://flex.com/products/power-modules/product-selector * ON Semiconductor ADP4000, NCP4200, NCP4208 diff --git a/drivers/hwmon/pmbus/pmbus.c b/drivers/hwmon/pmbus/pmbus.c index 34945ccd3afc..7a7eac90d62e 100644 --- a/drivers/hwmon/pmbus/pmbus.c +++ b/drivers/hwmon/pmbus/pmbus.c @@ -215,6 +215,10 @@ static const struct pmbus_device_info pmbus_info_one_status = { static const struct i2c_device_id pmbus_id[] = { { .name = "adp4000", .driver_data = (kernel_ulong_t)&pmbus_info_one }, { .name = "bmr310", .driver_data = (kernel_ulong_t)&pmbus_info_one_status }, + { .name = "bmr316", .driver_data = (kernel_ulong_t)&pmbus_info_one}, + { .name = "bmr321", .driver_data = (kernel_ulong_t)&pmbus_info_one}, + { .name = "bmr350", .driver_data = (kernel_ulong_t)&pmbus_info_one}, + { .name = "bmr351", .driver_data = (kernel_ulong_t)&pmbus_info_one}, { .name = "bmr453", .driver_data = (kernel_ulong_t)&pmbus_info_one }, { .name = "bmr454", .driver_data = (kernel_ulong_t)&pmbus_info_one }, { .name = "bmr456", .driver_data = (kernel_ulong_t)&pmbus_info_one }, From 9ccccac56b60f17eed85dbcf747558290ef7fd4f Mon Sep 17 00:00:00 2001 From: Sanman Pradhan Date: Mon, 1 Jun 2026 18:45:30 +0000 Subject: [PATCH 58/81] dt-bindings: hwmon: pmbus: Add Analog Devices MAX20860A Add devicetree binding documentation for the Analog Devices MAX20860A step-down DC-DC switching regulator with PMBus interface. Signed-off-by: Sanman Pradhan Acked-by: Conor Dooley Link: https://lore.kernel.org/r/20260601184516.919488-2-sanman.pradhan@hpe.com Signed-off-by: Guenter Roeck --- .../bindings/hwmon/pmbus/adi,max20860a.yaml | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 Documentation/devicetree/bindings/hwmon/pmbus/adi,max20860a.yaml diff --git a/Documentation/devicetree/bindings/hwmon/pmbus/adi,max20860a.yaml b/Documentation/devicetree/bindings/hwmon/pmbus/adi,max20860a.yaml new file mode 100644 index 000000000000..dd238265d462 --- /dev/null +++ b/Documentation/devicetree/bindings/hwmon/pmbus/adi,max20860a.yaml @@ -0,0 +1,45 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/hwmon/pmbus/adi,max20860a.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Analog Devices MAX20860A Step-Down Converter + +maintainers: + - Sanman Pradhan + +description: | + The MAX20860A is a fully integrated step-down DC-DC switching regulator + with PMBus interface for monitoring input/output voltage, output current + and temperature. + + Datasheet: https://www.analog.com/en/products/max20860a.html + +allOf: + - $ref: /schemas/regulator/regulator.yaml# + +properties: + compatible: + const: adi,max20860a + + reg: + maxItems: 1 + +required: + - compatible + - reg + +unevaluatedProperties: false + +examples: + - | + i2c { + #address-cells = <1>; + #size-cells = <0>; + + regulator@40 { + compatible = "adi,max20860a"; + reg = <0x40>; + }; + }; From 9e8475a02b4ac18d0d46318522d4f6980da8f72a Mon Sep 17 00:00:00 2001 From: Syed Arif Date: Mon, 1 Jun 2026 18:45:36 +0000 Subject: [PATCH 59/81] hwmon: (pmbus/max20860a) Add driver for Analog Devices MAX20860A Add a PMBus driver for the Analog Devices MAX20860A step-down DC-DC switching regulator. The MAX20860A provides monitoring of input/output voltage, output current, and temperature via the PMBus interface using linear data format. Optional regulator support is available via CONFIG_SENSORS_MAX20860A_REGULATOR. Signed-off-by: Syed Arif Signed-off-by: Sanman Pradhan Link: https://lore.kernel.org/r/20260601184516.919488-3-sanman.pradhan@hpe.com Signed-off-by: Guenter Roeck --- Documentation/hwmon/index.rst | 1 + Documentation/hwmon/max20860a.rst | 57 ++++++++++++++++++++++++++ MAINTAINERS | 8 ++++ drivers/hwmon/pmbus/Kconfig | 19 +++++++++ drivers/hwmon/pmbus/Makefile | 1 + drivers/hwmon/pmbus/max20860a.c | 68 +++++++++++++++++++++++++++++++ 6 files changed, 154 insertions(+) create mode 100644 Documentation/hwmon/max20860a.rst create mode 100644 drivers/hwmon/pmbus/max20860a.c diff --git a/Documentation/hwmon/index.rst b/Documentation/hwmon/index.rst index e880c6ca84f0..ffaacda416e7 100644 --- a/Documentation/hwmon/index.rst +++ b/Documentation/hwmon/index.rst @@ -163,6 +163,7 @@ Hardware Monitoring Kernel Drivers max20730 max20751 max20830 + max20860a max31722 max31730 max31760 diff --git a/Documentation/hwmon/max20860a.rst b/Documentation/hwmon/max20860a.rst new file mode 100644 index 000000000000..ea6d2228fafc --- /dev/null +++ b/Documentation/hwmon/max20860a.rst @@ -0,0 +1,57 @@ +.. SPDX-License-Identifier: GPL-2.0 + +Kernel driver max20860a +======================= + +Supported chips: + + * Analog Devices MAX20860A + + Prefix: 'max20860a' + + Addresses scanned: - + + Datasheet: https://www.analog.com/en/products/max20860a.html + +Author: + + - Syed Arif + - Sanman Pradhan + + +Description +----------- + +This driver supports hardware monitoring for Analog Devices MAX20860A +Step-Down Switching Regulator with PMBus Interface. + +The MAX20860A is a fully integrated step-down DC-DC switching regulator. +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. + +Sysfs entries +------------- + +================= ======================================== +in1_label "vin" +in1_input Measured input voltage +in1_alarm Input voltage alarm +in2_label "vout1" +in2_input Measured output voltage +in2_alarm Output voltage alarm +curr1_label "iout1" +curr1_input Measured output current +curr1_alarm Output current alarm +temp1_input Measured temperature +temp1_alarm Chip temperature alarm +temp2_input Measured temperature (secondary) +================= ======================================== diff --git a/MAINTAINERS b/MAINTAINERS index d57da4881737..61de4fd69cfa 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -15693,6 +15693,14 @@ F: Documentation/devicetree/bindings/hwmon/pmbus/adi,max20830.yaml F: Documentation/hwmon/max20830.rst F: drivers/hwmon/pmbus/max20830.c +MAX20860A HARDWARE MONITOR DRIVER +M: Sanman Pradhan +L: linux-hwmon@vger.kernel.org +S: Maintained +F: Documentation/devicetree/bindings/hwmon/pmbus/adi,max20860a.yaml +F: Documentation/hwmon/max20860a.rst +F: drivers/hwmon/pmbus/max20860a.c + MAX2175 SDR TUNER DRIVER M: Ramesh Shanmugasundaram L: linux-media@vger.kernel.org diff --git a/drivers/hwmon/pmbus/Kconfig b/drivers/hwmon/pmbus/Kconfig index 64f38654f4e7..f330dc3f21fe 100644 --- a/drivers/hwmon/pmbus/Kconfig +++ b/drivers/hwmon/pmbus/Kconfig @@ -402,6 +402,25 @@ config SENSORS_MAX20830 This driver can also be built as a module. If so, the module will be called max20830. +config SENSORS_MAX20860A + tristate "Analog Devices MAX20860A" + help + If you say yes here you get hardware monitoring support for Analog + Devices MAX20860A step-down converter. + + This driver can also be built as a module. If so, the module will + be called max20860a. + +config SENSORS_MAX20860A_REGULATOR + bool "Regulator support for MAX20860A" + depends on SENSORS_MAX20860A && REGULATOR + help + If you say yes here you get regulator support for Analog Devices + MAX20860A step-down converter. + + This enables the MAX20860A to be used as a regulator device, + providing voltage control through the regulator framework. + config SENSORS_MAX31785 tristate "Maxim MAX31785 and compatibles" help diff --git a/drivers/hwmon/pmbus/Makefile b/drivers/hwmon/pmbus/Makefile index 1f2c73b71953..ffc05f493213 100644 --- a/drivers/hwmon/pmbus/Makefile +++ b/drivers/hwmon/pmbus/Makefile @@ -39,6 +39,7 @@ obj-$(CONFIG_SENSORS_MAX17616) += max17616.o obj-$(CONFIG_SENSORS_MAX20730) += max20730.o obj-$(CONFIG_SENSORS_MAX20751) += max20751.o obj-$(CONFIG_SENSORS_MAX20830) += max20830.o +obj-$(CONFIG_SENSORS_MAX20860A) += max20860a.o obj-$(CONFIG_SENSORS_MAX31785) += max31785.o obj-$(CONFIG_SENSORS_MAX34440) += max34440.o obj-$(CONFIG_SENSORS_MAX8688) += max8688.o diff --git a/drivers/hwmon/pmbus/max20860a.c b/drivers/hwmon/pmbus/max20860a.c new file mode 100644 index 000000000000..905f916f6c08 --- /dev/null +++ b/drivers/hwmon/pmbus/max20860a.c @@ -0,0 +1,68 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Hardware monitoring driver for Analog Devices MAX20860A + * + * SPDX-FileCopyrightText: Copyright Hewlett Packard Enterprise Development LP + */ + +#include +#include +#include +#include "pmbus.h" + +#if IS_ENABLED(CONFIG_SENSORS_MAX20860A_REGULATOR) +static const struct regulator_desc max20860a_reg_desc[] = { + PMBUS_REGULATOR_ONE("vout"), +}; +#endif + +static struct pmbus_driver_info max20860a_info = { + .pages = 1, + .format[PSC_VOLTAGE_IN] = linear, + .format[PSC_VOLTAGE_OUT] = linear, + .format[PSC_CURRENT_OUT] = linear, + .format[PSC_TEMPERATURE] = linear, + .func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | + PMBUS_HAVE_STATUS_VOUT | + PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT | + PMBUS_HAVE_TEMP | PMBUS_HAVE_TEMP2 | + PMBUS_HAVE_STATUS_TEMP | PMBUS_HAVE_STATUS_INPUT, +#if IS_ENABLED(CONFIG_SENSORS_MAX20860A_REGULATOR) + .num_regulators = 1, + .reg_desc = max20860a_reg_desc, +#endif +}; + +static int max20860a_probe(struct i2c_client *client) +{ + return pmbus_do_probe(client, &max20860a_info); +} + +static const struct i2c_device_id max20860a_id[] = { + {"max20860a"}, + {} +}; +MODULE_DEVICE_TABLE(i2c, max20860a_id); + +static const struct of_device_id max20860a_of_match[] = { + { .compatible = "adi,max20860a" }, + {} +}; +MODULE_DEVICE_TABLE(of, max20860a_of_match); + +static struct i2c_driver max20860a_driver = { + .driver = { + .name = "max20860a", + .of_match_table = max20860a_of_match, + }, + .probe = max20860a_probe, + .id_table = max20860a_id, +}; + +module_i2c_driver(max20860a_driver); + +MODULE_AUTHOR("Syed Arif "); +MODULE_AUTHOR("Sanman Pradhan "); +MODULE_DESCRIPTION("PMBus driver for Analog Devices MAX20860A"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("PMBUS"); From 5f6617089dc06f9941e8ca57ba64345c9f1ed9ed Mon Sep 17 00:00:00 2001 From: Brian Downey Date: Mon, 8 Jun 2026 08:08:41 +0200 Subject: [PATCH 60/81] hwmon: (asus-ec-sensors) add ROG MAXIMUS Z790 EXTREME Add support for ROG MAXIMUS Z790 EXTREME Signed-off-by: Brian Downey Signed-off-by: Eugene Shalygin Link: https://lore.kernel.org/r/20260608060855.40469-1-eugene.shalygin@gmail.com Signed-off-by: Guenter Roeck --- Documentation/hwmon/asus_ec_sensors.rst | 1 + drivers/hwmon/asus-ec-sensors.c | 15 +++++++++++++++ 2 files changed, 16 insertions(+) diff --git a/Documentation/hwmon/asus_ec_sensors.rst b/Documentation/hwmon/asus_ec_sensors.rst index 8d963347bfca..77a709517437 100644 --- a/Documentation/hwmon/asus_ec_sensors.rst +++ b/Documentation/hwmon/asus_ec_sensors.rst @@ -29,6 +29,7 @@ Supported boards: * ROG MAXIMUS XI HERO * ROG MAXIMUS XI HERO (WI-FI) * ROG MAXIMUS Z690 FORMULA + * ROG MAXIMUS Z790 EXTREME * ROG STRIX B550-E GAMING * ROG STRIX B550-I GAMING * ROG STRIX B650E-E GAMING WIFI diff --git a/drivers/hwmon/asus-ec-sensors.c b/drivers/hwmon/asus-ec-sensors.c index 85badf16e304..29a23484cbe7 100644 --- a/drivers/hwmon/asus-ec-sensors.c +++ b/drivers/hwmon/asus-ec-sensors.c @@ -404,6 +404,12 @@ static const struct ec_sensor_info sensors_family_intel_700[] = { [ec_sensor_temp_vrm] = EC_SENSOR("VRM", hwmon_temp, 1, 0x00, 0x33), [ec_sensor_fan_cpu_opt] = EC_SENSOR("CPU_Opt", hwmon_fan, 2, 0x00, 0xb0), + [ec_sensor_fan_water_flow] = + EC_SENSOR("Water_Flow", hwmon_fan, 2, 0x00, 0xbc), + [ec_sensor_temp_water_in] = + EC_SENSOR("Water_In", hwmon_temp, 1, 0x01, 0x00), + [ec_sensor_temp_water_out] = + EC_SENSOR("Water_Out", hwmon_temp, 1, 0x01, 0x01), }; /* Shortcuts for common combinations */ @@ -514,6 +520,13 @@ static const struct ec_board_info board_info_maximus_z690_formula = { .family = family_intel_600_series, }; +static const struct ec_board_info board_info_maximus_z790_extreme = { + .sensors = SENSOR_TEMP_T_SENSOR | SENSOR_TEMP_VRM | + SENSOR_SET_TEMP_WATER | SENSOR_FAN_WATER_FLOW, + .mutex_path = ASUS_HW_ACCESS_MUTEX_RMTW_ASMX, + .family = family_intel_700_series, +}; + static const struct ec_board_info board_info_prime_x470_pro = { .sensors = SENSOR_SET_TEMP_CHIPSET_CPU_MB | SENSOR_TEMP_T_SENSOR | SENSOR_TEMP_VRM | @@ -878,6 +891,8 @@ static const struct dmi_system_id dmi_table[] = { &board_info_maximus_x_hero), DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG MAXIMUS Z690 FORMULA", &board_info_maximus_z690_formula), + DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG MAXIMUS Z790 EXTREME", + &board_info_maximus_z790_extreme), DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX B550-E GAMING", &board_info_strix_b550_e_gaming), DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX B550-I GAMING", From cd1b42617aafe01810ab7d3b9948d2f5fa9fb8af Mon Sep 17 00:00:00 2001 From: Kory Maincent Date: Mon, 8 Jun 2026 17:23:43 +0200 Subject: [PATCH 61/81] hwmon: (adt7462) Add of_match_table to support devicetree Add of_match_table to add support of devicetree probing. Signed-off-by: Kory Maincent [rgantois: Removed of_match_ptr().] Signed-off-by: Romain Gantois Link: https://lore.kernel.org/r/20260608-adt7462-bindings-v2-1-272982c40325@bootlin.com Signed-off-by: Guenter Roeck --- drivers/hwmon/adt7462.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/hwmon/adt7462.c b/drivers/hwmon/adt7462.c index 5101d30ed9c6..31cf9e3bb04f 100644 --- a/drivers/hwmon/adt7462.c +++ b/drivers/hwmon/adt7462.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -1814,10 +1815,17 @@ static const struct i2c_device_id adt7462_id[] = { }; MODULE_DEVICE_TABLE(i2c, adt7462_id); +static const struct of_device_id adt7462_of_match[] = { + { .compatible = "onnn,adt7462" }, + { }, +}; +MODULE_DEVICE_TABLE(of, adt7462_of_match); + static struct i2c_driver adt7462_driver = { .class = I2C_CLASS_HWMON, .driver = { .name = "adt7462", + .of_match_table = adt7462_of_match, }, .probe = adt7462_probe, .id_table = adt7462_id, From adf2bba494013651d0e56c3dcda0296e9b5c5809 Mon Sep 17 00:00:00 2001 From: Ashish Yadav Date: Tue, 9 Jun 2026 12:52:29 +0530 Subject: [PATCH 62/81] dt-bindings: hwmon/pmbus: Add Infineon xdp730 Add documentation for the device tree binding of the XDP730 eFuse. Rename node to efuse to accurately reflect its hardware function. Signed-off-by: Ashish Yadav Acked-by: Conor Dooley Link: https://lore.kernel.org/r/20260609072231.15486-2-Ashish.Yadav@infineon.com Signed-off-by: Guenter Roeck --- .../bindings/hwmon/pmbus/infineon,xdp720.yaml | 28 ++++++++++++------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/Documentation/devicetree/bindings/hwmon/pmbus/infineon,xdp720.yaml b/Documentation/devicetree/bindings/hwmon/pmbus/infineon,xdp720.yaml index 72bc3a5e7139..4a949c53f7ae 100644 --- a/Documentation/devicetree/bindings/hwmon/pmbus/infineon,xdp720.yaml +++ b/Documentation/devicetree/bindings/hwmon/pmbus/infineon,xdp720.yaml @@ -5,23 +5,31 @@ $id: http://devicetree.org/schemas/hwmon/pmbus/infineon,xdp720.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# -title: Infineon XDP720 Digital eFuse Controller +title: Infineon XDP720 / XDP730 Digital eFuse Controllers maintainers: - Ashish Yadav description: | - The XDP720 is an eFuse with integrated current sensor and digital - controller. It provides accurate system telemetry (V, I, P, T) and - reports analog current at the IMON pin for post-processing. + The XDP720 and XDP730 are PMBus-compliant digital eFuse controllers + with an integrated current sensor. They provide accurate system + telemetry (V, I, P, T) and report analog current at the IMON pin for + post-processing. - Datasheet: - https://www.infineon.com/assets/row/public/documents/24/49/infineon-xdp720-001-datasheet-en.pdf + Both parts share the same PMBus register map and direct-format + coefficients; they differ in the GIMON gain step exposed via the + TELEMETRY_AVG register (bit 10) and in the VDD_VIN pin number + (XDP720: pin 9, XDP730: pin 20). + + Datasheets: + - XDP720: https://www.infineon.com/assets/row/public/documents/24/49/infineon-xdp720-001-datasheet-en.pdf + - XDP730: https://www.infineon.com/assets/row/public/documents/24/49/infineon-xdp730-001-datasheet-en.pdf properties: compatible: enum: - infineon,xdp720 + - infineon,xdp730 reg: maxItems: 1 @@ -33,9 +41,9 @@ properties: vdd-vin-supply: description: - Supply for the VDD_VIN pin (pin 9), the IC controller power supply. - Typically connected to the input bus (VIN) through a 100 ohm / 100 nF - RC filter. + Supply for the VDD_VIN pin (XDP720 pin 9, XDP730 pin 20), the IC + controller power supply. Typically connected to the input bus + (VIN) through a 100 ohm / 100 nF RC filter. required: - compatible @@ -50,7 +58,7 @@ examples: #address-cells = <1>; #size-cells = <0>; - hwmon@11 { + efuse@11 { compatible = "infineon,xdp720"; reg = <0x11>; vdd-vin-supply = <&vdd_vin>; From 7c264a7d077586a64656ea10559ce6ab69377534 Mon Sep 17 00:00:00 2001 From: Ashish Yadav Date: Tue, 9 Jun 2026 12:52:30 +0530 Subject: [PATCH 63/81] hwmon: (pmbus/xdp720) Add support for efuse xdp730 Adds support for the Infineon XDP730 Digital eFuse Controller by updating the existing XDP720 driver. Signed-off-by: Ashish Yadav Link: https://lore.kernel.org/r/20260609072231.15486-3-Ashish.Yadav@infineon.com [groeck: Fixed conflicts in xdp720_id declaration] Signed-off-by: Guenter Roeck --- drivers/hwmon/pmbus/Kconfig | 2 +- drivers/hwmon/pmbus/xdp720.c | 74 +++++++++++++++++++++++++----------- 2 files changed, 53 insertions(+), 23 deletions(-) diff --git a/drivers/hwmon/pmbus/Kconfig b/drivers/hwmon/pmbus/Kconfig index f330dc3f21fe..c8cda160b5f8 100644 --- a/drivers/hwmon/pmbus/Kconfig +++ b/drivers/hwmon/pmbus/Kconfig @@ -780,7 +780,7 @@ config SENSORS_XDP720 tristate "Infineon XDP720 family" help If you say yes here you get hardware monitoring support for Infineon - XDP720. + XDP720 and XDP730 Digital eFuse Controllers. This driver can also be built as a module. If so, the module will be called xdp720. diff --git a/drivers/hwmon/pmbus/xdp720.c b/drivers/hwmon/pmbus/xdp720.c index 60491e9217bf..88f076e3d15b 100644 --- a/drivers/hwmon/pmbus/xdp720.c +++ b/drivers/hwmon/pmbus/xdp720.c @@ -1,6 +1,11 @@ // SPDX-License-Identifier: GPL-2.0+ /* - * Hardware monitoring driver for Infineon XDP720 Digital eFuse Controller + * Hardware monitoring driver for Infineon XDP720 / XDP730 Digital + * eFuse Controllers. + * + * Both parts share the same PMBus register map and direct-format + * coefficients; they differ in the GIMON gain step exposed via + * the TELEMETRY_AVG register and in the VDD_VIN pin number. * * Copyright (c) 2026 Infineon Technologies. All rights reserved. */ @@ -20,6 +25,18 @@ */ #define XDP720_DEFAULT_RIMON 2000000000 /* 2k ohm */ #define XDP720_TELEMETRY_AVG 0xE9 +#define XDP720_TELEMETRY_AVG_GIMON BIT(10) /* high/low GIMON select */ + +/* Chip identifiers carried in OF match-data and i2c_device_id->driver_data. */ +enum xdp720_chip_id { + CHIP_XDP720 = 0, + CHIP_XDP730, +}; + +struct xdp720_data { + enum xdp720_chip_id id; + struct pmbus_driver_info info; +}; static struct pmbus_driver_info xdp720_info = { .pages = 1, @@ -56,16 +73,18 @@ static struct pmbus_driver_info xdp720_info = { static int xdp720_probe(struct i2c_client *client) { - struct pmbus_driver_info *info; + struct xdp720_data *data; int ret; u32 rimon; int gimon; - info = devm_kmemdup(&client->dev, &xdp720_info, sizeof(*info), - GFP_KERNEL); - if (!info) + data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL); + if (!data) return -ENOMEM; + data->id = (enum xdp720_chip_id)(uintptr_t)i2c_get_match_data(client); + data->info = xdp720_info; + ret = devm_regulator_get_enable(&client->dev, "vdd-vin"); if (ret) return dev_err_probe(&client->dev, ret, @@ -77,36 +96,47 @@ static int xdp720_probe(struct i2c_client *client) return ret; } - ret >>= 10; /* 10th bit of TELEMETRY_AVG REG for GIMON Value */ - ret &= GENMASK(0, 0); - if (ret == 1) - gimon = 18200; /* output gain 18.2 microA/A */ - else - gimon = 9100; /* output gain 9.1 microA/A */ + /* Bit 10 of TELEMETRY_AVG selects the GIMON gain step in microA/A */ + switch (data->id) { + case CHIP_XDP720: + gimon = (ret & XDP720_TELEMETRY_AVG_GIMON) ? 18200 : 9100; + dev_info(&client->dev, "Initialised XDP720 instance\n"); + break; + case CHIP_XDP730: + gimon = (ret & XDP720_TELEMETRY_AVG_GIMON) ? 20000 : 10000; + dev_info(&client->dev, "Initialised XDP730 instance\n"); + break; + default: + return -EINVAL; + } - if (of_property_read_u32(client->dev.of_node, - "infineon,rimon-micro-ohms", &rimon)) - rimon = XDP720_DEFAULT_RIMON; /* Default if not set via DT */ + if (device_property_read_u32(&client->dev, + "infineon,rimon-micro-ohms", &rimon)) + rimon = XDP720_DEFAULT_RIMON; /* Default if not in FW */ if (rimon == 0) return -EINVAL; /* Adapt the current and power scale for each instance */ - info->m[PSC_CURRENT_OUT] = DIV64_U64_ROUND_CLOSEST((u64) - info->m[PSC_CURRENT_OUT] * rimon * gimon, 1000000000000ULL); - info->m[PSC_POWER] = DIV64_U64_ROUND_CLOSEST((u64) - info->m[PSC_POWER] * rimon * gimon, 1000000000000000ULL); + data->info.m[PSC_CURRENT_OUT] = DIV64_U64_ROUND_CLOSEST((u64) + data->info.m[PSC_CURRENT_OUT] * rimon * gimon, + 1000000000000ULL); + data->info.m[PSC_POWER] = DIV64_U64_ROUND_CLOSEST((u64) + data->info.m[PSC_POWER] * rimon * gimon, + 1000000000000000ULL); - return pmbus_do_probe(client, info); + return pmbus_do_probe(client, &data->info); } static const struct of_device_id xdp720_of_match[] = { - { .compatible = "infineon,xdp720" }, + { .compatible = "infineon,xdp720", .data = (void *)CHIP_XDP720 }, + { .compatible = "infineon,xdp730", .data = (void *)CHIP_XDP730 }, {} }; MODULE_DEVICE_TABLE(of, xdp720_of_match); static const struct i2c_device_id xdp720_id[] = { - { .name = "xdp720" }, + { .name = "xdp720", .driver_data = CHIP_XDP720 }, + { .name = "xdp730", .driver_data = CHIP_XDP730 }, { } }; MODULE_DEVICE_TABLE(i2c, xdp720_id); @@ -123,6 +153,6 @@ static struct i2c_driver xdp720_driver = { module_i2c_driver(xdp720_driver); MODULE_AUTHOR("Ashish Yadav "); -MODULE_DESCRIPTION("PMBus driver for Infineon XDP720 Digital eFuse Controller"); +MODULE_DESCRIPTION("PMBus driver for Infineon XDP720/XDP730 Digital eFuse Controllers"); MODULE_LICENSE("GPL"); MODULE_IMPORT_NS("PMBUS"); From d647ebf5a5d50bf794e609b397d471e5b92a02ca Mon Sep 17 00:00:00 2001 From: Ashish Yadav Date: Tue, 9 Jun 2026 12:52:31 +0530 Subject: [PATCH 64/81] hwmon: (pmbus/xdp720) Fix driver issues xdp720/730 Fix driver issues: - Add the missing regulator and property files in include - Declare XDP720_DEFAULT_RIMON as unsigned constant - Declare struct pmbus_driver_info xdp720_info as constant Signed-off-by: Ashish Yadav Link: https://lore.kernel.org/r/20260609072231.15486-4-Ashish.Yadav@infineon.com Signed-off-by: Guenter Roeck --- drivers/hwmon/pmbus/xdp720.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/hwmon/pmbus/xdp720.c b/drivers/hwmon/pmbus/xdp720.c index 88f076e3d15b..5a162084f338 100644 --- a/drivers/hwmon/pmbus/xdp720.c +++ b/drivers/hwmon/pmbus/xdp720.c @@ -17,13 +17,15 @@ #include #include #include +#include +#include #include "pmbus.h" /* * The IMON resistor required to generate the system overcurrent protection. * Arbitrary default Rimon value: 2k Ohm */ -#define XDP720_DEFAULT_RIMON 2000000000 /* 2k ohm */ +#define XDP720_DEFAULT_RIMON 2000000000U /* 2k ohm */ #define XDP720_TELEMETRY_AVG 0xE9 #define XDP720_TELEMETRY_AVG_GIMON BIT(10) /* high/low GIMON select */ @@ -38,7 +40,7 @@ struct xdp720_data { struct pmbus_driver_info info; }; -static struct pmbus_driver_info xdp720_info = { +static const struct pmbus_driver_info xdp720_info = { .pages = 1, .format[PSC_VOLTAGE_IN] = direct, .format[PSC_VOLTAGE_OUT] = direct, From 7ab12d5ab429677ed359b8ac768c138750fda946 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nuno=20S=C3=A1?= Date: Sat, 2 May 2026 10:56:52 +0100 Subject: [PATCH 65/81] dt-bindings: hwmon: Document the LTC4283 Swap Controller MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The LTC4283 is a negative voltage hot swap controller that drives an external N-channel MOSFET to allow a board to be safely inserted and removed from a live backplane. Special note for the "adi,vpower-drns-enable" property. It allows to choose between the attenuated MOSFET drain voltage or the attenuated input voltage at the RTNS pin (effectively choosing between input or output power). This is a system level decision not really intended to change at runtime and hence is being added as a Firmware property. Reviewed-by: Rob Herring (Arm) Signed-off-by: Nuno Sá Link: https://lore.kernel.org/r/20260502-ltc4283-support-v13-1-1c206542e652@analog.com Signed-off-by: Guenter Roeck --- .../bindings/hwmon/adi,ltc4283.yaml | 272 ++++++++++++++++++ MAINTAINERS | 6 + 2 files changed, 278 insertions(+) create mode 100644 Documentation/devicetree/bindings/hwmon/adi,ltc4283.yaml diff --git a/Documentation/devicetree/bindings/hwmon/adi,ltc4283.yaml b/Documentation/devicetree/bindings/hwmon/adi,ltc4283.yaml new file mode 100644 index 000000000000..05e2132ad4d8 --- /dev/null +++ b/Documentation/devicetree/bindings/hwmon/adi,ltc4283.yaml @@ -0,0 +1,272 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/hwmon/adi,ltc4283.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: LTC4283 Negative Voltage Hot Swap Controller + +maintainers: + - Nuno Sá + +description: | + The LTC4283 negative voltage hot swap controller drives an external N-channel + MOSFET to allow a board to be safely inserted and removed from a live + backplane. + + https://www.analog.com/media/en/technical-documentation/data-sheets/ltc4283.pdf + +properties: + compatible: + enum: + - adi,ltc4283 + + reg: + maxItems: 1 + + adi,rsense-nano-ohms: + description: Value of the sense resistor. + + adi,current-limit-sense-microvolt: + description: + The current limit sense voltage of the chip is adjustable between + 15mV and 30mV in 1mV steps. This effectively limits the current + on the load. + minimum: 15000 + maximum: 30000 + default: 15000 + + adi,current-limit-foldback-factor: + description: + Specifies the foldback factor for the current limit. The current limit + can be reduced (folded back) to one of four preset levels. The value + represents the percentage of the current limit sense voltage to use + during foldback. A value of 100 means no foldback. + $ref: /schemas/types.yaml#/definitions/uint32 + enum: [10, 20, 50, 100] + default: 100 + + adi,cooling-delay-ms: + description: + Cooling time to apply after an overcurrent fault, FET bad or + external fault. + enum: [512, 1002, 2005, 4100, 8190, 16400, 32800, 65600] + default: 512 + + adi,fet-bad-timer-delay-ms: + description: + FET bad timer delay. After a FET bad status condition is detected, + this timer is started. If the condition persists for the + specified time, the FET is turned off and a fault is logged. + enum: [256, 512, 1002, 2005] + default: 256 + + adi,power-good-reset-on-fet: + description: + If set, resets the power good status when the MOSFET is turned off. + Otherwise, it resets when a low output voltage is detected. + type: boolean + + adi,fet-turn-off-disable: + description: + If set, the MOSFET is not turned off when a FET fault is detected. + type: boolean + + adi,tmr-pull-down-disable: + description: Disables 2uA pull-down current on the TMR pin. + type: boolean + + adi,dvdt-inrush-control-disable: + description: + Disables dV/dt inrush control during startup. In dV/dt mode, the inrush + current is limited by controlling a constant output voltage ramp rate. + When disabled, the inrush control mechanism is active current limiting. + type: boolean + + adi,fault-log-enable: + description: + If set, enables logging fault registers and ADC data into EEPROM upon a + fault. + type: boolean + + adi,vpower-drns-enable: + description: + If set, enables the attenuated MOSFET drain voltage to be monitored. This + effectively means that the MOSFET power is monitored. If not set, the + attenuated input voltage (and hence input power) is monitored. + type: boolean + + adi,external-fault-fet-off-enable: + description: Turns MOSFET off following an external fault. + type: boolean + + adi,undervoltage-retry-disable: + description: Do not retry to turn on the MOSFET after an undervoltage fault. + type: boolean + + adi,overvoltage-retry-disable: + description: Do not retry to turn on the MOSFET after an overvoltage fault. + type: boolean + + adi,external-fault-retry-enable: + description: Retry to turn on the MOSFET after an external fault. + type: boolean + + adi,overcurrent-retries: + description: Configures auto-retry following an Overcurrent fault. + $ref: /schemas/types.yaml#/definitions/string + enum: [latch-off, "1", "7", unlimited] + default: latch-off + + adi,fet-bad-retries: + description: + Configures auto-retry following a FET bad fault and a consequent MOSFET + turn off. + $ref: /schemas/types.yaml#/definitions/string + enum: [latch-off, "1", "7", unlimited] + default: latch-off + + adi,pgio1-func: + description: Configures the function of the PGIO1 pin. + $ref: /schemas/types.yaml#/definitions/string + enum: [inverted_power_good, power_good, gpio] + default: inverted_power_good + + adi,pgio2-func: + description: Configures the function of the PGIO2 pin. + $ref: /schemas/types.yaml#/definitions/string + enum: [inverted_power_good, power_good, gpio, active_current_limiting] + default: inverted_power_good + + adi,pgio3-func: + description: Configures the function of the PGIO3 pin. + $ref: /schemas/types.yaml#/definitions/string + enum: [inverted_power_good_input, power_good_input, gpio] + default: inverted_power_good_input + + adi,pgio4-func: + description: Configures the function of the PGIO4 pin. + $ref: /schemas/types.yaml#/definitions/string + enum: [inverted_external_fault, external_fault, gpio] + default: inverted_external_fault + + adi,gpio-on-adio1: + description: If set, the ADIO1 pin is used as a GPIO. + type: boolean + + adi,gpio-on-adio2: + description: If set, the ADIO2 pin is used as a GPIO. + type: boolean + + adi,gpio-on-adio3: + description: If set, the ADIO3 pin is used as a GPIO. + type: boolean + + adi,gpio-on-adio4: + description: If set, the ADIO4 pin is used as a GPIO. + type: boolean + + gpio-controller: true + + '#gpio-cells': + const: 2 + +dependencies: + adi,gpio-on-adio1: + - gpio-controller + - '#gpio-cells' + adi,gpio-on-adio2: + - gpio-controller + - '#gpio-cells' + adi,gpio-on-adio3: + - gpio-controller + - '#gpio-cells' + adi,gpio-on-adio4: + - gpio-controller + - '#gpio-cells' + adi,external-fault-retry-enable: + - adi,pgio4-func + adi,external-fault-fet-off-enable: + - adi,pgio4-func + +required: + - compatible + - reg + - adi,rsense-nano-ohms + +allOf: + - if: + properties: + adi,pgio1-func: + const: gpio + required: + - adi,pgio1-func + then: + required: + - gpio-controller + - '#gpio-cells' + + - if: + properties: + adi,pgio2-func: + const: gpio + required: + - adi,pgio2-func + then: + required: + - gpio-controller + - '#gpio-cells' + + - if: + properties: + adi,pgio3-func: + const: gpio + required: + - adi,pgio3-func + then: + required: + - gpio-controller + - '#gpio-cells' + + - if: + properties: + adi,pgio4-func: + const: gpio + required: + - adi,pgio4-func + then: + properties: + adi,external-fault-retry-enable: false + adi,external-fault-fet-off-enable: false + required: + - gpio-controller + - '#gpio-cells' + +additionalProperties: false + +examples: + - | + i2c { + #address-cells = <1>; + #size-cells = <0>; + + swap-controller@15 { + compatible = "adi,ltc4283"; + reg = <0x15>; + + adi,rsense-nano-ohms = <500>; + adi,current-limit-sense-microvolt = <25000>; + adi,current-limit-foldback-factor = <10>; + adi,cooling-delay-ms = <8190>; + adi,fet-bad-timer-delay-ms = <512>; + + adi,external-fault-fet-off-enable; + adi,pgio4-func = "external_fault"; + + adi,gpio-on-adio1; + adi,pgio1-func = "gpio"; + gpio-controller; + #gpio-cells = <2>; + }; + }; +... diff --git a/MAINTAINERS b/MAINTAINERS index 61de4fd69cfa..6f951e946514 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -15252,6 +15252,12 @@ F: Documentation/devicetree/bindings/hwmon/adi,ltc4282.yaml F: Documentation/hwmon/ltc4282.rst F: drivers/hwmon/ltc4282.c +LTC4283 HARDWARE MONITOR AND GPIO DRIVER +M: Nuno Sá +L: linux-hwmon@vger.kernel.org +S: Supported +F: Documentation/devicetree/bindings/hwmon/adi,ltc4283.yaml + LTC4286 HARDWARE MONITOR DRIVER M: Delphine CC Chiu L: linux-hwmon@vger.kernel.org From dd63353a0b5ef05a248df4c6db566b93bff0d843 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nuno=20S=C3=A1?= Date: Sat, 2 May 2026 10:56:53 +0100 Subject: [PATCH 66/81] hwmon: ltc4283: Add support for the LTC4283 Swap Controller MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Support the LTC4283 Hot Swap Controller. The device features programmable current limit with foldback and independently adjustable inrush current to optimize the MOSFET safe operating area (SOA). The SOA timer limits MOSFET temperature rise for reliable protection against overstresses. An I2C interface and onboard ADC allow monitoring of board current, voltage, power, energy, and fault status. Signed-off-by: Nuno Sá Link: https://lore.kernel.org/r/20260502-ltc4283-support-v13-2-1c206542e652@analog.com Signed-off-by: Guenter Roeck --- Documentation/hwmon/index.rst | 1 + Documentation/hwmon/ltc4283.rst | 267 +++++ MAINTAINERS | 1 + drivers/hwmon/Kconfig | 12 + drivers/hwmon/Makefile | 1 + drivers/hwmon/ltc4283.c | 1795 +++++++++++++++++++++++++++++++ 6 files changed, 2077 insertions(+) create mode 100644 Documentation/hwmon/ltc4283.rst create mode 100644 drivers/hwmon/ltc4283.c diff --git a/Documentation/hwmon/index.rst b/Documentation/hwmon/index.rst index ffaacda416e7..0295e5989e1e 100644 --- a/Documentation/hwmon/index.rst +++ b/Documentation/hwmon/index.rst @@ -148,6 +148,7 @@ Hardware Monitoring Kernel Drivers ltc4260 ltc4261 ltc4282 + ltc4283 ltc4286 lx1308 macsmc-hwmon diff --git a/Documentation/hwmon/ltc4283.rst b/Documentation/hwmon/ltc4283.rst new file mode 100644 index 000000000000..a650c595bc8f --- /dev/null +++ b/Documentation/hwmon/ltc4283.rst @@ -0,0 +1,267 @@ +.. SPDX-License-Identifier: GPL-2.0-only + +Kernel drivers ltc4283 +========================================== + +Supported chips: + + * Analog Devices LTC4283 + + Prefix: 'ltc4283' + + Addresses scanned: - + + Datasheet: + + https://www.analog.com/media/en/technical-documentation/data-sheets/ltc4283.pdf + +Author: Nuno Sá + +Description +___________ + +The LTC4283 negative voltage hot swap controller drives an external N-channel +MOSFET to allow a board to be safely inserted and removed from a live backplane. +The device features programmable current limit with foldback and independently +adjustable inrush current to optimize the MOSFET safe operating area (SOA). The +SOA timer limits MOSFET temperature rise for reliable protection against +overstresses. An I2C interface and onboard gear-shift ADC allow monitoring of +board current, voltage, power, energy, and fault status. Additional features +respond to input UV/OV, interrupt the host when a fault has occurred, notify +when output power is good, detect insertion of a board, turn off the MOSFET +if an external supply monitor fails to indicate power good within a timeout +period, and auto-reboot after a programmable delay following a host commanded +turn-off. + +Sysfs entries +_____________ + +The following attributes are supported. Limits are read-write and all the other +attributes are read-only. Note that the VADIOx channels might not be available +if the ADIO pins are used as GPIOs (naturally also affects the respective +differential channels). + +======================= ========================================== +in0_lcrit_alarm Critical Undervoltage alarm +in0_crit_alarm Critical Overvoltage alarm +in0_reset_history Clears Under and Overvoltage fault logs. +in0_label Channel label (VIN) + +in1_input Output voltage (mV). +in1_min Undervoltage threshold +in1_max Overvoltage threshold +in1_lowest Lowest measured voltage +in1_highest Highest measured voltage +in1_reset_history Write 1 to reset history. +in1_min_alarm Undervoltage alarm +in1_max_alarm Overvoltage alarm +in1_label Channel label (VPWR) + +in2_input Output voltage (mV). +in2_min Undervoltage threshold +in2_max Overvoltage threshold +in2_lowest Lowest measured voltage +in2_highest Highest measured voltage +in2_reset_history Write 1 to reset history. +in2_min_alarm Undervoltage alarm +in2_max_alarm Overvoltage alarm +in2_enable Enable/Disable monitoring. +in2_label Channel label (VADI1) + +in3_input Output voltage (mV). +in3_min Undervoltage threshold +in3_max Overvoltage threshold +in3_lowest Lowest measured voltage +in3_highest Highest measured voltage +in3_reset_history Write 1 to reset history. +in3_min_alarm Undervoltage alarm +in3_max_alarm Overvoltage alarm +in3_enable Enable/Disable monitoring. +in3_label Channel label (VADI2) + +in4_input Output voltage (mV). +in4_min Undervoltage threshold +in4_max Overvoltage threshold +in4_lowest Lowest measured voltage +in4_highest Highest measured voltage +in4_reset_history Write 1 to reset history. +in4_min_alarm Undervoltage alarm +in4_max_alarm Overvoltage alarm +in4_enable Enable/Disable monitoring. +in4_label Channel label (VADI3) + +in5_input Output voltage (mV). +in5_min Undervoltage threshold +in5_max Overvoltage threshold +in5_lowest Lowest measured voltage +in5_highest Highest measured voltage +in5_reset_history Write 1 to reset history. +in5_min_alarm Undervoltage alarm +in5_max_alarm Overvoltage alarm +in5_enable Enable/Disable monitoring. +in5_label Channel label (VADI4) + +in6_input Output voltage (mV). +in6_min Undervoltage threshold +in6_max Overvoltage threshold +in6_lowest Lowest measured voltage +in6_highest Highest measured voltage +in6_reset_history Write 1 to reset history. +in6_min_alarm Undervoltage alarm +in6_max_alarm Overvoltage alarm +in6_enable Enable/Disable monitoring. +in6_label Channel label (VADIO1) + +in7_input Output voltage (mV). +in7_min Undervoltage threshold +in7_max Overvoltage threshold +in7_lowest Lowest measured voltage +in7_highest Highest measured voltage +in7_reset_history Write 1 to reset history. +in7_min_alarm Undervoltage alarm +in7_max_alarm Overvoltage alarm +in7_enable Enable/Disable monitoring. +in7_label Channel label (VADIO2) + +in8_input Output voltage (mV). +in8_min Undervoltage threshold +in8_max Overvoltage threshold +in8_lowest Lowest measured voltage +in8_highest Highest measured voltage +in8_reset_history Write 1 to reset history. +in8_min_alarm Undervoltage alarm +in8_max_alarm Overvoltage alarm +in8_enable Enable/Disable monitoring. +in8_label Channel label (VADIO3) + +in9_input Output voltage (mV). +in9_min Undervoltage threshold +in9_max Overvoltage threshold +in9_lowest Lowest measured voltage +in9_highest Highest measured voltage +in9_reset_history Write 1 to reset history. +in9_min_alarm Undervoltage alarm +in9_max_alarm Overvoltage alarm +in9_enable Enable/Disable monitoring. +in9_label Channel label (VADIO4) + +in10_input Output voltage (mV). +in10_min Undervoltage threshold +in10_max Overvoltage threshold +in10_lowest Lowest measured voltage +in10_highest Highest measured voltage +in10_reset_history Write 1 to reset history. +in10_min_alarm Undervoltage alarm +in10_max_alarm Overvoltage alarm +in10_enable Enable/Disable monitoring. +in10_label Channel label (DRNS) + +in11_input Output voltage (mV). +in11_min Undervoltage threshold +in11_max Overvoltage threshold +in11_lowest Lowest measured voltage +in11_highest Highest measured voltage +in11_reset_history Write 1 to reset history. + Also clears fet bad and short fault logs. +in11_min_alarm Undervoltage alarm +in11_max_alarm Overvoltage alarm +in11_enable Enable/Disable monitoring +in11_fault Failure in the MOSFET. Either bad or shorted FET. +in11_label Channel label (DRAIN) + +in12_input Output voltage (mV). +in12_min Undervoltage threshold +in12_max Overvoltage threshold +in12_lowest Lowest measured voltage +in12_highest Highest measured voltage +in12_reset_history Write 1 to reset history. +in12_min_alarm Undervoltage alarm +in12_max_alarm Overvoltage alarm +in12_enable Enable/Disable monitoring. +in12_label Channel label (ADIN2-ADIN1) + +in13_input Output voltage (mV). +in13_min Undervoltage threshold +in13_max Overvoltage threshold +in13_lowest Lowest measured voltage +in13_highest Highest measured voltage +in13_reset_history Write 1 to reset history. +in13_min_alarm Undervoltage alarm +in13_max_alarm Overvoltage alarm +in13_enable Enable/Disable monitoring. +in13_label Channel label (ADIN4-ADIN3) + +in14_input Output voltage (mV). +in14_min Undervoltage threshold +in14_max Overvoltage threshold +in14_lowest Lowest measured voltage +in14_highest Highest measured voltage +in14_reset_history Write 1 to reset history. +in14_min_alarm Undervoltage alarm +in14_max_alarm Overvoltage alarm +in14_enable Enable/Disable monitoring. +in14_label Channel label (ADIO2-ADIO1) + +in15_input Output voltage (mV). +in15_min Undervoltage threshold +in15_max Overvoltage threshold +in15_lowest Lowest measured voltage +in15_highest Highest measured voltage +in15_reset_history Write 1 to reset history. +in15_min_alarm Undervoltage alarm +in15_max_alarm Overvoltage alarm +in15_enable Enable/Disable monitoring. +in15_label Channel label (ADIO4-ADIO3) + +curr1_input Sense current (mA) +curr1_min Undercurrent threshold +curr1_max Overcurrent threshold +curr1_lowest Lowest measured current +curr1_highest Highest measured current +curr1_reset_history Write 1 to reset curr1 history. + Also clears overcurrent fault logs. +curr1_min_alarm Undercurrent alarm +curr1_max_alarm Overcurrent alarm +curr1_crit_alarm Critical Overcurrent alarm +curr1_label Channel label (ISENSE) + +power1_input Power (in uW) +power1_min Low power threshold +power1_max High power threshold +power1_input_lowest Historical minimum power use +power1_input_highest Historical maximum power use +power1_reset_history Write 1 to reset power1 history. + Also clears power fault logs. +power1_min_alarm Low power alarm +power1_max_alarm High power alarm +power1_label Channel label (Power) + +energy1_input Measured energy over time (in microJoule) +energy1_enable Enable/Disable Energy accumulation +======================= ========================================== + +DebugFs entries +_______________ + +The chip also has a fault log register where failures can be logged. Hence, +as these are logging events, we give access to them in debugfs. Note that +even if some failure is detected in these logs, it does necessarily mean +that the failure is still present. As mentioned in the proper Sysfs entries, +these logs can be cleared by writing in the proper reset_history attribute. + +.. warning:: The debugfs interface is subject to change without notice + and is only available when the kernel is compiled with + ``CONFIG_DEBUG_FS`` defined. + +``/sys/kernel/debug/i2c/i2c-[X]/[X]-addr/`` +contains the following attributes: + +======================= ========================================== +power1_failed_fault_log Set to 1 by a power1 fault occurring. +power1_good_input_fault_log Set to 1 by a power1 good input fault occurring at PGIO3. +in11_fet_short_fault_log Set to 1 when a FET-short fault occurs. +in11_fet_bad_fault_log Set to 1 when a FET-BAD fault occurs. +in0_lcrit_fault_log Set to 1 by a VIN undervoltage fault occurring. +in0_crit_fault_log Set to 1 by a VIN overvoltage fault occurring. +curr1_crit_fault_log Set to 1 by an overcurrent fault occurring. +======================= ========================================== diff --git a/MAINTAINERS b/MAINTAINERS index 6f951e946514..b55a902181cd 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -15257,6 +15257,7 @@ M: Nuno Sá L: linux-hwmon@vger.kernel.org S: Supported F: Documentation/devicetree/bindings/hwmon/adi,ltc4283.yaml +F: drivers/hwmon/ltc4283.c LTC4286 HARDWARE MONITOR DRIVER M: Delphine CC Chiu diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 997bba56fedf..f0470cb565f0 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -1169,6 +1169,18 @@ config SENSORS_LTC4282 This driver can also be built as a module. If so, the module will be called ltc4282. +config SENSORS_LTC4283 + tristate "Analog Devices LTC4283" + depends on I2C + select REGMAP_I2C + select AUXILIARY_BUS + help + If you say yes here you get support for Analog Devices LTC4283 + Negative Voltage Hot Swap Controller I2C interface. + + This driver can also be built as a module. If so, the module will + be called ltc4283. + config SENSORS_LTQ_CPUTEMP bool "Lantiq cpu temperature sensor driver" depends on SOC_XWAY diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index efbd1cb81816..0efea503c5ae 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -148,6 +148,7 @@ obj-$(CONFIG_SENSORS_LTC4245) += ltc4245.o obj-$(CONFIG_SENSORS_LTC4260) += ltc4260.o obj-$(CONFIG_SENSORS_LTC4261) += ltc4261.o obj-$(CONFIG_SENSORS_LTC4282) += ltc4282.o +obj-$(CONFIG_SENSORS_LTC4283) += ltc4283.o obj-$(CONFIG_SENSORS_LTQ_CPUTEMP) += ltq-cputemp.o obj-$(CONFIG_SENSORS_MACSMC_HWMON) += macsmc-hwmon.o obj-$(CONFIG_SENSORS_MAX1111) += max1111.o diff --git a/drivers/hwmon/ltc4283.c b/drivers/hwmon/ltc4283.c new file mode 100644 index 000000000000..d8931c9a4685 --- /dev/null +++ b/drivers/hwmon/ltc4283.c @@ -0,0 +1,1795 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Analog Devices LTC4283 I2C Negative Voltage Hot Swap Controller (HWMON) + * + * Copyright 2025 Analog Devices Inc. + */ +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#define LTC4283_SYSTEM_STATUS 0x00 +#define LTC4283_FAULT_STATUS 0x03 +#define LTC4283_OV_MASK BIT(0) +#define LTC4283_UV_MASK BIT(1) +#define LTC4283_OC_MASK BIT(2) +#define LTC4283_FET_BAD_MASK BIT(3) +#define LTC4283_FET_SHORT_MASK BIT(6) +#define LTC4283_FAULT_LOG 0x04 +#define LTC4283_OV_FAULT_MASK BIT(0) +#define LTC4283_UV_FAULT_MASK BIT(1) +#define LTC4283_OC_FAULT_MASK BIT(2) +#define LTC4283_FET_BAD_FAULT_MASK BIT(3) +#define LTC4283_PGI_FAULT_MASK BIT(4) +#define LTC4283_PWR_FAIL_FAULT_MASK BIT(5) +#define LTC4283_FET_SHORT_FAULT_MASK BIT(6) +#define LTC4283_ADC_ALM_LOG_1 0x05 +#define LTC4283_POWER_LOW_ALM BIT(0) +#define LTC4283_POWER_HIGH_ALM BIT(1) +#define LTC4283_SENSE_LOW_ALM BIT(4) +#define LTC4283_SENSE_HIGH_ALM BIT(5) +#define LTC4283_ADC_ALM_LOG_2 0x06 +#define LTC4283_ADC_ALM_LOG_3 0x07 +#define LTC4283_ADC_ALM_LOG_4 0x08 +#define LTC4283_ADC_ALM_LOG_5 0x09 +#define LTC4283_CONTROL_1 0x0a +#define LTC4283_RW_PAGE_MASK BIT(0) +#define LTC4283_PIGIO2_ACLB_MASK BIT(2) +#define LTC4283_PWRGD_RST_CTRL_MASK BIT(3) +#define LTC4283_FET_BAD_OFF_MASK BIT(4) +#define LTC4283_THERM_TMR_MASK BIT(5) +#define LTC4283_DVDT_MASK BIT(6) +#define LTC4283_CONTROL_2 0x0b +#define LTC4283_OV_RETRY_MASK BIT(0) +#define LTC4283_UV_RETRY_MASK BIT(1) +#define LTC4283_OC_RETRY_MASK GENMASK(3, 2) +#define LTC4283_FET_BAD_RETRY_MASK GENMASK(5, 4) +#define LTC4283_EXT_FAULT_RETRY_MASK BIT(7) +#define LTC4283_RESERVED_OC 0x0c +#define LTC4283_CONFIG_1 0x0d +#define LTC4283_FB_MASK GENMASK(3, 2) +#define LTC4283_ILIM_MASK GENMASK(7, 4) +#define LTC4283_CONFIG_2 0x0e +#define LTC4283_COOLING_DL_MASK GENMASK(3, 1) +#define LTC4283_FTBD_DL_MASK GENMASK(5, 4) +#define LTC4283_CONFIG_3 0x0f +#define LTC4283_VPWR_DRNS_MASK BIT(6) +#define LTC4283_EXTFLT_TURN_OFF_MASK BIT(7) +#define LTC4283_PGIO_CONFIG 0x10 +#define LTC4283_PGIO1_CFG_MASK GENMASK(1, 0) +#define LTC4283_PGIO2_CFG_MASK GENMASK(3, 2) +#define LTC4283_PGIO3_CFG_MASK GENMASK(5, 4) +#define LTC4283_PGIO4_CFG_MASK GENMASK(7, 6) +#define LTC4283_PGIO_CONFIG_2 0x11 +#define LTC4283_ADC_MASK GENMASK(2, 0) +#define LTC4283_ADC_SELECT(c) (0x13 + (c) / 8) +#define LTC4283_ADC_SELECT_MASK(c) BIT((c) % 8) +#define LTC4283_SENSE_MIN_TH 0x1b +#define LTC4283_SENSE_MAX_TH 0x1c +#define LTC4283_VPWR_MIN_TH 0x1d +#define LTC4283_VPWR_MAX_TH 0x1e +#define LTC4283_POWER_MIN_TH 0x1f +#define LTC4283_POWER_MAX_TH 0x20 +#define LTC4283_ADC_2_MIN_TH(c) (0x21 + (c) * 2) +#define LTC4283_ADC_2_MAX_TH(c) (0x22 + (c) * 2) +#define LTC4283_ADC_2_MIN_TH_DIFF(c) (0x39 + (c) * 2) +#define LTC4283_ADC_2_MAX_TH_DIFF(c) (0x3a + (c) * 2) +#define LTC4283_SENSE 0x41 +#define LTC4283_SENSE_MIN 0x42 +#define LTC4283_SENSE_MAX 0x43 +#define LTC4283_VPWR 0x44 +#define LTC4283_VPWR_MIN 0x45 +#define LTC4283_VPWR_MAX 0x46 +#define LTC4283_POWER 0x47 +#define LTC4283_POWER_MIN 0x48 +#define LTC4283_POWER_MAX 0x49 +#define LTC4283_RESERVED_68 0x68 +#define LTC4283_RESERVED_6D 0x6D +/* get channels from ADC 2 */ +#define LTC4283_ADC_2(c) (0x4a + (c) * 3) +#define LTC4283_ADC_2_MIN(c) (0x4b + (c) * 3) +#define LTC4283_ADC_2_MAX(c) (0x4c + (c) * 3) +#define LTC4283_ADC_2_DIFF(c) (0x6e + (c) * 3) +#define LTC4283_ADC_2_MIN_DIFF(c) (0x6f + (c) * 3) +#define LTC4283_ADC_2_MAX_DIFF(c) (0x70 + (c) * 3) +#define LTC4283_ENERGY 0x7a +#define LTC4283_METER_CONTROL 0x84 +#define LTC4283_INTEGRATE_I_MASK BIT(0) +#define LTC4283_METER_HALT_MASK BIT(6) +#define LTC4283_RESERVED_86 0x86 +#define LTC4283_RESERVED_8F 0x8F +#define LTC4283_FAULT_LOG_CTRL 0x90 +#define LTC4283_FAULT_LOG_EN_MASK BIT(7) +#define LTC4283_RESERVED_91 0x91 +#define LTC4283_RESERVED_A1 0xA1 +#define LTC4283_RESERVED_A3 0xA3 +#define LTC4283_RESERVED_AC 0xAC +#define LTC4283_POWER_PLAY_MSB 0xE7 +#define LTC4283_POWER_PLAY_LSB 0xE8 +#define LTC4283_RESERVED_F1 0xF1 +#define LTC4283_RESERVED_FF 0xFF + +/* also applies for differential channels */ +#define LTC4283_ADC1_FS_uV 32768 +#define LTC4283_ADC2_FS_mV 2048 +#define LTC4283_TCONV_uS 64103 +#define LTC4283_VILIM_MIN_uV 15000 +#define LTC4283_VILIM_MAX_uV 30000 +#define LTC4283_VILIM_RANGE \ + (LTC4283_VILIM_MAX_uV - LTC4283_VILIM_MIN_uV + 1) + +#define LTC4283_PGIO_FUNC_GPIO 2 +#define LTC4283_PGIO2_FUNC_ACLB 3 + +/* + * Maximum value for rsense in nano ohms. The reasoning for this value is that + * it's the max value for which multiplying by 256 does not overflow long on + * 32bits. For the minimum value, is a sane minimum rsense for which power_max + * does not overflow 32bits. + */ +#define LTC4283_MAX_RSENSE 1677721599 +#define LTC4283_MIN_RSENSE 50000 + +/* voltage channels */ +enum { + LTC4283_CHAN_VIN, + LTC4283_CHAN_VPWR, + LTC4283_CHAN_ADI_1, + LTC4283_CHAN_ADI_2, + LTC4283_CHAN_ADI_3, + LTC4283_CHAN_ADI_4, + LTC4283_CHAN_ADIO_1, + LTC4283_CHAN_ADIO_2, + LTC4283_CHAN_ADIO_3, + LTC4283_CHAN_ADIO_4, + LTC4283_CHAN_DRNS, + LTC4283_CHAN_DRAIN, + /* differential channels */ + LTC4283_CHAN_ADIN12, + LTC4283_CHAN_ADIN34, + LTC4283_CHAN_ADIO12, + LTC4283_CHAN_ADIO34, + LTC4283_CHAN_MAX +}; + +/* Just for ease of use on the regmap */ +#define LTC4283_ADIO34_MAX \ + LTC4283_ADC_2_MAX_DIFF(LTC4283_CHAN_ADIO34 - LTC4283_CHAN_ADIN12) + +struct ltc4283_hwmon { + struct regmap *map; + struct i2c_client *client; + unsigned long gpio_mask; + unsigned long ch_enable_mask; + /* in microwatt */ + unsigned long power_max; + /* in millivolt */ + u32 vsense_max; + /* in tenths of microohm*/ + u32 rsense; + bool energy_en; + bool ext_fault; +}; + +static int ltc4283_read_voltage_word(const struct ltc4283_hwmon *st, + u32 reg, u32 fs, long *val) +{ + unsigned int __raw; + int ret; + + ret = regmap_read(st->map, reg, &__raw); + if (ret) + return ret; + + *val = DIV_ROUND_CLOSEST(__raw * fs, BIT(16)); + return 0; +} + +static int ltc4283_read_voltage_byte(const struct ltc4283_hwmon *st, + u32 reg, u32 fs, long *val) +{ + int ret; + u32 in; + + ret = regmap_read(st->map, reg, &in); + if (ret) + return ret; + + *val = DIV_ROUND_CLOSEST(in * fs, BIT(8)); + return 0; +} + +static u32 ltc4283_in_reg(u32 attr, u32 channel) +{ + switch (attr) { + case hwmon_in_input: + if (channel == LTC4283_CHAN_VPWR) + return LTC4283_VPWR; + if (channel >= LTC4283_CHAN_ADI_1 && channel <= LTC4283_CHAN_DRAIN) + return LTC4283_ADC_2(channel - LTC4283_CHAN_ADI_1); + return LTC4283_ADC_2_DIFF(channel - LTC4283_CHAN_ADIN12); + case hwmon_in_highest: + if (channel == LTC4283_CHAN_VPWR) + return LTC4283_VPWR_MAX; + if (channel >= LTC4283_CHAN_ADI_1 && channel <= LTC4283_CHAN_DRAIN) + return LTC4283_ADC_2_MAX(channel - LTC4283_CHAN_ADI_1); + return LTC4283_ADC_2_MAX_DIFF(channel - LTC4283_CHAN_ADIN12); + case hwmon_in_lowest: + if (channel == LTC4283_CHAN_VPWR) + return LTC4283_VPWR_MIN; + if (channel >= LTC4283_CHAN_ADI_1 && channel <= LTC4283_CHAN_DRAIN) + return LTC4283_ADC_2_MIN(channel - LTC4283_CHAN_ADI_1); + return LTC4283_ADC_2_MIN_DIFF(channel - LTC4283_CHAN_ADIN12); + case hwmon_in_max: + if (channel == LTC4283_CHAN_VPWR) + return LTC4283_VPWR_MAX_TH; + if (channel >= LTC4283_CHAN_ADI_1 && channel <= LTC4283_CHAN_DRAIN) + return LTC4283_ADC_2_MAX_TH(channel - LTC4283_CHAN_ADI_1); + return LTC4283_ADC_2_MAX_TH_DIFF(channel - LTC4283_CHAN_ADIN12); + default: + if (channel == LTC4283_CHAN_VPWR) + return LTC4283_VPWR_MIN_TH; + if (channel >= LTC4283_CHAN_ADI_1 && channel <= LTC4283_CHAN_DRAIN) + return LTC4283_ADC_2_MIN_TH(channel - LTC4283_CHAN_ADI_1); + return LTC4283_ADC_2_MIN_TH_DIFF(channel - LTC4283_CHAN_ADIN12); + } +} + +static int ltc4283_read_in_vals(const struct ltc4283_hwmon *st, + u32 attr, u32 channel, long *val) +{ + u32 reg = ltc4283_in_reg(attr, channel); + int ret; + + if (channel < LTC4283_CHAN_ADIN12) { + if (attr != hwmon_in_max && attr != hwmon_in_min) + return ltc4283_read_voltage_word(st, reg, + LTC4283_ADC2_FS_mV, + val); + + return ltc4283_read_voltage_byte(st, reg, + LTC4283_ADC2_FS_mV, val); + } + + if (attr != hwmon_in_max && attr != hwmon_in_min) + ret = ltc4283_read_voltage_word(st, reg, + LTC4283_ADC1_FS_uV, val); + else + ret = ltc4283_read_voltage_byte(st, reg, + LTC4283_ADC1_FS_uV, val); + if (ret) + return ret; + + *val = DIV_ROUND_CLOSEST(*val, MILLI); + return 0; +} + +static int ltc4283_read_alarm(struct ltc4283_hwmon *st, u32 reg, + u32 mask, long *val) +{ + u32 alarm; + int ret; + + ret = regmap_read(st->map, reg, &alarm); + if (ret) + return ret; + + *val = !!(alarm & mask); + + /* If not status/fault logs, clear the alarm after reading it. */ + if (reg != LTC4283_FAULT_STATUS && reg != LTC4283_FAULT_LOG) + return regmap_write(st->map, reg, alarm & ~mask); + + return 0; +} + +static int ltc4283_read_in_alarm(struct ltc4283_hwmon *st, u32 channel, + bool max_alm, long *val) +{ + if (channel == LTC4283_CHAN_VPWR) + return ltc4283_read_alarm(st, LTC4283_ADC_ALM_LOG_1, + BIT(2 + max_alm), val); + + if (channel >= LTC4283_CHAN_ADI_1 && channel <= LTC4283_CHAN_ADI_4) { + u32 bit = (channel - LTC4283_CHAN_ADI_1) * 2; + /* + * Lower channels go to higher bits. We also want to go +1 down + * in the min_alarm case. + */ + return ltc4283_read_alarm(st, LTC4283_ADC_ALM_LOG_2, + BIT(7 - bit - !max_alm), val); + } + + if (channel >= LTC4283_CHAN_ADIO_1 && channel <= LTC4283_CHAN_ADIO_4) { + u32 bit = (channel - LTC4283_CHAN_ADIO_1) * 2; + + return ltc4283_read_alarm(st, LTC4283_ADC_ALM_LOG_3, + BIT(7 - bit - !max_alm), val); + } + + if (channel >= LTC4283_CHAN_ADIN12 && channel <= LTC4283_CHAN_ADIO34) { + u32 bit = (channel - LTC4283_CHAN_ADIN12) * 2; + + return ltc4283_read_alarm(st, LTC4283_ADC_ALM_LOG_5, + BIT(7 - bit - !max_alm), val); + } + + if (channel == LTC4283_CHAN_DRNS) + return ltc4283_read_alarm(st, LTC4283_ADC_ALM_LOG_4, + BIT(6 + max_alm), val); + + return ltc4283_read_alarm(st, LTC4283_ADC_ALM_LOG_4, BIT(4 + max_alm), + val); +} + +static int ltc4283_read_in(struct ltc4283_hwmon *st, u32 attr, u32 channel, + long *val) +{ + switch (attr) { + case hwmon_in_input: + if (!test_bit(channel, &st->ch_enable_mask)) + return -ENODATA; + + return ltc4283_read_in_vals(st, attr, channel, val); + case hwmon_in_highest: + case hwmon_in_lowest: + case hwmon_in_max: + case hwmon_in_min: + return ltc4283_read_in_vals(st, attr, channel, val); + case hwmon_in_max_alarm: + return ltc4283_read_in_alarm(st, channel, true, val); + case hwmon_in_min_alarm: + return ltc4283_read_in_alarm(st, channel, false, val); + case hwmon_in_crit_alarm: + return ltc4283_read_alarm(st, LTC4283_FAULT_STATUS, + LTC4283_OV_MASK, val); + case hwmon_in_lcrit_alarm: + return ltc4283_read_alarm(st, LTC4283_FAULT_STATUS, + LTC4283_UV_MASK, val); + case hwmon_in_fault: + /* + * We report failure if we detect either a fer_bad or a + * fet_short in the status register. + */ + return ltc4283_read_alarm(st, LTC4283_FAULT_STATUS, + LTC4283_FET_BAD_MASK | LTC4283_FET_SHORT_MASK, val); + case hwmon_in_enable: + *val = test_bit(channel, &st->ch_enable_mask); + return 0; + default: + return -EOPNOTSUPP; + } + return 0; +} + +static int ltc4283_read_current_word(const struct ltc4283_hwmon *st, u32 reg, + long *val) +{ + u64 temp = (u64)LTC4283_ADC1_FS_uV * DECA * MILLI; + unsigned int __raw; + int ret; + + ret = regmap_read(st->map, reg, &__raw); + if (ret) + return ret; + + *val = DIV64_U64_ROUND_CLOSEST(__raw * temp, + BIT_ULL(16) * st->rsense); + + return 0; +} + +static int ltc4283_read_current_byte(const struct ltc4283_hwmon *st, u32 reg, + long *val) +{ + u64 temp = (u64)LTC4283_ADC1_FS_uV * DECA * MILLI; + u32 curr; + int ret; + + ret = regmap_read(st->map, reg, &curr); + if (ret) + return ret; + + *val = DIV_ROUND_CLOSEST_ULL(curr * temp, BIT(8) * st->rsense); + return 0; +} + +static int ltc4283_read_curr(struct ltc4283_hwmon *st, u32 attr, long *val) +{ + switch (attr) { + case hwmon_curr_input: + return ltc4283_read_current_word(st, LTC4283_SENSE, val); + case hwmon_curr_highest: + return ltc4283_read_current_word(st, LTC4283_SENSE_MAX, val); + case hwmon_curr_lowest: + return ltc4283_read_current_word(st, LTC4283_SENSE_MIN, val); + case hwmon_curr_max: + return ltc4283_read_current_byte(st, LTC4283_SENSE_MAX_TH, val); + case hwmon_curr_min: + return ltc4283_read_current_byte(st, LTC4283_SENSE_MIN_TH, val); + case hwmon_curr_max_alarm: + return ltc4283_read_alarm(st, LTC4283_ADC_ALM_LOG_1, + LTC4283_SENSE_HIGH_ALM, val); + case hwmon_curr_min_alarm: + return ltc4283_read_alarm(st, LTC4283_ADC_ALM_LOG_1, + LTC4283_SENSE_LOW_ALM, val); + case hwmon_curr_crit_alarm: + return ltc4283_read_alarm(st, LTC4283_FAULT_STATUS, + LTC4283_OC_MASK, val); + default: + return -EOPNOTSUPP; + } +} + +static int ltc4283_read_power_word(const struct ltc4283_hwmon *st, + u32 reg, long *val) +{ + u64 temp = (u64)LTC4283_ADC1_FS_uV * LTC4283_ADC2_FS_mV * DECA * MILLI; + unsigned int __raw; + int ret; + + ret = regmap_read(st->map, reg, &__raw); + if (ret) + return ret; + + /* + * Power is given by: + * P = CODE(16b) * 32.768mV * 2.048V / (2^16 * Rsense) + */ + *val = DIV64_U64_ROUND_CLOSEST(temp * __raw, BIT_ULL(16) * st->rsense); + + return 0; +} + +static int ltc4283_read_power_byte(const struct ltc4283_hwmon *st, + u32 reg, long *val) +{ + u64 temp = (u64)LTC4283_ADC1_FS_uV * LTC4283_ADC2_FS_mV * DECA * MILLI; + u32 power; + int ret; + + ret = regmap_read(st->map, reg, &power); + if (ret) + return ret; + + *val = DIV_ROUND_CLOSEST_ULL(power * temp, BIT(8) * st->rsense); + + return 0; +} + +static int ltc4283_read_power(struct ltc4283_hwmon *st, u32 attr, long *val) +{ + switch (attr) { + case hwmon_power_input: + return ltc4283_read_power_word(st, LTC4283_POWER, val); + case hwmon_power_input_highest: + return ltc4283_read_power_word(st, LTC4283_POWER_MAX, val); + case hwmon_power_input_lowest: + return ltc4283_read_power_word(st, LTC4283_POWER_MIN, val); + case hwmon_power_max_alarm: + return ltc4283_read_alarm(st, LTC4283_ADC_ALM_LOG_1, + LTC4283_POWER_HIGH_ALM, val); + case hwmon_power_min_alarm: + return ltc4283_read_alarm(st, LTC4283_ADC_ALM_LOG_1, + LTC4283_POWER_LOW_ALM, val); + case hwmon_power_max: + return ltc4283_read_power_byte(st, LTC4283_POWER_MAX_TH, val); + case hwmon_power_min: + return ltc4283_read_power_byte(st, LTC4283_POWER_MIN_TH, val); + default: + return -EOPNOTSUPP; + } +} + +static int ltc4283_read_energy(struct ltc4283_hwmon *st, u32 attr, s64 *val) +{ + u64 temp = LTC4283_ADC1_FS_uV * LTC4283_ADC2_FS_mV, energy; + u8 raw[8] = {}; + int ret; + + if (!st->energy_en) + return -ENODATA; + + ret = i2c_smbus_read_i2c_block_data(st->client, LTC4283_ENERGY, 6, raw); + if (ret < 0) + return ret; + if (ret != 6) + return -EIO; + + energy = get_unaligned_be64(raw) >> 16; + + /* + * The formula for energy is given by: + * E = CODE(48b) * 32.768mV * 2.048V * Tconv / 2^24 * Rsense + * + * As Rsense can have tenths of micro-ohm resolution, we need to + * multiply by DECA to get microjoule. + */ + + /* + * Use mul_u64_u64_div_u64() to handle the 128-bit intermediate + * product of energy (up to 48 bits) * temp * Tconv without overflow. + * Multiply rsense by CENTI to convert from tenths-of-microohm back + * to nanoohm so the result comes out in microjoule. + */ + energy = mul_u64_u64_div_u64(energy, temp * LTC4283_TCONV_uS, + BIT_ULL(24) * st->rsense * CENTI); + + *val = energy; + return 0; +} + +static int ltc4283_read(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long *val) +{ + struct ltc4283_hwmon *st = dev_get_drvdata(dev); + + switch (type) { + case hwmon_in: + return ltc4283_read_in(st, attr, channel, val); + case hwmon_curr: + return ltc4283_read_curr(st, attr, val); + case hwmon_power: + return ltc4283_read_power(st, attr, val); + case hwmon_energy: + *val = st->energy_en; + return 0; + case hwmon_energy64: + return ltc4283_read_energy(st, attr, (s64 *)val); + default: + return -EOPNOTSUPP; + } +} + +static int ltc4283_write_power_byte(const struct ltc4283_hwmon *st, u32 reg, + long val) +{ + u64 temp = (u64)LTC4283_ADC1_FS_uV * LTC4283_ADC2_FS_mV * DECA * MILLI; + u32 __raw; + + val = clamp_val(val, 0, st->power_max); + __raw = DIV64_U64_ROUND_CLOSEST(val * BIT_ULL(8) * st->rsense, temp); + + return regmap_write(st->map, reg, __raw); +} + +static int ltc4283_write_power_word(const struct ltc4283_hwmon *st, + u32 reg, unsigned long val) +{ + u64 divisor = (u64)LTC4283_ADC1_FS_uV * LTC4283_ADC2_FS_mV * DECA * MILLI; + u16 __raw; + + __raw = mul_u64_u64_div_u64(val, st->rsense * BIT_ULL(16), divisor); + + return regmap_write(st->map, reg, __raw); +} + +static int ltc4283_reset_power_hist(struct ltc4283_hwmon *st) +{ + int ret; + + ret = ltc4283_write_power_word(st, LTC4283_POWER_MIN, st->power_max); + if (ret) + return ret; + + ret = ltc4283_write_power_word(st, LTC4283_POWER_MAX, 0); + if (ret) + return ret; + + /* Clear possible power faults. */ + return regmap_clear_bits(st->map, LTC4283_FAULT_LOG, + LTC4283_PWR_FAIL_FAULT_MASK | LTC4283_PGI_FAULT_MASK); +} + +static int ltc4283_write_power(struct ltc4283_hwmon *st, u32 attr, long val) +{ + switch (attr) { + case hwmon_power_max: + return ltc4283_write_power_byte(st, LTC4283_POWER_MAX_TH, val); + case hwmon_power_min: + return ltc4283_write_power_byte(st, LTC4283_POWER_MIN_TH, val); + case hwmon_power_reset_history: + return ltc4283_reset_power_hist(st); + default: + return -EOPNOTSUPP; + } +} + +static int ltc4283_write_in_history(struct ltc4283_hwmon *st, u32 reg, + long lowest, u32 fs) +{ + u32 __raw; + int ret; + + __raw = DIV_ROUND_CLOSEST(BIT(16) * lowest, fs); + if (__raw == BIT(16)) + __raw = U16_MAX; + + ret = regmap_write(st->map, reg, __raw); + if (ret) + return ret; + + return regmap_write(st->map, reg + 1, 0); +} + +static int ltc4283_write_in_byte(const struct ltc4283_hwmon *st, + u32 reg, u32 fs, long val) +{ + u32 __raw; + + val = clamp_val(val, 0, fs); + __raw = DIV_ROUND_CLOSEST(val * BIT(8), fs); + if (__raw == BIT(8)) + __raw = U8_MAX; + + return regmap_write(st->map, reg, __raw); +} + +static int ltc4283_reset_in_hist(struct ltc4283_hwmon *st, u32 channel) +{ + u32 reg, fs; + int ret; + + /* + * Make sure to clear possible under/over voltage faults. Otherwise the + * chip won't latch on again. + */ + if (channel == LTC4283_CHAN_VIN) + return regmap_clear_bits(st->map, LTC4283_FAULT_LOG, + LTC4283_OV_FAULT_MASK | LTC4283_UV_FAULT_MASK); + + if (channel == LTC4283_CHAN_VPWR) + return ltc4283_write_in_history(st, LTC4283_VPWR_MIN, + LTC4283_ADC2_FS_mV, + LTC4283_ADC2_FS_mV); + + if (channel >= LTC4283_CHAN_ADI_1 && channel <= LTC4283_CHAN_DRAIN) { + fs = LTC4283_ADC2_FS_mV; + reg = LTC4283_ADC_2_MIN(channel - LTC4283_CHAN_ADI_1); + } else { + fs = LTC4283_ADC1_FS_uV; + reg = LTC4283_ADC_2_MIN_DIFF(channel - LTC4283_CHAN_ADIN12); + } + + ret = ltc4283_write_in_history(st, reg, fs, fs); + if (ret) + return ret; + if (channel != LTC4283_CHAN_DRAIN) + return 0; + + /* Then, let's also clear possible fet faults. Same as above. */ + return regmap_clear_bits(st->map, LTC4283_FAULT_LOG, + LTC4283_FET_BAD_FAULT_MASK | LTC4283_FET_SHORT_FAULT_MASK); +} + +static int ltc4283_write_in_en(struct ltc4283_hwmon *st, u32 channel, bool en) +{ + unsigned int bit, adc_idx = channel - LTC4283_CHAN_ADI_1; + unsigned int reg = LTC4283_ADC_SELECT(adc_idx); + int ret; + + bit = LTC4283_ADC_SELECT_MASK(adc_idx); + if (channel > LTC4283_CHAN_DRAIN) + /* Account for two reserved fields after DRAIN. */ + bit <<= 2; + + if (en) + ret = regmap_set_bits(st->map, reg, bit); + else + ret = regmap_clear_bits(st->map, reg, bit); + if (ret) + return ret; + + __assign_bit(channel, &st->ch_enable_mask, en); + return 0; +} + +static int ltc4283_write_minmax(struct ltc4283_hwmon *st, long val, + u32 channel, bool is_max) +{ + u32 reg; + + if (channel == LTC4283_CHAN_VPWR) { + if (is_max) + return ltc4283_write_in_byte(st, LTC4283_VPWR_MAX_TH, + LTC4283_ADC2_FS_mV, val); + + return ltc4283_write_in_byte(st, LTC4283_VPWR_MIN_TH, + LTC4283_ADC2_FS_mV, val); + } + + if (channel >= LTC4283_CHAN_ADI_1 && channel <= LTC4283_CHAN_DRAIN) { + if (is_max) { + reg = LTC4283_ADC_2_MAX_TH(channel - LTC4283_CHAN_ADI_1); + return ltc4283_write_in_byte(st, reg, + LTC4283_ADC2_FS_mV, val); + } + + reg = LTC4283_ADC_2_MIN_TH(channel - LTC4283_CHAN_ADI_1); + return ltc4283_write_in_byte(st, reg, LTC4283_ADC2_FS_mV, val); + } + + /* Clamp before multiplying to avoid overflow on any arch. */ + val = clamp_val(val, 0, LONG_MAX / MILLI); + + if (is_max) { + reg = LTC4283_ADC_2_MAX_TH_DIFF(channel - LTC4283_CHAN_ADIN12); + return ltc4283_write_in_byte(st, reg, LTC4283_ADC1_FS_uV, + val * MILLI); + } + + reg = LTC4283_ADC_2_MIN_TH_DIFF(channel - LTC4283_CHAN_ADIN12); + return ltc4283_write_in_byte(st, reg, LTC4283_ADC1_FS_uV, val * MILLI); +} + +static int ltc4283_write_in(struct ltc4283_hwmon *st, u32 attr, long val, + int channel) +{ + switch (attr) { + case hwmon_in_max: + return ltc4283_write_minmax(st, val, channel, true); + case hwmon_in_min: + return ltc4283_write_minmax(st, val, channel, false); + case hwmon_in_reset_history: + return ltc4283_reset_in_hist(st, channel); + case hwmon_in_enable: + return ltc4283_write_in_en(st, channel, !!val); + default: + return -EOPNOTSUPP; + } +} + +static int ltc4283_write_curr_byte(const struct ltc4283_hwmon *st, + u32 reg, long val) +{ + u32 temp = LTC4283_ADC1_FS_uV * DECA * MILLI; + u32 reg_val, isense_max; + + isense_max = DIV_ROUND_CLOSEST(st->vsense_max * MICRO * DECA, st->rsense); + val = clamp_val(val, 0, isense_max); + reg_val = DIV_ROUND_CLOSEST_ULL(val * BIT_ULL(8) * st->rsense, temp); + + return regmap_write(st->map, reg, reg_val); +} + +static int ltc4283_write_curr_history(struct ltc4283_hwmon *st) +{ + int ret; + + ret = ltc4283_write_in_history(st, LTC4283_SENSE_MIN, + st->vsense_max * MILLI, + LTC4283_ADC1_FS_uV); + if (ret) + return ret; + + /* Now, let's also clear possible overcurrent logs. */ + return regmap_clear_bits(st->map, LTC4283_FAULT_LOG, + LTC4283_OC_FAULT_MASK); +} + +static int ltc4283_write_curr(struct ltc4283_hwmon *st, u32 attr, long val) +{ + switch (attr) { + case hwmon_curr_max: + return ltc4283_write_curr_byte(st, LTC4283_SENSE_MAX_TH, val); + case hwmon_curr_min: + return ltc4283_write_curr_byte(st, LTC4283_SENSE_MIN_TH, val); + case hwmon_curr_reset_history: + return ltc4283_write_curr_history(st); + default: + return -EOPNOTSUPP; + } +} + +static int ltc4283_energy_enable_set(struct ltc4283_hwmon *st, long val) +{ + int ret; + + /* Setting the bit halts the meter. */ + val = !!val; + ret = regmap_update_bits(st->map, LTC4283_METER_CONTROL, + LTC4283_METER_HALT_MASK, + FIELD_PREP(LTC4283_METER_HALT_MASK, !val)); + if (ret) + return ret; + + st->energy_en = val; + + return 0; +} + +static int ltc4283_write(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long val) +{ + struct ltc4283_hwmon *st = dev_get_drvdata(dev); + + switch (type) { + case hwmon_power: + return ltc4283_write_power(st, attr, val); + case hwmon_in: + return ltc4283_write_in(st, attr, val, channel); + case hwmon_curr: + return ltc4283_write_curr(st, attr, val); + case hwmon_energy: + return ltc4283_energy_enable_set(st, val); + default: + return -EOPNOTSUPP; + } +} + +static umode_t ltc4283_in_is_visible(const struct ltc4283_hwmon *st, + u32 attr, int channel) +{ + /* If ADIO is set as a GPIO, don´t make it visible. */ + if (channel >= LTC4283_CHAN_ADIO_1 && channel <= LTC4283_CHAN_ADIO_4) { + /* ADIOX pins come at index 0 in the gpio mask. */ + channel -= LTC4283_CHAN_ADIO_1; + if (test_bit(channel, &st->gpio_mask)) + return 0; + } + + /* Also take care of differential channels. */ + if (channel >= LTC4283_CHAN_ADIO12 && channel <= LTC4283_CHAN_ADIO34) { + channel -= LTC4283_CHAN_ADIO12; + /* If one channel in the pair is used, make it invisible. */ + if (test_bit(channel * 2, &st->gpio_mask) || + test_bit(channel * 2 + 1, &st->gpio_mask)) + return 0; + } + + switch (attr) { + case hwmon_in_input: + case hwmon_in_highest: + case hwmon_in_lowest: + case hwmon_in_max_alarm: + case hwmon_in_min_alarm: + case hwmon_in_label: + case hwmon_in_lcrit_alarm: + case hwmon_in_crit_alarm: + case hwmon_in_fault: + return 0444; + case hwmon_in_max: + case hwmon_in_min: + case hwmon_in_enable: + return 0644; + case hwmon_in_reset_history: + return 0200; + default: + return 0; + } +} + +static umode_t ltc4283_curr_is_visible(u32 attr) +{ + switch (attr) { + case hwmon_curr_input: + case hwmon_curr_highest: + case hwmon_curr_lowest: + case hwmon_curr_max_alarm: + case hwmon_curr_min_alarm: + case hwmon_curr_crit_alarm: + case hwmon_curr_label: + return 0444; + case hwmon_curr_max: + case hwmon_curr_min: + return 0644; + case hwmon_curr_reset_history: + return 0200; + default: + return 0; + } +} + +static umode_t ltc4283_power_is_visible(u32 attr) +{ + switch (attr) { + case hwmon_power_input: + case hwmon_power_input_highest: + case hwmon_power_input_lowest: + case hwmon_power_label: + case hwmon_power_max_alarm: + case hwmon_power_min_alarm: + return 0444; + case hwmon_power_max: + case hwmon_power_min: + return 0644; + case hwmon_power_reset_history: + return 0200; + default: + return 0; + } +} + +static umode_t ltc4283_is_visible(const void *data, + enum hwmon_sensor_types type, + u32 attr, int channel) +{ + switch (type) { + case hwmon_in: + return ltc4283_in_is_visible(data, attr, channel); + case hwmon_curr: + return ltc4283_curr_is_visible(attr); + case hwmon_power: + return ltc4283_power_is_visible(attr); + case hwmon_energy: + /* hwmon_energy_enable */ + return 0644; + case hwmon_energy64: + /* hwmon_energy_input */ + return 0444; + default: + return 0; + } +} + +static const char * const ltc4283_in_strs[] = { + "VIN", "VPWR", "VADI1", "VADI2", "VADI3", "VADI4", "VADIO1", "VADIO2", + "VADIO3", "VADIO4", "DRNS", "DRAIN", "ADIN2-ADIN1", "ADIN4-ADIN3", + "ADIO2-ADIO1", "ADIO4-ADIO3" +}; + +static int ltc4283_read_labels(struct device *dev, + enum hwmon_sensor_types type, + u32 attr, int channel, const char **str) +{ + switch (type) { + case hwmon_in: + *str = ltc4283_in_strs[channel]; + return 0; + case hwmon_curr: + *str = "ISENSE"; + return 0; + case hwmon_power: + *str = "Power"; + return 0; + default: + return -EOPNOTSUPP; + } +} + +/* + * Set max limits for ISENSE and Power as that depends on the max voltage on + * rsense that is defined in ILIM_ADJUST. This is specially important for power + * because for some rsense and vfsout values, if we allow the default raw 255 + * value, that would overflow long in 32bit archs when reading back the max + * power limit. + */ +static int ltc4283_set_max_limits(struct ltc4283_hwmon *st, struct device *dev) +{ + u32 temp = st->vsense_max * DECA * MICRO; + int ret; + + ret = ltc4283_write_in_byte(st, LTC4283_SENSE_MAX_TH, LTC4283_ADC1_FS_uV, + st->vsense_max * MILLI); + if (ret) + return ret; + + /* Power is given by ISENSE * Vout. */ + st->power_max = DIV_ROUND_CLOSEST(temp, st->rsense) * LTC4283_ADC2_FS_mV; + return ltc4283_write_power_byte(st, LTC4283_POWER_MAX_TH, st->power_max); +} + +static int ltc4283_parse_array_prop(const struct ltc4283_hwmon *st, + struct device *dev, const char *prop, + const u32 *vals, u32 n_vals) +{ + u32 prop_val; + int ret; + u32 i; + + ret = device_property_read_u32(dev, prop, &prop_val); + if (ret) + return n_vals; + + for (i = 0; i < n_vals; i++) { + if (prop_val != vals[i]) + continue; + + return i; + } + + return dev_err_probe(dev, -EINVAL, + "Invalid %s property value %u\n", prop, prop_val); +} + +static int ltc4283_get_defaults(struct ltc4283_hwmon *st) +{ + u32 reg_val, ilm_adjust, c; + int ret; + + ret = regmap_read(st->map, LTC4283_METER_CONTROL, ®_val); + if (ret) + return ret; + + st->energy_en = !FIELD_GET(LTC4283_METER_HALT_MASK, reg_val); + + ret = regmap_read(st->map, LTC4283_CONFIG_1, ®_val); + if (ret) + return ret; + + ilm_adjust = FIELD_GET(LTC4283_ILIM_MASK, reg_val); + st->vsense_max = LTC4283_VILIM_MIN_uV / MILLI + ilm_adjust; + + ret = regmap_read(st->map, LTC4283_PGIO_CONFIG, ®_val); + if (ret) + return ret; + + /* Can be latter overwritten in ltc4283_pgio_config() */ + if (FIELD_GET(LTC4283_PGIO4_CFG_MASK, reg_val) < LTC4283_PGIO_FUNC_GPIO) + st->ext_fault = true; + + /* VPWR and VIN are always enabled */ + __set_bit(LTC4283_CHAN_VIN, &st->ch_enable_mask); + __set_bit(LTC4283_CHAN_VPWR, &st->ch_enable_mask); + for (c = LTC4283_CHAN_ADI_1; c < LTC4283_CHAN_MAX; c++) { + u32 chan = c - LTC4283_CHAN_ADI_1, bit; + + ret = regmap_read(st->map, LTC4283_ADC_SELECT(chan), ®_val); + if (ret) + return ret; + + bit = LTC4283_ADC_SELECT_MASK(chan); + if (c > LTC4283_CHAN_DRAIN) + /* account for two reserved fields after DRAIN */ + bit <<= 2; + + if (!(bit & reg_val)) + continue; + + __set_bit(c, &st->ch_enable_mask); + } + + return 0; +} + +static const char * const ltc4283_pgio1_funcs[] = { + "inverted_power_good", "power_good", "gpio" +}; + +static const char * const ltc4283_pgio2_funcs[] = { + "inverted_power_good", "power_good", "gpio", "active_current_limiting" +}; + +static const char * const ltc4283_pgio3_funcs[] = { + "inverted_power_good_input", "power_good_input", "gpio" +}; + +static const char * const ltc4283_pgio4_funcs[] = { + "inverted_external_fault", "external_fault", "gpio" +}; + +enum { + LTC4283_PIN_ADIO1, + LTC4283_PIN_ADIO2, + LTC4283_PIN_ADIO3, + LTC4283_PIN_ADIO4, + LTC4283_PIN_PGIO1, + LTC4283_PIN_PGIO2, + LTC4283_PIN_PGIO3, + LTC4283_PIN_PGIO4, +}; + +static int ltc4283_pgio_config(struct ltc4283_hwmon *st, struct device *dev) +{ + int ret, func; + + func = device_property_match_property_string(dev, "adi,pgio1-func", + ltc4283_pgio1_funcs, + ARRAY_SIZE(ltc4283_pgio1_funcs)); + if (func < 0 && func != -EINVAL) + return dev_err_probe(dev, func, + "Invalid adi,pgio1-func property\n"); + if (func >= 0) { + if (func == LTC4283_PGIO_FUNC_GPIO) { + __set_bit(LTC4283_PIN_PGIO1, &st->gpio_mask); + /* If GPIO, default to an input pin. */ + func++; + } + + ret = regmap_update_bits(st->map, LTC4283_PGIO_CONFIG, + LTC4283_PGIO1_CFG_MASK, + FIELD_PREP(LTC4283_PGIO1_CFG_MASK, func)); + if (ret) + return ret; + } + + func = device_property_match_property_string(dev, "adi,pgio2-func", + ltc4283_pgio2_funcs, + ARRAY_SIZE(ltc4283_pgio2_funcs)); + + if (func < 0 && func != -EINVAL) + return dev_err_probe(dev, func, + "Invalid adi,pgio2-func property\n"); + if (func >= 0) { + if (func != LTC4283_PGIO2_FUNC_ACLB) { + if (func == LTC4283_PGIO_FUNC_GPIO) { + __set_bit(LTC4283_PIN_PGIO2, &st->gpio_mask); + func++; + } + + ret = regmap_update_bits(st->map, LTC4283_PGIO_CONFIG, + LTC4283_PGIO2_CFG_MASK, + FIELD_PREP(LTC4283_PGIO2_CFG_MASK, func)); + } else { + ret = regmap_set_bits(st->map, LTC4283_CONTROL_1, + LTC4283_PIGIO2_ACLB_MASK); + } + + if (ret) + return ret; + } + + func = device_property_match_property_string(dev, "adi,pgio3-func", + ltc4283_pgio3_funcs, + ARRAY_SIZE(ltc4283_pgio3_funcs)); + + if (func < 0 && func != -EINVAL) + return dev_err_probe(dev, func, + "Invalid adi,pgio3-func property\n"); + if (func >= 0) { + if (func == LTC4283_PGIO_FUNC_GPIO) { + __set_bit(LTC4283_PIN_PGIO3, &st->gpio_mask); + func++; + } + + ret = regmap_update_bits(st->map, LTC4283_PGIO_CONFIG, + LTC4283_PGIO3_CFG_MASK, + FIELD_PREP(LTC4283_PGIO3_CFG_MASK, func)); + if (ret) + return ret; + } + + func = device_property_match_property_string(dev, "adi,pgio4-func", + ltc4283_pgio4_funcs, + ARRAY_SIZE(ltc4283_pgio4_funcs)); + + if (func < 0 && func != -EINVAL) + return dev_err_probe(dev, func, + "Invalid adi,pgio4-func property\n"); + if (func >= 0) { + if (func == LTC4283_PGIO_FUNC_GPIO) { + __set_bit(LTC4283_PIN_PGIO4, &st->gpio_mask); + func++; + st->ext_fault = false; + } else { + st->ext_fault = true; + } + + ret = regmap_update_bits(st->map, LTC4283_PGIO_CONFIG, + LTC4283_PGIO4_CFG_MASK, + FIELD_PREP(LTC4283_PGIO4_CFG_MASK, func)); + if (ret) + return ret; + } + + return 0; +} + +static int ltc4283_adio_config(struct ltc4283_hwmon *st, struct device *dev, + const char *prop, u32 pin) +{ + u32 adc_idx; + int ret; + + if (!device_property_read_bool(dev, prop)) + return 0; + + adc_idx = LTC4283_CHAN_ADIO_1 - LTC4283_CHAN_ADI_1 + pin; + ret = regmap_clear_bits(st->map, LTC4283_ADC_SELECT(adc_idx), + LTC4283_ADC_SELECT_MASK(adc_idx)); + if (ret) + return ret; + + __set_bit(pin, &st->gpio_mask); + return 0; +} + +static int ltc4283_pin_config(struct ltc4283_hwmon *st, struct device *dev) +{ + int ret; + + ret = ltc4283_pgio_config(st, dev); + if (ret) + return ret; + + ret = ltc4283_adio_config(st, dev, "adi,gpio-on-adio1", LTC4283_PIN_ADIO1); + if (ret) + return ret; + + ret = ltc4283_adio_config(st, dev, "adi,gpio-on-adio2", LTC4283_PIN_ADIO2); + if (ret) + return ret; + + ret = ltc4283_adio_config(st, dev, "adi,gpio-on-adio3", LTC4283_PIN_ADIO3); + if (ret) + return ret; + + return ltc4283_adio_config(st, dev, "adi,gpio-on-adio4", LTC4283_PIN_ADIO4); +} + +static const char * const ltc4283_oc_fet_retry[] = { + "latch-off", "1", "7", "unlimited" +}; + +static const u32 ltc4283_fb_factor[] = { + 100, 50, 20, 10 +}; + +static const u32 ltc4283_cooling_dl[] = { + 512, 1002, 2005, 4100, 8190, 16400, 32800, 65600 +}; + +static const u32 ltc4283_fet_bad_delay[] = { + 256, 512, 1002, 2005 +}; + +static int ltc4283_setup(struct ltc4283_hwmon *st, struct device *dev) +{ + u32 val; + int ret; + + /* The part has an eeprom so let's get the needed defaults from it */ + ret = ltc4283_get_defaults(st); + if (ret) + return ret; + + /* + * Default to LTC4283_MIN_RSENSE so we can probe without FW properties. + */ + st->rsense = LTC4283_MIN_RSENSE; + ret = device_property_read_u32(dev, "adi,rsense-nano-ohms", + &st->rsense); + if (!ret) { + if (st->rsense < LTC4283_MIN_RSENSE || st->rsense > LTC4283_MAX_RSENSE) + return dev_err_probe(dev, -EINVAL, + "adi,rsense-nano-ohms(%u) too small or too large [%u %u]\n", + st->rsense, LTC4283_MIN_RSENSE, LTC4283_MAX_RSENSE); + } + + /* + * The resolution for rsense is tenths of micro (eg: 62.5 uOhm) which + * means we need nano in the bindings. However, to make things easier to + * handle (with respect to overflows) we divide it by 100 as we don't + * really need the last two digits. + */ + st->rsense /= CENTI; + + ret = device_property_read_u32(dev, "adi,current-limit-sense-microvolt", + &st->vsense_max); + if (!ret) { + u32 reg_val; + + if (!in_range(st->vsense_max, LTC4283_VILIM_MIN_uV, + LTC4283_VILIM_RANGE)) { + return dev_err_probe(dev, -EINVAL, + "adi,current-limit-sense-microvolt (%u) out of range [%u %u]\n", + st->vsense_max, LTC4283_VILIM_MIN_uV, + LTC4283_VILIM_MAX_uV); + } + + st->vsense_max /= MILLI; + reg_val = FIELD_PREP(LTC4283_ILIM_MASK, + st->vsense_max - LTC4283_VILIM_MIN_uV / MILLI); + ret = regmap_update_bits(st->map, LTC4283_CONFIG_1, + LTC4283_ILIM_MASK, reg_val); + if (ret) + return ret; + } + + ret = ltc4283_parse_array_prop(st, dev, "adi,current-limit-foldback-factor", + ltc4283_fb_factor, ARRAY_SIZE(ltc4283_fb_factor)); + if (ret < 0) + return ret; + if (ret < ARRAY_SIZE(ltc4283_fb_factor)) { + ret = regmap_update_bits(st->map, LTC4283_CONFIG_1, LTC4283_FB_MASK, + FIELD_PREP(LTC4283_FB_MASK, ret)); + if (ret) + return ret; + } + + ret = ltc4283_parse_array_prop(st, dev, "adi,cooling-delay-ms", + ltc4283_cooling_dl, ARRAY_SIZE(ltc4283_cooling_dl)); + if (ret < 0) + return ret; + if (ret < ARRAY_SIZE(ltc4283_cooling_dl)) { + ret = regmap_update_bits(st->map, LTC4283_CONFIG_2, LTC4283_COOLING_DL_MASK, + FIELD_PREP(LTC4283_COOLING_DL_MASK, ret)); + if (ret) + return ret; + } + + ret = ltc4283_parse_array_prop(st, dev, "adi,fet-bad-timer-delay-ms", + ltc4283_fet_bad_delay, ARRAY_SIZE(ltc4283_fet_bad_delay)); + if (ret < 0) + return ret; + if (ret < ARRAY_SIZE(ltc4283_fet_bad_delay)) { + ret = regmap_update_bits(st->map, LTC4283_CONFIG_2, LTC4283_FTBD_DL_MASK, + FIELD_PREP(LTC4283_FTBD_DL_MASK, ret)); + if (ret) + return ret; + } + + ret = ltc4283_set_max_limits(st, dev); + if (ret) + return ret; + + ret = ltc4283_pin_config(st, dev); + if (ret) + return ret; + + if (device_property_read_bool(dev, "adi,power-good-reset-on-fet")) { + ret = regmap_clear_bits(st->map, LTC4283_CONTROL_1, + LTC4283_PWRGD_RST_CTRL_MASK); + if (ret) + return ret; + } + + if (device_property_read_bool(dev, "adi,fet-turn-off-disable")) { + ret = regmap_clear_bits(st->map, LTC4283_CONTROL_1, + LTC4283_FET_BAD_OFF_MASK); + if (ret) + return ret; + } + + if (device_property_read_bool(dev, "adi,tmr-pull-down-disable")) { + ret = regmap_set_bits(st->map, LTC4283_CONTROL_1, + LTC4283_THERM_TMR_MASK); + if (ret) + return ret; + } + + if (device_property_read_bool(dev, "adi,dvdt-inrush-control-disable")) { + ret = regmap_clear_bits(st->map, LTC4283_CONTROL_1, + LTC4283_DVDT_MASK); + if (ret) + return ret; + } + + if (device_property_read_bool(dev, "adi,undervoltage-retry-disable")) { + ret = regmap_clear_bits(st->map, LTC4283_CONTROL_2, + LTC4283_UV_RETRY_MASK); + if (ret) + return ret; + } + + if (device_property_read_bool(dev, "adi,overvoltage-retry-disable")) { + ret = regmap_clear_bits(st->map, LTC4283_CONTROL_2, + LTC4283_OV_RETRY_MASK); + if (ret) + return ret; + } + + if (device_property_read_bool(dev, "adi,external-fault-retry-enable")) { + if (!st->ext_fault) + return dev_err_probe(dev, -EINVAL, + "adi,external-fault-retry-enable set but PGIO4 not configured\n"); + ret = regmap_set_bits(st->map, LTC4283_CONTROL_2, + LTC4283_EXT_FAULT_RETRY_MASK); + if (ret) + return ret; + } + + if (device_property_read_bool(dev, "adi,fault-log-enable")) { + ret = regmap_set_bits(st->map, LTC4283_FAULT_LOG_CTRL, + LTC4283_FAULT_LOG_EN_MASK); + if (ret) + return ret; + } + + ret = device_property_match_property_string(dev, "adi,overcurrent-retries", + ltc4283_oc_fet_retry, + ARRAY_SIZE(ltc4283_oc_fet_retry)); + /* We still want to catch when an invalid string is given. */ + if (ret < 0 && ret != -EINVAL) + return dev_err_probe(dev, ret, + "adi,overcurrent-retries invalid value\n"); + if (ret >= 0) { + ret = regmap_update_bits(st->map, LTC4283_CONTROL_2, + LTC4283_OC_RETRY_MASK, + FIELD_PREP(LTC4283_OC_RETRY_MASK, ret)); + if (ret) + return ret; + } + + ret = device_property_match_property_string(dev, "adi,fet-bad-retries", + ltc4283_oc_fet_retry, + ARRAY_SIZE(ltc4283_oc_fet_retry)); + if (ret < 0 && ret != -EINVAL) + return dev_err_probe(dev, ret, + "adi,fet-bad-retries invalid value\n"); + if (ret >= 0) { + ret = regmap_update_bits(st->map, LTC4283_CONTROL_2, + LTC4283_FET_BAD_RETRY_MASK, + FIELD_PREP(LTC4283_FET_BAD_RETRY_MASK, ret)); + if (ret) + return ret; + } + + if (device_property_read_bool(dev, "adi,external-fault-fet-off-enable")) { + if (!st->ext_fault) + return dev_err_probe(dev, -EINVAL, + "adi,external-fault-fet-off-enable set but PGIO4 not configured\n"); + ret = regmap_set_bits(st->map, LTC4283_CONFIG_3, + LTC4283_EXTFLT_TURN_OFF_MASK); + if (ret) + return ret; + } + + if (device_property_read_bool(dev, "adi,vpower-drns-enable")) { + u32 chan = LTC4283_CHAN_DRNS - LTC4283_CHAN_ADI_1; + + __clear_bit(LTC4283_CHAN_DRNS, &st->ch_enable_mask); + /* + * Then, let's by default disable DRNS from ADC2 given that it + * is already being monitored by the VPWR channel. One can still + * enable it later on if needed. + */ + ret = regmap_clear_bits(st->map, LTC4283_ADC_SELECT(chan), + LTC4283_ADC_SELECT_MASK(chan)); + if (ret) + return ret; + + val = 1; + } else { + val = 0; + } + + ret = regmap_update_bits(st->map, LTC4283_CONFIG_3, + LTC4283_VPWR_DRNS_MASK, + FIELD_PREP(LTC4283_VPWR_DRNS_MASK, val)); + if (ret) + return ret; + + /* Make sure the ADC has 12bit resolution since we're assuming that. */ + ret = regmap_update_bits(st->map, LTC4283_PGIO_CONFIG_2, + LTC4283_ADC_MASK, + FIELD_PREP(LTC4283_ADC_MASK, 3)); + if (ret) + return ret; + + /* Energy reads (which are 6 byte block reads) rely on page access */ + ret = regmap_set_bits(st->map, LTC4283_CONTROL_1, LTC4283_RW_PAGE_MASK); + if (ret) + return ret; + + /* + * Make sure we are integrating power as we only support reporting + * consumed energy. + */ + return regmap_clear_bits(st->map, LTC4283_METER_CONTROL, + LTC4283_INTEGRATE_I_MASK); +} + +static const struct hwmon_channel_info * const ltc4283_info[] = { + HWMON_CHANNEL_INFO(in, + HWMON_I_LCRIT_ALARM | HWMON_I_CRIT_ALARM | + HWMON_I_RESET_HISTORY | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_LOWEST | HWMON_I_HIGHEST | + HWMON_I_MAX | HWMON_I_MIN | HWMON_I_MIN_ALARM | + HWMON_I_MAX_ALARM | HWMON_I_RESET_HISTORY | + HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_LOWEST | HWMON_I_HIGHEST | + HWMON_I_MAX | HWMON_I_MIN | HWMON_I_MIN_ALARM | + HWMON_I_RESET_HISTORY | HWMON_I_MAX_ALARM | + HWMON_I_ENABLE | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_LOWEST | HWMON_I_HIGHEST | + HWMON_I_MAX | HWMON_I_MIN | HWMON_I_MIN_ALARM | + HWMON_I_RESET_HISTORY | HWMON_I_MAX_ALARM | + HWMON_I_ENABLE | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_LOWEST | HWMON_I_HIGHEST | + HWMON_I_MAX | HWMON_I_MIN | HWMON_I_MIN_ALARM | + HWMON_I_RESET_HISTORY | HWMON_I_MAX_ALARM | + HWMON_I_ENABLE | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_LOWEST | HWMON_I_HIGHEST | + HWMON_I_MAX | HWMON_I_MIN | HWMON_I_MIN_ALARM | + HWMON_I_RESET_HISTORY | HWMON_I_MAX_ALARM | + HWMON_I_ENABLE | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_LOWEST | HWMON_I_HIGHEST | + HWMON_I_MAX | HWMON_I_MIN | HWMON_I_MIN_ALARM | + HWMON_I_RESET_HISTORY | HWMON_I_MAX_ALARM | + HWMON_I_ENABLE | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_LOWEST | HWMON_I_HIGHEST | + HWMON_I_MAX | HWMON_I_MIN | HWMON_I_MIN_ALARM | + HWMON_I_RESET_HISTORY | HWMON_I_MAX_ALARM | + HWMON_I_ENABLE | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_LOWEST | HWMON_I_HIGHEST | + HWMON_I_MAX | HWMON_I_MIN | HWMON_I_MIN_ALARM | + HWMON_I_RESET_HISTORY | HWMON_I_MAX_ALARM | + HWMON_I_ENABLE | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_LOWEST | HWMON_I_HIGHEST | + HWMON_I_MAX | HWMON_I_MIN | HWMON_I_MIN_ALARM | + HWMON_I_RESET_HISTORY | HWMON_I_MAX_ALARM | + HWMON_I_ENABLE | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_LOWEST | HWMON_I_HIGHEST | + HWMON_I_MAX | HWMON_I_MIN | HWMON_I_MIN_ALARM | + HWMON_I_RESET_HISTORY | HWMON_I_MAX_ALARM | + HWMON_I_ENABLE | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_LOWEST | HWMON_I_HIGHEST | + HWMON_I_MAX | HWMON_I_MIN | HWMON_I_MIN_ALARM | + HWMON_I_RESET_HISTORY | HWMON_I_MAX_ALARM | + HWMON_I_FAULT | HWMON_I_ENABLE | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_LOWEST | HWMON_I_HIGHEST | + HWMON_I_MAX | HWMON_I_MIN | HWMON_I_MIN_ALARM | + HWMON_I_RESET_HISTORY | HWMON_I_MAX_ALARM | + HWMON_I_ENABLE | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_LOWEST | HWMON_I_HIGHEST | + HWMON_I_MAX | HWMON_I_MIN | HWMON_I_MIN_ALARM | + HWMON_I_RESET_HISTORY | HWMON_I_MAX_ALARM | + HWMON_I_ENABLE | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_LOWEST | HWMON_I_HIGHEST | + HWMON_I_MAX | HWMON_I_MIN | HWMON_I_MIN_ALARM | + HWMON_I_RESET_HISTORY | HWMON_I_MAX_ALARM | + HWMON_I_ENABLE | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_LOWEST | HWMON_I_HIGHEST | + HWMON_I_MAX | HWMON_I_MIN | HWMON_I_MIN_ALARM | + HWMON_I_RESET_HISTORY | HWMON_I_MAX_ALARM | + HWMON_I_ENABLE | HWMON_I_LABEL), + HWMON_CHANNEL_INFO(curr, + HWMON_C_INPUT | HWMON_C_LOWEST | HWMON_C_HIGHEST | + HWMON_C_MAX | HWMON_C_MIN | HWMON_C_MIN_ALARM | + HWMON_C_MAX_ALARM | HWMON_C_CRIT_ALARM | + HWMON_C_RESET_HISTORY | HWMON_C_LABEL), + HWMON_CHANNEL_INFO(power, + HWMON_P_INPUT | HWMON_P_INPUT_LOWEST | + HWMON_P_INPUT_HIGHEST | HWMON_P_MAX | HWMON_P_MIN | + HWMON_P_MAX_ALARM | HWMON_P_MIN_ALARM | + HWMON_P_RESET_HISTORY | HWMON_P_LABEL), + HWMON_CHANNEL_INFO(energy, + HWMON_E_ENABLE), + HWMON_CHANNEL_INFO(energy64, + HWMON_E_INPUT), + NULL +}; + +static const struct hwmon_ops ltc4283_ops = { + .read = ltc4283_read, + .write = ltc4283_write, + .is_visible = ltc4283_is_visible, + .read_string = ltc4283_read_labels, +}; + +static const struct hwmon_chip_info ltc4283_chip_info = { + .ops = <c4283_ops, + .info = ltc4283_info, +}; + +static int ltc4283_show_fault_log(void *arg, u64 *val, u32 mask) +{ + struct ltc4283_hwmon *st = arg; + long alarm; + int ret; + + ret = ltc4283_read_alarm(st, LTC4283_FAULT_LOG, mask, &alarm); + if (ret) + return ret; + + *val = alarm; + + return 0; +} + +static int ltc4283_show_in0_lcrit_fault_log(void *arg, u64 *val) +{ + return ltc4283_show_fault_log(arg, val, LTC4283_UV_FAULT_MASK); +} +DEFINE_DEBUGFS_ATTRIBUTE(ltc4283_in0_lcrit_fault_log, + ltc4283_show_in0_lcrit_fault_log, NULL, "%llu\n"); + +static int ltc4283_show_in0_crit_fault_log(void *arg, u64 *val) +{ + return ltc4283_show_fault_log(arg, val, LTC4283_OV_FAULT_MASK); +} +DEFINE_DEBUGFS_ATTRIBUTE(ltc4283_in0_crit_fault_log, + ltc4283_show_in0_crit_fault_log, NULL, "%llu\n"); + +static int ltc4283_show_fet_bad_fault_log(void *arg, u64 *val) +{ + return ltc4283_show_fault_log(arg, val, LTC4283_FET_BAD_FAULT_MASK); +} +DEFINE_DEBUGFS_ATTRIBUTE(ltc4283_fet_bad_fault_log, + ltc4283_show_fet_bad_fault_log, NULL, "%llu\n"); + +static int ltc4283_show_fet_short_fault_log(void *arg, u64 *val) +{ + return ltc4283_show_fault_log(arg, val, LTC4283_FET_SHORT_FAULT_MASK); +} +DEFINE_DEBUGFS_ATTRIBUTE(ltc4283_fet_short_fault_log, + ltc4283_show_fet_short_fault_log, NULL, "%llu\n"); + +static int ltc4283_show_curr1_crit_fault_log(void *arg, u64 *val) +{ + return ltc4283_show_fault_log(arg, val, LTC4283_OC_FAULT_MASK); +} +DEFINE_DEBUGFS_ATTRIBUTE(ltc4283_curr1_crit_fault_log, + ltc4283_show_curr1_crit_fault_log, NULL, "%llu\n"); + +static int ltc4283_show_power1_failed_fault_log(void *arg, u64 *val) +{ + return ltc4283_show_fault_log(arg, val, LTC4283_PWR_FAIL_FAULT_MASK); +} +DEFINE_DEBUGFS_ATTRIBUTE(ltc4283_power1_failed_fault_log, + ltc4283_show_power1_failed_fault_log, NULL, "%llu\n"); + +static int ltc4283_show_power1_good_input_fault_log(void *arg, u64 *val) +{ + return ltc4283_show_fault_log(arg, val, LTC4283_PGI_FAULT_MASK); +} +DEFINE_DEBUGFS_ATTRIBUTE(ltc4283_power1_good_input_fault_log, + ltc4283_show_power1_good_input_fault_log, NULL, "%llu\n"); + +static void ltc4283_debugfs_init(struct ltc4283_hwmon *st, struct i2c_client *i2c) +{ + debugfs_create_file_unsafe("in0_crit_fault_log", 0400, i2c->debugfs, st, + <c4283_in0_crit_fault_log); + debugfs_create_file_unsafe("in0_lcrit_fault_log", 0400, i2c->debugfs, st, + <c4283_in0_lcrit_fault_log); + debugfs_create_file_unsafe("in11_fet_bad_fault_log", 0400, i2c->debugfs, st, + <c4283_fet_bad_fault_log); + debugfs_create_file_unsafe("in11_fet_short_fault_log", 0400, i2c->debugfs, st, + <c4283_fet_short_fault_log); + debugfs_create_file_unsafe("curr1_crit_fault_log", 0400, i2c->debugfs, st, + <c4283_curr1_crit_fault_log); + debugfs_create_file_unsafe("power1_failed_fault_log", 0400, i2c->debugfs, st, + <c4283_power1_failed_fault_log); + debugfs_create_file_unsafe("power1_good_input_fault_log", 0400, i2c->debugfs, + st, <c4283_power1_good_input_fault_log); +} + +static bool ltc4283_is_word_reg(unsigned int reg) +{ + return reg >= LTC4283_SENSE && reg <= LTC4283_ADIO34_MAX; +} + +static int ltc4283_reg_read(void *context, unsigned int reg, unsigned int *val) +{ + struct i2c_client *client = context; + int ret; + + if (ltc4283_is_word_reg(reg)) + ret = i2c_smbus_read_word_swapped(client, reg); + else + ret = i2c_smbus_read_byte_data(client, reg); + + if (ret < 0) + return ret; + + *val = ret; + return 0; +} + +static int ltc4283_reg_write(void *context, unsigned int reg, unsigned int val) +{ + struct i2c_client *client = context; + + if (ltc4283_is_word_reg(reg)) + return i2c_smbus_write_word_swapped(client, reg, val); + + return i2c_smbus_write_byte_data(client, reg, val); +} + +static const struct regmap_bus ltc4283_regmap_bus = { + .reg_read = ltc4283_reg_read, + .reg_write = ltc4283_reg_write, +}; + +static bool ltc4283_writable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case LTC4283_SYSTEM_STATUS ... LTC4283_FAULT_STATUS: + return false; + case LTC4283_RESERVED_OC: + return false; + case LTC4283_RESERVED_86 ... LTC4283_RESERVED_8F: + return false; + case LTC4283_RESERVED_91 ... LTC4283_RESERVED_A1: + return false; + case LTC4283_RESERVED_A3: + return false; + case LTC4283_RESERVED_AC: + return false; + case LTC4283_POWER_PLAY_MSB ... LTC4283_POWER_PLAY_LSB: + return false; + case LTC4283_RESERVED_F1 ... LTC4283_RESERVED_FF: + return false; + default: + return true; + } +} + +static const struct regmap_config ltc4283_regmap_config = { + .reg_bits = 8, + .val_bits = 16, + .max_register = 0xFF, + .writeable_reg = ltc4283_writable_reg, +}; + +static int ltc4283_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev, *hwmon; + struct auxiliary_device *adev; + struct ltc4283_hwmon *st; + int ret, id; + + st = devm_kzalloc(dev, sizeof(*st), GFP_KERNEL); + if (!st) + return -ENOMEM; + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_BYTE_DATA | + I2C_FUNC_SMBUS_WORD_DATA | + I2C_FUNC_SMBUS_READ_I2C_BLOCK)) + return -EOPNOTSUPP; + + st->client = client; + st->map = devm_regmap_init(dev, <c4283_regmap_bus, client, + <c4283_regmap_config); + if (IS_ERR(st->map)) + return dev_err_probe(dev, PTR_ERR(st->map), + "Failed to create regmap\n"); + + ret = ltc4283_setup(st, dev); + if (ret) + return ret; + + hwmon = devm_hwmon_device_register_with_info(dev, "ltc4283", st, + <c4283_chip_info, NULL); + + if (IS_ERR(hwmon)) + return PTR_ERR(hwmon); + + ltc4283_debugfs_init(st, client); + + if (!st->gpio_mask) + return 0; + + id = (client->adapter->nr << 10) | client->addr; + adev = __devm_auxiliary_device_create(dev, KBUILD_MODNAME, "gpio", + &st->gpio_mask, id); + if (!adev) + return dev_err_probe(dev, -ENODEV, "Failed to add GPIO device\n"); + + return 0; +} + +static const struct of_device_id ltc4283_of_match[] = { + { .compatible = "adi,ltc4283" }, + { } +}; + +static const struct i2c_device_id ltc4283_i2c_id[] = { + { "ltc4283" }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ltc4283_i2c_id); + +static struct i2c_driver ltc4283_driver = { + .driver = { + .name = "ltc4283", + .of_match_table = ltc4283_of_match, + }, + .probe = ltc4283_probe, + .id_table = ltc4283_i2c_id, +}; +module_i2c_driver(ltc4283_driver); + +MODULE_AUTHOR("Nuno Sá "); +MODULE_DESCRIPTION("LTC4283 Hot Swap Controller driver"); +MODULE_LICENSE("GPL"); From 55c01d37949d215281106a3e532dd56fe678493d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nuno=20S=C3=A1?= Date: Sat, 2 May 2026 10:56:54 +0100 Subject: [PATCH 67/81] gpio: gpio-ltc4283: Add support for the LTC4283 Swap Controller MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The LTC4283 device has up to 8 pins that can be configured as GPIOs. Note that PGIO pins are not set as GPIOs by default so if they are configured to be used as GPIOs we need to make sure to initialize them to a sane default. They are set as inputs by default. Acked-by: Bartosz Golaszewski Reviewed-by: Linus Walleij Signed-off-by: Nuno Sá Link: https://lore.kernel.org/r/20260502-ltc4283-support-v13-3-1c206542e652@analog.com Signed-off-by: Guenter Roeck --- MAINTAINERS | 2 + drivers/gpio/Kconfig | 15 +++ drivers/gpio/Makefile | 1 + drivers/gpio/gpio-ltc4283.c | 218 ++++++++++++++++++++++++++++++++++++ 4 files changed, 236 insertions(+) create mode 100644 drivers/gpio/gpio-ltc4283.c diff --git a/MAINTAINERS b/MAINTAINERS index b55a902181cd..3b9d66ce05d0 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -15254,9 +15254,11 @@ F: drivers/hwmon/ltc4282.c LTC4283 HARDWARE MONITOR AND GPIO DRIVER M: Nuno Sá +L: linux-gpio@vger.kernel.org L: linux-hwmon@vger.kernel.org S: Supported F: Documentation/devicetree/bindings/hwmon/adi,ltc4283.yaml +F: drivers/gpio/gpio-ltc4283.c F: drivers/hwmon/ltc4283.c LTC4286 HARDWARE MONITOR DRIVER diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 020e51e30317..9b6e0a34eff6 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -1783,6 +1783,21 @@ config GPIO_WM8994 endmenu +menu "Auxiliary Bus GPIO drivers" + depends on AUXILIARY_BUS + +config GPIO_LTC4283 + tristate "Analog Devices LTC4283 GPIO support" + depends on SENSORS_LTC4283 + help + If you say yes here you want the GPIO function available in Analog + Devices LTC4283 Negative Voltage Hot Swap Controller. + + This driver can also be built as a module. If so, the module will + be called gpio-ltc4283. + +endmenu + menu "PCI GPIO expanders" depends on PCI diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index b267598b517d..6e2367cab94f 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -100,6 +100,7 @@ obj-$(CONFIG_GPIO_LP873X) += gpio-lp873x.o obj-$(CONFIG_GPIO_LP87565) += gpio-lp87565.o obj-$(CONFIG_GPIO_LPC18XX) += gpio-lpc18xx.o obj-$(CONFIG_GPIO_LPC32XX) += gpio-lpc32xx.o +obj-$(CONFIG_GPIO_LTC4283) += gpio-ltc4283.o obj-$(CONFIG_GPIO_MACSMC) += gpio-macsmc.o obj-$(CONFIG_GPIO_MADERA) += gpio-madera.o obj-$(CONFIG_GPIO_MAX3191X) += gpio-max3191x.o diff --git a/drivers/gpio/gpio-ltc4283.c b/drivers/gpio/gpio-ltc4283.c new file mode 100644 index 000000000000..6609443c5d62 --- /dev/null +++ b/drivers/gpio/gpio-ltc4283.c @@ -0,0 +1,218 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Analog Devices LTC4283 GPIO driver + * + * Copyright 2025 Analog Devices Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define LTC4283_PINS_MAX 8 +#define LTC4283_PGIOX_START_NR 4 +#define LTC4283_INPUT_STATUS 0x02 +#define LTC4283_PGIO_CONFIG 0x10 +#define LTC4283_PGIO_CFG_MASK(pin) \ + GENMASK(((pin) - LTC4283_PGIOX_START_NR) * 2 + 1, (((pin) - LTC4283_PGIOX_START_NR) * 2)) +#define LTC4283_PGIO_CONFIG_2 0x11 + +#define LTC4283_ADIO_CONFIG 0x12 +/* starts at bit 4 */ +#define LTC4283_ADIOX_CONFIG_MASK(pin) BIT((pin) + 4) +#define LTC4283_PGIO_DIR_IN 3 +#define LTC4283_PGIO_DIR_OUT 2 + +struct ltc4283_gpio { + struct gpio_chip gpio_chip; + struct regmap *regmap; +}; + +static int ltc4283_pgio_get_direction(const struct ltc4283_gpio *st, unsigned int off) +{ + unsigned int val; + int ret; + + ret = regmap_read(st->regmap, LTC4283_PGIO_CONFIG, &val); + if (ret) + return ret; + + val = field_get(LTC4283_PGIO_CFG_MASK(off), val); + if (val == LTC4283_PGIO_DIR_IN) + return GPIO_LINE_DIRECTION_IN; + + return GPIO_LINE_DIRECTION_OUT; +} + +static int ltc4283_gpio_get_direction(struct gpio_chip *gc, unsigned int off) +{ + struct ltc4283_gpio *st = gpiochip_get_data(gc); + unsigned int val; + int ret; + + if (off >= LTC4283_PGIOX_START_NR) + return ltc4283_pgio_get_direction(st, off); + + ret = regmap_read(st->regmap, LTC4283_ADIO_CONFIG, &val); + if (ret) + return ret; + + if (val & LTC4283_ADIOX_CONFIG_MASK(off)) + return GPIO_LINE_DIRECTION_IN; + + return GPIO_LINE_DIRECTION_OUT; +} + +static int ltc4283_gpio_direction_set(const struct ltc4283_gpio *st, + unsigned int off, bool input) +{ + if (off >= LTC4283_PGIOX_START_NR) { + unsigned int val = LTC4283_PGIO_DIR_OUT; + + if (input) + val = LTC4283_PGIO_DIR_IN; + + val = field_prep(LTC4283_PGIO_CFG_MASK(off), val); + return regmap_update_bits(st->regmap, LTC4283_PGIO_CONFIG, + LTC4283_PGIO_CFG_MASK(off), val); + } + + return regmap_update_bits(st->regmap, LTC4283_ADIO_CONFIG, + LTC4283_ADIOX_CONFIG_MASK(off), + field_prep(LTC4283_ADIOX_CONFIG_MASK(off), input)); +} + +static int __ltc4283_gpio_set_value(const struct ltc4283_gpio *st, + unsigned int off, int val) +{ + u32 reg = off < LTC4283_PGIOX_START_NR ? LTC4283_ADIO_CONFIG : LTC4283_PGIO_CONFIG_2; + + return regmap_update_bits(st->regmap, reg, BIT(off), + field_prep(BIT(off), !!val)); +} + +static int ltc4283_gpio_direction_input(struct gpio_chip *gc, unsigned int off) +{ + struct ltc4283_gpio *st = gpiochip_get_data(gc); + + return ltc4283_gpio_direction_set(st, off, true); +} + +static int ltc4283_gpio_direction_output(struct gpio_chip *gc, unsigned int off, int val) +{ + struct ltc4283_gpio *st = gpiochip_get_data(gc); + int ret; + + ret = ltc4283_gpio_direction_set(st, off, false); + if (ret) + return ret; + + return __ltc4283_gpio_set_value(st, off, val); +} + +static int ltc4283_gpio_get_value(struct gpio_chip *gc, unsigned int off) +{ + struct ltc4283_gpio *st = gpiochip_get_data(gc); + unsigned int val, reg; + int ret, dir; + + dir = ltc4283_gpio_get_direction(gc, off); + if (dir < 0) + return dir; + + if (dir == GPIO_LINE_DIRECTION_IN) { + ret = regmap_read(st->regmap, LTC4283_INPUT_STATUS, &val); + if (ret) + return ret; + + /* ADIO1 is at bit 3. */ + if (off < LTC4283_PGIOX_START_NR) + return !!(val & BIT(3 - off)); + + /* PGIO1 is at bit 7. */ + return !!(val & BIT(7 - (off - LTC4283_PGIOX_START_NR))); + } + + if (off < LTC4283_PGIOX_START_NR) + reg = LTC4283_ADIO_CONFIG; + else + reg = LTC4283_PGIO_CONFIG_2; + + ret = regmap_read(st->regmap, reg, &val); + if (ret) + return ret; + + return !!(val & BIT(off)); +} + +static int ltc4283_gpio_set_value(struct gpio_chip *gc, unsigned int off, int val) +{ + struct ltc4283_gpio *st = gpiochip_get_data(gc); + + return __ltc4283_gpio_set_value(st, off, val); +} + +static int ltc4283_init_valid_mask(struct gpio_chip *gc, unsigned long *valid_mask, + unsigned int ngpios) +{ + unsigned long *mask = dev_get_platdata(gc->parent); + + bitmap_copy(valid_mask, mask, ngpios); + return 0; +} + +static int ltc4283_gpio_probe(struct auxiliary_device *adev, + const struct auxiliary_device_id *id) +{ + struct device *dev = &adev->dev; + struct ltc4283_gpio *st; + struct gpio_chip *gc; + + st = devm_kzalloc(dev, sizeof(*st), GFP_KERNEL); + if (!st) + return -ENOMEM; + + st->regmap = dev_get_regmap(dev->parent, NULL); + if (!st->regmap) + return dev_err_probe(dev, -ENODEV, + "Failed to get regmap\n"); + + gc = &st->gpio_chip; + gc->parent = dev; + gc->get_direction = ltc4283_gpio_get_direction; + gc->direction_input = ltc4283_gpio_direction_input; + gc->direction_output = ltc4283_gpio_direction_output; + gc->get = ltc4283_gpio_get_value; + gc->set = ltc4283_gpio_set_value; + gc->init_valid_mask = ltc4283_init_valid_mask; + gc->can_sleep = true; + + gc->base = -1; + gc->ngpio = LTC4283_PINS_MAX; + gc->label = adev->name; + gc->owner = THIS_MODULE; + + return devm_gpiochip_add_data(dev, &st->gpio_chip, st); +} + +static const struct auxiliary_device_id ltc4283_aux_id_table[] = { + { "ltc4283.gpio" }, + { } +}; +MODULE_DEVICE_TABLE(auxiliary, ltc4283_aux_id_table); + +static struct auxiliary_driver ltc4283_gpio_driver = { + .probe = ltc4283_gpio_probe, + .id_table = ltc4283_aux_id_table, +}; +module_auxiliary_driver(ltc4283_gpio_driver); + +MODULE_AUTHOR("Nuno Sá "); +MODULE_DESCRIPTION("GPIO LTC4283 Driver"); +MODULE_LICENSE("GPL"); From 1f7a5cfe44bf96bf54ce879339c896598db8e12e Mon Sep 17 00:00:00 2001 From: Ferdinand Schwenk Date: Tue, 9 Jun 2026 21:43:10 +0200 Subject: [PATCH 68/81] hwmon: (ina238) Add support for samples and update_interval Expose INA238 ADC averaging count (AVG) and conversion timing (VBUSCT/VSHCT/VTCT) through chip-level hwmon attributes: chip/samples chip/update_interval Use per-chip conversion-time lookup tables so the same helpers work for INA228/INA237/INA238/INA700/INA780 and SQ52206. Cache ADC_CONFIG in driver data and update it on writes to avoid extra register reads during read-modify-write updates. Report update_interval in milliseconds as required by the hwmon ABI. Compute it from raw ADC cycle time multiplied by the active averaging count, and apply the inverse mapping on writes so programmed conversion time tracks the selected sample count. Clamp user-provided update_interval before unit scaling to prevent overflow in arithmetic conversions. Also combine chip attributes in HWMON_CHANNEL_INFO using a bitwise OR for a single logical chip channel. Signed-off-by: Ferdinand Schwenk Link: https://lore.kernel.org/r/20260609-hwmon-ina238-update-interval-us-v2-v3-1-016b55567950@advastore.com Signed-off-by: Guenter Roeck --- drivers/hwmon/ina238.c | 144 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 142 insertions(+), 2 deletions(-) diff --git a/drivers/hwmon/ina238.c b/drivers/hwmon/ina238.c index c20dd164a6a6..179012680080 100644 --- a/drivers/hwmon/ina238.c +++ b/drivers/hwmon/ina238.c @@ -15,6 +15,7 @@ #include #include #include +#include /* INA238 register definitions */ #define INA238_CONFIG 0x0 @@ -49,6 +50,17 @@ #define INA238_DIAG_ALERT_BUSUL BIT(3) #define INA238_DIAG_ALERT_POL BIT(2) +/* INA238_ADC_CONFIG register field masks and shifts */ +#define INA238_ADC_CONFIG_MODE_MASK GENMASK(15, 12) +#define INA238_ADC_CONFIG_VBUSCT_SHIFT 9 +#define INA238_ADC_CONFIG_VBUSCT_MASK GENMASK(11, 9) +#define INA238_ADC_CONFIG_VSHCT_SHIFT 6 +#define INA238_ADC_CONFIG_VSHCT_MASK GENMASK(8, 6) +#define INA238_ADC_CONFIG_VTCT_SHIFT 3 +#define INA238_ADC_CONFIG_VTCT_MASK GENMASK(5, 3) +#define INA238_ADC_CONFIG_AVG_SHIFT 0 +#define INA238_ADC_CONFIG_AVG_MASK GENMASK(2, 0) + #define INA238_REGISTERS 0x20 #define INA238_RSHUNT_DEFAULT 2500 /* uOhm */ @@ -101,6 +113,21 @@ static const struct regmap_config ina238_regmap_config = { .val_bits = 16, }; +/* Lookup table for conversion times in usec for INA238 family */ +static const u16 ina238_conv_time[] = { + 50, 84, 150, 280, 540, 1052, 2074, 4120, +}; + +/* Lookup table for conversion times in usec for SQ52206 */ +static const u16 sq52206_conv_time[] = { + 66, 118, 310, 566, 1070, 2090, 4140, 8230, +}; + +/* Lookup table for number of samples used in averaging mode */ +static const int ina238_avg_samples[] = { + 1, 4, 16, 64, 128, 256, 512, 1024, +}; + enum ina238_ids { ina228, ina237, ina238, ina700, ina780, sq52206 }; struct ina238_config { @@ -112,6 +139,7 @@ struct ina238_config { u32 power_calculate_factor; /* fixed parameter for power calculation, from datasheet */ u32 bus_voltage_lsb; /* bus voltage LSB, in nV */ int current_lsb; /* current LSB, in uA */ + const u16 *conv_time; /* conversion time lookup table */ }; struct ina238_data { @@ -124,6 +152,7 @@ struct ina238_data { int current_lsb; /* current LSB, in uA */ int power_lsb; /* power LSB, in uW */ int energy_lsb; /* energy LSB, in uJ */ + u16 adc_config; /* cached ADC_CONFIG register value */ }; static const struct ina238_config ina238_config[] = { @@ -135,6 +164,7 @@ static const struct ina238_config ina238_config[] = { .config_default = INA238_CONFIG_DEFAULT, .bus_voltage_lsb = INA238_BUS_VOLTAGE_LSB, .temp_resolution = 16, + .conv_time = ina238_conv_time, }, [ina237] = { .has_20bit_voltage_current = false, @@ -144,6 +174,7 @@ static const struct ina238_config ina238_config[] = { .config_default = INA238_CONFIG_DEFAULT, .bus_voltage_lsb = INA238_BUS_VOLTAGE_LSB, .temp_resolution = 12, + .conv_time = ina238_conv_time, }, [ina238] = { .has_20bit_voltage_current = false, @@ -153,6 +184,7 @@ static const struct ina238_config ina238_config[] = { .config_default = INA238_CONFIG_DEFAULT, .bus_voltage_lsb = INA238_BUS_VOLTAGE_LSB, .temp_resolution = 12, + .conv_time = ina238_conv_time, }, [ina700] = { .has_20bit_voltage_current = false, @@ -163,6 +195,7 @@ static const struct ina238_config ina238_config[] = { .bus_voltage_lsb = INA238_BUS_VOLTAGE_LSB, .temp_resolution = 12, .current_lsb = 480, + .conv_time = ina238_conv_time, }, [ina780] = { .has_20bit_voltage_current = false, @@ -173,6 +206,7 @@ static const struct ina238_config ina238_config[] = { .bus_voltage_lsb = INA238_BUS_VOLTAGE_LSB, .temp_resolution = 12, .current_lsb = 2400, + .conv_time = ina238_conv_time, }, [sq52206] = { .has_20bit_voltage_current = false, @@ -182,6 +216,7 @@ static const struct ina238_config ina238_config[] = { .config_default = SQ52206_CONFIG_DEFAULT, .bus_voltage_lsb = SQ52206_BUS_VOLTAGE_LSB, .temp_resolution = 16, + .conv_time = sq52206_conv_time, }, }; @@ -261,6 +296,97 @@ static int ina228_read_voltage(struct ina238_data *data, int channel, long *val) return 0; } +/* Converting ADC_CONFIG register value to update_interval in usec */ +static inline u32 ina238_reg_to_interval_us(struct ina238_data *data) +{ + const u16 *ct = data->config->conv_time; + u32 vbusct = ct[(data->adc_config & INA238_ADC_CONFIG_VBUSCT_MASK) >> + INA238_ADC_CONFIG_VBUSCT_SHIFT]; + u32 vshct = ct[(data->adc_config & INA238_ADC_CONFIG_VSHCT_MASK) >> + INA238_ADC_CONFIG_VSHCT_SHIFT]; + u32 vtct = ct[(data->adc_config & INA238_ADC_CONFIG_VTCT_MASK) >> + INA238_ADC_CONFIG_VTCT_SHIFT]; + + return vbusct + vshct + vtct; +} + +static inline u32 ina238_samples(struct ina238_data *data) +{ + return ina238_avg_samples[(data->adc_config & INA238_ADC_CONFIG_AVG_MASK) >> + INA238_ADC_CONFIG_AVG_SHIFT]; +} + +/* Converting update_interval in msec to a single conversion time in usec */ +static inline u32 ina238_interval_ms_to_conv_time(long interval, u32 samples) +{ + u64 interval_us; + + interval = clamp_val(interval, 0, INT_MAX / 1000); + interval_us = (u64)interval * 1000; + + /* + * update_interval reports the ADC cycle time including averaging. + * The target per-field conversion time is interval_us / (samples * 3). + */ + return DIV_ROUND_CLOSEST_ULL(interval_us, samples * 3); +} + +static int ina238_read_chip(struct device *dev, u32 attr, long *val) +{ + struct ina238_data *data = dev_get_drvdata(dev); + + switch (attr) { + case hwmon_chip_samples: + *val = ina238_samples(data); + return 0; + case hwmon_chip_update_interval: + /* Return in msec */ + *val = DIV_ROUND_CLOSEST(ina238_reg_to_interval_us(data) * + ina238_samples(data), 1000); + return 0; + default: + return -EOPNOTSUPP; + } +} + +static int ina238_write_chip(struct device *dev, u32 attr, long val) +{ + struct ina238_data *data = dev_get_drvdata(dev); + u16 adc_config; + int idx, ret; + + switch (attr) { + case hwmon_chip_samples: + idx = find_closest(val, ina238_avg_samples, + ARRAY_SIZE(ina238_avg_samples)); + adc_config = (data->adc_config & ~INA238_ADC_CONFIG_AVG_MASK) | + (idx << INA238_ADC_CONFIG_AVG_SHIFT); + ret = regmap_write(data->regmap, INA238_ADC_CONFIG, adc_config); + if (ret) + return ret; + data->adc_config = adc_config; + return 0; + case hwmon_chip_update_interval: + val = ina238_interval_ms_to_conv_time(val, ina238_samples(data)); + idx = find_closest(val, data->config->conv_time, + ARRAY_SIZE(ina238_conv_time)); + adc_config = (data->adc_config & + ~(INA238_ADC_CONFIG_VBUSCT_MASK | + INA238_ADC_CONFIG_VSHCT_MASK | + INA238_ADC_CONFIG_VTCT_MASK)) | + ((u16)idx << INA238_ADC_CONFIG_VBUSCT_SHIFT) | + ((u16)idx << INA238_ADC_CONFIG_VSHCT_SHIFT) | + ((u16)idx << INA238_ADC_CONFIG_VTCT_SHIFT); + ret = regmap_write(data->regmap, INA238_ADC_CONFIG, adc_config); + if (ret) + return ret; + data->adc_config = adc_config; + return 0; + default: + return -EOPNOTSUPP; + } +} + static int ina238_read_in(struct device *dev, u32 attr, int channel, long *val) { @@ -587,6 +713,8 @@ static int ina238_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel, long *val) { switch (type) { + case hwmon_chip: + return ina238_read_chip(dev, attr, val); case hwmon_in: return ina238_read_in(dev, attr, channel, val); case hwmon_curr: @@ -607,6 +735,8 @@ static int ina238_write(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel, long val) { switch (type) { + case hwmon_chip: + return ina238_write_chip(dev, attr, val); case hwmon_in: return ina238_write_in(dev, attr, channel, val); case hwmon_curr: @@ -629,6 +759,14 @@ static umode_t ina238_is_visible(const void *drvdata, bool has_energy = data->config->has_energy; switch (type) { + case hwmon_chip: + switch (attr) { + case hwmon_chip_samples: + case hwmon_chip_update_interval: + return 0644; + default: + return 0; + } case hwmon_in: switch (attr) { case hwmon_in_input: @@ -692,6 +830,8 @@ static umode_t ina238_is_visible(const void *drvdata, HWMON_I_MIN | HWMON_I_MIN_ALARM) static const struct hwmon_channel_info * const ina238_info[] = { + HWMON_CHANNEL_INFO(chip, + HWMON_C_SAMPLES | HWMON_C_UPDATE_INTERVAL), HWMON_CHANNEL_INFO(in, /* 0: shunt voltage */ INA238_HWMON_IN_CONFIG, @@ -798,8 +938,8 @@ static int ina238_probe(struct i2c_client *client) } /* Setup ADC_CONFIG register */ - ret = regmap_write(data->regmap, INA238_ADC_CONFIG, - INA238_ADC_CONFIG_DEFAULT); + data->adc_config = INA238_ADC_CONFIG_DEFAULT; + ret = regmap_write(data->regmap, INA238_ADC_CONFIG, data->adc_config); if (ret < 0) { dev_err(dev, "error configuring the device: %d\n", ret); return -ENODEV; From 82508141450d90a6d92a14e93e5a00ffb193f5e0 Mon Sep 17 00:00:00 2001 From: Ferdinand Schwenk Date: Tue, 9 Jun 2026 21:43:11 +0200 Subject: [PATCH 69/81] hwmon: Add update_interval_us chip attribute Some hardware monitoring chips support update intervals below one millisecond. The existing update_interval attribute uses millisecond granularity, which causes sub-millisecond steps to round to the same value and become inaccessible from userspace. Introduce update_interval_us, a companion chip-level attribute that expresses the same update interval in microseconds. Drivers implementing this attribute should also implement update_interval for compatibility with millisecond-based userspace interfaces. Signed-off-by: Ferdinand Schwenk Link: https://lore.kernel.org/r/20260609-hwmon-ina238-update-interval-us-v2-v3-2-016b55567950@advastore.com Signed-off-by: Guenter Roeck --- Documentation/ABI/testing/sysfs-class-hwmon | 14 ++++++++++++++ Documentation/hwmon/sysfs-interface.rst | 4 ++++ drivers/hwmon/hwmon.c | 1 + include/linux/hwmon.h | 2 ++ 4 files changed, 21 insertions(+) diff --git a/Documentation/ABI/testing/sysfs-class-hwmon b/Documentation/ABI/testing/sysfs-class-hwmon index cfd0d0bab483..b185bdfc7186 100644 --- a/Documentation/ABI/testing/sysfs-class-hwmon +++ b/Documentation/ABI/testing/sysfs-class-hwmon @@ -27,6 +27,20 @@ Description: Some devices have a variable update rate or interval. This attribute can be used to change it to the desired value. +What: /sys/class/hwmon/hwmonX/update_interval_us +Description: + The interval at which the chip will update readings, + expressed in microseconds. + Unit: microsecond + + RW + + Some devices have a variable update rate or interval and + require finer-than-millisecond control. + This attribute can be used to change it to the desired value. + Drivers implementing this attribute should also implement + update_interval for millisecond-based userspace interfaces. + What: /sys/class/hwmon/hwmonX/inY_min Description: Voltage min value. diff --git a/Documentation/hwmon/sysfs-interface.rst b/Documentation/hwmon/sysfs-interface.rst index f76e9f8cc1ad..94e1bbce172a 100644 --- a/Documentation/hwmon/sysfs-interface.rst +++ b/Documentation/hwmon/sysfs-interface.rst @@ -106,6 +106,10 @@ Global attributes `update_interval` The interval at which the chip will update readings. +`update_interval_us` + The interval at which the chip will update readings, + expressed in microseconds for finer resolution. + ******** Voltages diff --git a/drivers/hwmon/hwmon.c b/drivers/hwmon/hwmon.c index 6812d1fd7c28..29dc90a2c3fe 100644 --- a/drivers/hwmon/hwmon.c +++ b/drivers/hwmon/hwmon.c @@ -573,6 +573,7 @@ static const char * const hwmon_chip_attrs[] = { [hwmon_chip_curr_reset_history] = "curr_reset_history", [hwmon_chip_power_reset_history] = "power_reset_history", [hwmon_chip_update_interval] = "update_interval", + [hwmon_chip_update_interval_us] = "update_interval_us", [hwmon_chip_alarms] = "alarms", [hwmon_chip_samples] = "samples", [hwmon_chip_curr_samples] = "curr_samples", diff --git a/include/linux/hwmon.h b/include/linux/hwmon.h index f5330e7ae247..dd713e193d0c 100644 --- a/include/linux/hwmon.h +++ b/include/linux/hwmon.h @@ -40,6 +40,7 @@ enum hwmon_chip_attributes { hwmon_chip_power_reset_history, hwmon_chip_register_tz, hwmon_chip_update_interval, + hwmon_chip_update_interval_us, hwmon_chip_alarms, hwmon_chip_samples, hwmon_chip_curr_samples, @@ -56,6 +57,7 @@ enum hwmon_chip_attributes { #define HWMON_C_POWER_RESET_HISTORY BIT(hwmon_chip_power_reset_history) #define HWMON_C_REGISTER_TZ BIT(hwmon_chip_register_tz) #define HWMON_C_UPDATE_INTERVAL BIT(hwmon_chip_update_interval) +#define HWMON_C_UPDATE_INTERVAL_US BIT(hwmon_chip_update_interval_us) #define HWMON_C_ALARMS BIT(hwmon_chip_alarms) #define HWMON_C_SAMPLES BIT(hwmon_chip_samples) #define HWMON_C_CURR_SAMPLES BIT(hwmon_chip_curr_samples) From b47f4a72c62f2db5995ee4c7adfcd876faadf328 Mon Sep 17 00:00:00 2001 From: Ferdinand Schwenk Date: Tue, 9 Jun 2026 21:43:12 +0200 Subject: [PATCH 70/81] hwmon: (ina238) Add update_interval_us attribute The INA238 family supports eight conversion time steps from 50 us to 4120 us (SQ52206: 66 us to 8230 us). At the millisecond granularity of update_interval, the four shortest steps (50, 84, 150, 280 us) all round to the same value and cannot be individually selected. Add support for the generic update_interval_us attribute, which reports and programs the same ADC cycle time as update_interval but in microseconds, giving userspace full access to all conversion time steps. Both attributes reflect the total cycle time including the active averaging count: the reported value is the raw conversion time multiplied by the number of averaged samples, and writes apply the inverse mapping. Signed-off-by: Ferdinand Schwenk Link: https://lore.kernel.org/r/20260609-hwmon-ina238-update-interval-us-v2-v3-3-016b55567950@advastore.com [groeck: Fixed some multi-line alignment issues] Signed-off-by: Guenter Roeck --- Documentation/hwmon/ina238.rst | 4 ++ drivers/hwmon/ina238.c | 70 +++++++++++++++++++++------------- 2 files changed, 47 insertions(+), 27 deletions(-) diff --git a/Documentation/hwmon/ina238.rst b/Documentation/hwmon/ina238.rst index 43950d1ec551..a75b79e17d9d 100644 --- a/Documentation/hwmon/ina238.rst +++ b/Documentation/hwmon/ina238.rst @@ -106,4 +106,8 @@ energy1_input Energy measurement (uJ) temp1_input Die temperature measurement (mC) temp1_max Maximum die temperature threshold (mC) temp1_max_alarm Maximum die temperature alarm + +samples ADC averaging count (1, 4, 16, 64, 128, 256, 512, 1024) +update_interval Total ADC conversion cycle time including averaging (ms) +update_interval_us Total ADC conversion cycle time including averaging (us) ======================= ======================================================= diff --git a/drivers/hwmon/ina238.c b/drivers/hwmon/ina238.c index 179012680080..3632bbca607e 100644 --- a/drivers/hwmon/ina238.c +++ b/drivers/hwmon/ina238.c @@ -316,21 +316,38 @@ static inline u32 ina238_samples(struct ina238_data *data) INA238_ADC_CONFIG_AVG_SHIFT]; } -/* Converting update_interval in msec to a single conversion time in usec */ -static inline u32 ina238_interval_ms_to_conv_time(long interval, u32 samples) +/* Converting update_interval(_us) to a per-field conversion time in usec. + * interval_us is the total ADC cycle time including averaging in microseconds. + * All three conversion fields (VBUSCT, VSHCT, VTCT) are set equal, so the + * per-field time is interval_us / (samples * 3). + */ +static inline u32 ina238_interval_us_to_conv_time(u32 interval_us, u32 samples) { - u64 interval_us; - - interval = clamp_val(interval, 0, INT_MAX / 1000); - interval_us = (u64)interval * 1000; - - /* - * update_interval reports the ADC cycle time including averaging. - * The target per-field conversion time is interval_us / (samples * 3). - */ return DIV_ROUND_CLOSEST_ULL(interval_us, samples * 3); } +/* Write a per-field conversion time (in usec) to the ADC_CONFIG register */ +static int ina238_write_conv_time(struct ina238_data *data, u32 conv_time_us) +{ + u16 adc_config; + int idx, ret; + + idx = find_closest(conv_time_us, data->config->conv_time, + ARRAY_SIZE(ina238_conv_time)); + adc_config = (data->adc_config & + ~(INA238_ADC_CONFIG_VBUSCT_MASK | + INA238_ADC_CONFIG_VSHCT_MASK | + INA238_ADC_CONFIG_VTCT_MASK)) | + ((u16)idx << INA238_ADC_CONFIG_VBUSCT_SHIFT) | + ((u16)idx << INA238_ADC_CONFIG_VSHCT_SHIFT) | + ((u16)idx << INA238_ADC_CONFIG_VTCT_SHIFT); + ret = regmap_write(data->regmap, INA238_ADC_CONFIG, adc_config); + if (ret) + return ret; + data->adc_config = adc_config; + return 0; +} + static int ina238_read_chip(struct device *dev, u32 attr, long *val) { struct ina238_data *data = dev_get_drvdata(dev); @@ -344,6 +361,10 @@ static int ina238_read_chip(struct device *dev, u32 attr, long *val) *val = DIV_ROUND_CLOSEST(ina238_reg_to_interval_us(data) * ina238_samples(data), 1000); return 0; + case hwmon_chip_update_interval_us: + /* Return in usec */ + *val = ina238_reg_to_interval_us(data) * ina238_samples(data); + return 0; default: return -EOPNOTSUPP; } @@ -367,21 +388,14 @@ static int ina238_write_chip(struct device *dev, u32 attr, long val) data->adc_config = adc_config; return 0; case hwmon_chip_update_interval: - val = ina238_interval_ms_to_conv_time(val, ina238_samples(data)); - idx = find_closest(val, data->config->conv_time, - ARRAY_SIZE(ina238_conv_time)); - adc_config = (data->adc_config & - ~(INA238_ADC_CONFIG_VBUSCT_MASK | - INA238_ADC_CONFIG_VSHCT_MASK | - INA238_ADC_CONFIG_VTCT_MASK)) | - ((u16)idx << INA238_ADC_CONFIG_VBUSCT_SHIFT) | - ((u16)idx << INA238_ADC_CONFIG_VSHCT_SHIFT) | - ((u16)idx << INA238_ADC_CONFIG_VTCT_SHIFT); - ret = regmap_write(data->regmap, INA238_ADC_CONFIG, adc_config); - if (ret) - return ret; - data->adc_config = adc_config; - return 0; + /* Convert ms to us before passing to the shared helper */ + val = clamp_val(val, 0, INT_MAX / 1000) * 1000; + return ina238_write_conv_time(data, + ina238_interval_us_to_conv_time((u32)val, ina238_samples(data))); + case hwmon_chip_update_interval_us: + val = clamp_val(val, 0, INT_MAX); + return ina238_write_conv_time(data, + ina238_interval_us_to_conv_time((u32)val, ina238_samples(data))); default: return -EOPNOTSUPP; } @@ -763,6 +777,7 @@ static umode_t ina238_is_visible(const void *drvdata, switch (attr) { case hwmon_chip_samples: case hwmon_chip_update_interval: + case hwmon_chip_update_interval_us: return 0644; default: return 0; @@ -831,7 +846,8 @@ static umode_t ina238_is_visible(const void *drvdata, static const struct hwmon_channel_info * const ina238_info[] = { HWMON_CHANNEL_INFO(chip, - HWMON_C_SAMPLES | HWMON_C_UPDATE_INTERVAL), + HWMON_C_SAMPLES | HWMON_C_UPDATE_INTERVAL | + HWMON_C_UPDATE_INTERVAL_US), HWMON_CHANNEL_INFO(in, /* 0: shunt voltage */ INA238_HWMON_IN_CONFIG, From 479bfeba2eb62666cd4b981c8e721c61dcf36e7d Mon Sep 17 00:00:00 2001 From: Alexis Czezar Torreno Date: Wed, 10 Jun 2026 09:12:10 +0800 Subject: [PATCH 71/81] hwmon: (pmbus/max34440): add support adpm12250 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ADPM12250 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 2500W. Uses PMBus. Signed-off-by: Alexis Czezar Torreno Reviewed-by: Nuno Sá Link: https://lore.kernel.org/r/20260610-dev-adpm12250-v1-1-422760bb80da@analog.com Signed-off-by: Guenter Roeck --- Documentation/hwmon/max34440.rst | 27 ++++++++++++------- drivers/hwmon/pmbus/max34440.c | 45 +++++++++++++++++++++++++++++--- 2 files changed, 60 insertions(+), 12 deletions(-) diff --git a/Documentation/hwmon/max34440.rst b/Documentation/hwmon/max34440.rst index d6d4fbc863d9..e7421f4dbf38 100644 --- a/Documentation/hwmon/max34440.rst +++ b/Documentation/hwmon/max34440.rst @@ -19,6 +19,14 @@ Supported chips: Datasheet: - + * ADI ADPM12250 + + Prefixes: 'adpm12250' + + Addresses scanned: - + + Datasheet: - + * Maxim MAX34440 Prefixes: 'max34440' @@ -87,11 +95,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, 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. +MAX34461; PMBus DC/DC Power Module ADPM12160, ADPM12200, and ADPM12250. 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, ADPM12200, and ADPM12250 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. @@ -149,7 +157,7 @@ in[1-6]_reset_history Write any value to reset history. .. note:: - MAX34446 only supports in[1-4]. - - ADPM12160, and ADPM12200 only supports in[1-2]. Label is "vin1" + - ADPM12160, ADPM12200, and ADPM12250 only supports in[1-2]. Label is "vin1" and "vout1" respectively. Curr @@ -172,8 +180,9 @@ 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, and ADPM12200, curr[1] is "iin1" and curr[2-6] - are "iout[1-5]". + - For ADPM12160, ADPM12200, and ADPM12250, curr[1] is "iin1" + - For ADPM12160, and ADPM12200 curr[2-6] are "iout[1-5]". + - For ADPM12250, curr[2-4] are "iout[1-3]". Power ~~~~~ @@ -209,7 +218,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, and ADPM12200 only supports temp[1]. + - ADPM12160, ADPM12200, and ADPM12250 only supports temp[1]. .. note:: diff --git a/drivers/hwmon/pmbus/max34440.c b/drivers/hwmon/pmbus/max34440.c index 4525b9fc5626..74876d2207fb 100644 --- a/drivers/hwmon/pmbus/max34440.c +++ b/drivers/hwmon/pmbus/max34440.c @@ -18,6 +18,7 @@ enum chips { adpm12160, adpm12200, + adpm12250, max34440, max34441, max34446, @@ -97,7 +98,8 @@ 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 != adpm12200) + data->id != adpm12160 && data->id != adpm12200 && + data->id != adpm12250) return -ENXIO; ret = pmbus_read_word_data(client, page, phase, MAX34446_MFR_IOUT_AVG); @@ -182,7 +184,8 @@ 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 == adpm12200)) + data->id == adpm12160 || data->id == adpm12200 || + data->id == adpm12250)) ret = pmbus_write_word_data(client, page, MAX34446_MFR_IOUT_AVG, 0); @@ -399,6 +402,40 @@ static struct pmbus_driver_info max34440_info[] = { .read_word_data = max34440_read_word_data, .write_word_data = max34440_write_word_data, }, + [adpm12250] = { + .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[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, @@ -635,7 +672,8 @@ static int max34440_probe(struct i2c_client *client) rv = max34451_set_supported_funcs(client, data); if (rv) return rv; - } else if (data->id == adpm12160 || data->id == adpm12200) { + } else if (data->id == adpm12160 || data->id == adpm12200 || + data->id == adpm12250) { data->iout_oc_fault_limit = PMBUS_IOUT_OC_FAULT_LIMIT; data->iout_oc_warn_limit = PMBUS_IOUT_OC_WARN_LIMIT; } @@ -646,6 +684,7 @@ static int max34440_probe(struct i2c_client *client) static const struct i2c_device_id max34440_id[] = { { .name = "adpm12160", .driver_data = adpm12160 }, { .name = "adpm12200", .driver_data = adpm12200 }, + { .name = "adpm12250", .driver_data = adpm12250 }, { .name = "max34440", .driver_data = max34440 }, { .name = "max34441", .driver_data = max34441 }, { .name = "max34446", .driver_data = max34446 }, From 03b4addf8282fa2b63f2d7448d1a9bce9be3f556 Mon Sep 17 00:00:00 2001 From: Pei Xiao Date: Wed, 10 Jun 2026 09:49:09 +0800 Subject: [PATCH 72/81] hwmon: (gpd-fan): drop global driver data and use per-device allocation replace the global state gpd_driver_priv with per-device private data (struct gpd_fan_data) allocated in probe. This allows the driver to support multiple instances in the future and aligns with kernel best practices. No functional change intended. Signed-off-by: Pei Xiao Link: https://lore.kernel.org/r/1cd3e13033fdd3d0f9b59322f7c86e350d113b92.1781055639.git.xiaopei01@kylinos.cn Signed-off-by: Guenter Roeck --- drivers/hwmon/gpd-fan.c | 209 ++++++++++++++++++++++------------------ 1 file changed, 115 insertions(+), 94 deletions(-) diff --git a/drivers/hwmon/gpd-fan.c b/drivers/hwmon/gpd-fan.c index 80de5f20781e..7284babd4f5c 100644 --- a/drivers/hwmon/gpd-fan.c +++ b/drivers/hwmon/gpd-fan.c @@ -40,12 +40,11 @@ enum FAN_PWM_ENABLE { AUTOMATIC = 2, }; -static struct { +struct gpd_fan_data { enum FAN_PWM_ENABLE pwm_enable; u8 pwm_value; - const struct gpd_fan_drvdata *drvdata; -} gpd_driver_priv; +}; struct gpd_fan_drvdata { const char *board_name; // Board name for module param comparison @@ -249,10 +248,10 @@ static const struct gpd_fan_drvdata *gpd_module_drvdata[] = { }; // Helper functions to handle EC read/write -static void gpd_ecram_read(u16 offset, u8 *val) +static void gpd_ecram_read(const struct gpd_fan_drvdata *drvdata, u16 offset, u8 *val) { - u16 addr_port = gpd_driver_priv.drvdata->addr_port; - u16 data_port = gpd_driver_priv.drvdata->data_port; + u16 addr_port = drvdata->addr_port; + u16 data_port = drvdata->data_port; outb(0x2E, addr_port); outb(0x11, data_port); @@ -270,10 +269,10 @@ static void gpd_ecram_read(u16 offset, u8 *val) *val = inb(data_port); } -static void gpd_ecram_write(u16 offset, u8 value) +static void gpd_ecram_write(const struct gpd_fan_drvdata *drvdata, u16 offset, u8 value) { - u16 addr_port = gpd_driver_priv.drvdata->addr_port; - u16 data_port = gpd_driver_priv.drvdata->data_port; + u16 addr_port = drvdata->addr_port; + u16 data_port = drvdata->data_port; outb(0x2E, addr_port); outb(0x11, data_port); @@ -291,198 +290,198 @@ static void gpd_ecram_write(u16 offset, u8 value) outb(value, data_port); } -static int gpd_generic_read_rpm(void) +static int gpd_generic_read_rpm(struct gpd_fan_data *data) { - const struct gpd_fan_drvdata *const drvdata = gpd_driver_priv.drvdata; + const struct gpd_fan_drvdata *drvdata = data->drvdata; u8 high, low; - gpd_ecram_read(drvdata->rpm_read, &high); - gpd_ecram_read(drvdata->rpm_read + 1, &low); + gpd_ecram_read(drvdata, drvdata->rpm_read, &high); + gpd_ecram_read(drvdata, drvdata->rpm_read + 1, &low); return (u16)high << 8 | low; } -static int gpd_wm2_read_rpm(void) +static int gpd_wm2_read_rpm(struct gpd_fan_data *data) { + const struct gpd_fan_drvdata *drvdata = data->drvdata; + for (u16 pwm_ctr_offset = GPD_PWM_CTR_OFFSET; pwm_ctr_offset <= GPD_PWM_CTR_OFFSET + 2; pwm_ctr_offset++) { u8 PWMCTR; - gpd_ecram_read(pwm_ctr_offset, &PWMCTR); + gpd_ecram_read(drvdata, pwm_ctr_offset, &PWMCTR); if (PWMCTR != 0xB8) - gpd_ecram_write(pwm_ctr_offset, 0xB8); + gpd_ecram_write(drvdata, pwm_ctr_offset, 0xB8); } - return gpd_generic_read_rpm(); + return gpd_generic_read_rpm(data); } // Read value for fan1_input -static int gpd_read_rpm(void) +static int gpd_read_rpm(struct gpd_fan_data *data) { - switch (gpd_driver_priv.drvdata->board) { + switch (data->drvdata->board) { case win4_6800u: case win_mini: case duo: case mpc2: - return gpd_generic_read_rpm(); + return gpd_generic_read_rpm(data); case win_max_2: - return gpd_wm2_read_rpm(); + return gpd_wm2_read_rpm(data); } return 0; } -static int gpd_wm2_read_pwm(void) +static int gpd_wm2_read_pwm(struct gpd_fan_data *data) { - const struct gpd_fan_drvdata *const drvdata = gpd_driver_priv.drvdata; + const struct gpd_fan_drvdata *drvdata = data->drvdata; u8 var; - gpd_ecram_read(drvdata->pwm_write, &var); + gpd_ecram_read(drvdata, drvdata->pwm_write, &var); // Match gpd_generic_write_pwm(u8) below return DIV_ROUND_CLOSEST((var - 1) * 255, (drvdata->pwm_max - 1)); } // Read value for pwm1 -static int gpd_read_pwm(void) +static int gpd_read_pwm(struct gpd_fan_data *data) { - switch (gpd_driver_priv.drvdata->board) { + switch (data->drvdata->board) { case win_mini: case duo: case win4_6800u: case mpc2: - switch (gpd_driver_priv.pwm_enable) { + switch (data->pwm_enable) { case DISABLE: return 255; case MANUAL: - return gpd_driver_priv.pwm_value; + return data->pwm_value; case AUTOMATIC: return -EOPNOTSUPP; } break; case win_max_2: - return gpd_wm2_read_pwm(); + return gpd_wm2_read_pwm(data); } return 0; } // PWM value's range in EC is 1 - pwm_max, cast 0 - 255 to it. -static inline u8 gpd_cast_pwm_range(u8 val) +static inline u8 gpd_cast_pwm_range(const struct gpd_fan_drvdata *drvdata, u8 val) { - const struct gpd_fan_drvdata *const drvdata = gpd_driver_priv.drvdata; - return DIV_ROUND_CLOSEST(val * (drvdata->pwm_max - 1), 255) + 1; } -static void gpd_generic_write_pwm(u8 val) +static void gpd_generic_write_pwm(struct gpd_fan_data *data, u8 val) { - const struct gpd_fan_drvdata *const drvdata = gpd_driver_priv.drvdata; + const struct gpd_fan_drvdata *drvdata = data->drvdata; u8 pwm_reg; - pwm_reg = gpd_cast_pwm_range(val); - gpd_ecram_write(drvdata->pwm_write, pwm_reg); + pwm_reg = gpd_cast_pwm_range(drvdata, val); + gpd_ecram_write(drvdata, drvdata->pwm_write, pwm_reg); } -static void gpd_duo_write_pwm(u8 val) +static void gpd_duo_write_pwm(struct gpd_fan_data *data, u8 val) { - const struct gpd_fan_drvdata *const drvdata = gpd_driver_priv.drvdata; + const struct gpd_fan_drvdata *drvdata = data->drvdata; u8 pwm_reg; - pwm_reg = gpd_cast_pwm_range(val); - gpd_ecram_write(drvdata->pwm_write, pwm_reg); - gpd_ecram_write(drvdata->pwm_write + 1, pwm_reg); + pwm_reg = gpd_cast_pwm_range(drvdata, val); + gpd_ecram_write(drvdata, drvdata->pwm_write, pwm_reg); + gpd_ecram_write(drvdata, drvdata->pwm_write + 1, pwm_reg); } // Write value for pwm1 -static int gpd_write_pwm(u8 val) +static int gpd_write_pwm(struct gpd_fan_data *data, u8 val) { - if (gpd_driver_priv.pwm_enable != MANUAL) + if (data->pwm_enable != MANUAL) return -EPERM; - switch (gpd_driver_priv.drvdata->board) { + switch (data->drvdata->board) { case duo: - gpd_duo_write_pwm(val); + gpd_duo_write_pwm(data, val); break; case win_mini: case win4_6800u: case win_max_2: case mpc2: - gpd_generic_write_pwm(val); + gpd_generic_write_pwm(data, val); break; } return 0; } -static void gpd_win_mini_set_pwm_enable(enum FAN_PWM_ENABLE pwm_enable) +static void gpd_win_mini_set_pwm_enable(struct gpd_fan_data *data, enum FAN_PWM_ENABLE pwm_enable) { switch (pwm_enable) { case DISABLE: - gpd_generic_write_pwm(255); + gpd_generic_write_pwm(data, 255); break; case MANUAL: - gpd_generic_write_pwm(gpd_driver_priv.pwm_value); + gpd_generic_write_pwm(data, data->pwm_value); break; case AUTOMATIC: - gpd_ecram_write(gpd_driver_priv.drvdata->pwm_write, 0); + gpd_ecram_write(data->drvdata, data->drvdata->pwm_write, 0); break; } } -static void gpd_duo_set_pwm_enable(enum FAN_PWM_ENABLE pwm_enable) +static void gpd_duo_set_pwm_enable(struct gpd_fan_data *data, enum FAN_PWM_ENABLE pwm_enable) { switch (pwm_enable) { case DISABLE: - gpd_duo_write_pwm(255); + gpd_duo_write_pwm(data, 255); break; case MANUAL: - gpd_duo_write_pwm(gpd_driver_priv.pwm_value); + gpd_duo_write_pwm(data, data->pwm_value); break; case AUTOMATIC: - gpd_ecram_write(gpd_driver_priv.drvdata->pwm_write, 0); + gpd_ecram_write(data->drvdata, data->drvdata->pwm_write, 0); break; } } -static void gpd_wm2_set_pwm_enable(enum FAN_PWM_ENABLE enable) +static void gpd_wm2_set_pwm_enable(struct gpd_fan_data *data, enum FAN_PWM_ENABLE enable) { - const struct gpd_fan_drvdata *const drvdata = gpd_driver_priv.drvdata; + const struct gpd_fan_drvdata *drvdata = data->drvdata; switch (enable) { case DISABLE: - gpd_generic_write_pwm(255); - gpd_ecram_write(drvdata->manual_control_enable, 1); + gpd_generic_write_pwm(data, 255); + gpd_ecram_write(drvdata, drvdata->manual_control_enable, 1); break; case MANUAL: - gpd_generic_write_pwm(gpd_driver_priv.pwm_value); - gpd_ecram_write(drvdata->manual_control_enable, 1); + gpd_generic_write_pwm(data, data->pwm_value); + gpd_ecram_write(drvdata, drvdata->manual_control_enable, 1); break; case AUTOMATIC: - gpd_ecram_write(drvdata->manual_control_enable, 0); + gpd_ecram_write(drvdata, drvdata->manual_control_enable, 0); break; } } // Write value for pwm1_enable -static void gpd_set_pwm_enable(enum FAN_PWM_ENABLE enable) +static void gpd_set_pwm_enable(struct gpd_fan_data *data, enum FAN_PWM_ENABLE enable) { if (enable == MANUAL) // Set pwm_value to max firstly when switching to manual mode, in // consideration of device safety. - gpd_driver_priv.pwm_value = 255; + data->pwm_value = 255; - switch (gpd_driver_priv.drvdata->board) { + switch (data->drvdata->board) { case win_mini: case win4_6800u: case mpc2: - gpd_win_mini_set_pwm_enable(enable); + gpd_win_mini_set_pwm_enable(data, enable); break; case duo: - gpd_duo_set_pwm_enable(enable); + gpd_duo_set_pwm_enable(data, enable); break; case win_max_2: - gpd_wm2_set_pwm_enable(enable); + gpd_wm2_set_pwm_enable(data, enable); break; } } @@ -505,15 +504,16 @@ static umode_t gpd_fan_hwmon_is_visible(__always_unused const void *drvdata, return 0; } -static int gpd_fan_hwmon_read(__always_unused struct device *dev, +static int gpd_fan_hwmon_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, __always_unused int channel, long *val) { + struct gpd_fan_data *data = dev_get_drvdata(dev); int ret; if (type == hwmon_fan) { if (attr == hwmon_fan_input) { - ret = gpd_read_rpm(); + ret = gpd_read_rpm(data); if (ret < 0) return ret; @@ -524,10 +524,10 @@ static int gpd_fan_hwmon_read(__always_unused struct device *dev, } else if (type == hwmon_pwm) { switch (attr) { case hwmon_pwm_enable: - *val = gpd_driver_priv.pwm_enable; + *val = data->pwm_enable; return 0; case hwmon_pwm_input: - ret = gpd_read_pwm(); + ret = gpd_read_pwm(data); if (ret < 0) return ret; @@ -540,27 +540,29 @@ static int gpd_fan_hwmon_read(__always_unused struct device *dev, return -EOPNOTSUPP; } -static int gpd_fan_hwmon_write(__always_unused struct device *dev, +static int gpd_fan_hwmon_write(struct device *dev, enum hwmon_sensor_types type, u32 attr, __always_unused int channel, long val) { + struct gpd_fan_data *data = dev_get_drvdata(dev); + if (type == hwmon_pwm) { switch (attr) { case hwmon_pwm_enable: if (!in_range(val, 0, 3)) return -EINVAL; - gpd_driver_priv.pwm_enable = val; + data->pwm_enable = val; - gpd_set_pwm_enable(gpd_driver_priv.pwm_enable); + gpd_set_pwm_enable(data, data->pwm_enable); return 0; case hwmon_pwm_input: if (!in_range(val, 0, 256)) return -EINVAL; - gpd_driver_priv.pwm_value = val; + data->pwm_value = val; - return gpd_write_pwm(val); + return gpd_write_pwm(data, val); } } @@ -584,26 +586,27 @@ static struct hwmon_chip_info gpd_fan_chip_info = { .info = gpd_fan_hwmon_channel_info }; -static void gpd_win4_init_ec(void) +static void gpd_win4_init_ec(struct gpd_fan_data *data) { + const struct gpd_fan_drvdata *drvdata = data->drvdata; u8 chip_id, chip_ver; - gpd_ecram_read(0x2000, &chip_id); + gpd_ecram_read(drvdata, 0x2000, &chip_id); if (chip_id == 0x55) { - gpd_ecram_read(0x1060, &chip_ver); - gpd_ecram_write(0x1060, chip_ver | 0x80); + gpd_ecram_read(drvdata, 0x1060, &chip_ver); + gpd_ecram_write(drvdata, 0x1060, chip_ver | 0x80); } } -static void gpd_init_ec(void) +static void gpd_init_ec(struct gpd_fan_data *data) { // The buggy firmware won't initialize EC properly on boot. // Before its initialization, reading RPM will always return 0, // and writing PWM will have no effect. // Initialize it manually on driver load. - if (gpd_driver_priv.drvdata->board == win4_6800u) - gpd_win4_init_ec(); + if (data->drvdata->board == win4_6800u) + gpd_win4_init_ec(data); } static int gpd_fan_probe(struct platform_device *pdev) @@ -611,7 +614,9 @@ static int gpd_fan_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; const struct resource *region; const struct resource *res; - const struct device *hwdev; + struct device *hwdev; + struct gpd_fan_data *data; + const struct gpd_fan_drvdata *match; res = platform_get_resource(pdev, IORESOURCE_IO, 0); if (!res) @@ -624,24 +629,42 @@ static int gpd_fan_probe(struct platform_device *pdev) return dev_err_probe(dev, -EBUSY, "Failed to request region\n"); + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + match = dev_get_platdata(dev); + if (!match) + return -EINVAL; + + data->drvdata = match; + data->pwm_enable = AUTOMATIC; + data->pwm_value = 255; + + dev_set_drvdata(dev, data); + hwdev = devm_hwmon_device_register_with_info(dev, DRIVER_NAME, - NULL, + data, &gpd_fan_chip_info, NULL); if (IS_ERR(hwdev)) return dev_err_probe(dev, PTR_ERR(hwdev), "Failed to register hwmon device\n"); - gpd_init_ec(); + gpd_init_ec(data); return 0; } -static void gpd_fan_remove(__always_unused struct platform_device *pdev) +static void gpd_fan_remove(struct platform_device *pdev) { - gpd_driver_priv.pwm_enable = AUTOMATIC; - gpd_set_pwm_enable(AUTOMATIC); + struct gpd_fan_data *data = dev_get_drvdata(&pdev->dev); + + if (data) { + data->pwm_enable = AUTOMATIC; + gpd_set_pwm_enable(data, AUTOMATIC); + } } static struct platform_driver gpd_fan_driver = { @@ -668,6 +691,7 @@ static int __init gpd_fan_init(void) if (!match) { const struct dmi_system_id *dmi_match = dmi_first_match(dmi_table); + if (dmi_match) match = dmi_match->driver_data; } @@ -675,10 +699,6 @@ static int __init gpd_fan_init(void) if (!match) return -ENODEV; - gpd_driver_priv.pwm_enable = AUTOMATIC; - gpd_driver_priv.pwm_value = 255; - gpd_driver_priv.drvdata = match; - struct resource gpd_fan_resources[] = { { .start = match->addr_port, @@ -690,7 +710,8 @@ static int __init gpd_fan_init(void) gpd_fan_platform_device = platform_create_bundle(&gpd_fan_driver, gpd_fan_probe, gpd_fan_resources, - 1, NULL, 0); + 1, + match, sizeof(*match)); if (IS_ERR(gpd_fan_platform_device)) { pr_warn("Failed to create platform device\n"); From a8a444917fe5d30a9787f41cc179f55fc5f559d3 Mon Sep 17 00:00:00 2001 From: Pei Xiao Date: Wed, 10 Jun 2026 09:49:10 +0800 Subject: [PATCH 73/81] hwmon: (gpd-fan): Initialize EC before registering hwmon device Move the gpd_init_ec() call to before devm_hwmon_device_register_with_info in the probe function. With the previous ordering the hwmon device was registered and exposed to userspace before the EC initialization completes, creating a window where sysfs reads could return invalid values. Some buggy firmware won't initialize EC properly on boot. Before its initialization, reading RPM will always return 0, and writing PWM will have no effect. So move gpd_init_ec to before hwmon device register. Fixes: 0ab88e239439 ("hwmon: add GPD devices sensor driver") Signed-off-by: Pei Xiao Link: https://lore.kernel.org/r/4be3734b135c8013157979ab5e80c7ee51243ddd.1781055639.git.xiaopei01@kylinos.cn Signed-off-by: Guenter Roeck --- drivers/hwmon/gpd-fan.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/hwmon/gpd-fan.c b/drivers/hwmon/gpd-fan.c index 7284babd4f5c..745b3fb9e3a4 100644 --- a/drivers/hwmon/gpd-fan.c +++ b/drivers/hwmon/gpd-fan.c @@ -643,6 +643,7 @@ static int gpd_fan_probe(struct platform_device *pdev) dev_set_drvdata(dev, data); + gpd_init_ec(data); hwdev = devm_hwmon_device_register_with_info(dev, DRIVER_NAME, data, @@ -651,9 +652,6 @@ static int gpd_fan_probe(struct platform_device *pdev) if (IS_ERR(hwdev)) return dev_err_probe(dev, PTR_ERR(hwdev), "Failed to register hwmon device\n"); - - gpd_init_ec(data); - return 0; } From 046e6e1a023437bf80358cecba37ea58ebe89e94 Mon Sep 17 00:00:00 2001 From: Pei Xiao Date: Wed, 10 Jun 2026 09:49:11 +0800 Subject: [PATCH 74/81] hwmon: (gpd-fan): upgrade log level from warn to err for platform device creation failure When platform_create_bundle() fails, the error is fatal and prevents the driver from loading. Use pr_err() instead of pr_warn() to clearly indicate a critical failure. Signed-off-by: Pei Xiao Link: https://lore.kernel.org/r/aeb2eaa6df90178b18057a8022a0eccde7bbc82c.1781055639.git.xiaopei01@kylinos.cn Signed-off-by: Guenter Roeck --- drivers/hwmon/gpd-fan.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hwmon/gpd-fan.c b/drivers/hwmon/gpd-fan.c index 745b3fb9e3a4..1b57a5add934 100644 --- a/drivers/hwmon/gpd-fan.c +++ b/drivers/hwmon/gpd-fan.c @@ -712,7 +712,7 @@ static int __init gpd_fan_init(void) match, sizeof(*match)); if (IS_ERR(gpd_fan_platform_device)) { - pr_warn("Failed to create platform device\n"); + pr_err("Failed to create platform device\n"); return PTR_ERR(gpd_fan_platform_device); } From 1fb4397509bd8701d323e81ac9a97c2e24ed49eb Mon Sep 17 00:00:00 2001 From: Pei Xiao Date: Wed, 10 Jun 2026 09:49:12 +0800 Subject: [PATCH 75/81] hwmon: (gpd-fan): fix race condition between device removal and sysfs access Replace the manual gpd_fan_remove() callback with a devres-managed action using devm_add_action_or_reset(). The original remove hook resets the fan to AUTOMATIC mode, but the hwmon sysfs interface (registered with devm_hwmon_device_register_with_info()) remains active until after the remove callback completes. This creates a race window where a concurrent userspace sysfs access can interleave with the EC I/O sequence, potentially corrupting EC registers. Using devm_add_action_or_reset() registers the reset function as a devres action. Due to the LIFO release order of devres, the hwmon device is unregistered (sysfs removed) before the reset action executes, eliminating the race condition. Fixes: 0ab88e239439 ("hwmon: add GPD devices sensor driver") Signed-off-by: Pei Xiao Link: https://lore.kernel.org/r/4400828422cf3a88adad4db224d9efccdb1049d2.1781055639.git.xiaopei01@kylinos.cn Signed-off-by: Guenter Roeck --- drivers/hwmon/gpd-fan.c | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/drivers/hwmon/gpd-fan.c b/drivers/hwmon/gpd-fan.c index 1b57a5add934..d1993cd645cb 100644 --- a/drivers/hwmon/gpd-fan.c +++ b/drivers/hwmon/gpd-fan.c @@ -609,6 +609,16 @@ static void gpd_init_ec(struct gpd_fan_data *data) gpd_win4_init_ec(data); } +static void gpd_fan_reset_hardware(void *pdata) +{ + struct gpd_fan_data *data = pdata; + + if (data) { + data->pwm_enable = AUTOMATIC; + gpd_set_pwm_enable(data, AUTOMATIC); + } +} + static int gpd_fan_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -617,6 +627,7 @@ static int gpd_fan_probe(struct platform_device *pdev) struct device *hwdev; struct gpd_fan_data *data; const struct gpd_fan_drvdata *match; + int ret; res = platform_get_resource(pdev, IORESOURCE_IO, 0); if (!res) @@ -644,6 +655,11 @@ static int gpd_fan_probe(struct platform_device *pdev) dev_set_drvdata(dev, data); gpd_init_ec(data); + + ret = devm_add_action_or_reset(dev, gpd_fan_reset_hardware, data); + if (ret) + return ret; + hwdev = devm_hwmon_device_register_with_info(dev, DRIVER_NAME, data, @@ -655,19 +671,8 @@ static int gpd_fan_probe(struct platform_device *pdev) return 0; } -static void gpd_fan_remove(struct platform_device *pdev) -{ - struct gpd_fan_data *data = dev_get_drvdata(&pdev->dev); - - if (data) { - data->pwm_enable = AUTOMATIC; - gpd_set_pwm_enable(data, AUTOMATIC); - } -} - static struct platform_driver gpd_fan_driver = { .probe = gpd_fan_probe, - .remove = gpd_fan_remove, .driver = { .name = KBUILD_MODNAME, }, From 1df240f323bd9f588e6f5bb886e406c06043394f Mon Sep 17 00:00:00 2001 From: Marius Cristea Date: Wed, 10 Jun 2026 18:19:46 +0300 Subject: [PATCH 76/81] dt-bindings: hwmon: temperature: add support for EMC1812 This is the devicetree schema for Microchip EMC1812/13/14/15/33 Multichannel Low-Voltage Remote Diode Sensor Family. It also updates the MAINTAINERS file to include the new driver. EMC1812 has one external remote temperature monitoring channel. EMC1813 has two external remote temperature monitoring channels. EMC1814 has three external remote temperature monitoring channels and channels 2 and 3 support anti parallel diode. EMC1815 has four external remote temperature monitoring channels and channels 1/2 and 3/4 support anti parallel diode. EMC1833 has two external remote temperature monitoring channels and channels 1 and 2 support anti parallel diode. Resistance Error Correction is supported on channels 1/2 and 3/4. Signed-off-by: Marius Cristea Reviewed-by: Rob Herring (Arm) Link: https://lore.kernel.org/r/20260610-hw_mon-emc1812-v11-1-cef809af5c19@microchip.com Signed-off-by: Guenter Roeck --- .../bindings/hwmon/microchip,emc1812.yaml | 193 ++++++++++++++++++ MAINTAINERS | 6 + 2 files changed, 199 insertions(+) create mode 100644 Documentation/devicetree/bindings/hwmon/microchip,emc1812.yaml diff --git a/Documentation/devicetree/bindings/hwmon/microchip,emc1812.yaml b/Documentation/devicetree/bindings/hwmon/microchip,emc1812.yaml new file mode 100644 index 000000000000..1a273621db82 --- /dev/null +++ b/Documentation/devicetree/bindings/hwmon/microchip,emc1812.yaml @@ -0,0 +1,193 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/hwmon/microchip,emc1812.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Microchip EMC1812/13/14/15/33 multichannel temperature sensor + +maintainers: + - Marius Cristea + +description: | + The Microchip EMC1812/13/14/15/33 is a high-accuracy 2-wire multichannel + low-voltage remote diode temperature monitor. + + The datasheet can be found here: + https://ww1.microchip.com/downloads/aemDocuments/documents/MSLD/ProductDocuments/DataSheets/EMC1812-3-4-5-33-Data-Sheet-DS20005751.pdf + + EMC1812 has one external remote temperature monitoring channel + EMC1813 has two external remote temperature monitoring channels + EMC1814 has three external remote temperature monitoring channels and + channels 2 and 3 support anti parallel diode + EMC1815 has four external remote temperature monitoring channels and + channels 1/2 and 3/4 support anti parallel diode + EMC1833 has two external remote temperature monitoring channels and + channels 1 and 2 support anti parallel diode + +properties: + compatible: + enum: + - microchip,emc1812 + - microchip,emc1813 + - microchip,emc1814 + - microchip,emc1815 + - microchip,emc1833 + + reg: + maxItems: 1 + + interrupts: + items: + - description: alert-therm2 asserts when the ALERT limit is exceeded. + - description: therm-addr asserts when the THERM limit is exceeded. + minItems: 1 + + interrupt-names: + items: + - const: alert-therm2 + - const: therm-addr + minItems: 1 + + "#address-cells": + const: 1 + + "#size-cells": + const: 0 + + microchip,enable-anti-parallel: + description: + Enable anti-parallel diode mode operation. EMC1814, EMC1815 and EMC1833 + support reading two external diodes in anti-parallel connection on the + same set of pins. Disabling APD functionality to implement substrate + diodes on devices that support APD eliminates the benefit of APD + (two diodes on one channel). + type: boolean + + microchip,parasitic-res-on-channel1-2: + description: + Indicates that the chip and the diodes/transistors are sufficiently + far apart that a parasitic resistance is added to the wires, which can + affect the measurements. Due to the availability of only a single + configuration bit in hardware, channels 1 and 2 are affected together. + If channel 2 is not available in hardware, this setting affects only + channel 1. + type: boolean + + microchip,parasitic-res-on-channel3-4: + description: + Indicates that the chip and the diodes/transistors are sufficiently + far apart that a parasitic resistance is added to the wires, which can + affect the measurements. Due to the availability of only a single + configuration bit in hardware, channels 3 and 4 are affected together. + If channel 4 is not available in hardware, this setting affects only + channel 3. + type: boolean + + vdd-supply: true + +patternProperties: + "^channel@[0-4]$": + description: | + Represents the temperature channels. + 0: Internal sensor + 1-4: External remote diodes + type: object + + properties: + reg: + maxItems: 1 + + label: + description: Unique name to identify which channel this is. + + required: + - reg + + additionalProperties: false + +required: + - compatible + - reg + - vdd-supply + +allOf: + # EMC1812: 1 Internal, 1 External Channels, No APD, + # parasitic-res-on-channel1-2: for channel 1 + - if: + properties: + compatible: + const: microchip,emc1812 + then: + properties: + microchip,enable-anti-parallel: false + microchip,parasitic-res-on-channel3-4: false + patternProperties: + "^channel@[2-4]$": false + + # EMC1813: 1 Internal, 2 External Channels, No APD, + # parasitic-res-on-channel1-2: on both channel 1 & 2 + - if: + properties: + compatible: + const: microchip,emc1813 + then: + properties: + microchip,enable-anti-parallel: false + microchip,parasitic-res-on-channel3-4: false + patternProperties: + "^channel@[3-4]$": false + + # EMC1833: 1 Internal, 2 External Channels, Supports APD, + # parasitic-res-on-channel1-2: on both channel 1 & 2 + - if: + properties: + compatible: + const: microchip,emc1833 + then: + properties: + microchip,parasitic-res-on-channel3-4: false + patternProperties: + "^channel@[3-4]$": false + + # EMC1814: 1 Internal, 3 External Channels, Supports APD, + # parasitic-res-on-channel1-2: on both channel 1 & 2 + # parasitic-res-on-channel3-4: for channel 3 + - if: + properties: + compatible: + const: microchip,emc1814 + then: + properties: + channel@4: false + +unevaluatedProperties: false + +examples: + - | + i2c { + #address-cells = <1>; + #size-cells = <0>; + + temperature-sensor@4c { + compatible = "microchip,emc1813"; + reg = <0x4c>; + + #address-cells = <1>; + #size-cells = <0>; + + microchip,parasitic-res-on-channel1-2; + + vdd-supply = <&vdd>; + + channel@1 { + reg = <1>; + label = "External CH1 Temperature"; + }; + + channel@2 { + reg = <2>; + label = "External CH2 Temperature"; + }; + }; + }; diff --git a/MAINTAINERS b/MAINTAINERS index 3b9d66ce05d0..0a2d29fe9bca 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -17397,6 +17397,12 @@ S: Supported F: Documentation/devicetree/bindings/interrupt-controller/microchip,sama7g5-eic.yaml F: drivers/irqchip/irq-mchp-eic.c +MICROCHIP EMC1812 DRIVER +M: Marius Cristea +L: linux-hwmon@vger.kernel.org +S: Supported +F: Documentation/devicetree/bindings/hwmon/microchip,emc1812.yaml + MICROCHIP I2C DRIVER M: Codrin Ciubotariu L: linux-i2c@vger.kernel.org From 54b15c59a46f67c11dadd53ecb9a4d69ce6efe44 Mon Sep 17 00:00:00 2001 From: Marius Cristea Date: Wed, 10 Jun 2026 18:19:47 +0300 Subject: [PATCH 77/81] hwmon: temperature: add support for EMC1812 This is the hwmon driver for Microchip EMC1812/13/14/15/33 Multichannel Low-Voltage Remote Diode Sensor Family. EMC1812 has one external remote temperature monitoring channel. EMC1813 has two external remote temperature monitoring channels. EMC1814 has three external remote temperature monitoring channels, channels 2 and 3 support anti parallel diode. EMC1815 has four external remote temperature monitoring channels and channels 1/2 and 3/4 support anti parallel diode. EMC1833 has two external remote temperature monitoring channels and channels 1 and 2 support anti parallel diode. Resistance Error Correction is supported on channels 1/2 and 3/4. Signed-off-by: Marius Cristea Link: https://lore.kernel.org/r/20260610-hw_mon-emc1812-v11-2-cef809af5c19@microchip.com Signed-off-by: Guenter Roeck --- Documentation/hwmon/emc1812.rst | 67 +++ Documentation/hwmon/index.rst | 1 + MAINTAINERS | 2 + drivers/hwmon/Kconfig | 11 + drivers/hwmon/Makefile | 1 + drivers/hwmon/emc1812.c | 965 ++++++++++++++++++++++++++++++++ 6 files changed, 1047 insertions(+) create mode 100644 Documentation/hwmon/emc1812.rst create mode 100644 drivers/hwmon/emc1812.c diff --git a/Documentation/hwmon/emc1812.rst b/Documentation/hwmon/emc1812.rst new file mode 100644 index 000000000000..0b4fbcaaea71 --- /dev/null +++ b/Documentation/hwmon/emc1812.rst @@ -0,0 +1,67 @@ +.. SPDX-License-Identifier: GPL-2.0-or-later + +Kernel driver emc1812 +===================== + +Supported chips: + + * Microchip EMC1812, EMC1813, EMC1814, EMC1815, EMC1833 + + Prefix: 'emc1812' + + Datasheets: + + - https://ww1.microchip.com/downloads/aemDocuments/documents/MSLD/ProductDocuments/DataSheets/EMC1812-3-4-5-33-Data-Sheet-DS20005751.pdf + +Author: + Marius Cristea + + +Description +----------- + +The Microchip EMC181x/33 chips contain up to 4 remote temperature sensors +and one internal. +- The EMC1812 is a single channel remote temperature sensor. +- The EMC1813 and EMC1833 are dual channel remote temperature sensor. The +remote channels for this selection of devices can support substrate diodes, +discrete diode-connected transistors or CPU/GPU thermal diodes. +- The EMC1814 is a three channel remote temperature sensor that supports +Anti-Parallel Diode (APD) only on one channel. For the channel that does not +support APD functionality, substrate diodes, discrete diode-connected +transistors or CPU/GPU thermal diodes are supported. For the channel that +supports APD, only discrete diode-connected transistors may be implemented. +However, if APD is disabled on the EMC1814, then the channel that supports +APD will be functional with substrate diodes, discrete diode-connected +transistors and CPU/GPU thermal diodes. +- The EMC1815 is a four channel remote temperature sensor. + +The EMC1815 and EMC1833 support APD on all channels. When APD is enabled, +the channels support only diode-connected transistors. If APD is disabled, +then the channels will support substrate transistors, discrete diode-connected +transistors and CPU/GPU thermal diodes. + +Note: Disabling APD functionality to implement substrate diodes on devices +that support APD eliminates the benefit of APD (two diodes on one channel). + +The chips implement three limits for each sensor: low (tempX_min), high +(tempX_max) and critical (tempX_crit). The chips also implement an +hysteresis mechanism which applies to all limits. The relative difference +is stored in a single register on the chip, which means that the relative +difference between the limit and its hysteresis is always the same for +all three limits. + +This implementation detail implies the following: + +* When setting a limit, its hysteresis will automatically follow, the + difference staying unchanged. For example, if the old critical limit was + 80 degrees C, and the hysteresis was 75 degrees C, and you change the + critical limit to 90 degrees C, then the hysteresis will automatically + change to 85 degrees C. +* The hysteresis values can't be set independently. We decided to make + only tempX_crit_hyst writable, while all other hysteresis attributes + are read-only. Setting tempX_crit_hyst writes the difference between + tempX_crit_hyst and tempX_crit into the chip, and the same relative + hysteresis applies automatically to all other limits. +* The limits should be set before the hysteresis. At power up the device + starts with 10 degree hysteresis. diff --git a/Documentation/hwmon/index.rst b/Documentation/hwmon/index.rst index 0295e5989e1e..4aa910569c31 100644 --- a/Documentation/hwmon/index.rst +++ b/Documentation/hwmon/index.rst @@ -72,6 +72,7 @@ Hardware Monitoring Kernel Drivers ds620 e50sn12051 emc1403 + emc1812 emc2103 emc2305 emc6w201 diff --git a/MAINTAINERS b/MAINTAINERS index 0a2d29fe9bca..7457ffc76e2b 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -17402,6 +17402,8 @@ M: Marius Cristea L: linux-hwmon@vger.kernel.org S: Supported F: Documentation/devicetree/bindings/hwmon/microchip,emc1812.yaml +F: Documentation/hwmon/emc1812.rst +F: drivers/hwmon/emc1812.c MICROCHIP I2C DRIVER M: Codrin Ciubotariu diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index f0470cb565f0..e4c4f2b09732 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -2068,6 +2068,17 @@ config SENSORS_EMC1403 Threshold values can be configured using sysfs. Data from the different diodes are accessible via sysfs. +config SENSORS_EMC1812 + tristate "Microchip Technology EMC1812 driver" + depends on I2C + select REGMAP_I2C + help + If you say yes here to build support for Microchip Technology's + EMC181X/33 Multichannel Low-Voltage Remote Diode Sensor Family. + + This driver can also be built as a module. If so, the module + will be called emc1812. + config SENSORS_EMC2103 tristate "SMSC EMC2103" depends on I2C diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index 0efea503c5ae..d5c8bc05123a 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -73,6 +73,7 @@ obj-$(CONFIG_SENSORS_DRIVETEMP) += drivetemp.o obj-$(CONFIG_SENSORS_DS620) += ds620.o obj-$(CONFIG_SENSORS_DS1621) += ds1621.o obj-$(CONFIG_SENSORS_EMC1403) += emc1403.o +obj-$(CONFIG_SENSORS_EMC1812) += emc1812.o obj-$(CONFIG_SENSORS_EMC2103) += emc2103.o obj-$(CONFIG_SENSORS_EMC2305) += emc2305.o obj-$(CONFIG_SENSORS_EMC6W201) += emc6w201.o diff --git a/drivers/hwmon/emc1812.c b/drivers/hwmon/emc1812.c new file mode 100644 index 000000000000..68575c27d090 --- /dev/null +++ b/drivers/hwmon/emc1812.c @@ -0,0 +1,965 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * HWMON driver for Microchip EMC1812/13/14/15/33 Multichannel high-accuracy + * 2-wire low-voltage remote diode temperature monitor family. + * + * Copyright (C) 2026 Microchip Technology Inc. and its subsidiaries + * + * Author: Marius Cristea + * + * Datasheet can be found here: + * https://ww1.microchip.com/downloads/aemDocuments/documents/MSLD/ProductDocuments/DataSheets/EMC1812-3-4-5-33-Data-Sheet-DS20005751.pdf + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* EMC1812 Registers Addresses */ +#define EMC1812_STATUS_ADDR 0x02 +#define EMC1812_CONFIG_LO_ADDR 0x03 + +#define EMC1812_CFG_ADDR 0x09 +#define EMC1812_CONV_ADDR 0x0A +#define EMC1812_INT_DIODE_HIGH_LIMIT_ADDR 0x0B +#define EMC1812_INT_DIODE_LOW_LIMIT_ADDR 0x0C +#define EMC1812_EXT1_HIGH_LIMIT_HIGH_BYTE_ADDR 0x0D +#define EMC1812_EXT1_LOW_LIMIT_HIGH_BYTE_ADDR 0x0E +#define EMC1812_ONE_SHOT_ADDR 0x0F + +#define EMC1812_EXT1_HIGH_LIMIT_LOW_BYTE_ADDR 0x13 +#define EMC1812_EXT1_LOW_LIMIT_LOW_BYTE_ADDR 0x14 +#define EMC1812_EXT2_HIGH_LIMIT_HIGH_BYTE_ADDR 0x15 +#define EMC1812_EXT2_LOW_LIMIT_HIGH_BYTE_ADDR 0x16 +#define EMC1812_EXT2_HIGH_LIMIT_LOW_BYTE_ADDR 0x17 +#define EMC1812_EXT2_LOW_LIMIT_LOW_BYTE_ADDR 0x18 +#define EMC1812_EXT1_THERM_LIMIT_ADDR 0x19 +#define EMC1812_EXT2_THERM_LIMIT_ADDR 0x1A +#define EMC1812_EXT_DIODE_FAULT_STATUS_ADDR 0x1B + +#define EMC1812_DIODE_FAULT_MASK_ADDR 0x1F +#define EMC1812_INT_DIODE_THERM_LIMIT_ADDR 0x20 +#define EMC1812_THRM_HYS_ADDR 0x21 +#define EMC1812_CONSEC_ALERT_ADDR 0x22 + +#define EMC1812_EXT1_BETA_CONFIG_ADDR 0x25 +#define EMC1812_EXT2_BETA_CONFIG_ADDR 0x26 +#define EMC1812_EXT1_IDEALITY_FACTOR_ADDR 0x27 +#define EMC1812_EXT2_IDEALITY_FACTOR_ADDR 0x28 + +#define EMC1812_EXT3_HIGH_LIMIT_HIGH_BYTE_ADDR 0x2C +#define EMC1812_EXT3_LOW_LIMIT_HIGH_BYTE_ADDR 0x2D +#define EMC1812_EXT3_HIGH_LIMIT_LOW_BYTE_ADDR 0x2E +#define EMC1812_EXT3_LOW_LIMIT_LOW_BYTE_ADDR 0x2F +#define EMC1812_EXT3_THERM_LIMIT_ADDR 0x30 +#define EMC1812_EXT3_IDEALITY_FACTOR_ADDR 0x31 + +#define EMC1812_EXT4_HIGH_LIMIT_HIGH_BYTE_ADDR 0x34 +#define EMC1812_EXT4_LOW_LIMIT_HIGH_BYTE_ADDR 0x35 +#define EMC1812_EXT4_HIGH_LIMIT_LOW_BYTE_ADDR 0x36 +#define EMC1812_EXT4_LOW_LIMIT_LOW_BYTE_ADDR 0x37 +#define EMC1812_EXT4_THERM_LIMIT_ADDR 0x38 +#define EMC1812_EXT4_IDEALITY_FACTOR_ADDR 0x39 +#define EMC1812_HIGH_LIMIT_STATUS_ADDR 0x3A +#define EMC1812_LOW_LIMIT_STATUS_ADDR 0x3B +#define EMC1812_THERM_LIMIT_STATUS_ADDR 0x3C +#define EMC1812_ROC_GAIN_ADDR 0x3D +#define EMC1812_ROC_CONFIG_ADDR 0x3E +#define EMC1812_ROC_STATUS_ADDR 0x3F +#define EMC1812_R1_RESH_ADDR 0x40 +#define EMC1812_R1_LIMH_ADDR 0x41 +#define EMC1812_R1_LIML_ADDR 0x42 +#define EMC1812_R1_SMPL_ADDR 0x43 +#define EMC1812_R2_RESH_ADDR 0x44 +#define EMC1812_R2_3_RESL_ADDR 0x45 +#define EMC1812_R2_LIMH_ADDR 0x46 +#define EMC1812_R2_LIML_ADDR 0x47 +#define EMC1812_R2_SMPL_ADDR 0x48 +#define EMC1812_PER_MAXTH_1_ADDR 0x49 +#define EMC1812_PER_MAXT1L_ADDR 0x4A +#define EMC1812_PER_MAXTH_2_ADDR 0x4B +#define EMC1812_PER_MAXT2_3L_ADDR 0x4C +#define EMC1812_GBL_MAXT1H_ADDR 0x4D +#define EMC1812_GBL_MAXT1L_ADDR 0x4E +#define EMC1812_GBL_MAXT2H_ADDR 0x4F +#define EMC1812_GBL_MAXT2L_ADDR 0x50 +#define EMC1812_FILTER_SEL_ADDR 0x51 + +#define EMC1812_INT_HIGH_BYTE_ADDR 0x60 +#define EMC1812_INT_LOW_BYTE_ADDR 0x61 +#define EMC1812_EXT1_HIGH_BYTE_ADDR 0x62 +#define EMC1812_EXT1_LOW_BYTE_ADDR 0x63 +#define EMC1812_EXT2_HIGH_BYTE_ADDR 0x64 +#define EMC1812_EXT2_LOW_BYTE_ADDR 0x65 +#define EMC1812_EXT3_HIGH_BYTE_ADDR 0x66 +#define EMC1812_EXT3_LOW_BYTE_ADDR 0x67 +#define EMC1812_EXT4_HIGH_BYTE_ADDR 0x68 +#define EMC1812_EXT4_LOW_BYTE_ADDR 0x69 +#define EMC1812_HOTTEST_DIODE_HIGH_BYTE_ADDR 0x6A +#define EMC1812_HOTTEST_DIODE_LOW_BYTE_ADDR 0x6B +#define EMC1812_HOTTEST_STATUS_ADDR 0x6C +#define EMC1812_HOTTEST_CFG_ADDR 0x6D + +#define EMC1812_PRODUCT_ID_ADDR 0xFD +#define EMC1812_MANUFACTURER_ID_ADDR 0xFE +#define EMC1812_REVISION_ADDR 0xFF + +/* EMC1812 Config Bits */ +#define EMC1812_CFG_MSKAL BIT(7) +#define EMC1812_CFG_RS BIT(6) +#define EMC1812_CFG_ATTHM BIT(5) +#define EMC1812_CFG_RECD12 BIT(4) +#define EMC1812_CFG_RECD34 BIT(3) +#define EMC1812_CFG_RANGE BIT(2) +#define EMC1812_CFG_DA_ENA BIT(1) +#define EMC1812_CFG_APDD BIT(0) + +/* EMC1812 Status Bits */ +#define EMC1812_STATUS_ROCF BIT(7) +#define EMC1812_STATUS_HOTCHG BIT(6) +#define EMC1812_STATUS_BUSY BIT(5) +#define EMC1812_STATUS_HIGH BIT(4) +#define EMC1812_STATUS_LOW BIT(3) +#define EMC1812_STATUS_FAULT BIT(2) +#define EMC1812_STATUS_ETHRM BIT(1) +#define EMC1812_STATUS_ITHRM BIT(0) + +#define EMC1812_BETA_LOCK_VAL 0x0F + +#define EMC1812_TEMP_CH_ADDR(index) (EMC1812_INT_HIGH_BYTE_ADDR + 2 * (index)) + +#define EMC1812_FILTER_MASK_LEN 2 + +#define EMC1812_PID 0x81 +#define EMC1813_PID 0x87 +#define EMC1814_PID 0x84 +#define EMC1815_PID 0x85 +#define EMC1833_PID 0x83 + +/* The maximum number of channels a member of the family can have */ +#define EMC1812_MAX_NUM_CHANNELS 5 +#define EMC1812_TEMP_OFFSET 64 + +#define EMC1812_DEFAULT_IDEALITY_FACTOR 0x12 + +/* Constants and default values */ +#define EMC1812_HIGH_LIMIT_DEFAULT (85 + EMC1812_TEMP_OFFSET) + +#define EMC1812_TEMP_MASK (HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX | \ + HWMON_T_CRIT | HWMON_T_MAX_HYST | HWMON_T_CRIT_HYST | \ + HWMON_T_MIN_ALARM | HWMON_T_MAX_ALARM | \ + HWMON_T_CRIT_ALARM | HWMON_T_LABEL) + +static const struct hwmon_channel_info * const emc1812_info[] = { + HWMON_CHANNEL_INFO(chip, HWMON_C_UPDATE_INTERVAL), + HWMON_CHANNEL_INFO(temp, + EMC1812_TEMP_MASK, + EMC1812_TEMP_MASK | HWMON_T_FAULT, + EMC1812_TEMP_MASK | HWMON_T_FAULT, + EMC1812_TEMP_MASK | HWMON_T_FAULT, + EMC1812_TEMP_MASK | HWMON_T_FAULT), + NULL +}; + +/** + * struct emc1812_features - features of a emc1812 instance + * @name: chip's name + * @phys_channels: number of physical channels supported by the chip + * @has_ext2_beta_reg: the EXT2_BETA register is available on the chip + */ +struct emc1812_features { + const char *name; + u8 phys_channels; + bool has_ext2_beta_reg; +}; + +static const struct emc1812_features emc1833_chip_config = { + .name = "emc1833", + .phys_channels = 3, + .has_ext2_beta_reg = true, +}; + +static const struct emc1812_features emc1812_chip_config = { + .name = "emc1812", + .phys_channels = 2, + .has_ext2_beta_reg = false, +}; + +static const struct emc1812_features emc1813_chip_config = { + .name = "emc1813", + .phys_channels = 3, + .has_ext2_beta_reg = true, +}; + +static const struct emc1812_features emc1814_chip_config = { + .name = "emc1814", + .phys_channels = 4, + .has_ext2_beta_reg = false, +}; + +static const struct emc1812_features emc1815_chip_config = { + .name = "emc1815", + .phys_channels = 5, + .has_ext2_beta_reg = false, +}; + +enum emc1812_limit_type {temp_min, temp_max}; + +static const u8 emc1812_temp_map[] = { + [hwmon_temp_min] = temp_min, + [hwmon_temp_max] = temp_max, +}; + +static const u8 emc1812_ideality_regs[] = { + [0] = 0xff, + [1] = EMC1812_EXT1_IDEALITY_FACTOR_ADDR, + [2] = EMC1812_EXT2_IDEALITY_FACTOR_ADDR, + [3] = EMC1812_EXT3_IDEALITY_FACTOR_ADDR, + [4] = EMC1812_EXT4_IDEALITY_FACTOR_ADDR, +}; + +static const u8 emc1812_temp_crit_regs[] = { + [0] = EMC1812_INT_DIODE_THERM_LIMIT_ADDR, + [1] = EMC1812_EXT1_THERM_LIMIT_ADDR, + [2] = EMC1812_EXT2_THERM_LIMIT_ADDR, + [3] = EMC1812_EXT3_THERM_LIMIT_ADDR, + [4] = EMC1812_EXT4_THERM_LIMIT_ADDR, +}; + +static const u8 emc1812_limit_regs[][2] = { + [0] = { + [temp_min] = EMC1812_INT_DIODE_LOW_LIMIT_ADDR, + [temp_max] = EMC1812_INT_DIODE_HIGH_LIMIT_ADDR, + }, + [1] = { + [temp_min] = EMC1812_EXT1_LOW_LIMIT_HIGH_BYTE_ADDR, + [temp_max] = EMC1812_EXT1_HIGH_LIMIT_HIGH_BYTE_ADDR, + }, + [2] = { + [temp_min] = EMC1812_EXT2_LOW_LIMIT_HIGH_BYTE_ADDR, + [temp_max] = EMC1812_EXT2_HIGH_LIMIT_HIGH_BYTE_ADDR, + }, + [3] = { + [temp_min] = EMC1812_EXT3_LOW_LIMIT_HIGH_BYTE_ADDR, + [temp_max] = EMC1812_EXT3_HIGH_LIMIT_HIGH_BYTE_ADDR, + }, + [4] = { + [temp_min] = EMC1812_EXT4_LOW_LIMIT_HIGH_BYTE_ADDR, + [temp_max] = EMC1812_EXT4_HIGH_LIMIT_HIGH_BYTE_ADDR, + }, +}; + +static const u8 emc1812_limit_regs_low[][2] = { + [0] = { + [temp_min] = 0xff, + [temp_max] = 0xff, + }, + [1] = { + [temp_min] = EMC1812_EXT1_LOW_LIMIT_LOW_BYTE_ADDR, + [temp_max] = EMC1812_EXT1_HIGH_LIMIT_LOW_BYTE_ADDR, + }, + [2] = { + [temp_min] = EMC1812_EXT2_LOW_LIMIT_LOW_BYTE_ADDR, + [temp_max] = EMC1812_EXT2_HIGH_LIMIT_LOW_BYTE_ADDR, + }, + [3] = { + [temp_min] = EMC1812_EXT3_LOW_LIMIT_LOW_BYTE_ADDR, + [temp_max] = EMC1812_EXT3_HIGH_LIMIT_LOW_BYTE_ADDR, + }, + [4] = { + [temp_min] = EMC1812_EXT4_LOW_LIMIT_LOW_BYTE_ADDR, + [temp_max] = EMC1812_EXT4_HIGH_LIMIT_LOW_BYTE_ADDR, + }, +}; + +/* Lookup table for temperature conversion times in msec */ +static const u16 emc1812_conv_time[] = { + 16000, 8000, 4000, 2000, 1000, 500, 250, 125, 62, 31, 16 +}; + +/** + * struct emc1812_data - information about chip parameters + * @labels: labels of the channels + * @active_ch_mask: active channels + * @chip: pointer to structure holding chip features + * @regmap: device register map + * @recd34_en: state of Resistance Error Correction (REC) on channels 3 and 4 + * @recd12_en: state of Resistance Error Correction (REC) on channels 1 and 2 + * @apdd_en: state of anti-parallel diode mode + */ +struct emc1812_data { + const char *labels[EMC1812_MAX_NUM_CHANNELS]; + unsigned long active_ch_mask; + const struct emc1812_features *chip; + struct regmap *regmap; + bool recd34_en; + bool recd12_en; + bool apdd_en; +}; + +/* emc1812 regmap configuration */ +static const struct regmap_range emc1812_regmap_writable_ranges[] = { + regmap_reg_range(EMC1812_CFG_ADDR, EMC1812_ONE_SHOT_ADDR), + regmap_reg_range(EMC1812_EXT1_HIGH_LIMIT_LOW_BYTE_ADDR, EMC1812_EXT2_THERM_LIMIT_ADDR), + regmap_reg_range(EMC1812_DIODE_FAULT_MASK_ADDR, EMC1812_CONSEC_ALERT_ADDR), + regmap_reg_range(EMC1812_EXT1_BETA_CONFIG_ADDR, EMC1812_EXT4_IDEALITY_FACTOR_ADDR), + regmap_reg_range(EMC1812_ROC_GAIN_ADDR, EMC1812_ROC_CONFIG_ADDR), + regmap_reg_range(EMC1812_R1_LIMH_ADDR, EMC1812_R1_SMPL_ADDR), + regmap_reg_range(EMC1812_R2_LIMH_ADDR, EMC1812_R2_SMPL_ADDR), + regmap_reg_range(EMC1812_FILTER_SEL_ADDR, EMC1812_FILTER_SEL_ADDR), + regmap_reg_range(EMC1812_HOTTEST_CFG_ADDR, EMC1812_HOTTEST_CFG_ADDR), +}; + +static const struct regmap_access_table emc1812_regmap_wr_table = { + .yes_ranges = emc1812_regmap_writable_ranges, + .n_yes_ranges = ARRAY_SIZE(emc1812_regmap_writable_ranges), +}; + +static const struct regmap_range emc1812_regmap_rd_ranges[] = { + regmap_reg_range(EMC1812_STATUS_ADDR, EMC1812_CONFIG_LO_ADDR), + regmap_reg_range(EMC1812_CFG_ADDR, EMC1812_ONE_SHOT_ADDR), + regmap_reg_range(EMC1812_EXT1_HIGH_LIMIT_LOW_BYTE_ADDR, + EMC1812_EXT_DIODE_FAULT_STATUS_ADDR), + regmap_reg_range(EMC1812_DIODE_FAULT_MASK_ADDR, EMC1812_CONSEC_ALERT_ADDR), + regmap_reg_range(EMC1812_EXT1_BETA_CONFIG_ADDR, EMC1812_FILTER_SEL_ADDR), + regmap_reg_range(EMC1812_INT_HIGH_BYTE_ADDR, EMC1812_HOTTEST_CFG_ADDR), + regmap_reg_range(EMC1812_PRODUCT_ID_ADDR, EMC1812_REVISION_ADDR), +}; + +static const struct regmap_access_table emc1812_regmap_rd_table = { + .yes_ranges = emc1812_regmap_rd_ranges, + .n_yes_ranges = ARRAY_SIZE(emc1812_regmap_rd_ranges), +}; + +static bool emc1812_is_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case EMC1812_STATUS_ADDR: + case EMC1812_EXT_DIODE_FAULT_STATUS_ADDR: + case EMC1812_DIODE_FAULT_MASK_ADDR: + case EMC1812_EXT1_BETA_CONFIG_ADDR: + case EMC1812_EXT2_BETA_CONFIG_ADDR: + case EMC1812_HIGH_LIMIT_STATUS_ADDR: + case EMC1812_LOW_LIMIT_STATUS_ADDR: + case EMC1812_THERM_LIMIT_STATUS_ADDR: + case EMC1812_ROC_STATUS_ADDR: + case EMC1812_PER_MAXTH_1_ADDR: + case EMC1812_PER_MAXT1L_ADDR: + case EMC1812_PER_MAXTH_2_ADDR: + case EMC1812_PER_MAXT2_3L_ADDR: + case EMC1812_GBL_MAXT1H_ADDR: + case EMC1812_GBL_MAXT1L_ADDR: + case EMC1812_GBL_MAXT2H_ADDR: + case EMC1812_GBL_MAXT2L_ADDR: + case EMC1812_INT_HIGH_BYTE_ADDR: + case EMC1812_INT_LOW_BYTE_ADDR: + case EMC1812_EXT1_HIGH_BYTE_ADDR: + case EMC1812_EXT1_LOW_BYTE_ADDR: + case EMC1812_EXT2_HIGH_BYTE_ADDR: + case EMC1812_EXT2_LOW_BYTE_ADDR: + case EMC1812_EXT3_HIGH_BYTE_ADDR: + case EMC1812_EXT3_LOW_BYTE_ADDR: + case EMC1812_EXT4_HIGH_BYTE_ADDR: + case EMC1812_EXT4_LOW_BYTE_ADDR: + case EMC1812_HOTTEST_DIODE_HIGH_BYTE_ADDR: + case EMC1812_HOTTEST_DIODE_LOW_BYTE_ADDR: + case EMC1812_HOTTEST_STATUS_ADDR: + return true; + default: + return false; + } +} + +static const struct regmap_config emc1812_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .rd_table = &emc1812_regmap_rd_table, + .wr_table = &emc1812_regmap_wr_table, + .volatile_reg = emc1812_is_volatile_reg, + .max_register = EMC1812_REVISION_ADDR, + .cache_type = REGCACHE_MAPLE, +}; + +static umode_t emc1812_is_visible(const void *_data, enum hwmon_sensor_types type, + u32 attr, int channel) +{ + const struct emc1812_data *data = _data; + + switch (type) { + case hwmon_temp: + /* Don't show channels which are not enabled */ + if (!(data->active_ch_mask & BIT(channel))) + return 0; + + switch (attr) { + case hwmon_temp_min: + case hwmon_temp_max: + case hwmon_temp_crit: + case hwmon_temp_crit_hyst: + return 0644; + case hwmon_temp_crit_alarm: + case hwmon_temp_input: + case hwmon_temp_fault: + case hwmon_temp_max_alarm: + case hwmon_temp_max_hyst: + case hwmon_temp_min_alarm: + return 0444; + case hwmon_temp_label: + if (data->labels[channel]) + return 0444; + return 0; + default: + return 0; + } + case hwmon_chip: + switch (attr) { + case hwmon_chip_update_interval: + return 0644; + default: + return 0; + } + default: + return 0; + } +}; + +static int emc1812_get_temp(struct emc1812_data *data, int channel, long *val) +{ + __be16 tmp_be16; + int ret; + + ret = regmap_bulk_read(data->regmap, EMC1812_TEMP_CH_ADDR(channel), + &tmp_be16, sizeof(tmp_be16)); + if (ret) + return ret; + + /* Range is always -64 to 191.875°C */ + *val = ((be16_to_cpu(tmp_be16) >> 5) - (EMC1812_TEMP_OFFSET << 3)) * 125; + + return 0; +} + +static int emc1812_get_crit_limit_temp(struct emc1812_data *data, int channel, long *val) +{ + unsigned int tmp; + int ret; + + /* Critical register is 8bits long and keeps only integer part of temperature */ + ret = regmap_read(data->regmap, emc1812_temp_crit_regs[channel], &tmp); + if (ret) + return ret; + + *val = tmp; + /* Range is always -64 to 191°C */ + *val = (*val - EMC1812_TEMP_OFFSET) * 1000; + + return 0; +} + +static int emc1812_get_limit_temp(struct emc1812_data *data, int ch, + enum emc1812_limit_type type, long *val) +{ + unsigned int regvalh; + unsigned int regvall = 0; + int ret; + + ret = regmap_read(data->regmap, emc1812_limit_regs[ch][type], ®valh); + if (ret < 0) + return ret; + + if (ch) { + ret = regmap_read(data->regmap, emc1812_limit_regs_low[ch][type], ®vall); + if (ret < 0) + return ret; + } + + /* Range is always -64 to 191.875°C */ + *val = ((regvalh << 3) | (regvall >> 5)); + *val = (*val - (EMC1812_TEMP_OFFSET << 3)) * 125; + + return 0; +} + +static int emc1812_read_reg(struct device *dev, struct emc1812_data *data, u32 attr, + int channel, long *val) +{ + unsigned int hyst; + int ret; + + switch (attr) { + case hwmon_temp_min: + case hwmon_temp_max: + return emc1812_get_limit_temp(data, channel, emc1812_temp_map[attr], val); + case hwmon_temp_crit: + return emc1812_get_crit_limit_temp(data, channel, val); + case hwmon_temp_input: + return emc1812_get_temp(data, channel, val); + case hwmon_temp_max_hyst: + ret = emc1812_get_limit_temp(data, channel, temp_max, val); + if (ret < 0) + return ret; + + ret = regmap_read(data->regmap, EMC1812_THRM_HYS_ADDR, &hyst); + if (ret < 0) + return ret; + + *val -= (long)hyst * 1000; + + return 0; + case hwmon_temp_crit_hyst: + ret = emc1812_get_crit_limit_temp(data, channel, val); + if (ret < 0) + return ret; + + ret = regmap_read(data->regmap, EMC1812_THRM_HYS_ADDR, &hyst); + if (ret < 0) + return ret; + + *val -= (long)hyst * 1000; + + return 0; + case hwmon_temp_min_alarm: + *val = regmap_test_bits(data->regmap, EMC1812_LOW_LIMIT_STATUS_ADDR, + BIT(channel)); + if (*val < 0) + return *val; + + return 0; + case hwmon_temp_max_alarm: + *val = regmap_test_bits(data->regmap, EMC1812_HIGH_LIMIT_STATUS_ADDR, + BIT(channel)); + if (*val < 0) + return *val; + + return 0; + case hwmon_temp_crit_alarm: + *val = regmap_test_bits(data->regmap, EMC1812_THERM_LIMIT_STATUS_ADDR, + BIT(channel)); + if (*val < 0) + return *val; + + return 0; + case hwmon_temp_fault: + *val = regmap_test_bits(data->regmap, EMC1812_EXT_DIODE_FAULT_STATUS_ADDR, + BIT(channel)); + if (*val < 0) + return *val; + + return 0; + default: + return -EOPNOTSUPP; + } +} + +static int emc1812_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, + int channel, long *val) +{ + struct emc1812_data *data = dev_get_drvdata(dev); + unsigned int convrate; + int ret; + + switch (type) { + case hwmon_temp: + return emc1812_read_reg(dev, data, attr, channel, val); + case hwmon_chip: + switch (attr) { + case hwmon_chip_update_interval: + ret = regmap_read(data->regmap, EMC1812_CONV_ADDR, &convrate); + if (ret < 0) + return ret; + + if (convrate > 10) + convrate = 4; + + *val = DIV_ROUND_CLOSEST(16000, 1 << convrate); + return 0; + default: + return -EOPNOTSUPP; + } + default: + return -EOPNOTSUPP; + } +} + +static int emc1812_read_string(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, const char **str) +{ + struct emc1812_data *data = dev_get_drvdata(dev); + + if (channel >= data->chip->phys_channels) + return -EOPNOTSUPP; + + switch (type) { + case hwmon_temp: + switch (attr) { + case hwmon_temp_label: + *str = data->labels[channel]; + return 0; + default: + return -EOPNOTSUPP; + } + default: + return -EOPNOTSUPP; + } +} + +static int emc1812_set_hyst(struct emc1812_data *data, int channel, int val) +{ + unsigned int limit; + int hyst, ret; + + /* Critical register is 8bits long and keeps only integer part of temperature */ + ret = regmap_read(data->regmap, emc1812_temp_crit_regs[channel], &limit); + if (ret) + return ret; + + hyst = clamp_val((int)limit - val, 0, 255); + + ret = regmap_write(data->regmap, EMC1812_THRM_HYS_ADDR, hyst); + + return ret; +} + +static int emc1812_set_temp(struct emc1812_data *data, int channel, + enum emc1812_limit_type map, int val) +{ + unsigned int valh, vall; + u8 regh, regl; + int ret; + + regh = emc1812_limit_regs[channel][map]; + regl = emc1812_limit_regs_low[channel][map]; + + if (channel) { + val = DIV_ROUND_CLOSEST(val, 125); + valh = (val >> 3) & 0xff; + vall = (val & 0x07) << 5; + } else { + /* Temperature limit for internal channel is stored on 8bits */ + valh = DIV_ROUND_CLOSEST(val, 1000); + valh = clamp_val(valh, 0, 255); + } + + ret = regmap_write(data->regmap, regh, valh); + if (ret < 0) + return ret; + + if (channel) + ret = regmap_write(data->regmap, regl, vall); + + return ret; +} + +static int emc1812_write(struct device *dev, enum hwmon_sensor_types type, u32 attr, + int channel, long val) +{ + struct emc1812_data *data = dev_get_drvdata(dev); + unsigned int interval, tmp; + + switch (type) { + case hwmon_temp: + /* Range should be -64000 to 191875°C + (EMC1812_TEMP_OFFSET * 1000) */ + val = clamp_val(val, -64000, 191875); + val = val + (EMC1812_TEMP_OFFSET * 1000); + + switch (attr) { + case hwmon_temp_min: + case hwmon_temp_max: + return emc1812_set_temp(data, channel, emc1812_temp_map[attr], val); + case hwmon_temp_crit: + /* Critical temperature limit is stored on 8bits */ + val = DIV_ROUND_CLOSEST(val, 1000); + tmp = clamp_val(val, 0, 255); + return regmap_write(data->regmap, emc1812_temp_crit_regs[channel], tmp); + case hwmon_temp_crit_hyst: + /* Critical temperature hysteresis is stored on 8bits */ + val = DIV_ROUND_CLOSEST(val, 1000); + tmp = clamp_val(val, 0, 255); + return emc1812_set_hyst(data, channel, tmp); + default: + return -EOPNOTSUPP; + } + case hwmon_chip: + switch (attr) { + case hwmon_chip_update_interval: + interval = clamp_val(val, 0, 16000); + tmp = find_closest_descending(interval, emc1812_conv_time, + ARRAY_SIZE(emc1812_conv_time)); + return regmap_write(data->regmap, EMC1812_CONV_ADDR, tmp); + default: + return -EOPNOTSUPP; + } + default: + return -EOPNOTSUPP; + } +} + +static int emc1812_init(struct emc1812_data *priv) +{ + int i, ret; + u8 val; + + ret = regmap_write(priv->regmap, EMC1812_THRM_HYS_ADDR, 0x0A); + if (ret) + return ret; + + ret = regmap_write(priv->regmap, EMC1812_CONSEC_ALERT_ADDR, 0x70); + if (ret) + return ret; + + ret = regmap_write(priv->regmap, EMC1812_FILTER_SEL_ADDR, 0); + if (ret) + return ret; + + ret = regmap_write(priv->regmap, EMC1812_HOTTEST_CFG_ADDR, 0); + if (ret) + return ret; + + /* Enables the beta compensation factor auto-detection function for beta1 and beta2 */ + ret = regmap_write(priv->regmap, EMC1812_EXT1_BETA_CONFIG_ADDR, + EMC1812_BETA_LOCK_VAL); + if (ret) + return ret; + + if (priv->chip->has_ext2_beta_reg) { + ret = regmap_write(priv->regmap, EMC1812_EXT2_BETA_CONFIG_ADDR, + EMC1812_BETA_LOCK_VAL); + if (ret) + return ret; + } + + for (i = 0; i < priv->chip->phys_channels; i++) { + if (!test_bit(i, &priv->active_ch_mask)) + continue; + + /* Update the max temperature limit for extended temperature range. */ + ret = emc1812_set_temp(priv, i, emc1812_temp_map[hwmon_temp_max], + EMC1812_HIGH_LIMIT_DEFAULT * 1000); + if (ret) + return ret; + + /* Update the critical temperature limit for extended temperature range. */ + ret = regmap_write(priv->regmap, emc1812_temp_crit_regs[i], + EMC1812_HIGH_LIMIT_DEFAULT); + if (ret) + return ret; + + /* Set the ideality factor */ + if (i > 0) { + ret = regmap_write(priv->regmap, emc1812_ideality_regs[i], + EMC1812_DEFAULT_IDEALITY_FACTOR); + if (ret) + return ret; + } + } + + /* + * Set default values in registers. APDD, RECD12 and RECD34 are active on 0. + * Set the device to be in Run (Active) state and converting on all + * channels. + * Don't change conversion rate. After reset, default is 4 conversions/seconds. + * The temperature measurement range is -64°C to +191.875°C. + * Set ALERT/THERM2 pin to be in comparator mode (When the ALERT/THERM2 pin is + * asserted in comparator mode, the corresponding High Limit Status bits are set. + * Reading these bits does not clear them until the ALERT/THERM2 pin is deasserted. + * Once the ALERT/THERM2 pin is deasserted, the status bits are automatically + * cleared.). + */ + val = FIELD_PREP(EMC1812_CFG_MSKAL, 0) | + FIELD_PREP(EMC1812_CFG_RS, 0) | + FIELD_PREP(EMC1812_CFG_ATTHM, 1) | + FIELD_PREP(EMC1812_CFG_RECD12, !priv->recd12_en) | + FIELD_PREP(EMC1812_CFG_RECD34, !priv->recd34_en) | + FIELD_PREP(EMC1812_CFG_RANGE, 1) | + FIELD_PREP(EMC1812_CFG_DA_ENA, 0) | + FIELD_PREP(EMC1812_CFG_APDD, !priv->apdd_en); + + return regmap_write(priv->regmap, EMC1812_CFG_ADDR, val); +} + +static int emc1812_parse_fw_config(struct emc1812_data *data, struct device *dev) +{ + unsigned int reg_nr = 0; + int ret; + + /* To be able to load the driver in case we don't have device tree */ + if (!dev_fwnode(dev)) { + data->active_ch_mask = BIT(data->chip->phys_channels) - 1; + return 0; + } + + data->apdd_en = device_property_read_bool(dev, "microchip,enable-anti-parallel"); + data->recd12_en = device_property_read_bool(dev, "microchip,parasitic-res-on-channel1-2"); + data->recd34_en = device_property_read_bool(dev, "microchip,parasitic-res-on-channel3-4"); + + /* Internal temperature channel is always active */ + data->labels[reg_nr] = "internal_diode"; + set_bit(reg_nr, &data->active_ch_mask); + + device_for_each_child_node_scoped(dev, child) { + ret = fwnode_property_read_u32(child, "reg", ®_nr); + if (ret || reg_nr >= data->chip->phys_channels) + return dev_err_probe(dev, -EINVAL, + "The index is higher then the chip supports\n"); + /* Mark channel as active */ + set_bit(reg_nr, &data->active_ch_mask); + + fwnode_property_read_string(child, "label", &data->labels[reg_nr]); + } + + return 0; +} + +static int emc1812_chip_identify(struct emc1812_data *data, struct i2c_client *client) +{ + const struct emc1812_features *chip; + struct device *dev = &client->dev; + unsigned int tmp; + int ret; + + ret = regmap_read(data->regmap, EMC1812_PRODUCT_ID_ADDR, &tmp); + if (ret) + return ret; + + switch (tmp) { + case EMC1812_PID: + data->chip = &emc1812_chip_config; + break; + case EMC1813_PID: + data->chip = &emc1813_chip_config; + break; + case EMC1814_PID: + data->chip = &emc1814_chip_config; + break; + case EMC1815_PID: + data->chip = &emc1815_chip_config; + break; + case EMC1833_PID: + data->chip = &emc1833_chip_config; + break; + default: + /* + * If failed to identify the hardware based on internal registers, + * try using fallback compatible in device tree to deal with some + * newer part number. + */ + chip = i2c_get_match_data(client); + if (!chip) + return -ENODEV; + + dev_warn(dev, "Unrecognized hardware ID 0x%x, using %s from devicetree data\n", + tmp, chip->name); + + data->chip = chip; + + return 0; + } + + return 0; +} + +static const struct hwmon_ops emc1812_ops = { + .is_visible = emc1812_is_visible, + .read = emc1812_read, + .read_string = emc1812_read_string, + .write = emc1812_write, +}; + +static const struct hwmon_chip_info emc1812_chip_info = { + .ops = &emc1812_ops, + .info = emc1812_info, +}; + +static int emc1812_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct emc1812_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, &emc1812_regmap_config); + if (IS_ERR(data->regmap)) + return dev_err_probe(dev, PTR_ERR(data->regmap), + "Cannot initialize register map\n"); + + ret = emc1812_chip_identify(data, client); + if (ret) + return dev_err_probe(dev, ret, "Chip identification fails\n"); + + ret = emc1812_parse_fw_config(data, dev); + if (ret) + return ret; + + ret = emc1812_init(data); + if (ret) + return dev_err_probe(dev, ret, "Cannot initialize device\n"); + + hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, data, + &emc1812_chip_info, NULL); + + return PTR_ERR_OR_ZERO(hwmon_dev); +} + +static const struct i2c_device_id emc1812_id[] = { + { .name = "emc1812", .driver_data = (kernel_ulong_t)&emc1812_chip_config }, + { .name = "emc1813", .driver_data = (kernel_ulong_t)&emc1813_chip_config }, + { .name = "emc1814", .driver_data = (kernel_ulong_t)&emc1814_chip_config }, + { .name = "emc1815", .driver_data = (kernel_ulong_t)&emc1815_chip_config }, + { .name = "emc1833", .driver_data = (kernel_ulong_t)&emc1833_chip_config }, + { } +}; +MODULE_DEVICE_TABLE(i2c, emc1812_id); + +static const struct of_device_id emc1812_of_match[] = { + { + .compatible = "microchip,emc1812", + .data = &emc1812_chip_config + }, + { + .compatible = "microchip,emc1813", + .data = &emc1813_chip_config + }, + { + .compatible = "microchip,emc1814", + .data = &emc1814_chip_config + }, + { + .compatible = "microchip,emc1815", + .data = &emc1815_chip_config + }, + { + .compatible = "microchip,emc1833", + .data = &emc1833_chip_config + }, + { } +}; +MODULE_DEVICE_TABLE(of, emc1812_of_match); + +static struct i2c_driver emc1812_driver = { + .driver = { + .name = "emc1812", + .of_match_table = emc1812_of_match, + }, + .probe = emc1812_probe, + .id_table = emc1812_id, +}; +module_i2c_driver(emc1812_driver); + +MODULE_AUTHOR("Marius Cristea "); +MODULE_DESCRIPTION("EMC1812/13/14/15/33 high-accuracy remote diode temperature monitor Driver"); +MODULE_LICENSE("GPL"); From 5a1aba28f6c79f4bbb7a5361063225030ecd36f3 Mon Sep 17 00:00:00 2001 From: Armin Wolf Date: Wed, 10 Jun 2026 20:01:41 +0200 Subject: [PATCH 78/81] hwmon: (dell-smm) Add Dell Latitude 7530 to fan control whitelist MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A user reported that the Dell Latitude 7530 needs to be whitelisted for the special SMM calls necessary for globally enabling/disabling BIOS fan control. Closes: https://github.com/Wer-Wolf/i8kutils/issues/17 Signed-off-by: Armin Wolf Acked-by: Pali Rohár Link: https://lore.kernel.org/r/20260610180141.311503-1-W_Armin@gmx.de Signed-off-by: Guenter Roeck --- drivers/hwmon/dell-smm-hwmon.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/hwmon/dell-smm-hwmon.c b/drivers/hwmon/dell-smm-hwmon.c index 038edffc1ac7..9cec0c769e8a 100644 --- a/drivers/hwmon/dell-smm-hwmon.c +++ b/drivers/hwmon/dell-smm-hwmon.c @@ -1574,6 +1574,14 @@ static const struct dmi_system_id i8k_whitelist_fan_control[] __initconst = { }, .driver_data = (void *)&i8k_fan_control_data[I8K_FAN_30A3_31A3], }, + { + .ident = "Dell Latitude 7530", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Latitude 7530"), + }, + .driver_data = (void *)&i8k_fan_control_data[I8K_FAN_30A3_31A3], + }, { .ident = "Dell Latitude E6440", .matches = { From 69cbfd708884fa71a1c2353c07ecd8e646bd43a0 Mon Sep 17 00:00:00 2001 From: Pei Xiao Date: Thu, 11 Jun 2026 08:44:14 +0800 Subject: [PATCH 79/81] hwmon: (gpd-fan) Reject EC PWM value 0 as invalid The EC firmware is expected to return values in [1, pwm_max]. A read of 0 is illegal and would cause underflow in the conversion formula. Explicitly check for 0 and return -EIO. Signed-off-by: Pei Xiao Link: https://lore.kernel.org/r/1c2ffa0d832ae3a74f6d4ffa7cc7b7e6cced69e3.1781138459.git.xiaopei01@kylinos.cn Signed-off-by: Guenter Roeck --- drivers/hwmon/gpd-fan.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/hwmon/gpd-fan.c b/drivers/hwmon/gpd-fan.c index d1993cd645cb..ed212615b2fe 100644 --- a/drivers/hwmon/gpd-fan.c +++ b/drivers/hwmon/gpd-fan.c @@ -341,6 +341,10 @@ static int gpd_wm2_read_pwm(struct gpd_fan_data *data) gpd_ecram_read(drvdata, drvdata->pwm_write, &var); + // EC PWM register valid range is 1 ~ pwm_max; 0 is an invalid state + if (unlikely(!var)) + return -EIO; + // Match gpd_generic_write_pwm(u8) below return DIV_ROUND_CLOSEST((var - 1) * 255, (drvdata->pwm_max - 1)); } From 83dda7ed185501ba1f8165aeca83ff4a8ef7c263 Mon Sep 17 00:00:00 2001 From: Potin Lai Date: Thu, 11 Jun 2026 13:46:18 +0800 Subject: [PATCH 80/81] hwmon: (pmbus/lm25066) Fix PMBus coefficients for LM5064/5066/5066i Swap the high setting and low setting coefficients in the lm25066_coeff table for LM5064, LM5066, and LM5066i. The coefficients were previously mapped incorrectly, resulting in inverted current and power scaling. Additionally, dynamically assign the exponent (R) registers inside the probe's LM25066_DEV_SETUP_CL check. This ensures that the proper exponent is applied (e.g., for LM25056, high setting power exponent is -4, but low setting power exponent is -3). Signed-off-by: Potin Lai Link: https://lore.kernel.org/r/20260611-lm25066-driver-fix-v3-1-9d7d4b4e253d@gmail.com Signed-off-by: Guenter Roeck --- drivers/hwmon/pmbus/lm25066.c | 60 ++++++++++++++++++----------------- 1 file changed, 31 insertions(+), 29 deletions(-) diff --git a/drivers/hwmon/pmbus/lm25066.c b/drivers/hwmon/pmbus/lm25066.c index 0f7df7e2c9c5..2be50cf1bbde 100644 --- a/drivers/hwmon/pmbus/lm25066.c +++ b/drivers/hwmon/pmbus/lm25066.c @@ -132,23 +132,23 @@ static const struct __coeff lm25066_coeff[][PSC_NUM_CLASSES + 2] = { .R = -2, }, [PSC_CURRENT_IN] = { - .m = 10742, - .b = 1552, - .R = -2, - }, - [PSC_CURRENT_IN_L] = { .m = 5456, .b = 2118, .R = -2, }, + [PSC_CURRENT_IN_L] = { + .m = 10742, + .b = 1552, + .R = -2, + }, [PSC_POWER] = { - .m = 1204, - .b = 8524, + .m = 612, + .b = 11202, .R = -3, }, [PSC_POWER_L] = { - .m = 612, - .b = 11202, + .m = 1204, + .b = 8524, .R = -3, }, [PSC_TEMPERATURE] = { @@ -167,23 +167,23 @@ static const struct __coeff lm25066_coeff[][PSC_NUM_CLASSES + 2] = { .R = -2, }, [PSC_CURRENT_IN] = { - .m = 10753, - .b = -1200, - .R = -2, - }, - [PSC_CURRENT_IN_L] = { .m = 5405, .b = -600, .R = -2, }, + [PSC_CURRENT_IN_L] = { + .m = 10753, + .b = -1200, + .R = -2, + }, [PSC_POWER] = { - .m = 1204, - .b = -6000, + .m = 605, + .b = -8000, .R = -3, }, [PSC_POWER_L] = { - .m = 605, - .b = -8000, + .m = 1204, + .b = -6000, .R = -3, }, [PSC_TEMPERATURE] = { @@ -202,23 +202,23 @@ static const struct __coeff lm25066_coeff[][PSC_NUM_CLASSES + 2] = { .R = -2, }, [PSC_CURRENT_IN] = { - .m = 15076, - .b = -504, - .R = -2, - }, - [PSC_CURRENT_IN_L] = { .m = 7645, .b = 100, .R = -2, }, + [PSC_CURRENT_IN_L] = { + .m = 15076, + .b = -504, + .R = -2, + }, [PSC_POWER] = { - .m = 1701, - .b = -4000, + .m = 861, + .b = -965, .R = -3, }, [PSC_POWER_L] = { - .m = 861, - .b = -965, + .m = 1701, + .b = -4000, .R = -3, }, [PSC_TEMPERATURE] = { @@ -519,18 +519,20 @@ static int lm25066_probe(struct i2c_client *client) info->m[PSC_VOLTAGE_OUT] = coeff[PSC_VOLTAGE_OUT].m; info->b[PSC_VOLTAGE_OUT] = coeff[PSC_VOLTAGE_OUT].b; info->R[PSC_VOLTAGE_OUT] = coeff[PSC_VOLTAGE_OUT].R; - info->R[PSC_CURRENT_IN] = coeff[PSC_CURRENT_IN].R; - info->R[PSC_POWER] = coeff[PSC_POWER].R; if (config & LM25066_DEV_SETUP_CL) { info->m[PSC_CURRENT_IN] = coeff[PSC_CURRENT_IN_L].m; info->b[PSC_CURRENT_IN] = coeff[PSC_CURRENT_IN_L].b; + info->R[PSC_CURRENT_IN] = coeff[PSC_CURRENT_IN_L].R; info->m[PSC_POWER] = coeff[PSC_POWER_L].m; info->b[PSC_POWER] = coeff[PSC_POWER_L].b; + info->R[PSC_POWER] = coeff[PSC_POWER_L].R; } else { info->m[PSC_CURRENT_IN] = coeff[PSC_CURRENT_IN].m; info->b[PSC_CURRENT_IN] = coeff[PSC_CURRENT_IN].b; + info->R[PSC_CURRENT_IN] = coeff[PSC_CURRENT_IN].R; info->m[PSC_POWER] = coeff[PSC_POWER].m; info->b[PSC_POWER] = coeff[PSC_POWER].b; + info->R[PSC_POWER] = coeff[PSC_POWER].R; } /* From 9955c92abe72564a49c293b1c15cd3b4f02ea6a0 Mon Sep 17 00:00:00 2001 From: "Rob Herring (Arm)" Date: Fri, 12 Jun 2026 16:53:32 -0500 Subject: [PATCH 81/81] hwmon: tmp401: Read "ti,n-factor" as signed The "ti,n-factor" binding and examples allow negative correction values. Reading it as u32 makes the helper type disagree with the documented signed value and hides real schema mismatches. Use the signed helper so the DT access matches the s32 value stored by the driver. Assisted-by: Codex:gpt-5-5 Signed-off-by: Rob Herring (Arm) Link: https://lore.kernel.org/r/20260612215332.1889497-1-robh@kernel.org Signed-off-by: Guenter Roeck --- drivers/hwmon/tmp401.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hwmon/tmp401.c b/drivers/hwmon/tmp401.c index a09225a87355..ca0ff525ea29 100644 --- a/drivers/hwmon/tmp401.c +++ b/drivers/hwmon/tmp401.c @@ -560,7 +560,7 @@ static int tmp401_init_client(struct tmp401_data *data) return ret; } - ret = of_property_read_u32(data->client->dev.of_node, "ti,n-factor", &nfactor); + ret = of_property_read_s32(data->client->dev.of_node, "ti,n-factor", &nfactor); if (!ret) { if (data->kind == tmp401) { dev_err(&data->client->dev, "ti,tmp401 does not support n-factor correction\n");