]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
spi: cadence-quadspi: Implement refcount to handle unbind during busy
authorKhairul Anuar Romli <khairul.anuar.romli@altera.com>
Tue, 26 Aug 2025 00:33:58 +0000 (08:33 +0800)
committerMark Brown <broonie@kernel.org>
Mon, 1 Sep 2025 12:37:27 +0000 (13:37 +0100)
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.git.khairul.anuar.romli@altera.com
Signed-off-by: Mark Brown <broonie@kernel.org>
drivers/spi/spi-cadence-quadspi.c

index 177f9a33f3a25e41018b8b1b18152f1098201d7c..9bf823348cd30db53abb9c76117b0d5f674064af 100644 (file)
@@ -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(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);
 
@@ -1071,6 +1076,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);
 
@@ -1461,12 +1469,26 @@ static int cqspi_exec_mem_op(struct spi_mem *mem, const struct spi_mem_op *op)
        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_put_autosuspend(dev);
@@ -1474,6 +1496,9 @@ static int cqspi_exec_mem_op(struct spi_mem *mem, const struct spi_mem_op *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;
 }
 
@@ -1925,6 +1950,9 @@ static int cqspi_probe(struct platform_device *pdev)
                }
        }
 
+       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) {
@@ -1987,6 +2015,11 @@ static void 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);
+
        spi_unregister_controller(cqspi->host);
        cqspi_controller_enable(cqspi, 0);