/* * Copyright (c) 2020 HiSilicon (Shanghai) Technologies CO., LIMITED. * * 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, see . * */ #include #include #include #include #include #include //#include #include #include #include "mmc_private.h" #include "himci.h" #define MMC_CS_ERROR_MASK 0xFDFFA080 /*************************************************************************/ #ifdef CONFIG_TARGET_HI3516CV500 #include "himci_hi3516cv500.c" #endif #if (defined CONFIG_TARGET_HI3516DV300) || (defined CONFIG_TARGET_HI3516AV300) #include "himci_hi3516dv300.c" #endif #ifdef CONFIG_TARGET_HI3556V200 #include "himci_hi3556v200.c" #endif #ifdef CONFIG_TARGET_HI3559V200 #include "himci_hi3559v200.c" #endif #ifdef CONFIG_TARGET_HI3562V100 #include "himci_hi3559v200.c" #endif #ifdef CONFIG_TARGET_HI3566V100 #include "himci_hi3559v200.c" #endif /*************************************************************************/ //#define TUNING_PROC_DEBUG #if HI_MCI_DEBUG int debug_type = HIMCI_DEBUG_TYPE; char *get_debug_type_string(int type) { if (type & HIMCI_DEBUG_TYPE_REG) return "REG"; else if (type & HIMCI_DEBUG_TYPE_FUN) return "FUN"; else if (type & HIMCI_DEBUG_TYPE_CMD) return "CMD"; else if (type & HIMCI_DEBUG_TYPE_INFO) return "INFO"; else if (type & HIMCI_DEBUG_TYPE_ERR) return "ERR"; else return "UNKNOWN"; } #endif static unsigned int retry_count = MAX_RETRY_COUNT; static struct himci_dma_des hi_dma_des[MAX_DMA_DES] __attribute__ ((aligned(512))); /* reset MMC host controler */ static void hi_mci_sys_reset(struct himci_host *host) { unsigned int tmp_reg; unsigned int time_out; HIMCI_DEBUG_FUN("Function Call"); HIMCI_ASSERT(host); tmp_reg = himci_readl(host->base + MCI_BMOD); tmp_reg |= BMOD_SWR; himci_writel(tmp_reg, host->base + MCI_BMOD); time_out = 1000; do { tmp_reg = himci_readl(host->base + MCI_BMOD); udelay(10); } while ((tmp_reg & BMOD_SWR) && time_out--); tmp_reg = himci_readl(host->base + MCI_BMOD); tmp_reg |= BURST_16 | BURST_INCR; himci_writel(tmp_reg, host->base + MCI_BMOD); tmp_reg = himci_readl(host->base + MCI_CTRL); tmp_reg |= CTRL_RESET | FIFO_RESET | DMA_RESET; himci_writel(tmp_reg, host->base + MCI_CTRL); } static void hi_mci_ctrl_power(struct himci_host *host, int flag) { int reg_value = 0; HIMCI_DEBUG_FUN("Function Call: flag %d", flag); HIMCI_ASSERT(host); if (flag) reg_value = 1 << host->port; else reg_value = 0; himci_writel(reg_value, host->base + MCI_PWREN); } static int tuning_reset_flag = 0; static int hi_mci_wait_cmd(struct himci_host *host) { unsigned int tmp_reg, wait_retry_count = 0; unsigned int retry_count_cmd = 500; HIMCI_DEBUG_FUN("Function Call"); HIMCI_ASSERT(host); do { tmp_reg = himci_readl(host->base + MCI_CMD); if ((tmp_reg & START_CMD) == 0) return 0; udelay(1); wait_retry_count++; } while (wait_retry_count < retry_count_cmd); if (host->is_tuning) tuning_reset_flag = 1; HIMCI_DEBUG_INFO("CMD send timeout"); return -1; } static void hi_mci_control_cclk(struct himci_host *host, unsigned int flag) { unsigned int reg; cmd_arg_s cmd_reg; HIMCI_DEBUG_FUN("Function Call: flag %d", flag); HIMCI_ASSERT(host); reg = himci_readl(host->base + MCI_CLKENA); if (flag == ENABLE) reg |= CCLK_ENABLE << host->port; else reg &= ~(CCLK_ENABLE << host->port); himci_writel(reg, host->base + MCI_CLKENA); cmd_reg.cmd_arg = himci_readl(host->base + MCI_CMD); cmd_reg.cmd_arg &= ~MCI_CMD_MASK; cmd_reg.bits.start_cmd = 1; cmd_reg.bits.card_number = host->port; cmd_reg.bits.update_clk_reg_only = 1; himci_writel(cmd_reg.cmd_arg, host->base + MCI_CMD); if (hi_mci_wait_cmd(host) != 0) HIMCI_DEBUG_ERR("Disable or enable CLK is timeout"); } static void hi_mci_set_cclk(struct himci_host *host, unsigned int cclk) { unsigned int reg_value; cmd_arg_s cmd_reg; HIMCI_DEBUG_FUN("Function Call: cclk %d", cclk); HIMCI_ASSERT(host); HIMCI_ASSERT(cclk); /*set card clk divider value, clk_divider = Fmmcclk/(Fmmc_cclk * 2) */ reg_value = 0; if (cclk < MMC_CLK) { reg_value = MMC_CLK / (cclk * 2); if (MMC_CLK % (cclk * 2)) reg_value++; if (reg_value > 0xFF) reg_value = 0xFF; } himci_writel(reg_value, host->base + MCI_CLKDIV); cmd_reg.cmd_arg = himci_readl(host->base + MCI_CMD); cmd_reg.cmd_arg &= ~MCI_CMD_MASK; cmd_reg.bits.start_cmd = 1; cmd_reg.bits.card_number = host->port; cmd_reg.bits.update_clk_reg_only = 1; himci_writel(cmd_reg.cmd_arg, host->base + MCI_CMD); if (hi_mci_wait_cmd(host) != 0) HIMCI_DEBUG_ERR("Set card CLK divider is failed"); } /********************************************** *1: card off *0: card on ***********************************************/ static unsigned int hi_mci_sys_card_detect(struct himci_host *host) { unsigned int card_status; card_status = readl((uintptr_t)(host->base + MCI_CDETECT)); card_status &= (HIMCI_CARD0 << host->port); return card_status >> host->port; } static void hi_mci_init_card(struct himci_host *host) { unsigned int tmp_reg; HIMCI_DEBUG_FUN("Function Call"); HIMCI_ASSERT(host); hi_mci_sys_reset(host); /* card reset */ himci_writel(~(1<port), host->base + MCI_RESET_N); __udelay(CONFIG_MMC_RESET_LOW_TIMEOUT); /* card power off and power on */ hi_mci_ctrl_power(host, POWER_OFF); __udelay(CONFIG_MMC_POWER_OFF_TIMEOUT * 1000); hi_mci_ctrl_power(host, POWER_ON); __udelay(CONFIG_MMC_POWER_ON_TIMEROUT * 1000); /* card reset cancel */ himci_writel(1<port, host->base + MCI_RESET_N); __udelay(CONFIG_MMC_RESET_HIGH_TIMEROUT); /* set drv/smpl phase shift */ tmp_reg = himci_readl(host->base + MCI_UHS_REG_EXT); tmp_reg &= ~(DRV_PHASE_MASK | SMPL_PHASE_MASK); tmp_reg |= DRV_PHASE_SHIFT | SMPL_PHASE_SHIFT; himci_writel(tmp_reg, host->base + MCI_UHS_REG_EXT); /* clear MMC host intr */ himci_writel(ALL_INT_CLR, host->base + MCI_RINTSTS); /*read write threshold*/ himci_writel(RW_THRESHOLD_SIZE, host->base + MMC_CARDTHRCTL); /* MASK MMC host intr */ tmp_reg = himci_readl(host->base + MCI_INTMASK); tmp_reg &= ~ALL_INT_MASK; himci_writel(tmp_reg, host->base + MCI_INTMASK); /* enable inner DMA mode and close intr of MMC host controler */ tmp_reg = himci_readl(host->base + MCI_CTRL); tmp_reg &= ~INTR_EN; tmp_reg |= USE_INTERNAL_DMA; himci_writel(tmp_reg, host->base + MCI_CTRL); /* enable dma intr */ tmp_reg = TI | RI | NI; himci_writel(tmp_reg, host->base + MCI_IDINTEN); /* set timeout param */ himci_writel(DATA_TIMEOUT | RESPONSE_TIMEOUT, host->base + MCI_TIMEOUT); /* set FIFO param */ himci_writel(BURST_SIZE | RX_WMARK | TX_WMARK, host->base + MCI_FIFOTH); /* set dto fix bypass */ tmp_reg = himci_readl(host->base + MCI_GPIO); tmp_reg |= DTO_FIX_BYPASS; himci_writel(tmp_reg, host->base + MCI_GPIO); } static void hi_mci_idma_start(struct himci_host *host, struct mmc_data *data) { unsigned int tmp_reg; HIMCI_DEBUG_FUN("Function Call"); HIMCI_ASSERT(host); HIMCI_ASSERT(data); himci_writel((unsigned long)(uintptr_t)host->dma_des, host->base + MCI_DBADDR); HIMCI_DEBUG_INFO("host->dma_des is 0x%x", (unsigned int)host->dma_des); tmp_reg = himci_readl(host->base + MCI_BMOD); tmp_reg |= BMOD_DMA_EN; himci_writel(tmp_reg, host->base + MCI_BMOD); } static void hi_mci_idma_stop(struct himci_host *host) { unsigned int tmp_reg; HIMCI_DEBUG_FUN("Function Call"); HIMCI_ASSERT(host); tmp_reg = himci_readl(host->base + MCI_BMOD); tmp_reg &= ~BMOD_DMA_EN; himci_writel(tmp_reg, host->base + MCI_BMOD); himci_writel(0, host->base + MCI_BYTCNT); himci_writel(0, host->base + MCI_BLKSIZ); himci_writel(0, host->base + MCI_DBADDR); } static void hi_mci_idma_reset(struct himci_host *host) { unsigned int regval; regval = himci_readl(host->base + MCI_BMOD); regval |= BMOD_SWR; himci_writel(regval, host->base + MCI_BMOD); regval = himci_readl(host->base + MCI_CTRL); regval |= CTRL_RESET | FIFO_RESET | DMA_RESET; himci_writel(regval, host->base + MCI_CTRL); udelay(1); himci_writel(ALL_INT_CLR, host->base + MCI_RINTSTS); } static int hi_mci_setup_data(struct himci_host *host, struct mmc_data *data) { struct himci_dma_des *des = host->dma_des; unsigned long des_cnt; unsigned long data_size = data->blocks * data->blocksize; unsigned long blk_size = data_size; unsigned long src = (uintptr_t)data->src; HIMCI_DEBUG_FUN("Function Call"); HIMCI_ASSERT(host); HIMCI_ASSERT(data); if (((data_size + 0x1000 - 1) / 0x1000) > MAX_DMA_DES) { HIMCI_ERROR("Data size outside the limit of DMA des, " "data size: 0x%08lx, limit of DMA des: 0x%08x", data_size, 0x1000 * MAX_DMA_DES); return -1; } des_cnt = 0; while (data_size) { des[des_cnt].idmac_des_ctrl = DMA_DES_OWN | DMA_DES_NEXT_DES; des[des_cnt].idmac_des_buf_addr = src; des[des_cnt].idmac_des_next_addr = (uintptr_t)(des + des_cnt + 1); if (data_size >= 0x1000) { des[des_cnt].idmac_des_buf_size = 0x1000; data_size -= 0x1000; src += 0x1000; } else { des[des_cnt].idmac_des_buf_size = data_size; data_size = 0; } HIMCI_DEBUG_INFO("des[%ld] vaddr is 0x%X", des_cnt, (unsigned int)&des[des_cnt]); HIMCI_DEBUG_INFO("des[%ld].idmac_des_ctrl is 0x%X", des_cnt, (unsigned int)des[des_cnt].idmac_des_ctrl); HIMCI_DEBUG_INFO("des[%ld].idmac_des_buf_size is 0x%X", des_cnt, (unsigned int)des[des_cnt].idmac_des_buf_size); HIMCI_DEBUG_INFO("des[%ld].idmac_des_buf_addr 0x%X", des_cnt, (unsigned int)des[des_cnt].idmac_des_buf_addr); HIMCI_DEBUG_INFO("des[%ld].idmac_des_next_addr is 0x%X", des_cnt, (unsigned int)des[des_cnt].idmac_des_next_addr); des_cnt++; } des[0].idmac_des_ctrl |= DMA_DES_FIRST_DES; des[des_cnt - 1].idmac_des_ctrl |= DMA_DES_LAST_DES; des[des_cnt - 1].idmac_des_next_addr = 0; blk_size = ALIGN(blk_size, CONFIG_SYS_CACHELINE_SIZE); des_cnt = ALIGN(des_cnt, CONFIG_SYS_CACHELINE_SIZE); if (data->flags != MMC_DATA_READ){ flush_cache((uintptr_t)data->src, blk_size); } else invalidate_dcache_range((uintptr_t)data->dest,(uintptr_t)data->dest + blk_size); flush_cache((uintptr_t)host->dma_des, sizeof(struct himci_dma_des)*(unsigned long)des_cnt); return 0; } static int hi_mci_exec_cmd(struct himci_host *host, struct mmc_cmd *cmd, struct mmc_data *data) { volatile cmd_arg_s cmd_reg; HIMCI_DEBUG_FUN("Function Call"); HIMCI_ASSERT(host); HIMCI_ASSERT(cmd); himci_writel(cmd->cmdarg, host->base + MCI_CMDARG); cmd_reg.cmd_arg = himci_readl(host->base + MCI_CMD); cmd_reg.cmd_arg &= ~MCI_CMD_MASK; if (data) { cmd_reg.bits.data_transfer_expected = 1; if (data->flags & (MMC_DATA_WRITE | MMC_DATA_READ)) cmd_reg.bits.transfer_mode = 0; if (data->flags & MMC_DATA_WRITE) cmd_reg.bits.read_write = 1; else if (data->flags & MMC_DATA_READ) cmd_reg.bits.read_write = 0; } else { cmd_reg.bits.data_transfer_expected = 0; cmd_reg.bits.transfer_mode = 0; cmd_reg.bits.read_write = 0; } cmd_reg.bits.wait_prvdata_complete = 1; cmd_reg.bits.send_auto_stop = 0; if (!(host->is_tuning)) cmd_reg.bits.send_auto_stop = 1; if (cmd->cmdidx == MMC_CMD_STOP_TRANSMISSION) { cmd_reg.bits.stop_abort_cmd = 1; cmd_reg.bits.wait_prvdata_complete = 0; } else { cmd_reg.bits.stop_abort_cmd = 0; cmd_reg.bits.wait_prvdata_complete = 1; } switch (cmd->resp_type) { case MMC_RSP_NONE: cmd_reg.bits.response_expect = 0; cmd_reg.bits.response_length = 0; cmd_reg.bits.check_response_crc = 0; break; case MMC_RSP_R1: case MMC_RSP_R1b: cmd_reg.bits.response_expect = 1; cmd_reg.bits.response_length = 0; cmd_reg.bits.check_response_crc = 1; break; case MMC_RSP_R2: cmd_reg.bits.response_expect = 1; cmd_reg.bits.response_length = 1; cmd_reg.bits.check_response_crc = 1; break; case MMC_RSP_R3: cmd_reg.bits.response_expect = 1; cmd_reg.bits.response_length = 0; cmd_reg.bits.check_response_crc = 0; break; default: HIMCI_ERROR("unhandled response type %02x", cmd->resp_type); return -1; } HIMCI_DEBUG_INFO("Send cmd of card is CMD %d", cmd->cmdidx); if (cmd->cmdidx == MMC_CMD_GO_IDLE_STATE) cmd_reg.bits.send_initialization = 1; else cmd_reg.bits.send_initialization = 0; cmd_reg.bits.card_number = host->port; cmd_reg.bits.cmd_index = cmd->cmdidx; cmd_reg.bits.send_auto_stop = 0; cmd_reg.bits.start_cmd = 1; cmd_reg.bits.update_clk_reg_only = 0; himci_writel(cmd_reg.cmd_arg, host->base + MCI_CMD); if (hi_mci_wait_cmd(host) != 0) { HIMCI_DEBUG_ERR("Send card cmd is failed"); return -1; } return 0; } static int hi_mci_cmd_done(struct himci_host *host, unsigned int stat) { struct mmc_cmd *cmd = host->cmd; HIMCI_DEBUG_FUN("Function Call: stat 0x%08x", stat); HIMCI_ASSERT(host); HIMCI_ASSERT(cmd); host->cmd = NULL; if (cmd->resp_type & MMC_RSP_PRESENT) { if (cmd->resp_type & MMC_RSP_136) { cmd->response[0] = himci_readl(host->base + MCI_RESP3); cmd->response[1] = himci_readl(host->base + MCI_RESP2); cmd->response[2] = himci_readl(host->base + MCI_RESP1); cmd->response[3] = himci_readl(host->base + MCI_RESP0); HIMCI_DEBUG_INFO("CMD Response of card is " "%08x %08x %08x %08x", cmd->response[0], cmd->response[1], cmd->response[2], cmd->response[3]); } else { cmd->response[0] = himci_readl(host->base + MCI_RESP0); HIMCI_DEBUG_INFO("CMD Response of card is %08x", cmd->response[0]); } if (host->mmc->version && !IS_SD(host->mmc)) { if ((cmd->resp_type == MMC_RSP_R1) || (cmd->resp_type == MMC_RSP_R1b)) { if (cmd->response[0] & MMC_CS_ERROR_MASK) { HIMCI_DEBUG_ERR("Card status" " stat = 0x%x" " is card error!", cmd->response[0]); return -1; } } } } if (stat & RTO_INT_STATUS) { HIMCI_DEBUG_ERR("CMD status stat = 0x%x is timeout error!", stat); return -ETIMEDOUT; } else if (stat & (RCRC_INT_STATUS | RE_INT_STATUS)) { HIMCI_DEBUG_ERR("CMD status stat = 0x%x is response error!", stat); return -1; } return 0; } static void hi_mci_data_done(struct himci_host *host, unsigned int stat) { HIMCI_DEBUG_FUN("Function Call: stat 0x%08x", stat); HIMCI_ASSERT(host); if (stat & (HTO_INT_STATUS | DRTO_INT_STATUS)) { HIMCI_DEBUG_ERR("Data status stat = 0x%x is timeout error!", stat); } else if (stat & (EBE_INT_STATUS | SBE_INT_STATUS | FRUN_INT_STATUS | DCRC_INT_STATUS)) { HIMCI_DEBUG_ERR("Data status stat = 0x%x is data error!", stat); } } static int hi_mci_wait_cmd_complete(struct himci_host *host) { unsigned int wait_retry_count = 0; unsigned int reg_data = 0; int ret = 0; HIMCI_DEBUG_FUN("Function Call"); HIMCI_ASSERT(host); do { reg_data = himci_readl(host->base + MCI_RINTSTS); if (reg_data & CD_INT_STATUS) { himci_writel(CD_INT_STATUS | RTO_INT_STATUS | RCRC_INT_STATUS | RE_INT_STATUS, host->base + MCI_RINTSTS); ret = hi_mci_cmd_done(host, reg_data); return ret; } udelay(100); wait_retry_count++; } while (wait_retry_count < retry_count); HIMCI_DEBUG_ERR("Wait cmd complete error! irq status is 0x%x", reg_data); return -1; } static int hi_mci_wait_data_complete(struct himci_host *host) { unsigned int wait_retry_count = 0; unsigned int reg_data = 0; HIMCI_DEBUG_FUN("Function Call"); HIMCI_ASSERT(host); do { reg_data = himci_readl(host->base + MCI_RINTSTS); if (reg_data & DTO_INT_STATUS) { hi_mci_idma_stop(host); hi_mci_data_done(host, reg_data); return 0; } if (host->is_tuning){ if (reg_data & (HTO_INT_STATUS | DRTO_INT_STATUS | EBE_INT_STATUS | SBE_INT_STATUS | FRUN_INT_STATUS | DCRC_INT_STATUS)){ hi_mci_idma_stop(host); hi_mci_data_done(host, reg_data); return -1; } } udelay(100); wait_retry_count++; } while (wait_retry_count < retry_count); HIMCI_DEBUG_ERR("Wait data complete error! irq status is 0x%x", reg_data); return -1; } static int hi_mci_wait_card_complete(struct himci_host *host) { unsigned int wait_retry_count = 0; unsigned int reg_data = 0; HIMCI_DEBUG_FUN("Function Call"); HIMCI_ASSERT(host); do { reg_data = himci_readl(host->base + MCI_STATUS); if (!(reg_data & DATA_BUSY)) return 0; udelay(100); wait_retry_count++; } while (wait_retry_count < retry_count); HIMCI_DEBUG_ERR("Wait card complete error! status is 0x%x", reg_data); return -1; } static int hi_mci_request(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data) { struct himci_host *host = mmc->priv; unsigned int blk_size; unsigned int tmp_reg, fifo_count = 0; int ret = 0; HIMCI_DEBUG_FUN("Function Call"); HIMCI_ASSERT(mmc); HIMCI_ASSERT(host); HIMCI_ASSERT(cmd); host->cmd = cmd; himci_writel(ALL_INT_CLR, host->base + MCI_RINTSTS); /* prepare data */ if (data) { ret = hi_mci_setup_data(host, data); if (ret) { HIMCI_ERROR("Data setup is error!"); goto request_end; } blk_size = data->blocks * data->blocksize; himci_writel(blk_size, host->base + MCI_BYTCNT); himci_writel(data->blocksize, host->base + MCI_BLKSIZ); tmp_reg = himci_readl(host->base + MCI_CTRL); tmp_reg |= FIFO_RESET; himci_writel(tmp_reg, host->base + MCI_CTRL); do { tmp_reg = himci_readl(host->base + MCI_CTRL); if (fifo_count > retry_count) { HIMCI_ERROR("FIFO reset error!"); break; } fifo_count++; } while (tmp_reg & FIFO_RESET); /* start DMA */ hi_mci_idma_start(host, data); } else { himci_writel(0, host->base + MCI_BYTCNT); himci_writel(0, host->base + MCI_BLKSIZ); } /* send command */ ret = hi_mci_exec_cmd(host, cmd, data); if (ret) { HIMCI_ERROR("CMD execute is error!"); goto request_end; } /* wait command send complete */ ret = hi_mci_wait_cmd_complete(host); if (ret) goto request_end; /* start data transfer */ if (data) { /* wait data transfer complete */ ret = hi_mci_wait_data_complete(host); if (ret) goto request_end; /* wait card complete */ ret = hi_mci_wait_card_complete(host); if (ret) goto request_end; } if (cmd->resp_type & MMC_RSP_BUSY) { /* wait card complete */ ret = hi_mci_wait_card_complete(host); if (ret) goto request_end; } request_end: /* clear MMC host intr */ himci_writel(ALL_INT_CLR, host->base + MCI_RINTSTS); return ret; } static int hi_mci_set_ios(struct mmc *mmc) { struct himci_host *host = mmc->priv; unsigned int tmp_reg; HIMCI_DEBUG_FUN("Function Call"); HIMCI_ASSERT(mmc); HIMCI_ASSERT(host); if (mmc->clock) { hi_mci_control_cclk(host, DISABLE); hi_mci_set_cclk(host, mmc->clock); hi_mci_control_cclk(host, ENABLE); } else { hi_mci_control_cclk(host, DISABLE); } /* set bus_width */ HIMCI_DEBUG_INFO("ios->bus_width = %d", mmc->bus_width); tmp_reg = himci_readl(host->base + MCI_CTYPE); tmp_reg &= ~CARD_WIDTH_MASK; if (mmc->bus_width == 8) tmp_reg |= (CARD_WIDTH_8BIT<port); else if (mmc->bus_width == 4) tmp_reg |= (CARD_WIDTH_4BIT<port); else tmp_reg |= (CARD_WIDTH_1BIT<port); himci_writel(tmp_reg, host->base + MCI_CTYPE); hi_mci_set_drv_cap(mmc, 0); hi_mci_set_default_phase(mmc); return 0; } static int hi_mci_init(struct mmc *mmc) { struct himci_host *host = mmc->priv; HIMCI_DEBUG_FUN("Function Call"); hi_mci_init_card(host); return 0; } static int hi_mci_card_busy(struct mmc *mmc) { struct himci_host *host = mmc->priv; unsigned int regval; HIMCI_DEBUG_FUN("Function Call"); HIMCI_ASSERT(mmc); HIMCI_ASSERT(host); regval = himci_readl(host->base + MCI_STATUS); regval &= DATA_BUSY; return regval; } static void hi_mci_edge_tuning_enable(struct himci_host *host) { unsigned int val; himci_writel(0x80001, 0x1201014c); val = himci_readl(host->base + MCI_TUNING_CTRL); val |= HW_TUNING_EN; himci_writel(val, host->base + MCI_TUNING_CTRL); } static void hi_mci_edge_tuning_disable(struct himci_host *host) { unsigned int val; val = himci_readl(0x1201014c); val |= (1 << 16); himci_writel(val, 0x1201014c); val = himci_readl(host->base + MCI_TUNING_CTRL); val &= ~HW_TUNING_EN; himci_writel(val, host->base + MCI_TUNING_CTRL); } static void hi_mci_set_sap_phase(struct himci_host *host, unsigned int phase) { unsigned int reg_value; reg_value = himci_readl(host->base + MCI_UHS_REG_EXT); reg_value &= ~CLK_SMPL_PHS_MASK; reg_value |= (phase << CLK_SMPL_PHS_SHIFT); himci_writel(reg_value, host->base + MCI_UHS_REG_EXT); } static int hi_mci_send_stop(struct mmc * mmc) { struct mmc_cmd cmd = {0}; int err; cmd.cmdidx = MMC_CMD_STOP_TRANSMISSION; cmd.resp_type = MMC_RSP_R1; err = mmc_send_cmd(mmc, &cmd, NULL); return err; } static int hi_mci_send_tuning(struct mmc * mmc, unsigned int opcode) { int err = 0; unsigned int status = 1000; struct himci_host *host = mmc->priv; /* fix a problem that When the I/O voltage is increased to 1.89 V or 1.91V * at high and low temperatures, the system is suspended during the reboot test. */ unsigned cmd_count = 1000; hi_mci_control_cclk(host, DISABLE); tuning_retry: hi_mci_idma_reset(host); himci_writel(ALL_INT_CLR, host->base + MCI_RINTSTS); hi_mci_control_cclk(host, ENABLE); if (tuning_reset_flag == 1){ tuning_reset_flag = 0; cmd_count--; if (cmd_count == 0){ printf("BUG_ON:controller reset is failed!!!\n"); return -EINVAL; } goto tuning_retry; } err = mmc_send_tuning(mmc, opcode, NULL); hi_mci_send_stop(mmc); mmc_send_status(mmc ,&status); return err; } static unsigned int hi_mci_get_sap_dll_taps(void) { unsigned int regval = 0; regval = himci_readl(0x12010150); return (regval & 0xff); } static void hi_mci_set_dll_element(unsigned int element) { unsigned int regval; regval = himci_readl(0x1201014c); regval &=~(0xFF << 8); regval |= (element << 8); himci_writel(regval, 0x1201014c); } static void hi_mci_tuning_feedback(struct mmc * mmc) { struct himci_host *host = mmc->priv; hi_mci_control_cclk(host, DISABLE); mdelay(1); hi_mci_sys_reset(host); mdelay(1); himci_writel(ALL_INT_CLR, host->base + MCI_RINTSTS); hi_mci_control_cclk(host, ENABLE); mdelay(1); } /********************************************* ********************************************* EdgeMode A: |<---- totalphases(ele) ---->| _____________ ______|||||||||||||||_______ edge_p2f edge_f2p (endp) (startp) EdgeMode B: |<---- totalphases(ele) ---->| ________ _________ ||||||||||_________||||||||||| edge_f2p edge_p2f (startp) (endp) BestPhase: if(endp < startp) endp = endp + totalphases; Best = ((startp + endp) / 2) % totalphases ********************************************** **********************************************/ static int hi_mci_edgedll_mode_tuning(struct mmc *mmc, unsigned int opcode, int edge_p2f, int edge_f2p) { unsigned int index; unsigned int found = 0; unsigned int startp =-1, endp = -1; unsigned int startp_init = 0, endp_init = 0; unsigned int phaseoffset = 0, totalphases = 0; unsigned short ele,start_ele, phase_dll_elements; unsigned char mdly_tap_flag = 0; int prev_err = 0, err = 0; struct himci_host *host = mmc->priv; unsigned int phase_num = HIMCI_PHASE_SCALE; mdly_tap_flag = hi_mci_get_sap_dll_taps(); phase_dll_elements = mdly_tap_flag / HIMCI_PHASE_SCALE; totalphases = phase_dll_elements * phase_num; startp_init = edge_f2p * phase_dll_elements; endp_init = edge_p2f * phase_dll_elements; startp = startp_init; endp = endp_init; found = 1; start_ele = 2; /*Note: edgedll tuning must from edge_p2f to edge_f2p*/ if(edge_f2p >= edge_p2f) { phaseoffset = edge_p2f * phase_dll_elements; for (index = edge_p2f; index < edge_f2p; index++) { /* set phase shift */ hi_mci_set_sap_phase(host, index); for (ele = start_ele; ele <= phase_dll_elements ; ele++) { hi_mci_set_dll_element(ele); err = hi_mci_send_tuning(mmc, opcode); if (!err) found = 1; if (!prev_err && err && (endp == endp_init)) endp = phaseoffset + ele; if (err) startp = phaseoffset + ele; #ifdef TUNING_PROC_DEBUG printf("\tphase:%01u ele:%02u st:%03u end:%03u error:%d\n", index, ele, startp, endp, err); #endif prev_err = err; err = 0; } phaseoffset += phase_dll_elements; } } else { phaseoffset = edge_p2f * phase_dll_elements; for (index = edge_p2f ; index < phase_num ; index++) { /* set phase shift */ hi_mci_set_sap_phase(host, index); for (ele = start_ele; ele <= phase_dll_elements ; ele++) { hi_mci_set_dll_element(ele); err = hi_mci_send_tuning(mmc, opcode); if (!err) found = 1; if (!prev_err && err && (endp == endp_init)) endp = phaseoffset + ele; if (err) startp = phaseoffset + ele; #ifdef TUNING_PROC_DEBUG printf("\tphase:%02u ele:%02u st:%03u end:%03u error:%d\n", index, ele, startp, endp, err); #endif prev_err = err; err = 0; } phaseoffset += phase_dll_elements; } phaseoffset = 0; for (index = 0; index < edge_f2p; index++) { /* set phase shift */ hi_mci_set_sap_phase(host, index); for (ele = start_ele; ele <= phase_dll_elements ; ele++) { hi_mci_set_dll_element(ele); err = hi_mci_send_tuning(mmc, opcode); if (!err) found = 1; if (!prev_err && err && (endp == endp_init)) endp = phaseoffset + ele; if (err) startp = phaseoffset + ele; #ifdef TUNING_PROC_DEBUG printf("\tphase:%02u ele:%02u st:%03u end:%03u error:%d\n", index, ele, startp, endp, err); #endif prev_err = err; err = 0; } phaseoffset += phase_dll_elements; } } if (found) { printf("scan elemnts: startp:%u endp:%u\n", startp, endp); if (endp <= startp) endp += totalphases; phaseoffset = (( startp + endp ) / 2) % totalphases; index = (phaseoffset / phase_dll_elements); ele = (phaseoffset % phase_dll_elements); ele = ((ele > start_ele)?ele:start_ele); hi_mci_set_sap_phase(host, index); hi_mci_set_dll_element(ele); printf("Tuning SampleClock. mix set phase:[%02u/%02u] ele:[%0ud/%02u]\n", index, (phase_num - 1), ele, phase_dll_elements); himci_writel(ALL_INT_CLR, host->base + MCI_RINTSTS); return 0; } printf( "No valid phase shift! use default\n"); return -1; } static int hi_mci_execute_mix_mode_tuning(struct mmc * mmc, unsigned int opcode) { struct himci_host *host = mmc->priv; unsigned int index, regval; unsigned int found = 0,prefound = 0; unsigned int edge_p2f, edge_f2p; unsigned int edge_num = 0; int err; unsigned int phase_num = HIMCI_PHASE_SCALE; hi_mci_edge_tuning_enable(host); edge_p2f = 0; edge_f2p = phase_num; for (index = 0; index < phase_num; index++) { /* set phase shift */ hi_mci_set_sap_phase(host, index); err = hi_mci_send_tuning(mmc, opcode); if (!err) { regval = himci_readl(host->base + MCI_TUNING_CTRL); found = ((regval & FOUND_EDGE) == FOUND_EDGE); } else { found = 1; } if (found) edge_num++; if (prefound && !found) edge_f2p = index; else if (!prefound && found) edge_p2f = index; #ifdef TUNING_PROC_DEBUG printf("\tphase:%02u found:%02u p2f:%u f2p:%u error:%d\n", index, found, edge_p2f, edge_f2p, err); #endif if ((edge_p2f != 0) && (edge_f2p != phase_num)) break; prefound = found; found = 0; } if ((edge_p2f == 0) && (edge_f2p == phase_num)) { printf("unfound correct edge! check your config is correct!!\n"); return -1; } printf("scan edges:%u p2f:%u f2p:%u\n", edge_num, edge_p2f, edge_f2p); if (edge_f2p < edge_p2f) index = ((edge_f2p + edge_p2f) / 2) % phase_num; else index = ((edge_f2p + phase_num + edge_p2f) / 2) % phase_num; printf("mix set temp-phase %u\n", index); hi_mci_set_sap_phase(host, index); err = hi_mci_send_tuning(mmc,opcode); hi_mci_edge_tuning_disable(host); err = hi_mci_edgedll_mode_tuning(mmc, opcode, edge_p2f, edge_f2p); return err; } static int hi_mci_execute_tuning(struct mmc *mmc, unsigned int opcode) { struct himci_host *host = mmc->priv; int err; host->is_tuning = 1; err = hi_mci_execute_mix_mode_tuning(mmc, opcode); hi_mci_tuning_feedback(mmc); if (!err) err = hi_mci_send_tuning(mmc, opcode); host->is_tuning = 0; return err; } static void himci_shutdown(void) { unsigned long base_addr; unsigned int value; base_addr = EMMC_REG_BASE; value = readl((uintptr_t)(base_addr + MCI_CTRL)); value |= CTRL_RESET | FIFO_RESET | DMA_RESET; writel(value, (uintptr_t)(base_addr + MCI_CTRL)); writel(POWER_OFF, (uintptr_t)(base_addr + MCI_PWREN)); /* Delay 100ms, waiting for the eMMC device power off*/ udelay(100 * 1000); } void print_ext_csd(struct mmc *mmc) { unsigned char ext_csd[512]; unsigned int tmp; HIMCI_DEBUG_FUN("Function Call"); int err = mmc_send_ext_csd(mmc, ext_csd); if (err) { HIMCI_ERROR("Check est_csd error!"); return; } HIMCI_DEBUG_INFO("Extended CSD register:"); for (tmp = 0; tmp < 512; tmp += 8) HIMCI_DEBUG_INFO ("%03d: %02x %02x %02x %02x %02x %02x %02x %02x", tmp, ext_csd[tmp], ext_csd[tmp + 1], ext_csd[tmp + 2], ext_csd[tmp + 3], ext_csd[tmp + 4], ext_csd[tmp + 5], ext_csd[tmp + 6], ext_csd[tmp + 7]); } static void print_mmcinfo(struct mmc *mmc) { HIMCI_DEBUG_FUN("Function Call"); printf("MMC/SD Card:\n"); printf(" MID: 0x%x\n", mmc->cid[0] >> 24); printf(" Read Block: %d Bytes\n", mmc->read_bl_len); printf(" Write Block: %d Bytes\n", mmc->write_bl_len); printf(" Chip Size: %s Bytes (%s)\n", ultohstr(mmc->capacity), mmc->high_capacity ? "High Capacity" : "Low Capacity"); printf(" Name: \"%c%c%c%c%c\"\n", mmc->cid[0] & 0xff, (mmc->cid[1] >> 24), (mmc->cid[1] >> 16) & 0xff, (mmc->cid[1] >> 8) & 0xff, mmc->cid[1] & 0xff); printf(" Chip Type: %s\n" " Version: %d.%d\n", IS_SD(mmc) ? "SD" : "MMC", (mmc->version >> 4) & 0xf, mmc->version & 0xf); printf(" Speed: %sHz\n", ultohstr(mmc->clock)); printf(" Bus Width: %dbit\n", mmc->bus_width); } int himci_probe(int dev_num) { struct mmc *mmc = find_mmc_device(dev_num); int err = 0; puts("\nEMMC/MMC/SD controller initialization.\n"); if (!mmc) { printf("mmc device not found!!\n"); return -1; } err = mmc_init(mmc); if (err) { printf("mmc_init failed! err:%d\n", err); return err; } print_mmcinfo(mmc); #ifdef CONFIG_SUPPORT_EMMC_BOOT if (!IS_SD(mmc)) return mmc_set_boot_config(mmc); #endif return 0; } static const struct mmc_ops himci_ops = { .send_cmd = hi_mci_request, .set_ios = hi_mci_set_ios, .init = hi_mci_init, .execute_tuning = hi_mci_execute_tuning, .card_busy = hi_mci_card_busy, }; static void himci_setup_cfg(struct mmc_config *cfg, struct himci_host *host, unsigned int max_clk, unsigned int min_clk) { cfg->name = host->name; cfg->f_min = min_clk; cfg->f_max = max_clk; cfg->voltages = MMC_VDD_32_33 | MMC_VDD_33_34; cfg->host_caps |= MMC_MODE_4BIT; cfg->host_caps |= MMC_MODE_HS | MMC_MODE_HS_52MHz | MMC_MODE_HS200; cfg->b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT; cfg->ops = &himci_ops; } int add_himci(struct himci_host *host, unsigned int max_clk, unsigned int min_clk) { himci_setup_cfg(&host->cfg, host, max_clk, min_clk); host->mmc = mmc_create(&host->cfg, host); if (host->mmc == NULL) return -1; add_shutdown(himci_shutdown); return 0; } int himci_add_port(int index, unsigned int reg_base, unsigned int freq) { struct himci_host *host = NULL; unsigned int regval; HIMCI_DEBUG_FUN("Function Call"); hi_mci_sys_init(index, freq); /* check controller version. */ regval = himci_readl(reg_base + MCI_VERID); if ((regval != MCI_VERID_VALUE) && (regval != MCI_VERID_VALUE2) && (regval != MCI_VERID_VALUE3)) { printf("MMC/SD/EMMC controller version incorrect.\n"); return -ENODEV; } host = calloc(1, sizeof(struct himci_host)); if (!host) { puts("host malloc fail!\n"); return -ENOMEM; } host->name = "himci"; host->dev_id = index; host->base = (unsigned long)reg_base; host->dma_des = hi_dma_des; host->card_status = hi_mci_sys_card_detect(host); host->port = 0; host->is_tuning = 0; return add_himci(host, freq, MMC_CCLK_MIN); }