| |
| /* |
| * 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)); |
| } |