VL53L0X can be configured to use interrupt pin (GPIO1) to notify host about readiness of new measurement. If interrupt pin is not specified in DT, driver still uses polling. Signed-off-by: Ivan Drobyshevskyi <drobyshevskyi@xxxxxxxxx> --- drivers/iio/proximity/vl53l0x-i2c.c | 104 ++++++++++++++++++++++++---- 1 file changed, 92 insertions(+), 12 deletions(-) diff --git a/drivers/iio/proximity/vl53l0x-i2c.c b/drivers/iio/proximity/vl53l0x-i2c.c index b48216cc1..b676e3702 100644 --- a/drivers/iio/proximity/vl53l0x-i2c.c +++ b/drivers/iio/proximity/vl53l0x-i2c.c @@ -4,19 +4,21 @@ * * Copyright (C) 2016 STMicroelectronics Imaging Division. * Copyright (C) 2018 Song Qiang <songqiang1304521@xxxxxxxxx> + * Copyright (C) 2020 Ivan Drobyshevskyi <drobyshevskyi@xxxxxxxxx> * * Datasheet available at * <https://www.st.com/resource/en/datasheet/vl53l0x.pdf> * * Default 7-bit i2c slave address 0x29. * - * TODO: FIFO buffer, continuous mode, interrupts, range selection, - * sensor ID check. + * TODO: FIFO buffer, continuous mode, range selection, sensor ID check. */ #include <linux/delay.h> #include <linux/i2c.h> +#include <linux/interrupt.h> #include <linux/module.h> +#include <linux/of_irq.h> #include <linux/iio/iio.h> @@ -29,14 +31,67 @@ #define VL_REG_SYSRANGE_MODE_TIMED BIT(2) #define VL_REG_SYSRANGE_MODE_HISTOGRAM BIT(3) +#define VL_REG_SYSTEM_INTERRUPT_CONFIG_GPIO 0x0A +#define VL_REG_SYSTEM_INTERRUPT_GPIO_NEW_SAMPLE_READY BIT(2) + +#define VL_REG_SYSTEM_INTERRUPT_CLEAR 0x0B + #define VL_REG_RESULT_INT_STATUS 0x13 #define VL_REG_RESULT_RANGE_STATUS 0x14 #define VL_REG_RESULT_RANGE_STATUS_COMPLETE BIT(0) struct vl53l0x_data { - struct i2c_client *client; + struct i2c_client *client; + struct completion completion; + int irq; }; +static irqreturn_t vl53l0x_handle_irq(int irq, void *dev_id) +{ + struct iio_dev *indio_dev = dev_id; + struct vl53l0x_data *data = iio_priv(indio_dev); + + complete(&data->completion); + + return IRQ_HANDLED; +} + +static int vl53l0x_configure_irq(struct device *dev, struct iio_dev *indio_dev) +{ + struct vl53l0x_data *data = iio_priv(indio_dev); + int ret; + + ret = devm_request_irq(dev, data->irq, vl53l0x_handle_irq, + IRQF_TRIGGER_FALLING, indio_dev->name, indio_dev); + if (ret) { + dev_err(dev, "devm_request_irq error: %d\n", ret); + return ret; + } + + ret = i2c_smbus_write_byte_data(data->client, + VL_REG_SYSTEM_INTERRUPT_CONFIG_GPIO, + VL_REG_SYSTEM_INTERRUPT_GPIO_NEW_SAMPLE_READY); + if (ret) + dev_err(dev, "failed to configure IRQ: %d\n", ret); + + return ret; +} + +static void vl53l0x_clear_irq(struct vl53l0x_data *data) +{ + u8 status; + + i2c_smbus_write_byte_data(data->client, + VL_REG_SYSTEM_INTERRUPT_CLEAR, 1); + i2c_smbus_write_byte_data(data->client, + VL_REG_SYSTEM_INTERRUPT_CLEAR, 0); + + status = i2c_smbus_read_byte_data(data->client, + VL_REG_RESULT_INT_STATUS); + if (status & 0x07) + dev_err(&data->client->dev, "failed to clear irq\n"); +} + static int vl53l0x_read_proximity(struct vl53l0x_data *data, const struct iio_chan_spec *chan, int *val) @@ -50,19 +105,31 @@ static int vl53l0x_read_proximity(struct vl53l0x_data *data, if (ret < 0) return ret; - do { - ret = i2c_smbus_read_byte_data(client, - VL_REG_RESULT_RANGE_STATUS); + if (data->irq) { + reinit_completion(&data->completion); + + ret = wait_for_completion_timeout(&data->completion, HZ/10); if (ret < 0) return ret; + else if (ret == 0) + return -ETIMEDOUT; - if (ret & VL_REG_RESULT_RANGE_STATUS_COMPLETE) - break; + vl53l0x_clear_irq(data); + } else { + do { + ret = i2c_smbus_read_byte_data(client, + VL_REG_RESULT_RANGE_STATUS); + if (ret < 0) + return ret; + + if (ret & VL_REG_RESULT_RANGE_STATUS_COMPLETE) + break; - usleep_range(1000, 5000); - } while (--tries); - if (!tries) - return -ETIMEDOUT; + usleep_range(1000, 5000); + } while (--tries); + if (!tries) + return -ETIMEDOUT; + } ret = i2c_smbus_read_i2c_block_data(client, VL_REG_RESULT_RANGE_STATUS, 12, buffer); @@ -120,6 +187,7 @@ static int vl53l0x_probe(struct i2c_client *client) { struct vl53l0x_data *data; struct iio_dev *indio_dev; + struct device *dev = &client->dev; indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); if (!indio_dev) @@ -141,6 +209,18 @@ static int vl53l0x_probe(struct i2c_client *client) indio_dev->num_channels = ARRAY_SIZE(vl53l0x_channels); indio_dev->modes = INDIO_DIRECT_MODE; + data->irq = irq_of_parse_and_map(dev->of_node, 0); + /* usage of interrupt is optional */ + if (data->irq) { + int ret; + + init_completion(&data->completion); + + ret = vl53l0x_configure_irq(dev, indio_dev); + if (ret) + return ret; + } + return devm_iio_device_register(&client->dev, indio_dev); } -- 2.26.0