xref: /linux/drivers/iio/light/veml6070.c (revision dfd2ab8dda719b98203f5b3e940961019d373541)
1*dfd2ab8dSPeter Meerwald-Stadler /*
2*dfd2ab8dSPeter Meerwald-Stadler  * veml6070.c - Support for Vishay VEML6070 UV A light sensor
3*dfd2ab8dSPeter Meerwald-Stadler  *
4*dfd2ab8dSPeter Meerwald-Stadler  * Copyright 2016 Peter Meerwald-Stadler <pmeerw@pmeerw.net>
5*dfd2ab8dSPeter Meerwald-Stadler  *
6*dfd2ab8dSPeter Meerwald-Stadler  * This file is subject to the terms and conditions of version 2 of
7*dfd2ab8dSPeter Meerwald-Stadler  * the GNU General Public License.  See the file COPYING in the main
8*dfd2ab8dSPeter Meerwald-Stadler  * directory of this archive for more details.
9*dfd2ab8dSPeter Meerwald-Stadler  *
10*dfd2ab8dSPeter Meerwald-Stadler  * IIO driver for VEML6070 (7-bit I2C slave addresses 0x38 and 0x39)
11*dfd2ab8dSPeter Meerwald-Stadler  *
12*dfd2ab8dSPeter Meerwald-Stadler  * TODO: integration time, ACK signal
13*dfd2ab8dSPeter Meerwald-Stadler  */
14*dfd2ab8dSPeter Meerwald-Stadler 
15*dfd2ab8dSPeter Meerwald-Stadler #include <linux/module.h>
16*dfd2ab8dSPeter Meerwald-Stadler #include <linux/i2c.h>
17*dfd2ab8dSPeter Meerwald-Stadler #include <linux/mutex.h>
18*dfd2ab8dSPeter Meerwald-Stadler #include <linux/err.h>
19*dfd2ab8dSPeter Meerwald-Stadler #include <linux/delay.h>
20*dfd2ab8dSPeter Meerwald-Stadler 
21*dfd2ab8dSPeter Meerwald-Stadler #include <linux/iio/iio.h>
22*dfd2ab8dSPeter Meerwald-Stadler #include <linux/iio/sysfs.h>
23*dfd2ab8dSPeter Meerwald-Stadler 
24*dfd2ab8dSPeter Meerwald-Stadler #define VEML6070_DRV_NAME "veml6070"
25*dfd2ab8dSPeter Meerwald-Stadler 
26*dfd2ab8dSPeter Meerwald-Stadler #define VEML6070_ADDR_CONFIG_DATA_MSB 0x38 /* read: MSB data, write: config */
27*dfd2ab8dSPeter Meerwald-Stadler #define VEML6070_ADDR_DATA_LSB	0x39 /* LSB data */
28*dfd2ab8dSPeter Meerwald-Stadler 
29*dfd2ab8dSPeter Meerwald-Stadler #define VEML6070_COMMAND_ACK	BIT(5) /* raise interrupt when over threshold */
30*dfd2ab8dSPeter Meerwald-Stadler #define VEML6070_COMMAND_IT	GENMASK(3, 2) /* bit mask integration time */
31*dfd2ab8dSPeter Meerwald-Stadler #define VEML6070_COMMAND_RSRVD	BIT(1) /* reserved, set to 1 */
32*dfd2ab8dSPeter Meerwald-Stadler #define VEML6070_COMMAND_SD	BIT(0) /* shutdown mode when set */
33*dfd2ab8dSPeter Meerwald-Stadler 
34*dfd2ab8dSPeter Meerwald-Stadler #define VEML6070_IT_10	0x04 /* integration time 1x */
35*dfd2ab8dSPeter Meerwald-Stadler 
36*dfd2ab8dSPeter Meerwald-Stadler struct veml6070_data {
37*dfd2ab8dSPeter Meerwald-Stadler 	struct i2c_client *client1;
38*dfd2ab8dSPeter Meerwald-Stadler 	struct i2c_client *client2;
39*dfd2ab8dSPeter Meerwald-Stadler 	u8 config;
40*dfd2ab8dSPeter Meerwald-Stadler 	struct mutex lock;
41*dfd2ab8dSPeter Meerwald-Stadler };
42*dfd2ab8dSPeter Meerwald-Stadler 
43*dfd2ab8dSPeter Meerwald-Stadler static int veml6070_read(struct veml6070_data *data)
44*dfd2ab8dSPeter Meerwald-Stadler {
45*dfd2ab8dSPeter Meerwald-Stadler 	int ret;
46*dfd2ab8dSPeter Meerwald-Stadler 	u8 msb, lsb;
47*dfd2ab8dSPeter Meerwald-Stadler 
48*dfd2ab8dSPeter Meerwald-Stadler 	mutex_lock(&data->lock);
49*dfd2ab8dSPeter Meerwald-Stadler 
50*dfd2ab8dSPeter Meerwald-Stadler 	/* disable shutdown */
51*dfd2ab8dSPeter Meerwald-Stadler 	ret = i2c_smbus_write_byte(data->client1,
52*dfd2ab8dSPeter Meerwald-Stadler 	    data->config & ~VEML6070_COMMAND_SD);
53*dfd2ab8dSPeter Meerwald-Stadler 	if (ret < 0)
54*dfd2ab8dSPeter Meerwald-Stadler 		goto out;
55*dfd2ab8dSPeter Meerwald-Stadler 
56*dfd2ab8dSPeter Meerwald-Stadler 	msleep(125 + 10); /* measurement takes up to 125 ms for IT 1x */
57*dfd2ab8dSPeter Meerwald-Stadler 
58*dfd2ab8dSPeter Meerwald-Stadler 	ret = i2c_smbus_read_byte(data->client2); /* read MSB, address 0x39 */
59*dfd2ab8dSPeter Meerwald-Stadler 	if (ret < 0)
60*dfd2ab8dSPeter Meerwald-Stadler 		goto out;
61*dfd2ab8dSPeter Meerwald-Stadler 	msb = ret;
62*dfd2ab8dSPeter Meerwald-Stadler 
63*dfd2ab8dSPeter Meerwald-Stadler 	ret = i2c_smbus_read_byte(data->client1); /* read LSB, address 0x38 */
64*dfd2ab8dSPeter Meerwald-Stadler 	if (ret < 0)
65*dfd2ab8dSPeter Meerwald-Stadler 		goto out;
66*dfd2ab8dSPeter Meerwald-Stadler 	lsb = ret;
67*dfd2ab8dSPeter Meerwald-Stadler 
68*dfd2ab8dSPeter Meerwald-Stadler 	/* shutdown again */
69*dfd2ab8dSPeter Meerwald-Stadler 	ret = i2c_smbus_write_byte(data->client1, data->config);
70*dfd2ab8dSPeter Meerwald-Stadler 	if (ret < 0)
71*dfd2ab8dSPeter Meerwald-Stadler 		goto out;
72*dfd2ab8dSPeter Meerwald-Stadler 
73*dfd2ab8dSPeter Meerwald-Stadler 	ret = (msb << 8) | lsb;
74*dfd2ab8dSPeter Meerwald-Stadler 
75*dfd2ab8dSPeter Meerwald-Stadler out:
76*dfd2ab8dSPeter Meerwald-Stadler 	mutex_unlock(&data->lock);
77*dfd2ab8dSPeter Meerwald-Stadler 	return ret;
78*dfd2ab8dSPeter Meerwald-Stadler }
79*dfd2ab8dSPeter Meerwald-Stadler 
80*dfd2ab8dSPeter Meerwald-Stadler static const struct iio_chan_spec veml6070_channels[] = {
81*dfd2ab8dSPeter Meerwald-Stadler 	{
82*dfd2ab8dSPeter Meerwald-Stadler 		.type = IIO_INTENSITY,
83*dfd2ab8dSPeter Meerwald-Stadler 		.modified = 1,
84*dfd2ab8dSPeter Meerwald-Stadler 		.channel2 = IIO_MOD_LIGHT_UV,
85*dfd2ab8dSPeter Meerwald-Stadler 		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
86*dfd2ab8dSPeter Meerwald-Stadler 	},
87*dfd2ab8dSPeter Meerwald-Stadler 	{
88*dfd2ab8dSPeter Meerwald-Stadler 		.type = IIO_UVINDEX,
89*dfd2ab8dSPeter Meerwald-Stadler 		.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
90*dfd2ab8dSPeter Meerwald-Stadler 	}
91*dfd2ab8dSPeter Meerwald-Stadler };
92*dfd2ab8dSPeter Meerwald-Stadler 
93*dfd2ab8dSPeter Meerwald-Stadler static int veml6070_to_uv_index(unsigned val)
94*dfd2ab8dSPeter Meerwald-Stadler {
95*dfd2ab8dSPeter Meerwald-Stadler 	/*
96*dfd2ab8dSPeter Meerwald-Stadler 	 * conversion of raw UV intensity values to UV index depends on
97*dfd2ab8dSPeter Meerwald-Stadler 	 * integration time (IT) and value of the resistor connected to
98*dfd2ab8dSPeter Meerwald-Stadler 	 * the RSET pin (default: 270 KOhm)
99*dfd2ab8dSPeter Meerwald-Stadler 	 */
100*dfd2ab8dSPeter Meerwald-Stadler 	unsigned uvi[11] = {
101*dfd2ab8dSPeter Meerwald-Stadler 		187, 373, 560, /* low */
102*dfd2ab8dSPeter Meerwald-Stadler 		746, 933, 1120, /* moderate */
103*dfd2ab8dSPeter Meerwald-Stadler 		1308, 1494, /* high */
104*dfd2ab8dSPeter Meerwald-Stadler 		1681, 1868, 2054}; /* very high */
105*dfd2ab8dSPeter Meerwald-Stadler 	int i;
106*dfd2ab8dSPeter Meerwald-Stadler 
107*dfd2ab8dSPeter Meerwald-Stadler 	for (i = 0; i < ARRAY_SIZE(uvi); i++)
108*dfd2ab8dSPeter Meerwald-Stadler 		if (val <= uvi[i])
109*dfd2ab8dSPeter Meerwald-Stadler 			return i;
110*dfd2ab8dSPeter Meerwald-Stadler 
111*dfd2ab8dSPeter Meerwald-Stadler 	return 11; /* extreme */
112*dfd2ab8dSPeter Meerwald-Stadler }
113*dfd2ab8dSPeter Meerwald-Stadler 
114*dfd2ab8dSPeter Meerwald-Stadler static int veml6070_read_raw(struct iio_dev *indio_dev,
115*dfd2ab8dSPeter Meerwald-Stadler 				struct iio_chan_spec const *chan,
116*dfd2ab8dSPeter Meerwald-Stadler 				int *val, int *val2, long mask)
117*dfd2ab8dSPeter Meerwald-Stadler {
118*dfd2ab8dSPeter Meerwald-Stadler 	struct veml6070_data *data = iio_priv(indio_dev);
119*dfd2ab8dSPeter Meerwald-Stadler 	int ret;
120*dfd2ab8dSPeter Meerwald-Stadler 
121*dfd2ab8dSPeter Meerwald-Stadler 	switch (mask) {
122*dfd2ab8dSPeter Meerwald-Stadler 	case IIO_CHAN_INFO_RAW:
123*dfd2ab8dSPeter Meerwald-Stadler 	case IIO_CHAN_INFO_PROCESSED:
124*dfd2ab8dSPeter Meerwald-Stadler 		ret = veml6070_read(data);
125*dfd2ab8dSPeter Meerwald-Stadler 		if (ret < 0)
126*dfd2ab8dSPeter Meerwald-Stadler 			return ret;
127*dfd2ab8dSPeter Meerwald-Stadler 		if (mask == IIO_CHAN_INFO_PROCESSED)
128*dfd2ab8dSPeter Meerwald-Stadler 			*val = veml6070_to_uv_index(ret);
129*dfd2ab8dSPeter Meerwald-Stadler 		else
130*dfd2ab8dSPeter Meerwald-Stadler 			*val = ret;
131*dfd2ab8dSPeter Meerwald-Stadler 		return IIO_VAL_INT;
132*dfd2ab8dSPeter Meerwald-Stadler 	default:
133*dfd2ab8dSPeter Meerwald-Stadler 		return -EINVAL;
134*dfd2ab8dSPeter Meerwald-Stadler 	}
135*dfd2ab8dSPeter Meerwald-Stadler }
136*dfd2ab8dSPeter Meerwald-Stadler 
137*dfd2ab8dSPeter Meerwald-Stadler static const struct iio_info veml6070_info = {
138*dfd2ab8dSPeter Meerwald-Stadler 	.read_raw = veml6070_read_raw,
139*dfd2ab8dSPeter Meerwald-Stadler 	.driver_module = THIS_MODULE,
140*dfd2ab8dSPeter Meerwald-Stadler };
141*dfd2ab8dSPeter Meerwald-Stadler 
142*dfd2ab8dSPeter Meerwald-Stadler static int veml6070_probe(struct i2c_client *client,
143*dfd2ab8dSPeter Meerwald-Stadler 			  const struct i2c_device_id *id)
144*dfd2ab8dSPeter Meerwald-Stadler {
145*dfd2ab8dSPeter Meerwald-Stadler 	struct veml6070_data *data;
146*dfd2ab8dSPeter Meerwald-Stadler 	struct iio_dev *indio_dev;
147*dfd2ab8dSPeter Meerwald-Stadler 	int ret;
148*dfd2ab8dSPeter Meerwald-Stadler 
149*dfd2ab8dSPeter Meerwald-Stadler 	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
150*dfd2ab8dSPeter Meerwald-Stadler 	if (!indio_dev)
151*dfd2ab8dSPeter Meerwald-Stadler 		return -ENOMEM;
152*dfd2ab8dSPeter Meerwald-Stadler 
153*dfd2ab8dSPeter Meerwald-Stadler 	data = iio_priv(indio_dev);
154*dfd2ab8dSPeter Meerwald-Stadler 	i2c_set_clientdata(client, indio_dev);
155*dfd2ab8dSPeter Meerwald-Stadler 	data->client1 = client;
156*dfd2ab8dSPeter Meerwald-Stadler 	mutex_init(&data->lock);
157*dfd2ab8dSPeter Meerwald-Stadler 
158*dfd2ab8dSPeter Meerwald-Stadler 	indio_dev->dev.parent = &client->dev;
159*dfd2ab8dSPeter Meerwald-Stadler 	indio_dev->info = &veml6070_info;
160*dfd2ab8dSPeter Meerwald-Stadler 	indio_dev->channels = veml6070_channels;
161*dfd2ab8dSPeter Meerwald-Stadler 	indio_dev->num_channels = ARRAY_SIZE(veml6070_channels);
162*dfd2ab8dSPeter Meerwald-Stadler 	indio_dev->name = VEML6070_DRV_NAME;
163*dfd2ab8dSPeter Meerwald-Stadler 	indio_dev->modes = INDIO_DIRECT_MODE;
164*dfd2ab8dSPeter Meerwald-Stadler 
165*dfd2ab8dSPeter Meerwald-Stadler 	data->client2 = i2c_new_dummy(client->adapter, VEML6070_ADDR_DATA_LSB);
166*dfd2ab8dSPeter Meerwald-Stadler 	if (!data->client2) {
167*dfd2ab8dSPeter Meerwald-Stadler 		dev_err(&client->dev, "i2c device for second chip address failed\n");
168*dfd2ab8dSPeter Meerwald-Stadler 		return -ENODEV;
169*dfd2ab8dSPeter Meerwald-Stadler 	}
170*dfd2ab8dSPeter Meerwald-Stadler 
171*dfd2ab8dSPeter Meerwald-Stadler 	data->config = VEML6070_IT_10 | VEML6070_COMMAND_RSRVD |
172*dfd2ab8dSPeter Meerwald-Stadler 		VEML6070_COMMAND_SD;
173*dfd2ab8dSPeter Meerwald-Stadler 	ret = i2c_smbus_write_byte(data->client1, data->config);
174*dfd2ab8dSPeter Meerwald-Stadler 	if (ret < 0)
175*dfd2ab8dSPeter Meerwald-Stadler 		goto fail;
176*dfd2ab8dSPeter Meerwald-Stadler 
177*dfd2ab8dSPeter Meerwald-Stadler 	ret = iio_device_register(indio_dev);
178*dfd2ab8dSPeter Meerwald-Stadler 	if (ret < 0)
179*dfd2ab8dSPeter Meerwald-Stadler 		goto fail;
180*dfd2ab8dSPeter Meerwald-Stadler 
181*dfd2ab8dSPeter Meerwald-Stadler 	return ret;
182*dfd2ab8dSPeter Meerwald-Stadler 
183*dfd2ab8dSPeter Meerwald-Stadler fail:
184*dfd2ab8dSPeter Meerwald-Stadler 	i2c_unregister_device(data->client2);
185*dfd2ab8dSPeter Meerwald-Stadler 	return ret;
186*dfd2ab8dSPeter Meerwald-Stadler }
187*dfd2ab8dSPeter Meerwald-Stadler 
188*dfd2ab8dSPeter Meerwald-Stadler static int veml6070_remove(struct i2c_client *client)
189*dfd2ab8dSPeter Meerwald-Stadler {
190*dfd2ab8dSPeter Meerwald-Stadler 	struct iio_dev *indio_dev = i2c_get_clientdata(client);
191*dfd2ab8dSPeter Meerwald-Stadler 	struct veml6070_data *data = iio_priv(indio_dev);
192*dfd2ab8dSPeter Meerwald-Stadler 
193*dfd2ab8dSPeter Meerwald-Stadler 	iio_device_unregister(indio_dev);
194*dfd2ab8dSPeter Meerwald-Stadler 	i2c_unregister_device(data->client2);
195*dfd2ab8dSPeter Meerwald-Stadler 
196*dfd2ab8dSPeter Meerwald-Stadler 	return 0;
197*dfd2ab8dSPeter Meerwald-Stadler }
198*dfd2ab8dSPeter Meerwald-Stadler 
199*dfd2ab8dSPeter Meerwald-Stadler static const struct i2c_device_id veml6070_id[] = {
200*dfd2ab8dSPeter Meerwald-Stadler 	{ "veml6070", 0 },
201*dfd2ab8dSPeter Meerwald-Stadler 	{ }
202*dfd2ab8dSPeter Meerwald-Stadler };
203*dfd2ab8dSPeter Meerwald-Stadler MODULE_DEVICE_TABLE(i2c, veml6070_id);
204*dfd2ab8dSPeter Meerwald-Stadler 
205*dfd2ab8dSPeter Meerwald-Stadler static struct i2c_driver veml6070_driver = {
206*dfd2ab8dSPeter Meerwald-Stadler 	.driver = {
207*dfd2ab8dSPeter Meerwald-Stadler 		.name   = VEML6070_DRV_NAME,
208*dfd2ab8dSPeter Meerwald-Stadler 	},
209*dfd2ab8dSPeter Meerwald-Stadler 	.probe  = veml6070_probe,
210*dfd2ab8dSPeter Meerwald-Stadler 	.remove  = veml6070_remove,
211*dfd2ab8dSPeter Meerwald-Stadler 	.id_table = veml6070_id,
212*dfd2ab8dSPeter Meerwald-Stadler };
213*dfd2ab8dSPeter Meerwald-Stadler 
214*dfd2ab8dSPeter Meerwald-Stadler module_i2c_driver(veml6070_driver);
215*dfd2ab8dSPeter Meerwald-Stadler 
216*dfd2ab8dSPeter Meerwald-Stadler MODULE_AUTHOR("Peter Meerwald-Stadler <pmeerw@pmeerw.net>");
217*dfd2ab8dSPeter Meerwald-Stadler MODULE_DESCRIPTION("Vishay VEML6070 UV A light sensor driver");
218*dfd2ab8dSPeter Meerwald-Stadler MODULE_LICENSE("GPL");
219