From: Sasha Levin <sashal@kernel.org>
To: patches@lists.linux.dev, stable@vger.kernel.org
Cc: Matthew Schwartz <matthew.schwartz@linux.dev>,
Greg Kroah-Hartman <gregkh@linuxfoundation.org>,
Sasha Levin <sashal@kernel.org>,
ulf.hansson@linaro.org, linux-kernel@vger.kernel.org,
linux-mmc@vger.kernel.org
Subject: [PATCH AUTOSEL 6.19-6.1] mmc: rtsx: reset power state on suspend
Date: Wed, 18 Feb 2026 21:03:53 -0500 [thread overview]
Message-ID: <20260219020422.1539798-17-sashal@kernel.org> (raw)
In-Reply-To: <20260219020422.1539798-1-sashal@kernel.org>
From: Matthew Schwartz <matthew.schwartz@linux.dev>
[ Upstream commit eac85fbd0867c25ac517f58fae401d65c627edff ]
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 <matthew.schwartz@linux.dev>
Link: https://patch.msgid.link/20260105060236.400366-2-matthew.schwartz@linux.dev
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
LLM Generated explanations, may be completely bogus:
Good - `sd_power_on` has been part of this file since initial creation.
The forward declaration added in the patch is just to allow the function
(defined later in the file) to be called from `sd_request` (defined
earlier).
### Analysis Summary
**What problem the commit solves:**
This commit fixes a suspend/resume regression in Realtek PCI-E SD card
readers where, after suspend, 20% of resume attempts fail with DMA
transfer timeouts because:
1. During suspend, `rtsx_pci_power_off()` powers down the card reader
hardware
2. But the sdmmc driver's `prev_power_state` remains `MMC_POWER_ON`
3. On resume, when `sd_power_on()` is called, it sees `prev_power_state
== MMC_POWER_ON` at line 912 and returns immediately without
reinitializing the hardware
4. The first I/O attempt then fails with DMA timeout because the
hardware was never re-powered
**The fix has three parts:**
1. Adds a `power_off` callback to `struct rtsx_slot` (1 line in header)
2. In the PCR suspend paths (both system and runtime), calls
`slot->power_off()` to notify the sdmmc driver before powering off
hardware
3. In the sdmmc driver: implements `rtsx_pci_sdmmc_power_off()` to reset
`prev_power_state = MMC_POWER_OFF`, and adds a check in
`sd_request()` to call `sd_power_on()` if `prev_power_state ==
MMC_POWER_OFF`
**Stable kernel criteria assessment:**
- **Fixes a real bug:** Yes - 20% failure rate on resume with SD card
operations
- **Obviously correct:** Yes - the logic is straightforward: notify the
driver that hardware was powered off so it reinitializes properly
- **Small and contained:** Yes - 32 lines of insertion across 3 files,
all within the rtsx subsystem
- **No new features:** Correct - this adds only internal plumbing to fix
power state tracking
- **Tested:** Author tested 300+ s2idle cycles
- **Impact:** Users with Realtek card readers experience I/O errors,
filesystem journal failures, and card detection loss after
suspend/resume
**Risk assessment:**
- Low risk - the changes are confined to the rtsx card reader subsystem
- The `power_off` callback is properly guarded with NULL checks (`if
(slot->p_dev && slot->power_off)`)
- The `sd_request` power-on check has proper error handling
- The drv_remove function properly clears the callback
- No impact on other subsystems
**Dependencies:**
- Self-contained - no dependencies on other commits
- The companion delay patch (aced969e9bf37) has already been selected
for stable and is independent
- The code being modified (`rtsx_slot`, `rtsx_pci_suspend`,
`sd_request`) exists in all recent stable trees
### Verification
- **Verified** that `sd_power_on()` at line 912 returns early when
`prev_power_state == MMC_POWER_ON`, confirming the bug mechanism
- **Verified** via `git show aced969e9bf37` that the companion patch
(delay increase) has a Fixes: tag and was independently picked for
stable
- **Verified** via `git show ed0d7f8559dbd` (stable branch version) that
the delay patch was already applied to stable with `[Upstream commit
...]` marker
- **Verified** via lore.kernel.org that this is a 2-patch series (0/2
cover letter), both fixing suspend/resume failures, with testing
showing patch 1 reduces failures from 20% to 4%
- **Verified** that `struct rtsx_slot` currently only has `p_dev` and
`card_event` fields by reading `include/linux/rtsx_common.h`
- **Verified** that the suspend paths properly hold `pcr->pcr_mutex`
when calling the callback
- **Verified** the NULL check guards (`slot->p_dev && slot->power_off`)
in both suspend functions
- **Verified** that `sd_power_on()` has existed since the driver was
created (`ff984e57d36e8`) - the forward declaration is just for
compilation order
- **Verified** that `rtsx_pci_sdmmc_drv_remove` properly clears
`power_off = NULL` to prevent stale callbacks
This is a clear, well-tested bug fix for a user-impactful suspend/resume
issue. It's small, contained, properly guarded, and fixes a 20% failure
rate. It meets all stable kernel criteria.
**YES**
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 8df60000b5b41..34343b5d5823d 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);
@@ -1522,6 +1532,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;
@@ -1554,6 +1574,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);
@@ -1585,6 +1606,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.51.0
next prev parent reply other threads:[~2026-02-19 2:04 UTC|newest]
Thread overview: 45+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-02-19 2:03 [PATCH AUTOSEL 6.19] rust_binder: Fix build failure if !CONFIG_COMPAT Sasha Levin
2026-02-19 2:03 ` [PATCH AUTOSEL 6.19-6.12] usb: chipidea: udc: fix DMA and SG cleanup in _ep_nuke() Sasha Levin
2026-02-19 2:03 ` [PATCH AUTOSEL 6.19-5.15] staging: rtl8723bs: fix memory leak on failure path Sasha Levin
2026-02-19 2:03 ` [PATCH AUTOSEL 6.19] tty: vt/keyboard: Split apart vt_do_diacrit() Sasha Levin
2026-02-19 2:03 ` [PATCH AUTOSEL 6.19-5.10] fix it87_wdt early reboot by reporting running timer Sasha Levin
2026-02-19 2:03 ` [PATCH AUTOSEL 6.19-5.15] misc: eeprom: Fix EWEN/EWDS/ERAL commands for 93xx56 and 93xx66 Sasha Levin
2026-02-19 2:03 ` [PATCH AUTOSEL 6.19-5.15] mmc: rtsx_pci: add quirk to disable MMC_CAP_AGGRESSIVE_PM for RTS525A Sasha Levin
2026-02-19 10:29 ` Ulf Hansson
2026-02-26 13:23 ` Sasha Levin
2026-02-19 2:03 ` [PATCH AUTOSEL 6.19-6.1] fpga: of-fpga-region: Fail if any bridge is missing Sasha Levin
2026-02-19 2:03 ` [PATCH AUTOSEL 6.19-6.12] soundwire: intel_auxdevice: add cs42l45 codec to wake_capable_list Sasha Levin
2026-02-19 2:03 ` [PATCH AUTOSEL 6.19-5.10] iio: magnetometer: Remove IRQF_ONESHOT Sasha Levin
2026-02-19 2:03 ` [PATCH AUTOSEL 6.19-6.1] watchdog: imx7ulp_wdt: handle the nowayout option Sasha Levin
2026-02-19 2:03 ` [PATCH AUTOSEL 6.19-5.10] serial: 8250_dw: handle clock enable errors in runtime_resume Sasha Levin
2026-02-19 2:03 ` [PATCH AUTOSEL 6.19-6.12] most: core: fix resource leak in most_register_interface error paths Sasha Levin
2026-02-19 2:03 ` [PATCH AUTOSEL 6.19] block: fix partial IOVA mapping cleanup in blk_rq_dma_map_iova Sasha Levin
2026-02-19 2:03 ` [PATCH AUTOSEL 6.19-6.1] misc: bcm_vk: Fix possible null-pointer dereferences in bcm_vk_read() Sasha Levin
2026-02-19 2:03 ` [PATCH AUTOSEL 6.19-6.1] dmaengine: sun6i: Choose appropriate burst length under maxburst Sasha Levin
2026-02-19 2:03 ` Sasha Levin [this message]
2026-02-19 10:27 ` [PATCH AUTOSEL 6.19-6.1] mmc: rtsx: reset power state on suspend Ulf Hansson
2026-02-26 13:24 ` Sasha Levin
2026-02-19 2:03 ` [PATCH AUTOSEL 6.19] serial: rsci: Add set_rtrg() callback Sasha Levin
2026-02-19 2:03 ` [PATCH AUTOSEL 6.19-5.10] Revert "mfd: da9052-spi: Change read-mask to write-mask" Sasha Levin
2026-02-19 2:03 ` [PATCH AUTOSEL 6.19-6.18] pinctrl: mediatek: make devm allocations safer and clearer in mtk_eint_do_init() Sasha Levin
2026-02-19 2:03 ` [PATCH AUTOSEL 6.19-6.12] serial: 8250: 8250_omap.c: Add support for handling UART error conditions Sasha Levin
2026-02-19 2:03 ` [PATCH AUTOSEL 6.19-6.12] usb: gadget: f_fs: Fix ioctl error handling Sasha Levin
2026-02-19 2:03 ` [PATCH AUTOSEL 6.19-6.12] phy: cadence-torrent: restore parent clock for refclk during resume Sasha Levin
2026-02-19 2:04 ` [PATCH AUTOSEL 6.19-5.10] binder: don't use %pK through printk Sasha Levin
2026-02-19 2:04 ` [PATCH AUTOSEL 6.19-6.18] iio: bmi270_i2c: Add MODULE_DEVICE_TABLE for BMI260/270 Sasha Levin
2026-02-19 2:04 ` [PATCH AUTOSEL 6.19-5.15] iio: Use IRQF_NO_THREAD Sasha Levin
2026-02-19 2:04 ` [PATCH AUTOSEL 6.19-6.12] mfd: intel-lpss: Add Intel Nova Lake-S PCI IDs Sasha Levin
2026-02-19 2:04 ` [PATCH AUTOSEL 6.19-6.12] phy: ti: phy-j721e-wiz: restore mux selection during resume Sasha Levin
2026-02-19 2:04 ` [PATCH AUTOSEL 6.19-5.10] MIPS: Loongson: Make cpumask_of_node() robust against NUMA_NO_NODE Sasha Levin
2026-02-19 2:04 ` [PATCH AUTOSEL 6.19-6.12] usb: gadget: f_fs: fix DMA-BUF OUT queues Sasha Levin
2026-02-19 2:04 ` [PATCH AUTOSEL 6.19-5.10] phy: fsl-imx8mq-usb: disable bind/unbind platform driver feature Sasha Levin
2026-02-19 2:04 ` [PATCH AUTOSEL 6.19-6.18] watchdog: rzv2h_wdt: Discard pm_runtime_put() return value Sasha Levin
2026-02-19 2:04 ` [PATCH AUTOSEL 6.19-6.1] soundwire: dmi-quirks: add mapping for Avell B.ON (OEM rebranded of NUC15) Sasha Levin
2026-02-19 2:04 ` [PATCH AUTOSEL 6.19-6.18] pinctrl: renesas: rzt2h: Allow .get_direction() for IRQ function GPIOs Sasha Levin
2026-02-19 2:04 ` [PATCH AUTOSEL 6.19-6.12] dmaengine: stm32-dma3: use module_platform_driver Sasha Levin
2026-02-19 2:04 ` [PATCH AUTOSEL 6.19-5.15] staging: rtl8723bs: fix missing status update on sdio_alloc_irq() failure Sasha Levin
2026-02-19 2:04 ` [PATCH AUTOSEL 6.19-5.15] phy: mvebu-cp110-utmi: fix dr_mode property read from dts Sasha Levin
2026-02-19 2:04 ` [PATCH AUTOSEL 6.19-6.1] usb: typec: ucsi: psy: Fix voltage and current max for non-Fixed PDOs Sasha Levin
2026-02-19 2:04 ` [PATCH AUTOSEL 6.19-5.10] serial: 8250: 8250_omap.c: Clear DMA RX running status only after DMA termination is done Sasha Levin
2026-02-19 2:04 ` [PATCH AUTOSEL 6.19-6.1] dmaengine: stm32-mdma: initialize m2m_hw_period and ccr to fix warnings Sasha Levin
2026-02-19 2:04 ` [PATCH AUTOSEL 6.19-6.18] misc: ti_fpc202: fix a potential memory leak in probe function Sasha Levin
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260219020422.1539798-17-sashal@kernel.org \
--to=sashal@kernel.org \
--cc=gregkh@linuxfoundation.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-mmc@vger.kernel.org \
--cc=matthew.schwartz@linux.dev \
--cc=patches@lists.linux.dev \
--cc=stable@vger.kernel.org \
--cc=ulf.hansson@linaro.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox