[PATCH 2/4] spi: core: fix sticky -EINVAL after pm_runtime_get_sync() failure
From: Praveen Talari
Date: Thu Jul 02 2026 - 02:10:22 EST
When pm_runtime_get_sync() fails, the RPM core leaves
power.runtime_error set to a non-zero value. Every subsequent call
to pm_runtime_get_sync() returns -EINVAL immediately without
attempting the resume, making the controller permanently unusable
until rebound.
Add pm_runtime_set_suspended() after the pm_runtime_put_noidle()
call in each get_sync() failure path. pm_runtime_set_suspended()
clears power.runtime_error and returns the device to the suspended
state, allowing the next transfer to retry the resume.
Symptoms observed:
spi_master spi2: Failed to power device: -110
spi_master spi2: noqueue transfer failed
geni_spi 988000.spi: Runtime PM usage count underflow!
spi_master spi2: Failed to power device: -22
spi_master spi2: noqueue transfer failed
The -110 is the initial resume timeout; the -22 (EINVAL) on every
subsequent transfer is the sticky runtime_error; the underflow is
from pm_runtime_put() called against an already-balanced count after
the unchecked get_sync failure in the driver.
Signed-off-by: Praveen Talari <praveen.talari@xxxxxxxxxxxxxxxx>
---
drivers/spi/spi.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c
index f897789a44d1..8d5c8d7c5a1a 100644
--- a/drivers/spi/spi.c
+++ b/drivers/spi/spi.c
@@ -1746,6 +1746,7 @@ static int __spi_pump_transfer_message(struct spi_controller *ctlr,
ret = pm_runtime_get_sync(ctlr->dev.parent);
if (ret < 0) {
pm_runtime_put_noidle(ctlr->dev.parent);
+ pm_runtime_set_suspended(ctlr->dev.parent);
dev_err(&ctlr->dev, "Failed to power device: %d\n",
ret);
@@ -4016,6 +4017,7 @@ static int spi_set_cs_timing(struct spi_device *spi)
status = pm_runtime_get_sync(parent);
if (status < 0) {
pm_runtime_put_noidle(parent);
+ pm_runtime_set_suspended(parent);
dev_err(&spi->controller->dev, "Failed to power device: %d\n",
status);
return status;
@@ -4121,6 +4123,7 @@ static int __spi_setup(struct spi_device *spi, bool initial_setup)
if (spi->controller->auto_runtime_pm && spi->controller->set_cs) {
status = pm_runtime_resume_and_get(spi->controller->dev.parent);
if (status < 0) {
+ pm_runtime_set_suspended(spi->controller->dev.parent);
mutex_unlock(&spi->controller->io_mutex);
dev_err(&spi->controller->dev, "Failed to power device: %d\n",
status);
--
2.34.1