* [PATCH v6 0/2] iio: proximity: add driver for ST VL53L1X ToF sensor @ 2026-03-19 19:07 Siratul Islam 2026-03-19 19:07 ` [PATCH v6 1/2] dt-bindings: iio: proximity: add " Siratul Islam 2026-03-19 19:07 ` [PATCH v6 2/2] iio: proximity: add driver for " Siratul Islam 0 siblings, 2 replies; 21+ messages in thread From: Siratul Islam @ 2026-03-19 19:07 UTC (permalink / raw) To: linux-iio, devicetree Cc: jic23, dlechner, nuno.sa, andy, robh, krzk+dt, conor+dt, linux-kernel, Siratul Islam This series adds support for the STMicroelectronics VL53L1X Time-of-Flight ranging sensor. The VL53L1X is a ToF laser-ranging sensor with I2C interface, capable of measuring distances up to 4 meters. The driver supports both interrupt-driven and polled operation. Why a separate driver is needed (instead of extending vl53l0x-i2c.c): The VL53L1X is fundamentally different from the VL53L0X despite the similar naming. Extending the existing driver would require rewriting the majority of it. Key differences include: - A different register map (16-bit addresses vs. 8-bit addresses). - Requires a 91-byte firmware configuration blob to be loaded at boot. - Requires a VHV calibration cycle. - Has distance mode and timing budget configurations. - Uses the regmap API rather than raw i2c_smbus calls. I also reviewed other drivers in drivers/iio/proximity/ and can confirm this IP block does not appear to be shared by any other existing driver. Tested on Raspberry Pi 5 with a VL53L1X breakout board. Note on vdd-supply: In v2, I added vdd-supply as required. Since I'm using the shared st,vl53l0x.yaml binding now, I had to drop that requirement to avoid breaking backward compatibility for existing st,vl53l0x devicetrees. The driver itself still uses non-optional devm_regulator_get() so no change there. --- Changes in v6: - Make vdd-supply required. Add descriptions to vdd-supply and reset-gpios (per Jonathan). - Check reset_control_deassert() return value (per Andy). - Use volatile table for result registers and caching for the rest. Also mark write-only registers. Changes in v5: - Patch 1: No changes. Collected Reviewed-by tag from Krzysztof Kozlowski. - Switch XSHUT pin handling from the GPIO consumer API to the Reset controller API. - Drop <linux/gpio/consumer.h> and include <linux/reset.h>. Changes in v4: - Move i2c_check_functionality() to the top of probe before allocations. - Use struct u32_fract for oscillator correction math. - Explicitly set .cache_type = REGCACHE_NONE. - Document intentional predisable drain - Use dev_err_probe() for firmware boot timeout and remove duplicate IRQ request error message. - Use sizeof() in regmap bulk read/writes instead of hardcoded lengths. - Add missing headers (<linux/math.h>, <linux/time.h>, <asm/byteorder.h>). - Various formatting cleanups per Andy's review. Changes in v3: - Merge DT binding into existing st,vl53l0x.yaml (per Krzysztof). - Use "reset-gpios" in the binding but xshut_gpio in the driver since that's the actual pin name. - Replace manual polling loops in chip_init and read_proximity with regmap_read_poll_timeout(). - Remove irq_get_trigger_type() and pass IRQF_NO_THREAD directly (per Andy). - Drop struct i2c_client from private data and store irq as int instead. Derive struct device from regmap where needed. - Add dev_err_probe() to first devm_request_irq() error path. - Replace linux/device.h with linux/dev_printk.h, add linux/array_size.h, linux/err.h, linux/types.h (per Andy). - Use USEC_PER_MSEC for poll timeouts. - Remove unnecessary casts. - Divide long config blob to 8 values per line. - Rename goto label to notify_and_clear_irq and drop unused dev_dbg. - Add datasheet section reference for boot delay comment. - vdd-supply no longer required in shared binding to avoid breaking existing st,vl53l0x devicetrees. Changes in v2: - Skip software reset in chip_init when xshut GPIO is available, since the device was already hardware-reset during power-on. - Rename "reset" GPIO to "xshut" to match the datasheet pin name and updated DT binding accordingly. - Make vdd-supply required in DT binding. - Use reg_sequence arrays and regmap_multi_reg_write() for distance mode configuration. - Switch to a hardirq handler with iio_trigger_poll() and IRQF_NO_THREAD instead of a threaded handler. - Drop IRQF_TRIGGER_FALLING fallback. Leave trigger type to firmware/DT. - Use iio_validate_own_trigger() instead of a custom validate_trigger, drop trig pointer from driver data. - Switch usleep_range() to fsleep() throughout and add comments for sleep values. - Don't fail probe on unknown model ID, just log with dev_info(). - Split stop_ranging cleanup into its own devm action, separate from power_off. - Add missing includes: device.h, bitfield.h, completion.h, mod_devicetable.h. - Use FIELD_GET() for range status checks. - Move configure_irq() closer to probe(), use dev_err_probe() for its error paths. - Fix buffer ops symmetry: postdisable -> predisable. - Drop reg_format_endian from regmap config. - Various alignment and formatting fixes. Link to v5: https://lore.kernel.org/linux-iio/20260313113737.151881-1-email@sirat.me Link to v4: https://lore.kernel.org/linux-iio/20260312183721.40482-1-email@sirat.me Link to v3: https://lore.kernel.org/linux-iio/20260311224044.21480-1-email@sirat.me Link to v2: https://lore.kernel.org/linux-iio/20260308113728.40860-1-email@sirat.me Link to v1: https://lore.kernel.org/linux-iio/20260303090253.42076-1-email@sirat.me Siratul Islam (2): dt-bindings: iio: proximity: add ST VL53L1X ToF sensor iio: proximity: add driver for ST VL53L1X ToF sensor .../bindings/iio/proximity/st,vl53l0x.yaml | 17 +- MAINTAINERS | 7 + drivers/iio/proximity/Kconfig | 15 + drivers/iio/proximity/Makefile | 1 + drivers/iio/proximity/vl53l1x-i2c.c | 820 ++++++++++++++++++ 5 files changed, 856 insertions(+), 4 deletions(-) create mode 100644 drivers/iio/proximity/vl53l1x-i2c.c -- 2.53.0 ^ permalink raw reply [flat|nested] 21+ messages in thread
* [PATCH v6 1/2] dt-bindings: iio: proximity: add ST VL53L1X ToF sensor 2026-03-19 19:07 [PATCH v6 0/2] iio: proximity: add driver for ST VL53L1X ToF sensor Siratul Islam @ 2026-03-19 19:07 ` Siratul Islam 2026-03-20 9:05 ` Krzysztof Kozlowski 2026-03-20 12:52 ` Krzysztof Kozlowski 2026-03-19 19:07 ` [PATCH v6 2/2] iio: proximity: add driver for " Siratul Islam 1 sibling, 2 replies; 21+ messages in thread From: Siratul Islam @ 2026-03-19 19:07 UTC (permalink / raw) To: linux-iio, devicetree Cc: jic23, dlechner, nuno.sa, andy, robh, krzk+dt, conor+dt, linux-kernel, Siratul Islam Add device tree binding documentation for the STMicroelectronics VL53L1X Time-of-Flight ranging sensor connected via I2C. Signed-off-by: Siratul Islam <email@sirat.me> --- .../bindings/iio/proximity/st,vl53l0x.yaml | 17 +++++++++++++---- MAINTAINERS | 6 ++++++ 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/Documentation/devicetree/bindings/iio/proximity/st,vl53l0x.yaml b/Documentation/devicetree/bindings/iio/proximity/st,vl53l0x.yaml index 322befc41de6..de09f709850d 100644 --- a/Documentation/devicetree/bindings/iio/proximity/st,vl53l0x.yaml +++ b/Documentation/devicetree/bindings/iio/proximity/st,vl53l0x.yaml @@ -4,14 +4,17 @@ $id: http://devicetree.org/schemas/iio/proximity/st,vl53l0x.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# -title: ST VL53L0X ToF ranging sensor +title: ST VL53L0X/VL53L1X ToF ranging sensor maintainers: - Song Qiang <songqiang1304521@gmail.com> + - Siratul Islam <email@sirat.me> properties: compatible: - const: st,vl53l0x + enum: + - st,vl53l0x + - st,vl53l1x reg: maxItems: 1 @@ -21,12 +24,17 @@ properties: reset-gpios: maxItems: 1 + description: + Phandle to the XSHUT GPIO. Used for hardware reset. - vdd-supply: true + vdd-supply: + description: + Phandle to the vdd input voltage. This is physically required for operation. required: - compatible - reg + - vdd-supply additionalProperties: false @@ -38,8 +46,9 @@ examples: #size-cells = <0>; proximity@29 { - compatible = "st,vl53l0x"; + compatible = "st,vl53l1x"; reg = <0x29>; + vdd-supply = <®_3v3>; interrupt-parent = <&gpio>; interrupts = <23 IRQ_TYPE_EDGE_FALLING>; }; diff --git a/MAINTAINERS b/MAINTAINERS index 61bf550fd37c..a142a97be4cb 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -25093,6 +25093,12 @@ S: Maintained F: Documentation/devicetree/bindings/iio/proximity/st,vl53l0x.yaml F: drivers/iio/proximity/vl53l0x-i2c.c +ST VL53L1X ToF RANGER(I2C) IIO DRIVER +M: Siratul Islam <email@sirat.me> +L: linux-iio@vger.kernel.org +S: Maintained +F: Documentation/devicetree/bindings/iio/proximity/st,vl53l0x.yaml + STABLE BRANCH M: Greg Kroah-Hartman <gregkh@linuxfoundation.org> M: Sasha Levin <sashal@kernel.org> -- 2.53.0 ^ permalink raw reply related [flat|nested] 21+ messages in thread
* Re: [PATCH v6 1/2] dt-bindings: iio: proximity: add ST VL53L1X ToF sensor 2026-03-19 19:07 ` [PATCH v6 1/2] dt-bindings: iio: proximity: add " Siratul Islam @ 2026-03-20 9:05 ` Krzysztof Kozlowski 2026-03-20 12:10 ` Sirat 2026-03-20 12:52 ` Krzysztof Kozlowski 1 sibling, 1 reply; 21+ messages in thread From: Krzysztof Kozlowski @ 2026-03-20 9:05 UTC (permalink / raw) To: Siratul Islam, linux-iio, devicetree Cc: jic23, dlechner, nuno.sa, andy, robh, krzk+dt, conor+dt, linux-kernel On 19/03/2026 20:07, Siratul Islam wrote: > Add device tree binding documentation for the STMicroelectronics > VL53L1X Time-of-Flight ranging sensor connected via I2C. > > Signed-off-by: Siratul Islam <email@sirat.me> > --- > .../bindings/iio/proximity/st,vl53l0x.yaml | 17 +++++++++++++---- > MAINTAINERS | 6 ++++++ > 2 files changed, 19 insertions(+), 4 deletions(-) > Please read: https://elixir.bootlin.com/linux/v6.12-rc3/source/Documentation/process/submitting-patches.rst#L577 If a tag was not added on purpose, please state why and what changed. Best regards, Krzysztof ^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH v6 1/2] dt-bindings: iio: proximity: add ST VL53L1X ToF sensor 2026-03-20 9:05 ` Krzysztof Kozlowski @ 2026-03-20 12:10 ` Sirat 2026-03-20 12:44 ` Krzysztof Kozlowski 0 siblings, 1 reply; 21+ messages in thread From: Sirat @ 2026-03-20 12:10 UTC (permalink / raw) To: Krzysztof Kozlowski Cc: linux-iio, devicetree, jic23, dlechner, nuno.sa, andy, robh, krzk+dt, conor+dt, linux-kernel On Fri, Mar 20, 2026 at 3:05 PM Krzysztof Kozlowski <krzk@kernel.org> wrote: > > On 19/03/2026 20:07, Siratul Islam wrote: > > Add device tree binding documentation for the STMicroelectronics > > VL53L1X Time-of-Flight ranging sensor connected via I2C. > > > > Signed-off-by: Siratul Islam <email@sirat.me> > > --- > > .../bindings/iio/proximity/st,vl53l0x.yaml | 17 +++++++++++++---- > > MAINTAINERS | 6 ++++++ > > 2 files changed, 19 insertions(+), 4 deletions(-) > > > > Please read: > https://elixir.bootlin.com/linux/v6.12-rc3/source/Documentation/process/submitting-patches.rst#L577 > > If a tag was not added on purpose, please state why and what changed. > > Best regards, > Krzysztof > Hi! The vdd-supply requirement and the pin descriptions changed. I was not sure if it was a substantial change or not so I did not add the tag on purpose. Thanks, Sirat ^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH v6 1/2] dt-bindings: iio: proximity: add ST VL53L1X ToF sensor 2026-03-20 12:10 ` Sirat @ 2026-03-20 12:44 ` Krzysztof Kozlowski 0 siblings, 0 replies; 21+ messages in thread From: Krzysztof Kozlowski @ 2026-03-20 12:44 UTC (permalink / raw) To: Sirat Cc: linux-iio, devicetree, jic23, dlechner, nuno.sa, andy, robh, krzk+dt, conor+dt, linux-kernel On 20/03/2026 13:10, Sirat wrote: > On Fri, Mar 20, 2026 at 3:05 PM Krzysztof Kozlowski <krzk@kernel.org> wrote: >> >> On 19/03/2026 20:07, Siratul Islam wrote: >>> Add device tree binding documentation for the STMicroelectronics >>> VL53L1X Time-of-Flight ranging sensor connected via I2C. >>> >>> Signed-off-by: Siratul Islam <email@sirat.me> >>> --- >>> .../bindings/iio/proximity/st,vl53l0x.yaml | 17 +++++++++++++---- >>> MAINTAINERS | 6 ++++++ >>> 2 files changed, 19 insertions(+), 4 deletions(-) >>> >> >> Please read: >> https://elixir.bootlin.com/linux/v6.12-rc3/source/Documentation/process/submitting-patches.rst#L577 >> >> If a tag was not added on purpose, please state why and what changed. >> >> Best regards, >> Krzysztof >> > > Hi! > > The vdd-supply requirement and the pin descriptions changed. > I was not sure if it was a substantial change or not so I did not add > the tag on purpose. And this must be stated clearly. I linked a document, please read it. Best regards, Krzysztof ^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH v6 1/2] dt-bindings: iio: proximity: add ST VL53L1X ToF sensor 2026-03-19 19:07 ` [PATCH v6 1/2] dt-bindings: iio: proximity: add " Siratul Islam 2026-03-20 9:05 ` Krzysztof Kozlowski @ 2026-03-20 12:52 ` Krzysztof Kozlowski 2026-03-20 13:28 ` Sirat 1 sibling, 1 reply; 21+ messages in thread From: Krzysztof Kozlowski @ 2026-03-20 12:52 UTC (permalink / raw) To: Siratul Islam, linux-iio, devicetree Cc: jic23, dlechner, nuno.sa, andy, robh, krzk+dt, conor+dt, linux-kernel On 19/03/2026 20:07, Siratul Islam wrote: > > properties: > compatible: > - const: st,vl53l0x > + enum: > + - st,vl53l0x > + - st,vl53l1x > > reg: > maxItems: 1 > @@ -21,12 +24,17 @@ properties: > > reset-gpios: > maxItems: 1 > + description: > + Phandle to the XSHUT GPIO. Used for hardware reset. > > - vdd-supply: true > + vdd-supply: > + description: > + Phandle to the vdd input voltage. This is physically required for operation. Drop the description. Schema tells that. > > required: > - compatible > - reg > + - vdd-supply That's an ABI break and nothing in commit msg explains why. You need rationale for this, but most likely everything was working fine before, so supply should not be required. Best regards, Krzysztof ^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH v6 1/2] dt-bindings: iio: proximity: add ST VL53L1X ToF sensor 2026-03-20 12:52 ` Krzysztof Kozlowski @ 2026-03-20 13:28 ` Sirat 2026-03-21 10:00 ` Krzysztof Kozlowski 0 siblings, 1 reply; 21+ messages in thread From: Sirat @ 2026-03-20 13:28 UTC (permalink / raw) To: Krzysztof Kozlowski Cc: linux-iio, devicetree, jic23, dlechner, nuno.sa, andy, robh, krzk+dt, conor+dt, linux-kernel On Fri, Mar 20, 2026 at 6:52 PM Krzysztof Kozlowski <krzk@kernel.org> wrote: > > On 19/03/2026 20:07, Siratul Islam wrote: > > > > properties: > > compatible: > > - const: st,vl53l0x > > + enum: > > + - st,vl53l0x > > + - st,vl53l1x > > > > reg: > > maxItems: 1 > > @@ -21,12 +24,17 @@ properties: > > > > reset-gpios: > > maxItems: 1 > > + description: > > + Phandle to the XSHUT GPIO. Used for hardware reset. > > > > - vdd-supply: true > > + vdd-supply: > > + description: > > + Phandle to the vdd input voltage. This is physically required for operation. > > Drop the description. Schema tells that. > > > > > required: > > - compatible > > - reg > > + - vdd-supply > > That's an ABI break and nothing in commit msg explains why. You need > rationale for this, but most likely everything was working fine before, > so supply should not be required. > The vdd-supply is practically required by both the drivers depending on this binding, But the original driver author did not specify the requirement on the binding. The driver works with or without the binding (it falls back to a dummy regulator when not specified), but specifying it makes the intent clear. Upon discussion, Jonathan and I agreed that we make it required while making sure that the driver gracefully handles older DTs that omit it. If you feel this is still not appropriate, I'm happy to revert to optional. Thanks, Sirat ^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH v6 1/2] dt-bindings: iio: proximity: add ST VL53L1X ToF sensor 2026-03-20 13:28 ` Sirat @ 2026-03-21 10:00 ` Krzysztof Kozlowski 0 siblings, 0 replies; 21+ messages in thread From: Krzysztof Kozlowski @ 2026-03-21 10:00 UTC (permalink / raw) To: Sirat Cc: linux-iio, devicetree, jic23, dlechner, nuno.sa, andy, robh, krzk+dt, conor+dt, linux-kernel On 20/03/2026 14:28, Sirat wrote: > On Fri, Mar 20, 2026 at 6:52 PM Krzysztof Kozlowski <krzk@kernel.org> wrote: >> >> On 19/03/2026 20:07, Siratul Islam wrote: >>> >>> properties: >>> compatible: >>> - const: st,vl53l0x >>> + enum: >>> + - st,vl53l0x >>> + - st,vl53l1x >>> >>> reg: >>> maxItems: 1 >>> @@ -21,12 +24,17 @@ properties: >>> >>> reset-gpios: >>> maxItems: 1 >>> + description: >>> + Phandle to the XSHUT GPIO. Used for hardware reset. >>> >>> - vdd-supply: true >>> + vdd-supply: >>> + description: >>> + Phandle to the vdd input voltage. This is physically required for operation. >> >> Drop the description. Schema tells that. >> >>> >>> required: >>> - compatible >>> - reg >>> + - vdd-supply >> >> That's an ABI break and nothing in commit msg explains why. You need >> rationale for this, but most likely everything was working fine before, >> so supply should not be required. >> > The vdd-supply is practically required by both the drivers depending > on this binding, > But the original driver author did not specify the requirement on the binding. > The driver works with or without the binding (it falls back to a dummy > regulator when not specified), > but specifying it makes the intent clear. "That's an ABI break and nothing in commit msg explains why. " Please don't ask me to quote it third time... See also writing-bindings doc. You must provide rationale for ABI changes. Best regards, Krzysztof ^ permalink raw reply [flat|nested] 21+ messages in thread
* [PATCH v6 2/2] iio: proximity: add driver for ST VL53L1X ToF sensor 2026-03-19 19:07 [PATCH v6 0/2] iio: proximity: add driver for ST VL53L1X ToF sensor Siratul Islam 2026-03-19 19:07 ` [PATCH v6 1/2] dt-bindings: iio: proximity: add " Siratul Islam @ 2026-03-19 19:07 ` Siratul Islam 2026-03-20 8:38 ` Andy Shevchenko ` (2 more replies) 1 sibling, 3 replies; 21+ messages in thread From: Siratul Islam @ 2026-03-19 19:07 UTC (permalink / raw) To: linux-iio, devicetree Cc: jic23, dlechner, nuno.sa, andy, robh, krzk+dt, conor+dt, linux-kernel, Siratul Islam Add support for the STMicroelectronics VL53L1X Time-of-Flight ranging sensor with I2C interface. Signed-off-by: Siratul Islam <email@sirat.me> --- MAINTAINERS | 1 + drivers/iio/proximity/Kconfig | 15 + drivers/iio/proximity/Makefile | 1 + drivers/iio/proximity/vl53l1x-i2c.c | 820 ++++++++++++++++++++++++++++ 4 files changed, 837 insertions(+) create mode 100644 drivers/iio/proximity/vl53l1x-i2c.c diff --git a/MAINTAINERS b/MAINTAINERS index a142a97be4cb..50531a87bf2e 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -25098,6 +25098,7 @@ M: Siratul Islam <email@sirat.me> L: linux-iio@vger.kernel.org S: Maintained F: Documentation/devicetree/bindings/iio/proximity/st,vl53l0x.yaml +F: drivers/iio/proximity/vl53l1x-i2c.c STABLE BRANCH M: Greg Kroah-Hartman <gregkh@linuxfoundation.org> diff --git a/drivers/iio/proximity/Kconfig b/drivers/iio/proximity/Kconfig index 6070974c2c85..bb77fad2a1b3 100644 --- a/drivers/iio/proximity/Kconfig +++ b/drivers/iio/proximity/Kconfig @@ -244,6 +244,21 @@ config VL53L0X_I2C To compile this driver as a module, choose M here: the module will be called vl53l0x-i2c. +config VL53L1X_I2C + tristate "STMicroelectronics VL53L1X ToF ranger sensor (I2C)" + depends on I2C + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + select REGMAP_I2C + select RESET_CONTROLLER + help + Say Y here to build a driver for STMicroelectronics VL53L1X + ToF ranger sensors with i2c interface. + This driver can be used to measure the distance of objects. + + To compile this driver as a module, choose M here: the + module will be called vl53l1x-i2c. + config AW96103 tristate "AW96103/AW96105 Awinic proximity sensor" select REGMAP_I2C diff --git a/drivers/iio/proximity/Makefile b/drivers/iio/proximity/Makefile index 152034d38c49..4352833dd8a4 100644 --- a/drivers/iio/proximity/Makefile +++ b/drivers/iio/proximity/Makefile @@ -23,5 +23,6 @@ obj-$(CONFIG_SX_COMMON) += sx_common.o obj-$(CONFIG_SX9500) += sx9500.o obj-$(CONFIG_VCNL3020) += vcnl3020.o obj-$(CONFIG_VL53L0X_I2C) += vl53l0x-i2c.o +obj-$(CONFIG_VL53L1X_I2C) += vl53l1x-i2c.o obj-$(CONFIG_AW96103) += aw96103.o diff --git a/drivers/iio/proximity/vl53l1x-i2c.c b/drivers/iio/proximity/vl53l1x-i2c.c new file mode 100644 index 000000000000..771598b92e04 --- /dev/null +++ b/drivers/iio/proximity/vl53l1x-i2c.c @@ -0,0 +1,820 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause +/* + * Support for ST VL53L1X FlightSense ToF Ranging Sensor on a i2c bus. + * + * Copyright (C) 2026 Siratul Islam <email@sirat.me> + * + * Datasheet available at + * <https://www.st.com/resource/en/datasheet/vl53l1x.pdf> + * + * Default 7-bit i2c slave address 0x29. + * + * The VL53L1X requires a firmware configuration blob to be loaded at boot. + * Register values for the default configuration are taken from + * ST's VL53L1X Ultra Lite Driver (STSW-IMG009). + */ + +#include <linux/array_size.h> +#include <linux/bits.h> +#include <linux/bitfield.h> +#include <linux/completion.h> +#include <linux/delay.h> +#include <linux/dev_printk.h> +#include <linux/err.h> +#include <linux/i2c.h> +#include <linux/interrupt.h> +#include <linux/math.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/regmap.h> +#include <linux/regulator/consumer.h> +#include <linux/reset.h> +#include <linux/time.h> +#include <linux/types.h> + +#include <asm/byteorder.h> + +#include <linux/iio/buffer.h> +#include <linux/iio/iio.h> +#include <linux/iio/trigger.h> +#include <linux/iio/trigger_consumer.h> +#include <linux/iio/triggered_buffer.h> + +#define VL53L1X_SOFT_RESET 0x0000 +#define VL53L1X_VHV_CONFIG__TIMEOUT_MACROP_LOOP_BOUND 0x0008 +#define VL53L1X_VHV_CONFIG__INIT 0x000B +#define VL53L1X_GPIO_HV_MUX__CTRL 0x0030 +#define VL53L1X_GPIO__TIO_HV_STATUS 0x0031 +#define VL53L1X_SYSTEM__INTERRUPT_CONFIG_GPIO 0x0046 +#define VL53L1X_PHASECAL_CONFIG__TIMEOUT_MACROP 0x004B +#define VL53L1X_RANGE_CONFIG__TIMEOUT_MACROP_A 0x005E +#define VL53L1X_RANGE_CONFIG__VCSEL_PERIOD_A 0x0060 +#define VL53L1X_RANGE_CONFIG__TIMEOUT_MACROP_B 0x0061 +#define VL53L1X_RANGE_CONFIG__VCSEL_PERIOD_B 0x0063 +#define VL53L1X_RANGE_CONFIG__VALID_PHASE_HIGH 0x0069 +#define VL53L1X_SYSTEM__INTERMEASUREMENT_PERIOD 0x006C +#define VL53L1X_SD_CONFIG__WOI_SD0 0x0078 +#define VL53L1X_SD_CONFIG__WOI_SD1 0x0079 +#define VL53L1X_SD_CONFIG__INITIAL_PHASE_SD0 0x007A +#define VL53L1X_SD_CONFIG__INITIAL_PHASE_SD1 0x007B +#define VL53L1X_SYSTEM__INTERRUPT_CLEAR 0x0086 +#define VL53L1X_SYSTEM__MODE_START 0x0087 +#define VL53L1X_RESULT__RANGE_STATUS 0x0089 +#define VL53L1X_RESULT__FINAL_CROSSTALK_CORRECTED_RANGE_MM_SD0 0x0096 +#define VL53L1X_RESULT__OSC_CALIBRATE_VAL 0x00DE +#define VL53L1X_FIRMWARE__SYSTEM_STATUS 0x00E5 +#define VL53L1X_IDENTIFICATION__MODEL_ID 0x010F + +#define VL53L1X_MODEL_ID_VAL 0xEACC + +#define VL53L1X_DEFAULT_CONFIG_ADDR 0x2D + +#define VL53L1X_MODE_START_TIMED 0x40 +#define VL53L1X_MODE_START_STOP 0x00 + +#define VL53L1X_INT_NEW_SAMPLE_READY 0x02 + +#define VL53L1X_GPIO_HV_MUX_POLARITY BIT(4) + +#define VL53L1X_VHV_LOOP_BOUND_TWO 0x09 + +#define VL53L1X_RANGE_STATUS_MASK GENMASK(4, 0) +#define VL53L1X_RANGE_STATUS_VALID 9 + +#define VL53L1X_OSC_CALIBRATE_MASK GENMASK(9, 0) + +/* Inter-measurement period uses PLL divider with 1.075 oscillator correction */ +static const struct u32_fract vl53l1x_osc_correction = { + .numerator = 1075, + .denominator = 1000, +}; + +enum vl53l1x_distance_mode { + VL53L1X_SHORT, + VL53L1X_LONG, +}; + +struct vl53l1x_data { + struct regmap *regmap; + struct completion completion; + struct regulator *vdd_supply; + struct reset_control *xshut_reset; + enum vl53l1x_distance_mode distance_mode; + u8 gpio_polarity; + int irq; +}; + +static const struct regmap_range vl53l1x_volatile_ranges[] = { + regmap_reg_range(VL53L1X_GPIO__TIO_HV_STATUS, + VL53L1X_GPIO__TIO_HV_STATUS), + regmap_reg_range(VL53L1X_RESULT__RANGE_STATUS, + VL53L1X_RESULT__RANGE_STATUS), + regmap_reg_range(VL53L1X_RESULT__FINAL_CROSSTALK_CORRECTED_RANGE_MM_SD0, + VL53L1X_RESULT__FINAL_CROSSTALK_CORRECTED_RANGE_MM_SD0 + 1), + regmap_reg_range(VL53L1X_RESULT__OSC_CALIBRATE_VAL, + VL53L1X_RESULT__OSC_CALIBRATE_VAL + 1), + regmap_reg_range(VL53L1X_FIRMWARE__SYSTEM_STATUS, + VL53L1X_FIRMWARE__SYSTEM_STATUS), +}; + +static const struct regmap_access_table vl53l1x_volatile_table = { + .yes_ranges = vl53l1x_volatile_ranges, + .n_yes_ranges = ARRAY_SIZE(vl53l1x_volatile_ranges), +}; + +static const struct regmap_range vl53l1x_wr_only_ranges[] = { + regmap_reg_range(VL53L1X_SOFT_RESET, VL53L1X_SOFT_RESET), + regmap_reg_range(VL53L1X_SYSTEM__INTERRUPT_CLEAR, + VL53L1X_SYSTEM__MODE_START), +}; + +static const struct regmap_access_table vl53l1x_readable_table = { + .no_ranges = vl53l1x_wr_only_ranges, + .n_no_ranges = ARRAY_SIZE(vl53l1x_wr_only_ranges), +}; + +static const struct regmap_config vl53l1x_regmap_config = { + .reg_bits = 16, + .val_bits = 8, + .max_register = VL53L1X_IDENTIFICATION__MODEL_ID + 1, + .cache_type = REGCACHE_MAPLE, + .volatile_table = &vl53l1x_volatile_table, + .rd_table = &vl53l1x_readable_table, +}; + +static int vl53l1x_read_u16(struct vl53l1x_data *data, u16 reg, u16 *val) +{ + __be16 buf; + int ret; + + ret = regmap_bulk_read(data->regmap, reg, &buf, sizeof(buf)); + if (ret) + return ret; + + *val = be16_to_cpu(buf); + return 0; +} + +static int vl53l1x_write_u16(struct vl53l1x_data *data, u16 reg, u16 val) +{ + __be16 buf = cpu_to_be16(val); + + return regmap_bulk_write(data->regmap, reg, &buf, sizeof(buf)); +} + +static int vl53l1x_write_u32(struct vl53l1x_data *data, u16 reg, u32 val) +{ + __be32 buf = cpu_to_be32(val); + + return regmap_bulk_write(data->regmap, reg, &buf, sizeof(buf)); +} + +static int vl53l1x_clear_irq(struct vl53l1x_data *data) +{ + return regmap_write(data->regmap, VL53L1X_SYSTEM__INTERRUPT_CLEAR, 0x01); +} + +static int vl53l1x_start_ranging(struct vl53l1x_data *data) +{ + int ret; + + ret = vl53l1x_clear_irq(data); + if (ret) + return ret; + + return regmap_write(data->regmap, VL53L1X_SYSTEM__MODE_START, + VL53L1X_MODE_START_TIMED); +} + +static int vl53l1x_stop_ranging(struct vl53l1x_data *data) +{ + return regmap_write(data->regmap, VL53L1X_SYSTEM__MODE_START, + VL53L1X_MODE_START_STOP); +} + +/* + * Default configuration blob from ST's VL53L1X Ultra Lite Driver + * (STSW-IMG009). + */ +static const u8 vl53l1x_default_config[] = { + 0x00, 0x00, 0x00, 0x01, 0x02, 0x00, 0x02, 0x08, /* 0x2d..0x34 */ + 0x00, 0x08, 0x10, 0x01, 0x01, 0x00, 0x00, 0x00, /* 0x35..0x3c */ + 0x00, 0xFF, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x00, /* 0x3d..0x44 */ + 0x00, 0x20, 0x0B, 0x00, 0x00, 0x02, 0x0A, 0x21, /* 0x45..0x4c */ + 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0xC8, /* 0x4d..0x54 */ + 0x00, 0x00, 0x38, 0xFF, 0x01, 0x00, 0x08, 0x00, /* 0x55..0x5c */ + 0x00, 0x01, 0xCC, 0x0F, 0x01, 0xF1, 0x0D, 0x01, /* 0x5d..0x64 */ + 0x68, 0x00, 0x80, 0x08, 0xB8, 0x00, 0x00, 0x00, /* 0x65..0x6c */ + 0x00, 0x0F, 0x89, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x6d..0x74 */ + 0x00, 0x00, 0x01, 0x0F, 0x0D, 0x0E, 0x0E, 0x00, /* 0x75..0x7c */ + 0x00, 0x02, 0xC7, 0xFF, 0x9B, 0x00, 0x00, 0x00, /* 0x7d..0x84 */ + 0x01, 0x00, 0x00, /* 0x85..0x87 */ +}; + +static int vl53l1x_chip_init(struct vl53l1x_data *data) +{ + struct device *dev = regmap_get_device(data->regmap); + unsigned int val; + u16 model_id; + int ret; + + if (!data->xshut_reset) { + ret = regmap_write(data->regmap, VL53L1X_SOFT_RESET, 0x00); + if (ret) + return ret; + fsleep(100); /* conservative reset pulse, no spec */ + + ret = regmap_write(data->regmap, VL53L1X_SOFT_RESET, 0x01); + if (ret) + return ret; + fsleep(1000); /* conservative boot wait, no spec */ + } + + ret = regmap_read_poll_timeout(data->regmap, + VL53L1X_FIRMWARE__SYSTEM_STATUS, val, + val & BIT(0), + 1 * USEC_PER_MSEC, + 100 * USEC_PER_MSEC); + if (ret) + return dev_err_probe(dev, ret, "firmware boot timeout\n"); + + ret = vl53l1x_read_u16(data, VL53L1X_IDENTIFICATION__MODEL_ID, + &model_id); + if (ret) + return ret; + + if (model_id != VL53L1X_MODEL_ID_VAL) + dev_info(dev, "unknown model id: 0x%04x, continuing\n", model_id); + + ret = regmap_bulk_write(data->regmap, VL53L1X_DEFAULT_CONFIG_ADDR, + vl53l1x_default_config, + sizeof(vl53l1x_default_config)); + if (ret) + return ret; + + ret = regmap_read(data->regmap, VL53L1X_GPIO_HV_MUX__CTRL, &val); + if (ret) + return ret; + data->gpio_polarity = !!(val & VL53L1X_GPIO_HV_MUX_POLARITY); + + /* Initial ranging cycle for VHV calibration */ + ret = vl53l1x_start_ranging(data); + if (ret) + return ret; + + /* 1ms poll, 1s timeout covers max timing budgets (per ST Ultra Lite Driver) */ + ret = regmap_read_poll_timeout(data->regmap, + VL53L1X_GPIO__TIO_HV_STATUS, val, + (val & 1) != data->gpio_polarity, + 1 * USEC_PER_MSEC, + 1000 * USEC_PER_MSEC); + if (ret) + return ret; + + ret = vl53l1x_clear_irq(data); + if (ret) + return ret; + + ret = vl53l1x_stop_ranging(data); + if (ret) + return ret; + + ret = regmap_write(data->regmap, + VL53L1X_VHV_CONFIG__TIMEOUT_MACROP_LOOP_BOUND, + VL53L1X_VHV_LOOP_BOUND_TWO); + if (ret) + return ret; + + return regmap_write(data->regmap, VL53L1X_VHV_CONFIG__INIT, 0x00); +} + +static const struct reg_sequence vl53l1x_mode_short[] = { + { VL53L1X_PHASECAL_CONFIG__TIMEOUT_MACROP, 0x14 }, + { VL53L1X_RANGE_CONFIG__VCSEL_PERIOD_A, 0x07 }, + { VL53L1X_RANGE_CONFIG__VCSEL_PERIOD_B, 0x05 }, + { VL53L1X_RANGE_CONFIG__VALID_PHASE_HIGH, 0x38 }, + { VL53L1X_SD_CONFIG__WOI_SD0, 0x07 }, + { VL53L1X_SD_CONFIG__WOI_SD1, 0x05 }, + { VL53L1X_SD_CONFIG__INITIAL_PHASE_SD0, 0x06 }, + { VL53L1X_SD_CONFIG__INITIAL_PHASE_SD1, 0x06 }, +}; + +static const struct reg_sequence vl53l1x_mode_long[] = { + { VL53L1X_PHASECAL_CONFIG__TIMEOUT_MACROP, 0x0A }, + { VL53L1X_RANGE_CONFIG__VCSEL_PERIOD_A, 0x0F }, + { VL53L1X_RANGE_CONFIG__VCSEL_PERIOD_B, 0x0D }, + { VL53L1X_RANGE_CONFIG__VALID_PHASE_HIGH, 0xB8 }, + { VL53L1X_SD_CONFIG__WOI_SD0, 0x0F }, + { VL53L1X_SD_CONFIG__WOI_SD1, 0x0D }, + { VL53L1X_SD_CONFIG__INITIAL_PHASE_SD0, 0x0E }, + { VL53L1X_SD_CONFIG__INITIAL_PHASE_SD1, 0x0E }, +}; + +static const struct { + const struct reg_sequence *regs; + size_t num_regs; +} vl53l1x_mode_configs[] = { + [VL53L1X_SHORT] = { vl53l1x_mode_short, ARRAY_SIZE(vl53l1x_mode_short) }, + [VL53L1X_LONG] = { vl53l1x_mode_long, ARRAY_SIZE(vl53l1x_mode_long) }, +}; + +static int vl53l1x_set_distance_mode(struct vl53l1x_data *data, + enum vl53l1x_distance_mode mode) +{ + int ret; + + if (mode >= ARRAY_SIZE(vl53l1x_mode_configs)) + return -EINVAL; + + ret = regmap_multi_reg_write(data->regmap, + vl53l1x_mode_configs[mode].regs, + vl53l1x_mode_configs[mode].num_regs); + if (ret) + return ret; + + data->distance_mode = mode; + return 0; +} + +/* + * The timing budget controls how long the sensor spends collecting + * a single range measurement. Pre-computed TIMEOUT_MACROP register + * values from ST's VL53L1X Ultra Lite Driver. + */ +static int vl53l1x_set_timing_budget(struct vl53l1x_data *data, u16 budget_ms) +{ + u16 timeout_a, timeout_b; + int ret; + + switch (data->distance_mode) { + case VL53L1X_SHORT: + switch (budget_ms) { + case 15: + timeout_a = 0x001D; + timeout_b = 0x0027; + break; + case 20: + timeout_a = 0x0051; + timeout_b = 0x006E; + break; + case 33: + timeout_a = 0x00D6; + timeout_b = 0x006E; + break; + case 50: + timeout_a = 0x01AE; + timeout_b = 0x01E8; + break; + case 100: + timeout_a = 0x02E1; + timeout_b = 0x0388; + break; + case 200: + timeout_a = 0x03E1; + timeout_b = 0x0496; + break; + case 500: + timeout_a = 0x0591; + timeout_b = 0x05C1; + break; + default: + return -EINVAL; + } + break; + case VL53L1X_LONG: + switch (budget_ms) { + case 20: + timeout_a = 0x001E; + timeout_b = 0x0022; + break; + case 33: + timeout_a = 0x0060; + timeout_b = 0x006E; + break; + case 50: + timeout_a = 0x00AD; + timeout_b = 0x00C6; + break; + case 100: + timeout_a = 0x01CC; + timeout_b = 0x01EA; + break; + case 200: + timeout_a = 0x02D9; + timeout_b = 0x02F8; + break; + case 500: + timeout_a = 0x048F; + timeout_b = 0x04A4; + break; + default: + return -EINVAL; + } + break; + default: + return -EINVAL; + } + + ret = vl53l1x_write_u16(data, VL53L1X_RANGE_CONFIG__TIMEOUT_MACROP_A, + timeout_a); + if (ret) + return ret; + + return vl53l1x_write_u16(data, VL53L1X_RANGE_CONFIG__TIMEOUT_MACROP_B, + timeout_b); +} + +static int vl53l1x_set_inter_measurement_ms(struct vl53l1x_data *data, + u16 period_ms) +{ + u16 osc_calibrate_val; + u16 clock_pll; + u32 inter_meas; + int ret; + + ret = vl53l1x_read_u16(data, VL53L1X_RESULT__OSC_CALIBRATE_VAL, + &osc_calibrate_val); + if (ret) + return ret; + + clock_pll = osc_calibrate_val & VL53L1X_OSC_CALIBRATE_MASK; + inter_meas = (clock_pll * period_ms * vl53l1x_osc_correction.numerator) / + vl53l1x_osc_correction.denominator; + + return vl53l1x_write_u32(data, + VL53L1X_SYSTEM__INTERMEASUREMENT_PERIOD, + inter_meas); +} + +static int vl53l1x_read_proximity(struct vl53l1x_data *data, int *val) +{ + unsigned int range_status; + u16 distance; + int ret; + + if (data->irq) { + reinit_completion(&data->completion); + + ret = vl53l1x_clear_irq(data); + if (ret) + return ret; + + if (!wait_for_completion_timeout(&data->completion, HZ)) + return -ETIMEDOUT; + } else { + unsigned int rdy; + + /* 1ms poll, 1s timeout covers max timing budgets (per ST Ultra Lite Driver) */ + ret = regmap_read_poll_timeout(data->regmap, + VL53L1X_GPIO__TIO_HV_STATUS, rdy, + (rdy & 1) != data->gpio_polarity, + 1 * USEC_PER_MSEC, + 1000 * USEC_PER_MSEC); + if (ret) + return ret; + } + + ret = regmap_read(data->regmap, VL53L1X_RESULT__RANGE_STATUS, + &range_status); + if (ret) + goto clear_irq; + + if (FIELD_GET(VL53L1X_RANGE_STATUS_MASK, range_status) != + VL53L1X_RANGE_STATUS_VALID) { + ret = -EIO; + goto clear_irq; + } + + ret = vl53l1x_read_u16(data, + VL53L1X_RESULT__FINAL_CROSSTALK_CORRECTED_RANGE_MM_SD0, + &distance); + if (ret) + goto clear_irq; + + *val = distance; + +clear_irq: + vl53l1x_clear_irq(data); + return ret; +} + +static const struct iio_chan_spec vl53l1x_channels[] = { + { + .type = IIO_DISTANCE, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE), + .scan_index = 0, + .scan_type = { + .sign = 'u', + .realbits = 16, + .storagebits = 16, + }, + }, + IIO_CHAN_SOFT_TIMESTAMP(1), +}; + +static int vl53l1x_read_raw(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + int *val, int *val2, long mask) +{ + struct vl53l1x_data *data = iio_priv(indio_dev); + int ret; + + if (chan->type != IIO_DISTANCE) + return -EINVAL; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + ret = vl53l1x_read_proximity(data, val); + iio_device_release_direct(indio_dev); + if (ret) + return ret; + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + *val = 0; + *val2 = 1000; + return IIO_VAL_INT_PLUS_MICRO; + default: + return -EINVAL; + } +} + +static const struct iio_info vl53l1x_info = { + .read_raw = vl53l1x_read_raw, + .validate_trigger = iio_validate_own_trigger, +}; + +static irqreturn_t vl53l1x_trigger_handler(int irq, void *priv) +{ + struct iio_poll_func *pf = priv; + struct iio_dev *indio_dev = pf->indio_dev; + struct vl53l1x_data *data = iio_priv(indio_dev); + struct { + u16 distance; + aligned_s64 timestamp; + } scan = {}; + unsigned int range_status; + int ret; + + ret = regmap_read(data->regmap, VL53L1X_RESULT__RANGE_STATUS, + &range_status); + if (ret) + goto notify_and_clear_irq; + if (FIELD_GET(VL53L1X_RANGE_STATUS_MASK, range_status) != + VL53L1X_RANGE_STATUS_VALID) + goto notify_and_clear_irq; + + ret = vl53l1x_read_u16(data, + VL53L1X_RESULT__FINAL_CROSSTALK_CORRECTED_RANGE_MM_SD0, + &scan.distance); + if (ret) + goto notify_and_clear_irq; + + iio_push_to_buffers_with_timestamp(indio_dev, &scan, + iio_get_time_ns(indio_dev)); + +notify_and_clear_irq: + iio_trigger_notify_done(indio_dev->trig); + vl53l1x_clear_irq(data); + + return IRQ_HANDLED; +} + +static irqreturn_t vl53l1x_irq_handler(int irq, void *priv) +{ + struct iio_dev *indio_dev = priv; + struct vl53l1x_data *data = iio_priv(indio_dev); + + if (iio_buffer_enabled(indio_dev)) + iio_trigger_poll(indio_dev->trig); + else + complete(&data->completion); + + return IRQ_HANDLED; +} + +static int vl53l1x_buffer_postenable(struct iio_dev *indio_dev) +{ + struct vl53l1x_data *data = iio_priv(indio_dev); + + return vl53l1x_start_ranging(data); +} + +static int vl53l1x_buffer_predisable(struct iio_dev *indio_dev) +{ + struct vl53l1x_data *data = iio_priv(indio_dev); + int ret; + + ret = vl53l1x_stop_ranging(data); + if (ret) + return ret; + + /* + * Best-effort drain. Wait for any in-flight IRQ before clearing. + * Ignoring timeout as device is shutting down anyway. + */ + reinit_completion(&data->completion); + wait_for_completion_timeout(&data->completion, HZ / 10); + + return vl53l1x_clear_irq(data); +} + +static const struct iio_buffer_setup_ops vl53l1x_buffer_setup_ops = { + .postenable = &vl53l1x_buffer_postenable, + .predisable = &vl53l1x_buffer_predisable, +}; + +static const struct iio_trigger_ops vl53l1x_trigger_ops = { + .validate_device = iio_trigger_validate_own_device, +}; + +static void vl53l1x_stop_ranging_action(void *_data) +{ + vl53l1x_stop_ranging(_data); +} + +static void vl53l1x_power_off(void *_data) +{ + struct vl53l1x_data *data = _data; + + reset_control_assert(data->xshut_reset); + regulator_disable(data->vdd_supply); +} + +static int vl53l1x_power_on(struct vl53l1x_data *data) +{ + int ret; + + ret = regulator_enable(data->vdd_supply); + if (ret) + return ret; + + ret = reset_control_deassert(data->xshut_reset); + if (ret) { + regulator_disable(data->vdd_supply); + return ret; + } + /* + * 1.2 ms max boot duration. + * Datasheet Section 3.6 "Power up and boot sequence". + */ + fsleep(1200); + + return 0; +} + +static int vl53l1x_configure_irq(struct device *dev, int irq, + struct iio_dev *indio_dev) +{ + struct vl53l1x_data *data = iio_priv(indio_dev); + int ret; + + ret = devm_request_irq(dev, irq, vl53l1x_irq_handler, IRQF_NO_THREAD, + indio_dev->name, indio_dev); + if (ret) + return ret; + + ret = regmap_write(data->regmap, VL53L1X_SYSTEM__INTERRUPT_CONFIG_GPIO, + VL53L1X_INT_NEW_SAMPLE_READY); + if (ret) + return dev_err_probe(dev, ret, + "failed to configure IRQ\n"); + + return 0; +} + +static int vl53l1x_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct vl53l1x_data *data; + struct iio_dev *indio_dev; + int ret; + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_READ_I2C_BLOCK | + I2C_FUNC_SMBUS_BYTE_DATA)) + return -EOPNOTSUPP; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*data)); + if (!indio_dev) + return -ENOMEM; + + data = iio_priv(indio_dev); + data->irq = client->irq; + + data->regmap = devm_regmap_init_i2c(client, &vl53l1x_regmap_config); + if (IS_ERR(data->regmap)) + return dev_err_probe(dev, PTR_ERR(data->regmap), + "regmap initialization failed\n"); + + /* + * vdd-supply is required in the DT binding but we + * continue if it is missing to support older DTs. + */ + data->vdd_supply = devm_regulator_get(dev, "vdd"); + if (IS_ERR(data->vdd_supply)) + return dev_err_probe(dev, PTR_ERR(data->vdd_supply), + "Unable to get VDD regulator\n"); + + data->xshut_reset = devm_reset_control_get_optional_exclusive(dev, NULL); + if (IS_ERR(data->xshut_reset)) + return dev_err_probe(dev, PTR_ERR(data->xshut_reset), + "Cannot get reset control\n"); + + ret = vl53l1x_power_on(data); + if (ret) + return dev_err_probe(dev, ret, "Failed to power on the chip\n"); + + ret = devm_add_action_or_reset(dev, vl53l1x_power_off, data); + if (ret) + return ret; + + ret = vl53l1x_chip_init(data); + if (ret) + return ret; + + ret = vl53l1x_set_distance_mode(data, VL53L1X_LONG); + if (ret) + return ret; + + ret = vl53l1x_set_timing_budget(data, 50); + if (ret) + return ret; + + ret = vl53l1x_set_inter_measurement_ms(data, 50); + if (ret) + return ret; + + ret = vl53l1x_start_ranging(data); + if (ret) + return ret; + + ret = devm_add_action_or_reset(dev, vl53l1x_stop_ranging_action, data); + if (ret) + return ret; + + indio_dev->name = "vl53l1x"; + indio_dev->info = &vl53l1x_info; + indio_dev->channels = vl53l1x_channels; + indio_dev->num_channels = ARRAY_SIZE(vl53l1x_channels); + indio_dev->modes = INDIO_DIRECT_MODE; + + if (client->irq) { + struct iio_trigger *trig; + + init_completion(&data->completion); + + trig = devm_iio_trigger_alloc(dev, "%s-dev%d", indio_dev->name, + iio_device_id(indio_dev)); + if (!trig) + return -ENOMEM; + + trig->ops = &vl53l1x_trigger_ops; + iio_trigger_set_drvdata(trig, indio_dev); + ret = devm_iio_trigger_register(dev, trig); + if (ret) + return ret; + + indio_dev->trig = iio_trigger_get(trig); + + ret = vl53l1x_configure_irq(dev, client->irq, indio_dev); + if (ret) + return ret; + + ret = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL, + &vl53l1x_trigger_handler, + &vl53l1x_buffer_setup_ops); + if (ret) + return ret; + } + + return devm_iio_device_register(dev, indio_dev); +} + +static const struct i2c_device_id vl53l1x_id[] = { + { "vl53l1x" }, + { } +}; +MODULE_DEVICE_TABLE(i2c, vl53l1x_id); + +static const struct of_device_id st_vl53l1x_dt_match[] = { + { .compatible = "st,vl53l1x" }, + { } +}; +MODULE_DEVICE_TABLE(of, st_vl53l1x_dt_match); + +static struct i2c_driver vl53l1x_driver = { + .driver = { + .name = "vl53l1x-i2c", + .of_match_table = st_vl53l1x_dt_match, + }, + .probe = vl53l1x_probe, + .id_table = vl53l1x_id, +}; +module_i2c_driver(vl53l1x_driver); + +MODULE_AUTHOR("Siratul Islam <email@sirat.me>"); +MODULE_DESCRIPTION("ST VL53L1X ToF ranging sensor driver"); +MODULE_LICENSE("Dual BSD/GPL"); -- 2.53.0 ^ permalink raw reply related [flat|nested] 21+ messages in thread
* Re: [PATCH v6 2/2] iio: proximity: add driver for ST VL53L1X ToF sensor 2026-03-19 19:07 ` [PATCH v6 2/2] iio: proximity: add driver for " Siratul Islam @ 2026-03-20 8:38 ` Andy Shevchenko 2026-03-21 17:09 ` David Lechner 2026-03-21 17:21 ` Jonathan Cameron 2 siblings, 0 replies; 21+ messages in thread From: Andy Shevchenko @ 2026-03-20 8:38 UTC (permalink / raw) To: Siratul Islam Cc: linux-iio, devicetree, jic23, dlechner, nuno.sa, andy, robh, krzk+dt, conor+dt, linux-kernel On Fri, Mar 20, 2026 at 01:07:14AM +0600, Siratul Islam wrote: > Add support for the STMicroelectronics VL53L1X Time-of-Flight > ranging sensor with I2C interface. Reviewed-by: Andy Shevchenko <andriy.shevchenko@intel.com> A couple of small style amendments below, but no need to resend only for addressing them — Jonathan can tweak if he agrees on that. ... > +static const struct regmap_range vl53l1x_wr_only_ranges[] = { > + regmap_reg_range(VL53L1X_SOFT_RESET, VL53L1X_SOFT_RESET), > + regmap_reg_range(VL53L1X_SYSTEM__INTERRUPT_CLEAR, > + VL53L1X_SYSTEM__MODE_START), > +}; > + > +static const struct regmap_access_table vl53l1x_readable_table = { > + .no_ranges = vl53l1x_wr_only_ranges, I would spell 'write' in full in the name of the array variable. > + .n_no_ranges = ARRAY_SIZE(vl53l1x_wr_only_ranges), > +}; ... > + ret = regmap_write(data->regmap, VL53L1X_SYSTEM__INTERRUPT_CONFIG_GPIO, > + VL53L1X_INT_NEW_SAMPLE_READY); > + if (ret) > + return dev_err_probe(dev, ret, > + "failed to configure IRQ\n"); It's perfectly one line. -- With Best Regards, Andy Shevchenko ^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH v6 2/2] iio: proximity: add driver for ST VL53L1X ToF sensor 2026-03-19 19:07 ` [PATCH v6 2/2] iio: proximity: add driver for " Siratul Islam 2026-03-20 8:38 ` Andy Shevchenko @ 2026-03-21 17:09 ` David Lechner 2026-03-21 22:39 ` Sirat 2026-03-21 17:21 ` Jonathan Cameron 2 siblings, 1 reply; 21+ messages in thread From: David Lechner @ 2026-03-21 17:09 UTC (permalink / raw) To: Siratul Islam, linux-iio, devicetree Cc: jic23, nuno.sa, andy, robh, krzk+dt, conor+dt, linux-kernel On 3/19/26 2:07 PM, Siratul Islam wrote: > Add support for the STMicroelectronics VL53L1X Time-of-Flight > ranging sensor with I2C interface. > > Signed-off-by: Siratul Islam <email@sirat.me> > --- > MAINTAINERS | 1 + > drivers/iio/proximity/Kconfig | 15 + > drivers/iio/proximity/Makefile | 1 + > drivers/iio/proximity/vl53l1x-i2c.c | 820 ++++++++++++++++++++++++++++ > 4 files changed, 837 insertions(+) > create mode 100644 drivers/iio/proximity/vl53l1x-i2c.c > > diff --git a/MAINTAINERS b/MAINTAINERS > index a142a97be4cb..50531a87bf2e 100644 > --- a/MAINTAINERS > +++ b/MAINTAINERS > @@ -25098,6 +25098,7 @@ M: Siratul Islam <email@sirat.me> > L: linux-iio@vger.kernel.org > S: Maintained > F: Documentation/devicetree/bindings/iio/proximity/st,vl53l0x.yaml > +F: drivers/iio/proximity/vl53l1x-i2c.c > > STABLE BRANCH > M: Greg Kroah-Hartman <gregkh@linuxfoundation.org> > diff --git a/drivers/iio/proximity/Kconfig b/drivers/iio/proximity/Kconfig > index 6070974c2c85..bb77fad2a1b3 100644 > --- a/drivers/iio/proximity/Kconfig > +++ b/drivers/iio/proximity/Kconfig > @@ -244,6 +244,21 @@ config VL53L0X_I2C > To compile this driver as a module, choose M here: the > module will be called vl53l0x-i2c. > > +config VL53L1X_I2C > + tristate "STMicroelectronics VL53L1X ToF ranger sensor (I2C)" > + depends on I2C > + select IIO_BUFFER > + select IIO_TRIGGERED_BUFFER > + select REGMAP_I2C > + select RESET_CONTROLLER > + help > + Say Y here to build a driver for STMicroelectronics VL53L1X > + ToF ranger sensors with i2c interface. > + This driver can be used to measure the distance of objects. > + > + To compile this driver as a module, choose M here: the > + module will be called vl53l1x-i2c. > + > config AW96103 > tristate "AW96103/AW96105 Awinic proximity sensor" > select REGMAP_I2C > diff --git a/drivers/iio/proximity/Makefile b/drivers/iio/proximity/Makefile > index 152034d38c49..4352833dd8a4 100644 > --- a/drivers/iio/proximity/Makefile > +++ b/drivers/iio/proximity/Makefile > @@ -23,5 +23,6 @@ obj-$(CONFIG_SX_COMMON) += sx_common.o > obj-$(CONFIG_SX9500) += sx9500.o > obj-$(CONFIG_VCNL3020) += vcnl3020.o > obj-$(CONFIG_VL53L0X_I2C) += vl53l0x-i2c.o > +obj-$(CONFIG_VL53L1X_I2C) += vl53l1x-i2c.o > obj-$(CONFIG_AW96103) += aw96103.o > > diff --git a/drivers/iio/proximity/vl53l1x-i2c.c b/drivers/iio/proximity/vl53l1x-i2c.c > new file mode 100644 > index 000000000000..771598b92e04 > --- /dev/null > +++ b/drivers/iio/proximity/vl53l1x-i2c.c > @@ -0,0 +1,820 @@ > +// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause > +/* > + * Support for ST VL53L1X FlightSense ToF Ranging Sensor on a i2c bus. > + * > + * Copyright (C) 2026 Siratul Islam <email@sirat.me> > + * > + * Datasheet available at > + * <https://www.st.com/resource/en/datasheet/vl53l1x.pdf> > + * > + * Default 7-bit i2c slave address 0x29. > + * > + * The VL53L1X requires a firmware configuration blob to be loaded at boot. > + * Register values for the default configuration are taken from > + * ST's VL53L1X Ultra Lite Driver (STSW-IMG009). > + */ > + > +#include <linux/array_size.h> > +#include <linux/bits.h> > +#include <linux/bitfield.h> > +#include <linux/completion.h> > +#include <linux/delay.h> > +#include <linux/dev_printk.h> > +#include <linux/err.h> > +#include <linux/i2c.h> > +#include <linux/interrupt.h> > +#include <linux/math.h> > +#include <linux/mod_devicetable.h> > +#include <linux/module.h> > +#include <linux/regmap.h> > +#include <linux/regulator/consumer.h> > +#include <linux/reset.h> > +#include <linux/time.h> > +#include <linux/types.h> > + > +#include <asm/byteorder.h> > + > +#include <linux/iio/buffer.h> > +#include <linux/iio/iio.h> > +#include <linux/iio/trigger.h> > +#include <linux/iio/trigger_consumer.h> > +#include <linux/iio/triggered_buffer.h> > + > +#define VL53L1X_SOFT_RESET 0x0000 > +#define VL53L1X_VHV_CONFIG__TIMEOUT_MACROP_LOOP_BOUND 0x0008 > +#define VL53L1X_VHV_CONFIG__INIT 0x000B > +#define VL53L1X_GPIO_HV_MUX__CTRL 0x0030 > +#define VL53L1X_GPIO__TIO_HV_STATUS 0x0031 > +#define VL53L1X_SYSTEM__INTERRUPT_CONFIG_GPIO 0x0046 > +#define VL53L1X_PHASECAL_CONFIG__TIMEOUT_MACROP 0x004B > +#define VL53L1X_RANGE_CONFIG__TIMEOUT_MACROP_A 0x005E > +#define VL53L1X_RANGE_CONFIG__VCSEL_PERIOD_A 0x0060 > +#define VL53L1X_RANGE_CONFIG__TIMEOUT_MACROP_B 0x0061 > +#define VL53L1X_RANGE_CONFIG__VCSEL_PERIOD_B 0x0063 > +#define VL53L1X_RANGE_CONFIG__VALID_PHASE_HIGH 0x0069 > +#define VL53L1X_SYSTEM__INTERMEASUREMENT_PERIOD 0x006C > +#define VL53L1X_SD_CONFIG__WOI_SD0 0x0078 > +#define VL53L1X_SD_CONFIG__WOI_SD1 0x0079 > +#define VL53L1X_SD_CONFIG__INITIAL_PHASE_SD0 0x007A > +#define VL53L1X_SD_CONFIG__INITIAL_PHASE_SD1 0x007B > +#define VL53L1X_SYSTEM__INTERRUPT_CLEAR 0x0086 > +#define VL53L1X_SYSTEM__MODE_START 0x0087 > +#define VL53L1X_RESULT__RANGE_STATUS 0x0089 > +#define VL53L1X_RESULT__FINAL_CROSSTALK_CORRECTED_RANGE_MM_SD0 0x0096 > +#define VL53L1X_RESULT__OSC_CALIBRATE_VAL 0x00DE > +#define VL53L1X_FIRMWARE__SYSTEM_STATUS 0x00E5 > +#define VL53L1X_IDENTIFICATION__MODEL_ID 0x010F It would be helpful if all of these satarted with VL53L1X_REG_ so that we know that they are register addresses. > + > +#define VL53L1X_MODEL_ID_VAL 0xEACC > + > +#define VL53L1X_DEFAULT_CONFIG_ADDR 0x2D Why is this one not with the rest of the register addresses? > + > +#define VL53L1X_MODE_START_TIMED 0x40 > +#define VL53L1X_MODE_START_STOP 0x00 > + > +#define VL53L1X_INT_NEW_SAMPLE_READY 0x02 > + > +#define VL53L1X_GPIO_HV_MUX_POLARITY BIT(4) > + > +#define VL53L1X_VHV_LOOP_BOUND_TWO 0x09 > + > +#define VL53L1X_RANGE_STATUS_MASK GENMASK(4, 0) > +#define VL53L1X_RANGE_STATUS_VALID 9 > + > +#define VL53L1X_OSC_CALIBRATE_MASK GENMASK(9, 0) > + > +/* Inter-measurement period uses PLL divider with 1.075 oscillator correction */ > +static const struct u32_fract vl53l1x_osc_correction = { > + .numerator = 1075, > + .denominator = 1000, > +}; > + > +enum vl53l1x_distance_mode { > + VL53L1X_SHORT, > + VL53L1X_LONG, > +}; > + > +struct vl53l1x_data { > + struct regmap *regmap; > + struct completion completion; > + struct regulator *vdd_supply; > + struct reset_control *xshut_reset; > + enum vl53l1x_distance_mode distance_mode; > + u8 gpio_polarity; > + int irq; > +}; > + > +static const struct regmap_range vl53l1x_volatile_ranges[] = { > + regmap_reg_range(VL53L1X_GPIO__TIO_HV_STATUS, > + VL53L1X_GPIO__TIO_HV_STATUS), > + regmap_reg_range(VL53L1X_RESULT__RANGE_STATUS, > + VL53L1X_RESULT__RANGE_STATUS), > + regmap_reg_range(VL53L1X_RESULT__FINAL_CROSSTALK_CORRECTED_RANGE_MM_SD0, > + VL53L1X_RESULT__FINAL_CROSSTALK_CORRECTED_RANGE_MM_SD0 + 1), > + regmap_reg_range(VL53L1X_RESULT__OSC_CALIBRATE_VAL, > + VL53L1X_RESULT__OSC_CALIBRATE_VAL + 1), > + regmap_reg_range(VL53L1X_FIRMWARE__SYSTEM_STATUS, > + VL53L1X_FIRMWARE__SYSTEM_STATUS), > +}; > + > +static const struct regmap_access_table vl53l1x_volatile_table = { > + .yes_ranges = vl53l1x_volatile_ranges, > + .n_yes_ranges = ARRAY_SIZE(vl53l1x_volatile_ranges), > +}; > + > +static const struct regmap_range vl53l1x_wr_only_ranges[] = { > + regmap_reg_range(VL53L1X_SOFT_RESET, VL53L1X_SOFT_RESET), > + regmap_reg_range(VL53L1X_SYSTEM__INTERRUPT_CLEAR, > + VL53L1X_SYSTEM__MODE_START), > +}; > + > +static const struct regmap_access_table vl53l1x_readable_table = { > + .no_ranges = vl53l1x_wr_only_ranges, > + .n_no_ranges = ARRAY_SIZE(vl53l1x_wr_only_ranges), > +}; > + > +static const struct regmap_config vl53l1x_regmap_config = { > + .reg_bits = 16, > + .val_bits = 8, > + .max_register = VL53L1X_IDENTIFICATION__MODEL_ID + 1, Why + 1? > + .cache_type = REGCACHE_MAPLE, > + .volatile_table = &vl53l1x_volatile_table, > + .rd_table = &vl53l1x_readable_table, > +}; > + > +static int vl53l1x_read_u16(struct vl53l1x_data *data, u16 reg, u16 *val) > +{ > + __be16 buf; > + int ret; > + > + ret = regmap_bulk_read(data->regmap, reg, &buf, sizeof(buf)); > + if (ret) > + return ret; > + > + *val = be16_to_cpu(buf); > + return 0; > +} > + > +static int vl53l1x_write_u16(struct vl53l1x_data *data, u16 reg, u16 val) > +{ > + __be16 buf = cpu_to_be16(val); > + > + return regmap_bulk_write(data->regmap, reg, &buf, sizeof(buf)); > +} > + > +static int vl53l1x_write_u32(struct vl53l1x_data *data, u16 reg, u32 val) > +{ > + __be32 buf = cpu_to_be32(val); > + > + return regmap_bulk_write(data->regmap, reg, &buf, sizeof(buf)); > +} > + > +static int vl53l1x_clear_irq(struct vl53l1x_data *data) > +{ > + return regmap_write(data->regmap, VL53L1X_SYSTEM__INTERRUPT_CLEAR, 0x01); > +} > + > +static int vl53l1x_start_ranging(struct vl53l1x_data *data) > +{ > + int ret; > + > + ret = vl53l1x_clear_irq(data); > + if (ret) > + return ret; > + > + return regmap_write(data->regmap, VL53L1X_SYSTEM__MODE_START, > + VL53L1X_MODE_START_TIMED); > +} > + > +static int vl53l1x_stop_ranging(struct vl53l1x_data *data) > +{ > + return regmap_write(data->regmap, VL53L1X_SYSTEM__MODE_START, > + VL53L1X_MODE_START_STOP); > +} > + > +/* > + * Default configuration blob from ST's VL53L1X Ultra Lite Driver > + * (STSW-IMG009). > + */ > +static const u8 vl53l1x_default_config[] = { > + 0x00, 0x00, 0x00, 0x01, 0x02, 0x00, 0x02, 0x08, /* 0x2d..0x34 */ > + 0x00, 0x08, 0x10, 0x01, 0x01, 0x00, 0x00, 0x00, /* 0x35..0x3c */ > + 0x00, 0xFF, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x00, /* 0x3d..0x44 */ > + 0x00, 0x20, 0x0B, 0x00, 0x00, 0x02, 0x0A, 0x21, /* 0x45..0x4c */ > + 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0xC8, /* 0x4d..0x54 */ > + 0x00, 0x00, 0x38, 0xFF, 0x01, 0x00, 0x08, 0x00, /* 0x55..0x5c */ > + 0x00, 0x01, 0xCC, 0x0F, 0x01, 0xF1, 0x0D, 0x01, /* 0x5d..0x64 */ > + 0x68, 0x00, 0x80, 0x08, 0xB8, 0x00, 0x00, 0x00, /* 0x65..0x6c */ > + 0x00, 0x0F, 0x89, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x6d..0x74 */ > + 0x00, 0x00, 0x01, 0x0F, 0x0D, 0x0E, 0x0E, 0x00, /* 0x75..0x7c */ > + 0x00, 0x02, 0xC7, 0xFF, 0x9B, 0x00, 0x00, 0x00, /* 0x7d..0x84 */ > + 0x01, 0x00, 0x00, /* 0x85..0x87 */ > +}; What do the comments mean? register numbers? > + > +static int vl53l1x_chip_init(struct vl53l1x_data *data) > +{ > + struct device *dev = regmap_get_device(data->regmap); > + unsigned int val; > + u16 model_id; > + int ret; > + > + if (!data->xshut_reset) { > + ret = regmap_write(data->regmap, VL53L1X_SOFT_RESET, 0x00); > + if (ret) > + return ret; > + fsleep(100); /* conservative reset pulse, no spec */ > + > + ret = regmap_write(data->regmap, VL53L1X_SOFT_RESET, 0x01); > + if (ret) > + return ret; > + fsleep(1000); /* conservative boot wait, no spec */ > + } > + > + ret = regmap_read_poll_timeout(data->regmap, > + VL53L1X_FIRMWARE__SYSTEM_STATUS, val, > + val & BIT(0), > + 1 * USEC_PER_MSEC, > + 100 * USEC_PER_MSEC); > + if (ret) > + return dev_err_probe(dev, ret, "firmware boot timeout\n"); > + > + ret = vl53l1x_read_u16(data, VL53L1X_IDENTIFICATION__MODEL_ID, > + &model_id); > + if (ret) > + return ret; > + > + if (model_id != VL53L1X_MODEL_ID_VAL) > + dev_info(dev, "unknown model id: 0x%04x, continuing\n", model_id); > + > + ret = regmap_bulk_write(data->regmap, VL53L1X_DEFAULT_CONFIG_ADDR, > + vl53l1x_default_config, > + sizeof(vl53l1x_default_config)); > + if (ret) > + return ret; > + > + ret = regmap_read(data->regmap, VL53L1X_GPIO_HV_MUX__CTRL, &val); > + if (ret) > + return ret; > + data->gpio_polarity = !!(val & VL53L1X_GPIO_HV_MUX_POLARITY); > + > + /* Initial ranging cycle for VHV calibration */ > + ret = vl53l1x_start_ranging(data); > + if (ret) > + return ret; > + > + /* 1ms poll, 1s timeout covers max timing budgets (per ST Ultra Lite Driver) */ > + ret = regmap_read_poll_timeout(data->regmap, > + VL53L1X_GPIO__TIO_HV_STATUS, val, > + (val & 1) != data->gpio_polarity, > + 1 * USEC_PER_MSEC, > + 1000 * USEC_PER_MSEC); > + if (ret) > + return ret; > + > + ret = vl53l1x_clear_irq(data); > + if (ret) > + return ret; > + > + ret = vl53l1x_stop_ranging(data); > + if (ret) > + return ret; > + > + ret = regmap_write(data->regmap, > + VL53L1X_VHV_CONFIG__TIMEOUT_MACROP_LOOP_BOUND, > + VL53L1X_VHV_LOOP_BOUND_TWO); > + if (ret) > + return ret; > + > + return regmap_write(data->regmap, VL53L1X_VHV_CONFIG__INIT, 0x00); > +} > + > +static const struct reg_sequence vl53l1x_mode_short[] = { > + { VL53L1X_PHASECAL_CONFIG__TIMEOUT_MACROP, 0x14 }, > + { VL53L1X_RANGE_CONFIG__VCSEL_PERIOD_A, 0x07 }, > + { VL53L1X_RANGE_CONFIG__VCSEL_PERIOD_B, 0x05 }, > + { VL53L1X_RANGE_CONFIG__VALID_PHASE_HIGH, 0x38 }, > + { VL53L1X_SD_CONFIG__WOI_SD0, 0x07 }, > + { VL53L1X_SD_CONFIG__WOI_SD1, 0x05 }, > + { VL53L1X_SD_CONFIG__INITIAL_PHASE_SD0, 0x06 }, > + { VL53L1X_SD_CONFIG__INITIAL_PHASE_SD1, 0x06 }, > +}; > + > +static const struct reg_sequence vl53l1x_mode_long[] = { > + { VL53L1X_PHASECAL_CONFIG__TIMEOUT_MACROP, 0x0A }, > + { VL53L1X_RANGE_CONFIG__VCSEL_PERIOD_A, 0x0F }, > + { VL53L1X_RANGE_CONFIG__VCSEL_PERIOD_B, 0x0D }, > + { VL53L1X_RANGE_CONFIG__VALID_PHASE_HIGH, 0xB8 }, > + { VL53L1X_SD_CONFIG__WOI_SD0, 0x0F }, > + { VL53L1X_SD_CONFIG__WOI_SD1, 0x0D }, > + { VL53L1X_SD_CONFIG__INITIAL_PHASE_SD0, 0x0E }, > + { VL53L1X_SD_CONFIG__INITIAL_PHASE_SD1, 0x0E }, > +}; > + > +static const struct { > + const struct reg_sequence *regs; > + size_t num_regs; > +} vl53l1x_mode_configs[] = { > + [VL53L1X_SHORT] = { vl53l1x_mode_short, ARRAY_SIZE(vl53l1x_mode_short) }, > + [VL53L1X_LONG] = { vl53l1x_mode_long, ARRAY_SIZE(vl53l1x_mode_long) }, > +}; > + > +static int vl53l1x_set_distance_mode(struct vl53l1x_data *data, > + enum vl53l1x_distance_mode mode) > +{ > + int ret; > + > + if (mode >= ARRAY_SIZE(vl53l1x_mode_configs)) > + return -EINVAL; > + > + ret = regmap_multi_reg_write(data->regmap, > + vl53l1x_mode_configs[mode].regs, > + vl53l1x_mode_configs[mode].num_regs); > + if (ret) > + return ret; > + > + data->distance_mode = mode; > + return 0; > +} > + > +/* > + * The timing budget controls how long the sensor spends collecting > + * a single range measurement. Pre-computed TIMEOUT_MACROP register > + * values from ST's VL53L1X Ultra Lite Driver. > + */ > +static int vl53l1x_set_timing_budget(struct vl53l1x_data *data, u16 budget_ms) > +{ > + u16 timeout_a, timeout_b; > + int ret; > + > + switch (data->distance_mode) { > + case VL53L1X_SHORT: > + switch (budget_ms) { > + case 15: > + timeout_a = 0x001D; > + timeout_b = 0x0027; > + break; > + case 20: > + timeout_a = 0x0051; > + timeout_b = 0x006E; > + break; > + case 33: > + timeout_a = 0x00D6; > + timeout_b = 0x006E; > + break; > + case 50: > + timeout_a = 0x01AE; > + timeout_b = 0x01E8; > + break; > + case 100: > + timeout_a = 0x02E1; > + timeout_b = 0x0388; > + break; > + case 200: > + timeout_a = 0x03E1; > + timeout_b = 0x0496; > + break; > + case 500: > + timeout_a = 0x0591; > + timeout_b = 0x05C1; > + break; > + default: > + return -EINVAL; > + } > + break; > + case VL53L1X_LONG: > + switch (budget_ms) { > + case 20: > + timeout_a = 0x001E; > + timeout_b = 0x0022; > + break; > + case 33: > + timeout_a = 0x0060; > + timeout_b = 0x006E; > + break; > + case 50: > + timeout_a = 0x00AD; > + timeout_b = 0x00C6; > + break; > + case 100: > + timeout_a = 0x01CC; > + timeout_b = 0x01EA; > + break; > + case 200: > + timeout_a = 0x02D9; > + timeout_b = 0x02F8; > + break; > + case 500: > + timeout_a = 0x048F; > + timeout_b = 0x04A4; > + break; > + default: > + return -EINVAL; > + } > + break; > + default: > + return -EINVAL; > + } > + > + ret = vl53l1x_write_u16(data, VL53L1X_RANGE_CONFIG__TIMEOUT_MACROP_A, > + timeout_a); > + if (ret) > + return ret; > + > + return vl53l1x_write_u16(data, VL53L1X_RANGE_CONFIG__TIMEOUT_MACROP_B, > + timeout_b); > +} > + > +static int vl53l1x_set_inter_measurement_ms(struct vl53l1x_data *data, > + u16 period_ms) > +{ > + u16 osc_calibrate_val; > + u16 clock_pll; > + u32 inter_meas; > + int ret; > + > + ret = vl53l1x_read_u16(data, VL53L1X_RESULT__OSC_CALIBRATE_VAL, > + &osc_calibrate_val); > + if (ret) > + return ret; > + > + clock_pll = osc_calibrate_val & VL53L1X_OSC_CALIBRATE_MASK; > + inter_meas = (clock_pll * period_ms * vl53l1x_osc_correction.numerator) / > + vl53l1x_osc_correction.denominator; > + > + return vl53l1x_write_u32(data, > + VL53L1X_SYSTEM__INTERMEASUREMENT_PERIOD, > + inter_meas); > +} > + > +static int vl53l1x_read_proximity(struct vl53l1x_data *data, int *val) > +{ > + unsigned int range_status; > + u16 distance; > + int ret; > + > + if (data->irq) { > + reinit_completion(&data->completion); > + > + ret = vl53l1x_clear_irq(data); > + if (ret) > + return ret; > + > + if (!wait_for_completion_timeout(&data->completion, HZ)) > + return -ETIMEDOUT; > + } else { > + unsigned int rdy; > + > + /* 1ms poll, 1s timeout covers max timing budgets (per ST Ultra Lite Driver) */ > + ret = regmap_read_poll_timeout(data->regmap, > + VL53L1X_GPIO__TIO_HV_STATUS, rdy, > + (rdy & 1) != data->gpio_polarity, > + 1 * USEC_PER_MSEC, > + 1000 * USEC_PER_MSEC); > + if (ret) > + return ret; > + } > + > + ret = regmap_read(data->regmap, VL53L1X_RESULT__RANGE_STATUS, > + &range_status); > + if (ret) > + goto clear_irq; > + > + if (FIELD_GET(VL53L1X_RANGE_STATUS_MASK, range_status) != > + VL53L1X_RANGE_STATUS_VALID) { > + ret = -EIO; > + goto clear_irq; > + } > + > + ret = vl53l1x_read_u16(data, > + VL53L1X_RESULT__FINAL_CROSSTALK_CORRECTED_RANGE_MM_SD0, > + &distance); > + if (ret) > + goto clear_irq; > + > + *val = distance; > + > +clear_irq: > + vl53l1x_clear_irq(data); > + return ret; > +} > + > +static const struct iio_chan_spec vl53l1x_channels[] = { > + { > + .type = IIO_DISTANCE, > + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | > + BIT(IIO_CHAN_INFO_SCALE), > + .scan_index = 0, > + .scan_type = { > + .sign = 'u', > + .realbits = 16, > + .storagebits = 16, > + }, > + }, > + IIO_CHAN_SOFT_TIMESTAMP(1), > +}; > + > +static int vl53l1x_read_raw(struct iio_dev *indio_dev, > + const struct iio_chan_spec *chan, > + int *val, int *val2, long mask) > +{ > + struct vl53l1x_data *data = iio_priv(indio_dev); > + int ret; > + > + if (chan->type != IIO_DISTANCE) > + return -EINVAL; > + > + switch (mask) { > + case IIO_CHAN_INFO_RAW: > + if (!iio_device_claim_direct(indio_dev)) > + return -EBUSY; > + ret = vl53l1x_read_proximity(data, val); > + iio_device_release_direct(indio_dev); > + if (ret) > + return ret; > + return IIO_VAL_INT; > + case IIO_CHAN_INFO_SCALE: > + *val = 0; > + *val2 = 1000; > + return IIO_VAL_INT_PLUS_MICRO; > + default: > + return -EINVAL; > + } > +} > + > +static const struct iio_info vl53l1x_info = { > + .read_raw = vl53l1x_read_raw, > + .validate_trigger = iio_validate_own_trigger, > +}; > + > +static irqreturn_t vl53l1x_trigger_handler(int irq, void *priv) > +{ > + struct iio_poll_func *pf = priv; > + struct iio_dev *indio_dev = pf->indio_dev; > + struct vl53l1x_data *data = iio_priv(indio_dev); > + struct { > + u16 distance; > + aligned_s64 timestamp; > + } scan = {}; IIO style: { }; > + unsigned int range_status; > + int ret; > + > + ret = regmap_read(data->regmap, VL53L1X_RESULT__RANGE_STATUS, > + &range_status); > + if (ret) > + goto notify_and_clear_irq; > + if (FIELD_GET(VL53L1X_RANGE_STATUS_MASK, range_status) != > + VL53L1X_RANGE_STATUS_VALID) > + goto notify_and_clear_irq; > + > + ret = vl53l1x_read_u16(data, > + VL53L1X_RESULT__FINAL_CROSSTALK_CORRECTED_RANGE_MM_SD0, > + &scan.distance); > + if (ret) > + goto notify_and_clear_irq; > + > + iio_push_to_buffers_with_timestamp(indio_dev, &scan, Use iio_push_to_buffers_with_ts(). > + iio_get_time_ns(indio_dev)); > + > +notify_and_clear_irq: > + iio_trigger_notify_done(indio_dev->trig); > + vl53l1x_clear_irq(data); > + > + return IRQ_HANDLED; > +} > + > +static irqreturn_t vl53l1x_irq_handler(int irq, void *priv) > +{ > + struct iio_dev *indio_dev = priv; > + struct vl53l1x_data *data = iio_priv(indio_dev); > + > + if (iio_buffer_enabled(indio_dev)) > + iio_trigger_poll(indio_dev->trig); > + else > + complete(&data->completion); > + > + return IRQ_HANDLED; > +} > + > +static int vl53l1x_buffer_postenable(struct iio_dev *indio_dev) > +{ > + struct vl53l1x_data *data = iio_priv(indio_dev); > + > + return vl53l1x_start_ranging(data); > +} > + > +static int vl53l1x_buffer_predisable(struct iio_dev *indio_dev) > +{ > + struct vl53l1x_data *data = iio_priv(indio_dev); > + int ret; > + > + ret = vl53l1x_stop_ranging(data); > + if (ret) > + return ret; > + > + /* > + * Best-effort drain. Wait for any in-flight IRQ before clearing. > + * Ignoring timeout as device is shutting down anyway. > + */ > + reinit_completion(&data->completion); > + wait_for_completion_timeout(&data->completion, HZ / 10); > + > + return vl53l1x_clear_irq(data); > +} > + > +static const struct iio_buffer_setup_ops vl53l1x_buffer_setup_ops = { > + .postenable = &vl53l1x_buffer_postenable, > + .predisable = &vl53l1x_buffer_predisable, > +}; > + > +static const struct iio_trigger_ops vl53l1x_trigger_ops = { > + .validate_device = iio_trigger_validate_own_device, > +}; > + > +static void vl53l1x_stop_ranging_action(void *_data) Why leading underscrore? > +{ > + vl53l1x_stop_ranging(_data); > +} > + > +static void vl53l1x_power_off(void *_data) > +{ > + struct vl53l1x_data *data = _data; > + > + reset_control_assert(data->xshut_reset); > + regulator_disable(data->vdd_supply); > +} > + > +static int vl53l1x_power_on(struct vl53l1x_data *data) > +{ > + int ret; > + > + ret = regulator_enable(data->vdd_supply); > + if (ret) > + return ret; > + > + ret = reset_control_deassert(data->xshut_reset); > + if (ret) { > + regulator_disable(data->vdd_supply); > + return ret; > + } > + /* > + * 1.2 ms max boot duration. > + * Datasheet Section 3.6 "Power up and boot sequence". > + */ > + fsleep(1200); > + > + return 0; > +} > + > +static int vl53l1x_configure_irq(struct device *dev, int irq, > + struct iio_dev *indio_dev) > +{ > + struct vl53l1x_data *data = iio_priv(indio_dev); > + int ret; > + > + ret = devm_request_irq(dev, irq, vl53l1x_irq_handler, IRQF_NO_THREAD, > + indio_dev->name, indio_dev); > + if (ret) > + return ret; > + > + ret = regmap_write(data->regmap, VL53L1X_SYSTEM__INTERRUPT_CONFIG_GPIO, > + VL53L1X_INT_NEW_SAMPLE_READY); > + if (ret) > + return dev_err_probe(dev, ret, > + "failed to configure IRQ\n"); > + > + return 0; > +} > + > +static int vl53l1x_probe(struct i2c_client *client) > +{ > + struct device *dev = &client->dev; > + struct vl53l1x_data *data; > + struct iio_dev *indio_dev; > + int ret; > + > + if (!i2c_check_functionality(client->adapter, > + I2C_FUNC_SMBUS_READ_I2C_BLOCK | > + I2C_FUNC_SMBUS_BYTE_DATA)) > + return -EOPNOTSUPP; > + > + indio_dev = devm_iio_device_alloc(dev, sizeof(*data)); > + if (!indio_dev) > + return -ENOMEM; > + > + data = iio_priv(indio_dev); > + data->irq = client->irq; > + > + data->regmap = devm_regmap_init_i2c(client, &vl53l1x_regmap_config); > + if (IS_ERR(data->regmap)) > + return dev_err_probe(dev, PTR_ERR(data->regmap), > + "regmap initialization failed\n"); > + > + /* > + * vdd-supply is required in the DT binding but we > + * continue if it is missing to support older DTs. > + */ > + data->vdd_supply = devm_regulator_get(dev, "vdd"); > + if (IS_ERR(data->vdd_supply)) > + return dev_err_probe(dev, PTR_ERR(data->vdd_supply), > + "Unable to get VDD regulator\n"); > + This should have some explanation of why we consider the XSHUT pin as reset instead of power management. It looks like we are using it for power management anyway, so strange to use reset interface for that. AFAIKT, XSHUT will put the chip in standby mode rather than reset it. But it isn't clear if all registers need to be reporgrammed after coming out of standby. If that is the case, then calling it a reset can make sense. > + data->xshut_reset = devm_reset_control_get_optional_exclusive(dev, NULL); > + if (IS_ERR(data->xshut_reset)) > + return dev_err_probe(dev, PTR_ERR(data->xshut_reset), > + "Cannot get reset control\n"); > + > + ret = vl53l1x_power_on(data); > + if (ret) > + return dev_err_probe(dev, ret, "Failed to power on the chip\n"); > + > + ret = devm_add_action_or_reset(dev, vl53l1x_power_off, data); > + if (ret) > + return ret; > + > + ret = vl53l1x_chip_init(data); > + if (ret) > + return ret; > + > + ret = vl53l1x_set_distance_mode(data, VL53L1X_LONG); > + if (ret) > + return ret; > + Can we get a comment explaining why 50 is chose for default value? > + ret = vl53l1x_set_timing_budget(data, 50); > + if (ret) > + return ret; > + And here. > + ret = vl53l1x_set_inter_measurement_ms(data, 50); > + if (ret) > + return ret; Could make sense to move these into vl53l1x_chip_init() so that probe isn't so long. > + > + ret = vl53l1x_start_ranging(data); > + if (ret) > + return ret; Why start ranging here? It looks like this is controlled during buffer enable/disable. > + > + ret = devm_add_action_or_reset(dev, vl53l1x_stop_ranging_action, data); > + if (ret) > + return ret; > + > + indio_dev->name = "vl53l1x"; > + indio_dev->info = &vl53l1x_info; > + indio_dev->channels = vl53l1x_channels; > + indio_dev->num_channels = ARRAY_SIZE(vl53l1x_channels); > + indio_dev->modes = INDIO_DIRECT_MODE; > + > + if (client->irq) { > + struct iio_trigger *trig; > + > + init_completion(&data->completion); > + > + trig = devm_iio_trigger_alloc(dev, "%s-dev%d", indio_dev->name, > + iio_device_id(indio_dev)); > + if (!trig) > + return -ENOMEM; > + > + trig->ops = &vl53l1x_trigger_ops; > + iio_trigger_set_drvdata(trig, indio_dev); > + ret = devm_iio_trigger_register(dev, trig); > + if (ret) > + return ret; > + > + indio_dev->trig = iio_trigger_get(trig); > + > + ret = vl53l1x_configure_irq(dev, client->irq, indio_dev); > + if (ret) > + return ret; > + > + ret = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL, > + &vl53l1x_trigger_handler, > + &vl53l1x_buffer_setup_ops); > + if (ret) > + return ret; > + } > + > + return devm_iio_device_register(dev, indio_dev); > +} > + > +static const struct i2c_device_id vl53l1x_id[] = { > + { "vl53l1x" }, > + { } > +}; > +MODULE_DEVICE_TABLE(i2c, vl53l1x_id); > + > +static const struct of_device_id st_vl53l1x_dt_match[] = { > + { .compatible = "st,vl53l1x" }, > + { } > +}; > +MODULE_DEVICE_TABLE(of, st_vl53l1x_dt_match); > + > +static struct i2c_driver vl53l1x_driver = { > + .driver = { > + .name = "vl53l1x-i2c", > + .of_match_table = st_vl53l1x_dt_match, > + }, > + .probe = vl53l1x_probe, > + .id_table = vl53l1x_id, > +}; > +module_i2c_driver(vl53l1x_driver); > + > +MODULE_AUTHOR("Siratul Islam <email@sirat.me>"); > +MODULE_DESCRIPTION("ST VL53L1X ToF ranging sensor driver"); > +MODULE_LICENSE("Dual BSD/GPL"); ^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH v6 2/2] iio: proximity: add driver for ST VL53L1X ToF sensor 2026-03-21 17:09 ` David Lechner @ 2026-03-21 22:39 ` Sirat 2026-03-22 0:37 ` David Lechner 0 siblings, 1 reply; 21+ messages in thread From: Sirat @ 2026-03-21 22:39 UTC (permalink / raw) To: David Lechner Cc: linux-iio, devicetree, jic23, nuno.sa, andy, robh, krzk+dt, conor+dt, linux-kernel On Sat, Mar 21, 2026 at 11:09 PM David Lechner <dlechner@baylibre.com> wrote: > > On 3/19/26 2:07 PM, Siratul Islam wrote: > > Add support for the STMicroelectronics VL53L1X Time-of-Flight > > ranging sensor with I2C interface. > > ... > > > + ret = vl53l1x_set_inter_measurement_ms(data, 50); > > + if (ret) > > + return ret; > > Could make sense to move these into vl53l1x_chip_init() so that probe isn't so long. > These are kept out of chip_init() to match the ST Ultra Lite Driver init sequence. Init only covers hardware init and VHV calibration, distance mode and timing config are separate user-facing calls made after it. > > > + > > + ret = vl53l1x_start_ranging(data); > > + if (ret) > > + return ret; > > Why start ranging here? It looks like this is controlled during buffer > enable/disable. > The VL53L1X (unlike L0X) does not have a single-shot mode. So every measurement requires the device to be running (Autonomous mode according to the datasheet). Starting ranging once in probe allows the direct read path to work without any additional start/stop logic inside read_raw(). This keeps that path simple and low-latency. The buffered/triggered path independently controls ranging via postenable/predisable, so there is no conflict. > > I will address the rest of your review in the next version. Thanks, Sirat ^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH v6 2/2] iio: proximity: add driver for ST VL53L1X ToF sensor 2026-03-21 22:39 ` Sirat @ 2026-03-22 0:37 ` David Lechner 2026-03-22 11:03 ` Jonathan Cameron 0 siblings, 1 reply; 21+ messages in thread From: David Lechner @ 2026-03-22 0:37 UTC (permalink / raw) To: Sirat Cc: linux-iio, devicetree, jic23, nuno.sa, andy, robh, krzk+dt, conor+dt, linux-kernel On 3/21/26 5:39 PM, Sirat wrote: > On Sat, Mar 21, 2026 at 11:09 PM David Lechner <dlechner@baylibre.com> wrote: >> >> On 3/19/26 2:07 PM, Siratul Islam wrote: >>> Add support for the STMicroelectronics VL53L1X Time-of-Flight >>> ranging sensor with I2C interface. >>> ... >>> + >>> + ret = vl53l1x_start_ranging(data); >>> + if (ret) >>> + return ret; >> >> Why start ranging here? It looks like this is controlled during buffer >> enable/disable. >> > The VL53L1X (unlike L0X) does not have a single-shot mode. So every > measurement requires the device > to be running (Autonomous mode according to the datasheet). Starting > ranging once > in probe allows the direct read path to work without any additional > start/stop logic inside read_raw(). > This keeps that path simple and low-latency. The buffered/triggered > path independently controls > ranging via postenable/predisable, so there is no conflict. If you do a buffered read, which stop ranging when it is done, then how can the direct read work after that? Can we just start and stop ranging momentarily for a direct read? Otherwise, is seems like we would want to have ranging always enabled. >> >> > I will address the rest of your review in the next version. > > Thanks, > Sirat ^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH v6 2/2] iio: proximity: add driver for ST VL53L1X ToF sensor 2026-03-22 0:37 ` David Lechner @ 2026-03-22 11:03 ` Jonathan Cameron 2026-03-22 13:52 ` Sirat 0 siblings, 1 reply; 21+ messages in thread From: Jonathan Cameron @ 2026-03-22 11:03 UTC (permalink / raw) To: David Lechner Cc: Sirat, linux-iio, devicetree, nuno.sa, andy, robh, krzk+dt, conor+dt, linux-kernel On Sat, 21 Mar 2026 19:37:58 -0500 David Lechner <dlechner@baylibre.com> wrote: > On 3/21/26 5:39 PM, Sirat wrote: > > On Sat, Mar 21, 2026 at 11:09 PM David Lechner <dlechner@baylibre.com> wrote: > >> > >> On 3/19/26 2:07 PM, Siratul Islam wrote: > >>> Add support for the STMicroelectronics VL53L1X Time-of-Flight > >>> ranging sensor with I2C interface. > >>> > ... > > >>> + > >>> + ret = vl53l1x_start_ranging(data); > >>> + if (ret) > >>> + return ret; > >> > >> Why start ranging here? It looks like this is controlled during buffer > >> enable/disable. > >> > > The VL53L1X (unlike L0X) does not have a single-shot mode. So every > > measurement requires the device > > to be running (Autonomous mode according to the datasheet). Starting > > ranging once > > in probe allows the direct read path to work without any additional > > start/stop logic inside read_raw(). > > This keeps that path simple and low-latency. The buffered/triggered > > path independently controls > > ranging via postenable/predisable, so there is no conflict. > > If you do a buffered read, which stop ranging when it is done, > then how can the direct read work after that? > > Can we just start and stop ranging momentarily for a direct > read? If the latency is high, then worth considering whether autosuspend and runtime pm can help. That way a burst of reads will see low latency after the first one but we won't be wasting power when no one cares. J > > Otherwise, is seems like we would want to have ranging always > enabled. > > >> > >> > > I will address the rest of your review in the next version. > > > > Thanks, > > Sirat > > ^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH v6 2/2] iio: proximity: add driver for ST VL53L1X ToF sensor 2026-03-22 11:03 ` Jonathan Cameron @ 2026-03-22 13:52 ` Sirat 2026-03-24 15:17 ` Sirat 0 siblings, 1 reply; 21+ messages in thread From: Sirat @ 2026-03-22 13:52 UTC (permalink / raw) To: Jonathan Cameron Cc: David Lechner, linux-iio, devicetree, nuno.sa, andy, robh, krzk+dt, conor+dt, linux-kernel On Sun, Mar 22, 2026 at 5:03 PM Jonathan Cameron <jic23@kernel.org> wrote: > > On Sat, 21 Mar 2026 19:37:58 -0500 > David Lechner <dlechner@baylibre.com> wrote: > > > On 3/21/26 5:39 PM, Sirat wrote: > > > On Sat, Mar 21, 2026 at 11:09 PM David Lechner <dlechner@baylibre.com> wrote: > > >> > > >> On 3/19/26 2:07 PM, Siratul Islam wrote: ... > > > > If you do a buffered read, which stop ranging when it is done, > > then how can the direct read work after that? > > > > Can we just start and stop ranging momentarily for a direct > > read? > If the latency is high, then worth considering whether autosuspend > and runtime pm can help. That way a burst of reads will see low > latency after the first one but we won't be wasting power when > no one cares. > > J > > > > Otherwise, is seems like we would want to have ranging always > > enabled. > > Maybe we should go with continuous ranging then since it's the vendor-intended behaviour. Since there is no hardware single-shot mode, by design, I think the driver should just behave as the hardware intended. > > > Thanks, Sirat ^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH v6 2/2] iio: proximity: add driver for ST VL53L1X ToF sensor 2026-03-22 13:52 ` Sirat @ 2026-03-24 15:17 ` Sirat 2026-03-24 17:29 ` David Lechner 0 siblings, 1 reply; 21+ messages in thread From: Sirat @ 2026-03-24 15:17 UTC (permalink / raw) To: Jonathan Cameron Cc: David Lechner, linux-iio, devicetree, nuno.sa, andy, robh, krzk+dt, conor+dt, linux-kernel On Sun, Mar 22, 2026 at 7:52 PM Sirat <email@sirat.me> wrote: > > On Sun, Mar 22, 2026 at 5:03 PM Jonathan Cameron <jic23@kernel.org> wrote: > > > > On Sat, 21 Mar 2026 19:37:58 -0500 > > David Lechner <dlechner@baylibre.com> wrote: > > > > > On 3/21/26 5:39 PM, Sirat wrote: > > > > On Sat, Mar 21, 2026 at 11:09 PM David Lechner <dlechner@baylibre.com> wrote: > > > >> > > > >> On 3/19/26 2:07 PM, Siratul Islam wrote: > ... > > > > > > If you do a buffered read, which stop ranging when it is done, > > > then how can the direct read work after that? > > > > > > Can we just start and stop ranging momentarily for a direct > > > read? > > If the latency is high, then worth considering whether autosuspend > > and runtime pm can help. That way a burst of reads will see low > > latency after the first one but we won't be wasting power when > > no one cares. > > > > J > > > > > > Otherwise, is seems like we would want to have ranging always > > > enabled. > > > > Maybe we should go with continuous ranging then since it's the > vendor-intended behaviour. > Since there is no hardware single-shot mode, by design, I think the > driver should just behave > as the hardware intended. > > > > > Just for the record, I'm dropping postenable, and keeping predisable for only the cleanup. Thanks, Sirat ^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH v6 2/2] iio: proximity: add driver for ST VL53L1X ToF sensor 2026-03-24 15:17 ` Sirat @ 2026-03-24 17:29 ` David Lechner 2026-03-24 18:04 ` Sirat 0 siblings, 1 reply; 21+ messages in thread From: David Lechner @ 2026-03-24 17:29 UTC (permalink / raw) To: Sirat, Jonathan Cameron Cc: linux-iio, devicetree, nuno.sa, andy, robh, krzk+dt, conor+dt, linux-kernel On 3/24/26 10:17 AM, Sirat wrote: > On Sun, Mar 22, 2026 at 7:52 PM Sirat <email@sirat.me> wrote: >> >> On Sun, Mar 22, 2026 at 5:03 PM Jonathan Cameron <jic23@kernel.org> wrote: >>> >>> On Sat, 21 Mar 2026 19:37:58 -0500 >>> David Lechner <dlechner@baylibre.com> wrote: >>> >>>> On 3/21/26 5:39 PM, Sirat wrote: >>>>> On Sat, Mar 21, 2026 at 11:09 PM David Lechner <dlechner@baylibre.com> wrote: >>>>>> >>>>>> On 3/19/26 2:07 PM, Siratul Islam wrote: >> ... >>>> >>>> If you do a buffered read, which stop ranging when it is done, >>>> then how can the direct read work after that? >>>> >>>> Can we just start and stop ranging momentarily for a direct >>>> read? >>> If the latency is high, then worth considering whether autosuspend >>> and runtime pm can help. That way a burst of reads will see low >>> latency after the first one but we won't be wasting power when >>> no one cares. >>> >>> J >>>> >>>> Otherwise, is seems like we would want to have ranging always >>>> enabled. >>>> >> Maybe we should go with continuous ranging then since it's the >> vendor-intended behaviour. >> Since there is no hardware single-shot mode, by design, I think the >> driver should just behave >> as the hardware intended. >>>> >>> > Just for the record, I'm dropping postenable, and keeping predisable > for only the cleanup. > > Thanks, > Sirat predisable must clean up anything done in postenable. So it doesn't make sense to have predisable without a postenable. If there is something unusual going on here, it will need comments with a clear explanation of why it still works when it is breaking the convention. ^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH v6 2/2] iio: proximity: add driver for ST VL53L1X ToF sensor 2026-03-24 17:29 ` David Lechner @ 2026-03-24 18:04 ` Sirat 0 siblings, 0 replies; 21+ messages in thread From: Sirat @ 2026-03-24 18:04 UTC (permalink / raw) To: David Lechner Cc: Jonathan Cameron, linux-iio, devicetree, nuno.sa, andy, robh, krzk+dt, conor+dt, linux-kernel On Tue, Mar 24, 2026 at 11:29 PM David Lechner <dlechner@baylibre.com> wrote: > > On 3/24/26 10:17 AM, Sirat wrote: > > On Sun, Mar 22, 2026 at 7:52 PM Sirat <email@sirat.me> wrote: > >> > >> On Sun, Mar 22, 2026 at 5:03 PM Jonathan Cameron <jic23@kernel.org> wrote: > >>> > >>> On Sat, 21 Mar 2026 19:37:58 -0500 > >>> David Lechner <dlechner@baylibre.com> wrote: > >>> > >>>> On 3/21/26 5:39 PM, Sirat wrote: > >>>>> On Sat, Mar 21, 2026 at 11:09 PM David Lechner <dlechner@baylibre.com> wrote: > >>>>>> > >>>>>> On 3/19/26 2:07 PM, Siratul Islam wrote: > >> ... > >>>> > >>>> If you do a buffered read, which stop ranging when it is done, > >>>> then how can the direct read work after that? > >>>> > >>>> Can we just start and stop ranging momentarily for a direct > >>>> read? > >>> If the latency is high, then worth considering whether autosuspend > >>> and runtime pm can help. That way a burst of reads will see low > >>> latency after the first one but we won't be wasting power when > >>> no one cares. > >>> > >>> J > >>>> > >>>> Otherwise, is seems like we would want to have ranging always > >>>> enabled. > >>>> > >> Maybe we should go with continuous ranging then since it's the > >> vendor-intended behaviour. > >> Since there is no hardware single-shot mode, by design, I think the > >> driver should just behave > >> as the hardware intended. > >>>> > >>> > > Just for the record, I'm dropping postenable, and keeping predisable > > for only the cleanup. > > > > Thanks, > > Sirat > > predisable must clean up anything done in postenable. So it doesn't > make sense to have predisable without a postenable. > > If there is something unusual going on here, it will need comments > with a clear explanation of why it still works when it is breaking > the convention. > > I was thinking about the drain logic. But looks like it is not needed. Dropping predisable too. Thanks for the insight! Sirat ^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH v6 2/2] iio: proximity: add driver for ST VL53L1X ToF sensor 2026-03-19 19:07 ` [PATCH v6 2/2] iio: proximity: add driver for " Siratul Islam 2026-03-20 8:38 ` Andy Shevchenko 2026-03-21 17:09 ` David Lechner @ 2026-03-21 17:21 ` Jonathan Cameron 2026-03-21 23:40 ` Sirat 2 siblings, 1 reply; 21+ messages in thread From: Jonathan Cameron @ 2026-03-21 17:21 UTC (permalink / raw) To: Siratul Islam Cc: linux-iio, devicetree, dlechner, nuno.sa, andy, robh, krzk+dt, conor+dt, linux-kernel On Fri, 20 Mar 2026 01:07:14 +0600 Siratul Islam <email@sirat.me> wrote: > Add support for the STMicroelectronics VL53L1X Time-of-Flight > ranging sensor with I2C interface. > > Signed-off-by: Siratul Islam <email@sirat.me> Hi Siratul Just a few minor things seeing as you are going to v7 anyway. If you weren't I'd have applied some or maybe all of these as tweaks whilst picking the driver up. thanks, Jonathan > diff --git a/drivers/iio/proximity/vl53l1x-i2c.c b/drivers/iio/proximity/vl53l1x-i2c.c > new file mode 100644 > index 000000000000..771598b92e04 > --- /dev/null > +++ b/drivers/iio/proximity/vl53l1x-i2c.c > @@ -0,0 +1,820 @@ > +static irqreturn_t vl53l1x_trigger_handler(int irq, void *priv) > +{ > + struct iio_poll_func *pf = priv; > + struct iio_dev *indio_dev = pf->indio_dev; > + struct vl53l1x_data *data = iio_priv(indio_dev); > + struct { > + u16 distance; > + aligned_s64 timestamp; > + } scan = {}; > + unsigned int range_status; > + int ret; > + > + ret = regmap_read(data->regmap, VL53L1X_RESULT__RANGE_STATUS, > + &range_status); > + if (ret) > + goto notify_and_clear_irq; > + if (FIELD_GET(VL53L1X_RANGE_STATUS_MASK, range_status) != > + VL53L1X_RANGE_STATUS_VALID) > + goto notify_and_clear_irq; > + > + ret = vl53l1x_read_u16(data, > + VL53L1X_RESULT__FINAL_CROSSTALK_CORRECTED_RANGE_MM_SD0, > + &scan.distance); > + if (ret) > + goto notify_and_clear_irq; > + > + iio_push_to_buffers_with_timestamp(indio_dev, &scan, > + iio_get_time_ns(indio_dev)); Use iio_push_to_buffers_with_ts() that also takes the size of the buffer as a parameter. That defends against mismatch in channel spec and the buffer provided. Can help catch some types of bugs. > + > +notify_and_clear_irq: > + iio_trigger_notify_done(indio_dev->trig); > + vl53l1x_clear_irq(data); > + > + return IRQ_HANDLED; > +} > +static int vl53l1x_configure_irq(struct device *dev, int irq, > + struct iio_dev *indio_dev) > +{ > + struct vl53l1x_data *data = iio_priv(indio_dev); > + int ret; > + > + ret = devm_request_irq(dev, irq, vl53l1x_irq_handler, IRQF_NO_THREAD, > + indio_dev->name, indio_dev); > + if (ret) > + return ret; > + > + ret = regmap_write(data->regmap, VL53L1X_SYSTEM__INTERRUPT_CONFIG_GPIO, > + VL53L1X_INT_NEW_SAMPLE_READY); > + if (ret) > + return dev_err_probe(dev, ret, > + "failed to configure IRQ\n"); Andy already pointed out that easily fits on oneline. > + > + return 0; > +} > + > +static int vl53l1x_probe(struct i2c_client *client) > +{ > + struct device *dev = &client->dev; > + struct vl53l1x_data *data; > + struct iio_dev *indio_dev; > + int ret; > + > + if (!i2c_check_functionality(client->adapter, > + I2C_FUNC_SMBUS_READ_I2C_BLOCK | > + I2C_FUNC_SMBUS_BYTE_DATA)) > + return -EOPNOTSUPP; > + > + indio_dev = devm_iio_device_alloc(dev, sizeof(*data)); > + if (!indio_dev) > + return -ENOMEM; > + > + data = iio_priv(indio_dev); > + data->irq = client->irq; > + > + data->regmap = devm_regmap_init_i2c(client, &vl53l1x_regmap_config); > + if (IS_ERR(data->regmap)) > + return dev_err_probe(dev, PTR_ERR(data->regmap), > + "regmap initialization failed\n"); > + > + /* > + * vdd-supply is required in the DT binding but we > + * continue if it is missing to support older DTs. Given the driver does nothing different for an auto provided fake regulator and one from DT, I'm not seeing the comment as particularly useful. Drop it. > + */ > + data->vdd_supply = devm_regulator_get(dev, "vdd"); > + if (IS_ERR(data->vdd_supply)) > + return dev_err_probe(dev, PTR_ERR(data->vdd_supply), > + "Unable to get VDD regulator\n"); ^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH v6 2/2] iio: proximity: add driver for ST VL53L1X ToF sensor 2026-03-21 17:21 ` Jonathan Cameron @ 2026-03-21 23:40 ` Sirat 2026-03-22 0:39 ` David Lechner 0 siblings, 1 reply; 21+ messages in thread From: Sirat @ 2026-03-21 23:40 UTC (permalink / raw) To: Jonathan Cameron Cc: linux-iio, devicetree, dlechner, nuno.sa, andy, robh, krzk+dt, conor+dt, linux-kernel On Sat, Mar 21, 2026 at 11:22 PM Jonathan Cameron <jic23@kernel.org> wrote: > > On Fri, 20 Mar 2026 01:07:14 +0600 > Siratul Islam <email@sirat.me> wrote: > > > Add support for the STMicroelectronics VL53L1X Time-of-Flight > > ranging sensor with I2C interface. > > > > Signed-off-by: Siratul Islam <email@sirat.me> > Hi Siratul > > Just a few minor things seeing as you are going to v7 anyway. > If you weren't I'd have applied some or maybe all of these as tweaks > whilst picking the driver up. > No problem at all. > > > diff --git a/drivers/iio/proximity/vl53l1x-i2c.c b/drivers/iio/proximity/vl53l1x-i2c.c > > new file mode 100644 > > index 000000000000..771598b92e04 > > --- /dev/null > > +++ b/drivers/iio/proximity/vl53l1x-i2c.c > > @@ -0,0 +1,820 @@ > --- > > + /* > > + * vdd-supply is required in the DT binding but we > > + * continue if it is missing to support older DTs. > > Given the driver does nothing different for an auto provided fake regulator > and one from DT, I'm not seeing the comment as particularly useful. > > Drop it. > Considering there is zero difference between the fake and real regulators in terms of functionality of this driver, should we just drop the hard requirement in the binding? > Thanks, Sirat ^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH v6 2/2] iio: proximity: add driver for ST VL53L1X ToF sensor 2026-03-21 23:40 ` Sirat @ 2026-03-22 0:39 ` David Lechner 0 siblings, 0 replies; 21+ messages in thread From: David Lechner @ 2026-03-22 0:39 UTC (permalink / raw) To: Sirat, Jonathan Cameron Cc: linux-iio, devicetree, nuno.sa, andy, robh, krzk+dt, conor+dt, linux-kernel On 3/21/26 6:40 PM, Sirat wrote: > On Sat, Mar 21, 2026 at 11:22 PM Jonathan Cameron <jic23@kernel.org> wrote: >> >> On Fri, 20 Mar 2026 01:07:14 +0600 >> Siratul Islam <email@sirat.me> wrote: >> >>> Add support for the STMicroelectronics VL53L1X Time-of-Flight >>> ranging sensor with I2C interface. >>> >>> Signed-off-by: Siratul Islam <email@sirat.me> >> Hi Siratul >> >> Just a few minor things seeing as you are going to v7 anyway. >> If you weren't I'd have applied some or maybe all of these as tweaks >> whilst picking the driver up. >> > No problem at all. >> >>> diff --git a/drivers/iio/proximity/vl53l1x-i2c.c b/drivers/iio/proximity/vl53l1x-i2c.c >>> new file mode 100644 >>> index 000000000000..771598b92e04 >>> --- /dev/null >>> +++ b/drivers/iio/proximity/vl53l1x-i2c.c >>> @@ -0,0 +1,820 @@ >> > --- >>> + /* >>> + * vdd-supply is required in the DT binding but we >>> + * continue if it is missing to support older DTs. >> >> Given the driver does nothing different for an auto provided fake regulator >> and one from DT, I'm not seeing the comment as particularly useful. >> >> Drop it. >> > Considering there is zero difference between the fake and real regulators > in terms of functionality of this driver, should we just drop the hard > requirement in the binding? No, bindings should not depend on driver implementation. >> > > Thanks, > Sirat ^ permalink raw reply [flat|nested] 21+ messages in thread
end of thread, other threads:[~2026-03-24 18:04 UTC | newest] Thread overview: 21+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2026-03-19 19:07 [PATCH v6 0/2] iio: proximity: add driver for ST VL53L1X ToF sensor Siratul Islam 2026-03-19 19:07 ` [PATCH v6 1/2] dt-bindings: iio: proximity: add " Siratul Islam 2026-03-20 9:05 ` Krzysztof Kozlowski 2026-03-20 12:10 ` Sirat 2026-03-20 12:44 ` Krzysztof Kozlowski 2026-03-20 12:52 ` Krzysztof Kozlowski 2026-03-20 13:28 ` Sirat 2026-03-21 10:00 ` Krzysztof Kozlowski 2026-03-19 19:07 ` [PATCH v6 2/2] iio: proximity: add driver for " Siratul Islam 2026-03-20 8:38 ` Andy Shevchenko 2026-03-21 17:09 ` David Lechner 2026-03-21 22:39 ` Sirat 2026-03-22 0:37 ` David Lechner 2026-03-22 11:03 ` Jonathan Cameron 2026-03-22 13:52 ` Sirat 2026-03-24 15:17 ` Sirat 2026-03-24 17:29 ` David Lechner 2026-03-24 18:04 ` Sirat 2026-03-21 17:21 ` Jonathan Cameron 2026-03-21 23:40 ` Sirat 2026-03-22 0:39 ` David Lechner
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox