linux/drivers/iio/pressure/dlhl60d.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * All Sensors DLH series low voltage digital pressure sensors
   4 *
   5 * Copyright (c) 2019 AVL DiTEST GmbH
   6 *   Tomislav Denis <tomislav.denis@avl.com>
   7 *
   8 * Datasheet: https://www.allsensors.com/cad/DS-0355_Rev_B.PDF
   9 */
  10
  11#include <linux/module.h>
  12#include <linux/delay.h>
  13#include <linux/i2c.h>
  14#include <linux/iio/iio.h>
  15#include <linux/iio/buffer.h>
  16#include <linux/iio/trigger_consumer.h>
  17#include <linux/iio/triggered_buffer.h>
  18#include <asm/unaligned.h>
  19
  20/* Commands */
  21#define DLH_START_SINGLE    0xAA
  22
  23/* Status bits */
  24#define DLH_STATUS_OK       0x40
  25
  26/* DLH  data format */
  27#define DLH_NUM_READ_BYTES  7
  28#define DLH_NUM_DATA_BYTES  3
  29#define DLH_NUM_PR_BITS     24
  30#define DLH_NUM_TEMP_BITS   24
  31
  32/* DLH  timings */
  33#define DLH_SINGLE_DUT_MS   5
  34
  35enum dhl_ids {
  36        dlhl60d,
  37        dlhl60g,
  38};
  39
  40struct dlh_info {
  41        u8 osdig;           /* digital offset factor */
  42        unsigned int fss;   /* full scale span (inch H2O) */
  43};
  44
  45struct dlh_state {
  46        struct i2c_client *client;
  47        struct dlh_info info;
  48        bool use_interrupt;
  49        struct completion completion;
  50        u8 rx_buf[DLH_NUM_READ_BYTES] ____cacheline_aligned;
  51};
  52
  53static struct dlh_info dlh_info_tbl[] = {
  54        [dlhl60d] = {
  55                .osdig = 2,
  56                .fss = 120,
  57        },
  58        [dlhl60g] = {
  59                .osdig = 10,
  60                .fss = 60,
  61        },
  62};
  63
  64
  65static int dlh_cmd_start_single(struct dlh_state *st)
  66{
  67        int ret;
  68
  69        ret = i2c_smbus_write_byte(st->client, DLH_START_SINGLE);
  70        if (ret)
  71                dev_err(&st->client->dev,
  72                        "%s: I2C write byte failed\n", __func__);
  73
  74        return ret;
  75}
  76
  77static int dlh_cmd_read_data(struct dlh_state *st)
  78{
  79        int ret;
  80
  81        ret = i2c_master_recv(st->client, st->rx_buf, DLH_NUM_READ_BYTES);
  82        if (ret < 0) {
  83                dev_err(&st->client->dev,
  84                        "%s: I2C read block failed\n", __func__);
  85                return ret;
  86        }
  87
  88        if (st->rx_buf[0] != DLH_STATUS_OK) {
  89                dev_err(&st->client->dev,
  90                        "%s: invalid status 0x%02x\n", __func__, st->rx_buf[0]);
  91                return -EBUSY;
  92        }
  93
  94        return 0;
  95}
  96
  97static int dlh_start_capture_and_read(struct dlh_state *st)
  98{
  99        int ret;
 100
 101        if (st->use_interrupt)
 102                reinit_completion(&st->completion);
 103
 104        ret = dlh_cmd_start_single(st);
 105        if (ret)
 106                return ret;
 107
 108        if (st->use_interrupt) {
 109                ret = wait_for_completion_timeout(&st->completion,
 110                        msecs_to_jiffies(DLH_SINGLE_DUT_MS));
 111                if (!ret) {
 112                        dev_err(&st->client->dev,
 113                                "%s: conversion timed out\n", __func__);
 114                        return -ETIMEDOUT;
 115                }
 116        } else {
 117                mdelay(DLH_SINGLE_DUT_MS);
 118        }
 119
 120        return dlh_cmd_read_data(st);
 121}
 122
 123static int dlh_read_direct(struct dlh_state *st,
 124        unsigned int *pressure, unsigned int *temperature)
 125{
 126        int ret;
 127
 128        ret = dlh_start_capture_and_read(st);
 129        if (ret)
 130                return ret;
 131
 132        *pressure = get_unaligned_be32(&st->rx_buf[1]) >> 8;
 133        *temperature = get_unaligned_be32(&st->rx_buf[3]) &
 134                GENMASK(DLH_NUM_TEMP_BITS - 1, 0);
 135
 136        return 0;
 137}
 138
 139static int dlh_read_raw(struct iio_dev *indio_dev,
 140        struct iio_chan_spec const *channel, int *value,
 141        int *value2, long mask)
 142{
 143        struct dlh_state *st = iio_priv(indio_dev);
 144        unsigned int pressure, temperature;
 145        int ret;
 146        s64 tmp;
 147        s32 rem;
 148
 149        switch (mask) {
 150        case IIO_CHAN_INFO_RAW:
 151                ret = iio_device_claim_direct_mode(indio_dev);
 152                if (ret)
 153                        return ret;
 154
 155                ret = dlh_read_direct(st, &pressure, &temperature);
 156                iio_device_release_direct_mode(indio_dev);
 157                if (ret)
 158                        return ret;
 159
 160                switch (channel->type) {
 161                case IIO_PRESSURE:
 162                        *value = pressure;
 163                        return IIO_VAL_INT;
 164
 165                case IIO_TEMP:
 166                        *value = temperature;
 167                        return IIO_VAL_INT;
 168
 169                default:
 170                        return -EINVAL;
 171                }
 172        case IIO_CHAN_INFO_SCALE:
 173                switch (channel->type) {
 174                case IIO_PRESSURE:
 175                        tmp = div_s64(125LL * st->info.fss * 24909 * 100,
 176                                1 << DLH_NUM_PR_BITS);
 177                        tmp = div_s64_rem(tmp, 1000000000LL, &rem);
 178                        *value = tmp;
 179                        *value2 = rem;
 180                        return IIO_VAL_INT_PLUS_NANO;
 181
 182                case IIO_TEMP:
 183                        *value = 125 * 1000;
 184                        *value2 = DLH_NUM_TEMP_BITS;
 185                        return IIO_VAL_FRACTIONAL_LOG2;
 186
 187                default:
 188                        return -EINVAL;
 189                }
 190        case IIO_CHAN_INFO_OFFSET:
 191                switch (channel->type) {
 192                case IIO_PRESSURE:
 193                        *value = -125 * st->info.fss * 24909;
 194                        *value2 = 100 * st->info.osdig * 100000;
 195                        return IIO_VAL_FRACTIONAL;
 196
 197                case IIO_TEMP:
 198                        *value = -40 * 1000;
 199                        return IIO_VAL_INT;
 200
 201                default:
 202                        return -EINVAL;
 203                }
 204        }
 205
 206        return -EINVAL;
 207}
 208
 209static const struct iio_info dlh_info = {
 210        .read_raw = dlh_read_raw,
 211};
 212
 213static const struct iio_chan_spec dlh_channels[] = {
 214        {
 215                .type = IIO_PRESSURE,
 216                .indexed = 1,
 217                .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
 218                .info_mask_shared_by_type =
 219                        BIT(IIO_CHAN_INFO_SCALE) |
 220                        BIT(IIO_CHAN_INFO_OFFSET),
 221                .scan_index = 0,
 222                .scan_type = {
 223                        .sign = 'u',
 224                        .realbits = DLH_NUM_PR_BITS,
 225                        .storagebits = 32,
 226                        .shift = 8,
 227                        .endianness = IIO_BE,
 228                },
 229        }, {
 230                .type = IIO_TEMP,
 231                .indexed = 1,
 232                .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
 233                .info_mask_shared_by_type =
 234                        BIT(IIO_CHAN_INFO_SCALE) |
 235                        BIT(IIO_CHAN_INFO_OFFSET),
 236                .scan_index = 1,
 237                .scan_type = {
 238                        .sign = 'u',
 239                        .realbits = DLH_NUM_TEMP_BITS,
 240                        .storagebits = 32,
 241                        .shift = 8,
 242                        .endianness = IIO_BE,
 243                },
 244        }
 245};
 246
 247static irqreturn_t dlh_trigger_handler(int irq, void *private)
 248{
 249        struct iio_poll_func *pf = private;
 250        struct iio_dev *indio_dev = pf->indio_dev;
 251        struct dlh_state *st = iio_priv(indio_dev);
 252        int ret;
 253        unsigned int chn, i = 0;
 254        __be32 tmp_buf[2];
 255
 256        ret = dlh_start_capture_and_read(st);
 257        if (ret)
 258                goto out;
 259
 260        for_each_set_bit(chn, indio_dev->active_scan_mask,
 261                indio_dev->masklength) {
 262                memcpy(tmp_buf + i,
 263                        &st->rx_buf[1] + chn * DLH_NUM_DATA_BYTES,
 264                        DLH_NUM_DATA_BYTES);
 265                i++;
 266        }
 267
 268        iio_push_to_buffers(indio_dev, tmp_buf);
 269
 270out:
 271        iio_trigger_notify_done(indio_dev->trig);
 272
 273        return IRQ_HANDLED;
 274}
 275
 276static irqreturn_t dlh_interrupt(int irq, void *private)
 277{
 278        struct iio_dev *indio_dev = private;
 279        struct dlh_state *st = iio_priv(indio_dev);
 280
 281        complete(&st->completion);
 282
 283        return IRQ_HANDLED;
 284};
 285
 286static int dlh_probe(struct i2c_client *client,
 287        const struct i2c_device_id *id)
 288{
 289        struct dlh_state *st;
 290        struct iio_dev *indio_dev;
 291        int ret;
 292
 293        if (!i2c_check_functionality(client->adapter,
 294                I2C_FUNC_I2C | I2C_FUNC_SMBUS_WRITE_BYTE)) {
 295                dev_err(&client->dev,
 296                        "adapter doesn't support required i2c functionality\n");
 297                return -EOPNOTSUPP;
 298        }
 299
 300        indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*st));
 301        if (!indio_dev) {
 302                dev_err(&client->dev, "failed to allocate iio device\n");
 303                return -ENOMEM;
 304        }
 305
 306        i2c_set_clientdata(client, indio_dev);
 307
 308        st = iio_priv(indio_dev);
 309        st->info = dlh_info_tbl[id->driver_data];
 310        st->client = client;
 311        st->use_interrupt = false;
 312
 313        indio_dev->name = id->name;
 314        indio_dev->info = &dlh_info;
 315        indio_dev->modes = INDIO_DIRECT_MODE;
 316        indio_dev->channels =  dlh_channels;
 317        indio_dev->num_channels = ARRAY_SIZE(dlh_channels);
 318
 319        if (client->irq > 0) {
 320                ret = devm_request_threaded_irq(&client->dev, client->irq,
 321                        dlh_interrupt, NULL,
 322                        IRQF_TRIGGER_RISING | IRQF_ONESHOT,
 323                        id->name, indio_dev);
 324                if (ret) {
 325                        dev_err(&client->dev, "failed to allocate threaded irq");
 326                        return ret;
 327                }
 328
 329                st->use_interrupt = true;
 330                init_completion(&st->completion);
 331        }
 332
 333        ret = devm_iio_triggered_buffer_setup(&client->dev, indio_dev,
 334                NULL, &dlh_trigger_handler, NULL);
 335        if (ret) {
 336                dev_err(&client->dev, "failed to setup iio buffer\n");
 337                return ret;
 338        }
 339
 340        ret = devm_iio_device_register(&client->dev, indio_dev);
 341        if (ret)
 342                dev_err(&client->dev, "failed to register iio device\n");
 343
 344        return ret;
 345}
 346
 347static const struct of_device_id dlh_of_match[] = {
 348        { .compatible = "asc,dlhl60d" },
 349        { .compatible = "asc,dlhl60g" },
 350        {}
 351};
 352MODULE_DEVICE_TABLE(of, dlh_of_match);
 353
 354static const struct i2c_device_id dlh_id[] = {
 355        { "dlhl60d",    dlhl60d },
 356        { "dlhl60g",    dlhl60g },
 357        {}
 358};
 359MODULE_DEVICE_TABLE(i2c, dlh_id);
 360
 361static struct i2c_driver dlh_driver = {
 362        .driver = {
 363                .name = "dlhl60d",
 364                .of_match_table = dlh_of_match,
 365        },
 366        .probe = dlh_probe,
 367        .id_table = dlh_id,
 368};
 369module_i2c_driver(dlh_driver);
 370
 371MODULE_AUTHOR("Tomislav Denis <tomislav.denis@avl.com>");
 372MODULE_DESCRIPTION("Driver for All Sensors DLH series pressure sensors");
 373MODULE_LICENSE("GPL v2");
 374