Skip to content

Commit c64b612

Browse files
dlechmachschmitt
authored andcommitted
spi: axi-spi-engine: support SPI_MULTI_LANE_MODE_STRIPE
Add support for SPI_MULTI_LANE_MODE_STRIPE to the AXI SPI engine driver. The v2.0.0 version of the AXI SPI Engine IP core supports multiple lanes. This can be used with SPI_MULTI_LANE_MODE_STRIPE to support reading from simultaneous sampling ADCs that have a separate SDO line for each analog channel. This allows reading all channels at the same time to increase throughput. Reviewed-by: Marcelo Schmitt <marcelo.schmitt@analog.com> Reviewed-by: Jonathan Cameron <jonathan.cameron@huawei.com> Signed-off-by: David Lechner <dlechner@baylibre.com> Link: https://patch.msgid.link/20260123-spi-add-multi-bus-support-v6-7-12af183c06eb@baylibre.com Signed-off-by: Mark Brown <broonie@kernel.org>
1 parent 4f22589 commit c64b612

File tree

1 file changed

+141
-4
lines changed

1 file changed

+141
-4
lines changed

drivers/spi/spi-axi-spi-engine.c

Lines changed: 141 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@
2323
#include <linux/spi/spi.h>
2424
#include <trace/events/spi.h>
2525

26+
#define SPI_ENGINE_REG_DATA_WIDTH 0x0C
27+
#define SPI_ENGINE_REG_DATA_WIDTH_NUM_OF_SDIO_MASK GENMASK(23, 16)
28+
#define SPI_ENGINE_REG_DATA_WIDTH_MASK GENMASK(15, 0)
2629
#define SPI_ENGINE_REG_OFFLOAD_MEM_ADDR_WIDTH 0x10
2730
#define SPI_ENGINE_REG_RESET 0x40
2831

@@ -75,6 +78,8 @@
7578
#define SPI_ENGINE_CMD_REG_CLK_DIV 0x0
7679
#define SPI_ENGINE_CMD_REG_CONFIG 0x1
7780
#define SPI_ENGINE_CMD_REG_XFER_BITS 0x2
81+
#define SPI_ENGINE_CMD_REG_SDI_MASK 0x3
82+
#define SPI_ENGINE_CMD_REG_SDO_MASK 0x4
7883

7984
#define SPI_ENGINE_MISC_SYNC 0x0
8085
#define SPI_ENGINE_MISC_SLEEP 0x1
@@ -105,6 +110,10 @@
105110
#define SPI_ENGINE_OFFLOAD_CMD_FIFO_SIZE 16
106111
#define SPI_ENGINE_OFFLOAD_SDO_FIFO_SIZE 16
107112

