From eac85fbd0867c25ac517f58fae401d65c627edff Mon Sep 17 00:00:00 2001 From: Matthew Schwartz Date: Sun, 4 Jan 2026 22:02:35 -0800 Subject: [PATCH] mmc: rtsx: reset power state on suspend When rtsx_pci suspends, the card reader hardware powers off but the sdmmc driver's prev_power_state remains as MMC_POWER_ON. This causes sd_power_on to skip reinitialization on the next I/O request, leading to DMA transfer timeouts and errors on resume 20% of the time. Add a power_off slot callback so the PCR can notify the sdmmc driver during suspend. The sdmmc driver resets prev_power_state, and sd_request checks this to reinitialize the card before the next I/O. Signed-off-by: Matthew Schwartz Link: https://patch.msgid.link/20260105060236.400366-2-matthew.schwartz@linux.dev Signed-off-by: Greg Kroah-Hartman --- drivers/misc/cardreader/rtsx_pcr.c | 9 +++++++++ drivers/mmc/host/rtsx_pci_sdmmc.c | 22 ++++++++++++++++++++++ include/linux/rtsx_common.h | 1 + 3 files changed, 32 insertions(+) diff --git a/drivers/misc/cardreader/rtsx_pcr.c b/drivers/misc/cardreader/rtsx_pcr.c index f9952d76d6ed7..f1f4d8ed544d6 100644 --- a/drivers/misc/cardreader/rtsx_pcr.c +++ b/drivers/misc/cardreader/rtsx_pcr.c @@ -1654,6 +1654,7 @@ static int __maybe_unused rtsx_pci_suspend(struct device *dev_d) struct pci_dev *pcidev = to_pci_dev(dev_d); struct pcr_handle *handle = pci_get_drvdata(pcidev); struct rtsx_pcr *pcr = handle->pcr; + struct rtsx_slot *slot = &pcr->slots[RTSX_SD_CARD]; dev_dbg(&(pcidev->dev), "--> %s\n", __func__); @@ -1661,6 +1662,9 @@ static int __maybe_unused rtsx_pci_suspend(struct device *dev_d) mutex_lock(&pcr->pcr_mutex); + if (slot->p_dev && slot->power_off) + slot->power_off(slot->p_dev); + rtsx_pci_power_off(pcr, HOST_ENTER_S3, false); mutex_unlock(&pcr->pcr_mutex); @@ -1772,12 +1776,17 @@ static int rtsx_pci_runtime_suspend(struct device *device) struct pci_dev *pcidev = to_pci_dev(device); struct pcr_handle *handle = pci_get_drvdata(pcidev); struct rtsx_pcr *pcr = handle->pcr; + struct rtsx_slot *slot = &pcr->slots[RTSX_SD_CARD]; dev_dbg(device, "--> %s\n", __func__); cancel_delayed_work_sync(&pcr->carddet_work); mutex_lock(&pcr->pcr_mutex); + + if (slot->p_dev && slot->power_off) + slot->power_off(slot->p_dev); + rtsx_pci_power_off(pcr, HOST_ENTER_S3, true); mutex_unlock(&pcr->pcr_mutex); diff --git a/drivers/mmc/host/rtsx_pci_sdmmc.c b/drivers/mmc/host/rtsx_pci_sdmmc.c index 5d3599ee06bff..b847d79d4d8cc 100644 --- a/drivers/mmc/host/rtsx_pci_sdmmc.c +++ b/drivers/mmc/host/rtsx_pci_sdmmc.c @@ -47,6 +47,7 @@ struct realtek_pci_sdmmc { }; static int sdmmc_init_sd_express(struct mmc_host *mmc, struct mmc_ios *ios); +static int sd_power_on(struct realtek_pci_sdmmc *host, unsigned char power_mode); static inline struct device *sdmmc_dev(struct realtek_pci_sdmmc *host) { @@ -821,6 +822,15 @@ static void sd_request(struct work_struct *work) rtsx_pci_start_run(pcr); + if (host->prev_power_state == MMC_POWER_OFF) { + err = sd_power_on(host, MMC_POWER_ON); + if (err) { + cmd->error = err; + mutex_unlock(&pcr->pcr_mutex); + goto finish; + } + } + rtsx_pci_switch_clock(pcr, host->clock, host->ssc_depth, host->initial_mode, host->double_clk, host->vpclk); rtsx_pci_write_register(pcr, CARD_SELECT, 0x07, SD_MOD_SEL); @@ -1481,6 +1491,16 @@ static void rtsx_pci_sdmmc_card_event(struct platform_device *pdev) mmc_detect_change(host->mmc, 0); } +static void rtsx_pci_sdmmc_power_off(struct platform_device *pdev) +{ + struct realtek_pci_sdmmc *host = platform_get_drvdata(pdev); + + if (!host) + return; + + host->prev_power_state = MMC_POWER_OFF; +} + static int rtsx_pci_sdmmc_drv_probe(struct platform_device *pdev) { struct mmc_host *mmc; @@ -1513,6 +1533,7 @@ static int rtsx_pci_sdmmc_drv_probe(struct platform_device *pdev) platform_set_drvdata(pdev, host); pcr->slots[RTSX_SD_CARD].p_dev = pdev; pcr->slots[RTSX_SD_CARD].card_event = rtsx_pci_sdmmc_card_event; + pcr->slots[RTSX_SD_CARD].power_off = rtsx_pci_sdmmc_power_off; mutex_init(&host->host_mutex); @@ -1544,6 +1565,7 @@ static void rtsx_pci_sdmmc_drv_remove(struct platform_device *pdev) pcr = host->pcr; pcr->slots[RTSX_SD_CARD].p_dev = NULL; pcr->slots[RTSX_SD_CARD].card_event = NULL; + pcr->slots[RTSX_SD_CARD].power_off = NULL; mmc = host->mmc; cancel_work_sync(&host->work); diff --git a/include/linux/rtsx_common.h b/include/linux/rtsx_common.h index da9c8c6b5d50f..f294f478f0c0e 100644 --- a/include/linux/rtsx_common.h +++ b/include/linux/rtsx_common.h @@ -32,6 +32,7 @@ struct platform_device; struct rtsx_slot { struct platform_device *p_dev; void (*card_event)(struct platform_device *p_dev); + void (*power_off)(struct platform_device *p_dev); }; #endif -- 2.47.3