gk7205v200-uboot/drivers/mmc/bsp_sdhci.c

403 lines
9.4 KiB
C
Raw Normal View History

2025-08-07 17:13:54 +08:00
/*
* 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);
}