File Index
Files and directories
Sign in
FILE

inv_icm45600_buffer.c

File preview with syntax highlighting.

linux-6.19.14/drivers/iio/imu/inv_icm45600/inv_icm45600_buffer.c C
  1// SPDX-License-Identifier: GPL-2.0-or-later
  2/* Copyright (C) 2025 Invensense, Inc. */
  3
  4#include <linux/bitfield.h>
  5#include <linux/delay.h>
  6#include <linux/device.h>
  7#include <linux/err.h>
  8#include <linux/minmax.h>
  9#include <linux/mutex.h>
 10#include <linux/pm_runtime.h>
 11#include <linux/regmap.h>
 12#include <linux/time.h>
 13#include <linux/types.h>
 14
 15#include <asm/byteorder.h>
 16
 17#include <linux/iio/buffer.h>
 18#include <linux/iio/common/inv_sensors_timestamp.h>
 19#include <linux/iio/iio.h>
 20
 21#include "inv_icm45600_buffer.h"
 22#include "inv_icm45600.h"
 23
 24/* FIFO header: 1 byte */
 25#define INV_ICM45600_FIFO_EXT_HEADER		BIT(7)
 26#define INV_ICM45600_FIFO_HEADER_ACCEL		BIT(6)
 27#define INV_ICM45600_FIFO_HEADER_GYRO		BIT(5)
 28#define INV_ICM45600_FIFO_HEADER_HIGH_RES	BIT(4)
 29#define INV_ICM45600_FIFO_HEADER_TMST_FSYNC	GENMASK(3, 2)
 30#define INV_ICM45600_FIFO_HEADER_ODR_ACCEL	BIT(1)
 31#define INV_ICM45600_FIFO_HEADER_ODR_GYRO	BIT(0)
 32
 33struct inv_icm45600_fifo_1sensor_packet {
 34	u8 header;
 35	struct inv_icm45600_fifo_sensor_data data;
 36	s8 temp;
 37} __packed;
 38
 39struct inv_icm45600_fifo_2sensors_packet {
 40	u8 header;
 41	struct inv_icm45600_fifo_sensor_data accel;
 42	struct inv_icm45600_fifo_sensor_data gyro;
 43	s8 temp;
 44	__le16 timestamp;
 45} __packed;
 46
 47ssize_t inv_icm45600_fifo_decode_packet(const void *packet,
 48					const struct inv_icm45600_fifo_sensor_data **accel,
 49					const struct inv_icm45600_fifo_sensor_data **gyro,
 50					const s8 **temp,
 51					const __le16 **timestamp, unsigned int *odr)
 52{
 53	const struct inv_icm45600_fifo_1sensor_packet *pack1 = packet;
 54	const struct inv_icm45600_fifo_2sensors_packet *pack2 = packet;
 55	u8 header = *((const u8 *)packet);
 56
 57	/* FIFO extended header */
 58	if (header & INV_ICM45600_FIFO_EXT_HEADER) {
 59		/* Not yet supported */
 60		return 0;
 61	}
 62
 63	/* handle odr flags. */
 64	*odr = 0;
 65	if (header & INV_ICM45600_FIFO_HEADER_ODR_GYRO)
 66		*odr |= INV_ICM45600_SENSOR_GYRO;
 67	if (header & INV_ICM45600_FIFO_HEADER_ODR_ACCEL)
 68		*odr |= INV_ICM45600_SENSOR_ACCEL;
 69
 70	/* Accel + Gyro data are present. */
 71	if ((header & INV_ICM45600_FIFO_HEADER_ACCEL) &&
 72	    (header & INV_ICM45600_FIFO_HEADER_GYRO)) {
 73		*accel = &pack2->accel;
 74		*gyro = &pack2->gyro;
 75		*temp = &pack2->temp;
 76		*timestamp = &pack2->timestamp;
 77		return sizeof(*pack2);
 78	}
 79
 80	/* Accel data only. */
 81	if (header & INV_ICM45600_FIFO_HEADER_ACCEL) {
 82		*accel = &pack1->data;
 83		*gyro = NULL;
 84		*temp = &pack1->temp;
 85		*timestamp = NULL;
 86		return sizeof(*pack1);
 87	}
 88
 89	/* Gyro data only. */
 90	if (header & INV_ICM45600_FIFO_HEADER_GYRO) {
 91		*accel = NULL;
 92		*gyro = &pack1->data;
 93		*temp = &pack1->temp;
 94		*timestamp = NULL;
 95		return sizeof(*pack1);
 96	}
 97
 98	/* Invalid packet if here. */
 99	return -EINVAL;
100}
101
102void inv_icm45600_buffer_update_fifo_period(struct inv_icm45600_state *st)
103{
104	u32 period_gyro, period_accel;
105
106	if (st->fifo.en & INV_ICM45600_SENSOR_GYRO)
107		period_gyro = inv_icm45600_odr_to_period(st->conf.gyro.odr);
108	else
109		period_gyro = U32_MAX;
110
111	if (st->fifo.en & INV_ICM45600_SENSOR_ACCEL)
112		period_accel = inv_icm45600_odr_to_period(st->conf.accel.odr);
113	else
114		period_accel = U32_MAX;
115
116	st->fifo.period = min(period_gyro, period_accel);
117}
118
119int inv_icm45600_buffer_set_fifo_en(struct inv_icm45600_state *st,
120				    unsigned int fifo_en)
121{
122	unsigned int mask;
123	int ret;
124
125	mask = INV_ICM45600_FIFO_CONFIG3_GYRO_EN |
126	       INV_ICM45600_FIFO_CONFIG3_ACCEL_EN;
127
128	ret = regmap_assign_bits(st->map, INV_ICM45600_REG_FIFO_CONFIG3, mask,
129				 (fifo_en & INV_ICM45600_SENSOR_GYRO) ||
130				 (fifo_en & INV_ICM45600_SENSOR_ACCEL));
131	if (ret)
132		return ret;
133
134	st->fifo.en = fifo_en;
135	inv_icm45600_buffer_update_fifo_period(st);
136
137	return 0;
138}
139
140static unsigned int inv_icm45600_wm_truncate(unsigned int watermark, size_t packet_size,
141					     unsigned int fifo_period)
142{
143	size_t watermark_max, grace_samples;
144
145	/* Keep 20ms for processing FIFO.*/
146	grace_samples = (20U * NSEC_PER_MSEC) / fifo_period;
147	if (grace_samples < 1)
148		grace_samples = 1;
149
150	watermark_max = INV_ICM45600_FIFO_SIZE_MAX / packet_size;
151	watermark_max -= grace_samples;
152
153	return min(watermark, watermark_max);
154}
155
156/**
157 * inv_icm45600_buffer_update_watermark - update watermark FIFO threshold
158 * @st:	driver internal state
159 *
160 * FIFO watermark threshold is computed based on the required watermark values
161 * set for gyro and accel sensors. Since watermark is all about acceptable data
162 * latency, use the smallest setting between the 2. It means choosing the
163 * smallest latency but this is not as simple as choosing the smallest watermark
164 * value. Latency depends on watermark and ODR. It requires several steps:
165 * 1) compute gyro and accel latencies and choose the smallest value.
166 * 2) adapt the chosen latency so that it is a multiple of both gyro and accel
167 *    ones. Otherwise it is possible that you don't meet a requirement. (for
168 *    example with gyro @100Hz wm 4 and accel @100Hz with wm 6, choosing the
169 *    value of 4 will not meet accel latency requirement because 6 is not a
170 *    multiple of 4. You need to use the value 2.)
171 * 3) Since all periods are multiple of each others, watermark is computed by
172 *    dividing this computed latency by the smallest period, which corresponds
173 *    to the FIFO frequency.
174 *
175 * Returns: 0 on success, a negative error code otherwise.
176 */
177int inv_icm45600_buffer_update_watermark(struct inv_icm45600_state *st)
178{
179	const size_t packet_size = sizeof(struct inv_icm45600_fifo_2sensors_packet);
180	unsigned int wm_gyro, wm_accel, watermark;
181	u32 period_gyro, period_accel, period;
182	u32 latency_gyro, latency_accel, latency;
183
184	/* Compute sensors latency, depending on sensor watermark and odr. */
185	wm_gyro = inv_icm45600_wm_truncate(st->fifo.watermark.gyro, packet_size,
186					   st->fifo.period);
187	wm_accel = inv_icm45600_wm_truncate(st->fifo.watermark.accel, packet_size,
188					    st->fifo.period);
189	/* Use us for odr to avoid overflow using 32 bits values. */
190	period_gyro = inv_icm45600_odr_to_period(st->conf.gyro.odr) / NSEC_PER_USEC;
191	period_accel = inv_icm45600_odr_to_period(st->conf.accel.odr) / NSEC_PER_USEC;
192	latency_gyro = period_gyro * wm_gyro;
193	latency_accel = period_accel * wm_accel;
194
195	/* 0 value for watermark means that the sensor is turned off. */
196	if (wm_gyro == 0 && wm_accel == 0)
197		return 0;
198
199	if (latency_gyro == 0) {
200		watermark = wm_accel;
201		st->fifo.watermark.eff_accel = wm_accel;
202	} else if (latency_accel == 0) {
203		watermark = wm_gyro;
204		st->fifo.watermark.eff_gyro = wm_gyro;
205	} else {
206		/* Compute the smallest latency that is a multiple of both. */
207		if (latency_gyro <= latency_accel)
208			latency = latency_gyro - (latency_accel % latency_gyro);
209		else
210			latency = latency_accel - (latency_gyro % latency_accel);
211		/* Use the shortest period. */
212		period = min(period_gyro, period_accel);
213		/* All this works because periods are multiple of each others. */
214		watermark = max(latency / period, 1);
215		/* Update effective watermark. */
216		st->fifo.watermark.eff_gyro = max(latency / period_gyro, 1);
217		st->fifo.watermark.eff_accel = max(latency / period_accel, 1);
218	}
219
220	st->buffer.u16 = cpu_to_le16(watermark);
221	return regmap_bulk_write(st->map, INV_ICM45600_REG_FIFO_WATERMARK,
222				 &st->buffer.u16, sizeof(st->buffer.u16));
223}
224
225static int inv_icm45600_buffer_preenable(struct iio_dev *indio_dev)
226{
227	struct inv_icm45600_state *st = iio_device_get_drvdata(indio_dev);
228	struct device *dev = regmap_get_device(st->map);
229	struct inv_icm45600_sensor_state *sensor_st = iio_priv(indio_dev);
230	struct inv_sensors_timestamp *ts = &sensor_st->ts;
231	int ret;
232
233	ret = pm_runtime_resume_and_get(dev);
234	if (ret)
235		return ret;
236
237	guard(mutex)(&st->lock);
238	inv_sensors_timestamp_reset(ts);
239
240	return 0;
241}
242
243/*
244 * Update_scan_mode callback is turning sensors on and setting data FIFO enable
245 * bits.
246 */
247static int inv_icm45600_buffer_postenable(struct iio_dev *indio_dev)
248{
249	struct inv_icm45600_state *st = iio_device_get_drvdata(indio_dev);
250	unsigned int val;
251	int ret;
252
253	guard(mutex)(&st->lock);
254
255	/* Exit if FIFO is already on. */
256	if (st->fifo.on) {
257		st->fifo.on++;
258		return 0;
259	}
260
261	ret = regmap_set_bits(st->map, INV_ICM45600_REG_FIFO_CONFIG2,
262			      INV_ICM45600_REG_FIFO_CONFIG2_FIFO_FLUSH);
263	if (ret)
264		return ret;
265
266	ret = regmap_set_bits(st->map, INV_ICM45600_REG_INT1_CONFIG0,
267			      INV_ICM45600_INT1_CONFIG0_FIFO_THS_EN |
268			      INV_ICM45600_INT1_CONFIG0_FIFO_FULL_EN);
269	if (ret)
270		return ret;
271
272	val = FIELD_PREP(INV_ICM45600_FIFO_CONFIG0_MODE_MASK,
273			 INV_ICM45600_FIFO_CONFIG0_MODE_STREAM);
274	ret = regmap_update_bits(st->map, INV_ICM45600_REG_FIFO_CONFIG0,
275				 INV_ICM45600_FIFO_CONFIG0_MODE_MASK, val);
276	if (ret)
277		return ret;
278
279	/* Enable writing sensor data to FIFO. */
280	ret = regmap_set_bits(st->map, INV_ICM45600_REG_FIFO_CONFIG3,
281			      INV_ICM45600_FIFO_CONFIG3_IF_EN);
282	if (ret)
283		return ret;
284
285	st->fifo.on++;
286	return 0;
287}
288
289static int inv_icm45600_buffer_predisable(struct iio_dev *indio_dev)
290{
291	struct inv_icm45600_state *st = iio_device_get_drvdata(indio_dev);
292	unsigned int val;
293	int ret;
294
295	guard(mutex)(&st->lock);
296
297	/* Exit if there are several sensors using the FIFO. */
298	if (st->fifo.on > 1) {
299		st->fifo.on--;
300		return 0;
301	}
302
303	/* Disable writing sensor data to FIFO. */
304	ret = regmap_clear_bits(st->map, INV_ICM45600_REG_FIFO_CONFIG3,
305				INV_ICM45600_FIFO_CONFIG3_IF_EN);
306	if (ret)
307		return ret;
308
309	val = FIELD_PREP(INV_ICM45600_FIFO_CONFIG0_MODE_MASK,
310			 INV_ICM45600_FIFO_CONFIG0_MODE_BYPASS);
311	ret = regmap_update_bits(st->map, INV_ICM45600_REG_FIFO_CONFIG0,
312				 INV_ICM45600_FIFO_CONFIG0_MODE_MASK, val);
313	if (ret)
314		return ret;
315
316	ret = regmap_clear_bits(st->map, INV_ICM45600_REG_INT1_CONFIG0,
317				INV_ICM45600_INT1_CONFIG0_FIFO_THS_EN |
318				INV_ICM45600_INT1_CONFIG0_FIFO_FULL_EN);
319	if (ret)
320		return ret;
321
322	ret = regmap_set_bits(st->map, INV_ICM45600_REG_FIFO_CONFIG2,
323			      INV_ICM45600_REG_FIFO_CONFIG2_FIFO_FLUSH);
324	if (ret)
325		return ret;
326
327	st->fifo.on--;
328	return 0;
329}
330
331static int _inv_icm45600_buffer_postdisable(struct inv_icm45600_state *st,
332					    unsigned int sensor, unsigned int *watermark,
333					    unsigned int *sleep)
334{
335	struct inv_icm45600_sensor_conf conf = INV_ICM45600_SENSOR_CONF_KEEP_VALUES;
336	int ret;
337
338	ret = inv_icm45600_buffer_set_fifo_en(st, st->fifo.en & ~sensor);
339	if (ret)
340		return ret;
341
342	*watermark = 0;
343	ret = inv_icm45600_buffer_update_watermark(st);
344	if (ret)
345		return ret;
346
347	conf.mode = INV_ICM45600_SENSOR_MODE_OFF;
348	if (sensor == INV_ICM45600_SENSOR_GYRO)
349		return inv_icm45600_set_gyro_conf(st, &conf, sleep);
350	else
351		return inv_icm45600_set_accel_conf(st, &conf, sleep);
352}
353
354static int inv_icm45600_buffer_postdisable(struct iio_dev *indio_dev)
355{
356	struct inv_icm45600_state *st = iio_device_get_drvdata(indio_dev);
357	struct device *dev = regmap_get_device(st->map);
358	unsigned int sensor;
359	unsigned int *watermark;
360	unsigned int sleep;
361	int ret;
362
363	if (indio_dev == st->indio_gyro) {
364		sensor = INV_ICM45600_SENSOR_GYRO;
365		watermark = &st->fifo.watermark.gyro;
366	} else if (indio_dev == st->indio_accel) {
367		sensor = INV_ICM45600_SENSOR_ACCEL;
368		watermark = &st->fifo.watermark.accel;
369	} else {
370		return -EINVAL;
371	}
372
373	sleep = 0;
374	scoped_guard(mutex, &st->lock)
375		ret = _inv_icm45600_buffer_postdisable(st, sensor, watermark, &sleep);
376
377	/* Sleep required time. */
378	if (sleep)
379		msleep(sleep);
380
381	pm_runtime_put_autosuspend(dev);
382
383	return ret;
384}
385
386const struct iio_buffer_setup_ops inv_icm45600_buffer_ops = {
387	.preenable = inv_icm45600_buffer_preenable,
388	.postenable = inv_icm45600_buffer_postenable,
389	.predisable = inv_icm45600_buffer_predisable,
390	.postdisable = inv_icm45600_buffer_postdisable,
391};
392
393int inv_icm45600_buffer_fifo_read(struct inv_icm45600_state *st,
394				  unsigned int max)
395{
396	const ssize_t packet_size = sizeof(struct inv_icm45600_fifo_2sensors_packet);
397	__le16 *raw_fifo_count;
398	size_t fifo_nb, i;
399	ssize_t size;
400	const struct inv_icm45600_fifo_sensor_data *accel, *gyro;
401	const __le16 *timestamp;
402	const s8 *temp;
403	unsigned int odr;
404	int ret;
405
406	/* Reset all samples counters. */
407	st->fifo.count = 0;
408	st->fifo.nb.gyro = 0;
409	st->fifo.nb.accel = 0;
410	st->fifo.nb.total = 0;
411
412	raw_fifo_count = &st->buffer.u16;
413	ret = regmap_bulk_read(st->map, INV_ICM45600_REG_FIFO_COUNT,
414			       raw_fifo_count, sizeof(*raw_fifo_count));
415	if (ret)
416		return ret;
417
418	/* Check and limit number of samples if requested. */
419	fifo_nb = le16_to_cpup(raw_fifo_count);
420	if (fifo_nb == 0)
421		return 0;
422	if (max > 0 && fifo_nb > max)
423		fifo_nb = max;
424
425	/* Try to read all FIFO data in internal buffer. */
426	st->fifo.count = fifo_nb * packet_size;
427	ret = regmap_noinc_read(st->map, INV_ICM45600_REG_FIFO_DATA,
428				st->fifo.data, st->fifo.count);
429	if (ret == -ENOTSUPP || ret == -EFBIG) {
430		/* Read full fifo is not supported, read samples one by one. */
431		ret = 0;
432		for (i = 0; i < st->fifo.count && ret == 0; i += packet_size)
433			ret = regmap_noinc_read(st->map, INV_ICM45600_REG_FIFO_DATA,
434						&st->fifo.data[i], packet_size);
435	}
436	if (ret)
437		return ret;
438
439	for (i = 0; i < st->fifo.count; i += size) {
440		size = inv_icm45600_fifo_decode_packet(&st->fifo.data[i], &accel, &gyro,
441						       &temp, &timestamp, &odr);
442		if (size <= 0)
443			/* No more sample in buffer */
444			break;
445		if (gyro && inv_icm45600_fifo_is_data_valid(gyro))
446			st->fifo.nb.gyro++;
447		if (accel && inv_icm45600_fifo_is_data_valid(accel))
448			st->fifo.nb.accel++;
449		st->fifo.nb.total++;
450	}
451
452	return 0;
453}
454
455int inv_icm45600_buffer_fifo_parse(struct inv_icm45600_state *st)
456{
457	struct inv_icm45600_sensor_state *gyro_st = iio_priv(st->indio_gyro);
458	struct inv_icm45600_sensor_state *accel_st = iio_priv(st->indio_accel);
459	struct inv_sensors_timestamp *ts;
460	int ret;
461
462	if (st->fifo.nb.total == 0)
463		return 0;
464
465	/* Handle gyroscope timestamp and FIFO data parsing. */
466	if (st->fifo.nb.gyro > 0) {
467		ts = &gyro_st->ts;
468		inv_sensors_timestamp_interrupt(ts, st->fifo.watermark.eff_gyro,
469						st->timestamp.gyro);
470		ret = inv_icm45600_gyro_parse_fifo(st->indio_gyro);
471		if (ret)
472			return ret;
473	}
474
475	/* Handle accelerometer timestamp and FIFO data parsing. */
476	if (st->fifo.nb.accel > 0) {
477		ts = &accel_st->ts;
478		inv_sensors_timestamp_interrupt(ts, st->fifo.watermark.eff_accel,
479						st->timestamp.accel);
480		ret = inv_icm45600_accel_parse_fifo(st->indio_accel);
481		if (ret)
482			return ret;
483	}
484
485	return 0;
486}
487
488int inv_icm45600_buffer_hwfifo_flush(struct inv_icm45600_state *st,
489				     unsigned int count)
490{
491	struct inv_icm45600_sensor_state *gyro_st = iio_priv(st->indio_gyro);
492	struct inv_icm45600_sensor_state *accel_st = iio_priv(st->indio_accel);
493	struct inv_sensors_timestamp *ts;
494	s64 gyro_ts, accel_ts;
495	int ret;
496
497	gyro_ts = iio_get_time_ns(st->indio_gyro);
498	accel_ts = iio_get_time_ns(st->indio_accel);
499
500	ret = inv_icm45600_buffer_fifo_read(st, count);
501	if (ret)
502		return ret;
503
504	if (st->fifo.nb.total == 0)
505		return 0;
506
507	if (st->fifo.nb.gyro > 0) {
508		ts = &gyro_st->ts;
509		inv_sensors_timestamp_interrupt(ts, st->fifo.nb.gyro, gyro_ts);
510		ret = inv_icm45600_gyro_parse_fifo(st->indio_gyro);
511		if (ret)
512			return ret;
513	}
514
515	if (st->fifo.nb.accel > 0) {
516		ts = &accel_st->ts;
517		inv_sensors_timestamp_interrupt(ts, st->fifo.nb.accel, accel_ts);
518		ret = inv_icm45600_accel_parse_fifo(st->indio_accel);
519		if (ret)
520			return ret;
521	}
522
523	return 0;
524}
525
526int inv_icm45600_buffer_init(struct inv_icm45600_state *st)
527{
528	int ret;
529	unsigned int val;
530
531	st->fifo.watermark.eff_gyro = 1;
532	st->fifo.watermark.eff_accel = 1;
533
534	/* Disable all FIFO EN bits. */
535	ret = regmap_write(st->map, INV_ICM45600_REG_FIFO_CONFIG3, 0);
536	if (ret)
537		return ret;
538
539	/* Disable FIFO and set depth. */
540	val = FIELD_PREP(INV_ICM45600_FIFO_CONFIG0_MODE_MASK,
541			 INV_ICM45600_FIFO_CONFIG0_MODE_BYPASS) |
542	      FIELD_PREP(INV_ICM45600_FIFO_CONFIG0_FIFO_DEPTH_MASK,
543			 INV_ICM45600_FIFO_CONFIG0_FIFO_DEPTH_MAX);
544
545	ret = regmap_write(st->map, INV_ICM45600_REG_FIFO_CONFIG0, val);
546	if (ret)
547		return ret;
548
549	/* Enable only timestamp in fifo, disable compression. */
550	ret = regmap_write(st->map, INV_ICM45600_REG_FIFO_CONFIG4,
551			   INV_ICM45600_FIFO_CONFIG4_TMST_FSYNC_EN);
552	if (ret)
553		return ret;
554
555	/* Enable FIFO continuous watermark interrupt. */
556	return regmap_set_bits(st->map, INV_ICM45600_REG_FIFO_CONFIG2,
557			       INV_ICM45600_REG_FIFO_CONFIG2_WM_GT_TH);
558}
~/ Read-only file preview.