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 #include <linux/bitfield.h>
27
28 #include <linux/iio/iio.h>
29
30 #include <linux/unaligned.h>
31
32 #define LTR390_MAIN_CTRL 0x00
33 #define LTR390_ALS_UVS_MEAS_RATE 0x04
34 #define LTR390_ALS_UVS_GAIN 0x05
35 #define LTR390_PART_ID 0x06
36 #define LTR390_ALS_DATA 0x0D
37 #define LTR390_UVS_DATA 0x10
38 #define LTR390_INT_CFG 0x19
39
40 #define LTR390_PART_NUMBER_ID 0xb
41 #define LTR390_ALS_UVS_GAIN_MASK 0x07
42 #define LTR390_ALS_UVS_INT_TIME_MASK 0x70
43 #define LTR390_ALS_UVS_INT_TIME(x) FIELD_PREP(LTR390_ALS_UVS_INT_TIME_MASK, (x))
44
45 #define LTR390_SW_RESET BIT(4)
46 #define LTR390_UVS_MODE BIT(3)
47 #define LTR390_SENSOR_ENABLE BIT(1)
48
49 #define LTR390_FRACTIONAL_PRECISION 100
50
51 /*
52 * At 20-bit resolution (integration time: 400ms) and 18x gain, 2300 counts of
53 * the sensor are equal to 1 UV Index [Datasheet Page#8].
54 *
55 * For the default resolution of 18-bit (integration time: 100ms) and default
56 * gain of 3x, the counts/uvi are calculated as follows:
57 * 2300 / ((3/18) * (100/400)) = 95.83
58 */
59 #define LTR390_COUNTS_PER_UVI 96
60
61 /*
62 * Window Factor is needed when the device is under Window glass with coated
63 * tinted ink. This is to compensate for the light loss due to the lower
64 * transmission rate of the window glass and helps * in calculating lux.
65 */
66 #define LTR390_WINDOW_FACTOR 1
67
68 enum ltr390_mode {
69 LTR390_SET_ALS_MODE,
70 LTR390_SET_UVS_MODE,
71 };
72
73 struct ltr390_data {
74 struct regmap *regmap;
75 struct i2c_client *client;
76 /* Protects device from simulataneous reads */
77 struct mutex lock;
78 enum ltr390_mode mode;
79 int gain;
80 int int_time_us;
81 };
82
83 static const struct regmap_config ltr390_regmap_config = {
84 .name = "ltr390",
85 .reg_bits = 8,
86 .reg_stride = 1,
87 .val_bits = 8,
88 };
89
ltr390_register_read(struct ltr390_data * data,u8 register_address)90 static int ltr390_register_read(struct ltr390_data *data, u8 register_address)
91 {
92 struct device *dev = &data->client->dev;
93 int ret;
94 u8 recieve_buffer[3];
95
96 ret = regmap_bulk_read(data->regmap, register_address, recieve_buffer,
97 sizeof(recieve_buffer));
98 if (ret) {
99 dev_err(dev, "failed to read measurement data");
100 return ret;
101 }
102
103 return get_unaligned_le24(recieve_buffer);
104 }
105
ltr390_set_mode(struct ltr390_data * data,enum ltr390_mode mode)106 static int ltr390_set_mode(struct ltr390_data *data, enum ltr390_mode mode)
107 {
108 int ret;
109
110 if (data->mode == mode)
111 return 0;
112
113 switch (mode) {
114 case LTR390_SET_ALS_MODE:
115 ret = regmap_clear_bits(data->regmap, LTR390_MAIN_CTRL, LTR390_UVS_MODE);
116 break;
117
118 case LTR390_SET_UVS_MODE:
119 ret = regmap_set_bits(data->regmap, LTR390_MAIN_CTRL, LTR390_UVS_MODE);
120 break;
121 }
122
123 if (ret)
124 return ret;
125
126 data->mode = mode;
127 return 0;
128 }
129
ltr390_counts_per_uvi(struct ltr390_data * data)130 static int ltr390_counts_per_uvi(struct ltr390_data *data)
131 {
132 const int orig_gain = 18;
133 const int orig_int_time = 400;
134
135 return DIV_ROUND_CLOSEST(23 * data->gain * data->int_time_us, 10 * orig_gain * orig_int_time);
136 }
137
ltr390_read_raw(struct iio_dev * iio_device,struct iio_chan_spec const * chan,int * val,int * val2,long mask)138 static int ltr390_read_raw(struct iio_dev *iio_device,
139 struct iio_chan_spec const *chan, int *val,
140 int *val2, long mask)
141 {
142 int ret;
143 struct ltr390_data *data = iio_priv(iio_device);
144
145 guard(mutex)(&data->lock);
146 switch (mask) {
147 case IIO_CHAN_INFO_RAW:
148 switch (chan->type) {
149 case IIO_UVINDEX:
150 ret = ltr390_set_mode(data, LTR390_SET_UVS_MODE);
151 if (ret < 0)
152 return ret;
153
154 ret = ltr390_register_read(data, LTR390_UVS_DATA);
155 if (ret < 0)
156 return ret;
157 break;
158
159 case IIO_LIGHT:
160 ret = ltr390_set_mode(data, LTR390_SET_ALS_MODE);
161 if (ret < 0)
162 return ret;
163
164 ret = ltr390_register_read(data, LTR390_ALS_DATA);
165 if (ret < 0)
166 return ret;
167 break;
168
169 default:
170 return -EINVAL;
171 }
172 *val = ret;
173 return IIO_VAL_INT;
174 case IIO_CHAN_INFO_SCALE:
175 switch (chan->type) {
176 case IIO_UVINDEX:
177 *val = LTR390_WINDOW_FACTOR * LTR390_FRACTIONAL_PRECISION;
178 *val2 = ltr390_counts_per_uvi(data);
179 return IIO_VAL_FRACTIONAL;
180
181 case IIO_LIGHT:
182 *val = LTR390_WINDOW_FACTOR * 6 * 100;
183 *val2 = data->gain * data->int_time_us;
184 return IIO_VAL_FRACTIONAL;
185
186 default:
187 return -EINVAL;
188 }
189
190 case IIO_CHAN_INFO_INT_TIME:
191 *val = data->int_time_us;
192 return IIO_VAL_INT;
193
194 default:
195 return -EINVAL;
196 }
197 }
198
199 /* integration time in us */
200 static const int ltr390_int_time_map_us[] = { 400000, 200000, 100000, 50000, 25000, 12500 };
201 static const int ltr390_gain_map[] = { 1, 3, 6, 9, 18 };
202
203 static const struct iio_chan_spec ltr390_channels[] = {
204 /* UV sensor */
205 {
206 .type = IIO_UVINDEX,
207 .scan_index = 0,
208 .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
209 .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME),
210 .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_INT_TIME) | BIT(IIO_CHAN_INFO_SCALE)
211 },
212 /* ALS sensor */
213 {
214 .type = IIO_LIGHT,
215 .scan_index = 1,
216 .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
217 .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME),
218 .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_INT_TIME) | BIT(IIO_CHAN_INFO_SCALE)
219 },
220 };
221
ltr390_set_gain(struct ltr390_data * data,int val)222 static int ltr390_set_gain(struct ltr390_data *data, int val)
223 {
224 int ret, idx;
225
226 for (idx = 0; idx < ARRAY_SIZE(ltr390_gain_map); idx++) {
227 if (ltr390_gain_map[idx] != val)
228 continue;
229
230 guard(mutex)(&data->lock);
231 ret = regmap_update_bits(data->regmap,
232 LTR390_ALS_UVS_GAIN,
233 LTR390_ALS_UVS_GAIN_MASK, idx);
234 if (ret)
235 return ret;
236
237 data->gain = ltr390_gain_map[idx];
238 return 0;
239 }
240
241 return -EINVAL;
242 }
243
ltr390_set_int_time(struct ltr390_data * data,int val)244 static int ltr390_set_int_time(struct ltr390_data *data, int val)
245 {
246 int ret, idx;
247
248 for (idx = 0; idx < ARRAY_SIZE(ltr390_int_time_map_us); idx++) {
249 if (ltr390_int_time_map_us[idx] != val)
250 continue;
251
252 guard(mutex)(&data->lock);
253 ret = regmap_update_bits(data->regmap,
254 LTR390_ALS_UVS_MEAS_RATE,
255 LTR390_ALS_UVS_INT_TIME_MASK,
256 LTR390_ALS_UVS_INT_TIME(idx));
257 if (ret)
258 return ret;
259
260 data->int_time_us = ltr390_int_time_map_us[idx];
261 return 0;
262 }
263
264 return -EINVAL;
265 }
266
ltr390_read_avail(struct iio_dev * indio_dev,struct iio_chan_spec const * chan,const int ** vals,int * type,int * length,long mask)267 static int ltr390_read_avail(struct iio_dev *indio_dev, struct iio_chan_spec const *chan,
268 const int **vals, int *type, int *length, long mask)
269 {
270 switch (mask) {
271 case IIO_CHAN_INFO_SCALE:
272 *length = ARRAY_SIZE(ltr390_gain_map);
273 *type = IIO_VAL_INT;
274 *vals = ltr390_gain_map;
275 return IIO_AVAIL_LIST;
276 case IIO_CHAN_INFO_INT_TIME:
277 *length = ARRAY_SIZE(ltr390_int_time_map_us);
278 *type = IIO_VAL_INT;
279 *vals = ltr390_int_time_map_us;
280 return IIO_AVAIL_LIST;
281 default:
282 return -EINVAL;
283 }
284 }
285
ltr390_write_raw(struct iio_dev * indio_dev,struct iio_chan_spec const * chan,int val,int val2,long mask)286 static int ltr390_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan,
287 int val, int val2, long mask)
288 {
289 struct ltr390_data *data = iio_priv(indio_dev);
290
291 switch (mask) {
292 case IIO_CHAN_INFO_SCALE:
293 if (val2 != 0)
294 return -EINVAL;
295
296 return ltr390_set_gain(data, val);
297
298 case IIO_CHAN_INFO_INT_TIME:
299 if (val2 != 0)
300 return -EINVAL;
301
302 return ltr390_set_int_time(data, val);
303
304 default:
305 return -EINVAL;
306 }
307 }
308
309 static const struct iio_info ltr390_info = {
310 .read_raw = ltr390_read_raw,
311 .write_raw = ltr390_write_raw,
312 .read_avail = ltr390_read_avail,
313 };
314
ltr390_probe(struct i2c_client * client)315 static int ltr390_probe(struct i2c_client *client)
316 {
317 struct ltr390_data *data;
318 struct iio_dev *indio_dev;
319 struct device *dev;
320 int ret, part_number;
321
322 dev = &client->dev;
323 indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
324 if (!indio_dev)
325 return -ENOMEM;
326
327 data = iio_priv(indio_dev);
328
329 data->regmap = devm_regmap_init_i2c(client, <r390_regmap_config);
330 if (IS_ERR(data->regmap))
331 return dev_err_probe(dev, PTR_ERR(data->regmap),
332 "regmap initialization failed\n");
333
334 data->client = client;
335 /* default value of integration time from pg: 15 of the datasheet */
336 data->int_time_us = 100000;
337 /* default value of gain from pg: 16 of the datasheet */
338 data->gain = 3;
339 /* default mode for ltr390 is ALS mode */
340 data->mode = LTR390_SET_ALS_MODE;
341
342 mutex_init(&data->lock);
343
344 indio_dev->info = <r390_info;
345 indio_dev->channels = ltr390_channels;
346 indio_dev->num_channels = ARRAY_SIZE(ltr390_channels);
347 indio_dev->name = "ltr390";
348
349 ret = regmap_read(data->regmap, LTR390_PART_ID, &part_number);
350 if (ret)
351 return dev_err_probe(dev, ret,
352 "failed to get sensor's part id\n");
353 /* Lower 4 bits of `part_number` change with hardware revisions */
354 if (part_number >> 4 != LTR390_PART_NUMBER_ID)
355 dev_info(dev, "received invalid product id: 0x%x", part_number);
356 dev_dbg(dev, "LTR390, product id: 0x%x\n", part_number);
357
358 /* reset sensor, chip fails to respond to this, so ignore any errors */
359 regmap_set_bits(data->regmap, LTR390_MAIN_CTRL, LTR390_SW_RESET);
360
361 /* Wait for the registers to reset before proceeding */
362 usleep_range(1000, 2000);
363
364 ret = regmap_set_bits(data->regmap, LTR390_MAIN_CTRL, LTR390_SENSOR_ENABLE);
365 if (ret)
366 return dev_err_probe(dev, ret, "failed to enable the sensor\n");
367
368 return devm_iio_device_register(dev, indio_dev);
369 }
370
371 static const struct i2c_device_id ltr390_id[] = {
372 { "ltr390" },
373 { /* Sentinel */ }
374 };
375 MODULE_DEVICE_TABLE(i2c, ltr390_id);
376
377 static const struct of_device_id ltr390_of_table[] = {
378 { .compatible = "liteon,ltr390" },
379 { /* Sentinel */ }
380 };
381 MODULE_DEVICE_TABLE(of, ltr390_of_table);
382
383 static struct i2c_driver ltr390_driver = {
384 .driver = {
385 .name = "ltr390",
386 .of_match_table = ltr390_of_table,
387 },
388 .probe = ltr390_probe,
389 .id_table = ltr390_id,
390 };
391 module_i2c_driver(ltr390_driver);
392
393 MODULE_AUTHOR("Anshul Dalal <anshulusr@gmail.com>");
394 MODULE_DESCRIPTION("Lite-On LTR390 ALS and UV sensor Driver");
395 MODULE_LICENSE("GPL");
396