GNU Linux-libre 6.9.1-gnu
[releases.git] / drivers / iio / light / ltr390.c
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * IIO driver for Lite-On LTR390 ALS and UV sensor
4  * (7-bit I2C slave address 0x53)
5  *
6  * Based on the work of:
7  *   Shreeya Patel and Shi Zhigang (LTRF216 Driver)
8  *
9  * Copyright (C) 2023 Anshul Dalal <anshulusr@gmail.com>
10  *
11  * Datasheet:
12  *   https://optoelectronics.liteon.com/upload/download/DS86-2015-0004/LTR-390UV_Final_%20DS_V1%201.pdf
13  *
14  * TODO:
15  *   - Support for configurable gain and resolution
16  *   - Sensor suspend/resume support
17  *   - Add support for reading the ALS
18  *   - Interrupt support
19  */
20
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>
26
27 #include <linux/iio/iio.h>
28
29 #include <asm/unaligned.h>
30
31 #define LTR390_MAIN_CTRL      0x00
32 #define LTR390_PART_ID        0x06
33 #define LTR390_UVS_DATA       0x10
34
35 #define LTR390_SW_RESET       BIT(4)
36 #define LTR390_UVS_MODE       BIT(3)
37 #define LTR390_SENSOR_ENABLE  BIT(1)
38
39 #define LTR390_PART_NUMBER_ID 0xb
40
41 /*
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].
44  *
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
48  */
49 #define LTR390_COUNTS_PER_UVI 96
50
51 /*
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.
55  */
56 #define LTR390_WINDOW_FACTOR 1
57
58 struct ltr390_data {
59         struct regmap *regmap;
60         struct i2c_client *client;
61         /* Protects device from simulataneous reads */
62         struct mutex lock;
63 };
64
65 static const struct regmap_config ltr390_regmap_config = {
66         .name = "ltr390",
67         .reg_bits = 8,
68         .reg_stride = 1,
69         .val_bits = 8,
70 };
71
72 static int ltr390_register_read(struct ltr390_data *data, u8 register_address)
73 {
74         struct device *dev = &data->client->dev;
75         int ret;
76         u8 recieve_buffer[3];
77
78         guard(mutex)(&data->lock);
79
80         ret = regmap_bulk_read(data->regmap, register_address, recieve_buffer,
81                                sizeof(recieve_buffer));
82         if (ret) {
83                 dev_err(dev, "failed to read measurement data");
84                 return ret;
85         }
86
87         return get_unaligned_le24(recieve_buffer);
88 }
89
90 static int ltr390_read_raw(struct iio_dev *iio_device,
91                            struct iio_chan_spec const *chan, int *val,
92                            int *val2, long mask)
93 {
94         int ret;
95         struct ltr390_data *data = iio_priv(iio_device);
96
97         switch (mask) {
98         case IIO_CHAN_INFO_RAW:
99                 ret = ltr390_register_read(data, LTR390_UVS_DATA);
100                 if (ret < 0)
101                         return ret;
102                 *val = ret;
103                 return IIO_VAL_INT;
104         case IIO_CHAN_INFO_SCALE:
105                 *val = LTR390_WINDOW_FACTOR;
106                 *val2 = LTR390_COUNTS_PER_UVI;
107                 return IIO_VAL_FRACTIONAL;
108         default:
109                 return -EINVAL;
110         }
111 }
112
113 static const struct iio_info ltr390_info = {
114         .read_raw = ltr390_read_raw,
115 };
116
117 static const struct iio_chan_spec ltr390_channel = {
118         .type = IIO_UVINDEX,
119         .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE)
120 };
121
122 static int ltr390_probe(struct i2c_client *client)
123 {
124         struct ltr390_data *data;
125         struct iio_dev *indio_dev;
126         struct device *dev;
127         int ret, part_number;
128
129         dev = &client->dev;
130         indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
131         if (!indio_dev)
132                 return -ENOMEM;
133
134         data = iio_priv(indio_dev);
135
136         data->regmap = devm_regmap_init_i2c(client, &ltr390_regmap_config);
137         if (IS_ERR(data->regmap))
138                 return dev_err_probe(dev, PTR_ERR(data->regmap),
139                                      "regmap initialization failed\n");
140
141         data->client = client;
142         mutex_init(&data->lock);
143
144         indio_dev->info = &ltr390_info;
145         indio_dev->channels = &ltr390_channel;
146         indio_dev->num_channels = 1;
147         indio_dev->name = "ltr390";
148
149         ret = regmap_read(data->regmap, LTR390_PART_ID, &part_number);
150         if (ret)
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);
157
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);
160
161         /* Wait for the registers to reset before proceeding */
162         usleep_range(1000, 2000);
163
164         ret = regmap_set_bits(data->regmap, LTR390_MAIN_CTRL,
165                               LTR390_SENSOR_ENABLE | LTR390_UVS_MODE);
166         if (ret)
167                 return dev_err_probe(dev, ret, "failed to enable the sensor\n");
168
169         return devm_iio_device_register(dev, indio_dev);
170 }
171
172 static const struct i2c_device_id ltr390_id[] = {
173         { "ltr390" },
174         { /* Sentinel */ }
175 };
176 MODULE_DEVICE_TABLE(i2c, ltr390_id);
177
178 static const struct of_device_id ltr390_of_table[] = {
179         { .compatible = "liteon,ltr390" },
180         { /* Sentinel */ }
181 };
182 MODULE_DEVICE_TABLE(of, ltr390_of_table);
183
184 static struct i2c_driver ltr390_driver = {
185         .driver = {
186                 .name = "ltr390",
187                 .of_match_table = ltr390_of_table,
188         },
189         .probe = ltr390_probe,
190         .id_table = ltr390_id,
191 };
192 module_i2c_driver(ltr390_driver);
193
194 MODULE_AUTHOR("Anshul Dalal <anshulusr@gmail.com>");
195 MODULE_DESCRIPTION("Lite-On LTR390 ALS and UV sensor Driver");
196 MODULE_LICENSE("GPL");