File Index
Files and directories
Sign in
FILE

veml6070.c

File preview with syntax highlighting.

linux-6.19.14/drivers/iio/light/veml6070.c C
  1// SPDX-License-Identifier: GPL-2.0-only
  2/*
  3 * veml6070.c - Support for Vishay VEML6070 UV A light sensor
  4 *
  5 * Copyright 2016 Peter Meerwald-Stadler <pmeerw@pmeerw.net>
  6 *
  7 * IIO driver for VEML6070 (7-bit I2C slave addresses 0x38 and 0x39)
  8 *
  9 * TODO: ACK signal
 10 */
 11
 12#include <linux/bitfield.h>
 13#include <linux/module.h>
 14#include <linux/i2c.h>
 15#include <linux/mutex.h>
 16#include <linux/err.h>
 17#include <linux/delay.h>
 18#include <linux/units.h>
 19
 20#include <linux/iio/iio.h>
 21#include <linux/iio/sysfs.h>
 22
 23#define VEML6070_DRV_NAME "veml6070"
 24
 25#define VEML6070_ADDR_CONFIG_DATA_MSB 0x38 /* read: MSB data, write: config */
 26#define VEML6070_ADDR_DATA_LSB	0x39 /* LSB data */
 27
 28#define VEML6070_COMMAND_ACK	BIT(5) /* raise interrupt when over threshold */
 29#define VEML6070_COMMAND_IT	GENMASK(3, 2) /* bit mask integration time */
 30#define VEML6070_COMMAND_RSRVD	BIT(1) /* reserved, set to 1 */
 31#define VEML6070_COMMAND_SD	BIT(0) /* shutdown mode when set */
 32
 33#define VEML6070_IT_05		0x00
 34#define VEML6070_IT_10		0x01
 35#define VEML6070_IT_20		0x02
 36#define VEML6070_IT_40		0x03
 37
 38#define VEML6070_MIN_RSET_KOHM	75
 39#define VEML6070_MIN_IT_US	15625 /* Rset = 75 kohm, IT = 1/2 */
 40
 41struct veml6070_data {
 42	struct i2c_client *client1;
 43	struct i2c_client *client2;
 44	u8 config;
 45	struct mutex lock;
 46	u32 rset;
 47	int it[4][2];
 48};
 49
 50static int veml6070_calc_it(struct device *dev, struct veml6070_data *data)
 51{
 52	int i, tmp_it;
 53
 54	data->rset = 270000;
 55	device_property_read_u32(dev, "vishay,rset-ohms", &data->rset);
 56
 57	if (data->rset < 75000 || data->rset > 1200000)
 58		return dev_err_probe(dev, -EINVAL, "Rset out of range\n");
 59
 60	/*
 61	 * convert to kohm to avoid overflows and work with the same units as
 62	 * in the datasheet and simplify UVI operations.
 63	 */
 64	data->rset /= KILO;
 65
 66	tmp_it = VEML6070_MIN_IT_US * data->rset / VEML6070_MIN_RSET_KOHM;
 67	for (i = 0; i < ARRAY_SIZE(data->it); i++) {
 68		data->it[i][0] = (tmp_it << i) / MICRO;
 69		data->it[i][1] = (tmp_it << i) % MICRO;
 70	}
 71
 72	return 0;
 73}
 74
 75static int veml6070_get_it(struct veml6070_data *data, int *val, int *val2)
 76{
 77	int it_idx = FIELD_GET(VEML6070_COMMAND_IT, data->config);
 78
 79	*val = data->it[it_idx][0];
 80	*val2 = data->it[it_idx][1];
 81
 82	return IIO_VAL_INT_PLUS_MICRO;
 83}
 84
 85static int veml6070_set_it(struct veml6070_data *data, int val, int val2)
 86{
 87	int it_idx;
 88
 89	for (it_idx = 0; it_idx < ARRAY_SIZE(data->it); it_idx++) {
 90		if (data->it[it_idx][0] == val && data->it[it_idx][1] == val2)
 91			break;
 92	}
 93
 94	if (it_idx >= ARRAY_SIZE(data->it))
 95		return -EINVAL;
 96
 97	data->config = (data->config & ~VEML6070_COMMAND_IT) |
 98		FIELD_PREP(VEML6070_COMMAND_IT, it_idx);
 99
100	return i2c_smbus_write_byte(data->client1, data->config);
101}
102
103static int veml6070_read(struct veml6070_data *data)
104{
105	int ret, it_ms, val, val2;
106	u8 msb, lsb;
107
108	guard(mutex)(&data->lock);
109
110	/* disable shutdown */
111	ret = i2c_smbus_write_byte(data->client1,
112	    data->config & ~VEML6070_COMMAND_SD);
113	if (ret < 0)
114		return ret;
115
116	veml6070_get_it(data, &val, &val2);
117	it_ms = val * MILLI + val2 / (MICRO / MILLI);
118	msleep(it_ms + 10);
119
120	ret = i2c_smbus_read_byte(data->client2); /* read MSB, address 0x39 */
121	if (ret < 0)
122		return ret;
123
124	msb = ret;
125
126	ret = i2c_smbus_read_byte(data->client1); /* read LSB, address 0x38 */
127	if (ret < 0)
128		return ret;
129
130	lsb = ret;
131
132	/* shutdown again */
133	ret = i2c_smbus_write_byte(data->client1, data->config);
134	if (ret < 0)
135		return ret;
136
137	return (msb << 8) | lsb;
138}
139
140static const struct iio_chan_spec veml6070_channels[] = {
141	{
142		.type = IIO_INTENSITY,
143		.modified = 1,
144		.channel2 = IIO_MOD_LIGHT_UV,
145		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
146		.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME),
147		.info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_INT_TIME),
148	},
149	{
150		.type = IIO_UVINDEX,
151		.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
152		.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME),
153		.info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_INT_TIME),
154	}
155};
156
157static int veml6070_to_uv_index(struct veml6070_data *data, unsigned int val)
158{
159	/*
160	 * conversion of raw UV intensity values to UV index depends on
161	 * integration time (IT) and value of the resistor connected to
162	 * the RSET pin.
163	 */
164	unsigned int uvi[11] = {
165		187, 373, 560, /* low */
166		746, 933, 1120, /* moderate */
167		1308, 1494, /* high */
168		1681, 1868, 2054}; /* very high */
169	int i, it_idx;
170
171	it_idx = FIELD_GET(VEML6070_COMMAND_IT, data->config);
172
173	if (!it_idx)
174		val = (val * 270  / data->rset) << 1;
175	else
176		val = (val * 270 / data->rset) >> (it_idx - 1);
177
178	for (i = 0; i < ARRAY_SIZE(uvi); i++)
179		if (val <= uvi[i])
180			return i;
181
182	return 11; /* extreme */
183}
184
185static int veml6070_read_raw(struct iio_dev *indio_dev,
186				struct iio_chan_spec const *chan,
187				int *val, int *val2, long mask)
188{
189	struct veml6070_data *data = iio_priv(indio_dev);
190	int ret;
191
192	switch (mask) {
193	case IIO_CHAN_INFO_RAW:
194	case IIO_CHAN_INFO_PROCESSED:
195		ret = veml6070_read(data);
196		if (ret < 0)
197			return ret;
198		if (mask == IIO_CHAN_INFO_PROCESSED)
199			*val = veml6070_to_uv_index(data, ret);
200		else
201			*val = ret;
202		return IIO_VAL_INT;
203	case IIO_CHAN_INFO_INT_TIME:
204		return veml6070_get_it(data, val, val2);
205	default:
206		return -EINVAL;
207	}
208}
209
210static int veml6070_read_avail(struct iio_dev *indio_dev,
211			       struct iio_chan_spec const *chan,
212			       const int **vals, int *type, int *length,
213			       long mask)
214{
215	struct veml6070_data *data = iio_priv(indio_dev);
216
217	switch (mask) {
218	case IIO_CHAN_INFO_INT_TIME:
219		*vals = (int *)data->it;
220		*length = 2 * ARRAY_SIZE(data->it);
221		*type = IIO_VAL_INT_PLUS_MICRO;
222		return IIO_AVAIL_LIST;
223	default:
224		return -EINVAL;
225	}
226}
227
228static int veml6070_write_raw(struct iio_dev *indio_dev,
229			      struct iio_chan_spec const *chan,
230			      int val, int val2, long mask)
231{
232	struct veml6070_data *data = iio_priv(indio_dev);
233
234	switch (mask) {
235	case IIO_CHAN_INFO_INT_TIME:
236		return veml6070_set_it(data, val, val2);
237	default:
238		return -EINVAL;
239	}
240}
241
242static const struct iio_info veml6070_info = {
243	.read_raw = veml6070_read_raw,
244	.read_avail  = veml6070_read_avail,
245	.write_raw = veml6070_write_raw,
246};
247
248static void veml6070_i2c_unreg(void *p)
249{
250	struct veml6070_data *data = p;
251
252	i2c_unregister_device(data->client2);
253}
254
255static int veml6070_probe(struct i2c_client *client)
256{
257	struct veml6070_data *data;
258	struct iio_dev *indio_dev;
259	int ret;
260
261	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
262	if (!indio_dev)
263		return -ENOMEM;
264
265	data = iio_priv(indio_dev);
266	i2c_set_clientdata(client, indio_dev);
267	data->client1 = client;
268	mutex_init(&data->lock);
269
270	indio_dev->info = &veml6070_info;
271	indio_dev->channels = veml6070_channels;
272	indio_dev->num_channels = ARRAY_SIZE(veml6070_channels);
273	indio_dev->name = VEML6070_DRV_NAME;
274	indio_dev->modes = INDIO_DIRECT_MODE;
275
276	ret = veml6070_calc_it(&client->dev, data);
277	if (ret < 0)
278		return ret;
279
280	ret = devm_regulator_get_enable(&client->dev, "vdd");
281	if (ret < 0)
282		return ret;
283
284	data->client2 = i2c_new_dummy_device(client->adapter, VEML6070_ADDR_DATA_LSB);
285	if (IS_ERR(data->client2))
286		return dev_err_probe(&client->dev, PTR_ERR(data->client2),
287				     "i2c device for second chip address failed\n");
288
289	data->config = FIELD_PREP(VEML6070_COMMAND_IT, VEML6070_IT_10) |
290		VEML6070_COMMAND_RSRVD | VEML6070_COMMAND_SD;
291	ret = i2c_smbus_write_byte(data->client1, data->config);
292	if (ret < 0)
293		return ret;
294
295	ret = devm_add_action_or_reset(&client->dev, veml6070_i2c_unreg, data);
296	if (ret < 0)
297		return ret;
298
299	return devm_iio_device_register(&client->dev, indio_dev);
300}
301
302static const struct i2c_device_id veml6070_id[] = {
303	{ "veml6070" },
304	{ }
305};
306MODULE_DEVICE_TABLE(i2c, veml6070_id);
307
308static const struct of_device_id veml6070_of_match[] = {
309	{ .compatible = "vishay,veml6070" },
310	{ }
311};
312MODULE_DEVICE_TABLE(of, veml6070_of_match);
313
314static struct i2c_driver veml6070_driver = {
315	.driver = {
316		.name   = VEML6070_DRV_NAME,
317		.of_match_table = veml6070_of_match,
318	},
319	.probe = veml6070_probe,
320	.id_table = veml6070_id,
321};
322
323module_i2c_driver(veml6070_driver);
324
325MODULE_AUTHOR("Peter Meerwald-Stadler <pmeerw@pmeerw.net>");
326MODULE_DESCRIPTION("Vishay VEML6070 UV A light sensor driver");
327MODULE_LICENSE("GPL");
~/ Read-only file preview.