linux/drivers/iio/light/al3320a.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * AL3320A - Dyna Image Ambient Light Sensor
   4 *
   5 * Copyright (c) 2014, Intel Corporation.
   6 *
   7 * IIO driver for AL3320A (7-bit I2C slave address 0x1C).
   8 *
   9 * TODO: interrupt support, thresholds
  10 * When the driver will get support for interrupt handling, then interrupt
  11 * will need to be disabled before turning sensor OFF in order to avoid
  12 * potential races with the interrupt handling.
  13 */
  14
  15#include <linux/bitfield.h>
  16#include <linux/i2c.h>
  17#include <linux/module.h>
  18#include <linux/of.h>
  19
  20#include <linux/iio/iio.h>
  21#include <linux/iio/sysfs.h>
  22
  23#define AL3320A_DRV_NAME "al3320a"
  24
  25#define AL3320A_REG_CONFIG              0x00
  26#define AL3320A_REG_STATUS              0x01
  27#define AL3320A_REG_INT                 0x02
  28#define AL3320A_REG_WAIT                0x06
  29#define AL3320A_REG_CONFIG_RANGE        0x07
  30#define AL3320A_REG_PERSIST             0x08
  31#define AL3320A_REG_MEAN_TIME           0x09
  32#define AL3320A_REG_ADUMMY              0x0A
  33#define AL3320A_REG_DATA_LOW            0x22
  34
  35#define AL3320A_REG_LOW_THRESH_LOW      0x30
  36#define AL3320A_REG_LOW_THRESH_HIGH     0x31
  37#define AL3320A_REG_HIGH_THRESH_LOW     0x32
  38#define AL3320A_REG_HIGH_THRESH_HIGH    0x33
  39
  40#define AL3320A_CONFIG_DISABLE          0x00
  41#define AL3320A_CONFIG_ENABLE           0x01
  42
  43#define AL3320A_GAIN_MASK               GENMASK(2, 1)
  44
  45/* chip params default values */
  46#define AL3320A_DEFAULT_MEAN_TIME       4
  47#define AL3320A_DEFAULT_WAIT_TIME       0 /* no waiting */
  48
  49#define AL3320A_SCALE_AVAILABLE "0.512 0.128 0.032 0.01"
  50
  51enum al3320a_range {
  52        AL3320A_RANGE_1, /* 33.28 Klx */
  53        AL3320A_RANGE_2, /* 8.32 Klx  */
  54        AL3320A_RANGE_3, /* 2.08 Klx  */
  55        AL3320A_RANGE_4  /* 0.65 Klx  */
  56};
  57
  58static const int al3320a_scales[][2] = {
  59        {0, 512000}, {0, 128000}, {0, 32000}, {0, 10000}
  60};
  61
  62struct al3320a_data {
  63        struct i2c_client *client;
  64};
  65
  66static const struct iio_chan_spec al3320a_channels[] = {
  67        {
  68                .type   = IIO_LIGHT,
  69                .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
  70                                      BIT(IIO_CHAN_INFO_SCALE),
  71        }
  72};
  73
  74static IIO_CONST_ATTR(in_illuminance_scale_available, AL3320A_SCALE_AVAILABLE);
  75
  76static struct attribute *al3320a_attributes[] = {
  77        &iio_const_attr_in_illuminance_scale_available.dev_attr.attr,
  78        NULL,
  79};
  80
  81static const struct attribute_group al3320a_attribute_group = {
  82        .attrs = al3320a_attributes,
  83};
  84
  85static int al3320a_set_pwr(struct i2c_client *client, bool pwr)
  86{
  87        u8 val = pwr ? AL3320A_CONFIG_ENABLE : AL3320A_CONFIG_DISABLE;
  88        return i2c_smbus_write_byte_data(client, AL3320A_REG_CONFIG, val);
  89}
  90
  91static void al3320a_set_pwr_off(void *_data)
  92{
  93        struct al3320a_data *data = _data;
  94
  95        al3320a_set_pwr(data->client, false);
  96}
  97
  98static int al3320a_init(struct al3320a_data *data)
  99{
 100        int ret;
 101
 102        ret = al3320a_set_pwr(data->client, true);
 103
 104        if (ret < 0)
 105                return ret;
 106
 107        ret = i2c_smbus_write_byte_data(data->client, AL3320A_REG_CONFIG_RANGE,
 108                                        FIELD_PREP(AL3320A_GAIN_MASK,
 109                                                   AL3320A_RANGE_3));
 110        if (ret < 0)
 111                return ret;
 112
 113        ret = i2c_smbus_write_byte_data(data->client, AL3320A_REG_MEAN_TIME,
 114                                        AL3320A_DEFAULT_MEAN_TIME);
 115        if (ret < 0)
 116                return ret;
 117
 118        ret = i2c_smbus_write_byte_data(data->client, AL3320A_REG_WAIT,
 119                                        AL3320A_DEFAULT_WAIT_TIME);
 120        if (ret < 0)
 121                return ret;
 122
 123        return 0;
 124}
 125
 126static int al3320a_read_raw(struct iio_dev *indio_dev,
 127                            struct iio_chan_spec const *chan, int *val,
 128                            int *val2, long mask)
 129{
 130        struct al3320a_data *data = iio_priv(indio_dev);
 131        int ret;
 132
 133        switch (mask) {
 134        case IIO_CHAN_INFO_RAW:
 135                /*
 136                 * ALS ADC value is stored in two adjacent registers:
 137                 * - low byte of output is stored at AL3320A_REG_DATA_LOW
 138                 * - high byte of output is stored at AL3320A_REG_DATA_LOW + 1
 139                 */
 140                ret = i2c_smbus_read_word_data(data->client,
 141                                               AL3320A_REG_DATA_LOW);
 142                if (ret < 0)
 143                        return ret;
 144                *val = ret;
 145                return IIO_VAL_INT;
 146        case IIO_CHAN_INFO_SCALE:
 147                ret = i2c_smbus_read_byte_data(data->client,
 148                                               AL3320A_REG_CONFIG_RANGE);
 149                if (ret < 0)
 150                        return ret;
 151
 152                ret = FIELD_GET(AL3320A_GAIN_MASK, ret);
 153                *val = al3320a_scales[ret][0];
 154                *val2 = al3320a_scales[ret][1];
 155
 156                return IIO_VAL_INT_PLUS_MICRO;
 157        }
 158        return -EINVAL;
 159}
 160
 161static int al3320a_write_raw(struct iio_dev *indio_dev,
 162                             struct iio_chan_spec const *chan, int val,
 163                             int val2, long mask)
 164{
 165        struct al3320a_data *data = iio_priv(indio_dev);
 166        int i;
 167
 168        switch (mask) {
 169        case IIO_CHAN_INFO_SCALE:
 170                for (i = 0; i < ARRAY_SIZE(al3320a_scales); i++) {
 171                        if (val != al3320a_scales[i][0] ||
 172                            val2 != al3320a_scales[i][1])
 173                                continue;
 174
 175                        return i2c_smbus_write_byte_data(data->client,
 176                                        AL3320A_REG_CONFIG_RANGE,
 177                                        FIELD_PREP(AL3320A_GAIN_MASK, i));
 178                }
 179                break;
 180        }
 181        return -EINVAL;
 182}
 183
 184static const struct iio_info al3320a_info = {
 185        .read_raw       = al3320a_read_raw,
 186        .write_raw      = al3320a_write_raw,
 187        .attrs          = &al3320a_attribute_group,
 188};
 189
 190static int al3320a_probe(struct i2c_client *client,
 191                         const struct i2c_device_id *id)
 192{
 193        struct al3320a_data *data;
 194        struct iio_dev *indio_dev;
 195        int ret;
 196
 197        indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
 198        if (!indio_dev)
 199                return -ENOMEM;
 200
 201        data = iio_priv(indio_dev);
 202        i2c_set_clientdata(client, indio_dev);
 203        data->client = client;
 204
 205        indio_dev->info = &al3320a_info;
 206        indio_dev->name = AL3320A_DRV_NAME;
 207        indio_dev->channels = al3320a_channels;
 208        indio_dev->num_channels = ARRAY_SIZE(al3320a_channels);
 209        indio_dev->modes = INDIO_DIRECT_MODE;
 210
 211        ret = al3320a_init(data);
 212        if (ret < 0) {
 213                dev_err(&client->dev, "al3320a chip init failed\n");
 214                return ret;
 215        }
 216
 217        ret = devm_add_action_or_reset(&client->dev,
 218                                        al3320a_set_pwr_off,
 219                                        data);
 220        if (ret < 0)
 221                return ret;
 222
 223        return devm_iio_device_register(&client->dev, indio_dev);
 224}
 225
 226static int __maybe_unused al3320a_suspend(struct device *dev)
 227{
 228        return al3320a_set_pwr(to_i2c_client(dev), false);
 229}
 230
 231static int __maybe_unused al3320a_resume(struct device *dev)
 232{
 233        return al3320a_set_pwr(to_i2c_client(dev), true);
 234}
 235
 236static SIMPLE_DEV_PM_OPS(al3320a_pm_ops, al3320a_suspend, al3320a_resume);
 237
 238static const struct i2c_device_id al3320a_id[] = {
 239        {"al3320a", 0},
 240        {}
 241};
 242MODULE_DEVICE_TABLE(i2c, al3320a_id);
 243
 244static const struct of_device_id al3320a_of_match[] = {
 245        { .compatible = "dynaimage,al3320a", },
 246        {},
 247};
 248MODULE_DEVICE_TABLE(of, al3320a_of_match);
 249
 250static struct i2c_driver al3320a_driver = {
 251        .driver = {
 252                .name = AL3320A_DRV_NAME,
 253                .of_match_table = al3320a_of_match,
 254                .pm = &al3320a_pm_ops,
 255        },
 256        .probe          = al3320a_probe,
 257        .id_table       = al3320a_id,
 258};
 259
 260module_i2c_driver(al3320a_driver);
 261
 262MODULE_AUTHOR("Daniel Baluta <daniel.baluta@intel.com>");
 263MODULE_DESCRIPTION("AL3320A Ambient Light Sensor driver");
 264MODULE_LICENSE("GPL v2");
 265