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:01 EST


+ Matthew (again)

On Tue, 24 Mar 2026 at 12:26, Ulf Hansson <ulf.hansson@xxxxxxxxxx> wrote:
>
> + 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