mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH 0/6] spi: qcom-geni: Add QSPI support over GPI DMA
@ 2026-11-06 15:01 Jyothi Kumar Seerapu
  2026-11-06 15:01 ` [PATCH 1/6] spi: spi-geni-qcom: Add transfer_one_message() support Jyothi Kumar Seerapu
                   ` (6 more replies)
  0 siblings, 7 replies; 12+ messages in thread
From: Jyothi Kumar Seerapu @ 2026-11-06 15:01 UTC (permalink / raw)
  To: Mark Brown, Vinod Koul, Frank Li, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Andy Gross, Bjorn Andersson,
	Konrad Dybcio
  Cc: linux-arm-msm, linux-spi, linux-kernel, dmaengine, devicetree,
	Krzysztof Kozlowski, Jyothi Kumar Seerapu

This series adds QSPI support to the Qualcomm GENI SPI driver using
GPI DMA. It supports single, dual and quad SDR transfers in controller
mode.

GENI QSPI reads require a command/address TX phase, an optional dummy
phase and an RX data phase to execute as one hardware command. Add
driver-managed message sequencing to combine these SPI transfers
into a single SPI_TX_RX command.

The series is organized as follows:

1. Add transfer_one_message() support, including completion handling,
   GPIO chip-select sequencing, delays and DMA buffer synchronization.

2. Define QCOM_GPI_QSPI for identifying QSPI channels in GPI DMA
   specifiers.

3. Document the qcom,geni-qspi compatible.

4. Define GENI_SE_QSPI and update the firmware loader to accept the
   QSPI protocol.

5. Add QSPI CONFIG0 and GO TRE formatting to the GPI driver, including
   dummy cycles, transfer mode and the TX-then-RX command.

6. Add QSPI support to the SPI driver, combining read phases and
   accounting for them in message length, DMA synchronization and
   timeout calculations.

SPI and QSPI transfers have been tested using native GENI chip-select.
GPIO chip-select handling has not been tested on hardware.

Signed-off-by: Jyothi Kumar Seerapu <jyothi.seerapu@oss.qualcomm.com>
---
Jyothi Kumar Seerapu (6):
      spi: spi-geni-qcom: Add transfer_one_message() support
      dt-bindings: dma: qcom-gpi: Add QSPI protocol identifier
      dt-bindings: spi: qcom,spi-geni-qcom: Document QSPI support
      soc: qcom: geni-se: Define the QSPI firmware protocol
      dmaengine: qcom: gpi: Add QSPI protocol support
      spi: spi-geni-qcom: Add QSPI support over GPI DMA

 .../bindings/spi/qcom,spi-geni-qcom.yaml           |   8 +-
 drivers/dma/qcom/gpi.c                             |  22 +-
 drivers/soc/qcom/qcom-geni-se.c                    |   7 +-
 drivers/spi/spi-geni-qcom.c                        | 455 ++++++++++++++++++---
 include/dt-bindings/dma/qcom-gpi.h                 |   1 +
 include/linux/dma/qcom-gpi-dma.h                   |   6 +
 include/linux/soc/qcom/geni-se.h                   |   1 +
 7 files changed, 446 insertions(+), 54 deletions(-)
---
base-commit: 7079a12d7506b07fb53b54a664bfad5fa9b16d70
change-id: 20261021-qspi_upstream_series-850249e432f5

Best regards,
--  
Jyothi Kumar Seerapu <jyothi.seerapu@oss.qualcomm.com>


^ permalink raw reply	[flat|nested] 12+ messages in thread

* [PATCH 1/6] spi: spi-geni-qcom: Add transfer_one_message() support
  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 ` Jyothi Kumar Seerapu
  2026-11-06 15:01 ` [PATCH 2/6] dt-bindings: dma: qcom-gpi: Add QSPI protocol identifier Jyothi Kumar Seerapu
                   ` (5 subsequent siblings)
  6 siblings, 0 replies; 12+ messages in thread
From: Jyothi Kumar Seerapu @ 2026-11-06 15:01 UTC (permalink / raw)
  To: Mark Brown, Vinod Koul, Frank Li, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Andy Gross, Bjorn Andersson,
	Konrad Dybcio
  Cc: linux-arm-msm, linux-spi, linux-kernel, dmaengine, devicetree,
	Krzysztof Kozlowski, Jyothi Kumar Seerapu

The GENI SPI driver currently relies on the SPI core's default
transfer_one_message() implementation. Adding Quad SPI (QSPI) support
requires driver-managed message sequencing. In particular, GENI QSPI
reads require combining a command/address TX phase and an RX data
phase into a single hardware command.

Add a driver-specific transfer_one_message() implementation, retaining
spi_geni_transfer_one() as an internal helper for programming individual
transfers. Handle completion, transfer delays, errors and DMA buffer
synchronization at the message level.

The custom message handler must also provide the GPIO chip-select
sequencing and delays previously handled by the SPI core. Track the
selected GPIO and its timing and polarity for device switching.
Invalidate the cached selection during setup and preserve the software
inactive delay for native chip-selects.

Signed-off-by: Jyothi Kumar Seerapu <jyothi.seerapu@oss.qualcomm.com>
---
 drivers/spi/spi-geni-qcom.c | 247 +++++++++++++++++++++++++++++++++++++++-----
 1 file changed, 224 insertions(+), 23 deletions(-)

diff --git a/drivers/spi/spi-geni-qcom.c b/drivers/spi/spi-geni-qcom.c
index 6c57f8309a3b..25c03096ee23 100644
--- a/drivers/spi/spi-geni-qcom.c
+++ b/drivers/spi/spi-geni-qcom.c
@@ -6,6 +6,7 @@
 #define CREATE_TRACE_POINTS
 #include <trace/events/qcom_geni_spi.h>
 
+#include <linux/acpi.h>
 #include <linux/clk.h>
 #include <linux/dmaengine.h>
 #include <linux/dma-mapping.h>
@@ -13,6 +14,7 @@
 #include <linux/interrupt.h>
 #include <linux/io.h>
 #include <linux/log2.h>
+#include <linux/math64.h>
 #include <linux/module.h>
 #include <linux/platform_device.h>
 #include <linux/pm_opp.h>
@@ -106,10 +108,14 @@ struct spi_geni_master {
 	struct completion abort_done;
 	struct completion tx_reset_done;
 	struct completion rx_reset_done;
+	struct completion xfer_done;
 	unsigned int oversampling;
 	spinlock_t lock;
 	int irq;
-	bool cs_flag;
+	struct gpio_desc *cs_gpiod;
+	struct spi_delay cs_hold;
+	struct spi_delay cs_inactive;
+	bool cs_inactive_level;
 	bool abort_failed;
 	struct dma_chan *tx;
 	struct dma_chan *rx;
@@ -400,23 +406,23 @@ spi_gsi_callback_result(void *cb, const struct dmaengine_result *result)
 	struct spi_controller *spi = cb;
 	struct spi_geni_master *mas = spi_controller_get_devdata(spi);
 
-	spi->cur_msg->status = -EIO;
+	/*
+	 * Record only failures. msg->status is finalized when the
+	 * entire SPI message completes.
+	 */
 	if (result->result != DMA_TRANS_NOERROR) {
 		dev_err(&spi->dev, "DMA txn failed: %d\n", result->result);
 		trace_geni_se_regs(&mas->se);
-		spi_finalize_current_transfer(spi);
-		return;
-	}
-
-	if (!result->residue) {
-		spi->cur_msg->status = 0;
+		spi->cur_msg->status = -EIO;
+	} else if (!result->residue) {
 		dev_dbg(&spi->dev, "DMA txn completed\n");
 	} else {
 		dev_err(&spi->dev, "DMA xfer has pending: %d\n", result->residue);
 		trace_geni_se_regs(&mas->se);
+		spi->cur_msg->status = -EIO;
 	}
 
-	spi_finalize_current_transfer(spi);
+	complete(&mas->xfer_done);
 }
 
 static int setup_gsi_xfer(struct spi_transfer *xfer, struct spi_geni_master *mas,
@@ -511,7 +517,7 @@ static int setup_gsi_xfer(struct spi_transfer *xfer, struct spi_geni_master *mas
 		dma_async_issue_pending(mas->rx);
 
 	dma_async_issue_pending(mas->tx);
-	return 1;
+	return 0;
 }
 
 static u32 get_xfer_len_in_words(struct spi_transfer *xfer,
@@ -926,7 +932,6 @@ static int spi_geni_transfer_one(struct spi_controller *spi,
 				 struct spi_transfer *xfer)
 {
 	struct spi_geni_master *mas = spi_controller_get_devdata(spi);
-	int ret;
 
 	if (spi_geni_is_abort_still_pending(mas))
 		return -EBUSY;
@@ -935,16 +940,206 @@ static int spi_geni_transfer_one(struct spi_controller *spi,
 	if (!xfer->len)
 		return 0;
 
-	if (mas->cur_xfer_mode == GENI_SE_FIFO || mas->cur_xfer_mode == GENI_SE_DMA) {
-		ret = setup_se_xfer(xfer, mas, slv->mode, spi);
-		/* SPI framework expects +ve ret code to wait for transfer complete */
-		if (!ret)
-			ret = 1;
-		return ret;
-	}
+	if (mas->cur_xfer_mode == GENI_SE_FIFO || mas->cur_xfer_mode == GENI_SE_DMA)
+		return setup_se_xfer(xfer, mas, slv->mode, spi);
+
 	return setup_gsi_xfer(xfer, mas, slv, spi);
 }
 
+static int spi_geni_setup(struct spi_device *slv)
+{
+	struct spi_geni_master *mas = spi_controller_get_devdata(slv->controller);
+
+	/* The core deasserts CS after setup, possibly with a new polarity. */
+	if (!(slv->mode & SPI_NO_CS) && mas->cs_gpiod == spi_get_csgpiod(slv, 0))
+		mas->cs_gpiod = NULL;
+
+	return 0;
+}
+
+static void spi_geni_set_cs(struct spi_device *spi_slv, bool enable)
+{
+	struct gpio_desc *cs_gpiod = spi_get_csgpiod(spi_slv, 0);
+	struct spi_geni_master *mas = spi_controller_get_devdata(spi_slv->controller);
+	bool level = true;
+
+	if (spi_slv->mode & SPI_NO_CS)
+		cs_gpiod = NULL;
+
+	/* Release a previously selected GPIO before switching devices. */
+	if (mas->cs_gpiod && (!enable || mas->cs_gpiod != cs_gpiod)) {
+		spi_delay_exec(&mas->cs_hold, NULL);
+		gpiod_set_value_cansleep(mas->cs_gpiod, mas->cs_inactive_level);
+		spi_delay_exec(&mas->cs_inactive, NULL);
+		mas->cs_gpiod = NULL;
+	}
+
+	/* Preserve the software inactive delay for hardware-controlled CS. */
+	if (!cs_gpiod && !enable && !(spi_slv->mode & SPI_NO_CS))
+		spi_delay_exec(&spi_slv->cs_inactive, NULL);
+
+	if (!enable || !cs_gpiod || mas->cs_gpiod == cs_gpiod)
+		return;
+
+	/* DT polarity is handled by gpiolib; ACPI uses SPI_CS_HIGH. */
+	if (is_acpi_device_node(dev_fwnode(&spi_slv->dev)) &&
+	    !(spi_slv->mode & SPI_CS_HIGH))
+		level = false;
+
+	gpiod_set_value_cansleep(cs_gpiod, level);
+	spi_delay_exec(&spi_slv->cs_setup, NULL);
+
+	/* GPIOs belong to the controller; do not retain a spi_device pointer. */
+	mas->cs_gpiod = cs_gpiod;
+	mas->cs_hold = spi_slv->cs_hold;
+	mas->cs_inactive = spi_slv->cs_inactive;
+	mas->cs_inactive_level = !level;
+}
+
+static void spi_geni_dma_sync(struct spi_controller *spi,
+			      struct spi_transfer *xfer, bool for_device)
+{
+	/* The core maps buffers with DMA_ATTR_SKIP_CPU_SYNC. */
+	if (for_device) {
+		if (xfer->tx_sg_mapped)
+			dma_sync_sgtable_for_device(spi->cur_tx_dma_dev,
+						    &xfer->tx_sg, DMA_TO_DEVICE);
+		if (xfer->rx_sg_mapped)
+			dma_sync_sgtable_for_device(spi->cur_rx_dma_dev,
+						    &xfer->rx_sg, DMA_FROM_DEVICE);
+	} else {
+		if (xfer->rx_sg_mapped)
+			dma_sync_sgtable_for_cpu(spi->cur_rx_dma_dev,
+						 &xfer->rx_sg, DMA_FROM_DEVICE);
+		if (xfer->tx_sg_mapped)
+			dma_sync_sgtable_for_cpu(spi->cur_tx_dma_dev,
+						 &xfer->tx_sg, DMA_TO_DEVICE);
+	}
+}
+
+static int spi_geni_wait_for_xfer(struct spi_controller *spi,
+				  const struct spi_transfer *xfer)
+{
+	struct spi_geni_master *mas = spi_controller_get_devdata(spi);
+	u64 bits, ms;
+
+	if (spi_controller_is_target(spi)) {
+		/* A target cannot predict when its peer will supply clocks. */
+		if (wait_for_completion_interruptible(&mas->xfer_done)) {
+			dev_dbg(mas->dev, "SPI transfer interrupted\n");
+			return -EINTR;
+		}
+		return 0;
+	}
+
+	bits = (u64)xfer->len * BITS_PER_BYTE;
+	ms = div_u64(bits * MSEC_PER_SEC, xfer->speed_hz ?: 100000);
+	ms = min_t(u64, ms * 2 + 200, UINT_MAX);
+
+	if (!wait_for_completion_timeout(&mas->xfer_done, msecs_to_jiffies(ms))) {
+		dev_err(mas->dev, "Transfer timed out\n");
+		return -ETIMEDOUT;
+	}
+
+	return 0;
+}
+
+static int spi_geni_transfer_one_message(struct spi_controller *spi,
+					 struct spi_message *msg)
+{
+	struct spi_geni_master *mas = spi_controller_get_devdata(spi);
+	struct spi_device *spi_slv = msg->spi;
+	struct spi_transfer *xfer, *next_xfer;
+	bool keep_cs = false;
+	bool dma_synced = false;
+	bool started = false;
+	int ret = 0;
+
+	xfer = list_first_entry(&msg->transfers, struct spi_transfer, transfer_list);
+	spi_geni_set_cs(spi_slv, !xfer->cs_off);
+
+	list_for_each_entry(xfer, &msg->transfers, transfer_list) {
+		if ((xfer->tx_buf || xfer->rx_buf) && xfer->len) {
+			reinit_completion(&mas->xfer_done);
+
+			/* Synchronize buffers only for DMA; the SE FIFO path uses CPU access. */
+			dma_synced = mas->cur_xfer_mode == GENI_GPI_DMA ||
+				     ((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);
+
+			ret = spi_geni_transfer_one(spi, spi_slv, 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);
+			if (ret)
+				goto out_err;
+
+			/* DMA errors and target aborts are reported through msg->status. */
+			if (msg->status != -EINPROGRESS) {
+				ret = msg->status;
+				goto out_err;
+			}
+			if (dma_synced) {
+				spi_geni_dma_sync(spi, xfer, false);
+				dma_synced = false;
+			}
+		} else if (xfer->len) {
+			dev_err(&spi_slv->dev,
+				"Bufferless transfer has length %u\n", xfer->len);
+			ret = -EINVAL;
+			goto out_err;
+		}
+
+		msg->actual_length += xfer->len;
+		ret = spi_transfer_delay_exec(xfer);
+		if (ret)
+			goto out_err;
+
+		next_xfer = list_is_last(&xfer->transfer_list, &msg->transfers) ?
+			    NULL : list_next_entry(xfer, transfer_list);
+		if (!next_xfer) {
+			keep_cs = xfer->cs_change;
+		} else if (xfer->cs_change) {
+			if (!xfer->cs_off)
+				spi_geni_set_cs(spi_slv, false);
+
+			spi_transfer_cs_change_delay_exec(msg, xfer);
+
+			if (!next_xfer->cs_off)
+				spi_geni_set_cs(spi_slv, true);
+		} else if (xfer->cs_off != next_xfer->cs_off) {
+			spi_geni_set_cs(spi_slv, xfer->cs_off);
+		}
+	}
+
+out_err:
+	if (!ret && msg->status != -EINPROGRESS)
+		ret = msg->status;
+
+	if (ret && started)
+		spi_geni_handle_err(spi, msg);
+
+	/* Return buffer ownership only after DMA has completed or been stopped. */
+	if (dma_synced)
+		spi_geni_dma_sync(spi, xfer, false);
+
+	if (ret || !keep_cs)
+		spi_geni_set_cs(spi_slv, false);
+
+	if (msg->status == -EINPROGRESS)
+		msg->status = ret;
+
+	spi_finalize_current_message(spi);
+
+	return ret;
+}
+
 static irqreturn_t geni_spi_isr(int irq, void *data)
 {
 	struct spi_controller *spi = data;
@@ -979,8 +1174,6 @@ static irqreturn_t geni_spi_isr(int irq, void *data)
 
 		if (m_irq & M_CMD_DONE_EN) {
 			if (mas->cur_xfer) {
-				spi_finalize_current_transfer(spi);
-				mas->cur_xfer = NULL;
 				/*
 				 * If this happens, then a CMD_DONE came before all the
 				 * Tx buffer bytes were sent out. This is unusual, log
@@ -1005,6 +1198,9 @@ static irqreturn_t geni_spi_isr(int irq, void *data)
 						mas->rx_rem_bytes, mas->cur_bits_per_word);
 					trace_geni_se_regs(se);
 				}
+
+				complete(&mas->xfer_done);
+				mas->cur_xfer = NULL;
 			} else {
 				complete(&mas->cs_done);
 			}
@@ -1025,7 +1221,7 @@ static irqreturn_t geni_spi_isr(int irq, void *data)
 		if (dma_rx_status & RX_RESET_DONE)
 			complete(&mas->rx_reset_done);
 		if (!mas->tx_rem_bytes && !mas->rx_rem_bytes && xfer) {
-			spi_finalize_current_transfer(spi);
+			complete(&mas->xfer_done);
 			mas->cur_xfer = NULL;
 		}
 	}
@@ -1057,11 +1253,14 @@ static irqreturn_t geni_spi_isr(int irq, void *data)
 
 static int spi_geni_target_abort(struct spi_controller *spi)
 {
+	struct spi_geni_master *mas = spi_controller_get_devdata(spi);
+
 	if (!spi->cur_msg)
 		return 0;
 
+	spi->cur_msg->status = -ECANCELED;
 	handle_se_timeout(spi);
-	spi_finalize_current_transfer(spi);
+	complete(&mas->xfer_done);
 
 	return 0;
 }
@@ -1116,8 +1315,9 @@ static int spi_geni_probe(struct platform_device *pdev)
 	spi->num_chipselect = 4;
 	spi->max_speed_hz = 50000000;
 	spi->max_dma_len = 0xffff0; /* 24 bits for tx/rx dma length */
+	spi->setup = spi_geni_setup;
 	spi->prepare_message = spi_geni_prepare_message;
-	spi->transfer_one = spi_geni_transfer_one;
+	spi->transfer_one_message = spi_geni_transfer_one_message;
 	spi->can_dma = geni_can_dma;
 	spi->dma_map_dev = dev->parent;
 	spi->auto_runtime_pm = true;
@@ -1129,6 +1329,7 @@ static int spi_geni_probe(struct platform_device *pdev)
 	init_completion(&mas->abort_done);
 	init_completion(&mas->tx_reset_done);
 	init_completion(&mas->rx_reset_done);
+	init_completion(&mas->xfer_done);
 	spin_lock_init(&mas->lock);
 
 	if (spi->target)

-- 
2.34.1


^ permalink raw reply	[flat|nested] 12+ messages in thread

* [PATCH 2/6] dt-bindings: dma: qcom-gpi: Add QSPI protocol identifier
  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:01 ` Jyothi Kumar Seerapu
  2026-11-09 13:42   ` Krzysztof Kozlowski
  2026-11-06 15:01 ` [PATCH 3/6] dt-bindings: spi: qcom,spi-geni-qcom: Document QSPI support Jyothi Kumar Seerapu
                   ` (4 subsequent siblings)
  6 siblings, 1 reply; 12+ messages in thread
From: Jyothi Kumar Seerapu @ 2026-11-06 15:01 UTC (permalink / raw)
  To: Mark Brown, Vinod Koul, Frank Li, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Andy Gross, Bjorn Andersson,
	Konrad Dybcio
  Cc: linux-arm-msm, linux-spi, linux-kernel, dmaengine, devicetree,
	Krzysztof Kozlowski, Jyothi Kumar Seerapu

Add QCOM_GPI_QSPI to identify QSPI (Quad SPI) channels in GPI DMA
specifiers. Its value, 9, matches the GENI serial engine firmware
protocol ID for QSPI.

This lets a QSPI controller reference its TX and RX GPI DMA channels
through the dmas property, using the existing channel specifier format.

Signed-off-by: Jyothi Kumar Seerapu <jyothi.seerapu@oss.qualcomm.com>
---
 include/dt-bindings/dma/qcom-gpi.h | 1 +
 1 file changed, 1 insertion(+)

diff --git a/include/dt-bindings/dma/qcom-gpi.h b/include/dt-bindings/dma/qcom-gpi.h
index ebda2a37f52a..68fc1414af55 100644
--- 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		9
 
 #endif /* __DT_BINDINGS_DMA_QCOM_GPI_H__ */

-- 
2.34.1


^ permalink raw reply	[flat|nested] 12+ messages in thread

* [PATCH 3/6] dt-bindings: spi: qcom,spi-geni-qcom: Document QSPI support
  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:01 ` [PATCH 2/6] dt-bindings: dma: qcom-gpi: Add QSPI protocol identifier Jyothi Kumar Seerapu
