[PATCH v2 RESEND 1/6] mmc: rtsx_usb_sdmmc: avoid false card-detect on tray readers

Sean Rhodes posted 6 patches 3 weeks, 5 days ago
[PATCH v2 RESEND 1/6] mmc: rtsx_usb_sdmmc: avoid false card-detect on tray readers
Posted by Sean Rhodes 3 weeks, 5 days ago
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.

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: 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>
---
 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)
-- 
2.51.0
Re: [PATCH v2 RESEND 1/6] mmc: rtsx_usb_sdmmc: avoid false card-detect on tray readers
Posted by Ulf Hansson 2 weeks ago
+ 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@linux.dev/

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
Re: [PATCH v2 RESEND 1/6] mmc: rtsx_usb_sdmmc: avoid false card-detect on tray readers
Posted by Sean Rhodes 2 weeks ago
Hi Uffe

Thanks for the feedback - to answer your questions:

> Would you mind elaborating on this so I can better understand the problem?

SD_CD is used for presence, but that's asserted by trays or blanking cards.

> What prevents the MMC core from handling this correctly?
>
> Don't some of the commands time out, eventually causing mmc_rescan() to bail out?

It does bail out, but SD_CD is still asserted, so it tries again. The
main thing this breaks is S3 entry - it's either slow (like 100
seconds), or never gets there.

> 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?

They fail, and we use that result.
On Tue, 24 Mar 2026 at 11:26, Ulf Hansson <ulf.hansson@linaro.org> 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@linux.dev/
>
> 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
Re: [PATCH v2 RESEND 1/6] mmc: rtsx_usb_sdmmc: avoid false card-detect on tray readers
Posted by Ulf Hansson 2 weeks ago
On Tue, 24 Mar 2026 at 12:59, Sean Rhodes <sean@starlabs.systems> wrote:
>
> Hi Uffe
>
> Thanks for the feedback - to answer your questions:
>
> > Would you mind elaborating on this so I can better understand the problem?
>
> SD_CD is used for presence, but that's asserted by trays or blanking cards.
>
> > What prevents the MMC core from handling this correctly?
> >
> > Don't some of the commands time out, eventually causing mmc_rescan() to bail out?
>
> It does bail out, but SD_CD is still asserted, so it tries again. The
> main thing this breaks is S3 entry - it's either slow (like 100
> seconds), or never gets there.

First, please don't top post, but reply in-line below so we can follow
the discussion.

It's the mmc driver's ->runtime_resume() callback that calls
mmc_detect_change(host->mmc, 0).

It's sounds to me like the mmc host is being runtime resumed/suspended
over and over again, hence we end up calling mmc_detect_change() many
times when it's not really needed. Right?

>
> > 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?
>
> They fail, and we use that result.
> On Tue, 24 Mar 2026 at 11:26, Ulf Hansson <ulf.hansson@linaro.org> 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@linux.dev/
> >

[...]

Kind regards
Uffe
Re: [PATCH v2 RESEND 1/6] mmc: rtsx_usb_sdmmc: avoid false card-detect on tray readers
Posted by Sean Rhodes 2 weeks ago
On Tue, 24 Mar 2026 at 12:23, Ulf Hansson <ulf.hansson@linaro.org> wrote:
>
> On Tue, 24 Mar 2026 at 12:59, Sean Rhodes <sean@starlabs.systems> wrote:
> >
> > Hi Uffe
> >
> > Thanks for the feedback - to answer your questions:
> >
> > > Would you mind elaborating on this so I can better understand the problem?
> >
> > SD_CD is used for presence, but that's asserted by trays or blanking cards.
> >
> > > What prevents the MMC core from handling this correctly?
> > >
> > > Don't some of the commands time out, eventually causing mmc_rescan() to bail out?
> >
> > It does bail out, but SD_CD is still asserted, so it tries again. The
> > main thing this breaks is S3 entry - it's either slow (like 100
> > seconds), or never gets there.
>
> First, please don't top post, but reply in-line below so we can follow
> the discussion.
>
> It's the mmc driver's ->runtime_resume() callback that calls
> mmc_detect_change(host->mmc, 0).
>
> It's sounds to me like the mmc host is being runtime resumed/suspended
> over and over again, hence we end up calling mmc_detect_change() many
> times when it's not really needed. Right?
>

