| /* SPDX-License-Identifier: (GPL-2.0+ OR MIT) */ |
| /* |
| * drivers/mmc/aml_hs200_v3.c |
| * |
| * Copyright (C) 2020 Amlogic, Inc. All rights reserved. |
| * |
| */ |
| |
| #include <config.h> |
| #include <common.h> |
| #include <command.h> |
| #include <errno.h> |
| #include <mmc.h> |
| #include <part.h> |
| #include <malloc.h> |
| #include <linux/list.h> |
| #include <div64.h> |
| #include "mmc_private.h" |
| #include <asm/arch/sd_emmc.h> |
| #include <asm/arch/secure_apb.h> |
| #include <emmc_partitions.h> |
| #include <asm/cpu_id.h> |
| #include <linux/compat.h> |
| #include <asm/arch/timer.h> |
| #include <asm/arch/cpu_sdio.h> |
| |
| #define MMC_PATTERN_OFFSET ((SZ_1M * (36 + 3)) / 512) |
| #define MMC_MAGIC_OFFSET ((SZ_1M * (36 + 3)) / 512) |
| #define MMC_RANDOM_OFFSET ((SZ_1M * (36 + 3)) / 512) |
| |
| |
| #ifdef EMMC_DEBUG_ENABLE |
| #define emmc_debug(a...) printf(a); |
| #else |
| #define emmc_debug(a...) |
| #endif |
| #ifdef MMC_HS400_MODE |
| u64 align[10]; |
| |
| void print_all_reg(struct mmc *mmc) { |
| struct aml_card_sd_info *aml_priv = mmc->priv; |
| struct sd_emmc_global_regs *sd_emmc_regs = aml_priv->sd_emmc_reg; |
| |
| printf("sd_emmc_regs->gcfg is %x\n", sd_emmc_regs->gcfg); |
| printf("sd_emmc_regs->gclock is %x\n", sd_emmc_regs->gclock); |
| printf("sd_emmc_regs->gadjust is %x\n", sd_emmc_regs->gadjust); |
| printf("sd_emmc_regs->gdelay is %x\n", sd_emmc_regs->gdelay); |
| printf("sd_emmc_regs->gdelay1 is %x\n", sd_emmc_regs->gdelay1); |
| printf("sd_emmc_regs->gintf3 is %x\n", sd_emmc_regs->gintf3); |
| return; |
| } |
| |
| static int fbinary(u64 x) |
| { |
| int i; |
| for (i = 0; i < 64; i++) { |
| if ((x >> i) & 0x1) |
| return i; |
| } |
| return -1; |
| } |
| // |
| //static int mmc_get_status(struct mmc *mmc, int timeout) |
| //{ |
| // struct mmc_cmd cmd; |
| // int err, retries = 1; |
| // |
| // cmd.cmdidx = MMC_CMD_SEND_STATUS; |
| // cmd.resp_type = MMC_RSP_R1; |
| // if (!mmc_host_is_spi(mmc)) |
| // cmd.cmdarg = mmc->rca << 16; |
| // |
| // do { |
| // err = mmc_send_cmd(mmc, &cmd, NULL); |
| // if (!err) { |
| // if ((cmd.response[0] & MMC_STATUS_RDY_FOR_DATA) && |
| // (cmd.response[0] & MMC_STATUS_CURR_STATE) != |
| // MMC_STATE_PRG) |
| // break; |
| // else if (cmd.response[0] & MMC_STATUS_MASK) { |
| // return COMM_ERR; |
| // } |
| // } else if (--retries < 0) |
| // return err; |
| // udelay(1000); |
| // } while (timeout--); |
| // |
| // if (timeout <= 0) { |
| // return TIMEOUT; |
| // } |
| // if (cmd.response[0] & MMC_STATUS_SWITCH_ERROR) |
| // return SWITCH_ERR; |
| // |
| // return 0; |
| //} |
| |
| static int mmc_read_single_block(struct mmc *mmc, void *dst, lbaint_t start) |
| { |
| struct mmc_cmd cmd; |
| struct mmc_data data; |
| int ret = 0; |
| |
| cmd.cmdidx = MMC_CMD_READ_SINGLE_BLOCK; |
| |
| if (mmc->high_capacity) |
| cmd.cmdarg = start; |
| else |
| cmd.cmdarg = start * mmc->read_bl_len; |
| |
| cmd.resp_type = MMC_RSP_R1; |
| |
| data.dest = dst; |
| data.blocks = 1; |
| data.blocksize = mmc->read_bl_len; |
| data.flags = MMC_DATA_READ; |
| |
| ret = mmc_send_cmd(mmc, &cmd, &data); |
| return ret; |
| } |
| |
| static long long para_checksum_calc(struct tuning_para *para) |
| { |
| int i = 0; |
| int size = sizeof(struct tuning_para) - 6 * sizeof(unsigned int); |
| unsigned int *buffer; |
| long long checksum = 0; |
| |
| size = size >> 2; |
| buffer = (unsigned int *)para; |
| while (i < size) |
| checksum += buffer[i++]; |
| |
| return checksum; |
| } |
| |
| /*set para on controller register*/ |
| int aml_set_tuning_para(struct mmc *mmc) |
| { |
| struct aml_card_sd_info *aml_priv = mmc->priv; |
| struct tuning_para *para = aml_priv->para; |
| struct sd_emmc_global_regs *sd_emmc_regs = aml_priv->sd_emmc_reg; |
| int temp_index; |
| u32 delay1, delay2, intf3; |
| |
| if (para->update == 1) |
| return 0; |
| |
| temp_index = para->temperature / 10; |
| delay1 = para->hs4[temp_index].delay1; |
| delay2 = para->hs4[temp_index].delay2; |
| intf3 = para->hs4[temp_index].intf3; |
| |
| sd_emmc_regs->gdelay = delay1; |
| sd_emmc_regs->gdelay1 = delay2; |
| sd_emmc_regs->gintf3 = intf3; |
| sd_emmc_regs->gstatus = 0x1ff0000; |
| sd_emmc_regs->girq_en = 0x23fff; |
| return 1; |
| } |
| |
| /*save parameter on mmc_host pdata*/ |
| void aml_save_tuning_para(struct mmc *mmc) |
| { |
| long long checksum; |
| int temp_index; |
| struct aml_card_sd_info *aml_priv = mmc->priv; |
| struct sd_emmc_global_regs *sd_emmc_regs = aml_priv->sd_emmc_reg; |
| struct tuning_para *para = aml_priv->para; |
| |
| u32 delay1 = sd_emmc_regs->gdelay; |
| u32 delay2 = sd_emmc_regs->gdelay1; |
| u32 intf3 = sd_emmc_regs->gintf3; |
| |
| temp_index = para->temperature / 10; |
| if (temp_index < 0 || temp_index > 6) { |
| para->update = 0; |
| printf("temperature is out of index, can't be saved\n"); |
| return; |
| } |
| |
| para->hs4[temp_index].delay1 = delay1; |
| para->hs4[temp_index].delay2 = delay2; |
| para->hs4[temp_index].intf3 = intf3; |
| para->hs4[temp_index].flag = TUNED_FLAG; |
| |
| para->magic = TUNED_MAGIC; /*E~K\0*/ |
| para->version = TUNED_VERSION; |
| |
| checksum = para_checksum_calc(para); |
| para->checksum = checksum; |
| return; |
| } |
| |
| #ifdef MMC_WRITE_TUNING_PARA |
| /*write tuning para on emmc, the offset is 0x14400*/ |
| static int aml_write_tuning_para(struct mmc *mmc) |
| { |
| unsigned int size; |
| struct aml_card_sd_info *aml_priv = mmc->priv; |
| struct tuning_para *parameter = aml_priv->para; |
| unsigned int *buf; |
| int para_size; |
| int blocks; |
| int cnt; |
| int offset; |
| |
| offset = MMC_TUNING_OFFSET; |
| para_size = sizeof(struct tuning_para); |
| blocks = (para_size - 1) / 512 + 1; |
| size = blocks << 9; |
| |
| buf = (unsigned int *)malloc(size); |
| if (!buf) { |
| printf("malloc buf failed\n"); |
| return -1; |
| } |
| |
| memset(buf, 0, size); |
| memcpy(buf, parameter, sizeof(struct tuning_para)); |
| |
| cnt = mmc->block_dev.block_write(1, offset, blocks, buf); |
| if (cnt != blocks) |
| printf("write parameter failed\n"); |
| |
| free(buf); |
| return 0; |
| } |
| #endif |
| |
| int aml_emmc_hs200_tl1(struct mmc *mmc) |
| { |
| struct aml_card_sd_info *aml_priv = mmc->priv; |
| struct sd_emmc_global_regs *sd_emmc_regs = aml_priv->sd_emmc_reg; |
| struct sd_emmc_clock *vclkc = (struct sd_emmc_clock *)&sd_emmc_regs->gclock; |
| |
| u32 clk_bak = 0; |
| u32 delay2 = 0, count = 0; |
| int i, j, err = 0; |
| int retry_times = 1; |
| |
| mmc->refix = 1; |
| // clk_bak = vclkc; |
| clk_bak = 0x10100002; |
| |
| sd_emmc_regs->gclock = 0x10100004; |
| sd_emmc_regs->gintf3 |= 1 << 22; |
| sd_emmc_regs->gcfg = 0x804892; |
| sd_emmc_regs->gintf3 = 0x4000c8; |
| |
| printf("[%s][%d] clk config:0x%x\n", |
| __func__, __LINE__, sd_emmc_regs->gclock); |
| for (i = 0; i < 63; i++) { |
| delay2 += (1 << 24); |
| sd_emmc_regs->gdelay1 = delay2; |
| retry: |
| err = emmc_eyetest_log(mmc, 9); |
| if (err) |
| continue; |
| count = fbinary(align[9]); |
| if (((count >= 14) && (count <= 20)) |
| || ((count >= 48) && (count <= 54))) { |
| if (retry_times != 3) { |
| retry_times++; |
| goto retry; |
| } else |
| break; |
| } |
| } |
| |
| if (i == 63) { |
| for (j = 0; j < 6; j++) { |
| vclkc->tx_delay++; |
| err = emmc_eyetest_log(mmc, 9); |
| if (err) |
| continue; |
| count = fbinary(align[9]); |
| if (((count >= 14) && (count <= 20)) |
| || ((count >= 48) && (count <= 54))) |
| break; |
| } |
| } |
| |
| aml_priv->cmd_c = (delay2 >> 24); |
| sd_emmc_regs->gdelay1 = 0; |
| sd_emmc_regs->gclock = clk_bak; |
| delay2 = 0; |
| for (i = 0; i < 63; i++) { |
| retry_times = 0; |
| delay2 += (1 << 24); |
| sd_emmc_regs->gdelay1 = delay2; |
| retry1: |
| err = emmc_eyetest_log(mmc, 9); |
| if (err) |
| continue; |
| |
| count = fbinary(align[9]); |
| if (count >= 8 && count <= 56) { |
| if (retry_times != 3) { |
| retry_times++; |
| goto retry1; |
| } else |
| break; |
| } |
| } |
| |
| printf("[%s][%d] clk config:0x%x\n", |
| __func__, __LINE__, sd_emmc_regs->gclock); |
| mmc->refix = 0; |
| return 0; |
| } |
| |
| void select_clock_src(void) { |
| void* buf; |
| unsigned long addr; |
| int size; |
| u64 writeval; |
| unsigned int clock_src = 0; |
| unsigned long byte; |
| cpu_id_t cpu_id = get_cpu_id(); |
| |
| if (cpu_id.family_id == MESON_CPU_MAJOR_ID_TL1 |
| || cpu_id.family_id == MESON_CPU_MAJOR_ID_T5) |
| clock_src = 0x880; |
| else if (cpu_id.family_id >= MESON_CPU_MAJOR_ID_G12B) |
| clock_src = 0x880; |
| else |
| clock_src = 0xe80; |
| |
| size = 4; |
| byte = size; |
| addr = CLKSRC_BASE + 0x25c; |
| writeval = clock_src; |
| buf = map_sysmem(addr, byte); |
| *((u32 *)buf) = (u32)writeval; |
| unmap_sysmem(buf); |
| |
| writel(0x17ff, PAD_PULL_UP_REG0); |
| writel(0xffffffff, PAD_DS_REG0A); |
| } |
| |
| int mmc_set_hs200_mode(struct mmc *mmc) |
| { |
| struct aml_card_sd_info *aml_priv = mmc->priv; |
| int err = 0; |
| |
| if (aml_priv->sd_emmc_port != 2) |
| return err; |
| |
| err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, |
| EXT_CSD_BUS_WIDTH, 2); |
| if (err) { |
| printf("mmc switch buswidth failed\n"); |
| return err; |
| } |
| |
| mmc_set_bus_width(mmc, MMC_MODE_8BIT); |
| |
| err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, |
| EXT_CSD_HS_TIMING, EXT_CSD_TIMING_HS200); |
| if (err) { |
| printf("mmc switch HS200 failed\n"); |
| return err; |
| } |
| |
| return err; |
| } |
| |
| static int emmc_send_deselect(struct mmc *mmc) |
| { |
| struct mmc_cmd cmd = {0}; |
| u32 err = 0; |
| |
| cmd.cmdidx = MMC_CMD_SELECT_CARD; |
| cmd.cmdarg = 0; |
| cmd.resp_type = MMC_RSP_NONE; |
| |
| err = mmc_send_cmd(mmc, &cmd, NULL); |
| if (err) { |
| printf("[%s][%d] cmd:0x%x send error\n", |
| __func__, __LINE__, cmd.cmdidx); |
| return err; |
| } |
| return err; |
| } |
| |
| static int emmc_send_select(struct mmc *mmc) |
| { |
| struct mmc_cmd cmd = {0}; |
| u32 err = 0; |
| |
| cmd.cmdidx = MMC_CMD_SELECT_CARD; |
| cmd.resp_type = MMC_RSP_R1; |
| cmd.cmdarg = 1 << 16; |
| err = mmc_send_cmd(mmc, &cmd, NULL); |
| |
| // if (err) { |
| // printf("[%s][%d] cmd:0x%x send error\n", |
| // __func__, __LINE__, cmd.cmdidx); |
| // return err; |
| // } |
| return err; |
| } |
| |
| static int emmc_send_cid(struct mmc *mmc) |
| { |
| struct mmc_cmd cmd = {0}; |
| u32 err = 0; |
| |
| cmd.cmdidx = MMC_CMD_SEND_CID; |
| cmd.cmdarg = (1 << 16); |
| cmd.resp_type = MMC_RSP_R2; |
| |
| err = mmc_send_cmd(mmc, &cmd, NULL); |
| // if (err) { |
| // printf("[%s][%d] cmd:0x%x send error\n", |
| // __func__, __LINE__, cmd.cmdidx); |
| // return err; |
| // } |
| return err; |
| } |
| |
| static int aml_sd_emmc_cmd_v3(struct mmc *mmc) |
| { |
| int i; |
| int ret; |
| ret = mmc_send_status(mmc, 1000); |
| ret |= emmc_send_deselect(mmc); |
| for (i = 0; i < 2; i++) |
| ret |= emmc_send_cid(mmc); |
| ret |= emmc_send_select(mmc); |
| return ret; |
| } |
| |
| static int emmc_detect_base_line(u64 *arr) |
| { |
| u32 i = 0, first[10] = {0}; |
| u32 max = 0, l_max = 0xff; |
| for (i = 0; i < 8; i++) { |
| first[i] = fbinary(arr[i]); |
| if (first[i] > max) { |
| l_max = i; |
| max = first[i]; |
| } |
| } |
| printf("%s [%d] detect line:%d, max: %u\n", |
| __func__, __LINE__, l_max, max); |
| return max; |
| } |
| |
| /**************** start all data align ********************/ |
| static int emmc_all_data_line_alignment(struct mmc *mmc) |
| { |
| struct aml_card_sd_info *aml_priv = mmc->priv; |
| struct sd_emmc_global_regs *sd_emmc_regs = aml_priv->sd_emmc_reg; |
| u32 delay1 = 0, delay2 = 0; |
| int result; |
| int temp = 0, base_line = 0, line_x = 0; |
| |
| base_line = emmc_detect_base_line(align); |
| for (line_x = 0; line_x < 9; line_x++) { |
| if (line_x == 8) |
| continue; |
| if (align[line_x] & 0xf) |
| continue; |
| temp = fbinary(align[line_x]); |
| result = base_line - temp; |
| emmc_debug("*****line_x: %d, result: %d\n", |
| line_x, result); |
| if (line_x < 5) |
| delay1 |= result << (6 * line_x); |
| else |
| delay2 |= result << (6 * (line_x - 5)); |
| } |
| sd_emmc_regs->gdelay += delay1; |
| sd_emmc_regs->gdelay1 += delay2; |
| emmc_debug("gdelay: 0x%x, gdelay1: 0x%x\n", |
| sd_emmc_regs->gdelay, sd_emmc_regs->gdelay1); |
| return 0; |
| } |
| |
| int emmc_eyetest_log(struct mmc *mmc, u32 line_x) |
| { |
| struct aml_card_sd_info *aml_priv = mmc->priv; |
| struct sd_emmc_global_regs *sd_emmc_regs = aml_priv->sd_emmc_reg; |
| |
| u32 adjust = sd_emmc_regs->gadjust; |
| struct sd_emmc_adjust_v3 *gadjust = |
| (struct sd_emmc_adjust_v3 *)&adjust; |
| u32 eyetest_log = 0; |
| struct eyetest_log *geyetest_log = (struct eyetest_log *)&(eyetest_log); |
| u32 eyetest_out0 = 0, eyetest_out1 = 0; |
| u32 intf3 = sd_emmc_regs->gintf3; |
| struct intf3 *gintf3 = (struct intf3 *)&(intf3); |
| /*u32 vcfg = sd_emmc_regs->gcfg;*/ |
| int retry = 3; |
| u64 tmp = 0; |
| unsigned long phy_addr = 0x1080000; |
| void *addr = (void*)phy_addr; |
| int i; |
| lbaint_t start = 1; |
| //((SZ_1M*(26+3))/ 512); |
| |
| /****** calculate line_x ***************************/ |
| /******* init eyetest register ************************/ |
| gadjust->cali_enable = 1; |
| gadjust->cali_sel = line_x; |
| sd_emmc_regs->gadjust = adjust; |
| if (line_x < 9) |
| gintf3->eyetest_exp = 7; |
| else |
| gintf3->eyetest_exp = 3; |
| RETRY: |
| |
| gintf3->eyetest_on = 1; |
| sd_emmc_regs->gintf3 = intf3; |
| |
| /*****test start*************/ |
| udelay(5); |
| if (line_x < 9) |
| for (i = 0; i< 2; i++) |
| mmc_bread(1, start, 256, addr); |
| else |
| aml_sd_emmc_cmd_v3(mmc); |
| udelay(1); |
| eyetest_log = sd_emmc_regs->geyetest_log; |
| |
| if (!(geyetest_log->eyetest_done & 0x1)) { |
| printf("testint eyetest times: 0x%x, out: 0x%x, 0x%x\n", |
| geyetest_log->eyetest_times, |
| eyetest_out0, eyetest_out1); |
| gintf3->eyetest_on = 0; |
| sd_emmc_regs->gintf3 = intf3; |
| retry--; |
| |
| if (retry == 0) { |
| printf("[%s][%d] retry eyetest failed\n", |
| __func__, __LINE__); |
| return 1; |
| } |
| goto RETRY; |
| } |
| eyetest_out0 = sd_emmc_regs->geyetest_out0; |
| eyetest_out1 = sd_emmc_regs->geyetest_out1; |
| gintf3->eyetest_on = 0; |
| sd_emmc_regs->gintf3 = intf3; |
| align[line_x] = ((tmp | eyetest_out1) << 32) | eyetest_out0; |
| printf("d1:0x%x,d2:0x%x,u64eyet:0x%016llx,l_x:%d\n", |
| sd_emmc_regs->gdelay, sd_emmc_regs->gdelay1, |
| align[line_x], line_x); |
| return 0; |
| } |
| |
| static int emmc_ds_data_alignment(struct mmc *mmc) |
| { |
| struct aml_card_sd_info *aml_priv = mmc->priv; |
| struct sd_emmc_global_regs *sd_emmc_regs = aml_priv->sd_emmc_reg; |
| u32 delay1 = sd_emmc_regs->gdelay; |
| u32 delay2 = sd_emmc_regs->gdelay1; |
| int i, line_x, temp = 0; |
| |
| for (line_x = 0; line_x < 8; line_x++) { |
| temp = fbinary(align[line_x]); |
| if (temp <= 4) |
| continue; |
| for (i = 0; i < 64; i++) { |
| emmc_debug("i = %d,delay1:0x%x,delay2:0x%x\n", |
| i, sd_emmc_regs->gdelay, |
| sd_emmc_regs->gdelay1); |
| if (line_x < 5) |
| delay1 += 1<<(6*line_x); |
| else |
| delay2 += 1<<(6*(line_x-5)); |
| sd_emmc_regs->gdelay = delay1; |
| sd_emmc_regs->gdelay1 = delay2; |
| emmc_eyetest_log(mmc, line_x); |
| if (align[line_x] & 0xf0) |
| break; |
| } |
| if (i == 64) { |
| printf("%s [%d] Don't find line delay which aligned with DS\n", |
| __func__, __LINE__); |
| return 1; |
| } |
| } |
| return 0; |
| } |
| |
| static unsigned int get_emmc_cmd_win(struct mmc *mmc) |
| { |
| struct aml_card_sd_info *aml_priv = mmc->priv; |
| struct sd_emmc_global_regs *sd_emmc_regs = aml_priv->sd_emmc_reg; |
| u32 delay2 = sd_emmc_regs->gdelay1; |
| u32 max = 0, i, temp; |
| u32 str[64] = {0}; |
| int best_start = -1, best_size = -1; |
| int cur_start = -1, cur_size = 0; |
| |
| for (i = 0; i < 64; i++) { |
| delay2 &= ~(0x3f << 24); |
| delay2 |= (i << 24); |
| sd_emmc_regs->gdelay1 = delay2; |
| emmc_eyetest_log(mmc, 9); |
| temp = fbinary(align[9]); |
| str[i] = temp; |
| if (max < temp) |
| max = temp; |
| } |
| for (i = 0; i < 64; i++) { |
| if (str[i] >= 4) { |
| if (cur_start < 0) |
| cur_start = i; |
| cur_size++; |
| } else { |
| if (cur_start >= 0) { |
| if (best_start < 0) { |
| best_start = cur_start; |
| best_size = cur_size; |
| } else { |
| if (best_size < cur_size) { |
| best_start = cur_start; |
| best_size = cur_size; |
| } |
| } |
| cur_start = -1; |
| cur_size = 0; |
| } |
| } |
| } |
| if (cur_start >= 0) { |
| if (best_start < 0) { |
| best_start = cur_start; |
| best_size = cur_size; |
| } else if (best_size < cur_size) { |
| best_start = cur_start; |
| best_size = cur_size; |
| } |
| cur_start = -1; |
| cur_size = -1; |
| } |
| delay2 &= ~(0x3f << 24); |
| delay2 |= ((best_start + (best_size * 3 / 4)) << 24); |
| sd_emmc_regs->gdelay1 = delay2; |
| emmc_eyetest_log(mmc, 9); |
| return max; |
| } |
| |
| /* first step*/ |
| static int emmc_ds_core_align(struct mmc *mmc) |
| { |
| struct aml_card_sd_info *aml_priv = mmc->priv; |
| struct sd_emmc_global_regs *sd_emmc_regs = aml_priv->sd_emmc_reg; |
| |
| u32 delay1 = sd_emmc_regs->gdelay; |
| u32 delay2 = sd_emmc_regs->gdelay1; |
| u32 delay2_bak = delay2; |
| u32 count = 0, ds_count = 0, cmd_count = 0; |
| ds_count = fbinary(align[8]); |
| if (ds_count == 0) |
| if ((align[8] & 0x1e0) == 0) |
| goto out_cmd; |
| emmc_debug("ds_count:%d,delay1:0x%x,delay2:0x%x\n", |
| ds_count, sd_emmc_regs->gdelay, sd_emmc_regs->gdelay1); |
| if (ds_count < 20) { |
| delay2 += ((20 - ds_count) << 18); |
| sd_emmc_regs->gdelay1 = delay2; |
| } else |
| sd_emmc_regs->gdelay1 += (1<<18); |
| emmc_eyetest_log(mmc, 8); |
| while (!(align[8] & 0xf)) { |
| sd_emmc_regs->gdelay1 += (1<<18); |
| emmc_eyetest_log(mmc, 8); |
| } |
| delay1 = sd_emmc_regs->gdelay; |
| delay2 = sd_emmc_regs->gdelay1; |
| count = ((delay2>>18) & 0x3f) - ((delay2_bak>>18) & 0x3f); |
| |
| delay1 += (count<<0)|(count<<6)|(count<<12)|(count<<18)|(count<<24); |
| delay2 += (count<<0)|(count<<6)|(count<<12); |
| |
| sd_emmc_regs->gdelay = delay1; |
| sd_emmc_regs->gdelay1 = delay2; |
| out_cmd: |
| cmd_count = get_emmc_cmd_win(mmc); |
| printf("ds_count %u, count: %d, cmd_count:%u\n", ds_count, count, cmd_count); |
| |
| return 0; |
| } |
| |
| void update_all_line_eyetest(struct mmc *mmc) |
| { |
| int line_x; |
| |
| for (line_x = 0; line_x < 10; line_x++) { |
| emmc_eyetest_log(mmc, line_x); |
| } |
| } |
| |
| |
| /* |
| static int emmc_test_bus(struct mmc *mmc) |
| { |
| unsigned long phy_addr = 0x1080000; |
| void * addr = (void*) phy_addr; |
| int ret = 0; |
| ret = mmc_bread(1, MMC_PATTERN_OFFSET, 40, addr); |
| if (ret) |
| return ret; |
| ret = mmc_bread(1, MMC_MAGIC_OFFSET, 80, addr); |
| if (ret) |
| return ret; |
| ret = mmc_bread(1, MMC_RANDOM_OFFSET, 40, addr); |
| if (ret) |
| return ret; |
| return ret; |
| } |
| */ |
| int emmc_ds_manual_sht(struct mmc *mmc) |
| { |
| struct aml_card_sd_info *aml_priv = mmc->priv; |
| struct sd_emmc_global_regs *sd_emmc_regs = aml_priv->sd_emmc_reg; |
| |
| u32 intf3 = sd_emmc_regs->gintf3; |
| struct intf3 *gintf3 = (struct intf3 *)&(intf3); |
| int i, cnt = 0; |
| int match[64]; |
| int best_start = -1, best_size = -1; |
| int cur_start = -1, cur_size = 0; |
| unsigned long phy_addr = 0x1080000; |
| void * addr = (void*) phy_addr; |
| u32 begin_time, end_time; |
| u32 capacity = mmc->capacity >> 9; |
| u32 offset; |
| |
| // sd_emmc_regs->gintf3 = 0x4000c8; |
| sd_emmc_regs->gintf3 = 0x400000; |
| intf3 = sd_emmc_regs->gintf3; |
| begin_time = get_time(); |
| gintf3->ds_sht_m = 0; |
| offset = (u32)rand() % capacity; |
| for (i = 0; i < 64; i++) { |
| cnt = mmc_bread(1, offset, 200, addr); |
| if (cnt == 200) |
| match[i] = 0; |
| else |
| match[i] = 1; |
| gintf3->ds_sht_m += 1; |
| sd_emmc_regs->gintf3 = intf3; |
| } |
| end_time = get_time(); |
| printf("spend time is %dus.\n", end_time - begin_time); |
| |
| for (i = 0; i < 64; i++) { |
| if (match[i] == 0) { |
| if (cur_start < 0) |
| cur_start = i; |
| cur_size++; |
| } else { |
| if (cur_start >= 0) { |
| if (best_start < 0) { |
| best_start = cur_start; |
| best_size = cur_size; |
| } else { |
| if (best_size < cur_size) { |
| best_start = cur_start; |
| best_size = cur_size; |
| } |
| } |
| cur_start = -1; |
| cur_size = 0; |
| } |
| } |
| } |
| if (cur_start >= 0) { |
| if (best_start < 0) { |
| best_start = cur_start; |
| best_size = cur_size; |
| } else if (best_size < cur_size) { |
| best_start = cur_start; |
| best_size = cur_size; |
| } |
| cur_start = -1; |
| cur_size = -1; |
| } |
| |
| gintf3->ds_sht_m = best_start + best_size / 2; |
| sd_emmc_regs->gintf3 = intf3; |
| aml_priv->win_start = best_start; |
| printf("ds_sht:%u, window:%d, intf3:0x%x, clock:0x%x, cfg: 0x%x\n", |
| gintf3->ds_sht_m, best_size, |
| sd_emmc_regs->gintf3, |
| sd_emmc_regs->gclock, |
| sd_emmc_regs->gcfg); |
| return best_size; |
| } |
| |
| static ulong mmc_write_single_blocks(struct mmc *mmc, lbaint_t start, |
| const void *src) |
| { |
| struct mmc_cmd cmd; |
| struct mmc_data data; |
| int timeout = 1000; |
| int ret; |
| |
| udelay(100); |
| if ((start + 1) > mmc->block_dev.lba) { |
| printf("MMC: block number 0x" LBAF " exceeds max(0x" LBAF ")\n", |
| start + 1, mmc->block_dev.lba); |
| return 0; |
| } |
| |
| cmd.cmdidx = MMC_CMD_WRITE_SINGLE_BLOCK; |
| |
| if (mmc->high_capacity) |
| cmd.cmdarg = start; |
| else |
| cmd.cmdarg = start * mmc->write_bl_len; |
| |
| cmd.resp_type = MMC_RSP_R1; |
| |
| data.src = src; |
| data.blocks = 1; |
| data.blocksize = mmc->write_bl_len; |
| data.flags = MMC_DATA_WRITE; |
| |
| ret = mmc_send_cmd(mmc, &cmd, &data); |
| if (ret) { |
| goto _out; |
| } |
| |
| _out: |
| /* Waiting for the ready status */ |
| if (mmc_send_status(mmc, timeout)) |
| return 0; |
| if (ret) |
| return 0; |
| return 1; |
| } |
| |
| int auto_set_tx_delay(struct mmc *mmc) |
| { |
| struct aml_card_sd_info *aml_priv = mmc->priv; |
| struct sd_emmc_global_regs *sd_emmc_regs = aml_priv->sd_emmc_reg; |
| |
| u32 clock = sd_emmc_regs->gclock; |
| struct sd_emmc_clock *gclock = (struct sd_emmc_clock *)&(clock); |
| int i; |
| int match[64]; |
| int best_start = -1, best_size = -1; |
| int cur_start = -1, cur_size = 0; |
| unsigned long phy_addr = 0x1080000; |
| void * addr = (void*) phy_addr; |
| lbaint_t start = ((SZ_1M*(36+3))/512); |
| int success_count=0; |
| int count; |
| |
| if (mmc_bread(1, start, 1, addr) != 1) { |
| return 0; |
| } |
| |
| gclock->tx_delay = 0; |
| printf("tx sample result:"); |
| for (i = 0; i < 64; i++) { |
| gclock->tx_delay = i; |
| sd_emmc_regs->gclock = clock; |
| success_count = 0; |
| |
| for (count = 0; count < 256; count++) { |
| if (mmc_write_single_blocks(mmc, start, addr)) |
| success_count++; |
| } |
| if (success_count == 256) |
| printf("Y"); |
| else |
| printf("N"); |
| if (success_count == 256) |
| match[i] = 0; |
| else |
| match[i] = -1; |
| } |
| printf("\n"); |
| for (i = 0; i < 64; i++) { |
| if (match[i] == 0) { |
| if (cur_start < 0) |
| cur_start = i; |
| cur_size++; |
| } else { |
| if (cur_start >= 0) { |
| if (best_start < 0) { |
| best_start = cur_start; |
| best_size = cur_size; |
| } else { |
| if (best_size < cur_size) { |
| best_start = cur_start; |
| best_size = cur_size; |
| } |
| } |
| cur_start = -1; |
| cur_size = 0; |
| } |
| } |
| } |
| if (cur_start >= 0) { |
| if (best_start < 0) { |
| best_start = cur_start; |
| best_size = cur_size; |
| } else if (best_size < cur_size) { |
| best_start = cur_start; |
| best_size = cur_size; |
| } |
| cur_start = -1; |
| cur_size = -1; |
| } |
| if (best_size == -1) { |
| printf("meson-mmc: can not find tx_delay\n"); |
| return 0; |
| } |
| gclock->tx_delay = best_start + best_size / 2; |
| sd_emmc_regs->gclock = clock; |
| printf("meson-mmc: tx_delay:%u\n", gclock->tx_delay); |
| return 0; |
| } |
| |
| unsigned int aml_sd_emmc_clktest(struct mmc *mmc) |
| { |
| struct aml_card_sd_info *aml_priv = mmc->priv; |
| struct sd_emmc_global_regs *sd_emmc_regs = aml_priv->sd_emmc_reg; |
| |
| u32 intf3 = sd_emmc_regs->gintf3; |
| struct intf3 *gintf3 = (struct intf3 *)&(intf3); |
| u32 clktest = 0, delay_cell = 0, clktest_log = 0, count = 0; |
| u32 vcfg = sd_emmc_regs->gcfg; |
| |
| int i = 0; |
| unsigned int cycle = 0; |
| |
| sd_emmc_regs->gadjust = 0; |
| |
| /* one cycle = xxx(ps) */ |
| cycle = (1000000000 / mmc->clock) * 1000; |
| vcfg &= ~(1 << 23); |
| vcfg = 0x4896; |
| |
| sd_emmc_regs->gcfg = vcfg; |
| sd_emmc_regs->gdelay = 0; |
| sd_emmc_regs->gdelay1 = 0; |
| |
| gintf3->clktest_exp = 8; |
| gintf3->clktest_on_m = 1; |
| sd_emmc_regs->gintf3 = intf3; |
| |
| clktest_log = sd_emmc_regs->gclktest_log; |
| clktest = sd_emmc_regs->gclktest_out;; |
| while (!(clktest_log & 0x80000000)) { |
| mdelay(1); |
| i++; |
| clktest_log = sd_emmc_regs->gclktest_log; |
| clktest = sd_emmc_regs->gclktest_out; |
| if (i > 4) { |
| printf("[%s] [%d] emmc clktest error\n", |
| __func__, __LINE__); |
| break; |
| } |
| } |
| if (clktest_log & 0x80000000) { |
| clktest = sd_emmc_regs->gclktest_out; |
| count = clktest / (1 << 8); |
| if (vcfg & 0x4) |
| delay_cell = ((cycle / 2) / count); |
| else |
| delay_cell = (cycle / count); |
| } |
| printf("%s [%d] clktest : %u, delay_cell: %d, count: %u\n", |
| __func__, __LINE__, clktest, delay_cell, count); |
| intf3 = sd_emmc_regs->gintf3; |
| gintf3->clktest_on_m = 0; |
| sd_emmc_regs->gintf3 = intf3; |
| vcfg = sd_emmc_regs->gcfg; |
| vcfg |= (1 << 23); |
| sd_emmc_regs->gcfg = vcfg; |
| return count; |
| } |
| |
| static void tl1_emmc_line_timing(struct mmc *mmc) |
| { |
| struct aml_card_sd_info *aml_priv = mmc->priv; |
| struct sd_emmc_global_regs *sd_emmc_regs = aml_priv->sd_emmc_reg; |
| u32 delay1 = 0, delay2 = 0, count = 12; |
| |
| delay2 = sd_emmc_regs->gdelay1; |
| delay1 = (count<<0)|(count<<6)|(count<<12) |
| |(count<<18)|(count<<24); |
| delay2 |= (count<<0)|(count<<6)|(count<<12); |
| sd_emmc_regs->gdelay = delay1; |
| sd_emmc_regs->gdelay1 = delay2; |
| return; |
| |
| } |
| |
| static u32 emmc_search_cmd_delay(char *str, int repeat_times) |
| { |
| int best_start = -1, best_size = -1; |
| int cur_start = -1, cur_size = 0; |
| u32 cmd_delay; |
| int i; |
| |
| for (i = 0; i < 64; i++) { |
| if (str[i] == repeat_times) { |
| cur_size += 1; |
| if (cur_start == -1) |
| cur_start = i; |
| } else { |
| cur_size = 0; |
| cur_start = -1; |
| } |
| |
| if (cur_size > best_size) { |
| best_size = cur_size; |
| best_start = cur_start; |
| } |
| } |
| |
| cmd_delay = (best_start + best_size / 2) << 24; |
| printf("best_start 0x%x, best_size %d, cmd_delay is 0x%x\n", |
| best_start, best_size, cmd_delay >> 24); |
| return cmd_delay; |
| } |
| |
| static void emmc_show_cmd_window(char *str, int repeat_times) |
| { |
| int pre_status = 0; |
| int status = 0; |
| int single = 0; |
| int start = 0; |
| int i; |
| |
| printf(">>>>>>>>>>>>>>scan command window>>>>>>>>>>>>>>>\n"); |
| for (i = 0; i < 64; i++) { |
| if (str[i] == repeat_times) |
| status = 1; |
| else |
| status = -1; |
| |
| if (i != 0 && pre_status != status) { |
| if (pre_status == 1 && single == 1) |
| printf(">>cmd delay [ 0x%x ] is ok\n", i - 1); |
| else if (pre_status == 1 && single != 1) |
| printf(">>cmd delay [ 0x%x -- 0x%x ] is ok\n", |
| start, i - 1); |
| else if (pre_status != 1 && single == 1) |
| printf(">>cmd delay [ 0x%x ] is nok\n", i - 1); |
| else if (pre_status != 1 && single != 1) |
| printf(">>cmd delay [ 0x%x -- 0x%x ] is nok\n", |
| start, i - 1); |
| start = i; |
| single = 1; |
| } else |
| single++; |
| |
| if (i == 63) { |
| if (status == 1 && pre_status == 1) |
| printf(">>cmd delay [ 0x%x -- 0x%x ] is ok\n", |
| start, i); |
| else if (status != 1 && pre_status == -1) |
| printf(">>cmd delay [ 0x%x -- 0x%x ] is nok\n", |
| start, i); |
| else if (status == 1 && pre_status != 1) |
| printf(">>cmd delay [ 0x%x ] is ok\n", i); |
| else if (status != 1 && pre_status == 1) |
| printf(">>cmd delay [ 0x%x ] is nok\n", i); |
| } |
| pre_status = status; |
| } |
| printf("<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n"); |
| } |
| |
| static u32 scan_emmc_cmd_win(struct mmc *mmc, int send_status) |
| { |
| struct aml_card_sd_info *aml_priv = mmc->priv; |
| struct sd_emmc_global_regs *sd_emmc_regs = aml_priv->sd_emmc_reg; |
| u32 delay2 = sd_emmc_regs->gdelay1; |
| u32 cmd_delay = 0; |
| u32 delay2_bak = delay2; |
| u32 i, j, err; |
| int repeat_times = 100; |
| char str[64] = {0}; |
| u32 capacity = mmc->capacity >> 9; |
| unsigned long phy_addr = 0x1080000; |
| void * addr = (void*) phy_addr; |
| u32 offset; |
| u32 begin_time, end_time; |
| |
| addr = (void *)malloc(512 *sizeof(char)); |
| if (!addr) |
| return -1; |
| |
| delay2 &= ~(0xff << 24); |
| |
| begin_time = get_time(); |
| |
| for (i = 0; i < 64; i++) { |
| sd_emmc_regs->gdelay1 = delay2; |
| offset = (u32)rand() % capacity; |
| for (j = 0; j < repeat_times; j++) { |
| if (send_status) |
| err = aml_sd_emmc_cmd_v3(mmc); |
| else |
| err = mmc_read_single_block(mmc, addr, offset); |
| if (!err) |
| str[i]++; |
| else |
| break; |
| } |
| delay2 += (1 << 24); |
| } |
| end_time = get_time(); |
| printf("spend time is %dus.\n", end_time - begin_time); |
| |
| sd_emmc_regs->gdelay1 = delay2_bak; |
| cmd_delay = emmc_search_cmd_delay(str, repeat_times); |
| if (!send_status) |
| emmc_show_cmd_window(str, repeat_times); |
| |
| return cmd_delay; |
| } |
| |
| static void set_emmc_cmd_delay(struct mmc *mmc, int send_status) |
| { |
| struct aml_card_sd_info *aml_priv = mmc->priv; |
| struct sd_emmc_global_regs *sd_emmc_regs = aml_priv->sd_emmc_reg; |
| u32 delay2 = sd_emmc_regs->gdelay1; |
| u32 cmd_delay = 0; |
| |
| delay2 &= ~(0xff << 24); |
| cmd_delay = scan_emmc_cmd_win(mmc, send_status); |
| delay2 |= cmd_delay; |
| sd_emmc_regs->gdelay1 = delay2; |
| } |
| |
| static void aml_emmc_hs400_tl1(struct mmc *mmc) |
| { |
| set_emmc_cmd_delay(mmc, 1); |
| tl1_emmc_line_timing(mmc); |
| emmc_ds_manual_sht(mmc); |
| set_emmc_cmd_delay(mmc, 0); |
| } |
| |
| static void aml_emmc_hs400_general(struct mmc *mmc) { |
| update_all_line_eyetest(mmc); |
| emmc_ds_core_align(mmc); |
| update_all_line_eyetest(mmc); |
| emmc_all_data_line_alignment(mmc); |
| update_all_line_eyetest(mmc); |
| emmc_ds_data_alignment(mmc); |
| update_all_line_eyetest(mmc); |
| emmc_ds_manual_sht(mmc); |
| } |
| |
| static int emmc_data_alignment(struct mmc *mmc, int best_size) |
| { |
| struct aml_card_sd_info *aml_priv = mmc->priv; |
| struct sd_emmc_global_regs *sd_emmc_regs = aml_priv->sd_emmc_reg; |
| u32 delay1 = sd_emmc_regs->gdelay; |
| u32 delay2 = sd_emmc_regs->gdelay1; |
| u32 intf3 = sd_emmc_regs->gintf3; |
| struct intf3 *gintf3 = (struct intf3 *)&(intf3); |
| u32 delay1_bak = delay1; |
| u32 delay2_bak = delay2; |
| u32 intf3_bak = intf3; |
| int line_x, i, cnt = 0, win_new; |
| u32 d[8]; |
| int blk = 200; |
| unsigned long phy_addr = 0x1080000; |
| void *addr = (void*)phy_addr; |
| lbaint_t offset = 0; |
| //((SZ_1M*(26+3))/ 512); |
| |
| gintf3->ds_sht_m = aml_priv->win_start + 4; |
| sd_emmc_regs->gintf3 = intf3; |
| for (line_x = 0; line_x < 8; line_x++) { |
| for (i = 0; i < 20; i++) { |
| if (line_x < 5) { |
| delay1 += (1 << 6 * line_x); |
| sd_emmc_regs->gdelay = delay1; |
| } else { |
| delay2 += (1 << 6 * (line_x - 5)); |
| sd_emmc_regs->gdelay1 = delay2; |
| } |
| cnt = mmc_bread(1, offset, blk, addr); |
| if (cnt != blk) { |
| // printf("[%s]adjust line_x[%d]:%d\n", |
| // __func__, line_x, i); |
| d[line_x] = i; |
| delay1 = delay1_bak; |
| delay2 = delay2_bak; |
| sd_emmc_regs->gdelay = delay1_bak; |
| sd_emmc_regs->gdelay1 = delay2_bak; |
| break; |
| } |
| } |
| if (i == 20) { |
| printf("[%s][%d] return set default value", |
| __func__, __LINE__); |
| sd_emmc_regs->gdelay = delay1_bak; |
| sd_emmc_regs->gdelay1 = delay2_bak; |
| sd_emmc_regs->gintf3 = intf3_bak; |
| return -1; |
| } |
| } |
| |
| delay1 += (d[0]<<0)|(d[1]<<6)|(d[2]<<12)|(d[3]<<18)|(d[4]<<24); |
| delay2 += (d[5]<<0)|(d[6]<<6)|(d[7]<<12); |
| sd_emmc_regs->gdelay = delay1; |
| sd_emmc_regs->gdelay1 = delay2; |
| printf("delay1:0x%x, delay2:0x%x\n", |
| sd_emmc_regs->gdelay, |
| sd_emmc_regs->gdelay1); |
| gintf3->ds_sht_m = 0; |
| sd_emmc_regs->gintf3 = intf3; |
| win_new = emmc_ds_manual_sht(mmc); |
| if (win_new < best_size) { |
| printf("[%s][%d] win_new:%d < win_old:%d,set default!", |
| __func__, __LINE__, win_new, best_size); |
| sd_emmc_regs->gdelay = delay1_bak; |
| sd_emmc_regs->gdelay1 = delay2_bak; |
| sd_emmc_regs->gintf3 = intf3_bak; |
| printf("intf3:0x%x, delay1:0x%x, delay2:0x%x\n", |
| sd_emmc_regs->gdelay = delay1_bak, |
| sd_emmc_regs->gdelay1 = delay2_bak, |
| sd_emmc_regs->gintf3 = intf3_bak); |
| } |
| |
| return 0; |
| } |
| |
| int aml_emmc_hs400_Revb(struct mmc *mmc) { |
| struct aml_card_sd_info *aml_priv = mmc->priv; |
| struct sd_emmc_global_regs *sd_emmc_regs = aml_priv->sd_emmc_reg; |
| int win_size = 0; |
| |
| sd_emmc_regs->gintf3 = 0x4000c8; |
| set_emmc_cmd_delay(mmc, 1); |
| |
| printf("[%s], delay1: 0x%x, delay2: 0x%x\n", |
| __func__, sd_emmc_regs->gdelay, |
| sd_emmc_regs->gdelay1); |
| win_size = emmc_ds_manual_sht(mmc); |
| emmc_data_alignment(mmc, win_size); |
| set_emmc_cmd_delay(mmc, 0); |
| return 0; |
| } |
| |
| int aml_post_hs400_timming(struct mmc *mmc) |
| { |
| int i; |
| |
| cpu_id_t cpu_id = get_cpu_id(); |
| for (i = 0; i < 9; i++) |
| align[i] = 0; |
| mmc->refix = 1; |
| aml_sd_emmc_clktest(mmc); |
| #ifdef MMC_SET_TUNING_PARA |
| if (aml_set_tuning_para(mmc)) |
| return 0; |
| #endif |
| |
| if (cpu_id.family_id == MESON_CPU_MAJOR_ID_G12B) |
| aml_emmc_hs400_Revb(mmc); |
| else if (cpu_id.family_id > MESON_CPU_MAJOR_ID_G12B) |
| aml_emmc_hs400_tl1(mmc); |
| else |
| aml_emmc_hs400_general(mmc); |
| |
| #ifdef MMC_WRITE_TUNING_PARA |
| aml_save_tuning_para(mmc); |
| aml_write_tuning_para(mmc); |
| #endif |
| mmc->refix = 0; |
| return 0; |
| } |
| |
| void mmc_set_clock_phase(struct mmc *mmc, int hs_mode) |
| { |
| struct aml_card_sd_info *aml_priv = mmc->priv; |
| struct sd_emmc_global_regs *sd_emmc_regs = aml_priv->sd_emmc_reg; |
| u32 clock = sd_emmc_regs->gclock; |
| struct sd_emmc_clock *gclock = (struct sd_emmc_clock *) &clock; |
| |
| if (hs_mode) { |
| gclock->core_phase = MMC_HS_COPHASE; |
| sd_emmc_regs->gclock = clock; |
| } else { |
| gclock->core_phase = MMC_HS4_COPHASE; |
| gclock->tx_delay = MMC_HS400_TXDELAY; |
| sd_emmc_regs->gclock = clock; |
| } |
| return; |
| } |
| |
| void mmc_set_txdelay(struct mmc *mmc, unsigned int tx_delay) |
| { |
| struct aml_card_sd_info *aml_priv = mmc->priv; |
| struct sd_emmc_global_regs *sd_emmc_regs = aml_priv->sd_emmc_reg; |
| |
| sd_emmc_regs->gclock &= ~(0x1f << Cfg_tx_delay); |
| sd_emmc_regs->gclock |= (tx_delay << Cfg_tx_delay); |
| |
| return; |
| } |
| void mmc_set_ddr_mode(struct mmc *mmc) |
| { |
| struct aml_card_sd_info *aml_priv = mmc->priv; |
| struct sd_emmc_global_regs *sd_emmc_regs = aml_priv->sd_emmc_reg; |
| u8 clk_div; |
| |
| clk_div = 0x3f & sd_emmc_regs->gclock; |
| if (clk_div & 1) |
| clk_div = (clk_div + 1) >> 1 ; |
| else |
| clk_div = clk_div >> 1; |
| |
| sd_emmc_regs->gclock &= ~(0x3f & sd_emmc_regs->gclock); |
| sd_emmc_regs->gclock |= 0x3f & clk_div; |
| |
| sd_emmc_regs->gcfg |= 1 << 2; |
| |
| return; |
| } |
| |
| void mmc_set_ds_enable(struct mmc *mmc) |
| { |
| struct aml_card_sd_info *aml_priv = mmc->priv; |
| struct sd_emmc_global_regs *sd_emmc_regs = aml_priv->sd_emmc_reg; |
| |
| sd_emmc_regs->gadjust |= 1 << 15; |
| |
| return ; |
| } |
| |
| void mmc_set_clock_div(struct mmc *mmc, u32 src_speed) { |
| struct aml_card_sd_info *aml_priv = mmc->priv; |
| struct sd_emmc_global_regs *sd_emmc_regs = aml_priv->sd_emmc_reg; |
| u32 clock = sd_emmc_regs->gclock; |
| struct sd_emmc_clock *gclock = (struct sd_emmc_clock *) &clock; |
| u32 clock_speed = src_speed / 4; |
| |
| cpu_id_t cpu_id = get_cpu_id(); |
| |
| mmc_set_clock(mmc, clock_speed); |
| |
| if (cpu_id.family_id >= MESON_CPU_MAJOR_ID_G12B) { |
| gclock->div = 2; |
| gclock->tx_delay = MMC_HS400_TXDELAY; |
| } else { |
| gclock->div = 1; |
| gclock->tx_delay =12;//MMC_HS400_TXDELAY; |
| } |
| gclock->core_phase = MMC_HS4_COPHASE; |
| gclock->src = 0; |
| sd_emmc_regs->gclock = clock; |
| mmc->tran_speed = src_speed / 4; |
| printf("clock is 0x%x\n", clock); |
| |
| return ; |
| } |
| |
| /* |
| * Function to enable HS400 mode |
| * 1. Set the HS_TIMING on ext_csd 185 to 0x01 |
| * 2. Set the clock frequency to 52MHz |
| * 3. Set the bus width to 8 bit DDR as supported by the target & host |
| * 4. Set the HS_TIMING to 0x03 |
| * 5. Set the clock frequency to 200 MHZ |
| */ |
| uint32_t mmc_set_hs400_mode(struct mmc *mmc) |
| { |
| uint32_t err = 0; |
| struct aml_card_sd_info *aml_priv = mmc->priv; |
| cpu_id_t cpu_id = get_cpu_id(); |
| |
| if (aml_priv->sd_emmc_port != 2) |
| return err; |
| |
| /* set HS_TIMING TO 0X01 */ |
| err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, |
| EXT_CSD_HS_TIMING, EXT_CSD_TIMING_HS); |
| |
| if (err) { |
| printf("Switch cmd returned failure %d\n", __LINE__); |
| return err; |
| } |
| /* Set Clock @ 52MHZ */ |
| mmc_set_clock(mmc, 50000000); |
| |
| /* Set 8 bit DDR bus width */ |
| err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, |
| EXT_CSD_BUS_WIDTH, 6); |
| |
| if (err) { |
| printf("Switch cmd returned failure %d\n", __LINE__); |
| return err; |
| } |
| /* Setting HS400 in HS_TIMING using EXT_CSD (CMD6) */ |
| err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, |
| EXT_CSD_HS_TIMING, 3); |
| |
| if (err) { |
| printf("Switch cmd returned failure %d\n", __LINE__); |
| return err; |
| } |
| |
| mmc_set_ddr_mode(mmc); |
| mmc_set_ds_enable(mmc); |
| |
| mmc_set_clock_phase(mmc, 0); |
| |
| select_clock_src(); |
| |
| aml_priv->cfg.f_max = 200000000; |
| /* Set Clock @ 400 MHZ */ |
| if (cpu_id.family_id == MESON_CPU_MAJOR_ID_TL1) |
| mmc_set_clock_div(mmc, 792000000); |
| else if (cpu_id.family_id == MESON_CPU_MAJOR_ID_T5 || |
| cpu_id.family_id == MESON_CPU_MAJOR_ID_T5D) |
| mmc_set_clock_div(mmc, 768000000); |
| else if (cpu_id.family_id >= MESON_CPU_MAJOR_ID_G12B) |
| mmc_set_clock_div(mmc, 800000000); |
| else |
| mmc_set_clock_div(mmc, 400000000); |
| |
| if (cpu_id.family_id == MESON_CPU_MAJOR_ID_G12B) |
| aml_emmc_hs200_tl1(mmc); |
| aml_post_hs400_timming(mmc); |
| |
| return err; |
| } |
| #endif |