TERES/SOFTWARE/A64-TERES/u-boot_new/drivers/mmc/sunxi_mmc_tuning.c
2018-12-05 11:51:12 +02:00

1575 lines
38 KiB
C
Executable File

/*
* MMC driver for allwinner sunxi platform.
*
*/
#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 "sunxi_mmc.h"
#include "mmc_def.h"
char *spd_name[] = {"DS26/SDR12", "HSSDR52/SDR25", "HSDDR52/DDR50", "HS200/SDR104", "HS400"};
static const char hs200_tuning_blk_4b[64] = {
/*hs200/uhs*/
0xff, 0x0f, 0xff, 0x00, 0xff, 0xcc, 0xc3, 0xcc,
0xc3, 0x3c, 0xcc, 0xff, 0xfe, 0xff, 0xfe, 0xef,
0xff, 0xdf, 0xff, 0xdd, 0xff, 0xfb, 0xff, 0xfb,
0xbf, 0xff, 0x7f, 0xff, 0x77, 0xf7, 0xbd, 0xef,
0xff, 0xf0, 0xff, 0xf0, 0x0f, 0xfc, 0xcc, 0x3c,
0xcc, 0x33, 0xcc, 0xcf, 0xff, 0xef, 0xff, 0xee,
0xff, 0xfd, 0xff, 0xfd, 0xdf, 0xff, 0xbf, 0xff,
0xbb, 0xff, 0xf7, 0xff, 0xf7, 0x7f, 0x7b, 0xde,
};
static const char hs200_tuning_blk_8b[128] = {
0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00,
0xff, 0xff, 0xcc, 0xcc, 0xcc, 0x33, 0xcc, 0xcc,
0xcc, 0x33, 0x33, 0xcc, 0xcc, 0xcc, 0xff, 0xff,
0xff, 0xee, 0xff, 0xff, 0xff, 0xee, 0xee, 0xff,
0xff, 0xff, 0xdd, 0xff, 0xff, 0xff, 0xdd, 0xdd,
0xff, 0xff, 0xff, 0xbb, 0xff, 0xff, 0xff, 0xbb,
0xbb, 0xff, 0xff, 0xff, 0x77, 0xff, 0xff, 0xff,
0x77, 0x77, 0xff, 0x77, 0xbb, 0xdd, 0xee, 0xff,
0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00,
0x00, 0xff, 0xff, 0xcc, 0xcc, 0xcc, 0x33, 0xcc,
0xcc, 0xcc, 0x33, 0x33, 0xcc, 0xcc, 0xcc, 0xff,
0xff, 0xff, 0xee, 0xff, 0xff, 0xff, 0xee, 0xee,
0xff, 0xff, 0xff, 0xdd, 0xff, 0xff, 0xff, 0xdd,
0xdd, 0xff, 0xff, 0xff, 0xbb, 0xff, 0xff, 0xff,
0xbb, 0xbb, 0xff, 0xff, 0xff, 0x77, 0xff, 0xff,
0xff, 0x77, 0x77, 0xff, 0x77, 0xbb, 0xdd, 0xee,
};
static const char extra_tuning_data_wifi[64] = {
0x30, 0x43, 0x04, 0x16, 0x00, 0x90, 0x10, 0x18,
0x01, 0x00, 0xf8, 0x4b, 0x11, 0x42, 0x00, 0x27,
0x03, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x18,
0xc5, 0x00, 0x10, 0x18, 0x01, 0x12, 0xf8, 0x4b,
0x11, 0x42, 0x00, 0x19, 0x03, 0x01, 0x00, 0x00,
0x05, 0x10, 0x00, 0x18, 0xc5, 0x10, 0x10, 0x18,
0x30, 0x43, 0x04, 0x16, 0x00, 0x90, 0x10, 0x18,
0x01, 0x00, 0xf8, 0x4b, 0x11, 0x42, 0x00, 0x27,
};
static const char extra_tuning_data_rand[128] = {
0xe4, 0x4f, 0x76, 0xbb, 0xf0, 0xb7, 0xe0, 0xdb,
0xb9, 0x1f, 0x9f, 0xfb, 0x7e, 0x9b, 0x03, 0x7d,
0x2e, 0x32, 0x8f, 0x29, 0x7a, 0x9b, 0xab, 0x16,
0x2f, 0x44, 0x99, 0xce, 0xc3, 0x99, 0xaa, 0xad,
0x2d, 0x82, 0xb2, 0x8a, 0xfa, 0x2d, 0xb9, 0x9a,
0x9e, 0x0f, 0xf3, 0x90, 0x08, 0x25, 0xf3, 0x09,
0x79, 0x80, 0x1b, 0x28, 0x95, 0x00, 0x57, 0x7d,
0xbb, 0x60, 0x0b, 0x2c, 0x92, 0x72, 0x49, 0x4b,
0xe4, 0xac, 0x48, 0x8b, 0xb0, 0xe4, 0x11, 0x1b,
0x7a, 0x58, 0x7c, 0xc9, 0xe6, 0xf1, 0x5b, 0x6b,
0x85, 0xc9, 0xf5, 0x7d, 0xef, 0xea, 0xb6, 0x0b,
0x12, 0x59, 0x24, 0xd2, 0xc9, 0x53, 0x15, 0xa2,
0xb1, 0xd6, 0x1f, 0x06, 0x38, 0x63, 0x51, 0x27,
0xf6, 0x03, 0x20, 0xee, 0x41, 0x88, 0xa4, 0x69,
0xfb, 0x15, 0x05, 0x70, 0xaf, 0xe0, 0x30, 0x88,
0xdc, 0x37, 0xce, 0x07, 0x91, 0xc1, 0x76, 0x79,
};
__attribute__((section(".data")))
u8 *tuning_blk_4b = NULL;
__attribute__((section(".data")))
u8 *tuning_blk_8b = NULL;
__attribute__((section(".data")))
u32 tuning_blk_cnt_8b = 0;
__attribute__((section(".data")))
u32 tuning_blk_cnt_4b = 0;
u32 freq_def = 6*1000*1000;
u32 freq_ds26_sdr12[8] = {
400*1000, 25*1000*1000, 50*1000*1000,
};
u32 freq_hssdr52_sdr25[8] = {
400*1000, 25*1000*1000, 50*1000*1000,
};
u32 freq_hsddr52_ddr50[8] = {
400*1000, 25*1000*1000, 50*1000*1000,
};
u32 freq_hs200_sdr104[8] = {
400*1000, 25*1000*1000, 50*1000*1000, 100*1000*1000,
150*1000*1000, 200*1000*1000,
};
u32 freq_hs400[8] = {
400*1000, 25*1000*1000, 50*1000*1000, 100*1000*1000,
150*1000*1000, 200*1000*1000,
};
u32 freq_range_ds26_sdr12[2] = {400*1000, 26*1000*1000};
u32 freq_range_hssdr52_sdr25[2] = {50*1000*1000, 52*1000*1000};
u32 freq_range_hsddr52_ddr50[2] = {25*1000*1000, 52*1000*1000};
u32 freq_range_hs200_sdr104[2] = {50*1000*1000, 200*1000*1000};
u32 freq_range_hs400[2] = {50*1000*1000, 150*1000*1000};
u8 pat_seed_pair[8][2] = {
{0xFE, 0x01},
{0x01, 0xFE},
{0x00, 0xFE},
//{0xFE, 0x00},
{0x01, 0xFF},
//{0xFF, 0x01},
//{0x00, 0xFF},
//{0xFF, 0x00},
};
#define TUNING_PAT_CNT_EACH_DATA_LINE 4
#define TUNING_PAT_SIZE_8BIT 128
#define TUNING_PAT_SIZE_4BIT 64
int gen_pat_bus4_1bit(u8 *dat, int bit_no, int pat_size)
{
int p;
u8 tmp, d_1st, d_2nd;
int i, cur_len = 0;
int repeat_cnt = pat_size>>1;
cur_len = 0;
for (p=0; p<TUNING_PAT_CNT_EACH_DATA_LINE; p++)
{
tmp = pat_seed_pair[p][0];
for (i=1; i<=bit_no; i++)
{
tmp = ((tmp<<1)&0xf) | ((tmp>>(4-1)) & 0x1) ;
}
d_1st = ((tmp&0xf)<<4) | (tmp&0xf);
tmp = pat_seed_pair[p][1];
for (i=1; i<=bit_no; i++)
{
tmp = ((tmp<<1)&0xf) | ((tmp>>(4-1)) & 0x1) ;
}
d_2nd = ((tmp&0xf)<<4) | (tmp&0xf);
for (i=0; i<repeat_cnt; i++)
{
dat[cur_len++] = d_1st;
dat[cur_len++] = d_2nd;
}
}
return 0;
}
int gen_pat_bus8_1bit(u8 *dat, int bit_no, int pat_size)
{
int p;
u8 tmp, d_1st, d_2nd;
int i, cur_len = 0;
int repeat_cnt = pat_size>>1; //each pattern 128 byte for bus width 8
cur_len = 0;
for (p=0; p<TUNING_PAT_CNT_EACH_DATA_LINE; p++)
{
tmp = pat_seed_pair[p][0];
for (i=1; i<=bit_no; i++)
{
tmp = ((tmp<<1)&0xff) | ((tmp>>(8-1)) & 0x1) ;
}
d_1st = tmp & 0xff;
tmp = pat_seed_pair[p][1];
for (i=1; i<=bit_no; i++)
{
tmp = ((tmp<<1)&0xff) | ((tmp>>(8-1)) & 0x1) ;
}
d_2nd = tmp & 0xff;
for (i=0; i<repeat_cnt; i++)
{
dat[cur_len++] = d_1st;
dat[cur_len++] = d_2nd;
}
}
return 0;
}
int gen_tuning_blk_bus8(u8 *buf, u32 *pat_blk_cnt)
{
int i, tmp, bit = 0;
u8 *pdata = (u8 *)buf;
int pat_cnt = 0;
u8 *cur = NULL;
//hs200 tuning block
pat_cnt = 0;
for (i=0; i<512; i++)
{
*(pdata + i) = hs200_tuning_blk_8b[i%128];
}
//io data pattern
pat_cnt++;
for (bit=0; bit<8; bit++)
{
cur = pdata + pat_cnt*512 + bit*TUNING_PAT_CNT_EACH_DATA_LINE*TUNING_PAT_SIZE_8BIT;
gen_pat_bus8_1bit(cur, bit, TUNING_PAT_SIZE_8BIT);
}
pat_cnt += ((8*TUNING_PAT_CNT_EACH_DATA_LINE*TUNING_PAT_SIZE_8BIT) >> 9);
tmp = (8*TUNING_PAT_CNT_EACH_DATA_LINE*TUNING_PAT_SIZE_8BIT) % 512;
if (tmp)
{
for (i=0; i<(512-tmp); i++)
{
*(pdata + pat_cnt*512 + tmp + i) = extra_tuning_data_rand[i%128];
}
pat_cnt++;
}
//rand, wifi, 0x00, 0xff
for (i=0; i<128; i++)
{
*(pdata + pat_cnt*512 + i) = extra_tuning_data_rand[i%128];
}
for (i=128; i<256; i++)
{
*(pdata + pat_cnt*512 + i) = extra_tuning_data_wifi[i%64];
}
for (i=(256>>1); i<(384>>1); i++)
{
*(pdata + pat_cnt*512 + 2*i) = 0x00;
*(pdata + pat_cnt*512 + 2*i + 1) = 0xFF;
}
for (i=(384>>1); i<(512>>1); i++)
{
*(pdata + pat_cnt*512 + 2*i) = 0xFF;
*(pdata + pat_cnt*512 + 2*i + 1) = 0x00;
}
pat_cnt++;
*pat_blk_cnt = pat_cnt;
MMCINFO("%s: total blk %d\n", __FUNCTION__, *pat_blk_cnt);
return 0;
}
int gen_tuning_blk_bus4(u8 *buf, u32 *pat_blk_cnt)
{
int i, tmp, bit = 0;
u8 *pdata = (u8 *)buf;
int pat_cnt = 0;
u8 *cur = NULL;
//hs200 tuning block
pat_cnt = 0;
for (i=0; i<512; i++)
{
*(pdata + i) = hs200_tuning_blk_4b[i%64];
}
//io data pattern
pat_cnt++;
for (bit=0; bit<8; bit++)
{
cur = pdata + pat_cnt*512 + bit*TUNING_PAT_CNT_EACH_DATA_LINE*TUNING_PAT_SIZE_4BIT;
gen_pat_bus4_1bit(cur, bit, TUNING_PAT_SIZE_8BIT);
}
pat_cnt += ((8*TUNING_PAT_CNT_EACH_DATA_LINE*TUNING_PAT_SIZE_4BIT) >> 9);
tmp = (8*TUNING_PAT_CNT_EACH_DATA_LINE*TUNING_PAT_SIZE_4BIT) % 512;
if (tmp)
{
for (i=0; i<(512-tmp); i++)
{
*(pdata + pat_cnt*512 + tmp + i) = extra_tuning_data_rand[i%64];
}
pat_cnt++;
}
//rand, wifi, 0x00, 0xff
for (i=0; i<128; i++)
{
*(pdata + pat_cnt*512 + i) = extra_tuning_data_rand[i%128];
}
for (i=128; i<256; i++)
{
*(pdata + pat_cnt*512 + i) = extra_tuning_data_wifi[i%64];
}
for (i=(256>>1); i<(384>>1); i++)
{
*(pdata + pat_cnt*512 + 2*i) = 0x00;
*(pdata + pat_cnt*512 + 2*i + 1) = 0xFF;
}
for (i=(384>>1); i<(512>>1); i++)
{
*(pdata + pat_cnt*512 + 2*i) = 0xFF;
*(pdata + pat_cnt*512 + 2*i + 1) = 0x00;
}
pat_cnt++;
*pat_blk_cnt = pat_cnt;
MMCINFO("%s: total blk %d\n", __FUNCTION__, *pat_blk_cnt);
return 0;
}
int sunxi_mmc_tuning_init(void)
{
int ret = 0;
if ( NULL == (tuning_blk_8b = (u8 *)malloc(TUNING_LEN*512))) {
MMCINFO("%s: request memory for buf_bus8 fail\n", __FUNCTION__);
return -1;
}
if ( NULL == (tuning_blk_4b = (u8 *)malloc(TUNING_LEN*512))) {
MMCINFO("%s: request memory for buf_bus4 fail\n", __FUNCTION__);
ret = -1;
goto OUT;
}
gen_tuning_blk_bus8(tuning_blk_8b, &tuning_blk_cnt_8b);
gen_tuning_blk_bus4(tuning_blk_4b, &tuning_blk_cnt_4b);
if (tuning_blk_cnt_8b > TUNING_LEN)
tuning_blk_cnt_8b = TUNING_LEN;
if (tuning_blk_cnt_4b > TUNING_LEN)
tuning_blk_cnt_4b = TUNING_LEN;
return 0;
OUT:
if (!tuning_blk_8b)
free(tuning_blk_8b);
return ret;
}
int sunxi_mmc_tuning_exit(void)
{
if (!tuning_blk_4b)
free(tuning_blk_4b);
if (!tuning_blk_8b)
free(tuning_blk_8b);
return 0;
}
extern int mmc_mmc_switch_bus_mode(struct mmc *mmc, int spd_mode, int width);
static ulong sunxi_read_tuning(int dev_num, ulong start, lbaint_t blkcnt, void *dst);
unsigned int sunxi_select_freq(struct mmc *mmc, int speed_md, int freq_index)
{
u32 freq = 0;
struct sunxi_mmc_host *host = (struct sunxi_mmc_host *)mmc->priv;
int i;
u32 val;
if (freq_index >= 8) {
MMCINFO("freq_index error %d\n", freq_index);
return 0;
}
for (i=0; i<MAX_EXT_FREQ_POINT_NUM; i++)
{
val = host->cfg.platform_caps.tm4_tune_ext_freq[i];
if (val & (1U<<31)) {
if ((((val>>8) & 0xff) == freq_index) && (((val>>16) & 0xff) == speed_md)) {
freq = (val & 0xff)*1000*1000;
MMCINFO("select ext freq point: %d-%d MHz\n", i, (val & 0xff));
goto OUT;
}
}
}
if (speed_md == DS26_SDR12)
freq = freq_ds26_sdr12[freq_index];
else if (speed_md == HSSDR52_SDR25)
freq = freq_hssdr52_sdr25[freq_index];
else if (speed_md == HSDDR52_DDR50)
freq = freq_hsddr52_ddr50[freq_index];
else if (speed_md == HS200_SDR104)
freq = freq_hs200_sdr104[freq_index];
else if (speed_md == HS400)
freq = freq_hs400[freq_index];
else {
MMCINFO("speed_md error %d\n", speed_md);
freq = 0;
}
OUT:
return freq;
}
static int sunxi_tuning_method_0(struct mmc *mmc, int retry_times)
{
ulong ret = 0;
int j;
const u8 *std_pat = NULL;
char *rcv_pat = NULL;
int pat_blk_cnt = 0;
if (NULL == (rcv_pat = malloc(TUNING_LEN*512))) {
MMCINFO("request memory for rcv_pat fail\n");
return -1;
}
if (mmc->bus_width == 4) {
std_pat = tuning_blk_4b;
pat_blk_cnt = tuning_blk_cnt_4b;
} else if (mmc->bus_width == 8) {
std_pat = tuning_blk_8b;
pat_blk_cnt = tuning_blk_cnt_8b;
} else if(mmc->bus_width == 1) {
MMCINFO("Not support 1 bit tuning now\n");
ret = -1;
goto OUT;
}
for(j=0; j<retry_times; j++)
{
ret = sunxi_read_tuning(mmc->cfg->host_no,
TUNING_ADD,
pat_blk_cnt,
rcv_pat);
if (ret != pat_blk_cnt) {
MMCMSG(mmc, "read failed\n");
#if 0
//if read failedand block len>1,send stop for next try
//no care if it is successed
if (pat_blk_cnt>1){
MMCINFO("Send stop\n");
mmc_send_manual_stop(mmc);
}
#endif
#if 1
//MMCINFO("Send manual stop\n");
mmc_send_manual_stop(mmc);
#endif
break;
}
ret = memcmp(std_pat, rcv_pat, pat_blk_cnt*512);
if(ret) {
MMCINFO("pattern compare fail\n");
break;
}
}
if (j != retry_times)
ret = -1;
else
ret = 0;
OUT:
free(rcv_pat);
return ret;
}
static int sunxi_tuning_method_1(struct mmc *mmc, int retry_times)
{
struct mmc_cmd cmd;
int err, retry = 0;
/* set cmd13 to get status */
cmd.cmdidx = MMC_CMD_SEND_STATUS;
cmd.resp_type = MMC_RSP_R1;
if (!mmc_host_is_spi(mmc))
cmd.cmdarg = mmc->rca << 16;
cmd.flags = 0;
do {
err = mmc_send_cmd(mmc, &cmd, NULL);
if (err)
break;
udelay(10);
retry++;
} while (retry < retry_times);
return err;
}
static int _skip_curr_freq(struct mmc *mmc, int spd_md, int freq)
{
u32 min, max;
struct sunxi_mmc_host *host = (struct sunxi_mmc_host *)mmc->priv;
if (spd_md == DS26_SDR12) {
min = freq_range_ds26_sdr12[0];
max = freq_range_ds26_sdr12[1];
} else if (spd_md == HSSDR52_SDR25) {
min = freq_range_hssdr52_sdr25[0];
max = freq_range_hssdr52_sdr25[1];
} else if (spd_md == HSDDR52_DDR50) {
min = freq_range_hsddr52_ddr50[0];
max = freq_range_hsddr52_ddr50[1];
} else if (spd_md == HS200_SDR104) {
min = freq_range_hs200_sdr104[0];
if (host->cfg.platform_caps.tm4_tune_hs200_max_freq)
max = host->cfg.platform_caps.tm4_tune_hs200_max_freq * 1000 * 1000;
else
max = freq_range_hs200_sdr104[1];
} else if (spd_md == HS400) {
min = freq_range_hs400[0];
if (host->cfg.platform_caps.tm4_tune_hs400_max_freq)
max = host->cfg.platform_caps.tm4_tune_hs400_max_freq * 1000 * 1000;
else
max = freq_range_hs400[1];
} else {
min = 400*1000;
max = 100*1000*1000;
}
if ((freq < min) || (freq > max))
return 1;
else
return 0;
}
static int _get_best_sdly(int sdly_cnt, u8 win_th, u8 *p)
{
int i, j, max, group;
int s[15]={0}; //start
int e[15]={0}; //end
int w[15]={0}; //window
int in_group = 0;
u8 best = 0;
/* get window boundary */
i = 0;
group = 0; //group
in_group = 0;
while (i<sdly_cnt)
{
if (in_group) {
if (p[i] == 0) {
in_group = 0;
group++;
} else {
e[group] = i;
if (i == (sdly_cnt-1)) {
in_group = 0;
group++;
}
}
} else {
if (p[i] == 1) {
in_group = 1;
s[group] = i;
e[group] = i;
}
}
i++;
};
/* get window size */
for (i=0; i<group; i++)
{
if (e[i] >= s[i])
w[i] = e[i] - s[i] + 1;
else
w[i] = 0;
}
if (group)
printf("[mmc]: ");
for (i=0; i<group; i++)
{
printf("[%d-%d|%d] ", s[i], e[i], w[i]);
if (group && (i==(group-1)))
printf("\n");
}
/*
* in theory, the first two sample delay sections should include a max or almost max sections.
* we check and choose best sample delay from these two sections. it will reduce impact on sample delay
* caused by temperature and voltage variation partly.
*/
if (group > 2)
group = 2;
/* get max window */
j = 0;
max = w[0];
for (i=1; i<group; i++)
{
if (w[i] > max) {
j = i;
max = w[i];
}
}
/*get best point */
if (w[j] >= win_th)
best = s[j] + (w[j]>>1);
else
best = 0xff;
MMCDBG("---- %d-%d: %d - %d, best: 0x%x\n", j, w[j], s[j], e[j], best);
return best;
}
static int sunxi_tuning_speed_mode(struct mmc *mmc, int speed_mode, int tuning_mode, int retry_times)
{
int freq_index=0, freq = 0;
int i, ret = 0;
struct sunxi_mmc_host *host = (struct sunxi_mmc_host *)mmc->priv;
int tm = host->timing_mode;
u8 tm4_win_th = host->cfg.platform_caps.tm4_timing_window_th;
u8 *sdly_cfg = NULL;
int sdly, sdly_cnt = 0;
u8 *p = NULL;
u8 best = 0;
if (tm == SUNXI_MMC_TIMING_MODE_1) {
sdly_cnt = host->tm1.sample_point_cnt;
sdly_cfg = (u8 *)host->tm1.sdly;
} else if (tm == SUNXI_MMC_TIMING_MODE_3) {
sdly_cnt = host->tm3.sample_point_cnt;
sdly_cfg = (u8 *)host->tm3.sdly;
} else if (tm == SUNXI_MMC_TIMING_MODE_4) {
sdly_cnt = host->tm4.sample_point_cnt;
if (speed_mode == HS400) {
sdly_cfg = (u8 *)host->tm4.dsdly;
MMCDBG("%s: current is HS400 mode, dsdly:0x%x\n", __FUNCTION__, (u32)sdly_cfg);
} else
sdly_cfg = (u8 *)host->tm4.sdly;
} else if (tm == SUNXI_MMC_TIMING_MODE_2) {
if (speed_mode == HS400) {
sdly_cnt = host->tm2.sample_point_cnt_hs400;
sdly_cfg = (u8 *)host->tm2.dsdly;
MMCDBG("%s: current is HS400 mode, dsdly:0x%x\n", __FUNCTION__, (u32)sdly_cfg);
} else {
sdly_cnt = host->tm2.sample_point_cnt;
sdly_cfg = (u8 *)host->tm2.sdly;
}
}
if (NULL == (p = malloc(sdly_cnt*MAX_CLK_FREQ_NUM))) {
MMCINFO("%s: request memory fail\n", __FUNCTION__);
return -1;
}
freq_index = 0;
while ((freq = sunxi_select_freq(mmc, speed_mode, freq_index)) != 0)
{
/* change clock frequency */
mmc->tran_speed = freq;
if (!_skip_curr_freq(mmc, speed_mode, freq))
{
/* do tuning */
//MMCINFO("start tuning spd_md: %d-%s, freq: %d-%d\n", speed_mode, spd_name[speed_mode], freq_index, freq);
MMCINFO("freq: %d-%d\n", freq_index, freq);
for (sdly=0; sdly<sdly_cnt; sdly++)
{
/* modify sample point cfg*/
if (speed_mode == HS400)
sdly_cfg[freq_index] = sdly;
else
sdly_cfg[speed_mode*MAX_CLK_FREQ_NUM + freq_index] = sdly;
/* update sample point cfg */
mmc_set_clock(mmc, mmc->tran_speed);
if (tuning_mode == 0)
{
if (!sunxi_tuning_method_0(mmc, retry_times))
p[freq_index*sdly_cnt + sdly] = 1;
else
p[freq_index*sdly_cnt + sdly] = 0;
}
}
}
else
{
MMCINFO("skip freq %d\n", freq);
for (sdly=0; sdly<sdly_cnt; sdly++)
p[freq_index*sdly_cnt + sdly] = 0xFF;
}
freq_index++;
}
/* dump tuning result */
MMCMSG(mmc, "speed mode: %s\n", spd_name[speed_mode]);
for (i=0; i<freq_index; i++)
{
MMCMSG(mmc, "---%dHz: \n", sunxi_select_freq(mmc, speed_mode, i));
#if 0
for (j=0; j<sdly_cnt; j++)
{
if (j && (j%32==0))
printf("\n");
printf("%02x ", p[i*sdly_cnt + j]);
}
printf("\n");
#endif
if (speed_mode == HSDDR52_DDR50)
tm4_win_th = 8;
if (tm == SUNXI_MMC_TIMING_MODE_2) {
if (speed_mode == HS400) {
/* get best delay for hs400 data strobe delay chain */
best = _get_best_sdly(sdly_cnt, tm4_win_th, (p + i*sdly_cnt));
} else {
if (p[i*sdly_cnt + 0] == 1)
best = 0;
else if (p[i*sdly_cnt + 1] == 1)
best = 1;
else
best = 0xFF;
}
} else {
best = _get_best_sdly(sdly_cnt, tm4_win_th, (p + i*sdly_cnt));
}
MMCMSG(mmc, "--best %d\n", best);
/* record best sample point to result[] */
if (speed_mode == HS400)
sdly_cfg[i] = best;
else
sdly_cfg[speed_mode*MAX_CLK_FREQ_NUM + i] = best; //( ((speed_mode&0xf)<<4) | (i&0xF)); //best;
}
/* set proper sample cfg and clock to tune next speed mode */
freq_index = 2;
if (((freq = sunxi_select_freq(mmc, speed_mode, freq_index)) != 0)
&& (sdly_cfg[speed_mode*MAX_CLK_FREQ_NUM + freq_index] != 0xFF)) {
mmc->tran_speed = freq;
} else {
freq_index--;
MMCINFO("try next freq...%d\n", freq_index);
if (((freq = sunxi_select_freq(mmc, speed_mode, freq_index)) != 0)
&& (sdly_cfg[speed_mode*MAX_CLK_FREQ_NUM + freq_index] != 0xFF))
mmc->tran_speed = freq;
else {
ret = -ERR_NO_BEST_DLY;
mmc->tran_speed = freq_def;
MMCINFO("invalid freq, switch to %d\n", freq_def);
}
}
/* update sample point cfg */
mmc_set_clock(mmc, mmc->tran_speed);
free(p);
return ret;
}
static int sunxi_tuning_hs400_cmd(struct mmc *mmc, int speed_mode, int tuning_mode, int retry_times)
{
int freq_index=0, freq = 0;
int i, ret = 0;
struct sunxi_mmc_host *host = (struct sunxi_mmc_host *)mmc->priv;
int tm = host->timing_mode;
u8 tm4_win_th = host->cfg.platform_caps.tm4_timing_window_th;
u8 *sdly_cfg = NULL;
int sdly, sdly_cnt = 0;
u8 *p = NULL;
u8 best = 0;
if (speed_mode != HS400) {
MMCINFO("%s: input speed mode error %d\n", __FUNCTION__, speed_mode);
return 0;
}
if (tm == SUNXI_MMC_TIMING_MODE_4) {
sdly_cnt = host->tm4.sample_point_cnt;
sdly_cfg = (u8 *)host->tm4.sdly;
} else if (tm == SUNXI_MMC_TIMING_MODE_2) {
sdly_cnt = host->tm2.sample_point_cnt;
sdly_cfg = (u8 *)host->tm2.sdly;
}
if (NULL == (p = malloc(sdly_cnt*MAX_CLK_FREQ_NUM))) {
MMCINFO("%s: request memory fail\n", __FUNCTION__);
return -1;
}
freq_index = 0;
while ((freq = sunxi_select_freq(mmc, speed_mode, freq_index)) != 0)
{
/* change clock frequency */
mmc->tran_speed = freq;
if (!_skip_curr_freq(mmc, speed_mode, freq))
{
/* do tuning */
//MMCINFO("start tuning spd_md: %d-%s, freq: %d-%d\n", speed_mode, spd_name[speed_mode], freq_index, freq);
MMCINFO("freq: %d-%d\n", freq_index, freq);
for (sdly=0; sdly<sdly_cnt; sdly++)
{
/* modify sample point cfg*/
sdly_cfg[speed_mode*MAX_CLK_FREQ_NUM + freq_index] = sdly;
/* update sample point cfg */
mmc_set_clock(mmc, mmc->tran_speed);
if (tuning_mode == 1)
{
if (!sunxi_tuning_method_1(mmc, retry_times)) //use method 1
p[freq_index*sdly_cnt + sdly] = 1;
else
p[freq_index*sdly_cnt + sdly] = 0;
} else {
MMCINFO("%s: error tuning method!\n", __FUNCTION__);
}
}
}
else
{
MMCINFO("skip freq %d\n", freq);
for (sdly=0; sdly<sdly_cnt; sdly++)
p[freq_index*sdly_cnt + sdly] = 0xFF;
}
freq_index++;
}
/* dump tuning result */
MMCMSG(mmc, "speed mode: %s\n", spd_name[speed_mode]);
for (i=0; i<freq_index; i++)
{
MMCMSG(mmc, "---%dHz: \n", sunxi_select_freq(mmc, speed_mode, i));
#if 0
for (j=0; j<sdly_cnt; j++)
{
if (j && (j%32==0))
printf("\n");
printf("%02x ", p[i*sdly_cnt + j]);
}
printf("\n");
#endif
if (tm == SUNXI_MMC_TIMING_MODE_2) {
if (p[i*sdly_cnt + 0] == 1)
best = 0;
else if (p[i*sdly_cnt + 1] == 1)
best = 1;
else
best = 0xFF;
} else {
best = _get_best_sdly(sdly_cnt, tm4_win_th, (p + i*sdly_cnt));
}
MMCMSG(mmc, "--best %d\n", best);
/* record best sample point to result[] */
sdly_cfg[speed_mode*MAX_CLK_FREQ_NUM + i] = best; //( ((speed_mode&0xf)<<4) | (i&0xF)); //best;
}
/* set proper sample cfg and clock to tune next speed mode */
freq_index = 2;
if (((freq = sunxi_select_freq(mmc, speed_mode, freq_index)) != 0)
&& (sdly_cfg[speed_mode*MAX_CLK_FREQ_NUM + freq_index] != 0xFF)) {
mmc->tran_speed = freq;
} else {
ret = -ERR_NO_BEST_DLY;
mmc->tran_speed = freq_def;
MMCINFO("invalid freq, switch to 6MHz\n");
}
/* update sample point cfg */
mmc_set_clock(mmc, mmc->tran_speed);
free(p);
return ret;
}
int sunxi_need_rty(struct mmc *mmc)
{
int ret = 0;
u32 err_no = 0;
if (mmc->cfg->ops->decide_retry)
{
err_no = mmc->cfg->ops->get_detail_errno(mmc);
ret = mmc->cfg->ops->decide_retry(mmc, err_no, 0);
if (!ret) {
MMCINFO("need retry next clk %d\n", mmc->clock);
return 0;
}
}
return -1;
}
int write_tuning_try_freq(struct mmc *mmc,u32 clk)
{
int ret = 0;
char *rcv_pattern = NULL;
char *std_pattern = NULL;
u32 err_no = 0;
int pat_blk_cnt = 0;
if ( NULL == (rcv_pattern = (char *)malloc(TUNING_LEN*512))) {
MMCINFO("%s: request memory for rcv_pattern fail\n", __FUNCTION__);
return -1;
}
if ( NULL == (std_pattern = (char *)malloc(TUNING_LEN*512))) {
MMCINFO("%s: request memory for std_pattern fail\n", __FUNCTION__);
ret = -1;
goto out1;
}
if (mmc->bus_width == 4) {
//std_pattern = tuning_blk_4b;
memcpy(std_pattern, tuning_blk_4b, (TUNING_LEN*512));
pat_blk_cnt = tuning_blk_cnt_4b;
} else if (mmc->bus_width == 8) {
//std_pattern = tuning_blk_8b;
memcpy(std_pattern, tuning_blk_8b, (TUNING_LEN*512));
pat_blk_cnt = tuning_blk_cnt_8b;
} else if (mmc->bus_width == 1) {
MMCINFO("Don't support 1 bit tuning now\n");
ret = -1;
goto out;
}
do {
mmc_set_clock(mmc, clk);
ret = mmc_bwrite(mmc->cfg->host_no,
TUNING_ADD,
pat_blk_cnt,
std_pattern);
MMCDBG("Write pattern ret = %d\n",ret);
if (ret != pat_blk_cnt) { //fail
MMCINFO("write failed\n");
err_no = mmc->cfg->ops->get_detail_errno(mmc);
/* if write failed and block len>1,send stop for next try */
if (pat_blk_cnt>1) {
MMCINFO("send stop\n");
mmc_send_manual_stop(mmc);
}
} else {//ok
MMCINFO("write_tuning_try_freq: write ok\n");
/* read pattern and compare with the pattern show sent*/
ret = mmc_bread(mmc->cfg->host_no,
TUNING_ADD,
pat_blk_cnt,
rcv_pattern);
if (ret != pat_blk_cnt) {
MMCMSG(mmc, "read failed\n");
//MMCINFO("0x%08x 0x%08x 0x%08x 0x%08x\n", *(volatile uint *)(ulong)(0x1c1105c), *(volatile uint *)(ulong)(0x1c110140), *(volatile uint *)(ulong)(0x1c110144), *(volatile uint *)(ulong)(0x1c110148));
err_no = mmc->cfg->ops->get_detail_errno(mmc);
/*if read failed and block len>1,send stop for next try*/
if (pat_blk_cnt>1) {
MMCINFO("Send stop\n");
mmc_send_manual_stop(mmc);
}
} else {
ret = memcmp(std_pattern, rcv_pattern, pat_blk_cnt*512);
if(ret) {
MMCINFO("pattern compare fail\n");
err_no = 0xffffffff; //force retry
} else {
MMCINFO("Pattern compare ok\n");
MMCINFO("Write tuning pattern ok\n");
goto out;
}
}
}
} while(!mmc->cfg->ops->decide_retry(mmc, err_no, 0));
MMCINFO(" Write tuning pattern failded\n");
ret = -1;
out:
free(std_pattern);
out1:
free(rcv_pattern);
return ret;
}
int sunxi_write_tuning(struct mmc *mmc)
{
u32 freqs[] = {/*400000, */25*1000*1000, 50*1000*1000};
int i = 0;
int ret = -1;
int clk_bak = mmc->clock;
if (mmc->cfg->ops->decide_retry == NULL) {
MMCINFO("Don't support tuning\n");
return 0;
}
/* reset all sample point */
mmc->cfg->ops->decide_retry(mmc, 0, 1);
for (i=0; i<sizeof(freqs)/sizeof(freqs[0]); i++) {
ret = write_tuning_try_freq(mmc, freqs[i]);
if (!ret) {
/* recover the clock before write patten*/
mmc_set_clock(mmc, clk_bak);
return ret;
}
}
return ret;
}
static ulong sunxi_read_tuning(int dev_num, ulong start, lbaint_t blkcnt, void *dst)
{
lbaint_t cur, blocks_todo = blkcnt;
struct mmc *mmc = find_mmc_device(dev_num);
if (blkcnt == 0) {
MMCINFO("blkcnt should not be 0\n");
return 0;
}
if (!mmc) {
MMCINFO("can not find mmc dev\n");
return 0;
}
if ((start + blkcnt) > mmc->block_dev.lba) {
MMCINFO("MMC: block number 0x%lx exceeds max(0x%lx)\n",
start + blkcnt, mmc->block_dev.lba);
return 0;
}
if (mmc_set_blocklen(mmc, mmc->read_bl_len)) {
MMCMSG(mmc, "Set block len failed\n");
return 0;
}
do {
cur = 1;//force to read 1 block a time
if (mmc_bread(dev_num, start, cur, dst) != cur) {
MMCMSG(mmc, "block read failed, %s %d\n", __FUNCTION__, __LINE__);
return 0;
}
blocks_todo -= cur;
start += cur;
dst = (char*)dst + cur * mmc->read_bl_len;
} while (blocks_todo > 0);
return blkcnt;
}
int sunxi_execute_tuning(struct mmc *mmc, int speed_mode)
{
int ret = 0;
int bus_width = 0;
struct sunxi_mmc_host *host = (struct sunxi_mmc_host *)mmc->priv;
int r_cycle = host->cfg.platform_caps.tm4_tune_r_cycle;
if (!IS_SD(mmc))
{
if (mmc->card_caps & MMC_MODE_8BIT)
bus_width = 8;
else if (mmc->card_caps & MMC_MODE_4BIT)
bus_width = 4;
else
bus_width = 1;
/* switch to specific speed mode */
if (speed_mode == DS26_SDR12)
ret = mmc_mmc_switch_bus_mode(mmc, DS26_SDR12, bus_width);
else if (speed_mode == HSSDR52_SDR25)
ret = mmc_mmc_switch_bus_mode(mmc, HSSDR52_SDR25, bus_width);
else if (speed_mode == HSDDR52_DDR50)
ret = mmc_mmc_switch_bus_mode(mmc, HSDDR52_DDR50, bus_width);
else if (speed_mode == HS200_SDR104)
ret = mmc_mmc_switch_bus_mode(mmc, HS200_SDR104, bus_width);
else if (speed_mode == HS400) {
/* firstly, switch to HS-DDR 8 bit */
ret = mmc_mmc_switch_bus_mode(mmc, HSDDR52_DDR50, bus_width);
if (ret) {
MMCINFO("switch to %s fail\n", spd_name[speed_mode]);
goto OUT;
}
/* then, switch to HS400 */
ret = mmc_mmc_switch_bus_mode(mmc, HS400, bus_width);
}
if (ret) {
MMCINFO("switch to %s fail\n", spd_name[speed_mode]);
goto OUT;
}
}
else
{
if (speed_mode >= HSDDR52_DDR50) {
MMCINFO("don't spport %s for sd card\n", spd_name[speed_mode]);
ret = -1;
goto OUT;
}
}
/* execute tuning for current speed mode */
if (r_cycle == 0)
r_cycle = 15;
if (speed_mode == HS400) {
ret = sunxi_tuning_hs400_cmd(mmc, speed_mode, 1, r_cycle);
if (ret < 0) {
MMCINFO("tuning hs400 cmd line fail\n");
goto OUT;
}
}
ret = sunxi_tuning_speed_mode(mmc, speed_mode, 0, r_cycle);
if (ret) {
MMCINFO("tuning for %s fail\n", spd_name[speed_mode]);
}
OUT:
return ret;
}
int sunxi_pack_tuning_result(struct mmc *mmc)
{
struct sunxi_mmc_host *host = (struct sunxi_mmc_host *)mmc->priv;
//char *spd_name[] = {"DS26/SDR12", "HSSDR52/SDR25", "HSDDR52/DDR50", "HS200/SDR104", "HS400"};
u32 tm = host->timing_mode;
int spd_md, freq, g, group;
u32 t, val = 0;
u8 *p = NULL;
#if 0
for (spd_md=0; spd_md<MAX_SPD_MD_NUM; spd_md++)
{
p = host->tm4.sdly;
for (freq=0; freq<MAX_CLK_FREQ_NUM; freq++)
{
printf("%02x ", p[spd_md*MAX_CLK_FREQ_NUM + freq]);
}
if (spd_md == MAX_SPD_MD_NUM-1)
{
printf("\n---------- ds delay\n");
p = host->tm4.dsdly;
for (freq=0; freq<MAX_CLK_FREQ_NUM; freq++)
{
printf("%02x ", p[freq]);
}
}
printf("\n");
}
printf("\n");
#endif
for (spd_md=0; spd_md<MAX_SPD_MD_NUM; spd_md++)
{
/* get timing info of current timing mode */
if (spd_md == HS400)
group = 2;
else
group = 1;
for (g=0; g<group; g++)
{
/* get timing info of current timing mode */
if (tm == SUNXI_MMC_TIMING_MODE_2) {
if ((spd_md == HS400) && (g == 0))
p = host->tm2.dsdly;
else
p = host->tm2.sdly;
} else if (tm == SUNXI_MMC_TIMING_MODE_4) {
if ((spd_md == HS400) && (g == 0))
p = host->tm4.dsdly;
else
p = host->tm4.sdly;
}
val = 0;
for (freq=0; freq<4; freq++) {
if ((spd_md == HS400) && (g == 0))
t = p[freq];
else
t = p[spd_md*MAX_CLK_FREQ_NUM+freq];
val |= ((t & 0xFF) << (8*freq));
}
/* because it only use one timing mode and generate one group of timing info. even current
timing mode is SUNXI_MMC_TIMING_MODE_2, but we also pack the timing info of
SUNXI_MMC_TIMING_MODE_2 to cfg.platform_caps.sdly.tm4_smx_fx[].
*/
host->cfg.platform_caps.sdly.tm4_smx_fx[spd_md*2 + g*2 + 0] = val;
val = 0;
for (freq=4; freq<MAX_CLK_FREQ_NUM; freq++) {
if ((spd_md == HS400) && (g == 0))
t = p[freq];
else
t = p[spd_md*MAX_CLK_FREQ_NUM+freq];
val |= ((t & 0xFF) << (8*(freq-4)));
}
host->cfg.platform_caps.sdly.tm4_smx_fx[spd_md*2 + g*2 + 1] = val;
MMCINFO("%s: 0x%08x 0x%08x\n", spd_name[spd_md],
host->cfg.platform_caps.sdly.tm4_smx_fx[spd_md*2 + g*2 + 0],
host->cfg.platform_caps.sdly.tm4_smx_fx[spd_md*2 + g*2 + 1]);
}
}
return 0;
}
int sunxi_bus_tuning(struct mmc *mmc)
{
int err = 0, ret = 0;
unsigned err_flag = 0;
#if 0
MMCINFO("================== start tuning DS26_SDR12...\n");
ret = sunxi_execute_tuning(mmc, DS26_SDR12);
if (ret) {
MMCINFO("tuning fail at DS26_SDR12\n");
err = -1;
}
#endif
MMCINFO("================== HSSDR52_SDR25...\n");
ret = sunxi_execute_tuning(mmc, HSSDR52_SDR25);
if (ret) {
MMCINFO("tuning fail at HSSDR52_SDR25\n");
err = -1;
goto ERR_RET;
}
if (mmc->cfg->host_caps & MMC_MODE_HS200)
{
MMCINFO("================== HS200_SDR104...\n");
ret = sunxi_execute_tuning(mmc, HS200_SDR104);
if (ret) {
MMCINFO("tuning fail at HS200_SDR104\n");
//err = -2;
//goto ERR_RET;
err_flag |= 0x1;
}
}
if (mmc->cfg->host_caps & MMC_MODE_DDR_52MHz)
{
MMCINFO("================== HSDDR52_DDR50...\n");
ret = sunxi_execute_tuning(mmc, HSDDR52_DDR50);
if (ret) {
MMCINFO("tuning fail at HSDDR52_DDR50\n");
//err = -3;
//goto ERR_RET;
err_flag |= 0x2;
}
}
if (((mmc->cfg->host_caps & (MMC_MODE_HS400|MMC_MODE_8BIT)) == (MMC_MODE_HS400|MMC_MODE_8BIT))
&& !(err_flag & 0x3))
{
MMCINFO("================== HS400...\n");
ret = sunxi_execute_tuning(mmc, HS400);
if (ret) {
MMCINFO("tuning fail at HS400\n");
err = -4;
goto ERR_RET;
}
ret = mmc_mmc_switch_bus_mode(mmc, HSDDR52_DDR50, mmc->bus_width);
if (ret) {
MMCINFO("switch back to HSDDR52_DDR50 8bit fail\n");
err = -5;
goto ERR_RET;
}
}
ret = mmc_mmc_switch_bus_mode(mmc, HSSDR52_SDR25, mmc->bus_width);
if (ret) {
MMCINFO("switch back to HSSDR52_SDR25 8bit fail\n");
err = -6;
goto ERR_RET;
}
sunxi_pack_tuning_result(mmc);
ERR_RET:
return err;
}
int sunxi_switch_to_best_bus(struct mmc *mmc)
{
int ret = 0, err;
struct sunxi_mmc_host *host = (struct sunxi_mmc_host *)mmc->priv;
int ifreq, imd, freq;
int sdly = 0, dsdly = 0;
int bus_width = 0;
int work_mode = uboot_spare_head.boot_data.work_mode;
char caps_bak = 0;
ALLOC_CACHE_ALIGN_BUFFER(char, ext_csd, MMC_MAX_BLOCK_LEN);
u32 tm = host->timing_mode;
u8 *p = NULL, *pds=NULL;
if (IS_SD(mmc) || (host->mmc_no == 0))
{
return 0;
}
if ((work_mode == WORK_MODE_BOOT)
|| ((work_mode != WORK_MODE_BOOT)
&& (host->cfg.platform_caps.sample_mode != AUTO_SAMPLE_MODE)))
{
char cardtype;
caps_bak = mmc->card_caps;
mmc->card_caps = 0;
if (mmc_host_is_spi(mmc))
return 0;
/* Only version 4 supports high-speed */
if (mmc->version < MMC_VERSION_4)
return 0;
/* here we assume eMMC support 8 bit */
mmc->card_caps |= MMC_MODE_4BIT | MMC_MODE_8BIT;
#ifdef FPGA_PLATFORM
/*just for 1718 fpga, wangwei*/
mdelay(100);
#endif
err = mmc_send_ext_csd(mmc, ext_csd);
if (err) {
MMCINFO("mmc get ext csd failed\n");
return err;
}
cardtype = ext_csd[EXT_CSD_CARD_TYPE] & 0xFF;
MMCDBG("raw card caps 0x%x\n", cardtype);
mmc->card_caps = caps_bak;
if ((cardtype & EXT_CSD_CARD_TYPE_HS400)
&& (mmc->card_caps & MMC_MODE_8BIT))
mmc->card_caps |= MMC_MODE_HS400;
if (cardtype & EXT_CSD_CARD_TYPE_HS200)
mmc->card_caps |= MMC_MODE_HS200;
if (cardtype & EXT_CSD_CARD_TYPE_HS) {
if (cardtype & EXT_CSD_CARD_TYPE_DDR_52)
mmc->card_caps |= MMC_MODE_DDR_52MHz;
}
}
MMCDBG("card caps 0x%x\n", mmc->card_caps);
if (tm == SUNXI_MMC_TIMING_MODE_2) {
p = &host->tm2.sdly[0];
pds = &host->tm2.dsdly[0];
} else if (tm == SUNXI_MMC_TIMING_MODE_4) {
p = &host->tm4.sdly[0];
pds = &host->tm4.dsdly[0];
} else {
MMCINFO("%s: err timing mode %d\n", __FUNCTION__, tm);
goto OUT;
}
if ((mmc->card_caps & (MMC_MODE_HS400|MMC_MODE_8BIT))
== (MMC_MODE_HS400|MMC_MODE_8BIT))
{
imd = HS400;
for (ifreq=5; ifreq>=2; ifreq--) /*1-25MHz; 2-50MHz; 3-100MHz; 4-150MHz; 5-200MHz*/
{
imd = HS200_SDR104;
sdly = p[imd*MAX_CLK_FREQ_NUM+ifreq];
imd = HS400;
dsdly = pds[ifreq];
if ((sdly != 0xff) && (dsdly != 0xff))
goto START_SWITCH;
}
}
if (mmc->card_caps & MMC_MODE_HS200)
{
imd = HS200_SDR104;
for (ifreq=5; ifreq>=4; ifreq--) /*1-25MHz; 2-50MHz; 3-100MHz; 4-150MHz; 5-200MHz*/
{
sdly = p[imd*MAX_CLK_FREQ_NUM+ifreq];
if (sdly != 0xFF)
goto START_SWITCH;
}
}
if (mmc->card_caps & MMC_MODE_DDR_52MHz)
{
imd = HSDDR52_DDR50;
for (ifreq=2; ifreq>=2; ifreq--) /*1-25MHz; 2-50MHz; 3-100MHz; 4-150MHz; 5-200MHz*/
{
sdly = p[imd*MAX_CLK_FREQ_NUM+ifreq];
if (sdly != 0xFF)
goto START_SWITCH;
}
}
if (mmc->card_caps & MMC_MODE_HS_52MHz)
{
imd = HSSDR52_SDR25;
for (ifreq=2; ifreq>=1; ifreq--) /*1-25MHz; 2-50MHz; 3-100MHz; 4-150MHz; 5-200MHz*/
{
sdly = p[imd*MAX_CLK_FREQ_NUM+ifreq];
if (sdly != 0xFF)
goto START_SWITCH;
}
}
//imd = DS26_SDR12;
//ifreq = CLK_25M;
imd = HSSDR52_SDR25;
ifreq = CLK_50M;
MMCINFO("use default speed mode: %d-%s, ifreq: %d\n", imd, spd_name[imd], ifreq);
//MMCINFO("don't find best speed mode and freq\n");
//ret = -1;
//goto OUT;
START_SWITCH:
if ((freq = sunxi_select_freq(mmc, imd, ifreq)) != 0)
mmc->tran_speed = freq;
else {
MMCINFO("%s: err freq %d-%d\n", __func__, ifreq, freq);
ret = -1;
goto OUT;
}
MMCINFO("========best spd md: %d-%s, freq: %d-%d\n", imd, spd_name[imd], ifreq, freq);
if (mmc->card_caps & MMC_MODE_8BIT)
bus_width = 8;
else if (mmc->card_caps & MMC_MODE_4BIT)
bus_width = 4;
else
bus_width = 1;
/* switch to specific speed mode */
if (imd == DS26_SDR12)
ret = mmc_mmc_switch_bus_mode(mmc, DS26_SDR12, bus_width);
else if (imd == HSSDR52_SDR25)
ret = mmc_mmc_switch_bus_mode(mmc, HSSDR52_SDR25, bus_width);
else if (imd == HSDDR52_DDR50)
ret = mmc_mmc_switch_bus_mode(mmc, HSDDR52_DDR50, bus_width);
else if (imd == HS200_SDR104)
ret = mmc_mmc_switch_bus_mode(mmc, HS200_SDR104, bus_width);
else if (imd == HS400) {
/* firstly, switch to HS-DDR 8 bit */
ret = mmc_mmc_switch_bus_mode(mmc, HSDDR52_DDR50, bus_width);
if (ret) {
MMCINFO("switch to %s fail\n", spd_name[HSDDR52_DDR50]);
goto OUT;
}
/* then, switch to HS400 */
ret = mmc_mmc_switch_bus_mode(mmc, HS400, bus_width);
}
if (ret) {
MMCINFO("switch to %s fail\n", spd_name[HS400]);
goto OUT;
}
mmc_set_clock(mmc, mmc->tran_speed);
OUT:
return ret;
}
int mmc_request_update_boot0(int dev_num)
{
struct mmc *mmc = find_mmc_device(dev_num);
if (mmc == NULL)
return 0;
if ((mmc->cfg->platform_caps.sample_mode == AUTO_SAMPLE_MODE)
&& (mmc->tuning_end)) {
MMCINFO("mmc request udpate boot0\n");
return 1;
} else
return 0;
}
int mmc_write_info(int dev_num, void *buffer, u32 buffer_size)
{
struct mmc *mmc = find_mmc_device(dev_num);
//int work_mode = uboot_spare_head.boot_data.work_mode;
struct boot_sdmmc_private_info_t priv_info;
if (mmc == NULL) {
MMCINFO("%s:Can not find mmc\n", __FUNCTION__);
return -1;
}
/* this function will be called by fastboot, so delete this limitation. */
//if (work_mode != WORK_MODE_BOOT)
{
memset(&priv_info, 0x0, sizeof(priv_info));
if (mmc->cfg->platform_caps.sample_mode == AUTO_SAMPLE_MODE) {
if ((sizeof(struct tune_sdly)+64) > buffer_size) { /* 64byte is resvered for other information */
MMCINFO("size of tuning_sdly over %d\n", buffer_size);
} else
memcpy(&priv_info.tune_sdly.tm4_smx_fx[0],
(void *)&mmc->cfg->platform_caps.sdly.tm4_smx_fx[0], sizeof(struct tune_sdly));
/* if tuning procedure is executed successfully, set this flag. uboot will check this flag and determine it is need to
execute tuning when boot */
priv_info.ext_para0 = EXT_PARA0_ID;
if (mmc->tuning_end)
priv_info.ext_para0 |= EXT_PARA0_TUNING_SUCCESS_FLAG;
} else {
/* fill invalid information "0xff" */
memset(&priv_info.tune_sdly.tm4_smx_fx[0], 0xff, sizeof(struct tune_sdly));
}
priv_info.boot_mmc_cfg.boot0_para = mmc->cfg->platform_caps.boot0_para;
priv_info.boot_mmc_cfg.boot_odly_50M = mmc->cfg->platform_caps.boot_odly_50M;
priv_info.boot_mmc_cfg.boot_sdly_50M = mmc->cfg->platform_caps.boot_sdly_50M;
priv_info.boot_mmc_cfg.boot_odly_50M_ddr = mmc->cfg->platform_caps.boot_odly_50M_ddr;
priv_info.boot_mmc_cfg.boot_sdly_50M_ddr = mmc->cfg->platform_caps.boot_sdly_50M_ddr;
priv_info.boot_mmc_cfg.boot_hs_f_max = mmc->cfg->platform_caps.boot_hs_f_max;
if (IS_SD(mmc))
priv_info.card_type = CARD_TYPE_SD;
else
priv_info.card_type = CARD_TYPE_MMC;
/*
----- normal
offset 0~127: boot0 struct _boot_sdcard_info_t;
offset 128~255: struct tune_sdly, timing parameter for speed mode and frequency
----- secure
offset 128 ~ (224=384-160): struct tune_sdly, timing parameter for speed mode and frequency
sizeof(priv_info) is about 60 bytes.
*/
memcpy((buffer+SDMMC_PRIV_INFO_ADDR_OFFSET), (void *)&priv_info, sizeof(priv_info));
#if 0
{
u32 i, *p;
p = (u32 *)(buffer+SDMMC_PRIV_INFO_ADDR_OFFSET);
for (i=0; i<sizeof(priv_info)/4; i++)
MMCINFO("%d %x\n", i, p[i]);
}
#endif
MMCINFO("write mmc info ok\n");
return 0;
}
return -1;
}