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