From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mx0a-00128a01.pphosted.com (mx0a-00128a01.pphosted.com [148.163.135.77]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 511064CB8C2; Tue, 15 Sep 2026 19:16:51 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=148.163.135.77 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789499813; cv=none; b=PI3Em0qJLkuX0u6GgsRyeUYCYQz9y/qTiYdvyJd2KVccTBfzYKQbbMSVXa/2ddVEmp4JTznvkY4JQaUwawBEVr5x7r94lZv/HI6n4TgfY4oQUmPAM7hTQ5PENhAekwS0B863q5UF5PIIQvWuTOaMadXw7MZwqkbBVf2gzWG81x8= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789499813; c=relaxed/simple; bh=7DGHB8www0S6yUjbkae3NR2/FhPGNM2c/vpMokH+M1I=; h=From:To:CC:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=I+A9THgsCM4IL2dMBsg9ySvZyiyXSWu+dy66xmS124NkJhw6YkMODkEEgs+gTmfsHEKXBWo9W7Laa1hMhX0dmpUorMWliT9KVVDE+IWZnltCzGIHgCOvdIKuQ7+u5LvjvhFRSHRw8InFfwDNiKZQJIK1qCzNgIqgHEzFpXH9nG0= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=analog.com; spf=pass smtp.mailfrom=analog.com; dkim=pass (2048-bit key) header.d=analog.com header.i=@analog.com header.b=Hm/d0cDh; arc=none smtp.client-ip=148.163.135.77 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=analog.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=analog.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=analog.com header.i=@analog.com header.b="Hm/d0cDh" Received: from pps.filterd (m0167089.ppops.net [127.0.0.1]) by mx0a-00128a01.pphosted.com (8.18.1.11/8.18.1.11) with ESMTP id 68FIsPPZ1279413; Tue, 15 Sep 2026 15:16:45 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=analog.com; h=cc :content-transfer-encoding:content-type:date:from:in-reply-to :message-id:mime-version:references:subject:to; s=DKIM; bh=l5LDy adkPFDjPPJ9x0cXLDmeRe0eNh9IDVjITYR3z9M=; b=Hm/d0cDhp85bvnz8xfGT0 g9lfEvBvgKrtRJW0+CUMtovmjCRTRzD8nQN9rlx2k8yXWTINY7RxxxSWwZRkxurt Ftateql1KAWAZ/Gw8Raj/FZ1C8k/RAz6UCn1R9kZJ0By1dyIsf8KBSRJxU6r4sVA sU2pqO2hlN9yisALiWqWnqr7FWvRDeMfGnPklvzWL71jcNsUD62SNaonvjgn6hU4 w0/d+N1evk/odfqzsZ93Jy1PIOQRBYDNGiysb6UHPZ7sx7gDEEEWOhvGNQo46dHK uO2RunyuVDED26vR9lCdZKHX/qSJhbGiMOkf+KSScWuW0yZevlYRDS0daRtNLyGG Q== Received: from nwd2mta4.analog.com ([137.71.173.58]) by mx0a-00128a01.pphosted.com (PPS) with ESMTPS id 4gq98c0uk0-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Tue, 15 Sep 2026 15:16:45 -0400 (EDT) Received: from ASHBMBX8.ad.analog.com (ASHBMBX8.ad.analog.com [10.64.17.5]) by nwd2mta4.analog.com (8.14.7/8.14.7) with ESMTP id 68FJGigV019664 (version=TLSv1/SSLv3 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=FAIL); Tue, 15 Sep 2026 15:16:44 -0400 Received: from ASHBMBX9.ad.analog.com (10.64.17.10) by ASHBMBX8.ad.analog.com (10.64.17.5) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.1748.37; Tue, 15 Sep 2026 15:16:43 -0400 Received: from zeus.spd.analog.com (10.66.68.11) by ashbmbx9.ad.analog.com (10.64.17.10) with Microsoft SMTP Server id 15.2.1748.37 via Frontend Transport; Tue, 15 Sep 2026 15:16:43 -0400 Received: from work.maxim-ic.internal ([10.66.6.190]) by zeus.spd.analog.com (8.15.1/8.15.1) with ESMTP id 68FJGUOO028234; Tue, 15 Sep 2026 15:16:32 -0400 From: Marcelo Schmitt To: , , , CC: , , , , , , , , , , Subject: [PATCH v2 14/15] iio: adc: ad4134: Support high-speed data capture Date: Tue, 15 Sep 2026 16:16:24 -0300 Message-ID: <0bf6ca5c7ac264b90429efb2009574db0d0e3315.1789494473.git.marcelo.schmitt@analog.com> X-Mailer: git-send-email 2.39.2 In-Reply-To: References: Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Content-Type: text/plain X-ADIRuleOP-NewSCL: Rule Triggered X-Proofpoint-Spam-Info: AW1haW4tMjYwOTE1MDI4MCBTYWx0ZWRfXwB3TgsuqP94N YdrsObI/nTsEtJWh90BCIge+0acO0Uqn3trm/qjVHfo00O99P57/iBnqpfqDe6LBJIdSs0+taJV omTI1bl0aQOwJE/eKxVyp7Jcn24BjqJFbz6xWzirjIR/uPbcGPTc X-Proofpoint-Spam-Details-Enc: AW1haW4tMjYwOTE1MDI4MCBTYWx0ZWRfX5IY4A7M+n7MN pO82TxDw/FuZdQ04WsZg2zk6fyvghnpExE8qqCeUWNQVQmFkYth+5nw4u0fW4oWVNKZp0RmsMKL n8y8ySO8fhJocoGOVmorfoycbDfi5mJgD3ngeOBqJY2LeozkLcClQBd4JWMhT6zFLAE7uH3PSU+ mlk3LIZ8K7Rbf5/UEtv6S6ZA1PLzM/aHIOYaj90u4sgw7p4kszfSEhoCc3Bg0vlba4Obp0tE7fJ VB598CmjAEMD9xJzoGHkrEl1wmns0dtqm0ILj/9VNdhp+hiZ6FAKfRaVZF6UWFEo+Zt4n2CNR11 MSktrRbZwMTXp5DawWvbKyocJoxD9c4B40o+GbFKjqbOuKj2mxVOz3vV6FuJSOPofp8LhAXMiWk SiTxVhH+l0iHmAJY5guNgcFG+belCUNE1FgQ6MLLCeZRpDRBpcYdgTNf9oxkOm7TeGrd6PLB2it V8L+cS9bXf6sBhfvM+Q== X-Proofpoint-GUID: caew0NH1Tw30GNWkyHXlBFzzuZJMaHxJ X-Proofpoint-ORIG-GUID: caew0NH1Tw30GNWkyHXlBFzzuZJMaHxJ X-Authority-Analysis: v=2.4 cv=KKvPn1Fo c=1 sm=1 tr=0 ts=6aa9999d cx=c_pps a=3WNzaoukacrqR9RwcOSAdA==:117 a=3WNzaoukacrqR9RwcOSAdA==:17 a=VdqzKS8jKosA:10 a=VkNPw1HP01LnGYTKEx00:22 a=0sLvza09kfJOxVLZPwjg:22 a=Z0pTeXoby7EwIRygza74:22 a=gAnH3GRIAAAA:8 a=1pGq5-J5k6OOzPly9DYA:9 X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.293,Aquarius:18.0.1176,Hydra:6.1.134,FMLib:17.12.100.49 definitions=2026-09-15_05,2026-09-15_02,2025-10-01_01 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 malwarescore=0 clxscore=1015 priorityscore=1501 impostorscore=0 phishscore=0 bulkscore=0 lowpriorityscore=0 adultscore=0 spamscore=0 suspectscore=0 classifier=typeunknown authscore=0 authtc= authcc= route=outbound adjust=0 reason=mlx scancount=1 engine=8.22.0-2609040000 definitions=main-2609150280 Make use of SPI transfer offloading to speed up data capture, enabling data acquisition at faster sample rates (up to 1.496 MSPS). Signed-off-by: Marcelo Schmitt --- Change log v1 -> v2: - Error out on min I/O + offload config (currently not being supported). - Improved PWM setup readability, update allowed scan_mask codestyle. - Preserved triggered buffer setup callbacks. - Used pahole to reduce memory holes in ad4134_state struct. We can't drop target_ns and use odr_wf.duty_length_ns directly because pwm_..._waveform...() may round waveform parameters. Same reasoning applies to offload_offset_ns. drivers/iio/adc/Kconfig | 5 + drivers/iio/adc/ad4134.c | 387 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 383 insertions(+), 9 deletions(-) diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index 3b0216e75678..12ef9a053f04 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -123,9 +123,14 @@ config AD4130 config AD4134 tristate "Analog Devices AD4134 ADC Driver" depends on SPI + depends on PWM select REGMAP_SPI + select SPI_OFFLOAD + select SPI_OFFLOAD_TRIGGER_PWM select CRC8 select IIO_BUFFER + select IIO_BUFFER_DMA + select IIO_BUFFER_DMAENGINE select IIO_TRIGGERED_BUFFER select MULTIPLEXER help diff --git a/drivers/iio/adc/ad4134.c b/drivers/iio/adc/ad4134.c index cc6bc325f6ee..94d979c61501 100644 --- a/drivers/iio/adc/ad4134.c +++ b/drivers/iio/adc/ad4134.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -19,9 +20,12 @@ #include #include #include +#include #include #include #include +#include +#include #include #include #include @@ -29,11 +33,16 @@ #include #include +#include +#include #include #include #include #define AD4134_RESET_TIME_US (10 * USEC_PER_SEC) +#define AD4134_DCLK_RISING_OFFSET_NS 8 +#define AD4134_MIN_ODR_FREQ_HZ 10 +#define AD4134_MAX_ODR_FREQ_HZ (1496 * HZ_PER_KHZ) #define AD4134_REG_READ_MASK BIT(7) #define AD4134_SPI_MAX_XFER_LEN 3 @@ -64,6 +73,7 @@ #define AD4134_DIG_IF_CFG_REG 0x12 #define AD4134_DIF_IF_CFG_FORMAT_MASK GENMASK(1, 0) #define AD4134_DATA_FORMAT_SINGLE_CH_MODE 0x0 +#define AD4134_DATA_FORMAT_QUAD_CH_PARALLEL 0x2 #define AD4134_PW_DOWN_CTRL_REG 0x13 #define AD4134_DEVICE_STATUS_REG 0x15 @@ -116,6 +126,10 @@ static const char * const ad4134_filt_names[] = { [AD4134_SINC3_REJ60] = "sinc3+rej60", }; +static const int ad4134_max_samp_freq_range_Hz[3] = { + AD4134_MIN_ODR_FREQ_HZ, 1, AD4134_MAX_ODR_FREQ_HZ, +}; + static int ad4134_get_filter_type(struct iio_dev *indio_dev, struct iio_chan_spec const *chan); @@ -161,6 +175,39 @@ static const struct iio_chan_spec ad4134_chan_set[] = { IIO_CHAN_SOFT_TIMESTAMP(4), }; +/* + * Hardcoded 32-bit storagebits and CPU endianness because the currently + * available HDL only supports that configuration. + */ +#define AD4134_OFFLOAD_CHANNEL(_index) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = (_index), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .ext_info = ad4134_filter_type_ext_info, \ + .scan_index = (_index), \ + .scan_type = { \ + .format = IIO_SCAN_FORMAT_SIGNED_INT, \ + .realbits = AD4134_CHAN_PRECISION_BITS, \ + .storagebits = 32, \ + .endianness = IIO_CPU, \ + }, \ +} + +/* + * It's not possible for software to record when offloaded SPI transfers run so + * no additional timestamp channel is added. + */ +static const struct iio_chan_spec ad4134_offload_chan_set[] = { + AD4134_OFFLOAD_CHANNEL(0), + AD4134_OFFLOAD_CHANNEL(1), + AD4134_OFFLOAD_CHANNEL(2), + AD4134_OFFLOAD_CHANNEL(3), +}; + struct ad4134_state { struct spi_device *spi; struct regmap *regmap; @@ -178,6 +225,14 @@ struct ad4134_state { struct mux_state *mux_st[2]; /* For external multiplexer control */ enum ad4134_spi_mode spi_mode; int refin_mv; + struct spi_message msg; + struct spi_transfer xfers; + struct spi_offload_trigger_config offload_trigger_config; + struct spi_offload_trigger *offload_trigger; + struct spi_offload *offload; + struct pwm_waveform odr_wf; + struct pwm_device *odr_pwm; + unsigned int odr_hz; bool crc_en; /* * DMA (thus cache coherency maintenance) requires the transfer buffers @@ -464,6 +519,91 @@ static const struct regmap_config ad4134_regmap_config = { .max_register = AD4134_CH_VREG(ARRAY_SIZE(ad4134_chan_set)), }; +static int ad4134_update_conversion_rate(struct ad4134_state *st, + unsigned int freq_Hz) +{ + struct spi_offload_trigger_config config = st->offload_trigger_config; + struct pwm_waveform odr_wf = { }; + u64 offload_period_ns; + u64 offload_offset_ns; + u64 odr_high_time_ns; + unsigned int count; + u64 target_ns; + int ret; + + if (!in_range(freq_Hz, AD4134_MIN_ODR_FREQ_HZ, AD4134_MAX_ODR_FREQ_HZ)) + return -ERANGE; + + odr_wf.period_length_ns = DIV_ROUND_UP_ULL(NSEC_PER_SEC, freq_Hz); + /* + * Set the PWM duty cycle to keep ODR high for at least minimum required + * time. If the rounded PWM's value is less than the minimum required, + * increase the target value by 10 and attempt to round the waveform + * again, until the minimum (or try count limit) is reached. + */ + odr_high_time_ns = div64_ul(6ULL * NSEC_PER_SEC, st->sys_clk_hz); + target_ns = 0; + count = 100; + do { + target_ns += 10; /* Increment by PWM duty cycle period */ + odr_wf.duty_length_ns = target_ns; + ret = pwm_round_waveform_might_sleep(st->odr_pwm, &odr_wf); + if (ret) + return ret; + } while (count-- && odr_wf.duty_length_ns < odr_high_time_ns); + + /* Check the minimum ODR high time is met */ + if (odr_wf.duty_length_ns < odr_high_time_ns) + return -EDOM; + + if (odr_wf.period_length_ns < 2 * odr_high_time_ns) + return -EDOM; + + /* + * Configure SPI offload PWM trigger. + * For gated DCLK, the minimum required time between ODR rising edge + * and DCLK rising edge is the sum of ODR high time and ODR falling + * edge to DCLK rising edge time. Delay the offload trigger for at least + * that amount of time so the ADC sample data will be available when the + * SPI transfer begin. + * + * Use the same period as ODR PWM to avoid timing issues. + * Convert back from period to frequency for the SPI offload API. + */ + offload_period_ns = odr_wf.period_length_ns; + config.periodic.frequency_hz = DIV_ROUND_UP_ULL(HZ_PER_GHZ, offload_period_ns); + offload_offset_ns = odr_high_time_ns + AD4134_DCLK_RISING_OFFSET_NS; + count = 100; + do { + config.periodic.offset_ns = offload_offset_ns; + ret = spi_offload_trigger_validate(st->offload_trigger, &config); + if (ret) + return ret; + + offload_offset_ns += 10; + } while (count-- && config.periodic.offset_ns < odr_high_time_ns + + AD4134_DCLK_RISING_OFFSET_NS); + + /* Check the minimum ODR to DCLK delay is met */ + if (config.periodic.offset_ns < odr_high_time_ns + AD4134_DCLK_RISING_OFFSET_NS) + return -EDOM; + + /* Check the PWM periods remain the same */ + offload_period_ns = DIV_ROUND_UP_ULL(HZ_PER_GHZ, config.periodic.frequency_hz); + if (odr_wf.period_length_ns != offload_period_ns) + return -EDOM; + + ret = pwm_set_waveform_might_sleep(st->odr_pwm, &odr_wf, false); + if (ret) + return ret; + + st->offload_trigger_config = config; + st->odr_wf = odr_wf; + st->odr_hz = DIV_ROUND_UP_ULL(NSEC_PER_SEC, odr_wf.period_length_ns); + + return 0; +} + static irqreturn_t ad4134_trigger_handler(int irq, void *p) { struct iio_poll_func *pf = p; @@ -549,6 +689,44 @@ static int ad4134_read_raw(struct iio_dev *indio_dev, *val2 = AD4134_CHAN_PRECISION_BITS - 1; return IIO_VAL_FRACTIONAL_LOG2; + case IIO_CHAN_INFO_SAMP_FREQ: + *val = st->odr_hz; + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + +static int ad4134_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, int *type, int *length, + long info) +{ + switch (info) { + case IIO_CHAN_INFO_SAMP_FREQ: + *vals = (int *)ad4134_max_samp_freq_range_Hz; + *type = IIO_VAL_INT; + return IIO_AVAIL_RANGE; + default: + return -EINVAL; + } +} + +static int ad4134_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct ad4134_state *st = iio_priv(indio_dev); + + IIO_DEV_ACQUIRE_DIRECT_MODE(indio_dev, claim); + if (IIO_DEV_ACQUIRE_FAILED(claim)) + return -EBUSY; + + guard(mutex)(&st->lock); + + switch (mask) { + case IIO_CHAN_INFO_SAMP_FREQ: + return ad4134_update_conversion_rate(st, val); default: return -EINVAL; } @@ -600,6 +778,176 @@ static const struct iio_info ad4134_info = { .debugfs_reg_access = ad4134_debugfs_reg_access, }; +static const struct iio_info ad4134_offload_info = { + .read_raw = ad4134_read_raw, + .read_avail = ad4134_read_avail, + .write_raw = ad4134_write_raw, + .debugfs_reg_access = ad4134_debugfs_reg_access, +}; + +static void ad4134_prepare_offload_msg(struct iio_dev *indio_dev) +{ + struct ad4134_state *st = iio_priv(indio_dev); + unsigned int base_len = roundup_pow_of_two(BITS_TO_BYTES(AD4134_CHAN_PRECISION_BITS)); + unsigned int bpw = AD4134_CHAN_PRECISION_BITS; + + st->xfers.cs_off = 1; + st->xfers.bits_per_word = bpw; + st->xfers.len = base_len * st->spi->num_rx_lanes; + if (st->spi->num_rx_lanes > 1) + st->xfers.multi_lane_mode = SPI_MULTI_LANE_MODE_STRIPE; + + st->xfers.offload_flags = SPI_OFFLOAD_XFER_RX_STREAM; + + spi_message_init_with_transfers(&st->msg, &st->xfers, 1); +} + +static int ad4134_offload_buffer_postenable(struct iio_dev *indio_dev) +{ + struct ad4134_state *st = iio_priv(indio_dev); + int ret, access_ret; + + if (st->spi_mode == AD4134_SPI_MODE_4_WIRE) { + ret = ad4134_set_sample_access(st); + if (ret) + return ret; + } + + ad4134_prepare_offload_msg(indio_dev); + st->msg.offload = st->offload; + ret = spi_optimize_message(st->spi, &st->msg); + if (ret) + goto out_set_register_input; + + ret = spi_offload_trigger_enable(st->offload, st->offload_trigger, + &st->offload_trigger_config); + if (ret) + goto out_unoptimize; + + return 0; + +out_unoptimize: + spi_unoptimize_message(&st->msg); + +out_set_register_input: + access_ret = ad4134_set_register_access(st); + if (access_ret) + dev_err(&st->spi->dev, "reg input select error: %d\n", access_ret); + + return ret; +} + +static int ad4134_offload_buffer_predisable(struct iio_dev *indio_dev) +{ + struct ad4134_state *st = iio_priv(indio_dev); + int ret = 0; + + spi_offload_trigger_disable(st->offload, st->offload_trigger); + + if (st->spi_mode == AD4134_SPI_MODE_4_WIRE) + ret = ad4134_set_register_access(st); + + spi_unoptimize_message(&st->msg); + + return ret; +} + +static const struct iio_buffer_setup_ops ad4134_offload_buffer_setup_ops = { + .postenable = &ad4134_offload_buffer_postenable, + .predisable = &ad4134_offload_buffer_predisable, +}; + +static int ad4134_pwm_get(struct ad4134_state *st) +{ + struct device *dev = &st->spi->dev; + + st->odr_pwm = devm_pwm_get(dev, NULL); + if (IS_ERR(st->odr_pwm)) + return dev_err_probe(dev, PTR_ERR(st->odr_pwm), + "failed to get ODR PWM\n"); + + return 0; +} + +static const struct spi_offload_config ad4134_offload_config = { + .capability_flags = SPI_OFFLOAD_CAP_TRIGGER | + SPI_OFFLOAD_CAP_RX_STREAM_DMA, +}; + +static int ad4134_offload_buffer_setup(struct iio_dev *indio_dev, struct spi_device *spi) +{ + struct ad4134_state *st = iio_priv(indio_dev); + struct device *dev = &spi->dev; + struct dma_chan *rx_dma; + + st->offload_trigger = devm_spi_offload_trigger_get(dev, st->offload, + SPI_OFFLOAD_TRIGGER_PERIODIC); + if (IS_ERR(st->offload_trigger)) + return dev_err_probe(dev, PTR_ERR(st->offload_trigger), + "failed to get offload trigger\n"); + + st->offload_trigger_config.type = SPI_OFFLOAD_TRIGGER_PERIODIC; + + 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"); + + return devm_iio_dmaengine_buffer_setup_with_handle(dev, indio_dev, rx_dma, + IIO_BUFFER_DIRECTION_IN); +} + +/* The chip converts and outputs all 4 channels on each sample request */ +static const unsigned long ad4134_scan_masks[] = { + BIT(3) | BIT(2) | BIT(1) | BIT(0), + 0 +}; + +static int ad4134_offload_setup(struct iio_dev *indio_dev, struct ad4134_state *st) +{ + struct device *dev = &st->spi->dev; + int ret; + + ret = ad4134_pwm_get(st); + if (ret) + return ret; + + ret = ad4134_offload_buffer_setup(indio_dev, st->spi); + if (ret) + return ret; + + /* + * Start with a sampling rate slower than 374 kSPS because that is the + * maximum rate supported with wideband filter (default on power up). + */ + st->odr_hz = 250 * HZ_PER_KHZ; + ret = ad4134_update_conversion_rate(st, st->odr_hz); + if (ret) + return dev_err_probe(dev, ret, "failed to set sampling freq\n"); + + indio_dev->setup_ops = &ad4134_offload_buffer_setup_ops; + indio_dev->available_scan_masks = ad4134_scan_masks; + + switch (st->spi->num_rx_lanes) { + case 1: + return regmap_update_bits(st->regmap, AD4134_DIG_IF_CFG_REG, + AD4134_DIF_IF_CFG_FORMAT_MASK, + FIELD_PREP(AD4134_DIF_IF_CFG_FORMAT_MASK, + AD4134_DATA_FORMAT_SINGLE_CH_MODE)); + case 4: + return regmap_update_bits(st->regmap, AD4134_DIG_IF_CFG_REG, + AD4134_DIF_IF_CFG_FORMAT_MASK, + FIELD_PREP(AD4134_DIF_IF_CFG_FORMAT_MASK, + AD4134_DATA_FORMAT_QUAD_CH_PARALLEL)); + default: + return dev_err_probe(dev, -EINVAL, + "unsupported number of spi-rx-bus-width elements: %d\n", + st->spi->num_rx_lanes); + } + + return 0; +} + static const char * const ad4143_required_regulators[] = { "avdd5", "dvdd5", "iovdd", }; @@ -693,10 +1041,7 @@ static int ad4134_probe(struct spi_device *spi) st->spi = spi; indio_dev->name = "ad4134"; - indio_dev->channels = ad4134_chan_set; - indio_dev->num_channels = ARRAY_SIZE(ad4134_chan_set); indio_dev->modes = INDIO_DIRECT_MODE; - indio_dev->info = &ad4134_info; ret = ad4134_regulator_setup(st); if (ret) @@ -780,12 +1125,35 @@ static int ad4134_probe(struct spi_device *spi) indio_dev->setup_ops = &ad4134_buffer_setup_ops; } - ret = devm_iio_triggered_buffer_setup(dev, indio_dev, - iio_pollfunc_store_time, - ad4134_trigger_handler, - NULL); - if (ret) - return ret; + st->offload = devm_spi_offload_get(dev, spi, &ad4134_offload_config); + ret = PTR_ERR_OR_ZERO(st->offload); + /* Fall back to low speed usage when no SPI offload is available. */ + if (ret == -ENODEV) { + indio_dev->info = &ad4134_info; + indio_dev->channels = ad4134_chan_set; + indio_dev->num_channels = ARRAY_SIZE(ad4134_chan_set); + ret = devm_iio_triggered_buffer_setup(dev, indio_dev, + iio_pollfunc_store_time, + ad4134_trigger_handler, + st->spi_mode != AD4134_SPI_MODE_NO_CS ? + &ad4134_buffer_setup_ops : NULL); + if (ret) + return ret; + } else if (ret) { + return dev_err_probe(dev, ret, "failed to get offload\n"); + } else { + if (st->spi_mode == AD4134_SPI_MODE_NO_CS) + return dev_err_probe(dev, -EPROTONOSUPPORT, + "Unsupported min I/O + offload config\n"); + + indio_dev->info = &ad4134_offload_info; + indio_dev->channels = ad4134_offload_chan_set; + indio_dev->num_channels = ARRAY_SIZE(ad4134_offload_chan_set); + ret = ad4134_offload_setup(indio_dev, st); + if (ret) + return dev_err_probe(dev, ret, + "failed to setup SPI offload\n"); + } /* Bump precision to 24-bit */ ret = regmap_update_bits(st->regmap, AD4134_DATA_PACKET_CONFIG_REG, @@ -831,3 +1199,4 @@ module_spi_driver(ad4134_driver); MODULE_AUTHOR("Marcelo Schmitt "); MODULE_DESCRIPTION("Analog Devices AD4134 SPI driver"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("IIO_DMAENGINE_BUFFER"); -- 2.53.0