@ 2026-11-06 15:01 ` 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
                   ` (3 subsequent siblings)
  6 siblings, 1 reply; 12+ messages in thread
From: Jyothi Kumar Seerapu @ 2026-11-06 15:01 UTC (permalink / raw)
  To: Mark Brown, Vinod Koul, Frank Li, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Andy Gross, Bjorn Andersson,
	Konrad Dybcio
  Cc: linux-arm-msm, linux-spi, linux-kernel, dmaengine, devicetree,
	Krzysztof Kozlowski, Jyothi Kumar Seerapu

Add the "qcom,geni-qspi" compatible to identify GENI Serial Engine
instances configured for QSPI operation.

The compatible also allows the driver to select the appropriate protocol
firmware when the Serial Engine is unconfigured (GENI_SE_INVALID_PROTO),
distinguishing between SPI and QSPI firmware.

Signed-off-by: Jyothi Kumar Seerapu <jyothi.seerapu@oss.qualcomm.com>
---
 Documentation/devicetree/bindings/spi/qcom,spi-geni-qcom.yaml | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/Documentation/devicetree/bindings/spi/qcom,spi-geni-qcom.yaml b/Documentation/devicetree/bindings/spi/qcom,spi-geni-qcom.yaml
index edf399681d7a..e568b67d077e 100644
--- a/Documentation/devicetree/bindings/spi/qcom,spi-geni-qcom.yaml
+++ b/Documentation/devicetree/bindings/spi/qcom,spi-geni-qcom.yaml
@@ -19,6 +19,10 @@ description:
   SPI in master mode supports up to 50MHz, up to four chip selects,
   programmable data path from 4 bits to 32 bits and numerous protocol variants.
 
