18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Support for ST VL53L0X FlightSense ToF Ranging Sensor on a i2c bus. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2016 STMicroelectronics Imaging Division. 68c2ecf20Sopenharmony_ci * Copyright (C) 2018 Song Qiang <songqiang1304521@gmail.com> 78c2ecf20Sopenharmony_ci * Copyright (C) 2020 Ivan Drobyshevskyi <drobyshevskyi@gmail.com> 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * Datasheet available at 108c2ecf20Sopenharmony_ci * <https://www.st.com/resource/en/datasheet/vl53l0x.pdf> 118c2ecf20Sopenharmony_ci * 128c2ecf20Sopenharmony_ci * Default 7-bit i2c slave address 0x29. 138c2ecf20Sopenharmony_ci * 148c2ecf20Sopenharmony_ci * TODO: FIFO buffer, continuous mode, range selection, sensor ID check. 158c2ecf20Sopenharmony_ci */ 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#include <linux/delay.h> 188c2ecf20Sopenharmony_ci#include <linux/i2c.h> 198c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 208c2ecf20Sopenharmony_ci#include <linux/module.h> 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#include <linux/iio/iio.h> 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci#define VL_REG_SYSRANGE_START 0x00 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#define VL_REG_SYSRANGE_MODE_MASK GENMASK(3, 0) 278c2ecf20Sopenharmony_ci#define VL_REG_SYSRANGE_MODE_SINGLESHOT 0x00 288c2ecf20Sopenharmony_ci#define VL_REG_SYSRANGE_MODE_START_STOP BIT(0) 298c2ecf20Sopenharmony_ci#define VL_REG_SYSRANGE_MODE_BACKTOBACK BIT(1) 308c2ecf20Sopenharmony_ci#define VL_REG_SYSRANGE_MODE_TIMED BIT(2) 318c2ecf20Sopenharmony_ci#define VL_REG_SYSRANGE_MODE_HISTOGRAM BIT(3) 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci#define VL_REG_SYSTEM_INTERRUPT_CONFIG_GPIO 0x0A 348c2ecf20Sopenharmony_ci#define VL_REG_SYSTEM_INTERRUPT_GPIO_NEW_SAMPLE_READY BIT(2) 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci#define VL_REG_SYSTEM_INTERRUPT_CLEAR 0x0B 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci#define VL_REG_RESULT_INT_STATUS 0x13 398c2ecf20Sopenharmony_ci#define VL_REG_RESULT_RANGE_STATUS 0x14 408c2ecf20Sopenharmony_ci#define VL_REG_RESULT_RANGE_STATUS_COMPLETE BIT(0) 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_cistruct vl53l0x_data { 438c2ecf20Sopenharmony_ci struct i2c_client *client; 448c2ecf20Sopenharmony_ci struct completion completion; 458c2ecf20Sopenharmony_ci}; 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_cistatic irqreturn_t vl53l0x_handle_irq(int irq, void *priv) 488c2ecf20Sopenharmony_ci{ 498c2ecf20Sopenharmony_ci struct iio_dev *indio_dev = priv; 508c2ecf20Sopenharmony_ci struct vl53l0x_data *data = iio_priv(indio_dev); 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci complete(&data->completion); 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci return IRQ_HANDLED; 558c2ecf20Sopenharmony_ci} 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_cistatic int vl53l0x_configure_irq(struct i2c_client *client, 588c2ecf20Sopenharmony_ci struct iio_dev *indio_dev) 598c2ecf20Sopenharmony_ci{ 608c2ecf20Sopenharmony_ci struct vl53l0x_data *data = iio_priv(indio_dev); 618c2ecf20Sopenharmony_ci int ret; 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci ret = devm_request_irq(&client->dev, client->irq, vl53l0x_handle_irq, 648c2ecf20Sopenharmony_ci IRQF_TRIGGER_FALLING, indio_dev->name, indio_dev); 658c2ecf20Sopenharmony_ci if (ret) { 668c2ecf20Sopenharmony_ci dev_err(&client->dev, "devm_request_irq error: %d\n", ret); 678c2ecf20Sopenharmony_ci return ret; 688c2ecf20Sopenharmony_ci } 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci ret = i2c_smbus_write_byte_data(data->client, 718c2ecf20Sopenharmony_ci VL_REG_SYSTEM_INTERRUPT_CONFIG_GPIO, 728c2ecf20Sopenharmony_ci VL_REG_SYSTEM_INTERRUPT_GPIO_NEW_SAMPLE_READY); 738c2ecf20Sopenharmony_ci if (ret < 0) 748c2ecf20Sopenharmony_ci dev_err(&client->dev, "failed to configure IRQ: %d\n", ret); 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci return ret; 778c2ecf20Sopenharmony_ci} 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_cistatic void vl53l0x_clear_irq(struct vl53l0x_data *data) 808c2ecf20Sopenharmony_ci{ 818c2ecf20Sopenharmony_ci struct device *dev = &data->client->dev; 828c2ecf20Sopenharmony_ci int ret; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci ret = i2c_smbus_write_byte_data(data->client, 858c2ecf20Sopenharmony_ci VL_REG_SYSTEM_INTERRUPT_CLEAR, 1); 868c2ecf20Sopenharmony_ci if (ret < 0) 878c2ecf20Sopenharmony_ci dev_err(dev, "failed to clear error irq: %d\n", ret); 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci ret = i2c_smbus_write_byte_data(data->client, 908c2ecf20Sopenharmony_ci VL_REG_SYSTEM_INTERRUPT_CLEAR, 0); 918c2ecf20Sopenharmony_ci if (ret < 0) 928c2ecf20Sopenharmony_ci dev_err(dev, "failed to clear range irq: %d\n", ret); 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci ret = i2c_smbus_read_byte_data(data->client, VL_REG_RESULT_INT_STATUS); 958c2ecf20Sopenharmony_ci if (ret < 0 || ret & 0x07) 968c2ecf20Sopenharmony_ci dev_err(dev, "failed to clear irq: %d\n", ret); 978c2ecf20Sopenharmony_ci} 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_cistatic int vl53l0x_read_proximity(struct vl53l0x_data *data, 1008c2ecf20Sopenharmony_ci const struct iio_chan_spec *chan, 1018c2ecf20Sopenharmony_ci int *val) 1028c2ecf20Sopenharmony_ci{ 1038c2ecf20Sopenharmony_ci struct i2c_client *client = data->client; 1048c2ecf20Sopenharmony_ci u16 tries = 20; 1058c2ecf20Sopenharmony_ci u8 buffer[12]; 1068c2ecf20Sopenharmony_ci int ret; 1078c2ecf20Sopenharmony_ci unsigned long time_left; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci ret = i2c_smbus_write_byte_data(client, VL_REG_SYSRANGE_START, 1); 1108c2ecf20Sopenharmony_ci if (ret < 0) 1118c2ecf20Sopenharmony_ci return ret; 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci if (data->client->irq) { 1148c2ecf20Sopenharmony_ci reinit_completion(&data->completion); 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci time_left = wait_for_completion_timeout(&data->completion, HZ/10); 1178c2ecf20Sopenharmony_ci if (time_left == 0) 1188c2ecf20Sopenharmony_ci return -ETIMEDOUT; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci vl53l0x_clear_irq(data); 1218c2ecf20Sopenharmony_ci } else { 1228c2ecf20Sopenharmony_ci do { 1238c2ecf20Sopenharmony_ci ret = i2c_smbus_read_byte_data(client, 1248c2ecf20Sopenharmony_ci VL_REG_RESULT_RANGE_STATUS); 1258c2ecf20Sopenharmony_ci if (ret < 0) 1268c2ecf20Sopenharmony_ci return ret; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci if (ret & VL_REG_RESULT_RANGE_STATUS_COMPLETE) 1298c2ecf20Sopenharmony_ci break; 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci usleep_range(1000, 5000); 1328c2ecf20Sopenharmony_ci } while (--tries); 1338c2ecf20Sopenharmony_ci if (!tries) 1348c2ecf20Sopenharmony_ci return -ETIMEDOUT; 1358c2ecf20Sopenharmony_ci } 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci ret = i2c_smbus_read_i2c_block_data(client, VL_REG_RESULT_RANGE_STATUS, 1388c2ecf20Sopenharmony_ci 12, buffer); 1398c2ecf20Sopenharmony_ci if (ret < 0) 1408c2ecf20Sopenharmony_ci return ret; 1418c2ecf20Sopenharmony_ci else if (ret != 12) 1428c2ecf20Sopenharmony_ci return -EREMOTEIO; 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci /* Values should be between 30~1200 in millimeters. */ 1458c2ecf20Sopenharmony_ci *val = (buffer[10] << 8) + buffer[11]; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci return 0; 1488c2ecf20Sopenharmony_ci} 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_cistatic const struct iio_chan_spec vl53l0x_channels[] = { 1518c2ecf20Sopenharmony_ci { 1528c2ecf20Sopenharmony_ci .type = IIO_DISTANCE, 1538c2ecf20Sopenharmony_ci .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 1548c2ecf20Sopenharmony_ci BIT(IIO_CHAN_INFO_SCALE), 1558c2ecf20Sopenharmony_ci }, 1568c2ecf20Sopenharmony_ci}; 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_cistatic int vl53l0x_read_raw(struct iio_dev *indio_dev, 1598c2ecf20Sopenharmony_ci const struct iio_chan_spec *chan, 1608c2ecf20Sopenharmony_ci int *val, int *val2, long mask) 1618c2ecf20Sopenharmony_ci{ 1628c2ecf20Sopenharmony_ci struct vl53l0x_data *data = iio_priv(indio_dev); 1638c2ecf20Sopenharmony_ci int ret; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci if (chan->type != IIO_DISTANCE) 1668c2ecf20Sopenharmony_ci return -EINVAL; 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci switch (mask) { 1698c2ecf20Sopenharmony_ci case IIO_CHAN_INFO_RAW: 1708c2ecf20Sopenharmony_ci ret = vl53l0x_read_proximity(data, chan, val); 1718c2ecf20Sopenharmony_ci if (ret < 0) 1728c2ecf20Sopenharmony_ci return ret; 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci return IIO_VAL_INT; 1758c2ecf20Sopenharmony_ci case IIO_CHAN_INFO_SCALE: 1768c2ecf20Sopenharmony_ci *val = 0; 1778c2ecf20Sopenharmony_ci *val2 = 1000; 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci return IIO_VAL_INT_PLUS_MICRO; 1808c2ecf20Sopenharmony_ci default: 1818c2ecf20Sopenharmony_ci return -EINVAL; 1828c2ecf20Sopenharmony_ci } 1838c2ecf20Sopenharmony_ci} 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_cistatic const struct iio_info vl53l0x_info = { 1868c2ecf20Sopenharmony_ci .read_raw = vl53l0x_read_raw, 1878c2ecf20Sopenharmony_ci}; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_cistatic int vl53l0x_probe(struct i2c_client *client) 1908c2ecf20Sopenharmony_ci{ 1918c2ecf20Sopenharmony_ci struct vl53l0x_data *data; 1928c2ecf20Sopenharmony_ci struct iio_dev *indio_dev; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); 1958c2ecf20Sopenharmony_ci if (!indio_dev) 1968c2ecf20Sopenharmony_ci return -ENOMEM; 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci data = iio_priv(indio_dev); 1998c2ecf20Sopenharmony_ci data->client = client; 2008c2ecf20Sopenharmony_ci i2c_set_clientdata(client, indio_dev); 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci if (!i2c_check_functionality(client->adapter, 2038c2ecf20Sopenharmony_ci I2C_FUNC_SMBUS_READ_I2C_BLOCK | 2048c2ecf20Sopenharmony_ci I2C_FUNC_SMBUS_BYTE_DATA)) 2058c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci indio_dev->name = "vl53l0x"; 2088c2ecf20Sopenharmony_ci indio_dev->info = &vl53l0x_info; 2098c2ecf20Sopenharmony_ci indio_dev->channels = vl53l0x_channels; 2108c2ecf20Sopenharmony_ci indio_dev->num_channels = ARRAY_SIZE(vl53l0x_channels); 2118c2ecf20Sopenharmony_ci indio_dev->modes = INDIO_DIRECT_MODE; 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci /* usage of interrupt is optional */ 2148c2ecf20Sopenharmony_ci if (client->irq) { 2158c2ecf20Sopenharmony_ci int ret; 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci init_completion(&data->completion); 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci ret = vl53l0x_configure_irq(client, indio_dev); 2208c2ecf20Sopenharmony_ci if (ret) 2218c2ecf20Sopenharmony_ci return ret; 2228c2ecf20Sopenharmony_ci } 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci return devm_iio_device_register(&client->dev, indio_dev); 2258c2ecf20Sopenharmony_ci} 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_cistatic const struct of_device_id st_vl53l0x_dt_match[] = { 2288c2ecf20Sopenharmony_ci { .compatible = "st,vl53l0x", }, 2298c2ecf20Sopenharmony_ci { } 2308c2ecf20Sopenharmony_ci}; 2318c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, st_vl53l0x_dt_match); 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_cistatic struct i2c_driver vl53l0x_driver = { 2348c2ecf20Sopenharmony_ci .driver = { 2358c2ecf20Sopenharmony_ci .name = "vl53l0x-i2c", 2368c2ecf20Sopenharmony_ci .of_match_table = st_vl53l0x_dt_match, 2378c2ecf20Sopenharmony_ci }, 2388c2ecf20Sopenharmony_ci .probe_new = vl53l0x_probe, 2398c2ecf20Sopenharmony_ci}; 2408c2ecf20Sopenharmony_cimodule_i2c_driver(vl53l0x_driver); 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ciMODULE_AUTHOR("Song Qiang <songqiang1304521@gmail.com>"); 2438c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("ST vl53l0x ToF ranging sensor driver"); 2448c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 245