Re: [PATCH v2] iio: humidity: hdc3020: add threshold events support

From: Dimitri Fedrau
Date: Mon Feb 05 2024 - 02:04:43 EST


Am Sun, Feb 04, 2024 at 02:43:47PM +0000 schrieb Jonathan Cameron:
> On Sun, 4 Feb 2024 11:37:10 +0100
> Dimitri Fedrau <dima.fedrau@xxxxxxxxx> wrote:
>
> > Add threshold events support for temperature and relative humidity. To
> > enable them the higher and lower threshold registers must be programmed
> > and the higher threshold must be greater then or equal to the lower
> > threshold. Otherwise the event is disabled. Invalid hysteresis values
> > are ignored by the device. There is no further configuration possible.
> >
> > Tested by setting thresholds/hysteresis and turning the heater on/off.
> > Used iio_event_monitor in tools/iio to catch events while constantly
> > displaying temperature and humidity values.
> > Threshold and hysteresis values are cached in the driver, used i2c-tools
> > to read the threshold and hysteresis values from the device and make
> > sure cached values are consistent to values written to the device.
> >
> > Based on Fix:
> > a69eeaad093d "iio: humidity: hdc3020: fix temperature offset" in branch
> > fixes-togreg
> Move this below the ---
> as we don't want to keep that info in the log long term.
>
Ok.

> It's good to have it for now though as helps me know when I can apply the patch.
>
> >
> > Signed-off-by: Dimitri Fedrau <dima.fedrau@xxxxxxxxx>
> > ---
> > Changes in V2:
> > - Fix alphabetical order of includes(Christophe)
> > - Fix typo: change varibale name "HDC3020_R_R_RH_THRESH_LOW_CLR" to
> > HDC3020_R_T_RH_THRESH_LOW_CLR to match variable name pattern
> > (Christophe)
> > - Add constants HDC3020_MIN_TEMP and HDC3020_MAX_TEMP for min/max
> > threshold inputs (Christophe)
> > - Change HDC3020_MIN_TEMP to -40, as stated in the datasheet(Javier)
> > - Fix shadowing of global variable "hdc3020_id" in probe function,
> > rename variable in probe function to "dev_id"(Javier)
> > ---
> > drivers/iio/humidity/hdc3020.c | 342 +++++++++++++++++++++++++++++++++
> > 1 file changed, 342 insertions(+)
> >
> > diff --git a/drivers/iio/humidity/hdc3020.c b/drivers/iio/humidity/hdc3020.c
> > index ed70415512f6..80cfb343c92d 100644
> > --- a/drivers/iio/humidity/hdc3020.c
> > +++ b/drivers/iio/humidity/hdc3020.c
> > @@ -14,11 +14,13 @@
> > #include <linux/delay.h>
> > #include <linux/i2c.h>
> > #include <linux/init.h>
> > +#include <linux/interrupt.h>
> > #include <linux/module.h>
> > #include <linux/mutex.h>
> >
> > #include <asm/unaligned.h>
> >
> > +#include <linux/iio/events.h>
> > #include <linux/iio/iio.h>
> >
> > #define HDC3020_HEATER_CMD_MSB 0x30 /* shared by all heater commands */
> > @@ -26,21 +28,47 @@
> > #define HDC3020_HEATER_DISABLE 0x66
> > #define HDC3020_HEATER_CONFIG 0x6E
> >
> > +#define HDC3020_THRESH_TEMP_MASK GENMASK(8, 0)
> > +#define HDC3020_THRESH_TEMP_SHIFT 7
>
> This is odd. Normally a mask and shift pair like this is about
> a register field. Here that's not true. So don't use the common
> naming and instead use something like TRUNCATED_BITS.
> Define that for both fields, then use
> FIELD_PREP() to set them, even though that will meant shifting
> the humidity values down and up again by the same amount.
>
Could have done better with the naming. Will fix this.

