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 */
  11
  12#include <linux/module.h>
  13#include <linux/init.h>
  14#include <linux/i2c.h>
  15
  16#include <linux/iio/iio.h>
  17#include <linux/iio/sysfs.h>
  18
  19#define AL3320A_DRV_NAME "al3320a"
  20
  21#define AL3320A_REG_CONFIG              0x00
  22#define AL3320A_REG_STATUS              0x01
  23#define AL3320A_REG_INT                 0x02
  24#define AL3320A_REG_WAIT                0x06
  25#define AL3320A_REG_CONFIG_RANGE        0x07
  26#define AL3320A_REG_PERSIST             0x08
  27#define AL3320A_REG_MEAN_TIME           0x09
  28#define AL3320A_REG_ADUMMY              0x0A
  29#define AL3320A_REG_DATA_LOW            0x22
  30
  31#define AL3320A_REG_LOW_THRESH_LOW      0x30
  32#define AL3320A_REG_LOW_THRESH_HIGH     0x31
  33#define AL3320A_REG_HIGH_THRESH_LOW     0x32
  34#define AL3320A_REG_HIGH_THRESH_HIGH    0x33
  35
  36#define AL3320A_CONFIG_DISABLE          0x00
  37#define AL3320A_CONFIG_ENABLE           0x01
  38
  39#define AL3320A_GAIN_SHIFT              1
  40#define AL3320A_GAIN_MASK               (BIT(2) | BIT(1))
  41
  42/* chip params default values */
  43#define AL3320A_DEFAULT_MEAN_TIME       4
  44#define AL3320A_DEFAULT_WAIT_TIME       0 /* no waiting */
  45
  46#define AL3320A_SCALE_AVAILABLE "0.512 0.128 0.032 0.01"
  47
  48enum al3320a_range {
  49        AL3320A_RANGE_1, /* 33.28 Klx */
  50        AL3320A_RANGE_2, /* 8.32 Klx  */
  51        AL3320A_RANGE_3, /* 2.08 Klx  */
  52        AL3320A_RANGE_4  /* 0.65 Klx  */
  53};
  54
  55static const int al3320a_scales[][2] = {
  56        {0, 512000}, {0, 128000}, {0, 32000}, {0, 10000}
  57};
  58
  59struct al3320a_data {
  60        struct i2c_client *client;
  61};
  62
  63static const struct iio_chan_spec al3320a_channels[] = {
  64        {
  65                .type   = IIO_LIGHT,
  66                .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
  67                                      BIT(IIO_CHAN_INFO_SCALE),
  68        }
  69};
  70
  71static IIO_CONST_ATTR(in_illuminance_scale_available, AL3320A_SCALE_AVAILABLE);
  72
  73static struct attribute *al3320a_attributes[] = {
  74        &iio_const_attr_in_illuminance_scale_available.dev_attr.attr,
  75        NULL,
  76};
  77
  78static const struct attribute_group al3320a_attribute_group = {
  79        .attrs = al3320a_attributes,
  80};
  81
  82static int al3320a_init(struct al3320a_data *data)
  83{
  84        int ret;
  85
  86        /* power on */
  87        ret = i2c_smbus_write_byte_data(data->client, AL3320A_REG_CONFIG,
  88                                        AL3320A_CONFIG_ENABLE);
  89        if (ret < 0)
  90                return ret;
  91
  92        ret = i2c_smbus_write_byte_data(data->client, AL3320A_REG_CONFIG_RANGE,
  93                                        AL3320A_RANGE_3 << AL3320A_GAIN_SHIFT);
  94        if (ret < 0)
  95                return ret;
  96
  97        ret = i2c_smbus_write_byte_data(data->client, AL3320A_REG_MEAN_TIME,
  98                                        AL3320A_DEFAULT_MEAN_TIME);
  99        if (ret < 0)
 100                return ret;
 101
 102        ret = i2c_smbus_write_byte_data(data->client, AL3320A_REG_WAIT,
 103                                        AL3320A_DEFAULT_WAIT_TIME);
 104        if (ret < 0)
 105                return ret;
 106
 107        return 0;
 108}
 109
 110static int al3320a_read_raw(struct iio_dev *indio_dev,
 111                            struct iio_chan_spec const *chan, int *val,
 112                            int *val2, long mask)
 113{
 114        struct al3320a_data *data = iio_priv(indio_dev);
 115        int ret;
 116
 117        switch (mask) {
 118        case IIO_CHAN_INFO_RAW:
 119                /*
 120                 * ALS ADC value is stored in two adjacent registers:
 121                 * - low byte of output is stored at AL3320A_REG_DATA_LOW
 122                 * - high byte of output is stored at AL3320A_REG_DATA_LOW + 1
 123                 */
 124                ret = i2c_smbus_read_word_data(data->client,
 125                                               AL3320A_REG_DATA_LOW);
 126                if (ret < 0)
 127                        return ret;
 128                *val = ret;
 129                return IIO_VAL_INT;
 130        case IIO_CHAN_INFO_SCALE:
 131                ret = i2c_smbus_read_byte_data(data->client,
 132                                               AL3320A_REG_CONFIG_RANGE);
 133                if (ret < 0)
 134                        return ret;
 135
 136                ret = (ret & AL3320A_GAIN_MASK) >> AL3320A_GAIN_SHIFT;
 137                *val = al3320a_scales[ret][0];
 138                *val2 = al3320a_scales[ret][1];
 139
 140                return IIO_VAL_INT_PLUS_MICRO;
 141        }
 142        return -EINVAL;
 143}
 144
 145static int al3320a_write_raw(struct iio_dev *indio_dev,
 146                             struct iio_chan_spec const *chan, int val,
 147                             int val2, long mask)
 148{
 149        struct al3320a_data *data = iio_priv(indio_dev);
 150        int i;
 151
 152        switch (mask) {
 153        case IIO_CHAN_INFO_SCALE:
 154                for (i = 0; i < ARRAY_SIZE(al3320a_scales); i++) {
 155                        if (val == al3320a_scales[i][0] &&
 156                            val2 == al3320a_scales[i][1])
 157                                return i2c_smbus_write_byte_data(data->client,
 158                                        AL3320A_REG_CONFIG_RANGE,
 159                                        i << AL3320A_GAIN_SHIFT);
 160                }
 161                break;
 162        }
 163        return -EINVAL;
 164}
 165
 166static const struct iio_info al3320a_info = {
 167        .read_raw       = al3320a_read_raw,
 168        .write_raw      = al3320a_write_raw,
 169        .attrs          = &al3320a_attribute_group,
 170};
 171
 172static int al3320a_probe(struct i2c_client *client,
 173                         const struct i2c_device_id *id)
 174{
 175        struct al3320a_data *data;
 176        struct iio_dev *indio_dev;
 177        int ret;
 178
 179        indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
 180        if (!indio_dev)
 181                return -ENOMEM;
 182
 183        data = iio_priv(indio_dev);
 184        i2c_set_clientdata(client, indio_dev);
 185        data->client = client;
 186
 187        indio_dev->dev.parent = &client->dev;
 188        indio_dev->info = &al3320a_info;
 189        indio_dev->name = AL3320A_DRV_NAME;
 190        indio_dev->channels = al3320a_channels;
 191        indio_dev->num_channels = ARRAY_SIZE(al3320a_channels);
 192        indio_dev->modes = INDIO_DIRECT_MODE;
 193
 194        ret = al3320a_init(data);
 195        if (ret < 0) {
 196                dev_err(&client->dev, "al3320a chip init failed\n");
 197                return ret;
 198        }
 199        return devm_iio_device_register(&client->dev, indio_dev);
 200}
 201
 202static int al3320a_remove(struct i2c_client *client)
 203{
 204        return i2c_smbus_write_byte_data(client, AL3320A_REG_CONFIG,
 205                                         AL3320A_CONFIG_DISABLE);
 206}
 207
 208static const struct i2c_device_id al3320a_id[] = {
 209        {"al3320a", 0},
 210        {}
 211};
 212MODULE_DEVICE_TABLE(i2c, al3320a_id);
 213
 214static struct i2c_driver al3320a_driver = {
 215        .driver = {
 216                .name = AL3320A_DRV_NAME,
 217        },
 218        .probe          = al3320a_probe,
 219        .remove         = al3320a_remove,
 220        .id_table       = al3320a_id,
 221};
 222
 223module_i2c_driver(al3320a_driver);
 224
 225MODULE_AUTHOR("Daniel Baluta <daniel.baluta@intel.com>");
 226MODULE_DESCRIPTION("AL3320A Ambient Light Sensor driver");
 227MODULE_LICENSE("GPL v2");
 228