Skip to contentSkip to conversation

spi: add multi-CS and multi-chip aggregation support

PATCH Superseded Jonathan Santos 6 participants 23 messages Aug 17 – Sep 2

Subsystems (6)

v3 of 4 Superseded Latest is v4 →

lore
Share & tools
lore
#0 lore raw

v3 cover spi: add multi-CS and multi-chip aggregation support

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.

---
v3 summary:
* Added new patch to document the properties for multidevice support on
ad7768-1 bindings.
* Refactored "spi: introduce SPI ancillary device with lanes" patch
to reduce duplicated code using helper functions.
* Modified channel mask allocation on ad7768_parse_aaf_gain() to get
a static value, Since we assume the family is single channel. Added a
comment explaining.
* Link to v2: https://lore.kernel.org/linux-spi/[email protected]/T/#t

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/[email protected]/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%[email protected]/T/#t
[2]: https://lore.kernel.org/all/[email protected]/

Jonathan Santos (6):
dt-bindings: iio: adc: ad7768-1: Document multidevice support
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

 .../bindings/iio/adc/adi,ad7768-1.yaml        |  78 +++++++++-
 drivers/iio/adc/ad7768-1.c                    | 100 +++++++++++--
 drivers/spi/spi-axi-spi-engine.c              |  22 ++-
 drivers/spi/spi.c                             | 135 +++++++++++++++---
 include/linux/spi/spi.h                       |   8 ++
 5 files changed, 305 insertions(+), 38 deletions(-)

base-commit: 350d1fb9204b13c5f95e511e98b8bcb47574d425

Signature · 1 line
2.34.1
#1 lore raw

PATCH v3 1/6 dt-bindings: iio: adc: ad7768-1: Document multidevice support

The AD7768-1 family supports aggregating up to four devices on a single SPI controller, with each device occupying a dedicated CS line and a single data lane.

Extend the reg property to accept up to 4 entries (one per device).

Add spi-rx-bus-width property to declare the per-device lane count. Since each lane is bound to one CS, the number of entries must match reg. Make it required when reg has more than one entry.

Add an example for a four ADAQ7768-1 aggregation, with shared DRDY and SYNC_IN pin.

---
Changes in v3:
* New patch.

---
 .../bindings/iio/adc/adi,ad7768-1.yaml        | 78 ++++++++++++++++++-
 1 file changed, 77 insertions(+), 1 deletion(-)
Documentation/devicetree/bindings/iio/adc/adi,ad7768-1.yaml +77 -1
index dfa2d7fa9fb3..804793a56d1d 100644
@@ -26,7 +26,10 @@ properties:
       - adi,adaq7769-1
 
   reg:
-    maxItems: 1
+    minItems: 1
+    maxItems: 4
+    description:
+      Chip select number. For multi-device setups, one entry per device.
 
   clocks:
     maxItems: 1
@@ -109,6 +112,12 @@ properties:
   reset-gpios:
     maxItems: 1
 
+  spi-rx-bus-width:
+    description:
+      Bus width per CS. For multi-device setups (multiple reg entries), one
+      entry per device must be provided. Required when reg has more than
+      one entry.
+
   spi-cpol: true
 
   spi-cpha: true
@@ -203,6 +212,15 @@ allOf:
       properties:
         pga-gpios: false
 
+  # spi-rx-bus-width is required for multi-device setups
+  - if:
+      properties:
+        reg:
+          minItems: 2
+    then:
+      required:
+        - spi-rx-bus-width
+
 unevaluatedProperties: false
 
 examples:
@@ -244,4 +262,62 @@ examples:
             };
         };
     };
+  - |
+    #include <dt-bindings/interrupt-controller/irq.h>
+    #include <dt-bindings/gpio/gpio.h>
+    #include <dt-bindings/iio/adc/adi,ad7768-1.h>
+    /*
+     * Four ADAQ7768-1 devices aggregated on a single SPI controller.
+     * Each device occupies one CS and one data lane; DRDY and SYNC_IN
+     * pins are wired together across all four devices.
+     */
+    spi {
+        #address-cells = <1>;
+        #size-cells = <0>;
+
+        adc@0 {
+            compatible = "adi,adaq7768-1";
+            reg = <0>, <1>, <2>, <3>;
+            spi-max-frequency = <2000000>;
+            spi-rx-bus-width = <1>, <1>, <1>, <1>;
+            spi-cpol;
+            spi-cpha;
+            vref-supply = <&adc_vref>;
+            pga-gpios = <&gpio 10 GPIO_ACTIVE_HIGH>,
+                        <&gpio 11 GPIO_ACTIVE_HIGH>,
+                        <&gpio 12 GPIO_ACTIVE_HIGH>;
+            interrupts = <25 IRQ_TYPE_EDGE_RISING>;
+            interrupt-parent = <&gpio>;
+            #trigger-source-cells = <1>;
+            adi,sync-in-gpios = <&gpio 22 GPIO_ACTIVE_LOW>;
+            reset-gpios = <&gpio 27 GPIO_ACTIVE_LOW>;
+            clocks = <&ad7768_mclk>;
+            clock-names = "mclk";
+            gpio-controller;
+            #gpio-cells = <2>;
+            #io-channel-cells = <1>;
+            #address-cells = <1>;
+            #size-cells = <0>;
+
+            channel@0 {
+                reg = <0>;
+                label = "channel_0";
+            };
+
+            channel@1 {
+                reg = <1>;
+                label = "channel_1";
+            };
+
+            channel@2 {
+                reg = <2>;
+                label = "channel_2";
+            };
+
+            channel@3 {
+                reg = <3>;
+                label = "channel_3";
+            };
+        };
+    };
 ...
Signature · 1 line
2.34.1
#2 lore raw

Re: [PATCH v3 1/6] dt-bindings: iio: adc: ad7768-1: Document multidevice support

Quoted 10 lines · Jonathan Santos

The AD7768-1 family supports aggregating up to four devices on a single SPI controller, with each device occupying a dedicated CS line and a single data lane.

Extend the reg property to accept up to 4 entries (one per device).

Add spi-rx-bus-width property to declare the per-device lane count. Since each lane is bound to one CS, the number of entries must match reg. Make it required when reg has more than one entry.

Quoted 7 lines

Add an example for a four ADAQ7768-1 aggregation, with shared DRDY and SYNC_IN pin.

---
Changes in v3:
* New patch.

Please use scripts/get_maintainers.pl to get a list of necessary people and lists to CC. It might happen, that command when run on an older kernel, gives you outdated entries. Therefore please be sure you base your patches on recent Linux kernel.

Tools like b4 or scripts/get_maintainer.pl provide you proper list of people, so fix your workflow. Tools might also fail if you work on some ancient tree (don't, instead use mainline) or work on fork of kernel (don't, instead use mainline). Just use b4 and everything should be fine, although remember about b4 prep --auto-to-cc if you added new patches to the patchset.

You missed at least devicetree list (maybe more), so this won't be tested by automated tooling. Performing review on untested code might be a waste of time.

Please kindly resend and include all necessary To/Cc entries.

Best regards,
Krzysztof

reviewer
#3 lore raw

Re: [PATCH v3 1/6] dt-bindings: iio: adc: ad7768-1: Document multidevice support

Quoted 24 lines · Krzysztof Kozlowski
Quoted 10 lines · Jonathan Santos

The AD7768-1 family supports aggregating up to four devices on a single SPI controller, with each device occupying a dedicated CS line and a single data lane.

Extend the reg property to accept up to 4 entries (one per device).

Add spi-rx-bus-width property to declare the per-device lane count. Since each lane is bound to one CS, the number of entries must match reg. Make it required when reg has more than one entry.

Quoted 7 lines

Add an example for a four ADAQ7768-1 aggregation, with shared DRDY and SYNC_IN pin.

---
Changes in v3:
* New patch.

Please use scripts/get_maintainers.pl to get a list of necessary people and lists to CC. It might happen, that command when run on an older kernel, gives you outdated entries. Therefore please be sure you base your patches on recent Linux kernel.

Quoted 8 lines

Tools like b4 or scripts/get_maintainer.pl provide you proper list of people, so fix your workflow. Tools might also fail if you work on some ancient tree (don't, instead use mainline) or work on fork of kernel (don't, instead use mainline). Just use b4 and everything should be fine, although remember about b4 prep --auto-to-cc if you added new patches to the patchset.

You missed at least devicetree list (maybe more), so this won't be

Also missed IIO.

Quoted 7 lines

tested by automated tooling. Performing review on untested code might be a waste of time.

Please kindly resend and include all necessary To/Cc entries.

Best regards,
Krzysztof

reviewer
#4 lore raw

Re: [PATCH v3 1/6] dt-bindings: iio: adc: ad7768-1: Document multidevice support

In reply to Jonathan Santos
Quoted 10 lines · Jonathan Santos

The AD7768-1 family supports aggregating up to four devices on a single SPI controller, with each device occupying a dedicated CS line and a single data lane.

Extend the reg property to accept up to 4 entries (one per device).

Add spi-rx-bus-width property to declare the per-device lane count. Since each lane is bound to one CS, the number of entries must match reg. Make it required when reg has more than one entry.

I don't see why we should have to require the spi-rx-bus-width property. It seems logical that if it is not present, then we would assume one lane per CS. This is already how bindings work.

For example, adi,ad4080 and ge,achc have multiple CS lines and don't require any bus width properties.

maintainer
#5 lore raw

Re: [PATCH v3 1/6] dt-bindings: iio: adc: ad7768-1: Document multidevice support

Quoted 11 lines · David Lechner
Quoted 10 lines · Jonathan Santos

The AD7768-1 family supports aggregating up to four devices on a single SPI controller, with each device occupying a dedicated CS line and a single data lane.

Extend the reg property to accept up to 4 entries (one per device).

Add spi-rx-bus-width property to declare the per-device lane count. Since each lane is bound to one CS, the number of entries must match reg. Make it required when reg has more than one entry.

Quoted 6 lines

I don't see why we should have to require the spi-rx-bus-width property. It seems logical that if it is not present, then we would assume one lane per CS. This is already how bindings work.

