1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * IIO driver for Lite-On LTR390 ALS and UV sensor 4 * (7-bit I2C slave address 0x53) 5 * 6 * Based on the work of: 7 * Shreeya Patel and Shi Zhigang (LTRF216 Driver) 8 * 9 * Copyright (C) 2023 Anshul Dalal <anshulusr@gmail.com> 10 * 11 * Datasheet: 12 * https://optoelectronics.liteon.com/upload/download/DS86-2015-0004/LTR-390UV_Final_%20DS_V1%201.pdf 13 * 14 * TODO: 15 * - Support for configurable gain and resolution 16 * - Sensor suspend/resume support 17 * - Add support for reading the ALS 18 * - Interrupt support 19 */ 20 21 #include <linux/i2c.h> 22 #include <linux/math.h> 23 #include <linux/module.h> 24 #include <linux/mutex.h> 25 #include <linux/regmap.h> 26 27 #include <linux/iio/iio.h> 28 29 #include <asm/unaligned.h> 30 31 #define LTR390_MAIN_CTRL 0x00 32 #define LTR390_PART_ID 0x06 33 #define LTR390_UVS_DATA 0x10 34 35 #define LTR390_SW_RESET BIT(4) 36 #define LTR390_UVS_MODE BIT(3) 37 #define LTR390_SENSOR_ENABLE BIT(1) 38 39 #define LTR390_PART_NUMBER_ID 0xb 40 41 /* 42 * At 20-bit resolution (integration time: 400ms) and 18x gain, 2300 counts of 43 * the sensor are equal to 1 UV Index [Datasheet Page#8]. 44 * 45 * For the default resolution of 18-bit (integration time: 100ms) and default 46 * gain of 3x, the counts/uvi are calculated as follows: 47 * 2300 / ((3/18) * (100/400)) = 95.83 48 */ 49 #define LTR390_COUNTS_PER_UVI 96 50 51 /* 52 * Window Factor is needed when the device is under Window glass with coated 53 * tinted ink. This is to compensate for the light loss due to the lower 54 * transmission rate of the window glass and helps * in calculating lux. 55 */ 56 #define LTR390_WINDOW_FACTOR 1 57 58 struct ltr390_data { 59 struct regmap *regmap; 60 struct i2c_client *client; 61 /* Protects device from simulataneous reads */ 62 struct mutex lock; 63 }; 64 65 static const struct regmap_config ltr390_regmap_config = { 66 .name = "ltr390", 67 .reg_bits = 8, 68 .reg_stride = 1, 69 .val_bits = 8, 70 }; 71 72 static int ltr390_register_read(struct ltr390_data *data, u8 register_address) 73 { 74 struct device *dev = &data->client->dev; 75 int ret; 76 u8 recieve_buffer[3]; 77 78 guard(mutex)(&data->lock); 79 80 ret = regmap_bulk_read(data->regmap, register_address, recieve_buffer, 81 sizeof(recieve_buffer)); 82 if (ret) { 83 dev_err(dev, "failed to read measurement data"); 84 return ret; 85 } 86 87 return get_unaligned_le24(recieve_buffer); 88 } 89 90 static int ltr390_read_raw(struct iio_dev *iio_device, 91 struct iio_chan_spec const *chan, int *val, 92 int *val2, long mask) 93 { 94 int ret; 95 struct ltr390_data *data = iio_priv(iio_device); 96 97 switch (mask) { 98 case IIO_CHAN_INFO_RAW: 99 ret = ltr390_register_read(data, LTR390_UVS_DATA); 100 if (ret < 0) 101 return ret; 102 *val = ret; 103 return IIO_VAL_INT; 104 case IIO_CHAN_INFO_SCALE: 105 *val = LTR390_WINDOW_FACTOR; 106 *val2 = LTR390_COUNTS_PER_UVI; 107 return IIO_VAL_FRACTIONAL; 108 default: 109 return -EINVAL; 110 } 111 } 112 113 static const struct iio_info ltr390_info = { 114 .read_raw = ltr390_read_raw, 115 }; 116 117 static const struct iio_chan_spec ltr390_channel = { 118 .type = IIO_UVINDEX, 119 .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE) 120 }; 121 122 static int ltr390_probe(struct i2c_client *client) 123 { 124 struct ltr390_data *data; 125 struct iio_dev *indio_dev; 126 struct device *dev; 127 int ret, part_number; 128 129 dev = &client->dev; 130 indio_dev = devm_iio_device_alloc(dev, sizeof(*data)); 131 if (!indio_dev) 132 return -ENOMEM; 133 134 data = iio_priv(indio_dev); 135 136 data->regmap = devm_regmap_init_i2c(client, <r390_regmap_config); 137 if (IS_ERR(data->regmap)) 138 return dev_err_probe(dev, PTR_ERR(data->regmap), 139 "regmap initialization failed\n"); 140 141 data->client = client; 142 mutex_init(&data->lock); 143 144 indio_dev->info = <r390_info; 145 indio_dev->channels = <r390_channel; 146 indio_dev->num_channels = 1; 147 indio_dev->name = "ltr390"; 148 149 ret = regmap_read(data->regmap, LTR390_PART_ID, &part_number); 150 if (ret) 151 return dev_err_probe(dev, ret, 152 "failed to get sensor's part id\n"); 153 /* Lower 4 bits of `part_number` change with hardware revisions */ 154 if (part_number >> 4 != LTR390_PART_NUMBER_ID) 155 dev_info(dev, "received invalid product id: 0x%x", part_number); 156 dev_dbg(dev, "LTR390, product id: 0x%x\n", part_number); 157 158 /* reset sensor, chip fails to respond to this, so ignore any errors */ 159 regmap_set_bits(data->regmap, LTR390_MAIN_CTRL, LTR390_SW_RESET); 160 161 /* Wait for the registers to reset before proceeding */ 162 usleep_range(1000, 2000); 163 164 ret = regmap_set_bits(data->regmap, LTR390_MAIN_CTRL, 165 LTR390_SENSOR_ENABLE | LTR390_UVS_MODE); 166 if (ret) 167 return dev_err_probe(dev, ret, "failed to enable the sensor\n"); 168 169 return devm_iio_device_register(dev, indio_dev); 170 } 171 172 static const struct i2c_device_id ltr390_id[] = { 173 { "ltr390" }, 174 { /* Sentinel */ } 175 }; 176 MODULE_DEVICE_TABLE(i2c, ltr390_id); 177 178 static const struct of_device_id ltr390_of_table[] = { 179 { .compatible = "liteon,ltr390" }, 180 { /* Sentinel */ } 181 }; 182 MODULE_DEVICE_TABLE(of, ltr390_of_table); 183 184 static struct i2c_driver ltr390_driver = { 185 .driver = { 186 .name = "ltr390", 187 .of_match_table = ltr390_of_table, 188 }, 189 .probe = ltr390_probe, 190 .id_table = ltr390_id, 191 }; 192 module_i2c_driver(ltr390_driver); 193 194 MODULE_AUTHOR("Anshul Dalal <anshulusr@gmail.com>"); 195 MODULE_DESCRIPTION("Lite-On LTR390 ALS and UV sensor Driver"); 196 MODULE_LICENSE("GPL"); 197