linux/drivers/iio/pressure/ms5611_core.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * MS5611 pressure and temperature sensor driver
   4 *
   5 * Copyright (c) Tomasz Duszynski <tduszyns@gmail.com>
   6 *
   7 * Data sheet:
   8 *  http://www.meas-spec.com/downloads/MS5611-01BA03.pdf
   9 *  http://www.meas-spec.com/downloads/MS5607-02BA03.pdf
  10 *
  11 */
  12
  13#include <linux/module.h>
  14#include <linux/iio/iio.h>
  15#include <linux/delay.h>
  16#include <linux/regulator/consumer.h>
  17
  18#include <linux/iio/sysfs.h>
  19#include <linux/iio/buffer.h>
  20#include <linux/iio/triggered_buffer.h>
  21#include <linux/iio/trigger_consumer.h>
  22#include "ms5611.h"
  23
  24#define MS5611_INIT_OSR(_cmd, _conv_usec, _rate) \
  25        { .cmd = _cmd, .conv_usec = _conv_usec, .rate = _rate }
  26
  27static const struct ms5611_osr ms5611_avail_pressure_osr[] = {
  28        MS5611_INIT_OSR(0x40, 600,  256),
  29        MS5611_INIT_OSR(0x42, 1170, 512),
  30        MS5611_INIT_OSR(0x44, 2280, 1024),
  31        MS5611_INIT_OSR(0x46, 4540, 2048),
  32        MS5611_INIT_OSR(0x48, 9040, 4096)
  33};
  34
  35static const struct ms5611_osr ms5611_avail_temp_osr[] = {
  36        MS5611_INIT_OSR(0x50, 600,  256),
  37        MS5611_INIT_OSR(0x52, 1170, 512),
  38        MS5611_INIT_OSR(0x54, 2280, 1024),
  39        MS5611_INIT_OSR(0x56, 4540, 2048),
  40        MS5611_INIT_OSR(0x58, 9040, 4096)
  41};
  42
  43static const char ms5611_show_osr[] = "256 512 1024 2048 4096";
  44
  45static IIO_CONST_ATTR(oversampling_ratio_available, ms5611_show_osr);
  46
  47static struct attribute *ms5611_attributes[] = {
  48        &iio_const_attr_oversampling_ratio_available.dev_attr.attr,
  49        NULL,
  50};
  51
  52static const struct attribute_group ms5611_attribute_group = {
  53        .attrs = ms5611_attributes,
  54};
  55
  56static bool ms5611_prom_is_valid(u16 *prom, size_t len)
  57{
  58        int i, j;
  59        uint16_t crc = 0, crc_orig = prom[7] & 0x000F;
  60
  61        prom[7] &= 0xFF00;
  62
  63        for (i = 0; i < len * 2; i++) {
  64                if (i % 2 == 1)
  65                        crc ^= prom[i >> 1] & 0x00FF;
  66                else
  67                        crc ^= prom[i >> 1] >> 8;
  68
  69                for (j = 0; j < 8; j++) {
  70                        if (crc & 0x8000)
  71                                crc = (crc << 1) ^ 0x3000;
  72                        else
  73                                crc <<= 1;
  74                }
  75        }
  76
  77        crc = (crc >> 12) & 0x000F;
  78
  79        return crc_orig != 0x0000 && crc == crc_orig;
  80}
  81
  82static int ms5611_read_prom(struct iio_dev *indio_dev)
  83{
  84        int ret, i;
  85        struct ms5611_state *st = iio_priv(indio_dev);
  86
  87        for (i = 0; i < MS5611_PROM_WORDS_NB; i++) {
  88                ret = st->read_prom_word(&indio_dev->dev,
  89                                         i, &st->chip_info->prom[i]);
  90                if (ret < 0) {
  91                        dev_err(&indio_dev->dev,
  92                                "failed to read prom at %d\n", i);
  93                        return ret;
  94                }
  95        }
  96
  97        if (!ms5611_prom_is_valid(st->chip_info->prom, MS5611_PROM_WORDS_NB)) {
  98                dev_err(&indio_dev->dev, "PROM integrity check failed\n");
  99                return -ENODEV;
 100        }
 101
 102        return 0;
 103}
 104
 105static int ms5611_read_temp_and_pressure(struct iio_dev *indio_dev,
 106                                         s32 *temp, s32 *pressure)
 107{
 108        int ret;
 109        struct ms5611_state *st = iio_priv(indio_dev);
 110
 111        ret = st->read_adc_temp_and_pressure(&indio_dev->dev, temp, pressure);
 112        if (ret < 0) {
 113                dev_err(&indio_dev->dev,
 114                        "failed to read temperature and pressure\n");
 115                return ret;
 116        }
 117
 118        return st->chip_info->temp_and_pressure_compensate(st->chip_info,
 119                                                           temp, pressure);
 120}
 121
 122static int ms5611_temp_and_pressure_compensate(struct ms5611_chip_info *chip_info,
 123                                               s32 *temp, s32 *pressure)
 124{
 125        s32 t = *temp, p = *pressure;
 126        s64 off, sens, dt;
 127
 128        dt = t - (chip_info->prom[5] << 8);
 129        off = ((s64)chip_info->prom[2] << 16) + ((chip_info->prom[4] * dt) >> 7);
 130        sens = ((s64)chip_info->prom[1] << 15) + ((chip_info->prom[3] * dt) >> 8);
 131
 132        t = 2000 + ((chip_info->prom[6] * dt) >> 23);
 133        if (t < 2000) {
 134                s64 off2, sens2, t2;
 135
 136                t2 = (dt * dt) >> 31;
 137                off2 = (5 * (t - 2000) * (t - 2000)) >> 1;
 138                sens2 = off2 >> 1;
 139
 140                if (t < -1500) {
 141                        s64 tmp = (t + 1500) * (t + 1500);
 142
 143                        off2 += 7 * tmp;
 144                        sens2 += (11 * tmp) >> 1;
 145                }
 146
 147                t -= t2;
 148                off -= off2;
 149                sens -= sens2;
 150        }
 151
 152        *temp = t;
 153        *pressure = (((p * sens) >> 21) - off) >> 15;
 154
 155        return 0;
 156}
 157
 158static int ms5607_temp_and_pressure_compensate(struct ms5611_chip_info *chip_info,
 159                                               s32 *temp, s32 *pressure)
 160{
 161        s32 t = *temp, p = *pressure;
 162        s64 off, sens, dt;
 163
 164        dt = t - (chip_info->prom[5] << 8);
 165        off = ((s64)chip_info->prom[2] << 17) + ((chip_info->prom[4] * dt) >> 6);
 166        sens = ((s64)chip_info->prom[1] << 16) + ((chip_info->prom[3] * dt) >> 7);
 167
 168        t = 2000 + ((chip_info->prom[6] * dt) >> 23);
 169        if (t < 2000) {
 170                s64 off2, sens2, t2, tmp;
 171
 172                t2 = (dt * dt) >> 31;
 173                tmp = (t - 2000) * (t - 2000);
 174                off2 = (61 * tmp) >> 4;
 175                sens2 = tmp << 1;
 176
 177                if (t < -1500) {
 178                        tmp = (t + 1500) * (t + 1500);
 179                        off2 += 15 * tmp;
 180                        sens2 += 8 * tmp;
 181                }
 182
 183                t -= t2;
 184                off -= off2;
 185                sens -= sens2;
 186        }
 187
 188        *temp = t;
 189        *pressure = (((p * sens) >> 21) - off) >> 15;
 190
 191        return 0;
 192}
 193
 194static int ms5611_reset(struct iio_dev *indio_dev)
 195{
 196        int ret;
 197        struct ms5611_state *st = iio_priv(indio_dev);
 198
 199        ret = st->reset(&indio_dev->dev);
 200        if (ret < 0) {
 201                dev_err(&indio_dev->dev, "failed to reset device\n");
 202                return ret;
 203        }
 204
 205        usleep_range(3000, 4000);
 206
 207        return 0;
 208}
 209
 210static irqreturn_t ms5611_trigger_handler(int irq, void *p)
 211{
 212        struct iio_poll_func *pf = p;
 213        struct iio_dev *indio_dev = pf->indio_dev;
 214        struct ms5611_state *st = iio_priv(indio_dev);
 215        s32 buf[4]; /* s32 (pressure) + s32 (temp) + 2 * s32 (timestamp) */
 216        int ret;
 217
 218        mutex_lock(&st->lock);
 219        ret = ms5611_read_temp_and_pressure(indio_dev, &buf[1], &buf[0]);
 220        mutex_unlock(&st->lock);
 221        if (ret < 0)
 222                goto err;
 223
 224        iio_push_to_buffers_with_timestamp(indio_dev, buf,
 225                                           iio_get_time_ns(indio_dev));
 226
 227err:
 228        iio_trigger_notify_done(indio_dev->trig);
 229
 230        return IRQ_HANDLED;
 231}
 232
 233static int ms5611_read_raw(struct iio_dev *indio_dev,
 234                           struct iio_chan_spec const *chan,
 235                           int *val, int *val2, long mask)
 236{
 237        int ret;
 238        s32 temp, pressure;
 239        struct ms5611_state *st = iio_priv(indio_dev);
 240
 241        switch (mask) {
 242        case IIO_CHAN_INFO_PROCESSED:
 243                mutex_lock(&st->lock);
 244                ret = ms5611_read_temp_and_pressure(indio_dev,
 245                                                    &temp, &pressure);
 246                mutex_unlock(&st->lock);
 247                if (ret < 0)
 248                        return ret;
 249
 250                switch (chan->type) {
 251                case IIO_TEMP:
 252                        *val = temp * 10;
 253                        return IIO_VAL_INT;
 254                case IIO_PRESSURE:
 255                        *val = pressure / 1000;
 256                        *val2 = (pressure % 1000) * 1000;
 257                        return IIO_VAL_INT_PLUS_MICRO;
 258                default:
 259                        return -EINVAL;
 260                }
 261        case IIO_CHAN_INFO_SCALE:
 262                switch (chan->type) {
 263                case IIO_TEMP:
 264                        *val = 10;
 265                        return IIO_VAL_INT;
 266                case IIO_PRESSURE:
 267                        *val = 0;
 268                        *val2 = 1000;
 269                        return IIO_VAL_INT_PLUS_MICRO;
 270                default:
 271                        return -EINVAL;
 272                }
 273        case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
 274                if (chan->type != IIO_TEMP && chan->type != IIO_PRESSURE)
 275                        break;
 276                mutex_lock(&st->lock);
 277                if (chan->type == IIO_TEMP)
 278                        *val = (int)st->temp_osr->rate;
 279                else
 280                        *val = (int)st->pressure_osr->rate;
 281                mutex_unlock(&st->lock);
 282                return IIO_VAL_INT;
 283        }
 284
 285        return -EINVAL;
 286}
 287
 288static const struct ms5611_osr *ms5611_find_osr(int rate,
 289                                                const struct ms5611_osr *osr,
 290                                                size_t count)
 291{
 292        unsigned int r;
 293
 294        for (r = 0; r < count; r++)
 295                if ((unsigned short)rate == osr[r].rate)
 296                        break;
 297        if (r >= count)
 298                return NULL;
 299        return &osr[r];
 300}
 301
 302static int ms5611_write_raw(struct iio_dev *indio_dev,
 303                            struct iio_chan_spec const *chan,
 304                            int val, int val2, long mask)
 305{
 306        struct ms5611_state *st = iio_priv(indio_dev);
 307        const struct ms5611_osr *osr = NULL;
 308        int ret;
 309
 310        if (mask != IIO_CHAN_INFO_OVERSAMPLING_RATIO)
 311                return -EINVAL;
 312
 313        if (chan->type == IIO_TEMP)
 314                osr = ms5611_find_osr(val, ms5611_avail_temp_osr,
 315                                      ARRAY_SIZE(ms5611_avail_temp_osr));
 316        else if (chan->type == IIO_PRESSURE)
 317                osr = ms5611_find_osr(val, ms5611_avail_pressure_osr,
 318                                      ARRAY_SIZE(ms5611_avail_pressure_osr));
 319        if (!osr)
 320                return -EINVAL;
 321
 322        ret = iio_device_claim_direct_mode(indio_dev);
 323        if (ret)
 324                return ret;
 325
 326        mutex_lock(&st->lock);
 327
 328        if (chan->type == IIO_TEMP)
 329                st->temp_osr = osr;
 330        else
 331                st->pressure_osr = osr;
 332
 333        mutex_unlock(&st->lock);
 334        iio_device_release_direct_mode(indio_dev);
 335
 336        return 0;
 337}
 338
 339static const unsigned long ms5611_scan_masks[] = {0x3, 0};
 340
 341static struct ms5611_chip_info chip_info_tbl[] = {
 342        [MS5611] = {
 343                .temp_and_pressure_compensate = ms5611_temp_and_pressure_compensate,
 344        },
 345        [MS5607] = {
 346                .temp_and_pressure_compensate = ms5607_temp_and_pressure_compensate,
 347        }
 348};
 349
 350static const struct iio_chan_spec ms5611_channels[] = {
 351        {
 352                .type = IIO_PRESSURE,
 353                .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) |
 354                        BIT(IIO_CHAN_INFO_SCALE) |
 355                        BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
 356                .scan_index = 0,
 357                .scan_type = {
 358                        .sign = 's',
 359                        .realbits = 32,
 360                        .storagebits = 32,
 361                        .endianness = IIO_CPU,
 362                },
 363        },
 364        {
 365                .type = IIO_TEMP,
 366                .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) |
 367                        BIT(IIO_CHAN_INFO_SCALE) |
 368                        BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
 369                .scan_index = 1,
 370                .scan_type = {
 371                        .sign = 's',
 372                        .realbits = 32,
 373                        .storagebits = 32,
 374                        .endianness = IIO_CPU,
 375                },
 376        },
 377        IIO_CHAN_SOFT_TIMESTAMP(2),
 378};
 379
 380static const struct iio_info ms5611_info = {
 381        .read_raw = &ms5611_read_raw,
 382        .write_raw = &ms5611_write_raw,
 383        .attrs = &ms5611_attribute_group,
 384};
 385
 386static int ms5611_init(struct iio_dev *indio_dev)
 387{
 388        int ret;
 389        struct ms5611_state *st = iio_priv(indio_dev);
 390
 391        /* Enable attached regulator if any. */
 392        st->vdd = devm_regulator_get(indio_dev->dev.parent, "vdd");
 393        if (IS_ERR(st->vdd))
 394                return PTR_ERR(st->vdd);
 395
 396        ret = regulator_enable(st->vdd);
 397        if (ret) {
 398                dev_err(indio_dev->dev.parent,
 399                        "failed to enable Vdd supply: %d\n", ret);
 400                return ret;
 401        }
 402
 403        ret = ms5611_reset(indio_dev);
 404        if (ret < 0)
 405                goto err_regulator_disable;
 406
 407        ret = ms5611_read_prom(indio_dev);
 408        if (ret < 0)
 409                goto err_regulator_disable;
 410
 411        return 0;
 412
 413err_regulator_disable:
 414        regulator_disable(st->vdd);
 415        return ret;
 416}
 417
 418static void ms5611_fini(const struct iio_dev *indio_dev)
 419{
 420        const struct ms5611_state *st = iio_priv(indio_dev);
 421
 422        regulator_disable(st->vdd);
 423}
 424
 425int ms5611_probe(struct iio_dev *indio_dev, struct device *dev,
 426                 const char *name, int type)
 427{
 428        int ret;
 429        struct ms5611_state *st = iio_priv(indio_dev);
 430
 431        mutex_init(&st->lock);
 432        st->chip_info = &chip_info_tbl[type];
 433        st->temp_osr =
 434                &ms5611_avail_temp_osr[ARRAY_SIZE(ms5611_avail_temp_osr) - 1];
 435        st->pressure_osr =
 436                &ms5611_avail_pressure_osr[ARRAY_SIZE(ms5611_avail_pressure_osr)
 437                                           - 1];
 438        indio_dev->dev.parent = dev;
 439        indio_dev->name = name;
 440        indio_dev->info = &ms5611_info;
 441        indio_dev->channels = ms5611_channels;
 442        indio_dev->num_channels = ARRAY_SIZE(ms5611_channels);
 443        indio_dev->modes = INDIO_DIRECT_MODE;
 444        indio_dev->available_scan_masks = ms5611_scan_masks;
 445
 446        ret = ms5611_init(indio_dev);
 447        if (ret < 0)
 448                return ret;
 449
 450        ret = iio_triggered_buffer_setup(indio_dev, NULL,
 451                                         ms5611_trigger_handler, NULL);
 452        if (ret < 0) {
 453                dev_err(dev, "iio triggered buffer setup failed\n");
 454                goto err_fini;
 455        }
 456
 457        ret = iio_device_register(indio_dev);
 458        if (ret < 0) {
 459                dev_err(dev, "unable to register iio device\n");
 460                goto err_buffer_cleanup;
 461        }
 462
 463        return 0;
 464
 465err_buffer_cleanup:
 466        iio_triggered_buffer_cleanup(indio_dev);
 467err_fini:
 468        ms5611_fini(indio_dev);
 469        return ret;
 470}
 471EXPORT_SYMBOL(ms5611_probe);
 472
 473int ms5611_remove(struct iio_dev *indio_dev)
 474{
 475        iio_device_unregister(indio_dev);
 476        iio_triggered_buffer_cleanup(indio_dev);
 477        ms5611_fini(indio_dev);
 478
 479        return 0;
 480}
 481EXPORT_SYMBOL(ms5611_remove);
 482
 483MODULE_AUTHOR("Tomasz Duszynski <tduszyns@gmail.com>");
 484MODULE_DESCRIPTION("MS5611 core driver");
 485MODULE_LICENSE("GPL v2");
 486