linux/drivers/iio/pressure/ms5611_core.c
<<
>>
Prefs
   1/*
   2 * MS5611 pressure and temperature sensor driver
   3 *
   4 * Copyright (c) Tomasz Duszynski <tduszyns@gmail.com>
   5 *
   6 * This program is free software; you can redistribute it and/or modify
   7 * it under the terms of the GNU General Public License version 2 as
   8 * published by the Free Software Foundation.
   9 *
  10 * Data sheet:
  11 *  http://www.meas-spec.com/downloads/MS5611-01BA03.pdf
  12 *  http://www.meas-spec.com/downloads/MS5607-02BA03.pdf
  13 *
  14 */
  15
  16#include <linux/module.h>
  17#include <linux/iio/iio.h>
  18#include <linux/delay.h>
  19#include <linux/regulator/consumer.h>
  20
  21#include <linux/iio/buffer.h>
  22#include <linux/iio/triggered_buffer.h>
  23#include <linux/iio/trigger_consumer.h>
  24#include "ms5611.h"
  25
  26static bool ms5611_prom_is_valid(u16 *prom, size_t len)
  27{
  28        int i, j;
  29        uint16_t crc = 0, crc_orig = prom[7] & 0x000F;
  30
  31        prom[7] &= 0xFF00;
  32
  33        for (i = 0; i < len * 2; i++) {
  34                if (i % 2 == 1)
  35                        crc ^= prom[i >> 1] & 0x00FF;
  36                else
  37                        crc ^= prom[i >> 1] >> 8;
  38
  39                for (j = 0; j < 8; j++) {
  40                        if (crc & 0x8000)
  41                                crc = (crc << 1) ^ 0x3000;
  42                        else
  43                                crc <<= 1;
  44                }
  45        }
  46
  47        crc = (crc >> 12) & 0x000F;
  48
  49        return crc_orig != 0x0000 && crc == crc_orig;
  50}
  51
  52static int ms5611_read_prom(struct iio_dev *indio_dev)
  53{
  54        int ret, i;
  55        struct ms5611_state *st = iio_priv(indio_dev);
  56
  57        for (i = 0; i < MS5611_PROM_WORDS_NB; i++) {
  58                ret = st->read_prom_word(&indio_dev->dev,
  59                                         i, &st->chip_info->prom[i]);
  60                if (ret < 0) {
  61                        dev_err(&indio_dev->dev,
  62                                "failed to read prom at %d\n", i);
  63                        return ret;
  64                }
  65        }
  66
  67        if (!ms5611_prom_is_valid(st->chip_info->prom, MS5611_PROM_WORDS_NB)) {
  68                dev_err(&indio_dev->dev, "PROM integrity check failed\n");
  69                return -ENODEV;
  70        }
  71
  72        return 0;
  73}
  74
  75static int ms5611_read_temp_and_pressure(struct iio_dev *indio_dev,
  76                                         s32 *temp, s32 *pressure)
  77{
  78        int ret;
  79        struct ms5611_state *st = iio_priv(indio_dev);
  80
  81        ret = st->read_adc_temp_and_pressure(&indio_dev->dev, temp, pressure);
  82        if (ret < 0) {
  83                dev_err(&indio_dev->dev,
  84                        "failed to read temperature and pressure\n");
  85                return ret;
  86        }
  87
  88        return st->chip_info->temp_and_pressure_compensate(st->chip_info,
  89                                                           temp, pressure);
  90}
  91
  92static int ms5611_temp_and_pressure_compensate(struct ms5611_chip_info *chip_info,
  93                                               s32 *temp, s32 *pressure)
  94{
  95        s32 t = *temp, p = *pressure;
  96        s64 off, sens, dt;
  97
  98        dt = t - (chip_info->prom[5] << 8);
  99        off = ((s64)chip_info->prom[2] << 16) + ((chip_info->prom[4] * dt) >> 7);
 100        sens = ((s64)chip_info->prom[1] << 15) + ((chip_info->prom[3] * dt) >> 8);
 101
 102        t = 2000 + ((chip_info->prom[6] * dt) >> 23);
 103        if (t < 2000) {
 104                s64 off2, sens2, t2;
 105
 106                t2 = (dt * dt) >> 31;
 107                off2 = (5 * (t - 2000) * (t - 2000)) >> 1;
 108                sens2 = off2 >> 1;
 109
 110                if (t < -1500) {
 111                        s64 tmp = (t + 1500) * (t + 1500);
 112
 113                        off2 += 7 * tmp;
 114                        sens2 += (11 * tmp) >> 1;
 115                }
 116
 117                t -= t2;
 118                off -= off2;
 119                sens -= sens2;
 120        }
 121
 122        *temp = t;
 123        *pressure = (((p * sens) >> 21) - off) >> 15;
 124
 125        return 0;
 126}
 127
 128static int ms5607_temp_and_pressure_compensate(struct ms5611_chip_info *chip_info,
 129                                               s32 *temp, s32 *pressure)
 130{
 131        s32 t = *temp, p = *pressure;
 132        s64 off, sens, dt;
 133
 134        dt = t - (chip_info->prom[5] << 8);
 135        off = ((s64)chip_info->prom[2] << 17) + ((chip_info->prom[4] * dt) >> 6);
 136        sens = ((s64)chip_info->prom[1] << 16) + ((chip_info->prom[3] * dt) >> 7);
 137
 138        t = 2000 + ((chip_info->prom[6] * dt) >> 23);
 139        if (t < 2000) {
 140                s64 off2, sens2, t2, tmp;
 141
 142                t2 = (dt * dt) >> 31;
 143                tmp = (t - 2000) * (t - 2000);
 144                off2 = (61 * tmp) >> 4;
 145                sens2 = tmp << 1;
 146
 147                if (t < -1500) {
 148                        tmp = (t + 1500) * (t + 1500);
 149                        off2 += 15 * tmp;
 150                        sens2 += 8 * tmp;
 151                }
 152
 153                t -= t2;
 154                off -= off2;
 155                sens -= sens2;
 156        }
 157
 158        *temp = t;
 159        *pressure = (((p * sens) >> 21) - off) >> 15;
 160
 161        return 0;
 162}
 163
 164static int ms5611_reset(struct iio_dev *indio_dev)
 165{
 166        int ret;
 167        struct ms5611_state *st = iio_priv(indio_dev);
 168
 169        ret = st->reset(&indio_dev->dev);
 170        if (ret < 0) {
 171                dev_err(&indio_dev->dev, "failed to reset device\n");
 172                return ret;
 173        }
 174
 175        usleep_range(3000, 4000);
 176
 177        return 0;
 178}
 179
 180static irqreturn_t ms5611_trigger_handler(int irq, void *p)
 181{
 182        struct iio_poll_func *pf = p;
 183        struct iio_dev *indio_dev = pf->indio_dev;
 184        struct ms5611_state *st = iio_priv(indio_dev);
 185        s32 buf[4]; /* s32 (pressure) + s32 (temp) + 2 * s32 (timestamp) */
 186        int ret;
 187
 188        mutex_lock(&st->lock);
 189        ret = ms5611_read_temp_and_pressure(indio_dev, &buf[1], &buf[0]);
 190        mutex_unlock(&st->lock);
 191        if (ret < 0)
 192                goto err;
 193
 194        iio_push_to_buffers_with_timestamp(indio_dev, buf, iio_get_time_ns());
 195
 196err:
 197        iio_trigger_notify_done(indio_dev->trig);
 198
 199        return IRQ_HANDLED;
 200}
 201
 202static int ms5611_read_raw(struct iio_dev *indio_dev,
 203                           struct iio_chan_spec const *chan,
 204                           int *val, int *val2, long mask)
 205{
 206        int ret;
 207        s32 temp, pressure;
 208        struct ms5611_state *st = iio_priv(indio_dev);
 209
 210        switch (mask) {
 211        case IIO_CHAN_INFO_PROCESSED:
 212                mutex_lock(&st->lock);
 213                ret = ms5611_read_temp_and_pressure(indio_dev,
 214                                                    &temp, &pressure);
 215                mutex_unlock(&st->lock);
 216                if (ret < 0)
 217                        return ret;
 218
 219                switch (chan->type) {
 220                case IIO_TEMP:
 221                        *val = temp * 10;
 222                        return IIO_VAL_INT;
 223                case IIO_PRESSURE:
 224                        *val = pressure / 1000;
 225                        *val2 = (pressure % 1000) * 1000;
 226                        return IIO_VAL_INT_PLUS_MICRO;
 227                default:
 228                        return -EINVAL;
 229                }
 230        case IIO_CHAN_INFO_SCALE:
 231                switch (chan->type) {
 232                case IIO_TEMP:
 233                        *val = 10;
 234                        return IIO_VAL_INT;
 235                case IIO_PRESSURE:
 236                        *val = 0;
 237                        *val2 = 1000;
 238                        return IIO_VAL_INT_PLUS_MICRO;
 239                default:
 240                        return -EINVAL;
 241                }
 242        }
 243
 244        return -EINVAL;
 245}
 246
 247static const unsigned long ms5611_scan_masks[] = {0x3, 0};
 248
 249static struct ms5611_chip_info chip_info_tbl[] = {
 250        [MS5611] = {
 251                .temp_and_pressure_compensate = ms5611_temp_and_pressure_compensate,
 252        },
 253        [MS5607] = {
 254                .temp_and_pressure_compensate = ms5607_temp_and_pressure_compensate,
 255        }
 256};
 257
 258static const struct iio_chan_spec ms5611_channels[] = {
 259        {
 260                .type = IIO_PRESSURE,
 261                .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) |
 262                        BIT(IIO_CHAN_INFO_SCALE),
 263                .scan_index = 0,
 264                .scan_type = {
 265                        .sign = 's',
 266                        .realbits = 32,
 267                        .storagebits = 32,
 268                        .endianness = IIO_CPU,
 269                },
 270        },
 271        {
 272                .type = IIO_TEMP,
 273                .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) |
 274                        BIT(IIO_CHAN_INFO_SCALE),
 275                .scan_index = 1,
 276                .scan_type = {
 277                        .sign = 's',
 278                        .realbits = 32,
 279                        .storagebits = 32,
 280                        .endianness = IIO_CPU,
 281                },
 282        },
 283        IIO_CHAN_SOFT_TIMESTAMP(2),
 284};
 285
 286static const struct iio_info ms5611_info = {
 287        .read_raw = &ms5611_read_raw,
 288        .driver_module = THIS_MODULE,
 289};
 290
 291static int ms5611_init(struct iio_dev *indio_dev)
 292{
 293        int ret;
 294        struct regulator *vdd = devm_regulator_get(indio_dev->dev.parent,
 295                                                   "vdd");
 296
 297        /* Enable attached regulator if any. */
 298        if (!IS_ERR(vdd)) {
 299                ret = regulator_enable(vdd);
 300                if (ret) {
 301                        dev_err(indio_dev->dev.parent,
 302                                "failed to enable Vdd supply: %d\n", ret);
 303                        return ret;
 304                }
 305        }
 306
 307        ret = ms5611_reset(indio_dev);
 308        if (ret < 0)
 309                return ret;
 310
 311        return ms5611_read_prom(indio_dev);
 312}
 313
 314int ms5611_probe(struct iio_dev *indio_dev, struct device *dev,
 315                 const char *name, int type)
 316{
 317        int ret;
 318        struct ms5611_state *st = iio_priv(indio_dev);
 319
 320        mutex_init(&st->lock);
 321        st->chip_info = &chip_info_tbl[type];
 322        indio_dev->dev.parent = dev;
 323        indio_dev->name = name;
 324        indio_dev->info = &ms5611_info;
 325        indio_dev->channels = ms5611_channels;
 326        indio_dev->num_channels = ARRAY_SIZE(ms5611_channels);
 327        indio_dev->modes = INDIO_DIRECT_MODE;
 328        indio_dev->available_scan_masks = ms5611_scan_masks;
 329
 330        ret = ms5611_init(indio_dev);
 331        if (ret < 0)
 332                return ret;
 333
 334        ret = iio_triggered_buffer_setup(indio_dev, NULL,
 335                                         ms5611_trigger_handler, NULL);
 336        if (ret < 0) {
 337                dev_err(dev, "iio triggered buffer setup failed\n");
 338                return ret;
 339        }
 340
 341        ret = iio_device_register(indio_dev);
 342        if (ret < 0) {
 343                dev_err(dev, "unable to register iio device\n");
 344                goto err_buffer_cleanup;
 345        }
 346
 347        return 0;
 348
 349err_buffer_cleanup:
 350        iio_triggered_buffer_cleanup(indio_dev);
 351
 352        return ret;
 353}
 354EXPORT_SYMBOL(ms5611_probe);
 355
 356int ms5611_remove(struct iio_dev *indio_dev)
 357{
 358        iio_device_unregister(indio_dev);
 359        iio_triggered_buffer_cleanup(indio_dev);
 360
 361        return 0;
 362}
 363EXPORT_SYMBOL(ms5611_remove);
 364
 365MODULE_AUTHOR("Tomasz Duszynski <tduszyns@gmail.com>");
 366MODULE_DESCRIPTION("MS5611 core driver");
 367MODULE_LICENSE("GPL v2");
 368