brintos

brintos / linux-shallow public Read only

0
0
Text · 9.9 KiB · 2fc706f Raw
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