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
,
478 const struct i2c_device_id
*id
)
480 struct iio_dev
*indio_dev
;
481 struct lv0104cs_private
*lv0104cs
;
484 indio_dev
= devm_iio_device_alloc(&client
->dev
, sizeof(*lv0104cs
));
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
);
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
[] = {
517 MODULE_DEVICE_TABLE(i2c
, lv0104cs_id
);
519 static struct i2c_driver lv0104cs_i2c_driver
= {
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");