Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  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;
199
200	ret = regmap_read(data->regmap, VEML6075_CMD_CONF, &conf);
201	if (ret < 0)
202		return ret;
203
204	return FIELD_GET(VEML6075_CONF_IT, conf);
205}
206
207static int veml6075_read_int_time_ms(struct veml6075_data *data, int *val)
208{
209	int int_index;
210
211	guard(mutex)(&data->lock);
212	int_index = veml6075_read_int_time_index(data);
213	if (int_index < 0)
214		return int_index;
215
216	*val = veml6075_it_ms[int_index];
217
218	return IIO_VAL_INT;
219}
220
221static int veml6075_get_uvi_micro(struct veml6075_data *data, int uva_comp,
222				  int uvb_comp)
223{
224	int uvia_micro = uva_comp * VEML6075_UVA_RESP;
225	int uvib_micro = uvb_comp * VEML6075_UVB_RESP;
226	int int_index;
227
228	int_index = veml6075_read_int_time_index(data);
229	if (int_index < 0)
230		return int_index;
231
232	switch (int_index) {
233	case VEML6075_IT_50_MS:
234		return uvia_micro + uvib_micro;
235	case VEML6075_IT_100_MS:
236	case VEML6075_IT_200_MS:
237	case VEML6075_IT_400_MS:
238	case VEML6075_IT_800_MS:
239		return (uvia_micro + uvib_micro) / (2 << int_index);
240	default:
241		return -EINVAL;
242	}
243}
244
245static int veml6075_read_uvi(struct veml6075_data *data, int *val, int *val2)
246{
247	int ret, c1, c2, uva, uvb, uvi_micro;
248
249	guard(mutex)(&data->lock);
250
251	ret = veml6075_request_measurement(data);
252	if (ret < 0)
253		return ret;
254
255	ret = veml6075_read_comp(data, &c1, &c2);
256	if (ret < 0)
257		return ret;
258
259	ret = regmap_read(data->regmap, VEML6075_CMD_UVA, &uva);
260	if (ret < 0)
261		return ret;
262
263	ret = regmap_read(data->regmap, VEML6075_CMD_UVB, &uvb);
264	if (ret < 0)
265		return ret;
266
267	uvi_micro = veml6075_get_uvi_micro(data, veml6075_uva_comp(uva, c1, c2),
268					   veml6075_uvb_comp(uvb, c1, c2));
269	if (uvi_micro < 0)
270		return uvi_micro;
271
272	*val = uvi_micro / MICRO;
273	*val2 = uvi_micro % MICRO;
274
275	return IIO_VAL_INT_PLUS_MICRO;
276}
277
278static int veml6075_read_responsivity(int chan, int *val, int *val2)
279{
280	/* scale = 1 / resp */
281	switch (chan) {
282	case CH_UVA:
283		/* resp = 0.93 c/uW/cm2: scale = 1.75268817 */
284		*val = 1;
285		*val2 = 75268817;
286		return IIO_VAL_INT_PLUS_NANO;
287	case CH_UVB:
288		/* resp = 2.1 c/uW/cm2: scale = 0.476190476 */
289		*val = 0;
290		*val2 = 476190476;
291		return IIO_VAL_INT_PLUS_NANO;
292	default:
293		return -EINVAL;
294	}
295}
296
297static int veml6075_read_avail(struct iio_dev *indio_dev,
298			       struct iio_chan_spec const *chan,
299			       const int **vals, int *type, int *length,
300			       long mask)
301{
302	switch (mask) {
303	case IIO_CHAN_INFO_INT_TIME:
304		*length = ARRAY_SIZE(veml6075_it_ms);
305		*vals = veml6075_it_ms;
306		*type = IIO_VAL_INT;
307		return IIO_AVAIL_LIST;
308
309	default:
310		return -EINVAL;
311	}
312}
313
314static int veml6075_read_raw(struct iio_dev *indio_dev,
315			     struct iio_chan_spec const *chan,
316			     int *val, int *val2, long mask)
317{
318	struct veml6075_data *data = iio_priv(indio_dev);
319
320	switch (mask) {
321	case IIO_CHAN_INFO_RAW:
322		return veml6075_read_uv_direct(data, chan->channel, val);
323	case IIO_CHAN_INFO_PROCESSED:
324		return veml6075_read_uvi(data, val, val2);
325	case IIO_CHAN_INFO_INT_TIME:
326		return veml6075_read_int_time_ms(data, val);
327	case IIO_CHAN_INFO_SCALE:
328		return veml6075_read_responsivity(chan->channel, val, val2);
329	default:
330		return -EINVAL;
331	}
332}
333
334static int veml6075_write_int_time_ms(struct veml6075_data *data, int val)
335{
336	int i = ARRAY_SIZE(veml6075_it_ms);
337
338	guard(mutex)(&data->lock);
339
340	while (i-- > 0) {
341		if (val == veml6075_it_ms[i])
342			break;
343	}
344	if (i < 0)
345		return -EINVAL;
346
347	return regmap_update_bits(data->regmap, VEML6075_CMD_CONF,
348				  VEML6075_CONF_IT,
349				  FIELD_PREP(VEML6075_CONF_IT, i));
350}
351
352static int veml6075_write_raw(struct iio_dev *indio_dev,
353			      struct iio_chan_spec const *chan,
354			      int val, int val2, long mask)
355{
356	struct veml6075_data *data = iio_priv(indio_dev);
357
358	switch (mask) {
359	case IIO_CHAN_INFO_INT_TIME:
360		return veml6075_write_int_time_ms(data, val);
361	default:
362		return -EINVAL;
363	}
364}
365
366static const struct iio_info veml6075_info = {
367	.read_avail = veml6075_read_avail,
368	.read_raw = veml6075_read_raw,
369	.write_raw = veml6075_write_raw,
370};
371
372static bool veml6075_readable_reg(struct device *dev, unsigned int reg)
373{
374	switch (reg) {
375	case VEML6075_CMD_CONF:
376	case VEML6075_CMD_UVA:
377	case VEML6075_CMD_UVB:
378	case VEML6075_CMD_COMP1:
379	case VEML6075_CMD_COMP2:
380	case VEML6075_CMD_ID:
381		return true;
382	default:
383		return false;
384	}
385}
386
387static bool veml6075_writable_reg(struct device *dev, unsigned int reg)
388{
389	switch (reg) {
390	case VEML6075_CMD_CONF:
391		return true;
392	default:
393		return false;
394	}
395}
396
397static const struct regmap_config veml6075_regmap_config = {
398	.name = "veml6075",
399	.reg_bits = 8,
400	.val_bits = 16,
401	.max_register = VEML6075_CMD_ID,
402	.readable_reg = veml6075_readable_reg,
403	.writeable_reg = veml6075_writable_reg,
404	.val_format_endian = REGMAP_ENDIAN_LITTLE,
405};
406
407static int veml6075_probe(struct i2c_client *client)
408{
409	struct veml6075_data *data;
410	struct iio_dev *indio_dev;
411	struct regmap *regmap;
412	int config, ret;
413
414	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
415	if (!indio_dev)
416		return -ENOMEM;
417
418	regmap = devm_regmap_init_i2c(client, &veml6075_regmap_config);
419	if (IS_ERR(regmap))
420		return PTR_ERR(regmap);
421
422	data = iio_priv(indio_dev);
423	data->client = client;
424	data->regmap = regmap;
425
426	mutex_init(&data->lock);
427
428	indio_dev->name = "veml6075";
429	indio_dev->info = &veml6075_info;
430	indio_dev->channels = veml6075_channels;
431	indio_dev->num_channels = ARRAY_SIZE(veml6075_channels);
432	indio_dev->modes = INDIO_DIRECT_MODE;
433
434	ret = devm_regulator_get_enable(&client->dev, "vdd");
435	if (ret < 0)
436		return ret;
437
438	/* default: 100ms integration time, active force enable, shutdown */
439	config = FIELD_PREP(VEML6075_CONF_IT, VEML6075_IT_100_MS) |
440		FIELD_PREP(VEML6075_CONF_AF, VEML6075_AF_ENABLE) |
441		FIELD_PREP(VEML6075_CONF_SD, VEML6075_SD_ENABLE);
442	ret = regmap_write(data->regmap, VEML6075_CMD_CONF, config);
443	if (ret < 0)
444		return ret;
445
446	return devm_iio_device_register(&client->dev, indio_dev);
447}
448
449static const struct i2c_device_id veml6075_id[] = {
450	{ "veml6075" },
451	{ }
452};
453MODULE_DEVICE_TABLE(i2c, veml6075_id);
454
455static const struct of_device_id veml6075_of_match[] = {
456	{ .compatible = "vishay,veml6075" },
457	{}
458};
459MODULE_DEVICE_TABLE(of, veml6075_of_match);
460
461static struct i2c_driver veml6075_driver = {
462	.driver = {
463		.name   = "veml6075",
464		.of_match_table = veml6075_of_match,
465	},
466	.probe = veml6075_probe,
467	.id_table = veml6075_id,
468};
469
470module_i2c_driver(veml6075_driver);
471
472MODULE_AUTHOR("Javier Carrasco <javier.carrasco.cruz@gmail.com>");
473MODULE_DESCRIPTION("Vishay VEML6075 UVA and UVB light sensor driver");
474MODULE_LICENSE("GPL");