1// SPDX-License-Identifier: GPL-2.0+
2/*
3 * lv0104cs.c: LV0104CS Ambient Light Sensor Driver
4 *
5 * Copyright (C) 2018
6 * Author: Jeff LaBundy <jeff@labundy.com>
7 *
8 * 7-bit I2C slave address: 0x13
9 *
10 * Link to data sheet: https://www.onsemi.com/pub/Collateral/LV0104CS-D.PDF
11 */
12
13#include <linux/kernel.h>
14#include <linux/module.h>
15#include <linux/i2c.h>
16#include <linux/err.h>
17#include <linux/mutex.h>
18#include <linux/delay.h>
19#include <linux/iio/iio.h>
20#include <linux/iio/sysfs.h>
21
22#define LV0104CS_REGVAL_MEASURE 0xE0
23#define LV0104CS_REGVAL_SLEEP 0x00
24
25#define LV0104CS_SCALE_0_25X 0
26#define LV0104CS_SCALE_1X 1
27#define LV0104CS_SCALE_2X 2
28#define LV0104CS_SCALE_8X 3
29#define LV0104CS_SCALE_SHIFT 3
30
31#define LV0104CS_INTEG_12_5MS 0
32#define LV0104CS_INTEG_100MS 1
33#define LV0104CS_INTEG_200MS 2
34#define LV0104CS_INTEG_SHIFT 1
35
36#define LV0104CS_CALIBSCALE_UNITY 31
37
38struct lv0104cs_private {
39 struct i2c_client *client;
40 struct mutex lock;
41 u8 calibscale;
42 u8 scale;
43 u8 int_time;
44};
45
46struct lv0104cs_mapping {
47 int val;
48 int val2;
49 u8 regval;
50};
51
52static const struct lv0104cs_mapping lv0104cs_calibscales[] = {
53 { 0, 666666, 0x81 },
54 { 0, 800000, 0x82 },
55 { 0, 857142, 0x83 },
56 { 0, 888888, 0x84 },
57 { 0, 909090, 0x85 },
58 { 0, 923076, 0x86 },
59 { 0, 933333, 0x87 },
60 { 0, 941176, 0x88 },
61 { 0, 947368, 0x89 },
62 { 0, 952380, 0x8A },
63 { 0, 956521, 0x8B },
64 { 0, 960000, 0x8C },
65 { 0, 962962, 0x8D },
66 { 0, 965517, 0x8E },
67 { 0, 967741, 0x8F },
68 { 0, 969696, 0x90 },
69 { 0, 971428, 0x91 },
70 { 0, 972972, 0x92 },
71 { 0, 974358, 0x93 },
72 { 0, 975609, 0x94 },
73 { 0, 976744, 0x95 },
74 { 0, 977777, 0x96 },
75 { 0, 978723, 0x97 },
76 { 0, 979591, 0x98 },
77 { 0, 980392, 0x99 },
78 { 0, 981132, 0x9A },
79 { 0, 981818, 0x9B },
80 { 0, 982456, 0x9C },
81 { 0, 983050, 0x9D },
82 { 0, 983606, 0x9E },
83 { 0, 984126, 0x9F },
84 { 1, 0, 0x80 },
85 { 1, 16129, 0xBF },
86 { 1, 16666, 0xBE },
87 { 1, 17241, 0xBD },
88 { 1, 17857, 0xBC },
89 { 1, 18518, 0xBB },
90 { 1, 19230, 0xBA },
91 { 1, 20000, 0xB9 },
92 { 1, 20833, 0xB8 },
93 { 1, 21739, 0xB7 },
94 { 1, 22727, 0xB6 },
95 { 1, 23809, 0xB5 },
96 { 1, 24999, 0xB4 },
97 { 1, 26315, 0xB3 },
98 { 1, 27777, 0xB2 },
99 { 1, 29411, 0xB1 },
100 { 1, 31250, 0xB0 },
101 { 1, 33333, 0xAF },
102 { 1, 35714, 0xAE },
103 { 1, 38461, 0xAD },
104 { 1, 41666, 0xAC },
105 { 1, 45454, 0xAB },
106 { 1, 50000, 0xAA },
107 { 1, 55555, 0xA9 },
108 { 1, 62500, 0xA8 },
109 { 1, 71428, 0xA7 },
110 { 1, 83333, 0xA6 },
111 { 1, 100000, 0xA5 },
112 { 1, 125000, 0xA4 },
113 { 1, 166666, 0xA3 },
114 { 1, 250000, 0xA2 },
115 { 1, 500000, 0xA1 },
116};
117
118static const struct lv0104cs_mapping lv0104cs_scales[] = {
119 { 0, 250000, LV0104CS_SCALE_0_25X << LV0104CS_SCALE_SHIFT },
120 { 1, 0, LV0104CS_SCALE_1X << LV0104CS_SCALE_SHIFT },
121 { 2, 0, LV0104CS_SCALE_2X << LV0104CS_SCALE_SHIFT },
122 { 8, 0, LV0104CS_SCALE_8X << LV0104CS_SCALE_SHIFT },
123};
124
125static const struct lv0104cs_mapping lv0104cs_int_times[] = {
126 { 0, 12500, LV0104CS_INTEG_12_5MS << LV0104CS_INTEG_SHIFT },
127 { 0, 100000, LV0104CS_INTEG_100MS << LV0104CS_INTEG_SHIFT },
128 { 0, 200000, LV0104CS_INTEG_200MS << LV0104CS_INTEG_SHIFT },
129};
130
131static int lv0104cs_write_reg(struct i2c_client *client, u8 regval)
132{
133 int ret;
134
135 ret = i2c_master_send(client, buf: (char *)&regval, count: sizeof(regval));
136 if (ret < 0)
137 return ret;
138 if (ret != sizeof(regval))
139 return -EIO;
140
141 return 0;
142}
143
144static int lv0104cs_read_adc(struct i2c_client *client, u16 *adc_output)
145{
146 __be16 regval;
147 int ret;
148
149 ret = i2c_master_recv(client, buf: (char *)&regval, count: sizeof(regval));
150 if (ret < 0)
151 return ret;
152 if (ret != sizeof(regval))
153 return -EIO;
154
155 *adc_output = be16_to_cpu(regval);
156
157 return 0;
158}
159
160static int lv0104cs_get_lux(struct lv0104cs_private *lv0104cs,
161 int *val, int *val2)
162{
163 u8 regval = LV0104CS_REGVAL_MEASURE;
164 u16 adc_output;
165 int ret;
166
167 regval |= lv0104cs_scales[lv0104cs->scale].regval;
168 regval |= lv0104cs_int_times[lv0104cs->int_time].regval;
169 ret = lv0104cs_write_reg(client: lv0104cs->client, regval);
170 if (ret)
171 return ret;
172
173 /* wait for integration time to pass (with margin) */
174 switch (lv0104cs->int_time) {
175 case LV0104CS_INTEG_12_5MS:
176 msleep(msecs: 50);
177 break;
178
179 case LV0104CS_INTEG_100MS:
180 msleep(msecs: 150);
181 break;
182
183 case LV0104CS_INTEG_200MS:
184 msleep(msecs: 250);
185 break;
186
187 default:
188 return -EINVAL;
189 }
190
191 ret = lv0104cs_read_adc(client: lv0104cs->client, adc_output: &adc_output);
192 if (ret)
193 return ret;
194
195 ret = lv0104cs_write_reg(client: lv0104cs->client, LV0104CS_REGVAL_SLEEP);
196 if (ret)
197 return ret;
198
199 /* convert ADC output to lux */
200 switch (lv0104cs->scale) {
201 case LV0104CS_SCALE_0_25X:
202 *val = adc_output * 4;
203 *val2 = 0;
204 return 0;
205
206 case LV0104CS_SCALE_1X:
207 *val = adc_output;
208 *val2 = 0;
209 return 0;
210
211 case LV0104CS_SCALE_2X:
212 *val = adc_output / 2;
213 *val2 = (adc_output % 2) * 500000;
214 return 0;
215
216 case LV0104CS_SCALE_8X:
217 *val = adc_output / 8;
218 *val2 = (adc_output % 8) * 125000;
219 return 0;
220
221 default:
222 return -EINVAL;
223 }
224}
225
226static int lv0104cs_read_raw(struct iio_dev *indio_dev,
227 struct iio_chan_spec const *chan,
228 int *val, int *val2, long mask)
229{
230 struct lv0104cs_private *lv0104cs = iio_priv(indio_dev);
231 int ret;
232
233 if (chan->type != IIO_LIGHT)
234 return -EINVAL;
235
236 mutex_lock(&lv0104cs->lock);
237
238 switch (mask) {
239 case IIO_CHAN_INFO_PROCESSED:
240 ret = lv0104cs_get_lux(lv0104cs, val, val2);
241 if (ret)
242 goto err_mutex;
243 ret = IIO_VAL_INT_PLUS_MICRO;
244 break;
245
246 case IIO_CHAN_INFO_CALIBSCALE:
247 *val = lv0104cs_calibscales[lv0104cs->calibscale].val;
248 *val2 = lv0104cs_calibscales[lv0104cs->calibscale].val2;
249 ret = IIO_VAL_INT_PLUS_MICRO;
250 break;
251
252 case IIO_CHAN_INFO_SCALE:
253 *val = lv0104cs_scales[lv0104cs->scale].val;
254 *val2 = lv0104cs_scales[lv0104cs->scale].val2;
255 ret = IIO_VAL_INT_PLUS_MICRO;
256 break;
257
258 case IIO_CHAN_INFO_INT_TIME:
259 *val = lv0104cs_int_times[lv0104cs->int_time].val;
260 *val2 = lv0104cs_int_times[lv0104cs->int_time].val2;
261 ret = IIO_VAL_INT_PLUS_MICRO;
262 break;
263
264 default:
265 ret = -EINVAL;
266 }
267
268err_mutex:
269 mutex_unlock(lock: &lv0104cs->lock);
270
271 return ret;
272}
273
274static int lv0104cs_set_calibscale(struct lv0104cs_private *lv0104cs,
275 int val, int val2)
276{
277 int calibscale = val * 1000000 + val2;
278 int floor, ceil, mid;
279 int ret, i, index;
280
281 /* round to nearest quantized calibscale (sensitivity) */
282 for (i = 0; i < ARRAY_SIZE(lv0104cs_calibscales) - 1; i++) {
283 floor = lv0104cs_calibscales[i].val * 1000000
284 + lv0104cs_calibscales[i].val2;
285 ceil = lv0104cs_calibscales[i + 1].val * 1000000
286 + lv0104cs_calibscales[i + 1].val2;
287 mid = (floor + ceil) / 2;
288
289 /* round down */
290 if (calibscale >= floor && calibscale < mid) {
291 index = i;
292 break;
293 }
294
295 /* round up */
296 if (calibscale >= mid && calibscale <= ceil) {
297 index = i + 1;
298 break;
299 }
300 }
301
302 if (i == ARRAY_SIZE(lv0104cs_calibscales) - 1)
303 return -EINVAL;
304
305 mutex_lock(&lv0104cs->lock);
306
307 /* set calibscale (sensitivity) */
308 ret = lv0104cs_write_reg(client: lv0104cs->client,
309 regval: lv0104cs_calibscales[index].regval);
310 if (ret)
311 goto err_mutex;
312
313 lv0104cs->calibscale = index;
314
315err_mutex:
316 mutex_unlock(lock: &lv0104cs->lock);
317
318 return ret;
319}
320
321static int lv0104cs_set_scale(struct lv0104cs_private *lv0104cs,
322 int val, int val2)
323{
324 int i;
325
326 /* hard matching */
327 for (i = 0; i < ARRAY_SIZE(lv0104cs_scales); i++) {
328 if (val != lv0104cs_scales[i].val)
329 continue;
330
331 if (val2 == lv0104cs_scales[i].val2)
332 break;
333 }
334
335 if (i == ARRAY_SIZE(lv0104cs_scales))
336 return -EINVAL;
337
338 mutex_lock(&lv0104cs->lock);
339 lv0104cs->scale = i;
340 mutex_unlock(lock: &lv0104cs->lock);
341
342 return 0;
343}
344
345static int lv0104cs_set_int_time(struct lv0104cs_private *lv0104cs,
346 int val, int val2)
347{
348 int i;
349
350 /* hard matching */
351 for (i = 0; i < ARRAY_SIZE(lv0104cs_int_times); i++) {
352 if (val != lv0104cs_int_times[i].val)
353 continue;
354
355 if (val2 == lv0104cs_int_times[i].val2)
356 break;
357 }
358
359 if (i == ARRAY_SIZE(lv0104cs_int_times))
360 return -EINVAL;
361
362 mutex_lock(&lv0104cs->lock);
363 lv0104cs->int_time = i;
364 mutex_unlock(lock: &lv0104cs->lock);
365
366 return 0;
367}
368
369static int lv0104cs_write_raw(struct iio_dev *indio_dev,
370 struct iio_chan_spec const *chan,
371 int val, int val2, long mask)
372{
373 struct lv0104cs_private *lv0104cs = iio_priv(indio_dev);
374
375 if (chan->type != IIO_LIGHT)
376 return -EINVAL;
377
378 switch (mask) {
379 case IIO_CHAN_INFO_CALIBSCALE:
380 return lv0104cs_set_calibscale(lv0104cs, val, val2);
381
382 case IIO_CHAN_INFO_SCALE:
383 return lv0104cs_set_scale(lv0104cs, val, val2);
384
385 case IIO_CHAN_INFO_INT_TIME:
386 return lv0104cs_set_int_time(lv0104cs, val, val2);
387
388 default:
389 return -EINVAL;
390 }
391}
392
393static ssize_t lv0104cs_show_calibscale_avail(struct device *dev,
394 struct device_attribute *attr, char *buf)
395{
396 ssize_t len = 0;
397 int i;
398
399 for (i = 0; i < ARRAY_SIZE(lv0104cs_calibscales); i++) {
400 len += scnprintf(buf: buf + len, PAGE_SIZE - len, fmt: "%d.%06d ",
401 lv0104cs_calibscales[i].val,
402 lv0104cs_calibscales[i].val2);
403 }
404
405 buf[len - 1] = '\n';
406
407 return len;
408}
409
410static ssize_t lv0104cs_show_scale_avail(struct device *dev,
411 struct device_attribute *attr, char *buf)
412{
413 ssize_t len = 0;
414 int i;
415
416 for (i = 0; i < ARRAY_SIZE(lv0104cs_scales); i++) {
417 len += scnprintf(buf: buf + len, PAGE_SIZE - len, fmt: "%d.%06d ",
418 lv0104cs_scales[i].val,
419 lv0104cs_scales[i].val2);
420 }
421
422 buf[len - 1] = '\n';
423
424 return len;
425}
426
427static ssize_t lv0104cs_show_int_time_avail(struct device *dev,
428 struct device_attribute *attr, char *buf)
429{
430 ssize_t len = 0;
431 int i;
432
433 for (i = 0; i < ARRAY_SIZE(lv0104cs_int_times); i++) {
434 len += scnprintf(buf: buf + len, PAGE_SIZE - len, fmt: "%d.%06d ",
435 lv0104cs_int_times[i].val,
436 lv0104cs_int_times[i].val2);
437 }
438
439 buf[len - 1] = '\n';
440
441 return len;
442}
443
444static IIO_DEVICE_ATTR(calibscale_available, 0444,
445 lv0104cs_show_calibscale_avail, NULL, 0);
446static IIO_DEVICE_ATTR(scale_available, 0444,
447 lv0104cs_show_scale_avail, NULL, 0);
448static IIO_DEV_ATTR_INT_TIME_AVAIL(lv0104cs_show_int_time_avail);
449
450static struct attribute *lv0104cs_attributes[] = {
451 &iio_dev_attr_calibscale_available.dev_attr.attr,
452 &iio_dev_attr_scale_available.dev_attr.attr,
453 &iio_dev_attr_integration_time_available.dev_attr.attr,
454 NULL
455};
456
457static const struct attribute_group lv0104cs_attribute_group = {
458 .attrs = lv0104cs_attributes,
459};
460
461static const struct iio_info lv0104cs_info = {
462 .attrs = &lv0104cs_attribute_group,
463 .read_raw = &lv0104cs_read_raw,
464 .write_raw = &lv0104cs_write_raw,
465};
466
467static const struct iio_chan_spec lv0104cs_channels[] = {
468 {
469 .type = IIO_LIGHT,
470 .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) |
471 BIT(IIO_CHAN_INFO_CALIBSCALE) |
472 BIT(IIO_CHAN_INFO_SCALE) |
473 BIT(IIO_CHAN_INFO_INT_TIME),
474 },
475};
476
477static int lv0104cs_probe(struct i2c_client *client)
478{
479 struct iio_dev *indio_dev;
480 struct lv0104cs_private *lv0104cs;
481 int ret;
482
483 indio_dev = devm_iio_device_alloc(parent: &client->dev, sizeof_priv: sizeof(*lv0104cs));
484 if (!indio_dev)
485 return -ENOMEM;
486
487 lv0104cs = iio_priv(indio_dev);
488
489 i2c_set_clientdata(client, data: lv0104cs);
490 lv0104cs->client = client;
491
492 mutex_init(&lv0104cs->lock);
493
494 lv0104cs->calibscale = LV0104CS_CALIBSCALE_UNITY;
495 lv0104cs->scale = LV0104CS_SCALE_1X;
496 lv0104cs->int_time = LV0104CS_INTEG_200MS;
497
498 ret = lv0104cs_write_reg(client: lv0104cs->client,
499 regval: lv0104cs_calibscales[LV0104CS_CALIBSCALE_UNITY].regval);
500 if (ret)
501 return ret;
502
503 indio_dev->modes = INDIO_DIRECT_MODE;
504 indio_dev->channels = lv0104cs_channels;
505 indio_dev->num_channels = ARRAY_SIZE(lv0104cs_channels);
506 indio_dev->name = client->name;
507 indio_dev->info = &lv0104cs_info;
508
509 return devm_iio_device_register(&client->dev, indio_dev);
510}
511
512static const struct i2c_device_id lv0104cs_id[] = {
513 { "lv0104cs", 0 },
514 { }
515};
516MODULE_DEVICE_TABLE(i2c, lv0104cs_id);
517
518static struct i2c_driver lv0104cs_i2c_driver = {
519 .driver = {
520 .name = "lv0104cs",
521 },
522 .id_table = lv0104cs_id,
523 .probe = lv0104cs_probe,
524};
525module_i2c_driver(lv0104cs_i2c_driver);
526
527MODULE_AUTHOR("Jeff LaBundy <jeff@labundy.com>");
528MODULE_DESCRIPTION("LV0104CS Ambient Light Sensor Driver");
529MODULE_LICENSE("GPL");
530

source code of linux/drivers/iio/light/lv0104cs.c