For example, adi,ad4080 and ge,achc have multiple CS lines and don't require any bus width properties.

Hmm that's my default then! I was the one requesting it. Basically because of:

https://elixir.bootlin.com/linux/v7.2/source/Documentation/devicetree/bindings/spi/spi-peripheral-props.yaml#L76

So I thought that $refing spi-peripheral-props.yaml and leaving the default value would make dtbs_check to fail if one used

spi-rx-bus-width = <1>, <1>, <1>, <1>;

in DT.

Won't it fail?

And if we don't give the property I believe we'll have:

https://elixir.bootlin.com/linux/v7.2/source/drivers/spi/spi.c#L2509

And given these series assumption of spi->num_rx_lanes == number of devices (which I don't agree btw), I thought we would need to make spi-rx-bus-width explicit in our bindings! I still think but I might be wrong and I'm by no means an expert on dt bindings schemas work :)

- Nuno Sá

Quoted 1 line
reviewer
#6 lore raw

Re: [PATCH v3 1/6] dt-bindings: iio: adc: ad7768-1: Document multidevice support

Quoted 35 lines · Nuno Sá
Quoted 11 lines · David Lechner
Quoted 10 lines · Jonathan Santos

The AD7768-1 family supports aggregating up to four devices on a single SPI controller, with each device occupying a dedicated CS line and a single data lane.

Extend the reg property to accept up to 4 entries (one per device).

Add spi-rx-bus-width property to declare the per-device lane count. Since each lane is bound to one CS, the number of entries must match reg. Make it required when reg has more than one entry.

Quoted 6 lines

I don't see why we should have to require the spi-rx-bus-width property. It seems logical that if it is not present, then we would assume one lane per CS. This is already how bindings work.

For example, adi,ad4080 and ge,achc have multiple CS lines and don't require any bus width properties.

Hmm that's my default then! I was the one requesting it. Basically because of:

https://elixir.bootlin.com/linux/v7.2/source/Documentation/devicetree/bindings/spi/spi-peripheral-props.yaml#L76

So I thought that $refing spi-peripheral-props.yaml and leaving the default value would make dtbs_check to fail if one used

spi-rx-bus-width = <1>, <1>, <1>, <1>;

in DT.

Won't it fail?

And if we don't give the property I believe we'll have:

Quoted 10 lines

https://elixir.bootlin.com/linux/v7.2/source/drivers/spi/spi.c#L2509

And given these series assumption of spi->num_rx_lanes == number of devices (which I don't agree btw), I thought we would need to make spi-rx-bus-width explicit in our bindings! I still think but I might be wrong and I'm by no means an expert on dt bindings schemas work :)

- Nuno Sá

Quoted 1 line

Now that I'm reading the later patches again, it is jogging my memory. I forgot that we want this to operate like a single multi-lane device when reading data.

So for that part, yes, having spi-rx-bus-width kind of makes sense.

And I'm thinking that we might need a new property to indicate that this should be treated as a composite device rather than main + 3 auxiliary. I'll reply more about that in a later patch.

#7 lore raw

PATCH v3 2/6 spi: support simultaneous assertion of multiple CS

In reply to Jonathan Santos

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.

---
Changes in v3:
* None.

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(-)
drivers/spi/spi.c +5 -4
index d9e6b4b87c89..55fb96fea243 100644
@@ -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))
Signature · 1 line
2.34.1
#8 lore raw

Re: [PATCH v3 2/6] spi: support simultaneous assertion of multiple CS

Quoted 18 lines · Jonathan Santos

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.

...

