]> exis.tech > repos - linux.git/commitdiff
Merge tag 'hwmon-for-v7.2-rc5' of git://git.kernel.org/pub/scm/linux/kernel/git/groec... master
authorLinus Torvalds <torvalds@linux-foundation.org>
Tue, 21 Jul 2026 15:34:39 +0000 (08:34 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Tue, 21 Jul 2026 15:34:39 +0000 (08:34 -0700)
Pull hwmon fixes from Guenter Roeck:

 - asus-ec-sensors: Add missed handle for ENOMEM, fix EC read
   intervals, and fix looping over banks while reading from EC

 - occ: validate poll response sensor blocks

 - pmbus/max34440: Block unsupported VIN and IIN limit registers

 - nzxt-kraken3, nzxt-smart2: gigabyte_waterforce, corsair-cpro,
   corsair-psu: Stop device IO before calling hid_hw_stop

* tag 'hwmon-for-v7.2-rc5' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging:
  hwmon: occ: validate poll response sensor blocks
  hwmon: (asus-ec-sensors) add missed handle for ENOMEM
  hwmon: (asus-ec-sensors) fix EC read intervals
  hwmon: (asus-ec-sensors) fix looping over banks while reading from EC
  hwmon: (pmbus/max34440) block unsupported VIN and IIN limit registers
  hwmon: (nzxt-kraken3) Stop device IO before calling hid_hw_stop
  hwmon: (nzxt-smart2) Stop device IO before calling hid_hw_stop
  hwmon: (gigabyte_waterforce) Stop device IO before calling hid_hw_stop
  hwmon: (corsair-cpro) Stop device IO before calling hid_hw_stop
  hwmon: (corsair-psu) Stop device IO before calling hid_hw_stop

drivers/hwmon/asus-ec-sensors.c
drivers/hwmon/corsair-cpro.c
drivers/hwmon/corsair-psu.c
drivers/hwmon/gigabyte_waterforce.c
drivers/hwmon/nzxt-kraken3.c
drivers/hwmon/nzxt-smart2.c
drivers/hwmon/occ/common.c
drivers/hwmon/pmbus/max34440.c

index 29a23484cbe7b290c1e5676a786fe142f414c75d..01a022b3ee826eabc6dd66868a7117dce179cbe6 100644 (file)
@@ -1009,7 +1009,7 @@ struct ec_sensors_data {
        /* sorted list of unique register banks */
        u8 banks[ASUS_EC_MAX_BANK + 1];
        /* in jiffies */
-       unsigned long last_updated;
+       u64 next_update;
        struct lock_data lock_data;
        /* number of board EC sensors */
        u8 nr_sensors;
@@ -1189,7 +1189,7 @@ static int asus_ec_block_read(const struct device *dev,
                }
                for (ireg = 0; ireg < ec->nr_registers; ireg++) {
                        reg_bank = register_bank(ec->registers[ireg]);
-                       if (reg_bank < bank) {
+                       if (reg_bank != bank) {
                                continue;
                        }
                        ec_read(register_index(ec->registers[ireg]),
@@ -1278,13 +1278,12 @@ static int get_cached_value_or_update(const struct device *dev,
                                      int sensor_index,
                                      struct ec_sensors_data *state, s32 *value)
 {
-       if (time_after(jiffies, state->last_updated + HZ)) {
+       if (time_after64(get_jiffies_64(), state->next_update)) {
                if (update_ec_sensors(dev, state)) {
                        dev_err(dev, "update_ec_sensors() failure\n");
                        return -EIO;
                }
-
-               state->last_updated = jiffies;
+               state->next_update = get_jiffies_64() + HZ;
        }
 
        *value = state->sensors[sensor_index].cached_value;
@@ -1402,6 +1401,7 @@ static int asus_ec_probe(struct platform_device *pdev)
        if (!ec_data)
                return -ENOMEM;
 
+       ec_data->next_update = INITIAL_JIFFIES;
        dev_set_drvdata(dev, ec_data);
        ec_data->board_info = pboard_info;
 
@@ -1495,9 +1495,11 @@ static int asus_ec_probe(struct platform_device *pdev)
                if (!nr_count[type])
                        continue;
 
-               asus_ec_hwmon_add_chan_info(asus_ec_hwmon_chan, dev,
-                                            nr_count[type], type,
-                                            hwmon_attributes[type]);
+               status = asus_ec_hwmon_add_chan_info(asus_ec_hwmon_chan, dev,
+                                                    nr_count[type], type,
+                                                    hwmon_attributes[type]);
+               if (status)
+                       return status;
                *ptr_asus_ec_ci++ = asus_ec_hwmon_chan++;
        }
 
index b6e508e43fa17f8c2d3b9360f4294f6d282c0d1b..8354a002f4c5e13223826f6bf7924e249899870f 100644 (file)
@@ -645,6 +645,7 @@ static int ccp_probe(struct hid_device *hdev, const struct hid_device_id *id)
 
 out_hw_close:
        hid_hw_close(hdev);
+       hid_device_io_stop(hdev);
 out_hw_stop:
        hid_hw_stop(hdev);
        return ret;
index 76f3e1da68d09e94811b62eb9887fcd21ee2cd5a..ce958cdaef58a3062062678fbc97c52e9007e8ca 100644 (file)
@@ -822,6 +822,7 @@ static int corsairpsu_probe(struct hid_device *hdev, const struct hid_device_id
 
 fail_and_close:
        hid_hw_close(hdev);
+       hid_device_io_stop(hdev);
 fail_and_stop:
        hid_hw_stop(hdev);
        return ret;
index 27487e215bddff5a4110464868346910a7768b55..4eea05f8b569c2344804bd9d542550169ad86726 100644 (file)
@@ -371,13 +371,15 @@ static int waterforce_probe(struct hid_device *hdev, const struct hid_device_id
        if (IS_ERR(priv->hwmon_dev)) {
                ret = PTR_ERR(priv->hwmon_dev);
                hid_err(hdev, "hwmon registration failed with %d\n", ret);
-               goto fail_and_close;
+               goto fail_and_io_stop;
        }
 
        waterforce_debugfs_init(priv);
 
        return 0;
 
+fail_and_io_stop:
+       hid_device_io_stop(hdev);
 fail_and_close:
        hid_hw_close(hdev);
 fail_and_stop:
index d00409bcab93ad5d0e3d7889e47684420f739772..05525406c5fbb58b6ae532e2801ce2efb6164ee0 100644 (file)
@@ -948,7 +948,7 @@ static int kraken3_probe(struct hid_device *hdev, const struct hid_device_id *id
        ret = kraken3_init_device(hdev);
        if (ret < 0) {
                hid_err(hdev, "device init failed with %d\n", ret);
-               goto fail_and_close;
+               goto fail_and_stop_io;
        }
 
        ret = kraken3_get_fw_ver(hdev);
@@ -960,13 +960,15 @@ static int kraken3_probe(struct hid_device *hdev, const struct hid_device_id *id
        if (IS_ERR(priv->hwmon_dev)) {
                ret = PTR_ERR(priv->hwmon_dev);
                hid_err(hdev, "hwmon registration failed with %d\n", ret);
-               goto fail_and_close;
+               goto fail_and_stop_io;
        }
 
        kraken3_debugfs_init(priv, device_name);
 
        return 0;
 
+fail_and_stop_io:
+       hid_device_io_stop(hdev);
 fail_and_close:
        hid_hw_close(hdev);
 fail_and_stop:
index 58ef9fa0184be4718afd08fdaacd6bdd1463dd24..e2316c46629d61fa8f5511ce1c83e73a946a9667 100644 (file)
@@ -768,7 +768,7 @@ static int nzxt_smart2_hid_probe(struct hid_device *hdev,
 
 out_hw_close:
        hid_hw_close(hdev);
-
+       hid_device_io_stop(hdev);
 out_hw_stop:
        hid_hw_stop(hdev);
        return ret;
index e18e80e832fd3f119a5297d343d97f472e850e5d..175208d712b06e3778bf8d28edf8c879b9cff603 100644 (file)
@@ -1052,32 +1052,49 @@ static int occ_setup_sensor_attrs(struct occ *occ)
 }
 
 /* only need to do this once at startup, as OCC won't change sensors on us */
-static void occ_parse_poll_response(struct occ *occ)
+static int occ_parse_poll_response(struct occ *occ)
 {
        unsigned int i, old_offset, offset = 0, size = 0;
+       u16 data_length;
        struct occ_sensor *sensor;
-       struct occ_sensors *sensors = &occ->sensors;
+       struct occ_sensors parsed = {};
+       struct occ_sensors *sensors = &parsed;
        struct occ_response *resp = &occ->resp;
        struct occ_poll_response *poll =
                (struct occ_poll_response *)&resp->data[0];
        struct occ_poll_response_header *header = &poll->header;
        struct occ_sensor_data_block *block = &poll->block;
 
+       data_length = get_unaligned_be16(&resp->data_length);
+       if (data_length < sizeof(*header) || data_length > OCC_RESP_DATA_BYTES) {
+               dev_err(occ->bus_dev, "invalid OCC poll response length %u\n",
+                       data_length);
+               return -EMSGSIZE;
+       }
+
        dev_info(occ->bus_dev, "OCC found, code level: %.16s\n",
                 header->occ_code_level);
 
        for (i = 0; i < header->num_sensor_data_blocks; ++i) {
                block = (struct occ_sensor_data_block *)((u8 *)block + offset);
+               if (size + sizeof(*header) + sizeof(block->header) >
+                   data_length) {
+                       dev_err(occ->bus_dev,
+                               "truncated OCC sensor block header\n");
+                       return -EMSGSIZE;
+               }
+
                old_offset = offset;
                offset = (block->header.num_sensors *
                          block->header.sensor_length) + sizeof(block->header);
-               size += offset;
 
                /* validate all the length/size fields */
-               if ((size + sizeof(*header)) >= OCC_RESP_DATA_BYTES) {
-                       dev_warn(occ->bus_dev, "exceeded response buffer\n");
-                       return;
+               if (size + sizeof(*header) + offset > data_length) {
+                       dev_err(occ->bus_dev,
+                               "exceeded OCC poll response length\n");
+                       return -EMSGSIZE;
                }
+               size += offset;
 
                dev_dbg(occ->bus_dev, " %04x..%04x: %.4s (%d sensors)\n",
                        old_offset, offset - 1, block->header.eye_catcher,
@@ -1107,6 +1124,9 @@ static void occ_parse_poll_response(struct occ *occ)
 
        dev_dbg(occ->bus_dev, "Max resp size: %u+%zd=%zd\n", size,
                sizeof(*header), size + sizeof(*header));
+       occ->sensors = parsed;
+
+       return 0;
 }
 
 int occ_active(struct occ *occ, bool active)
@@ -1138,10 +1158,12 @@ int occ_active(struct occ *occ, bool active)
                        goto unlock;
                }
 
-               occ->active = true;
                occ->next_update = jiffies + OCC_UPDATE_FREQUENCY;
-               occ_parse_poll_response(occ);
+               rc = occ_parse_poll_response(occ);
+               if (rc)
+                       goto unlock;
 
+               occ->active = true;
                rc = occ_setup_sensor_attrs(occ);
                if (rc) {
                        dev_err(occ->bus_dev,
index 74876d2207fbe4014b8b54a9fd9682370fc3bbed..e56057e9273c1639bddbaf4e665ea159f1d7c3ed 100644 (file)
@@ -88,6 +88,33 @@ static int max34440_read_word_data(struct i2c_client *client, int page,
                ret = pmbus_read_word_data(client, page, phase,
                                           data->iout_oc_warn_limit);
                break;
+       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_MFR_VIN_MIN:
+       case PMBUS_MFR_VIN_MAX:
+       case PMBUS_IIN_OC_WARN_LIMIT:
+       case PMBUS_IIN_OC_FAULT_LIMIT:
+       case PMBUS_MFR_IIN_MAX:
+       case PMBUS_MFR_VOUT_MIN:
+       case PMBUS_MFR_VOUT_MAX:
+       case PMBUS_IOUT_UC_FAULT_LIMIT:
+       case PMBUS_MFR_IOUT_MAX:
+       case PMBUS_UT_WARN_LIMIT:
+       case PMBUS_UT_FAULT_LIMIT:
+       case PMBUS_MFR_MAX_TEMP_1:
+               /*
+                * MAX34451/ADPM family do not support VIN/IIN limit registers,
+                * manufacturer-specific min/max registers, or undercurrent/
+                * undertemperature fault limits. Accessing these triggers CML
+                * error and asserts ALERT.
+                */
+               if (data->id == max34451 || data->id == adpm12160 ||
+                   data->id == adpm12200 || data->id == adpm12250)
+                       return -ENXIO;
+               ret = -ENODATA;
+               break;
        case PMBUS_VIRT_READ_VOUT_MIN:
                ret = pmbus_read_word_data(client, page, phase,
                                           MAX34440_MFR_VOUT_MIN);
@@ -244,6 +271,51 @@ static int max34440_read_byte_data(struct i2c_client *client, int page, int reg)
        return ret;
 }
 
+static int max34451_read_byte_data(struct i2c_client *client, int page, int reg)
+{
+       const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
+       const struct max34440_data *data = to_max34440_data(info);
+
+       switch (reg) {
+       case PMBUS_STATUS_BYTE:
+       case PMBUS_STATUS_OTHER:
+               /*
+                * MAX34451/ADPM family do not support STATUS_BYTE or
+                * STATUS_OTHER registers. Accessing them triggers CML
+                * error and asserts ALERT.
+                */
+               if (data->id == max34451 || data->id == adpm12160 ||
+                   data->id == adpm12200 || data->id == adpm12250)
+                       return -ENXIO;
+               return -ENODATA;
+       default:
+               return -ENODATA;
+       }
+}
+
+static int max34451_write_byte_data(struct i2c_client *client, int page,
+                                   int reg, u8 byte)
+{
+       const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
+       const struct max34440_data *data = to_max34440_data(info);
+
+       switch (reg) {
+       case PMBUS_STATUS_BYTE:
+       case PMBUS_STATUS_OTHER:
+               /*
+                * MAX34451/ADPM family do not support STATUS_BYTE or
+                * STATUS_OTHER registers. Writing to them triggers CML
+                * error and asserts ALERT.
+                */
+               if (data->id == max34451 || data->id == adpm12160 ||
+                   data->id == adpm12200 || data->id == adpm12250)
+                       return -ENXIO;
+               return -ENODATA;
+       default:
+               return -ENODATA;
+       }
+}
+
 static int max34451_set_supported_funcs(struct i2c_client *client,
                                         struct max34440_data *data)
 {
@@ -363,7 +435,9 @@ static struct pmbus_driver_info max34440_info[] = {
                .func[9] = PMBUS_HAVE_VIN | PMBUS_HAVE_STATUS_INPUT,
                .func[10] = PMBUS_HAVE_IIN | PMBUS_HAVE_STATUS_INPUT,
                .func[18] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP,
+               .read_byte_data = max34451_read_byte_data,
                .read_word_data = max34440_read_word_data,
+               .write_byte_data = max34451_write_byte_data,
                .write_word_data = max34440_write_word_data,
        },
        [adpm12200] = {
@@ -399,7 +473,9 @@ static struct pmbus_driver_info max34440_info[] = {
                .func[10] = PMBUS_HAVE_IIN | PMBUS_HAVE_STATUS_INPUT,
                .func[14] = PMBUS_HAVE_IOUT,
                .func[18] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP,
+               .read_byte_data = max34451_read_byte_data,
                .read_word_data = max34440_read_word_data,
+               .write_byte_data = max34451_write_byte_data,
                .write_word_data = max34440_write_word_data,
        },
        [adpm12250] = {
@@ -433,7 +509,9 @@ static struct pmbus_driver_info max34440_info[] = {
                .func[10] = PMBUS_HAVE_IIN | PMBUS_HAVE_STATUS_INPUT,
                .func[14] = PMBUS_HAVE_IOUT,
                .func[18] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP,
+               .read_byte_data = max34451_read_byte_data,
                .read_word_data = max34440_read_word_data,
+               .write_byte_data = max34451_write_byte_data,
                .write_word_data = max34440_write_word_data,
        },
        [max34440] = {
@@ -581,7 +659,9 @@ static struct pmbus_driver_info max34440_info[] = {
                .func[18] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP,
                .func[19] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP,
                .func[20] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP,
+               .read_byte_data = max34451_read_byte_data,
                .read_word_data = max34440_read_word_data,
+               .write_byte_data = max34451_write_byte_data,
                .write_word_data = max34440_write_word_data,
                .page_change_delay = MAX34440_PAGE_CHANGE_DELAY,
        },