brintos

brintos / linux-shallow public Read only

0
0
Text · 16.6 KiB · aae7c56 Raw
614 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * Copyright (C) 2020 Invensense, Inc.4 */5 6#include <linux/kernel.h>7#include <linux/device.h>8#include <linux/mutex.h>9#include <linux/pm_runtime.h>10#include <linux/regmap.h>11#include <linux/delay.h>12 13#include <linux/iio/buffer.h>14#include <linux/iio/common/inv_sensors_timestamp.h>15#include <linux/iio/iio.h>16 17#include "inv_icm42600.h"18#include "inv_icm42600_buffer.h"19 20/* FIFO header: 1 byte */21#define INV_ICM42600_FIFO_HEADER_MSG		BIT(7)22#define INV_ICM42600_FIFO_HEADER_ACCEL		BIT(6)23#define INV_ICM42600_FIFO_HEADER_GYRO		BIT(5)24#define INV_ICM42600_FIFO_HEADER_TMST_FSYNC	GENMASK(3, 2)25#define INV_ICM42600_FIFO_HEADER_ODR_ACCEL	BIT(1)26#define INV_ICM42600_FIFO_HEADER_ODR_GYRO	BIT(0)27 28struct inv_icm42600_fifo_1sensor_packet {29	uint8_t header;30	struct inv_icm42600_fifo_sensor_data data;31	int8_t temp;32} __packed;33#define INV_ICM42600_FIFO_1SENSOR_PACKET_SIZE		834 35struct inv_icm42600_fifo_2sensors_packet {36	uint8_t header;37	struct inv_icm42600_fifo_sensor_data accel;38	struct inv_icm42600_fifo_sensor_data gyro;39	int8_t temp;40	__be16 timestamp;41} __packed;42#define INV_ICM42600_FIFO_2SENSORS_PACKET_SIZE		1643 44ssize_t inv_icm42600_fifo_decode_packet(const void *packet, const void **accel,45					const void **gyro, const int8_t **temp,46					const void **timestamp, unsigned int *odr)47{48	const struct inv_icm42600_fifo_1sensor_packet *pack1 = packet;49	const struct inv_icm42600_fifo_2sensors_packet *pack2 = packet;50	uint8_t header = *((const uint8_t *)packet);51 52	/* FIFO empty */53	if (header & INV_ICM42600_FIFO_HEADER_MSG) {54		*accel = NULL;55		*gyro = NULL;56		*temp = NULL;57		*timestamp = NULL;58		*odr = 0;59		return 0;60	}61 62	/* handle odr flags */63	*odr = 0;64	if (header & INV_ICM42600_FIFO_HEADER_ODR_GYRO)65		*odr |= INV_ICM42600_SENSOR_GYRO;66	if (header & INV_ICM42600_FIFO_HEADER_ODR_ACCEL)67		*odr |= INV_ICM42600_SENSOR_ACCEL;68 69	/* accel + gyro */70	if ((header & INV_ICM42600_FIFO_HEADER_ACCEL) &&71	    (header & INV_ICM42600_FIFO_HEADER_GYRO)) {72		*accel = &pack2->accel;73		*gyro = &pack2->gyro;74		*temp = &pack2->temp;75		*timestamp = &pack2->timestamp;76		return INV_ICM42600_FIFO_2SENSORS_PACKET_SIZE;77	}78 79	/* accel only */80	if (header & INV_ICM42600_FIFO_HEADER_ACCEL) {81		*accel = &pack1->data;82		*gyro = NULL;83		*temp = &pack1->temp;84		*timestamp = NULL;85		return INV_ICM42600_FIFO_1SENSOR_PACKET_SIZE;86	}87 88	/* gyro only */89	if (header & INV_ICM42600_FIFO_HEADER_GYRO) {90		*accel = NULL;91		*gyro = &pack1->data;92		*temp = &pack1->temp;93		*timestamp = NULL;94		return INV_ICM42600_FIFO_1SENSOR_PACKET_SIZE;95	}96 97	/* invalid packet if here */98	return -EINVAL;99}100 101void inv_icm42600_buffer_update_fifo_period(struct inv_icm42600_state *st)102{103	uint32_t period_gyro, period_accel, period;104 105	if (st->fifo.en & INV_ICM42600_SENSOR_GYRO)106		period_gyro = inv_icm42600_odr_to_period(st->conf.gyro.odr);107	else108		period_gyro = U32_MAX;109 110	if (st->fifo.en & INV_ICM42600_SENSOR_ACCEL)111		period_accel = inv_icm42600_odr_to_period(st->conf.accel.odr);112	else113		period_accel = U32_MAX;114 115	if (period_gyro <= period_accel)116		period = period_gyro;117	else118		period = period_accel;119 120	st->fifo.period = period;121}122 123int inv_icm42600_buffer_set_fifo_en(struct inv_icm42600_state *st,124				    unsigned int fifo_en)125{126	unsigned int mask, val;127	int ret;128 129	/* update only FIFO EN bits */130	mask = INV_ICM42600_FIFO_CONFIG1_TMST_FSYNC_EN |131		INV_ICM42600_FIFO_CONFIG1_TEMP_EN |132		INV_ICM42600_FIFO_CONFIG1_GYRO_EN |133		INV_ICM42600_FIFO_CONFIG1_ACCEL_EN;134 135	val = 0;136	if (fifo_en & INV_ICM42600_SENSOR_GYRO)137		val |= INV_ICM42600_FIFO_CONFIG1_GYRO_EN;138	if (fifo_en & INV_ICM42600_SENSOR_ACCEL)139		val |= INV_ICM42600_FIFO_CONFIG1_ACCEL_EN;140	if (fifo_en & INV_ICM42600_SENSOR_TEMP)141		val |= INV_ICM42600_FIFO_CONFIG1_TEMP_EN;142 143	ret = regmap_update_bits(st->map, INV_ICM42600_REG_FIFO_CONFIG1, mask, val);144	if (ret)145		return ret;146 147	st->fifo.en = fifo_en;148	inv_icm42600_buffer_update_fifo_period(st);149 150	return 0;151}152 153static size_t inv_icm42600_get_packet_size(unsigned int fifo_en)154{155	size_t packet_size;156 157	if ((fifo_en & INV_ICM42600_SENSOR_GYRO) &&158	    (fifo_en & INV_ICM42600_SENSOR_ACCEL))159		packet_size = INV_ICM42600_FIFO_2SENSORS_PACKET_SIZE;160	else161		packet_size = INV_ICM42600_FIFO_1SENSOR_PACKET_SIZE;162 163	return packet_size;164}165 166static unsigned int inv_icm42600_wm_truncate(unsigned int watermark,167					     size_t packet_size)168{169	size_t wm_size;170	unsigned int wm;171 172	wm_size = watermark * packet_size;173	if (wm_size > INV_ICM42600_FIFO_WATERMARK_MAX)174		wm_size = INV_ICM42600_FIFO_WATERMARK_MAX;175 176	wm = wm_size / packet_size;177 178	return wm;179}180 181/**182 * inv_icm42600_buffer_update_watermark - update watermark FIFO threshold183 * @st:	driver internal state184 *185 * Returns 0 on success, a negative error code otherwise.186 *187 * FIFO watermark threshold is computed based on the required watermark values188 * set for gyro and accel sensors. Since watermark is all about acceptable data189 * latency, use the smallest setting between the 2. It means choosing the190 * smallest latency but this is not as simple as choosing the smallest watermark191 * value. Latency depends on watermark and ODR. It requires several steps:192 * 1) compute gyro and accel latencies and choose the smallest value.193 * 2) adapt the choosen latency so that it is a multiple of both gyro and accel194 *    ones. Otherwise it is possible that you don't meet a requirement. (for195 *    example with gyro @100Hz wm 4 and accel @100Hz with wm 6, choosing the196 *    value of 4 will not meet accel latency requirement because 6 is not a197 *    multiple of 4. You need to use the value 2.)198 * 3) Since all periods are multiple of each others, watermark is computed by199 *    dividing this computed latency by the smallest period, which corresponds200 *    to the FIFO frequency. Beware that this is only true because we are not201 *    using 500Hz frequency which is not a multiple of the others.202 */203int inv_icm42600_buffer_update_watermark(struct inv_icm42600_state *st)204{205	size_t packet_size, wm_size;206	unsigned int wm_gyro, wm_accel, watermark;207	uint32_t period_gyro, period_accel, period;208	uint32_t latency_gyro, latency_accel, latency;209	bool restore;210	__le16 raw_wm;211	int ret;212 213	packet_size = inv_icm42600_get_packet_size(st->fifo.en);214 215	/* compute sensors latency, depending on sensor watermark and odr */216	wm_gyro = inv_icm42600_wm_truncate(st->fifo.watermark.gyro, packet_size);217	wm_accel = inv_icm42600_wm_truncate(st->fifo.watermark.accel, packet_size);218	/* use us for odr to avoid overflow using 32 bits values */219	period_gyro = inv_icm42600_odr_to_period(st->conf.gyro.odr) / 1000UL;220	period_accel = inv_icm42600_odr_to_period(st->conf.accel.odr) / 1000UL;221	latency_gyro = period_gyro * wm_gyro;222	latency_accel = period_accel * wm_accel;223 224	/* 0 value for watermark means that the sensor is turned off */225	if (wm_gyro == 0 && wm_accel == 0)226		return 0;227 228	if (latency_gyro == 0) {229		watermark = wm_accel;230		st->fifo.watermark.eff_accel = wm_accel;231	} else if (latency_accel == 0) {232		watermark = wm_gyro;233		st->fifo.watermark.eff_gyro = wm_gyro;234	} else {235		/* compute the smallest latency that is a multiple of both */236		if (latency_gyro <= latency_accel)237			latency = latency_gyro - (latency_accel % latency_gyro);238		else239			latency = latency_accel - (latency_gyro % latency_accel);240		/* use the shortest period */241		if (period_gyro <= period_accel)242			period = period_gyro;243		else244			period = period_accel;245		/* all this works because periods are multiple of each others */246		watermark = latency / period;247		if (watermark < 1)248			watermark = 1;249		/* update effective watermark */250		st->fifo.watermark.eff_gyro = latency / period_gyro;251		if (st->fifo.watermark.eff_gyro < 1)252			st->fifo.watermark.eff_gyro = 1;253		st->fifo.watermark.eff_accel = latency / period_accel;254		if (st->fifo.watermark.eff_accel < 1)255			st->fifo.watermark.eff_accel = 1;256	}257 258	/* compute watermark value in bytes */259	wm_size = watermark * packet_size;260 261	/* changing FIFO watermark requires to turn off watermark interrupt */262	ret = regmap_update_bits_check(st->map, INV_ICM42600_REG_INT_SOURCE0,263				       INV_ICM42600_INT_SOURCE0_FIFO_THS_INT1_EN,264				       0, &restore);265	if (ret)266		return ret;267 268	raw_wm = INV_ICM42600_FIFO_WATERMARK_VAL(wm_size);269	memcpy(st->buffer, &raw_wm, sizeof(raw_wm));270	ret = regmap_bulk_write(st->map, INV_ICM42600_REG_FIFO_WATERMARK,271				st->buffer, sizeof(raw_wm));272	if (ret)273		return ret;274 275	/* restore watermark interrupt */276	if (restore) {277		ret = regmap_set_bits(st->map, INV_ICM42600_REG_INT_SOURCE0,278				      INV_ICM42600_INT_SOURCE0_FIFO_THS_INT1_EN);279		if (ret)280			return ret;281	}282 283	return 0;284}285 286static int inv_icm42600_buffer_preenable(struct iio_dev *indio_dev)287{288	struct inv_icm42600_state *st = iio_device_get_drvdata(indio_dev);289	struct device *dev = regmap_get_device(st->map);290	struct inv_icm42600_sensor_state *sensor_st = iio_priv(indio_dev);291	struct inv_sensors_timestamp *ts = &sensor_st->ts;292 293	pm_runtime_get_sync(dev);294 295	mutex_lock(&st->lock);296	inv_sensors_timestamp_reset(ts);297	mutex_unlock(&st->lock);298 299	return 0;300}301 302/*303 * update_scan_mode callback is turning sensors on and setting data FIFO enable304 * bits.305 */306static int inv_icm42600_buffer_postenable(struct iio_dev *indio_dev)307{308	struct inv_icm42600_state *st = iio_device_get_drvdata(indio_dev);309	int ret;310 311	mutex_lock(&st->lock);312 313	/* exit if FIFO is already on */314	if (st->fifo.on) {315		ret = 0;316		goto out_on;317	}318 319	/* set FIFO threshold interrupt */320	ret = regmap_set_bits(st->map, INV_ICM42600_REG_INT_SOURCE0,321			      INV_ICM42600_INT_SOURCE0_FIFO_THS_INT1_EN);322	if (ret)323		goto out_unlock;324 325	/* flush FIFO data */326	ret = regmap_write(st->map, INV_ICM42600_REG_SIGNAL_PATH_RESET,327			   INV_ICM42600_SIGNAL_PATH_RESET_FIFO_FLUSH);328	if (ret)329		goto out_unlock;330 331	/* set FIFO in streaming mode */332	ret = regmap_write(st->map, INV_ICM42600_REG_FIFO_CONFIG,333			   INV_ICM42600_FIFO_CONFIG_STREAM);334	if (ret)335		goto out_unlock;336 337	/* workaround: first read of FIFO count after reset is always 0 */338	ret = regmap_bulk_read(st->map, INV_ICM42600_REG_FIFO_COUNT, st->buffer, 2);339	if (ret)340		goto out_unlock;341 342out_on:343	/* increase FIFO on counter */344	st->fifo.on++;345out_unlock:346	mutex_unlock(&st->lock);347	return ret;348}349 350static int inv_icm42600_buffer_predisable(struct iio_dev *indio_dev)351{352	struct inv_icm42600_state *st = iio_device_get_drvdata(indio_dev);353	int ret;354 355	mutex_lock(&st->lock);356 357	/* exit if there are several sensors using the FIFO */358	if (st->fifo.on > 1) {359		ret = 0;360		goto out_off;361	}362 363	/* set FIFO in bypass mode */364	ret = regmap_write(st->map, INV_ICM42600_REG_FIFO_CONFIG,365			   INV_ICM42600_FIFO_CONFIG_BYPASS);366	if (ret)367		goto out_unlock;368 369	/* flush FIFO data */370	ret = regmap_write(st->map, INV_ICM42600_REG_SIGNAL_PATH_RESET,371			   INV_ICM42600_SIGNAL_PATH_RESET_FIFO_FLUSH);372	if (ret)373		goto out_unlock;374 375	/* disable FIFO threshold interrupt */376	ret = regmap_clear_bits(st->map, INV_ICM42600_REG_INT_SOURCE0,377				INV_ICM42600_INT_SOURCE0_FIFO_THS_INT1_EN);378	if (ret)379		goto out_unlock;380 381out_off:382	/* decrease FIFO on counter */383	st->fifo.on--;384out_unlock:385	mutex_unlock(&st->lock);386	return ret;387}388 389static int inv_icm42600_buffer_postdisable(struct iio_dev *indio_dev)390{391	struct inv_icm42600_state *st = iio_device_get_drvdata(indio_dev);392	struct device *dev = regmap_get_device(st->map);393	unsigned int sensor;394	unsigned int *watermark;395	struct inv_icm42600_sensor_conf conf = INV_ICM42600_SENSOR_CONF_INIT;396	unsigned int sleep_temp = 0;397	unsigned int sleep_sensor = 0;398	unsigned int sleep;399	int ret;400 401	if (indio_dev == st->indio_gyro) {402		sensor = INV_ICM42600_SENSOR_GYRO;403		watermark = &st->fifo.watermark.gyro;404	} else if (indio_dev == st->indio_accel) {405		sensor = INV_ICM42600_SENSOR_ACCEL;406		watermark = &st->fifo.watermark.accel;407	} else {408		return -EINVAL;409	}410 411	mutex_lock(&st->lock);412 413	ret = inv_icm42600_buffer_set_fifo_en(st, st->fifo.en & ~sensor);414	if (ret)415		goto out_unlock;416 417	*watermark = 0;418	ret = inv_icm42600_buffer_update_watermark(st);419	if (ret)420		goto out_unlock;421 422	conf.mode = INV_ICM42600_SENSOR_MODE_OFF;423	if (sensor == INV_ICM42600_SENSOR_GYRO)424		ret = inv_icm42600_set_gyro_conf(st, &conf, &sleep_sensor);425	else426		ret = inv_icm42600_set_accel_conf(st, &conf, &sleep_sensor);427	if (ret)428		goto out_unlock;429 430	/* if FIFO is off, turn temperature off */431	if (!st->fifo.on)432		ret = inv_icm42600_set_temp_conf(st, false, &sleep_temp);433 434out_unlock:435	mutex_unlock(&st->lock);436 437	/* sleep maximum required time */438	sleep = max(sleep_sensor, sleep_temp);439	if (sleep)440		msleep(sleep);441 442	pm_runtime_mark_last_busy(dev);443	pm_runtime_put_autosuspend(dev);444 445	return ret;446}447 448const struct iio_buffer_setup_ops inv_icm42600_buffer_ops = {449	.preenable = inv_icm42600_buffer_preenable,450	.postenable = inv_icm42600_buffer_postenable,451	.predisable = inv_icm42600_buffer_predisable,452	.postdisable = inv_icm42600_buffer_postdisable,453};454 455int inv_icm42600_buffer_fifo_read(struct inv_icm42600_state *st,456				  unsigned int max)457{458	size_t max_count;459	__be16 *raw_fifo_count;460	ssize_t i, size;461	const void *accel, *gyro, *timestamp;462	const int8_t *temp;463	unsigned int odr;464	int ret;465 466	/* reset all samples counters */467	st->fifo.count = 0;468	st->fifo.nb.gyro = 0;469	st->fifo.nb.accel = 0;470	st->fifo.nb.total = 0;471 472	/* compute maximum FIFO read size */473	if (max == 0)474		max_count = sizeof(st->fifo.data);475	else476		max_count = max * inv_icm42600_get_packet_size(st->fifo.en);477 478	/* read FIFO count value */479	raw_fifo_count = (__be16 *)st->buffer;480	ret = regmap_bulk_read(st->map, INV_ICM42600_REG_FIFO_COUNT,481			       raw_fifo_count, sizeof(*raw_fifo_count));482	if (ret)483		return ret;484	st->fifo.count = be16_to_cpup(raw_fifo_count);485 486	/* check and clamp FIFO count value */487	if (st->fifo.count == 0)488		return 0;489	if (st->fifo.count > max_count)490		st->fifo.count = max_count;491 492	/* read all FIFO data in internal buffer */493	ret = regmap_noinc_read(st->map, INV_ICM42600_REG_FIFO_DATA,494				st->fifo.data, st->fifo.count);495	if (ret)496		return ret;497 498	/* compute number of samples for each sensor */499	for (i = 0; i < st->fifo.count; i += size) {500		size = inv_icm42600_fifo_decode_packet(&st->fifo.data[i],501				&accel, &gyro, &temp, &timestamp, &odr);502		if (size <= 0)503			break;504		if (gyro != NULL && inv_icm42600_fifo_is_data_valid(gyro))505			st->fifo.nb.gyro++;506		if (accel != NULL && inv_icm42600_fifo_is_data_valid(accel))507			st->fifo.nb.accel++;508		st->fifo.nb.total++;509	}510 511	return 0;512}513 514int inv_icm42600_buffer_fifo_parse(struct inv_icm42600_state *st)515{516	struct inv_icm42600_sensor_state *gyro_st = iio_priv(st->indio_gyro);517	struct inv_icm42600_sensor_state *accel_st = iio_priv(st->indio_accel);518	struct inv_sensors_timestamp *ts;519	int ret;520 521	if (st->fifo.nb.total == 0)522		return 0;523 524	/* handle gyroscope timestamp and FIFO data parsing */525	if (st->fifo.nb.gyro > 0) {526		ts = &gyro_st->ts;527		inv_sensors_timestamp_interrupt(ts, st->fifo.watermark.eff_gyro,528						st->timestamp.gyro);529		ret = inv_icm42600_gyro_parse_fifo(st->indio_gyro);530		if (ret)531			return ret;532	}533 534	/* handle accelerometer timestamp and FIFO data parsing */535	if (st->fifo.nb.accel > 0) {536		ts = &accel_st->ts;537		inv_sensors_timestamp_interrupt(ts, st->fifo.watermark.eff_accel,538						st->timestamp.accel);539		ret = inv_icm42600_accel_parse_fifo(st->indio_accel);540		if (ret)541			return ret;542	}543 544	return 0;545}546 547int inv_icm42600_buffer_hwfifo_flush(struct inv_icm42600_state *st,548				     unsigned int count)549{550	struct inv_icm42600_sensor_state *gyro_st = iio_priv(st->indio_gyro);551	struct inv_icm42600_sensor_state *accel_st = iio_priv(st->indio_accel);552	struct inv_sensors_timestamp *ts;553	int64_t gyro_ts, accel_ts;554	int ret;555 556	gyro_ts = iio_get_time_ns(st->indio_gyro);557	accel_ts = iio_get_time_ns(st->indio_accel);558 559	ret = inv_icm42600_buffer_fifo_read(st, count);560	if (ret)561		return ret;562 563	if (st->fifo.nb.total == 0)564		return 0;565 566	if (st->fifo.nb.gyro > 0) {567		ts = &gyro_st->ts;568		inv_sensors_timestamp_interrupt(ts, st->fifo.nb.gyro, gyro_ts);569		ret = inv_icm42600_gyro_parse_fifo(st->indio_gyro);570		if (ret)571			return ret;572	}573 574	if (st->fifo.nb.accel > 0) {575		ts = &accel_st->ts;576		inv_sensors_timestamp_interrupt(ts, st->fifo.nb.accel, accel_ts);577		ret = inv_icm42600_accel_parse_fifo(st->indio_accel);578		if (ret)579			return ret;580	}581 582	return 0;583}584 585int inv_icm42600_buffer_init(struct inv_icm42600_state *st)586{587	unsigned int val;588	int ret;589 590	st->fifo.watermark.eff_gyro = 1;591	st->fifo.watermark.eff_accel = 1;592 593	/*594	 * Default FIFO configuration (bits 7 to 5)595	 * - use invalid value596	 * - FIFO count in bytes597	 * - FIFO count in big endian598	 */599	val = INV_ICM42600_INTF_CONFIG0_FIFO_COUNT_ENDIAN;600	ret = regmap_update_bits(st->map, INV_ICM42600_REG_INTF_CONFIG0,601				 GENMASK(7, 5), val);602	if (ret)603		return ret;604 605	/*606	 * Enable FIFO partial read and continuous watermark interrupt.607	 * Disable all FIFO EN bits.608	 */609	val = INV_ICM42600_FIFO_CONFIG1_RESUME_PARTIAL_RD |610	      INV_ICM42600_FIFO_CONFIG1_WM_GT_TH;611	return regmap_update_bits(st->map, INV_ICM42600_REG_FIFO_CONFIG1,612				  GENMASK(6, 5) | GENMASK(3, 0), val);613}614