[U-Boot] [PATCH 01/33] mmc: select the available type from host_caps and card_caps

The original implementation select HS timing by default, add available type selection for higher speed mode compatibility, such as hs200, hs400, hs400es.
By the way, we assume that card run at 1.8V or 1.2V I/O when its timing is ddr52/hs200/hs400(es).
Signed-off-by: Ziyuan Xu xzy.xu@rock-chips.com ---
drivers/mmc/mmc.c | 59 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- include/mmc.h | 16 +++++++++++++++ 2 files changed, 74 insertions(+), 1 deletion(-)
diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index 72fc177..f5b2280 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -546,10 +546,62 @@ int mmc_switch(struct mmc *mmc, u8 set, u8 index, u8 value)
}
+static u32 mmc_select_card_type(struct mmc *mmc, u8 *ext_csd) +{ + u8 card_type; + u32 host_caps, avail_type = 0; + + card_type = ext_csd[EXT_CSD_CARD_TYPE]; + host_caps = mmc->cfg->host_caps; + + if ((host_caps & MMC_MODE_HS) && + (card_type & EXT_CSD_CARD_TYPE_26)) + avail_type |= EXT_CSD_CARD_TYPE_26; + + if ((host_caps & MMC_MODE_HS) && + (card_type & EXT_CSD_CARD_TYPE_52)) + avail_type |= EXT_CSD_CARD_TYPE_52; + + /* + * For the moment, u-boot doesn't support signal voltage + * switch, therefor we assume that host support ddr52 + * at 1.8v or 3.3v I/O(1.2v I/O not supported, hs200 and + * hs400 are the same). + */ + if ((host_caps & MMC_MODE_DDR_52MHz) && + (card_type & EXT_CSD_CARD_TYPE_DDR_1_8V)) + avail_type |= EXT_CSD_CARD_TYPE_DDR_1_8V; + + if ((host_caps & MMC_MODE_HS200) && + (card_type & EXT_CSD_CARD_TYPE_HS200_1_8V)) + avail_type |= EXT_CSD_CARD_TYPE_HS200_1_8V; + + /* + * If host can support HS400, it means that host can also + * support HS200. + */ + if ((host_caps & MMC_MODE_HS400) && + (host_caps & MMC_MODE_8BIT) && + (card_type & EXT_CSD_CARD_TYPE_HS400_1_8V)) + avail_type |= EXT_CSD_CARD_TYPE_HS200_1_8V | + EXT_CSD_CARD_TYPE_HS400_1_8V; + + if ((host_caps & MMC_MODE_HS400ES) && + (host_caps & MMC_MODE_8BIT) && + ext_csd[EXT_CSD_STROBE_SUPPORT] && + (avail_type & EXT_CSD_CARD_TYPE_HS400_1_8V)) + avail_type |= EXT_CSD_CARD_TYPE_HS200_1_8V | + EXT_CSD_CARD_TYPE_HS400_1_8V | + EXT_CSD_CARD_TYPE_HS400ES; + + return avail_type; +} + static int mmc_change_freq(struct mmc *mmc) { ALLOC_CACHE_ALIGN_BUFFER(u8, ext_csd, MMC_MAX_BLOCK_LEN); char cardtype; + u32 avail_type; int err;
mmc->card_caps = 0; @@ -569,8 +621,13 @@ static int mmc_change_freq(struct mmc *mmc) return err;
cardtype = ext_csd[EXT_CSD_CARD_TYPE] & 0xf; + avail_type = mmc_select_card_type(mmc, ext_csd);
- err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_HS_TIMING, 1); + if (avail_type & EXT_CSD_CARD_TYPE_HS) + err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, + EXT_CSD_HS_TIMING, 1); + else + err = -EINVAL;
if (err) return err; diff --git a/include/mmc.h b/include/mmc.h index fad12d6..0bae1a1 100644 --- a/include/mmc.h +++ b/include/mmc.h @@ -58,6 +58,9 @@ #define MMC_MODE_8BIT (1 << 3) #define MMC_MODE_SPI (1 << 4) #define MMC_MODE_DDR_52MHz (1 << 5) +#define MMC_MODE_HS200 (1 << 6) +#define MMC_MODE_HS400 (1 << 7) +#define MMC_MODE_HS400ES (1 << 8)
#define SD_DATA_4BIT 0x00040000
@@ -182,6 +185,7 @@ #define EXT_CSD_BOOT_BUS_WIDTH 177 #define EXT_CSD_PART_CONF 179 /* R/W */ #define EXT_CSD_BUS_WIDTH 183 /* R/W */ +#define EXT_CSD_STROBE_SUPPORT 184 /* RO */ #define EXT_CSD_HS_TIMING 185 /* R/W */ #define EXT_CSD_REV 192 /* RO */ #define EXT_CSD_CARD_TYPE 196 /* RO */ @@ -201,6 +205,18 @@
#define EXT_CSD_CARD_TYPE_26 (1 << 0) /* Card can run at 26MHz */ #define EXT_CSD_CARD_TYPE_52 (1 << 1) /* Card can run at 52MHz */ +#define EXT_CSD_CARD_TYPE_HS (EXT_CSD_CARD_TYPE_26 | \ + EXT_CSD_CARD_TYPE_52) +#define EXT_CSD_CARD_TYPE_HS200_1_8V BIT(4) /* Card can run at 200MHz */ +#define EXT_CSD_CARD_TYPE_HS200_1_2V BIT(5) /* Card can run at 200MHz */ +#define EXT_CSD_CARD_TYPE_HS200 (EXT_CSD_CARD_TYPE_HS200_1_8V | \ + EXT_CSD_CARD_TYPE_HS200_1_2V) +#define EXT_CSD_CARD_TYPE_HS400_1_8V BIT(6) /* Card can run at 200MHz DDR, 1.8V */ +#define EXT_CSD_CARD_TYPE_HS400_1_2V BIT(7) /* Card can run at 200MHz DDR, 1.2V */ +#define EXT_CSD_CARD_TYPE_HS400 (EXT_CSD_CARD_TYPE_HS400_1_8V | \ + EXT_CSD_CARD_TYPE_HS400_1_2V) +#define EXT_CSD_CARD_TYPE_HS400ES BIT(8) /* Card can run at HS400ES */ + #define EXT_CSD_CARD_TYPE_DDR_1_8V (1 << 2) #define EXT_CSD_CARD_TYPE_DDR_1_2V (1 << 3) #define EXT_CSD_CARD_TYPE_DDR_52 (EXT_CSD_CARD_TYPE_DDR_1_8V \

Some controller should do some configuration according to the selected timing.
Signed-off-by: Ziyuan Xu xzy.xu@rock-chips.com ---
drivers/mmc/mmc.c | 7 +++++++ include/mmc.h | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+)
diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index f5b2280..1b3652a 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -318,6 +318,12 @@ ulong mmc_bread(struct blk_desc *block_dev, lbaint_t start, lbaint_t blkcnt, return blkcnt; }
+static void mmc_set_timing(struct mmc *mmc, uint timing) +{ + mmc->timing = timing; + mmc_set_ios(mmc); +} + static int mmc_go_idle(struct mmc *mmc) { struct mmc_cmd cmd; @@ -1734,6 +1740,7 @@ int mmc_start_init(struct mmc *mmc) mmc->ddr_mode = 0; mmc_set_bus_width(mmc, 1); mmc_set_clock(mmc, 1); + mmc_set_timing(mmc, MMC_TIMING_LEGACY);
/* Reset the Card */ err = mmc_go_idle(mmc); diff --git a/include/mmc.h b/include/mmc.h index 0bae1a1..68b6790 100644 --- a/include/mmc.h +++ b/include/mmc.h @@ -434,6 +434,21 @@ struct mmc { uint has_init; int high_capacity; uint bus_width; + uint timing; + +#define MMC_TIMING_LEGACY 0 +#define MMC_TIMING_MMC_HS 1 +#define MMC_TIMING_SD_HS 2 +#define MMC_TIMING_UHS_SDR12 3 +#define MMC_TIMING_UHS_SDR25 4 +#define MMC_TIMING_UHS_SDR50 5 +#define MMC_TIMING_UHS_SDR104 6 +#define MMC_TIMING_UHS_DDR50 7 +#define MMC_TIMING_MMC_DDR52 8 +#define MMC_TIMING_MMC_HS200 9 +#define MMC_TIMING_MMC_HS400 10 +#define MMC_TIMING_MMC_HS400ES 11 + uint clock; uint card_caps; uint ocr; @@ -493,6 +508,40 @@ enum mmc_hwpart_conf_mode { MMC_HWPART_CONF_COMPLETE, };
+static inline bool mmc_card_hs(struct mmc *mmc) +{ + return (mmc->timing == MMC_TIMING_MMC_HS) || + (mmc->timing == MMC_TIMING_SD_HS); +} + +static inline bool mmc_card_ddr(struct mmc *mmc) +{ + return (mmc->timing == MMC_TIMING_UHS_DDR50) || + (mmc->timing == MMC_TIMING_MMC_DDR52) || + (mmc->timing == MMC_TIMING_MMC_HS400) || + (mmc->timing == MMC_TIMING_MMC_HS400ES); +} + +static inline bool mmc_card_hs200(struct mmc *mmc) +{ + return mmc->timing == MMC_TIMING_MMC_HS200; +} + +static inline bool mmc_card_ddr52(struct mmc *mmc) +{ + return mmc->timing == MMC_TIMING_MMC_DDR52; +} + +static inline bool mmc_card_hs400(struct mmc *mmc) +{ + return mmc->timing == MMC_TIMING_MMC_HS400; +} + +static inline bool mmc_card_hs400es(struct mmc *mmc) +{ + return mmc->timing == MMC_TIMING_MMC_HS400ES; +} + struct mmc *mmc_create(const struct mmc_config *cfg, void *priv);
/**

Remove the redundant mmc timing definitions which have defined in mmc.h.
Signed-off-by: Ziyuan Xu xzy.xu@rock-chips.com ---
drivers/mmc/xenon_sdhci.c | 12 ------------ 1 file changed, 12 deletions(-)
diff --git a/drivers/mmc/xenon_sdhci.c b/drivers/mmc/xenon_sdhci.c index 2a0d8b4..f0a33c1 100644 --- a/drivers/mmc/xenon_sdhci.c +++ b/drivers/mmc/xenon_sdhci.c @@ -94,18 +94,6 @@ DECLARE_GLOBAL_DATA_PTR; /* Hyperion only have one slot 0 */ #define XENON_MMC_SLOT_ID_HYPERION 0
-#define MMC_TIMING_LEGACY 0 -#define MMC_TIMING_MMC_HS 1 -#define MMC_TIMING_SD_HS 2 -#define MMC_TIMING_UHS_SDR12 3 -#define MMC_TIMING_UHS_SDR25 4 -#define MMC_TIMING_UHS_SDR50 5 -#define MMC_TIMING_UHS_SDR104 6 -#define MMC_TIMING_UHS_DDR50 7 -#define MMC_TIMING_MMC_DDR52 8 -#define MMC_TIMING_MMC_HS200 9 -#define MMC_TIMING_MMC_HS400 10 - #define XENON_MMC_MAX_CLK 400000000
enum soc_pad_ctrl_type {

Select timing parameter for the host since HS mode switch is completed.
Signed-off-by: Ziyuan Xu xzy.xu@rock-chips.com ---
drivers/mmc/mmc.c | 16 ++++++++++++++-- include/mmc.h | 6 ++++++ 2 files changed, 20 insertions(+), 2 deletions(-)
diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index 1b3652a..0b30172 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -552,6 +552,19 @@ int mmc_switch(struct mmc *mmc, u8 set, u8 index, u8 value)
}
+static int mmc_select_hs(struct mmc *mmc) +{ + int ret; + + ret = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, + EXT_CSD_HS_TIMING, EXT_CSD_TIMING_HS); + + if (!ret) + mmc_set_timing(mmc, MMC_TIMING_MMC_HS); + + return ret; +} + static u32 mmc_select_card_type(struct mmc *mmc, u8 *ext_csd) { u8 card_type; @@ -630,8 +643,7 @@ static int mmc_change_freq(struct mmc *mmc) avail_type = mmc_select_card_type(mmc, ext_csd);
if (avail_type & EXT_CSD_CARD_TYPE_HS) - err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, - EXT_CSD_HS_TIMING, 1); + err = mmc_select_hs(mmc); else err = -EINVAL;
diff --git a/include/mmc.h b/include/mmc.h index 68b6790..060c1f8 100644 --- a/include/mmc.h +++ b/include/mmc.h @@ -228,6 +228,12 @@ #define EXT_CSD_DDR_BUS_WIDTH_4 5 /* Card is in 4 bit DDR mode */ #define EXT_CSD_DDR_BUS_WIDTH_8 6 /* Card is in 8 bit DDR mode */
+#define EXT_CSD_TIMING_BC 0 /* Backwards compatility */ +#define EXT_CSD_TIMING_HS 1 /* High speed */ +#define EXT_CSD_TIMING_HS200 2 /* HS200 */ +#define EXT_CSD_TIMING_HS400 3 /* HS400 */ +#define EXT_CSD_DRV_STR_SHIFT 4 /* Driver Strength shift */ + #define EXT_CSD_BOOT_ACK_ENABLE (1 << 6) #define EXT_CSD_BOOT_PARTITION_ENABLE (1 << 3) #define EXT_CSD_PARTITION_ACCESS_ENABLE (1 << 0)

Configure HISPD bit field according to the timing parameter instead of the card clock frequency.
Signed-off-by: Ziyuan Xu xzy.xu@rock-chips.com ---
drivers/mmc/sdhci.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-)
diff --git a/drivers/mmc/sdhci.c b/drivers/mmc/sdhci.c index b745977..58cc0ab 100644 --- a/drivers/mmc/sdhci.c +++ b/drivers/mmc/sdhci.c @@ -457,14 +457,12 @@ static int sdhci_set_ios(struct mmc *mmc) ctrl &= ~SDHCI_CTRL_4BITBUS; }
- if (mmc->clock > 26000000) + if (!(mmc->timing == MMC_TIMING_LEGACY) && + !(host->quirks & SDHCI_QUIRK_NO_HISPD_BIT)) ctrl |= SDHCI_CTRL_HISPD; else ctrl &= ~SDHCI_CTRL_HISPD;
- if (host->quirks & SDHCI_QUIRK_NO_HISPD_BIT) - ctrl &= ~SDHCI_CTRL_HISPD; - sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
/* If available, call the driver specific "post" set_ios() function */

Card devices get into busy status since host request speed mode switch, if host controller is able to query whether the device is busy, try it instead of sending cmd13.
Signed-off-by: Ziyuan Xu xzy.xu@rock-chips.com ---
drivers/mmc/mmc-uclass.c | 16 ++++++++++++++++ drivers/mmc/mmc.c | 13 +++++++++++++ include/mmc.h | 11 +++++++++++ 3 files changed, 40 insertions(+)
diff --git a/drivers/mmc/mmc-uclass.c b/drivers/mmc/mmc-uclass.c index 9c07871..a300a6d 100644 --- a/drivers/mmc/mmc-uclass.c +++ b/drivers/mmc/mmc-uclass.c @@ -38,6 +38,22 @@ int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data) return dm_mmc_send_cmd(mmc->dev, cmd, data); }
+bool mmc_card_busy(struct mmc *mmc) +{ + struct dm_mmc_ops *ops = mmc_get_ops(mmc->dev); + + if (!ops->card_busy) + return -ENOSYS; + return ops->card_busy(mmc->dev); +} + +bool mmc_can_card_busy(struct mmc *mmc) +{ + struct dm_mmc_ops *ops = mmc_get_ops(mmc->dev); + + return !!ops->card_busy; +} + int dm_mmc_set_ios(struct udevice *dev) { struct dm_mmc_ops *ops = mmc_get_ops(dev); diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index 0b30172..13d8f04 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -1156,6 +1156,19 @@ static void mmc_set_ios(struct mmc *mmc) if (mmc->cfg->ops->set_ios) mmc->cfg->ops->set_ios(mmc); } + +static bool mmc_card_busy(struct mmc *mmc) +{ + if (!mmc->cfg->ops->card_busy) + return -ENOSYS; + + return mmc->cfg->ops->card_busy(mmc); +} + +static bool mmc_can_card_busy(struct mmc *) +{ + return !!mmc->cfg->ops->card_busy; +} #endif
void mmc_set_clock(struct mmc *mmc, uint clock) diff --git a/include/mmc.h b/include/mmc.h index 060c1f8..9bed935 100644 --- a/include/mmc.h +++ b/include/mmc.h @@ -357,6 +357,14 @@ struct dm_mmc_ops { struct mmc_data *data);
/** + * card_busy() - Query the card device status + * + * @dev: Device to update + * @return true if card device is busy + */ + bool (*card_busy)(struct udevice *dev); + + /** * set_ios() - Set the I/O speed/width for an MMC device * * @dev: Device to update @@ -390,12 +398,15 @@ int dm_mmc_get_cd(struct udevice *dev); int dm_mmc_get_wp(struct udevice *dev);
/* Transition functions for compatibility */ +bool mmc_card_busy(struct mmc *mmc); +bool mmc_can_card_busy(struct mmc *mmc); int mmc_set_ios(struct mmc *mmc); int mmc_getcd(struct mmc *mmc); int mmc_getwp(struct mmc *mmc);
#else struct mmc_ops { + bool (*card_busy)(struct mmc *mmc); int (*send_cmd)(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data); int (*set_ios)(struct mmc *mmc);

