emmc: add emmc HS400 function

PD#142470: emmc: support hs400 busmode

1. refix source clock 400MHZ
2. add hs400 timming function

Change-Id: I9cc767262379ba2be5ab5d3e68aae87c1f18c242
Signed-off-by: Long Yu <long.yu@amlogic.com>
This commit is contained in:
Long Yu
2017-05-19 09:54:22 +08:00
committed by Victor Wan
parent 3c14d09194
commit 65b59ca4e2
10 changed files with 660 additions and 62 deletions

View File

@@ -462,8 +462,9 @@
pinctrl-1 = <&emmc_conf_pull_up &emmc_conf_pull_done>;
clocks = <&clkc CLKID_SD_EMMC_C>,
<&clkc CLKID_SD_EMMC_C_P0_COMP>,
<&clkc CLKID_FCLK_DIV2>;
clock-names = "core", "clkin0", "clkin1";
<&clkc CLKID_FCLK_DIV2>,
<&clkc CLKID_FCLK_DIV5>;
clock-names = "core", "clkin0", "clkin1", "clkin2";
bus-width = <8>;
cap-sd-highspeed;
@@ -486,9 +487,9 @@
"MMC_CAP_HW_RESET",
"MMC_CAP_ERASE",
"MMC_CAP_CMD23";
/*caps2 = "MMC_CAP2_HS200", "MMC_CAP2_HS400";*/
caps2 = "MMC_CAP2_HS200", "MMC_CAP2_HS400";
f_min = <300000>;
f_max = <100000000>;
f_max = <200000000>;
max_req_size = <0x20000>; /**128KB*/
gpio_dat3 = <&gpio BOOT_3 GPIO_ACTIVE_HIGH>;
hw_reset = <&gpio BOOT_9 GPIO_ACTIVE_HIGH>;
@@ -510,8 +511,9 @@
pinctrl-1 = <&sdio_all_pins>;
clocks = <&clkc CLKID_SD_EMMC_B>,
<&clkc CLKID_SD_EMMC_B_P0_COMP>,
<&clkc CLKID_FCLK_DIV2>;
clock-names = "core", "clkin0", "clkin1";
<&clkc CLKID_FCLK_DIV2>,
<&clkc CLKID_FCLK_DIV5>;
clock-names = "core", "clkin0", "clkin1", "clkin2";
bus-width = <4>;
cap-sd-highspeed;

View File

@@ -388,8 +388,9 @@
pinctrl-1 = <&emmc_conf_pull_up &emmc_conf_pull_done>;
clocks = <&clkc CLKID_SD_EMMC_C>,
<&clkc CLKID_SD_EMMC_C_P0_COMP>,
<&clkc CLKID_FCLK_DIV2>;
clock-names = "core", "clkin0", "clkin1";
<&clkc CLKID_FCLK_DIV2>,
<&clkc CLKID_FCLK_DIV5>;
clock-names = "core", "clkin0", "clkin1", "clkin2";
bus-width = <8>;
cap-sd-highspeed;

View File

