Skip to content

mmc: Add quirk to disable DDR50 tuning #213

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 3 commits into
base: nilrt/master/6.6
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions drivers/mmc/core/card.h
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ struct mmc_fixup {
#define CID_MANFID_MICRON 0x13
#define CID_MANFID_SAMSUNG 0x15
#define CID_MANFID_APACER 0x27
#define CID_MANFID_SWISSBIT 0x5D
#define CID_MANFID_KINGSTON 0x70
#define CID_MANFID_HYNIX 0x90
#define CID_MANFID_KINGSTON_SD 0x9F
Expand Down Expand Up @@ -291,4 +292,9 @@ static inline int mmc_card_broken_sd_poweroff_notify(const struct mmc_card *c)
return c->quirks & MMC_QUIRK_BROKEN_SD_POWEROFF_NOTIFY;
}

static inline int mmc_card_no_uhs_ddr50_tuning(const struct mmc_card *c)
{
return c->quirks & MMC_QUIRK_NO_UHS_DDR50_TUNING;
}

#endif
10 changes: 10 additions & 0 deletions drivers/mmc/core/quirks.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,16 @@ static const struct mmc_fixup __maybe_unused mmc_sd_fixups[] = {
MMC_QUIRK_BROKEN_SD_CACHE | MMC_QUIRK_BROKEN_SD_POWEROFF_NOTIFY,
EXT_CSD_REV_ANY),

/*
* Swissbit series S46-u cards throw I/O errors during tuning requests
* after the initial tuning request expectedly times out. This has
* only been observed on cards manufactured on 01/2019 that are using
* Bay Trail host controllers.
*/
_FIXUP_EXT("0016G", CID_MANFID_SWISSBIT, 0x5342, 2019, 1,
0, -1ull, SDIO_ANY_ID, SDIO_ANY_ID, add_quirk_sd,
MMC_QUIRK_NO_UHS_DDR50_TUNING, EXT_CSD_REV_ANY),

END_FIXUP
};

Expand Down
32 changes: 24 additions & 8 deletions drivers/mmc/core/sd.c
Original file line number Diff line number Diff line change
Expand Up @@ -618,6 +618,29 @@ static int sd_set_current_limit(struct mmc_card *card, u8 *status)
return 0;
}

/*
* Determine if the card should tune or not.
*/
static bool mmc_sd_use_tuning(struct mmc_card *card)
{
/*
* SPI mode doesn't define CMD19 and tuning is only valid for SDR50 and
* SDR104 mode SD-cards. Note that tuning is mandatory for SDR104.
*/
if (mmc_host_is_spi(card->host))
return false;

switch (card->host->ios.timing) {
case MMC_TIMING_UHS_SDR50:
case MMC_TIMING_UHS_SDR104:
return true;
case MMC_TIMING_UHS_DDR50:
return !mmc_card_no_uhs_ddr50_tuning(card);
}

return false;
}

/*
* UHS-I specific initialization procedure
*/
Expand Down Expand Up @@ -661,14 +684,7 @@ static int mmc_sd_init_uhs_card(struct mmc_card *card)
if (err)
goto out;

