lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <97c283e3bf48ca6425a61363afcdd221e7392a39.camel@gmail.com>
Date: Tue, 17 Dec 2024 13:15:05 +0100
From: Nuno Sá <noname.nuno@...il.com>
To: David Lechner <dlechner@...libre.com>, Mark Brown <broonie@...nel.org>, 
 Jonathan Cameron
	 <jic23@...nel.org>, Rob Herring <robh@...nel.org>, Krzysztof Kozlowski
	 <krzk+dt@...nel.org>, Conor Dooley <conor+dt@...nel.org>, Nuno
 Sá
	 <nuno.sa@...log.com>
Cc: Uwe Kleine-König <ukleinek@...nel.org>, Michael
 Hennerich <Michael.Hennerich@...log.com>, Lars-Peter Clausen
 <lars@...afoo.de>, David Jander	 <david@...tonic.nl>, Martin Sperl
 <kernel@...tin.sperl.org>, 	linux-spi@...r.kernel.org,
 devicetree@...r.kernel.org, 	linux-kernel@...r.kernel.org,
 linux-iio@...r.kernel.org, 	linux-pwm@...r.kernel.org, Jonathan Cameron
 <Jonathan.Cameron@...wei.com>
Subject: Re: [PATCH v6 14/17] iio: adc: ad4695: Add support for SPI offload

On Wed, 2024-12-11 at 14:54 -0600, David Lechner wrote:
> Add support for SPI offload to the ad4695 driver. SPI offload allows
> sampling data at the max sample rate (500kSPS or 1MSPS).
> 
> This is developed and tested against the ADI example FPGA design for
> this family of ADCs [1].
> 
> [1]: http://analogdevicesinc.github.io/hdl/projects/ad469x_fmc/index.html
> 
> Reviewed-by: Jonathan Cameron <Jonathan.Cameron@...wei.com>
> Signed-off-by: David Lechner <dlechner@...libre.com>
> ---
> 

LGTM,

Reviewed-by: Nuno Sa <nuno.sa@...log.com>

