linux/drivers/iio/light/iqs621-als.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * Azoteq IQS621/622 Ambient Light Sensors
   4 *
   5 * Copyright (C) 2019 Jeff LaBundy <jeff@labundy.com>
   6 */
   7
   8#include <linux/device.h>
   9#include <linux/iio/events.h>
  10#include <linux/iio/iio.h>
  11#include <linux/kernel.h>
  12#include <linux/mfd/iqs62x.h>
  13#include <linux/module.h>
  14#include <linux/mutex.h>
  15#include <linux/notifier.h>
  16#include <linux/platform_device.h>
  17#include <linux/regmap.h>
  18
  19#define IQS621_ALS_FLAGS_LIGHT                  BIT(7)
  20#define IQS621_ALS_FLAGS_RANGE                  GENMASK(3, 0)
  21
  22#define IQS621_ALS_UI_OUT                       0x17
  23
  24#define IQS621_ALS_THRESH_DARK                  0x80
  25#define IQS621_ALS_THRESH_LIGHT                 0x81
  26
  27#define IQS622_IR_RANGE                         0x15
  28#define IQS622_IR_FLAGS                         0x16
  29#define IQS622_IR_FLAGS_TOUCH                   BIT(1)
  30#define IQS622_IR_FLAGS_PROX                    BIT(0)
  31
  32#define IQS622_IR_UI_OUT                        0x17
  33
  34#define IQS622_IR_THRESH_PROX                   0x91
  35#define IQS622_IR_THRESH_TOUCH                  0x92
  36
  37struct iqs621_als_private {
  38        struct iqs62x_core *iqs62x;
  39        struct iio_dev *indio_dev;
  40        struct notifier_block notifier;
  41        struct mutex lock;
  42        bool light_en;
  43        bool range_en;
  44        bool prox_en;
  45        u8 als_flags;
  46        u8 ir_flags_mask;
  47        u8 ir_flags;
  48        u8 thresh_light;
  49        u8 thresh_dark;
  50        u8 thresh_prox;
  51};
  52
  53static int iqs621_als_init(struct iqs621_als_private *iqs621_als)
  54{
  55        struct iqs62x_core *iqs62x = iqs621_als->iqs62x;
  56        unsigned int event_mask = 0;
  57        int ret;
  58
  59        switch (iqs621_als->ir_flags_mask) {
  60        case IQS622_IR_FLAGS_TOUCH:
  61                ret = regmap_write(iqs62x->regmap, IQS622_IR_THRESH_TOUCH,
  62                                   iqs621_als->thresh_prox);
  63                break;
  64
  65        case IQS622_IR_FLAGS_PROX:
  66                ret = regmap_write(iqs62x->regmap, IQS622_IR_THRESH_PROX,
  67                                   iqs621_als->thresh_prox);
  68                break;
  69
  70        default:
  71                ret = regmap_write(iqs62x->regmap, IQS621_ALS_THRESH_LIGHT,
  72                                   iqs621_als->thresh_light);
  73                if (ret)
  74                        return ret;
  75
  76                ret = regmap_write(iqs62x->regmap, IQS621_ALS_THRESH_DARK,
  77                                   iqs621_als->thresh_dark);
  78        }
  79
  80        if (ret)
  81                return ret;
  82
  83        if (iqs621_als->light_en || iqs621_als->range_en)
  84                event_mask |= iqs62x->dev_desc->als_mask;
  85
  86        if (iqs621_als->prox_en)
  87                event_mask |= iqs62x->dev_desc->ir_mask;
  88
  89        return regmap_update_bits(iqs62x->regmap, IQS620_GLBL_EVENT_MASK,
  90                                  event_mask, 0);
  91}
  92
  93static int iqs621_als_notifier(struct notifier_block *notifier,
  94                               unsigned long event_flags, void *context)
  95{
  96        struct iqs62x_event_data *event_data = context;
  97        struct iqs621_als_private *iqs621_als;
  98        struct iio_dev *indio_dev;
  99        bool light_new, light_old;
 100        bool prox_new, prox_old;
 101        u8 range_new, range_old;
 102        s64 timestamp;
 103        int ret;
 104
 105        iqs621_als = container_of(notifier, struct iqs621_als_private,
 106                                  notifier);
 107        indio_dev = iqs621_als->indio_dev;
 108        timestamp = iio_get_time_ns(indio_dev);
 109
 110        mutex_lock(&iqs621_als->lock);
 111
 112        if (event_flags & BIT(IQS62X_EVENT_SYS_RESET)) {
 113                ret = iqs621_als_init(iqs621_als);
 114                if (ret) {
 115                        dev_err(indio_dev->dev.parent,
 116                                "Failed to re-initialize device: %d\n", ret);
 117                        ret = NOTIFY_BAD;
 118                } else {
 119                        ret = NOTIFY_OK;
 120                }
 121
 122                goto err_mutex;
 123        }
 124
 125        if (!iqs621_als->light_en && !iqs621_als->range_en &&
 126            !iqs621_als->prox_en) {
 127                ret = NOTIFY_DONE;
 128                goto err_mutex;
 129        }
 130
 131        /* IQS621 only */
 132        light_new = event_data->als_flags & IQS621_ALS_FLAGS_LIGHT;
 133        light_old = iqs621_als->als_flags & IQS621_ALS_FLAGS_LIGHT;
 134
 135        if (iqs621_als->light_en && light_new && !light_old)
 136                iio_push_event(indio_dev,
 137                               IIO_UNMOD_EVENT_CODE(IIO_LIGHT, 0,
 138                                                    IIO_EV_TYPE_THRESH,
 139                                                    IIO_EV_DIR_RISING),
 140                               timestamp);
 141        else if (iqs621_als->light_en && !light_new && light_old)
 142                iio_push_event(indio_dev,
 143                               IIO_UNMOD_EVENT_CODE(IIO_LIGHT, 0,
 144                                                    IIO_EV_TYPE_THRESH,
 145                                                    IIO_EV_DIR_FALLING),
 146                               timestamp);
 147
 148        /* IQS621 and IQS622 */
 149        range_new = event_data->als_flags & IQS621_ALS_FLAGS_RANGE;
 150        range_old = iqs621_als->als_flags & IQS621_ALS_FLAGS_RANGE;
 151
 152        if (iqs621_als->range_en && (range_new > range_old))
 153                iio_push_event(indio_dev,
 154                               IIO_UNMOD_EVENT_CODE(IIO_INTENSITY, 0,
 155                                                    IIO_EV_TYPE_CHANGE,
 156                                                    IIO_EV_DIR_RISING),
 157                               timestamp);
 158        else if (iqs621_als->range_en && (range_new < range_old))
 159                iio_push_event(indio_dev,
 160                               IIO_UNMOD_EVENT_CODE(IIO_INTENSITY, 0,
 161                                                    IIO_EV_TYPE_CHANGE,
 162                                                    IIO_EV_DIR_FALLING),
 163                               timestamp);
 164
 165        /* IQS622 only */
 166        prox_new = event_data->ir_flags & iqs621_als->ir_flags_mask;
 167        prox_old = iqs621_als->ir_flags & iqs621_als->ir_flags_mask;
 168
 169        if (iqs621_als->prox_en && prox_new && !prox_old)
 170                iio_push_event(indio_dev,
 171                               IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, 0,
 172                                                    IIO_EV_TYPE_THRESH,
 173                                                    IIO_EV_DIR_RISING),
 174                               timestamp);
 175        else if (iqs621_als->prox_en && !prox_new && prox_old)
 176                iio_push_event(indio_dev,
 177                               IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, 0,
 178                                                    IIO_EV_TYPE_THRESH,
 179                                                    IIO_EV_DIR_FALLING),
 180                               timestamp);
 181
 182        iqs621_als->als_flags = event_data->als_flags;
 183        iqs621_als->ir_flags = event_data->ir_flags;
 184        ret = NOTIFY_OK;
 185
 186err_mutex:
 187        mutex_unlock(&iqs621_als->lock);
 188
 189        return ret;
 190}
 191
 192static void iqs621_als_notifier_unregister(void *context)
 193{
 194        struct iqs621_als_private *iqs621_als = context;
 195        struct iio_dev *indio_dev = iqs621_als->indio_dev;
 196        int ret;
 197
 198        ret = blocking_notifier_chain_unregister(&iqs621_als->iqs62x->nh,
 199                                                 &iqs621_als->notifier);
 200        if (ret)
 201                dev_err(indio_dev->dev.parent,
 202                        "Failed to unregister notifier: %d\n", ret);
 203}
 204
 205static int iqs621_als_read_raw(struct iio_dev *indio_dev,
 206                               struct iio_chan_spec const *chan,
 207                               int *val, int *val2, long mask)
 208{
 209        struct iqs621_als_private *iqs621_als = iio_priv(indio_dev);
 210        struct iqs62x_core *iqs62x = iqs621_als->iqs62x;
 211        int ret;
 212        __le16 val_buf;
 213
 214        switch (chan->type) {
 215        case IIO_INTENSITY:
 216                ret = regmap_read(iqs62x->regmap, chan->address, val);
 217                if (ret)
 218                        return ret;
 219
 220                *val &= IQS621_ALS_FLAGS_RANGE;
 221                return IIO_VAL_INT;
 222
 223        case IIO_PROXIMITY:
 224        case IIO_LIGHT:
 225                ret = regmap_raw_read(iqs62x->regmap, chan->address, &val_buf,
 226                                      sizeof(val_buf));
 227                if (ret)
 228                        return ret;
 229
 230                *val = le16_to_cpu(val_buf);
 231                return IIO_VAL_INT;
 232
 233        default:
 234                return -EINVAL;
 235        }
 236}
 237
 238static int iqs621_als_read_event_config(struct iio_dev *indio_dev,
 239                                        const struct iio_chan_spec *chan,
 240                                        enum iio_event_type type,
 241                                        enum iio_event_direction dir)
 242{
 243        struct iqs621_als_private *iqs621_als = iio_priv(indio_dev);
 244        int ret;
 245
 246        mutex_lock(&iqs621_als->lock);
 247
 248        switch (chan->type) {
 249        case IIO_LIGHT:
 250                ret = iqs621_als->light_en;
 251                break;
 252
 253        case IIO_INTENSITY:
 254                ret = iqs621_als->range_en;
 255                break;
 256
 257        case IIO_PROXIMITY:
 258                ret = iqs621_als->prox_en;
 259                break;
 260
 261        default:
 262                ret = -EINVAL;
 263        }
 264
 265        mutex_unlock(&iqs621_als->lock);
 266
 267        return ret;
 268}
 269
 270static int iqs621_als_write_event_config(struct iio_dev *indio_dev,
 271                                         const struct iio_chan_spec *chan,
 272                                         enum iio_event_type type,
 273                                         enum iio_event_direction dir,
 274                                         int state)
 275{
 276        struct iqs621_als_private *iqs621_als = iio_priv(indio_dev);
 277        struct iqs62x_core *iqs62x = iqs621_als->iqs62x;
 278        unsigned int val;
 279        int ret;
 280
 281        mutex_lock(&iqs621_als->lock);
 282
 283        ret = regmap_read(iqs62x->regmap, iqs62x->dev_desc->als_flags, &val);
 284        if (ret)
 285                goto err_mutex;
 286        iqs621_als->als_flags = val;
 287
 288        switch (chan->type) {
 289        case IIO_LIGHT:
 290                ret = regmap_update_bits(iqs62x->regmap, IQS620_GLBL_EVENT_MASK,
 291                                         iqs62x->dev_desc->als_mask,
 292                                         iqs621_als->range_en || state ? 0 :
 293                                                                         0xFF);
 294                if (!ret)
 295                        iqs621_als->light_en = state;
 296                break;
 297
 298        case IIO_INTENSITY:
 299                ret = regmap_update_bits(iqs62x->regmap, IQS620_GLBL_EVENT_MASK,
 300                                         iqs62x->dev_desc->als_mask,
 301                                         iqs621_als->light_en || state ? 0 :
 302                                                                         0xFF);
 303                if (!ret)
 304                        iqs621_als->range_en = state;
 305                break;
 306
 307        case IIO_PROXIMITY:
 308                ret = regmap_read(iqs62x->regmap, IQS622_IR_FLAGS, &val);
 309                if (ret)
 310                        goto err_mutex;
 311                iqs621_als->ir_flags = val;
 312
 313                ret = regmap_update_bits(iqs62x->regmap, IQS620_GLBL_EVENT_MASK,
 314                                         iqs62x->dev_desc->ir_mask,
 315                                         state ? 0 : 0xFF);
 316                if (!ret)
 317                        iqs621_als->prox_en = state;
 318                break;
 319
 320        default:
 321                ret = -EINVAL;
 322        }
 323
 324err_mutex:
 325        mutex_unlock(&iqs621_als->lock);
 326
 327        return ret;
 328}
 329
 330static int iqs621_als_read_event_value(struct iio_dev *indio_dev,
 331                                       const struct iio_chan_spec *chan,
 332                                       enum iio_event_type type,
 333                                       enum iio_event_direction dir,
 334                                       enum iio_event_info info,
 335                                       int *val, int *val2)
 336{
 337        struct iqs621_als_private *iqs621_als = iio_priv(indio_dev);
 338        int ret = IIO_VAL_INT;
 339
 340        mutex_lock(&iqs621_als->lock);
 341
 342        switch (dir) {
 343        case IIO_EV_DIR_RISING:
 344                *val = iqs621_als->thresh_light * 16;
 345                break;
 346
 347        case IIO_EV_DIR_FALLING:
 348                *val = iqs621_als->thresh_dark * 4;
 349                break;
 350
 351        case IIO_EV_DIR_EITHER:
 352                if (iqs621_als->ir_flags_mask == IQS622_IR_FLAGS_TOUCH)
 353                        *val = iqs621_als->thresh_prox * 4;
 354                else
 355                        *val = iqs621_als->thresh_prox;
 356                break;
 357
 358        default:
 359                ret = -EINVAL;
 360        }
 361
 362        mutex_unlock(&iqs621_als->lock);
 363
 364        return ret;
 365}
 366
 367static int iqs621_als_write_event_value(struct iio_dev *indio_dev,
 368                                        const struct iio_chan_spec *chan,
 369                                        enum iio_event_type type,
 370                                        enum iio_event_direction dir,
 371                                        enum iio_event_info info,
 372                                        int val, int val2)
 373{
 374        struct iqs621_als_private *iqs621_als = iio_priv(indio_dev);
 375        struct iqs62x_core *iqs62x = iqs621_als->iqs62x;
 376        unsigned int thresh_reg, thresh_val;
 377        u8 ir_flags_mask, *thresh_cache;
 378        int ret = -EINVAL;
 379
 380        mutex_lock(&iqs621_als->lock);
 381
 382        switch (dir) {
 383        case IIO_EV_DIR_RISING:
 384                thresh_reg = IQS621_ALS_THRESH_LIGHT;
 385                thresh_val = val / 16;
 386
 387                thresh_cache = &iqs621_als->thresh_light;
 388                ir_flags_mask = 0;
 389                break;
 390
 391        case IIO_EV_DIR_FALLING:
 392                thresh_reg = IQS621_ALS_THRESH_DARK;
 393                thresh_val = val / 4;
 394
 395                thresh_cache = &iqs621_als->thresh_dark;
 396                ir_flags_mask = 0;
 397                break;
 398
 399        case IIO_EV_DIR_EITHER:
 400                /*
 401                 * The IQS622 supports two detection thresholds, both measured
 402                 * in the same arbitrary units reported by read_raw: proximity
 403                 * (0 through 255 in steps of 1), and touch (0 through 1020 in
 404                 * steps of 4).
 405                 *
 406                 * Based on the single detection threshold chosen by the user,
 407                 * select the hardware threshold that gives the best trade-off
 408                 * between range and resolution.
 409                 *
 410                 * By default, the close-range (but coarse) touch threshold is
 411                 * chosen during probe.
 412                 */
 413                switch (val) {
 414                case 0 ... 255:
 415                        thresh_reg = IQS622_IR_THRESH_PROX;
 416                        thresh_val = val;
 417
 418                        ir_flags_mask = IQS622_IR_FLAGS_PROX;
 419                        break;
 420
 421                case 256 ... 1020:
 422                        thresh_reg = IQS622_IR_THRESH_TOUCH;
 423                        thresh_val = val / 4;
 424
 425                        ir_flags_mask = IQS622_IR_FLAGS_TOUCH;
 426                        break;
 427
 428                default:
 429                        goto err_mutex;
 430                }
 431
 432                thresh_cache = &iqs621_als->thresh_prox;
 433                break;
 434
 435        default:
 436                goto err_mutex;
 437        }
 438
 439        if (thresh_val > 0xFF)
 440                goto err_mutex;
 441
 442        ret = regmap_write(iqs62x->regmap, thresh_reg, thresh_val);
 443        if (ret)
 444                goto err_mutex;
 445
 446        *thresh_cache = thresh_val;
 447        iqs621_als->ir_flags_mask = ir_flags_mask;
 448
 449err_mutex:
 450        mutex_unlock(&iqs621_als->lock);
 451
 452        return ret;
 453}
 454
 455static const struct iio_info iqs621_als_info = {
 456        .read_raw = &iqs621_als_read_raw,
 457        .read_event_config = iqs621_als_read_event_config,
 458        .write_event_config = iqs621_als_write_event_config,
 459        .read_event_value = iqs621_als_read_event_value,
 460        .write_event_value = iqs621_als_write_event_value,
 461};
 462
 463static const struct iio_event_spec iqs621_als_range_events[] = {
 464        {
 465                .type = IIO_EV_TYPE_CHANGE,
 466                .dir = IIO_EV_DIR_EITHER,
 467                .mask_separate = BIT(IIO_EV_INFO_ENABLE),
 468        },
 469};
 470
 471static const struct iio_event_spec iqs621_als_light_events[] = {
 472        {
 473                .type = IIO_EV_TYPE_THRESH,
 474                .dir = IIO_EV_DIR_EITHER,
 475                .mask_separate = BIT(IIO_EV_INFO_ENABLE),
 476        },
 477        {
 478                .type = IIO_EV_TYPE_THRESH,
 479                .dir = IIO_EV_DIR_RISING,
 480                .mask_separate = BIT(IIO_EV_INFO_VALUE),
 481        },
 482        {
 483                .type = IIO_EV_TYPE_THRESH,
 484                .dir = IIO_EV_DIR_FALLING,
 485                .mask_separate = BIT(IIO_EV_INFO_VALUE),
 486        },
 487};
 488
 489static const struct iio_chan_spec iqs621_als_channels[] = {
 490        {
 491                .type = IIO_INTENSITY,
 492                .address = IQS621_ALS_FLAGS,
 493                .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
 494                .event_spec = iqs621_als_range_events,
 495                .num_event_specs = ARRAY_SIZE(iqs621_als_range_events),
 496        },
 497        {
 498                .type = IIO_LIGHT,
 499                .address = IQS621_ALS_UI_OUT,
 500                .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
 501                .event_spec = iqs621_als_light_events,
 502                .num_event_specs = ARRAY_SIZE(iqs621_als_light_events),
 503        },
 504};
 505
 506static const struct iio_event_spec iqs622_als_prox_events[] = {
 507        {
 508                .type = IIO_EV_TYPE_THRESH,
 509                .dir = IIO_EV_DIR_EITHER,
 510                .mask_separate = BIT(IIO_EV_INFO_ENABLE) |
 511                                 BIT(IIO_EV_INFO_VALUE),
 512        },
 513};
 514
 515static const struct iio_chan_spec iqs622_als_channels[] = {
 516        {
 517                .type = IIO_INTENSITY,
 518                .channel2 = IIO_MOD_LIGHT_BOTH,
 519                .address = IQS622_ALS_FLAGS,
 520                .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
 521                .event_spec = iqs621_als_range_events,
 522                .num_event_specs = ARRAY_SIZE(iqs621_als_range_events),
 523                .modified = true,
 524        },
 525        {
 526                .type = IIO_INTENSITY,
 527                .channel2 = IIO_MOD_LIGHT_IR,
 528                .address = IQS622_IR_RANGE,
 529                .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
 530                .modified = true,
 531        },
 532        {
 533                .type = IIO_PROXIMITY,
 534                .address = IQS622_IR_UI_OUT,
 535                .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
 536                .event_spec = iqs622_als_prox_events,
 537                .num_event_specs = ARRAY_SIZE(iqs622_als_prox_events),
 538        },
 539};
 540
 541static int iqs621_als_probe(struct platform_device *pdev)
 542{
 543        struct iqs62x_core *iqs62x = dev_get_drvdata(pdev->dev.parent);
 544        struct iqs621_als_private *iqs621_als;
 545        struct iio_dev *indio_dev;
 546        unsigned int val;
 547        int ret;
 548
 549        indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*iqs621_als));
 550        if (!indio_dev)
 551                return -ENOMEM;
 552
 553        iqs621_als = iio_priv(indio_dev);
 554        iqs621_als->iqs62x = iqs62x;
 555        iqs621_als->indio_dev = indio_dev;
 556
 557        if (iqs62x->dev_desc->prod_num == IQS622_PROD_NUM) {
 558                ret = regmap_read(iqs62x->regmap, IQS622_IR_THRESH_TOUCH,
 559                                  &val);
 560                if (ret)
 561                        return ret;
 562                iqs621_als->thresh_prox = val;
 563                iqs621_als->ir_flags_mask = IQS622_IR_FLAGS_TOUCH;
 564
 565                indio_dev->channels = iqs622_als_channels;
 566                indio_dev->num_channels = ARRAY_SIZE(iqs622_als_channels);
 567        } else {
 568                ret = regmap_read(iqs62x->regmap, IQS621_ALS_THRESH_LIGHT,
 569                                  &val);
 570                if (ret)
 571                        return ret;
 572                iqs621_als->thresh_light = val;
 573
 574                ret = regmap_read(iqs62x->regmap, IQS621_ALS_THRESH_DARK,
 575                                  &val);
 576                if (ret)
 577                        return ret;
 578                iqs621_als->thresh_dark = val;
 579
 580                indio_dev->channels = iqs621_als_channels;
 581                indio_dev->num_channels = ARRAY_SIZE(iqs621_als_channels);
 582        }
 583
 584        indio_dev->modes = INDIO_DIRECT_MODE;
 585        indio_dev->name = iqs62x->dev_desc->dev_name;
 586        indio_dev->info = &iqs621_als_info;
 587
 588        mutex_init(&iqs621_als->lock);
 589
 590        iqs621_als->notifier.notifier_call = iqs621_als_notifier;
 591        ret = blocking_notifier_chain_register(&iqs621_als->iqs62x->nh,
 592                                               &iqs621_als->notifier);
 593        if (ret) {
 594                dev_err(&pdev->dev, "Failed to register notifier: %d\n", ret);
 595                return ret;
 596        }
 597
 598        ret = devm_add_action_or_reset(&pdev->dev,
 599                                       iqs621_als_notifier_unregister,
 600                                       iqs621_als);
 601        if (ret)
 602                return ret;
 603
 604        return devm_iio_device_register(&pdev->dev, indio_dev);
 605}
 606
 607static struct platform_driver iqs621_als_platform_driver = {
 608        .driver = {
 609                .name = "iqs621-als",
 610        },
 611        .probe = iqs621_als_probe,
 612};
 613module_platform_driver(iqs621_als_platform_driver);
 614
 615MODULE_AUTHOR("Jeff LaBundy <jeff@labundy.com>");
 616MODULE_DESCRIPTION("Azoteq IQS621/622 Ambient Light Sensors");
 617MODULE_LICENSE("GPL");
 618MODULE_ALIAS("platform:iqs621-als");
 619