/*
* SPI mode doesn't define CMD19 and tuning is only valid for SDR50 and
* SDR104 mode SD-cards. Note that tuning is mandatory for SDR104.
*/
if (!mmc_host_is_spi(card->host) &&
(card->host->ios.timing == MMC_TIMING_UHS_SDR50 ||
card->host->ios.timing == MMC_TIMING_UHS_DDR50 ||
card->host->ios.timing == MMC_TIMING_UHS_SDR104)) {
if (mmc_sd_use_tuning(card)) {
err = mmc_execute_tuning(card);

/*
Expand Down
34 changes: 10 additions & 24 deletions drivers/mmc/host/sdhci.c
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@
#include <linux/mmc/sdio.h>
#include <linux/mmc/slot-gpio.h>

#include "../core/host.h"
#include "sdhci.h"

#define DRIVER_NAME "sdhci"
Expand Down Expand Up @@ -305,7 +304,7 @@ static void sdhci_set_default_irqs(struct sdhci_host *host)

if (host->tuning_mode == SDHCI_TUNING_MODE_2 ||
host->tuning_mode == SDHCI_TUNING_MODE_3)
host->ier |= SDHCI_INT_RETUNE | SDHCI_INT_TUNING_ERR;
host->ier |= SDHCI_INT_RETUNE;

sdhci_writel(host, host->ier, SDHCI_INT_ENABLE);
sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE);
Expand Down Expand Up @@ -3450,12 +3449,18 @@ static void sdhci_data_irq(struct sdhci_host *host, u32 intmask)
host->data->error = -EILSEQ;
if (!mmc_op_tuning(SDHCI_GET_CMD(sdhci_readw(host, SDHCI_COMMAND))))
sdhci_err_stats_inc(host, DAT_CRC);
} else if ((intmask & SDHCI_INT_DATA_CRC) &&
} else if ((intmask & (SDHCI_INT_DATA_CRC | SDHCI_INT_TUNING_ERROR)) &&
SDHCI_GET_CMD(sdhci_readw(host, SDHCI_COMMAND))
!= MMC_BUS_TEST_R) {
host->data->error = -EILSEQ;
if (!mmc_op_tuning(SDHCI_GET_CMD(sdhci_readw(host, SDHCI_COMMAND))))
sdhci_err_stats_inc(host, DAT_CRC);
if (intmask & SDHCI_INT_TUNING_ERROR) {
u16 ctrl2 = sdhci_readw(host, SDHCI_HOST_CONTROL2);

ctrl2 &= ~SDHCI_CTRL_TUNED_CLK;
sdhci_writew(host, ctrl2, SDHCI_HOST_CONTROL2);
}
} else if (intmask & SDHCI_INT_ADMA_ERROR) {
pr_err("%s: ADMA error: 0x%08x\n", mmc_hostname(host->mmc),
intmask);
Expand Down Expand Up @@ -3602,24 +3607,6 @@ static irqreturn_t sdhci_irq(int irq, void *dev_id)
if (intmask & SDHCI_INT_RETUNE)
mmc_retune_needed(host->mmc);

if (intmask & SDHCI_INT_TUNING_ERR) {
u16 ctrl2 = sdhci_readw(host, SDHCI_HOST_CONTROL2);
/*
* Only complain and retune if we're actually using the
* host's tuning circuits.
*/
if (ctrl2 & SDHCI_CTRL_TUNED_CLK) {
sdhci_writew(host,
ctrl2 & ~SDHCI_CTRL_TUNED_CLK,
SDHCI_HOST_CONTROL2);
mmc_retune_recheck(host->mmc);
pr_err("%s: Unrecoverable error in tuning circuit\n",
mmc_hostname(host->mmc));
}
sdhci_writel(host, SDHCI_INT_TUNING_ERR,
SDHCI_INT_STATUS);
}

if ((intmask & SDHCI_INT_CARD_INT) &&
(host->ier & SDHCI_INT_CARD_INT)) {
sdhci_enable_sdio_irq_nolock(host, false);
Expand All @@ -3629,8 +3616,7 @@ static irqreturn_t sdhci_irq(int irq, void *dev_id)
intmask &= ~(SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE |
SDHCI_INT_CMD_MASK | SDHCI_INT_DATA_MASK |
SDHCI_INT_ERROR | SDHCI_INT_BUS_POWER |
SDHCI_INT_RETUNE | SDHCI_INT_TUNING_ERR |
SDHCI_INT_CARD_INT);
SDHCI_INT_RETUNE | SDHCI_INT_CARD_INT);

if (intmask) {
unexpected |= intmask;
Expand Down Expand Up @@ -4009,7 +3995,7 @@ bool sdhci_cqe_irq(struct sdhci_host *host, u32 intmask, int *cmd_error,
} else
*cmd_error = 0;

if (intmask & (SDHCI_INT_DATA_END_BIT | SDHCI_INT_DATA_CRC)) {
if (intmask & (SDHCI_INT_DATA_END_BIT | SDHCI_INT_DATA_CRC | SDHCI_INT_TUNING_ERROR)) {
*data_error = -EILSEQ;
if (!mmc_op_tuning(SDHCI_GET_CMD(sdhci_readw(host, SDHCI_COMMAND))))
sdhci_err_stats_inc(host, DAT_CRC);
Expand Down
4 changes: 2 additions & 2 deletions drivers/mmc/host/sdhci.h
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@
#define SDHCI_INT_BUS_POWER 0x00800000
#define SDHCI_INT_AUTO_CMD_ERR 0x01000000
#define SDHCI_INT_ADMA_ERROR 0x02000000
#define SDHCI_INT_TUNING_ERR 0x04000000
#define SDHCI_INT_TUNING_ERROR 0x04000000

#define SDHCI_INT_NORMAL_MASK 0x00007FFF
#define SDHCI_INT_ERROR_MASK 0xFFFF8000
Expand All @@ -170,7 +170,7 @@
SDHCI_INT_DATA_AVAIL | SDHCI_INT_SPACE_AVAIL | \
SDHCI_INT_DATA_TIMEOUT | SDHCI_INT_DATA_CRC | \
SDHCI_INT_DATA_END_BIT | SDHCI_INT_ADMA_ERROR | \
SDHCI_INT_BLK_GAP)
SDHCI_INT_BLK_GAP | SDHCI_INT_TUNING_ERROR)
#define SDHCI_INT_ALL_MASK ((unsigned int)-1)

#define SDHCI_CQE_INT_ERR_MASK ( \
Expand Down
1 change: 1 addition & 0 deletions include/linux/mmc/card.h
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,7 @@ struct mmc_card {
#define MMC_QUIRK_BROKEN_SD_CACHE (1<<15) /* Disable broken SD cache support */
#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 */

bool written_flag; /* Indicates eMMC has been written since power on */
bool reenable_cmdq; /* Re-enable Command Queue */
Expand Down
Loading