drivers/spi/spi.c static int of_spi_parse_dt(struct spi_controller *ctlr, struct spi_device *spi, · line 2601

Quoted 9 lines
drivers/spi/spi.c +4 -3
 	/*
-	 * 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);

Maybe I'm missing something, but isn't it a simple mask, and for-loop is not required?

	spi->cs_index_mask = BIT(rc + 1) - 1;

OR

	spi->cs_index_mask = GENMASK(rc, 0); // This is better as it supports rc to be 32 or 64
Signature · 2 lines
With Best Regards,
Andy Shevchenko
reviewer
#9 lore raw

Re: [PATCH v3 2/6] spi: support simultaneous assertion of multiple CS

In reply to Jonathan Santos
Quoted 50 lines · Jonathan Santos

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.

---
Changes in v3:
* None.

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(-)
drivers/spi/spi.c +1 -2
index d9e6b4b87c89..55fb96fea243 100644
@@ -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,

drivers/spi/spi.c static int of_spi_parse_dt(struct spi_controller *ctlr, struct spi_device *spi, · line 2604

Quoted 10 lines
drivers/spi/spi.c +4 -2
-	 * 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))

I have the same concern that sashiko calls out here.

Existing users of multi-cs (not including spi-mem) follow the pattern that a SPI device gets registered with the CS at index 0 and they later create an auxiliary using the additional CS. This would cause the main device to now assert both CS. Not what we want to happen.

If I understood (and remember) the previous explanations of this series correctly, we have a different case for this one.

We want a main device that acts as a single composite device that asserts all 4 CS at the same time. Then we also need 4 auxiliary devices that only assert one CS at a time for configuring the individual chips.

So it seems to me like we need a new DT property or some way to be able to tell the difference to decide whether we just use the first CS here or all of them.

Perhaps another possibility would be to leave this code the way it is and do it this way instead:
- The SPI device passed to the IIO driver is just the first chip (one CS)
- The IIO driver then registers auxiliary drivers for the other 3 chips (also 1 CS each)
- These 4 devices will be used individual to handle configuration.
- The IIO driver registers a separate composite device that has the multiple lanes and and multiple CS. Whether this using the same auxiliary mechanism with additional parameters or something new probably doesn't matter too much.
- In this way of doing things, it would not make sense to have the spi-rx-bus-width property in the devcietree since as far as the devicetree is concerned, these are more-or-less 4 separate devices (from the SPI point of view). Instead, this new composite device registration function would be the one setting the number of lanes based on the number of chip selects.

---
sashiko output for reference:

--- Patch [2]: [PATCH v3 2/6] spi: support simultaneous assertion of multiple CS --- commit 17290644299c39d6ca397b14180cd885c5b04919
Author: Jonathan Santos <[email protected]>
Subject: spi: support simultaneous assertion of multiple CS

This commit modifies spi_set_cs() and of_spi_parse_dt() to allow simultaneous toggling of multiple chip select lines. It builds cs_index_mask from all parsed CS entries instead of hardcoding bit 0.

Quoted 4 lines
drivers/spi/spi.c +0 -0
index d9e6b4b87c899..55fb96fea2432 100644

[ ... ]

drivers/spi/spi.c static int of_spi_parse_dt(struct spi_controller *ctlr, struct spi_device *spi, · line 2601

Quoted 12 lines
drivers/spi/spi.c +4 -3
@@ -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);

Will this loop break backward compatibility for devices that use multiple reg entries for independent logical chip selects?

For example, if a device has multiple reg entries and uses spi_new_ancillary_device(), the primary device's cs_index_mask will now have multiple bits set. During a transfer on the primary device, all of these parsed chip selects would be asserted simultaneously.

Could this result in multiple chips driving the shared MISO line at the same time, causing bus contention and data corruption?

Quoted 3 lines
drivers/spi/spi.c +0 -0
 	/* Device speed */
 	if (!of_property_read_u32(nc, "spi-max-frequency", &value))
#10 lore raw

Re: [PATCH v3 2/6] spi: support simultaneous assertion of multiple CS

Quoted 64 lines · David Lechner
Quoted 50 lines · Jonathan Santos

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.

---
Changes in v3:
* None.

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(-)
drivers/spi/spi.c +1 -2
index d9e6b4b87c89..55fb96fea243 100644
@@ -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,

drivers/spi/spi.c static int of_spi_parse_dt(struct spi_controller *ctlr, struct spi_device *spi, · line 2604

Quoted 10 lines
drivers/spi/spi.c +4 -2
-	 * 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))

I have the same concern that sashiko calls out here.

Quoted 4 lines

Existing users of multi-cs (not including spi-mem) follow the pattern that a SPI device gets registered with the CS at index 0 and they later create an auxiliary using the additional CS. This would cause the main device to now assert both CS. Not what we want to happen.

Indeed, enalbing all CS for those cases would cause problems (like you describe below). AD4080 would be an example.

The devicetree property seems to be a good ideia to fix this and simpler to execute, but the composite device is interesting.

Quoted 12 lines

If I understood (and remember) the previous explanations of this series correctly, we have a different case for this one.

We want a main device that acts as a single composite device that asserts all 4 CS at the same time. Then we also need 4 auxiliary devices that only assert one CS at a time for configuring the individual chips.

So it seems to me like we need a new DT property or some way to be able to tell the difference to decide whether we just use the first CS here or all of them.

Quoted 17 lines

Perhaps another possibility would be to leave this code the way it is and do it this way instead:
- The SPI device passed to the IIO driver is just the first chip (one CS)
- The IIO driver then registers auxiliary drivers for the other 3 chips (also 1 CS each)
- These 4 devices will be used individual to handle configuration.
- The IIO driver registers a separate composite device that has the multiple lanes and and multiple CS. Whether this using the same auxiliary mechanism with additional parameters or something new probably doesn't matter too much.
- In this way of doing things, it would not make sense to have the spi-rx-bus-width property in the devcietree since as far as the devicetree is concerned, these are more-or-less 4 separate devices (from the SPI point of view). Instead, this new composite device registration function would be the one setting the number of lanes based on the number of chip selects.

