File Index
Files and directories
Sign in
FILE

veml6075.c

File preview with syntax highlighting.

linux-6.19.14/drivers/iio/light/veml6075.c C
  1// SPDX-License-Identifier: GPL-2.0-only
  2/*
  3 * Vishay VEML6075 UVA and UVB light sensor
  4 *
  5 * Copyright 2023 Javier Carrasco <javier.carrasco.cruz@gmail.com>
  6 *
  7 * 7-bit I2C slave, address 0x10
  8 */
  9
 10#include <linux/bitfield.h>
 11#include <linux/delay.h>
 12#include <linux/err.h>
 13#include <linux/i2c.h>
 14#include <linux/module.h>
 15#include <linux/mutex.h>
 16#include <linux/regmap.h>
 17#include <linux/units.h>
 18
 19#include <linux/iio/iio.h>
 20
 21#define VEML6075_CMD_CONF	0x00 /* configuration register */
 22#define VEML6075_CMD_UVA	0x07 /* UVA channel */
 23#define VEML6075_CMD_UVB	0x09 /* UVB channel */
 24#define VEML6075_CMD_COMP1	0x0A /* visible light compensation */
 25#define VEML6075_CMD_COMP2	0x0B /* infrarred light compensation */
 26#define VEML6075_CMD_ID		0x0C /* device ID */
 27
 28#define VEML6075_CONF_IT	GENMASK(6, 4) /* intregration time */
 29#define VEML6075_CONF_HD	BIT(3) /* dynamic setting */
 30#define VEML6075_CONF_TRIG	BIT(2) /* trigger */
 31#define VEML6075_CONF_AF	BIT(1) /* active force enable */
 32#define VEML6075_CONF_SD	BIT(0) /* shutdown */
 33
 34#define VEML6075_IT_50_MS	0x00
 35#define VEML6075_IT_100_MS	0x01
 36#define VEML6075_IT_200_MS	0x02
 37#define VEML6075_IT_400_MS	0x03
 38#define VEML6075_IT_800_MS	0x04
 39
 40#define VEML6075_AF_DISABLE	0x00
 41#define VEML6075_AF_ENABLE	0x01
 42
 43#define VEML6075_SD_DISABLE	0x00
 44#define VEML6075_SD_ENABLE	0x01
 45
 46/* Open-air coefficients and responsivity */
 47#define VEML6075_A_COEF		2220
 48#define VEML6075_B_COEF		1330
 49#define VEML6075_C_COEF		2950
 50#define VEML6075_D_COEF		1740
 51#define VEML6075_UVA_RESP	1461
 52#define VEML6075_UVB_RESP	2591
 53
 54static const int veml6075_it_ms[] = { 50, 100, 200, 400, 800 };
 55
 56struct veml6075_data {
 57	struct i2c_client *client;
 58	struct regmap *regmap;
 59	/*
 60	 * prevent integration time modification and triggering
 61	 * measurements while a measurement is underway.
 62	 */
 63	struct mutex lock;
 64};
 65
 66/* channel number */
 67enum veml6075_chan {
 68	CH_UVA,
 69	CH_UVB,
 70};
 71
 72static const struct iio_chan_spec veml6075_channels[] = {
 73	{
 74		.type = IIO_INTENSITY,
 75		.channel = CH_UVA,
 76		.modified = 1,
 77		.channel2 = IIO_MOD_LIGHT_UVA,
 78		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
 79			BIT(IIO_CHAN_INFO_SCALE),
 80		.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME),
 81		.info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_INT_TIME),
 82	},
 83	{
 84		.type = IIO_INTENSITY,
 85		.channel = CH_UVB,
 86		.modified = 1,
 87		.channel2 = IIO_MOD_LIGHT_UVB,
 88		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
 89			BIT(IIO_CHAN_INFO_SCALE),
 90		.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME),
 91		.info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_INT_TIME),
 92	},
 93	{
 94		.type = IIO_UVINDEX,
 95		.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
 96		.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME),
 97		.info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_INT_TIME),
 98	},
 99};
