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