sd: sdmmc: rework frequency and timing selection logic
SDMMC framework frequency and timing selection logic has several longstanding issues, including: - requiring that SD hosts support the maximum frequency possible for a given UHS mode in order to apply that timing - selecting SDHC_TIMING_SDR25 for high speed mode, when SDHC_TIMING_HS would be correct Rework the frequency and timing selection logic within the SD framework to resolve these issues. Fixes #52589 Fixes #67943 Signed-off-by: Daniel DeGrasse <daniel.degrasse@nxp.com>
This commit is contained in:
parent
87030f4cbf
commit
82ce2b412d
2 changed files with 104 additions and 47 deletions
|
|
@ -373,6 +373,16 @@ enum hs_max_data_rate {
|
||||||
HS_MAX_DTR = MHZ(50),
|
HS_MAX_DTR = MHZ(50),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* Maximum data rate possible for SD uhs cards */
|
||||||
|
enum uhs_max_data_rate {
|
||||||
|
UHS_UNSUPPORTED = 0,
|
||||||
|
UHS_SDR12_MAX_DTR = MHZ(25),
|
||||||
|
UHS_SDR25_MAX_DTR = MHZ(50),
|
||||||
|
UHS_SDR50_MAX_DTR = MHZ(100),
|
||||||
|
UHS_SDR104_MAX_DTR = MHZ(208),
|
||||||
|
UHS_DDR50_MAX_DTR = MHZ(50),
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief SD bus speed support bit flags
|
* @brief SD bus speed support bit flags
|
||||||
*
|
*
|
||||||
|
|
@ -494,7 +504,7 @@ enum sd_driver_strength {
|
||||||
*/
|
*/
|
||||||
struct sd_switch_caps {
|
struct sd_switch_caps {
|
||||||
enum hs_max_data_rate hs_max_dtr;
|
enum hs_max_data_rate hs_max_dtr;
|
||||||
enum sdhc_clock_speed uhs_max_dtr;
|
enum uhs_max_data_rate uhs_max_dtr;
|
||||||
enum sd_bus_speed bus_speed;
|
enum sd_bus_speed bus_speed;
|
||||||
enum sd_driver_type sd_drv_type;
|
enum sd_driver_type sd_drv_type;
|
||||||
enum sd_current_limit sd_current_limit;
|
enum sd_current_limit sd_current_limit;
|
||||||
|
|
|
||||||
|
|
@ -332,8 +332,25 @@ static int sdmmc_read_switch(struct sd_card *card)
|
||||||
* Bit n being set in support bit field indicates support for function
|
* Bit n being set in support bit field indicates support for function
|
||||||
* number n on the card. (So 0x3 indicates support for functions 0 and 1)
|
* number n on the card. (So 0x3 indicates support for functions 0 and 1)
|
||||||
*/
|
*/
|
||||||
|
/* Determine HS speed support, if any */
|
||||||
if (status[13] & HIGH_SPEED_BUS_SPEED) {
|
if (status[13] & HIGH_SPEED_BUS_SPEED) {
|
||||||
card->switch_caps.hs_max_dtr = HS_MAX_DTR;
|
card->switch_caps.hs_max_dtr = HS_MAX_DTR;
|
||||||
|
} else {
|
||||||
|
card->switch_caps.hs_max_dtr = HS_UNSUPPORTED;
|
||||||
|
}
|
||||||
|
/* Determine UHS speed support, if any */
|
||||||
|
if (status[13] & UHS_SDR104_BUS_SPEED) {
|
||||||
|
card->switch_caps.uhs_max_dtr = UHS_SDR104_MAX_DTR;
|
||||||
|
} else if (status[13] & UHS_DDR50_BUS_SPEED) {
|
||||||
|
card->switch_caps.uhs_max_dtr = UHS_DDR50_MAX_DTR;
|
||||||
|
} else if (status[13] & UHS_SDR50_BUS_SPEED) {
|
||||||
|
card->switch_caps.uhs_max_dtr = UHS_SDR50_MAX_DTR;
|
||||||
|
} else if (status[13] & UHS_SDR25_BUS_SPEED) {
|
||||||
|
card->switch_caps.uhs_max_dtr = UHS_SDR25_MAX_DTR;
|
||||||
|
} else if (status[13] & UHS_SDR12_BUS_SPEED) {
|
||||||
|
card->switch_caps.uhs_max_dtr = UHS_SDR12_MAX_DTR;
|
||||||
|
} else {
|
||||||
|
card->switch_caps.uhs_max_dtr = UHS_UNSUPPORTED;
|
||||||
}
|
}
|
||||||
if (card->sd_version >= SD_SPEC_VER3_0) {
|
if (card->sd_version >= SD_SPEC_VER3_0) {
|
||||||
card->switch_caps.bus_speed = status[13];
|
card->switch_caps.bus_speed = status[13];
|
||||||
|
|
@ -349,22 +366,31 @@ static inline void sdmmc_select_bus_speed(struct sd_card *card)
|
||||||
* Note that function support is defined using bitfields, but function
|
* Note that function support is defined using bitfields, but function
|
||||||
* selection is defined using values 0x0-0xF.
|
* selection is defined using values 0x0-0xF.
|
||||||
*/
|
*/
|
||||||
if (card->host_props.host_caps.sdr104_support &&
|
if ((card->flags & SD_1800MV_FLAG) && sdmmc_host_uhs(&card->host_props) &&
|
||||||
(card->switch_caps.bus_speed & UHS_SDR104_BUS_SPEED) &&
|
!(card->host_props.is_spi) && IS_ENABLED(CONFIG_SD_UHS_PROTOCOL)) {
|
||||||
(card->host_props.f_max >= SD_CLOCK_208MHZ)) {
|
/* Select UHS mode timing */
|
||||||
card->card_speed = SD_TIMING_SDR104;
|
if (card->host_props.host_caps.sdr104_support &&
|
||||||
} else if (card->host_props.host_caps.ddr50_support &&
|
(card->switch_caps.bus_speed & UHS_SDR104_BUS_SPEED)) {
|
||||||
(card->switch_caps.bus_speed & UHS_DDR50_BUS_SPEED) &&
|
card->card_speed = SD_TIMING_SDR104;
|
||||||
(card->host_props.f_max >= SD_CLOCK_50MHZ)) {
|
} else if (card->host_props.host_caps.ddr50_support &&
|
||||||
card->card_speed = SD_TIMING_DDR50;
|
(card->switch_caps.bus_speed & UHS_DDR50_BUS_SPEED)) {
|
||||||
} else if (card->host_props.host_caps.sdr50_support &&
|
card->card_speed = SD_TIMING_DDR50;
|
||||||
(card->switch_caps.bus_speed & UHS_SDR50_BUS_SPEED) &&
|
} else if (card->host_props.host_caps.sdr50_support &&
|
||||||
(card->host_props.f_max >= SD_CLOCK_100MHZ)) {
|
(card->switch_caps.bus_speed & UHS_SDR50_BUS_SPEED)) {
|
||||||
card->card_speed = SD_TIMING_SDR50;
|
card->card_speed = SD_TIMING_SDR50;
|
||||||
} else if (card->host_props.host_caps.high_spd_support &&
|
} else if (card->switch_caps.bus_speed & UHS_SDR12_BUS_SPEED) {
|
||||||
(card->switch_caps.bus_speed & UHS_SDR12_BUS_SPEED) &&
|
card->card_speed = SD_TIMING_SDR25;
|
||||||
(card->host_props.f_max >= SD_CLOCK_25MHZ)) {
|
} else {
|
||||||
card->card_speed = SD_TIMING_SDR12;
|
card->card_speed = SD_TIMING_SDR12;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/* Select HS mode timing */
|
||||||
|
if (card->host_props.host_caps.high_spd_support &&
|
||||||
|
(card->switch_caps.bus_speed & HIGH_SPEED_BUS_SPEED)) {
|
||||||
|
card->card_speed = SD_TIMING_HIGH_SPEED;
|
||||||
|
} else {
|
||||||
|
card->card_speed = SD_TIMING_DEFAULT;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -432,34 +458,52 @@ static int sdmmc_set_current_limit(struct sd_card *card)
|
||||||
static int sdmmc_set_bus_speed(struct sd_card *card)
|
static int sdmmc_set_bus_speed(struct sd_card *card)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
int timing = 0;
|
|
||||||
uint8_t *status = card->card_buffer;
|
uint8_t *status = card->card_buffer;
|
||||||
|
enum sdhc_timing_mode timing;
|
||||||
|
uint32_t card_clock;
|
||||||
|
|
||||||
switch (card->card_speed) {
|
/* Set card clock and host timing. Since the card's maximum clock
|
||||||
/* Set bus clock speed */
|
* was calculated within sdmmc_read_switch(), we can safely use the
|
||||||
case SD_TIMING_SDR104:
|
* minimum between that clock and the host's highest clock supported.
|
||||||
card->switch_caps.uhs_max_dtr = SD_CLOCK_208MHZ;
|
*/
|
||||||
timing = SDHC_TIMING_SDR104;
|
if ((card->flags & SD_1800MV_FLAG) && sdmmc_host_uhs(&card->host_props) &&
|
||||||
break;
|
!(card->host_props.is_spi) && IS_ENABLED(CONFIG_SD_UHS_PROTOCOL)) {
|
||||||
case SD_TIMING_DDR50:
|
/* UHS mode */
|
||||||
card->switch_caps.uhs_max_dtr = SD_CLOCK_50MHZ;
|
card_clock = MIN(card->host_props.f_max, card->switch_caps.uhs_max_dtr);
|
||||||
timing = SDHC_TIMING_DDR50;
|
switch (card->card_speed) {
|
||||||
break;
|
case SD_TIMING_SDR104:
|
||||||
case SD_TIMING_SDR50:
|
timing = SDHC_TIMING_SDR104;
|
||||||
card->switch_caps.uhs_max_dtr = SD_CLOCK_100MHZ;
|
break;
|
||||||
timing = SDHC_TIMING_SDR50;
|
case SD_TIMING_DDR50:
|
||||||
break;
|
timing = SDHC_TIMING_DDR50;
|
||||||
case SD_TIMING_SDR25:
|
break;
|
||||||
card->switch_caps.uhs_max_dtr = SD_CLOCK_50MHZ;
|
case SD_TIMING_SDR50:
|
||||||
timing = SDHC_TIMING_SDR25;
|
timing = SDHC_TIMING_SDR50;
|
||||||
break;
|
break;
|
||||||
case SD_TIMING_SDR12:
|
case SD_TIMING_SDR25:
|
||||||
card->switch_caps.uhs_max_dtr = SD_CLOCK_25MHZ;
|
timing = SDHC_TIMING_SDR25;
|
||||||
timing = SDHC_TIMING_SDR12;
|
break;
|
||||||
break;
|
case SD_TIMING_SDR12:
|
||||||
default:
|
timing = SDHC_TIMING_SDR12;
|
||||||
/* No need to change bus speed */
|
break;
|
||||||
return 0;
|
default:
|
||||||
|
/* No need to change bus speed */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/* High speed/default mode */
|
||||||
|
card_clock = MIN(card->host_props.f_max, card->switch_caps.hs_max_dtr);
|
||||||
|
switch (card->card_speed) {
|
||||||
|
case SD_TIMING_HIGH_SPEED:
|
||||||
|
timing = SDHC_TIMING_HS;
|
||||||
|
break;
|
||||||
|
case SD_TIMING_DEFAULT:
|
||||||
|
timing = SDHC_TIMING_LEGACY;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
/* No need to change bus speed */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Switch bus speed */
|
/* Switch bus speed */
|
||||||
|
|
@ -473,7 +517,7 @@ static int sdmmc_set_bus_speed(struct sd_card *card)
|
||||||
} else {
|
} else {
|
||||||
/* Change host bus speed */
|
/* Change host bus speed */
|
||||||
card->bus_io.timing = timing;
|
card->bus_io.timing = timing;
|
||||||
card->bus_io.clock = card->switch_caps.uhs_max_dtr;
|
card->bus_io.clock = card_clock;
|
||||||
LOG_DBG("Setting bus clock to: %d", card->bus_io.clock);
|
LOG_DBG("Setting bus clock to: %d", card->bus_io.clock);
|
||||||
ret = sdhc_set_io(card->sdhc, &card->bus_io);
|
ret = sdhc_set_io(card->sdhc, &card->bus_io);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
|
|
@ -533,12 +577,15 @@ static int sdmmc_init_hs(struct sd_card *card)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if ((!card->host_props.host_caps.high_spd_support) || (card->sd_version < SD_SPEC_VER1_1) ||
|
if ((!card->host_props.host_caps.high_spd_support) ||
|
||||||
(card->switch_caps.hs_max_dtr == 0)) {
|
(card->sd_version < SD_SPEC_VER1_1) ||
|
||||||
|
(card->switch_caps.hs_max_dtr == HS_UNSUPPORTED)) {
|
||||||
/* No high speed support. Leave card untouched */
|
/* No high speed support. Leave card untouched */
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
card->card_speed = SD_TIMING_SDR25;
|
/* Select bus speed for card depending on host and card capability*/
|
||||||
|
sdmmc_select_bus_speed(card);
|
||||||
|
/* Apply selected bus speed */
|
||||||
ret = sdmmc_set_bus_speed(card);
|
ret = sdmmc_set_bus_speed(card);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
LOG_ERR("Failed to switch card to HS mode");
|
LOG_ERR("Failed to switch card to HS mode");
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue