Devicetree
 help / color / mirror / Atom feed
From: Kanak Shilledar <Kanak.Shilledar@axis.com>
To: "joshua.crofts1@gmail.com" <joshua.crofts1@gmail.com>
Cc: "dlechner@baylibre.com" <dlechner@baylibre.com>,
	Henrik Grimler <Henrik.Grimler@axis.com>,
	"nuno.sa@analog.com" <nuno.sa@analog.com>,
	"jean-baptiste.maneyrol@tdk.com" <jean-baptiste.maneyrol@tdk.com>,
	"robh@kernel.org" <robh@kernel.org>,
	"jic23@kernel.org" <jic23@kernel.org>,
	"andy@kernel.org" <andy@kernel.org>,
	"krzk+dt@kernel.org" <krzk+dt@kernel.org>,
	"linux-iio@vger.kernel.org" <linux-iio@vger.kernel.org>,
	"conor+dt@kernel.org" <conor+dt@kernel.org>,
	Kernel <Kernel@axis.com>,
	"linux-kernel@vger.kernel.org" <linux-kernel@vger.kernel.org>,
	"devicetree@vger.kernel.org" <devicetree@vger.kernel.org>
Subject: Re: [PATCH 2/3] iio: accel: Add support for ICM42370P
Date: Fri, 7 Aug 2026 13:41:04 +0000	[thread overview]
Message-ID: <ba866b98384dcd827d5a610de9f00386da5824e2.camel@axis.com> (raw)
In-Reply-To: <20260807120937.00004e3c@gmail.com>