113+
/* Extending SPI_MULTI_LANE_MODE values for optimizing messages. */
114+
#define SPI_ENGINE_MULTI_BUS_MODE_UNKNOWN -1
115+
#define SPI_ENGINE_MULTI_BUS_MODE_CONFLICTING -2
116+
108117
struct spi_engine_program {
109118
unsigned int length;
110119
uint16_t instructions[] __counted_by(length);
@@ -142,6 +151,11 @@ struct spi_engine_offload {
142151
unsigned long flags;
143152
unsigned int offload_num;
144153
unsigned int spi_mode_config;
154+
unsigned int multi_lane_mode;
155+
u8 rx_primary_lane_mask;
156+
u8 tx_primary_lane_mask;
157+
u8 rx_all_lanes_mask;
158+
u8 tx_all_lanes_mask;
145159
u8 bits_per_word;
146160
};
147161

@@ -165,6 +179,25 @@ struct spi_engine {
165179
bool offload_requires_sync;
166180
};
167181

182+
static void spi_engine_primary_lane_flag(struct spi_device *spi,
183+
u8 *rx_lane_flags, u8 *tx_lane_flags)
184+
{
185+
*rx_lane_flags = BIT(spi->rx_lane_map[0]);
186+
*tx_lane_flags = BIT(spi->tx_lane_map[0]);
187+
}
188+
189+
static void spi_engine_all_lanes_flags(struct spi_device *spi,
190+
u8 *rx_lane_flags, u8 *tx_lane_flags)
191+
{
192+
int i;
193+
194+
for (i = 0; i < spi->num_rx_lanes; i++)
195+
*rx_lane_flags |= BIT(spi->rx_lane_map[i]);
196+
197+
for (i = 0; i < spi->num_tx_lanes; i++)
198+
*tx_lane_flags |= BIT(spi->tx_lane_map[i]);
199+
}
200+
168201
static void spi_engine_program_add_cmd(struct spi_engine_program *p,
169202
bool dry, uint16_t cmd)
170203
{
@@ -193,7 +226,7 @@ static unsigned int spi_engine_get_config(struct spi_device *spi)
193226
}
194227

195228
static void spi_engine_gen_xfer(struct spi_engine_program *p, bool dry,
196-
struct spi_transfer *xfer)
229+
struct spi_transfer *xfer, u32 num_lanes)
197230
{
198231
unsigned int len;
199232

@@ -204,6 +237,9 @@ static void spi_engine_gen_xfer(struct spi_engine_program *p, bool dry,
204237
else
205238
len = xfer->len / 4;
206239

240+
if (xfer->multi_lane_mode == SPI_MULTI_LANE_MODE_STRIPE)
241+
len /= num_lanes;
242+
207243
while (len) {
208244
unsigned int n = min(len, 256U);
209245
unsigned int flags = 0;
@@ -269,6 +305,7 @@ static int spi_engine_precompile_message(struct spi_message *msg)
269305
{
270306
unsigned int clk_div, max_hz = msg->spi->controller->max_speed_hz;
271307
struct spi_transfer *xfer;
308+
int multi_lane_mode = SPI_ENGINE_MULTI_BUS_MODE_UNKNOWN;
272309
u8 min_bits_per_word = U8_MAX;
273310
u8 max_bits_per_word = 0;
274311

@@ -284,6 +321,24 @@ static int spi_engine_precompile_message(struct spi_message *msg)
284321
min_bits_per_word = min(min_bits_per_word, xfer->bits_per_word);
285322
max_bits_per_word = max(max_bits_per_word, xfer->bits_per_word);
286323
}
324+
325+
if (xfer->rx_buf || xfer->offload_flags & SPI_OFFLOAD_XFER_RX_STREAM ||
326+
xfer->tx_buf || xfer->offload_flags & SPI_OFFLOAD_XFER_TX_STREAM) {
327+
switch (xfer->multi_lane_mode) {
328+
case SPI_MULTI_LANE_MODE_SINGLE:
329+
case SPI_MULTI_LANE_MODE_STRIPE:
330+
break;
331+
default:
332+
/* Other modes, like mirror not supported */
333+
return -EINVAL;
334+
}
335+
336+
/* If all xfers have the same multi-lane mode, we can optimize. */
337+
if (multi_lane_mode == SPI_ENGINE_MULTI_BUS_MODE_UNKNOWN)
338+
multi_lane_mode = xfer->multi_lane_mode;
339+
else if (multi_lane_mode != xfer->multi_lane_mode)
340+
multi_lane_mode = SPI_ENGINE_MULTI_BUS_MODE_CONFLICTING;
341+
}
287342
}
288343

289344
/*
@@ -297,6 +352,14 @@ static int spi_engine_precompile_message(struct spi_message *msg)
297352
priv->bits_per_word = min_bits_per_word;
298353
else
299354
priv->bits_per_word = 0;
355+
356+
priv->multi_lane_mode = multi_lane_mode;
357+
spi_engine_primary_lane_flag(msg->spi,
358+
&priv->rx_primary_lane_mask,
359+
&priv->tx_primary_lane_mask);
360+
spi_engine_all_lanes_flags(msg->spi,
361+
&priv->rx_all_lanes_mask,
362+
&priv->tx_all_lanes_mask);
300363
}
301364

302365
return 0;
@@ -310,6 +373,7 @@ static void spi_engine_compile_message(struct spi_message *msg, bool dry,
310373
struct spi_engine_offload *priv;
311374
struct spi_transfer *xfer;
312375
int clk_div, new_clk_div, inst_ns;
376+
int prev_multi_lane_mode = SPI_MULTI_LANE_MODE_SINGLE;
313377
bool keep_cs = false;
314378
u8 bits_per_word = 0;
315379

@@ -334,6 +398,7 @@ static void spi_engine_compile_message(struct spi_message *msg, bool dry,
334398
* in the same way.
335399
*/
336400
bits_per_word = priv->bits_per_word;
401+
prev_multi_lane_mode = priv->multi_lane_mode;
337402
} else {
338403
spi_engine_program_add_cmd(p, dry,
339404
SPI_ENGINE_CMD_WRITE(SPI_ENGINE_CMD_REG_CONFIG,
@@ -344,6 +409,28 @@ static void spi_engine_compile_message(struct spi_message *msg, bool dry,
344409
spi_engine_gen_cs(p, dry, spi, !xfer->cs_off);
345410

346411
list_for_each_entry(xfer, &msg->transfers, transfer_list) {
412+
if (xfer->rx_buf || xfer->offload_flags & SPI_OFFLOAD_XFER_RX_STREAM ||
413+
xfer->tx_buf || xfer->offload_flags & SPI_OFFLOAD_XFER_TX_STREAM) {
414+
if (xfer->multi_lane_mode != prev_multi_lane_mode) {
415+
u8 tx_lane_flags, rx_lane_flags;
416+
417+
if (xfer->multi_lane_mode == SPI_MULTI_LANE_MODE_STRIPE)
418+
spi_engine_all_lanes_flags(spi, &rx_lane_flags,
419+
&tx_lane_flags);
420+
else
421+
spi_engine_primary_lane_flag(spi, &rx_lane_flags,
422+
&tx_lane_flags);
423+
424+
spi_engine_program_add_cmd(p, dry,
425+
SPI_ENGINE_CMD_WRITE(SPI_ENGINE_CMD_REG_SDI_MASK,
426+
rx_lane_flags));
427+
spi_engine_program_add_cmd(p, dry,
428+
SPI_ENGINE_CMD_WRITE(SPI_ENGINE_CMD_REG_SDO_MASK,
429+
tx_lane_flags));
430+
}
431+
prev_multi_lane_mode = xfer->multi_lane_mode;
432+
}
433+
347434
new_clk_div = host->max_speed_hz / xfer->effective_speed_hz;
348435
if (new_clk_div != clk_div) {
349436
clk_div = new_clk_div;
@@ -360,7 +447,7 @@ static void spi_engine_compile_message(struct spi_message *msg, bool dry,
360447
bits_per_word));
361448
}
362449

363-
spi_engine_gen_xfer(p, dry, xfer);
450+
spi_engine_gen_xfer(p, dry, xfer, spi->num_rx_lanes);
364451
spi_engine_gen_sleep(p, dry, spi_delay_to_ns(&xfer->delay, xfer),
365452
inst_ns, xfer->effective_speed_hz);
366453

@@ -394,6 +481,19 @@ static void spi_engine_compile_message(struct spi_message *msg, bool dry,
394481
if (clk_div != 1)
395482
spi_engine_program_add_cmd(p, dry,
396483
SPI_ENGINE_CMD_WRITE(SPI_ENGINE_CMD_REG_CLK_DIV, 0));
484+
485+
/* Restore single lane mode unless offload disable will restore it later. */
486+
if (prev_multi_lane_mode == SPI_MULTI_LANE_MODE_STRIPE &&
487+
(!msg->offload || priv->multi_lane_mode != SPI_MULTI_LANE_MODE_STRIPE)) {
488+
u8 rx_lane_flags, tx_lane_flags;
489+
490+
spi_engine_primary_lane_flag(spi, &rx_lane_flags, &tx_lane_flags);
491+
492+
spi_engine_program_add_cmd(p, dry,
493+
SPI_ENGINE_CMD_WRITE(SPI_ENGINE_CMD_REG_SDI_MASK, rx_lane_flags));
494+
spi_engine_program_add_cmd(p, dry,
495+
SPI_ENGINE_CMD_WRITE(SPI_ENGINE_CMD_REG_SDO_MASK, tx_lane_flags));
496+
}
397497
}
398498

399499
static void spi_engine_xfer_next(struct spi_message *msg,
@@ -799,6 +899,19 @@ static int spi_engine_setup(struct spi_device *device)
799899
writel_relaxed(SPI_ENGINE_CMD_CS_INV(spi_engine->cs_inv),
800900
spi_engine->base + SPI_ENGINE_REG_CMD_FIFO);
801901

902+
if (host->num_data_lanes > 1) {
903+
u8 rx_lane_flags, tx_lane_flags;
904+
905+
spi_engine_primary_lane_flag(device, &rx_lane_flags, &tx_lane_flags);
906+
907+
writel_relaxed(SPI_ENGINE_CMD_WRITE(SPI_ENGINE_CMD_REG_SDI_MASK,
908+
rx_lane_flags),
909+
spi_engine->base + SPI_ENGINE_REG_CMD_FIFO);
910+
writel_relaxed(SPI_ENGINE_CMD_WRITE(SPI_ENGINE_CMD_REG_SDO_MASK,
911+
tx_lane_flags),
912+
spi_engine->base + SPI_ENGINE_REG_CMD_FIFO);
913+
}
914+
802915
/*
803916
* In addition to setting the flags, we have to do a CS assert command
804917
* to make the new setting actually take effect.
@@ -902,6 +1015,15 @@ static int spi_engine_trigger_enable(struct spi_offload *offload)
9021015
priv->bits_per_word),
9031016
spi_engine->base + SPI_ENGINE_REG_CMD_FIFO);
9041017

1018+
if (priv->multi_lane_mode == SPI_MULTI_LANE_MODE_STRIPE) {
1019+
writel_relaxed(SPI_ENGINE_CMD_WRITE(SPI_ENGINE_CMD_REG_SDI_MASK,
1020+
priv->rx_all_lanes_mask),
1021+
spi_engine->base + SPI_ENGINE_REG_CMD_FIFO);
1022+
writel_relaxed(SPI_ENGINE_CMD_WRITE(SPI_ENGINE_CMD_REG_SDO_MASK,
1023+
priv->tx_all_lanes_mask),
1024+
spi_engine->base + SPI_ENGINE_REG_CMD_FIFO);
1025+
}
1026+
9051027
writel_relaxed(SPI_ENGINE_CMD_SYNC(1),
9061028
spi_engine->base + SPI_ENGINE_REG_CMD_FIFO);
9071029

@@ -929,6 +1051,16 @@ static void spi_engine_trigger_disable(struct spi_offload *offload)
9291051
reg &= ~SPI_ENGINE_OFFLOAD_CTRL_ENABLE;
9301052
writel_relaxed(reg, spi_engine->base +
9311053
SPI_ENGINE_REG_OFFLOAD_CTRL(priv->offload_num));
1054+
1055+
/* Restore single-lane mode. */
1056+
if (priv->multi_lane_mode == SPI_MULTI_LANE_MODE_STRIPE) {
1057+
writel_relaxed(SPI_ENGINE_CMD_WRITE(SPI_ENGINE_CMD_REG_SDI_MASK,
1058+
priv->rx_primary_lane_mask),
1059+
spi_engine->base + SPI_ENGINE_REG_CMD_FIFO);
1060+
writel_relaxed(SPI_ENGINE_CMD_WRITE(SPI_ENGINE_CMD_REG_SDO_MASK,
1061+
priv->tx_primary_lane_mask),
1062+
spi_engine->base + SPI_ENGINE_REG_CMD_FIFO);
1063+
}
9321064
}
9331065

9341066
static struct dma_chan
@@ -973,7 +1105,7 @@ static int spi_engine_probe(struct platform_device *pdev)
9731105
{
9741106
struct spi_engine *spi_engine;
9751107
struct spi_controller *host;
976-
unsigned int version;
1108+
unsigned int version, data_width_reg_val;
9771109
int irq, ret;
9781110

9791111
irq = platform_get_irq(pdev, 0);
@@ -1042,14 +1174,16 @@ static int spi_engine_probe(struct platform_device *pdev)
10421174
return PTR_ERR(spi_engine->base);
10431175

10441176
version = readl(spi_engine->base + ADI_AXI_REG_VERSION);
1045-
if (ADI_AXI_PCORE_VER_MAJOR(version) != 1) {
1177+
if (ADI_AXI_PCORE_VER_MAJOR(version) > 2) {
10461178
dev_err(&pdev->dev, "Unsupported peripheral version %u.%u.%u\n",
10471179
ADI_AXI_PCORE_VER_MAJOR(version),
10481180
ADI_AXI_PCORE_VER_MINOR(version),
10491181
ADI_AXI_PCORE_VER_PATCH(version));
10501182
return -ENODEV;
10511183
}
10521184

1185+
data_width_reg_val = readl(spi_engine->base + SPI_ENGINE_REG_DATA_WIDTH);
1186+
10531187
if (adi_axi_pcore_ver_gteq(version, 1, 1)) {
10541188
unsigned int sizes = readl(spi_engine->base +
10551189
SPI_ENGINE_REG_OFFLOAD_MEM_ADDR_WIDTH);
@@ -1097,6 +1231,9 @@ static int spi_engine_probe(struct platform_device *pdev)
10971231
}
10981232
if (adi_axi_pcore_ver_gteq(version, 1, 3))
10991233
host->mode_bits |= SPI_MOSI_IDLE_LOW | SPI_MOSI_IDLE_HIGH;
1234+
if (adi_axi_pcore_ver_gteq(version, 2, 0))
1235+
host->num_data_lanes = FIELD_GET(SPI_ENGINE_REG_DATA_WIDTH_NUM_OF_SDIO_MASK,
1236+
data_width_reg_val);
11001237

11011238
if (host->max_speed_hz == 0)
11021239
return dev_err_probe(&pdev->dev, -EINVAL, "spi_clk rate is 0");

0 commit comments

Comments
 (0)