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