On 05/15/2017 03:07 PM, Ziyuan Xu wrote:
Card devices get into busy status since host request speed mode switch, if host controller is able to query whether the device is busy, try it instead of sending cmd13.
This patch is similar to one of Jean-Jacques's patches.
Signed-off-by: Ziyuan Xu xzy.xu@rock-chips.com
drivers/mmc/mmc-uclass.c | 16 ++++++++++++++++ drivers/mmc/mmc.c | 13 +++++++++++++ include/mmc.h | 11 +++++++++++ 3 files changed, 40 insertions(+)
diff --git a/drivers/mmc/mmc-uclass.c b/drivers/mmc/mmc-uclass.c index 9c07871..a300a6d 100644 --- a/drivers/mmc/mmc-uclass.c +++ b/drivers/mmc/mmc-uclass.c @@ -38,6 +38,22 @@ int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data) return dm_mmc_send_cmd(mmc->dev, cmd, data); }
+bool mmc_card_busy(struct mmc *mmc) +{
- struct dm_mmc_ops *ops = mmc_get_ops(mmc->dev);
- if (!ops->card_busy)
return -ENOSYS;
return is ENOSYS but this function is return bool type..fix it.
- return ops->card_busy(mmc->dev);
+}
+bool mmc_can_card_busy(struct mmc *mmc) +{
- struct dm_mmc_ops *ops = mmc_get_ops(mmc->dev);
- return !!ops->card_busy;
+}
int dm_mmc_set_ios(struct udevice *dev) { struct dm_mmc_ops *ops = mmc_get_ops(dev); diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index 0b30172..13d8f04 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -1156,6 +1156,19 @@ static void mmc_set_ios(struct mmc *mmc) if (mmc->cfg->ops->set_ios) mmc->cfg->ops->set_ios(mmc); }
+static bool mmc_card_busy(struct mmc *mmc) +{
- if (!mmc->cfg->ops->card_busy)
return -ENOSYS;
- return mmc->cfg->ops->card_busy(mmc);
ditto.
+}
+static bool mmc_can_card_busy(struct mmc *) +{
- return !!mmc->cfg->ops->card_busy;
+} #endif
void mmc_set_clock(struct mmc *mmc, uint clock) diff --git a/include/mmc.h b/include/mmc.h index 060c1f8..9bed935 100644 --- a/include/mmc.h +++ b/include/mmc.h @@ -357,6 +357,14 @@ struct dm_mmc_ops { struct mmc_data *data);
/**
* card_busy() - Query the card device status
*
* @dev: Device to update
* @return true if card device is busy
*/
- bool (*card_busy)(struct udevice *dev);
- /**
- set_ios() - Set the I/O speed/width for an MMC device
- @dev: Device to update
@@ -390,12 +398,15 @@ int dm_mmc_get_cd(struct udevice *dev); int dm_mmc_get_wp(struct udevice *dev);
/* Transition functions for compatibility */ +bool mmc_card_busy(struct mmc *mmc); +bool mmc_can_card_busy(struct mmc *mmc); int mmc_set_ios(struct mmc *mmc); int mmc_getcd(struct mmc *mmc); int mmc_getwp(struct mmc *mmc);
#else struct mmc_ops {
- bool (*card_busy)(struct mmc *mmc); int (*send_cmd)(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data); int (*set_ios)(struct mmc *mmc);

Signed-off-by: Ziyuan Xu xzy.xu@rock-chips.com ---
drivers/mmc/dw_mmc.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+)
diff --git a/drivers/mmc/dw_mmc.c b/drivers/mmc/dw_mmc.c index 700f764..baf2280 100644 --- a/drivers/mmc/dw_mmc.c +++ b/drivers/mmc/dw_mmc.c @@ -384,6 +384,26 @@ static int dwmci_setup_bus(struct dwmci_host *host, u32 freq) }
#ifdef CONFIG_DM_MMC_OPS +static bool dwmci_card_busy(struct udevice *dev) +{ + struct mmc *mmc = mmc_get_mmc_dev(dev); +#else +static bool dwmci_card_busy(struct mmc *mmc) +{ +#endif + u32 status; + struct dwmci_host *host = (struct dwmci_host *)mmc->priv; + + /* + * Check the busy bit which is low when DAT[3:0] + * (the data lines) are 0000 + */ + status = dwmci_readl(host, DWMCI_STATUS); + + return !!(status & DWMCI_BUSY); +} + +#ifdef CONFIG_DM_MMC_OPS static int dwmci_set_ios(struct udevice *dev) { struct mmc *mmc = mmc_get_mmc_dev(dev); @@ -475,12 +495,14 @@ int dwmci_probe(struct udevice *dev) }
const struct dm_mmc_ops dm_dwmci_ops = { + .card_busy = dwmci_card_busy, .send_cmd = dwmci_send_cmd, .set_ios = dwmci_set_ios, };
#else static const struct mmc_ops dwmci_ops = { + .card_busy = dwmci_card_busy, .send_cmd = dwmci_send_cmd, .set_ios = dwmci_set_ios, .init = dwmci_init,

Signed-off-by: Ziyuan Xu xzy.xu@rock-chips.com ---
drivers/mmc/sdhci.c | 19 +++++++++++++++++++ include/sdhci.h | 1 + 2 files changed, 20 insertions(+)
diff --git a/drivers/mmc/sdhci.c b/drivers/mmc/sdhci.c index 58cc0ab..48bac04 100644 --- a/drivers/mmc/sdhci.c +++ b/drivers/mmc/sdhci.c @@ -424,6 +424,23 @@ static void sdhci_set_power(struct sdhci_host *host, unsigned short power) }
#ifdef CONFIG_DM_MMC_OPS +static bool sdhci_card_busy(struct udevice *dev) +{ + struct mmc *mmc = mmc_get_mmc_dev(dev); +#else +static bool sdhci_card_busy(struct mmc *mmc) +{ +#endif + struct sdhci_host *host = mmc->priv; + u32 present_state; + + /* Check whether DAT[0] is 0 */ + present_state = sdhci_readl(host, SDHCI_PRESENT_STATE); + + return !(present_state & SDHCI_DATA_0_LVL); +} + +#ifdef CONFIG_DM_MMC_OPS static int sdhci_set_ios(struct udevice *dev) { struct mmc *mmc = mmc_get_mmc_dev(dev); @@ -510,11 +527,13 @@ int sdhci_probe(struct udevice *dev) }
const struct dm_mmc_ops sdhci_ops = { + .card_busy = sdhci_card_busy, .send_cmd = sdhci_send_command, .set_ios = sdhci_set_ios, }; #else static const struct mmc_ops sdhci_ops = { + .card_busy = sdhci_card_busy, .send_cmd = sdhci_send_command, .set_ios = sdhci_set_ios, .init = sdhci_init, diff --git a/include/sdhci.h b/include/sdhci.h index 6a43271..75432db 100644 --- a/include/sdhci.h +++ b/include/sdhci.h @@ -64,6 +64,7 @@ #define SDHCI_CARD_STATE_STABLE BIT(17) #define SDHCI_CARD_DETECT_PIN_LEVEL BIT(18) #define SDHCI_WRITE_PROTECT BIT(19) +#define SDHCI_DATA_0_LVL BIT(20)
#define SDHCI_HOST_CONTROL 0x28 #define SDHCI_CTRL_LED BIT(0)

Per JEDEC spec, it is not recommended to use cmd13 to get card status after speed mode switch. CMD13 can't be guaranteed due to the asynchronous operation.
Besieds, if the host controller supports busy detection in HW, we use it instead of cmd13.
Signed-off-by: Ziyuan Xu xzy.xu@rock-chips.com ---
drivers/mmc/mmc.c | 55 +++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 45 insertions(+), 10 deletions(-)
diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index 13d8f04..9aee6ff 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -523,10 +523,46 @@ static int mmc_send_ext_csd(struct mmc *mmc, u8 *ext_csd) return err; }
-int mmc_switch(struct mmc *mmc, u8 set, u8 index, u8 value) +static int mmc_poll_for_busy(struct mmc *mmc) { struct mmc_cmd cmd; + u8 busy = true; + uint start; + int ret; int timeout = 1000; + + cmd.cmdidx = MMC_CMD_SEND_STATUS; + cmd.resp_type = MMC_RSP_R1; + cmd.cmdarg = mmc->rca << 16; + + start = get_timer(0); + + do { + if (mmc_can_card_busy(mmc)) { + busy = mmc_card_busy(mmc); + } else { + ret = mmc_send_cmd(mmc, &cmd, NULL); + + if (ret) + return ret; + + if (cmd.response[0] & MMC_STATUS_SWITCH_ERROR) + return -EBADMSG; + busy = (cmd.response[0] & MMC_STATUS_CURR_STATE) == + MMC_STATE_PRG; + } + + if (get_timer(start) > timeout && busy) + return -ETIMEDOUT; + } while (busy); + + return 0; +} + +static int __mmc_switch(struct mmc *mmc, u8 set, u8 index, u8 value, + u8 send_status) +{ + struct mmc_cmd cmd; int retries = 3; int ret;
@@ -536,20 +572,19 @@ int mmc_switch(struct mmc *mmc, u8 set, u8 index, u8 value) (index << 16) | (value << 8);
- while (retries > 0) { + do { ret = mmc_send_cmd(mmc, &cmd, NULL);
- /* Waiting for the ready status */ - if (!ret) { - ret = mmc_send_status(mmc, timeout); - return ret; - } - - retries--; - } + if (!ret && send_status) + return mmc_poll_for_busy(mmc); + } while (--retries > 0 && ret);
return ret; +}
+int mmc_switch(struct mmc *mmc, u8 set, u8 index, u8 value) +{ + return __mmc_switch(mmc, set, index, value, true); }
static int mmc_select_hs(struct mmc *mmc)

