* [PATCH v3 01/11] hwmon: chipcap2: fix command error diagnostics
2026-10-14 17:33 [PATCH v3 00/11] hwmon: chipcap2: various bug fixes Javier Carrasco
@ 2026-10-14 17:34 ` Javier Carrasco
2026-10-14 17:34 ` [PATCH v3 02/11] hwmon: chipcap2: fix off-by-one in command mode retry loop Javier Carrasco
` (9 subsequent siblings)
10 siblings, 0 replies; 12+ messages in thread
From: Javier Carrasco @ 2026-10-14 17:34 UTC (permalink / raw)
To: Guenter Roeck; +Cc: linux-hwmon, linux-kernel, Javier Carrasco
The command response field only contains bits 1:0, while the
error flags are stored in bits 2:5. The existing diagnostic
checks test the extracted response field, so they can never report
an error flag.
Test the full status byte for the error flags, and check the
configuration-load flag instead of RAM parity for the last diagnostic.
Fixes: 3af350929e75 ("hwmon: Add support for Amphenol ChipCap 2")
Signed-off-by: Javier Carrasco <javier.carrasco.cruz@gmail.com>
---
drivers/hwmon/chipcap2.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/drivers/hwmon/chipcap2.c b/drivers/hwmon/chipcap2.c
index 9bef767b589e..855057892852 100644
--- a/drivers/hwmon/chipcap2.c
+++ b/drivers/hwmon/chipcap2.c
@@ -167,13 +167,13 @@ static int cc2_cmd_response_diagnostic(struct device *dev, u8 status)
case CC2_RESPONSE_BUSY:
return -EBUSY;
case CC2_RESPONSE_NACK:
- if (resp & CC2_ERR_CORR_EEPROM)
+ if (status & CC2_ERR_CORR_EEPROM)
dev_dbg(dev, "Command failed: corrected EEPROM\n");
- if (resp & CC2_ERR_UNCORR_EEPROM)
+ if (status & CC2_ERR_UNCORR_EEPROM)
dev_dbg(dev, "Command failed: uncorrected EEPROM\n");
- if (resp & CC2_ERR_RAM_PARITY)
+ if (status & CC2_ERR_RAM_PARITY)
dev_dbg(dev, "Command failed: RAM parity\n");
- if (resp & CC2_ERR_RAM_PARITY)
+ if (status & CC2_ERR_CONFIG_LOAD)
dev_dbg(dev, "Command failed: configuration error\n");
return -ENODATA;
default:
--
2.43.0
^ permalink raw reply [flat|nested] 12+ messages in thread* [PATCH v3 02/11] hwmon: chipcap2: fix off-by-one in command mode retry loop
2026-10-14 17:33 [PATCH v3 00/11] hwmon: chipcap2: various bug fixes Javier Carrasco
2026-10-14 17:34 ` [PATCH v3 01/11] hwmon: chipcap2: fix command error diagnostics Javier Carrasco
@ 2026-10-14 17:34 ` Javier Carrasco
2026-10-14 17:34 ` [PATCH v3 03/11] hwmon: chipcap2: fix be16 handling Javier Carrasco
` (8 subsequent siblings)
10 siblings, 0 replies; 12+ messages in thread
From: Javier Carrasco @ 2026-10-14 17:34 UTC (permalink / raw)
To: Guenter Roeck; +Cc: linux-hwmon, linux-kernel, Javier Carrasco
i == CC2_CM_RETRIES can never be true inside the loop, since it
only runs while i < CC2_CM_RETRIES. Use CC2_CM_RETRIES - 1 so the
last failed retry breaks out immediately instead of doing a useless
disable + 80 ms power-cycle delay before the loop condition ends it
anyway.
Fixes: 3af350929e75 ("hwmon: Add support for Amphenol ChipCap 2")
Signed-off-by: Javier Carrasco <javier.carrasco.cruz@gmail.com>
---
drivers/hwmon/chipcap2.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/hwmon/chipcap2.c b/drivers/hwmon/chipcap2.c
index 855057892852..8102b6851d3d 100644
--- a/drivers/hwmon/chipcap2.c
+++ b/drivers/hwmon/chipcap2.c
@@ -226,7 +226,7 @@ static int cc2_command_mode_start(struct cc2_data *data)
2 * CC2_RESP_START_CM_US);
}
ret = cc2_read_command_status(data->client);
- if (ret != -ETIMEDOUT || i == CC2_CM_RETRIES)
+ if (ret != -ETIMEDOUT || i == CC2_CM_RETRIES - 1)
break;
/* command window missed, prepare for a retry */
--
2.43.0
^ permalink raw reply [flat|nested] 12+ messages in thread* [PATCH v3 03/11] hwmon: chipcap2: fix be16 handling
2026-10-14 17:33 [PATCH v3 00/11] hwmon: chipcap2: various bug fixes Javier Carrasco
2026-10-14 17:34 ` [PATCH v3 01/11] hwmon: chipcap2: fix command error diagnostics Javier Carrasco
2026-10-14 17:34 ` [PATCH v3 02/11] hwmon: chipcap2: fix off-by-one in command mode retry loop Javier Carrasco
@ 2026-10-14 17:34 ` Javier Carrasco
2026-10-14 17:34 ` [PATCH v3 04/11] hwmon: chipcap2: defer probe when IRQs are unavailable Javier Carrasco
` (7 subsequent siblings)
10 siblings, 0 replies; 12+ messages in thread
From: Javier Carrasco @ 2026-10-14 17:34 UTC (permalink / raw)
To: Guenter Roeck; +Cc: linux-hwmon, linux-kernel, Javier Carrasco, stable
The driver currently only works correctly on little-endian
architectures when writing 16-bit register values.
Use i2c_smbus_write_word_swapped() for writes and get_unaligned_be16()
for reads to correctly handle the ChipCap 2's big-endian data format
on all architectures.
Fixes: 3af350929e75 ("hwmon: Add support for Amphenol ChipCap 2")
Cc: stable@vger.kernel.org
Signed-off-by: Javier Carrasco <javier.carrasco.cruz@gmail.com>
---
drivers/hwmon/chipcap2.c | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/drivers/hwmon/chipcap2.c b/drivers/hwmon/chipcap2.c
index 8102b6851d3d..aec4adf98658 100644
--- a/drivers/hwmon/chipcap2.c
+++ b/drivers/hwmon/chipcap2.c
@@ -21,6 +21,7 @@
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/module.h>
+#include <linux/unaligned.h>
#include <linux/regulator/consumer.h>
#include <linux/mod_devicetable.h>
#include <linux/property.h>
@@ -260,8 +261,7 @@ static int cc2_write_reg(struct cc2_data *data, u8 reg, u16 val)
if (ret < 0)
goto disable;
- cpu_to_be16s(&val);
- ret = i2c_smbus_write_word_data(data->client, reg, val);
+ ret = i2c_smbus_write_word_swapped(data->client, reg, val);
if (ret < 0)
goto disable;
@@ -311,7 +311,7 @@ static int cc2_read_reg(struct cc2_data *data, u8 reg, u16 *val)
if (ret != CC2_EEPROM_DATA_LEN)
return ret < 0 ? ret : -EIO;
- *val = be16_to_cpup((__be16 *)&buf[1]);
+ *val = get_unaligned_be16(&buf[1]);
return cc2_read_command_status(data->client);
}
@@ -351,10 +351,10 @@ static int cc2_data_fetch(struct i2c_client *client,
switch (type) {
case hwmon_humidity:
- *val = cc2_rh_convert(be16_to_cpup((__be16 *)&data[0]));
+ *val = cc2_rh_convert(get_unaligned_be16(&data[0]));
break;
case hwmon_temp:
- *val = cc2_temp_convert(be16_to_cpup((__be16 *)&data[2]));
+ *val = cc2_temp_convert(get_unaligned_be16(&data[2]));
break;
default:
return -EINVAL;
--
2.43.0
^ permalink raw reply [flat|nested] 12+ messages in thread* [PATCH v3 04/11] hwmon: chipcap2: defer probe when IRQs are unavailable
2026-10-14 17:33 [PATCH v3 00/11] hwmon: chipcap2: various bug fixes Javier Carrasco
` (2 preceding siblings ...)
2026-10-14 17:34 ` [PATCH v3 03/11] hwmon: chipcap2: fix be16 handling Javier Carrasco
@ 2026-10-14 17:34 ` Javier Carrasco
2026-10-14 17:34 ` [PATCH v3 05/11] hwmon: chipcap2: fix IRQ teardown ordering Javier Carrasco
` (6 subsequent siblings)
10 siblings, 0 replies; 12+ messages in thread
From: Javier Carrasco @ 2026-10-14 17:34 UTC (permalink / raw)
To: Guenter Roeck; +Cc: linux-hwmon, linux-kernel, Javier Carrasco
This device can work with or without interrupts, but if they
are provided and still not available, but if -EPROBE_DEFER is read,
its value must be propagated to enable probe retries.
Fixes: 3af350929e75 ("hwmon: Add support for Amphenol ChipCap 2")
Signed-off-by: Javier Carrasco <javier.carrasco.cruz@gmail.com>
---
drivers/hwmon/chipcap2.c | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/drivers/hwmon/chipcap2.c b/drivers/hwmon/chipcap2.c
index aec4adf98658..37dbf6a76fbc 100644
--- a/drivers/hwmon/chipcap2.c
+++ b/drivers/hwmon/chipcap2.c
@@ -636,6 +636,8 @@ static int cc2_request_ready_irq(struct cc2_data *data, struct device *dev)
int ret = 0;
data->irq_ready = fwnode_irq_get_byname(dev_fwnode(dev), "ready");
+ if (data->irq_ready == -EPROBE_DEFER)
+ return data->irq_ready;
if (data->irq_ready > 0) {
init_completion(&data->complete);
ret = devm_request_threaded_irq(dev, data->irq_ready, NULL,
@@ -653,6 +655,8 @@ static int cc2_request_alarm_irqs(struct cc2_data *data, struct device *dev)
int ret = 0;
data->irq_low = fwnode_irq_get_byname(dev_fwnode(dev), "low");
+ if (data->irq_low == -EPROBE_DEFER)
+ return data->irq_low;
if (data->irq_low > 0) {
ret = devm_request_threaded_irq(dev, data->irq_low, NULL,
cc2_low_interrupt,
@@ -666,6 +670,8 @@ static int cc2_request_alarm_irqs(struct cc2_data *data, struct device *dev)
}
data->irq_high = fwnode_irq_get_byname(dev_fwnode(dev), "high");
+ if (data->irq_high == -EPROBE_DEFER)
+ return data->irq_high;
if (data->irq_high > 0) {
ret = devm_request_threaded_irq(dev, data->irq_high, NULL,
cc2_high_interrupt,
--
2.43.0
^ permalink raw reply [flat|nested] 12+ messages in thread* [PATCH v3 05/11] hwmon: chipcap2: fix IRQ teardown ordering
2026-10-14 17:33 [PATCH v3 00/11] hwmon: chipcap2: various bug fixes Javier Carrasco
` (3 preceding siblings ...)
2026-10-14 17:34 ` [PATCH v3 04/11] hwmon: chipcap2: defer probe when IRQs are unavailable Javier Carrasco
@ 2026-10-14 17:34 ` Javier Carrasco
2026-10-14 17:34 ` [PATCH v3 06/11] hwmon: chipcap2: disable device after hwmon teardown Javier Carrasco
` (5 subsequent siblings)
10 siblings, 0 replies; 12+ messages in thread
From: Javier Carrasco @ 2026-10-14 17:34 UTC (permalink / raw)
To: Guenter Roeck; +Cc: linux-hwmon, linux-kernel, Javier Carrasco, stable
The threaded IRQs are currently requested before the hwmon device
is registered. Since devres releases resources in reverse order,
the hwmon device is unregistered before the IRQs are freed during
driver teardown. An IRQ may therefore still run after the hwmon
device has been released and access the stale data->hwmon pointer.
Separate IRQ discovery from IRQ registration so that the IRQ numbers
and alarm visibility flags are initialized before registering the
hwmon device while the IRQs are released before the hwmon device
during devres cleanup.
Fixes: 3af350929e75 ("hwmon: Add support for Amphenol ChipCap 2")
Cc: stable@vger.kernel.org
Signed-off-by: Javier Carrasco <javier.carrasco.cruz@gmail.com>
---
drivers/hwmon/chipcap2.c | 52 +++++++++++++++++++++++++++++-------------------
1 file changed, 32 insertions(+), 20 deletions(-)
diff --git a/drivers/hwmon/chipcap2.c b/drivers/hwmon/chipcap2.c
index 37dbf6a76fbc..21dc6db27c40 100644
--- a/drivers/hwmon/chipcap2.c
+++ b/drivers/hwmon/chipcap2.c
@@ -631,32 +631,47 @@ static int cc2_write(struct device *dev, enum hwmon_sensor_types type, u32 attr,
}
}
-static int cc2_request_ready_irq(struct cc2_data *data, struct device *dev)
+static int cc2_get_irqs(struct cc2_data *data, struct device *dev)
{
- int ret = 0;
-
data->irq_ready = fwnode_irq_get_byname(dev_fwnode(dev), "ready");
if (data->irq_ready == -EPROBE_DEFER)
return data->irq_ready;
- if (data->irq_ready > 0) {
+ if (data->irq_ready > 0)
init_completion(&data->complete);
+
+ data->irq_low = fwnode_irq_get_byname(dev_fwnode(dev), "low");
+ if (data->irq_low == -EPROBE_DEFER)
+ return data->irq_low;
+ if (data->irq_low > 0)
+ data->rh_alarm.low_alarm_visible = true;
+
+ data->irq_high = fwnode_irq_get_byname(dev_fwnode(dev), "high");
+ if (data->irq_high == -EPROBE_DEFER)
+ return data->irq_high;
+ if (data->irq_high > 0)
+ data->rh_alarm.high_alarm_visible = true;
+
+ return 0;
+}
+
+static int cc2_request_data_ready(struct cc2_data *data, struct device *dev)
+{
+ int ret = 0;
+
+ if (data->irq_ready > 0)
ret = devm_request_threaded_irq(dev, data->irq_ready, NULL,
cc2_ready_interrupt,
IRQF_ONESHOT |
IRQF_TRIGGER_RISING,
dev_name(dev), data);
- }
return ret;
}
-static int cc2_request_alarm_irqs(struct cc2_data *data, struct device *dev)
+static int cc2_request_alarms(struct cc2_data *data, struct device *dev)
{
int ret = 0;
- data->irq_low = fwnode_irq_get_byname(dev_fwnode(dev), "low");
- if (data->irq_low == -EPROBE_DEFER)
- return data->irq_low;
if (data->irq_low > 0) {
ret = devm_request_threaded_irq(dev, data->irq_low, NULL,
cc2_low_interrupt,
@@ -665,13 +680,8 @@ static int cc2_request_alarm_irqs(struct cc2_data *data, struct device *dev)
dev_name(dev), data);
if (ret)
return ret;
-
- data->rh_alarm.low_alarm_visible = true;
}
- data->irq_high = fwnode_irq_get_byname(dev_fwnode(dev), "high");
- if (data->irq_high == -EPROBE_DEFER)
- return data->irq_high;
if (data->irq_high > 0) {
ret = devm_request_threaded_irq(dev, data->irq_high, NULL,
cc2_high_interrupt,
@@ -680,8 +690,6 @@ static int cc2_request_alarm_irqs(struct cc2_data *data, struct device *dev)
dev_name(dev), data);
if (ret)
return ret;
-
- data->rh_alarm.high_alarm_visible = true;
}
return ret;
@@ -732,13 +740,13 @@ static int cc2_probe(struct i2c_client *client)
device_property_read_string(dev, "label", &data->label);
- ret = cc2_request_ready_irq(data, dev);
+ ret = cc2_get_irqs(data, dev);
if (ret)
- return dev_err_probe(dev, ret, "Failed to request ready irq\n");
+ return dev_err_probe(dev, ret, "Failed to get IRQs\n");
- ret = cc2_request_alarm_irqs(data, dev);
+ ret = cc2_request_data_ready(data, dev);
if (ret)
- return dev_err_probe(dev, ret, "Failed to request alarm irqs\n");
+ return dev_err_probe(dev, ret, "Failed to request ready irq\n");
data->hwmon = devm_hwmon_device_register_with_info(dev, client->name,
data, &cc2_chip_info,
@@ -747,6 +755,10 @@ static int cc2_probe(struct i2c_client *client)
return dev_err_probe(dev, PTR_ERR(data->hwmon),
"Failed to register hwmon device\n");
+ ret = cc2_request_alarms(data, dev);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to request alarm irqs\n");
+
return 0;
}
--
2.43.0
^ permalink raw reply [flat|nested] 12+ messages in thread* [PATCH v3 06/11] hwmon: chipcap2: disable device after hwmon teardown
2026-10-14 17:33 [PATCH v3 00/11] hwmon: chipcap2: various bug fixes Javier Carrasco
` (4 preceding siblings ...)
2026-10-14 17:34 ` [PATCH v3 05/11] hwmon: chipcap2: fix IRQ teardown ordering Javier Carrasco
@ 2026-10-14 17:34 ` Javier Carrasco
2026-10-14 17:34 ` [PATCH v3 07/11] hwmon: chipcap2: enable IRQ processing when regulator is already enabled Javier Carrasco
` (4 subsequent siblings)
10 siblings, 0 replies; 12+ messages in thread
From: Javier Carrasco @ 2026-10-14 17:34 UTC (permalink / raw)
To: Guenter Roeck; +Cc: linux-hwmon, linux-kernel, Javier Carrasco, stable
cc2_remove() disables the device while the device-managed hwmon device is
still registered. A concurrent sysfs callback can then enable the device
after it has been disabled during removal.
Register a device-managed disable action before registering the hwmon
device, so devres unregisters the hwmon device before disabling the
physical device.
Fixes: 3af350929e75 ("hwmon: Add support for Amphenol ChipCap 2")
Cc: stable@vger.kernel.org
Signed-off-by: Javier Carrasco <javier.carrasco.cruz@gmail.com>
---
drivers/hwmon/chipcap2.c | 17 +++++++++--------
1 file changed, 9 insertions(+), 8 deletions(-)
diff --git a/drivers/hwmon/chipcap2.c b/drivers/hwmon/chipcap2.c
index 21dc6db27c40..654f6420da7a 100644
--- a/drivers/hwmon/chipcap2.c
+++ b/drivers/hwmon/chipcap2.c
@@ -152,6 +152,11 @@ static void cc2_disable(struct cc2_data *data)
}
}
+static void cc2_disable_action(void *data)
+{
+ cc2_disable(data);
+}
+
static int cc2_cmd_response_diagnostic(struct device *dev, u8 status)
{
int resp;
@@ -744,6 +749,10 @@ static int cc2_probe(struct i2c_client *client)
if (ret)
return dev_err_probe(dev, ret, "Failed to get IRQs\n");
+ ret = devm_add_action_or_reset(dev, cc2_disable_action, data);
+ if (ret)
+ return ret;
+
ret = cc2_request_data_ready(data, dev);
if (ret)
return dev_err_probe(dev, ret, "Failed to request ready irq\n");
@@ -762,13 +771,6 @@ static int cc2_probe(struct i2c_client *client)
return 0;
}
-static void cc2_remove(struct i2c_client *client)
-{
- struct cc2_data *data = i2c_get_clientdata(client);
-
- cc2_disable(data);
-}
-
static const struct i2c_device_id cc2_id[] = {
{ .name = "cc2d23" },
{ .name = "cc2d23s" },
@@ -801,7 +803,6 @@ static struct i2c_driver cc2_driver = {
.of_match_table = cc2_of_match,
},
.probe = cc2_probe,
- .remove = cc2_remove,
.id_table = cc2_id,
};
module_i2c_driver(cc2_driver);
--
2.43.0
^ permalink raw reply [flat|nested] 12+ messages in thread* [PATCH v3 07/11] hwmon: chipcap2: enable IRQ processing when regulator is already enabled
2026-10-14 17:33 [PATCH v3 00/11] hwmon: chipcap2: various bug fixes Javier Carrasco
` (5 preceding siblings ...)
2026-10-14 17:34 ` [PATCH v3 06/11] hwmon: chipcap2: disable device after hwmon teardown Javier Carrasco
@ 2026-10-14 17:34 ` Javier Carrasco
2026-10-14 17:34 ` [PATCH v3 08/11] hwmon: chipcap2: balance regulator state Javier Carrasco
` (3 subsequent siblings)
10 siblings, 0 replies; 12+ messages in thread
From: Javier Carrasco @ 2026-10-14 17:34 UTC (permalink / raw)
To: Guenter Roeck; +Cc: linux-hwmon, linux-kernel, Javier Carrasco, stable
cc2_enable() returns early when the regulator is already enabled.
This can happen if a previous regulator_disable() failed or if the
regulator was configured as always-on.
In this case, process_irqs remains false, causing the ready interrupt
to be ignored. Subsequent operations waiting for the ready completion
then time out.
Set process_irqs before returning when the regulator is already
enabled, so that the driver continues to process ready interrupts.
Fixes: 3af350929e75 ("hwmon: Add support for Amphenol ChipCap 2")
Cc: stable@vger.kernel.org
Signed-off-by: Javier Carrasco <javier.carrasco.cruz@gmail.com>
---
drivers/hwmon/chipcap2.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/drivers/hwmon/chipcap2.c b/drivers/hwmon/chipcap2.c
index 654f6420da7a..837b1830f2e4 100644
--- a/drivers/hwmon/chipcap2.c
+++ b/drivers/hwmon/chipcap2.c
@@ -120,8 +120,10 @@ static int cc2_enable(struct cc2_data *data)
int ret;
/* exclusive regulator, check in case a disable failed */
- if (regulator_is_enabled(data->regulator))
+ if (regulator_is_enabled(data->regulator)) {
+ data->process_irqs = true;
return 0;
+ }
/* clear any pending completion */
try_wait_for_completion(&data->complete);
--
2.43.0
^ permalink raw reply [flat|nested] 12+ messages in thread* [PATCH v3 08/11] hwmon: chipcap2: balance regulator state
2026-10-14 17:33 [PATCH v3 00/11] hwmon: chipcap2: various bug fixes Javier Carrasco
` (6 preceding siblings ...)
2026-10-14 17:34 ` [PATCH v3 07/11] hwmon: chipcap2: enable IRQ processing when regulator is already enabled Javier Carrasco
@ 2026-10-14 17:34 ` Javier Carrasco
2026-10-14 17:34 ` [PATCH v3 09/11] hwmon: chipcap2: do not discard pending alarms on disable Javier Carrasco
` (2 subsequent siblings)
10 siblings, 0 replies; 12+ messages in thread
From: Javier Carrasco @ 2026-10-14 17:34 UTC (permalink / raw)
To: Guenter Roeck; +Cc: linux-hwmon, linux-kernel, Javier Carrasco, stable
The current implementation does not guarantee that the calls to
regulator_enable() and regulator_disable() are balanced.
regulator_is_enabled() might return a negative value, and an always-on
attribute in the DT might break the symmetry anyway.
Add a regulator_enabled flag to keep balance within this regulator
consumer.
Fixes: 3af350929e75 ("hwmon: Add support for Amphenol ChipCap 2")
Cc: stable@vger.kernel.org
Signed-off-by: Javier Carrasco <javier.carrasco.cruz@gmail.com>
---
drivers/hwmon/chipcap2.c | 18 +++++++++++++-----
1 file changed, 13 insertions(+), 5 deletions(-)
diff --git a/drivers/hwmon/chipcap2.c b/drivers/hwmon/chipcap2.c
index 837b1830f2e4..952789fdb9db 100644
--- a/drivers/hwmon/chipcap2.c
+++ b/drivers/hwmon/chipcap2.c
@@ -91,6 +91,7 @@ struct cc2_data {
int irq_low;
int irq_high;
bool process_irqs;
+ bool regulator_enabled;
};
/* %RH as a per cent mille from a register value */
@@ -120,7 +121,7 @@ static int cc2_enable(struct cc2_data *data)
int ret;
/* exclusive regulator, check in case a disable failed */
- if (regulator_is_enabled(data->regulator)) {
+ if (data->regulator_enabled) {
data->process_irqs = true;
return 0;
}
@@ -132,6 +133,8 @@ static int cc2_enable(struct cc2_data *data)
if (ret < 0)
return ret;
+ data->regulator_enabled = true;
+
usleep_range(CC2_STARTUP_TIME_US, CC2_STARTUP_TIME_US + 125);
data->process_irqs = true;
@@ -147,11 +150,16 @@ static void cc2_disable(struct cc2_data *data)
data->process_irqs = false;
/* exclusive regulator, check in case an enable failed */
- if (regulator_is_enabled(data->regulator)) {
- err = regulator_disable(data->regulator);
- if (err)
- dev_dbg(&data->client->dev, "Failed to disable device");
+ if (!data->regulator_enabled)
+ return;
+
+ err = regulator_disable(data->regulator);
+ if (err) {
+ dev_dbg(&data->client->dev, "Failed to disable device");
+ return;
}
+
+ data->regulator_enabled = false;
}
static void cc2_disable_action(void *data)
--
2.43.0
^ permalink raw reply [flat|nested] 12+ messages in thread* [PATCH v3 09/11] hwmon: chipcap2: do not discard pending alarms on disable
2026-10-14 17:33 [PATCH v3 00/11] hwmon: chipcap2: various bug fixes Javier Carrasco
` (7 preceding siblings ...)
2026-10-14 17:34 ` [PATCH v3 08/11] hwmon: chipcap2: balance regulator state Javier Carrasco
@ 2026-10-14 17:34 ` Javier Carrasco
2026-10-14 17:34 ` [PATCH v3 10/11] hwmon: chipcap2: serialize access to the alarm indicators Javier Carrasco
2026-10-14 17:34 ` [PATCH v3 11/11] hwmon: chipcap2: synchronize ready IRQ state Javier Carrasco
10 siblings, 0 replies; 12+ messages in thread
From: Javier Carrasco @ 2026-10-14 17:34 UTC (permalink / raw)
To: Guenter Roeck; +Cc: linux-hwmon, linux-kernel, Javier Carrasco, stable
cc2_disable() stops processing interrupts before the device is powered
down to ignore the alarms caused by voltage toggling. The handler of an
alarm raised by the last measurement might not have run at that point,
and the alarm is discarded when it finally does.
The sysfs read that triggered the measurement can then report no alarm
although the device raised one.
Wait for the alarm handlers that have already been raised before the
interrupt processing is stopped. This covers the case where the handler
thread has not been scheduled yet; an alarm that has not reached the
interrupt core at that point is discarded.
Fixes: 3af350929e75 ("hwmon: Add support for Amphenol ChipCap 2")
Cc: stable@vger.kernel.org
Signed-off-by: Javier Carrasco <javier.carrasco.cruz@gmail.com>
---
drivers/hwmon/chipcap2.c | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/drivers/hwmon/chipcap2.c b/drivers/hwmon/chipcap2.c
index 952789fdb9db..eba6469940ab 100644
--- a/drivers/hwmon/chipcap2.c
+++ b/drivers/hwmon/chipcap2.c
@@ -146,6 +146,13 @@ static void cc2_disable(struct cc2_data *data)
{
int err;
+ /* let the alarms already raised by the last measurement be processed */
+ if (data->irq_low > 0)
+ synchronize_irq(data->irq_low);
+
+ if (data->irq_high > 0)
+ synchronize_irq(data->irq_high);
+
/* ignore alarms triggered by voltage toggling when powering up */
data->process_irqs = false;
--
2.43.0
^ permalink raw reply [flat|nested] 12+ messages in thread* [PATCH v3 10/11] hwmon: chipcap2: serialize access to the alarm indicators
2026-10-14 17:33 [PATCH v3 00/11] hwmon: chipcap2: various bug fixes Javier Carrasco
` (8 preceding siblings ...)
2026-10-14 17:34 ` [PATCH v3 09/11] hwmon: chipcap2: do not discard pending alarms on disable Javier Carrasco
@ 2026-10-14 17:34 ` Javier Carrasco
2026-10-14 17:34 ` [PATCH v3 11/11] hwmon: chipcap2: synchronize ready IRQ state Javier Carrasco
10 siblings, 0 replies; 12+ messages in thread
From: Javier Carrasco @ 2026-10-14 17:34 UTC (permalink / raw)
To: Guenter Roeck; +Cc: linux-hwmon, linux-kernel, Javier Carrasco, stable
low_alarm and high_alarm are accessed from the threaded interrupt
handlers and from sysfs without any locking, which can lead to lost
alarms.
Add a mutex to serialize the access to the alarm indicators and to
process_irqs, which the handlers use to decide whether an interrupt
belongs to an ongoing operation.
Fixes: 3af350929e75 ("hwmon: Add support for Amphenol ChipCap 2")
Cc: stable@vger.kernel.org
Signed-off-by: Javier Carrasco <javier.carrasco.cruz@gmail.com>
Assisted-by: opencode:claude-opus-5
---
drivers/hwmon/chipcap2.c | 58 ++++++++++++++++++++++++++++++------------------
1 file changed, 37 insertions(+), 21 deletions(-)
diff --git a/drivers/hwmon/chipcap2.c b/drivers/hwmon/chipcap2.c
index eba6469940ab..b3b725cc7224 100644
--- a/drivers/hwmon/chipcap2.c
+++ b/drivers/hwmon/chipcap2.c
@@ -20,6 +20,7 @@
#include <linux/i2c.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
+#include <linux/mutex.h>
#include <linux/module.h>
#include <linux/unaligned.h>
#include <linux/regulator/consumer.h>
@@ -91,6 +92,8 @@ struct cc2_data {
int irq_low;
int irq_high;
bool process_irqs;
+ /* protects process_irqs and the alarm indicators */
+ struct mutex irq_lock;
bool regulator_enabled;
};
@@ -122,7 +125,8 @@ static int cc2_enable(struct cc2_data *data)
/* exclusive regulator, check in case a disable failed */
if (data->regulator_enabled) {
- data->process_irqs = true;
+ scoped_guard(mutex, &data->irq_lock)
+ data->process_irqs = true;
return 0;
}
@@ -137,7 +141,8 @@ static int cc2_enable(struct cc2_data *data)
usleep_range(CC2_STARTUP_TIME_US, CC2_STARTUP_TIME_US + 125);
- data->process_irqs = true;
+ scoped_guard(mutex, &data->irq_lock)
+ data->process_irqs = true;
return 0;
}
@@ -154,7 +159,8 @@ static void cc2_disable(struct cc2_data *data)
synchronize_irq(data->irq_high);
/* ignore alarms triggered by voltage toggling when powering up */
- data->process_irqs = false;
+ scoped_guard(mutex, &data->irq_lock)
+ data->process_irqs = false;
/* exclusive regulator, check in case an enable failed */
if (!data->regulator_enabled)
@@ -514,10 +520,12 @@ static irqreturn_t cc2_low_interrupt(int irq, void *data)
{
struct cc2_data *cc2 = data;
- if (cc2->process_irqs) {
- hwmon_notify_event(cc2->hwmon, hwmon_humidity,
- hwmon_humidity_min_alarm, 0);
- cc2->rh_alarm.low_alarm = true;
+ scoped_guard(mutex, &cc2->irq_lock) {
+ if (cc2->process_irqs) {
+ hwmon_notify_event(cc2->hwmon, hwmon_humidity,
+ hwmon_humidity_min_alarm, 0);
+ cc2->rh_alarm.low_alarm = true;
+ }
}
return IRQ_HANDLED;
@@ -527,10 +535,12 @@ static irqreturn_t cc2_high_interrupt(int irq, void *data)
{
struct cc2_data *cc2 = data;
- if (cc2->process_irqs) {
- hwmon_notify_event(cc2->hwmon, hwmon_humidity,
- hwmon_humidity_max_alarm, 0);
- cc2->rh_alarm.high_alarm = true;
+ scoped_guard(mutex, &cc2->irq_lock) {
+ if (cc2->process_irqs) {
+ hwmon_notify_event(cc2->hwmon, hwmon_humidity,
+ hwmon_humidity_max_alarm, 0);
+ cc2->rh_alarm.high_alarm = true;
+ }
}
return IRQ_HANDLED;
@@ -546,11 +556,13 @@ static int cc2_humidity_min_alarm_status(struct cc2_data *data, long *val)
if (ret < 0)
return ret;
- if (data->rh_alarm.low_alarm) {
- *val = (measurement < min_hyst) ? 1 : 0;
- data->rh_alarm.low_alarm = *val;
- } else {
- *val = 0;
+ scoped_guard(mutex, &data->irq_lock) {
+ if (data->rh_alarm.low_alarm) {
+ *val = (measurement < min_hyst) ? 1 : 0;
+ data->rh_alarm.low_alarm = *val;
+ } else {
+ *val = 0;
+ }
}
return 0;
@@ -566,11 +578,13 @@ static int cc2_humidity_max_alarm_status(struct cc2_data *data, long *val)
if (ret < 0)
return ret;
- if (data->rh_alarm.high_alarm) {
- *val = (measurement > max_hyst) ? 1 : 0;
- data->rh_alarm.high_alarm = *val;
- } else {
- *val = 0;
+ scoped_guard(mutex, &data->irq_lock) {
+ if (data->rh_alarm.high_alarm) {
+ *val = (measurement > max_hyst) ? 1 : 0;
+ data->rh_alarm.high_alarm = *val;
+ } else {
+ *val = 0;
+ }
}
return 0;
@@ -753,6 +767,8 @@ static int cc2_probe(struct i2c_client *client)
i2c_set_clientdata(client, data);
+ mutex_init(&data->irq_lock);
+
data->client = client;
data->regulator = devm_regulator_get_exclusive(dev, "vdd");
--
2.43.0
^ permalink raw reply [flat|nested] 12+ messages in thread* [PATCH v3 11/11] hwmon: chipcap2: synchronize ready IRQ state
2026-10-14 17:33 [PATCH v3 00/11] hwmon: chipcap2: various bug fixes Javier Carrasco
` (9 preceding siblings ...)
2026-10-14 17:34 ` [PATCH v3 10/11] hwmon: chipcap2: serialize access to the alarm indicators Javier Carrasco
@ 2026-10-14 17:34 ` Javier Carrasco
10 siblings, 0 replies; 12+ messages in thread
From: Javier Carrasco @ 2026-10-14 17:34 UTC (permalink / raw)
To: Guenter Roeck; +Cc: linux-hwmon, linux-kernel, Javier Carrasco, stable
The ready IRQ handler can race with reinit_completion() in the enable
path. In particular, a handler from an earlier operation can complete
the completion after it is reset for a new operation.
Use irq_lock to serialize access to the completion and process_irqs.
Disable IRQ processing and synchronize any running handler before resetting
the completion for a new operation.
Fixes: 3af350929e75 ("hwmon: Add support for Amphenol ChipCap 2")
Cc: stable@vger.kernel.org
Signed-off-by: Javier Carrasco <javier.carrasco.cruz@gmail.com>
Assisted-by: opencode:claude-opus-5
---
drivers/hwmon/chipcap2.c | 22 +++++++++++++++++-----
1 file changed, 17 insertions(+), 5 deletions(-)
diff --git a/drivers/hwmon/chipcap2.c b/drivers/hwmon/chipcap2.c
index b3b725cc7224..00f62a33d463 100644
--- a/drivers/hwmon/chipcap2.c
+++ b/drivers/hwmon/chipcap2.c
@@ -123,6 +123,16 @@ static int cc2_enable(struct cc2_data *data)
{
int ret;
+ if (data->irq_ready > 0) {
+ scoped_guard(mutex, &data->irq_lock)
+ data->process_irqs = false;
+
+ synchronize_irq(data->irq_ready);
+
+ scoped_guard(mutex, &data->irq_lock)
+ reinit_completion(&data->complete);
+ }
+
/* exclusive regulator, check in case a disable failed */
if (data->regulator_enabled) {
scoped_guard(mutex, &data->irq_lock)
@@ -130,9 +140,6 @@ static int cc2_enable(struct cc2_data *data)
return 0;
}
- /* clear any pending completion */
- try_wait_for_completion(&data->complete);
-
ret = regulator_enable(data->regulator);
if (ret < 0)
return ret;
@@ -162,6 +169,9 @@ static void cc2_disable(struct cc2_data *data)
scoped_guard(mutex, &data->irq_lock)
data->process_irqs = false;
+ if (data->irq_ready > 0)
+ synchronize_irq(data->irq_ready);
+
/* exclusive regulator, check in case an enable failed */
if (!data->regulator_enabled)
return;
@@ -510,8 +520,10 @@ static irqreturn_t cc2_ready_interrupt(int irq, void *data)
{
struct cc2_data *cc2 = data;
- if (cc2->process_irqs)
- complete(&cc2->complete);
+ scoped_guard(mutex, &cc2->irq_lock) {
+ if (cc2->process_irqs)
+ complete(&cc2->complete);
+ }
return IRQ_HANDLED;
}
--
2.43.0
^ permalink raw reply [flat|nested] 12+ messages in thread