* [PATCH v3 1/5] iio: light: vcnl4000: use correct channel array size
2026-09-03 4:53 [PATCH v3 0/5] iio: light: vcnl4000: support shared IRQs Tsz Shan Chan
@ 2026-09-03 4:53 ` Tsz Shan Chan
2026-09-03 7:55 ` Andy Shevchenko
2026-09-03 4:53 ` [PATCH v3 2/5] iio: light: vcnl4000: clear DRDY interrupt when buffer disabled Tsz Shan Chan
` (3 subsequent siblings)
4 siblings, 1 reply; 10+ messages in thread
From: Tsz Shan Chan @ 2026-09-03 4:53 UTC (permalink / raw)
To: Jonathan Cameron, David Lechner, Nuno Sá, Andy Shevchenko
Cc: linux-iio, linux-kernel, Tsz Shan Chan
vcnl4200_spec sets .channel to vcnl4040_channels but .num_channels uses
ARRAY_SIZE(vcnl4000_channels). Use ARRAY_SIZE(vcnl4040_channels) so
num_channels matches .channels.
Both arrays have the same size. No behaviour change.
Signed-off-by: Tsz Shan Chan <tchan@jacques.com.au>
---
drivers/iio/light/vcnl4000.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/iio/light/vcnl4000.c b/drivers/iio/light/vcnl4000.c
index 336468d59ea7..cf55fe6afcba 100644
--- a/drivers/iio/light/vcnl4000.c
+++ b/drivers/iio/light/vcnl4000.c
@@ -1864,7 +1864,7 @@ static const struct vcnl4000_chip_spec vcnl4200_spec = {
.measure_proximity = vcnl4200_measure_proximity,
.set_power_state = vcnl4200_set_power_state,
.channels = vcnl4040_channels,
- .num_channels = ARRAY_SIZE(vcnl4000_channels),
+ .num_channels = ARRAY_SIZE(vcnl4040_channels),
.info = &vcnl4040_info,
.irq_thread = vcnl4040_irq_thread,
.int_reg = VCNL4200_INT_FLAGS,
--
2.55.0
^ permalink raw reply related [flat|nested] 10+ messages in thread* Re: [PATCH v3 1/5] iio: light: vcnl4000: use correct channel array size
2026-09-03 4:53 ` [PATCH v3 1/5] iio: light: vcnl4000: use correct channel array size Tsz Shan Chan
@ 2026-09-03 7:55 ` Andy Shevchenko
0 siblings, 0 replies; 10+ messages in thread
From: Andy Shevchenko @ 2026-09-03 7:55 UTC (permalink / raw)
To: Tsz Shan Chan
Cc: Jonathan Cameron, David Lechner, Nuno Sá, Andy Shevchenko,
linux-iio, linux-kernel, Tsz Shan Chan
On Thu, Sep 03, 2026 at 02:53:46PM +1000, Tsz Shan Chan wrote:
> vcnl4200_spec sets .channel to vcnl4040_channels but .num_channels uses
> ARRAY_SIZE(vcnl4000_channels). Use ARRAY_SIZE(vcnl4040_channels) so
> num_channels matches .channels.
>
> Both arrays have the same size. No behaviour change.
Reviewed-by: Andy Shevchenko <andriy.shevchenko@intel.com>
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH v3 2/5] iio: light: vcnl4000: clear DRDY interrupt when buffer disabled
2026-09-03 4:53 [PATCH v3 0/5] iio: light: vcnl4000: support shared IRQs Tsz Shan Chan
2026-09-03 4:53 ` [PATCH v3 1/5] iio: light: vcnl4000: use correct channel array size Tsz Shan Chan
@ 2026-09-03 4:53 ` Tsz Shan Chan
2026-09-03 7:55 ` Andy Shevchenko
2026-09-03 4:53 ` [PATCH v3 3/5] iio: light: vcnl4000: refactor interrupt config functions Tsz Shan Chan
` (2 subsequent siblings)
4 siblings, 1 reply; 10+ messages in thread
From: Tsz Shan Chan @ 2026-09-03 4:53 UTC (permalink / raw)
To: Jonathan Cameron, David Lechner, Nuno Sá, Andy Shevchenko
Cc: linux-iio, linux-kernel, Tsz Shan Chan
on vcnl4010/4020, interrupt status bits are cleared by writing 1 to the
corresponding bit in the ISR register. If data ready interrupt triggers
when the iio buffer is disabled, the DRDY bit is never cleared and the
interrupt line stays low.
Clear the DRDY bit in the interrupt handler when the buffer is disabled.
Signed-off-by: Tsz Shan Chan <tchan@jacques.com.au>
---
drivers/iio/light/vcnl4000.c | 9 +++++++--
1 file changed, 7 insertions(+), 2 deletions(-)
diff --git a/drivers/iio/light/vcnl4000.c b/drivers/iio/light/vcnl4000.c
index cf55fe6afcba..c5f34e748e1a 100644
--- a/drivers/iio/light/vcnl4000.c
+++ b/drivers/iio/light/vcnl4000.c
@@ -1545,8 +1545,13 @@ static irqreturn_t vcnl4010_irq_thread(int irq, void *p)
isr & VCNL4010_INT_THR);
}
- if (isr & VCNL4010_INT_DRDY && iio_buffer_enabled(indio_dev))
- iio_trigger_poll_nested(indio_dev->trig);
+ if ((isr & VCNL4010_INT_DRDY)) {
+ if (iio_buffer_enabled(indio_dev))
+ iio_trigger_poll_nested(indio_dev->trig);
+ else
+ i2c_smbus_write_byte_data(data->client, VCNL4010_ISR,
+ isr & VCNL4010_INT_DRDY);
+ }
end:
return IRQ_HANDLED;
--
2.55.0
^ permalink raw reply related [flat|nested] 10+ messages in thread* Re: [PATCH v3 2/5] iio: light: vcnl4000: clear DRDY interrupt when buffer disabled
2026-09-03 4:53 ` [PATCH v3 2/5] iio: light: vcnl4000: clear DRDY interrupt when buffer disabled Tsz Shan Chan
@ 2026-09-03 7:55 ` Andy Shevchenko
0 siblings, 0 replies; 10+ messages in thread
From: Andy Shevchenko @ 2026-09-03 7:55 UTC (permalink / raw)
To: Tsz Shan Chan
Cc: Jonathan Cameron, David Lechner, Nuno Sá, Andy Shevchenko,
linux-iio, linux-kernel, Tsz Shan Chan
On Thu, Sep 03, 2026 at 02:53:47PM +1000, Tsz Shan Chan wrote:
> on vcnl4010/4020, interrupt status bits are cleared by writing 1 to the
> corresponding bit in the ISR register. If data ready interrupt triggers
> when the iio buffer is disabled, the DRDY bit is never cleared and the
> interrupt line stays low.
>
> Clear the DRDY bit in the interrupt handler when the buffer is disabled.
Shouldn't this have a Fixes tag?
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH v3 3/5] iio: light: vcnl4000: refactor interrupt config functions
2026-09-03 4:53 [PATCH v3 0/5] iio: light: vcnl4000: support shared IRQs Tsz Shan Chan
2026-09-03 4:53 ` [PATCH v3 1/5] iio: light: vcnl4000: use correct channel array size Tsz Shan Chan
2026-09-03 4:53 ` [PATCH v3 2/5] iio: light: vcnl4000: clear DRDY interrupt when buffer disabled Tsz Shan Chan
@ 2026-09-03 4:53 ` Tsz Shan Chan
2026-09-03 4:53 ` [PATCH v3 4/5] iio: light: vcnl4000: Add IRQ disable callback on cleanup Tsz Shan Chan
2026-09-03 4:53 ` [PATCH v3 5/5] iio: light: vcnl4000: add shared IRQ support Tsz Shan Chan
4 siblings, 0 replies; 10+ messages in thread
From: Tsz Shan Chan @ 2026-09-03 4:53 UTC (permalink / raw)
To: Jonathan Cameron, David Lechner, Nuno Sá, Andy Shevchenko
Cc: linux-iio, linux-kernel, Tsz Shan Chan
Move interrupt configuration logic into helper functions
(vcnl4010_stop, vcnl4040_update_als_int, vcnl4040_update_ps_int).
This avoids code duplication and prepares for disabling interrupt duirng
cleanup. Also change the disable order for vcnl4010/4020 to clear
INT_CTRL before COMMAND to avoid spurious interrupts.
Signed-off-by: Tsz Shan Chan <tchan@jacques.com.au>
---
drivers/iio/light/vcnl4000.c | 79 ++++++++++++++++++++++++--------------------
1 file changed, 44 insertions(+), 35 deletions(-)
diff --git a/drivers/iio/light/vcnl4000.c b/drivers/iio/light/vcnl4000.c
index c5f34e748e1a..f77b219c46e4 100644
--- a/drivers/iio/light/vcnl4000.c
+++ b/drivers/iio/light/vcnl4000.c
@@ -1299,6 +1299,17 @@ static bool vcnl4010_is_thr_enabled(struct vcnl4000_data *data)
return !!(ret & VCNL4010_INT_THR_EN);
}
+static int vcnl4010_stop(struct vcnl4000_data *data)
+{
+ int ret;
+
+ ret = i2c_smbus_write_byte_data(data->client, VCNL4010_INT_CTRL, 0);
+ if (ret < 0)
+ return ret;
+
+ return i2c_smbus_write_byte_data(data->client, VCNL4000_COMMAND, 0);
+}
+
static int vcnl4010_read_event_config(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan,
enum iio_event_type type,
@@ -1334,16 +1345,10 @@ static int vcnl4010_config_threshold_enable(struct vcnl4000_data *data)
static int vcnl4010_config_threshold_disable(struct vcnl4000_data *data)
{
- int ret;
-
if (!vcnl4010_is_thr_enabled(data))
return 0;
- ret = i2c_smbus_write_byte_data(data->client, VCNL4000_COMMAND, 0);
- if (ret < 0)
- return ret;
-
- return i2c_smbus_write_byte_data(data->client, VCNL4010_INT_CTRL, 0);
+ return vcnl4010_stop(data);
}
static int vcnl4010_config_threshold(struct iio_dev *indio_dev, bool state)
@@ -1375,6 +1380,34 @@ static int vcnl4010_write_event_config(struct iio_dev *indio_dev,
}
}
+static int vcnl4040_update_als_int(struct vcnl4000_data *data, u16 mask, bool state)
+{
+ int ret, val;
+
+ ret = i2c_smbus_read_word_data(data->client, VCNL4200_AL_CONF);
+ if (ret < 0)
+ return ret;
+
+ val = state ? (ret | mask) : (ret & ~mask);
+
+ data->als_int = FIELD_GET(VCNL4040_ALS_CONF_INT_EN, val);
+ return i2c_smbus_write_word_data(data->client, VCNL4200_AL_CONF, val);
+}
+
+static int vcnl4040_update_ps_int(struct vcnl4000_data *data, u16 mask, bool state)
+{
+ int ret, val;
+
+ ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF1);
+ if (ret < 0)
+ return ret;
+
+ val = state ? (ret | mask) : (ret & ~mask);
+
+ data->ps_int = FIELD_GET(VCNL4040_PS_CONF2_PS_INT, val);
+ return i2c_smbus_write_word_data(data->client, VCNL4200_PS_CONF1, val);
+}
+
static int vcnl4040_read_event_config(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan,
enum iio_event_type type,
@@ -1413,40 +1446,21 @@ static int vcnl4040_write_event_config(struct iio_dev *indio_dev,
enum iio_event_direction dir,
bool state)
{
- int ret;
- u16 val, mask;
+ u16 mask;
struct vcnl4000_data *data = iio_priv(indio_dev);
guard(mutex)(&data->vcnl4000_lock);
switch (chan->type) {
case IIO_LIGHT:
- ret = i2c_smbus_read_word_data(data->client, VCNL4200_AL_CONF);
- if (ret < 0)
- return ret;
-
- mask = VCNL4040_ALS_CONF_INT_EN;
- if (state)
- val = (ret | mask);
- else
- val = (ret & ~mask);
-
- data->als_int = FIELD_GET(VCNL4040_ALS_CONF_INT_EN, val);
- return i2c_smbus_write_word_data(data->client, VCNL4200_AL_CONF, val);
+ return vcnl4040_update_als_int(data, VCNL4040_ALS_CONF_INT_EN, state);
case IIO_PROXIMITY:
- ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF1);
- if (ret < 0)
- return ret;
-
if (dir == IIO_EV_DIR_RISING)
mask = VCNL4040_PS_IF_AWAY;
else
mask = VCNL4040_PS_IF_CLOSE;
- val = state ? (ret | mask) : (ret & ~mask);
-
- data->ps_int = FIELD_GET(VCNL4040_PS_CONF2_PS_INT, val);
- return i2c_smbus_write_word_data(data->client, VCNL4200_PS_CONF1, val);
+ return vcnl4040_update_ps_int(data, mask, state);
default:
return -EINVAL;
}
@@ -1629,13 +1643,8 @@ static int vcnl4010_buffer_postenable(struct iio_dev *indio_dev)
static int vcnl4010_buffer_predisable(struct iio_dev *indio_dev)
{
struct vcnl4000_data *data = iio_priv(indio_dev);
- int ret;
-
- ret = i2c_smbus_write_byte_data(data->client, VCNL4010_INT_CTRL, 0);
- if (ret < 0)
- return ret;
- return i2c_smbus_write_byte_data(data->client, VCNL4000_COMMAND, 0);
+ return vcnl4010_stop(data);
}
static const struct iio_buffer_setup_ops vcnl4010_buffer_ops = {
--
2.55.0
^ permalink raw reply related [flat|nested] 10+ messages in thread* [PATCH v3 4/5] iio: light: vcnl4000: Add IRQ disable callback on cleanup
2026-09-03 4:53 [PATCH v3 0/5] iio: light: vcnl4000: support shared IRQs Tsz Shan Chan
` (2 preceding siblings ...)
2026-09-03 4:53 ` [PATCH v3 3/5] iio: light: vcnl4000: refactor interrupt config functions Tsz Shan Chan
@ 2026-09-03 4:53 ` Tsz Shan Chan
2026-09-03 8:03 ` Andy Shevchenko
2026-09-03 4:53 ` [PATCH v3 5/5] iio: light: vcnl4000: add shared IRQ support Tsz Shan Chan
4 siblings, 1 reply; 10+ messages in thread
From: Tsz Shan Chan @ 2026-09-03 4:53 UTC (permalink / raw)
To: Jonathan Cameron, David Lechner, Nuno Sá, Andy Shevchenko
Cc: linux-iio, linux-kernel, Tsz Shan Chan
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@jacques.com.au>
---
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
^ permalink raw reply related [flat|nested] 10+ messages in thread* Re: [PATCH v3 4/5] iio: light: vcnl4000: Add IRQ disable callback on cleanup
2026-09-03 4:53 ` [PATCH v3 4/5] iio: light: vcnl4000: Add IRQ disable callback on cleanup Tsz Shan Chan
@ 2026-09-03 8:03 ` Andy Shevchenko
0 siblings, 0 replies; 10+ messages in thread
From: Andy Shevchenko @ 2026-09-03 8:03 UTC (permalink / raw)
To: Tsz Shan Chan
Cc: Jonathan Cameron, David Lechner, Nuno Sá, Andy Shevchenko,
linux-iio, linux-kernel, Tsz Shan Chan
On Thu, Sep 03, 2026 at 02:53:49PM +1000, Tsz Shan Chan wrote:
> 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.
...
> +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);
Since ret is in use it's better to follow regular pattern
ret &= VCNL4010_INT_THR | VCNL4010_INT_DRDY;
if (ret)
return i2c_smbus_write_byte_data(data->client, VCNL4010_ISR, ret);
return 0;
BUT, since ret doesn't care an error code at this point, it's even better to
use proper typing.
u8 byte;
...
byte = ret & (VCNL4010_INT_THR | VCNL4010_INT_DRDY);
if (!byte)
return 0;
return i2c_smbus_write_byte_data(data->client, VCNL4010_ISR, byte);
> +}
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH v3 5/5] iio: light: vcnl4000: add shared IRQ support
2026-09-03 4:53 [PATCH v3 0/5] iio: light: vcnl4000: support shared IRQs Tsz Shan Chan
` (3 preceding siblings ...)
2026-09-03 4:53 ` [PATCH v3 4/5] iio: light: vcnl4000: Add IRQ disable callback on cleanup Tsz Shan Chan
@ 2026-09-03 4:53 ` Tsz Shan Chan
2026-09-03 8:07 ` Andy Shevchenko
4 siblings, 1 reply; 10+ messages in thread
From: Tsz Shan Chan @ 2026-09-03 4:53 UTC (permalink / raw)
To: Jonathan Cameron, David Lechner, Nuno Sá, Andy Shevchenko
Cc: linux-iio, linux-kernel, Tsz Shan Chan
Change the interrupt trigger to IRQF_TRIGGER_LOW and add IRQF_SHARED to
allow the sensor to share an interrupt line.
Return IRQ_NONE from the irq handler when read fails or when none of the
interrupt source bits handled by the driver are set. This prevents
claiming interrupts from other devices on a shared line.
The datasheets confirm that the INT pin is open drain active low, and
the interrupts are latched:
- vcnl4010/4020: The INT pin is open drain. It is pulled low
while a status bit is 1, and it stays low until the driver writes
a 1 to clear it.
- vcnl4040/4200: The INT pin is pulled low when a flag triggers. It
resets to high only after the driver reads the INT_Flag register.
Signed-off-by: Tsz Shan Chan <tchan@jacques.com.au>
---
drivers/iio/light/vcnl4000.c | 16 +++++++++++-----
1 file changed, 11 insertions(+), 5 deletions(-)
diff --git a/drivers/iio/light/vcnl4000.c b/drivers/iio/light/vcnl4000.c
index df929eab2bef..9855c2954429 100644
--- a/drivers/iio/light/vcnl4000.c
+++ b/drivers/iio/light/vcnl4000.c
@@ -1517,7 +1517,11 @@ static irqreturn_t vcnl4040_irq_thread(int irq, void *p)
ret = i2c_smbus_read_word_data(data->client, data->chip_spec->int_reg);
if (ret < 0)
- return IRQ_HANDLED;
+ return IRQ_NONE;
+
+ if (!(ret & (VCNL4040_PS_IF_CLOSE | VCNL4040_PS_IF_AWAY |
+ VCNL4040_ALS_FALLING | VCNL4040_ALS_RISING)))
+ return IRQ_NONE;
if (ret & VCNL4040_PS_IF_CLOSE) {
iio_push_event(indio_dev,
@@ -1573,7 +1577,10 @@ static irqreturn_t vcnl4010_irq_thread(int irq, void *p)
ret = i2c_smbus_read_byte_data(data->client, VCNL4010_ISR);
if (ret < 0)
- goto end;
+ return IRQ_NONE;
+
+ if (!(ret & (VCNL4010_INT_THR | VCNL4010_INT_DRDY)))
+ return IRQ_NONE;
isr = ret;
@@ -1610,7 +1617,6 @@ static irqreturn_t vcnl4010_irq_thread(int irq, void *p)
isr & VCNL4010_INT_DRDY);
}
-end:
return IRQ_HANDLED;
}
@@ -2047,8 +2053,8 @@ static int vcnl4000_probe(struct i2c_client *client)
if (client->irq && data->chip_spec->irq_thread) {
ret = devm_request_threaded_irq(dev, client->irq, NULL,
data->chip_spec->irq_thread,
- IRQF_TRIGGER_FALLING |
- IRQF_ONESHOT,
+ IRQF_TRIGGER_LOW |
+ IRQF_ONESHOT | IRQF_SHARED,
"vcnl4000_irq",
indio_dev);
if (ret < 0)
--
2.55.0
^ permalink raw reply related [flat|nested] 10+ messages in thread* Re: [PATCH v3 5/5] iio: light: vcnl4000: add shared IRQ support
2026-09-03 4:53 ` [PATCH v3 5/5] iio: light: vcnl4000: add shared IRQ support Tsz Shan Chan
@ 2026-09-03 8:07 ` Andy Shevchenko
0 siblings, 0 replies; 10+ messages in thread
From: Andy Shevchenko @ 2026-09-03 8:07 UTC (permalink / raw)
To: Tsz Shan Chan
Cc: Jonathan Cameron, David Lechner, Nuno Sá, Andy Shevchenko,
linux-iio, linux-kernel, Tsz Shan Chan
On Thu, Sep 03, 2026 at 02:53:50PM +1000, Tsz Shan Chan wrote:
> Change the interrupt trigger to IRQF_TRIGGER_LOW and add IRQF_SHARED to
> allow the sensor to share an interrupt line.
>
> Return IRQ_NONE from the irq handler when read fails or when none of the
> interrupt source bits handled by the driver are set. This prevents
> claiming interrupts from other devices on a shared line.
>
> The datasheets confirm that the INT pin is open drain active low, and
> the interrupts are latched:
> - vcnl4010/4020: The INT pin is open drain. It is pulled low
> while a status bit is 1, and it stays low until the driver writes
> a 1 to clear it.
> - vcnl4040/4200: The INT pin is pulled low when a flag triggers. It
> resets to high only after the driver reads the INT_Flag register.
...
> ret = devm_request_threaded_irq(dev, client->irq, NULL,
> data->chip_spec->irq_thread,
> - IRQF_TRIGGER_FALLING |
> - IRQF_ONESHOT,
> + IRQF_TRIGGER_LOW |
> + IRQF_ONESHOT | IRQF_SHARED,
> "vcnl4000_irq",
> indio_dev);
This will require to have other drivers be in the same settings to share this
interrupt line. Which means that the cases when we have active-high setting
in one device in DT and 'fake' one in another (assuming a NOT gate is added
on the PCB), this won't work. In another driver this was split to issue a warning
and use default trigger if and only if it's not provided by the firmware.
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply [flat|nested] 10+ messages in thread