2
0
mirror of git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git synced 2025-09-04 20:19:47 +08:00

iio: adc: ad7606: add SPI offload support

Add SPI offload support for this family.

Signed-off-by: Angelo Dureghello <adureghello@baylibre.com>
Reviewed-by: David Lechner <dlechner@baylibre.com>
Link: https://patch.msgid.link/20250403-wip-bl-spi-offload-ad7606-v1-3-1b00cb638b12@baylibre.com
Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
This commit is contained in:
Angelo Dureghello 2025-04-03 18:19:06 +02:00 committed by Jonathan Cameron
parent 89e1f95a61
commit e96d35faf3
4 changed files with 265 additions and 14 deletions

View File

@ -289,6 +289,8 @@ config AD7606_IFACE_SPI
tristate "Analog Devices AD7606 ADC driver with spi interface support" tristate "Analog Devices AD7606 ADC driver with spi interface support"
depends on SPI depends on SPI
select AD7606 select AD7606
select IIO_BUFFER_DMAENGINE
select SPI_OFFLOAD
help help
Say yes here to build spi interface support for Analog Devices: Say yes here to build spi interface support for Analog Devices:
ad7605-4, ad7606, ad7606-6, ad7606-4 analog to digital converters (ADC). ad7605-4, ad7606, ad7606-6, ad7606-4 analog to digital converters (ADC).

View File

