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