All of lore.kernel.org
 help / color / mirror / Atom feed
From: Alexandru Ardelean <alexandru.ardelean@analog.com>
To: <linux-kernel@vger.kernel.org>, <linux-iio@vger.kernel.org>
Cc: <lars@metafoo.de>, <Michael.Hennerich@analog.com>,
	<jic23@kernel.org>, <nuno.sa@analog.com>,
	<dragos.bogdan@analog.com>,
	Mircea Caprioru <mircea.caprioru@analog.com>,
	Mihail Chindris <Mihail.Chindris@analog.com>,
	Alexandru Ardelean <alexandru.ardelean@analog.com>
Subject: [PATCH v2 5/5] iio: dac: ad5686: Add PWM as a trigger source
Date: Wed, 17 Feb 2021 10:34:38 +0200	[thread overview]
Message-ID: <20210217083438.37865-6-alexandru.ardelean@analog.com> (raw)
In-Reply-To: <20210217083438.37865-1-alexandru.ardelean@analog.com>

From: Mircea Caprioru <mircea.caprioru@analog.com>

A PWM signal will be used as a trigger source to have a deterministic
sampling frequency since this family of DAC has no hardware interrupt
source.

This feature is made optional however, as there are some board setups where
this isn't used.

Signed-off-by: Mircea Caprioru <mircea.caprioru@analog.com>
Signed-off-by: Mihail Chindris <Mihail.Chindris@analog.com>
Signed-off-by: Alexandru Ardelean <alexandru.ardelean@analog.com>
---
 drivers/iio/dac/ad5686-spi.c |   2 +-
 drivers/iio/dac/ad5686.c     | 146 ++++++++++++++++++++++++++++++++++-
 drivers/iio/dac/ad5686.h     |   7 +-
 drivers/iio/dac/ad5696-i2c.c |   2 +-
 4 files changed, 152 insertions(+), 5 deletions(-)

diff --git a/drivers/iio/dac/ad5686-spi.c b/drivers/iio/dac/ad5686-spi.c
index 0188ded5137c..07fadcf8e1e3 100644
--- a/drivers/iio/dac/ad5686-spi.c
+++ b/drivers/iio/dac/ad5686-spi.c
@@ -92,7 +92,7 @@ static int ad5686_spi_probe(struct spi_device *spi)
 	const struct spi_device_id *id = spi_get_device_id(spi);
 
 	return ad5686_probe(&spi->dev, id->driver_data, id->name,
-			    ad5686_spi_write, ad5686_spi_read);
+			    ad5686_spi_write, ad5686_spi_read, spi->irq);
 }
 
 static int ad5686_spi_remove(struct spi_device *spi)
diff --git a/drivers/iio/dac/ad5686.c b/drivers/iio/dac/ad5686.c
index 7d6792ac1020..9e48559ec566 100644
--- a/drivers/iio/dac/ad5686.c
+++ b/drivers/iio/dac/ad5686.c
@@ -16,6 +16,10 @@
 
 #include <linux/iio/iio.h>
 #include <linux/iio/sysfs.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
 
 #include "ad5686.h"
 
