linux/drivers/iio/light/isl29125.c
<<
>>
Prefs
   1/*
   2 * isl29125.c - Support for Intersil ISL29125 RGB light sensor
   3 *
   4 * Copyright (c) 2014 Peter Meerwald <pmeerw@pmeerw.net>
   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 * RGB light sensor with 16-bit channels for red, green, blue);
  11 * 7-bit I2C slave address 0x44
  12 *
  13 * TODO: interrupt support, IR compensation, thresholds, 12bit
  14 */
  15
  16#include <linux/module.h>
  17#include <linux/i2c.h>
  18#include <linux/delay.h>
  19#include <linux/pm.h>
  20
  21#include <linux/iio/iio.h>
  22#include <linux/iio/sysfs.h>
  23#include <linux/iio/trigger_consumer.h>
  24#include <linux/iio/buffer.h>
  25#include <linux/iio/triggered_buffer.h>
  26
  27#define ISL29125_DRV_NAME "isl29125"
  28
  29#define ISL29125_DEVICE_ID 0x00
  30#define ISL29125_CONF1 0x01
  31#define ISL29125_CONF2 0x02
  32#define ISL29125_CONF3 0x03
  33#define ISL29125_STATUS 0x08
  34#define ISL29125_GREEN_DATA 0x09
  35#define ISL29125_RED_DATA 0x0b
  36#define ISL29125_BLUE_DATA 0x0d
  37
  38#define ISL29125_ID 0x7d
  39
  40#define ISL29125_MODE_MASK GENMASK(2, 0)
  41#define ISL29125_MODE_PD 0x0
  42#define ISL29125_MODE_G 0x1
  43#define ISL29125_MODE_R 0x2
  44#define ISL29125_MODE_B 0x3
  45#define ISL29125_MODE_RGB 0x5
  46
  47#define ISL29125_SENSING_RANGE_0 5722   /* 375 lux full range */
  48#define ISL29125_SENSING_RANGE_1 152590 /* 10k lux full range */
  49
  50#define ISL29125_MODE_RANGE BIT(3)
  51
  52#define ISL29125_STATUS_CONV BIT(1)
  53
  54struct isl29125_data {
  55        struct i2c_client *client;
  56        u8 conf1;
  57        u16 buffer[8]; /* 3x 16-bit, padding, 8 bytes timestamp */
  58};
  59
  60#define ISL29125_CHANNEL(_color, _si) { \
  61        .type = IIO_INTENSITY, \
  62        .modified = 1, \
  63        .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
  64        .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
  65        .channel2 = IIO_MOD_LIGHT_##_color, \
  66        .scan_index = _si, \
  67        .scan_type = { \
  68                .sign = 'u', \
  69                .realbits = 16, \
  70                .storagebits = 16, \
  71                .endianness = IIO_CPU, \
  72        }, \
  73}
  74
  75static const struct iio_chan_spec isl29125_channels[] = {
  76        ISL29125_CHANNEL(GREEN, 0),
  77        ISL29125_CHANNEL(RED, 1),
  78        ISL29125_CHANNEL(BLUE, 2),
  79        IIO_CHAN_SOFT_TIMESTAMP(3),
  80};
  81
  82static const struct {
  83        u8 mode, data;
  84} isl29125_regs[] = {
  85        {ISL29125_MODE_G, ISL29125_GREEN_DATA},
  86        {ISL29125_MODE_R, ISL29125_RED_DATA},
  87        {ISL29125_MODE_B, ISL29125_BLUE_DATA},
  88};
  89
  90static int isl29125_read_data(struct isl29125_data *data, int si)
  91{
  92        int tries = 5;
  93        int ret;
  94
  95        ret = i2c_smbus_write_byte_data(data->client, ISL29125_CONF1,
  96                data->conf1 | isl29125_regs[si].mode);
  97        if (ret < 0)
  98                return ret;
  99
 100        msleep(101);
 101
 102        while (tries--) {
 103                ret = i2c_smbus_read_byte_data(data->client, ISL29125_STATUS);
 104                if (ret < 0)
 105                        goto fail;
 106                if (ret & ISL29125_STATUS_CONV)
 107                        break;
 108                msleep(20);
 109        }
 110
 111        if (tries < 0) {
 112                dev_err(&data->client->dev, "data not ready\n");
 113                ret = -EIO;
 114                goto fail;
 115        }
 116
 117        ret = i2c_smbus_read_word_data(data->client, isl29125_regs[si].data);
 118
 119fail:
 120        i2c_smbus_write_byte_data(data->client, ISL29125_CONF1, data->conf1);
 121        return ret;
 122}
 123
 124static int isl29125_read_raw(struct iio_dev *indio_dev,
 125                           struct iio_chan_spec const *chan,
 126                           int *val, int *val2, long mask)
 127{
 128        struct isl29125_data *data = iio_priv(indio_dev);
 129        int ret;
 130
 131        switch (mask) {
 132        case IIO_CHAN_INFO_RAW:
 133                ret = iio_device_claim_direct_mode(indio_dev);
 134                if (ret)
 135                        return ret;
 136                ret = isl29125_read_data(data, chan->scan_index);
 137                iio_device_release_direct_mode(indio_dev);
 138                if (ret < 0)
 139                        return ret;
 140                *val = ret;
 141                return IIO_VAL_INT;
 142        case IIO_CHAN_INFO_SCALE:
 143                *val = 0;
 144                if (data->conf1 & ISL29125_MODE_RANGE)
 145                        *val2 = ISL29125_SENSING_RANGE_1; /*10k lux full range*/
 146                else
 147                        *val2 = ISL29125_SENSING_RANGE_0; /*375 lux full range*/
 148                return IIO_VAL_INT_PLUS_MICRO;
 149        }
 150        return -EINVAL;
 151}
 152
 153static int isl29125_write_raw(struct iio_dev *indio_dev,
 154                               struct iio_chan_spec const *chan,
 155                               int val, int val2, long mask)
 156{
 157        struct isl29125_data *data = iio_priv(indio_dev);
 158
 159        switch (mask) {
 160        case IIO_CHAN_INFO_SCALE:
 161                if (val != 0)
 162                        return -EINVAL;
 163                if (val2 == ISL29125_SENSING_RANGE_1)
 164                        data->conf1 |= ISL29125_MODE_RANGE;
 165                else if (val2 == ISL29125_SENSING_RANGE_0)
 166                        data->conf1 &= ~ISL29125_MODE_RANGE;
 167                else
 168                        return -EINVAL;
 169                return i2c_smbus_write_byte_data(data->client, ISL29125_CONF1,
 170                        data->conf1);
 171        default:
 172                return -EINVAL;
 173        }
 174}
 175
 176static irqreturn_t isl29125_trigger_handler(int irq, void *p)
 177{
 178        struct iio_poll_func *pf = p;
 179        struct iio_dev *indio_dev = pf->indio_dev;
 180        struct isl29125_data *data = iio_priv(indio_dev);
 181        int i, j = 0;
 182
 183        for_each_set_bit(i, indio_dev->active_scan_mask,
 184                indio_dev->masklength) {
 185                int ret = i2c_smbus_read_word_data(data->client,
 186                        isl29125_regs[i].data);
 187                if (ret < 0)
 188                        goto done;
 189
 190                data->buffer[j++] = ret;
 191        }
 192
 193        iio_push_to_buffers_with_timestamp(indio_dev, data->buffer,
 194                iio_get_time_ns(indio_dev));
 195
 196done:
 197        iio_trigger_notify_done(indio_dev->trig);
 198
 199        return IRQ_HANDLED;
 200}
 201
 202static IIO_CONST_ATTR(scale_available, "0.005722 0.152590");
 203
 204static struct attribute *isl29125_attributes[] = {
 205        &iio_const_attr_scale_available.dev_attr.attr,
 206        NULL
 207};
 208
 209static const struct attribute_group isl29125_attribute_group = {
 210        .attrs = isl29125_attributes,
 211};
 212
 213static const struct iio_info isl29125_info = {
 214        .read_raw = isl29125_read_raw,
 215        .write_raw = isl29125_write_raw,
 216        .attrs = &isl29125_attribute_group,
 217        .driver_module = THIS_MODULE,
 218};
 219
 220static int isl29125_buffer_preenable(struct iio_dev *indio_dev)
 221{
 222        struct isl29125_data *data = iio_priv(indio_dev);
 223
 224        data->conf1 |= ISL29125_MODE_RGB;
 225        return i2c_smbus_write_byte_data(data->client, ISL29125_CONF1,
 226                data->conf1);
 227}
 228
 229static int isl29125_buffer_predisable(struct iio_dev *indio_dev)
 230{
 231        struct isl29125_data *data = iio_priv(indio_dev);
 232        int ret;
 233
 234        ret = iio_triggered_buffer_predisable(indio_dev);
 235        if (ret < 0)
 236                return ret;
 237
 238        data->conf1 &= ~ISL29125_MODE_MASK;
 239        data->conf1 |= ISL29125_MODE_PD;
 240        return i2c_smbus_write_byte_data(data->client, ISL29125_CONF1,
 241                data->conf1);
 242}
 243
 244static const struct iio_buffer_setup_ops isl29125_buffer_setup_ops = {
 245        .preenable = isl29125_buffer_preenable,
 246        .postenable = &iio_triggered_buffer_postenable,
 247        .predisable = isl29125_buffer_predisable,
 248};
 249
 250static int isl29125_probe(struct i2c_client *client,
 251                           const struct i2c_device_id *id)
 252{
 253        struct isl29125_data *data;
 254        struct iio_dev *indio_dev;
 255        int ret;
 256
 257        indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
 258        if (indio_dev == NULL)
 259                return -ENOMEM;
 260
 261        data = iio_priv(indio_dev);
 262        i2c_set_clientdata(client, indio_dev);
 263        data->client = client;
 264
 265        indio_dev->dev.parent = &client->dev;
 266        indio_dev->info = &isl29125_info;
 267        indio_dev->name = ISL29125_DRV_NAME;
 268        indio_dev->channels = isl29125_channels;
 269        indio_dev->num_channels = ARRAY_SIZE(isl29125_channels);
 270        indio_dev->modes = INDIO_DIRECT_MODE;
 271
 272        ret = i2c_smbus_read_byte_data(data->client, ISL29125_DEVICE_ID);
 273        if (ret < 0)
 274                return ret;
 275        if (ret != ISL29125_ID)
 276                return -ENODEV;
 277
 278        data->conf1 = ISL29125_MODE_PD | ISL29125_MODE_RANGE;
 279        ret = i2c_smbus_write_byte_data(data->client, ISL29125_CONF1,
 280                data->conf1);
 281        if (ret < 0)
 282                return ret;
 283
 284        ret = i2c_smbus_write_byte_data(data->client, ISL29125_STATUS, 0);
 285        if (ret < 0)
 286                return ret;
 287
 288        ret = iio_triggered_buffer_setup(indio_dev, NULL,
 289                isl29125_trigger_handler, &isl29125_buffer_setup_ops);
 290        if (ret < 0)
 291                return ret;
 292
 293        ret = iio_device_register(indio_dev);
 294        if (ret < 0)
 295                goto buffer_cleanup;
 296
 297        return 0;
 298
 299buffer_cleanup:
 300        iio_triggered_buffer_cleanup(indio_dev);
 301        return ret;
 302}
 303
 304static int isl29125_powerdown(struct isl29125_data *data)
 305{
 306        return i2c_smbus_write_byte_data(data->client, ISL29125_CONF1,
 307                (data->conf1 & ~ISL29125_MODE_MASK) | ISL29125_MODE_PD);
 308}
 309
 310static int isl29125_remove(struct i2c_client *client)
 311{
 312        struct iio_dev *indio_dev = i2c_get_clientdata(client);
 313
 314        iio_device_unregister(indio_dev);
 315        iio_triggered_buffer_cleanup(indio_dev);
 316        isl29125_powerdown(iio_priv(indio_dev));
 317
 318        return 0;
 319}
 320
 321#ifdef CONFIG_PM_SLEEP
 322static int isl29125_suspend(struct device *dev)
 323{
 324        struct isl29125_data *data = iio_priv(i2c_get_clientdata(
 325                to_i2c_client(dev)));
 326        return isl29125_powerdown(data);
 327}
 328
 329static int isl29125_resume(struct device *dev)
 330{
 331        struct isl29125_data *data = iio_priv(i2c_get_clientdata(
 332                to_i2c_client(dev)));
 333        return i2c_smbus_write_byte_data(data->client, ISL29125_CONF1,
 334                data->conf1);
 335}
 336#endif
 337
 338static SIMPLE_DEV_PM_OPS(isl29125_pm_ops, isl29125_suspend, isl29125_resume);
 339
 340static const struct i2c_device_id isl29125_id[] = {
 341        { "isl29125", 0 },
 342        { }
 343};
 344MODULE_DEVICE_TABLE(i2c, isl29125_id);
 345
 346static struct i2c_driver isl29125_driver = {
 347        .driver = {
 348                .name   = ISL29125_DRV_NAME,
 349                .pm     = &isl29125_pm_ops,
 350        },
 351        .probe          = isl29125_probe,
 352        .remove         = isl29125_remove,
 353        .id_table       = isl29125_id,
 354};
 355module_i2c_driver(isl29125_driver);
 356
 357MODULE_AUTHOR("Peter Meerwald <pmeerw@pmeerw.net>");
 358MODULE_DESCRIPTION("ISL29125 RGB light sensor driver");
 359MODULE_LICENSE("GPL");
 360