diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 85dc8cd6d647..724a574ed4e9 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -30,6 +30,7 @@ #include #include #include +#include #include "sdhci.h" @@ -1522,6 +1523,34 @@ static bool sdhci_check_state(struct sdhci_host *host) return false; } +static bool sdhci_check_auto_tuning(struct sdhci_host *host, + struct mmc_command *cmd) +{ + if (((cmd->opcode != MMC_READ_SINGLE_BLOCK) && + (cmd->opcode != MMC_READ_MULTIPLE_BLOCK) && + (cmd->opcode != SD_IO_RW_EXTENDED)) || (host->clock < 100000000)) + return false; + else if (host->mmc->ios.timing == MMC_TIMING_MMC_HS200 || + host->mmc->ios.timing == MMC_TIMING_UHS_SDR104) + return true; + else + return false; +} + +static int sdhci_get_tuning_cmd(struct sdhci_host *host) +{ + if (!host->mmc || !host->mmc->card) + return 0; + /* + * If we are here, all conditions have already been true + * and the card can either be an eMMC or SD/SDIO + */ + if (mmc_card_mmc(host->mmc->card)) + return MMC_SEND_TUNING_BLOCK_HS200; + else + return MMC_SEND_TUNING_BLOCK; +} + static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq) { struct sdhci_host *host; @@ -1583,6 +1612,15 @@ static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq) host->mrq->cmd->error = -ENOMEDIUM; tasklet_schedule(&host->finish_tasklet); } else { + if (host->ops->config_auto_tuning_cmd) { + if (sdhci_check_auto_tuning(host, mrq->cmd)) + host->ops->config_auto_tuning_cmd(host, true, + sdhci_get_tuning_cmd(host)); + else + host->ops->config_auto_tuning_cmd(host, false, + sdhci_get_tuning_cmd(host)); + } + if (mrq->sbc && !(host->flags & SDHCI_AUTO_CMD23)) sdhci_send_command(host, mrq->sbc); else diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h index 065e13abedfc..63106bb28782 100644 --- a/drivers/mmc/host/sdhci.h +++ b/drivers/mmc/host/sdhci.h @@ -632,6 +632,9 @@ struct sdhci_ops { void (*card_event)(struct sdhci_host *host); void (*platform_bus_voting)(struct sdhci_host *host, u32 enable); void (*check_power_status)(struct sdhci_host *host, u32 req_type); + int (*config_auto_tuning_cmd)(struct sdhci_host *host, + bool enable, + u32 type); void (*dump_vendor_regs)(struct sdhci_host *host); void (*voltage_switch)(struct sdhci_host *host); int (*select_drive_strength)(struct sdhci_host *host,