My initial idea was to use the main device as the "composite" because it holds the hw description, therefore the multi-lane would be handled by the existing flow (that's why the spi-rx-bus-width property was mandatory). This way each ancillary device would take the lane index from the main device. Using the main + 3 ancillaries for individual access and the new composite for simultaneous is a great alternative, but removing the spi-rx-bus-width kind of excludes the information that we are using multiple lanes for this device. Unless we document that "number of CS = number of lanes" on multiple-data-lanes.rst, the assumption is:

spi-rx-bus-width = <1>
spi-rx-lane-map = <1>

Also, it would make more sense to take the lane description from the main device described in the DT considering cases like non-default mapping. Or at least take it into consideration.

Quoted 36 lines

---
sashiko output for reference:

--- Patch [2]: [PATCH v3 2/6] spi: support simultaneous assertion of multiple CS --- commit 17290644299c39d6ca397b14180cd885c5b04919
Author: Jonathan Santos <[email protected]>
Subject: spi: support simultaneous assertion of multiple CS

This commit modifies spi_set_cs() and of_spi_parse_dt() to allow simultaneous toggling of multiple chip select lines. It builds cs_index_mask from all parsed CS entries instead of hardcoding bit 0.

Quoted 4 lines
drivers/spi/spi.c +0 -0
index d9e6b4b87c899..55fb96fea2432 100644

[ ... ]

drivers/spi/spi.c static int of_spi_parse_dt(struct spi_controller *ctlr, struct spi_device *spi, · line 2601

Quoted 12 lines
drivers/spi/spi.c +4 -3
@@ -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);

Will this loop break backward compatibility for devices that use multiple reg entries for independent logical chip selects?

Quoted 8 lines

For example, if a device has multiple reg entries and uses spi_new_ancillary_device(), the primary device's cs_index_mask will now have multiple bits set. During a transfer on the primary device, all of these parsed chip selects would be asserted simultaneously.

Could this result in multiple chips driving the shared MISO line at the same time, causing bus contention and data corruption?

Yes, that could happen.

Quoted 3 lines
Quoted 3 lines
drivers/spi/spi.c +0 -0
 	/* Device speed */
 	if (!of_property_read_u32(nc, "spi-max-frequency", &value))
reviewer
#11 lore raw

Re: [PATCH v3 2/6] spi: support simultaneous assertion of multiple CS

Quoted 106 lines · Jonathan Santos
Quoted 64 lines · David Lechner
Quoted 50 lines · Jonathan Santos

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.

---
Changes in v3:
* None.

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(-)
drivers/spi/spi.c +1 -2
index d9e6b4b87c89..55fb96fea243 100644
@@ -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,

drivers/spi/spi.c static int of_spi_parse_dt(struct spi_controller *ctlr, struct spi_device *spi, · line 2604

Quoted 10 lines
drivers/spi/spi.c +4 -2
-	 * 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))

I have the same concern that sashiko calls out here.

Quoted 4 lines

Existing users of multi-cs (not including spi-mem) follow the pattern that a SPI device gets registered with the CS at index 0 and they later create an auxiliary using the additional CS. This would cause the main device to now assert both CS. Not what we want to happen.

Indeed, enalbing all CS for those cases would cause problems (like you describe below). AD4080 would be an example.

The devicetree property seems to be a good ideia to fix this and simpler to execute, but the composite device is interesting.

Quoted 12 lines

If I understood (and remember) the previous explanations of this series correctly, we have a different case for this one.

We want a main device that acts as a single composite device that asserts all 4 CS at the same time. Then we also need 4 auxiliary devices that only assert one CS at a time for configuring the individual chips.

So it seems to me like we need a new DT property or some way to be able to tell the difference to decide whether we just use the first CS here or all of them.

Quoted 17 lines

Perhaps another possibility would be to leave this code the way it is and do it this way instead:
- The SPI device passed to the IIO driver is just the first chip (one CS)
- The IIO driver then registers auxiliary drivers for the other 3 chips (also 1 CS each)
- These 4 devices will be used individual to handle configuration.
- The IIO driver registers a separate composite device that has the multiple lanes and and multiple CS. Whether this using the same auxiliary mechanism with additional parameters or something new probably doesn't matter too much.
- In this way of doing things, it would not make sense to have the spi-rx-bus-width property in the devcietree since as far as the devicetree is concerned, these are more-or-less 4 separate devices (from the SPI point of view). Instead, this new composite device registration function would be the one setting the number of lanes based on the number of chip selects.

Quoted 8 lines

My initial idea was to use the main device as the "composite" because it holds the hw description, therefore the multi-lane would be handled by the existing flow (that's why the spi-rx-bus-width property was mandatory). This way each ancillary device would take the lane index from the main device. Using the main + 3 ancillaries for individual access and the new composite for simultaneous is a great alternative, but removing the spi-rx-bus-width kind of excludes the information that we are using multiple lanes for this device. Unless we document that

I don't think it hides it. How the chip handles multi-lane is described by the compatible string itself. So it will be up to the IIO driver to call the composite device creation function with the appropriate parameters to tell it that in this case we need to assert all 4 CS in order to get a 4-lane device. I don't think we need any extra devicetree description for this.

Quoted 9 lines

"number of CS = number of lanes" on multiple-data-lanes.rst, the assumption is:

spi-rx-bus-width = <1>
spi-rx-lane-map = <1>

Also, it would make more sense to take the lane description from the main device described in the DT considering cases like non-default mapping. Or at least take it into consideration.

I think if we put the CS lines in a logical order, we don't need any mapping. For example:

CS | Lane
---+-----

0  |  0
1  |  2
2  |  1
3  |  3

Would just be:

reg = <0>, <2>, <1>, <3>;

#12 lore raw

PATCH v3 3/6 spi: expand device name to include all CS lines for multi-CS devices

In reply to Jonathan Santos

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.

---
Changes in v3:
* replaced int idx = 1 by unsigned int idx = 1 in the for loop.

Changes in v2:
* New patch.

---
 drivers/spi/spi.c | 10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)
