Re: [PATCH v5 3/4] iio: chemical: Add Senseair Sunrise 006-0-007 driver

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi Peter,

On Thu, Sep 09, 2021 at 02:04:31PM +0200, Peter Rosin wrote:
> Hi!
>
> Subject looks wrong, shouldn't it be 006-0-0007?
>
> On 2021-09-09 11:45, Jacopo Mondi wrote:
> > Add support for the Senseair Sunrise 006-0-0007 driver through the
> > IIO subsystem.
> >
> > Datasheet: https://rmtplusstoragesenseair.blob.core.windows.net/docs/Dev/publicerat/TDE5531.pdf
> > Signed-off-by: Jacopo Mondi <jacopo@xxxxxxxxxx>
> > ---
> >  MAINTAINERS                        |   7 +
> >  drivers/iio/chemical/Kconfig       |  10 +
> >  drivers/iio/chemical/Makefile      |   1 +
> >  drivers/iio/chemical/sunrise_co2.c | 526 +++++++++++++++++++++++++++++
> >  4 files changed, 544 insertions(+)
> >  create mode 100644 drivers/iio/chemical/sunrise_co2.c
> >
>
> *snip*
>
> > + * the i2c bus.
> > + */
> > +static int sunrise_read_byte(struct sunrise_dev *sunrise, u8 reg)
> > +{
> > +	const struct i2c_client *client = sunrise->client;
> > +	const struct device *dev = &client->dev;
> > +	unsigned int val;
> > +	int ret;
> > +
> > +	i2c_lock_bus(client->adapter, I2C_LOCK_SEGMENT);
> > +	ret = regmap_read(sunrise->regmap, reg, &val);
> > +	i2c_unlock_bus(client->adapter, I2C_LOCK_SEGMENT);
> > +	if (ret) {
> > +		dev_err(dev, "Read byte failed: reg 0x%2x (%d)\n", reg, ret);
>
> Nitpick, %02x looks better methinks ("reg 0x04" vs. "reg 0x 4").
>
> [more instances below]
>

Sure!

> > +		return ret;
> > +	}
> > +
> > +	return val;
> > +}
> > +
> > +static int sunrise_read_word(struct sunrise_dev *sunrise, u8 reg, u16 *val)
> > +{
> > +	const struct i2c_client *client = sunrise->client;
> > +	const struct device *dev = &client->dev;
> > +	__be16 be_val;
> > +	int ret;
> > +
> > +	i2c_lock_bus(client->adapter, I2C_LOCK_SEGMENT);
> > +	ret = regmap_bulk_read(sunrise->regmap, reg, &be_val, sizeof(be_val));
> > +	i2c_unlock_bus(client->adapter, I2C_LOCK_SEGMENT);
> > +	if (ret) {
> > +		dev_err(dev, "Read word failed: reg 0x%2x (%d)\n", reg, ret);
> > +		return ret;
> > +	}
> > +
> > +	*val = be16_to_cpu(be_val);
> > +
> > +	return 0;
> > +}
> > +
> > +static int sunrise_write_byte(struct sunrise_dev *sunrise, u8 reg, u8 val)
> > +{
> > +	const struct i2c_client *client = sunrise->client;
> > +	const struct device *dev = &client->dev;
> > +	int ret;
> > +
> > +	i2c_lock_bus(client->adapter, I2C_LOCK_SEGMENT);
> > +	ret = regmap_write(sunrise->regmap, reg, val);
> > +	i2c_unlock_bus(client->adapter, I2C_LOCK_SEGMENT);
> > +	if (ret)
> > +		dev_err(dev, "Write byte failed: reg 0x%2x (%d)\n", reg, ret);
> > +
> > +	return ret;
> > +}
> > +
> > +static int sunrise_write_word(struct sunrise_dev *sunrise, u8 reg, u16 data)
> > +{
> > +	const struct i2c_client *client = sunrise->client;
> > +	const struct device *dev = &client->dev;
> > +	__be16 be_data = cpu_to_be16(data);
> > +	int ret;
> > +
> > +	i2c_lock_bus(client->adapter, I2C_LOCK_SEGMENT);
> > +	ret = regmap_bulk_write(sunrise->regmap, reg, &be_data, sizeof(be_data));
> > +	i2c_unlock_bus(client->adapter, I2C_LOCK_SEGMENT);
> > +	if (ret)
> > +		dev_err(dev, "Write word failed: reg 0x%2x (%d)\n", reg, ret);
> > +
> > +	return ret;
> > +}
>
> *snip*
>
> > +
> > +static const struct regmap_bus sunrise_regmap_bus = {
> > +	.read = sunrise_regmap_read,
> > +	.write = sunrise_regmap_write,
> > +};
> > +
> > +static const struct regmap_config sunrise_regmap_config = {
> > +	.reg_bits = 8,
> > +	.val_bits = 8,
> > +};
> > +
> > +static int sunrise_probe(struct i2c_client *client)
> > +{
> > +	struct sunrise_dev *sunrise;
> > +	struct iio_dev *iio_dev;
> > +
> > +	iio_dev = devm_iio_device_alloc(&client->dev, sizeof(*sunrise));
> > +	if (!iio_dev)
> > +		return -ENOMEM;
> > +
> > +	sunrise = iio_priv(iio_dev);
> > +	sunrise->client = client;
>
> You should check if the I2C adapter supports the needed operations.
>
> 	if (!i2c_check_functionality(client->adapter,
> 				     I2C_FUNC_SMBUS_BLOCK_DATA |
> 				     I2C_FUNC_SMBUS_BYTE_DATA |
> 				     I2C_FUNC_SMBUS_QUICK |
> 				     I2C_FUNC_PROTOCOL_MANGLING))
> 		...
>
> And, I suspect that protocol mangling might not be the first thing simple
> SMBus-only adapters support?? Maybe you don't lose all that many adapters
> by not using the restart-after-wakeup method, when odd things are required
> anyway? That is, if the device gets enough time to wake up properly using
> that method? (see the message from Andy in the v3 discussion)

So, I've tried to apply the change you suggested in v3

static int sunrise_regmap_read(void *context, const void *reg_buf,
			       size_t reg_size, void *val_buf, size_t val_size)
{
	struct i2c_client *client = context;
	unsigned char reg =  ((char *)reg_buf)[0];
	unsigned char databuf[2];
        struct i2c_msg msg[3] = {
                {       /* wakeup */
                        .addr = client->addr,
                        .flags = I2C_M_RD | I2C_M_IGNORE_NAK,
                        .len = 0,
                }, {    /* write register number */
                        .addr = client->addr,
                        .flags = 0,
                        .len = reg_size,
                        .buf = &reg,
                }, {    /* read register contents */
                        .addr = client->addr,
                        .flags = I2C_M_RD,
                        .len = val_size,
                        .buf = databuf,
                },
        };
	int ret;

	ret = __i2c_transfer(client->adapter, msg, 3);
	if (ret != 3)
		return -EIO;

	memcpy(val_buf, databuf, val_size);

	return 0;

And I get a rather unstable system, ie communications -sometimes-
fail.

This becomes more evident when trying to calibrate, due to register
polling, but also simply reading one of the channels fails randomly.

I blame the chip wakeup time requirement between the wakeup message
and the rest. So unfortunately this version cannot replace the two
separate __i2c_smbu_xfer() calls I have now.

As the wakeup time is not characterized, I'll give it 1 msec even when
using raw __i2c_smbus_xfer() as suggested by Andy.

>
> SMBus quick isn't universal either...

This seems problematic, as I see few adapters support this (might be
the reason why there's no i2c_smbus_quick() helper ?).

I tried using I2C_SMBUS_WRITE in place of I2C_SMBUS_QUICK and the chip
seems to work properly (please not my adapater supports protocol
mangling though)

>
> But hmmm, maybe you don't *really* need protocol mangling when you don't
> check the return value of the wakeup call? At the same time, without
> I2C_M_IGNORE_NAK, you'll perhaps end up with boring entries in the log
> or the adapter doing retries or some other inconvenient crap?

Possibly, but I think it's fine as long as I don't check the error
code ?

>
> You could perhaps set I2C_M_IGNORE_NAK only if the adapter supports
> protocol mangling? Any maybe you don't care about losers who don't have
> a nice enough adapter. :-)

:) I'm on i2c-gpio, so it's all but fancy I guess..

Considering the above in v6 I will:

- Keep the two __i2c_smbu_xfer() calls
- Optional protocol mangling
- usleep_range(500, 1500) between wakup and actual message
- Use I2C_SMBUS_WRITE in place of I2C_SMBUS_QUICK for the wakeup
  message not to rule out too many adapters

Anything wrong with this in your opinion ?

Thanks
  j

>
> Cheers,
> Peter
>
> > +	mutex_init(&sunrise->lock);
> > +
> > +	sunrise->regmap = devm_regmap_init(&client->dev, &sunrise_regmap_bus,
> > +					   client, &sunrise_regmap_config);
> > +	if (IS_ERR(sunrise->regmap)) {
> > +		dev_err(&client->dev, "Failed to initialize regmap\n");
> > +		return PTR_ERR(sunrise->regmap);
> > +	}
> > +
> > +	iio_dev->info = &sunrise_info;
> > +	iio_dev->name = DRIVER_NAME;
> > +	iio_dev->channels = sunrise_channels;
> > +	iio_dev->num_channels = ARRAY_SIZE(sunrise_channels);
> > +	iio_dev->modes = INDIO_DIRECT_MODE;
> > +
> > +	return devm_iio_device_register(&client->dev, iio_dev);
> > +}
> > +
> > +static const struct of_device_id sunrise_of_match[] = {
> > +	{ .compatible = "senseair,sunrise-006-0-0007" },
> > +	{}
> > +};
> > +MODULE_DEVICE_TABLE(of, sunrise_of_match);
> > +
> > +static struct i2c_driver sunrise_driver = {
> > +	.driver = {
> > +		.name = DRIVER_NAME,
> > +		.of_match_table = sunrise_of_match,
> > +	},
> > +	.probe_new = sunrise_probe,
> > +};
> > +module_i2c_driver(sunrise_driver);
> > +
> > +MODULE_AUTHOR("Jacopo Mondi <jacopo@xxxxxxxxxx>");
> > +MODULE_DESCRIPTION("Senseair Sunrise 006-0-0007 CO2 sensor IIO driver");
> > +MODULE_LICENSE("GPL v2");
> > --
> > 2.32.0
> >



[Index of Archives]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Input]     [Linux Kernel]     [Linux SCSI]     [X.org]

  Powered by Linux