xref: /linux/drivers/iio/light/ltr390.c (revision 7ec462100ef9142344ddbf86f2c3008b97acddbe)
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, &ltr390_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 = &ltr390_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