Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * AL3320A - Dyna Image Ambient Light Sensor
0004  *
0005  * Copyright (c) 2014, Intel Corporation.
0006  *
0007  * IIO driver for AL3320A (7-bit I2C slave address 0x1C).
0008  *
0009  * TODO: interrupt support, thresholds
0010  * When the driver will get support for interrupt handling, then interrupt
0011  * will need to be disabled before turning sensor OFF in order to avoid
0012  * potential races with the interrupt handling.
0013  */
0014 
0015 #include <linux/bitfield.h>
0016 #include <linux/i2c.h>
0017 #include <linux/module.h>
0018 #include <linux/of.h>
0019 
0020 #include <linux/iio/iio.h>
0021 #include <linux/iio/sysfs.h>
0022 
0023 #define AL3320A_DRV_NAME "al3320a"
0024 
0025 #define AL3320A_REG_CONFIG      0x00
0026 #define AL3320A_REG_STATUS      0x01
0027 #define AL3320A_REG_INT         0x02
0028 #define AL3320A_REG_WAIT        0x06
0029 #define AL3320A_REG_CONFIG_RANGE    0x07
0030 #define AL3320A_REG_PERSIST     0x08
0031 #define AL3320A_REG_MEAN_TIME       0x09
0032 #define AL3320A_REG_ADUMMY      0x0A
0033 #define AL3320A_REG_DATA_LOW        0x22
0034 
0035 #define AL3320A_REG_LOW_THRESH_LOW  0x30
0036 #define AL3320A_REG_LOW_THRESH_HIGH 0x31
0037 #define AL3320A_REG_HIGH_THRESH_LOW 0x32
0038 #define AL3320A_REG_HIGH_THRESH_HIGH    0x33
0039 
0040 #define AL3320A_CONFIG_DISABLE      0x00
0041 #define AL3320A_CONFIG_ENABLE       0x01
0042 
0043 #define AL3320A_GAIN_MASK       GENMASK(2, 1)
0044 
0045 /* chip params default values */
0046 #define AL3320A_DEFAULT_MEAN_TIME   4
0047 #define AL3320A_DEFAULT_WAIT_TIME   0 /* no waiting */
0048 
0049 #define AL3320A_SCALE_AVAILABLE "0.512 0.128 0.032 0.01"
0050 
0051 enum al3320a_range {
0052     AL3320A_RANGE_1, /* 33.28 Klx */
0053     AL3320A_RANGE_2, /* 8.32 Klx  */
0054     AL3320A_RANGE_3, /* 2.08 Klx  */
0055     AL3320A_RANGE_4  /* 0.65 Klx  */
0056 };
0057 
0058 static const int al3320a_scales[][2] = {
0059     {0, 512000}, {0, 128000}, {0, 32000}, {0, 10000}
0060 };
0061 
0062 struct al3320a_data {
0063     struct i2c_client *client;
0064 };
0065 
0066 static const struct iio_chan_spec al3320a_channels[] = {
0067     {
0068         .type   = IIO_LIGHT,
0069         .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
0070                       BIT(IIO_CHAN_INFO_SCALE),
0071     }
0072 };
0073 
0074 static IIO_CONST_ATTR(in_illuminance_scale_available, AL3320A_SCALE_AVAILABLE);
0075 
0076 static struct attribute *al3320a_attributes[] = {
0077     &iio_const_attr_in_illuminance_scale_available.dev_attr.attr,
0078     NULL,
0079 };
0080 
0081 static const struct attribute_group al3320a_attribute_group = {
0082     .attrs = al3320a_attributes,
0083 };
0084 
0085 static int al3320a_set_pwr(struct i2c_client *client, bool pwr)
0086 {
0087     u8 val = pwr ? AL3320A_CONFIG_ENABLE : AL3320A_CONFIG_DISABLE;
0088     return i2c_smbus_write_byte_data(client, AL3320A_REG_CONFIG, val);
0089 }
0090 
0091 static void al3320a_set_pwr_off(void *_data)
0092 {
0093     struct al3320a_data *data = _data;
0094 
0095     al3320a_set_pwr(data->client, false);
0096 }
0097 
0098 static int al3320a_init(struct al3320a_data *data)
0099 {
0100     int ret;
0101 
0102     ret = al3320a_set_pwr(data->client, true);
0103 
0104     if (ret < 0)
0105         return ret;
0106 
0107     ret = i2c_smbus_write_byte_data(data->client, AL3320A_REG_CONFIG_RANGE,
0108                     FIELD_PREP(AL3320A_GAIN_MASK,
0109                            AL3320A_RANGE_3));
0110     if (ret < 0)
0111         return ret;
0112 
0113     ret = i2c_smbus_write_byte_data(data->client, AL3320A_REG_MEAN_TIME,
0114                     AL3320A_DEFAULT_MEAN_TIME);
0115     if (ret < 0)
0116         return ret;
0117 
0118     ret = i2c_smbus_write_byte_data(data->client, AL3320A_REG_WAIT,
0119                     AL3320A_DEFAULT_WAIT_TIME);
0120     if (ret < 0)
0121         return ret;
0122 
0123     return 0;
0124 }
0125 
0126 static int al3320a_read_raw(struct iio_dev *indio_dev,
0127                 struct iio_chan_spec const *chan, int *val,
0128                 int *val2, long mask)
0129 {
0130     struct al3320a_data *data = iio_priv(indio_dev);
0131     int ret;
0132 
0133     switch (mask) {
0134     case IIO_CHAN_INFO_RAW:
0135         /*
0136          * ALS ADC value is stored in two adjacent registers:
0137          * - low byte of output is stored at AL3320A_REG_DATA_LOW
0138          * - high byte of output is stored at AL3320A_REG_DATA_LOW + 1
0139          */
0140         ret = i2c_smbus_read_word_data(data->client,
0141                            AL3320A_REG_DATA_LOW);
0142         if (ret < 0)
0143             return ret;
0144         *val = ret;
0145         return IIO_VAL_INT;
0146     case IIO_CHAN_INFO_SCALE:
0147         ret = i2c_smbus_read_byte_data(data->client,
0148                            AL3320A_REG_CONFIG_RANGE);
0149         if (ret < 0)
0150             return ret;
0151 
0152         ret = FIELD_GET(AL3320A_GAIN_MASK, ret);
0153         *val = al3320a_scales[ret][0];
0154         *val2 = al3320a_scales[ret][1];
0155 
0156         return IIO_VAL_INT_PLUS_MICRO;
0157     }
0158     return -EINVAL;
0159 }
0160 
0161 static int al3320a_write_raw(struct iio_dev *indio_dev,
0162                  struct iio_chan_spec const *chan, int val,
0163                  int val2, long mask)
0164 {
0165     struct al3320a_data *data = iio_priv(indio_dev);
0166     int i;
0167 
0168     switch (mask) {
0169     case IIO_CHAN_INFO_SCALE:
0170         for (i = 0; i < ARRAY_SIZE(al3320a_scales); i++) {
0171             if (val != al3320a_scales[i][0] ||
0172                 val2 != al3320a_scales[i][1])
0173                 continue;
0174 
0175             return i2c_smbus_write_byte_data(data->client,
0176                     AL3320A_REG_CONFIG_RANGE,
0177                     FIELD_PREP(AL3320A_GAIN_MASK, i));
0178         }
0179         break;
0180     }
0181     return -EINVAL;
0182 }
0183 
0184 static const struct iio_info al3320a_info = {
0185     .read_raw   = al3320a_read_raw,
0186     .write_raw  = al3320a_write_raw,
0187     .attrs      = &al3320a_attribute_group,
0188 };
0189 
0190 static int al3320a_probe(struct i2c_client *client,
0191              const struct i2c_device_id *id)
0192 {
0193     struct al3320a_data *data;
0194     struct iio_dev *indio_dev;
0195     int ret;
0196 
0197     indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
0198     if (!indio_dev)
0199         return -ENOMEM;
0200 
0201     data = iio_priv(indio_dev);
0202     i2c_set_clientdata(client, indio_dev);
0203     data->client = client;
0204 
0205     indio_dev->info = &al3320a_info;
0206     indio_dev->name = AL3320A_DRV_NAME;
0207     indio_dev->channels = al3320a_channels;
0208     indio_dev->num_channels = ARRAY_SIZE(al3320a_channels);
0209     indio_dev->modes = INDIO_DIRECT_MODE;
0210 
0211     ret = al3320a_init(data);
0212     if (ret < 0) {
0213         dev_err(&client->dev, "al3320a chip init failed\n");
0214         return ret;
0215     }
0216 
0217     ret = devm_add_action_or_reset(&client->dev,
0218                     al3320a_set_pwr_off,
0219                     data);
0220     if (ret < 0)
0221         return ret;
0222 
0223     return devm_iio_device_register(&client->dev, indio_dev);
0224 }
0225 
0226 static int al3320a_suspend(struct device *dev)
0227 {
0228     return al3320a_set_pwr(to_i2c_client(dev), false);
0229 }
0230 
0231 static int al3320a_resume(struct device *dev)
0232 {
0233     return al3320a_set_pwr(to_i2c_client(dev), true);
0234 }
0235 
0236 static DEFINE_SIMPLE_DEV_PM_OPS(al3320a_pm_ops, al3320a_suspend,
0237                 al3320a_resume);
0238 
0239 static const struct i2c_device_id al3320a_id[] = {
0240     {"al3320a", 0},
0241     {}
0242 };
0243 MODULE_DEVICE_TABLE(i2c, al3320a_id);
0244 
0245 static const struct of_device_id al3320a_of_match[] = {
0246     { .compatible = "dynaimage,al3320a", },
0247     {},
0248 };
0249 MODULE_DEVICE_TABLE(of, al3320a_of_match);
0250 
0251 static struct i2c_driver al3320a_driver = {
0252     .driver = {
0253         .name = AL3320A_DRV_NAME,
0254         .of_match_table = al3320a_of_match,
0255         .pm = pm_sleep_ptr(&al3320a_pm_ops),
0256     },
0257     .probe      = al3320a_probe,
0258     .id_table   = al3320a_id,
0259 };
0260 
0261 module_i2c_driver(al3320a_driver);
0262 
0263 MODULE_AUTHOR("Daniel Baluta <daniel.baluta@intel.com>");
0264 MODULE_DESCRIPTION("AL3320A Ambient Light Sensor driver");
0265 MODULE_LICENSE("GPL v2");