mirror of
https://github.com/Atmosphere-NX/Atmosphere.git
synced 2024-12-01 02:37:27 +01:00
fusee: begin adding support for MMC HS modes
This commit is contained in:
parent
5f3fc8156c
commit
d5411b5b1f
@ -135,10 +135,39 @@ enum sdmmc_constants {
|
|||||||
* SDMMC clock divider constants
|
* SDMMC clock divider constants
|
||||||
*/
|
*/
|
||||||
enum sdmmc_clock_dividers {
|
enum sdmmc_clock_dividers {
|
||||||
|
|
||||||
|
/* Clock dividers: SD */
|
||||||
MMC_CLOCK_DIVIDER_SDR12 = 31, // 16.5, from the TRM table
|
MMC_CLOCK_DIVIDER_SDR12 = 31, // 16.5, from the TRM table
|
||||||
MMC_CLOCK_DIVIDER_SDR25 = 15, // 8.5, from the table
|
MMC_CLOCK_DIVIDER_SDR25 = 15, // 8.5, from the table
|
||||||
MMC_CLOCK_DIVIDER_SDR50 = 7, // 4.5, from the table
|
MMC_CLOCK_DIVIDER_SDR50 = 7, // 4.5, from the table
|
||||||
MMC_CLOCK_DIVIDER_SDR104 = 3, // 2, from the datasheet
|
MMC_CLOCK_DIVIDER_SDR104 = 3, // 2, from the datasheet
|
||||||
|
|
||||||
|
/* Clock dividers: MMC */
|
||||||
|
MMC_CLOCK_DIVIDER_HS26 = 30, // 16, from the TRM table
|
||||||
|
MMC_CLOCK_DIVIDER_HS52 = 14, // 8, from the table
|
||||||
|
|
||||||
|
MMC_CLOCK_DIVIDER_HS200 = 2, // 1 -- NOTE THIS IS WITH RESPECT TO PLLC4_OUT2_LJ
|
||||||
|
MMC_CLOCK_DIVIDER_HS400 = 2, // 1 -- NOTE THIS IS WITH RESPECT TO PLLC4_OUT2_LJ
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SDMMC clock divider constants
|
||||||
|
*/
|
||||||
|
enum sdmmc_clock_sources {
|
||||||
|
|
||||||
|
/* Clock dividers: SD */
|
||||||
|
MMC_CLOCK_SOURCE_SDR12 = 0, // PLLP
|
||||||
|
MMC_CLOCK_SOURCE_SDR25 = 0,
|
||||||
|
MMC_CLOCK_SOURCE_SDR50 = 0,
|
||||||
|
MMC_CLOCK_SOURCE_SDR104 = 0,
|
||||||
|
|
||||||
|
/* Clock dividers: MMC */
|
||||||
|
MMC_CLOCK_SOURCE_HS26 = 0, // PLLP
|
||||||
|
MMC_CLOCK_SOURCE_HS52 = 0,
|
||||||
|
MMC_CLOCK_SOURCE_HS200 = 1, // PLLC4_OUT2_LJ
|
||||||
|
MMC_CLOCK_SOURCE_HS400 = 1,
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -244,6 +273,8 @@ enum sdmmc_register_bits {
|
|||||||
MMC_CLOCK_TRIM_SDMMC1 = (0x02 << 24),
|
MMC_CLOCK_TRIM_SDMMC1 = (0x02 << 24),
|
||||||
MMC_CLOCK_TRIM_SDMMC4 = (0x08 << 24),
|
MMC_CLOCK_TRIM_SDMMC4 = (0x08 << 24),
|
||||||
|
|
||||||
|
MMC_CLOCK_PADPIPE_CLKEN_OVERRIDE = (1 << 3),
|
||||||
|
|
||||||
/* Autocal configuration */
|
/* Autocal configuration */
|
||||||
MMC_AUTOCAL_PDPU_CONFIG_MASK = 0x7f7f,
|
MMC_AUTOCAL_PDPU_CONFIG_MASK = 0x7f7f,
|
||||||
MMC_AUTOCAL_PDPU_SDMMC1_1V8 = 0x7b7b,
|
MMC_AUTOCAL_PDPU_SDMMC1_1V8 = 0x7b7b,
|
||||||
@ -274,14 +305,18 @@ enum sdmmc_register_bits {
|
|||||||
|
|
||||||
MMC_VENDOR_TUNING_SET_BY_HW = (1 << 17),
|
MMC_VENDOR_TUNING_SET_BY_HW = (1 << 17),
|
||||||
|
|
||||||
/* Vendor tuning control 0*/
|
/* Vendor tuning control 1*/
|
||||||
MMC_VENDOR_TUNING_STEP_SIZE_SDR50_DEFAULT = (0 << 0),
|
MMC_VENDOR_TUNING_STEP_SIZE_SDR50_DEFAULT = (0 << 0),
|
||||||
MMC_VENDOR_TUNING_STEP_SIZE_SDR104_DEFAULT = (0 << 4),
|
MMC_VENDOR_TUNING_STEP_SIZE_SDR104_DEFAULT = (0 << 4),
|
||||||
|
|
||||||
|
/* Vendor capability overrides */
|
||||||
|
MMC_VENDOR_CAPABILITY_DQS_TRIM_MASK = (0x3f << 8),
|
||||||
|
MMC_VENDOR_CAPABILITY_DQS_TRIM_HS400 = (0x11 << 8),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The number o
|
* Represents the possible tuning modes for the X1 SDMMC controller.
|
||||||
*/
|
*/
|
||||||
enum sdmmc_tuning_attempts {
|
enum sdmmc_tuning_attempts {
|
||||||
MMC_VENDOR_TUNING_TRIES_40 = 0,
|
MMC_VENDOR_TUNING_TRIES_40 = 0,
|
||||||
@ -293,6 +328,8 @@ enum sdmmc_tuning_attempts {
|
|||||||
/* Helpful aliases; values are from the TRM */
|
/* Helpful aliases; values are from the TRM */
|
||||||
MMC_VENDOR_TUNING_TRIES_SDR50 = 4,
|
MMC_VENDOR_TUNING_TRIES_SDR50 = 4,
|
||||||
MMC_VENDOR_TUNING_TRIES_SDR104 = 2,
|
MMC_VENDOR_TUNING_TRIES_SDR104 = 2,
|
||||||
|
MMC_VENDOR_TUNING_TRIES_HS200 = 2,
|
||||||
|
MMC_VENDOR_TUNING_TRIES_HS400 = 2,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -325,7 +362,8 @@ enum sdmmc_command {
|
|||||||
CMD_SET_BLKLEN = 16,
|
CMD_SET_BLKLEN = 16,
|
||||||
CMD_READ_SINGLE_BLOCK = 17,
|
CMD_READ_SINGLE_BLOCK = 17,
|
||||||
CMD_READ_MULTIPLE_BLOCK = 18,
|
CMD_READ_MULTIPLE_BLOCK = 18,
|
||||||
CMD_SEND_TUNING_BLOCK = 19,
|
CMD_SD_SEND_TUNING_BLOCK = 19,
|
||||||
|
CMD_MMC_SEND_TUNING_BLOCK = 21,
|
||||||
CMD_WRITE_SINGLE_BLOCK = 24,
|
CMD_WRITE_SINGLE_BLOCK = 24,
|
||||||
CMD_WRITE_MULTIPLE_BLOCK = 25,
|
CMD_WRITE_MULTIPLE_BLOCK = 25,
|
||||||
|
|
||||||
@ -336,6 +374,18 @@ enum sdmmc_command {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fields that can be modified by CMD_SWITCH_MODE.
|
||||||
|
*/
|
||||||
|
enum sdmmc_switch_field {
|
||||||
|
/* Fields */
|
||||||
|
MMC_PARTITION_CONFIG = 179,
|
||||||
|
MMC_BUS_WIDTH = 183,
|
||||||
|
MMC_HS_TIMING = 185,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* String descriptions of each command.
|
* String descriptions of each command.
|
||||||
*/
|
*/
|
||||||
@ -359,6 +409,13 @@ static const char *sdmmc_command_string[] = {
|
|||||||
"CMD_SET_BLKLEN",
|
"CMD_SET_BLKLEN",
|
||||||
"CMD_READ_SINGLE_BLOCK",
|
"CMD_READ_SINGLE_BLOCK",
|
||||||
"CMD_READ_MULTIPLE_BLOCK",
|
"CMD_READ_MULTIPLE_BLOCK",
|
||||||
|
"CMD_SD_SEND_TUNING_BLOCK",
|
||||||
|
"<invalid>",
|
||||||
|
"CMD_MMC_SEND_TUNING_BLOCK",
|
||||||
|
"<invalid>",
|
||||||
|
"<invalid>",
|
||||||
|
"CMD_WRITE_SINGLE_BLOCK",
|
||||||
|
"CMD_WRITE_WRITE_BLOCK",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -391,11 +448,6 @@ enum sdmmc_command_magic {
|
|||||||
MMC_IF_VOLTAGE_3V3 = (1 << 8),
|
MMC_IF_VOLTAGE_3V3 = (1 << 8),
|
||||||
MMC_IF_CHECK_PATTERN = 0xAA,
|
MMC_IF_CHECK_PATTERN = 0xAA,
|
||||||
|
|
||||||
/* Misc constants */
|
|
||||||
MMC_DEFAULT_BLOCK_ORDER = 9,
|
|
||||||
MMC_VOLTAGE_SWITCH_TIME = 5000, // 5mS
|
|
||||||
MMC_POST_CLOCK_DELAY = 1000, // 1mS
|
|
||||||
|
|
||||||
/* Switch mode constants */
|
/* Switch mode constants */
|
||||||
SDMMC_SWITCH_MODE_MODE_SHIFT = 31,
|
SDMMC_SWITCH_MODE_MODE_SHIFT = 31,
|
||||||
SDMMC_SWITCH_MODE_ALL_FUNCTIONS_UNUSED = 0xFFFFFF,
|
SDMMC_SWITCH_MODE_ALL_FUNCTIONS_UNUSED = 0xFFFFFF,
|
||||||
@ -408,8 +460,11 @@ enum sdmmc_command_magic {
|
|||||||
SDMMC_SWITCH_MODE_NO_GROUP = -1,
|
SDMMC_SWITCH_MODE_NO_GROUP = -1,
|
||||||
|
|
||||||
/* Misc constants */
|
/* Misc constants */
|
||||||
|
MMC_DEFAULT_BLOCK_ORDER = 9,
|
||||||
|
MMC_VOLTAGE_SWITCH_TIME = 5000, // 5mS
|
||||||
|
MMC_POST_CLOCK_DELAY = 1000, // 1mS
|
||||||
MMC_TUNING_TIMEOUT = 150 * 1000, // 150mS
|
MMC_TUNING_TIMEOUT = 150 * 1000, // 150mS
|
||||||
|
MMC_SPEED_MMC_OFFSET = 10,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -463,6 +518,17 @@ enum sdmmc_ext_csd_extents {
|
|||||||
|
|
||||||
MMC_EXT_CSD_PARTITION_SWITCH_TIME = 199,
|
MMC_EXT_CSD_PARTITION_SWITCH_TIME = 199,
|
||||||
MMC_EXT_CSD_PARTITION_SWITCH_SCALE_US = 10000,
|
MMC_EXT_CSD_PARTITION_SWITCH_SCALE_US = 10000,
|
||||||
|
|
||||||
|
/* Card type register; we skip entries for
|
||||||
|
* non-1V8 modes, as we're fixed to 1V8 */
|
||||||
|
MMC_EXT_CSD_CARD_TYPE = 196,
|
||||||
|
MMC_EXT_CSD_CARD_TYPE_HS26 = (1 << 0),
|
||||||
|
MMC_EXT_CSD_CARD_TYPE_HS52 = (1 << 1),
|
||||||
|
MMC_EXT_CSD_CARD_TYPE_HS200_1V8 = (1 << 4),
|
||||||
|
MMC_EXT_CSD_CARD_TYPE_HS400_1V8 = (1 << 6),
|
||||||
|
|
||||||
|
/* Current HS mode register */
|
||||||
|
MMC_EXT_CSD_HS_TIMING = 185,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -616,11 +682,20 @@ static const char *sdmmc_get_speed_string(enum sdmmc_bus_speed speed)
|
|||||||
{
|
{
|
||||||
switch (speed) {
|
switch (speed) {
|
||||||
case SDMMC_SPEED_INIT: return "400kHz (init)";
|
case SDMMC_SPEED_INIT: return "400kHz (init)";
|
||||||
|
|
||||||
|
// SD card speeds
|
||||||
case SDMMC_SPEED_SDR12: return "12.5MB/s";
|
case SDMMC_SPEED_SDR12: return "12.5MB/s";
|
||||||
case SDMMC_SPEED_SDR25: return "25MB/s";
|
case SDMMC_SPEED_SDR25: return "25MB/s";
|
||||||
case SDMMC_SPEED_SDR50: return "50MB/s";
|
case SDMMC_SPEED_SDR50: return "50MB/s";
|
||||||
case SDMMC_SPEED_SDR104: return "104MB/s";
|
case SDMMC_SPEED_SDR104: return "104MB/s";
|
||||||
case SDMMC_SPEED_DDR50: return "104MB/s (DDR)";
|
case SDMMC_SPEED_DDR50: return "104MB/s (DDR)";
|
||||||
|
case SDMMC_SPEED_OTHER: return "<invalid speed>";
|
||||||
|
|
||||||
|
// eMMC card speeds
|
||||||
|
case SDMMC_SPEED_HS26: return "26 MHz";
|
||||||
|
case SDMMC_SPEED_HS52: return "52 MHz";
|
||||||
|
case SDMMC_SPEED_HS200: return "200MHz";
|
||||||
|
case SDMMC_SPEED_HS400: return "200MHz (DDR)";
|
||||||
}
|
}
|
||||||
|
|
||||||
return "";
|
return "";
|
||||||
@ -643,12 +718,6 @@ static const char *sdmmc_get_command_string(enum sdmmc_command command)
|
|||||||
return "CMD_APP_SET_CARD_DETECT";
|
return "CMD_APP_SET_CARD_DETECT";
|
||||||
case CMD_APP_SEND_SCR:
|
case CMD_APP_SEND_SCR:
|
||||||
return "CMD_APP_SEND_SCR";
|
return "CMD_APP_SEND_SCR";
|
||||||
case CMD_WRITE_SINGLE_BLOCK:
|
|
||||||
return "CMD_WRITE_SINGLE_BLOCK";
|
|
||||||
case CMD_WRITE_MULTIPLE_BLOCK:
|
|
||||||
return "CMD_WRITE_MULTIPLE_BLOCK";
|
|
||||||
case CMD_SEND_TUNING_BLOCK:
|
|
||||||
return "CMD_SEND_TUNING_BLOCK";
|
|
||||||
|
|
||||||
// For commands with low numbers, read them string from the relevant array.
|
// For commands with low numbers, read them string from the relevant array.
|
||||||
default:
|
default:
|
||||||
@ -745,7 +814,7 @@ static int sdmmc4_set_up_clock_and_io(struct mmc *mmc)
|
|||||||
car->rst_dev_l_clr |= 0x8000;
|
car->rst_dev_l_clr |= 0x8000;
|
||||||
|
|
||||||
// Enable input paths for all pins.
|
// Enable input paths for all pins.
|
||||||
padctl->sdmmc2_control |=
|
padctl->sdmmc4_control |=
|
||||||
PADCTL_SDMMC4_ENABLE_DATA_IN | PADCTL_SDMMC4_ENABLE_CLK_IN | PADCTL_SDMMC4_DEEP_LOOPBACK;
|
PADCTL_SDMMC4_ENABLE_DATA_IN | PADCTL_SDMMC4_ENABLE_CLK_IN | PADCTL_SDMMC4_DEEP_LOOPBACK;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@ -833,6 +902,9 @@ static int sdmmc_set_up_clocking_parameters(struct mmc *mmc, enum sdmmc_bus_volt
|
|||||||
mmc->regs->vendor_clock_cntrl &= ~(MMC_CLOCK_TRIM_MASK | MMC_CLOCK_TAP_MASK);
|
mmc->regs->vendor_clock_cntrl &= ~(MMC_CLOCK_TRIM_MASK | MMC_CLOCK_TAP_MASK);
|
||||||
mmc->regs->auto_cal_config &= ~MMC_AUTOCAL_PDPU_CONFIG_MASK;
|
mmc->regs->auto_cal_config &= ~MMC_AUTOCAL_PDPU_CONFIG_MASK;
|
||||||
|
|
||||||
|
// Per the TRM, set the PADPIPE clock enable.
|
||||||
|
mmc->regs->vendor_clock_cntrl |= MMC_CLOCK_PADPIPE_CLKEN_OVERRIDE;
|
||||||
|
|
||||||
switch (operating_voltage) {
|
switch (operating_voltage) {
|
||||||
case MMC_VOLTAGE_1V8:
|
case MMC_VOLTAGE_1V8:
|
||||||
mmc->regs->auto_cal_config |= MMC_AUTOCAL_PDPU_SDMMC4_1V8;
|
mmc->regs->auto_cal_config |= MMC_AUTOCAL_PDPU_SDMMC4_1V8;
|
||||||
@ -1040,7 +1112,7 @@ static void sdmmc1_configure_clock(struct mmc *mmc, int source, int car_divisor,
|
|||||||
* Runs a single iteration of an active SDMMC clock tune.
|
* Runs a single iteration of an active SDMMC clock tune.
|
||||||
* You probably want sdmmc_tune_clock instead.
|
* You probably want sdmmc_tune_clock instead.
|
||||||
*/
|
*/
|
||||||
static int sdmmc_run_tuning_iteration(struct mmc *mmc)
|
static int sdmmc_run_tuning_iteration(struct mmc *mmc, enum sdmmc_command tuning_command)
|
||||||
{
|
{
|
||||||
int rc;
|
int rc;
|
||||||
uint32_t saved_int_enable = mmc->regs->int_enable;
|
uint32_t saved_int_enable = mmc->regs->int_enable;
|
||||||
@ -1070,7 +1142,7 @@ static int sdmmc_run_tuning_iteration(struct mmc *mmc)
|
|||||||
|
|
||||||
// Issue our tuning command. [TRM 32.7.6.2 Step 4]
|
// Issue our tuning command. [TRM 32.7.6.2 Step 4]
|
||||||
sdmmc_prepare_command_data(mmc, 1, false, false, false, 0);
|
sdmmc_prepare_command_data(mmc, 1, false, false, false, 0);
|
||||||
sdmmc_prepare_command_registers(mmc, 1, CMD_SEND_TUNING_BLOCK, MMC_RESPONSE_LEN48, MMC_CHECKS_ALL);
|
sdmmc_prepare_command_registers(mmc, 1, tuning_command, MMC_RESPONSE_LEN48, MMC_CHECKS_ALL);
|
||||||
|
|
||||||
// Wait for 1us [TRM 32.7.6.2 Step 5]
|
// Wait for 1us [TRM 32.7.6.2 Step 5]
|
||||||
// As part of the workaround above, we'll wait one microsecond for the glitch window to pass.
|
// As part of the workaround above, we'll wait one microsecond for the glitch window to pass.
|
||||||
@ -1117,8 +1189,10 @@ static int sdmmc_run_tuning_iteration(struct mmc *mmc)
|
|||||||
*
|
*
|
||||||
* @param mmc The controller to tune.
|
* @param mmc The controller to tune.
|
||||||
* @param iterations The total number of iterations to perform.
|
* @param iterations The total number of iterations to perform.
|
||||||
|
* @param tuning_command The command to be used for tuning; usually CMD19/21.
|
||||||
*/
|
*/
|
||||||
static int sdmmc_tune_clock(struct mmc *mmc, enum sdmmc_tuning_attempts iterations)
|
static int sdmmc_tune_clock(struct mmc *mmc, enum sdmmc_tuning_attempts iterations,
|
||||||
|
enum sdmmc_command tuning_command)
|
||||||
{
|
{
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
@ -1174,7 +1248,7 @@ static int sdmmc_tune_clock(struct mmc *mmc, enum sdmmc_tuning_attempts iteratio
|
|||||||
while (attempts_remaining--) {
|
while (attempts_remaining--) {
|
||||||
|
|
||||||
// Run an iteration of our tuning process.
|
// Run an iteration of our tuning process.
|
||||||
rc = sdmmc_run_tuning_iteration(mmc);
|
rc = sdmmc_run_tuning_iteration(mmc, tuning_command);
|
||||||
|
|
||||||
// If we have an error other than "retry, break.
|
// If we have an error other than "retry, break.
|
||||||
if (rc != EAGAIN)
|
if (rc != EAGAIN)
|
||||||
@ -1202,6 +1276,7 @@ static int sdmmc_tune_clock(struct mmc *mmc, enum sdmmc_tuning_attempts iteratio
|
|||||||
// Check for a tuning failure (SAMPLE CLOCK = 0). [TRM 32.7.6.2 Step 11]
|
// Check for a tuning failure (SAMPLE CLOCK = 0). [TRM 32.7.6.2 Step 11]
|
||||||
if (!(mmc->regs->host_control2 & MMC_HOST2_SAMPLING_CLOCK_ENABLED)) {
|
if (!(mmc->regs->host_control2 & MMC_HOST2_SAMPLING_CLOCK_ENABLED)) {
|
||||||
mmc_print(mmc, "ERROR: tuning failed after complete iteration!");
|
mmc_print(mmc, "ERROR: tuning failed after complete iteration!");
|
||||||
|
mmc_print(mmc, "host_control2: %08x", mmc->regs->host_control2);
|
||||||
return EIO;
|
return EIO;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1235,6 +1310,11 @@ static int sdmmc_apply_clock_speed(struct mmc *mmc, enum sdmmc_bus_speed speed,
|
|||||||
{
|
{
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
|
// By default, don't execute tuning after applying the clock.
|
||||||
|
bool execute_tuning = false;
|
||||||
|
enum sdmmc_tuning_attempts tuning_attempts = MMC_VENDOR_TUNING_TRIES_40;
|
||||||
|
enum sdmmc_command tuning_command = CMD_SD_SEND_TUNING_BLOCK;
|
||||||
|
|
||||||
// Ensure the clocks are not currently running to avoid glitches.
|
// Ensure the clocks are not currently running to avoid glitches.
|
||||||
sdmmc_clock_enable(mmc, false);
|
sdmmc_clock_enable(mmc, false);
|
||||||
|
|
||||||
@ -1246,50 +1326,65 @@ static int sdmmc_apply_clock_speed(struct mmc *mmc, enum sdmmc_bus_speed speed,
|
|||||||
|
|
||||||
// 400kHz initialization mode.
|
// 400kHz initialization mode.
|
||||||
case SDMMC_SPEED_INIT:
|
case SDMMC_SPEED_INIT:
|
||||||
mmc->configure_clock(mmc, CLK_SOURCE_FIRST, MMC_CLOCK_DIVIDER_SDR12, MMC_CLOCK_CONTROL_FREQUENCY_INIT);
|
mmc->configure_clock(mmc, MMC_CLOCK_SOURCE_SDR12, MMC_CLOCK_DIVIDER_SDR12, MMC_CLOCK_CONTROL_FREQUENCY_INIT);
|
||||||
sdmmc_set_uhs_mode(mmc, SDMMC_SPEED_SDR12);
|
sdmmc_set_uhs_mode(mmc, SDMMC_SPEED_SDR12);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// 25MHz default speed
|
// 25MHz default speed (SD)
|
||||||
case SDMMC_SPEED_SDR12:
|
case SDMMC_SPEED_SDR12:
|
||||||
mmc->configure_clock(mmc, CLK_SOURCE_FIRST, MMC_CLOCK_DIVIDER_SDR12, MMC_CLOCK_CONTROL_FREQUENCY_PASSTHROUGH);
|
mmc->configure_clock(mmc, MMC_CLOCK_SOURCE_SDR12, MMC_CLOCK_DIVIDER_SDR12, MMC_CLOCK_CONTROL_FREQUENCY_PASSTHROUGH);
|
||||||
sdmmc_set_uhs_mode(mmc, SDMMC_SPEED_SDR12);
|
sdmmc_set_uhs_mode(mmc, SDMMC_SPEED_SDR12);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// 50MHz high speed
|
// 26MHz default speed (MMC)
|
||||||
|
case SDMMC_SPEED_HS26:
|
||||||
|
mmc->configure_clock(mmc, MMC_CLOCK_SOURCE_HS26, MMC_CLOCK_DIVIDER_HS26, MMC_CLOCK_CONTROL_FREQUENCY_PASSTHROUGH);
|
||||||
|
break;
|
||||||
|
|
||||||
|
// 50MHz high speed (SD)
|
||||||
case SDMMC_SPEED_SDR25:
|
case SDMMC_SPEED_SDR25:
|
||||||
// Configure the host to use high-speed timing.
|
// Configure the host to use high-speed timing.
|
||||||
mmc->regs->host_control |= MMC_HOST_ENABLE_HIGH_SPEED;
|
mmc->regs->host_control |= MMC_HOST_ENABLE_HIGH_SPEED;
|
||||||
mmc->configure_clock(mmc, CLK_SOURCE_FIRST, MMC_CLOCK_DIVIDER_SDR25, MMC_CLOCK_CONTROL_FREQUENCY_PASSTHROUGH);
|
mmc->configure_clock(mmc, MMC_CLOCK_SOURCE_SDR25, MMC_CLOCK_DIVIDER_SDR25, MMC_CLOCK_CONTROL_FREQUENCY_PASSTHROUGH);
|
||||||
sdmmc_set_uhs_mode(mmc, SDMMC_SPEED_SDR25);
|
sdmmc_set_uhs_mode(mmc, SDMMC_SPEED_SDR25);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// 100MHz UHS-I
|
// 52MHz high speed (MMC)
|
||||||
case SDMMC_SPEED_SDR50:
|
case SDMMC_SPEED_HS52:
|
||||||
mmc->regs->host_control |= MMC_HOST_ENABLE_HIGH_SPEED;
|
mmc->configure_clock(mmc, MMC_CLOCK_SOURCE_HS52, MMC_CLOCK_DIVIDER_HS52, MMC_CLOCK_CONTROL_FREQUENCY_PASSTHROUGH);
|
||||||
mmc->configure_clock(mmc, CLK_SOURCE_FIRST, MMC_CLOCK_DIVIDER_SDR50, MMC_CLOCK_CONTROL_FREQUENCY_PASSTHROUGH);
|
|
||||||
sdmmc_set_uhs_mode(mmc, SDMMC_SPEED_SDR50);
|
|
||||||
|
|
||||||
// Execute tuning.
|
|
||||||
rc = sdmmc_tune_clock(mmc, MMC_VENDOR_TUNING_TRIES_SDR50);
|
|
||||||
if (rc) {
|
|
||||||
mmc_print(mmc, "ERROR: tuning failed! (%d)", rc);
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// 200MHz UHS-I
|
// 100MHz UHS-I (SD)
|
||||||
|
case SDMMC_SPEED_SDR50:
|
||||||
|
mmc->regs->host_control |= MMC_HOST_ENABLE_HIGH_SPEED;
|
||||||
|
mmc->configure_clock(mmc, MMC_CLOCK_SOURCE_SDR50, MMC_CLOCK_DIVIDER_SDR50, MMC_CLOCK_CONTROL_FREQUENCY_PASSTHROUGH);
|
||||||
|
sdmmc_set_uhs_mode(mmc, SDMMC_SPEED_SDR50);
|
||||||
|
|
||||||
|
execute_tuning = true;
|
||||||
|
tuning_attempts = MMC_VENDOR_TUNING_TRIES_SDR50;
|
||||||
|
break;
|
||||||
|
|
||||||
|
// 200MHz UHS-I (SD)
|
||||||
case SDMMC_SPEED_SDR104:
|
case SDMMC_SPEED_SDR104:
|
||||||
mmc->regs->host_control |= MMC_HOST_ENABLE_HIGH_SPEED;
|
mmc->regs->host_control |= MMC_HOST_ENABLE_HIGH_SPEED;
|
||||||
mmc->configure_clock(mmc, CLK_SOURCE_FIRST, MMC_CLOCK_DIVIDER_SDR104, MMC_CLOCK_CONTROL_FREQUENCY_PASSTHROUGH);
|
mmc->configure_clock(mmc, MMC_CLOCK_SOURCE_SDR104, MMC_CLOCK_DIVIDER_SDR104, MMC_CLOCK_CONTROL_FREQUENCY_PASSTHROUGH);
|
||||||
sdmmc_set_uhs_mode(mmc, SDMMC_SPEED_SDR104);
|
sdmmc_set_uhs_mode(mmc, SDMMC_SPEED_SDR104);
|
||||||
|
|
||||||
|
execute_tuning = true;
|
||||||
|
tuning_attempts = MMC_VENDOR_TUNING_TRIES_SDR104;
|
||||||
|
break;
|
||||||
|
|
||||||
|
// 200MHz single-data rate (MMC)
|
||||||
|
case SDMMC_SPEED_HS200:
|
||||||
|
case SDMMC_SPEED_HS400:
|
||||||
|
mmc->regs->host_control |= MMC_HOST_ENABLE_HIGH_SPEED;
|
||||||
|
mmc->configure_clock(mmc, MMC_CLOCK_SOURCE_HS200, MMC_CLOCK_DIVIDER_HS200, MMC_CLOCK_CONTROL_FREQUENCY_PASSTHROUGH);
|
||||||
|
sdmmc_set_uhs_mode(mmc, SDMMC_SPEED_SDR104); // Per datasheet; we set the controller in SDR104 mode.
|
||||||
|
|
||||||
// Execute tuning.
|
// Execute tuning.
|
||||||
rc = sdmmc_tune_clock(mmc, MMC_VENDOR_TUNING_TRIES_SDR104);
|
execute_tuning = true;
|
||||||
if (rc) {
|
tuning_attempts = MMC_VENDOR_TUNING_TRIES_HS200;
|
||||||
mmc_print(mmc, "ERROR: tuning failed! (%d)", rc);
|
tuning_command = CMD_MMC_SEND_TUNING_BLOCK;
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@ -1297,6 +1392,31 @@ static int sdmmc_apply_clock_speed(struct mmc *mmc, enum sdmmc_bus_speed speed,
|
|||||||
return ENOSYS;
|
return ENOSYS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If we need to execute tuning for this clock mode, do so.
|
||||||
|
if (execute_tuning) {
|
||||||
|
rc = sdmmc_tune_clock(mmc, tuning_attempts, tuning_command);
|
||||||
|
if (rc) {
|
||||||
|
mmc_print(mmc, "WARNING: clock tuning failed! speed mode can't be used. (%d)", rc);
|
||||||
|
sdmmc_clock_enable(mmc, true);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Special case: HS400 mode should be applied _after_ HS200 is applied, so we apply that
|
||||||
|
// first above, and then switch up and re-tune.
|
||||||
|
if (speed == SDMMC_SPEED_HS400) {
|
||||||
|
sdmmc_set_uhs_mode(mmc, SDMMC_SPEED_OTHER); // Special value, per datasheet
|
||||||
|
|
||||||
|
// Per the TRM, we should also use this opportunity to set up the DQS path trimmer.
|
||||||
|
// TODO: should this be used only in HS400?
|
||||||
|
mmc->regs->vendor_cap_overrides &= ~MMC_VENDOR_CAPABILITY_DQS_TRIM_MASK;
|
||||||
|
mmc->regs->vendor_cap_overrides |= MMC_VENDOR_CAPABILITY_DQS_TRIM_HS400;
|
||||||
|
|
||||||
|
// TODO: run the DLLCAL here!
|
||||||
|
|
||||||
|
mmc_print(mmc, "TODO: double the data rate here!");
|
||||||
|
}
|
||||||
|
|
||||||
// Re-enable the clock, if necessary.
|
// Re-enable the clock, if necessary.
|
||||||
if (enable_after) {
|
if (enable_after) {
|
||||||
sdmmc_clock_enable(mmc, true);
|
sdmmc_clock_enable(mmc, true);
|
||||||
@ -2358,6 +2478,9 @@ static int sdmmc_read_and_parse_ext_csd(struct mmc *mmc)
|
|||||||
mmc->partitioned = ext_csd[MMC_EXT_CSD_PARTITION_SETTING_COMPLETE] & MMC_EXT_CSD_PARTITION_SETTING_COMPLETED;
|
mmc->partitioned = ext_csd[MMC_EXT_CSD_PARTITION_SETTING_COMPLETE] & MMC_EXT_CSD_PARTITION_SETTING_COMPLETED;
|
||||||
mmc->partition_attribute = ext_csd[MMC_EXT_CSD_PARTITION_ATTRIBUTE];
|
mmc->partition_attribute = ext_csd[MMC_EXT_CSD_PARTITION_ATTRIBUTE];
|
||||||
|
|
||||||
|
// Speed support.
|
||||||
|
mmc->mmc_card_type = ext_csd[MMC_EXT_CSD_CARD_TYPE];
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2442,18 +2565,6 @@ static int sdmmc_optimize_transfer_mode(struct mmc *mmc)
|
|||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
// We started off with the controller opearting with a divided clock,
|
|
||||||
// which is meant to keep us within the pre-init operating frequency of 400kHz.
|
|
||||||
// We're now set up, so we can drop the divider. From this point forward, the
|
|
||||||
// clock is now driven by the CAR frequency.
|
|
||||||
rc = sdmmc_apply_clock_speed(mmc, SDMMC_SPEED_SDR12, true);
|
|
||||||
if (rc) {
|
|
||||||
mmc_print(mmc, "WARNING: could not step up to normal operating speeds!");
|
|
||||||
mmc_print(mmc, " ... expect delays.");
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
mmc_debug(mmc, "now operating at 12.5MB/s!");
|
|
||||||
|
|
||||||
// Automatically optimize speed as much as is possible. How this works depends on
|
// Automatically optimize speed as much as is possible. How this works depends on
|
||||||
// the type of card.
|
// the type of card.
|
||||||
rc = mmc->optimize_speed(mmc);
|
rc = mmc->optimize_speed(mmc);
|
||||||
@ -2467,31 +2578,23 @@ static int sdmmc_optimize_transfer_mode(struct mmc *mmc)
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Switches the active SD card and host controller to the given sppeed mode.
|
* Switches the active card and host controller to the given speed mode.
|
||||||
*
|
*
|
||||||
* @param mmc The MMC controller to affect.
|
* @param mmc The MMC controller to affect.
|
||||||
* @param speed The speed to switch to.
|
* @param speed The speed to switch to.
|
||||||
* @param status_out The status result of the relevant switch operation.
|
* @param status_out The status result of the relevant switch operation.
|
||||||
*/
|
*/
|
||||||
static int sdmmc_sd_switch_bus_speed(struct mmc *mmc, enum sdmmc_bus_speed speed, struct sdmmc_function_status *status_out)
|
static int sdmmc_switch_bus_speed(struct mmc *mmc, enum sdmmc_bus_speed speed)
|
||||||
{
|
{
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
// Read the supported functions from the card.
|
// Ask the card to switch to the new speed.
|
||||||
rc = mmc->switch_mode(mmc, SDMMC_SWITCH_MODE_MODE_APPLY, SDMMC_SWITCH_MODE_ACCESS_MODE, speed, 0, status_out);
|
rc = mmc->card_switch_bus_speed(mmc, speed);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
mmc_print(mmc, "WARNING: could not issue the mode switch");
|
mmc_print(mmc, "WARNING: could not switch card to %s", sdmmc_get_speed_string(speed));
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check that the active operating mode has switched to the new mode.
|
|
||||||
if (status_out->active_access_mode != speed) {
|
|
||||||
mmc_print(mmc, "WARNING: device did not accept mode %s!", sdmmc_get_speed_string(speed));
|
|
||||||
mmc_print(mmc, " reported mode is %s / $d", sdmmc_get_speed_string(status_out->active_access_mode), status_out->active_access_mode);
|
|
||||||
mmc_print(mmc, " adjacent mode is %s / $d", sdmmc_get_speed_string(status_out->group2_selection), status_out->group2_selection);
|
|
||||||
return EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Switch the host controller so it talks the relevant speed.
|
// Switch the host controller so it talks the relevant speed.
|
||||||
rc = sdmmc_apply_clock_speed(mmc, speed, true);
|
rc = sdmmc_apply_clock_speed(mmc, speed, true);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
@ -2504,7 +2607,78 @@ static int sdmmc_sd_switch_bus_speed(struct mmc *mmc, enum sdmmc_bus_speed speed
|
|||||||
|
|
||||||
mmc_debug(mmc, "now operating at %s!", sdmmc_get_speed_string(speed));
|
mmc_debug(mmc, "now operating at %s!", sdmmc_get_speed_string(speed));
|
||||||
return 0;
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Switches the active SD card and host controller to the given speed mode.
|
||||||
|
*
|
||||||
|
* @param mmc The MMC controller to affect.
|
||||||
|
* @param speed The speed to switch to.
|
||||||
|
* @param status_out The status result of the relevant switch operation.
|
||||||
|
*/
|
||||||
|
static int sdmmc_sd_switch_bus_speed(struct mmc *mmc, enum sdmmc_bus_speed speed)
|
||||||
|
{
|
||||||
|
struct sdmmc_function_status status_out;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
// Read the supported functions from the card.
|
||||||
|
rc = mmc->switch_mode(mmc, SDMMC_SWITCH_MODE_MODE_APPLY, SDMMC_SWITCH_MODE_ACCESS_MODE, speed, 0, &status_out);
|
||||||
|
if (rc) {
|
||||||
|
mmc_print(mmc, "WARNING: could not issue the bus speed switch");
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that the active operating mode has switched to the new mode.
|
||||||
|
if (status_out.active_access_mode != speed) {
|
||||||
|
mmc_print(mmc, "WARNING: device did not accept mode %s!", sdmmc_get_speed_string(speed));
|
||||||
|
mmc_print(mmc, " reported mode is %s / %d", sdmmc_get_speed_string(status_out.active_access_mode), status_out.active_access_mode);
|
||||||
|
return EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Switches the active MMC card and host controller to the given speed mode.
|
||||||
|
*
|
||||||
|
* @param mmc The MMC controller to affect.
|
||||||
|
* @param speed The speed to switch to.
|
||||||
|
* @param status_out The status result of the relevant switch operation.
|
||||||
|
*/
|
||||||
|
static int sdmmc_mmc_switch_bus_speed(struct mmc *mmc, enum sdmmc_bus_speed speed)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
uint8_t ext_csd[MMC_EXT_CSD_SIZE];
|
||||||
|
|
||||||
|
// To disambiguate constants, we add ten to every MMC speed constant.
|
||||||
|
// we have to undo this before sending the constants out to the card.
|
||||||
|
uint32_t argument = speed - MMC_SPEED_MMC_OFFSET;
|
||||||
|
|
||||||
|
// Read the supported functions from the card.
|
||||||
|
rc = mmc->switch_mode(mmc, MMC_SWITCH_MODE_WRITE_BYTE, MMC_HS_TIMING, argument, 0, NULL);
|
||||||
|
if (rc) {
|
||||||
|
mmc_print(mmc, "WARNING: could not issue the mode switch");
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the single EXT-CSD block, which contains the current speed.
|
||||||
|
rc = sdmmc_send_command(mmc, CMD_SEND_EXT_CSD, MMC_RESPONSE_LEN48,
|
||||||
|
MMC_CHECKS_ALL, 0, NULL, 1, false, false, ext_csd);
|
||||||
|
if (rc) {
|
||||||
|
mmc_print(mmc, "ERROR: failed to read the extended CSD after mode-switch!");
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check the ext-csd to make sure the change took.
|
||||||
|
if (ext_csd[MMC_EXT_CSD_HS_TIMING] != argument) {
|
||||||
|
mmc_print(mmc, "WARNING: device did not accept mode %s!", sdmmc_get_speed_string(speed));
|
||||||
|
mmc_print(mmc, " reported mode is %s / %d", sdmmc_get_speed_string(ext_csd[MMC_EXT_CSD_HS_TIMING]), ext_csd[MMC_EXT_CSD_HS_TIMING]);
|
||||||
|
return EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -2516,12 +2690,23 @@ static int sdmmc_sd_optimize_speed(struct mmc *mmc)
|
|||||||
int rc;
|
int rc;
|
||||||
struct sdmmc_function_status function_status;
|
struct sdmmc_function_status function_status;
|
||||||
|
|
||||||
|
// We started off with the controller opearting with a divided clock,
|
||||||
|
// which is meant to keep us within the pre-init operating frequency of 400kHz.
|
||||||
|
// We're now set up, so we can drop the divider. From this point forward, the
|
||||||
|
// clock is now driven by the CAR frequency.
|
||||||
|
rc = sdmmc_apply_clock_speed(mmc, SDMMC_SPEED_SDR12, true);
|
||||||
|
if (rc) {
|
||||||
|
mmc_print(mmc, "WARNING: could not step up to normal operating speeds!");
|
||||||
|
mmc_print(mmc, " ... expect delays.");
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
mmc_debug(mmc, "now operating at 12.5MB/s!");
|
||||||
|
|
||||||
// If we're not at a full bus width, we can't switch to a higher speed.
|
// If we're not at a full bus width, we can't switch to a higher speed.
|
||||||
// We're already optimal, vacuously succeed.
|
// We're already optimal, vacuously succeed.
|
||||||
if (mmc->max_bus_width != SD_BUS_WIDTH_4BIT)
|
if (mmc->max_bus_width != SD_BUS_WIDTH_4BIT)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
|
||||||
// Read the supported functions from the card.
|
// Read the supported functions from the card.
|
||||||
rc = mmc->switch_mode(mmc, SDMMC_SWITCH_MODE_MODE_QUERY, SDMMC_SWITCH_MODE_NO_GROUP, 0, 0, &function_status);
|
rc = mmc->switch_mode(mmc, SDMMC_SWITCH_MODE_MODE_QUERY, SDMMC_SWITCH_MODE_NO_GROUP, 0, 0, &function_status);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
@ -2540,15 +2725,15 @@ static int sdmmc_sd_optimize_speed(struct mmc *mmc)
|
|||||||
if (mmc->operating_voltage == MMC_VOLTAGE_1V8) {
|
if (mmc->operating_voltage == MMC_VOLTAGE_1V8) {
|
||||||
|
|
||||||
// Try each of the UHS-I modes, we support.
|
// Try each of the UHS-I modes, we support.
|
||||||
if (function_status.sdr104_support && !sdmmc_sd_switch_bus_speed(mmc, SDMMC_SPEED_SDR104, &function_status))
|
if (function_status.sdr104_support && !sdmmc_switch_bus_speed(mmc, SDMMC_SPEED_SDR104))
|
||||||
return 0;
|
return 0;
|
||||||
if (function_status.sdr50_support && !sdmmc_sd_switch_bus_speed(mmc, SDMMC_SPEED_SDR50, &function_status))
|
if (function_status.sdr50_support && !sdmmc_switch_bus_speed(mmc, SDMMC_SPEED_SDR50))
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we support High Speed but not a UHS-I mode, use it.
|
// If we support High Speed but not a UHS-I mode, use it.
|
||||||
if (function_status.sdr25_support)
|
if (function_status.sdr25_support)
|
||||||
return sdmmc_sd_switch_bus_speed(mmc, SDMMC_SPEED_SDR25, &function_status);
|
return sdmmc_switch_bus_speed(mmc, SDMMC_SPEED_SDR25);
|
||||||
|
|
||||||
mmc_debug(mmc, "couldn't improve speed above the speed already set.");
|
mmc_debug(mmc, "couldn't improve speed above the speed already set.");
|
||||||
return 0;
|
return 0;
|
||||||
@ -2560,17 +2745,46 @@ static int sdmmc_sd_optimize_speed(struct mmc *mmc)
|
|||||||
*/
|
*/
|
||||||
static int sdmmc_mmc_optimize_speed(struct mmc *mmc)
|
static int sdmmc_mmc_optimize_speed(struct mmc *mmc)
|
||||||
{
|
{
|
||||||
// TODO
|
int rc;
|
||||||
return 0;
|
bool hs52_support, hs200_support, hs400_support;
|
||||||
}
|
|
||||||
|
|
||||||
|
// XXX
|
||||||
|
sdmmc_set_loglevel(3);
|
||||||
|
|
||||||
/**
|
// We started off with the controller opearting with a divided clock,
|
||||||
* Optimize our MMC transfer speed by maxing out the bus as much as is possible.
|
// which is meant to keep us within the pre-init operating frequency of 400kHz.
|
||||||
*/
|
// We're now set up, so we can drop the divider. From this point forward, the
|
||||||
static int sdmmc_emmc_optimize_speed(struct mmc *mmc)
|
// clock is now driven by the CAR frequency.
|
||||||
{
|
rc = sdmmc_apply_clock_speed(mmc, SDMMC_SPEED_SDR12, true);
|
||||||
// TODO
|
if (rc) {
|
||||||
|
mmc_print(mmc, "WARNING: could not step up to normal operating speeds!");
|
||||||
|
mmc_print(mmc, " ... expect delays.");
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
mmc_debug(mmc, "now operating at 25MB/s!");
|
||||||
|
|
||||||
|
// Determine which high-speed modes are supported, for easy reference below.
|
||||||
|
hs200_support = mmc->mmc_card_type & MMC_EXT_CSD_CARD_TYPE_HS400_1V8;
|
||||||
|
hs400_support = mmc->mmc_card_type & MMC_EXT_CSD_CARD_TYPE_HS400_1V8;
|
||||||
|
hs52_support = mmc->mmc_card_type & MMC_EXT_CSD_CARD_TYPE_HS52;
|
||||||
|
|
||||||
|
// First, try modes that are only supported at 1V8 and below,
|
||||||
|
// if we can.
|
||||||
|
if (mmc->operating_voltage == MMC_VOLTAGE_1V8) {
|
||||||
|
//if (hs400_support && !sdmmc_switch_bus_speed(mmc, SDMMC_SPEED_HS400))
|
||||||
|
// return 0;
|
||||||
|
//if (hs200_support && !sdmmc_switch_bus_speed(mmc, SDMMC_SPEED_HS200))
|
||||||
|
// return 0;
|
||||||
|
|
||||||
|
(void)hs400_support;
|
||||||
|
(void)hs200_support;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Next, try the legacy "high speed" mode.
|
||||||
|
if (hs52_support)
|
||||||
|
return sdmmc_switch_bus_speed(mmc, SDMMC_SPEED_HS52);
|
||||||
|
|
||||||
|
mmc_debug(mmc, "couldn't improve speed above the default");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3061,12 +3275,8 @@ static void sdmmc_apply_card_type(struct mmc *mmc, enum sdmmc_card_type type)
|
|||||||
mmc->establish_relative_address = sdmmc_set_relative_address;
|
mmc->establish_relative_address = sdmmc_set_relative_address;
|
||||||
mmc->switch_mode = sdmmc_mmc_switch_mode;
|
mmc->switch_mode = sdmmc_mmc_switch_mode;
|
||||||
mmc->switch_bus_width = sdmmc_mmc_switch_bus_width;
|
mmc->switch_bus_width = sdmmc_mmc_switch_bus_width;
|
||||||
|
mmc->card_switch_bus_speed = sdmmc_mmc_switch_bus_speed;
|
||||||
// Handle eMMC and MMC speed optimizations differently.
|
mmc->optimize_speed = sdmmc_mmc_optimize_speed;
|
||||||
if (type == MMC_CARD_EMMC)
|
|
||||||
mmc->optimize_speed = sdmmc_emmc_optimize_speed;
|
|
||||||
else
|
|
||||||
mmc->optimize_speed = sdmmc_mmc_optimize_speed;
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -3077,6 +3287,7 @@ static void sdmmc_apply_card_type(struct mmc *mmc, enum sdmmc_card_type type)
|
|||||||
mmc->switch_mode = sdmmc_sd_switch_mode;
|
mmc->switch_mode = sdmmc_sd_switch_mode;
|
||||||
mmc->switch_bus_width = sdmmc_sd_switch_bus_width;
|
mmc->switch_bus_width = sdmmc_sd_switch_bus_width;
|
||||||
mmc->optimize_speed = sdmmc_sd_optimize_speed;
|
mmc->optimize_speed = sdmmc_sd_optimize_speed;
|
||||||
|
mmc->card_switch_bus_speed = sdmmc_sd_switch_bus_speed;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// Switch-cart protocol cards
|
// Switch-cart protocol cards
|
||||||
|
@ -34,7 +34,6 @@ enum sdmmc_bus_width {
|
|||||||
*/
|
*/
|
||||||
enum sdmmc_bus_voltage {
|
enum sdmmc_bus_voltage {
|
||||||
MMC_VOLTAGE_3V3 = 0b111,
|
MMC_VOLTAGE_3V3 = 0b111,
|
||||||
MMC_VOLTAGE_3V0 = 0b110,
|
|
||||||
MMC_VOLTAGE_1V8 = 0b101,
|
MMC_VOLTAGE_1V8 = 0b101,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -115,27 +114,28 @@ enum sdmmc_switch_argument_offsets {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Fields that can be modified by CMD_SWITCH_MODE.
|
|
||||||
*/
|
|
||||||
enum sdmmc_switch_field {
|
|
||||||
/* Fields */
|
|
||||||
MMC_GROUP_ERASE_DEF = 175,
|
|
||||||
MMC_PARTITION_CONFIG = 179,
|
|
||||||
MMC_BUS_WIDTH = 183,
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Bus speeds possible for an SDMMC controller.
|
* Bus speeds possible for an SDMMC controller.
|
||||||
*/
|
*/
|
||||||
enum sdmmc_bus_speed {
|
enum sdmmc_bus_speed {
|
||||||
|
/* SD card speeds */
|
||||||
SDMMC_SPEED_SDR12 = 0,
|
SDMMC_SPEED_SDR12 = 0,
|
||||||
SDMMC_SPEED_SDR25 = 1,
|
SDMMC_SPEED_SDR25 = 1,
|
||||||
SDMMC_SPEED_SDR50 = 2,
|
SDMMC_SPEED_SDR50 = 2,
|
||||||
SDMMC_SPEED_SDR104 = 3,
|
SDMMC_SPEED_SDR104 = 3,
|
||||||
SDMMC_SPEED_DDR50 = 4,
|
SDMMC_SPEED_DDR50 = 4,
|
||||||
|
|
||||||
|
/* Other speed: non-spec-compliant value used for e.g. HS400 */
|
||||||
|
SDMMC_SPEED_OTHER = 5,
|
||||||
|
|
||||||
|
/* eMMC card speeds */
|
||||||
|
/* note: to avoid an enum clash, we add ten to these */
|
||||||
|
SDMMC_SPEED_HS26 = 10 ,
|
||||||
|
SDMMC_SPEED_HS52 = 11 ,
|
||||||
|
SDMMC_SPEED_HS200 = 12 ,
|
||||||
|
SDMMC_SPEED_HS400 = 13 ,
|
||||||
|
|
||||||
|
/* special speeds */
|
||||||
SDMMC_SPEED_INIT = -1,
|
SDMMC_SPEED_INIT = -1,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -152,7 +152,6 @@ struct mmc {
|
|||||||
bool use_dma;
|
bool use_dma;
|
||||||
unsigned int timeout;
|
unsigned int timeout;
|
||||||
enum tegra_named_gpio card_detect_gpio;
|
enum tegra_named_gpio card_detect_gpio;
|
||||||
enum sdmmc_card_type card_type;
|
|
||||||
enum sdmmc_write_permission write_enable;
|
enum sdmmc_write_permission write_enable;
|
||||||
|
|
||||||
/* Per-controller operations. */
|
/* Per-controller operations. */
|
||||||
@ -168,8 +167,12 @@ struct mmc {
|
|||||||
int (*switch_mode)(struct mmc *mmc, int a, int b, int c, uint32_t timeout, void *response);
|
int (*switch_mode)(struct mmc *mmc, int a, int b, int c, uint32_t timeout, void *response);
|
||||||
int (*switch_bus_width)(struct mmc *mmc, enum sdmmc_bus_width width);
|
int (*switch_bus_width)(struct mmc *mmc, enum sdmmc_bus_width width);
|
||||||
int (*optimize_speed)(struct mmc *mmc);
|
int (*optimize_speed)(struct mmc *mmc);
|
||||||
|
int (*card_switch_bus_speed)(struct mmc *mmc, enum sdmmc_bus_speed speed);
|
||||||
|
|
||||||
/* Card properties */
|
/* Card properties */
|
||||||
|
enum sdmmc_card_type card_type;
|
||||||
|
uint32_t mmc_card_type;
|
||||||
|
|
||||||
uint8_t cid[15];
|
uint8_t cid[15];
|
||||||
uint32_t relative_address;
|
uint32_t relative_address;
|
||||||
uint8_t partitioned;
|
uint8_t partitioned;
|
||||||
|
Loading…
Reference in New Issue
Block a user