diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2026-04-24 13:16:36 -0700 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2026-04-24 13:16:36 -0700 |
| commit | b2680ba4a2ad259c7bbd856ed830b459e11d88ba (patch) | |
| tree | 4d90dbf641d88509cbb27e82dd92bad5294f4ce6 | |
| parent | f64399836125c8512f8a3fe72c1719795a9c5812 (diff) | |
| parent | 16ab65df5d867961a79cef366cdb33f09ebda603 (diff) | |
Merge tag 'spi-fix-v7.1-merge-window' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi
Pull spi fixes from Mark Brown:
"This is quite a big set of fixes, almost all from Johan Hovold who is
on an ongoing quest to clean up issues with probe and removal handling
in drivers.
There isn't anything too concerning here especially with the
deregistration stuff which will very rarely get run in production
systems since this is all platform devices in the SoC on embedded
hardware, but it's all real issues which should be fixed. There's more
in flight here.
We also have a few other minor fixes, one from Felix Gu along the same
lines as Johan's work and a couple of documentation things"
* tag 'spi-fix-v7.1-merge-window' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi: (23 commits)
spi: fix controller cleanup() documentation
spi: fix resource leaks on device setup failure
spi: axiado: clean up probe return value
spi: axiado: rename probe error labels
spi: axiado: fix runtime pm imbalance on probe failure
spi: orion: clean up probe return value
spi: orion: fix clock imbalance on registration failure
spi: orion: fix runtime pm leak on unbind
spi: imx: fix runtime pm leak on probe deferral
spi: mpc52xx: fix use-after-free on registration failure
spi: Fix the error description in the `ptp_sts_word_post` comment
spi: topcliff-pch: fix use-after-free on unbind
spi: topcliff-pch: fix controller deregistration
spi: orion: fix controller deregistration
spi: mxic: fix controller deregistration
spi: mpc52xx: fix use-after-free on unbind
spi: mpc52xx: fix controller deregistration
spi: cadence-quadspi: fix controller deregistration
spi: cadence: fix controller deregistration
spi: mtk-snfi: fix memory leak in probe
...
| -rw-r--r-- | Documentation/devicetree/bindings/spi/fsl,spi.yaml | 6 | ||||
| -rw-r--r-- | MAINTAINERS | 4 | ||||
| -rw-r--r-- | drivers/spi/spi-axiado.c | 31 | ||||
| -rw-r--r-- | drivers/spi/spi-cadence-quadspi.c | 4 | ||||
| -rw-r--r-- | drivers/spi/spi-cadence.c | 6 | ||||
| -rw-r--r-- | drivers/spi/spi-imx.c | 1 | ||||
| -rw-r--r-- | drivers/spi/spi-mpc52xx.c | 9 | ||||
| -rw-r--r-- | drivers/spi/spi-mtk-snfi.c | 8 | ||||
| -rw-r--r-- | drivers/spi/spi-mxic.c | 3 | ||||
| -rw-r--r-- | drivers/spi/spi-orion.c | 20 | ||||
| -rw-r--r-- | drivers/spi/spi-topcliff-pch.c | 11 | ||||
| -rw-r--r-- | drivers/spi/spi.c | 61 | ||||
| -rw-r--r-- | include/linux/spi/spi.h | 4 |
13 files changed, 110 insertions, 58 deletions
diff --git a/Documentation/devicetree/bindings/spi/fsl,spi.yaml b/Documentation/devicetree/bindings/spi/fsl,spi.yaml index d74792fc9bf2..6a359488dd41 100644 --- a/Documentation/devicetree/bindings/spi/fsl,spi.yaml +++ b/Documentation/devicetree/bindings/spi/fsl,spi.yaml @@ -59,7 +59,7 @@ unevaluatedProperties: false examples: - | - #include <dt-bindings/interrupt-controller/irq.h> + #include <dt-bindings/gpio/gpio.h> spi@4c0 { compatible = "fsl,spi"; @@ -67,8 +67,8 @@ examples: cell-index = <0>; interrupts = <82 0>; mode = "cpu"; - cs-gpios = <&gpio 18 IRQ_TYPE_EDGE_RISING // device reg=<0> - &gpio 19 IRQ_TYPE_EDGE_RISING>; // device reg=<1> + cs-gpios = <&gpio 18 GPIO_ACTIVE_HIGH>, // device reg=<0> + <&gpio 19 GPIO_ACTIVE_HIGH>; // device reg=<1> }; ... diff --git a/MAINTAINERS b/MAINTAINERS index 6b456089ef40..9922adc1f493 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -24655,8 +24655,8 @@ F: Documentation/devicetree/bindings/net/socionext,synquacer-netsec.yaml F: drivers/net/ethernet/socionext/netsec.c SOCIONEXT (SNI) Synquacer SPI DRIVER -M: Masahisa Kojima <masahisa.kojima@linaro.org> -M: Jassi Brar <jaswinder.singh@linaro.org> +M: Masahisa Kojima <kojima.masahisa@socionext.com> +M: Jassi Brar <jassisinghbrar@gmail.com> L: linux-spi@vger.kernel.org S: Maintained F: Documentation/devicetree/bindings/spi/socionext,synquacer-spi.yaml diff --git a/drivers/spi/spi-axiado.c b/drivers/spi/spi-axiado.c index dc55c55ae63c..9057a0a8df4a 100644 --- a/drivers/spi/spi-axiado.c +++ b/drivers/spi/spi-axiado.c @@ -751,9 +751,9 @@ static const struct spi_controller_mem_ops ax_spi_mem_ops = { */ static int ax_spi_probe(struct platform_device *pdev) { - int ret = 0, irq; struct spi_controller *ctlr; struct ax_spi *xspi; + int ret, irq; u32 num_cs; ctlr = devm_spi_alloc_host(&pdev->dev, sizeof(*xspi)); @@ -785,7 +785,7 @@ static int ax_spi_probe(struct platform_device *pdev) ret = clk_prepare_enable(xspi->ref_clk); if (ret) { dev_err(&pdev->dev, "Unable to enable device clock.\n"); - goto clk_dis_apb; + goto err_disable_apb; } pm_runtime_use_autosuspend(&pdev->dev); @@ -815,7 +815,7 @@ static int ax_spi_probe(struct platform_device *pdev) irq = platform_get_irq(pdev, 0); if (irq <= 0) { ret = -ENXIO; - goto clk_dis_all; + goto err_disable_rpm; } ret = devm_request_irq(&pdev->dev, irq, ax_spi_irq, @@ -823,7 +823,7 @@ static int ax_spi_probe(struct platform_device *pdev) if (ret != 0) { ret = -ENXIO; dev_err(&pdev->dev, "request_irq failed\n"); - goto clk_dis_all; + goto err_disable_rpm; } ctlr->use_gpio_descriptors = true; @@ -842,23 +842,26 @@ static int ax_spi_probe(struct platform_device *pdev) ctlr->bits_per_word_mask = SPI_BPW_MASK(8); - pm_runtime_put_autosuspend(&pdev->dev); - ctlr->mem_ops = &ax_spi_mem_ops; ret = spi_register_controller(ctlr); if (ret) { dev_err(&pdev->dev, "spi_register_controller failed\n"); - goto clk_dis_all; + goto err_disable_rpm; } - return ret; + pm_runtime_put_autosuspend(&pdev->dev); -clk_dis_all: - pm_runtime_set_suspended(&pdev->dev); + return 0; + +err_disable_rpm: pm_runtime_disable(&pdev->dev); + pm_runtime_put_noidle(&pdev->dev); + pm_runtime_set_suspended(&pdev->dev); + pm_runtime_dont_use_autosuspend(&pdev->dev); + clk_disable_unprepare(xspi->ref_clk); -clk_dis_apb: +err_disable_apb: clk_disable_unprepare(xspi->pclk); return ret; @@ -877,10 +880,14 @@ static void ax_spi_remove(struct platform_device *pdev) struct spi_controller *ctlr = platform_get_drvdata(pdev); struct ax_spi *xspi = spi_controller_get_devdata(ctlr); + pm_runtime_get_sync(&pdev->dev); + spi_unregister_controller(ctlr); - pm_runtime_set_suspended(&pdev->dev); pm_runtime_disable(&pdev->dev); + pm_runtime_put_noidle(&pdev->dev); + pm_runtime_set_suspended(&pdev->dev); + pm_runtime_dont_use_autosuspend(&pdev->dev); clk_disable_unprepare(xspi->ref_clk); clk_disable_unprepare(xspi->pclk); diff --git a/drivers/spi/spi-cadence-quadspi.c b/drivers/spi/spi-cadence-quadspi.c index b6f7f95e8bd3..65aff2e70265 100644 --- a/drivers/spi/spi-cadence-quadspi.c +++ b/drivers/spi/spi-cadence-quadspi.c @@ -2016,13 +2016,13 @@ static void cqspi_remove(struct platform_device *pdev) ddata = of_device_get_match_data(dev); + spi_unregister_controller(cqspi->host); + refcount_set(&cqspi->refcount, 0); if (!refcount_dec_and_test(&cqspi->inflight_ops)) cqspi_wait_idle(cqspi); - spi_unregister_controller(cqspi->host); - if (cqspi->rx_chan) dma_release_channel(cqspi->rx_chan); diff --git a/drivers/spi/spi-cadence.c b/drivers/spi/spi-cadence.c index caa7a57e6d27..08d7dabe818d 100644 --- a/drivers/spi/spi-cadence.c +++ b/drivers/spi/spi-cadence.c @@ -777,6 +777,10 @@ static void cdns_spi_remove(struct platform_device *pdev) struct spi_controller *ctlr = platform_get_drvdata(pdev); struct cdns_spi *xspi = spi_controller_get_devdata(ctlr); + spi_controller_get(ctlr); + + spi_unregister_controller(ctlr); + cdns_spi_write(xspi, CDNS_SPI_ER, CDNS_SPI_ER_DISABLE); if (!spi_controller_is_target(ctlr)) { @@ -784,7 +788,7 @@ static void cdns_spi_remove(struct platform_device *pdev) pm_runtime_set_suspended(&pdev->dev); } - spi_unregister_controller(ctlr); + spi_controller_put(ctlr); } /** diff --git a/drivers/spi/spi-imx.c b/drivers/spi/spi-imx.c index 4747899e0646..e5c907c45b87 100644 --- a/drivers/spi/spi-imx.c +++ b/drivers/spi/spi-imx.c @@ -2373,6 +2373,7 @@ out_register_controller: out_runtime_pm_put: pm_runtime_dont_use_autosuspend(spi_imx->dev); pm_runtime_disable(spi_imx->dev); + pm_runtime_put_noidle(spi_imx->dev); pm_runtime_set_suspended(&pdev->dev); clk_disable_unprepare(spi_imx->clk_ipg); diff --git a/drivers/spi/spi-mpc52xx.c b/drivers/spi/spi-mpc52xx.c index 05bbd3795e7d..924d820448fb 100644 --- a/drivers/spi/spi-mpc52xx.c +++ b/drivers/spi/spi-mpc52xx.c @@ -498,6 +498,9 @@ static int mpc52xx_spi_probe(struct platform_device *op) err_register: dev_err(&ms->host->dev, "initialization failed\n"); + free_irq(ms->irq0, ms); + free_irq(ms->irq1, ms); + cancel_work_sync(&ms->work); err_gpio: while (i-- > 0) gpiod_put(ms->gpio_cs[i]); @@ -517,15 +520,17 @@ static void mpc52xx_spi_remove(struct platform_device *op) struct mpc52xx_spi *ms = spi_controller_get_devdata(host); int i; - cancel_work_sync(&ms->work); + spi_unregister_controller(host); + free_irq(ms->irq0, ms); free_irq(ms->irq1, ms); + cancel_work_sync(&ms->work); + for (i = 0; i < ms->gpio_cs_count; i++) gpiod_put(ms->gpio_cs[i]); kfree(ms->gpio_cs); - spi_unregister_controller(host); iounmap(ms->regs); spi_controller_put(host); } diff --git a/drivers/spi/spi-mtk-snfi.c b/drivers/spi/spi-mtk-snfi.c index 73fa84475f0e..e616e6800e92 100644 --- a/drivers/spi/spi-mtk-snfi.c +++ b/drivers/spi/spi-mtk-snfi.c @@ -1447,14 +1447,14 @@ static int mtk_snand_probe(struct platform_device *pdev) ret = nand_ecc_register_on_host_hw_engine(&ms->ecc_eng); if (ret) { dev_err(&pdev->dev, "failed to register ecc engine.\n"); - goto release_ecc; + goto free_buf; } ret = devm_add_action_or_reset(&pdev->dev, mtk_unregister_ecc_engine, &ms->ecc_eng); if (ret) { dev_err_probe(&pdev->dev, ret, "failed to add ECC unregister action\n"); - goto release_ecc; + goto free_buf; } ctlr->num_chipselect = 1; @@ -1465,10 +1465,12 @@ static int mtk_snand_probe(struct platform_device *pdev) ret = spi_register_controller(ctlr); if (ret) { dev_err(&pdev->dev, "spi_register_controller failed.\n"); - goto release_ecc; + goto free_buf; } return 0; +free_buf: + kfree(ms->buf); release_ecc: mtk_ecc_release(ms->ecc); return ret; diff --git a/drivers/spi/spi-mxic.c b/drivers/spi/spi-mxic.c index f9369c69911c..b0e7fc828a50 100644 --- a/drivers/spi/spi-mxic.c +++ b/drivers/spi/spi-mxic.c @@ -832,9 +832,10 @@ static void mxic_spi_remove(struct platform_device *pdev) struct spi_controller *host = platform_get_drvdata(pdev); struct mxic_spi *mxic = spi_controller_get_devdata(host); + spi_unregister_controller(host); + pm_runtime_disable(&pdev->dev); mxic_spi_mem_ecc_remove(mxic); - spi_unregister_controller(host); } static const struct of_device_id mxic_spi_of_ids[] = { diff --git a/drivers/spi/spi-orion.c b/drivers/spi/spi-orion.c index 7a2186b51b4c..64bf215c1804 100644 --- a/drivers/spi/spi-orion.c +++ b/drivers/spi/spi-orion.c @@ -648,8 +648,8 @@ static int orion_spi_probe(struct platform_device *pdev) struct orion_spi *spi; struct resource *r; unsigned long tclk_hz; - int status = 0; struct device_node *np; + int status; host = spi_alloc_host(&pdev->dev, sizeof(*spi)); if (host == NULL) { @@ -774,6 +774,7 @@ static int orion_spi_probe(struct platform_device *pdev) pm_runtime_set_active(&pdev->dev); pm_runtime_use_autosuspend(&pdev->dev); pm_runtime_set_autosuspend_delay(&pdev->dev, SPI_AUTOSUSPEND_TIMEOUT); + pm_runtime_get_noresume(&pdev->dev); pm_runtime_enable(&pdev->dev); status = orion_spi_reset(spi); @@ -784,10 +785,15 @@ static int orion_spi_probe(struct platform_device *pdev) if (status < 0) goto out_rel_pm; - return status; + pm_runtime_put_autosuspend(&pdev->dev); + + return 0; out_rel_pm: pm_runtime_disable(&pdev->dev); + pm_runtime_put_noidle(&pdev->dev); + pm_runtime_set_suspended(&pdev->dev); + pm_runtime_dont_use_autosuspend(&pdev->dev); out_rel_axi_clk: clk_disable_unprepare(spi->axi_clk); out: @@ -801,11 +807,19 @@ static void orion_spi_remove(struct platform_device *pdev) struct spi_controller *host = platform_get_drvdata(pdev); struct orion_spi *spi = spi_controller_get_devdata(host); + spi_controller_get(host); + + spi_unregister_controller(host); + pm_runtime_get_sync(&pdev->dev); clk_disable_unprepare(spi->axi_clk); - spi_unregister_controller(host); + spi_controller_put(host); + pm_runtime_disable(&pdev->dev); + pm_runtime_put_noidle(&pdev->dev); + pm_runtime_set_suspended(&pdev->dev); + pm_runtime_dont_use_autosuspend(&pdev->dev); } MODULE_ALIAS("platform:" DRIVER_NAME); diff --git a/drivers/spi/spi-topcliff-pch.c b/drivers/spi/spi-topcliff-pch.c index cae2dcefabea..14d11450e86d 100644 --- a/drivers/spi/spi-topcliff-pch.c +++ b/drivers/spi/spi-topcliff-pch.c @@ -1406,8 +1406,9 @@ static void pch_spi_pd_remove(struct platform_device *plat_dev) dev_dbg(&plat_dev->dev, "%s:[ch%d] irq=%d\n", __func__, plat_dev->id, board_dat->pdev->irq); - if (use_dma) - pch_free_dma_buf(board_dat, data); + spi_controller_get(data->host); + + spi_unregister_controller(data->host); /* check for any pending messages; no action is taken if the queue * is still full; but at least we tried. Unload anyway */ @@ -1432,8 +1433,12 @@ static void pch_spi_pd_remove(struct platform_device *plat_dev) free_irq(board_dat->pdev->irq, data); } + if (use_dma) + pch_free_dma_buf(board_dat, data); + pci_iounmap(board_dat->pdev, data->io_remap_addr); - spi_unregister_controller(data->host); + + spi_controller_put(data->host); } #ifdef CONFIG_PM static int pch_spi_pd_suspend(struct platform_device *pd_dev, diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 7001f5dce8bd..104279858f56 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -43,6 +43,8 @@ EXPORT_TRACEPOINT_SYMBOL(spi_transfer_stop); #include "internals.h" +static int __spi_setup(struct spi_device *spi, bool initial_setup); + static DEFINE_IDR(spi_controller_idr); static void spidev_release(struct device *dev) @@ -743,7 +745,7 @@ static int __spi_add_device(struct spi_device *spi, struct spi_device *parent) * normally rely on the device being setup. Devices * using SPI_CS_HIGH can't coexist well otherwise... */ - status = spi_setup(spi); + status = __spi_setup(spi, true); if (status < 0) { dev_err(dev, "can't setup %s, status %d\n", dev_name(&spi->dev), status); @@ -4049,27 +4051,7 @@ static int spi_set_cs_timing(struct spi_device *spi) return status; } -/** - * spi_setup - setup SPI mode and clock rate - * @spi: the device whose settings are being modified - * Context: can sleep, and no requests are queued to the device - * - * SPI protocol drivers may need to update the transfer mode if the - * device doesn't work with its default. They may likewise need - * to update clock rates or word sizes from initial values. This function - * changes those settings, and must be called from a context that can sleep. - * Except for SPI_CS_HIGH, which takes effect immediately, the changes take - * effect the next time the device is selected and data is transferred to - * or from it. When this function returns, the SPI device is deselected. - * - * Note that this call will fail if the protocol driver specifies an option - * that the underlying controller or its driver does not support. For - * example, not all hardware supports wire transfers using nine bit words, - * LSB-first wire encoding, or active-high chipselects. - * - * Return: zero on success, else a negative error code. - */ -int spi_setup(struct spi_device *spi) +static int __spi_setup(struct spi_device *spi, bool initial_setup) { unsigned bad_bits, ugly_bits; int status; @@ -4154,7 +4136,7 @@ int spi_setup(struct spi_device *spi) status = spi_set_cs_timing(spi); if (status) { mutex_unlock(&spi->controller->io_mutex); - return status; + goto err_cleanup; } if (spi->controller->auto_runtime_pm && spi->controller->set_cs) { @@ -4163,7 +4145,7 @@ int spi_setup(struct spi_device *spi) mutex_unlock(&spi->controller->io_mutex); dev_err(&spi->controller->dev, "Failed to power device: %d\n", status); - return status; + goto err_cleanup; } /* @@ -4199,6 +4181,37 @@ int spi_setup(struct spi_device *spi) status); return status; + +err_cleanup: + if (initial_setup) + spi_cleanup(spi); + + return status; +} + +/** + * spi_setup - setup SPI mode and clock rate + * @spi: the device whose settings are being modified + * Context: can sleep, and no requests are queued to the device + * + * SPI protocol drivers may need to update the transfer mode if the + * device doesn't work with its default. They may likewise need + * to update clock rates or word sizes from initial values. This function + * changes those settings, and must be called from a context that can sleep. + * Except for SPI_CS_HIGH, which takes effect immediately, the changes take + * effect the next time the device is selected and data is transferred to + * or from it. When this function returns, the SPI device is deselected. + * + * Note that this call will fail if the protocol driver specifies an option + * that the underlying controller or its driver does not support. For + * example, not all hardware supports wire transfers using nine bit words, + * LSB-first wire encoding, or active-high chipselects. + * + * Return: zero on success, else a negative error code. + */ +int spi_setup(struct spi_device *spi) +{ + return __spi_setup(spi, false); } EXPORT_SYMBOL_GPL(spi_setup); diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h index 7587b1c5d7ec..79513f5941cc 100644 --- a/include/linux/spi/spi.h +++ b/include/linux/spi/spi.h @@ -701,7 +701,7 @@ struct spi_controller { int (*transfer)(struct spi_device *spi, struct spi_message *mesg); - /* Called on release() to free memory provided by spi_controller */ + /* Called on deregistration to free memory provided by spi_controller */ void (*cleanup)(struct spi_device *spi); /* @@ -1019,7 +1019,7 @@ struct spi_res { * this value may have changed compared to what was requested, depending * on the available snapshotting resolution (DMA transfer, * @ptp_sts_supported is false, etc). - * @ptp_sts_word_post: See @ptp_sts_word_post. The two can be equal (meaning + * @ptp_sts_word_post: See @ptp_sts_word_pre. The two can be equal (meaning * that a single byte should be snapshotted). * If the core takes care of the timestamp (if @ptp_sts_supported is false * for this controller), it will set @ptp_sts_word_pre to 0, and |
