From nobody Fri Apr 3 11:13:18 2026 Received: from mail-wm1-f42.google.com (mail-wm1-f42.google.com [209.85.128.42]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id EC8A1318BAC for ; Thu, 19 Feb 2026 20:39:54 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.42 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771533598; cv=none; b=fxm/hoUrHYN19FeYnTh9d5WoSR/uvwDREXBaL9yo2AdNbB3xOkVUS3Q52t7FCy5qvZe2777BWO6JE0/6UQ8+wJdqKwIXVxc8HOxTp5arjx3Rh5nQ+F1xzOIFuYITdtshz0oTWZtnnzbn06NRagwhG405AgF3OnS1gr7ZE0iEaiw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771533598; c=relaxed/simple; bh=G+UmSvJ/mogFhvBvrBYtM1J+Zu9ud3Q0gu1d/a4pkLA=; h=From:To:Cc:Subject:Date:Message-ID:MIME-Version; b=NEb0ZRJJk9k2ifcq5AFajSBC10bgika+nI0A/qacy5eyOPuoTEp408TNG7bskmbnAWpkxTsN5Zvmyd8vvBDdbpXMCOc0lwapwzeaQlbpREPD9i00Z8sapoxRCsrStCDN3jEsRGBdLQQaEbEittOrr3bUAxVjEKFkKDgTMvW8BPY= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=starlabs.systems; spf=pass smtp.mailfrom=starlabs.systems; dkim=pass (2048-bit key) header.d=starlabs-systems.20230601.gappssmtp.com header.i=@starlabs-systems.20230601.gappssmtp.com header.b=bgCYjOID; arc=none smtp.client-ip=209.85.128.42 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=starlabs.systems Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=starlabs.systems Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=starlabs-systems.20230601.gappssmtp.com header.i=@starlabs-systems.20230601.gappssmtp.com header.b="bgCYjOID" Received: by mail-wm1-f42.google.com with SMTP id 5b1f17b1804b1-4837907f535so12593165e9.3 for ; Thu, 19 Feb 2026 12:39:54 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=starlabs-systems.20230601.gappssmtp.com; s=20230601; t=1771533593; x=1772138393; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:from:to:cc:subject:date:message-id:reply-to; bh=fpZqiageYAEzhLdcOQIy6H2gADhn8PAzdRurtJ0NPdk=; b=bgCYjOID26/JYGU/XUgSsVmlTzPpfFHjjMG+XcbA0wKvvd7YCi8Yd7jbsVOqk7oI8r zQJIjzmmA2gInVCT8yzYC83bfBuuKPnb48Ozfj/gBFaZS8CKGOi7iqcLNmonIuXCKDNo 9KeQmIpf1upySjioydn/HaSSjWAqWG/NS7Ud35ataeNhWMnslwG68/2PaNH/ps0qRukE gH74bRs4f8uiuWCvD2nBpfgun+zlA8QjOuoeUxE/VnRoXjoVp4j5L4nHH5m2SAtLgR8H wnDwFoLGZaYmn4+7iVavgIprL4rJY9vDw9vSYnT1hCrNm018MMf8RyYAMxq68oISWL8w e2xw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1771533593; x=1772138393; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:x-gm-gg:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to; bh=fpZqiageYAEzhLdcOQIy6H2gADhn8PAzdRurtJ0NPdk=; b=sXZgWQNgcgcfJW3dLdEHR343TzdP98YnenQkFJeocZpULZIjFFl76bO2GsSkEMkIaF LQnJ4sVZ3KY29Dzrgw7exNFd9T+r7YrvSHXcu3kHHsRG6RoXiqfJEUISj68joMh4XgE6 /bPwTWeXCqwa0toxj31Uqo7JuzqkbgAvEK+U8xG6lSW7QYZ2Uwc6l4GJoKt1WcuLUUSn xYXZNTAFZzuQrRhl7/A6v1a7AR+FiO6xPJiwjlGLIfPXd32eOXI8hqwd9kKWvTNnFAcW Q9/RQ4ka2GWjpAnbHe7qJymQzpbr45VzamLpkr3CRoyaXGou4SsD7BN5++CmJOC3HnNi XtJg== X-Gm-Message-State: AOJu0Yweg2s1efuTEacA+qTzpiC0b5xJAVhh5vjHgV2Dwuj0mROqFqiK 5kL285Gb+RMD200vbT4hMp/YMvEgJseEvnSDNIPTcHIhXAzYVWWlHbQntsvdIOFUFQK1P+2BhET JnWk= X-Gm-Gg: AZuq6aJTcfJBELLpjNUkJnsicJT11+vDCFRJ2bu7hzpPkDEMuzpvYYFLKHj3aKQQkRS h+q3XEwCGnPOGYfBoh62U/VMXbE18GY4U6AKLs3SYN3XLTQug4+0U7Snkd60wdtx4sMuWRKdEXX DVXaxqYv2huV2LCez/mji+YZPzLbg2RGyijVvsAldUh6OkB0Zpu7yiZX02psk8q5l1z+zLXzEEM Ok1koP7mMwjv2UwwvY2q9qwXM+4cS6L6v2ow5dHrqJci1RWAnKtOAcB4/Sxv4DUdd5MTDDHOXeE Uj8SOY1ypEF1fUH/TKLD5q4XXuQHN6GbIolpZveC48BdDPiQFcmDr5loYX8vRFE/dA9fiklNdVu +XRoLMHM/RKCHyeW010IejRHSz1qIj0RVfgvMcw0+2eVHEOCUsxtRgv3YNr8i6Va01QtftOEapm RClMB3GLUvRNRQ7DZx39CQc4h3QE0wXBIX7pQNIC1TW7703vh5AVg= X-Received: by 2002:a05:600c:444f:b0:471:1765:839c with SMTP id 5b1f17b1804b1-48379bd7414mr301491845e9.20.1771533592908; Thu, 19 Feb 2026 12:39:52 -0800 (PST) Received: from starbook ([217.155.46.38]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-483a31efe02sm24947825e9.10.2026.02.19.12.39.52 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 19 Feb 2026 12:39:52 -0800 (PST) From: Sean Rhodes To: linux-kernel@vger.kernel.org Cc: Ulf Hansson , Ricky Wu , Avri Altman , Dan Carpenter , Nathan Chancellor , Sean Rhodes , Jisheng Zhang , Binbin Zhou , linux-mmc@vger.kernel.org Subject: [PATCH] mmc: rtsx_usb_sdmmc: avoid false card-detect on tray readers Date: Thu, 19 Feb 2026 20:39:51 +0000 Message-ID: <1ca7b488a11e03b3f107f1829a40cf2c92c7d5fd.1771533586.git.sean@starlabs.systems> X-Mailer: git-send-email 2.51.0 Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" 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) with tray inserted and no card. No mmc device was created, and suspend pm_test=3Ddevices passed. Tested-by: Sean Rhodes Signed-off-by: Sean Rhodes --- 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 #include #include +#include =20 #include #include @@ -30,6 +31,9 @@ #define RTSX_USB_USE_LEDS_CLASS #endif =20 +#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; =20 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); } =20 +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; } =20 +static bool sdmmc_validate_insert_locked(struct rtsx_usb_sdmmc *host) +{ + struct rtsx_ucr *ucr =3D host->ucr; + struct mmc_command cmd =3D { }; + int err =3D 0; + bool probe_powered =3D false; + bool ddr_mode =3D 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 =3D=3D MMC_POWER_OFF) { + err =3D sd_power_on(host); + if (err) + return false; + probe_powered =3D 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 =3D sd_set_bus_width(host, MMC_BUS_WIDTH_1); + if (err) + goto out; + + err =3D sd_set_timing(host, MMC_TIMING_LEGACY, &ddr_mode); + if (err) + goto out; + + ucr->cur_clk =3D 0; + err =3D rtsx_usb_switch_clock(ucr, 400000, SSC_DEPTH_512K, + true, true, false); + if (err) + goto out; + + cmd.opcode =3D MMC_GO_IDLE_STATE; + cmd.arg =3D 0; + cmd.flags =3D MMC_RSP_NONE | MMC_CMD_BC; + sd_send_cmd_get_rsp(host, &cmd); + + /* SD v2.0+: CMD8 */ + cmd.opcode =3D SD_SEND_IF_COND; + cmd.arg =3D 0x1aa; + cmd.flags =3D MMC_RSP_R7 | MMC_CMD_BCR; + sd_send_cmd_get_rsp(host, &cmd); + if (!cmd.error) + goto out; + + /* SD v1.x: CMD55 */ + cmd.opcode =3D MMC_APP_CMD; + cmd.arg =3D 0; + cmd.flags =3D MMC_RSP_R1 | MMC_CMD_AC; + sd_send_cmd_get_rsp(host, &cmd); + if (!cmd.error) + goto out; + + /* MMC: CMD1 */ + cmd.opcode =3D MMC_SEND_OP_COND; + cmd.arg =3D 0; + cmd.flags =3D 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 =3D mmc_priv(mmc); struct rtsx_ucr *ucr =3D host->ucr; int err; u16 val; + u8 pend; + bool sd_int =3D false; + bool cd_raw =3D false; =20 if (host->host_removal) return -ENOMEDIUM; @@ -782,28 +877,71 @@ static int sdmmc_get_cd(struct mmc_host *mmc) =20 /* Check SD card detect */ err =3D 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; =20 /* get OCP status */ host->ocp_stat =3D (val >> 4) & 0x03; =20 - if (val & SD_CD) { - host->card_exist =3D true; + cd_raw =3D !!(val & SD_CD); + + /* Use SD_INT as a reliable removal indication on some tray readers. */ + err =3D rtsx_usb_read_register(ucr, CARD_INT_PEND, &pend); + if (!err) { + sd_int =3D !!(pend & SD_INT); + if (sd_int) + rtsx_usb_write_register(ucr, CARD_INT_PEND, + SD_INT, SD_INT); + } + + if (!cd_raw) { + host->cd_debounce =3D 0; + host->next_insert_check =3D 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 =3D 0; + host->next_insert_check =3D 0; + goto no_card_unlock; + } + mutex_unlock(&ucr->dev_mutex); return 1; } =20 -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 =3D jiffies + + msecs_to_jiffies(RTSX_USB_SD_INSERT_RETRY_MS); + goto no_card_unlock; + } + + host->card_exist =3D 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 =3D 0; } host->card_exist =3D false; + mutex_unlock(&ucr->dev_mutex); return 0; } =20 @@ -1359,6 +1497,8 @@ static void rtsx_usb_init_host(struct rtsx_usb_sdmmc = *host) =20 host->power_mode =3D MMC_POWER_OFF; host->ocp_stat =3D 0; + host->cd_debounce =3D 0; + host->next_insert_check =3D 0; } =20 static int rtsx_usb_sdmmc_drv_probe(struct platform_device *pdev)