From nobody Thu Apr 9 21:57:23 2026 Received: from mail-wm1-f46.google.com (mail-wm1-f46.google.com [209.85.128.46]) (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 CACAD2E7185 for ; Thu, 5 Mar 2026 19:40:56 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.46 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772739658; cv=none; b=cZOsOxW+GejBHvv56obxmb3Y8FF1XOZDoBm73yKcF1QX4Mw7wCw/aEGazow5RJcEW6ZyBChGjWhCvnsEEHGIQkJ6Kd+1AkPALnMx4htf0szVFaMK1Yd+bm2TASdOXGIpOk42q4+uIAsMPpfgd1Jl6Paai/yxtCM6imygmGL1IB0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772739658; c=relaxed/simple; bh=q7K+v3piZJcrosJPTRdRPWUfVCCMq202FgtLyEjDs48=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=XVvu1m+4Y3ZyKlYMYqL+L6+L8mgWmYSkN+IVHY2ghEnsvd5Xv24/2MCHb12xUE7irJ0aSeQ1jPzZcxNh0v5IbqLjPEEyZwp6tVWXXZPJD/evhs+wkshrxdbLKgI5nx6g+OOqQcRMXqBmdLXQu571ByQ/3aI3oT0ksr2e3tR8hq4= 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=pfhiXIq3; arc=none smtp.client-ip=209.85.128.46 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="pfhiXIq3" Received: by mail-wm1-f46.google.com with SMTP id 5b1f17b1804b1-48375f1defeso63664025e9.0 for ; Thu, 05 Mar 2026 11:40:56 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=starlabs-systems.20230601.gappssmtp.com; s=20230601; t=1772739655; x=1773344455; 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=pfhiXIq3nAWzGUbYE3clcn08V6DXlp25FuGdXfFAA61qibpPF3K6MsB7G9JdxJlabR 2TsJIFJFDOi5BnaZNRwSnMGadvwrm4ZAaR/AoC97OkQWzdnCf9ZuMZim3sNuFOCClAwO +KlwlsNsztvoFbs8UBdH4YZtCNVhm3bVTf6X7Z4sbrAh1Iq/IB30HpJSRvQm5NiMJ5qf uzggkG/w6gXKoTobLK5nlAWz4/CYVGcA5vFgneGK66WRkgus6sHB8PCqeALjavVWkNTu wPk1p3CTd9767WAh6aOLv/89H/bCaujAqi9UoAJcgO776y55Al8lBUUA7dEsHnFPEqEs cTLw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1772739655; x=1773344455; 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=fM0KbuCEhSX96Y4j32176ENi2ecSXQljrR2/2CT5ecqJEYMoIEP1vSufKK4c12klLu loFhifImyepfWrRFPjkVJXoil+YWAYnOQVFwdiWmzYppXVtGSjNhWUD/6Hqx7KhX/4mR rL8Q+ChiJN5kExi/YF5N2RTIAC1+xDrYLx5dtlpblTNqJ9+JfGt7BpxKY7wqaf8956AP ElwAsmQLYnI6ViMguhxOuoM5mZM9FLP9Q5EW2WuM4V9m2v4O7jyNpvsjB0HkDDSSB19S WlLbJosPDXDaXO3LCuuNGavNPZYyFutu0nafSYNWHsNCEYdTAAxPQuIMMvghmRv+rf6x bx8w== X-Forwarded-Encrypted: i=1; AJvYcCVrStiS6TJCHDGphNgGrFsWOpmhjbA0SP4xq5t/yi/XI9sdKk+Ff4goFAaWvKNeK9MMKjH4BujQQ7eFzms=@vger.kernel.org X-Gm-Message-State: AOJu0YwYMPh87vJF+cn8IZNaU3fMzlNGOBP2ciP72dqdNWq4X7K/s4kY 38/xwjbSCk5IRbTT08qY4Ctg29EvKv3nRqinGEaNqoUHMwNh/nJFKrf2dngynZ0kcA== X-Gm-Gg: ATEYQzxlWW3i+O546+gheW1EGGuZRd6cDVImBOgCrlLxIOwdfHSpTF+t0LET0mZzHtT gzM9fSmGEcmWxaFm2z3XtG2r/f5C5PPn5etXcXj+yIN2mx43Z27ETBDCRBdrBCld1JqkRgMU4ZU eHyrgTmZ1m6b4UDwT8sSKQW8JPCpnh7Qe7tlm/ybYIl4SdUPb8nk0g/rGOZbbTLccqHrgkesXbx gFqENrPOU45IH4pT8b8/T/lcmnyHD6zqR7XnmXtykhwNr+gycQ9o5kKFaJPbNjvZOg/W29OddF3 zrGOI4NnXuOl5AHjJWwLP9G+/HK1DGdQ9Muun4Ql+EQ3jND4gMP73HGS35iDLnzNS6u0zWkk+Nn 7ewaTLg+NaEPq5K1HsPPDeTur73h5HLg7OF8qiQUnPuHQIQBDUSydGXFxMgJUT8GW94qUXXV5bu 7SthyXzePY8+L6uIMIt7hwj/OHXywomuulT0aAqWSwSRGaq12qfzb/kf0aHCdD6w== X-Received: by 2002:a05:600c:5490:b0:480:4a90:1af2 with SMTP id 5b1f17b1804b1-48523610816mr9841645e9.35.1772739655147; Thu, 05 Mar 2026 11:40:55 -0800 (PST) Received: from starbook ([217.155.46.38]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-485246fd127sm3265e9.6.2026.03.05.11.40.54 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 05 Mar 2026 11:40:54 -0800 (PST) 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 1/6] mmc: rtsx_usb_sdmmc: avoid false card-detect on tray readers Date: Thu, 5 Mar 2026 19:40:47 +0000 Message-ID: <20260305194052.5120-2-sean@starlabs.systems> X-Mailer: git-send-email 2.51.0 In-Reply-To: <20260305194052.5120-1-sean@starlabs.systems> References: <1ca7b488a11e03b3f107f1829a40cf2c92c7d5fd.1771533586.git.sean@starlabs.systems> <20260305194052.5120-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