Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * AL3010 - Dyna Image Ambient Light Sensor
0004  *
0005  * Copyright (c) 2014, Intel Corporation.
0006  * Copyright (c) 2016, Dyna-Image Corp.
0007  * Copyright (c) 2020, David Heidelberg, Michał Mirosław, Dmitry Osipenko
0008  *
0009  * IIO driver for AL3010 (7-bit I2C slave address 0x1C).
0010  *
0011  * TODO: interrupt support, thresholds
0012  * When the driver will get support for interrupt handling, then interrupt
0013  * will need to be disabled before turning sensor OFF in order to avoid
0014  * potential races with the interrupt handling.
0015  */
0016 
0017 #include <linux/bitfield.h>
0018 #include <linux/i2c.h>
0019 #include <linux/module.h>
0020 #include <linux/of.h>
0021 
0022 #include <linux/iio/iio.h>
0023 #include <linux/iio/sysfs.h>
0024 
0025 #define AL3010_DRV_NAME "al3010"
0026 
0027 #define AL3010_REG_SYSTEM       0x00
0028 #define AL3010_REG_DATA_LOW     0x0c
0029 #define AL3010_REG_CONFIG       0x10
0030 
0031 #define AL3010_CONFIG_DISABLE       0x00
0032 #define AL3010_CONFIG_ENABLE        0x01
0033 
0034 #define AL3010_GAIN_MASK        GENMASK(6,4)
0035 
0036 #define AL3010_SCALE_AVAILABLE "1.1872 0.2968 0.0742 0.018"
0037 
0038 enum al3xxxx_range {
0039     AL3XXX_RANGE_1, /* 77806 lx */
0040     AL3XXX_RANGE_2, /* 19542 lx */
0041     AL3XXX_RANGE_3, /*  4863 lx */
0042     AL3XXX_RANGE_4  /*  1216 lx */
0043 };
0044 
0045 static const int al3010_scales[][2] = {
0046     {0, 1187200}, {0, 296800}, {0, 74200}, {0, 18600}
0047 };
0048 
0049 struct al3010_data {
0050     struct i2c_client *client;
0051 };
0052 
0053 static const struct iio_chan_spec al3010_channels[] = {
0054     {
0055         .type   = IIO_LIGHT,
0056         .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
0057                       BIT(IIO_CHAN_INFO_SCALE),
0058     }
0059 };
0060 
0061 static IIO_CONST_ATTR(in_illuminance_scale_available, AL3010_SCALE_AVAILABLE);
0062 
0063 static struct attribute *al3010_attributes[] = {
0064     &iio_const_attr_in_illuminance_scale_available.dev_attr.attr,
0065     NULL,
0066 };
0067 
0068 static const struct attribute_group al3010_attribute_group = {
0069     .attrs = al3010_attributes,
0070 };
0071 
0072 static int al3010_set_pwr(struct i2c_client *client, bool pwr)
0073 {
0074     u8 val = pwr ? AL3010_CONFIG_ENABLE : AL3010_CONFIG_DISABLE;
0075     return i2c_smbus_write_byte_data(client, AL3010_REG_SYSTEM, val);
0076 }
0077 
0078 static void al3010_set_pwr_off(void *_data)
0079 {
0080     struct al3010_data *data = _data;
0081 
0082     al3010_set_pwr(data->client, false);
0083 }
0084 
0085 static int al3010_init(struct al3010_data *data)
0086 {
0087     int ret;
0088 
0089     ret = al3010_set_pwr(data->client, true);
0090 
0091     if (ret < 0)
0092         return ret;
0093 
0094     ret = i2c_smbus_write_byte_data(data->client, AL3010_REG_CONFIG,
0095                     FIELD_PREP(AL3010_GAIN_MASK,
0096                            AL3XXX_RANGE_3));
0097     if (ret < 0)
0098         return ret;
0099 
0100     return 0;
0101 }
0102 
0103 static int al3010_read_raw(struct iio_dev *indio_dev,
0104                struct iio_chan_spec const *chan, int *val,
0105                int *val2, long mask)
0106 {
0107     struct al3010_data *data = iio_priv(indio_dev);
0108     int ret;
0109 
0110     switch (mask) {
0111     case IIO_CHAN_INFO_RAW:
0112         /*
0113          * ALS ADC value is stored in two adjacent registers:
0114          * - low byte of output is stored at AL3010_REG_DATA_LOW
0115          * - high byte of output is stored at AL3010_REG_DATA_LOW + 1
0116          */
0117         ret = i2c_smbus_read_word_data(data->client,
0118                            AL3010_REG_DATA_LOW);
0119         if (ret < 0)
0120             return ret;
0121         *val = ret;
0122         return IIO_VAL_INT;
0123     case IIO_CHAN_INFO_SCALE:
0124         ret = i2c_smbus_read_byte_data(data->client,
0125                            AL3010_REG_CONFIG);
0126         if (ret < 0)
0127             return ret;
0128 
0129         ret = FIELD_GET(AL3010_GAIN_MASK, ret);
0130         *val = al3010_scales[ret][0];
0131         *val2 = al3010_scales[ret][1];
0132 
0133         return IIO_VAL_INT_PLUS_MICRO;
0134     }
0135     return -EINVAL;
0136 }
0137 
0138 static int al3010_write_raw(struct iio_dev *indio_dev,
0139                 struct iio_chan_spec const *chan, int val,
0140                 int val2, long mask)
0141 {
0142     struct al3010_data *data = iio_priv(indio_dev);
0143     int i;
0144 
0145     switch (mask) {
0146     case IIO_CHAN_INFO_SCALE:
0147         for (i = 0; i < ARRAY_SIZE(al3010_scales); i++) {
0148             if (val != al3010_scales[i][0] ||
0149                 val2 != al3010_scales[i][1])
0150                 continue;
0151 
0152             return i2c_smbus_write_byte_data(data->client,
0153                     AL3010_REG_CONFIG,
0154                     FIELD_PREP(AL3010_GAIN_MASK, i));
0155         }
0156         break;
0157     }
0158     return -EINVAL;
0159 }
0160 
0161 static const struct iio_info al3010_info = {
0162     .read_raw   = al3010_read_raw,
0163     .write_raw  = al3010_write_raw,
0164     .attrs      = &al3010_attribute_group,
0165 };
0166 
0167 static int al3010_probe(struct i2c_client *client,
0168             const struct i2c_device_id *id)
0169 {
0170     struct al3010_data *data;
0171     struct iio_dev *indio_dev;
0172     int ret;
0173 
0174     indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
0175     if (!indio_dev)
0176         return -ENOMEM;
0177 
0178     data = iio_priv(indio_dev);
0179     i2c_set_clientdata(client, indio_dev);
0180     data->client = client;
0181 
0182     indio_dev->info = &al3010_info;
0183     indio_dev->name = AL3010_DRV_NAME;
0184     indio_dev->channels = al3010_channels;
0185     indio_dev->num_channels = ARRAY_SIZE(al3010_channels);
0186     indio_dev->modes = INDIO_DIRECT_MODE;
0187 
0188     ret = al3010_init(data);
0189     if (ret < 0) {
0190         dev_err(&client->dev, "al3010 chip init failed\n");
0191         return ret;
0192     }
0193 
0194     ret = devm_add_action_or_reset(&client->dev,
0195                     al3010_set_pwr_off,
0196                     data);
0197     if (ret < 0)
0198         return ret;
0199 
0200     return devm_iio_device_register(&client->dev, indio_dev);
0201 }
0202 
0203 static int al3010_suspend(struct device *dev)
0204 {
0205     return al3010_set_pwr(to_i2c_client(dev), false);
0206 }
0207 
0208 static int al3010_resume(struct device *dev)
0209 {
0210     return al3010_set_pwr(to_i2c_client(dev), true);
0211 }
0212 
0213 static DEFINE_SIMPLE_DEV_PM_OPS(al3010_pm_ops, al3010_suspend, al3010_resume);
0214 
0215 static const struct i2c_device_id al3010_id[] = {
0216     {"al3010", },
0217     {}
0218 };
0219 MODULE_DEVICE_TABLE(i2c, al3010_id);
0220 
0221 static const struct of_device_id al3010_of_match[] = {
0222     { .compatible = "dynaimage,al3010", },
0223     {},
0224 };
0225 MODULE_DEVICE_TABLE(of, al3010_of_match);
0226 
0227 static struct i2c_driver al3010_driver = {
0228     .driver = {
0229         .name = AL3010_DRV_NAME,
0230         .of_match_table = al3010_of_match,
0231         .pm = pm_sleep_ptr(&al3010_pm_ops),
0232     },
0233     .probe      = al3010_probe,
0234     .id_table   = al3010_id,
0235 };
0236 module_i2c_driver(al3010_driver);
0237 
0238 MODULE_AUTHOR("Daniel Baluta <daniel.baluta@nxp.com>");
0239 MODULE_AUTHOR("David Heidelberg <david@ixit.cz>");
0240 MODULE_DESCRIPTION("AL3010 Ambient Light Sensor driver");
0241 MODULE_LICENSE("GPL v2");