+  The "qcom,geni-qspi" compatible string identifies GENI serial engine
+  instances configured for QSPI operation. QSPI supports transfers over
+  multiple data lines for higher throughput than standard SPI.
+
   SPI Controller nodes must be child of GENI based Qualcomm Universal
   Peripharal. Please refer GENI based QUP wrapper controller node bindings
   described in Documentation/devicetree/bindings/soc/qcom/qcom,geni-se.yaml.
@@ -29,7 +33,9 @@ allOf:
 
 properties:
   compatible:
-    const: qcom,geni-spi
+    enum:
+      - qcom,geni-spi
+      - qcom,geni-qspi
 
   clocks:
     maxItems: 1

-- 
2.34.1


^ permalink raw reply	[flat|nested] 12+ messages in thread

* [PATCH 4/6] soc: qcom: geni-se: Define the QSPI firmware protocol
  2026-11-06 15:01 [PATCH 0/6] spi: qcom-geni: Add QSPI support over GPI DMA Jyothi Kumar Seerapu
                   ` (2 preceding siblings ...)
  2026-11-06 15:01 ` [PATCH 3/6] dt-bindings: spi: qcom,spi-geni-qcom: Document QSPI support Jyothi Kumar Seerapu
@ 2026-11-06 15:01 ` Jyothi Kumar Seerapu
  2026-11-09 13:44   ` Krzysztof Kozlowski
  2026-11-06 15:01 ` [PATCH 5/6] dmaengine: qcom: gpi: Add QSPI protocol support Jyothi Kumar Seerapu
                   ` (2 subsequent siblings)
  6 siblings, 1 reply; 12+ messages in thread
From: Jyothi Kumar Seerapu @ 2026-11-06 15:01 UTC (permalink / raw)
  To: Mark Brown, Vinod Koul, Frank Li, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Andy Gross, Bjorn Andersson,
	Konrad Dybcio
  Cc: linux-arm-msm, linux-spi, linux-kernel, dmaengine, devicetree,
	Krzysztof Kozlowski, Jyothi Kumar Seerapu

Add GENI_SE_QSPI with firmware protocol ID 9. The GENI SPI driver uses
this value to request QSPI firmware and validate the protocol running
on a serial engine.

Extend the firmware loader's protocol-name table to accept QSPI.
Reject unused protocol IDs in the sparse table before looking up
firmware.

Signed-off-by: Jyothi Kumar Seerapu <jyothi.seerapu@oss.qualcomm.com>
---
 drivers/soc/qcom/qcom-geni-se.c  | 7 +++++--
 include/linux/soc/qcom/geni-se.h | 1 +
 2 files changed, 6 insertions(+), 2 deletions(-)

