[U-Boot] [PATCH 7/8] mmc: sdhci-cadence: add HS200 support
Masahiro Yamada
yamada.masahiro at socionext.com
Fri Jan 12 09:11:32 UTC 2018
Hi Jaehoon,
2018-01-12 17:44 GMT+09:00 Jaehoon Chung <jh80.chung at samsung.com>:
> Hi Masahiro,
>
> On 12/30/2017 02:00 AM, Masahiro Yamada wrote:
>> Add HS200 timing setting and the MMC tuning callback.
>>
>> Signed-off-by: Masahiro Yamada <yamada.masahiro at socionext.com>
>> ---
>>
>> drivers/mmc/sdhci-cadence.c | 87 ++++++++++++++++++++++++++++++++++++++++-----
>> 1 file changed, 78 insertions(+), 9 deletions(-)
>>
>> diff --git a/drivers/mmc/sdhci-cadence.c b/drivers/mmc/sdhci-cadence.c
>> index 921095b..8658126 100644
>> --- a/drivers/mmc/sdhci-cadence.c
>> +++ b/drivers/mmc/sdhci-cadence.c
>> @@ -52,6 +52,13 @@
>> #define SDHCI_CDNS_PHY_DLY_HSMMC 0x0c
>> #define SDHCI_CDNS_PHY_DLY_STROBE 0x0d
>>
>> +/*
>> + * The tuned val register is 6 bit-wide, but not the whole of the range is
>> + * available. The range 0-42 seems to be available (then 43 wraps around to 0)
>> + * but I am not quite sure if it is official. Use only 0 to 39 for safety.
>> + */
>> +#define SDHCI_CDNS_MAX_TUNING_LOOP 40
>> +
>> struct sdhci_cdns_plat {
>> struct mmc_config cfg;
>> struct mmc mmc;
>> @@ -135,20 +142,18 @@ static void sdhci_cdns_set_control_reg(struct sdhci_host *host)
>> * The mode should be decided by MMC_TIMING_* like Linux, but
>> * U-Boot does not support timing. Use the clock frequency instead.
>> */
>> - if (clock <= 26000000)
>> + if (clock <= 26000000) {
>> mode = SDHCI_CDNS_HRS06_MODE_SD; /* use this for Legacy */
>> - else if (clock <= 52000000) {
>> + } else if (clock <= 52000000) {
>> if (mmc->ddr_mode)
>> mode = SDHCI_CDNS_HRS06_MODE_MMC_DDR;
>> else
>> mode = SDHCI_CDNS_HRS06_MODE_MMC_SDR;
>> } else {
>> - /*
>> - * REVISIT:
>> - * The IP supports HS200/HS400, revisit once U-Boot support it
>> - */
>> - printf("unsupported frequency %d\n", clock);
>> - return;
>> + if (mmc->ddr_mode)
>> + mode = SDHCI_CDNS_HRS06_MODE_MMC_HS400;
>> + else
>> + mode = SDHCI_CDNS_HRS06_MODE_MMC_HS200;
>> }
>>
>> tmp = readl(plat->hrs_addr + SDHCI_CDNS_HRS06);
>> @@ -161,6 +166,68 @@ static const struct sdhci_ops sdhci_cdns_ops = {
>> .set_control_reg = sdhci_cdns_set_control_reg,
>> };
>>
>> +static int sdhci_cdns_set_tune_val(struct sdhci_cdns_plat *plat,
>> + unsigned int val)
>> +{
>> + void __iomem *reg = plat->hrs_addr + SDHCI_CDNS_HRS06;
>> + u32 tmp;
>> +
>> + if (WARN_ON(!FIELD_FIT(SDHCI_CDNS_HRS06_TUNE, val)))
>> + return -EINVAL;
>> +
>> + tmp = readl(reg);
>> + tmp &= ~SDHCI_CDNS_HRS06_TUNE;
>> + tmp |= FIELD_PREP(SDHCI_CDNS_HRS06_TUNE, val);
>> + tmp |= SDHCI_CDNS_HRS06_TUNE_UP;
>> + writel(tmp, reg);
>> +
>> + return readl_poll_timeout(reg, tmp, !(tmp & SDHCI_CDNS_HRS06_TUNE_UP),
>> + 1);
>> +}
>> +
>> +static int sdhci_cdns_execute_tuning(struct udevice *dev, unsigned int opcode)
>> +{
>> + struct sdhci_cdns_plat *plat = dev_get_platdata(dev);
>> + struct mmc *mmc = &plat->mmc;
>> + int cur_streak = 0;
>> + int max_streak = 0;
>> + int end_of_streak = 0;
>> + int i;
>> +
>> + /*
>> + * This handler only implements the eMMC tuning that is specific to
>> + * this controller. The tuning for SD timing should be handled by the
>> + * SDHCI core.
>> + */
>> + if (!IS_MMC(mmc))
>> + return -ENOSYS;
>> +
>> + if (WARN_ON(opcode != MMC_CMD_SEND_TUNING_BLOCK_HS200))
>> + return -EINVAL;
>> +
>> + for (i = 0; i < SDHCI_CDNS_MAX_TUNING_LOOP; i++) {
>> + if (sdhci_cdns_set_tune_val(plat, i) ||
>> + mmc_send_tuning(mmc, opcode, NULL)) { /* bad */
>> + cur_streak = 0;
>> + } else { /* good */
>> + cur_streak++;
>> + if (cur_streak > max_streak) {
>> + max_streak = cur_streak;
>> + end_of_streak = i;
>> + }
>> + }
>> + }
>> +
>> + if (!max_streak) {
>> + dev_err(dev, "no tuning point found\n");
>> + return -EIO;
>> + }
>> +
>> + return sdhci_cdns_set_tune_val(plat, end_of_streak - max_streak / 2);
>> +}
>> +
>> +static struct dm_mmc_ops sdhci_cdns_mmc_ops;
>> +
>> static int sdhci_cdns_bind(struct udevice *dev)
>> {
>> struct sdhci_cdns_plat *plat = dev_get_platdata(dev);
>> @@ -189,6 +256,8 @@ static int sdhci_cdns_probe(struct udevice *dev)
>> host->ioaddr = plat->hrs_addr + SDHCI_CDNS_SRS_BASE;
>> host->ops = &sdhci_cdns_ops;
>> host->quirks |= SDHCI_QUIRK_WAIT_SEND_CMD;
>> + sdhci_cdns_mmc_ops = sdhci_ops;
>> + sdhci_cdns_mmc_ops.execute_tuning = sdhci_cdns_execute_tuning;
>
> It needs to add the #ifdef MMC_SUPPORT_TUNING.
> When MMC_SUPPPORT_TUNING is enabled, excute_tuning is included as member.
Nice catch!
I've sent v2. Thanks!
--
Best Regards
Masahiro Yamada
More information about the U-Boot
mailing list