> v6 changes:
> * Fixed use of c++ style comments
> * Moved static const struct definition out of probe function
> * Changes bits_per_word to always be 19 for future oversampling
>   compatibility (Trevor is working on implementing oversampling support
>   on top of this patch, so we have high confidence this is the correct
>   thing to do)
> * Fixed wrong xfer->len
> 
> v5 changes:
> * Register SCLK speed handling has been split out into a separate series.
> * Add sampling_frequency_available attribute.
> * Limit max allowed sampling frequency based on chip info.
> * Expand explanations of offload enable/disable ordering requirements.
> * Finish TODO to use macros for phandle arg values.
> * Don't use dev_info() when falling back to non-offload operation.
> * Update to accommodate changes in other patches in this series.
> 
> v4 changes: new patch in v4
> ---
>  drivers/iio/adc/Kconfig  |   1 +
>  drivers/iio/adc/ad4695.c | 445 +++++++++++++++++++++++++++++++++++++++++++++-
> -
>  2 files changed, 429 insertions(+), 17 deletions(-)
> 
> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
> index
> 995b9cacbaa964d26424346120c139858f93cdcd..ec60b64c46e187e2be18ab1f8ca9e6f4f032
> 99f9 100644
> --- a/drivers/iio/adc/Kconfig
> +++ b/drivers/iio/adc/Kconfig
> @@ -52,6 +52,7 @@ config AD4695
>  	tristate "Analog Device AD4695 ADC Driver"
>  	depends on SPI
>  	select IIO_BUFFER
> +	select IIO_BUFFER_DMAENGINE
>  	select IIO_TRIGGERED_BUFFER
>  	select REGMAP
>  	help
> diff --git a/drivers/iio/adc/ad4695.c b/drivers/iio/adc/ad4695.c
> index
> 13cf01d35301be40369571e7dd2aeac1a8148d15..c8cd73d19e869f11999608f61df5724d329b
> 4427 100644
> --- a/drivers/iio/adc/ad4695.c
> +++ b/drivers/iio/adc/ad4695.c
> @@ -19,14 +19,19 @@
>  #include <linux/device.h>
>  #include <linux/err.h>
>  #include <linux/gpio/consumer.h>
> +#include <linux/iio/buffer-dmaengine.h>
>  #include <linux/iio/buffer.h>
>  #include <linux/iio/iio.h>
>  #include <linux/iio/triggered_buffer.h>
>  #include <linux/iio/trigger_consumer.h>
>  #include <linux/minmax.h>
> +#include <linux/mutex.h>
>  #include <linux/property.h>
> +#include <linux/pwm.h>
>  #include <linux/regmap.h>
>  #include <linux/regulator/consumer.h>
> +#include <linux/spi/offload/consumer.h>
> +#include <linux/spi/offload/provider.h>
>  #include <linux/spi/spi.h>
>  #include <linux/units.h>
>  
> @@ -66,6 +71,8 @@
>  #define AD4695_REG_STD_SEQ_CONFIG			0x0024
>  #define AD4695_REG_GPIO_CTRL				0x0026
>  #define AD4695_REG_GP_MODE				0x0027
> +#define   AD4695_REG_GP_MODE_BUSY_GP_SEL		  BIT(5)
> +#define   AD4695_REG_GP_MODE_BUSY_GP_EN			  BIT(1)
>  #define AD4695_REG_TEMP_CTRL				0x0029
>  #define   AD4695_REG_TEMP_CTRL_TEMP_EN			  BIT(0)
>  #define AD4695_REG_CONFIG_IN(n)				(0x0030 |
> (n))
> @@ -124,13 +131,22 @@ struct ad4695_channel_config {
>  
>  struct ad4695_state {
>  	struct spi_device *spi;
> +	struct spi_offload *offload;
> +	struct spi_offload_trigger *offload_trigger;
>  	struct regmap *regmap;
>  	struct regmap *regmap16;
>  	struct gpio_desc *reset_gpio;
> +	/* currently PWM CNV only supported with SPI offload use */
> +	struct pwm_device *cnv_pwm;
> +	/* protects against concurrent use of cnv_pwm */
> +	struct mutex cnv_pwm_lock;
> +	/* offload also requires separate gpio to manually control CNV */
> +	struct gpio_desc *cnv_gpio;
>  	/* voltages channels plus temperature and timestamp */
>  	struct iio_chan_spec iio_chan[AD4695_MAX_CHANNELS + 2];
>  	struct ad4695_channel_config channels_cfg[AD4695_MAX_CHANNELS];
>  	const struct ad4695_chip_info *chip_info;
> +	int sample_freq_range[3];
>  	/* Reference voltage. */
>  	unsigned int vref_mv;
>  	/* Common mode input pin voltage. */
> @@ -355,6 +371,13 @@ static const struct ad4695_chip_info ad4698_chip_info = {
>  	.num_voltage_inputs = 8,
>  };
>  
> +static void ad4695_cnv_manual_trigger(struct ad4695_state *st)
> +{
> +	gpiod_set_value_cansleep(st->cnv_gpio, 1);
> +	ndelay(10);
> +	gpiod_set_value_cansleep(st->cnv_gpio, 0);
> +}
> +
>  /**
>   * ad4695_set_single_cycle_mode - Set the device in single cycle mode
>   * @st: The AD4695 state
> @@ -460,6 +483,17 @@ static int ad4695_exit_conversion_mode(struct
> ad4695_state *st)
>  	 */
>  	st->cnv_cmd2 = AD4695_CMD_EXIT_CNV_MODE << 3;
>  
> +	if (st->cnv_gpio) {
> +		ad4695_cnv_manual_trigger(st);
> +
> +		/*
> +		 * In this case, CNV is not connected to CS, so we don't need
> +		 * the extra CS toggle to trigger the conversion and toggling
> +		 * CS would have no effect.
> +		 */
> +		return spi_sync_transfer(st->spi, &xfers[1], 1);
> +	}
> +
>  	return spi_sync_transfer(st->spi, xfers, ARRAY_SIZE(xfers));
>  }
>  
> @@ -687,6 +721,160 @@ static irqreturn_t ad4695_trigger_handler(int irq, void
> *p)
>  	return IRQ_HANDLED;
>  }
>  
> +static int ad4695_offload_buffer_postenable(struct iio_dev *indio_dev)
> +{
> +	struct ad4695_state *st = iio_priv(indio_dev);
> +	struct spi_offload_trigger_config config = {
> +		.type = SPI_OFFLOAD_TRIGGER_DATA_READY,
> +	};
> +	struct spi_transfer *xfer = &st->buf_read_xfer[0];
> +	struct pwm_state state;
> +	u8 temp_chan_bit = st->chip_info->num_voltage_inputs;
> +	u8 num_slots = 0;
> +	u8 temp_en = 0;
> +	unsigned int bit;
> +	int ret;
> +
> +	iio_for_each_active_channel(indio_dev, bit) {
> +		if (bit == temp_chan_bit) {
> +			temp_en = 1;
> +			continue;
> +		}
> +
> +		ret = regmap_write(st->regmap, AD4695_REG_AS_SLOT(num_slots),
> +				   FIELD_PREP(AD4695_REG_AS_SLOT_INX, bit));
> +		if (ret)
> +			return ret;
> +
> +		num_slots++;
> +	}
> +
> +	/*
> +	 * For non-offload, we could discard data to work around this
> +	 * restriction, but with offload, that is not possible.
> +	 */
> +	if (num_slots < 2) {
> +		dev_err(&st->spi->dev,
> +			"At least two voltage channels must be enabled.\n");
> +		return -EINVAL;
> +	}
> +
> +	ret = regmap_update_bits(st->regmap, AD4695_REG_TEMP_CTRL,
> +				 AD4695_REG_TEMP_CTRL_TEMP_EN,
> +				 FIELD_PREP(AD4695_REG_TEMP_CTRL_TEMP_EN,
> +					    temp_en));
> +	if (ret)
> +		return ret;
> +
> +	/* Each BUSY event means just one sample for one channel is ready. */
> +	memset(xfer, 0, sizeof(*xfer));
> +	xfer->offload_flags = SPI_OFFLOAD_XFER_RX_STREAM;
> +	/* Using 19 bits per word to allow for possible oversampling */
> +	xfer->bits_per_word = 19;
> +	xfer->len = 4;
> +
> +	spi_message_init_with_transfers(&st->buf_read_msg, xfer, 1);
> +	st->buf_read_msg.offload = st->offload;
> +
> +	ret = spi_optimize_message(st->spi, &st->buf_read_msg);
> +	if (ret)
> +		return ret;
> +
> +	/*
> +	 * NB: technically, this is part the SPI offload trigger enable, but
> it
> +	 * doesn't work to call it from the offload trigger enable callback
> +	 * because it requires accessing the SPI bus. Calling it from the
> +	 * trigger enable callback could cause a deadlock.
> +	 */
> +	ret = regmap_set_bits(st->regmap, AD4695_REG_GP_MODE,
> +			      AD4695_REG_GP_MODE_BUSY_GP_EN);
> +	if (ret)
> +		goto err_unoptimize_message;
> +
> +	ret = spi_offload_trigger_enable(st->offload, st->offload_trigger,
> +					 &config);
> +	if (ret)
> +		goto err_disable_busy_output;
> +
> +	ret = ad4695_enter_advanced_sequencer_mode(st, num_slots);
> +	if (ret)
> +		goto err_offload_trigger_disable;
> +
> +	guard(mutex)(&st->cnv_pwm_lock);
> +	pwm_get_state(st->cnv_pwm, &state);
> +	/*
> +	 * PWM subsystem generally rounds down, so requesting 2x minimum high
> +	 * time ensures that we meet the minimum high time in any case.
> +	 */
> +	state.duty_cycle = AD4695_T_CNVH_NS * 2;
> +	ret = pwm_apply_might_sleep(st->cnv_pwm, &state);
> +	if (ret)
> +		goto err_offload_exit_conversion_mode;
> +
> +	return 0;
> +
> +err_offload_exit_conversion_mode:
> +	/*
> +	 * We have to unwind in a different order to avoid triggering
> offload.
> +	 * ad4695_exit_conversion_mode() triggers a conversion, so it has to
> be
> +	 * done after spi_offload_trigger_disable().
> +	 */
> +	spi_offload_trigger_disable(st->offload, st->offload_trigger);
> +	ad4695_exit_conversion_mode(st);
> +	goto err_disable_busy_output;
> +
> +err_offload_trigger_disable:
> +	spi_offload_trigger_disable(st->offload, st->offload_trigger);
> +
> +err_disable_busy_output:
> +	regmap_clear_bits(st->regmap, AD4695_REG_GP_MODE,
> +			  AD4695_REG_GP_MODE_BUSY_GP_EN);
> +
> +err_unoptimize_message:
> +	spi_unoptimize_message(&st->buf_read_msg);
> +
> +	return ret;
> +}
> +
> +static int ad4695_offload_buffer_predisable(struct iio_dev *indio_dev)
> +{
> +	struct ad4695_state *st = iio_priv(indio_dev);
> +	struct pwm_state state;
> +	int ret;
> +
> +	scoped_guard(mutex, &st->cnv_pwm_lock) {
> +		pwm_get_state(st->cnv_pwm, &state);
> +		state.duty_cycle = 0;
> +		ret = pwm_apply_might_sleep(st->cnv_pwm, &state);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	spi_offload_trigger_disable(st->offload, st->offload_trigger);
> +
> +	/*
> +	 * ad4695_exit_conversion_mode() triggers a conversion, so it has to
> be
> +	 * done after spi_offload_trigger_disable().
> +	 */
> +	ret = ad4695_exit_conversion_mode(st);
> +	if (ret)
> +		return ret;
> +
> +	ret = regmap_clear_bits(st->regmap, AD4695_REG_GP_MODE,
> +				AD4695_REG_GP_MODE_BUSY_GP_EN);
> +	if (ret)
> +		return ret;
> +
> +	spi_unoptimize_message(&st->buf_read_msg);
> +
> +	return 0;
> +}
> +
> +static const struct iio_buffer_setup_ops ad4695_offload_buffer_setup_ops = {
> +	.postenable = ad4695_offload_buffer_postenable,
> +	.predisable = ad4695_offload_buffer_predisable,
> +};
> +
>  /**
>   * ad4695_read_one_sample - Read a single sample using single-cycle mode
>   * @st: The AD4695 state
> @@ -718,6 +906,13 @@ static int ad4695_read_one_sample(struct ad4695_state
> *st, unsigned int address)
>  	if (ret)
>  		return ret;
>  
> +	/*
> +	 * If CNV is connected to CS, the previous function will have
> triggered
> +	 * the conversion, otherwise, we do it manually.
> +	 */
> +	if (st->cnv_gpio)
> +		ad4695_cnv_manual_trigger(st);
> +
>  	/*
>  	 * Setting the first channel to the temperature channel isn't
> supported
>  	 * in single-cycle mode, so we have to do an extra conversion to read
> @@ -729,6 +924,13 @@ static int ad4695_read_one_sample(struct ad4695_state
> *st, unsigned int address)
>  		ret = spi_sync_transfer(st->spi, xfers, ARRAY_SIZE(xfers));
>  		if (ret)
>  			return ret;
> +
> +		/*
> +		 * If CNV is connected to CS, the previous function will have
> +		 * triggered the conversion, otherwise, we do it manually.
> +		 */
> +		if (st->cnv_gpio)
> +			ad4695_cnv_manual_trigger(st);
>  	}
>  
>  	/* Then read the result and exit conversion mode. */
> @@ -842,11 +1044,34 @@ static int ad4695_read_raw(struct iio_dev *indio_dev,
>  		default:
>  			return -EINVAL;
>  		}
> +	case IIO_CHAN_INFO_SAMP_FREQ: {
> +		struct pwm_state state;
> +
> +		ret = pwm_get_state_hw(st->cnv_pwm, &state);
> +		if (ret)
> +			return ret;
> +
> +		*val = DIV_ROUND_UP_ULL(NSEC_PER_SEC, state.period);
> +
> +		return IIO_VAL_INT;
> +	}
>  	default:
>  		return -EINVAL;
>  	}
>  }
>  
> +static int ad4695_write_raw_get_fmt(struct iio_dev *indio_dev,
> +				    struct iio_chan_spec const *chan,
> +				    long mask)
> +{
> +	switch (mask) {
> +	case IIO_CHAN_INFO_SAMP_FREQ:
> +		return IIO_VAL_INT;
> +	default:
> +		return IIO_VAL_INT_PLUS_MICRO;
> +	}
> +}
> +
>  static int ad4695_write_raw(struct iio_dev *indio_dev,
>  			    struct iio_chan_spec const *chan,
>  			    int val, int val2, long mask)
> @@ -900,6 +1125,17 @@ static int ad4695_write_raw(struct iio_dev *indio_dev,
>  			default:
>  				return -EINVAL;
>  			}
> +		case IIO_CHAN_INFO_SAMP_FREQ: {
> +			struct pwm_state state;
> +
> +			if (val <= 0 || val > st->chip_info->max_sample_rate)
> +				return -EINVAL;
> +
> +			guard(mutex)(&st->cnv_pwm_lock);
> +			pwm_get_state(st->cnv_pwm, &state);
> +			state.period = DIV_ROUND_UP_ULL(NSEC_PER_SEC, val);
> +			return pwm_apply_might_sleep(st->cnv_pwm, &state);
> +		}
>  		default:
>  			return -EINVAL;
>  		}
> @@ -923,6 +1159,7 @@ static int ad4695_read_avail(struct iio_dev *indio_dev,
>  		 */
>  		S16_MIN / 4, 0, 0, MICRO / 4, S16_MAX / 4, S16_MAX % 4 *
> MICRO / 4
>  	};
> +	struct ad4695_state *st = iio_priv(indio_dev);
>  
>  	switch (mask) {
>  	case IIO_CHAN_INFO_CALIBSCALE:
> @@ -943,6 +1180,10 @@ static int ad4695_read_avail(struct iio_dev *indio_dev,
>  		default:
>  			return -EINVAL;
>  		}
> +	case IIO_CHAN_INFO_SAMP_FREQ:
> +		*vals = st->sample_freq_range;
> +		*type = IIO_VAL_INT;
> +		return IIO_AVAIL_RANGE;
>  	default:
>  		return -EINVAL;
>  	}
> @@ -978,6 +1219,7 @@ static int ad4695_debugfs_reg_access(struct iio_dev
> *indio_dev,
>  
>  static const struct iio_info ad4695_info = {
>  	.read_raw = &ad4695_read_raw,
> +	.write_raw_get_fmt = &ad4695_write_raw_get_fmt,
>  	.write_raw = &ad4695_write_raw,
>  	.read_avail = &ad4695_read_avail,
>  	.debugfs_reg_access = &ad4695_debugfs_reg_access,
> @@ -1091,26 +1333,166 @@ static int ad4695_parse_channel_cfg(struct
> ad4695_state *st)
>  	return 0;
>  }
>  
> +static bool ad4695_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 2 args:
> +	 * args[0] is the trigger event.
> +	 * args[1] is the GPIO pin number.
> +	 */
> +	if (nargs != 2 || args[0] != AD4695_TRIGGER_EVENT_BUSY)
> +		return false;
> +
> +	return true;
> +}
> +
> +static int ad4695_offload_trigger_request(struct spi_offload_trigger
> *trigger,
> +					  enum spi_offload_trigger_type type,
> +					  u64 *args, u32 nargs)
> +{
> +	struct ad4695_state *st = spi_offload_trigger_get_priv(trigger);
> +
> +	/* Should already be validated by match, but just in case. */
> +	if (nargs != 2)
> +		return -EINVAL;
> +
> +	/* DT tells us if BUSY event uses GP0 or GP3. */
> +	if (args[1] == AD4695_TRIGGER_PIN_GP3)
> +		return regmap_set_bits(st->regmap, AD4695_REG_GP_MODE,
> +				       AD4695_REG_GP_MODE_BUSY_GP_SEL);
> +
> +	return regmap_clear_bits(st->regmap, AD4695_REG_GPIO_CTRL,
> +				 AD4695_REG_GP_MODE_BUSY_GP_SEL);
> +}
> +
> +static int
> +ad4695_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;
> +}
> +
> +/*
> + * NB: There are no enable/disable callbacks here due to requiring a SPI
> + * message to enable or disable the BUSY output on the ADC.
> + */
> +static const struct spi_offload_trigger_ops ad4695_offload_trigger_ops = {
> +	.match = ad4695_offload_trigger_match,
> +	.request = ad4695_offload_trigger_request,
> +	.validate = ad4695_offload_trigger_validate,
> +};
> +
> +static void ad4695_pwm_disable(void *pwm)
> +{
> +	pwm_disable(pwm);
> +}
> +
> +static int ad4695_probe_spi_offload(struct iio_dev *indio_dev,
> +				    struct ad4695_state *st)
> +{
> +	struct device *dev = &st->spi->dev;
> +	struct spi_offload_trigger_info trigger_info = {
> +		.fwnode = dev_fwnode(dev),
> +		.ops = &ad4695_offload_trigger_ops,
> +		.priv = st,
> +	};
> +	struct pwm_state pwm_state;
> +	struct dma_chan *rx_dma;
> +	int ret, i;
> +
> +	indio_dev->num_channels = st->chip_info->num_voltage_inputs + 1;
> +	indio_dev->setup_ops = &ad4695_offload_buffer_setup_ops;
> +
> +	if (!st->cnv_gpio)
> +		return dev_err_probe(dev, -ENODEV,
> +				     "CNV GPIO is required for SPI
> offload\n");
> +
> +	ret = devm_spi_offload_trigger_register(dev, &trigger_info);
> +	if (ret)
> +		return dev_err_probe(dev, ret,
> +				     "failed to register offload trigger\n");
> +
> +	st->offload_trigger = devm_spi_offload_trigger_get(dev, st->offload,
> +		SPI_OFFLOAD_TRIGGER_DATA_READY);
> +	if (IS_ERR(st->offload_trigger))
> +		return dev_err_probe(dev, PTR_ERR(st->offload_trigger),
> +				     "failed to get offload trigger\n");
> +
> +	ret = devm_mutex_init(dev, &st->cnv_pwm_lock);
> +	if (ret)
> +		return ret;
> +
> +	st->cnv_pwm = devm_pwm_get(dev, NULL);
> +	if (IS_ERR(st->cnv_pwm))
> +		return dev_err_probe(dev, PTR_ERR(st->cnv_pwm),
> +				     "failed to get CNV PWM\n");
> +
> +	pwm_init_state(st->cnv_pwm, &pwm_state);
> +
> +	/* If firmware didn't provide default rate, use 10kHz (arbitrary). */
> +	if (pwm_state.period == 0)
> +		pwm_state.period = 100 * MILLI;
> +
> +	pwm_state.enabled = true;
> +
> +	ret = pwm_apply_might_sleep(st->cnv_pwm, &pwm_state);
> +	if (ret)
> +		return dev_err_probe(dev, ret, "failed to apply CNV PWM\n");
> +
> +	ret = devm_add_action_or_reset(dev, ad4695_pwm_disable, st->cnv_pwm);
> +	if (ret)
> +		return ret;
> +
> +	rx_dma = devm_spi_offload_rx_stream_request_dma_chan(dev, st-
> >offload);
> +	if (IS_ERR(rx_dma))
> +		return dev_err_probe(dev, PTR_ERR(rx_dma),
> +				     "failed to get offload RX DMA\n");
> +
> +	for (i = 0; i < indio_dev->num_channels; i++) {
> +		struct iio_chan_spec *chan = &st->iio_chan[i];
> +
> +		/*
> +		 * NB: When using offload support, all channels need to have
> the
> +		 * same bits_per_word because they all use the same SPI
> message
> +		 * for reading one sample. In order to prevent breaking
> +		 * userspace in the future when oversampling support is
> added,
> +		 * all channels are set read 19 bits with a shift of 3 to
> mask
> +		 * out the extra bits even though we currently only support
> 16
> +		 * bit samples (oversampling ratio == 1).
> +		 */
> +		chan->scan_type.shift = 3;
> +		chan->scan_type.storagebits = 32;
> +		/* add sample frequency for PWM CNV trigger */
> +		chan->info_mask_separate |= BIT(IIO_CHAN_INFO_SAMP_FREQ);
> +		chan->info_mask_separate_available |=
> BIT(IIO_CHAN_INFO_SAMP_FREQ);
> +	}
> +
> +	return devm_iio_dmaengine_buffer_setup_with_handle(dev, indio_dev,
> +		rx_dma, IIO_BUFFER_DIRECTION_IN);
> +}
> +
> +static const struct spi_offload_config ad4695_spi_offload_config = {
> +	.capability_flags = SPI_OFFLOAD_CAP_TRIGGER |
> +			    SPI_OFFLOAD_CAP_RX_STREAM_DMA,
> +};
> +
>  static int ad4695_probe(struct spi_device *spi)
>  {
>  	struct device *dev = &spi->dev;
>  	struct ad4695_state *st;
>  	struct iio_dev *indio_dev;
> -	struct gpio_desc *cnv_gpio;
>  	bool use_internal_ldo_supply;
>  	bool use_internal_ref_buffer;
>  	int ret;
>  
> -	cnv_gpio = devm_gpiod_get_optional(dev, "cnv", GPIOD_OUT_LOW);
> -	if (IS_ERR(cnv_gpio))
> -		return dev_err_probe(dev, PTR_ERR(cnv_gpio),
> -				     "Failed to get CNV GPIO\n");
> -
> -	/* Driver currently requires CNV pin to be connected to SPI CS */
> -	if (cnv_gpio)
> -		return dev_err_probe(dev, -ENODEV,
> -				     "CNV GPIO is not supported\n");
> -
>  	indio_dev = devm_iio_device_alloc(dev, sizeof(*st));
>  	if (!indio_dev)
>  		return -ENOMEM;
> @@ -1122,6 +1504,10 @@ static int ad4695_probe(struct spi_device *spi)
>  	if (!st->chip_info)
>  		return -EINVAL;
>  
> +	st->sample_freq_range[0] = 1; /* min */
> +	st->sample_freq_range[1] = 1; /* step */
> +	st->sample_freq_range[2] = st->chip_info->max_sample_rate; /* max */
> +
>  	st->regmap = devm_regmap_init(dev, &ad4695_regmap_bus, st,
>  				      &ad4695_regmap_config);
>  	if (IS_ERR(st->regmap))
> @@ -1134,6 +1520,11 @@ static int ad4695_probe(struct spi_device *spi)
>  		return dev_err_probe(dev, PTR_ERR(st->regmap16),
>  				     "Failed to initialize regmap16\n");
>  
> +	st->cnv_gpio = devm_gpiod_get_optional(dev, "cnv", GPIOD_OUT_LOW);
> +	if (IS_ERR(st->cnv_gpio))
> +		return dev_err_probe(dev, PTR_ERR(st->cnv_gpio),
> +				     "Failed to get CNV GPIO\n");
> +
>  	ret = devm_regulator_bulk_get_enable(dev,
>  					    
> ARRAY_SIZE(ad4695_power_supplies),
>  					     ad4695_power_supplies);
> @@ -1261,12 +1652,31 @@ static int ad4695_probe(struct spi_device *spi)
>  	indio_dev->channels = st->iio_chan;
>  	indio_dev->num_channels = st->chip_info->num_voltage_inputs + 2;
>  
> -	ret = devm_iio_triggered_buffer_setup(dev, indio_dev,
> -					      iio_pollfunc_store_time,
> -					      ad4695_trigger_handler,
> -					      &ad4695_buffer_setup_ops);
> -	if (ret)
> -		return ret;
> +	st->offload = devm_spi_offload_get(dev, spi,
> &ad4695_spi_offload_config);
> +	ret = PTR_ERR_OR_ZERO(st->offload);
> +	if (ret && ret != -ENODEV)
> +		return dev_err_probe(dev, ret, "failed to get SPI
> offload\n");
> +
> +	/* If no SPI offload, fall back to low speed usage. */
> +	if (ret == -ENODEV) {
> +		/* Driver currently requires CNV pin to be connected to SPI
> CS */
> +		if (st->cnv_gpio)
> +			return dev_err_probe(dev, -EINVAL,
> +					     "CNV GPIO is not supported\n");
> +
> +		indio_dev->num_channels = st->chip_info->num_voltage_inputs +
> 2;
> +
> +		ret = devm_iio_triggered_buffer_setup(dev, indio_dev,
> +						     
> iio_pollfunc_store_time,
> +						      ad4695_trigger_handler,
> +						     
> &ad4695_buffer_setup_ops);
> +		if (ret)
> +			return ret;
> +	} else {
> +		ret = ad4695_probe_spi_offload(indio_dev, st);
> +		if (ret)
> +			return ret;
> +	}
>  
>  	return devm_iio_device_register(dev, indio_dev);
>  }
> @@ -1303,3 +1713,4 @@ MODULE_AUTHOR("Ramona Gradinariu
> <ramona.gradinariu@...log.com>");
>  MODULE_AUTHOR("David Lechner <dlechner@...libre.com>");
>  MODULE_DESCRIPTION("Analog Devices AD4695 ADC driver");
>  MODULE_LICENSE("GPL");
> +MODULE_IMPORT_NS("IIO_DMAENGINE_BUFFER");
> 


Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