>
>
> > +#define HDC3020_THRESH_HUM_MASK GENMASK(15, 9)
> > +
> > +#define HDC3020_STATUS_T_LOW_ALERT BIT(6)
> > +#define HDC3020_STATUS_T_HIGH_ALERT BIT(7)
> > +#define HDC3020_STATUS_RH_LOW_ALERT BIT(8)
> > +#define HDC3020_STATUS_RH_HIGH_ALERT BIT(9)
> > +
> > #define HDC3020_READ_RETRY_TIMES 10
> > #define HDC3020_BUSY_DELAY_MS 10
> >
> > #define HDC3020_CRC8_POLYNOMIAL 0x31
> >
> > +#define HDC3020_MIN_TEMP -40
> > +#define HDC3020_MAX_TEMP 125
> > +
> > static const u8 HDC3020_S_AUTO_10HZ_MOD0[2] = { 0x27, 0x37 };
> >
> > +static const u8 HDC3020_S_STATUS[2] = { 0x30, 0x41 };
> > +
> > static const u8 HDC3020_EXIT_AUTO[2] = { 0x30, 0x93 };
> >
> > +static const u8 HDC3020_S_T_RH_THRESH_LOW[2] = { 0x61, 0x00 };
>
> Ah. missed this in original driver, but this use of capitals for
> non #defines is really confusing and we should aim to clean that
> up.
>
Could use small letters instead.

> As I mention below, I'm unconvinced that it makes sense to handle
> these as pairs.
>
For the threshold I could convert it as it is for the heater registers:

#define HDC3020_S_T_RH_THRESH_MSB 0x61
#define HDC3020_S_T_RH_THRESH_LOW 0x00
#define HDC3020_S_T_RH_THRESH_LOW_CLR 0x0B
#define HDC3020_S_T_RH_THRESH_HIGH_CLR 0x16
#define HDC3020_S_T_RH_THRESH_HIGH 0x1D

#define HDC3020_R_T_RH_THRESH_MSB 0xE1
#define HDC3020_R_T_RH_THRESH_LOW 0x02
#define HDC3020_R_T_RH_THRESH_LOW_CLR 0x09
#define HDC3020_R_T_RH_THRESH_HIGH_CLR 0x14
#define HDC3020_R_T_RH_THRESH_HIGH 0x1F

or:

#define HDC3020_S_T_RH_THRESH_LOW 0x6100
#define HDC3020_S_T_RH_THRESH_LOW_CLR 0x610B
#define HDC3020_S_T_RH_THRESH_HIGH_CLR 0x6116
#define HDC3020_S_T_RH_THRESH_HIGH 0x611D

#define HDC3020_R_T_RH_THRESH_LOW 0x6102
#define HDC3020_R_T_RH_THRESH_LOW_CLR 0x6109
#define HDC3020_R_T_RH_THRESH_HIGH_CLR 0x6114
#define HDC3020_R_T_RH_THRESH_HIGH 0x611F