drivers/spi/spi.c +8 -2
index 55fb96fea243..9ccfa5be557b 100644
@@ -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 (unsigned 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);
 }
 
 /*
Signature · 1 line
2.34.1
#13 lore raw

Re: [PATCH v3 3/6] spi: expand device name to include all CS lines for multi-CS devices

Quoted 8 lines · Jonathan Santos

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.

...

drivers/spi/spi.c static void spi_dev_set_name(struct spi_device *spi) · line 613

Quoted 6 lines
drivers/spi/spi.c +4 -2
-	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 (unsigned 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));

So, if this won't support sparse CS list, I would just go with a standard range:

	dev_set_name(&spi->dev, "%s.%u-%u", dev_name(&spi->controller->dev),
		     spi_get_chipselect(spi, 0), spi_get_chipselect(spi, 0) + spi->num_chipselect - 1);

(yes, need a variation for num_chipselect == 1, but I leave the rest for you)

Note, I do not see that we are expecting to see the sparse CS list here. It would make too complex logic to follow. So, range approach is KISS in my opinion.

Signature · 2 lines
With Best Regards,
Andy Shevchenko
maintainer
#14 lore raw

Re: [PATCH v3 3/6] spi: expand device name to include all CS lines for multi-CS devices

Quoted 22 lines · Andy Shevchenko
Quoted 8 lines · Jonathan Santos

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.

...

drivers/spi/spi.c static void spi_dev_set_name(struct spi_device *spi) · line 613

Quoted 6 lines
drivers/spi/spi.c +4 -2
-	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 (unsigned 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));

So, if this won't support sparse CS list, I would just go with a standard range:

Quoted 3 lines
	dev_set_name(&spi->dev, "%s.%u-%u", dev_name(&spi->controller->dev),
		     spi_get_chipselect(spi, 0), spi_get_chipselect(spi, 0) + spi->num_chipselect - 1);

I don't think there's anything forcing cs to be contiguous. So cs4-0 does not mean we have 0,1,2,3 and 4 which I think the above implies.

Moreover, i also don't think is guaranteed that `spi_get_chipselect(spi,
0) + spi->num_chipselect - 1)` yields a valid cs. IOW, in theory

spi_get_chipselect(spi, spi->num_chipselect - 1) might be different than spi_get_chipselect(spi, 0) + spi->num_chipselect - 1

- Nuno Sá

Quoted 4 lines

(yes, need a variation for num_chipselect == 1, but I leave the rest for you)

Note, I do not see that we are expecting to see the sparse CS list here. It would make too complex logic to follow. So, range approach is KISS in my opinion.

#15 lore raw

Re: [PATCH v3 3/6] spi: expand device name to include all CS lines for multi-CS devices

Quoted 2 lines · Nuno Sá
Quoted 1 line · Andy Shevchenko

On Mon, Aug 17, 2026 at 08:32:57PM -0300, Jonathan Santos wrote:

...

Quoted 12 lines
Quoted 11 lines

drivers/spi/spi.c static void spi_dev_set_name(struct spi_device *spi) · line 613

Quoted 6 lines
drivers/spi/spi.c +4 -2
-	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 (unsigned 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));

So, if this won't support sparse CS list, I would just go with a standard range:

	dev_set_name(&spi->dev, "%s.%u-%u", dev_name(&spi->controller->dev),
		     spi_get_chipselect(spi, 0), spi_get_chipselect(spi, 0) + spi->num_chipselect - 1);
Quoted 8 lines

I don't think there's anything forcing cs to be contiguous. So cs4-0 does not mean we have 0,1,2,3 and 4 which I think the above implies.

Moreover, i also don't think is guaranteed that `spi_get_chipselect(spi,
0) + spi->num_chipselect - 1)` yields a valid cs. IOW, in theory

spi_get_chipselect(spi, spi->num_chipselect - 1) might be different than spi_get_chipselect(spi, 0) + spi->num_chipselect - 1

So, I would expect then two things:
- following the KISS and make sure we operate on the sequential CS ranges.
- definitely add some test cases, now it's a time (I consider NAKing any version without test cases of this rather complex mapping logic)

Quoted 4 lines
Quoted 4 lines

(yes, need a variation for num_chipselect == 1, but I leave the rest for you)

Note, I do not see that we are expecting to see the sparse CS list here. It would make too complex logic to follow. So, range approach is KISS in my opinion.

Signature · 2 lines
With Best Regards,
Andy Shevchenko
#16 lore raw

PATCH v3 4/6 spi: introduce SPI ancillary device with lanes

In reply to Jonathan Santos

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.

---
Changes in v3:
* Refactored code using helper function to reduce duplicated code.

Changes in v2:
* New patch.

---
 drivers/spi/spi.c       | 116 +++++++++++++++++++++++++++++++++++-----
 include/linux/spi/spi.h |   8 +++
 2 files changed, 110 insertions(+), 14 deletions(-)
drivers/spi/spi.c +102 -14
index 9ccfa5be557b..174507696f1f 100644
@@ -2692,20 +2692,12 @@ static void of_register_spi_devices(struct spi_controller *ctlr)
 static void of_register_spi_devices(struct spi_controller *ctlr) { }
 #endif
 
-/**
- * spi_new_ancillary_device() - Register ancillary SPI device
- * @spi:         Pointer to the main SPI device registering the ancillary device
- * @chip_select: Chip Select of the ancillary device
- *
- * Register an ancillary SPI device; for example some chips have a chip-select
- * for normal device usage and another one for setup/firmware upload.
- *
- * This may only be called from main SPI device's probe routine.
- *
- * Return: 0 on success; negative errno on failure
- */
-struct spi_device *spi_new_ancillary_device(struct spi_device *spi,
-					     u8 chip_select)
+#define SPI_UNDEFINED_LANE	-1
+
+static struct spi_device *__spi_new_ancillary_device(struct spi_device *spi,
+						     u8 chip_select,
+						     int rx_lane_idx,
+						     int tx_lane_idx)
 {
 	struct spi_controller *ctlr = spi->controller;
 	struct spi_device *ancillary;
@@ -2732,6 +2724,21 @@ struct spi_device *spi_new_ancillary_device(struct spi_device *spi,
 	 */
 	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;
+	}
+
+	if (rx_lane_idx != SPI_UNDEFINED_LANE) {
+		ancillary->rx_lane_map[0] = spi->rx_lane_map[rx_lane_idx];
+		ancillary->num_rx_lanes = 1;
+	}
+
+	if (tx_lane_idx != SPI_UNDEFINED_LANE) {
+		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 */
@@ -2747,8 +2754,53 @@ struct spi_device *spi_new_ancillary_device(struct spi_device *spi,
 	spi_dev_put(ancillary);
 	return ERR_PTR(rc);
 }
+
+/**
+ * spi_new_ancillary_device() - Register ancillary SPI device
+ * @spi:         Pointer to the main SPI device registering the ancillary device
+ * @chip_select: Chip Select of the ancillary device
+ *
+ * Register an ancillary SPI device; for example some chips have a chip-select
+ * for normal device usage and another one for setup/firmware upload.
+ *
+ * This may only be called from main SPI device's probe routine.
+ *
+ * Return: 0 on success; negative errno on failure
+ */
+struct spi_device *spi_new_ancillary_device(struct spi_device *spi,
+					    u8 chip_select)
+{
+	return __spi_new_ancillary_device(spi, chip_select,
+					  SPI_UNDEFINED_LANE,
+					  SPI_UNDEFINED_LANE);
+}
 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)
+{
+	return __spi_new_ancillary_device(spi, chip_select, rx_lane_idx, tx_lane_idx);
+}
+EXPORT_SYMBOL_GPL(spi_new_ancillary_device_with_lane);
+
 static void devm_spi_unregister_device(void *spi)
 {
 	spi_unregister_device(spi);
@@ -2789,6 +2841,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;
include/linux/spi/spi.h +8 -0
index 4c285d3ede1d..91c9e2d2e7e3 100644
@@ -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) \
Signature · 1 line
2.34.1
#17 lore raw

Re: [PATCH v3 4/6] spi: introduce SPI ancillary device with lanes

Quoted 11 lines · Jonathan Santos

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.

...

include/linux/spi/spi.h static inline void spi_unregister_driver(struct spi_driver *sdrv) · line 395

Quoted 10 lines

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);

