linux/drivers/iio/light/st_uvis25_core.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * STMicroelectronics uvis25 sensor driver
   4 *
   5 * Copyright 2017 STMicroelectronics Inc.
   6 *
   7 * Lorenzo Bianconi <lorenzo.bianconi83@gmail.com>
   8 */
   9
  10#include <linux/kernel.h>
  11#include <linux/module.h>
  12#include <linux/device.h>
  13#include <linux/iio/sysfs.h>
  14#include <linux/delay.h>
  15#include <linux/pm.h>
  16#include <linux/interrupt.h>
  17#include <linux/irqreturn.h>
  18#include <linux/iio/trigger.h>
  19#include <linux/iio/trigger_consumer.h>
  20#include <linux/iio/triggered_buffer.h>
  21#include <linux/iio/buffer.h>
  22#include <linux/regmap.h>
  23
  24#include "st_uvis25.h"
  25
  26#define ST_UVIS25_REG_WHOAMI_ADDR       0x0f
  27#define ST_UVIS25_REG_WHOAMI_VAL        0xca
  28#define ST_UVIS25_REG_CTRL1_ADDR        0x20
  29#define ST_UVIS25_REG_ODR_MASK          BIT(0)
  30#define ST_UVIS25_REG_BDU_MASK          BIT(1)
  31#define ST_UVIS25_REG_CTRL2_ADDR        0x21
  32#define ST_UVIS25_REG_BOOT_MASK         BIT(7)
  33#define ST_UVIS25_REG_CTRL3_ADDR        0x22
  34#define ST_UVIS25_REG_HL_MASK           BIT(7)
  35#define ST_UVIS25_REG_STATUS_ADDR       0x27
  36#define ST_UVIS25_REG_UV_DA_MASK        BIT(0)
  37#define ST_UVIS25_REG_OUT_ADDR          0x28
  38
  39static const struct iio_chan_spec st_uvis25_channels[] = {
  40        {
  41                .type = IIO_UVINDEX,
  42                .address = ST_UVIS25_REG_OUT_ADDR,
  43                .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
  44                .scan_index = 0,
  45                .scan_type = {
  46                        .sign = 'u',
  47                        .realbits = 8,
  48                        .storagebits = 8,
  49                },
  50        },
  51        IIO_CHAN_SOFT_TIMESTAMP(1),
  52};
  53
  54static int st_uvis25_check_whoami(struct st_uvis25_hw *hw)
  55{
  56        int err, data;
  57
  58        err = regmap_read(hw->regmap, ST_UVIS25_REG_WHOAMI_ADDR, &data);
  59        if (err < 0) {
  60                dev_err(regmap_get_device(hw->regmap),
  61                        "failed to read whoami register\n");
  62                return err;
  63        }
  64
  65        if (data != ST_UVIS25_REG_WHOAMI_VAL) {
  66                dev_err(regmap_get_device(hw->regmap),
  67                        "wrong whoami {%02x vs %02x}\n",
  68                        data, ST_UVIS25_REG_WHOAMI_VAL);
  69                return -ENODEV;
  70        }
  71
  72        return 0;
  73}
  74
  75static int st_uvis25_set_enable(struct st_uvis25_hw *hw, bool enable)
  76{
  77        int err;
  78
  79        err = regmap_update_bits(hw->regmap, ST_UVIS25_REG_CTRL1_ADDR,
  80                                 ST_UVIS25_REG_ODR_MASK, enable);
  81        if (err < 0)
  82                return err;
  83
  84        hw->enabled = enable;
  85
  86        return 0;
  87}
  88
  89static int st_uvis25_read_oneshot(struct st_uvis25_hw *hw, u8 addr, int *val)
  90{
  91        int err;
  92
  93        err = st_uvis25_set_enable(hw, true);
  94        if (err < 0)
  95                return err;
  96
  97        msleep(1500);
  98
  99        /*
 100         * in order to avoid possible race conditions with interrupt
 101         * generation, disable the sensor first and then poll output
 102         * register. That sequence guarantees the interrupt will be reset
 103         * when irq line is unmasked
 104         */
 105        err = st_uvis25_set_enable(hw, false);
 106        if (err < 0)
 107                return err;
 108
 109        err = regmap_read(hw->regmap, addr, val);
 110
 111        return err < 0 ? err : IIO_VAL_INT;
 112}
 113
 114static int st_uvis25_read_raw(struct iio_dev *iio_dev,
 115                              struct iio_chan_spec const *ch,
 116                              int *val, int *val2, long mask)
 117{
 118        int ret;
 119
 120        ret = iio_device_claim_direct_mode(iio_dev);
 121        if (ret)
 122                return ret;
 123
 124        switch (mask) {
 125        case IIO_CHAN_INFO_PROCESSED: {
 126                struct st_uvis25_hw *hw = iio_priv(iio_dev);
 127
 128                /*
 129                 * mask irq line during oneshot read since the sensor
 130                 * does not export the capability to disable data-ready line
 131                 * in the register map and it is enabled by default.
 132                 * If the line is unmasked during read_raw() it will be set
 133                 * active and never reset since the trigger is disabled
 134                 */
 135                if (hw->irq > 0)
 136                        disable_irq(hw->irq);
 137                ret = st_uvis25_read_oneshot(hw, ch->address, val);
 138                if (hw->irq > 0)
 139                        enable_irq(hw->irq);
 140                break;
 141        }
 142        default:
 143                ret = -EINVAL;
 144                break;
 145        }
 146
 147        iio_device_release_direct_mode(iio_dev);
 148
 149        return ret;
 150}
 151
 152static irqreturn_t st_uvis25_trigger_handler_thread(int irq, void *private)
 153{
 154        struct st_uvis25_hw *hw = private;
 155        int err, status;
 156
 157        err = regmap_read(hw->regmap, ST_UVIS25_REG_STATUS_ADDR, &status);
 158        if (err < 0)
 159                return IRQ_HANDLED;
 160
 161        if (!(status & ST_UVIS25_REG_UV_DA_MASK))
 162                return IRQ_NONE;
 163
 164        iio_trigger_poll_chained(hw->trig);
 165
 166        return IRQ_HANDLED;
 167}
 168
 169static int st_uvis25_allocate_trigger(struct iio_dev *iio_dev)
 170{
 171        struct st_uvis25_hw *hw = iio_priv(iio_dev);
 172        struct device *dev = regmap_get_device(hw->regmap);
 173        bool irq_active_low = false;
 174        unsigned long irq_type;
 175        int err;
 176
 177        irq_type = irqd_get_trigger_type(irq_get_irq_data(hw->irq));
 178
 179        switch (irq_type) {
 180        case IRQF_TRIGGER_HIGH:
 181        case IRQF_TRIGGER_RISING:
 182                break;
 183        case IRQF_TRIGGER_LOW:
 184        case IRQF_TRIGGER_FALLING:
 185                irq_active_low = true;
 186                break;
 187        default:
 188                dev_info(dev, "mode %lx unsupported\n", irq_type);
 189                return -EINVAL;
 190        }
 191
 192        err = regmap_update_bits(hw->regmap, ST_UVIS25_REG_CTRL3_ADDR,
 193                                 ST_UVIS25_REG_HL_MASK, irq_active_low);
 194        if (err < 0)
 195                return err;
 196
 197        err = devm_request_threaded_irq(dev, hw->irq, NULL,
 198                                        st_uvis25_trigger_handler_thread,
 199                                        irq_type | IRQF_ONESHOT,
 200                                        iio_dev->name, hw);
 201        if (err) {
 202                dev_err(dev, "failed to request trigger irq %d\n",
 203                        hw->irq);
 204                return err;
 205        }
 206
 207        hw->trig = devm_iio_trigger_alloc(dev, "%s-trigger",
 208                                          iio_dev->name);
 209        if (!hw->trig)
 210                return -ENOMEM;
 211
 212        iio_trigger_set_drvdata(hw->trig, iio_dev);
 213        hw->trig->dev.parent = dev;
 214
 215        return devm_iio_trigger_register(dev, hw->trig);
 216}
 217
 218static int st_uvis25_buffer_preenable(struct iio_dev *iio_dev)
 219{
 220        return st_uvis25_set_enable(iio_priv(iio_dev), true);
 221}
 222
 223static int st_uvis25_buffer_postdisable(struct iio_dev *iio_dev)
 224{
 225        return st_uvis25_set_enable(iio_priv(iio_dev), false);
 226}
 227
 228static const struct iio_buffer_setup_ops st_uvis25_buffer_ops = {
 229        .preenable = st_uvis25_buffer_preenable,
 230        .postenable = iio_triggered_buffer_postenable,
 231        .predisable = iio_triggered_buffer_predisable,
 232        .postdisable = st_uvis25_buffer_postdisable,
 233};
 234
 235static irqreturn_t st_uvis25_buffer_handler_thread(int irq, void *p)
 236{
 237        u8 buffer[ALIGN(sizeof(u8), sizeof(s64)) + sizeof(s64)];
 238        struct iio_poll_func *pf = p;
 239        struct iio_dev *iio_dev = pf->indio_dev;
 240        struct st_uvis25_hw *hw = iio_priv(iio_dev);
 241        int err;
 242
 243        err = regmap_read(hw->regmap, ST_UVIS25_REG_OUT_ADDR, (int *)buffer);
 244        if (err < 0)
 245                goto out;
 246
 247        iio_push_to_buffers_with_timestamp(iio_dev, buffer,
 248                                           iio_get_time_ns(iio_dev));
 249
 250out:
 251        iio_trigger_notify_done(hw->trig);
 252
 253        return IRQ_HANDLED;
 254}
 255
 256static int st_uvis25_allocate_buffer(struct iio_dev *iio_dev)
 257{
 258        struct st_uvis25_hw *hw = iio_priv(iio_dev);
 259
 260        return devm_iio_triggered_buffer_setup(regmap_get_device(hw->regmap),
 261                                               iio_dev, NULL,
 262                                               st_uvis25_buffer_handler_thread,
 263                                               &st_uvis25_buffer_ops);
 264}
 265
 266static const struct iio_info st_uvis25_info = {
 267        .read_raw = st_uvis25_read_raw,
 268};
 269
 270static int st_uvis25_init_sensor(struct st_uvis25_hw *hw)
 271{
 272        int err;
 273
 274        err = regmap_update_bits(hw->regmap, ST_UVIS25_REG_CTRL2_ADDR,
 275                                 ST_UVIS25_REG_BOOT_MASK, 1);
 276        if (err < 0)
 277                return err;
 278
 279        msleep(2000);
 280
 281        return regmap_update_bits(hw->regmap, ST_UVIS25_REG_CTRL1_ADDR,
 282                                  ST_UVIS25_REG_BDU_MASK, 1);
 283}
 284
 285int st_uvis25_probe(struct device *dev, int irq, struct regmap *regmap)
 286{
 287        struct st_uvis25_hw *hw;
 288        struct iio_dev *iio_dev;
 289        int err;
 290
 291        iio_dev = devm_iio_device_alloc(dev, sizeof(*hw));
 292        if (!iio_dev)
 293                return -ENOMEM;
 294
 295        dev_set_drvdata(dev, (void *)iio_dev);
 296
 297        hw = iio_priv(iio_dev);
 298        hw->irq = irq;
 299        hw->regmap = regmap;
 300
 301        err = st_uvis25_check_whoami(hw);
 302        if (err < 0)
 303                return err;
 304
 305        iio_dev->modes = INDIO_DIRECT_MODE;
 306        iio_dev->dev.parent = dev;
 307        iio_dev->channels = st_uvis25_channels;
 308        iio_dev->num_channels = ARRAY_SIZE(st_uvis25_channels);
 309        iio_dev->name = ST_UVIS25_DEV_NAME;
 310        iio_dev->info = &st_uvis25_info;
 311
 312        err = st_uvis25_init_sensor(hw);
 313        if (err < 0)
 314                return err;
 315
 316        if (hw->irq > 0) {
 317                err = st_uvis25_allocate_buffer(iio_dev);
 318                if (err < 0)
 319                        return err;
 320
 321                err = st_uvis25_allocate_trigger(iio_dev);
 322                if (err)
 323                        return err;
 324        }
 325
 326        return devm_iio_device_register(dev, iio_dev);
 327}
 328EXPORT_SYMBOL(st_uvis25_probe);
 329
 330static int __maybe_unused st_uvis25_suspend(struct device *dev)
 331{
 332        struct iio_dev *iio_dev = dev_get_drvdata(dev);
 333        struct st_uvis25_hw *hw = iio_priv(iio_dev);
 334
 335        return regmap_update_bits(hw->regmap, ST_UVIS25_REG_CTRL1_ADDR,
 336                                  ST_UVIS25_REG_ODR_MASK, 0);
 337}
 338
 339static int __maybe_unused st_uvis25_resume(struct device *dev)
 340{
 341        struct iio_dev *iio_dev = dev_get_drvdata(dev);
 342        struct st_uvis25_hw *hw = iio_priv(iio_dev);
 343
 344        if (hw->enabled)
 345                return regmap_update_bits(hw->regmap, ST_UVIS25_REG_CTRL1_ADDR,
 346                                          ST_UVIS25_REG_ODR_MASK, 1);
 347
 348        return 0;
 349}
 350
 351const struct dev_pm_ops st_uvis25_pm_ops = {
 352        SET_SYSTEM_SLEEP_PM_OPS(st_uvis25_suspend, st_uvis25_resume)
 353};
 354EXPORT_SYMBOL(st_uvis25_pm_ops);
 355
 356MODULE_AUTHOR("Lorenzo Bianconi <lorenzo.bianconi83@gmail.com>");
 357MODULE_DESCRIPTION("STMicroelectronics uvis25 sensor driver");
 358MODULE_LICENSE("GPL v2");
 359