WIP FPC-III support
[linux/fpc-iii.git] / drivers / iio / light / lv0104cs.c
blobc2aef88f4e6349f16918a5e3ae64a7f29ac402c5
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3 * lv0104cs.c: LV0104CS Ambient Light Sensor Driver
5 * Copyright (C) 2018
6 * Author: Jeff LaBundy <jeff@labundy.com>
8 * 7-bit I2C slave address: 0x13
10 * Link to data sheet: https://www.onsemi.com/pub/Collateral/LV0104CS-D.PDF
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>
22 #define LV0104CS_REGVAL_MEASURE 0xE0
23 #define LV0104CS_REGVAL_SLEEP 0x00
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
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
36 #define LV0104CS_CALIBSCALE_UNITY 31
38 struct lv0104cs_private {
39 struct i2c_client *client;
40 struct mutex lock;
41 u8 calibscale;
42 u8 scale;
43 u8 int_time;
46 struct lv0104cs_mapping {
47 int val;
48 int val2;
49 u8 regval;
52 static 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 },
118 static 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 },
125 static 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 },
131 static int lv0104cs_write_reg(struct i2c_client *client, u8 regval)
133 int ret;
135 ret = i2c_master_send(client, (char *)&regval, sizeof(regval));
136 if (ret < 0)
137 return ret;
138 if (ret != sizeof(regval))
139 return -EIO;
141 return 0;
144 static int lv0104cs_read_adc(struct i2c_client *client, u16 *adc_output)
146 __be16 regval;
147 int ret;
149 ret = i2c_master_recv(client, (char *)&regval, sizeof(regval));
150 if (ret < 0)
151 return ret;
152 if (ret != sizeof(regval))
153 return -EIO;
155 *adc_output = be16_to_cpu(regval);
157 return 0;
160 static int lv0104cs_get_lux(struct lv0104cs_private *lv0104cs,
161 int *val, int *val2)
163 u8 regval = LV0104CS_REGVAL_MEASURE;
164 u16 adc_output;
165 int ret;
167 regval |= lv0104cs_scales[lv0104cs->scale].regval;
168 regval |= lv0104cs_int_times[lv0104cs->int_time].regval;
169 ret = lv0104cs_write_reg(lv0104cs->client, regval);
170 if (ret)
171 return ret;
173 /* wait for integration time to pass (with margin) */
174 switch (lv0104cs->int_time) {
175 case LV0104CS_INTEG_12_5MS:
176 msleep(50);
177 break;
179 case LV0104CS_INTEG_100MS:
180 msleep(150);
181 break;
183 case LV0104CS_INTEG_200MS:
184 msleep(250);
185 break;
187 default:
188 return -EINVAL;
191 ret = lv0104cs_read_adc(lv0104cs->client, &adc_output);
192 if (ret)
193 return ret;
195 ret = lv0104cs_write_reg(lv0104cs->client, LV0104CS_REGVAL_SLEEP);
196 if (ret)
197 return ret;
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;
206 case LV0104CS_SCALE_1X:
207 *val = adc_output;
208 *val2 = 0;
209 return 0;
211 case LV0104CS_SCALE_2X:
212 *val = adc_output / 2;
213 *val2 = (adc_output % 2) * 500000;
214 return 0;
216 case LV0104CS_SCALE_8X:
217 *val = adc_output / 8;
218 *val2 = (adc_output % 8) * 125000;
219 return 0;
221 default:
222 return -EINVAL;
226 static int lv0104cs_read_raw(struct iio_dev *indio_dev,
227 struct iio_chan_spec const *chan,
228 int *val, int *val2, long mask)
230 struct lv0104cs_private *lv0104cs = iio_priv(indio_dev);
231 int ret;
233 if (chan->type != IIO_LIGHT)
234 return -EINVAL;
236 mutex_lock(&lv0104cs->lock);
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;
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;
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;
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;
264 default:
265 ret = -EINVAL;
268 err_mutex:
269 mutex_unlock(&lv0104cs->lock);
271 return ret;
274 static int lv0104cs_set_calibscale(struct lv0104cs_private *lv0104cs,
275 int val, int val2)
277 int calibscale = val * 1000000 + val2;
278 int floor, ceil, mid;
279 int ret, i, index;
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;
289 /* round down */
290 if (calibscale >= floor && calibscale < mid) {
291 index = i;
292 break;
295 /* round up */
296 if (calibscale >= mid && calibscale <= ceil) {
297 index = i + 1;
298 break;
302 if (i == ARRAY_SIZE(lv0104cs_calibscales) - 1)
303 return -EINVAL;
305 mutex_lock(&lv0104cs->lock);
307 /* set calibscale (sensitivity) */
308 ret = lv0104cs_write_reg(lv0104cs->client,
309 lv0104cs_calibscales[index].regval);
310 if (ret)
311 goto err_mutex;
313 lv0104cs->calibscale = index;
315 err_mutex:
316 mutex_unlock(&lv0104cs->lock);
318 return ret;
321 static int lv0104cs_set_scale(struct lv0104cs_private *lv0104cs,
322 int val, int val2)
324 int i;
326 /* hard matching */
327 for (i = 0; i < ARRAY_SIZE(lv0104cs_scales); i++) {
328 if (val != lv0104cs_scales[i].val)
329 continue;
331 if (val2 == lv0104cs_scales[i].val2)
332 break;
335 if (i == ARRAY_SIZE(lv0104cs_scales))
336 return -EINVAL;
338 mutex_lock(&lv0104cs->lock);
339 lv0104cs->scale = i;
340 mutex_unlock(&lv0104cs->lock);
342 return 0;
345 static int lv0104cs_set_int_time(struct lv0104cs_private *lv0104cs,
346 int val, int val2)
348 int i;
350 /* hard matching */
351 for (i = 0; i < ARRAY_SIZE(lv0104cs_int_times); i++) {
352 if (val != lv0104cs_int_times[i].val)
353 continue;
355 if (val2 == lv0104cs_int_times[i].val2)
356 break;
359 if (i == ARRAY_SIZE(lv0104cs_int_times))
360 return -EINVAL;
362 mutex_lock(&lv0104cs->lock);
363 lv0104cs->int_time = i;
364 mutex_unlock(&lv0104cs->lock);
366 return 0;
369 static int lv0104cs_write_raw(struct iio_dev *indio_dev,
370 struct iio_chan_spec const *chan,
371 int val, int val2, long mask)
373 struct lv0104cs_private *lv0104cs = iio_priv(indio_dev);
375 if (chan->type != IIO_LIGHT)
376 return -EINVAL;
378 switch (mask) {
379 case IIO_CHAN_INFO_CALIBSCALE:
380 return lv0104cs_set_calibscale(lv0104cs, val, val2);
382 case IIO_CHAN_INFO_SCALE:
383 return lv0104cs_set_scale(lv0104cs, val, val2);
385 case IIO_CHAN_INFO_INT_TIME:
386 return lv0104cs_set_int_time(lv0104cs, val, val2);
388 default:
389 return -EINVAL;
393 static ssize_t lv0104cs_show_calibscale_avail(struct device *dev,
394 struct device_attribute *attr, char *buf)
396 ssize_t len = 0;
397 int i;
399 for (i = 0; i < ARRAY_SIZE(lv0104cs_calibscales); i++) {
400 len += scnprintf(buf + len, PAGE_SIZE - len, "%d.%06d ",
401 lv0104cs_calibscales[i].val,
402 lv0104cs_calibscales[i].val2);
405 buf[len - 1] = '\n';
407 return len;
410 static ssize_t lv0104cs_show_scale_avail(struct device *dev,
411 struct device_attribute *attr, char *buf)
413 ssize_t len = 0;
414 int i;
416 for (i = 0; i < ARRAY_SIZE(lv0104cs_scales); i++) {
417 len += scnprintf(buf + len, PAGE_SIZE - len, "%d.%06d ",
418 lv0104cs_scales[i].val,
419 lv0104cs_scales[i].val2);
422 buf[len - 1] = '\n';
424 return len;
427 static ssize_t lv0104cs_show_int_time_avail(struct device *dev,
428 struct device_attribute *attr, char *buf)
430 ssize_t len = 0;
431 int i;
433 for (i = 0; i < ARRAY_SIZE(lv0104cs_int_times); i++) {
434 len += scnprintf(buf + len, PAGE_SIZE - len, "%d.%06d ",
435 lv0104cs_int_times[i].val,
436 lv0104cs_int_times[i].val2);
439 buf[len - 1] = '\n';
441 return len;
444 static IIO_DEVICE_ATTR(calibscale_available, 0444,
445 lv0104cs_show_calibscale_avail, NULL, 0);
446 static IIO_DEVICE_ATTR(scale_available, 0444,
447 lv0104cs_show_scale_avail, NULL, 0);
448 static IIO_DEV_ATTR_INT_TIME_AVAIL(lv0104cs_show_int_time_avail);
450 static 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
457 static const struct attribute_group lv0104cs_attribute_group = {
458 .attrs = lv0104cs_attributes,
461 static const struct iio_info lv0104cs_info = {
462 .attrs = &lv0104cs_attribute_group,
463 .read_raw = &lv0104cs_read_raw,
464 .write_raw = &lv0104cs_write_raw,
467 static const struct iio_chan_spec lv0104cs_channels[] = {
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),
477 static int lv0104cs_probe(struct i2c_client *client,
478 const struct i2c_device_id *id)
480 struct iio_dev *indio_dev;
481 struct lv0104cs_private *lv0104cs;
482 int ret;
484 indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*lv0104cs));
485 if (!indio_dev)
486 return -ENOMEM;
488 lv0104cs = iio_priv(indio_dev);
490 i2c_set_clientdata(client, lv0104cs);
491 lv0104cs->client = client;
493 mutex_init(&lv0104cs->lock);
495 lv0104cs->calibscale = LV0104CS_CALIBSCALE_UNITY;
496 lv0104cs->scale = LV0104CS_SCALE_1X;
497 lv0104cs->int_time = LV0104CS_INTEG_200MS;
499 ret = lv0104cs_write_reg(lv0104cs->client,
500 lv0104cs_calibscales[LV0104CS_CALIBSCALE_UNITY].regval);
501 if (ret)
502 return ret;
504 indio_dev->modes = INDIO_DIRECT_MODE;
505 indio_dev->channels = lv0104cs_channels;
506 indio_dev->num_channels = ARRAY_SIZE(lv0104cs_channels);
507 indio_dev->name = client->name;
508 indio_dev->info = &lv0104cs_info;
510 return devm_iio_device_register(&client->dev, indio_dev);
513 static const struct i2c_device_id lv0104cs_id[] = {
514 { "lv0104cs", 0 },
517 MODULE_DEVICE_TABLE(i2c, lv0104cs_id);
519 static struct i2c_driver lv0104cs_i2c_driver = {
520 .driver = {
521 .name = "lv0104cs",
523 .id_table = lv0104cs_id,
524 .probe = lv0104cs_probe,
526 module_i2c_driver(lv0104cs_i2c_driver);
528 MODULE_AUTHOR("Jeff LaBundy <jeff@labundy.com>");
529 MODULE_DESCRIPTION("LV0104CS Ambient Light Sensor Driver");
530 MODULE_LICENSE("GPL");