The GENI SPI driver reports various transfer failures such as command
timeouts, DMA reset timeouts, DMA transaction errors, and unexpected
interrupt conditions. However, diagnosing the root cause of these
failures is difficult as the hardware state is not captured when the
error occurs.

Add trace_geni_se_regs() calls at critical SPI error handling paths to
automatically capture GENI serial engine debug registers when failures
are detected. This includes:

- M_CMD abort/cancel timeout
- DMA TX/RX FSM reset timeout
- DMA transaction failures and pending residue conditions
- Unexpected interrupt error status
- Premature transfer completion with pending TX/RX data

Dumping the SE debug registers at the time of failure provides
additional hardware context and significantly improves post-mortem
analysis of SPI transfer issues without affecting normal operation.

Signed-off-by: Praveen Talari <[email protected]>
---
 drivers/spi/spi-geni-qcom.c | 22 ++++++++++++++++++----
 1 file changed, 18 insertions(+), 4 deletions(-)

diff --git a/drivers/spi/spi-geni-qcom.c b/drivers/spi/spi-geni-qcom.c
index 26e723cfea61..7db0836308c2 100644
--- a/drivers/spi/spi-geni-qcom.c
+++ b/drivers/spi/spi-geni-qcom.c
@@ -3,6 +3,7 @@
 
 #define CREATE_TRACE_POINTS
 #include <trace/events/qcom_geni_spi.h>
+#include <trace/events/qcom_geni_se.h>
 
 #include <linux/clk.h>
 #include <linux/dmaengine.h>
@@ -184,6 +185,7 @@ static void handle_se_timeout(struct spi_controller *spi)
        time_left = wait_for_completion_timeout(&mas->abort_done, HZ);
        if (!time_left) {
                dev_err(mas->dev, "Failed to cancel/abort m_cmd\n");
+               trace_geni_se_regs(se);
 
                /*
                 * No need for a lock since SPI core has a lock and we never
@@ -201,8 +203,10 @@ static void handle_se_timeout(struct spi_controller *spi)
                                writel(1, se->base + SE_DMA_TX_FSM_RST);
                                spin_unlock_irq(&mas->lock);
                                time_left = 
wait_for_completion_timeout(&mas->tx_reset_done, HZ);
-                               if (!time_left)
+                               if (!time_left) {
                                        dev_err(mas->dev, "DMA TX RESET 
failed\n");
+                                       trace_geni_se_regs(se);
+                               }
                        }
                        if (xfer->rx_buf) {
                                spin_lock_irq(&mas->lock);
@@ -210,8 +214,10 @@ static void handle_se_timeout(struct spi_controller *spi)
                                writel(1, se->base + SE_DMA_RX_FSM_RST);
                                spin_unlock_irq(&mas->lock);
                                time_left = 
wait_for_completion_timeout(&mas->rx_reset_done, HZ);
-                               if (!time_left)
+                               if (!time_left) {
                                        dev_err(mas->dev, "DMA RX RESET 
failed\n");
+                                       trace_geni_se_regs(se);
+                               }
                        }
                } else {
                        /*
@@ -382,10 +388,12 @@ static void
 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;
        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;
        }
@@ -395,6 +403,7 @@ spi_gsi_callback_result(void *cb, const struct 
dmaengine_result *result)
                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_finalize_current_transfer(spi);
@@ -941,8 +950,10 @@ static irqreturn_t geni_spi_isr(int irq, void *data)
 
        if (m_irq & (M_CMD_OVERRUN_EN | M_ILLEGAL_CMD_EN | M_CMD_FAILURE_EN |
                     M_RX_FIFO_RD_ERR_EN | M_RX_FIFO_WR_ERR_EN |
-                    M_TX_FIFO_RD_ERR_EN | M_TX_FIFO_WR_ERR_EN))
+                    M_TX_FIFO_RD_ERR_EN | M_TX_FIFO_WR_ERR_EN)) {
                dev_warn(mas->dev, "Unexpected IRQ err status %#010x\n", m_irq);
+               trace_geni_se_regs(se);
+       }
 
        spin_lock(&mas->lock);
 
@@ -974,10 +985,13 @@ static irqreturn_t geni_spi_isr(int irq, void *data)
                                        writel(0, se->base + 
SE_GENI_TX_WATERMARK_REG);
                                        dev_err(mas->dev, "Premature done. 
tx_rem = %d bpw%d\n",
                                                mas->tx_rem_bytes, 
mas->cur_bits_per_word);
+                                       trace_geni_se_regs(se);
                                }
-                               if (mas->rx_rem_bytes)
+                               if (mas->rx_rem_bytes) {
                                        dev_err(mas->dev, "Premature done. 
rx_rem = %d bpw%d\n",
                                                mas->rx_rem_bytes, 
mas->cur_bits_per_word);
+                                       trace_geni_se_regs(se);
+                               }
                        } else {
                                complete(&mas->cs_done);
                        }

-- 
2.34.1


Reply via email to