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 24 struct 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 33 static 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 45 static 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); 77 done: 78 mutex_unlock(&data->lock); 79 return ret; 80 } 81 82 static 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); 91 done: 92 mutex_unlock(&data->lock); 93 return ret; 94 } 95 96 static 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 128 static 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 141 static const struct iio_info mpl115_info = { 142 .read_raw = &mpl115_read_raw, 143 }; 144 145 int 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 } 190 EXPORT_SYMBOL_GPL(mpl115_probe); 191 192 MODULE_AUTHOR("Peter Meerwald <pmeerw@pmeerw.net>"); 193 MODULE_DESCRIPTION("Freescale MPL115 pressure/temperature driver"); 194 MODULE_LICENSE("GPL"); 195