From: Jonathan Cameron <jic23@kernel.org>
To: John Erasmus Mari Geronimo <johnerasmusmari.geronimo@analog.com>
Cc: <linux-iio@vger.kernel.org>, <dlechner@baylibre.com>,
<nuno.sa@analog.com>, <andy@kernel.org>,
<linux-kernel@vger.kernel.org>
Subject: Re: [PATCH v2 2/2] iio: temperature: add support for Analog Devices MAX30210
Date: Sat, 7 Mar 2026 12:38:47 +0000 [thread overview]
Message-ID: <20260307123847.19395745@jic23-huawei> (raw)
In-Reply-To: <20260304122509.67931-3-johnerasmusmari.geronimo@analog.com>
On Wed, 4 Mar 2026 20:25:09 +0800
John Erasmus Mari Geronimo <johnerasmusmari.geronimo@analog.com> wrote:
> Add support for the Analog Devices MAX30210 I2C temperature
> sensor.
>
> The driver uses regmap for register access and integrates with
> the IIO framework. It supports:
>
> - Direct mode temperature conversion
> - Configurable sampling frequency
> - Threshold events
> - FIFO operation with IIO kfifo buffer support
> - Optional interrupt-driven data ready signaling
>
> The device provides 16-bit signed temperature data and a
> 64-sample FIFO.
>
> Signed-off-by: John Erasmus Mari Geronimo <johnerasmusmari.geronimo@analog.com>
Hi John
A few additional comment from me, but overall this is coming together nicely.
Thanks,
Jonathan
> diff --git a/drivers/iio/temperature/max30210.c b/drivers/iio/temperature/max30210.c
> new file mode 100644
> index 000000000000..839ed9830957
> --- /dev/null
> +++ b/drivers/iio/temperature/max30210.c
> +static void max30210_fifo_read(struct iio_dev *indio_dev)
> +{
> + struct max30210_state *st = iio_priv(indio_dev);
> + int ret;
> +
> + ret = regmap_bulk_read(st->regmap, MAX30210_FIFO_DATA_REG,
> + st->fifo_buf, 3 * st->watermark);
> + if (ret) {
> + dev_err(&indio_dev->dev, "Failed to read from fifo.\n");
> + return;
> + }
> +
> + for (unsigned int i = 0; i < st->watermark; i++) {
> + u32 raw = get_unaligned_be24(&st->fifo_buf[3 * i]);
> +
> + if (raw == MAX30210_FIFO_INVAL_DATA) {
Whilst this aligns with how the datasheet describes it, the
only bit that is definitely different for invalid data is bit 7 of the
tag. So could just check that. The code to get the temperature value then
just becomes memcpy() with the channel described as bit endian.
I don't mind if you prefer it this way though, just thought I'd raise
the possibility.
> + dev_err_ratelimited(&indio_dev->dev, "Invalid data\n");
> + continue;
> + }
> +
> + s16 temp = (s16)(raw & 0xFFFF);
> +
> + iio_push_to_buffers(indio_dev, &temp);
> + }
> +}
> +static int max30210_read_raw(struct iio_dev *indio_dev,
> + struct iio_chan_spec const *chan, int *val,
> + int *val2, long mask)
> +{
> + struct max30210_state *st = iio_priv(indio_dev);
> + unsigned int uval;
> + int ret;
> +
> + switch (mask) {
> + case IIO_CHAN_INFO_SCALE:
> + *val = 5;
> + *val2 = 1000;
> +
> + return IIO_VAL_FRACTIONAL;
> + case IIO_CHAN_INFO_SAMP_FREQ:
> + ret = regmap_read(st->regmap, MAX30210_TEMP_CONF_2_REG, &uval);
> + if (ret)
> + return ret;
> +
> + uval = FIELD_GET(MAX30210_TEMPCONF2_TEMP_PERIOD_MASK, uval);
> +
> + if (uval >= ARRAY_SIZE(max30210_samp_freq_avail))
> + uval = ARRAY_SIZE(max30210_samp_freq_avail) - 1;
uval = min(uval, ARRAY_SIZE(max30210_samp_freq_avail) - 1);
perhaps?
> +
> + *val = max30210_samp_freq_avail[uval][0];
> + *val2 = max30210_samp_freq_avail[uval][1];
> +
> + return IIO_VAL_FRACTIONAL;
> +static int max30210_write_raw(struct iio_dev *indio_dev,
> + struct iio_chan_spec const *chan, int val,
> + int val2, long mask)
> +{
> + struct max30210_state *st = iio_priv(indio_dev);
> +
> + switch (mask) {
> + case IIO_CHAN_INFO_SAMP_FREQ: {
> + IIO_DEV_ACQUIRE_DIRECT_MODE(indio_dev, claim);
> + if (IIO_DEV_ACQUIRE_FAILED(claim))
> + return -EBUSY;
> +
> + if (val < 0 || val2 < 0)
> + return -EINVAL;
> +
> + for (unsigned int i = 0; i < ARRAY_SIZE(max30210_samp_freq_avail); i++) {
> + if (val == max30210_samp_freq_avail[i][0] &&
> + val2 == max30210_samp_freq_avail[i][1]) {
Flip the logic to reduce indent.
if (val != max...[0] ||
val != max...[1])
continue;
return....
> + return regmap_update_bits(st->regmap, MAX30210_TEMP_CONF_2_REG,
> + MAX30210_TEMPCONF2_TEMP_PERIOD_MASK,
> + FIELD_PREP(MAX30210_TEMPCONF2_TEMP_PERIOD_MASK, i));
> + }
> + }
> +
> + return -EINVAL;
> + }
> + default:
> + return -EINVAL;
> + }
> +}
> +static int max30210_buffer_preenable(struct iio_dev *indio_dev)
> +{
> + struct max30210_state *st = iio_priv(indio_dev);
> + int ret;
> +
> + ret = regmap_set_bits(st->regmap, MAX30210_INT_EN_REG, MAX30210_STATUS_A_FULL_MASK);
I would wrap this one. Won't hurt readability and keeps it inline with the wrapping
on the clear in postdisable()
> + if (ret)
> + return ret;
> +
> + ret = regmap_set_bits(st->regmap, MAX30210_FIFO_CONF_2_REG,
> + MAX30210_FIFOCONF1_FLUSH_FIFO_MASK);
> + if (ret)
> + return ret;
> +
> + return regmap_write(st->regmap, MAX30210_TEMP_CONV_REG,
> + MAX30210_TEMPCONV_AUTO_MASK | MAX30210_TEMPCONV_CONV_T_MASK);
> +}
> +
> +static int max30210_buffer_postdisable(struct iio_dev *indio_dev)
> +{
> + struct max30210_state *st = iio_priv(indio_dev);
> + int ret;
> +
> + ret = regmap_write(st->regmap, MAX30210_TEMP_CONV_REG, 0x0);
> + if (ret)
> + return ret;
> +
> + ret = regmap_set_bits(st->regmap, MAX30210_FIFO_CONF_2_REG,
> + MAX30210_FIFOCONF1_FLUSH_FIFO_MASK);
> + if (ret)
> + return ret;
> +
> + return regmap_clear_bits(st->regmap, MAX30210_INT_EN_REG,
> + MAX30210_STATUS_A_FULL_MASK);
> +}
> +
> +static const struct iio_chan_spec max30210_channels = {
only one. So max30210_channel
> + .type = IIO_TEMP,
> + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
> + BIT(IIO_CHAN_INFO_SCALE) |
> + BIT(IIO_CHAN_INFO_SAMP_FREQ),
> + .info_mask_separate_available = BIT(IIO_CHAN_INFO_SAMP_FREQ),
> + .scan_index = 0,
> + .event_spec = max30210_events,
> + .num_event_specs = ARRAY_SIZE(max30210_events),
> + .scan_type = {
> + .sign = 's',
> + .realbits = 16,
> + .storagebits = 16,
> + .shift = 0,
A zero shift is kind of considered the obvious default, so we normally don't
bother setting it explicitly.
> + .endianness = IIO_CPU,
> + },
> +};
> +
> +static int max30210_setup(struct max30210_state *st, struct device *dev)
> +{
> + struct gpio_desc *powerdown_gpio;
> + unsigned int val;
> + int ret;
> +
> + /* Optional hardware reset via powerdown GPIO */
> + powerdown_gpio = devm_gpiod_get_optional(dev, "powerdown",
> + GPIOD_OUT_HIGH);
> + if (IS_ERR(powerdown_gpio))
> + return dev_err_probe(dev, PTR_ERR(powerdown_gpio),
> + "failed to request powerdown GPIO\n");
> +
> + if (powerdown_gpio) {
> + /* Deassert powerdown to power up device */
> + gpiod_set_value(powerdown_gpio, 0);
> + } else {
> + /* Software reset fallback */
> + ret = regmap_update_bits(st->regmap, MAX30210_SYS_CONF_REG,
> + MAX30210_SYSCONF_RESET_MASK,
> + MAX30210_SYSCONF_RESET_MASK);
> + if (ret)
> + return ret;
> + }
> +
> + /* Datasheet Figure 6:
/*
* Datasheet..
> + * tPU max = 700 µs after power-up or reset before device is ready.
> + */
> + fsleep(700);
> +
> + /* Clear status byte */
> + return regmap_read(st->regmap, MAX30210_STATUS_REG, &val);
> +}
> +
> +static int max30210_probe(struct i2c_client *client)
> +{
...
> + ret = devm_iio_kfifo_buffer_setup_ext(dev, indio_dev, &max30210_buffer_ops,
> + max30210_fifo_attributes);
> + if (ret)
> + return ret;
> +
> + if (client->irq) {
> + ret = devm_request_irq(dev, client->irq, max30210_irq_handler, IRQF_NO_THREAD,
You are doing a bunch of bus accesses in your handler and I can't see anything int here
that wouldn't work in a thread. So I think this is backwards. All the work should be
done in an interrupt thread.
> + indio_dev->name, indio_dev);
> + if (ret)
> + return ret;
> + }
> +
> + return devm_iio_device_register(dev, indio_dev);
> +}
next prev parent reply other threads:[~2026-03-07 12:38 UTC|newest]
Thread overview: 11+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-03-04 12:25 [PATCH v2 0/2] Add support for Analog Devices MAX30210 John Erasmus Mari Geronimo
2026-03-04 12:25 ` [PATCH v2 1/2] dt-bindings: iio: temperature: add ADI MAX30210 John Erasmus Mari Geronimo
2026-03-05 0:11 ` David Lechner
2026-03-07 12:21 ` Jonathan Cameron
2026-03-05 7:00 ` Krzysztof Kozlowski
2026-03-04 12:25 ` [PATCH v2 2/2] iio: temperature: add support for Analog Devices MAX30210 John Erasmus Mari Geronimo
2026-03-05 0:34 ` kernel test robot
2026-03-05 0:45 ` kernel test robot
2026-03-05 0:56 ` David Lechner
2026-03-07 12:38 ` Jonathan Cameron [this message]
2026-03-04 13:42 ` [PATCH v2 0/2] Add " Andy Shevchenko
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260307123847.19395745@jic23-huawei \
--to=jic23@kernel.org \
--cc=andy@kernel.org \
--cc=dlechner@baylibre.com \
--cc=johnerasmusmari.geronimo@analog.com \
--cc=linux-iio@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=nuno.sa@analog.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.