[PATCH 5/6] dmaengine: qcom: gpi: Add QSPI protocol support
From: Jyothi Kumar Seerapu
Date: Fri Nov 06 2026 - 10:20:03 EST
QSPI reuses the SPI TRE format but requires protocol-specific handling
for GO TRE fragmentation, transfer mode selection and dummy cycles.
Add QCOM_GPI_QSPI support to the SPI TRE generation path and extend
gpi_spi_config with QSPI transfer mode and dummy-cycle parameters.
Encode dummy cycles in CONFIG0 and select the appropriate GO TRE
format based on the channel protocol, enabling single, dual and quad
SDR transfers while preserving the existing SPI encoding.
Also add SPI_TX_RX to the GPI DMA interface to represent sequential
TX-then-RX transactions used for QSPI read operations.
Enable RX end-of-transfer events for QSPI TX-then-RX commands so
clients can wait for both transmit and receive DMA completion.
Signed-off-by: Jyothi Kumar Seerapu <jyothi.seerapu@xxxxxxxxxxxxxxxx>
---
drivers/dma/qcom/gpi.c | 22 ++++++++++++++++++----
drivers/spi/spi-geni-qcom.c | 1 -
include/linux/dma/qcom-gpi-dma.h | 6 ++++++
3 files changed, 24 insertions(+), 5 deletions(-)
diff --git a/drivers/dma/qcom/gpi.c b/drivers/dma/qcom/gpi.c
index a5055a6273af..487e023287aa 100644
--- a/drivers/dma/qcom/gpi.c
+++ b/drivers/dma/qcom/gpi.c
@@ -36,6 +36,7 @@
#define TRE_SPI_C0_CS BIT(11)
#define TRE_SPI_C0_CPHA BIT(12)
#define TRE_SPI_C0_CPOL BIT(13)
+#define TRE_SPI_C0_DUMMY_CYCLES GENMASK(21, 14)
#define TRE_SPI_C0_TX_PACK BIT(24)
#define TRE_SPI_C0_RX_PACK BIT(25)
@@ -48,6 +49,10 @@
#define TRE_SPI_GO_CS GENMASK(10, 8)
#define TRE_SPI_GO_FRAG BIT(26)
+/* QSPI GO WD0 */
+#define TRE_QSPI_GO_FRAG BIT(22)
+#define TRE_QSPI_GO_MODE GENMASK(31, 28)
+
/* GO WD2 */
#define TRE_RX_LEN GENMASK(23, 0)
@@ -1719,6 +1724,7 @@ static int gpi_create_spi_tre(struct gchan *chan, struct gpi_desc *desc,
tre->dword[0] |= u32_encode_bits(spi->loopback_en, TRE_SPI_C0_LOOPBACK);
tre->dword[0] |= u32_encode_bits(spi->clock_pol_high, TRE_SPI_C0_CPOL);
tre->dword[0] |= u32_encode_bits(spi->data_pol_high, TRE_SPI_C0_CPHA);
+ tre->dword[0] |= u32_encode_bits(spi->dummy_cycles, TRE_SPI_C0_DUMMY_CYCLES);
tre->dword[0] |= u32_encode_bits(spi->pack_en, TRE_SPI_C0_TX_PACK);
tre->dword[0] |= u32_encode_bits(spi->pack_en, TRE_SPI_C0_RX_PACK);
@@ -1736,7 +1742,12 @@ static int gpi_create_spi_tre(struct gchan *chan, struct gpi_desc *desc,
tre = &desc->tre[tre_idx];
tre_idx++;
- tre->dword[0] = u32_encode_bits(spi->fragmentation, TRE_SPI_GO_FRAG);
+ if (chan->protocol == QCOM_GPI_QSPI) {
+ tre->dword[0] = u32_encode_bits(spi->fragmentation, TRE_QSPI_GO_FRAG);
+ tre->dword[0] |= u32_encode_bits(spi->qspi_mode, TRE_QSPI_GO_MODE);
+ } else {
+ tre->dword[0] = u32_encode_bits(spi->fragmentation, TRE_SPI_GO_FRAG);
+ }
tre->dword[0] |= u32_encode_bits(spi->cs, TRE_SPI_GO_CS);
tre->dword[0] |= u32_encode_bits(spi->cmd, TRE_SPI_GO_CMD);
@@ -1750,7 +1761,7 @@ static int gpi_create_spi_tre(struct gchan *chan, struct gpi_desc *desc,
tre->dword[3] |= u32_encode_bits(1, TRE_FLAGS_LINK);
} else if (spi->cmd == SPI_TX) {
tre->dword[3] |= u32_encode_bits(1, TRE_FLAGS_CHAIN);
- } else { /* SPI_DUPLEX */
+ } else { /* SPI_DUPLEX or SPI_TX_RX */
tre->dword[3] |= u32_encode_bits(1, TRE_FLAGS_CHAIN);
tre->dword[3] |= u32_encode_bits(1, TRE_FLAGS_LINK);
}
@@ -1783,7 +1794,10 @@ static int gpi_create_spi_tre(struct gchan *chan, struct gpi_desc *desc,
tre->dword[3] = u32_encode_bits(TRE_TYPE_DMA, TRE_FLAGS_TYPE);
}
- tre->dword[3] |= u32_encode_bits(direction == DMA_MEM_TO_DEV,
+ /* QSPI TX-then-RX commands need a separate RX completion event. */
+ tre->dword[3] |= u32_encode_bits(direction == DMA_MEM_TO_DEV ||
+ (chan->protocol == QCOM_GPI_QSPI &&
+ spi->cmd == SPI_TX_RX),
TRE_FLAGS_IEOT);
for (i = 0; i < tre_idx; i++)
@@ -1841,7 +1855,7 @@ gpi_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
return NULL;
/* create TREs for xfer */
- if (gchan->protocol == QCOM_GPI_SPI) {
+ if (gchan->protocol == QCOM_GPI_SPI || gchan->protocol == QCOM_GPI_QSPI) {
i = gpi_create_spi_tre(gchan, gpi_desc, sgl, direction);
} else if (gchan->protocol == QCOM_GPI_I2C) {
i = gpi_create_i2c_tre(gchan, gpi_desc, sgl, direction, flags);
diff --git a/drivers/spi/spi-geni-qcom.c b/drivers/spi/spi-geni-qcom.c
index 25c03096ee23..c2c3400b24c4 100644
--- a/drivers/spi/spi-geni-qcom.c
+++ b/drivers/spi/spi-geni-qcom.c
@@ -66,7 +66,6 @@
/* M_CMD OP codes for SPI */
#define SPI_TX_ONLY 1
#define SPI_RX_ONLY 2
-#define SPI_TX_RX 7
#define SPI_CS_ASSERT 8
#define SPI_CS_DEASSERT 9
#define SPI_SCK_ONLY 10
diff --git a/include/linux/dma/qcom-gpi-dma.h b/include/linux/dma/qcom-gpi-dma.h
index 332be28427e4..d2d659a075f9 100644
--- a/include/linux/dma/qcom-gpi-dma.h
+++ b/include/linux/dma/qcom-gpi-dma.h
@@ -11,11 +11,13 @@
* @SPI_TX: SPI peripheral TX command
* @SPI_RX: SPI peripheral RX command
* @SPI_DUPLEX: SPI peripheral Duplex command
+ * @SPI_TX_RX: SPI peripheral Tx-then-Rx command (QSPI read transactions)
*/
enum spi_transfer_cmd {
SPI_TX = 1,
SPI_RX,
SPI_DUPLEX,
+ SPI_TX_RX = 7,
};
/**
@@ -33,6 +35,8 @@ enum spi_transfer_cmd {
* @cs: chip select toggle
* @set_config: set peripheral config
* @rx_len: receive length for buffer
+ * @dummy_cycles: number of dummy clock cycles for a QSPI transfer
+ * @qspi_mode: QSPI bus-width/SDR-DDR mode for the GO TRE
*/
struct gpi_spi_config {
u8 set_config;
@@ -43,10 +47,12 @@ struct gpi_spi_config {
u8 word_len;
u8 fragmentation;
u8 cs;
+ u8 dummy_cycles;
u32 clk_div;
u32 clk_src;
enum spi_transfer_cmd cmd;
u32 rx_len;
+ u16 qspi_mode;
};
enum i2c_op {
--
2.34.1