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
