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] [thread-next>] [day] [month] [year] [list]
Message-ID:
 <FR2PPF4571F02BCC073F7740CBA818676388C00A@FR2PPF4571F02BC.DEUP281.PROD.OUTLOOK.COM>
Date: Thu, 4 Sep 2025 12:58:10 +0000
From: Remi Buisson <Remi.Buisson@....com>
To: Andy Shevchenko <andriy.shevchenko@...el.com>
CC: Jonathan Cameron <jic23@...nel.org>,
        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 v5 2/9] iio: imu: inv_icm45600: add new inv_icm45600
 driver

>
>
>From: Andy Shevchenko <andriy.shevchenko@...el.com> 
>Sent: Thursday, August 21, 2025 11:03 AM
>To: Remi Buisson <Remi.Buisson@....com>
>Cc: Jonathan Cameron <jic23@...nel.org>; 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 v5 2/9] iio: imu: inv_icm45600: add new inv_icm45600 driver
>
>On Wed, Aug 20, 2025 at 02:24:20PM +0000, Remi Buisson via B4 Relay wrote:
>> 
>> Core component of a new driver for InvenSense ICM-45600 devices.
>> It includes registers definition, main probe/setup, and device
>> utility functions.
>> 
>> ICM-456xx devices are latest generation of 6-axis IMU,
>> gyroscope+accelerometer and temperature sensor. This device
>> includes a 8K FIFO, supports I2C/I3C/SPI, and provides
>> intelligent motion features like pedometer, tilt detection,
>> and tap detection.
>
Hello Andy,

Thanks for this review.

>...
>
>> +#ifndef INV_ICM45600_H_
>> +#define INV_ICM45600_H_
>> +
>> +#include <linux/bitfield.h>
>> +#include <linux/bits.h>
>> +#include <linux/iio/common/inv_sensors_timestamp.h>
>> +#include <linux/iio/iio.h>
>> +#include <linux/types.h>
>
>Please, follow IWYU principle. Also, it's better to split out the IIO group as
>it's part of the subsystem this driver is for.

Sure, I already did a header review once, but it looks like it still need a fix.

>
>#include <linux/bitfield.h>
>#include <linux/bits.h>
>#include <linux/types.h>
>
>#include <linux/iio/common/inv_sensors_timestamp.h>
>#include <linux/iio/iio.h>
>
>(but again, the list of the headers seems incorrect / incomplete).
>
>...
>
>> +struct inv_icm45600_state {
>> +	struct mutex lock;
>
>No header for this.

Correct
>
>> +	struct regmap *map;
>
>No forward declaration.

Correct again
>
>> +	struct regulator *vddio_supply;
>
>Ditto.

Correct
>
>> +	struct iio_mount_matrix orientation;
>
>
>
>> +	struct iio_dev *indio_gyro;
>> +	struct iio_dev *indio_accel;
>> +	const struct inv_icm45600_chip_info *chip_info;
>> +	struct {
>> +		s64 gyro;
>> +		s64 accel;
>> +	} timestamp;
>> +	union {
>> +		u8 buff[2];
>> +		__le16 u16;
>> +		u8 ireg[3];
>> +	} buffer __aligned(IIO_DMA_MINALIGN);
>> +};
>
>...
>
>> +#define INV_ICM45600_FIFO_SIZE_MAX			(8 * 1024)
>
>SZ_8K from sizes.h ?

Definitely better this way.

>
>...
>
>> +#include <linux/bitfield.h>
>> +#include <linux/delay.h>
>> +#include <linux/device.h>
>> +#include <linux/err.h>
>> +#include <linux/iio/iio.h>
>> +#include <linux/limits.h>
>> +#include <linux/module.h>
>> +#include <linux/mutex.h>
>> +#include <linux/pm_runtime.h>
>> +#include <linux/property.h>
>> +#include <linux/regmap.h>
>> +#include <linux/regulator/consumer.h>
>> +#include <linux/types.h>
>
>As per above, please double check for IWYU principle.

