GK SDK 源码库: XMIPCLinuxV100R005C00SPC030 (kernel/tools/open_source excluded)
This commit is contained in:
@@ -0,0 +1,462 @@
|
||||
/*
|
||||
* Copyright (c) LOTUS. All rights reserved.
|
||||
*/
|
||||
|
||||
static void 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);
|
||||
}
|
||||
|
||||
#ifdef MMC_SUPPORTS_TUNING
|
||||
static void 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);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void 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 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 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 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 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 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");
|
||||
}
|
||||
|
||||
#ifdef MMC_SUPPORTS_TUNING
|
||||
static void enable_edge_tuning(struct sdhci_host *host)
|
||||
{
|
||||
uintptr_t crg_addr;
|
||||
unsigned int reg;
|
||||
|
||||
crg_addr = (host->type == MMC_TYPE_MMC) ?
|
||||
REG_EMMC_SAMPLB_DLL_CTRL : REG_SDIO0_SAMPLB_DLL_CTRL;
|
||||
|
||||
reg = readl(crg_addr);
|
||||
reg &= ~((host->type == MMC_TYPE_MMC) ?
|
||||
EMMC_SAMPLB_DLL_CLK_MASK : SDIO_SAMPLB_DLL_CLK_MASK);
|
||||
|
||||
#ifdef CONFIG_LOTUS_FPGA
|
||||
/* fpga 1 -> 45 degree */
|
||||
reg |= ((host->type == MMC_TYPE_MMC) ?
|
||||
emmc_samplb_sel(2) : sdio_samplb_sel(2));
|
||||
#else
|
||||
/* soc 1-> 11.25 degree */
|
||||
reg |= ((host->type == MMC_TYPE_MMC) ?
|
||||
emmc_samplb_sel(8) : sdio_samplb_sel(8)); /* sel 8 */
|
||||
#endif
|
||||
|
||||
writel(reg, crg_addr);
|
||||
|
||||
reg = sdhci_readl(host, SDHCI_MULTI_CYCLE);
|
||||
reg |= SDHCI_EDGE_DETECT_EN;
|
||||
sdhci_writel(host, reg, SDHCI_MULTI_CYCLE);
|
||||
}
|
||||
|
||||
static void 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 select_sampl_phase(struct sdhci_host *host, unsigned int phase)
|
||||
{
|
||||
disable_card_clk(host);
|
||||
set_sampl_phase(host, phase);
|
||||
wait_sampl_dll_slave_ready(host);
|
||||
enable_card_clk(host);
|
||||
udelay(100); /* delay 100us */
|
||||
}
|
||||
|
||||
static int send_tuning(struct sdhci_host *host, u32 opcode, int* cmd_error)
|
||||
{
|
||||
int count, err;
|
||||
const int tuning_num = 1;
|
||||
|
||||
count = 0;
|
||||
do {
|
||||
err = mmc_send_tuning(host->mmc, opcode, NULL);
|
||||
if (err)
|
||||
break;
|
||||
|
||||
count++;
|
||||
} while (count < tuning_num);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int lotus_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;
|
||||
|
||||
wait_drv_dll_lock(host);
|
||||
enable_sampl_dll_slave(host);
|
||||
enable_sample(host);
|
||||
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++) {
|
||||
select_sampl_phase(host, index * EDGE_TUNING_PHASE_STEP);
|
||||
|
||||
err = mmc_send_tuning(host->mmc, opcode, NULL);
|
||||
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;
|
||||
}
|
||||
|
||||
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++) {
|
||||
select_sampl_phase(host, index % PHASE_SCALE);
|
||||
|
||||
err = send_tuning(host, opcode, NULL);
|
||||
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;
|
||||
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;
|
||||
}
|
||||
#endif
|
||||
|
||||
void sdhci_set_host_caps(struct sdhci_host *host)
|
||||
{
|
||||
host->host_caps = MMC_MODE_HS | MMC_MODE_HS_52MHz |
|
||||
MMC_MODE_DDR_52MHz | MMC_MODE_HS200 | MMC_MODE_4BIT;
|
||||
#ifdef CONFIG_MMC_8BIT
|
||||
host->host_caps |= MMC_MODE_8BIT;
|
||||
#endif
|
||||
#if !defined(CONFIG_AUTO_SD_UPDATE) && defined(CONFIG_TARGET_XMFALCON) && defined(CONFIG_MMC_HS400_SUPPORT)
|
||||
host->host_caps |= MMC_MODE_HS400 |
|
||||
MMC_MODE_8BIT;
|
||||
#if defined(CONFIG_MMC_HS400_ES_SUPPORT)
|
||||
host->host_caps |= MMC_MODE_HS400_ES;
|
||||
#endif
|
||||
#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 = lotus_mmc_set_clk;
|
||||
host->priv_init = lotus_mmc_priv_init;
|
||||
host->set_control_reg = lotus_mmc_set_ioconfig;
|
||||
#ifdef MMC_SUPPORTS_TUNING
|
||||
host->execute_tuning = lotus_mmc_exec_tuning;
|
||||
#endif
|
||||
#ifdef CONFIG_TARGET_XMORCA
|
||||
host->set_io_cfg = lotus_set_io_cfg;
|
||||
#endif
|
||||
sdhci_set_host_caps(host);
|
||||
add_sdhci(host, CONFIG_LOTUS_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 0 //FIXME: XXX
|
||||
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)
|
||||
{
|
||||
int i;
|
||||
|
||||
printf("Device: %s\n", mmc->cfg->name);
|
||||
printf("Manufacturer ID: %x\n", mmc->cid[0] >> 24);
|
||||
printf("OEM: %x\n", (mmc->cid[0] >> 8) & 0xffff);
|
||||
printf("Name: %c%c%c%c%c\n", mmc->cid[0] & 0xff,
|
||||
(mmc->cid[1] >> 24), (mmc->cid[1] >> 16) & 0xff,
|
||||
(mmc->cid[1] >> 8) & 0xff, mmc->cid[1] & 0xff);
|
||||
|
||||
printf("Bus Speed: %d\n", mmc->clock);
|
||||
#if CONFIG_IS_ENABLED(MMC_VERBOSE)
|
||||
printf("Mode: %s\n", mmc_mode_name(mmc->selected_mode));
|
||||
mmc_dump_capabilities("card capabilities", mmc->card_caps);
|
||||
mmc_dump_capabilities("host capabilities", mmc->host_caps);
|
||||
#endif
|
||||
printf("Rd Block Len: %d\n", mmc->read_bl_len);
|
||||
|
||||
printf("%s version %d.%d", IS_SD(mmc) ? "SD" : "MMC",
|
||||
EXTRACT_SDMMC_MAJOR_VERSION(mmc->version),
|
||||
EXTRACT_SDMMC_MINOR_VERSION(mmc->version));
|
||||
if (EXTRACT_SDMMC_CHANGE_VERSION(mmc->version) != 0)
|
||||
printf(".%d", EXTRACT_SDMMC_CHANGE_VERSION(mmc->version));
|
||||
printf("\n");
|
||||
|
||||
printf("High Capacity: %s\n", mmc->high_capacity ? "Yes" : "No");
|
||||
print_to_tool("Capacity: %lld\r\n", mmc->capacity);
|
||||
|
||||
printf("Bus Width: %d-bit%s\n", mmc->bus_width,
|
||||
mmc->ddr_mode ? " DDR" : "");
|
||||
|
||||
#if CONFIG_IS_ENABLED(MMC_WRITE)
|
||||
puts("Erase Group Size: ");
|
||||
print_size(((u64)mmc->erase_grp_size) << 9, "\n");
|
||||
#endif
|
||||
|
||||
if (!IS_SD(mmc) && mmc->version >= MMC_VERSION_4_41) {
|
||||
bool has_enh = (mmc->part_support & ENHNCD_SUPPORT) != 0;
|
||||
bool usr_enh = has_enh && (mmc->part_attr & EXT_CSD_ENH_USR);
|
||||
|
||||
#if CONFIG_IS_ENABLED(MMC_HW_PARTITIONING)
|
||||
puts("HC WP Group Size: ");
|
||||
print_size(((u64)mmc->hc_wp_grp_size) << 9, "\n");
|
||||
#endif
|
||||
|
||||
puts("User Capacity: ");
|
||||
print_size(mmc->capacity_user, usr_enh ? " ENH" : "");
|
||||
if (mmc->wr_rel_set & EXT_CSD_WR_DATA_REL_USR)
|
||||
puts(" WRREL\n");
|
||||
else
|
||||
putc('\n');
|
||||
if (usr_enh) {
|
||||
puts("User Enhanced Start: ");
|
||||
print_size(mmc->enh_user_start, "\n");
|
||||
puts("User Enhanced Size: ");
|
||||
print_size(mmc->enh_user_size, "\n");
|
||||
}
|
||||
puts("Boot Capacity: ");
|
||||
print_size(mmc->capacity_boot, has_enh ? " ENH\n" : "\n");
|
||||
puts("RPMB Capacity: ");
|
||||
print_size(mmc->capacity_rpmb, has_enh ? " ENH\n" : "\n");
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(mmc->capacity_gp); i++) {
|
||||
bool is_enh = has_enh &&
|
||||
(mmc->part_attr & EXT_CSD_ENH_GP(i));
|
||||
if (mmc->capacity_gp[i]) {
|
||||
printf("GP%i Capacity: ", i+1);
|
||||
print_size(mmc->capacity_gp[i],
|
||||
is_enh ? " ENH" : "");
|
||||
if (mmc->wr_rel_set & EXT_CSD_WR_DATA_REL_GP(i))
|
||||
puts(" WRREL\n");
|
||||
else
|
||||
putc('\n');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int lotus_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;
|
||||
|
||||
print_mmcinfo(mmc);
|
||||
|
||||
if (!IS_SD(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);
|
||||
}
|
||||
Reference in New Issue
Block a user