@ -138,6 +138,7 @@ const struct ad7606_chip_info ad7606_6_info = {
.oversampling_avail = ad7606_oversampling_avail, .oversampling_avail = ad7606_oversampling_avail,
.oversampling_num = ARRAY_SIZE(ad7606_oversampling_avail), .oversampling_num = ARRAY_SIZE(ad7606_oversampling_avail),
.scale_setup_cb = ad7606_16bit_chan_scale_setup, .scale_setup_cb = ad7606_16bit_chan_scale_setup,
.offload_storagebits = 32,
}; };
EXPORT_SYMBOL_NS_GPL(ad7606_6_info, "IIO_AD7606"); EXPORT_SYMBOL_NS_GPL(ad7606_6_info, "IIO_AD7606");
@ -149,6 +150,7 @@ const struct ad7606_chip_info ad7606_4_info = {
.oversampling_avail = ad7606_oversampling_avail, .oversampling_avail = ad7606_oversampling_avail,
.oversampling_num = ARRAY_SIZE(ad7606_oversampling_avail), .oversampling_num = ARRAY_SIZE(ad7606_oversampling_avail),
.scale_setup_cb = ad7606_16bit_chan_scale_setup, .scale_setup_cb = ad7606_16bit_chan_scale_setup,
.offload_storagebits = 32,
}; };
EXPORT_SYMBOL_NS_GPL(ad7606_4_info, "IIO_AD7606"); EXPORT_SYMBOL_NS_GPL(ad7606_4_info, "IIO_AD7606");
@ -161,6 +163,7 @@ const struct ad7606_chip_info ad7606b_info = {
.oversampling_num = ARRAY_SIZE(ad7606_oversampling_avail), .oversampling_num = ARRAY_SIZE(ad7606_oversampling_avail),
.scale_setup_cb = ad7606_16bit_chan_scale_setup, .scale_setup_cb = ad7606_16bit_chan_scale_setup,
.sw_setup_cb = ad7606b_sw_mode_setup, .sw_setup_cb = ad7606b_sw_mode_setup,
.offload_storagebits = 32,
}; };
EXPORT_SYMBOL_NS_GPL(ad7606b_info, "IIO_AD7606"); EXPORT_SYMBOL_NS_GPL(ad7606b_info, "IIO_AD7606");
@ -173,6 +176,7 @@ const struct ad7606_chip_info ad7606c_16_info = {
.oversampling_num = ARRAY_SIZE(ad7606_oversampling_avail), .oversampling_num = ARRAY_SIZE(ad7606_oversampling_avail),
.scale_setup_cb = ad7606c_16bit_chan_scale_setup, .scale_setup_cb = ad7606c_16bit_chan_scale_setup,
.sw_setup_cb = ad7606b_sw_mode_setup, .sw_setup_cb = ad7606b_sw_mode_setup,
.offload_storagebits = 32,
}; };
EXPORT_SYMBOL_NS_GPL(ad7606c_16_info, "IIO_AD7606"); EXPORT_SYMBOL_NS_GPL(ad7606c_16_info, "IIO_AD7606");
@ -184,6 +188,7 @@ const struct ad7606_chip_info ad7607_info = {
.oversampling_avail = ad7606_oversampling_avail, .oversampling_avail = ad7606_oversampling_avail,
.oversampling_num = ARRAY_SIZE(ad7606_oversampling_avail), .oversampling_num = ARRAY_SIZE(ad7606_oversampling_avail),
.scale_setup_cb = ad7607_chan_scale_setup, .scale_setup_cb = ad7607_chan_scale_setup,
.offload_storagebits = 32,
}; };
EXPORT_SYMBOL_NS_GPL(ad7607_info, "IIO_AD7606"); EXPORT_SYMBOL_NS_GPL(ad7607_info, "IIO_AD7606");
@ -195,6 +200,7 @@ const struct ad7606_chip_info ad7608_info = {
.oversampling_avail = ad7606_oversampling_avail, .oversampling_avail = ad7606_oversampling_avail,
.oversampling_num = ARRAY_SIZE(ad7606_oversampling_avail), .oversampling_num = ARRAY_SIZE(ad7606_oversampling_avail),
.scale_setup_cb = ad7608_chan_scale_setup, .scale_setup_cb = ad7608_chan_scale_setup,
.offload_storagebits = 32,
}; };
EXPORT_SYMBOL_NS_GPL(ad7608_info, "IIO_AD7606"); EXPORT_SYMBOL_NS_GPL(ad7608_info, "IIO_AD7606");
@ -206,6 +212,7 @@ const struct ad7606_chip_info ad7609_info = {
.oversampling_avail = ad7606_oversampling_avail, .oversampling_avail = ad7606_oversampling_avail,
.oversampling_num = ARRAY_SIZE(ad7606_oversampling_avail), .oversampling_num = ARRAY_SIZE(ad7606_oversampling_avail),
.scale_setup_cb = ad7609_chan_scale_setup, .scale_setup_cb = ad7609_chan_scale_setup,
.offload_storagebits = 32,
}; };
EXPORT_SYMBOL_NS_GPL(ad7609_info, "IIO_AD7606"); EXPORT_SYMBOL_NS_GPL(ad7609_info, "IIO_AD7606");
@ -218,6 +225,7 @@ const struct ad7606_chip_info ad7606c_18_info = {
.oversampling_num = ARRAY_SIZE(ad7606_oversampling_avail), .oversampling_num = ARRAY_SIZE(ad7606_oversampling_avail),
.scale_setup_cb = ad7606c_18bit_chan_scale_setup, .scale_setup_cb = ad7606c_18bit_chan_scale_setup,
.sw_setup_cb = ad7606b_sw_mode_setup, .sw_setup_cb = ad7606b_sw_mode_setup,
.offload_storagebits = 32,
}; };
EXPORT_SYMBOL_NS_GPL(ad7606c_18_info, "IIO_AD7606"); EXPORT_SYMBOL_NS_GPL(ad7606c_18_info, "IIO_AD7606");
@ -232,6 +240,7 @@ const struct ad7606_chip_info ad7616_info = {
.os_req_reset = true, .os_req_reset = true,
.scale_setup_cb = ad7606_16bit_chan_scale_setup, .scale_setup_cb = ad7606_16bit_chan_scale_setup,
.sw_setup_cb = ad7616_sw_mode_setup, .sw_setup_cb = ad7616_sw_mode_setup,
.offload_storagebits = 16,
}; };
EXPORT_SYMBOL_NS_GPL(ad7616_info, "IIO_AD7606"); EXPORT_SYMBOL_NS_GPL(ad7616_info, "IIO_AD7606");
@ -514,7 +523,7 @@ static int ad7606_pwm_set_high(struct ad7606_state *st)
return ret; return ret;
} }
static int ad7606_pwm_set_low(struct ad7606_state *st) int ad7606_pwm_set_low(struct ad7606_state *st)
{ {
struct pwm_state cnvst_pwm_state; struct pwm_state cnvst_pwm_state;
int ret; int ret;
@ -527,8 +536,9 @@ static int ad7606_pwm_set_low(struct ad7606_state *st)
return ret; return ret;
} }
EXPORT_SYMBOL_NS_GPL(ad7606_pwm_set_low, "IIO_AD7606");
static int ad7606_pwm_set_swing(struct ad7606_state *st) int ad7606_pwm_set_swing(struct ad7606_state *st)
{ {
struct pwm_state cnvst_pwm_state; struct pwm_state cnvst_pwm_state;
@ -538,6 +548,7 @@ static int ad7606_pwm_set_swing(struct ad7606_state *st)
return pwm_apply_might_sleep(st->cnvst_pwm, &cnvst_pwm_state); return pwm_apply_might_sleep(st->cnvst_pwm, &cnvst_pwm_state);
} }
EXPORT_SYMBOL_NS_GPL(ad7606_pwm_set_swing, "IIO_AD7606");
static bool ad7606_pwm_is_swinging(struct ad7606_state *st) static bool ad7606_pwm_is_swinging(struct ad7606_state *st)
{ {
@ -626,7 +637,7 @@ static int ad7606_scan_direct(struct iio_dev *indio_dev, unsigned int ch,
* will not happen because IIO_CHAN_INFO_RAW is not supported for the backend. * will not happen because IIO_CHAN_INFO_RAW is not supported for the backend.
* TODO: Add support for reading a single value when the backend is used. * TODO: Add support for reading a single value when the backend is used.
*/ */
if (!st->back) { if (st->trig) {
ret = wait_for_completion_timeout(&st->completion, ret = wait_for_completion_timeout(&st->completion,
msecs_to_jiffies(1000)); msecs_to_jiffies(1000));
if (!ret) { if (!ret) {
@ -634,7 +645,12 @@ static int ad7606_scan_direct(struct iio_dev *indio_dev, unsigned int ch,
goto error_ret; goto error_ret;
} }
} else { } else {
fsleep(1); /*
* If the BUSY interrupt is not available, wait enough time for
* the longest possible conversion (max for the whole family is
* around 350us).
*/
fsleep(400);
} }
ret = ad7606_read_samples(st); ret = ad7606_read_samples(st);
@ -698,10 +714,6 @@ static int ad7606_read_raw(struct iio_dev *indio_dev,
*val = st->oversampling; *val = st->oversampling;
return IIO_VAL_INT; return IIO_VAL_INT;
case IIO_CHAN_INFO_SAMP_FREQ: case IIO_CHAN_INFO_SAMP_FREQ:
/*
* TODO: return the real frequency intead of the requested one once
* pwm_get_state_hw comes upstream.
*/
pwm_get_state(st->cnvst_pwm, &cnvst_pwm_state); pwm_get_state(st->cnvst_pwm, &cnvst_pwm_state);
*val = DIV_ROUND_CLOSEST_ULL(NSEC_PER_SEC, cnvst_pwm_state.period); *val = DIV_ROUND_CLOSEST_ULL(NSEC_PER_SEC, cnvst_pwm_state.period);
return IIO_VAL_INT; return IIO_VAL_INT;
@ -1196,7 +1208,7 @@ static int ad7606_probe_channels(struct iio_dev *indio_dev)
bool slow_bus; bool slow_bus;
int ret, i; int ret, i;
slow_bus = !st->bops->iio_backend_config; slow_bus = !(st->bops->iio_backend_config || st->offload_en);
indio_dev->num_channels = st->chip_info->num_adc_channels; indio_dev->num_channels = st->chip_info->num_adc_channels;
/* Slow buses also get 1 more channel for soft timestamp */ /* Slow buses also get 1 more channel for soft timestamp */
@ -1217,7 +1229,14 @@ static int ad7606_probe_channels(struct iio_dev *indio_dev)
chan->scan_index = i; chan->scan_index = i;
chan->scan_type.sign = 's'; chan->scan_type.sign = 's';
chan->scan_type.realbits = st->chip_info->bits; chan->scan_type.realbits = st->chip_info->bits;
chan->scan_type.storagebits = st->chip_info->bits > 16 ? 32 : 16; /*
* If in SPI offload mode, storagebits are set based
* on the spi-engine hw implementation.
*/
chan->scan_type.storagebits = st->offload_en ?
st->chip_info->offload_storagebits :
(st->chip_info->bits > 16 ? 32 : 16);
chan->scan_type.endianness = IIO_CPU; chan->scan_type.endianness = IIO_CPU;
if (indio_dev->modes & INDIO_DIRECT_MODE) if (indio_dev->modes & INDIO_DIRECT_MODE)
@ -1335,6 +1354,13 @@ int ad7606_probe(struct device *dev, int irq, void __iomem *base_address,
indio_dev->modes = st->bops->iio_backend_config ? 0 : INDIO_DIRECT_MODE; indio_dev->modes = st->bops->iio_backend_config ? 0 : INDIO_DIRECT_MODE;
indio_dev->name = chip_info->name; indio_dev->name = chip_info->name;
/* Using spi-engine with offload support ? */
if (st->bops->offload_config) {
ret = st->bops->offload_config(dev, indio_dev);
if (ret)
return ret;
}
ret = ad7606_probe_channels(indio_dev); ret = ad7606_probe_channels(indio_dev);
if (ret) if (ret)
return ret; return ret;
@ -1350,7 +1376,7 @@ int ad7606_probe(struct device *dev, int irq, void __iomem *base_address,
} }
/* If convst pin is not defined, setup PWM. */ /* If convst pin is not defined, setup PWM. */
if (!st->gpio_convst) { if (!st->gpio_convst || st->offload_en) {
st->cnvst_pwm = devm_pwm_get(dev, NULL); st->cnvst_pwm = devm_pwm_get(dev, NULL);
if (IS_ERR(st->cnvst_pwm)) if (IS_ERR(st->cnvst_pwm))
return PTR_ERR(st->cnvst_pwm); return PTR_ERR(st->cnvst_pwm);
@ -1389,8 +1415,7 @@ int ad7606_probe(struct device *dev, int irq, void __iomem *base_address,
return ret; return ret;
indio_dev->setup_ops = &ad7606_backend_buffer_ops; indio_dev->setup_ops = &ad7606_backend_buffer_ops;
} else { } else if (!st->offload_en) {
/* Reserve the PWM use only for backend (force gpio_convst definition) */ /* Reserve the PWM use only for backend (force gpio_convst definition) */
if (!st->gpio_convst) if (!st->gpio_convst)
return dev_err_probe(dev, -EINVAL, return dev_err_probe(dev, -EINVAL,
@ -1428,7 +1453,8 @@ int ad7606_probe(struct device *dev, int irq, void __iomem *base_address,
st->write_scale = ad7606_write_scale_hw; st->write_scale = ad7606_write_scale_hw;
st->write_os = ad7606_write_os_hw; st->write_os = ad7606_write_os_hw;
if (st->sw_mode_en) { /* Offload needs 1 DOUT line, applying this setting in sw_setup_cb. */
if (st->sw_mode_en || st->offload_en) {
indio_dev->info = &ad7606_info_sw_mode; indio_dev->info = &ad7606_info_sw_mode;
st->chip_info->sw_setup_cb(indio_dev); st->chip_info->sw_setup_cb(indio_dev);
} }

View File

@ -60,6 +60,7 @@ typedef int (*ad7606_sw_setup_cb_t)(struct iio_dev *indio_dev);
* @os_req_reset: some devices require a reset to update oversampling * @os_req_reset: some devices require a reset to update oversampling
* @init_delay_ms: required delay in milliseconds for initialization * @init_delay_ms: required delay in milliseconds for initialization
* after a restart * after a restart
* @offload_storagebits: storage bits used by the offload hw implementation
*/ */
struct ad7606_chip_info { struct ad7606_chip_info {
unsigned int max_samplerate; unsigned int max_samplerate;
@ -72,6 +73,7 @@ struct ad7606_chip_info {
unsigned int oversampling_num; unsigned int oversampling_num;
bool os_req_reset; bool os_req_reset;
unsigned long init_delay_ms; unsigned long init_delay_ms;
u8 offload_storagebits;
}; };
/** /**
@ -118,6 +120,8 @@ struct ad7606_chan_scale {
* @trig: The IIO trigger associated with the device. * @trig: The IIO trigger associated with the device.
* @completion: completion to indicate end of conversion * @completion: completion to indicate end of conversion
* @data: buffer for reading data from the device * @data: buffer for reading data from the device
* @offload_en: SPI offload enabled
* @bus_data: bus-specific variables
* @d16: be16 buffer for reading data from the device * @d16: be16 buffer for reading data from the device
*/ */
struct ad7606_state { struct ad7606_state {
@ -145,6 +149,9 @@ struct ad7606_state {
struct iio_trigger *trig; struct iio_trigger *trig;
struct completion completion; struct completion completion;
bool offload_en;
void *bus_data;
/* /*
* DMA (thus cache coherency maintenance) may require the * DMA (thus cache coherency maintenance) may require the
* transfer buffers to live in their own cache lines. * transfer buffers to live in their own cache lines.
@ -165,6 +172,8 @@ struct ad7606_state {
* @read_block: function pointer for reading blocks of data * @read_block: function pointer for reading blocks of data
* @sw_mode_config: pointer to a function which configured the device * @sw_mode_config: pointer to a function which configured the device
* for software mode * for software mode
* @offload_config: function pointer for configuring offload support,
* where any
* @reg_read: function pointer for reading spi register * @reg_read: function pointer for reading spi register
* @reg_write: function pointer for writing spi register * @reg_write: function pointer for writing spi register
* @update_scan_mode: function pointer for handling the calls to iio_info's * @update_scan_mode: function pointer for handling the calls to iio_info's
@ -174,6 +183,7 @@ struct ad7606_state {
struct ad7606_bus_ops { struct ad7606_bus_ops {
/* more methods added in future? */ /* more methods added in future? */
int (*iio_backend_config)(struct device *dev, struct iio_dev *indio_dev); int (*iio_backend_config)(struct device *dev, struct iio_dev *indio_dev);
int (*offload_config)(struct device *dev, struct iio_dev *indio_dev);
int (*read_block)(struct device *dev, int num, void *data); int (*read_block)(struct device *dev, int num, void *data);
int (*sw_mode_config)(struct iio_dev *indio_dev); int (*sw_mode_config)(struct iio_dev *indio_dev);
int (*reg_read)(struct ad7606_state *st, unsigned int addr); int (*reg_read)(struct ad7606_state *st, unsigned int addr);
@ -199,6 +209,8 @@ int ad7606_probe(struct device *dev, int irq, void __iomem *base_address,
const struct ad7606_bus_ops *bops); const struct ad7606_bus_ops *bops);
int ad7606_reset(struct ad7606_state *st); int ad7606_reset(struct ad7606_state *st);
int ad7606_pwm_set_swing(struct ad7606_state *st);
int ad7606_pwm_set_low(struct ad7606_state *st);
extern const struct ad7606_chip_info ad7605_4_info; extern const struct ad7606_chip_info ad7605_4_info;
extern const struct ad7606_chip_info ad7606_8_info; extern const struct ad7606_chip_info ad7606_8_info;

View File

@ -6,15 +6,31 @@
*/ */
#include <linux/err.h> #include <linux/err.h>
#include <linux/math.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/pwm.h>
#include <linux/spi/offload/consumer.h>
#include <linux/spi/offload/provider.h>
#include <linux/spi/spi.h> #include <linux/spi/spi.h>
#include <linux/types.h> #include <linux/types.h>
#include <linux/units.h>
#include <linux/iio/buffer-dmaengine.h>
#include <linux/iio/iio.h> #include <linux/iio/iio.h>
#include <dt-bindings/iio/adc/adi,ad7606.h>
#include "ad7606.h" #include "ad7606.h"
#define MAX_SPI_FREQ_HZ 23500000 /* VDRIVE above 4.75 V */ #define MAX_SPI_FREQ_HZ 23500000 /* VDRIVE above 4.75 V */
struct spi_bus_data {
struct spi_offload *offload;
struct spi_offload_trigger *offload_trigger;
struct spi_transfer offload_xfer;
struct spi_message offload_msg;
};
static u16 ad7616_spi_rd_wr_cmd(int addr, char is_write_op) static u16 ad7616_spi_rd_wr_cmd(int addr, char is_write_op)
{ {
/* /*
@ -125,19 +141,211 @@ static int ad7606b_sw_mode_config(struct iio_dev *indio_dev)
AD7606_SINGLE_DOUT); AD7606_SINGLE_DOUT);
} }
static const struct spi_offload_config ad7606_spi_offload_config = {
.capability_flags = SPI_OFFLOAD_CAP_TRIGGER |
SPI_OFFLOAD_CAP_RX_STREAM_DMA,
};
static int ad7606_spi_offload_buffer_postenable(struct iio_dev *indio_dev)
{
const struct iio_scan_type *scan_type;
struct ad7606_state *st = iio_priv(indio_dev);
struct spi_bus_data *bus_data = st->bus_data;
struct spi_transfer *xfer = &bus_data->offload_xfer;
struct spi_device *spi = to_spi_device(st->dev);
struct spi_offload_trigger_config config = {
.type = SPI_OFFLOAD_TRIGGER_DATA_READY,
};
int ret;
scan_type = &indio_dev->channels[0].scan_type;
xfer->bits_per_word = scan_type->realbits;
xfer->offload_flags = SPI_OFFLOAD_XFER_RX_STREAM;
/*
* Using SPI offload, storagebits are related to the spi-engine
* hw implementation, can be 16 or 32, so can't be used to compute
* struct spi_transfer.len. Using realbits instead.
*/
xfer->len = (scan_type->realbits > 16 ? 4 : 2) *
st->chip_info->num_adc_channels;
spi_message_init_with_transfers(&bus_data->offload_msg, xfer, 1);
bus_data->offload_msg.offload = bus_data->offload;
ret = spi_optimize_message(spi, &bus_data->offload_msg);
if (ret) {
dev_err(st->dev, "failed to prepare offload, err: %d\n", ret);
return ret;
}
ret = spi_offload_trigger_enable(bus_data->offload,
bus_data->offload_trigger,
&config);
if (ret)
goto err_unoptimize_message;
ret = ad7606_pwm_set_swing(st);
if (ret)
goto err_offload_exit_conversion_mode;
return 0;
err_offload_exit_conversion_mode:
spi_offload_trigger_disable(bus_data->offload,
bus_data->offload_trigger);
err_unoptimize_message:
spi_unoptimize_message(&bus_data->offload_msg);
return ret;
}
static int ad7606_spi_offload_buffer_predisable(struct iio_dev *indio_dev)
{
struct ad7606_state *st = iio_priv(indio_dev);
struct spi_bus_data *bus_data = st->bus_data;
int ret;
ret = ad7606_pwm_set_low(st);
if (ret)
return ret;
spi_offload_trigger_disable(bus_data->offload,
bus_data->offload_trigger);
spi_unoptimize_message(&bus_data->offload_msg);
return 0;
}
static const struct iio_buffer_setup_ops ad7606_offload_buffer_setup_ops = {
.postenable = ad7606_spi_offload_buffer_postenable,
.predisable = ad7606_spi_offload_buffer_predisable,
};
static bool ad7606_spi_offload_trigger_match(
struct spi_offload_trigger *trigger,
enum spi_offload_trigger_type type,
u64 *args, u32 nargs)
{
if (type != SPI_OFFLOAD_TRIGGER_DATA_READY)
return false;
/*
* Requires 1 arg:
* args[0] is the trigger event.
*/
if (nargs != 1 || args[0] != AD7606_TRIGGER_EVENT_BUSY)
return false;
return true;
}
static int ad7606_spi_offload_trigger_request(
struct spi_offload_trigger *trigger,
enum spi_offload_trigger_type type,
u64 *args, u32 nargs)
{
/* Should already be validated by match, but just in case. */
if (nargs != 1)
return -EINVAL;
return 0;
}
static int ad7606_spi_offload_trigger_validate(
struct spi_offload_trigger *trigger,
struct spi_offload_trigger_config *config)
{
if (config->type != SPI_OFFLOAD_TRIGGER_DATA_READY)
return -EINVAL;
return 0;
}
static const struct spi_offload_trigger_ops ad7606_offload_trigger_ops = {
.match = ad7606_spi_offload_trigger_match,
.request = ad7606_spi_offload_trigger_request,
.validate = ad7606_spi_offload_trigger_validate,
};
static int ad7606_spi_offload_probe(struct device *dev,
struct iio_dev *indio_dev)
{
struct ad7606_state *st = iio_priv(indio_dev);
struct spi_device *spi = to_spi_device(dev);
struct spi_bus_data *bus_data;
struct dma_chan *rx_dma;
struct spi_offload_trigger_info trigger_info = {
.fwnode = dev_fwnode(dev),
.ops = &ad7606_offload_trigger_ops,
.priv = st,
};
int ret;
bus_data = devm_kzalloc(dev, sizeof(*bus_data), GFP_KERNEL);
if (!bus_data)
return -ENOMEM;
st->bus_data = bus_data;
bus_data->offload = devm_spi_offload_get(dev, spi,
&ad7606_spi_offload_config);
ret = PTR_ERR_OR_ZERO(bus_data->offload);
if (ret && ret != -ENODEV)
return dev_err_probe(dev, ret, "failed to get SPI offload\n");
/* Allow main ad7606_probe function to continue. */
if (ret == -ENODEV)
return 0;
ret = devm_spi_offload_trigger_register(dev, &trigger_info);
if (ret)
return dev_err_probe(dev, ret,
"failed to register offload trigger\n");
bus_data->offload_trigger = devm_spi_offload_trigger_get(dev,
bus_data->offload, SPI_OFFLOAD_TRIGGER_DATA_READY);
if (IS_ERR(bus_data->offload_trigger))
return dev_err_probe(dev, PTR_ERR(bus_data->offload_trigger),
"failed to get offload trigger\n");
/* TODO: PWM setup should be ok, done for the backend. PWM mutex ? */
rx_dma = devm_spi_offload_rx_stream_request_dma_chan(dev,
bus_data->offload);
if (IS_ERR(rx_dma))
return dev_err_probe(dev, PTR_ERR(rx_dma),
"failed to get offload RX DMA\n");
ret = devm_iio_dmaengine_buffer_setup_with_handle(dev, indio_dev,
rx_dma, IIO_BUFFER_DIRECTION_IN);
if (ret)
return dev_err_probe(dev, ret,
"failed to setup offload RX DMA\n");
/* Use offload ops. */
indio_dev->setup_ops = &ad7606_offload_buffer_setup_ops;
st->offload_en = true;
return 0;
}
static const struct ad7606_bus_ops ad7606_spi_bops = { static const struct ad7606_bus_ops ad7606_spi_bops = {
.offload_config = ad7606_spi_offload_probe,
.read_block = ad7606_spi_read_block, .read_block = ad7606_spi_read_block,
}; };
static const struct ad7606_bus_ops ad7607_spi_bops = { static const struct ad7606_bus_ops ad7607_spi_bops = {
.offload_config = ad7606_spi_offload_probe,
.read_block = ad7606_spi_read_block14to16, .read_block = ad7606_spi_read_block14to16,
}; };
static const struct ad7606_bus_ops ad7608_spi_bops = { static const struct ad7606_bus_ops ad7608_spi_bops = {
.offload_config = ad7606_spi_offload_probe,
.read_block = ad7606_spi_read_block18to32, .read_block = ad7606_spi_read_block18to32,
}; };
static const struct ad7606_bus_ops ad7616_spi_bops = { static const struct ad7606_bus_ops ad7616_spi_bops = {
.offload_config = ad7606_spi_offload_probe,
.read_block = ad7606_spi_read_block, .read_block = ad7606_spi_read_block,
.reg_read = ad7606_spi_reg_read, .reg_read = ad7606_spi_reg_read,
.reg_write = ad7606_spi_reg_write, .reg_write = ad7606_spi_reg_write,
@ -145,6 +353,7 @@ static const struct ad7606_bus_ops ad7616_spi_bops = {
}; };
static const struct ad7606_bus_ops ad7606b_spi_bops = { static const struct ad7606_bus_ops ad7606b_spi_bops = {
.offload_config = ad7606_spi_offload_probe,
.read_block = ad7606_spi_read_block, .read_block = ad7606_spi_read_block,
.reg_read = ad7606_spi_reg_read, .reg_read = ad7606_spi_reg_read,
.reg_write = ad7606_spi_reg_write, .reg_write = ad7606_spi_reg_write,
@ -153,6 +362,7 @@ static const struct ad7606_bus_ops ad7606b_spi_bops = {
}; };
static const struct ad7606_bus_ops ad7606c_18_spi_bops = { static const struct ad7606_bus_ops ad7606c_18_spi_bops = {
.offload_config = ad7606_spi_offload_probe,
.read_block = ad7606_spi_read_block18to32, .read_block = ad7606_spi_read_block18to32,
.reg_read = ad7606_spi_reg_read, .reg_read = ad7606_spi_reg_read,
.reg_write = ad7606_spi_reg_write, .reg_write = ad7606_spi_reg_write,
@ -270,3 +480,4 @@ MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>");
MODULE_DESCRIPTION("Analog Devices AD7606 ADC"); MODULE_DESCRIPTION("Analog Devices AD7606 ADC");
MODULE_LICENSE("GPL v2"); MODULE_LICENSE("GPL v2");
MODULE_IMPORT_NS("IIO_AD7606"); MODULE_IMPORT_NS("IIO_AD7606");
MODULE_IMPORT_NS("IIO_DMAENGINE_BUFFER");