Skip to content

Commit 7861156

Browse files
committed
spi: add support for pre-cooking messages
Merge series from David Lechner <[email protected]>: This is a follow-up to [1] where it was suggested to break down the proposed SPI offload support into smaller series. This takes on the first suggested task of introducing an API to "pre-cook" SPI messages. This idea was first discussed extensively in 2013 [2][3] and revisited more briefly 2022 [4]. The goal here is to be able to improve performance (higher throughput, and reduced CPU usage) by allowing peripheral drivers that use the same struct spi_message repeatedly to "pre-cook" the message once to avoid repeating the same validation, and possibly other operations each time the message is sent. This series includes __spi_validate() and the automatic splitting of xfers in the optimizations. Another frequently suggested optimization is doing DMA mapping only once. This is not included in this series, but can be added later (preferably by someone with a real use case for it). To show how this all works and get some real-world measurements, this series includes the core changes, optimization of a SPI controller driver, and optimization of an ADC driver. This test case was only able to take advantage of the single validation optimization, since it didn't require splitting transfers. With these changes, CPU usage of the threaded interrupt handler, which calls spi_sync(), was reduced from 83% to 73% while at the same time the sample rate (frequency of SPI xfers) was increased from 20kHz to 25kHz. [1]: https://lore.kernel.org/linux-spi/[email protected]/T/ [2]: https://lore.kernel.org/linux-spi/[email protected]/T/ [3]: https://lore.kernel.org/linux-spi/[email protected]/T/ [4]: https://lore.kernel.org/linux-spi/20220525163946.48ea40c9@erd992/T/
2 parents e63aef9 + 7dba2ad commit 7861156

File tree

4 files changed

+267
-80
lines changed

4 files changed

+267
-80
lines changed

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

Lines changed: 17 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ struct spi_engine {
109109
spinlock_t lock;
110110

111111
void __iomem *base;
112+
struct spi_engine_message_state msg_state;
112113
struct completion msg_complete;
113114
unsigned int int_enable;
114115
};
@@ -499,49 +500,34 @@ static irqreturn_t spi_engine_irq(int irq, void *devid)
499500
return IRQ_HANDLED;
500501
}
501502

502-
static int spi_engine_prepare_message(struct spi_controller *host,
503-
struct spi_message *msg)
503+
static int spi_engine_optimize_message(struct spi_message *msg)
504504
{
505505
struct spi_engine_program p_dry, *p;
506-
struct spi_engine_message_state *st;
507506
size_t size;
508507

509-
st = kzalloc(sizeof(*st), GFP_KERNEL);
510-
if (!st)
511-
return -ENOMEM;
512-
513508
spi_engine_precompile_message(msg);
514509

515510
p_dry.length = 0;
516511
spi_engine_compile_message(msg, true, &p_dry);
517512

518513
size = sizeof(*p->instructions) * (p_dry.length + 1);
519514
p = kzalloc(sizeof(*p) + size, GFP_KERNEL);
520-
if (!p) {
521-
kfree(st);
515+
if (!p)
522516
return -ENOMEM;
523-
}
524517

525518
spi_engine_compile_message(msg, false, p);
526519

527520
spi_engine_program_add_cmd(p, false, SPI_ENGINE_CMD_SYNC(
528521
AXI_SPI_ENGINE_CUR_MSG_SYNC_ID));
529522

530-
st->p = p;
531-
st->cmd_buf = p->instructions;
532-
st->cmd_length = p->length;
533-
msg->state = st;
523+
msg->opt_state = p;
534524

535525
return 0;
536526
}
537527

