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        /* Ensure buffer elements are naturally aligned */
 216        struct {
 217                s32 channels[2];
 218                s64 ts __aligned(8);
 219        } scan;
 220        int ret;
 221
 222        mutex_lock(&st->lock);
 223        ret = ms5611_read_temp_and_pressure(indio_dev, &scan.channels[1],
 224                                            &scan.channels[0]);
 225        mutex_unlock(&st->lock);
 226        if (ret < 0)
 227                goto err;
 228
 229        iio_push_to_buffers_with_timestamp(indio_dev, &scan,
 230                                           iio_get_time_ns(indio_dev));
 231
 232err:
 233        iio_trigger_notify_done(indio_dev->trig);
 234
 235        return IRQ_HANDLED;
 236}
 237
 238static int ms5611_read_raw(struct iio_dev *indio_dev,
 239                           struct iio_chan_spec const *chan,
 240                           int *val, int *val2, long mask)
 241{
 242        int ret;
 243        s32 temp, pressure;
 244        struct ms5611_state *st = iio_priv(indio_dev);
 245
 246        switch (mask) {
 247        case IIO_CHAN_INFO_PROCESSED:
 248                mutex_lock(&st->lock);
 249                ret = ms5611_read_temp_and_pressure(indio_dev,
 250                                                    &temp, &pressure);
 251                mutex_unlock(&st->lock);
 252                if (ret < 0)
 253                        return ret;
 254
 255                switch (chan->type) {
 256                case IIO_TEMP:
 257                        *val = temp * 10;
 258                        return IIO_VAL_INT;
 259                case IIO_PRESSURE:
 260                        *val = pressure / 1000;
 261                        *val2 = (pressure % 1000) * 1000;
 262                        return IIO_VAL_INT_PLUS_MICRO;
 263                default:
 264                        return -EINVAL;
 265                }
 266        case IIO_CHAN_INFO_SCALE:
 267                switch (chan->type) {
 268                case IIO_TEMP:
 269                        *val = 10;
 270                        return IIO_VAL_INT;
 271                case IIO_PRESSURE:
 272                        *val = 0;
 273                        *val2 = 1000;
 274                        return IIO_VAL_INT_PLUS_MICRO;
 275                default:
 276                        return -EINVAL;
 277                }
 278        case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
 279                if (chan->type != IIO_TEMP && chan->type != IIO_PRESSURE)
 280                        break;
 281                mutex_lock(&st->lock);
 282                if (chan->type == IIO_TEMP)
 283                        *val = (int)st->temp_osr->rate;
 284                else
 285                        *val = (int)st->pressure_osr->rate;
 286                mutex_unlock(&st->lock);
 287                return IIO_VAL_INT;
 288        }
 289
 290        return -EINVAL;
 291}
 292
 293static const struct ms5611_osr *ms5611_find_osr(int rate,
 294                                                const struct ms5611_osr *osr,
 295                                                size_t count)
 296{
 297        unsigned int r;
 298
 299        for (r = 0; r < count; r++)
 300                if ((unsigned short)rate == osr[r].rate)
 301                        break;
 302        if (r >= count)
 303                return NULL;
 304        return &osr[r];
 305}
 306
 307static int ms5611_write_raw(struct iio_dev *indio_dev,
 308                            struct iio_chan_spec const *chan,
 309                            int val, int val2, long mask)
 310{
 311        struct ms5611_state *st = iio_priv(indio_dev);
 312        const struct ms5611_osr *osr = NULL;
 313        int ret;
 314
 315        if (mask != IIO_CHAN_INFO_OVERSAMPLING_RATIO)
 316                return -EINVAL;
 317
 318        if (chan->type == IIO_TEMP)
 319                osr = ms5611_find_osr(val, ms5611_avail_temp_osr,
 320                                      ARRAY_SIZE(ms5611_avail_temp_osr));
 321        else if (chan->type == IIO_PRESSURE)
 322                osr = ms5611_find_osr(val, ms5611_avail_pressure_osr,
 323                                      ARRAY_SIZE(ms5611_avail_pressure_osr));
 324        if (!osr)
 325                return -EINVAL;
 326
 327        ret = iio_device_claim_direct_mode(indio_dev);
 328        if (ret)
 329                return ret;
 330
 331        mutex_lock(&st->lock);
 332
 333        if (chan->type == IIO_TEMP)
 334                st->temp_osr = osr;
 335        else
 336                st->pressure_osr = osr;
 337
 338        mutex_unlock(&st->lock);
 339        iio_device_release_direct_mode(indio_dev);
 340
 341        return 0;
 342}
 343
 344static const unsigned long ms5611_scan_masks[] = {0x3, 0};
 345
 346static struct ms5611_chip_info chip_info_tbl[] = {
 347        [MS5611] = {
 348                .temp_and_pressure_compensate = ms5611_temp_and_pressure_compensate,
 349        },
 350        [MS5607] = {
 351                .temp_and_pressure_compensate = ms5607_temp_and_pressure_compensate,
 352        }
 353};
 354
 355static const struct iio_chan_spec ms5611_channels[] = {
 356        {
 357                .type = IIO_PRESSURE,
 358                .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) |
 359                        BIT(IIO_CHAN_INFO_SCALE) |
 360                        BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
 361                .scan_index = 0,
 362                .scan_type = {
 363                        .sign = 's',
 364                        .realbits = 32,
 365                        .storagebits = 32,
 366                        .endianness = IIO_CPU,
 367                },
 368        },
 369        {
 370                .type = IIO_TEMP,
 371                .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) |
 372                        BIT(IIO_CHAN_INFO_SCALE) |
 373                        BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
 374                .scan_index = 1,
 375                .scan_type = {
 376                        .sign = 's',
 377                        .realbits = 32,
 378                        .storagebits = 32,
 379                        .endianness = IIO_CPU,
 380                },
 381        },
 382        IIO_CHAN_SOFT_TIMESTAMP(2),
 383};
 384
 385static const struct iio_info ms5611_info = {
 386        .read_raw = &ms5611_read_raw,
 387        .write_raw = &ms5611_write_raw,
 388        .attrs = &ms5611_attribute_group,
 389};
 390
 391static int ms5611_init(struct iio_dev *indio_dev)
 392{
 393        int ret;
 394        struct ms5611_state *st = iio_priv(indio_dev);
 395
 396        /* Enable attached regulator if any. */
 397        st->vdd = devm_regulator_get(indio_dev->dev.parent, "vdd");
 398        if (IS_ERR(st->vdd))
 399                return PTR_ERR(st->vdd);
 400
 401        ret = regulator_enable(st->vdd);
 402        if (ret) {
 403                dev_err(indio_dev->dev.parent,
 404                        "failed to enable Vdd supply: %d\n", ret);
 405                return ret;
 406        }
 407
 408        ret = ms5611_reset(indio_dev);
 409        if (ret < 0)
 410                goto err_regulator_disable;
 411
 412        ret = ms5611_read_prom(indio_dev);
 413        if (ret < 0)
 414                goto err_regulator_disable;
 415
 416        return 0;
 417
 418err_regulator_disable:
 419        regulator_disable(st->vdd);
 420        return ret;
 421}
 422
 423static void ms5611_fini(const struct iio_dev *indio_dev)
 424{
 425        const struct ms5611_state *st = iio_priv(indio_dev);
 426
 427        regulator_disable(st->vdd);
 428}
 429
 430int ms5611_probe(struct iio_dev *indio_dev, struct device *dev,
 431                 const char *name, int type)
 432{
 433        int ret;
 434        struct ms5611_state *st = iio_priv(indio_dev);
 435
 436        mutex_init(&st->lock);
 437        st->chip_info = &chip_info_tbl[type];
 438        st->temp_osr =
 439                &ms5611_avail_temp_osr[ARRAY_SIZE(ms5611_avail_temp_osr) - 1];
 440        st->pressure_osr =
 441                &ms5611_avail_pressure_osr[ARRAY_SIZE(ms5611_avail_pressure_osr)
 442                                           - 1];
 443        indio_dev->name = name;
 444        indio_dev->info = &ms5611_info;
 445        indio_dev->channels = ms5611_channels;
 446        indio_dev->num_channels = ARRAY_SIZE(ms5611_channels);
 447        indio_dev->modes = INDIO_DIRECT_MODE;
 448        indio_dev->available_scan_masks = ms5611_scan_masks;
 449
 450        ret = ms5611_init(indio_dev);
 451        if (ret < 0)
 452                return ret;
 453
 454        ret = iio_triggered_buffer_setup(indio_dev, NULL,
 455                                         ms5611_trigger_handler, NULL);
 456        if (ret < 0) {
 457                dev_err(dev, "iio triggered buffer setup failed\n");
 458                goto err_fini;
 459        }
 460
 461        ret = iio_device_register(indio_dev);
 462        if (ret < 0) {
 463                dev_err(dev, "unable to register iio device\n");
 464                goto err_buffer_cleanup;
 465        }
 466
 467        return 0;
 468
 469err_buffer_cleanup:
 470        iio_triggered_buffer_cleanup(indio_dev);
 471err_fini:
 472        ms5611_fini(indio_dev);
 473        return ret;
 474}
 475EXPORT_SYMBOL(ms5611_probe);
 476
 477int ms5611_remove(struct iio_dev *indio_dev)
 478{
 479        iio_device_unregister(indio_dev);
 480        iio_triggered_buffer_cleanup(indio_dev);
 481        ms5611_fini(indio_dev);
 482
 483        return 0;
 484}
 485EXPORT_SYMBOL(ms5611_remove);
 486
 487MODULE_AUTHOR("Tomasz Duszynski <tduszyns@gmail.com>");
 488MODULE_DESCRIPTION("MS5611 core driver");
 489MODULE_LICENSE("GPL v2");
 490