[-- Attachment #1: Type: text/plain, Size: 42711 bytes --]

Hi Joshua,

On Fri, 2026-08-07 at 12:09 +0200, Joshua Crofts wrote:
> [You don't often get email from joshua.crofts1@gmail.com. Learn why
> this is important at https://aka.ms/LearnAboutSenderIdentification ]
> 
> On Thu, 6 Aug 2026 14:46:28 +0200
> Kanak Shilledar <kanak.shilledar@axis.com> wrote:
> 
> > Add support for the Invensense ICM42370P MEMS MotionTracking 3-axis
> > accelerometer with a built-in temperature sensor. Compared to other
> > sensors from the same vendor ICM42370 uses a different way of
> > handling
> > register banks. Although the device supports I2C, SPI, and I3C,
> > implement only I2C support.  Provide basic support for raw sensor
> > reads and a sysfs interface for setting the calibration bias. Keep
> > the
> > embedded temperature sensor enabled because the device design does
> > not
> > allow it to be turned off.
> > 
> > Signed-off-by: Kanak Shilledar <kanak.shilledar@axis.com>
> > ---
> 
> Hi Kanak,
> 
> my comments inline. This is an large driver and I've probably missed
> something. Additionally, Sashiko had some pretty good remarks about
> the scaling math etc. so please check out those:


Thanks for the detailed review.

> 
> https://sashiko.dev/#/patchset/20260806-b4-inv_icm42370p-v1-0-670837f5842f%40axis.com

I am going through the sashiko's comments and incorporating them in my
v2.

> Josh
> 
> > +config INV_ICM42370
> > +     tristate
> > +     select IIO_BUFFER
> > +     select IIO_INV_SENSORS_TIMESTAMP
> > +
> > +config INV_ICM42370_I2C
> > +     tristate "InvenSense ICM-42370 I2C driver"
> > +     depends on I2C
> > +     select INV_ICM42370
> > +     select REGMAP_I2C
> > +     help
> > +       This driver supports the InvenSense ICM-42730 motion
> > tracking
> > +       devices over I2C.
> > +
> > +       This driver can be built as a module. The module will be
> > called
> > +       inv_icm42370_i2c.
> > +
> >  config KXSD9
> >       tristate "Kionix KXSD9 Accelerometer Driver"
> >       select IIO_BUFFER
> > diff --git a/drivers/iio/accel/Makefile
> > b/drivers/iio/accel/Makefile
> > index fa440a8592839..6750b03edf518 100644
> > --- a/drivers/iio/accel/Makefile
> > +++ b/drivers/iio/accel/Makefile
> > @@ -49,6 +49,11 @@ obj-$(CONFIG_HID_SENSOR_ACCEL_3D) += hid-sensor-
> > accel-3d.o
> >  obj-$(CONFIG_IIO_KX022A)     += kionix-kx022a.o
> >  obj-$(CONFIG_IIO_KX022A_I2C) += kionix-kx022a-i2c.o
> >  obj-$(CONFIG_IIO_KX022A_SPI) += kionix-kx022a-spi.o
> > +
> > +obj-$(CONFIG_INV_ICM42370) += inv-icm42370.o
> > +inv-icm42370-y += inv_icm42370_core.o
> > +obj-$(CONFIG_INV_ICM42370_I2C) += inv_icm42370_i2c.o
> > +
> >  obj-$(CONFIG_KXCJK1013) += kxcjk-1013.o
> >  obj-$(CONFIG_KXSD9)  += kxsd9.o
> >  obj-$(CONFIG_KXSD9_SPI)      += kxsd9-spi.o
> > diff --git a/drivers/iio/accel/inv_icm42370.h
> > b/drivers/iio/accel/inv_icm42370.h
> > new file mode 100644
> > index 0000000000000..9866a5e970dcd
> > --- /dev/null
> > +++ b/drivers/iio/accel/inv_icm42370.h
> > @@ -0,0 +1,365 @@
> > +/* SPDX-License-Identifier: GPL-2.0-or-later */
> > +/*
> > + * Copyright (C) 2020 Invensense, Inc.
> > + * Copyright (C) 2026 Axis Communications AB
> > + */
> > +
> > +#ifndef INV_ICM42370_H_
> > +#define INV_ICM42370_H_
> > +
> > +#include <linux/bits.h>
> > +#include <linux/bitfield.h>
> > +#include <linux/regmap.h>
> > +#include <linux/mutex.h>
> > +#include <linux/regulator/consumer.h>
> 
> Sort these headers alphabetically. Also you're missing types.h.
> 
> > +#include <linux/iio/iio.h>
> > +#include <linux/iio/common/inv_sensors_timestamp.h>
> 
> Group the <linux/iio/*> headers separately (check other drivers in
> IIO
> for reference).

Will sort and group the headers as per the convention.

> > +
> > +enum inv_icm42370_chip {
> > +     INV_CHIP_INVALID,
> > +     INV_CHIP_ICM42370,
> > +     INV_CHIP_NB,
> > +};
> > +
> > +/* sensor configuration struct */
> > +struct inv_icm42370_conf {
> > +     int mode;
> > +     int fs;
> > +     int odr;
> > +     int filter;
> > +};
> > +
> > +/**
> > + * struct inv_icm42370_data - driver state variables
> > + * @lock:            lock for serializing multiple register
> > access.
> > + * @name:            chip name.
> > + * @map:             regmap pointer.
> > + * @vdd_supply:              VDD voltage regulator for the chip.
> > + * @vddio_supply:    I/O voltage regulator for the chip.
> > + * @indio_accel:     accelerometer IIO device.
> > + * @sensor_state:    per-sensor state tracking (e.g. power, ODR).
> > + * @buffer:          buffer for reading data registers, aligned
> > for DMA.
> > + * @accel_calibbias: accelerometer calibration bias for X, Y, and
> > Z axes.
> > + * @fifo:            FIFO state and configuration.
> > + * @timestamp:               interrupt timestamp.
> > + * @chip:            chip identifier.
> > + * @conf:            chip sensors configurations.
> > + */
> > +struct inv_icm42370_data {
> > +     struct mutex lock;
> > +     const char *name;
> > +     struct regmap *map;
> > +     struct regulator *vdd_supply;
> > +     struct regulator *vddio_supply;
> > +     struct iio_dev *indio_accel;
> 
> You probably don't need this.
Can you please clarify this comment? As I am using `*indio_accel` in
other places inside inv_icm42370_core.c and in many places in
inv_icm42370_buffer.c. Or are you perhaps referring to the
*sensor_state struct below?

> 
> > +     struct inv_icm42370_sensor_state *sensor_state;
> > +     u8 buffer[2] __aligned(IIO_DMA_MINALIGN);
> 
> You shouldn't have a DMA buffer in the middle of your struct, move it
> to
> the end to ensure the buffer gets its own cacheline to prevent the
> CPU
> overwriting the DMA area on accident.

Will move the buffer property at the end.

> 
> > +     s16 accel_calibbias[3];
> > +     struct inv_icm42370_fifo fifo;
> 
> You add buffer support in patch 3, yet this is in patch 2, causing
> build
> failures if you compile without buffer support. Move this to patch 3.

Will move this to patch 3.

> 
> > +     s64 timestamp;
> > +     enum inv_icm42370_chip chip;
> > +     struct inv_icm42370_conf conf;
> > +};
> > +
[snip]
> > +
> > +/* Temperature sensor filters */
> > +enum inv_icm42370_temp_filter {
> > +     INV_ICM42370_TEMP_FILT_BW_DLPF_BYPASS,
> > +     INV_ICM42370_TEMP_FILT_BW_DLPF_180HZ,
> > +     INV_ICM42370_TEMP_FILT_BW_DLPF_72HZ,
> > +     INV_ICM42370_TEMP_FILT_BW_DLPF_34HZ,
> > +     INV_ICM42370_TEMP_FILT_BW_DLPF_16HZ,
> > +     INV_ICM42370_TEMP_FILT_BW_DLPF_8HZ,
> > +     INV_ICM42370_TEMP_FILT_BW_DLPF_4HZ,
> > +     INV_ICM42370_TEMP_FILT_BW_DLPF_NB,
> > +};
> > +
> > +/* IIO format int + micro */
> 
> No need for this comment.
> 
> > +static const int inv_icm42370_accel_odr[] = {
> > +     /* 1.5625Hz */
> 
> No need for these comments.

Will remove the comments.

> 
> > +     1, 562500,
> > +     /* 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,
> > +};
> > +
> > +#define INV_ICM42370_SENSOR_CONF_INIT { -1, -1, -1, -1 }
> > +
> > 
[snip]
> > diff --git a/drivers/iio/accel/inv_icm42370_core.c
> > b/drivers/iio/accel/inv_icm42370_core.c
> > new file mode 100644
> > index 0000000000000..9f6c302e6f331
> > --- /dev/null
> > +++ b/drivers/iio/accel/inv_icm42370_core.c
> > @@ -0,0 +1,1251 @@
> > +// SPDX-License-Identifier: GPL-2.0-or-later
> > +/*
> > + * Copyright (C) 2020 Invensense, Inc.
> > + * Copyright (C) 2026 Axis Communications AB
> > + */
> > +
> > +#include <linux/delay.h>
> > +#include <linux/device.h>
> > +#include <linux/i2c.h>
> > +#include <linux/irq.h>
> > +#include <linux/slab.h>
> > +#include <linux/mod_devicetable.h>
> 
> As Uwe said in his email, remove this.
> 
> > +#include <linux/module.h>
> > +#include <linux/pm_runtime.h>
> > +#include <linux/property.h>
> > +#include <linux/regmap.h>
> > +
> 
> You're missing types.h, bitops.h, err.h, array_size.h and
> <asm/byteorder.h>.

Will sort the includes, add the missing headers and remove the
<linux/mod_devicetable.h>.

> 
> > +#include <linux/iio/common/inv_sensors_timestamp.h>
> > +#include <linux/iio/iio.h>
> > +#include <linux/iio/sysfs.h>
> > +
> > +#include "inv_icm42370.h"
> > +
> > +const struct regmap_config inv_icm42370_regmap_config = {
> > +     .name = "inv_icm42370",
> > +     .reg_bits = 8,
> > +     .val_bits = 8,
> > +     .max_register = 0x7E,
> > +};
> > +EXPORT_SYMBOL_NS_GPL(inv_icm42370_regmap_config, "IIO_ICM42370");
> > +
> > 
[snip]
> > +/**
> > + *  inv_icm42370_odr_to_period() - map ODR to Period
> > + *  @odr - enum of ODR value
> > + *
> > + * Returns the period in nanoseconds
> > + */
> > +u32 inv_icm42370_odr_to_period(enum inv_icm42370_odr odr)
> > +{
> > +     static u32 odr_periods[INV_ICM42370_ODR_NB] = {
> > +             /* reserved values */
> > +             0,
> > +             0,
> > +             0,
> > +             0,
> > +             0,
> > +             /* 1.6kHz */
> 
> Maybe a personal preference, but wouldn't it be better to have
> the comment on the same line as the value? I find this harder
> to read.

Will reformat this array to be more cleaner.

> 
> > +             625000,
> > +             /* 800Hz */
> > +             1250000,
> > +             /* 400Hz */
> > +             2500000,
> > +             /* 200Hz */
> > +             5000000,
> > +             /* 100Hz */
> > +             10000000,
> > +             /* 50Hz */
> > +             20000000,
> > +             /* 25Hz */
> > +             40000000,
> > +             /* 12.5Hz */
> > +             80000000,
> > +             /* 6.25Hz */
> > +             160000000,
> > +             /* 3.125Hz */
> > +             320000000,
> > +             /* 1.5625Hz */
> > +             640000000,
> > +     };
> > +
> > +     return odr_periods[odr];
> > +}
> > +
> > +
[snip]
> > +/**
> > + * inv_icm42370_mreg_read() - routine for reading from other bank
> > registers
> > + *
> > + * @map: regmap of the device
> > + * @bank: register bank being accessed
> > + * @addr: address of the register being accessed
> > + * @val: pointer to store the register's data
> > + *
> > + * Returns 0 on success, negative errno on error
> > + */
> > +static int inv_icm42370_mreg_read(struct regmap *map, u8 bank, u8
> > addr, u8 *val)
> > +{
> > +     int ret;
> > +     unsigned int read_val;
> > +
> > +     ret = inv_icm42370_mreg_check(map);
> > +     if (ret)
> > +             return ret;
> > +
> > +     ret = regmap_write(map, INV_ICM42370_REG_BLK_SEL_R, bank);
> > +     if (ret)
> > +             return -EINVAL;
> > +
> > +     ret = regmap_write(map, INV_ICM42370_REG_MADDR_R, addr);
> > +     if (ret)
> > +             return -EINVAL;
> > +
> > +     usleep_range(10, 20);
> 
> fsleep() will guarantee at least 10ms of sleep and it chooses
> the optimal way of achieving this.

Will convert all instances of usleep_range() to fsleep().

> 
> > +     ret = regmap_read(map, INV_ICM42370_REG_M_R, &read_val);
> > +     if (ret)
> > +             return -EINVAL;
> > +
> > +     usleep_range(10, 20);
> > +     *val = (u8)read_val;
> > +
> > +     return regmap_write(map, INV_ICM42370_REG_BLK_SEL_R, 0x00);
> > +}
> > +
> > +/**
> > + * inv_icm42370_set_conf() - set sensor configuration
> > + *
> > + * @data: pointer to struct containing the sensor data
> > + * @conf: pointer to configuration data
> > + *
> > + * Returns 0 on success, negative errno on error
> > + */
> > +static int inv_icm42370_set_conf(struct inv_icm42370_data *data,
> > +                              const struct inv_icm42370_conf
> > *conf)
> > +{
> > +     unsigned int val;
> > +     int ret;
> > +
> > +     /* set PWR_MGMT0 register (accel sensor mode, temp enabled)
> > */
> > +     val = INV_ICM42370_PWR_MGMT0(conf->mode);
> > +     ret = regmap_write(data->map, INV_ICM42370_REG_PWR_MGMT0,
> > val);
> > +     if (ret)
> > +             return ret;
> > +
> > +     msleep(200);
> > +     /* set ACCEL_CONFIG0 register (accel fullscale & odr) */
> > +     val = INV_ICM42370_ACCEL_CONFIG0_FS(conf->fs) |
> > +           INV_ICM42370_ACCEL_CONFIG0_ODR(conf->odr);
> > +     ret = regmap_write(data->map, INV_ICM42370_REG_ACCEL_CONFIG0,
> > val);
> > +     if (ret)
> > +             return ret;
> > +
> > +     msleep(200);
> > +
> > +     /* update internal conf */
> 
> Redundant comment.

Will remove this.

> 
> > +     data->conf = *conf;
> > +
> > +     return 0;
> > +}
> > +
> > +/**
> > + * inv_icm42370_set_pwr_mgmt0() - set the PWR_MGMT0 register for
> > sensor
> > + *
> > + * @dev_data: pointer to struct containing the sensor data
> > + * @accel: enum of sensor power mode
> > + * @sleep_ms: pointer to check how long the sensor is in sleep
> > mode
> > + *
> > + * Returns 0 on success, negative errno on error
> > + */
> > +static int inv_icm42370_set_pwr_mgmt0(struct inv_icm42370_data
> > *dev_data,
> > +                                   enum inv_icm42370_sensor_mode
> > accel,
> > +                                   unsigned int *sleep_ms)
> > +{
> > +     enum inv_icm42370_sensor_mode oldaccel = dev_data->conf.mode;
> > +     unsigned int sleepval;
> > +     unsigned int val;
> > +     int ret;
> > +
> > +     /* if nothing changed, exit */
> > +     if (accel == oldaccel)
> > +             return 0;
> > +
> > +     val = INV_ICM42370_PWR_MGMT0(accel);
> > +     ret = regmap_write(dev_data->map, INV_ICM42370_REG_PWR_MGMT0,
> > val);
> > +     if (ret)
> > +             return ret;
> > +
> > +     dev_data->conf.mode = accel;
> > +     dev_data->sensor_state->power_mode = accel;
> > +
> > +     /* compute required wait time for sensors to stabilize */
> > +     sleepval = 0;
> > +     /* accel startup time */
> > +     if (accel != oldaccel && oldaccel ==
> > INV_ICM42370_SENSOR_MODE_OFF) {
> > +             /* block any register write for at least 200 µs */
> > +             usleep_range(200, 300);
> 
> Use fsleep() here as well.
> 
> > +             if (sleepval < INV_ICM42370_ACCEL_STARTUP_TIME_MS)
> > +                     sleepval =
> > INV_ICM42370_ACCEL_STARTUP_TIME_MS;
> > +     }
> > +
> > +     /* deferred sleep value if sleep pointer is provided or
> > direct sleep */
> > +     if (sleep_ms)
> > +             *sleep_ms = sleepval;
> > +     else if (sleepval)
> > +             msleep(sleepval);
> > +
> > +     return 0;
> > +}
> > +
> > +static void inv_icm42370_disable_pm(void *_data)
> > +{
> > +     struct device *dev = _data;
> > +
> > +     pm_runtime_put_sync(dev);
> > +     pm_runtime_disable(dev);
> > +}
> > +
> > +/**
> > + * inv_icm42370_set_accel_conf() - set configuration data for
> > accelerometer
> > + *
> > + * @dev_data: pointer to struct containing the sensor data
> > + * @conf: pointer to configuration data
> > + * @sleep_ms: pointer to check how long the sensor is in sleep
> > mode
> > + *
> > + * Returns 0 on success, negative errno on error
> > + */
> > +int inv_icm42370_set_accel_conf(struct inv_icm42370_data
> > *dev_data,
> > +                             struct inv_icm42370_conf *conf,
> > +                             unsigned int *sleep_ms)
> > +{
> > +     struct inv_icm42370_conf *oldconf = &dev_data->conf;
> > +     unsigned int val;
> > +     int ret;
> > +
> > +     /* sanitize missing values with current values */
> > +     if (conf->mode < 0)
> > +             conf->mode = oldconf->mode;
> > +     if (conf->fs < 0)
> > +             conf->fs = oldconf->fs;
> > +     if (conf->odr < 0)
> > +             conf->odr = oldconf->odr;
> > +     if (conf->filter < 0)
> > +             conf->filter = oldconf->filter;
> > +
> > +     /* force power mode against ODR when sensor is on */
> > +     switch (conf->mode) {
> > +     case INV_ICM42370_SENSOR_MODE_LOW_POWER:
> > +     case INV_ICM42370_SENSOR_MODE_LOW_NOISE:
> > +             if (conf->odr <= INV_ICM42370_ODR_800HZ_LN) {
> > +                     conf->mode =
> > INV_ICM42370_SENSOR_MODE_LOW_NOISE;
> > +                     conf->filter =
> > +                            
> > INV_ICM42370_UI_FILT_BW_LP_FILTER_BYPASSED;
> > +             } else if (conf->odr == INV_ICM42370_ODR_400HZ) {
> > +                     if (conf->filter ==
> > INV_ICM42370_FILTER_AVG_16X ||
> > +                         conf->filter ==
> > INV_ICM42370_FILTER_AVG_32X ||
> > +                         conf->filter ==
> > INV_ICM42370_FILTER_AVG_64X) {
> > +                             conf->mode =
> > INV_ICM42370_SENSOR_MODE_LOW_NOISE;
> > +                     } else {
> > +                             conf->mode =
> > INV_ICM42370_SENSOR_MODE_LOW_POWER;
> > +                     }
> > +             } else if (conf->odr == INV_ICM42370_ODR_200HZ &&
> > +                        conf->filter ==
> > INV_ICM42370_FILTER_AVG_64X) {
> > +                     conf->mode =
> > INV_ICM42370_SENSOR_MODE_LOW_NOISE;
> > +                     conf->filter =
> > +                            
> > INV_ICM42370_UI_FILT_BW_LP_FILTER_BYPASSED;
> > +             } else if (conf->odr >= INV_ICM42370_ODR_6_25HZ_LP) {
> > +                     conf->mode =
> > INV_ICM42370_SENSOR_MODE_LOW_POWER;
> > +                     conf->filter = INV_ICM42370_FILTER_AVG_16X;
> > +             }
> > +             break;
> > +     default:
> > +             break;
> > +     }
> > +
> > +     /* set ACCEL_CONFIG0 register (accel fullscale & odr) */
> > +     if (conf->fs != oldconf->fs || conf->odr != oldconf->odr) {
> > +             val = INV_ICM42370_ACCEL_CONFIG0_FS(conf->fs) |
> > +                   INV_ICM42370_ACCEL_CONFIG0_ODR(conf->odr);
> > +             ret = regmap_write(dev_data->map,
> > +                                INV_ICM42370_REG_ACCEL_CONFIG0,
> > val);
> > +             if (ret)
> > +                     return ret;
> 
> Maybe a personal opinion as well, but readability is an issue
> sometimes.
> Adding blank lines to separate blocks of code is fine! (like here for
> example)

Will reformat to make it a bit cleaner by adding spaces.
> 
> > +             oldconf->fs = conf->fs;
> > +             oldconf->odr = conf->odr;
> > +     }
> > +
> > +     /* set PWR_MGMT0 register (accel sensor mode) */
> > +     return inv_icm42370_set_pwr_mgmt0(dev_data, dev_data-
> > >conf.mode,
> > +                                       sleep_ms);
> > +}
> > +
> > 
[snip]
> > +static irqreturn_t inv_icm42370_irq_timestamp(int irq, void
> > *_data)
> > +{
> > +     struct inv_icm42370_data *dev_data = _data;
> > +
> > +     dev_data->timestamp = iio_get_time_ns(dev_data->indio_accel);
> > +
> > +     return IRQ_WAKE_THREAD;
> > +}
> > +
> > +static irqreturn_t inv_icm42370_irq_handler(int irq, void *_data)
> > +{
> > +     struct inv_icm42370_data *dev_data = _data;
> > +     unsigned int status;
> > +     int ret;
> > +
> > +     mutex_lock(&dev_data->lock);
> 
> Use guard(mutex) from cleanup.h, it eliminates the need to use gotos
> and
> labels for cleaning up functions (a lot of examples in IIO for this).

Will convert all instances of mutex operations with a guard(mutex).

> > +
> > +     ret = regmap_read(dev_data->map, INV_ICM42370_REG_INT_STATUS,
> > &status);
> > +     if (ret)
> > +             goto out_unlock;
> > +
> > +out_unlock:
> > +     mutex_unlock(&dev_data->lock);
> > +     return IRQ_HANDLED;
> 
> Not sure, but is it okay to always return IRQ_HANDLED, even on regmap
> failure?

From some reference drivers it seems to be common, but we will
investigate this.

> 
> > +}
> > +
> > +/**
> > + * inv_icm42370_irq_init() - initialize int pin and interrupt
> > handler
> > + * @data:            driver internal state
> > + * @irq:     irq number
> > + * @irq_type:        irq trigger type
> > + * @open_drain:      true if irq is open drain, false for push-
> > pull
> > + *
> > + * Returns 0 on success, a negative error code otherwise.
> > + */
> > +static int inv_icm42370_irq_init(struct inv_icm42370_data *data,
> > int irq,
> > +                              int irq_type, bool open_drain)
> > +{
> > +     struct device *dev = regmap_get_device(data->map);
> > +     u8 val;
> > +     int ret;
> > +
> > +     /* configure INT1 interrupt: default is active low on edge */
> > +     switch (irq_type) {
> > +     case IRQF_TRIGGER_RISING:
> > +     case IRQF_TRIGGER_HIGH:
> > +             val = INV_ICM42370_INT_CONFIG_INT1_ACTIVE_HIGH;
> > +             break;
> > +     default:
> > +             val = INV_ICM42370_INT_CONFIG_INT1_ACTIVE_LOW;
> > +             break;
> > +     }
> > +
> > +     switch (irq_type) {
> > +     case IRQF_TRIGGER_LOW:
> > +     case IRQF_TRIGGER_HIGH:
> > +             val |= INV_ICM42370_INT_CONFIG_INT1_LATCHED;
> > +             break;
> > +     default:
> > +             break;
> > +     }
> > +
> > +     if (!open_drain)
> > +             val |= INV_ICM42370_INT_CONFIG_INT1_PUSH_PULL;
> > +
> > +     ret = regmap_write(data->map, INV_ICM42370_REG_INT_CONFIG,
> > val);
> > +     if (ret)
> > +             return ret;
> > +
> > +     /* Deassert async reset for proper INT pin operation (cf
> > datasheet) */
> > +     ret = inv_icm42370_mreg_read(data->map, INV_ICM42370_MREG1,
> > +                                  INV_ICM42370_REG_INT_CONFIG1,
> > &val);
> > +     if (ret)
> > +             return ret;
> 
> Blank line.

Will add here.

> 
> > +     val &= ~INV_ICM42370_INT_CONFIG1_ASYNC_RESET;
> > +
> > +     ret = inv_icm42370_mreg_write(data->map, INV_ICM42370_MREG1,
> > +                                   INV_ICM42370_REG_INT_CONFIG1,
> > val);
> > +     if (ret)
> > +             return ret;
> > +
> > +     irq_type |= IRQF_ONESHOT;
> > +     return devm_request_threaded_irq(dev, irq,
> > inv_icm42370_irq_timestamp,
> > +                                      inv_icm42370_irq_handler,
> > irq_type,
> > +                                      "inv_icm42370", data);
> > +}
> > +
> > +/*
> > + * Calibration bias values, IIO range format int + micro.
> > + * Value is limited to +/-1g coded on 12 bits signed. Step is
> > 0.5mg.
> > + */
> > +static int inv_icm42370_accel_calibbias[] = {
> > +     -10, 42010, /* min: -2^12 * 0.0005 * 9.80665 = -10.042010
> > m/s² */
> > +     0,   4903, /* step: 0.5 * 0.00980655 = 0.004903 m/s² */
> > +     10,  37106, /* max: (2^12 - 1) * 0.0005 * 9.80665 = 10.037106
> > m/s² */
> > +};
> > +
> > +/**
> > + * inv_icm42370_temp_read() - internal function to access
> > temperature sensor registers
> > + *
> > + * @dev_data: pointer to struct containing the sensor data
> > + * @temp: pointer containing the temperature data in s16 format
> > + *
> > + * Return 0 on success, negative errno on error
> > + */
> > +static int inv_icm42370_temp_read(struct inv_icm42370_data
> > *dev_data, s16 *temp)
> > +{
> > +     struct device *dev = regmap_get_device(dev_data->map);
> > +     __be16 *raw;
> > +     int ret;
> > +
> > +     pm_runtime_get_sync(dev);
> 
> Please check out PM_RUNTIME_ACQUIRE_IF_ENABLED_AUTOSUSPEND, it
> utilizes the cleanup.h
> magic as guard(mutex).
> 
> > +     mutex_lock(&dev_data->lock);
> 
> guard(mutex) combined with the macro above removes the need for gotos
> and the exit
> label.

Will convert the pm_runtime_get_sync() callbacks to use the newer
methods.

> 
> > +
> > +     raw = (__be16 *)&dev_data->buffer[0];
> > +     ret = regmap_bulk_read(dev_data->map,
> > INV_ICM42370_REG_TEMP_DATA1, raw,
> > +                            sizeof(*raw));
> > +     if (ret)
> > +             goto exit;
> > +
> > +     *temp = (s16)be16_to_cpup(raw);
> > +
> > +     /*
> > +      * Temperature data is invalid if both accel and gyro are
> > off.
> > +      * Return -EBUSY in this case.
> > +      */
> > +     if (*temp == INV_ICM42370_DATA_INVALID)
> > +             ret = -EBUSY;
> > +
> > 

[snip]

> > +
> > +     /*
> > +      * convert raw offset to g then to m/s²
> > +      * 12 bits signed raw step 0.5mg to g: 5 / 10000
> > +      * g to m/s²: 9.806650
> > +      * result in micro (1000000)
> > +      * (offset * 5 * 9.806650 * 1000000) / 10000
> > +      */
> > +     val64 = (s64)offset * 5LL * 9806650LL;
> > +     /* for rounding, add + or - divisor (10000) divided by 2 */
> > +     if (val64 >= 0)
> > +             val64 += 10000LL / 2LL;
> > +     else
> > +             val64 -= 10000LL / 2LL;
> > +     bias = div_s64(val64, 10000L);
> 
> This is probably the last time I'll mention a blank line in this
> review, but
> there are more places in this patch where they should be added.

Will do our best to add more empty lines to make it more readable.

> > +     *val = bias / 1000000L;
> > +     *val2 = bias % 1000000L;
> > +
> > +     return IIO_VAL_INT_PLUS_MICRO;
> > +}
> > +
> > +/**
> > + * inv_icm42370_accel_write_offset() - write offset values to the
> > accelerometer
> > + *
> > + * @dev_data: pointer to struct containing the sensor data
> > + * @chan: pointer to iio channel specification
> > + * @val: integer part of the value
> > + * @val2: decimal part of the value
> > + *
> > + * Returns 0 on success, negative errno on error
> > + */
> > +static int inv_icm42370_accel_write_offset(struct
> > inv_icm42370_data *dev_data,
> > +                                        struct iio_chan_spec const
> > *chan,
> > +                                        int val, int val2)
> > +{
> > +     struct device *dev = regmap_get_device(dev_data->map);
> > +     s64 val64;
> > +     s32 min, max;
> > +     unsigned int regval;
> > +     s16 offset;
> > +     int ret;
> > +
> > +     if (chan->type != IIO_ACCEL)
> > +             return -EINVAL;
> > +
> > +     /* inv_icm42370_accel_calibbias: min - step - max in micro */
> > +     min = inv_icm42370_accel_calibbias[0] * 1000000L +
> 
> This would benefit from using the macros from units.h.

Will use values from units.h

> 
> > +           inv_icm42370_accel_calibbias[1];
> > +     max = inv_icm42370_accel_calibbias[4] * 1000000L +
> > +           inv_icm42370_accel_calibbias[5];
> > +     val64 = (s64)val * 1000000LL + (s64)val2;
> > +     if (val64 < min || val64 > max)
> > +             return -EINVAL;
> > +
> > +     /*
> > +      * convert m/s² to g then to raw value
> > +      * m/s² to g: 1 / 9.806650
> > +      * g to raw 12 bits signed, step 0.5mg: 10000 / 5
> > +      * val in micro (1000000)
> > +      * val * 10000 / (9.806650 * 1000000 * 5)
> > +      */
> > +     val64 = val64 * 10000LL;
> > +     /* for rounding, add + or - divisor (9806650 * 5) divided by
> > 2 */
> > +     if (val64 >= 0)
> > +             val64 += 9806650 * 5 / 2;
> > +     else
> > +             val64 -= 9806650 * 5 / 2;
> > +     offset = div_s64(val64, 9806650 * 5);
> > +
> > +     /* clamp value limited to 12 bits signed */
> > +     if (offset < -2048)
> > +             offset = -2048;
> > +     else if (offset > 2047)
> > +             offset = 2047;
> > +
> > +     pm_runtime_get_sync(dev);
> > +     mutex_lock(&dev_data->lock);
> 
> The pm_runtime and guard_macros would clean this up nicely.

Will do here.

> 
> > +
> > +     switch (chan->channel2) {
> > +     case IIO_MOD_X:
> > +             /* OFFSET_USER4 register is shared */
> > +             ret = inv_icm42370_mreg_read(dev_data->map,
> > INV_ICM42370_MREG1,
> > +                                         
> > INV_ICM42370_REG_OFFSET_USER4,
> > +                                          (u8 *)&regval);
> > +             if (ret)
> > +                     goto out_unlock;
> > +             dev_data->buffer[0] = ((offset & 0xF00) >> 4) |
> > (regval & 0x0F);
> > +             dev_data->buffer[1] = offset & 0xFF;
> > +
> > +             ret = inv_icm42370_mreg_write(dev_data->map,
> > INV_ICM42370_MREG1,
> > +                                          
> > INV_ICM42370_REG_OFFSET_USER4,
> > +                                           dev_data->buffer[0]);
> > +             if (ret)
> > +                     goto out_unlock;
> > 

[snip]

> > +}
> > +
> > +/**
> > + * inv_icm42370_accel_read_sensor() - internal function to read
> > accelerometer sensor registers
> > + *
> > + * @indio_dev: pointer to the industrial io struct
> 
> I/O.

Will use the abbrevation.

> > + * @chan: pointer to iio channel specification
> > + * @val: pointer containing accelerometer data in s16 format
> > + *
> > + * Return 0 on success, negative errno on error
> > + */
> > +static int inv_icm42370_accel_read_sensor(struct iio_dev
> > *indio_dev,
> > +                                       struct iio_chan_spec const
> > *chan,
> > +                                       s16 *val)
> > 

[snip]

> > +      * 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_icm42370_odr_to_period(data-
> > >conf.odr);
> > +     inv_sensors_timestamp_init(&data->sensor_state->ts,
> > &ts_chip);
> > +
> > +     indio_dev->name = "inv_icm42370";
> > +     indio_dev->info = &inv_icm42370_info;
> > +     indio_dev->modes = INDIO_DIRECT_MODE;
> > +     indio_dev->channels = inv_icm42370_accel_channels;
> > +     indio_dev->num_channels =
> > ARRAY_SIZE(inv_icm42370_accel_channels);
> > +
> > +     ret = devm_iio_device_register(dev, indio_dev);
> 
> You register the device before initializing IRQs and PM runtime,
> causing potential
> race conditions as userspace could already interact with the device
> before probe()
> has finished.

Will go through the setup to fix the potential race condition.
> 
> > +     if (ret)
> > +             return ERR_PTR(ret);
> > +
> > +     return indio_dev;
> > +}
> > +
> > +/**
> > + * inv_icm42370_core_probe() - initialize and register the ICM-
> > 42370 device
> > + * @regmap:  register map for accessing the device's registers.
> > + * @chip:    chip identifier, must be %INV_CHIP_ICM42370.
> > + * @irq:     interrupt number for the device's data-ready signal.
> > + * @bus_setup:       callback to configure bus-specific settings
> > (e.g. I2C).
> > + *
> > + * Returns 0 on success, a negative error code otherwise.
> > + */
> > +int inv_icm42370_core_probe(struct regmap *regmap, int chip, int
> > irq,
> > +                         inv_icm42370_bus_setup bus_setup)
> > +{
> > +     struct device *dev = regmap_get_device(regmap);
> > +     struct inv_icm42370_data *data;
> > +     struct iio_dev *indio_dev;
> > +     struct irq_data *irq_desc;
> > +     int irq_type;
> > +     bool open_drain;
> > +     int ret;
> > +
> > +     if (chip != INV_CHIP_ICM42370) {
> > +             dev_err(dev, "invalid chip = %d\n", chip);
> > +             return -ENODEV;
> > +     }
> > +
> > +     /* get irq properties, set trigger falling by default */
> > +     irq_desc = irq_get_irq_data(irq);
> > +     if (!irq_desc) {
> > +             dev_err(dev, "could not find IRQ %d\n", irq);
> 
> dev_err_probe() would be better.

Will convert it to dev_err_probe()

> 
> > +             return -EINVAL;
> > +     }
> > +
> > +     irq_type = irqd_get_trigger_type(irq_desc);
> > +     if (!irq_type)
> > +             irq_type = IRQF_TRIGGER_FALLING;
> > +
> > +     open_drain = device_property_read_bool(dev, "drive-open-
> > drain");
> > +
> > +     indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
> > +     if (!indio_dev)
> > +             return -ENOMEM;
> > +
> > +     data = iio_priv(indio_dev);
> > +     data->sensor_state =
> > +             devm_kzalloc(dev, sizeof(*data->sensor_state),
> > GFP_KERNEL);
> > +     if (!data->sensor_state)
> > +             return -ENOMEM;
> > +
> > +     mutex_init(&data->lock);
> 
> devm_mutex_init() and check the return value.

Will convert it to devm_mutex_init() and check for the return value.

> 
> > +     data->chip = chip;
> > +     data->map = regmap;
> > +
> > +     data->vdd_supply = devm_regulator_get(dev, "vdd");
> 
> devm_regulator_get_enable will also handle the disabling on unbind.
> Same
> for data->vddio.

Will convert it to use devm_regulator_get_enable().
> 
> > +     if (IS_ERR(data->vdd_supply))
> > +             return PTR_ERR(data->vdd_supply);
> 
> dev_error_probe() here and below as well (if you want to print an
> error message)

Will add the error message with dev_error_probe()

> 
> > +
> > +     data->vddio_supply = devm_regulator_get(dev, "vddio");
> > +     if (IS_ERR(data->vddio_supply))
> > +             return PTR_ERR(data->vddio_supply);
> > +
> > +     ret = regulator_enable(data->vdd_supply);
> 
> Use devm_regulator_get_enable() as mentioned above, you wouldn't need
> this then.

Will fix this.

> > +     if (ret)
> > +             return ret;
> > +
> > +     ret = inv_icm42370_setup(data, bus_setup);
> > +     if (ret)
> > +             return dev_err_probe(dev, ret, "Setup failed\n");
> > +
> > +     data->indio_accel = inv_icm42370_accel_init(indio_dev, data);
> > +     if (IS_ERR(data->indio_accel))
> > +             return PTR_ERR(data->indio_accel);
> > +
> > +     ret = inv_icm42370_irq_init(data, irq, irq_type, open_drain);
> > +     if (ret)
> > +             return ret;
> > +
> > +     /* setup runtime power management */
> > +     ret = pm_runtime_set_active(dev);
> > +     if (ret)
> > +             return ret;
> > +
> > +     pm_runtime_get_noresume(dev);
> > +     pm_runtime_enable(dev);
> > +     pm_runtime_use_autosuspend(dev);
> > +     pm_runtime_put(dev);
> > +
> > +     return devm_add_action_or_reset(dev, inv_icm42370_disable_pm,
> > dev);
> > +}
> > +EXPORT_SYMBOL_NS_GPL(inv_icm42370_core_probe, "IIO_ICM42370");
> > +
> > +MODULE_AUTHOR("Kanak Shilledar <kanak.shilledar@axis.com>");
> > +MODULE_AUTHOR("Henrik Grimler <henrik.grimler@axis.com>");
> > +MODULE_DESCRIPTION("Invensense device ICM42370 driver");
> 
> No point in having the word "device" in the description.

Will remove it from the description.

> 
> > +MODULE_LICENSE("GPL");
> > +MODULE_IMPORT_NS("IIO_INV_SENSORS_TIMESTAMP");
> > diff --git a/drivers/iio/accel/inv_icm42370_i2c.c
> > b/drivers/iio/accel/inv_icm42370_i2c.c
> > new file mode 100644
> > index 0000000000000..57e97d66329a1
> > --- /dev/null
> > +++ b/drivers/iio/accel/inv_icm42370_i2c.c
> > @@ -0,0 +1,103 @@
> > +// SPDX-License-Identifier: GPL-2.0-or-later
> > +/*
> > + * Copyright (C) 2020 InvenSense, Inc.
> > + * Copyright (C) 2026 Axis Communications AB
> > + */
> > +
> > +#include <linux/kernel.h>
> 
> Don't include this in new drivers.
> 
> > +#include <linux/device.h>
> > +#include <linux/module.h>
> > +#include <linux/mod_devicetable.h>
> 
> Remove this.

Will remove the suggested includes and sort them alphabetically.
> 
> > +#include <linux/i2c.h>
> > +#include <linux/regmap.h>
> > +#include <linux/property.h>
> > +
> > +#include "inv_icm42370.h"
> > +
> > +/**
> > + * inv_icm42370_i2c_bus_setup() - I2C bus setup for icm42370
> > + *
> > + * @data: pointer to struct containing the sensor data
> > + *
> > + * Returns 0 on success, negative errno on error
> > + */
> > +static int inv_icm42370_i2c_bus_setup(struct inv_icm42370_data
> > *data)
> > +{
> > +     unsigned int mask, val;
> > +     int ret;
> > +
> > +     /* set slew rates for I2C */
> > +     mask = INV_ICM42370_DRIVE_CONFIG2_I2C_MASK;
> > +     val =
> > INV_ICM42370_DRIVE_CONFIG2_I2C(INV_ICM42370_SLEW_RATE_12_36NS);
> > +     ret = regmap_update_bits(data->map,
> > INV_ICM42370_REG_DRIVE_CONFIG2,
> > +                              mask, val);
> > +     if (ret)
> > +             return ret;
> > +
> > +     /* set slew rates for SPI */
> > +     mask = INV_ICM42370_DRIVE_CONFIG3_SPI_MASK;
> > +     val =
> > INV_ICM42370_DRIVE_CONFIG3_SPI(INV_ICM42370_SLEW_RATE_12_36NS);
> > +     ret = regmap_update_bits(data->map,
> > INV_ICM42370_REG_DRIVE_CONFIG3,
> > +                              mask, val);
> > +     if (ret)
> > +             return ret;
> > +
> > +     return 0;
> > +}
> > +static int inv_icm42370_probe(struct i2c_client *client)
> > +{
> > +     const void *match;
> > +     enum inv_icm42370_chip chip;
> > +     struct regmap *regmap;
> > +
> > +     if (!i2c_check_functionality(client->adapter,
> > I2C_FUNC_SMBUS_I2C_BLOCK))
> > +             return -EOPNOTSUPP;
> > +
> > +     match = device_get_match_data(&client->dev);
> > +     if (!match)
> > +             return -EINVAL;
> > +     chip = (uintptr_t)match;
> > +
> > +     regmap = devm_regmap_init_i2c(client,
> > &inv_icm42370_regmap_config);
> > +     if (IS_ERR(regmap))
> > +             return PTR_ERR(regmap);
> > +
> > +     return inv_icm42370_core_probe(regmap, chip, client->irq,
> > +                                    inv_icm42370_i2c_bus_setup);
> > +}
> > +
> > +/**
> > + * device id table is used to identify what device can be
> > supported by this driver
> > + */
> > +static const struct i2c_device_id inv_icm42370_id[] = { {
> > "icm42370",
> > +                                                      
> > INV_CHIP_ICM42370 },
> > +                                                     {} };
> 
> The formatting is awful, fix it to something like:
> static const struct i2c_device_id inv_icm42370_id[] = {
>         { .compatible = "icm42370", .data = INV_CHIP_ICM42370 },
>         { }
> };
> 
> Also the use of named initializers is preferred.

Will fix it.

> > +MODULE_DEVICE_TABLE(i2c, inv_icm42370_id);
> > +
> > +/**
> > + * inv_icm42370_of_matches - struct for all the compatibe strings
> > + *
> > + */
> > +static const struct of_device_id inv_icm42370_of_matches[] = {
> > +     {
> > +             .compatible = "invensense,icm42370",
> > +             .data = (void *)INV_CHIP_ICM42370,
> > +     },
> 
> You can put the two on one line.

Will fix it.

> 
> > +     {}
> > +};
> > +MODULE_DEVICE_TABLE(of, inv_icm42370_of_matches);
> > +
> > +static struct i2c_driver inv_icm42370_driver = {
> > +     .driver = {
> > +             .name = "inv-icm42370-i2c",
> > +             .of_match_table = inv_icm42370_of_matches,
> > +     },
> > +     .probe = inv_icm42370_probe,
> > +};
> > +module_i2c_driver(inv_icm42370_driver);
> > +
> > +MODULE_AUTHOR("Kanak Shilledar <kanak.shilledar@axis.com>");
> > +MODULE_AUTHOR("Henrik Grimler <henrik.grimler@axis.com>");
> > +MODULE_DESCRIPTION("InvenSense ICM-42370P I2C driver");
> > +MODULE_LICENSE("GPL");
> > +MODULE_IMPORT_NS("IIO_ICM42370");
> > 
> 
> 
> 
> --
> Kind regards,
> Joshua Crofts

Thanks and Regards,
Kanak Shilledar


[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

  reply	other threads:[~2026-08-07 13:41 UTC|newest]

Thread overview: 14+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-08-06 12:46 [PATCH 0/3] Add driver for Invensense ICM42370P accelerometer Kanak Shilledar
2026-08-06 12:46 ` [PATCH 1/3] dt-bindings: Add InvenSense ICM-42370-p accelerometer Kanak Shilledar
2026-08-06 12:53   ` sashiko-bot
2026-08-07 10:16   ` Joshua Crofts
2026-08-07 13:15     ` Kanak Shilledar
2026-08-06 12:46 ` [PATCH 2/3] iio: accel: Add support for ICM42370P Kanak Shilledar
2026-08-06 13:02   ` sashiko-bot
2026-08-07  6:51   ` Uwe Kleine-König
2026-08-07 13:11     ` Kanak Shilledar
2026-08-07 10:09   ` Joshua Crofts
2026-08-07 13:41     ` Kanak Shilledar [this message]
2026-08-07 14:13       ` Joshua Crofts
2026-08-06 12:46 ` [PATCH 3/3] iio: accel: icm42370: Add FIFO buffer functionality Kanak Shilledar
2026-08-06 13:03   ` sashiko-bot

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=ba866b98384dcd827d5a610de9f00386da5824e2.camel@axis.com \
    --to=kanak.shilledar@axis.com \
    --cc=Henrik.Grimler@axis.com \
    --cc=Kernel@axis.com \
    --cc=andy@kernel.org \
    --cc=conor+dt@kernel.org \
    --cc=devicetree@vger.kernel.org \
    --cc=dlechner@baylibre.com \
    --cc=jean-baptiste.maneyrol@tdk.com \
    --cc=jic23@kernel.org \
    --cc=joshua.crofts1@gmail.com \
    --cc=krzk+dt@kernel.org \
    --cc=linux-iio@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=nuno.sa@analog.com \
    --cc=robh@kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox