From: Jorge Marques <jorge.marques@analog.com>
To: "Jonathan Cameron" <jic23@kernel.org>,
"Lars-Peter Clausen" <lars@metafoo.de>,
"Michael Hennerich" <Michael.Hennerich@analog.com>,
"Rob Herring" <robh@kernel.org>,
"Krzysztof Kozlowski" <krzk+dt@kernel.org>,
"Conor Dooley" <conor+dt@kernel.org>,
"Jonathan Corbet" <corbet@lwn.net>,
"David Lechner" <dlechner@baylibre.com>,
"Nuno Sá" <nuno.sa@analog.com>,
"Andy Shevchenko" <andy@kernel.org>,
"Uwe Kleine-König" <ukleinek@kernel.org>
Cc: <linux-iio@vger.kernel.org>, <linux-kernel@vger.kernel.org>,
<devicetree@vger.kernel.org>, <linux-doc@vger.kernel.org>,
<linux-pwm@vger.kernel.org>,
Jorge Marques <jorge.marques@analog.com>
Subject: [PATCH v3 8/8] iio: adc: Add events support to ad4052
Date: Tue, 10 Jun 2025 09:34:41 +0200 [thread overview]
Message-ID: <20250610-iio-driver-ad4052-v3-8-cf1e44c516d4@analog.com> (raw)
In-Reply-To: <20250610-iio-driver-ad4052-v3-0-cf1e44c516d4@analog.com>
The AD4052 family supports autonomous monitoring readings for threshold
crossings. Add support for catching the GPIO interrupt and expose as an IIO
event. The device allows to set either, rising and falling directions. Only
either threshold crossing is implemented.
Signed-off-by: Jorge Marques <jorge.marques@analog.com>
---
drivers/iio/adc/ad4052.c | 369 +++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 369 insertions(+)
diff --git a/drivers/iio/adc/ad4052.c b/drivers/iio/adc/ad4052.c
index 7d32dc4701ddb0204b5505a650ce7caafc2cb5ed..ff52ff002bfe0ee413ae352b0c1854798b8e89f8 100644
--- a/drivers/iio/adc/ad4052.c
+++ b/drivers/iio/adc/ad4052.c
@@ -13,6 +13,7 @@
#include <linux/gpio/consumer.h>
#include <linux/iio/buffer.h>
#include <linux/iio/buffer-dmaengine.h>
+#include <linux/iio/events.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
#include <linux/interrupt.h>
@@ -69,6 +70,8 @@
#define AD4052_REG_MON_VAL 0x2F
#define AD4052_REG_FUSE_CRC 0x40
#define AD4052_REG_DEVICE_STATUS 0x41
+#define AD4052_REG_DEVICE_STATUS_MIN_FLAG BIT(2)
+#define AD4052_REG_DEVICE_STATUS_MAX_FLAG BIT(3)
#define AD4052_REG_DEVICE_STATUS_DEVICE_RDY BIT(7)
#define AD4052_REG_DEVICE_STATUS_DEVICE_RESET BIT(6)
#define AD4052_REG_MIN_SAMPLE 0x45
@@ -173,6 +176,8 @@ struct ad4052_state {
struct completion completion;
struct regmap *regmap;
u16 oversampling_frequency;
+ u16 events_frequency;
+ bool wait_event;
int gp1_irq;
int vio_uv;
int vref_uv;
@@ -259,6 +264,26 @@ static const struct regmap_access_table ad4052_regmap_wr_table = {
.n_yes_ranges = ARRAY_SIZE(ad4052_regmap_wr_ranges),
};
+static const struct iio_event_spec ad4052_events[] = {
+ {
+ .type = IIO_EV_TYPE_THRESH,
+ .dir = IIO_EV_DIR_EITHER,
+ .mask_shared_by_all = BIT(IIO_EV_INFO_ENABLE),
+ },
+ {
+ .type = IIO_EV_TYPE_THRESH,
+ .dir = IIO_EV_DIR_RISING,
+ .mask_shared_by_all = BIT(IIO_EV_INFO_VALUE) |
+ BIT(IIO_EV_INFO_HYSTERESIS),
+ },
+ {
+ .type = IIO_EV_TYPE_THRESH,
+ .dir = IIO_EV_DIR_FALLING,
+ .mask_shared_by_all = BIT(IIO_EV_INFO_VALUE) |
+ BIT(IIO_EV_INFO_HYSTERESIS),
+ },
+};
+
static const char *const ad4052_conversion_freqs[] = {
"2000000", "1000000", "300000", "100000", /* 0 - 3 */
"33300", "10000", "3000", "500", /* 4 - 7 */
@@ -328,6 +353,8 @@ AD4052_EXT_INFO(AD4052_500KSPS);
.info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \
.indexed = 1, \
.channel = 0, \
+ .event_spec = ad4052_events, \
+ .num_event_specs = ARRAY_SIZE(ad4052_events), \
.has_ext_scan_type = 1, \
.ext_scan_type = ad4052_scan_type_##bits##_s, \
.num_ext_scan_type = ARRAY_SIZE(ad4052_scan_type_##bits##_s), \
@@ -344,6 +371,8 @@ AD4052_EXT_INFO(AD4052_500KSPS);
.info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \
.indexed = 1, \
.channel = 0, \
+ .event_spec = ad4052_events, \
+ .num_event_specs = ARRAY_SIZE(ad4052_events), \
.has_ext_scan_type = 1, \
.ext_scan_type = ad4052_scan_type_##bits##_s, \
.num_ext_scan_type = ARRAY_SIZE(ad4052_scan_type_##bits##_s), \
@@ -386,6 +415,74 @@ static const struct ad4052_chip_info ad4058_chip_info = {
.grade = AD4052_500KSPS,
};
+static ssize_t ad4052_events_frequency_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct ad4052_state *st = iio_priv(dev_to_iio_dev(dev));
+
+ return sysfs_emit(buf, "%s\n", ad4052_conversion_freqs[st->events_frequency]);
+}
+
+static ssize_t ad4052_events_frequency_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct ad4052_state *st = iio_priv(indio_dev);
+ int ret;
+
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
+ if (st->wait_event) {
+ ret = -EBUSY;
+ goto out_release;
+ }
+
+ ret = __sysfs_match_string(AD4052_FS(st->chip->grade),
+ AD4052_FS_LEN(st->chip->grade), buf);
+ if (ret < 0)
+ goto out_release;
+
+ st->events_frequency = ret;
+
+out_release:
+ iio_device_release_direct(indio_dev);
+ return ret ? ret : len;
+}
+
+static IIO_DEVICE_ATTR(sampling_frequency, 0644,
+ ad4052_events_frequency_show,
+ ad4052_events_frequency_store, 0);
+
+static ssize_t sampling_frequency_available_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct ad4052_state *st = iio_priv(dev_to_iio_dev(dev));
+ int ret = 0;
+
+ for (u8 i = AD4052_FS_OFFSET(st->chip->grade);
+ i < AD4052_FS_LEN(st->chip->grade); i++)
+ ret += sysfs_emit_at(buf, ret, "%s ", ad4052_conversion_freqs[i]);
+
+ ret += sysfs_emit_at(buf, ret, "\n");
+ return ret;
+}
+
+static IIO_DEVICE_ATTR_RO(sampling_frequency_available, 0);
+
+static struct attribute *ad4052_event_attributes[] = {
+ &iio_dev_attr_sampling_frequency.dev_attr.attr,
+ &iio_dev_attr_sampling_frequency_available.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group ad4052_event_attribute_group = {
+ .attrs = ad4052_event_attributes,
+};
+
static int ad4052_update_xfer_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan)
{
@@ -602,6 +699,19 @@ static int ad4052_setup(struct iio_dev *indio_dev, struct iio_chan_spec const *c
val);
}
+static irqreturn_t ad4052_irq_handler_thresh(int irq, void *private)
+{
+ struct iio_dev *indio_dev = private;
+
+ iio_push_event(indio_dev,
+ IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, 0,
+ IIO_EV_TYPE_THRESH,
+ IIO_EV_DIR_EITHER),
+ iio_get_time_ns(indio_dev));
+
+ return IRQ_HANDLED;
+}
+
static irqreturn_t ad4052_irq_handler_drdy(int irq, void *private)
{
struct ad4052_state *st = private;
@@ -616,6 +726,18 @@ static int ad4052_request_irq(struct iio_dev *indio_dev)
struct device *dev = &st->spi->dev;
int ret;
+ ret = fwnode_irq_get_byname(dev_fwnode(&st->spi->dev), "gp0");
+ if (ret > 0) {
+ ret = devm_request_threaded_irq(dev, ret, NULL,
+ ad4052_irq_handler_thresh,
+ IRQF_ONESHOT, indio_dev->name,
+ indio_dev);
+ if (ret)
+ return ret;
+ } else if (ret == -EPROBE_DEFER) {
+ return ret;
+ }
+
ret = fwnode_irq_get_byname(dev_fwnode(&st->spi->dev), "gp1");
if (ret > 0) {
ret = devm_request_threaded_irq(dev, ret, NULL,
@@ -822,6 +944,7 @@ static int ad4052_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, int *val,
int *val2, long info)
{
+ struct ad4052_state *st = iio_priv(indio_dev);
int ret;
if (info == IIO_CHAN_INFO_SAMP_FREQ)
@@ -831,8 +954,14 @@ static int ad4052_read_raw(struct iio_dev *indio_dev,
if (!iio_device_claim_direct(indio_dev))
return -EBUSY;
+ if (st->wait_event) {
+ ret = -EBUSY;
+ goto out_release;
+ }
ret = ad4052_read_raw_dispatch(indio_dev, chan, val, val2, info);
+
+out_release:
iio_device_release_direct(indio_dev);
return ret ? ret : IIO_VAL_INT;
}
@@ -867,8 +996,231 @@ static int ad4052_write_raw(struct iio_dev *indio_dev,
if (!iio_device_claim_direct(indio_dev))
return -EBUSY;
+ if (st->wait_event) {
+ ret = -EBUSY;
+ goto out_release;
+ }
ret = ad4052_write_raw_dispatch(indio_dev, chan, val, val2, info);
+
+out_release:
+ iio_device_release_direct(indio_dev);
+ return ret;
+}
+
+static int ad4052_monitor_mode_enable(struct ad4052_state *st)
+{
+ int ret;
+
+ ret = pm_runtime_resume_and_get(&st->spi->dev);
+ if (ret)
+ return ret;
+
+ ret = ad4052_conversion_frequency_set(st, st->events_frequency);
+ if (ret)
+ goto out_error;
+
+ ret = ad4052_set_operation_mode(st, AD4052_MONITOR_MODE);
+ if (ret)
+ goto out_error;
+
+ return ret;
+out_error:
+ pm_runtime_mark_last_busy(&st->spi->dev);
+ pm_runtime_put_autosuspend(&st->spi->dev);
+ return ret;
+}
+
+static int ad4052_monitor_mode_disable(struct ad4052_state *st)
+{
+ int ret;
+
+ pm_runtime_mark_last_busy(&st->spi->dev);
+ pm_runtime_put_autosuspend(&st->spi->dev);
+
+ ret = ad4052_exit_command(st);
+ if (ret)
+ return ret;
+ return regmap_write(st->regmap, AD4052_REG_DEVICE_STATUS,
+ AD4052_REG_DEVICE_STATUS_MAX_FLAG |
+ AD4052_REG_DEVICE_STATUS_MIN_FLAG);
+}
+
+static int ad4052_read_event_config(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ enum iio_event_type type,
+ enum iio_event_direction dir)
+{
+ struct ad4052_state *st = iio_priv(indio_dev);
+
+ return st->wait_event;
+}
+
+static int ad4052_write_event_config(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ enum iio_event_type type,
+ enum iio_event_direction dir,
+ bool state)
+{
+ struct ad4052_state *st = iio_priv(indio_dev);
+ int ret;
+
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
+ if (st->wait_event == state) {
+ ret = 0;
+ goto out_release;
+ }
+
+ if (state)
+ ret = ad4052_monitor_mode_enable(st);
+ else
+ ret = ad4052_monitor_mode_disable(st);
+
+ if (!ret)
+ st->wait_event = state;
+
+out_release:
+ iio_device_release_direct(indio_dev);
+ return ret;
+}
+
+static int __ad4052_read_event_info_value(struct ad4052_state *st,
+ enum iio_event_direction dir, int *val)
+{
+ int ret;
+ u8 reg;
+
+ if (dir == IIO_EV_DIR_RISING)
+ reg = AD4052_REG_MAX_LIMIT;
+ else
+ reg = AD4052_REG_MIN_LIMIT;
+
+ ret = regmap_bulk_read(st->regmap, reg, &st->raw, 2);
+ if (ret)
+ return ret;
+
+ *val = sign_extend32(get_unaligned_be16(st->raw), 11);
+
+ return 0;
+}
+
+static int __ad4052_read_event_info_hysteresis(struct ad4052_state *st,
+ enum iio_event_direction dir, int *val)
+{
+ u8 reg;
+
+ if (dir == IIO_EV_DIR_RISING)
+ reg = AD4052_REG_MAX_HYST;
+ else
+ reg = AD4052_REG_MIN_HYST;
+ return regmap_read(st->regmap, reg, val);
+}
+
+static int ad4052_read_event_value(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 ad4052_state *st = iio_priv(indio_dev);
+ int ret;
+
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
+
+ if (st->wait_event) {
+ ret = -EBUSY;
+ goto out_release;
+ }
+
+ switch (info) {
+ case IIO_EV_INFO_VALUE:
+ ret = __ad4052_read_event_info_value(st, dir, val);
+ break;
+ case IIO_EV_INFO_HYSTERESIS:
+ ret = __ad4052_read_event_info_hysteresis(st, dir, val);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+out_release:
+ iio_device_release_direct(indio_dev);
+ return ret ? ret : IIO_VAL_INT;
+}
+
+static int __ad4052_write_event_info_value(struct ad4052_state *st,
+ enum iio_event_direction dir, int val)
+{
+ u8 reg;
+
+ if (val > 2047 || val < -2048)
+ return -EINVAL;
+ if (dir == IIO_EV_DIR_RISING)
+ reg = AD4052_REG_MAX_LIMIT;
+ else
+ reg = AD4052_REG_MIN_LIMIT;
+ put_unaligned_be16(val, &st->raw);
+
+ return regmap_bulk_write(st->regmap, reg, &st->raw, 2);
+}
+
+static int __ad4052_write_event_info_hysteresis(struct ad4052_state *st,
+ enum iio_event_direction dir, int val)
+{
+ u8 reg;
+
+ if (val >= BIT(7))
+ return -EINVAL;
+ if (dir == IIO_EV_DIR_RISING)
+ reg = AD4052_REG_MAX_HYST;
+ else
+ reg = AD4052_REG_MIN_HYST;
+
+ return regmap_write(st->regmap, reg, val);
+}
+
+static int ad4052_write_event_value(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 ad4052_state *st = iio_priv(indio_dev);
+ int ret;
+
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
+
+ if (st->wait_event) {
+ ret = -EBUSY;
+ goto out_release;
+ }
+
+ switch (type) {
+ case IIO_EV_TYPE_THRESH:
+ switch (info) {
+ case IIO_EV_INFO_VALUE:
+ ret = __ad4052_write_event_info_value(st, dir, val);
+ break;
+ case IIO_EV_INFO_HYSTERESIS:
+ ret = __ad4052_write_event_info_hysteresis(st, dir, val);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+out_release:
iio_device_release_direct(indio_dev);
return ret;
}
@@ -881,6 +1233,9 @@ static int ad4052_offload_buffer_postenable(struct iio_dev *indio_dev)
};
int ret;
+ if (st->wait_event)
+ return -EBUSY;
+
ret = pm_runtime_resume_and_get(&st->spi->dev);
if (ret)
return ret;
@@ -963,10 +1318,17 @@ static int ad4052_debugfs_reg_access(struct iio_dev *indio_dev, unsigned int reg
if (!iio_device_claim_direct(indio_dev))
return -EBUSY;
+ if (st->wait_event) {
+ ret = -EBUSY;
+ goto out_release;
+ }
+
if (readval)
ret = regmap_read(st->regmap, reg, readval);
else
ret = regmap_write(st->regmap, reg, writeval);
+
+out_release:
iio_device_release_direct(indio_dev);
return ret;
}
@@ -985,6 +1347,11 @@ static const struct iio_info ad4052_info = {
.read_raw = ad4052_read_raw,
.write_raw = ad4052_write_raw,
.read_avail = ad4052_read_avail,
+ .read_event_config = &ad4052_read_event_config,
+ .write_event_config = &ad4052_write_event_config,
+ .read_event_value = &ad4052_read_event_value,
+ .write_event_value = &ad4052_write_event_value,
+ .event_attrs = &ad4052_event_attribute_group,
.get_current_scan_type = &ad4052_get_current_scan_type,
.debugfs_reg_access = &ad4052_debugfs_reg_access,
};
@@ -1193,8 +1560,10 @@ static int ad4052_probe(struct spi_device *spi)
"Failed to initialize regmap\n");
st->mode = AD4052_SAMPLE_MODE;
+ st->wait_event = false;
st->chip = chip;
st->oversampling_frequency = AD4052_FS_OFFSET(st->chip->grade);
+ st->events_frequency = AD4052_FS_OFFSET(st->chip->grade);
st->cnv_gp = devm_gpiod_get_optional(dev, "cnv", GPIOD_OUT_LOW);
if (IS_ERR(st->cnv_gp))
--
2.49.0
next prev parent reply other threads:[~2025-06-10 7:35 UTC|newest]
Thread overview: 37+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-06-10 7:34 [PATCH v3 0/8] Add support for AD4052 device family Jorge Marques
2025-06-10 7:34 ` [PATCH v3 1/8] Documentation: ABI: add oversampling frequency in sysfs-bus-iio Jorge Marques
2025-06-11 17:02 ` Jonathan Cameron
2025-06-12 10:10 ` Jorge Marques
2025-06-10 7:34 ` [PATCH v3 2/8] dt-bindings: iio: adc: Add adi,ad4052 Jorge Marques
2025-06-11 17:18 ` Jonathan Cameron
2025-06-12 10:11 ` Jorge Marques
2025-06-12 15:03 ` David Lechner
2025-06-12 19:42 ` Jorge Marques
2025-06-12 20:20 ` David Lechner
2025-06-13 11:17 ` Jorge Marques
2025-06-14 10:14 ` Jonathan Cameron
2025-06-10 7:34 ` [PATCH v3 3/8] docs: iio: New docs for ad4052 driver Jorge Marques
2025-06-10 7:34 ` [PATCH v3 4/8] iio: adc: Add support for ad4052 Jorge Marques
2025-06-14 10:08 ` Jonathan Cameron
2025-06-16 14:54 ` David Lechner
2025-06-21 16:08 ` Jonathan Cameron
2025-06-21 16:13 ` David Lechner
2025-06-22 14:28 ` Jonathan Cameron
2025-06-17 14:59 ` Uwe Kleine-König
2025-06-17 15:34 ` Jorge Marques
2025-06-18 17:55 ` Uwe Kleine-König
2025-06-10 7:34 ` [PATCH v3 5/8] docs: iio: ad4052: Add offload support documentation Jorge Marques
2025-06-10 7:34 ` [PATCH v3 6/8] iio: adc: Add offload support for ad4052 Jorge Marques
2025-06-14 10:20 ` Jonathan Cameron
2025-06-20 18:52 ` Jorge Marques
2025-06-21 16:16 ` Jonathan Cameron
2025-06-10 7:34 ` [PATCH v3 7/8] docs: iio: ad4052: Add event documentation Jorge Marques
2025-06-10 7:34 ` Jorge Marques [this message]
2025-06-12 19:38 ` [PATCH v3 8/8] iio: adc: Add events support to ad4052 David Lechner
2025-06-13 10:02 ` Jorge Marques
2025-06-13 16:03 ` David Lechner
2025-06-14 10:25 ` Jonathan Cameron
2025-06-14 10:40 ` Jonathan Cameron
2025-06-14 10:36 ` Jonathan Cameron
2025-06-16 13:54 ` Jorge Marques
2025-06-21 16:20 ` Jonathan Cameron
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=20250610-iio-driver-ad4052-v3-8-cf1e44c516d4@analog.com \
--to=jorge.marques@analog.com \
--cc=Michael.Hennerich@analog.com \
--cc=andy@kernel.org \
--cc=conor+dt@kernel.org \
--cc=corbet@lwn.net \
--cc=devicetree@vger.kernel.org \
--cc=dlechner@baylibre.com \
--cc=jic23@kernel.org \
--cc=krzk+dt@kernel.org \
--cc=lars@metafoo.de \
--cc=linux-doc@vger.kernel.org \
--cc=linux-iio@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-pwm@vger.kernel.org \
--cc=nuno.sa@analog.com \
--cc=robh@kernel.org \
--cc=ukleinek@kernel.org \
/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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).