diff -ruN a/drivers/dma/qcom/gpi.c b/drivers/dma/qcom/gpi.c --- a/drivers/dma/qcom/gpi.c +++ b/drivers/dma/qcom/gpi.c @@ -48,6 +48,12 @@ #define TRE_SPI_GO_CS GENMASK(10, 8) #define TRE_SPI_GO_FRAG BIT(26) +/* QSPI GO WD0 - flags field is 12 bits at [31:20] instead of SPI's 8 bits */ +#define TRE_QSPI_GO_FLAGS GENMASK(31, 20) + +/* QSPI Config0 WD0 - dummy clock count */ +#define TRE_SPI_C0_DUMMY_CLK GENMASK(21, 14) + /* GO WD2 */ #define TRE_RX_LEN GENMASK(23, 0) @@ -1275,8 +1281,17 @@ upper_32_bits(ring->phys_addr)); gpi_write_reg(gpii, chan->ch_cntxt_db_reg + CNTXT_5_RING_RP_MSB - CNTXT_4_RING_RP_LSB, upper_32_bits(ring->phys_addr)); - gpi_write_reg(gpii, gpii->regs + GPII_n_CH_k_SCRATCH_0_OFFS(id, chid), - GPII_n_CH_k_SCRATCH_0(pair_chid, chan->protocol, chan->seid)); + /* + * For QSPI, use the SE's native protocol value (9) in SCRATCH_0. + * The DT binding uses QCOM_GPI_QSPI=4 for channel selection, but + * the GSI firmware expects the actual SE protocol ID. + */ + { + u32 hw_proto = chan->protocol == QCOM_GPI_QSPI ? + 9 : chan->protocol; + gpi_write_reg(gpii, gpii->regs + GPII_n_CH_k_SCRATCH_0_OFFS(id, chid), + GPII_n_CH_k_SCRATCH_0(pair_chid, hw_proto, chan->seid)); + } gpi_write_reg(gpii, gpii->regs + GPII_n_CH_k_SCRATCH_1_OFFS(id, chid), 0); gpi_write_reg(gpii, gpii->regs + GPII_n_CH_k_SCRATCH_2_OFFS(id, chid), 0); gpi_write_reg(gpii, gpii->regs + GPII_n_CH_k_SCRATCH_3_OFFS(id, chid), 0); @@ -1699,6 +1714,116 @@ return tre_idx; } +static void qspi_add_config_tre(struct gpi_spi_config *spi, + struct gpi_tre *tre) +{ + tre->dword[0] = u32_encode_bits(spi->word_len, TRE_SPI_C0_WORD_SZ); + 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->pack_en, TRE_SPI_C0_TX_PACK); + tre->dword[0] |= u32_encode_bits(spi->pack_en, TRE_SPI_C0_RX_PACK); + tre->dword[0] |= u32_encode_bits(spi->dummy_clk_cnt, TRE_SPI_C0_DUMMY_CLK); + + tre->dword[1] = 0; + + tre->dword[2] = u32_encode_bits(spi->clk_div, TRE_C0_CLK_DIV); + tre->dword[2] |= u32_encode_bits(spi->clk_src, TRE_C0_CLK_SRC); + + tre->dword[3] = u32_encode_bits(TRE_TYPE_CONFIG0, TRE_FLAGS_TYPE); + tre->dword[3] |= u32_encode_bits(1, TRE_FLAGS_CHAIN); +} + +static int gpi_create_qspi_tre(struct gchan *chan, struct gpi_desc *desc, + struct scatterlist *sgl, + enum dma_transfer_direction direction) +{ + struct gpi_spi_config *spi = chan->config; + struct device *dev = chan->gpii->gpi_dev->dev; + unsigned int tre_idx = 0; + struct gpi_tre *tre; + u32 qspi_flags; + dma_addr_t address; + int len; + + /* Config TRE (TX channel only) */ + if (direction == DMA_MEM_TO_DEV && spi->set_config) { + qspi_add_config_tre(spi, &desc->tre[tre_idx]); + tre_idx++; + } + + /* Go TRE (TX channel only) */ + if (direction == DMA_MEM_TO_DEV) { + tre = &desc->tre[tre_idx]; + tre_idx++; + + qspi_flags = spi->qspi_lane_flags; + if (spi->fragmentation) + qspi_flags |= BIT(6); + + tre->dword[0] = u32_encode_bits(qspi_flags, TRE_QSPI_GO_FLAGS); + tre->dword[0] |= u32_encode_bits(spi->cs, TRE_SPI_GO_CS); + tre->dword[0] |= u32_encode_bits(spi->cmd, TRE_SPI_GO_CMD); + + tre->dword[1] = 0; + tre->dword[2] = u32_encode_bits(spi->rx_len, TRE_RX_LEN); + + tre->dword[3] = u32_encode_bits(TRE_TYPE_GO, TRE_FLAGS_TYPE); + if (spi->cmd == SPI_RX) { + tre->dword[3] |= u32_encode_bits(1, TRE_FLAGS_IEOB); + 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_TX_RX or SPI_DUPLEX */ + tre->dword[3] |= u32_encode_bits(1, TRE_FLAGS_CHAIN); + if (spi->rx_len > 0) + tre->dword[3] |= u32_encode_bits(1, TRE_FLAGS_LINK); + } + } + + /* DMA TRE - skip for SPI_RX on TX channel */ + if (direction == DMA_MEM_TO_DEV && spi->cmd == SPI_RX) + goto skip_dma_tre; + + tre = &desc->tre[tre_idx]; + tre_idx++; + + address = sg_dma_address(sgl); + len = sg_dma_len(sgl); + + /* + * For QSPI TX_RX, limit TX DMA to command bytes only. + * The SE parses the TX data as opcode+address and uses rx_len + * from the Go TRE for the receive phase. Sending more TX bytes + * than the command length confuses the SE's QSPI state machine. + */ + if (direction == DMA_MEM_TO_DEV && spi->cmd == SPI_TX_RX && + spi->tx_cmd_len > 0 && spi->tx_cmd_len < len) + len = spi->tx_cmd_len; + + if (direction == DMA_MEM_TO_DEV && len <= 2 * sizeof(tre->dword[0])) { + tre->dword[0] = 0; + tre->dword[1] = 0; + memcpy(&tre->dword[0], sg_virt(sgl), len); + tre->dword[2] = u32_encode_bits(len, TRE_DMA_IMMEDIATE_LEN); + tre->dword[3] = u32_encode_bits(TRE_TYPE_IMMEDIATE_DMA, TRE_FLAGS_TYPE); + } else { + tre->dword[0] = lower_32_bits(address); + tre->dword[1] = upper_32_bits(address); + tre->dword[2] = u32_encode_bits(len, TRE_DMA_LEN); + tre->dword[3] = u32_encode_bits(TRE_TYPE_DMA, TRE_FLAGS_TYPE); + } + tre->dword[3] |= u32_encode_bits(1, TRE_FLAGS_IEOT); + +skip_dma_tre: + for (len = 0; len < tre_idx; len++) + dev_dbg(dev, "QSPI TRE:%d %x:%x:%x:%x\n", len, + desc->tre[len].dword[0], desc->tre[len].dword[1], + desc->tre[len].dword[2], desc->tre[len].dword[3]); + + return tre_idx; +} + static int gpi_create_spi_tre(struct gchan *chan, struct gpi_desc *desc, struct scatterlist *sgl, enum dma_transfer_direction direction) { @@ -1841,7 +1966,9 @@ return NULL; /* create TREs for xfer */ - if (gchan->protocol == QCOM_GPI_SPI) { + if (gchan->protocol == QCOM_GPI_QSPI) { + i = gpi_create_qspi_tre(gchan, gpi_desc, sgl, direction); + } else if (gchan->protocol == QCOM_GPI_SPI) { 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 -ruN a/include/dt-bindings/dma/qcom-gpi.h b/include/dt-bindings/dma/qcom-gpi.h --- a/include/dt-bindings/dma/qcom-gpi.h +++ b/include/dt-bindings/dma/qcom-gpi.h @@ -7,5 +7,6 @@ #define QCOM_GPI_SPI 1 #define QCOM_GPI_UART 2 #define QCOM_GPI_I2C 3 +#define QCOM_GPI_QSPI 4 #endif /* __DT_BINDINGS_DMA_QCOM_GPI_H__ */ diff -ruN a/include/linux/dma/qcom-gpi-dma.h b/include/linux/dma/qcom-gpi-dma.h --- a/include/linux/dma/qcom-gpi-dma.h +++ b/include/linux/dma/qcom-gpi-dma.h @@ -13,6 +13,7 @@ SPI_TX = 1, SPI_RX, SPI_DUPLEX, + SPI_TX_RX = 7, /* QSPI full-duplex (TX opcode+addr, RX data) */ }; /** @@ -44,6 +45,11 @@ u32 clk_src; enum spi_transfer_cmd cmd; u32 rx_len; + /* QSPI extensions */ + bool qspi_mode; + u16 qspi_lane_flags; + u8 dummy_clk_cnt; + u16 tx_cmd_len; }; enum i2c_op {