git subrepo pull emummc

subrepo:
  subdir:   "emummc"
  merged:   "25075973"
upstream:
  origin:   "https://github.com/m4xw/emuMMC"
  branch:   "develop"
  commit:   "25075973"
git-subrepo:
  version:  "0.4.1"
  origin:   "???"
  commit:   "???"
This commit is contained in:
Michael Scire 2020-11-24 23:20:20 -08:00
parent 8ba513fefb
commit ee9585dd57
17 changed files with 697 additions and 310 deletions

View file

@ -6,7 +6,7 @@
[subrepo]
remote = https://github.com/m4xw/emuMMC
branch = develop
commit = 791681f50b92fe11cb139ee4b504c062bfc6c925
parent = c77b76fbd2285081328844a65ae01bd52024b739
commit = 25075973d31a5be6f2e769f1ea0fff44daf0cdfa
parent = 8ba513fefbcfd8278a433090e59017963ba9887f
method = rebase
cmdver = 0.4.1

View file

@ -136,8 +136,8 @@ c : clear by read
#define R1_WP_ERASE_SKIP (1 << 15) /* sx, c */
#define R1_CARD_ECC_DISABLED (1 << 14) /* sx, a */
#define R1_ERASE_RESET (1 << 13) /* sr, c */
#define R1_STATUS(x) (x & 0xFFFFE000)
#define R1_CURRENT_STATE(x) ((x & 0x00001E00) >> 9) /* sx, b (4 bits) */
#define R1_STATUS(x) ((x) & 0xFFFFE000)
#define R1_CURRENT_STATE(x) (((x) & 0x00001E00) >> 9) /* sx, b (4 bits) */
#define R1_READY_FOR_DATA (1 << 8) /* sx, a */
#define R1_SWITCH_ERROR (1 << 7) /* sx, c */
#define R1_EXCEPTION_EVENT (1 << 6) /* sr, a */

View file

@ -1,6 +1,4 @@
/*
* include/linux/mmc/sd.h
*
* Copyright (c) 2005-2007 Pierre Ossman, All Rights Reserved.
* Copyright (c) 2018 CTCaer
*
@ -10,8 +8,8 @@
* your option) any later version.
*/
#ifndef LINUX_MMC_SD_H
#define LINUX_MMC_SD_H
#ifndef MMC_SD_H
#define MMC_SD_H
/* SD commands type argument response */
/* class 0 */

View file

