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 * 13 */ 14 15 #include <linux/module.h> 16 #include <linux/iio/iio.h> 17 #include <linux/delay.h> 18 19 #include "ms5611.h" 20 21 static bool ms5611_prom_is_valid(u16 *prom, size_t len) 22 { 23 int i, j; 24 uint16_t crc = 0, crc_orig = prom[7] & 0x000F; 25 26 prom[7] &= 0xFF00; 27 28 for (i = 0; i < len * 2; i++) { 29 if (i % 2 == 1) 30 crc ^= prom[i >> 1] & 0x00FF; 31 else 32 crc ^= prom[i >> 1] >> 8; 33 34 for (j = 0; j < 8; j++) { 35 if (crc & 0x8000) 36 crc = (crc << 1) ^ 0x3000; 37 else 38 crc <<= 1; 39 } 40 } 41 42 crc = (crc >> 12) & 0x000F; 43 44 return crc_orig != 0x0000 && crc == crc_orig; 45 } 46 47 static int ms5611_read_prom(struct iio_dev *indio_dev) 48 { 49 int ret, i; 50 struct ms5611_state *st = iio_priv(indio_dev); 51 52 for (i = 0; i < MS5611_PROM_WORDS_NB; i++) { 53 ret = st->read_prom_word(&indio_dev->dev, i, &st->prom[i]); 54 if (ret < 0) { 55 dev_err(&indio_dev->dev, 56 "failed to read prom at %d\n", i); 57 return ret; 58 } 59 } 60 61 if (!ms5611_prom_is_valid(st->prom, MS5611_PROM_WORDS_NB)) { 62 dev_err(&indio_dev->dev, "PROM integrity check failed\n"); 63 return -ENODEV; 64 } 65 66 return 0; 67 } 68 69 static int ms5611_read_temp_and_pressure(struct iio_dev *indio_dev, 70 s32 *temp, s32 *pressure) 71 { 72 int ret; 73 s32 t, p; 74 s64 off, sens, dt; 75 struct ms5611_state *st = iio_priv(indio_dev); 76 77 ret = st->read_adc_temp_and_pressure(&indio_dev->dev, &t, &p); 78 if (ret < 0) { 79 dev_err(&indio_dev->dev, 80 "failed to read temperature and pressure\n"); 81 return ret; 82 } 83 84 dt = t - (st->prom[5] << 8); 85 off = ((s64)st->prom[2] << 16) + ((st->prom[4] * dt) >> 7); 86 sens = ((s64)st->prom[1] << 15) + ((st->prom[3] * dt) >> 8); 87 88 t = 2000 + ((st->prom[6] * dt) >> 23); 89 if (t < 2000) { 90 s64 off2, sens2, t2; 91 92 t2 = (dt * dt) >> 31; 93 off2 = (5 * (t - 2000) * (t - 2000)) >> 1; 94 sens2 = off2 >> 1; 95 96 if (t < -1500) { 97 s64 tmp = (t + 1500) * (t + 1500); 98 99 off2 += 7 * tmp; 100 sens2 += (11 * tmp) >> 1; 101 } 102 103 t -= t2; 104 off -= off2; 105 sens -= sens2; 106 } 107 108 *temp = t; 109 *pressure = (((p * sens) >> 21) - off) >> 15; 110 111 return 0; 112 } 113 114 static int ms5611_reset(struct iio_dev *indio_dev) 115 { 116 int ret; 117 struct ms5611_state *st = iio_priv(indio_dev); 118 119 ret = st->reset(&indio_dev->dev); 120 if (ret < 0) { 121 dev_err(&indio_dev->dev, "failed to reset device\n"); 122 return ret; 123 } 124 125 usleep_range(3000, 4000); 126 127 return 0; 128 } 129 130 static int ms5611_read_raw(struct iio_dev *indio_dev, 131 struct iio_chan_spec const *chan, 132 int *val, int *val2, long mask) 133 { 134 int ret; 135 s32 temp, pressure; 136 struct ms5611_state *st = iio_priv(indio_dev); 137 138 switch (mask) { 139 case IIO_CHAN_INFO_PROCESSED: 140 mutex_lock(&st->lock); 141 ret = ms5611_read_temp_and_pressure(indio_dev, 142 &temp, &pressure); 143 mutex_unlock(&st->lock); 144 if (ret < 0) 145 return ret; 146 147 switch (chan->type) { 148 case IIO_TEMP: 149 *val = temp * 10; 150 return IIO_VAL_INT; 151 case IIO_PRESSURE: 152 *val = pressure / 1000; 153 *val2 = (pressure % 1000) * 1000; 154 return IIO_VAL_INT_PLUS_MICRO; 155 default: 156 return -EINVAL; 157 } 158 } 159 160 return -EINVAL; 161 } 162 163 static const struct iio_chan_spec ms5611_channels[] = { 164 { 165 .type = IIO_PRESSURE, 166 .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) | 167 BIT(IIO_CHAN_INFO_SCALE) 168 }, 169 { 170 .type = IIO_TEMP, 171 .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) | 172 BIT(IIO_CHAN_INFO_SCALE) 173 } 174 }; 175 176 static const struct iio_info ms5611_info = { 177 .read_raw = &ms5611_read_raw, 178 .driver_module = THIS_MODULE, 179 }; 180 181 static int ms5611_init(struct iio_dev *indio_dev) 182 { 183 int ret; 184 185 ret = ms5611_reset(indio_dev); 186 if (ret < 0) 187 return ret; 188 189 return ms5611_read_prom(indio_dev); 190 } 191 192 int ms5611_probe(struct iio_dev *indio_dev, struct device *dev) 193 { 194 int ret; 195 struct ms5611_state *st = iio_priv(indio_dev); 196 197 mutex_init(&st->lock); 198 indio_dev->dev.parent = dev; 199 indio_dev->name = dev->driver->name; 200 indio_dev->info = &ms5611_info; 201 indio_dev->channels = ms5611_channels; 202 indio_dev->num_channels = ARRAY_SIZE(ms5611_channels); 203 indio_dev->modes = INDIO_DIRECT_MODE; 204 205 ret = ms5611_init(indio_dev); 206 if (ret < 0) 207 return ret; 208 209 return devm_iio_device_register(dev, indio_dev); 210 } 211 EXPORT_SYMBOL(ms5611_probe); 212 213 MODULE_AUTHOR("Tomasz Duszynski <tduszyns@gmail.com>"); 214 MODULE_DESCRIPTION("MS5611 core driver"); 215 MODULE_LICENSE("GPL v2"); 216