Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

iio: Add Vishay VEML6070 UV A light sensor driver

ultraviolet (UV) light sensor with I2C interface with a peak
sensitivity at 355 nm

strangely, chip uses two addresses 0x38 and 0x39 for LSB and
MSB data, resp.

datasheet: http://www.vishay.com/docs/84277/veml6070.pdf

Signed-off-by: Peter Meerwald-Stadler <pmeerw@pmeerw.net>
Signed-off-by: Jonathan Cameron <jic23@kernel.org>

authored by

Peter Meerwald-Stadler and committed by
Jonathan Cameron
dfd2ab8d e21a294d

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