1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Support for ST VL53L0X FlightSense ToF Ranging Sensor on a i2c bus. 4 * 5 * Copyright (C) 2016 STMicroelectronics Imaging Division. 6 * Copyright (C) 2018 Song Qiang <songqiang1304521@gmail.com> 7 * 8 * Datasheet available at 9 * <https://www.st.com/resource/en/datasheet/vl53l0x.pdf> 10 * 11 * Default 7-bit i2c slave address 0x29. 12 * 13 * TODO: FIFO buffer, continuous mode, interrupts, range selection, 14 * sensor ID check. 15 */ 16 17 #include <linux/delay.h> 18 #include <linux/i2c.h> 19 #include <linux/module.h> 20 21 #include <linux/iio/iio.h> 22 23 #define VL_REG_SYSRANGE_START 0x00 24 25 #define VL_REG_SYSRANGE_MODE_MASK GENMASK(3, 0) 26 #define VL_REG_SYSRANGE_MODE_SINGLESHOT 0x00 27 #define VL_REG_SYSRANGE_MODE_START_STOP BIT(0) 28 #define VL_REG_SYSRANGE_MODE_BACKTOBACK BIT(1) 29 #define VL_REG_SYSRANGE_MODE_TIMED BIT(2) 30 #define VL_REG_SYSRANGE_MODE_HISTOGRAM BIT(3) 31 32 #define VL_REG_RESULT_INT_STATUS 0x13 33 #define VL_REG_RESULT_RANGE_STATUS 0x14 34 #define VL_REG_RESULT_RANGE_STATUS_COMPLETE BIT(0) 35 36 struct vl53l0x_data { 37 struct i2c_client *client; 38 }; 39 40 static int vl53l0x_read_proximity(struct vl53l0x_data *data, 41 const struct iio_chan_spec *chan, 42 int *val) 43 { 44 struct i2c_client *client = data->client; 45 u16 tries = 20; 46 u8 buffer[12]; 47 int ret; 48 49 ret = i2c_smbus_write_byte_data(client, VL_REG_SYSRANGE_START, 1); 50 if (ret < 0) 51 return ret; 52 53 do { 54 ret = i2c_smbus_read_byte_data(client, 55 VL_REG_RESULT_RANGE_STATUS); 56 if (ret < 0) 57 return ret; 58 59 if (ret & VL_REG_RESULT_RANGE_STATUS_COMPLETE) 60 break; 61 62 usleep_range(1000, 5000); 63 } while (--tries); 64 if (!tries) 65 return -ETIMEDOUT; 66 67 ret = i2c_smbus_read_i2c_block_data(client, VL_REG_RESULT_RANGE_STATUS, 68 12, buffer); 69 if (ret < 0) 70 return ret; 71 else if (ret != 12) 72 return -EREMOTEIO; 73 74 /* Values should be between 30~1200 in millimeters. */ 75 *val = (buffer[10] << 8) + buffer[11]; 76 77 return 0; 78 } 79 80 static const struct iio_chan_spec vl53l0x_channels[] = { 81 { 82 .type = IIO_DISTANCE, 83 .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 84 BIT(IIO_CHAN_INFO_SCALE), 85 }, 86 }; 87 88 static int vl53l0x_read_raw(struct iio_dev *indio_dev, 89 const struct iio_chan_spec *chan, 90 int *val, int *val2, long mask) 91 { 92 struct vl53l0x_data *data = iio_priv(indio_dev); 93 int ret; 94 95 if (chan->type != IIO_DISTANCE) 96 return -EINVAL; 97 98 switch (mask) { 99 case IIO_CHAN_INFO_RAW: 100 ret = vl53l0x_read_proximity(data, chan, val); 101 if (ret < 0) 102 return ret; 103 104 return IIO_VAL_INT; 105 case IIO_CHAN_INFO_SCALE: 106 *val = 0; 107 *val2 = 1000; 108 109 return IIO_VAL_INT_PLUS_MICRO; 110 default: 111 return -EINVAL; 112 } 113 } 114 115 static const struct iio_info vl53l0x_info = { 116 .read_raw = vl53l0x_read_raw, 117 }; 118 119 static int vl53l0x_probe(struct i2c_client *client) 120 { 121 struct vl53l0x_data *data; 122 struct iio_dev *indio_dev; 123 124 indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); 125 if (!indio_dev) 126 return -ENOMEM; 127 128 data = iio_priv(indio_dev); 129 data->client = client; 130 i2c_set_clientdata(client, indio_dev); 131 132 if (!i2c_check_functionality(client->adapter, 133 I2C_FUNC_SMBUS_READ_I2C_BLOCK | 134 I2C_FUNC_SMBUS_BYTE_DATA)) 135 return -EOPNOTSUPP; 136 137 indio_dev->dev.parent = &client->dev; 138 indio_dev->name = "vl53l0x"; 139 indio_dev->info = &vl53l0x_info; 140 indio_dev->channels = vl53l0x_channels; 141 indio_dev->num_channels = ARRAY_SIZE(vl53l0x_channels); 142 indio_dev->modes = INDIO_DIRECT_MODE; 143 144 return devm_iio_device_register(&client->dev, indio_dev); 145 } 146 147 static const struct of_device_id st_vl53l0x_dt_match[] = { 148 { .compatible = "st,vl53l0x", }, 149 { } 150 }; 151 MODULE_DEVICE_TABLE(of, st_vl53l0x_dt_match); 152 153 static struct i2c_driver vl53l0x_driver = { 154 .driver = { 155 .name = "vl53l0x-i2c", 156 .of_match_table = st_vl53l0x_dt_match, 157 }, 158 .probe_new = vl53l0x_probe, 159 }; 160 module_i2c_driver(vl53l0x_driver); 161 162 MODULE_AUTHOR("Song Qiang <songqiang1304521@gmail.com>"); 163 MODULE_DESCRIPTION("ST vl53l0x ToF ranging sensor driver"); 164 MODULE_LICENSE("GPL v2"); 165