lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [day] [month] [year] [list]
Message-ID:
 <FR2PPF4571F02BC5D9696D082E7D59737EA8C28A@FR2PPF4571F02BC.DEUP281.PROD.OUTLOOK.COM>
Date: Mon, 11 Aug 2025 14:56:15 +0000
From: Remi Buisson <Remi.Buisson@....com>
To: Jonathan Cameron <jic23@...nel.org>,
        Remi Buisson via B4 Relay
	<devnull+remi.buisson.tdk.com@...nel.org>
CC: David Lechner <dlechner@...libre.com>,
        Nuno Sá
	<nuno.sa@...log.com>,
        Andy Shevchenko <andy@...nel.org>, Rob Herring
	<robh@...nel.org>,
        Krzysztof Kozlowski <krzk+dt@...nel.org>,
        Conor Dooley
	<conor+dt@...nel.org>,
        "linux-kernel@...r.kernel.org"
	<linux-kernel@...r.kernel.org>,
        "linux-iio@...r.kernel.org"
	<linux-iio@...r.kernel.org>,
        "devicetree@...r.kernel.org"
	<devicetree@...r.kernel.org>
Subject: RE: [PATCH v2 4/8] iio: imu: inv_icm45600: add IMU IIO devices

>
>
>From: Jonathan Cameron <jic23@...nel.org> 
>Sent: Thursday, July 17, 2025 4:48 PM
>To: Remi Buisson via B4 Relay <devnull+remi.buisson.tdk.com@...nel.org>
>Cc: Remi Buisson <Remi.Buisson@....com>; David Lechner <dlechner@...libre.com>; Nuno Sá <nuno.sa@...log.com>; Andy Shevchenko <andy@...nel.org>; Rob Herring <robh@...nel.org>; Krzysztof Kozlowski <krzk+dt@...nel.org>; Conor Dooley <conor+dt@...nel.org>; linux-kernel@...r.kernel.org; linux-iio@...r.kernel.org; devicetree@...r.kernel.org
>Subject: Re: [PATCH v2 4/8] iio: imu: inv_icm45600: add IMU IIO devices
>
>On Thu, 10 Jul 2025 08:57:59 +0000
>Remi Buisson via B4 Relay <devnull+remi.buisson.tdk.com@...nel.org> wrote:
>
>> From: Remi Buisson <remi.buisson@....com>
>> 
>> Add IIO devices for accelerometer and gyroscope sensors
>> with data polling interface and FIFO parsing.
>> Attributes: raw, scale, sampling_frequency, calibbias.
>> Temperature is available as a processed channel.
>> 
>> Signed-off-by: Remi Buisson <remi.buisson@....com>
>
>Hi Remi,
>
>A few comments inline.
>
>Jonathan
>
Thanks for the review, I sent a v3 of the patch before seeing this.
I'll include those fixes in v4 (with the fixes linked with your review of v3).
Remi
>
>> diff --git a/drivers/iio/imu/inv_icm45600/inv_icm45600_gyro.c b/drivers/iio/imu/inv_icm45600/inv_icm45600_gyro.c
>> new file mode 100644
>> index 0000000000000000000000000000000000000000..8ba2e8e54c738a3289726d6f23a46f5307ebdffe
>> --- /dev/null
>> +++ b/drivers/iio/imu/inv_icm45600/inv_icm45600_gyro.c
>> @@ -0,0 +1,809 @@
>> +// SPDX-License-Identifier: GPL-2.0-or-later
>> +/*
>> + * Copyright (C) 2025 Invensense, Inc.
>> + */
>> +
>> +#include <linux/delay.h>
>> +#include <linux/device.h>
>> +#include <linux/err.h>
>> +#include <linux/iio/buffer.h>
>> +#include <linux/iio/common/inv_sensors_timestamp.h>
>> +#include <linux/iio/iio.h>
>> +#include <linux/iio/kfifo_buf.h>
>> +#include <linux/math64.h>
>> +#include <linux/mutex.h>
>> +#include <linux/pm_runtime.h>
>> +#include <linux/regmap.h>
>> +#include <linux/types.h>
>> +
>> +#include "inv_icm45600_buffer.h"
>> +#include "inv_icm45600.h"
>> +
>> +enum inv_icm45600_gyro_scan {
>> +	INV_ICM45600_GYRO_SCAN_X,
>> +	INV_ICM45600_GYRO_SCAN_Y,
>> +	INV_ICM45600_GYRO_SCAN_Z,
>> +	INV_ICM45600_GYRO_SCAN_TEMP,
>> +	INV_ICM45600_GYRO_SCAN_TIMESTAMP,
>> +};
>> +
>> +static const struct iio_chan_spec_ext_info inv_icm45600_gyro_ext_infos[] = {
>> +	IIO_MOUNT_MATRIX(IIO_SHARED_BY_ALL, inv_icm45600_get_mount_matrix),
>> +	{ },
>
>No trailing comma on a terminating entry.
Ok.
>
>> +};
>> +
>> +#define INV_ICM45600_GYRO_CHAN(_modifier, _index, _ext_info)		\
>> +	{								\
>> +		.type = IIO_ANGL_VEL,					\
>> +		.modified = 1,						\
>> +		.channel2 = _modifier,					\
>> +		.info_mask_separate =					\
>> +			BIT(IIO_CHAN_INFO_RAW) |			\
>> +			BIT(IIO_CHAN_INFO_CALIBBIAS),			\
>> +		.info_mask_shared_by_type =				\
>> +			BIT(IIO_CHAN_INFO_SCALE),			\
>> +		.info_mask_shared_by_type_available =			\
>> +			BIT(IIO_CHAN_INFO_SCALE) |			\
>> +			BIT(IIO_CHAN_INFO_CALIBBIAS),			\
>> +		.info_mask_shared_by_all =				\
>> +			BIT(IIO_CHAN_INFO_SAMP_FREQ),			\
>> +		.info_mask_shared_by_all_available =			\
>> +			BIT(IIO_CHAN_INFO_SAMP_FREQ),			\
>> +		.scan_index = _index,					\
>> +		.scan_type = {						\
>> +			.sign = 's',					\
>> +			.realbits = 16,					\
>> +			.storagebits = 16,				\
>> +			.endianness = IIO_LE,				\
>> +		},							\
>> +		.ext_info = _ext_info,					\
>> +	}
>
>> +static const unsigned long inv_icm45600_gyro_scan_masks[] = {
>> +	/* 3-axis gyro + temperature */
>> +	BIT(INV_ICM45600_GYRO_SCAN_X) |
>> +	BIT(INV_ICM45600_GYRO_SCAN_Y) |
>> +	BIT(INV_ICM45600_GYRO_SCAN_Z) |
>> +	BIT(INV_ICM45600_GYRO_SCAN_TEMP),
>> +	0,
>
>No comma as that's a terminating entry so nothing should come after it.
Ok.
>
>> +};
>> +
>> +/* enable gyroscope sensor and FIFO write */
>> +static int inv_icm45600_gyro_update_scan_mode(struct iio_dev *indio_dev,
>> +					      const unsigned long *scan_mask)
>> +{
>> +	struct inv_icm45600_state *st = iio_device_get_drvdata(indio_dev);
>> +	struct inv_icm45600_sensor_state *gyro_st = iio_priv(indio_dev);
>> +	struct inv_icm45600_sensor_conf conf = INV_ICM45600_SENSOR_CONF_INIT;
>> +	unsigned int fifo_en = 0;
>> +	unsigned int sleep;
>> +	int ret;
>> +
>> +	scoped_guard(mutex, &st->lock) {
>> +		if (*scan_mask & BIT(INV_ICM45600_GYRO_SCAN_TEMP))
>> +			fifo_en |= INV_ICM45600_SENSOR_TEMP;
>> +
>> +		if (*scan_mask & (BIT(INV_ICM45600_GYRO_SCAN_X) |
>> +				 BIT(INV_ICM45600_GYRO_SCAN_Y) |
>> +				 BIT(INV_ICM45600_GYRO_SCAN_Z))) {
>> +			/* enable gyro sensor */
>> +			conf.mode = gyro_st->power_mode;
>> +			ret = inv_icm45600_set_gyro_conf(st, &conf, &sleep);
>> +			if (ret)
>> +				return ret;
>> +			fifo_en |= INV_ICM45600_SENSOR_GYRO;
>> +		}
>> +		/* update data FIFO write */
>> +		ret = inv_icm45600_buffer_set_fifo_en(st, fifo_en | st->fifo.en);
>> +	}
>> +	/* sleep required time */
>Can we just always sleep?
That will be a big loss of time when sensor is already switched on.
It would add a 60ms delay to each individual access.
>
>> +	if (sleep)
>> +		msleep(sleep);
>> +
>> +	return ret;
>> +}
>
>> +
>> +/* IIO format int + nano */
>> +const int inv_icm45600_gyro_scale[][2] = {
>> +	/* +/- 2000dps => 0.001065264 rad/s */
>> +	[INV_ICM45600_GYRO_FS_2000DPS] = {0, 1065264},
>> +	/* +/- 1000dps => 0.000532632 rad/s */
>> +	[INV_ICM45600_GYRO_FS_1000DPS] = {0, 532632},
>> +	/* +/- 500dps => 0.000266316 rad/s */
>> +	[INV_ICM45600_GYRO_FS_500DPS] = {0, 266316},
>> +	/* +/- 250dps => 0.000133158 rad/s */
>> +	[INV_ICM45600_GYRO_FS_250DPS] = {0, 133158},
>> +	/* +/- 125dps => 0.000066579 rad/s */
>> +	[INV_ICM45600_GYRO_FS_125DPS] = {0, 66579},
>> +	/* +/- 62.5dps => 0.000033290 rad/s */
>> +	[INV_ICM45600_GYRO_FS_62_5DPS] = {0, 33290},
>> +	/* +/- 31.25dps => 0.000016645 rad/s */
>> +	[INV_ICM45600_GYRO_FS_31_25DPS] = {0, 16645},
>> +	/* +/- 15.625dps => 0.000008322 rad/s */
>> +	[INV_ICM45600_GYRO_FS_15_625DPS] = {0, 8322},
>> +};
>> +
>> +/* IIO format int + nano */
>> +const int inv_icm45686_gyro_scale[][2] = {
>> +	/* +/- 4000dps => 0.002130529 rad/s */
>> +	[INV_ICM45686_GYRO_FS_4000DPS] = {0, 2130529},
>{ 0, 2130529 },
>
>preferred for IIO code.  Arbitrary choice I made a while back
>in the interests of homogeneous looking code.
Ok, I'll align the code on this.
>
>
>> +	/* +/- 2000dps => 0.001065264 rad/s */
>> +	[INV_ICM45686_GYRO_FS_2000DPS] = {0, 1065264},
>> +	/* +/- 1000dps => 0.000532632 rad/s */
>> +	[INV_ICM45686_GYRO_FS_1000DPS] = {0, 532632},
>> +	/* +/- 500dps => 0.000266316 rad/s */
>> +	[INV_ICM45686_GYRO_FS_500DPS] = {0, 266316},
>> +	/* +/- 250dps => 0.000133158 rad/s */
>> +	[INV_ICM45686_GYRO_FS_250DPS] = {0, 133158},
>> +	/* +/- 125dps => 0.000066579 rad/s */
>> +	[INV_ICM45686_GYRO_FS_125DPS] = {0, 66579},
>> +	/* +/- 62.5dps => 0.000033290 rad/s */
>> +	[INV_ICM45686_GYRO_FS_62_5DPS] = {0, 33290},
>> +	/* +/- 31.25dps => 0.000016645 rad/s */
>> +	[INV_ICM45686_GYRO_FS_31_25DPS] = {0, 16645},
>> +	/* +/- 15.625dps => 0.000008322 rad/s */
>> +	[INV_ICM45686_GYRO_FS_15_625DPS] = {0, 8322},
>> +};
>
>> +
>> +/* IIO format int + micro */
>> +static const int inv_icm45600_gyro_odr[] = {
>> +	/* 1.5625Hz */
>> +	1, 562500,
>	1, 562500, /* 1.5625Hz */
>	3, 125000, /* 3.125Hz */
>
>etc if you think the comments are needed.
Ok I'll update.
>
>> +	/* 3.125Hz */
>> +	3, 125000,
>> +	/* 6.25Hz */
>> +	6, 250000,
>> +	/* 12.5Hz */
>> +	12, 500000,
>> +	/* 25Hz */
>> +	25, 0,
>> +	/* 50Hz */
>> +	50, 0,
>> +	/* 100Hz */
>> +	100, 0,
>> +	/* 200Hz */
>> +	200, 0,
>> +	/* 400Hz */
>> +	400, 0,
>> +	/* 800Hz */
>> +	800, 0,
>> +	/* 1.6kHz */
>> +	1600, 0,
>> +	/* 3.2kHz */
>> +	3200, 0,
>> +	/* 6.4kHz */
>> +	6400, 0,
>> +};
>
>> +/*
>> + * Calibration bias values, IIO range format int + nano.
>> + * Value is limited to +/-62.5dps coded on 14 bits signed. Step is 7.5mdps.
>> + */
>> +static int inv_icm45600_gyro_calibbias[] = {
>> +	-1, 90830336,	/* min: -1.090830336 rad/s */
>> +	0, 133158,	/* step: 0.000133158 rad/s */
>> +	1, 90697178,	/* max: 1.090697178 rad/s */
>> +};
>
>> +static int inv_icm45600_gyro_write_offset(struct inv_icm45600_state *st,
>> +					  struct iio_chan_spec const *chan,
>> +					  int val, int val2)
>As this is calibbias, it's not really required to be in any particular
>units.  Often these are analog front end tweaks where that doesn't matter.
>So you could just have this take the integer value you compute instead
>of doing all this maths.  Also update the available ranges if you do that.
>
>
>I don't mind if you want something specific though (like radians) though
>as that's also compliant with the ABI.
I'd prefer to keep rad/s, to align with other invensense IMU.
>
>> +{
>> +	struct device *dev = regmap_get_device(st->map);
>> +	s64 val64, min, max;
>> +	unsigned int reg;
>> +	s16 offset;
>> +	int ret;
>> +
>> +	if (chan->type != IIO_ANGL_VEL)
>> +		return -EINVAL;
>> +
>> +	switch (chan->channel2) {
>> +	case IIO_MOD_X:
>> +		reg = INV_ICM45600_IPREG_SYS1_REG_42;
>> +		break;
>> +	case IIO_MOD_Y:
>> +		reg = INV_ICM45600_IPREG_SYS1_REG_56;
>> +		break;
>> +	case IIO_MOD_Z:
>> +		reg = INV_ICM45600_IPREG_SYS1_REG_70;
>> +		break;
>> +	default:
>> +		return -EINVAL;
>> +	}
>> +
>> +	/* inv_icm45600_gyro_calibbias: min - step - max in nano */
>> +	min = (s64)inv_icm45600_gyro_calibbias[0] * 1000000000LL -
>> +	      (s64)inv_icm45600_gyro_calibbias[1];
>> +	max = (s64)inv_icm45600_gyro_calibbias[4] * 1000000000LL +
>> +	      (s64)inv_icm45600_gyro_calibbias[5];
>> +	val64 = (s64)val * 1000000000LL;
>> +	if (val > 0)
>> +		val64 += (s64)val2;
>> +	else
>> +		val64 -= (s64)val2;
>> +	if (val64 < min || val64 > max)
>> +		return -EINVAL;
>> +
>> +	/*
>> +	 * convert rad/s to dps then to raw value
>> +	 * rad to dps: 180 / Pi
>> +	 * dps to raw 14 bits signed, max 62.5: 8192 / 62.5
>> +	 * val in nano (1000000000)
>> +	 * val * 180 * 8192 / (Pi * 1000000000 * 62.5)
>> +	 */
>> +	val64 = val64 * 180LL * 8192;
>> +	/* for rounding, add + or - divisor (314159265 * 625) divided by 2 */
>> +	if (val64 >= 0)
>> +		val64 += 314159265LL * 625LL / 2LL;
>> +	else
>> +		val64 -= 314159265LL * 625LL / 2LL;
>> +	offset = div64_s64(val64, 314159265LL * 625LL);
>> +
>> +	/* clamp value limited to 14 bits signed */
>> +	if (offset < -8192)
>> +		offset = -8192;
>> +	else if (offset > 8191)
>> +		offset = 8191;
>> +
>> +	st->buffer.u16 = cpu_to_le16(offset & INV_ICM45600_GYRO_OFFUSER_MASK);
>> +
>> +	ret = pm_runtime_resume_and_get(dev);
>> +	if (ret)
>> +		return ret;
>> +
>> +	scoped_guard(mutex, &st->lock)
>> +		ret = regmap_bulk_write(st->map, reg, &st->buffer.u16, sizeof(st->buffer.u16));
>> +
>> +	pm_runtime_put_autosuspend(dev);
>> +	return ret;
>> +}
>
>
>
>> +static int inv_icm45600_gyro_hwfifo_flush(struct iio_dev *indio_dev,
>> +					  unsigned int count)
>> +{
>> +	struct inv_icm45600_state *st = iio_device_get_drvdata(indio_dev);
>> +	int ret;
>> +
>> +	if (count == 0)
>> +		return 0;
>> +
>> +	guard(mutex)(&st->lock);
>> +
>> +	ret = inv_icm45600_buffer_hwfifo_flush(st, count);
>> +	if (!ret)
>> +		ret = st->fifo.nb.gyro;
>	if (ret)
>		return ret;
>
>	return s->fifo.nb.gyro;
>
>preferred slightly because it keeps the error path out of line.
>When reading a lot of code conventions like that an help.
>
Agreed, your suggestion is far clearer.
>> +
>> +	return ret;
>> +}
>
>> +
>> +struct iio_dev *inv_icm45600_gyro_init(struct inv_icm45600_state *st)
>> +{
>> +	struct device *dev = regmap_get_device(st->map);
>> +	const char *name;
>
>Pick an ordering.  If nothing else makes sense, reverse xmas tree
>isn't too ugly.
I'll fix.
>
>> +	struct inv_icm45600_sensor_state *gyro_st;
>> +	struct inv_sensors_timestamp_chip ts_chip;
>> +	struct iio_dev *indio_dev;
>> +	int ret;
>> +
>> +	name = devm_kasprintf(dev, GFP_KERNEL, "%s-gyro", st->chip_info->name);
>> +	if (!name)
>> +		return ERR_PTR(-ENOMEM);
>> +
>> +	indio_dev = devm_iio_device_alloc(dev, sizeof(*gyro_st));
>> +	if (!indio_dev)
>> +		return ERR_PTR(-ENOMEM);
>> +	gyro_st = iio_priv(indio_dev);
>> +
>> +	gyro_st->scales = st->chip_info->gyro_scales;
>> +	gyro_st->scales_len = st->chip_info->gyro_scales_len * 2;
>> +
>> +	/* low-noise by default at init */
>> +	gyro_st->power_mode = INV_ICM45600_SENSOR_MODE_LOW_NOISE;
>> +
>> +	/*
>> +	 * clock period is 32kHz (31250ns)
>> +	 * jitter is +/- 2% (20 per mille)
>> +	 */
>> +	ts_chip.clock_period = 31250;
>> +	ts_chip.jitter = 20;
>> +	ts_chip.init_period = inv_icm45600_odr_to_period(st->conf.gyro.odr);
>> +	inv_sensors_timestamp_init(&gyro_st->ts, &ts_chip);
>> +
>> +	iio_device_set_drvdata(indio_dev, st);
>> +	indio_dev->name = name;
>> +	indio_dev->info = &inv_icm45600_gyro_info;
>> +	indio_dev->modes = INDIO_DIRECT_MODE;
>> +	indio_dev->channels = inv_icm45600_gyro_channels;
>> +	indio_dev->num_channels = ARRAY_SIZE(inv_icm45600_gyro_channels);
>> +	indio_dev->available_scan_masks = inv_icm45600_gyro_scan_masks;
>> +	indio_dev->setup_ops = &inv_icm45600_buffer_ops;
>> +
>> +	ret = devm_iio_kfifo_buffer_setup(dev, indio_dev,
>> +					  &inv_icm45600_buffer_ops);
>> +	if (ret)
>> +		return ERR_PTR(ret);
>> +
>> +	ret = devm_iio_device_register(dev, indio_dev);
>> +	if (ret)
>> +		return ERR_PTR(ret);
>> +
>> +	return indio_dev;
>> +}
>> +
>> +int inv_icm45600_gyro_parse_fifo(struct iio_dev *indio_dev)
>> +{
>> +	struct inv_icm45600_state *st = iio_device_get_drvdata(indio_dev);
>> +	struct inv_icm45600_sensor_state *gyro_st = iio_priv(indio_dev);
>> +	struct inv_sensors_timestamp *ts = &gyro_st->ts;
>> +	ssize_t i, size;
>> +	unsigned int no;
>> +	const struct inv_icm45600_fifo_sensor_data *accel, *gyro;
>> +	const __le16 *timestamp;
>> +	const s8 *temp;
>> +	unsigned int odr;
>> +	s64 ts_val;
>> +	struct inv_icm45600_gyro_buffer buffer;
>
>I'd push scope of many of these into the for loop.
Sure, good point!
>
>> +
>> +	/* parse all fifo packets */
>> +	for (i = 0, no = 0; i < st->fifo.count; i += size, ++no) {
>> +		size = inv_icm45600_fifo_decode_packet(&st->fifo.data[i],
>> +				&accel, &gyro, &temp, &timestamp, &odr);
>> +		/* quit if error or FIFO is empty */
>> +		if (size <= 0)
>> +			return size;
>> +
>> +		/* skip packet if no gyro data or data is invalid */
>> +		if (gyro == NULL || !inv_icm45600_fifo_is_data_valid(gyro))
>> +			continue;
>> +
>> +		/* update odr */
>> +		if (odr & INV_ICM45600_SENSOR_GYRO)
>> +			inv_sensors_timestamp_apply_odr(ts, st->fifo.period,
>> +							st->fifo.nb.total, no);
>> +
>> +		/* buffer is copied to userspace, zeroing it to avoid any data leak */
>> +		memset(&buffer, 0, sizeof(buffer));
>> +		memcpy(&buffer.gyro, gyro, sizeof(buffer.gyro));
>> +		/* convert 8 bits FIFO temperature in high resolution format */
>> +		buffer.temp = temp ? (*temp * 64) : 0;
>> +		ts_val = inv_sensors_timestamp_pop(ts);
>> +		iio_push_to_buffers_with_timestamp(indio_dev, &buffer, ts_val);
>> +	}
>> +
>> +	return 0;
>> +}
>> 
>
>
>

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