From 76645c1fd682c9df746c3f7c149b868ceff914fb Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 23 Apr 2026 09:58:01 +0200 Subject: [PATCH 001/118] spi: mpc52xx: clean up interrupt handling The driver is relying on the assumption that the invalid interrupt 0 can be freed without any side effects, but that is not the case on architectures like x86 where it would trigger a warning about freeing an already free interrupt. This should not cause any trouble on powerpc where this driver is used, but make the code more portable (and obviously correct) by making sure that the interrupts have been requested before freeing them. Signed-off-by: Johan Hovold Link: https://patch.msgid.link/20260423075801.2252318-1-johan@kernel.org Signed-off-by: Mark Brown --- drivers/spi/spi-mpc52xx.c | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/drivers/spi/spi-mpc52xx.c b/drivers/spi/spi-mpc52xx.c index 924d820448fb..04c2270cd2cf 100644 --- a/drivers/spi/spi-mpc52xx.c +++ b/drivers/spi/spi-mpc52xx.c @@ -472,13 +472,15 @@ static int mpc52xx_spi_probe(struct platform_device *op) if (ms->irq0 && ms->irq1) { rc = request_irq(ms->irq0, mpc52xx_spi_irq, 0, "mpc5200-spi-modf", ms); - rc |= request_irq(ms->irq1, mpc52xx_spi_irq, 0, - "mpc5200-spi-spif", ms); - if (rc) { - free_irq(ms->irq0, ms); - free_irq(ms->irq1, ms); - ms->irq0 = ms->irq1 = 0; + if (rc == 0) { + rc = request_irq(ms->irq1, mpc52xx_spi_irq, 0, + "mpc5200-spi-spif", ms); + if (rc) + free_irq(ms->irq0, ms); } + + if (rc) + ms->irq0 = ms->irq1 = 0; } else { /* operate in polled mode */ ms->irq0 = ms->irq1 = 0; @@ -498,8 +500,10 @@ 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); + if (ms->irq0) { + free_irq(ms->irq0, ms); + free_irq(ms->irq1, ms); + } cancel_work_sync(&ms->work); err_gpio: while (i-- > 0) @@ -522,8 +526,10 @@ static void mpc52xx_spi_remove(struct platform_device *op) spi_unregister_controller(host); - free_irq(ms->irq0, ms); - free_irq(ms->irq1, ms); + if (ms->irq0) { + free_irq(ms->irq0, ms); + free_irq(ms->irq1, ms); + } cancel_work_sync(&ms->work); From 63f34e35f87f32fb8e92525516e5eaf30cbf0973 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 21 Apr 2026 14:36:14 +0200 Subject: [PATCH 002/118] spi: cadence: rename probe error labels The "clk_dis_all" error label is not used to disable clocks since commit f64b1600f92e ("spi: spi-cadence: Use helper function devm_clk_get_enabled()"). Similarly, "remove_ctlr" drops a reference rather than deregisters the controller. Rename the labels after what they do. Signed-off-by: Johan Hovold Link: https://patch.msgid.link/20260421123615.1533617-4-johan@kernel.org Signed-off-by: Mark Brown --- drivers/spi/spi-cadence.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/spi/spi-cadence.c b/drivers/spi/spi-cadence.c index 891e2ba36958..f27586151ca9 100644 --- a/drivers/spi/spi-cadence.c +++ b/drivers/spi/spi-cadence.c @@ -656,21 +656,21 @@ static int cdns_spi_probe(struct platform_device *pdev) xspi->regs = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(xspi->regs)) { ret = PTR_ERR(xspi->regs); - goto remove_ctlr; + goto err_put_ctlr; } xspi->pclk = devm_clk_get_enabled(&pdev->dev, "pclk"); if (IS_ERR(xspi->pclk)) { dev_err(&pdev->dev, "pclk clock not found.\n"); ret = PTR_ERR(xspi->pclk); - goto remove_ctlr; + goto err_put_ctlr; } xspi->rstc = devm_reset_control_get_optional_exclusive(&pdev->dev, "spi"); if (IS_ERR(xspi->rstc)) { ret = dev_err_probe(&pdev->dev, PTR_ERR(xspi->rstc), "Cannot get SPI reset.\n"); - goto remove_ctlr; + goto err_put_ctlr; } reset_control_assert(xspi->rstc); @@ -680,7 +680,7 @@ static int cdns_spi_probe(struct platform_device *pdev) if (IS_ERR(xspi->ref_clk)) { dev_err(&pdev->dev, "ref_clk clock not found.\n"); ret = PTR_ERR(xspi->ref_clk); - goto remove_ctlr; + goto err_put_ctlr; } if (!spi_controller_is_target(ctlr)) { @@ -710,7 +710,7 @@ static int cdns_spi_probe(struct platform_device *pdev) irq = platform_get_irq(pdev, 0); if (irq < 0) { ret = irq; - goto clk_dis_all; + goto err_disable_rpm; } ret = devm_request_irq(&pdev->dev, irq, cdns_spi_irq, @@ -718,7 +718,7 @@ static int cdns_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; @@ -748,7 +748,7 @@ static int cdns_spi_probe(struct platform_device *pdev) ret = spi_register_controller(ctlr); if (ret) { dev_err(&pdev->dev, "spi_register_controller failed\n"); - goto clk_dis_all; + goto err_disable_rpm; } if (!spi_controller_is_target(ctlr)) @@ -756,14 +756,14 @@ static int cdns_spi_probe(struct platform_device *pdev) return ret; -clk_dis_all: +err_disable_rpm: if (!spi_controller_is_target(ctlr)) { pm_runtime_disable(&pdev->dev); pm_runtime_set_suspended(&pdev->dev); pm_runtime_put_noidle(&pdev->dev); pm_runtime_dont_use_autosuspend(&pdev->dev); } -remove_ctlr: +err_put_ctlr: spi_controller_put(ctlr); return ret; } From bf7b648acd48ae5b9e265727c84b4e0a4a33727b Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 21 Apr 2026 14:36:15 +0200 Subject: [PATCH 003/118] spi: cadence: clean up probe return value Drop the redundant initialisation and return explicit zero on successful probe to make the code more readable. Signed-off-by: Johan Hovold Link: https://patch.msgid.link/20260421123615.1533617-5-johan@kernel.org Signed-off-by: Mark Brown --- drivers/spi/spi-cadence.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/spi/spi-cadence.c b/drivers/spi/spi-cadence.c index f27586151ca9..d108e89fda22 100644 --- a/drivers/spi/spi-cadence.c +++ b/drivers/spi/spi-cadence.c @@ -635,7 +635,7 @@ static int cdns_target_abort(struct spi_controller *ctlr) */ static int cdns_spi_probe(struct platform_device *pdev) { - int ret = 0, irq; + int ret, irq; struct spi_controller *ctlr; struct cdns_spi *xspi; u32 num_cs; @@ -754,7 +754,7 @@ static int cdns_spi_probe(struct platform_device *pdev) if (!spi_controller_is_target(ctlr)) pm_runtime_put_autosuspend(&pdev->dev); - return ret; + return 0; err_disable_rpm: if (!spi_controller_is_target(ctlr)) { From edbaae583ead2c06aea756b0fafd5fa7a1e89fc1 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 21 Apr 2026 14:53:53 +0200 Subject: [PATCH 004/118] spi: cadence-quadspi: clean up disable runtime pm quirk Commit 30dbc1c8d50f ("spi: cadence-qspi: defer runtime support on socfpga if reset bit is enabled") fixed a warm reset issue on SoCFPGA by disabling runtime PM on that platform. Clean up the quirk implementation by never dropping the runtime PM usage count on probe instead of sprinkling conditionals throughout the driver which makes the code unnecessarily hard to read and maintain. Cc: Khairul Anuar Romli Cc: Adrian Ng Ho Yin Cc: Niravkumar L Rabara Cc: Matthew Gerlach Signed-off-by: Johan Hovold Link: https://patch.msgid.link/20260421125354.1534871-6-johan@kernel.org Signed-off-by: Mark Brown --- drivers/spi/spi-cadence-quadspi.c | 47 +++++++++++++------------------ 1 file changed, 19 insertions(+), 28 deletions(-) diff --git a/drivers/spi/spi-cadence-quadspi.c b/drivers/spi/spi-cadence-quadspi.c index 057381e56a7f..348236ea503d 100644 --- a/drivers/spi/spi-cadence-quadspi.c +++ b/drivers/spi/spi-cadence-quadspi.c @@ -1478,7 +1478,6 @@ 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); struct device *dev = &cqspi->pdev->dev; - const struct cqspi_driver_platdata *ddata = of_device_get_match_data(dev); if (refcount_read(&cqspi->inflight_ops) == 0) return -ENODEV; @@ -1494,18 +1493,15 @@ static int cqspi_exec_mem_op(struct spi_mem *mem, const struct spi_mem_op *op) return -EBUSY; } - if (!(ddata && (ddata->quirks & CQSPI_DISABLE_RUNTIME_PM))) { - ret = pm_runtime_resume_and_get(dev); - if (ret) { - dev_err(&mem->spi->dev, "resume failed with %d\n", ret); - goto dec_inflight_refcount; - } + ret = pm_runtime_resume_and_get(dev); + if (ret) { + dev_err(&mem->spi->dev, "resume failed with %d\n", ret); + goto dec_inflight_refcount; } ret = cqspi_mem_process(mem, op); - if (!(ddata && (ddata->quirks & CQSPI_DISABLE_RUNTIME_PM))) - pm_runtime_put_autosuspend(dev); + pm_runtime_put_autosuspend(dev); if (ret) dev_err(&mem->spi->dev, "operation failed with %d\n", ret); @@ -1957,13 +1953,11 @@ static int cqspi_probe(struct platform_device *pdev) cqspi->current_cs = -1; cqspi->sclk = 0; - if (!(ddata && (ddata->quirks & CQSPI_DISABLE_RUNTIME_PM))) { - pm_runtime_set_autosuspend_delay(dev, CQSPI_AUTOSUSPEND_TIMEOUT); - pm_runtime_use_autosuspend(dev); - pm_runtime_get_noresume(dev); - pm_runtime_set_active(dev); - pm_runtime_enable(dev); - } + pm_runtime_set_autosuspend_delay(dev, CQSPI_AUTOSUSPEND_TIMEOUT); + pm_runtime_use_autosuspend(dev); + pm_runtime_get_noresume(dev); + pm_runtime_set_active(dev); + pm_runtime_enable(dev); host->num_chipselect = cqspi->num_chipselect; @@ -1993,12 +1987,11 @@ static int cqspi_probe(struct platform_device *pdev) if (cqspi->rx_chan) dma_release_channel(cqspi->rx_chan); disable_rpm: - if (!(ddata && (ddata->quirks & CQSPI_DISABLE_RUNTIME_PM))) { - pm_runtime_disable(dev); - pm_runtime_set_suspended(dev); - pm_runtime_put_noidle(dev); - pm_runtime_dont_use_autosuspend(dev); - } + pm_runtime_disable(dev); + pm_runtime_set_suspended(dev); + pm_runtime_put_noidle(dev); + pm_runtime_dont_use_autosuspend(dev); + cqspi_controller_enable(cqspi, 0); disable_clks: clk_bulk_disable_unprepare(CLK_QSPI_NUM, cqspi->clks); @@ -2033,12 +2026,10 @@ static void cqspi_remove(struct platform_device *pdev) clk_bulk_disable_unprepare(CLK_QSPI_NUM, cqspi->clks); } - if (!(ddata && (ddata->quirks & CQSPI_DISABLE_RUNTIME_PM))) { - pm_runtime_disable(&pdev->dev); - pm_runtime_set_suspended(&pdev->dev); - pm_runtime_put_noidle(&pdev->dev); - pm_runtime_dont_use_autosuspend(&pdev->dev); - } + pm_runtime_disable(&pdev->dev); + pm_runtime_set_suspended(&pdev->dev); + pm_runtime_put_noidle(&pdev->dev); + pm_runtime_dont_use_autosuspend(&pdev->dev); } static int cqspi_runtime_suspend(struct device *dev) From 37c9dfa385db995e2c8b369a40c72a53dd644df1 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 21 Apr 2026 14:53:54 +0200 Subject: [PATCH 005/118] spi: cadence-quadspi: drop redundant match data lookup Use the OF match data stored at probe instead of looking it up again on driver unbind. Signed-off-by: Johan Hovold Link: https://patch.msgid.link/20260421125354.1534871-7-johan@kernel.org Signed-off-by: Mark Brown --- drivers/spi/spi-cadence-quadspi.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/spi/spi-cadence-quadspi.c b/drivers/spi/spi-cadence-quadspi.c index 348236ea503d..aaba1a3ad577 100644 --- a/drivers/spi/spi-cadence-quadspi.c +++ b/drivers/spi/spi-cadence-quadspi.c @@ -2001,13 +2001,10 @@ static int cqspi_probe(struct platform_device *pdev) static void cqspi_remove(struct platform_device *pdev) { - const struct cqspi_driver_platdata *ddata; struct cqspi_st *cqspi = platform_get_drvdata(pdev); - struct device *dev = &pdev->dev; + const struct cqspi_driver_platdata *ddata = cqspi->ddata; int ret = 0; - ddata = of_device_get_match_data(dev); - spi_unregister_controller(cqspi->host); refcount_set(&cqspi->refcount, 0); From 565bdf45125a05aa8f622f58f598283f46ba43f4 Mon Sep 17 00:00:00 2001 From: Felix Gu Date: Fri, 17 Apr 2026 00:27:54 +0800 Subject: [PATCH 006/118] spi: atcspi200: fix use-after-free when driver unbind DMA resource is initialized after SPI controller registration. So when driver unbind, this can trigger a use-after-free when DMA is torn down while the controller is still alive and triggers DMA transfers. Fixes: 34e3815ea459 ("spi: atcspi200: Add ATCSPI200 SPI controller driver") Signed-off-by: Felix Gu Link: https://patch.msgid.link/20260417-atcspi-v1-1-854831667d63@gmail.com Signed-off-by: Mark Brown --- drivers/spi/spi-atcspi200.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/drivers/spi/spi-atcspi200.c b/drivers/spi/spi-atcspi200.c index 3832d9db3cbf..c5cf1aa2d674 100644 --- a/drivers/spi/spi-atcspi200.c +++ b/drivers/spi/spi-atcspi200.c @@ -575,12 +575,6 @@ static int atcspi_probe(struct platform_device *pdev) if (ret) goto free_controller; - ret = devm_spi_register_controller(&pdev->dev, host); - if (ret) { - dev_err_probe(spi->dev, ret, - "Failed to register SPI controller\n"); - goto free_controller; - } spi->use_dma = false; if (ATCSPI_DMA_SUPPORT) { ret = atcspi_configure_dma(spi); @@ -591,6 +585,13 @@ static int atcspi_probe(struct platform_device *pdev) spi->use_dma = true; } + ret = devm_spi_register_controller(&pdev->dev, host); + if (ret) { + dev_err_probe(spi->dev, ret, + "Failed to register SPI controller\n"); + goto free_controller; + } + return 0; free_controller: From aaea50c3bd768d03ee791b7428ac9b264777b6d7 Mon Sep 17 00:00:00 2001 From: Felix Gu Date: Fri, 17 Apr 2026 00:27:55 +0800 Subject: [PATCH 007/118] spi: atcspi200: switch to devm functions Switch to use devm_spi_alloc_host and devm_mutex_init to make code clean. Signed-off-by: Felix Gu Link: https://patch.msgid.link/20260417-atcspi-v1-2-854831667d63@gmail.com Signed-off-by: Mark Brown --- drivers/spi/spi-atcspi200.c | 25 ++++++++++--------------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/drivers/spi/spi-atcspi200.c b/drivers/spi/spi-atcspi200.c index c5cf1aa2d674..6d4b6aeb3f5b 100644 --- a/drivers/spi/spi-atcspi200.c +++ b/drivers/spi/spi-atcspi200.c @@ -550,7 +550,7 @@ static int atcspi_probe(struct platform_device *pdev) struct resource *mem_res; int ret; - host = spi_alloc_host(&pdev->dev, sizeof(*spi)); + host = devm_spi_alloc_host(&pdev->dev, sizeof(*spi)); if (!host) return -ENOMEM; @@ -559,21 +559,23 @@ static int atcspi_probe(struct platform_device *pdev) spi->dev = &pdev->dev; dev_set_drvdata(&pdev->dev, host); - mutex_init(&spi->mutex_lock); + ret = devm_mutex_init(&pdev->dev, &spi->mutex_lock); + if (ret) + return ret; ret = atcspi_init_resources(pdev, spi, &mem_res); if (ret) - goto free_controller; + return ret; ret = atcspi_enable_clk(spi); if (ret) - goto free_controller; + return ret; atcspi_init_controller(pdev, spi, host, mem_res); ret = atcspi_setup(spi); if (ret) - goto free_controller; + return ret; spi->use_dma = false; if (ATCSPI_DMA_SUPPORT) { @@ -586,18 +588,11 @@ static int atcspi_probe(struct platform_device *pdev) } ret = devm_spi_register_controller(&pdev->dev, host); - if (ret) { - dev_err_probe(spi->dev, ret, - "Failed to register SPI controller\n"); - goto free_controller; - } + if (ret) + return dev_err_probe(spi->dev, ret, + "Failed to register SPI controller\n"); return 0; - -free_controller: - mutex_destroy(&spi->mutex_lock); - spi_controller_put(host); - return ret; } static int atcspi_suspend(struct device *dev) From 4cf8806a637e98c9d83f4254f56e922e9a8714fd Mon Sep 17 00:00:00 2001 From: Conor Dooley Date: Tue, 28 Apr 2026 19:11:11 +0100 Subject: [PATCH 008/118] spi: microchip-core-qspi: report device on which timeout occured instead of which controller When prepare_message callbacks fail, the SPI core already reports which controller the failure happened on. The corresponding code in the mem_ops portion of the driver already reports the device a timeout occurred on, so make the regular part of the driver do the same. Signed-off-by: Conor Dooley Link: https://patch.msgid.link/20260428-porcupine-ninetieth-af00cb11b990@spud Signed-off-by: Mark Brown --- drivers/spi/spi-microchip-core-qspi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/spi/spi-microchip-core-qspi.c b/drivers/spi/spi-microchip-core-qspi.c index eab059fb0bc2..c8b053c7c857 100644 --- a/drivers/spi/spi-microchip-core-qspi.c +++ b/drivers/spi/spi-microchip-core-qspi.c @@ -604,7 +604,7 @@ static int mchp_coreqspi_prepare_message(struct spi_controller *ctlr, struct spi ret = mchp_coreqspi_wait_for_ready(qspi); if (ret) { mutex_unlock(&qspi->op_lock); - dev_err(&ctlr->dev, "Timeout waiting on QSPI ready.\n"); + dev_err(&m->spi->dev, "Timeout waiting on QSPI ready.\n"); return ret; } From 9c69bc6057a20642ee7db258b13f536c2e3214a2 Mon Sep 17 00:00:00 2001 From: Conor Dooley Date: Tue, 28 Apr 2026 19:11:12 +0100 Subject: [PATCH 009/118] spi: microchip-core-qspi: remove an unused define I noticed this define was incorrect, it should be UpperAddress, but in renaming it it became clear there were actually no users. Just get rid of it. Signed-off-by: Conor Dooley Link: https://patch.msgid.link/20260428-viability-crepe-4e4c85e7c506@spud Signed-off-by: Mark Brown --- drivers/spi/spi-microchip-core-qspi.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/spi/spi-microchip-core-qspi.c b/drivers/spi/spi-microchip-core-qspi.c index c8b053c7c857..477a1bb390bb 100644 --- a/drivers/spi/spi-microchip-core-qspi.c +++ b/drivers/spi/spi-microchip-core-qspi.c @@ -92,7 +92,6 @@ #define REG_IEN (0x0c) #define REG_STATUS (0x10) #define REG_DIRECT_ACCESS (0x14) -#define REG_UPPER_ACCESS (0x18) #define REG_RX_DATA (0x40) #define REG_TX_DATA (0x44) #define REG_X4_RX_DATA (0x48) From 54dac8230d9cbc26391ca61b45e1d6c2407c4daf Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 29 Apr 2026 11:23:01 +0200 Subject: [PATCH 010/118] spi: clean up controller registration return value Return explicit zero on successful controller registration to make the code more readable. Signed-off-by: Johan Hovold Link: https://patch.msgid.link/20260429092301.166375-1-johan@kernel.org Signed-off-by: Mark Brown --- drivers/spi/spi.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 104279858f56..5f57de24b9f7 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -3552,7 +3552,8 @@ int spi_register_controller(struct spi_controller *ctlr) /* Register devices from the device tree and ACPI */ of_register_spi_devices(ctlr); acpi_register_spi_devices(ctlr); - return status; + + return 0; del_ctrl: device_del(&ctlr->dev); @@ -3560,6 +3561,7 @@ int spi_register_controller(struct spi_controller *ctlr) mutex_lock(&board_lock); idr_remove(&spi_controller_idr, ctlr->bus_num); mutex_unlock(&board_lock); + return status; } EXPORT_SYMBOL_GPL(spi_register_controller); From 4bde3ad9ee7b92ef226e02cbd3b5c743ed8f781e Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Wed, 29 Apr 2026 19:56:38 +0200 Subject: [PATCH 011/118] mtd: spinand: Drop a too strong limitation Since continuous reads may sometimes not be able to go past an erase block boundary, it has been decided not to attempt longer reads and if the user request is bigger, it will be split across eraseblocks. As these request will anyway be handled correctly, there is no reason to filter out cases where we would go over a target or a die, so drop this limitation which had a side effect: any request to read more than the content of an eraseblock would simply not benefit from the continuous read feature. Signed-off-by: Miquel Raynal --- drivers/mtd/nand/spi/core.c | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c index 8aa3753aaaa1..43df7d558b74 100644 --- a/drivers/mtd/nand/spi/core.c +++ b/drivers/mtd/nand/spi/core.c @@ -878,6 +878,12 @@ static int spinand_mtd_continuous_page_read(struct mtd_info *mtd, loff_t from, * Each data read must be a multiple of 4-bytes and full pages should be read; * otherwise, the data output might get out of sequence from one read command * to another. + * + * Continuous reads never cross LUN boundaries. Some devices don't + * support crossing planes boundaries. Some devices don't even support + * crossing blocks boundaries. The common case being to read through UBI, + * we will very rarely read two consequent blocks or more, so let's only enable + * continuous reads when reading within the same erase block. */ nanddev_io_for_each_block(nand, NAND_PAGE_READ, from, ops, &iter) { ret = spinand_select_target(spinand, iter.req.pos.target); @@ -968,19 +974,6 @@ static bool spinand_use_cont_read(struct mtd_info *mtd, loff_t from, nanddev_offs_to_pos(nand, from, &start_pos); nanddev_offs_to_pos(nand, from + ops->len - 1, &end_pos); - /* - * Continuous reads never cross LUN boundaries. Some devices don't - * support crossing planes boundaries. Some devices don't even support - * crossing blocks boundaries. The common case being to read through UBI, - * we will very rarely read two consequent blocks or more, so it is safer - * and easier (can be improved) to only enable continuous reads when - * reading within the same erase block. - */ - if (start_pos.target != end_pos.target || - start_pos.plane != end_pos.plane || - start_pos.eraseblock != end_pos.eraseblock) - return false; - return start_pos.page < end_pos.page; } From 22fa40c7ecdb11ddc1c95db88cce379408687962 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Wed, 29 Apr 2026 19:56:39 +0200 Subject: [PATCH 012/118] mtd: spinand: Expose spinand_op_is_odtr() This helper is going to be needed in a vendor driver, so expose it. Signed-off-by: Miquel Raynal --- drivers/mtd/nand/spi/core.c | 2 +- include/linux/mtd/spinand.h | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c index 43df7d558b74..a8247e5720c3 100644 --- a/drivers/mtd/nand/spi/core.c +++ b/drivers/mtd/nand/spi/core.c @@ -1400,7 +1400,7 @@ static void spinand_manufacturer_cleanup(struct spinand_device *spinand) return spinand->manufacturer->ops->cleanup(spinand); } -static bool spinand_op_is_odtr(const struct spi_mem_op *op) +bool spinand_op_is_odtr(const struct spi_mem_op *op) { return op->cmd.dtr && op->cmd.buswidth == 8; } diff --git a/include/linux/mtd/spinand.h b/include/linux/mtd/spinand.h index 58abd306ebe3..e1f19664bb25 100644 --- a/include/linux/mtd/spinand.h +++ b/include/linux/mtd/spinand.h @@ -862,6 +862,8 @@ static inline void spinand_set_of_node(struct spinand_device *spinand, nanddev_set_of_node(&spinand->base, np); } +bool spinand_op_is_odtr(const struct spi_mem_op *op); + int spinand_match_and_init(struct spinand_device *spinand, const struct spinand_info *table, unsigned int table_size, From c952533f25e3dc9f121a612299bd54adc795b2ec Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Wed, 29 Apr 2026 19:56:40 +0200 Subject: [PATCH 013/118] mtd: spinand: Drop ECC dirmaps Direct mappings are very static concepts, which allow us to reuse a template to perform reads or writes in a very efficient manner after a single initialization. With the introduction of pipelined ECC engines for SPI controllers, the need to differentiate between an operation with and without correction has arised. The chosen solution at that time has been to create new direct mappings for these operations, jumping from 2 to 4 dirmaps per target. Enabling ECC was done by choosing the correct dirmap. Today, we need to further parametrize dirmaps. With the goal to enable continuous reads on a wider range of devices, we will need more flexibility regarding the read from cache operation template to pick at run time, for instance to use shorter "continuous read from cache" variants. We could create other direct mappings, but it would increase the matrix by a power of two, bringing the theoretical number of dirmaps to 8 (read/write, ecc, shorter read variants) per target. This grow is not sustainable, so let's change how dirmaps work - a little bit. Operations already carry an ECC parameter, use it to indicate whether error correction is required or not. In practice this change happens only at the core level, SPI controller drivers do not care about the direct mapping structure in this case, they just pick whatever is in the template as a base. As a result, we allow the core to dynamically change the content of the templates. He who can do more can do less, so during the checking steps, make sure to enable the ECC requirement just for the time of the checks. Signed-off-by: Miquel Raynal --- drivers/mtd/nand/spi/core.c | 52 ++++++++++++++----------------------- include/linux/mtd/spinand.h | 2 -- 2 files changed, 20 insertions(+), 34 deletions(-) diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c index a8247e5720c3..0f154260e70f 100644 --- a/drivers/mtd/nand/spi/core.c +++ b/drivers/mtd/nand/spi/core.c @@ -487,10 +487,13 @@ static int spinand_read_from_cache_op(struct spinand_device *spinand, } } - if (req->mode == MTD_OPS_RAW) - rdesc = spinand->dirmaps[req->pos.plane].rdesc; + rdesc = spinand->dirmaps[req->pos.plane].rdesc; + + if (nand->ecc.engine->integration == NAND_ECC_ENGINE_INTEGRATION_PIPELINED && + req->mode != MTD_OPS_RAW) + rdesc->info.op_tmpl.data.ecc = true; else - rdesc = spinand->dirmaps[req->pos.plane].rdesc_ecc; + rdesc->info.op_tmpl.data.ecc = false; if (spinand->flags & SPINAND_HAS_READ_PLANE_SELECT_BIT) column |= req->pos.plane << fls(nanddev_page_size(nand)); @@ -579,10 +582,13 @@ static int spinand_write_to_cache_op(struct spinand_device *spinand, req->ooblen); } - if (req->mode == MTD_OPS_RAW) - wdesc = spinand->dirmaps[req->pos.plane].wdesc; + wdesc = spinand->dirmaps[req->pos.plane].wdesc; + + if (nand->ecc.engine->integration == NAND_ECC_ENGINE_INTEGRATION_PIPELINED && + req->mode != MTD_OPS_RAW) + wdesc->info.op_tmpl.data.ecc = true; else - wdesc = spinand->dirmaps[req->pos.plane].wdesc_ecc; + wdesc->info.op_tmpl.data.ecc = false; if (spinand->flags & SPINAND_HAS_PROG_PLANE_SELECT_BIT) column |= req->pos.plane << fls(nanddev_page_size(nand)); @@ -1231,12 +1237,17 @@ static int spinand_create_dirmap(struct spinand_device *spinand, struct nand_device *nand = spinand_to_nand(spinand); struct spi_mem_dirmap_info info = { 0 }; struct spi_mem_dirmap_desc *desc; + bool enable_ecc = false; + + if (nand->ecc.engine->integration == NAND_ECC_ENGINE_INTEGRATION_PIPELINED) + enable_ecc = true; /* The plane number is passed in MSB just above the column address */ info.offset = plane << fls(nand->memorg.pagesize); + /* Write descriptor */ info.length = nanddev_page_size(nand) + nanddev_per_page_oobsize(nand); - info.op_tmpl = *spinand->op_templates->update_cache; + info.op_tmpl.data.ecc = enable_ecc; desc = devm_spi_mem_dirmap_create(&spinand->spimem->spi->dev, spinand->spimem, &info); if (IS_ERR(desc)) @@ -1244,38 +1255,15 @@ static int spinand_create_dirmap(struct spinand_device *spinand, spinand->dirmaps[plane].wdesc = desc; + /* Read descriptor */ info.op_tmpl = *spinand->op_templates->read_cache; + info.op_tmpl.data.ecc = enable_ecc; desc = spinand_create_rdesc(spinand, &info); if (IS_ERR(desc)) return PTR_ERR(desc); spinand->dirmaps[plane].rdesc = desc; - if (nand->ecc.engine->integration != NAND_ECC_ENGINE_INTEGRATION_PIPELINED) { - spinand->dirmaps[plane].wdesc_ecc = spinand->dirmaps[plane].wdesc; - spinand->dirmaps[plane].rdesc_ecc = spinand->dirmaps[plane].rdesc; - - return 0; - } - - info.length = nanddev_page_size(nand) + nanddev_per_page_oobsize(nand); - info.op_tmpl = *spinand->op_templates->update_cache; - info.op_tmpl.data.ecc = true; - desc = devm_spi_mem_dirmap_create(&spinand->spimem->spi->dev, - spinand->spimem, &info); - if (IS_ERR(desc)) - return PTR_ERR(desc); - - spinand->dirmaps[plane].wdesc_ecc = desc; - - info.op_tmpl = *spinand->op_templates->read_cache; - info.op_tmpl.data.ecc = true; - desc = spinand_create_rdesc(spinand, &info); - if (IS_ERR(desc)) - return PTR_ERR(desc); - - spinand->dirmaps[plane].rdesc_ecc = desc; - return 0; } diff --git a/include/linux/mtd/spinand.h b/include/linux/mtd/spinand.h index e1f19664bb25..896e9b5de0c4 100644 --- a/include/linux/mtd/spinand.h +++ b/include/linux/mtd/spinand.h @@ -684,8 +684,6 @@ struct spinand_info { struct spinand_dirmap { struct spi_mem_dirmap_desc *wdesc; struct spi_mem_dirmap_desc *rdesc; - struct spi_mem_dirmap_desc *wdesc_ecc; - struct spi_mem_dirmap_desc *rdesc_ecc; }; /** From 39d0ea33123ffe0214217b529830ad91574c8757 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Wed, 29 Apr 2026 19:56:41 +0200 Subject: [PATCH 014/118] spi: spi-mem: Transform the read operation template As of now, we only use a single operation template when creating SPI memory direct mappings. With the idea to extend this possibility to 2, rename the template to reflect that we are currently setting the "primary" operation, and create a pointer in the same structure to point to it. From a user point of view, the op_tmpl name remains but becomes a pointer, leading to minor changes in both the SPI NAND and SPI NOR cores. There is no functional change. Acked-by: Mark Brown Signed-off-by: Miquel Raynal --- drivers/mtd/nand/spi/core.c | 15 ++++++++------- drivers/mtd/spi-nor/core.c | 22 ++++++++++++---------- drivers/spi/spi-airoha-snfi.c | 6 +++--- drivers/spi/spi-aspeed-smc.c | 4 ++-- drivers/spi/spi-intel.c | 6 +++--- drivers/spi/spi-mem.c | 15 ++++++++------- drivers/spi/spi-mxic.c | 18 +++++++++--------- drivers/spi/spi-npcm-fiu.c | 16 ++++++++-------- drivers/spi/spi-rpc-if.c | 8 ++++---- drivers/spi/spi-stm32-ospi.c | 6 +++--- drivers/spi/spi-stm32-qspi.c | 6 +++--- drivers/spi/spi-wpcm-fiu.c | 2 +- include/linux/spi/spi-mem.h | 3 ++- 13 files changed, 66 insertions(+), 61 deletions(-) diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c index 0f154260e70f..b4c1410d5d8a 100644 --- a/drivers/mtd/nand/spi/core.c +++ b/drivers/mtd/nand/spi/core.c @@ -491,9 +491,9 @@ static int spinand_read_from_cache_op(struct spinand_device *spinand, if (nand->ecc.engine->integration == NAND_ECC_ENGINE_INTEGRATION_PIPELINED && req->mode != MTD_OPS_RAW) - rdesc->info.op_tmpl.data.ecc = true; + rdesc->info.op_tmpl->data.ecc = true; else - rdesc->info.op_tmpl.data.ecc = false; + rdesc->info.op_tmpl->data.ecc = false; if (spinand->flags & SPINAND_HAS_READ_PLANE_SELECT_BIT) column |= req->pos.plane << fls(nanddev_page_size(nand)); @@ -586,9 +586,9 @@ static int spinand_write_to_cache_op(struct spinand_device *spinand, if (nand->ecc.engine->integration == NAND_ECC_ENGINE_INTEGRATION_PIPELINED && req->mode != MTD_OPS_RAW) - wdesc->info.op_tmpl.data.ecc = true; + wdesc->info.op_tmpl->data.ecc = true; else - wdesc->info.op_tmpl.data.ecc = false; + wdesc->info.op_tmpl->data.ecc = false; if (spinand->flags & SPINAND_HAS_PROG_PLANE_SELECT_BIT) column |= req->pos.plane << fls(nanddev_page_size(nand)); @@ -1247,7 +1247,8 @@ static int spinand_create_dirmap(struct spinand_device *spinand, /* Write descriptor */ info.length = nanddev_page_size(nand) + nanddev_per_page_oobsize(nand); - info.op_tmpl.data.ecc = enable_ecc; + info.primary_op_tmpl = *spinand->op_templates->update_cache; + info.primary_op_tmpl.data.ecc = enable_ecc; desc = devm_spi_mem_dirmap_create(&spinand->spimem->spi->dev, spinand->spimem, &info); if (IS_ERR(desc)) @@ -1256,8 +1257,8 @@ static int spinand_create_dirmap(struct spinand_device *spinand, spinand->dirmaps[plane].wdesc = desc; /* Read descriptor */ - info.op_tmpl = *spinand->op_templates->read_cache; - info.op_tmpl.data.ecc = enable_ecc; + info.primary_op_tmpl = *spinand->op_templates->read_cache; + info.primary_op_tmpl.data.ecc = enable_ecc; desc = spinand_create_rdesc(spinand, &info); if (IS_ERR(desc)) return PTR_ERR(desc); diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c index 5dd0b3cb5250..a7bc458edc5c 100644 --- a/drivers/mtd/spi-nor/core.c +++ b/drivers/mtd/spi-nor/core.c @@ -3641,14 +3641,15 @@ EXPORT_SYMBOL_GPL(spi_nor_scan); static int spi_nor_create_read_dirmap(struct spi_nor *nor) { struct spi_mem_dirmap_info info = { - .op_tmpl = SPI_MEM_OP(SPI_MEM_OP_CMD(nor->read_opcode, 0), - SPI_MEM_OP_ADDR(nor->addr_nbytes, 0, 0), - SPI_MEM_OP_DUMMY(nor->read_dummy, 0), - SPI_MEM_OP_DATA_IN(0, NULL, 0)), + .op_tmpl = &info.primary_op_tmpl, + .primary_op_tmpl = SPI_MEM_OP(SPI_MEM_OP_CMD(nor->read_opcode, 0), + SPI_MEM_OP_ADDR(nor->addr_nbytes, 0, 0), + SPI_MEM_OP_DUMMY(nor->read_dummy, 0), + SPI_MEM_OP_DATA_IN(0, NULL, 0)), .offset = 0, .length = nor->params->size, }; - struct spi_mem_op *op = &info.op_tmpl; + struct spi_mem_op *op = info.op_tmpl; spi_nor_spimem_setup_op(nor, op, nor->read_proto); @@ -3672,14 +3673,15 @@ static int spi_nor_create_read_dirmap(struct spi_nor *nor) static int spi_nor_create_write_dirmap(struct spi_nor *nor) { struct spi_mem_dirmap_info info = { - .op_tmpl = SPI_MEM_OP(SPI_MEM_OP_CMD(nor->program_opcode, 0), - SPI_MEM_OP_ADDR(nor->addr_nbytes, 0, 0), - SPI_MEM_OP_NO_DUMMY, - SPI_MEM_OP_DATA_OUT(0, NULL, 0)), + .op_tmpl = &info.primary_op_tmpl, + .primary_op_tmpl = SPI_MEM_OP(SPI_MEM_OP_CMD(nor->program_opcode, 0), + SPI_MEM_OP_ADDR(nor->addr_nbytes, 0, 0), + SPI_MEM_OP_NO_DUMMY, + SPI_MEM_OP_DATA_OUT(0, NULL, 0)), .offset = 0, .length = nor->params->size, }; - struct spi_mem_op *op = &info.op_tmpl; + struct spi_mem_op *op = info.op_tmpl; if (nor->program_opcode == SPINOR_OP_AAI_WP && nor->sst_write_second) op->addr.nbytes = 0; diff --git a/drivers/spi/spi-airoha-snfi.c b/drivers/spi/spi-airoha-snfi.c index 7b6c09f91fef..95bfde7c8e7f 100644 --- a/drivers/spi/spi-airoha-snfi.c +++ b/drivers/spi/spi-airoha-snfi.c @@ -546,7 +546,7 @@ static int airoha_snand_dirmap_create(struct spi_mem_dirmap_desc *desc) if (desc->info.length > SPI_NAND_CACHE_SIZE) return -E2BIG; - if (!airoha_snand_supports_op(desc->mem, &desc->info.op_tmpl)) + if (!airoha_snand_supports_op(desc->mem, desc->info.op_tmpl)) return -EOPNOTSUPP; return 0; @@ -572,7 +572,7 @@ static ssize_t airoha_snand_dirmap_read(struct spi_mem_dirmap_desc *desc, * DUALIO and QUADIO opcodes are not supported by the spi controller, * replace them with supported opcodes. */ - opcode = desc->info.op_tmpl.cmd.opcode; + opcode = desc->info.op_tmpl->cmd.opcode; switch (opcode) { case SPI_NAND_OP_READ_FROM_CACHE_SINGLE: case SPI_NAND_OP_READ_FROM_CACHE_SINGLE_FAST: @@ -761,7 +761,7 @@ static ssize_t airoha_snand_dirmap_write(struct spi_mem_dirmap_desc *desc, /* minimum oob size is 64 */ bytes = round_up(offs + len, 64); - opcode = desc->info.op_tmpl.cmd.opcode; + opcode = desc->info.op_tmpl->cmd.opcode; switch (opcode) { case SPI_NAND_OP_PROGRAM_LOAD_SINGLE: case SPI_NAND_OP_PROGRAM_LOAD_RAMDOM_SINGLE: diff --git a/drivers/spi/spi-aspeed-smc.c b/drivers/spi/spi-aspeed-smc.c index c21323e07d3c..c20a33734f5c 100644 --- a/drivers/spi/spi-aspeed-smc.c +++ b/drivers/spi/spi-aspeed-smc.c @@ -697,7 +697,7 @@ static int aspeed_spi_dirmap_create(struct spi_mem_dirmap_desc *desc) { struct aspeed_spi *aspi = spi_controller_get_devdata(desc->mem->spi->controller); struct aspeed_spi_chip *chip = &aspi->chips[spi_get_chipselect(desc->mem->spi, 0)]; - struct spi_mem_op *op = &desc->info.op_tmpl; + struct spi_mem_op *op = desc->info.op_tmpl; u32 ctl_val; int ret = 0; @@ -769,7 +769,7 @@ static ssize_t aspeed_spi_dirmap_read(struct spi_mem_dirmap_desc *desc, if (chip->ahb_window_size < offset + len || chip->force_user_mode) { int ret; - ret = aspeed_spi_read_user(chip, &desc->info.op_tmpl, offset, len, buf); + ret = aspeed_spi_read_user(chip, desc->info.op_tmpl, offset, len, buf); if (ret < 0) return ret; } else { diff --git a/drivers/spi/spi-intel.c b/drivers/spi/spi-intel.c index 1775ad39e633..7494b921a743 100644 --- a/drivers/spi/spi-intel.c +++ b/drivers/spi/spi-intel.c @@ -814,7 +814,7 @@ static int intel_spi_dirmap_create(struct spi_mem_dirmap_desc *desc) struct intel_spi *ispi = spi_controller_get_devdata(desc->mem->spi->controller); const struct intel_spi_mem_op *iop; - iop = intel_spi_match_mem_op(ispi, &desc->info.op_tmpl); + iop = intel_spi_match_mem_op(ispi, desc->info.op_tmpl); if (!iop) return -EOPNOTSUPP; @@ -827,7 +827,7 @@ static ssize_t intel_spi_dirmap_read(struct spi_mem_dirmap_desc *desc, u64 offs, { struct intel_spi *ispi = spi_controller_get_devdata(desc->mem->spi->controller); const struct intel_spi_mem_op *iop = desc->priv; - struct spi_mem_op op = desc->info.op_tmpl; + struct spi_mem_op op = *desc->info.op_tmpl; int ret; /* Fill in the gaps */ @@ -844,7 +844,7 @@ static ssize_t intel_spi_dirmap_write(struct spi_mem_dirmap_desc *desc, u64 offs { struct intel_spi *ispi = spi_controller_get_devdata(desc->mem->spi->controller); const struct intel_spi_mem_op *iop = desc->priv; - struct spi_mem_op op = desc->info.op_tmpl; + struct spi_mem_op op = *desc->info.op_tmpl; int ret; op.addr.val = offs; diff --git a/drivers/spi/spi-mem.c b/drivers/spi/spi-mem.c index a09371a075d2..e2eaa1ba4ff6 100644 --- a/drivers/spi/spi-mem.c +++ b/drivers/spi/spi-mem.c @@ -647,7 +647,7 @@ EXPORT_SYMBOL_GPL(spi_mem_calc_op_duration); static ssize_t spi_mem_no_dirmap_read(struct spi_mem_dirmap_desc *desc, u64 offs, size_t len, void *buf) { - struct spi_mem_op op = desc->info.op_tmpl; + struct spi_mem_op op = *desc->info.op_tmpl; int ret; op.addr.val = desc->info.offset + offs; @@ -667,7 +667,7 @@ static ssize_t spi_mem_no_dirmap_read(struct spi_mem_dirmap_desc *desc, static ssize_t spi_mem_no_dirmap_write(struct spi_mem_dirmap_desc *desc, u64 offs, size_t len, const void *buf) { - struct spi_mem_op op = desc->info.op_tmpl; + struct spi_mem_op op = *desc->info.op_tmpl; int ret; op.addr.val = desc->info.offset + offs; @@ -706,11 +706,11 @@ spi_mem_dirmap_create(struct spi_mem *mem, int ret = -ENOTSUPP; /* Make sure the number of address cycles is between 1 and 8 bytes. */ - if (!info->op_tmpl.addr.nbytes || info->op_tmpl.addr.nbytes > 8) + if (!info->primary_op_tmpl.addr.nbytes || info->primary_op_tmpl.addr.nbytes > 8) return ERR_PTR(-EINVAL); /* data.dir should either be SPI_MEM_DATA_IN or SPI_MEM_DATA_OUT. */ - if (info->op_tmpl.data.dir == SPI_MEM_NO_DATA) + if (info->primary_op_tmpl.data.dir == SPI_MEM_NO_DATA) return ERR_PTR(-EINVAL); desc = kzalloc_obj(*desc); @@ -719,6 +719,7 @@ spi_mem_dirmap_create(struct spi_mem *mem, desc->mem = mem; desc->info = *info; + desc->info.op_tmpl = &desc->info.primary_op_tmpl; if (ctlr->mem_ops && ctlr->mem_ops->dirmap_create) { ret = spi_mem_access_start(mem); if (ret) { @@ -733,7 +734,7 @@ spi_mem_dirmap_create(struct spi_mem *mem, if (ret) { desc->nodirmap = true; - if (!spi_mem_supports_op(desc->mem, &desc->info.op_tmpl)) + if (!spi_mem_supports_op(desc->mem, &desc->info.primary_op_tmpl)) ret = -EOPNOTSUPP; else ret = 0; @@ -857,7 +858,7 @@ ssize_t spi_mem_dirmap_read(struct spi_mem_dirmap_desc *desc, struct spi_controller *ctlr = desc->mem->spi->controller; ssize_t ret; - if (desc->info.op_tmpl.data.dir != SPI_MEM_DATA_IN) + if (desc->info.op_tmpl->data.dir != SPI_MEM_DATA_IN) return -EINVAL; if (!len) @@ -903,7 +904,7 @@ ssize_t spi_mem_dirmap_write(struct spi_mem_dirmap_desc *desc, struct spi_controller *ctlr = desc->mem->spi->controller; ssize_t ret; - if (desc->info.op_tmpl.data.dir != SPI_MEM_DATA_OUT) + if (desc->info.op_tmpl->data.dir != SPI_MEM_DATA_OUT) return -EINVAL; if (!len) diff --git a/drivers/spi/spi-mxic.c b/drivers/spi/spi-mxic.c index b0e7fc828a50..83b688e65284 100644 --- a/drivers/spi/spi-mxic.c +++ b/drivers/spi/spi-mxic.c @@ -403,20 +403,20 @@ static ssize_t mxic_spi_mem_dirmap_read(struct spi_mem_dirmap_desc *desc, if (WARN_ON(offs + desc->info.offset + len > U32_MAX)) return -EINVAL; - writel(mxic_spi_prep_hc_cfg(desc->mem->spi, 0, desc->info.op_tmpl.data.swap16), + writel(mxic_spi_prep_hc_cfg(desc->mem->spi, 0, desc->info.op_tmpl->data.swap16), mxic->regs + HC_CFG); - writel(mxic_spi_mem_prep_op_cfg(&desc->info.op_tmpl, len), + writel(mxic_spi_mem_prep_op_cfg(desc->info.op_tmpl, len), mxic->regs + LRD_CFG); writel(desc->info.offset + offs, mxic->regs + LRD_ADDR); len = min_t(size_t, len, mxic->linear.size); writel(len, mxic->regs + LRD_RANGE); - writel(LMODE_CMD0(desc->info.op_tmpl.cmd.opcode) | + writel(LMODE_CMD0(desc->info.op_tmpl->cmd.opcode) | LMODE_SLV_ACT(spi_get_chipselect(desc->mem->spi, 0)) | LMODE_EN, mxic->regs + LRD_CTRL); - if (mxic->ecc.use_pipelined_conf && desc->info.op_tmpl.data.ecc) { + if (mxic->ecc.use_pipelined_conf && desc->info.op_tmpl->data.ecc) { ret = mxic_ecc_process_data_pipelined(mxic->ecc.pipelined_engine, NAND_PAGE_READ, mxic->linear.dma + offs); @@ -448,20 +448,20 @@ static ssize_t mxic_spi_mem_dirmap_write(struct spi_mem_dirmap_desc *desc, if (WARN_ON(offs + desc->info.offset + len > U32_MAX)) return -EINVAL; - writel(mxic_spi_prep_hc_cfg(desc->mem->spi, 0, desc->info.op_tmpl.data.swap16), + writel(mxic_spi_prep_hc_cfg(desc->mem->spi, 0, desc->info.op_tmpl->data.swap16), mxic->regs + HC_CFG); - writel(mxic_spi_mem_prep_op_cfg(&desc->info.op_tmpl, len), + writel(mxic_spi_mem_prep_op_cfg(desc->info.op_tmpl, len), mxic->regs + LWR_CFG); writel(desc->info.offset + offs, mxic->regs + LWR_ADDR); len = min_t(size_t, len, mxic->linear.size); writel(len, mxic->regs + LWR_RANGE); - writel(LMODE_CMD0(desc->info.op_tmpl.cmd.opcode) | + writel(LMODE_CMD0(desc->info.op_tmpl->cmd.opcode) | LMODE_SLV_ACT(spi_get_chipselect(desc->mem->spi, 0)) | LMODE_EN, mxic->regs + LWR_CTRL); - if (mxic->ecc.use_pipelined_conf && desc->info.op_tmpl.data.ecc) { + if (mxic->ecc.use_pipelined_conf && desc->info.op_tmpl->data.ecc) { ret = mxic_ecc_process_data_pipelined(mxic->ecc.pipelined_engine, NAND_PAGE_WRITE, mxic->linear.dma + offs); @@ -509,7 +509,7 @@ static int mxic_spi_mem_dirmap_create(struct spi_mem_dirmap_desc *desc) if (desc->info.offset + desc->info.length > U32_MAX) return -EINVAL; - if (!mxic_spi_mem_supports_op(desc->mem, &desc->info.op_tmpl)) + if (!mxic_spi_mem_supports_op(desc->mem, desc->info.op_tmpl)) return -EOPNOTSUPP; return 0; diff --git a/drivers/spi/spi-npcm-fiu.c b/drivers/spi/spi-npcm-fiu.c index 6617751009c3..4b825044038b 100644 --- a/drivers/spi/spi-npcm-fiu.c +++ b/drivers/spi/spi-npcm-fiu.c @@ -299,11 +299,11 @@ static ssize_t npcm_fiu_direct_read(struct spi_mem_dirmap_desc *desc, for (i = 0 ; i < len ; i++) *(buf_rx + i) = ioread8(src + i); } else { - if (desc->info.op_tmpl.addr.buswidth != fiu->drd_op.addr.buswidth || - desc->info.op_tmpl.dummy.nbytes != fiu->drd_op.dummy.nbytes || - desc->info.op_tmpl.cmd.opcode != fiu->drd_op.cmd.opcode || - desc->info.op_tmpl.addr.nbytes != fiu->drd_op.addr.nbytes) - npcm_fiu_set_drd(fiu, &desc->info.op_tmpl); + if (desc->info.op_tmpl->addr.buswidth != fiu->drd_op.addr.buswidth || + desc->info.op_tmpl->dummy.nbytes != fiu->drd_op.dummy.nbytes || + desc->info.op_tmpl->cmd.opcode != fiu->drd_op.cmd.opcode || + desc->info.op_tmpl->addr.nbytes != fiu->drd_op.addr.nbytes) + npcm_fiu_set_drd(fiu, desc->info.op_tmpl); memcpy_fromio(buf_rx, src, len); } @@ -609,7 +609,7 @@ static int npcm_fiu_dirmap_create(struct spi_mem_dirmap_desc *desc) } if (!fiu->spix_mode && - desc->info.op_tmpl.data.dir == SPI_MEM_DATA_OUT) { + desc->info.op_tmpl->data.dir == SPI_MEM_DATA_OUT) { desc->nodirmap = true; return 0; } @@ -644,9 +644,9 @@ static int npcm_fiu_dirmap_create(struct spi_mem_dirmap_desc *desc) NPCM_FIU_CFG_FIU_FIX); } - if (desc->info.op_tmpl.data.dir == SPI_MEM_DATA_IN) { + if (desc->info.op_tmpl->data.dir == SPI_MEM_DATA_IN) { if (!fiu->spix_mode) - npcm_fiu_set_drd(fiu, &desc->info.op_tmpl); + npcm_fiu_set_drd(fiu, desc->info.op_tmpl); else npcm_fiux_set_direct_rd(fiu); diff --git a/drivers/spi/spi-rpc-if.c b/drivers/spi/spi-rpc-if.c index 6edc0c4db854..1ef7bd91b3b3 100644 --- a/drivers/spi/spi-rpc-if.c +++ b/drivers/spi/spi-rpc-if.c @@ -83,7 +83,7 @@ static ssize_t xspi_spi_mem_dirmap_write(struct spi_mem_dirmap_desc *desc, if (offs + desc->info.offset + len > U32_MAX) return -EINVAL; - rpcif_spi_mem_prepare(desc->mem->spi, &desc->info.op_tmpl, &offs, &len); + rpcif_spi_mem_prepare(desc->mem->spi, desc->info.op_tmpl, &offs, &len); return xspi_dirmap_write(rpc->dev, offs, len, buf); } @@ -97,7 +97,7 @@ static ssize_t rpcif_spi_mem_dirmap_read(struct spi_mem_dirmap_desc *desc, if (offs + desc->info.offset + len > U32_MAX) return -EINVAL; - rpcif_spi_mem_prepare(desc->mem->spi, &desc->info.op_tmpl, &offs, &len); + rpcif_spi_mem_prepare(desc->mem->spi, desc->info.op_tmpl, &offs, &len); return rpcif_dirmap_read(rpc->dev, offs, len, buf); } @@ -110,13 +110,13 @@ static int rpcif_spi_mem_dirmap_create(struct spi_mem_dirmap_desc *desc) if (desc->info.offset + desc->info.length > U32_MAX) return -EINVAL; - if (!rpcif_spi_mem_supports_op(desc->mem, &desc->info.op_tmpl)) + if (!rpcif_spi_mem_supports_op(desc->mem, desc->info.op_tmpl)) return -EOPNOTSUPP; if (!rpc->dirmap) return -EOPNOTSUPP; - if (!rpc->xspi && desc->info.op_tmpl.data.dir != SPI_MEM_DATA_IN) + if (!rpc->xspi && desc->info.op_tmpl->data.dir != SPI_MEM_DATA_IN) return -EOPNOTSUPP; return 0; diff --git a/drivers/spi/spi-stm32-ospi.c b/drivers/spi/spi-stm32-ospi.c index 4461c6e24b9e..5f5b3cd5d725 100644 --- a/drivers/spi/spi-stm32-ospi.c +++ b/drivers/spi/spi-stm32-ospi.c @@ -602,11 +602,11 @@ static int stm32_ospi_dirmap_create(struct spi_mem_dirmap_desc *desc) { struct stm32_ospi *ospi = spi_controller_get_devdata(desc->mem->spi->controller); - if (desc->info.op_tmpl.data.dir == SPI_MEM_DATA_OUT) + if (desc->info.op_tmpl->data.dir == SPI_MEM_DATA_OUT) return -EOPNOTSUPP; /* Should never happen, as mm_base == null is an error probe exit condition */ - if (!ospi->mm_base && desc->info.op_tmpl.data.dir == SPI_MEM_DATA_IN) + if (!ospi->mm_base && desc->info.op_tmpl->data.dir == SPI_MEM_DATA_IN) return -EOPNOTSUPP; if (!ospi->mm_size) @@ -633,7 +633,7 @@ static ssize_t stm32_ospi_dirmap_read(struct spi_mem_dirmap_desc *desc, * spi_mem_op template with offs, len and *buf in order to get * all needed transfer information into struct spi_mem_op */ - memcpy(&op, &desc->info.op_tmpl, sizeof(struct spi_mem_op)); + memcpy(&op, desc->info.op_tmpl, sizeof(struct spi_mem_op)); dev_dbg(ospi->dev, "%s len = 0x%zx offs = 0x%llx buf = 0x%p\n", __func__, len, offs, buf); op.data.nbytes = len; diff --git a/drivers/spi/spi-stm32-qspi.c b/drivers/spi/spi-stm32-qspi.c index df1bbacec90a..e2a6a6eaf9b2 100644 --- a/drivers/spi/spi-stm32-qspi.c +++ b/drivers/spi/spi-stm32-qspi.c @@ -506,11 +506,11 @@ static int stm32_qspi_dirmap_create(struct spi_mem_dirmap_desc *desc) { struct stm32_qspi *qspi = spi_controller_get_devdata(desc->mem->spi->controller); - if (desc->info.op_tmpl.data.dir == SPI_MEM_DATA_OUT) + if (desc->info.op_tmpl->data.dir == SPI_MEM_DATA_OUT) return -EOPNOTSUPP; /* should never happen, as mm_base == null is an error probe exit condition */ - if (!qspi->mm_base && desc->info.op_tmpl.data.dir == SPI_MEM_DATA_IN) + if (!qspi->mm_base && desc->info.op_tmpl->data.dir == SPI_MEM_DATA_IN) return -EOPNOTSUPP; if (!qspi->mm_size) @@ -536,7 +536,7 @@ static ssize_t stm32_qspi_dirmap_read(struct spi_mem_dirmap_desc *desc, * spi_mem_op template with offs, len and *buf in order to get * all needed transfer information into struct spi_mem_op */ - memcpy(&op, &desc->info.op_tmpl, sizeof(struct spi_mem_op)); + memcpy(&op, desc->info.op_tmpl, sizeof(struct spi_mem_op)); dev_dbg(qspi->dev, "%s len = 0x%zx offs = 0x%llx buf = 0x%p\n", __func__, len, offs, buf); op.data.nbytes = len; diff --git a/drivers/spi/spi-wpcm-fiu.c b/drivers/spi/spi-wpcm-fiu.c index 0e26ff178505..cd78e927953d 100644 --- a/drivers/spi/spi-wpcm-fiu.c +++ b/drivers/spi/spi-wpcm-fiu.c @@ -377,7 +377,7 @@ static int wpcm_fiu_dirmap_create(struct spi_mem_dirmap_desc *desc) struct wpcm_fiu_spi *fiu = spi_controller_get_devdata(desc->mem->spi->controller); int cs = spi_get_chipselect(desc->mem->spi, 0); - if (desc->info.op_tmpl.data.dir != SPI_MEM_DATA_IN) + if (desc->info.op_tmpl->data.dir != SPI_MEM_DATA_IN) return -EOPNOTSUPP; /* diff --git a/include/linux/spi/spi-mem.h b/include/linux/spi/spi-mem.h index c8e207522223..9a96ddace3eb 100644 --- a/include/linux/spi/spi-mem.h +++ b/include/linux/spi/spi-mem.h @@ -237,7 +237,8 @@ struct spi_mem_op { * direction is directly encoded in the ->op_tmpl.data.dir field. */ struct spi_mem_dirmap_info { - struct spi_mem_op op_tmpl; + struct spi_mem_op *op_tmpl; + struct spi_mem_op primary_op_tmpl; u64 offset; u64 length; }; From 6f96f2fa152518d93ffeedbea781db50aef7f7dc Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Wed, 29 Apr 2026 19:56:42 +0200 Subject: [PATCH 015/118] spi: spi-mem: Create a secondary read operation In some situations, direct mappings may need to use different operation templates. For instance, when enabling continuous reads, Winbond SPI NANDs no longer expect address cycles because they would be ignoring them otherwise. Hence, right after the command opcode, they start counting dummy cycles, followed by the data cycles as usual. This breaks the assumptions of "reads from cache" always being done identically once the best variant has been picked up, across the lifetime of the system. In order to support this feature, we must give direct mapping more than a single operation template to use, in order to switch to using secondary operations upon request by the upper layer. Create the concept of optional secondary operation template, which may or may not be fulfilled by the SPI NAND and SPI NOR cores. If the underlying SPI controller does not leverage any kind of direct mapping acceleration, the feature has no impact and can be freely used. Otherwise, the controller driver needs to opt-in for using this feature, if supported. The condition checked to know whether a secondary operation has been provided or not is to look for a non zero opcode to limit the creation of extra variables. In practice, the opcode 0x00 exist, but is not related to any cache related operation. Acked-by: Mark Brown Signed-off-by: Miquel Raynal --- drivers/spi/spi-mem.c | 17 +++++++++++++++++ include/linux/spi/spi-mem.h | 5 +++++ 2 files changed, 22 insertions(+) diff --git a/drivers/spi/spi-mem.c b/drivers/spi/spi-mem.c index e2eaa1ba4ff6..f64eda9bbd9f 100644 --- a/drivers/spi/spi-mem.c +++ b/drivers/spi/spi-mem.c @@ -713,6 +713,23 @@ spi_mem_dirmap_create(struct spi_mem *mem, if (info->primary_op_tmpl.data.dir == SPI_MEM_NO_DATA) return ERR_PTR(-EINVAL); + /* Apply similar constraints to the secondary template */ + if (info->secondary_op_tmpl.cmd.opcode) { + if (!info->secondary_op_tmpl.addr.nbytes || + info->secondary_op_tmpl.addr.nbytes > 8) + return ERR_PTR(-EINVAL); + + if (info->secondary_op_tmpl.data.dir == SPI_MEM_NO_DATA) + return ERR_PTR(-EINVAL); + + if (!spi_mem_supports_op(mem, &info->secondary_op_tmpl)) + return ERR_PTR(-EOPNOTSUPP); + + if (ctlr->mem_ops && ctlr->mem_ops->dirmap_create && + !spi_mem_controller_is_capable(ctlr, secondary_op_tmpl)) + return ERR_PTR(-EOPNOTSUPP); + } + desc = kzalloc_obj(*desc); if (!desc) return ERR_PTR(-ENOMEM); diff --git a/include/linux/spi/spi-mem.h b/include/linux/spi/spi-mem.h index 9a96ddace3eb..2012a3b2ef91 100644 --- a/include/linux/spi/spi-mem.h +++ b/include/linux/spi/spi-mem.h @@ -227,6 +227,8 @@ struct spi_mem_op { * struct spi_mem_dirmap_info - Direct mapping information * @op_tmpl: operation template that should be used by the direct mapping when * the memory device is accessed + * @secondary_op_tmpl: secondary template, may be used as an alternative to the + * primary template (decided by the upper layer) * @offset: absolute offset this direct mapping is pointing to * @length: length in byte of this direct mapping * @@ -239,6 +241,7 @@ struct spi_mem_op { struct spi_mem_dirmap_info { struct spi_mem_op *op_tmpl; struct spi_mem_op primary_op_tmpl; + struct spi_mem_op secondary_op_tmpl; u64 offset; u64 length; }; @@ -382,12 +385,14 @@ struct spi_controller_mem_ops { * @swap16: Supports swapping bytes on a 16 bit boundary when configured in * Octal DTR * @per_op_freq: Supports per operation frequency switching + * @secondary_op_tmpl: Supports leveraging a secondary memory operation template */ struct spi_controller_mem_caps { bool dtr; bool ecc; bool swap16; bool per_op_freq; + bool secondary_op_tmpl; }; #define spi_mem_controller_is_capable(ctlr, cap) \ From ae4ccd216dd3a9fa89a87a295380f6d62c4832ed Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 29 Apr 2026 11:20:05 +0200 Subject: [PATCH 016/118] spi: at91-usart: drop dead runtime pm support Drop the dead runtime PM support which has never been enabled. Fixes: 96ed3ecde2c0 ("spi: at91-usart: add power management support") Cc: Radu Pirea Signed-off-by: Johan Hovold Link: https://patch.msgid.link/20260429092005.166128-1-johan@kernel.org Signed-off-by: Mark Brown --- drivers/spi/spi-at91-usart.c | 39 ++++++++---------------------------- 1 file changed, 8 insertions(+), 31 deletions(-) diff --git a/drivers/spi/spi-at91-usart.c b/drivers/spi/spi-at91-usart.c index 79edc1cd13c0..dce879627091 100644 --- a/drivers/spi/spi-at91-usart.c +++ b/drivers/spi/spi-at91-usart.c @@ -16,7 +16,6 @@ #include #include #include -#include #include @@ -576,38 +575,18 @@ static int at91_usart_spi_probe(struct platform_device *pdev) return ret; } -__maybe_unused static int at91_usart_spi_runtime_suspend(struct device *dev) -{ - struct spi_controller *ctlr = dev_get_drvdata(dev); - struct at91_usart_spi *aus = spi_controller_get_devdata(ctlr); - - clk_disable_unprepare(aus->clk); - pinctrl_pm_select_sleep_state(dev); - - return 0; -} - -__maybe_unused static int at91_usart_spi_runtime_resume(struct device *dev) -{ - struct spi_controller *ctrl = dev_get_drvdata(dev); - struct at91_usart_spi *aus = spi_controller_get_devdata(ctrl); - - pinctrl_pm_select_default_state(dev); - - return clk_prepare_enable(aus->clk); -} - __maybe_unused static int at91_usart_spi_suspend(struct device *dev) { struct spi_controller *ctrl = dev_get_drvdata(dev); + struct at91_usart_spi *aus = spi_controller_get_devdata(ctrl); int ret; ret = spi_controller_suspend(ctrl); if (ret) return ret; - if (!pm_runtime_suspended(dev)) - at91_usart_spi_runtime_suspend(dev); + clk_disable_unprepare(aus->clk); + pinctrl_pm_select_sleep_state(dev); return 0; } @@ -618,11 +597,11 @@ __maybe_unused static int at91_usart_spi_resume(struct device *dev) struct at91_usart_spi *aus = spi_controller_get_devdata(ctrl); int ret; - if (!pm_runtime_suspended(dev)) { - ret = at91_usart_spi_runtime_resume(dev); - if (ret) - return ret; - } + pinctrl_pm_select_default_state(dev); + + ret = clk_prepare_enable(aus->clk); + if (ret) + return ret; at91_usart_spi_init(aus); @@ -646,8 +625,6 @@ static void at91_usart_spi_remove(struct platform_device *pdev) static const struct dev_pm_ops at91_usart_spi_pm_ops = { SET_SYSTEM_SLEEP_PM_OPS(at91_usart_spi_suspend, at91_usart_spi_resume) - SET_RUNTIME_PM_OPS(at91_usart_spi_runtime_suspend, - at91_usart_spi_runtime_resume, NULL) }; static struct platform_driver at91_usart_spi_driver = { From 38fbe4b3f66e5b8e2f2ab8e7ca3d912e1e935fe2 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Thu, 26 Mar 2026 17:47:15 +0100 Subject: [PATCH 017/118] spi: spi-mem: Add a no_cs_assertion capability Some controllers are 'smart', and that's a problem. For instance, the Cadence quadspi controller is capable of deasserting the CS automatically whenever a too long period of time without any data to transfer elapses. This 'feature' combined with a loaded interconnect with arbitration, a "long" transfer may be split into smaller DMA transfers. In this case the controller may allow itself to deassert the CS between chunks. Deasserting the CS stops any ongoing continuous read. Reasserting it later to continue the reading will only result in the host getting garbage. In this case, the host controller driver has no control over the CS state, so we cannot reliably enable continuous reads. Flag this limitation through a spi-mem controller capability. The inversion in the flag name (starting with 'no_') is voluntary, in order to avoid the need to set this flag in all controller drivers. Only the broken controllers shall set this bit, the default being that the controller masters its CS fully. Reviewed-by: Mark Brown Signed-off-by: Miquel Raynal --- include/linux/spi/spi-mem.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/include/linux/spi/spi-mem.h b/include/linux/spi/spi-mem.h index 2012a3b2ef91..722abd9aee3c 100644 --- a/include/linux/spi/spi-mem.h +++ b/include/linux/spi/spi-mem.h @@ -385,7 +385,10 @@ struct spi_controller_mem_ops { * @swap16: Supports swapping bytes on a 16 bit boundary when configured in * Octal DTR * @per_op_freq: Supports per operation frequency switching - * @secondary_op_tmpl: Supports leveraging a secondary memory operation template + * @no_cs_assertion: The controller may automatically deassert the CS if there + * is a pause in the transfer (eg. internal bus contention or + * DMA arbitration on an interconnect). Features such as NAND + * continuous reads shall not be leveraged. */ struct spi_controller_mem_caps { bool dtr; @@ -393,6 +396,7 @@ struct spi_controller_mem_caps { bool swap16; bool per_op_freq; bool secondary_op_tmpl; + bool no_cs_assertion; }; #define spi_mem_controller_is_capable(ctlr, cap) \ From 5113e23077103e6e92b21133065e5a4b5fd09c47 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 29 Apr 2026 11:13:15 +0200 Subject: [PATCH 018/118] spi: at91-usart: switch to managed controller allocation Switch to device managed controller allocation to simplify error handling and to avoid having to take another reference during deregistration. Signed-off-by: Johan Hovold Link: https://patch.msgid.link/20260429091333.165363-2-johan@kernel.org Signed-off-by: Mark Brown --- drivers/spi/spi-at91-usart.c | 24 ++++++++---------------- 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/drivers/spi/spi-at91-usart.c b/drivers/spi/spi-at91-usart.c index dce879627091..77cad4118202 100644 --- a/drivers/spi/spi-at91-usart.c +++ b/drivers/spi/spi-at91-usart.c @@ -495,14 +495,13 @@ static int at91_usart_spi_probe(struct platform_device *pdev) if (IS_ERR(clk)) return PTR_ERR(clk); - ret = -ENOMEM; - controller = spi_alloc_host(&pdev->dev, sizeof(*aus)); + controller = devm_spi_alloc_host(&pdev->dev, sizeof(*aus)); if (!controller) - goto at91_usart_spi_probe_fail; + return -ENOMEM; ret = at91_usart_gpio_setup(pdev); if (ret) - goto at91_usart_spi_probe_fail; + return ret; controller->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LOOP | SPI_CS_HIGH; controller->dev.of_node = pdev->dev.parent->of_node; @@ -524,10 +523,8 @@ static int at91_usart_spi_probe(struct platform_device *pdev) aus->dev = &pdev->dev; aus->regs = devm_ioremap_resource(&pdev->dev, regs); - if (IS_ERR(aus->regs)) { - ret = PTR_ERR(aus->regs); - goto at91_usart_spi_probe_fail; - } + if (IS_ERR(aus->regs)) + return PTR_ERR(aus->regs); aus->irq = irq; aus->clk = clk; @@ -535,11 +532,11 @@ static int at91_usart_spi_probe(struct platform_device *pdev) ret = devm_request_irq(&pdev->dev, irq, at91_usart_spi_interrupt, 0, dev_name(&pdev->dev), controller); if (ret) - goto at91_usart_spi_probe_fail; + return ret; ret = clk_prepare_enable(clk); if (ret) - goto at91_usart_spi_probe_fail; + return ret; aus->spi_clk = clk_get_rate(clk); at91_usart_spi_init(aus); @@ -570,8 +567,7 @@ static int at91_usart_spi_probe(struct platform_device *pdev) at91_usart_spi_release_dma(controller); at91_usart_fail_dma: clk_disable_unprepare(clk); -at91_usart_spi_probe_fail: - spi_controller_put(controller); + return ret; } @@ -613,14 +609,10 @@ static void at91_usart_spi_remove(struct platform_device *pdev) struct spi_controller *ctlr = platform_get_drvdata(pdev); struct at91_usart_spi *aus = spi_controller_get_devdata(ctlr); - spi_controller_get(ctlr); - spi_unregister_controller(ctlr); at91_usart_spi_release_dma(ctlr); clk_disable_unprepare(aus->clk); - - spi_controller_put(ctlr); } static const struct dev_pm_ops at91_usart_spi_pm_ops = { From ebf99aebc458391893c598f1caddd42bfcc97fc5 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 29 Apr 2026 11:13:16 +0200 Subject: [PATCH 019/118] spi: atmel: switch to managed controller allocation Switch to device managed controller allocation to simplify error handling and to avoid having to take another reference during deregistration. Signed-off-by: Johan Hovold Link: https://patch.msgid.link/20260429091333.165363-3-johan@kernel.org Signed-off-by: Mark Brown --- drivers/spi/spi-atmel.c | 34 ++++++++++++---------------------- 1 file changed, 12 insertions(+), 22 deletions(-) diff --git a/drivers/spi/spi-atmel.c b/drivers/spi/spi-atmel.c index 42db85d7ff8e..25aa294631c8 100644 --- a/drivers/spi/spi-atmel.c +++ b/drivers/spi/spi-atmel.c @@ -1528,7 +1528,7 @@ static int atmel_spi_probe(struct platform_device *pdev) return PTR_ERR(clk); /* setup spi core then atmel-specific driver state */ - host = spi_alloc_host(&pdev->dev, sizeof(*as)); + host = devm_spi_alloc_host(&pdev->dev, sizeof(*as)); if (!host) return -ENOMEM; @@ -1555,18 +1555,15 @@ static int atmel_spi_probe(struct platform_device *pdev) as->pdev = pdev; as->regs = devm_platform_get_and_ioremap_resource(pdev, 0, ®s); - if (IS_ERR(as->regs)) { - ret = PTR_ERR(as->regs); - goto out_unmap_regs; - } + if (IS_ERR(as->regs)) + return PTR_ERR(as->regs); + as->phybase = regs->start; as->irq = irq; as->clk = clk; as->gclk = devm_clk_get_optional(&pdev->dev, "spi_gclk"); - if (IS_ERR(as->gclk)) { - ret = PTR_ERR(as->gclk); - goto out_unmap_regs; - } + if (IS_ERR(as->gclk)) + return PTR_ERR(as->gclk); init_completion(&as->xfer_completion); @@ -1576,11 +1573,10 @@ static int atmel_spi_probe(struct platform_device *pdev) as->use_pdc = false; if (as->caps.has_dma_support) { ret = atmel_spi_configure_dma(host, as); - if (ret == 0) { + if (ret == 0) as->use_dma = true; - } else if (ret == -EPROBE_DEFER) { - goto out_unmap_regs; - } + else if (ret == -EPROBE_DEFER) + return ret; } else if (as->caps.has_pdc_support) { as->use_pdc = true; } @@ -1620,12 +1616,12 @@ static int atmel_spi_probe(struct platform_device *pdev) 0, dev_name(&pdev->dev), host); } if (ret) - goto out_unmap_regs; + return ret; /* Initialize the hardware */ ret = clk_prepare_enable(clk); if (ret) - goto out_free_irq; + return ret; /* * In cases where the peripheral clock is higher,the FLEX_SPI_CSRx.SCBR @@ -1677,9 +1673,7 @@ static int atmel_spi_probe(struct platform_device *pdev) clk_disable_unprepare(as->gclk); out_disable_clk: clk_disable_unprepare(clk); -out_free_irq: -out_unmap_regs: - spi_controller_put(host); + return ret; } @@ -1688,8 +1682,6 @@ static void atmel_spi_remove(struct platform_device *pdev) struct spi_controller *host = platform_get_drvdata(pdev); struct atmel_spi *as = spi_controller_get_devdata(host); - spi_controller_get(host); - pm_runtime_get_sync(&pdev->dev); spi_unregister_controller(host); @@ -1720,8 +1712,6 @@ static void atmel_spi_remove(struct platform_device *pdev) pm_runtime_put_noidle(&pdev->dev); pm_runtime_disable(&pdev->dev); - - spi_controller_put(host); } static int atmel_spi_runtime_suspend(struct device *dev) From 414c359e7295d383e6bf2321eb7499f95008f1a8 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 29 Apr 2026 11:13:17 +0200 Subject: [PATCH 020/118] spi: bcm63xx: switch to managed controller allocation Switch to device managed controller allocation to simplify error handling and to avoid having to take another reference during deregistration. Signed-off-by: Johan Hovold Link: https://patch.msgid.link/20260429091333.165363-4-johan@kernel.org Signed-off-by: Mark Brown --- drivers/spi/spi-bcm63xx.c | 23 +++++++---------------- 1 file changed, 7 insertions(+), 16 deletions(-) diff --git a/drivers/spi/spi-bcm63xx.c b/drivers/spi/spi-bcm63xx.c index 40cd7efc4b54..f8cfe535b2a3 100644 --- a/drivers/spi/spi-bcm63xx.c +++ b/drivers/spi/spi-bcm63xx.c @@ -541,11 +541,9 @@ static int bcm63xx_spi_probe(struct platform_device *pdev) if (IS_ERR(reset)) return PTR_ERR(reset); - host = spi_alloc_host(dev, sizeof(*bs)); - if (!host) { - dev_err(dev, "out of memory\n"); + host = devm_spi_alloc_host(dev, sizeof(*bs)); + if (!host) return -ENOMEM; - } bs = spi_controller_get_devdata(host); init_completion(&bs->done); @@ -554,10 +552,8 @@ static int bcm63xx_spi_probe(struct platform_device *pdev) bs->pdev = pdev; bs->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &r); - if (IS_ERR(bs->regs)) { - ret = PTR_ERR(bs->regs); - goto out_err; - } + if (IS_ERR(bs->regs)) + return PTR_ERR(bs->regs); bs->irq = irq; bs->clk = clk; @@ -568,7 +564,7 @@ static int bcm63xx_spi_probe(struct platform_device *pdev) pdev->name, host); if (ret) { dev_err(dev, "unable to request irq\n"); - goto out_err; + return ret; } host->bus_num = bus_num; @@ -587,7 +583,7 @@ static int bcm63xx_spi_probe(struct platform_device *pdev) /* Initialize hardware */ ret = clk_prepare_enable(bs->clk); if (ret) - goto out_err; + return ret; ret = reset_control_reset(reset); if (ret) { @@ -615,8 +611,7 @@ static int bcm63xx_spi_probe(struct platform_device *pdev) out_clk_disable: clk_disable_unprepare(clk); -out_err: - spi_controller_put(host); + return ret; } @@ -625,8 +620,6 @@ static void bcm63xx_spi_remove(struct platform_device *pdev) struct spi_controller *host = platform_get_drvdata(pdev); struct bcm63xx_spi *bs = spi_controller_get_devdata(host); - spi_controller_get(host); - spi_unregister_controller(host); /* reset spi block */ @@ -634,8 +627,6 @@ static void bcm63xx_spi_remove(struct platform_device *pdev) /* HW shutdown */ clk_disable_unprepare(bs->clk); - - spi_controller_put(host); } static int bcm63xx_spi_suspend(struct device *dev) From fe010594a8575715b879956dfb970894431dd69f Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 29 Apr 2026 11:13:18 +0200 Subject: [PATCH 021/118] spi: bcm63xx-hsspi: switch to managed controller allocation Switch to device managed controller allocation to simplify error handling and to avoid having to take another reference during deregistration. Signed-off-by: Johan Hovold Acked-by: William Zhang Link: https://patch.msgid.link/20260429091333.165363-5-johan@kernel.org Signed-off-by: Mark Brown --- drivers/spi/spi-bcm63xx-hsspi.c | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/drivers/spi/spi-bcm63xx-hsspi.c b/drivers/spi/spi-bcm63xx-hsspi.c index e935e8ab9cfd..58012e1b5ae7 100644 --- a/drivers/spi/spi-bcm63xx-hsspi.c +++ b/drivers/spi/spi-bcm63xx-hsspi.c @@ -783,7 +783,7 @@ static int bcm63xx_hsspi_probe(struct platform_device *pdev) "failed get pll clk rate\n"); } - host = spi_alloc_host(&pdev->dev, sizeof(*bs)); + host = devm_spi_alloc_host(&pdev->dev, sizeof(*bs)); if (!host) return dev_err_probe(dev, -ENOMEM, "alloc host no mem\n"); @@ -796,10 +796,8 @@ static int bcm63xx_hsspi_probe(struct platform_device *pdev) bs->fifo = (u8 __iomem *)(bs->regs + HSSPI_FIFO_REG(0)); bs->wait_mode = HSSPI_WAIT_MODE_POLLING; bs->prepend_buf = devm_kzalloc(dev, HSSPI_BUFFER_LEN, GFP_KERNEL); - if (!bs->prepend_buf) { - ret = -ENOMEM; - goto out_put_host; - } + if (!bs->prepend_buf) + return -ENOMEM; mutex_init(&bs->bus_mutex); mutex_init(&bs->msg_mutex); @@ -845,7 +843,7 @@ static int bcm63xx_hsspi_probe(struct platform_device *pdev) pdev->name, bs); if (ret) - goto out_put_host; + return ret; } pm_runtime_enable(&pdev->dev); @@ -869,8 +867,7 @@ static int bcm63xx_hsspi_probe(struct platform_device *pdev) sysfs_remove_group(&pdev->dev.kobj, &bcm63xx_hsspi_group); out_pm_disable: pm_runtime_disable(&pdev->dev); -out_put_host: - spi_controller_put(host); + return ret; } @@ -880,15 +877,11 @@ static void bcm63xx_hsspi_remove(struct platform_device *pdev) struct spi_controller *host = platform_get_drvdata(pdev); struct bcm63xx_hsspi *bs = spi_controller_get_devdata(host); - spi_controller_get(host); - spi_unregister_controller(host); /* reset the hardware and block queue progress */ __raw_writel(0, bs->regs + HSSPI_INT_MASK_REG); sysfs_remove_group(&pdev->dev.kobj, &bcm63xx_hsspi_group); - - spi_controller_put(host); } #ifdef CONFIG_PM_SLEEP From 83c4ded3917d0cf5bcb1899c2747cede8dc6ab14 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 29 Apr 2026 11:13:19 +0200 Subject: [PATCH 022/118] spi: cadence: switch to managed controller allocation Switch to device managed controller allocation to simplify error handling and to avoid having to take another reference during deregistration. Signed-off-by: Johan Hovold Link: https://patch.msgid.link/20260429091333.165363-6-johan@kernel.org Signed-off-by: Mark Brown --- drivers/spi/spi-cadence.c | 28 +++++++++------------------- 1 file changed, 9 insertions(+), 19 deletions(-) diff --git a/drivers/spi/spi-cadence.c b/drivers/spi/spi-cadence.c index d108e89fda22..9b4e5b7013ae 100644 --- a/drivers/spi/spi-cadence.c +++ b/drivers/spi/spi-cadence.c @@ -643,9 +643,9 @@ static int cdns_spi_probe(struct platform_device *pdev) target = of_property_read_bool(pdev->dev.of_node, "spi-slave"); if (target) - ctlr = spi_alloc_target(&pdev->dev, sizeof(*xspi)); + ctlr = devm_spi_alloc_target(&pdev->dev, sizeof(*xspi)); else - ctlr = spi_alloc_host(&pdev->dev, sizeof(*xspi)); + ctlr = devm_spi_alloc_host(&pdev->dev, sizeof(*xspi)); if (!ctlr) return -ENOMEM; @@ -654,23 +654,19 @@ static int cdns_spi_probe(struct platform_device *pdev) platform_set_drvdata(pdev, ctlr); xspi->regs = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(xspi->regs)) { - ret = PTR_ERR(xspi->regs); - goto err_put_ctlr; - } + if (IS_ERR(xspi->regs)) + return PTR_ERR(xspi->regs); xspi->pclk = devm_clk_get_enabled(&pdev->dev, "pclk"); if (IS_ERR(xspi->pclk)) { dev_err(&pdev->dev, "pclk clock not found.\n"); - ret = PTR_ERR(xspi->pclk); - goto err_put_ctlr; + return PTR_ERR(xspi->pclk); } xspi->rstc = devm_reset_control_get_optional_exclusive(&pdev->dev, "spi"); if (IS_ERR(xspi->rstc)) { - ret = dev_err_probe(&pdev->dev, PTR_ERR(xspi->rstc), - "Cannot get SPI reset.\n"); - goto err_put_ctlr; + return dev_err_probe(&pdev->dev, PTR_ERR(xspi->rstc), + "Cannot get SPI reset.\n"); } reset_control_assert(xspi->rstc); @@ -679,8 +675,7 @@ static int cdns_spi_probe(struct platform_device *pdev) xspi->ref_clk = devm_clk_get_enabled(&pdev->dev, "ref_clk"); if (IS_ERR(xspi->ref_clk)) { dev_err(&pdev->dev, "ref_clk clock not found.\n"); - ret = PTR_ERR(xspi->ref_clk); - goto err_put_ctlr; + return PTR_ERR(xspi->ref_clk); } if (!spi_controller_is_target(ctlr)) { @@ -763,8 +758,7 @@ static int cdns_spi_probe(struct platform_device *pdev) pm_runtime_put_noidle(&pdev->dev); pm_runtime_dont_use_autosuspend(&pdev->dev); } -err_put_ctlr: - spi_controller_put(ctlr); + return ret; } @@ -785,8 +779,6 @@ static void cdns_spi_remove(struct platform_device *pdev) if (!spi_controller_is_target(ctlr)) ret = pm_runtime_get_sync(&pdev->dev); - spi_controller_get(ctlr); - spi_unregister_controller(ctlr); if (ret >= 0) @@ -798,8 +790,6 @@ static void cdns_spi_remove(struct platform_device *pdev) pm_runtime_put_noidle(&pdev->dev); pm_runtime_dont_use_autosuspend(&pdev->dev); } - - spi_controller_put(ctlr); } /** From eab6ce941217cf12190945d6f8068306ccd4f6eb Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 29 Apr 2026 11:13:20 +0200 Subject: [PATCH 023/118] spi: octeon: switch to managed controller allocation Switch to device managed controller allocation to simplify error handling and to avoid having to take another reference during deregistration. Signed-off-by: Johan Hovold Link: https://patch.msgid.link/20260429091333.165363-7-johan@kernel.org Signed-off-by: Mark Brown --- drivers/spi/spi-cavium-octeon.c | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/drivers/spi/spi-cavium-octeon.c b/drivers/spi/spi-cavium-octeon.c index b95bfa6a3013..28c922c72068 100644 --- a/drivers/spi/spi-cavium-octeon.c +++ b/drivers/spi/spi-cavium-octeon.c @@ -23,17 +23,15 @@ static int octeon_spi_probe(struct platform_device *pdev) struct octeon_spi *p; int err = -ENOENT; - host = spi_alloc_host(&pdev->dev, sizeof(struct octeon_spi)); + host = devm_spi_alloc_host(&pdev->dev, sizeof(struct octeon_spi)); if (!host) return -ENOMEM; p = spi_controller_get_devdata(host); platform_set_drvdata(pdev, host); reg_base = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(reg_base)) { - err = PTR_ERR(reg_base); - goto fail; - } + if (IS_ERR(reg_base)) + return PTR_ERR(reg_base); p->register_base = reg_base; p->sys_freq = octeon_get_io_clock_rate(); @@ -57,15 +55,12 @@ static int octeon_spi_probe(struct platform_device *pdev) err = spi_register_controller(host); if (err) { dev_err(&pdev->dev, "register host failed: %d\n", err); - goto fail; + return err; } dev_info(&pdev->dev, "OCTEON SPI bus driver\n"); return 0; -fail: - spi_controller_put(host); - return err; } static void octeon_spi_remove(struct platform_device *pdev) @@ -73,14 +68,10 @@ static void octeon_spi_remove(struct platform_device *pdev) struct spi_controller *host = platform_get_drvdata(pdev); struct octeon_spi *p = spi_controller_get_devdata(host); - spi_controller_get(host); - spi_unregister_controller(host); /* Clear the CSENA* and put everything in a known state. */ writeq(0, p->register_base + OCTEON_SPI_CFG(p)); - - spi_controller_put(host); } static const struct of_device_id octeon_spi_match[] = { From fcca78c086383c8b22aa1e529963eebab46d3f0c Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 29 Apr 2026 11:13:21 +0200 Subject: [PATCH 024/118] spi: cavium-thunderx: switch to managed controller allocation Switch to device managed controller allocation to simplify error handling and to avoid having to take another reference during deregistration. Signed-off-by: Johan Hovold Link: https://patch.msgid.link/20260429091333.165363-8-johan@kernel.org Signed-off-by: Mark Brown --- drivers/spi/spi-cavium-thunderx.c | 32 ++++++++----------------------- 1 file changed, 8 insertions(+), 24 deletions(-) diff --git a/drivers/spi/spi-cavium-thunderx.c b/drivers/spi/spi-cavium-thunderx.c index f1a9aa696c87..529b4066c922 100644 --- a/drivers/spi/spi-cavium-thunderx.c +++ b/drivers/spi/spi-cavium-thunderx.c @@ -24,7 +24,7 @@ static int thunderx_spi_probe(struct pci_dev *pdev, struct octeon_spi *p; int ret; - host = spi_alloc_host(dev, sizeof(struct octeon_spi)); + host = devm_spi_alloc_host(dev, sizeof(struct octeon_spi)); if (!host) return -ENOMEM; @@ -32,17 +32,15 @@ static int thunderx_spi_probe(struct pci_dev *pdev, ret = pcim_enable_device(pdev); if (ret) - goto error; + return ret; ret = pcim_request_all_regions(pdev, DRV_NAME); if (ret) - goto error; + return ret; p->register_base = pcim_iomap(pdev, 0, pci_resource_len(pdev, 0)); - if (!p->register_base) { - ret = -EINVAL; - goto error; - } + if (!p->register_base) + return -EINVAL; p->regs.config = 0x1000; p->regs.status = 0x1008; @@ -50,10 +48,8 @@ static int thunderx_spi_probe(struct pci_dev *pdev, p->regs.data = 0x1080; p->clk = devm_clk_get_enabled(dev, NULL); - if (IS_ERR(p->clk)) { - ret = PTR_ERR(p->clk); - goto error; - } + if (IS_ERR(p->clk)) + return PTR_ERR(p->clk); p->sys_freq = clk_get_rate(p->clk); if (!p->sys_freq) @@ -70,15 +66,7 @@ static int thunderx_spi_probe(struct pci_dev *pdev, pci_set_drvdata(pdev, host); - ret = spi_register_controller(host); - if (ret) - goto error; - - return 0; - -error: - spi_controller_put(host); - return ret; + return spi_register_controller(host); } static void thunderx_spi_remove(struct pci_dev *pdev) @@ -90,14 +78,10 @@ static void thunderx_spi_remove(struct pci_dev *pdev) if (!p) return; - spi_controller_get(host); - spi_unregister_controller(host); /* Put everything in a known state. */ writeq(0, p->register_base + OCTEON_SPI_CFG(p)); - - spi_controller_put(host); } static const struct pci_device_id thunderx_spi_pci_id_table[] = { From 079c7a626c7d2a7f3841ce1195e4da09b8ca365b Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 29 Apr 2026 11:13:22 +0200 Subject: [PATCH 025/118] spi: coldfire-qspi: switch to managed controller allocation Switch to device managed controller allocation to simplify error handling and to avoid having to take another reference during deregistration. Signed-off-by: Johan Hovold Link: https://patch.msgid.link/20260429091333.165363-9-johan@kernel.org Signed-off-by: Mark Brown --- drivers/spi/spi-coldfire-qspi.c | 28 ++++++++-------------------- 1 file changed, 8 insertions(+), 20 deletions(-) diff --git a/drivers/spi/spi-coldfire-qspi.c b/drivers/spi/spi-coldfire-qspi.c index b45f44de85dc..3b175c1da36b 100644 --- a/drivers/spi/spi-coldfire-qspi.c +++ b/drivers/spi/spi-coldfire-qspi.c @@ -353,39 +353,33 @@ static int mcfqspi_probe(struct platform_device *pdev) return -EINVAL; } - host = spi_alloc_host(&pdev->dev, sizeof(*mcfqspi)); - if (host == NULL) { - dev_dbg(&pdev->dev, "spi_alloc_host failed\n"); + host = devm_spi_alloc_host(&pdev->dev, sizeof(*mcfqspi)); + if (host == NULL) return -ENOMEM; - } mcfqspi = spi_controller_get_devdata(host); mcfqspi->iobase = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(mcfqspi->iobase)) { - status = PTR_ERR(mcfqspi->iobase); - goto fail0; - } + if (IS_ERR(mcfqspi->iobase)) + return PTR_ERR(mcfqspi->iobase); mcfqspi->irq = platform_get_irq(pdev, 0); if (mcfqspi->irq < 0) { dev_dbg(&pdev->dev, "platform_get_irq failed\n"); - status = -ENXIO; - goto fail0; + return -ENXIO; } status = devm_request_irq(&pdev->dev, mcfqspi->irq, mcfqspi_irq_handler, 0, pdev->name, mcfqspi); if (status) { dev_dbg(&pdev->dev, "request_irq failed\n"); - goto fail0; + return status; } mcfqspi->clk = devm_clk_get_enabled(&pdev->dev, "qspi_clk"); if (IS_ERR(mcfqspi->clk)) { dev_dbg(&pdev->dev, "clk_get failed\n"); - status = PTR_ERR(mcfqspi->clk); - goto fail0; + return PTR_ERR(mcfqspi->clk); } host->bus_num = pdata->bus_num; @@ -395,7 +389,7 @@ static int mcfqspi_probe(struct platform_device *pdev) status = mcfqspi_cs_setup(mcfqspi); if (status) { dev_dbg(&pdev->dev, "error initializing cs_control\n"); - goto fail0; + return status; } init_waitqueue_head(&mcfqspi->waitq); @@ -423,8 +417,6 @@ static int mcfqspi_probe(struct platform_device *pdev) fail1: pm_runtime_disable(&pdev->dev); mcfqspi_cs_teardown(mcfqspi); -fail0: - spi_controller_put(host); dev_dbg(&pdev->dev, "Coldfire QSPI probe failed\n"); @@ -436,8 +428,6 @@ static void mcfqspi_remove(struct platform_device *pdev) struct spi_controller *host = platform_get_drvdata(pdev); struct mcfqspi *mcfqspi = spi_controller_get_devdata(host); - spi_controller_get(host); - spi_unregister_controller(host); pm_runtime_disable(&pdev->dev); @@ -445,8 +435,6 @@ static void mcfqspi_remove(struct platform_device *pdev) mcfqspi_wr_qmr(mcfqspi, MCFQSPI_QMR_MSTR); mcfqspi_cs_teardown(mcfqspi); - - spi_controller_put(host); } #ifdef CONFIG_PM_SLEEP From 6bd505e710aa612cf57f7894a10d00c99d600226 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 29 Apr 2026 11:13:23 +0200 Subject: [PATCH 026/118] spi: dln2: switch to managed controller allocation Switch to device managed controller allocation to simplify error handling and to avoid having to take another reference during deregistration. Signed-off-by: Johan Hovold Link: https://patch.msgid.link/20260429091333.165363-10-johan@kernel.org Signed-off-by: Mark Brown --- drivers/spi/spi-dln2.c | 26 +++++++++----------------- 1 file changed, 9 insertions(+), 17 deletions(-) diff --git a/drivers/spi/spi-dln2.c b/drivers/spi/spi-dln2.c index 392f0d05f508..8333dda7d1e8 100644 --- a/drivers/spi/spi-dln2.c +++ b/drivers/spi/spi-dln2.c @@ -684,7 +684,7 @@ static int dln2_spi_probe(struct platform_device *pdev) struct dln2_platform_data *pdata = dev_get_platdata(&pdev->dev); int ret; - host = spi_alloc_host(&pdev->dev, sizeof(*dln2)); + host = devm_spi_alloc_host(&pdev->dev, sizeof(*dln2)); if (!host) return -ENOMEM; @@ -693,10 +693,8 @@ static int dln2_spi_probe(struct platform_device *pdev) dln2 = spi_controller_get_devdata(host); dln2->buf = devm_kmalloc(&pdev->dev, DLN2_SPI_BUF_SIZE, GFP_KERNEL); - if (!dln2->buf) { - ret = -ENOMEM; - goto exit_free_host; - } + if (!dln2->buf) + return -ENOMEM; dln2->host = host; dln2->pdev = pdev; @@ -709,13 +707,13 @@ static int dln2_spi_probe(struct platform_device *pdev) ret = dln2_spi_enable(dln2, false); if (ret < 0) { dev_err(&pdev->dev, "Failed to disable SPI module\n"); - goto exit_free_host; + return ret; } ret = dln2_spi_get_cs_num(dln2, &host->num_chipselect); if (ret < 0) { dev_err(&pdev->dev, "Failed to get number of CS pins\n"); - goto exit_free_host; + return ret; } ret = dln2_spi_get_speed_range(dln2, @@ -723,20 +721,20 @@ static int dln2_spi_probe(struct platform_device *pdev) &host->max_speed_hz); if (ret < 0) { dev_err(&pdev->dev, "Failed to read bus min/max freqs\n"); - goto exit_free_host; + return ret; } ret = dln2_spi_get_supported_frame_sizes(dln2, &host->bits_per_word_mask); if (ret < 0) { dev_err(&pdev->dev, "Failed to read supported frame sizes\n"); - goto exit_free_host; + return ret; } ret = dln2_spi_cs_enable_all(dln2, true); if (ret < 0) { dev_err(&pdev->dev, "Failed to enable CS pins\n"); - goto exit_free_host; + return ret; } host->bus_num = -1; @@ -749,7 +747,7 @@ static int dln2_spi_probe(struct platform_device *pdev) ret = dln2_spi_enable(dln2, true); if (ret < 0) { dev_err(&pdev->dev, "Failed to enable SPI module\n"); - goto exit_free_host; + return ret; } pm_runtime_set_autosuspend_delay(&pdev->dev, @@ -772,8 +770,6 @@ static int dln2_spi_probe(struct platform_device *pdev) if (dln2_spi_enable(dln2, false) < 0) dev_err(&pdev->dev, "Failed to disable SPI module\n"); -exit_free_host: - spi_controller_put(host); return ret; } @@ -783,16 +779,12 @@ static void dln2_spi_remove(struct platform_device *pdev) struct spi_controller *host = platform_get_drvdata(pdev); struct dln2_spi *dln2 = spi_controller_get_devdata(host); - spi_controller_get(host); - spi_unregister_controller(host); pm_runtime_disable(&pdev->dev); if (dln2_spi_enable(dln2, false) < 0) dev_err(&pdev->dev, "Failed to disable SPI module\n"); - - spi_controller_put(host); } #ifdef CONFIG_PM_SLEEP From 01500b2cb05ab60b6eb7b8a24a56bad45296227c Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 29 Apr 2026 11:13:24 +0200 Subject: [PATCH 027/118] spi: ep93xx: switch to managed controller allocation Switch to device managed controller allocation to simplify error handling and to avoid having to take another reference during deregistration. Signed-off-by: Johan Hovold Link: https://patch.msgid.link/20260429091333.165363-11-johan@kernel.org Signed-off-by: Mark Brown --- drivers/spi/spi-ep93xx.c | 22 +++++++--------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/drivers/spi/spi-ep93xx.c b/drivers/spi/spi-ep93xx.c index db50018050e5..d7e82d80c35a 100644 --- a/drivers/spi/spi-ep93xx.c +++ b/drivers/spi/spi-ep93xx.c @@ -629,7 +629,7 @@ static int ep93xx_spi_probe(struct platform_device *pdev) if (irq < 0) return irq; - host = spi_alloc_host(&pdev->dev, sizeof(*espi)); + host = devm_spi_alloc_host(&pdev->dev, sizeof(*espi)); if (!host) return -ENOMEM; @@ -654,8 +654,7 @@ static int ep93xx_spi_probe(struct platform_device *pdev) espi->clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(espi->clk)) { dev_err(&pdev->dev, "unable to get spi clock\n"); - error = PTR_ERR(espi->clk); - goto fail_release_host; + return PTR_ERR(espi->clk); } /* @@ -666,22 +665,21 @@ static int ep93xx_spi_probe(struct platform_device *pdev) host->min_speed_hz = clk_get_rate(espi->clk) / (254 * 256); espi->mmio = devm_platform_get_and_ioremap_resource(pdev, 0, &res); - if (IS_ERR(espi->mmio)) { - error = PTR_ERR(espi->mmio); - goto fail_release_host; - } + if (IS_ERR(espi->mmio)) + return PTR_ERR(espi->mmio); + espi->sspdr_phys = res->start + SSPDR; error = devm_request_irq(&pdev->dev, irq, ep93xx_spi_interrupt, 0, "ep93xx-spi", host); if (error) { dev_err(&pdev->dev, "failed to request irq\n"); - goto fail_release_host; + return error; } error = ep93xx_spi_setup_dma(&pdev->dev, espi); if (error == -EPROBE_DEFER) - goto fail_release_host; + return error; if (error) dev_warn(&pdev->dev, "DMA setup failed. Falling back to PIO\n"); @@ -702,8 +700,6 @@ static int ep93xx_spi_probe(struct platform_device *pdev) fail_free_dma: ep93xx_spi_release_dma(espi); -fail_release_host: - spi_controller_put(host); return error; } @@ -713,13 +709,9 @@ static void ep93xx_spi_remove(struct platform_device *pdev) struct spi_controller *host = platform_get_drvdata(pdev); struct ep93xx_spi *espi = spi_controller_get_devdata(host); - spi_controller_get(host); - spi_unregister_controller(host); ep93xx_spi_release_dma(espi); - - spi_controller_put(host); } static const struct of_device_id ep93xx_spi_of_ids[] = { From 6afe041c2ce9068b2b6c64eddb5537911752050c Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 29 Apr 2026 11:13:25 +0200 Subject: [PATCH 028/118] spi: fsl: switch to managed controller allocation Switch to device managed controller allocation to simplify error handling and to avoid having to take another reference during deregistration. Signed-off-by: Johan Hovold Link: https://patch.msgid.link/20260429091333.165363-12-johan@kernel.org Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-spi.c | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/drivers/spi/spi-fsl-spi.c b/drivers/spi/spi-fsl-spi.c index 1252c41c206f..e45816ef7b65 100644 --- a/drivers/spi/spi-fsl-spi.c +++ b/drivers/spi/spi-fsl-spi.c @@ -535,7 +535,7 @@ static struct spi_controller *fsl_spi_probe(struct device *dev, u32 regval; int ret = 0; - host = spi_alloc_host(dev, sizeof(struct mpc8xxx_spi)); + host = devm_spi_alloc_host(dev, sizeof(struct mpc8xxx_spi)); if (host == NULL) { ret = -ENOMEM; goto err; @@ -559,7 +559,7 @@ static struct spi_controller *fsl_spi_probe(struct device *dev, ret = fsl_spi_cpm_init(mpc8xxx_spi); if (ret) - goto err_cpm_init; + goto err; mpc8xxx_spi->reg_base = devm_ioremap_resource(dev, mem); if (IS_ERR(mpc8xxx_spi->reg_base)) { @@ -625,8 +625,6 @@ static struct spi_controller *fsl_spi_probe(struct device *dev, err_probe: fsl_spi_cpm_free(mpc8xxx_spi); -err_cpm_init: - spi_controller_put(host); err: return ERR_PTR(ret); } @@ -705,13 +703,9 @@ static void of_fsl_spi_remove(struct platform_device *ofdev) struct spi_controller *host = platform_get_drvdata(ofdev); struct mpc8xxx_spi *mpc8xxx_spi = spi_controller_get_devdata(host); - spi_controller_get(host); - spi_unregister_controller(host); fsl_spi_cpm_free(mpc8xxx_spi); - - spi_controller_put(host); } static struct platform_driver of_fsl_spi_driver = { @@ -757,13 +751,9 @@ static void plat_mpc8xxx_spi_remove(struct platform_device *pdev) struct spi_controller *host = platform_get_drvdata(pdev); struct mpc8xxx_spi *mpc8xxx_spi = spi_controller_get_devdata(host); - spi_controller_get(host); - spi_unregister_controller(host); fsl_spi_cpm_free(mpc8xxx_spi); - - spi_controller_put(host); } MODULE_ALIAS("platform:mpc8xxx_spi"); From 9979501afa4a94e71e0c127bbbcd70bcdf397dbc Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 29 Apr 2026 11:13:26 +0200 Subject: [PATCH 029/118] spi: fsl-espi: switch to managed controller allocation Switch to device managed controller allocation to simplify error handling and to avoid having to take another reference during deregistration. Signed-off-by: Johan Hovold Link: https://patch.msgid.link/20260429091333.165363-13-johan@kernel.org Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-espi.c | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/drivers/spi/spi-fsl-espi.c b/drivers/spi/spi-fsl-espi.c index 45b9974ae911..f560bd537f7d 100644 --- a/drivers/spi/spi-fsl-espi.c +++ b/drivers/spi/spi-fsl-espi.c @@ -667,7 +667,7 @@ static int fsl_espi_probe(struct device *dev, struct resource *mem, struct fsl_espi *espi; int ret; - host = spi_alloc_host(dev, sizeof(struct fsl_espi)); + host = devm_spi_alloc_host(dev, sizeof(struct fsl_espi)); if (!host) return -ENOMEM; @@ -690,8 +690,7 @@ static int fsl_espi_probe(struct device *dev, struct resource *mem, espi->spibrg = fsl_get_sys_freq(); if (espi->spibrg == -1) { dev_err(dev, "Can't get sys frequency!\n"); - ret = -EINVAL; - goto err_probe; + return -EINVAL; } /* determined by clock divider fields DIV16/PM in register SPMODEx */ host->min_speed_hz = DIV_ROUND_UP(espi->spibrg, 4 * 16 * 16); @@ -700,15 +699,13 @@ static int fsl_espi_probe(struct device *dev, struct resource *mem, init_completion(&espi->done); espi->reg_base = devm_ioremap_resource(dev, mem); - if (IS_ERR(espi->reg_base)) { - ret = PTR_ERR(espi->reg_base); - goto err_probe; - } + if (IS_ERR(espi->reg_base)) + return PTR_ERR(espi->reg_base); /* Register for SPI Interrupt */ ret = devm_request_irq(dev, irq, fsl_espi_irq, 0, "fsl_espi", espi); if (ret) - goto err_probe; + return ret; fsl_espi_init_regs(dev, true); @@ -732,8 +729,7 @@ static int fsl_espi_probe(struct device *dev, struct resource *mem, pm_runtime_put_noidle(dev); pm_runtime_disable(dev); pm_runtime_set_suspended(dev); -err_probe: - spi_controller_put(host); + return ret; } @@ -784,13 +780,9 @@ static void of_fsl_espi_remove(struct platform_device *dev) { struct spi_controller *host = platform_get_drvdata(dev); - spi_controller_get(host); - spi_unregister_controller(host); pm_runtime_disable(&dev->dev); - - spi_controller_put(host); } #ifdef CONFIG_PM_SLEEP From 8015cac85a698d471f100ca8602c27448bee146b Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 29 Apr 2026 11:13:27 +0200 Subject: [PATCH 030/118] spi: img-spfi: switch to managed controller allocation Switch to device managed controller allocation to simplify error handling and to avoid having to take another reference during deregistration. Signed-off-by: Johan Hovold Link: https://patch.msgid.link/20260429091333.165363-14-johan@kernel.org Signed-off-by: Mark Brown --- drivers/spi/spi-img-spfi.c | 40 ++++++++++++++------------------------ 1 file changed, 15 insertions(+), 25 deletions(-) diff --git a/drivers/spi/spi-img-spfi.c b/drivers/spi/spi-img-spfi.c index 57625a3ce2f2..aec724e3f824 100644 --- a/drivers/spi/spi-img-spfi.c +++ b/drivers/spi/spi-img-spfi.c @@ -530,7 +530,7 @@ static int img_spfi_probe(struct platform_device *pdev) int ret; u32 max_speed_hz; - host = spi_alloc_host(&pdev->dev, sizeof(*spfi)); + host = devm_spi_alloc_host(&pdev->dev, sizeof(*spfi)); if (!host) return -ENOMEM; platform_set_drvdata(pdev, host); @@ -541,36 +541,32 @@ static int img_spfi_probe(struct platform_device *pdev) spin_lock_init(&spfi->lock); spfi->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res); - if (IS_ERR(spfi->regs)) { - ret = PTR_ERR(spfi->regs); - goto put_spi; - } + if (IS_ERR(spfi->regs)) + return PTR_ERR(spfi->regs); + spfi->phys = res->start; spfi->irq = platform_get_irq(pdev, 0); - if (spfi->irq < 0) { - ret = spfi->irq; - goto put_spi; - } + if (spfi->irq < 0) + return spfi->irq; + ret = devm_request_irq(spfi->dev, spfi->irq, img_spfi_irq, IRQ_TYPE_LEVEL_HIGH, dev_name(spfi->dev), spfi); if (ret) - goto put_spi; + return ret; spfi->sys_clk = devm_clk_get(spfi->dev, "sys"); - if (IS_ERR(spfi->sys_clk)) { - ret = PTR_ERR(spfi->sys_clk); - goto put_spi; - } + if (IS_ERR(spfi->sys_clk)) + return PTR_ERR(spfi->sys_clk); + spfi->spfi_clk = devm_clk_get(spfi->dev, "spfi"); - if (IS_ERR(spfi->spfi_clk)) { - ret = PTR_ERR(spfi->spfi_clk); - goto put_spi; - } + if (IS_ERR(spfi->spfi_clk)) + return PTR_ERR(spfi->spfi_clk); ret = clk_prepare_enable(spfi->sys_clk); if (ret) - goto put_spi; + return ret; + ret = clk_prepare_enable(spfi->spfi_clk); if (ret) goto disable_pclk; @@ -658,8 +654,6 @@ static int img_spfi_probe(struct platform_device *pdev) clk_disable_unprepare(spfi->spfi_clk); disable_pclk: clk_disable_unprepare(spfi->sys_clk); -put_spi: - spi_controller_put(host); return ret; } @@ -669,8 +663,6 @@ static void img_spfi_remove(struct platform_device *pdev) struct spi_controller *host = platform_get_drvdata(pdev); struct img_spfi *spfi = spi_controller_get_devdata(host); - spi_controller_get(host); - spi_unregister_controller(host); if (spfi->tx_ch) @@ -683,8 +675,6 @@ static void img_spfi_remove(struct platform_device *pdev) clk_disable_unprepare(spfi->spfi_clk); clk_disable_unprepare(spfi->sys_clk); } - - spi_controller_put(host); } #ifdef CONFIG_PM From 8cecd707d5358b88bcd0ae49f2f38e143d6bf7da Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 29 Apr 2026 11:13:28 +0200 Subject: [PATCH 031/118] spi: lantiq-ssc: switch to managed controller allocation Switch to device managed controller allocation to simplify error handling and to avoid having to take another reference during deregistration. Signed-off-by: Johan Hovold Link: https://patch.msgid.link/20260429091333.165363-15-johan@kernel.org Signed-off-by: Mark Brown --- drivers/spi/spi-lantiq-ssc.c | 28 ++++++++-------------------- 1 file changed, 8 insertions(+), 20 deletions(-) diff --git a/drivers/spi/spi-lantiq-ssc.c b/drivers/spi/spi-lantiq-ssc.c index 75b9af8cb5db..ecae50a50b14 100644 --- a/drivers/spi/spi-lantiq-ssc.c +++ b/drivers/spi/spi-lantiq-ssc.c @@ -913,7 +913,7 @@ static int lantiq_ssc_probe(struct platform_device *pdev) hwcfg = of_device_get_match_data(dev); - host = spi_alloc_host(dev, sizeof(struct lantiq_ssc_spi)); + host = devm_spi_alloc_host(dev, sizeof(struct lantiq_ssc_spi)); if (!host) return -ENOMEM; @@ -923,20 +923,16 @@ static int lantiq_ssc_probe(struct platform_device *pdev) spi->hwcfg = hwcfg; platform_set_drvdata(pdev, spi); spi->regbase = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(spi->regbase)) { - err = PTR_ERR(spi->regbase); - goto err_host_put; - } + if (IS_ERR(spi->regbase)) + return PTR_ERR(spi->regbase); err = hwcfg->cfg_irq(pdev, spi); if (err) - goto err_host_put; + return err; spi->spi_clk = devm_clk_get_enabled(dev, "gate"); - if (IS_ERR(spi->spi_clk)) { - err = PTR_ERR(spi->spi_clk); - goto err_host_put; - } + if (IS_ERR(spi->spi_clk)) + return PTR_ERR(spi->spi_clk); /* * Use the old clk_get_fpi() function on Lantiq platform, till it @@ -947,10 +943,8 @@ static int lantiq_ssc_probe(struct platform_device *pdev) #else spi->fpi_clk = clk_get(dev, "freq"); #endif - if (IS_ERR(spi->fpi_clk)) { - err = PTR_ERR(spi->fpi_clk); - goto err_host_put; - } + if (IS_ERR(spi->fpi_clk)) + return PTR_ERR(spi->fpi_clk); num_cs = 8; of_property_read_u32(pdev->dev.of_node, "num-cs", &num_cs); @@ -1006,8 +1000,6 @@ static int lantiq_ssc_probe(struct platform_device *pdev) destroy_workqueue(spi->wq); err_clk_put: clk_put(spi->fpi_clk); -err_host_put: - spi_controller_put(host); return err; } @@ -1016,8 +1008,6 @@ static void lantiq_ssc_remove(struct platform_device *pdev) { struct lantiq_ssc_spi *spi = platform_get_drvdata(pdev); - spi_controller_get(spi->host); - spi_unregister_controller(spi->host); lantiq_ssc_writel(spi, 0, LTQ_SPI_IRNEN); @@ -1028,8 +1018,6 @@ static void lantiq_ssc_remove(struct platform_device *pdev) destroy_workqueue(spi->wq); clk_put(spi->fpi_clk); - - spi_controller_put(spi->host); } static struct platform_driver lantiq_ssc_driver = { From 69fb8784c81e0699fbea2ee81f65d1a23bd11649 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 29 Apr 2026 11:13:29 +0200 Subject: [PATCH 032/118] spi: meson-spicc: switch to managed controller allocation Switch to device managed controller allocation to simplify error handling and to avoid having to take another reference during deregistration. Signed-off-by: Johan Hovold Link: https://patch.msgid.link/20260429091333.165363-16-johan@kernel.org Signed-off-by: Mark Brown --- drivers/spi/spi-meson-spicc.c | 43 +++++++++++------------------------ 1 file changed, 13 insertions(+), 30 deletions(-) diff --git a/drivers/spi/spi-meson-spicc.c b/drivers/spi/spi-meson-spicc.c index b80f9f457b66..8ad2ad0c97e2 100644 --- a/drivers/spi/spi-meson-spicc.c +++ b/drivers/spi/spi-meson-spicc.c @@ -982,7 +982,7 @@ static int meson_spicc_probe(struct platform_device *pdev) struct meson_spicc_device *spicc; int ret, irq; - host = spi_alloc_host(&pdev->dev, sizeof(*spicc)); + host = devm_spi_alloc_host(&pdev->dev, sizeof(*spicc)); if (!host) { dev_err(&pdev->dev, "host allocation failed\n"); return -ENOMEM; @@ -993,8 +993,7 @@ static int meson_spicc_probe(struct platform_device *pdev) spicc->data = of_device_get_match_data(&pdev->dev); if (!spicc->data) { dev_err(&pdev->dev, "failed to get match data\n"); - ret = -EINVAL; - goto out_host; + return -EINVAL; } spicc->pdev = pdev; @@ -1005,8 +1004,7 @@ static int meson_spicc_probe(struct platform_device *pdev) spicc->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(spicc->base)) { dev_err(&pdev->dev, "io resource mapping failed\n"); - ret = PTR_ERR(spicc->base); - goto out_host; + return PTR_ERR(spicc->base); } /* Set master mode and enable controller */ @@ -1017,39 +1015,33 @@ static int meson_spicc_probe(struct platform_device *pdev) writel_relaxed(0, spicc->base + SPICC_INTREG); irq = platform_get_irq(pdev, 0); - if (irq < 0) { - ret = irq; - goto out_host; - } + if (irq < 0) + return irq; ret = devm_request_irq(&pdev->dev, irq, meson_spicc_irq, 0, NULL, spicc); if (ret) { dev_err(&pdev->dev, "irq request failed\n"); - goto out_host; + return ret; } spicc->core = devm_clk_get_enabled(&pdev->dev, "core"); if (IS_ERR(spicc->core)) { dev_err(&pdev->dev, "core clock request failed\n"); - ret = PTR_ERR(spicc->core); - goto out_host; + return PTR_ERR(spicc->core); } if (spicc->data->has_pclk) { spicc->pclk = devm_clk_get_enabled(&pdev->dev, "pclk"); if (IS_ERR(spicc->pclk)) { dev_err(&pdev->dev, "pclk clock request failed\n"); - ret = PTR_ERR(spicc->pclk); - goto out_host; + return PTR_ERR(spicc->pclk); } } spicc->pinctrl = devm_pinctrl_get(&pdev->dev); - if (IS_ERR(spicc->pinctrl)) { - ret = PTR_ERR(spicc->pinctrl); - goto out_host; - } + if (IS_ERR(spicc->pinctrl)) + return PTR_ERR(spicc->pinctrl); device_reset_optional(&pdev->dev); @@ -1070,43 +1062,34 @@ static int meson_spicc_probe(struct platform_device *pdev) ret = meson_spicc_pow2_clk_init(spicc); if (ret) { dev_err(&pdev->dev, "pow2 clock registration failed\n"); - goto out_host; + return ret; } if (spicc->data->has_enhance_clk_div) { ret = meson_spicc_enh_clk_init(spicc); if (ret) { dev_err(&pdev->dev, "clock registration failed\n"); - goto out_host; + return ret; } } ret = spi_register_controller(host); if (ret) { dev_err(&pdev->dev, "spi registration failed\n"); - goto out_host; + return ret; } return 0; - -out_host: - spi_controller_put(host); - - return ret; } static void meson_spicc_remove(struct platform_device *pdev) { struct meson_spicc_device *spicc = platform_get_drvdata(pdev); - spi_controller_get(spicc->host); - spi_unregister_controller(spicc->host); /* Disable SPI */ writel(0, spicc->base + SPICC_CONREG); - - spi_controller_put(spicc->host); } static const struct meson_spicc_data meson_spicc_gx_data = { From f7c857559eb20a73d2ec6fe79808cdb1f4afabfa Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 29 Apr 2026 11:13:30 +0200 Subject: [PATCH 033/118] spi: mxs: switch to managed controller allocation Switch to device managed controller allocation to simplify error handling and to avoid having to take another reference during deregistration. Signed-off-by: Johan Hovold Link: https://patch.msgid.link/20260429091333.165363-17-johan@kernel.org Signed-off-by: Mark Brown --- drivers/spi/spi-mxs.c | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/drivers/spi/spi-mxs.c b/drivers/spi/spi-mxs.c index 0164e04d59a1..fda1d260b296 100644 --- a/drivers/spi/spi-mxs.c +++ b/drivers/spi/spi-mxs.c @@ -563,7 +563,7 @@ static int mxs_spi_probe(struct platform_device *pdev) if (ret) clk_freq = clk_freq_default; - host = spi_alloc_host(&pdev->dev, sizeof(*spi)); + host = devm_spi_alloc_host(&pdev->dev, sizeof(*spi)); if (!host) return -ENOMEM; @@ -589,13 +589,12 @@ static int mxs_spi_probe(struct platform_device *pdev) ret = devm_request_irq(&pdev->dev, irq_err, mxs_ssp_irq_handler, 0, dev_name(&pdev->dev), ssp); if (ret) - goto out_host_free; + return ret; ssp->dmach = dma_request_chan(&pdev->dev, "rx-tx"); if (IS_ERR(ssp->dmach)) { dev_err(ssp->dev, "Failed to request DMA\n"); - ret = PTR_ERR(ssp->dmach); - goto out_host_free; + return PTR_ERR(ssp->dmach); } pm_runtime_enable(ssp->dev); @@ -635,8 +634,7 @@ static int mxs_spi_probe(struct platform_device *pdev) pm_runtime_disable(ssp->dev); out_dma_release: dma_release_channel(ssp->dmach); -out_host_free: - spi_controller_put(host); + return ret; } @@ -650,8 +648,6 @@ static void mxs_spi_remove(struct platform_device *pdev) spi = spi_controller_get_devdata(host); ssp = &spi->ssp; - spi_controller_get(host); - spi_unregister_controller(host); pm_runtime_disable(&pdev->dev); @@ -659,8 +655,6 @@ static void mxs_spi_remove(struct platform_device *pdev) mxs_spi_runtime_suspend(&pdev->dev); dma_release_channel(ssp->dmach); - - spi_controller_put(host); } static struct platform_driver mxs_spi_driver = { From c0c6875f0b7de6094f15ffd5b1dcebb3cebe53e1 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 29 Apr 2026 11:13:31 +0200 Subject: [PATCH 034/118] spi: npcm-pspi: switch to managed controller allocation Switch to device managed controller allocation to simplify error handling and to avoid having to take another reference during deregistration. Signed-off-by: Johan Hovold Link: https://patch.msgid.link/20260429091333.165363-18-johan@kernel.org Signed-off-by: Mark Brown --- drivers/spi/spi-npcm-pspi.c | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/drivers/spi/spi-npcm-pspi.c b/drivers/spi/spi-npcm-pspi.c index cffef0a5977d..a437a30d636c 100644 --- a/drivers/spi/spi-npcm-pspi.c +++ b/drivers/spi/spi-npcm-pspi.c @@ -345,7 +345,7 @@ static int npcm_pspi_probe(struct platform_device *pdev) int irq; int ret; - host = spi_alloc_host(&pdev->dev, sizeof(*priv)); + host = devm_spi_alloc_host(&pdev->dev, sizeof(*priv)); if (!host) return -ENOMEM; @@ -356,21 +356,18 @@ static int npcm_pspi_probe(struct platform_device *pdev) priv->is_save_param = false; priv->base = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(priv->base)) { - ret = PTR_ERR(priv->base); - goto out_host_put; - } + if (IS_ERR(priv->base)) + return PTR_ERR(priv->base); priv->clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(priv->clk)) { dev_err(&pdev->dev, "failed to get clock\n"); - ret = PTR_ERR(priv->clk); - goto out_host_put; + return PTR_ERR(priv->clk); } ret = clk_prepare_enable(priv->clk); if (ret) - goto out_host_put; + return ret; irq = platform_get_irq(pdev, 0); if (irq < 0) { @@ -424,8 +421,6 @@ static int npcm_pspi_probe(struct platform_device *pdev) out_disable_clk: clk_disable_unprepare(priv->clk); -out_host_put: - spi_controller_put(host); return ret; } @@ -434,14 +429,10 @@ static void npcm_pspi_remove(struct platform_device *pdev) struct spi_controller *host = platform_get_drvdata(pdev); struct npcm_pspi *priv = spi_controller_get_devdata(host); - spi_controller_get(host); - spi_unregister_controller(host); npcm_pspi_reset_hw(priv); clk_disable_unprepare(priv->clk); - - spi_controller_put(host); } static const struct of_device_id npcm_pspi_match[] = { From bdab3707ddfdf89a054b2372a984316c84d3ce43 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 29 Apr 2026 11:13:33 +0200 Subject: [PATCH 035/118] spi: orion: switch to managed controller allocation Switch to device managed controller allocation to simplify error handling and to avoid having to take another reference during deregistration. Signed-off-by: Johan Hovold Link: https://patch.msgid.link/20260429091333.165363-20-johan@kernel.org Signed-off-by: Mark Brown --- drivers/spi/spi-orion.c | 26 ++++++++------------------ 1 file changed, 8 insertions(+), 18 deletions(-) diff --git a/drivers/spi/spi-orion.c b/drivers/spi/spi-orion.c index 64bf215c1804..265708a94984 100644 --- a/drivers/spi/spi-orion.c +++ b/drivers/spi/spi-orion.c @@ -651,11 +651,9 @@ static int orion_spi_probe(struct platform_device *pdev) struct device_node *np; int status; - host = spi_alloc_host(&pdev->dev, sizeof(*spi)); - if (host == NULL) { - dev_dbg(&pdev->dev, "host allocation failed\n"); + host = devm_spi_alloc_host(&pdev->dev, sizeof(*spi)); + if (host == NULL) return -ENOMEM; - } if (pdev->id != -1) host->bus_num = pdev->id; @@ -689,17 +687,14 @@ static int orion_spi_probe(struct platform_device *pdev) spi->devdata = devdata; spi->clk = devm_clk_get_enabled(&pdev->dev, NULL); - if (IS_ERR(spi->clk)) { - status = PTR_ERR(spi->clk); - goto out; - } + if (IS_ERR(spi->clk)) + return PTR_ERR(spi->clk); /* The following clock is only used by some SoCs */ spi->axi_clk = devm_clk_get(&pdev->dev, "axi"); - if (PTR_ERR(spi->axi_clk) == -EPROBE_DEFER) { - status = -EPROBE_DEFER; - goto out; - } + if (PTR_ERR(spi->axi_clk) == -EPROBE_DEFER) + return -EPROBE_DEFER; + if (!IS_ERR(spi->axi_clk)) clk_prepare_enable(spi->axi_clk); @@ -796,8 +791,7 @@ static int orion_spi_probe(struct platform_device *pdev) pm_runtime_dont_use_autosuspend(&pdev->dev); out_rel_axi_clk: clk_disable_unprepare(spi->axi_clk); -out: - spi_controller_put(host); + return status; } @@ -807,15 +801,11 @@ 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_controller_put(host); - pm_runtime_disable(&pdev->dev); pm_runtime_put_noidle(&pdev->dev); pm_runtime_set_suspended(&pdev->dev); From caf2fd997bf36728661612c03a74bd2bf23de9e4 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 30 Apr 2026 14:01:58 +0200 Subject: [PATCH 036/118] spi: omap2-mcspi: switch to managed controller allocation Switch to device managed controller allocation to simplify error handling and to avoid having to take another reference during deregistration. Signed-off-by: Johan Hovold Link: https://patch.msgid.link/20260430120200.249323-2-johan@kernel.org Signed-off-by: Mark Brown --- drivers/spi/spi-omap2-mcspi.c | 23 ++++++++--------------- 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/drivers/spi/spi-omap2-mcspi.c b/drivers/spi/spi-omap2-mcspi.c index 56b30ff58771..60c05eb91781 100644 --- a/drivers/spi/spi-omap2-mcspi.c +++ b/drivers/spi/spi-omap2-mcspi.c @@ -1484,9 +1484,9 @@ static int omap2_mcspi_probe(struct platform_device *pdev) const struct of_device_id *match; if (of_property_read_bool(node, "spi-slave")) - ctlr = spi_alloc_target(&pdev->dev, sizeof(*mcspi)); + ctlr = devm_spi_alloc_target(&pdev->dev, sizeof(*mcspi)); else - ctlr = spi_alloc_host(&pdev->dev, sizeof(*mcspi)); + ctlr = devm_spi_alloc_host(&pdev->dev, sizeof(*mcspi)); if (!ctlr) return -ENOMEM; @@ -1530,10 +1530,9 @@ static int omap2_mcspi_probe(struct platform_device *pdev) } mcspi->base = devm_platform_get_and_ioremap_resource(pdev, 0, &r); - if (IS_ERR(mcspi->base)) { - status = PTR_ERR(mcspi->base); - goto free_ctlr; - } + if (IS_ERR(mcspi->base)) + return PTR_ERR(mcspi->base); + mcspi->phys = r->start + regs_offset; mcspi->base += regs_offset; @@ -1544,10 +1543,8 @@ static int omap2_mcspi_probe(struct platform_device *pdev) mcspi->dma_channels = devm_kcalloc(&pdev->dev, ctlr->num_chipselect, sizeof(struct omap2_mcspi_dma), GFP_KERNEL); - if (mcspi->dma_channels == NULL) { - status = -ENOMEM; - goto free_ctlr; - } + if (mcspi->dma_channels == NULL) + return -ENOMEM; for (i = 0; i < ctlr->num_chipselect; i++) { sprintf(mcspi->dma_channels[i].dma_rx_ch_name, "rx%d", i); @@ -1604,7 +1601,7 @@ static int omap2_mcspi_probe(struct platform_device *pdev) pm_runtime_disable(&pdev->dev); free_ctlr: omap2_mcspi_release_dma(ctlr); - spi_controller_put(ctlr); + return status; } @@ -1613,8 +1610,6 @@ static void omap2_mcspi_remove(struct platform_device *pdev) struct spi_controller *ctlr = platform_get_drvdata(pdev); struct omap2_mcspi *mcspi = spi_controller_get_devdata(ctlr); - spi_controller_get(ctlr); - spi_unregister_controller(ctlr); omap2_mcspi_release_dma(ctlr); @@ -1622,8 +1617,6 @@ static void omap2_mcspi_remove(struct platform_device *pdev) pm_runtime_dont_use_autosuspend(mcspi->dev); pm_runtime_put_sync(mcspi->dev); pm_runtime_disable(&pdev->dev); - - spi_controller_put(ctlr); } /* work with hotplug and coldplug */ From 186fda6ee1ac5f61b847ab8d65560252c8eae06f Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 30 Apr 2026 14:01:59 +0200 Subject: [PATCH 037/118] spi: omap2-mcspi: clean up error labels Clean up the error labels by adding a common prefix and naming them after what they do. Signed-off-by: Johan Hovold Link: https://patch.msgid.link/20260430120200.249323-3-johan@kernel.org Signed-off-by: Mark Brown --- drivers/spi/spi-omap2-mcspi.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/drivers/spi/spi-omap2-mcspi.c b/drivers/spi/spi-omap2-mcspi.c index 60c05eb91781..59ebdf7edbd2 100644 --- a/drivers/spi/spi-omap2-mcspi.c +++ b/drivers/spi/spi-omap2-mcspi.c @@ -1553,26 +1553,27 @@ static int omap2_mcspi_probe(struct platform_device *pdev) status = omap2_mcspi_request_dma(mcspi, &mcspi->dma_channels[i]); if (status == -EPROBE_DEFER) - goto free_ctlr; + goto err_release_dma; } status = platform_get_irq(pdev, 0); if (status < 0) - goto free_ctlr; + goto err_release_dma; + init_completion(&mcspi->txdone); status = devm_request_irq(&pdev->dev, status, omap2_mcspi_irq_handler, 0, pdev->name, mcspi); if (status) { dev_err(&pdev->dev, "Cannot request IRQ"); - goto free_ctlr; + goto err_release_dma; } mcspi->ref_clk = devm_clk_get_optional_enabled(&pdev->dev, NULL); if (IS_ERR(mcspi->ref_clk)) { status = PTR_ERR(mcspi->ref_clk); dev_err_probe(&pdev->dev, status, "Failed to get ref_clk"); - goto free_ctlr; + goto err_release_dma; } if (mcspi->ref_clk) mcspi->ref_clk_hz = clk_get_rate(mcspi->ref_clk); @@ -1587,19 +1588,19 @@ static int omap2_mcspi_probe(struct platform_device *pdev) status = omap2_mcspi_controller_setup(mcspi); if (status < 0) - goto disable_pm; + goto err_disable_rpm; status = spi_register_controller(ctlr); if (status < 0) - goto disable_pm; + goto err_disable_rpm; return status; -disable_pm: +err_disable_rpm: pm_runtime_dont_use_autosuspend(&pdev->dev); pm_runtime_put_sync(&pdev->dev); pm_runtime_disable(&pdev->dev); -free_ctlr: +err_release_dma: omap2_mcspi_release_dma(ctlr); return status; From 46bd1fafc494744cd099f605b364f6fbb097069e Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 30 Apr 2026 14:02:00 +0200 Subject: [PATCH 038/118] spi: omap2-mcspi: clean up probe return value Return explicit zero on successful probe to clearly separate the success and error paths and make the code more readable. Signed-off-by: Johan Hovold Link: https://patch.msgid.link/20260430120200.249323-4-johan@kernel.org Signed-off-by: Mark Brown --- drivers/spi/spi-omap2-mcspi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/spi/spi-omap2-mcspi.c b/drivers/spi/spi-omap2-mcspi.c index 59ebdf7edbd2..d53f98aa0aac 100644 --- a/drivers/spi/spi-omap2-mcspi.c +++ b/drivers/spi/spi-omap2-mcspi.c @@ -1594,7 +1594,7 @@ static int omap2_mcspi_probe(struct platform_device *pdev) if (status < 0) goto err_disable_rpm; - return status; + return 0; err_disable_rpm: pm_runtime_dont_use_autosuspend(&pdev->dev); From 0065dc1fed2a87f815065b1348732ba45469ea83 Mon Sep 17 00:00:00 2001 From: Viken Dadhaniya Date: Wed, 29 Apr 2026 22:31:36 +0530 Subject: [PATCH 039/118] spi: dt-bindings: qcom,spi-qcom-qspi: Add qcom,qcs615-qspi compatible Add support for the QSPI controller on QCS615 SoC. Move allOf section after required properties and add if:then constraint to require minimum 2 interconnects for qcs615 variant. Reviewed-by: Krzysztof Kozlowski Signed-off-by: Viken Dadhaniya Link: https://patch.msgid.link/20260429-spi-nor-v5-1-993016c9711e@oss.qualcomm.com Signed-off-by: Mark Brown --- .../bindings/spi/qcom,spi-qcom-qspi.yaml | 21 ++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/Documentation/devicetree/bindings/spi/qcom,spi-qcom-qspi.yaml b/Documentation/devicetree/bindings/spi/qcom,spi-qcom-qspi.yaml index 1696ac46a660..ee2199027e89 100644 --- a/Documentation/devicetree/bindings/spi/qcom,spi-qcom-qspi.yaml +++ b/Documentation/devicetree/bindings/spi/qcom,spi-qcom-qspi.yaml @@ -13,13 +13,11 @@ description: The QSPI controller allows SPI protocol communication in single, dual, or quad wire transmission modes for read/write access to slaves such as NOR flash. -allOf: - - $ref: /schemas/spi/spi-controller.yaml# - properties: compatible: items: - enum: + - qcom,qcs615-qspi - qcom,sc7180-qspi - qcom,sc7280-qspi - qcom,sdm845-qspi @@ -67,6 +65,23 @@ required: - clock-names - clocks +allOf: + - $ref: /schemas/spi/spi-controller.yaml# + - if: + properties: + compatible: + contains: + const: qcom,qcs615-qspi + then: + properties: + interconnects: + minItems: 2 + interconnect-names: + minItems: 2 + required: + - interconnects + - interconnect-names + unevaluatedProperties: false examples: From d283d5d4d9f6d081ddb65e371be26fffeb611c42 Mon Sep 17 00:00:00 2001 From: Viken Dadhaniya Date: Wed, 29 Apr 2026 22:31:37 +0530 Subject: [PATCH 040/118] spi: spi-qcom-qspi: Fix incomplete error handling in runtime PM The runtime PM functions had incomplete error handling that could leave the system in an inconsistent state. If any operation failed midway through suspend or resume, some resources would be left in the wrong state while others were already changed, leading to potential clock/power imbalances. Reorder the suspend/resume sequences to avoid brownout risk by ensuring the performance state is set appropriately before clocks are enabled and clocks are disabled before dropping the performance state. Fix by adding proper error checking for all operations and using goto-based cleanup to ensure all successfully acquired resources are properly released on any error. Signed-off-by: Viken Dadhaniya Link: https://patch.msgid.link/20260429-spi-nor-v5-2-993016c9711e@oss.qualcomm.com Signed-off-by: Mark Brown --- drivers/spi/spi-qcom-qspi.c | 44 ++++++++++++++++++++++++++++++------- 1 file changed, 36 insertions(+), 8 deletions(-) diff --git a/drivers/spi/spi-qcom-qspi.c b/drivers/spi/spi-qcom-qspi.c index 7e39038160e0..edfbf0b5d1fa 100644 --- a/drivers/spi/spi-qcom-qspi.c +++ b/drivers/spi/spi-qcom-qspi.c @@ -818,20 +818,34 @@ static int __maybe_unused qcom_qspi_runtime_suspend(struct device *dev) struct qcom_qspi *ctrl = spi_controller_get_devdata(host); int ret; - /* Drop the performance state vote */ - dev_pm_opp_set_rate(dev, 0); clk_bulk_disable_unprepare(QSPI_NUM_CLKS, ctrl->clks); ret = icc_disable(ctrl->icc_path_cpu_to_qspi); if (ret) { dev_err_ratelimited(ctrl->dev, "%s: ICC disable failed for cpu: %d\n", __func__, ret); - return ret; + goto err_enable_clk; } - pinctrl_pm_select_sleep_state(dev); + ret = pinctrl_pm_select_sleep_state(dev); + if (ret) + goto err_enable_icc; + + /* Drop the performance state vote */ + ret = dev_pm_opp_set_rate(dev, 0); + if (ret) + goto err_select_default_state; return 0; + +err_select_default_state: + pinctrl_pm_select_default_state(dev); +err_enable_icc: + icc_enable(ctrl->icc_path_cpu_to_qspi); +err_enable_clk: + if (clk_bulk_prepare_enable(QSPI_NUM_CLKS, ctrl->clks)) + dev_err_ratelimited(ctrl->dev, "Failed to re-enable clocks\n"); + return ret; } static int __maybe_unused qcom_qspi_runtime_resume(struct device *dev) @@ -840,20 +854,34 @@ static int __maybe_unused qcom_qspi_runtime_resume(struct device *dev) struct qcom_qspi *ctrl = spi_controller_get_devdata(host); int ret; - pinctrl_pm_select_default_state(dev); + ret = dev_pm_opp_set_rate(dev, ctrl->last_speed * 4); + if (ret) + return ret; + + ret = pinctrl_pm_select_default_state(dev); + if (ret) + goto err_opp_set_rate_zero; ret = icc_enable(ctrl->icc_path_cpu_to_qspi); if (ret) { dev_err_ratelimited(ctrl->dev, "%s: ICC enable failed for cpu: %d\n", __func__, ret); - return ret; + goto err_select_sleep_state; } ret = clk_bulk_prepare_enable(QSPI_NUM_CLKS, ctrl->clks); if (ret) - return ret; + goto err_disable_icc; - return dev_pm_opp_set_rate(dev, ctrl->last_speed * 4); + return 0; + +err_disable_icc: + icc_disable(ctrl->icc_path_cpu_to_qspi); +err_select_sleep_state: + pinctrl_pm_select_sleep_state(dev); +err_opp_set_rate_zero: + dev_pm_opp_set_rate(dev, 0); + return ret; } static int __maybe_unused qcom_qspi_suspend(struct device *dev) From 104b5e9b85c00c3fe552032164bf5bbd78e0f0b4 Mon Sep 17 00:00:00 2001 From: Viken Dadhaniya Date: Wed, 29 Apr 2026 22:31:38 +0530 Subject: [PATCH 041/118] spi: spi-qcom-qspi: Add interconnect support for memory path The QSPI controller has two interconnect paths: 1. qspi-config: CPU to QSPI controller for register access 2. qspi-memory: QSPI controller to memory for DMA operations Currently, the driver only manages the qspi-config path. Add support for the qspi-memory path to ensure proper bandwidth allocation for QSPI data transfers to/from memory. Enable and disable both paths during runtime PM transitions. Signed-off-by: Viken Dadhaniya Link: https://patch.msgid.link/20260429-spi-nor-v5-3-993016c9711e@oss.qualcomm.com Signed-off-by: Mark Brown --- drivers/spi/spi-qcom-qspi.c | 44 ++++++++++++++++++++++++++++++++----- 1 file changed, 38 insertions(+), 6 deletions(-) diff --git a/drivers/spi/spi-qcom-qspi.c b/drivers/spi/spi-qcom-qspi.c index edfbf0b5d1fa..caf55a6f70b3 100644 --- a/drivers/spi/spi-qcom-qspi.c +++ b/drivers/spi/spi-qcom-qspi.c @@ -174,6 +174,7 @@ struct qcom_qspi { void *virt_cmd_desc[QSPI_MAX_SG]; unsigned int n_cmd_desc; struct icc_path *icc_path_cpu_to_qspi; + struct icc_path *icc_path_mem; unsigned long last_speed; /* Lock to protect data accessed by IRQs */ spinlock_t lock; @@ -272,7 +273,7 @@ static void qcom_qspi_handle_err(struct spi_controller *host, static int qcom_qspi_set_speed(struct qcom_qspi *ctrl, unsigned long speed_hz) { int ret; - unsigned int avg_bw_cpu; + unsigned int avg_bw_cpu, avg_bw_mem; if (speed_hz == ctrl->last_speed) return 0; @@ -285,7 +286,7 @@ static int qcom_qspi_set_speed(struct qcom_qspi *ctrl, unsigned long speed_hz) } /* - * Set BW quota for CPU. + * Set BW quota for CPU and memory paths. * We don't have explicit peak requirement so keep it equal to avg_bw. */ avg_bw_cpu = Bps_to_icc(speed_hz); @@ -296,6 +297,13 @@ static int qcom_qspi_set_speed(struct qcom_qspi *ctrl, unsigned long speed_hz) return ret; } + avg_bw_mem = Bps_to_icc(speed_hz); + ret = icc_set_bw(ctrl->icc_path_mem, avg_bw_mem, avg_bw_mem); + if (ret) { + dev_err(ctrl->dev, "ICC BW voting failed for memory: %d\n", ret); + return ret; + } + ctrl->last_speed = speed_hz; return 0; @@ -729,6 +737,14 @@ static int qcom_qspi_probe(struct platform_device *pdev) return dev_err_probe(dev, PTR_ERR(ctrl->icc_path_cpu_to_qspi), "Failed to get cpu path\n"); + ctrl->icc_path_mem = devm_of_icc_get(dev, "qspi-memory"); + if (IS_ERR(ctrl->icc_path_mem)) { + if (PTR_ERR(ctrl->icc_path_mem) != -ENODATA) + return dev_err_probe(dev, PTR_ERR(ctrl->icc_path_mem), + "Failed to get memory path\n"); + ctrl->icc_path_mem = NULL; + } + /* Set BW vote for register access */ ret = icc_set_bw(ctrl->icc_path_cpu_to_qspi, Bps_to_icc(1000), Bps_to_icc(1000)); @@ -827,9 +843,15 @@ static int __maybe_unused qcom_qspi_runtime_suspend(struct device *dev) goto err_enable_clk; } + ret = icc_disable(ctrl->icc_path_mem); + if (ret) { + dev_err_ratelimited(ctrl->dev, "ICC disable failed for memory: %d\n", ret); + goto err_enable_icc_cpu; + } + ret = pinctrl_pm_select_sleep_state(dev); if (ret) - goto err_enable_icc; + goto err_enable_icc_mem; /* Drop the performance state vote */ ret = dev_pm_opp_set_rate(dev, 0); @@ -840,7 +862,9 @@ static int __maybe_unused qcom_qspi_runtime_suspend(struct device *dev) err_select_default_state: pinctrl_pm_select_default_state(dev); -err_enable_icc: +err_enable_icc_mem: + icc_enable(ctrl->icc_path_mem); +err_enable_icc_cpu: icc_enable(ctrl->icc_path_cpu_to_qspi); err_enable_clk: if (clk_bulk_prepare_enable(QSPI_NUM_CLKS, ctrl->clks)) @@ -869,13 +893,21 @@ static int __maybe_unused qcom_qspi_runtime_resume(struct device *dev) goto err_select_sleep_state; } + ret = icc_enable(ctrl->icc_path_mem); + if (ret) { + dev_err_ratelimited(ctrl->dev, "ICC enable failed for memory: %d\n", ret); + goto err_disable_icc_cpu; + } + ret = clk_bulk_prepare_enable(QSPI_NUM_CLKS, ctrl->clks); if (ret) - goto err_disable_icc; + goto err_disable_icc_mem; return 0; -err_disable_icc: +err_disable_icc_mem: + icc_disable(ctrl->icc_path_mem); +err_disable_icc_cpu: icc_disable(ctrl->icc_path_cpu_to_qspi); err_select_sleep_state: pinctrl_pm_select_sleep_state(dev); From b610d5333c4b93389240b62d1b6299e7e2ee2e65 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Sat, 2 May 2026 21:30:51 -0400 Subject: [PATCH 042/118] spi: dt-bindings: add SpacemiT K1 SPI support Add support for the SPI controller implemented by the SpacemiT K1 SoC. Acked-by: Conor Dooley Acked-by: Troy Mitchell Reviewed-by: Rob Herring (Arm) Signed-off-by: Alex Elder Signed-off-by: Guodong Xu Link: https://patch.msgid.link/20260502-spi-spacemit-k1-v10-1-f412e1ae8a34@riscstar.com Signed-off-by: Mark Brown --- .../bindings/spi/spacemit,k1-spi.yaml | 84 +++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 Documentation/devicetree/bindings/spi/spacemit,k1-spi.yaml diff --git a/Documentation/devicetree/bindings/spi/spacemit,k1-spi.yaml b/Documentation/devicetree/bindings/spi/spacemit,k1-spi.yaml new file mode 100644 index 000000000000..e82c7f8d0b98 --- /dev/null +++ b/Documentation/devicetree/bindings/spi/spacemit,k1-spi.yaml @@ -0,0 +1,84 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/spi/spacemit,k1-spi.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: SpacemiT K1 SoC Serial Peripheral Interface (SPI) + +maintainers: + - Alex Elder + +description: + The SpacemiT K1 SoC implements a SPI controller that has two 32-entry + FIFOs, for transmit and receive. Details are currently available in + section 18.2.1 of the K1 User Manual, found in the SpacemiT Keystone + K1 Documentation[1]. The controller transfers words using PIO. DMA + transfers are supported as well, if both TX and RX DMA channels are + specified, + + [1] https://developer.spacemit.com/documentation + +allOf: + - $ref: /schemas/spi/spi-controller.yaml# + +properties: + compatible: + const: spacemit,k1-spi + + reg: + maxItems: 1 + + clocks: + items: + - description: Core clock + - description: Bus clock + + clock-names: + items: + - const: core + - const: bus + + resets: + maxItems: 1 + + interrupts: + maxItems: 1 + + dmas: + items: + - description: RX DMA channel + - description: TX DMA channel + + dma-names: + items: + - const: rx + - const: tx + +required: + - compatible + - reg + - clocks + - clock-names + - resets + - interrupts + +unevaluatedProperties: false + +examples: + - | + + #include + spi@d401c000 { + compatible = "spacemit,k1-spi"; + reg = <0xd401c000 0x30>; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&syscon_apbc CLK_SSP3>, + <&syscon_apbc CLK_SSP3_BUS>; + clock-names = "core", "bus"; + resets = <&syscon_apbc RESET_SSP3>; + interrupts = <55>; + dmas = <&pdma 20>, <&pdma 19>; + dma-names = "rx", "tx"; + }; From efcd8b9d111177d48c841d09beca43b15d5b9e5f Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Sat, 2 May 2026 21:30:52 -0400 Subject: [PATCH 043/118] spi: spacemit: introduce SpacemiT K1 SPI controller driver This patch introduces the driver for the SPI controller found in the SpacemiT K1 SoC. Currently the driver supports master mode only. The SPI hardware implements RX and TX FIFOs, 32 entries each, and supports both PIO and DMA mode transfers. Signed-off-by: Alex Elder Signed-off-by: Guodong Xu Link: https://patch.msgid.link/20260502-spi-spacemit-k1-v10-2-f412e1ae8a34@riscstar.com Signed-off-by: Mark Brown --- drivers/spi/Kconfig | 9 + drivers/spi/Makefile | 1 + drivers/spi/spi-spacemit-k1.c | 789 ++++++++++++++++++++++++++++++++++ 3 files changed, 799 insertions(+) create mode 100644 drivers/spi/spi-spacemit-k1.c diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index b563f49e2197..8782514bb89b 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -1085,6 +1085,15 @@ config SPI_SG2044_NOR also supporting 3Byte address devices and 4Byte address devices. +config SPI_SPACEMIT_K1 + tristate "K1 SPI Controller" + depends on ARCH_SPACEMIT || COMPILE_TEST + depends on OF + imply MMP_PDMA if ARCH_SPACEMIT + default m if ARCH_SPACEMIT + help + Enable support for the SpacemiT K1 SPI controller. + config SPI_SPRD tristate "Spreadtrum SPI controller" depends on ARCH_SPRD || COMPILE_TEST diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 9d36190a9884..9fa12498ce8c 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -143,6 +143,7 @@ obj-$(CONFIG_SPI_SIFIVE) += spi-sifive.o obj-$(CONFIG_SPI_SLAVE_MT27XX) += spi-slave-mt27xx.o obj-$(CONFIG_SPI_SN_F_OSPI) += spi-sn-f-ospi.o obj-$(CONFIG_SPI_SG2044_NOR) += spi-sg2044-nor.o +obj-$(CONFIG_SPI_SPACEMIT_K1) += spi-spacemit-k1.o obj-$(CONFIG_SPI_SPRD) += spi-sprd.o obj-$(CONFIG_SPI_SPRD_ADI) += spi-sprd-adi.o obj-$(CONFIG_SPI_STM32) += spi-stm32.o diff --git a/drivers/spi/spi-spacemit-k1.c b/drivers/spi/spi-spacemit-k1.c new file mode 100644 index 000000000000..99db429db0b2 --- /dev/null +++ b/drivers/spi/spi-spacemit-k1.c @@ -0,0 +1,789 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// SpacemiT K1 SPI controller driver +// +// Copyright (C) 2026, RISCstar Solutions Corporation +// Copyright (C) 2023, SpacemiT Corporation + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "internals.h" + +/* This is the range of transfer rates supported by the K1 SoC */ +#define K1_SPI_MIN_SPEED_HZ 6250 +#define K1_SPI_MAX_SPEED_HZ 51200000 + +/* DMA constraints */ +#define K1_SPI_DMA_ALIGNMENT 64 +#define K1_SPI_MAX_DMA_LEN SZ_512K + +/* SSP Top Control Register */ +#define SSP_TOP_CTRL 0x00 +#define TOP_SSE BIT(0) /* Enable port */ +#define TOP_FRF_MASK GENMASK(2, 1) /* Frame format */ +#define TOP_FRF_MOTOROLA 0 /* Motorola SPI */ +#define TOP_DSS_MASK GENMASK(9, 5) /* Data size (1-32) */ +#define TOP_SPO BIT(10) /* Polarity: 0=low */ +#define TOP_SPH BIT(11) /* Half-cycle phase */ +#define TOP_LBM BIT(12) /* Loopback mode */ +#define TOP_TRAIL BIT(13) /* Trailing bytes */ +#define TOP_HOLD_FRAME_LOW BIT(14) /* Chip select */ + +/* SSP FIFO Control Register */ +#define SSP_FIFO_CTRL 0x04 +#define FIFO_TFT_MASK GENMASK(4, 0) /* TX FIFO threshold */ +#define FIFO_RFT_MASK GENMASK(9, 5) /* RX FIFO threshold */ +#define FIFO_TSRE BIT(10) /* TX service request */ +#define FIFO_RSRE BIT(11) /* RX service request */ + +/* SSP Interrupt Enable Register */ +#define SSP_INT_EN 0x08 +#define SSP_INT_EN_TINTE BIT(1) /* RX timeout */ +#define SSP_INT_EN_RIE BIT(2) /* RX FIFO */ +#define SSP_INT_EN_TIE BIT(3) /* TX FIFO */ +#define SSP_INT_EN_RIM BIT(4) /* RX FIFO overrun */ +#define SSP_INT_EN_TIM BIT(5) /* TX FIFO underrun */ +#define SSP_INT_EN_EBCEI BIT(6) /* Bit count error */ + +/* TX interrupts, RX interrupts, and error interrupts */ +#define SSP_INT_EN_TX SSP_INT_EN_TIE +#define SSP_INT_EN_RX \ + (SSP_INT_EN_TINTE | SSP_INT_EN_RIE) +#define SSP_INT_EN_ERROR \ + (SSP_INT_EN_RIM | SSP_INT_EN_TIM | SSP_INT_EN_EBCEI) + +/* SSP Time Out Register */ +#define SSP_TIMEOUT 0x0c +#define SSP_TIMEOUT_MASK GENMASK(23, 0) + +/* SSP Data Register */ +#define SSP_DATAR 0x10 + +/* SSP Status Register */ +#define SSP_STATUS 0x14 +#define SSP_STATUS_BSY BIT(0) /* SPI/I2S busy */ +#define SSP_STATUS_TNF BIT(6) /* TX FIFO not full */ +#define SSP_STATUS_TFL GENMASK(11, 7) /* TX FIFO level */ +#define SSP_STATUS_TUR BIT(12) /* TX FIFO underrun */ +#define SSP_STATUS_RNE BIT(14) /* RX FIFO not empty */ +#define SSP_STATUS_RFL GENMASK(19, 15) /* RX FIFO level */ +#define SSP_STATUS_ROR BIT(20) /* RX FIFO overrun */ +#define SSP_STATUS_BCE BIT(21) /* Bit count error */ + +/* Error status mask */ +#define SSP_STATUS_ERROR \ + (SSP_STATUS_TUR | SSP_STATUS_ROR | SSP_STATUS_BCE) + +/* The FIFO sizes and thresholds are the same for RX and TX */ +#define K1_SPI_FIFO_SIZE 32 +#define K1_SPI_THRESH (K1_SPI_FIFO_SIZE / 2) + +struct k1_spi_driver_data { + struct spi_controller *host; + void __iomem *base; + phys_addr_t base_addr; + unsigned long bus_rate; + struct clk *clk; + unsigned long rate; + int irq; + + /* Current transfer information; not valid if message is null */ + u32 bytes; /* Bytes used for bits_per_word */ + unsigned int rx_resid; /* RX bytes left in transfer */ + unsigned int tx_resid; /* TX bytes left in transfer */ + struct spi_transfer *transfer; /* Current transfer */ + + bool dma_enabled; +}; + +/* Set our registers to a known initial state */ +static void +k1_spi_register_reset(struct k1_spi_driver_data *drv_data, bool initial) +{ + u32 val = 0; + + writel(0, drv_data->base + SSP_TOP_CTRL); + + if (initial) { + /* + * The TX and RX FIFO thresholds are the same no matter + * what the speed or bits per word, so we can just set + * them once. The thresholds are one more than the values + * in the register. + */ + val = FIELD_PREP(FIFO_RFT_MASK, K1_SPI_THRESH - 1); + val |= FIELD_PREP(FIFO_TFT_MASK, K1_SPI_THRESH - 1); + } + writel(val, drv_data->base + SSP_FIFO_CTRL); + + writel(0, drv_data->base + SSP_INT_EN); + writel(0, drv_data->base + SSP_TIMEOUT); + + /* Clear any pending interrupt conditions */ + writel(~0, drv_data->base + SSP_STATUS); +} + +/* + * The client can call the setup function multiple times, and each call + * can specify a different SPI mode (and transfer speed). Each transfer + * can specify its own speed though, and the core code ensures each + * transfer's speed is set to something nonzero and supported by both + * the controller and the device. We just set the speed for each transfer. + */ +static int k1_spi_setup(struct spi_device *spi) +{ + struct k1_spi_driver_data *drv_data; + u32 val; + + drv_data = spi_controller_get_devdata(spi->controller); + + /* + * Configure the message format for this device. We only + * support Motorola SPI format in master mode. + */ + val = FIELD_PREP(TOP_FRF_MASK, TOP_FRF_MOTOROLA); + + /* Translate the mode into the value used to program the hardware. */ + if (spi->mode & SPI_CPHA) + val |= TOP_SPH; /* 1/2 cycle */ + if (spi->mode & SPI_CPOL) + val |= TOP_SPO; /* active low */ + if (spi->mode & SPI_LOOP) + val |= TOP_LBM; /* enable loopback */ + writel(val, drv_data->base + SSP_TOP_CTRL); + + return 0; +} + +static void k1_spi_cleanup(struct spi_device *spi) +{ + struct k1_spi_driver_data *drv_data; + + drv_data = spi_controller_get_devdata(spi->controller); + k1_spi_register_reset(drv_data, false); +} + +static bool k1_spi_can_dma(struct spi_controller *host, struct spi_device *spi, + struct spi_transfer *transfer) +{ + struct k1_spi_driver_data *drv_data = spi_controller_get_devdata(host); + u32 burst_size; + + if (!drv_data->dma_enabled) + return false; + + if (transfer->len > SZ_2K) + return false; + + /* Don't bother with DMA if we can't do even a single burst */ + burst_size = K1_SPI_THRESH * spi_bpw_to_bytes(transfer->bits_per_word); + + return transfer->len >= burst_size; +} + +static void k1_spi_dma_callback(void *param) +{ + struct k1_spi_driver_data *drv_data = param; + u32 val; + + val = readl(drv_data->base + SSP_FIFO_CTRL); + val &= ~(FIFO_TSRE | FIFO_RSRE); + writel(val, drv_data->base + SSP_FIFO_CTRL); + + val = readl(drv_data->base + SSP_TOP_CTRL); + val &= ~TOP_TRAIL; + writel(val, drv_data->base + SSP_TOP_CTRL); + + /* Check for any error conditions */ + val = readl(drv_data->base + SSP_STATUS); + if (val & SSP_STATUS_ERROR) + drv_data->transfer->error |= SPI_TRANS_FAIL_IO; + + /* Disable the port */ + val = readl(drv_data->base + SSP_TOP_CTRL); + val &= ~TOP_SSE; + writel(val, drv_data->base + SSP_TOP_CTRL); + + drv_data->transfer = NULL; + + spi_finalize_current_transfer(drv_data->host); +} + +/* Prepare a descriptor for TX or RX DMA */ +static struct dma_async_tx_descriptor * +k1_spi_dma_prep(struct k1_spi_driver_data *drv_data, + struct spi_transfer *transfer, bool tx) +{ + phys_addr_t addr = drv_data->base_addr + SSP_DATAR; + u32 burst_size = K1_SPI_THRESH * drv_data->bytes; + struct dma_slave_config cfg = { }; + enum dma_transfer_direction dir; + enum dma_slave_buswidth width; + struct dma_chan *chan; + struct sg_table *sgt; + + switch (drv_data->bytes) { + case 1: + width = DMA_SLAVE_BUSWIDTH_1_BYTE; + break; + case 2: + width = DMA_SLAVE_BUSWIDTH_2_BYTES; + break; + default: /* bytes == 4 */ + width = DMA_SLAVE_BUSWIDTH_4_BYTES; + break; + } + + if (tx) { + chan = drv_data->host->dma_tx; + sgt = &transfer->tx_sg; + dir = DMA_MEM_TO_DEV; + + cfg.dst_addr = addr; + cfg.dst_addr_width = width; + cfg.dst_maxburst = burst_size; + } else { + chan = drv_data->host->dma_rx; + sgt = &transfer->rx_sg; + dir = DMA_DEV_TO_MEM; + + cfg.src_addr = addr; + cfg.src_addr_width = width; + cfg.src_maxburst = burst_size; + } + cfg.direction = dir; + + if (dmaengine_slave_config(chan, &cfg)) + return NULL; + + return dmaengine_prep_slave_sg(chan, sgt->sgl, sgt->nents, dir, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + +} + +static int k1_spi_dma_one(struct spi_controller *host, struct spi_device *spi, + struct spi_transfer *transfer) +{ + struct k1_spi_driver_data *drv_data = spi_controller_get_devdata(host); + struct dma_async_tx_descriptor *desc; + u32 val; + + /* Prepare the TX descriptor and submit it */ + desc = k1_spi_dma_prep(drv_data, transfer, true); + if (!desc) + goto fallback; + dmaengine_submit(desc); + + /* Prepare the RX descriptor and submit it */ + desc = k1_spi_dma_prep(drv_data, transfer, false); + if (!desc) + goto fallback; + + /* When RX is complete we also know TX has completed */ + desc->callback = k1_spi_dma_callback; + desc->callback_param = drv_data; + + dmaengine_submit(desc); + + val = readl(drv_data->base + SSP_TOP_CTRL); + val |= TOP_TRAIL; /* Trailing bytes handled by DMA */ + writel(val, drv_data->base + SSP_TOP_CTRL); + + val = readl(drv_data->base + SSP_FIFO_CTRL); + val |= FIFO_TSRE | FIFO_RSRE; + writel(val, drv_data->base + SSP_FIFO_CTRL); + + /* Start RX first so we're ready the instant we start transmitting */ + dma_async_issue_pending(host->dma_rx); + dma_async_issue_pending(host->dma_tx); + + return 1; +fallback: + transfer->error |= SPI_TRANS_FAIL_NO_START; + + return -EAGAIN; +} + +/* Flush the RX FIFO of any leftover data before processing a message */ +static int k1_spi_prepare_message(struct spi_controller *host, + struct spi_message *message) +{ + struct k1_spi_driver_data *drv_data = spi_controller_get_devdata(host); + u32 val = readl(drv_data->base + SSP_STATUS); + u32 count; + + /* If there's nothing in the FIFO, we're done */ + if (!(val & SSP_STATUS_RNE)) + return 0; + + /* Read and discard what's there (one more than what the field says) */ + count = FIELD_GET(SSP_STATUS_RFL, val) + 1; + do + (void)readl(drv_data->base + SSP_DATAR); + while (--count); + + return 0; +} + +/* Set logic level of chip select line (high=true means CS deasserted) */ +static void k1_spi_set_cs(struct spi_device *spi, bool high) +{ + struct k1_spi_driver_data *drv_data; + u32 val; + + drv_data = spi_controller_get_devdata(spi->controller); + + val = readl(drv_data->base + SSP_TOP_CTRL); + if (high) + val &= ~TOP_HOLD_FRAME_LOW; + else + val |= TOP_HOLD_FRAME_LOW; + writel(val, drv_data->base + SSP_TOP_CTRL); +} + +/* Set the transfer speed; the SPI core code ensures it is supported */ +static int k1_spi_set_speed(struct k1_spi_driver_data *drv_data, + struct spi_transfer *transfer) +{ + struct clk *clk = drv_data->clk; + u64 nsec_per_word; + u64 bus_ticks; + u32 timeout; + u32 val; + int ret; + + ret = clk_set_rate(clk, transfer->speed_hz); + if (ret) + return ret; + + drv_data->rate = clk_get_rate(clk); + + /* No need for RX FIFO timeout if we're not receiving anything */ + if (!transfer->rx_buf) + return 0; + + /* + * Compute the RX FIFO inactivity timeout value that should be used. + * The inactivity timer restarts with each word that lands in the + * FIFO. If several "word transfer times" pass without any new data + * in the RX FIFO, we might as well read what's there. + * + * The rate at which words land in the FIFO is determined by the + * word size and the transfer rate. One bit is transferred per + * clock tick, and 8 (or 16 or 32) bits are transferred per word. + * + * So we can get word transfer time (in nanoseconds) from: + * nsec_per_tick = NSEC_PER_SEC / drv_data->rate; + * ticks_per_word = BITS_PER_BYTE * drv_data->bytes; + * We do the divide last for better accuracy. + */ + nsec_per_word = NSEC_PER_SEC * BITS_PER_BYTE * drv_data->bytes; + nsec_per_word = DIV_ROUND_UP_ULL(nsec_per_word, drv_data->rate); + + /* + * The timeout (which we'll set to three word transfer times) is + * expressed as a number of APB clock ticks. + * bus_ticks = 3 * nsec * (drv_data->bus_rate / NSEC_PER_SEC) + */ + bus_ticks = 3 * nsec_per_word * drv_data->bus_rate; + timeout = DIV_ROUND_UP_ULL(bus_ticks, NSEC_PER_SEC); + + /* Set the RX timeout period (required for both DMA and PIO) */ + val = FIELD_PREP(SSP_TIMEOUT_MASK, timeout); + writel(val, drv_data->base + SSP_TIMEOUT); + + return 0; +} + +static int k1_spi_transfer_one(struct spi_controller *host, + struct spi_device *spi, + struct spi_transfer *transfer) +{ + struct k1_spi_driver_data *drv_data = spi_controller_get_devdata(host); + u32 ctrl; + u32 val; + int ret; + + /* Bits per word can change on a per-transfer basis */ + drv_data->bytes = spi_bpw_to_bytes(transfer->bits_per_word); + + /* Each transfer can also specify a different rate */ + ret = k1_spi_set_speed(drv_data, transfer); + if (ret) { + dev_err(&host->dev, + "failed to set transfer speed: %d\n", ret); + return ret; + } + + drv_data->rx_resid = transfer->len; + drv_data->tx_resid = transfer->len; + + drv_data->transfer = transfer; + + /* Clear any existing interrupt conditions */ + writel(~0, drv_data->base + SSP_STATUS); + + /* Set the data (word) size, and enable the port */ + ctrl = readl(drv_data->base + SSP_TOP_CTRL); + ctrl &= ~TOP_DSS_MASK; + ctrl |= FIELD_PREP(TOP_DSS_MASK, transfer->bits_per_word - 1); + ctrl |= TOP_SSE; + writel(ctrl, drv_data->base + SSP_TOP_CTRL); + + if (spi_xfer_is_dma_mapped(host, spi, transfer)) + return k1_spi_dma_one(host, spi, transfer); + + /* An interrupt will initiate the transfer */ + val = SSP_INT_EN_TX | SSP_INT_EN_RX | SSP_INT_EN_ERROR; + writel(val, drv_data->base + SSP_INT_EN); + + return 1; /* We will call spi_finalize_current_transfer() */ +} + +static void +k1_spi_handle_err(struct spi_controller *host, struct spi_message *message) +{ + struct k1_spi_driver_data *drv_data = spi_controller_get_devdata(host); + + if (drv_data->dma_enabled) { + dmaengine_terminate_sync(host->dma_rx); + dmaengine_terminate_sync(host->dma_tx); + } +} + +static void k1_spi_write_word(struct k1_spi_driver_data *drv_data) +{ + struct spi_transfer *transfer = drv_data->transfer; + u32 bytes = drv_data->bytes; + u32 val; + + if (transfer->tx_buf) { + const void *buf; + + buf = transfer->tx_buf + (transfer->len - drv_data->tx_resid); + if (bytes == 1) + val = *(u8 *)buf; + else if (bytes == 2) + val = *(u16 *)buf; + else /* bytes == 4 */ + val = *(u32 *)buf; + } else { + val = 0; /* Null writer; write 1, 2, or 4 zero bytes */ + } + /* Fill the next TX FIFO entry */ + writel(val, drv_data->base + SSP_DATAR); + + drv_data->tx_resid -= bytes; +} + +/* The last-read status value is provided; we know SSP_STATUS_TNF is set */ +static bool k1_spi_write(struct k1_spi_driver_data *drv_data, u32 val) +{ + unsigned int count; + + /* Get the number of open slots in the FIFO; zero means all */ + count = FIELD_GET(SSP_STATUS_TFL, val) ? : K1_SPI_FIFO_SIZE; + + /* + * Limit how much we try to send at a time, to reduce the + * chance the other side can overrun our RX FIFO. + */ + count = min3(count, K1_SPI_THRESH, drv_data->tx_resid / drv_data->bytes); + do + k1_spi_write_word(drv_data); + while (--count); + + return !drv_data->tx_resid; +} + +static void k1_spi_read_word(struct k1_spi_driver_data *drv_data) +{ + struct spi_transfer *transfer = drv_data->transfer; + u32 bytes = drv_data->bytes; + u32 val; + + /* Consume the next RX FIFO entry */ + val = readl(drv_data->base + SSP_DATAR); + if (transfer->rx_buf) { + void *buf; + + buf = transfer->rx_buf + (transfer->len - drv_data->rx_resid); + + if (bytes == 1) + *(u8 *)buf = val; + else if (bytes == 2) + *(u16 *)buf = val; + else /* bytes == 4 */ + *(u32 *)buf = val; + } /* Otherwise null reader: discard the data */ + + drv_data->rx_resid -= bytes; +} + +/* The last-read status value is provided; we know SSP_STATUS_RNE is set */ +static bool k1_spi_read(struct k1_spi_driver_data *drv_data, u32 val) +{ + do { + unsigned int count = FIELD_GET(SSP_STATUS_RFL, val) + 1; + + /* Only read what we need */ + count = min(count, drv_data->rx_resid / drv_data->bytes); + do + k1_spi_read_word(drv_data); + while (--count); + + /* If there's no more to read, we're done */ + if (!drv_data->rx_resid) + return true; + + /* Check again in case more became available to read */ + val = readl(drv_data->base + SSP_STATUS); + if (val & SSP_STATUS_RNE) + writel(SSP_STATUS_RNE, drv_data->base + SSP_STATUS); + else + return false; + } while (true); +} + +static irqreturn_t k1_spi_ssp_isr(int irq, void *dev_id) +{ + struct k1_spi_driver_data *drv_data = dev_id; + u32 status; + u32 top_ctrl; + + /* Get status and clear pending interrupts */ + status = readl(drv_data->base + SSP_STATUS); + writel(status, drv_data->base + SSP_STATUS); + + /* If no actionable status bits are set, this is not our interrupt */ + if (!(status & (SSP_STATUS_ERROR | SSP_STATUS_TNF | SSP_STATUS_RNE))) + return IRQ_NONE; + + /* Check for any error conditions first */ + if (status & SSP_STATUS_ERROR) { + if (drv_data->transfer) + drv_data->transfer->error |= SPI_TRANS_FAIL_IO; + goto done; + } + + /* + * For SPI, bytes are transferred in both directions equally, and + * RX always follows TX. Start by writing if there is anything to + * write, then read. Once there's no more to read, we're done. + */ + if (drv_data->tx_resid && (status & SSP_STATUS_TNF)) { + /* If we finish writing, disable TX interrupts */ + if (k1_spi_write(drv_data, status)) + writel(SSP_INT_EN_RX | SSP_INT_EN_ERROR, + drv_data->base + SSP_INT_EN); + } + + /* We're not done unless we've read all that was requested */ + if (drv_data->rx_resid) { + /* Read more if the FIFO is not empty */ + if (status & SSP_STATUS_RNE) + if (k1_spi_read(drv_data, status)) + goto done; + + return IRQ_HANDLED; + } +done: + /* Disable the port */ + top_ctrl = readl(drv_data->base + SSP_TOP_CTRL); + top_ctrl &= ~TOP_SSE; + writel(top_ctrl, drv_data->base + SSP_TOP_CTRL); + + /* Disable all interrupts */ + writel(0, drv_data->base + SSP_INT_EN); + + if (drv_data->transfer) { + drv_data->transfer = NULL; + spi_finalize_current_transfer(drv_data->host); + } + + return IRQ_HANDLED; +} + +static int +k1_spi_dma_setup(struct k1_spi_driver_data *drv_data, struct device *dev) +{ + struct spi_controller *host = drv_data->host; + struct dma_chan *chan; + + chan = dma_request_chan(dev, "tx"); + if (IS_ERR(chan)) + return PTR_ERR(chan); + host->dma_tx = chan; + + chan = dma_request_chan(dev, "rx"); + if (IS_ERR(chan)) { + dma_release_channel(host->dma_tx); + host->dma_tx = NULL; + return PTR_ERR(chan); + } + host->dma_rx = chan; + + drv_data->dma_enabled = true; + + return 0; +} + +static void k1_spi_dma_cleanup(struct device *dev, void *res) +{ + struct k1_spi_driver_data **ptr = res; + struct k1_spi_driver_data *drv_data = *ptr; + struct spi_controller *host = drv_data->host; + + if (!drv_data->dma_enabled) + return; + + drv_data->dma_enabled = false; + + dma_release_channel(host->dma_rx); + host->dma_rx = NULL; + dma_release_channel(host->dma_tx); + host->dma_tx = NULL; +} + +static int +devm_k1_spi_dma_setup(struct k1_spi_driver_data *drv_data, struct device *dev) +{ + struct k1_spi_driver_data **ptr; + int ret; + + if (!IS_ENABLED(CONFIG_MMP_PDMA)) { + dev_info(dev, "DMA not available; using PIO\n"); + return 0; + } + + ptr = devres_alloc(k1_spi_dma_cleanup, sizeof(*ptr), GFP_KERNEL); + if (!ptr) + return -ENOMEM; + + ret = k1_spi_dma_setup(drv_data, dev); + if (ret) { + devres_free(ptr); + return ret; + } + + *ptr = drv_data; + devres_add(dev, ptr); + + return 0; +} + +static int k1_spi_probe(struct platform_device *pdev) +{ + struct k1_spi_driver_data *drv_data; + struct device *dev = &pdev->dev; + struct reset_control *reset; + struct spi_controller *host; + struct resource *iores; + struct clk *clk_bus; + int ret; + + host = devm_spi_alloc_host(dev, sizeof(*drv_data)); + if (!host) + return -ENOMEM; + drv_data = spi_controller_get_devdata(host); + drv_data->host = host; + platform_set_drvdata(pdev, drv_data); + + ret = devm_k1_spi_dma_setup(drv_data, dev); + if (ret == -EPROBE_DEFER) + return ret; + if (ret) + dev_warn(dev, "DMA setup failed (%d), falling back to PIO\n", ret); + + drv_data->base = devm_platform_get_and_ioremap_resource(pdev, 0, + &iores); + if (IS_ERR(drv_data->base)) + return dev_err_probe(dev, PTR_ERR(drv_data->base), + "error mapping memory\n"); + drv_data->base_addr = iores->start; + + clk_bus = devm_clk_get_enabled(dev, "bus"); + if (IS_ERR(clk_bus)) + return dev_err_probe(dev, PTR_ERR(clk_bus), + "error getting/enabling bus clock\n"); + drv_data->bus_rate = clk_get_rate(clk_bus); + + drv_data->clk = devm_clk_get_enabled(dev, "core"); + if (IS_ERR(drv_data->clk)) + return dev_err_probe(dev, PTR_ERR(drv_data->clk), + "error getting/enabling core clock\n"); + + reset = devm_reset_control_get_exclusive_deasserted(dev, NULL); + if (IS_ERR(reset)) + return dev_err_probe(dev, PTR_ERR(reset), + "error getting/deasserting reset\n"); + + k1_spi_register_reset(drv_data, true); + + drv_data->irq = platform_get_irq(pdev, 0); + if (drv_data->irq < 0) + return dev_err_probe(dev, drv_data->irq, "error getting IRQ\n"); + + ret = devm_request_irq(dev, drv_data->irq, k1_spi_ssp_isr, + IRQF_SHARED, dev_name(dev), drv_data); + if (ret < 0) + return dev_err_probe(dev, ret, "error requesting IRQ\n"); + + /* Initialize the host structure, then register it */ + host->dev.of_node = dev_of_node(dev); + host->dev.parent = dev; + host->num_chipselect = 1; + if (drv_data->dma_enabled) + host->dma_alignment = K1_SPI_DMA_ALIGNMENT; + host->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LOOP; + host->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 32); + host->min_speed_hz = K1_SPI_MIN_SPEED_HZ; + host->max_speed_hz = K1_SPI_MAX_SPEED_HZ; + host->flags = SPI_CONTROLLER_MUST_RX | SPI_CONTROLLER_MUST_TX; + host->max_dma_len = K1_SPI_MAX_DMA_LEN; + + host->setup = k1_spi_setup; + host->cleanup = k1_spi_cleanup; + host->can_dma = k1_spi_can_dma; + host->prepare_message = k1_spi_prepare_message; + host->set_cs = k1_spi_set_cs; + host->transfer_one = k1_spi_transfer_one; + host->handle_err = k1_spi_handle_err; + + ret = devm_spi_register_controller(dev, host); + if (ret) + dev_err(dev, "error registering controller\n"); + + return ret; +} + +static const struct of_device_id k1_spi_dt_ids[] = { + { .compatible = "spacemit,k1-spi", }, + {} +}; +MODULE_DEVICE_TABLE(of, k1_spi_dt_ids); + +static struct platform_driver k1_spi_driver = { + .probe = k1_spi_probe, + .driver = { + .name = "k1-spi", + .of_match_table = k1_spi_dt_ids, + }, +}; +module_platform_driver(k1_spi_driver); + +MODULE_DESCRIPTION("SpacemiT K1 SPI controller driver"); +MODULE_LICENSE("GPL"); From e4358093816df69b574a5632051801a881663d12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig=20=28The=20Capable=20Hub=29?= Date: Mon, 4 May 2026 16:21:17 +0200 Subject: [PATCH 044/118] spi: Consistently define pci_device_ids using named initializers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .driver_data member of the various struct pci_device_id arrays were initialized by list expressions. This isn't easily readable if you're not into PCI. Using named initializers is more explicit and thus easier to parse. Also skip explicit assignments of 0 (which the compiler then takes care of). This change doesn't introduce changes to the compiled pci_device_id arrays. Tested on x86 and arm64. Signed-off-by: Uwe Kleine-König (The Capable Hub) Link: https://patch.msgid.link/20260504142117.2116978-2-u.kleine-koenig@baylibre.com Signed-off-by: Mark Brown --- drivers/spi/spi-dw-pci.c | 14 ++++---- drivers/spi/spi-intel-pci.c | 66 +++++++++++++++++----------------- drivers/spi/spi-pci1xxxx.c | 42 +++++++++++----------- drivers/spi/spi-topcliff-pch.c | 8 ++--- 4 files changed, 65 insertions(+), 65 deletions(-) diff --git a/drivers/spi/spi-dw-pci.c b/drivers/spi/spi-dw-pci.c index 72d9f5bc87f7..7f002d5e5b88 100644 --- a/drivers/spi/spi-dw-pci.c +++ b/drivers/spi/spi-dw-pci.c @@ -185,15 +185,15 @@ static const struct pci_device_id dw_spi_pci_ids[] = { * exclusively used by SCU to communicate with MSIC. */ /* Intel MID platform SPI controller 1 */ - { PCI_VDEVICE(INTEL, 0x0800), (kernel_ulong_t)&dw_spi_pci_mid_desc_1}, + { PCI_VDEVICE(INTEL, 0x0800), .driver_data = (kernel_ulong_t)&dw_spi_pci_mid_desc_1 }, /* Intel MID platform SPI controller 2 */ - { PCI_VDEVICE(INTEL, 0x0812), (kernel_ulong_t)&dw_spi_pci_mid_desc_2}, + { PCI_VDEVICE(INTEL, 0x0812), .driver_data = (kernel_ulong_t)&dw_spi_pci_mid_desc_2 }, /* Intel Elkhart Lake PSE SPI controllers */ - { PCI_VDEVICE(INTEL, 0x4b84), (kernel_ulong_t)&dw_spi_pci_ehl_desc}, - { PCI_VDEVICE(INTEL, 0x4b85), (kernel_ulong_t)&dw_spi_pci_ehl_desc}, - { PCI_VDEVICE(INTEL, 0x4b86), (kernel_ulong_t)&dw_spi_pci_ehl_desc}, - { PCI_VDEVICE(INTEL, 0x4b87), (kernel_ulong_t)&dw_spi_pci_ehl_desc}, - {}, + { PCI_VDEVICE(INTEL, 0x4b84), .driver_data = (kernel_ulong_t)&dw_spi_pci_ehl_desc }, + { PCI_VDEVICE(INTEL, 0x4b85), .driver_data = (kernel_ulong_t)&dw_spi_pci_ehl_desc }, + { PCI_VDEVICE(INTEL, 0x4b86), .driver_data = (kernel_ulong_t)&dw_spi_pci_ehl_desc }, + { PCI_VDEVICE(INTEL, 0x4b87), .driver_data = (kernel_ulong_t)&dw_spi_pci_ehl_desc }, + { }, }; MODULE_DEVICE_TABLE(pci, dw_spi_pci_ids); diff --git a/drivers/spi/spi-intel-pci.c b/drivers/spi/spi-intel-pci.c index d8ef8f89330a..8c429c832ddd 100644 --- a/drivers/spi/spi-intel-pci.c +++ b/drivers/spi/spi-intel-pci.c @@ -66,39 +66,39 @@ static int intel_spi_pci_probe(struct pci_dev *pdev, } static const struct pci_device_id intel_spi_pci_ids[] = { - { PCI_VDEVICE(INTEL, 0x02a4), (unsigned long)&cnl_info }, - { PCI_VDEVICE(INTEL, 0x06a4), (unsigned long)&cnl_info }, - { PCI_VDEVICE(INTEL, 0x18e0), (unsigned long)&bxt_info }, - { PCI_VDEVICE(INTEL, 0x19e0), (unsigned long)&bxt_info }, - { PCI_VDEVICE(INTEL, 0x1bca), (unsigned long)&bxt_info }, - { PCI_VDEVICE(INTEL, 0x34a4), (unsigned long)&cnl_info }, - { PCI_VDEVICE(INTEL, 0x38a4), (unsigned long)&bxt_info }, - { PCI_VDEVICE(INTEL, 0x43a4), (unsigned long)&cnl_info }, - { PCI_VDEVICE(INTEL, 0x4b24), (unsigned long)&bxt_info }, - { PCI_VDEVICE(INTEL, 0x4d23), (unsigned long)&cnl_info }, - { PCI_VDEVICE(INTEL, 0x4da4), (unsigned long)&bxt_info }, - { PCI_VDEVICE(INTEL, 0x51a4), (unsigned long)&cnl_info }, - { PCI_VDEVICE(INTEL, 0x54a4), (unsigned long)&cnl_info }, - { PCI_VDEVICE(INTEL, 0x5794), (unsigned long)&cnl_info }, - { PCI_VDEVICE(INTEL, 0x5825), (unsigned long)&cnl_info }, - { PCI_VDEVICE(INTEL, 0x6e24), (unsigned long)&cnl_info }, - { PCI_VDEVICE(INTEL, 0x7723), (unsigned long)&cnl_info }, - { PCI_VDEVICE(INTEL, 0x7a24), (unsigned long)&cnl_info }, - { PCI_VDEVICE(INTEL, 0x7aa4), (unsigned long)&cnl_info }, - { PCI_VDEVICE(INTEL, 0x7e23), (unsigned long)&cnl_info }, - { PCI_VDEVICE(INTEL, 0x7f24), (unsigned long)&cnl_info }, - { PCI_VDEVICE(INTEL, 0x9d24), (unsigned long)&cnl_info }, - { PCI_VDEVICE(INTEL, 0x9da4), (unsigned long)&cnl_info }, - { PCI_VDEVICE(INTEL, 0xa0a4), (unsigned long)&cnl_info }, - { PCI_VDEVICE(INTEL, 0xa1a4), (unsigned long)&bxt_info }, - { PCI_VDEVICE(INTEL, 0xa224), (unsigned long)&bxt_info }, - { PCI_VDEVICE(INTEL, 0xa2a4), (unsigned long)&cnl_info }, - { PCI_VDEVICE(INTEL, 0xa324), (unsigned long)&cnl_info }, - { PCI_VDEVICE(INTEL, 0xa3a4), (unsigned long)&cnl_info }, - { PCI_VDEVICE(INTEL, 0xa823), (unsigned long)&cnl_info }, - { PCI_VDEVICE(INTEL, 0xd323), (unsigned long)&cnl_info }, - { PCI_VDEVICE(INTEL, 0xe323), (unsigned long)&cnl_info }, - { PCI_VDEVICE(INTEL, 0xe423), (unsigned long)&cnl_info }, + { PCI_VDEVICE(INTEL, 0x02a4), .driver_data = (unsigned long)&cnl_info }, + { PCI_VDEVICE(INTEL, 0x06a4), .driver_data = (unsigned long)&cnl_info }, + { PCI_VDEVICE(INTEL, 0x18e0), .driver_data = (unsigned long)&bxt_info }, + { PCI_VDEVICE(INTEL, 0x19e0), .driver_data = (unsigned long)&bxt_info }, + { PCI_VDEVICE(INTEL, 0x1bca), .driver_data = (unsigned long)&bxt_info }, + { PCI_VDEVICE(INTEL, 0x34a4), .driver_data = (unsigned long)&cnl_info }, + { PCI_VDEVICE(INTEL, 0x38a4), .driver_data = (unsigned long)&bxt_info }, + { PCI_VDEVICE(INTEL, 0x43a4), .driver_data = (unsigned long)&cnl_info }, + { PCI_VDEVICE(INTEL, 0x4b24), .driver_data = (unsigned long)&bxt_info }, + { PCI_VDEVICE(INTEL, 0x4d23), .driver_data = (unsigned long)&cnl_info }, + { PCI_VDEVICE(INTEL, 0x4da4), .driver_data = (unsigned long)&bxt_info }, + { PCI_VDEVICE(INTEL, 0x51a4), .driver_data = (unsigned long)&cnl_info }, + { PCI_VDEVICE(INTEL, 0x54a4), .driver_data = (unsigned long)&cnl_info }, + { PCI_VDEVICE(INTEL, 0x5794), .driver_data = (unsigned long)&cnl_info }, + { PCI_VDEVICE(INTEL, 0x5825), .driver_data = (unsigned long)&cnl_info }, + { PCI_VDEVICE(INTEL, 0x6e24), .driver_data = (unsigned long)&cnl_info }, + { PCI_VDEVICE(INTEL, 0x7723), .driver_data = (unsigned long)&cnl_info }, + { PCI_VDEVICE(INTEL, 0x7a24), .driver_data = (unsigned long)&cnl_info }, + { PCI_VDEVICE(INTEL, 0x7aa4), .driver_data = (unsigned long)&cnl_info }, + { PCI_VDEVICE(INTEL, 0x7e23), .driver_data = (unsigned long)&cnl_info }, + { PCI_VDEVICE(INTEL, 0x7f24), .driver_data = (unsigned long)&cnl_info }, + { PCI_VDEVICE(INTEL, 0x9d24), .driver_data = (unsigned long)&cnl_info }, + { PCI_VDEVICE(INTEL, 0x9da4), .driver_data = (unsigned long)&cnl_info }, + { PCI_VDEVICE(INTEL, 0xa0a4), .driver_data = (unsigned long)&cnl_info }, + { PCI_VDEVICE(INTEL, 0xa1a4), .driver_data = (unsigned long)&bxt_info }, + { PCI_VDEVICE(INTEL, 0xa224), .driver_data = (unsigned long)&bxt_info }, + { PCI_VDEVICE(INTEL, 0xa2a4), .driver_data = (unsigned long)&cnl_info }, + { PCI_VDEVICE(INTEL, 0xa324), .driver_data = (unsigned long)&cnl_info }, + { PCI_VDEVICE(INTEL, 0xa3a4), .driver_data = (unsigned long)&cnl_info }, + { PCI_VDEVICE(INTEL, 0xa823), .driver_data = (unsigned long)&cnl_info }, + { PCI_VDEVICE(INTEL, 0xd323), .driver_data = (unsigned long)&cnl_info }, + { PCI_VDEVICE(INTEL, 0xe323), .driver_data = (unsigned long)&cnl_info }, + { PCI_VDEVICE(INTEL, 0xe423), .driver_data = (unsigned long)&cnl_info }, { }, }; MODULE_DEVICE_TABLE(pci, intel_spi_pci_ids); diff --git a/drivers/spi/spi-pci1xxxx.c b/drivers/spi/spi-pci1xxxx.c index 8577a19705de..af6ed78493e3 100644 --- a/drivers/spi/spi-pci1xxxx.c +++ b/drivers/spi/spi-pci1xxxx.c @@ -173,27 +173,27 @@ struct pci1xxxx_spi { }; static const struct pci_device_id pci1xxxx_spi_pci_id_table[] = { - { PCI_DEVICE_SUB(VENDOR_ID_MCHP, 0xa004, PCI_ANY_ID, 0x0001), 0, 0, 0x02}, - { PCI_DEVICE_SUB(VENDOR_ID_MCHP, 0xa004, PCI_ANY_ID, 0x0002), 0, 0, 0x01}, - { PCI_DEVICE_SUB(VENDOR_ID_MCHP, 0xa004, PCI_ANY_ID, 0x0003), 0, 0, 0x11}, - { PCI_DEVICE_SUB(VENDOR_ID_MCHP, 0xa004, PCI_ANY_ID, PCI_ANY_ID), 0, 0, 0x01}, - { PCI_DEVICE_SUB(VENDOR_ID_MCHP, 0xa014, PCI_ANY_ID, 0x0001), 0, 0, 0x02}, - { PCI_DEVICE_SUB(VENDOR_ID_MCHP, 0xa014, PCI_ANY_ID, 0x0002), 0, 0, 0x01}, - { PCI_DEVICE_SUB(VENDOR_ID_MCHP, 0xa014, PCI_ANY_ID, 0x0003), 0, 0, 0x11}, - { PCI_DEVICE_SUB(VENDOR_ID_MCHP, 0xa014, PCI_ANY_ID, PCI_ANY_ID), 0, 0, 0x01}, - { PCI_DEVICE_SUB(VENDOR_ID_MCHP, 0xa024, PCI_ANY_ID, 0x0001), 0, 0, 0x02}, - { PCI_DEVICE_SUB(VENDOR_ID_MCHP, 0xa024, PCI_ANY_ID, 0x0002), 0, 0, 0x01}, - { PCI_DEVICE_SUB(VENDOR_ID_MCHP, 0xa024, PCI_ANY_ID, 0x0003), 0, 0, 0x11}, - { PCI_DEVICE_SUB(VENDOR_ID_MCHP, 0xa024, PCI_ANY_ID, PCI_ANY_ID), 0, 0, 0x01}, - { PCI_DEVICE_SUB(VENDOR_ID_MCHP, 0xa034, PCI_ANY_ID, 0x0001), 0, 0, 0x02}, - { PCI_DEVICE_SUB(VENDOR_ID_MCHP, 0xa034, PCI_ANY_ID, 0x0002), 0, 0, 0x01}, - { PCI_DEVICE_SUB(VENDOR_ID_MCHP, 0xa034, PCI_ANY_ID, 0x0003), 0, 0, 0x11}, - { PCI_DEVICE_SUB(VENDOR_ID_MCHP, 0xa034, PCI_ANY_ID, PCI_ANY_ID), 0, 0, 0x01}, - { PCI_DEVICE_SUB(VENDOR_ID_MCHP, 0xa044, PCI_ANY_ID, 0x0001), 0, 0, 0x02}, - { PCI_DEVICE_SUB(VENDOR_ID_MCHP, 0xa044, PCI_ANY_ID, 0x0002), 0, 0, 0x01}, - { PCI_DEVICE_SUB(VENDOR_ID_MCHP, 0xa044, PCI_ANY_ID, 0x0003), 0, 0, 0x11}, - { PCI_DEVICE_SUB(VENDOR_ID_MCHP, 0xa044, PCI_ANY_ID, PCI_ANY_ID), 0, 0, 0x01}, - { 0, } + { PCI_DEVICE_SUB(VENDOR_ID_MCHP, 0xa004, PCI_ANY_ID, 0x0001), .driver_data = 0x02 }, + { PCI_DEVICE_SUB(VENDOR_ID_MCHP, 0xa004, PCI_ANY_ID, 0x0002), .driver_data = 0x01 }, + { PCI_DEVICE_SUB(VENDOR_ID_MCHP, 0xa004, PCI_ANY_ID, 0x0003), .driver_data = 0x11 }, + { PCI_DEVICE_SUB(VENDOR_ID_MCHP, 0xa004, PCI_ANY_ID, PCI_ANY_ID), .driver_data = 0x01 }, + { PCI_DEVICE_SUB(VENDOR_ID_MCHP, 0xa014, PCI_ANY_ID, 0x0001), .driver_data = 0x02 }, + { PCI_DEVICE_SUB(VENDOR_ID_MCHP, 0xa014, PCI_ANY_ID, 0x0002), .driver_data = 0x01 }, + { PCI_DEVICE_SUB(VENDOR_ID_MCHP, 0xa014, PCI_ANY_ID, 0x0003), .driver_data = 0x11 }, + { PCI_DEVICE_SUB(VENDOR_ID_MCHP, 0xa014, PCI_ANY_ID, PCI_ANY_ID), .driver_data = 0x01 }, + { PCI_DEVICE_SUB(VENDOR_ID_MCHP, 0xa024, PCI_ANY_ID, 0x0001), .driver_data = 0x02 }, + { PCI_DEVICE_SUB(VENDOR_ID_MCHP, 0xa024, PCI_ANY_ID, 0x0002), .driver_data = 0x01 }, + { PCI_DEVICE_SUB(VENDOR_ID_MCHP, 0xa024, PCI_ANY_ID, 0x0003), .driver_data = 0x11 }, + { PCI_DEVICE_SUB(VENDOR_ID_MCHP, 0xa024, PCI_ANY_ID, PCI_ANY_ID), .driver_data = 0x01 }, + { PCI_DEVICE_SUB(VENDOR_ID_MCHP, 0xa034, PCI_ANY_ID, 0x0001), .driver_data = 0x02 }, + { PCI_DEVICE_SUB(VENDOR_ID_MCHP, 0xa034, PCI_ANY_ID, 0x0002), .driver_data = 0x01 }, + { PCI_DEVICE_SUB(VENDOR_ID_MCHP, 0xa034, PCI_ANY_ID, 0x0003), .driver_data = 0x11 }, + { PCI_DEVICE_SUB(VENDOR_ID_MCHP, 0xa034, PCI_ANY_ID, PCI_ANY_ID), .driver_data = 0x01 }, + { PCI_DEVICE_SUB(VENDOR_ID_MCHP, 0xa044, PCI_ANY_ID, 0x0001), .driver_data = 0x02 }, + { PCI_DEVICE_SUB(VENDOR_ID_MCHP, 0xa044, PCI_ANY_ID, 0x0002), .driver_data = 0x01 }, + { PCI_DEVICE_SUB(VENDOR_ID_MCHP, 0xa044, PCI_ANY_ID, 0x0003), .driver_data = 0x11 }, + { PCI_DEVICE_SUB(VENDOR_ID_MCHP, 0xa044, PCI_ANY_ID, PCI_ANY_ID), .driver_data = 0x01 }, + { } }; MODULE_DEVICE_TABLE(pci, pci1xxxx_spi_pci_id_table); diff --git a/drivers/spi/spi-topcliff-pch.c b/drivers/spi/spi-topcliff-pch.c index 14d11450e86d..02ced638d8b4 100644 --- a/drivers/spi/spi-topcliff-pch.c +++ b/drivers/spi/spi-topcliff-pch.c @@ -207,10 +207,10 @@ struct pch_pd_dev_save { }; static const struct pci_device_id pch_spi_pcidev_id[] = { - { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_GE_SPI), 1, }, - { PCI_VDEVICE(ROHM, PCI_DEVICE_ID_ML7213_SPI), 2, }, - { PCI_VDEVICE(ROHM, PCI_DEVICE_ID_ML7223_SPI), 1, }, - { PCI_VDEVICE(ROHM, PCI_DEVICE_ID_ML7831_SPI), 1, }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_GE_SPI), .driver_data = 1 }, + { PCI_VDEVICE(ROHM, PCI_DEVICE_ID_ML7213_SPI), .driver_data = 2 }, + { PCI_VDEVICE(ROHM, PCI_DEVICE_ID_ML7223_SPI), .driver_data = 1 }, + { PCI_VDEVICE(ROHM, PCI_DEVICE_ID_ML7831_SPI), .driver_data = 1 }, { } }; From 00cef7eb08c8b9af0978f667c77c6a30b71b7e22 Mon Sep 17 00:00:00 2001 From: Guodong Xu Date: Tue, 5 May 2026 09:53:34 -0400 Subject: [PATCH 045/118] spi: spacemit: add u64 cast to NSEC_PER_SEC to avoid 32-bit overflow NSEC_PER_SEC expands to the long constant 1000000000L, so NSEC_PER_SEC * BITS_PER_BYTE (8 * 10^9) overflows on 32-bit-long architectures before the result reaches the u64 nsec_per_word. Promote the multiplication to u64 by casting the first operand, which is NSEC_PER_SEC. Fixes: efcd8b9d1111 ("spi: spacemit: introduce SpacemiT K1 SPI controller driver") Suggested-by: Alex Elder Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202605050437.RS6mmV2b-lkp@intel.com/ Closes: https://lore.kernel.org/oe-kbuild-all/202605050317.Tf9j487w-lkp@intel.com/ Signed-off-by: Guodong Xu Link: https://patch.msgid.link/20260505-spi-spacemit-k1-fix-overflow-v1-1-77564c2e4e86@riscstar.com Signed-off-by: Mark Brown --- drivers/spi/spi-spacemit-k1.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/spi/spi-spacemit-k1.c b/drivers/spi/spi-spacemit-k1.c index 99db429db0b2..215fe66d27b4 100644 --- a/drivers/spi/spi-spacemit-k1.c +++ b/drivers/spi/spi-spacemit-k1.c @@ -390,7 +390,7 @@ static int k1_spi_set_speed(struct k1_spi_driver_data *drv_data, * ticks_per_word = BITS_PER_BYTE * drv_data->bytes; * We do the divide last for better accuracy. */ - nsec_per_word = NSEC_PER_SEC * BITS_PER_BYTE * drv_data->bytes; + nsec_per_word = (u64)NSEC_PER_SEC * BITS_PER_BYTE * drv_data->bytes; nsec_per_word = DIV_ROUND_UP_ULL(nsec_per_word, drv_data->rate); /* From 88f731e7962945614a477a8a86c2517d87fd6b02 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Wed, 6 May 2026 10:51:44 -0700 Subject: [PATCH 046/118] spi: s3c64xx: fix all kernel-doc warnings Add kernel-doc for one struct member and use the correct function name to eliminate kernel-doc warnings: Warning: include/linux/platform_data/spi-s3c64xx.h:40 struct member 'polling' not described in 's3c64xx_spi_info' Warning: include/linux/platform_data/spi-s3c64xx.h:51 expecting prototype for s3c64xx_spi_set_platdata(). Prototype was for s3c64xx_spi0_set_platdata() instead Signed-off-by: Randy Dunlap Reviewed-by: Krzysztof Kozlowski Reviewed-by: Tudor Ambarus Link: https://patch.msgid.link/20260506175144.449364-1-rdunlap@infradead.org Signed-off-by: Mark Brown --- include/linux/platform_data/spi-s3c64xx.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/linux/platform_data/spi-s3c64xx.h b/include/linux/platform_data/spi-s3c64xx.h index 1d6e6c424fc6..f92bb4a1213b 100644 --- a/include/linux/platform_data/spi-s3c64xx.h +++ b/include/linux/platform_data/spi-s3c64xx.h @@ -30,6 +30,7 @@ struct s3c64xx_spi_csinfo { * @src_clk_nr: Clock source index for the CLK_CFG[SPI_CLKSEL] field. * @num_cs: Number of CS this controller emulates. * @no_cs: Used when CS line is not connected. + * @polling: Using polling mode when %true (no 'dmas' property in devicetree) * @cfg_gpio: Configure pins for this SPI controller. */ struct s3c64xx_spi_info { @@ -41,7 +42,7 @@ struct s3c64xx_spi_info { }; /** - * s3c64xx_spi_set_platdata - SPI Controller configure callback by the board + * s3c64xx_spi0_set_platdata - SPI Controller configure callback by the board * initialization code. * @src_clk_nr: Clock the SPI controller is to use to generate SPI clocks. * @num_cs: Number of elements in the 'cs' array. From 54725e3049e1684bc77e0cf892ab1d194c515121 Mon Sep 17 00:00:00 2001 From: Stepan Ionichev Date: Wed, 6 May 2026 23:35:12 +0500 Subject: [PATCH 047/118] spi: amlogic-spisg: drop misleading NULL check on exdesc aml_spisg_setup_transfer() takes a non-NULL exdesc pointer; the function dereferences exdesc unconditionally later in the body to populate the SPI scatter-gather descriptors (tx_ccsg / rx_ccsg). The sole caller, aml_spisg_transfer_one_message(), always passes a valid pointer derived from kcalloc(). The "if (exdesc)" guard around the memset() at the start of the function is therefore dead and misleading -- it suggests callers may pass NULL when in fact they may not. smatch flags the inconsistency: drivers/spi/spi-amlogic-spisg.c:314 aml_spisg_setup_transfer() error: we previously assumed 'exdesc' could be null (see line 261) Drop the check; the unconditional memset matches the unconditional dereferences elsewhere in the function and removes the inconsistency that smatch reports. No functional change. Signed-off-by: Stepan Ionichev Reviewed-by: Xianwei Zhao Link: https://patch.msgid.link/20260506183513.482-1-sozdayvek@gmail.com Signed-off-by: Mark Brown --- drivers/spi/spi-amlogic-spisg.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/spi/spi-amlogic-spisg.c b/drivers/spi/spi-amlogic-spisg.c index f9de2d2c9213..601fb73b3595 100644 --- a/drivers/spi/spi-amlogic-spisg.c +++ b/drivers/spi/spi-amlogic-spisg.c @@ -258,8 +258,7 @@ static int aml_spisg_setup_transfer(struct spisg_device *spisg, int ret; memset(desc, 0, sizeof(*desc)); - if (exdesc) - memset(exdesc, 0, sizeof(*exdesc)); + memset(exdesc, 0, sizeof(*exdesc)); aml_spisg_set_speed(spisg, xfer->speed_hz); xfer->effective_speed_hz = spisg->effective_speed_hz; From 4af89d7d8552a1f0437521acf89fa51601bce973 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 5 May 2026 09:28:50 +0200 Subject: [PATCH 048/118] spi: pic32: switch to managed controller allocation Switch to device managed controller allocation to simplify error handling and to avoid having to take another reference during deregistration. Signed-off-by: Johan Hovold Link: https://patch.msgid.link/20260505072909.618363-2-johan@kernel.org Signed-off-by: Mark Brown --- drivers/spi/spi-pic32.c | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/drivers/spi/spi-pic32.c b/drivers/spi/spi-pic32.c index 70427e529945..972128271e4b 100644 --- a/drivers/spi/spi-pic32.c +++ b/drivers/spi/spi-pic32.c @@ -752,7 +752,7 @@ static int pic32_spi_probe(struct platform_device *pdev) struct pic32_spi *pic32s; int ret; - host = spi_alloc_host(&pdev->dev, sizeof(*pic32s)); + host = devm_spi_alloc_host(&pdev->dev, sizeof(*pic32s)); if (!host) return -ENOMEM; @@ -761,7 +761,7 @@ static int pic32_spi_probe(struct platform_device *pdev) ret = pic32_spi_hw_probe(pdev, pic32s); if (ret) - goto err_host; + return ret; host->dev.of_node = pdev->dev.of_node; host->mode_bits = SPI_MODE_3 | SPI_MODE_0 | SPI_CS_HIGH; @@ -833,8 +833,7 @@ static int pic32_spi_probe(struct platform_device *pdev) err_bailout: pic32_spi_dma_unprep(pic32s); -err_host: - spi_controller_put(host); + return ret; } @@ -842,14 +841,10 @@ static void pic32_spi_remove(struct platform_device *pdev) { struct pic32_spi *pic32s = platform_get_drvdata(pdev); - spi_controller_get(pic32s->host); - spi_unregister_controller(pic32s->host); pic32_spi_disable(pic32s); pic32_spi_dma_unprep(pic32s); - - spi_controller_put(pic32s->host); } static const struct of_device_id pic32_spi_of_match[] = { From dda3a77e1a32b329d3c543a1ac236106acf64ec5 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 5 May 2026 09:28:51 +0200 Subject: [PATCH 049/118] spi: pic32-sqi: switch to managed controller allocation Switch to device managed controller allocation to simplify error handling and to avoid having to take another reference during deregistration. Signed-off-by: Johan Hovold Link: https://patch.msgid.link/20260505072909.618363-3-johan@kernel.org Signed-off-by: Mark Brown --- drivers/spi/spi-pic32-sqi.c | 28 ++++++++-------------------- 1 file changed, 8 insertions(+), 20 deletions(-) diff --git a/drivers/spi/spi-pic32-sqi.c b/drivers/spi/spi-pic32-sqi.c index 41662992dbe5..5d3921e29461 100644 --- a/drivers/spi/spi-pic32-sqi.c +++ b/drivers/spi/spi-pic32-sqi.c @@ -572,7 +572,7 @@ static int pic32_sqi_probe(struct platform_device *pdev) struct pic32_sqi *sqi; int ret; - host = spi_alloc_host(&pdev->dev, sizeof(*sqi)); + host = devm_spi_alloc_host(&pdev->dev, sizeof(*sqi)); if (!host) return -ENOMEM; @@ -580,31 +580,25 @@ static int pic32_sqi_probe(struct platform_device *pdev) sqi->host = host; sqi->regs = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(sqi->regs)) { - ret = PTR_ERR(sqi->regs); - goto err_free_host; - } + if (IS_ERR(sqi->regs)) + return PTR_ERR(sqi->regs); /* irq */ sqi->irq = platform_get_irq(pdev, 0); - if (sqi->irq < 0) { - ret = sqi->irq; - goto err_free_host; - } + if (sqi->irq < 0) + return sqi->irq; /* clocks */ sqi->sys_clk = devm_clk_get_enabled(&pdev->dev, "reg_ck"); if (IS_ERR(sqi->sys_clk)) { - ret = PTR_ERR(sqi->sys_clk); dev_err(&pdev->dev, "no sys_clk ?\n"); - goto err_free_host; + return PTR_ERR(sqi->sys_clk); } sqi->base_clk = devm_clk_get_enabled(&pdev->dev, "spi_ck"); if (IS_ERR(sqi->base_clk)) { - ret = PTR_ERR(sqi->base_clk); dev_err(&pdev->dev, "no base clk ?\n"); - goto err_free_host; + return PTR_ERR(sqi->base_clk); } init_completion(&sqi->xfer_done); @@ -616,7 +610,7 @@ static int pic32_sqi_probe(struct platform_device *pdev) ret = ring_desc_ring_alloc(sqi); if (ret) { dev_err(&pdev->dev, "ring alloc failed\n"); - goto err_free_host; + return ret; } /* install irq handlers */ @@ -656,8 +650,6 @@ static int pic32_sqi_probe(struct platform_device *pdev) err_free_ring: ring_desc_ring_free(sqi); -err_free_host: - spi_controller_put(host); return ret; } @@ -665,15 +657,11 @@ static void pic32_sqi_remove(struct platform_device *pdev) { struct pic32_sqi *sqi = platform_get_drvdata(pdev); - spi_controller_get(sqi->host); - spi_unregister_controller(sqi->host); /* release resources */ free_irq(sqi->irq, sqi); ring_desc_ring_free(sqi); - - spi_controller_put(sqi->host); } static const struct of_device_id pic32_sqi_of_ids[] = { From 02efc5557c8e4202b1c5d260ec532986e9769897 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 5 May 2026 09:28:52 +0200 Subject: [PATCH 050/118] spi: pl022: switch to managed controller allocation Switch to device managed controller allocation to simplify error handling and to avoid having to take another reference during deregistration. Signed-off-by: Johan Hovold Reviewed-by: Linus Walleij Link: https://patch.msgid.link/20260505072909.618363-4-johan@kernel.org Signed-off-by: Mark Brown --- drivers/spi/spi-pl022.c | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/drivers/spi/spi-pl022.c b/drivers/spi/spi-pl022.c index 9c0211f94fd0..95652df5fd09 100644 --- a/drivers/spi/spi-pl022.c +++ b/drivers/spi/spi-pl022.c @@ -1868,7 +1868,7 @@ static int pl022_probe(struct amba_device *adev, const struct amba_id *id) } /* Allocate host with space for data */ - host = spi_alloc_host(dev, sizeof(struct pl022)); + host = devm_spi_alloc_host(dev, sizeof(struct pl022)); if (host == NULL) { dev_err(&adev->dev, "probe - cannot alloc SPI host\n"); return -ENOMEM; @@ -1907,7 +1907,7 @@ static int pl022_probe(struct amba_device *adev, const struct amba_id *id) status = amba_request_regions(adev, NULL); if (status) - goto err_no_ioregion; + return status; pl022->phybase = adev->res.start; pl022->virtbase = devm_ioremap(dev, adev->res.start, @@ -1984,8 +1984,7 @@ static int pl022_probe(struct amba_device *adev, const struct amba_id *id) err_no_clk: err_no_ioremap: amba_release_regions(adev); - err_no_ioregion: - spi_controller_put(host); + return status; } @@ -1997,8 +1996,6 @@ pl022_remove(struct amba_device *adev) if (!pl022) return; - spi_controller_get(pl022->host); - spi_unregister_controller(pl022->host); /* @@ -2012,8 +2009,6 @@ pl022_remove(struct amba_device *adev) pl022_dma_remove(pl022); amba_release_regions(adev); - - spi_controller_put(pl022->host); } #ifdef CONFIG_PM_SLEEP From 86e8160240af1143e0a9f185e45ac300fe0d93a6 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 5 May 2026 09:28:53 +0200 Subject: [PATCH 051/118] spi: qup: switch to managed controller allocation Switch to device managed controller allocation to simplify error handling and to avoid having to take another reference during deregistration. Signed-off-by: Johan Hovold Link: https://patch.msgid.link/20260505072909.618363-5-johan@kernel.org Signed-off-by: Mark Brown --- drivers/spi/spi-qup.c | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/drivers/spi/spi-qup.c b/drivers/spi/spi-qup.c index 45d9b4cb75e4..4df01ef2e662 100644 --- a/drivers/spi/spi-qup.c +++ b/drivers/spi/spi-qup.c @@ -1071,11 +1071,9 @@ static int spi_qup_probe(struct platform_device *pdev) if (ret && ret != -ENODEV) return dev_err_probe(dev, ret, "invalid OPP table\n"); - host = spi_alloc_host(dev, sizeof(struct spi_qup)); - if (!host) { - dev_err(dev, "cannot allocate host\n"); + host = devm_spi_alloc_host(dev, sizeof(struct spi_qup)); + if (!host) return -ENOMEM; - } /* use num-cs unless not present or out of range */ if (of_property_read_u32(dev->of_node, "num-cs", &num_cs) || @@ -1108,7 +1106,7 @@ static int spi_qup_probe(struct platform_device *pdev) ret = spi_qup_init_dma(host, res->start); if (ret == -EPROBE_DEFER) - goto error; + return ret; else if (!ret) host->can_dma = spi_qup_can_dma; @@ -1206,8 +1204,7 @@ static int spi_qup_probe(struct platform_device *pdev) clk_disable_unprepare(iclk); error_dma: spi_qup_release_dma(host); -error: - spi_controller_put(host); + return ret; } @@ -1320,8 +1317,6 @@ static void spi_qup_remove(struct platform_device *pdev) struct spi_qup *controller = spi_controller_get_devdata(host); int ret; - spi_controller_get(host); - spi_unregister_controller(host); ret = pm_runtime_get_sync(&pdev->dev); @@ -1343,8 +1338,6 @@ static void spi_qup_remove(struct platform_device *pdev) pm_runtime_put_noidle(&pdev->dev); pm_runtime_disable(&pdev->dev); - - spi_controller_put(host); } static const struct of_device_id spi_qup_dt_match[] = { From 368d0e6c6f82a090b3fb080929f4478217e597e8 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 5 May 2026 09:28:54 +0200 Subject: [PATCH 052/118] spi: rspi: switch to managed controller allocation Switch to device managed controller allocation to simplify error handling and to avoid having to take another reference during deregistration. Signed-off-by: Johan Hovold Link: https://patch.msgid.link/20260505072909.618363-6-johan@kernel.org Signed-off-by: Mark Brown --- drivers/spi/spi-rspi.c | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/drivers/spi/spi-rspi.c b/drivers/spi/spi-rspi.c index a8180dece716..951e9a8af547 100644 --- a/drivers/spi/spi-rspi.c +++ b/drivers/spi/spi-rspi.c @@ -1171,14 +1171,10 @@ static void rspi_remove(struct platform_device *pdev) { struct rspi_data *rspi = platform_get_drvdata(pdev); - spi_controller_get(rspi->ctlr); - spi_unregister_controller(rspi->ctlr); rspi_release_dma(rspi->ctlr); pm_runtime_disable(&pdev->dev); - - spi_controller_put(rspi->ctlr); } static const struct spi_ops rspi_ops = { @@ -1294,7 +1290,7 @@ static int rspi_probe(struct platform_device *pdev) const struct spi_ops *ops; unsigned long clksrc; - ctlr = spi_alloc_host(&pdev->dev, sizeof(struct rspi_data)); + ctlr = devm_spi_alloc_host(&pdev->dev, sizeof(struct rspi_data)); if (ctlr == NULL) return -ENOMEM; @@ -1302,7 +1298,7 @@ static int rspi_probe(struct platform_device *pdev) if (ops) { ret = rspi_parse_dt(&pdev->dev, ctlr); if (ret) - goto error1; + return ret; } else { ops = (struct spi_ops *)pdev->id_entry->driver_data; ctlr->num_chipselect = 2; /* default */ @@ -1314,16 +1310,13 @@ static int rspi_probe(struct platform_device *pdev) rspi->ctlr = ctlr; rspi->addr = devm_platform_get_and_ioremap_resource(pdev, 0, &res); - if (IS_ERR(rspi->addr)) { - ret = PTR_ERR(rspi->addr); - goto error1; - } + if (IS_ERR(rspi->addr)) + return PTR_ERR(rspi->addr); rspi->clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(rspi->clk)) { dev_err(&pdev->dev, "cannot get clock\n"); - ret = PTR_ERR(rspi->clk); - goto error1; + return PTR_ERR(rspi->clk); } rspi->pdev = pdev; @@ -1396,8 +1389,6 @@ static int rspi_probe(struct platform_device *pdev) rspi_release_dma(ctlr); error2: pm_runtime_disable(&pdev->dev); -error1: - spi_controller_put(ctlr); return ret; } From 042414e4da73e67d0e1e77ac1292e6aa15a54928 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 5 May 2026 09:28:55 +0200 Subject: [PATCH 053/118] spi: sh-hspi: switch to managed controller allocation Switch to device managed controller allocation to simplify error handling and to avoid having to take another reference during deregistration. Signed-off-by: Johan Hovold Link: https://patch.msgid.link/20260505072909.618363-7-johan@kernel.org Signed-off-by: Mark Brown --- drivers/spi/spi-sh-hspi.c | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/drivers/spi/spi-sh-hspi.c b/drivers/spi/spi-sh-hspi.c index 1e3ca718ca73..f840467cfdb2 100644 --- a/drivers/spi/spi-sh-hspi.c +++ b/drivers/spi/spi-sh-hspi.c @@ -224,15 +224,14 @@ static int hspi_probe(struct platform_device *pdev) return -EINVAL; } - ctlr = spi_alloc_host(&pdev->dev, sizeof(*hspi)); + ctlr = devm_spi_alloc_host(&pdev->dev, sizeof(*hspi)); if (!ctlr) return -ENOMEM; clk = clk_get(&pdev->dev, NULL); if (IS_ERR(clk)) { dev_err(&pdev->dev, "couldn't get clock\n"); - ret = -EINVAL; - goto error0; + return PTR_ERR(clk); } hspi = spi_controller_get_devdata(ctlr); @@ -269,8 +268,6 @@ static int hspi_probe(struct platform_device *pdev) pm_runtime_disable(&pdev->dev); error1: clk_put(clk); - error0: - spi_controller_put(ctlr); return ret; } @@ -279,15 +276,11 @@ static void hspi_remove(struct platform_device *pdev) { struct hspi_priv *hspi = platform_get_drvdata(pdev); - spi_controller_get(hspi->ctlr); - spi_unregister_controller(hspi->ctlr); pm_runtime_disable(&pdev->dev); clk_put(hspi->clk); - - spi_controller_put(hspi->ctlr); } static const struct of_device_id hspi_of_match[] = { From 354b0a4ad4eb50a947b1b7b143c01a01a37489e7 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 5 May 2026 09:28:56 +0200 Subject: [PATCH 054/118] spi: sh-msiof: switch to managed controller allocation Switch to device managed controller allocation to simplify error handling and to avoid having to take another reference during deregistration. Signed-off-by: Johan Hovold Link: https://patch.msgid.link/20260505072909.618363-8-johan@kernel.org Signed-off-by: Mark Brown --- drivers/spi/spi-sh-msiof.c | 28 +++++++++------------------- 1 file changed, 9 insertions(+), 19 deletions(-) diff --git a/drivers/spi/spi-sh-msiof.c b/drivers/spi/spi-sh-msiof.c index f114b6313f4f..070e16bc764f 100644 --- a/drivers/spi/spi-sh-msiof.c +++ b/drivers/spi/spi-sh-msiof.c @@ -1215,9 +1215,9 @@ static int sh_msiof_spi_probe(struct platform_device *pdev) info->dtdl = 200; if (info->mode == MSIOF_SPI_TARGET) - ctlr = spi_alloc_target(dev, sizeof(struct sh_msiof_spi_priv)); + ctlr = devm_spi_alloc_target(dev, sizeof(struct sh_msiof_spi_priv)); else - ctlr = spi_alloc_host(dev, sizeof(struct sh_msiof_spi_priv)); + ctlr = devm_spi_alloc_host(dev, sizeof(struct sh_msiof_spi_priv)); if (ctlr == NULL) return -ENOMEM; @@ -1234,26 +1234,21 @@ static int sh_msiof_spi_probe(struct platform_device *pdev) p->clk = devm_clk_get(dev, NULL); if (IS_ERR(p->clk)) { dev_err(dev, "cannot get clock\n"); - ret = PTR_ERR(p->clk); - goto err1; + return PTR_ERR(p->clk); } i = platform_get_irq(pdev, 0); - if (i < 0) { - ret = i; - goto err1; - } + if (i < 0) + return i; p->mapbase = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(p->mapbase)) { - ret = PTR_ERR(p->mapbase); - goto err1; - } + if (IS_ERR(p->mapbase)) + return PTR_ERR(p->mapbase); ret = devm_request_irq(dev, i, sh_msiof_spi_irq, 0, dev_name(dev), p); if (ret) { dev_err(dev, "unable to request irq\n"); - goto err1; + return ret; } p->pdev = pdev; @@ -1300,8 +1295,7 @@ static int sh_msiof_spi_probe(struct platform_device *pdev) err2: sh_msiof_release_dma(p); pm_runtime_disable(dev); - err1: - spi_controller_put(ctlr); + return ret; } @@ -1309,14 +1303,10 @@ static void sh_msiof_spi_remove(struct platform_device *pdev) { struct sh_msiof_spi_priv *p = platform_get_drvdata(pdev); - spi_controller_get(p->ctlr); - spi_unregister_controller(p->ctlr); sh_msiof_release_dma(p); pm_runtime_disable(&pdev->dev); - - spi_controller_put(p->ctlr); } static const struct platform_device_id spi_driver_ids[] = { From fd260013577d0a6f3b6a8b07d059c34fb710efac Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 5 May 2026 09:28:57 +0200 Subject: [PATCH 055/118] spi: sifive: switch to managed controller allocation Switch to device managed controller allocation to simplify error handling and to avoid having to take another reference during deregistration. Signed-off-by: Johan Hovold Link: https://patch.msgid.link/20260505072909.618363-9-johan@kernel.org Signed-off-by: Mark Brown --- drivers/spi/spi-sifive.c | 39 +++++++++++---------------------------- 1 file changed, 11 insertions(+), 28 deletions(-) diff --git a/drivers/spi/spi-sifive.c b/drivers/spi/spi-sifive.c index 74a3e32fd2b5..cee4d92e46f4 100644 --- a/drivers/spi/spi-sifive.c +++ b/drivers/spi/spi-sifive.c @@ -296,7 +296,7 @@ static int sifive_spi_probe(struct platform_device *pdev) u32 cs_bits, max_bits_per_word; struct spi_controller *host; - host = spi_alloc_host(&pdev->dev, sizeof(struct sifive_spi)); + host = devm_spi_alloc_host(&pdev->dev, sizeof(struct sifive_spi)); if (!host) { dev_err(&pdev->dev, "out of memory\n"); return -ENOMEM; @@ -307,24 +307,19 @@ static int sifive_spi_probe(struct platform_device *pdev) platform_set_drvdata(pdev, host); spi->regs = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(spi->regs)) { - ret = PTR_ERR(spi->regs); - goto put_host; - } + if (IS_ERR(spi->regs)) + return PTR_ERR(spi->regs); /* Spin up the bus clock before hitting registers */ spi->clk = devm_clk_get_enabled(&pdev->dev, NULL); if (IS_ERR(spi->clk)) { dev_err(&pdev->dev, "Unable to find bus clock\n"); - ret = PTR_ERR(spi->clk); - goto put_host; + return PTR_ERR(spi->clk); } irq = platform_get_irq(pdev, 0); - if (irq < 0) { - ret = irq; - goto put_host; - } + if (irq < 0) + return irq; /* Optional parameters */ ret = @@ -339,8 +334,7 @@ static int sifive_spi_probe(struct platform_device *pdev) if (!ret && max_bits_per_word < 8) { dev_err(&pdev->dev, "Only 8bit SPI words supported by the driver\n"); - ret = -EINVAL; - goto put_host; + return -EINVAL; } /* probe the number of CS lines */ @@ -350,15 +344,13 @@ static int sifive_spi_probe(struct platform_device *pdev) sifive_spi_write(spi, SIFIVE_SPI_REG_CSDEF, spi->cs_inactive); if (!cs_bits) { dev_err(&pdev->dev, "Could not auto probe CS lines\n"); - ret = -EINVAL; - goto put_host; + return -EINVAL; } num_cs = ilog2(cs_bits) + 1; if (num_cs > SIFIVE_SPI_MAX_CS) { dev_err(&pdev->dev, "Invalid number of spi targets\n"); - ret = -EINVAL; - goto put_host; + return -EINVAL; } /* Define our host */ @@ -386,7 +378,7 @@ static int sifive_spi_probe(struct platform_device *pdev) dev_name(&pdev->dev), spi); if (ret) { dev_err(&pdev->dev, "Unable to bind to interrupt\n"); - goto put_host; + return ret; } dev_info(&pdev->dev, "mapped; irq=%d, cs=%d\n", @@ -395,15 +387,10 @@ static int sifive_spi_probe(struct platform_device *pdev) ret = spi_register_controller(host); if (ret < 0) { dev_err(&pdev->dev, "spi_register_host failed\n"); - goto put_host; + return ret; } return 0; - -put_host: - spi_controller_put(host); - - return ret; } static void sifive_spi_remove(struct platform_device *pdev) @@ -411,14 +398,10 @@ static void sifive_spi_remove(struct platform_device *pdev) struct spi_controller *host = platform_get_drvdata(pdev); struct sifive_spi *spi = spi_controller_get_devdata(host); - spi_controller_get(host); - spi_unregister_controller(host); /* Disable all the interrupts just in case */ sifive_spi_write(spi, SIFIVE_SPI_REG_IE, 0); - - spi_controller_put(host); } static int sifive_spi_suspend(struct device *dev) From cd1cd2ff56bf106a71d3170d6b74d08386d99428 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 5 May 2026 09:28:58 +0200 Subject: [PATCH 056/118] spi: slave-mt27xx: switch to managed controller allocation Switch to device managed controller allocation to simplify error handling and to avoid having to take another reference during deregistration. Signed-off-by: Johan Hovold Link: https://patch.msgid.link/20260505072909.618363-10-johan@kernel.org Signed-off-by: Mark Brown --- drivers/spi/spi-slave-mt27xx.c | 33 ++++++++++----------------------- 1 file changed, 10 insertions(+), 23 deletions(-) diff --git a/drivers/spi/spi-slave-mt27xx.c b/drivers/spi/spi-slave-mt27xx.c index 7aedeaa5889d..e60ab4c18bed 100644 --- a/drivers/spi/spi-slave-mt27xx.c +++ b/drivers/spi/spi-slave-mt27xx.c @@ -388,11 +388,9 @@ static int mtk_spi_slave_probe(struct platform_device *pdev) int irq, ret; const struct of_device_id *of_id; - ctlr = spi_alloc_target(&pdev->dev, sizeof(*mdata)); - if (!ctlr) { - dev_err(&pdev->dev, "failed to alloc spi target\n"); + ctlr = devm_spi_alloc_target(&pdev->dev, sizeof(*mdata)); + if (!ctlr) return -ENOMEM; - } ctlr->auto_runtime_pm = true; ctlr->mode_bits = SPI_CPOL | SPI_CPHA; @@ -406,8 +404,7 @@ static int mtk_spi_slave_probe(struct platform_device *pdev) of_id = of_match_node(mtk_spi_slave_of_match, pdev->dev.of_node); if (!of_id) { dev_err(&pdev->dev, "failed to probe of_node\n"); - ret = -EINVAL; - goto err_put_ctlr; + return -EINVAL; } mdata = spi_controller_get_devdata(ctlr); mdata->dev_comp = of_id->data; @@ -420,35 +417,31 @@ static int mtk_spi_slave_probe(struct platform_device *pdev) init_completion(&mdata->xfer_done); mdata->dev = &pdev->dev; mdata->base = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(mdata->base)) { - ret = PTR_ERR(mdata->base); - goto err_put_ctlr; - } + if (IS_ERR(mdata->base)) + return PTR_ERR(mdata->base); irq = platform_get_irq(pdev, 0); - if (irq < 0) { - ret = irq; - goto err_put_ctlr; - } + if (irq < 0) + return irq; ret = devm_request_irq(&pdev->dev, irq, mtk_spi_slave_interrupt, IRQF_TRIGGER_NONE, dev_name(&pdev->dev), ctlr); if (ret) { dev_err(&pdev->dev, "failed to register irq (%d)\n", ret); - goto err_put_ctlr; + return ret; } mdata->spi_clk = devm_clk_get(&pdev->dev, "spi"); if (IS_ERR(mdata->spi_clk)) { ret = PTR_ERR(mdata->spi_clk); dev_err(&pdev->dev, "failed to get spi-clk: %d\n", ret); - goto err_put_ctlr; + return ret; } ret = clk_prepare_enable(mdata->spi_clk); if (ret < 0) { dev_err(&pdev->dev, "failed to enable spi_clk (%d)\n", ret); - goto err_put_ctlr; + return ret; } pm_runtime_enable(&pdev->dev); @@ -465,8 +458,6 @@ static int mtk_spi_slave_probe(struct platform_device *pdev) err_disable_runtime_pm: pm_runtime_disable(&pdev->dev); -err_put_ctlr: - spi_controller_put(ctlr); return ret; } @@ -475,13 +466,9 @@ static void mtk_spi_slave_remove(struct platform_device *pdev) { struct spi_controller *ctlr = platform_get_drvdata(pdev); - spi_controller_get(ctlr); - spi_unregister_controller(ctlr); pm_runtime_disable(&pdev->dev); - - spi_controller_put(ctlr); } #ifdef CONFIG_PM_SLEEP From d68627cc76cd895d07363c795a198d1edd687107 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 5 May 2026 09:28:59 +0200 Subject: [PATCH 057/118] spi: sprd: switch to managed controller allocation Switch to device managed controller allocation to simplify error handling and to avoid having to take another reference during deregistration. Signed-off-by: Johan Hovold Link: https://patch.msgid.link/20260505072909.618363-11-johan@kernel.org Signed-off-by: Mark Brown --- drivers/spi/spi-sprd.c | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/drivers/spi/spi-sprd.c b/drivers/spi/spi-sprd.c index fd3fd0ce122c..d27b51698dbd 100644 --- a/drivers/spi/spi-sprd.c +++ b/drivers/spi/spi-sprd.c @@ -923,16 +923,14 @@ static int sprd_spi_probe(struct platform_device *pdev) int ret; pdev->id = of_alias_get_id(pdev->dev.of_node, "spi"); - sctlr = spi_alloc_host(&pdev->dev, sizeof(*ss)); + sctlr = devm_spi_alloc_host(&pdev->dev, sizeof(*ss)); if (!sctlr) return -ENOMEM; ss = spi_controller_get_devdata(sctlr); ss->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); - if (IS_ERR(ss->base)) { - ret = PTR_ERR(ss->base); - goto free_controller; - } + if (IS_ERR(ss->base)) + return PTR_ERR(ss->base); ss->phy_base = res->start; ss->dev = &pdev->dev; @@ -949,15 +947,15 @@ static int sprd_spi_probe(struct platform_device *pdev) platform_set_drvdata(pdev, sctlr); ret = sprd_spi_clk_init(pdev, ss); if (ret) - goto free_controller; + return ret; ret = sprd_spi_irq_init(pdev, ss); if (ret) - goto free_controller; + return ret; ret = sprd_spi_dma_init(pdev, ss); if (ret) - goto free_controller; + return ret; ret = clk_prepare_enable(ss->clk); if (ret) @@ -992,8 +990,6 @@ static int sprd_spi_probe(struct platform_device *pdev) clk_disable_unprepare(ss->clk); release_dma: sprd_spi_dma_release(ss); -free_controller: - spi_controller_put(sctlr); return ret; } @@ -1008,8 +1004,6 @@ static void sprd_spi_remove(struct platform_device *pdev) if (ret < 0) dev_err(ss->dev, "failed to resume SPI controller\n"); - spi_controller_get(sctlr); - spi_unregister_controller(sctlr); if (ret >= 0) { @@ -1019,8 +1013,6 @@ static void sprd_spi_remove(struct platform_device *pdev) } pm_runtime_put_noidle(&pdev->dev); pm_runtime_disable(&pdev->dev); - - spi_controller_put(sctlr); } static int __maybe_unused sprd_spi_runtime_suspend(struct device *dev) From d3cf5ebdf1c9fa909ae8de5be041eac2658c0c68 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 5 May 2026 09:29:00 +0200 Subject: [PATCH 058/118] spi: st-ssc4: switch to managed controller allocation Switch to device managed controller allocation to simplify error handling and to avoid having to take another reference during deregistration. Signed-off-by: Johan Hovold Link: https://patch.msgid.link/20260505072909.618363-12-johan@kernel.org Signed-off-by: Mark Brown --- drivers/spi/spi-st-ssc4.c | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/drivers/spi/spi-st-ssc4.c b/drivers/spi/spi-st-ssc4.c index 9c8099fe6e19..df61094fc444 100644 --- a/drivers/spi/spi-st-ssc4.c +++ b/drivers/spi/spi-st-ssc4.c @@ -279,7 +279,7 @@ static int spi_st_probe(struct platform_device *pdev) int irq, ret = 0; u32 var; - host = spi_alloc_host(&pdev->dev, sizeof(*spi_st)); + host = devm_spi_alloc_host(&pdev->dev, sizeof(*spi_st)); if (!host) return -ENOMEM; @@ -296,13 +296,12 @@ static int spi_st_probe(struct platform_device *pdev) spi_st->clk = devm_clk_get(&pdev->dev, "ssc"); if (IS_ERR(spi_st->clk)) { dev_err(&pdev->dev, "Unable to request clock\n"); - ret = PTR_ERR(spi_st->clk); - goto put_host; + return PTR_ERR(spi_st->clk); } ret = clk_prepare_enable(spi_st->clk); if (ret) - goto put_host; + return ret; init_completion(&spi_st->done); @@ -361,8 +360,7 @@ static int spi_st_probe(struct platform_device *pdev) pm_runtime_disable(&pdev->dev); clk_disable: clk_disable_unprepare(spi_st->clk); -put_host: - spi_controller_put(host); + return ret; } @@ -371,16 +369,12 @@ static void spi_st_remove(struct platform_device *pdev) struct spi_controller *host = platform_get_drvdata(pdev); struct spi_st *spi_st = spi_controller_get_devdata(host); - spi_controller_get(host); - spi_unregister_controller(host); pm_runtime_disable(&pdev->dev); clk_disable_unprepare(spi_st->clk); - spi_controller_put(host); - pinctrl_pm_select_sleep_state(&pdev->dev); } From 02b36d644ded410e8f70cdd78d0c5d523252bafd Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 5 May 2026 09:29:01 +0200 Subject: [PATCH 059/118] spi: sun4i: switch to managed controller allocation Switch to device managed controller allocation to simplify error handling and to avoid having to take another reference during deregistration. Signed-off-by: Johan Hovold Link: https://patch.msgid.link/20260505072909.618363-13-johan@kernel.org Signed-off-by: Mark Brown --- drivers/spi/spi-sun4i.c | 35 +++++++++++------------------------ 1 file changed, 11 insertions(+), 24 deletions(-) diff --git a/drivers/spi/spi-sun4i.c b/drivers/spi/spi-sun4i.c index b7fbb5270edb..d5c16392cd4d 100644 --- a/drivers/spi/spi-sun4i.c +++ b/drivers/spi/spi-sun4i.c @@ -434,32 +434,26 @@ static int sun4i_spi_probe(struct platform_device *pdev) struct sun4i_spi *sspi; int ret = 0, irq; - host = spi_alloc_host(&pdev->dev, sizeof(struct sun4i_spi)); - if (!host) { - dev_err(&pdev->dev, "Unable to allocate SPI Host\n"); + host = devm_spi_alloc_host(&pdev->dev, sizeof(struct sun4i_spi)); + if (!host) return -ENOMEM; - } platform_set_drvdata(pdev, host); sspi = spi_controller_get_devdata(host); sspi->base_addr = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(sspi->base_addr)) { - ret = PTR_ERR(sspi->base_addr); - goto err_free_host; - } + if (IS_ERR(sspi->base_addr)) + return PTR_ERR(sspi->base_addr); irq = platform_get_irq(pdev, 0); - if (irq < 0) { - ret = -ENXIO; - goto err_free_host; - } + if (irq < 0) + return -ENXIO; ret = devm_request_irq(&pdev->dev, irq, sun4i_spi_handler, 0, "sun4i-spi", sspi); if (ret) { dev_err(&pdev->dev, "Cannot request IRQ\n"); - goto err_free_host; + return ret; } sspi->host = host; @@ -477,15 +471,13 @@ static int sun4i_spi_probe(struct platform_device *pdev) sspi->hclk = devm_clk_get(&pdev->dev, "ahb"); if (IS_ERR(sspi->hclk)) { dev_err(&pdev->dev, "Unable to acquire AHB clock\n"); - ret = PTR_ERR(sspi->hclk); - goto err_free_host; + return PTR_ERR(sspi->hclk); } sspi->mclk = devm_clk_get(&pdev->dev, "mod"); if (IS_ERR(sspi->mclk)) { dev_err(&pdev->dev, "Unable to acquire module clock\n"); - ret = PTR_ERR(sspi->mclk); - goto err_free_host; + return PTR_ERR(sspi->mclk); } init_completion(&sspi->done); @@ -497,7 +489,7 @@ static int sun4i_spi_probe(struct platform_device *pdev) ret = sun4i_spi_runtime_resume(&pdev->dev); if (ret) { dev_err(&pdev->dev, "Couldn't resume the device\n"); - goto err_free_host; + return ret; } pm_runtime_set_active(&pdev->dev); @@ -515,8 +507,7 @@ static int sun4i_spi_probe(struct platform_device *pdev) err_pm_disable: pm_runtime_disable(&pdev->dev); sun4i_spi_runtime_suspend(&pdev->dev); -err_free_host: - spi_controller_put(host); + return ret; } @@ -524,13 +515,9 @@ static void sun4i_spi_remove(struct platform_device *pdev) { struct spi_controller *host = platform_get_drvdata(pdev); - spi_controller_get(host); - spi_unregister_controller(host); pm_runtime_force_suspend(&pdev->dev); - - spi_controller_put(host); } static const struct of_device_id sun4i_spi_match[] = { From 9864636b1cd95dd2b3c702a2ff22e9d169f6523a Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 5 May 2026 09:29:02 +0200 Subject: [PATCH 060/118] spi: sun6i: switch to managed controller allocation Switch to device managed controller allocation to simplify error handling and to avoid having to take another reference during deregistration. Signed-off-by: Johan Hovold Link: https://patch.msgid.link/20260505072909.618363-14-johan@kernel.org Signed-off-by: Mark Brown --- drivers/spi/spi-sun6i.c | 38 ++++++++++++-------------------------- 1 file changed, 12 insertions(+), 26 deletions(-) diff --git a/drivers/spi/spi-sun6i.c b/drivers/spi/spi-sun6i.c index 5ac73d324d06..4631e9c7ca1d 100644 --- a/drivers/spi/spi-sun6i.c +++ b/drivers/spi/spi-sun6i.c @@ -633,7 +633,7 @@ static int sun6i_spi_probe(struct platform_device *pdev) struct resource *mem; int ret = 0, irq; - host = spi_alloc_host(&pdev->dev, sizeof(struct sun6i_spi)); + host = devm_spi_alloc_host(&pdev->dev, sizeof(struct sun6i_spi)); if (!host) { dev_err(&pdev->dev, "Unable to allocate SPI Host\n"); return -ENOMEM; @@ -643,22 +643,18 @@ static int sun6i_spi_probe(struct platform_device *pdev) sspi = spi_controller_get_devdata(host); sspi->base_addr = devm_platform_get_and_ioremap_resource(pdev, 0, &mem); - if (IS_ERR(sspi->base_addr)) { - ret = PTR_ERR(sspi->base_addr); - goto err_free_host; - } + if (IS_ERR(sspi->base_addr)) + return PTR_ERR(sspi->base_addr); irq = platform_get_irq(pdev, 0); - if (irq < 0) { - ret = -ENXIO; - goto err_free_host; - } + if (irq < 0) + return -ENXIO; ret = devm_request_irq(&pdev->dev, irq, sun6i_spi_handler, 0, "sun6i-spi", sspi); if (ret) { dev_err(&pdev->dev, "Cannot request IRQ\n"); - goto err_free_host; + return ret; } sspi->host = host; @@ -679,15 +675,13 @@ static int sun6i_spi_probe(struct platform_device *pdev) sspi->hclk = devm_clk_get(&pdev->dev, "ahb"); if (IS_ERR(sspi->hclk)) { dev_err(&pdev->dev, "Unable to acquire AHB clock\n"); - ret = PTR_ERR(sspi->hclk); - goto err_free_host; + return PTR_ERR(sspi->hclk); } sspi->mclk = devm_clk_get(&pdev->dev, "mod"); if (IS_ERR(sspi->mclk)) { dev_err(&pdev->dev, "Unable to acquire module clock\n"); - ret = PTR_ERR(sspi->mclk); - goto err_free_host; + return PTR_ERR(sspi->mclk); } init_completion(&sspi->done); @@ -696,17 +690,14 @@ static int sun6i_spi_probe(struct platform_device *pdev) sspi->rstc = devm_reset_control_get_exclusive(&pdev->dev, NULL); if (IS_ERR(sspi->rstc)) { dev_err(&pdev->dev, "Couldn't get reset controller\n"); - ret = PTR_ERR(sspi->rstc); - goto err_free_host; + return PTR_ERR(sspi->rstc); } host->dma_tx = dma_request_chan(&pdev->dev, "tx"); if (IS_ERR(host->dma_tx)) { /* Check tx to see if we need defer probing driver */ - if (PTR_ERR(host->dma_tx) == -EPROBE_DEFER) { - ret = -EPROBE_DEFER; - goto err_free_host; - } + if (PTR_ERR(host->dma_tx) == -EPROBE_DEFER) + return -EPROBE_DEFER; dev_warn(&pdev->dev, "Failed to request TX DMA channel\n"); host->dma_tx = NULL; } @@ -759,8 +750,7 @@ static int sun6i_spi_probe(struct platform_device *pdev) err_free_dma_tx: if (host->dma_tx) dma_release_channel(host->dma_tx); -err_free_host: - spi_controller_put(host); + return ret; } @@ -768,8 +758,6 @@ static void sun6i_spi_remove(struct platform_device *pdev) { struct spi_controller *host = platform_get_drvdata(pdev); - spi_controller_get(host); - spi_unregister_controller(host); pm_runtime_force_suspend(&pdev->dev); @@ -778,8 +766,6 @@ static void sun6i_spi_remove(struct platform_device *pdev) dma_release_channel(host->dma_tx); if (host->dma_rx) dma_release_channel(host->dma_rx); - - spi_controller_put(host); } static const struct sun6i_spi_cfg sun6i_a31_spi_cfg = { From 5d5bbf177d18bdec01b9356f85d5883cbc6acf29 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 5 May 2026 09:29:03 +0200 Subject: [PATCH 061/118] spi: syncuacer: switch to managed controller allocation Switch to device managed controller allocation to simplify error handling and to avoid having to take another reference during deregistration. Signed-off-by: Johan Hovold Link: https://patch.msgid.link/20260505072909.618363-15-johan@kernel.org Signed-off-by: Mark Brown --- drivers/spi/spi-synquacer.c | 24 +++++++----------------- 1 file changed, 7 insertions(+), 17 deletions(-) diff --git a/drivers/spi/spi-synquacer.c b/drivers/spi/spi-synquacer.c index 290c439897c4..c14225e39fd1 100644 --- a/drivers/spi/spi-synquacer.c +++ b/drivers/spi/spi-synquacer.c @@ -605,7 +605,7 @@ static int synquacer_spi_probe(struct platform_device *pdev) int ret; int rx_irq, tx_irq; - host = spi_alloc_host(&pdev->dev, sizeof(*sspi)); + host = devm_spi_alloc_host(&pdev->dev, sizeof(*sspi)); if (!host) return -ENOMEM; @@ -617,10 +617,8 @@ static int synquacer_spi_probe(struct platform_device *pdev) init_completion(&sspi->transfer_done); sspi->regs = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(sspi->regs)) { - ret = PTR_ERR(sspi->regs); - goto put_spi; - } + if (IS_ERR(sspi->regs)) + return PTR_ERR(sspi->regs); sspi->clk_src_type = SYNQUACER_HSSPI_CLOCK_SRC_IHCLK; /* Default */ device_property_read_u32(&pdev->dev, "socionext,ihclk-rate", @@ -637,21 +635,19 @@ static int synquacer_spi_probe(struct platform_device *pdev) sspi->clk = devm_clk_get(sspi->dev, "iPCLK"); } else { dev_err(&pdev->dev, "specified wrong clock source\n"); - ret = -EINVAL; - goto put_spi; + return -EINVAL; } if (IS_ERR(sspi->clk)) { - ret = dev_err_probe(&pdev->dev, PTR_ERR(sspi->clk), - "clock not found\n"); - goto put_spi; + return dev_err_probe(&pdev->dev, PTR_ERR(sspi->clk), + "clock not found\n"); } ret = clk_prepare_enable(sspi->clk); if (ret) { dev_err(&pdev->dev, "failed to enable clock (%d)\n", ret); - goto put_spi; + return ret; } host->max_speed_hz = clk_get_rate(sspi->clk); @@ -726,8 +722,6 @@ static int synquacer_spi_probe(struct platform_device *pdev) pm_runtime_disable(sspi->dev); disable_clk: clk_disable_unprepare(sspi->clk); -put_spi: - spi_controller_put(host); return ret; } @@ -737,15 +731,11 @@ static void synquacer_spi_remove(struct platform_device *pdev) struct spi_controller *host = platform_get_drvdata(pdev); struct synquacer_spi *sspi = spi_controller_get_devdata(host); - spi_controller_get(host); - spi_unregister_controller(host); pm_runtime_disable(sspi->dev); clk_disable_unprepare(sspi->clk); - - spi_controller_put(host); } static int __maybe_unused synquacer_spi_suspend(struct device *dev) From 3068e7063cc42032b30e3eaef34066a86cb0aa68 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 5 May 2026 09:29:04 +0200 Subject: [PATCH 062/118] spi: tegra114: switch to managed controller allocation Switch to device managed controller allocation to simplify error handling and to avoid having to take another reference during deregistration. Signed-off-by: Johan Hovold Link: https://patch.msgid.link/20260505072909.618363-16-johan@kernel.org Signed-off-by: Mark Brown --- drivers/spi/spi-tegra114.c | 34 ++++++++++++---------------------- 1 file changed, 12 insertions(+), 22 deletions(-) diff --git a/drivers/spi/spi-tegra114.c b/drivers/spi/spi-tegra114.c index b8b0ebe0fe93..aa44ffd09e61 100644 --- a/drivers/spi/spi-tegra114.c +++ b/drivers/spi/spi-tegra114.c @@ -1302,7 +1302,7 @@ static int tegra_spi_probe(struct platform_device *pdev) int ret, spi_irq; int bus_num; - host = spi_alloc_host(&pdev->dev, sizeof(*tspi)); + host = devm_spi_alloc_host(&pdev->dev, sizeof(*tspi)); if (!host) { dev_err(&pdev->dev, "host allocation failed\n"); return -ENOMEM; @@ -1336,36 +1336,31 @@ static int tegra_spi_probe(struct platform_device *pdev) tspi->soc_data = of_device_get_match_data(&pdev->dev); if (!tspi->soc_data) { dev_err(&pdev->dev, "unsupported tegra\n"); - ret = -ENODEV; - goto exit_free_host; + return -ENODEV; } tspi->base = devm_platform_get_and_ioremap_resource(pdev, 0, &r); - if (IS_ERR(tspi->base)) { - ret = PTR_ERR(tspi->base); - goto exit_free_host; - } + if (IS_ERR(tspi->base)) + return PTR_ERR(tspi->base); + tspi->phys = r->start; spi_irq = platform_get_irq(pdev, 0); - if (spi_irq < 0) { - ret = spi_irq; - goto exit_free_host; - } + if (spi_irq < 0) + return spi_irq; + tspi->irq = spi_irq; tspi->clk = devm_clk_get(&pdev->dev, "spi"); if (IS_ERR(tspi->clk)) { dev_err(&pdev->dev, "can not get clock\n"); - ret = PTR_ERR(tspi->clk); - goto exit_free_host; + return PTR_ERR(tspi->clk); } tspi->rst = devm_reset_control_get_exclusive(&pdev->dev, "spi"); if (IS_ERR(tspi->rst)) { dev_err(&pdev->dev, "can not get reset\n"); - ret = PTR_ERR(tspi->rst); - goto exit_free_host; + return PTR_ERR(tspi->rst); } tspi->max_buf_size = SPI_FIFO_DEPTH << 2; @@ -1373,7 +1368,7 @@ static int tegra_spi_probe(struct platform_device *pdev) ret = tegra_spi_init_dma_param(tspi, true); if (ret < 0) - goto exit_free_host; + return ret; ret = tegra_spi_init_dma_param(tspi, false); if (ret < 0) goto exit_rx_dma_free; @@ -1431,8 +1426,7 @@ static int tegra_spi_probe(struct platform_device *pdev) tegra_spi_deinit_dma_param(tspi, false); exit_rx_dma_free: tegra_spi_deinit_dma_param(tspi, true); -exit_free_host: - spi_controller_put(host); + return ret; } @@ -1441,8 +1435,6 @@ static void tegra_spi_remove(struct platform_device *pdev) struct spi_controller *host = platform_get_drvdata(pdev); struct tegra_spi_data *tspi = spi_controller_get_devdata(host); - spi_controller_get(host); - spi_unregister_controller(host); free_irq(tspi->irq, tspi); @@ -1456,8 +1448,6 @@ static void tegra_spi_remove(struct platform_device *pdev) pm_runtime_disable(&pdev->dev); if (!pm_runtime_status_suspended(&pdev->dev)) tegra_spi_runtime_suspend(&pdev->dev); - - spi_controller_put(host); } #ifdef CONFIG_PM_SLEEP From 3a14bf4f5453121cae3cbdfb6180317c5d74f424 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 5 May 2026 09:29:05 +0200 Subject: [PATCH 063/118] spi: tegra20-sflash: switch to managed controller allocation Switch to device managed controller allocation to simplify error handling and to avoid having to take another reference during deregistration. Signed-off-by: Johan Hovold Link: https://patch.msgid.link/20260505072909.618363-17-johan@kernel.org Signed-off-by: Mark Brown --- drivers/spi/spi-tegra20-sflash.c | 24 ++++++++---------------- 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/drivers/spi/spi-tegra20-sflash.c b/drivers/spi/spi-tegra20-sflash.c index 9256729f2d49..2caa33f0a52c 100644 --- a/drivers/spi/spi-tegra20-sflash.c +++ b/drivers/spi/spi-tegra20-sflash.c @@ -427,11 +427,9 @@ static int tegra_sflash_probe(struct platform_device *pdev) return -ENODEV; } - host = spi_alloc_host(&pdev->dev, sizeof(*tsd)); - if (!host) { - dev_err(&pdev->dev, "host allocation failed\n"); + host = devm_spi_alloc_host(&pdev->dev, sizeof(*tsd)); + if (!host) return -ENOMEM; - } /* the spi->mode bits understood by this driver: */ host->mode_bits = SPI_CPOL | SPI_CPHA; @@ -450,14 +448,13 @@ static int tegra_sflash_probe(struct platform_device *pdev) host->max_speed_hz = 25000000; /* 25MHz */ tsd->base = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(tsd->base)) { - ret = PTR_ERR(tsd->base); - goto exit_free_host; - } + if (IS_ERR(tsd->base)) + return PTR_ERR(tsd->base); ret = platform_get_irq(pdev, 0); if (ret < 0) - goto exit_free_host; + return ret; + tsd->irq = ret; ret = request_irq(tsd->irq, tegra_sflash_isr, 0, @@ -465,7 +462,7 @@ static int tegra_sflash_probe(struct platform_device *pdev) if (ret < 0) { dev_err(&pdev->dev, "Failed to register ISR for IRQ %d\n", tsd->irq); - goto exit_free_host; + return ret; } tsd->clk = devm_clk_get(&pdev->dev, NULL); @@ -518,8 +515,7 @@ static int tegra_sflash_probe(struct platform_device *pdev) tegra_sflash_runtime_suspend(&pdev->dev); exit_free_irq: free_irq(tsd->irq, tsd); -exit_free_host: - spi_controller_put(host); + return ret; } @@ -528,8 +524,6 @@ static void tegra_sflash_remove(struct platform_device *pdev) struct spi_controller *host = platform_get_drvdata(pdev); struct tegra_sflash_data *tsd = spi_controller_get_devdata(host); - spi_controller_get(host); - spi_unregister_controller(host); free_irq(tsd->irq, tsd); @@ -537,8 +531,6 @@ static void tegra_sflash_remove(struct platform_device *pdev) pm_runtime_disable(&pdev->dev); if (!pm_runtime_status_suspended(&pdev->dev)) tegra_sflash_runtime_suspend(&pdev->dev); - - spi_controller_put(host); } #ifdef CONFIG_PM_SLEEP From 76a24627b98ca712df8724985a4601c502b7875f Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 5 May 2026 09:29:06 +0200 Subject: [PATCH 064/118] spi: ti-qspi: switch to managed controller allocation Switch to device managed controller allocation to simplify error handling and to avoid having to take another reference during deregistration. Signed-off-by: Johan Hovold Link: https://patch.msgid.link/20260505072909.618363-18-johan@kernel.org Signed-off-by: Mark Brown --- drivers/spi/spi-ti-qspi.c | 30 +++++++++--------------------- 1 file changed, 9 insertions(+), 21 deletions(-) diff --git a/drivers/spi/spi-ti-qspi.c b/drivers/spi/spi-ti-qspi.c index 1fbd710d616f..2a8548810f84 100644 --- a/drivers/spi/spi-ti-qspi.c +++ b/drivers/spi/spi-ti-qspi.c @@ -765,7 +765,7 @@ static int ti_qspi_probe(struct platform_device *pdev) int ret = 0, num_cs, irq; dma_cap_mask_t mask; - host = spi_alloc_host(&pdev->dev, sizeof(*qspi)); + host = devm_spi_alloc_host(&pdev->dev, sizeof(*qspi)); if (!host) return -ENOMEM; @@ -793,8 +793,7 @@ static int ti_qspi_probe(struct platform_device *pdev) r = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (r == NULL) { dev_err(&pdev->dev, "missing platform data\n"); - ret = -ENODEV; - goto free_host; + return -ENODEV; } } @@ -812,28 +811,22 @@ static int ti_qspi_probe(struct platform_device *pdev) qspi->mmap_size = resource_size(res_mmap); irq = platform_get_irq(pdev, 0); - if (irq < 0) { - ret = irq; - goto free_host; - } + if (irq < 0) + return irq; mutex_init(&qspi->list_lock); qspi->base = devm_ioremap_resource(&pdev->dev, r); - if (IS_ERR(qspi->base)) { - ret = PTR_ERR(qspi->base); - goto free_host; - } + if (IS_ERR(qspi->base)) + return PTR_ERR(qspi->base); if (of_property_present(np, "syscon-chipselects")) { qspi->ctrl_base = syscon_regmap_lookup_by_phandle_args(np, "syscon-chipselects", 1, &qspi->ctrl_reg); - if (IS_ERR(qspi->ctrl_base)) { - ret = PTR_ERR(qspi->ctrl_base); - goto free_host; - } + if (IS_ERR(qspi->ctrl_base)) + return PTR_ERR(qspi->ctrl_base); } qspi->fclk = devm_clk_get(&pdev->dev, "fck"); @@ -895,8 +888,7 @@ static int ti_qspi_probe(struct platform_device *pdev) ti_qspi_dma_cleanup(qspi); pm_runtime_disable(&pdev->dev); -free_host: - spi_controller_put(host); + return ret; } @@ -904,16 +896,12 @@ static void ti_qspi_remove(struct platform_device *pdev) { struct ti_qspi *qspi = platform_get_drvdata(pdev); - spi_controller_get(qspi->host); - spi_unregister_controller(qspi->host); pm_runtime_put_sync(&pdev->dev); pm_runtime_disable(&pdev->dev); ti_qspi_dma_cleanup(qspi); - - spi_controller_put(qspi->host); } static const struct dev_pm_ops ti_qspi_pm_ops = { From f8689d5a9ee44a7cdd0ff469e5ee943c933fc830 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 5 May 2026 09:29:07 +0200 Subject: [PATCH 065/118] spi: ti-qspi: cleanup registration error path Add a proper error path for when registration fails so that the probe tests for errors consistently. Signed-off-by: Johan Hovold Link: https://patch.msgid.link/20260505072909.618363-19-johan@kernel.org Signed-off-by: Mark Brown --- drivers/spi/spi-ti-qspi.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/spi/spi-ti-qspi.c b/drivers/spi/spi-ti-qspi.c index 2a8548810f84..6b407c7b5d33 100644 --- a/drivers/spi/spi-ti-qspi.c +++ b/drivers/spi/spi-ti-qspi.c @@ -882,9 +882,12 @@ static int ti_qspi_probe(struct platform_device *pdev) qspi->current_cs = -1; ret = spi_register_controller(host); - if (!ret) - return 0; + if (ret) + goto err_free_dma; + return 0; + +err_free_dma: ti_qspi_dma_cleanup(qspi); pm_runtime_disable(&pdev->dev); From 789986b1456497837c542f64b1a0c7d9dafd1b2a Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 5 May 2026 09:29:08 +0200 Subject: [PATCH 066/118] spi: uniphier: switch to managed controller allocation Switch to device managed controller allocation to simplify error handling and to avoid having to take another reference during deregistration. Signed-off-by: Johan Hovold Link: https://patch.msgid.link/20260505072909.618363-20-johan@kernel.org Signed-off-by: Mark Brown --- drivers/spi/spi-uniphier.c | 33 +++++++++++---------------------- 1 file changed, 11 insertions(+), 22 deletions(-) diff --git a/drivers/spi/spi-uniphier.c b/drivers/spi/spi-uniphier.c index eac6c3e8908b..5f0abc59b716 100644 --- a/drivers/spi/spi-uniphier.c +++ b/drivers/spi/spi-uniphier.c @@ -649,7 +649,7 @@ static int uniphier_spi_probe(struct platform_device *pdev) int irq; int ret; - host = spi_alloc_host(&pdev->dev, sizeof(*priv)); + host = devm_spi_alloc_host(&pdev->dev, sizeof(*priv)); if (!host) return -ENOMEM; @@ -660,30 +660,26 @@ static int uniphier_spi_probe(struct platform_device *pdev) priv->is_save_param = false; priv->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); - if (IS_ERR(priv->base)) { - ret = PTR_ERR(priv->base); - goto out_host_put; - } + if (IS_ERR(priv->base)) + return PTR_ERR(priv->base); + priv->base_dma_addr = res->start; priv->clk = devm_clk_get_enabled(&pdev->dev, NULL); if (IS_ERR(priv->clk)) { dev_err(&pdev->dev, "failed to get clock\n"); - ret = PTR_ERR(priv->clk); - goto out_host_put; + return PTR_ERR(priv->clk); } irq = platform_get_irq(pdev, 0); - if (irq < 0) { - ret = irq; - goto out_host_put; - } + if (irq < 0) + return irq; ret = devm_request_irq(&pdev->dev, irq, uniphier_spi_handler, 0, "uniphier-spi", priv); if (ret) { dev_err(&pdev->dev, "failed to request IRQ\n"); - goto out_host_put; + return ret; } init_completion(&priv->xfer_done); @@ -710,10 +706,9 @@ static int uniphier_spi_probe(struct platform_device *pdev) host->dma_tx = dma_request_chan(&pdev->dev, "tx"); if (IS_ERR_OR_NULL(host->dma_tx)) { - if (PTR_ERR(host->dma_tx) == -EPROBE_DEFER) { - ret = -EPROBE_DEFER; - goto out_host_put; - } + if (PTR_ERR(host->dma_tx) == -EPROBE_DEFER) + return -EPROBE_DEFER; + host->dma_tx = NULL; dma_tx_burst = INT_MAX; } else { @@ -762,8 +757,6 @@ static int uniphier_spi_probe(struct platform_device *pdev) host->dma_tx = NULL; } -out_host_put: - spi_controller_put(host); return ret; } @@ -771,16 +764,12 @@ static void uniphier_spi_remove(struct platform_device *pdev) { struct spi_controller *host = platform_get_drvdata(pdev); - spi_controller_get(host); - spi_unregister_controller(host); if (host->dma_tx) dma_release_channel(host->dma_tx); if (host->dma_rx) dma_release_channel(host->dma_rx); - - spi_controller_put(host); } static const struct of_device_id uniphier_spi_match[] = { From be552efa43eec9972b490fce9a3dd9cb0da92a3c Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 5 May 2026 09:29:09 +0200 Subject: [PATCH 067/118] spi: zync-qspi: switch to managed controller allocation Switch to device managed controller allocation to simplify error handling and to avoid having to take another reference during deregistration. Signed-off-by: Johan Hovold Link: https://patch.msgid.link/20260505072909.618363-21-johan@kernel.org Signed-off-by: Mark Brown --- drivers/spi/spi-zynq-qspi.c | 40 +++++++++++-------------------------- 1 file changed, 12 insertions(+), 28 deletions(-) diff --git a/drivers/spi/spi-zynq-qspi.c b/drivers/spi/spi-zynq-qspi.c index 406fd9d5337e..d762aaf452af 100644 --- a/drivers/spi/spi-zynq-qspi.c +++ b/drivers/spi/spi-zynq-qspi.c @@ -637,7 +637,7 @@ static int zynq_qspi_probe(struct platform_device *pdev) struct zynq_qspi *xqspi; u32 num_cs; - ctlr = spi_alloc_host(&pdev->dev, sizeof(*xqspi)); + ctlr = devm_spi_alloc_host(&pdev->dev, sizeof(*xqspi)); if (!ctlr) return -ENOMEM; @@ -645,16 +645,13 @@ static int zynq_qspi_probe(struct platform_device *pdev) xqspi->dev = dev; platform_set_drvdata(pdev, ctlr); xqspi->regs = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(xqspi->regs)) { - ret = PTR_ERR(xqspi->regs); - goto remove_ctlr; - } + if (IS_ERR(xqspi->regs)) + return PTR_ERR(xqspi->regs); xqspi->pclk = devm_clk_get_enabled(&pdev->dev, "pclk"); if (IS_ERR(xqspi->pclk)) { dev_err(&pdev->dev, "pclk clock not found.\n"); - ret = PTR_ERR(xqspi->pclk); - goto remove_ctlr; + return PTR_ERR(xqspi->pclk); } init_completion(&xqspi->data_completion); @@ -662,21 +659,18 @@ static int zynq_qspi_probe(struct platform_device *pdev) xqspi->refclk = devm_clk_get_enabled(&pdev->dev, "ref_clk"); if (IS_ERR(xqspi->refclk)) { dev_err(&pdev->dev, "ref_clk clock not found.\n"); - ret = PTR_ERR(xqspi->refclk); - goto remove_ctlr; + return PTR_ERR(xqspi->refclk); } xqspi->irq = platform_get_irq(pdev, 0); - if (xqspi->irq < 0) { - ret = xqspi->irq; - goto remove_ctlr; - } + if (xqspi->irq < 0) + return xqspi->irq; + ret = devm_request_irq(&pdev->dev, xqspi->irq, zynq_qspi_irq, 0, pdev->name, xqspi); if (ret != 0) { - ret = -ENXIO; dev_err(&pdev->dev, "request_irq failed\n"); - goto remove_ctlr; + return -ENXIO; } ret = of_property_read_u32(np, "num-cs", @@ -684,9 +678,8 @@ static int zynq_qspi_probe(struct platform_device *pdev) if (ret < 0) { ctlr->num_chipselect = 1; } else if (num_cs > ZYNQ_QSPI_MAX_NUM_CS) { - ret = -EINVAL; dev_err(&pdev->dev, "only 2 chip selects are available\n"); - goto remove_ctlr; + return -EINVAL; } else { ctlr->num_chipselect = num_cs; } @@ -705,15 +698,10 @@ static int zynq_qspi_probe(struct platform_device *pdev) ret = spi_register_controller(ctlr); if (ret) { dev_err(&pdev->dev, "failed to register controller\n"); - goto remove_ctlr; + return ret; } - return ret; - -remove_ctlr: - spi_controller_put(ctlr); - - return ret; + return 0; } /** @@ -731,13 +719,9 @@ static void zynq_qspi_remove(struct platform_device *pdev) struct spi_controller *ctlr = platform_get_drvdata(pdev); struct zynq_qspi *xqspi = spi_controller_get_devdata(ctlr); - spi_controller_get(ctlr); - spi_unregister_controller(ctlr); zynq_qspi_write(xqspi, ZYNQ_QSPI_ENABLE_OFFSET, 0); - - spi_controller_put(ctlr); } static const struct of_device_id zynq_qspi_of_match[] = { From 8262b1421dddab6d13d430aee34cef6a47a0b93f Mon Sep 17 00:00:00 2001 From: Hans Zhang <18255117159@163.com> Date: Thu, 30 Apr 2026 23:54:47 +0800 Subject: [PATCH 068/118] spi: amlogic-spifc-a1: Use FIELD_MODIFY() Use FIELD_MODIFY() to remove open-coded bit manipulation. No functional change intended. Signed-off-by: Hans Zhang <18255117159@163.com> Link: https://patch.msgid.link/20260430155456.36998-2-18255117159@163.com Signed-off-by: Mark Brown --- drivers/spi/spi-amlogic-spifc-a1.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/spi/spi-amlogic-spifc-a1.c b/drivers/spi/spi-amlogic-spifc-a1.c index 7ee4c92e6e09..77a2c11bec5e 100644 --- a/drivers/spi/spi-amlogic-spifc-a1.c +++ b/drivers/spi/spi-amlogic-spifc-a1.c @@ -206,10 +206,9 @@ static int amlogic_spifc_a1_read(struct amlogic_spifc_a1 *spifc, void *buf, u32 val = readl(spifc->base + SPIFC_A1_USER_CTRL3_REG); int ret; - val &= ~(SPIFC_A1_USER_DIN_MODE | SPIFC_A1_USER_DIN_BYTES); val |= SPIFC_A1_USER_DIN_ENABLE; - val |= FIELD_PREP(SPIFC_A1_USER_DIN_MODE, mode); - val |= FIELD_PREP(SPIFC_A1_USER_DIN_BYTES, size); + FIELD_MODIFY(SPIFC_A1_USER_DIN_MODE, &val, mode); + FIELD_MODIFY(SPIFC_A1_USER_DIN_BYTES, &val, size); writel(val, spifc->base + SPIFC_A1_USER_CTRL3_REG); ret = amlogic_spifc_a1_request(spifc, true); From b69bfa5933299b3cd84aae821f8890f10ea56df7 Mon Sep 17 00:00:00 2001 From: Hans Zhang <18255117159@163.com> Date: Thu, 30 Apr 2026 23:54:48 +0800 Subject: [PATCH 069/118] spi: amlogic-spisg: Use FIELD_MODIFY() Use FIELD_MODIFY() to remove open-coded bit manipulation. No functional change intended. Signed-off-by: Hans Zhang <18255117159@163.com> Link: https://patch.msgid.link/20260430155456.36998-3-18255117159@163.com Signed-off-by: Mark Brown --- drivers/spi/spi-amlogic-spisg.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/drivers/spi/spi-amlogic-spisg.c b/drivers/spi/spi-amlogic-spisg.c index 19c5eba412ef..4c9f4088cb37 100644 --- a/drivers/spi/spi-amlogic-spisg.c +++ b/drivers/spi/spi-amlogic-spisg.c @@ -601,14 +601,11 @@ static int aml_spisg_prepare_message(struct spi_controller *ctlr, spisg->bytes_per_word = spi->bits_per_word >> 3; - spisg->cfg_spi &= ~CFG_SLAVE_SELECT; - spisg->cfg_spi |= FIELD_PREP(CFG_SLAVE_SELECT, spi_get_chipselect(spi, 0)); - - spisg->cfg_bus &= ~(CFG_CPOL | CFG_CPHA | CFG_B_L_ENDIAN | CFG_HALF_DUPLEX); - spisg->cfg_bus |= FIELD_PREP(CFG_CPOL, !!(spi->mode & SPI_CPOL)) | - FIELD_PREP(CFG_CPHA, !!(spi->mode & SPI_CPHA)) | - FIELD_PREP(CFG_B_L_ENDIAN, !!(spi->mode & SPI_LSB_FIRST)) | - FIELD_PREP(CFG_HALF_DUPLEX, !!(spi->mode & SPI_3WIRE)); + FIELD_MODIFY(CFG_SLAVE_SELECT, &spisg->cfg_spi, spi_get_chipselect(spi, 0)); + FIELD_MODIFY(CFG_CPOL, &spisg->cfg_bus, !!(spi->mode & SPI_CPOL)); + FIELD_MODIFY(CFG_CPHA, &spisg->cfg_bus, !!(spi->mode & SPI_CPHA)); + FIELD_MODIFY(CFG_B_L_ENDIAN, &spisg->cfg_bus, !!(spi->mode & SPI_LSB_FIRST)); + FIELD_MODIFY(CFG_HALF_DUPLEX, &spisg->cfg_bus, !!(spi->mode & SPI_3WIRE)); return 0; } From 6fa473f4c5dcc20db9799f90da48b977730e05f1 Mon Sep 17 00:00:00 2001 From: Hans Zhang <18255117159@163.com> Date: Thu, 30 Apr 2026 23:54:49 +0800 Subject: [PATCH 070/118] spi: cadence-xspi: Use FIELD_MODIFY() Use FIELD_MODIFY() to remove open-coded bit manipulation. No functional change intended. Signed-off-by: Hans Zhang <18255117159@163.com> Link: https://patch.msgid.link/20260430155456.36998-4-18255117159@163.com Signed-off-by: Mark Brown --- drivers/spi/spi-cadence-xspi.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/spi/spi-cadence-xspi.c b/drivers/spi/spi-cadence-xspi.c index 895b4b3276a5..32fa19ebf7a9 100644 --- a/drivers/spi/spi-cadence-xspi.c +++ b/drivers/spi/spi-cadence-xspi.c @@ -453,8 +453,7 @@ static bool cdns_mrvl_xspi_setup_clock(struct cdns_xspi_dev *cdns_xspi, writel(clk_reg, cdns_xspi->auxbase + MRVL_XSPI_CLK_CTRL_AUX_REG); clk_reg = FIELD_PREP(MRVL_XSPI_CLK_DIV, i); - clk_reg &= ~MRVL_XSPI_CLK_DIV; - clk_reg |= FIELD_PREP(MRVL_XSPI_CLK_DIV, i); + FIELD_MODIFY(MRVL_XSPI_CLK_DIV, &clk_reg, i); clk_reg |= MRVL_XSPI_CLK_ENABLE; clk_reg |= MRVL_XSPI_IRQ_ENABLE; update_clk = true; From cfdab17cd2d7666ce164f64fbaadeb782dbb28d2 Mon Sep 17 00:00:00 2001 From: Hans Zhang <18255117159@163.com> Date: Thu, 30 Apr 2026 23:54:50 +0800 Subject: [PATCH 071/118] spi: meson-spicc: Use FIELD_MODIFY() Use FIELD_MODIFY() to remove open-coded bit manipulation. No functional change intended. Signed-off-by: Hans Zhang <18255117159@163.com> Link: https://patch.msgid.link/20260430155456.36998-5-18255117159@163.com Signed-off-by: Mark Brown --- drivers/spi/spi-meson-spicc.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/spi/spi-meson-spicc.c b/drivers/spi/spi-meson-spicc.c index b80f9f457b66..682dda114412 100644 --- a/drivers/spi/spi-meson-spicc.c +++ b/drivers/spi/spi-meson-spicc.c @@ -539,9 +539,8 @@ static void meson_spicc_setup_xfer(struct meson_spicc_device *spicc, conf = conf_orig = readl_relaxed(spicc->base + SPICC_CONREG); /* Setup word width */ - conf &= ~SPICC_BITLENGTH_MASK; - conf |= FIELD_PREP(SPICC_BITLENGTH_MASK, - (spicc->bytes_per_word << 3) - 1); + FIELD_MODIFY(SPICC_BITLENGTH_MASK, &conf, + (spicc->bytes_per_word << 3) - 1); /* Ignore if unchanged */ if (conf != conf_orig) From 0f2efc6d493833332dc9224e4e4c039b9824af51 Mon Sep 17 00:00:00 2001 From: Hans Zhang <18255117159@163.com> Date: Thu, 30 Apr 2026 23:54:51 +0800 Subject: [PATCH 072/118] spi: nxp-xspi: Use FIELD_MODIFY() Use FIELD_MODIFY() to remove open-coded bit manipulation. No functional change intended. Signed-off-by: Hans Zhang <18255117159@163.com> Reviewed-by: Frank Li Link: https://patch.msgid.link/20260430155456.36998-6-18255117159@163.com Signed-off-by: Mark Brown --- drivers/spi/spi-nxp-xspi.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/drivers/spi/spi-nxp-xspi.c b/drivers/spi/spi-nxp-xspi.c index 385302a6e62f..037eac24e6fd 100644 --- a/drivers/spi/spi-nxp-xspi.c +++ b/drivers/spi/spi-nxp-xspi.c @@ -493,9 +493,8 @@ static void nxp_xspi_disable_ddr(struct nxp_xspi *xspi) writel(reg, base + XSPI_MCR); reg &= ~XSPI_MCR_DDR_EN; - reg &= ~XSPI_MCR_DQS_FA_SEL_MASK; /* Use dummy pad loopback mode to sample data */ - reg |= FIELD_PREP(XSPI_MCR_DQS_FA_SEL_MASK, 0x01); + FIELD_MODIFY(XSPI_MCR_DQS_FA_SEL_MASK, ®, 0x01); writel(reg, base + XSPI_MCR); xspi->support_max_rate = 133000000; @@ -524,15 +523,13 @@ static void nxp_xspi_enable_ddr(struct nxp_xspi *xspi) writel(reg, base + XSPI_MCR); reg |= XSPI_MCR_DDR_EN; - reg &= ~XSPI_MCR_DQS_FA_SEL_MASK; /* Use external dqs to sample data */ - reg |= FIELD_PREP(XSPI_MCR_DQS_FA_SEL_MASK, 0x03); + FIELD_MODIFY(XSPI_MCR_DQS_FA_SEL_MASK, ®, 0x03); writel(reg, base + XSPI_MCR); xspi->support_max_rate = 200000000; reg = readl(base + XSPI_FLSHCR); - reg &= ~XSPI_FLSHCR_TDH_MASK; - reg |= FIELD_PREP(XSPI_FLSHCR_TDH_MASK, 0x01); + FIELD_MODIFY(XSPI_FLSHCR_TDH_MASK, ®, 0x01); writel(reg, base + XSPI_FLSHCR); reg = FIELD_PREP(XSPI_SMPR_DLLFSMPFA_MASK, 0x04); @@ -1096,8 +1093,7 @@ static int nxp_xspi_default_setup(struct nxp_xspi *xspi) /* Give read/write access right to EENV0 */ reg = readl(base + XSPI_FRAD0_WORD2); - reg &= ~XSPI_FRAD0_WORD2_MD0ACP_MASK; - reg |= FIELD_PREP(XSPI_FRAD0_WORD2_MD0ACP_MASK, 0x03); + FIELD_MODIFY(XSPI_FRAD0_WORD2_MD0ACP_MASK, ®, 0x03); writel(reg, base + XSPI_FRAD0_WORD2); /* Enable the FRAD check for EENV0 */ From 579fcc06576d760fd439cc3a1bea0507e14a266b Mon Sep 17 00:00:00 2001 From: Hans Zhang <18255117159@163.com> Date: Thu, 30 Apr 2026 23:54:52 +0800 Subject: [PATCH 073/118] spi: sn-f-ospi: Use FIELD_MODIFY() Use FIELD_MODIFY() to remove open-coded bit manipulation. No functional change intended. Signed-off-by: Hans Zhang <18255117159@163.com> Link: https://patch.msgid.link/20260430155456.36998-7-18255117159@163.com Signed-off-by: Mark Brown --- drivers/spi/spi-sn-f-ospi.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/spi/spi-sn-f-ospi.c b/drivers/spi/spi-sn-f-ospi.c index b459d51cb3a8..f0320e96fe23 100644 --- a/drivers/spi/spi-sn-f-ospi.c +++ b/drivers/spi/spi-sn-f-ospi.c @@ -222,9 +222,8 @@ static void f_ospi_config_clk(struct f_ospi *ospi, u32 device_hz) */ val = readl(ospi->base + OSPI_CLK_CTL); - val &= ~(OSPI_CLK_CTL_PHA | OSPI_CLK_CTL_DIV); - val |= FIELD_PREP(OSPI_CLK_CTL_PHA, OSPI_CLK_CTL_PHA_180) - | FIELD_PREP(OSPI_CLK_CTL_DIV, div_reg); + FIELD_MODIFY(OSPI_CLK_CTL_PHA, &val, OSPI_CLK_CTL_PHA_180); + FIELD_MODIFY(OSPI_CLK_CTL_DIV, &val, div_reg); writel(val, ospi->base + OSPI_CLK_CTL); } From 21ee6902a5767e4e8488b061654aad5bb84182c8 Mon Sep 17 00:00:00 2001 From: Hans Zhang <18255117159@163.com> Date: Thu, 30 Apr 2026 23:54:53 +0800 Subject: [PATCH 074/118] spi: stm32-ospi: Use FIELD_MODIFY() Use FIELD_MODIFY() to remove open-coded bit manipulation. No functional change intended. Signed-off-by: Hans Zhang <18255117159@163.com> Reviewed-by: Patrice Chotard Link: https://patch.msgid.link/20260430155456.36998-8-18255117159@163.com Signed-off-by: Mark Brown --- drivers/spi/spi-stm32-ospi.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/spi/spi-stm32-ospi.c b/drivers/spi/spi-stm32-ospi.c index 4461c6e24b9e..3757f6ba8fc6 100644 --- a/drivers/spi/spi-stm32-ospi.c +++ b/drivers/spi/spi-stm32-ospi.c @@ -470,10 +470,9 @@ static int stm32_ospi_send(struct spi_device *spi, const struct spi_mem_op *op) u8 cs = spi->chip_select[ffs(spi->cs_index_mask) - 1]; cr = readl_relaxed(ospi->regs_base + OSPI_CR); - cr &= ~CR_CSSEL; - cr |= FIELD_PREP(CR_CSSEL, cs); - cr &= ~CR_FMODE_MASK; - cr |= FIELD_PREP(CR_FMODE_MASK, ospi->fmode); + FIELD_MODIFY(CR_CSSEL, &cr, cs); + + FIELD_MODIFY(CR_FMODE_MASK, &cr, ospi->fmode); writel_relaxed(cr, regs_base + OSPI_CR); if (op->data.nbytes) From 3e0530c087a93a8477c9df6760629e326e97f795 Mon Sep 17 00:00:00 2001 From: Hans Zhang <18255117159@163.com> Date: Thu, 30 Apr 2026 23:54:54 +0800 Subject: [PATCH 075/118] spi: stm32-qspi: Use FIELD_MODIFY() Use FIELD_MODIFY() to remove open-coded bit manipulation. No functional change intended. Signed-off-by: Hans Zhang <18255117159@163.com> Reviewed-by: Patrice Chotard Link: https://patch.msgid.link/20260430155456.36998-9-18255117159@163.com Signed-off-by: Mark Brown --- drivers/spi/spi-stm32-qspi.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/spi/spi-stm32-qspi.c b/drivers/spi/spi-stm32-qspi.c index df1bbacec90a..ea69fe25686f 100644 --- a/drivers/spi/spi-stm32-qspi.c +++ b/drivers/spi/spi-stm32-qspi.c @@ -374,9 +374,8 @@ static int stm32_qspi_send(struct spi_device *spi, const struct spi_mem_op *op) int timeout, err = 0, err_poll_status = 0; cr = readl_relaxed(qspi->io_base + QSPI_CR); - cr &= ~CR_PRESC_MASK & ~CR_FSEL; - cr |= FIELD_PREP(CR_PRESC_MASK, flash->presc); - cr |= FIELD_PREP(CR_FSEL, flash->cs); + FIELD_MODIFY(CR_PRESC_MASK, &cr, flash->presc); + FIELD_MODIFY(CR_FSEL, &cr, flash->cs); writel_relaxed(cr, qspi->io_base + QSPI_CR); if (op->data.nbytes) From 673214ac9bcdf9326f96dfb56a5a432e77fcacd8 Mon Sep 17 00:00:00 2001 From: Hans Zhang <18255117159@163.com> Date: Thu, 30 Apr 2026 23:54:55 +0800 Subject: [PATCH 076/118] spi: sunplus-sp7021: Use FIELD_MODIFY() Use FIELD_MODIFY() to remove open-coded bit manipulation. No functional change intended. Signed-off-by: Hans Zhang <18255117159@163.com> Link: https://patch.msgid.link/20260430155456.36998-10-18255117159@163.com Signed-off-by: Mark Brown --- drivers/spi/spi-sunplus-sp7021.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/spi/spi-sunplus-sp7021.c b/drivers/spi/spi-sunplus-sp7021.c index 35601212fb78..c1870322d976 100644 --- a/drivers/spi/spi-sunplus-sp7021.c +++ b/drivers/spi/spi-sunplus-sp7021.c @@ -290,8 +290,7 @@ static void sp7021_spi_setup_clk(struct spi_controller *ctlr, struct spi_transfe div = max(2U, clk_rate / xfer->speed_hz); clk_sel = (div / 2) - 1; - pspim->xfer_conf &= ~SP7021_CLK_MASK; - pspim->xfer_conf |= FIELD_PREP(SP7021_CLK_MASK, clk_sel); + FIELD_MODIFY(SP7021_CLK_MASK, &pspim->xfer_conf, clk_sel); writel(pspim->xfer_conf, pspim->m_base + SP7021_SPI_CONFIG_REG); } From ce7984bea2a185d4a7cfbd1b8972fdff0ca615c0 Mon Sep 17 00:00:00 2001 From: Hans Zhang <18255117159@163.com> Date: Thu, 30 Apr 2026 23:54:56 +0800 Subject: [PATCH 077/118] spi: uniphier: Use FIELD_MODIFY() Use FIELD_MODIFY() to remove open-coded bit manipulation. No functional change intended. Signed-off-by: Hans Zhang <18255117159@163.com> Link: https://patch.msgid.link/20260430155456.36998-11-18255117159@163.com Signed-off-by: Mark Brown --- drivers/spi/spi-uniphier.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/drivers/spi/spi-uniphier.c b/drivers/spi/spi-uniphier.c index eac6c3e8908b..846f65ba9495 100644 --- a/drivers/spi/spi-uniphier.c +++ b/drivers/spi/spi-uniphier.c @@ -184,14 +184,12 @@ static void uniphier_spi_set_transfer_size(struct spi_device *spi, int size) u32 val; val = readl(priv->base + SSI_TXWDS); - val &= ~(SSI_TXWDS_WDLEN_MASK | SSI_TXWDS_DTLEN_MASK); - val |= FIELD_PREP(SSI_TXWDS_WDLEN_MASK, size); - val |= FIELD_PREP(SSI_TXWDS_DTLEN_MASK, size); + FIELD_MODIFY(SSI_TXWDS_WDLEN_MASK, &val, size); + FIELD_MODIFY(SSI_TXWDS_DTLEN_MASK, &val, size); writel(val, priv->base + SSI_TXWDS); val = readl(priv->base + SSI_RXWDS); - val &= ~SSI_RXWDS_DTLEN_MASK; - val |= FIELD_PREP(SSI_RXWDS_DTLEN_MASK, size); + FIELD_MODIFY(SSI_RXWDS_DTLEN_MASK, &val, size); writel(val, priv->base + SSI_RXWDS); } @@ -308,9 +306,8 @@ static void uniphier_spi_set_fifo_threshold(struct uniphier_spi_priv *priv, u32 val; val = readl(priv->base + SSI_FC); - val &= ~(SSI_FC_TXFTH_MASK | SSI_FC_RXFTH_MASK); - val |= FIELD_PREP(SSI_FC_TXFTH_MASK, SSI_FIFO_DEPTH - threshold); - val |= FIELD_PREP(SSI_FC_RXFTH_MASK, threshold); + FIELD_MODIFY(SSI_FC_TXFTH_MASK, &val, SSI_FIFO_DEPTH - threshold); + FIELD_MODIFY(SSI_FC_RXFTH_MASK, &val, threshold); writel(val, priv->base + SSI_FC); } From 59b991990a04b1d1ce95373983b7c8b65bdf7acc Mon Sep 17 00:00:00 2001 From: Qiang Ma Date: Fri, 15 May 2026 18:26:20 +0800 Subject: [PATCH 078/118] spi: hisi-kunpeng: Use dev_err_probe() for host registration failure When the SPI core registers the Kunpeng controller it may need to acquire chip-select GPIO descriptors. If the GPIO provider has not probed yet, spi_register_controller() returns -EPROBE_DEFER. On a Kunpeng system this currently prints an alarming error even though the next deferred-probe retry succeeds: hisi-kunpeng-spi HISI03E1:00: failed to register spi host, ret=-517 hisi-kunpeng-spi HISI03E1:00: hw version:0x30 max-freq:12500 kHz Use dev_err_probe() so that -EPROBE_DEFER is reported through the deferred probe mechanism instead of as a hard error, while preserving normal error reporting for real registration failures. Fixes: c770d8631e18 ("spi: Add HiSilicon SPI Controller Driver for Kunpeng SoCs") Signed-off-by: Qiang Ma Link: https://patch.msgid.link/20260515102620.1926930-1-maqianga@uniontech.com Signed-off-by: Mark Brown --- drivers/spi/spi-hisi-kunpeng.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/spi/spi-hisi-kunpeng.c b/drivers/spi/spi-hisi-kunpeng.c index 046bd894040b..395214b81179 100644 --- a/drivers/spi/spi-hisi-kunpeng.c +++ b/drivers/spi/spi-hisi-kunpeng.c @@ -520,10 +520,8 @@ static int hisi_spi_probe(struct platform_device *pdev) } ret = spi_register_controller(host); - if (ret) { - dev_err(dev, "failed to register spi host, ret=%d\n", ret); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, "failed to register spi host\n"); if (hisi_spi_debugfs_init(hs)) dev_info(dev, "failed to create debugfs dir\n"); From d3873c5f4d6e86852dee427832105b36fb06aef8 Mon Sep 17 00:00:00 2001 From: Felix Gu Date: Thu, 7 May 2026 22:06:36 +0800 Subject: [PATCH 079/118] spi: rspi: Simplify reset control handling Use devm_reset_control_get_optional_exclusive_deasserted() to combine get + deassert + cleanup in a single call, removing the redundant rspi_reset_control_assert() helper. Signed-off-by: Felix Gu Link: https://patch.msgid.link/20260507-rspi-v1-1-8cfa47cd56aa@gmail.com Signed-off-by: Mark Brown --- drivers/spi/spi-rspi.c | 21 ++------------------- 1 file changed, 2 insertions(+), 19 deletions(-) diff --git a/drivers/spi/spi-rspi.c b/drivers/spi/spi-rspi.c index 951e9a8af547..a0c77e02bc90 100644 --- a/drivers/spi/spi-rspi.c +++ b/drivers/spi/spi-rspi.c @@ -1222,11 +1222,6 @@ static const struct of_device_id rspi_of_match[] __maybe_unused = { MODULE_DEVICE_TABLE(of, rspi_of_match); #ifdef CONFIG_OF -static void rspi_reset_control_assert(void *data) -{ - reset_control_assert(data); -} - static int rspi_parse_dt(struct device *dev, struct spi_controller *ctlr) { struct reset_control *rstc; @@ -1242,22 +1237,10 @@ static int rspi_parse_dt(struct device *dev, struct spi_controller *ctlr) ctlr->num_chipselect = num_cs; - rstc = devm_reset_control_get_optional_exclusive(dev, NULL); + rstc = devm_reset_control_get_optional_exclusive_deasserted(dev, NULL); if (IS_ERR(rstc)) return dev_err_probe(dev, PTR_ERR(rstc), - "failed to get reset ctrl\n"); - - error = reset_control_deassert(rstc); - if (error) { - dev_err(dev, "failed to deassert reset %d\n", error); - return error; - } - - error = devm_add_action_or_reset(dev, rspi_reset_control_assert, rstc); - if (error) { - dev_err(dev, "failed to register assert devm action, %d\n", error); - return error; - } + "failed to get reset ctrl and deassert reset\n"); return 0; } From a7a24f0eaa7163f6988badee601492deb9e51551 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 11 May 2026 17:03:57 +0200 Subject: [PATCH 080/118] spi: altera-platform: switch to managed controller allocation Switch to device managed controller allocation for consistency and to simplify error handling. Signed-off-by: Johan Hovold Link: https://patch.msgid.link/20260511150408.796155-2-johan@kernel.org Signed-off-by: Mark Brown --- drivers/spi/spi-altera-platform.c | 27 ++++++++++----------------- 1 file changed, 10 insertions(+), 17 deletions(-) diff --git a/drivers/spi/spi-altera-platform.c b/drivers/spi/spi-altera-platform.c index fc81de2610ef..3ee5d3480bb4 100644 --- a/drivers/spi/spi-altera-platform.c +++ b/drivers/spi/spi-altera-platform.c @@ -39,12 +39,12 @@ static int altera_spi_probe(struct platform_device *pdev) enum altera_spi_type type = ALTERA_SPI_TYPE_UNKNOWN; struct altera_spi *hw; struct spi_controller *host; - int err = -ENODEV; + int err; u16 i; - host = spi_alloc_host(&pdev->dev, sizeof(struct altera_spi)); + host = devm_spi_alloc_host(&pdev->dev, sizeof(struct altera_spi)); if (!host) - return err; + return -ENOMEM; /* setup the host state. */ host->bus_num = -1; @@ -54,8 +54,7 @@ static int altera_spi_probe(struct platform_device *pdev) dev_err(&pdev->dev, "Invalid number of chipselect: %u\n", pdata->num_chipselect); - err = -EINVAL; - goto exit; + return -EINVAL; } host->num_chipselect = pdata->num_chipselect; @@ -80,7 +79,7 @@ static int altera_spi_probe(struct platform_device *pdev) hw->regmap = dev_get_regmap(pdev->dev.parent, NULL); if (!hw->regmap) { dev_err(&pdev->dev, "get regmap failed\n"); - goto exit; + return -ENODEV; } regoff = platform_get_resource(pdev, IORESOURCE_REG, 0); @@ -90,17 +89,14 @@ static int altera_spi_probe(struct platform_device *pdev) void __iomem *res; res = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(res)) { - err = PTR_ERR(res); - goto exit; - } + if (IS_ERR(res)) + return PTR_ERR(res); hw->regmap = devm_regmap_init_mmio(&pdev->dev, res, &spi_altera_config); if (IS_ERR(hw->regmap)) { dev_err(&pdev->dev, "regmap mmio init failed\n"); - err = PTR_ERR(hw->regmap); - goto exit; + return PTR_ERR(hw->regmap); } } @@ -112,12 +108,12 @@ static int altera_spi_probe(struct platform_device *pdev) err = devm_request_irq(&pdev->dev, hw->irq, altera_spi_irq, 0, pdev->name, host); if (err) - goto exit; + return err; } err = devm_spi_register_controller(&pdev->dev, host); if (err) - goto exit; + return err; if (pdata) { for (i = 0; i < pdata->num_devices; i++) { @@ -131,9 +127,6 @@ static int altera_spi_probe(struct platform_device *pdev) dev_info(&pdev->dev, "regoff %u, irq %d\n", hw->regoff, hw->irq); return 0; -exit: - spi_controller_put(host); - return err; } #ifdef CONFIG_OF From 5b4a0450fe2f7912f34590721eb191010bf98dd3 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 11 May 2026 17:03:58 +0200 Subject: [PATCH 081/118] spi: armada-3700: switch to managed controller allocation Switch to device managed controller allocation for consistency and to simplify error handling. Signed-off-by: Johan Hovold Link: https://patch.msgid.link/20260511150408.796155-3-johan@kernel.org Signed-off-by: Mark Brown --- drivers/spi/spi-armada-3700.c | 44 +++++++++++------------------------ 1 file changed, 14 insertions(+), 30 deletions(-) diff --git a/drivers/spi/spi-armada-3700.c b/drivers/spi/spi-armada-3700.c index 78248729d3e9..ca2faa265ca0 100644 --- a/drivers/spi/spi-armada-3700.c +++ b/drivers/spi/spi-armada-3700.c @@ -818,17 +818,13 @@ static int a3700_spi_probe(struct platform_device *pdev) u32 num_cs = 0; int irq, ret = 0; - host = spi_alloc_host(dev, sizeof(*spi)); - if (!host) { - dev_err(dev, "host allocation failed\n"); - ret = -ENOMEM; - goto out; - } + host = devm_spi_alloc_host(dev, sizeof(*spi)); + if (!host) + return -ENOMEM; if (of_property_read_u32(dev->of_node, "num-cs", &num_cs)) { dev_err(dev, "could not find num-cs\n"); - ret = -ENXIO; - goto error; + return -ENXIO; } host->bus_num = pdev->id; @@ -849,25 +845,20 @@ static int a3700_spi_probe(struct platform_device *pdev) spi->host = host; spi->base = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(spi->base)) { - ret = PTR_ERR(spi->base); - goto error; - } + if (IS_ERR(spi->base)) + return PTR_ERR(spi->base); irq = platform_get_irq(pdev, 0); - if (irq < 0) { - ret = -ENXIO; - goto error; - } + if (irq < 0) + return -ENXIO; + spi->irq = irq; init_completion(&spi->done); spi->clk = devm_clk_get_prepared(dev, NULL); - if (IS_ERR(spi->clk)) { - dev_err(dev, "could not find clk: %ld\n", PTR_ERR(spi->clk)); - goto error; - } + if (IS_ERR(spi->clk)) + return dev_err_probe(dev, PTR_ERR(spi->clk), "could not find clk\n"); host->max_speed_hz = min_t(unsigned long, A3700_SPI_MAX_SPEED_HZ, clk_get_rate(spi->clk)); @@ -878,23 +869,16 @@ static int a3700_spi_probe(struct platform_device *pdev) ret = devm_request_irq(dev, spi->irq, a3700_spi_interrupt, 0, dev_name(dev), host); - if (ret) { - dev_err(dev, "could not request IRQ: %d\n", ret); - goto error; - } + if (ret) + return dev_err_probe(dev, ret, "could not request IRQ\n"); ret = devm_spi_register_controller(dev, host); if (ret) { dev_err(dev, "Failed to register host\n"); - goto error; + return ret; } return 0; - -error: - spi_controller_put(host); -out: - return ret; } static struct platform_driver a3700_spi_driver = { From ee9575d2b3db1f3fde4563b6e52832dacc49d9c9 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 11 May 2026 17:03:59 +0200 Subject: [PATCH 082/118] spi: clps711x: switch to managed controller allocation Switch to device managed controller allocation for consistency and to simplify error handling. Signed-off-by: Johan Hovold Link: https://patch.msgid.link/20260511150408.796155-4-johan@kernel.org Signed-off-by: Mark Brown --- drivers/spi/spi-clps711x.c | 31 +++++++++---------------------- 1 file changed, 9 insertions(+), 22 deletions(-) diff --git a/drivers/spi/spi-clps711x.c b/drivers/spi/spi-clps711x.c index d6458e59d41b..382c3c81c4b9 100644 --- a/drivers/spi/spi-clps711x.c +++ b/drivers/spi/spi-clps711x.c @@ -99,7 +99,7 @@ static int spi_clps711x_probe(struct platform_device *pdev) if (irq < 0) return irq; - host = spi_alloc_host(&pdev->dev, sizeof(*hw)); + host = devm_spi_alloc_host(&pdev->dev, sizeof(*hw)); if (!host) return -ENOMEM; @@ -113,22 +113,16 @@ static int spi_clps711x_probe(struct platform_device *pdev) hw = spi_controller_get_devdata(host); hw->spi_clk = devm_clk_get(&pdev->dev, NULL); - if (IS_ERR(hw->spi_clk)) { - ret = PTR_ERR(hw->spi_clk); - goto err_out; - } + if (IS_ERR(hw->spi_clk)) + return PTR_ERR(hw->spi_clk); hw->syscon = syscon_regmap_lookup_by_phandle(np, "syscon"); - if (IS_ERR(hw->syscon)) { - ret = PTR_ERR(hw->syscon); - goto err_out; - } + if (IS_ERR(hw->syscon)) + return PTR_ERR(hw->syscon); hw->syncio = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(hw->syncio)) { - ret = PTR_ERR(hw->syncio); - goto err_out; - } + if (IS_ERR(hw->syncio)) + return PTR_ERR(hw->syncio); /* Disable extended mode due hardware problems */ regmap_update_bits(hw->syscon, SYSCON_OFFSET, SYSCON3_ADCCON, 0); @@ -139,16 +133,9 @@ static int spi_clps711x_probe(struct platform_device *pdev) ret = devm_request_irq(&pdev->dev, irq, spi_clps711x_isr, 0, dev_name(&pdev->dev), host); if (ret) - goto err_out; + return ret; - ret = devm_spi_register_controller(&pdev->dev, host); - if (!ret) - return 0; - -err_out: - spi_controller_put(host); - - return ret; + return devm_spi_register_controller(&pdev->dev, host); } static const struct of_device_id clps711x_spi_dt_ids[] = { From 864c368199cf30cadf6d9b5dbab82a8504d9650a Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 11 May 2026 17:04:00 +0200 Subject: [PATCH 083/118] spi: falcon: switch to managed controller allocation Switch to device managed controller allocation for consistency and to simplify error handling. Signed-off-by: Johan Hovold Link: https://patch.msgid.link/20260511150408.796155-5-johan@kernel.org Signed-off-by: Mark Brown --- drivers/spi/spi-falcon.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/drivers/spi/spi-falcon.c b/drivers/spi/spi-falcon.c index cb15faabd88f..e00e808eafee 100644 --- a/drivers/spi/spi-falcon.c +++ b/drivers/spi/spi-falcon.c @@ -392,9 +392,8 @@ static int falcon_sflash_probe(struct platform_device *pdev) { struct falcon_sflash *priv; struct spi_controller *host; - int ret; - host = spi_alloc_host(&pdev->dev, sizeof(*priv)); + host = devm_spi_alloc_host(&pdev->dev, sizeof(*priv)); if (!host) return -ENOMEM; @@ -406,10 +405,7 @@ static int falcon_sflash_probe(struct platform_device *pdev) host->setup = falcon_sflash_setup; host->transfer_one_message = falcon_sflash_xfer_one; - ret = devm_spi_register_controller(&pdev->dev, host); - if (ret) - spi_controller_put(host); - return ret; + return devm_spi_register_controller(&pdev->dev, host); } static const struct of_device_id falcon_sflash_match[] = { From c087eb4603d90e992ff26483682ba62c8c3f5628 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 11 May 2026 17:04:01 +0200 Subject: [PATCH 084/118] spi: fsi: switch to managed controller allocation Switch to device managed controller allocation for consistency and to simplify error handling. Signed-off-by: Johan Hovold Link: https://patch.msgid.link/20260511150408.796155-6-johan@kernel.org Signed-off-by: Mark Brown --- drivers/spi/spi-fsi.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/spi/spi-fsi.c b/drivers/spi/spi-fsi.c index f6a75f0184c4..451cb4cfdb9c 100644 --- a/drivers/spi/spi-fsi.c +++ b/drivers/spi/spi-fsi.c @@ -554,7 +554,7 @@ static int fsi_spi_probe(struct fsi_device *fsi) if (of_property_read_u32(np, "reg", &base)) continue; - ctlr = spi_alloc_host(dev, sizeof(*ctx)); + ctlr = devm_spi_alloc_host(dev, sizeof(*ctx)); if (!ctlr) break; @@ -571,9 +571,9 @@ static int fsi_spi_probe(struct fsi_device *fsi) rc = devm_spi_register_controller(dev, ctlr); if (rc) - spi_controller_put(ctlr); - else - num_controllers_registered++; + continue; + + num_controllers_registered++; } if (!num_controllers_registered) From 572a9fb1a6098170b669681e8444e8516a95b8c3 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 11 May 2026 17:04:02 +0200 Subject: [PATCH 085/118] spi: hisi-sfc-v3xx: switch to managed controller allocation Switch to device managed controller allocation for consistency and to simplify error handling. Signed-off-by: Johan Hovold Link: https://patch.msgid.link/20260511150408.796155-7-johan@kernel.org Signed-off-by: Mark Brown --- drivers/spi/spi-hisi-sfc-v3xx.c | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/drivers/spi/spi-hisi-sfc-v3xx.c b/drivers/spi/spi-hisi-sfc-v3xx.c index b2af2eed197f..eeeb86381862 100644 --- a/drivers/spi/spi-hisi-sfc-v3xx.c +++ b/drivers/spi/spi-hisi-sfc-v3xx.c @@ -436,7 +436,7 @@ static int hisi_sfc_v3xx_probe(struct platform_device *pdev) u32 version, glb_config; int ret; - ctlr = spi_alloc_host(&pdev->dev, sizeof(*host)); + ctlr = devm_spi_alloc_host(&pdev->dev, sizeof(*host)); if (!ctlr) return -ENOMEM; @@ -451,16 +451,12 @@ static int hisi_sfc_v3xx_probe(struct platform_device *pdev) platform_set_drvdata(pdev, host); host->regbase = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(host->regbase)) { - ret = PTR_ERR(host->regbase); - goto err_put_host; - } + if (IS_ERR(host->regbase)) + return PTR_ERR(host->regbase); host->irq = platform_get_irq_optional(pdev, 0); - if (host->irq == -EPROBE_DEFER) { - ret = -EPROBE_DEFER; - goto err_put_host; - } + if (host->irq == -EPROBE_DEFER) + return -EPROBE_DEFER; hisi_sfc_v3xx_disable_int(host); @@ -501,16 +497,12 @@ static int hisi_sfc_v3xx_probe(struct platform_device *pdev) ret = devm_spi_register_controller(dev, ctlr); if (ret) - goto err_put_host; + return ret; dev_info(&pdev->dev, "hw version 0x%x, %s mode.\n", version, host->irq ? "irq" : "polling"); return 0; - -err_put_host: - spi_controller_put(ctlr); - return ret; } static const struct acpi_device_id hisi_sfc_v3xx_acpi_ids[] = { From 0a290bb1ff0b11998db2b36c8ebcca4913ccacb7 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 11 May 2026 17:04:03 +0200 Subject: [PATCH 086/118] spi: jcore: switch to managed controller allocation Switch to device managed controller allocation for consistency and to simplify error handling. Signed-off-by: Johan Hovold Link: https://patch.msgid.link/20260511150408.796155-8-johan@kernel.org Signed-off-by: Mark Brown --- drivers/spi/spi-jcore.c | 23 ++++++----------------- 1 file changed, 6 insertions(+), 17 deletions(-) diff --git a/drivers/spi/spi-jcore.c b/drivers/spi/spi-jcore.c index e37ca22e04ba..a75cd61ec7a3 100644 --- a/drivers/spi/spi-jcore.c +++ b/drivers/spi/spi-jcore.c @@ -146,11 +146,10 @@ static int jcore_spi_probe(struct platform_device *pdev) struct resource *res; u32 clock_freq; struct clk *clk; - int err = -ENODEV; - host = spi_alloc_host(&pdev->dev, sizeof(struct jcore_spi)); + host = devm_spi_alloc_host(&pdev->dev, sizeof(struct jcore_spi)); if (!host) - return err; + return -ENOMEM; /* Setup the host state. */ host->num_chipselect = 3; @@ -167,14 +166,14 @@ static int jcore_spi_probe(struct platform_device *pdev) /* Find and map our resources */ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) - goto exit_busy; + return -EBUSY; if (!devm_request_mem_region(&pdev->dev, res->start, resource_size(res), pdev->name)) - goto exit_busy; + return -EBUSY; hw->base = devm_ioremap(&pdev->dev, res->start, resource_size(res)); if (!hw->base) - goto exit_busy; + return -EBUSY; /* * The SPI clock rate controlled via a configurable clock divider @@ -200,17 +199,7 @@ static int jcore_spi_probe(struct platform_device *pdev) jcore_spi_baudrate(hw, 400000); /* Register our spi controller */ - err = devm_spi_register_controller(&pdev->dev, host); - if (err) - goto exit; - - return 0; - -exit_busy: - err = -EBUSY; -exit: - spi_controller_put(host); - return err; + return devm_spi_register_controller(&pdev->dev, host); } static const struct of_device_id jcore_spi_of_match[] = { From 06ba67d9d47929652f66b7c3eeda5293f48a4545 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 11 May 2026 17:04:04 +0200 Subject: [PATCH 087/118] spi: lp8841-rtc: switch to managed controller allocation Switch to device managed controller allocation for consistency and to simplify error handling. Signed-off-by: Johan Hovold Link: https://patch.msgid.link/20260511150408.796155-9-johan@kernel.org Signed-off-by: Mark Brown --- drivers/spi/spi-lp8841-rtc.c | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/drivers/spi/spi-lp8841-rtc.c b/drivers/spi/spi-lp8841-rtc.c index e466866d5e80..355d9df4d1be 100644 --- a/drivers/spi/spi-lp8841-rtc.c +++ b/drivers/spi/spi-lp8841-rtc.c @@ -185,7 +185,7 @@ spi_lp8841_rtc_probe(struct platform_device *pdev) struct spi_controller *host; struct spi_lp8841_rtc *data; - host = spi_alloc_host(&pdev->dev, sizeof(*data)); + host = devm_spi_alloc_host(&pdev->dev, sizeof(*data)); if (!host) return -ENOMEM; platform_set_drvdata(pdev, host); @@ -208,23 +208,17 @@ spi_lp8841_rtc_probe(struct platform_device *pdev) ret = PTR_ERR_OR_ZERO(data->iomem); if (ret) { dev_err(&pdev->dev, "failed to get IO address\n"); - goto err_put_host; + return ret; } /* register with the SPI framework */ ret = devm_spi_register_controller(&pdev->dev, host); if (ret) { dev_err(&pdev->dev, "cannot register spi host\n"); - goto err_put_host; + return ret; } - return ret; - - -err_put_host: - spi_controller_put(host); - - return ret; + return 0; } MODULE_ALIAS("platform:" DRIVER_NAME); From 4fee1d3a563d2fd6eee8434adde0af10cd7db2ae Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 11 May 2026 17:04:05 +0200 Subject: [PATCH 088/118] spi: lp8841-rtc: drop unused ifdef Drop the probe CONFIG_OF ifdef which is unused since commit 3974a585be78 ("spi: Drop duplicate of_node assignment"). Signed-off-by: Johan Hovold Link: https://patch.msgid.link/20260511150408.796155-10-johan@kernel.org Signed-off-by: Mark Brown --- drivers/spi/spi-lp8841-rtc.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/spi/spi-lp8841-rtc.c b/drivers/spi/spi-lp8841-rtc.c index 355d9df4d1be..b2546a3a9eaa 100644 --- a/drivers/spi/spi-lp8841-rtc.c +++ b/drivers/spi/spi-lp8841-rtc.c @@ -199,8 +199,6 @@ spi_lp8841_rtc_probe(struct platform_device *pdev) host->set_cs = spi_lp8841_rtc_set_cs; host->transfer_one = spi_lp8841_rtc_transfer_one; host->bits_per_word_mask = SPI_BPW_MASK(8); -#ifdef CONFIG_OF -#endif data = spi_controller_get_devdata(host); From 1e447a5aa12443410da74b4bbf2ae922ca63ef4e Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 11 May 2026 17:04:06 +0200 Subject: [PATCH 089/118] spi: meson-spifc: switch to managed controller allocation Switch to device managed controller allocation for consistency and to simplify error handling. Signed-off-by: Johan Hovold Link: https://patch.msgid.link/20260511150408.796155-11-johan@kernel.org Signed-off-by: Mark Brown --- drivers/spi/spi-meson-spifc.c | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/drivers/spi/spi-meson-spifc.c b/drivers/spi/spi-meson-spifc.c index b818950a8cb7..d700fa315223 100644 --- a/drivers/spi/spi-meson-spifc.c +++ b/drivers/spi/spi-meson-spifc.c @@ -288,9 +288,9 @@ static int meson_spifc_probe(struct platform_device *pdev) struct meson_spifc *spifc; void __iomem *base; unsigned int rate; - int ret = 0; + int ret; - host = spi_alloc_host(&pdev->dev, sizeof(struct meson_spifc)); + host = devm_spi_alloc_host(&pdev->dev, sizeof(struct meson_spifc)); if (!host) return -ENOMEM; @@ -300,23 +300,18 @@ static int meson_spifc_probe(struct platform_device *pdev) spifc->dev = &pdev->dev; base = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(base)) { - ret = PTR_ERR(base); - goto out_err; - } + if (IS_ERR(base)) + return PTR_ERR(base); spifc->regmap = devm_regmap_init_mmio(spifc->dev, base, &spifc_regmap_config); - if (IS_ERR(spifc->regmap)) { - ret = PTR_ERR(spifc->regmap); - goto out_err; - } + if (IS_ERR(spifc->regmap)) + return PTR_ERR(spifc->regmap); spifc->clk = devm_clk_get_enabled(spifc->dev, NULL); if (IS_ERR(spifc->clk)) { dev_err(spifc->dev, "missing clock\n"); - ret = PTR_ERR(spifc->clk); - goto out_err; + return PTR_ERR(spifc->clk); } rate = clk_get_rate(spifc->clk); @@ -342,8 +337,7 @@ static int meson_spifc_probe(struct platform_device *pdev) return 0; out_pm: pm_runtime_disable(spifc->dev); -out_err: - spi_controller_put(host); + return ret; } From 60db555fca747c8d860d9b683d281d903c4dab50 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 11 May 2026 17:04:07 +0200 Subject: [PATCH 090/118] spi: mux: switch to managed controller allocation Switch to device managed controller allocation for consistency and to simplify error handling. Signed-off-by: Johan Hovold Link: https://patch.msgid.link/20260511150408.796155-12-johan@kernel.org Signed-off-by: Mark Brown --- drivers/spi/spi-mux.c | 19 ++++--------------- 1 file changed, 4 insertions(+), 15 deletions(-) diff --git a/drivers/spi/spi-mux.c b/drivers/spi/spi-mux.c index bd122de152c0..08fe1fa32dea 100644 --- a/drivers/spi/spi-mux.c +++ b/drivers/spi/spi-mux.c @@ -127,9 +127,8 @@ static int spi_mux_probe(struct spi_device *spi) { struct spi_controller *ctlr; struct spi_mux_priv *priv; - int ret; - ctlr = spi_alloc_host(&spi->dev, sizeof(*priv)); + ctlr = devm_spi_alloc_host(&spi->dev, sizeof(*priv)); if (!ctlr) return -ENOMEM; @@ -146,9 +145,8 @@ static int spi_mux_probe(struct spi_device *spi) priv->mux = devm_mux_control_get(&spi->dev, NULL); if (IS_ERR(priv->mux)) { - ret = dev_err_probe(&spi->dev, PTR_ERR(priv->mux), - "failed to get control-mux\n"); - goto err_put_ctlr; + return dev_err_probe(&spi->dev, PTR_ERR(priv->mux), + "failed to get control-mux\n"); } priv->current_cs = SPI_MUX_NO_CS; @@ -164,16 +162,7 @@ static int spi_mux_probe(struct spi_device *spi) ctlr->must_async = true; ctlr->defer_optimize_message = true; - ret = devm_spi_register_controller(&spi->dev, ctlr); - if (ret) - goto err_put_ctlr; - - return 0; - -err_put_ctlr: - spi_controller_put(ctlr); - - return ret; + return devm_spi_register_controller(&spi->dev, ctlr); } static const struct spi_device_id spi_mux_id[] = { From f3d1dc0bc65ae902a821bc2e03baaa107b319350 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 11 May 2026 17:04:08 +0200 Subject: [PATCH 091/118] spi: xlp: switch to managed controller allocation Switch to device managed controller allocation for consistency and to simplify error handling. Signed-off-by: Johan Hovold Link: https://patch.msgid.link/20260511150408.796155-13-johan@kernel.org Signed-off-by: Mark Brown --- drivers/spi/spi-xlp.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/spi/spi-xlp.c b/drivers/spi/spi-xlp.c index be8bbe1cbba3..d014955e6d4b 100644 --- a/drivers/spi/spi-xlp.c +++ b/drivers/spi/spi-xlp.c @@ -398,7 +398,7 @@ static int xlp_spi_probe(struct platform_device *pdev) xspi->spi_clk = clk_get_rate(clk); - host = spi_alloc_host(&pdev->dev, 0); + host = devm_spi_alloc_host(&pdev->dev, 0); if (!host) { dev_err(&pdev->dev, "could not alloc host\n"); return -ENOMEM; @@ -418,7 +418,6 @@ static int xlp_spi_probe(struct platform_device *pdev) err = devm_spi_register_controller(&pdev->dev, host); if (err) { dev_err(&pdev->dev, "spi register host failed!\n"); - spi_controller_put(host); return err; } From bf62d130b1f2e34d91ba606a8c1bd3037d6c1450 Mon Sep 17 00:00:00 2001 From: Praveen Talari Date: Mon, 18 May 2026 22:30:51 +0530 Subject: [PATCH 092/118] spi: qcom-geni: trace: Add trace events for Qualcomm GENI SPI Add tracepoint support to the Qualcomm GENI SPI driver to provide runtime visibility into driver behavior without requiring invasive debug patches. The trace events cover clock and setup parameter configuration, transfer metadata, interrupt status to be making it easier to diagnose communication issues in the field.. Reviewed-by: Konrad Dybcio Signed-off-by: Praveen Talari Link: https://patch.msgid.link/20260518-add-tracepoints-for-qcom-geni-spi-v3-1-7928f6810a79@oss.qualcomm.com Signed-off-by: Mark Brown --- include/trace/events/qcom_geni_spi.h | 103 +++++++++++++++++++++++++++ 1 file changed, 103 insertions(+) create mode 100644 include/trace/events/qcom_geni_spi.h diff --git a/include/trace/events/qcom_geni_spi.h b/include/trace/events/qcom_geni_spi.h new file mode 100644 index 000000000000..6d027adf2e1d --- /dev/null +++ b/include/trace/events/qcom_geni_spi.h @@ -0,0 +1,103 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#undef TRACE_SYSTEM +#define TRACE_SYSTEM qcom_geni_spi + +#if !defined(_TRACE_QCOM_GENI_SPI_H) || defined(TRACE_HEADER_MULTI_READ) +#define _TRACE_QCOM_GENI_SPI_H + +#include + +TRACE_EVENT(geni_spi_setup_params, + TP_PROTO(struct device *dev, u8 cs, u32 mode, + u32 mode_changed, bool cs_changed), + TP_ARGS(dev, cs, mode, mode_changed, cs_changed), + + TP_STRUCT__entry(__string(name, dev_name(dev)) + __field(u8, cs) + __field(u32, mode) + __field(u32, mode_changed) + __field(bool, cs_changed) + ), + + TP_fast_assign(__assign_str(name); + __entry->cs = cs; + __entry->mode = mode; + __entry->mode_changed = mode_changed; + __entry->cs_changed = cs_changed; + ), + + TP_printk("%s: cs=%u mode=0x%08x mode_changed=0x%08x cs_changed=%d", + __get_str(name), __entry->cs, __entry->mode, + __entry->mode_changed, __entry->cs_changed) +); + +TRACE_EVENT(geni_spi_clk_cfg, + TP_PROTO(struct device *dev, unsigned long req_hz, + unsigned long sclk_hz, unsigned int clk_idx, + unsigned int clk_div, unsigned int bpw), + TP_ARGS(dev, req_hz, sclk_hz, clk_idx, clk_div, bpw), + + TP_STRUCT__entry(__string(name, dev_name(dev)) + __field(unsigned long, req_hz) + __field(unsigned long, sclk_hz) + __field(unsigned int, clk_idx) + __field(unsigned int, clk_div) + __field(unsigned int, bpw) + ), + + TP_fast_assign(__assign_str(name); + __entry->req_hz = req_hz; + __entry->sclk_hz = sclk_hz; + __entry->clk_idx = clk_idx; + __entry->clk_div = clk_div; + __entry->bpw = bpw; + ), + + TP_printk("%s: req_hz=%lu sclk_hz=%lu clk_idx=%u clk_div=%u bpw=%u", + __get_str(name), __entry->req_hz, __entry->sclk_hz, + __entry->clk_idx, __entry->clk_div, __entry->bpw) +); + +TRACE_EVENT(geni_spi_transfer, + TP_PROTO(struct device *dev, unsigned int len, u32 m_cmd), + TP_ARGS(dev, len, m_cmd), + + TP_STRUCT__entry(__string(name, dev_name(dev)) + __field(unsigned int, len) + __field(u32, m_cmd) + ), + + TP_fast_assign(__assign_str(name); + __entry->len = len; + __entry->m_cmd = m_cmd; + ), + + TP_printk("%s: len=%u m_cmd=0x%08x", + __get_str(name), __entry->len, __entry->m_cmd) +); + +TRACE_EVENT(geni_spi_irq, + TP_PROTO(struct device *dev, u32 m_irq, u32 dma_tx, u32 dma_rx), + TP_ARGS(dev, m_irq, dma_tx, dma_rx), + + TP_STRUCT__entry(__string(name, dev_name(dev)) + __field(u32, m_irq) + __field(u32, dma_tx) + __field(u32, dma_rx) + ), + + TP_fast_assign(__assign_str(name); + __entry->m_irq = m_irq; + __entry->dma_tx = dma_tx; + __entry->dma_rx = dma_rx; + ), + + TP_printk("%s: m_irq=0x%08x dma_tx=0x%08x dma_rx=0x%08x", + __get_str(name), __entry->m_irq, __entry->dma_tx, + __entry->dma_rx) +); + +#endif /* _TRACE_QCOM_GENI_SPI_H */ + +/* This part must be outside protection */ +#include From b5687af4af890cfd0a59f879ded40be92a19076e Mon Sep 17 00:00:00 2001 From: Praveen Talari Date: Mon, 18 May 2026 22:30:52 +0530 Subject: [PATCH 093/118] spi: qcom-geni: Add trace events for Qualcomm GENI SPI driver Add tracepoints to the Qualcomm GENI (Generic Interface) SPI driver. These trace events enable runtime debugging and performance analysis of SPI operations. The trace events capture SPI clock configuration, setup parameters, transfer details, interrupt status. Reviewed-by: Konrad Dybcio Signed-off-by: Praveen Talari Link: https://patch.msgid.link/20260518-add-tracepoints-for-qcom-geni-spi-v3-2-7928f6810a79@oss.qualcomm.com Signed-off-by: Mark Brown --- drivers/spi/spi-geni-qcom.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/drivers/spi/spi-geni-qcom.c b/drivers/spi/spi-geni-qcom.c index d5fb0edc8e0c..a04cdc1e5ad4 100644 --- a/drivers/spi/spi-geni-qcom.c +++ b/drivers/spi/spi-geni-qcom.c @@ -1,6 +1,9 @@ // SPDX-License-Identifier: GPL-2.0 // Copyright (c) 2017-2018, The Linux foundation. All rights reserved. +#define CREATE_TRACE_POINTS +#include + #include #include #include @@ -332,6 +335,9 @@ static int geni_spi_set_clock_and_bw(struct spi_geni_master *mas, writel(clk_sel, se->base + SE_GENI_CLK_SEL); writel(m_clk_cfg, se->base + GENI_SER_M_CLK_CFG); + trace_geni_spi_clk_cfg(mas->dev, clk_hz, mas->cur_sclk_hz, idx, div, + mas->cur_bits_per_word); + /* Set BW quota for CPU as driver supports FIFO mode only. */ se->icc_paths[CPU_TO_GENI].avg_bw = Bps_to_icc(mas->cur_speed_hz); ret = geni_icc_set_bw(se); @@ -366,6 +372,9 @@ static int setup_fifo_params(struct spi_device *spi_slv, if ((mode_changed & SPI_CS_HIGH) || (cs_changed && (spi_slv->mode & SPI_CS_HIGH))) writel((spi_slv->mode & SPI_CS_HIGH) ? BIT(chipselect) : 0, se->base + SE_SPI_DEMUX_OUTPUT_INV); + trace_geni_spi_setup_params(mas->dev, chipselect, spi_slv->mode, + mode_changed, cs_changed); + return 0; } @@ -861,6 +870,8 @@ static int setup_se_xfer(struct spi_transfer *xfer, spin_lock_irq(&mas->lock); geni_se_setup_m_cmd(se, m_cmd, m_params); + trace_geni_spi_transfer(mas->dev, len, m_cmd); + if (mas->cur_xfer_mode == GENI_SE_DMA) { if (m_cmd & SPI_RX_ONLY) geni_se_rx_init_dma(se, sg_dma_address(xfer->rx_sg.sgl), @@ -915,6 +926,8 @@ static irqreturn_t geni_spi_isr(int irq, void *data) if (!m_irq && !dma_tx_status && !dma_rx_status) return IRQ_NONE; + trace_geni_spi_irq(mas->dev, m_irq, dma_tx_status, dma_rx_status); + if (m_irq & (M_CMD_OVERRUN_EN | M_ILLEGAL_CMD_EN | M_CMD_FAILURE_EN | M_RX_FIFO_RD_ERR_EN | M_RX_FIFO_WR_ERR_EN | M_TX_FIFO_RD_ERR_EN | M_TX_FIFO_WR_ERR_EN)) From 0c5b5c40dc31089e8e59d53a32313baa50bc0809 Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Mon, 18 May 2026 17:56:14 -0700 Subject: [PATCH 094/118] spi: cadence-xspi: Add COMPILE_TEST support The Cadence XSPI driver uses readq() and writeq(), which are not provided directly by all 32-bit architectures. Include the generic non-atomic 64-bit I/O accessor fallback for non-64-bit builds so the driver can build there. Drop the 64BIT dependency at the same time. The driver only needs MMIO and the SPI memory interface at build time, and the fallback accessors cover the 32-bit compile-test case. Assisted-by: Codex:GPT-5.5 Signed-off-by: Rosen Penev Link: https://patch.msgid.link/20260519005614.628437-1-rosenp@gmail.com Signed-off-by: Mark Brown --- drivers/spi/Kconfig | 3 ++- drivers/spi/spi-cadence-xspi.c | 4 ++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 8782514bb89b..957c3e065b83 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -321,7 +321,8 @@ config SPI_CADENCE_QUADSPI config SPI_CADENCE_XSPI tristate "Cadence XSPI controller" - depends on OF && HAS_IOMEM && 64BIT + depends on HAS_IOMEM || COMPILE_TEST + depends on OF depends on SPI_MEM help Enable support for the Cadence XSPI Flash controller. diff --git a/drivers/spi/spi-cadence-xspi.c b/drivers/spi/spi-cadence-xspi.c index 32fa19ebf7a9..c45b29c043bf 100644 --- a/drivers/spi/spi-cadence-xspi.c +++ b/drivers/spi/spi-cadence-xspi.c @@ -22,6 +22,10 @@ #include #include +#ifndef CONFIG_64BIT +#include +#endif + #define CDNS_XSPI_MAGIC_NUM_VALUE 0x6522 #define CDNS_XSPI_MAX_BANKS 8 #define CDNS_XSPI_NAME "cadence-xspi" From 019947c495850461242fdcc0780258805595036c Mon Sep 17 00:00:00 2001 From: Thomas Lin Date: Thu, 21 May 2026 10:34:50 +0800 Subject: [PATCH 095/118] spi: dw-mmio: Add ACPI ID LECA0002 for LECARC SoCs This ID requires a custom initialization function dw_spi_hssi_no_dma_init() that sets dws->dws.ip to DW_HSSI_ID. Signed-off-by: Thomas Lin Reviewed-by: Andy Shevchenko Link: https://patch.msgid.link/20260521-lecarc-acpi-ids-v1-2-ae0ae90b2817@lecomputing.com Signed-off-by: Mark Brown --- drivers/acpi/acpi_apd.c | 7 +++++++ drivers/spi/spi-dw-mmio.c | 7 ++++--- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/drivers/acpi/acpi_apd.c b/drivers/acpi/acpi_apd.c index bed0791c17fc..4d5a51d30adc 100644 --- a/drivers/acpi/acpi_apd.c +++ b/drivers/acpi/acpi_apd.c @@ -181,6 +181,12 @@ static const struct apd_device_desc hip08_spi_desc = { .setup = acpi_apd_setup, .fixed_clk_rate = 250000000, }; + +static const struct apd_device_desc leca_spi_desc = { + .setup = acpi_apd_setup, + .fixed_clk_rate = 400000000, +}; + #endif /* CONFIG_ARM64 */ #endif @@ -251,6 +257,7 @@ static const struct acpi_device_id acpi_apd_device_ids[] = { { "HISI02A2", APD_ADDR(hip08_i2c_desc) }, { "HISI02A3", APD_ADDR(hip08_lite_i2c_desc) }, { "HISI0173", APD_ADDR(hip08_spi_desc) }, + { "LECA0002", APD_ADDR(leca_spi_desc) }, { "NXP0001", APD_ADDR(nxp_i2c_desc) }, #endif { } diff --git a/drivers/spi/spi-dw-mmio.c b/drivers/spi/spi-dw-mmio.c index 1bfdf24c3227..4fc864d38cff 100644 --- a/drivers/spi/spi-dw-mmio.c +++ b/drivers/spi/spi-dw-mmio.c @@ -226,8 +226,8 @@ static int dw_spi_hssi_init(struct platform_device *pdev, return 0; } -static int dw_spi_intel_init(struct platform_device *pdev, - struct dw_spi_mmio *dwsmmio) +static int dw_spi_hssi_no_dma_init(struct platform_device *pdev, + struct dw_spi_mmio *dwsmmio) { dwsmmio->dws.ip = DW_HSSI_ID; @@ -438,7 +438,7 @@ static const struct of_device_id dw_spi_mmio_of_match[] = { { .compatible = "amazon,alpine-dw-apb-ssi", .data = dw_spi_alpine_init}, { .compatible = "renesas,rzn1-spi", .data = dw_spi_pssi_init}, { .compatible = "snps,dwc-ssi-1.01a", .data = dw_spi_hssi_init}, - { .compatible = "intel,keembay-ssi", .data = dw_spi_intel_init}, + { .compatible = "intel,keembay-ssi", .data = dw_spi_hssi_no_dma_init}, { .compatible = "intel,mountevans-imc-ssi", .data = dw_spi_mountevans_imc_init, @@ -453,6 +453,7 @@ MODULE_DEVICE_TABLE(of, dw_spi_mmio_of_match); #ifdef CONFIG_ACPI static const struct acpi_device_id dw_spi_mmio_acpi_match[] = { {"HISI0173", (kernel_ulong_t)dw_spi_pssi_init}, + {"LECA0002", (kernel_ulong_t)dw_spi_hssi_no_dma_init}, {}, }; MODULE_DEVICE_TABLE(acpi, dw_spi_mmio_acpi_match); From 16ba3b0c66ef1d16bce2e99d787d7d12281bb2de Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 21 May 2026 09:38:16 +0200 Subject: [PATCH 096/118] spi: fix controller registration API inconsistency The SPI controller API is asymmetric in that a controller is allocated and registered in two step, while it is freed as part of deregistration. [1] This is especially unfortunate as any driver data is freed along with the controller, something which has lead to use-after-free bugs during deregistration when drivers tear down resources after deregistering the controller (or tear down resources that may still be in use before deregistering the controller in an attempt to work around the API). To reduce the risk of such bugs being introduced a device managed allocation interface was added, but this arguably made things even less consistent as now whether the controller gets freed as part of deregistration depends on how it was allocated. [2][3] With most drivers converted to use managed allocation in preparation for fixing the API, the remaining 16 drivers can be converted in one tree-wide change. Ten of those drivers use the bitbang interface and can be converted by simply removing the extra reference already taken by spi_bitbang_start() (and updating the two bitbang drivers that use managed allocation). [4] Fix the API inconsistency by no longer dropping a reference when deregistering non-devres allocated controllers. [1] 68b892f1fdc4 ("spi: document odd controller reference handling") [2] 5e844cc37a5c ("spi: Introduce device-managed SPI controller allocation") [3] 3f174274d224 ("spi: fix misleading controller deregistration kernel-doc") [4] 702a4879ec33 ("spi: bitbang: Let spi_bitbang_start() take a reference to master") Signed-off-by: Johan Hovold Link: https://patch.msgid.link/20260521073816.766596-1-johan@kernel.org Signed-off-by: Mark Brown --- drivers/media/usb/msi2500/msi2500.c | 9 ++++---- drivers/spi/spi-bitbang.c | 11 +-------- drivers/spi/spi-dw-core.c | 2 ++ drivers/spi/spi-fsl-dspi.c | 2 ++ drivers/spi/spi-mpc52xx.c | 2 +- drivers/spi/spi-topcliff-pch.c | 2 -- drivers/spi/spi-xilinx.c | 2 -- drivers/spi/spi-xtensa-xtfpga.c | 1 - drivers/spi/spi.c | 35 +++++------------------------ drivers/staging/greybus/spilib.c | 8 +++---- include/linux/spi/spi.h | 4 ---- 11 files changed, 19 insertions(+), 59 deletions(-) diff --git a/drivers/media/usb/msi2500/msi2500.c b/drivers/media/usb/msi2500/msi2500.c index 1ff98956b680..5c512d61dc1b 100644 --- a/drivers/media/usb/msi2500/msi2500.c +++ b/drivers/media/usb/msi2500/msi2500.c @@ -575,6 +575,7 @@ static void msi2500_disconnect(struct usb_interface *intf) v4l2_device_disconnect(&dev->v4l2_dev); video_unregister_device(&dev->vdev); spi_unregister_controller(dev->ctlr); + spi_controller_put(dev->ctlr); mutex_unlock(&dev->v4l2_lock); mutex_unlock(&dev->vb_queue_lock); @@ -1230,10 +1231,8 @@ static int msi2500_probe(struct usb_interface *intf, ctlr->transfer_one_message = msi2500_transfer_one_message; spi_controller_set_devdata(ctlr, dev); ret = spi_register_controller(ctlr); - if (ret) { - spi_controller_put(ctlr); - goto err_unregister_v4l2_dev; - } + if (ret) + goto err_put_controller; /* load v4l2 subdevice */ sd = v4l2_spi_new_subdev(&dev->v4l2_dev, ctlr, &board_info); @@ -1276,6 +1275,8 @@ static int msi2500_probe(struct usb_interface *intf, v4l2_ctrl_handler_free(&dev->hdl); err_unregister_controller: spi_unregister_controller(dev->ctlr); +err_put_controller: + spi_controller_put(dev->ctlr); err_unregister_v4l2_dev: v4l2_device_unregister(&dev->v4l2_dev); err_free_mem: diff --git a/drivers/spi/spi-bitbang.c b/drivers/spi/spi-bitbang.c index 9f03ac217319..fb288ed56d94 100644 --- a/drivers/spi/spi-bitbang.c +++ b/drivers/spi/spi-bitbang.c @@ -420,11 +420,6 @@ EXPORT_SYMBOL_GPL(spi_bitbang_init); * This routine registers the spi_controller, which will process requests in a * dedicated task, keeping IRQs unblocked most of the time. To stop * processing those requests, call spi_bitbang_stop(). - * - * On success, this routine will take a reference to the controller. The caller - * is responsible for calling spi_bitbang_stop() to decrement the reference and - * spi_controller_put() as counterpart of spi_alloc_host() to prevent a memory - * leak. */ int spi_bitbang_start(struct spi_bitbang *bitbang) { @@ -438,11 +433,7 @@ int spi_bitbang_start(struct spi_bitbang *bitbang) /* driver may get busy before register() returns, especially * if someone registered boardinfo for devices */ - ret = spi_register_controller(spi_controller_get(ctlr)); - if (ret) - spi_controller_put(ctlr); - - return ret; + return spi_register_controller(ctlr); } EXPORT_SYMBOL_GPL(spi_bitbang_start); diff --git a/drivers/spi/spi-dw-core.c b/drivers/spi/spi-dw-core.c index b47637888c5c..9a85a3ce5652 100644 --- a/drivers/spi/spi-dw-core.c +++ b/drivers/spi/spi-dw-core.c @@ -1032,6 +1032,8 @@ void dw_spi_remove_controller(struct dw_spi *dws) dw_spi_shutdown_chip(dws); free_irq(dws->irq, dws->ctlr); + + spi_controller_put(dws->ctlr); } EXPORT_SYMBOL_NS_GPL(dw_spi_remove_controller, "SPI_DW_CORE"); diff --git a/drivers/spi/spi-fsl-dspi.c b/drivers/spi/spi-fsl-dspi.c index 9f2a7b8163b1..019d05cdefe6 100644 --- a/drivers/spi/spi-fsl-dspi.c +++ b/drivers/spi/spi-fsl-dspi.c @@ -1715,6 +1715,8 @@ static void dspi_remove(struct platform_device *pdev) dspi_release_dma(dspi); if (dspi->irq) free_irq(dspi->irq, dspi); + + spi_controller_put(dspi->ctlr); } static void dspi_shutdown(struct platform_device *pdev) diff --git a/drivers/spi/spi-mpc52xx.c b/drivers/spi/spi-mpc52xx.c index 04c2270cd2cf..70d8e17e8c43 100644 --- a/drivers/spi/spi-mpc52xx.c +++ b/drivers/spi/spi-mpc52xx.c @@ -520,7 +520,7 @@ static int mpc52xx_spi_probe(struct platform_device *op) static void mpc52xx_spi_remove(struct platform_device *op) { - struct spi_controller *host = spi_controller_get(platform_get_drvdata(op)); + struct spi_controller *host = platform_get_drvdata(op); struct mpc52xx_spi *ms = spi_controller_get_devdata(host); int i; diff --git a/drivers/spi/spi-topcliff-pch.c b/drivers/spi/spi-topcliff-pch.c index 02ced638d8b4..c5409ca7faef 100644 --- a/drivers/spi/spi-topcliff-pch.c +++ b/drivers/spi/spi-topcliff-pch.c @@ -1406,8 +1406,6 @@ 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); - spi_controller_get(data->host); - spi_unregister_controller(data->host); /* check for any pending messages; no action is taken if the queue diff --git a/drivers/spi/spi-xilinx.c b/drivers/spi/spi-xilinx.c index 9f065d4e27d1..3aeef70caa96 100644 --- a/drivers/spi/spi-xilinx.c +++ b/drivers/spi/spi-xilinx.c @@ -515,8 +515,6 @@ static void xilinx_spi_remove(struct platform_device *pdev) xspi->write_fn(0, regs_base + XIPIF_V123B_IIER_OFFSET); /* Disable the global IPIF interrupt */ xspi->write_fn(0, regs_base + XIPIF_V123B_DGIER_OFFSET); - - spi_controller_put(xspi->bitbang.ctlr); } /* work with hotplug and coldplug */ diff --git a/drivers/spi/spi-xtensa-xtfpga.c b/drivers/spi/spi-xtensa-xtfpga.c index 71f0f176cfd9..d47a1e548fce 100644 --- a/drivers/spi/spi-xtensa-xtfpga.c +++ b/drivers/spi/spi-xtensa-xtfpga.c @@ -122,7 +122,6 @@ static void xtfpga_spi_remove(struct platform_device *pdev) struct xtfpga_spi *xspi = spi_controller_get_devdata(host); spi_bitbang_stop(&xspi->bitbang); - spi_controller_put(host); } MODULE_ALIAS("platform:" XTFPGA_SPI_NAME); diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 5f57de24b9f7..0576bd00d3ef 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -3226,9 +3226,9 @@ extern struct class spi_target_class; /* dummy */ * This must be called from context that can sleep. * * The caller is responsible for assigning the bus number and initializing the - * controller's methods before calling spi_register_controller(); and (after - * errors adding the device) calling spi_controller_put() to prevent a memory - * leak. + * controller's methods before calling spi_register_controller(); and calling + * spi_controller_put() to prevent a memory leak when done with the + * controller. * * Return: the SPI controller structure on success, else NULL. */ @@ -3312,8 +3312,6 @@ struct spi_controller *__devm_spi_alloc_controller(struct device *dev, if (ret) return NULL; - ctlr->devm_allocated = true; - return ctlr; } EXPORT_SYMBOL_GPL(__devm_spi_alloc_controller); @@ -3579,8 +3577,7 @@ static void devm_spi_unregister_controller(void *ctlr) * Context: can sleep * * Register a SPI device as with spi_register_controller() which will - * automatically be unregistered (and freed unless it has been allocated using - * devm_spi_alloc_host/target()). + * automatically be unregistered. * * Return: zero on success, else a negative error code. */ @@ -3593,19 +3590,7 @@ int devm_spi_register_controller(struct device *dev, if (ret) return ret; - /* - * Prevent controller from being freed by spi_unregister_controller() - * if devm_add_action_or_reset() fails for a non-devres allocated - * controller. - */ - spi_controller_get(ctlr); - - ret = devm_add_action_or_reset(dev, devm_spi_unregister_controller, ctlr); - - if (ret == 0 || ctlr->devm_allocated) - spi_controller_put(ctlr); - - return ret; + return devm_add_action_or_reset(dev, devm_spi_unregister_controller, ctlr); } EXPORT_SYMBOL_GPL(devm_spi_register_controller); @@ -3624,9 +3609,6 @@ static int __unregister(struct device *dev, void *null) * only ones directly touching chip registers. * * This must be called from context that can sleep. - * - * Note that this function also drops a reference to the controller unless it - * has been allocated using devm_spi_alloc_host/target(). */ void spi_unregister_controller(struct spi_controller *ctlr) { @@ -3661,13 +3643,6 @@ void spi_unregister_controller(struct spi_controller *ctlr) if (IS_ENABLED(CONFIG_SPI_DYNAMIC)) mutex_unlock(&ctlr->add_lock); - - /* - * Release the last reference on the controller if its driver - * has not yet been converted to devm_spi_alloc_host/target(). - */ - if (!ctlr->devm_allocated) - put_device(&ctlr->dev); } EXPORT_SYMBOL_GPL(spi_unregister_controller); diff --git a/drivers/staging/greybus/spilib.c b/drivers/staging/greybus/spilib.c index 24e9c909fa02..e4d1ae8308aa 100644 --- a/drivers/staging/greybus/spilib.c +++ b/drivers/staging/greybus/spilib.c @@ -547,13 +547,10 @@ int gb_spilib_master_init(struct gb_connection *connection, struct device *dev, return 0; -exit_spi_put: - spi_controller_put(ctlr); - - return ret; - exit_spi_unregister: spi_unregister_controller(ctlr); +exit_spi_put: + spi_controller_put(ctlr); return ret; } @@ -564,6 +561,7 @@ void gb_spilib_master_exit(struct gb_connection *connection) struct spi_controller *ctlr = gb_connection_get_data(connection); spi_unregister_controller(ctlr); + spi_controller_put(ctlr); } EXPORT_SYMBOL_GPL(gb_spilib_master_exit); diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h index 79513f5941cc..f6ed93eff00b 100644 --- a/include/linux/spi/spi.h +++ b/include/linux/spi/spi.h @@ -424,7 +424,6 @@ extern struct spi_device *devm_spi_new_ancillary_device(struct spi_device *spi, * @flags: other constraints relevant to this driver * @slave: indicates that this is an SPI slave controller * @target: indicates that this is an SPI target controller - * @devm_allocated: whether the allocation of this struct is devres-managed * @max_transfer_size: function that returns the max transfer size for * a &spi_device; may be %NULL, so the default %SIZE_MAX will be used. * @max_message_size: function that returns the max message size for @@ -629,9 +628,6 @@ struct spi_controller { */ #define SPI_CONTROLLER_MULTI_CS BIT(7) - /* Flag indicating if the allocation of this struct is devres-managed */ - bool devm_allocated; - union { /* Flag indicating this is an SPI slave controller */ bool slave; From 90330ae4e0d891ecb4879d03cbdd6bdca062c7a0 Mon Sep 17 00:00:00 2001 From: Lad Prabhakar Date: Wed, 20 May 2026 10:13:42 +0100 Subject: [PATCH 097/118] spi: Drop redundant 'cs' variable declaration in __spi_add_device() Remove the redundant local variable 'cs' definition declared within the chipselect GPIO configuration block. Signed-off-by: Lad Prabhakar Link: https://patch.msgid.link/20260520091342.68029-1-prabhakar.mahadev-lad.rj@bp.renesas.com Signed-off-by: Mark Brown --- drivers/spi/spi.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 0576bd00d3ef..fe7e3b3b98fc 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -732,8 +732,6 @@ static int __spi_add_device(struct spi_device *spi, struct spi_device *parent) } if (ctlr->cs_gpiods) { - u8 cs; - for (idx = 0; idx < spi->num_chipselect; idx++) { cs = spi_get_chipselect(spi, idx); spi_set_csgpiod(spi, idx, ctlr->cs_gpiods[cs]); From 5c3091e23f8fc2bdb6d85ca23b6097f05f3f0467 Mon Sep 17 00:00:00 2001 From: Chin-Ting Kuo Date: Fri, 22 May 2026 15:16:20 +0800 Subject: [PATCH 098/118] spi: aspeed: Fix missing __iomem annotation in output transfer path MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The dst parameter of aspeed_spi_user_transfer_tx() is an MMIO address obtained from chip->ahb_base, but it was typed as void * instead of void __iomem *. This caused a sparse warning report. Fix the parameter type to void __iomem * and drop the now-unnecessary cast at the call site. Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202605180441.uD3toFRJ-lkp@intel.com/ Signed-off-by: Chin-Ting Kuo Reviewed-by: Cédric Le Goater Link: https://patch.msgid.link/20260522071621.102507-2-chin-ting_kuo@aspeedtech.com Signed-off-by: Mark Brown --- drivers/spi/spi-aspeed-smc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/spi/spi-aspeed-smc.c b/drivers/spi/spi-aspeed-smc.c index c21323e07d3c..808659a1f460 100644 --- a/drivers/spi/spi-aspeed-smc.c +++ b/drivers/spi/spi-aspeed-smc.c @@ -891,7 +891,7 @@ static int aspeed_spi_user_unprepare_msg(struct spi_controller *ctlr, static void aspeed_spi_user_transfer_tx(struct aspeed_spi *aspi, struct spi_device *spi, const u8 *tx_buf, u8 *rx_buf, - void *dst, u32 len) + void __iomem *dst, u32 len) { const struct aspeed_spi_data *data = aspi->data; bool full_duplex_transfer = data->full_duplex && tx_buf == rx_buf; @@ -936,7 +936,7 @@ static int aspeed_spi_user_transfer(struct spi_controller *ctlr, aspeed_spi_set_io_mode(chip, CTRL_IO_QUAD_DATA); aspeed_spi_user_transfer_tx(aspi, spi, tx_buf, rx_buf, - (void *)ahb_base, xfer->len); + ahb_base, xfer->len); } if (rx_buf && rx_buf != tx_buf) { From 94f5efbaa7518cfa0f9c684be85d66bd005cfbe3 Mon Sep 17 00:00:00 2001 From: Chin-Ting Kuo Date: Fri, 22 May 2026 15:16:21 +0800 Subject: [PATCH 099/118] spi: aspeed: Replace VLA parameter with flat pointer in calibration helper MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit aspeed_spi_ast2600_optimized_timing() declared its buffer argument as a variable-length array parameter (u8 buf[rows][cols]), which causes a sparse warning. Replace the VLA parameter with a plain u8 * and compute the 2-D index manually. The corresponding call site is also updated. Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202605180441.uD3toFRJ-lkp@intel.com/ Signed-off-by: Chin-Ting Kuo Reviewed-by: Cédric Le Goater Link: https://patch.msgid.link/20260522071621.102507-3-chin-ting_kuo@aspeedtech.com Signed-off-by: Mark Brown --- drivers/spi/spi-aspeed-smc.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/spi/spi-aspeed-smc.c b/drivers/spi/spi-aspeed-smc.c index 808659a1f460..027caa2eeb5c 100644 --- a/drivers/spi/spi-aspeed-smc.c +++ b/drivers/spi/spi-aspeed-smc.c @@ -1467,8 +1467,7 @@ static int aspeed_spi_do_calibration(struct aspeed_spi_chip *chip) * must contains the highest number of consecutive "pass" * results and not span across multiple rows. */ -static u32 aspeed_spi_ast2600_optimized_timing(u32 rows, u32 cols, - u8 buf[rows][cols]) +static u32 aspeed_spi_ast2600_optimized_timing(u32 rows, u32 cols, u8 *buf) { int r = 0, c = 0; int max = 0; @@ -1478,7 +1477,7 @@ static u32 aspeed_spi_ast2600_optimized_timing(u32 rows, u32 cols, for (j = 0; j < cols;) { int k = j; - while (k < cols && buf[i][k]) + while (k < cols && buf[(i * cols) + k]) k++; if (k - j > max) { @@ -1541,7 +1540,7 @@ static int aspeed_spi_ast2600_calibrate(struct aspeed_spi_chip *chip, u32 hdiv, } } - calib_point = aspeed_spi_ast2600_optimized_timing(6, 17, calib_res); + calib_point = aspeed_spi_ast2600_optimized_timing(6, 17, &calib_res[0][0]); /* No good setting for this frequency */ if (calib_point == 0) return -1; From 84bfaa6ceed629cbaeaccf03c4b287c719663284 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig=20=28The=20Capable=20Hub=29?= Date: Mon, 18 May 2026 19:05:41 +0200 Subject: [PATCH 100/118] spi: Use named initializers for arrays of i2c_device_data MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While being less compact, using named initializers allows to more easily see which members of the structs are assigned which value without having to lookup the declaration of the struct. And it's also more robust against changes to the struct definition. The mentioned robustness is relevant for a planned change to struct i2c_device_id that replaces .driver_data by an anonymous union. While touching these arrays, drop a comma after the list terminator. This patch doesn't modify the compiled arrays, only their representation in source form benefits. The former was confirmed with x86 and arm64 builds. Signed-off-by: Uwe Kleine-König (The Capable Hub) Link: https://patch.msgid.link/20260518170542.807843-2-u.kleine-koenig@baylibre.com Signed-off-by: Mark Brown --- drivers/spi/spi-sc18is602.c | 6 +++--- drivers/spi/spi-xcomm.c | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/spi/spi-sc18is602.c b/drivers/spi/spi-sc18is602.c index 78c558e7228e..ae534ebd5e87 100644 --- a/drivers/spi/spi-sc18is602.c +++ b/drivers/spi/spi-sc18is602.c @@ -295,9 +295,9 @@ static int sc18is602_probe(struct i2c_client *client) } static const struct i2c_device_id sc18is602_id[] = { - { "sc18is602", sc18is602 }, - { "sc18is602b", sc18is602b }, - { "sc18is603", sc18is603 }, + { .name = "sc18is602", .driver_data = sc18is602 }, + { .name = "sc18is602b", .driver_data = sc18is602b }, + { .name = "sc18is603", .driver_data = sc18is603 }, { } }; MODULE_DEVICE_TABLE(i2c, sc18is602_id); diff --git a/drivers/spi/spi-xcomm.c b/drivers/spi/spi-xcomm.c index 130a3d716dd4..e40ec6ebe4e5 100644 --- a/drivers/spi/spi-xcomm.c +++ b/drivers/spi/spi-xcomm.c @@ -269,8 +269,8 @@ static int spi_xcomm_probe(struct i2c_client *i2c) } static const struct i2c_device_id spi_xcomm_ids[] = { - { "spi-xcomm" }, - { }, + { .name = "spi-xcomm" }, + { } }; MODULE_DEVICE_TABLE(i2c, spi_xcomm_ids); From 82056957e5c42c4060a1d8b1576aad2dfe54e568 Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Mon, 18 May 2026 17:43:52 -0700 Subject: [PATCH 101/118] spi: omap2-mcspi: Use of_device_get_match_data() Use of_device_get_match_data() to fetch platform match data directly instead of open-coding an of_match_device() lookup. This also lets the driver drop the of_device.h include. Assisted-by: Codex:GPT-5.5 Signed-off-by: Rosen Penev Link: https://patch.msgid.link/20260519004352.627148-1-rosenp@gmail.com Signed-off-by: Mark Brown --- drivers/spi/spi-omap2-mcspi.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/drivers/spi/spi-omap2-mcspi.c b/drivers/spi/spi-omap2-mcspi.c index d53f98aa0aac..9cc078acc13c 100644 --- a/drivers/spi/spi-omap2-mcspi.c +++ b/drivers/spi/spi-omap2-mcspi.c @@ -22,7 +22,6 @@ #include #include #include -#include #include #include @@ -1481,7 +1480,6 @@ static int omap2_mcspi_probe(struct platform_device *pdev) int status = 0, i; u32 regs_offset = 0; struct device_node *node = pdev->dev.of_node; - const struct of_device_id *match; if (of_property_read_bool(node, "spi-slave")) ctlr = devm_spi_alloc_target(&pdev->dev, sizeof(*mcspi)); @@ -1509,10 +1507,9 @@ static int omap2_mcspi_probe(struct platform_device *pdev) mcspi = spi_controller_get_devdata(ctlr); mcspi->ctlr = ctlr; - match = of_match_device(omap_mcspi_of_match, &pdev->dev); - if (match) { + pdata = of_device_get_match_data(&pdev->dev); + if (pdata) { u32 num_cs = 1; /* default number of chipselect */ - pdata = match->data; of_property_read_u32(node, "ti,spi-num-cs", &num_cs); ctlr->num_chipselect = num_cs; From bd7e9843ec95bffe2643c901dd625f0bab32e639 Mon Sep 17 00:00:00 2001 From: Felix Gu Date: Fri, 22 May 2026 20:40:48 +0800 Subject: [PATCH 102/118] spi: atmel: fix DMA channel and bounce buffer leaks The original code set use_dma to false when dma_alloc_coherent() for bounce buffers failed, but DMA channels acquired earlier via atmel_spi_configure_dma() were never freed. When devm_request_irq() or clk_prepare_enable() failed later in probe, the driver also did not release DMA channels or bounce buffers already allocated. The out_free_dma error path released DMA channels but did not free the bounce buffers. Fix by moving bounce buffer allocation into atmel_spi_configure_dma() and registering the devres cleanup for DMA channels and bounce buffers. Fixes: a9889ed62d06 ("spi: atmel: Implements transfers with bounce buffer") Signed-off-by: Felix Gu Link: https://patch.msgid.link/20260522-atmel-v3-1-23f8c6e6aa43@gmail.com Signed-off-by: Mark Brown --- drivers/spi/spi-atmel.c | 133 ++++++++++++++++++++-------------------- 1 file changed, 68 insertions(+), 65 deletions(-) diff --git a/drivers/spi/spi-atmel.c b/drivers/spi/spi-atmel.c index 25aa294631c8..c8012c82c3a7 100644 --- a/drivers/spi/spi-atmel.c +++ b/drivers/spi/spi-atmel.c @@ -559,6 +559,38 @@ static int atmel_spi_dma_slave_config(struct atmel_spi *as, u8 bits_per_word) return err; } +static void atmel_spi_release_dma(void *data) +{ + struct spi_controller *host = data; + struct atmel_spi *as = spi_controller_get_devdata(host); + struct device *dev = &as->pdev->dev; + + if (host->dma_tx) { + dma_release_channel(host->dma_tx); + host->dma_tx = NULL; + } + + if (host->dma_rx) { + dma_release_channel(host->dma_rx); + host->dma_rx = NULL; + } + + if (IS_ENABLED(CONFIG_SOC_SAM_V4_V5)) { + if (as->addr_tx_bbuf) { + dma_free_coherent(dev, SPI_MAX_DMA_XFER, + as->addr_tx_bbuf, + as->dma_addr_tx_bbuf); + as->addr_tx_bbuf = NULL; + } + if (as->addr_rx_bbuf) { + dma_free_coherent(dev, SPI_MAX_DMA_XFER, + as->addr_rx_bbuf, + as->dma_addr_rx_bbuf); + as->addr_rx_bbuf = NULL; + } + } +} + static int atmel_spi_configure_dma(struct spi_controller *host, struct atmel_spi *as) { @@ -569,7 +601,8 @@ static int atmel_spi_configure_dma(struct spi_controller *host, if (IS_ERR(host->dma_tx)) { err = PTR_ERR(host->dma_tx); dev_dbg(dev, "No TX DMA channel, DMA is disabled\n"); - goto error_clear; + host->dma_tx = NULL; + return err; } host->dma_rx = dma_request_chan(dev, "rx"); @@ -580,26 +613,45 @@ static int atmel_spi_configure_dma(struct spi_controller *host, * requested tx channel. */ dev_dbg(dev, "No RX DMA channel, DMA is disabled\n"); - goto error; + host->dma_rx = NULL; + goto err_release_dma; } err = atmel_spi_dma_slave_config(as, 8); if (err) - goto error; + goto err_release_dma; + + if (IS_ENABLED(CONFIG_SOC_SAM_V4_V5)) { + as->addr_tx_bbuf = dma_alloc_coherent(dev, SPI_MAX_DMA_XFER, + &as->dma_addr_tx_bbuf, + GFP_KERNEL | GFP_DMA); + if (!as->addr_tx_bbuf) { + err = -ENOMEM; + goto err_release_dma; + } + + as->addr_rx_bbuf = dma_alloc_coherent(dev, SPI_MAX_DMA_XFER, + &as->dma_addr_rx_bbuf, + GFP_KERNEL | GFP_DMA); + if (!as->addr_rx_bbuf) { + err = -ENOMEM; + goto err_release_dma; + } + } + + err = devm_add_action_or_reset(dev, atmel_spi_release_dma, host); + if (err) + return err; dev_info(&as->pdev->dev, - "Using %s (tx) and %s (rx) for DMA transfers\n", - dma_chan_name(host->dma_tx), - dma_chan_name(host->dma_rx)); + "Using %s (tx) and %s (rx) for DMA transfers\n", + dma_chan_name(host->dma_tx), dma_chan_name(host->dma_rx)); return 0; -error: - if (!IS_ERR(host->dma_rx)) - dma_release_channel(host->dma_rx); - if (!IS_ERR(host->dma_tx)) - dma_release_channel(host->dma_tx); -error_clear: - host->dma_tx = host->dma_rx = NULL; + +err_release_dma: + atmel_spi_release_dma(host); + return err; } @@ -611,18 +663,6 @@ static void atmel_spi_stop_dma(struct spi_controller *host) dmaengine_terminate_all(host->dma_tx); } -static void atmel_spi_release_dma(struct spi_controller *host) -{ - if (host->dma_rx) { - dma_release_channel(host->dma_rx); - host->dma_rx = NULL; - } - if (host->dma_tx) { - dma_release_channel(host->dma_tx); - host->dma_tx = NULL; - } -} - /* This function is called by the DMA driver from tasklet context */ static void dma_callback(void *data) { @@ -1581,30 +1621,6 @@ static int atmel_spi_probe(struct platform_device *pdev) as->use_pdc = true; } - if (IS_ENABLED(CONFIG_SOC_SAM_V4_V5)) { - as->addr_rx_bbuf = dma_alloc_coherent(&pdev->dev, - SPI_MAX_DMA_XFER, - &as->dma_addr_rx_bbuf, - GFP_KERNEL | GFP_DMA); - if (!as->addr_rx_bbuf) { - as->use_dma = false; - } else { - as->addr_tx_bbuf = dma_alloc_coherent(&pdev->dev, - SPI_MAX_DMA_XFER, - &as->dma_addr_tx_bbuf, - GFP_KERNEL | GFP_DMA); - if (!as->addr_tx_bbuf) { - as->use_dma = false; - dma_free_coherent(&pdev->dev, SPI_MAX_DMA_XFER, - as->addr_rx_bbuf, - as->dma_addr_rx_bbuf); - } - } - if (!as->use_dma) - dev_info(host->dev.parent, - " can not allocate dma coherent memory\n"); - } - if (as->caps.has_dma_support && !as->use_dma) dev_info(&pdev->dev, "Atmel SPI Controller using PIO only\n"); @@ -1664,13 +1680,10 @@ static int atmel_spi_probe(struct platform_device *pdev) out_free_dma: pm_runtime_disable(&pdev->dev); pm_runtime_set_suspended(&pdev->dev); - - if (as->use_dma) - atmel_spi_release_dma(host); - spi_writel(as, CR, SPI_BIT(SWRST)); spi_writel(as, CR, SPI_BIT(SWRST)); /* AT91SAM9263 Rev B workaround */ - clk_disable_unprepare(as->gclk); + if (as->gclk) + clk_disable_unprepare(as->gclk); out_disable_clk: clk_disable_unprepare(clk); @@ -1687,18 +1700,8 @@ static void atmel_spi_remove(struct platform_device *pdev) spi_unregister_controller(host); /* reset the hardware and block queue progress */ - if (as->use_dma) { + if (as->use_dma) atmel_spi_stop_dma(host); - atmel_spi_release_dma(host); - if (IS_ENABLED(CONFIG_SOC_SAM_V4_V5)) { - dma_free_coherent(&pdev->dev, SPI_MAX_DMA_XFER, - as->addr_tx_bbuf, - as->dma_addr_tx_bbuf); - dma_free_coherent(&pdev->dev, SPI_MAX_DMA_XFER, - as->addr_rx_bbuf, - as->dma_addr_rx_bbuf); - } - } spin_lock_irq(&as->lock); spi_writel(as, CR, SPI_BIT(SWRST)); From e703ce47691b967fe9b4057fb1d062273211afa9 Mon Sep 17 00:00:00 2001 From: Carlos Song Date: Mon, 25 May 2026 14:23:56 +0800 Subject: [PATCH 103/118] spi: fsl-lpspi: replace dmaengine_terminate_all() with dmaengine_terminate_sync() dmaengine_terminate_all() has been deprecated, so replace it with dmaengine_terminate_sync(). Fixes: 09c04466ce7e ("spi: lpspi: add dma mode support") Cc: stable@vger.kernel.org Signed-off-by: Carlos Song Link: https://patch.msgid.link/20260525062357.3191349-2-carlos.song@oss.nxp.com Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-lpspi.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/spi/spi-fsl-lpspi.c b/drivers/spi/spi-fsl-lpspi.c index e201309f8aae..1a94a42fac31 100644 --- a/drivers/spi/spi-fsl-lpspi.c +++ b/drivers/spi/spi-fsl-lpspi.c @@ -647,7 +647,7 @@ static int fsl_lpspi_dma_transfer(struct spi_controller *controller, tx->sgl, tx->nents, DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); if (!desc_tx) { - dmaengine_terminate_all(controller->dma_tx); + dmaengine_terminate_sync(controller->dma_tx); return -EINVAL; } @@ -668,8 +668,8 @@ static int fsl_lpspi_dma_transfer(struct spi_controller *controller, transfer_timeout); if (!time_left) { dev_err(fsl_lpspi->dev, "I/O Error in DMA TX\n"); - dmaengine_terminate_all(controller->dma_tx); - dmaengine_terminate_all(controller->dma_rx); + dmaengine_terminate_sync(controller->dma_tx); + dmaengine_terminate_sync(controller->dma_rx); fsl_lpspi_reset(fsl_lpspi); return -ETIMEDOUT; } @@ -678,8 +678,8 @@ static int fsl_lpspi_dma_transfer(struct spi_controller *controller, transfer_timeout); if (!time_left) { dev_err(fsl_lpspi->dev, "I/O Error in DMA RX\n"); - dmaengine_terminate_all(controller->dma_tx); - dmaengine_terminate_all(controller->dma_rx); + dmaengine_terminate_sync(controller->dma_tx); + dmaengine_terminate_sync(controller->dma_rx); fsl_lpspi_reset(fsl_lpspi); return -ETIMEDOUT; } @@ -688,8 +688,8 @@ static int fsl_lpspi_dma_transfer(struct spi_controller *controller, fsl_lpspi->target_aborted) { dev_dbg(fsl_lpspi->dev, "I/O Error in DMA TX interrupted\n"); - dmaengine_terminate_all(controller->dma_tx); - dmaengine_terminate_all(controller->dma_rx); + dmaengine_terminate_sync(controller->dma_tx); + dmaengine_terminate_sync(controller->dma_rx); fsl_lpspi_reset(fsl_lpspi); return -EINTR; } @@ -698,8 +698,8 @@ static int fsl_lpspi_dma_transfer(struct spi_controller *controller, fsl_lpspi->target_aborted) { dev_dbg(fsl_lpspi->dev, "I/O Error in DMA RX interrupted\n"); - dmaengine_terminate_all(controller->dma_tx); - dmaengine_terminate_all(controller->dma_rx); + dmaengine_terminate_sync(controller->dma_tx); + dmaengine_terminate_sync(controller->dma_rx); fsl_lpspi_reset(fsl_lpspi); return -EINTR; } From 01980b5da56e573d62798d0ff6c86bcaa2b22cbe Mon Sep 17 00:00:00 2001 From: Carlos Song Date: Mon, 25 May 2026 14:23:57 +0800 Subject: [PATCH 104/118] spi: fsl-lpspi: terminate the RX channel on TX prepare failure path When dmaengine_prep_slave_sg() fails for the TX channel, the error path terminates the TX DMA channel but leaves the RX channel running. Since the RX channel was already submitted and issued prior to preparing the TX descriptor, returning -EINVAL causes the SPI core to unmap the DMA buffers while the RX DMA engine continues writing to them, leading to potential memory corruption or use-after-free. Terminate the RX channel before returning on the TX prepare failure path. Fixes: 09c04466ce7e ("spi: lpspi: add dma mode support") Cc: stable@vger.kernel.org Signed-off-by: Carlos Song Link: https://patch.msgid.link/20260525062357.3191349-3-carlos.song@oss.nxp.com Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-lpspi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/spi/spi-fsl-lpspi.c b/drivers/spi/spi-fsl-lpspi.c index 1a94a42fac31..e14753144e19 100644 --- a/drivers/spi/spi-fsl-lpspi.c +++ b/drivers/spi/spi-fsl-lpspi.c @@ -647,7 +647,7 @@ static int fsl_lpspi_dma_transfer(struct spi_controller *controller, tx->sgl, tx->nents, DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); if (!desc_tx) { - dmaengine_terminate_sync(controller->dma_tx); + dmaengine_terminate_sync(controller->dma_rx); return -EINVAL; } From 4503b2fe761c2bfd33ed043d9b9deec0d1eb40e0 Mon Sep 17 00:00:00 2001 From: Carlos Song Date: Mon, 25 May 2026 14:29:28 +0800 Subject: [PATCH 105/118] spi: imx: replace dmaengine_terminate_all() with dmaengine_terminate_sync() dmaengine_terminate_all() has been deprecated, so replace it with dmaengine_terminate_sync(). Fixes: ba9b28652c75 ("spi: imx: enable DMA mode for target operation") Fixes: a450c8b77f92 ("spi: imx: handle DMA submission errors with dma_submit_error()") Signed-off-by: Carlos Song Link: https://patch.msgid.link/20260525062928.3191821-1-carlos.song@oss.nxp.com Signed-off-by: Mark Brown --- drivers/spi/spi-imx.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/spi/spi-imx.c b/drivers/spi/spi-imx.c index 480d1e8b281f..ae9912905c67 100644 --- a/drivers/spi/spi-imx.c +++ b/drivers/spi/spi-imx.c @@ -1774,8 +1774,8 @@ static int spi_imx_dma_submit(struct spi_imx_data *spi_imx, transfer_timeout); if (!time_left) { dev_err(spi_imx->dev, "I/O Error in DMA TX\n"); - dmaengine_terminate_all(controller->dma_tx); - dmaengine_terminate_all(controller->dma_rx); + dmaengine_terminate_sync(controller->dma_tx); + dmaengine_terminate_sync(controller->dma_rx); return -ETIMEDOUT; } @@ -1784,7 +1784,7 @@ static int spi_imx_dma_submit(struct spi_imx_data *spi_imx, if (!time_left) { dev_err(&controller->dev, "I/O Error in DMA RX\n"); spi_imx->devtype_data->reset(spi_imx); - dmaengine_terminate_all(controller->dma_rx); + dmaengine_terminate_sync(controller->dma_rx); return -ETIMEDOUT; } } else { @@ -1793,15 +1793,15 @@ static int spi_imx_dma_submit(struct spi_imx_data *spi_imx, if (wait_for_completion_interruptible(&spi_imx->dma_tx_completion) || READ_ONCE(spi_imx->target_aborted)) { dev_dbg(spi_imx->dev, "I/O Error in DMA TX interrupted\n"); - dmaengine_terminate_all(controller->dma_tx); - dmaengine_terminate_all(controller->dma_rx); + dmaengine_terminate_sync(controller->dma_tx); + dmaengine_terminate_sync(controller->dma_rx); return -EINTR; } if (wait_for_completion_interruptible(&spi_imx->dma_rx_completion) || READ_ONCE(spi_imx->target_aborted)) { dev_dbg(spi_imx->dev, "I/O Error in DMA RX interrupted\n"); - dmaengine_terminate_all(controller->dma_rx); + dmaengine_terminate_sync(controller->dma_rx); return -EINTR; } @@ -1818,9 +1818,9 @@ static int spi_imx_dma_submit(struct spi_imx_data *spi_imx, return 0; dmaengine_terminate_tx: - dmaengine_terminate_all(controller->dma_tx); + dmaengine_terminate_sync(controller->dma_tx); dmaengine_terminate_rx: - dmaengine_terminate_all(controller->dma_rx); + dmaengine_terminate_sync(controller->dma_rx); return -EINVAL; } From f469138a77ac5ab685dfe15dfed7dccb9d5c33e5 Mon Sep 17 00:00:00 2001 From: Aaron Kling Date: Mon, 25 May 2026 01:47:44 -0500 Subject: [PATCH 106/118] spi: tegra210-quad: Allocate DMA memory for DMA engine When the SPI controllers are running in DMA mode, it is the DMA engine that performs the memory accesses rather than the SPI controller. Pass the DMA engine's struct device pointer to the DMA API to make sure the correct DMA operations are used. Suggested-by: Thierry Reding Signed-off-by: Aaron Kling Link: https://patch.msgid.link/20260525-tegra194-qspi-iommu-v2-1-a11c53f804b2@gmail.com Signed-off-by: Mark Brown --- drivers/spi/spi-tegra210-quad.c | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/drivers/spi/spi-tegra210-quad.c b/drivers/spi/spi-tegra210-quad.c index db28dd556484..588a929a9785 100644 --- a/drivers/spi/spi-tegra210-quad.c +++ b/drivers/spi/spi-tegra210-quad.c @@ -226,11 +226,13 @@ struct tegra_qspi { struct completion xfer_completion; struct spi_transfer *curr_xfer; + struct device *rx_dma_dev; struct dma_chan *rx_dma_chan; u32 *rx_dma_buf; dma_addr_t rx_dma_phys; struct dma_async_tx_descriptor *rx_dma_desc; + struct device *tx_dma_dev; struct dma_chan *tx_dma_chan; u32 *tx_dma_buf; dma_addr_t tx_dma_phys; @@ -574,15 +576,15 @@ static int tegra_qspi_dma_map_xfer(struct tegra_qspi *tqspi, struct spi_transfer len = DIV_ROUND_UP(tqspi->curr_dma_words * tqspi->bytes_per_word, 4) * 4; if (t->tx_buf) { - t->tx_dma = dma_map_single(tqspi->dev, (void *)tx_buf, len, DMA_TO_DEVICE); - if (dma_mapping_error(tqspi->dev, t->tx_dma)) + t->tx_dma = dma_map_single(tqspi->tx_dma_dev, (void *)tx_buf, len, DMA_TO_DEVICE); + if (dma_mapping_error(tqspi->tx_dma_dev, t->tx_dma)) return -ENOMEM; } if (t->rx_buf) { - t->rx_dma = dma_map_single(tqspi->dev, (void *)rx_buf, len, DMA_FROM_DEVICE); - if (dma_mapping_error(tqspi->dev, t->rx_dma)) { - dma_unmap_single(tqspi->dev, t->tx_dma, len, DMA_TO_DEVICE); + t->rx_dma = dma_map_single(tqspi->rx_dma_dev, (void *)rx_buf, len, DMA_FROM_DEVICE); + if (dma_mapping_error(tqspi->rx_dma_dev, t->rx_dma)) { + dma_unmap_single(tqspi->tx_dma_dev, t->tx_dma, len, DMA_TO_DEVICE); return -ENOMEM; } } @@ -597,9 +599,9 @@ static void tegra_qspi_dma_unmap_xfer(struct tegra_qspi *tqspi, struct spi_trans len = DIV_ROUND_UP(tqspi->curr_dma_words * tqspi->bytes_per_word, 4) * 4; if (t->tx_buf) - dma_unmap_single(tqspi->dev, t->tx_dma, len, DMA_TO_DEVICE); + dma_unmap_single(tqspi->tx_dma_dev, t->tx_dma, len, DMA_TO_DEVICE); if (t->rx_buf) - dma_unmap_single(tqspi->dev, t->rx_dma, len, DMA_FROM_DEVICE); + dma_unmap_single(tqspi->rx_dma_dev, t->rx_dma, len, DMA_FROM_DEVICE); } static int tegra_qspi_start_dma_based_transfer(struct tegra_qspi *tqspi, struct spi_transfer *t) @@ -745,7 +747,7 @@ static int tegra_qspi_start_cpu_based_transfer(struct tegra_qspi *qspi, struct s static void tegra_qspi_deinit_dma(struct tegra_qspi *tqspi) { if (tqspi->tx_dma_buf) { - dma_free_coherent(tqspi->dev, tqspi->dma_buf_size, + dma_free_coherent(tqspi->tx_dma_dev, tqspi->dma_buf_size, tqspi->tx_dma_buf, tqspi->tx_dma_phys); tqspi->tx_dma_buf = NULL; } @@ -756,7 +758,7 @@ static void tegra_qspi_deinit_dma(struct tegra_qspi *tqspi) } if (tqspi->rx_dma_buf) { - dma_free_coherent(tqspi->dev, tqspi->dma_buf_size, + dma_free_coherent(tqspi->rx_dma_dev, tqspi->dma_buf_size, tqspi->rx_dma_buf, tqspi->rx_dma_phys); tqspi->rx_dma_buf = NULL; } @@ -782,6 +784,7 @@ static int tegra_qspi_init_dma(struct tegra_qspi *tqspi) } tqspi->rx_dma_chan = dma_chan; + tqspi->rx_dma_dev = dmaengine_get_dma_device(tqspi->rx_dma_chan); dma_chan = dma_request_chan(tqspi->dev, "tx"); if (IS_ERR(dma_chan)) { @@ -790,15 +793,19 @@ static int tegra_qspi_init_dma(struct tegra_qspi *tqspi) } tqspi->tx_dma_chan = dma_chan; + tqspi->tx_dma_dev = dmaengine_get_dma_device(tqspi->tx_dma_chan); } else { if (!device_iommu_mapped(tqspi->dev)) { dev_warn(tqspi->dev, "IOMMU not enabled in device-tree, falling back to PIO mode\n"); return 0; } + + tqspi->rx_dma_dev = tqspi->dev; + tqspi->tx_dma_dev = tqspi->dev; } - dma_buf = dma_alloc_coherent(tqspi->dev, tqspi->dma_buf_size, &dma_phys, GFP_KERNEL); + dma_buf = dma_alloc_coherent(tqspi->rx_dma_dev, tqspi->dma_buf_size, &dma_phys, GFP_KERNEL); if (!dma_buf) { err = -ENOMEM; goto err_out; @@ -807,7 +814,7 @@ static int tegra_qspi_init_dma(struct tegra_qspi *tqspi) tqspi->rx_dma_buf = dma_buf; tqspi->rx_dma_phys = dma_phys; - dma_buf = dma_alloc_coherent(tqspi->dev, tqspi->dma_buf_size, &dma_phys, GFP_KERNEL); + dma_buf = dma_alloc_coherent(tqspi->tx_dma_dev, tqspi->dma_buf_size, &dma_phys, GFP_KERNEL); if (!dma_buf) { err = -ENOMEM; goto err_out; From 4954d4eca469419339452cb5fea26dd0fc678c54 Mon Sep 17 00:00:00 2001 From: Jisheng Zhang Date: Wed, 3 Jun 2026 07:58:25 +0800 Subject: [PATCH 107/118] spi: cadence-xspi: Support 32bit and 64bit slave dma interface The cdns xspi controller slave dma interface may support wider data width. Wider I/O width can benefit performance. We can know the width by checking the CTRL_FEATURES_REG's DMA_DATA_WIDTH bit, 0 means 32bit 1 means 64bit. A simple test with QSPI nor flash on one arm64 platform: Use 8bit slave dma data width (now): # dd if=/dev/mtdblock0 of=/dev/null bs=8192 count=1000 1000+0 records in 1000+0 records out 8192000 bytes (7.8MB) copied, 1.368735 seconds, 5.7MB/s Use 32bit slave dma data width: # dd if=/dev/mtdblock0 of=/dev/null bs=8192 count=1000 1000+0 records in 1000+0 records out 8192000 bytes (7.8MB) copied, 1.088787 seconds, 7.2MB/s Improved by 26.3%! Use 64bit slave dma data width: # dd if=/dev/mtdblock0 of=/dev/null bs=8192 count=1000 1000+0 records in 1000+0 records out 8192000 bytes (7.8MB) copied, 0.831104 seconds, 9.4MB/s Improved by 64.9%! Signed-off-by: Jisheng Zhang Link: https://patch.msgid.link/20260602235825.28614-1-jszhang@kernel.org Signed-off-by: Mark Brown --- drivers/spi/spi-cadence-xspi.c | 53 +++++++++++++++++++++++++++++++--- 1 file changed, 49 insertions(+), 4 deletions(-) diff --git a/drivers/spi/spi-cadence-xspi.c b/drivers/spi/spi-cadence-xspi.c index c45b29c043bf..e2bfb0c78b82 100644 --- a/drivers/spi/spi-cadence-xspi.c +++ b/drivers/spi/spi-cadence-xspi.c @@ -373,6 +373,8 @@ struct cdns_xspi_dev { void *in_buffer; const void *out_buffer; + /* Slave DMA data width in bytes (4 or 8). */ + u8 dma_data_width; u8 hw_num_banks; @@ -576,11 +578,56 @@ static int cdns_xspi_controller_init(struct cdns_xspi_dev *cdns_xspi) ctrl_features = readl(cdns_xspi->iobase + CDNS_XSPI_CTRL_FEATURES_REG); cdns_xspi->hw_num_banks = FIELD_GET(CDNS_XSPI_NUM_BANKS, ctrl_features); + cdns_xspi->dma_data_width = (ctrl_features & CDNS_XSPI_DMA_DATA_WIDTH) ? 8 : 4; cdns_xspi->set_interrupts_handler(cdns_xspi, false); return 0; } +static inline void cdns_xspi_sdma_read(struct cdns_xspi_dev *cdns_xspi, size_t len) +{ + void __iomem *src = cdns_xspi->sdmabase; + void *buf = cdns_xspi->in_buffer; + size_t offset = 0; + + if (cdns_xspi->dma_data_width == 4) { + if (IS_ALIGNED((uintptr_t)src, 4) && IS_ALIGNED((uintptr_t)buf, 4)) { + ioread32_rep(src, buf, len >> 2); + offset = len & ~0x3; + len -= offset; + } + } else { + if (IS_ALIGNED((uintptr_t)src, 8) && IS_ALIGNED((uintptr_t)buf, 8)) { + readsq(src, buf, len >> 3); + offset = len & ~0x7; + len -= offset; + } + } + ioread8_rep(src, (u8 *)buf + offset, len); +} + +static inline void cdns_xspi_sdma_write(struct cdns_xspi_dev *cdns_xspi, size_t len) +{ + void __iomem *dst = cdns_xspi->sdmabase; + const void *buf = cdns_xspi->out_buffer; + size_t offset = 0; + + if (cdns_xspi->dma_data_width == 4) { + if (IS_ALIGNED((uintptr_t)dst, 4) && IS_ALIGNED((uintptr_t)buf, 4)) { + iowrite32_rep(dst, buf, len >> 2); + offset = len & ~0x3; + len -= offset; + } + } else { + if (IS_ALIGNED((uintptr_t)dst, 8) && IS_ALIGNED((uintptr_t)buf, 8)) { + writesq(dst, buf, len >> 3); + offset = len & ~0x7; + len -= offset; + } + } + iowrite8_rep(dst, (const u8 *)buf + offset, len); +} + static void cdns_xspi_sdma_handle(struct cdns_xspi_dev *cdns_xspi) { u32 sdma_size, sdma_trd_info; @@ -592,13 +639,11 @@ static void cdns_xspi_sdma_handle(struct cdns_xspi_dev *cdns_xspi) switch (sdma_dir) { case CDNS_XSPI_SDMA_DIR_READ: - ioread8_rep(cdns_xspi->sdmabase, - cdns_xspi->in_buffer, sdma_size); + cdns_xspi_sdma_read(cdns_xspi, sdma_size); break; case CDNS_XSPI_SDMA_DIR_WRITE: - iowrite8_rep(cdns_xspi->sdmabase, - cdns_xspi->out_buffer, sdma_size); + cdns_xspi_sdma_write(cdns_xspi, sdma_size); break; } } From 2fb242e4f375b682c800ab40dd4fbbcde1711528 Mon Sep 17 00:00:00 2001 From: Nathan Chancellor Date: Sat, 6 Jun 2026 15:26:04 -0700 Subject: [PATCH 108/118] spi: cadence-xspi: Revert COMPILE_TEST support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit 0c5b5c40dc31 ("spi: cadence-xspi: Add COMPILE_TEST support") allows this driver to be built for 32-bit platforms, which causes a semantic conflict with commit 4954d4eca469 ("spi: cadence-xspi: Support 32bit and 64bit slave dma interface"), as readsq() and writesq() are only available when targeting 64-bit platforms: drivers/spi/spi-cadence-xspi.c: In function 'cdns_xspi_sdma_read': drivers/spi/spi-cadence-xspi.c:601:25: error: implicit declaration of function 'readsq'; did you mean 'readsl'? [-Wimplicit-function-declaration] 601 | readsq(src, buf, len >> 3); | ^~~~~~ | readsl drivers/spi/spi-cadence-xspi.c: In function 'cdns_xspi_sdma_write': drivers/spi/spi-cadence-xspi.c:623:25: error: implicit declaration of function 'writesq'; did you mean 'writesl'? [-Wimplicit-function-declaration] 623 | writesq(dst, buf, len >> 3); | ^~~~~~~ | writesl As there are no known 32-bit platforms that use this controller, revert compile testing support to restrict the driver to 64-bit platforms to avoid burdening the driver with workarounds. Signed-off-by: Nathan Chancellor Fixes: 4954d4eca469 ("spi: cadence-xspi: Support 32bit and 64bit slave dma interface") Acked-by: Uwe Kleine-König Link: https://patch.msgid.link/20260606-spi-cadence-xspi-revert-compile-testing-v1-1-76219ea378bd@kernel.org Signed-off-by: Mark Brown --- drivers/spi/Kconfig | 3 +-- drivers/spi/spi-cadence-xspi.c | 4 ---- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 957c3e065b83..8782514bb89b 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -321,8 +321,7 @@ config SPI_CADENCE_QUADSPI config SPI_CADENCE_XSPI tristate "Cadence XSPI controller" - depends on HAS_IOMEM || COMPILE_TEST - depends on OF + depends on OF && HAS_IOMEM && 64BIT depends on SPI_MEM help Enable support for the Cadence XSPI Flash controller. diff --git a/drivers/spi/spi-cadence-xspi.c b/drivers/spi/spi-cadence-xspi.c index e2bfb0c78b82..e1b337789fce 100644 --- a/drivers/spi/spi-cadence-xspi.c +++ b/drivers/spi/spi-cadence-xspi.c @@ -22,10 +22,6 @@ #include #include -#ifndef CONFIG_64BIT -#include -#endif - #define CDNS_XSPI_MAGIC_NUM_VALUE 0x6522 #define CDNS_XSPI_MAX_BANKS 8 #define CDNS_XSPI_NAME "cadence-xspi" From 7886054b06f762f62054957a8f6de0f14e6b7541 Mon Sep 17 00:00:00 2001 From: Felix Gu Date: Fri, 29 May 2026 23:31:06 +0800 Subject: [PATCH 109/118] spi: ep93xx: fix double-free of zeropage on DMA setup failure If DMA setup fails after allocating the zeropage, the error path frees the page but leaves espi->zeropage dangling. A subsequent call to ep93xx_spi_release_dma() sees the non-NULL pointer and frees the page again. Clear the pointer after freeing in the error path of ep93xx_spi_setup_dma(). Fixes: 626a96db1169 ("spi/ep93xx: add DMA support") Signed-off-by: Felix Gu Link: https://patch.msgid.link/20260529-ep93xx-v1-1-9185070ca1fc@gmail.com Signed-off-by: Mark Brown --- drivers/spi/spi-ep93xx.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/spi/spi-ep93xx.c b/drivers/spi/spi-ep93xx.c index ea610b0537a9..bf389d7590d3 100644 --- a/drivers/spi/spi-ep93xx.c +++ b/drivers/spi/spi-ep93xx.c @@ -600,6 +600,7 @@ static int ep93xx_spi_setup_dma(struct device *dev, struct ep93xx_spi *espi) espi->dma_rx = NULL; fail_free_page: free_page((unsigned long)espi->zeropage); + espi->zeropage = NULL; return ret; } From af4310929afcd500df6b75a5fccff85ce13cc90d Mon Sep 17 00:00:00 2001 From: Felix Gu Date: Sat, 30 May 2026 02:54:31 +0800 Subject: [PATCH 110/118] spi: dw-pci: remove redundant pci_free_irq_vectors() calls The driver uses pcim_enable_device(), so IRQ vectors are automatically freed by devres on driver detach. The explicit pci_free_irq_vectors() calls in the probe error path and remove function are redundant. Drop them and the now-unused error label. Signed-off-by: Felix Gu Link: https://patch.msgid.link/20260530-dw-pci-v1-1-5d2cf798b3c3@gmail.com Signed-off-by: Mark Brown --- drivers/spi/spi-dw-pci.c | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/drivers/spi/spi-dw-pci.c b/drivers/spi/spi-dw-pci.c index 7f002d5e5b88..bfb874f96a26 100644 --- a/drivers/spi/spi-dw-pci.c +++ b/drivers/spi/spi-dw-pci.c @@ -120,16 +120,15 @@ static int dw_spi_pci_probe(struct pci_dev *pdev, const struct pci_device_id *en if (desc->setup) { ret = desc->setup(dws); if (ret) - goto err_free_irq_vectors; + return ret; } } else { - ret = -ENODEV; - goto err_free_irq_vectors; + return -ENODEV; } ret = dw_spi_add_controller(&pdev->dev, dws); if (ret) - goto err_free_irq_vectors; + return ret; /* PCI hook and SPI hook use the same drv data */ pci_set_drvdata(pdev, dws); @@ -143,10 +142,6 @@ static int dw_spi_pci_probe(struct pci_dev *pdev, const struct pci_device_id *en pm_runtime_allow(&pdev->dev); return 0; - -err_free_irq_vectors: - pci_free_irq_vectors(pdev); - return ret; } static void dw_spi_pci_remove(struct pci_dev *pdev) @@ -157,7 +152,6 @@ static void dw_spi_pci_remove(struct pci_dev *pdev) pm_runtime_get_noresume(&pdev->dev); dw_spi_remove_controller(dws); - pci_free_irq_vectors(pdev); } #ifdef CONFIG_PM_SLEEP From 2cf4ad412f90f54597be95a6ce297d016d2fbef9 Mon Sep 17 00:00:00 2001 From: Tommaso Merciai Date: Mon, 8 Jun 2026 22:25:08 +0200 Subject: [PATCH 111/118] spi: rzv2h-rspi: Add suspend/resume support Add suspend/resume support to the rzv2h-rspi driver by implementing suspend and resume callbacks that delegate to spi_controller_suspend() and spi_controller_resume() respectively. Signed-off-by: Tommaso Merciai Link: https://patch.msgid.link/20260608202509.3651345-1-tommaso.merciai.xr@bp.renesas.com Signed-off-by: Mark Brown --- drivers/spi/spi-rzv2h-rspi.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/drivers/spi/spi-rzv2h-rspi.c b/drivers/spi/spi-rzv2h-rspi.c index 1655efda7d20..694e5305c638 100644 --- a/drivers/spi/spi-rzv2h-rspi.c +++ b/drivers/spi/spi-rzv2h-rspi.c @@ -802,6 +802,23 @@ static int rzv2h_rspi_probe(struct platform_device *pdev) return ret; } +static int rzv2h_rspi_suspend(struct device *dev) +{ + struct rzv2h_rspi_priv *rspi = dev_get_drvdata(dev); + + return spi_controller_suspend(rspi->controller); +} + +static int rzv2h_rspi_resume(struct device *dev) +{ + struct rzv2h_rspi_priv *rspi = dev_get_drvdata(dev); + + return spi_controller_resume(rspi->controller); +} + +static DEFINE_SIMPLE_DEV_PM_OPS(rzv2h_rspi_pm_ops, rzv2h_rspi_suspend, + rzv2h_rspi_resume); + static const struct rzv2h_rspi_info rzv2h_info = { .find_tclk_rate = rzv2h_rspi_find_rate_fixed, .tclk_name = "tclk", @@ -837,6 +854,7 @@ static struct platform_driver rzv2h_rspi_drv = { .driver = { .name = "rzv2h_rspi", .of_match_table = rzv2h_rspi_match, + .pm = pm_sleep_ptr(&rzv2h_rspi_pm_ops), }, }; module_platform_driver(rzv2h_rspi_drv); From fc82dda1dcc61a4e48c1511d0a3beefc9dc77312 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig=20=28The=20Capable=20Hub=29?= Date: Thu, 4 Jun 2026 22:55:26 +0200 Subject: [PATCH 112/118] spi: Use named initializers for platform_device_id arrays MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Named initializers are better readable and more robust to changes of the struct definition. This robustness is relevant for a planned change to struct platform_device_id replacing .driver_data by an anonymous union. While touching these arrays unify spacing and usage of commas. Signed-off-by: Uwe Kleine-König (The Capable Hub) Link: https://patch.msgid.link/3fcd432a505bb1bb7f8ef0fba9162243200b3347.1780606153.git.u.kleine-koenig@baylibre.com Signed-off-by: Mark Brown --- drivers/spi/spi-altera-platform.c | 4 ++-- drivers/spi/spi-bcm63xx.c | 3 +-- drivers/spi/spi-cs42l43.c | 4 ++-- drivers/spi/spi-rspi.c | 4 ++-- drivers/spi/spi-s3c64xx.c | 6 +++--- drivers/spi/spi-sh-msiof.c | 4 ++-- 6 files changed, 12 insertions(+), 13 deletions(-) diff --git a/drivers/spi/spi-altera-platform.c b/drivers/spi/spi-altera-platform.c index 3ee5d3480bb4..3de7df73f216 100644 --- a/drivers/spi/spi-altera-platform.c +++ b/drivers/spi/spi-altera-platform.c @@ -139,8 +139,8 @@ MODULE_DEVICE_TABLE(of, altera_spi_match); #endif /* CONFIG_OF */ static const struct platform_device_id altera_spi_ids[] = { - { DRV_NAME, ALTERA_SPI_TYPE_UNKNOWN }, - { "subdev_spi_altera", ALTERA_SPI_TYPE_SUBDEV }, + { .name = DRV_NAME, .driver_data = ALTERA_SPI_TYPE_UNKNOWN }, + { .name = "subdev_spi_altera", .driver_data = ALTERA_SPI_TYPE_SUBDEV }, { } }; MODULE_DEVICE_TABLE(platform, altera_spi_ids); diff --git a/drivers/spi/spi-bcm63xx.c b/drivers/spi/spi-bcm63xx.c index f8cfe535b2a3..43d7b54e3ae8 100644 --- a/drivers/spi/spi-bcm63xx.c +++ b/drivers/spi/spi-bcm63xx.c @@ -477,8 +477,7 @@ static const struct platform_device_id bcm63xx_spi_dev_match[] = { .name = "bcm6358-spi", .driver_data = (unsigned long)bcm6358_spi_reg_offsets, }, - { - }, + { } }; MODULE_DEVICE_TABLE(platform, bcm63xx_spi_dev_match); diff --git a/drivers/spi/spi-cs42l43.c b/drivers/spi/spi-cs42l43.c index 68f208ef1e01..6961e36b89d1 100644 --- a/drivers/spi/spi-cs42l43.c +++ b/drivers/spi/spi-cs42l43.c @@ -438,8 +438,8 @@ static int cs42l43_spi_probe(struct platform_device *pdev) } static const struct platform_device_id cs42l43_spi_id_table[] = { - { "cs42l43-spi", }, - {} + { .name = "cs42l43-spi" }, + { } }; MODULE_DEVICE_TABLE(platform, cs42l43_spi_id_table); diff --git a/drivers/spi/spi-rspi.c b/drivers/spi/spi-rspi.c index a0c77e02bc90..38df676774ee 100644 --- a/drivers/spi/spi-rspi.c +++ b/drivers/spi/spi-rspi.c @@ -1377,8 +1377,8 @@ static int rspi_probe(struct platform_device *pdev) } static const struct platform_device_id spi_driver_ids[] = { - { "rspi", (kernel_ulong_t)&rspi_ops }, - {}, + { .name = "rspi", .driver_data = (kernel_ulong_t)&rspi_ops }, + { } }; MODULE_DEVICE_TABLE(platform, spi_driver_ids); diff --git a/drivers/spi/spi-s3c64xx.c b/drivers/spi/spi-s3c64xx.c index 37176e557099..28c56b06fa99 100644 --- a/drivers/spi/spi-s3c64xx.c +++ b/drivers/spi/spi-s3c64xx.c @@ -1613,10 +1613,10 @@ static const struct s3c64xx_spi_port_config gs101_spi_port_config = { static const struct platform_device_id s3c64xx_spi_driver_ids[] = { { - .name = "s3c6410-spi", - .driver_data = (kernel_ulong_t)&s3c6410_spi_port_config, + .name = "s3c6410-spi", + .driver_data = (kernel_ulong_t)&s3c6410_spi_port_config, }, - { }, + { } }; MODULE_DEVICE_TABLE(platform, s3c64xx_spi_driver_ids); diff --git a/drivers/spi/spi-sh-msiof.c b/drivers/spi/spi-sh-msiof.c index 070e16bc764f..f23db85a1889 100644 --- a/drivers/spi/spi-sh-msiof.c +++ b/drivers/spi/spi-sh-msiof.c @@ -1310,8 +1310,8 @@ static void sh_msiof_spi_remove(struct platform_device *pdev) } static const struct platform_device_id spi_driver_ids[] = { - { "spi_sh_msiof", (kernel_ulong_t)&sh_data }, - {}, + { .name = "spi_sh_msiof", .driver_data = (kernel_ulong_t)&sh_data }, + { } }; MODULE_DEVICE_TABLE(platform, spi_driver_ids); From 606c0826bd90384a54571c0c5475ca41f50164ea Mon Sep 17 00:00:00 2001 From: Ruoyu Wang Date: Tue, 9 Jun 2026 13:26:47 +0800 Subject: [PATCH 113/118] spi: meson-spifc: fix runtime PM leak on remove pm_runtime_get_sync() increments the runtime PM usage counter even when it returns an error. meson_spifc_remove() uses it to resume the controller before disabling runtime PM, but never drops the usage counter again. Balance the get with pm_runtime_put_noidle() after disabling runtime PM, matching the teardown pattern used by other SPI controller drivers. Found by static analysis. I do not have hardware to test this. Fixes: c3e4bc5434d2 ("spi: meson: Add support for Amlogic Meson SPIFC") Signed-off-by: Ruoyu Wang Link: https://patch.msgid.link/20260609052647.5-1-ruoyuw560@gmail.com Signed-off-by: Mark Brown --- drivers/spi/spi-meson-spifc.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/spi/spi-meson-spifc.c b/drivers/spi/spi-meson-spifc.c index d700fa315223..e36aa94bbdae 100644 --- a/drivers/spi/spi-meson-spifc.c +++ b/drivers/spi/spi-meson-spifc.c @@ -345,6 +345,7 @@ static void meson_spifc_remove(struct platform_device *pdev) { pm_runtime_get_sync(&pdev->dev); pm_runtime_disable(&pdev->dev); + pm_runtime_put_noidle(&pdev->dev); } #ifdef CONFIG_PM_SLEEP From 3da90b29241d5a08e689e87c2c76712a63acfb47 Mon Sep 17 00:00:00 2001 From: Tomer Maimon Date: Tue, 9 Jun 2026 19:39:19 +0300 Subject: [PATCH 114/118] spi: dt-bindings: nuvoton,npcm750-fiu: Convert to DT schema Convert the Nuvoton NPCM FIU binding to DT schema format. Document the required control registers and the optional direct- mapped flash window separately, matching the driver behavior when the direct mapping is not described. Signed-off-by: Tomer Maimon Reviewed-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20260609163919.3321228-4-tmaimon77@gmail.com Signed-off-by: Mark Brown --- .../bindings/spi/nuvoton,npcm-fiu.txt | 58 ------------ .../bindings/spi/nuvoton,npcm750-fiu.yaml | 93 +++++++++++++++++++ 2 files changed, 93 insertions(+), 58 deletions(-) delete mode 100644 Documentation/devicetree/bindings/spi/nuvoton,npcm-fiu.txt create mode 100644 Documentation/devicetree/bindings/spi/nuvoton,npcm750-fiu.yaml diff --git a/Documentation/devicetree/bindings/spi/nuvoton,npcm-fiu.txt b/Documentation/devicetree/bindings/spi/nuvoton,npcm-fiu.txt deleted file mode 100644 index fb38e96d395f..000000000000 --- a/Documentation/devicetree/bindings/spi/nuvoton,npcm-fiu.txt +++ /dev/null @@ -1,58 +0,0 @@ -* Nuvoton FLASH Interface Unit (FIU) SPI Controller - -NPCM FIU supports single, dual and quad communication interface. - -The NPCM7XX supports three FIU modules, -FIU0 and FIUx supports two chip selects, -FIU3 support four chip select. - -The NPCM8XX supports four FIU modules, -FIU0 and FIUx supports two chip selects, -FIU1 and FIU3 supports four chip selects. - -Required properties: - - compatible : "nuvoton,npcm750-fiu" for Poleg NPCM7XX BMC - "nuvoton,npcm845-fiu" for Arbel NPCM8XX BMC - - #address-cells : should be 1. - - #size-cells : should be 0. - - reg : the first contains the register location and length, - the second contains the memory mapping address and length - - reg-names: Should contain the reg names "control" and "memory" - - clocks : phandle of FIU reference clock. - -Required properties in case the pins can be muxed: - - pinctrl-names : a pinctrl state named "default" must be defined. - - pinctrl-0 : phandle referencing pin configuration of the device. - -Optional property: - - nuvoton,spix-mode: enable spix-mode for an expansion bus to an ASIC or CPLD. - -Aliases: -- All the FIU controller nodes should be represented in the aliases node using - the following format 'fiu{n}' where n is a unique number for the alias. - In the NPCM7XX BMC: - fiu0 represent fiu 0 controller - fiu1 represent fiu 3 controller - fiu2 represent fiu x controller - - In the NPCM8XX BMC: - fiu0 represent fiu 0 controller - fiu1 represent fiu 1 controller - fiu2 represent fiu 3 controller - fiu3 represent fiu x controller - -Example: -fiu3: spi@c00000000 { - compatible = "nuvoton,npcm750-fiu"; - #address-cells = <1>; - #size-cells = <0>; - reg = <0xfb000000 0x1000>, <0x80000000 0x10000000>; - reg-names = "control", "memory"; - clocks = <&clk NPCM7XX_CLK_AHB>; - pinctrl-names = "default"; - pinctrl-0 = <&spi3_pins>; - flash@0 { - ... - }; -}; - diff --git a/Documentation/devicetree/bindings/spi/nuvoton,npcm750-fiu.yaml b/Documentation/devicetree/bindings/spi/nuvoton,npcm750-fiu.yaml new file mode 100644 index 000000000000..965904a98785 --- /dev/null +++ b/Documentation/devicetree/bindings/spi/nuvoton,npcm750-fiu.yaml @@ -0,0 +1,93 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/spi/nuvoton,npcm750-fiu.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Nuvoton NPCM Flash Interface Unit (FIU) SPI Controller + +maintainers: + - Tomer Maimon + +allOf: + - $ref: /schemas/spi/spi-controller.yaml# + +description: | + NPCM FIU supports single, dual and quad communication interface. + + The NPCM7XX supports three FIU modules: + FIU0 and FIUx support two chip selects + FIU3 supports four chip selects. + + The NPCM8XX supports four FIU modules: + FIU0 and FIUx support two chip selects + FIU1 and FIU3 support four chip selects. + + The FIU control register block is always required. The direct-mapped + flash window is optional because the controller can still access flash + through the UMA path when that mapping is not described. + + Alias convention: + The '/aliases' node should define: + For NPCM7xx: fiu0=&fiu0; fiu1=&fiu3; fiu2=&fiux; + For NPCM8xx: fiu0=&fiu0; fiu1=&fiu3; fiu2=&fiux; fiu3=&fiu1; + +properties: + compatible: + enum: + - nuvoton,npcm750-fiu # Poleg NPCM7XX + - nuvoton,npcm845-fiu # Arbel NPCM8XX + + reg: + description: + The first resource is the FIU control register block. An optional second + resource describes the direct-mapped flash window used for direct + read/write accesses. + minItems: 1 + items: + - description: FIU control registers + - description: Memory-mapped flash contents + + reg-names: + description: + Resource names for the control registers and optional direct-mapped + flash window. + minItems: 1 + items: + - const: control + - const: memory + + clocks: + maxItems: 1 + description: FIU reference clock. + + nuvoton,spix-mode: + type: boolean + description: Enable SPIX mode for an expansion bus to an ASIC or CPLD. + +required: + - compatible + - reg + - reg-names + - clocks + +unevaluatedProperties: false + +examples: + - | + #include + spi@fb000000 { + compatible = "nuvoton,npcm750-fiu"; + reg = <0xfb000000 0x1000>, <0x80000000 0x10000000>; + reg-names = "control", "memory"; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&clk NPCM7XX_CLK_SPI0>; + + flash@0 { + compatible = "jedec,spi-nor"; + reg = <0>; + #address-cells = <1>; + #size-cells = <1>; + }; + }; From 0f95264f49ace739d411fd9149e2b3545d741d06 Mon Sep 17 00:00:00 2001 From: Vadim Fedorenko Date: Wed, 10 Jun 2026 22:28:43 +0000 Subject: [PATCH 115/118] spi: xilinx: let transfers timeout in case of no IRQ In case of failed HW the driver may not see an interrupt and will stuck in waiting forever. We can avoid such situation by timing out of transfers if the interrupt is not seen in a reasonable time. This problem can be found on unload of ptp_ocp driver for TimeCard which uses Xilinx SPI AXI and SPI-NOR flash memory. During tear-down process spi-nor drivers send soft reset command which is not triggering an interrupt stalling the unload process completely. Signed-off-by: Vadim Fedorenko Acked-by: Michal Simek Link: https://patch.msgid.link/20260610222843.782337-1-vadim.fedorenko@linux.dev Signed-off-by: Mark Brown --- drivers/spi/spi-xilinx.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/spi/spi-xilinx.c b/drivers/spi/spi-xilinx.c index 3aeef70caa96..a9fe3784932f 100644 --- a/drivers/spi/spi-xilinx.c +++ b/drivers/spi/spi-xilinx.c @@ -285,7 +285,11 @@ static int xilinx_spi_txrx_bufs(struct spi_device *spi, struct spi_transfer *t) if (use_irq) { xspi->write_fn(cr, xspi->regs + XSPI_CR_OFFSET); - wait_for_completion(&xspi->done); + if (!wait_for_completion_timeout(&xspi->done, secs_to_jiffies(1))) { + dev_err(&spi->dev, "SPI transfer timed out\n"); + xspi_init_hw(xspi); + return -ETIMEDOUT; + } /* A transmit has just completed. Process received data * and check for more data to transmit. Always inhibit * the transmitter while the Isr refills the transmit From c0a5405ec38b95c7fd503fd2e73b6f3119423cab Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Fri, 12 Jun 2026 10:58:07 +0200 Subject: [PATCH 116/118] spi: spi-mem: Fix spi_controller_mem_ops kdoc The secondary_op_tmpl kdoc line has been removed accidentally, add it back. Reported-by: Michael Walle Closes: https://lore.kernel.org/linux-mtd/DJ56CDMRVFQ6.FOZRIQTF3VDW@kernel.org/T/#u Fixes: 38fbe4b3f66e ("spi: spi-mem: Add a no_cs_assertion capability") Signed-off-by: Miquel Raynal Link: https://patch.msgid.link/20260612-perso-fix-no-cs-assertion-kdoc-v1-1-626b2d6d0d9b@bootlin.com Signed-off-by: Mark Brown --- include/linux/spi/spi-mem.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/linux/spi/spi-mem.h b/include/linux/spi/spi-mem.h index 722abd9aee3c..f660bb2e9f85 100644 --- a/include/linux/spi/spi-mem.h +++ b/include/linux/spi/spi-mem.h @@ -385,6 +385,7 @@ struct spi_controller_mem_ops { * @swap16: Supports swapping bytes on a 16 bit boundary when configured in * Octal DTR * @per_op_freq: Supports per operation frequency switching + * @secondary_op_tmpl: Supports leveraging a secondary memory operation template * @no_cs_assertion: The controller may automatically deassert the CS if there * is a pause in the transfer (eg. internal bus contention or * DMA arbitration on an interconnect). Features such as NAND From 47f3b5365536e8c38f264824ab15fdb74454e066 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20P=C3=B6schel?= Date: Fri, 12 Jun 2026 12:52:44 +0200 Subject: [PATCH 117/118] spi: xilinx: use FIFO occupancy register to determine buffer size MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The method the driver uses to determine the size of the FIFO has a problem. What it currently does is this: It stops the SPI hardware and writes to the TX FIFO register until TX FIFO FULL asserts in the status register. But the hardware does not only have the FIFO, it also has a shift register which can hold a byte. This can be seen, when writing a byte to the FIFO (while the SPI hardware is stopped,) the TX FIFO EMPTY is still empty. So, if we have a FIFO size of 16 for example, the current method returns a 17. This is a problem, at least when using the driver in irq mode. The same size determined for the TX FIFO is also assumed for the RX FIFO. When a SPI transaction wants to write the amount of the FIFO size or more bytes, the following happens, for example with 16 bytes FIFO size: The driver stops the SPI hardware and writes 17 bytes to the TX FIFO and starts the SPI hardware and goes sleep. The hardware then shifts out 17 bytes (FIFO + shift register) and simultaneously reads bytes into the RX FIFO, but it only has 16 places, so it looses one byte. Then TX FIFO empty asserts, wakes the driver again, which has a fast path and reads 16 bytes from the RX FIFO, but before reading the last 17th byte (which is lost) it does this: sr = xspi->read_fn(xspi->regs + XSPI_SR_OFFSET); if (!(sr & XSPI_SR_RX_EMPTY_MASK)) { xilinx_spi_rx(xspi); rx_words--; } It reads the status register and checks if the RX FIFO is not empty. But it is empty in our case. So this check spins in a while loop forever locking the driver. This patch fixes the logic to determine the FIFO size. Fixes: 4c9a761402d7 ("spi/xilinx: Simplify spi_fill_tx_fifo") Signed-off-by: Lars Pöschel Reviewed-by: Michal Simek Link: https://patch.msgid.link/20260612105244.9076-1-lars.poeschel.linux@edag.com Signed-off-by: Mark Brown --- drivers/spi/spi-xilinx.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/spi/spi-xilinx.c b/drivers/spi/spi-xilinx.c index a9fe3784932f..dc1e968659e1 100644 --- a/drivers/spi/spi-xilinx.c +++ b/drivers/spi/spi-xilinx.c @@ -375,11 +375,18 @@ static int xilinx_spi_find_buffer_size(struct xilinx_spi *xspi) xspi->regs + XIPIF_V123B_RESETR_OFFSET); /* Fill the Tx FIFO with as many words as possible */ - do { + while (1) { xspi->write_fn(0, xspi->regs + XSPI_TXD_OFFSET); sr = xspi->read_fn(xspi->regs + XSPI_SR_OFFSET); + if (sr & XSPI_SR_TX_FULL_MASK) + break; + n_words++; - } while (!(sr & XSPI_SR_TX_FULL_MASK)); + } + + /* Handle the NO FIFO case separately */ + if (!n_words) + return 1; return n_words; } From f846d68992142034b1d34a83200a10cdc713eeda Mon Sep 17 00:00:00 2001 From: "Rob Herring (Arm)" Date: Fri, 12 Jun 2026 16:50:17 -0500 Subject: [PATCH 118/118] spi: Fix mismatched DT property access types The SPI drivers read properties whose bindings use normal uint32 cells. Using boolean or u16 helpers makes the access look like a different DT encoding and causes the property checker to flag the call sites. Use presence checks for unsupported properties and read numeric cell properties through u32 helpers before assigning to driver fields. Assisted-by: Codex:gpt-5-5 Signed-off-by: Rob Herring (Arm) Link: https://patch.msgid.link/20260612215017.1884893-1-robh@kernel.org Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-espi.c | 2 +- drivers/spi/spi-hisi-kunpeng.c | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/spi/spi-fsl-espi.c b/drivers/spi/spi-fsl-espi.c index f560bd537f7d..1341bdd7db75 100644 --- a/drivers/spi/spi-fsl-espi.c +++ b/drivers/spi/spi-fsl-espi.c @@ -756,7 +756,7 @@ static int of_fsl_espi_probe(struct platform_device *ofdev) unsigned int irq, num_cs; int ret; - if (of_property_read_bool(np, "mode")) { + if (of_property_present(np, "mode")) { dev_err(dev, "mode property is not supported on ESPI!\n"); return -EINVAL; } diff --git a/drivers/spi/spi-hisi-kunpeng.c b/drivers/spi/spi-hisi-kunpeng.c index 395214b81179..30fcdb5028dc 100644 --- a/drivers/spi/spi-hisi-kunpeng.c +++ b/drivers/spi/spi-hisi-kunpeng.c @@ -463,6 +463,7 @@ static int hisi_spi_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct spi_controller *host; struct hisi_spi *hs; + u32 num_cs; int ret, irq; irq = platform_get_irq(pdev, 0); @@ -495,10 +496,11 @@ static int hisi_spi_probe(struct platform_device *pdev) if (host->max_speed_hz == 0) return dev_err_probe(dev, -EINVAL, "spi-max-frequency can't be 0\n"); - ret = device_property_read_u16(dev, "num-cs", - &host->num_chipselect); + ret = device_property_read_u32(dev, "num-cs", &num_cs); if (ret) host->num_chipselect = DEFAULT_NUM_CS; + else + host->num_chipselect = num_cs; host->use_gpio_descriptors = true; host->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | SPI_LOOP;