@@ -123,6 +127,7 @@ static int ad5686_read_raw(struct iio_dev *indio_dev,
 			   long m)
 {
 	struct ad5686_state *st = iio_priv(indio_dev);
+	struct pwm_state state;
 	int ret;
 
 	switch (m) {
@@ -139,6 +144,10 @@ static int ad5686_read_raw(struct iio_dev *indio_dev,
 		*val = st->vref_mv;
 		*val2 = chan->scan_type.realbits;
 		return IIO_VAL_FRACTIONAL_LOG2;
+	case IIO_CHAN_INFO_SAMP_FREQ:
+		pwm_get_state(st->pwm, &state);
+		*val = DIV_ROUND_CLOSEST_ULL(1000000000ULL, state.period);
+		return IIO_VAL_INT;
 	}
 	return -EINVAL;
 }
@@ -150,6 +159,7 @@ static int ad5686_write_raw(struct iio_dev *indio_dev,
 			    long mask)
 {
 	struct ad5686_state *st = iio_priv(indio_dev);
+	struct pwm_state state;
 	int ret;
 
 	switch (mask) {
@@ -164,6 +174,14 @@ static int ad5686_write_raw(struct iio_dev *indio_dev,
 				val << chan->scan_type.shift);
 		mutex_unlock(&st->lock);
 		break;
+	case IIO_CHAN_INFO_SAMP_FREQ:
+		pwm_get_state(st->pwm, &state);
+
+		state.period = DIV_ROUND_CLOSEST_ULL(1000000000ULL, val);
+		pwm_set_relative_duty_cycle(&state, 50, 100);
+
+		ret = pwm_apply_state(st->pwm, &state);
+		break;
 	default:
 		ret = -EINVAL;
 	}
@@ -171,7 +189,37 @@ static int ad5686_write_raw(struct iio_dev *indio_dev,
 	return ret;
 }
 
+static int ad5686_trig_set_state(struct iio_trigger *trig,
+				 bool state)
+{
+	struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
+	struct ad5686_state *st = iio_priv(indio_dev);
+	struct pwm_state pwm_st;
+
+	pwm_get_state(st->pwm, &pwm_st);
+	pwm_st.enabled = state;
+
+	return pwm_apply_state(st->pwm, &pwm_st);
+}
+
+static int ad5686_validate_trigger(struct iio_dev *indio_dev,
+				    struct iio_trigger *trig)
+{
+	struct ad5686_state *st = iio_priv(indio_dev);
+
+	if (st->trig != trig)
+		return -EINVAL;
+
+	return 0;
+}
+
+static const struct iio_trigger_ops ad5686_trigger_ops = {
+	.validate_device = &iio_trigger_validate_own_device,
+	.set_trigger_state = &ad5686_trig_set_state,
+};
+
 static const struct iio_info ad5686_info = {
+	.validate_trigger = &ad5686_validate_trigger,
 	.read_raw = ad5686_read_raw,
 	.write_raw = ad5686_write_raw,
 };
@@ -194,8 +242,10 @@ static const struct iio_chan_spec_ext_info ad5686_ext_info[] = {
 		.output = 1,					\
 		.channel = chan,				\
 		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),	\
-		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),\
+		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \
+					    BIT(IIO_CHAN_INFO_SAMP_FREQ),\
 		.address = addr,				\
+		.scan_index = chan,				\
 		.scan_type = {					\
 			.sign = 'u',				\
 			.realbits = (bits),			\
@@ -428,13 +478,57 @@ static const struct ad5686_chip_info ad5686_chip_info_tbl[] = {
 	},
 };
 
+static irqreturn_t ad5686_trigger_handler(int irq, void *p)
+{
+	struct iio_poll_func *pf = p;
+	struct iio_dev *indio_dev = pf->indio_dev;
+	const struct iio_chan_spec *chan;
+	struct iio_buffer *buffer = indio_dev->buffer;
+	struct ad5686_state *st = iio_priv(indio_dev);
+	u8 sample[2];
+	unsigned int i;
+	u16 val;
+	int ret;
+
+	ret = iio_buffer_remove_sample(buffer, sample);
+	if (ret < 0)
+		goto out;
+
+	mutex_lock(&st->lock);
+	for_each_set_bit(i, indio_dev->active_scan_mask, indio_dev->masklength) {
+		val = (sample[1] << 8) + sample[0];
+
+		chan = iio_find_channel_from_si(indio_dev, i);
+		ret = st->write(st, AD5686_CMD_WRITE_INPUT_N_UPDATE_N,
+				chan->address, val << chan->scan_type.shift);
+	}
+	mutex_unlock(&st->lock);
+
+out:
+	iio_trigger_notify_done(indio_dev->trig);
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t ad5686_irq_handler(int irq, void *data)
+{
+	struct iio_dev *indio_dev = data;
+	struct ad5686_state *st = iio_priv(indio_dev);
+
+	if (iio_buffer_enabled(indio_dev))
+		iio_trigger_poll(st->trig);
+
+	return IRQ_HANDLED;
+}
+
 int ad5686_probe(struct device *dev,
 		 enum ad5686_supported_device_ids chip_type,
 		 const char *name, ad5686_write_func write,
-		 ad5686_read_func read)
+		 ad5686_read_func read, int irq)
 {
 	struct ad5686_state *st;
 	struct iio_dev *indio_dev;
+	struct pwm_state state;
 	unsigned int val, ref_bit_msk;
 	u8 cmd;
 	int ret, i, voltage_uv = 0;
@@ -450,6 +544,23 @@ int ad5686_probe(struct device *dev,
 	st->write = write;
 	st->read = read;
 
+	mutex_init(&st->lock);
+
+	st->trig = devm_iio_trigger_alloc(dev, "%s-dev%d", name, indio_dev->id);
+	if (st->trig == NULL)
+		ret = -ENOMEM;
+
+	st->trig->ops = &ad5686_trigger_ops;
+	st->trig->dev.parent = dev;
+	iio_trigger_set_drvdata(st->trig, indio_dev);
+
+	ret = devm_iio_trigger_register(dev, st->trig);
+	if (ret)
+		return ret;
+
+	/* select default trigger */
+	indio_dev->trig = iio_trigger_get(st->trig);
+
 	st->reg = devm_regulator_get_optional(dev, "vcc");
 	if (!IS_ERR(st->reg)) {
 		ret = regulator_enable(st->reg);
@@ -463,6 +574,30 @@ int ad5686_probe(struct device *dev,
 		voltage_uv = ret;
 	}
 
+	/* PWM configuration */
+	st->pwm = devm_pwm_get(dev, "pwm-trigger");
+	if (!IS_ERR(st->pwm)) {
+		/* Set a default pwm frequency of 1kHz and 50% duty cycle */
+		pwm_init_state(st->pwm, &state);
+		state.enabled = false;
+		state.period = 1000000;
+		pwm_set_relative_duty_cycle(&state, 50, 100);
+		ret = pwm_apply_state(st->pwm, &state);
+		if (ret < 0)
+			return ret;
+	}
+
+	/* Configure IRQ */
+	if (irq) {
+		ret = devm_request_threaded_irq(dev, irq, NULL, ad5686_irq_handler,
+						IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+						"ad5686 irq", indio_dev);
+		if (ret)
+			return ret;
+
+		st->irq = irq;
+	}
+
 	st->chip_info = &ad5686_chip_info_tbl[chip_type];
 
 	if (voltage_uv)
@@ -513,6 +648,13 @@ int ad5686_probe(struct device *dev,
 	if (ret)
 		goto error_disable_reg;
 
+	ret = devm_iio_triggered_buffer_setup_ext(dev, indio_dev, NULL,
+						  &ad5686_trigger_handler,
+						  IIO_BUFFER_DIRECTION_OUT,
+						  NULL, NULL);
+	if (ret)
+		goto error_disable_reg;
+
 	ret = iio_device_register(indio_dev);
 	if (ret)
 		goto error_disable_reg;
diff --git a/drivers/iio/dac/ad5686.h b/drivers/iio/dac/ad5686.h
index d9c8ba413fe9..b287873553a1 100644
--- a/drivers/iio/dac/ad5686.h
+++ b/drivers/iio/dac/ad5686.h
@@ -12,6 +12,7 @@
 #include <linux/cache.h>
 #include <linux/mutex.h>
 #include <linux/kernel.h>
+#include <linux/pwm.h>
 
 #define AD5310_CMD(x)				((x) << 12)
 
@@ -112,6 +113,7 @@ struct ad5686_chip_info {
 /**
  * struct ad5446_state - driver instance specific data
  * @spi:		spi_device
+ * @pwm:		pwm used for buffer trigger
  * @chip_info:		chip model specific constants, available modes etc
  * @reg:		supply regulator
  * @vref_mv:		actual reference voltage used
@@ -124,6 +126,8 @@ struct ad5686_chip_info {
 
 struct ad5686_state {
 	struct device			*dev;
+	struct pwm_device		*pwm;
+	struct iio_trigger		*trig;
 	const struct ad5686_chip_info	*chip_info;
 	struct regulator		*reg;
 	unsigned short			vref_mv;
@@ -133,6 +137,7 @@ struct ad5686_state {
 	ad5686_read_func		read;
 	bool				use_internal_vref;
 	struct mutex			lock;
+	int				irq;
 
 	/*
 	 * DMA (thus cache coherency maintenance) requires the
@@ -150,7 +155,7 @@ struct ad5686_state {
 int ad5686_probe(struct device *dev,
 		 enum ad5686_supported_device_ids chip_type,
 		 const char *name, ad5686_write_func write,
-		 ad5686_read_func read);
+		 ad5686_read_func read, int irq);
 
 int ad5686_remove(struct device *dev);
 
diff --git a/drivers/iio/dac/ad5696-i2c.c b/drivers/iio/dac/ad5696-i2c.c
index a39eda7c02d2..f80acc0972ea 100644
--- a/drivers/iio/dac/ad5696-i2c.c
+++ b/drivers/iio/dac/ad5696-i2c.c
@@ -62,7 +62,7 @@ static int ad5686_i2c_probe(struct i2c_client *i2c,
 			    const struct i2c_device_id *id)
 {
 	return ad5686_probe(&i2c->dev, id->driver_data, id->name,
-			    ad5686_i2c_write, ad5686_i2c_read);
+			    ad5686_i2c_write, ad5686_i2c_read, i2c->irq);
 }
 
 static int ad5686_i2c_remove(struct i2c_client *i2c)
-- 
2.17.1


  parent reply	other threads:[~2021-02-17  8:35 UTC|newest]

Thread overview: 18+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-02-17  8:34 [PATCH v2 0/5] iio: Add output buffer support Alexandru Ardelean
2021-02-17  8:34 ` [PATCH v2 1/5] iio: Documentation: update definitions for bufferY and scan_elements Alexandru Ardelean
2021-02-18 12:19   ` Jonathan Cameron
2021-02-18 13:36     ` Jonathan Cameron
2021-04-01 13:44   ` Mauro Carvalho Chehab
2021-04-01 15:00     ` Jonathan Cameron
2021-02-17  8:34 ` [PATCH v2 2/5] iio: Add output buffer support Alexandru Ardelean
2021-02-18 13:42   ` Jonathan Cameron
2021-02-19  8:45     ` Alexandru Ardelean
2021-02-17  8:34 ` [PATCH v2 3/5] iio: kfifo-buffer: " Alexandru Ardelean
2021-02-17  8:34 ` [PATCH v2 4/5] iio: triggered-buffer: extend support to configure output buffers Alexandru Ardelean
2021-02-17  8:34 ` Alexandru Ardelean [this message]
2021-02-18 14:05   ` [PATCH v2 5/5] iio: dac: ad5686: Add PWM as a trigger source Jonathan Cameron
2021-02-18 14:35     ` Rob Herring
2021-02-19  8:49       ` Alexandru Ardelean
2021-02-23 16:37     ` Lars-Peter Clausen
2021-02-27 15:45       ` Jonathan Cameron
2021-02-18 13:38 ` [PATCH v2 0/5] iio: Add output buffer support 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=20210217083438.37865-6-alexandru.ardelean@analog.com \
    --to=alexandru.ardelean@analog.com \
    --cc=Michael.Hennerich@analog.com \
    --cc=Mihail.Chindris@analog.com \
    --cc=dragos.bogdan@analog.com \
    --cc=jic23@kernel.org \
    --cc=lars@metafoo.de \
    --cc=linux-iio@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=mircea.caprioru@analog.com \
    --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.