linux/drivers/iio/pressure/mpl115.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * mpl115.c - Support for Freescale MPL115A pressure/temperature sensor
   4 *
   5 * Copyright (c) 2014 Peter Meerwald <pmeerw@pmeerw.net>
   6 *
   7 * TODO: shutdown pin
   8 */
   9
  10#include <linux/module.h>
  11#include <linux/iio/iio.h>
  12#include <linux/delay.h>
  13
  14#include "mpl115.h"
  15
  16#define MPL115_PADC 0x00 /* pressure ADC output value, MSB first, 10 bit */
  17#define MPL115_TADC 0x02 /* temperature ADC output value, MSB first, 10 bit */
  18#define MPL115_A0 0x04 /* 12 bit integer, 3 bit fraction */
  19#define MPL115_B1 0x06 /* 2 bit integer, 13 bit fraction */
  20#define MPL115_B2 0x08 /* 1 bit integer, 14 bit fraction */
  21#define MPL115_C12 0x0a /* 0 bit integer, 13 bit fraction */
  22#define MPL115_CONVERT 0x12 /* convert temperature and pressure */
  23
  24struct mpl115_data {
  25        struct device *dev;
  26        struct mutex lock;
  27        s16 a0;
  28        s16 b1, b2;
  29        s16 c12;
  30        const struct mpl115_ops *ops;
  31};
  32
  33static int mpl115_request(struct mpl115_data *data)
  34{
  35        int ret = data->ops->write(data->dev, MPL115_CONVERT, 0);
  36
  37        if (ret < 0)
  38                return ret;
  39
  40        usleep_range(3000, 4000);
  41
  42        return 0;
  43}
  44
  45static int mpl115_comp_pressure(struct mpl115_data *data, int *val, int *val2)
  46{
  47        int ret;
  48        u16 padc, tadc;
  49        int a1, y1, pcomp;
  50        unsigned kpa;
  51
  52        mutex_lock(&data->lock);
  53        ret = mpl115_request(data);
  54        if (ret < 0)
  55                goto done;
  56
  57        ret = data->ops->read(data->dev, MPL115_PADC);
  58        if (ret < 0)
  59                goto done;
  60        padc = ret >> 6;
  61
  62        ret = data->ops->read(data->dev, MPL115_TADC);
  63        if (ret < 0)
  64                goto done;
  65        tadc = ret >> 6;
  66
  67        /* see Freescale AN3785 */
  68        a1 = data->b1 + ((data->c12 * tadc) >> 11);
  69        y1 = (data->a0 << 10) + a1 * padc;
  70
  71        /* compensated pressure with 4 fractional bits */
  72        pcomp = (y1 + ((data->b2 * (int) tadc) >> 1)) >> 9;
  73
  74        kpa = pcomp * (115 - 50) / 1023 + (50 << 4);
  75        *val = kpa >> 4;
  76        *val2 = (kpa & 15) * (1000000 >> 4);
  77done:
  78        mutex_unlock(&data->lock);
  79        return ret;
  80}
  81
  82static int mpl115_read_temp(struct mpl115_data *data)
  83{
  84        int ret;
  85
  86        mutex_lock(&data->lock);
  87        ret = mpl115_request(data);
  88        if (ret < 0)
  89                goto done;
  90        ret = data->ops->read(data->dev, MPL115_TADC);
  91done:
  92        mutex_unlock(&data->lock);
  93        return ret;
  94}
  95
  96static int mpl115_read_raw(struct iio_dev *indio_dev,
  97                            struct iio_chan_spec const *chan,
  98                            int *val, int *val2, long mask)
  99{
 100        struct mpl115_data *data = iio_priv(indio_dev);
 101        int ret;
 102
 103        switch (mask) {
 104        case IIO_CHAN_INFO_PROCESSED:
 105                ret = mpl115_comp_pressure(data, val, val2);
 106                if (ret < 0)
 107                        return ret;
 108                return IIO_VAL_INT_PLUS_MICRO;
 109        case IIO_CHAN_INFO_RAW:
 110                /* temperature -5.35 C / LSB, 472 LSB is 25 C */
 111                ret = mpl115_read_temp(data);
 112                if (ret < 0)
 113                        return ret;
 114                *val = ret >> 6;
 115                return IIO_VAL_INT;
 116        case IIO_CHAN_INFO_OFFSET:
 117                *val = -605;
 118                *val2 = 750000;
 119                return IIO_VAL_INT_PLUS_MICRO;
 120        case IIO_CHAN_INFO_SCALE:
 121                *val = -186;
 122                *val2 = 915888;
 123                return IIO_VAL_INT_PLUS_MICRO;
 124        }
 125        return -EINVAL;
 126}
 127
 128static const struct iio_chan_spec mpl115_channels[] = {
 129        {
 130                .type = IIO_PRESSURE,
 131                .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
 132        },
 133        {
 134                .type = IIO_TEMP,
 135                .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
 136                .info_mask_shared_by_type =
 137                        BIT(IIO_CHAN_INFO_OFFSET) | BIT(IIO_CHAN_INFO_SCALE),
 138        },
 139};
 140
 141static const struct iio_info mpl115_info = {
 142        .read_raw = &mpl115_read_raw,
 143};
 144
 145int mpl115_probe(struct device *dev, const char *name,
 146                        const struct mpl115_ops *ops)
 147{
 148        struct mpl115_data *data;
 149        struct iio_dev *indio_dev;
 150        int ret;
 151
 152        indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
 153        if (!indio_dev)
 154                return -ENOMEM;
 155
 156        data = iio_priv(indio_dev);
 157        data->dev = dev;
 158        data->ops = ops;
 159        mutex_init(&data->lock);
 160
 161        indio_dev->info = &mpl115_info;
 162        indio_dev->name = name;
 163        indio_dev->modes = INDIO_DIRECT_MODE;
 164        indio_dev->channels = mpl115_channels;
 165        indio_dev->num_channels = ARRAY_SIZE(mpl115_channels);
 166
 167        ret = data->ops->init(data->dev);
 168        if (ret)
 169                return ret;
 170
 171        ret = data->ops->read(data->dev, MPL115_A0);
 172        if (ret < 0)
 173                return ret;
 174        data->a0 = ret;
 175        ret = data->ops->read(data->dev, MPL115_B1);
 176        if (ret < 0)
 177                return ret;
 178        data->b1 = ret;
 179        ret = data->ops->read(data->dev, MPL115_B2);
 180        if (ret < 0)
 181                return ret;
 182        data->b2 = ret;
 183        ret = data->ops->read(data->dev, MPL115_C12);
 184        if (ret < 0)
 185                return ret;
 186        data->c12 = ret;
 187
 188        return devm_iio_device_register(dev, indio_dev);
 189}
 190EXPORT_SYMBOL_GPL(mpl115_probe);
 191
 192MODULE_AUTHOR("Peter Meerwald <pmeerw@pmeerw.net>");
 193MODULE_DESCRIPTION("Freescale MPL115 pressure/temperature driver");
 194MODULE_LICENSE("GPL");
 195