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