Add the support of the HS200 mode for eMMC 4.5 devices. The eMMC 4.5 device has support up to 200MHz bus speed, it can speed up the boot speed.
We can enable this feature via MMC_MODE_HS200 if the host controller has the ability to support HS200 timing. Also the tuning feature required when the HS200 mode is selected.
By the way, mmc card can only switch to high speed mode in SPL stage.
Signed-off-by: Ziyuan Xu xzy.xu@rock-chips.com ---
drivers/mmc/mmc.c | 395 ++++++++++++++++++++++++++++++++++++------------------ include/mmc.h | 27 ++++ 2 files changed, 289 insertions(+), 133 deletions(-)
diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index 9aee6ff..bebf8f3 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -318,6 +318,26 @@ ulong mmc_bread(struct blk_desc *block_dev, lbaint_t start, lbaint_t blkcnt, return blkcnt; }
+void mmc_set_clock(struct mmc *mmc, uint clock) +{ + if (clock > mmc->cfg->f_max) + clock = mmc->cfg->f_max; + + if (clock < mmc->cfg->f_min) + clock = mmc->cfg->f_min; + + mmc->clock = clock; + + mmc_set_ios(mmc); +} + +static void mmc_set_bus_width(struct mmc *mmc, uint width) +{ + mmc->bus_width = width; + + mmc_set_ios(mmc); +} + static void mmc_set_timing(struct mmc *mmc, uint timing) { mmc->timing = timing; @@ -587,6 +607,181 @@ int mmc_switch(struct mmc *mmc, u8 set, u8 index, u8 value) return __mmc_switch(mmc, set, index, value, true); }
+static int mmc_select_bus_width(struct mmc *mmc) +{ + u32 ext_csd_bits[] = { + EXT_CSD_BUS_WIDTH_8, + EXT_CSD_BUS_WIDTH_4, + }; + u32 bus_widths[] = { + MMC_BUS_WIDTH_8BIT, + MMC_BUS_WIDTH_4BIT, + }; + ALLOC_CACHE_ALIGN_BUFFER(u8, ext_csd, MMC_MAX_BLOCK_LEN); + ALLOC_CACHE_ALIGN_BUFFER(u8, test_csd, MMC_MAX_BLOCK_LEN); + u32 idx, bus_width = 0; + int err = 0; + + if (mmc->version < MMC_VERSION_4 || + !(mmc->cfg->host_caps & (MMC_MODE_4BIT | MMC_MODE_8BIT))) + return 0; + + err = mmc_send_ext_csd(mmc, ext_csd); + + if (err) + return err; + + idx = (mmc->cfg->host_caps & MMC_MODE_8BIT) ? 0 : 1; + + /* + * Unlike SD, MMC cards dont have a configuration register to notify + * supported bus width. So bus test command should be run to identify + * the supported bus width or compare the ext csd values of current + * bus width and ext csd values of 1 bit mode read earlier. + */ + for (; idx < ARRAY_SIZE(bus_widths); idx++) { + /* + * Host is capable of 8bit transfer, then switch + * the device to work in 8bit transfer mode. If the + * mmc switch command returns error then switch to + * 4bit transfer mode. On success set the corresponding + * bus width on the host. + */ + err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, + EXT_CSD_BUS_WIDTH, ext_csd_bits[idx]); + if (err) + continue; + + bus_width = bus_widths[idx]; + mmc_set_bus_width(mmc, bus_width); + + err = mmc_send_ext_csd(mmc, test_csd); + + if (err) + continue; + + /* Only compare read only fields */ + if ((ext_csd[EXT_CSD_PARTITIONING_SUPPORT] == + test_csd[EXT_CSD_PARTITIONING_SUPPORT]) && + (ext_csd[EXT_CSD_HC_WP_GRP_SIZE] == + test_csd[EXT_CSD_HC_WP_GRP_SIZE]) && + (ext_csd[EXT_CSD_REV] == test_csd[EXT_CSD_REV]) && + (ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE] == + test_csd[EXT_CSD_HC_ERASE_GRP_SIZE]) && + !memcmp(&ext_csd[EXT_CSD_SEC_CNT], + &test_csd[EXT_CSD_SEC_CNT], 4)) { + err = bus_width; + break; + } else { + err = -EBADMSG; + } + } + + return err; +} + +static const u8 tuning_blk_pattern_4bit[] = { + 0xff, 0x0f, 0xff, 0x00, 0xff, 0xcc, 0xc3, 0xcc, + 0xc3, 0x3c, 0xcc, 0xff, 0xfe, 0xff, 0xfe, 0xef, + 0xff, 0xdf, 0xff, 0xdd, 0xff, 0xfb, 0xff, 0xfb, + 0xbf, 0xff, 0x7f, 0xff, 0x77, 0xf7, 0xbd, 0xef, + 0xff, 0xf0, 0xff, 0xf0, 0x0f, 0xfc, 0xcc, 0x3c, + 0xcc, 0x33, 0xcc, 0xcf, 0xff, 0xef, 0xff, 0xee, + 0xff, 0xfd, 0xff, 0xfd, 0xdf, 0xff, 0xbf, 0xff, + 0xbb, 0xff, 0xf7, 0xff, 0xf7, 0x7f, 0x7b, 0xde, +}; + +static const u8 tuning_blk_pattern_8bit[] = { + 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, + 0xff, 0xff, 0xcc, 0xcc, 0xcc, 0x33, 0xcc, 0xcc, + 0xcc, 0x33, 0x33, 0xcc, 0xcc, 0xcc, 0xff, 0xff, + 0xff, 0xee, 0xff, 0xff, 0xff, 0xee, 0xee, 0xff, + 0xff, 0xff, 0xdd, 0xff, 0xff, 0xff, 0xdd, 0xdd, + 0xff, 0xff, 0xff, 0xbb, 0xff, 0xff, 0xff, 0xbb, + 0xbb, 0xff, 0xff, 0xff, 0x77, 0xff, 0xff, 0xff, + 0x77, 0x77, 0xff, 0x77, 0xbb, 0xdd, 0xee, 0xff, + 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, + 0x00, 0xff, 0xff, 0xcc, 0xcc, 0xcc, 0x33, 0xcc, + 0xcc, 0xcc, 0x33, 0x33, 0xcc, 0xcc, 0xcc, 0xff, + 0xff, 0xff, 0xee, 0xff, 0xff, 0xff, 0xee, 0xee, + 0xff, 0xff, 0xff, 0xdd, 0xff, 0xff, 0xff, 0xdd, + 0xdd, 0xff, 0xff, 0xff, 0xbb, 0xff, 0xff, 0xff, + 0xbb, 0xbb, 0xff, 0xff, 0xff, 0x77, 0xff, 0xff, + 0xff, 0x77, 0x77, 0xff, 0x77, 0xbb, 0xdd, 0xee, +}; + +int mmc_send_tuning(struct mmc *mmc, u32 opcode) +{ + struct mmc_cmd cmd; + struct mmc_data data; + const u8 *tuning_block_pattern; + int size, err = 0; + u8 *data_buf; + + if (mmc->bus_width == MMC_BUS_WIDTH_8BIT) { + tuning_block_pattern = tuning_blk_pattern_8bit; + size = sizeof(tuning_blk_pattern_8bit); + } else if (mmc->bus_width == MMC_BUS_WIDTH_4BIT) { + tuning_block_pattern = tuning_blk_pattern_4bit; + size = sizeof(tuning_blk_pattern_4bit); + } else { + return -EINVAL; + } + + data_buf = calloc(1, size); + if (!data_buf) + return -ENOMEM; + + cmd.cmdidx = opcode; + cmd.resp_type = MMC_RSP_R1; + cmd.cmdarg = 0; + + data.dest = (char *)data_buf; + data.blocksize = size; + data.blocks = 1; + data.flags = MMC_DATA_READ; + + err = mmc_send_cmd(mmc, &cmd, &data); + if (err) + goto out; + + if (memcmp(data_buf, tuning_block_pattern, size)) + err = -EIO; +out: + free(data_buf); + return err; +} + +static int mmc_execute_tuning(struct mmc *mmc) +{ +#ifdef CONFIG_DM_MMC_OPS + struct dm_mmc_ops *ops = mmc_get_ops(mmc->dev); +#endif + u32 opcode; + + if (IS_SD(mmc)) + opcode = MMC_SEND_TUNING_BLOCK; + else + opcode = MMC_SEND_TUNING_BLOCK_HS200; + +#ifndef CONFIG_DM_MMC_OPS + if (mmc->cfg->ops->execute_tuning) { + return mmc->cfg->ops->execute_tuning(mmc, opcode); +#else + if (ops->execute_tuning) { + return ops->execute_tuning(mmc->dev, opcode); +#endif + } else { + debug("Tuning feature required for HS200 mode.\n"); + return -EIO; + } +} + +static int mmc_hs200_tuning(struct mmc *mmc) +{ + return mmc_execute_tuning(mmc); +} + static int mmc_select_hs(struct mmc *mmc) { int ret; @@ -600,6 +795,45 @@ static int mmc_select_hs(struct mmc *mmc) return ret; }
+#ifndef CONFIG_SPL_BUILD +static int mmc_select_hs200(struct mmc *mmc) +{ + int ret; + struct mmc_cmd cmd; + + /* + * Set the bus width(4 or 8) with host's support and + * switch to HS200 mode if bus width is set successfully. + */ + ret = mmc_select_bus_width(mmc); + + if (ret > 0) { + ret = __mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, + EXT_CSD_HS_TIMING, + EXT_CSD_TIMING_HS200, false); + + if (ret) + return ret; + + mmc_set_timing(mmc, MMC_TIMING_MMC_HS200); + + cmd.cmdidx = MMC_CMD_SEND_STATUS; + cmd.resp_type = MMC_RSP_R1; + cmd.cmdarg = mmc->rca << 16; + + ret = mmc_send_cmd(mmc, &cmd, NULL); + + if (ret) + return ret; + + if (cmd.response[0] & MMC_STATUS_SWITCH_ERROR) + return -EBADMSG; + } + + return ret; +} +#endif + static u32 mmc_select_card_type(struct mmc *mmc, u8 *ext_csd) { u8 card_type; @@ -651,10 +885,24 @@ static u32 mmc_select_card_type(struct mmc *mmc, u8 *ext_csd) return avail_type; }
+static void mmc_set_bus_speed(struct mmc *mmc, u8 avail_type) +{ + int clock = 0; + + if (mmc_card_hs(mmc)) + clock = (avail_type & EXT_CSD_CARD_TYPE_52) ? + MMC_HIGH_52_MAX_DTR : MMC_HIGH_26_MAX_DTR; + else if (mmc_card_hs200(mmc) || + mmc_card_hs400(mmc) || + mmc_card_hs400es(mmc)) + clock = MMC_HS200_MAX_DTR; + + mmc_set_clock(mmc, clock); +} + static int mmc_change_freq(struct mmc *mmc) { ALLOC_CACHE_ALIGN_BUFFER(u8, ext_csd, MMC_MAX_BLOCK_LEN); - char cardtype; u32 avail_type; int err;
@@ -674,9 +922,13 @@ static int mmc_change_freq(struct mmc *mmc) if (err) return err;
- cardtype = ext_csd[EXT_CSD_CARD_TYPE] & 0xf; avail_type = mmc_select_card_type(mmc, ext_csd);
+#ifndef CONFIG_SPL_BUILD + if (avail_type & EXT_CSD_CARD_TYPE_HS200) + err = mmc_select_hs200(mmc); + else +#endif if (avail_type & EXT_CSD_CARD_TYPE_HS) err = mmc_select_hs(mmc); else @@ -685,26 +937,14 @@ static int mmc_change_freq(struct mmc *mmc) if (err) return err;
- /* Now check to see that it worked */ - err = mmc_send_ext_csd(mmc, ext_csd); - - if (err) - return err; + mmc_set_bus_speed(mmc, avail_type);
- /* No high-speed support */ - if (!ext_csd[EXT_CSD_HS_TIMING]) - return 0; - - /* High Speed is set, there are two types: 52MHz and 26MHz */ - if (cardtype & EXT_CSD_CARD_TYPE_52) { - if (cardtype & EXT_CSD_CARD_TYPE_DDR_1_8V) - mmc->card_caps |= MMC_MODE_DDR_52MHz; - mmc->card_caps |= MMC_MODE_HS_52MHz | MMC_MODE_HS; - } else { - mmc->card_caps |= MMC_MODE_HS; - } + if (mmc_card_hs200(mmc)) + err = mmc_hs200_tuning(mmc); + else + err = mmc_select_bus_width(mmc) > 0 ? 0 : err;
- return 0; + return err; }
static int mmc_set_capacity(struct mmc *mmc, int part_num) @@ -1206,26 +1446,6 @@ static bool mmc_can_card_busy(struct mmc *) } #endif
-void mmc_set_clock(struct mmc *mmc, uint clock) -{ - if (clock > mmc->cfg->f_max) - clock = mmc->cfg->f_max; - - if (clock < mmc->cfg->f_min) - clock = mmc->cfg->f_min; - - mmc->clock = clock; - - mmc_set_ios(mmc); -} - -static void mmc_set_bus_width(struct mmc *mmc, uint width) -{ - mmc->bus_width = width; - - mmc_set_ios(mmc); -} - static int mmc_startup(struct mmc *mmc) { int err, i; @@ -1233,7 +1453,6 @@ static int mmc_startup(struct mmc *mmc) u64 cmult, csize, capacity; struct mmc_cmd cmd; ALLOC_CACHE_ALIGN_BUFFER(u8, ext_csd, MMC_MAX_BLOCK_LEN); - ALLOC_CACHE_ALIGN_BUFFER(u8, test_csd, MMC_MAX_BLOCK_LEN); bool has_parts = false; bool part_completed; struct blk_desc *bdesc; @@ -1576,102 +1795,12 @@ static int mmc_startup(struct mmc *mmc) mmc->tran_speed = 50000000; else mmc->tran_speed = 25000000; - } else if (mmc->version >= MMC_VERSION_4) { - /* Only version 4 of MMC supports wider bus widths */ - int idx; - - /* An array of possible bus widths in order of preference */ - static unsigned ext_csd_bits[] = { - EXT_CSD_DDR_BUS_WIDTH_8, - EXT_CSD_DDR_BUS_WIDTH_4, - EXT_CSD_BUS_WIDTH_8, - EXT_CSD_BUS_WIDTH_4, - EXT_CSD_BUS_WIDTH_1, - }; - - /* An array to map CSD bus widths to host cap bits */ - static unsigned ext_to_hostcaps[] = { - [EXT_CSD_DDR_BUS_WIDTH_4] = - MMC_MODE_DDR_52MHz | MMC_MODE_4BIT, - [EXT_CSD_DDR_BUS_WIDTH_8] = - MMC_MODE_DDR_52MHz | MMC_MODE_8BIT, - [EXT_CSD_BUS_WIDTH_4] = MMC_MODE_4BIT, - [EXT_CSD_BUS_WIDTH_8] = MMC_MODE_8BIT, - }; - - /* An array to map chosen bus width to an integer */ - static unsigned widths[] = { - 8, 4, 8, 4, 1, - }; - - for (idx=0; idx < ARRAY_SIZE(ext_csd_bits); idx++) { - unsigned int extw = ext_csd_bits[idx]; - unsigned int caps = ext_to_hostcaps[extw]; - - /* - * If the bus width is still not changed, - * don't try to set the default again. - * Otherwise, recover from switch attempts - * by switching to 1-bit bus width. - */ - if (extw == EXT_CSD_BUS_WIDTH_1 && - mmc->bus_width == 1) { - err = 0; - break; - } - - /* - * Check to make sure the card and controller support - * these capabilities - */ - if ((mmc->card_caps & caps) != caps) - continue; - - err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, - EXT_CSD_BUS_WIDTH, extw); - - if (err) - continue; - - mmc->ddr_mode = (caps & MMC_MODE_DDR_52MHz) ? 1 : 0; - mmc_set_bus_width(mmc, widths[idx]);
- err = mmc_send_ext_csd(mmc, test_csd); - - if (err) - continue; - - /* Only compare read only fields */ - if (ext_csd[EXT_CSD_PARTITIONING_SUPPORT] - == test_csd[EXT_CSD_PARTITIONING_SUPPORT] && - ext_csd[EXT_CSD_HC_WP_GRP_SIZE] - == test_csd[EXT_CSD_HC_WP_GRP_SIZE] && - ext_csd[EXT_CSD_REV] - == test_csd[EXT_CSD_REV] && - ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE] - == test_csd[EXT_CSD_HC_ERASE_GRP_SIZE] && - memcmp(&ext_csd[EXT_CSD_SEC_CNT], - &test_csd[EXT_CSD_SEC_CNT], 4) == 0) - break; - else - err = -EBADMSG; - } - - if (err) - return err; - - if (mmc->card_caps & MMC_MODE_HS) { - if (mmc->card_caps & MMC_MODE_HS_52MHz) - mmc->tran_speed = 52000000; - else - mmc->tran_speed = 26000000; - } + mmc_set_clock(mmc, mmc->tran_speed); }
- mmc_set_clock(mmc, mmc->tran_speed); - /* Fix the block length for DDR mode */ - if (mmc->ddr_mode) { + if (mmc_card_ddr(mmc)) { mmc->read_bl_len = MMC_MAX_BLOCK_LEN; mmc->write_bl_len = MMC_MAX_BLOCK_LEN; } diff --git a/include/mmc.h b/include/mmc.h index 9bed935..a2ef986 100644 --- a/include/mmc.h +++ b/include/mmc.h @@ -85,6 +85,8 @@ #define MMC_CMD_SET_BLOCKLEN 16 #define MMC_CMD_READ_SINGLE_BLOCK 17 #define MMC_CMD_READ_MULTIPLE_BLOCK 18 +#define MMC_SEND_TUNING_BLOCK 19 +#define MMC_SEND_TUNING_BLOCK_HS200 21 #define MMC_CMD_SET_BLOCK_COUNT 23 #define MMC_CMD_WRITE_SINGLE_BLOCK 24 #define MMC_CMD_WRITE_MULTIPLE_BLOCK 25 @@ -387,6 +389,17 @@ struct dm_mmc_ops { * @return 0 if write-enabled, 1 if write-protected, -ve on error */ int (*get_wp)(struct udevice *dev); + + /** + * execute_tuning() - Find the optimal sampling point of a data + * input signals. + * + * @dev: Device to check + * @opcode: The tuning command opcode value is different + * for SD and eMMC cards + * @return 0 if write-enabled, 1 if write-protected, -ve on error + */ + int (*execute_tuning)(struct udevice *dev, u32 opcode); };
#define mmc_get_ops(dev) ((struct dm_mmc_ops *)(dev)->driver->ops) @@ -413,6 +426,7 @@ struct mmc_ops { int (*init)(struct mmc *mmc); int (*getcd)(struct mmc *mmc); int (*getwp)(struct mmc *mmc); + int (*execute_tuning)(struct udevice *dev, u32 opcode); }; #endif
@@ -451,6 +465,11 @@ struct mmc { uint has_init; int high_capacity; uint bus_width; + +#define MMC_BUS_WIDTH_1BIT 1 +#define MMC_BUS_WIDTH_4BIT 4 +#define MMC_BUS_WIDTH_8BIT 8 + uint timing;
#define MMC_TIMING_LEGACY 0 @@ -467,6 +486,12 @@ struct mmc { #define MMC_TIMING_MMC_HS400ES 11
uint clock; + +#define MMC_HIGH_26_MAX_DTR 26000000 +#define MMC_HIGH_52_MAX_DTR 52000000 +#define MMC_HIGH_DDR_MAX_DTR 52000000 +#define MMC_HS200_MAX_DTR 200000000 + uint card_caps; uint ocr; uint dsr; @@ -559,6 +584,8 @@ static inline bool mmc_card_hs400es(struct mmc *mmc) return mmc->timing == MMC_TIMING_MMC_HS400ES; }
+int mmc_send_tuning(struct mmc *mmc, u32 opcode); + struct mmc *mmc_create(const struct mmc_config *cfg, void *priv);
/**

Since the card device is set the proper timing after speed mode switch is completed, host driver can get ddr_mode from timing parameter. So drop the antiquated ddr_mode.
Signed-off-by: Ziyuan Xu xzy.xu@rock-chips.com ---
cmd/mmc.c | 2 +- drivers/mmc/dw_mmc.c | 2 +- drivers/mmc/fsl_esdhc.c | 4 ++-- drivers/mmc/mmc.c | 3 +-- drivers/mmc/uniphier-sd.c | 4 ++-- drivers/mmc/xenon_sdhci.c | 6 +++--- include/mmc.h | 1 - 7 files changed, 10 insertions(+), 12 deletions(-)
diff --git a/cmd/mmc.c b/cmd/mmc.c index f83032e..ef05fa5 100644 --- a/cmd/mmc.c +++ b/cmd/mmc.c @@ -38,7 +38,7 @@ static void print_mmcinfo(struct mmc *mmc) print_size(mmc->capacity, "\n");
printf("Bus Width: %d-bit%s\n", mmc->bus_width, - mmc->ddr_mode ? " DDR" : ""); + mmc_card_ddr(mmc) ? " DDR" : "");
puts("Erase Group Size: "); print_size(((u64)mmc->erase_grp_size) << 9, "\n"); diff --git a/drivers/mmc/dw_mmc.c b/drivers/mmc/dw_mmc.c index baf2280..7e9ffc2 100644 --- a/drivers/mmc/dw_mmc.c +++ b/drivers/mmc/dw_mmc.c @@ -432,7 +432,7 @@ static int dwmci_set_ios(struct mmc *mmc) dwmci_writel(host, DWMCI_CTYPE, ctype);
regs = dwmci_readl(host, DWMCI_UHS_REG); - if (mmc->ddr_mode) + if (mmc_card_ddr(mmc)) regs |= DWMCI_DDR_MODE; else regs &= ~DWMCI_DDR_MODE; diff --git a/drivers/mmc/fsl_esdhc.c b/drivers/mmc/fsl_esdhc.c index f3c6358..5a6942e 100644 --- a/drivers/mmc/fsl_esdhc.c +++ b/drivers/mmc/fsl_esdhc.c @@ -400,7 +400,7 @@ esdhc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data) #if defined(CONFIG_FSL_USDHC) esdhc_write32(®s->mixctrl, (esdhc_read32(®s->mixctrl) & 0xFFFFFF80) | (xfertyp & 0x7F) - | (mmc->ddr_mode ? XFERTYP_DDREN : 0)); + | (mmc_card_ddr(mmc) ? XFERTYP_DDREN : 0)); esdhc_write32(®s->xfertyp, xfertyp & 0xFFFF0000); #else esdhc_write32(®s->xfertyp, xfertyp); @@ -541,7 +541,7 @@ static void set_sysctl(struct mmc *mmc, uint clock) if ((sdhc_clk / (div * pre_div)) <= clock) break;
- pre_div >>= mmc->ddr_mode ? 2 : 1; + pre_div >>= mmc_card_ddr(mmc) ? 2 : 1; div -= 1;
clk = (pre_div << 8) | (div << 4); diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index bebf8f3..d47cfe6 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -210,7 +210,7 @@ int mmc_set_blocklen(struct mmc *mmc, int len) { struct mmc_cmd cmd;
- if (mmc->ddr_mode) + if (mmc_card_ddr(mmc)) return 0;
cmd.cmdidx = MMC_CMD_SET_BLOCKLEN; @@ -1926,7 +1926,6 @@ int mmc_start_init(struct mmc *mmc) if (err) return err; #endif - mmc->ddr_mode = 0; mmc_set_bus_width(mmc, 1); mmc_set_clock(mmc, 1); mmc_set_timing(mmc, MMC_TIMING_LEGACY); diff --git a/drivers/mmc/uniphier-sd.c b/drivers/mmc/uniphier-sd.c index 7f20ef1..ac7359b 100644 --- a/drivers/mmc/uniphier-sd.c +++ b/drivers/mmc/uniphier-sd.c @@ -532,7 +532,7 @@ static void uniphier_sd_set_ddr_mode(struct uniphier_sd_priv *priv, u32 tmp;
tmp = readl(priv->regbase + UNIPHIER_SD_IF_MODE); - if (mmc->ddr_mode) + if (mmc_card_ddr(mmc)) tmp |= UNIPHIER_SD_IF_MODE_DDR; else tmp &= ~UNIPHIER_SD_IF_MODE_DDR; @@ -599,7 +599,7 @@ static int uniphier_sd_set_ios(struct udevice *dev) int ret;
dev_dbg(dev, "clock %uHz, DDRmode %d, width %u\n", - mmc->clock, mmc->ddr_mode, mmc->bus_width); + mmc->clock, mmc_card_ddr(mmc), mmc->bus_width);
ret = uniphier_sd_set_bus_width(priv, mmc); if (ret) diff --git a/drivers/mmc/xenon_sdhci.c b/drivers/mmc/xenon_sdhci.c index f0a33c1..7685e15 100644 --- a/drivers/mmc/xenon_sdhci.c +++ b/drivers/mmc/xenon_sdhci.c @@ -237,7 +237,7 @@ static void xenon_mmc_phy_set(struct sdhci_host *host) sdhci_writew(host, var, SDHCI_CLOCK_CONTROL);
var = sdhci_readl(host, EMMC_PHY_FUNC_CONTROL); - if (host->mmc->ddr_mode) { + if (mmc_card_ddr(host->mmc)) { var |= (DQ_DDR_MODE_MASK << DQ_DDR_MODE_SHIFT) | CMD_DDR_MODE; } else { var &= ~((DQ_DDR_MODE_MASK << DQ_DDR_MODE_SHIFT) | @@ -329,7 +329,7 @@ static void xenon_sdhci_set_ios_post(struct sdhci_host *host) if (IS_SD(host->mmc)) { /* SD/SDIO */ if (pwr_18v) { - if (host->mmc->ddr_mode) + if (mmc_card_ddr(host->mmc)) priv->timing = MMC_TIMING_UHS_DDR50; else if (speed <= 25000000) priv->timing = MMC_TIMING_UHS_SDR25; @@ -343,7 +343,7 @@ static void xenon_sdhci_set_ios_post(struct sdhci_host *host) } } else { /* eMMC */ - if (host->mmc->ddr_mode) + if (mmc_card_ddr(host->mmc)) priv->timing = MMC_TIMING_MMC_DDR52; else if (speed <= 26000000) priv->timing = MMC_TIMING_LEGACY; diff --git a/include/mmc.h b/include/mmc.h index a2ef986..bde8b37 100644 --- a/include/mmc.h +++ b/include/mmc.h @@ -523,7 +523,6 @@ struct mmc { char op_cond_pending; /* 1 if we are waiting on an op_cond command */ char init_in_progress; /* 1 if we have done mmc_start_init() */ char preinit; /* start init as early as possible */ - int ddr_mode; #ifdef CONFIG_DM_MMC struct udevice *dev; /* Device for this MMC controller */ #endif

The clock element is updated by mmc_set_clock every time, it denotes the current transfer speed.
Signed-off-by: Ziyuan Xu xzy.xu@rock-chips.com ---
cmd/mmc.c | 2 +- drivers/mmc/mmc.c | 10 +++++----- drivers/mmc/xenon_sdhci.c | 2 +- include/mmc.h | 1 - 4 files changed, 7 insertions(+), 8 deletions(-)
diff --git a/cmd/mmc.c b/cmd/mmc.c index ef05fa5..6ead27a 100644 --- a/cmd/mmc.c +++ b/cmd/mmc.c @@ -23,7 +23,7 @@ static void print_mmcinfo(struct mmc *mmc) (mmc->cid[1] >> 24), (mmc->cid[1] >> 16) & 0xff, (mmc->cid[1] >> 8) & 0xff, mmc->cid[1] & 0xff);
- printf("Tran Speed: %d\n", mmc->tran_speed); + printf("Tran Speed: %d\n", mmc->clock); printf("Rd Block Len: %d\n", mmc->read_bl_len);
printf("%s version %d.%d", IS_SD(mmc) ? "SD" : "MMC", diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index d47cfe6..953ffd8 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -1449,7 +1449,7 @@ static bool mmc_can_card_busy(struct mmc *) static int mmc_startup(struct mmc *mmc) { int err, i; - uint mult, freq; + uint mult, freq, tran_speed; u64 cmult, csize, capacity; struct mmc_cmd cmd; ALLOC_CACHE_ALIGN_BUFFER(u8, ext_csd, MMC_MAX_BLOCK_LEN); @@ -1545,7 +1545,7 @@ static int mmc_startup(struct mmc *mmc) freq = fbase[(cmd.response[0] & 0x7)]; mult = multipliers[((cmd.response[0] >> 3) & 0xf)];
- mmc->tran_speed = freq * mult; + tran_speed = freq * mult;
mmc->dsr_imp = ((cmd.response[1] >> 12) & 0x1); mmc->read_bl_len = 1 << ((cmd.response[1] >> 16) & 0xf); @@ -1792,11 +1792,11 @@ static int mmc_startup(struct mmc *mmc) return err;
if (mmc->card_caps & MMC_MODE_HS) - mmc->tran_speed = 50000000; + tran_speed = 50000000; else - mmc->tran_speed = 25000000; + tran_speed = 25000000;
- mmc_set_clock(mmc, mmc->tran_speed); + mmc_set_clock(mmc, tran_speed); }
/* Fix the block length for DDR mode */ diff --git a/drivers/mmc/xenon_sdhci.c b/drivers/mmc/xenon_sdhci.c index 7685e15..f18dc8f 100644 --- a/drivers/mmc/xenon_sdhci.c +++ b/drivers/mmc/xenon_sdhci.c @@ -318,7 +318,7 @@ static void xenon_mask_cmd_conflict_err(struct sdhci_host *host) static void xenon_sdhci_set_ios_post(struct sdhci_host *host) { struct xenon_sdhci_priv *priv = host->mmc->priv; - uint speed = host->mmc->tran_speed; + uint speed = host->mmc->clock; int pwr_18v = 0;
if ((sdhci_readb(host, SDHCI_POWER_CONTROL) & ~SDHCI_POWER_ON) == diff --git a/include/mmc.h b/include/mmc.h index bde8b37..05bf39d 100644 --- a/include/mmc.h +++ b/include/mmc.h @@ -504,7 +504,6 @@ struct mmc { u8 part_attr; u8 wr_rel_set; char part_config; - uint tran_speed; uint read_bl_len; uint write_bl_len; uint erase_grp_size; /* in 512-byte sectors */

So far mmc framework had support speed mode switch, it good to show the current speed mode from 'mmc info'.
Signed-off-by: Ziyuan Xu xzy.xu@rock-chips.com ---
cmd/mmc.c | 5 +++++ 1 file changed, 5 insertions(+)
diff --git a/cmd/mmc.c b/cmd/mmc.c index 6ead27a..832eeb0 100644 --- a/cmd/mmc.c +++ b/cmd/mmc.c @@ -15,6 +15,10 @@ static int curr_device = -1; static void print_mmcinfo(struct mmc *mmc) { int i; + const char *timing[] = { + "Legacy", "High Speed", "High Speed", "SDR12", + "SDR25", "SDR50", "SDR104", "DDR50", + "DDR52", "HS200", "HS400", "HS400 Enhanced Strobe"};
printf("Device: %s\n", mmc->cfg->name); printf("Manufacturer ID: %x\n", mmc->cid[0] >> 24); @@ -23,6 +27,7 @@ static void print_mmcinfo(struct mmc *mmc) (mmc->cid[1] >> 24), (mmc->cid[1] >> 16) & 0xff, (mmc->cid[1] >> 8) & 0xff, mmc->cid[1] & 0xff);
+ printf("Timing Interface: %s\n", timing[mmc->timing]); printf("Tran Speed: %d\n", mmc->clock); printf("Rd Block Len: %d\n", mmc->read_bl_len);

A common operation for a clock signal generator is to shift the phase of that signal. This patch introduces a new function to the clk.h API to dynamically adjust the phase of a clock signal. Additionally this patch introduces support for the new function in the clock framework via the .set_phase & .get_phase callback in struct clk_ops.
Signed-off-by: Ziyuan Xu xzy.xu@rock-chips.com ---
drivers/clk/clk-uclass.c | 20 ++++++++++++++++++++ include/clk-uclass.h | 17 +++++++++++++++++ include/clk.h | 20 ++++++++++++++++++++ 3 files changed, 57 insertions(+)
diff --git a/drivers/clk/clk-uclass.c b/drivers/clk/clk-uclass.c index 6fcfd69..47628b1 100644 --- a/drivers/clk/clk-uclass.c +++ b/drivers/clk/clk-uclass.c @@ -165,6 +165,26 @@ ulong clk_set_rate(struct clk *clk, ulong rate) return ops->set_rate(clk, rate); }
+int clk_get_phase(struct clk *clk) +{ + struct clk_ops *ops = clk_dev_ops(clk->dev); + + if (!ops->get_phase) + return -ENOSYS; + + return ops->get_phase(clk); +} + +int clk_set_phase(struct clk *clk, int degrees) +{ + struct clk_ops *ops = clk_dev_ops(clk->dev); + + if (!ops->set_phase) + return -ENOSYS; + + return ops->set_phase(clk, degrees); +} + int clk_enable(struct clk *clk) { struct clk_ops *ops = clk_dev_ops(clk->dev); diff --git a/include/clk-uclass.h b/include/clk-uclass.h index 07c1065..0e56daa 100644 --- a/include/clk-uclass.h +++ b/include/clk-uclass.h @@ -77,6 +77,23 @@ struct clk_ops { */ ulong (*set_rate)(struct clk *clk, ulong rate); /** + * clk_get_phase() - Get the phase shift of a clock signal. + * + * @clk: The clock to manipulate. + * @return the phase shift of a clock node in degrees, + * otherwise returns -ve error code. + */ + int (*get_phase)(struct clk *clk); + + /** + * clk_set_rate() - Adjust the phase shift of a clock signal. + * + * @clk: The clock to manipulate. + * @degrees: Numberof degrees the signal is shifted. + * @return 0 on success, or -ve error code. + */ + int (*set_phase)(struct clk *clk, int degrees); + /** * enable() - Enable a clock. * * @clk: The clock to manipulate. diff --git a/include/clk.h b/include/clk.h index 5a5c2ff..1858fef 100644 --- a/include/clk.h +++ b/include/clk.h @@ -157,6 +157,26 @@ ulong clk_get_rate(struct clk *clk); ulong clk_set_rate(struct clk *clk, ulong rate);
/** + * clk_get_phase() - Get the phase shift of a clock signal. + * + * @clk: A clock struct that was previously successfully requested by + * clk_request/get_by_*(). + * @return the phase shift of a clock node in degrees, otherwise returns + * -ve error code. + */ +int clk_get_phase(struct clk *clk); + +/** + * clk_set_rate() - Adjust the phase shift of a clock signal. + * + * @clk: A clock struct that was previously successfully requested by + * clk_request/get_by_*(). + * @degrees: Numberof degrees the signal is shifted. + * @return 0 on success, or -ve error code. + */ +int clk_set_phase(struct clk *clk, int degrees); + +/** * clk_enable() - Enable (turn on) a clock. * * @clk: A clock struct that was previously successfully requested by

Mmc clock automatically divide 2 in internal.
Before this: gpll = 594MHz, clock = 148.5MHz div = 594/148.5-1 = 3 output clock is 99MHz
After this: gpll = 594MHz, clock = 148.5MHz div = 297+148.5-1/148.5 = 2 output clock is 148.5Mhz
Signed-off-by: Ziyuan Xu xzy.xu@rock-chips.com ---
drivers/clk/rockchip/clk_rk3288.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/drivers/clk/rockchip/clk_rk3288.c b/drivers/clk/rockchip/clk_rk3288.c index fc369dd..b924a3b 100644 --- a/drivers/clk/rockchip/clk_rk3288.c +++ b/drivers/clk/rockchip/clk_rk3288.c @@ -535,7 +535,7 @@ static ulong rockchip_mmc_get_clk(struct rk3288_cru *cru, uint gclk_rate, }
src_rate = mux == EMMC_PLL_SELECT_24MHZ ? OSC_HZ : gclk_rate; - return DIV_TO_RATE(src_rate, div); + return DIV_TO_RATE(src_rate, div) / 2; }
static ulong rockchip_mmc_set_clk(struct rk3288_cru *cru, uint gclk_rate, @@ -545,10 +545,10 @@ static ulong rockchip_mmc_set_clk(struct rk3288_cru *cru, uint gclk_rate, int mux;
debug("%s: gclk_rate=%u\n", __func__, gclk_rate); - src_clk_div = RATE_TO_DIV(gclk_rate, freq); + src_clk_div = DIV_ROUND_UP(gclk_rate / 2, freq);
if (src_clk_div > 0x3f) { - src_clk_div = RATE_TO_DIV(OSC_HZ, freq); + src_clk_div = DIV_ROUND_UP(OSC_HZ / 2, freq); mux = EMMC_PLL_SELECT_24MHZ; assert((int)EMMC_PLL_SELECT_24MHZ == (int)MMC0_PLL_SELECT_24MHZ);

This patch adds phase adjustment for mmc clock(ciu_sample), which is used to select the optimal sampling point of a data input.
The phase shift is achieved through 255 delay elements(40-80 picoseconds), and calculate the number of delay element via clock frequency.
Signed-off-by: Ziyuan Xu xzy.xu@rock-chips.com ---
drivers/clk/rockchip/clk_rk3288.c | 124 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 124 insertions(+)
diff --git a/drivers/clk/rockchip/clk_rk3288.c b/drivers/clk/rockchip/clk_rk3288.c index b924a3b..3279e01 100644 --- a/drivers/clk/rockchip/clk_rk3288.c +++ b/drivers/clk/rockchip/clk_rk3288.c @@ -514,6 +514,7 @@ static ulong rockchip_mmc_get_clk(struct rk3288_cru *cru, uint gclk_rate, switch (periph) { case HCLK_EMMC: case SCLK_EMMC: + case SCLK_EMMC_SAMPLE: con = readl(&cru->cru_clksel_con[12]); mux = (con >> EMMC_PLL_SHIFT) & EMMC_PLL_MASK; div = (con >> EMMC_DIV_SHIFT) & EMMC_DIV_MASK; @@ -669,7 +670,9 @@ static ulong rk3288_clk_get_rate(struct clk *clk) case HCLK_SDMMC: case HCLK_SDIO0: case SCLK_EMMC: + case SCLK_EMMC_SAMPLE: case SCLK_SDMMC: + case SCLK_SDMMC_SAMPLE: case SCLK_SDIO0: new_rate = rockchip_mmc_get_clk(priv->cru, gclk_rate, clk->id); break; @@ -784,9 +787,130 @@ static ulong rk3288_clk_set_rate(struct clk *clk, ulong rate) return new_rate; }
+#define ROCKCHIP_MMC_DELAY_SEL BIT(10) +#define ROCKCHIP_MMC_DEGREE_MASK 0x3 +#define ROCKCHIP_MMC_DELAYNUM_OFFSET 2 +#define ROCKCHIP_MMC_DELAYNUM_MASK (0xff << ROCKCHIP_MMC_DELAYNUM_OFFSET) + +#define PSECS_PER_SEC 1000000000000LL +/* + * Each fine delay is between 44ps-77ps. Assume each fine delay is 60ps to + * simplify calculations. So 45degs could be anywhere between 33deg and 57.8deg. + */ +#define ROCKCHIP_MMC_DELAY_ELEMENT_PSEC 60 + +int rockchip_mmc_get_phase(struct clk *clk) +{ + struct rk3288_clk_priv *priv = dev_get_priv(clk->dev); + struct rk3288_cru *cru = priv->cru; + u32 raw_value, delay_num; + u16 degrees = 0; + ulong rate; + + rate = rk3288_clk_get_rate(clk); + + if (rate < 0) + return rate; + + if (clk->id == SCLK_EMMC_SAMPLE) + raw_value = readl(&cru->cru_emmc_con[1]); + else + raw_value = readl(&cru->cru_sdmmc_con[1]); + + degrees = (raw_value & ROCKCHIP_MMC_DEGREE_MASK) * 90; + + if (raw_value & ROCKCHIP_MMC_DELAY_SEL) { + /* degrees/delaynum * 10000 */ + unsigned long factor = (ROCKCHIP_MMC_DELAY_ELEMENT_PSEC / 10) * + 36 * (rate / 1000000); + + delay_num = (raw_value & ROCKCHIP_MMC_DELAYNUM_MASK); + delay_num >>= ROCKCHIP_MMC_DELAYNUM_OFFSET; + degrees += DIV_ROUND_CLOSEST(delay_num * factor, 10000); + } + + return degrees % 360; +} + +int rockchip_mmc_set_phase(struct clk *clk, u32 degrees) +{ + struct rk3288_clk_priv *priv = dev_get_priv(clk->dev); + struct rk3288_cru *cru = priv->cru; + u8 nineties, remainder, delay_num; + u32 raw_value, delay; + ulong rate; + + rate = rk3288_clk_get_rate(clk); + + if (rate < 0) + return rate; + + nineties = degrees / 90; + remainder = (degrees % 90); + + /* + * Convert to delay; do a little extra work to make sure we + * don't overflow 32-bit / 64-bit numbers. + */ + delay = 10000000; /* PSECS_PER_SEC / 10000 / 10 */ + delay *= remainder; + delay = DIV_ROUND_CLOSEST(delay, (rate / 1000) * 36 * + (ROCKCHIP_MMC_DELAY_ELEMENT_PSEC / 10)); + + delay_num = (u8)min_t(u32, delay, 255); + + raw_value = delay_num ? ROCKCHIP_MMC_DELAY_SEL : 0; + raw_value |= delay_num << ROCKCHIP_MMC_DELAYNUM_OFFSET; + raw_value |= nineties; + + if (clk->id == SCLK_EMMC_SAMPLE) + writel(raw_value | 0xffff0000, &cru->cru_emmc_con[1]); + else + writel(raw_value | 0xffff0000, &cru->cru_sdmmc_con[1]); + + debug("mmc set_phase(%d) delay_nums=%u reg=%#x actual_degrees=%d\n", + degrees, delay_num, raw_value, rockchip_mmc_get_phase(clk)); + + return 0; +} + +static int rk3288_clk_get_phase(struct clk *clk) +{ + int ret; + + switch (clk->id) { + case SCLK_EMMC_SAMPLE: + case SCLK_SDMMC_SAMPLE: + ret = rockchip_mmc_get_phase(clk); + break; + default: + return -ENOENT; + } + + return ret; +} + +static int rk3288_clk_set_phase(struct clk *clk, int degrees) +{ + int ret; + + switch (clk->id) { + case SCLK_EMMC_SAMPLE: + case SCLK_SDMMC_SAMPLE: + ret = rockchip_mmc_set_phase(clk, degrees); + break; + default: + return -ENOENT; + } + + return ret; +} + static struct clk_ops rk3288_clk_ops = { .get_rate = rk3288_clk_get_rate, .set_rate = rk3288_clk_set_rate, + .get_phase = rk3288_clk_get_phase, + .set_phase = rk3288_clk_set_phase, };
static int rk3288_clk_ofdata_to_platdata(struct udevice *dev)

Before this: gpll = 594MHz, set_clock = 200MHz div = 594/200 = 2 real clock is 297MHz
After this: gpll = 594MHz, clock = 148.5MHz div = 594+200-1/200 = 3 real clock is 198Mhz
Signed-off-by: Ziyuan Xu xzy.xu@rock-chips.com ---
drivers/clk/rockchip/clk_rk3399.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/clk/rockchip/clk_rk3399.c b/drivers/clk/rockchip/clk_rk3399.c index 026ed4d..3656f02 100644 --- a/drivers/clk/rockchip/clk_rk3399.c +++ b/drivers/clk/rockchip/clk_rk3399.c @@ -794,7 +794,7 @@ static ulong rk3399_mmc_set_clk(struct rk3399_cru *cru, break; case SCLK_EMMC: /* Select aclk_emmc source from GPLL */ - src_clk_div = GPLL_HZ / aclk_emmc; + src_clk_div = DIV_ROUND_UP(GPLL_HZ, aclk_emmc); assert(src_clk_div - 1 < 31);
rk_clrsetreg(&cru->clksel_con[21], @@ -803,7 +803,7 @@ static ulong rk3399_mmc_set_clk(struct rk3399_cru *cru, (src_clk_div - 1) << ACLK_EMMC_DIV_CON_SHIFT);
/* Select clk_emmc source from GPLL too */ - src_clk_div = GPLL_HZ / set_rate; + src_clk_div = DIV_ROUND_UP(GPLL_HZ, set_rate); assert(src_clk_div - 1 < 127);
rk_clrsetreg(&cru->clksel_con[22],

For the HS200/HS400/SDR104, tuning is needed to determine the optimal sampling point. Actual tuning procedure is provided by specific host controller driver.
Signed-off-by: Ziyuan Xu xzy.xu@rock-chips.com ---
drivers/mmc/dw_mmc.c | 18 ++++++++++++++++++ include/dwmmc.h | 1 + 2 files changed, 19 insertions(+)
diff --git a/drivers/mmc/dw_mmc.c b/drivers/mmc/dw_mmc.c index 7e9ffc2..c05288c 100644 --- a/drivers/mmc/dw_mmc.c +++ b/drivers/mmc/dw_mmc.c @@ -404,6 +404,22 @@ static bool dwmci_card_busy(struct mmc *mmc) }
#ifdef CONFIG_DM_MMC_OPS +static int dwmci_execute_tuning(struct udevice *dev, u32 opcode) +{ + struct mmc *mmc = mmc_get_mmc_dev(dev); +#else +static int dwmci_execute_tuning(struct mmc *mmc, u32 opcode) +{ +#endif + struct dwmci_host *host = (struct dwmci_host *)mmc->priv; + + if (!host->execute_tuning) + return -EIO; + + return host->execute_tuning(host, opcode); +} + +#ifdef CONFIG_DM_MMC_OPS static int dwmci_set_ios(struct udevice *dev) { struct mmc *mmc = mmc_get_mmc_dev(dev); @@ -498,6 +514,7 @@ const struct dm_mmc_ops dm_dwmci_ops = { .card_busy = dwmci_card_busy, .send_cmd = dwmci_send_cmd, .set_ios = dwmci_set_ios, + .execute_tuning = dwmci_execute_tuning, };
#else @@ -506,6 +523,7 @@ static const struct mmc_ops dwmci_ops = { .send_cmd = dwmci_send_cmd, .set_ios = dwmci_set_ios, .init = dwmci_init, + .execute_tuning = dwmci_execute_tuning, }; #endif
diff --git a/include/dwmmc.h b/include/dwmmc.h index 4dda009..95be7c2 100644 --- a/include/dwmmc.h +++ b/include/dwmmc.h @@ -180,6 +180,7 @@ struct dwmci_host { * @freq: Frequency the host is trying to achieve */ unsigned int (*get_mmc_clk)(struct dwmci_host *host, uint freq); + int (*execute_tuning)(struct dwmci_host *host, u32 opcode); #ifndef CONFIG_BLK struct mmc_config cfg; #endif

This algorithm will try 1 degree increment, since there's no way to tell what resolution the underlying phase code uses. As an added bonus, doing many tunings yields better results since some tests are run more than once (ex: if the underlying driver use 45 degree increments, the tuning code will try the same angle more than once).
It will then construct a list of good phase ranges (even range that cross 270/0), will pick the biggest range then it will set the sample_clk to the middle of that range.
Please notice that it tuning only 0-270 degree in U-Boot, but kernel tuning range is 0-360 degree. Below are two reasons about this: 1. Expect data-related interrupt may miss during 270-360 degree on rockchip platform, dw_mmc driver will poll for data interrupt until 240 seconds timeout afterwards. And the host controller will be left in an unpredictable state. 2. The phase of a clock signal is shift by some delay elements on rockchip platform. And the delay element affected by logic voltage and temperature in runtime. These factors wouldn't have changed a lot in U-Boot stage.
Signed-off-by: Ziyuan Xu xzy.xu@rock-chips.com ---
drivers/mmc/rockchip_dw_mmc.c | 121 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 121 insertions(+)
diff --git a/drivers/mmc/rockchip_dw_mmc.c b/drivers/mmc/rockchip_dw_mmc.c index 2885ef2..474ca1c 100644 --- a/drivers/mmc/rockchip_dw_mmc.c +++ b/drivers/mmc/rockchip_dw_mmc.c @@ -30,6 +30,7 @@ struct rockchip_mmc_plat {
struct rockchip_dwmmc_priv { struct clk clk; + struct clk sample_clk; struct dwmci_host host; int fifo_depth; bool fifo_mode; @@ -99,6 +100,123 @@ static int rockchip_dwmmc_ofdata_to_platdata(struct udevice *dev) return 0; }
+#define NUM_PHASES 270 +#define TUNING_ITERATION_TO_PHASE(i) (DIV_ROUND_UP((i) * 270, NUM_PHASES)) + +static int rockchip_dwmmc_execute_tuning(struct dwmci_host *host, u32 opcode) +{ + int ret = 0; + int i; + bool v, prev_v = 0, first_v; + struct range_t { + int start; + int end; /* inclusive */ + }; + struct range_t *ranges; + unsigned int range_count = 0; + int longest_range_len = -1; + int longest_range = -1; + int middle_phase; + struct udevice *dev = host->priv; + struct rockchip_dwmmc_priv *priv = dev_get_priv(dev); + struct mmc *mmc = host->mmc; + + if (IS_ERR(&priv->sample_clk)) + return -EIO; + + ranges = calloc(sizeof(*ranges), NUM_PHASES / 2 + 1); + if (!ranges) + return -ENOMEM; + + /* Try each phase and extract good ranges */ + for (i = 0; i < NUM_PHASES; ) { + clk_set_phase(&priv->sample_clk, TUNING_ITERATION_TO_PHASE(i)); + + v = !mmc_send_tuning(mmc, opcode); + + if (i == 0) + first_v = v; + + if ((!prev_v) && v) { + range_count++; + ranges[range_count - 1].start = i; + } + if (v) { + ranges[range_count - 1].end = i; + i++; + } else if (i == NUM_PHASES - 1) { + /* No extra skipping rules if we're at the end */ + i++; + } else { + /* + * No need to check too close to an invalid + * one since testing bad phases is slow. Skip + * 20 degrees. + */ + i += DIV_ROUND_UP(20 * NUM_PHASES, NUM_PHASES); + + /* Always test the last one */ + if (i >= NUM_PHASES) + i = NUM_PHASES - 1; + } + + prev_v = v; + } + + if (range_count == 0) { + debug("All phases bad!"); + ret = -EIO; + goto free; + } + + /* wrap around case, merge the end points */ + if ((range_count > 1) && first_v && v) { + ranges[0].start = ranges[range_count - 1].start; + range_count--; + } + + if (ranges[0].start == 0 && ranges[0].end == NUM_PHASES - 1) { + clk_set_phase(&priv->sample_clk, + TUNING_ITERATION_TO_PHASE(NUM_PHASES / 2)); + debug("All phases work, using middle phase.\n"); + goto free; + } + + /* Find the longest range */ + for (i = 0; i < range_count; i++) { + int len = (ranges[i].end - ranges[i].start + 1); + + if (len < 0) + len += NUM_PHASES; + + if (longest_range_len < len) { + longest_range_len = len; + longest_range = i; + } + + debug("Good phase range %d-%d (%d len)\n", + TUNING_ITERATION_TO_PHASE(ranges[i].start), + TUNING_ITERATION_TO_PHASE(ranges[i].end), len); + } + + printf("Best phase range %d-%d (%d len)\n", + TUNING_ITERATION_TO_PHASE(ranges[longest_range].start), + TUNING_ITERATION_TO_PHASE(ranges[longest_range].end), + longest_range_len); + + middle_phase = ranges[longest_range].start + longest_range_len / 2; + middle_phase %= NUM_PHASES; + debug("Successfully tuned phase to %d\n", + TUNING_ITERATION_TO_PHASE(middle_phase)); + + clk_set_phase(&priv->sample_clk, + TUNING_ITERATION_TO_PHASE(middle_phase)); + +free: + free(ranges); + return ret; +} + static int rockchip_dwmmc_probe(struct udevice *dev) { struct rockchip_mmc_plat *plat = dev_get_platdata(dev); @@ -115,6 +233,7 @@ static int rockchip_dwmmc_probe(struct udevice *dev) host->ioaddr = map_sysmem(dtplat->reg[0], dtplat->reg[1]); host->buswidth = dtplat->bus_width; host->get_mmc_clk = rockchip_dwmmc_get_mmc_clk; + host->execute_tuning = rockchip_dwmmc_execute_tuning; host->priv = dev; host->dev_index = 0; priv->fifo_depth = dtplat->fifo_depth; @@ -128,6 +247,8 @@ static int rockchip_dwmmc_probe(struct udevice *dev) ret = clk_get_by_name(dev, "ciu", &priv->clk); if (ret < 0) return ret; + clk_get_by_name(dev, "ciu_sample", &priv->sample_clk); + host->execute_tuning = rockchip_dwmmc_execute_tuning; #endif host->fifoth_val = MSIZE(0x2) | RX_WMARK(priv->fifo_depth / 2 - 1) |

Per dw_mmc databook, it's recommend that reset the host contoller if some data-related error occurre during tuning progress.
Signed-off-by: Ziyuan Xu xzy.xu@rock-chips.com ---
drivers/mmc/dw_mmc.c | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-)
diff --git a/drivers/mmc/dw_mmc.c b/drivers/mmc/dw_mmc.c index c05288c..e862eb2 100644 --- a/drivers/mmc/dw_mmc.c +++ b/drivers/mmc/dw_mmc.c @@ -97,7 +97,7 @@ static int dwmci_data_transfer(struct dwmci_host *host, struct mmc_data *data) { int ret = 0; u32 timeout = 240000; - u32 mask, size, i, len = 0; + u32 status, ctrl, mask, size, i, len = 0; u32 *buf = NULL; ulong start = get_timer(0); u32 fifo_depth = (((host->fifoth_val & RX_WMARK_MASK) >> @@ -114,6 +114,23 @@ static int dwmci_data_transfer(struct dwmci_host *host, struct mmc_data *data) /* Error during data transfer. */ if (mask & (DWMCI_DATA_ERR | DWMCI_DATA_TOUT)) { debug("%s: DATA ERROR!\n", __func__); + + dwmci_wait_reset(host, DWMCI_RESET_ALL); + dwmci_writel(host, DWMCI_CMD, DWMCI_CMD_PRV_DAT_WAIT | + DWMCI_CMD_UPD_CLK | DWMCI_CMD_START); + + do { + status = dwmci_readl(host, DWMCI_CMD); + if (timeout-- < 0) + ret = -ETIMEDOUT; + } while (status & DWMCI_CMD_START); + + if (!host->fifo_mode) { + ctrl = dwmci_readl(host, DWMCI_BMOD); + ctrl |= DWMCI_BMOD_IDMAC_RESET; + dwmci_writel(host, DWMCI_BMOD, ctrl); + } + ret = -EINVAL; break; }

4.41+ eMMC card devices can run at 52MHz on DDR 8-bit mode, it can improve write/read performance. Host driver can set MMC_MODE_DDR_52Mhz to enable this feature.
Signed-off-by: Ziyuan Xu xzy.xu@rock-chips.com ---
drivers/mmc/mmc.c | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-)
diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index 953ffd8..c1f54c3 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -795,6 +795,27 @@ static int mmc_select_hs(struct mmc *mmc) return ret; }
+static int mmc_select_hs_ddr(struct mmc *mmc) +{ + u32 ext_csd_bits; + int err = 0; + + if (mmc->bus_width == MMC_BUS_WIDTH_1BIT) + return 0; + + ext_csd_bits = (mmc->bus_width == MMC_BUS_WIDTH_8BIT) ? + EXT_CSD_DDR_BUS_WIDTH_8 : EXT_CSD_DDR_BUS_WIDTH_4; + + err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, + EXT_CSD_BUS_WIDTH, ext_csd_bits); + if (err) + return err; + + mmc_set_timing(mmc, MMC_TIMING_MMC_DDR52); + + return 0; +} + #ifndef CONFIG_SPL_BUILD static int mmc_select_hs200(struct mmc *mmc) { @@ -941,8 +962,11 @@ static int mmc_change_freq(struct mmc *mmc)
if (mmc_card_hs200(mmc)) err = mmc_hs200_tuning(mmc); - else + else if (!mmc_card_hs400es(mmc)) { err = mmc_select_bus_width(mmc) > 0 ? 0 : err; + if (!err && avail_type & EXT_CSD_CARD_TYPE_DDR_52) + err = mmc_select_hs_ddr(mmc); + }
return err; }

The clk_divider must be set to 1 on ddr52 8bit mode for rockchip platform. Otherwise we will get a data crc error during data transmission.
Signed-off-by: Ziyuan Xu xzy.xu@rock-chips.com ---
drivers/mmc/dw_mmc.c | 2 +- drivers/mmc/rockchip_dw_mmc.c | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-)
diff --git a/drivers/mmc/dw_mmc.c b/drivers/mmc/dw_mmc.c index e862eb2..dcd7fba 100644 --- a/drivers/mmc/dw_mmc.c +++ b/drivers/mmc/dw_mmc.c @@ -344,7 +344,7 @@ static int dwmci_setup_bus(struct dwmci_host *host, u32 freq) int timeout = 10000; unsigned long sclk;
- if ((freq == host->clock) || (freq == 0)) + if (freq == 0) return 0; /* * If host->get_mmc_clk isn't defined, diff --git a/drivers/mmc/rockchip_dw_mmc.c b/drivers/mmc/rockchip_dw_mmc.c index 474ca1c..b2b7f5a 100644 --- a/drivers/mmc/rockchip_dw_mmc.c +++ b/drivers/mmc/rockchip_dw_mmc.c @@ -43,6 +43,13 @@ static uint rockchip_dwmmc_get_mmc_clk(struct dwmci_host *host, uint freq) struct rockchip_dwmmc_priv *priv = dev_get_priv(dev); int ret;
+ /* + * If DDR52 8bit mode(only emmc work in 8bit mode), + * divider must be set 1 + */ + if (mmc_card_ddr52(host->mmc) && host->mmc->bus_width == 8) + freq *= 2; + ret = clk_set_rate(&priv->clk, freq); if (ret < 0) { printf("%s: err=%d\n", __func__, ret);

Hosts capable of 8-bit transfers can also do 4 bits.
Signed-off-by: Ziyuan Xu xzy.xu@rock-chips.com ---
drivers/mmc/dw_mmc.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/drivers/mmc/dw_mmc.c b/drivers/mmc/dw_mmc.c index dcd7fba..3b89e7a 100644 --- a/drivers/mmc/dw_mmc.c +++ b/drivers/mmc/dw_mmc.c @@ -559,8 +559,7 @@ void dwmci_setup_cfg(struct mmc_config *cfg, struct dwmci_host *host, cfg->host_caps = host->caps;
if (host->buswidth == 8) { - cfg->host_caps |= MMC_MODE_8BIT; - cfg->host_caps &= ~MMC_MODE_4BIT; + cfg->host_caps |= MMC_MODE_8BIT | MMC_MODE_4BIT; } else { cfg->host_caps |= MMC_MODE_4BIT; cfg->host_caps &= ~MMC_MODE_8BIT;

Rockchip sdhci controller capable of 8-bit transfer. The original can only run at 4 bit mode.
Signed-off-by: Ziyuan Xu xzy.xu@rock-chips.com ---
drivers/mmc/rockchip_sdhci.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+)
diff --git a/drivers/mmc/rockchip_sdhci.c b/drivers/mmc/rockchip_sdhci.c index bdde831..562fb35 100644 --- a/drivers/mmc/rockchip_sdhci.c +++ b/drivers/mmc/rockchip_sdhci.c @@ -47,11 +47,26 @@ static int arasan_sdhci_probe(struct udevice *dev)
host->name = dev->name; host->ioaddr = map_sysmem(dtplat->reg[1], dtplat->reg[3]); + host->host_caps |= MMC_MODE_8BIT; max_frequency = dtplat->max_frequency; ret = clk_get_by_index_platdata(dev, 0, dtplat->clocks, &clk); #else max_frequency = fdtdec_get_int(gd->fdt_blob, dev_of_offset(dev), "max-frequency", 0); + switch (fdtdec_get_int(gd->fdt_blob, dev_of_offset(dev), + "bus-width", 4)) { + case 8: + host->host_caps |= MMC_MODE_8BIT; + break; + case 4: + host->host_caps |= MMC_MODE_4BIT; + break; + case 1: + break; + default: + printf("Invalid "bus-width" value\n"); + return -EINVAL; + } ret = clk_get_by_index(dev, 0, &clk); #endif if (!ret) {

Overwrite host->clock after clock setting to avoid repetitive reset clock.
Signed-off-by: Ziyuan Xu xzy.xu@rock-chips.com ---
drivers/mmc/sdhci.c | 3 +++ 1 file changed, 3 insertions(+)
diff --git a/drivers/mmc/sdhci.c b/drivers/mmc/sdhci.c index 48bac04..ad86278 100644 --- a/drivers/mmc/sdhci.c +++ b/drivers/mmc/sdhci.c @@ -390,6 +390,9 @@ static int sdhci_set_clock(struct mmc *mmc, unsigned int clock)
clk |= SDHCI_CLOCK_CARD_EN; sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); + + host->clock = clock; + return 0; }

To support UHS speed mode, controller should enable 1.8V signaling and select one of UHS modes.
Signed-off-by: Ziyuan Xu xzy.xu@rock-chips.com ---
drivers/mmc/sdhci.c | 40 ++++++++++++++++++++++++++++++++++++++++ include/mmc.h | 1 + include/sdhci.h | 17 +++++++++++++++++ 3 files changed, 58 insertions(+)
diff --git a/drivers/mmc/sdhci.c b/drivers/mmc/sdhci.c index ad86278..8f4a2a1 100644 --- a/drivers/mmc/sdhci.c +++ b/drivers/mmc/sdhci.c @@ -426,6 +426,39 @@ static void sdhci_set_power(struct sdhci_host *host, unsigned short power) sdhci_writeb(host, pwr, SDHCI_POWER_CONTROL); }
+static void sdhci_set_uhs_signaling(struct sdhci_host *host) +{ + u16 ctrl_2; + u32 timing = host->mmc->timing; + + ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2); + /* Select Bus Speed Mode for host */ + ctrl_2 &= ~SDHCI_CTRL_UHS_MASK; + + if ((timing != MMC_TIMING_LEGACY) && + (timing != MMC_TIMING_MMC_HS) && + (timing != MMC_TIMING_SD_HS)) + ctrl_2 |= SDHCI_CTRL_VDD_180; + + if ((timing == MMC_TIMING_MMC_HS200) || + (timing == MMC_TIMING_UHS_SDR104)) + ctrl_2 |= SDHCI_CTRL_UHS_SDR104 | SDHCI_CTRL_DRV_TYPE_A; + else if (timing == MMC_TIMING_UHS_SDR12) + ctrl_2 |= SDHCI_CTRL_UHS_SDR12; + else if (timing == MMC_TIMING_UHS_SDR25) + ctrl_2 |= SDHCI_CTRL_UHS_SDR25; + else if (timing == MMC_TIMING_UHS_SDR50) + ctrl_2 |= SDHCI_CTRL_UHS_SDR50; + else if ((timing == MMC_TIMING_UHS_DDR50) || + (timing == MMC_TIMING_MMC_DDR52)) + ctrl_2 |= SDHCI_CTRL_UHS_DDR50; + else if (timing == MMC_TIMING_MMC_HS400 || + timing == MMC_TIMING_MMC_HS400ES) + ctrl_2 |= SDHCI_CTRL_HS400 | SDHCI_CTRL_DRV_TYPE_A; + + sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2); +} + #ifdef CONFIG_DM_MMC_OPS static bool sdhci_card_busy(struct udevice *dev) { @@ -485,6 +518,13 @@ static int sdhci_set_ios(struct mmc *mmc)
sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
+ if ((mmc->timing != MMC_TIMING_LEGACY) && + (mmc->timing != MMC_TIMING_MMC_HS) && + (mmc->timing != MMC_TIMING_SD_HS)) + sdhci_set_power(host, MMC_VDD_165_195_SHIFT); + + sdhci_set_uhs_signaling(host); + /* If available, call the driver specific "post" set_ios() function */ if (host->ops && host->ops->set_ios_post) host->ops->set_ios_post(host); diff --git a/include/mmc.h b/include/mmc.h index 05bf39d..b5817f3 100644 --- a/include/mmc.h +++ b/include/mmc.h @@ -138,6 +138,7 @@
#define MMC_STATE_PRG (7 << 9)
+#define MMC_VDD_165_195_SHIFT 7 #define MMC_VDD_165_195 0x00000080 /* VDD voltage 1.65 - 1.95 */ #define MMC_VDD_20_21 0x00000100 /* VDD voltage 2.0 ~ 2.1 */ #define MMC_VDD_21_22 0x00000200 /* VDD voltage 2.1 ~ 2.2 */ diff --git a/include/sdhci.h b/include/sdhci.h index 75432db..449ada1 100644 --- a/include/sdhci.h +++ b/include/sdhci.h @@ -147,6 +147,23 @@ #define SDHCI_ACMD12_ERR 0x3C
/* 3E-3F reserved */ +#define SDHCI_HOST_CONTROL2 0x3E +#define SDHCI_CTRL_UHS_MASK 0x0007 +#define SDHCI_CTRL_UHS_SDR12 0x0000 +#define SDHCI_CTRL_UHS_SDR25 0x0001 +#define SDHCI_CTRL_UHS_SDR50 0x0002 +#define SDHCI_CTRL_UHS_SDR104 0x0003 +#define SDHCI_CTRL_UHS_DDR50 0x0004 +#define SDHCI_CTRL_HS400 0x0005 +#define SDHCI_CTRL_VDD_180 0x0008 +#define SDHCI_CTRL_DRV_TYPE_MASK 0x0030 +#define SDHCI_CTRL_DRV_TYPE_B 0x0000 +#define SDHCI_CTRL_DRV_TYPE_A 0x0010 +#define SDHCI_CTRL_DRV_TYPE_C 0x0020 +#define SDHCI_CTRL_DRV_TYPE_D 0x0030 +#define SDHCI_CTRL_EXEC_TUNING 0x0040 +#define SDHCI_CTRL_TUNED_CLK 0x0080 +#define SDHCI_CTRL_PRESET_VAL_ENABLE 0x8000
#define SDHCI_CAPABILITIES 0x40 #define SDHCI_TIMEOUT_CLK_MASK 0x0000003F

In fact, the original name is unsuitable for its behavior. It's better to rename to set_clock_ext.
Signed-off-by: Ziyuan Xu xzy.xu@rock-chips.com ---
drivers/mmc/s5p_sdhci.c | 4 ++-- drivers/mmc/sdhci.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/drivers/mmc/s5p_sdhci.c b/drivers/mmc/s5p_sdhci.c index 640ea02..b8d3ded 100644 --- a/drivers/mmc/s5p_sdhci.c +++ b/drivers/mmc/s5p_sdhci.c @@ -73,14 +73,14 @@ static void s5p_sdhci_set_control_reg(struct sdhci_host *host) sdhci_writel(host, ctrl, SDHCI_CONTROL2); }
-static void s5p_set_clock(struct sdhci_host *host, u32 div) +static void s5p_set_clock_ext(struct sdhci_host *host, u32 div) { /* ToDo : Use the Clock Framework */ set_mmc_clk(host->index, div); }
static const struct sdhci_ops s5p_sdhci_ops = { - .set_clock = &s5p_set_clock, + .set_clock_ext = &s5p_set_clock_ext, .set_control_reg = &s5p_sdhci_set_control_reg, };
diff --git a/drivers/mmc/sdhci.c b/drivers/mmc/sdhci.c index 8f4a2a1..5c6dbdc 100644 --- a/drivers/mmc/sdhci.c +++ b/drivers/mmc/sdhci.c @@ -366,8 +366,8 @@ static int sdhci_set_clock(struct mmc *mmc, unsigned int clock) div >>= 1; }
- if (host->ops && host->ops->set_clock) - host->ops->set_clock(host, div); + if (host->ops && host->ops->set_clock_ext) + host->ops->set_clock_ext(host, div);
clk |= (div & SDHCI_DIV_MASK) << SDHCI_DIVIDER_SHIFT; clk |= ((div & SDHCI_DIV_HI_MASK) >> SDHCI_DIV_MASK_LEN)

For arasan-rk3399-sdhci controller, we should make sure the phy is in poweroff status before we configure the clock stuff. So that we need to export it for phy configuration.
Signed-off-by: Ziyuan Xu xzy.xu@rock-chips.com ---
drivers/mmc/sdhci.c | 16 +++++++--------- include/sdhci.h | 5 ++++- 2 files changed, 11 insertions(+), 10 deletions(-)
diff --git a/drivers/mmc/sdhci.c b/drivers/mmc/sdhci.c index 5c6dbdc..b9cd13a 100644 --- a/drivers/mmc/sdhci.c +++ b/drivers/mmc/sdhci.c @@ -301,9 +301,8 @@ static int sdhci_send_command(struct mmc *mmc, struct mmc_cmd *cmd, return -ECOMM; }
-static int sdhci_set_clock(struct mmc *mmc, unsigned int clock) +int sdhci_set_clock(struct sdhci_host *host, unsigned int clock) { - struct sdhci_host *host = mmc->priv; unsigned int div, clk = 0, timeout;
/* Wait max 20 ms */ @@ -319,12 +318,10 @@ static int sdhci_set_clock(struct mmc *mmc, unsigned int clock) timeout--; udelay(100); } - sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL);
if (clock == 0) return 0; - if (SDHCI_GET_VERSION(host) >= SDHCI_SPEC_300) { /* * Check if the Host Controller supports Programmable Clock @@ -365,7 +362,6 @@ static int sdhci_set_clock(struct mmc *mmc, unsigned int clock) } div >>= 1; } - if (host->ops && host->ops->set_clock_ext) host->ops->set_clock_ext(host, div);
@@ -387,12 +383,10 @@ static int sdhci_set_clock(struct mmc *mmc, unsigned int clock) timeout--; udelay(1000); } - clk |= SDHCI_CLOCK_CARD_EN; sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
host->clock = clock; - return 0; }
@@ -490,8 +484,12 @@ static int sdhci_set_ios(struct mmc *mmc) if (host->ops && host->ops->set_control_reg) host->ops->set_control_reg(host);
- if (mmc->clock != host->clock) - sdhci_set_clock(mmc, mmc->clock); + if (mmc->clock != host->clock) { + if (host->ops && host->ops->set_clock) + host->ops->set_clock(host, mmc->clock); + else + sdhci_set_clock(host, mmc->clock); + }
/* Set bus width */ ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL); diff --git a/include/sdhci.h b/include/sdhci.h index 449ada1..da21194 100644 --- a/include/sdhci.h +++ b/include/sdhci.h @@ -254,7 +254,8 @@ struct sdhci_ops { int (*get_cd)(struct sdhci_host *host); void (*set_control_reg)(struct sdhci_host *host); void (*set_ios_post)(struct sdhci_host *host); - void (*set_clock)(struct sdhci_host *host, u32 div); + int (*set_clock)(struct sdhci_host *host, unsigned int clock); + void (*set_clock_ext)(struct sdhci_host *host, u32 div); };
struct sdhci_host { @@ -279,6 +280,8 @@ struct sdhci_host { struct mmc_config cfg; };
+int sdhci_set_clock(struct sdhci_host *host, unsigned int clock); + #ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS
static inline void sdhci_writel(struct sdhci_host *host, u32 val, int reg)

This patch gets phy phandle from dt-binding, and power cycle/re-configure phy whilst changing card clock.
Signed-off-by: Ziyuan Xu xzy.xu@rock-chips.com ---
drivers/mmc/rockchip_sdhci.c | 147 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 147 insertions(+)
diff --git a/drivers/mmc/rockchip_sdhci.c b/drivers/mmc/rockchip_sdhci.c index 562fb35..5b6b262 100644 --- a/drivers/mmc/rockchip_sdhci.c +++ b/drivers/mmc/rockchip_sdhci.c @@ -6,6 +6,7 @@ * SPDX-License-Identifier: GPL-2.0+ */
+#include <asm/arch/hardware.h> #include <common.h> #include <dm.h> #include <dt-structs.h> @@ -28,11 +29,151 @@ struct rockchip_sdhc_plat { struct mmc mmc; };
+struct rockchip_emmc_phy { + u32 emmcphy_con[7]; + u32 reserved; + u32 emmcphy_status; +}; + struct rockchip_sdhc { struct sdhci_host host; void *base; + struct rockchip_emmc_phy *phy; };
+#define PHYCTRL_CALDONE_MASK 0x1 +#define PHYCTRL_CALDONE_SHIFT 0x6 +#define PHYCTRL_CALDONE_DONE 0x1 + +#define PHYCTRL_DLLRDY_MASK 0x1 +#define PHYCTRL_DLLRDY_SHIFT 0x5 +#define PHYCTRL_DLLRDY_DONE 0x1 + +#define PHYCTRL_FREQSEL_200M 0x0 +#define PHYCTRL_FREQSEL_50M 0x1 +#define PHYCTRL_FREQSEL_100M 0x2 +#define PHYCTRL_FREQSEL_150M 0x3 + +#define KHz (1000) +#define MHz (1000 * KHz) + +static void rk3399_emmc_phy_power_on(struct rockchip_emmc_phy *phy, u32 clock) +{ + u32 caldone, dllrdy, freqsel; + uint start; + + writel(RK_CLRSETBITS(7 << 4, 0), &phy->emmcphy_con[6]); + writel(RK_CLRSETBITS(1 << 11, 1 << 11), &phy->emmcphy_con[0]); + writel(RK_CLRSETBITS(0xf << 7, 4 << 7), &phy->emmcphy_con[0]); + + /* + * According to the user manual, calpad calibration + * cycle takes more than 2us without the minimal recommended + * value, so we may need a little margin here + */ + udelay(3); + writel(RK_CLRSETBITS(1, 1), &phy->emmcphy_con[6]); + + /* + * According to the user manual, it asks driver to + * wait 5us for calpad busy trimming + */ + udelay(5); + caldone = readl(&phy->emmcphy_status); + caldone = (caldone >> PHYCTRL_CALDONE_SHIFT) & PHYCTRL_CALDONE_MASK; + if (caldone != PHYCTRL_CALDONE_DONE) { + debug("%s: caldone timeout.\n", __func__); + return; + } + + /* Set the frequency of the DLL operation */ + if (clock < 75 * MHz) + freqsel = PHYCTRL_FREQSEL_50M; + else if (clock < 125 * MHz) + freqsel = PHYCTRL_FREQSEL_100M; + else if (clock < 175 * MHz) + freqsel = PHYCTRL_FREQSEL_150M; + else + freqsel = PHYCTRL_FREQSEL_200M; + + /* Set the frequency of the DLL operation */ + writel(RK_CLRSETBITS(3 << 12, freqsel << 12), &phy->emmcphy_con[0]); + writel(RK_CLRSETBITS(1 << 1, 1 << 1), &phy->emmcphy_con[6]); + + start = get_timer(0); + + do { + udelay(1); + dllrdy = readl(&phy->emmcphy_status); + dllrdy = (dllrdy >> PHYCTRL_DLLRDY_SHIFT) & PHYCTRL_DLLRDY_MASK; + if (dllrdy == PHYCTRL_DLLRDY_DONE) + break; + } while (get_timer(start) < 50000); + + if (dllrdy != PHYCTRL_DLLRDY_DONE) + debug("%s: dllrdy timeout.\n", __func__); +} + +static void rk3399_emmc_phy_power_off(struct rockchip_emmc_phy *phy) +{ + writel(RK_CLRSETBITS(1, 0), &phy->emmcphy_con[6]); + writel(RK_CLRSETBITS(1 << 1, 0), &phy->emmcphy_con[6]); +} + +static int arasan_sdhci_set_clock(struct sdhci_host *host, unsigned int clock) +{ + struct rockchip_sdhc *priv = + container_of(host, struct rockchip_sdhc, host); + int cycle_phy = host->clock != clock && + clock > EMMC_MIN_FREQ; + + if (cycle_phy) + rk3399_emmc_phy_power_off(priv->phy); + + sdhci_set_clock(host, clock); + + if (cycle_phy) + rk3399_emmc_phy_power_on(priv->phy, clock); + + return 0; +} + +static struct sdhci_ops arasan_sdhci_ops = { + .set_clock = arasan_sdhci_set_clock, +}; + +static int arasan_get_phy(struct udevice *dev) +{ + struct rockchip_sdhc *priv = dev_get_priv(dev); + +#if CONFIG_IS_ENABLED(OF_PLATDATA) + priv->phy = (struct rockchip_emmc_phy *)0xff77f780; +#else + int phy_node, grf_node; + fdt_addr_t grf_base, grf_phy_offset; + + phy_node = fdtdec_lookup_phandle(gd->fdt_blob, + dev_of_offset(dev), "phys"); + if (phy_node <= 0) { + debug("Not found emmc phy device\n"); + return -ENODEV; + } + + grf_node = fdt_parent_offset(gd->fdt_blob, phy_node); + if (grf_node <= 0) { + debug("Not found usb phy device\n"); + return -ENODEV; + } + + grf_base = fdtdec_get_addr(gd->fdt_blob, grf_node, "reg"); + grf_phy_offset = fdtdec_get_addr_size_auto_parent(gd->fdt_blob, + grf_node, phy_node, "reg", 0, NULL, false); + + priv->phy = (struct rockchip_emmc_phy *)(grf_base + grf_phy_offset); +#endif + return 0; +} + static int arasan_sdhci_probe(struct udevice *dev) { struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev); @@ -77,6 +218,12 @@ static int arasan_sdhci_probe(struct udevice *dev) printf("%s fail to get clk\n", __func__); }
+ ret = arasan_get_phy(dev); + if (ret) + return ret; + + host->ops = &arasan_sdhci_ops; + host->quirks = SDHCI_QUIRK_WAIT_SEND_CMD; host->max_clk = max_frequency;

MMC framework has already implemented hs200 mode for eMMC devices, moreover the standard SDHC3.0 controller support tuning. We can set the corresponding flag in host->host_cpas.
Host driver issue tuning command repeatedly until the host controller resets Execute Tuning to 0. Host controller resets Execute Tuning to 0 when tuning is completed or tuning is not completed within 40 times. Host driver can abort this loop by 40 times CMD19/CMD21 issue or 150ms time-out. If tuning is completed successfully, driver set Sampling Clock Select to 1 and this means the host contorller start to use tuned sampling clcok. If tuning is failed, host controller keeps Sampling Clock Select to 0.
Signed-off-by: Ziyuan Xu xzy.xu@rock-chips.com ---
drivers/mmc/sdhci.c | 116 +++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 115 insertions(+), 1 deletion(-)
diff --git a/drivers/mmc/sdhci.c b/drivers/mmc/sdhci.c index b9cd13a..e346820 100644 --- a/drivers/mmc/sdhci.c +++ b/drivers/mmc/sdhci.c @@ -158,7 +158,10 @@ static int sdhci_send_command(struct mmc *mmc, struct mmc_cmd *cmd, static unsigned int cmd_timeout = SDHCI_CMD_DEFAULT_TIMEOUT;
sdhci_writel(host, SDHCI_INT_ALL_MASK, SDHCI_INT_STATUS); - mask = SDHCI_CMD_INHIBIT | SDHCI_DATA_INHIBIT; + mask = SDHCI_CMD_INHIBIT; + + if (data) + mask |= SDHCI_DATA_INHIBIT;
/* We shouldn't wait for data inihibit for stop commands, even though they might use busy signaling */ @@ -200,6 +203,13 @@ static int sdhci_send_command(struct mmc *mmc, struct mmc_cmd *cmd, if (data) flags |= SDHCI_CMD_DATA;
+ if (cmd->cmdidx == MMC_SEND_TUNING_BLOCK || + cmd->cmdidx == MMC_SEND_TUNING_BLOCK_HS200) { + mask &= ~SDHCI_INT_RESPONSE; + mask |= SDHCI_INT_DATA_AVAIL; + flags |= SDHCI_CMD_DATA; + } + /* Set Transfer mode regarding to data flag */ if (data != 0) { sdhci_writeb(host, 0xe, SDHCI_TIMEOUT_CONTROL); @@ -559,6 +569,108 @@ static int sdhci_init(struct mmc *mmc) return 0; }
+static int sdhci_send_tuning(struct sdhci_host *host, u32 opcode) +{ + struct mmc_cmd cmd; + + cmd.cmdidx = opcode; + cmd.resp_type = MMC_RSP_R1; + cmd.cmdarg = 0; + /* + * In response to CMD19, the card sends 64 bytes of tuning + * block to the Host Controller. So we set the block size + * to 64 here. + */ + if (opcode == MMC_SEND_TUNING_BLOCK_HS200 && + host->mmc->bus_width == MMC_BUS_WIDTH_8BIT) + sdhci_writew(host, SDHCI_MAKE_BLKSZ(7, 128), SDHCI_BLOCK_SIZE); + else + sdhci_writew(host, SDHCI_MAKE_BLKSZ(7, 64), SDHCI_BLOCK_SIZE); + + /* + * The tuning block is sent by the card to the host controller. + * So we set the TRNS_READ bit in the Transfer Mode register. + * This also takes care of setting DMA Enable and Multi Block + * Select in the same register to 0. + */ + sdhci_writew(host, SDHCI_TRNS_READ, SDHCI_TRANSFER_MODE); + +#ifdef CONFIG_DM_MMC_OPS + return sdhci_send_command(host->mmc->dev, &cmd, NULL); +#else + return sdhci_send_command(host->mmc, &cmd, NULL); +#endif +} + +#define MAX_TUNING_LOOP 40 +static int __sdhci_execute_tuning(struct sdhci_host *host, u32 opcode) +{ + int i; + int ret; + + /* + * Issue opcode repeatedly till Execute Tuning is set to 0 or the number + * of loops reaches 40 times. + */ + for (i = 0; i < MAX_TUNING_LOOP; i++) { + u16 ctrl; + + ret = sdhci_send_tuning(host, opcode); + + if (ret) + return ret; + + ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2); + if (!(ctrl & SDHCI_CTRL_EXEC_TUNING)) { + if (ctrl & SDHCI_CTRL_TUNED_CLK) + /* Tuning successfully */ + return 0; + break; + } + } + + return -ETIMEDOUT; +} + +#ifdef CONFIG_DM_MMC_OPS +static int sdhci_execute_tuning(struct udevice *dev, u32 opcode) +{ + struct mmc *mmc = mmc_get_mmc_dev(dev); +#else +static int sdhci_execute_tuning(struct mmc *mmc, u32 opcode) +{ +#endif + struct sdhci_host *host = mmc->priv; + u16 ctrl; + + /* + * The Host Controller needs tuning in case of SDR104 and DDR50 + * mode, and for SDR50 mode when Use Tuning for SDR50 is set in + * the Capabilities register. + * If the Host Controller supports the HS200 mode then the + * tuning function has to be executed. + */ + switch (mmc->timing) { + /* HS400 tuning is done in HS200 mode */ + case MMC_TIMING_MMC_HS400: + return -EINVAL; + case MMC_TIMING_MMC_HS200: + /* + * Periodic re-tuning for HS400 is not expected to be needed, so + * disable it here. + */ + break; + default: + return -EINVAL; + } + + ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2); + ctrl |= SDHCI_CTRL_EXEC_TUNING; + sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2); + + return __sdhci_execute_tuning(host, opcode); +} + #ifdef CONFIG_DM_MMC_OPS int sdhci_probe(struct udevice *dev) { @@ -571,6 +683,7 @@ const struct dm_mmc_ops sdhci_ops = { .card_busy = sdhci_card_busy, .send_cmd = sdhci_send_command, .set_ios = sdhci_set_ios, + .execute_tuning = sdhci_execute_tuning, }; #else static const struct mmc_ops sdhci_ops = { @@ -578,6 +691,7 @@ static const struct mmc_ops sdhci_ops = { .send_cmd = sdhci_send_command, .set_ios = sdhci_set_ios, .init = sdhci_init, + .execute_tuning = sdhci_execute_tuning, }; #endif

This patch adds HS400 mode support for eMMC5.0 device. HS400 mode is high speed DDR interface timing from HS200. Clock frequency is up to 200MHz and only 8-bit bus width is supported. In addition, tuning process of HS200 is required to synchronize the command response on the CMD line because CMD input timing for HS400 mode is the same as HS200 mode.
Signed-off-by: Ziyuan Xu xzy.xu@rock-chips.com ---
drivers/mmc/mmc.c | 48 ++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 46 insertions(+), 2 deletions(-)
diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index c1f54c3..71c9cfa 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -855,6 +855,45 @@ static int mmc_select_hs200(struct mmc *mmc) } #endif
+static int mmc_select_hs400(struct mmc *mmc) +{ + int ret; + + /* Switch card to HS mode */ + ret = __mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, + EXT_CSD_HS_TIMING, EXT_CSD_TIMING_HS, false); + if (ret) + return ret; + + /* Set host controller to HS timing */ + mmc_set_timing(mmc, MMC_TIMING_MMC_HS); + + /* Reduce frequency to HS frequency */ + mmc_set_clock(mmc, MMC_HIGH_52_MAX_DTR); + + ret = mmc_send_status(mmc, 1000); + if (ret) + return ret; + + /* Switch card to DDR */ + ret = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, + EXT_CSD_BUS_WIDTH, + EXT_CSD_DDR_BUS_WIDTH_8); + if (ret) + return ret; + + /* Switch card to HS400 */ + ret = __mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, + EXT_CSD_HS_TIMING, EXT_CSD_TIMING_HS400, false); + if (ret) + return ret; + + /* Set host controller to HS400 timing and frequency */ + mmc_set_timing(mmc, MMC_TIMING_MMC_HS400); + + return ret; +} + static u32 mmc_select_card_type(struct mmc *mmc, u8 *ext_csd) { u8 card_type; @@ -960,9 +999,14 @@ static int mmc_change_freq(struct mmc *mmc)
mmc_set_bus_speed(mmc, avail_type);
- if (mmc_card_hs200(mmc)) + if (mmc_card_hs200(mmc)) { err = mmc_hs200_tuning(mmc); - else if (!mmc_card_hs400es(mmc)) { + if (avail_type & EXT_CSD_CARD_TYPE_HS400 && + mmc->bus_width == MMC_BUS_WIDTH_8BIT) { + err = mmc_select_hs400(mmc); + mmc_set_bus_speed(mmc, avail_type); + } + } else if (!mmc_card_hs400es(mmc)) { err = mmc_select_bus_width(mmc) > 0 ? 0 : err; if (!err && avail_type & EXT_CSD_CARD_TYPE_DDR_52) err = mmc_select_hs_ddr(mmc);

The rockchip mmc controllers don't support _the _odd__ divider, otherwise probably cause unpredictable error.
The driver originally select gpll(594M) as the clock source, and we set div to 3 at 200MHz. We have to change the maximum eMMC clock frequency to 150MHz in U-Boot stage, so that the div will be 4.
Signed-off-by: Ziyuan Xu xzy.xu@rock-chips.com ---
arch/arm/dts/rk3399.dtsi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/arch/arm/dts/rk3399.dtsi b/arch/arm/dts/rk3399.dtsi index f3d3f53..b2122b6 100644 --- a/arch/arm/dts/rk3399.dtsi +++ b/arch/arm/dts/rk3399.dtsi @@ -283,7 +283,7 @@ arasan,soc-ctl-syscon = <&grf>; assigned-clocks = <&cru SCLK_EMMC>; assigned-clock-rates = <200000000>; - max-frequency = <200000000>; + max-frequency = <150000000>; clocks = <&cru SCLK_EMMC>, <&cru ACLK_EMMC>; clock-names = "clk_xin", "clk_ahb"; clock-output-names = "emmc_cardclock";

tiny-printf does not know about the "X" modifier so far, which print all zero. The mmc driver use '0x%08X' to print command argument and response.
Signed-off-by: Ziyuan Xu xzy.xu@rock-chips.com ---
lib/tiny-printf.c | 1 + 1 file changed, 1 insertion(+)
diff --git a/lib/tiny-printf.c b/lib/tiny-printf.c index 0b04813..f1183d5 100644 --- a/lib/tiny-printf.c +++ b/lib/tiny-printf.c @@ -273,6 +273,7 @@ static int _vprintf(struct printf_info *info, const char *fmt, va_list va) } break; case 'x': + case 'X': if (islong) { num = va_arg(va, unsigned long); div = 1UL << (sizeof(long) * 8 - 4);

Hi Ziyuan,
On 15 May 2017 at 00:06, Ziyuan Xu xzy.xu@rock-chips.com wrote:
The original implementation select HS timing by default, add available type selection for higher speed mode compatibility, such as hs200, hs400, hs400es.
By the way, we assume that card run at 1.8V or 1.2V I/O when its timing is ddr52/hs200/hs400(es).
Signed-off-by: Ziyuan Xu xzy.xu@rock-chips.com
drivers/mmc/mmc.c | 59 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- include/mmc.h | 16 +++++++++++++++ 2 files changed, 74 insertions(+), 1 deletion(-)
Is there a cover letter for this series, please?
I just reviewed an MMC series at add higher speed support. I'm not sure but I suspect these overlap.
Regards, Simon

hi Simon & Jaehoon,
On 05/16/2017 08:18 AM, Simon Glass wrote:
Hi Ziyuan,
On 15 May 2017 at 00:06, Ziyuan Xu xzy.xu@rock-chips.com wrote:
The original implementation select HS timing by default, add available type selection for higher speed mode compatibility, such as hs200, hs400, hs400es.
By the way, we assume that card run at 1.8V or 1.2V I/O when its timing is ddr52/hs200/hs400(es).
Signed-off-by: Ziyuan Xu xzy.xu@rock-chips.com
drivers/mmc/mmc.c | 59 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- include/mmc.h | 16 +++++++++++++++ 2 files changed, 74 insertions(+), 1 deletion(-)
Is there a cover letter for this series, please?
This patchset is used for hs200/hs400/ddr52 mode of eMMC device, and fixes some bug for dw_mmc & sdhci controller. It's only valid in U-Boot stage, we still use 'High Speed' in SPL.
I tested it on evb-rk3288 board(eMMC 4.5) and evb-rk3388 board(eMMC 5.0).
I just reviewed an MMC series at add higher speed support. I'm not sure but I suspect these overlap.
Ha, I just reviewed Vignesh's patches, it focuses on uhs mode of sd card. It looks to me. But some details are not the same as mine. Anyway, what do you think?
Regards, Simon

Hi Ziyuan,
On 05/16/2017 10:15 AM, Ziyuan wrote:
hi Simon & Jaehoon,
On 05/16/2017 08:18 AM, Simon Glass wrote:
Hi Ziyuan,
On 15 May 2017 at 00:06, Ziyuan Xu xzy.xu@rock-chips.com wrote:
The original implementation select HS timing by default, add available type selection for higher speed mode compatibility, such as hs200, hs400, hs400es.
By the way, we assume that card run at 1.8V or 1.2V I/O when its timing is ddr52/hs200/hs400(es).
Signed-off-by: Ziyuan Xu xzy.xu@rock-chips.com
drivers/mmc/mmc.c | 59 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- include/mmc.h | 16 +++++++++++++++ 2 files changed, 74 insertions(+), 1 deletion(-)
Is there a cover letter for this series, please?
This patchset is used for hs200/hs400/ddr52 mode of eMMC device, and fixes some bug for dw_mmc & sdhci controller. It's only valid in U-Boot stage, we still use 'High Speed' in SPL.
I tested it on evb-rk3288 board(eMMC 4.5) and evb-rk3388 board(eMMC 5.0).
I just reviewed an MMC series at add higher speed support. I'm not sure but I suspect these overlap.
Ha, I just reviewed Vignesh's patches, it focuses on uhs mode of sd card. It looks to me. But some details are not the same as mine. Anyway, what do you think?
I didn't review both yet..After checking, i will share my opinion. how about?
Regards, Simon

hi Jaehoon,
On 05/16/2017 09:55 AM, Jaehoon Chung wrote:
Hi Ziyuan,
On 05/16/2017 10:15 AM, Ziyuan wrote:
hi Simon & Jaehoon,
On 05/16/2017 08:18 AM, Simon Glass wrote:
Hi Ziyuan,
On 15 May 2017 at 00:06, Ziyuan Xu xzy.xu@rock-chips.com wrote:
The original implementation select HS timing by default, add available type selection for higher speed mode compatibility, such as hs200, hs400, hs400es.
By the way, we assume that card run at 1.8V or 1.2V I/O when its timing is ddr52/hs200/hs400(es).
Signed-off-by: Ziyuan Xu xzy.xu@rock-chips.com
drivers/mmc/mmc.c | 59 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- include/mmc.h | 16 +++++++++++++++ 2 files changed, 74 insertions(+), 1 deletion(-)
Is there a cover letter for this series, please?
This patchset is used for hs200/hs400/ddr52 mode of eMMC device, and fixes some bug for dw_mmc & sdhci controller. It's only valid in U-Boot stage, we still use 'High Speed' in SPL.
I tested it on evb-rk3288 board(eMMC 4.5) and evb-rk3388 board(eMMC 5.0).
I just reviewed an MMC series at add higher speed support. I'm not sure but I suspect these overlap.
Ha, I just reviewed Vignesh's patches, it focuses on uhs mode of sd card. It looks to me. But some details are not the same as mine. Anyway, what do you think?
I didn't review both yet..After checking, i will share my opinion. how about?
Fine, I look forward to your opinion.
Regards, Simon

hi Jaehoon,
On 05/16/2017 09:55 AM, Jaehoon Chung wrote:
Hi Ziyuan,
On 05/16/2017 10:15 AM, Ziyuan wrote:
hi Simon & Jaehoon,
On 05/16/2017 08:18 AM, Simon Glass wrote:
Hi Ziyuan,
On 15 May 2017 at 00:06, Ziyuan Xu xzy.xu@rock-chips.com wrote:
The original implementation select HS timing by default, add available type selection for higher speed mode compatibility, such as hs200, hs400, hs400es.
By the way, we assume that card run at 1.8V or 1.2V I/O when its timing is ddr52/hs200/hs400(es).
Signed-off-by: Ziyuan Xu xzy.xu@rock-chips.com
drivers/mmc/mmc.c | 59 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- include/mmc.h | 16 +++++++++++++++ 2 files changed, 74 insertions(+), 1 deletion(-)
Is there a cover letter for this series, please?
This patchset is used for hs200/hs400/ddr52 mode of eMMC device, and fixes some bug for dw_mmc & sdhci controller. It's only valid in U-Boot stage, we still use 'High Speed' in SPL.
I tested it on evb-rk3288 board(eMMC 4.5) and evb-rk3388 board(eMMC 5.0).
I just reviewed an MMC series at add higher speed support. I'm not sure but I suspect these overlap.
Ha, I just reviewed Vignesh's patches, it focuses on uhs mode of sd card. It looks to me. But some details are not the same as mine. Anyway, what do you think?
I didn't review both yet..After checking, i will share my opinion. how about?
*ping*... Did you test this patchset on Samsung platform? As far as I know, Samsung SoCs also make use of dw_mmc and sdhci controllers.
Regards, Simon

On 05/25/2017 05:12 PM, Ziyuan wrote:
hi Jaehoon,
On 05/16/2017 09:55 AM, Jaehoon Chung wrote:
Hi Ziyuan,
On 05/16/2017 10:15 AM, Ziyuan wrote:
hi Simon & Jaehoon,
On 05/16/2017 08:18 AM, Simon Glass wrote:
Hi Ziyuan,
On 15 May 2017 at 00:06, Ziyuan Xu xzy.xu@rock-chips.com wrote:
The original implementation select HS timing by default, add available type selection for higher speed mode compatibility, such as hs200, hs400, hs400es.
By the way, we assume that card run at 1.8V or 1.2V I/O when its timing is ddr52/hs200/hs400(es).
Signed-off-by: Ziyuan Xu xzy.xu@rock-chips.com
drivers/mmc/mmc.c | 59 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- include/mmc.h | 16 +++++++++++++++ 2 files changed, 74 insertions(+), 1 deletion(-)
Is there a cover letter for this series, please?
This patchset is used for hs200/hs400/ddr52 mode of eMMC device, and fixes some bug for dw_mmc & sdhci controller. It's only valid in U-Boot stage, we still use 'High Speed' in SPL.
I tested it on evb-rk3288 board(eMMC 4.5) and evb-rk3388 board(eMMC 5.0).
I just reviewed an MMC series at add higher speed support. I'm not sure but I suspect these overlap.
Ha, I just reviewed Vignesh's patches, it focuses on uhs mode of sd card. It looks to me. But some details are not the same as mine. Anyway, what do you think?
I didn't review both yet..After checking, i will share my opinion. how about?
*ping*... Did you test this patchset on Samsung platform? As far as I know, Samsung SoCs also make use of dw_mmc and sdhci controllers.
Sorry for late..Sure I will test this..Thanks for kindly ping.
Best Regards, Jaehoon Chung
Regards, Simon

Hi Ziyuan,
On 05/25/2017 05:12 PM, Ziyuan wrote:
hi Jaehoon,
On 05/16/2017 09:55 AM, Jaehoon Chung wrote:
Hi Ziyuan,
On 05/16/2017 10:15 AM, Ziyuan wrote:
hi Simon & Jaehoon,
On 05/16/2017 08:18 AM, Simon Glass wrote:
Hi Ziyuan,
On 15 May 2017 at 00:06, Ziyuan Xu xzy.xu@rock-chips.com wrote:
The original implementation select HS timing by default, add available type selection for higher speed mode compatibility, such as hs200, hs400, hs400es.
By the way, we assume that card run at 1.8V or 1.2V I/O when its timing is ddr52/hs200/hs400(es).
Signed-off-by: Ziyuan Xu xzy.xu@rock-chips.com
drivers/mmc/mmc.c | 59 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- include/mmc.h | 16 +++++++++++++++ 2 files changed, 74 insertions(+), 1 deletion(-)
Is there a cover letter for this series, please?
This patchset is used for hs200/hs400/ddr52 mode of eMMC device, and fixes some bug for dw_mmc & sdhci controller. It's only valid in U-Boot stage, we still use 'High Speed' in SPL.
I tested it on evb-rk3288 board(eMMC 4.5) and evb-rk3388 board(eMMC 5.0).
I just reviewed an MMC series at add higher speed support. I'm not sure but I suspect these overlap.
Ha, I just reviewed Vignesh's patches, it focuses on uhs mode of sd card. It looks to me. But some details are not the same as mine. Anyway, what do you think?
I didn't review both yet..After checking, i will share my opinion. how about?
*ping*... Did you test this patchset on Samsung platform? As far as I know, Samsung SoCs also make use of dw_mmc and sdhci controllers.
some details are not the same as yours..but some points are duplicated. DWMMC and sdhci controller side are not duplicated..
As i am feeling..your patches are based on Linux kernel code..right?
Regards, Simon

hi Jaehoon,
On 05/25/2017 09:08 PM, Jaehoon Chung wrote:
Hi Ziyuan,
On 05/25/2017 05:12 PM, Ziyuan wrote:
hi Jaehoon,
On 05/16/2017 09:55 AM, Jaehoon Chung wrote:
Hi Ziyuan,
On 05/16/2017 10:15 AM, Ziyuan wrote:
hi Simon & Jaehoon,
On 05/16/2017 08:18 AM, Simon Glass wrote:
Hi Ziyuan,
On 15 May 2017 at 00:06, Ziyuan Xu xzy.xu@rock-chips.com wrote:
The original implementation select HS timing by default, add available type selection for higher speed mode compatibility, such as hs200, hs400, hs400es.
By the way, we assume that card run at 1.8V or 1.2V I/O when its timing is ddr52/hs200/hs400(es).
Signed-off-by: Ziyuan Xu xzy.xu@rock-chips.com
drivers/mmc/mmc.c | 59 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- include/mmc.h | 16 +++++++++++++++ 2 files changed, 74 insertions(+), 1 deletion(-)
Is there a cover letter for this series, please?
This patchset is used for hs200/hs400/ddr52 mode of eMMC device, and fixes some bug for dw_mmc & sdhci controller. It's only valid in U-Boot stage, we still use 'High Speed' in SPL.
I tested it on evb-rk3288 board(eMMC 4.5) and evb-rk3388 board(eMMC 5.0).
I just reviewed an MMC series at add higher speed support. I'm not sure but I suspect these overlap.
Ha, I just reviewed Vignesh's patches, it focuses on uhs mode of sd card. It looks to me. But some details are not the same as mine. Anyway, what do you think?
I didn't review both yet..After checking, i will share my opinion. how about?
*ping*... Did you test this patchset on Samsung platform? As far as I know, Samsung SoCs also make use of dw_mmc and sdhci controllers.
some details are not the same as yours..but some points are duplicated. DWMMC and sdhci controller side are not duplicated..
As i am feeling..your patches are based on Linux kernel code..right?
Yup, the kernel's mmc driver is stabilized and it was tested on so many various boards. Simon had review Jean-jacques's patch, but a little glitch need to be debugged. So I should wait for him, and rebase?
Regards, Simon

Hi Ziyuan,
On 06/05/2017 09:50 AM, Ziyuan wrote:
hi Jaehoon,
On 05/25/2017 09:08 PM, Jaehoon Chung wrote:
Hi Ziyuan,
On 05/25/2017 05:12 PM, Ziyuan wrote:
hi Jaehoon,
On 05/16/2017 09:55 AM, Jaehoon Chung wrote:
Hi Ziyuan,
On 05/16/2017 10:15 AM, Ziyuan wrote:
hi Simon & Jaehoon,
On 05/16/2017 08:18 AM, Simon Glass wrote:
Hi Ziyuan,
On 15 May 2017 at 00:06, Ziyuan Xu xzy.xu@rock-chips.com wrote: > The original implementation select HS timing by default, add available > type selection for higher speed mode compatibility, such as hs200, > hs400, hs400es. > > By the way, we assume that card run at 1.8V or 1.2V I/O when its timing > is ddr52/hs200/hs400(es). > > Signed-off-by: Ziyuan Xu xzy.xu@rock-chips.com > --- > > drivers/mmc/mmc.c | 59 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- > include/mmc.h | 16 +++++++++++++++ > 2 files changed, 74 insertions(+), 1 deletion(-) > Is there a cover letter for this series, please?
This patchset is used for hs200/hs400/ddr52 mode of eMMC device, and fixes some bug for dw_mmc & sdhci controller. It's only valid in U-Boot stage, we still use 'High Speed' in SPL.
I tested it on evb-rk3288 board(eMMC 4.5) and evb-rk3388 board(eMMC 5.0).
I just reviewed an MMC series at add higher speed support. I'm not sure but I suspect these overlap.
Ha, I just reviewed Vignesh's patches, it focuses on uhs mode of sd card. It looks to me. But some details are not the same as mine. Anyway, what do you think?
I didn't review both yet..After checking, i will share my opinion. how about?
*ping*... Did you test this patchset on Samsung platform? As far as I know, Samsung SoCs also make use of dw_mmc and sdhci controllers.
some details are not the same as yours..but some points are duplicated. DWMMC and sdhci controller side are not duplicated..
As i am feeling..your patches are based on Linux kernel code..right?
Yup, the kernel's mmc driver is stabilized and it was tested on so many various boards. Simon had review Jean-jacques's patch, but a little glitch need to be debugged. So I should wait for him, and rebase?
I will make a branch for testing about HS200 and UHS-I..then you can make the patches based on it. Well, after making branch, i will share it at mailing. Thanks for your effort!
Best Regards, Jaehoon Chung
Regards, Simon

Hi,
On 15 May 2017 at 19:15, Ziyuan xzy.xu@rock-chips.com wrote:
hi Simon & Jaehoon,
On 05/16/2017 08:18 AM, Simon Glass wrote:
Hi Ziyuan,
On 15 May 2017 at 00:06, Ziyuan Xu xzy.xu@rock-chips.com wrote:
The original implementation select HS timing by default, add available type selection for higher speed mode compatibility, such as hs200, hs400, hs400es.
By the way, we assume that card run at 1.8V or 1.2V I/O when its timing is ddr52/hs200/hs400(es).
Signed-off-by: Ziyuan Xu xzy.xu@rock-chips.com
drivers/mmc/mmc.c | 59 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- include/mmc.h | 16 +++++++++++++++ 2 files changed, 74 insertions(+), 1 deletion(-)
Is there a cover letter for this series, please?
This patchset is used for hs200/hs400/ddr52 mode of eMMC device, and fixes some bug for dw_mmc & sdhci controller. It's only valid in U-Boot stage, we still use 'High Speed' in SPL.
I tested it on evb-rk3288 board(eMMC 4.5) and evb-rk3388 board(eMMC 5.0).
I just reviewed an MMC series at add higher speed support. I'm not sure but I suspect these overlap.
Ha, I just reviewed Vignesh's patches, it focuses on uhs mode of sd card. It looks to me. But some details are not the same as mine. Anyway, what do you think?
I think the other series should go in first, so if there are conflicts can you rebase on top of it?
Regards, Simon
participants (4)
-
Jaehoon Chung
-
Simon Glass
-
Ziyuan
-
Ziyuan Xu