Sure
>
>...
>
>> +static int inv_icm45600_ireg_read(struct regmap *map, unsigned int reg,
>> +				   u8 *data, size_t count)
>> +{
>> +	const struct device *dev = regmap_get_device(map);
>> +	struct inv_icm45600_state *st = dev_get_drvdata(dev);
>> +	unsigned int d;
>
>> +	ssize_t i;
>
>Why signed? Same comment for all similar cases.

My mistake.
>
>> +	int ret;
>> +
>> +	st->buffer.ireg[0] = FIELD_GET(INV_ICM45600_REG_BANK_MASK, reg);
>> +	st->buffer.ireg[1] = FIELD_GET(INV_ICM45600_REG_ADDR_MASK, reg);
>> +
>> +	/* Burst write address. */
>> +	ret = regmap_bulk_write(map, INV_ICM45600_REG_IREG_ADDR, st->buffer.ireg, 2);
>> +	/* Wait while the device is busy processing the address. */
>> +	fsleep(INV_ICM45600_IREG_DELAY_US);
>> +	if (ret)
>> +		return ret;
>> +
>> +	/* Read the data. */
>> +	for (i = 0; i < count; i++) {
>> +		ret = regmap_read(map, INV_ICM45600_REG_IREG_DATA, &d);
>> +		/* Wait while the device is busy processing the data. */
>> +		fsleep(INV_ICM45600_IREG_DELAY_US);
>> +		if (ret)
>> +			return ret;
>> +		data[i] = d;
>> +	}
>> +
>> +	return 0;
>> +}
>
>...
>
>> +	if (FIELD_GET(INV_ICM45600_REG_BANK_MASK, reg) == 0)
>
>Why not using positive conditional?

Sure, it will be easier to read.
>
>> +		return regmap_bulk_read(map, FIELD_GET(INV_ICM45600_REG_ADDR_MASK, reg),
>> +					val_buf, val_size);
>> +
>> +	return inv_icm45600_ireg_read(map, reg, val_buf, val_size);
>
>	if (FIELD_GET(INV_ICM45600_REG_BANK_MASK, reg))
>		return inv_icm45600_ireg_read(map, reg, val_buf, val_size);
>
>Ditto for other similar cases.

Yes
>
>...
>
>> +static int inv_icm45600_write(void *context, const void *data,
>> +				   size_t count)
>
>This is perfectly 1 line, please, check that the code utilises exactly 80 limit
>when there is a room. It's probably a wrapping done by the (mis)configured editor.

I probably did that one by hand... I'll fix.
>
>...
>
>> +static const struct regmap_config inv_icm45600_regmap_config = {
>> +	.reg_bits = 16,
>> +	.val_bits = 8,
>
>No cache?
>
If OK for you, we prefer to push this patch without cache.
And introduce it in another patchset.

>> +};
>
>...
>
>> +static const struct inv_icm45600_conf inv_icm45600_default_conf = {
>> +	.gyro = {
>> +		.mode = INV_ICM45600_SENSOR_MODE_OFF,
>> +		.fs = INV_ICM45686_GYRO_FS_2000DPS,
>> +		.odr = INV_ICM45600_ODR_800HZ_LN,
>> +		.filter = INV_ICM45600_GYRO_LP_AVG_SEL_8X,
>> +	},
>> +	.accel = {
>> +		.mode = INV_ICM45600_SENSOR_MODE_OFF,
>> +		.fs = INV_ICM45686_ACCEL_FS_16G,
>> +		.odr = INV_ICM45600_ODR_800HZ_LN,
>> +		.filter = INV_ICM45600_ACCEL_LP_AVG_SEL_4X,
>> +	},
>> +};
>
>Can you split the patch adding accel or gyro separately? I haven't checked all
>the details, so it might be not worth it, just consider it.

Accel and Gyro code is added later in the patchset.
I move these definitions to the corresponding patchs.

