[PATCH v2 5/5] iio: dac: ad5686: Add PWM as a trigger source

From: Alexandru Ardelean
Date: Wed Feb 17 2021 - 03:35:11 EST


From: Mircea Caprioru <mircea.caprioru@xxxxxxxxxx>

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@xxxxxxxxxx>
Signed-off-by: Mihail Chindris <Mihail.Chindris@xxxxxxxxxx>
Signed-off-by: Alexandru Ardelean <alexandru.ardelean@xxxxxxxxxx>
---
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