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: 25+ 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-12 13:47 ` Jyothi Kumar Seerapu
2026-11-12 13:57 ` 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-12 13:48 ` Jyothi Kumar Seerapu
2026-11-12 13:56 ` 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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.