538-
static int spi_engine_unprepare_message(struct spi_controller *host,
539-
struct spi_message *msg)
528+
static int spi_engine_unoptimize_message(struct spi_message *msg)
540529
{
541-
struct spi_engine_message_state *st = msg->state;
542-
543-
kfree(st->p);
544-
kfree(st);
530+
kfree(msg->opt_state);
545531

546532
return 0;
547533
}
@@ -550,10 +536,18 @@ static int spi_engine_transfer_one_message(struct spi_controller *host,
550536
struct spi_message *msg)
551537
{
552538
struct spi_engine *spi_engine = spi_controller_get_devdata(host);
553-
struct spi_engine_message_state *st = msg->state;
539+
struct spi_engine_message_state *st = &spi_engine->msg_state;
540+
struct spi_engine_program *p = msg->opt_state;
554541
unsigned int int_enable = 0;
555542
unsigned long flags;
556543

544+
/* reinitialize message state for this transfer */
545+
memset(st, 0, sizeof(*st));
546+
st->p = p;
547+
st->cmd_buf = p->instructions;
548+
st->cmd_length = p->length;
549+
msg->state = st;
550+
557551
reinit_completion(&spi_engine->msg_complete);
558552

559553
spin_lock_irqsave(&spi_engine->lock, flags);
@@ -658,8 +652,8 @@ static int spi_engine_probe(struct platform_device *pdev)
658652
host->bits_per_word_mask = SPI_BPW_RANGE_MASK(1, 32);
659653
host->max_speed_hz = clk_get_rate(spi_engine->ref_clk) / 2;
660654
host->transfer_one_message = spi_engine_transfer_one_message;
661-
host->prepare_message = spi_engine_prepare_message;
662-
host->unprepare_message = spi_engine_unprepare_message;
655+
host->optimize_message = spi_engine_optimize_message;
656+
host->unoptimize_message = spi_engine_unoptimize_message;
663657
host->num_chipselect = 8;
664658

665659
if (host->max_speed_hz == 0)

drivers/spi/spi-stm32.c

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1118,6 +1118,21 @@ static irqreturn_t stm32h7_spi_irq_thread(int irq, void *dev_id)
11181118
return IRQ_HANDLED;
11191119
}
11201120

1121+
static int stm32_spi_optimize_message(struct spi_message *msg)
1122+
{
1123+
struct spi_controller *ctrl = msg->spi->controller;
1124+
struct stm32_spi *spi = spi_controller_get_devdata(ctrl);
1125+
1126+
/* On STM32H7, messages should not exceed a maximum size set
1127+
* later via the set_number_of_data function. In order to
1128+
* ensure that, split large messages into several messages
1129+
*/
1130+
if (spi->cfg->set_number_of_data)
1131+
return spi_split_transfers_maxwords(ctrl, msg, spi->t_size_max);
1132+
1133+
return 0;
1134+
}
1135+
11211136
/**
11221137
* stm32_spi_prepare_msg - set up the controller to transfer a single message
11231138
* @ctrl: controller interface
@@ -1163,18 +1178,6 @@ static int stm32_spi_prepare_msg(struct spi_controller *ctrl,
11631178
!!(spi_dev->mode & SPI_LSB_FIRST),
11641179
!!(spi_dev->mode & SPI_CS_HIGH));
11651180

1166-
/* On STM32H7, messages should not exceed a maximum size setted
1167-
* afterward via the set_number_of_data function. In order to
1168-
* ensure that, split large messages into several messages
1169-
*/
1170-
if (spi->cfg->set_number_of_data) {
1171-
int ret;
1172-
1173-
ret = spi_split_transfers_maxwords(ctrl, msg, spi->t_size_max);
1174-
if (ret)
1175-
return ret;
1176-
}
1177-
11781181
spin_lock_irqsave(&spi->lock, flags);
11791182

11801183
/* CPOL, CPHA and LSB FIRST bits have common register */
@@ -2180,6 +2183,7 @@ static int stm32_spi_probe(struct platform_device *pdev)
21802183
ctrl->max_speed_hz = spi->clk_rate / spi->cfg->baud_rate_div_min;
21812184
ctrl->min_speed_hz = spi->clk_rate / spi->cfg->baud_rate_div_max;
21822185
ctrl->use_gpio_descriptors = true;
2186+
ctrl->optimize_message = stm32_spi_optimize_message;
21832187
ctrl->prepare_message = stm32_spi_prepare_msg;
21842188
ctrl->transfer_one = stm32_spi_transfer_one;
21852189
ctrl->unprepare_message = stm32_spi_unprepare_msg;

0 commit comments

Comments
 (0)