[U-Boot] [PATCH 20/22] mmc: Add support for UHS modes

Jaehoon Chung jh80.chung at samsung.com
Thu May 25 12:50:25 UTC 2017


On 05/13/2017 03:16 AM, Jean-Jacques Hiblot wrote:
> Add UHS modes to the list of supported modes, get the UHS capabilites of
> the SDcard and implement the procedure to switch the voltage (UHS modes
> use 1v8 IO lines)
> During the voltage switch procedure, DAT0 is used by the card to signal
> when it's ready. The optional card_busy() callback can be used to get this
> information from the host driver.
> 
> Signed-off-by: Jean-Jacques Hiblot <jjhiblot at ti.com>
> ---
>  drivers/mmc/mmc.c | 169 +++++++++++++++++++++++++++++++++++++++++++++++++++---
>  include/mmc.h     |  27 ++++++++-
>  2 files changed, 188 insertions(+), 8 deletions(-)
> 
> diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c
> index f6509f1..074d286 100644
> --- a/drivers/mmc/mmc.c
> +++ b/drivers/mmc/mmc.c
> @@ -31,6 +31,7 @@ static const unsigned int sd_au_size[] = {
>  };
>  static int mmc_set_signal_voltage(struct mmc *mmc, uint signal_voltage);
>  static void mmc_power_cycle(struct mmc *mmc);
> +static int mmc_card_busy(struct mmc *mmc);
>  
>  #if CONFIG_IS_ENABLED(MMC_TINY)
>  static struct mmc mmc_static;
> @@ -403,7 +404,68 @@ static int mmc_go_idle(struct mmc *mmc)
>  	return 0;
>  }
>  
> -static int sd_send_op_cond(struct mmc *mmc)
> +static int mmc_switch_voltage(struct mmc *mmc, int signal_voltage)
> +{
> +	struct mmc_cmd cmd;
> +	int err = 0;
> +
> +	/*
> +	 * Send CMD11 only if the request is to switch the card to
> +	 * 1.8V signalling.
> +	 */
> +	if (signal_voltage == MMC_SIGNAL_VOLTAGE_330)
> +		return mmc_set_signal_voltage(mmc, signal_voltage);
> +
> +	cmd.cmdidx = SD_CMD_SWITCH_UHS18V;
> +	cmd.cmdarg = 0;
> +	cmd.resp_type = MMC_RSP_R1;
> +
> +	err = mmc_send_cmd(mmc, &cmd, NULL);
> +	if (err)
> +		goto fail;

goto fail..then it's changed the error number..just return err.

> +
> +	if (!mmc_host_is_spi(host) && (cmd.response[0] & MMC_STATUS_ERROR))
> +		goto fail;
> +
> +	/*
> +	 * The card should drive cmd and dat[0:3] low immediately
> +	 * after the response of cmd11, but wait 1 ms to be sure
> +	 */
> +	udelay(1000);
> +	if (mmc_card_busy(mmc))
> +		goto fail;

If Card is busy..it's not -EIO..it may be -EBUSY.

> +
> +	/*
> +	 * During a signal voltage level switch, the clock must be gated
> +	 * for 5 ms according to the SD spec
> +	 */
> +	mmc_set_clock(mmc, mmc->clock, true);
> +
> +	err = mmc_set_signal_voltage(mmc, signal_voltage);
> +	if (err)
> +		goto fail;

ditto..return err.

> +
> +	/* Keep clock gated for at least 10 ms, though spec only says 5 ms */
> +	udelay(10000);
> +	mmc_set_clock(mmc, mmc->clock, false);
> +
> +	/* Wait for at least 1 ms according to spec */
> +	udelay(1000);
> +
> +	/*
> +	 * Failure to switch is indicated by the card holding
> +	 * dat[0:3] low
> +	 */
> +	if (mmc_card_busy(mmc))
> +		goto fail;

ditto -EBUSY.

> +
> +	return 0;
> +
> +fail:
> +	return -EIO;
> +}
> +
> +static int sd_send_op_cond(struct mmc *mmc, bool uhs_en)
>  {
>  	int timeout = 1000;
>  	int err;
> @@ -435,6 +497,9 @@ static int sd_send_op_cond(struct mmc *mmc)
>  		if (mmc->version == SD_VERSION_2)
>  			cmd.cmdarg |= OCR_HCS;
>  
> +		if (uhs_en)
> +			cmd.cmdarg |= OCR_S18R;
> +
>  		err = mmc_send_cmd(mmc, &cmd, NULL);
>  
>  		if (err)
> @@ -465,6 +530,13 @@ static int sd_send_op_cond(struct mmc *mmc)
>  
>  	mmc->ocr = cmd.response[0];
>  
> +	if (!(mmc_host_is_spi(mmc)) && (cmd.response[0] & 0x41000000)
> +	    == 0x41000000) {
> +		err = mmc_switch_voltage(mmc, MMC_SIGNAL_VOLTAGE_180);
> +		if (err)
> +			return err;
> +	}
> +
>  	mmc->high_capacity = ((mmc->ocr & OCR_HCS) == OCR_HCS);
>  	mmc->rca = 0;
>  
> @@ -977,6 +1049,7 @@ static int sd_get_capabilities(struct mmc *mmc)
>  	ALLOC_CACHE_ALIGN_BUFFER(uint, switch_status, 16);
>  	struct mmc_data data;
>  	int timeout;
> +	u32 sd3_bus_mode;
>  
>  	mmc->card_caps = MMC_MODE_1BIT;
>  
> @@ -1058,6 +1131,22 @@ retry_scr:
>  	if (__be32_to_cpu(switch_status[3]) & SD_HIGHSPEED_SUPPORTED)
>  		mmc->card_caps |= MMC_CAP(SD_HS);
>  
> +	/* Version before 3.0 don't support UHS modes */
> +	if (mmc->version < SD_VERSION_3)
> +		return 0;
> +
> +	sd3_bus_mode = __be32_to_cpu(switch_status[3]) >> 16 & 0x1f;
> +	if (sd3_bus_mode & SD_MODE_UHS_SDR104)
> +		mmc->card_caps |= MMC_CAP(UHS_SDR104);
> +	if (sd3_bus_mode & SD_MODE_UHS_SDR50)
> +		mmc->card_caps |= MMC_CAP(UHS_SDR50);
> +	if (sd3_bus_mode & SD_MODE_UHS_SDR25)
> +		mmc->card_caps |= MMC_CAP(UHS_SDR25);
> +	if (sd3_bus_mode & SD_MODE_UHS_SDR12)
> +		mmc->card_caps |= MMC_CAP(UHS_SDR12);
> +	if (sd3_bus_mode & SD_MODE_UHS_DDR50)
> +		mmc->card_caps |= MMC_CAP(UHS_DDR50);
> +
>  	return 0;
>  }
>  
> @@ -1065,13 +1154,36 @@ static int sd_set_card_speed(struct mmc *mmc, enum bus_mode mode)
>  {
>  	int err;
>  	ALLOC_CACHE_ALIGN_BUFFER(uint, switch_status, 16);
> +	int speed;
> +
> +	switch (mode) {
> +	case SD_LEGACY:
> +	case UHS_SDR12:
> +		speed = UHS_SDR12_BUS_SPEED;
> +		break;
> +	case SD_HS:
> +	case UHS_SDR25:
> +		speed = UHS_SDR25_BUS_SPEED;
> +		break;
> +	case UHS_SDR50:
> +		speed = UHS_SDR50_BUS_SPEED;
> +		break;
> +	case UHS_DDR50:
> +		speed = UHS_DDR50_BUS_SPEED;
> +		break;
> +	case UHS_SDR104:
> +		speed = UHS_SDR104_BUS_SPEED;
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
>  
> -	err = sd_switch(mmc, SD_SWITCH_SWITCH, 0, 1, (u8 *)switch_status);
> +	err = sd_switch(mmc, SD_SWITCH_SWITCH, 0, speed, (u8 *)switch_status);
>  
>  	if (err)
>  		return err;
>  
> -	if ((__be32_to_cpu(switch_status[4]) & 0x0f000000) != 0x01000000)
> +	if ((__be32_to_cpu(switch_status[4]) >> 24) != speed)
>  		return -ENOTSUPP;
>  
>  	return 0;
> @@ -1291,10 +1403,31 @@ static int mmc_set_signal_voltage(struct mmc *mmc, uint signal_voltage)
>  
>  static const struct mode_width_tuning sd_modes_by_pref[] = {
>  	{
> +		.mode = UHS_SDR104,
> +		.widths = MMC_MODE_4BIT | MMC_MODE_1BIT,
> +		.tuning = MMC_SEND_TUNING_BLOCK
> +	},
> +	{
> +		.mode = UHS_SDR50,
> +		.widths = MMC_MODE_4BIT | MMC_MODE_1BIT,
> +	},
> +	{
> +		.mode = UHS_DDR50,
> +		.widths = MMC_MODE_4BIT | MMC_MODE_1BIT,
> +	},
> +	{
> +		.mode = UHS_SDR25,
> +		.widths = MMC_MODE_4BIT | MMC_MODE_1BIT,
> +	},
> +	{
>  		.mode = SD_HS,
>  		.widths = MMC_MODE_4BIT | MMC_MODE_1BIT,
>  	},
>  	{
> +		.mode = UHS_SDR12,
> +		.widths = MMC_MODE_4BIT | MMC_MODE_1BIT,
> +	},
> +	{
>  		.mode = SD_LEGACY,
>  		.widths = MMC_MODE_4BIT | MMC_MODE_1BIT,
>  	}
> @@ -1310,18 +1443,24 @@ static int sd_select_mode_and_width(struct mmc *mmc)
>  	int err;
>  	uint widths[] = {MMC_MODE_4BIT, MMC_MODE_1BIT};
>  	const struct mode_width_tuning *mwt;
> +	bool uhs_en = (mmc->ocr & OCR_S18R) ? true : false;
> +	uint caps;
> +
>  
>  	err = sd_get_capabilities(mmc);
>  	if (err)
>  		return err;
>  	/* Restrict card's capabilities by what the host can do */
> -	mmc->card_caps &= (mmc->cfg->host_caps | MMC_MODE_1BIT);
> +	caps = mmc->card_caps & (mmc->cfg->host_caps | MMC_MODE_1BIT);
>  
> -	for_each_sd_mode_by_pref(mmc->card_caps, mwt) {
> +	if (!uhs_en)
> +		caps &= ~UHS_CAPS;
> +
> +	for_each_sd_mode_by_pref(caps, mwt) {
>  		uint *w;
>  
>  		for (w = widths; w < widths + ARRAY_SIZE(widths); w++) {
> -			if (*w & mmc->card_caps & mwt->widths) {
> +			if (*w & caps & mwt->widths) {
>  				debug("trying mode %s width %d (at %d MHz)\n",
>  				      mmc_mode_name(mwt->mode),
>  				      bus_width(*w),
> @@ -1342,6 +1481,16 @@ static int sd_select_mode_and_width(struct mmc *mmc)
>  				mmc_select_mode(mmc, mwt->mode);
>  				mmc_set_clock(mmc, mmc->tran_speed, false);
>  
> +				/* execute tuning if needed */
> +				if (mwt->tuning && !mmc_host_is_spi(mmc)) {
> +					err = mmc_execute_tuning(mmc,
> +								 mwt->tuning);
> +					if (err) {
> +						debug("tuning failed\n");
> +						goto error;
> +					}
> +				}
> +
>  				err = sd_read_ssr(mmc);
>  				if (!err)
>  					return 0;
> @@ -1993,6 +2142,7 @@ static void mmc_power_cycle(struct mmc *mmc)
>  int mmc_start_init(struct mmc *mmc)
>  {
>  	bool no_card;
> +	bool uhs_en = supports_uhs(mmc->cfg->host_caps);
>  	int err;
>  
>  	/* we pretend there's no card when init is NULL */
> @@ -2028,6 +2178,7 @@ int mmc_start_init(struct mmc *mmc)
>  #endif
>  	mmc->ddr_mode = 0;
>  
> +retry:
>  	mmc_power_cycle(mmc);
>  
>  	/* Reset the Card */
> @@ -2043,7 +2194,11 @@ int mmc_start_init(struct mmc *mmc)
>  	err = mmc_send_if_cond(mmc);
>  
>  	/* Now try to get the SD card's operating condition */
> -	err = sd_send_op_cond(mmc);
> +	err = sd_send_op_cond(mmc, uhs_en);
> +	if (err && uhs_en) {
> +		uhs_en = false;
> +		goto retry;
> +	}
>  
>  	/* If the command timed out, we check for an MMC card */
>  	if (err == -ETIMEDOUT) {
> diff --git a/include/mmc.h b/include/mmc.h
> index b42f686..775c47e 100644
> --- a/include/mmc.h
> +++ b/include/mmc.h
> @@ -87,6 +87,7 @@
>  #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

Ditto..Use the MMC_CMD prefix. :)

>  #define MMC_SEND_TUNING_BLOCK_HS200	21
>  #define MMC_CMD_SET_BLOCK_COUNT         23
>  #define MMC_CMD_WRITE_SINGLE_BLOCK	24
> @@ -117,7 +118,8 @@
>  
>  static inline bool mmc_is_tuning_cmd(uint cmdidx)
>  {
> -	if (cmdidx == MMC_SEND_TUNING_BLOCK_HS200)
> +	if ((cmdidx == MMC_SEND_TUNING_BLOCK_HS200) ||
> +	    (cmdidx == MMC_SEND_TUNING_BLOCK))
>  		return true;
>  	return false;
>  }
> @@ -126,8 +128,22 @@ static inline bool mmc_is_tuning_cmd(uint cmdidx)
>  #define SD_HIGHSPEED_BUSY	0x00020000
>  #define SD_HIGHSPEED_SUPPORTED	0x00020000
>  
> +#define UHS_SDR12_BUS_SPEED	0
> +#define HIGH_SPEED_BUS_SPEED	1
> +#define UHS_SDR25_BUS_SPEED	1
> +#define UHS_SDR50_BUS_SPEED	2
> +#define UHS_SDR104_BUS_SPEED	3
> +#define UHS_DDR50_BUS_SPEED	4
> +
> +#define SD_MODE_UHS_SDR12	(1 << UHS_SDR12_BUS_SPEED)
> +#define SD_MODE_UHS_SDR25	(1 << UHS_SDR25_BUS_SPEED)
> +#define SD_MODE_UHS_SDR50	(1 << UHS_SDR50_BUS_SPEED)
> +#define SD_MODE_UHS_SDR104	(1 << UHS_SDR104_BUS_SPEED)
> +#define SD_MODE_UHS_DDR50	(1 << UHS_DDR50_BUS_SPEED)
> +
>  #define OCR_BUSY		0x80000000
>  #define OCR_HCS			0x40000000
> +#define OCR_S18R		0x1000000
>  #define OCR_VOLTAGE_MASK	0x007FFF80
>  #define OCR_ACCESS_MODE		0x60000000
>  
> @@ -490,6 +506,15 @@ static inline bool mmc_is_mode_ddr(enum bus_mode mode)
>  		return false;
>  }
>  
> +#define UHS_CAPS (MMC_CAP(UHS_SDR12) | MMC_CAP(UHS_SDR25) | \
> +		  MMC_CAP(UHS_SDR50) | MMC_CAP(UHS_SDR104) | \
> +		  MMC_CAP(UHS_DDR50))
> +
> +static inline bool supports_uhs(uint caps)
> +{
> +	return (caps & UHS_CAPS) ? true : false;
> +}
> +
>  
>  /*
>   * With CONFIG_DM_MMC enabled, struct mmc can be accessed from the MMC device
> 



More information about the U-Boot mailing list