From nobody Thu Apr 9 23:26:08 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (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 7A0F73E0C7D; Thu, 9 Apr 2026 15:28:29 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1775748509; cv=none; b=OSf/IBujbVCF4qcZZWIMFXqTCm+Z1jxmpaPeG6r42Pay4sv0uTBHkPB0+Zl+kIaeNO+ZmsAAtR7TZWaUMLZaIIVZx9mLNYfbnWW5/cvZ4mmBrwnV21biUq4vZMvBsTVvuTp+ojap7iY3BPuf8Cn7NEjDiKbyao+zXnpHSsMaxag= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1775748509; c=relaxed/simple; bh=KsblKUkQGH4qzMaiWWjhoWft+PHq8/XzwCcpm7GCJu0=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=PMNkAWTVMRGwzZoCUxZKKpIyo76KpkCIOzOP3GRsFdgXM1sBi54KV9Tzc1/DdjWMqdzJUhy1vTG4LtsfM/tzrdODdKazl1DucHtogpP6Et0r/HD8SRs/fL/RDj6NMet5TnFYCE2QkzG4aMpC9p0Cnut5BDsyFtzp8YAZBbmLegs= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=HuaXksia; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="HuaXksia" Received: by smtp.kernel.org (Postfix) with ESMTPS id 510B6C2BCC9; Thu, 9 Apr 2026 15:28:29 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1775748509; bh=KsblKUkQGH4qzMaiWWjhoWft+PHq8/XzwCcpm7GCJu0=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From; b=HuaXksiaEkeUTtpQlqyKpcpokZ6jE3zuvXFUZWReOTEA6SE/lq6/0lrBfHsfPmgkl 5qh09hJmpHANYGJmaIx8i/4mwX09BHYbziX37xi/ouC/Pibl5RgpeTVfx7cz9dHDYN QTH69qGk9hsPhv2UUciNBalHFx45WOzhmTPNOmjtQ76wmHzm1/KJXfbeZiPgfsBw3F dkZf3zJ7QFPQq6ePiQvPOqGztlLVOTkAbow+lTq2W9RdEX9zt+WR5ywtmzqF4PxqBS 325TrTMv1kv5Rxc6tZxYTcmDUj/ne4zB6Dk9JbD564JCfp8Xd0qs5cK8z2xbxcn401 PGwZKbyN6fR7A== Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id 4779AF31E28; Thu, 9 Apr 2026 15:28:29 +0000 (UTC) From: Radu Sabau via B4 Relay Date: Thu, 09 Apr 2026 18:28:26 +0300 Subject: [PATCH v7 5/6] iio: adc: ad4691: add oversampling support Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260409-ad4692-multichannel-sar-adc-driver-v7-5-be375d4df2c5@analog.com> References: <20260409-ad4692-multichannel-sar-adc-driver-v7-0-be375d4df2c5@analog.com> In-Reply-To: <20260409-ad4692-multichannel-sar-adc-driver-v7-0-be375d4df2c5@analog.com> To: Lars-Peter Clausen , Michael Hennerich , Jonathan Cameron , David Lechner , =?utf-8?q?Nuno_S=C3=A1?= , Andy Shevchenko , Rob Herring , Krzysztof Kozlowski , Conor Dooley , =?utf-8?q?Uwe_Kleine-K=C3=B6nig?= , Liam Girdwood , Mark Brown , Linus Walleij , Bartosz Golaszewski , Philipp Zabel , Jonathan Corbet , Shuah Khan Cc: linux-iio@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-pwm@vger.kernel.org, linux-gpio@vger.kernel.org, linux-doc@vger.kernel.org, Radu Sabau X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=ed25519-sha256; t=1775748505; l=9894; i=radu.sabau@analog.com; s=20260220; h=from:subject:message-id; bh=eB6uvSJZMwxlszpLMEXiVdwqo2+1DAbz0m1ETwwp1E8=; b=Q9j9fNUH+gyIVtPhmTXEJpOmAQAIZwPDs3DvcRhKK3vjvmaqorlbeSfKFkBOsWJcG4pAwxb5U ENfshks7CfnBUWaVta7dRxuHpc0ypqzrtQRNwA4SpgDL2BydQYuRE81 X-Developer-Key: i=radu.sabau@analog.com; a=ed25519; pk=lDPQHgn9jTdt0vo58Na9lLxLaE2mb330if71Cn+EvFU= X-Endpoint-Received: by B4 Relay for radu.sabau@analog.com/20260220 with auth_id=642 X-Original-From: Radu Sabau Reply-To: radu.sabau@analog.com From: Radu Sabau Add per-channel oversampling ratio (OSR) support for CNV burst mode. The accumulator depth register (ACC_DEPTH_IN) is programmed with the selected OSR at buffer enable time and before each single-shot read. Supported OSR values: 1, 2, 4, 8, 16, 32. Introduce AD4691_MANUAL_CHANNEL() for manual mode channels, which do not expose the oversampling ratio attribute since OSR is not applicable in that mode. A separate manual_channels array is added to struct ad4691_channel_info and selected at probe time; offload paths reuse the same arrays with num_channels capping access before the soft timestamp entry. The reported sampling frequency accounts for the active OSR: effective_freq =3D oscillator_freq / osr OSR defaults to 1 (no accumulation) for all channels. Signed-off-by: Radu Sabau --- drivers/iio/adc/ad4691.c | 137 +++++++++++++++++++++++++++++++++++++++++++= ---- 1 file changed, 128 insertions(+), 9 deletions(-) diff --git a/drivers/iio/adc/ad4691.c b/drivers/iio/adc/ad4691.c index 839ea7f44c78..ef96d736996e 100644 --- a/drivers/iio/adc/ad4691.c +++ b/drivers/iio/adc/ad4691.c @@ -116,6 +116,7 @@ enum ad4691_ref_ctrl { =20 struct ad4691_channel_info { const struct iio_chan_spec *channels; + const struct iio_chan_spec *manual_channels; unsigned int num_channels; }; =20 @@ -126,7 +127,34 @@ struct ad4691_chip_info { const struct ad4691_channel_info *offload_info; }; =20 +/* CNV burst mode channel =E2=80=94 exposes oversampling ratio. */ #define AD4691_CHANNEL(ch) \ + { \ + .type =3D IIO_VOLTAGE, \ + .indexed =3D 1, \ + .info_mask_separate =3D BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO) | \ + BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .info_mask_separate_available =3D \ + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO) | \ + BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .info_mask_shared_by_all =3D BIT(IIO_CHAN_INFO_SCALE), \ + .channel =3D ch, \ + .scan_index =3D ch, \ + .scan_type =3D { \ + .sign =3D 'u', \ + .realbits =3D 16, \ + .storagebits =3D 16, \ + .endianness =3D IIO_BE, \ + }, \ + } + +/* + * Manual mode channel =E2=80=94 no oversampling ratio attribute. OSR is n= ot + * supported in manual mode; ACC_DEPTH_IN is not configured during manual + * buffer enable. + */ +#define AD4691_MANUAL_CHANNEL(ch) \ { \ .type =3D IIO_VOLTAGE, \ .indexed =3D 1, \ @@ -177,25 +205,65 @@ static const struct iio_chan_spec ad4693_channels[] = =3D { IIO_CHAN_SOFT_TIMESTAMP(8), }; =20 +static const struct iio_chan_spec ad4691_manual_channels[] =3D { + AD4691_MANUAL_CHANNEL(0), + AD4691_MANUAL_CHANNEL(1), + AD4691_MANUAL_CHANNEL(2), + AD4691_MANUAL_CHANNEL(3), + AD4691_MANUAL_CHANNEL(4), + AD4691_MANUAL_CHANNEL(5), + AD4691_MANUAL_CHANNEL(6), + AD4691_MANUAL_CHANNEL(7), + AD4691_MANUAL_CHANNEL(8), + AD4691_MANUAL_CHANNEL(9), + AD4691_MANUAL_CHANNEL(10), + AD4691_MANUAL_CHANNEL(11), + AD4691_MANUAL_CHANNEL(12), + AD4691_MANUAL_CHANNEL(13), + AD4691_MANUAL_CHANNEL(14), + AD4691_MANUAL_CHANNEL(15), + IIO_CHAN_SOFT_TIMESTAMP(16), +}; + +static const struct iio_chan_spec ad4693_manual_channels[] =3D { + AD4691_MANUAL_CHANNEL(0), + AD4691_MANUAL_CHANNEL(1), + AD4691_MANUAL_CHANNEL(2), + AD4691_MANUAL_CHANNEL(3), + AD4691_MANUAL_CHANNEL(4), + AD4691_MANUAL_CHANNEL(5), + AD4691_MANUAL_CHANNEL(6), + AD4691_MANUAL_CHANNEL(7), + IIO_CHAN_SOFT_TIMESTAMP(8), +}; + +static const int ad4691_oversampling_ratios[] =3D { 1, 2, 4, 8, 16, 32 }; + static const struct ad4691_channel_info ad4691_sw_info =3D { .channels =3D ad4691_channels, + .manual_channels =3D ad4691_manual_channels, .num_channels =3D ARRAY_SIZE(ad4691_channels), }; =20 static const struct ad4691_channel_info ad4693_sw_info =3D { .channels =3D ad4693_channels, + .manual_channels =3D ad4693_manual_channels, .num_channels =3D ARRAY_SIZE(ad4693_channels), }; =20 static const struct ad4691_channel_info ad4691_offload_info =3D { .channels =3D ad4691_channels, - /* No soft timestamp; num_channels caps access to 16. */ + /* + * Offload paths share the SW channel arrays. num_channels caps access + * before the soft timestamp entry, so no separate array is needed. + */ + .manual_channels =3D ad4691_manual_channels, .num_channels =3D 16, }; =20 static const struct ad4691_channel_info ad4693_offload_info =3D { .channels =3D ad4693_channels, - /* No soft timestamp; num_channels caps access to 8. */ + .manual_channels =3D ad4693_manual_channels, .num_channels =3D 8, }; =20 @@ -270,6 +338,8 @@ struct ad4691_state { int irq; int vref_uV; u32 cnv_period_ns; + /* Per-channel oversampling ratio; always 1 in manual mode. */ + u8 osr[16]; =20 bool manual_mode; bool refbuf_en; @@ -490,7 +560,8 @@ static const struct regmap_config ad4691_regmap_config = =3D { .cache_type =3D REGCACHE_MAPLE, }; =20 -static int ad4691_get_sampling_freq(struct ad4691_state *st, int *val) +static int ad4691_get_sampling_freq(struct ad4691_state *st, int *val, + unsigned int osr) { unsigned int reg_val; int ret; @@ -499,7 +570,7 @@ static int ad4691_get_sampling_freq(struct ad4691_state= *st, int *val) if (ret) return ret; =20 - *val =3D ad4691_osc_freqs_Hz[FIELD_GET(AD4691_OSC_FREQ_MASK, reg_val)]; + *val =3D ad4691_osc_freqs_Hz[FIELD_GET(AD4691_OSC_FREQ_MASK, reg_val)] / = osr; return IIO_VAL_INT; } =20 @@ -536,6 +607,11 @@ static int ad4691_read_avail(struct iio_dev *indio_dev, *type =3D IIO_VAL_INT; *length =3D ARRAY_SIZE(ad4691_osc_freqs_Hz) - start; return IIO_AVAIL_LIST; + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + *vals =3D ad4691_oversampling_ratios; + *type =3D IIO_VAL_INT; + *length =3D ARRAY_SIZE(ad4691_oversampling_ratios); + return IIO_AVAIL_LIST; default: return -EINVAL; } @@ -566,6 +642,11 @@ static int ad4691_single_shot_read(struct iio_dev *ind= io_dev, if (ret) return ret; =20 + ret =3D regmap_write(st->regmap, AD4691_ACC_DEPTH_IN(chan->channel), + st->osr[chan->channel]); + if (ret) + return ret; + ret =3D regmap_read(st->regmap, AD4691_OSC_FREQ_REG, ®_val); if (ret) return ret; @@ -575,8 +656,9 @@ static int ad4691_single_shot_read(struct iio_dev *indi= o_dev, return ret; =20 osc_idx =3D FIELD_GET(AD4691_OSC_FREQ_MASK, reg_val); - /* Wait 2 oscillator periods for the conversion to complete. */ - period_us =3D DIV_ROUND_UP(2UL * USEC_PER_SEC, ad4691_osc_freqs_Hz[osc_id= x]); + /* Wait osr oscillator periods for all accumulator samples to complete. */ + period_us =3D DIV_ROUND_UP((unsigned long)st->osr[chan->channel] * USEC_P= ER_SEC, + ad4691_osc_freqs_Hz[osc_idx]); fsleep(period_us); =20 ret =3D regmap_write(st->regmap, AD4691_OSC_EN_REG, 0); @@ -611,7 +693,10 @@ static int ad4691_read_raw(struct iio_dev *indio_dev, return ad4691_single_shot_read(indio_dev, chan, val); } case IIO_CHAN_INFO_SAMP_FREQ: - return ad4691_get_sampling_freq(st, val); + return ad4691_get_sampling_freq(st, val, st->osr[chan->channel]); + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + *val =3D st->osr[chan->channel]; + return IIO_VAL_INT; case IIO_CHAN_INFO_SCALE: *val =3D st->vref_uV / (MICRO / MILLI); *val2 =3D chan->scan_type.realbits; @@ -625,9 +710,24 @@ static int ad4691_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int val, int val2, long mask) { + struct ad4691_state *st =3D iio_priv(indio_dev); + switch (mask) { case IIO_CHAN_INFO_SAMP_FREQ: return ad4691_set_sampling_freq(indio_dev, val); + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: { + IIO_DEV_ACQUIRE_DIRECT_MODE(indio_dev, claim); + if (IIO_DEV_ACQUIRE_FAILED(claim)) + return -EBUSY; + + for (unsigned int i =3D 0; i < ARRAY_SIZE(ad4691_oversampling_ratios); i= ++) { + if (ad4691_oversampling_ratios[i] !=3D val) + continue; + st->osr[chan->channel] =3D val; + return 0; + } + return -EINVAL; + } default: return -EINVAL; } @@ -842,6 +942,12 @@ static int ad4691_cnv_burst_buffer_preenable(struct ii= o_dev *indio_dev) if (ret) goto err_unoptimize; =20 + iio_for_each_active_channel(indio_dev, i) { + ret =3D regmap_write(st->regmap, AD4691_ACC_DEPTH_IN(i), st->osr[i]); + if (ret) + goto err_unoptimize; + } + ret =3D ad4691_enter_conversion_mode(st); if (ret) goto err_unoptimize; @@ -998,6 +1104,12 @@ static int ad4691_cnv_burst_offload_buffer_postenable= (struct iio_dev *indio_dev) if (ret) return ret; =20 + iio_for_each_active_channel(indio_dev, bit) { + ret =3D regmap_write(st->regmap, AD4691_ACC_DEPTH_IN(bit), st->osr[bit]); + if (ret) + return ret; + } + ret =3D ad4691_enter_conversion_mode(st); if (ret) return ret; @@ -1519,6 +1631,7 @@ static int ad4691_probe(struct spi_device *spi) st =3D iio_priv(indio_dev); st->spi =3D spi; st->info =3D spi_get_device_match_data(spi); + memset(st->osr, 1, sizeof(st->osr)); =20 ret =3D devm_mutex_init(dev, &st->lock); if (ret) @@ -1553,11 +1666,17 @@ static int ad4691_probe(struct spi_device *spi) indio_dev->modes =3D INDIO_DIRECT_MODE; =20 if (spi_offload) { - indio_dev->channels =3D st->info->offload_info->channels; + if (st->manual_mode) + indio_dev->channels =3D st->info->offload_info->manual_channels; + else + indio_dev->channels =3D st->info->offload_info->channels; indio_dev->num_channels =3D st->info->offload_info->num_channels; ret =3D ad4691_setup_offload(indio_dev, st, spi_offload); } else { - indio_dev->channels =3D st->info->sw_info->channels; + if (st->manual_mode) + indio_dev->channels =3D st->info->sw_info->manual_channels; + else + indio_dev->channels =3D st->info->sw_info->channels; indio_dev->num_channels =3D st->info->sw_info->num_channels; ret =3D ad4691_setup_triggered_buffer(indio_dev, st); } --=20 2.43.0