diff --git a/bdk/soc/clock.c b/bdk/soc/clock.c index 1b73d6d..22c3486 100644 --- a/bdk/soc/clock.c +++ b/bdk/soc/clock.c @@ -764,6 +764,14 @@ static int _clock_sdmmc_config_clock_host(u32 *pclock, u32 id, u32 val) *pclock = 199680; divisor = 0; // 1 div. break; + +#ifdef BDK_SDMMC_UHS_DDR200_SUPPORT + case 400000: + source = SDMMC_CLOCK_SRC_PLLC4_OUT0; + *pclock = 399360; + divisor = 3; // 2.5 div + break; +#endif } _clock_sdmmc_table[id].clock = val; @@ -878,6 +886,13 @@ void clock_sdmmc_get_card_clock_div(u32 *pclock, u16 *pdivisor, u32 type) *pclock = 200000; *pdivisor = 2; break; + +#ifdef BDK_SDMMC_UHS_DDR200_SUPPORT + case SDHCI_TIMING_UHS_DDR200: // Actual card clock: 199.68 KHz. + *pclock = 400000; + *pdivisor = 2; + break; +#endif } } diff --git a/bdk/storage/sd.c b/bdk/storage/sd.c index dd7bd22..f2a22ed 100644 --- a/bdk/storage/sd.c +++ b/bdk/storage/sd.c @@ -22,6 +22,12 @@ #include #include +#ifndef BDK_SDMMC_UHS_DDR200_SUPPORT +#define SD_DEFAULT_SPEED SD_UHS_SDR104 +#else +#define SD_DEFAULT_SPEED SD_UHS_DDR208 +#endif + static bool sd_mounted = false; static bool sd_init_done = false; static bool insertion_event = false; @@ -80,7 +86,11 @@ u32 sd_get_mode() int sd_init_retry(bool power_cycle) { u32 bus_width = SDMMC_BUS_WIDTH_4; +#ifndef BDK_SDMMC_UHS_DDR200_SUPPORT u32 type = SDHCI_TIMING_UHS_SDR104; +#else + u32 type = SDHCI_TIMING_UHS_DDR200; +#endif // Power cycle SD card. if (power_cycle) @@ -111,6 +121,13 @@ int sd_init_retry(bool power_cycle) case SD_UHS_SDR104: type = SDHCI_TIMING_UHS_SDR104; break; + +#ifdef BDK_SDMMC_UHS_DDR200_SUPPORT + case SD_UHS_DDR208: + type = SDHCI_TIMING_UHS_DDR200; + break; +#endif + default: sd_mode = SD_DEFAULT_SPEED; break; diff --git a/bdk/storage/sd.h b/bdk/storage/sd.h index 863fb3c..7c6170b 100644 --- a/bdk/storage/sd.h +++ b/bdk/storage/sd.h @@ -1,6 +1,6 @@ /* * Copyright (c) 2018 naehrwert - * Copyright (c) 2018-2021 CTCaer + * Copyright (c) 2018-2023 CTCaer * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -30,7 +30,11 @@ enum SD_1BIT_HS25 = 1, SD_4BIT_HS25 = 2, SD_UHS_SDR82 = 3, - SD_UHS_SDR104 = 4 + SD_UHS_SDR104 = 4, +#ifdef BDK_SDMMC_UHS_DDR200_SUPPORT + SD_UHS_DDR208 = 5 +#endif + }; enum diff --git a/bdk/storage/sd_def.h b/bdk/storage/sd_def.h index 5895b15..1316b50 100644 --- a/bdk/storage/sd_def.h +++ b/bdk/storage/sd_def.h @@ -45,15 +45,14 @@ #define SD_APP_CHANGE_SECURE_AREA 49 /* adtc R1b */ /* OCR bit definitions */ -#define SD_OCR_VDD_18 (1 << 7) /* VDD voltage 1.8 */ -#define SD_VHD_27_36 (1 << 8) /* VDD voltage 2.7 ~ 3.6 */ -#define SD_OCR_VDD_27_34 (0x7F << 15) /* VDD voltage 2.7 ~ 3.4 */ -#define SD_OCR_VDD_32_33 (1 << 20) /* VDD voltage 3.2 ~ 3.3 */ -#define SD_OCR_S18R (1 << 24) /* 1.8V switching request */ -#define SD_ROCR_S18A SD_OCR_S18R /* 1.8V switching accepted by card */ -#define SD_OCR_XPC (1 << 28) /* SDXC power control */ -#define SD_OCR_CCS (1 << 30) /* Card Capacity Status */ -#define SD_OCR_BUSY (1 << 31) /* Card Power up Status */ +#define SD_OCR_VDD_18 (1U << 7) /* VDD voltage 1.8 */ +#define SD_VHD_27_36 (1U << 8) /* VDD voltage 2.7 ~ 3.6 */ +#define SD_OCR_VDD_32_33 (1U << 20) /* VDD voltage 3.2 ~ 3.3 */ +#define SD_OCR_S18R (1U << 24) /* 1.8V switching request */ +#define SD_ROCR_S18A SD_OCR_S18R /* 1.8V switching accepted by card */ +#define SD_OCR_XPC (1U << 28) /* SDXC power control */ +#define SD_OCR_CCS (1U << 30) /* Card Capacity Status */ +#define SD_OCR_BUSY (1U << 31) /* Card Power up Status */ /* * SD_SWITCH argument format: @@ -90,8 +89,8 @@ #define SCR_SPEC_VER_0 0 /* Implements system specification 1.0 - 1.01 */ #define SCR_SPEC_VER_1 1 /* Implements system specification 1.10 */ #define SCR_SPEC_VER_2 2 /* Implements system specification 2.00-3.0X */ -#define SD_SCR_BUS_WIDTH_1 (1<<0) -#define SD_SCR_BUS_WIDTH_4 (1<<2) +#define SD_SCR_BUS_WIDTH_1 (1U << 0) +#define SD_SCR_BUS_WIDTH_4 (1U << 2) /* * SD bus widths @@ -102,33 +101,55 @@ /* * SD bus speeds */ -#define UHS_SDR12_BUS_SPEED 0 +#define UHS_SDR12_BUS_SPEED 0 #define HIGH_SPEED_BUS_SPEED 1 -#define UHS_SDR25_BUS_SPEED 1 -#define UHS_SDR50_BUS_SPEED 2 +#define UHS_SDR25_BUS_SPEED 1 +#define UHS_SDR50_BUS_SPEED 2 #define UHS_SDR104_BUS_SPEED 3 -#define UHS_DDR50_BUS_SPEED 4 -#define HS400_BUS_SPEED 5 +#define UHS_DDR50_BUS_SPEED 4 +#define HS400_BUS_SPEED 5 -#define SD_MODE_HIGH_SPEED (1 << HIGH_SPEED_BUS_SPEED) -#define SD_MODE_UHS_SDR12 (1 << UHS_SDR12_BUS_SPEED) -#define SD_MODE_UHS_SDR25 (1 << UHS_SDR25_BUS_SPEED) -#define SD_MODE_UHS_SDR50 (1 << UHS_SDR50_BUS_SPEED) -#define SD_MODE_UHS_SDR104 (1 << UHS_SDR104_BUS_SPEED) -#define SD_MODE_UHS_DDR50 (1 << UHS_DDR50_BUS_SPEED) +#define SD_MODE_HIGH_SPEED (1U << HIGH_SPEED_BUS_SPEED) +#define SD_MODE_UHS_SDR12 (1U << UHS_SDR12_BUS_SPEED) +#define SD_MODE_UHS_SDR25 (1U << UHS_SDR25_BUS_SPEED) +#define SD_MODE_UHS_SDR50 (1U << UHS_SDR50_BUS_SPEED) +#define SD_MODE_UHS_SDR104 (1U << UHS_SDR104_BUS_SPEED) +#define SD_MODE_UHS_DDR50 (1U << UHS_DDR50_BUS_SPEED) -#define SD_DRIVER_TYPE_B 0x01 -#define SD_DRIVER_TYPE_A 0x02 + +#define SD_SET_DRIVER_TYPE_B 0 +#define SD_SET_DRIVER_TYPE_A 1 +#define SD_SET_DRIVER_TYPE_C 2 +#define SD_SET_DRIVER_TYPE_D 3 + +#define SD_DRIVER_TYPE_B (1U << SD_SET_DRIVER_TYPE_B) +#define SD_DRIVER_TYPE_A (1U << SD_SET_DRIVER_TYPE_A) +#define SD_DRIVER_TYPE_C (1U << SD_SET_DRIVER_TYPE_C) +#define SD_DRIVER_TYPE_D (1U << SD_SET_DRIVER_TYPE_D) #define SD_SET_POWER_LIMIT_0_72 0 #define SD_SET_POWER_LIMIT_1_44 1 #define SD_SET_POWER_LIMIT_2_16 2 #define SD_SET_POWER_LIMIT_2_88 3 -#define SD_MAX_POWER_0_72 (1 << SD_SET_POWER_LIMIT_0_72) -#define SD_MAX_POWER_1_44 (1 << SD_SET_POWER_LIMIT_1_44) -#define SD_MAX_POWER_2_16 (1 << SD_SET_POWER_LIMIT_2_16) -#define SD_MAX_POWER_2_88 (1 << SD_SET_POWER_LIMIT_2_88) +#define SD_MAX_POWER_0_72 (1U << SD_SET_POWER_LIMIT_0_72) +#define SD_MAX_POWER_1_44 (1U << SD_SET_POWER_LIMIT_1_44) +#define SD_MAX_POWER_2_16 (1U << SD_SET_POWER_LIMIT_2_16) +#define SD_MAX_POWER_2_88 (1U << SD_SET_POWER_LIMIT_2_88) + +#define SD_SET_CMD_SYSTEM_DEF 0 +#define SD_SET_CMD_SYSTEM_MEC 1 +#define SD_SET_CMD_SYSTEM_OTP 3 +#define SD_SET_CMD_SYSTEM_OSD 3 +#define SD_SET_CMD_SYSTEM_VND 14 +#define UHS_DDR200_BUS_SPEED SD_SET_CMD_SYSTEM_VND + +#define SD_CMD_SYSTEM_DEF (1U << SD_SET_CMD_SYSTEM_DEF) +#define SD_CMD_SYSTEM_MEC (1U << SD_SET_CMD_SYSTEM_MEC) +#define SD_CMD_SYSTEM_OTP (1U << SD_SET_CMD_SYSTEM_OTP) +#define SD_CMD_SYSTEM_OSD (1U << SD_SET_CMD_SYSTEM_OSD) +#define SD_CMD_SYSTEM_VND (1U << SD_SET_CMD_SYSTEM_VND) +#define SD_MODE_UHS_DDR200 SD_CMD_SYSTEM_VND /* * SD_SWITCH mode @@ -140,14 +161,14 @@ * SD_SWITCH function groups */ #define SD_SWITCH_GRP_ACCESS 0 -#define SD_SWITCH_GRP_CMDSYS 1 -#define SD_SWITCH_GRP_DRVSTR 2 -#define SD_SWITCH_GRP_PWRLIM 3 +#define SD_SWITCH_GRP_CMDSYS 1 +#define SD_SWITCH_GRP_DRVSTR 2 +#define SD_SWITCH_GRP_PWRLIM 3 /* * SD_SWITCH access modes */ #define SD_SWITCH_ACCESS_DEF 0 -#define SD_SWITCH_ACCESS_HS 1 +#define SD_SWITCH_ACCESS_HS 1 #endif /* SD_DEF_H */ diff --git a/bdk/storage/sdmmc.c b/bdk/storage/sdmmc.c index f87fcfb..1fd7dea 100644 --- a/bdk/storage/sdmmc.c +++ b/bdk/storage/sdmmc.c @@ -909,8 +909,6 @@ static void _sd_storage_parse_scr(sdmmc_storage_t *storage) memcpy(&resp[2], storage->raw_scr, 8); - _debug_scr(storage->raw_scr); - storage->scr.sda_vsn = unstuff_bits(resp, 56, 4); storage->scr.bus_widths = unstuff_bits(resp, 48, 4); @@ -1032,6 +1030,74 @@ static void _sd_storage_set_power_limit(sdmmc_storage_t *storage, u16 power_limi } } +/* + * SD Card DDR200 (DDR208) support + * + * Proper procedure: + * 1. Check that Vendor Specific Command System is supported. + * Used as Enable DDR200 Bus. + * 2. Enable DDR200 bus mode via setting 14 to Group 2 via CMD6. + * Access Mode group is left to default 0 (SDR12). + * 3. Setup clock to 200 or 208 MHz. + * 4. Set host to DDR bus mode that supports such high clocks. + * Some hosts have special mode, others use DDR50 and others HS400. + * 5. Execute Tuning. + * + * The true validation that this value in Group 2 activates it, is that DDR50 bus + * and clocks/timings work fully after that point. + * + * On Tegra X1, that can be done with DDR50 host mode. + * Tuning though can't be done automatically on any DDR mode. + * So it needs to be done manually and selected tap will be applied from the biggest + * sampling window. + * + * Finally, all that simply works, because the marketing materials for DDR200 are + * basically overstatements to sell the feature. DDR200 is simply SDR104 in DDR mode, + * so sampling on rising and falling edge and with variable output data window. + * It can be supported by any host that is fast enough to support DDR at 200/208MHz + * and can do hw/sw tuning for finding the proper sampling window in that mode. + */ +#ifdef BDK_SDMMC_UHS_DDR200_SUPPORT +static int _sd_storage_enable_DDR200(sdmmc_storage_t *storage, u8 *buf) +{ + u32 cmd_system = UHS_DDR200_BUS_SPEED; + if (!_sd_storage_switch(storage, buf, SD_SWITCH_CHECK, SD_SWITCH_GRP_CMDSYS, cmd_system)) + return 0; + + u32 system_out = (buf[16] >> 4) & 0xF; + if (system_out != cmd_system) + return 0; + DPRINTF("[SD] supports DDR200 mode\n"); + + u16 total_pwr_consumption = ((u16)buf[0] << 8) | buf[1]; + DPRINTF("[SD] max power: %d mW\n", total_pwr_consumption * 3600 / 1000); + storage->card_power_limit = total_pwr_consumption; + + if (total_pwr_consumption <= 800) + { + if (!_sd_storage_switch(storage, buf, SD_SWITCH_SET, SD_SWITCH_GRP_CMDSYS, cmd_system)) + return 0; + + if (system_out != ((buf[16] >> 4) & 0xF)) + return 0; + DPRINTF("[SD] card accepted DDR200\n"); + + if (!sdmmc_setup_clock(storage->sdmmc, SDHCI_TIMING_UHS_DDR200)) + return 0; + DPRINTF("[SD] after setup clock DDR200\n"); + + if (!sdmmc_tuning_execute(storage->sdmmc, SDHCI_TIMING_UHS_DDR200, MMC_SEND_TUNING_BLOCK)) + return 0; + DPRINTF("[SD] after tuning DDR200\n"); + + return _sdmmc_storage_check_status(storage); + } + + DPRINTF("[SD] card max power over limit\n"); + return 0; +} +#endif + static int _sd_storage_set_card_bus_speed(sdmmc_storage_t *storage, u32 hs_type, u8 *buf) { if (!_sd_storage_switch(storage, buf, SD_SWITCH_CHECK, SD_SWITCH_GRP_ACCESS, hs_type)) @@ -1070,12 +1136,27 @@ static int _sd_storage_enable_uhs_low_volt(sdmmc_storage_t *storage, u32 type, u return 0; u8 access_mode = buf[13]; - u16 power_limit = buf[7] | buf[6] << 8; + u16 power_limit = buf[7] | buf[6] << 8; +#ifdef BDK_SDMMC_UHS_DDR200_SUPPORT + u16 cmd_system = buf[11] | buf[10] << 8; +#endif DPRINTF("[SD] access: %02X, power: %02X\n", access_mode, power_limit); u32 hs_type = 0; switch (type) { +#ifdef BDK_SDMMC_UHS_DDR200_SUPPORT + case SDHCI_TIMING_UHS_DDR200: + // Fall through if DDR200 is not supported. + if (cmd_system & SD_MODE_UHS_DDR200) + { + DPRINTF("[SD] setting bus speed to DDR200\n"); + storage->csd.busspeed = 200; + _sd_storage_set_power_limit(storage, power_limit, buf); + return _sd_storage_enable_DDR200(storage, buf); + } +#endif + case SDHCI_TIMING_UHS_SDR104: case SDHCI_TIMING_UHS_SDR82: // Fall through if not supported. @@ -1348,6 +1429,7 @@ static bool _sdmmc_storage_get_bus_uhs_support(u32 bus_width, u32 type) case SDHCI_TIMING_UHS_SDR104: case SDHCI_TIMING_UHS_SDR82: case SDHCI_TIMING_UHS_DDR50: + case SDHCI_TIMING_UHS_DDR200: if (bus_width == SDMMC_BUS_WIDTH_4) return true; default: diff --git a/bdk/storage/sdmmc_driver.c b/bdk/storage/sdmmc_driver.c index 97d163c..b705091 100644 --- a/bdk/storage/sdmmc_driver.c +++ b/bdk/storage/sdmmc_driver.c @@ -352,6 +352,9 @@ int sdmmc_setup_clock(sdmmc_t *sdmmc, u32 type) break; case SDHCI_TIMING_UHS_DDR50: +#ifdef BDK_SDMMC_UHS_DDR200_SUPPORT + case SDHCI_TIMING_UHS_DDR200: +#endif sdmmc->regs->hostctl2 = (sdmmc->regs->hostctl2 & (~SDHCI_CTRL_UHS_MASK)) | UHS_DDR50_BUS_SPEED; sdmmc->regs->hostctl2 |= SDHCI_CTRL_VDD_180; break; @@ -617,10 +620,8 @@ static void _sdmmc_send_tuning_cmd(sdmmc_t *sdmmc, u32 cmd) _sdmmc_send_cmd(sdmmc, &cmdbuf, true); } -static int _sdmmc_tuning_execute_once(sdmmc_t *sdmmc, u32 cmd) +static int _sdmmc_tuning_execute_once(sdmmc_t *sdmmc, u32 cmd, u32 tap) { - if (sdmmc->powersave_enabled) - return 0; if (!_sdmmc_wait_cmd_data_inhibit(sdmmc, true)) return 0; @@ -630,6 +631,16 @@ static int _sdmmc_tuning_execute_once(sdmmc_t *sdmmc, u32 cmd) sdmmc->regs->norintsts = sdmmc->regs->norintsts; sdmmc->regs->clkcon &= ~SDHCI_CLOCK_CARD_EN; +#ifdef BDK_SDMMC_UHS_DDR200_SUPPORT + // Set tap if manual tuning. + if (tap != HW_TAP_TUNING) + { + sdmmc->regs->ventunctl0 &= ~SDHCI_TEGRA_TUNING_TAP_HW_UPDATED; + sdmmc->regs->venclkctl = (sdmmc->regs->venclkctl & 0xFF00FFFF) | (tap << 16); + sdmmc->regs->ventunctl0 |= SDHCI_TEGRA_TUNING_TAP_HW_UPDATED; + } +#endif + _sdmmc_send_tuning_cmd(sdmmc, cmd); _sdmmc_commit_changes(sdmmc); usleep(1); @@ -661,10 +672,112 @@ static int _sdmmc_tuning_execute_once(sdmmc_t *sdmmc, u32 cmd) return 0; } +#ifdef BDK_SDMMC_UHS_DDR200_SUPPORT +typedef struct _sdmmc_manual_tuning_t +{ + u32 result[8]; + u32 num_iter; + u32 tap_start; + u32 tap_end; +} sdmmc_manual_tuning_t; + +static int _sdmmc_manual_tuning_set_tap(sdmmc_t *sdmmc, sdmmc_manual_tuning_t *tuning) +{ + u32 tap_start = INVALID_TAP; + u32 win_size = 0; + u32 best_tap = 0; + u32 best_size = 0; + + for (u32 i = 0; i < tuning->num_iter; i++) + { + u32 iter_end = i == (tuning->num_iter - 1) ? 1 : 0; + u32 stable = tuning->result[i / 32] & BIT(i % 32); + if (stable && !iter_end) + { + if (tap_start == INVALID_TAP) + tap_start = i; + + win_size++; + } + else + { + if (tap_start != INVALID_TAP) + { + u32 tap_end = !iter_end ? (i - 1) : i; + + // Check if window is wider. + if (win_size > best_size) + { + best_tap = (tap_start + tap_end) / 2; + best_size = win_size + iter_end; + + } + + tap_start = INVALID_TAP; + win_size = 0; + } + } + } + + // Check if failed. + if (!best_tap) + return 0; + + sdmmc->regs->clkcon &= ~SDHCI_CLOCK_CARD_EN; + sdmmc->regs->ventunctl0 &= ~SDHCI_TEGRA_TUNING_TAP_HW_UPDATED; + + // Set tap. + sdmmc->regs->venclkctl = (sdmmc->regs->venclkctl & 0xFF00FFFF) | (best_tap << 16); + + sdmmc->regs->ventunctl0 |= SDHCI_TEGRA_TUNING_TAP_HW_UPDATED; + sdmmc->regs->clkcon |= SDHCI_CLOCK_CARD_EN; + + return 1; +} + +/* + * SD Card DDR200 (DDR208) support + * + * On Tegra X1, that can be done with DDR50 host mode. + * Tuning though can't be done automatically on any DDR mode. + * So it needs to be done manually and selected tap will be applied from the biggest + * sampling window. + */ +static int sdmmc_tuning_execute_ddr200(sdmmc_t *sdmmc) +{ + sdmmc_manual_tuning_t manual_tuning = { 0 }; + manual_tuning.num_iter = 128; + + sdmmc->regs->ventunctl1 = 0; // step_size 1. + sdmmc->regs->ventunctl0 = (sdmmc->regs->ventunctl0 & 0xFFFF1FFF) | (2 << 13); // 128 Tries. + sdmmc->regs->ventunctl0 = (sdmmc->regs->ventunctl0 & 0xFFFFE03F) | (1 << 6); // 1x Multiplier. + sdmmc->regs->ventunctl0 |= SDHCI_TEGRA_TUNING_TAP_HW_UPDATED; + + sdmmc->regs->hostctl2 |= SDHCI_CTRL_EXEC_TUNING; + + for (u32 i = 0; i < manual_tuning.num_iter; i++) + { + _sdmmc_tuning_execute_once(sdmmc, MMC_SEND_TUNING_BLOCK, i); + + // Save result for manual tuning. + int sampled = (sdmmc->regs->hostctl2 >> SDHCI_CTRL_TUNED_CLK_SHIFT) & 1; + manual_tuning.result[i / 32] |= sampled << (i % 32); + + if (!(sdmmc->regs->hostctl2 & SDHCI_CTRL_EXEC_TUNING)) + break; + } + + return _sdmmc_manual_tuning_set_tap(sdmmc, &manual_tuning); +} +#endif + int sdmmc_tuning_execute(sdmmc_t *sdmmc, u32 type, u32 cmd) { u32 num_iter, flag; + if (sdmmc->powersave_enabled) + return 0; + switch (type) { case SDHCI_TIMING_MMC_HS200: @@ -686,6 +799,11 @@ int sdmmc_tuning_execute(sdmmc_t *sdmmc, u32 type, u32 cmd) case SDHCI_TIMING_UHS_SDR25: return 1; +#ifdef BDK_SDMMC_UHS_DDR200_SUPPORT + case SDHCI_TIMING_UHS_DDR200: + return sdmmc_tuning_execute_ddr200(sdmmc); +#endif + default: return 0; } @@ -699,7 +817,7 @@ int sdmmc_tuning_execute(sdmmc_t *sdmmc, u32 type, u32 cmd) for (u32 i = 0; i < num_iter; i++) { - _sdmmc_tuning_execute_once(sdmmc, cmd); + _sdmmc_tuning_execute_once(sdmmc, cmd, HW_TAP_TUNING); if (!(sdmmc->regs->hostctl2 & SDHCI_CTRL_EXEC_TUNING)) break; diff --git a/bdk/storage/sdmmc_driver.h b/bdk/storage/sdmmc_driver.h index f5c096d..a3c7bf5 100644 --- a/bdk/storage/sdmmc_driver.h +++ b/bdk/storage/sdmmc_driver.h @@ -262,6 +262,7 @@ // SDR104 with a 163.2MHz -> 81.6MHz clock. #define SDHCI_TIMING_UHS_SDR82 13 // GC FPGA. Obsolete and Repurposed. MMC_HS50 -> SDR82. #define SDHCI_TIMING_MMC_HS100 14 // GC ASIC. +#define SDHCI_TIMING_UHS_DDR200 15 /*! SDMMC Low power features. */ #define SDMMC_POWER_SAVE_DISABLE 0 @@ -270,6 +271,9 @@ /*! Helper for SWITCH command argument. */ #define SDMMC_SWITCH(mode, index, value) (((mode) << 24) | ((index) << 16) | ((value) << 8)) +#define HW_TAP_TUNING 0x100 +#define INVALID_TAP 0x100 + /*! SDMMC controller context. */ typedef struct _sdmmc_t {