Correct

> >
> > > 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?
> >
> > They fail, and we use that result.
> > On Tue, 24 Mar 2026 at 11:26, Ulf Hansson <ulf.hansson@linaro.org> 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@linux.dev/
> > >
>
> [...]
>
> Kind regards
> Uffe
Re: [PATCH v2 RESEND 1/6] mmc: rtsx_usb_sdmmc: avoid false card-detect on tray readers
Posted by Ulf Hansson 2 weeks ago
On Tue, 24 Mar 2026 at 13:30, Sean Rhodes <sean@starlabs.systems> wrote:
>
> On Tue, 24 Mar 2026 at 12:23, Ulf Hansson <ulf.hansson@linaro.org> wrote:
> >
> > On Tue, 24 Mar 2026 at 12:59, Sean Rhodes <sean@starlabs.systems> wrote:
> > >
> > > Hi Uffe
> > >
> > > Thanks for the feedback - to answer your questions:
> > >
> > > > Would you mind elaborating on this so I can better understand the problem?
> > >
> > > SD_CD is used for presence, but that's asserted by trays or blanking cards.
> > >
> > > > What prevents the MMC core from handling this correctly?
> > > >
> > > > Don't some of the commands time out, eventually causing mmc_rescan() to bail out?
> > >
> > > It does bail out, but SD_CD is still asserted, so it tries again. The
> > > main thing this breaks is S3 entry - it's either slow (like 100
> > > seconds), or never gets there.
> >
> > First, please don't top post, but reply in-line below so we can follow
> > the discussion.
> >
> > It's the mmc driver's ->runtime_resume() callback that calls
> > mmc_detect_change(host->mmc, 0).
> >
> > It's sounds to me like the mmc host is being runtime resumed/suspended
> > over and over again, hence we end up calling mmc_detect_change() many
> > times when it's not really needed. Right?
> >
>
> Correct

So why does the rtsx usb card driver do that?

Could it keep the mmc host runtime resumed for a while and then we
could add a debounce period to the call to mmc_detect_change()?

Kind regards
Uffe
Re: [PATCH v2 RESEND 1/6] mmc: rtsx_usb_sdmmc: avoid false card-detect on tray readers
Posted by Sean Rhodes 2 weeks ago
On Tue, 24 Mar 2026 at 12:58, Ulf Hansson <ulf.hansson@linaro.org> wrote:
>
> On Tue, 24 Mar 2026 at 13:30, Sean Rhodes <sean@starlabs.systems> wrote:
> >
> > On Tue, 24 Mar 2026 at 12:23, Ulf Hansson <ulf.hansson@linaro.org> wrote:
> > >
> > > On Tue, 24 Mar 2026 at 12:59, Sean Rhodes <sean@starlabs.systems> wrote:
> > > >
> > > > Hi Uffe
> > > >
> > > > Thanks for the feedback - to answer your questions:
> > > >
> > > > > Would you mind elaborating on this so I can better understand the problem?
> > > >
> > > > SD_CD is used for presence, but that's asserted by trays or blanking cards.
> > > >
> > > > > What prevents the MMC core from handling this correctly?
> > > > >
> > > > > Don't some of the commands time out, eventually causing mmc_rescan() to bail out?
> > > >
> > > > It does bail out, but SD_CD is still asserted, so it tries again. The
> > > > main thing this breaks is S3 entry - it's either slow (like 100
> > > > seconds), or never gets there.
> > >
> > > First, please don't top post, but reply in-line below so we can follow
> > > the discussion.
> > >
> > > It's the mmc driver's ->runtime_resume() callback that calls
> > > mmc_detect_change(host->mmc, 0).
> > >
> > > It's sounds to me like the mmc host is being runtime resumed/suspended
> > > over and over again, hence we end up calling mmc_detect_change() many
> > > times when it's not really needed. Right?
> > >
> >
> > Correct
>
> So why does the rtsx usb card driver do that?

