[PATCH v3 4/5] iio: light: vcnl4000: Add IRQ disable callback on cleanup
From: Tsz Shan Chan
Date: Thu Sep 03 2026 - 00:55:11 EST
During driver unbind, devm cleans up resources in LIFO order. The IRQ
handler is freed before the device is powered down. This can lead to
unhandled interrupts.
Register a devm action to disable interrupt explicitly after
devm_request_threaded_irq(), so that the interrupt is disabled before
the IRQ handler is freed. This prevents any unhandled interrupts.
Add a disable_irq callback to chip info structure. This allows each chip
type to implement its own disable sequence.
Signed-off-by: Tsz Shan Chan <tchan@xxxxxxxxxxxxxx>
---
drivers/iio/light/vcnl4000.c | 67 ++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 67 insertions(+)
diff --git a/drivers/iio/light/vcnl4000.c b/drivers/iio/light/vcnl4000.c
index f77b219c46e4..df929eab2bef 100644
--- a/drivers/iio/light/vcnl4000.c
+++ b/drivers/iio/light/vcnl4000.c
@@ -217,6 +217,7 @@ struct vcnl4000_chip_spec {
int (*measure_light)(struct vcnl4000_data *data, int *val);
int (*measure_proximity)(struct vcnl4000_data *data, int *val);
int (*set_power_state)(struct vcnl4000_data *data, bool on);
+ int (*disable_irq)(struct vcnl4000_data *data);
irqreturn_t (*irq_thread)(int irq, void *priv);
irqreturn_t (*trig_buffer_func)(int irq, void *priv);
@@ -1466,6 +1467,48 @@ static int vcnl4040_write_event_config(struct iio_dev *indio_dev,
}
}
+static int vcnl4010_disable_irq(struct vcnl4000_data *data)
+{
+ int ret;
+
+ guard(mutex)(&data->vcnl4000_lock);
+
+ ret = vcnl4010_stop(data);
+ if (ret < 0)
+ return ret;
+
+ ret = i2c_smbus_read_byte_data(data->client, VCNL4010_ISR);
+ if (ret < 0)
+ return ret;
+
+ ret &= VCNL4010_INT_THR | VCNL4010_INT_DRDY;
+ if (!ret)
+ return 0;
+
+ return i2c_smbus_write_byte_data(data->client, VCNL4010_ISR, ret);
+}
+
+static int vcnl4040_disable_irq(struct vcnl4000_data *data)
+{
+ int ret;
+
+ guard(mutex)(&data->vcnl4000_lock);
+
+ ret = vcnl4040_update_ps_int(data, VCNL4040_PS_CONF2_PS_INT, false);
+ if (ret < 0)
+ return ret;
+
+ ret = vcnl4040_update_als_int(data, VCNL4040_ALS_CONF_INT_EN, false);
+ if (ret < 0)
+ return ret;
+
+ ret = i2c_smbus_read_word_data(data->client, data->chip_spec->int_reg);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
static irqreturn_t vcnl4040_irq_thread(int irq, void *p)
{
struct iio_dev *indio_dev = p;
@@ -1815,6 +1858,7 @@ static const struct vcnl4000_chip_spec cm36672p_spec = {
.init = vcnl4200_init,
.measure_proximity = vcnl4200_measure_proximity,
.set_power_state = vcnl4200_set_power_state,
+ .disable_irq = vcnl4040_disable_irq,
.channels = cm36672p_channels,
.num_channels = ARRAY_SIZE(cm36672p_channels),
.info = &vcnl4040_info,
@@ -1843,6 +1887,7 @@ static const struct vcnl4000_chip_spec vcnl4010_spec = {
.measure_light = vcnl4000_measure_light,
.measure_proximity = vcnl4000_measure_proximity,
.set_power_state = vcnl4000_set_power_state,
+ .disable_irq = vcnl4010_disable_irq,
.channels = vcnl4010_channels,
.num_channels = ARRAY_SIZE(vcnl4010_channels),
.info = &vcnl4010_info,
@@ -1858,6 +1903,7 @@ static const struct vcnl4000_chip_spec vcnl4040_spec = {
.measure_light = vcnl4200_measure_light,
.measure_proximity = vcnl4200_measure_proximity,
.set_power_state = vcnl4200_set_power_state,
+ .disable_irq = vcnl4040_disable_irq,
.channels = vcnl4040_channels,
.num_channels = ARRAY_SIZE(vcnl4040_channels),
.info = &vcnl4040_info,
@@ -1877,6 +1923,7 @@ static const struct vcnl4000_chip_spec vcnl4200_spec = {
.measure_light = vcnl4200_measure_light,
.measure_proximity = vcnl4200_measure_proximity,
.set_power_state = vcnl4200_set_power_state,
+ .disable_irq = vcnl4040_disable_irq,
.channels = vcnl4040_channels,
.num_channels = ARRAY_SIZE(vcnl4040_channels),
.info = &vcnl4040_info,
@@ -1912,6 +1959,21 @@ static int vcnl4010_probe_trigger(struct iio_dev *indio_dev)
return devm_iio_trigger_register(&client->dev, trigger);
}
+static void vcnl4000_disable_irq_action(void *data)
+{
+ struct iio_dev *indio_dev = data;
+ struct vcnl4000_data *chip = iio_priv(indio_dev);
+ struct device *dev = &chip->client->dev;
+ int ret;
+
+ if (!chip->chip_spec->disable_irq)
+ return;
+
+ ret = chip->chip_spec->disable_irq(chip);
+ if (ret)
+ dev_warn(dev, "Failed to disable interrupt(%pe)", ERR_PTR(ret));
+}
+
static void vcnl4000_cleanup(void *data)
{
struct iio_dev *indio_dev = data;
@@ -1995,6 +2057,11 @@ static int vcnl4000_probe(struct i2c_client *client)
ret = vcnl4010_probe_trigger(indio_dev);
if (ret < 0)
return ret;
+
+ ret = devm_add_action_or_reset(dev, vcnl4000_disable_irq_action,
+ indio_dev);
+ if (ret)
+ return ret;
}
ret = devm_pm_runtime_set_active_enabled(dev);
--
2.55.0