>
>...
>
>> +u32 inv_icm45600_odr_to_period(enum inv_icm45600_odr odr)
>> +{
>> +	static const u32 odr_periods[INV_ICM45600_ODR_MAX] = {
>> +		0, 0, 0,	/* reserved values */
>
>Make it one per line as the rest.

Sure.
>
>> +		156250,		/* 6.4kHz */
>> +		312500,		/* 3.2kHz */
>> +		625000,		/* 1.6kHz */
>> +		1250000,	/* 800kHz */
>> +		2500000,	/* 400Hz */
>> +		5000000,	/* 200Hz */
>> +		10000000,	/* 100Hz */
>> +		20000000,	/* 50Hz */
>> +		40000000,	/* 25Hz */
>> +		80000000,	/* 12.5Hz */
>> +		160000000,	/* 6.25Hz */
>> +		320000000,	/* 3.125Hz */
>> +		640000000,	/* 1.5625Hz */
>
>These seem to be times or so, can you use proper naming instead of _periods?

It actually corresponds to periods = 1/frequency.
It's time for sure, but using "period" is a bit more specific IMO.

>
>> +	};
>> +
>> +	return odr_periods[odr];
>> +}
>
>...
>
>> +int inv_icm45600_debugfs_reg(struct iio_dev *indio_dev, unsigned int reg,
>> +			     unsigned int writeval, unsigned int *readval)
>> +{
>> +	struct inv_icm45600_state *st = iio_device_get_drvdata(indio_dev);
>
>> +	int ret;
>
>Useless, just return directly.
Clear.

>
>> +	guard(mutex)(&st->lock);
>
>> +	if (readval)
>> +		ret = regmap_read(st->map, reg, readval);
>> +	else
>> +		ret = regmap_write(st->map, reg, writeval);
>> +
>> +	return ret;
>> +}
>
>...
>
>> +/**
>> + *  inv_icm45600_setup() - check and setup chip
>> + *  @st:	driver internal state
>> + *  @chip_info:	detected chip description
>> + *  @reset:	define whether a reset is required or not
>> + *  @bus_setup:	callback for setting up bus specific registers
>> + *
>> + *  Returns 0 on success, a negative error code otherwise.
>
>Please, run kernel-doc validator. It's not happy (Return section is missing)

kernel-doc does not complain on this, on my side. 
I ran kernel-doc.py -v -none drivers/iio/imu/inv_icm45600/*
Is there any option I'm missing.
Anyway, I will add the missing colon and check the result.

>
>> + */
>
>...
>
>> +	if (val != chip_info->whoami) {
>> +		if (val == U8_MAX || val == 0)
>
>Hmm... Perhaps in_range() ?

Not sure of the benefit of this change.
I prefer to keep it this way if OK for you.

>
>> +			return dev_err_probe(dev, -ENODEV,
>> +					     "Invalid whoami %#02x expected %#02x (%s)\n",
>> +					     val, chip_info->whoami, chip_info->name);
>
>> +		else
>
>Redundant 'else'.

Right, I will change that one.
>
>> +			dev_warn(dev, "Unexpected whoami %#02x expected %#02x (%s)\n",
>> +				 val, chip_info->whoami, chip_info->name);
>> +	}
>
>...
>
>> +		ret = regmap_write(st->map, INV_ICM45600_REG_MISC2,
>> +				   INV_ICM45600_MISC2_SOFT_RESET);
>> +		if (ret)
>> +			return ret;
>> +		/* IMU reset time: 1ms. */
>> +		fsleep(1000);
>
>Use 1 * USEC_PER_MSEC and drop useless comment after that.
>You will need time.h for it.

Thanks for the tip, clear improvement.
>
>> +
>> +		if (bus_setup) {
>> +			ret = bus_setup(st);
>> +			if (ret)
>> +				return ret;
>> +		}
>> +
>> +		ret = regmap_read(st->map, INV_ICM45600_REG_INT_STATUS, &val);
>> +		if (ret)
>> +			return ret;
>> +		if (!(val & INV_ICM45600_INT_STATUS_RESET_DONE)) {
>> +			dev_err(dev, "reset error, reset done bit not set\n");
>> +			return -ENODEV;
>> +		}
>
>...
>
>> +static int inv_icm45600_enable_regulator_vddio(struct inv_icm45600_state *st)
>> +{
>> +	int ret;
>> +
>> +	ret = regulator_enable(st->vddio_supply);
>> +	if (ret)
>> +		return ret;
>> +
>> +	/* Wait a little for supply ramp. */
>> +	fsleep(3000);
>
>As per above.
Yes.

>
>> +	return 0;
>> +}
>
>...
>
>> +int inv_icm45600_core_probe(struct regmap *regmap, const struct inv_icm45600_chip_info *chip_info,
>> +				bool reset, inv_icm45600_bus_setup bus_setup)
>> +{
>> +	struct device *dev = regmap_get_device(regmap);
>> +	struct fwnode_handle *fwnode;
>> +	struct inv_icm45600_state *st;
>> +	struct regmap *regmap_custom;
>> +	int ret;
>> +
>> +	regmap_custom = devm_regmap_init(dev, &inv_icm45600_regmap_bus,
>> +					 regmap, &inv_icm45600_regmap_config);
>
>There is room for 'regmap' on the previous line.

Sure, I'll reformat.
>
>> +	if (IS_ERR(regmap_custom))
>> +		return dev_err_probe(dev, PTR_ERR(regmap_custom), "Failed to register regmap\n");
>> +
>> +	st = devm_kzalloc(dev, sizeof(*st), GFP_KERNEL);
>> +	if (!st)
>> +		return dev_err_probe(dev, -ENOMEM, "Cannot allocate memory\n");
>> +
>> +	dev_set_drvdata(dev, st);
>> +
>> +	ret = devm_mutex_init(dev, &st->lock);
>> +	if (ret)
>> +		return ret;
>> +
>> +	st->map = regmap_custom;
>> +
>> +	ret = iio_read_mount_matrix(dev, &st->orientation);
>> +	if (ret)
>> +		return dev_err_probe(dev, ret, "Failed to retrieve mounting matrix\n");
>> +
>> +	st->vddio_supply = devm_regulator_get(dev, "vddio");
>> +	if (IS_ERR(st->vddio_supply))
>> +		return PTR_ERR(st->vddio_supply);
>> +
>> +	ret = devm_regulator_get_enable(dev, "vdd");
>> +	if (ret)
>> +		return dev_err_probe(dev, ret, "Failed to get vdd regulator\n");
>> +
>> +	/* IMU start-up time. */
>> +	fsleep(100000);
>
>100 * USEC_PER_MSEC
Yes.
>
>> +	ret = inv_icm45600_enable_regulator_vddio(st);
>> +	if (ret)
>> +		return ret;
>> +
>> +	ret = devm_add_action_or_reset(dev, inv_icm45600_disable_vddio_reg, st);
>> +	if (ret)
>> +		return ret;
>> +
>> +	ret = inv_icm45600_setup(st, chip_info, reset, bus_setup);
>> +	if (ret)
>> +		return ret;
>> +
>> +	ret = inv_icm45600_timestamp_setup(st);
>> +	if (ret)
>> +		return ret;
>> +
>> +	/* Setup runtime power management. */
>> +	ret = devm_pm_runtime_set_active_enabled(dev);
>> +	if (ret)
>> +		return ret;
>> +
>> +	pm_runtime_get_noresume(dev);
>> +	/* Suspend after 2 seconds. */
>> +	pm_runtime_set_autosuspend_delay(dev, 2000);
>
>2 * MSEC_PER_SEC and drop yet another useless comment.
Sure.
>
>> +	pm_runtime_use_autosuspend(dev);
>> +	pm_runtime_put(dev);
>> +
>> +	return 0;
>> +}
>
>...
>
>> +static int inv_icm45600_resume(struct device *dev)
>> +{
>> +	struct inv_icm45600_state *st = dev_get_drvdata(dev);
>> +	int ret = 0;
>
>Why assignment?

No reason, I'll remove it.
>
>> +	ret = pm_runtime_force_resume(dev);
>> +	if (ret)
>> +		return ret;
>> +
>> +	scoped_guard(mutex, &st->lock)
>> +		/* Restore sensors state. */
>> +		ret = inv_icm45600_set_pwr_mgmt0(st, st->suspended.gyro,
>> +						st->suspended.accel, NULL);
>
>With guard()() this whole construction will look better.

It's coming in later patch.
I thought it would better follow coding guidelines this way.
But let me know if it is not the case.

>
>> +	return ret;
>> +}
>
>-- 
>With Best Regards,
>Andy Shevchenko
>
>
>

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