Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * veml6070.c - Support for Vishay VEML6070 UV A light sensor
0004  *
0005  * Copyright 2016 Peter Meerwald-Stadler <pmeerw@pmeerw.net>
0006  *
0007  * IIO driver for VEML6070 (7-bit I2C slave addresses 0x38 and 0x39)
0008  *
0009  * TODO: integration time, ACK signal
0010  */
0011 
0012 #include <linux/module.h>
0013 #include <linux/i2c.h>
0014 #include <linux/mutex.h>
0015 #include <linux/err.h>
0016 #include <linux/delay.h>
0017 
0018 #include <linux/iio/iio.h>
0019 #include <linux/iio/sysfs.h>
0020 
0021 #define VEML6070_DRV_NAME "veml6070"
0022 
0023 #define VEML6070_ADDR_CONFIG_DATA_MSB 0x38 /* read: MSB data, write: config */
0024 #define VEML6070_ADDR_DATA_LSB  0x39 /* LSB data */
0025 
0026 #define VEML6070_COMMAND_ACK    BIT(5) /* raise interrupt when over threshold */
0027 #define VEML6070_COMMAND_IT GENMASK(3, 2) /* bit mask integration time */
0028 #define VEML6070_COMMAND_RSRVD  BIT(1) /* reserved, set to 1 */
0029 #define VEML6070_COMMAND_SD BIT(0) /* shutdown mode when set */
0030 
0031 #define VEML6070_IT_10  0x04 /* integration time 1x */
0032 
0033 struct veml6070_data {
0034     struct i2c_client *client1;
0035     struct i2c_client *client2;
0036     u8 config;
0037     struct mutex lock;
0038 };
0039 
0040 static int veml6070_read(struct veml6070_data *data)
0041 {
0042     int ret;
0043     u8 msb, lsb;
0044 
0045     mutex_lock(&data->lock);
0046 
0047     /* disable shutdown */
0048     ret = i2c_smbus_write_byte(data->client1,
0049         data->config & ~VEML6070_COMMAND_SD);
0050     if (ret < 0)
0051         goto out;
0052 
0053     msleep(125 + 10); /* measurement takes up to 125 ms for IT 1x */
0054 
0055     ret = i2c_smbus_read_byte(data->client2); /* read MSB, address 0x39 */
0056     if (ret < 0)
0057         goto out;
0058     msb = ret;
0059 
0060     ret = i2c_smbus_read_byte(data->client1); /* read LSB, address 0x38 */
0061     if (ret < 0)
0062         goto out;
0063     lsb = ret;
0064 
0065     /* shutdown again */
0066     ret = i2c_smbus_write_byte(data->client1, data->config);
0067     if (ret < 0)
0068         goto out;
0069 
0070     ret = (msb << 8) | lsb;
0071 
0072 out:
0073     mutex_unlock(&data->lock);
0074     return ret;
0075 }
0076 
0077 static const struct iio_chan_spec veml6070_channels[] = {
0078     {
0079         .type = IIO_INTENSITY,
0080         .modified = 1,
0081         .channel2 = IIO_MOD_LIGHT_UV,
0082         .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
0083     },
0084     {
0085         .type = IIO_UVINDEX,
0086         .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
0087     }
0088 };
0089 
0090 static int veml6070_to_uv_index(unsigned val)
0091 {
0092     /*
0093      * conversion of raw UV intensity values to UV index depends on
0094      * integration time (IT) and value of the resistor connected to
0095      * the RSET pin (default: 270 KOhm)
0096      */
0097     unsigned uvi[11] = {
0098         187, 373, 560, /* low */
0099         746, 933, 1120, /* moderate */
0100         1308, 1494, /* high */
0101         1681, 1868, 2054}; /* very high */
0102     int i;
0103 
0104     for (i = 0; i < ARRAY_SIZE(uvi); i++)
0105         if (val <= uvi[i])
0106             return i;
0107 
0108     return 11; /* extreme */
0109 }
0110 
0111 static int veml6070_read_raw(struct iio_dev *indio_dev,
0112                 struct iio_chan_spec const *chan,
0113                 int *val, int *val2, long mask)
0114 {
0115     struct veml6070_data *data = iio_priv(indio_dev);
0116     int ret;
0117 
0118     switch (mask) {
0119     case IIO_CHAN_INFO_RAW:
0120     case IIO_CHAN_INFO_PROCESSED:
0121         ret = veml6070_read(data);
0122         if (ret < 0)
0123             return ret;
0124         if (mask == IIO_CHAN_INFO_PROCESSED)
0125             *val = veml6070_to_uv_index(ret);
0126         else
0127             *val = ret;
0128         return IIO_VAL_INT;
0129     default:
0130         return -EINVAL;
0131     }
0132 }
0133 
0134 static const struct iio_info veml6070_info = {
0135     .read_raw = veml6070_read_raw,
0136 };
0137 
0138 static int veml6070_probe(struct i2c_client *client,
0139               const struct i2c_device_id *id)
0140 {
0141     struct veml6070_data *data;
0142     struct iio_dev *indio_dev;
0143     int ret;
0144 
0145     indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
0146     if (!indio_dev)
0147         return -ENOMEM;
0148 
0149     data = iio_priv(indio_dev);
0150     i2c_set_clientdata(client, indio_dev);
0151     data->client1 = client;
0152     mutex_init(&data->lock);
0153 
0154     indio_dev->info = &veml6070_info;
0155     indio_dev->channels = veml6070_channels;
0156     indio_dev->num_channels = ARRAY_SIZE(veml6070_channels);
0157     indio_dev->name = VEML6070_DRV_NAME;
0158     indio_dev->modes = INDIO_DIRECT_MODE;
0159 
0160     data->client2 = i2c_new_dummy_device(client->adapter, VEML6070_ADDR_DATA_LSB);
0161     if (IS_ERR(data->client2)) {
0162         dev_err(&client->dev, "i2c device for second chip address failed\n");
0163         return PTR_ERR(data->client2);
0164     }
0165 
0166     data->config = VEML6070_IT_10 | VEML6070_COMMAND_RSRVD |
0167         VEML6070_COMMAND_SD;
0168     ret = i2c_smbus_write_byte(data->client1, data->config);
0169     if (ret < 0)
0170         goto fail;
0171 
0172     ret = iio_device_register(indio_dev);
0173     if (ret < 0)
0174         goto fail;
0175 
0176     return ret;
0177 
0178 fail:
0179     i2c_unregister_device(data->client2);
0180     return ret;
0181 }
0182 
0183 static int veml6070_remove(struct i2c_client *client)
0184 {
0185     struct iio_dev *indio_dev = i2c_get_clientdata(client);
0186     struct veml6070_data *data = iio_priv(indio_dev);
0187 
0188     iio_device_unregister(indio_dev);
0189     i2c_unregister_device(data->client2);
0190 
0191     return 0;
0192 }
0193 
0194 static const struct i2c_device_id veml6070_id[] = {
0195     { "veml6070", 0 },
0196     { }
0197 };
0198 MODULE_DEVICE_TABLE(i2c, veml6070_id);
0199 
0200 static struct i2c_driver veml6070_driver = {
0201     .driver = {
0202         .name   = VEML6070_DRV_NAME,
0203     },
0204     .probe  = veml6070_probe,
0205     .remove  = veml6070_remove,
0206     .id_table = veml6070_id,
0207 };
0208 
0209 module_i2c_driver(veml6070_driver);
0210 
0211 MODULE_AUTHOR("Peter Meerwald-Stadler <pmeerw@pmeerw.net>");
0212 MODULE_DESCRIPTION("Vishay VEML6070 UV A light sensor driver");
0213 MODULE_LICENSE("GPL");