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