1
0
Fork 0
mirror of https://github.com/Atmosphere-NX/Atmosphere.git synced 2024-11-10 06:01:52 +00:00

fusee: work around some dual-init SDMMC issues

This commit is contained in:
Kate J. Temkin 2018-05-23 06:14:38 -06:00
parent ef9adabb40
commit eaf8e559d6
3 changed files with 44 additions and 16 deletions

View file

@ -15,6 +15,13 @@
static bool g_ahb_redirect_enabled = false; static bool g_ahb_redirect_enabled = false;
// Only use voltage switching in stage2 and later.
#ifdef FUSEE_STAGE1_SRC
#define MMC_VOLTAGE_SWITCHING_ALLOWED false
#else
#define MMC_VOLTAGE_SWITCHING_ALLOWED true
#endif
/* Global sd struct. */ /* Global sd struct. */
static struct mmc g_sd_mmc = {0}; static struct mmc g_sd_mmc = {0};
static bool g_sd_initialized = false; static bool g_sd_initialized = false;
@ -26,7 +33,7 @@ int initialize_sd_mmc(void) {
} }
if (!g_sd_initialized) { if (!g_sd_initialized) {
int rc = sdmmc_init(&g_sd_mmc, SWITCH_MICROSD); int rc = sdmmc_init(&g_sd_mmc, SWITCH_MICROSD, MMC_VOLTAGE_SWITCHING_ALLOWED);
if (rc == 0) { if (rc == 0) {
g_sd_initialized = true; g_sd_initialized = true;
return 0; return 0;

View file

@ -140,7 +140,7 @@ enum sdmmc_clock_dividers {
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 = 2, // 2, from the datasheet MMC_CLOCK_DIVIDER_SDR104 = 4, // 2, from the datasheet
/* Clock dividers: MMC */ /* Clock dividers: MMC */
MMC_CLOCK_DIVIDER_HS26 = 30, // 16, from the TRM table MMC_CLOCK_DIVIDER_HS26 = 30, // 16, from the TRM table
@ -568,11 +568,11 @@ struct PACKED sdmmc_function_status {
/* support for various speed modes */ /* support for various speed modes */
uint16_t group1_support_reserved1 : 8; uint16_t group1_support_reserved1 : 8;
uint16_t ddr50_support : 1;
uint16_t sdr104_support : 1;
uint16_t sdr50_support : 1;
uint16_t sdr25_support : 1;
uint16_t sdr12_support : 1; uint16_t sdr12_support : 1;
uint16_t sdr25_support : 1;
uint16_t sdr50_support : 1;
uint16_t sdr104_support : 1;
uint16_t ddr50_support : 1;
uint16_t group1_support_reserved2 : 3; uint16_t group1_support_reserved2 : 3;
@ -631,10 +631,14 @@ static const uint16_t sdmmc_bounce_dma_boundary = MMC_DMA_BOUNDARY_8K;
* Sets the current SDMMC debugging loglevel. * Sets the current SDMMC debugging loglevel.
* *
* @param loglevel Current log level. A higher value prints more logs. * @param loglevel Current log level. A higher value prints more logs.
* @return The loglevel prior to when this was applied, for easy restoration.
*/ */
void sdmmc_set_loglevel(int loglevel) int sdmmc_set_loglevel(int loglevel)
{ {
int original_loglevel = sdmmc_loglevel;
sdmmc_loglevel = loglevel; sdmmc_loglevel = loglevel;
return original_loglevel;
} }
@ -887,9 +891,9 @@ static int sdmmc1_enable_supplies(struct mmc *mmc)
pinmux->dmic3_clk = PINMUX_SELECT_FUNCTION0; pinmux->dmic3_clk = PINMUX_SELECT_FUNCTION0;
gpio_configure_mode(GPIO_MICROSD_SUPPLY_ENABLE, GPIO_MODE_GPIO); gpio_configure_mode(GPIO_MICROSD_SUPPLY_ENABLE, GPIO_MODE_GPIO);
gpio_configure_direction(GPIO_MICROSD_SUPPLY_ENABLE, GPIO_DIRECTION_OUTPUT); gpio_configure_direction(GPIO_MICROSD_SUPPLY_ENABLE, GPIO_DIRECTION_OUTPUT);
// Bring up the SD card fixed regulator.
gpio_write(GPIO_MICROSD_SUPPLY_ENABLE, GPIO_LEVEL_HIGH); gpio_write(GPIO_MICROSD_SUPPLY_ENABLE, GPIO_LEVEL_HIGH);
return 0; return 0;
} }
@ -1415,6 +1419,7 @@ static int sdmmc_apply_clock_speed(struct mmc *mmc, enum sdmmc_bus_speed speed,
mmc_print(mmc, "TODO: double the data rate 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);
@ -2902,13 +2907,17 @@ static int sdmmc_mmc_wait_for_card_readiness(struct mmc *mmc)
int rc; int rc;
uint32_t response[4]; uint32_t response[4];
while (true) { while (true) {
uint32_t response_masked; uint32_t response_masked;
// Ask the SD card to identify its state. It will respond with readiness and a capacity magic. // Ask the SD card to identify its state. It will respond with readiness and a capacity magic.
int original_loglevel = sdmmc_set_loglevel(0);
rc = sdmmc_send_command(mmc, CMD_SEND_OPERATING_CONDITIONS, MMC_RESPONSE_LEN48, rc = sdmmc_send_command(mmc, CMD_SEND_OPERATING_CONDITIONS, MMC_RESPONSE_LEN48,
MMC_CHECKS_NONE, 0x40000080, response, 0, false, false, NULL); MMC_CHECKS_NONE, 0x40000080, response, 0, false, false, NULL);
sdmmc_set_loglevel(original_loglevel);
if (rc) { if (rc) {
mmc_print(mmc, "ERROR: could not read the card's operating conditions!"); mmc_print(mmc, "ERROR: could not read the card's operating conditions!");
return rc; return rc;
@ -2953,8 +2962,10 @@ static int sdmmc_sd_wait_for_card_readiness(struct mmc *mmc, uint32_t *response)
while (true) { while (true) {
// Ask the SD card to identify its state. // Ask the SD card to identify its state.
int original_loglevel = sdmmc_set_loglevel(0);
rc = sdmmc_send_simple_app_command(mmc, CMD_APP_SEND_OP_COND, rc = sdmmc_send_simple_app_command(mmc, CMD_APP_SEND_OP_COND,
MMC_RESPONSE_LEN48, MMC_CHECKS_NONE, argument, response); MMC_RESPONSE_LEN48, MMC_CHECKS_NONE, argument, response);
sdmmc_set_loglevel(original_loglevel);
if (rc) { if (rc) {
mmc_print(mmc, "ERROR: could not read the card's operating conditions!"); mmc_print(mmc, "ERROR: could not read the card's operating conditions!");
return rc; return rc;
@ -3071,7 +3082,7 @@ static int sdmmc_sd_card_init(struct mmc *mmc)
mmc->uses_block_addressing = !!(ocr & MMC_SD_OPERATING_COND_HIGH_CAPACITY); mmc->uses_block_addressing = !!(ocr & MMC_SD_OPERATING_COND_HIGH_CAPACITY);
// If the card supports using 1V8, drop down using lower voltages. // If the card supports using 1V8, drop down using lower voltages.
if (ocr & MMC_SD_OPERATING_COND_ACCEPTS_1V8) { if (mmc->allow_voltage_switching && ocr & MMC_SD_OPERATING_COND_ACCEPTS_1V8) {
if (mmc->operating_voltage != MMC_VOLTAGE_1V8) { if (mmc->operating_voltage != MMC_VOLTAGE_1V8) {
rc = mmc->switch_to_low_voltage(mmc); rc = mmc->switch_to_low_voltage(mmc);
if (rc) if (rc)
@ -3369,14 +3380,18 @@ static int sdmmc_initialize_defaults(struct mmc *mmc)
* @param mmc The SDMMC structure to be initiailized with the device state. * @param mmc The SDMMC structure to be initiailized with the device state.
* @param controler The controller description to be used; usually SWITCH_EMMC * @param controler The controller description to be used; usually SWITCH_EMMC
* or SWITCH_MICROSD. * or SWITCH_MICROSD.
* @param allow_voltage_switching True if we should allow voltage switching,
* which may not make sense if we're about to chainload to another component,
* a la fusee stage1.
*/ */
int sdmmc_init(struct mmc *mmc, enum sdmmc_controller controller) int sdmmc_init(struct mmc *mmc, enum sdmmc_controller controller, bool allow_voltage_switching)
{ {
int rc; int rc;
// Get a reference to the registers for the relevant SDMMC controller. // Get a reference to the registers for the relevant SDMMC controller.
mmc->controller = controller; mmc->controller = controller;
mmc->regs = sdmmc_get_regs(controller); mmc->regs = sdmmc_get_regs(controller);
mmc->allow_voltage_switching = false;
// Set the defaults for the card, including the default function pointers // Set the defaults for the card, including the default function pointers
// for the assumed card type, and the per-controller options. // for the assumed card type, and the per-controller options.

View file

@ -150,6 +150,7 @@ struct mmc {
/* Controller properties */ /* Controller properties */
const char *name; const char *name;
bool use_dma; bool use_dma;
bool allow_voltage_switching;
unsigned int timeout; unsigned int timeout;
enum tegra_named_gpio card_detect_gpio; enum tegra_named_gpio card_detect_gpio;
enum sdmmc_write_permission write_enable; enum sdmmc_write_permission write_enable;
@ -221,17 +222,22 @@ enum sdmmc_partition {
* Sets the current SDMMC debugging loglevel. * Sets the current SDMMC debugging loglevel.
* *
* @param loglevel Current log level. A higher value prints more logs. * @param loglevel Current log level. A higher value prints more logs.
* @return The loglevel prior to when this was applied, for easy restoration.
*/ */
void sdmmc_set_loglevel(int loglevel); int sdmmc_set_loglevel(int loglevel);
/** /**
* Initiailzes an SDMMC controller for use with an eMMC or SD card device. * Set up a new SDMMC driver.
* *
* @param mmc An (uninitialized) structure for the MMC device. * @param mmc The SDMMC structure to be initiailized with the device state.
* @param controller The controller number to be initialized. Either SWITCH_MICROSD or SWITCH_EMMC. * @param controler The controller description to be used; usually SWITCH_EMMC
* or SWITCH_MICROSD.
* @param allow_voltage_switching True if we should allow voltage switching,
* which may not make sense if we're about to chainload to another component,
* a la fusee stage1.
*/ */
int sdmmc_init(struct mmc *mmc, enum sdmmc_controller controller); int sdmmc_init(struct mmc *mmc, enum sdmmc_controller controller, bool allow_voltage_switching);
/** /**