Re: [PATCH 04/12] iio: imu: inv_icm42600: add gyroscope IIO device

From: Jonathan Cameron
Date: Fri May 08 2020 - 10:01:31 EST


On Thu, 7 May 2020 16:42:14 +0200
Jean-Baptiste Maneyrol <jmaneyrol@xxxxxxxxxxxxxx> wrote:

> Add IIO device for gyroscope sensor with data polling interface.
> Attributes: raw, scale, sampling_frequency, calibbias.
>
> Gyroscope in low noise mode.
>
> Signed-off-by: Jean-Baptiste Maneyrol <jmaneyrol@xxxxxxxxxxxxxx>
Few trivial things and questions inline.

J

> ---
> drivers/iio/imu/inv_icm42600/inv_icm42600.h | 4 +
> .../iio/imu/inv_icm42600/inv_icm42600_core.c | 5 +
> .../iio/imu/inv_icm42600/inv_icm42600_gyro.c | 549 ++++++++++++++++++
> 3 files changed, 558 insertions(+)
> create mode 100644 drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c
>
> diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600.h b/drivers/iio/imu/inv_icm42600/inv_icm42600.h
> index 8da4c8249aed..ca41a9d6404a 100644
> --- a/drivers/iio/imu/inv_icm42600/inv_icm42600.h
> +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600.h
> @@ -120,6 +120,7 @@ struct inv_icm42600_suspended {
> * @orientation: sensor chip orientation relative to main hardware.
> * @conf: chip sensors configurations.
> * @suspended: suspended sensors configuration.
> + * @indio_gyro: gyroscope IIO device.
> */
> struct inv_icm42600_state {
> struct mutex lock;
> @@ -131,6 +132,7 @@ struct inv_icm42600_state {
> struct iio_mount_matrix orientation;
> struct inv_icm42600_conf conf;
> struct inv_icm42600_suspended suspended;
> + struct iio_dev *indio_gyro;
> };
>
> /* Virtual register addresses: @bank on MSB (4 upper bits), @address on LSB */
> @@ -369,4 +371,6 @@ int inv_icm42600_debugfs_reg(struct iio_dev *indio_dev, unsigned int reg,
> int inv_icm42600_core_probe(struct regmap *regmap, int chip,
> inv_icm42600_bus_setup bus_setup);
>
> +int inv_icm42600_gyro_init(struct inv_icm42600_state *st);
> +
> #endif
> diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_core.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_core.c
> index 35bdf4f9d31e..151257652ce6 100644
> --- a/drivers/iio/imu/inv_icm42600/inv_icm42600_core.c
> +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_core.c
> @@ -503,6 +503,11 @@ int inv_icm42600_core_probe(struct regmap *regmap, int chip,
> if (ret)
> return ret;
>
> + /* create and init gyroscope iio device */

'Kind' of obvious from function name? Maybe drop the comment?

> + ret = inv_icm42600_gyro_init(st);
> + if (ret)
> + return ret;
> +
> /* setup runtime power management */
> ret = pm_runtime_set_active(dev);
> if (ret)
> diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c
> new file mode 100644
> index 000000000000..74aa2b5fa611
> --- /dev/null
> +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c
> @@ -0,0 +1,549 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +/*
> + * Copyright (C) 2020 Invensense, Inc.
> + */
> +
> +#include <linux/device.h>
> +#include <linux/mutex.h>
> +#include <linux/interrupt.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/regmap.h>
> +#include <linux/delay.h>
> +#include <linux/iio/iio.h>
> +
> +#include "inv_icm42600.h"
> +
> +#define INV_ICM42600_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), \
> + .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, \
> + .shift = 0, \

Shift has the 'obviously' default of 0, so normally we don't bother explicitly
setting it to 0 like this.

> + .endianness = IIO_BE, \
> + }, \
> + .ext_info = _ext_info, \
> + }
> +
> +enum inv_icm42600_gyro_scan {
> + INV_ICM42600_GYRO_SCAN_X,
> + INV_ICM42600_GYRO_SCAN_Y,
> + INV_ICM42600_GYRO_SCAN_Z,
> +};
> +
> +static const struct iio_chan_spec_ext_info inv_icm42600_gyro_ext_infos[] = {
> + IIO_MOUNT_MATRIX(IIO_SHARED_BY_ALL, inv_icm42600_get_mount_matrix),
> + {},
> +};
> +
> +static const struct iio_chan_spec inv_icm42600_gyro_channels[] = {
> + INV_ICM42600_GYRO_CHAN(IIO_MOD_X, INV_ICM42600_GYRO_SCAN_X,
> + inv_icm42600_gyro_ext_infos),
> + INV_ICM42600_GYRO_CHAN(IIO_MOD_Y, INV_ICM42600_GYRO_SCAN_Y,
> + inv_icm42600_gyro_ext_infos),
> + INV_ICM42600_GYRO_CHAN(IIO_MOD_Z, INV_ICM42600_GYRO_SCAN_Z,
> + inv_icm42600_gyro_ext_infos),
> +};
> +
> +static int inv_icm42600_gyro_read_sensor(struct inv_icm42600_state *st,
> + struct iio_chan_spec const *chan,
> + int16_t *val)
> +{
> + struct device *dev = regmap_get_device(st->map);
> + struct inv_icm42600_sensor_conf conf = INV_ICM42600_SENSOR_CONF_INIT;
> + unsigned int reg;
> + __be16 data;
> + int ret;
> +
> + if (chan->type != IIO_ANGL_VEL)
> + return -EINVAL;
> +
> + switch (chan->channel2) {
> + case IIO_MOD_X:
> + reg = INV_ICM42600_REG_GYRO_DATA_X;
> + break;
> + case IIO_MOD_Y:
> + reg = INV_ICM42600_REG_GYRO_DATA_Y;
> + break;
> + case IIO_MOD_Z:
> + reg = INV_ICM42600_REG_GYRO_DATA_Z;
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + pm_runtime_get_sync(dev);
> + mutex_lock(&st->lock);
> +
> + /* enable gyro sensor */
> + conf.mode = INV_ICM42600_SENSOR_MODE_LOW_NOISE;
> + ret = inv_icm42600_set_gyro_conf(st, &conf, NULL);
> + if (ret)
> + goto exit;
> +
> + /* read gyro register data */
> + ret = regmap_bulk_read(st->map, reg, &data, sizeof(data));

IIRC bulk reads need to be to dma safe buffers. So typically on the stack and
in appropriately aligned location in any containing structure.

> + if (ret)
> + goto exit;
> +
> + *val = (int16_t)be16_to_cpu(data);
> + if (*val == INV_ICM42600_DATA_INVALID)
> + ret = -EINVAL;
> +exit:
> + mutex_unlock(&st->lock);
> + pm_runtime_mark_last_busy(dev);
> + pm_runtime_put_autosuspend(dev);
> + return ret;
> +}
> +
> +/* IIO format int + nano */
> +static const int inv_icm42600_gyro_scale[] = {
> + /* +/- 2000dps => 0.001065264 rad/s */
> + [2 * INV_ICM42600_GYRO_FS_2000DPS] = 0,
> + [2 * INV_ICM42600_GYRO_FS_2000DPS + 1] = 1065264,
> + /* +/- 1000dps => 0.000532632 rad/s */
> + [2 * INV_ICM42600_GYRO_FS_1000DPS] = 0,
> + [2 * INV_ICM42600_GYRO_FS_1000DPS + 1] = 532632,
> + /* +/- 500dps => 0.000266316 rad/s */
> + [2 * INV_ICM42600_GYRO_FS_500DPS] = 0,
> + [2 * INV_ICM42600_GYRO_FS_500DPS + 1] = 266316,
> + /* +/- 250dps => 0.000133158 rad/s */
> + [2 * INV_ICM42600_GYRO_FS_250DPS] = 0,
> + [2 * INV_ICM42600_GYRO_FS_250DPS + 1] = 133158,
> + /* +/- 125dps => 0.000066579 rad/s */
> + [2 * INV_ICM42600_GYRO_FS_125DPS] = 0,
> + [2 * INV_ICM42600_GYRO_FS_125DPS + 1] = 66579,
> + /* +/- 62.5dps => 0.000033290 rad/s */
> + [2 * INV_ICM42600_GYRO_FS_62_5DPS] = 0,
> + [2 * INV_ICM42600_GYRO_FS_62_5DPS + 1] = 33290,
> + /* +/- 31.25dps => 0.000016645 rad/s */
> + [2 * INV_ICM42600_GYRO_FS_31_25DPS] = 0,
> + [2 * INV_ICM42600_GYRO_FS_31_25DPS + 1] = 16645,
> + /* +/- 15.625dps => 0.000008322 rad/s */
> + [2 * INV_ICM42600_GYRO_FS_15_625DPS] = 0,
> + [2 * INV_ICM42600_GYRO_FS_15_625DPS + 1] = 8322,
> +};
> +
> +static int inv_icm42600_gyro_read_scale(struct inv_icm42600_state *st,
> + int *val, int *val2)
> +{
> + unsigned int idx;
> +
> + mutex_lock(&st->lock);
> + idx = st->conf.gyro.fs;

Seems like we shouldn't need the lock to retrieve a single value.
Is there some odd intermediate state somewhere I'm missing?

> + mutex_unlock(&st->lock);
> +
> + *val = inv_icm42600_gyro_scale[2 * idx];
> + *val2 = inv_icm42600_gyro_scale[2 * idx + 1];
> + return IIO_VAL_INT_PLUS_NANO;
> +}
> +
> +static int inv_icm42600_gyro_write_scale(struct inv_icm42600_state *st,
> + int val, int val2)
> +{
> + struct device *dev = regmap_get_device(st->map);
> + unsigned int idx;
> + struct inv_icm42600_sensor_conf conf = INV_ICM42600_SENSOR_CONF_INIT;
> + int ret;
> +
> + for (idx = 0; idx < ARRAY_SIZE(inv_icm42600_gyro_scale); idx += 2) {
> + if (val == inv_icm42600_gyro_scale[idx] &&
> + val2 == inv_icm42600_gyro_scale[idx + 1])

Alignment of code seems odd.

> + break;
> + }
> + if (idx >= ARRAY_SIZE(inv_icm42600_gyro_scale))
> + return -EINVAL;
> +
> + /* update gyro fs */
> + pm_runtime_get_sync(dev);
> +
> + mutex_lock(&st->lock);
> + conf.fs = idx / 2;
> + ret = inv_icm42600_set_gyro_conf(st, &conf, NULL);
> + mutex_unlock(&st->lock);
> +
> + pm_runtime_mark_last_busy(dev);
> + pm_runtime_put_autosuspend(dev);
> +
> + return ret;
> +}
> +
> +/* IIO format int + micro */
> +static const int inv_icm42600_gyro_odr[] = {
> + /* 12.5Hz */
> + 12, 500000,
> + /* 25Hz */
> + 25, 0,
> + /* 50Hz */
> + 50, 0,
> + /* 100Hz */
> + 100, 0,
> + /* 200Hz */
> + 200, 0,
> + /* 1kHz */
> + 1000, 0,
> + /* 2kHz */
> + 2000, 0,
> + /* 4kHz */
> + 4000, 0,
> +};
> +
> +static const int inv_icm42600_gyro_odr_conv[] = {
> + INV_ICM42600_ODR_12_5HZ,
> + INV_ICM42600_ODR_25HZ,
> + INV_ICM42600_ODR_50HZ,
> + INV_ICM42600_ODR_100HZ,
> + INV_ICM42600_ODR_200HZ,
> + INV_ICM42600_ODR_1KHZ_LN,
> + INV_ICM42600_ODR_2KHZ_LN,
> + INV_ICM42600_ODR_4KHZ_LN,
> +};
> +
> +static int inv_icm42600_gyro_read_odr(struct inv_icm42600_state *st,
> + int *val, int *val2)
> +{
> + unsigned int odr;
> + unsigned int i;
> +
> + mutex_lock(&st->lock);
> + odr = st->conf.gyro.odr;
> + mutex_unlock(&st->lock);
> +
> + for (i = 0; i < ARRAY_SIZE(inv_icm42600_gyro_odr_conv); ++i) {
> + if (inv_icm42600_gyro_odr_conv[i] == odr)
> + break;
> + }
> + if (i >= ARRAY_SIZE(inv_icm42600_gyro_odr_conv))
> + return -EINVAL;
> +
> + *val = inv_icm42600_gyro_odr[2 * i];
> + *val2 = inv_icm42600_gyro_odr[2 * i + 1];
> +
> + return IIO_VAL_INT_PLUS_MICRO;
> +}
> +
> +static int inv_icm42600_gyro_write_odr(struct inv_icm42600_state *st,
> + int val, int val2)
> +{
> + struct device *dev = regmap_get_device(st->map);
> + unsigned int idx;
> + struct inv_icm42600_sensor_conf conf = INV_ICM42600_SENSOR_CONF_INIT;
> + int ret;
> +
> + for (idx = 0; idx < ARRAY_SIZE(inv_icm42600_gyro_odr); idx += 2) {
> + if (val == inv_icm42600_gyro_odr[idx] &&
> + val2 == inv_icm42600_gyro_odr[idx + 1])
> + break;
> + }
> + if (idx >= ARRAY_SIZE(inv_icm42600_gyro_odr))
> + return -EINVAL;
> +
> + /* update gyro odr */
> + pm_runtime_get_sync(dev);
> +
> + mutex_lock(&st->lock);
> + conf.odr = inv_icm42600_gyro_odr_conv[idx / 2];
> + ret = inv_icm42600_set_gyro_conf(st, &conf, NULL);
> + mutex_unlock(&st->lock);
> +
> + pm_runtime_mark_last_busy(dev);
> + pm_runtime_put_autosuspend(dev);
> +
> + return ret;
> +}
> +
> +static int inv_icm42600_gyro_read_offset(struct inv_icm42600_state *st,
> + struct iio_chan_spec const *chan,
> + int16_t *val)
> +{
> + struct device *dev = regmap_get_device(st->map);
> + unsigned int reg;
> + uint8_t data[2];
> + int ret;
> +
> + if (chan->type != IIO_ANGL_VEL)
> + return -EINVAL;
> +
> + switch (chan->channel2) {
> + case IIO_MOD_X:
> + reg = INV_ICM42600_REG_OFFSET_USER0;
> + break;
> + case IIO_MOD_Y:
> + reg = INV_ICM42600_REG_OFFSET_USER1;
> + break;
> + case IIO_MOD_Z:
> + reg = INV_ICM42600_REG_OFFSET_USER3;
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + pm_runtime_get_sync(dev);
> +
> + /* read gyro offset data */
> + mutex_lock(&st->lock);
> + ret = regmap_bulk_read(st->map, reg, &data, sizeof(data));
> + mutex_unlock(&st->lock);
> + if (ret)
> + goto exit;
> +
> + switch (chan->channel2) {
> + case IIO_MOD_X:
> + *val = (int16_t)(((data[1] & 0x0F) << 8) | data[0]);

This doesn't look right for negative values. You would be better
off with a sign extend of the 12 bit value.

> + break;
> + case IIO_MOD_Y:
> + *val = (int16_t)(((data[0] & 0xF0) << 4) | data[1]);
> + break;
> + case IIO_MOD_Z:
> + *val = (int16_t)(((data[1] & 0x0F) << 8) | data[0]);
> + break;
> + default:
> + ret = -EINVAL;
> + break;
> + }
> +
> +exit:
> + pm_runtime_mark_last_busy(dev);
> + pm_runtime_put_autosuspend(dev);
> + return ret;
> +}
> +
> +static int inv_icm42600_gyro_write_offset(struct inv_icm42600_state *st,
> + struct iio_chan_spec const *chan,
> + int val)
> +{
> + struct device *dev = regmap_get_device(st->map);
> + unsigned int reg, regval;
> + uint8_t data[2];
> + int ret;
> +
> + if (chan->type != IIO_ANGL_VEL)
> + return -EINVAL;
> +
> + switch (chan->channel2) {
> + case IIO_MOD_X:
> + reg = INV_ICM42600_REG_OFFSET_USER0;
> + break;
> + case IIO_MOD_Y:
> + reg = INV_ICM42600_REG_OFFSET_USER1;
> + break;
> + case IIO_MOD_Z:
> + reg = INV_ICM42600_REG_OFFSET_USER3;
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + /* value is limited to 12 bits signed */
> + if (val < -2048 || val > 2047)
> + return -EINVAL;

Perhaps worth an available callback to give the range?

> +
> + pm_runtime_get_sync(dev);
> + mutex_lock(&st->lock);
> +
> + switch (chan->channel2) {
> + case IIO_MOD_X:
> + /* OFFSET_USER1 register is shared */
> + ret = regmap_read(st->map, INV_ICM42600_REG_OFFSET_USER1,
> + &regval);
> + if (ret)
> + goto out_unlock;
> + data[0] = val & 0xFF;
> + data[1] = (regval & 0xF0) | ((val & 0xF00) >> 8);
> + break;
> + case IIO_MOD_Y:
> + /* OFFSET_USER1 register is shared */
> + ret = regmap_read(st->map, INV_ICM42600_REG_OFFSET_USER1,
> + &regval);
> + if (ret)
> + goto out_unlock;
> + data[0] = ((val & 0xF00) >> 4) | (regval & 0x0F);
> + data[1] = val & 0xFF;
> + break;
> + case IIO_MOD_Z:
> + /* OFFSET_USER4 register is shared */
> + ret = regmap_read(st->map, INV_ICM42600_REG_OFFSET_USER4,
> + &regval);
> + if (ret)
> + goto out_unlock;
> + data[0] = val & 0xFF;
> + data[1] = (regval & 0xF0) | ((val & 0xF00) >> 8);
> + break;
> + default:
> + ret = -EINVAL;
> + goto out_unlock;
> + }
> +
> + ret = regmap_bulk_write(st->map, reg, data, sizeof(data));
> +
> +out_unlock:
> + mutex_unlock(&st->lock);
> + pm_runtime_mark_last_busy(dev);
> + pm_runtime_put_autosuspend(dev);
> + return ret;
> +}
> +
> +static int inv_icm42600_gyro_read_raw(struct iio_dev *indio_dev,
> + struct iio_chan_spec const *chan,
> + int *val, int *val2, long mask)
> +{
> + struct inv_icm42600_state *st = iio_device_get_drvdata(indio_dev);
> + int16_t data;
> + int ret;
> +
> + if (chan->type != IIO_ANGL_VEL)
> + return -EINVAL;
> +
> + switch (mask) {
> + case IIO_CHAN_INFO_RAW:
> + ret = iio_device_claim_direct_mode(indio_dev);
> + if (ret)
> + return ret;
> + ret = inv_icm42600_gyro_read_sensor(st, chan, &data);
> + iio_device_release_direct_mode(indio_dev);
> + if (ret)
> + return ret;
> + *val = data;
> + return IIO_VAL_INT;
> + case IIO_CHAN_INFO_SCALE:
> + return inv_icm42600_gyro_read_scale(st, val, val2);
> + case IIO_CHAN_INFO_SAMP_FREQ:
> + return inv_icm42600_gyro_read_odr(st, val, val2);
> + case IIO_CHAN_INFO_CALIBBIAS:
> + ret = iio_device_claim_direct_mode(indio_dev);
> + if (ret)
> + return ret;

I'm curious. Why can't we read back a calibration offset whilst doing
buffered capture?

> + ret = inv_icm42600_gyro_read_offset(st, chan, &data);
> + iio_device_release_direct_mode(indio_dev);
> + if (ret)
> + return ret;
> + *val = data;
> + return IIO_VAL_INT;
> + default:
> + return -EINVAL;
> + }
> +}
> +
> +static int inv_icm42600_gyro_read_avail(struct iio_dev *indio_dev,
> + struct iio_chan_spec const *chan,
> + const int **vals,
> + int *type, int *length, long mask)
> +{
> + if (chan->type != IIO_ANGL_VEL)
> + return -EINVAL;
> +
> + switch (mask) {
> + case IIO_CHAN_INFO_SCALE:
> + *vals = inv_icm42600_gyro_scale;
> + *type = IIO_VAL_INT_PLUS_NANO;
> + *length = ARRAY_SIZE(inv_icm42600_gyro_scale);
> + return IIO_AVAIL_LIST;
> + case IIO_CHAN_INFO_SAMP_FREQ:
> + *vals = inv_icm42600_gyro_odr;
> + *type = IIO_VAL_INT_PLUS_MICRO;
> + *length = ARRAY_SIZE(inv_icm42600_gyro_odr);
> + return IIO_AVAIL_LIST;
> + default:
> + return -EINVAL;
> + }
> +}
> +
> +static int inv_icm42600_gyro_write_raw(struct iio_dev *indio_dev,
> + struct iio_chan_spec const *chan,
> + int val, int val2, long mask)
> +{
> + struct inv_icm42600_state *st = iio_device_get_drvdata(indio_dev);
> + int ret;
> +
> + if (chan->type != IIO_ANGL_VEL)
> + return -EINVAL;
> +
> + switch (mask) {
> + case IIO_CHAN_INFO_SCALE:
> + ret = iio_device_claim_direct_mode(indio_dev);
> + if (ret)
> + return ret;
> + ret = inv_icm42600_gyro_write_scale(st, val, val2);
> + iio_device_release_direct_mode(indio_dev);
> + return ret;
> + case IIO_CHAN_INFO_SAMP_FREQ:
> + return inv_icm42600_gyro_write_odr(st, val, val2);
> + case IIO_CHAN_INFO_CALIBBIAS:
> + ret = iio_device_claim_direct_mode(indio_dev);
> + if (ret)
> + return ret;
> + ret = inv_icm42600_gyro_write_offset(st, chan, val);
> + iio_device_release_direct_mode(indio_dev);
> + return ret;
> + default:
> + return -EINVAL;
> + }
> +}
> +
> +static int inv_icm42600_gyro_write_raw_get_fmt(struct iio_dev *indio_dev,
> + struct iio_chan_spec const *chan,
> + long mask)
> +{
> + if (chan->type != IIO_ANGL_VEL)
> + return -EINVAL;
> +
> + switch (mask) {
> + case IIO_CHAN_INFO_SCALE:
> + return IIO_VAL_INT_PLUS_NANO;
> + case IIO_CHAN_INFO_SAMP_FREQ:
> + return IIO_VAL_INT_PLUS_MICRO;
> + case IIO_CHAN_INFO_CALIBBIAS:
> + return IIO_VAL_INT;
> + default:
> + return -EINVAL;
> + }
> +}
> +
> +static const struct iio_info inv_icm42600_gyro_info = {
> + .read_raw = inv_icm42600_gyro_read_raw,
> + .read_avail = inv_icm42600_gyro_read_avail,
> + .write_raw = inv_icm42600_gyro_write_raw,
> + .write_raw_get_fmt = inv_icm42600_gyro_write_raw_get_fmt,
> + .debugfs_reg_access = inv_icm42600_debugfs_reg,
> +};
> +
> +int inv_icm42600_gyro_init(struct inv_icm42600_state *st)
> +{
> + struct device *dev = regmap_get_device(st->map);
> + const char *name;
> + struct iio_dev *indio_dev;
> +
> + name = devm_kasprintf(dev, GFP_KERNEL, "%s-gyro", st->name);
> + if (!name)
> + return -ENOMEM;
> +
> + indio_dev = devm_iio_device_alloc(dev, 0);
> + if (!indio_dev)
> + return -ENOMEM;
> +
> + iio_device_set_drvdata(indio_dev, st);
> + indio_dev->dev.parent = dev;
> + indio_dev->name = name;
> + indio_dev->info = &inv_icm42600_gyro_info;
> + indio_dev->modes = INDIO_DIRECT_MODE;
> + indio_dev->channels = inv_icm42600_gyro_channels;
> + indio_dev->num_channels = ARRAY_SIZE(inv_icm42600_gyro_channels);
> +
> + st->indio_gyro = indio_dev;
> + return devm_iio_device_register(dev, st->indio_gyro);
> +}