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