Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * zopt2201.c - Support for IDT ZOPT2201 ambient light and UV B sensor
0004  *
0005  * Copyright 2017 Peter Meerwald-Stadler <pmeerw@pmeerw.net>
0006  *
0007  * Datasheet: https://www.idt.com/document/dst/zopt2201-datasheet
0008  * 7-bit I2C slave addresses 0x53 (default) or 0x52 (programmed)
0009  *
0010  * TODO: interrupt support, ALS/UVB raw mode
0011  */
0012 
0013 #include <linux/module.h>
0014 #include <linux/i2c.h>
0015 #include <linux/mutex.h>
0016 #include <linux/err.h>
0017 #include <linux/delay.h>
0018 
0019 #include <linux/iio/iio.h>
0020 #include <linux/iio/sysfs.h>
0021 
0022 #include <asm/unaligned.h>
0023 
0024 #define ZOPT2201_DRV_NAME "zopt2201"
0025 
0026 /* Registers */
0027 #define ZOPT2201_MAIN_CTRL      0x00
0028 #define ZOPT2201_LS_MEAS_RATE       0x04
0029 #define ZOPT2201_LS_GAIN        0x05
0030 #define ZOPT2201_PART_ID        0x06
0031 #define ZOPT2201_MAIN_STATUS        0x07
0032 #define ZOPT2201_ALS_DATA       0x0d /* LSB first, 13 to 20 bits */
0033 #define ZOPT2201_UVB_DATA       0x10 /* LSB first, 13 to 20 bits */
0034 #define ZOPT2201_UV_COMP_DATA       0x13 /* LSB first, 13 to 20 bits */
0035 #define ZOPT2201_COMP_DATA      0x16 /* LSB first, 13 to 20 bits */
0036 #define ZOPT2201_INT_CFG        0x19
0037 #define ZOPT2201_INT_PST        0x1a
0038 
0039 #define ZOPT2201_MAIN_CTRL_LS_MODE  BIT(3) /* 0 .. ALS, 1 .. UV B */
0040 #define ZOPT2201_MAIN_CTRL_LS_EN    BIT(1)
0041 
0042 /* Values for ZOPT2201_LS_MEAS_RATE resolution / bit width */
0043 #define ZOPT2201_MEAS_RES_20BIT     0 /* takes 400 ms */
0044 #define ZOPT2201_MEAS_RES_19BIT     1 /* takes 200 ms */
0045 #define ZOPT2201_MEAS_RES_18BIT     2 /* takes 100 ms, default */
0046 #define ZOPT2201_MEAS_RES_17BIT     3 /* takes 50 ms */
0047 #define ZOPT2201_MEAS_RES_16BIT     4 /* takes 25 ms */
0048 #define ZOPT2201_MEAS_RES_13BIT     5 /* takes 3.125 ms */
0049 #define ZOPT2201_MEAS_RES_SHIFT     4
0050 
0051 /* Values for ZOPT2201_LS_MEAS_RATE measurement rate */
0052 #define ZOPT2201_MEAS_FREQ_25MS     0
0053 #define ZOPT2201_MEAS_FREQ_50MS     1
0054 #define ZOPT2201_MEAS_FREQ_100MS    2 /* default */
0055 #define ZOPT2201_MEAS_FREQ_200MS    3
0056 #define ZOPT2201_MEAS_FREQ_500MS    4
0057 #define ZOPT2201_MEAS_FREQ_1000MS   5
0058 #define ZOPT2201_MEAS_FREQ_2000MS   6
0059 
0060 /* Values for ZOPT2201_LS_GAIN */
0061 #define ZOPT2201_LS_GAIN_1      0
0062 #define ZOPT2201_LS_GAIN_3      1
0063 #define ZOPT2201_LS_GAIN_6      2
0064 #define ZOPT2201_LS_GAIN_9      3
0065 #define ZOPT2201_LS_GAIN_18     4
0066 
0067 /* Values for ZOPT2201_MAIN_STATUS */
0068 #define ZOPT2201_MAIN_STATUS_POWERON    BIT(5)
0069 #define ZOPT2201_MAIN_STATUS_INT    BIT(4)
0070 #define ZOPT2201_MAIN_STATUS_DRDY   BIT(3)
0071 
0072 #define ZOPT2201_PART_NUMBER        0xb2
0073 
0074 struct zopt2201_data {
0075     struct i2c_client *client;
0076     struct mutex lock;
0077     u8 gain;
0078     u8 res;
0079     u8 rate;
0080 };
0081 
0082 static const struct {
0083     unsigned int gain; /* gain factor */
0084     unsigned int scale; /* micro lux per count */
0085 } zopt2201_gain_als[] = {
0086     {  1, 19200000 },
0087     {  3,  6400000 },
0088     {  6,  3200000 },
0089     {  9,  2133333 },
0090     { 18,  1066666 },
0091 };
0092 
0093 static const struct {
0094     unsigned int gain; /* gain factor */
0095     unsigned int scale; /* micro W/m2 per count */
0096 } zopt2201_gain_uvb[] = {
0097     {  1, 460800 },
0098     {  3, 153600 },
0099     {  6,  76800 },
0100     {  9,  51200 },
0101     { 18,  25600 },
0102 };
0103 
0104 static const struct {
0105     unsigned int bits; /* sensor resolution in bits */
0106     unsigned long us; /* measurement time in micro seconds */
0107 } zopt2201_resolution[] = {
0108     { 20, 400000 },
0109     { 19, 200000 },
0110     { 18, 100000 },
0111     { 17,  50000 },
0112     { 16,  25000 },
0113     { 13,   3125 },
0114 };
0115 
0116 static const struct {
0117     unsigned int scale, uscale; /* scale factor as integer + micro */
0118     u8 gain; /* gain register value */
0119     u8 res; /* resolution register value */
0120 } zopt2201_scale_als[] = {
0121     { 19, 200000, 0, 5 },
0122     {  6, 400000, 1, 5 },
0123     {  3, 200000, 2, 5 },
0124     {  2, 400000, 0, 4 },
0125     {  2, 133333, 3, 5 },
0126     {  1, 200000, 0, 3 },
0127     {  1,  66666, 4, 5 },
0128     {  0, 800000, 1, 4 },
0129     {  0, 600000, 0, 2 },
0130     {  0, 400000, 2, 4 },
0131     {  0, 300000, 0, 1 },
0132     {  0, 266666, 3, 4 },
0133     {  0, 200000, 2, 3 },
0134     {  0, 150000, 0, 0 },
0135     {  0, 133333, 4, 4 },
0136     {  0, 100000, 2, 2 },
0137     {  0,  66666, 4, 3 },
0138     {  0,  50000, 2, 1 },
0139     {  0,  33333, 4, 2 },
0140     {  0,  25000, 2, 0 },
0141     {  0,  16666, 4, 1 },
0142     {  0,   8333, 4, 0 },
0143 };
0144 
0145 static const struct {
0146     unsigned int scale, uscale; /* scale factor as integer + micro */
0147     u8 gain; /* gain register value */
0148     u8 res; /* resolution register value */
0149 } zopt2201_scale_uvb[] = {
0150     { 0, 460800, 0, 5 },
0151     { 0, 153600, 1, 5 },
0152     { 0,  76800, 2, 5 },
0153     { 0,  57600, 0, 4 },
0154     { 0,  51200, 3, 5 },
0155     { 0,  28800, 0, 3 },
0156     { 0,  25600, 4, 5 },
0157     { 0,  19200, 1, 4 },
0158     { 0,  14400, 0, 2 },
0159     { 0,   9600, 2, 4 },
0160     { 0,   7200, 0, 1 },
0161     { 0,   6400, 3, 4 },
0162     { 0,   4800, 2, 3 },
0163     { 0,   3600, 0, 0 },
0164     { 0,   3200, 4, 4 },
0165     { 0,   2400, 2, 2 },
0166     { 0,   1600, 4, 3 },
0167     { 0,   1200, 2, 1 },
0168     { 0,    800, 4, 2 },
0169     { 0,    600, 2, 0 },
0170     { 0,    400, 4, 1 },
0171     { 0,    200, 4, 0 },
0172 };
0173 
0174 static int zopt2201_enable_mode(struct zopt2201_data *data, bool uvb_mode)
0175 {
0176     u8 out = ZOPT2201_MAIN_CTRL_LS_EN;
0177 
0178     if (uvb_mode)
0179         out |= ZOPT2201_MAIN_CTRL_LS_MODE;
0180 
0181     return i2c_smbus_write_byte_data(data->client, ZOPT2201_MAIN_CTRL, out);
0182 }
0183 
0184 static int zopt2201_read(struct zopt2201_data *data, u8 reg)
0185 {
0186     struct i2c_client *client = data->client;
0187     int tries = 10;
0188     u8 buf[3];
0189     int ret;
0190 
0191     mutex_lock(&data->lock);
0192     ret = zopt2201_enable_mode(data, reg == ZOPT2201_UVB_DATA);
0193     if (ret < 0)
0194         goto fail;
0195 
0196     while (tries--) {
0197         unsigned long t = zopt2201_resolution[data->res].us;
0198 
0199         if (t <= 20000)
0200             usleep_range(t, t + 1000);
0201         else
0202             msleep(t / 1000);
0203         ret = i2c_smbus_read_byte_data(client, ZOPT2201_MAIN_STATUS);
0204         if (ret < 0)
0205             goto fail;
0206         if (ret & ZOPT2201_MAIN_STATUS_DRDY)
0207             break;
0208     }
0209 
0210     if (tries < 0) {
0211         ret = -ETIMEDOUT;
0212         goto fail;
0213     }
0214 
0215     ret = i2c_smbus_read_i2c_block_data(client, reg, sizeof(buf), buf);
0216     if (ret < 0)
0217         goto fail;
0218 
0219     ret = i2c_smbus_write_byte_data(client, ZOPT2201_MAIN_CTRL, 0x00);
0220     if (ret < 0)
0221         goto fail;
0222     mutex_unlock(&data->lock);
0223 
0224     return get_unaligned_le24(&buf[0]);
0225 
0226 fail:
0227     mutex_unlock(&data->lock);
0228     return ret;
0229 }
0230 
0231 static const struct iio_chan_spec zopt2201_channels[] = {
0232     {
0233         .type = IIO_LIGHT,
0234         .address = ZOPT2201_ALS_DATA,
0235         .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
0236                       BIT(IIO_CHAN_INFO_SCALE),
0237         .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME),
0238     },
0239     {
0240         .type = IIO_INTENSITY,
0241         .modified = 1,
0242         .channel2 = IIO_MOD_LIGHT_UV,
0243         .address = ZOPT2201_UVB_DATA,
0244         .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
0245                       BIT(IIO_CHAN_INFO_SCALE),
0246         .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME),
0247     },
0248     {
0249         .type = IIO_UVINDEX,
0250         .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
0251     },
0252 };
0253 
0254 static int zopt2201_read_raw(struct iio_dev *indio_dev,
0255                 struct iio_chan_spec const *chan,
0256                 int *val, int *val2, long mask)
0257 {
0258     struct zopt2201_data *data = iio_priv(indio_dev);
0259     u64 tmp;
0260     int ret;
0261 
0262     switch (mask) {
0263     case IIO_CHAN_INFO_RAW:
0264         ret = zopt2201_read(data, chan->address);
0265         if (ret < 0)
0266             return ret;
0267         *val = ret;
0268         return IIO_VAL_INT;
0269     case IIO_CHAN_INFO_PROCESSED:
0270         ret = zopt2201_read(data, ZOPT2201_UVB_DATA);
0271         if (ret < 0)
0272             return ret;
0273         *val = ret * 18 *
0274             (1 << (20 - zopt2201_resolution[data->res].bits)) /
0275             zopt2201_gain_uvb[data->gain].gain;
0276         return IIO_VAL_INT;
0277     case IIO_CHAN_INFO_SCALE:
0278         switch (chan->address) {
0279         case ZOPT2201_ALS_DATA:
0280             *val = zopt2201_gain_als[data->gain].scale;
0281             break;
0282         case ZOPT2201_UVB_DATA:
0283             *val = zopt2201_gain_uvb[data->gain].scale;
0284             break;
0285         default:
0286             return -EINVAL;
0287         }
0288 
0289         *val2 = 1000000;
0290         *val2 *= (1 << (zopt2201_resolution[data->res].bits - 13));
0291         tmp = div_s64(*val * 1000000ULL, *val2);
0292         *val = div_s64_rem(tmp, 1000000, val2);
0293 
0294         return IIO_VAL_INT_PLUS_MICRO;
0295     case IIO_CHAN_INFO_INT_TIME:
0296         *val = 0;
0297         *val2 = zopt2201_resolution[data->res].us;
0298         return IIO_VAL_INT_PLUS_MICRO;
0299     default:
0300         return -EINVAL;
0301     }
0302 }
0303 
0304 static int zopt2201_set_resolution(struct zopt2201_data *data, u8 res)
0305 {
0306     int ret;
0307 
0308     ret = i2c_smbus_write_byte_data(data->client, ZOPT2201_LS_MEAS_RATE,
0309                     (res << ZOPT2201_MEAS_RES_SHIFT) |
0310                     data->rate);
0311     if (ret < 0)
0312         return ret;
0313 
0314     data->res = res;
0315 
0316     return 0;
0317 }
0318 
0319 static int zopt2201_write_resolution(struct zopt2201_data *data,
0320                      int val, int val2)
0321 {
0322     int i, ret;
0323 
0324     if (val != 0)
0325         return -EINVAL;
0326 
0327     for (i = 0; i < ARRAY_SIZE(zopt2201_resolution); i++)
0328         if (val2 == zopt2201_resolution[i].us) {
0329             mutex_lock(&data->lock);
0330             ret = zopt2201_set_resolution(data, i);
0331             mutex_unlock(&data->lock);
0332             return ret;
0333         }
0334 
0335     return -EINVAL;
0336 }
0337 
0338 static int zopt2201_set_gain(struct zopt2201_data *data, u8 gain)
0339 {
0340     int ret;
0341 
0342     ret = i2c_smbus_write_byte_data(data->client, ZOPT2201_LS_GAIN, gain);
0343     if (ret < 0)
0344         return ret;
0345 
0346     data->gain = gain;
0347 
0348     return 0;
0349 }
0350 
0351 static int zopt2201_write_scale_als_by_idx(struct zopt2201_data *data, int idx)
0352 {
0353     int ret;
0354 
0355     mutex_lock(&data->lock);
0356     ret = zopt2201_set_resolution(data, zopt2201_scale_als[idx].res);
0357     if (ret < 0)
0358         goto unlock;
0359 
0360     ret = zopt2201_set_gain(data, zopt2201_scale_als[idx].gain);
0361 
0362 unlock:
0363     mutex_unlock(&data->lock);
0364     return ret;
0365 }
0366 
0367 static int zopt2201_write_scale_als(struct zopt2201_data *data,
0368                      int val, int val2)
0369 {
0370     int i;
0371 
0372     for (i = 0; i < ARRAY_SIZE(zopt2201_scale_als); i++)
0373         if (val == zopt2201_scale_als[i].scale &&
0374             val2 == zopt2201_scale_als[i].uscale) {
0375             return zopt2201_write_scale_als_by_idx(data, i);
0376         }
0377 
0378     return -EINVAL;
0379 }
0380 
0381 static int zopt2201_write_scale_uvb_by_idx(struct zopt2201_data *data, int idx)
0382 {
0383     int ret;
0384 
0385     mutex_lock(&data->lock);
0386     ret = zopt2201_set_resolution(data, zopt2201_scale_als[idx].res);
0387     if (ret < 0)
0388         goto unlock;
0389 
0390     ret = zopt2201_set_gain(data, zopt2201_scale_als[idx].gain);
0391 
0392 unlock:
0393     mutex_unlock(&data->lock);
0394     return ret;
0395 }
0396 
0397 static int zopt2201_write_scale_uvb(struct zopt2201_data *data,
0398                      int val, int val2)
0399 {
0400     int i;
0401 
0402     for (i = 0; i < ARRAY_SIZE(zopt2201_scale_uvb); i++)
0403         if (val == zopt2201_scale_uvb[i].scale &&
0404             val2 == zopt2201_scale_uvb[i].uscale)
0405             return zopt2201_write_scale_uvb_by_idx(data, i);
0406 
0407     return -EINVAL;
0408 }
0409 
0410 static int zopt2201_write_raw(struct iio_dev *indio_dev,
0411                   struct iio_chan_spec const *chan,
0412                   int val, int val2, long mask)
0413 {
0414     struct zopt2201_data *data = iio_priv(indio_dev);
0415 
0416     switch (mask) {
0417     case IIO_CHAN_INFO_INT_TIME:
0418         return zopt2201_write_resolution(data, val, val2);
0419     case IIO_CHAN_INFO_SCALE:
0420         switch (chan->address) {
0421         case ZOPT2201_ALS_DATA:
0422             return zopt2201_write_scale_als(data, val, val2);
0423         case ZOPT2201_UVB_DATA:
0424             return zopt2201_write_scale_uvb(data, val, val2);
0425         default:
0426             return -EINVAL;
0427         }
0428     }
0429 
0430     return -EINVAL;
0431 }
0432 
0433 static ssize_t zopt2201_show_int_time_available(struct device *dev,
0434                         struct device_attribute *attr,
0435                         char *buf)
0436 {
0437     size_t len = 0;
0438     int i;
0439 
0440     for (i = 0; i < ARRAY_SIZE(zopt2201_resolution); i++)
0441         len += scnprintf(buf + len, PAGE_SIZE - len, "0.%06lu ",
0442                  zopt2201_resolution[i].us);
0443     buf[len - 1] = '\n';
0444 
0445     return len;
0446 }
0447 
0448 static IIO_DEV_ATTR_INT_TIME_AVAIL(zopt2201_show_int_time_available);
0449 
0450 static ssize_t zopt2201_show_als_scale_avail(struct device *dev,
0451                          struct device_attribute *attr,
0452                          char *buf)
0453 {
0454     ssize_t len = 0;
0455     int i;
0456 
0457     for (i = 0; i < ARRAY_SIZE(zopt2201_scale_als); i++)
0458         len += scnprintf(buf + len, PAGE_SIZE - len, "%d.%06u ",
0459                  zopt2201_scale_als[i].scale,
0460                  zopt2201_scale_als[i].uscale);
0461     buf[len - 1] = '\n';
0462 
0463     return len;
0464 }
0465 
0466 static ssize_t zopt2201_show_uvb_scale_avail(struct device *dev,
0467                          struct device_attribute *attr,
0468                          char *buf)
0469 {
0470     ssize_t len = 0;
0471     int i;
0472 
0473     for (i = 0; i < ARRAY_SIZE(zopt2201_scale_uvb); i++)
0474         len += scnprintf(buf + len, PAGE_SIZE - len, "%d.%06u ",
0475                  zopt2201_scale_uvb[i].scale,
0476                  zopt2201_scale_uvb[i].uscale);
0477     buf[len - 1] = '\n';
0478 
0479     return len;
0480 }
0481 
0482 static IIO_DEVICE_ATTR(in_illuminance_scale_available, 0444,
0483                zopt2201_show_als_scale_avail, NULL, 0);
0484 static IIO_DEVICE_ATTR(in_intensity_uv_scale_available, 0444,
0485                zopt2201_show_uvb_scale_avail, NULL, 0);
0486 
0487 static struct attribute *zopt2201_attributes[] = {
0488     &iio_dev_attr_integration_time_available.dev_attr.attr,
0489     &iio_dev_attr_in_illuminance_scale_available.dev_attr.attr,
0490     &iio_dev_attr_in_intensity_uv_scale_available.dev_attr.attr,
0491     NULL
0492 };
0493 
0494 static const struct attribute_group zopt2201_attribute_group = {
0495     .attrs = zopt2201_attributes,
0496 };
0497 
0498 static const struct iio_info zopt2201_info = {
0499     .read_raw = zopt2201_read_raw,
0500     .write_raw = zopt2201_write_raw,
0501     .attrs = &zopt2201_attribute_group,
0502 };
0503 
0504 static int zopt2201_probe(struct i2c_client *client,
0505               const struct i2c_device_id *id)
0506 {
0507     struct zopt2201_data *data;
0508     struct iio_dev *indio_dev;
0509     int ret;
0510 
0511     if (!i2c_check_functionality(client->adapter,
0512                      I2C_FUNC_SMBUS_READ_I2C_BLOCK))
0513         return -EOPNOTSUPP;
0514 
0515     ret = i2c_smbus_read_byte_data(client, ZOPT2201_PART_ID);
0516     if (ret < 0)
0517         return ret;
0518     if (ret != ZOPT2201_PART_NUMBER)
0519         return -ENODEV;
0520 
0521     indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
0522     if (!indio_dev)
0523         return -ENOMEM;
0524 
0525     data = iio_priv(indio_dev);
0526     i2c_set_clientdata(client, indio_dev);
0527     data->client = client;
0528     mutex_init(&data->lock);
0529 
0530     indio_dev->info = &zopt2201_info;
0531     indio_dev->channels = zopt2201_channels;
0532     indio_dev->num_channels = ARRAY_SIZE(zopt2201_channels);
0533     indio_dev->name = ZOPT2201_DRV_NAME;
0534     indio_dev->modes = INDIO_DIRECT_MODE;
0535 
0536     data->rate = ZOPT2201_MEAS_FREQ_100MS;
0537     ret = zopt2201_set_resolution(data, ZOPT2201_MEAS_RES_18BIT);
0538     if (ret < 0)
0539         return ret;
0540 
0541     ret = zopt2201_set_gain(data, ZOPT2201_LS_GAIN_3);
0542     if (ret < 0)
0543         return ret;
0544 
0545     return devm_iio_device_register(&client->dev, indio_dev);
0546 }
0547 
0548 static const struct i2c_device_id zopt2201_id[] = {
0549     { "zopt2201", 0 },
0550     { }
0551 };
0552 MODULE_DEVICE_TABLE(i2c, zopt2201_id);
0553 
0554 static struct i2c_driver zopt2201_driver = {
0555     .driver = {
0556         .name   = ZOPT2201_DRV_NAME,
0557     },
0558     .probe  = zopt2201_probe,
0559     .id_table = zopt2201_id,
0560 };
0561 
0562 module_i2c_driver(zopt2201_driver);
0563 
0564 MODULE_AUTHOR("Peter Meerwald-Stadler <pmeerw@pmeerw.net>");
0565 MODULE_DESCRIPTION("IDT ZOPT2201 ambient light and UV B sensor driver");
0566 MODULE_LICENSE("GPL");