403 lines
9.4 KiB
C
Executable File
403 lines
9.4 KiB
C
Executable File
/*
|
|
* Copyright (c) Hunan Goke,Chengdu Goke,Shandong Goke. 2021. All rights reserved.
|
|
*/
|
|
|
|
static void bsp_enable_card_clk(struct sdhci_host *host)
|
|
{
|
|
u16 clk;
|
|
|
|
clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
|
|
clk |= SDHCI_CLOCK_CARD_EN;
|
|
sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
|
|
}
|
|
|
|
static void bsp_disable_card_clk(struct sdhci_host *host)
|
|
{
|
|
u16 clk;
|
|
|
|
clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
|
|
clk &= ~SDHCI_CLOCK_CARD_EN;
|
|
sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
|
|
}
|
|
|
|
static void bsp_enable_internal_clk(struct sdhci_host *host)
|
|
{
|
|
u16 clk;
|
|
u16 timeout = 20;
|
|
|
|
clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
|
|
clk |= SDHCI_CLOCK_INT_EN | SDHCI_CLOCK_PLL_EN;
|
|
sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
|
|
|
|
/* Wait max 20 ms */
|
|
clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
|
|
while (!(clk & SDHCI_CLOCK_INT_STABLE)) {
|
|
if (timeout == 0) {
|
|
printf("%s: Internal clock never stabilised.\n",
|
|
__func__);
|
|
return;
|
|
}
|
|
timeout--;
|
|
udelay(1000); /* delay 1000us */
|
|
clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
|
|
}
|
|
}
|
|
|
|
static void __maybe_unused bsp_disable_internal_clk(struct sdhci_host *host)
|
|
{
|
|
u16 clk;
|
|
|
|
clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
|
|
clk &= ~SDHCI_CLOCK_INT_EN;
|
|
sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
|
|
}
|
|
|
|
static void bsp_set_drv_phase(struct sdhci_host *host, unsigned int phase)
|
|
{
|
|
uintptr_t crg_addr;
|
|
unsigned int reg;
|
|
|
|
crg_addr = host->type == MMC_TYPE_MMC ?
|
|
REG_EMMC_DRV_DLL_CTRL : REG_SDIO0_DRV_DLL_CTRL;
|
|
reg = readl(crg_addr);
|
|
reg &= ~SDIO_DRV_PHASE_SEL_MASK;
|
|
reg |= sdio_drv_sel(phase);
|
|
writel(reg, crg_addr);
|
|
}
|
|
|
|
static void bsp_enable_sample(struct sdhci_host *host)
|
|
{
|
|
unsigned int reg;
|
|
|
|
reg = sdhci_readl(host, SDHCI_AT_CTRL);
|
|
reg |= SDHCI_SAMPLE_EN;
|
|
sdhci_writel(host, reg, SDHCI_AT_CTRL);
|
|
}
|
|
|
|
static void bsp_set_sampl_phase(struct sdhci_host *host, unsigned int phase)
|
|
{
|
|
unsigned int reg;
|
|
|
|
reg = sdhci_readl(host, SDHCI_AT_STAT);
|
|
reg &= ~SDHCI_PHASE_SEL_MASK;
|
|
reg |= phase;
|
|
sdhci_writel(host, reg, SDHCI_AT_STAT);
|
|
}
|
|
|
|
static void bsp_wait_sampl_dll_slave_ready(struct sdhci_host *host)
|
|
{
|
|
unsigned int reg;
|
|
unsigned int timeout = 20;
|
|
uintptr_t reg_addr;
|
|
|
|
reg_addr = host->type == MMC_TYPE_MMC ?
|
|
REG_EMMC_SAMPL_DLL_STATUS : REG_SDIO0_SAMPL_DLL_STATUS;
|
|
do {
|
|
reg = readl(reg_addr);
|
|
if (reg & SDIO_SAMPL_DLL_SLAVE_READY)
|
|
return;
|
|
|
|
udelay(1000); /* delay 1000us */
|
|
timeout--;
|
|
} while (timeout > 0);
|
|
|
|
printf("sdhci: SAMPL DLL slave not ready.\n");
|
|
}
|
|
|
|
static void bsp_enable_edge_tuning(struct sdhci_host *host)
|
|
{
|
|
unsigned int reg;
|
|
|
|
reg = readl(REG_EMMC_SAMPLB_DLL_CTRL);
|
|
reg &= ~SDIO_SAMPLB_DLL_CLK_MASK;
|
|
reg |= sdio_samplb_sel(8); /* sel 8 */
|
|
writel(reg, REG_EMMC_SAMPLB_DLL_CTRL);
|
|
|
|
reg = sdhci_readl(host, SDHCI_MULTI_CYCLE);
|
|
reg |= SDHCI_EDGE_DETECT_EN;
|
|
sdhci_writel(host, reg, SDHCI_MULTI_CYCLE);
|
|
}
|
|
|
|
static void bsp_disable_edge_tuning(struct sdhci_host *host)
|
|
{
|
|
unsigned int reg;
|
|
|
|
reg = sdhci_readl(host, SDHCI_MULTI_CYCLE);
|
|
reg &= ~SDHCI_EDGE_DETECT_EN;
|
|
sdhci_writel(host, reg, SDHCI_MULTI_CYCLE);
|
|
}
|
|
|
|
static void bsp_select_sampl_phase(struct sdhci_host *host, unsigned int phase)
|
|
{
|
|
bsp_disable_card_clk(host);
|
|
bsp_set_sampl_phase(host, phase);
|
|
bsp_wait_sampl_dll_slave_ready(host);
|
|
bsp_enable_card_clk(host);
|
|
udelay(1);
|
|
}
|
|
|
|
static int bsp_send_tuning(struct sdhci_host *host, u32 opcode)
|
|
{
|
|
int count, err;
|
|
const int tuning_num = 1;
|
|
|
|
count = 0;
|
|
do {
|
|
err = mmc_send_tuning(host->mmc, opcode);
|
|
if (err)
|
|
break;
|
|
|
|
count++;
|
|
} while (count < tuning_num);
|
|
|
|
return err;
|
|
}
|
|
|
|
static int bsp_mmc_exec_tuning(struct sdhci_host *host, unsigned int opcode)
|
|
{
|
|
unsigned int index, val;
|
|
unsigned int edge_p2f, edge_f2p, start, end, phase;
|
|
unsigned int fall, rise, fall_updat_flag;
|
|
unsigned int found;
|
|
unsigned int prev_found = 0;
|
|
int err;
|
|
int prev_err = 0;
|
|
unsigned short ctrl;
|
|
|
|
bsp_wait_drv_dll_lock(host);
|
|
bsp_enable_sampl_dll_slave(host);
|
|
bsp_enable_sample(host);
|
|
bsp_enable_edge_tuning(host);
|
|
host->is_tuning = 1;
|
|
|
|
start = 0;
|
|
end = PHASE_SCALE / EDGE_TUNING_PHASE_STEP;
|
|
|
|
edge_p2f = start;
|
|
edge_f2p = end;
|
|
for (index = 0; index <= end; index++) {
|
|
bsp_select_sampl_phase(host, index * EDGE_TUNING_PHASE_STEP);
|
|
|
|
err = mmc_send_tuning(host->mmc, opcode);
|
|
if (!err) {
|
|
val = sdhci_readl(host, SDHCI_MULTI_CYCLE);
|
|
found = val & SDHCI_FOUND_EDGE;
|
|
} else {
|
|
found = 1;
|
|
}
|
|
|
|
if (prev_found && !found)
|
|
edge_f2p = index;
|
|
else if (!prev_found && found)
|
|
edge_p2f = index;
|
|
|
|
if ((edge_p2f != start) && (edge_f2p != end))
|
|
break;
|
|
|
|
prev_found = found;
|
|
}
|
|
|
|
if ((edge_p2f == start) && (edge_f2p == end)) {
|
|
printf("sdhci: tuning failed! can not found edge!\n");
|
|
return -1;
|
|
}
|
|
|
|
bsp_disable_edge_tuning(host);
|
|
|
|
start = edge_p2f * EDGE_TUNING_PHASE_STEP;
|
|
end = edge_f2p * EDGE_TUNING_PHASE_STEP;
|
|
if (end <= start)
|
|
end += PHASE_SCALE;
|
|
|
|
fall = start;
|
|
rise = end;
|
|
fall_updat_flag = 0;
|
|
for (index = start; index <= end; index++) {
|
|
bsp_select_sampl_phase(host, index % PHASE_SCALE);
|
|
|
|
err = bsp_send_tuning(host, opcode);
|
|
if (err)
|
|
debug("sdhci: send tuning CMD%u fail! phase:%u err:%d\n",
|
|
opcode, index, err);
|
|
|
|
if (err && index == start) {
|
|
if (!fall_updat_flag) {
|
|
fall_updat_flag = 1;
|
|
fall = start;
|
|
}
|
|
} else {
|
|
if (!prev_err && err) {
|
|
if (!fall_updat_flag) {
|
|
fall_updat_flag = 1;
|
|
fall = index;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (prev_err && !err)
|
|
rise = index;
|
|
|
|
if (err && index == end)
|
|
rise = end;
|
|
|
|
prev_err = err;
|
|
}
|
|
|
|
phase = ((fall + rise) / 2 + PHASE_SCALE / 2) % PHASE_SCALE; /* 2 for cal average */
|
|
|
|
printf("sdhci: tuning done! valid phase shift [%u, %u] Final Phase:%u\n",
|
|
rise % PHASE_SCALE, fall % PHASE_SCALE, phase);
|
|
|
|
host->tuning_phase = phase;
|
|
bsp_select_sampl_phase(host, phase);
|
|
|
|
ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
|
|
ctrl |= SDHCI_CTRL_TUNED_CLK;
|
|
sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
|
|
host->is_tuning = 0;
|
|
|
|
return 0;
|
|
}
|
|
|
|
void sdhci_set_host_caps(struct sdhci_host *host)
|
|
{
|
|
host->host_caps = MMC_MODE_HS | MMC_MODE_HS_52MHz |
|
|
MMC_MODE_HS200_1_8V | MMC_MODE_4BIT;
|
|
|
|
#if !(defined(CONFIG_TARGET_GK7205V200) || defined(CONFIG_TARGET_GK7202V300))
|
|
host->host_caps |= MMC_MODE_HS400ES | MMC_MODE_HS400_1_8V |
|
|
MMC_MODE_8BIT;
|
|
#endif
|
|
}
|
|
|
|
int sdhci_add_port(int index, uintptr_t regbase, u32 type)
|
|
{
|
|
struct sdhci_host *host = NULL;
|
|
|
|
if (type == MMC_TYPE_MMC)
|
|
emmc_hardware_init();
|
|
else
|
|
sd_hardware_init();
|
|
|
|
host = calloc(1, sizeof(struct sdhci_host));
|
|
if (host == NULL) {
|
|
puts("sdhci_host malloc fail!\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
host->name = "sdhci";
|
|
host->index = index;
|
|
host->type = type;
|
|
host->ioaddr = (void *)regbase;
|
|
host->quirks = 0;
|
|
host->set_clock = bsp_mmc_set_clk;
|
|
host->priv_init = bsp_mmc_priv_init;
|
|
host->set_control_reg = bsp_mmc_set_ioconfig;
|
|
host->execute_tuning = bsp_mmc_exec_tuning;
|
|
sdhci_set_host_caps(host);
|
|
#ifdef CONFIG_SDHCI_ADMA
|
|
host->max_segs = 512; /* max seg 512 */
|
|
host->max_seg_size = 65536; /* max seg size 65536 */
|
|
#if CONFIG_PHYS_64BIT
|
|
host->desc_sz = sizeof(struct sdhci_adma2_64_desc);
|
|
#else
|
|
host->desc_sz = sizeof(struct sdhci_adma2_32_desc);
|
|
#endif
|
|
host->adma_table_sz = (host->max_segs + 2) * host->desc_sz; /* adma tables need 2 extra desc */
|
|
host->adma_table = malloc(ALIGN(host->adma_table_sz, CONFIG_SYS_CACHELINE_SIZE));
|
|
if (!host->adma_table) {
|
|
puts("adma table malloc fail!\n");
|
|
free(host);
|
|
return -ENOMEM;
|
|
}
|
|
host->adma_table += CONFIG_SYS_CACHELINE_SIZE -
|
|
((uintptr_t)host->adma_table &
|
|
(CONFIG_SYS_CACHELINE_SIZE - 1));
|
|
#endif
|
|
add_sdhci(host, CONFIG_GK_SDHCI_MAX_FREQ, MIN_FREQ);
|
|
return 0;
|
|
}
|
|
|
|
void sdhci_hs400_enhanced_stobe(struct mmc *mmc, bool enable)
|
|
{
|
|
struct sdhci_host *host = mmc->priv;
|
|
u32 ctrl;
|
|
|
|
ctrl = sdhci_readl(host, SDHCI_EMMC_CTRL);
|
|
if (enable)
|
|
ctrl |= SDHCI_ENH_STROBE_EN;
|
|
else
|
|
ctrl &= ~SDHCI_ENH_STROBE_EN;
|
|
|
|
sdhci_writel(host, ctrl, SDHCI_EMMC_CTRL);
|
|
|
|
#if defined(CONFIG_TARGET_GK7605V100) || defined(CONFIG_TARGET_GK7205V300)
|
|
ctrl = sdhci_readl(host, SDHCI_MULTI_CYCLE);
|
|
if (enable)
|
|
ctrl |= SDHCI_CMD_DLY_EN;
|
|
else
|
|
ctrl &= ~SDHCI_CMD_DLY_EN;
|
|
|
|
sdhci_writel(host, ctrl, SDHCI_MULTI_CYCLE);
|
|
#endif
|
|
}
|
|
|
|
static void print_mmcinfo(struct mmc *mmc)
|
|
{
|
|
printf("MMC/SD Card:\n");
|
|
printf(" MID: 0x%x\n", mmc->cid[0] >> 24); /* bit24 - 31 */
|
|
printf(" Read Block: %d Bytes\n", mmc->read_bl_len);
|
|
printf(" Write Block: %d Bytes\n", mmc->write_bl_len);
|
|
printf(" Chip Size: %s Bytes (%s)\n",
|
|
ultohstr(mmc->capacity),
|
|
mmc->high_capacity ? "High Capacity" : "Low Capacity");
|
|
printf(" Name: \"%c%c%c%c%c\"\n",
|
|
mmc->cid[0] & 0xff, /* bit0 - 7 */
|
|
(mmc->cid[1] >> 24), /* bit24 - 32 */
|
|
(mmc->cid[1] >> 16) & 0xff, /* bit16 - 23 */
|
|
(mmc->cid[1] >> 8) & 0xff, /* bit8 - 15 */
|
|
mmc->cid[1] & 0xff); /* bit0 - 7 */
|
|
|
|
printf(" Chip Type: %s\n"
|
|
" Version: %d.%d\n",
|
|
IS_SD(mmc) ? "SD" : "MMC",
|
|
EXTRACT_SDMMC_MAJOR_VERSION(mmc->version),
|
|
EXTRACT_SDMMC_MINOR_VERSION(mmc->version));
|
|
|
|
printf(" Speed: %sHz\n", ultohstr(mmc->clock));
|
|
printf(" Bus Width: %dbit\n", mmc->bus_width);
|
|
printf(" Mode: %s\n", mmc->strobe_enhanced ? "HS400ES" :
|
|
mmc->timing == MMC_TIMING_MMC_HS400 ? "HS400" :
|
|
mmc->timing == MMC_TIMING_MMC_HS200 ? "HS200" : "HS");
|
|
}
|
|
|
|
int bsp_mmc_init(int dev_num)
|
|
{
|
|
struct mmc *mmc = find_mmc_device(dev_num);
|
|
int ret;
|
|
|
|
if (mmc == NULL) {
|
|
printf("mmc device not found!!\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
ret = mmc_init(mmc);
|
|
if (ret)
|
|
return ret;
|
|
|
|
if (!IS_SD(mmc)) {
|
|
print_mmcinfo(mmc);
|
|
return mmc_set_boot_config(mmc);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void printf_mmc(int dev_num)
|
|
{
|
|
struct mmc *mmc = find_mmc_device(dev_num);
|
|
|
|
if (mmc != NULL)
|
|
print_mmcinfo(mmc);
|
|
}
|