@@ -1240,6 +1240,8 @@ int aml_emmc_clktree_init(struct amlsd_host *host)
ret = PTR_ERR(host->core_clk);
return ret;
}
pr_info("core->rate: %lu\n", clk_get_rate(host->core_clk));
pr_info("core->name: %s\n", __clk_get_name(host->core_clk));
ret = clk_prepare_enable(host->core_clk);
if (ret)
return ret;
@@ -1257,6 +1259,8 @@ int aml_emmc_clktree_init(struct amlsd_host *host)
}
host->mux_parent_rate[i] = clk_get_rate(host->mux_parent[i]);
mux_parent_names[i] = __clk_get_name(host->mux_parent[i]);
pr_info("rate: %lu, name: %s\n",
host->mux_parent_rate[i], mux_parent_names[i]);
mux_parent_count++;
if (host->mux_parent_rate[i] < f_min)
f_min = host->mux_parent_rate[i];
@@ -1272,6 +1276,7 @@ int aml_emmc_clktree_init(struct amlsd_host *host)
/* create the mux */
snprintf(clk_name, sizeof(clk_name), "%s#mux", dev_name(host->dev));
pr_info("clk_name: %s\n", clk_name);
init.name = clk_name;
init.ops = &clk_mux_ops;
init.flags = 0;
@@ -1306,7 +1311,8 @@ int aml_emmc_clktree_init(struct amlsd_host *host)
return PTR_ERR(host->cfg_div_clk);
ret = clk_prepare_enable(host->cfg_div_clk);
pr_info("[%s] clock: 0x%x\n",
__func__, readl(host->base + SD_EMMC_CLOCK_V3));
return ret;
}
@@ -1324,6 +1330,7 @@ static int meson_mmc_clk_init(struct amlsd_host *host)
struct sd_emmc_config *pconf = (struct sd_emmc_config *)&vconf;
struct amlsd_platform *pdata = host->pdata;
writel(0, host->base + SD_EMMC_CLOCK);
ret = aml_emmc_clktree_init(host);
if (ret)
return ret;
@@ -1662,7 +1669,7 @@ err_exit:
*a linear buffer and an SG list for amlogic,
* We don't disable irq in this function
**/
static int aml_sd_emmc_post_dma(struct amlsd_host *host,
int aml_sd_emmc_post_dma(struct amlsd_host *host,
struct mmc_request *mrq)
{
struct mmc_data *data = NULL;
@@ -1718,7 +1725,7 @@ static void aml_sd_emmc_check_sdio_irq(struct amlsd_host *host)
}
}
}
static int meson_mmc_request_done(struct mmc_host *mmc, struct mmc_request *mrq)
int meson_mmc_request_done(struct mmc_host *mmc, struct mmc_request *mrq)
{
struct amlsd_host *host = mmc_priv(mmc);
struct amlsd_platform *pdata = host->pdata;
@@ -2023,7 +2030,7 @@ static void meson_mmc_request(struct mmc_host *mmc, struct mmc_request *mrq)
spin_unlock_irqrestore(&host->mrq_lock, flags);
}
static int meson_mmc_read_resp(struct mmc_host *mmc, struct mmc_command *cmd)
int meson_mmc_read_resp(struct mmc_host *mmc, struct mmc_command *cmd)
{
struct amlsd_host *host = mmc_priv(mmc);
struct sd_emmc_desc_info *desc_info =
@@ -2694,6 +2701,7 @@ static const struct mmc_host_ops meson_mmc_ops_v3 = {
.card_busy = aml_sd_emmc_card_busy,
.execute_tuning = aml_mmc_execute_tuning_v3,
.hw_reset = aml_emmc_hw_reset,
.post_hs400_timming = aml_post_hs400_timming,
};
static void aml_reg_print(struct amlsd_host *host)
@@ -2746,8 +2754,12 @@ static int meson_mmc_probe(struct platform_device *pdev)
ret = -EINVAL;
goto free_host;
}
ret = devm_request_threaded_irq(&pdev->dev, host->irq,
if (host->ctrl_ver >= 3)
ret = devm_request_threaded_irq(&pdev->dev, host->irq,
meson_mmc_irq, meson_mmc_irq_thread_v3,
IRQF_SHARED, "meson-aml-mmc", host);
else
ret = devm_request_threaded_irq(&pdev->dev, host->irq,
meson_mmc_irq, meson_mmc_irq_thread,
IRQF_SHARED, "meson-aml-mmc", host);
if (ret)

View File

@@ -51,6 +51,7 @@ int meson_mmc_clk_init_v3(struct amlsd_host *host)
struct sd_emmc_config *pconf = (struct sd_emmc_config *)&vconf;
struct amlsd_platform *pdata = host->pdata;
writel(0, host->base + SD_EMMC_CLOCK);
ret = aml_emmc_clktree_init(host);
if (ret)
return ret;
@@ -89,7 +90,10 @@ static int meson_mmc_clk_set_rate_v3(struct amlsd_host *host,
unsigned long clk_ios)
{
struct mmc_host *mmc = host->mmc;
struct amlsd_platform *pdata = host->pdata;
int ret = 0;
struct clk *fdiv5_clk = NULL;
void __iomem *source_base = NULL;
#ifdef SD_EMMC_CLK_CTRL
u32 clk_rate, clk_div, clk_src_sel;
struct amlsd_platform *pdata = host->pdata;
@@ -141,7 +145,8 @@ static int meson_mmc_clk_set_rate_v3(struct amlsd_host *host,
#else
if (clk_ios == mmc->actual_clock)
return 0;
pr_info("[%s] before clock: 0x%x\n",
__func__, readl(host->base + SD_EMMC_CLOCK_V3));
/* stop clock */
vcfg = readl(host->base + SD_EMMC_CFG);
if (!conf->stop_clk) {
@@ -149,6 +154,16 @@ static int meson_mmc_clk_set_rate_v3(struct amlsd_host *host,
writel(vcfg, host->base + SD_EMMC_CFG);
}
if ((clk_ios >= 200000000) && aml_card_type_mmc(pdata)) {
ret = clk_set_parent(host->cfg_div_clk, host->mux_clk);
if (ret)
pr_warn("set mux_clk as parent error\n");
fdiv5_clk = devm_clk_get(host->dev, "clkin2");
ret = clk_set_parent(host->mux_parent[0], fdiv5_clk);
if (ret)
pr_warn("set fdiv5_clk as parent error\n");
}
dev_dbg(host->dev, "change clock rate %u -> %lu\n",
mmc->actual_clock, clk_ios);
ret = clk_set_rate(host->cfg_div_clk, clk_ios);
@@ -165,7 +180,16 @@ static int meson_mmc_clk_set_rate_v3(struct amlsd_host *host,
writel(vcfg, host->base + SD_EMMC_CFG);
}
#endif
source_base =
ioremap_nocache(P_HHI_NAND_CLK_CNTL,
sizeof(u32));
/* pr_info("actual_clock :%u, HHI_nand: 0x%x\n",
* mmc->actual_clock, readl(source_base));
*/
/* pr_info("[%s] after clock: 0x%x\n",
* __func__, readl(host->base + SD_EMMC_CLOCK_V3));
*/
iounmap(source_base);
return ret;
}
@@ -192,6 +216,7 @@ static void aml_sd_emmc_set_timing_v3(struct amlsd_host *host,
adjust = readl(host->base + SD_EMMC_ADJUST_V3);
gadjust->ds_enable = 1;
writel(adjust, host->base + SD_EMMC_ADJUST_V3);
clk_rate = 400000000;
/*host->tuning_mode = AUTO_TUNING_MODE;*/
}
}
@@ -273,8 +298,7 @@ int aml_mmc_execute_tuning_v3(struct mmc_host *mmc, u32 opcode)
{
struct amlsd_host *host = mmc_priv(mmc);
struct aml_tuning_data tuning_data;
int err;
u32 adj_win_start = 100;
int err = 0;
u32 intf3;
if (opcode == MMC_SEND_TUNING_BLOCK_HS200) {
@@ -298,8 +322,6 @@ int aml_mmc_execute_tuning_v3(struct mmc_host *mmc, u32 opcode)
intf3 = readl(host->base + SD_EMMC_INTF3);
intf3 |= (1 << 22);
writel(intf3, host->base + SD_EMMC_INTF3);
err = aml_sd_emmc_execute_tuning_(mmc, opcode,
&tuning_data, adj_win_start);
pr_info("%s: gclock=0x%x, gdelay1=0x%x, gdelay2=0x%x,intf3=0x%x\n",
mmc_hostname(mmc), readl(host->base + SD_EMMC_CLOCK_V3),
@@ -309,39 +331,553 @@ int aml_mmc_execute_tuning_v3(struct mmc_host *mmc, u32 opcode)
return err;
}
/*static int aml_sd_emmc_cali_v3(struct mmc_host *mmc,
* u8 opcode, u8 *blk_test, u32 blksz, u32 blocks)
*{
* struct amlsd_host *host = mmc_priv(mmc);
* struct mmc_request mrq = {NULL};
* struct mmc_command cmd = {0};
* struct mmc_command stop = {0};
* struct mmc_data data = {0};
* struct scatterlist sg;
*
* cmd.opcode = opcode;
* cmd.arg = ((SZ_1M * (36 + 3)) / 512);
* cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
*
* stop.opcode = MMC_STOP_TRANSMISSION;
* stop.arg = 0;
* stop.flags = MMC_RSP_R1B | MMC_CMD_AC;
*
* data.blksz = blksz;
* data.blocks = blocks;
* data.flags = MMC_DATA_READ;
* data.sg = &sg;
* data.sg_len = 1;
*
* memset(blk_test, 0, blksz * data.blocks);
* sg_init_one(&sg, blk_test, blksz * data.blocks);
*
* mrq.cmd = &cmd;
* mrq.stop = &stop;
* mrq.data = &data;
* host->mrq = &mrq;
* mmc_wait_for_req(mmc, &mrq);
* return data.error | cmd.error;
*}
*/
irqreturn_t meson_mmc_irq_thread_v3(int irq, void *dev_id)
{
struct amlsd_host *host = dev_id;
struct amlsd_platform *pdata = host->pdata;
struct mmc_request *mrq;
unsigned long flags;
enum aml_mmc_waitfor xfer_step;
u32 status, xfer_bytes = 0;
spin_lock_irqsave(&host->mrq_lock, flags);
mrq = host->mrq;
xfer_step = host->xfer_step;
status = host->status;
if ((xfer_step == XFER_FINISHED) || (xfer_step == XFER_TIMER_TIMEOUT)) {
sd_emmc_err("Warning: %s xfer_step=%d, host->status=%d\n",
mmc_hostname(host->mmc), xfer_step, status);
spin_unlock_irqrestore(&host->mrq_lock, flags);
return IRQ_HANDLED;
}
WARN_ON((host->xfer_step != XFER_IRQ_OCCUR)
&& (host->xfer_step != XFER_IRQ_TASKLET_BUSY));
if (!mrq) {
sd_emmc_err("%s: !mrq xfer_step %d\n",
mmc_hostname(host->mmc), xfer_step);
if (xfer_step == XFER_FINISHED ||
xfer_step == XFER_TIMER_TIMEOUT){
spin_unlock_irqrestore(&host->mrq_lock, flags);
return IRQ_HANDLED;
}
/* aml_sd_emmc_print_err(host); */
}
/* process stop cmd we sent on porpos */
if (host->cmd_is_stop) {
/* --new irq enter, */
host->cmd_is_stop = 0;
mrq->cmd->error = host->error_bak;
spin_unlock_irqrestore(&host->mrq_lock, flags);
meson_mmc_request_done(host->mmc, host->mrq);
return IRQ_HANDLED;
}
spin_unlock_irqrestore(&host->mrq_lock, flags);
WARN_ON(!host->mrq->cmd);
switch (status) {
case HOST_TASKLET_DATA:
case HOST_TASKLET_CMD:
/* WARN_ON(aml_sd_emmc_desc_check(host)); */
sd_emmc_dbg(AMLSD_DBG_REQ, "%s %d cmd:%d\n",
__func__, __LINE__, mrq->cmd->opcode);
host->error_flag = 0;
if (mrq->cmd->data && mrq->cmd->opcode) {
xfer_bytes = mrq->data->blksz*mrq->data->blocks;
/* copy buffer from dma to data->sg in read cmd*/
#ifdef SD_EMMC_REQ_DMA_SGMAP
WARN_ON(aml_sd_emmc_post_dma(host, mrq));
#else
if (host->mrq->data->flags & MMC_DATA_READ) {
aml_sg_copy_buffer(mrq->data->sg,
mrq->data->sg_len, host->bn_buf,
xfer_bytes, 0);
}
#endif
mrq->data->bytes_xfered = xfer_bytes;
host->xfer_step = XFER_TASKLET_DATA;
} else {
host->xfer_step = XFER_TASKLET_CMD;
}
spin_lock_irqsave(&host->mrq_lock, flags);
mrq->cmd->error = 0;
spin_unlock_irqrestore(&host->mrq_lock, flags);
meson_mmc_read_resp(host->mmc, mrq->cmd);
meson_mmc_request_done(host->mmc, mrq);
break;
case HOST_RSP_TIMEOUT_ERR:
case HOST_DAT_TIMEOUT_ERR:
case HOST_RSP_CRC_ERR:
case HOST_DAT_CRC_ERR:
if (host->is_tunning == 0)
pr_info("%s %d %s: cmd:%d\n", __func__, __LINE__,
mmc_hostname(host->mmc), mrq->cmd->opcode);
if (mrq->cmd->data) {
dma_unmap_sg(mmc_dev(host->mmc), mrq->cmd->data->sg,
mrq->cmd->data->sg_len,
(mrq->cmd->data->flags & MMC_DATA_READ) ?
DMA_FROM_DEVICE : DMA_TO_DEVICE);
}
meson_mmc_read_resp(host->mmc, host->mrq->cmd);
/* set retry @ 1st error happens! */
if ((host->error_flag == 0)
&& (aml_card_type_mmc(pdata)
|| aml_card_type_non_sdio(pdata))
&& (host->is_tunning == 0)) {
sd_emmc_err("%s() %d: set 1st retry!\n",
__func__, __LINE__);
host->error_flag |= (1<<0);
spin_lock_irqsave(&host->mrq_lock, flags);
mrq->cmd->retries = 3;
spin_unlock_irqrestore(&host->mrq_lock, flags);
}
if (aml_card_type_mmc(pdata) &&
(host->error_flag & (1<<0)) && mrq->cmd->retries) {
sd_emmc_err("retry cmd %d the %d-th time(s)\n",
mrq->cmd->opcode, mrq->cmd->retries);
/* chage configs on current host */
}
/* last retry effort! */
if ((aml_card_type_mmc(pdata) || aml_card_type_non_sdio(pdata))
&& host->error_flag && (mrq->cmd->retries == 0)) {
host->error_flag |= (1<<30);
sd_emmc_err("Command retried failed line:%d, cmd:%d\n",
__LINE__, mrq->cmd->opcode);
}
/* retry need send a stop 2 emmc... */
/* do not send stop for sdio wifi case */
if (host->mrq->stop
&& (aml_card_type_mmc(pdata)
|| aml_card_type_non_sdio(pdata))
&& pdata->is_in
&& (host->mrq->cmd->opcode != MMC_SEND_TUNING_BLOCK)
&& (host->mrq->cmd->opcode !=
MMC_SEND_TUNING_BLOCK_HS200))
aml_sd_emmc_send_stop(host);
else
meson_mmc_request_done(host->mmc, mrq);
break;
default:
sd_emmc_err("BUG %s: xfer_step=%d, host->status=%d\n",
mmc_hostname(host->mmc), xfer_step, status);
/* aml_sd_emmc_print_err(host); */
}
return IRQ_HANDLED;
}
static int aml_sd_emmc_cali_v3(struct mmc_host *mmc,
u8 opcode, u8 *blk_test, u32 blksz, u32 blocks)
{
struct amlsd_host *host = mmc_priv(mmc);
struct mmc_request mrq = {NULL};
struct mmc_command cmd = {0};
struct mmc_command stop = {0};
struct mmc_data data = {0};
struct scatterlist sg;
cmd.opcode = opcode;
cmd.arg = ((SZ_1M * (36 + 3)) / 512);
cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
stop.opcode = MMC_STOP_TRANSMISSION;
stop.arg = 0;
stop.flags = MMC_RSP_R1B | MMC_CMD_AC;
data.blksz = blksz;
data.blocks = blocks;
data.flags = MMC_DATA_READ;
data.sg = &sg;
data.sg_len = 1;
memset(blk_test, 0, blksz * data.blocks);
sg_init_one(&sg, blk_test, blksz * data.blocks);
mrq.cmd = &cmd;
mrq.stop = &stop;
mrq.data = &data;
host->mrq = &mrq;
mmc_wait_for_req(mmc, &mrq);
return data.error | cmd.error;
}
#define EMMC_TIMMING_DBG 1
static int emmc_eyetest_log(struct mmc_host *mmc, u32 line_x)
{
struct amlsd_host *host = mmc_priv(mmc);
struct amlsd_platform *pdata = host->pdata;
u32 adjust = readl(host->base + SD_EMMC_ADJUST_V3);
struct sd_emmc_adjust_v3 *gadjust =
(struct sd_emmc_adjust_v3 *)&adjust;
/*u32 delay1 = sd_emmc_regs->reg.v3.gdelay1;
*struct sd_emmc_delay1 *gdelay1 = (struct sd_emmc_delay1 *)&delay1;
*u32 delay2 = sd_emmc_regs->reg.v3.gdelay2;
*struct sd_emmc_delay2 *gdelay2 = (struct sd_emmc_delay2 *)&delay2;
*u32 clktest_log = 0;
*struct clktest_log gclktest_log =
* (struct clktest_log *)&(clktest_log);
*u32 clktest_out = 0;
*/
u32 eyetest_log = 0;
struct eyetest_log *geyetest_log = (struct eyetest_log *)&(eyetest_log);
u32 eyetest_out0 = 0, eyetest_out1 = 0;
u32 intf3 = readl(host->base + SD_EMMC_INTF3);
struct intf3 *gintf3 = (struct intf3 *)&(intf3);
u32 vcfg = readl(host->base + SD_EMMC_CFG);
int j = 0;
u64 tmp = 0;
u32 blksz = 512;
u8 *blk_test;
blk_test = kmalloc(blksz * CALI_BLK_CNT, GFP_KERNEL);
if (!blk_test)
return -ENOMEM;
host->is_tunning = 1;
emmc_dbg(EMMC_TIMMING_DBG,
"line_x: %d\n", line_x);
/*sd_emmc_regs->reg.v3.gdelay1 = delay1;
*sd_emmc_regs->reg.v3.gdelay2 = delay2;
*/
emmc_dbg(EMMC_TIMMING_DBG, "delay1: 0x%x , delay2: 0x%x\n",
readl(host->base + SD_EMMC_DELAY1_V3),
readl(host->base + SD_EMMC_DELAY2_V3));
gadjust->cali_enable = 1;
gadjust->cali_sel = line_x;
writel(adjust, host->base + SD_EMMC_ADJUST_V3);
if (line_x < 9)
gintf3->eyetest_exp = 7;
else
gintf3->eyetest_exp = 3;
gintf3->eyetest_on = 1;
writel(intf3, host->base + SD_EMMC_INTF3);
emmc_dbg(EMMC_TIMMING_DBG, "intf3: 0x%x,adjust: 0x%x\n",
readl(host->base + SD_EMMC_INTF3),
readl(host->base + SD_EMMC_ADJUST_V3));
/*****test start*************/
if (line_x < 9)
aml_sd_emmc_cali_v3(mmc,
MMC_READ_MULTIPLE_BLOCK,
blk_test, blksz, 40);
else
aml_sd_emmc_cali_v3(mmc,
MMC_READ_MULTIPLE_BLOCK,
blk_test, blksz, 80);
eyetest_log = readl(host->base + SD_EMMC_EYETEST_LOG);
eyetest_out0 = readl(host->base + SD_EMMC_EYETEST_OUT0);
eyetest_out1 = readl(host->base + SD_EMMC_EYETEST_OUT1);
while ((!(geyetest_log->eyetest_done & 0x1))) {
eyetest_out0 = readl(host->base + SD_EMMC_EYETEST_OUT0);
eyetest_out1 = readl(host->base + SD_EMMC_EYETEST_OUT1);
eyetest_log = readl(host->base + SD_EMMC_EYETEST_LOG);
emmc_dbg(EMMC_TIMMING_DBG, "testing eyetest times: 0x%x, out: 0x%x, 0x%x\n",
readl(host->base + SD_EMMC_EYETEST_LOG),
eyetest_out0, eyetest_out1);
if (j == 10)
break;
j++;
}
emmc_dbg(EMMC_TIMMING_DBG, "test done! eyetest times: 0x%x, out: 0x%x, 0x%x\n",
readl(host->base + SD_EMMC_EYETEST_LOG),
eyetest_out0, eyetest_out1);
gintf3->eyetest_on = 0;
writel(intf3, host->base + SD_EMMC_INTF3);
emmc_dbg(EMMC_TIMMING_DBG, "intf3: 0x%x,adjust: 0x%x\n",
readl(host->base + SD_EMMC_INTF3),
readl(host->base + SD_EMMC_ADJUST_V3));
if (vcfg & 0x4) {
if (pdata->count > 32) {
eyetest_out1 <<= (32 - (pdata->count - 32));
eyetest_out1 >>= (32 - (pdata->count - 32));
} else
eyetest_out1 = 0x0;
}
pdata->align[line_x] = ((tmp | eyetest_out1) << 32) | eyetest_out0;
emmc_dbg(EMMC_TIMMING_DBG, "u64 eyetestout 0x%llx\n",
pdata->align[line_x]);
host->is_tunning = 0;
kfree(blk_test);
return 0;
}
static int fbinary(u64 x)
{
int i;
for (i = 0; i < 64; i++) {
if ((x >> i) & 0x1)
return i;
}
return -1;
}
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];
}
}
pr_warn("%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_host *mmc)
{
struct amlsd_host *host = mmc_priv(mmc);
struct amlsd_platform *pdata = host->pdata;
u32 delay1 = 0, delay2 = 0;
int result;
int temp = 0, base_line = 0, line_x = 0;
pdata->base_line = emmc_detect_base_line(pdata->align);
base_line = pdata->base_line;
for (line_x = 0; line_x < 9; line_x++) {
if (line_x == 8)
continue;
temp = fbinary(pdata->align[line_x]);
if (temp <= 4)
continue;
result = base_line - temp;
emmc_dbg(EMMC_TIMMING_DBG, "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));
}
delay1 |= readl(host->base + SD_EMMC_DELAY1_V3);
delay2 |= readl(host->base + SD_EMMC_DELAY2_V3);
writel(delay1, host->base + SD_EMMC_DELAY1_V3);
writel(delay2, host->base + SD_EMMC_DELAY2_V3);
emmc_dbg(EMMC_TIMMING_DBG, "delay1: 0x%x, delay2: 0x%x\n",
delay1, delay2);
emmc_dbg(EMMC_TIMMING_DBG, "gdelay1: 0x%x, gdelay2: 0x%x\n",
readl(host->base + SD_EMMC_DELAY1_V3),
readl(host->base + SD_EMMC_DELAY2_V3));
return 0;
}
static int emmc_ds_data_alignment(struct mmc_host *mmc)
{
struct amlsd_host *host = mmc_priv(mmc);
struct amlsd_platform *pdata = host->pdata;
u32 delay1 = readl(host->base + SD_EMMC_DELAY1_V3);
u32 delay2 = readl(host->base + SD_EMMC_DELAY2_V3);
int i;
for (i = 0; i < 64; i++) {
pr_info("i = %d, delay1: 0x%x, delay2: 0x%x\n",
i, readl(host->base + SD_EMMC_DELAY1_V3),
readl(host->base + SD_EMMC_DELAY2_V3));
delay1 += (1<<0)|(1<<6)|(1<<12)|(1<<18)|(1<<24);
delay2 += (1<<0)|(1<<6)|(1<<12)|(1<<24);
writel(delay1, host->base + SD_EMMC_DELAY1_V3);
writel(delay2, host->base + SD_EMMC_DELAY2_V3);
emmc_eyetest_log(mmc, 3);
emmc_eyetest_log(mmc, 8);
if (pdata->align[3] & 0xf)
break;
}
if (i == 64) {
pr_warn("%s [%d] Don't find line delay which aligned with DS\n",
__func__, __LINE__);
return 1;
}
return 0;
}
static void print_all_line_eyetest(struct mmc_host *mmc)
{
int line_x;
for (line_x = 0; line_x < 10; line_x++)
emmc_eyetest_log(mmc, line_x);
}
/* first step*/
static int emmc_ds_core_align(struct mmc_host *mmc)
{
struct amlsd_host *host = mmc_priv(mmc);
struct amlsd_platform *pdata = host->pdata;
u32 delay1 = readl(host->base + SD_EMMC_DELAY1_V3);
u32 delay2 = readl(host->base + SD_EMMC_DELAY2_V3);
u32 delay2_bak = delay2;
u32 count = 0;
u32 ds_count = 0;
if (pdata->align[8] & 0xf)
return 0;
ds_count = fbinary(pdata->align[8]);
//BUG_ON(ds_count >= 4 && ds_count <= 8);
emmc_dbg(EMMC_TIMMING_DBG, "ds_count:%d,delay1:0x%x,delay2:0x%x\n",
ds_count, readl(host->base + SD_EMMC_DELAY1_V3),
readl(host->base + SD_EMMC_DELAY2_V3));
if (ds_count < 20) {
delay2 += ((20 - ds_count) << 18);
writel(delay2, host->base + SD_EMMC_DELAY2_V3);
} else {
delay2 += (1<<18);
writel(delay2, host->base + SD_EMMC_DELAY2_V3);
}
emmc_eyetest_log(mmc, 8);
while (!(pdata->align[8] & 0xf)) {
delay2 += (1<<18);
writel(delay2, host->base + SD_EMMC_DELAY2_V3);
emmc_eyetest_log(mmc, 8);
}
delay1 = readl(host->base + SD_EMMC_DELAY1_V3);
delay2 = readl(host->base + SD_EMMC_DELAY2_V3);
ds_count = fbinary(pdata->align[8]);
count = ((delay2>>18) & 0x3f) - ((delay2_bak>>18) & 0x3f);
pdata->ds_core = count;
delay1 += (count<<0)|(count<<6)|(count<<12)|(count<<18)|(count<<24);
delay2 += (count<<0)|(count<<6)|(count<<12)|(count<<24);
writel(delay1, host->base + SD_EMMC_DELAY1_V3);
writel(delay2, host->base + SD_EMMC_DELAY2_V3);
emmc_dbg(EMMC_TIMMING_DBG,
"ds_count:%d,delay1:0x%x,delay2:0x%x,count: %u\n",
ds_count, readl(host->base + SD_EMMC_DELAY1_V3),
readl(host->base + SD_EMMC_DELAY2_V3), count);
return 0;
}
#if 1
static int emmc_ds_manual_sht(struct mmc_host *mmc)
{
struct amlsd_host *host = mmc_priv(mmc);
/* struct amlsd_platform *pdata = host->pdata; */
u32 intf3 = readl(host->base + SD_EMMC_INTF3);
struct intf3 *gintf3 = (struct intf3 *)&(intf3);
u8 *blk_test = NULL;
u32 blksz = 512;
int i, err = 0;
u32 sta = 0, end = 0, flag = 0;
blk_test = kmalloc(blksz * CALI_BLK_CNT, GFP_KERNEL);
if (!blk_test)
return -ENOMEM;
print_all_line_eyetest(mmc);
emmc_ds_core_align(mmc);
print_all_line_eyetest(mmc);
emmc_all_data_line_alignment(mmc);
print_all_line_eyetest(mmc);
emmc_ds_data_alignment(mmc);
print_all_line_eyetest(mmc);
host->is_tunning = 1;
for (i = 0; i < 64; i++) {
gintf3->ds_sht_m += 1;
writel(intf3, host->base + SD_EMMC_INTF3);
err = aml_sd_emmc_cali_v3(mmc,
MMC_READ_MULTIPLE_BLOCK,
blk_test, blksz, 20);
emmc_dbg(EMMC_TIMMING_DBG, "intf3: 0x%x, err: %d\n",
readl(host->base + SD_EMMC_INTF3), err);
if (!err && !flag) {
sta = i;
flag = 1;
} else if (err && flag) {
end = i-1;
break;
}
}
gintf3->ds_sht_m = (sta + end) / 2;
writel(intf3, host->base + SD_EMMC_INTF3);
emmc_dbg(EMMC_TIMMING_DBG, "sta:%u, end:%u,ds_sht:%u, intf3:0x%x",
sta, end, gintf3->ds_sht_m,
readl(host->base + SD_EMMC_INTF3));
host->is_tunning = 0;
kfree(blk_test);
blk_test = NULL;
return 0;
}
#endif
/* test clock, return delay cells for one cycle
*/
static unsigned int emmc_clktest(struct mmc_host *mmc)
{
struct amlsd_host *host = mmc_priv(mmc);
struct amlsd_platform *pdata = host->pdata;
u32 intf3 = readl(host->base + SD_EMMC_INTF3);
struct intf3 *gintf3 = (struct intf3 *)&(intf3);
u32 clktest = 0, delay_cell = 0, clktest_log = 0, count = 0;
u32 vcfg = readl(host->base + SD_EMMC_CFG);
int i = 0;
vcfg &= ~(1 << 23);
writel(vcfg, host->base + SD_EMMC_CFG);
writel(0, host->base + SD_EMMC_DELAY1_V3);
writel(0, host->base + SD_EMMC_DELAY2_V3);
gintf3->clktest_exp = 8;
gintf3->clktest_on_m = 1;
writel(intf3, host->base + SD_EMMC_INTF3);
emmc_dbg(EMMC_TIMMING_DBG, "CFG: 0x%x\n",
readl(host->base + SD_EMMC_CFG));
clktest_log = readl(host->base + SD_EMMC_CLKTEST_LOG);
clktest = readl(host->base + SD_EMMC_CLKTEST_OUT);
while (!(clktest_log & 0x80000000)) {
mdelay(1);
i++;
clktest_log = readl(host->base + SD_EMMC_CLKTEST_LOG);
clktest = readl(host->base + SD_EMMC_CLKTEST_OUT);
if (i > 4) {
pr_warn("[%s] [%d] emmc clktest error\n",
__func__, __LINE__);
break;
}
}
if (clktest_log & 0x80000000) {
clktest = readl(host->base + SD_EMMC_CLKTEST_OUT);
count = clktest / (1 << 8);
if (vcfg & 0x4)
delay_cell = (2500 / count);
else
delay_cell = (5000 / count);
}
pr_info("%s [%d] clktest : %u, delay_cell: %d, count: %u\n",
__func__, __LINE__, clktest, delay_cell, count);
gintf3->clktest_on_m = 0;
writel(intf3, host->base + SD_EMMC_INTF3);
vcfg |= (1 << 23);
writel(vcfg, host->base + SD_EMMC_CFG);
emmc_dbg(EMMC_TIMMING_DBG, "CFG: 0x%x\n",
readl(host->base + SD_EMMC_CFG));
pdata->count = count;
pdata->delay_cell = delay_cell;
return count;
}
int aml_post_hs400_timming(struct mmc_host *mmc)
{
int ret = 0;
emmc_clktest(mmc);
ret = emmc_ds_manual_sht(mmc);
return 0;
}

View File

@@ -27,6 +27,7 @@
#include "sd_ops.h"
#define DEFAULT_CMD6_TIMEOUT_MS 500
#define AMLOGIC_HS400_TIMING 1
static const unsigned int tran_exp[] = {
10000, 100000, 1000000, 10000000,
@@ -1500,7 +1501,23 @@ static int mmc_hs200_tuning(struct mmc_card *card)
return mmc_execute_tuning(card);
}
#ifdef AMLOGIC_HS400_TIMING
static int mmc_hs400_timming(struct mmc_card *card)
{
struct mmc_host *host = card->host;
int err = 0;
if ((card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS400)
&& (host->ops->post_hs400_timming)) {
err = host->ops->post_hs400_timming(host);
if (err)
pr_warn("%s: refix HS400 timming failed\n",
mmc_hostname(host));
}
return err;
}
#endif
/*
* Handle the detection and initialisation of a card.
*
@@ -1742,6 +1759,11 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
err = mmc_select_hs400(card);
if (err)
goto free_card;
#ifdef AMLOGIC_HS400_TIMING
err = mmc_hs400_timming(card);
if (err)
goto err;
#endif
} else {
/* Select the desired bus width optionally */
err = mmc_select_bus_width(card);

View File

@@ -24,9 +24,21 @@ extern int meson_mmc_clk_init_v3(struct amlsd_host *host);
extern void meson_mmc_set_ios_v3(struct mmc_host *mmc, struct mmc_ios *ios);
extern int aml_sd_emmc_execute_tuning_(struct mmc_host *mmc, u32 opcode,
struct aml_tuning_data *tuning_data, u32 adj_win_start);
/*extern int aml_sd_emmc_execute_tuning_(struct mmc_host *mmc, u32 opcode,
* struct aml_tuning_data *tuning_data, u32 adj_win_start);
*/
extern void aml_sd_emmc_set_buswidth(struct amlsd_host *host, u32 busw_ios);
extern int meson_mmc_request_done(struct mmc_host *mmc,
struct mmc_request *mrq);
extern int meson_mmc_read_resp(struct mmc_host *mmc,
struct mmc_command *cmd);
extern void aml_sd_emmc_send_stop(struct amlsd_host *host);
extern int aml_sd_emmc_post_dma(struct amlsd_host *host,
struct mmc_request *mrq);
#endif

View File

@@ -25,10 +25,9 @@ void meson_mmc_set_ios_v3(struct mmc_host *mmc, struct mmc_ios *ios);
int aml_mmc_execute_tuning_v3(struct mmc_host *mmc, u32 opcode);
/* irqreturn_t meson_mmc_irq_v3(int irq, void *dev_id);*/
irqreturn_t meson_mmc_irq_thread_v3(int irq, void *dev_id);
/*irqreturn_t meson_mmc_irq_thread_v3(int irq, void *dev_id);*/
/* int aml_post_hs400_timming(struct mmc_host *mmc); */
int aml_post_hs400_timming(struct mmc_host *mmc);
/* extern ssize_t emmc_eyetest_show(struct device *dev,
* struct device_attribute *attr, char *buf);

View File

@@ -109,6 +109,11 @@ extern const u8 tuning_blk_pattern_8bit[128];
#define sd_emmc_err(fmt, args...) \
pr_warn("[%s] " fmt, __func__, ##args)
#define emmc_dbg(emmc_timming_dbg, fmt, args...) do {\
if (emmc_timming_dbg) \
pr_warn("[%s]" fmt, __func__, ##args); \
} while (0)
#define SD_PARSE_U32_PROP_HEX(node, prop_name, prop, value) do { \
if (!of_property_read_u32(node, prop_name, &prop)) {\
value = prop;\

View File

@@ -39,7 +39,7 @@
#ifdef AML_CALIBRATION
#define MAX_CALI_RETRY 3
#define MAX_DELAY_CNT 16
#define CALI_BLK_CNT 10
#define CALI_BLK_CNT 80
#endif
#define SD_EMMC_CLOCK 0x0
@@ -58,7 +58,7 @@
#define SD_EMMC_CLOCK_V3 0x0
#define SD_EMMC_DELAY1_V3 0x4
#define SD_EMMC_DELAY2_V3 0x8
#define SD_EMMC_ADJUST_V3 0X10
#define SD_EMMC_ADJUST_V3 0xc
#define SD_EMMC_ADJ_IDX_LOG 0x20
#define SD_EMMC_CLKTEST_LOG 0x24
#define SD_EMMC_CLKTEST_OUT 0x28
@@ -224,6 +224,13 @@ struct amlsd_platform {
unsigned char caling;
unsigned char calout[20][20];
#endif
u64 align[10];
int base_line;
unsigned int count;
unsigned int delay_cell;
unsigned int ds_core;
/* int order; */
unsigned int rx_err;
/* 0:unknown, 1:mmc card(include eMMC), 2:sd card(include tSD),
* 3:sdio device(ie:sdio-wifi), 4:SD combo (IO+mem) card,
* 5:NON sdio device(means sd/mmc card), other:reserved
@@ -327,6 +334,7 @@ struct amlsd_host {
dma_addr_t dma_gping; /* 0x400 */
dma_addr_t dma_gpong; /* 0x800 */
char is_tunning;
char is_timming;
char tuning_mode;
unsigned int irq;
unsigned int irq_in;

View File

@@ -155,6 +155,7 @@ struct mmc_host_ops {
int card_drv, int *drv_type);
void (*hw_reset)(struct mmc_host *host);
void (*card_event)(struct mmc_host *host);
int (*post_hs400_timming)(struct mmc_host *host);
/*
* Optional callback to support controllers with HW issues for multiple