* [RFC PATCH 0/2] mmc: rtsx_usb_sdmmc: qualify card-detect on tray readers
@ 2026-05-13 11:40 Sean Rhodes
2026-05-13 11:40 ` [RFC PATCH 1/2] mmc: rtsx_usb_sdmmc: avoid false " Sean Rhodes
2026-05-13 11:40 ` [RFC PATCH 2/2] mmc: rtsx_usb_sdmmc: start card power-up at 3.3V Sean Rhodes
0 siblings, 2 replies; 3+ messages in thread
From: Sean Rhodes @ 2026-05-13 11:40 UTC (permalink / raw)
To: Ulf Hansson
Cc: Ricky Wu, Avri Altman, Sean Rhodes, Jisheng Zhang,
Nathan Chancellor, Dan Carpenter, Binbin Zhou, linux-mmc,
Huacai Chen, linux-kernel, linux-usb, gregkh, arnd, ulf.hansson,
adrian.hunter, rogerable, matthew.schwartz
Hi Uffe, USB/cardreader folks,
Uffe suggested sending this as an RFC and adding the USB/cardreader side,
because this may need input on where the media qualification should live.
I previously sent an RFC for the USB/cardreader runtime-PM side of this
problem. That did not get USB/cardreader-side guidance on the detect
layering question; the only substantive feedback was that the MMC child
should already own runtime PM for a real inserted card. I have since
retested that, and it is correct: with real media inserted,
rtsx_usb_sdmmc holds runtime_usage=1 and the USB parent remains active
through runtime_active_kids=1.
The remaining failure is narrower: empty tray only. Raw SD_CD remains
asserted, the MMC core repeatedly probes non-existent media, and the
runtime-PM hierarchy does not settle.
This is an RFC for the Realtek RTS5129 tray-reader false-detect issue.
Short version: on these machines, raw SD_CD is not a reliable "card is
present" signal. It is asserted when the tray is inserted, even if the tray
contains no SD card.
That means the current driver reports card-present to the MMC core for an
empty tray. The MMC core then does what it should do: it tries to initialize
a card. The commands time out, no mmcblk device is created, but because
SD_CD remains asserted the detect path is entered again. In practice this
keeps rtsx_usb_sdmmc runtime-active and prevents the USB parent from
autosuspending.
I retested this on an RTS5129 reader:
- tray removed: no mmcblk device, rtsx_usb_sdmmc suspends, USB parent
suspends
- empty tray inserted: no mmcblk device, repeated CMD0/CMD8/CMD55/CMD1
probe loop, rtsx_usb_sdmmc remains active, USB parent remains active
- tray + SD card: mmcblk0 appears and reads correctly; the MMC child
holds runtime PM as expected
The old Realtek rts5139 staging driver did not treat raw tray/CD state as
sufficient. It qualified insertion by checking whether media actually
responded before reporting a card present. That approach works on this
hardware, and avoids papering over the issue in runtime PM.
I know the previous version put the validation directly in ->get_cd(), and
the objection was that MMC command probing does not belong there. I'm not
trying to ignore that feedback. The question for this RFC is the layering:
- if the only exposed hardware bit is "tray inserted", not "card present",
where should the Realtek-specific media qualification live?
- is an rts5139-style qualification acceptable in rtsx_usb_sdmmc if it is
kept out of the generic MMC core?
- or should the USB/cardreader parent expose a qualified media-present
state to the SD/MMC child?
This is intentionally copied to linux-usb and the char/misc/cardreader
maintainers, not just linux-mmc, because one possible answer is that the
USB/cardreader parent should expose a qualified media-present state rather
than having rtsx_usb_sdmmc compensate for raw SD_CD.
Patch 2 keeps the validation path in a known initial electrical state by
starting SD power-up at 3.3V, matching the old Realtek rts5139 driver. It is
included because the validation sequence must not inherit a previous 1.8V
state.
I have deliberately left the SDR/UHS rate patches out of this RFC. They are
separate capability work and just make this harder to review. This series is
only about making tray card-detect correct and stopping the empty-tray detect
loop.
Thanks,
Sean
Sean Rhodes (2):
mmc: rtsx_usb_sdmmc: avoid false card-detect on tray readers
mmc: rtsx_usb_sdmmc: start card power-up at 3.3V
drivers/mmc/host/rtsx_usb_sdmmc.c | 161 ++++++++++++++++++++++++++++--
1 file changed, 153 insertions(+), 8 deletions(-)
^ permalink raw reply [flat|nested] 3+ messages in thread
* [RFC PATCH 1/2] mmc: rtsx_usb_sdmmc: avoid false card-detect on tray readers
2026-05-13 11:40 [RFC PATCH 0/2] mmc: rtsx_usb_sdmmc: qualify card-detect on tray readers Sean Rhodes
@ 2026-05-13 11:40 ` Sean Rhodes
2026-05-13 11:40 ` [RFC PATCH 2/2] mmc: rtsx_usb_sdmmc: start card power-up at 3.3V Sean Rhodes
1 sibling, 0 replies; 3+ messages in thread
From: Sean Rhodes @ 2026-05-13 11:40 UTC (permalink / raw)
To: Ulf Hansson
Cc: Ricky Wu, Avri Altman, Sean Rhodes, Jisheng Zhang,
Nathan Chancellor, Dan Carpenter, Binbin Zhou, linux-mmc,
Huacai Chen, linux-kernel, linux-usb, gregkh, arnd, ulf.hansson,
adrian.hunter, rogerable, matthew.schwartz
Some Realtek USB SD readers with a tray can assert SD_CD even when no
card is present. This can make the MMC core believe a card exists, and
may trigger unnecessary initialization and suspend failures.
Debounce the CD signal and validate a newly detected card by probing for
a response (CMD0 + CMD8/CMD55/CMD1) before reporting it present. Also
treat SD_INT as a removal indication even if SD_CD stays asserted.
Tested on a Realtek RTS5129 USB reader (0bda:0129):
- Tray inserted, no card: no mmc device created (hot-plug and cold boot)
- Tray + SDXC card: hot-plug and cold boot detect mmcblk0
Tested-by: Sean Rhodes <sean@starlabs.systems>
Signed-off-by: Sean Rhodes <sean@starlabs.systems>
---
drivers/mmc/host/rtsx_usb_sdmmc.c | 156 ++++++++++++++++++++++++++++--
1 file changed, 148 insertions(+), 8 deletions(-)
diff --git a/drivers/mmc/host/rtsx_usb_sdmmc.c b/drivers/mmc/host/rtsx_usb_sdmmc.c
index 84674659a84d..ec3eeea78e95 100644
--- a/drivers/mmc/host/rtsx_usb_sdmmc.c
+++ b/drivers/mmc/host/rtsx_usb_sdmmc.c
@@ -19,6 +19,7 @@
#include <linux/scatterlist.h>
#include <linux/pm.h>
#include <linux/pm_runtime.h>
+#include <linux/jiffies.h>
#include <linux/rtsx_usb.h>
#include <linux/unaligned.h>
@@ -30,6 +31,9 @@
#define RTSX_USB_USE_LEDS_CLASS
#endif
+#define RTSX_USB_SD_CD_DEBOUNCE_CNT 2
+#define RTSX_USB_SD_INSERT_RETRY_MS 1000
+
struct rtsx_usb_sdmmc {
struct platform_device *pdev;
struct rtsx_ucr *ucr;
@@ -46,6 +50,8 @@ struct rtsx_usb_sdmmc {
bool card_exist;
bool initial_mode;
bool ddr_mode;
+ u8 cd_debounce;
+ unsigned long next_insert_check;
unsigned char power_mode;
u16 ocp_stat;
@@ -72,6 +78,13 @@ static inline void sd_clear_error(struct rtsx_usb_sdmmc *host)
rtsx_usb_clear_fsm_err(ucr);
}
+static int sd_set_bus_width(struct rtsx_usb_sdmmc *host,
+ unsigned char bus_width);
+static int sd_set_timing(struct rtsx_usb_sdmmc *host,
+ unsigned char timing, bool *ddr_mode);
+static int sd_power_on(struct rtsx_usb_sdmmc *host);
+static int sd_power_off(struct rtsx_usb_sdmmc *host);
+
#ifdef DEBUG
static void sd_print_debug_regs(struct rtsx_usb_sdmmc *host)
{
@@ -768,12 +781,94 @@ static int sdmmc_get_ro(struct mmc_host *mmc)
return 0;
}
+static bool sdmmc_validate_insert_locked(struct rtsx_usb_sdmmc *host)
+{
+ struct rtsx_ucr *ucr = host->ucr;
+ struct mmc_command cmd = { };
+ int err = 0;
+ bool probe_powered = false;
+ bool ddr_mode = false;
+
+ /*
+ * Some readers with a tray assert the mechanical SD_CD pin even when no
+ * card is present. Only report a card present when it responds to a
+ * minimal reset/probe sequence, similar to the old rts5139 behavior.
+ *
+ * Must be called with ucr->dev_mutex held.
+ */
+ if (host->power_mode == MMC_POWER_OFF) {
+ err = sd_power_on(host);
+ if (err)
+ return false;
+ probe_powered = true;
+
+ /* Issue clock signals to card for at least 74 clocks. */
+ rtsx_usb_write_register(ucr, SD_BUS_STAT,
+ SD_CLK_TOGGLE_EN, SD_CLK_TOGGLE_EN);
+ usleep_range(200, 400);
+ rtsx_usb_write_register(ucr, SD_BUS_STAT, SD_CLK_TOGGLE_EN, 0);
+ }
+
+ /*
+ * Ensure the interface is in a safe, legacy / initial-clock mode before
+ * probing for a response. The MMC core may not have configured ios yet.
+ */
+ err = sd_set_bus_width(host, MMC_BUS_WIDTH_1);
+ if (err)
+ goto out;
+
+ err = sd_set_timing(host, MMC_TIMING_LEGACY, &ddr_mode);
+ if (err)
+ goto out;
+
+ ucr->cur_clk = 0;
+ err = rtsx_usb_switch_clock(ucr, 400000, SSC_DEPTH_512K,
+ true, true, false);
+ if (err)
+ goto out;
+
+ cmd.opcode = MMC_GO_IDLE_STATE;
+ cmd.arg = 0;
+ cmd.flags = MMC_RSP_NONE | MMC_CMD_BC;
+ sd_send_cmd_get_rsp(host, &cmd);
+
+ /* SD v2.0+: CMD8 */
+ cmd.opcode = SD_SEND_IF_COND;
+ cmd.arg = 0x1aa;
+ cmd.flags = MMC_RSP_R7 | MMC_CMD_BCR;
+ sd_send_cmd_get_rsp(host, &cmd);
+ if (!cmd.error)
+ goto out;
+
+ /* SD v1.x: CMD55 */
+ cmd.opcode = MMC_APP_CMD;
+ cmd.arg = 0;
+ cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
+ sd_send_cmd_get_rsp(host, &cmd);
+ if (!cmd.error)
+ goto out;
+
+ /* MMC: CMD1 */
+ cmd.opcode = MMC_SEND_OP_COND;
+ cmd.arg = 0;
+ cmd.flags = MMC_RSP_R3 | MMC_CMD_BCR;
+ sd_send_cmd_get_rsp(host, &cmd);
+
+out:
+ if (probe_powered)
+ sd_power_off(host);
+ return !err && !cmd.error;
+}
+
static int sdmmc_get_cd(struct mmc_host *mmc)
{
struct rtsx_usb_sdmmc *host = mmc_priv(mmc);
struct rtsx_ucr *ucr = host->ucr;
int err;
u16 val;
+ u8 pend;
+ bool sd_int = false;
+ bool cd_raw = false;
if (host->host_removal)
return -ENOMEDIUM;
@@ -782,28 +877,71 @@ static int sdmmc_get_cd(struct mmc_host *mmc)
/* Check SD card detect */
err = rtsx_usb_get_card_status(ucr, &val);
-
- mutex_unlock(&ucr->dev_mutex);
-
- /* Treat failed detection as non-exist */
if (err)
- goto no_card;
+ goto no_card_unlock;
/* get OCP status */
host->ocp_stat = (val >> 4) & 0x03;
- if (val & SD_CD) {
- host->card_exist = true;
+ cd_raw = !!(val & SD_CD);
+
+ /* Use SD_INT as a reliable removal indication on some tray readers. */
+ err = rtsx_usb_read_register(ucr, CARD_INT_PEND, &pend);
+ if (!err) {
+ sd_int = !!(pend & SD_INT);
+ if (sd_int)
+ rtsx_usb_write_register(ucr, CARD_INT_PEND,
+ SD_INT, SD_INT);
+ }
+
+ if (!cd_raw) {
+ host->cd_debounce = 0;
+ host->next_insert_check = 0;
+ goto no_card_unlock;
+ }
+
+ /*
+ * rts5139-style: when a card is already known present, treat SD_INT as
+ * a removal event even if SD_CD stays high (e.g. tray-based readers).
+ */
+ if (host->card_exist) {
+ if (sd_int) {
+ host->cd_debounce = 0;
+ host->next_insert_check = 0;
+ goto no_card_unlock;
+ }
+ mutex_unlock(&ucr->dev_mutex);
return 1;
}
-no_card:
+ /* Debounce mechanical CD before probing for a response. */
+ if (host->cd_debounce < RTSX_USB_SD_CD_DEBOUNCE_CNT) {
+ host->cd_debounce++;
+ goto no_card_unlock;
+ }
+
+ /* Avoid pounding the bus with probes if CD is stuck asserted. */
+ if (time_before(jiffies, host->next_insert_check))
+ goto no_card_unlock;
+
+ if (!sdmmc_validate_insert_locked(host)) {
+ host->next_insert_check = jiffies +
+ msecs_to_jiffies(RTSX_USB_SD_INSERT_RETRY_MS);
+ goto no_card_unlock;
+ }
+
+ host->card_exist = true;
+ mutex_unlock(&ucr->dev_mutex);
+ return 1;
+
+no_card_unlock:
/* clear OCP status */
if (host->ocp_stat & (MS_OCP_NOW | MS_OCP_EVER)) {
rtsx_usb_write_register(ucr, OCPCTL, MS_OCP_CLEAR, MS_OCP_CLEAR);
host->ocp_stat = 0;
}
host->card_exist = false;
+ mutex_unlock(&ucr->dev_mutex);
return 0;
}
@@ -1359,6 +1497,8 @@ static void rtsx_usb_init_host(struct rtsx_usb_sdmmc *host)
host->power_mode = MMC_POWER_OFF;
host->ocp_stat = 0;
+ host->cd_debounce = 0;
+ host->next_insert_check = 0;
}
static int rtsx_usb_sdmmc_drv_probe(struct platform_device *pdev)
^ permalink raw reply related [flat|nested] 3+ messages in thread
* [RFC PATCH 2/2] mmc: rtsx_usb_sdmmc: start card power-up at 3.3V
2026-05-13 11:40 [RFC PATCH 0/2] mmc: rtsx_usb_sdmmc: qualify card-detect on tray readers Sean Rhodes
2026-05-13 11:40 ` [RFC PATCH 1/2] mmc: rtsx_usb_sdmmc: avoid false " Sean Rhodes
@ 2026-05-13 11:40 ` Sean Rhodes
1 sibling, 0 replies; 3+ messages in thread
From: Sean Rhodes @ 2026-05-13 11:40 UTC (permalink / raw)
To: Ulf Hansson
Cc: Ricky Wu, Avri Altman, Sean Rhodes, Jisheng Zhang,
Nathan Chancellor, Dan Carpenter, Binbin Zhou, linux-mmc,
Huacai Chen, linux-kernel, linux-usb, gregkh, arnd, ulf.hansson,
adrian.hunter, rogerable, matthew.schwartz
Some tray-based readers keep SD_CD asserted even without a card. The
rtsx_usb_sdmmc driver now validates insertion with a minimal probe
sequence. That probe must start with the SD pads in 3.3V mode.
Like the old rts5139 driver (sd_init_power()), force the SD pads to
3.3V and tune the SD18 regulator to 3.3V before powering up the card.
This avoids spurious probe timeouts when the reader is left in 1.8V
from a previous UHS session.
Tested: Realtek RTS5129 (0bda:0129) + tray + Lexar 2TB SDXC
Tested: cold boot detects mmcblk0 (2026-02-24)
Tested: hotplug insert enumerates mmcblk0 (2026-02-23)
Signed-off-by: Sean Rhodes <sean@starlabs.systems>
---
drivers/mmc/host/rtsx_usb_sdmmc.c | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/drivers/mmc/host/rtsx_usb_sdmmc.c b/drivers/mmc/host/rtsx_usb_sdmmc.c
index ec3eeea78e95..6be98926387d 100644
--- a/drivers/mmc/host/rtsx_usb_sdmmc.c
+++ b/drivers/mmc/host/rtsx_usb_sdmmc.c
@@ -1108,6 +1108,11 @@ static int sd_power_on(struct rtsx_usb_sdmmc *host)
}
dev_dbg(sdmmc_dev(host), "%s\n", __func__);
rtsx_usb_init_cmd(ucr);
+ /* Start SD init at 3.3V, like the old rts5139 driver. */
+ rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SD_PAD_CTL,
+ SD_IO_USING_1V8, SD_IO_USING_3V3);
+ rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, LDO_POWER_CFG,
+ TUNE_SD18_MASK, TUNE_SD18_3V3);
rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_SELECT, 0x07, SD_MOD_SEL);
rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_SHARE_MODE,
CARD_SHARE_MASK, CARD_SHARE_SD);
^ permalink raw reply related [flat|nested] 3+ messages in thread
end of thread, other threads:[~2026-05-13 11:40 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-05-13 11:40 [RFC PATCH 0/2] mmc: rtsx_usb_sdmmc: qualify card-detect on tray readers Sean Rhodes
2026-05-13 11:40 ` [RFC PATCH 1/2] mmc: rtsx_usb_sdmmc: avoid false " Sean Rhodes
2026-05-13 11:40 ` [RFC PATCH 2/2] mmc: rtsx_usb_sdmmc: start card power-up at 3.3V Sean Rhodes
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox