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 28 struct 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 37 static 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 49 static 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); 81 done: 82 mutex_unlock(&data->lock); 83 return ret; 84 } 85 86 static 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); 95 done: 96 mutex_unlock(&data->lock); 97 return ret; 98 } 99 100 static 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 132 static 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 BIT(IIO_CHAN_INFO_OFFSET) | BIT(IIO_CHAN_INFO_SCALE), 141 }, 142 }; 143 144 static const struct iio_info mpl115_info = { 145 .read_raw = &mpl115_read_raw, 146 .driver_module = THIS_MODULE, 147 }; 148 149 int mpl115_probe(struct device *dev, const char *name, 150 const struct mpl115_ops *ops) 151 { 152 struct mpl115_data *data; 153 struct iio_dev *indio_dev; 154 int ret; 155 156 indio_dev = devm_iio_device_alloc(dev, sizeof(*data)); 157 if (!indio_dev) 158 return -ENOMEM; 159 160 data = iio_priv(indio_dev); 161 data->dev = dev; 162 data->ops = ops; 163 mutex_init(&data->lock); 164 165 indio_dev->info = &mpl115_info; 166 indio_dev->name = name; 167 indio_dev->dev.parent = dev; 168 indio_dev->modes = INDIO_DIRECT_MODE; 169 indio_dev->channels = mpl115_channels; 170 indio_dev->num_channels = ARRAY_SIZE(mpl115_channels); 171 172 ret = data->ops->init(data->dev); 173 if (ret) 174 return ret; 175 176 ret = data->ops->read(data->dev, MPL115_A0); 177 if (ret < 0) 178 return ret; 179 data->a0 = ret; 180 ret = data->ops->read(data->dev, MPL115_B1); 181 if (ret < 0) 182 return ret; 183 data->b1 = ret; 184 ret = data->ops->read(data->dev, MPL115_B2); 185 if (ret < 0) 186 return ret; 187 data->b2 = ret; 188 ret = data->ops->read(data->dev, MPL115_C12); 189 if (ret < 0) 190 return ret; 191 data->c12 = ret; 192 193 return devm_iio_device_register(dev, indio_dev); 194 } 195 EXPORT_SYMBOL_GPL(mpl115_probe); 196 197 MODULE_AUTHOR("Peter Meerwald <pmeerw@pmeerw.net>"); 198 MODULE_DESCRIPTION("Freescale MPL115 pressure/temperature driver"); 199 MODULE_LICENSE("GPL"); 200