* Re: [PATCH v3 2/2] Documentation: drm: Remove drm_atomic_state rename entry
From: kernel test robot @ 2026-04-05 11:32 UTC (permalink / raw)
To: Maxime Ripard, David Airlie, Simona Vetter, Maarten Lankhorst,
Thomas Zimmermann, Jonathan Corbet
Cc: oe-kbuild-all, Jani Nikula, Joonas Lahtinen, Rodrigo Vivi,
Tvrtko Ursulin, Alex Deucher, Christian König, Rob Clark,
Dmitry Baryshkov, Andrzej Hajda, Neil Armstrong, Robert Foss,
Dave Stevenson, Laurent Pinchart, dri-devel, linux-doc,
Maxime Ripard, Luca Ceresoli
In-Reply-To: <20260402-drm-drm-atomic-update-v3-2-b826f51ac511@kernel.org>
Hi Maxime,
kernel test robot noticed the following build warnings:
[auto build test WARNING on 9bdbf7eb25b3121ef19533df4fb70f2c39fc0d6a]
url: https://github.com/intel-lab-lkp/linux/commits/Maxime-Ripard/drm-Rename-struct-drm_atomic_state-to-drm_atomic_commit/20260405-115623
base: 9bdbf7eb25b3121ef19533df4fb70f2c39fc0d6a
patch link: https://lore.kernel.org/r/20260402-drm-drm-atomic-update-v3-2-b826f51ac511%40kernel.org
patch subject: [PATCH v3 2/2] Documentation: drm: Remove drm_atomic_state rename entry
compiler: clang version 20.1.8 (https://github.com/llvm/llvm-project 87f0227cb60147a26a1eeb4fb06e3b505e9c7261)
docutils: docutils (Docutils 0.21.2, Python 3.13.5, on linux)
reproduce: (https://download.01.org/0day-ci/archive/20260405/202604051325.jSSmpYZj-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202604051325.jSSmpYZj-lkp@intel.com/
All warnings (new ones prefixed by >>):
Examples
~~~~~~~~ [docutils]
>> Documentation/gpu/komeda-kms.rst:420: WARNING: Title underline too short.
vim +420 Documentation/gpu/komeda-kms.rst
557c37360eca86 james qian wang (Arm Technology China 2019-01-03 408)
557c37360eca86 james qian wang (Arm Technology China 2019-01-03 409) struct komeda_component {
557c37360eca86 james qian wang (Arm Technology China 2019-01-03 410) struct drm_private_obj obj;
557c37360eca86 james qian wang (Arm Technology China 2019-01-03 411) ...
557c37360eca86 james qian wang (Arm Technology China 2019-01-03 412) }
557c37360eca86 james qian wang (Arm Technology China 2019-01-03 413)
557c37360eca86 james qian wang (Arm Technology China 2019-01-03 414) struct komeda_pipeline {
557c37360eca86 james qian wang (Arm Technology China 2019-01-03 415) struct drm_private_obj obj;
557c37360eca86 james qian wang (Arm Technology China 2019-01-03 416) ...
557c37360eca86 james qian wang (Arm Technology China 2019-01-03 417) }
557c37360eca86 james qian wang (Arm Technology China 2019-01-03 418)
77e56dfef2e28b Maxime Ripard 2026-04-02 419 Tracking component_state/pipeline_state by drm_atomic_commit
557c37360eca86 james qian wang (Arm Technology China 2019-01-03 @420) -----------------------------------------------------------
557c37360eca86 james qian wang (Arm Technology China 2019-01-03 421)
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply
* Re: [PATCH v6 3/4] iio: adc: ad4691: add triggered buffer support
From: Andy Shevchenko @ 2026-04-05 8:57 UTC (permalink / raw)
To: David Lechner
Cc: radu.sabau, Lars-Peter Clausen, Michael Hennerich,
Jonathan Cameron, Nuno Sá, Andy Shevchenko, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Uwe Kleine-König,
Liam Girdwood, Mark Brown, Linus Walleij, Bartosz Golaszewski,
Philipp Zabel, Jonathan Corbet, Shuah Khan, linux-iio, devicetree,
linux-kernel, linux-pwm, linux-gpio, linux-doc
In-Reply-To: <e38e5b97-e90f-4613-a15e-6c3d08cd77f7@baylibre.com>
On Sat, Apr 04, 2026 at 10:12:04AM -0500, David Lechner wrote:
> On 4/3/26 6:03 AM, Radu Sabau via B4 Relay wrote:
> > Add buffered capture support using the IIO triggered buffer framework.
> >
> > CNV Burst Mode: the GP pin identified by interrupt-names in the device
> > tree is configured as DATA_READY output. The IRQ handler stops
> > conversions and fires the IIO trigger; the trigger handler executes a
> > pre-built SPI message that reads all active channels from the AVG_IN
> > accumulator registers and then resets accumulator state and restarts
> > conversions for the next cycle.
> >
> > Manual Mode: CNV is tied to SPI CS so each transfer simultaneously
> > reads the previous result and starts the next conversion (pipelined
> > N+1 scheme). At preenable time a pre-built, optimised SPI message of
> > N+1 transfers is constructed (N channel reads plus one NOOP to drain
> > the pipeline). The trigger handler executes the message in a single
> > spi_sync() call and collects the results. An external trigger (e.g.
> > iio-trig-hrtimer) is required to drive the trigger at the desired
> > sample rate.
> >
> > Both modes share the same trigger handler and push a complete scan —
> > one u16 slot per channel at its scan_index position, followed by a
> > timestamp — to the IIO buffer via iio_push_to_buffers_with_ts().
> >
> > The CNV Burst Mode sampling frequency (PWM period) is exposed as a
> > buffer-level attribute via IIO_DEVICE_ATTR.
Tried my best to avoid clashes with David's review.
...
> > #include <linux/array_size.h>
> > #include <linux/bitfield.h>
> > +#include <linux/bitmap.h>
> > #include <linux/bitops.h>
When bitmap.h is present, it implies bitops.h, hence the latter can be simply
replaced.
> > #include <linux/cleanup.h>
> > #include <linux/delay.h>
> > #include <linux/dev_printk.h>
> > #include <linux/device/devres.h>
> > #include <linux/err.h>
> > +#include <linux/interrupt.h>
> > #include <linux/math.h>
> > #include <linux/module.h>
> > #include <linux/mod_devicetable.h>
> > +#include <linux/property.h>
> > +#include <linux/pwm.h>
> > #include <linux/regmap.h>
> > #include <linux/regulator/consumer.h>
> > #include <linux/reset.h>
...
> > .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) \
> > - | BIT(IIO_CHAN_INFO_SAMP_FREQ), \
> > + | BIT(IIO_CHAN_INFO_SAMP_FREQ) \
> > + | BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \
> > .info_mask_separate_available = \
> > - BIT(IIO_CHAN_INFO_SAMP_FREQ), \
> > + BIT(IIO_CHAN_INFO_SAMP_FREQ) \
> > + | BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \
You may reduce churn by squeezing a new ones in between existing ones.
Also consider use usual patter of placing the operator on the same line where
left operand is (currently it goes with the right operand).
...
> > struct ad4691_state {
Just to double check, when add a new field or fields into the data structure
check with `pahole` that the new members placed at the best or at least good
enough locations.
> > const struct ad4691_chip_info *info;
> > struct regmap *regmap;
> > +
> > + struct pwm_device *conv_trigger;
> > + int irq;
> > +
> > + bool manual_mode;
> > +
> > int vref_uV;
> > + u8 osr[16];
> > bool refbuf_en;
> > bool ldo_en;
> > + u32 cnv_period_ns;
> > /*
> > * Synchronize access to members of the driver state, and ensure
> > * atomicity of consecutive SPI operations.
> > */
> > struct mutex lock;
> > + /*
> > + * Per-buffer-enable lifetime resources:
> > + * Manual Mode - a pre-built SPI message that clocks out N+1
> > + * transfers in one go.
> > + * CNV Burst Mode - a pre-built SPI message that clocks out 2*N
> > + * transfers in one go.
> > + */
> > + struct spi_message scan_msg;
> > + struct spi_transfer *scan_xfers;
> > + __be16 *scan_tx;
> > + __be16 *scan_rx;
>
> Why not embed these arrays here? Then we don't have to deal with
> alloc/free later.
>
> > + /* Scan buffer: one slot per channel plus timestamp */
> > + struct {
> > + u16 vals[16];
> > + aligned_s64 ts;
> > + } scan __aligned(IIO_DMA_MINALIGN);
>
> Better would be IIO_DECLARE_BUFFER_WITH_TS() since we don't always
> use all vals.
>
> Also, current usage doesn't need to be DMA-safe because scan_tx
> is being used for the actual SPI xfer.
>
> > };
...
> > +static int ad4691_gpio_setup(struct ad4691_state *st, unsigned int gp_num)
> > +{
> > + unsigned int shift = 4 * (gp_num % 2);
> > +
> > + return regmap_update_bits(st->regmap,
> > + AD4691_GPIO_MODE1_REG + gp_num / 2,
> > + AD4691_GP_MODE_MASK << shift,
> > + AD4691_GP_MODE_DATA_READY << shift);
Not sure if compiler will see % and / together, I would go with two more
temporary variables to make it clear to it:
... _bit_off = % 2;
... _reg_off = / 2;
The practical example is described, for example, here:
9b3cd5c7099f ("regmap: place foo / 8 and foo % 8 closer to each other").
> > +}
...
> > +static int ad4691_manual_buffer_preenable(struct iio_dev *indio_dev)
> > +{
> > + struct ad4691_state *st = iio_priv(indio_dev);
> > + struct device *dev = regmap_get_device(st->regmap);
> > + struct spi_device *spi = to_spi_device(dev);
> > + unsigned int n_active = bitmap_weight(indio_dev->active_scan_mask,
> > + iio_get_masklength(indio_dev));
In such cases please split definition and assignment. Will take two lines, but
readability will be better.
> > + unsigned int n_xfers = n_active + 1;
> > + unsigned int k, i;
> > + int ret;
> > +
> > + st->scan_xfers = kcalloc(n_xfers, sizeof(*st->scan_xfers), GFP_KERNEL);
>
> Usually, we make st->scan_xfers a fixed array with the max number of possible
> xfers. Then we don't have to deal with alloc/free.
And please if it's still be required to allocate and possible use kzalloc_objs().
> > + if (!st->scan_xfers)
> > + return -ENOMEM;
> > +
> > + st->scan_tx = kcalloc(n_xfers, sizeof(*st->scan_tx), GFP_KERNEL);
> > + if (!st->scan_tx) {
> > + kfree(st->scan_xfers);
> > + return -ENOMEM;
> > + }
> > +
> > + st->scan_rx = kcalloc(n_xfers, sizeof(*st->scan_rx), GFP_KERNEL);
> > + if (!st->scan_rx) {
> > + kfree(st->scan_tx);
> > + kfree(st->scan_xfers);
> > + return -ENOMEM;
> > + }
> > +
> > + spi_message_init(&st->scan_msg);
> > +
> > + k = 0;
> > + iio_for_each_active_channel(indio_dev, i) {
> > + st->scan_tx[k] = cpu_to_be16(AD4691_ADC_CHAN(i));
> > + st->scan_xfers[k].tx_buf = &st->scan_tx[k];
> > + st->scan_xfers[k].rx_buf = &st->scan_rx[k];
> > + st->scan_xfers[k].len = sizeof(__be16);
> > + st->scan_xfers[k].cs_change = 1;
> > + spi_message_add_tail(&st->scan_xfers[k], &st->scan_msg);
> > + k++;
> > + }
> > +
> > + /* Final NOOP transfer to retrieve last channel's result. */
> > + st->scan_tx[k] = cpu_to_be16(AD4691_NOOP);
> > + st->scan_xfers[k].tx_buf = &st->scan_tx[k];
> > + st->scan_xfers[k].rx_buf = &st->scan_rx[k];
> > + st->scan_xfers[k].len = sizeof(__be16);
> > + spi_message_add_tail(&st->scan_xfers[k], &st->scan_msg);
> > +
> > + st->scan_msg.spi = spi;
>
> This isn't how the SPI framework is intended to be used. We should
> have st->spi = spi in probe instead.
>
> > +
> > + ret = spi_optimize_message(spi, &st->scan_msg);
> > + if (ret) {
> > + ad4691_free_scan_bufs(st);
> > + return ret;
> > + }
> > +
> > + ret = ad4691_enter_conversion_mode(st);
> > + if (ret) {
> > + spi_unoptimize_message(&st->scan_msg);
> > + ad4691_free_scan_bufs(st);
> > + return ret;
> > + }
> > +
> > + return 0;
> > +}
...
> > +static int ad4691_cnv_burst_buffer_preenable(struct iio_dev *indio_dev)
As per above.
...
> > +static ssize_t sampling_frequency_show(struct device *dev,
> > + struct device_attribute *attr,
> > + char *buf)
> > +{
> > + struct iio_dev *indio_dev = dev_to_iio_dev(dev);
> > + struct ad4691_state *st = iio_priv(indio_dev);
> > +
> > + return sysfs_emit(buf, "%u\n", (u32)(NSEC_PER_SEC / st->cnv_period_ns));
Why casting?
> > +}
...
> > +static IIO_DEVICE_ATTR(sampling_frequency, 0644,
> > + sampling_frequency_show,
> > + sampling_frequency_store, 0);
IIO_DEVICE_ATTR_RW().
...
> > +static int ad4691_read_scan(struct iio_dev *indio_dev, s64 timestamp)
> > +{
> > + struct ad4691_state *st = iio_priv(indio_dev);
> > + unsigned int i, k = 0;
> > + int ret;
> > +
> > + guard(mutex)(&st->lock);
> > +
> > + ret = spi_sync(st->scan_msg.spi, &st->scan_msg);
> > + if (ret)
> > + return ret;
> > +
> > + if (st->manual_mode) {
> > + iio_for_each_active_channel(indio_dev, i) {
> > + st->scan.vals[i] = be16_to_cpu(st->scan_rx[k + 1]);
> > + k++;
> > + }
> > + } else {
> > + iio_for_each_active_channel(indio_dev, i) {
> > + st->scan.vals[i] = be16_to_cpu(st->scan_rx[k]);
> > + k++;
> > + }
>
> I suppose this is fine, but we usually try to avoid extra copiying and
> byte swapping of bufferes like this if we can. It seems completly doable
> in both modes. Manual mode will just one extra two-byte buffer for the
> throw-away conversion on the first read xfer (or just write to the same
> element twice).
And in case it's still needed, we may introduce a helper in
include/linux/byteorder/generic.h calling it memcpy_to/from_be16().
> > + ret = regmap_write(st->regmap, AD4691_STATE_RESET_REG,
> > + AD4691_STATE_RESET_ALL);
> > + if (ret)
> > + return ret;
> > +
> > + ret = ad4691_sampling_enable(st, true);
> > + if (ret)
> > + return ret;
> > + }
> > +
> > + iio_push_to_buffers_with_ts(indio_dev, &st->scan, sizeof(st->scan),
> > + timestamp);
> > + return 0;
> > +}
...
> > +static int ad4691_setup_triggered_buffer(struct iio_dev *indio_dev,
> > + struct ad4691_state *st)
> > +{
> > + struct device *dev = regmap_get_device(st->regmap);
> > + struct iio_trigger *trig;
> > + unsigned int i;
> > + int irq, ret;
> > +
> > + trig = devm_iio_trigger_alloc(dev, "%s-dev%d",
> > + indio_dev->name,
> > + iio_device_id(indio_dev));
> > + if (!trig)
> > + return -ENOMEM;
> > +
> > + trig->ops = &ad4691_trigger_ops;
> > + iio_trigger_set_drvdata(trig, st);
> > +
> > + ret = devm_iio_trigger_register(dev, trig);
> > + if (ret)
> > + return dev_err_probe(dev, ret, "IIO trigger register failed\n");
> > +
> > + indio_dev->trig = iio_trigger_get(trig);
> > +
> > + if (!st->manual_mode) {
>
> I would invert the if since the other case is shorter.
+1.
> > + /*
> > + * The GP pin named in interrupt-names asserts at end-of-conversion.
> > + * The IRQ handler stops conversions and fires the IIO trigger so
> > + * the trigger handler can read and push the sample to the buffer.
> > + * The IRQ is kept disabled until the buffer is enabled.
> > + */
> > + irq = -ENODEV;
> > + for (i = 0; i < ARRAY_SIZE(ad4691_gp_names); i++) {
> > + irq = fwnode_irq_get_byname(dev_fwnode(dev),
> > + ad4691_gp_names[i]);
> > + if (irq > 0)
> > + break;
> > + }
> > + if (irq <= 0)
> > + return dev_err_probe(dev, irq < 0 ? irq : -ENODEV,
> > + "failed to get GP interrupt\n");
>
> Usually we would usually just use spi->irq since it already
> has been looked up. But I guess it is OK to do it like this.
No, it's not. (Linux) IRQ shouldn't ever be 0, so this check is effectively a
dead code.
irq = -ENXIO; // Note, this is the error code used by core for
// IRQ not found.
...
if (irq < 0)
return dev_err_probe(dev, irq, "failed to get GP interrupt\n");
> > + st->irq = irq;
> > +
> > + ret = ad4691_gpio_setup(st, i);
> > + if (ret)
> > + return ret;
> > +
> > + /*
> > + * IRQ is kept disabled until the buffer is enabled to prevent
> > + * spurious DATA_READY events before the SPI message is set up.
> > + */
> > + ret = devm_request_threaded_irq(dev, irq, NULL,
> > + &ad4691_irq,
> > + IRQF_ONESHOT | IRQF_NO_AUTOEN,
> > + indio_dev->name, indio_dev);
> > + if (ret)
> > + return ret;
> > +
> > + return devm_iio_triggered_buffer_setup_ext(dev, indio_dev,
> > + &iio_pollfunc_store_time,
> > + &ad4691_trigger_handler,
> > + IIO_BUFFER_DIRECTION_IN,
> > + &ad4691_cnv_burst_buffer_setup_ops,
> > + ad4691_buffer_attrs);
> > + }
> > +
> > + return devm_iio_triggered_buffer_setup(dev, indio_dev,
> > + &iio_pollfunc_store_time,
> > + &ad4691_trigger_handler,
> > + &ad4691_manual_buffer_setup_ops);
> > +}
> > +
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply
* Re: [PATCH v9 00/10] VMSCAPE optimization for BHI variant
From: Pawan Gupta @ 2026-04-05 7:23 UTC (permalink / raw)
To: David Laight
Cc: x86, Jon Kohler, Nikolay Borisov, H. Peter Anvin, Josh Poimboeuf,
David Kaplan, Sean Christopherson, Borislav Petkov, Dave Hansen,
Peter Zijlstra, Alexei Starovoitov, Daniel Borkmann,
Andrii Nakryiko, KP Singh, Jiri Olsa, David S. Miller,
Andy Lutomirski, Thomas Gleixner, Ingo Molnar, David Ahern,
Martin KaFai Lau, Eduard Zingerman, Song Liu, Yonghong Song,
John Fastabend, Stanislav Fomichev, Hao Luo, Paolo Bonzini,
Jonathan Corbet, linux-kernel, kvm, Asit Mallick, Tao Zhang, bpf,
netdev, linux-doc
In-Reply-To: <20260404162059.34ca90df@pumpkin>
On Sat, Apr 04, 2026 at 04:20:59PM +0100, David Laight wrote:
> On Thu, 2 Apr 2026 17:30:32 -0700
> Pawan Gupta <pawan.kumar.gupta@linux.intel.com> wrote:
>
> > v9:
> > - Use global variables for BHB loop counters instead of ALTERNATIVE-based
> > approach. (Dave & others)
> > - Use 32-bit registers (%eax/%ecx) for loop counters, loaded via movzbl
> > from 8-bit globals. 8-bit registers (e.g. %ah in the inner loop) caused
> > performance regression on certain CPUs due to partial-register stalls. (David Laight)
> > - Let BPF save/restore %rax/%rcx as in the original implementation, since
> > it is the only caller that needs these registers preserved across the
> > BHB clearing sequence.
>
> That is as dangerous as hell...
> Does BPF even save %rcx - I'm sure I checked that a long time ago
> and found it didn't.
Below code injects save/restore of %rax and %rcx to BPF programs:
arch/x86/net/bpf_jit_comp.c
emit_spectre_bhb_barrier()
{
u8 *prog = *pprog;
u8 *func;
if (cpu_feature_enabled(X86_FEATURE_CLEAR_BHB_LOOP)) {
/* The clearing sequence clobbers eax and ecx. */
EMIT1(0x50); /* push rax */
EMIT1(0x51); /* push rcx */
ip += 2;
func = (u8 *)clear_bhb_loop_nofence;
ip += x86_call_depth_emit_accounting(&prog, func, ip);
if (emit_call(&prog, func, ip))
return -EINVAL;
/* Don't speculate past this until BHB is cleared */
EMIT_LFENCE();
EMIT1(0x59); /* pop rcx */
EMIT1(0x58); /* pop rax */
}
...
> (I'm mostly AFK over Easter and can't check.)
> A least there should be a blood great big comment that BPF calls this code
> and only saves specific registers.
Sure, will add.
> But given the number of mispredicted branches and other pipeline stalls
> in this code a couple of register saves to stack are unlikely to make
> any difference.
BPF programs have been saving/restoring the registers since long now. What
problem are you anticipating?
^ permalink raw reply
* Re: [PATCH v3 0/2] docs: advanced search with benchmark harness
From: Randy Dunlap @ 2026-04-05 5:51 UTC (permalink / raw)
To: Rito Rhymes; +Cc: linux-doc, linux-kernel
In-Reply-To: <DHK7FY79AOJW.AC6LHU703AIR@ritovision.com>
Hi,
On 4/4/26 12:50 AM, Rito Rhymes wrote:
> Randy, I meant to include you on the v3 reroll; this new version is
> intended to address the compatibility issue you hit earlier in our
> initial test and debugging (among other improvements).
>
> I believe the problem came from version-dependent differences in the
> generated Sphinx search data, so this reroll hardens the compatibility
> handling around those differences and the search logic that consumes the
> data.
>
> If you have time to try it again with the setup that exposed the
> problem before, I would appreciate confirmation that the updated
> version behaves correctly there.
>
> I would also appreciate your broader assessment of the feature:
> whether it seems genuinely useful in practice, how large the benefit is
> relative to the current Quick Search interface, how many other users you
> think would benefit from it, and whether you see any remaining issues or
> obvious room for improvement.
I like it. I think it's useful -- the old search could give a bit too much
output. The search result tabs (groups) are helpful.
But it will be up to Jon whether its usefulness exceeds its complications.
Also, I'm not sure that Linux developer mailing lists will reach the right
audience for feedback about this change.
I mostly use 'grep' for searching Documentation/ and I expect lots of other
developers also do that (if they bother to look). So I don't know who will
be the largest user(s) of this feature. I.e., I don't know who uses
docs.kernel.org.
I do notice under the Pages tab that all of the pages listed say
"Summary unavailable." I don't know what should be there instead
of that message.
--
~Randy
^ permalink raw reply
* Re: [PATCH] docs: fix typos and duplicated words across documentation
From: Bagas Sanjaya @ 2026-04-05 5:01 UTC (permalink / raw)
To: Randy Dunlap, Manuel Cortez, linux-doc; +Cc: corbet
In-Reply-To: <8e489ff9-e9f8-4924-ad92-a1dbd6d33121@infradead.org>
[-- Attachment #1: Type: text/plain, Size: 1186 bytes --]
On Sat, Apr 04, 2026 at 08:16:05PM -0700, Randy Dunlap wrote:
> Hi,
>
> This last one is a little awkward as is but dropping one "in" doesn't help it --
> it harms it (i.e., it's correct as is but could possibly be improved.)
>
> > diff --git a/Documentation/networking/switchdev.rst b/Documentation/networking/switchdev.rst
> > index 2966b7122f05..948bce44ca9b 100644
> > --- a/Documentation/networking/switchdev.rst
> > +++ b/Documentation/networking/switchdev.rst
> > @@ -162,7 +162,7 @@ The switchdev driver can know a particular port's position in the topology by
> > monitoring NETDEV_CHANGEUPPER notifications. For example, a port moved into a
> > bond will see its upper master change. If that bond is moved into a bridge,
> > the bond's upper master will change. And so on. The driver will track such
> > -movements to know what position a port is in in the overall topology by
> > +movements to know what position a port is in the overall topology by
I think it can be reworded as "The driver will track movements to locate
the port's position in the overall topology ...".
Thanks.
--
An old man doll... just what I always wanted! - Clara
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
^ permalink raw reply
* Re: [PATCH] docs: fix typos and duplicated words across documentation
From: Randy Dunlap @ 2026-04-05 3:16 UTC (permalink / raw)
To: Manuel Cortez, linux-doc; +Cc: corbet
In-Reply-To: <20260405030359.7392-1-mdjesuscv@gmail.com>
Hi,
On 4/4/26 8:03 PM, Manuel Cortez wrote:
> Fix the following typos and duplicated words:
>
> - admin-guide/pm/intel-speed-select.rst: "weather" -> "whether"
> - core-api/real-time/differences.rst: "the the" -> "the"
> - admin-guide/bcache.rst: "to to" -> "to"
> - networking/switchdev.rst: "is in in" -> "is in"
>
> Signed-off-by: Manuel Cortez <mdjesuscv@gmail.com>
> ---
> Documentation/admin-guide/bcache.rst | 2 +-
> Documentation/admin-guide/pm/intel-speed-select.rst | 2 +-
> Documentation/core-api/real-time/differences.rst | 2 +-
> Documentation/networking/switchdev.rst | 2 +-
> 4 files changed, 4 insertions(+), 4 deletions(-)
>
> diff --git a/Documentation/admin-guide/bcache.rst b/Documentation/admin-guide/bcache.rst
> index f71f349553e4..325816edbdab 100644
> --- a/Documentation/admin-guide/bcache.rst
> +++ b/Documentation/admin-guide/bcache.rst
> @@ -618,7 +618,7 @@ cache_replacement_policy
> One of either lru, fifo or random.
>
> freelist_percent
> - Size of the freelist as a percentage of nbuckets. Can be written to to
> + Size of the freelist as a percentage of nbuckets. Can be written to
> increase the number of buckets kept on the freelist, which lets you
> artificially reduce the size of the cache at runtime. Mostly for testing
> purposes (i.e. testing how different size caches affect your hit rate).
> diff --git a/Documentation/admin-guide/pm/intel-speed-select.rst b/Documentation/admin-guide/pm/intel-speed-select.rst
> index a2bfb971654f..dec2a25f10bc 100644
> --- a/Documentation/admin-guide/pm/intel-speed-select.rst
> +++ b/Documentation/admin-guide/pm/intel-speed-select.rst
> @@ -287,7 +287,7 @@ level.
> Check presence of other Intel(R) SST features
> ---------------------------------------------
>
> -Each of the performance profiles also specifies weather there is support of
> +Each of the performance profiles also specifies whether there is support of
> other two Intel(R) SST features (Intel(R) Speed Select Technology - Base Frequency
> (Intel(R) SST-BF) and Intel(R) Speed Select Technology - Turbo Frequency (Intel
> SST-TF)).
> diff --git a/Documentation/core-api/real-time/differences.rst b/Documentation/core-api/real-time/differences.rst
> index 83ec9aa1c61a..a129570dab5a 100644
> --- a/Documentation/core-api/real-time/differences.rst
> +++ b/Documentation/core-api/real-time/differences.rst
> @@ -213,7 +213,7 @@ to suspend until the callback completes, ensuring forward progress without
> risking livelock.
>
> In order to solve the problem at the API level, the sequence locks were extended
> -to allow a proper handover between the the spinning reader and the maybe
> +to allow a proper handover between the spinning reader and the maybe
> blocked writer.
>
> Sequence locks
Those first 3 look good.
Just a matter of how they get merged.
This last one is a little awkward as is but dropping one "in" doesn't help it --
it harms it (i.e., it's correct as is but could possibly be improved.)
> diff --git a/Documentation/networking/switchdev.rst b/Documentation/networking/switchdev.rst
> index 2966b7122f05..948bce44ca9b 100644
> --- a/Documentation/networking/switchdev.rst
> +++ b/Documentation/networking/switchdev.rst
> @@ -162,7 +162,7 @@ The switchdev driver can know a particular port's position in the topology by
> monitoring NETDEV_CHANGEUPPER notifications. For example, a port moved into a
> bond will see its upper master change. If that bond is moved into a bridge,
> the bond's upper master will change. And so on. The driver will track such
> -movements to know what position a port is in in the overall topology by
> +movements to know what position a port is in the overall topology by
> registering for netdevice events and acting on NETDEV_CHANGEUPPER.
>
> L2 Forwarding Offload
--
~Randy
^ permalink raw reply
* [PATCH] docs: fix typos and duplicated words across documentation
From: Manuel Cortez @ 2026-04-05 3:03 UTC (permalink / raw)
To: linux-doc; +Cc: corbet, Manuel Cortez
Fix the following typos and duplicated words:
- admin-guide/pm/intel-speed-select.rst: "weather" -> "whether"
- core-api/real-time/differences.rst: "the the" -> "the"
- admin-guide/bcache.rst: "to to" -> "to"
- networking/switchdev.rst: "is in in" -> "is in"
Signed-off-by: Manuel Cortez <mdjesuscv@gmail.com>
---
Documentation/admin-guide/bcache.rst | 2 +-
Documentation/admin-guide/pm/intel-speed-select.rst | 2 +-
Documentation/core-api/real-time/differences.rst | 2 +-
Documentation/networking/switchdev.rst | 2 +-
4 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/Documentation/admin-guide/bcache.rst b/Documentation/admin-guide/bcache.rst
index f71f349553e4..325816edbdab 100644
--- a/Documentation/admin-guide/bcache.rst
+++ b/Documentation/admin-guide/bcache.rst
@@ -618,7 +618,7 @@ cache_replacement_policy
One of either lru, fifo or random.
freelist_percent
- Size of the freelist as a percentage of nbuckets. Can be written to to
+ Size of the freelist as a percentage of nbuckets. Can be written to
increase the number of buckets kept on the freelist, which lets you
artificially reduce the size of the cache at runtime. Mostly for testing
purposes (i.e. testing how different size caches affect your hit rate).
diff --git a/Documentation/admin-guide/pm/intel-speed-select.rst b/Documentation/admin-guide/pm/intel-speed-select.rst
index a2bfb971654f..dec2a25f10bc 100644
--- a/Documentation/admin-guide/pm/intel-speed-select.rst
+++ b/Documentation/admin-guide/pm/intel-speed-select.rst
@@ -287,7 +287,7 @@ level.
Check presence of other Intel(R) SST features
---------------------------------------------
-Each of the performance profiles also specifies weather there is support of
+Each of the performance profiles also specifies whether there is support of
other two Intel(R) SST features (Intel(R) Speed Select Technology - Base Frequency
(Intel(R) SST-BF) and Intel(R) Speed Select Technology - Turbo Frequency (Intel
SST-TF)).
diff --git a/Documentation/core-api/real-time/differences.rst b/Documentation/core-api/real-time/differences.rst
index 83ec9aa1c61a..a129570dab5a 100644
--- a/Documentation/core-api/real-time/differences.rst
+++ b/Documentation/core-api/real-time/differences.rst
@@ -213,7 +213,7 @@ to suspend until the callback completes, ensuring forward progress without
risking livelock.
In order to solve the problem at the API level, the sequence locks were extended
-to allow a proper handover between the the spinning reader and the maybe
+to allow a proper handover between the spinning reader and the maybe
blocked writer.
Sequence locks
diff --git a/Documentation/networking/switchdev.rst b/Documentation/networking/switchdev.rst
index 2966b7122f05..948bce44ca9b 100644
--- a/Documentation/networking/switchdev.rst
+++ b/Documentation/networking/switchdev.rst
@@ -162,7 +162,7 @@ The switchdev driver can know a particular port's position in the topology by
monitoring NETDEV_CHANGEUPPER notifications. For example, a port moved into a
bond will see its upper master change. If that bond is moved into a bridge,
the bond's upper master will change. And so on. The driver will track such
-movements to know what position a port is in in the overall topology by
+movements to know what position a port is in the overall topology by
registering for netdevice events and acting on NETDEV_CHANGEUPPER.
L2 Forwarding Offload
--
2.51.0
^ permalink raw reply related
* Re: (sashiko status) [RFC PATCH 0/9] mm/damon: introduce DAMOS failed region quota charge ratio
From: SeongJae Park @ 2026-04-04 21:06 UTC (permalink / raw)
To: SeongJae Park
Cc: damon, kunit-dev, linux-doc, linux-kernel, linux-kselftest,
linux-mm
In-Reply-To: <20260404163943.89278-1-sj@kernel.org>
Dropped individuals from Cc list to reduce the traffic.
TL; DR: sashiko made a few useful findings. I will address those in the next
revision.
Forwarding sashiko.dev review status for the overall picture. Read my replies
to 'ISSUES MAY FOUND' patches for more details.
# review url: https://sashiko.dev/#/patchset/20260404163943.89278-1-sj@kernel.org
- [RFC PATCH 1/9] mm/damon/core: introduce failed region quota charge ratio
- status: Reviewed
- review: ISSUES MAY FOUND
- [RFC PATCH 2/9] mm/damon/sysfs-schemes: implement fail_charge_{num,denom} files
- status: Reviewed
- review: ISSUES MAY FOUND
- [RFC PATCH 3/9] Docs/mm/damon/design: document fail_charge_{num,denom}
- status: Reviewed
- review: ISSUES MAY FOUND
- [RFC PATCH 4/9] Docs/admin-guide/mm/damon/usage: document fail_charge_{num,denom} files
- status: Reviewed
- review: ISSUES MAY FOUND
- [RFC PATCH 5/9] Docs/ABI/damon: document fail_charge_{num,denom}
- status: Reviewed
- review: No issues found.
- [RFC PATCH 6/9] mm/damon/tests/core-kunit: test fail_charge_{num,denom} committing
- status: Reviewed
- review: ISSUES MAY FOUND
- [RFC PATCH 7/9] selftets/damon/_damon_sysfs: support failed region quota charge ratio
- status: Reviewed
- review: No issues found.
- [RFC PATCH 8/9] selftests/damon/drgn_dump_damon_status: support failed region quota charge ratio
- status: Reviewed
- review: No issues found.
- [RFC PATCH 9/9] selftets/damon/sysfs.py: test failed region quota charge ratio
- status: Reviewed
- review: No issues found.
# hkml [1] generated a draft of this mail. It can be regenerated
# using below command:
#
# hkml patch sashiko_dev --thread_status --for_forwarding \
# 20260404163943.89278-1-sj@kernel.org
#
# [1] https://github.com/sjp38/hackermail
Thanks,
SJ
^ permalink raw reply
* Re: (sashiko review) [RFC PATCH 4/9] Docs/admin-guide/mm/damon/usage: document fail_charge_{num,denom} files
From: SeongJae Park @ 2026-04-04 21:01 UTC (permalink / raw)
To: SeongJae Park; +Cc: damon, linux-doc, linux-kernel, linux-mm
In-Reply-To: <20260404163943.89278-5-sj@kernel.org>
Dropped individual recipients from Cc list, to reduce unnecessary traffic.
TL; DR: sashiko found real issues. I will fix those in the next revision.
Forwarding sashiko review in a reply format with my inline
comments below, for details of my view and doing discussions
via mails if needed.
> # review url: https://sashiko.dev/#/patchset/20260404163943.89278-5-sj@kernel.org
> # start of sashiko.dev inline review
> commit 5f8a4cfde3d93d0a4d4ac3d9a57f14f310c43e02
> Author: SeongJae Park <sj@kernel.org>
>
> Docs/admin-guide/mm/damon/usage: document fail_charge_{num,denom} files
>
> Update DAMON usage document for the DAMOS action failed regions quota charge
> ratio control sysfs files.
>
> > diff --git a/Documentation/admin-guide/mm/damon/usage.rst b/Documentation/admin-guide/mm/damon/usage.rst
> > index 534e1199cf091..3d6af6ce72d82 100644
> > --- a/Documentation/admin-guide/mm/damon/usage.rst
> > +++ b/Documentation/admin-guide/mm/damon/usage.rst
> [ ... ]
> > @@ -398,6 +401,13 @@ the background design of the feature and the name of the selectable algorithms.
> > Refer to :ref:`goals directory <sysfs_schemes_quota_goals>` for the goals
> > setup.
> >
> > +You can set the action-failed memory quota charging ratio by writing the
> > +numerator and the denominator for the ratio to ``fail_charge_num`` and
> > +`fail_charge_denom`` files, respectively. Reading those file will return the
>
> This isn't a bug, but fail_charge_denom has a single leading backtick and
> double trailing backticks.
>
> Additionally, should "Reading those file" be "Reading those files"?
Good catch! I will fix those in the next spin.
Thanks,
SJ
>
> > +current set values. Refer to :ref:`design
> > +<damon_design_damos_quotas_failed_memory_charging_ratio>` for more details of
> > +the ratio feature.
> > +
> > The time quota is internally transformed to a size quota. Between the
>
>
> # end of sashiko.dev inline review
> # review url: https://sashiko.dev/#/patchset/20260404163943.89278-5-sj@kernel.org
# hkml [1] generated a draft of this mail. You can regenerate
# this using below command:
#
# hkml patch sashiko_dev --for_forwarding \
# 20260404163943.89278-5-sj@kernel.org
#
# [1] https://github.com/sjp38/hackermail
^ permalink raw reply
* Re: (sashiko review) [RFC PATCH 3/9] Docs/mm/damon/design: document fail_charge_{num,denom}
From: SeongJae Park @ 2026-04-04 20:58 UTC (permalink / raw)
To: SeongJae Park; +Cc: damon, linux-doc, linux-kernel, linux-mm
In-Reply-To: <20260404163943.89278-4-sj@kernel.org>
Dropped individual Cc's except myself to reduce unnecessary traffic. I
initailly thought it would be good to keep Andrew in the Cc list, but even
Andrew may not really interested in each sashiko review for RFC patch, so I
dropped him from the list, too.
sashiko found issues. I will fix those in the next revision.
Forwarding sashiko review in a reply format with my inline
comments below, for details of my view and doing discussions
via mails if needed.
> # review url: https://sashiko.dev/#/patchset/20260404163943.89278-4-sj@kernel.org
> # start of sashiko.dev inline review
> commit bab641e86f0e9820b2ac0a7d4ae493d23c4c50f4
> Author: SeongJae Park <sj@kernel.org>
> Docs/mm/damon/design: document fail_charge_{num,denom}
>
> This commit updates the DAMON design document to explain the action-failed
> memory charging ratio. It provides an example of how to calculate the
> charged memory when an action fails for a portion of a region.
>
> > diff --git a/Documentation/mm/damon/design.rst b/Documentation/mm/damon/design.rst
> > index afc7d52bda2f7..01047e8431d17 100644
> > --- a/Documentation/mm/damon/design.rst
> > +++ b/Documentation/mm/damon/design.rst
> > @@ -565,6 +565,27 @@ interface <sysfs_interface>`, refer to :ref:`weights <sysfs_quotas>` part of
> > the documentation.
> >
> >
> > +.. _damon_design_damos_quotas_failed_memory_charging_ratio:
> > +
> > +Action-failed Memory Charging Ratio
> > +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
> > +
> > +DAMOS action to a given region can fail for sub subsets of the memory of the
>
> This isn't a bug, but should "sub subsets" just be "some subsets" or
> "subsets"?
Good eye. I will fix this in the next revision.
>
> > +region. For example, if the action is ``pageout`` and the region has some
> > +unreclaimable pages, applying the action to the pages will fail. The amount of
> > +system resource that is taken for such failed action applications is usually
> > +different from that for successful action applications. For such cases, users
> > +can set different charging ratio for such failed memory. The ratio can be
> > +specified using ``fail_charge_num`` and ``fail_charge_denom`` parameters. The
> > +two parameters represent the numerator and denominator of the ratio.
> > +
> > +For example, let's suppose a DAMOS action is applied to a region of 1 GiB size.
> > +The action is successfully applied to only 700 MiB of the region.
> > +``fail_charge_num`` and ``fail_charge_denom`` are set to ``1`` and ``1024``,
> > +respectively. Then only 700 MiB and 300 KiB of size (``700 MiB + 300 MiB * 1 /
> > +1024``) will be charged.
>
> Is this calculation accurate? Since 1 GiB is 1024 MiB, if 700 MiB are
> successfully applied, the remaining failed region would be 324 MiB rather
> than 300 MiB. Does the example intentionally use 1 GiB = 1000 MiB, or should
> the remaining portion be updated to 324 MiB?
Nice finding. I will fix this in the next revision.
Thanks,
SJ
>
>
> # end of sashiko.dev inline review
> # review url: https://sashiko.dev/#/patchset/20260404163943.89278-4-sj@kernel.org
# hkml [1] generated a draft of this mail. You can regenerate
# this using below command:
#
# hkml patch sashiko_dev --for_forwarding \
# 20260404163943.89278-4-sj@kernel.org
#
# [1] https://github.com/sjp38/hackermail
^ permalink raw reply
* Re: [PATCH v5 1/1] mm/damon: add node_eligible_mem_bp and node_ineligible_mem_bp goal metrics
From: SeongJae Park @ 2026-04-04 20:01 UTC (permalink / raw)
To: Ravi Jonnalagadda
Cc: SeongJae Park, damon, linux-mm, linux-kernel, linux-doc, akpm,
corbet, bijan311, ajayjoshi, honggyu.kim, yunjeong.mun,
kernel test robot
In-Reply-To: <20260404012215.1539-2-ravis.opensrc@gmail.com>
On Fri, 3 Apr 2026 18:22:15 -0700 Ravi Jonnalagadda <ravis.opensrc@gmail.com> wrote:
> Add new quota goal metrics for memory tiering that track scheme-eligible
> memory distribution across NUMA nodes:
>
> - DAMOS_QUOTA_NODE_ELIGIBLE_MEM_BP: ratio of eligible memory on a node
> - DAMOS_QUOTA_NODE_INELIGIBLE_MEM_BP: ratio of ineligible memory on a
> node
>
> These complementary metrics enable push-pull migration schemes that
> maintain a target memory distribution across different NUMA nodes
> representing different memory tiers, based on access patterns defined
> by each scheme.
>
> The metrics iterate scheme-eligible regions and use damon_get_folio()
> to determine NUMA node placement of each folio, calculating the ratio
> of eligible memory on the specified node versus total eligible memory.
>
> The implementation is guarded by CONFIG_DAMON_PADDR since damon_get_folio()
> is only available when physical address space monitoring is enabled.
>
> Suggested-by: SeongJae Park <sj@kernel.org>
> Reported-by: kernel test robot <lkp@intel.com>
> Closes: https://lore.kernel.org/oe-kbuild-all/202603251034.978zcsQ2-lkp@intel.com/
I think you don't need to add the Reported-by: and Closes: for bug that found
before this patch is merged. Could you please drop those?
> Signed-off-by: Ravi Jonnalagadda <ravis.opensrc@gmail.com>
> ---
> include/linux/damon.h | 6 ++
> mm/damon/core.c | 186 ++++++++++++++++++++++++++++++++++++---
> mm/damon/sysfs-schemes.c | 12 +++
> 3 files changed, 190 insertions(+), 14 deletions(-)
[...]
The code changes look good to me. But, as I replied to the cover letter, I'd
like to get sashiko review before giving my Reviewed-by:. Could you please
rebase this to the latest mm-new and repost for getting the sashiko review?
Thanks,
SJ
^ permalink raw reply
* Re: [PATCH v5 0/1] mm/damon: add node_eligible_mem_bp and node_ineligible_mem_bp goal metrics
From: SeongJae Park @ 2026-04-04 19:53 UTC (permalink / raw)
To: Ravi Jonnalagadda
Cc: SeongJae Park, damon, linux-mm, linux-kernel, linux-doc, akpm,
corbet, bijan311, ajayjoshi, honggyu.kim, yunjeong.mun
In-Reply-To: <20260404012215.1539-1-ravis.opensrc@gmail.com>
Hello Ravi,
On Fri, 3 Apr 2026 18:22:14 -0700 Ravi Jonnalagadda <ravis.opensrc@gmail.com> wrote:
> This patch introduces two new DAMOS quota goal metrics for controlling
> memory distribution in heterogeneous memory systems (e.g., DRAM and CXL
> memory tiering) using physical address (PA) mode monitoring.
>
> Changes since v4:
> =================
> https://lore.kernel.org/linux-mm/20260320190453.1430-1-ravis.opensrc@gmail.com/
>
> - Fixed commit message description for DAMOS_QUOTA_NODE_INELIGIBLE_MEM_BP
> per review feedback
> - Added clarifying comment for ops-common.h include (for damon_get_folio())
> - Fixed build error when CONFIG_DAMON_PADDR is disabled by adding
> #ifdef CONFIG_DAMON_PADDR guards around functions using damon_get_folio()
> - Dropped RFC tag per maintainer feedback
Thank you for revisioning while addressing my comments on the previous version!
>
> This patch is based on top of damon/next.
Maybe because of this, sashiko was unable to review this.
To my understanding, there is no real reason to make this based on damon/next.
And I'd like to get sashiko review for this patch. Could you please rebase
this to latest mm-new and repost for that?
>
> Background and Motivation
> =========================
>
> In heterogeneous memory systems, controlling memory distribution across
> NUMA nodes is essential for performance optimization. This patch enables
> system-wide page distribution with target-state goals such as "maintain
> 30% of scheme-eligible memory on CXL" using PA-mode DAMON schemes.
[...]
Other than the lack of the sashiko review, this cover letter looks good to me.
Thanks,
SJ
[...]
^ permalink raw reply
* Re: [PATCH v5 0/3] PCI Controller event and LTSSM tracepoint support
From: Manivannan Sadhasivam @ 2026-04-04 16:53 UTC (permalink / raw)
To: Steven Rostedt, Shawn Lin
Cc: Bjorn Helgaas, linux-rockchip, linux-pci, linux-trace-kernel,
linux-doc
In-Reply-To: <1774403912-210670-1-git-send-email-shawn.lin@rock-chips.com>
On Wed, Mar 25, 2026 at 09:58:29AM +0800, Shawn Lin wrote:
>
> This patch-set adds new pci controller event and LTSSM tracepoint used by host drivers
> which provide LTSSM trace functionality. The first user is pcie-dw-rockchip with a 256
> Bytes FIFO for recording LTSSM transition.
>
Steve, could you please take a look at the tracing part?
- Mani
> Testing
> =========
>
> This series was tested on RK3588/RK3588s EVB1 with NVMe SSD connected to PCIe3 and PCIe2
> root ports.
>
> echo 1 > /sys/kernel/debug/tracing/events/pci_controller/pcie_ltssm_state_transition/enable
> cat /sys/kernel/debug/tracing/trace_pipe
>
> # tracer: nop
> #
> # entries-in-buffer/entries-written: 64/64 #P:8
> #
> # _-----=> irqs-off/BH-disabled
> # / _----=> need-resched
> # | / _---=> hardirq/softirq
> # || / _--=> preempt-depth
> # ||| / _-=> migrate-disable
> # |||| / delay
> # TASK-PID CPU# ||||| TIMESTAMP FUNCTION
> # | | | ||||| | |
> kworker/0:0-9 [000] ..... 5.600194: pcie_ltssm_state_transition: dev: a40000000.pcie state: DETECT_ACT rate: Unknown
> kworker/0:0-9 [000] ..... 5.600198: pcie_ltssm_state_transition: dev: a40000000.pcie state: DETECT_WAIT rate: Unknown
> kworker/0:0-9 [000] ..... 5.600199: pcie_ltssm_state_transition: dev: a40000000.pcie state: DETECT_ACT rate: Unknown
> kworker/0:0-9 [000] ..... 5.600201: pcie_ltssm_state_transition: dev: a40000000.pcie state: POLL_ACTIVE rate: Unknown
> kworker/0:0-9 [000] ..... 5.600202: pcie_ltssm_state_transition: dev: a40000000.pcie state: POLL_CONFIG rate: Unknown
> kworker/0:0-9 [000] ..... 5.600204: pcie_ltssm_state_transition: dev: a40000000.pcie state: CFG_LINKWD_START rate: Unknown
> kworker/0:0-9 [000] ..... 5.600206: pcie_ltssm_state_transition: dev: a40000000.pcie state: CFG_LINKWD_ACEPT rate: Unknown
> kworker/0:0-9 [000] ..... 5.600207: pcie_ltssm_state_transition: dev: a40000000.pcie state: CFG_LANENUM_WAI rate: Unknown
> kworker/0:0-9 [000] ..... 5.600208: pcie_ltssm_state_transition: dev: a40000000.pcie state: CFG_LANENUM_ACEPT rate: Unknown
> kworker/0:0-9 [000] ..... 5.600210: pcie_ltssm_state_transition: dev: a40000000.pcie state: CFG_COMPLETE rate: Unknown
> kworker/0:0-9 [000] ..... 5.600212: pcie_ltssm_state_transition: dev: a40000000.pcie state: CFG_IDLE rate: Unknown
> kworker/0:0-9 [000] ..... 5.600213: pcie_ltssm_state_transition: dev: a40000000.pcie state: L0 rate: 2.5 GT/s
> kworker/0:0-9 [000] ..... 5.600214: pcie_ltssm_state_transition: dev: a40000000.pcie state: RCVRY_LOCK rate: Unknown
> kworker/0:0-9 [000] ..... 5.600216: pcie_ltssm_state_transition: dev: a40000000.pcie state: RCVRY_RCVRCFG rate: Unknown
> kworker/0:0-9 [000] ..... 5.600217: pcie_ltssm_state_transition: dev: a40000000.pcie state: RCVRY_SPEED rate: Unknown
> kworker/0:0-9 [000] ..... 5.600218: pcie_ltssm_state_transition: dev: a40000000.pcie state: RCVRY_LOCK rate: Unknown
> kworker/0:0-9 [000] ..... 5.600220: pcie_ltssm_state_transition: dev: a40000000.pcie state: RCVRY_EQ1 rate: Unknown
> kworker/0:0-9 [000] ..... 5.600221: pcie_ltssm_state_transition: dev: a40000000.pcie state: RCVRY_EQ2 rate: 8.0 GT/s
> kworker/0:0-9 [000] ..... 5.600222: pcie_ltssm_state_transition: dev: a40000000.pcie state: RCVRY_EQ3 rate: 8.0 GT/s
> kworker/0:0-9 [000] ..... 5.600224: pcie_ltssm_state_transition: dev: a40000000.pcie state: RCVRY_LOCK rate: 8.0 GT/s
> kworker/0:0-9 [000] ..... 5.600225: pcie_ltssm_state_transition: dev: a40000000.pcie state: RCVRY_RCVRCFG rate: 8.0 GT/s
> kworker/0:0-9 [000] ..... 5.600226: pcie_ltssm_state_transition: dev: a40000000.pcie state: RCVRY_IDLE rate: 8.0 GT/s
> kworker/0:0-9 [000] ..... 5.600227: pcie_ltssm_state_transition: dev: a40000000.pcie state: L0 rate: 8.0 GT/s
> kworker/0:0-9 [000] ..... 5.600228: pcie_ltssm_state_transition: dev: a40000000.pcie state: RCVRY_LOCK rate: 8.0 GT/s
> kworker/0:0-9 [000] ..... 5.600229: pcie_ltssm_state_transition: dev: a40000000.pcie state: RCVRY_RCVRCFG rate: 8.0 GT/s
> kworker/0:0-9 [000] ..... 5.600231: pcie_ltssm_state_transition: dev: a40000000.pcie state: RCVRY_IDLE rate: 8.0 GT/s
> kworker/0:0-9 [000] ..... 5.600232: pcie_ltssm_state_transition: dev: a40000000.pcie state: L0 rate: 8.0 GT/s
> kworker/0:0-9 [000] ..... 5.600233: pcie_ltssm_state_transition: dev: a40000000.pcie state: L123_SEND_EIDLE rate: 8.0 GT/s
> kworker/0:0-9 [000] ..... 5.600234: pcie_ltssm_state_transition: dev: a40000000.pcie state: L1_IDLE rate: 8.0 GT/s
> kworker/0:0-9 [000] ..... 5.600236: pcie_ltssm_state_transition: dev: a40000000.pcie state: RCVRY_LOCK rate: 8.0 GT/s
> kworker/0:0-9 [000] ..... 5.600237: pcie_ltssm_state_transition: dev: a40000000.pcie state: RCVRY_RCVRCFG rate: 8.0 GT/s
> kworker/0:0-9 [000] ..... 5.600238: pcie_ltssm_state_transition: dev: a40000000.pcie state: RCVRY_IDLE rate: 8.0 GT/s
> kworker/0:0-9 [000] ..... 5.600239: pcie_ltssm_state_transition: dev: a40000000.pcie state: L0 rate: 8.0 GT/s
>
>
> Changes in v5:
> - rebase
> - use EM/EMe instead
> - remove reg/unreg function and back to use TRACE_EVENT
> - use trace_pcie_ltssm_state_transition_enabled()
>
> Changes in v4:
> - use TRACE_EVENT_FN to notify when to start and stop the tracepoint,
> and export pci_ltssm_tp_enabled() for host drivers to use
> - skip trace if pci_ltssm_tp_enabled() is false.(Steven)
> - wrap into 80 columns(Bjorn)
>
> Changes in v3:
> - add TRACE_DEFINE_ENUM for all enums(Steven Rostedt)
> - Add toctree entry in Documentation/trace/index.rst(Bagas Sanjaya)
> - fix mismatch section underline length(Bagas Sanjaya)
> - Make example snippets in code block(Bagas Sanjaya)
> - warp context into 80 columns and fix the file name(Bjorn)
> - reorder variables(Mani)
> - rename loop to i; rename en to enable(Mani)
> - use FIELD_GET(Mani)
> - add comment about how the FIFO works(Mani)
>
> Changes in v2:
> - use tracepoint
>
> Shawn Lin (3):
> PCI: trace: Add PCI controller LTSSM transition tracepoint
> Documentation: tracing: Add PCI controller event documentation
> PCI: dw-rockchip: Add pcie_ltssm_state_transition trace support
>
> Documentation/trace/events-pci-controller.rst | 42 ++++++++++
> Documentation/trace/index.rst | 1 +
> drivers/pci/controller/dwc/pcie-dw-rockchip.c | 111 ++++++++++++++++++++++++++
> drivers/pci/trace.c | 1 +
> include/trace/events/pci_controller.h | 58 ++++++++++++++
> 5 files changed, 213 insertions(+)
> create mode 100644 Documentation/trace/events-pci-controller.rst
> create mode 100644 include/trace/events/pci_controller.h
>
> --
> 2.7.4
>
--
மணிவண்ணன் சதாசிவம்
^ permalink raw reply
* [RFC PATCH 4/9] Docs/admin-guide/mm/damon/usage: document fail_charge_{num,denom} files
From: SeongJae Park @ 2026-04-04 16:39 UTC (permalink / raw)
Cc: SeongJae Park, Liam R. Howlett, Andrew Morton, David Hildenbrand,
Jonathan Corbet, Lorenzo Stoakes, Michal Hocko, Mike Rapoport,
Shuah Khan, Suren Baghdasaryan, Vlastimil Babka, damon, linux-doc,
linux-kernel, linux-mm
In-Reply-To: <20260404163943.89278-1-sj@kernel.org>
Update DAMON usage document for the DAMOS action failed regions quota
charge ratio control sysfs files.
Signed-off-by: SeongJae Park <sj@kernel.org>
---
Documentation/admin-guide/mm/damon/usage.rst | 18 ++++++++++++++----
1 file changed, 14 insertions(+), 4 deletions(-)
diff --git a/Documentation/admin-guide/mm/damon/usage.rst b/Documentation/admin-guide/mm/damon/usage.rst
index bfdb717441f05..ffb32bdbe34ff 100644
--- a/Documentation/admin-guide/mm/damon/usage.rst
+++ b/Documentation/admin-guide/mm/damon/usage.rst
@@ -84,7 +84,9 @@ comma (",").
│ │ │ │ │ │ │ │ sz/min,max
│ │ │ │ │ │ │ │ nr_accesses/min,max
│ │ │ │ │ │ │ │ age/min,max
- │ │ │ │ │ │ │ :ref:`quotas <sysfs_quotas>`/ms,bytes,reset_interval_ms,effective_bytes,goal_tuner
+ │ │ │ │ │ │ │ :ref:`quotas <sysfs_quotas>`/ms,bytes,reset_interval_ms,
+ │ │ │ │ │ │ │ effective_bytes,goal_tuner,
+ │ │ │ │ │ │ │ fail_charge_num,fail_charge_denom
│ │ │ │ │ │ │ │ weights/sz_permil,nr_accesses_permil,age_permil
│ │ │ │ │ │ │ │ :ref:`goals <sysfs_schemes_quota_goals>`/nr_goals
│ │ │ │ │ │ │ │ │ 0/target_metric,target_value,current_value,nid,path
@@ -381,9 +383,10 @@ schemes/<N>/quotas/
The directory for the :ref:`quotas <damon_design_damos_quotas>` of the given
DAMON-based operation scheme.
-Under ``quotas`` directory, five files (``ms``, ``bytes``,
-``reset_interval_ms``, ``effective_bytes`` and ``goal_tuner``) and two
-directories (``weights`` and ``goals``) exist.
+Under ``quotas`` directory, seven files (``ms``, ``bytes``,
+``reset_interval_ms``, ``effective_bytes``, ``goal_tuner``, ``fail_charge_num``
+and ``fail_charge_denom``) and two directories (``weights`` and ``goals``)
+exist.
You can set the ``time quota`` in milliseconds, ``size quota`` in bytes, and
``reset interval`` in milliseconds by writing the values to the three files,
@@ -402,6 +405,13 @@ the background design of the feature and the name of the selectable algorithms.
Refer to :ref:`goals directory <sysfs_schemes_quota_goals>` for the goals
setup.
+You can set the action-failed memory quota charging ratio by writing the
+numerator and the denominator for the ratio to ``fail_charge_num`` and
+`fail_charge_denom`` files, respectively. Reading those file will return the
+current set values. Refer to :ref:`design
+<damon_design_damos_quotas_failed_memory_charging_ratio>` for more details of
+the ratio feature.
+
The time quota is internally transformed to a size quota. Between the
transformed size quota and user-specified size quota, smaller one is applied.
Based on the user-specified :ref:`goal <sysfs_schemes_quota_goals>`, the
--
2.47.3
^ permalink raw reply related
* [RFC PATCH 3/9] Docs/mm/damon/design: document fail_charge_{num,denom}
From: SeongJae Park @ 2026-04-04 16:39 UTC (permalink / raw)
Cc: SeongJae Park, Liam R. Howlett, Andrew Morton, David Hildenbrand,
Jonathan Corbet, Lorenzo Stoakes, Michal Hocko, Mike Rapoport,
Shuah Khan, Suren Baghdasaryan, Vlastimil Babka, damon, linux-doc,
linux-kernel, linux-mm
In-Reply-To: <20260404163943.89278-1-sj@kernel.org>
Update DAMON design document for the DAMOS action failed region quota
charge ratio.
Signed-off-by: SeongJae Park <sj@kernel.org>
---
Documentation/mm/damon/design.rst | 21 +++++++++++++++++++++
1 file changed, 21 insertions(+)
diff --git a/Documentation/mm/damon/design.rst b/Documentation/mm/damon/design.rst
index 510ec6375178d..58a72bd26dc11 100644
--- a/Documentation/mm/damon/design.rst
+++ b/Documentation/mm/damon/design.rst
@@ -572,6 +572,27 @@ interface <sysfs_interface>`, refer to :ref:`weights <sysfs_quotas>` part of
the documentation.
+.. _damon_design_damos_quotas_failed_memory_charging_ratio:
+
+Action-failed Memory Charging Ratio
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+DAMOS action to a given region can fail for sub subsets of the memory of the
+region. For example, if the action is ``pageout`` and the region has some
+unreclaimable pages, applying the action to the pages will fail. The amount of
+system resource that is taken for such failed action applications is usually
+different from that for successful action applications. For such cases, users
+can set different charging ratio for such failed memory. The ratio can be
+specified using ``fail_charge_num`` and ``fail_charge_denom`` parameters. The
+two parameters represent the numerator and denominator of the ratio.
+
+For example, let's suppose a DAMOS action is applied to a region of 1 GiB size.
+The action is successfully applied to only 700 MiB of the region.
+``fail_charge_num`` and ``fail_charge_denom`` are set to ``1`` and ``1024``,
+respectively. Then only 700 MiB and 300 KiB of size (``700 MiB + 300 MiB * 1 /
+1024``) will be charged.
+
+
.. _damon_design_damos_quotas_auto_tuning:
Aim-oriented Feedback-driven Auto-tuning
--
2.47.3
^ permalink raw reply related
* [RFC PATCH 0/9] mm/damon: introduce DAMOS failed region quota charge ratio
From: SeongJae Park @ 2026-04-04 16:39 UTC (permalink / raw)
Cc: SeongJae Park, Liam R. Howlett, Andrew Morton, Brendan Higgins,
David Gow, David Hildenbrand, Jonathan Corbet, Lorenzo Stoakes,
Michal Hocko, Mike Rapoport, Shuah Khan, Shuah Khan,
Suren Baghdasaryan, Vlastimil Babka, damon, kunit-dev, linux-doc,
linux-kernel, linux-kselftest, linux-mm
TL; DR: Let users set different DAMOS quota charge ratios for DAMOS
action failed regions, for deterministic and consistent DAMOS action
progress.
Common Reports: Unexpectedly Slow DAMOS
=======================================
One common issue report that we get from DAMON users is that DAMOS
action applying progress speed is sometimes much slower than expected.
And one common root cause is that the DAMOS quota is exceeded by the
action applying failed memory regions.
For example, a group of users tried to run DAMOS-based proactive memory
reclamation (DAMON_RECLAIM) with 100 MiB per second DAMOS quota. They
ran it on a system having no active workload which means all memory of
the system is cold. The expectation was that the system will show 100
MiB per second reclamation until (nearly) all memory is reclaimed. But
what they found is that the speed is quite inconsistent and sometimes it
becomes very slower than the expectation, sometimes even no reclamation
at all for about tens of seconds. The upper limit of the speed (100 MiB
per second) was being kept as expected, though.
By monitoring the qt_exceeds (number of DAMOS quota exceed events) DAMOS
stat, we found DAMOS quota is always exceeded when the speed is slow. By
monitoring sz_tried and sz_applied (the total amount of DAMOS action
tried memory and succeeded memory) DAMOS stats together, we found the
reclamation attempts nearly always failed when the speed is slow.
DAMOS quota charges DAMOS action tried regions regardless of the
successfulness of the try. Hence in the example reported case, there
was unreclaimable memory spread around the system memory. Sometimes
nearly 100 MiB of memory that DAMOS tried to reclaim in the given quota
interval was reclaimable, and therefore showed nearly 100 MiB per second
speed. Sometimes nearly 99 MiB of memory that DAMOS was trying to
reclaim in the given quota interval was unreclaimable, and therefore
showing only about 1 MiB per second reclaim speed.
We explained it is an expected behavior of the feature rather than a
bug, as DAMOS quota is there for only the upper-limit of the speed. The
users agreed and later reported a huge win from the adoption of
DAMON_RECLAIM on their products.
It is Not a Bug but a Feature; But...
=====================================
So nothing is broken. DAMOS quota is working as intended, as the upper
limit of the speed. It also provides its behavior observability via
DAMOS stat. In the real world production environment that runs long
term active workloads and matters stability, the speed sometimes being
slow is not a real problem.
But, the non-deterministic behavior is sometimes annoying, especially in
lab environments. Even in a realistic production environment, when
there is a huge amount of DAMOS action unapplicable memory, the speed
could be problematically slow. Let's suppose a virtual machines
provider that setup 99% of the host memory as hugetlb pages that cannot
be reclaimed, to give it to virtual machines. Also, when aim-oriented
DAMOS auto-tuning is applied, this could also make the internal feedback
loop confused.
The intention of the current behavior was that trying DAMOS action to
regions would anyway impose some overhead, and therefore somehow be
charged. But in the real world, the overhead for failed action is much
lighter than successful action. Charging those at the same ratio may be
unfair, or at least suboptimum in some environments.
DAMOS Action Failed Region Quota Charge Ratio
=============================================
Let users set the charge ratio for the action-failed memory, for more
optimal and deterministic use of DAMOS. It allows users to specify the
numerator and the denominator of the ratio for flexible setup. For
example, let's suppose the numerator and the denominator are set to 1
and 4,096, respectively. The ratio is 1 / 4,096. A DAMOS scheme action
is applied to 5 GiB memory. For 1 GiB of the memory, the action is
succeeded. For the rest (4 GiB), the action is failed. Then, only 1
GiB and 1 MiB quota is charged.
The optimal charge ratio will depend on the use case and
system/workload. I'd recommend starting from setting the nominator as 1
and the denominator as PAGE_SIZE and tune based on the results, because
many DAMOS actions are applied at page level.
Tests
=====
I tested this feature in the steps below.
1. Allocate 50% of system memory and mlock() it using a test program.
2. Fill up the page cache to exhaust nearly all free memory.
3. Start DAMON-based proactive reclamation with 100 MiB/second DAMOS
hard-quota. Auto-tune the DAMOS soft-quota under the hard-quota for
achieving 40% free memory of the system with 'temporal' tuner.
For step 1, I run a simple C program that is written by Gemini. It is
quite straightforward, so I'm not sharing the code here.
For step 2, I use dd command like below:
dd if=/dev/zero of=foo bs=1M count=$50_percent_of_system_memory
For step 3, I use the latest version of DAMON user-space tool (damo)
like below.
sudo damo start --damos_action pageout \
` # Do the pageout only up to 100 MiB per second ` \
--damos_quota_space 100M --damos_quota_interval 1s \
` # Auto-tune the quota below the hard quota aiming` \
` # 40% free memory of the node 0 ` \
` # (entire node of the test system)` \
--damos_quota_goal node_mem_free_bp 40% 0 \
` # use temporal tuner, which is easy to understnd ` \
--damos_quota_goal_tuner temporal
As expected, the progress of the reclamation is not consistent, because
the quota is exceeded for the failed reclamation of the unreclaimable
memory.
I do this again, but with the failed region charge ratio feature. For
this, the above 'damo' command is used, after appending command line
option for setup of the charge ratio like below. Note that the option
was added to 'damo' after v3.1.9.
sudo ./damo start --damos_action pageout \
[...]
` # quota-charge only 1/4096 for pageout-failed regions ` \
--damos_quota_fail_charge_ratio 1 4096
The progress of the reclamation was nearly 100 MiB per second until the
goal was achieved, meeting the expectation.
Patches Sequence
================
Patch 1 implements the feature and exposes it via DAMON core API.
Patch 2 implements DAMON sysfs ABI for the feature. Three following
patches (3-5) document the feature and ABI on design, usage, and ABI
documents, respectively. Four patches for testing of the new feature
follow. Patch 6 implements a kunit test for the feature. Patches 7
and 8 extend DAMON selftest helpers for DAMON sysfs control and internal
state dumping for adding a new selftest for the feature. Patch 9
extends existing DAMON sysfs interface selftest to test the new feature
using the extended helper scripts.
SeongJae Park (9):
mm/damon/core: introduce failed region quota charge ratio
mm/damon/sysfs-schemes: implement fail_charge_{num,denom} files
Docs/mm/damon/design: document fail_charge_{num,denom}
Docs/admin-guide/mm/damon/usage: document fail_charge_{num,denom}
files
Docs/ABI/damon: document fail_charge_{num,denom}
mm/damon/tests/core-kunit: test fail_charge_{num,denom} committing
selftets/damon/_damon_sysfs: support failed region quota charge ratio
selftests/damon/drgn_dump_damon_status: support failed region quota
charge ratio
selftets/damon/sysfs.py: test failed region quota charge ratio
.../ABI/testing/sysfs-kernel-mm-damon | 12 +++++
Documentation/admin-guide/mm/damon/usage.rst | 18 +++++--
Documentation/mm/damon/design.rst | 21 ++++++++
include/linux/damon.h | 9 ++++
mm/damon/core.c | 9 +++-
mm/damon/sysfs-schemes.c | 54 +++++++++++++++++++
mm/damon/tests/core-kunit.h | 6 +++
tools/testing/selftests/damon/_damon_sysfs.py | 21 +++++++-
.../selftests/damon/drgn_dump_damon_status.py | 2 +
tools/testing/selftests/damon/sysfs.py | 6 +++
10 files changed, 151 insertions(+), 7 deletions(-)
base-commit: 9e634d6813be2e3d1cb023a0b83619fd2bcdd13b
--
2.47.3
^ permalink raw reply
* Re: [PATCH v6 4/4] iio: adc: ad4691: add SPI offload support
From: David Lechner @ 2026-04-04 15:57 UTC (permalink / raw)
To: radu.sabau, Lars-Peter Clausen, Michael Hennerich,
Jonathan Cameron, Nuno Sá, Andy Shevchenko, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Uwe Kleine-König,
Liam Girdwood, Mark Brown, Linus Walleij, Bartosz Golaszewski,
Philipp Zabel, Jonathan Corbet, Shuah Khan
Cc: linux-iio, devicetree, linux-kernel, linux-pwm, linux-gpio,
linux-doc
In-Reply-To: <20260403-ad4692-multichannel-sar-adc-driver-v6-4-fa2a01a57c4e@analog.com>
On 4/3/26 6:03 AM, Radu Sabau via B4 Relay wrote:
> From: Radu Sabau <radu.sabau@analog.com>
>
> Add SPI offload support to enable DMA-based, CPU-independent data
> acquisition using the SPI Engine offload framework.
>
> When an SPI offload is available (devm_spi_offload_get() succeeds),
> the driver registers a DMA engine IIO buffer and uses dedicated buffer
> setup operations. If no offload is available the existing software
> triggered buffer path is used unchanged.
>
> Both CNV Burst Mode and Manual Mode support offload, but use different
> trigger mechanisms:
>
> CNV Burst Mode: the SPI Engine is triggered by the ADC's DATA_READY
> signal on the GP pin specified by the trigger-source consumer reference
> in the device tree (one cell = GP pin number 0-3). For this mode the
> driver acts as both an SPI offload consumer (DMA RX stream, message
> optimization) and a trigger source provider: it registers the
> GP/DATA_READY output via devm_spi_offload_trigger_register() so the
> offload framework can match the '#trigger-source-cells' phandle and
> automatically fire the SPI Engine DMA transfer at end-of-conversion.
>
> Manual Mode: the SPI Engine is triggered by a periodic trigger at
> the configured sampling frequency. The pre-built SPI message uses
> the pipelined CNV-on-CS protocol: N+1 4-byte transfers are issued
> for N active channels (the first result is discarded as garbage from
> the pipeline flush) and the remaining N results are captured by DMA.
>
> All offload transfers use 32-bit frames (bits_per_word=32, len=4) for
> DMA word alignment. This patch promotes the channel scan_type from
> storagebits=16 (triggered-buffer path) to storagebits=32 to match the
> DMA word size; the triggered-buffer paths are updated to the same layout
> for consistency. CNV Burst Mode channel data arrives in the lower 16
> bits of the 32-bit word (shift=0); Manual Mode data arrives in the upper
> 16 bits (shift=16), matching the 4-byte SPI transfer layout
> [data_hi, data_lo, 0, 0]. A separate ad4691_manual_channels[] array
> encodes the shift=16 scan type for manual mode.
>
> Add driver documentation under Documentation/iio/ad4691.rst covering
> operating modes, oversampling, reference voltage, SPI offload paths,
> and buffer data layout; register in MAINTAINERS and index.rst
>
> Kconfig gains a dependency on IIO_BUFFER_DMAENGINE.
>
> Signed-off-by: Radu Sabau <radu.sabau@analog.com>
> ---
> Documentation/iio/ad4691.rst | 259 ++++++++++++++++++++++++++
> Documentation/iio/index.rst | 1 +
> MAINTAINERS | 1 +
> drivers/iio/adc/Kconfig | 1 +
> drivers/iio/adc/ad4691.c | 422 ++++++++++++++++++++++++++++++++++++++++++-
> 5 files changed, 676 insertions(+), 8 deletions(-)
>
...
> ANALOG DEVICES INC AD4695 DRIVER
> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
> index d498f16c0816..93f090e9a562 100644
> --- a/drivers/iio/adc/Kconfig
> +++ b/drivers/iio/adc/Kconfig
> @@ -144,6 +144,7 @@ config AD4691
> depends on SPI
> select IIO_BUFFER
> select IIO_TRIGGERED_BUFFER
> + select IIO_BUFFER_DMAENGINE
> select REGMAP
> help
> Say yes here to build support for Analog Devices AD4691 Family MuxSAR
> diff --git a/drivers/iio/adc/ad4691.c b/drivers/iio/adc/ad4691.c
> index f2a7273e43b9..cc2138e47feb 100644
> --- a/drivers/iio/adc/ad4691.c
> +++ b/drivers/iio/adc/ad4691.c
> @@ -11,6 +11,7 @@
> #include <linux/delay.h>
> #include <linux/dev_printk.h>
> #include <linux/device/devres.h>
> +#include <linux/dmaengine.h>
> #include <linux/err.h>
> #include <linux/interrupt.h>
> #include <linux/math.h>
> @@ -22,10 +23,14 @@
> #include <linux/regulator/consumer.h>
> #include <linux/reset.h>
> #include <linux/spi/spi.h>
> +#include <linux/spi/offload/consumer.h>
> +#include <linux/spi/offload/provider.h>
> #include <linux/units.h>
> #include <linux/unaligned.h>
>
> #include <linux/iio/buffer.h>
> +#include <linux/iio/buffer-dma.h>
> +#include <linux/iio/buffer-dmaengine.h>
> #include <linux/iio/iio.h>
> #include <linux/iio/sysfs.h>
> #include <linux/iio/trigger.h>
> @@ -40,6 +45,7 @@
> #define AD4691_VREF_4P096_uV_MAX 4500000
>
> #define AD4691_CNV_DUTY_CYCLE_NS 380
> +#define AD4691_CNV_HIGH_TIME_NS 430
>
> #define AD4691_SPI_CONFIG_A_REG 0x000
> #define AD4691_SW_RESET (BIT(7) | BIT(0))
> @@ -92,6 +98,8 @@
> #define AD4691_ACC_IN(n) (0x252 + (3 * (n)))
> #define AD4691_ACC_STS_DATA(n) (0x283 + (4 * (n)))
>
> +#define AD4691_OFFLOAD_BITS_PER_WORD 32
> +
> static const char * const ad4691_supplies[] = { "avdd", "vio" };
>
> enum ad4691_ref_ctrl {
> @@ -109,6 +117,31 @@ struct ad4691_chip_info {
> unsigned int max_rate;
> };
>
> +enum {
> + AD4691_SCAN_TYPE_NORMAL, /* triggered buffer: storagebits=16, shift=0 */
> + AD4691_SCAN_TYPE_OFFLOAD_CNV, /* CNV burst offload: storagebits=32, shift=0 */
> + AD4691_SCAN_TYPE_OFFLOAD_MANUAL, /* manual offload: storagebits=32, shift=16 */
> +};
> +
> +static const struct iio_scan_type ad4691_scan_types[] = {
> + [AD4691_SCAN_TYPE_NORMAL] = {
> + .sign = 'u',
> + .realbits = 16,
> + .storagebits = 16,
> + },
> + [AD4691_SCAN_TYPE_OFFLOAD_CNV] = {
> + .sign = 'u',
> + .realbits = 16,
> + .storagebits = 32,
> + },
> + [AD4691_SCAN_TYPE_OFFLOAD_MANUAL] = {
> + .sign = 'u',
> + .realbits = 16,
> + .storagebits = 32,
> + .shift = 16,
> + },
> +};
> +
> #define AD4691_CHANNEL(ch) \
> { \
> .type = IIO_VOLTAGE, \
> @@ -122,11 +155,9 @@ struct ad4691_chip_info {
> .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SCALE), \
> .channel = ch, \
> .scan_index = ch, \
> - .scan_type = { \
> - .sign = 'u', \
> - .realbits = 16, \
> - .storagebits = 16, \
> - }, \
> + .has_ext_scan_type = 1, \
> + .ext_scan_type = ad4691_scan_types, \
> + .num_ext_scan_type = ARRAY_SIZE(ad4691_scan_types), \
Usually, we just make two separte ad4691_chip_info structs for offload
vs. not offload.
ext_scan_type is generally only used when the scan type can change
dynamically after probe.
> }
>
> static const struct iio_chan_spec ad4691_channels[] = {
> @@ -221,6 +252,17 @@ static const struct ad4691_chip_info ad4694_chip_info = {
> .max_rate = 1 * HZ_PER_MHZ,
> };
>
> +struct ad4691_offload_state {
> + struct spi_offload *spi;
> + struct spi_offload_trigger *trigger;
> + u64 trigger_hz;
> + struct spi_message msg;
> + /* Max 16 channel xfers + 1 state-reset or NOOP */
> + struct spi_transfer xfer[17];
> + u8 tx_cmd[17][4];
> + u8 tx_reset[4];
> +};
> +
> struct ad4691_state {
> const struct ad4691_chip_info *info;
> struct regmap *regmap;
> @@ -251,6 +293,8 @@ struct ad4691_state {
> struct spi_transfer *scan_xfers;
> __be16 *scan_tx;
> __be16 *scan_rx;
> + /* NULL when no SPI offload hardware is present */
> + struct ad4691_offload_state *offload;
> /* Scan buffer: one slot per channel plus timestamp */
> struct {
> u16 vals[16];
> @@ -273,6 +317,46 @@ static int ad4691_gpio_setup(struct ad4691_state *st, unsigned int gp_num)
> AD4691_GP_MODE_DATA_READY << shift);
> }
>
> +static const struct spi_offload_config ad4691_offload_config = {
> + .capability_flags = SPI_OFFLOAD_CAP_TRIGGER |
> + SPI_OFFLOAD_CAP_RX_STREAM_DMA,
> +};
> +
> +static bool ad4691_offload_trigger_match(struct spi_offload_trigger *trigger,
> + enum spi_offload_trigger_type type,
> + u64 *args, u32 nargs)
> +{
> + return type == SPI_OFFLOAD_TRIGGER_DATA_READY &&
> + nargs == 1 && args[0] <= 3;
> +}
> +
> +static int ad4691_offload_trigger_request(struct spi_offload_trigger *trigger,
> + enum spi_offload_trigger_type type,
> + u64 *args, u32 nargs)
> +{
> + struct ad4691_state *st = spi_offload_trigger_get_priv(trigger);
> +
> + if (nargs != 1)
> + return -EINVAL;
> +
> + return ad4691_gpio_setup(st, (unsigned int)args[0]);
Should be fine to leave out the cast here.
> +}
> +
> +static int ad4691_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 ad4691_offload_trigger_ops = {
> + .match = ad4691_offload_trigger_match,
> + .request = ad4691_offload_trigger_request,
> + .validate = ad4691_offload_trigger_validate,
> +};
> +
> static int ad4691_reg_read(void *context, unsigned int reg, unsigned int *val)
> {
> struct spi_device *spi = context;
> @@ -553,10 +637,17 @@ static int ad4691_read_raw(struct iio_dev *indio_dev,
> case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
> *val = st->osr[chan->scan_index];
> return IIO_VAL_INT;
> - case IIO_CHAN_INFO_SCALE:
> + case IIO_CHAN_INFO_SCALE: {
> + const struct iio_scan_type *scan_type;
> +
> + scan_type = iio_get_current_scan_type(indio_dev, chan);
> + if (IS_ERR(scan_type))
> + return PTR_ERR(scan_type);
> +
> *val = st->vref_uV / (MICRO / MILLI);
> - *val2 = chan->scan_type.realbits;
> + *val2 = scan_type->realbits;
> return IIO_VAL_FRACTIONAL_LOG2;
> + }
> default:
> return -EINVAL;
> }
> @@ -856,6 +947,213 @@ static const struct iio_buffer_setup_ops ad4691_cnv_burst_buffer_setup_ops = {
> .postdisable = &ad4691_cnv_burst_buffer_postdisable,
> };
>
> +static int ad4691_manual_offload_buffer_postenable(struct iio_dev *indio_dev)
> +{
> + struct ad4691_state *st = iio_priv(indio_dev);
> + struct ad4691_offload_state *offload = st->offload;
> + struct device *dev = regmap_get_device(st->regmap);
> + struct spi_device *spi = to_spi_device(dev);
> + struct spi_offload_trigger_config config = {
> + .type = SPI_OFFLOAD_TRIGGER_PERIODIC,
> + };
> + unsigned int bit, k;
> + int ret;
> +
> + ret = ad4691_enter_conversion_mode(st);
> + if (ret)
> + return ret;
> +
> + memset(offload->xfer, 0, sizeof(offload->xfer));
> +
> + /*
> + * N+1 transfers for N channels. Each CS-low period triggers
> + * a conversion AND returns the previous result (pipelined).
> + * TX: [AD4691_ADC_CHAN(n), 0x00, 0x00, 0x00]
> + * RX: [data_hi, data_lo, 0x00, 0x00] (shift=16)
> + * Transfer 0 RX is garbage; transfers 1..N carry real data.
> + */
> + k = 0;
> + iio_for_each_active_channel(indio_dev, bit) {
> + offload->tx_cmd[k][0] = AD4691_ADC_CHAN(bit);
> + offload->xfer[k].tx_buf = offload->tx_cmd[k];
> + offload->xfer[k].len = sizeof(offload->tx_cmd[k]);
> + offload->xfer[k].bits_per_word = AD4691_OFFLOAD_BITS_PER_WORD;
> + offload->xfer[k].cs_change = 1;
> + offload->xfer[k].cs_change_delay.value = AD4691_CNV_HIGH_TIME_NS;
> + offload->xfer[k].cs_change_delay.unit = SPI_DELAY_UNIT_NSECS;
> + /* First transfer RX is garbage — skip it. */
> + if (k > 0)
> + offload->xfer[k].offload_flags = SPI_OFFLOAD_XFER_RX_STREAM;
> + k++;
> + }
> +
> + /* Final NOOP to flush pipeline and capture last channel. */
> + offload->tx_cmd[k][0] = AD4691_NOOP;
> + offload->xfer[k].tx_buf = offload->tx_cmd[k];
> + offload->xfer[k].len = sizeof(offload->tx_cmd[k]);
> + offload->xfer[k].bits_per_word = AD4691_OFFLOAD_BITS_PER_WORD;
> + offload->xfer[k].offload_flags = SPI_OFFLOAD_XFER_RX_STREAM;
> + k++;
> +
> + spi_message_init_with_transfers(&offload->msg, offload->xfer, k);
> + offload->msg.offload = offload->spi;
> +
> + ret = spi_optimize_message(spi, &offload->msg);
> + if (ret)
> + goto err_exit_conversion;
> +
> + config.periodic.frequency_hz = offload->trigger_hz;
> + ret = spi_offload_trigger_enable(offload->spi, offload->trigger, &config);
> + if (ret)
> + goto err_unoptimize;
> +
> + return 0;
> +
> +err_unoptimize:
> + spi_unoptimize_message(&offload->msg);
> +err_exit_conversion:
> + ad4691_exit_conversion_mode(st);
> + return ret;
> +}
> +
> +static int ad4691_manual_offload_buffer_predisable(struct iio_dev *indio_dev)
> +{
> + struct ad4691_state *st = iio_priv(indio_dev);
> + struct ad4691_offload_state *offload = st->offload;
> +
> + spi_offload_trigger_disable(offload->spi, offload->trigger);
> + spi_unoptimize_message(&offload->msg);
> +
> + return ad4691_exit_conversion_mode(st);
> +}
> +
> +static const struct iio_buffer_setup_ops ad4691_manual_offload_buffer_setup_ops = {
> + .postenable = &ad4691_manual_offload_buffer_postenable,
> + .predisable = &ad4691_manual_offload_buffer_predisable,
> +};
> +
> +static int ad4691_cnv_burst_offload_buffer_postenable(struct iio_dev *indio_dev)
> +{
> + struct ad4691_state *st = iio_priv(indio_dev);
> + struct ad4691_offload_state *offload = st->offload;
> + struct device *dev = regmap_get_device(st->regmap);
> + struct spi_device *spi = to_spi_device(dev);
> + struct spi_offload_trigger_config config = {
> + .type = SPI_OFFLOAD_TRIGGER_DATA_READY,
> + };
> + unsigned int n_active = bitmap_weight(indio_dev->active_scan_mask,
> + iio_get_masklength(indio_dev));
> + unsigned int bit, k;
> + int ret;
> +
> + ret = regmap_write(st->regmap, AD4691_STD_SEQ_CONFIG,
> + bitmap_read(indio_dev->active_scan_mask, 0,
> + iio_get_masklength(indio_dev)));
> + if (ret)
> + return ret;
> +
> + ret = regmap_write(st->regmap, AD4691_ACC_MASK_REG,
> + ~bitmap_read(indio_dev->active_scan_mask, 0,
> + iio_get_masklength(indio_dev)) & GENMASK(15, 0));
> + if (ret)
> + return ret;
> +
> + iio_for_each_active_channel(indio_dev, bit) {
> + ret = regmap_write(st->regmap, AD4691_ACC_DEPTH_IN(bit),
> + st->osr[bit]);
> + if (ret)
> + return ret;
> + }
> +
> + ret = ad4691_enter_conversion_mode(st);
> + if (ret)
> + return ret;
> +
> + memset(offload->xfer, 0, sizeof(offload->xfer));
> +
> + /*
> + * N transfers to read N AVG_IN registers plus one state-reset
> + * transfer (no RX) to re-arm DATA_READY.
> + * TX: [reg_hi | 0x80, reg_lo, 0x00, 0x00]
> + * RX: [0x00, 0x00, data_hi, data_lo] (shift=0)
> + */
> + k = 0;
> + iio_for_each_active_channel(indio_dev, bit) {
> + unsigned int reg = AD4691_AVG_IN(bit);
> +
> + offload->tx_cmd[k][0] = (reg >> 8) | 0x80;
> + offload->tx_cmd[k][1] = reg & 0xFF;
Can we use put_unaligned_be16()?
> + offload->xfer[k].tx_buf = offload->tx_cmd[k];
> + offload->xfer[k].len = sizeof(offload->tx_cmd[k]);
> + offload->xfer[k].bits_per_word = AD4691_OFFLOAD_BITS_PER_WORD;
> + offload->xfer[k].offload_flags = SPI_OFFLOAD_XFER_RX_STREAM;
> + if (k < n_active - 1)
> + offload->xfer[k].cs_change = 1;
> + k++;
> + }
> +
> + /* State reset to re-arm DATA_READY for the next scan. */
> + offload->tx_reset[0] = AD4691_STATE_RESET_REG >> 8;
> + offload->tx_reset[1] = AD4691_STATE_RESET_REG & 0xFF;
ditto.
> + offload->tx_reset[2] = AD4691_STATE_RESET_ALL;
> + offload->xfer[k].tx_buf = offload->tx_reset;
> + offload->xfer[k].len = sizeof(offload->tx_reset);
> + offload->xfer[k].bits_per_word = AD4691_OFFLOAD_BITS_PER_WORD;
> + k++;
> +
> + spi_message_init_with_transfers(&offload->msg, offload->xfer, k);
> + offload->msg.offload = offload->spi;
> +
> + ret = spi_optimize_message(spi, &offload->msg);
> + if (ret)
> + goto err_exit_conversion;
> +
> + ret = ad4691_sampling_enable(st, true);
> + if (ret)
> + goto err_unoptimize;
> +
> + ret = spi_offload_trigger_enable(offload->spi, offload->trigger, &config);
> + if (ret)
> + goto err_sampling_disable;
> +
> + return 0;
> +
> +err_sampling_disable:
> + ad4691_sampling_enable(st, false);
> +err_unoptimize:
> + spi_unoptimize_message(&offload->msg);
> +err_exit_conversion:
> + ad4691_exit_conversion_mode(st);
> + return ret;
> +}
> +
> +static int ad4691_cnv_burst_offload_buffer_predisable(struct iio_dev *indio_dev)
> +{
> + struct ad4691_state *st = iio_priv(indio_dev);
> + struct ad4691_offload_state *offload = st->offload;
> + int ret;
> +
> + spi_offload_trigger_disable(offload->spi, offload->trigger);
> +
> + ret = ad4691_sampling_enable(st, false);
> + if (ret)
> + return ret;
> +
> + ret = regmap_write(st->regmap, AD4691_STD_SEQ_CONFIG,
> + AD4691_SEQ_ALL_CHANNELS_OFF);
> + if (ret)
> + return ret;
> +
> + spi_unoptimize_message(&offload->msg);
> +
> + return ad4691_exit_conversion_mode(st);
> +}
> +
> +static const struct iio_buffer_setup_ops ad4691_cnv_burst_offload_buffer_setup_ops = {
> + .postenable = &ad4691_cnv_burst_offload_buffer_postenable,
> + .predisable = &ad4691_cnv_burst_offload_buffer_predisable,
> +};
> +
> static ssize_t sampling_frequency_show(struct device *dev,
> struct device_attribute *attr,
> char *buf)
> @@ -863,6 +1161,9 @@ static ssize_t sampling_frequency_show(struct device *dev,
> struct iio_dev *indio_dev = dev_to_iio_dev(dev);
> struct ad4691_state *st = iio_priv(indio_dev);
>
> + if (st->manual_mode && st->offload)
> + return sysfs_emit(buf, "%llu\n", st->offload->trigger_hz);
> +
> return sysfs_emit(buf, "%u\n", (u32)(NSEC_PER_SEC / st->cnv_period_ns));
> }
>
> @@ -883,6 +1184,20 @@ static ssize_t sampling_frequency_store(struct device *dev,
> if (iio_buffer_enabled(indio_dev))
> return -EBUSY;
>
> + if (st->manual_mode && st->offload) {
> + struct spi_offload_trigger_config config = {
> + .type = SPI_OFFLOAD_TRIGGER_PERIODIC,
> + .periodic = { .frequency_hz = freq },
> + };
Same comment as other patches. This needs to account for oversampling ratio.
> +
> + ret = spi_offload_trigger_validate(st->offload->trigger, &config);
> + if (ret)
> + return ret;
> +
> + st->offload->trigger_hz = config.periodic.frequency_hz;
> + return len;
> + }
> +
> ret = ad4691_set_pwm_freq(st, freq);
> if (ret)
> return ret;
> @@ -968,10 +1283,23 @@ static irqreturn_t ad4691_trigger_handler(int irq, void *p)
> return IRQ_HANDLED;
> }
>
> +static int ad4691_get_current_scan_type(const struct iio_dev *indio_dev,
> + const struct iio_chan_spec *chan)
> +{
> + struct ad4691_state *st = iio_priv(indio_dev);
> +
> + if (!st->offload)
> + return AD4691_SCAN_TYPE_NORMAL;
> + if (st->manual_mode)
> + return AD4691_SCAN_TYPE_OFFLOAD_MANUAL;
> + return AD4691_SCAN_TYPE_OFFLOAD_CNV;
> +}
> +
> static const struct iio_info ad4691_info = {
> .read_raw = &ad4691_read_raw,
> .write_raw = &ad4691_write_raw,
> .read_avail = &ad4691_read_avail,
> + .get_current_scan_type = &ad4691_get_current_scan_type,
> .debugfs_reg_access = &ad4691_reg_access,
> };
>
> @@ -1195,9 +1523,75 @@ static int ad4691_setup_triggered_buffer(struct iio_dev *indio_dev,
> &ad4691_manual_buffer_setup_ops);
> }
>
> +static int ad4691_setup_offload(struct iio_dev *indio_dev,
> + struct ad4691_state *st,
> + struct spi_offload *spi_offload)
> +{
> + struct device *dev = regmap_get_device(st->regmap);
> + struct ad4691_offload_state *offload;
> + struct dma_chan *rx_dma;
> + int ret;
> +
> + offload = devm_kzalloc(dev, sizeof(*offload), GFP_KERNEL);
> + if (!offload)
> + return -ENOMEM;
> +
> + offload->spi = spi_offload;
> + st->offload = offload;
> +
> + if (st->manual_mode) {
> + offload->trigger =
> + devm_spi_offload_trigger_get(dev, offload->spi,
> + SPI_OFFLOAD_TRIGGER_PERIODIC);
> + if (IS_ERR(offload->trigger))
> + return dev_err_probe(dev, PTR_ERR(offload->trigger),
> + "Failed to get periodic offload trigger\n");
> +
> + offload->trigger_hz = st->info->max_rate;
I think I mentioned this elsewhere, but can we really get max_rate in manual mode
due to the extra SPI overhead? Probably safer to start with a lower rate.
> + } else {
> + struct spi_offload_trigger_info trigger_info = {
> + .fwnode = dev_fwnode(dev),
> + .ops = &ad4691_offload_trigger_ops,
> + .priv = st,
> + };
> +
> + ret = devm_spi_offload_trigger_register(dev, &trigger_info);
> + if (ret)
> + return dev_err_probe(dev, ret,
> + "Failed to register offload trigger\n");
> +
> + offload->trigger =
> + devm_spi_offload_trigger_get(dev, offload->spi,
> + SPI_OFFLOAD_TRIGGER_DATA_READY);
> + if (IS_ERR(offload->trigger))
> + return dev_err_probe(dev, PTR_ERR(offload->trigger),
> + "Failed to get DATA_READY offload trigger\n");
> + }
> +
> + rx_dma = devm_spi_offload_rx_stream_request_dma_chan(dev, offload->spi);
> + if (IS_ERR(rx_dma))
> + return dev_err_probe(dev, PTR_ERR(rx_dma),
> + "Failed to get offload RX DMA channel\n");
> +
> + if (st->manual_mode)
> + indio_dev->setup_ops = &ad4691_manual_offload_buffer_setup_ops;
> + else
> + indio_dev->setup_ops = &ad4691_cnv_burst_offload_buffer_setup_ops;
> +
> + ret = devm_iio_dmaengine_buffer_setup_with_handle(dev, indio_dev, rx_dma,
> + IIO_BUFFER_DIRECTION_IN);
> + if (ret)
> + return ret;
> +
> + indio_dev->buffer->attrs = ad4691_buffer_attrs;
Should including ad4691_buffer_attrs depend on st->manual_mode?
I thought it was only used when PWM is connected to CNV.
> +
> + return 0;
> +}
> +
> static int ad4691_probe(struct spi_device *spi)
> {
> struct device *dev = &spi->dev;
> + struct spi_offload *spi_offload;
> struct iio_dev *indio_dev;
> struct ad4691_state *st;
> int ret;
> @@ -1232,6 +1626,13 @@ static int ad4691_probe(struct spi_device *spi)
> if (ret)
> return ret;
>
> + spi_offload = devm_spi_offload_get(dev, spi, &ad4691_offload_config);
> + ret = PTR_ERR_OR_ZERO(spi_offload);
> + if (ret == -ENODEV)
> + spi_offload = NULL;
> + else if (ret)
> + return dev_err_probe(dev, ret, "Failed to get SPI offload\n");
> +
> indio_dev->name = st->info->name;
> indio_dev->info = &ad4691_info;
> indio_dev->modes = INDIO_DIRECT_MODE;
> @@ -1239,7 +1640,10 @@ static int ad4691_probe(struct spi_device *spi)
> indio_dev->channels = st->info->channels;
> indio_dev->num_channels = st->info->num_channels;
As mentioned earlier, we generally want separate channel structs
for SPI offload. These will also have different num_channels because
there is no timestamp channel in SPI offload.
>
> - ret = ad4691_setup_triggered_buffer(indio_dev, st);
> + if (spi_offload)
> + ret = ad4691_setup_offload(indio_dev, st, spi_offload);
> + else
> + ret = ad4691_setup_triggered_buffer(indio_dev, st);
> if (ret)
> return ret;
>
> @@ -1277,3 +1681,5 @@ module_spi_driver(ad4691_driver);
> MODULE_AUTHOR("Radu Sabau <radu.sabau@analog.com>");
> MODULE_DESCRIPTION("Analog Devices AD4691 Family ADC Driver");
> MODULE_LICENSE("GPL");
> +MODULE_IMPORT_NS("IIO_DMA_BUFFER");
> +MODULE_IMPORT_NS("IIO_DMAENGINE_BUFFER");
>
^ permalink raw reply
* Re: [PATCH v6 4/4] iio: adc: ad4691: add SPI offload support
From: David Lechner @ 2026-04-04 15:34 UTC (permalink / raw)
To: radu.sabau, Lars-Peter Clausen, Michael Hennerich,
Jonathan Cameron, Nuno Sá, Andy Shevchenko, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Uwe Kleine-König,
Liam Girdwood, Mark Brown, Linus Walleij, Bartosz Golaszewski,
Philipp Zabel, Jonathan Corbet, Shuah Khan
Cc: linux-iio, devicetree, linux-kernel, linux-pwm, linux-gpio,
linux-doc
In-Reply-To: <20260403-ad4692-multichannel-sar-adc-driver-v6-4-fa2a01a57c4e@analog.com>
On 4/3/26 6:03 AM, Radu Sabau via B4 Relay wrote:
> From: Radu Sabau <radu.sabau@analog.com>
>
> Add SPI offload support to enable DMA-based, CPU-independent data
> acquisition using the SPI Engine offload framework.
>
> When an SPI offload is available (devm_spi_offload_get() succeeds),
> the driver registers a DMA engine IIO buffer and uses dedicated buffer
> setup operations. If no offload is available the existing software
> triggered buffer path is used unchanged.
>
> Both CNV Burst Mode and Manual Mode support offload, but use different
> trigger mechanisms:
>
> CNV Burst Mode: the SPI Engine is triggered by the ADC's DATA_READY
> signal on the GP pin specified by the trigger-source consumer reference
> in the device tree (one cell = GP pin number 0-3). For this mode the
> driver acts as both an SPI offload consumer (DMA RX stream, message
> optimization) and a trigger source provider: it registers the
> GP/DATA_READY output via devm_spi_offload_trigger_register() so the
> offload framework can match the '#trigger-source-cells' phandle and
> automatically fire the SPI Engine DMA transfer at end-of-conversion.
>
> Manual Mode: the SPI Engine is triggered by a periodic trigger at
> the configured sampling frequency. The pre-built SPI message uses
> the pipelined CNV-on-CS protocol: N+1 4-byte transfers are issued
> for N active channels (the first result is discarded as garbage from
> the pipeline flush) and the remaining N results are captured by DMA.
>
> All offload transfers use 32-bit frames (bits_per_word=32, len=4) for
> DMA word alignment. This patch promotes the channel scan_type from
> storagebits=16 (triggered-buffer path) to storagebits=32 to match the
> DMA word size; the triggered-buffer paths are updated to the same layout
> for consistency. CNV Burst Mode channel data arrives in the lower 16
> bits of the 32-bit word (shift=0); Manual Mode data arrives in the upper
> 16 bits (shift=16), matching the 4-byte SPI transfer layout
> [data_hi, data_lo, 0, 0]. A separate ad4691_manual_channels[] array
> encodes the shift=16 scan type for manual mode.
>
> Add driver documentation under Documentation/iio/ad4691.rst covering
> operating modes, oversampling, reference voltage, SPI offload paths,
> and buffer data layout; register in MAINTAINERS and index.rst
Documentation should be separate patch. It covers more than just SPI
offload.
>
> Kconfig gains a dependency on IIO_BUFFER_DMAENGINE.
>
> Signed-off-by: Radu Sabau <radu.sabau@analog.com>
> ---
> Documentation/iio/ad4691.rst | 259 ++++++++++++++++++++++++++
> Documentation/iio/index.rst | 1 +
> MAINTAINERS | 1 +
> drivers/iio/adc/Kconfig | 1 +
> drivers/iio/adc/ad4691.c | 422 ++++++++++++++++++++++++++++++++++++++++++-
> 5 files changed, 676 insertions(+), 8 deletions(-)
>
> diff --git a/Documentation/iio/ad4691.rst b/Documentation/iio/ad4691.rst
> new file mode 100644
> index 000000000000..36f0c841605a
> --- /dev/null
> +++ b/Documentation/iio/ad4691.rst
> @@ -0,0 +1,259 @@
> +.. SPDX-License-Identifier: GPL-2.0-only
> +
> +=============
> +AD4691 driver
> +=============
> +
> +ADC driver for Analog Devices Inc. AD4691 family of multichannel SAR ADCs.
> +The module name is ``ad4691``.
> +
> +
> +Supported devices
> +=================
> +
> +The following chips are supported by this driver:
> +
> +* `AD4691 <https://www.analog.com/en/products/ad4691.html>`_ — 16-channel, 500 kSPS
> +* `AD4692 <https://www.analog.com/en/products/ad4692.html>`_ — 16-channel, 1 MSPS
> +* `AD4693 <https://www.analog.com/en/products/ad4693.html>`_ — 8-channel, 500 kSPS
> +* `AD4694 <https://www.analog.com/en/products/ad4694.html>`_ — 8-channel, 1 MSPS
> +
> +
> +IIO channels
> +============
> +
> +Each physical ADC input maps to one IIO voltage channel. The AD4691 and AD4692
> +expose 16 channels (``voltage0`` through ``voltage15``); the AD4693 and AD4694
> +expose 8 channels (``voltage0`` through ``voltage7``).
> +
> +All channels share a common scale (``in_voltage_scale``), derived from the
> +reference voltage. Each channel independently exposes:
> +
> +* ``in_voltageN_raw`` — single-shot ADC result
> +* ``in_voltageN_sampling_frequency`` — internal oscillator frequency used for
As mentioned in another patch, sampling_frquency != osciallator frequency when
oversampling ratio != 1. So this needs to be changed to reflect that.
> + single-shot reads and CNV Burst Mode buffered captures
> +* ``in_voltageN_sampling_frequency_available`` — list of valid oscillator
> + frequencies
> +* ``in_voltageN_oversampling_ratio`` — per-channel hardware accumulation depth
> +* ``in_voltageN_oversampling_ratio_available`` — list of valid ratios
> +
> +
> +Operating modes
> +===============
> +
> +The driver supports two operating modes, auto-detected from the device tree at
> +probe time. Both modes transition to and from an internal Autonomous Mode idle
> +state when the IIO buffer is enabled and disabled.
> +
> +Manual Mode
> +-----------
> +
> +Selected when no ``pwms`` property is present in the device tree. The CNV pin
> +is tied to the SPI chip-select: every CS assertion both triggers a new
> +conversion and returns the result of the previous one (pipelined N+1 scheme).
> +
> +To read N channels the driver issues N+1 SPI transfers in a single optimised
> +message:
> +
> +* Transfers 0 to N-1 each carry ``AD4691_ADC_CHAN(n)`` in the TX byte to
> + select the next channel; the RX byte of transfer ``k+1`` contains the result
> + of the channel selected in transfer ``k``.
> +* Transfer N is a NOOP (0x00) to flush the last conversion result out of the
> + pipeline.
> +
> +The external IIO trigger (``pollfunc_store_time``) drives the trigger handler,
I'm not sure "external" is the best word to describe this. I would say a "user-
defined IIO triger (e.g. hrtimer trigger)".
> +which executes the pre-built SPI message and pushes the scan to the buffer.
> +
> +CNV Burst Mode
> +--------------
> +
> +Selected when a ``pwms`` property is present in the device tree. The PWM drives
> +the CNV pin independently of SPI at the configured conversion rate, and a GP
> +pin (identified by ``interrupt-names``) asserts DATA_READY at end-of-burst to
> +signal that the AVG_IN result registers are ready to be read.
> +
> +The IRQ handler stops the PWM, fires the IIO trigger, and the trigger handler
If we stop the PWM after an IRQ, then we don't get a consistent sample rate.
Ideally, we would leave the PWM running and just pick a rate slow enough that
there is plenty of time to read the data. Otherwise, this mode doesn't seem
particularly useful.
> +reads all active ``AVG_IN(n)`` registers in a single optimised SPI message and
> +pushes the scan to the buffer.
> +
> +The buffer sampling frequency (i.e. the PWM rate) is controlled by the
> +``sampling_frequency`` attribute on the IIO buffer. Valid values span from the
> +chip's minimum oscillator rate up to its maximum conversion rate
> +(500 kSPS for AD4691/AD4693, 1 MSPS for AD4692/AD4694).
Valid, but not usable without SPI offload.
> +
> +Autonomous Mode (idle / single-shot)
> +-------------------------------------
> +
> +The chip idles in Autonomous Mode whenever the IIO buffer is disabled. In this
> +state, ``read_raw`` requests (``in_voltageN_raw``) use the internal oscillator
> +to perform a single conversion on the requested channel and read back the
> +result from the ``AVG_IN(N)`` register. The oscillator is started and stopped
> +for each read to save power.
> +
> +
> +Oversampling
> +============
> +
> +Each channel has an independent hardware accumulator (ACC_DEPTH_IN) that
> +averages a configurable number of successive conversions before DATA_READY
> +asserts. The result is always returned as a 16-bit mean from the ``AVG_IN``
> +register, so the IIO ``realbits`` and ``storagebits`` are unaffected by the
> +oversampling ratio.
> +
> +Valid ratios are 1, 2, 4, 8, 16 and 32. The default is 1 (no averaging).
> +
> +.. code-block:: bash
> +
> + # Set oversampling ratio to 16 on channel 0
> + echo 16 > /sys/bus/iio/devices/iio:device0/in_voltage0_oversampling_ratio
> +
> +When OSR > 1 the effective conversion rate for ``read_raw`` is reduced
> +accordingly, since the driver waits for 2 × OSR oscillator periods before
> +reading the result.
> +
> +
> +Reference voltage
> +=================
> +
> +The driver supports two reference configurations, mutually exclusive:
> +
> +* **External reference** (``ref-supply``): a voltage between 2.4 V and 5.25 V
> + supplied externally. The internal reference buffer is disabled.
> +* **Buffered internal reference** (``refin-supply``): An internal reference
> + buffer is used. The driver enables ``REFBUF_EN`` in the REF_CTRL register
> + when this supply is used.
> +
> +Exactly one of ``ref-supply`` or ``refin-supply`` must be present in the
> +device tree.
> +
> +The reference voltage determines the full-scale range:
> +
> +.. code-block::
> +
> + full-scale = Vref / 2^16 (per LSB)
> +
> +
> +LDO supply
> +==========
> +
> +The chip contains an internal LDO that powers part of the analog front-end.
> +The LDO input can be driven externally via the ``ldo-in-supply`` regulator. If
> +that supply is absent, the driver enables the internal LDO path (``LDO_EN``
> +bit in DEVICE_SETUP).
> +
> +
> +Reset
> +=====
> +
> +The driver supports two reset mechanisms:
> +
> +* **Hardware reset** (``reset-gpios`` in device tree): the GPIO is already
> + asserted at driver probe by the reset controller framework. The driver waits
> + for the required 300 µs reset pulse width and then deasserts.
> +* **Software reset** (fallback when ``reset-gpios`` is absent): the driver
> + writes the software-reset pattern to the SPI_CONFIG_A register.
> +
> +
> +GP pins and interrupts
> +======================
> +
> +The chip exposes up to four general-purpose (GP) pins that can be configured as
> +interrupt outputs. In CNV Burst Mode (non-offload), one GP pin must be wired to
Or trigger sources.
> +an interrupt-capable SoC input and declared in the device tree using the
> +``interrupts`` and ``interrupt-names`` properties.
> +
> +The ``interrupt-names`` value identifies which GP pin is used (``"gp0"``
> +through ``"gp3"``). The driver configures that pin as a DATA_READY output in
> +the GPIO_MODE register.
> +
> +Example device tree fragment::
> +
> + adc@0 {
> + compatible = "adi,ad4692";
> + ...
> + interrupts = <17 IRQ_TYPE_LEVEL_HIGH>;
> + interrupt-parent = <&gpio0>;
> + interrupt-names = "gp0";
> + };
> +
> +
> +SPI offload support
> +===================
> +
> +When a SPI offload engine (e.g. the AXI SPI Engine) is present, the driver
> +uses DMA-backed transfers for CPU-independent, high-throughput data capture.
> +SPI offload is detected automatically at probe via ``devm_spi_offload_get()``;
> +if no offload hardware is available the driver falls back to the software
> +triggered-buffer path.
> +
> +Two SPI offload sub-modes exist, corresponding to the two operating modes:
> +
> +CNV Burst offload
> +-----------------
> +
> +Used when a ``pwms`` property is present and SPI offload is available.
> +
> +The PWM drives CNV at the configured rate. On DATA_READY the SPI offload
> +engine automatically executes a pre-built message that reads all active
> +``AVG_IN`` registers and streams the data directly to an IIO DMA buffer with
> +no CPU involvement. A final state-reset transfer re-arms DATA_READY for the
> +next burst.
> +
> +The GP pin used as DATA_READY trigger is supplied by the trigger-source
> +consumer (via ``#trigger-source-cells``) at buffer enable time; no
> +``interrupt-names`` entry is required in this path.
> +
> +The buffer sampling frequency is controlled by the ``sampling_frequency``
> +attribute on the IIO buffer (same as the non-offload CNV Burst path).
> +
> +Manual offload
> +--------------
> +
> +Used when no ``pwms`` property is present and SPI offload is available.
> +
> +A periodic SPI offload trigger controls the conversion rate. On each trigger
> +period, the SPI engine executes an N+1 transfer message (same pipelined scheme
How does this work with oversampling?
> +as software Manual Mode) and streams the data directly to the IIO DMA buffer.
> +
> +The ``sampling_frequency`` attribute on the IIO buffer controls the trigger
> +rate (in Hz). The default is the chip's maximum conversion rate.
> +
> +
> +Buffer data format
> +==================
> +
> +The IIO buffer data format (``in_voltageN_type``) depends on the active path:
> +
> ++-------------------------+-------------+-------------+-------+
> +| Path | storagebits | realbits | shift |
> ++=========================+=============+=============+=======+
> +| Triggered buffer | 16 | 16 | 0 |
> ++-------------------------+-------------+-------------+-------+
> +| CNV Burst offload (DMA) | 32 | 16 | 0 |
> ++-------------------------+-------------+-------------+-------+
> +| Manual offload (DMA) | 32 | 16 | 16 |
> ++-------------------------+-------------+-------------+-------+
> +
> +In the triggered-buffer path the driver unpacks the 16-bit result in software
> +before pushing to the buffer, so ``storagebits`` is 16.
> +
> +In the DMA offload paths the DMA engine writes 32-bit words directly into the
> +IIO DMA buffer:
> +
> +* **CNV Burst offload**: the SPI engine reads AVG_IN registers with a 2-byte
> + address phase followed by a 2-byte data phase; the 16-bit result lands in
> + the lower half of the 32-bit word (``shift=0``).
> +* **Manual offload**: each 32-bit SPI word carries the channel byte in the
> + first byte; the 16-bit result is returned in the upper half of the 32-bit
I would expect the "first" byte to be in the "upper half" of the 32-bits as
well. This layout could be explained better.
Also, since extra data has to be read in this mode, does this affect the max
conversion rate?
> + word (``shift=16``).
> +
> +The ``in_voltageN_type`` sysfs attribute reflects the active scan type.
> +
> +
> +Unimplemented features
> +======================
> +
> +* GPIO controller functionality of the GP pins
> +* Clamp status and overrange events
> +* Raw accumulator (ACC_IN) and accumulator status registers
> +* ADC_BUSY and overrun status interrupts
^ permalink raw reply
* Re: [PATCH v9 00/10] VMSCAPE optimization for BHI variant
From: David Laight @ 2026-04-04 15:20 UTC (permalink / raw)
To: Pawan Gupta
Cc: x86, Jon Kohler, Nikolay Borisov, H. Peter Anvin, Josh Poimboeuf,
David Kaplan, Sean Christopherson, Borislav Petkov, Dave Hansen,
Peter Zijlstra, Alexei Starovoitov, Daniel Borkmann,
Andrii Nakryiko, KP Singh, Jiri Olsa, David S. Miller,
Andy Lutomirski, Thomas Gleixner, Ingo Molnar, David Ahern,
Martin KaFai Lau, Eduard Zingerman, Song Liu, Yonghong Song,
John Fastabend, Stanislav Fomichev, Hao Luo, Paolo Bonzini,
Jonathan Corbet, linux-kernel, kvm, Asit Mallick, Tao Zhang, bpf,
netdev, linux-doc
In-Reply-To: <20260402-vmscape-bhb-v9-0-94d16bc29774@linux.intel.com>
On Thu, 2 Apr 2026 17:30:32 -0700
Pawan Gupta <pawan.kumar.gupta@linux.intel.com> wrote:
> v9:
> - Use global variables for BHB loop counters instead of ALTERNATIVE-based
> approach. (Dave & others)
> - Use 32-bit registers (%eax/%ecx) for loop counters, loaded via movzbl
> from 8-bit globals. 8-bit registers (e.g. %ah in the inner loop) caused
> performance regression on certain CPUs due to partial-register stalls. (David Laight)
> - Let BPF save/restore %rax/%rcx as in the original implementation, since
> it is the only caller that needs these registers preserved across the
> BHB clearing sequence.
That is as dangerous as hell...
Does BPF even save %rcx - I'm sure I checked that a long time ago
and found it didn't.
(I'm mostly AFK over Easter and can't check.)
A least there should be a blood great big comment that BPF calls this code
and only saves specific registers.
But given the number of mispredicted branches and other pipeline stalls
in this code a couple of register saves to stack are unlikely to make
any difference.
David
^ permalink raw reply
* Re: [PATCH v6 3/4] iio: adc: ad4691: add triggered buffer support
From: David Lechner @ 2026-04-04 15:12 UTC (permalink / raw)
To: radu.sabau, Lars-Peter Clausen, Michael Hennerich,
Jonathan Cameron, Nuno Sá, Andy Shevchenko, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Uwe Kleine-König,
Liam Girdwood, Mark Brown, Linus Walleij, Bartosz Golaszewski,
Philipp Zabel, Jonathan Corbet, Shuah Khan
Cc: linux-iio, devicetree, linux-kernel, linux-pwm, linux-gpio,
linux-doc
In-Reply-To: <20260403-ad4692-multichannel-sar-adc-driver-v6-3-fa2a01a57c4e@analog.com>
On 4/3/26 6:03 AM, Radu Sabau via B4 Relay wrote:
> From: Radu Sabau <radu.sabau@analog.com>
>
> Add buffered capture support using the IIO triggered buffer framework.
>
> CNV Burst Mode: the GP pin identified by interrupt-names in the device
> tree is configured as DATA_READY output. The IRQ handler stops
> conversions and fires the IIO trigger; the trigger handler executes a
> pre-built SPI message that reads all active channels from the AVG_IN
> accumulator registers and then resets accumulator state and restarts
> conversions for the next cycle.
>
> Manual Mode: CNV is tied to SPI CS so each transfer simultaneously
> reads the previous result and starts the next conversion (pipelined
> N+1 scheme). At preenable time a pre-built, optimised SPI message of
> N+1 transfers is constructed (N channel reads plus one NOOP to drain
> the pipeline). The trigger handler executes the message in a single
> spi_sync() call and collects the results. An external trigger (e.g.
> iio-trig-hrtimer) is required to drive the trigger at the desired
> sample rate.
>
> Both modes share the same trigger handler and push a complete scan —
> one u16 slot per channel at its scan_index position, followed by a
> timestamp — to the IIO buffer via iio_push_to_buffers_with_ts().
>
> The CNV Burst Mode sampling frequency (PWM period) is exposed as a
> buffer-level attribute via IIO_DEVICE_ATTR.
>
> Signed-off-by: Radu Sabau <radu.sabau@analog.com>
> ---
> drivers/iio/adc/Kconfig | 2 +
> drivers/iio/adc/ad4691.c | 592 ++++++++++++++++++++++++++++++++++++++++++++++-
> 2 files changed, 592 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
> index 3685a03aa8dc..d498f16c0816 100644
> --- a/drivers/iio/adc/Kconfig
> +++ b/drivers/iio/adc/Kconfig
> @@ -142,6 +142,8 @@ config AD4170_4
> config AD4691
> tristate "Analog Devices AD4691 Family ADC Driver"
> depends on SPI
> + select IIO_BUFFER
> + select IIO_TRIGGERED_BUFFER
> select REGMAP
> help
> Say yes here to build support for Analog Devices AD4691 Family MuxSAR
> diff --git a/drivers/iio/adc/ad4691.c b/drivers/iio/adc/ad4691.c
> index 43bd408c3d11..f2a7273e43b9 100644
> --- a/drivers/iio/adc/ad4691.c
> +++ b/drivers/iio/adc/ad4691.c
> @@ -5,15 +5,19 @@
> */
> #include <linux/array_size.h>
> #include <linux/bitfield.h>
> +#include <linux/bitmap.h>
> #include <linux/bitops.h>
> #include <linux/cleanup.h>
> #include <linux/delay.h>
> #include <linux/dev_printk.h>
> #include <linux/device/devres.h>
> #include <linux/err.h>
> +#include <linux/interrupt.h>
> #include <linux/math.h>
> #include <linux/module.h>
> #include <linux/mod_devicetable.h>
> +#include <linux/property.h>
> +#include <linux/pwm.h>
> #include <linux/regmap.h>
> #include <linux/regulator/consumer.h>
> #include <linux/reset.h>
> @@ -21,7 +25,12 @@
> #include <linux/units.h>
> #include <linux/unaligned.h>
>
> +#include <linux/iio/buffer.h>
> #include <linux/iio/iio.h>
> +#include <linux/iio/sysfs.h>
> +#include <linux/iio/trigger.h>
> +#include <linux/iio/triggered_buffer.h>
> +#include <linux/iio/trigger_consumer.h>
>
> #define AD4691_VREF_uV_MIN 2400000
> #define AD4691_VREF_uV_MAX 5250000
> @@ -30,6 +39,8 @@
> #define AD4691_VREF_3P3_uV_MAX 3750000
> #define AD4691_VREF_4P096_uV_MAX 4500000
>
> +#define AD4691_CNV_DUTY_CYCLE_NS 380
> +
> #define AD4691_SPI_CONFIG_A_REG 0x000
> #define AD4691_SW_RESET (BIT(7) | BIT(0))
>
> @@ -37,6 +48,7 @@
> #define AD4691_CLAMP_STATUS1_REG 0x01A
> #define AD4691_CLAMP_STATUS2_REG 0x01B
> #define AD4691_DEVICE_SETUP 0x020
> +#define AD4691_MANUAL_MODE BIT(2)
> #define AD4691_LDO_EN BIT(4)
> #define AD4691_REF_CTRL 0x021
> #define AD4691_REF_CTRL_MASK GENMASK(4, 2)
> @@ -44,13 +56,18 @@
> #define AD4691_OSC_FREQ_REG 0x023
> #define AD4691_OSC_FREQ_MASK GENMASK(3, 0)
> #define AD4691_STD_SEQ_CONFIG 0x025
> +#define AD4691_SEQ_ALL_CHANNELS_OFF 0x00
> #define AD4691_SPARE_CONTROL 0x02A
>
> +#define AD4691_NOOP 0x00
> +#define AD4691_ADC_CHAN(ch) ((0x10 + (ch)) << 3)
> +
> #define AD4691_OSC_EN_REG 0x180
> #define AD4691_STATE_RESET_REG 0x181
> #define AD4691_STATE_RESET_ALL 0x01
> #define AD4691_ADC_SETUP 0x182
> #define AD4691_ADC_MODE_MASK GENMASK(1, 0)
> +#define AD4691_CNV_BURST_MODE 0x01
> #define AD4691_AUTONOMOUS_MODE 0x02
> /*
> * ACC_MASK_REG covers both mask bytes via ADDR_DESCENDING SPI: writing a
> @@ -60,6 +77,8 @@
> #define AD4691_ACC_DEPTH_IN(n) (0x186 + (n))
> #define AD4691_GPIO_MODE1_REG 0x196
> #define AD4691_GPIO_MODE2_REG 0x197
> +#define AD4691_GP_MODE_MASK GENMASK(3, 0)
> +#define AD4691_GP_MODE_DATA_READY 0x06
> #define AD4691_GPIO_READ 0x1A0
> #define AD4691_ACC_STATUS_FULL1_REG 0x1B0
> #define AD4691_ACC_STATUS_FULL2_REG 0x1B1
> @@ -95,9 +114,11 @@ struct ad4691_chip_info {
> .type = IIO_VOLTAGE, \
> .indexed = 1, \
> .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) \
> - | BIT(IIO_CHAN_INFO_SAMP_FREQ), \
> + | BIT(IIO_CHAN_INFO_SAMP_FREQ) \
> + | BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \
> .info_mask_separate_available = \
> - BIT(IIO_CHAN_INFO_SAMP_FREQ), \
> + BIT(IIO_CHAN_INFO_SAMP_FREQ) \
> + | BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \
> .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SCALE), \
> .channel = ch, \
> .scan_index = ch, \
> @@ -125,6 +146,7 @@ static const struct iio_chan_spec ad4691_channels[] = {
> AD4691_CHANNEL(13),
> AD4691_CHANNEL(14),
> AD4691_CHANNEL(15),
> + IIO_CHAN_SOFT_TIMESTAMP(16),
> };
>
> static const struct iio_chan_spec ad4693_channels[] = {
> @@ -136,6 +158,7 @@ static const struct iio_chan_spec ad4693_channels[] = {
> AD4691_CHANNEL(5),
> AD4691_CHANNEL(6),
> AD4691_CHANNEL(7),
> + IIO_CHAN_SOFT_TIMESTAMP(8),
> };
>
> /*
> @@ -162,6 +185,14 @@ static const int ad4691_osc_freqs_Hz[] = {
> [0xF] = 1250,
> };
>
> +static const char * const ad4691_gp_names[] = { "gp0", "gp1", "gp2", "gp3" };
> +
> +/*
> + * Valid ACC_DEPTH values where the effective divisor equals the count.
> + * From Table 13: ACC_DEPTH = 2^N yields right-shift = N, divisor = 2^N.
> + */
> +static const int ad4691_oversampling_ratios[] = { 1, 2, 4, 8, 16, 32 };
It would be nice to add oversampling in a separate commit as that is a
separate feature.
Oversampling also affects sampling frequency. When there isn't oversampling,
sample rate == conversion rate. However, with oversampling, sample rate ==
conversion rate / oversampling ratio (because each sample involves #OSR
conversions).
So more code will be required to make IIO_CHAN_INFO_SAMP_FREQ attributes
(both read/write_raw and read_avail) adjust the values based on the current
oversampling ratio.
> +
> static const struct ad4691_chip_info ad4691_chip_info = {
> .channels = ad4691_channels,
> .name = "ad4691",
> @@ -193,16 +224,55 @@ static const struct ad4691_chip_info ad4694_chip_info = {
> struct ad4691_state {
> const struct ad4691_chip_info *info;
> struct regmap *regmap;
> +
> + struct pwm_device *conv_trigger;
> + int irq;
> +
> + bool manual_mode;
> +
> int vref_uV;
> + u8 osr[16];
> bool refbuf_en;
> bool ldo_en;
> + u32 cnv_period_ns;
> /*
> * Synchronize access to members of the driver state, and ensure
> * atomicity of consecutive SPI operations.
> */
> struct mutex lock;
> + /*
> + * Per-buffer-enable lifetime resources:
> + * Manual Mode - a pre-built SPI message that clocks out N+1
> + * transfers in one go.
> + * CNV Burst Mode - a pre-built SPI message that clocks out 2*N
> + * transfers in one go.
> + */
> + struct spi_message scan_msg;
> + struct spi_transfer *scan_xfers;
> + __be16 *scan_tx;
> + __be16 *scan_rx;
Why not embed these arrays here? Then we don't have to deal with
alloc/free later.
> + /* Scan buffer: one slot per channel plus timestamp */
> + struct {
> + u16 vals[16];
> + aligned_s64 ts;
> + } scan __aligned(IIO_DMA_MINALIGN);
Better would be IIO_DECLARE_BUFFER_WITH_TS() since we don't always
use all vals.
Also, current usage doesn't need to be DMA-safe because scan_tx
is being used for the actual SPI xfer.
> };
>
> +/*
> + * Configure the given GP pin (0-3) as DATA_READY output.
> + * GP0/GP1 → GPIO_MODE1_REG, GP2/GP3 → GPIO_MODE2_REG.
> + * Even pins occupy bits [3:0], odd pins bits [7:4].
> + */
> +static int ad4691_gpio_setup(struct ad4691_state *st, unsigned int gp_num)
> +{
> + unsigned int shift = 4 * (gp_num % 2);
> +
> + return regmap_update_bits(st->regmap,
> + AD4691_GPIO_MODE1_REG + gp_num / 2,
> + AD4691_GP_MODE_MASK << shift,
> + AD4691_GP_MODE_DATA_READY << shift);
> +}
> +
> static int ad4691_reg_read(void *context, unsigned int reg, unsigned int *val)
> {
> struct spi_device *spi = context;
> @@ -362,6 +432,24 @@ static int ad4691_set_sampling_freq(struct iio_dev *indio_dev, int freq)
> return -EINVAL;
> }
>
> +static int ad4691_set_oversampling_ratio(struct iio_dev *indio_dev,
> + const struct iio_chan_spec *chan,
> + int osr)
> +{
> + struct ad4691_state *st = iio_priv(indio_dev);
> +
> + if (osr < 1 || osr > 32 || !is_power_of_2(osr))
> + return -EINVAL;
> +
> + IIO_DEV_ACQUIRE_DIRECT_MODE(indio_dev, claim);
> + if (IIO_DEV_ACQUIRE_FAILED(claim))
> + return -EBUSY;
> +
> + st->osr[chan->scan_index] = osr;
> + return regmap_write(st->regmap,
> + AD4691_ACC_DEPTH_IN(chan->scan_index), osr);
> +}
> +
> static int ad4691_read_avail(struct iio_dev *indio_dev,
> struct iio_chan_spec const *chan,
> const int **vals, int *type,
> @@ -376,6 +464,11 @@ static int ad4691_read_avail(struct iio_dev *indio_dev,
> *type = IIO_VAL_INT;
> *length = ARRAY_SIZE(ad4691_osc_freqs_Hz) - start;
> return IIO_AVAIL_LIST;
> + case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
> + *vals = ad4691_oversampling_ratios;
> + *type = IIO_VAL_INT;
> + *length = ARRAY_SIZE(ad4691_oversampling_ratios);
> + return IIO_AVAIL_LIST;
> default:
> return -EINVAL;
> }
> @@ -406,6 +499,11 @@ static int ad4691_single_shot_read(struct iio_dev *indio_dev,
> if (ret)
> return ret;
>
> + ret = regmap_write(st->regmap, AD4691_ACC_DEPTH_IN(chan->scan_index),
> + st->osr[chan->scan_index]);
> + if (ret)
> + return ret;
> +
> ret = regmap_read(st->regmap, AD4691_OSC_FREQ_REG, ®_val);
> if (ret)
> return ret;
> @@ -452,6 +550,9 @@ static int ad4691_read_raw(struct iio_dev *indio_dev,
> }
> case IIO_CHAN_INFO_SAMP_FREQ:
> return ad4691_get_sampling_freq(st, val);
> + case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
> + *val = st->osr[chan->scan_index];
> + return IIO_VAL_INT;
> case IIO_CHAN_INFO_SCALE:
> *val = st->vref_uV / (MICRO / MILLI);
> *val2 = chan->scan_type.realbits;
> @@ -468,6 +569,8 @@ static int ad4691_write_raw(struct iio_dev *indio_dev,
> switch (mask) {
> case IIO_CHAN_INFO_SAMP_FREQ:
> return ad4691_set_sampling_freq(indio_dev, val);
> + case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
> + return ad4691_set_oversampling_ratio(indio_dev, chan, val);
> default:
> return -EINVAL;
> }
> @@ -486,6 +589,385 @@ static int ad4691_reg_access(struct iio_dev *indio_dev, unsigned int reg,
> return regmap_write(st->regmap, reg, writeval);
> }
>
> +static int ad4691_set_pwm_freq(struct ad4691_state *st, int freq)
> +{
> + if (!freq)
> + return -EINVAL;
> +
> + st->cnv_period_ns = DIV_ROUND_UP(NSEC_PER_SEC, freq);
> + return 0;
> +}
> +
> +static int ad4691_sampling_enable(struct ad4691_state *st, bool enable)
> +{
> + struct pwm_state conv_state = {
> + .period = st->cnv_period_ns,
> + .duty_cycle = AD4691_CNV_DUTY_CYCLE_NS,
> + .polarity = PWM_POLARITY_NORMAL,
> + .enabled = enable,
> + };
> +
> + return pwm_apply_might_sleep(st->conv_trigger, &conv_state);
> +}
> +
> +/*
> + * ad4691_enter_conversion_mode - Switch the chip to its buffer conversion mode.
> + *
> + * Configures the ADC hardware registers for the mode selected at probe
> + * (CNV_BURST or MANUAL). Called from buffer preenable before starting
> + * sampling. The chip is in AUTONOMOUS mode during idle (for read_raw).
> + */
> +static int ad4691_enter_conversion_mode(struct ad4691_state *st)
> +{
> + int ret;
> +
> + if (st->manual_mode)
> + return regmap_update_bits(st->regmap, AD4691_DEVICE_SETUP,
> + AD4691_MANUAL_MODE, AD4691_MANUAL_MODE);
> +
> + ret = regmap_update_bits(st->regmap, AD4691_ADC_SETUP,
> + AD4691_ADC_MODE_MASK, AD4691_CNV_BURST_MODE);
> + if (ret)
> + return ret;
> +
> + return regmap_write(st->regmap, AD4691_STATE_RESET_REG,
> + AD4691_STATE_RESET_ALL);
> +}
> +
> +/*
> + * ad4691_exit_conversion_mode - Return the chip to AUTONOMOUS mode.
> + *
> + * Called from buffer postdisable to restore the chip to the
> + * idle state used by read_raw. Clears the sequencer and resets state.
> + */
> +static int ad4691_exit_conversion_mode(struct ad4691_state *st)
> +{
> + if (st->manual_mode)
> + return regmap_update_bits(st->regmap, AD4691_DEVICE_SETUP,
> + AD4691_MANUAL_MODE, 0);
> +
> + return regmap_update_bits(st->regmap, AD4691_ADC_SETUP,
> + AD4691_ADC_MODE_MASK, AD4691_AUTONOMOUS_MODE);
> +}
> +
> +static void ad4691_free_scan_bufs(struct ad4691_state *st)
> +{
> + kfree(st->scan_xfers);
> + kfree(st->scan_tx);
> + kfree(st->scan_rx);
> +}
> +
> +static int ad4691_manual_buffer_preenable(struct iio_dev *indio_dev)
> +{
> + struct ad4691_state *st = iio_priv(indio_dev);
> + struct device *dev = regmap_get_device(st->regmap);
> + struct spi_device *spi = to_spi_device(dev);
> + unsigned int n_active = bitmap_weight(indio_dev->active_scan_mask,
> + iio_get_masklength(indio_dev));
> + unsigned int n_xfers = n_active + 1;
> + unsigned int k, i;
> + int ret;
> +
> + st->scan_xfers = kcalloc(n_xfers, sizeof(*st->scan_xfers), GFP_KERNEL);
Usually, we make st->scan_xfers a fixed array with the max number of possible
xfers. Then we don't have to deal with alloc/free.
> + if (!st->scan_xfers)
> + return -ENOMEM;
> +
> + st->scan_tx = kcalloc(n_xfers, sizeof(*st->scan_tx), GFP_KERNEL);
> + if (!st->scan_tx) {
> + kfree(st->scan_xfers);
> + return -ENOMEM;
> + }
> +
> + st->scan_rx = kcalloc(n_xfers, sizeof(*st->scan_rx), GFP_KERNEL);
> + if (!st->scan_rx) {
> + kfree(st->scan_tx);
> + kfree(st->scan_xfers);
> + return -ENOMEM;
> + }
> +
> + spi_message_init(&st->scan_msg);
> +
> + k = 0;
> + iio_for_each_active_channel(indio_dev, i) {
> + st->scan_tx[k] = cpu_to_be16(AD4691_ADC_CHAN(i));
> + st->scan_xfers[k].tx_buf = &st->scan_tx[k];
> + st->scan_xfers[k].rx_buf = &st->scan_rx[k];
> + st->scan_xfers[k].len = sizeof(__be16);
> + st->scan_xfers[k].cs_change = 1;
> + spi_message_add_tail(&st->scan_xfers[k], &st->scan_msg);
> + k++;
> + }
> +
> + /* Final NOOP transfer to retrieve last channel's result. */
> + st->scan_tx[k] = cpu_to_be16(AD4691_NOOP);
> + st->scan_xfers[k].tx_buf = &st->scan_tx[k];
> + st->scan_xfers[k].rx_buf = &st->scan_rx[k];
> + st->scan_xfers[k].len = sizeof(__be16);
> + spi_message_add_tail(&st->scan_xfers[k], &st->scan_msg);
> +
> + st->scan_msg.spi = spi;
This isn't how the SPI framework is intended to be used. We should
have st->spi = spi in probe instead.
> +
> + ret = spi_optimize_message(spi, &st->scan_msg);
> + if (ret) {
> + ad4691_free_scan_bufs(st);
> + return ret;
> + }
> +
> + ret = ad4691_enter_conversion_mode(st);
> + if (ret) {
> + spi_unoptimize_message(&st->scan_msg);
> + ad4691_free_scan_bufs(st);
> + return ret;
> + }
> +
> + return 0;
> +}
> +
> +static int ad4691_manual_buffer_postdisable(struct iio_dev *indio_dev)
> +{
> + struct ad4691_state *st = iio_priv(indio_dev);
> + int ret;
> +
> + ret = ad4691_exit_conversion_mode(st);
> + spi_unoptimize_message(&st->scan_msg);
> + ad4691_free_scan_bufs(st);
> + return ret;
> +}
> +
> +static const struct iio_buffer_setup_ops ad4691_manual_buffer_setup_ops = {
> + .preenable = &ad4691_manual_buffer_preenable,
> + .postdisable = &ad4691_manual_buffer_postdisable,
> +};
> +
> +static int ad4691_cnv_burst_buffer_preenable(struct iio_dev *indio_dev)
> +{
> + struct ad4691_state *st = iio_priv(indio_dev);
> + struct device *dev = regmap_get_device(st->regmap);
> + struct spi_device *spi = to_spi_device(dev);
> + unsigned int n_active = bitmap_weight(indio_dev->active_scan_mask,
> + iio_get_masklength(indio_dev));
> + unsigned int bit, k, i;
> + int ret;
> +
> + st->scan_xfers = kcalloc(2 * n_active, sizeof(*st->scan_xfers), GFP_KERNEL);
> + if (!st->scan_xfers)
> + return -ENOMEM;
> +
> + st->scan_tx = kcalloc(n_active, sizeof(*st->scan_tx), GFP_KERNEL);
> + if (!st->scan_tx) {
> + kfree(st->scan_xfers);
> + return -ENOMEM;
> + }
> +
> + st->scan_rx = kcalloc(n_active, sizeof(*st->scan_rx), GFP_KERNEL);
> + if (!st->scan_rx) {
> + kfree(st->scan_tx);
> + kfree(st->scan_xfers);
> + return -ENOMEM;
> + }
> +
> + spi_message_init(&st->scan_msg);
> +
> + /*
> + * Each AVG_IN read needs two transfers: a 2-byte address write phase
> + * followed by a 2-byte data read phase. CS toggles between channels
> + * (cs_change=1 on the read phase of all but the last channel).
> + */
> + k = 0;
> + iio_for_each_active_channel(indio_dev, i) {
> + st->scan_tx[k] = cpu_to_be16(0x8000 | AD4691_AVG_IN(i));
> + st->scan_xfers[2 * k].tx_buf = &st->scan_tx[k];
> + st->scan_xfers[2 * k].len = sizeof(__be16);
> + spi_message_add_tail(&st->scan_xfers[2 * k], &st->scan_msg);
> + st->scan_xfers[2 * k + 1].rx_buf = &st->scan_rx[k];
> + st->scan_xfers[2 * k + 1].len = sizeof(__be16);
> + if (k < n_active - 1)
> + st->scan_xfers[2 * k + 1].cs_change = 1;
> + spi_message_add_tail(&st->scan_xfers[2 * k + 1], &st->scan_msg);
> + k++;
> + }
> +
> + st->scan_msg.spi = spi;
> +
> + ret = spi_optimize_message(spi, &st->scan_msg);
> + if (ret)
> + goto err_free_bufs;
> +
> + ret = regmap_write(st->regmap, AD4691_STD_SEQ_CONFIG,
> + bitmap_read(indio_dev->active_scan_mask, 0,
> + iio_get_masklength(indio_dev)));
> + if (ret)
> + goto err;
> +
> + ret = regmap_write(st->regmap, AD4691_ACC_MASK_REG,
> + ~bitmap_read(indio_dev->active_scan_mask, 0,
> + iio_get_masklength(indio_dev)) & GENMASK(15, 0));
> + if (ret)
> + goto err;
> +
> + iio_for_each_active_channel(indio_dev, bit) {
> + ret = regmap_write(st->regmap, AD4691_ACC_DEPTH_IN(bit),
> + st->osr[bit]);
> + if (ret)
> + goto err;
> + }
> +
> + ret = ad4691_enter_conversion_mode(st);
> + if (ret)
> + goto err;
> +
> + ret = ad4691_sampling_enable(st, true);
> + if (ret)
> + goto err;
Do we need to do something to exit conversion mode on error here?
> +
> + enable_irq(st->irq);
> + return 0;
> +err:
> + spi_unoptimize_message(&st->scan_msg);
> +err_free_bufs:
> + ad4691_free_scan_bufs(st);
> + return ret;
> +}
> +
> +static int ad4691_cnv_burst_buffer_postdisable(struct iio_dev *indio_dev)
> +{
> + struct ad4691_state *st = iio_priv(indio_dev);
> + int ret;
> +
> + disable_irq(st->irq);
> +
> + ret = ad4691_sampling_enable(st, false);
> + if (ret)
> + return ret;
> +
> + ret = regmap_write(st->regmap, AD4691_STD_SEQ_CONFIG,
> + AD4691_SEQ_ALL_CHANNELS_OFF);
> + if (ret)
> + return ret;
> +
This order of unwinding is not the exact reverse of how it was
set up. So either the order needs to be fixed or a comment added
explaining why this order is needed instead.
> + ret = ad4691_exit_conversion_mode(st);
> + spi_unoptimize_message(&st->scan_msg);
> + ad4691_free_scan_bufs(st);
> + return ret;
> +}
> +
> +static const struct iio_buffer_setup_ops ad4691_cnv_burst_buffer_setup_ops = {
> + .preenable = &ad4691_cnv_burst_buffer_preenable,
> + .postdisable = &ad4691_cnv_burst_buffer_postdisable,
> +};
> +
> +static ssize_t sampling_frequency_show(struct device *dev,
> + struct device_attribute *attr,
> + char *buf)
> +{
> + struct iio_dev *indio_dev = dev_to_iio_dev(dev);
> + struct ad4691_state *st = iio_priv(indio_dev);
> +
> + return sysfs_emit(buf, "%u\n", (u32)(NSEC_PER_SEC / st->cnv_period_ns));
> +}
> +
> +static ssize_t sampling_frequency_store(struct device *dev,
> + struct device_attribute *attr,
> + const char *buf, size_t len)
> +{
> + struct iio_dev *indio_dev = dev_to_iio_dev(dev);
> + struct ad4691_state *st = iio_priv(indio_dev);
> + int freq, ret;
> +
> + ret = kstrtoint(buf, 10, &freq);
> + if (ret)
> + return ret;
> +
> + guard(mutex)(&st->lock);
> +
> + if (iio_buffer_enabled(indio_dev))
This should be using iio_device_claim_direct(), otherwise
it is racy.
> + return -EBUSY;
> +
> + ret = ad4691_set_pwm_freq(st, freq);
> + if (ret)
> + return ret;
> +
> + return len;
> +}
> +
> +static IIO_DEVICE_ATTR(sampling_frequency, 0644,
> + sampling_frequency_show,
> + sampling_frequency_store, 0);
> +
> +static const struct iio_dev_attr *ad4691_buffer_attrs[] = {
> + &iio_dev_attr_sampling_frequency,
> + NULL
> +};
> +
> +static irqreturn_t ad4691_irq(int irq, void *private)
> +{
> + struct iio_dev *indio_dev = private;
> + struct ad4691_state *st = iio_priv(indio_dev);
> +
> + /*
> + * GPx has asserted: stop conversions before reading so the
Does this happen per-channel or only once per complete sequence?
> + * accumulator does not continue sampling while the trigger handler
> + * processes the data. Then fire the IIO trigger to push the sample
> + * to the buffer.
> + */
> + ad4691_sampling_enable(st, false);
> + iio_trigger_poll(indio_dev->trig);
> +
> + return IRQ_HANDLED;
> +}
> +
> +static const struct iio_trigger_ops ad4691_trigger_ops = {
> + .validate_device = iio_trigger_validate_own_device,
> +};
> +
> +static int ad4691_read_scan(struct iio_dev *indio_dev, s64 timestamp)
> +{
> + struct ad4691_state *st = iio_priv(indio_dev);
> + unsigned int i, k = 0;
> + int ret;
> +
> + guard(mutex)(&st->lock);
> +
> + ret = spi_sync(st->scan_msg.spi, &st->scan_msg);
> + if (ret)
> + return ret;
> +
> + if (st->manual_mode) {
> + iio_for_each_active_channel(indio_dev, i) {
> + st->scan.vals[i] = be16_to_cpu(st->scan_rx[k + 1]);
> + k++;
> + }
> + } else {
> + iio_for_each_active_channel(indio_dev, i) {
> + st->scan.vals[i] = be16_to_cpu(st->scan_rx[k]);
> + k++;
> + }
I suppose this is fine, but we usually try to avoid extra copiying and
byte swapping of bufferes like this if we can. It seems completly doable
in both modes. Manual mode will just one extra two-byte buffer for the
throw-away conversion on the first read xfer (or just write to the same
element twice).
> +
> + ret = regmap_write(st->regmap, AD4691_STATE_RESET_REG,
> + AD4691_STATE_RESET_ALL);
> + if (ret)
> + return ret;
> +
> + ret = ad4691_sampling_enable(st, true);
> + if (ret)
> + return ret;
> + }
> +
> + iio_push_to_buffers_with_ts(indio_dev, &st->scan, sizeof(st->scan),
> + timestamp);
> + return 0;
> +}
> +
> +static irqreturn_t ad4691_trigger_handler(int irq, void *p)
> +{
> + struct iio_poll_func *pf = p;
> + struct iio_dev *indio_dev = pf->indio_dev;
> +
> + ad4691_read_scan(indio_dev, pf->timestamp);
> + iio_trigger_notify_done(indio_dev->trig);
> + return IRQ_HANDLED;
> +}
> +
> static const struct iio_info ad4691_info = {
> .read_raw = &ad4691_read_raw,
> .write_raw = &ad4691_write_raw,
> @@ -493,6 +975,18 @@ static const struct iio_info ad4691_info = {
> .debugfs_reg_access = &ad4691_reg_access,
> };
>
> +static int ad4691_pwm_setup(struct ad4691_state *st)
> +{
> + struct device *dev = regmap_get_device(st->regmap);
> +
> + st->conv_trigger = devm_pwm_get(dev, "cnv");
> + if (IS_ERR(st->conv_trigger))
> + return dev_err_probe(dev, PTR_ERR(st->conv_trigger),
> + "Failed to get cnv pwm\n");
> +
> + return ad4691_set_pwm_freq(st, st->info->max_rate);
> +}
> +
> static int ad4691_regulator_setup(struct ad4691_state *st)
> {
> struct device *dev = regmap_get_device(st->regmap);
> @@ -558,6 +1052,22 @@ static int ad4691_config(struct ad4691_state *st)
> unsigned int val;
> int ret;
>
> + /*
> + * Determine buffer conversion mode from DT: if a PWM is provided it
> + * drives the CNV pin (CNV_BURST_MODE); otherwise CNV is tied to CS
> + * and each SPI transfer triggers a conversion (MANUAL_MODE).
> + * Both modes idle in AUTONOMOUS mode so that read_raw can use the
> + * internal oscillator without disturbing the hardware configuration.
> + */
> + if (device_property_present(dev, "pwms")) {
> + st->manual_mode = false;
> + ret = ad4691_pwm_setup(st);
> + if (ret)
> + return ret;
> + } else {
> + st->manual_mode = true;
> + }
> +
> switch (st->vref_uV) {
> case AD4691_VREF_uV_MIN ... AD4691_VREF_2P5_uV_MAX:
> ref_val = AD4691_VREF_2P5;
> @@ -613,6 +1123,78 @@ static int ad4691_config(struct ad4691_state *st)
> return 0;
> }
>
> +static int ad4691_setup_triggered_buffer(struct iio_dev *indio_dev,
> + struct ad4691_state *st)
> +{
> + struct device *dev = regmap_get_device(st->regmap);
> + struct iio_trigger *trig;
> + unsigned int i;
> + int irq, ret;
> +
> + trig = devm_iio_trigger_alloc(dev, "%s-dev%d",
> + indio_dev->name,
> + iio_device_id(indio_dev));
> + if (!trig)
> + return -ENOMEM;
> +
> + trig->ops = &ad4691_trigger_ops;
> + iio_trigger_set_drvdata(trig, st);
> +
> + ret = devm_iio_trigger_register(dev, trig);
> + if (ret)
> + return dev_err_probe(dev, ret, "IIO trigger register failed\n");
> +
> + indio_dev->trig = iio_trigger_get(trig);
> +
> + if (!st->manual_mode) {
I would invert the if since the other case is shorter.
> + /*
> + * The GP pin named in interrupt-names asserts at end-of-conversion.
> + * The IRQ handler stops conversions and fires the IIO trigger so
> + * the trigger handler can read and push the sample to the buffer.
> + * The IRQ is kept disabled until the buffer is enabled.
> + */
> + irq = -ENODEV;
> + for (i = 0; i < ARRAY_SIZE(ad4691_gp_names); i++) {
> + irq = fwnode_irq_get_byname(dev_fwnode(dev),
> + ad4691_gp_names[i]);
> + if (irq > 0)
> + break;
> + }
> + if (irq <= 0)
> + return dev_err_probe(dev, irq < 0 ? irq : -ENODEV,
> + "failed to get GP interrupt\n");
Usually we would usually just use spi->irq since it already
has been looked up. But I guess it is OK to do it like this.
> +
> + st->irq = irq;
> +
> + ret = ad4691_gpio_setup(st, i);
> + if (ret)
> + return ret;
> +
> + /*
> + * IRQ is kept disabled until the buffer is enabled to prevent
> + * spurious DATA_READY events before the SPI message is set up.
> + */
> + ret = devm_request_threaded_irq(dev, irq, NULL,
> + &ad4691_irq,
> + IRQF_ONESHOT | IRQF_NO_AUTOEN,
> + indio_dev->name, indio_dev);
> + if (ret)
> + return ret;
> +
> + return devm_iio_triggered_buffer_setup_ext(dev, indio_dev,
> + &iio_pollfunc_store_time,
> + &ad4691_trigger_handler,
> + IIO_BUFFER_DIRECTION_IN,
> + &ad4691_cnv_burst_buffer_setup_ops,
> + ad4691_buffer_attrs);
> + }
> +
> + return devm_iio_triggered_buffer_setup(dev, indio_dev,
> + &iio_pollfunc_store_time,
> + &ad4691_trigger_handler,
> + &ad4691_manual_buffer_setup_ops);
> +}
> +
^ permalink raw reply
* Re: [PATCH v6 2/4] iio: adc: ad4691: add initial driver for AD4691 family
From: David Lechner @ 2026-04-04 14:25 UTC (permalink / raw)
To: radu.sabau, Lars-Peter Clausen, Michael Hennerich,
Jonathan Cameron, Nuno Sá, Andy Shevchenko, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Uwe Kleine-König,
Liam Girdwood, Mark Brown, Linus Walleij, Bartosz Golaszewski,
Philipp Zabel, Jonathan Corbet, Shuah Khan
Cc: linux-iio, devicetree, linux-kernel, linux-pwm, linux-gpio,
linux-doc
In-Reply-To: <20260403-ad4692-multichannel-sar-adc-driver-v6-2-fa2a01a57c4e@analog.com>
On 4/3/26 6:03 AM, Radu Sabau via B4 Relay wrote:
> From: Radu Sabau <radu.sabau@analog.com>
>
> Add support for the Analog Devices AD4691 family of high-speed,
> low-power multichannel SAR ADCs: AD4691 (16-ch, 500 kSPS),
> AD4692 (16-ch, 1 MSPS), AD4693 (8-ch, 500 kSPS) and
> AD4694 (8-ch, 1 MSPS).
>
> The driver implements a custom regmap layer over raw SPI to handle the
> device's mixed 1/2/3/4-byte register widths and uses the standard IIO
> read_raw/write_raw interface for single-channel reads.
>
> The chip idles in Autonomous Mode so that single-shot read_raw can use
> the internal oscillator without disturbing the hardware configuration.
>
> Three voltage supply domains are managed: avdd (required), vio, and a
> reference supply on either the REF pin (ref-supply, external buffer)
> or the REFIN pin (refin-supply, uses the on-chip reference buffer;
> REFBUF_EN is set accordingly). Hardware reset is performed via
> the reset controller framework; a software reset through SPI_CONFIG_A
> is used as fallback when no hardware reset is available.
>
> Accumulator channel masking for single-shot reads uses ACC_MASK_REG via
> an ADDR_DESCENDING SPI write, which covers both mask bytes in a single
> 16-bit transfer.
>
> Signed-off-by: Radu Sabau <radu.sabau@analog.com>
> ---
This patch looks in good shape. Although I wouldn't mind using
MEGA/MICRO, etc. more in numbers with more than 3 or 4 zeros.
Reviewed-by: David Lechner <dlechner@baylibre.com>
^ permalink raw reply
* Re: [PATCH v8 0/3] Support runtime configuration for per-VM's HGATP mode
From: Anup Patel @ 2026-04-04 11:02 UTC (permalink / raw)
To: fangyu.yu
Cc: pbonzini, corbet, atish.patra, pjw, palmer, aou, alex, skhan,
guoren, radim.krcmar, andrew.jones, linux-doc, kvm, kvm-riscv,
linux-riscv, linux-kernel
In-Reply-To: <20260403153019.9916-1-fangyu.yu@linux.alibaba.com>
On Fri, Apr 3, 2026 at 9:00 PM <fangyu.yu@linux.alibaba.com> wrote:
>
> From: Fangyu Yu <fangyu.yu@linux.alibaba.com>
>
> Currently, RISC-V KVM hardcodes the G-stage page table format (HGATP mode)
> to the maximum mode detected at boot time (e.g., SV57x4 if supported). but
> often such a wide GPA is unnecessary, just as a host sometimes doesn't need
> sv57.
>
> This patch reuse KVM_CAP_VM_GPA_BITS to select HGATP.MODE. User-space can
> now explicitly request a specific HGATP mode (SV39x4, SV48x4, SV57x4 or
> SV32x4) during VM creation.
>
> ---
> Changes in v8:
> - Reuse KVM_CAP_VM_GPA_BITS to advertise and select the effective GPA width
> for a VM (Anup's suggestion).
> - Handle the kvm == NULL case and task kvm->lock and kvm->slots_lock to serialize
> against concurrent vCPU creation and memslot updates (Radim's suggestion).
> - Link to v7:
> https://lore.kernel.org/linux-riscv/20260402132303.6252-1-fangyu.yu@linux.alibaba.com/
> ---
> Changes in v7 (Anup's suggestions):
> - Keep the original HGATP mode probing logic.
> - Link to v6:
> https://lore.kernel.org/linux-riscv/20260330122601.22140-1-fangyu.yu@linux.alibaba.com/
> ---
> Changes in v6 (Anup's suggestions):
> - Reworked kvm_riscv_gstage_gpa_bits() and kvm_riscv_gstage_gpa_size() to
> take "unsigned long pgd_levels" instead of "struct kvm_arch *".
> - Moved kvm_riscv_gstage_mode() helper from kvm_host.h to kvm_gstage.h.
> - Renamed kvm->arch.kvm_riscv_gstage_pgd_levels to kvm->arch.pgd_levels.
> - Added pgd_levels to struct kvm_gstage to avoid repeated
> gstage->kvm->arch pointer chasing.
> - Link to v5:
> https://lore.kernel.org/linux-riscv/20260204134507.33912-1-fangyu.yu@linux.alibaba.com/
> ---
> Changes in v5:
> - Use architectural HGATP.MODE encodings as the bit index for the supported-mode
> bitmap and for the VM-mode selection UAPI; no new UAPI mode/bit defines are
> introduced(per Radim).
> - Allow KVM_CAP_RISCV_SET_HGATP_MODE on RV32 as well(per Drew).
> - Link to v4:
> https://lore.kernel.org/linux-riscv/20260202140716.34323-1-fangyu.yu@linux.alibaba.com/
> ---
> Changes in v4:
> - Extend kvm_riscv_gstage_mode_detect() to probe all HGATP.MODE values
> supported by the host and record them in a bitmask.
> - Treat unexpected pgd_levels in kvm_riscv_gstage_mode() as an internal error
> (e.g. WARN_ON_ONCE())(per Radim).
> - Move kvm_riscv_gstage_gpa_bits() and kvm_riscv_gstage_gpa_size() to header
> as static inline helpers(per Radim).
> - Drop gstage_mode_user_initialized and Remove the kvm_debug() message from
> KVM_CAP_RISCV_SET_HGATP_MODE(per Radim).
> - Link to v3:
> https://lore.kernel.org/linux-riscv/20260125150450.27068-1-fangyu.yu@linux.alibaba.com/
> ---
> Changes in v3:
> - Reworked the patch formatting (per Drew).
> - Dropped kvm->arch.kvm_riscv_gstage_mode and derive HGATP.MODE from
> kvm_riscv_gstage_pgd_levels via a helper, avoiding redundant per-VM state(per Drew).
> - Removed kvm_riscv_gstage_max_mode and keep only kvm_riscv_gstage_max_pgd_levels
> for host capability detection(per Drew).
> - Other initialization and return value issues(per Drew).
> - Enforce that KVM_CAP_RISCV_SET_HGATP_MODE can only be enabled before any vCPUs
> are created by rejecting the ioctl once kvm->created_vcpus is non-zero(per Radim).
> - Add a memslot safety check and reject the capability unless
> kvm_are_all_memslots_empty(kvm) is true, ensuring the G-stage format is not
> changed after any memslots have been installed(per Radim).
> - Link to v2:
> https://lore.kernel.org/linux-riscv/20260105143232.76715-1-fangyu.yu@linux.alibaba.com/
>
> Fangyu Yu (3):
> RISC-V: KVM: Support runtime configuration for per-VM's HGATP mode
> RISC-V: KVM: Cache gstage pgd_levels in struct kvm_gstage
> RISC-V: KVM: Reuse KVM_CAP_VM_GPA_BITS to select HGATP.MODE
>
> arch/riscv/include/asm/kvm_gstage.h | 47 ++++++++++++++++---
> arch/riscv/include/asm/kvm_host.h | 1 +
> arch/riscv/kvm/gstage.c | 65 +++++++++++++--------------
> arch/riscv/kvm/main.c | 12 ++---
> arch/riscv/kvm/mmu.c | 70 +++++++++--------------------
> arch/riscv/kvm/vm.c | 49 ++++++++++++++++++--
> arch/riscv/kvm/vmid.c | 3 +-
> 7 files changed, 148 insertions(+), 99 deletions(-)
>
> --
> 2.50.1
>
Queued this series for Linux-7.1
Thanks,
Anup
^ permalink raw reply
* Documentation: fix two typos in latest update to the security report howto
From: Willy Tarreau @ 2026-04-04 8:20 UTC (permalink / raw)
To: greg
Cc: Jonathan Corbet, skhan, workflows, linux-doc, linux-kernel,
Willy Tarreau
In previous patch "Documentation: clarify the mandatory and desirable
info for security reports" I left two typos that I didn't detect in local
checks. One is "get_maintainers.pl" (no 's' in the script name), and the
other one is a missing closing quote after "Reported-by", which didn't
have effect here but I don't know if it can break rendering elsewhere
(e.g. on the public HTML page). Better fix it before it gets merged.
Signed-off-by: Willy Tarreau <w@1wt.eu>
---
Greg, this is a fix for commit a72b832a482372 that is currently pending
in your char-misc-linus branch.
---
Documentation/process/security-bugs.rst | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/Documentation/process/security-bugs.rst b/Documentation/process/security-bugs.rst
index 0b1f6d8e3cbe..27b028e85861 100644
--- a/Documentation/process/security-bugs.rst
+++ b/Documentation/process/security-bugs.rst
@@ -59,7 +59,7 @@ In addition, the following information are highly desirable:
immediately merged (see Documentation/process/submitting-patches.rst).
This will save some back-and-forth exchanges if it is accepted, and you
will be credited for finding and fixing this issue. Note that in this case
- only a ``Signed-off-by:`` tag is needed, without ``Reported-by:` when the
+ only a ``Signed-off-by:`` tag is needed, without ``Reported-by:`` when the
reporter and author are the same.
* **mitigations**: very often during a bug analysis, some ways of mitigating
@@ -88,7 +88,7 @@ recipients to send a report to. In the Linux kernel, all official maintainers
are trusted, so the consequences of accidentally including the wrong maintainer
are essentially a bit more noise for that person, i.e. nothing dramatic. As
such, a suitable method to figure the list of maintainers (which kernel
-security officers use) is to rely on the get_maintainers.pl script, tuned to
+security officers use) is to rely on the get_maintainer.pl script, tuned to
only report maintainers. This script, when passed a file name, will look for
its path in the MAINTAINERS file to figure a hierarchical list of relevant
maintainers. Calling it a first time with the finest level of filtering will
--
2.52.0
^ permalink raw reply related
* Re: [PATCH v3] docs: allow long unbroken headings to wrap and prevent overflow
From: Rito Rhymes @ 2026-04-04 8:19 UTC (permalink / raw)
To: Jonathan Corbet, Rito Rhymes, linux-doc; +Cc: Shuah Khan, linux-kernel, rdunlap
In-Reply-To: <87h5q3g288.fsf@trenco.lwn.net>
Jon,
Following up on this point:
> I do not see the problem you are referring to here; headings wrap just
> fine for me using both Firefox and Chrome. (Firefox arguably does a
> little better since it wraps at "/", but that is what also make it turn
> "I/O into "I/
> O."
In my previous reply I sent a catalog of the environments I tested, with
per-environment screenshots for both slash-delimited and
underscore-delimited cases.
Does that breakdown help explain why we may be seeing different results,
or help you reproduce the issue in the environments where it occurs?
Thanks,
Rito
^ permalink raw reply
* Re: [PATCH v3] docs: wrap generated tables to contain small-screen overflow
From: Rito Rhymes @ 2026-04-04 8:13 UTC (permalink / raw)
To: Rito Rhymes, Jonathan Corbet, Mauro Carvalho Chehab, linux-doc
Cc: Shuah Khan, linux-kernel, rdunlap
In-Reply-To: <DHCD6BWJTLTB.VJWKPRWC2GHZ@ritovision.com>
Jon,
Following up on this point:
> So this CSS perhaps makes sense, but.. But why do you need to inject
> another <div>, creating a whole new extension to do so, rather than
> just applying the CSS directly to the <table> elements? I just gave
> that a try, and it would appear to work just fine.
In my previous reply I outlined the regressions I saw when applying the
CSS directly to the `<table>` elements, and why that led me to the
wrapper-based approach instead.
Given the regressions and rationale I already outlined, is the
wrapper-based approach acceptable?
Thanks,
Rito
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox