mirror of
https://github.com/CTCaer/hekate
synced 2024-12-22 11:21:23 +00:00
sdmmc: Add T210B01 support
The driver was working before this, but adding the changes provides a proper and better sdmmc controller inner state.
This commit is contained in:
parent
8e45fcc069
commit
c13eabcde8
4 changed files with 219 additions and 69 deletions
|
@ -71,6 +71,18 @@
|
||||||
#define PINMUX_AUX_GPIO_PH6 0x250
|
#define PINMUX_AUX_GPIO_PH6 0x250
|
||||||
#define PINMUX_AUX_GPIO_PK3 0x260
|
#define PINMUX_AUX_GPIO_PK3 0x260
|
||||||
#define PINMUX_AUX_GPIO_PZ1 0x280
|
#define PINMUX_AUX_GPIO_PZ1 0x280
|
||||||
|
/* Only in T210B01 */
|
||||||
|
#define PINMUX_AUX_SDMMC2_DAT0 0x294
|
||||||
|
#define PINMUX_AUX_SDMMC2_DAT1 0x298
|
||||||
|
#define PINMUX_AUX_SDMMC2_DAT2 0x29C
|
||||||
|
#define PINMUX_AUX_SDMMC2_DAT3 0x2A0
|
||||||
|
#define PINMUX_AUX_SDMMC2_DAT4 0x2A4
|
||||||
|
#define PINMUX_AUX_SDMMC2_DAT5 0x2A8
|
||||||
|
#define PINMUX_AUX_SDMMC2_DAT6 0x2AC
|
||||||
|
#define PINMUX_AUX_SDMMC2_DAT7 0x2B0
|
||||||
|
#define PINMUX_AUX_SDMMC2_CLK 0x2B4
|
||||||
|
#define PINMUX_AUX_SDMMC2_CMD 0x2BC
|
||||||
|
|
||||||
/*! 0:UART-A, 1:UART-B, 3:UART-C, 3:UART-D */
|
/*! 0:UART-A, 1:UART-B, 3:UART-C, 3:UART-D */
|
||||||
#define PINMUX_AUX_UARTX_TX(x) (0xE4 + 0x10 * (x))
|
#define PINMUX_AUX_UARTX_TX(x) (0xE4 + 0x10 * (x))
|
||||||
#define PINMUX_AUX_UARTX_RX(x) (0xE8 + 0x10 * (x))
|
#define PINMUX_AUX_UARTX_RX(x) (0xE8 + 0x10 * (x))
|
||||||
|
|
|
@ -1205,9 +1205,10 @@ static bool _sdmmc_storage_get_low_voltage_support(u32 bus_width, u32 type)
|
||||||
|
|
||||||
void sdmmc_storage_init_wait_sd()
|
void sdmmc_storage_init_wait_sd()
|
||||||
{
|
{
|
||||||
|
// T210/T210B01 WAR: Wait exactly 239ms for IO and Controller power to discharge.
|
||||||
u32 sd_poweroff_time = (u32)get_tmr_ms() - sd_power_cycle_time_start;
|
u32 sd_poweroff_time = (u32)get_tmr_ms() - sd_power_cycle_time_start;
|
||||||
if (sd_poweroff_time < 100)
|
if (sd_poweroff_time < 239)
|
||||||
msleep(100 - sd_poweroff_time);
|
msleep(239 - sd_poweroff_time);
|
||||||
}
|
}
|
||||||
|
|
||||||
int sdmmc_storage_init_sd(sdmmc_storage_t *storage, sdmmc_t *sdmmc, u32 bus_width, u32 type)
|
int sdmmc_storage_init_sd(sdmmc_storage_t *storage, sdmmc_t *sdmmc, u32 bus_width, u32 type)
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
#include <soc/bpmp.h>
|
#include <soc/bpmp.h>
|
||||||
#include <soc/clock.h>
|
#include <soc/clock.h>
|
||||||
#include <soc/gpio.h>
|
#include <soc/gpio.h>
|
||||||
|
#include <soc/hw_init.h>
|
||||||
#include <soc/pinmux.h>
|
#include <soc/pinmux.h>
|
||||||
#include <soc/pmc.h>
|
#include <soc/pmc.h>
|
||||||
#include <soc/t210.h>
|
#include <soc/t210.h>
|
||||||
|
@ -115,7 +116,7 @@ void sdmmc_save_tap_value(sdmmc_t *sdmmc)
|
||||||
static int _sdmmc_config_tap_val(sdmmc_t *sdmmc, u32 type)
|
static int _sdmmc_config_tap_val(sdmmc_t *sdmmc, u32 type)
|
||||||
{
|
{
|
||||||
const u32 dqs_trim_val = 0x28;
|
const u32 dqs_trim_val = 0x28;
|
||||||
const u32 tap_values[] = { 4, 0, 3, 0 };
|
const u32 tap_values_t210[] = { 4, 0, 3, 0 };
|
||||||
|
|
||||||
u32 tap_val = 0;
|
u32 tap_val = 0;
|
||||||
|
|
||||||
|
@ -132,9 +133,8 @@ static int _sdmmc_config_tap_val(sdmmc_t *sdmmc, u32 type)
|
||||||
tap_val = sdmmc->venclkctl_tap;
|
tap_val = sdmmc->venclkctl_tap;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
tap_val = sdmmc->t210b01 ? 11 : tap_values_t210[sdmmc->id];
|
||||||
tap_val = tap_values[sdmmc->id];
|
|
||||||
}
|
|
||||||
sdmmc->regs->venclkctl = (sdmmc->regs->venclkctl & 0xFF00FFFF) | (tap_val << 16);
|
sdmmc->regs->venclkctl = (sdmmc->regs->venclkctl & 0xFF00FFFF) | (tap_val << 16);
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
|
@ -154,16 +154,28 @@ static void _sdmmc_pad_config_fallback(sdmmc_t *sdmmc, u32 power)
|
||||||
if (power == SDMMC_POWER_OFF)
|
if (power == SDMMC_POWER_OFF)
|
||||||
break;
|
break;
|
||||||
u32 sdmmc1_pad_cfg = APB_MISC(APB_MISC_GP_SDMMC1_PAD_CFGPADCTRL) & 0xF8080FFF;
|
u32 sdmmc1_pad_cfg = APB_MISC(APB_MISC_GP_SDMMC1_PAD_CFGPADCTRL) & 0xF8080FFF;
|
||||||
if (power == SDMMC_POWER_1_8)
|
if (sdmmc->t210b01)
|
||||||
APB_MISC(APB_MISC_GP_SDMMC1_PAD_CFGPADCTRL) = sdmmc1_pad_cfg | (0xB0F << 12); // Up: 11, Dn: 15. For 33 ohm.
|
sdmmc1_pad_cfg |= (0x808 << 12); // Up: 8, Dn: 8. For 33 ohm.
|
||||||
|
else if (power == SDMMC_POWER_1_8)
|
||||||
|
sdmmc1_pad_cfg |= (0xB0F << 12); // Up: 11, Dn: 15. For 33 ohm.
|
||||||
else if (power == SDMMC_POWER_3_3)
|
else if (power == SDMMC_POWER_3_3)
|
||||||
APB_MISC(APB_MISC_GP_SDMMC1_PAD_CFGPADCTRL) = sdmmc1_pad_cfg | (0xC0C << 12); // Up: 12, Dn: 12. For 33 ohm.
|
sdmmc1_pad_cfg |= (0xC0C << 12); // Up: 12, Dn: 12. For 33 ohm.
|
||||||
|
APB_MISC(APB_MISC_GP_SDMMC1_PAD_CFGPADCTRL) = sdmmc1_pad_cfg;
|
||||||
|
(void)APB_MISC(APB_MISC_GP_SDMMC1_PAD_CFGPADCTRL); // Commit write.
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SDMMC_2:
|
case SDMMC_2:
|
||||||
|
if (sdmmc->t210b01)
|
||||||
|
APB_MISC(APB_MISC_GP_EMMC2_PAD_CFGPADCTRL) = (APB_MISC(APB_MISC_GP_EMMC2_PAD_CFGPADCTRL) & 0xF8080FFF) | 0xA0A000;
|
||||||
|
else
|
||||||
|
APB_MISC(APB_MISC_GP_EMMC2_PAD_CFGPADCTRL) = (APB_MISC(APB_MISC_GP_EMMC2_PAD_CFGPADCTRL) & 0xFFFFC003) | 0x1040; // PU:16, PD:16.
|
||||||
|
(void)APB_MISC(APB_MISC_GP_EMMC2_PAD_CFGPADCTRL);
|
||||||
|
break;
|
||||||
|
|
||||||
case SDMMC_4: // 50 Ohm 2X Driver. PU:16, PD:16.
|
case SDMMC_4: // 50 Ohm 2X Driver. PU:16, PD:16, B01: PU:10, PD:10.
|
||||||
APB_MISC(APB_MISC_GP_EMMC4_PAD_CFGPADCTRL) = (APB_MISC(APB_MISC_GP_EMMC4_PAD_CFGPADCTRL) & 0xFFFFC003) | 0x1040;
|
APB_MISC(APB_MISC_GP_EMMC4_PAD_CFGPADCTRL) =
|
||||||
|
(APB_MISC(APB_MISC_GP_EMMC4_PAD_CFGPADCTRL) & 0xFFFFC003) | (sdmmc->t210b01 ? 0xA28 : 0x1040);
|
||||||
|
(void)APB_MISC(APB_MISC_GP_EMMC4_PAD_CFGPADCTRL); // Commit write.
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -199,24 +211,18 @@ static void _sdmmc_autocal_execute(sdmmc_t *sdmmc, u32 power)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/*
|
|
||||||
// Check if PU results are inside limits.
|
#ifdef ERROR_EXTRA_PRINTING
|
||||||
// SDMMC1: CZ pads - 7-bit PU. SDMMC2/4: LV_CZ pads - 5-bit PU.
|
// Check if Comp pad is open or short to ground.
|
||||||
u8 autocal_pu_status = sdmmc->regs->autocalsts & 0x7F;
|
// SDMMC1: CZ pads - T210/T210B01: 7-bit/5-bit. SDMMC2/4: LV_CZ pads - 5-bit.
|
||||||
switch (sdmmc->id)
|
u8 code_mask = (sdmmc->t210b01 || sdmmc->id != SDMMC_1) ? 0x1F : 0x7F;
|
||||||
{
|
u8 autocal_pu_status = sdmmc->regs->autocalsts & code_mask;
|
||||||
case SDMMC_1:
|
if (!autocal_pu_status)
|
||||||
if (!autocal_pu_status || autocal_pu_status == 0x7F)
|
EPRINTF("SDMMC: Comp Pad short to gnd!");
|
||||||
timeout = 0;
|
else if (autocal_pu_status == code_mask)
|
||||||
break;
|
EPRINTF("SDMMC: Comp Pad open!");
|
||||||
case SDMMC_2:
|
#endif
|
||||||
case SDMMC_4:
|
|
||||||
autocal_pu_status &= 0x1F;
|
|
||||||
if (!autocal_pu_status || autocal_pu_status == 0x1F)
|
|
||||||
timeout = 0;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
// In case auto calibration fails, we load suggested standard values.
|
// In case auto calibration fails, we load suggested standard values.
|
||||||
if (!timeout)
|
if (!timeout)
|
||||||
{
|
{
|
||||||
|
@ -289,6 +295,15 @@ static void _sdmmc_reset(sdmmc_t *sdmmc)
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void _sdmmc_reset_all(sdmmc_t *sdmmc)
|
||||||
|
{
|
||||||
|
sdmmc->regs->swrst |= SDHCI_RESET_ALL;
|
||||||
|
_sdmmc_commit_changes(sdmmc);
|
||||||
|
u32 timeout = get_tmr_ms() + 2000;//100ms
|
||||||
|
while ((sdmmc->regs->swrst & SDHCI_RESET_ALL) && get_tmr_ms() < timeout)
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
int sdmmc_setup_clock(sdmmc_t *sdmmc, u32 type)
|
int sdmmc_setup_clock(sdmmc_t *sdmmc, u32 type)
|
||||||
{
|
{
|
||||||
// Disable the SD clock if it was enabled, and reenable it later.
|
// Disable the SD clock if it was enabled, and reenable it later.
|
||||||
|
@ -384,7 +399,7 @@ int sdmmc_setup_clock(sdmmc_t *sdmmc, u32 type)
|
||||||
static void _sdmmc_card_clock_enable(sdmmc_t *sdmmc)
|
static void _sdmmc_card_clock_enable(sdmmc_t *sdmmc)
|
||||||
{
|
{
|
||||||
// Recalibrate conditionally.
|
// Recalibrate conditionally.
|
||||||
if ((sdmmc->id == SDMMC_1) && !sdmmc->powersave_enabled)
|
if (sdmmc->manual_cal && !sdmmc->powersave_enabled)
|
||||||
_sdmmc_autocal_execute(sdmmc, sdmmc_get_io_power(sdmmc));
|
_sdmmc_autocal_execute(sdmmc, sdmmc_get_io_power(sdmmc));
|
||||||
|
|
||||||
if (!sdmmc->powersave_enabled)
|
if (!sdmmc->powersave_enabled)
|
||||||
|
@ -404,7 +419,7 @@ static void _sdmmc_sd_clock_disable(sdmmc_t *sdmmc)
|
||||||
void sdmmc_card_clock_powersave(sdmmc_t *sdmmc, int powersave_enable)
|
void sdmmc_card_clock_powersave(sdmmc_t *sdmmc, int powersave_enable)
|
||||||
{
|
{
|
||||||
// Recalibrate periodically for SDMMC1.
|
// Recalibrate periodically for SDMMC1.
|
||||||
if ((sdmmc->id == SDMMC_1) && !powersave_enable && sdmmc->card_clock_enabled)
|
if (sdmmc->manual_cal && !powersave_enable && sdmmc->card_clock_enabled)
|
||||||
_sdmmc_autocal_execute(sdmmc, sdmmc_get_io_power(sdmmc));
|
_sdmmc_autocal_execute(sdmmc, sdmmc_get_io_power(sdmmc));
|
||||||
|
|
||||||
sdmmc->powersave_enabled = powersave_enable;
|
sdmmc->powersave_enabled = powersave_enable;
|
||||||
|
@ -749,13 +764,24 @@ static int _sdmmc_autocal_config_offset(sdmmc_t *sdmmc, u32 power)
|
||||||
case SDMMC_1:
|
case SDMMC_1:
|
||||||
if (power == SDMMC_POWER_1_8)
|
if (power == SDMMC_POWER_1_8)
|
||||||
{
|
{
|
||||||
off_pd = 123;
|
if (!sdmmc->t210b01)
|
||||||
off_pu = 123;
|
{
|
||||||
|
off_pd = 123;
|
||||||
|
off_pu = 123;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
off_pd = 6;
|
||||||
|
off_pu = 6;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (power == SDMMC_POWER_3_3)
|
else if (power == SDMMC_POWER_3_3)
|
||||||
{
|
{
|
||||||
off_pd = 125;
|
if (!sdmmc->t210b01)
|
||||||
off_pu = 0;
|
{
|
||||||
|
off_pd = 125;
|
||||||
|
off_pu = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -861,7 +887,7 @@ int sdmmc_stop_transmission(sdmmc_t *sdmmc, u32 *rsp)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
// Recalibrate periodically for SDMMC1.
|
// Recalibrate periodically for SDMMC1.
|
||||||
if ((sdmmc->id == SDMMC_1) && sdmmc->powersave_enabled)
|
if (sdmmc->manual_cal && sdmmc->powersave_enabled)
|
||||||
_sdmmc_autocal_execute(sdmmc, sdmmc_get_io_power(sdmmc));
|
_sdmmc_autocal_execute(sdmmc, sdmmc_get_io_power(sdmmc));
|
||||||
|
|
||||||
bool should_disable_sd_clock = false;
|
bool should_disable_sd_clock = false;
|
||||||
|
@ -1079,7 +1105,46 @@ static void _sdmmc_config_sdmmc1_schmitt()
|
||||||
PINMUX_AUX(PINMUX_AUX_SDMMC1_DAT0) |= PINMUX_SCHMT;
|
PINMUX_AUX(PINMUX_AUX_SDMMC1_DAT0) |= PINMUX_SCHMT;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int _sdmmc_config_sdmmc1()
|
static void _sdmmc_config_sdmmc2_schmitt()
|
||||||
|
{
|
||||||
|
PINMUX_AUX(PINMUX_AUX_SDMMC2_CLK) |= PINMUX_SCHMT;
|
||||||
|
PINMUX_AUX(PINMUX_AUX_SDMMC2_CMD) |= PINMUX_SCHMT;
|
||||||
|
PINMUX_AUX(PINMUX_AUX_SDMMC2_DAT7) |= PINMUX_SCHMT;
|
||||||
|
PINMUX_AUX(PINMUX_AUX_SDMMC2_DAT6) |= PINMUX_SCHMT;
|
||||||
|
PINMUX_AUX(PINMUX_AUX_SDMMC2_DAT5) |= PINMUX_SCHMT;
|
||||||
|
PINMUX_AUX(PINMUX_AUX_SDMMC2_DAT4) |= PINMUX_SCHMT;
|
||||||
|
PINMUX_AUX(PINMUX_AUX_SDMMC2_DAT3) |= PINMUX_SCHMT;
|
||||||
|
PINMUX_AUX(PINMUX_AUX_SDMMC2_DAT2) |= PINMUX_SCHMT;
|
||||||
|
PINMUX_AUX(PINMUX_AUX_SDMMC2_DAT1) |= PINMUX_SCHMT;
|
||||||
|
PINMUX_AUX(PINMUX_AUX_SDMMC2_DAT0) |= PINMUX_SCHMT;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _sdmmc_config_sdmmc1_pads(bool discharge)
|
||||||
|
{
|
||||||
|
u32 sdmmc1_pin_mask = GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_3 | GPIO_PIN_4 | GPIO_PIN_5;
|
||||||
|
|
||||||
|
// Set values for Reset state.
|
||||||
|
u32 function = GPIO_MODE_SPIO;
|
||||||
|
u32 level = GPIO_LOW;
|
||||||
|
u32 output = GPIO_OUTPUT_DISABLE;
|
||||||
|
|
||||||
|
// Set values for dicharging.
|
||||||
|
if (discharge)
|
||||||
|
{
|
||||||
|
function = GPIO_MODE_GPIO;
|
||||||
|
level = GPIO_HIGH;
|
||||||
|
output = GPIO_OUTPUT_ENABLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set all pads function.
|
||||||
|
gpio_config(GPIO_PORT_M, sdmmc1_pin_mask, function);
|
||||||
|
// Set all pads output level.
|
||||||
|
gpio_write(GPIO_PORT_M, sdmmc1_pin_mask, level);
|
||||||
|
// Set all pads output.
|
||||||
|
gpio_output_enable(GPIO_PORT_M, sdmmc1_pin_mask, output);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int _sdmmc_config_sdmmc1(bool t210b01)
|
||||||
{
|
{
|
||||||
// Configure SD card detect.
|
// Configure SD card detect.
|
||||||
PINMUX_AUX(PINMUX_AUX_GPIO_PZ1) = PINMUX_INPUT_ENABLE | PINMUX_PULL_UP | 2; // GPIO control, pull up.
|
PINMUX_AUX(PINMUX_AUX_GPIO_PZ1) = PINMUX_INPUT_ENABLE | PINMUX_PULL_UP | 2; // GPIO control, pull up.
|
||||||
|
@ -1104,28 +1169,35 @@ static int _sdmmc_config_sdmmc1()
|
||||||
// Enable deep loopback for SDMMC1 CLK pad.
|
// Enable deep loopback for SDMMC1 CLK pad.
|
||||||
APB_MISC(APB_MISC_GP_SDMMC1_CLK_LPBK_CONTROL) = 1;
|
APB_MISC(APB_MISC_GP_SDMMC1_CLK_LPBK_CONTROL) = 1;
|
||||||
|
|
||||||
// Configure SDMMC1 pinmux.
|
// Configure SDMMC1 CLK pinmux, based on state and SoC type.
|
||||||
PINMUX_AUX(PINMUX_AUX_SDMMC1_CLK) = PINMUX_DRIVE_2X | PINMUX_INPUT_ENABLE | PINMUX_PARKED;
|
if (PINMUX_AUX(PINMUX_AUX_SDMMC1_CLK) != (PINMUX_DRIVE_2X | PINMUX_INPUT_ENABLE | PINMUX_PULL_DOWN)) // Check if CLK pad is already configured.
|
||||||
PINMUX_AUX(PINMUX_AUX_SDMMC1_CMD) = PINMUX_DRIVE_2X | PINMUX_INPUT_ENABLE | PINMUX_PARKED | PINMUX_PULL_UP;
|
PINMUX_AUX(PINMUX_AUX_SDMMC1_CLK) = PINMUX_DRIVE_2X | PINMUX_INPUT_ENABLE | (t210b01 ? PINMUX_PULL_NONE : PINMUX_PULL_DOWN);
|
||||||
PINMUX_AUX(PINMUX_AUX_SDMMC1_DAT3) = PINMUX_DRIVE_2X | PINMUX_INPUT_ENABLE | PINMUX_PARKED | PINMUX_PULL_UP;
|
|
||||||
PINMUX_AUX(PINMUX_AUX_SDMMC1_DAT2) = PINMUX_DRIVE_2X | PINMUX_INPUT_ENABLE | PINMUX_PARKED | PINMUX_PULL_UP;
|
// Configure reset state of SDMMC1 pins pinmux.
|
||||||
PINMUX_AUX(PINMUX_AUX_SDMMC1_DAT1) = PINMUX_DRIVE_2X | PINMUX_INPUT_ENABLE | PINMUX_PARKED | PINMUX_PULL_UP;
|
PINMUX_AUX(PINMUX_AUX_SDMMC1_CMD) = PINMUX_DRIVE_2X | PINMUX_INPUT_ENABLE | PINMUX_PULL_UP;
|
||||||
PINMUX_AUX(PINMUX_AUX_SDMMC1_DAT0) = PINMUX_DRIVE_2X | PINMUX_INPUT_ENABLE | PINMUX_PARKED | PINMUX_PULL_UP;
|
PINMUX_AUX(PINMUX_AUX_SDMMC1_DAT3) = PINMUX_DRIVE_2X | PINMUX_INPUT_ENABLE | PINMUX_PULL_UP;
|
||||||
|
PINMUX_AUX(PINMUX_AUX_SDMMC1_DAT2) = PINMUX_DRIVE_2X | PINMUX_INPUT_ENABLE | PINMUX_PULL_UP;
|
||||||
|
PINMUX_AUX(PINMUX_AUX_SDMMC1_DAT1) = PINMUX_DRIVE_2X | PINMUX_INPUT_ENABLE | PINMUX_PULL_UP;
|
||||||
|
PINMUX_AUX(PINMUX_AUX_SDMMC1_DAT0) = PINMUX_DRIVE_2X | PINMUX_INPUT_ENABLE | PINMUX_PULL_UP;
|
||||||
|
|
||||||
|
// Force schmitt trigger for T210B01.
|
||||||
|
if (t210b01)
|
||||||
|
_sdmmc_config_sdmmc1_schmitt();
|
||||||
|
|
||||||
// Make sure the SDMMC1 controller is powered.
|
// Make sure the SDMMC1 controller is powered.
|
||||||
PMC(APBDEV_PMC_NO_IOPOWER) |= PMC_NO_IOPOWER_SDMMC1_IO_EN;
|
|
||||||
usleep(1000);
|
|
||||||
PMC(APBDEV_PMC_NO_IOPOWER) &= ~(PMC_NO_IOPOWER_SDMMC1_IO_EN);
|
PMC(APBDEV_PMC_NO_IOPOWER) &= ~(PMC_NO_IOPOWER_SDMMC1_IO_EN);
|
||||||
|
(void)PMC(APBDEV_PMC_NO_IOPOWER); // Commit write.
|
||||||
|
|
||||||
// Inform IO pads that voltage is gonna be 3.3V.
|
// Inform IO pads that voltage is gonna be 3.3V.
|
||||||
PMC(APBDEV_PMC_PWR_DET_VAL) |= PMC_PWR_DET_SDMMC1_IO_EN;
|
PMC(APBDEV_PMC_PWR_DET_VAL) |= PMC_PWR_DET_SDMMC1_IO_EN;
|
||||||
|
(void)PMC(APBDEV_PMC_PWR_DET_VAL); // Commit write.
|
||||||
|
|
||||||
// Set enable SD card power.
|
// Set enable SD card power.
|
||||||
PINMUX_AUX(PINMUX_AUX_DMIC3_CLK) = PINMUX_PULL_DOWN | 2; // Pull down.
|
PINMUX_AUX(PINMUX_AUX_DMIC3_CLK) = PINMUX_PULL_DOWN | 2; // Pull down.
|
||||||
gpio_config(GPIO_PORT_E, GPIO_PIN_4, GPIO_MODE_GPIO);
|
gpio_config(GPIO_PORT_E, GPIO_PIN_4, GPIO_MODE_GPIO);
|
||||||
gpio_write(GPIO_PORT_E, GPIO_PIN_4, GPIO_HIGH);
|
gpio_write(GPIO_PORT_E, GPIO_PIN_4, GPIO_HIGH);
|
||||||
gpio_output_enable(GPIO_PORT_E, GPIO_PIN_4, GPIO_OUTPUT_ENABLE);
|
gpio_output_enable(GPIO_PORT_E, GPIO_PIN_4, GPIO_OUTPUT_ENABLE);
|
||||||
usleep(1000);
|
usleep(10000);
|
||||||
|
|
||||||
// Enable SD card IO power.
|
// Enable SD card IO power.
|
||||||
max77620_regulator_set_voltage(REGULATOR_LDO2, 3300000);
|
max77620_regulator_set_voltage(REGULATOR_LDO2, 3300000);
|
||||||
|
@ -1133,26 +1205,40 @@ static int _sdmmc_config_sdmmc1()
|
||||||
usleep(1000);
|
usleep(1000);
|
||||||
|
|
||||||
// Set pad slew codes to get good quality clock.
|
// Set pad slew codes to get good quality clock.
|
||||||
APB_MISC(APB_MISC_GP_SDMMC1_PAD_CFGPADCTRL) = (APB_MISC(APB_MISC_GP_SDMMC1_PAD_CFGPADCTRL) & 0xFFFFFFF) | 0x50000000;
|
if (!t210b01)
|
||||||
usleep(1000);
|
{
|
||||||
|
APB_MISC(APB_MISC_GP_SDMMC1_PAD_CFGPADCTRL) = (APB_MISC(APB_MISC_GP_SDMMC1_PAD_CFGPADCTRL) & 0xFFFFFFF) | 0x50000000;
|
||||||
|
(void)APB_MISC(APB_MISC_GP_SDMMC1_PAD_CFGPADCTRL); // Commit write.
|
||||||
|
usleep(1000);
|
||||||
|
}
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void _sdmmc_config_emmc(u32 id)
|
static void _sdmmc_config_emmc(u32 id, bool t210b01)
|
||||||
{
|
{
|
||||||
switch (id)
|
switch (id)
|
||||||
{
|
{
|
||||||
case SDMMC_2:
|
case SDMMC_2:
|
||||||
// Unset park for pads.
|
if (!t210b01)
|
||||||
APB_MISC(APB_MISC_GP_EMMC2_PAD_CFGPADCTRL) &= 0xF8003FFF;
|
{
|
||||||
|
// Unset park for pads.
|
||||||
|
APB_MISC(APB_MISC_GP_EMMC2_PAD_CFGPADCTRL) &= 0xF8003FFF;
|
||||||
|
(void)APB_MISC(APB_MISC_GP_EMMC2_PAD_CFGPADCTRL); // Commit write.
|
||||||
|
}
|
||||||
|
else // Enable schmitt trigger for T210B01.
|
||||||
|
_sdmmc_config_sdmmc2_schmitt();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SDMMC_4:
|
case SDMMC_4:
|
||||||
// Unset park for pads.
|
// Unset park for pads.
|
||||||
APB_MISC(APB_MISC_GP_EMMC4_PAD_CFGPADCTRL) &= 0xF8003FFF;
|
APB_MISC(APB_MISC_GP_EMMC4_PAD_CFGPADCTRL) &= 0xF8003FFF;
|
||||||
|
// Set default pad cfg.
|
||||||
|
if (t210b01)
|
||||||
|
APB_MISC(APB_MISC_GP_EMMC4_PAD_PUPD_CFGPADCTRL) &= 0xFFBFFFF9; // Unset CMD/CLK/DQS powedown.
|
||||||
// Enable schmitt trigger.
|
// Enable schmitt trigger.
|
||||||
APB_MISC(APB_MISC_GP_EMMC4_PAD_CFGPADCTRL) |= 1; // Enable Schmitt trigger.
|
APB_MISC(APB_MISC_GP_EMMC4_PAD_CFGPADCTRL) |= 1;
|
||||||
|
(void)APB_MISC(APB_MISC_GP_EMMC4_PAD_CFGPADCTRL); // Commit write.
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1161,8 +1247,11 @@ int sdmmc_init(sdmmc_t *sdmmc, u32 id, u32 power, u32 bus_width, u32 type, int p
|
||||||
{
|
{
|
||||||
u32 clock;
|
u32 clock;
|
||||||
u16 divisor;
|
u16 divisor;
|
||||||
|
u8 vref_sel = 7;
|
||||||
|
|
||||||
const u32 trim_values[] = { 2, 8, 3, 8 };
|
const u32 trim_values_t210[] = { 2, 8, 3, 8 };
|
||||||
|
const u32 trim_values_t210b01[] = { 14, 13, 15, 13 };
|
||||||
|
const u32 *trim_values = sdmmc->t210b01 ? trim_values_t210b01 : trim_values_t210;
|
||||||
|
|
||||||
if (id > SDMMC_4 || id == SDMMC_3)
|
if (id > SDMMC_4 || id == SDMMC_3)
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -1172,18 +1261,23 @@ int sdmmc_init(sdmmc_t *sdmmc, u32 id, u32 power, u32 bus_width, u32 type, int p
|
||||||
sdmmc->regs = (t210_sdmmc_t *)_sdmmc_bases[id];
|
sdmmc->regs = (t210_sdmmc_t *)_sdmmc_bases[id];
|
||||||
sdmmc->id = id;
|
sdmmc->id = id;
|
||||||
sdmmc->clock_stopped = 1;
|
sdmmc->clock_stopped = 1;
|
||||||
|
sdmmc->t210b01 = hw_get_chip_id() == GP_HIDREV_MAJOR_T210B01;
|
||||||
|
|
||||||
// Do specific SDMMC HW configuration.
|
// Do specific SDMMC HW configuration.
|
||||||
switch (id)
|
switch (id)
|
||||||
{
|
{
|
||||||
case SDMMC_1:
|
case SDMMC_1:
|
||||||
if (!_sdmmc_config_sdmmc1())
|
if (!_sdmmc_config_sdmmc1(sdmmc->t210b01))
|
||||||
return 0;
|
return 0;
|
||||||
|
if (sdmmc->t210b01)
|
||||||
|
vref_sel = 0;
|
||||||
|
else
|
||||||
|
sdmmc->manual_cal = 1;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SDMMC_2:
|
case SDMMC_2:
|
||||||
case SDMMC_4:
|
case SDMMC_4:
|
||||||
_sdmmc_config_emmc(id);
|
_sdmmc_config_emmc(id, sdmmc->t210b01);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1198,6 +1292,9 @@ int sdmmc_init(sdmmc_t *sdmmc, u32 id, u32 power, u32 bus_width, u32 type, int p
|
||||||
clock_sdmmc_get_card_clock_div(&clock, &divisor, type);
|
clock_sdmmc_get_card_clock_div(&clock, &divisor, type);
|
||||||
clock_sdmmc_enable(id, clock);
|
clock_sdmmc_enable(id, clock);
|
||||||
|
|
||||||
|
// Make sure all sdmmc registers are reset.
|
||||||
|
_sdmmc_reset_all(sdmmc);
|
||||||
|
|
||||||
sdmmc->clock_stopped = 0;
|
sdmmc->clock_stopped = 0;
|
||||||
|
|
||||||
// Set default pad IO trimming configuration.
|
// Set default pad IO trimming configuration.
|
||||||
|
@ -1205,7 +1302,7 @@ int sdmmc_init(sdmmc_t *sdmmc, u32 id, u32 power, u32 bus_width, u32 type, int p
|
||||||
sdmmc->regs->veniotrimctl &= 0xFFFFFFFB; // Set Band Gap VREG to supply DLL.
|
sdmmc->regs->veniotrimctl &= 0xFFFFFFFB; // Set Band Gap VREG to supply DLL.
|
||||||
sdmmc->regs->venclkctl = (sdmmc->regs->venclkctl & 0xE0FFFFFB) | (trim_values[sdmmc->id] << 24);
|
sdmmc->regs->venclkctl = (sdmmc->regs->venclkctl & 0xE0FFFFFB) | (trim_values[sdmmc->id] << 24);
|
||||||
sdmmc->regs->sdmemcmppadctl =
|
sdmmc->regs->sdmemcmppadctl =
|
||||||
(sdmmc->regs->sdmemcmppadctl & TEGRA_MMC_SDMEMCOMPPADCTRL_COMP_VREF_SEL_MASK) | 7;
|
(sdmmc->regs->sdmemcmppadctl & TEGRA_MMC_SDMEMCOMPPADCTRL_COMP_VREF_SEL_MASK) | vref_sel;
|
||||||
|
|
||||||
// Configure auto calibration values.
|
// Configure auto calibration values.
|
||||||
if (!_sdmmc_autocal_config_offset(sdmmc, power))
|
if (!_sdmmc_autocal_config_offset(sdmmc, power))
|
||||||
|
@ -1233,6 +1330,52 @@ int sdmmc_init(sdmmc_t *sdmmc, u32 id, u32 power, u32 bus_width, u32 type, int p
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void sdmmc1_disable_power()
|
||||||
|
{
|
||||||
|
// Ensure regulator is into default voltage.
|
||||||
|
if (PMC(APBDEV_PMC_PWR_DET_VAL) & PMC_PWR_DET_SDMMC1_IO_EN)
|
||||||
|
{
|
||||||
|
// Switch to 1.8V and wait for regulator to stabilize.
|
||||||
|
max77620_regulator_set_voltage(REGULATOR_LDO2, 1800000);
|
||||||
|
usleep(150);
|
||||||
|
|
||||||
|
// Inform IO pads that we switched to 1.8V.
|
||||||
|
PMC(APBDEV_PMC_PWR_DET_VAL) &= ~(PMC_PWR_DET_SDMMC1_IO_EN);
|
||||||
|
(void)PMC(APBDEV_PMC_PWR_DET_VAL); // Commit write.
|
||||||
|
}
|
||||||
|
|
||||||
|
// T210B01 WAR: Clear pull down from CLK pad.
|
||||||
|
PINMUX_AUX(PINMUX_AUX_SDMMC1_CLK) &= ~PINMUX_PULL_MASK;
|
||||||
|
|
||||||
|
// T210B01 WAR: Set pads to discharge state.
|
||||||
|
_sdmmc_config_sdmmc1_pads(true);
|
||||||
|
|
||||||
|
// Disable SD card IO power regulator.
|
||||||
|
max77620_regulator_enable(REGULATOR_LDO2, 0);
|
||||||
|
usleep(4000);
|
||||||
|
|
||||||
|
// Disable SD card IO power pin.
|
||||||
|
gpio_write(GPIO_PORT_E, GPIO_PIN_4, GPIO_LOW);
|
||||||
|
|
||||||
|
// T210/T210B01 WAR: Set start timer for IO and Controller power discharge.
|
||||||
|
sd_power_cycle_time_start = get_tmr_ms();
|
||||||
|
usleep(1000); // To power cycle, min 1ms without power is needed.
|
||||||
|
|
||||||
|
// Disable SDMMC1 controller power.
|
||||||
|
PMC(APBDEV_PMC_NO_IOPOWER) |= PMC_NO_IOPOWER_SDMMC1_IO_EN;
|
||||||
|
(void)PMC(APBDEV_PMC_NO_IOPOWER); // Commit write.
|
||||||
|
|
||||||
|
// Inform IO pads that next voltage might be 3.3V.
|
||||||
|
PMC(APBDEV_PMC_PWR_DET_VAL) |= PMC_PWR_DET_SDMMC1_IO_EN;
|
||||||
|
(void)PMC(APBDEV_PMC_PWR_DET_VAL); // Commit write.
|
||||||
|
|
||||||
|
// T210B01 WAR: Restore pads to reset state.
|
||||||
|
_sdmmc_config_sdmmc1_pads(false);
|
||||||
|
|
||||||
|
// T210B01 WAR: Restore pull down to CLK pad.
|
||||||
|
PINMUX_AUX(PINMUX_AUX_SDMMC1_CLK) |= PINMUX_PULL_DOWN;
|
||||||
|
}
|
||||||
|
|
||||||
void sdmmc_end(sdmmc_t *sdmmc)
|
void sdmmc_end(sdmmc_t *sdmmc)
|
||||||
{
|
{
|
||||||
if (!sdmmc->clock_stopped)
|
if (!sdmmc->clock_stopped)
|
||||||
|
@ -1243,16 +1386,7 @@ void sdmmc_end(sdmmc_t *sdmmc)
|
||||||
|
|
||||||
// Disable SD card power.
|
// Disable SD card power.
|
||||||
if (sdmmc->id == SDMMC_1)
|
if (sdmmc->id == SDMMC_1)
|
||||||
{
|
sdmmc1_disable_power();
|
||||||
gpio_output_enable(GPIO_PORT_E, GPIO_PIN_4, GPIO_OUTPUT_DISABLE);
|
|
||||||
max77620_regulator_enable(REGULATOR_LDO2, 0);
|
|
||||||
|
|
||||||
// Inform IO pads that next voltage might be 3.3V.
|
|
||||||
PMC(APBDEV_PMC_PWR_DET_VAL) |= PMC_PWR_DET_SDMMC1_IO_EN;
|
|
||||||
|
|
||||||
sd_power_cycle_time_start = get_tmr_ms(); // Some SanDisk U1 cards need 100ms for a power cycle.
|
|
||||||
usleep(1000); // To power cycle, min 1ms without power is needed.
|
|
||||||
}
|
|
||||||
|
|
||||||
_sdmmc_commit_changes(sdmmc);
|
_sdmmc_commit_changes(sdmmc);
|
||||||
clock_sdmmc_disable(sdmmc->id);
|
clock_sdmmc_disable(sdmmc->id);
|
||||||
|
@ -1274,7 +1408,7 @@ int sdmmc_execute_cmd(sdmmc_t *sdmmc, sdmmc_cmd_t *cmd, sdmmc_req_t *req, u32 *b
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
// Recalibrate periodically for SDMMC1.
|
// Recalibrate periodically for SDMMC1.
|
||||||
if (sdmmc->id == SDMMC_1 && sdmmc->powersave_enabled)
|
if (sdmmc->manual_cal && sdmmc->powersave_enabled)
|
||||||
_sdmmc_autocal_execute(sdmmc, sdmmc_get_io_power(sdmmc));
|
_sdmmc_autocal_execute(sdmmc, sdmmc_get_io_power(sdmmc));
|
||||||
|
|
||||||
int should_disable_sd_clock = 0;
|
int should_disable_sd_clock = 0;
|
||||||
|
@ -1307,10 +1441,11 @@ int sdmmc_enable_low_voltage(sdmmc_t *sdmmc)
|
||||||
|
|
||||||
// Switch to 1.8V and wait for regulator to stabilize. Assume max possible wait needed.
|
// Switch to 1.8V and wait for regulator to stabilize. Assume max possible wait needed.
|
||||||
max77620_regulator_set_voltage(REGULATOR_LDO2, 1800000);
|
max77620_regulator_set_voltage(REGULATOR_LDO2, 1800000);
|
||||||
usleep(300);
|
usleep(150);
|
||||||
|
|
||||||
// Inform IO pads that we switched to 1.8V.
|
// Inform IO pads that we switched to 1.8V.
|
||||||
PMC(APBDEV_PMC_PWR_DET_VAL) &= ~(PMC_PWR_DET_SDMMC1_IO_EN);
|
PMC(APBDEV_PMC_PWR_DET_VAL) &= ~(PMC_PWR_DET_SDMMC1_IO_EN);
|
||||||
|
(void)PMC(APBDEV_PMC_PWR_DET_VAL); // Commit write.
|
||||||
|
|
||||||
// Enable schmitt trigger for better duty cycle and low jitter clock.
|
// Enable schmitt trigger for better duty cycle and low jitter clock.
|
||||||
_sdmmc_config_sdmmc1_schmitt();
|
_sdmmc_config_sdmmc1_schmitt();
|
||||||
|
|
|
@ -214,6 +214,7 @@ typedef struct _sdmmc_t
|
||||||
u32 divisor;
|
u32 divisor;
|
||||||
u32 clock_stopped;
|
u32 clock_stopped;
|
||||||
int powersave_enabled;
|
int powersave_enabled;
|
||||||
|
int manual_cal;
|
||||||
int card_clock_enabled;
|
int card_clock_enabled;
|
||||||
int venclkctl_set;
|
int venclkctl_set;
|
||||||
u32 venclkctl_tap;
|
u32 venclkctl_tap;
|
||||||
|
@ -221,6 +222,7 @@ typedef struct _sdmmc_t
|
||||||
u32 dma_addr_next;
|
u32 dma_addr_next;
|
||||||
u32 rsp[4];
|
u32 rsp[4];
|
||||||
u32 rsp3;
|
u32 rsp3;
|
||||||
|
int t210b01;
|
||||||
} sdmmc_t;
|
} sdmmc_t;
|
||||||
|
|
||||||
/*! SDMMC command. */
|
/*! SDMMC command. */
|
||||||
|
|
Loading…
Reference in a new issue