Linux SPI subsystem development
 help / color / mirror / Atom feed
* [PATCH] spi: rockchip: Fix runtime PM underflow on CS error paths
@ 2026-08-06 19:18 Alexander Shiyan
  0 siblings, 0 replies; only message in thread
From: Alexander Shiyan @ 2026-08-06 19:18 UTC (permalink / raw)
  To: linux-spi; +Cc: linux-rockchip, Mark Brown, Heiko Stuebner, Alexander Shiyan

The Rockchip SPI driver manages runtime PM reference counting around
chip select (CS) assertion: pm_runtime_get_sync() is called when CS is
asserted, and pm_runtime_put() when deasserted. This balanced pair is
expected.

However, when a transfer is aborted due to an error (e.g., power domain
timeout, DMA failure, or suspend/resume), the CS may remain in asserted
state while the transfer is terminated early. In such cases the
corresponding pm_runtime_put() is never called, leaving the usage count
artificially high. A later attempt to deassert CS (either explicitly or
during a new transfer) will call pm_runtime_put() without a prior get(),
triggering a "Runtime PM usage count underflow" warning and breaking
subsequent SPI operations.

Furthermore, the error handler may already have released the PM
reference and cleared the CS, but the SPI core may still call
rockchip_spi_set_cs() to deassert CS at message completion. Without
additional state tracking, this results in an extra pm_runtime_put()
and another underflow.

Add a cs_claimed flag to struct rockchip_spi to track whether the
runtime PM reference is currently held. Set it in the CS assertion path
after pm_runtime_get_sync(), clear it in deassertion path after
pm_runtime_put(). In error paths (rockchip_spi_handle_err() and
rockchip_spi_target_abort()), check this flag and release the reference
if still held. In rockchip_spi_set_cs() deassertion branch, only call
pm_runtime_put() if cs_claimed is true, preventing double decrement.
---
[root@diasom-evb ~]# rtcwake -s 15 -v -m freeze
rtcwake: assuming RTC uses UTC ...
Using UTC time.
        delta   = 37
        tzone   = 0
        tzname  = UTC
        systime = 1773421731, (UTC) Fri Mar 13 17:08:51 2026
        rtctime = 1773421694, (UTC) Fri Mar 13 17:08:14 2026
alarm 0, sys_time 1773421731, rtc_time 1773421694, seconds 15
rtcwake: wakeup from "freeze" using /dev/rtc0 at Fri Mar 13 17:08:30 2026
suspend mode: freeze; suspending system
[   28.951039] PM: suspend entry (s2idle)
[   28.957997] Filesystems sync: 0.006 seconds
[   28.963184] Freezing user space processes
[   28.966553] Freezing user space processes completed (elapsed 0.003 seconds)
[   28.967162] OOM killer disabled.
[   28.967464] Freezing remaining freezable tasks
[   28.968457] Freezing remaining freezable tasks completed (elapsed 0.000 seconds)
...
[   33.249429] spi_master spi2: Failed to power device: -13
[   33.249893] spi_master spi2: noqueue transfer failed
[   33.250331] spi_master spi2: Failed to power device: -13
[   33.250797] spi_master spi2: noqueue transfer failed
[   33.251230] rockchip-spi feb20000.spi: Runtime PM usage count underflow!
---

Signed-off-by: Alexander Shiyan <eagle.alexander923@gmail.com>
---
 drivers/spi/spi-rockchip.c | 22 ++++++++++++++++++++--
 1 file changed, 20 insertions(+), 2 deletions(-)

diff --git a/drivers/spi/spi-rockchip.c b/drivers/spi/spi-rockchip.c
index 231fbcf0e7aa..fb5795bc734d 100644
--- a/drivers/spi/spi-rockchip.c
+++ b/drivers/spi/spi-rockchip.c
@@ -191,6 +191,7 @@ struct rockchip_spi {
 	bool target_abort;
 	bool cs_inactive; /* spi target transmission stop when cs inactive */
 	bool cs_high_supported; /* native CS supports active-high polarity */
+	bool cs_claimed; /* runtime PM reference taken for CS */
 
 	struct spi_transfer *xfer; /* Store xfer temporarily */
 };
@@ -256,6 +257,7 @@ static void rockchip_spi_set_cs(struct spi_device *spi, bool enable)
 	if (cs_asserted) {
 		/* Keep things powered as long as CS is asserted */
 		pm_runtime_get_sync(rs->dev);
+		rs->cs_claimed = true;
 
 		if (spi_get_csgpiod(spi, 0))
 			ROCKCHIP_SPI_SET_BITS(rs->regs + ROCKCHIP_SPI_SER, 1);
@@ -263,14 +265,18 @@ static void rockchip_spi_set_cs(struct spi_device *spi, bool enable)
 			ROCKCHIP_SPI_SET_BITS(rs->regs + ROCKCHIP_SPI_SER,
 					      BIT(spi_get_chipselect(spi, 0)));
 	} else {
+		/* Clear CS first, then release PM */
 		if (spi_get_csgpiod(spi, 0))
 			ROCKCHIP_SPI_CLR_BITS(rs->regs + ROCKCHIP_SPI_SER, 1);
 		else
 			ROCKCHIP_SPI_CLR_BITS(rs->regs + ROCKCHIP_SPI_SER,
 					      BIT(spi_get_chipselect(spi, 0)));
 
-		/* Drop reference from when we first asserted CS */
-		pm_runtime_put(rs->dev);
+		/* Only release PM if we still hold the reference */
+		if (rs->cs_claimed) {
+			pm_runtime_put(rs->dev);
+			rs->cs_claimed = false;
+		}
 	}
 }
 
@@ -279,6 +285,12 @@ static void rockchip_spi_handle_err(struct spi_controller *ctlr,
 {
 	struct rockchip_spi *rs = spi_controller_get_devdata(ctlr);
 
+	/* If CS was claimed, release it to avoid PM underflow */
+	if (rs->cs_claimed) {
+		pm_runtime_put(rs->dev);
+		rs->cs_claimed = false;
+	}
+
 	/* stop running spi transfer
 	 * this also flushes both rx and tx fifos
 	 */
@@ -624,6 +636,12 @@ static int rockchip_spi_target_abort(struct spi_controller *ctlr)
 	struct dma_tx_state state;
 	enum dma_status status;
 
+	/* If CS was claimed, release it */
+	if (rs->cs_claimed) {
+		pm_runtime_put(rs->dev);
+		rs->cs_claimed = false;
+	}
+
 	/* Get current dma rx point */
 	if (atomic_read(&rs->state) & RXDMA) {
 		dmaengine_pause(ctlr->dma_rx);
-- 
2.52.0


^ permalink raw reply related	[flat|nested] only message in thread

only message in thread, other threads:[~2026-08-06 19:19 UTC | newest]

Thread overview: (only message) (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-06 19:18 [PATCH] spi: rockchip: Fix runtime PM underflow on CS error paths Alexander Shiyan

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