Re: [PATCH v2 3/6] iio: chemical: add driver for ENS160 sensor

From: Jonathan Cameron
Date: Sun Jun 02 2024 - 07:24:41 EST


On Tue, 28 May 2024 21:14:20 -0300
Gustavo Silva <gustavograzs@xxxxxxxxx> wrote:

> ScioSense ENS160 is a digital metal oxide multi-gas sensor, designed
> for indoor air quality monitoring. The driver supports readings of
> CO2 and VOC, and can be accessed via both SPI and I2C.
>
> Datasheet: https://www.sciosense.com/wp-content/uploads/2023/12/ENS160-Datasheet.pdf
>
> Signed-off-by: Gustavo Silva <gustavograzs@xxxxxxxxx>
Hi Gustavo,

A few more comments inline.

Jonathan

> diff --git a/drivers/iio/chemical/ens160_core.c b/drivers/iio/chemical/ens160_core.c
> new file mode 100644
> index 000000000..a535f62c4
> --- /dev/null
> +++ b/drivers/iio/chemical/ens160_core.c
> @@ -0,0 +1,221 @@

...

> +static void ens160_set_idle(void *data)
> +{
> + ens160_set_mode(data, ENS160_REG_MODE_IDLE);
> +}
> +
> +static int ens160_chip_init(struct ens160_data *data)
> +{
> + struct device *dev = regmap_get_device(data->regmap);
> + unsigned int status;
> + int ret;
> +
> + ret = ens160_set_mode(data, ENS160_REG_MODE_RESET);
> + if (ret)
> + return ret;
> +
> + ret = regmap_bulk_read(data->regmap, ENS160_REG_PART_ID, &data->buf,
> + sizeof(data->buf));
> + if (ret)
> + return ret;
> +
> + if (le16_to_cpu(data->buf) != ENS160_PART_ID)
> + return -ENODEV;
> +
> + ret = ens160_set_mode(data, ENS160_REG_MODE_IDLE);
> + if (ret)
> + return ret;
> +
> + ret = regmap_write(data->regmap, ENS160_REG_COMMAND,
> + ENS160_REG_COMMAND_CLRGPR);
> + if (ret)
> + return ret;
> +
> + ret = regmap_write(data->regmap, ENS160_REG_COMMAND,
> + ENS160_REG_COMMAND_GET_APPVER);
> + if (ret)
> + return ret;
> +
> + ret = regmap_bulk_read(data->regmap, ENS160_REG_GPR_READ4,
> + data->fw_version, sizeof(data->fw_version));
> + if (ret)
> + return ret;
> +
> + dev_info(dev, "firmware version: %u.%u.%u\n", data->fw_version[2],
> + data->fw_version[1], data->fw_version[0]);
> +
> + ret = ens160_set_mode(data, ENS160_REG_MODE_STANDARD);
> + if (ret)
> + return ret;

I'd expect to see the devm code to set this to IDLE registered here
not before this function is called. If it makes sense after reset
then register it there.

> +
> + ret = regmap_read(data->regmap, ENS160_REG_DEVICE_STATUS, &status);
> + if (ret)
> + return ret;
> +
> + if (FIELD_GET(ENS160_STATUS_VALIDITY_FLAG, status)
> + != ENS160_STATUS_NORMAL)
> + return -EINVAL;
> +
> + return 0;
> +}
> +
> +static const struct iio_info ens160_info = {
> + .read_raw = ens160_read_raw,
> +};
> +
> +int devm_ens160_core_probe(struct device *dev, struct regmap *regmap,
> + const char *name)
> +{
> + struct ens160_data *data;
> + struct iio_dev *indio_dev;
> + int ret;
> +
> + indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
> + if (!indio_dev)
> + return -ENOMEM;
> +
> + data = iio_priv(indio_dev);
> + data->regmap = regmap;
> +
> + indio_dev->name = name;
> + indio_dev->info = &ens160_info;
> + indio_dev->modes = INDIO_DIRECT_MODE;
> + indio_dev->channels = ens160_channels;
> + indio_dev->num_channels = ARRAY_SIZE(ens160_channels);
> +
> + ret = devm_add_action_or_reset(dev, ens160_set_idle, data);
What is this 'undoing'? My guess is this belongs after chip_init.
Note that the expectation is that functions that return error codes
should not have side effects, so you may need to clean up manually
in ens160_chip_init() or move this devm call in there so it's immediately
after whatever it undoing.

> + if (ret)
> + return ret;
> +
> + ret = ens160_chip_init(data);
> + if (ret)
> + return dev_err_probe(dev, ret, "chip initialization failed\n");
> +
> + return devm_iio_device_register(dev, indio_dev);
> +}
> +EXPORT_SYMBOL_NS(devm_ens160_core_probe, IIO_ENS160);
> +
> +MODULE_AUTHOR("Gustavo Silva <gustavograzs@xxxxxxxxx>");
> +MODULE_DESCRIPTION("ScioSense ENS160 driver");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/iio/chemical/ens160_i2c.c b/drivers/iio/chemical/ens160_i2c.c
> new file mode 100644
> index 000000000..2f0b08e52
> --- /dev/null
> +++ b/drivers/iio/chemical/ens160_i2c.c
> @@ -0,0 +1,60 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * ScioSense ENS160 multi-gas sensor I2C driver
> + *
> + * Copyright (c) 2024 Gustavo Silva <gustavograzs@xxxxxxxxx>
> + *
> + * 7-Bit I2C slave address is:
> + * - 0x52 if ADDR pin LOW
> + * - 0x53 if ADDR pin HIGH
> + */
> +
> +#include <linux/i2c.h>
> +#include <linux/module.h>
> +#include <linux/regmap.h>
> +
> +#include "ens160.h"
> +
> +static const struct regmap_config ens160_regmap_i2c_conf = {
> + .reg_bits = 8,
> + .val_bits = 8,
> +};
> +
> +static int ens160_i2c_probe(struct i2c_client *client)
> +{
> + struct regmap *regmap;
> +
> + regmap = devm_regmap_init_i2c(client, &ens160_regmap_i2c_conf);
> + if (IS_ERR(regmap))
> + return dev_err_probe(&client->dev, PTR_ERR(regmap),
> + "Failed to register i2c regmap\n");
> +
> + return devm_ens160_core_probe(&client->dev, regmap, "ens160_i2c");

The user tends not to care if it's spi or i2c + it's easy to tell anyway
by looking at the parent of the iio device. + the ABI is part number, not
part number with a bus prefix so some standard tools may run into problems
with this form.

So drop that _i2c and the _spi one.