linux/drivers/iio/light/al3320a.c
<<
>>
Prefs
   1/*
   2 * AL3320A - Dyna Image Ambient Light Sensor
   3 *
   4 * Copyright (c) 2014, Intel Corporation.
   5 *
   6 * This file is subject to the terms and conditions of version 2 of
   7 * the GNU General Public License.  See the file COPYING in the main
   8 * directory of this archive for more details.
   9 *
  10 * IIO driver for AL3320A (7-bit I2C slave address 0x1C).
  11 *
  12 * TODO: interrupt support, thresholds
  13 *
  14 */
  15
  16#include <linux/module.h>
  17#include <linux/init.h>
  18#include <linux/i2c.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_SHIFT              1
  44#define AL3320A_GAIN_MASK               (BIT(2) | BIT(1))
  45
  46/* chip params default values */
  47#define AL3320A_DEFAULT_MEAN_TIME       4
  48#define AL3320A_DEFAULT_WAIT_TIME       0 /* no waiting */
  49
  50#define AL3320A_SCALE_AVAILABLE "0.512 0.128 0.032 0.01"
  51
  52enum al3320a_range {
  53        AL3320A_RANGE_1, /* 33.28 Klx */
  54        AL3320A_RANGE_2, /* 8.32 Klx  */
  55        AL3320A_RANGE_3, /* 2.08 Klx  */
  56        AL3320A_RANGE_4  /* 0.65 Klx  */
  57};
  58
  59static const int al3320a_scales[][2] = {
  60        {0, 512000}, {0, 128000}, {0, 32000}, {0, 10000}
  61};
  62
  63struct al3320a_data {
  64        struct i2c_client *client;
  65};
  66
  67static const struct iio_chan_spec al3320a_channels[] = {
  68        {
  69                .type   = IIO_LIGHT,
  70                .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
  71                                      BIT(IIO_CHAN_INFO_SCALE),
  72        }
  73};
  74
  75static IIO_CONST_ATTR(in_illuminance_scale_available, AL3320A_SCALE_AVAILABLE);
  76
  77static struct attribute *al3320a_attributes[] = {
  78        &iio_const_attr_in_illuminance_scale_available.dev_attr.attr,
  79        NULL,
  80};
  81
  82static const struct attribute_group al3320a_attribute_group = {
  83        .attrs = al3320a_attributes,
  84};
  85
  86static int al3320a_init(struct al3320a_data *data)
  87{
  88        int ret;
  89
  90        /* power on */
  91        ret = i2c_smbus_write_byte_data(data->client, AL3320A_REG_CONFIG,
  92                                        AL3320A_CONFIG_ENABLE);
  93        if (ret < 0)
  94                return ret;
  95
  96        ret = i2c_smbus_write_byte_data(data->client, AL3320A_REG_CONFIG_RANGE,
  97                                        AL3320A_RANGE_3 << AL3320A_GAIN_SHIFT);
  98        if (ret < 0)
  99                return ret;
 100
 101        ret = i2c_smbus_write_byte_data(data->client, AL3320A_REG_MEAN_TIME,
 102                                        AL3320A_DEFAULT_MEAN_TIME);
 103        if (ret < 0)
 104                return ret;
 105
 106        ret = i2c_smbus_write_byte_data(data->client, AL3320A_REG_WAIT,
 107                                        AL3320A_DEFAULT_WAIT_TIME);
 108        if (ret < 0)
 109                return ret;
 110
 111        return 0;
 112}
 113
 114static int al3320a_read_raw(struct iio_dev *indio_dev,
 115                            struct iio_chan_spec const *chan, int *val,
 116                            int *val2, long mask)
 117{
 118        struct al3320a_data *data = iio_priv(indio_dev);
 119        int ret;
 120
 121        switch (mask) {
 122        case IIO_CHAN_INFO_RAW:
 123                /*
 124                 * ALS ADC value is stored in two adjacent registers:
 125                 * - low byte of output is stored at AL3320A_REG_DATA_LOW
 126                 * - high byte of output is stored at AL3320A_REG_DATA_LOW + 1
 127                 */
 128                ret = i2c_smbus_read_word_data(data->client,
 129                                               AL3320A_REG_DATA_LOW);
 130                if (ret < 0)
 131                        return ret;
 132                *val = ret;
 133                return IIO_VAL_INT;
 134        case IIO_CHAN_INFO_SCALE:
 135                ret = i2c_smbus_read_byte_data(data->client,
 136                                               AL3320A_REG_CONFIG_RANGE);
 137                if (ret < 0)
 138                        return ret;
 139
 140                ret = (ret & AL3320A_GAIN_MASK) >> AL3320A_GAIN_SHIFT;
 141                *val = al3320a_scales[ret][0];
 142                *val2 = al3320a_scales[ret][1];
 143
 144                return IIO_VAL_INT_PLUS_MICRO;
 145        }
 146        return -EINVAL;
 147}
 148
 149static int al3320a_write_raw(struct iio_dev *indio_dev,
 150                             struct iio_chan_spec const *chan, int val,
 151                             int val2, long mask)
 152{
 153        struct al3320a_data *data = iio_priv(indio_dev);
 154        int i;
 155
 156        switch (mask) {
 157        case IIO_CHAN_INFO_SCALE:
 158                for (i = 0; i < ARRAY_SIZE(al3320a_scales); i++) {
 159                        if (val == al3320a_scales[i][0] &&
 160                            val2 == al3320a_scales[i][1])
 161                                return i2c_smbus_write_byte_data(data->client,
 162                                        AL3320A_REG_CONFIG_RANGE,
 163                                        i << AL3320A_GAIN_SHIFT);
 164                }
 165                break;
 166        }
 167        return -EINVAL;
 168}
 169
 170static const struct iio_info al3320a_info = {
 171        .driver_module  = THIS_MODULE,
 172        .read_raw       = al3320a_read_raw,
 173        .write_raw      = al3320a_write_raw,
 174        .attrs          = &al3320a_attribute_group,
 175};
 176
 177static int al3320a_probe(struct i2c_client *client,
 178                         const struct i2c_device_id *id)
 179{
 180        struct al3320a_data *data;
 181        struct iio_dev *indio_dev;
 182        int ret;
 183
 184        indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
 185        if (!indio_dev)
 186                return -ENOMEM;
 187
 188        data = iio_priv(indio_dev);
 189        i2c_set_clientdata(client, indio_dev);
 190        data->client = client;
 191
 192        indio_dev->dev.parent = &client->dev;
 193        indio_dev->info = &al3320a_info;
 194        indio_dev->name = AL3320A_DRV_NAME;
 195        indio_dev->channels = al3320a_channels;
 196        indio_dev->num_channels = ARRAY_SIZE(al3320a_channels);
 197        indio_dev->modes = INDIO_DIRECT_MODE;
 198
 199        ret = al3320a_init(data);
 200        if (ret < 0) {
 201                dev_err(&client->dev, "al3320a chip init failed\n");
 202                return ret;
 203        }
 204        return devm_iio_device_register(&client->dev, indio_dev);
 205}
 206
 207static int al3320a_remove(struct i2c_client *client)
 208{
 209        return i2c_smbus_write_byte_data(client, AL3320A_REG_CONFIG,
 210                                         AL3320A_CONFIG_DISABLE);
 211}
 212
 213static const struct i2c_device_id al3320a_id[] = {
 214        {"al3320a", 0},
 215        {}
 216};
 217MODULE_DEVICE_TABLE(i2c, al3320a_id);
 218
 219static struct i2c_driver al3320a_driver = {
 220        .driver = {
 221                .name = AL3320A_DRV_NAME,
 222        },
 223        .probe          = al3320a_probe,
 224        .remove         = al3320a_remove,
 225        .id_table       = al3320a_id,
 226};
 227
 228module_i2c_driver(al3320a_driver);
 229
 230MODULE_AUTHOR("Daniel Baluta <daniel.baluta@intel.com>");
 231MODULE_DESCRIPTION("AL3320A Ambient Light Sensor driver");
 232MODULE_LICENSE("GPL v2");
 233