100
101static int veml6075_request_measurement(struct veml6075_data *data)
102{
103	int ret, conf, int_time;
104
105	ret = regmap_read(data->regmap, VEML6075_CMD_CONF, &conf);
106	if (ret < 0)
107		return ret;
108
109	/* disable shutdown and trigger measurement */
110	ret = regmap_write(data->regmap, VEML6075_CMD_CONF,
111			   (conf | VEML6075_CONF_TRIG) & ~VEML6075_CONF_SD);
112	if (ret < 0)
113		return ret;
114
115	/*
116	 * A measurement requires between 1.30 and 1.40 times the integration
117	 * time for all possible configurations. Using a 1.50 factor simplifies
118	 * operations and ensures reliability under all circumstances.
119	 */
120	int_time = veml6075_it_ms[FIELD_GET(VEML6075_CONF_IT, conf)];
121	msleep(int_time + (int_time / 2));
122
123	/* shutdown again, data registers are still accessible */
124	return regmap_update_bits(data->regmap, VEML6075_CMD_CONF,
125				  VEML6075_CONF_SD, VEML6075_CONF_SD);
126}
127
128static int veml6075_uva_comp(int raw_uva, int comp1, int comp2)
129{
130	int comp1a_c, comp2a_c, uva_comp;
131
132	comp1a_c = (comp1 * VEML6075_A_COEF) / 1000U;
133	comp2a_c = (comp2 * VEML6075_B_COEF) / 1000U;
134	uva_comp = raw_uva - comp1a_c - comp2a_c;
135
136	return clamp_val(uva_comp, 0, U16_MAX);
137}
138
139static int veml6075_uvb_comp(int raw_uvb, int comp1, int comp2)
140{
141	int comp1b_c, comp2b_c, uvb_comp;
142
143	comp1b_c = (comp1 * VEML6075_C_COEF) / 1000U;
144	comp2b_c = (comp2 * VEML6075_D_COEF) / 1000U;
145	uvb_comp = raw_uvb - comp1b_c - comp2b_c;
146
147	return clamp_val(uvb_comp, 0, U16_MAX);
148}
149
150static int veml6075_read_comp(struct veml6075_data *data, int *c1, int *c2)
151{
152	int ret;
153
154	ret = regmap_read(data->regmap, VEML6075_CMD_COMP1, c1);
155	if (ret < 0)
156		return ret;
157
158	return regmap_read(data->regmap, VEML6075_CMD_COMP2, c2);
159}
160
161static int veml6075_read_uv_direct(struct veml6075_data *data, int chan,
162				   int *val)
163{
164	int c1, c2, ret;
165
166	guard(mutex)(&data->lock);
167
168	ret = veml6075_request_measurement(data);
169	if (ret < 0)
170		return ret;
171
172	ret = veml6075_read_comp(data, &c1, &c2);
173	if (ret < 0)
174		return ret;
175
176	switch (chan) {
177	case CH_UVA:
178		ret = regmap_read(data->regmap, VEML6075_CMD_UVA, val);
179		if (ret < 0)
180			return ret;
181
182		*val = veml6075_uva_comp(*val, c1, c2);
183		return IIO_VAL_INT;
184	case CH_UVB:
185		ret = regmap_read(data->regmap, VEML6075_CMD_UVB, val);
186		if (ret < 0)
187			return ret;
188
189		*val = veml6075_uvb_comp(*val, c1, c2);
190		return IIO_VAL_INT;
191	default:
192		return -EINVAL;
193	}
194}
195
196static int veml6075_read_int_time_index(struct veml6075_data *data)
197{
198	int ret, conf, int_index;
199
200	ret = regmap_read(data->regmap, VEML6075_CMD_CONF, &conf);
201	if (ret < 0)
202		return ret;
203
204	int_index = FIELD_GET(VEML6075_CONF_IT, conf);
205	if (int_index >= ARRAY_SIZE(veml6075_it_ms))
206		return -EINVAL;
207
208	return int_index;
209}
210
211static int veml6075_read_int_time_ms(struct veml6075_data *data, int *val)
212{
213	int int_index;
214
215	guard(mutex)(&data->lock);
216	int_index = veml6075_read_int_time_index(data);
217	if (int_index < 0)
218		return int_index;
219
220	*val = veml6075_it_ms[int_index];
221
222	return IIO_VAL_INT;
223}
224
225static int veml6075_get_uvi_micro(struct veml6075_data *data, int uva_comp,
226				  int uvb_comp)
227{
228	int uvia_micro = uva_comp * VEML6075_UVA_RESP;
229	int uvib_micro = uvb_comp * VEML6075_UVB_RESP;
230	int int_index;
231
232	int_index = veml6075_read_int_time_index(data);
233	if (int_index < 0)
234		return int_index;
235
236	switch (int_index) {
237	case VEML6075_IT_50_MS:
238		return uvia_micro + uvib_micro;
239	case VEML6075_IT_100_MS:
240	case VEML6075_IT_200_MS:
241	case VEML6075_IT_400_MS:
242	case VEML6075_IT_800_MS:
243		return (uvia_micro + uvib_micro) / (2 << int_index);
244	default:
245		return -EINVAL;
246	}
247}
248
249static int veml6075_read_uvi(struct veml6075_data *data, int *val, int *val2)
250{
251	int ret, c1, c2, uva, uvb, uvi_micro;
252
253	guard(mutex)(&data->lock);
254
255	ret = veml6075_request_measurement(data);
256	if (ret < 0)
257		return ret;
258
259	ret = veml6075_read_comp(data, &c1, &c2);
260	if (ret < 0)
261		return ret;
262
263	ret = regmap_read(data->regmap, VEML6075_CMD_UVA, &uva);
264	if (ret < 0)
265		return ret;
266
267	ret = regmap_read(data->regmap, VEML6075_CMD_UVB, &uvb);
268	if (ret < 0)
269		return ret;
270
271	uvi_micro = veml6075_get_uvi_micro(data, veml6075_uva_comp(uva, c1, c2),
272					   veml6075_uvb_comp(uvb, c1, c2));
273	if (uvi_micro < 0)
274		return uvi_micro;
275
276	*val = uvi_micro / MICRO;
277	*val2 = uvi_micro % MICRO;
278
279	return IIO_VAL_INT_PLUS_MICRO;
280}
281
282static int veml6075_read_responsivity(int chan, int *val, int *val2)
283{
284	/* scale = 1 / resp */
285	switch (chan) {
286	case CH_UVA:
287		/* resp = 0.93 c/uW/cm2: scale = 1.75268817 */
288		*val = 1;
289		*val2 = 75268817;
290		return IIO_VAL_INT_PLUS_NANO;
291	case CH_UVB:
292		/* resp = 2.1 c/uW/cm2: scale = 0.476190476 */
293		*val = 0;
294		*val2 = 476190476;
295		return IIO_VAL_INT_PLUS_NANO;
296	default:
297		return -EINVAL;
298	}
299}
300
301static int veml6075_read_avail(struct iio_dev *indio_dev,
302			       struct iio_chan_spec const *chan,
303			       const int **vals, int *type, int *length,
304			       long mask)
305{
306	switch (mask) {
307	case IIO_CHAN_INFO_INT_TIME:
308		*length = ARRAY_SIZE(veml6075_it_ms);
309		*vals = veml6075_it_ms;
310		*type = IIO_VAL_INT;
311		return IIO_AVAIL_LIST;
312
313	default:
314		return -EINVAL;
315	}
316}
317
318static int veml6075_read_raw(struct iio_dev *indio_dev,
319			     struct iio_chan_spec const *chan,
320			     int *val, int *val2, long mask)
321{
322	struct veml6075_data *data = iio_priv(indio_dev);
323
324	switch (mask) {
325	case IIO_CHAN_INFO_RAW:
326		return veml6075_read_uv_direct(data, chan->channel, val);
327	case IIO_CHAN_INFO_PROCESSED:
328		return veml6075_read_uvi(data, val, val2);
329	case IIO_CHAN_INFO_INT_TIME:
330		return veml6075_read_int_time_ms(data, val);
331	case IIO_CHAN_INFO_SCALE:
332		return veml6075_read_responsivity(chan->channel, val, val2);
333	default:
334		return -EINVAL;
335	}
336}
337
338static int veml6075_write_int_time_ms(struct veml6075_data *data, int val)
339{
340	int i = ARRAY_SIZE(veml6075_it_ms);
341
342	guard(mutex)(&data->lock);
343
344	while (i-- > 0) {
345		if (val == veml6075_it_ms[i])
346			break;
347	}
348	if (i < 0)
349		return -EINVAL;
350
351	return regmap_update_bits(data->regmap, VEML6075_CMD_CONF,
352				  VEML6075_CONF_IT,
353				  FIELD_PREP(VEML6075_CONF_IT, i));
354}
355
356static int veml6075_write_raw(struct iio_dev *indio_dev,
357			      struct iio_chan_spec const *chan,
358			      int val, int val2, long mask)
359{
360	struct veml6075_data *data = iio_priv(indio_dev);
361
362	switch (mask) {
363	case IIO_CHAN_INFO_INT_TIME:
364		return veml6075_write_int_time_ms(data, val);
365	default:
366		return -EINVAL;
367	}
368}
369
370static const struct iio_info veml6075_info = {
371	.read_avail = veml6075_read_avail,
372	.read_raw = veml6075_read_raw,
373	.write_raw = veml6075_write_raw,
374};
375
376static bool veml6075_readable_reg(struct device *dev, unsigned int reg)
377{
378	switch (reg) {
379	case VEML6075_CMD_CONF:
380	case VEML6075_CMD_UVA:
381	case VEML6075_CMD_UVB:
382	case VEML6075_CMD_COMP1:
383	case VEML6075_CMD_COMP2:
384	case VEML6075_CMD_ID:
385		return true;
386	default:
387		return false;
388	}
389}
390
391static bool veml6075_writable_reg(struct device *dev, unsigned int reg)
392{
393	switch (reg) {
394	case VEML6075_CMD_CONF:
395		return true;
396	default:
397		return false;
398	}
399}
400
401static const struct regmap_config veml6075_regmap_config = {
402	.name = "veml6075",
403	.reg_bits = 8,
404	.val_bits = 16,
405	.max_register = VEML6075_CMD_ID,
406	.readable_reg = veml6075_readable_reg,
407	.writeable_reg = veml6075_writable_reg,
408	.val_format_endian = REGMAP_ENDIAN_LITTLE,
409};
410
411static int veml6075_probe(struct i2c_client *client)
412{
413	struct veml6075_data *data;
414	struct iio_dev *indio_dev;
415	struct regmap *regmap;
416	int config, ret;
417
418	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
419	if (!indio_dev)
420		return -ENOMEM;
421
422	regmap = devm_regmap_init_i2c(client, &veml6075_regmap_config);
423	if (IS_ERR(regmap))
424		return PTR_ERR(regmap);
425
426	data = iio_priv(indio_dev);
427	data->client = client;
428	data->regmap = regmap;
429
430	mutex_init(&data->lock);
431
432	indio_dev->name = "veml6075";
433	indio_dev->info = &veml6075_info;
434	indio_dev->channels = veml6075_channels;
435	indio_dev->num_channels = ARRAY_SIZE(veml6075_channels);
436	indio_dev->modes = INDIO_DIRECT_MODE;
437
438	ret = devm_regulator_get_enable(&client->dev, "vdd");
439	if (ret < 0)
440		return ret;
441
442	/* default: 100ms integration time, active force enable, shutdown */
443	config = FIELD_PREP(VEML6075_CONF_IT, VEML6075_IT_100_MS) |
444		FIELD_PREP(VEML6075_CONF_AF, VEML6075_AF_ENABLE) |
445		FIELD_PREP(VEML6075_CONF_SD, VEML6075_SD_ENABLE);
446	ret = regmap_write(data->regmap, VEML6075_CMD_CONF, config);
447	if (ret < 0)
448		return ret;
449
450	return devm_iio_device_register(&client->dev, indio_dev);
451}
452
453static const struct i2c_device_id veml6075_id[] = {
454	{ "veml6075" },
455	{ }
456};
457MODULE_DEVICE_TABLE(i2c, veml6075_id);
458
459static const struct of_device_id veml6075_of_match[] = {
460	{ .compatible = "vishay,veml6075" },
461	{ }
462};
463MODULE_DEVICE_TABLE(of, veml6075_of_match);
464
465static struct i2c_driver veml6075_driver = {
466	.driver = {
467		.name   = "veml6075",
468		.of_match_table = veml6075_of_match,
469	},
470	.probe = veml6075_probe,
471	.id_table = veml6075_id,
472};
473
474module_i2c_driver(veml6075_driver);
475
476MODULE_AUTHOR("Javier Carrasco <javier.carrasco.cruz@gmail.com>");
477MODULE_DESCRIPTION("Vishay VEML6075 UVA and UVB light sensor driver");
478MODULE_LICENSE("GPL");
~/ Read-only file preview.