diff --git a/drivers/soc/qcom/qcom-geni-se.c b/drivers/soc/qcom/qcom-geni-se.c
index 873bfbd6b2b7..396f3d81d292 100644
--- a/drivers/soc/qcom/qcom-geni-se.c
+++ b/drivers/soc/qcom/qcom-geni-se.c
@@ -122,7 +122,10 @@ struct geni_se_desc {
 static const char * const icc_path_names[] = {"qup-core", "qup-config",
 						"qup-memory"};
 
-static const char * const protocol_name[] = { "None", "SPI", "UART", "I2C", "I3C", "SPI SLAVE" };
+static const char * const protocol_name[] = {
+	"None", "SPI", "UART", "I2C", "I3C", "SPI SLAVE",
+	[GENI_SE_QSPI] = "QSPI",
+};
 
 /**
  * struct se_fw_hdr - Serial Engine firmware configuration header
@@ -1557,7 +1560,7 @@ int geni_load_se_firmware(struct geni_se *se, enum geni_se_protocol_type protoco
 	enum geni_se_xfer_mode mode = GENI_SE_FIFO;
 	int ret;
 
-	if (protocol >= ARRAY_SIZE(protocol_name)) {
+	if (protocol >= ARRAY_SIZE(protocol_name) || !protocol_name[protocol]) {
 		dev_err(se->dev, "Invalid geni-se protocol: %d", protocol);
 		return -EINVAL;
 	}
diff --git a/include/linux/soc/qcom/geni-se.h b/include/linux/soc/qcom/geni-se.h
index 29a53bbc0dd4..e96266631ae1 100644
--- a/include/linux/soc/qcom/geni-se.h
+++ b/include/linux/soc/qcom/geni-se.h
@@ -37,6 +37,7 @@ enum geni_se_protocol_type {
 	GENI_SE_I2C,
 	GENI_SE_I3C,
 	GENI_SE_SPI_SLAVE,
+	GENI_SE_QSPI = 9,
 	GENI_SE_INVALID_PROTO = 255,
 };
 

-- 
2.34.1


^ permalink raw reply	[flat|nested] 12+ messages in thread

* [PATCH 5/6] dmaengine: qcom: gpi: Add QSPI protocol support
  2026-11-06 15:01 [PATCH 0/6] spi: qcom-geni: Add QSPI support over GPI DMA Jyothi Kumar Seerapu
                   ` (3 preceding siblings ...)
  2026-11-06 15:01 ` [PATCH 4/6] soc: qcom: geni-se: Define the QSPI firmware protocol Jyothi Kumar Seerapu
@ 2026-11-06 15:01 ` Jyothi Kumar Seerapu
  2026-11-06 15:01 ` [PATCH 6/6] spi: spi-geni-qcom: Add QSPI support over GPI DMA Jyothi Kumar Seerapu
  2026-11-08 15:18 ` [PATCH 0/6] spi: qcom-geni: " Mark Brown
  6 siblings, 0 replies; 12+ messages in thread
From: Jyothi Kumar Seerapu @ 2026-11-06 15:01 UTC (permalink / raw)
  To: Mark Brown, Vinod Koul, Frank Li, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Andy Gross, Bjorn Andersson,
	Konrad Dybcio
  Cc: linux-arm-msm, linux-spi, linux-kernel, dmaengine, devicetree,
	Krzysztof Kozlowski, Jyothi Kumar Seerapu

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@oss.qualcomm.com>
---
 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


^ permalink raw reply	[flat|nested] 12+ messages in thread

* [PATCH 6/6] spi: spi-geni-qcom: Add QSPI support over GPI DMA
  2026-11-06 15:01 [PATCH 0/6] spi: qcom-geni: Add QSPI support over GPI DMA Jyothi Kumar Seerapu
                   ` (4 preceding siblings ...)
  2026-11-06 15:01 ` [PATCH 5/6] dmaengine: qcom: gpi: Add QSPI protocol support Jyothi Kumar Seerapu
@ 2026-11-06 15:01 ` Jyothi Kumar Seerapu
  2026-11-08 15:18 ` [PATCH 0/6] spi: qcom-geni: " Mark Brown
  6 siblings, 0 replies; 12+ messages in thread
From: Jyothi Kumar Seerapu @ 2026-11-06 15:01 UTC (permalink / raw)
  To: Mark Brown, Vinod Koul, Frank Li, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Andy Gross, Bjorn Andersson,
	Konrad Dybcio
  Cc: linux-arm-msm, linux-spi, linux-kernel, dmaengine, devicetree,
	Krzysztof Kozlowski, Jyothi Kumar Seerapu

Add QSPI support to the GENI SPI driver using the qcom,geni-qspi
compatible. Select the QSPI firmware protocol and enable single, dual
and quad SDR transfers. QSPI operation is supported only in controller
mode and requires GPI DMA.

QSPI read transactions consist of a command/address TX phase, an optional
dummy phase and an RX data phase that must be executed as a single
hardware command. Detect and combine the corresponding SPI transfers into
one SPI_TX_RX command before submitting it to GPI DMA.

Update message accounting, DMA buffer synchronization and timeout
calculations to cover all transfers consumed by a combined QSPI command.

Wait for both TX and RX DMA completions within a shared timeout budget
before completing a combined read. This prevents the RX buffer from
being unmapped while receive DMA is still active.

Signed-off-by: Jyothi Kumar Seerapu <jyothi.seerapu@oss.qualcomm.com>
---
 drivers/spi/spi-geni-qcom.c | 271 +++++++++++++++++++++++++++++++++++---------
 1 file changed, 216 insertions(+), 55 deletions(-)

diff --git a/drivers/spi/spi-geni-qcom.c b/drivers/spi/spi-geni-qcom.c
index c2c3400b24c4..26dad85a590e 100644
--- a/drivers/spi/spi-geni-qcom.c
+++ b/drivers/spi/spi-geni-qcom.c
@@ -67,7 +67,6 @@
 #define SPI_TX_ONLY		1
 #define SPI_RX_ONLY		2
 #define SPI_CS_ASSERT		8
-#define SPI_CS_DEASSERT		9
 #define SPI_SCK_ONLY		10
 /* M_CMD params for SPI */
 #define SPI_PRE_CMD_DELAY	BIT(0)
@@ -81,11 +80,20 @@
 #define GSI_CPHA		BIT(4)
 #define GSI_CPOL		BIT(5)
 
+/* Raw QSPI GO mode values encoded by the GPI DMA driver into the GO TRE. */
+#define QSPI_SINGLE_SDR		0x0
+#define QSPI_SINGLE_DDR		(BIT(2) | BIT(3))
+#define QSPI_DUAL_SDR		BIT(2)
+#define QSPI_DUAL_DDR		(BIT(1) | BIT(2))
+#define QSPI_QUAD_SDR		BIT(1)
+#define QSPI_QUAD_DDR		BIT(0)
+
 struct geni_spi_desc {
 	int (*resources_init)(struct geni_se *se);
 	int (*set_rate)(struct geni_se *se, unsigned long clk_freq);
 	int (*power_on)(struct geni_se *se);
 	int (*power_off)(struct geni_se *se);
+	bool is_qspi;
 };
 
 struct spi_geni_master {
@@ -120,6 +128,7 @@ struct spi_geni_master {
 	struct dma_chan *rx;
 	int cur_xfer_mode;
 	const struct geni_spi_desc *dev_data;
+	u32 dummy_len;
 };
 
 static void spi_slv_setup(struct spi_geni_master *mas)
@@ -412,27 +421,50 @@ spi_gsi_callback_result(void *cb, const struct dmaengine_result *result)
 	if (result->result != DMA_TRANS_NOERROR) {
 		dev_err(&spi->dev, "DMA txn failed: %d\n", result->result);
 		trace_geni_se_regs(&mas->se);
-		spi->cur_msg->status = -EIO;
+		WRITE_ONCE(spi->cur_msg->status, -EIO);
 	} else if (!result->residue) {
 		dev_dbg(&spi->dev, "DMA txn completed\n");
 	} else {
 		dev_err(&spi->dev, "DMA xfer has pending: %d\n", result->residue);
 		trace_geni_se_regs(&mas->se);
-		spi->cur_msg->status = -EIO;
+		WRITE_ONCE(spi->cur_msg->status, -EIO);
 	}
 
 	complete(&mas->xfer_done);
 }
 
+static u32 get_xfer_len_in_words(struct spi_transfer *xfer,
+				 struct spi_geni_master *mas);
+
+static u32 spi_geni_qspi_mode(const struct spi_transfer *xfer)
+{
+	unsigned int buswidth = xfer->rx_buf ? xfer->rx_nbits : xfer->tx_nbits;
+
+	switch (buswidth) {
+	case SPI_NBITS_DUAL:
+		return QSPI_DUAL_SDR;
+	case SPI_NBITS_QUAD:
+		return QSPI_QUAD_SDR;
+	default:
+		return QSPI_SINGLE_SDR;
+	}
+}
+
 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;
 	}
 
 	peripheral.loopback_en = !!(spi_slv->mode & SPI_LOOP);