include/linux/spi/spi.h +8 -0
+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);

Can we stop using legacy 'extern' for the function declarations?

Signature · 2 lines
With Best Regards,
Andy Shevchenko
#18 lore raw

PATCH v3 5/6 spi: spi-engine-ex: Add support for multi-CS devices

In reply to Jonathan Santos

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.

---
Changes in v3:
* None.

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(-)
drivers/spi/spi-axi-spi-engine.c +16 -6
index 02bbc5d0cfc5..8a2cae81aea6 100644
@@ -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)) {
Signature · 1 line
2.34.1
#19 lore raw

Re: [PATCH v3 5/6] spi: spi-engine-ex: Add support for multi-CS devices

Quoted 11 lines · Jonathan Santos

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.

...

drivers/spi/spi-axi-spi-engine.c static int spi_engine_setup(struct spi_device *device) · line 903

Quoted 9 lines
drivers/spi/spi-axi-spi-engine.c +8 -0
+		for_each_set_bit(cs_bit, &cs_index_mask, SPI_ENGINE_MAX_CS)
+			mask ^= BIT(spi_get_chipselect(spi, cs_bit));
 
+	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));
+	}

Looking at these two I would rather see a helper

unsigned long spi_get_chipselect_bits(..., unsigned long index_mask)

With that the above becomes for-loop free.

		mask ^= spi_get_chipselect_bits(spi, cs_index_mask);

and

	if (device->mode & SPI_CS_HIGH)
		spi_engine->cs_inv |= spi_get_chipselect_bits(device, cs_index_mask));
	else
		spi_engine->cs_inv &= ~spi_get_chipselect_bits(device, cs_index_mask));

