diff --git a/drivers/mmc/core/card.h b/drivers/mmc/core/card.h index a36cda051d160c..1a9c8fa5adb775 100644 --- a/drivers/mmc/core/card.h +++ b/drivers/mmc/core/card.h @@ -90,8 +90,10 @@ 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 #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..8ea9b0248490e4 100644 --- a/drivers/mmc/core/quirks.h +++ b/drivers/mmc/core/quirks.h @@ -76,6 +76,16 @@ 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), + + /* 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, 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); 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 */