From nobody Tue Apr 7 18:02:09 2026 Received: from mail-wr1-f47.google.com (mail-wr1-f47.google.com [209.85.221.47]) (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 8F2E51D9663 for ; Thu, 12 Mar 2026 12:16:05 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.221.47 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1773317770; cv=none; b=PitjBw2J7obVBm1KvR2THVDPUuScYbtPYb9QFLBWFGHyKMW2y/Ohm+KuhcFT8BriEVLwFg1MLGc9RYwGi8+2kvpTofMz+56L8qoWoxjYQ6wq4E53e/gcWHgiNsIvLI4E5K36RC1Dw17d7t5mFWyWxu84e/MqufdaCbD3kKpqLXw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1773317770; c=relaxed/simple; bh=q7K+v3piZJcrosJPTRdRPWUfVCCMq202FgtLyEjDs48=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=H7i3OSXcVDUCePyddBWHo06FWHGKUjQ4NZIJyhs/Ta5kbj8eFnicB2Nvf5TcnJKXIWYxIXZlThHHQ0wj8YGhs410d8PX5j0/79Hl7e/yXthu7w85RubrzEgba/MzDaWmUp9CK/q1W6AJzE2exHozOfOXxN+uE7lwnh/tHUWWgTU= 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=Cz+Al66g; arc=none smtp.client-ip=209.85.221.47 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="Cz+Al66g" Received: by mail-wr1-f47.google.com with SMTP id ffacd0b85a97d-439b6d9c981so693949f8f.1 for ; Thu, 12 Mar 2026 05:16:05 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=starlabs-systems.20230601.gappssmtp.com; s=20230601; t=1773317764; x=1773922564; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=dpvL5W3u/QtSEFCecru0A3tqQQejPltz3A2r1FTtM90=; b=Cz+Al66g7/kVsYoAqCwETrymvjChAVZgkMe4q6S33PNUsQOSFk8LLnAC4+Z/C1xvmi bfa8Z0JDSo+E1oExYEXqNsTKX74JhQnuTbmzRPQH6rUzFJKFR2wkXA5LMj1yBE+ouYUQ mUi5HmxNpON8X0G0f9RyqueYA/uMlHlXDBHb+xG5w6zuZKT+2HYR5osgpli64JyvVG97 Tv61MmcYRh3bTm9jWT7Al0se5l8EtaOyJt+ZN6c1Q0aY5uuN4W6ugT60Gnqp8UIzfGeU OuBtUqN34O3Gx3X3fmCr4YO/U2DJaxi8GBdsToiKeRxfxgV3juozqnck4KpGH2ioT+BJ yiPA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1773317764; x=1773922564; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=dpvL5W3u/QtSEFCecru0A3tqQQejPltz3A2r1FTtM90=; b=mdpDX86kxO7dRp9OCbcc+TO3P/dG+PL5mlKPM1r/WkU8VIq2SdcjBWEMcoy2lbV7TM VaCz5kHMI50iFNOsTLSqZNITygwTptHMYXk/X1srWZaLEZA1COmSp+ABhV1+zJ4ajVEO G7ntU/W6kFcLhqrANGdj9hcfsJMOdwCrMPcGZXB0BRGq4cp4bWsd5sETbpAw9y4r0LTe sjUFNYuzrALzM0h+Yi6BHO8v73ulDBwHoQudFtGerW5ubjxTOy1byvl4ED+D+x0FxTsP X5M1+GzVLow7UE044O4xt5A04DT9I3QJEURcn4vuWdmVqJFZ36MAsIaC6BlLt6HbAGFh Uzgw== X-Forwarded-Encrypted: i=1; AJvYcCW+BO0mm0uT3izsWLJPbXaxwcR7duI5QcVMZiWrflN3sN8V2jkltT9UOl0mgM1Om5o/rdpGb1m9lNZyYvw=@vger.kernel.org X-Gm-Message-State: AOJu0YyQekFSk1TsU93QlsO/JaF14XgueOuDtdCpFjQev+5yeIhZSVDi KDuKmcmOWwQ7qd8pcJj/25O7F+iiblZkJEplHa3lmNQmTd6LV3diEPizjBIZgFcnvw== X-Gm-Gg: ATEYQzw2NICG+1JhI31Dk+mojnaMCoXJ04eIayyfXgLdjxuA3NOMRUkbq552q6EdxA3 R9JZwUBleaqQ6uy0EAo9Xc4Yjh5OlSLcegJ1GSP/4EPUKBwZ3goR7YVb7ESBrGY4AvGrqPexCWz 63uvG/6aUd9Hy4mMDf0idHikiCBhoYolpadA9hlrP2lLXzcUdFjKFxt/YgAFJ+vuiPbDyu1+Evb Qtz7SOUIYB9eML2jpY3L7Pl789MUWzpCuN+S4hZsyGLvpiFRZM8Gel/J4zYmywYzV/lBcqaniTH cYAMqF7Ho0/kbIuHHAe0twFCW+NO814jUgE4lPUjalnSzfI1U1WxuB0SS1W/nmjqs/SbnZWoyPJ 28xcomFu4uz1QzwliYxtpohhQfK35GLkmHIrgvJcOxvocfxYiUnjHNDgKH9OrUVTLvL6W4FoLmf nWinK/rRlXFG0RIS3qU+bXa6BlHwR1KJh5A7G3XRj3dYoacLM7/nHZO+BCDYoQFj90n1kNIw== X-Received: by 2002:a5d:5f96:0:b0:439:c8b3:a3e9 with SMTP id ffacd0b85a97d-439fe194765mr6267301f8f.14.1773317763477; Thu, 12 Mar 2026 05:16:03 -0700 (PDT) Received: from starbook.localdomain ([212.105.129.204]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-439fe20c473sm8139874f8f.24.2026.03.12.05.16.02 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 12 Mar 2026 05:16:02 -0700 (PDT) From: Sean Rhodes To: linux-mmc@vger.kernel.org, Ulf Hansson , Greg Kroah-Hartman Cc: Ricky Wu , Avri Altman , Binbin Zhou , Dan Carpenter , Jisheng Zhang , Nathan Chancellor , Arnd Bergmann , Huacai Chen , Ingo Molnar , Thomas Gleixner , linux-kernel@vger.kernel.org Subject: [PATCH v2 RESEND 1/6] mmc: rtsx_usb_sdmmc: avoid false card-detect on tray readers Date: Thu, 12 Mar 2026 12:15:54 +0000 Message-ID: <20260312121559.19197-2-sean@starlabs.systems> X-Mailer: git-send-email 2.51.0 In-Reply-To: <20260312121559.19197-1-sean@starlabs.systems> References: <20260305194052.5120-1-sean@starlabs.systems> <20260312121559.19197-1-sean@starlabs.systems> 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 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 --- 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) --=20 2.51.0