[PATCH 5.15.y] spi: cadence-quadspi: Implement refcount to handle unbind during busy

From: Robert Garcia

Date: Mon Mar 02 2026 - 01:48:17 EST


From: Khairul Anuar Romli <khairul.anuar.romli@xxxxxxxxxx>

[ Upstream commit 7446284023e8ef694fb392348185349c773eefb3 ]

driver support indirect read and indirect write operation with
assumption no force device removal(unbind) operation. However
force device removal(removal) is still available to root superuser.

Unbinding driver during operation causes kernel crash. This changes
ensure driver able to handle such operation for indirect read and
indirect write by implementing refcount to track attached devices
to the controller and gracefully wait and until attached devices
remove operation completed before proceed with removal operation.

Signed-off-by: Khairul Anuar Romli <khairul.anuar.romli@xxxxxxxxxx>
Reviewed-by: Matthew Gerlach <matthew.gerlach@xxxxxxxxxx>
Reviewed-by: Niravkumar L Rabara <nirav.rabara@xxxxxxxxxx>
Link: https://patch.msgid.link/8704fd6bd2ff4d37bba4a0eacf5eba3ba001079e.1756168074.git.khairul.anuar.romli@xxxxxxxxxx
Signed-off-by: Mark Brown <broonie@xxxxxxxxxx>
[Add cqspi defination in cqspi_exec_mem_op and minor context change fixed.]
Signed-off-by: Robert Garcia <rob_garcia@xxxxxxx>
---
drivers/spi/spi-cadence-quadspi.c | 34 +++++++++++++++++++++++++++++++
1 file changed, 34 insertions(+)

diff --git a/drivers/spi/spi-cadence-quadspi.c b/drivers/spi/spi-cadence-quadspi.c
index 91fdc9132b96..260d4a5848dd 100644
--- a/drivers/spi/spi-cadence-quadspi.c
+++ b/drivers/spi/spi-cadence-quadspi.c
@@ -85,6 +85,8 @@ struct cqspi_st {
bool use_direct_mode;
struct cqspi_flash_pdata f_pdata[CQSPI_MAX_CHIPSELECT];
bool wr_completion;
+ refcount_t refcount;
+ refcount_t inflight_ops;
};

struct cqspi_driver_platdata {
@@ -684,6 +686,9 @@ static int cqspi_indirect_read_execute(struct cqspi_flash_pdata *f_pdata,
u8 *rxbuf_end = rxbuf + n_rx;
int ret = 0;

+ if (!refcount_read(&cqspi->refcount))
+ return -ENODEV;
+
writel(from_addr, reg_base + CQSPI_REG_INDIRECTRDSTARTADDR);
writel(remaining, reg_base + CQSPI_REG_INDIRECTRDBYTES);

@@ -826,6 +831,9 @@ static int cqspi_indirect_write_execute(struct cqspi_flash_pdata *f_pdata,
unsigned int write_bytes;
int ret;

+ if (!refcount_read(&cqspi->refcount))
+ return -ENODEV;
+
writel(to_addr, reg_base + CQSPI_REG_INDIRECTWRSTARTADDR);
writel(remaining, reg_base + CQSPI_REG_INDIRECTWRBYTES);

@@ -1210,11 +1218,29 @@ static int cqspi_mem_process(struct spi_mem *mem, const struct spi_mem_op *op)
static int cqspi_exec_mem_op(struct spi_mem *mem, const struct spi_mem_op *op)
{
int ret;
+ struct cqspi_st *cqspi = spi_controller_get_devdata(mem->spi->controller);
+
+ if (refcount_read(&cqspi->inflight_ops) == 0)
+ return -ENODEV;
+
+ if (!refcount_read(&cqspi->refcount))
+ return -EBUSY;
+
+ refcount_inc(&cqspi->inflight_ops);
+
+ if (!refcount_read(&cqspi->refcount)) {
+ if (refcount_read(&cqspi->inflight_ops))
+ refcount_dec(&cqspi->inflight_ops);
+ return -EBUSY;
+ }

ret = cqspi_mem_process(mem, op);
if (ret)
dev_err(&mem->spi->dev, "operation failed with %d\n", ret);

+ if (refcount_read(&cqspi->inflight_ops) > 1)
+ refcount_dec(&cqspi->inflight_ops);
+
return ret;
}

@@ -1564,6 +1590,9 @@ static int cqspi_probe(struct platform_device *pdev)
cqspi->wr_completion = false;
}

+ refcount_set(&cqspi->refcount, 1);
+ refcount_set(&cqspi->inflight_ops, 1);
+
ret = devm_request_irq(dev, irq, cqspi_irq_handler, 0,
pdev->name, cqspi);
if (ret) {
@@ -1613,6 +1642,11 @@ static int cqspi_remove(struct platform_device *pdev)
{
struct cqspi_st *cqspi = platform_get_drvdata(pdev);

+ refcount_set(&cqspi->refcount, 0);
+
+ if (!refcount_dec_and_test(&cqspi->inflight_ops))
+ cqspi_wait_idle(cqspi);
+
cqspi_controller_enable(cqspi, 0);

if (cqspi->rx_chan)
--
2.34.1