mirror of
https://github.com/Atmosphere-NX/Atmosphere.git
synced 2024-11-29 23:32:23 +00:00
Merge pull request #126 from desowin/sdmmc-autocal
Make sdmmc autocalibration follow TRM procedure
This commit is contained in:
commit
f5163fa35d
4 changed files with 76 additions and 14 deletions
|
@ -11,4 +11,14 @@
|
|||
#define APB_MISC_GP_SDMMC1_PAD_CFGPADCTRL_0 MAKE_REG32(MISC_BASE + 0xa98)
|
||||
#define APB_MISC_GP_EMMC4_PAD_CFGPADCTRL_0 MAKE_REG32(MISC_BASE + 0xab4)
|
||||
|
||||
#define SDMMC1_PAD_CAL_DRVUP_SHIFT (20)
|
||||
#define SDMMC1_PAD_CAL_DRVDN_SHIFT (12)
|
||||
#define SDMMC1_PAD_CAL_DRVUP_MASK (0x7Fu << SDMMC1_PAD_CAL_DRVUP_SHIFT)
|
||||
#define SDMMC1_PAD_CAL_DRVDN_MASK (0x7Fu << SDMMC1_PAD_CAL_DRVDN_SHIFT)
|
||||
|
||||
#define CFG2TMC_EMMC4_PAD_DRVUP_COMP_SHIFT (8)
|
||||
#define CFG2TMC_EMMC4_PAD_DRVDN_COMP_SHIFT (2)
|
||||
#define CFG2TMC_EMMC4_PAD_DRVUP_COMP_MASK (0x3Fu << CFG2TMC_EMMC4_PAD_DRVUP_COMP_SHIFT)
|
||||
#define CFG2TMC_EMMC4_PAD_DRVDN_COMP_MASK (0x3Fu << CFG2TMC_EMMC4_PAD_DRVDN_COMP_SHIFT)
|
||||
|
||||
#endif
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include "supplies.h"
|
||||
#include "pmc.h"
|
||||
#include "pad_control.h"
|
||||
#include "apb_misc.h"
|
||||
|
||||
#define TEGRA_SDMMC_BASE (0x700B0000)
|
||||
#define TEGRA_SDMMC_SIZE (0x200)
|
||||
|
@ -462,11 +463,12 @@ enum sdmmc_command_magic {
|
|||
|
||||
/* Misc constants */
|
||||
MMC_DEFAULT_BLOCK_ORDER = 9,
|
||||
MMC_VOLTAGE_SWITCH_TIME = 5000, // 5mS
|
||||
MMC_POST_CLOCK_DELAY = 1000, // 1mS
|
||||
MMC_VOLTAGE_SWITCH_TIME = 5000, // 5ms
|
||||
MMC_POST_CLOCK_DELAY = 1000, // 1ms
|
||||
MMC_SPEED_MMC_OFFSET = 10,
|
||||
|
||||
MMC_TUNING_TIMEOUT = 150 * 1000, // 150mS
|
||||
MMC_AUTOCAL_TIMEOUT = 10 * 1000, // 10ms
|
||||
MMC_TUNING_TIMEOUT = 150 * 1000, // 150ms
|
||||
MMC_TUNING_BLOCK_ORDER_4BIT = 6,
|
||||
MMC_TUNING_BLOCK_ORDER_8BIT = 7,
|
||||
};
|
||||
|
@ -975,6 +977,7 @@ void sdmmc_clock_enable(struct mmc *mmc, bool enabled)
|
|||
static int sdmmc_run_autocal(struct mmc *mmc, bool restart_sd_clock)
|
||||
{
|
||||
uint32_t timebase;
|
||||
int ret = 0;
|
||||
|
||||
// Stop the SD card's clock, so our autocal sequence doesn't
|
||||
// confuse the target card.
|
||||
|
@ -982,16 +985,34 @@ static int sdmmc_run_autocal(struct mmc *mmc, bool restart_sd_clock)
|
|||
|
||||
// Start automatic calibration...
|
||||
mmc->regs->auto_cal_config |= (MMC_AUTOCAL_START | MMC_AUTOCAL_ENABLE);
|
||||
udelay(1000);
|
||||
udelay(1);
|
||||
|
||||
// ... and wait until the autocal is complete
|
||||
timebase = get_time();
|
||||
while ((mmc->regs->auto_cal_status & MMC_AUTOCAL_ACTIVE)) {
|
||||
|
||||
// Ensure we haven't timed out...
|
||||
if (get_time_since(timebase) > mmc->timeout) {
|
||||
if (get_time_since(timebase) > MMC_AUTOCAL_TIMEOUT) {
|
||||
mmc_print(mmc, "ERROR: autocal timed out!");
|
||||
return ETIMEDOUT;
|
||||
if (mmc->controller == SWITCH_MICROSD) {
|
||||
// Fallback driver strengths from Tegra X1 TRM
|
||||
uint32_t drvup = (mmc->operating_voltage == MMC_VOLTAGE_3V3) ? 0x12 : 0x11;
|
||||
uint32_t drvdn = (mmc->operating_voltage == MMC_VOLTAGE_3V3) ? 0x12 : 0x15;
|
||||
uint32_t value = APB_MISC_GP_SDMMC1_PAD_CFGPADCTRL_0;
|
||||
value &= ~(SDMMC1_PAD_CAL_DRVUP_MASK | SDMMC1_PAD_CAL_DRVDN_MASK);
|
||||
value |= (drvup << SDMMC1_PAD_CAL_DRVUP_SHIFT);
|
||||
value |= (drvdn << SDMMC1_PAD_CAL_DRVDN_SHIFT);
|
||||
APB_MISC_GP_SDMMC1_PAD_CFGPADCTRL_0 = value;
|
||||
} else if (mmc->controller == SWITCH_EMMC) {
|
||||
uint32_t value = APB_MISC_GP_EMMC4_PAD_CFGPADCTRL_0;
|
||||
value &= ~(CFG2TMC_EMMC4_PAD_DRVUP_COMP_MASK | CFG2TMC_EMMC4_PAD_DRVDN_COMP_MASK);
|
||||
value |= (0x10 << CFG2TMC_EMMC4_PAD_DRVUP_COMP_SHIFT);
|
||||
value |= (0x10 << CFG2TMC_EMMC4_PAD_DRVDN_COMP_SHIFT);
|
||||
APB_MISC_GP_EMMC4_PAD_CFGPADCTRL_0 = value;
|
||||
}
|
||||
mmc->regs->auto_cal_config &= ~MMC_AUTOCAL_ENABLE;
|
||||
ret = ETIMEDOUT;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -999,7 +1020,7 @@ static int sdmmc_run_autocal(struct mmc *mmc, bool restart_sd_clock)
|
|||
if (restart_sd_clock)
|
||||
sdmmc_clock_enable(mmc, true);
|
||||
|
||||
return 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -11,4 +11,14 @@
|
|||
#define APB_MISC_GP_SDMMC1_PAD_CFGPADCTRL_0 MAKE_REG32(MISC_BASE + 0xa98)
|
||||
#define APB_MISC_GP_EMMC4_PAD_CFGPADCTRL_0 MAKE_REG32(MISC_BASE + 0xab4)
|
||||
|
||||
#define SDMMC1_PAD_CAL_DRVUP_SHIFT (20)
|
||||
#define SDMMC1_PAD_CAL_DRVDN_SHIFT (12)
|
||||
#define SDMMC1_PAD_CAL_DRVUP_MASK (0x7Fu << SDMMC1_PAD_CAL_DRVUP_SHIFT)
|
||||
#define SDMMC1_PAD_CAL_DRVDN_MASK (0x7Fu << SDMMC1_PAD_CAL_DRVDN_SHIFT)
|
||||
|
||||
#define CFG2TMC_EMMC4_PAD_DRVUP_COMP_SHIFT (8)
|
||||
#define CFG2TMC_EMMC4_PAD_DRVDN_COMP_SHIFT (2)
|
||||
#define CFG2TMC_EMMC4_PAD_DRVUP_COMP_MASK (0x3Fu << CFG2TMC_EMMC4_PAD_DRVUP_COMP_SHIFT)
|
||||
#define CFG2TMC_EMMC4_PAD_DRVDN_COMP_MASK (0x3Fu << CFG2TMC_EMMC4_PAD_DRVDN_COMP_SHIFT)
|
||||
|
||||
#endif
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include "supplies.h"
|
||||
#include "pmc.h"
|
||||
#include "pad_control.h"
|
||||
#include "apb_misc.h"
|
||||
|
||||
#define TEGRA_SDMMC_BASE (0x700B0000)
|
||||
#define TEGRA_SDMMC_SIZE (0x200)
|
||||
|
@ -462,11 +463,12 @@ enum sdmmc_command_magic {
|
|||
|
||||
/* Misc constants */
|
||||
MMC_DEFAULT_BLOCK_ORDER = 9,
|
||||
MMC_VOLTAGE_SWITCH_TIME = 5000, // 5mS
|
||||
MMC_POST_CLOCK_DELAY = 1000, // 1mS
|
||||
MMC_VOLTAGE_SWITCH_TIME = 5000, // 5ms
|
||||
MMC_POST_CLOCK_DELAY = 1000, // 1ms
|
||||
MMC_SPEED_MMC_OFFSET = 10,
|
||||
|
||||
MMC_TUNING_TIMEOUT = 150 * 1000, // 150mS
|
||||
MMC_AUTOCAL_TIMEOUT = 10 * 1000, // 10ms
|
||||
MMC_TUNING_TIMEOUT = 150 * 1000, // 150ms
|
||||
MMC_TUNING_BLOCK_ORDER_4BIT = 6,
|
||||
MMC_TUNING_BLOCK_ORDER_8BIT = 7,
|
||||
};
|
||||
|
@ -975,6 +977,7 @@ void sdmmc_clock_enable(struct mmc *mmc, bool enabled)
|
|||
static int sdmmc_run_autocal(struct mmc *mmc, bool restart_sd_clock)
|
||||
{
|
||||
uint32_t timebase;
|
||||
int ret = 0;
|
||||
|
||||
// Stop the SD card's clock, so our autocal sequence doesn't
|
||||
// confuse the target card.
|
||||
|
@ -982,16 +985,34 @@ static int sdmmc_run_autocal(struct mmc *mmc, bool restart_sd_clock)
|
|||
|
||||
// Start automatic calibration...
|
||||
mmc->regs->auto_cal_config |= (MMC_AUTOCAL_START | MMC_AUTOCAL_ENABLE);
|
||||
udelay(1000);
|
||||
udelay(1);
|
||||
|
||||
// ... and wait until the autocal is complete
|
||||
timebase = get_time();
|
||||
while ((mmc->regs->auto_cal_status & MMC_AUTOCAL_ACTIVE)) {
|
||||
|
||||
// Ensure we haven't timed out...
|
||||
if (get_time_since(timebase) > mmc->timeout) {
|
||||
if (get_time_since(timebase) > MMC_AUTOCAL_TIMEOUT) {
|
||||
mmc_print(mmc, "ERROR: autocal timed out!");
|
||||
return ETIMEDOUT;
|
||||
if (mmc->controller == SWITCH_MICROSD) {
|
||||
// Fallback driver strengths from Tegra X1 TRM
|
||||
uint32_t drvup = (mmc->operating_voltage == MMC_VOLTAGE_3V3) ? 0x12 : 0x11;
|
||||
uint32_t drvdn = (mmc->operating_voltage == MMC_VOLTAGE_3V3) ? 0x12 : 0x15;
|
||||
uint32_t value = APB_MISC_GP_SDMMC1_PAD_CFGPADCTRL_0;
|
||||
value &= ~(SDMMC1_PAD_CAL_DRVUP_MASK | SDMMC1_PAD_CAL_DRVDN_MASK);
|
||||
value |= (drvup << SDMMC1_PAD_CAL_DRVUP_SHIFT);
|
||||
value |= (drvdn << SDMMC1_PAD_CAL_DRVDN_SHIFT);
|
||||
APB_MISC_GP_SDMMC1_PAD_CFGPADCTRL_0 = value;
|
||||
} else if (mmc->controller == SWITCH_EMMC) {
|
||||
uint32_t value = APB_MISC_GP_EMMC4_PAD_CFGPADCTRL_0;
|
||||
value &= ~(CFG2TMC_EMMC4_PAD_DRVUP_COMP_MASK | CFG2TMC_EMMC4_PAD_DRVDN_COMP_MASK);
|
||||
value |= (0x10 << CFG2TMC_EMMC4_PAD_DRVUP_COMP_SHIFT);
|
||||
value |= (0x10 << CFG2TMC_EMMC4_PAD_DRVDN_COMP_SHIFT);
|
||||
APB_MISC_GP_EMMC4_PAD_CFGPADCTRL_0 = value;
|
||||
}
|
||||
mmc->regs->auto_cal_config &= ~MMC_AUTOCAL_ENABLE;
|
||||
ret = ETIMEDOUT;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -999,7 +1020,7 @@ static int sdmmc_run_autocal(struct mmc *mmc, bool restart_sd_clock)
|
|||
if (restart_sd_clock)
|
||||
sdmmc_clock_enable(mmc, true);
|
||||
|
||||
return 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
|
|
Loading…
Reference in a new issue