* [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