From nobody Mon Feb 9 06:25:13 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id CDF4EC7EE23 for ; Tue, 30 May 2023 14:27:00 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232526AbjE3O07 (ORCPT ); Tue, 30 May 2023 10:26:59 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:37692 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232530AbjE3O01 (ORCPT ); Tue, 30 May 2023 10:26:27 -0400 Received: from smtp2.axis.com (smtp2.axis.com [195.60.68.18]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 719F6E42; Tue, 30 May 2023 07:25:33 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=axis.com; q=dns/txt; s=axis-central1; t=1685456733; x=1716992733; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=684bgFNAKenIiEPZespOZgUaXW19Q0dcrMT3J5pA2iU=; b=oLeRHxwXi2ccsfNu7WLuzo3jds2C+Dsn1t2dZQ2bwxbi+s/r3ozJR8qF fBxcoYxaC0p4ZaCMc+2RAzQxNWyRrGiaIbmwrM90r/Tjt67o2aUsjL1xg g9rIxwDTj5hSIy8oysCpktDtLhfwBSzcvqi9SizXEo+o07P+Yy6+sanp9 ke3Y/dQGs1Zk9dZy883l/ui/7JpAtTNEwX2OvHhEWjbCOA34Sery8lFCs t6i41RhRNztU2sV65P3J4ok8i2oDhHtz5AMNQ/D4bsOkBdShljHHO0+cK AXibZxgYSfW9YmROS98wVjnNq/6+W+LyOLTFLf2JLOPWhT20GYUQQzQ/6 w==; From: Astrid Rost To: Jonathan Cameron , Lars-Peter Clausen CC: , , , =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= , Mathieu Othacehe , Astrid Rost Subject: [PATCH v5 4/7] iio: light: vcnl4000: add illuminance irq vcnl4040/4200 Date: Tue, 30 May 2023 16:24:02 +0200 Message-ID: <20230530142405.1679146-5-astrid.rost@axis.com> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20230530142405.1679146-1-astrid.rost@axis.com> References: <20230530142405.1679146-1-astrid.rost@axis.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Type: text/plain; charset="utf-8" Add support to configure ambient light sensor interrupts and threshold limits for vcnl4040 and vcnl4200. If an interrupt is detected an event will be pushed to the event interface. Signed-off-by: Astrid Rost --- drivers/iio/light/vcnl4000.c | 197 ++++++++++++++++++++++++++--------- 1 file changed, 146 insertions(+), 51 deletions(-) diff --git a/drivers/iio/light/vcnl4000.c b/drivers/iio/light/vcnl4000.c index b53f75b71309..8022805a6af4 100644 --- a/drivers/iio/light/vcnl4000.c +++ b/drivers/iio/light/vcnl4000.c @@ -62,6 +62,8 @@ #define VCNL4200_PS_CONF1 0x03 /* Proximity configuration */ #define VCNL4040_PS_THDL_LM 0x06 /* Proximity threshold low */ #define VCNL4040_PS_THDH_LM 0x07 /* Proximity threshold high */ +#define VCNL4040_ALS_THDL_LM 0x02 /* Ambient light threshold low */ +#define VCNL4040_ALS_THDH_LM 0x01 /* Ambient light threshold high */ #define VCNL4200_PS_DATA 0x08 /* Proximity data */ #define VCNL4200_AL_DATA 0x09 /* Ambient light data */ #define VCNL4040_INT_FLAGS 0x0b /* Interrupt register */ @@ -81,11 +83,14 @@ =20 #define VCNL4040_ALS_CONF_ALS_SHUTDOWN BIT(0) #define VCNL4040_ALS_CONF_IT GENMASK(7, 6) /* Ambient integration time */ +#define VCNL4040_ALS_CONF_INT_EN BIT(1) /* Ambient light Interrupt enable = */ #define VCNL4040_PS_CONF1_PS_SHUTDOWN BIT(0) #define VCNL4040_PS_CONF2_PS_IT GENMASK(3, 1) /* Proximity integration tim= e */ #define VCNL4040_PS_CONF2_PS_INT GENMASK(9, 8) /* Proximity interrupt mode= */ #define VCNL4040_PS_IF_AWAY BIT(8) /* Proximity event cross low threshold= */ #define VCNL4040_PS_IF_CLOSE BIT(9) /* Proximity event cross high thresho= ld */ +#define VCNL4040_ALS_RISING BIT(12) /* Ambient Light cross high threshold= */ +#define VCNL4040_ALS_FALLING BIT(13) /* Ambient Light cross low threshold= */ =20 /* Bit masks for interrupt registers. */ #define VCNL4010_INT_THR_SEL BIT(0) /* Select threshold interrupt source */ @@ -170,6 +175,7 @@ struct vcnl4000_data { int rev; int al_scale; u8 ps_int; /* proximity interrupt mode */ + u8 als_int; /* ambient light interrupt mode*/ const struct vcnl4000_chip_spec *chip_spec; struct mutex vcnl4000_lock; struct vcnl4200_channel vcnl4200_al; @@ -294,7 +300,7 @@ static int vcnl4200_set_power_state(struct vcnl4000_dat= a *data, bool on) int ret; =20 /* Do not power down if interrupts are enabled */ - if (!on && data->ps_int) + if (!on && (data->ps_int || data->als_int)) return 0; =20 ret =3D vcnl4000_write_als_enable(data, on); @@ -339,6 +345,7 @@ static int vcnl4200_init(struct vcnl4000_data *data) =20 data->rev =3D (ret >> 8) & 0xf; data->ps_int =3D 0; + data->als_int =3D 0; =20 data->vcnl4200_al.reg =3D VCNL4200_AL_DATA; data->vcnl4200_ps.reg =3D VCNL4200_PS_DATA; @@ -928,27 +935,45 @@ static int vcnl4040_read_event(struct iio_dev *indio_= dev, enum iio_event_info info, int *val, int *val2) { - int ret; + int ret =3D -EINVAL; struct vcnl4000_data *data =3D iio_priv(indio_dev); =20 - switch (dir) { - case IIO_EV_DIR_RISING: - ret =3D i2c_smbus_read_word_data(data->client, - VCNL4040_PS_THDH_LM); - if (ret < 0) - return ret; - *val =3D ret; - return IIO_VAL_INT; - case IIO_EV_DIR_FALLING: - ret =3D i2c_smbus_read_word_data(data->client, - VCNL4040_PS_THDL_LM); - if (ret < 0) - return ret; - *val =3D ret; - return IIO_VAL_INT; + switch (chan->type) { + case IIO_LIGHT: + switch (dir) { + case IIO_EV_DIR_RISING: + ret =3D i2c_smbus_read_word_data(data->client, + VCNL4040_ALS_THDH_LM); + break; + case IIO_EV_DIR_FALLING: + ret =3D i2c_smbus_read_word_data(data->client, + VCNL4040_ALS_THDL_LM); + break; + default: + return -EINVAL; + } + break; + case IIO_PROXIMITY: + switch (dir) { + case IIO_EV_DIR_RISING: + ret =3D i2c_smbus_read_word_data(data->client, + VCNL4040_PS_THDH_LM); + break; + case IIO_EV_DIR_FALLING: + ret =3D i2c_smbus_read_word_data(data->client, + VCNL4040_PS_THDL_LM); + break; + default: + return -EINVAL; + } + break; default: return -EINVAL; } + if (ret < 0) + return ret; + *val =3D ret; + return IIO_VAL_INT; } =20 static int vcnl4040_write_event(struct iio_dev *indio_dev, @@ -958,25 +983,43 @@ static int vcnl4040_write_event(struct iio_dev *indio= _dev, enum iio_event_info info, int val, int val2) { - int ret; + int ret =3D -EINVAL; struct vcnl4000_data *data =3D iio_priv(indio_dev); - - switch (dir) { - case IIO_EV_DIR_RISING: - ret =3D i2c_smbus_write_word_data(data->client, - VCNL4040_PS_THDH_LM, val); - if (ret < 0) - return ret; - return IIO_VAL_INT; - case IIO_EV_DIR_FALLING: - ret =3D i2c_smbus_write_word_data(data->client, - VCNL4040_PS_THDL_LM, val); - if (ret < 0) - return ret; - return IIO_VAL_INT; + switch (chan->type) { + case IIO_LIGHT: + switch (dir) { + case IIO_EV_DIR_RISING: + ret =3D i2c_smbus_write_word_data(data->client, + VCNL4040_ALS_THDH_LM, val); + break; + case IIO_EV_DIR_FALLING: + ret =3D i2c_smbus_write_word_data(data->client, + VCNL4040_ALS_THDL_LM, val); + break; + default: + return -EINVAL; + } + break; + case IIO_PROXIMITY: + switch (dir) { + case IIO_EV_DIR_RISING: + ret =3D i2c_smbus_write_word_data(data->client, + VCNL4040_PS_THDH_LM, val); + break; + case IIO_EV_DIR_FALLING: + ret =3D i2c_smbus_write_word_data(data->client, + VCNL4040_PS_THDL_LM, val); + break; + default: + return -EINVAL; + } + break; default: return -EINVAL; } + if (ret < 0) + return ret; + return IIO_VAL_INT; } =20 static bool vcnl4010_is_thr_enabled(struct vcnl4000_data *data) @@ -1068,16 +1111,28 @@ static int vcnl4040_read_event_config(struct iio_de= v *indio_dev, { int ret; struct vcnl4000_data *data =3D iio_priv(indio_dev); + switch (chan->type) { + case IIO_LIGHT: + ret =3D i2c_smbus_read_word_data(data->client, VCNL4200_AL_CONF); + if (ret < 0) + return ret; =20 - ret =3D i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF1); - if (ret < 0) - return ret; + data->als_int =3D FIELD_GET(VCNL4040_ALS_CONF_INT_EN, ret); =20 - data->ps_int =3D FIELD_GET(VCNL4040_PS_CONF2_PS_INT, ret); + return data->als_int; + case IIO_PROXIMITY: + ret =3D i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF1); + if (ret < 0) + return ret; + + data->ps_int =3D FIELD_GET(VCNL4040_PS_CONF2_PS_INT, ret); =20 - return (dir =3D=3D IIO_EV_DIR_RISING) ? - FIELD_GET(VCNL4040_PS_IF_AWAY, ret) : - FIELD_GET(VCNL4040_PS_IF_CLOSE, ret); + return (dir =3D=3D IIO_EV_DIR_RISING) ? + FIELD_GET(VCNL4040_PS_IF_AWAY, ret) : + FIELD_GET(VCNL4040_PS_IF_CLOSE, ret); + default: + return -EINVAL; + } } =20 static int vcnl4040_write_event_config(struct iio_dev *indio_dev, @@ -1085,29 +1140,51 @@ static int vcnl4040_write_event_config(struct iio_d= ev *indio_dev, enum iio_event_type type, enum iio_event_direction dir, int state) { - int ret; + int ret =3D -EINVAL; u16 val, mask; struct vcnl4000_data *data =3D iio_priv(indio_dev); =20 mutex_lock(&data->vcnl4000_lock); =20 - ret =3D i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF1); - if (ret < 0) - goto out; + switch (chan->type) { + case IIO_LIGHT: + ret =3D i2c_smbus_read_word_data(data->client, VCNL4200_AL_CONF); + if (ret < 0) + goto out; =20 - if (dir =3D=3D IIO_EV_DIR_RISING) - mask =3D VCNL4040_PS_IF_AWAY; - else - mask =3D VCNL4040_PS_IF_CLOSE; + mask =3D VCNL4040_ALS_CONF_INT_EN; =20 - val =3D state ? (ret | mask) : (ret & ~mask); + val =3D state ? (ret | mask) : (ret & ~mask); =20 - data->ps_int =3D FIELD_GET(VCNL4040_PS_CONF2_PS_INT, val); - ret =3D i2c_smbus_write_word_data(data->client, VCNL4200_PS_CONF1, val); + data->als_int =3D FIELD_GET(VCNL4040_ALS_CONF_INT_EN, val); + ret =3D i2c_smbus_write_word_data(data->client, VCNL4200_AL_CONF, + val); + break; + case IIO_PROXIMITY: + + ret =3D i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF1); + if (ret < 0) + goto out; + + if (dir =3D=3D IIO_EV_DIR_RISING) + mask =3D VCNL4040_PS_IF_AWAY; + else + mask =3D VCNL4040_PS_IF_CLOSE; + + val =3D state ? (ret | mask) : (ret & ~mask); + + data->ps_int =3D FIELD_GET(VCNL4040_PS_CONF2_PS_INT, val); + ret =3D i2c_smbus_write_word_data(data->client, VCNL4200_PS_CONF1, + val); + break; + default: + break; + } =20 out: mutex_unlock(&data->vcnl4000_lock); - data->chip_spec->set_power_state(data, data->ps_int !=3D 0); + data->chip_spec->set_power_state(data, data->ps_int || + data->als_int); =20 return ret; } @@ -1138,6 +1215,22 @@ static irqreturn_t vcnl4040_irq_thread(int irq, void= *p) iio_get_time_ns(indio_dev)); } =20 + if (ret & VCNL4040_ALS_FALLING) { + iio_push_event(indio_dev, + IIO_UNMOD_EVENT_CODE(IIO_LIGHT, 0, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_FALLING), + iio_get_time_ns(indio_dev)); + } + + if (ret & VCNL4040_ALS_RISING) { + iio_push_event(indio_dev, + IIO_UNMOD_EVENT_CODE(IIO_LIGHT, 0, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_RISING), + iio_get_time_ns(indio_dev)); + } + return IRQ_HANDLED; } =20 @@ -1360,6 +1453,8 @@ static const struct iio_chan_spec vcnl4040_channels[]= =3D { BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_INT_TIME), .info_mask_separate_available =3D BIT(IIO_CHAN_INFO_INT_TIME), + .event_spec =3D vcnl4000_event_spec, + .num_event_specs =3D ARRAY_SIZE(vcnl4000_event_spec), }, { .type =3D IIO_PROXIMITY, .info_mask_separate =3D BIT(IIO_CHAN_INFO_RAW) | --=20 2.30.2