summaryrefslogtreecommitdiffstats
path: root/Documentation/spi/multiple-data-lanes.rst
blob: 69cb532d052fb105833332cb7baab85eee4f6163 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
====================================
SPI devices with multiple data lanes
====================================

Some specialized SPI controllers and peripherals support multiple data lanes
that allow reading more than one word at a time in parallel. This is different
from dual/quad/octal SPI where multiple bits of a single word are transferred
simultaneously.

For example, controllers that support parallel flash memories have this feature
as do some simultaneous-sampling ADCs where each channel has its own data lane.

---------------------
Describing the wiring
---------------------

The ``spi-tx-bus-width`` and ``spi-rx-bus-width`` properties in the devicetree
are used to describe how many data lanes are connected between the controller
and how wide each lane is. The number of items in the array indicates how many
lanes there are, and the value of each item indicates how many bits wide that
lane is.

For example, a dual-simultaneous-sampling ADC with two 4-bit lanes might be
wired up like this::

    +--------------+    +----------+
    | SPI          |    | AD4630   |
    | Controller   |    | ADC      |
    |              |    |          |
    |          CS0 |--->| CS       |
    |          SCK |--->| SCK      |
    |          SDO |--->| SDI      |
    |              |    |          |
    |        SDIA0 |<---| SDOA0    |
    |        SDIA1 |<---| SDOA1    |
    |        SDIA2 |<---| SDOA2    |
    |        SDIA3 |<---| SDOA3    |
    |              |    |          |
    |        SDIB0 |<---| SDOB0    |
    |        SDIB1 |<---| SDOB1    |
    |        SDIB2 |<---| SDOB2    |
    |        SDIB3 |<---| SDOB3    |
    |              |    |          |
    +--------------+    +----------+

It is described in a devicetree like this::

    spi {
        compatible = "my,spi-controller";

        ...

        adc@0 {
            compatible = "adi,ad4630";
            reg = <0>;
            ...
            spi-rx-bus-width = <4>, <4>; /* 2 lanes of 4 bits each */
            ...
        };
    };

In most cases, lanes will be wired up symmetrically (A to A, B to B, etc). If
this isn't the case, extra ``spi-rx-lane-map`` and ``spi-tx-lane-map``
properties are needed to provide a mapping between controller lanes and the
physical lane wires.

Here is an example where a multi-lane SPI controller has each lane wired to
separate single-lane peripherals::

    +--------------+    +----------+
    | SPI          |    | Thing 1  |
    | Controller   |    |          |
    |              |    |          |
    |          CS0 |--->| CS       |
    |         SDO0 |--->| SDI      |
    |         SDI0 |<---| SDO      |
    |        SCLK0 |--->| SCLK     |
    |              |    |          |
    |              |    +----------+
    |              |
    |              |    +----------+
    |              |    | Thing 2  |
    |              |    |          |
    |          CS1 |--->| CS       |
    |         SDO1 |--->| SDI      |
    |         SDI1 |<---| SDO      |
    |        SCLK1 |--->| SCLK     |
    |              |    |          |
    +--------------+    +----------+

This is described in a devicetree like this::

    spi {
        compatible = "my,spi-controller";

        ...

        thing1@0 {
            compatible = "my,thing1";
            reg = <0>;
            ...
        };

        thing2@1 {
            compatible = "my,thing2";
            reg = <1>;
            ...
            spi-tx-lane-map = <1>; /* lane 0 is not used, lane 1 is used for tx wire */
            spi-rx-lane-map = <1>; /* lane 0 is not used, lane 1 is used for rx wire */
            ...
        };
    };


The default values of ``spi-rx-bus-width`` and ``spi-tx-bus-width`` are ``<1>``,
so these properties can still be omitted even when ``spi-rx-lane-map`` and
``spi-tx-lane-map`` are used.

----------------------------
Usage in a peripheral driver
----------------------------

These types of SPI controllers generally do not support arbitrary use of the
multiple lanes. Instead, they operate in one of a few defined modes. Peripheral
drivers should set the :c:type:`struct spi_transfer.multi_lane_mode <spi_transfer>`
field to indicate which mode they want to use for a given transfer.

The possible values for this field have the following semantics:

- :c:macro:`SPI_MULTI_BUS_MODE_SINGLE`: Only use the first lane. Other lanes are
    ignored. This means that it is operating just like a conventional SPI
    peripheral. This is the default, so it does not need to be explicitly set.

    Example::

        tx_buf[0] = 0x88;

        struct spi_transfer xfer = {
            .tx_buf = tx_buf,
            .len = 1,
        };

        spi_sync_transfer(spi, &xfer, 1);

    Assuming the controller is sending the MSB first, the sequence of bits
    sent over the tx wire would be (right-most bit is sent first)::

        controller    > data bits >     peripheral
        ----------   ----------------   ----------
            SDO 0    0-0-0-1-0-0-0-1    SDI 0

- :c:macro:`SPI_MULTI_BUS_MODE_MIRROR`: Send a single data word over all of the
    lanes at the same time. This only makes sense for writes and not
    for reads.

    Example::

        tx_buf[0] = 0x88;

        struct spi_transfer xfer = {
            .tx_buf = tx_buf,
            .len = 1,
            .multi_lane_mode = SPI_MULTI_BUS_MODE_MIRROR,
        };

        spi_sync_transfer(spi, &xfer, 1);

    The data is mirrored on each tx wire::

        controller    > data bits >     peripheral
        ----------   ----------------   ----------
            SDO 0    0-0-0-1-0-0-0-1    SDI 0
            SDO 1    0-0-0-1-0-0-0-1    SDI 1

- :c:macro:`SPI_MULTI_BUS_MODE_STRIPE`: Send or receive two different data words
    at the same time, one on each lane. This means that the buffer needs to be
    sized to hold data for all lanes. Data is interleaved in the buffer, with
    the first word corresponding to lane 0, the second to lane 1, and so on.
    Once the last lane is used, the next word in the buffer corresponds to lane
    0 again. Accordingly, the buffer size must be a multiple of the number of
    lanes. This mode works for both reads and writes.

    Example::

        struct spi_transfer xfer = {
            .rx_buf = rx_buf,
            .len = 2,
            .multi_lane_mode = SPI_MULTI_BUS_MODE_STRIPE,
        };

        spi_sync_transfer(spi, &xfer, 1);

    Each rx wire has a different data word sent simultaneously::

        controller    < data bits <     peripheral
        ----------   ----------------   ----------
            SDI 0    0-0-0-1-0-0-0-1    SDO 0
            SDI 1    1-0-0-0-1-0-0-0    SDO 1

    After the transfer, ``rx_buf[0] == 0x11`` (word from SDO 0) and
    ``rx_buf[1] == 0x88`` (word from SDO 1).


-----------------------------
SPI controller driver support
-----------------------------

To support multiple data lanes, SPI controller drivers need to set
:c:type:`struct spi_controller.num_data_lanes <spi_controller>` to a value
greater than 1.

Then the part of the driver that handles SPI transfers needs to check the
:c:type:`struct spi_transfer.multi_lane_mode <spi_transfer>` field and implement
the appropriate behavior for each supported mode and return an error for
unsupported modes.

The core SPI code should handle the rest.