[PATCH 2/2] spi: qcom-geni: Use GPIO to notify master of SPI target activity
From: Praveen Talari
Date: Thu Aug 06 2026 - 06:22:19 EST
When operating in SPI target mode, the GENI controller relies on an
external GPIO to notify the SPI master about the target's active state.
Add support for an optional device GPIO that is asserted when a target
transfer begins and deasserted when the transfer completes, is aborted,
or hits a timeout. This allows the target to explicitly signal its
availability to the master and ensures the GPIO is released in all error
and completion paths, preventing the master from observing a stale or
incorrect target-ready indication.
The GPIO is optional and requested only when target mode is enabled,
using devm_gpiod_get_index_optional() so targets without this signal
continue to work unchanged.
Signed-off-by: Praveen Talari <praveen.talari@xxxxxxxxxxxxxxxx>
---
drivers/spi/spi-geni-qcom.c | 25 +++++++++++++++++++++++--
1 file changed, 23 insertions(+), 2 deletions(-)
diff --git a/drivers/spi/spi-geni-qcom.c b/drivers/spi/spi-geni-qcom.c
index d262206ed663..ffb8871a55d8 100644
--- a/drivers/spi/spi-geni-qcom.c
+++ b/drivers/spi/spi-geni-qcom.c
@@ -8,6 +8,7 @@
#include <linux/dmaengine.h>
#include <linux/dma-mapping.h>
#include <linux/dma/qcom-gpi-dma.h>
+#include <linux/gpio/consumer.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/log2.h>
@@ -113,6 +114,7 @@ struct spi_geni_master {
struct dma_chan *rx;
int cur_xfer_mode;
const struct geni_spi_desc *dev_data;
+ struct gpio_desc *target_gpio;
};
static void spi_slv_setup(struct spi_geni_master *mas)
@@ -171,6 +173,9 @@ static void handle_se_timeout(struct spi_controller *spi)
xfer = mas->cur_xfer;
mas->cur_xfer = NULL;
+ if (spi->target && mas->target_gpio)
+ gpiod_set_value(mas->target_gpio, 0);
+
/* The controller doesn't support the Cancel commnand in target mode */
if (!spi->target) {
reinit_completion(&mas->cancel_done);
@@ -928,8 +933,12 @@ 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) {
ret = setup_se_xfer(xfer, mas, slv->mode, spi);
/* SPI framework expects +ve ret code to wait for transfer complete */
- if (!ret)
+ if (!ret) {
+ if (spi->target && mas->target_gpio)
+ gpiod_set_value(mas->target_gpio, 1);
ret = 1;
+ }
+
return ret;
}
return setup_gsi_xfer(xfer, mas, slv, spi);
@@ -969,6 +978,8 @@ static irqreturn_t geni_spi_isr(int irq, void *data)
if (mas->cur_xfer) {
spi_finalize_current_transfer(spi);
mas->cur_xfer = NULL;
+ if (spi->target && mas->target_gpio)
+ gpiod_set_value(mas->target_gpio, 0);
/*
* If this happens, then a CMD_DONE came before all the
* Tx buffer bytes were sent out. This is unusual, log
@@ -1012,6 +1023,8 @@ static irqreturn_t geni_spi_isr(int irq, void *data)
if (!mas->tx_rem_bytes && !mas->rx_rem_bytes && xfer) {
spi_finalize_current_transfer(spi);
mas->cur_xfer = NULL;
+ if (spi->target && mas->target_gpio)
+ gpiod_set_value(mas->target_gpio, 0);
}
}
@@ -1116,8 +1129,16 @@ static int spi_geni_probe(struct platform_device *pdev)
init_completion(&mas->rx_reset_done);
spin_lock_init(&mas->lock);
- if (spi->target)
+ if (spi->target) {
spi->target_abort = spi_geni_target_abort;
+ mas->target_gpio = devm_gpiod_get_index_optional(dev, "device", 0,
+ GPIOD_OUT_LOW);
+ if (IS_ERR(mas->target_gpio)) {
+ dev_err(dev, "Failed to request GPIO: %ld\n",
+ PTR_ERR(mas->target_gpio));
+ mas->target_gpio = NULL;
+ }
+ }
pm_runtime_use_autosuspend(&pdev->dev);
pm_runtime_set_autosuspend_delay(&pdev->dev, 250);
--
2.34.1