1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * IIO driver for Lite-On LTR390 ALS and UV sensor
4 * (7-bit I2C slave address 0x53)
6 * Based on the work of:
7 * Shreeya Patel and Shi Zhigang (LTRF216 Driver)
9 * Copyright (C) 2023 Anshul Dalal <anshulusr@gmail.com>
12 * https://optoelectronics.liteon.com/upload/download/DS86-2015-0004/LTR-390UV_Final_%20DS_V1%201.pdf
15 * - Support for configurable gain and resolution
16 * - Sensor suspend/resume support
17 * - Add support for reading the ALS
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>
27 #include <linux/iio/iio.h>
29 #include <asm/unaligned.h>
31 #define LTR390_MAIN_CTRL 0x00
32 #define LTR390_PART_ID 0x06
33 #define LTR390_UVS_DATA 0x10
35 #define LTR390_SW_RESET BIT(4)
36 #define LTR390_UVS_MODE BIT(3)
37 #define LTR390_SENSOR_ENABLE BIT(1)
39 #define LTR390_PART_NUMBER_ID 0xb
42 * At 20-bit resolution (integration time: 400ms) and 18x gain, 2300 counts of
43 * the sensor are equal to 1 UV Index [Datasheet Page#8].
45 * For the default resolution of 18-bit (integration time: 100ms) and default
46 * gain of 3x, the counts/uvi are calculated as follows:
47 * 2300 / ((3/18) * (100/400)) = 95.83
49 #define LTR390_COUNTS_PER_UVI 96
52 * Window Factor is needed when the device is under Window glass with coated
53 * tinted ink. This is to compensate for the light loss due to the lower
54 * transmission rate of the window glass and helps * in calculating lux.
56 #define LTR390_WINDOW_FACTOR 1
59 struct regmap *regmap;
60 struct i2c_client *client;
61 /* Protects device from simulataneous reads */
65 static const struct regmap_config ltr390_regmap_config = {
72 static int ltr390_register_read(struct ltr390_data *data, u8 register_address)
74 struct device *dev = &data->client->dev;
78 guard(mutex)(&data->lock);
80 ret = regmap_bulk_read(data->regmap, register_address, recieve_buffer,
81 sizeof(recieve_buffer));
83 dev_err(dev, "failed to read measurement data");
87 return get_unaligned_le24(recieve_buffer);
90 static int ltr390_read_raw(struct iio_dev *iio_device,
91 struct iio_chan_spec const *chan, int *val,
95 struct ltr390_data *data = iio_priv(iio_device);
98 case IIO_CHAN_INFO_RAW:
99 ret = ltr390_register_read(data, LTR390_UVS_DATA);
104 case IIO_CHAN_INFO_SCALE:
105 *val = LTR390_WINDOW_FACTOR;
106 *val2 = LTR390_COUNTS_PER_UVI;
107 return IIO_VAL_FRACTIONAL;
113 static const struct iio_info ltr390_info = {
114 .read_raw = ltr390_read_raw,
117 static const struct iio_chan_spec ltr390_channel = {
119 .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE)
122 static int ltr390_probe(struct i2c_client *client)
124 struct ltr390_data *data;
125 struct iio_dev *indio_dev;
127 int ret, part_number;
130 indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
134 data = iio_priv(indio_dev);
136 data->regmap = devm_regmap_init_i2c(client, <r390_regmap_config);
137 if (IS_ERR(data->regmap))
138 return dev_err_probe(dev, PTR_ERR(data->regmap),
139 "regmap initialization failed\n");
141 data->client = client;
142 mutex_init(&data->lock);
144 indio_dev->info = <r390_info;
145 indio_dev->channels = <r390_channel;
146 indio_dev->num_channels = 1;
147 indio_dev->name = "ltr390";
149 ret = regmap_read(data->regmap, LTR390_PART_ID, &part_number);
151 return dev_err_probe(dev, ret,
152 "failed to get sensor's part id\n");
153 /* Lower 4 bits of `part_number` change with hardware revisions */
154 if (part_number >> 4 != LTR390_PART_NUMBER_ID)
155 dev_info(dev, "received invalid product id: 0x%x", part_number);
156 dev_dbg(dev, "LTR390, product id: 0x%x\n", part_number);
158 /* reset sensor, chip fails to respond to this, so ignore any errors */
159 regmap_set_bits(data->regmap, LTR390_MAIN_CTRL, LTR390_SW_RESET);
161 /* Wait for the registers to reset before proceeding */
162 usleep_range(1000, 2000);
164 ret = regmap_set_bits(data->regmap, LTR390_MAIN_CTRL,
165 LTR390_SENSOR_ENABLE | LTR390_UVS_MODE);
167 return dev_err_probe(dev, ret, "failed to enable the sensor\n");
169 return devm_iio_device_register(dev, indio_dev);
172 static const struct i2c_device_id ltr390_id[] = {
176 MODULE_DEVICE_TABLE(i2c, ltr390_id);
178 static const struct of_device_id ltr390_of_table[] = {
179 { .compatible = "liteon,ltr390" },
182 MODULE_DEVICE_TABLE(of, ltr390_of_table);
184 static struct i2c_driver ltr390_driver = {
187 .of_match_table = ltr390_of_table,
189 .probe = ltr390_probe,
190 .id_table = ltr390_id,
192 module_i2c_driver(ltr390_driver);
194 MODULE_AUTHOR("Anshul Dalal <anshulusr@gmail.com>");
195 MODULE_DESCRIPTION("Lite-On LTR390 ALS and UV sensor Driver");
196 MODULE_LICENSE("GPL");