blob: f3b602bf4daf256543f1502d1656dcf550de0c60 [file] [log] [blame] [edit]
/*
* drivers/mmc/aml_sd_emmc.c
*
* Copyright (C) 2015 Amlogic, Inc. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <common.h>
#include <malloc.h>
//#include <asm/dma-mapping.h>
#include <asm/io.h>
#include <mmc.h>
#include <asm/arch/sd_emmc.h>
#include <asm/arch/cpu_sdio.h>
#ifdef CONFIG_STORE_COMPATIBLE
#include <storage.h>
#endif
#include <asm/cpu_id.h>
//#define SD_DEBUG_ENABLE
#ifdef SD_DEBUG_ENABLE
#define sd_debug(a...) printf(a);
#else
#define sd_debug(a...)
#endif
//#define EMMC_DEBUG_ENABLE
#ifdef EMMC_DEBUG_ENABLE
#define emmc_debug(a...) printf(a);
#else
#define emmc_debug(a...)
#endif
extern bool aml_is_emmc_tsd (struct mmc *mmc);
/*
* **********************************************************************************************
* board relative
* **********************************************************************************************
*/
unsigned long sd_emmc_base_addr[3] = {SD_EMMC_BASE_A,
SD_EMMC_BASE_B,
SD_EMMC_BASE_C};
static struct aml_card_sd_info aml_sd_emmc_ports[]={
{ .sd_emmc_port=SDIO_PORT_A,.name="SDIO Port A"},
{ .sd_emmc_port=SDIO_PORT_B,.name="SDIO Port B"},
{ .sd_emmc_port=SDIO_PORT_C,.name="SDIO Port C"},
};
struct aml_card_sd_info * cpu_sd_emmc_get(unsigned port)
{
if (port<SDIO_PORT_C+1)
return &aml_sd_emmc_ports[port];
return NULL;
}
void aml_sd_cfg_swth(struct mmc *mmc)
{
unsigned sd_emmc_clkc = 0,clk,clk_src,clk_div = 0;
unsigned vconf;
unsigned bus_width=(mmc->bus_width == 1)?0:mmc->bus_width/4;
struct aml_card_sd_info *aml_priv = mmc->priv;
struct sd_emmc_global_regs *sd_emmc_reg = aml_priv->sd_emmc_reg;
struct sd_emmc_config* sd_emmc_cfg = (struct sd_emmc_config*)&vconf;
cpu_id_t cpu_id = get_cpu_id();
emmc_debug("mmc->clock=%d; clk_div=%d\n",mmc->clock ,clk_div);
/* reset gdelay , gadjust register */
sd_emmc_reg->gdelay = 0;
sd_emmc_reg->gadjust = 0;
if (mmc->clock > 12000000) {
clk = SD_EMMC_CLKSRC_DIV2;
clk_src = 1;
}else{
clk = SD_EMMC_CLKSRC_24M;
clk_src = 0;
}
if (mmc->clock<mmc->cfg->f_min)
mmc->clock=mmc->cfg->f_min;
if (mmc->clock>mmc->cfg->f_max)
mmc->clock=mmc->cfg->f_max;
clk_div= clk / mmc->clock;
#if CONFIG_EMMC_DDR52_EN
if (mmc->ddr_mode) {
clk_div /= 2;
sd_debug("DDR: \n");
}
#endif
sd_emmc_clkc =((0 << Cfg_irq_sdio_sleep_ds) |
(0 << Cfg_irq_sdio_sleep) |
(1 << Cfg_always_on) |
(0 << Cfg_rx_delay) |
(0 << Cfg_tx_delay) |
(0 << Cfg_sram_pd) |
(0 << Cfg_rx_phase) |
(0 << Cfg_tx_phase) |
(2 << Cfg_co_phase) |
(clk_src << Cfg_src) |
(clk_div << Cfg_div));
if (((cpu_id.family_id >= MESON_CPU_MAJOR_ID_TXLX)
&& (cpu_id.family_id != MESON_CPU_MAJOR_ID_TXHD)
&& (cpu_id.family_id != MESON_CPU_MAJOR_ID_G12A))
|| ((cpu_id.family_id == MESON_CPU_MAJOR_ID_G12A)
&& (cpu_id.chip_rev == 0xB))) {
if (aml_is_emmc_tsd(mmc)
|| (cpu_id.family_id == MESON_CPU_MAJOR_ID_AXG)
|| (cpu_id.family_id == MESON_CPU_MAJOR_ID_GXLX)) {
sd_emmc_clkc &= ~(3 << Cfg_co_phase);
sd_emmc_clkc |= (3 << Cfg_co_phase);
}
}
printf("co-phase 0x%x, tx-dly %d\n",
(sd_emmc_clkc >> Cfg_co_phase) & 3,
(sd_emmc_clkc >> Cfg_tx_delay) & 0x3f);
sd_emmc_reg->gclock = sd_emmc_clkc;
vconf = sd_emmc_reg->gcfg;
sd_emmc_cfg->bus_width = bus_width; //1bit mode
sd_emmc_cfg->bl_len = 9; //512byte block length
sd_emmc_cfg->resp_timeout = 7; //64 CLK cycle, here 2^8 = 256 clk cycles
sd_emmc_cfg->rc_cc = 4; //1024 CLK cycle, Max. 100mS.
#if CONFIG_EMMC_DDR52_EN
sd_emmc_cfg->ddr = mmc->ddr_mode;
#endif
sd_emmc_reg->gcfg = vconf;
emmc_debug("bus_width=%d; tclk_div=%d; tclk=%d;sd_clk=%d\n",
bus_width,clk_div,clk,mmc->clock);
emmc_debug("port=%d act_clk=%d\n",aml_priv->sd_emmc_port,clk/clk_div);
return;
}
static int sd_inand_check_insert(struct mmc *mmc)
{
int level;
struct aml_card_sd_info *sd_inand_info = mmc->priv;
level = sd_inand_info->sd_emmc_detect(sd_inand_info->sd_emmc_port);
if (level) {
if (sd_inand_info->init_retry) {
sd_inand_info->sd_emmc_pwr_off(sd_inand_info->sd_emmc_port);
sd_inand_info->init_retry = 0;
}
if (sd_inand_info->inited_flag) {
sd_inand_info->sd_emmc_pwr_off(sd_inand_info->sd_emmc_port);
sd_inand_info->removed_flag = 1;
sd_inand_info->inited_flag = 0;
}
return 0; //No card is inserted
} else {
return 1; //A card is inserted
}
}
//Clear response data buffer
static void sd_inand_clear_response(unsigned * res_buf)
{
int i;
if (res_buf == NULL)
return;
for (i = 0; i < MAX_RESPONSE_BYTES; i++)
res_buf[i]=0;
}
/*
static int sd_inand_check_response(struct mmc_cmd *cmd)
{
int ret = SD_NO_ERROR;
SD_Response_R1_t *r1 = (SD_Response_R1_t *)cmd->response;
switch (cmd->resp_type) {
case MMC_RSP_R1:
case MMC_RSP_R1b:
if (r1->card_status.OUT_OF_RANGE)
return SD_ERROR_OUT_OF_RANGE;
else if (r1->card_status.ADDRESS_ERROR)
return SD_ERROR_ADDRESS;
else if (r1->card_status.BLOCK_LEN_ERROR)
return SD_ERROR_BLOCK_LEN;
else if (r1->card_status.ERASE_SEQ_ERROR)
return SD_ERROR_ERASE_SEQ;
else if (r1->card_status.ERASE_PARAM)
return SD_ERROR_ERASE_PARAM;
else if (r1->card_status.WP_VIOLATION)
return SD_ERROR_WP_VIOLATION;
else if (r1->card_status.CARD_IS_LOCKED)
return SD_ERROR_CARD_IS_LOCKED;
else if (r1->card_status.LOCK_UNLOCK_FAILED)
return SD_ERROR_LOCK_UNLOCK_FAILED;
else if (r1->card_status.COM_CRC_ERROR)
return SD_ERROR_COM_CRC;
else if (r1->card_status.ILLEGAL_COMMAND)
return SD_ERROR_ILLEGAL_COMMAND;
else if (r1->card_status.CARD_ECC_FAILED)
return SD_ERROR_CARD_ECC_FAILED;
else if (r1->card_status.CC_ERROR)
return SD_ERROR_CC;
else if (r1->card_status.ERROR)
return SD_ERROR_GENERAL;
else if (r1->card_status.CID_CSD_OVERWRITE)
return SD_ERROR_CID_CSD_OVERWRITE;
else if (r1->card_status.AKE_SEQ_ERROR)
return SD_ERROR_AKE_SEQ;
break;
default:
break;
}
return ret;
}*/
extern unsigned sd_debug_board_1bit_flag;
static int sd_inand_staff_init(struct mmc *mmc)
{
struct aml_card_sd_info * sdio=mmc->priv;
//unsigned base;
sd_debug("");
sdio->sd_emmc_pwr_prepare(sdio->sd_emmc_port);
sd_debug("power off");
sdio->sd_emmc_pwr_off(sdio->sd_emmc_port);
//try to init mmc controller clock firstly
mmc->clock = 400000;
aml_sd_cfg_swth(mmc);
//only power ctrl for external tf card
if (sdio->sd_emmc_port == SDIO_PORT_B) {
//base=get_timer(0);
#if defined(CONFIG_VLSI_EMULATOR)
//while (get_timer(base)<1) ;
#else
//while (get_timer(base)<200) ;
#endif
}
sdio->sd_emmc_pwr_on(sdio->sd_emmc_port);
sdio->sd_emmc_init(sdio->sd_emmc_port);
if (sd_debug_board_1bit_flag == 1) {
struct mmc_config *cfg;
cfg = &((struct aml_card_sd_info *)mmc->priv)->cfg;
cfg->host_caps = MMC_MODE_HS;
mmc->cfg = cfg;
}
//only power ctrl for external tf card
if (sdio->sd_emmc_port == SDIO_PORT_B) {
//base=get_timer(0);
#if defined(CONFIG_VLSI_EMULATOR)
//while (get_timer(base)<1) ;
#else
//while (get_timer(base)<200) ;
#endif
}
if (!sdio->inited_flag)
sdio->inited_flag = 1;
return SD_NO_ERROR;
}
/*
* **********************************************************************************************
* u-boot interface function
* **********************************************************************************************
*/
int aml_sd_send_cmd_ffu(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data)
{
int ret = SD_NO_ERROR;
u32 resp_buffer;
u32 vstart = 0;
u32 status_irq = 0;
u32 *write_buffer = NULL;
struct sd_emmc_status *status_irq_reg = (void *)&status_irq;
struct sd_emmc_start *desc_start = (struct sd_emmc_start*)&vstart;
struct aml_card_sd_info *aml_priv = mmc->priv;
struct sd_emmc_global_regs *sd_emmc_reg = aml_priv->sd_emmc_reg;
struct cmd_cfg *des_cmd_cur = NULL;
struct sd_emmc_desc_info *desc_cur = (struct sd_emmc_desc_info*)aml_priv->desc_buf;
u32 blks = 0, desc_cnt = 0;
memset(desc_cur, 0, (NEWSD_MAX_DESC_MUN>>2)*sizeof(struct sd_emmc_desc_info));
des_cmd_cur = (struct cmd_cfg *)&(desc_cur->cmd_info);
des_cmd_cur->cmd_index = 0x80 | cmd->cmdidx; //bit:31 owner = 1 bit:24-29 cmdidx
desc_cur->cmd_arg = cmd->cmdarg;
sd_inand_clear_response(cmd->response);
//check response type
if (cmd->resp_type & MMC_RSP_PRESENT) {
resp_buffer = (unsigned long)cmd->response;
des_cmd_cur->no_resp = 0;
//save Resp into Resp addr, and check response from register for RSP_136
if (cmd->resp_type & MMC_RSP_136)
des_cmd_cur->resp_128 = 1;
if (cmd->resp_type & MMC_RSP_BUSY)
des_cmd_cur->r1b = 1; //check data0 busy after R1 reponse
if (!(cmd->resp_type & MMC_RSP_CRC))
des_cmd_cur->resp_nocrc = 1;
des_cmd_cur->resp_num = 0;
desc_cur->resp_addr = resp_buffer;
}else
des_cmd_cur->no_resp = 1;
if (data) {
des_cmd_cur->data_io = 1; // cmd has data read or write
if (data->flags == MMC_DATA_WRITE) {
write_buffer = (u32 *)malloc(data->blocks * data->blocksize);
memset(write_buffer, 0, data->blocks * data->blocksize);
memcpy(write_buffer, (u32 *)data->src, data->blocks*data->blocksize);
flush_dcache_range((unsigned)(long)write_buffer,(unsigned long)(write_buffer+data->blocks*data->blocksize));
if (data->blocks > 1) {
blks = data->blocks;
desc_cnt = 0;
while (blks) {
des_cmd_cur = (struct cmd_cfg *)&(desc_cur->cmd_info);
des_cmd_cur->block_mode = 1;
if (blks > 511) {
des_cmd_cur->length = 511;
blks -= 511;
} else {
des_cmd_cur->length = blks;
blks = 0;
}
if (desc_cnt != 0) {
des_cmd_cur->no_resp = 1;
des_cmd_cur->no_cmd = 1;
}
des_cmd_cur->data_num = 0;
des_cmd_cur->data_io = 1; // cmd has data read or write
des_cmd_cur->owner = 1;
des_cmd_cur->data_wr = 1;
desc_cur->data_addr = ((unsigned long)(write_buffer) + (desc_cnt * 511 * 512));
desc_cur->data_addr &= ~(1<<0); //DDR
if (blks) {
desc_cur++;
desc_cnt++;
memset(desc_cur, 0, sizeof(struct sd_emmc_desc_info));
}
}
} else {
printf("data blks < 1\n");
return 1;
}
} else {
printf("ffu (flags == read) error\n");
return 1;
}
}
/*Prepare desc for config register*/
des_cmd_cur->end_of_chain = 1; //the end flag of descriptor chain
sd_emmc_reg->gstatus = NEWSD_IRQ_ALL;
invalidate_dcache_range((unsigned long)aml_priv->desc_buf,
(unsigned long)(aml_priv->desc_buf+NEWSD_MAX_DESC_MUN*(sizeof(struct sd_emmc_desc_info))));
//start transfer cmd
desc_start->init = 0;
desc_start->busy = 1;
desc_start->addr = (unsigned long)aml_priv->desc_buf >> 2;
sd_emmc_reg->gstart = vstart;
while (1) {
status_irq = sd_emmc_reg->gstatus;
if (status_irq_reg->end_of_chain)
break;
}
if (status_irq_reg->rxd_err) {
ret |= SD_EMMC_RXD_ERROR;
if (!mmc->refix)
printf("emmc/sd read error, cmd%d, status=0x%x\n",
cmd->cmdidx, status_irq);
}
if (status_irq_reg->txd_err) {
ret |= SD_EMMC_TXD_ERROR;
if (!mmc->refix)
printf("emmc/sd write error, cmd%d, status=0x%x\n",
cmd->cmdidx, status_irq);
}
if (status_irq_reg->desc_err) {
ret |= SD_EMMC_DESC_ERROR;
if (!mmc->refix)
printf("emmc/sd descripter error, cmd%d, status=0x%x\n",
cmd->cmdidx, status_irq);
}
if (status_irq_reg->resp_err) {
ret |= SD_EMMC_RESP_CRC_ERROR;
if (!mmc->refix)
printf("emmc/sd response crc error, cmd%d, status=0x%x\n",
cmd->cmdidx, status_irq);
}
if (status_irq_reg->resp_timeout) {
ret |= SD_EMMC_RESP_TIMEOUT_ERROR;
if (!mmc->refix)
printf("emmc/sd response timeout, cmd%d, status=0x%x\n",
cmd->cmdidx, status_irq);
}
if (status_irq_reg->desc_timeout) {
ret |= SD_EMMC_DESC_TIMEOUT_ERROR;
if (!mmc->refix)
printf("emmc/sd descripter timeout, cmd%d, status=0x%x\n",
cmd->cmdidx, status_irq);
}
if (cmd->resp_type & MMC_RSP_136) {
cmd->response[0] = sd_emmc_reg->gcmd_rsp3;
cmd->response[1] = sd_emmc_reg->gcmd_rsp2;
cmd->response[2] = sd_emmc_reg->gcmd_rsp1;
cmd->response[3] = sd_emmc_reg->gcmd_rsp0;
} else {
cmd->response[0] = sd_emmc_reg->gcmd_rsp0;
}
sd_debug("cmd->cmdidx = %d, cmd->cmdarg=0x%x, ret=0x%x\n",cmd->cmdidx,cmd->cmdarg,ret);
sd_debug("cmd->response[0]=0x%x;\n",cmd->response[0]);
sd_debug("cmd->response[1]=0x%x;\n",cmd->response[1]);
sd_debug("cmd->response[2]=0x%x;\n",cmd->response[2]);
sd_debug("cmd->response[3]=0x%x;\n",cmd->response[3]);
if (des_cmd_cur->data_wr == 1) {
free(write_buffer);
write_buffer = NULL;
}
if (ret) {
if (status_irq_reg->resp_timeout)
return TIMEOUT;
else
return ret;
}
return SD_NO_ERROR;
}
/*
* Sends a command out on the bus. Takes the mmc pointer,
* a command pointer, and an optional data pointer.
*/
int aml_sd_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data)
{
int ret = SD_NO_ERROR;
//u32 vconf;
u32 buffer = 0;
u32 resp_buffer;
u32 vstart = 0;
u32 status_irq = 0;
//u32 inalign = 0;
u32 *write_buffer = NULL;
struct sd_emmc_status *status_irq_reg = (void *)&status_irq;
struct sd_emmc_start *desc_start = (struct sd_emmc_start*)&vstart;
//struct sd_emmc_config* sd_emmc_cfg = (struct sd_emmc_config*)&vconf;
struct aml_card_sd_info *aml_priv = mmc->priv;
struct sd_emmc_global_regs *sd_emmc_reg = aml_priv->sd_emmc_reg;
struct cmd_cfg *des_cmd_cur = NULL;
struct sd_emmc_desc_info *desc_cur = (struct sd_emmc_desc_info*)aml_priv->desc_buf;
//vconf = sd_emmc_reg->gcfg;
memset(desc_cur, 0, (NEWSD_MAX_DESC_MUN>>2)*sizeof(struct sd_emmc_desc_info));
des_cmd_cur = (struct cmd_cfg *)&(desc_cur->cmd_info);
des_cmd_cur->cmd_index = 0x80 | cmd->cmdidx; //bit:31 owner = 1 bit:24-29 cmdidx
desc_cur->cmd_arg = cmd->cmdarg;
sd_inand_clear_response(cmd->response);
//check response type
if (cmd->resp_type & MMC_RSP_PRESENT) {
resp_buffer = (unsigned long)cmd->response;//dma_map_single((void*)cmd->response,sizeof(uint)*4,DMA_FROM_DEVICE);
des_cmd_cur->no_resp = 0;
//save Resp into Resp addr, and check response from register for RSP_136
if (cmd->resp_type & MMC_RSP_136)
des_cmd_cur->resp_128 = 1;
if (cmd->resp_type & MMC_RSP_BUSY)
des_cmd_cur->r1b = 1; //check data0 busy after R1 reponse
if (!(cmd->resp_type & MMC_RSP_CRC))
des_cmd_cur->resp_nocrc = 1;
des_cmd_cur->resp_num = 0;
desc_cur->resp_addr = resp_buffer;
}else
des_cmd_cur->no_resp = 1;
if (data) {
des_cmd_cur->data_io = 1; // cmd has data read or write
if (data->flags == MMC_DATA_READ) {
des_cmd_cur->data_wr = 0; //read data from sd/emmc
buffer = (unsigned long)data->dest;//dma_map_single((void*)data->dest,data->blocks*data->blocksize,DMA_FROM_DEVICE);
invalidate_dcache_range((unsigned long)data->dest, (unsigned long)(data->dest+data->blocks*data->blocksize));
}else{
des_cmd_cur->data_wr = 1;
//buffer = (unsigned long)data->src;//dma_map_single((void*)data->src,data->blocks*data->blocksize,DMA_TO_DEVICE);//(char *)data->src;
write_buffer = (u32 *)malloc(128*1024);
memset(write_buffer, 0 ,128*1024);
memcpy(write_buffer, (u32 *)data->src, data->blocks*data->blocksize);
flush_dcache_range((unsigned)(long)write_buffer,(unsigned long)(write_buffer+data->blocks*data->blocksize));
}
if (data->blocks > 1) {
des_cmd_cur->block_mode = 1;
des_cmd_cur->length = data->blocks;
}else{
des_cmd_cur->block_mode = 0;
des_cmd_cur->length = data->blocksize;
}
des_cmd_cur->data_num = 0;
if (des_cmd_cur->data_wr == 1)
desc_cur->data_addr = (unsigned long)write_buffer;
else
desc_cur->data_addr = buffer;
desc_cur->data_addr &= ~(1<<0); //DDR
}
if (data) {
if ((data->blocks*data->blocksize <0x200) && (data->flags == MMC_DATA_READ)) {
desc_cur->data_addr = (unsigned long)sd_emmc_reg->gping;
desc_cur->data_addr |= 1<<0;
}
//des_cmd_cur->timeout = 7;
}
/*Prepare desc for config register*/
des_cmd_cur->owner = 1;
des_cmd_cur->end_of_chain = 0;
//sd_emmc_reg->gcfg = vconf;
des_cmd_cur->end_of_chain = 1; //the end flag of descriptor chain
sd_emmc_reg->gstatus = NEWSD_IRQ_ALL;
invalidate_dcache_range((unsigned long)aml_priv->desc_buf,
(unsigned long)(aml_priv->desc_buf+NEWSD_MAX_DESC_MUN*(sizeof(struct sd_emmc_desc_info))));
//start transfer cmd
desc_start->init = 0;
desc_start->busy = 1;
desc_start->addr = (unsigned long)aml_priv->desc_buf >> 2;
#if 0
sd_emmc_reg->gstart = vstart;
#else
sd_emmc_reg->gcmd_cfg = desc_cur->cmd_info;
sd_emmc_reg->gcmd_dat = desc_cur->data_addr;
sd_emmc_reg->gcmd_arg = desc_cur->cmd_arg;
#endif
//waiting end of chain
//mmc->refix = 0;
while (1) {
status_irq = sd_emmc_reg->gstatus;
if (status_irq_reg->end_of_chain)
break;
}
if (status_irq_reg->rxd_err) {
ret |= SD_EMMC_RXD_ERROR;
if (!mmc->refix)
printf("emmc/sd read error, cmd%d, status=0x%x\n",
cmd->cmdidx, status_irq);
}
if (status_irq_reg->txd_err) {
ret |= SD_EMMC_TXD_ERROR;
if (!mmc->refix)
printf("emmc/sd write error, cmd%d, status=0x%x\n",
cmd->cmdidx, status_irq);
}
if (status_irq_reg->desc_err) {
ret |= SD_EMMC_DESC_ERROR;
if (!mmc->refix)
printf("emmc/sd descripter error, cmd%d, status=0x%x\n",
cmd->cmdidx, status_irq);
}
if (status_irq_reg->resp_err) {
ret |= SD_EMMC_RESP_CRC_ERROR;
if (!mmc->refix)
printf("emmc/sd response crc error, cmd%d, status=0x%x\n",
cmd->cmdidx, status_irq);
}
if (status_irq_reg->resp_timeout) {
ret |= SD_EMMC_RESP_TIMEOUT_ERROR;
if (!mmc->refix)
printf("emmc/sd response timeout, cmd%d, status=0x%x\n",
cmd->cmdidx, status_irq);
}
if (status_irq_reg->desc_timeout) {
ret |= SD_EMMC_DESC_TIMEOUT_ERROR;
if (!mmc->refix)
printf("emmc/sd descripter timeout, cmd%d, status=0x%x\n",
cmd->cmdidx, status_irq);
}
if (data) {
if ((data->blocks*data->blocksize <0x200) && (data->flags == MMC_DATA_READ)) {
memcpy(data->dest, (const void *)sd_emmc_reg->gping,data->blocks*data->blocksize);
}
}
/*we get response [0]:bit0~31
* response [1]:bit32~63
* response [2]:bit64~95
* response [3]:bit96~127
* actually mmc driver definition is:
* response [0]:bit96~127
* response [1]:bit64~95
* response [2]:bit32~63
* response [3]:bit0~31
*/
if (cmd->resp_type & MMC_RSP_136) {
cmd->response[0] = sd_emmc_reg->gcmd_rsp3;
cmd->response[1] = sd_emmc_reg->gcmd_rsp2;
cmd->response[2] = sd_emmc_reg->gcmd_rsp1;
cmd->response[3] = sd_emmc_reg->gcmd_rsp0;
} else {
cmd->response[0] = sd_emmc_reg->gcmd_rsp0;
}
sd_debug("cmd->cmdidx = %d, cmd->cmdarg=0x%x, ret=0x%x\n",cmd->cmdidx,cmd->cmdarg,ret);
sd_debug("cmd->response[0]=0x%x;\n",cmd->response[0]);
sd_debug("cmd->response[1]=0x%x;\n",cmd->response[1]);
sd_debug("cmd->response[2]=0x%x;\n",cmd->response[2]);
sd_debug("cmd->response[3]=0x%x;\n",cmd->response[3]);
if (des_cmd_cur->data_wr == 1) {
free(write_buffer);
write_buffer = NULL;
}
if (ret) {
if (status_irq_reg->resp_timeout)
return TIMEOUT;
else
return ret;
}
return SD_NO_ERROR;
}
int aml_sd_init(struct mmc *mmc)
{
struct aml_card_sd_info *sdio=mmc->priv;
if (sdio->inited_flag) {
sdio->sd_emmc_init(sdio->sd_emmc_port);
mmc->cfg->ops->set_ios(mmc);
return 0;
}
if (sd_inand_check_insert(mmc)) {
sd_inand_staff_init(mmc);
return 0;
} else
return 1;
}
#define MAX_CALI_RETRY (3)
#define MAX_DELAY_CNT (16)
#define CALI_BLK_CNT (10)
#define REFIX_BLK_CNT (100)
u8 line_x, cal_time;
u8 dly_tmp;
u8 max_index;
int aml_send_calibration_blocks(struct mmc *mmc, char *buffer, u32 start_blk, u32 cnt)
{
int err = 0, ret = 0;
struct mmc_cmd cmd = {0};
struct mmc_cmd stop = {0};
struct mmc_data data = {{0}, 0};
stop.cmdidx = MMC_CMD_STOP_TRANSMISSION;
stop.cmdarg = 0;
stop.resp_type = MMC_RSP_R1b;
if (cnt > 1)
cmd.cmdidx = MMC_CMD_READ_MULTIPLE_BLOCK;
else
cmd.cmdidx = MMC_CMD_READ_SINGLE_BLOCK;
cmd.cmdarg = start_blk; //start address = 0
cmd.resp_type = MMC_RSP_R1;
data.dest = buffer;
data.blocks = cnt;
data.blocksize = mmc->read_bl_len;
data.flags = MMC_DATA_READ;
err = aml_sd_send_cmd(mmc, &cmd, &data);
if (err)
emmc_debug("%s [%d] send calibration read blocks error %d cnt = %d\n", __func__, __LINE__, err, cnt);
if (cnt > 1 || err) {
ret = aml_sd_send_cmd(mmc, &stop, NULL);
if (ret)
emmc_debug("%s [%d] send calibration stop blocks error %d\n", __func__, __LINE__, ret);
}
if (ret || err)
return -1;
return 0;
}
int sd_emmc_test_adj(struct mmc *mmc)
{
int err = 0, ret = 0;
struct mmc_cmd cmd = {0};
struct mmc_cmd stop = {0};
struct mmc_data data = {{0}, 0};
char *blk_test = NULL;
struct aml_card_sd_info *aml_priv = mmc->priv;
struct sd_emmc_global_regs *sd_emmc_reg = aml_priv->sd_emmc_reg;
u32 adjust = sd_emmc_reg->gadjust;
struct sd_emmc_adjust *gadjust = (struct sd_emmc_adjust *)&adjust;
u32 vclk, clk_div, retry;
struct sd_emmc_clock *clkc = (struct sd_emmc_clock *)&(vclk);
vclk = sd_emmc_reg->gclock;
clk_div = clkc->div;
retry = clk_div;
blk_test = malloc(0x400 * mmc->read_bl_len);
if (!blk_test)
return -1;
stop.cmdidx = MMC_CMD_STOP_TRANSMISSION;
stop.cmdarg = 0;
stop.resp_type = MMC_RSP_R1b;
cmd.cmdarg = 0x14000;
cmd.cmdidx = MMC_CMD_READ_MULTIPLE_BLOCK;
cmd.resp_type = MMC_RSP_R1;
data.dest = blk_test;
data.blocks = 0x400;
data.blocksize = mmc->read_bl_len;
data.flags = MMC_DATA_READ;
__Retry:
err = aml_sd_send_cmd(mmc, &cmd, &data);
if (err)
emmc_debug("%s [%d] send test read blocks error %d\n", __func__, __LINE__, err);
ret = aml_sd_send_cmd(mmc, &stop, NULL);
if (ret)
emmc_debug("%s [%d] send test stop blocks error %d\n", __func__, __LINE__, ret);
if (ret || err) {
if (retry == 0) {
free(blk_test);
return 1;
}
retry--;
gadjust->adj_delay--;
sd_emmc_reg->gadjust = adjust;
printf("adj retry sampling point:(%d)->(%d)",
gadjust->adj_delay+1, gadjust->adj_delay);
emmc_debug("%s [%d]: delay = 0x%x gadjust =0x%x\n",
__func__, __LINE__, sd_emmc_reg->gdelay, sd_emmc_reg->gadjust);
goto __Retry;
}
free(blk_test);
return 0;
}
int aml_sd_retry_refix(struct mmc *mmc)
{
struct aml_card_sd_info *aml_priv = mmc->priv;
struct sd_emmc_global_regs *sd_emmc_reg = aml_priv->sd_emmc_reg;
u32 start_blk = 0;
u32 vclk, clk_div;
struct sd_emmc_clock *clkc = (struct sd_emmc_clock *)&(vclk);
u32 adjust = sd_emmc_reg->gadjust;
struct sd_emmc_adjust *gadjust = (struct sd_emmc_adjust *)&adjust;
int err = 0, ret = 0, adj_delay = 0;
char *blk_test = NULL;
emmc_debug("tuning..................\n");
//const u8 *blk_pattern = tuning_data->blk_pattern;
//u8 *blk_test;
//unsigned int blksz = tuning_data->blksz;
//int ret = 0;
int n, nmatch, ntries = 20;
int wrap_win_start = -1, wrap_win_size = 0;
int best_win_start = -1, best_win_size = -1;
int curr_win_start = -1, curr_win_size = 0;
u8 rx_tuning_result[20] = { 0 };
blk_test = malloc(REFIX_BLK_CNT * mmc->read_bl_len);
if (!blk_test)
return -1;
vclk = sd_emmc_reg->gclock;
clk_div = clkc->div;
for (adj_delay = 0; adj_delay < clk_div; adj_delay++) {
// Perform tuning ntries times per clk_div increment
gadjust->adj_delay = adj_delay;
gadjust->adj_enable = 1;
gadjust->cali_enable = 0;
gadjust->cali_rise = 0;
sd_emmc_reg->gadjust = adjust;
start_blk = 0x14000;
emmc_debug("%s [%d]: gadjust =0x%x\n",
__func__, __LINE__, sd_emmc_reg->gadjust);
for (n = 0, nmatch = 0; n < ntries; n++) {
memset(blk_test, 0, REFIX_BLK_CNT);
ret = aml_send_calibration_blocks(mmc, blk_test, 0, 1);
err = aml_send_calibration_blocks(mmc, blk_test, start_blk, REFIX_BLK_CNT);
if (!err && !ret)
nmatch++;
else {
emmc_debug("refix error: adj_delay=%d nmatch=%d err=%d ret=%d\n",adj_delay, nmatch, err, ret);
break;
}
}
if (adj_delay < sizeof(rx_tuning_result) / sizeof (rx_tuning_result[0]))
rx_tuning_result[adj_delay] = nmatch;
if (nmatch == ntries) {
if (adj_delay == 0)
wrap_win_start = adj_delay;
if (wrap_win_start >= 0)
wrap_win_size++;
if (curr_win_start < 0)
curr_win_start = adj_delay;
curr_win_size++;
} else {
if (curr_win_start >= 0) {
if (best_win_start < 0) {
best_win_start = curr_win_start;
best_win_size = curr_win_size;
}
else {
if (best_win_size < curr_win_size) {
best_win_start = curr_win_start;
best_win_size = curr_win_size;
}
}
wrap_win_start = -1;
curr_win_start = -1;
curr_win_size = 0;
}
}
emmc_debug("curr_win_start=%d, curr_win_size=%d\n",curr_win_start, curr_win_size);
emmc_debug("wrap_win_start=%d, wrap_win_size=%d\n",wrap_win_start, wrap_win_size);
emmc_debug("best_win_start=%d, best_win_size=%d\n\n",best_win_start, best_win_size);
}
#if 0
for (n = 0; n <= clkc->div; n++) {
if (n < sizeof(rx_tuning_result) / sizeof (rx_tuning_result[0]))
printf("RX[%d]=%d\n", n, rx_tuning_result[n]);
}
#endif
if (curr_win_start >= 0) {
if (best_win_start < 0) {
best_win_start = curr_win_start;
best_win_size = curr_win_size;
} else if (wrap_win_size > 0) {
// Wrap around case
if (curr_win_size + wrap_win_size > best_win_size) {
best_win_start = curr_win_start;
best_win_size = curr_win_size + wrap_win_size;
}
} else if (best_win_size < curr_win_size) {
best_win_start = curr_win_start;
best_win_size = curr_win_size;
}
curr_win_start = -1;
curr_win_size = 0;
}
if (best_win_start < 0) {
printf("Tuning failed to find a valid window, using default rx phase\n");
ret = -1;
//writel(vclk2_bak, host->base + SDHC_CLK2);
} else {
adj_delay = best_win_start + (best_win_size / 2);
if (adj_delay > clkc->div)
adj_delay -= (clkc->div + 1);
gadjust->adj_enable = 1;
gadjust->cali_enable = 0;
gadjust->cali_rise = 0;
gadjust->adj_delay = adj_delay;
sd_emmc_reg->gadjust = adjust;
}
emmc_debug("%s [%d]: delay = 0x%x gadjust =0x%x\n",
__func__, __LINE__, sd_emmc_reg->gdelay, sd_emmc_reg->gadjust);
emmc_debug("%s [%d]: adj_delay = %d\n", __func__, __LINE__, adj_delay);
free(blk_test);
/* test adj sampling point*/
ret = sd_emmc_test_adj(mmc);
return ret;
}
int aml_sd_calibration(struct mmc *mmc)
{
struct aml_card_sd_info *aml_priv = mmc->priv;
struct sd_emmc_global_regs *sd_emmc_reg = aml_priv->sd_emmc_reg;
cpu_id_t cpu_id = get_cpu_id();
if (cpu_id.family_id == MESON_CPU_MAJOR_ID_GXBB)
sd_emmc_reg->gdelay = 0x85854055;
else if (cpu_id.family_id == MESON_CPU_MAJOR_ID_GXTVBB)
sd_emmc_reg->gdelay = 0x10101331;
return 0;
}
static const struct mmc_ops aml_sd_emmc_ops = {
.send_cmd = aml_sd_send_cmd,
.set_ios = aml_sd_cfg_swth,
.init = aml_sd_init,
// .getcd = ,
// .getwp = ,
.calibration = aml_sd_calibration,
.refix = aml_sd_retry_refix,
};
void sd_emmc_register(struct aml_card_sd_info * aml_priv)
{
struct mmc_config *cfg;
aml_priv->removed_flag = 1;
aml_priv->inited_flag = 0;
aml_priv->sd_emmc_reg = (struct sd_emmc_global_regs *)sd_emmc_base_addr[aml_priv->sd_emmc_port];
cfg = &aml_priv->cfg;
cfg->name = aml_priv->name;
cfg->ops = &aml_sd_emmc_ops;
cfg->voltages = MMC_VDD_33_34|MMC_VDD_32_33|MMC_VDD_31_32|MMC_VDD_165_195;
cfg->host_caps = MMC_MODE_8BIT|MMC_MODE_4BIT | MMC_MODE_HS_52MHz | MMC_MODE_HS |
#if CONFIG_EMMC_DDR52_EN
MMC_MODE_HC | MMC_MODE_DDR_52MHz;
#else
MMC_MODE_HC;
#endif
cfg->f_min = 400000;
cfg->f_max = 40000000;
cfg->part_type = PART_TYPE_UNKNOWN;
//cfg->part_type = PART_TYPE_AML;
cfg->b_max = 256;
mmc_create(cfg,aml_priv);
}
bool aml_is_emmc_tsd (struct mmc *mmc) // is eMMC OR TSD
{
struct aml_card_sd_info * sdio=mmc->priv;
return ((sdio->sd_emmc_port == SDIO_PORT_C));
}