linux/drivers/staging/iio/light/isl29028.c
<<
>>
Prefs
   1/*
   2 * IIO driver for the light sensor ISL29028.
   3 * ISL29028 is Concurrent Ambient Light and Proximity Sensor
   4 *
   5 * Copyright (c) 2012, NVIDIA CORPORATION.  All rights reserved.
   6 *
   7 * This program is free software; you can redistribute it and/or modify it
   8 * under the terms and conditions of the GNU General Public License,
   9 * version 2, as published by the Free Software Foundation.
  10 *
  11 * This program is distributed in the hope it will be useful, but WITHOUT
  12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
  14 * more details.
  15 *
  16 * You should have received a copy of the GNU General Public License
  17 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  18 */
  19
  20#include <linux/module.h>
  21#include <linux/i2c.h>
  22#include <linux/err.h>
  23#include <linux/mutex.h>
  24#include <linux/delay.h>
  25#include <linux/slab.h>
  26#include <linux/regmap.h>
  27#include <linux/iio/iio.h>
  28#include <linux/iio/sysfs.h>
  29
  30#define CONVERSION_TIME_MS              100
  31
  32#define ISL29028_REG_CONFIGURE          0x01
  33
  34#define CONFIGURE_ALS_IR_MODE_ALS       0
  35#define CONFIGURE_ALS_IR_MODE_IR        BIT(0)
  36#define CONFIGURE_ALS_IR_MODE_MASK      BIT(0)
  37
  38#define CONFIGURE_ALS_RANGE_LOW_LUX     0
  39#define CONFIGURE_ALS_RANGE_HIGH_LUX    BIT(1)
  40#define CONFIGURE_ALS_RANGE_MASK        BIT(1)
  41
  42#define CONFIGURE_ALS_DIS               0
  43#define CONFIGURE_ALS_EN                BIT(2)
  44#define CONFIGURE_ALS_EN_MASK           BIT(2)
  45
  46#define CONFIGURE_PROX_DRIVE            BIT(3)
  47
  48#define CONFIGURE_PROX_SLP_SH           4
  49#define CONFIGURE_PROX_SLP_MASK         (7 << CONFIGURE_PROX_SLP_SH)
  50
  51#define CONFIGURE_PROX_EN               BIT(7)
  52#define CONFIGURE_PROX_EN_MASK          BIT(7)
  53
  54#define ISL29028_REG_INTERRUPT          0x02
  55
  56#define ISL29028_REG_PROX_DATA          0x08
  57#define ISL29028_REG_ALSIR_L            0x09
  58#define ISL29028_REG_ALSIR_U            0x0A
  59
  60#define ISL29028_REG_TEST1_MODE         0x0E
  61#define ISL29028_REG_TEST2_MODE         0x0F
  62
  63#define ISL29028_NUM_REGS               (ISL29028_REG_TEST2_MODE + 1)
  64
  65enum als_ir_mode {
  66        MODE_NONE = 0,
  67        MODE_ALS,
  68        MODE_IR
  69};
  70
  71struct isl29028_chip {
  72        struct device           *dev;
  73        struct mutex            lock;
  74        struct regmap           *regmap;
  75
  76        unsigned int            prox_sampling;
  77        bool                    enable_prox;
  78
  79        int                     lux_scale;
  80        int                     als_ir_mode;
  81};
  82
  83static int isl29028_set_proxim_sampling(struct isl29028_chip *chip,
  84                        unsigned int sampling)
  85{
  86        static unsigned int prox_period[] = {800, 400, 200, 100, 75, 50, 12, 0};
  87        int sel;
  88        unsigned int period = DIV_ROUND_UP(1000, sampling);
  89
  90        for (sel = 0; sel < ARRAY_SIZE(prox_period); ++sel) {
  91                if (period >= prox_period[sel])
  92                        break;
  93        }
  94        return regmap_update_bits(chip->regmap, ISL29028_REG_CONFIGURE,
  95                        CONFIGURE_PROX_SLP_MASK, sel << CONFIGURE_PROX_SLP_SH);
  96}
  97
  98static int isl29028_enable_proximity(struct isl29028_chip *chip, bool enable)
  99{
 100        int ret;
 101        int val = 0;
 102
 103        if (enable)
 104                val = CONFIGURE_PROX_EN;
 105        ret = regmap_update_bits(chip->regmap, ISL29028_REG_CONFIGURE,
 106                        CONFIGURE_PROX_EN_MASK, val);
 107        if (ret < 0)
 108                return ret;
 109
 110        /* Wait for conversion to be complete for first sample */
 111        mdelay(DIV_ROUND_UP(1000, chip->prox_sampling));
 112        return 0;
 113}
 114
 115static int isl29028_set_als_scale(struct isl29028_chip *chip, int lux_scale)
 116{
 117        int val = (lux_scale == 2000) ? CONFIGURE_ALS_RANGE_HIGH_LUX :
 118                                        CONFIGURE_ALS_RANGE_LOW_LUX;
 119
 120        return regmap_update_bits(chip->regmap, ISL29028_REG_CONFIGURE,
 121                CONFIGURE_ALS_RANGE_MASK, val);
 122}
 123
 124static int isl29028_set_als_ir_mode(struct isl29028_chip *chip,
 125        enum als_ir_mode mode)
 126{
 127        int ret = 0;
 128
 129        switch (mode) {
 130        case MODE_ALS:
 131                ret = regmap_update_bits(chip->regmap, ISL29028_REG_CONFIGURE,
 132                        CONFIGURE_ALS_IR_MODE_MASK, CONFIGURE_ALS_IR_MODE_ALS);
 133                if (ret < 0)
 134                        return ret;
 135
 136                ret = regmap_update_bits(chip->regmap, ISL29028_REG_CONFIGURE,
 137                        CONFIGURE_ALS_RANGE_MASK, CONFIGURE_ALS_RANGE_HIGH_LUX);
 138                break;
 139
 140        case MODE_IR:
 141                ret = regmap_update_bits(chip->regmap, ISL29028_REG_CONFIGURE,
 142                        CONFIGURE_ALS_IR_MODE_MASK, CONFIGURE_ALS_IR_MODE_IR);
 143                break;
 144
 145        case MODE_NONE:
 146                return regmap_update_bits(chip->regmap, ISL29028_REG_CONFIGURE,
 147                        CONFIGURE_ALS_EN_MASK, CONFIGURE_ALS_DIS);
 148        }
 149
 150        if (ret < 0)
 151                return ret;
 152
 153        /* Enable the ALS/IR */
 154        ret = regmap_update_bits(chip->regmap, ISL29028_REG_CONFIGURE,
 155                        CONFIGURE_ALS_EN_MASK, CONFIGURE_ALS_EN);
 156        if (ret < 0)
 157                return ret;
 158
 159        /* Need to wait for conversion time if ALS/IR mode enabled */
 160        mdelay(CONVERSION_TIME_MS);
 161        return 0;
 162}
 163
 164static int isl29028_read_als_ir(struct isl29028_chip *chip, int *als_ir)
 165{
 166        unsigned int lsb;
 167        unsigned int msb;
 168        int ret;
 169
 170        ret = regmap_read(chip->regmap, ISL29028_REG_ALSIR_L, &lsb);
 171        if (ret < 0) {
 172                dev_err(chip->dev,
 173                        "Error in reading register ALSIR_L err %d\n", ret);
 174                return ret;
 175        }
 176
 177        ret = regmap_read(chip->regmap, ISL29028_REG_ALSIR_U, &msb);
 178        if (ret < 0) {
 179                dev_err(chip->dev,
 180                        "Error in reading register ALSIR_U err %d\n", ret);
 181                return ret;
 182        }
 183
 184        *als_ir = ((msb & 0xF) << 8) | (lsb & 0xFF);
 185        return 0;
 186}
 187
 188static int isl29028_read_proxim(struct isl29028_chip *chip, int *prox)
 189{
 190        unsigned int data;
 191        int ret;
 192
 193        ret = regmap_read(chip->regmap, ISL29028_REG_PROX_DATA, &data);
 194        if (ret < 0) {
 195                dev_err(chip->dev, "Error in reading register %d, error %d\n",
 196                                ISL29028_REG_PROX_DATA, ret);
 197                return ret;
 198        }
 199        *prox = data;
 200        return 0;
 201}
 202
 203static int isl29028_proxim_get(struct isl29028_chip *chip, int *prox_data)
 204{
 205        int ret;
 206
 207        if (!chip->enable_prox) {
 208                ret = isl29028_enable_proximity(chip, true);
 209                if (ret < 0)
 210                        return ret;
 211                chip->enable_prox = true;
 212        }
 213        return isl29028_read_proxim(chip, prox_data);
 214}
 215
 216static int isl29028_als_get(struct isl29028_chip *chip, int *als_data)
 217{
 218        int ret;
 219        int als_ir_data;
 220
 221        if (chip->als_ir_mode != MODE_ALS) {
 222                ret = isl29028_set_als_ir_mode(chip, MODE_ALS);
 223                if (ret < 0) {
 224                        dev_err(chip->dev,
 225                                "Error in enabling ALS mode err %d\n", ret);
 226                        return ret;
 227                }
 228                chip->als_ir_mode = MODE_ALS;
 229        }
 230
 231        ret = isl29028_read_als_ir(chip, &als_ir_data);
 232        if (ret < 0)
 233                return ret;
 234
 235        /*
 236         * convert als data count to lux.
 237         * if lux_scale = 125,  lux = count * 0.031
 238         * if lux_scale = 2000, lux = count * 0.49
 239         */
 240        if (chip->lux_scale == 125)
 241                als_ir_data = (als_ir_data * 31) / 1000;
 242        else
 243                als_ir_data = (als_ir_data * 49) / 100;
 244
 245        *als_data = als_ir_data;
 246        return 0;
 247}
 248
 249static int isl29028_ir_get(struct isl29028_chip *chip, int *ir_data)
 250{
 251        int ret;
 252
 253        if (chip->als_ir_mode != MODE_IR) {
 254                ret = isl29028_set_als_ir_mode(chip, MODE_IR);
 255                if (ret < 0) {
 256                        dev_err(chip->dev,
 257                                "Error in enabling IR mode err %d\n", ret);
 258                        return ret;
 259                }
 260                chip->als_ir_mode = MODE_IR;
 261        }
 262        return isl29028_read_als_ir(chip, ir_data);
 263}
 264
 265/* Channel IO */
 266static int isl29028_write_raw(struct iio_dev *indio_dev,
 267             struct iio_chan_spec const *chan, int val, int val2, long mask)
 268{
 269        struct isl29028_chip *chip = iio_priv(indio_dev);
 270        int ret = -EINVAL;
 271
 272        mutex_lock(&chip->lock);
 273        switch (chan->type) {
 274        case IIO_PROXIMITY:
 275                if (mask != IIO_CHAN_INFO_SAMP_FREQ) {
 276                        dev_err(chip->dev,
 277                                "proximity: mask value 0x%08lx not supported\n",
 278                                mask);
 279                        break;
 280                }
 281                if (val < 1 || val > 100) {
 282                        dev_err(chip->dev,
 283                                "Samp_freq %d is not in range[1:100]\n", val);
 284                        break;
 285                }
 286                ret = isl29028_set_proxim_sampling(chip, val);
 287                if (ret < 0) {
 288                        dev_err(chip->dev,
 289                                "Setting proximity samp_freq fail, err %d\n",
 290                                ret);
 291                        break;
 292                }
 293                chip->prox_sampling = val;
 294                break;
 295
 296        case IIO_LIGHT:
 297                if (mask != IIO_CHAN_INFO_SCALE) {
 298                        dev_err(chip->dev,
 299                                "light: mask value 0x%08lx not supported\n",
 300                                mask);
 301                        break;
 302                }
 303                if ((val != 125) && (val != 2000)) {
 304                        dev_err(chip->dev,
 305                                "lux scale %d is invalid [125, 2000]\n", val);
 306                        break;
 307                }
 308                ret = isl29028_set_als_scale(chip, val);
 309                if (ret < 0) {
 310                        dev_err(chip->dev,
 311                                "Setting lux scale fail with error %d\n", ret);
 312                        break;
 313                }
 314                chip->lux_scale = val;
 315                break;
 316
 317        default:
 318                dev_err(chip->dev, "Unsupported channel type\n");
 319                break;
 320        }
 321        mutex_unlock(&chip->lock);
 322        return ret;
 323}
 324
 325static int isl29028_read_raw(struct iio_dev *indio_dev,
 326             struct iio_chan_spec const *chan, int *val, int *val2, long mask)
 327{
 328        struct isl29028_chip *chip = iio_priv(indio_dev);
 329        int ret = -EINVAL;
 330
 331        mutex_lock(&chip->lock);
 332        switch (mask) {
 333        case IIO_CHAN_INFO_RAW:
 334        case IIO_CHAN_INFO_PROCESSED:
 335                switch (chan->type) {
 336                case IIO_LIGHT:
 337                        ret = isl29028_als_get(chip, val);
 338                        break;
 339                case IIO_INTENSITY:
 340                        ret = isl29028_ir_get(chip, val);
 341                        break;
 342                case IIO_PROXIMITY:
 343                        ret = isl29028_proxim_get(chip, val);
 344                        break;
 345                default:
 346                        break;
 347                }
 348                if (ret < 0)
 349                        break;
 350                ret = IIO_VAL_INT;
 351                break;
 352
 353        case IIO_CHAN_INFO_SAMP_FREQ:
 354                if (chan->type != IIO_PROXIMITY)
 355                        break;
 356                *val = chip->prox_sampling;
 357                ret = IIO_VAL_INT;
 358                break;
 359
 360        case IIO_CHAN_INFO_SCALE:
 361                if (chan->type != IIO_LIGHT)
 362                        break;
 363                *val = chip->lux_scale;
 364                ret = IIO_VAL_INT;
 365                break;
 366
 367        default:
 368                dev_err(chip->dev, "mask value 0x%08lx not supported\n", mask);
 369                break;
 370        }
 371        mutex_unlock(&chip->lock);
 372        return ret;
 373}
 374
 375static IIO_CONST_ATTR(in_proximity_sampling_frequency_available,
 376                                "1, 3, 5, 10, 13, 20, 83, 100");
 377static IIO_CONST_ATTR(in_illuminance_scale_available, "125, 2000");
 378
 379#define ISL29028_DEV_ATTR(name) (&iio_dev_attr_##name.dev_attr.attr)
 380#define ISL29028_CONST_ATTR(name) (&iio_const_attr_##name.dev_attr.attr)
 381static struct attribute *isl29028_attributes[] = {
 382        ISL29028_CONST_ATTR(in_proximity_sampling_frequency_available),
 383        ISL29028_CONST_ATTR(in_illuminance_scale_available),
 384        NULL,
 385};
 386
 387static const struct attribute_group isl29108_group = {
 388        .attrs = isl29028_attributes,
 389};
 390
 391static const struct iio_chan_spec isl29028_channels[] = {
 392        {
 393                .type = IIO_LIGHT,
 394                .info_mask = IIO_CHAN_INFO_PROCESSED_SEPARATE_BIT |
 395                IIO_CHAN_INFO_SCALE_SEPARATE_BIT,
 396        }, {
 397                .type = IIO_INTENSITY,
 398                .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT,
 399        }, {
 400                .type = IIO_PROXIMITY,
 401                .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT |
 402                IIO_CHAN_INFO_SAMP_FREQ_SEPARATE_BIT,
 403        }
 404};
 405
 406static const struct iio_info isl29028_info = {
 407        .attrs = &isl29108_group,
 408        .driver_module = THIS_MODULE,
 409        .read_raw = &isl29028_read_raw,
 410        .write_raw = &isl29028_write_raw,
 411};
 412
 413static int isl29028_chip_init(struct isl29028_chip *chip)
 414{
 415        int ret;
 416
 417        chip->enable_prox  = false;
 418        chip->prox_sampling = 20;
 419        chip->lux_scale = 2000;
 420        chip->als_ir_mode = MODE_NONE;
 421
 422        ret = regmap_write(chip->regmap, ISL29028_REG_TEST1_MODE, 0x0);
 423        if (ret < 0) {
 424                dev_err(chip->dev, "%s(): write to reg %d failed, err = %d\n",
 425                        __func__, ISL29028_REG_TEST1_MODE, ret);
 426                return ret;
 427        }
 428        ret = regmap_write(chip->regmap, ISL29028_REG_TEST2_MODE, 0x0);
 429        if (ret < 0) {
 430                dev_err(chip->dev, "%s(): write to reg %d failed, err = %d\n",
 431                        __func__, ISL29028_REG_TEST2_MODE, ret);
 432                return ret;
 433        }
 434
 435        ret = regmap_write(chip->regmap, ISL29028_REG_CONFIGURE, 0x0);
 436        if (ret < 0) {
 437                dev_err(chip->dev, "%s(): write to reg %d failed, err = %d\n",
 438                        __func__, ISL29028_REG_CONFIGURE, ret);
 439                return ret;
 440        }
 441
 442        ret = isl29028_set_proxim_sampling(chip, chip->prox_sampling);
 443        if (ret < 0) {
 444                dev_err(chip->dev, "%s(): setting the proximity, err = %d\n",
 445                        __func__, ret);
 446                return ret;
 447        }
 448
 449        ret = isl29028_set_als_scale(chip, chip->lux_scale);
 450        if (ret < 0)
 451                dev_err(chip->dev, "%s(): setting als scale failed, err = %d\n",
 452                        __func__, ret);
 453        return ret;
 454}
 455
 456static bool is_volatile_reg(struct device *dev, unsigned int reg)
 457{
 458        switch (reg) {
 459        case ISL29028_REG_INTERRUPT:
 460        case ISL29028_REG_PROX_DATA:
 461        case ISL29028_REG_ALSIR_L:
 462        case ISL29028_REG_ALSIR_U:
 463                return true;
 464        default:
 465                return false;
 466        }
 467}
 468
 469static const struct regmap_config isl29028_regmap_config = {
 470        .reg_bits = 8,
 471        .val_bits = 8,
 472        .volatile_reg = is_volatile_reg,
 473        .max_register = ISL29028_NUM_REGS - 1,
 474        .num_reg_defaults_raw = ISL29028_NUM_REGS,
 475        .cache_type = REGCACHE_RBTREE,
 476};
 477
 478static int isl29028_probe(struct i2c_client *client,
 479        const struct i2c_device_id *id)
 480{
 481        struct isl29028_chip *chip;
 482        struct iio_dev *indio_dev;
 483        int ret;
 484
 485        indio_dev = iio_device_alloc(sizeof(*chip));
 486        if (!indio_dev) {
 487                dev_err(&client->dev, "iio allocation fails\n");
 488                return -ENOMEM;
 489        }
 490
 491        chip = iio_priv(indio_dev);
 492
 493        i2c_set_clientdata(client, indio_dev);
 494        chip->dev = &client->dev;
 495        mutex_init(&chip->lock);
 496
 497        chip->regmap = devm_regmap_init_i2c(client, &isl29028_regmap_config);
 498        if (IS_ERR(chip->regmap)) {
 499                ret = PTR_ERR(chip->regmap);
 500                dev_err(chip->dev, "regmap initialization failed: %d\n", ret);
 501                goto exit_iio_free;
 502        }
 503
 504        ret = isl29028_chip_init(chip);
 505        if (ret < 0) {
 506                dev_err(chip->dev, "chip initialization failed: %d\n", ret);
 507                goto exit_iio_free;
 508        }
 509
 510        indio_dev->info = &isl29028_info;
 511        indio_dev->channels = isl29028_channels;
 512        indio_dev->num_channels = ARRAY_SIZE(isl29028_channels);
 513        indio_dev->name = id->name;
 514        indio_dev->dev.parent = &client->dev;
 515        indio_dev->modes = INDIO_DIRECT_MODE;
 516        ret = iio_device_register(indio_dev);
 517        if (ret < 0) {
 518                dev_err(chip->dev, "iio registration fails with error %d\n",
 519                        ret);
 520                goto exit_iio_free;
 521        }
 522        return 0;
 523
 524exit_iio_free:
 525        iio_device_free(indio_dev);
 526        return ret;
 527}
 528
 529static int isl29028_remove(struct i2c_client *client)
 530{
 531        struct iio_dev *indio_dev = i2c_get_clientdata(client);
 532
 533        iio_device_unregister(indio_dev);
 534        iio_device_free(indio_dev);
 535        return 0;
 536}
 537
 538static const struct i2c_device_id isl29028_id[] = {
 539        {"isl29028", 0},
 540        {}
 541};
 542MODULE_DEVICE_TABLE(i2c, isl29028_id);
 543
 544static const struct of_device_id isl29028_of_match[] = {
 545        { .compatible = "isil,isl29028", },
 546        { },
 547};
 548MODULE_DEVICE_TABLE(of, isl29028_of_match);
 549
 550static struct i2c_driver isl29028_driver = {
 551        .class  = I2C_CLASS_HWMON,
 552        .driver  = {
 553                .name = "isl29028",
 554                .owner = THIS_MODULE,
 555                .of_match_table = isl29028_of_match,
 556        },
 557        .probe   = isl29028_probe,
 558        .remove  = isl29028_remove,
 559        .id_table = isl29028_id,
 560};
 561
 562module_i2c_driver(isl29028_driver);
 563
 564MODULE_DESCRIPTION("ISL29028 Ambient Light and Proximity Sensor driver");
 565MODULE_LICENSE("GPL v2");
 566MODULE_AUTHOR("Laxman Dewangan <ldewangan@nvidia.com>");
 567