1 // SPDX-License-Identifier: GPL-2.0+
3 * lv0104cs.c: LV0104CS Ambient Light Sensor Driver
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;
46 struct lv0104cs_mapping {
52 static const struct lv0104cs_mapping lv0104cs_calibscales[] = {
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)
135 ret = i2c_master_send(client, (char *)®val, sizeof(regval));
138 if (ret != sizeof(regval))
144 static int lv0104cs_read_adc(struct i2c_client *client, u16 *adc_output)
149 ret = i2c_master_recv(client, (char *)®val, sizeof(regval));
152 if (ret != sizeof(regval))
155 *adc_output = be16_to_cpu(regval);
160 static int lv0104cs_get_lux(struct lv0104cs_private *lv0104cs,
163 u8 regval = LV0104CS_REGVAL_MEASURE;
167 regval |= lv0104cs_scales[lv0104cs->scale].regval;
168 regval |= lv0104cs_int_times[lv0104cs->int_time].regval;
169 ret = lv0104cs_write_reg(lv0104cs->client, regval);
173 /* wait for integration time to pass (with margin) */
174 switch (lv0104cs->int_time) {
175 case LV0104CS_INTEG_12_5MS:
179 case LV0104CS_INTEG_100MS:
183 case LV0104CS_INTEG_200MS:
191 ret = lv0104cs_read_adc(lv0104cs->client, &adc_output);
195 ret = lv0104cs_write_reg(lv0104cs->client, LV0104CS_REGVAL_SLEEP);
199 /* convert ADC output to lux */
200 switch (lv0104cs->scale) {
201 case LV0104CS_SCALE_0_25X:
202 *val = adc_output * 4;
206 case LV0104CS_SCALE_1X:
211 case LV0104CS_SCALE_2X:
212 *val = adc_output / 2;
213 *val2 = (adc_output % 2) * 500000;
216 case LV0104CS_SCALE_8X:
217 *val = adc_output / 8;
218 *val2 = (adc_output % 8) * 125000;
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);
233 if (chan->type != IIO_LIGHT)
236 mutex_lock(&lv0104cs->lock);
239 case IIO_CHAN_INFO_PROCESSED:
240 ret = lv0104cs_get_lux(lv0104cs, val, val2);
243 ret = IIO_VAL_INT_PLUS_MICRO;
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;
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;
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;
269 mutex_unlock(&lv0104cs->lock);
274 static int lv0104cs_set_calibscale(struct lv0104cs_private *lv0104cs,
277 int calibscale = val * 1000000 + val2;
278 int floor, ceil, mid;
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;
290 if (calibscale >= floor && calibscale < mid) {
296 if (calibscale >= mid && calibscale <= ceil) {
302 if (i == ARRAY_SIZE(lv0104cs_calibscales) - 1)
305 mutex_lock(&lv0104cs->lock);
307 /* set calibscale (sensitivity) */
308 ret = lv0104cs_write_reg(lv0104cs->client,
309 lv0104cs_calibscales[index].regval);
313 lv0104cs->calibscale = index;
316 mutex_unlock(&lv0104cs->lock);
321 static int lv0104cs_set_scale(struct lv0104cs_private *lv0104cs,
327 for (i = 0; i < ARRAY_SIZE(lv0104cs_scales); i++) {
328 if (val != lv0104cs_scales[i].val)
331 if (val2 == lv0104cs_scales[i].val2)
335 if (i == ARRAY_SIZE(lv0104cs_scales))
338 mutex_lock(&lv0104cs->lock);
340 mutex_unlock(&lv0104cs->lock);
345 static int lv0104cs_set_int_time(struct lv0104cs_private *lv0104cs,
351 for (i = 0; i < ARRAY_SIZE(lv0104cs_int_times); i++) {
352 if (val != lv0104cs_int_times[i].val)
355 if (val2 == lv0104cs_int_times[i].val2)
359 if (i == ARRAY_SIZE(lv0104cs_int_times))
362 mutex_lock(&lv0104cs->lock);
363 lv0104cs->int_time = i;
364 mutex_unlock(&lv0104cs->lock);
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)
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);
393 static ssize_t lv0104cs_show_calibscale_avail(struct device *dev,
394 struct device_attribute *attr, char *buf)
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);
410 static ssize_t lv0104cs_show_scale_avail(struct device *dev,
411 struct device_attribute *attr, char *buf)
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);
427 static ssize_t lv0104cs_show_int_time_avail(struct device *dev,
428 struct device_attribute *attr, char *buf)
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);
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,
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[] = {
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)
479 struct iio_dev *indio_dev;
480 struct lv0104cs_private *lv0104cs;
483 indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*lv0104cs));
487 lv0104cs = iio_priv(indio_dev);
489 i2c_set_clientdata(client, lv0104cs);
490 lv0104cs->client = client;
492 mutex_init(&lv0104cs->lock);
494 lv0104cs->calibscale = LV0104CS_CALIBSCALE_UNITY;
495 lv0104cs->scale = LV0104CS_SCALE_1X;
496 lv0104cs->int_time = LV0104CS_INTEG_200MS;
498 ret = lv0104cs_write_reg(lv0104cs->client,
499 lv0104cs_calibscales[LV0104CS_CALIBSCALE_UNITY].regval);
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;
509 return devm_iio_device_register(&client->dev, indio_dev);
512 static const struct i2c_device_id lv0104cs_id[] = {
516 MODULE_DEVICE_TABLE(i2c, lv0104cs_id);
518 static struct i2c_driver lv0104cs_i2c_driver = {
522 .id_table = lv0104cs_id,
523 .probe = lv0104cs_probe,
525 module_i2c_driver(lv0104cs_i2c_driver);
527 MODULE_AUTHOR("Jeff LaBundy <jeff@labundy.com>");
528 MODULE_DESCRIPTION("LV0104CS Ambient Light Sensor Driver");
529 MODULE_LICENSE("GPL");