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,
 133                                         CONFIGURE_ALS_IR_MODE_ALS);
 134                if (ret < 0)
 135                        return ret;
 136
 137                ret = regmap_update_bits(chip->regmap, ISL29028_REG_CONFIGURE,
 138                                         CONFIGURE_ALS_RANGE_MASK,
 139                                         CONFIGURE_ALS_RANGE_HIGH_LUX);
 140                break;
 141
 142        case MODE_IR:
 143                ret = regmap_update_bits(chip->regmap, ISL29028_REG_CONFIGURE,
 144                                         CONFIGURE_ALS_IR_MODE_MASK,
 145                                         CONFIGURE_ALS_IR_MODE_IR);
 146                break;
 147
 148        case MODE_NONE:
 149                return regmap_update_bits(chip->regmap, ISL29028_REG_CONFIGURE,
 150                        CONFIGURE_ALS_EN_MASK, CONFIGURE_ALS_DIS);
 151        }
 152
 153        if (ret < 0)
 154                return ret;
 155
 156        /* Enable the ALS/IR */
 157        ret = regmap_update_bits(chip->regmap, ISL29028_REG_CONFIGURE,
 158                                 CONFIGURE_ALS_EN_MASK, CONFIGURE_ALS_EN);
 159        if (ret < 0)
 160                return ret;
 161
 162        /* Need to wait for conversion time if ALS/IR mode enabled */
 163        mdelay(CONVERSION_TIME_MS);
 164        return 0;
 165}
 166
 167static int isl29028_read_als_ir(struct isl29028_chip *chip, int *als_ir)
 168{
 169        unsigned int lsb;
 170        unsigned int msb;
 171        int ret;
 172
 173        ret = regmap_read(chip->regmap, ISL29028_REG_ALSIR_L, &lsb);
 174        if (ret < 0) {
 175                dev_err(chip->dev,
 176                        "Error in reading register ALSIR_L err %d\n", ret);
 177                return ret;
 178        }
 179
 180        ret = regmap_read(chip->regmap, ISL29028_REG_ALSIR_U, &msb);
 181        if (ret < 0) {
 182                dev_err(chip->dev,
 183                        "Error in reading register ALSIR_U err %d\n", ret);
 184                return ret;
 185        }
 186
 187        *als_ir = ((msb & 0xF) << 8) | (lsb & 0xFF);
 188        return 0;
 189}
 190
 191static int isl29028_read_proxim(struct isl29028_chip *chip, int *prox)
 192{
 193        unsigned int data;
 194        int ret;
 195
 196        ret = regmap_read(chip->regmap, ISL29028_REG_PROX_DATA, &data);
 197        if (ret < 0) {
 198                dev_err(chip->dev, "Error in reading register %d, error %d\n",
 199                        ISL29028_REG_PROX_DATA, ret);
 200                return ret;
 201        }
 202        *prox = data;
 203        return 0;
 204}
 205
 206static int isl29028_proxim_get(struct isl29028_chip *chip, int *prox_data)
 207{
 208        int ret;
 209
 210        if (!chip->enable_prox) {
 211                ret = isl29028_enable_proximity(chip, true);
 212                if (ret < 0)
 213                        return ret;
 214                chip->enable_prox = true;
 215        }
 216        return isl29028_read_proxim(chip, prox_data);
 217}
 218
 219static int isl29028_als_get(struct isl29028_chip *chip, int *als_data)
 220{
 221        int ret;
 222        int als_ir_data;
 223
 224        if (chip->als_ir_mode != MODE_ALS) {
 225                ret = isl29028_set_als_ir_mode(chip, MODE_ALS);
 226                if (ret < 0) {
 227                        dev_err(chip->dev,
 228                                "Error in enabling ALS mode err %d\n", ret);
 229                        return ret;
 230                }
 231                chip->als_ir_mode = MODE_ALS;
 232        }
 233
 234        ret = isl29028_read_als_ir(chip, &als_ir_data);
 235        if (ret < 0)
 236                return ret;
 237
 238        /*
 239         * convert als data count to lux.
 240         * if lux_scale = 125,  lux = count * 0.031
 241         * if lux_scale = 2000, lux = count * 0.49
 242         */
 243        if (chip->lux_scale == 125)
 244                als_ir_data = (als_ir_data * 31) / 1000;
 245        else
 246                als_ir_data = (als_ir_data * 49) / 100;
 247
 248        *als_data = als_ir_data;
 249        return 0;
 250}
 251
 252static int isl29028_ir_get(struct isl29028_chip *chip, int *ir_data)
 253{
 254        int ret;
 255
 256        if (chip->als_ir_mode != MODE_IR) {
 257                ret = isl29028_set_als_ir_mode(chip, MODE_IR);
 258                if (ret < 0) {
 259                        dev_err(chip->dev,
 260                                "Error in enabling IR mode err %d\n", ret);
 261                        return ret;
 262                }
 263                chip->als_ir_mode = MODE_IR;
 264        }
 265        return isl29028_read_als_ir(chip, ir_data);
 266}
 267
 268/* Channel IO */
 269static int isl29028_write_raw(struct iio_dev *indio_dev,
 270                              struct iio_chan_spec const *chan,
 271                              int val, int val2, long mask)
 272{
 273        struct isl29028_chip *chip = iio_priv(indio_dev);
 274        int ret = -EINVAL;
 275
 276        mutex_lock(&chip->lock);
 277        switch (chan->type) {
 278        case IIO_PROXIMITY:
 279                if (mask != IIO_CHAN_INFO_SAMP_FREQ) {
 280                        dev_err(chip->dev,
 281                                "proximity: mask value 0x%08lx not supported\n",
 282                                mask);
 283                        break;
 284                }
 285                if (val < 1 || val > 100) {
 286                        dev_err(chip->dev,
 287                                "Samp_freq %d is not in range[1:100]\n", val);
 288                        break;
 289                }
 290                ret = isl29028_set_proxim_sampling(chip, val);
 291                if (ret < 0) {
 292                        dev_err(chip->dev,
 293                                "Setting proximity samp_freq fail, err %d\n",
 294                                ret);
 295                        break;
 296                }
 297                chip->prox_sampling = val;
 298                break;
 299
 300        case IIO_LIGHT:
 301                if (mask != IIO_CHAN_INFO_SCALE) {
 302                        dev_err(chip->dev,
 303                                "light: mask value 0x%08lx not supported\n",
 304                                mask);
 305                        break;
 306                }
 307                if ((val != 125) && (val != 2000)) {
 308                        dev_err(chip->dev,
 309                                "lux scale %d is invalid [125, 2000]\n", val);
 310                        break;
 311                }
 312                ret = isl29028_set_als_scale(chip, val);
 313                if (ret < 0) {
 314                        dev_err(chip->dev,
 315                                "Setting lux scale fail with error %d\n", ret);
 316                        break;
 317                }
 318                chip->lux_scale = val;
 319                break;
 320
 321        default:
 322                dev_err(chip->dev, "Unsupported channel type\n");
 323                break;
 324        }
 325        mutex_unlock(&chip->lock);
 326        return ret;
 327}
 328
 329static int isl29028_read_raw(struct iio_dev *indio_dev,
 330                             struct iio_chan_spec const *chan,
 331                             int *val, int *val2, long mask)
 332{
 333        struct isl29028_chip *chip = iio_priv(indio_dev);
 334        int ret = -EINVAL;
 335
 336        mutex_lock(&chip->lock);
 337        switch (mask) {
 338        case IIO_CHAN_INFO_RAW:
 339        case IIO_CHAN_INFO_PROCESSED:
 340                switch (chan->type) {
 341                case IIO_LIGHT:
 342                        ret = isl29028_als_get(chip, val);
 343                        break;
 344                case IIO_INTENSITY:
 345                        ret = isl29028_ir_get(chip, val);
 346                        break;
 347                case IIO_PROXIMITY:
 348                        ret = isl29028_proxim_get(chip, val);
 349                        break;
 350                default:
 351                        break;
 352                }
 353                if (ret < 0)
 354                        break;
 355                ret = IIO_VAL_INT;
 356                break;
 357
 358        case IIO_CHAN_INFO_SAMP_FREQ:
 359                if (chan->type != IIO_PROXIMITY)
 360                        break;
 361                *val = chip->prox_sampling;
 362                ret = IIO_VAL_INT;
 363                break;
 364
 365        case IIO_CHAN_INFO_SCALE:
 366                if (chan->type != IIO_LIGHT)
 367                        break;
 368                *val = chip->lux_scale;
 369                ret = IIO_VAL_INT;
 370                break;
 371
 372        default:
 373                dev_err(chip->dev, "mask value 0x%08lx not supported\n", mask);
 374                break;
 375        }
 376        mutex_unlock(&chip->lock);
 377        return ret;
 378}
 379
 380static IIO_CONST_ATTR(in_proximity_sampling_frequency_available,
 381                                "1, 3, 5, 10, 13, 20, 83, 100");
 382static IIO_CONST_ATTR(in_illuminance_scale_available, "125, 2000");
 383
 384#define ISL29028_DEV_ATTR(name) (&iio_dev_attr_##name.dev_attr.attr)
 385#define ISL29028_CONST_ATTR(name) (&iio_const_attr_##name.dev_attr.attr)
 386static struct attribute *isl29028_attributes[] = {
 387        ISL29028_CONST_ATTR(in_proximity_sampling_frequency_available),
 388        ISL29028_CONST_ATTR(in_illuminance_scale_available),
 389        NULL,
 390};
 391
 392static const struct attribute_group isl29108_group = {
 393        .attrs = isl29028_attributes,
 394};
 395
 396static const struct iio_chan_spec isl29028_channels[] = {
 397        {
 398                .type = IIO_LIGHT,
 399                .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) |
 400                BIT(IIO_CHAN_INFO_SCALE),
 401        }, {
 402                .type = IIO_INTENSITY,
 403                .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
 404        }, {
 405                .type = IIO_PROXIMITY,
 406                .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
 407                BIT(IIO_CHAN_INFO_SAMP_FREQ),
 408        }
 409};
 410
 411static const struct iio_info isl29028_info = {
 412        .attrs = &isl29108_group,
 413        .driver_module = THIS_MODULE,
 414        .read_raw = isl29028_read_raw,
 415        .write_raw = isl29028_write_raw,
 416};
 417
 418static int isl29028_chip_init(struct isl29028_chip *chip)
 419{
 420        int ret;
 421
 422        chip->enable_prox  = false;
 423        chip->prox_sampling = 20;
 424        chip->lux_scale = 2000;
 425        chip->als_ir_mode = MODE_NONE;
 426
 427        ret = regmap_write(chip->regmap, ISL29028_REG_TEST1_MODE, 0x0);
 428        if (ret < 0) {
 429                dev_err(chip->dev, "%s(): write to reg %d failed, err = %d\n",
 430                        __func__, ISL29028_REG_TEST1_MODE, ret);
 431                return ret;
 432        }
 433        ret = regmap_write(chip->regmap, ISL29028_REG_TEST2_MODE, 0x0);
 434        if (ret < 0) {
 435                dev_err(chip->dev, "%s(): write to reg %d failed, err = %d\n",
 436                        __func__, ISL29028_REG_TEST2_MODE, ret);
 437                return ret;
 438        }
 439
 440        ret = regmap_write(chip->regmap, ISL29028_REG_CONFIGURE, 0x0);
 441        if (ret < 0) {
 442                dev_err(chip->dev, "%s(): write to reg %d failed, err = %d\n",
 443                        __func__, ISL29028_REG_CONFIGURE, ret);
 444                return ret;
 445        }
 446
 447        ret = isl29028_set_proxim_sampling(chip, chip->prox_sampling);
 448        if (ret < 0) {
 449                dev_err(chip->dev, "setting the proximity, err = %d\n",
 450                        ret);
 451                return ret;
 452        }
 453
 454        ret = isl29028_set_als_scale(chip, chip->lux_scale);
 455        if (ret < 0)
 456                dev_err(chip->dev,
 457                        "setting als scale failed, err = %d\n", ret);
 458        return ret;
 459}
 460
 461static bool is_volatile_reg(struct device *dev, unsigned int reg)
 462{
 463        switch (reg) {
 464        case ISL29028_REG_INTERRUPT:
 465        case ISL29028_REG_PROX_DATA:
 466        case ISL29028_REG_ALSIR_L:
 467        case ISL29028_REG_ALSIR_U:
 468                return true;
 469        default:
 470                return false;
 471        }
 472}
 473
 474static const struct regmap_config isl29028_regmap_config = {
 475        .reg_bits = 8,
 476        .val_bits = 8,
 477        .volatile_reg = is_volatile_reg,
 478        .max_register = ISL29028_NUM_REGS - 1,
 479        .num_reg_defaults_raw = ISL29028_NUM_REGS,
 480        .cache_type = REGCACHE_RBTREE,
 481};
 482
 483static int isl29028_probe(struct i2c_client *client,
 484                          const struct i2c_device_id *id)
 485{
 486        struct isl29028_chip *chip;
 487        struct iio_dev *indio_dev;
 488        int ret;
 489
 490        indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*chip));
 491        if (!indio_dev) {
 492                dev_err(&client->dev, "iio allocation fails\n");
 493                return -ENOMEM;
 494        }
 495
 496        chip = iio_priv(indio_dev);
 497
 498        i2c_set_clientdata(client, indio_dev);
 499        chip->dev = &client->dev;
 500        mutex_init(&chip->lock);
 501
 502        chip->regmap = devm_regmap_init_i2c(client, &isl29028_regmap_config);
 503        if (IS_ERR(chip->regmap)) {
 504                ret = PTR_ERR(chip->regmap);
 505                dev_err(chip->dev, "regmap initialization failed: %d\n", ret);
 506                return ret;
 507        }
 508
 509        ret = isl29028_chip_init(chip);
 510        if (ret < 0) {
 511                dev_err(chip->dev, "chip initialization failed: %d\n", ret);
 512                return ret;
 513        }
 514
 515        indio_dev->info = &isl29028_info;
 516        indio_dev->channels = isl29028_channels;
 517        indio_dev->num_channels = ARRAY_SIZE(isl29028_channels);
 518        indio_dev->name = id->name;
 519        indio_dev->dev.parent = &client->dev;
 520        indio_dev->modes = INDIO_DIRECT_MODE;
 521        ret = devm_iio_device_register(indio_dev->dev.parent, indio_dev);
 522        if (ret < 0) {
 523                dev_err(chip->dev, "iio registration fails with error %d\n",
 524                        ret);
 525                return ret;
 526        }
 527        return 0;
 528}
 529
 530static const struct i2c_device_id isl29028_id[] = {
 531        {"isl29028", 0},
 532        {}
 533};
 534MODULE_DEVICE_TABLE(i2c, isl29028_id);
 535
 536static const struct of_device_id isl29028_of_match[] = {
 537        { .compatible = "isl,isl29028", }, /* for backward compat., don't use */
 538        { .compatible = "isil,isl29028", },
 539        { },
 540};
 541MODULE_DEVICE_TABLE(of, isl29028_of_match);
 542
 543static struct i2c_driver isl29028_driver = {
 544        .class  = I2C_CLASS_HWMON,
 545        .driver  = {
 546                .name = "isl29028",
 547                .of_match_table = isl29028_of_match,
 548        },
 549        .probe   = isl29028_probe,
 550        .id_table = isl29028_id,
 551};
 552
 553module_i2c_driver(isl29028_driver);
 554
 555MODULE_DESCRIPTION("ISL29028 Ambient Light and Proximity Sensor driver");
 556MODULE_LICENSE("GPL v2");
 557MODULE_AUTHOR("Laxman Dewangan <ldewangan@nvidia.com>");
 558