xref: /linux/drivers/iio/light/veml6040.c (revision 6efc0ab3b05de0d7bab8ec0597214e4788251071)
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Vishay VEML6040 RGBW light sensor driver
4  *
5  * Copyright (C) 2024 Sentec AG
6  * Author: Arthur Becker <arthur.becker@sentec.com>
7  *
8  */
9 
10 #include <linux/bitfield.h>
11 #include <linux/err.h>
12 #include <linux/i2c.h>
13 #include <linux/iio/iio.h>
14 #include <linux/iio/sysfs.h>
15 #include <linux/module.h>
16 #include <linux/regmap.h>
17 
18 /* VEML6040 Configuration Registers
19  *
20  * SD: Shutdown
21  * AF: Auto / Force Mode (Auto Measurements On:0, Off:1)
22  * TR: Trigger Measurement (when AF Bit is set)
23  * IT: Integration Time
24  */
25 #define VEML6040_CONF_REG 0x000
26 #define VEML6040_CONF_SD_MSK BIT(0)
27 #define VEML6040_CONF_AF_MSK BIT(1)
28 #define VEML6040_CONF_TR_MSK BIT(2)
29 #define VEML6040_CONF_IT_MSK GENMASK(6, 4)
30 #define VEML6040_CONF_IT_40_MS 0
31 #define VEML6040_CONF_IT_80_MS 1
32 #define VEML6040_CONF_IT_160_MS 2
33 #define VEML6040_CONF_IT_320_MS 3
34 #define VEML6040_CONF_IT_640_MS 4
35 #define VEML6040_CONF_IT_1280_MS 5
36 
37 /* VEML6040 Read Only Registers */
38 #define VEML6040_REG_R 0x08
39 #define VEML6040_REG_G 0x09
40 #define VEML6040_REG_B 0x0A
41 #define VEML6040_REG_W 0x0B
42 
43 static const int veml6040_it_ms[] = { 40, 80, 160, 320, 640, 1280 };
44 
45 enum veml6040_chan {
46 	CH_RED,
47 	CH_GREEN,
48 	CH_BLUE,
49 	CH_WHITE,
50 };
51 
52 struct veml6040_data {
53 	struct i2c_client *client;
54 	struct regmap *regmap;
55 };
56 
57 static const struct regmap_config veml6040_regmap_config = {
58 	.name = "veml6040_regmap",
59 	.reg_bits = 8,
60 	.val_bits = 16,
61 	.max_register = VEML6040_REG_W,
62 	.val_format_endian = REGMAP_ENDIAN_LITTLE,
63 };
64 
65 static int veml6040_read_raw(struct iio_dev *indio_dev,
66 			     struct iio_chan_spec const *chan, int *val,
67 			     int *val2, long mask)
68 {
69 	int ret, reg, it_index;
70 	struct veml6040_data *data = iio_priv(indio_dev);
71 	struct regmap *regmap = data->regmap;
72 	struct device *dev = &data->client->dev;
73 
74 	switch (mask) {
75 	case IIO_CHAN_INFO_RAW:
76 		ret = regmap_read(regmap, chan->address, &reg);
77 		if (ret) {
78 			dev_err(dev, "Data read failed: %d\n", ret);
79 			return ret;
80 		}
81 		*val = reg;
82 		return IIO_VAL_INT;
83 
84 	case IIO_CHAN_INFO_INT_TIME:
85 		ret = regmap_read(regmap, VEML6040_CONF_REG, &reg);
86 		if (ret) {
87 			dev_err(dev, "Data read failed: %d\n", ret);
88 			return ret;
89 		}
90 		it_index = FIELD_GET(VEML6040_CONF_IT_MSK, reg);
91 		if (it_index >= ARRAY_SIZE(veml6040_it_ms)) {
92 			dev_err(dev, "Invalid Integration Time Set");
93 			return -EINVAL;
94 		}
95 		*val = veml6040_it_ms[it_index];
96 		return IIO_VAL_INT;
97 
98 	default:
99 		return -EINVAL;
100 	}
101 }
102 
103 static int veml6040_write_raw(struct iio_dev *indio_dev,
104 			      struct iio_chan_spec const *chan, int val,
105 			      int val2, long mask)
106 {
107 	struct veml6040_data *data = iio_priv(indio_dev);
108 
109 	switch (mask) {
110 	case IIO_CHAN_INFO_INT_TIME:
111 		for (int i = 0; i < ARRAY_SIZE(veml6040_it_ms); i++) {
112 			if (veml6040_it_ms[i] != val)
113 				continue;
114 
115 			return regmap_update_bits(data->regmap,
116 					VEML6040_CONF_REG,
117 					VEML6040_CONF_IT_MSK,
118 					FIELD_PREP(VEML6040_CONF_IT_MSK, i));
119 		}
120 		return -EINVAL;
121 	default:
122 		return -EINVAL;
123 	}
124 }
125 
126 static int veml6040_read_avail(struct iio_dev *indio_dev,
127 			       struct iio_chan_spec const *chan,
128 			       const int **vals, int *type, int *length,
129 			       long mask)
130 {
131 	switch (mask) {
132 	case IIO_CHAN_INFO_INT_TIME:
133 		*length = ARRAY_SIZE(veml6040_it_ms);
134 		*vals = veml6040_it_ms;
135 		*type = IIO_VAL_INT;
136 		return IIO_AVAIL_LIST;
137 
138 	default:
139 		return -EINVAL;
140 	}
141 }
142 
143 static const struct iio_info veml6040_info = {
144 	.read_raw = veml6040_read_raw,
145 	.write_raw = veml6040_write_raw,
146 	.read_avail = veml6040_read_avail,
147 };
148 
149 static const struct iio_chan_spec veml6040_channels[] = {
150 	{
151 		.type = IIO_INTENSITY,
152 		.address = VEML6040_REG_R,
153 		.channel = CH_RED,
154 		.channel2 = IIO_MOD_LIGHT_RED,
155 		.modified = 1,
156 		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
157 		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_INT_TIME),
158 		.info_mask_shared_by_type_available =
159 			BIT(IIO_CHAN_INFO_INT_TIME),
160 	},
161 	{
162 		.type = IIO_INTENSITY,
163 		.address = VEML6040_REG_G,
164 		.channel = CH_GREEN,
165 		.channel2 = IIO_MOD_LIGHT_GREEN,
166 		.modified = 1,
167 		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
168 		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_INT_TIME),
169 		.info_mask_shared_by_type_available =
170 			BIT(IIO_CHAN_INFO_INT_TIME),
171 	},
172 	{
173 		.type = IIO_INTENSITY,
174 		.address = VEML6040_REG_B,
175 		.channel = CH_BLUE,
176 		.channel2 = IIO_MOD_LIGHT_BLUE,
177 		.modified = 1,
178 		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
179 		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_INT_TIME),
180 		.info_mask_shared_by_type_available =
181 			BIT(IIO_CHAN_INFO_INT_TIME),
182 	},
183 	{
184 		.type = IIO_INTENSITY,
185 		.address = VEML6040_REG_W,
186 		.channel = CH_WHITE,
187 		.channel2 = IIO_MOD_LIGHT_CLEAR,
188 		.modified = 1,
189 		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
190 		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_INT_TIME),
191 		.info_mask_shared_by_type_available =
192 			BIT(IIO_CHAN_INFO_INT_TIME),
193 	}
194 };
195 
196 static void veml6040_shutdown_action(void *data)
197 {
198 	struct veml6040_data *veml6040_data = data;
199 
200 	regmap_update_bits(veml6040_data->regmap, VEML6040_CONF_REG,
201 			   VEML6040_CONF_SD_MSK, VEML6040_CONF_SD_MSK);
202 }
203 
204 static int veml6040_probe(struct i2c_client *client)
205 {
206 	struct device *dev = &client->dev;
207 	struct veml6040_data *data;
208 	struct iio_dev *indio_dev;
209 	struct regmap *regmap;
210 	const int init_config =
211 		FIELD_PREP(VEML6040_CONF_IT_MSK, VEML6040_CONF_IT_40_MS) |
212 		FIELD_PREP(VEML6040_CONF_AF_MSK, 0) |
213 		FIELD_PREP(VEML6040_CONF_SD_MSK, 0);
214 	int ret;
215 
216 	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
217 		return dev_err_probe(dev, -EOPNOTSUPP,
218 				     "I2C adapter doesn't support plain I2C\n");
219 
220 	indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
221 	if (!indio_dev)
222 		return dev_err_probe(dev, -ENOMEM,
223 				     "IIO device allocation failed\n");
224 
225 	regmap = devm_regmap_init_i2c(client, &veml6040_regmap_config);
226 	if (IS_ERR(regmap))
227 		return dev_err_probe(dev, PTR_ERR(regmap),
228 				     "Regmap setup failed\n");
229 
230 	data = iio_priv(indio_dev);
231 	i2c_set_clientdata(client, indio_dev);
232 	data->client = client;
233 	data->regmap = regmap;
234 
235 	indio_dev->name = "veml6040";
236 	indio_dev->info = &veml6040_info;
237 	indio_dev->channels = veml6040_channels;
238 	indio_dev->num_channels = ARRAY_SIZE(veml6040_channels);
239 	indio_dev->modes = INDIO_DIRECT_MODE;
240 
241 	ret = devm_regulator_get_enable(dev, "vdd");
242 	if (ret)
243 		return ret;
244 
245 	ret = regmap_write(regmap, VEML6040_CONF_REG, init_config);
246 	if (ret)
247 		return dev_err_probe(dev, ret,
248 				     "Could not set initial config\n");
249 
250 	ret = devm_add_action_or_reset(dev, veml6040_shutdown_action, data);
251 	if (ret)
252 		return ret;
253 
254 	return devm_iio_device_register(dev, indio_dev);
255 }
256 
257 static const struct i2c_device_id veml6040_id_table[] = {
258 	{"veml6040"},
259 	{}
260 };
261 MODULE_DEVICE_TABLE(i2c, veml6040_id_table);
262 
263 static const struct of_device_id veml6040_of_match[] = {
264 	{.compatible = "vishay,veml6040"},
265 	{}
266 };
267 MODULE_DEVICE_TABLE(of, veml6040_of_match);
268 
269 static struct i2c_driver veml6040_driver = {
270 	.probe = veml6040_probe,
271 	.id_table = veml6040_id_table,
272 	.driver = {
273 		.name = "veml6040",
274 		.of_match_table = veml6040_of_match,
275 	},
276 };
277 module_i2c_driver(veml6040_driver);
278 
279 MODULE_DESCRIPTION("veml6040 RGBW light sensor driver");
280 MODULE_AUTHOR("Arthur Becker <arthur.becker@sentec.com>");
281 MODULE_LICENSE("GPL");
282