I don't know if it's a good idea, as we would need to make sure it is
big endian in the buffer. Probably with a function that handles this.
>
> > +static const u8 HDC3020_S_T_RH_THRESH_LOW_CLR[2] = { 0x61, 0x0B };
> > +static const u8 HDC3020_S_T_RH_THRESH_HIGH_CLR[2] = { 0x61, 0x16 };
> > +static const u8 HDC3020_S_T_RH_THRESH_HIGH[2] = { 0x61, 0x1D };
> > +
> > static const u8 HDC3020_R_T_RH_AUTO[2] = { 0xE0, 0x00 };
> > static const u8 HDC3020_R_T_LOW_AUTO[2] = { 0xE0, 0x02 };
> > static const u8 HDC3020_R_T_HIGH_AUTO[2] = { 0xE0, 0x03 };
> > static const u8 HDC3020_R_RH_LOW_AUTO[2] = { 0xE0, 0x04 };
> > static const u8 HDC3020_R_RH_HIGH_AUTO[2] = { 0xE0, 0x05 };
> >
> > +static const u8 HDC3020_R_T_RH_THRESH_LOW[2] = { 0xE1, 0x02 };
> > +static const u8 HDC3020_R_T_RH_THRESH_LOW_CLR[2] = { 0xE1, 0x09 };
> > +static const u8 HDC3020_R_T_RH_THRESH_HIGH_CLR[2] = { 0xE1, 0x14 };
> > +static const u8 HDC3020_R_T_RH_THRESH_HIGH[2] = { 0xE1, 0x1F };
> > +
> > +static const u8 HDC3020_R_STATUS[2] = { 0xF3, 0x2D };
> > +
> > struct hdc3020_data {
> > struct i2c_client *client;
> > /*
> > @@ -50,22 +78,54 @@ struct hdc3020_data {
> > * if the device does not respond).
> > */
> > struct mutex lock;
> > + /*
> > + * Temperature and humidity thresholds are packed together into a single
> > + * 16 bit value. Each threshold is represented by a truncated 16 bit
> > + * value. The 7 MSBs of a relative humidity threshold are concatenated
> > + * with the 9 MSBs of a temperature threshold, where the temperature
> > + * threshold resides in the 7 LSBs. Due to the truncated representation,
> > + * there is a resolution loss of 0.5 degree celsius in temperature and a
> > + * 1% resolution loss in relative humidity.
> > + */
> > + u16 t_rh_thresh_low;
> > + u16 t_rh_thresh_high;
> > + u16 t_rh_thresh_low_clr;
> > + u16 t_rh_thresh_high_clr;
> > };
> >
> > static const int hdc3020_heater_vals[] = {0, 1, 0x3FFF};
> >
> > +static const struct iio_event_spec hdc3020_t_rh_event[] = {
> > + {
> > + .type = IIO_EV_TYPE_THRESH,
> > + .dir = IIO_EV_DIR_RISING,
> > + .mask_separate = BIT(IIO_EV_INFO_VALUE) |
> > + BIT(IIO_EV_INFO_HYSTERESIS),
> > + },
> > + {
> > + .type = IIO_EV_TYPE_THRESH,
> > + .dir = IIO_EV_DIR_FALLING,
> > + .mask_separate = BIT(IIO_EV_INFO_VALUE) |
> > + BIT(IIO_EV_INFO_HYSTERESIS),
> > + },
> > +};
> > +
> > static const struct iio_chan_spec hdc3020_channels[] = {
> > {
> > .type = IIO_TEMP,
> > .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
> > BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_PEAK) |
> > BIT(IIO_CHAN_INFO_TROUGH) | BIT(IIO_CHAN_INFO_OFFSET),
> > + .event_spec = hdc3020_t_rh_event,
> > + .num_event_specs = ARRAY_SIZE(hdc3020_t_rh_event),
> > },
> > {
> > .type = IIO_HUMIDITYRELATIVE,
> > .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
> > BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_PEAK) |
> > BIT(IIO_CHAN_INFO_TROUGH),
> > + .event_spec = hdc3020_t_rh_event,
> > + .num_event_specs = ARRAY_SIZE(hdc3020_t_rh_event),
> > },
> > {
> > /*
> > @@ -389,10 +449,241 @@ static int hdc3020_write_raw(struct iio_dev *indio_dev,
> > return -EINVAL;
> > }
> >
> > +static int hdc3020_write_thresh(struct iio_dev *indio_dev,
> > + const struct iio_chan_spec *chan,
> > + enum iio_event_type type,
> > + enum iio_event_direction dir,
> > + enum iio_event_info info,
> > + int val, int val2)
> > +{
> > + struct hdc3020_data *data = iio_priv(indio_dev);
> > + u16 *thresh;
> > + u8 buf[5];
> > + int ret;
> > +
> > + /* Supported temperature range is from –40 to 125 degree celsius */
> > + if (val < HDC3020_MIN_TEMP || val > HDC3020_MAX_TEMP)
> > + return -EINVAL;
> > +
> > + /* Select threshold and associated register */
> > + if (info == IIO_EV_INFO_VALUE) {
> > + if (dir == IIO_EV_DIR_RISING) {
> > + thresh = &data->t_rh_thresh_high;
> > + memcpy(buf, HDC3020_S_T_RH_THRESH_HIGH, 2);
> > + } else {
> > + thresh = &data->t_rh_thresh_low;
> > + memcpy(buf, HDC3020_S_T_RH_THRESH_LOW, 2);
> First value of buf is always 0x61 - so just set that above
> u8 buf[5] = { 0x61, };
> and here just write buf[1] with appropriate value.
>
Will fix it.
> > + }
> > + } else {
> > + if (dir == IIO_EV_DIR_RISING) {
> > + thresh = &data->t_rh_thresh_high_clr;
> > + memcpy(buf, HDC3020_S_T_RH_THRESH_HIGH_CLR, 2);
> > + } else {
> > + thresh = &data->t_rh_thresh_low_clr;
> > + memcpy(buf, HDC3020_S_T_RH_THRESH_LOW_CLR, 2);
> > + }
> > + }
> > +
> > + guard(mutex)(&data->lock);
> > + switch (chan->type) {
> > + case IIO_TEMP:
> > + /*
> > + * Store truncated temperature threshold into 9 LSBs while
> > + * keeping the old humidity threshold in the 7 MSBs.
> > + */
> > + val = (((val + 45) * 65535 / 175) >> HDC3020_THRESH_TEMP_SHIFT);
>
> This almost looks like FIELD_PREP() but is really a division then a store?
> Perhaps rename TEMP_SHIFT TEMP_TRUNCATED_BITS or something like that to avoid
> the currently confusing naming.
>
The comment is misleading. Calculation of the temperature threshold goes first
and then the value is truncated. Will fix this.

> > + val &= HDC3020_THRESH_TEMP_MASK;
> > + val |= (*thresh & HDC3020_THRESH_HUM_MASK);
> > + break;
> > + case IIO_HUMIDITYRELATIVE:
> > + /*
> > + * Store truncated humidity threshold into 7 MSBs while
> > + * keeping the old temperature threshold in the 9 LSBs.
> > + */
> > + val = ((val * 65535 / 100) & HDC3020_THRESH_HUM_MASK);
> > + val |= (*thresh & HDC3020_THRESH_TEMP_MASK);
> > + break;
> > + default:
> > + return -EOPNOTSUPP;
> > + }
> > +
> > + put_unaligned_be16(val, &buf[2]);
> > + buf[4] = crc8(hdc3020_crc8_table, buf + 2, 2, CRC8_INIT_VALUE);
> > + ret = hdc3020_write_bytes(data, buf, 5);
> > + if (ret)
> > + return ret;
> > +
> > + /* Update threshold */
> > + *thresh = val;
> > +
> > + return 0;
> > +}
> > +
> > +static int _hdc3020_read_thresh(struct hdc3020_data *data,
>
> Relationship of this function to the following one not clear from
> naming. Rename it to make the two different cases easier to follow.
> Mind you, threshold checking isn't usually a fast path - so
> it's unusual to cache the thresholds in the driver explicitly
> (as opposed to getting them cached for free via regmap which doesn't
> add driver complexity).
>
It is left from a previous version which I didn't submit. Will fix the
naming if I need the function in the next version. I probably remove the
caching, as you already explained it adds complexity and isn't in the
fast path.

>
> > + enum iio_event_info info,
> > + enum iio_event_direction dir, u16 *thresh)
> > +{
> > + u8 crc, buf[3];
> > + const u8 *cmd;
> > + int ret;
> > +
> > + if (info == IIO_EV_INFO_VALUE) {
> > + if (dir == IIO_EV_DIR_RISING)
>
> As you only use these in the initial readback, maybe just pass the
> cmd directly into each call. No need to use general interfaces.
>
Ok.
> > + cmd = HDC3020_R_T_RH_THRESH_HIGH;
> > + else
> > + cmd = HDC3020_R_T_RH_THRESH_LOW;
> > + } else {
> > + if (dir == IIO_EV_DIR_RISING)
> > + cmd = HDC3020_R_T_RH_THRESH_HIGH_CLR;
> > + else
> > + cmd = HDC3020_R_T_RH_THRESH_LOW_CLR;
> > + }
> > +
> > + ret = hdc3020_read_bytes(data, cmd, buf, 3);
> > + if (ret < 0)
> > + return ret;
> > +
> > + /* CRC check of the threshold */
> > + crc = crc8(hdc3020_crc8_table, buf, 2, CRC8_INIT_VALUE);
> > + if (crc != buf[2])
> > + return -EINVAL;
>
> > +
> > + *thresh = get_unaligned_be16(buf);
>
> This 3 byte read, crc check and be16 extraction is common in this diver
> maybe - just add a helper function for this rather than adding
> yet another copy of the same code?
>
> int hdc3020_read_be16_reg(struct iio_dev *indio_dev, u8 cmd)
> {...
>
> return get_unaligned_be16(buf);
>
You are right. Will also fix this for the existing code.

> > +
> > + return 0;
> > +}
> > +
> > +static int hdc3020_read_thresh(struct iio_dev *indio_dev,
> > + const struct iio_chan_spec *chan,
> > + enum iio_event_type type,
> > + enum iio_event_direction dir,
> > + enum iio_event_info info,
> > + int *val, int *val2)
> > +{
> > + struct hdc3020_data *data = iio_priv(indio_dev);
> > + u16 *thresh;
> > +
> > + /* Select threshold */
> > + if (info == IIO_EV_INFO_VALUE) {
> > + if (dir == IIO_EV_DIR_RISING)
> > + thresh = &data->t_rh_thresh_high;
> > + else
> > + thresh = &data->t_rh_thresh_low;
> > + } else {
> > + if (dir == IIO_EV_DIR_RISING)
> > + thresh = &data->t_rh_thresh_high_clr;
> > + else
> > + thresh = &data->t_rh_thresh_low_clr;
> > + }
> > +
> > + guard(mutex)(&data->lock);
>
> Why take the lock here?
>
> you are relying on a single value that is already cached.
>
A single threshold value is used for humidity and temperature values. I
didn't see a lock in "iio_ev_value_show", so there might be some
concurrent access triggered by "in_temp_thresh_rising_value" and
"in_humidityrelative_thresh_rising_value" sysfs files which is not
secured by a mutex or similiar.

>
> > + switch (chan->type) {
> > + case IIO_TEMP:
> > + /* Get the truncated temperature threshold from 9 LSBs,
> > + * shift them and calculate the threshold according to the
> > + * formula in the datasheet.
> > + */
> > + *val = ((*thresh) & HDC3020_THRESH_TEMP_MASK) <<
> > + HDC3020_THRESH_TEMP_SHIFT;
> > + *val = -2949075 + (175 * (*val));
> > + *val2 = 65535;
> > + break;
> return here. Gives easier to review code as no need for
> a reader to check if anything else happens in this path.
>
Ok.

> > + case IIO_HUMIDITYRELATIVE:
> > + /* Get the truncated humidity threshold from 7 MSBs, and
> > + * calculate the threshold according to the formula in the
> > + * datasheet.
> > + */
> > + *val = 100 * ((*thresh) & HDC3020_THRESH_HUM_MASK);
> > + *val2 = 65535;
> > + break;
> return here
Ok.

> > + default:
> > + return -EOPNOTSUPP;
> > + }
> > +
> > + return IIO_VAL_FRACTIONAL;
> Drop this as will have returned above.
Ok.

> > +}
>
> > +
> > +static irqreturn_t hdc3020_interrupt_handler(int irq, void *private)
> > +{
> > + struct iio_dev *indio_dev = private;
> > + struct hdc3020_data *data;
> > + u16 stat;
> > + int ret;
> > +
> > + data = iio_priv(indio_dev);
> > + ret = hdc3020_read_status(data, &stat);
> > + if (ret)
> > + return IRQ_NONE;
> Hmm. In cases where we get a read back failure on an interrupt it is always
> messy to decide if it's spurious or not. If this actually happens you may
> find you want to return IRQ_HANDLED; even though it wasn't.
Will fix this, thanks.

> > +
> > + if (!(stat & (HDC3020_STATUS_T_HIGH_ALERT | HDC3020_STATUS_T_LOW_ALERT |
> > + HDC3020_STATUS_RH_HIGH_ALERT | HDC3020_STATUS_RH_LOW_ALERT)))
> > + return IRQ_NONE;
>
> This one is fine as you know it is spurious.
>
> > +
> > + if (stat & HDC3020_STATUS_T_HIGH_ALERT)
> > + iio_push_event(indio_dev,
> > + IIO_MOD_EVENT_CODE(IIO_TEMP, 0,
> > + IIO_NO_MOD,
> > + IIO_EV_TYPE_THRESH,
> > + IIO_EV_DIR_RISING),
> > + iio_get_time_ns(indio_dev));
> If you happen to get more than one, you probably want the timestamp to match.
> I'd take the timestamp first into a local variable then use it in each of these.
>
Will fix this.

> > +
> > + if (stat & HDC3020_STATUS_T_LOW_ALERT)
> > + iio_push_event(indio_dev,
> > + IIO_MOD_EVENT_CODE(IIO_TEMP, 0,
> > + IIO_NO_MOD,
> > + IIO_EV_TYPE_THRESH,
> > + IIO_EV_DIR_FALLING),
> > + iio_get_time_ns(indio_dev));
> > +
> > + if (stat & HDC3020_STATUS_RH_HIGH_ALERT)
> > + iio_push_event(indio_dev,
> > + IIO_MOD_EVENT_CODE(IIO_HUMIDITYRELATIVE, 0,
> > + IIO_NO_MOD,
> > + IIO_EV_TYPE_THRESH,
> > + IIO_EV_DIR_RISING),
> > + iio_get_time_ns(indio_dev));
> > +
> > + if (stat & HDC3020_STATUS_RH_LOW_ALERT)
> > + iio_push_event(indio_dev,
> > + IIO_MOD_EVENT_CODE(IIO_HUMIDITYRELATIVE, 0,
> > + IIO_NO_MOD,
> > + IIO_EV_TYPE_THRESH,
> > + IIO_EV_DIR_FALLING),
> > + iio_get_time_ns(indio_dev));
> > +
> > + return IRQ_HANDLED;
> > +}
> > +
> > static const struct iio_info hdc3020_info = {
> > .read_raw = hdc3020_read_raw,
> > .write_raw = hdc3020_write_raw,
> > .read_avail = hdc3020_read_available,
> > + .read_event_value = hdc3020_read_thresh,
> > + .write_event_value = hdc3020_write_thresh,
> > };
> >
> > static void hdc3020_stop(void *data)
> > @@ -402,6 +693,7 @@ static void hdc3020_stop(void *data)
> >
> > static int hdc3020_probe(struct i2c_client *client)
> > {
> > + const struct i2c_device_id *dev_id;
> > struct iio_dev *indio_dev;
> > struct hdc3020_data *data;
> > int ret;
> > @@ -413,6 +705,8 @@ static int hdc3020_probe(struct i2c_client *client)
> > if (!indio_dev)
> > return -ENOMEM;
> >
> > + dev_id = i2c_client_get_device_id(client);
> > +
> > data = iio_priv(indio_dev);
> > data->client = client;
> > mutex_init(&data->lock);
> > @@ -425,6 +719,54 @@ static int hdc3020_probe(struct i2c_client *client)
> > indio_dev->channels = hdc3020_channels;
> > indio_dev->num_channels = ARRAY_SIZE(hdc3020_channels);
> >
> > + /* Read out upper and lower thresholds and hysteresis, which can be the
>
> As below - comment syntax wrong for IIO drivers (only net and a few other
> corners of the kernel prefer this one for historical reasons).
>
Will fix this.

> > + * default values or values programmed into non-volatile memory.
> > + */
> > + ret = _hdc3020_read_thresh(data, IIO_EV_INFO_VALUE, IIO_EV_DIR_FALLING,
> > + &data->t_rh_thresh_low);
> As above, it feels to me like you can just use the registers directly into
> a be16 readback function.
>
> ret = hdc3020_read_be16_reg(indio_dev, HDC3020_R_T_RH_THRESH_LOW)
> if (ret < 0)
> return ...
>
> data->t_rh_thresh_low = ret;
> etc
>
Will fix this.

> > + if (ret)
> > + return dev_err_probe(&client->dev, ret,
> > + "Unable to get low thresholds\n");
> > +
> > + ret = _hdc3020_read_thresh(data, IIO_EV_INFO_VALUE, IIO_EV_DIR_RISING,
> > + &data->t_rh_thresh_high);
> > + if (ret)
> > + return dev_err_probe(&client->dev, ret,
> > + "Unable to get high thresholds\n");
> > +
> > + ret = _hdc3020_read_thresh(data, IIO_EV_INFO_HYSTERESIS,
> > + IIO_EV_DIR_FALLING,
> > + &data->t_rh_thresh_low_clr);
> > + if (ret)
> > + return dev_err_probe(&client->dev, ret,
> > + "Unable to get low hysteresis\n");
> > +
> > + ret = _hdc3020_read_thresh(data, IIO_EV_INFO_HYSTERESIS,
> > + IIO_EV_DIR_RISING,
> > + &data->t_rh_thresh_high_clr);
> > + if (ret)
> > + return dev_err_probe(&client->dev, ret,
> > + "Unable to get high hysteresis\n");
> > +
> > + if (client->irq) {
> Comment syntax in IIO (and most of the kernel) is
> /*
> * The alert....
>
Will fix this.

> > + /* The alert output is activated by default upon power up, hardware
> > + * reset, and soft reset. Clear the status register before enabling
> > + * the interrupt.
> That's a bit nasty. Ah well. Ordering not critical though as you are registering
> a rising trigger. However...
Will fix this. It makes more sense to clear the interrupt after registering the
interrupt handler.

> > + */
> > + ret = hdc3020_clear_status(data);
> > + if (ret)
> > + return ret;
> > +
> > + ret = devm_request_threaded_irq(&client->dev, client->irq,
> > + NULL, hdc3020_interrupt_handler,
> > + IRQF_TRIGGER_RISING |
>
> These days (we got this wrong a lot in the past) we tend to leave the interrupt
> polarity to the firmware to configure (DT or similar) as people have an annoying
> habit of using not gates in interrupt wiring. So Just pass IRQF_ONESHOT but
> make sure the DT binding example sets this right.
>
Will fix this, thanks.

> > + IRQF_ONESHOT,
> > + dev_id->name, indio_dev);
>
> dev_id->name is annoyingly unstable depending on the probe path and whether
> the various firmware match tables align perfectly with the i2c_device_id
> table. I'd just use a fixed value here for the driver in question and
> not worry about it too much. hdc3020 is fine. If you really care about
> it add a device specific structure and put a string for the name in there.
> That can then be referenced from all the firmware tables (safely) and
> i2c_get_match_data() used to get it from which ever one is available.
>
Will stick to the fixed value "hdc3020". Thanks for the explanation,
didn't know it. :)
> > + if (ret)
> > + return dev_err_probe(&client->dev, ret,
> > + "Failed to request IRQ\n");
> > + }
> > +
> > ret = hdc3020_write_bytes(data, HDC3020_S_AUTO_10HZ_MOD0, 2);
> > if (ret)
> > return dev_err_probe(&client->dev, ret,
>