linux/drivers/misc/isl29003.c
<<
>>
Prefs
   1/*
   2 *  isl29003.c - Linux kernel module for
   3 *      Intersil ISL29003 ambient light sensor
   4 *
   5 *  See file:Documentation/misc-devices/isl29003
   6 *
   7 *  Copyright (c) 2009 Daniel Mack <daniel@caiaq.de>
   8 *
   9 *  Based on code written by
  10 *      Rodolfo Giometti <giometti@linux.it>
  11 *      Eurotech S.p.A. <info@eurotech.it>
  12 *
  13 *  This program is free software; you can redistribute it and/or modify
  14 *  it under the terms of the GNU General Public License as published by
  15 *  the Free Software Foundation; either version 2 of the License, or
  16 *  (at your option) any later version.
  17 *
  18 *  This program is distributed in the hope that it will be useful,
  19 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  20 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  21 *  GNU General Public License for more details.
  22 *
  23 *  You should have received a copy of the GNU General Public License
  24 *  along with this program; if not, write to the Free Software
  25 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  26 */
  27
  28#include <linux/module.h>
  29#include <linux/slab.h>
  30#include <linux/i2c.h>
  31#include <linux/mutex.h>
  32#include <linux/delay.h>
  33
  34#define ISL29003_DRV_NAME       "isl29003"
  35#define DRIVER_VERSION          "1.0"
  36
  37#define ISL29003_REG_COMMAND            0x00
  38#define ISL29003_ADC_ENABLED            (1 << 7)
  39#define ISL29003_ADC_PD                 (1 << 6)
  40#define ISL29003_TIMING_INT             (1 << 5)
  41#define ISL29003_MODE_SHIFT             (2)
  42#define ISL29003_MODE_MASK              (0x3 << ISL29003_MODE_SHIFT)
  43#define ISL29003_RES_SHIFT              (0)
  44#define ISL29003_RES_MASK               (0x3 << ISL29003_RES_SHIFT)
  45
  46#define ISL29003_REG_CONTROL            0x01
  47#define ISL29003_INT_FLG                (1 << 5)
  48#define ISL29003_RANGE_SHIFT            (2)
  49#define ISL29003_RANGE_MASK             (0x3 << ISL29003_RANGE_SHIFT)
  50#define ISL29003_INT_PERSISTS_SHIFT     (0)
  51#define ISL29003_INT_PERSISTS_MASK      (0xf << ISL29003_INT_PERSISTS_SHIFT)
  52
  53#define ISL29003_REG_IRQ_THRESH_HI      0x02
  54#define ISL29003_REG_IRQ_THRESH_LO      0x03
  55#define ISL29003_REG_LSB_SENSOR         0x04
  56#define ISL29003_REG_MSB_SENSOR         0x05
  57#define ISL29003_REG_LSB_TIMER          0x06
  58#define ISL29003_REG_MSB_TIMER          0x07
  59
  60#define ISL29003_NUM_CACHABLE_REGS      4
  61
  62struct isl29003_data {
  63        struct i2c_client *client;
  64        struct mutex lock;
  65        u8 reg_cache[ISL29003_NUM_CACHABLE_REGS];
  66        u8 power_state_before_suspend;
  67};
  68
  69static int gain_range[] = {
  70        1000, 4000, 16000, 64000
  71};
  72
  73/*
  74 * register access helpers
  75 */
  76
  77static int __isl29003_read_reg(struct i2c_client *client,
  78                               u32 reg, u8 mask, u8 shift)
  79{
  80        struct isl29003_data *data = i2c_get_clientdata(client);
  81        return (data->reg_cache[reg] & mask) >> shift;
  82}
  83
  84static int __isl29003_write_reg(struct i2c_client *client,
  85                                u32 reg, u8 mask, u8 shift, u8 val)
  86{
  87        struct isl29003_data *data = i2c_get_clientdata(client);
  88        int ret = 0;
  89        u8 tmp;
  90
  91        if (reg >= ISL29003_NUM_CACHABLE_REGS)
  92                return -EINVAL;
  93
  94        mutex_lock(&data->lock);
  95
  96        tmp = data->reg_cache[reg];
  97        tmp &= ~mask;
  98        tmp |= val << shift;
  99
 100        ret = i2c_smbus_write_byte_data(client, reg, tmp);
 101        if (!ret)
 102                data->reg_cache[reg] = tmp;
 103
 104        mutex_unlock(&data->lock);
 105        return ret;
 106}
 107
 108/*
 109 * internally used functions
 110 */
 111
 112/* range */
 113static int isl29003_get_range(struct i2c_client *client)
 114{
 115        return __isl29003_read_reg(client, ISL29003_REG_CONTROL,
 116                ISL29003_RANGE_MASK, ISL29003_RANGE_SHIFT);
 117}
 118
 119static int isl29003_set_range(struct i2c_client *client, int range)
 120{
 121        return __isl29003_write_reg(client, ISL29003_REG_CONTROL,
 122                ISL29003_RANGE_MASK, ISL29003_RANGE_SHIFT, range);
 123}
 124
 125/* resolution */
 126static int isl29003_get_resolution(struct i2c_client *client)
 127{
 128        return __isl29003_read_reg(client, ISL29003_REG_COMMAND,
 129                ISL29003_RES_MASK, ISL29003_RES_SHIFT);
 130}
 131
 132static int isl29003_set_resolution(struct i2c_client *client, int res)
 133{
 134        return __isl29003_write_reg(client, ISL29003_REG_COMMAND,
 135                ISL29003_RES_MASK, ISL29003_RES_SHIFT, res);
 136}
 137
 138/* mode */
 139static int isl29003_get_mode(struct i2c_client *client)
 140{
 141        return __isl29003_read_reg(client, ISL29003_REG_COMMAND,
 142                ISL29003_RES_MASK, ISL29003_RES_SHIFT);
 143}
 144
 145static int isl29003_set_mode(struct i2c_client *client, int mode)
 146{
 147        return __isl29003_write_reg(client, ISL29003_REG_COMMAND,
 148                ISL29003_RES_MASK, ISL29003_RES_SHIFT, mode);
 149}
 150
 151/* power_state */
 152static int isl29003_set_power_state(struct i2c_client *client, int state)
 153{
 154        return __isl29003_write_reg(client, ISL29003_REG_COMMAND,
 155                                ISL29003_ADC_ENABLED | ISL29003_ADC_PD, 0,
 156                                state ? ISL29003_ADC_ENABLED : ISL29003_ADC_PD);
 157}
 158
 159static int isl29003_get_power_state(struct i2c_client *client)
 160{
 161        struct isl29003_data *data = i2c_get_clientdata(client);
 162        u8 cmdreg = data->reg_cache[ISL29003_REG_COMMAND];
 163        return ~cmdreg & ISL29003_ADC_PD;
 164}
 165
 166static int isl29003_get_adc_value(struct i2c_client *client)
 167{
 168        struct isl29003_data *data = i2c_get_clientdata(client);
 169        int lsb, msb, range, bitdepth;
 170
 171        mutex_lock(&data->lock);
 172        lsb = i2c_smbus_read_byte_data(client, ISL29003_REG_LSB_SENSOR);
 173
 174        if (lsb < 0) {
 175                mutex_unlock(&data->lock);
 176                return lsb;
 177        }
 178
 179        msb = i2c_smbus_read_byte_data(client, ISL29003_REG_MSB_SENSOR);
 180        mutex_unlock(&data->lock);
 181
 182        if (msb < 0)
 183                return msb;
 184
 185        range = isl29003_get_range(client);
 186        bitdepth = (4 - isl29003_get_resolution(client)) * 4;
 187        return (((msb << 8) | lsb) * gain_range[range]) >> bitdepth;
 188}
 189
 190/*
 191 * sysfs layer
 192 */
 193
 194/* range */
 195static ssize_t isl29003_show_range(struct device *dev,
 196                                   struct device_attribute *attr, char *buf)
 197{
 198        struct i2c_client *client = to_i2c_client(dev);
 199        return sprintf(buf, "%i\n", isl29003_get_range(client));
 200}
 201
 202static ssize_t isl29003_store_range(struct device *dev,
 203                                    struct device_attribute *attr,
 204                                    const char *buf, size_t count)
 205{
 206        struct i2c_client *client = to_i2c_client(dev);
 207        unsigned long val;
 208        int ret;
 209
 210        ret = kstrtoul(buf, 10, &val);
 211        if (ret)
 212                return ret;
 213
 214        if (val > 3)
 215                return -EINVAL;
 216
 217        ret = isl29003_set_range(client, val);
 218        if (ret < 0)
 219                return ret;
 220
 221        return count;
 222}
 223
 224static DEVICE_ATTR(range, S_IWUSR | S_IRUGO,
 225                   isl29003_show_range, isl29003_store_range);
 226
 227
 228/* resolution */
 229static ssize_t isl29003_show_resolution(struct device *dev,
 230                                        struct device_attribute *attr,
 231                                        char *buf)
 232{
 233        struct i2c_client *client = to_i2c_client(dev);
 234        return sprintf(buf, "%d\n", isl29003_get_resolution(client));
 235}
 236
 237static ssize_t isl29003_store_resolution(struct device *dev,
 238                                         struct device_attribute *attr,
 239                                         const char *buf, size_t count)
 240{
 241        struct i2c_client *client = to_i2c_client(dev);
 242        unsigned long val;
 243        int ret;
 244
 245        ret = kstrtoul(buf, 10, &val);
 246        if (ret)
 247                return ret;
 248
 249        if (val > 3)
 250                return -EINVAL;
 251
 252        ret = isl29003_set_resolution(client, val);
 253        if (ret < 0)
 254                return ret;
 255
 256        return count;
 257}
 258
 259static DEVICE_ATTR(resolution, S_IWUSR | S_IRUGO,
 260                   isl29003_show_resolution, isl29003_store_resolution);
 261
 262/* mode */
 263static ssize_t isl29003_show_mode(struct device *dev,
 264                                  struct device_attribute *attr, char *buf)
 265{
 266        struct i2c_client *client = to_i2c_client(dev);
 267        return sprintf(buf, "%d\n", isl29003_get_mode(client));
 268}
 269
 270static ssize_t isl29003_store_mode(struct device *dev,
 271                struct device_attribute *attr, const char *buf, size_t count)
 272{
 273        struct i2c_client *client = to_i2c_client(dev);
 274        unsigned long val;
 275        int ret;
 276
 277        ret = kstrtoul(buf, 10, &val);
 278        if (ret)
 279                return ret;
 280
 281        if (val > 2)
 282                return -EINVAL;
 283
 284        ret = isl29003_set_mode(client, val);
 285        if (ret < 0)
 286                return ret;
 287
 288        return count;
 289}
 290
 291static DEVICE_ATTR(mode, S_IWUSR | S_IRUGO,
 292                   isl29003_show_mode, isl29003_store_mode);
 293
 294
 295/* power state */
 296static ssize_t isl29003_show_power_state(struct device *dev,
 297                                         struct device_attribute *attr,
 298                                         char *buf)
 299{
 300        struct i2c_client *client = to_i2c_client(dev);
 301        return sprintf(buf, "%d\n", isl29003_get_power_state(client));
 302}
 303
 304static ssize_t isl29003_store_power_state(struct device *dev,
 305                                          struct device_attribute *attr,
 306                                          const char *buf, size_t count)
 307{
 308        struct i2c_client *client = to_i2c_client(dev);
 309        unsigned long val;
 310        int ret;
 311
 312        ret = kstrtoul(buf, 10, &val);
 313        if (ret)
 314                return ret;
 315
 316        if (val > 1)
 317                return -EINVAL;
 318
 319        ret = isl29003_set_power_state(client, val);
 320        return ret ? ret : count;
 321}
 322
 323static DEVICE_ATTR(power_state, S_IWUSR | S_IRUGO,
 324                   isl29003_show_power_state, isl29003_store_power_state);
 325
 326
 327/* lux */
 328static ssize_t isl29003_show_lux(struct device *dev,
 329                                 struct device_attribute *attr, char *buf)
 330{
 331        struct i2c_client *client = to_i2c_client(dev);
 332
 333        /* No LUX data if not operational */
 334        if (!isl29003_get_power_state(client))
 335                return -EBUSY;
 336
 337        return sprintf(buf, "%d\n", isl29003_get_adc_value(client));
 338}
 339
 340static DEVICE_ATTR(lux, S_IRUGO, isl29003_show_lux, NULL);
 341
 342static struct attribute *isl29003_attributes[] = {
 343        &dev_attr_range.attr,
 344        &dev_attr_resolution.attr,
 345        &dev_attr_mode.attr,
 346        &dev_attr_power_state.attr,
 347        &dev_attr_lux.attr,
 348        NULL
 349};
 350
 351static const struct attribute_group isl29003_attr_group = {
 352        .attrs = isl29003_attributes,
 353};
 354
 355static int isl29003_init_client(struct i2c_client *client)
 356{
 357        struct isl29003_data *data = i2c_get_clientdata(client);
 358        int i;
 359
 360        /* read all the registers once to fill the cache.
 361         * if one of the reads fails, we consider the init failed */
 362        for (i = 0; i < ARRAY_SIZE(data->reg_cache); i++) {
 363                int v = i2c_smbus_read_byte_data(client, i);
 364                if (v < 0)
 365                        return -ENODEV;
 366
 367                data->reg_cache[i] = v;
 368        }
 369
 370        /* set defaults */
 371        isl29003_set_range(client, 0);
 372        isl29003_set_resolution(client, 0);
 373        isl29003_set_mode(client, 0);
 374        isl29003_set_power_state(client, 0);
 375
 376        return 0;
 377}
 378
 379/*
 380 * I2C layer
 381 */
 382
 383static int isl29003_probe(struct i2c_client *client,
 384                                    const struct i2c_device_id *id)
 385{
 386        struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
 387        struct isl29003_data *data;
 388        int err = 0;
 389
 390        if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE))
 391                return -EIO;
 392
 393        data = kzalloc(sizeof(struct isl29003_data), GFP_KERNEL);
 394        if (!data)
 395                return -ENOMEM;
 396
 397        data->client = client;
 398        i2c_set_clientdata(client, data);
 399        mutex_init(&data->lock);
 400
 401        /* initialize the ISL29003 chip */
 402        err = isl29003_init_client(client);
 403        if (err)
 404                goto exit_kfree;
 405
 406        /* register sysfs hooks */
 407        err = sysfs_create_group(&client->dev.kobj, &isl29003_attr_group);
 408        if (err)
 409                goto exit_kfree;
 410
 411        dev_info(&client->dev, "driver version %s enabled\n", DRIVER_VERSION);
 412        return 0;
 413
 414exit_kfree:
 415        kfree(data);
 416        return err;
 417}
 418
 419static int isl29003_remove(struct i2c_client *client)
 420{
 421        sysfs_remove_group(&client->dev.kobj, &isl29003_attr_group);
 422        isl29003_set_power_state(client, 0);
 423        kfree(i2c_get_clientdata(client));
 424        return 0;
 425}
 426
 427#ifdef CONFIG_PM_SLEEP
 428static int isl29003_suspend(struct device *dev)
 429{
 430        struct i2c_client *client = to_i2c_client(dev);
 431        struct isl29003_data *data = i2c_get_clientdata(client);
 432
 433        data->power_state_before_suspend = isl29003_get_power_state(client);
 434        return isl29003_set_power_state(client, 0);
 435}
 436
 437static int isl29003_resume(struct device *dev)
 438{
 439        int i;
 440        struct i2c_client *client = to_i2c_client(dev);
 441        struct isl29003_data *data = i2c_get_clientdata(client);
 442
 443        /* restore registers from cache */
 444        for (i = 0; i < ARRAY_SIZE(data->reg_cache); i++)
 445                if (i2c_smbus_write_byte_data(client, i, data->reg_cache[i]))
 446                        return -EIO;
 447
 448        return isl29003_set_power_state(client,
 449                data->power_state_before_suspend);
 450}
 451
 452static SIMPLE_DEV_PM_OPS(isl29003_pm_ops, isl29003_suspend, isl29003_resume);
 453#define ISL29003_PM_OPS (&isl29003_pm_ops)
 454
 455#else
 456#define ISL29003_PM_OPS NULL
 457#endif /* CONFIG_PM_SLEEP */
 458
 459static const struct i2c_device_id isl29003_id[] = {
 460        { "isl29003", 0 },
 461        {}
 462};
 463MODULE_DEVICE_TABLE(i2c, isl29003_id);
 464
 465static struct i2c_driver isl29003_driver = {
 466        .driver = {
 467                .name   = ISL29003_DRV_NAME,
 468                .pm     = ISL29003_PM_OPS,
 469        },
 470        .probe  = isl29003_probe,
 471        .remove = isl29003_remove,
 472        .id_table = isl29003_id,
 473};
 474
 475module_i2c_driver(isl29003_driver);
 476
 477MODULE_AUTHOR("Daniel Mack <daniel@caiaq.de>");
 478MODULE_DESCRIPTION("ISL29003 ambient light sensor driver");
 479MODULE_LICENSE("GPL v2");
 480MODULE_VERSION(DRIVER_VERSION);
 481