From: sashiko-bot@kernel.org
To: "Jyothi Kumar Seerapu" <jyothi.seerapu@oss.qualcomm.com>
Cc: dmaengine@vger.kernel.org, devicetree@vger.kernel.org,
robh@kernel.org, conor+dt@kernel.org, vkoul@kernel.org,
Frank.Li@kernel.org
Subject: Re: [PATCH 6/6] spi: spi-geni-qcom: Add QSPI support over GPI DMA
Date: Fri, 06 Nov 2026 15:32:33 +0000 [thread overview]
Message-ID: <sashiko-outbox-201699@kernel.org> (raw)
In-Reply-To: <20261106-qspi_upstream_series-v1-6-21f43042dd60@oss.qualcomm.com>
Thank you for your contribution! Sashiko AI review found 4 potential
issues to consider:
- [High] spi: spi-geni-qcom: dummy_data len treated as cycles in
setup_gsi_xfer()
- [Medium] spi: spi-geni-qcom: uncombined dummy_data sent as TX in
setup_gsi_xfer()
- [Medium] spi: spi-geni-qcom: unreset started flag in
spi_geni_transfer_one_message()
- [Medium] spi: geni-qcom: unvalidated tx_nbits and phase split in
setup_gsi_xfer()
--
commit d6c09f855544d5373d7663c53224613976d60d6e
Author: Jyothi Kumar Seerapu <jyothi.seerapu@oss.qualcomm.com>
spi: spi-geni-qcom: Add QSPI support over GPI DMA
This commit adds QSPI controller support over GPI DMA for the
qcom,geni-qspi compatible, combining TX, optional dummy, and RX transfers
into a single SPI_TX_RX command.
> diff --git a/drivers/spi/spi-geni-qcom.c b/drivers/spi/spi-geni-qcom.c
> index c2c3400b24c41..26dad85a590e3 100644
> --- a/drivers/spi/spi-geni-qcom.c
> +++ b/drivers/spi/spi-geni-qcom.c
[ ... ]
> @@ -412,27 +421,50 @@ spi_gsi_callback_result(void *cb, const struct dmaengine_result *result)
[ ... ]
> static int setup_gsi_xfer(struct spi_transfer *xfer, struct spi_geni_master *mas,
> - struct spi_device *spi_slv, struct spi_controller *spi)
> + struct spi_device *spi_slv, struct spi_controller *spi,
> + struct spi_transfer *tx_rx_xfer)
> {
> unsigned long flags = DMA_PREP_INTERRUPT | DMA_CTRL_ACK;
> struct dma_slave_config config = {};
> struct gpi_spi_config peripheral = {};
> - struct dma_async_tx_descriptor *tx_desc, *rx_desc;
> + struct dma_async_tx_descriptor *tx_desc, *rx_desc = NULL;
> + struct spi_transfer *last = tx_rx_xfer ?: xfer;
> int ret;
>
> + /* QSPI receives data only as part of a combined TX-then-RX command. */
> + if (mas->dev_data->is_qspi && xfer->rx_buf && !tx_rx_xfer)
> + return -EOPNOTSUPP;
> +
> config.peripheral_config = &peripheral;
> config.peripheral_size = sizeof(peripheral);
> peripheral.set_config = true;
> @@ -443,20 +475,21 @@ static int setup_gsi_xfer(struct spi_transfer *xfer, struct spi_geni_master *mas
> mas->cur_speed_hz = xfer->speed_hz;
> }
>
> - if (xfer->tx_buf && xfer->rx_buf) {
> + if (mas->dev_data->is_qspi && tx_rx_xfer) {
> + /* Combine the command and data phases into one hardware command. */
> + peripheral.cmd = SPI_TX_RX;
> + peripheral.rx_len = get_xfer_len_in_words(tx_rx_xfer, mas);
> + } else if (xfer->tx_buf && xfer->rx_buf) {
> peripheral.cmd = SPI_DUPLEX;
> } else if (xfer->tx_buf) {
> peripheral.cmd = SPI_TX;
> peripheral.rx_len = 0;
> } else if (xfer->rx_buf) {
> peripheral.cmd = SPI_RX;
> - if (!(mas->cur_bits_per_word % MIN_WORD_LEN)) {
> - peripheral.rx_len = ((xfer->len << 3) / mas->cur_bits_per_word);
> - } else {
> - int bytes_per_word = (mas->cur_bits_per_word / BITS_PER_BYTE) + 1;
> -
> - peripheral.rx_len = (xfer->len / bytes_per_word);
> - }
> + peripheral.rx_len = get_xfer_len_in_words(xfer, mas);
> + } else {
> + dev_err(mas->dev, "Invalid transfer: no tx_buf or rx_buf\n");
> + return -EINVAL;
> }
[Severity: Medium]
Can an uncombined dummy_data transfer or a bufferless transfer be sent on
the bus as an active SPI_TX DMA transfer here?
In GENI_GPI_DMA mode, spi->flags has SPI_CONTROLLER_MUST_TX set, so
spi_map_msg() sets xfer->tx_buf = spi->dummy_tx on every non-empty
transfer without a tx_buf (and callers such as spi_mem_exec_op() also
populate tx_buf on dummy_data transfers). If a dummy_data transfer is not
merged into a [TX, dummy, RX] sequence in spi_geni_transfer_one_message(),
or if a transfer has no caller-supplied tx_buf or rx_buf, xfer->tx_buf is
still non-NULL. It bypasses the bufferless check in
spi_geni_transfer_one_message() and takes the SPI_TX branch in
setup_gsi_xfer() with dummy_cycles = 0, driving dummy_tx bytes onto the
data lines instead of tri-stating or returning an error. Because
xfer->tx_buf is always populated in GPI DMA mode, the SPI_RX and -EINVAL
branches here are also unreachable.
> @@ -466,6 +499,17 @@ static int setup_gsi_xfer(struct spi_transfer *xfer, struct spi_geni_master *mas
> peripheral.pack_en = true;
> peripheral.word_len = xfer->bits_per_word - MIN_WORD_LEN;
>
> + /* TRE_SPI_C0_DUMMY_CYCLES in the CONFIG0 TRE is an 8-bit field */
> + if (mas->dummy_len > U8_MAX) {
> + dev_err(mas->dev, "Dummy cycles %u exceed hardware limit %u\n",
> + mas->dummy_len, (unsigned int)U8_MAX);
> + return -EINVAL;
> + }
> + peripheral.dummy_cycles = mas->dummy_len;
> +
> + if (mas->dev_data->is_qspi)
> + peripheral.qspi_mode = spi_geni_qspi_mode(last);
[ ... ]
> @@ -1058,6 +1153,54 @@ static int spi_geni_transfer_one_message(struct spi_controller *spi,
> spi_geni_set_cs(spi_slv, !xfer->cs_off);
>
> list_for_each_entry(xfer, &msg->transfers, transfer_list) {
> + mas->dummy_len = 0;
> + last_xfer = xfer;
> +
> + /*
> + * Combine a TX phase, optional dummy phase and RX phase into
> + * one QSPI command.
> + */
> + if (mas->dev_data->is_qspi && mas->cur_xfer_mode == GENI_GPI_DMA &&
> + xfer->len && xfer->tx_buf && xfer->tx_buf != spi->dummy_tx &&
> + !xfer->rx_buf && !xfer->dummy_data &&
> + !list_is_last(&xfer->transfer_list, &msg->transfers)) {
> + next_xfer = list_next_entry(xfer, transfer_list);
> +
> + if (next_xfer->dummy_data &&
> + !list_is_last(&next_xfer->transfer_list, &msg->transfers)) {
> + /* The current client supplies the dummy length in cycles. */
> + mas->dummy_len = next_xfer->len;
> + next_xfer = list_next_entry(next_xfer, transfer_list);
> + }
[Severity: High]
Does assigning next_xfer->len directly to mas->dummy_len program the wrong
number of dummy clock cycles for standard SPI clients?
In struct spi_transfer, len is always in bytes: the SPI core validates
word alignment, allocates dummy_tx, DMA-maps the buffer, and accounts
msg->actual_length (below in this function) in bytes, and callers such as
spi_mem_exec_op() set len = op->dummy.nbytes and tx_nbits =
op->dummy.buswidth. Passing next_xfer->len directly into
peripheral.dummy_cycles without converting bytes to cycles via
(next_xfer->len * BITS_PER_BYTE) / next_xfer->tx_nbits programs too few
dummy cycles (for example, 8x too few in 1-bit mode), corrupting QSPI read
data.
> +
> + /* Ignore the TX buffer inserted by SPI_CONTROLLER_MUST_TX. */
> + if (next_xfer->len && next_xfer->rx_buf && !next_xfer->dummy_data &&
> + (!next_xfer->tx_buf || next_xfer->tx_buf == spi->dummy_tx)) {
> + last_xfer = next_xfer;
> + } else {
> + mas->dummy_len = 0;
> + }
> + }
> +
> + if (last_xfer != xfer) {
> + next_xfer = xfer;
> + for (;;) {
> + /* A combined command cannot change timing or CS between phases. */
> + if (next_xfer->speed_hz != xfer->speed_hz ||
> + next_xfer->bits_per_word != xfer->bits_per_word ||
> + next_xfer->cs_off || next_xfer->dtr_mode ||
> + next_xfer->word_delay.value ||
> + (next_xfer != last_xfer &&
> + (next_xfer->cs_change || next_xfer->delay.value))) {
> + ret = -EOPNOTSUPP;
> + goto out_err;
> + }
> + if (next_xfer == last_xfer)
> + break;
> + next_xfer = list_next_entry(next_xfer, transfer_list);
> + }
> + }
[Severity: Medium]
Does combining transfers into SPI_TX_RX drop the bus width of the TX and
dummy phases and fail to combine split command/address transfers?
In setup_gsi_xfer(), peripheral.qspi_mode is derived only from
spi_geni_qspi_mode(last), ignoring xfer->tx_nbits on the TX transfer and
next_xfer->tx_nbits on the dummy transfer, and this validation loop does
not check whether the phase bus widths are compatible with the single GO
TRE mode. In addition, because spi_geni_probe() sets SPI_TX_DUAL/QUAD and
SPI_RX_DUAL/QUAD without implementing ctlr->mem_ops, spi_mem_exec_op()
splits reads with an address into four transfers ([CMD TX, ADDR TX, DUMMY,
DATA RX]); the combiner above only merges a single TX transfer before
DUMMY and DATA RX, sending CMD TX as a separate SPI_TX command.
> +
> if ((xfer->tx_buf || xfer->rx_buf) && xfer->len) {
> reinit_completion(&mas->xfer_done);
>
> @@ -1066,16 +1209,17 @@ static int spi_geni_transfer_one_message(struct spi_controller *spi,
> ((xfer->tx_sg.nents || xfer->rx_sg.nents) &&
> xfer->tx_sg.nents <= 1 && xfer->rx_sg.nents <= 1);
> if (dma_synced)
> - spi_geni_dma_sync(spi, xfer, true);
> + spi_geni_dma_sync(spi, xfer, last_xfer, true);
>
> - ret = spi_geni_transfer_one(spi, spi_slv, xfer);
> + ret = spi_geni_transfer_one(spi, spi_slv, xfer,
> + last_xfer == xfer ? NULL : last_xfer);
> if (ret < 0) {
> dev_err(mas->dev, "Transfer failed: %d\n", ret);
> goto out_err;
> }
> started = true;
>
> - ret = spi_geni_wait_for_xfer(spi, xfer);
> + ret = spi_geni_wait_for_xfer(spi, xfer, last_xfer);
> if (ret)
> goto out_err;
>
> @@ -1085,7 +1229,7 @@ static int spi_geni_transfer_one_message(struct spi_controller *spi,
> goto out_err;
> }
> if (dma_synced) {
> - spi_geni_dma_sync(spi, xfer, false);
> + spi_geni_dma_sync(spi, xfer, last_xfer, false);
> dma_synced = false;
> }
> } else if (xfer->len) {
[Severity: Medium]
Should started be reset to false after a transfer completes, just like
dma_synced?
If one transfer in a message completes normally and a subsequent transfer
fails validation before hardware submission (such as the combined-phase
check above or the -EOPNOTSUPP / -EINVAL checks in setup_gsi_xfer()),
out_err sees started == true from the earlier transfer and calls
spi_geni_handle_err() on an idle controller, resetting the GPI DMA
channels or issuing cancel/abort commands that time out.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261106-qspi_upstream_series-v1-0-21f43042dd60@oss.qualcomm.com?part=6
next prev parent reply other threads:[~2026-11-06 15:32 UTC|newest]
Thread overview: 21+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-06 15:01 [PATCH 0/6] spi: qcom-geni: Add QSPI support over GPI DMA Jyothi Kumar Seerapu
2026-11-06 15:01 ` [PATCH 1/6] spi: spi-geni-qcom: Add transfer_one_message() support Jyothi Kumar Seerapu
2026-11-06 15:20 ` sashiko-bot
2026-11-06 15:01 ` [PATCH 2/6] dt-bindings: dma: qcom-gpi: Add QSPI protocol identifier Jyothi Kumar Seerapu
2026-11-09 13:42 ` Krzysztof Kozlowski
2026-11-09 14:33 ` Jyothi Kumar Seerapu
2026-11-11 12:37 ` Krzysztof Kozlowski
2026-11-06 15:01 ` [PATCH 3/6] dt-bindings: spi: qcom,spi-geni-qcom: Document QSPI support Jyothi Kumar Seerapu
2026-11-09 13:46 ` Krzysztof Kozlowski
2026-11-06 15:01 ` [PATCH 4/6] soc: qcom: geni-se: Define the QSPI firmware protocol Jyothi Kumar Seerapu
2026-11-09 13:44 ` Krzysztof Kozlowski
2026-11-09 14:43 ` Jyothi Kumar Seerapu
2026-11-11 12:40 ` Krzysztof Kozlowski
2026-11-09 14:47 ` Konrad Dybcio
2026-11-09 16:45 ` Jyothi Kumar Seerapu
2026-11-06 15:01 ` [PATCH 5/6] dmaengine: qcom: gpi: Add QSPI protocol support Jyothi Kumar Seerapu
2026-11-06 15:12 ` sashiko-bot
2026-11-06 15:01 ` [PATCH 6/6] spi: spi-geni-qcom: Add QSPI support over GPI DMA Jyothi Kumar Seerapu
2026-11-06 15:32 ` sashiko-bot [this message]
2026-11-08 15:18 ` [PATCH 0/6] spi: qcom-geni: " Mark Brown
2026-11-09 7:10 ` Jyothi Kumar Seerapu
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=sashiko-outbox-201699@kernel.org \
--to=sashiko-bot@kernel.org \
--cc=Frank.Li@kernel.org \
--cc=conor+dt@kernel.org \
--cc=devicetree@vger.kernel.org \
--cc=dmaengine@vger.kernel.org \
--cc=jyothi.seerapu@oss.qualcomm.com \
--cc=robh@kernel.org \
--cc=sashiko-reviews@lists.linux.dev \
--cc=vkoul@kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox