Re: [PATCH v2 RESEND 1/6] mmc: rtsx_usb_sdmmc: avoid false card-detect on tray readers
From: Ulf Hansson
Date: Tue Mar 24 2026 - 07:28:02 EST
+ Matthew Schwartz
On Thu, 12 Mar 2026 at 13:16, Sean Rhodes <sean@starlabs.systems> wrote:
>
> 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
> trigger unnecessary initialization and suspend/shutdown failures.
Would you mind elaborating on this so I can better understand the problem?
>
> 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.
What prevents the MMC core from handling this correctly?
Don't some of the commands time out, eventually causing mmc_rescan()
to bail out?
>
> Tested: Realtek RTS5129 (0bda:0129), tray inserted, no card (2026-02-24)
> Tested: Realtek RTS5129 (0bda:0129), tray + SDXC card, mmcblk0 (2026-02-24)
>
> Signed-off-by: Sean Rhodes <sean@starlabs.systems>
I have looped in Matthew who recently worked on some similar problems
for the PCI variant [1] of the driver. Perhaps he and Ricky have some
input on this series too.
[1]
https://lore.kernel.org/all/20260105060236.400366-1-matthew.schwartz@xxxxxxxxx/
Please see a few more comments below.
> ---
> 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;
> +}
No, the above code doesn't belong in an MMC host driver.
I will try to guide you toward a better solution, but first I need to
understand the problem better.
I guess one key piece of information I'm missing is what happens when
the mmc_recsan() work tries to send commands to initialize a card,
when there is no card inserted?
> +
> 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)
> --
> 2.51.0
Kind regards
Uffe