1 // SPDX-License-Identifier: GPL-2.0+
3 * Vishay VEML6040 RGBW light sensor driver
5 * Copyright (C) 2024 Sentec AG
6 * Author: Arthur Becker <arthur.becker@sentec.com>
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>
18 /* VEML6040 Configuration Registers
21 * AF: Auto / Force Mode (Auto Measurements On:0, Off:1)
22 * TR: Trigger Measurement (when AF Bit is set)
23 * IT: Integration Time
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
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
43 static const int veml6040_it_ms
[] = { 40, 80, 160, 320, 640, 1280 };
52 struct veml6040_data
{
53 struct i2c_client
*client
;
54 struct regmap
*regmap
;
57 static const struct regmap_config veml6040_regmap_config
= {
58 .name
= "veml6040_regmap",
61 .max_register
= VEML6040_REG_W
,
62 .val_format_endian
= REGMAP_ENDIAN_LITTLE
,
65 static int veml6040_read_raw(struct iio_dev
*indio_dev
,
66 struct iio_chan_spec
const *chan
, int *val
,
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
;
75 case IIO_CHAN_INFO_RAW
:
76 ret
= regmap_read(regmap
, chan
->address
, ®
);
78 dev_err(dev
, "Data read failed: %d\n", ret
);
84 case IIO_CHAN_INFO_INT_TIME
:
85 ret
= regmap_read(regmap
, VEML6040_CONF_REG
, ®
);
87 dev_err(dev
, "Data read failed: %d\n", ret
);
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");
95 *val
= veml6040_it_ms
[it_index
];
103 static int veml6040_write_raw(struct iio_dev
*indio_dev
,
104 struct iio_chan_spec
const *chan
, int val
,
107 struct veml6040_data
*data
= iio_priv(indio_dev
);
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
)
115 return regmap_update_bits(data
->regmap
,
117 VEML6040_CONF_IT_MSK
,
118 FIELD_PREP(VEML6040_CONF_IT_MSK
, i
));
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
,
132 case IIO_CHAN_INFO_INT_TIME
:
133 *length
= ARRAY_SIZE(veml6040_it_ms
);
134 *vals
= veml6040_it_ms
;
136 return IIO_AVAIL_LIST
;
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
,
149 static const struct iio_chan_spec veml6040_channels
[] = {
151 .type
= IIO_INTENSITY
,
152 .address
= VEML6040_REG_R
,
154 .channel2
= IIO_MOD_LIGHT_RED
,
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
),
162 .type
= IIO_INTENSITY
,
163 .address
= VEML6040_REG_G
,
165 .channel2
= IIO_MOD_LIGHT_GREEN
,
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
),
173 .type
= IIO_INTENSITY
,
174 .address
= VEML6040_REG_B
,
176 .channel2
= IIO_MOD_LIGHT_BLUE
,
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
),
184 .type
= IIO_INTENSITY
,
185 .address
= VEML6040_REG_W
,
187 .channel2
= IIO_MOD_LIGHT_CLEAR
,
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
),
196 static void veml6040_shutdown_action(void *data
)
198 struct veml6040_data
*veml6040_data
= data
;
200 regmap_update_bits(veml6040_data
->regmap
, VEML6040_CONF_REG
,
201 VEML6040_CONF_SD_MSK
, VEML6040_CONF_SD_MSK
);
204 static int veml6040_probe(struct i2c_client
*client
)
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);
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");
220 indio_dev
= devm_iio_device_alloc(dev
, sizeof(*data
));
222 return dev_err_probe(dev
, -ENOMEM
,
223 "IIO device allocation failed\n");
225 regmap
= devm_regmap_init_i2c(client
, &veml6040_regmap_config
);
227 return dev_err_probe(dev
, PTR_ERR(regmap
),
228 "Regmap setup failed\n");
230 data
= iio_priv(indio_dev
);
231 i2c_set_clientdata(client
, indio_dev
);
232 data
->client
= client
;
233 data
->regmap
= regmap
;
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
;
241 ret
= devm_regulator_get_enable(dev
, "vdd");
245 ret
= regmap_write(regmap
, VEML6040_CONF_REG
, init_config
);
247 return dev_err_probe(dev
, ret
,
248 "Could not set initial config\n");
250 ret
= devm_add_action_or_reset(dev
, veml6040_shutdown_action
, data
);
254 return devm_iio_device_register(dev
, indio_dev
);
257 static const struct i2c_device_id veml6040_id_table
[] = {
261 MODULE_DEVICE_TABLE(i2c
, veml6040_id_table
);
263 static const struct of_device_id veml6040_of_match
[] = {
264 {.compatible
= "vishay,veml6040"},
267 MODULE_DEVICE_TABLE(of
, veml6040_of_match
);
269 static struct i2c_driver veml6040_driver
= {
270 .probe
= veml6040_probe
,
271 .id_table
= veml6040_id_table
,
274 .of_match_table
= veml6040_of_match
,
277 module_i2c_driver(veml6040_driver
);
279 MODULE_DESCRIPTION("veml6040 RGBW light sensor driver");
280 MODULE_AUTHOR("Arthur Becker <arthur.becker@sentec.com>");
281 MODULE_LICENSE("GPL");