@ -1,6 +1,6 @@
/*
* Copyright (c) 2018 naehrwert
* Copyright (c) 2018-2019 CTCaer
* Copyright (c) 2018-2020 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,
@ -164,15 +164,18 @@ int sdmmc_calculate_fitting_dma_index(sdmmc_accessor_t *_this, unsigned int num_
static int _sdmmc_storage_check_result(u32 res)
{
//Error mask:
//R1_OUT_OF_RANGE, R1_ADDRESS_ERROR, R1_BLOCK_LEN_ERROR,
//R1_ERASE_SEQ_ERROR, R1_ERASE_PARAM, R1_WP_VIOLATION,
//R1_LOCK_UNLOCK_FAILED, R1_COM_CRC_ERROR, R1_ILLEGAL_COMMAND,
//R1_CARD_ECC_FAILED, R1_CC_ERROR, R1_ERROR, R1_CID_CSD_OVERWRITE,
//R1_WP_ERASE_SKIP, R1_ERASE_RESET, R1_SWITCH_ERROR
if (!(res & 0xFDF9A080))
return 1;
//TODO: R1_SWITCH_ERROR we can skip for certain card types.
return 0;
//TODO: R1_SWITCH_ERROR can be skipped for certain card types.
if (res &
(R1_OUT_OF_RANGE | R1_ADDRESS_ERROR | R1_BLOCK_LEN_ERROR |
R1_ERASE_SEQ_ERROR | R1_ERASE_PARAM | R1_WP_VIOLATION |
R1_LOCK_UNLOCK_FAILED | R1_COM_CRC_ERROR | R1_ILLEGAL_COMMAND |
R1_CARD_ECC_FAILED | R1_CC_ERROR | R1_ERROR |
R1_CID_CSD_OVERWRITE | R1_WP_ERASE_SKIP | R1_ERASE_RESET |
R1_SWITCH_ERROR))
return 0;
// No errors.
return 1;
}
static int _sdmmc_storage_execute_cmd_type1_ex(sdmmc_storage_t *storage, u32 *resp, u32 cmd, u32 arg, u32 check_busy, u32 expected_state, u32 mask)
@ -285,14 +288,23 @@ int sdmmc_storage_end(sdmmc_storage_t *storage)
sdmmc_end(storage->sdmmc);
storage->initialized = 0;
return 1;
}
static int _sdmmc_storage_readwrite(sdmmc_storage_t *storage, u32 sector, u32 num_sectors, void *buf, u32 is_write)
{
u8 *bbuf = (u8 *)buf;
bool first_reinit = false;
while (num_sectors)
u32 sct_off = sector;
u32 sct_total = num_sectors;
bool first_reinit = true;
// Exit if not initialized.
if (!storage->initialized)
return 0;
while (sct_total)
{
u32 blkcnt = 0;
// Retry 5 times if failed.
@ -300,7 +312,7 @@ static int _sdmmc_storage_readwrite(sdmmc_storage_t *storage, u32 sector, u32 nu
do
{
reinit_try:
if (_sdmmc_storage_readwrite_ex(storage, &blkcnt, sector, MIN(num_sectors, 0xFFFF), bbuf, is_write))
if (_sdmmc_storage_readwrite_ex(storage, &blkcnt, sct_off, MIN(sct_total, 0xFFFF), bbuf, is_write))
goto out;
else
retries--;
@ -313,24 +325,33 @@ reinit_try:
{
int res;
if (!first_reinit)
if (first_reinit)
res = nx_sd_initialize(true);
else
res = nx_sd_init_retry(true);
// Reset values for a retry.
blkcnt = 0;
retries = 3;
first_reinit = true;
first_reinit = false;
// If succesful reinit, restart xfer.
if (res)
{
bbuf = (u8 *)buf;
sct_off = sector;
sct_total = num_sectors;
goto reinit_try;
}
}
// Failed.
return 0;
out:
DPRINTF("readwrite: %08X\n", blkcnt);
sector += blkcnt;
num_sectors -= blkcnt;
sct_off += blkcnt;
sct_total -= blkcnt;
bbuf += 512 * blkcnt;
}
@ -459,9 +480,11 @@ static int _mmc_storage_get_op_cond_inner(sdmmc_storage_t *storage, u32 *pout, u
case SDMMC_POWER_1_8:
arg = SD_OCR_CCS | SD_OCR_VDD_18;
break;
case SDMMC_POWER_3_3:
arg = SD_OCR_CCS | SD_OCR_VDD_27_34;
break;
default:
return 0;
}
@ -518,6 +541,7 @@ static void _mmc_storage_parse_cid(sdmmc_storage_t *storage)
storage->cid.fwrev = unstuff_bits(raw_cid, 40, 4);
storage->cid.serial = unstuff_bits(raw_cid, 16, 24);
break;
case 2: /* MMC v2.0 - v2.2 */
case 3: /* MMC v3.1 - v3.3 */
case 4: /* MMC v4 */
@ -527,6 +551,7 @@ static void _mmc_storage_parse_cid(sdmmc_storage_t *storage)
storage->cid.prv = unstuff_bits(raw_cid, 48, 8);
storage->cid.serial = unstuff_bits(raw_cid, 16, 32);
break;
default:
break;
}
@ -571,6 +596,10 @@ static void _mmc_storage_parse_ext_csd(sdmmc_storage_t *storage, u8 *buf)
storage->ext_csd.bkops_en = buf[EXT_CSD_BKOPS_EN];
storage->ext_csd.bkops_status = buf[EXT_CSD_BKOPS_STATUS];
storage->ext_csd.pre_eol_info = buf[EXT_CSD_PRE_EOL_INFO];
storage->ext_csd.dev_life_est_a = buf[EXT_CSD_DEVICE_LIFE_TIME_EST_TYP_A];
storage->ext_csd.dev_life_est_b = buf[EXT_CSD_DEVICE_LIFE_TIME_EST_TYP_B];
storage->sec_cnt = *(u32 *)&buf[EXT_CSD_SEC_CNT];
}
@ -613,6 +642,7 @@ static int _mmc_storage_switch_buswidth(sdmmc_storage_t *storage, u32 bus_width)
case SDMMC_BUS_WIDTH_4:
arg = SDMMC_SWITCH(MMC_SWITCH_MODE_WRITE_BYTE, EXT_CSD_BUS_WIDTH, EXT_CSD_BUS_WIDTH_4);
break;
case SDMMC_BUS_WIDTH_8:
arg = SDMMC_SWITCH(MMC_SWITCH_MODE_WRITE_BYTE, EXT_CSD_BUS_WIDTH, EXT_CSD_BUS_WIDTH_8);
break;
@ -671,7 +701,7 @@ static int _mmc_storage_enable_HS400(sdmmc_storage_t *storage)
if (!_mmc_storage_enable_HS200(storage))
return 0;
sdmmc_set_tap_value(storage->sdmmc);
sdmmc_save_tap_value(storage->sdmmc);
if (!_mmc_storage_enable_HS(storage, 0))
return 0;
@ -727,7 +757,7 @@ int sdmmc_storage_init_mmc(sdmmc_storage_t *storage, sdmmc_t *sdmmc, u32 bus_wid
storage->sdmmc = sdmmc;
storage->rca = 2; //TODO: this could be a config item.
if (!sdmmc_init(sdmmc, SDMMC_4, SDMMC_POWER_1_8, SDMMC_BUS_WIDTH_1, SDHCI_TIMING_MMC_ID, SDMMC_AUTO_CAL_DISABLE))
if (!sdmmc_init(sdmmc, SDMMC_4, SDMMC_POWER_1_8, SDMMC_BUS_WIDTH_1, SDHCI_TIMING_MMC_ID, SDMMC_POWER_SAVE_DISABLE))
return 0;
DPRINTF("[MMC] after init\n");
@ -798,7 +828,9 @@ DPRINTF("[MMC] BKOPS enabled\n");
return 0;
DPRINTF("[MMC] succesfully switched to HS mode\n");
sdmmc_card_clock_ctrl(storage->sdmmc, SDMMC_AUTO_CAL_ENABLE);
sdmmc_card_clock_powersave(storage->sdmmc, SDMMC_POWER_SAVE_ENABLE);
storage->initialized = 1;
return 1;
}
@ -812,6 +844,7 @@ int sdmmc_storage_set_mmc_partition(sdmmc_storage_t *storage, u32 partition)
return 0;
storage->partition = partition;
return 1;
}
@ -850,7 +883,7 @@ static int _sd_storage_send_if_cond(sdmmc_storage_t *storage)
return (resp & 0xFF) == 0xAA ? 0 : 2;
}
static int _sd_storage_get_op_cond_once(sdmmc_storage_t *storage, u32 *cond, int is_version_1, int supports_low_voltage)
static int _sd_storage_get_op_cond_once(sdmmc_storage_t *storage, u32 *cond, int is_version_1, int bus_low_voltage_support)
{
sdmmc_cmd_t cmdbuf;
// Support for Current > 150mA
@ -858,7 +891,7 @@ static int _sd_storage_get_op_cond_once(sdmmc_storage_t *storage, u32 *cond, int
// Support for handling block-addressed SDHC cards
arg |= (~is_version_1 & 1) ? SD_OCR_CCS : 0;
// Support for 1.8V
arg |= (supports_low_voltage & ~is_version_1 & 1) ? SD_OCR_S18R : 0;
arg |= (bus_low_voltage_support & ~is_version_1 & 1) ? SD_OCR_S18R : 0;
// This is needed for most cards. Do not set bit7 even if 1.8V is supported.
arg |= SD_OCR_VDD_32_33;
sdmmc_init_cmd(&cmdbuf, SD_APP_OP_COND, arg, SDMMC_RSP_TYPE_3, 0);
@ -868,22 +901,24 @@ static int _sd_storage_get_op_cond_once(sdmmc_storage_t *storage, u32 *cond, int
return sdmmc_get_rsp(storage->sdmmc, cond, 4, SDMMC_RSP_TYPE_3);
}
static int _sd_storage_get_op_cond(sdmmc_storage_t *storage, int is_version_1, int supports_low_voltage)
static int _sd_storage_get_op_cond(sdmmc_storage_t *storage, int is_version_1, int bus_low_voltage_support)
{
u64 timeout = get_tmr_ms() + 1500;
while (1)
{
u32 cond = 0;
if (!_sd_storage_get_op_cond_once(storage, &cond, is_version_1, supports_low_voltage))
if (!_sd_storage_get_op_cond_once(storage, &cond, is_version_1, bus_low_voltage_support))
break;
if (cond & MMC_CARD_BUSY)
{
DPRINTF("[SD] cond: %08X, lv: %d\n", cond, bus_low_voltage_support);
if (cond & SD_OCR_CCS)
storage->has_sector_access = 1;
// Check if card supports 1.8V signaling.
if (cond & SD_ROCR_S18A && supports_low_voltage)
if (cond & SD_ROCR_S18A && bus_low_voltage_support)
{
//The low voltage regulator configuration is valid for SDMMC1 only.
if (storage->sdmmc->id == SDMMC_1 &&
@ -896,6 +931,10 @@ static int _sd_storage_get_op_cond(sdmmc_storage_t *storage, int is_version_1, i
DPRINTF("-> switched to low voltage\n");
}
}
else
{
DPRINTF("[SD] no low voltage support\n");
}
return 1;
}
@ -1050,12 +1089,15 @@ void _sd_storage_set_current_limit(sdmmc_storage_t *storage, u16 current_limit,
case SD_SET_CURRENT_LIMIT_800:
DPRINTF("[SD] power limit raised to 800mA\n");
break;
case SD_SET_CURRENT_LIMIT_600:
DPRINTF("[SD] power limit raised to 600mA\n");
break;
case SD_SET_CURRENT_LIMIT_400:
DPRINTF("[SD] power limit raised to 400mA\n");
break;
default:
case SD_SET_CURRENT_LIMIT_200:
DPRINTF("[SD] power limit defaulted to 200mA\n");
@ -1068,7 +1110,7 @@ int _sd_storage_enable_highspeed(sdmmc_storage_t *storage, u32 hs_type, u8 *buf)
{
if (!_sd_storage_switch(storage, buf, SD_SWITCH_CHECK, 0, hs_type))
return 0;
DPRINTF("[SD] supports switch to (U)HS mode\n");
DPRINTF("[SD] supports (U)HS mode: %d\n", buf[16] & 0xF);
u32 type_out = buf[16] & 0xF;
if (type_out != hs_type)
@ -1104,6 +1146,7 @@ int _sd_storage_enable_uhs_low_volt(sdmmc_storage_t *storage, u32 type, u8 *buf)
u8 access_mode = buf[13];
u16 current_limit = buf[7] | buf[6] << 8;
DPRINTF("[SD] access: %02X, current: %02X\n", access_mode, current_limit);
// Try to raise the current limit to let the card perform better.
_sd_storage_set_current_limit(storage, current_limit, buf);
@ -1142,7 +1185,7 @@ DPRINTF("[SD] bus speed set to SDR50\n");
if (access_mode & SD_MODE_UHS_SDR25)
{
type = SDHCI_TIMING_UHS_SDR25;
hs_type = UHS_SDR50_BUS_SPEED;
hs_type = UHS_SDR25_BUS_SPEED;
DPRINTF("[SD] bus speed set to SDR25\n");
storage->csd.busspeed = 25;
break;
@ -1155,6 +1198,7 @@ DPRINTF("[SD] bus speed set to SDR25\n");
DPRINTF("[SD] bus speed set to SDR12\n");
storage->csd.busspeed = 12;
break;
default:
return 0;
break;
@ -1165,10 +1209,10 @@ DPRINTF("[SD] bus speed set to SDR12\n");
DPRINTF("[SD] card accepted UHS\n");
if (!sdmmc_setup_clock(storage->sdmmc, type))
return 0;
DPRINTF("[SD] setup clock\n");
DPRINTF("[SD] after setup clock\n");
if (!sdmmc_tuning_execute(storage->sdmmc, type, MMC_SEND_TUNING_BLOCK))
return 0;
DPRINTF("[SD] config tuning\n");
DPRINTF("[SD] after tuning\n");
return _sdmmc_storage_check_status(storage);
}
@ -1227,6 +1271,7 @@ static void _sd_storage_parse_csd(sdmmc_storage_t *storage)
case 0:
storage->csd.capacity = (1 + unstuff_bits(raw_csd, 62, 12)) << (unstuff_bits(raw_csd, 47, 3) + 2);
break;
case 1:
storage->csd.c_size = (1 + unstuff_bits(raw_csd, 48, 22));
storage->csd.capacity = storage->csd.c_size << 10;
@ -1235,7 +1280,7 @@ static void _sd_storage_parse_csd(sdmmc_storage_t *storage)
}
}
static bool _sdmmc_storage_supports_low_voltage(u32 bus_width, u32 type)
static bool _sdmmc_storage_get_low_voltage_support(u32 bus_width, u32 type)
{
switch (type)
{
@ -1261,7 +1306,7 @@ int sdmmc_storage_init_sd(sdmmc_storage_t *storage, sdmmc_t *sdmmc, u32 bus_widt
memset(storage, 0, sizeof(sdmmc_storage_t));
storage->sdmmc = sdmmc;
if (!sdmmc_init(sdmmc, SDMMC_1, SDMMC_POWER_3_3, SDMMC_BUS_WIDTH_1, SDHCI_TIMING_SD_ID, SDMMC_AUTO_CAL_DISABLE))
if (!sdmmc_init(sdmmc, SDMMC_1, SDMMC_POWER_3_3, SDMMC_BUS_WIDTH_1, SDHCI_TIMING_SD_ID, SDMMC_POWER_SAVE_DISABLE))
return 0;
DPRINTF("[SD] after init\n");
@ -1276,9 +1321,9 @@ DPRINTF("[SD] went to idle state\n");
return 0;
DPRINTF("[SD] after send if cond\n");
bool supports_low_voltage = _sdmmc_storage_supports_low_voltage(bus_width, type);
bool bus_low_voltage_support = _sdmmc_storage_get_low_voltage_support(bus_width, type);
if (!_sd_storage_get_op_cond(storage, is_version_1, supports_low_voltage))
if (!_sd_storage_get_op_cond(storage, is_version_1, bus_low_voltage_support))
return 0;
DPRINTF("[SD] got op cond\n");
@ -1356,7 +1401,7 @@ DPRINTF("[SD] SD does not support wide bus width\n");
return 0;
DPRINTF("[SD] enabled UHS\n");
sdmmc_card_clock_ctrl(sdmmc, SDMMC_AUTO_CAL_ENABLE);
sdmmc_card_clock_powersave(sdmmc, SDMMC_POWER_SAVE_ENABLE);
}
else if (type != SDHCI_TIMING_SD_DS12 && (storage->scr.sda_vsn & 0xF) != 0)
{
@ -1369,12 +1414,15 @@ DPRINTF("[SD] enabled HS\n");
case SDMMC_BUS_WIDTH_4:
storage->csd.busspeed = 25;
break;
case SDMMC_BUS_WIDTH_1:
storage->csd.busspeed = 6;
break;
}
}
storage->initialized = 1;
return 1;
}
@ -1414,17 +1462,19 @@ int sdmmc_storage_init_gc(sdmmc_storage_t *storage, sdmmc_t *sdmmc)
memset(storage, 0, sizeof(sdmmc_storage_t));
storage->sdmmc = sdmmc;
if (!sdmmc_init(sdmmc, SDMMC_2, SDMMC_POWER_1_8, SDMMC_BUS_WIDTH_8, SDHCI_TIMING_MMC_DDR52, SDMMC_AUTO_CAL_DISABLE))
if (!sdmmc_init(sdmmc, SDMMC_2, SDMMC_POWER_1_8, SDMMC_BUS_WIDTH_8, SDHCI_TIMING_MMC_HS102, SDMMC_POWER_SAVE_DISABLE))
return 0;
DPRINTF("[gc] after init\n");
usleep(1000 + (10000 + sdmmc->divisor - 1) / sdmmc->divisor);
if (!sdmmc_tuning_execute(storage->sdmmc, SDHCI_TIMING_MMC_DDR52, MMC_SEND_TUNING_BLOCK_HS200))
if (!sdmmc_tuning_execute(storage->sdmmc, SDHCI_TIMING_MMC_HS102, MMC_SEND_TUNING_BLOCK_HS200))
return 0;
DPRINTF("[gc] after tuning\n");
sdmmc_card_clock_ctrl(sdmmc, SDMMC_AUTO_CAL_ENABLE);
sdmmc_card_clock_powersave(sdmmc, SDMMC_POWER_SAVE_ENABLE);
storage->initialized = 1;
return 1;
}

View file

@ -1,6 +1,6 @@
/*
* Copyright (c) 2018 naehrwert
* Copyright (c) 2018 CTCaer
* Copyright (c) 2018-2020 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,
@ -54,16 +54,19 @@ typedef struct _mmc_csd
typedef struct _mmc_ext_csd
{
u8 rev;
u32 sectors;
int bkops; /* background support bit */
int bkops_en; /* manual bkops enable bit */
u8 rev;
u8 ext_struct; /* 194 */
u8 card_type; /* 196 */
u8 bkops_status; /* 246 */
u16 dev_version;
u8 pre_eol_info;
u8 dev_life_est_a;
u8 dev_life_est_b;
u8 boot_mult;
u8 rpmb_mult;
u16 dev_version;
} mmc_ext_csd_t;
typedef struct _sd_scr
@ -81,6 +84,7 @@ typedef struct _sd_ssr
u8 uhs_grade;
u8 video_class;
u8 app_class;
u32 protected_size;
} sd_ssr_t;
/*! SDMMC storage context. */
@ -99,6 +103,7 @@ typedef struct _sdmmc_storage_t
mmc_csd_t csd;
mmc_ext_csd_t ext_csd;
sd_scr_t scr;
int initialized;
} sdmmc_storage_t;
extern sdmmc_accessor_t *_current_accessor;

View file

@ -1,6 +1,6 @@
/*
* Copyright (c) 2018 naehrwert
* Copyright (c) 2018-2019 CTCaer
* Copyright (c) 2018-2020 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,
@ -58,12 +58,15 @@ static int _sdmmc_set_io_power(sdmmc_t *sdmmc, u32 power)
case SDMMC_POWER_OFF:
sdmmc->regs->pwrcon &= ~SDHCI_POWER_ON;
break;
case SDMMC_POWER_1_8:
sdmmc->regs->pwrcon = SDHCI_POWER_180;
break;
case SDMMC_POWER_3_3:
sdmmc->regs->pwrcon = SDHCI_POWER_330;
break;
default:
return 0;
}
@ -96,7 +99,7 @@ void sdmmc_set_bus_width(sdmmc_t *sdmmc, u32 bus_width)
sdmmc->regs->hostctl = host_control | SDHCI_CTRL_8BITBUS;
}
void sdmmc_set_tap_value(sdmmc_t *sdmmc)
void sdmmc_save_tap_value(sdmmc_t *sdmmc)
{
sdmmc->venclkctl_tap = sdmmc->regs->venclkctl >> 16;
sdmmc->venclkctl_set = 1;
@ -105,7 +108,7 @@ void sdmmc_set_tap_value(sdmmc_t *sdmmc)
static int _sdmmc_config_tap_val(sdmmc_t *sdmmc, u32 type)
{
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;
@ -122,36 +125,49 @@ static int _sdmmc_config_tap_val(sdmmc_t *sdmmc, u32 type)
tap_val = sdmmc->venclkctl_tap;
}
else
{
tap_val = tap_values[sdmmc->id];
}
tap_val = sdmmc->t210b01 ? 11 : tap_values_t210[sdmmc->id];
sdmmc->regs->venclkctl = (sdmmc->regs->venclkctl & 0xFF00FFFF) | (tap_val << 16);
return 1;
}
static int _sdmmc_get_clkcon(sdmmc_t *sdmmc)
static int _sdmmc_commit_changes(sdmmc_t *sdmmc)
{
return sdmmc->regs->clkcon;
}
static void _sdmmc_pad_config_fallback(sdmmc_t *sdmmc, u32 power)
{
_sdmmc_get_clkcon(sdmmc);
_sdmmc_commit_changes(sdmmc);
switch (sdmmc->id)
{
case SDMMC_1: // 33 Ohm 2X Driver.
if (power == SDMMC_POWER_OFF)
break;
u32 sdmmc1_pad_cfg = APB_MISC(APB_MISC_GP_SDMMC1_PAD_CFGPADCTRL) & 0xF8080FFF;
if (power == SDMMC_POWER_1_8)
APB_MISC(APB_MISC_GP_SDMMC1_PAD_CFGPADCTRL) = sdmmc1_pad_cfg | (0xB0F << 12); // Up: 11, Dn: 15. For 33 ohm.
if (sdmmc->t210b01)
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)
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;
case SDMMC_2:
case SDMMC_4: // 50 Ohm 2X Driver. PU:16, PD:16.
APB_MISC(APB_MISC_GP_EMMC4_PAD_CFGPADCTRL) = (APB_MISC(APB_MISC_GP_EMMC4_PAD_CFGPADCTRL) & 0xFFFFC003) | 0x1040;
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, B01: PU:10, PD:10.
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;
}
}
@ -169,13 +185,13 @@ static void _sdmmc_autocal_execute(sdmmc_t *sdmmc, u32 power)
if (!(sdmmc->regs->sdmemcmppadctl & TEGRA_MMC_SDMEMCOMPPADCTRL_PAD_E_INPUT_PWRD))
{
sdmmc->regs->sdmemcmppadctl |= TEGRA_MMC_SDMEMCOMPPADCTRL_PAD_E_INPUT_PWRD;
_sdmmc_get_clkcon(sdmmc);
_sdmmc_commit_changes(sdmmc);
usleep(1);
}
// Enable auto calibration and start auto configuration.
sdmmc->regs->autocalcfg |= TEGRA_MMC_AUTOCALCFG_AUTO_CAL_ENABLE | TEGRA_MMC_AUTOCALCFG_AUTO_CAL_START;
_sdmmc_get_clkcon(sdmmc);
_sdmmc_commit_changes(sdmmc);
usleep(2);
u64 timeout = get_tmr_ms() + 10;
@ -187,24 +203,18 @@ static void _sdmmc_autocal_execute(sdmmc_t *sdmmc, u32 power)
break;
}
}
/*
// Check if PU results are inside limits.
// SDMMC1: CZ pads - 7-bit PU. SDMMC2/4: LV_CZ pads - 5-bit PU.
u8 autocal_pu_status = sdmmc->regs->autocalsts & 0x7F;
switch (sdmmc->id)
{
case SDMMC_1:
if (!autocal_pu_status || autocal_pu_status == 0x7F)
timeout = 0;
break;
case SDMMC_2:
case SDMMC_4:
autocal_pu_status &= 0x1F;
if (!autocal_pu_status || autocal_pu_status == 0x1F)
timeout = 0;
break;
}
*/
#if 0
// Check if Comp pad is open or short to ground.
// SDMMC1: CZ pads - T210/T210B01: 7-bit/5-bit. SDMMC2/4: LV_CZ pads - 5-bit.
u8 code_mask = (sdmmc->t210b01 || sdmmc->id != SDMMC_1) ? 0x1F : 0x7F;
u8 autocal_pu_status = sdmmc->regs->autocalsts & code_mask;
if (!autocal_pu_status)
EPRINTF("SDMMC: Comp Pad short to gnd!");
else if (autocal_pu_status == code_mask)
EPRINTF("SDMMC: Comp Pad open!");
#endif
// In case auto calibration fails, we load suggested standard values.
if (!timeout)
{
@ -230,7 +240,7 @@ static int _sdmmc_dll_cal_execute(sdmmc_t *sdmmc)
}
sdmmc->regs->vendllcalcfg |= TEGRA_MMC_DLLCAL_CFG_EN_CALIBRATE;
_sdmmc_get_clkcon(sdmmc);
_sdmmc_commit_changes(sdmmc);
u64 timeout = get_tmr_ms() + 5;
while (sdmmc->regs->vendllcalcfg & TEGRA_MMC_DLLCAL_CFG_EN_CALIBRATE)
@ -261,12 +271,21 @@ out:;
static void _sdmmc_reset(sdmmc_t *sdmmc)
{
sdmmc->regs->swrst |= SDHCI_RESET_CMD | SDHCI_RESET_DATA;
_sdmmc_get_clkcon(sdmmc);
_sdmmc_commit_changes(sdmmc);
u64 timeout = get_tmr_ms() + 2000;
while ((sdmmc->regs->swrst & (SDHCI_RESET_CMD | SDHCI_RESET_DATA)) && get_tmr_ms() < timeout)
;
}
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)
{
// Disable the SD clock if it was enabled, and reenable it later.
@ -290,36 +309,41 @@ int sdmmc_setup_clock(sdmmc_t *sdmmc, u32 type)
sdmmc->regs->hostctl &= ~SDHCI_CTRL_HISPD;
sdmmc->regs->hostctl2 &= ~SDHCI_CTRL_VDD_180;
break;
case SDHCI_TIMING_MMC_HS52:
case SDHCI_TIMING_SD_HS25:
sdmmc->regs->hostctl |= SDHCI_CTRL_HISPD;
sdmmc->regs->hostctl2 &= ~SDHCI_CTRL_VDD_180;
break;
case SDHCI_TIMING_MMC_HS200:
case SDHCI_TIMING_UHS_SDR50: // T210 Errata for SDR50, the host must be set to SDR104.
case SDHCI_TIMING_UHS_SDR104:
case SDHCI_TIMING_UHS_SDR82:
case SDHCI_TIMING_UHS_DDR50:
case SDHCI_TIMING_MMC_DDR52:
case SDHCI_TIMING_MMC_HS102:
sdmmc->regs->hostctl2 = (sdmmc->regs->hostctl2 & SDHCI_CTRL_UHS_MASK) | UHS_SDR104_BUS_SPEED;
sdmmc->regs->hostctl2 |= SDHCI_CTRL_VDD_180;
break;
case SDHCI_TIMING_MMC_HS400:
// Non standard.
sdmmc->regs->hostctl2 = (sdmmc->regs->hostctl2 & SDHCI_CTRL_UHS_MASK) | HS400_BUS_SPEED;
sdmmc->regs->hostctl2 |= SDHCI_CTRL_VDD_180;
break;
case SDHCI_TIMING_UHS_SDR25:
sdmmc->regs->hostctl2 = (sdmmc->regs->hostctl2 & SDHCI_CTRL_UHS_MASK) | UHS_SDR25_BUS_SPEED;
sdmmc->regs->hostctl2 |= SDHCI_CTRL_VDD_180;
break;
case SDHCI_TIMING_UHS_SDR12:
sdmmc->regs->hostctl2 = (sdmmc->regs->hostctl2 & SDHCI_CTRL_UHS_MASK) | UHS_SDR12_BUS_SPEED;
sdmmc->regs->hostctl2 |= SDHCI_CTRL_VDD_180;
break;
}
_sdmmc_get_clkcon(sdmmc);
_sdmmc_commit_changes(sdmmc);
u32 clock;
u16 divisor;
@ -349,10 +373,10 @@ int sdmmc_setup_clock(sdmmc_t *sdmmc, u32 type)
static void _sdmmc_card_clock_enable(sdmmc_t *sdmmc)
{
// Recalibrate conditionally.
if ((sdmmc->id == SDMMC_1) && !sdmmc->auto_cal_enabled)
if (sdmmc->manual_cal && !sdmmc->powersave_enabled)
_sdmmc_autocal_execute(sdmmc, sdmmc_get_io_power(sdmmc));
if (!sdmmc->auto_cal_enabled)
if (!sdmmc->powersave_enabled)
{
if (!(sdmmc->regs->clkcon & SDHCI_CLOCK_CARD_EN))
sdmmc->regs->clkcon |= SDHCI_CLOCK_CARD_EN;
@ -366,18 +390,17 @@ static void _sdmmc_sd_clock_disable(sdmmc_t *sdmmc)
sdmmc->regs->clkcon &= ~SDHCI_CLOCK_CARD_EN;
}
void sdmmc_card_clock_ctrl(sdmmc_t *sdmmc, int auto_cal_enable)
void sdmmc_card_clock_powersave(sdmmc_t *sdmmc, int powersave_enable)
{
// Recalibrate periodically for SDMMC1.
if ((sdmmc->id == SDMMC_1) && !auto_cal_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->auto_cal_enabled = auto_cal_enable;
if (auto_cal_enable)
sdmmc->powersave_enabled = powersave_enable;
if (powersave_enable)
{
if (!(sdmmc->regs->clkcon & SDHCI_CLOCK_CARD_EN))
return;
sdmmc->regs->clkcon &= ~SDHCI_CLOCK_CARD_EN;
if (sdmmc->regs->clkcon & SDHCI_CLOCK_CARD_EN)
sdmmc->regs->clkcon &= ~SDHCI_CLOCK_CARD_EN;
return;
}
@ -398,6 +421,7 @@ static int _sdmmc_cache_rsp(sdmmc_t *sdmmc, u32 *rsp, u32 size, u32 type)
return 0;
rsp[0] = sdmmc->regs->rspreg0;
break;
case SDMMC_RSP_TYPE_2:
if (size < 0x10)
return 0;
@ -426,9 +450,9 @@ static int _sdmmc_cache_rsp(sdmmc_t *sdmmc, u32 *rsp, u32 size, u32 type)
rsp[i - 1] |= (tempreg >> 24) & 0xFF;
}
break;
default:
return 0;
break;
}
return 1;
@ -449,6 +473,7 @@ int sdmmc_get_rsp(sdmmc_t *sdmmc, u32 *rsp, u32 size, u32 type)
return 0;
rsp[0] = sdmmc->rsp[0];
break;
case SDMMC_RSP_TYPE_2:
if (size < 0x10)
return 0;
@ -457,9 +482,9 @@ int sdmmc_get_rsp(sdmmc_t *sdmmc, u32 *rsp, u32 size, u32 type)
rsp[2] = sdmmc->rsp[2];
rsp[3] = sdmmc->rsp[3];
break;
default:
return 0;
break;
}
return 1;
@ -467,7 +492,7 @@ int sdmmc_get_rsp(sdmmc_t *sdmmc, u32 *rsp, u32 size, u32 type)
static int _sdmmc_wait_cmd_data_inhibit(sdmmc_t *sdmmc, bool wait_dat)
{
_sdmmc_get_clkcon(sdmmc);
_sdmmc_commit_changes(sdmmc);
u64 timeout = get_tmr_ms() + 2000;
while(sdmmc->regs->prnsts & SDHCI_CMD_INHIBIT)
@ -493,7 +518,7 @@ static int _sdmmc_wait_cmd_data_inhibit(sdmmc_t *sdmmc, bool wait_dat)
static int _sdmmc_wait_card_busy(sdmmc_t *sdmmc)
{
_sdmmc_get_clkcon(sdmmc);
_sdmmc_commit_changes(sdmmc);
u64 timeout = get_tmr_ms() + 2000;
while (!(sdmmc->regs->prnsts & SDHCI_DATA_0_LVL_MASK))
@ -512,16 +537,19 @@ static int _sdmmc_setup_read_small_block(sdmmc_t *sdmmc)
{
case SDMMC_BUS_WIDTH_1:
return 0;
break;
case SDMMC_BUS_WIDTH_4:
sdmmc->regs->blksize = 64;
break;
case SDMMC_BUS_WIDTH_8:
sdmmc->regs->blksize = 128;
break;
}
sdmmc->regs->blkcnt = 1;
sdmmc->regs->trnmod = SDHCI_TRNS_READ;
return 1;
}
@ -533,6 +561,7 @@ static int _sdmmc_send_cmd(sdmmc_t *sdmmc, sdmmc_cmd_t *cmd, bool is_data_presen
{
case SDMMC_RSP_TYPE_0:
break;
case SDMMC_RSP_TYPE_1:
case SDMMC_RSP_TYPE_4:
case SDMMC_RSP_TYPE_5:
@ -541,15 +570,17 @@ static int _sdmmc_send_cmd(sdmmc_t *sdmmc, sdmmc_cmd_t *cmd, bool is_data_presen
else
cmdflags = SDHCI_CMD_RESP_LEN48 | SDHCI_CMD_INDEX | SDHCI_CMD_CRC;
break;
case SDMMC_RSP_TYPE_2:
cmdflags = SDHCI_CMD_RESP_LEN136 | SDHCI_CMD_CRC;
break;
case SDMMC_RSP_TYPE_3:
cmdflags = SDHCI_CMD_RESP_LEN48;
break;
default:
return 0;
break;
}
if (is_data_present)
@ -572,7 +603,7 @@ static void _sdmmc_send_tuning_cmd(sdmmc_t *sdmmc, u32 cmd)
static int _sdmmc_tuning_execute_once(sdmmc_t *sdmmc, u32 cmd)
{
if (sdmmc->auto_cal_enabled)
if (sdmmc->powersave_enabled)
return 0;
if (!_sdmmc_wait_cmd_data_inhibit(sdmmc, true))
return 0;
@ -584,13 +615,13 @@ static int _sdmmc_tuning_execute_once(sdmmc_t *sdmmc, u32 cmd)
sdmmc->regs->clkcon &= ~SDHCI_CLOCK_CARD_EN;
_sdmmc_send_tuning_cmd(sdmmc, cmd);
_sdmmc_get_clkcon(sdmmc);
_sdmmc_commit_changes(sdmmc);
usleep(1);
_sdmmc_reset(sdmmc);
sdmmc->regs->clkcon |= SDHCI_CLOCK_CARD_EN;
_sdmmc_get_clkcon(sdmmc);
_sdmmc_commit_changes(sdmmc);
u64 timeout = get_tmr_us() + 5000;
while (get_tmr_us() < timeout)
@ -599,7 +630,7 @@ static int _sdmmc_tuning_execute_once(sdmmc_t *sdmmc, u32 cmd)
{
sdmmc->regs->norintsts = SDHCI_INT_DATA_AVAIL;
sdmmc->regs->norintstsen &= ~SDHCI_INT_DATA_AVAIL;
_sdmmc_get_clkcon(sdmmc);
_sdmmc_commit_changes(sdmmc);
usleep((1000 * 8 + sdmmc->divisor - 1) / sdmmc->divisor);
return 1;
}
@ -608,7 +639,7 @@ static int _sdmmc_tuning_execute_once(sdmmc_t *sdmmc, u32 cmd)
_sdmmc_reset(sdmmc);
sdmmc->regs->norintstsen &= ~SDHCI_INT_DATA_AVAIL;
_sdmmc_get_clkcon(sdmmc);
_sdmmc_commit_changes(sdmmc);
usleep((1000 * 8 + sdmmc->divisor - 1) / sdmmc->divisor);
return 0;
@ -627,15 +658,18 @@ int sdmmc_tuning_execute(sdmmc_t *sdmmc, u32 type, u32 cmd)
max = 128;
flag = (2 << 13); // 128 iterations.
break;
case SDHCI_TIMING_UHS_SDR50:
case SDHCI_TIMING_UHS_DDR50:
case SDHCI_TIMING_MMC_DDR52:
case SDHCI_TIMING_MMC_HS102:
max = 256;
flag = (4 << 13); // 256 iterations.
break;
case SDHCI_TIMING_UHS_SDR12:
case SDHCI_TIMING_UHS_SDR25:
return 1;
default:
return 0;
}
@ -664,7 +698,7 @@ static int _sdmmc_enable_internal_clock(sdmmc_t *sdmmc)
{
//Enable internal clock and wait till it is stable.
sdmmc->regs->clkcon |= SDHCI_CLOCK_INT_EN;
_sdmmc_get_clkcon(sdmmc);
_sdmmc_commit_changes(sdmmc);
u64 timeout = get_tmr_ms() + 2000;
while (!(sdmmc->regs->clkcon & SDHCI_CLOCK_INT_STABLE))
{
@ -700,17 +734,28 @@ static int _sdmmc_autocal_config_offset(sdmmc_t *sdmmc, u32 power)
off_pd = 5;
off_pu = 5;
break;
case SDMMC_1:
case SDMMC_3:
if (power == SDMMC_POWER_1_8)
{
off_pd = 123;
off_pu = 123;
if (!sdmmc->t210b01)
{
off_pd = 123;
off_pu = 123;
}
else
{
off_pd = 6;
off_pu = 6;
}
}
else if (power == SDMMC_POWER_3_3)
{
off_pd = 125;
off_pu = 0;
if (!sdmmc->t210b01)
{
off_pd = 125;
off_pu = 0;
}
}
else
return 0;
@ -740,7 +785,7 @@ static int _sdmmc_check_mask_interrupt(sdmmc_t *sdmmc, u16 *pout, u16 mask)
u16 norintsts = sdmmc->regs->norintsts;
u16 errintsts = sdmmc->regs->errintsts;
DPRINTF("norintsts %08X; errintsts %08X\n", norintsts, errintsts);
DPRINTF("norintsts %08X, errintsts %08X\n", norintsts, errintsts);
if (pout)
*pout = norintsts;
@ -762,7 +807,7 @@ DPRINTF("norintsts %08X; errintsts %08X\n", norintsts, errintsts);
static int _sdmmc_wait_response(sdmmc_t *sdmmc)
{
_sdmmc_get_clkcon(sdmmc);
_sdmmc_commit_changes(sdmmc);
u64 timeout = get_tmr_ms() + 2000;
while (true)
@ -813,7 +858,7 @@ int sdmmc_stop_transmission(sdmmc_t *sdmmc, u32 *rsp)
return 0;
// Recalibrate periodically for SDMMC1.
if ((sdmmc->id == SDMMC_1) && sdmmc->auto_cal_enabled)
if (sdmmc->manual_cal && sdmmc->powersave_enabled)
_sdmmc_autocal_execute(sdmmc, sdmmc_get_io_power(sdmmc));
bool should_disable_sd_clock = false;
@ -821,7 +866,7 @@ int sdmmc_stop_transmission(sdmmc_t *sdmmc, u32 *rsp)
{
should_disable_sd_clock = true;
sdmmc->regs->clkcon |= SDHCI_CLOCK_CARD_EN;
_sdmmc_get_clkcon(sdmmc);
_sdmmc_commit_changes(sdmmc);
usleep((8000 + sdmmc->divisor - 1) / sdmmc->divisor);
}
@ -861,7 +906,7 @@ static int _sdmmc_config_dma(sdmmc_t *sdmmc, u32 *blkcnt_out, sdmmc_req_t *req)
sdmmc->dma_addr_next = (admaaddr + 0x80000) & 0xFFFFFFFFFFF80000;
sdmmc->regs->blksize = req->blksize | 0x7000;
sdmmc->regs->blksize = req->blksize | 0x7000; // DMA 512KB (Detects A18 carry out).
sdmmc->regs->blkcnt = blkcnt;
if (blkcnt_out)
@ -953,8 +998,6 @@ static int _sdmmc_execute_cmd_inner(sdmmc_t *sdmmc, sdmmc_cmd_t *cmd, sdmmc_req_
is_data_present = true;
}
else
is_data_present = false;
_sdmmc_enable_interrupts(sdmmc);
@ -962,7 +1005,7 @@ static int _sdmmc_execute_cmd_inner(sdmmc_t *sdmmc, sdmmc_cmd_t *cmd, sdmmc_req_
return 0;
int result = _sdmmc_wait_response(sdmmc);
DPRINTF("rsp(%d): %08X, %08X, %08X, %08X\n", result,
DPRINTF("rsp(%d): %08X, %08X, %08X, %08X\n", result,
sdmmc->regs->rspreg0, sdmmc->regs->rspreg1, sdmmc->regs->rspreg2, sdmmc->regs->rspreg3);
if (result)
{
@ -970,12 +1013,6 @@ static int _sdmmc_execute_cmd_inner(sdmmc_t *sdmmc, sdmmc_cmd_t *cmd, sdmmc_req_
{
sdmmc->expected_rsp_type = cmd->rsp_type;
result = _sdmmc_cache_rsp(sdmmc, sdmmc->rsp, 0x10, cmd->rsp_type);
/*if(sdmmc->rsp[0] & 0xFDF9A080)
{
res = 0;
sdmmc->rsp[0] = 0; // Reset error
}*/
}
if (req && result)
@ -1029,7 +1066,56 @@ bool sdmmc_get_sd_inserted()
return (!gpio_read(GPIO_PORT_Z, GPIO_PIN_1));
}
static int _sdmmc_config_sdmmc1()
static void _sdmmc_config_sdmmc1_schmitt()
{
PINMUX_AUX(PINMUX_AUX_SDMMC1_CLK) |= PINMUX_SCHMT;
PINMUX_AUX(PINMUX_AUX_SDMMC1_CMD) |= PINMUX_SCHMT;
PINMUX_AUX(PINMUX_AUX_SDMMC1_DAT3) |= PINMUX_SCHMT;
PINMUX_AUX(PINMUX_AUX_SDMMC1_DAT2) |= PINMUX_SCHMT;
PINMUX_AUX(PINMUX_AUX_SDMMC1_DAT1) |= PINMUX_SCHMT;
PINMUX_AUX(PINMUX_AUX_SDMMC1_DAT0) |= PINMUX_SCHMT;
}
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.
PINMUX_AUX(PINMUX_AUX_GPIO_PZ1) = PINMUX_INPUT_ENABLE | PINMUX_PULL_UP | 2; // GPIO control, pull up.
@ -1044,76 +1130,102 @@ static int _sdmmc_config_sdmmc1()
/*
* Pinmux config:
* DRV_TYPE = DRIVE_2X
* DRV_TYPE = DRIVE_2X (for 33 Ohm driver)
* E_SCHMT = ENABLE (for 1.8V), DISABLE (for 3.3V)
* E_INPUT = ENABLE
* TRISTATE = PASSTHROUGH
* APB_MISC_GP_SDMMCx_CLK_LPBK_CONTROL = SDMMCx_CLK_PAD_E_LPBK for CLK
*/
// Configure SDMMC1 pinmux.
APB_MISC(APB_MISC_GP_SDMMC1_CLK_LPBK_CONTROL) = 1; // Enable deep loopback for SDMMC1 CLK pad.
PINMUX_AUX(PINMUX_AUX_SDMMC1_CLK) = PINMUX_DRIVE_2X | PINMUX_INPUT_ENABLE | PINMUX_PARKED;
PINMUX_AUX(PINMUX_AUX_SDMMC1_CMD) = PINMUX_DRIVE_2X | PINMUX_INPUT_ENABLE | PINMUX_PARKED | PINMUX_PULL_UP;
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;
PINMUX_AUX(PINMUX_AUX_SDMMC1_DAT1) = PINMUX_DRIVE_2X | PINMUX_INPUT_ENABLE | PINMUX_PARKED | PINMUX_PULL_UP;
PINMUX_AUX(PINMUX_AUX_SDMMC1_DAT0) = PINMUX_DRIVE_2X | PINMUX_INPUT_ENABLE | PINMUX_PARKED | PINMUX_PULL_UP;
// Enable deep loopback for SDMMC1 CLK pad.
APB_MISC(APB_MISC_GP_SDMMC1_CLK_LPBK_CONTROL) = 1;
// Configure SDMMC1 CLK pinmux, based on state and SoC type.
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_CLK) = PINMUX_DRIVE_2X | PINMUX_INPUT_ENABLE | (t210b01 ? PINMUX_PULL_NONE : PINMUX_PULL_DOWN);
// Configure the reset of SDMMC1 pins pinmux.
PINMUX_AUX(PINMUX_AUX_SDMMC1_CMD) = PINMUX_DRIVE_2X | PINMUX_INPUT_ENABLE | 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.
smcReadWriteRegister(PMC_BASE + APBDEV_PMC_NO_IOPOWER, PMC_NO_IOPOWER_SDMMC1_IO_EN, PMC_NO_IOPOWER_SDMMC1_IO_EN);
usleep(1000);
smcReadWriteRegister(PMC_BASE + APBDEV_PMC_NO_IOPOWER, ~PMC_NO_IOPOWER_SDMMC1_IO_EN, PMC_NO_IOPOWER_SDMMC1_IO_EN);
smcReadWriteRegister(PMC_BASE + APBDEV_PMC_NO_IOPOWER, 0, 0); // Commit write.
// Inform IO pads that voltage is gonna be 3.3V.
smcReadWriteRegister(PMC_BASE + APBDEV_PMC_PWR_DET_VAL, PMC_PWR_DET_SDMMC1_IO_EN, PMC_PWR_DET_SDMMC1_IO_EN);
smcReadWriteRegister(PMC_BASE + APBDEV_PMC_PWR_DET_VAL, 0, 0); // Commit write.
// Set enable SD card power.
//PINMUX_AUX(PINMUX_AUX_DMIC3_CLK) = PINMUX_PULL_DOWN | 2; // Pull down.
PINMUX_AUX(PINMUX_AUX_DMIC3_CLK) = PINMUX_INPUT_ENABLE | PINMUX_PULL_DOWN | 1; // GPIO control, pull down.
//PINMUX_AUX(PINMUX_AUX_DMIC3_CLK) = PINMUX_PULL_DOWN | 2; // Proper pinmuxing. Breaks on HOS, takes over 1 minute to recover.
PINMUX_AUX(PINMUX_AUX_DMIC3_CLK) = PINMUX_INPUT_ENABLE | PINMUX_PULL_DOWN | 1; // Wrong but working pinmuxing. Instant take over by FS.
gpio_config(GPIO_PORT_E, GPIO_PIN_4, GPIO_MODE_GPIO);
gpio_write(GPIO_PORT_E, GPIO_PIN_4, GPIO_HIGH);
gpio_output_enable(GPIO_PORT_E, GPIO_PIN_4, GPIO_OUTPUT_ENABLE);
usleep(1000);
usleep(10000);
// Enable SD card power.
// Enable SD card IO power.
max77620_regulator_set_voltage(REGULATOR_LDO2, 3300000);
max77620_regulator_enable(REGULATOR_LDO2, 1);
usleep(1000);
// 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;
usleep(1000);
if (!t210b01)
{
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;
}
static void _sdmmc_config_emmc(u32 id)
static void _sdmmc_config_emmc(u32 id, bool t210b01)
{
switch (id)
{
case SDMMC_2:
// Unset park for pads.
APB_MISC(APB_MISC_GP_EMMC2_PAD_CFGPADCTRL) &= 0xF8003FFF;
if (!t210b01)
{
// 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;
case SDMMC_4:
// Unset park for pads.
APB_MISC(APB_MISC_GP_EMMC4_PAD_CFGPADCTRL) &= 0xF8003FFF;
// Set default pad cfg.
APB_MISC(APB_MISC_GP_EMMC4_PAD_CFGPADCTRL) = (APB_MISC(APB_MISC_GP_EMMC4_PAD_CFGPADCTRL) & 0xFFFFC003) | 0x1040;
// Enabled schmitt trigger.
APB_MISC(APB_MISC_GP_EMMC4_PAD_CFGPADCTRL) |= 1; // Enable Schmitt trigger.
if (t210b01)
APB_MISC(APB_MISC_GP_EMMC4_PAD_PUPD_CFGPADCTRL) &= 0xFFBFFFF9; // Unset CMD/CLK/DQS powedown.
// Enable schmitt trigger.
APB_MISC(APB_MISC_GP_EMMC4_PAD_CFGPADCTRL) |= 1;
(void)APB_MISC(APB_MISC_GP_EMMC4_PAD_CFGPADCTRL); // Commit write.
break;
}
}
int sdmmc_init(sdmmc_t *sdmmc, u32 id, u32 power, u32 bus_width, u32 type, int auto_cal_enable)
int sdmmc_init(sdmmc_t *sdmmc, u32 id, u32 power, u32 bus_width, u32 type, int powersave_enable)
{
const u32 trim_values[] = { 2, 8, 3, 8 };
u32 clock;
u16 divisor;
u8 vref_sel = 7;
if (id > SDMMC_4)
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)
return 0;
memset(sdmmc, 0, sizeof(sdmmc_t));
@ -1121,45 +1233,57 @@ int sdmmc_init(sdmmc_t *sdmmc, u32 id, u32 power, u32 bus_width, u32 type, int a
sdmmc->regs = (t210_sdmmc_t *)QueryIoMapping(_sdmmc_bases[id], 0x200);
sdmmc->id = id;
sdmmc->clock_stopped = 1;
sdmmc->t210b01 = splGetSocType() == SplSocType_Mariko;
// Do specific SDMMC HW configuration.
switch (id)
{
case SDMMC_1:
if (!_sdmmc_config_sdmmc1())
if (!_sdmmc_config_sdmmc1(sdmmc->t210b01))
return 0;
if (sdmmc->t210b01)
vref_sel = 0;
else
sdmmc->manual_cal = 1;
break;
case SDMMC_2:
case SDMMC_4:
_sdmmc_config_emmc(id);
_sdmmc_config_emmc(id, sdmmc->t210b01);
break;
}
// Disable clock if enabled.
if (clock_sdmmc_is_not_reset_and_enabled(id))
{
_sdmmc_sd_clock_disable(sdmmc);
_sdmmc_get_clkcon(sdmmc);
_sdmmc_commit_changes(sdmmc);
}
u32 clock;
u16 divisor;
// Configure and enable selected clock.
clock_sdmmc_get_card_clock_div(&clock, &divisor, type);
clock_sdmmc_enable(id, clock);
// Make sure all sdmmc registers are reset.
_sdmmc_reset_all(sdmmc);
sdmmc->clock_stopped = 0;
//TODO: make this skip-able.
// Set default pad IO trimming configuration.
sdmmc->regs->iospare |= 0x80000; // Enable muxing.
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->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.
if (!_sdmmc_autocal_config_offset(sdmmc, power))
return 0;
// Calibrate pads.
_sdmmc_autocal_execute(sdmmc, power);
// Enable internal clock and power.
if (_sdmmc_enable_internal_clock(sdmmc))
{
sdmmc_set_bus_width(sdmmc, bus_width);
@ -1167,18 +1291,50 @@ int sdmmc_init(sdmmc_t *sdmmc, u32 id, u32 power, u32 bus_width, u32 type, int a
if (sdmmc_setup_clock(sdmmc, type))
{
sdmmc_card_clock_ctrl(sdmmc, auto_cal_enable);
sdmmc_card_clock_powersave(sdmmc, powersave_enable);
_sdmmc_card_clock_enable(sdmmc);
_sdmmc_get_clkcon(sdmmc);
_sdmmc_commit_changes(sdmmc);
return 1;
}
return 0;
}
return 0;
}
void sdmmc1_disable_power()
{
// Clear pull down from CLK pad.
PINMUX_AUX(PINMUX_AUX_SDMMC1_CLK) &= ~PINMUX_PULL_MASK;
// 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.
msleep(239);
// Disable SDMMC1 controller power.
smcReadWriteRegister(PMC_BASE + APBDEV_PMC_NO_IOPOWER, PMC_NO_IOPOWER_SDMMC1_IO_EN, PMC_NO_IOPOWER_SDMMC1_IO_EN);
smcReadWriteRegister(PMC_BASE + APBDEV_PMC_NO_IOPOWER, 0, 0); // Commit write.
// Inform IO pads that next voltage might be 3.3V.
smcReadWriteRegister(PMC_BASE + APBDEV_PMC_PWR_DET_VAL, PMC_PWR_DET_SDMMC1_IO_EN, PMC_PWR_DET_SDMMC1_IO_EN);
smcReadWriteRegister(PMC_BASE + APBDEV_PMC_PWR_DET_VAL, 0, 0); // Commit write.
// Set pads to reset state.
_sdmmc_config_sdmmc1_pads(false);
// Set pull down to CLK pad.
PINMUX_AUX(PINMUX_AUX_SDMMC1_CLK) |= PINMUX_PULL_DOWN;
}
void sdmmc_end(sdmmc_t *sdmmc)
{
if (!sdmmc->clock_stopped)
@ -1189,17 +1345,9 @@ void sdmmc_end(sdmmc_t *sdmmc)
// Disable SD card power.
if (sdmmc->id == SDMMC_1)
{
gpio_output_enable(GPIO_PORT_E, GPIO_PIN_4, GPIO_OUTPUT_DISABLE);
max77620_regulator_enable(REGULATOR_LDO2, 0);
sdmmc1_disable_power();
// Inform IO pads that next voltage might be 3.3V.
smcReadWriteRegister(PMC_BASE + APBDEV_PMC_PWR_DET_VAL, PMC_PWR_DET_SDMMC1_IO_EN, PMC_PWR_DET_SDMMC1_IO_EN);
msleep(100); // To power cycle min 1ms without power is needed.
}
_sdmmc_get_clkcon(sdmmc);
_sdmmc_commit_changes(sdmmc);
clock_sdmmc_disable(sdmmc->id);
sdmmc->clock_stopped = 1;
}
@ -1219,7 +1367,7 @@ int sdmmc_execute_cmd(sdmmc_t *sdmmc, sdmmc_cmd_t *cmd, sdmmc_req_t *req, u32 *b
return 0;
// Recalibrate periodically for SDMMC1.
if (sdmmc->id == SDMMC_1 && sdmmc->auto_cal_enabled)
if (sdmmc->manual_cal && sdmmc->powersave_enabled)
_sdmmc_autocal_execute(sdmmc, sdmmc_get_io_power(sdmmc));
int should_disable_sd_clock = 0;
@ -1227,7 +1375,7 @@ int sdmmc_execute_cmd(sdmmc_t *sdmmc, sdmmc_cmd_t *cmd, sdmmc_req_t *req, u32 *b
{
should_disable_sd_clock = 1;
sdmmc->regs->clkcon |= SDHCI_CLOCK_CARD_EN;
_sdmmc_get_clkcon(sdmmc);
_sdmmc_commit_changes(sdmmc);
usleep((8000 + sdmmc->divisor - 1) / sdmmc->divisor);
}
@ -1248,36 +1396,32 @@ int sdmmc_enable_low_voltage(sdmmc_t *sdmmc)
if (!sdmmc_setup_clock(sdmmc, SDHCI_TIMING_UHS_SDR12))
return 0;
_sdmmc_get_clkcon(sdmmc);
_sdmmc_commit_changes(sdmmc);
// Switch to 1.8V and wait for regulator to stabilize. Assume max possible wait needed.
max77620_regulator_set_voltage(REGULATOR_LDO2, 1800000);
usleep(300);
usleep(150);
// Inform IO pads that we switched to 1.8V.
smcReadWriteRegister(PMC_BASE + APBDEV_PMC_PWR_DET_VAL, ~PMC_PWR_DET_SDMMC1_IO_EN, PMC_PWR_DET_SDMMC1_IO_EN);
smcReadWriteRegister(PMC_BASE + APBDEV_PMC_PWR_DET_VAL, 0, 0); // Commit write.
// Enable schmitt trigger for better duty cycle and low jitter clock.
PINMUX_AUX(PINMUX_AUX_SDMMC1_CLK) |= PINMUX_SCHMT;
PINMUX_AUX(PINMUX_AUX_SDMMC1_CMD) |= PINMUX_SCHMT;
PINMUX_AUX(PINMUX_AUX_SDMMC1_DAT3) |= PINMUX_SCHMT;
PINMUX_AUX(PINMUX_AUX_SDMMC1_DAT2) |= PINMUX_SCHMT;
PINMUX_AUX(PINMUX_AUX_SDMMC1_DAT1) |= PINMUX_SCHMT;
PINMUX_AUX(PINMUX_AUX_SDMMC1_DAT0) |= PINMUX_SCHMT;
_sdmmc_config_sdmmc1_schmitt();
_sdmmc_autocal_config_offset(sdmmc, SDMMC_POWER_1_8);
_sdmmc_autocal_execute(sdmmc, SDMMC_POWER_1_8);
_sdmmc_set_io_power(sdmmc, SDMMC_POWER_1_8);
_sdmmc_get_clkcon(sdmmc);
_sdmmc_commit_changes(sdmmc);
msleep(5); // Wait minimum 5ms before turning on the card clock.
// Turn on SDCLK.
if (sdmmc->regs->hostctl2 & SDHCI_CTRL_VDD_180)
{
sdmmc->regs->clkcon |= SDHCI_CLOCK_CARD_EN;
_sdmmc_get_clkcon(sdmmc);
_sdmmc_commit_changes(sdmmc);
usleep(1000);
if ((sdmmc->regs->prnsts & 0xF00000) == 0xF00000)
if ((sdmmc->regs->prnsts & SDHCI_DATA_LVL_MASK) == SDHCI_DATA_LVL_MASK)
return 1;
}

View file

@ -1,6 +1,6 @@
/*
* Copyright (c) 2018 naehrwert
* Copyright (c) 2018-2019 CTCaer
* Copyright (c) 2018-2020 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,
@ -195,13 +195,13 @@
#define SDHCI_TIMING_UHS_SDR104 11
#define SDHCI_TIMING_UHS_SDR82 12 // SDR104 with a 163.2MHz -> 81.6MHz clock.
#define SDHCI_TIMING_UHS_DDR50 13
#define SDHCI_TIMING_MMC_DDR52 14
#define SDHCI_TIMING_MMC_HS102 14
#define SDHCI_CAN_64BIT 0x10000000
/*! SDMMC Low power features. */
#define SDMMC_AUTO_CAL_DISABLE 0
#define SDMMC_AUTO_CAL_ENABLE 1
#define SDMMC_POWER_SAVE_DISABLE 0
#define SDMMC_POWER_SAVE_ENABLE 1
/*! Helper for SWITCH command argument. */
#define SDMMC_SWITCH(mode, index, value) (((mode) << 24) | ((index) << 16) | ((value) << 8))
@ -213,7 +213,8 @@ typedef struct _sdmmc_t
u32 id;
u32 divisor;
u32 clock_stopped;
int auto_cal_enabled;
int powersave_enabled;
int manual_cal;
int card_clock_enabled;
int venclkctl_set;
u32 venclkctl_tap;
@ -222,6 +223,7 @@ typedef struct _sdmmc_t
u64 dma_addr_next;
u32 rsp[4];
u32 rsp3;
int t210b01;
} sdmmc_t;
/*! SDMMC command. */
@ -247,15 +249,15 @@ typedef struct _sdmmc_req_t
int sdmmc_get_io_power(sdmmc_t *sdmmc);
u32 sdmmc_get_bus_width(sdmmc_t *sdmmc);
void sdmmc_set_bus_width(sdmmc_t *sdmmc, u32 bus_width);
void sdmmc_set_tap_value(sdmmc_t *sdmmc);
void sdmmc_save_tap_value(sdmmc_t *sdmmc);
int sdmmc_setup_clock(sdmmc_t *sdmmc, u32 type);
void sdmmc_card_clock_ctrl(sdmmc_t *sdmmc, int auto_cal_enable);
void sdmmc_card_clock_powersave(sdmmc_t *sdmmc, int powersave_enable);
int sdmmc_get_rsp(sdmmc_t *sdmmc, u32 *rsp, u32 size, u32 type);
int sdmmc_tuning_execute(sdmmc_t *sdmmc, u32 type, u32 cmd);
int sdmmc_stop_transmission(sdmmc_t *sdmmc, u32 *rsp);
int sdmmc_get_sd_power_enabled();
bool sdmmc_get_sd_inserted();
int sdmmc_init(sdmmc_t *sdmmc, u32 id, u32 power, u32 bus_width, u32 type, int auto_cal_enable);
int sdmmc_init(sdmmc_t *sdmmc, u32 id, u32 power, u32 bus_width, u32 type, int powersave_enable);
void sdmmc_end(sdmmc_t *sdmmc);
void sdmmc_init_cmd(sdmmc_cmd_t *cmdbuf, u16 cmd, u32 arg, u32 rsp_type, u32 check_busy);
int sdmmc_execute_cmd(sdmmc_t *sdmmc, sdmmc_cmd_t *cmd, sdmmc_req_t *req, u32 *blkcnt_out);

View file

@ -103,6 +103,7 @@ typedef struct _t210_sdmmc_t
vu32 iospare;
vu32 mcciffifoctl;
vu32 timeoutwcoal;
vu32 unk1;
} t210_sdmmc_t;
#endif

View file

@ -72,19 +72,31 @@ SplHardwareType splGetHardwareType(void)
SplSocType splGetSocType(void)
{
static SplSocType soc_type;
static bool soc_type_set = false;
if (soc_type_set)
return soc_type;
switch (splGetHardwareType())
{
case SplHardwareType_Icosa:
case SplHardwareType_Copper:
return SplSocType_Erista;
soc_type = SplSocType_Erista;
break;
case SplHardwareType_Hoag:
case SplHardwareType_Iowa:
case SplHardwareType_Calcio:
case SplHardwareType_Five:
return SplSocType_Mariko;
soc_type = SplSocType_Mariko;
break;
default:
fatal_abort(Fatal_InvalidEnum);
}
soc_type_set = true;
return soc_type;
}

View file

@ -1,5 +1,6 @@
/*
* Copyright (c) 2018 naehrwert
* Copyright (c) 2018-2020 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,
@ -410,7 +411,7 @@ void clock_sdmmc_get_card_clock_div(u32 *pclock, u16 *pdivisor, u32 type)
*pclock = 40800;
*pdivisor = 1;
break;
case SDHCI_TIMING_MMC_DDR52: // Actual IO Freq: 49.92 MHz.
case SDHCI_TIMING_MMC_HS102: // Actual IO Freq: 99.84 MHz.
*pclock = 200000;
*pdivisor = 2;
break;

View file

@ -1,5 +1,6 @@
/*
* Copyright (c) 2018 naehrwert
* Copyright (c) 2018-2020 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,

View file

@ -1,5 +1,6 @@
/*
* Copyright (c) 2018 naehrwert
* Copyright (c) 2019 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,
@ -17,78 +18,151 @@
#include "../soc/gpio.h"
#include "../soc/t210.h"
static const u16 _gpio_cnf[31] = {
0x000, 0x004, 0x008, 0x00C,
0x100, 0x104, 0x108, 0x10C,
0x200, 0x204, 0x208, 0x20C,
0x300, 0x304, 0x308, 0x30C,
0x400, 0x404, 0x408, 0x40C,
0x500, 0x504, 0x508, 0x50C,
0x600, 0x604, 0x608, 0x60C,
0x700, 0x704, 0x708
};
#define GPIO_BANK_IDX(port) ((port) >> 2)
static const u16 _gpio_oe[31] = {
0x010, 0x014, 0x018, 0x01C,
0x110, 0x114, 0x118, 0x11C,
0x210, 0x214, 0x218, 0x21C,
0x310, 0x314, 0x318, 0x31C,
0x410, 0x414, 0x418, 0x41C,
0x510, 0x514, 0x518, 0x51C,
0x610, 0x614, 0x618, 0x61C,
0x710, 0x714, 0x718
};
#define GPIO_CNF_OFFSET(port) (0x00 + (((port) >> 2) << 8) + (((port) % 4) << 2))
#define GPIO_OE_OFFSET(port) (0x10 + (((port) >> 2) << 8) + (((port) % 4) << 2))
#define GPIO_OUT_OFFSET(port) (0x20 + (((port) >> 2) << 8) + (((port) % 4) << 2))
#define GPIO_IN_OFFSET(port) (0x30 + (((port) >> 2) << 8) + (((port) % 4) << 2))
#define GPIO_INT_STA_OFFSET(port) (0x40 + (((port) >> 2) << 8) + (((port) % 4) << 2))
#define GPIO_INT_ENB_OFFSET(port) (0x50 + (((port) >> 2) << 8) + (((port) % 4) << 2))
#define GPIO_INT_LVL_OFFSET(port) (0x60 + (((port) >> 2) << 8) + (((port) % 4) << 2))
#define GPIO_INT_CLR_OFFSET(port) (0x70 + (((port) >> 2) << 8) + (((port) % 4) << 2))
static const u16 _gpio_out[31] = {
0x020, 0x024, 0x028, 0x02C,
0x120, 0x124, 0x128, 0x12C,
0x220, 0x224, 0x228, 0x22C,
0x320, 0x324, 0x328, 0x32C,
0x420, 0x424, 0x428, 0x42C,
0x520, 0x524, 0x528, 0x52C,
0x620, 0x624, 0x628, 0x62C,
0x720, 0x724, 0x728
};
#define GPIO_CNF_MASKED_OFFSET(port) (0x80 + (((port) >> 2) << 8) + (((port) % 4) << 2))
#define GPIO_OE_MASKED_OFFSET(port) (0x90 + (((port) >> 2) << 8) + (((port) % 4) << 2))
#define GPIO_OUT_MASKED_OFFSET(port) (0xA0 + (((port) >> 2) << 8) + (((port) % 4) << 2))
#define GPIO_INT_STA_MASKED_OFFSET(port) (0xC0 + (((port) >> 2) << 8) + (((port) % 4) << 2))
#define GPIO_INT_ENB_MASKED_OFFSET(port) (0xD0 + (((port) >> 2) << 8) + (((port) % 4) << 2))
#define GPIO_INT_LVL_MASKED_OFFSET(port) (0xE0 + (((port) >> 2) << 8) + (((port) % 4) << 2))
static const u16 _gpio_in[31] = {
0x030, 0x034, 0x038, 0x03C,
0x130, 0x134, 0x138, 0x13C,
0x230, 0x234, 0x238, 0x23C,
0x330, 0x334, 0x338, 0x33C,
0x430, 0x434, 0x438, 0x43C,
0x530, 0x534, 0x538, 0x53C,
0x630, 0x634, 0x638, 0x63C,
0x730, 0x734, 0x738
#define GPIO_IRQ_BANK1 32
#define GPIO_IRQ_BANK2 33
#define GPIO_IRQ_BANK3 34
#define GPIO_IRQ_BANK4 35
#define GPIO_IRQ_BANK5 55
#define GPIO_IRQ_BANK6 87
#define GPIO_IRQ_BANK7 89
#define GPIO_IRQ_BANK8 125
static u8 gpio_bank_irq_ids[8] = {
GPIO_IRQ_BANK1, GPIO_IRQ_BANK2, GPIO_IRQ_BANK3, GPIO_IRQ_BANK4,
GPIO_IRQ_BANK5, GPIO_IRQ_BANK6, GPIO_IRQ_BANK7, GPIO_IRQ_BANK8
};
void gpio_config(u32 port, u32 pins, int mode)
{
u32 offset = GPIO_CNF_OFFSET(port);
if (mode)
GPIO(_gpio_cnf[port]) |= pins;
GPIO(offset) |= pins;
else
GPIO(_gpio_cnf[port]) &= ~pins;
(void)GPIO(_gpio_cnf[port]);
GPIO(offset) &= ~pins;
(void)GPIO(offset); // Commit the write.
}
void gpio_output_enable(u32 port, u32 pins, int enable)
{
u32 port_offset = GPIO_OE_OFFSET(port);
if (enable)
GPIO(_gpio_oe[port]) |= pins;
GPIO(port_offset) |= pins;
else
GPIO(_gpio_oe[port]) &= ~pins;
(void)GPIO(_gpio_oe[port]);
GPIO(port_offset) &= ~pins;
(void)GPIO(port_offset); // Commit the write.
}
void gpio_write(u32 port, u32 pins, int high)
{
u32 port_offset = GPIO_OUT_OFFSET(port);
if (high)
GPIO(_gpio_out[port]) |= pins;
GPIO(port_offset) |= pins;
else
GPIO(_gpio_out[port]) &= ~pins;
(void)GPIO(_gpio_out[port]);
GPIO(port_offset) &= ~pins;
(void)GPIO(port_offset); // Commit the write.
}
int gpio_read(u32 port, u32 pins)
{
return (GPIO(_gpio_in[port]) & pins) ? 1 : 0;
u32 port_offset = GPIO_IN_OFFSET(port);
return (GPIO(port_offset) & pins) ? 1 : 0;
}
static void _gpio_interrupt_clear(u32 port, u32 pins)
{
u32 port_offset = GPIO_INT_CLR_OFFSET(port);
GPIO(port_offset) |= pins;
(void)GPIO(port_offset); // Commit the write.
}
int gpio_interrupt_status(u32 port, u32 pins)
{
u32 port_offset = GPIO_INT_STA_OFFSET(port);
u32 enabled = GPIO(GPIO_INT_ENB_OFFSET(port)) & pins;
int status = ((GPIO(port_offset) & pins) && enabled) ? 1 : 0;
// Clear the interrupt status.
if (status)
_gpio_interrupt_clear(port, pins);
return status;
}
void gpio_interrupt_enable(u32 port, u32 pins, int enable)
{
u32 port_offset = GPIO_INT_ENB_OFFSET(port);
// Clear any possible stray interrupt.
_gpio_interrupt_clear(port, pins);
if (enable)
GPIO(port_offset) |= pins;
else
GPIO(port_offset) &= ~pins;
(void)GPIO(port_offset); // Commit the write.
}
void gpio_interrupt_level(u32 port, u32 pins, int high, int edge, int delta)
{
u32 port_offset = GPIO_INT_LVL_OFFSET(port);
u32 val = GPIO(port_offset);
if (high)
val |= pins;
else
val &= ~pins;
if (edge)
val |= pins << 8;
else
val &= ~(pins << 8);
if (delta)
val |= pins << 16;
else
val &= ~(pins << 16);
GPIO(port_offset) = val;
(void)GPIO(port_offset); // Commit the write.
// Clear any possible stray interrupt.
_gpio_interrupt_clear(port, pins);
}
u32 gpio_get_bank_irq_id(u32 port)
{
u32 bank_idx = GPIO_BANK_IDX(port);
return gpio_bank_irq_ids[bank_idx];
}

View file

@ -1,5 +1,6 @@
/*
* Copyright (c) 2018 naehrwert
* Copyright (c) 2019 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,
@ -21,10 +22,23 @@
#define GPIO_MODE_SPIO 0
#define GPIO_MODE_GPIO 1
#define GPIO_OUTPUT_DISABLE 0
#define GPIO_OUTPUT_ENABLE 1
#define GPIO_IRQ_DISABLE 0
#define GPIO_IRQ_ENABLE 1
#define GPIO_LOW 0
#define GPIO_HIGH 1
#define GPIO_FALLING 0
#define GPIO_RISING 1
#define GPIO_LEVEL 0
#define GPIO_EDGE 1
#define GPIO_CONFIGURED_EDGE 0
#define GPIO_ANY_EDGE_CHANGE 1
/*! GPIO pins (0-7 for each port). */
#define GPIO_PIN_0 (1 << 0)
@ -72,6 +86,10 @@
void gpio_config(u32 port, u32 pins, int mode);
void gpio_output_enable(u32 port, u32 pins, int enable);
void gpio_write(u32 port, u32 pins, int high);
int gpio_read(u32 port, u32 pins);
int gpio_read(u32 port, u32 pins);
int gpio_interrupt_status(u32 port, u32 pins);
void gpio_interrupt_enable(u32 port, u32 pins, int enable);
void gpio_interrupt_level(u32 port, u32 pins, int high, int edge, int delta);
u32 gpio_get_bank_irq_id(u32 port);
#endif

View file

@ -1,5 +1,6 @@
/*
* Copyright (c) 2018 naehrwert
* Copyright (c) 2018-2020 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,
@ -20,25 +21,88 @@
#include "../utils/util.h"
#include "t210.h"
// TODO: not hardcode I2C_5
static u64 i2c_addrs[] = {
0x7000C000, 0x7000C400, 0x7000C500,
0x7000C700, 0x7000D000, 0x7000D100
#define I2C_PACKET_PROT_I2C (1 << 4)
#define I2C_HEADER_CONT_XFER (1 << 15)
#define I2C_HEADER_REP_START (1 << 16)
#define I2C_HEADER_IE_ENABLE (1 << 17)
#define I2C_HEADER_READ (1 << 19)
#define I2C_CNFG (0x00 / 4)
#define CMD1_WRITE (0 << 6)
#define CMD1_READ (1 << 6)
#define NORMAL_MODE_GO (1 << 9)
#define PACKET_MODE_GO (1 << 10)
#define NEW_MASTER_FSM (1 << 11)
#define DEBOUNCE_CNT_4T (2 << 12)
#define I2C_CMD_ADDR0 (0x04 / 4)
#define ADDR0_WRITE 0
#define ADDR0_READ 1
#define I2C_CMD_DATA1 (0x0C / 4)
#define I2C_CMD_DATA2 (0x10 / 4)
#define I2C_STATUS (0x1C / 4)
#define I2C_STATUS_NOACK (0xF << 0)
#define I2C_STATUS_BUSY (1 << 8)
#define I2C_TX_FIFO (0x50 / 4)
#define I2C_RX_FIFO (0x54 / 4)
#define I2C_FIFO_CONTROL (0x5C / 4)
#define RX_FIFO_FLUSH (1 << 0)
#define TX_FIFO_FLUSH (1 << 1)
#define I2C_FIFO_STATUS (0x60 / 4)
#define RX_FIFO_FULL_CNT (0xF << 0)
#define TX_FIFO_EMPTY_CNT (0xF << 4)
#define I2C_INT_EN (0x64 / 4)
#define I2C_INT_STATUS (0x68 / 4)
#define I2C_INT_SOURCE (0x70 / 4)
#define RX_FIFO_DATA_REQ (1 << 0)
#define TX_FIFO_DATA_REQ (1 << 1)
#define ARB_LOST (1 << 2)
#define NO_ACK (1 << 3)
#define RX_FIFO_UNDER (1 << 4)
#define TX_FIFO_OVER (1 << 5)
#define ALL_PACKETS_COMPLETE (1 << 6)
#define PACKET_COMPLETE (1 << 7)
#define BUS_CLEAR_DONE (1 << 11)
#define I2C_CLK_DIVISOR (0x6C / 4)
#define I2C_BUS_CLEAR_CONFIG (0x84 / 4)
#define BC_ENABLE (1 << 0)
#define BC_TERMINATE (1 << 1)
#define I2C_BUS_CLEAR_STATUS (0x88 / 4)
#define I2C_CONFIG_LOAD (0x8C / 4)
#define MSTR_CONFIG_LOAD (1 << 0)
#define TIMEOUT_CONFIG_LOAD (1 << 2)
static const u64 i2c_addrs[] = {
0x7000C000, // I2C_1.
0x7000C400, // I2C_2.
0x7000C500, // I2C_3.
0x7000C700, // I2C_4.
0x7000D000, // I2C_5.
0x7000D100 // I2C_6.
};
static void _i2c_wait(vu32 *base)
static void _i2c_load_cfg_wait(vu32 *base)
{
base[I2C_CONFIG_LOAD] = 0x25;
base[I2C_CONFIG_LOAD] = (1 << 5) | TIMEOUT_CONFIG_LOAD | MSTR_CONFIG_LOAD;
for (u32 i = 0; i < 20; i++)
{
usleep(1);
if (!(base[I2C_CONFIG_LOAD] & 1))
if (!(base[I2C_CONFIG_LOAD] & MSTR_CONFIG_LOAD))
break;
}
}
static int _i2c_send_pkt(u32 idx, u32 x, u8 *buf, u32 size)
static int _i2c_send_single(u32 i2c_idx, u32 dev_addr, u8 *buf, u32 size)
{
if (size > 4)
return 0;
@ -47,46 +111,58 @@ static int _i2c_send_pkt(u32 idx, u32 x, u8 *buf, u32 size)
memcpy(&tmp, buf, size);
vu32 *base = (vu32 *)QueryIoMapping(i2c_addrs[I2C_5], 0x1000);
base[I2C_CMD_ADDR0] = x << 1; //Set x (send mode).
// Set device address and send mode.
base[I2C_CMD_ADDR0] = dev_addr << 1 | ADDR0_WRITE;
base[I2C_CMD_DATA1] = tmp; //Set value.
base[I2C_CNFG] = ((size - 1) << 1) | 0x2800; //Set size and send mode.
_i2c_wait(base); //Kick transaction.
// Set size and send mode.
base[I2C_CNFG] = ((size - 1) << 1) | DEBOUNCE_CNT_4T | NEW_MASTER_FSM | CMD1_WRITE;
base[I2C_CNFG] = (base[I2C_CNFG] & 0xFFFFFDFF) | 0x200;
// Load configuration.
_i2c_load_cfg_wait(base);
u32 timeout = get_tmr_ms() + 1500;
while (base[I2C_STATUS] & 0x100)
// Initiate transaction on normal mode.
base[I2C_CNFG] = (base[I2C_CNFG] & 0xFFFFF9FF) | NORMAL_MODE_GO;
u64 timeout = get_tmr_ms() + 100;
while (base[I2C_STATUS] & I2C_STATUS_BUSY)
{
if (get_tmr_ms() > timeout)
return 0;
}
if (base[I2C_STATUS] << 28)
if (base[I2C_STATUS] & I2C_STATUS_NOACK)
return 0;
return 1;
}
static int _i2c_recv_pkt(u32 idx, u8 *buf, u32 size, u32 x)
static int _i2c_recv_single(u32 i2c_idx, u8 *buf, u32 size, u32 dev_addr)
{
if (size > 8)
if (size > 4)
return 0;
vu32 *base = (vu32 *)QueryIoMapping(i2c_addrs[I2C_5], 0x1000);
base[I2C_CMD_ADDR0] = (x << 1) | 1; // Set x (recv mode).
base[I2C_CNFG] = ((size - 1) << 1) | 0x2840; // Set size and recv mode.
_i2c_wait(base); // Kick transaction.
// Set device address and recv mode.
base[I2C_CMD_ADDR0] = (dev_addr << 1) | ADDR0_READ;
base[I2C_CNFG] = (base[I2C_CNFG] & 0xFFFFFDFF) | 0x200;
// Set size and recv mode.
base[I2C_CNFG] = ((size - 1) << 1) | DEBOUNCE_CNT_4T | NEW_MASTER_FSM | CMD1_READ;
u32 timeout = get_tmr_ms() + 1500;
while (base[I2C_STATUS] & 0x100)
// Load configuration.
_i2c_load_cfg_wait(base);
// Initiate transaction on normal mode.
base[I2C_CNFG] = (base[I2C_CNFG] & 0xFFFFF9FF) | NORMAL_MODE_GO;
u64 timeout = get_tmr_ms() + 100;
while (base[I2C_STATUS] & I2C_STATUS_BUSY)
{
if (get_tmr_ms() > timeout)
return 0;
}
if (base[I2C_STATUS] << 28)
if (base[I2C_STATUS] & I2C_STATUS_NOACK)
return 0;
u32 tmp = base[I2C_CMD_DATA1]; // Get LS value.
@ -106,50 +182,52 @@ void i2c_init()
{
vu32 *base = (vu32 *)QueryIoMapping(i2c_addrs[I2C_5], 0x1000);
base[I2C_CLK_DIVISOR_REGISTER] = 0x50001;
base[I2C_BUS_CLEAR_CONFIG] = 0x90003;
_i2c_wait(base);
base[I2C_CLK_DIVISOR] = (5 << 16) | 1; // SF mode Div: 6, HS mode div: 2.
base[I2C_BUS_CLEAR_CONFIG] = (9 << 16) | BC_TERMINATE | BC_ENABLE;
// Load configuration.
_i2c_load_cfg_wait(base);
for (u32 i = 0; i < 10; i++)
{
if (base[INTERRUPT_STATUS_REGISTER] & 0x800)
if (base[I2C_INT_STATUS] & BUS_CLEAR_DONE)
break;
}
(vu32)base[I2C_BUS_CLEAR_STATUS];
base[INTERRUPT_STATUS_REGISTER] = base[INTERRUPT_STATUS_REGISTER];
base[I2C_INT_STATUS] = base[I2C_INT_STATUS];
}
int i2c_send_buf_small(u32 idx, u32 x, u32 y, u8 *buf, u32 size)
int i2c_send_buf_small(u32 i2c_idx, u32 dev_addr, u32 reg, u8 *buf, u32 size)
{
u8 tmp[4];
if (size > 3)
return 0;
tmp[0] = y;
tmp[0] = reg;
memcpy(tmp + 1, buf, size);
return _i2c_send_pkt(idx, x, tmp, size + 1);
return _i2c_send_single(i2c_idx, dev_addr, tmp, size + 1);
}
int i2c_recv_buf_small(u8 *buf, u32 size, u32 idx, u32 x, u32 y)
int i2c_recv_buf_small(u8 *buf, u32 size, u32 i2c_idx, u32 dev_addr, u32 reg)
{
int res = _i2c_send_pkt(idx, x, (u8 *)&y, 1);
int res = _i2c_send_single(i2c_idx, dev_addr, (u8 *)&reg, 1);
if (res)
res = _i2c_recv_pkt(idx, buf, size, x);
res = _i2c_recv_single(i2c_idx, buf, size, dev_addr);
return res;
}
int i2c_send_byte(u32 idx, u32 x, u32 y, u8 b)
int i2c_send_byte(u32 i2c_idx, u32 dev_addr, u32 reg, u8 val)
{
return i2c_send_buf_small(idx, x, y, &b, 1);
return i2c_send_buf_small(i2c_idx, dev_addr, reg, &val, 1);
}
u8 i2c_recv_byte(u32 idx, u32 x, u32 y)
u8 i2c_recv_byte(u32 i2c_idx, u32 dev_addr, u32 reg)
{
u8 tmp = 0;
i2c_recv_buf_small(&tmp, 1, idx, x, y);
i2c_recv_buf_small(&tmp, 1, i2c_idx, dev_addr, reg);
return tmp;
}

View file

@ -1,5 +1,6 @@
/*
* Copyright (c) 2018 naehrwert
* Copyright (c) 2020 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,
@ -26,21 +27,10 @@
#define I2C_5 4
#define I2C_6 5
#define I2C_CNFG 0x00
#define I2C_CMD_ADDR0 0x01
#define I2C_CMD_DATA1 0x03
#define I2C_CMD_DATA2 0x04
#define I2C_STATUS 0x07
#define INTERRUPT_STATUS_REGISTER 0x1A
#define I2C_CLK_DIVISOR_REGISTER 0x1B
#define I2C_BUS_CLEAR_CONFIG 0x21
#define I2C_BUS_CLEAR_STATUS 0x22
#define I2C_CONFIG_LOAD 0x23
void i2c_init();
int i2c_send_buf_small(u32 idx, u32 x, u32 y, u8 *buf, u32 size);
int i2c_recv_buf_small(u8 *buf, u32 size, u32 idx, u32 x, u32 y);
int i2c_send_byte(u32 idx, u32 x, u32 y, u8 b);
u8 i2c_recv_byte(u32 idx, u32 x, u32 y);
int i2c_send_buf_small(u32 i2c_idx, u32 dev_addr, u32 reg, u8 *buf, u32 size);
int i2c_recv_buf_small(u8 *buf, u32 size, u32 i2c_idx, u32 dev_addr, u32 reg);
int i2c_send_byte(u32 i2c_idx, u32 dev_addr, u32 reg, u8 val);
u8 i2c_recv_byte(u32 i2c_idx, u32 dev_addr, u32 reg);
#endif

View file

@ -51,6 +51,18 @@
#define PINMUX_AUX_GPIO_PE6 0x248
#define PINMUX_AUX_GPIO_PH6 0x250
#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 */
#define PINMUX_AUX_UARTX_TX(x) (0xE4 + 0x10 * (x))
#define PINMUX_AUX_UARTX_RX(x) (0xE8 + 0x10 * (x))
@ -78,6 +90,7 @@
#define PINMUX_OPEN_DRAIN (1 << 11)
#define PINMUX_SCHMT (1 << 12)
#define PINMUX_DRIVE_MASK (3 << 13)
#define PINMUX_DRIVE_1X (0 << 13)
#define PINMUX_DRIVE_2X (1 << 13)
#define PINMUX_DRIVE_3X (2 << 13)