respectively.

Signature · 2 lines
With Best Regards,
Andy Shevchenko
#20 lore raw

PATCH v3 6/6 iio: adc: ad7768-1: add support for multiple chip aggregation

In reply to Jonathan Santos

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.

---
Changes in v3:
* Modified channel mask allocation on ad7768_parse_aaf_gain() to get a static value, Since we assume the family is single channel. Added a comment explaining.

Changes in v2:
* New patch.

---
 drivers/iio/adc/ad7768-1.c | 100 +++++++++++++++++++++++++++++++++----
 1 file changed, 89 insertions(+), 11 deletions(-)
drivers/iio/adc/ad7768-1.c +89 -11
index e9060c1bbe6f..deff36165ed1 100644
@@ -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,56 @@ 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;
+
+	/* One mask entry, considering single channel ADCs, plus a zero terminator */
+	masks = devm_kcalloc(dev, 2, 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 +1892,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 single-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 +1924,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 +1967,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;
 
Signature · 1 line
2.34.1
#21 lore raw

Re: [PATCH v3 6/6] iio: adc: ad7768-1: add support for multiple chip aggregation

Quoted 20 lines · Jonathan Santos

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.

...

drivers/iio/adc/ad7768-1.c struct ad7768_chip_info { · line 309

Quoted 3 lines
drivers/iio/adc/ad7768-1.c +2 -0
+	struct spi_device *spi_anc[AD7768_MAX_AGGR_DEVICES];
 
+	struct regmap *regmap24_anc[AD7768_MAX_AGGR_DEVICES];

Yeah, this will be enormous memory consumption...

...

drivers/iio/adc/ad7768-1.c static int ad7768_parse_aaf_gain(struct device *dev, struct ad7768_state *st) · line 1716

Quoted 8 lines
drivers/iio/adc/ad7768-1.c +8 -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;

Why is 'i' signed?

drivers/iio/adc/ad7768-1.c static int ad7768_parse_aaf_gain(struct device *dev, struct ad7768_state *st) · line 1718

Quoted 1 line
drivers/iio/adc/ad7768-1.c +1 -0
+	indio_dev->num_channels = st->num_devices * st->chip->num_channels;

This wants to have size_mul() from overflow.h to follow what allocator is using beneath.

Quoted 8 lines
drivers/iio/adc/ad7768-1.c +8 -0
+	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;

drivers/iio/adc/ad7768-1.c static int ad7768_parse_aaf_gain(struct device *dev, struct ad7768_state *st) · line 1736

Quoted 10 lines
drivers/iio/adc/ad7768-1.c +10 -0
+		chan->channel = i;
+		chan->scan_index = i;
+	}
+
+	indio_dev->channels = channels;
+
+	/* One mask entry, considering single channel ADCs, plus a zero terminator */
+	masks = devm_kcalloc(dev, 2, sizeof(*masks), GFP_KERNEL);
+	if (!masks)
+		return -ENOMEM;

Isn't this devm_bitmap_zalloc()? Yes, it might require to reconsider the design of the masks.

drivers/iio/adc/ad7768-1.c static int ad7768_parse_aaf_gain(struct device *dev, struct ad7768_state *st) · line 1738

Quoted 1 line
drivers/iio/adc/ad7768-1.c +1 -0
+	masks[0] = GENMASK(st->num_devices - 1, 0);

bitmap_set() (needs bitmap.h)

Quoted 19 lines
drivers/iio/adc/ad7768-1.c +19 -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;
+}

...

drivers/iio/adc/ad7768-1.c static int ad7768_probe(struct spi_device *spi) · line 1933

Quoted 4 lines
drivers/iio/adc/ad7768-1.c +4 -0
+	if (st->num_devices > 1) {
+		ret = ad7768_probe_multidevices(indio_dev);
+		if (ret)
+			return dev_err_probe(&spi->dev, ret,

Having

	struct device *dev = &spi->dev;

may help here and elsewhere to make code neater.

Quoted 5 lines
drivers/iio/adc/ad7768-1.c +5 -0
+					     "Failed to configure multidevice\n");
+	} else {
+		indio_dev->channels = st->chip->channel_spec;
+		indio_dev->num_channels = st->chip->num_channels;
+	}
Signature · 2 lines
With Best Regards,
Andy Shevchenko
maintainer
#22 lore raw

Re: [PATCH v3 6/6] iio: adc: ad7768-1: add support for multiple chip aggregation

In reply to Jonathan Santos

drivers/iio/adc/ad7768-1.c static int ad7768_probe(struct spi_device *spi) · line 1899

Quoted 13 lines · Jonathan Santos
drivers/iio/adc/ad7768-1.c +5 -0
index e9060c1bbe6f..deff36165ed1 100644
@@ -1830,6 +1892,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 single-lane devices, so we assume that
+	 * each lane is bound to a different device.
+	 */
+	st->num_devices = spi->num_rx_lanes;

I still don't agree with the above! At the very least we should infer st->num_devices from the number of chipselects. Anyways, won't extend much the review for now given that David seems to have some ideas different from the ancillary devices.

- Nuno Sá

Signature · 1 line
Nuno Sá <[email protected]>