457 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * MS5611 pressure and temperature sensor driver4 *5 * Copyright (c) Tomasz Duszynski <tduszyns@gmail.com>6 *7 * Data sheet:8 * http://www.meas-spec.com/downloads/MS5611-01BA03.pdf9 * http://www.meas-spec.com/downloads/MS5607-02BA03.pdf10 *11 */12 13#include <linux/module.h>14#include <linux/iio/iio.h>15#include <linux/delay.h>16#include <linux/regulator/consumer.h>17 18#include <linux/iio/sysfs.h>19#include <linux/iio/buffer.h>20#include <linux/iio/triggered_buffer.h>21#include <linux/iio/trigger_consumer.h>22#include "ms5611.h"23 24#define MS5611_INIT_OSR(_cmd, _conv_usec, _rate) \25 { .cmd = _cmd, .conv_usec = _conv_usec, .rate = _rate }26 27static const struct ms5611_osr ms5611_avail_pressure_osr[] = {28 MS5611_INIT_OSR(0x40, 600, 256),29 MS5611_INIT_OSR(0x42, 1170, 512),30 MS5611_INIT_OSR(0x44, 2280, 1024),31 MS5611_INIT_OSR(0x46, 4540, 2048),32 MS5611_INIT_OSR(0x48, 9040, 4096)33};34 35static const struct ms5611_osr ms5611_avail_temp_osr[] = {36 MS5611_INIT_OSR(0x50, 600, 256),37 MS5611_INIT_OSR(0x52, 1170, 512),38 MS5611_INIT_OSR(0x54, 2280, 1024),39 MS5611_INIT_OSR(0x56, 4540, 2048),40 MS5611_INIT_OSR(0x58, 9040, 4096)41};42 43static const char ms5611_show_osr[] = "256 512 1024 2048 4096";44 45static IIO_CONST_ATTR(oversampling_ratio_available, ms5611_show_osr);46 47static struct attribute *ms5611_attributes[] = {48 &iio_const_attr_oversampling_ratio_available.dev_attr.attr,49 NULL,50};51 52static const struct attribute_group ms5611_attribute_group = {53 .attrs = ms5611_attributes,54};55 56static bool ms5611_prom_is_valid(u16 *prom, size_t len)57{58 int i, j;59 uint16_t crc = 0, crc_orig = prom[7] & 0x000F;60 61 prom[7] &= 0xFF00;62 63 for (i = 0; i < len * 2; i++) {64 if (i % 2 == 1)65 crc ^= prom[i >> 1] & 0x00FF;66 else67 crc ^= prom[i >> 1] >> 8;68 69 for (j = 0; j < 8; j++) {70 if (crc & 0x8000)71 crc = (crc << 1) ^ 0x3000;72 else73 crc <<= 1;74 }75 }76 77 crc = (crc >> 12) & 0x000F;78 79 return crc == crc_orig;80}81 82static int ms5611_read_prom(struct iio_dev *indio_dev)83{84 int ret, i;85 struct ms5611_state *st = iio_priv(indio_dev);86 87 for (i = 0; i < MS5611_PROM_WORDS_NB; i++) {88 ret = st->read_prom_word(st, i, &st->prom[i]);89 if (ret < 0) {90 dev_err(&indio_dev->dev,91 "failed to read prom at %d\n", i);92 return ret;93 }94 }95 96 if (!ms5611_prom_is_valid(st->prom, MS5611_PROM_WORDS_NB)) {97 dev_err(&indio_dev->dev, "PROM integrity check failed\n");98 return -ENODEV;99 }100 101 return 0;102}103 104static int ms5611_read_temp_and_pressure(struct iio_dev *indio_dev,105 s32 *temp, s32 *pressure)106{107 int ret;108 struct ms5611_state *st = iio_priv(indio_dev);109 110 ret = st->read_adc_temp_and_pressure(st, temp, pressure);111 if (ret < 0) {112 dev_err(&indio_dev->dev,113 "failed to read temperature and pressure\n");114 return ret;115 }116 117 return st->compensate_temp_and_pressure(st, temp, pressure);118}119 120static int ms5611_temp_and_pressure_compensate(struct ms5611_state *st,121 s32 *temp, s32 *pressure)122{123 s32 t = *temp, p = *pressure;124 s64 off, sens, dt;125 126 dt = t - (st->prom[5] << 8);127 off = ((s64)st->prom[2] << 16) + ((st->prom[4] * dt) >> 7);128 sens = ((s64)st->prom[1] << 15) + ((st->prom[3] * dt) >> 8);129 130 t = 2000 + ((st->prom[6] * dt) >> 23);131 if (t < 2000) {132 s64 off2, sens2, t2;133 134 t2 = (dt * dt) >> 31;135 off2 = (5 * (t - 2000) * (t - 2000)) >> 1;136 sens2 = off2 >> 1;137 138 if (t < -1500) {139 s64 tmp = (t + 1500) * (t + 1500);140 141 off2 += 7 * tmp;142 sens2 += (11 * tmp) >> 1;143 }144 145 t -= t2;146 off -= off2;147 sens -= sens2;148 }149 150 *temp = t;151 *pressure = (((p * sens) >> 21) - off) >> 15;152 153 return 0;154}155 156static int ms5607_temp_and_pressure_compensate(struct ms5611_state *st,157 s32 *temp, s32 *pressure)158{159 s32 t = *temp, p = *pressure;160 s64 off, sens, dt;161 162 dt = t - (st->prom[5] << 8);163 off = ((s64)st->prom[2] << 17) + ((st->prom[4] * dt) >> 6);164 sens = ((s64)st->prom[1] << 16) + ((st->prom[3] * dt) >> 7);165 166 t = 2000 + ((st->prom[6] * dt) >> 23);167 if (t < 2000) {168 s64 off2, sens2, t2, tmp;169 170 t2 = (dt * dt) >> 31;171 tmp = (t - 2000) * (t - 2000);172 off2 = (61 * tmp) >> 4;173 sens2 = tmp << 1;174 175 if (t < -1500) {176 tmp = (t + 1500) * (t + 1500);177 off2 += 15 * tmp;178 sens2 += 8 * tmp;179 }180 181 t -= t2;182 off -= off2;183 sens -= sens2;184 }185 186 *temp = t;187 *pressure = (((p * sens) >> 21) - off) >> 15;188 189 return 0;190}191 192static int ms5611_reset(struct iio_dev *indio_dev)193{194 int ret;195 struct ms5611_state *st = iio_priv(indio_dev);196 197 ret = st->reset(st);198 if (ret < 0) {199 dev_err(&indio_dev->dev, "failed to reset device\n");200 return ret;201 }202 203 usleep_range(3000, 4000);204 205 return 0;206}207 208static irqreturn_t ms5611_trigger_handler(int irq, void *p)209{210 struct iio_poll_func *pf = p;211 struct iio_dev *indio_dev = pf->indio_dev;212 struct ms5611_state *st = iio_priv(indio_dev);213 /* Ensure buffer elements are naturally aligned */214 struct {215 s32 channels[2];216 s64 ts __aligned(8);217 } scan;218 int ret;219 220 mutex_lock(&st->lock);221 ret = ms5611_read_temp_and_pressure(indio_dev, &scan.channels[1],222 &scan.channels[0]);223 mutex_unlock(&st->lock);224 if (ret < 0)225 goto err;226 227 iio_push_to_buffers_with_timestamp(indio_dev, &scan,228 iio_get_time_ns(indio_dev));229 230err:231 iio_trigger_notify_done(indio_dev->trig);232 233 return IRQ_HANDLED;234}235 236static int ms5611_read_raw(struct iio_dev *indio_dev,237 struct iio_chan_spec const *chan,238 int *val, int *val2, long mask)239{240 int ret;241 s32 temp, pressure;242 struct ms5611_state *st = iio_priv(indio_dev);243 244 switch (mask) {245 case IIO_CHAN_INFO_PROCESSED:246 mutex_lock(&st->lock);247 ret = ms5611_read_temp_and_pressure(indio_dev,248 &temp, &pressure);249 mutex_unlock(&st->lock);250 if (ret < 0)251 return ret;252 253 switch (chan->type) {254 case IIO_TEMP:255 *val = temp * 10;256 return IIO_VAL_INT;257 case IIO_PRESSURE:258 *val = pressure / 1000;259 *val2 = (pressure % 1000) * 1000;260 return IIO_VAL_INT_PLUS_MICRO;261 default:262 return -EINVAL;263 }264 case IIO_CHAN_INFO_SCALE:265 switch (chan->type) {266 case IIO_TEMP:267 *val = 10;268 return IIO_VAL_INT;269 case IIO_PRESSURE:270 *val = 0;271 *val2 = 1000;272 return IIO_VAL_INT_PLUS_MICRO;273 default:274 return -EINVAL;275 }276 case IIO_CHAN_INFO_OVERSAMPLING_RATIO:277 if (chan->type != IIO_TEMP && chan->type != IIO_PRESSURE)278 break;279 mutex_lock(&st->lock);280 if (chan->type == IIO_TEMP)281 *val = (int)st->temp_osr->rate;282 else283 *val = (int)st->pressure_osr->rate;284 mutex_unlock(&st->lock);285 return IIO_VAL_INT;286 }287 288 return -EINVAL;289}290 291static const struct ms5611_osr *ms5611_find_osr(int rate,292 const struct ms5611_osr *osr,293 size_t count)294{295 unsigned int r;296 297 for (r = 0; r < count; r++)298 if ((unsigned short)rate == osr[r].rate)299 break;300 if (r >= count)301 return NULL;302 return &osr[r];303}304 305static int ms5611_write_raw(struct iio_dev *indio_dev,306 struct iio_chan_spec const *chan,307 int val, int val2, long mask)308{309 struct ms5611_state *st = iio_priv(indio_dev);310 const struct ms5611_osr *osr = NULL;311 int ret;312 313 if (mask != IIO_CHAN_INFO_OVERSAMPLING_RATIO)314 return -EINVAL;315 316 if (chan->type == IIO_TEMP)317 osr = ms5611_find_osr(val, ms5611_avail_temp_osr,318 ARRAY_SIZE(ms5611_avail_temp_osr));319 else if (chan->type == IIO_PRESSURE)320 osr = ms5611_find_osr(val, ms5611_avail_pressure_osr,321 ARRAY_SIZE(ms5611_avail_pressure_osr));322 if (!osr)323 return -EINVAL;324 325 ret = iio_device_claim_direct_mode(indio_dev);326 if (ret)327 return ret;328 329 mutex_lock(&st->lock);330 331 if (chan->type == IIO_TEMP)332 st->temp_osr = osr;333 else334 st->pressure_osr = osr;335 336 mutex_unlock(&st->lock);337 iio_device_release_direct_mode(indio_dev);338 339 return 0;340}341 342static const unsigned long ms5611_scan_masks[] = {0x3, 0};343 344static const struct iio_chan_spec ms5611_channels[] = {345 {346 .type = IIO_PRESSURE,347 .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) |348 BIT(IIO_CHAN_INFO_SCALE) |349 BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),350 .scan_index = 0,351 .scan_type = {352 .sign = 's',353 .realbits = 32,354 .storagebits = 32,355 .endianness = IIO_CPU,356 },357 },358 {359 .type = IIO_TEMP,360 .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) |361 BIT(IIO_CHAN_INFO_SCALE) |362 BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),363 .scan_index = 1,364 .scan_type = {365 .sign = 's',366 .realbits = 32,367 .storagebits = 32,368 .endianness = IIO_CPU,369 },370 },371 IIO_CHAN_SOFT_TIMESTAMP(2),372};373 374static const struct iio_info ms5611_info = {375 .read_raw = &ms5611_read_raw,376 .write_raw = &ms5611_write_raw,377 .attrs = &ms5611_attribute_group,378};379 380static int ms5611_init(struct iio_dev *indio_dev)381{382 int ret;383 384 /* Enable attached regulator if any. */385 ret = devm_regulator_get_enable(indio_dev->dev.parent, "vdd");386 if (ret)387 return ret;388 389 ret = ms5611_reset(indio_dev);390 if (ret < 0)391 return ret;392 393 ret = ms5611_read_prom(indio_dev);394 if (ret < 0)395 return ret;396 397 return 0;398}399 400int ms5611_probe(struct iio_dev *indio_dev, struct device *dev,401 const char *name, int type)402{403 int ret;404 struct ms5611_state *st = iio_priv(indio_dev);405 406 mutex_init(&st->lock);407 408 switch (type) {409 case MS5611:410 st->compensate_temp_and_pressure =411 ms5611_temp_and_pressure_compensate;412 break;413 case MS5607:414 st->compensate_temp_and_pressure =415 ms5607_temp_and_pressure_compensate;416 break;417 default:418 return -EINVAL;419 }420 421 st->temp_osr =422 &ms5611_avail_temp_osr[ARRAY_SIZE(ms5611_avail_temp_osr) - 1];423 st->pressure_osr =424 &ms5611_avail_pressure_osr[ARRAY_SIZE(ms5611_avail_pressure_osr)425 - 1];426 indio_dev->name = name;427 indio_dev->info = &ms5611_info;428 indio_dev->channels = ms5611_channels;429 indio_dev->num_channels = ARRAY_SIZE(ms5611_channels);430 indio_dev->modes = INDIO_DIRECT_MODE;431 indio_dev->available_scan_masks = ms5611_scan_masks;432 433 ret = ms5611_init(indio_dev);434 if (ret < 0)435 return ret;436 437 ret = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL,438 ms5611_trigger_handler, NULL);439 if (ret < 0) {440 dev_err(dev, "iio triggered buffer setup failed\n");441 return ret;442 }443 444 ret = devm_iio_device_register(dev, indio_dev);445 if (ret < 0) {446 dev_err(dev, "unable to register iio device\n");447 return ret;448 }449 450 return 0;451}452EXPORT_SYMBOL_NS(ms5611_probe, IIO_MS5611);453 454MODULE_AUTHOR("Tomasz Duszynski <tduszyns@gmail.com>");455MODULE_DESCRIPTION("MS5611 core driver");456MODULE_LICENSE("GPL v2");457