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