@@ -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);
+
 	ret = get_spi_clk_cfg(mas->cur_speed_hz, mas,
 			      &peripheral.clk_src, &peripheral.clk_div);
 	if (ret) {
@@ -474,18 +518,17 @@ static int setup_gsi_xfer(struct spi_transfer *xfer, struct spi_geni_master *mas
 	}
 
 	/*
-	 * Set fragmentation to keep CS asserted after this transfer when:
-	 *  - non-last transfer with cs_change=0: keep CS asserted between chained transfers
-	 *  - last transfer with cs_change=1: keep CS asserted after the message
-	 *    (e.g. TPM TIS SPI uses cs_change=1 on single-transfer messages to
-	 *     keep CS asserted across header, wait-state and data phases)
+	 * Set fragmentation from the last transfer consumed by this command.
+	 * Keep CS asserted between transfers when cs_change is clear, or after
+	 * the message when the final transfer sets cs_change.
 	 */
-	peripheral.fragmentation = list_is_last(&xfer->transfer_list, &spi->cur_msg->transfers) ?
-				   xfer->cs_change : !xfer->cs_change;
+	peripheral.fragmentation =
+		list_is_last(&last->transfer_list, &spi->cur_msg->transfers) ?
+		last->cs_change : !last->cs_change;
 
 	if (peripheral.cmd & SPI_RX) {
 		dmaengine_slave_config(mas->rx, &config);
-		rx_desc = dmaengine_prep_slave_sg(mas->rx, xfer->rx_sg.sgl, xfer->rx_sg.nents,
+		rx_desc = dmaengine_prep_slave_sg(mas->rx, last->rx_sg.sgl, last->rx_sg.nents,
 						  DMA_DEV_TO_MEM, flags);
 		if (!rx_desc) {
 			dev_err(mas->dev, "Err setting up rx desc\n");
@@ -502,12 +545,18 @@ static int setup_gsi_xfer(struct spi_transfer *xfer, struct spi_geni_master *mas
 					  DMA_MEM_TO_DEV, flags);
 	if (!tx_desc) {
 		dev_err(mas->dev, "Err setting up tx desc\n");
-		return -EIO;
+		ret = -EIO;
+		goto err_free_rx_desc;
 	}
 
 	tx_desc->callback_result = spi_gsi_callback_result;
 	tx_desc->callback_param = spi;
 
+	if (peripheral.cmd == SPI_TX_RX) {
+		rx_desc->callback_result = spi_gsi_callback_result;
+		rx_desc->callback_param = spi;
+	}
+
 	if (peripheral.cmd & SPI_RX)
 		dmaengine_submit(rx_desc);
 	dmaengine_submit(tx_desc);
@@ -517,6 +566,11 @@ static int setup_gsi_xfer(struct spi_transfer *xfer, struct spi_geni_master *mas
 
 	dma_async_issue_pending(mas->tx);
 	return 0;
+
+err_free_rx_desc:
+	if (rx_desc)
+		rx_desc->desc_free(rx_desc);
+	return ret;
 }
 
 static u32 get_xfer_len_in_words(struct spi_transfer *xfer,
@@ -647,17 +701,27 @@ static int spi_geni_init(struct spi_geni_master *mas)
 	proto = geni_se_read_proto(se);
 
 	if (spi->target) {
+		if (mas->dev_data->is_qspi) {
+			dev_err(mas->dev, "QSPI is not supported in target mode\n");
+			return -EINVAL;
+		}
 		if (proto != GENI_SE_SPI_SLAVE) {
 			dev_err(mas->dev, "Invalid proto %d\n", proto);
 			return -EINVAL;
 		}
 		spi_slv_setup(mas);
 	} else if (proto == GENI_SE_INVALID_PROTO) {
-		ret = geni_load_se_firmware(se, GENI_SE_SPI);
+		ret = geni_load_se_firmware(se, mas->dev_data->is_qspi ?
+					   GENI_SE_QSPI : GENI_SE_SPI);
 		if (ret) {
 			dev_err(mas->dev, "spi master firmware load failed ret: %d\n", ret);
 			return ret;
 		}
+	} else if (mas->dev_data->is_qspi) {
+		if (proto != GENI_SE_QSPI) {
+			dev_err(mas->dev, "Invalid proto %d, expected QSPI\n", proto);
+			return -EINVAL;
+		}
 	} else if (proto != GENI_SE_SPI) {
 		dev_err(mas->dev, "Invalid proto %d\n", proto);
 		return -EINVAL;
@@ -703,6 +767,10 @@ static int spi_geni_init(struct spi_geni_master *mas)
 		fallthrough;
 
 	case 0:
+		if (mas->dev_data->is_qspi) {
+			dev_err(mas->dev, "QSPI is not supported in FIFO mode\n");
+			return -EOPNOTSUPP;
+		}
 		mas->cur_xfer_mode = GENI_SE_FIFO;
 		geni_se_select_mode(se, GENI_SE_FIFO);
 		/* setup_fifo_params assumes that these registers start with a zero value */
@@ -928,7 +996,8 @@ static int setup_se_xfer(struct spi_transfer *xfer,
 
 static int spi_geni_transfer_one(struct spi_controller *spi,
 				 struct spi_device *slv,
-				 struct spi_transfer *xfer)
+				 struct spi_transfer *xfer,
+				 struct spi_transfer *rx_xfer)
 {
 	struct spi_geni_master *mas = spi_controller_get_devdata(spi);
 
@@ -942,7 +1011,7 @@ static int spi_geni_transfer_one(struct spi_controller *spi,
 	if (mas->cur_xfer_mode == GENI_SE_FIFO || mas->cur_xfer_mode == GENI_SE_DMA)
 		return setup_se_xfer(xfer, mas, slv->mode, spi);
 
-	return setup_gsi_xfer(xfer, mas, slv, spi);
+	return setup_gsi_xfer(xfer, mas, slv, spi, rx_xfer);
 }
 
 static int spi_geni_setup(struct spi_device *slv)
@@ -996,30 +1065,46 @@ static void spi_geni_set_cs(struct spi_device *spi_slv, bool enable)
 }
 
 static void spi_geni_dma_sync(struct spi_controller *spi,
-			      struct spi_transfer *xfer, bool for_device)
+			      struct spi_transfer *first,
+			      struct spi_transfer *last, bool for_device)
 {
-	/* The core maps buffers with DMA_ATTR_SKIP_CPU_SYNC. */
-	if (for_device) {
-		if (xfer->tx_sg_mapped)
-			dma_sync_sgtable_for_device(spi->cur_tx_dma_dev,
-						    &xfer->tx_sg, DMA_TO_DEVICE);
-		if (xfer->rx_sg_mapped)
-			dma_sync_sgtable_for_device(spi->cur_rx_dma_dev,
-						    &xfer->rx_sg, DMA_FROM_DEVICE);
-	} else {
-		if (xfer->rx_sg_mapped)
-			dma_sync_sgtable_for_cpu(spi->cur_rx_dma_dev,
-						 &xfer->rx_sg, DMA_FROM_DEVICE);
-		if (xfer->tx_sg_mapped)
-			dma_sync_sgtable_for_cpu(spi->cur_tx_dma_dev,
-						 &xfer->tx_sg, DMA_TO_DEVICE);
+	struct spi_transfer *xfer = first;
+
+	/*
+	 * The core maps buffers with DMA_ATTR_SKIP_CPU_SYNC. Match its per-transfer
+	 * synchronization for every mapped buffer from first through last,
+	 * including transfers consumed by a combined QSPI command.
+	 */
+	for (;;) {
+		if (for_device) {
+			if (xfer->tx_sg_mapped)
+				dma_sync_sgtable_for_device(spi->cur_tx_dma_dev,
+							    &xfer->tx_sg, DMA_TO_DEVICE);
+			if (xfer->rx_sg_mapped)
+				dma_sync_sgtable_for_device(spi->cur_rx_dma_dev,
+							    &xfer->rx_sg, DMA_FROM_DEVICE);
+		} else {
+			if (xfer->rx_sg_mapped)
+				dma_sync_sgtable_for_cpu(spi->cur_rx_dma_dev,
+							 &xfer->rx_sg, DMA_FROM_DEVICE);
+			if (xfer->tx_sg_mapped)
+				dma_sync_sgtable_for_cpu(spi->cur_tx_dma_dev,
+							 &xfer->tx_sg, DMA_TO_DEVICE);
+		}
+
+		if (xfer == last)
+			break;
+		xfer = list_next_entry(xfer, transfer_list);
 	}
 }
 
 static int spi_geni_wait_for_xfer(struct spi_controller *spi,
-				  const struct spi_transfer *xfer)
+				  const struct spi_transfer *first,
+				  const struct spi_transfer *last)
 {
 	struct spi_geni_master *mas = spi_controller_get_devdata(spi);
+	unsigned int num_completions = first == last ? 1 : 2;
+	unsigned long time_left;
 	u64 bits, ms;
 
 	if (spi_controller_is_target(spi)) {
@@ -1031,14 +1116,24 @@ static int spi_geni_wait_for_xfer(struct spi_controller *spi,
 		return 0;
 	}
 
-	bits = (u64)xfer->len * BITS_PER_BYTE;
-	ms = div_u64(bits * MSEC_PER_SEC, xfer->speed_hz ?: 100000);
+	/* Use single-lane timing for all phases as an upper bound. */
+	bits = (u64)first->len * BITS_PER_BYTE;
+	if (last != first)
+		bits += (u64)last->len * BITS_PER_BYTE + mas->dummy_len;
+	ms = div_u64(bits * MSEC_PER_SEC, first->speed_hz ?: 100000);
 	ms = min_t(u64, ms * 2 + 200, UINT_MAX);
 
-	if (!wait_for_completion_timeout(&mas->xfer_done, msecs_to_jiffies(ms))) {
-		dev_err(mas->dev, "Transfer timed out\n");
-		return -ETIMEDOUT;
-	}
+	/* Wait for all expected completions before returning buffer ownership. */
+	time_left = msecs_to_jiffies(ms);
+	do {
+		time_left = wait_for_completion_timeout(&mas->xfer_done, time_left);
+		if (!time_left) {
+			dev_err(mas->dev, "Transfer timed out\n");
+			return -ETIMEDOUT;
+		}
+		if (READ_ONCE(spi->cur_msg->status) != -EINPROGRESS)
+			break;
+	} while (--num_completions);
 
 	return 0;
 }
@@ -1048,7 +1143,7 @@ static int spi_geni_transfer_one_message(struct spi_controller *spi,
 {
 	struct spi_geni_master *mas = spi_controller_get_devdata(spi);
 	struct spi_device *spi_slv = msg->spi;
-	struct spi_transfer *xfer, *next_xfer;
+	struct spi_transfer *xfer, *next_xfer, *last_xfer;
 	bool keep_cs = false;
 	bool dma_synced = false;
 	bool started = false;
@@ -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);
+			}
+
+			/* 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);
+			}
+		}
+
 		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) {
@@ -1095,6 +1239,11 @@ static int spi_geni_transfer_one_message(struct spi_controller *spi,
 			goto out_err;
 		}
 
+		/* Account for all phases and use the final phase's delay and CS. */
+		while (xfer != last_xfer) {
+			msg->actual_length += xfer->len;
+			xfer = list_next_entry(xfer, transfer_list);
+		}
 		msg->actual_length += xfer->len;
 		ret = spi_transfer_delay_exec(xfer);
 		if (ret)
@@ -1126,7 +1275,7 @@ static int spi_geni_transfer_one_message(struct spi_controller *spi,
 
 	/* Return buffer ownership only after DMA has completed or been stopped. */
 	if (dma_synced)
-		spi_geni_dma_sync(spi, xfer, false);
+		spi_geni_dma_sync(spi, xfer, last_xfer, false);
 
 	if (ret || !keep_cs)
 		spi_geni_set_cs(spi_slv, false);
@@ -1309,7 +1458,10 @@ static int spi_geni_probe(struct platform_device *pdev)
 		return ret;
 
 	spi->bus_num = -1;
-	spi->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LOOP | SPI_CS_HIGH;
+	spi->mode_bits = mas->dev_data->is_qspi ?
+			 (SPI_CPOL | SPI_CPHA | SPI_CS_HIGH |
+			  SPI_TX_DUAL | SPI_RX_DUAL | SPI_TX_QUAD | SPI_RX_QUAD) :
+			 (SPI_CPOL | SPI_CPHA | SPI_LOOP | SPI_CS_HIGH);
 	spi->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 32);
 	spi->num_chipselect = 4;
 	spi->max_speed_hz = 50000000;
@@ -1428,6 +1580,14 @@ static const struct geni_spi_desc geni_spi = {
 	.power_off = geni_se_resources_deactivate,
 };
 
+static const struct geni_spi_desc geni_qspi = {
+	.resources_init = geni_se_resources_init,
+	.set_rate = geni_spi_set_clock_and_bw,
+	.power_on = geni_se_resources_activate,
+	.power_off = geni_se_resources_deactivate,
+	.is_qspi = true,
+};
+
 static const struct geni_spi_desc sa8255p_geni_spi = {
 	.resources_init = geni_se_domain_attach,
 	.set_rate = geni_se_set_perf_opp,
@@ -1435,6 +1595,7 @@ static const struct geni_spi_desc sa8255p_geni_spi = {
 
 static const struct of_device_id spi_geni_dt_match[] = {
 	{ .compatible = "qcom,geni-spi", .data = &geni_spi },
+	{ .compatible = "qcom,geni-qspi", .data = &geni_qspi },
 	{ .compatible = "qcom,sa8255p-geni-spi", .data = &sa8255p_geni_spi },
 	{}
 };

-- 
2.34.1


^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH 0/6] spi: qcom-geni: Add QSPI support over GPI DMA
  2026-11-06 15:01 [PATCH 0/6] spi: qcom-geni: Add QSPI support over GPI DMA Jyothi Kumar Seerapu
                   ` (5 preceding siblings ...)
  2026-11-06 15:01 ` [PATCH 6/6] spi: spi-geni-qcom: Add QSPI support over GPI DMA Jyothi Kumar Seerapu
@ 2026-11-08 15:18 ` Mark Brown
  2026-11-09  7:10   ` Jyothi Kumar Seerapu
  6 siblings, 1 reply; 12+ messages in thread
From: Mark Brown @ 2026-11-08 15:18 UTC (permalink / raw)
  To: Jyothi Kumar Seerapu
  Cc: Mark Brown, Vinod Koul, Frank Li, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Andy Gross, Bjorn Andersson,
	Konrad Dybcio, linux-arm-msm, linux-spi, linux-kernel, dmaengine,
	devicetree, Krzysztof Kozlowski

> This series adds QSPI support to the Qualcomm GENI SPI driver using
> GPI DMA. It supports single, dual and quad SDR transfers in controller
> mode.

This doesn't apply against current code, please check and resend.

> ---
> base-commit: 7079a12d7506b07fb53b54a664bfad5fa9b16d70
> change-id: 20261021-qspi_upstream_series-850249e432f5

That's a random -next commit...

-- 
Mark Brown <broonie@kernel.org>

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH 0/6] spi: qcom-geni: Add QSPI support over GPI DMA
  2026-11-08 15:18 ` [PATCH 0/6] spi: qcom-geni: " Mark Brown
@ 2026-11-09  7:10   ` Jyothi Kumar Seerapu
  0 siblings, 0 replies; 12+ messages in thread
From: Jyothi Kumar Seerapu @ 2026-11-09  7:10 UTC (permalink / raw)
  To: Mark Brown
  Cc: Vinod Koul, Frank Li, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Andy Gross, Bjorn Andersson, Konrad Dybcio,
	linux-arm-msm, linux-spi, linux-kernel, dmaengine, devicetree,
	Krzysztof Kozlowski



On 11/8/2026 8:48 PM, Mark Brown wrote:
>> This series adds QSPI support to the Qualcomm GENI SPI driver using
>> GPI DMA. It supports single, dual and quad SDR transfers in controller
>> mode.
> 
> This doesn't apply against current code, please check and resend.
Hi Mark,
Thanks for pointing this out. The series was based on a slightly older 
linux-next snapshot. I’ll rebase it onto the current SPI for next 
branch, check for any missing dependencies, and resend the series as v2.

Thanks,
JyothiKumar>
>> ---
>> base-commit: 7079a12d7506b07fb53b54a664bfad5fa9b16d70
>> change-id: 20261021-qspi_upstream_series-850249e432f5
> 
> That's a random -next commit...
> 


^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH 2/6] dt-bindings: dma: qcom-gpi: Add QSPI protocol identifier
  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
  0 siblings, 0 replies; 12+ messages in thread
From: Krzysztof Kozlowski @ 2026-11-09 13:42 UTC (permalink / raw)
  To: Jyothi Kumar Seerapu
  Cc: Mark Brown, Vinod Koul, Frank Li, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Andy Gross, Bjorn Andersson,
	Konrad Dybcio, linux-arm-msm, linux-spi, linux-kernel, dmaengine,
	devicetree

On Fri, Nov 06, 2026 at 08:31:20PM +0530, Jyothi Kumar Seerapu wrote:
> Add QCOM_GPI_QSPI to identify QSPI (Quad SPI) channels in GPI DMA
> specifiers. Its value, 9, matches the GENI serial engine firmware
> protocol ID for QSPI.

What is GENI serial engine firmware? How does the Linux rely on it that
we add bindings for it?

Best regards,
Krzysztof


^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH 4/6] soc: qcom: geni-se: Define the QSPI firmware protocol
  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
  0 siblings, 0 replies; 12+ messages in thread
From: Krzysztof Kozlowski @ 2026-11-09 13:44 UTC (permalink / raw)
  To: Jyothi Kumar Seerapu
  Cc: Mark Brown, Vinod Koul, Frank Li, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Andy Gross, Bjorn Andersson,
	Konrad Dybcio, linux-arm-msm, linux-spi, linux-kernel, dmaengine,
	devicetree

On Fri, Nov 06, 2026 at 08:31:22PM +0530, Jyothi Kumar Seerapu wrote:
> Add GENI_SE_QSPI with firmware protocol ID 9. The GENI SPI driver uses
> this value to request QSPI firmware and validate the protocol running
> on a serial engine.
> 
> Extend the firmware loader's protocol-name table to accept QSPI.
> Reject unused protocol IDs in the sparse table before looking up
> firmware.
> 
> Signed-off-by: Jyothi Kumar Seerapu <jyothi.seerapu@oss.qualcomm.com>
> ---
>  drivers/soc/qcom/qcom-geni-se.c  | 7 +++++--
>  include/linux/soc/qcom/geni-se.h | 1 +
>  2 files changed, 6 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/soc/qcom/qcom-geni-se.c b/drivers/soc/qcom/qcom-geni-se.c
> index 873bfbd6b2b7..396f3d81d292 100644
> --- a/drivers/soc/qcom/qcom-geni-se.c
> +++ b/drivers/soc/qcom/qcom-geni-se.c
> @@ -122,7 +122,10 @@ struct geni_se_desc {
>  static const char * const icc_path_names[] = {"qup-core", "qup-config",
>  						"qup-memory"};
>  
> -static const char * const protocol_name[] = { "None", "SPI", "UART", "I2C", "I3C", "SPI SLAVE" };
> +static const char * const protocol_name[] = {
> +	"None", "SPI", "UART", "I2C", "I3C", "SPI SLAVE",
> +	[GENI_SE_QSPI] = "QSPI",

Why are you creating holes in this array?

Best regards,
Krzysztof


^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH 3/6] dt-bindings: spi: qcom,spi-geni-qcom: Document QSPI support
  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
  0 siblings, 0 replies; 12+ messages in thread
From: Krzysztof Kozlowski @ 2026-11-09 13:46 UTC (permalink / raw)
  To: Jyothi Kumar Seerapu
  Cc: Mark Brown, Vinod Koul, Frank Li, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Andy Gross, Bjorn Andersson,
	Konrad Dybcio, linux-arm-msm, linux-spi, linux-kernel, dmaengine,
	devicetree

On Fri, Nov 06, 2026 at 08:31:21PM +0530, Jyothi Kumar Seerapu wrote:
> Add the "qcom,geni-qspi" compatible to identify GENI Serial Engine
> instances configured for QSPI operation.
> 
> The compatible also allows the driver to select the appropriate protocol
> firmware when the Serial Engine is unconfigured (GENI_SE_INVALID_PROTO),
> distinguishing between SPI and QSPI firmware.
> 
> Signed-off-by: Jyothi Kumar Seerapu <jyothi.seerapu@oss.qualcomm.com>
> ---
>  Documentation/devicetree/bindings/spi/qcom,spi-geni-qcom.yaml | 8 +++++++-
>  1 file changed, 7 insertions(+), 1 deletion(-)

Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com>

Best regards,
Krzysztof


^ permalink raw reply	[flat|nested] 12+ messages in thread

end of thread, other threads:[~2026-11-09 13:46 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
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: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-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-06 15:01 ` [PATCH 5/6] dmaengine: qcom: gpi: Add QSPI protocol support Jyothi Kumar Seerapu
2026-11-06 15:01 ` [PATCH 6/6] spi: spi-geni-qcom: Add QSPI support over GPI DMA Jyothi Kumar Seerapu
2026-11-08 15:18 ` [PATCH 0/6] spi: qcom-geni: " Mark Brown
2026-11-09  7:10   ` Jyothi Kumar Seerapu

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®