[PATCH v16 1/7] spi: pxa2xx: introduce clock enable and disable helper functions
From: Shih-Yuan Lee
Date: Mon Jul 20 2026 - 12:55:31 EST
The driver disables the clock during PM runtime suspend, PM system
suspend, and device unbinding (remove). It also disables the clock
on various error unwinding paths in pxa2xx_spi_probe().
However, if the clock is already disabled (for example, if the device is
already runtime-suspended during driver unbinding), calling the common
clock framework's clk_disable_unprepare() again leads to clock prepare/enable
count underflows, generating kernel warnings.
Introduce pxa2xx_spi_clk_enable() and pxa2xx_spi_clk_disable() helper
functions that track the clock enable state using a new 'clk_enabled'
boolean flag in struct driver_data.
This ensures clk_disable_unprepare() is called only when the clock is
active, preventing clock underflows during suspend transitions and unbind.
It also allows the probe function to safely unwind resource allocations
without triggering clock underflows.
Signed-off-by: Shih-Yuan Lee <fourdollars@xxxxxxxxxx>
---
drivers/spi/spi-pxa2xx.c | 37 +++++++++++++++++++++++++++++--------
drivers/spi/spi-pxa2xx.h | 2 ++
2 files changed, 31 insertions(+), 8 deletions(-)
diff --git a/drivers/spi/spi-pxa2xx.c b/drivers/spi/spi-pxa2xx.c
index 6291d7c2e06f..d50152aad348 100644
--- a/drivers/spi/spi-pxa2xx.c
+++ b/drivers/spi/spi-pxa2xx.c
@@ -713,6 +713,28 @@ static void handle_bad_msg(struct driver_data *drv_data)
dev_err(drv_data->ssp->dev, "bad message state in interrupt handler\n");
}
+static int pxa2xx_spi_clk_enable(struct driver_data *drv_data)
+{
+ int ret;
+
+ if (drv_data->clk_enabled)
+ return 0;
+
+ ret = clk_prepare_enable(drv_data->ssp->clk);
+ if (ret == 0)
+ drv_data->clk_enabled = true;
+
+ return ret;
+}
+
+static void pxa2xx_spi_clk_disable(struct driver_data *drv_data)
+{
+ if (drv_data->clk_enabled) {
+ clk_disable_unprepare(drv_data->ssp->clk);
+ drv_data->clk_enabled = false;
+ }
+}
+
static irqreturn_t ssp_int(int irq, void *dev_id)
{
struct driver_data *drv_data = dev_id;
@@ -1352,7 +1374,7 @@ int pxa2xx_spi_probe(struct device *dev, struct ssp_device *ssp,
}
/* Enable SOC clock */
- status = clk_prepare_enable(ssp->clk);
+ status = pxa2xx_spi_clk_enable(drv_data);
if (status)
goto out_error_dma_irq_alloc;
@@ -1449,7 +1471,7 @@ int pxa2xx_spi_probe(struct device *dev, struct ssp_device *ssp,
return status;
out_error_clock_enabled:
- clk_disable_unprepare(ssp->clk);
+ pxa2xx_spi_clk_disable(drv_data);
out_error_dma_irq_alloc:
pxa2xx_spi_dma_release(drv_data);
@@ -1468,7 +1490,7 @@ void pxa2xx_spi_remove(struct device *dev)
/* Disable the SSP at the peripheral and SOC level */
pxa_ssp_disable(ssp);
- clk_disable_unprepare(ssp->clk);
+ pxa2xx_spi_clk_disable(drv_data);
/* Release DMA */
if (drv_data->controller_info->enable_dma)
@@ -1492,7 +1514,7 @@ static int pxa2xx_spi_suspend(struct device *dev)
pxa_ssp_disable(ssp);
if (!pm_runtime_suspended(dev))
- clk_disable_unprepare(ssp->clk);
+ pxa2xx_spi_clk_disable(drv_data);
return 0;
}
@@ -1500,12 +1522,11 @@ static int pxa2xx_spi_suspend(struct device *dev)
static int pxa2xx_spi_resume(struct device *dev)
{
struct driver_data *drv_data = dev_get_drvdata(dev);
- struct ssp_device *ssp = drv_data->ssp;
int status;
/* Enable the SSP clock */
if (!pm_runtime_suspended(dev)) {
- status = clk_prepare_enable(ssp->clk);
+ status = pxa2xx_spi_clk_enable(drv_data);
if (status)
return status;
}
@@ -1518,7 +1539,7 @@ static int pxa2xx_spi_runtime_suspend(struct device *dev)
{
struct driver_data *drv_data = dev_get_drvdata(dev);
- clk_disable_unprepare(drv_data->ssp->clk);
+ pxa2xx_spi_clk_disable(drv_data);
return 0;
}
@@ -1526,7 +1547,7 @@ static int pxa2xx_spi_runtime_resume(struct device *dev)
{
struct driver_data *drv_data = dev_get_drvdata(dev);
- return clk_prepare_enable(drv_data->ssp->clk);
+ return pxa2xx_spi_clk_enable(drv_data);
}
EXPORT_NS_GPL_DEV_PM_OPS(pxa2xx_spi_pm_ops, SPI_PXA2xx) = {
diff --git a/drivers/spi/spi-pxa2xx.h b/drivers/spi/spi-pxa2xx.h
index 447be0369384..820e573a3c60 100644
--- a/drivers/spi/spi-pxa2xx.h
+++ b/drivers/spi/spi-pxa2xx.h
@@ -72,6 +72,8 @@ struct driver_data {
void __iomem *lpss_base;
+ bool clk_enabled;
+
/* Optional slave FIFO ready signal */
struct gpio_desc *gpiod_ready;
};
--
2.39.5