I don't know; I'd assume it was just a shortcut or done by people who
didn't have the hardware when moving the driver out of staging (see
00d8521dcd236d1b8f664f54a0309e96bfdcb4f9 which works flawlessly)

>
> Could it keep the mmc host runtime resumed for a while and then we
> could add a debounce period to the call to mmc_detect_change()?

I might be missing something, but wouldn't that just make symptoms
less obvious? We have bad detection - why not fix that?

>
> Kind regards
> Uffe
Re: [PATCH v2 RESEND 1/6] mmc: rtsx_usb_sdmmc: avoid false card-detect on tray readers
Posted by Ulf Hansson 2 weeks ago
On Tue, 24 Mar 2026 at 14:14, Sean Rhodes <sean@starlabs.systems> wrote:
>
> On Tue, 24 Mar 2026 at 12:58, Ulf Hansson <ulf.hansson@linaro.org> wrote:
> >
> > On Tue, 24 Mar 2026 at 13:30, Sean Rhodes <sean@starlabs.systems> wrote:
> > >
> > > On Tue, 24 Mar 2026 at 12:23, Ulf Hansson <ulf.hansson@linaro.org> wrote:
> > > >
> > > > On Tue, 24 Mar 2026 at 12:59, Sean Rhodes <sean@starlabs.systems> wrote:
> > > > >
> > > > > Hi Uffe
> > > > >
> > > > > Thanks for the feedback - to answer your questions:
> > > > >
> > > > > > Would you mind elaborating on this so I can better understand the problem?
> > > > >
> > > > > SD_CD is used for presence, but that's asserted by trays or blanking cards.
> > > > >
> > > > > > What prevents the MMC core from handling this correctly?
> > > > > >
> > > > > > Don't some of the commands time out, eventually causing mmc_rescan() to bail out?
> > > > >
> > > > > It does bail out, but SD_CD is still asserted, so it tries again. The
> > > > > main thing this breaks is S3 entry - it's either slow (like 100
> > > > > seconds), or never gets there.
> > > >
> > > > First, please don't top post, but reply in-line below so we can follow
> > > > the discussion.
> > > >
> > > > It's the mmc driver's ->runtime_resume() callback that calls
> > > > mmc_detect_change(host->mmc, 0).
> > > >
> > > > It's sounds to me like the mmc host is being runtime resumed/suspended
> > > > over and over again, hence we end up calling mmc_detect_change() many
> > > > times when it's not really needed. Right?
> > > >
> > >
> > > Correct
> >
> > So why does the rtsx usb card driver do that?
>
> I don't know; I'd assume it was just a shortcut or done by people who
> didn't have the hardware when moving the driver out of staging (see
> 00d8521dcd236d1b8f664f54a0309e96bfdcb4f9 which works flawlessly)
>
> >
> > Could it keep the mmc host runtime resumed for a while and then we
> > could add a debounce period to the call to mmc_detect_change()?
>
> I might be missing something, but wouldn't that just make symptoms
> less obvious? We have bad detection - why not fix that?
>

I don't think so. Assuming the host gets runtime resumed at some point
again, after we failed to detect a card. Not sure if this solves the
problem, but should just make things less noisy, I think.

Anyway, you may very well be right, but I don't know how to fix this
properly on the usb side.

That said, we might need input from the usb maintainers on how to
proceed. Perhaps if you make a new submission (RFC), by describing the
problem in more detail and ask for guidance from the usb folkz?

Kind regards
Uffe
Re: [PATCH v2 RESEND 1/6] mmc: rtsx_usb_sdmmc: avoid false card-detect on tray readers
Posted by Ulf Hansson 2 weeks ago
+ Matthew (again)

On Tue, 24 Mar 2026 at 12:26, Ulf Hansson <ulf.hansson@linaro.org> 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@linux.dev/
>
> 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