6.16-stable review patch. If anyone has any objections, please let me know.
------------------
From: Khairul Anuar Romli khairul.anuar.romli@altera.com
[ 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@altera.com Reviewed-by: Matthew Gerlach matthew.gerlach@altera.com Reviewed-by: Niravkumar L Rabara nirav.rabara@altera.com Link: https://patch.msgid.link/8704fd6bd2ff4d37bba4a0eacf5eba3ba001079e.1756168074... Signed-off-by: Mark Brown broonie@kernel.org Stable-dep-of: 30dbc1c8d50f ("spi: cadence-qspi: defer runtime support on socfpga if reset bit is enabled") Signed-off-by: Sasha Levin sashal@kernel.org Signed-off-by: Greg Kroah-Hartman gregkh@linuxfoundation.org --- drivers/spi/spi-cadence-quadspi.c | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+)
--- a/drivers/spi/spi-cadence-quadspi.c +++ b/drivers/spi/spi-cadence-quadspi.c @@ -108,6 +108,8 @@ struct cqspi_st {
bool is_jh7110; /* Flag for StarFive JH7110 SoC */ bool disable_stig_mode; + refcount_t refcount; + refcount_t inflight_ops;
const struct cqspi_driver_platdata *ddata; }; @@ -735,6 +737,9 @@ static int cqspi_indirect_read_execute(s 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);
@@ -1071,6 +1076,9 @@ static int cqspi_indirect_write_execute( 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);
@@ -1461,12 +1469,26 @@ static int cqspi_exec_mem_op(struct spi_ struct cqspi_st *cqspi = spi_controller_get_devdata(mem->spi->controller); struct device *dev = &cqspi->pdev->dev;
+ if (refcount_read(&cqspi->inflight_ops) == 0) + return -ENODEV; + ret = pm_runtime_resume_and_get(dev); if (ret) { dev_err(&mem->spi->dev, "resume failed with %d\n", ret); return ret; }
+ 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);
pm_runtime_mark_last_busy(dev); @@ -1475,6 +1497,9 @@ static int cqspi_exec_mem_op(struct spi_ 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; }
@@ -1926,6 +1951,9 @@ static int cqspi_probe(struct platform_d } }
+ 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) { @@ -1989,6 +2017,11 @@ static void cqspi_remove(struct platform { 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); + spi_unregister_controller(cqspi->host); cqspi_controller_enable(cqspi, 0);