spi: fsl-qspi: use devm function instead of driver remove

Driver use devm APIs to manage clk/irq/resources and register the spi
controller, but the legacy remove function will be called first during
device detach and trigger kernel panic. Drop the remove function and use
devm_add_action_or_reset() for driver cleanup to ensure the release
sequence.

Trigger kernel panic on i.MX8MQ by
echo 30bb0000.spi >/sys/bus/platform/drivers/fsl-quadspi/unbind

Cc: stable@vger.kernel.org
Fixes: 8fcb830a00 ("spi: spi-fsl-qspi: use devm_spi_register_controller")
Reported-by: Kevin Hao <haokexin@gmail.com>
Signed-off-by: Han Xu <han.xu@nxp.com>
Reviewed-by: Frank Li <Frank.Li@nxp.com>
Link: https://patch.msgid.link/20250326224152.2147099-1-han.xu@nxp.com
Signed-off-by: Mark Brown <broonie@kernel.org>
This commit is contained in:
Han Xu 2025-03-26 17:41:51 -05:00 committed by Mark Brown
parent d32c4e5854
commit 40369bfe71
No known key found for this signature in database
GPG Key ID: 24D68B725D5487D0

View File

@ -844,6 +844,19 @@ static const struct spi_controller_mem_caps fsl_qspi_mem_caps = {
.per_op_freq = true, .per_op_freq = true,
}; };
static void fsl_qspi_cleanup(void *data)
{
struct fsl_qspi *q = data;
/* disable the hardware */
qspi_writel(q, QUADSPI_MCR_MDIS_MASK, q->iobase + QUADSPI_MCR);
qspi_writel(q, 0x0, q->iobase + QUADSPI_RSER);
fsl_qspi_clk_disable_unprep(q);
mutex_destroy(&q->lock);
}
static int fsl_qspi_probe(struct platform_device *pdev) static int fsl_qspi_probe(struct platform_device *pdev)
{ {
struct spi_controller *ctlr; struct spi_controller *ctlr;
@ -934,6 +947,10 @@ static int fsl_qspi_probe(struct platform_device *pdev)
ctlr->dev.of_node = np; ctlr->dev.of_node = np;
ret = devm_add_action_or_reset(dev, fsl_qspi_cleanup, q);
if (ret)
goto err_destroy_mutex;
ret = devm_spi_register_controller(dev, ctlr); ret = devm_spi_register_controller(dev, ctlr);
if (ret) if (ret)
goto err_destroy_mutex; goto err_destroy_mutex;
@ -953,19 +970,6 @@ static int fsl_qspi_probe(struct platform_device *pdev)
return ret; return ret;
} }
static void fsl_qspi_remove(struct platform_device *pdev)
{
struct fsl_qspi *q = platform_get_drvdata(pdev);
/* disable the hardware */
qspi_writel(q, QUADSPI_MCR_MDIS_MASK, q->iobase + QUADSPI_MCR);
qspi_writel(q, 0x0, q->iobase + QUADSPI_RSER);
fsl_qspi_clk_disable_unprep(q);
mutex_destroy(&q->lock);
}
static int fsl_qspi_suspend(struct device *dev) static int fsl_qspi_suspend(struct device *dev)
{ {
return 0; return 0;
@ -1003,7 +1007,6 @@ static struct platform_driver fsl_qspi_driver = {
.pm = &fsl_qspi_pm_ops, .pm = &fsl_qspi_pm_ops,
}, },
.probe = fsl_qspi_probe, .probe = fsl_qspi_probe,
.remove = fsl_qspi_remove,
}; };
module_platform_driver(fsl_qspi_driver); module_platform_driver(fsl_qspi_driver);