From 90af7fde083e1b22c349c3a8b1626728e44e474c Mon Sep 17 00:00:00 2001 From: Yifei Gao Date: Tue, 4 Aug 2026 21:34:56 +0000 Subject: [PATCH 01/15] memstick: ms_block: destroy io_queue workqueue on removal msb_init_disk() creates the per-card ordered workqueue msb->io_queue with alloc_ordered_workqueue(). It is torn down with destroy_workqueue() only on the init error path; msb_remove() never destroys it. msb_stop() merely flushes the queue, and neither msb_data_clear() nor put_disk() free it. As a result every card insert/remove cycle leaks the workqueue and its kworker, exhausting kernel memory over repeated cycles. Destroy the workqueue in msb_remove() after the disk has been removed and the queue drained. Fixes: 0ab30494bc4f ("memstick: add support for legacy memorysticks") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Yifei Gao Signed-off-by: Ulf Hansson --- drivers/memstick/core/ms_block.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/memstick/core/ms_block.c b/drivers/memstick/core/ms_block.c index ce33907bfc24..65154e569a9e 100644 --- a/drivers/memstick/core/ms_block.c +++ b/drivers/memstick/core/ms_block.c @@ -2204,6 +2204,8 @@ static void msb_remove(struct memstick_dev *card) msb_data_clear(msb); mutex_unlock(&msb_disk_lock); + destroy_workqueue(msb->io_queue); + put_disk(msb->disk); memstick_set_drvdata(card, NULL); } From 6feadbecdae60a6324c967f3b1493741083793a3 Mon Sep 17 00:00:00 2001 From: Fan Wu Date: Thu, 6 Aug 2026 13:02:33 +0000 Subject: [PATCH 02/15] mmc: core: Cancel SDIO IRQ work before freeing host A host controller that uses sdio_signal_irq() schedules host->sdio_irq_work from its interrupt handler. That work is only cancelled on the suspend path (mmc_sdio_suspend()), not on the remove/free path, so a worker armed just before the controller freed its IRQ can run after mmc_host_classdev_release() has freed the host and dereference it through container_of(). Cancel host->sdio_irq_work in mmc_free_host(), like the existing host->detect drain added by commit 1036f69e2513 ("mmc: core: Cancel delayed work before releasing host"). This issue was found by an in-house static analysis tool. Fixes: 682696605c70 ("mmc: sdio: Add API to manage SDIO IRQs from a workqueue") Cc: stable@vger.kernel.org Assisted-by: Codex:gpt-5.6 Signed-off-by: Fan Wu Signed-off-by: Ulf Hansson --- drivers/mmc/core/host.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c index b7ce3137d452..828078b3f962 100644 --- a/drivers/mmc/core/host.c +++ b/drivers/mmc/core/host.c @@ -698,6 +698,7 @@ EXPORT_SYMBOL(mmc_remove_host); void mmc_free_host(struct mmc_host *host) { cancel_delayed_work_sync(&host->detect); + cancel_work_sync(&host->sdio_irq_work); mmc_pwrseq_free(host); put_device(&host->class_dev); } From 2b19cf3e50cddaff07b657dae1a8f30f06032852 Mon Sep 17 00:00:00 2001 From: Fan Wu Date: Fri, 14 Aug 2026 08:25:50 +0000 Subject: [PATCH 03/15] mmc: mmci: Fix use-after-free in busy-timeout work ux500_busy_complete() can queue ux500_busy_timeout_work for an R1b command, but mmci_remove() never cancels it. The work can subsequently dereference the devm-allocated mmci_host after it has been released. Mask the controller interrupts and disable the delayed work during removal. This drains any queued instance and stops an IRQ handler that is still in progress from queueing the work again once it has been disabled. This issue was found by an in-house static analysis tool. Fixes: b1a665932dc2 ("mmc: mmci: Add support for SW busy-end timeouts") Cc: stable@vger.kernel.org # v6.10+ Assisted-by: Codex:gpt-5.6 Signed-off-by: Fan Wu Reviewed-by: Linus Walleij Signed-off-by: Ulf Hansson --- drivers/mmc/host/mmci.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index e500051bd572..416bdb184ed4 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -2511,6 +2511,9 @@ static void mmci_remove(struct amba_device *dev) writel(0, host->base + MMCICOMMAND); writel(0, host->base + MMCIDATACTRL); + if (variant->busy_detect) + disable_delayed_work_sync(&host->ux500_busy_timeout_work); + mmci_dma_release(host); clk_disable_unprepare(host->clk); } From d3a421c82412344022982d5b91ba23194a0a6f29 Mon Sep 17 00:00:00 2001 From: Fan Wu Date: Fri, 7 Aug 2026 03:26:54 +0000 Subject: [PATCH 04/15] mmc: mxcmmc: cancel data work and watchdog on remove mxcmci_remove() frees the host through the devm tail, but neither it nor mmc_remove_host() drains the driver's own asynchronous state. host->watchdog, a 10 s timer armed on the DMA path in mxcmci_setup_data(), is deleted only by the DMA- and IRQ-complete paths, which the remove path does not explicitly drain; it can therefore fire after the host is freed and dereference it in mxcmci_watchdog(). host->datawork, armed from the IRQ handler on the PIO path, is not cancelled by the remove path either. Free the devm-registered IRQ, then cancel datawork and delete the watchdog in mxcmci_remove(), before dma_release_channel(). Freeing the IRQ first keeps a trailing handler from re-arming datawork between the cancel and the host free. Both callbacks are non-self-rearming. This issue was found by an in-house static analysis tool. Fixes: f6ad0a481342 ("mmc: mxcmmc: fix bug that may block a data transfer forever") Cc: stable@vger.kernel.org Assisted-by: Codex:gpt-5.6 Signed-off-by: Fan Wu Signed-off-by: Ulf Hansson --- drivers/mmc/host/mxcmmc.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/mmc/host/mxcmmc.c b/drivers/mmc/host/mxcmmc.c index c405cfb8b269..097498a3f8ff 100644 --- a/drivers/mmc/host/mxcmmc.c +++ b/drivers/mmc/host/mxcmmc.c @@ -1173,6 +1173,10 @@ static void mxcmci_remove(struct platform_device *pdev) mmc_remove_host(mmc); + devm_free_irq(&pdev->dev, platform_get_irq(pdev, 0), host); + cancel_work_sync(&host->datawork); + timer_delete_sync(&host->watchdog); + if (host->pdata && host->pdata->exit) host->pdata->exit(&pdev->dev, mmc); From 5d132990475f02cfa1debe03d50b479432864ebd Mon Sep 17 00:00:00 2001 From: Fan Wu Date: Fri, 14 Aug 2026 08:23:54 +0000 Subject: [PATCH 05/15] mmc: hsq: Fix use-after-free in retry work mmc_hsq_pump_requests() queues retry_work when request_atomic() returns -EBUSY; today sdhci-sprd is the only consumer that implements request_atomic(). The work is embedded in a devm-allocated mmc_hsq, but is never cancelled during driver removal. Work still pending at unbind can therefore run after the devm allocation has been released and dereference hsq->mmc and hsq->mrq. Use devm_work_autocancel() to cancel and drain retry_work before the devm allocation is released. By the time devres cleanup begins, mmc_remove_host() has already stopped the host, so no new requests can arm the work. This issue was found by an in-house static analysis tool. Fixes: 6db96e5810e0 ("mmc: host: Introduce the request_atomic() for the host") Cc: stable@vger.kernel.org Assisted-by: Codex:gpt-5.6 Signed-off-by: Fan Wu Signed-off-by: Ulf Hansson --- drivers/mmc/host/mmc_hsq.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/host/mmc_hsq.c b/drivers/mmc/host/mmc_hsq.c index 79836705c176..57e172bd3487 100644 --- a/drivers/mmc/host/mmc_hsq.c +++ b/drivers/mmc/host/mmc_hsq.c @@ -7,6 +7,7 @@ * Author: Baolin Wang */ +#include #include #include #include @@ -345,6 +346,7 @@ static const struct mmc_cqe_ops mmc_hsq_ops = { int mmc_hsq_init(struct mmc_hsq *hsq, struct mmc_host *mmc) { + int ret; int i; hsq->num_slots = HSQ_NUM_SLOTS; hsq->next_tag = HSQ_INVALID_TAG; @@ -363,7 +365,11 @@ int mmc_hsq_init(struct mmc_hsq *hsq, struct mmc_host *mmc) for (i = 0; i < HSQ_NUM_SLOTS; i++) hsq->tag_slot[i] = HSQ_INVALID_TAG; - INIT_WORK(&hsq->retry_work, mmc_hsq_retry_handler); + ret = devm_work_autocancel(mmc_dev(mmc), &hsq->retry_work, + mmc_hsq_retry_handler); + if (ret) + return ret; + spin_lock_init(&hsq->lock); init_waitqueue_head(&hsq->wait_queue); From ff894dced1a7ad7523f9c65dbdb53d02474cca0f Mon Sep 17 00:00:00 2001 From: "Diogo Ivo (Schneider Electric)" Date: Fri, 7 Aug 2026 13:06:57 +0200 Subject: [PATCH 06/15] mmc: sdhci_am654: Move tuning_loop to local variable The tuning_loop field in struct sdhci_am654_data is only used within sdhci_am654_platform_execute_tuning() as a loop counter that is initialized to 0 in sdhci_am654_init(). Since it shouldn't persist across function calls, otherwise every failure expends its "budget", move it to a local variable and remove the struct field along with the now-unnecessary initialization. Signed-off-by: Diogo Ivo (Schneider Electric) Reviewed-by: Judith Mendez Acked-by: Adrian Hunter Fixes: de31f6ab68a3 ("mmc: sdhci_am654: Reset Command and Data line after tuning") Cc: stable@vger.kernel.org Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci_am654.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/drivers/mmc/host/sdhci_am654.c b/drivers/mmc/host/sdhci_am654.c index 2a27db2f558b..4b74a4115509 100644 --- a/drivers/mmc/host/sdhci_am654.c +++ b/drivers/mmc/host/sdhci_am654.c @@ -151,7 +151,6 @@ struct sdhci_am654_data { u32 flags; u32 quirks; bool dll_enable; - u32 tuning_loop; #define SDHCI_AM654_QUIRK_FORCE_CDTEST BIT(0) #define SDHCI_AM654_QUIRK_SUPPRESS_V1P8_ENA BIT(1) @@ -576,13 +575,14 @@ static int sdhci_am654_platform_execute_tuning(struct sdhci_host *host, struct sdhci_am654_data *sdhci_am654 = sdhci_pltfm_priv(pltfm_host); unsigned char timing = host->mmc->ios.timing; struct device *dev = mmc_dev(host->mmc); + unsigned int tuning_loop = 0; int itapdly; do { itapdly = sdhci_am654_do_tuning(host, opcode); if (itapdly >= 0) break; - } while (++sdhci_am654->tuning_loop < RETRY_TUNING_MAX); + } while (++tuning_loop < RETRY_TUNING_MAX); if (itapdly < 0) { dev_err(dev, "Failed to find itapdly, fail tuning\n"); @@ -806,9 +806,6 @@ static int sdhci_am654_init(struct sdhci_host *host) regmap_update_bits(sdhci_am654->base, CTL_CFG_3, TUNINGFORSDR50_MASK, TUNINGFORSDR50_MASK); - /* Use to re-execute tuning */ - sdhci_am654->tuning_loop = 0; - ret = sdhci_setup_host(host); if (ret) return ret; From 7197d9107d9545730153b82ea5a411c5208b443f Mon Sep 17 00:00:00 2001 From: "Diogo Ivo (Schneider Electric)" Date: Fri, 7 Aug 2026 13:06:58 +0200 Subject: [PATCH 07/15] mmc: sdhci_am654: Reset command and data lines on failed tuning The CMD/DATA reset after tuning should be performed regardless of whether tuning succeeded or failed, since tuning data may remain in the buffer in either case. Move the error return after the reset so that the controller is always cleaned up. Fixes: de31f6ab68a3 ("mmc: sdhci_am654: Reset Command and Data line after tuning") Cc: stable@vger.kernel.org Signed-off-by: Diogo Ivo (Schneider Electric) Reviewed-by: Judith Mendez Acked-by: Adrian Hunter Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci_am654.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/mmc/host/sdhci_am654.c b/drivers/mmc/host/sdhci_am654.c index 4b74a4115509..6abe206ed292 100644 --- a/drivers/mmc/host/sdhci_am654.c +++ b/drivers/mmc/host/sdhci_am654.c @@ -442,15 +442,13 @@ static int sdhci_am654_execute_tuning(struct mmc_host *mmc, u32 opcode) struct sdhci_host *host = mmc_priv(mmc); int err = sdhci_execute_tuning(mmc, opcode); - if (err) - return err; /* * Tuning data remains in the buffer after tuning. * Do a command and data reset to get rid of it */ sdhci_reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA); - return 0; + return err; } static u32 sdhci_am654_cqhci_irq(struct sdhci_host *host, u32 intmask) From c9f47cc8c37f7659897142ffe216c250fbc1d4ed Mon Sep 17 00:00:00 2001 From: "Diogo Ivo (Schneider Electric)" Date: Fri, 7 Aug 2026 13:06:59 +0200 Subject: [PATCH 08/15] mmc: sdhci_am654: Clear ITAPDLY on tuning failure When tuning fails, stale ITAPDLY values can persist and interfere with subsequent I/O accesses, for example in DDR50 mode in cards with no tuning support. Move the ITAPDLY enable setting out of the tuning loop to after successful tuning, and explicitly clear ITAPDLY (delay and enable) when tuning fails so that we are sure only working values are actually left in hardware. Fixes: 901d16e46296 ("mmc: sdhci_am654: Add retry tuning") Cc: stable@vger.kernel.org Signed-off-by: Diogo Ivo (Schneider Electric) Reviewed-by: Judith Mendez Acked-by: Adrian Hunter Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci_am654.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/drivers/mmc/host/sdhci_am654.c b/drivers/mmc/host/sdhci_am654.c index 6abe206ed292..49a46583c8bc 100644 --- a/drivers/mmc/host/sdhci_am654.c +++ b/drivers/mmc/host/sdhci_am654.c @@ -527,7 +527,6 @@ static int sdhci_am654_do_tuning(struct sdhci_host *host, { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_am654_data *sdhci_am654 = sdhci_pltfm_priv(pltfm_host); - unsigned char timing = host->mmc->ios.timing; struct window fail_window[ITAPDLY_LENGTH]; struct device *dev = mmc_dev(host->mmc); u8 curr_pass, itap; @@ -536,11 +535,8 @@ static int sdhci_am654_do_tuning(struct sdhci_host *host, memset(fail_window, 0, sizeof(fail_window)); - /* Enable ITAPDLY */ - sdhci_am654->itap_del_ena[timing] = 0x1; - for (itap = 0; itap < ITAPDLY_LENGTH; itap++) { - sdhci_am654_write_itapdly(sdhci_am654, itap, sdhci_am654->itap_del_ena[timing]); + sdhci_am654_write_itapdly(sdhci_am654, itap, 0x1); curr_pass = !mmc_send_tuning(host->mmc, opcode, NULL); @@ -584,10 +580,16 @@ static int sdhci_am654_platform_execute_tuning(struct sdhci_host *host, if (itapdly < 0) { dev_err(dev, "Failed to find itapdly, fail tuning\n"); + sdhci_am654_write_itapdly(sdhci_am654, 0, 0); + sdhci_am654->itap_del_ena[timing] = 0; + sdhci_am654->itap_del_sel[timing] = 0; return -1; } dev_dbg(dev, "Passed tuning, final itapdly=%d\n", itapdly); + + /* Enable ITAPDLY */ + sdhci_am654->itap_del_ena[timing] = 0x1; sdhci_am654_write_itapdly(sdhci_am654, itapdly, sdhci_am654->itap_del_ena[timing]); /* Save ITAPDLY */ sdhci_am654->itap_del_sel[timing] = itapdly; From 308d05225281d86150d88141990d6caf8c902349 Mon Sep 17 00:00:00 2001 From: "Diogo Ivo (Schneider Electric)" Date: Fri, 7 Aug 2026 13:07:00 +0200 Subject: [PATCH 09/15] mmc: sdhci_am654: Fallback to DT-provided itap delay on DDR50 tuning failure DDR50 mode is not required to support the tuning command CMD19, meaning that calibration may fail on cards that do not implement it, in which case a known-good itap delay value should be programmed into the host controller. Do this by reading the (already defined) itap delay DT property for DDR50 and, if tuning fails for this mode, fall back to the DT-provided itap delay value. If the DT does not provide a value for DDR50 fallback then this simply disables using itapdly. Fixes: 901d16e46296 ("mmc: sdhci_am654: Add retry tuning") Cc: stable@vger.kernel.org Signed-off-by: Diogo Ivo (Schneider Electric) Acked-by: Adrian Hunter Reviewed-by: Judith Mendez Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci_am654.c | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/drivers/mmc/host/sdhci_am654.c b/drivers/mmc/host/sdhci_am654.c index 49a46583c8bc..2e332f52c301 100644 --- a/drivers/mmc/host/sdhci_am654.c +++ b/drivers/mmc/host/sdhci_am654.c @@ -126,7 +126,7 @@ static const struct timing_data td[] = { NULL, MMC_CAP_UHS_SDR104}, [MMC_TIMING_UHS_DDR50] = {"ti,otap-del-sel-ddr50", - NULL, + "ti,itap-del-sel-ddr50", MMC_CAP_UHS_DDR50}, [MMC_TIMING_MMC_DDR52] = {"ti,otap-del-sel-ddr52", "ti,itap-del-sel-ddr52", @@ -144,6 +144,8 @@ struct sdhci_am654_data { u32 otap_del_sel[ARRAY_SIZE(td)]; u32 itap_del_sel[ARRAY_SIZE(td)]; u32 itap_del_ena[ARRAY_SIZE(td)]; + u32 itap_del_sel_dt_ddr50; + u32 itap_del_ena_dt_ddr50; int clkbuf_sel; int trm_icp; int drv_strength; @@ -579,10 +581,19 @@ static int sdhci_am654_platform_execute_tuning(struct sdhci_host *host, } while (++tuning_loop < RETRY_TUNING_MAX); if (itapdly < 0) { - dev_err(dev, "Failed to find itapdly, fail tuning\n"); - sdhci_am654_write_itapdly(sdhci_am654, 0, 0); - sdhci_am654->itap_del_ena[timing] = 0; - sdhci_am654->itap_del_sel[timing] = 0; + if (timing == MMC_TIMING_UHS_DDR50) { + dev_dbg(dev, "Failed DDR50 tuning, fallback to DT ITAP\n"); + sdhci_am654->itap_del_sel[timing] = sdhci_am654->itap_del_sel_dt_ddr50; + sdhci_am654->itap_del_ena[timing] = sdhci_am654->itap_del_ena_dt_ddr50; + } else { + dev_err(dev, "Failed to find itapdly, fail tuning\n"); + sdhci_am654->itap_del_ena[timing] = 0; + sdhci_am654->itap_del_sel[timing] = 0; + } + + sdhci_am654_write_itapdly(sdhci_am654, + sdhci_am654->itap_del_sel[timing], + sdhci_am654->itap_del_ena[timing]); return -1; } @@ -758,6 +769,11 @@ static int sdhci_am654_get_otap_delay(struct sdhci_host *host, } } + sdhci_am654->itap_del_sel_dt_ddr50 = + sdhci_am654->itap_del_sel[MMC_TIMING_UHS_DDR50]; + sdhci_am654->itap_del_ena_dt_ddr50 = + sdhci_am654->itap_del_ena[MMC_TIMING_UHS_DDR50]; + return 0; } From 8b0cc8707f65e0f51912e764e1b309b2559db1ec Mon Sep 17 00:00:00 2001 From: Xu Rao Date: Tue, 18 Aug 2026 19:31:53 +0800 Subject: [PATCH 10/15] mmc: spi: reset bytes_xfered before retrying CRC failures mmc_spi_data_do() updates data->bytes_xfered after each block has been transferred successfully. If a later block in the same data request fails with a CRC error, data->bytes_xfered may therefore contain the number of bytes completed before the failing block. mmc_spi_request() has a private recovery path for such CRC failures. It sends STOP_TRANSMISSION, clears data->error and jumps back to crc_recover to issue the same command and data request again. However, it does not clear data->bytes_xfered before the retry. If the retry succeeds, the request is completed with the bytes from the failed attempt still included in data->bytes_xfered. For a multi-block request this can make the completed request report more bytes than were transferred by the successful retry, and can even exceed the request size when most blocks completed before the CRC error. This is most likely to be observed on MMC-over-SPI systems where long multi-block transfers occasionally hit a data CRC error but the mmc_spi-internal retry succeeds. The data itself is retried, but the completion accounting is not. Clear data->bytes_xfered together with data->error before repeating the request so the final completion reports only the bytes transferred by the successful attempt. Fixes: 061c6c847eeb ("mmc_spi: Recover from CRC errors for r/w operation over SPI.") Cc: stable@vger.kernel.org Signed-off-by: Xu Rao Signed-off-by: Ulf Hansson --- drivers/mmc/host/mmc_spi.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/mmc/host/mmc_spi.c b/drivers/mmc/host/mmc_spi.c index b471a7795b4d..5217d713c826 100644 --- a/drivers/mmc/host/mmc_spi.c +++ b/drivers/mmc/host/mmc_spi.c @@ -952,6 +952,7 @@ static void mmc_spi_request(struct mmc_host *mmc, struct mmc_request *mrq) status = mmc_spi_command_send(host, mrq, &stop, 0); crc_retry--; mrq->data->error = 0; + mrq->data->bytes_xfered = 0; goto crc_recover; } From 53823e25793a97d07e6e98e0904bbf74cac8bc76 Mon Sep 17 00:00:00 2001 From: Felix Gu Date: Sat, 22 Aug 2026 02:58:47 +0800 Subject: [PATCH 11/15] mmc: sdio_uart: fix xmit_fifo leak when the port table is full sdio_uart_add_port() allocates the transmit fifo before claiming a slot in sdio_uart_table[]. When all UART_NR slots are taken, it returns -EBUSY with the fifo still allocated, but the probe error path only kfree()s the port, leaking the transmit fifo. Free the fifo in the failure path of sdio_uart_add_port() itself so the function retains nothing on error. Fixes: 8b197a5ce7a7 ("sdio_uart: Use kfifo instead of the messy circ stuff") Signed-off-by: Felix Gu Cc: stable@vger.kernel.org Signed-off-by: Ulf Hansson --- drivers/mmc/core/sdio_uart.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/mmc/core/sdio_uart.c b/drivers/mmc/core/sdio_uart.c index 7fd5dedf3ac0..705163c42972 100644 --- a/drivers/mmc/core/sdio_uart.c +++ b/drivers/mmc/core/sdio_uart.c @@ -104,6 +104,9 @@ static int sdio_uart_add_port(struct sdio_uart_port *port) } spin_unlock(&sdio_uart_table_lock); + if (ret) + kfifo_free(&port->xmit_fifo); + return ret; } From 9c182bc5d7817437a7d04ab96133f9191846d93d Mon Sep 17 00:00:00 2001 From: Florian Maillard Date: Mon, 24 Aug 2026 08:57:55 +0200 Subject: [PATCH 12/15] mmc: rtsx_pci_sdmmc: ignore broken write-protect on ThinkPad X260 The Realtek RTS522A card reader in the Lenovo ThinkPad X260 (subsystem 17aa:504a) incorrectly reports inserted SD cards as write-protected. This causes the MMC core to expose the card as read-only: mmcblk0: mmc0:aaaa SN256 238 GiB (ro) and /sys/block/mmcblk0/ro reports 1. Setting MMC_CAP2_NO_WRITE_PROTECT makes the card writable again. Limit the quirk to the affected Lenovo subsystem. Assisted-by: ChatGPT:GPT-5.6 Sol Signed-off-by: Florian Maillard Cc: stable@vger.kernel.org Signed-off-by: Ulf Hansson --- drivers/mmc/host/rtsx_pci_sdmmc.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/mmc/host/rtsx_pci_sdmmc.c b/drivers/mmc/host/rtsx_pci_sdmmc.c index 8dfbc62f165b..d25d4cb59bc9 100644 --- a/drivers/mmc/host/rtsx_pci_sdmmc.c +++ b/drivers/mmc/host/rtsx_pci_sdmmc.c @@ -1425,6 +1425,11 @@ static void realtek_init_host(struct realtek_pci_sdmmc *host) mmc->caps = mmc->caps | MMC_CAP_AGGRESSIVE_PM; mmc->caps2 = MMC_CAP2_NO_PRESCAN_POWERUP | MMC_CAP2_FULL_PWR_CYCLE | MMC_CAP2_NO_SDIO; + + if (pcr->pci->device == 0x522a && + pcr->pci->subsystem_vendor == PCI_VENDOR_ID_LENOVO && + pcr->pci->subsystem_device == 0x504a) + mmc->caps2 |= MMC_CAP2_NO_WRITE_PROTECT; mmc->max_current_330 = 400; mmc->max_current_180 = 800; mmc->ops = &realtek_pci_sdmmc_ops; From 08b54e16d547d5c1aa61bf7a3595bb1620975eeb Mon Sep 17 00:00:00 2001 From: Zhu Ling Date: Fri, 4 Sep 2026 17:07:46 +0800 Subject: [PATCH 13/15] mmc: core: Fix OF node reference leak on card add failure mmc_of_find_child_device() returns a device node with its reference count incremented. mmc_add_card() stores the reference before calling device_add(), while the card is marked present only after device_add() succeeds. If device_add() fails, the callers release the card through mmc_remove_card(). However, mmc_remove_card() only drops the OF node reference for a present card, leaking the reference on this error path. Move of_node_put() outside the present-card conditional so the reference is released for both registered cards and card-add failures. Fixes: 25185f3f31c9 ("mmc: Add SDIO function devicetree subnode parsing") Cc: stable@vger.kernel.org Signed-off-by: Zhu Ling Reviewed-by: Shawn Lin Signed-off-by: Ulf Hansson --- drivers/mmc/core/bus.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mmc/core/bus.c b/drivers/mmc/core/bus.c index be5cf338bdeb..92ad39c0cabc 100644 --- a/drivers/mmc/core/bus.c +++ b/drivers/mmc/core/bus.c @@ -417,8 +417,8 @@ void mmc_remove_card(struct mmc_card *card) mmc_hostname(card->host), card->rca); } device_del(&card->dev); - of_node_put(card->dev.of_node); } + of_node_put(card->dev.of_node); if (host->cqe_enabled) { host->cqe_ops->cqe_disable(host); From d5ea0d226e8f0801d78702142a124d78c317d822 Mon Sep 17 00:00:00 2001 From: Runyu Xiao Date: Wed, 2 Sep 2026 22:09:26 +0800 Subject: [PATCH 14/15] mmc: sh_mmcif: initialize IRQ-thread mutex before requesting interrupt The threaded IRQ handler can run before devm_request_threaded_irq() returns, but thread_lock was initialized afterwards. Initialize it before requesting either interrupt. Fixes: 8047310ee984 ("mmc: sh_mmcif: fix a race, causing an Oops on SMP") Cc: stable@vger.kernel.org Assisted-by: Codex:GPT-5 Signed-off-by: Runyu Xiao Signed-off-by: Ulf Hansson --- drivers/mmc/host/sh_mmcif.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c index 7706d01b149e..8bd9cdedafa4 100644 --- a/drivers/mmc/host/sh_mmcif.c +++ b/drivers/mmc/host/sh_mmcif.c @@ -1461,6 +1461,7 @@ static int sh_mmcif_probe(struct platform_device *pdev) host->pd = pdev; spin_lock_init(&host->lock); + mutex_init(&host->thread_lock); mmc->ops = &sh_mmcif_ops; sh_mmcif_init_ocr(host); @@ -1515,8 +1516,6 @@ static int sh_mmcif_probe(struct platform_device *pdev) goto err_clk; } - mutex_init(&host->thread_lock); - ret = mmc_add_host(mmc); if (ret < 0) goto err_clk; From 4396d70bb7fec531bcf934fed016b2f3300c670b Mon Sep 17 00:00:00 2001 From: Myeonghun Pak Date: Sun, 13 Sep 2026 18:35:38 -0400 Subject: [PATCH 15/15] mmc: sdhci-of-aspeed: Remove children before releasing SDC resources Probe failure and removal leave SDHCI child devices registered after the parent clock and managed resources are released. Unregister the OF children in reverse order before disabling the parent clock on both paths. Use of_platform_device_destroy() because manual child creation does not set the flag required by of_platform_depopulate(). This issue was identified during our ongoing static-analysis research while reviewing kernel code. Fixes: bb7b8ec62dfb ("mmc: sdhci-of-aspeed: Add support for the ASPEED SD controller") Co-developed-by: Ijae Kim Signed-off-by: Ijae Kim Signed-off-by: Myeonghun Pak Assisted-by: OpenAI:GPT-5.6 Cc: stable@vger.kernel.org Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-of-aspeed.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/host/sdhci-of-aspeed.c b/drivers/mmc/host/sdhci-of-aspeed.c index f5d973783cbe..d317626feb4a 100644 --- a/drivers/mmc/host/sdhci-of-aspeed.c +++ b/drivers/mmc/host/sdhci-of-aspeed.c @@ -560,12 +560,14 @@ static int aspeed_sdc_probe(struct platform_device *pdev) cpdev = of_platform_device_create(child, NULL, &pdev->dev); if (!cpdev) { ret = -ENODEV; - goto err_clk; + goto err_children; } } return 0; +err_children: + device_for_each_child_reverse(&pdev->dev, NULL, of_platform_device_destroy); err_clk: clk_disable_unprepare(sdc->clk); return ret; @@ -575,6 +577,7 @@ static void aspeed_sdc_remove(struct platform_device *pdev) { struct aspeed_sdc *sdc = dev_get_drvdata(&pdev->dev); + device_for_each_child_reverse(&pdev->dev, NULL, of_platform_device_destroy); clk_disable_unprepare(sdc->clk); }