From ec1ef20dbf0684a8414bf54c1cdb419059f9473c Mon Sep 17 00:00:00 2001 From: Jonathan Bell Date: Thu, 5 Mar 2026 09:59:08 +0000 Subject: [PATCH 1/4] mmc: sd: add fall-back extension register parsing attempt Several types of cards have a common bug where the vendor did not populate the General Information block, but extension registers are present at assumed function offsets, and the features are correctly implemented. Probe the offsets to see if valid data is returned. Signed-off-by: Jonathan Bell --- drivers/mmc/core/sd.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c index 551553a44d9784..fe45adfcafb286 100644 --- a/drivers/mmc/core/sd.c +++ b/drivers/mmc/core/sd.c @@ -1218,6 +1218,26 @@ static int mmc_sd_read_ext_regs(struct mmc_card *card) goto out; } + /* Some cards have zeroes in GEN_INFO but correctly implement EXT_PERF and EXT_PWR */ + if (!memcmp(card->ext_reg_buf, gen_info_buf, 512)) { + pr_info("%s: using fall-back extension register parsing\n", + mmc_hostname(card->host)); + /* PWR typically hard-coded at FNO=1 */ + err = sd_parse_ext_reg_power(card, 1, 0, 0); + if (err) { + pr_err("%s: error %d parsing SD Power extension\n", + mmc_hostname(card->host), err); + goto out; + } + /* PERF typically hard-coded at FNO=2 */ + err = sd_parse_ext_reg_perf(card, 2, 0, 0); + if (err) { + pr_err("%s: error %d parsing SD Performance extension\n", + mmc_hostname(card->host), err); + } + goto out; + } + /* General info structure revision. */ memcpy(&rev, &gen_info_buf[0], 2); From 397d0e65941df16ad0deee589e2f0f3e7ad6437f Mon Sep 17 00:00:00 2001 From: Jonathan Bell Date: Thu, 5 Mar 2026 10:34:40 +0000 Subject: [PATCH 2/4] mmc: sd: add quirk for buggy CMD49 handling Several card OEMs, mainly using Silicon Motion SM270x flash controllers, have a deficient CMD49 handler that throws away length/offset and applies the whole block to the addressed extension register page. Add a quirk to do a read-modify-write cycle for any extension register write. Signed-off-by: Jonathan Bell --- drivers/mmc/core/sd_ops.c | 19 ++++++++++++++++--- include/linux/mmc/card.h | 1 + 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/drivers/mmc/core/sd_ops.c b/drivers/mmc/core/sd_ops.c index 47a1903268b2e0..4f11be42d9636d 100644 --- a/drivers/mmc/core/sd_ops.c +++ b/drivers/mmc/core/sd_ops.c @@ -421,10 +421,23 @@ int mmc_sd_write_ext_reg(struct mmc_card *card, u8 fno, u8 page, u16 offset, * [17:9] offset address. * [8:0] length (0 = 1 byte). */ - cmd.arg = fno << 27 | page << 18 | offset << 9; + if (card->quirks & MMC_QUIRK_NONSTD_SD_CMD49) { + int err; + /* + * Card ignores length/offset and always applies + * all 512B of the write data block. RmW cycle required. + */ + err = mmc_sd_read_ext_reg(card, fno, page, 0, 512, reg_buf); + if (err) + return err; - /* The first byte in the buffer is the data to be written. */ - reg_buf[0] = reg_data; + cmd.arg = fno << 27 | page << 18; + reg_buf[offset] = reg_data; + } else { + cmd.arg = fno << 27 | page << 18 | offset << 9; + /* The first byte in the buffer is the data to be written. */ + reg_buf[0] = reg_data; + } data.flags = MMC_DATA_WRITE; data.blksz = 512; diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h index a6c766c157bd54..eeb6a39200c25e 100644 --- a/include/linux/mmc/card.h +++ b/include/linux/mmc/card.h @@ -296,6 +296,7 @@ struct mmc_card { #define MMC_QUIRK_BROKEN_CACHE_FLUSH (1<<16) /* Don't flush cache until the write has occurred */ #define MMC_QUIRK_BROKEN_SD_POWEROFF_NOTIFY (1<<17) /* Disable broken SD poweroff notify support */ #define MMC_QUIRK_NO_UHS_DDR50_TUNING (1<<18) /* Disable DDR50 tuning */ +#define MMC_QUIRK_NONSTD_SD_CMD49 (1<<29) /* SD card ignores length/offset argument */ #define MMC_QUIRK_WORKING_SD_CQ (1<<30) /* SD card has known-good CQ implementation */ #define MMC_QUIRK_ERASE_BROKEN (1<<31) /* Skip erase */ From 87a401c3816cba68096ed55bfd5e86b61fd8a077 Mon Sep 17 00:00:00 2001 From: Jonathan Bell Date: Thu, 5 Mar 2026 11:05:56 +0000 Subject: [PATCH 3/4] mmc: sd: Transcend cards need a CMD49 quirk These cards have the same broken CMD49 behaviour across 465T/460T/240I product lines. Add the Manufacturer ID and match on CID fields that remain consistent across these types. Signed-off-by: Jonathan Bell --- drivers/mmc/core/card.h | 1 + drivers/mmc/core/quirks.h | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/drivers/mmc/core/card.h b/drivers/mmc/core/card.h index a36cda051d160c..12b484b97efd3b 100644 --- a/drivers/mmc/core/card.h +++ b/drivers/mmc/core/card.h @@ -92,6 +92,7 @@ struct mmc_fixup { #define CID_MANFID_APACER 0x27 #define CID_MANFID_SWISSBIT 0x5D #define CID_MANFID_KINGSTON 0x70 +#define CID_MANFID_TRANSCEND_SD 0x74 #define CID_MANFID_HYNIX 0x90 #define CID_MANFID_KINGSTON_SD 0x9F #define CID_MANFID_LONGSYS_SD 0xAD diff --git a/drivers/mmc/core/quirks.h b/drivers/mmc/core/quirks.h index f23319a5fb79bc..3afed284078f85 100644 --- a/drivers/mmc/core/quirks.h +++ b/drivers/mmc/core/quirks.h @@ -76,6 +76,11 @@ static const struct mmc_fixup __maybe_unused mmc_sd_fixups[] = { 0, -1ull, SDIO_ANY_ID, SDIO_ANY_ID, add_quirk_sd, MMC_QUIRK_BROKEN_SD_CACHE, EXT_CSD_REV_ANY), + /* Transcend cards need a CMD49 workaround */ + _FIXUP_EXT("USDU1", CID_MANFID_TRANSCEND_SD, 0x4a60, CID_YEAR_ANY, CID_MONTH_ANY, + cid_rev(2, 0, 0, 0), -1ull, SDIO_ANY_ID, SDIO_ANY_ID, add_quirk_sd, + MMC_QUIRK_NONSTD_SD_CMD49, EXT_CSD_REV_ANY), + /* SD A2 allow-list - only trust CQ on these cards */ /* Raspberry Pi A2 cards */ _FIXUP_EXT(CID_NAME_ANY, CID_MANFID_LONGSYS_SD, 0x4c53, CID_YEAR_ANY, CID_MONTH_ANY, From da91f3ed57f4bf7d9716a359fd9143224f33b734 Mon Sep 17 00:00:00 2001 From: Jonathan Bell Date: Thu, 5 Mar 2026 13:33:14 +0000 Subject: [PATCH 4/4] mmc: sd: Biwin cards need a CMD49 quirk The Biwin MS160 line of cards have a broken CMD49 handler and require this workaround. The CID match is somewhat broad, but the card name appears to be unique to these cards. Signed-off-by: Jonathan Bell --- drivers/mmc/core/card.h | 1 + drivers/mmc/core/quirks.h | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/drivers/mmc/core/card.h b/drivers/mmc/core/card.h index 12b484b97efd3b..1a9c8fa5adb775 100644 --- a/drivers/mmc/core/card.h +++ b/drivers/mmc/core/card.h @@ -90,6 +90,7 @@ struct mmc_fixup { #define CID_MANFID_SAMSUNG 0x15 #define CID_MANFID_SAMSUNG_SD 0x1b #define CID_MANFID_APACER 0x27 +#define CID_MANFID_BIWIN_SD 0x4E #define CID_MANFID_SWISSBIT 0x5D #define CID_MANFID_KINGSTON 0x70 #define CID_MANFID_TRANSCEND_SD 0x74 diff --git a/drivers/mmc/core/quirks.h b/drivers/mmc/core/quirks.h index 3afed284078f85..8ea9b0248490e4 100644 --- a/drivers/mmc/core/quirks.h +++ b/drivers/mmc/core/quirks.h @@ -81,6 +81,11 @@ static const struct mmc_fixup __maybe_unused mmc_sd_fixups[] = { cid_rev(2, 0, 0, 0), -1ull, SDIO_ANY_ID, SDIO_ANY_ID, add_quirk_sd, MMC_QUIRK_NONSTD_SD_CMD49, EXT_CSD_REV_ANY), + /* Certain Biwin cards need a CMD49 workaround */ + _FIXUP_EXT("SMI ", CID_MANFID_BIWIN_SD, 0x4257, CID_YEAR_ANY, CID_MONTH_ANY, + cid_rev(1, 0, 2025, 0), -1ull, SDIO_ANY_ID, SDIO_ANY_ID, add_quirk_sd, + MMC_QUIRK_NONSTD_SD_CMD49, EXT_CSD_REV_ANY), + /* SD A2 allow-list - only trust CQ on these cards */ /* Raspberry Pi A2 cards */ _FIXUP_EXT(CID_NAME_ANY, CID_MANFID_LONGSYS_SD, 0x4c53, CID_YEAR_ANY, CID_MONTH_ANY,