* [PATCH v2 0/5] spi: add multi-CS and multi-chip aggregation support
@ 2026-08-03 3:01 Jonathan Santos
2026-08-03 3:01 ` [PATCH v2 1/5] spi: support simultaneous assertion of multiple CS Jonathan Santos
` (4 more replies)
0 siblings, 5 replies; 6+ messages in thread
From: Jonathan Santos @ 2026-08-03 3:01 UTC (permalink / raw)
To: linux-spi, linux-kernel
Cc: Jonathan Santos, dlechner, nuno.sa, michael.hennerich, broonie,
jonath4nns, marcelo.schmitt1
This series adds support to multi-CS devices and ancillary device with
lanes, to enable the multi-device setup described in [1], where multiple
AD7768-1 ADCs are wired to the same controller in a synchronized sampling
configuration, and the limitation noted in [2], where no SPI controller
completely handles logical chip selects beyond the first one.
The first part of the set addresses multi-CS support. Some SPI controllers
can assert multiple chip selects simultaneously, but the existing code
hardcoded CS index 0 in both spi_set_cs() and of_spi_parse_dt(),
preventing this from working. The spi device name pattern is modified to
include all active CS lines, so multi-CS devices are distinguishable in
sysfs.
The second part introduces spi_new_ancillary_device_with_lane() and its
devm variant, used here to create subdevices that binds the CS with a
correspondent rx and tx lane from the parent device. With that we can
handle each device individually when there are separate data lanes.
The last part uses the above infrastructure in the AD7768-1 driver to
support aggregating multiple chips as a single IIO device. The number of
sub-devices is derived from spi->num_rx_lanes. IIO channels are
allocated dynamically, buffered capture is restricted to the full-device
scan mask, and SPI offload mode enables SPI_MULTI_LANE_MODE_STRIPE to
interleave samples from all lanes into the DMA stream.
---
v2 summary:
* Removed the per-transfer CS mask patches, the devices will be
individually handled by the ancillary device feature.
* Removed dynamic multilane selection patches. The lane is bound to the
CS using the new spi_new_ancillary_device_with_lane() feature.
* Included a use case patch modifying the AD7768-1 driver to support
multidevice aggregation.
* Link to v1: https://lore.kernel.org/linux-spi/cover.1783729282.git.Jonathan.Santos@analog.com/T/#t
OBS: The ancillary device approach was chosen over per-transfer CS masks
because it has better integrattion with regmap. Both approaches require
multiple regmap instances, but the CS mask approach would additionally
require custom read/write functions to route transfers to the correct CS,
while ancillary devices handle this more transparently.
David suggested adding an ancillary device field to spi_message, I guess to
enable dynamic CS-lane selection. For now, using one ancillary device per
chip for individual access and the main SPI device for simultaneous access
seems to be sufficient. The dynamic approach can be implemented in the future
if needed.
---
[1]: https://lore.kernel.org/linux-iio/af0EGv172ZMl%2F6N5@JSANTO12-L01.ad.analog.com/T/#t
[2]: https://lore.kernel.org/all/20250915183725.219473-1-jonas.gorski@gmail.com/
Jonathan Santos (5):
spi: support simultaneous assertion of multiple CS
spi: expand device name to include all CS lines for multi-CS devices
spi: introduce SPI ancillary device with lanes
spi: spi-engine-ex: Add support for multi-CS devices
iio: adc: ad7768-1: add support for multiple chip aggregation
drivers/iio/adc/ad7768-1.c | 99 ++++++++++++++++++++++---
drivers/spi/spi-axi-spi-engine.c | 22 ++++--
drivers/spi/spi.c | 121 +++++++++++++++++++++++++++++--
include/linux/spi/spi.h | 8 ++
4 files changed, 227 insertions(+), 23 deletions(-)
base-commit: e0484d62e8e1cff75b210938be835ea6221bda59
--
2.34.1
^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH v2 1/5] spi: support simultaneous assertion of multiple CS
2026-08-03 3:01 [PATCH v2 0/5] spi: add multi-CS and multi-chip aggregation support Jonathan Santos
@ 2026-08-03 3:01 ` Jonathan Santos
2026-08-03 3:02 ` [PATCH v2 2/5] spi: expand device name to include all CS lines for multi-CS devices Jonathan Santos
` (3 subsequent siblings)
4 siblings, 0 replies; 6+ messages in thread
From: Jonathan Santos @ 2026-08-03 3:01 UTC (permalink / raw)
To: linux-spi, linux-kernel
Cc: Jonathan Santos, dlechner, nuno.sa, michael.hennerich, broonie,
jonath4nns, marcelo.schmitt1
Some SPI controllers allow multiple CS lines to be toggled at the same
time. The existing code always used CS index 0 when tracking the last
active CS in spi_set_cs(), and unconditionally set cs_index_mask to
BIT(0) when parsing DT, both forcing the single CS usage.
Modify spi_set_cs() to iterate last_cs[] using each logical CS index
instead of always reading index 0. Modify of_spi_parse_dt() to build
cs_index_mask from all parsed CS entries rather than hardcoding BIT(0),
so the controller correctly identifies which CS lines belong to a device
when asserting them simultaneously.
Board info, ACPI, and ancillary device paths are not updated here.
Board info would require an API change to accept an array of CS values
and is left for a follow-up when we have a use case for this. Ancillary
devices are by design single-CS, so multi-CS is not a current use case for
them. ACPI represents the CS as a 64-bit integer with no established
convention for encoding multiple CS indices yet, so any extension there
would require a separate specification effort.
Acked-by: Nuno Sá <nuno.sa@analog.com>
Signed-off-by: Jonathan Santos <Jonathan.Santos@analog.com>
---
Changes in v2:
* Include Summary describind why the other SPI paths were not addressed
here.
---
drivers/spi/spi.c | 9 +++++----
1 file changed, 5 insertions(+), 4 deletions(-)
diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c
index d9e6b4b87c89..55fb96fea243 100644
--- a/drivers/spi/spi.c
+++ b/drivers/spi/spi.c
@@ -1090,7 +1090,7 @@ static void spi_set_cs(struct spi_device *spi, bool enable, bool force)
spi->controller->last_cs_index_mask = spi->cs_index_mask;
for (idx = 0; idx < SPI_DEVICE_CS_CNT_MAX; idx++) {
if (enable && idx < spi->num_chipselect)
- spi->controller->last_cs[idx] = spi_get_chipselect(spi, 0);
+ spi->controller->last_cs[idx] = spi_get_chipselect(spi, idx);
else
spi->controller->last_cs[idx] = SPI_INVALID_CS;
}
@@ -2594,10 +2594,11 @@ static int of_spi_parse_dt(struct spi_controller *ctlr, struct spi_device *spi,
spi_set_chipselect(spi, idx, cs[idx]);
/*
- * By default spi->chip_select[0] will hold the physical CS number,
- * so set bit 0 in spi->cs_index_mask.
+ * Set cs_index_mask to indicate which logical CS indices are active.
+ * Each bit corresponds to a logical CS index in the spi->chip_select array.
*/
- spi->cs_index_mask = BIT(0);
+ for (idx = 0; idx < rc; idx++)
+ spi->cs_index_mask |= BIT(idx);
/* Device speed */
if (!of_property_read_u32(nc, "spi-max-frequency", &value))
--
2.34.1
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH v2 2/5] spi: expand device name to include all CS lines for multi-CS devices
2026-08-03 3:01 [PATCH v2 0/5] spi: add multi-CS and multi-chip aggregation support Jonathan Santos
2026-08-03 3:01 ` [PATCH v2 1/5] spi: support simultaneous assertion of multiple CS Jonathan Santos
@ 2026-08-03 3:02 ` Jonathan Santos
2026-08-03 3:02 ` [PATCH v2 3/5] spi: introduce SPI ancillary device with lanes Jonathan Santos
` (2 subsequent siblings)
4 siblings, 0 replies; 6+ messages in thread
From: Jonathan Santos @ 2026-08-03 3:02 UTC (permalink / raw)
To: linux-spi, linux-kernel
Cc: Jonathan Santos, dlechner, nuno.sa, michael.hennerich, broonie,
jonath4nns, marcelo.schmitt1
The device name for SPI devices was formatted as <controller>.<cs0>,
always using only the first chip-select index, ignoring the remaining CS
lines.
Change the naming format to <controller>.<cs0>+<cs1>+... so that all
active chip-selects are reflected in the device name. Single-CS devices
are not affected since the loop only appends extra indices when
num_chipselect is greater than one.
Signed-off-by: Jonathan Santos <Jonathan.Santos@analog.com>
---
Changes in v2:
* New patch.
---
drivers/spi/spi.c | 10 ++++++++--
1 file changed, 8 insertions(+), 2 deletions(-)
diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c
index 55fb96fea243..61423aee1525 100644
--- a/drivers/spi/spi.c
+++ b/drivers/spi/spi.c
@@ -594,6 +594,8 @@ static void spi_dev_set_name(struct spi_device *spi)
{
struct device *dev = &spi->dev;
struct fwnode_handle *fwnode = dev_fwnode(dev);
+ char cs_str[32];
+ int cs_len;
if (is_acpi_device_node(fwnode)) {
dev_set_name(dev, "spi-%s", acpi_dev_name(to_acpi_device_node(fwnode)));
@@ -605,8 +607,12 @@ static void spi_dev_set_name(struct spi_device *spi)
return;
}
- dev_set_name(&spi->dev, "%s.%u", dev_name(&spi->controller->dev),
- spi_get_chipselect(spi, 0));
+ cs_len = scnprintf(cs_str, sizeof(cs_str), "%u", spi_get_chipselect(spi, 0));
+ for (int idx = 1; idx < spi->num_chipselect; idx++)
+ cs_len += scnprintf(cs_str + cs_len, sizeof(cs_str) - cs_len,
+ "+%u", spi_get_chipselect(spi, idx));
+
+ dev_set_name(&spi->dev, "%s.%s", dev_name(&spi->controller->dev), cs_str);
}
/*
--
2.34.1
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH v2 3/5] spi: introduce SPI ancillary device with lanes
2026-08-03 3:01 [PATCH v2 0/5] spi: add multi-CS and multi-chip aggregation support Jonathan Santos
2026-08-03 3:01 ` [PATCH v2 1/5] spi: support simultaneous assertion of multiple CS Jonathan Santos
2026-08-03 3:02 ` [PATCH v2 2/5] spi: expand device name to include all CS lines for multi-CS devices Jonathan Santos
@ 2026-08-03 3:02 ` Jonathan Santos
2026-08-03 3:02 ` [PATCH v2 4/5] spi: spi-engine-ex: Add support for multi-CS devices Jonathan Santos
2026-08-03 3:02 ` [PATCH v2 5/5] iio: adc: ad7768-1: add support for multiple chip aggregation Jonathan Santos
4 siblings, 0 replies; 6+ messages in thread
From: Jonathan Santos @ 2026-08-03 3:02 UTC (permalink / raw)
To: linux-spi, linux-kernel
Cc: Jonathan Santos, dlechner, nuno.sa, michael.hennerich, broonie,
jonath4nns, marcelo.schmitt1
The existing spi_new_ancillary_device() creates an ancillary SPI device
but does not consider the parent's lane map. In multi-device setups where
each sub-device's chip-select is bound to a dedicated data lane, there is
no way to bind an ancillary device to an arbitrary lane index.
Introduce spi_new_ancillary_device_with_lane() and
devm_spi_new_ancillary_device_with_lane(), which accept rx_lane_idx and
tx_lane_idx parameters to select a specific lane from the parent's
rx_lane_map and tx_lane_map respectively. The resulting ancillary device
is registered with a single RX and TX lane, keeping it independent from
the other sub-devices sharing the same controller.
Signed-off-by: Jonathan Santos <Jonathan.Santos@analog.com>
---
Changes in v2:
* New patch.
---
drivers/spi/spi.c | 102 ++++++++++++++++++++++++++++++++++++++++
include/linux/spi/spi.h | 8 ++++
2 files changed, 110 insertions(+)
diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c
index 61423aee1525..d41c9392c04e 100644
--- a/drivers/spi/spi.c
+++ b/drivers/spi/spi.c
@@ -2749,6 +2749,72 @@ struct spi_device *spi_new_ancillary_device(struct spi_device *spi,
}
EXPORT_SYMBOL_GPL(spi_new_ancillary_device);
+/**
+ * spi_new_ancillary_device_with_lane() - Register ancillary SPI device bound to specific lane
+ * @spi: Pointer to the main SPI device registering the ancillary device
+ * @chip_select: Chip Select of the ancillary device
+ * @rx_lane_idx: Lane index within the parent's rx_lane_map
+ * @tx_lane_idx: Lane index within the parent's tx_lane_map
+ *
+ * Like spi_new_ancillary_device(), but additionally binds the ancillary device
+ * to a single lane from the parent's lane map. Use this in multi-device setups
+ * where each sub-device is physically wired to a dedicated CS and a dedicated
+ * data lane.
+ *
+ * This may only be called from main SPI device's probe routine.
+ *
+ * Return: Pointer to new ancillary device on success; ERR_PTR on failure
+ */
+struct spi_device *spi_new_ancillary_device_with_lane(struct spi_device *spi,
+ u8 chip_select,
+ unsigned int rx_lane_idx,
+ unsigned int tx_lane_idx)
+{
+ struct spi_controller *ctlr = spi->controller;
+ struct spi_device *ancillary;
+ int rc;
+
+ ancillary = spi_alloc_device(ctlr);
+ if (!ancillary) {
+ rc = -ENOMEM;
+ goto err_out;
+ }
+
+ strscpy(ancillary->modalias, "dummy", sizeof(ancillary->modalias));
+
+ spi_set_chipselect(ancillary, 0, chip_select);
+
+ ancillary->max_speed_hz = spi->max_speed_hz;
+ ancillary->mode = spi->mode;
+ ancillary->cs_index_mask = BIT(0);
+
+ if (rx_lane_idx >= spi->num_rx_lanes || tx_lane_idx >= spi->num_tx_lanes) {
+ rc = -EINVAL;
+ goto err_out;
+ }
+
+ ancillary->rx_lane_map[0] = spi->rx_lane_map[rx_lane_idx];
+ ancillary->num_rx_lanes = 1;
+ ancillary->tx_lane_map[0] = spi->tx_lane_map[tx_lane_idx];
+ ancillary->num_tx_lanes = 1;
+
+ WARN_ON(!mutex_is_locked(&ctlr->add_lock));
+
+ /* Register the new device, passing the parent to skip CS conflict check */
+ rc = __spi_add_device(ancillary, spi);
+ if (rc) {
+ dev_err(&spi->dev, "failed to register ancillary device\n");
+ goto err_out;
+ }
+
+ return ancillary;
+
+err_out:
+ spi_dev_put(ancillary);
+ return ERR_PTR(rc);
+}
+EXPORT_SYMBOL_GPL(spi_new_ancillary_device_with_lane);
+
static void devm_spi_unregister_device(void *spi)
{
spi_unregister_device(spi);
@@ -2789,6 +2855,42 @@ struct spi_device *devm_spi_new_ancillary_device(struct spi_device *spi,
}
EXPORT_SYMBOL_GPL(devm_spi_new_ancillary_device);
+/**
+ * devm_spi_new_ancillary_device_with_lane() - Register managed ancillary SPI device bound to a lane
+ * @spi: Pointer to the main SPI device registering the ancillary device
+ * @chip_select: Chip Select of the ancillary device
+ * @rx_lane_idx: Per-device lane index within the parent's rx_lane_map
+ * @tx_lane_idx: Per-device lane index within the parent's tx_lane_map
+ *
+ * Managed version of spi_new_ancillary_device_with_lane(). The ancillary device
+ * will be unregistered automatically when the parent SPI device is unregistered.
+ *
+ * This may only be called from main SPI device's probe routine.
+ *
+ * Return: Pointer to new ancillary device on success; ERR_PTR on failure
+ */
+struct spi_device *devm_spi_new_ancillary_device_with_lane(struct spi_device *spi,
+ u8 chip_select,
+ unsigned int rx_lane_idx,
+ unsigned int tx_lane_idx)
+{
+ struct spi_device *ancillary;
+ int ret;
+
+ ancillary = spi_new_ancillary_device_with_lane(spi, chip_select,
+ rx_lane_idx, tx_lane_idx);
+ if (IS_ERR(ancillary))
+ return ancillary;
+
+ ret = devm_add_action_or_reset(&spi->dev, devm_spi_unregister_device,
+ ancillary);
+ if (ret)
+ return ERR_PTR(ret);
+
+ return ancillary;
+}
+EXPORT_SYMBOL_GPL(devm_spi_new_ancillary_device_with_lane);
+
#ifdef CONFIG_ACPI
struct acpi_spi_lookup {
struct spi_controller *ctlr;
diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h
index 4c285d3ede1d..91c9e2d2e7e3 100644
--- a/include/linux/spi/spi.h
+++ b/include/linux/spi/spi.h
@@ -385,6 +385,14 @@ static inline void spi_unregister_driver(struct spi_driver *sdrv)
extern struct spi_device *spi_new_ancillary_device(struct spi_device *spi, u8 chip_select);
extern struct spi_device *devm_spi_new_ancillary_device(struct spi_device *spi, u8 chip_select);
+extern struct spi_device *spi_new_ancillary_device_with_lane(struct spi_device *spi,
+ u8 chip_select,
+ unsigned int rx_lane_idx,
+ unsigned int tx_lane_idx);
+extern struct spi_device *devm_spi_new_ancillary_device_with_lane(struct spi_device *spi,
+ u8 chip_select,
+ unsigned int rx_lane_idx,
+ unsigned int tx_lane_idx);
/* Use a define to avoid include chaining to get THIS_MODULE */
#define spi_register_driver(driver) \
--
2.34.1
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH v2 4/5] spi: spi-engine-ex: Add support for multi-CS devices
2026-08-03 3:01 [PATCH v2 0/5] spi: add multi-CS and multi-chip aggregation support Jonathan Santos
` (2 preceding siblings ...)
2026-08-03 3:02 ` [PATCH v2 3/5] spi: introduce SPI ancillary device with lanes Jonathan Santos
@ 2026-08-03 3:02 ` Jonathan Santos
2026-08-03 3:02 ` [PATCH v2 5/5] iio: adc: ad7768-1: add support for multiple chip aggregation Jonathan Santos
4 siblings, 0 replies; 6+ messages in thread
From: Jonathan Santos @ 2026-08-03 3:02 UTC (permalink / raw)
To: linux-spi, linux-kernel
Cc: Jonathan Santos, dlechner, nuno.sa, michael.hennerich, broonie,
jonath4nns, marcelo.schmitt1
The SPI Engine controller only handled the first chip select when
asserting CS lines and configuring CS polarity inversion, ignoring
additional CS indices present in cs_index_mask.
Update spi_engine_gen_cs() to iterate over all set bits in
cs_index_mask and toggle each corresponding CS line in the assert mask.
Update spi_engine_setup() likewise so that SPI_CS_HIGH polarity is
applied to every active CS index rather than only index 0.
Set SPI_CONTROLLER_MULTI_CS in the controller flags to reflect the
capability to the SPI core.
Signed-off-by: Jonathan Santos <Jonathan.Santos@analog.com>
---
Changes in v2:
* Adaptation of the patch
"spi: spi-engine-ex: Add support for multi-CS devices" from v1,
discarding the per transfer CS mask and keeping the Multi-CS support
only.
---
drivers/spi/spi-axi-spi-engine.c | 22 ++++++++++++++++------
1 file changed, 16 insertions(+), 6 deletions(-)
diff --git a/drivers/spi/spi-axi-spi-engine.c b/drivers/spi/spi-axi-spi-engine.c
index 02bbc5d0cfc5..8a2cae81aea6 100644
--- a/drivers/spi/spi-axi-spi-engine.c
+++ b/drivers/spi/spi-axi-spi-engine.c
@@ -46,6 +46,7 @@
#define SPI_ENGINE_REG_SDI_DATA_FIFO_PEEK 0xec
#define SPI_ENGINE_MAX_NUM_OFFLOADS 32
+#define SPI_ENGINE_MAX_CS 8
#define SPI_ENGINE_REG_OFFLOAD_CTRL(x) (0x100 + SPI_ENGINE_MAX_NUM_OFFLOADS * (x))
#define SPI_ENGINE_REG_OFFLOAD_STATUS(x) (0x104 + SPI_ENGINE_MAX_NUM_OFFLOADS * (x))
@@ -281,10 +282,14 @@ static void spi_engine_gen_sleep(struct spi_engine_program *p, bool dry,
static void spi_engine_gen_cs(struct spi_engine_program *p, bool dry,
struct spi_device *spi, bool assert)
{
+ unsigned long cs_index_mask = spi->cs_index_mask;
unsigned int mask = 0xff;
+ unsigned int cs_bit;
- if (assert)
- mask ^= BIT(spi_get_chipselect(spi, 0));
+ if (assert) {
+ for_each_set_bit(cs_bit, &cs_index_mask, SPI_ENGINE_MAX_CS)
+ mask ^= BIT(spi_get_chipselect(spi, cs_bit));
+ }
spi_engine_program_add_cmd(p, dry, SPI_ENGINE_CMD_ASSERT(0, mask));
}
@@ -886,12 +891,16 @@ static int spi_engine_setup(struct spi_device *device)
{
struct spi_controller *host = device->controller;
struct spi_engine *spi_engine = spi_controller_get_devdata(host);
+ unsigned long cs_index_mask = device->cs_index_mask;
unsigned int reg;
+ u32 cs_bit;
- if (device->mode & SPI_CS_HIGH)
- spi_engine->cs_inv |= BIT(spi_get_chipselect(device, 0));
- else
- spi_engine->cs_inv &= ~BIT(spi_get_chipselect(device, 0));
+ for_each_set_bit(cs_bit, &cs_index_mask, SPI_ENGINE_MAX_CS) {
+ if (device->mode & SPI_CS_HIGH)
+ spi_engine->cs_inv |= BIT(spi_get_chipselect(device, cs_bit));
+ else
+ spi_engine->cs_inv &= ~BIT(spi_get_chipselect(device, cs_bit));
+ }
writel_relaxed(SPI_ENGINE_CMD_SYNC(0),
spi_engine->base + SPI_ENGINE_REG_CMD_FIFO);
@@ -1222,6 +1231,7 @@ static int spi_engine_probe(struct platform_device *pdev)
host->unoptimize_message = spi_engine_unoptimize_message;
host->get_offload = spi_engine_get_offload;
host->put_offload = spi_engine_put_offload;
+ host->flags |= SPI_CONTROLLER_MULTI_CS;
host->num_chipselect = 8;
if (adi_axi_pcore_ver_gteq(version, 1, 2)) {
--
2.34.1
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH v2 5/5] iio: adc: ad7768-1: add support for multiple chip aggregation
2026-08-03 3:01 [PATCH v2 0/5] spi: add multi-CS and multi-chip aggregation support Jonathan Santos
` (3 preceding siblings ...)
2026-08-03 3:02 ` [PATCH v2 4/5] spi: spi-engine-ex: Add support for multi-CS devices Jonathan Santos
@ 2026-08-03 3:02 ` Jonathan Santos
4 siblings, 0 replies; 6+ messages in thread
From: Jonathan Santos @ 2026-08-03 3:02 UTC (permalink / raw)
To: linux-spi, linux-kernel
Cc: Jonathan Santos, dlechner, nuno.sa, michael.hennerich, broonie,
jonath4nns, marcelo.schmitt1
The AD7768-1 family is a single-channel ADC, but it is designed to allow
connecting multiple devices to the same SPI controller, each on a
dedicated CS and data lane, clocked synchronously, and sharing SDO and
SCLK. The number of aggregated devices are derived from
spi->num_rx_lanes, assuming all parts are single lane.
The DRDY pins are combined to trigger the data interrupt when all
devices are ready, and since the synchronization pins are tied, they
stay in synchrony.
To reflect the multidevice setup, IIO channels are dynamically set
based on the number of devices. Restrict buffered capture to the
all-channels scan mask, since all devices sample in lockstep. Register
an ancillary SPI device per lane for individual direct reads, and enable
SPI_MULTI_LANE_MODE_STRIPE in offload mode to interleave samples from all
lanes into the DMA stream.
Since all devices must be in sync, all configurations that affects the
sampling rate are unified, so they always have the same sampling
frequency.
Signed-off-by: Jonathan Santos <Jonathan.Santos@analog.com>
---
Changes in v2:
* New patch.
---
drivers/iio/adc/ad7768-1.c | 99 +++++++++++++++++++++++++++++++++-----
1 file changed, 88 insertions(+), 11 deletions(-)
diff --git a/drivers/iio/adc/ad7768-1.c b/drivers/iio/adc/ad7768-1.c
index e9060c1bbe6f..0a34d408c188 100644
--- a/drivers/iio/adc/ad7768-1.c
+++ b/drivers/iio/adc/ad7768-1.c
@@ -120,7 +120,8 @@
#define AD7768_TRIGGER_SOURCE_SYNC_IDX 0
-#define AD7768_MAX_CHANNELS 1
+#define AD7768_MAX_CHANNELS 1
+#define AD7768_MAX_AGGR_DEVICES 4
#define ADAQ7768_PGA_PINS 3
@@ -300,10 +301,12 @@ struct ad7768_chip_info {
struct ad7768_state {
struct spi_device *spi;
+ struct spi_device *spi_anc[AD7768_MAX_AGGR_DEVICES];
struct spi_offload *offload;
struct spi_offload_trigger *offload_trigger;
struct regmap *regmap;
struct regmap *regmap24;
+ struct regmap *regmap24_anc[AD7768_MAX_AGGR_DEVICES];
int vref_uv;
struct regulator_dev *vcm_rdev;
unsigned int vcm_output_sel;
@@ -322,11 +325,12 @@ struct ad7768_state {
struct gpio_descs *pga_gpios;
struct gpio_desc *gpio_sync_in;
struct gpio_desc *gpio_reset;
- const char *labels[AD7768_MAX_CHANNELS];
+ const char *labels[AD7768_MAX_CHANNELS * AD7768_MAX_AGGR_DEVICES];
struct gpio_chip gpiochip;
struct spi_transfer offload_xfer;
struct spi_message offload_msg;
const struct ad7768_chip_info *chip;
+ u8 num_devices;
bool en_spi_sync;
struct mutex pga_lock; /* protect device internal state (PGA) */
/*
@@ -335,7 +339,7 @@ struct ad7768_state {
*/
union {
struct {
- __be32 chan;
+ __be32 chan[AD7768_MAX_AGGR_DEVICES];
aligned_s64 timestamp;
} scan;
__be32 d32;
@@ -478,7 +482,7 @@ static int ad7768_set_mode(struct ad7768_state *st,
AD7768_CONV_MODE_MSK, AD7768_CONV_MODE(mode));
}
-static int ad7768_scan_direct(struct iio_dev *indio_dev)
+static int ad7768_scan_direct(struct iio_dev *indio_dev, unsigned int chan)
{
struct ad7768_state *st = iio_priv(indio_dev);
int readval, ret;
@@ -492,9 +496,15 @@ static int ad7768_scan_direct(struct iio_dev *indio_dev)
if (!ret)
return -ETIMEDOUT;
- ret = regmap_read(st->regmap24, AD7768_REG24_ADC_DATA, &readval);
- if (ret)
- return ret;
+ if (st->num_devices > 1) {
+ ret = regmap_read(st->regmap24_anc[chan], AD7768_REG24_ADC_DATA, &readval);
+ if (ret)
+ return ret;
+ } else {
+ ret = regmap_read(st->regmap24, AD7768_REG24_ADC_DATA, &readval);
+ if (ret)
+ return ret;
+ }
/*
* When the decimation rate is set to x8, the ADC data precision is
@@ -989,7 +999,7 @@ static int ad7768_read_raw(struct iio_dev *indio_dev,
if (!iio_device_claim_direct(indio_dev))
return -EBUSY;
- ret = ad7768_scan_direct(indio_dev);
+ ret = ad7768_scan_direct(indio_dev, chan->channel);
iio_device_release_direct(indio_dev);
if (ret < 0)
@@ -1391,6 +1401,8 @@ static int ad7768_offload_buffer_postenable(struct iio_dev *indio_dev)
st->offload_xfer.len = spi_bpw_to_bytes(scan_type->realbits);
st->offload_xfer.bits_per_word = scan_type->realbits;
st->offload_xfer.offload_flags = SPI_OFFLOAD_XFER_RX_STREAM;
+ if (st->num_devices > 1)
+ st->offload_xfer.multi_lane_mode = SPI_MULTI_LANE_MODE_STRIPE;
spi_message_init_with_transfers(&st->offload_msg, &st->offload_xfer, 1);
st->offload_msg.offload = st->offload;
@@ -1694,6 +1706,55 @@ static int ad7768_parse_aaf_gain(struct device *dev, struct ad7768_state *st)
return 0;
}
+static int ad7768_probe_multidevices(struct iio_dev *indio_dev)
+{
+ struct ad7768_state *st = iio_priv(indio_dev);
+ struct device *dev = indio_dev->dev.parent;
+ struct iio_chan_spec *channels;
+ unsigned long *masks;
+ u8 cs;
+ int i;
+
+ indio_dev->num_channels = st->num_devices * st->chip->num_channels;
+ channels = devm_kcalloc(dev, indio_dev->num_channels, sizeof(*channels), GFP_KERNEL);
+ if (!channels)
+ return -ENOMEM;
+
+ for (i = 0; i < st->num_devices; i++) {
+ struct iio_chan_spec *chan = &channels[i];
+
+ *chan = *st->chip->channel_spec;
+ chan->channel = i;
+ chan->scan_index = i;
+ }
+
+ indio_dev->channels = channels;
+
+ masks = devm_kcalloc(dev, st->chip->num_channels + 1, sizeof(*masks), GFP_KERNEL);
+ if (!masks)
+ return -ENOMEM;
+
+ masks[0] = GENMASK(st->num_devices - 1, 0);
+ indio_dev->available_scan_masks = masks;
+
+ /* Setup ancillary SPI devices for single device access */
+ for (i = 0; i < st->num_devices; i++) {
+ cs = spi_get_chipselect(st->spi, i);
+ st->spi_anc[i] = devm_spi_new_ancillary_device_with_lane(st->spi,
+ cs, i, 0);
+ if (IS_ERR(st->spi_anc[i]))
+ return dev_err_probe(dev, PTR_ERR(st->spi_anc[i]),
+ "failed to register ancillary device\n");
+
+ st->regmap24_anc[i] = devm_regmap_init_spi(st->spi_anc[i],
+ &ad7768_regmap24_config);
+ if (IS_ERR(st->regmap24_anc[i]))
+ return PTR_ERR(st->regmap24_anc[i]);
+ }
+
+ return 0;
+}
+
static bool ad7768_offload_trigger_match(struct spi_offload_trigger *trigger,
enum spi_offload_trigger_type type,
u64 *args, u32 nargs)
@@ -1830,6 +1891,15 @@ static int ad7768_probe(struct spi_device *spi)
st->chip = spi_get_device_match_data(spi);
st->spi = spi;
+ /*
+ * This family is composed of 1-lane devices, so we assume that each
+ * lane is bound to a different device.
+ */
+ st->num_devices = spi->num_rx_lanes;
+ if (st->num_devices > AD7768_MAX_AGGR_DEVICES)
+ return dev_err_probe(&spi->dev, -EINVAL,
+ "Too many devices (%u), max %d supported\n",
+ st->num_devices, AD7768_MAX_AGGR_DEVICES);
st->regmap = devm_regmap_init_spi(spi, &ad7768_regmap_config);
if (IS_ERR(st->regmap))
@@ -1853,11 +1923,18 @@ static int ad7768_probe(struct spi_device *spi)
st->mclk_freq = clk_get_rate(st->mclk);
- indio_dev->channels = st->chip->channel_spec;
- indio_dev->num_channels = st->chip->num_channels;
indio_dev->name = st->chip->name;
indio_dev->info = &ad7768_info;
indio_dev->modes = INDIO_DIRECT_MODE;
+ if (st->num_devices > 1) {
+ ret = ad7768_probe_multidevices(indio_dev);
+ if (ret)
+ return dev_err_probe(&spi->dev, ret,
+ "Failed to configure multidevice\n");
+ } else {
+ indio_dev->channels = st->chip->channel_spec;
+ indio_dev->num_channels = st->chip->num_channels;
+ }
/* Register VCM output regulator */
if (st->chip->has_vcm_regulator) {
@@ -1889,7 +1966,7 @@ static int ad7768_probe(struct spi_device *spi)
return ret;
}
- ret = ad7768_set_channel_label(indio_dev, st->chip->num_channels);
+ ret = ad7768_set_channel_label(indio_dev, st->num_devices);
if (ret)
return ret;
--
2.34.1
^ permalink raw reply related [flat|nested] 6+ messages in thread
end of thread, other threads:[~2026-08-03 3:03 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-03 3:01 [PATCH v2 0/5] spi: add multi-CS and multi-chip aggregation support Jonathan Santos
2026-08-03 3:01 ` [PATCH v2 1/5] spi: support simultaneous assertion of multiple CS Jonathan Santos
2026-08-03 3:02 ` [PATCH v2 2/5] spi: expand device name to include all CS lines for multi-CS devices Jonathan Santos
2026-08-03 3:02 ` [PATCH v2 3/5] spi: introduce SPI ancillary device with lanes Jonathan Santos
2026-08-03 3:02 ` [PATCH v2 4/5] spi: spi-engine-ex: Add support for multi-CS devices Jonathan Santos
2026-08-03 3:02 ` [PATCH v2 5/5] iio: adc: ad7768-1: add support for multiple chip aggregation Jonathan Santos
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox