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),
|
||||
};
|
||||
|
||||
/* 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
|
||||
*
|
||||
|
|
@ -494,7 +504,7 @@ enum sd_driver_strength {
|
|||
*/
|
||||
struct sd_switch_caps {
|
||||
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_driver_type sd_drv_type;
|
||||
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
|
||||
* 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) {
|
||||
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) {
|
||||
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
|
||||
* selection is defined using values 0x0-0xF.
|
||||
*/
|
||||
if (card->host_props.host_caps.sdr104_support &&
|
||||
(card->switch_caps.bus_speed & UHS_SDR104_BUS_SPEED) &&
|
||||
(card->host_props.f_max >= SD_CLOCK_208MHZ)) {
|
||||
card->card_speed = SD_TIMING_SDR104;
|
||||
} else if (card->host_props.host_caps.ddr50_support &&
|
||||
(card->switch_caps.bus_speed & UHS_DDR50_BUS_SPEED) &&
|
||||
(card->host_props.f_max >= SD_CLOCK_50MHZ)) {
|
||||
card->card_speed = SD_TIMING_DDR50;
|
||||
} else if (card->host_props.host_caps.sdr50_support &&
|
||||
(card->switch_caps.bus_speed & UHS_SDR50_BUS_SPEED) &&
|
||||
(card->host_props.f_max >= SD_CLOCK_100MHZ)) {
|
||||
card->card_speed = SD_TIMING_SDR50;
|
||||
} else if (card->host_props.host_caps.high_spd_support &&
|
||||
(card->switch_caps.bus_speed & UHS_SDR12_BUS_SPEED) &&
|
||||
(card->host_props.f_max >= SD_CLOCK_25MHZ)) {
|
||||
card->card_speed = SD_TIMING_SDR12;
|
||||
if ((card->flags & SD_1800MV_FLAG) && sdmmc_host_uhs(&card->host_props) &&
|
||||
!(card->host_props.is_spi) && IS_ENABLED(CONFIG_SD_UHS_PROTOCOL)) {
|
||||
/* Select UHS mode timing */
|
||||
if (card->host_props.host_caps.sdr104_support &&
|
||||
(card->switch_caps.bus_speed & UHS_SDR104_BUS_SPEED)) {
|
||||
card->card_speed = SD_TIMING_SDR104;
|
||||
} else if (card->host_props.host_caps.ddr50_support &&
|
||||
(card->switch_caps.bus_speed & UHS_DDR50_BUS_SPEED)) {
|
||||
card->card_speed = SD_TIMING_DDR50;
|
||||
} else if (card->host_props.host_caps.sdr50_support &&
|
||||
(card->switch_caps.bus_speed & UHS_SDR50_BUS_SPEED)) {
|
||||
card->card_speed = SD_TIMING_SDR50;
|
||||
} else if (card->switch_caps.bus_speed & UHS_SDR12_BUS_SPEED) {
|
||||
card->card_speed = SD_TIMING_SDR25;
|
||||
} else {
|
||||
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)
|
||||
{
|
||||
int ret;
|
||||
int timing = 0;
|
||||
uint8_t *status = card->card_buffer;
|
||||
enum sdhc_timing_mode timing;
|
||||
uint32_t card_clock;
|
||||
|
||||
switch (card->card_speed) {
|
||||
/* Set bus clock speed */
|
||||
case SD_TIMING_SDR104:
|
||||
card->switch_caps.uhs_max_dtr = SD_CLOCK_208MHZ;
|
||||
timing = SDHC_TIMING_SDR104;
|
||||
break;
|
||||
case SD_TIMING_DDR50:
|
||||
card->switch_caps.uhs_max_dtr = SD_CLOCK_50MHZ;
|
||||
timing = SDHC_TIMING_DDR50;
|
||||
break;
|
||||
case SD_TIMING_SDR50:
|
||||
card->switch_caps.uhs_max_dtr = SD_CLOCK_100MHZ;
|
||||
timing = SDHC_TIMING_SDR50;
|
||||
break;
|
||||
case SD_TIMING_SDR25:
|
||||
card->switch_caps.uhs_max_dtr = SD_CLOCK_50MHZ;
|
||||
timing = SDHC_TIMING_SDR25;
|
||||
break;
|
||||
case SD_TIMING_SDR12:
|
||||
card->switch_caps.uhs_max_dtr = SD_CLOCK_25MHZ;
|
||||
timing = SDHC_TIMING_SDR12;
|
||||
break;
|
||||
default:
|
||||
/* No need to change bus speed */
|
||||
return 0;
|
||||
/* Set card clock and host timing. Since the card's maximum clock
|
||||
* was calculated within sdmmc_read_switch(), we can safely use the
|
||||
* minimum between that clock and the host's highest clock supported.
|
||||
*/
|
||||
if ((card->flags & SD_1800MV_FLAG) && sdmmc_host_uhs(&card->host_props) &&
|
||||
!(card->host_props.is_spi) && IS_ENABLED(CONFIG_SD_UHS_PROTOCOL)) {
|
||||
/* UHS mode */
|
||||
card_clock = MIN(card->host_props.f_max, card->switch_caps.uhs_max_dtr);
|
||||
switch (card->card_speed) {
|
||||
case SD_TIMING_SDR104:
|
||||
timing = SDHC_TIMING_SDR104;
|
||||
break;
|
||||
case SD_TIMING_DDR50:
|
||||
timing = SDHC_TIMING_DDR50;
|
||||
break;
|
||||
case SD_TIMING_SDR50:
|
||||
timing = SDHC_TIMING_SDR50;
|
||||
break;
|
||||
case SD_TIMING_SDR25:
|
||||
timing = SDHC_TIMING_SDR25;
|
||||
break;
|
||||
case SD_TIMING_SDR12:
|
||||
timing = SDHC_TIMING_SDR12;
|
||||
break;
|
||||
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 */
|
||||
|
|
@ -473,7 +517,7 @@ static int sdmmc_set_bus_speed(struct sd_card *card)
|
|||
} else {
|
||||
/* Change host bus speed */
|
||||
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);
|
||||
ret = sdhc_set_io(card->sdhc, &card->bus_io);
|
||||
if (ret) {
|
||||
|
|
@ -533,12 +577,15 @@ static int sdmmc_init_hs(struct sd_card *card)
|
|||
{
|
||||
int ret;
|
||||
|
||||
if ((!card->host_props.host_caps.high_spd_support) || (card->sd_version < SD_SPEC_VER1_1) ||
|
||||
(card->switch_caps.hs_max_dtr == 0)) {
|
||||
if ((!card->host_props.host_caps.high_spd_support) ||
|
||||
(card->sd_version < SD_SPEC_VER1_1) ||
|
||||
(card->switch_caps.hs_max_dtr == HS_UNSUPPORTED)) {
|
||||
/* No high speed support. Leave card untouched */
|
||||
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);
|
||||
if (ret) {
|
||||
LOG_ERR("Failed to switch card to HS mode");
|
||||
|
|
|
|||
Loading…
Reference in a new issue