Re: [PATCH v3 2/2] iio: light: as73211: New driver

From: Andy Shevchenko
Date: Thu Jul 30 2020 - 07:31:53 EST


On Thu, Jul 30, 2020 at 1:52 PM Christian Eggers <ceggers@xxxxxxx> wrote:
> Support for AMS AS73211 JENCOLOR(R) Digital XYZ Sensor.
>
> This driver has no built-in trigger. In order for making triggered
> measurements, an external (software) trigger driver like
> iio-trig-hrtimer or iio-trig-sysfs is required.
>
> The sensor supports single and continuous measurement modes. The latter
> is not used by design as this would require tight timing synchronization
> between hardware and driver without much benefit.

...

> +#include <linux/iio/iio.h>
> +#include <linux/iio/sysfs.h>
> +#include <linux/iio/trigger_consumer.h>
> +#include <linux/iio/buffer.h>
> +#include <linux/iio/triggered_buffer.h>

Ordered?

...

> +/**
> + * struct as73211_data - Instance data for one AS73211
> + * @client: I2C client.
> + * @osr: Cached Operational State Register.
> + * @creg1: Cached Configuration Register 1.
> + * @creg2: Cached Configuration Register 2.
> + * @creg3: Cached Configuration Register 3.
> + * @buffer: Buffer for triggered measurements.
> + * @mutex: Keeps cached registers in synch with the device.
> + * @completion: Completion to wait for interrupt.
> + */
> +struct as73211_data {
> + struct i2c_client *client;
> + u8 osr;
> + u8 creg1;
> + u8 creg2;
> + u8 creg3;
> + struct mutex mutex;
> + struct completion completion;
> +};

Actually Jonathan is correct -- the above has kernel doc issues.

...

> +#define AS73211_SCAN_MASK_COLOR \
> + (BIT(AS73211_SCAN_INDEX_X) | BIT(AS73211_SCAN_INDEX_Y) | BIT(AS73211_SCAN_INDEX_Z))

> +#define AS73211_SCAN_MASK_ALL (BIT(AS73211_SCAN_INDEX_TEMP) | AS73211_SCAN_MASK_COLOR)

Perhaps split for easy reading.

...

> +static unsigned int as73211_integration_time_us(struct as73211_data *data)
> +{
> + /* Integration time has 15 steps, the step size depends on the clock. */
> + unsigned int mul = BIT(3 - (data->creg3 & GENMASK(1, 0)));

Shouldn't be rather

#define FOO GENMASK(1, 0)
mul = BIT(FOO - (creg & FOO));

with a descriptive name of FOO?

Or if both 3:s have different meaning, two definitions?

> + return as73211_integration_time_cycles(data) * 125 * mul;
> +}

...

> +static unsigned int as73211_gain(struct as73211_data *data)
> +{

> + return BIT(0xb - FIELD_GET(AS73211_CREG1_GAIN_MASK, data->creg1));

Magic!

> +}

...

> + struct device *dev = &data->client->dev;
> + unsigned int time_us = as73211_integration_time_us(data);
> + int ret;
> + union i2c_smbus_data smbus_data;
> + u16 osr_status;

Can you keep this in reversed tree order?

unsigned int time_us = as73211_integration_time_us(data);
struct device *dev = &data->client->dev;
union i2c_smbus_data smbus_data;
u16 osr_status;
int ret;

Same comment to the rest where applicable.

...

> + /* Add some extra margin for the timeout. sensor timing is not as precise
> + * as our one ...
> + */

Comment style. Same to the rest of the code where applicable.

...

> + if (data->client->irq) {
> + dev_dbg(dev, "Waiting for completion...\n");
> + ret = wait_for_completion_timeout(&data->completion,

> + 2 + usecs_to_jiffies(time_us));

Magic!

> + if (!ret) {
> + dev_err(dev, "timeout waiting for READY IRQ\n");
> + i2c_unlock_bus(data->client->adapter, I2C_LOCK_SEGMENT);
> + return -ETIMEDOUT;
> + }
> + } else {
> + /* Wait integration time */

> + dev_dbg(dev, "Sleeping %d us\n", time_us);

Looks like a noise (good for development, bad for upstream)

> + usleep_range(time_us, time_us + 100000);

If time_us appears to be, let's say, 100. The above margin is way too high.

> + }

...

> + osr_status = (u16)ret;

Why casting?

...

> + dev_dbg(dev, "osr_status = 0x%04x\n", osr_status);

Looks like a noise (good for development, bad for upstream)

...

> + if (osr_status & AS73211_OSR_SS) {
> + dev_warn(dev, "%s() Measurement has not stopped\n", __func__);
> + return -ETIME;
> + }
> + if (osr_status & AS73211_OSR_STATUS_NOTREADY) {
> + dev_warn(dev, "%s() Data is not ready\n", __func__);
> + return -ENODATA;
> + }
> + if (!(osr_status & AS73211_OSR_STATUS_NDATA)) {
> + dev_warn(dev, "%s() New new data available\n", __func__);
> + return -ENODATA;
> + }
> + if (osr_status & AS73211_OSR_STATUS_LDATA) {
> + dev_warn(dev, "%s() Result buffer overrun\n", __func__);
> + return -ENOBUFS;
> + }
> + if (osr_status & AS73211_OSR_STATUS_ADCOF) {
> + dev_warn(dev, "%s() ADC overflow\n", __func__);
> + return -EOVERFLOW;
> + }
> + if (osr_status & AS73211_OSR_STATUS_MRESOF) {
> + dev_warn(dev, "%s() Measurement result overflow\n", __func__);
> + return -EOVERFLOW;
> + }
> + if (osr_status & AS73211_OSR_STATUS_OUTCONVOF) {
> + dev_warn(dev, "%s() Timer overflow\n", __func__);
> + return -EOVERFLOW;
> + }
> + dev_warn(dev, "%s() Unexpected status value\n", __func__);
> + return -EIO;
> + }

All above dev_warn() are wrong. Should be dev_err(). Otherwise all
return -ERRNO are wrong.

...

> + int ret = iio_device_claim_direct_mode(indio_dev);
> +
> + if (ret)
> + return ret;

Please, use better arrangement, i.e.

int ret;

ret = ...
if (ret)
...

+ blank line.

> + ret = as73211_req_data(data);
> + if (ret < 0) {
> + iio_device_release_direct_mode(indio_dev);
> + return ret;
> + }

+ blank line

> + ret = i2c_smbus_read_word_data(data->client, chan->address);
> + iio_device_release_direct_mode(indio_dev);
> + if (ret < 0)
> + return ret;

+ blank line

> + *val = ret;
> + return IIO_VAL_INT;
> + }

...

> + case IIO_CHAN_INFO_SCALE:
> + {

On one line.

...

> + *val = BIT(data->creg3 & GENMASK(1, 0)) * 1024 * 1000;

1000 is magic and 1024 either.

1000 is frequency multiplier? If it's not defined do it like
#define HZ_PER_KHZ
#define KHZ_PER_MHZ
or what is that?

...

> + *val = time_us / 1000000;
> + *val2 = time_us % 1000000;

Ditto.

NSEC_PER_SEC or what?

...

> + int reg_bits, freq_kHz = val / 1000; /* 1024, 2048, ... */

HZ_PER_KHZ?

...

> + if (val < 0 || (freq_kHz * 1000) != val || val2)

Ditto.

...

> + return i2c_smbus_write_byte_data(data->client, AS73211_REG_CREG3, data->creg3);

Can it suddenly return positive number?

...

> + int time_us = val * 1000000 + val2, time_ms;

NSEC_PER_MSEC ?
USEC_PER_SEC ?

...

> + time_ms = (time_us * mul) / 1000; /* 1 ms, 2 ms, ... (power of two) */

NSEC_PER_MSEC

...

> + if (time_ms < 0 || !is_power_of_2(time_ms) || time_ms > 16384)

Magic! Shouldn't be rather BIT(14) and defined somewhere?

...


> + /* Need to switch to config mode ... */

Not sure how this comment related to locking.

> + mutex_lock(&data->mutex);

...

> + dev_dbg(&indio_dev->dev, "Switching to configuration state...");

Noise.

...

> +static irqreturn_t as73211_ready_handler(int irq __always_unused, void *priv)
> +{

> + struct iio_dev *indio_dev = priv;

Unneeded churn.

> + struct as73211_data *data = iio_priv(indio_dev);

iio_priv(priv)

> + complete(&data->completion);
> +
> + return IRQ_HANDLED;
> +}

...

> + struct iio_dev *indio_dev = pf->indio_dev;

Ditto. Do you use indio_dev below (except one case)? I haven't noticed.

> + struct as73211_data *data = iio_priv(indio_dev);

...

> + struct i2c_msg msgs[] = {
> + {
> + .addr = data->client->addr,
> + .flags = 0,
> + .len = 1,
> + .buf = &addr

+ comma

> + },
> + {
> + .addr = data->client->addr,
> + .flags = I2C_M_RD,
> + .len = sizeof(scan.chan),
> + .buf = (u8 *)&scan.chan

+ comma

> + },
> + };

...

> + }
> + /* Optimization for reading only color channels */
> + else {

Should be one line. Had you run checkpatch?

...

> +static ssize_t as73211_show_samp_freq_available(struct device *dev __always_unused,
> + struct device_attribute *attr __always_unused,
> + char *buf)
> +{
> +}
> +
> +static ssize_t as73211_show_int_time_available(struct device *dev,
> + struct device_attribute *attr __always_unused,
> + char *buf)
> +{
> +}
> +
> +static ssize_t as73211_show_hardwaregain_available(struct device *dev __always_unused,
> + struct device_attribute *attr __always_unused,
> + char *buf)
> +{
> +}

I guess above is an overkill. See example here

commit 6085102c494b8afc62bc093f8f32d6248f6d33e5
Author: Andy Shevchenko <andriy.shevchenko@xxxxxxxxxxxxxxx>
Date: Mon Mar 23 12:41:26 2020 +0200

iio: pressure: bmp280: Convert to use ->read_avail()

...

> + ret = i2c_smbus_read_byte_data(data->client, AS73211_REG_OSR);
> + if (ret < 0)
> + return ret;

Yeah, be consistent. Either all such call should be checked against
negative error code, or drop ' < 0' everywhere it applies.

...

> + /* At the time of writing this driver, only DEVID 2 and MUT 1 is known. */
> + if ((ret & AS73211_AGEN_DEVID_MASK) != AS73211_AGEN_DEVID(2)
> + || (ret & AS73211_AGEN_MUT_MASK) != AS73211_AGEN_MUT(1))

Looks like broken indentation, and leave || on previous line.

> + return -ENODEV;

...

> + if (client->irq) {
> + ret = request_threaded_irq(client->irq,
> + NULL,
> + as73211_ready_handler,

> + IRQF_TRIGGER_RISING | IRQF_ONESHOT,

Should not be IRQ trigger type here. (Do you have broken firmware table?)

> + client->name, indio_dev);
> + if (ret)
> + goto buffer_cleanup;
> + }

--
With Best Regards,
Andy Shevchenko