Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * All Sensors DLH series low voltage digital pressure sensors
0004  *
0005  * Copyright (c) 2019 AVL DiTEST GmbH
0006  *   Tomislav Denis <tomislav.denis@avl.com>
0007  *
0008  * Datasheet: https://www.allsensors.com/cad/DS-0355_Rev_B.PDF
0009  */
0010 
0011 #include <linux/module.h>
0012 #include <linux/delay.h>
0013 #include <linux/i2c.h>
0014 #include <linux/iio/iio.h>
0015 #include <linux/iio/buffer.h>
0016 #include <linux/iio/trigger_consumer.h>
0017 #include <linux/iio/triggered_buffer.h>
0018 #include <asm/unaligned.h>
0019 
0020 /* Commands */
0021 #define DLH_START_SINGLE    0xAA
0022 
0023 /* Status bits */
0024 #define DLH_STATUS_OK       0x40
0025 
0026 /* DLH  data format */
0027 #define DLH_NUM_READ_BYTES  7
0028 #define DLH_NUM_DATA_BYTES  3
0029 #define DLH_NUM_PR_BITS     24
0030 #define DLH_NUM_TEMP_BITS   24
0031 
0032 /* DLH  timings */
0033 #define DLH_SINGLE_DUT_MS   5
0034 
0035 enum dhl_ids {
0036     dlhl60d,
0037     dlhl60g,
0038 };
0039 
0040 struct dlh_info {
0041     u8 osdig;           /* digital offset factor */
0042     unsigned int fss;   /* full scale span (inch H2O) */
0043 };
0044 
0045 struct dlh_state {
0046     struct i2c_client *client;
0047     struct dlh_info info;
0048     bool use_interrupt;
0049     struct completion completion;
0050     u8 rx_buf[DLH_NUM_READ_BYTES];
0051 };
0052 
0053 static struct dlh_info dlh_info_tbl[] = {
0054     [dlhl60d] = {
0055         .osdig = 2,
0056         .fss = 120,
0057     },
0058     [dlhl60g] = {
0059         .osdig = 10,
0060         .fss = 60,
0061     },
0062 };
0063 
0064 
0065 static int dlh_cmd_start_single(struct dlh_state *st)
0066 {
0067     int ret;
0068 
0069     ret = i2c_smbus_write_byte(st->client, DLH_START_SINGLE);
0070     if (ret)
0071         dev_err(&st->client->dev,
0072             "%s: I2C write byte failed\n", __func__);
0073 
0074     return ret;
0075 }
0076 
0077 static int dlh_cmd_read_data(struct dlh_state *st)
0078 {
0079     int ret;
0080 
0081     ret = i2c_master_recv(st->client, st->rx_buf, DLH_NUM_READ_BYTES);
0082     if (ret < 0) {
0083         dev_err(&st->client->dev,
0084             "%s: I2C read block failed\n", __func__);
0085         return ret;
0086     }
0087 
0088     if (st->rx_buf[0] != DLH_STATUS_OK) {
0089         dev_err(&st->client->dev,
0090             "%s: invalid status 0x%02x\n", __func__, st->rx_buf[0]);
0091         return -EBUSY;
0092     }
0093 
0094     return 0;
0095 }
0096 
0097 static int dlh_start_capture_and_read(struct dlh_state *st)
0098 {
0099     int ret;
0100 
0101     if (st->use_interrupt)
0102         reinit_completion(&st->completion);
0103 
0104     ret = dlh_cmd_start_single(st);
0105     if (ret)
0106         return ret;
0107 
0108     if (st->use_interrupt) {
0109         ret = wait_for_completion_timeout(&st->completion,
0110             msecs_to_jiffies(DLH_SINGLE_DUT_MS));
0111         if (!ret) {
0112             dev_err(&st->client->dev,
0113                 "%s: conversion timed out\n", __func__);
0114             return -ETIMEDOUT;
0115         }
0116     } else {
0117         mdelay(DLH_SINGLE_DUT_MS);
0118     }
0119 
0120     return dlh_cmd_read_data(st);
0121 }
0122 
0123 static int dlh_read_direct(struct dlh_state *st,
0124     unsigned int *pressure, unsigned int *temperature)
0125 {
0126     int ret;
0127 
0128     ret = dlh_start_capture_and_read(st);
0129     if (ret)
0130         return ret;
0131 
0132     *pressure = get_unaligned_be32(&st->rx_buf[1]) >> 8;
0133     *temperature = get_unaligned_be32(&st->rx_buf[3]) &
0134         GENMASK(DLH_NUM_TEMP_BITS - 1, 0);
0135 
0136     return 0;
0137 }
0138 
0139 static int dlh_read_raw(struct iio_dev *indio_dev,
0140     struct iio_chan_spec const *channel, int *value,
0141     int *value2, long mask)
0142 {
0143     struct dlh_state *st = iio_priv(indio_dev);
0144     unsigned int pressure, temperature;
0145     int ret;
0146     s64 tmp;
0147     s32 rem;
0148 
0149     switch (mask) {
0150     case IIO_CHAN_INFO_RAW:
0151         ret = iio_device_claim_direct_mode(indio_dev);
0152         if (ret)
0153             return ret;
0154 
0155         ret = dlh_read_direct(st, &pressure, &temperature);
0156         iio_device_release_direct_mode(indio_dev);
0157         if (ret)
0158             return ret;
0159 
0160         switch (channel->type) {
0161         case IIO_PRESSURE:
0162             *value = pressure;
0163             return IIO_VAL_INT;
0164 
0165         case IIO_TEMP:
0166             *value = temperature;
0167             return IIO_VAL_INT;
0168 
0169         default:
0170             return -EINVAL;
0171         }
0172     case IIO_CHAN_INFO_SCALE:
0173         switch (channel->type) {
0174         case IIO_PRESSURE:
0175             tmp = div_s64(125LL * st->info.fss * 24909 * 100,
0176                 1 << DLH_NUM_PR_BITS);
0177             tmp = div_s64_rem(tmp, 1000000000LL, &rem);
0178             *value = tmp;
0179             *value2 = rem;
0180             return IIO_VAL_INT_PLUS_NANO;
0181 
0182         case IIO_TEMP:
0183             *value = 125 * 1000;
0184             *value2 = DLH_NUM_TEMP_BITS;
0185             return IIO_VAL_FRACTIONAL_LOG2;
0186 
0187         default:
0188             return -EINVAL;
0189         }
0190     case IIO_CHAN_INFO_OFFSET:
0191         switch (channel->type) {
0192         case IIO_PRESSURE:
0193             *value = -125 * st->info.fss * 24909;
0194             *value2 = 100 * st->info.osdig * 100000;
0195             return IIO_VAL_FRACTIONAL;
0196 
0197         case IIO_TEMP:
0198             *value = -40 * 1000;
0199             return IIO_VAL_INT;
0200 
0201         default:
0202             return -EINVAL;
0203         }
0204     }
0205 
0206     return -EINVAL;
0207 }
0208 
0209 static const struct iio_info dlh_info = {
0210     .read_raw = dlh_read_raw,
0211 };
0212 
0213 static const struct iio_chan_spec dlh_channels[] = {
0214     {
0215         .type = IIO_PRESSURE,
0216         .indexed = 1,
0217         .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
0218         .info_mask_shared_by_type =
0219             BIT(IIO_CHAN_INFO_SCALE) |
0220             BIT(IIO_CHAN_INFO_OFFSET),
0221         .scan_index = 0,
0222         .scan_type = {
0223             .sign = 'u',
0224             .realbits = DLH_NUM_PR_BITS,
0225             .storagebits = 32,
0226             .shift = 8,
0227             .endianness = IIO_BE,
0228         },
0229     }, {
0230         .type = IIO_TEMP,
0231         .indexed = 1,
0232         .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
0233         .info_mask_shared_by_type =
0234             BIT(IIO_CHAN_INFO_SCALE) |
0235             BIT(IIO_CHAN_INFO_OFFSET),
0236         .scan_index = 1,
0237         .scan_type = {
0238             .sign = 'u',
0239             .realbits = DLH_NUM_TEMP_BITS,
0240             .storagebits = 32,
0241             .shift = 8,
0242             .endianness = IIO_BE,
0243         },
0244     }
0245 };
0246 
0247 static irqreturn_t dlh_trigger_handler(int irq, void *private)
0248 {
0249     struct iio_poll_func *pf = private;
0250     struct iio_dev *indio_dev = pf->indio_dev;
0251     struct dlh_state *st = iio_priv(indio_dev);
0252     int ret;
0253     unsigned int chn, i = 0;
0254     __be32 tmp_buf[2];
0255 
0256     ret = dlh_start_capture_and_read(st);
0257     if (ret)
0258         goto out;
0259 
0260     for_each_set_bit(chn, indio_dev->active_scan_mask,
0261         indio_dev->masklength) {
0262         memcpy(tmp_buf + i,
0263             &st->rx_buf[1] + chn * DLH_NUM_DATA_BYTES,
0264             DLH_NUM_DATA_BYTES);
0265         i++;
0266     }
0267 
0268     iio_push_to_buffers(indio_dev, tmp_buf);
0269 
0270 out:
0271     iio_trigger_notify_done(indio_dev->trig);
0272 
0273     return IRQ_HANDLED;
0274 }
0275 
0276 static irqreturn_t dlh_interrupt(int irq, void *private)
0277 {
0278     struct iio_dev *indio_dev = private;
0279     struct dlh_state *st = iio_priv(indio_dev);
0280 
0281     complete(&st->completion);
0282 
0283     return IRQ_HANDLED;
0284 };
0285 
0286 static int dlh_probe(struct i2c_client *client,
0287     const struct i2c_device_id *id)
0288 {
0289     struct dlh_state *st;
0290     struct iio_dev *indio_dev;
0291     int ret;
0292 
0293     if (!i2c_check_functionality(client->adapter,
0294         I2C_FUNC_I2C | I2C_FUNC_SMBUS_WRITE_BYTE)) {
0295         dev_err(&client->dev,
0296             "adapter doesn't support required i2c functionality\n");
0297         return -EOPNOTSUPP;
0298     }
0299 
0300     indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*st));
0301     if (!indio_dev) {
0302         dev_err(&client->dev, "failed to allocate iio device\n");
0303         return -ENOMEM;
0304     }
0305 
0306     i2c_set_clientdata(client, indio_dev);
0307 
0308     st = iio_priv(indio_dev);
0309     st->info = dlh_info_tbl[id->driver_data];
0310     st->client = client;
0311     st->use_interrupt = false;
0312 
0313     indio_dev->name = id->name;
0314     indio_dev->info = &dlh_info;
0315     indio_dev->modes = INDIO_DIRECT_MODE;
0316     indio_dev->channels =  dlh_channels;
0317     indio_dev->num_channels = ARRAY_SIZE(dlh_channels);
0318 
0319     if (client->irq > 0) {
0320         ret = devm_request_threaded_irq(&client->dev, client->irq,
0321             dlh_interrupt, NULL,
0322             IRQF_TRIGGER_RISING | IRQF_ONESHOT,
0323             id->name, indio_dev);
0324         if (ret) {
0325             dev_err(&client->dev, "failed to allocate threaded irq");
0326             return ret;
0327         }
0328 
0329         st->use_interrupt = true;
0330         init_completion(&st->completion);
0331     }
0332 
0333     ret = devm_iio_triggered_buffer_setup(&client->dev, indio_dev,
0334         NULL, &dlh_trigger_handler, NULL);
0335     if (ret) {
0336         dev_err(&client->dev, "failed to setup iio buffer\n");
0337         return ret;
0338     }
0339 
0340     ret = devm_iio_device_register(&client->dev, indio_dev);
0341     if (ret)
0342         dev_err(&client->dev, "failed to register iio device\n");
0343 
0344     return ret;
0345 }
0346 
0347 static const struct of_device_id dlh_of_match[] = {
0348     { .compatible = "asc,dlhl60d" },
0349     { .compatible = "asc,dlhl60g" },
0350     {}
0351 };
0352 MODULE_DEVICE_TABLE(of, dlh_of_match);
0353 
0354 static const struct i2c_device_id dlh_id[] = {
0355     { "dlhl60d",    dlhl60d },
0356     { "dlhl60g",    dlhl60g },
0357     {}
0358 };
0359 MODULE_DEVICE_TABLE(i2c, dlh_id);
0360 
0361 static struct i2c_driver dlh_driver = {
0362     .driver = {
0363         .name = "dlhl60d",
0364         .of_match_table = dlh_of_match,
0365     },
0366     .probe = dlh_probe,
0367     .id_table = dlh_id,
0368 };
0369 module_i2c_driver(dlh_driver);
0370 
0371 MODULE_AUTHOR("Tomislav Denis <tomislav.denis@avl.com>");
0372 MODULE_DESCRIPTION("Driver for All Sensors DLH series pressure sensors");
0373 MODULE_LICENSE("GPL v2");