// SPDX-License-Identifier: GPL-2.0+ /* * (C) Copyright 2007-2011 * Allwinner Technology Co., Ltd. * Aaron * * MMC driver for allwinner sunxi platform. */ #include #include #include #include #include #include #include #include #include #include #include #include "sunxi_mmc.h" #include "host/sunxi_mmc_host_common.h" #include "mmc_def.h" #ifndef readl #define readl(a) *(volatile uint *)(ulong)(a) #endif #ifndef writel #define writel(v, c) *(volatile uint *)(ulong)(c) = (v) #endif #define DTO_MAX 200 #if !CONFIG_IS_ENABLED(DM_MMC) /* support 4 mmc hosts */ struct sunxi_mmc_priv mmc_host[4]; struct mmc_reg_v4p1 mmc_host_reg_bak[3]; //extern u8 ext_odly_spd_freq[]; //extern u8 ext_sdly_spd_freq[]; //extern u8 ext_odly_spd_freq_sdc0[]; //extern u8 ext_sdly_spd_freq_sdc0[]; static u8 ext_odly_spd_freq[MAX_SPD_MD_NUM*MAX_CLK_FREQ_NUM]; static u8 ext_sdly_spd_freq[MAX_SPD_MD_NUM*MAX_CLK_FREQ_NUM]; static u8 ext_odly_spd_freq_sdc0[MAX_SPD_MD_NUM*MAX_CLK_FREQ_NUM]; static u8 ext_sdly_spd_freq_sdc0[MAX_SPD_MD_NUM*MAX_CLK_FREQ_NUM]; void dumphex32(char *name, char *base, int len) { __u32 i; printf("dump %s registers:", name); for (i = 0; i < len; i += 4) { if (!(i & 0xf)) printf("\n0x%p : ", base + i); printf("0x%08x ", readl((ulong)base + i)); } printf("\n"); } void mmc_dump_errinfo(struct sunxi_mmc_priv *smc_priv, struct mmc_cmd *cmd) { MMCMSG(smc_priv->mmc, "smc %d err, cmd %d, %s%s%s%s%s%s%s%s%s%s%s\n", smc_priv->mmc_no, cmd ? cmd->cmdidx : -1, smc_priv->raw_int_bak & SDXC_RespErr ? " RE" : "", smc_priv->raw_int_bak & SDXC_RespCRCErr ? " RCE" : "", smc_priv->raw_int_bak & SDXC_DataCRCErr ? " DCE" : "", smc_priv->raw_int_bak & SDXC_RespTimeout ? " RTO" : "", smc_priv->raw_int_bak & SDXC_DataTimeout ? " DTO" : "", smc_priv->raw_int_bak & SDXC_DataStarve ? " DS" : "", smc_priv->raw_int_bak & SDXC_FIFORunErr ? " FE" : "", smc_priv->raw_int_bak & SDXC_HardWLocked ? " HL" : "", smc_priv->raw_int_bak & SDXC_StartBitErr ? " SBE" : "", smc_priv->raw_int_bak & SDXC_EndBitErr ? " EBE" : "", smc_priv->raw_int_bak == 0 ? " STO" : "" ); } static int sunxi_mmc_getcd_gpio(int sdc_no) { /* switch (sdc_no) { case 0: return sunxi_name_to_gpio(CONFIG_MMC0_CD_PIN); case 1: return sunxi_name_to_gpio(CONFIG_MMC1_CD_PIN); case 2: return sunxi_name_to_gpio(CONFIG_MMC2_CD_PIN); case 3: return sunxi_name_to_gpio(CONFIG_MMC3_CD_PIN); } */ return -EINVAL; } static int mmc_resource_init(int sdc_no) { struct sunxi_mmc_priv *priv = &mmc_host[sdc_no]; struct sunxi_ccm_reg *ccm = (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; int cd_pin, ret = 0; MMCDBG("init mmc %d resource\n", sdc_no); switch (sdc_no) { case 0: priv->reg = (struct mmc_reg_v4p1 *)SUNXI_MMC0_BASE; priv->mclkreg = &ccm->sd0_clk_cfg; break; case 1: priv->reg = (struct mmc_reg_v4p1 *)SUNXI_MMC1_BASE; priv->mclkreg = &ccm->sd1_clk_cfg; break; case 2: priv->reg = (struct mmc_reg_v4p1 *)SUNXI_MMC2_BASE; priv->mclkreg = &ccm->sd2_clk_cfg; break; #ifdef SUNXI_MMC3_BASE case 3: priv->reg = (struct mmc_reg_v4p1 *)SUNXI_MMC3_BASE; priv->mclkreg = &ccm->sd3_clk_cfg; break; #endif default: MMCINFO("Wrong mmc number %d\n", sdc_no); return -1; } #if !defined(CONFIG_MACH_SUN8IW5) && !defined(CONFIG_MACH_SUN8IW6) && !defined(CONFIG_MACH_SUN8IW7) && !defined(CONFIG_MACH_SUN8IW8)\ && !defined(CONFIG_MACH_SUN8IW10) && !defined(CONFIG_MACH_SUN8IW11) priv->hclkbase = IOMEM_ADDR(&ccm->sd_gate_reset); priv->hclkrst = IOMEM_ADDR(&ccm->sd_gate_reset); #else priv->hclkbase = IOMEM_ADDR(&ccm->ahb_gate0); priv->hclkrst = IOMEM_ADDR(&ccm->ahb_reset0_cfg); #endif priv->mmc_no = sdc_no; cd_pin = sunxi_mmc_getcd_gpio(sdc_no); if (cd_pin >= 0) { ret = gpio_request(cd_pin, "mmc_cd"); if (!ret) { sunxi_gpio_set_pull(cd_pin, SUNXI_GPIO_PULL_UP); ret = gpio_direction_input(cd_pin); } } #ifdef CONFIG_MACH_SUN55IW3 #ifdef CONFIG_CLK_SUNXI priv->cfg.clk_mmc = clk_get(NULL, priv->cfg.clk_mmc_name); if (IS_ERR_OR_NULL(priv->cfg.clk_mmc)) { MMCINFO("Error to get clk_mmc\n"); return -1; } #else MMCINFO("%s: need ccu config open\n", __func__); return -1; #endif #endif return ret; } #endif static int sunxi_mmc_pin_set(int sdc_no) { struct sunxi_mmc_priv *priv = &mmc_host[sdc_no]; struct sunxi_mmc_pininfo *pin_default = &priv->pin_default; struct sunxi_mmc_pininfo *pin_disable = &priv->pin_disable; int ret = -1; if (priv->pwr_handler != 0 && pin_disable->pin_count > 0) { ret = gpio_request_early(pin_disable->pin_set, pin_disable->pin_count, 1); gpio_write_one_pin_value(priv->pwr_handler, 1, "card-pwr-gpios"); mdelay(priv->time_pwroff); gpio_write_one_pin_value(priv->pwr_handler, 0, "card-pwr-gpios"); /*delay to ensure voltage stability*/ mdelay(1); } if (pin_default->pin_count > 0) { ret = gpio_request_early(pin_default->pin_set, pin_default->pin_count, 1); } return ret; } void sunxi_mmc_pin_release(int sdc_no) { struct sunxi_mmc_priv *priv = &mmc_host[sdc_no]; if (priv->pwr_handler != 0) { gpio_release(priv->pwr_handler, 0); } } int mmc_clk_io_onoff(int sdc_no, int onoff, int reset_clk) { struct sunxi_mmc_priv *priv = &mmc_host[sdc_no]; priv->sunxi_mmc_clk_io_onoff(sdc_no, onoff, reset_clk); return 0; } static int mmc_update_clk(struct sunxi_mmc_priv *priv) { unsigned int cmd; unsigned timeout_msecs = 10000; unsigned long start = get_timer(0); writel(readl(&priv->reg->clkcr)|(0x1 << 31), &priv->reg->clkcr); cmd = SUNXI_MMC_CMD_START | SUNXI_MMC_CMD_UPCLK_ONLY | SUNXI_MMC_CMD_WAIT_PRE_OVER; writel(cmd, &priv->reg->cmd); while (readl(&priv->reg->cmd) & SUNXI_MMC_CMD_START) { if (get_timer(start) > timeout_msecs) { dumphex32("mmc", (char *)priv->reg, 0x200); return -1; } } writel(readl(&priv->reg->clkcr) & (~(0x1 << 31)), &priv->reg->clkcr); /* clock update sets various irq status bits, clear these */ writel(readl(&priv->reg->rint), &priv->reg->rint); return 0; } static int mmc_set_mod_clk(struct sunxi_mmc_priv *priv, unsigned int hz) { int rval; rval = priv->mmc_set_mod_clk(priv, hz); return rval; } static int mmc_config_clock(struct sunxi_mmc_priv *priv, struct mmc *mmc) { unsigned rval = readl(&priv->reg->clkcr); /* Disable Clock */ rval &= ~SUNXI_MMC_CLK_ENABLE; writel(rval, &priv->reg->clkcr); if (mmc_update_clk(priv)) { MMCINFO("Disable clock: mmc update clk failed\n"); return -1; } /* Set mod_clk to new rate */ if (mmc_set_mod_clk(priv, mmc->clock)) return -1; #if 0 /* Clear internal divider */ rval &= ~SUNXI_MMC_CLK_DIVIDER_MASK; writel(rval, &priv->reg->clkcr); #endif /* Re-enable Clock */ rval = readl(&priv->reg->clkcr); rval |= SUNXI_MMC_CLK_ENABLE; writel(rval, &priv->reg->clkcr); if (mmc_update_clk(priv)) { MMCINFO("Re-enable clock: mmc update clk failed\n"); return -1; } return 0; } static int sunxi_mmc_set_ios_common(struct sunxi_mmc_priv *priv, struct mmc *mmc) { MMCDBG("set ios: bus_width: %x, clock: %d\n", mmc->bus_width, mmc->clock); /* Change clock first */ if (mmc->clock && mmc_config_clock(priv, mmc) != 0) { priv->fatal_err = 1; return -EINVAL; } /* Change bus width */ if (mmc->bus_width == 8) writel(0x2, &priv->reg->width); else if (mmc->bus_width == 4) writel(0x1, &priv->reg->width); else writel(0x0, &priv->reg->width); /* set speed mode */ priv->sunxi_mmc_set_speed_mode(priv, mmc); return 0; } #if !CONFIG_IS_ENABLED(DM_MMC) static int sunxi_mmc_core_init(struct mmc *mmc) { struct sunxi_mmc_priv *priv = mmc->priv; priv->sunxi_mmc_core_init(mmc); return 0; } #endif static int mmc_save_regs(struct sunxi_mmc_priv *mmchost) { struct mmc_reg_v4p1 *reg = (struct mmc_reg_v4p1 *)mmchost->reg; struct mmc_reg_v4p1 *reg_bak = (struct mmc_reg_v4p1 *)mmchost->reg_bak; reg_bak->gctrl = readl(®->gctrl); reg_bak->clkcr = readl(®->clkcr); reg_bak->timeout = readl(®->timeout); reg_bak->width = readl(®->width); reg_bak->imask = readl(®->imask); reg_bak->ftrglevel = readl(®->ftrglevel); reg_bak->dbgc = readl(®->dbgc); reg_bak->ntsr = readl(®->ntsr); reg_bak->hwrst = readl(®->hwrst); reg_bak->dmac = readl(®->dmac); reg_bak->idie = readl(®->idie); reg_bak->thldc = readl(®->thldc); reg_bak->dsbd = readl(®->dsbd); #if (!defined(CONFIG_MACH_SUN8IW7)) reg_bak->csdc = readl(®->csdc); reg_bak->drv_dl = readl(®->drv_dl); reg_bak->samp_dl = readl(®->samp_dl); reg_bak->ds_dl = readl(®->ds_dl); #endif return 0; } static int mmc_restore_regs(struct sunxi_mmc_priv *mmchost) { struct mmc_reg_v4p1 *reg = (struct mmc_reg_v4p1 *)mmchost->reg; struct mmc_reg_v4p1 *reg_bak = (struct mmc_reg_v4p1 *)mmchost->reg_bak; writel(reg_bak->gctrl, ®->gctrl); writel(reg_bak->clkcr, ®->clkcr); writel(reg_bak->timeout, ®->timeout); writel(reg_bak->width, ®->width); writel(reg_bak->imask, ®->imask); writel(reg_bak->ftrglevel, ®->ftrglevel); if (reg_bak->dbgc) writel(0xdeb, ®->dbgc); writel(reg_bak->ntsr, ®->ntsr); writel(reg_bak->hwrst, ®->hwrst); writel(reg_bak->dmac, ®->dmac); writel(reg_bak->idie, ®->idie); writel(reg_bak->thldc, ®->thldc); writel(reg_bak->dsbd, ®->dsbd); #if (!defined(CONFIG_MACH_SUN8IW7)) writel(reg_bak->csdc, ®->csdc); sunxi_r_op(mmchost, writel(reg_bak->drv_dl, ®->drv_dl)); writel(reg_bak->samp_dl, ®->samp_dl); writel(reg_bak->ds_dl, ®->ds_dl); #endif return 0; } static int mmc_trans_data_by_cpu(struct sunxi_mmc_priv *priv, struct mmc *mmc, struct mmc_data *data) { const int reading = !!(data->flags & MMC_DATA_READ); const uint32_t status_bit = reading ? SUNXI_MMC_STATUS_FIFO_EMPTY : SUNXI_MMC_STATUS_FIFO_FULL; unsigned i; unsigned *buff = (unsigned int *)(reading ? data->dest : data->src); unsigned byte_cnt = data->blocksize * data->blocks; unsigned timeout_msecs = byte_cnt; unsigned long start; if (timeout_msecs < 2000) timeout_msecs = 2000; /* Always read / write data through the CPU */ setbits_le32(&priv->reg->gctrl, SUNXI_MMC_GCTRL_ACCESS_BY_AHB); start = get_timer(0); for (i = 0; i < (byte_cnt >> 2); i++) { while (readl(&priv->reg->status) & status_bit) { if (get_timer(start) > timeout_msecs) return -1; } if (reading) buff[i] = readl(&priv->reg->fifo); else writel(buff[i], &priv->reg->fifo); } return 0; } static int mmc_trans_data_by_dma(struct sunxi_mmc_priv *priv, struct mmc *mmc, struct mmc_data *data) { struct mmc_des_v4p1 *pdes = priv->pdes; unsigned byte_cnt = data->blocksize * data->blocks; unsigned char *buff; unsigned des_idx = 0; unsigned buff_frag_num = 0; unsigned remain; unsigned i, rval; buff = data->flags & MMC_DATA_READ ? (unsigned char *)data->dest : (unsigned char *)data->src; buff_frag_num = byte_cnt >> SDXC_DES_NUM_SHIFT; remain = byte_cnt & (SDXC_DES_BUFFER_MAX_LEN - 1); if (remain) buff_frag_num++; else remain = SDXC_DES_BUFFER_MAX_LEN; flush_cache((unsigned long)buff, ALIGN((unsigned long)byte_cnt, CONFIG_SYS_CACHELINE_SIZE)); for (i = 0; i < buff_frag_num; i++, des_idx++) { memset((void *)&pdes[des_idx], 0, sizeof(struct mmc_des_v4p1)); pdes[des_idx].des_chain = 1; pdes[des_idx].own = 1; pdes[des_idx].dic = 1; if (buff_frag_num > 1 && i != buff_frag_num - 1) pdes[des_idx].data_buf1_sz = SDXC_DES_BUFFER_MAX_LEN; else pdes[des_idx].data_buf1_sz = remain; if (priv->version == 0x40200 || priv->version == 0x40502 || priv->version >= 0x50300 || priv->version == 0x40104) pdes[des_idx].buf_addr_ptr1 = ((ulong)buff + i * SDXC_DES_BUFFER_MAX_LEN) >> 2; else pdes[des_idx].buf_addr_ptr1 = ((ulong)buff + i * SDXC_DES_BUFFER_MAX_LEN); if (i == 0) pdes[des_idx].first_des = 1; if (i == buff_frag_num - 1) { pdes[des_idx].dic = 0; pdes[des_idx].last_des = 1; pdes[des_idx].end_of_ring = 1; pdes[des_idx].buf_addr_ptr2 = 0; } else { if (priv->version == 0x40200 || priv->version == 0x40502 || priv->version >= 0x50300 || priv->version == 0x40104) pdes[des_idx].buf_addr_ptr2 = ((ulong)&pdes[des_idx + 1]) >> 2; else pdes[des_idx].buf_addr_ptr2 = ((ulong)&pdes[des_idx + 1]); } MMCDBG("frag %d, remain %d, des[%d](%08x): " "[0] = %08x, [1] = %08x, [2] = %08x, [3] = %08x\n", i, remain, des_idx, PT_TO_PHU(&pdes[des_idx]), (u32)((u32 *)&pdes[des_idx])[0], (u32)((u32 *)&pdes[des_idx])[1], (u32)((u32 *)&pdes[des_idx])[2], (u32)((u32 *)&pdes[des_idx])[3]); } flush_cache((unsigned long)pdes, ALIGN(sizeof(struct mmc_des_v4p1) * (des_idx + 1), CONFIG_SYS_CACHELINE_SIZE)); WR_MB(); /* * GCTRLREG * GCTRL[2] : DMA reset * GCTRL[5] : DMA enable * * IDMACREG * IDMAC[0] : IDMA soft reset * IDMAC[1] : IDMA fix burst flag * IDMAC[7] : IDMA on * * IDIECREG * IDIE[0] : IDMA transmit interrupt flag * IDIE[1] : IDMA receive interrupt flag */ rval = readl(&priv->reg->gctrl); writel(rval | (1 << 5) | (1 << 2), &priv->reg->gctrl); /* dma enable */ writel((1 << 0), &priv->reg->dmac); /* idma reset */ while (readl(&priv->reg->dmac) & 0x1) { } /* wait idma reset done */ writel((1 << 1) | (1 << 7), &priv->reg->dmac); /* idma on */ rval = readl(&priv->reg->idie) & (~3); if (data->flags & MMC_DATA_WRITE) rval |= (1 << 0); else rval |= (1 << 1); writel(rval, &priv->reg->idie); if (priv->version == 0x40200 || priv->version == 0x40502 || priv->version >= 0x50300 || priv->version == 0x40104) writel(((unsigned long)pdes) >> 2, &priv->reg->dlba); else writel(((unsigned long)pdes), &priv->reg->dlba); writel(priv->dma_tl, &priv->reg->ftrglevel); return 0; } static int mmc_rint_wait(struct sunxi_mmc_priv *priv, struct mmc *mmc, uint timeout_msecs, uint done_bit, const char *what, uint usedma) { unsigned int status; unsigned int done = 0; unsigned long start = get_timer(0); do { status = readl(&priv->reg->rint); if ((get_timer(start) > timeout_msecs) || (status & SUNXI_MMC_RINT_INTERRUPT_ERROR_BIT)) { MMCMSG(mmc, "mmc %d %s timeout %x status %x\n", priv->mmc_no, what, status & SUNXI_MMC_RINT_INTERRUPT_ERROR_BIT, status); return -ETIMEDOUT; } if (usedma && !strncmp(what, "data", sizeof("data"))) done = ((status & done_bit) && (readl(&priv->reg->idst) & 0x3)) ? 1 : 0; else done = (status & done_bit); } while (!done); return 0; } static void sunxi_mmc_set_rdtmout_reg(struct sunxi_mmc_priv *priv, struct mmc *mmc, unsigned int rdtmout) { unsigned int rval = 0; unsigned int rdto_clk = 0; unsigned int mode_2x = 0; unsigned int hs400_ntm_en = 0; rdto_clk = mmc->clock / 1000 * rdtmout; rval = readl(&priv->reg->ntsr); mode_2x = rval & (0x1 << 31); hs400_ntm_en = rval & 0x1; if ((mmc->speed_mode == HS400 && hs400_ntm_en) || (mmc->speed_mode == HSDDR52_DDR50 && mmc->bus_width == 8) || (mmc->speed_mode == HSDDR52_DDR50 && mmc->bus_width == 4 && mode_2x)) { rdto_clk = rdto_clk << 1; } rval = readl(&priv->reg->gctrl); /*ddr50 mode don't use 256x timeout unit*/ if (rdto_clk > 0xffffff && mmc->speed_mode != HSDDR52_DDR50) { rdto_clk = (rdto_clk + 255)/256; rval |= (0x1 << 11); } else { rdto_clk = 0xffffff; rval &= ~(0x1 << 11); } writel(rval, &priv->reg->gctrl); rval = readl(&priv->reg->timeout); rval &= ~(0xffffff << 8); rval |= (rdto_clk << 8); writel(rval, &priv->reg->timeout); MMCDBG("rdtoclk:%d, reg-tmout:%d, gctl:%x, speed_mode:%d, clock:%d, nstr:%x\n", rdto_clk, readl(&priv->reg->timeout), readl(&priv->reg->gctrl), mmc->speed_mode, mmc->clock, readl(&priv->reg->ntsr)); } static int sunxi_mmc_do_send_cmd_common(struct sunxi_mmc_priv *priv, struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data) { unsigned int cmdval = SUNXI_MMC_CMD_START; unsigned int timeout_msecs; int error = 0; unsigned int status = 0; unsigned int usedma = 0; unsigned int bytecnt = 0; if (priv->fatal_err) { MMCINFO("mmc %d Found fatal err,so no send cmd\n", priv->mmc_no); return -1; } if (cmd->resp_type & MMC_RSP_BUSY) MMCDBG("mmc cmd %d check rsp busy\n", cmd->cmdidx); if (cmd->cmdidx == 12 && mmc->manual_stop_flag == 0) { MMCDBG("usually, cmd12 is sent after cmd18/cmd25 automantically.\n"); /* don't wait write busy here, because no cmd12 will be sent for cmd24. * write busy status will be check after sent cmd25. */ return 0; } if (!cmd->cmdidx) cmdval |= SUNXI_MMC_CMD_SEND_INIT_SEQ; if (cmd->resp_type & MMC_RSP_PRESENT) cmdval |= SUNXI_MMC_CMD_RESP_EXPIRE; if (cmd->resp_type & MMC_RSP_136) cmdval |= SUNXI_MMC_CMD_LONG_RESPONSE; if (cmd->resp_type & MMC_RSP_CRC) cmdval |= SUNXI_MMC_CMD_CHK_RESPONSE_CRC; #if 0 /* write ds dly Traversal test*/ if ((uboot_spare_head.boot_data.work_mode == WORK_MODE_BOOT) && ((cmd->cmdidx == 24) || (cmd->cmdidx == 25))) { unsigned tmode = priv->timing_mode; printf("===========there is write opration!========\n"); if (mmc->speed_mode == HS200_SDR104 && (tmode == SUNXI_MMC_TIMING_MODE_4)) { u32 rval = 0; rval = readl(&priv->reg->samp_dl); rval &= (~SDXC_CfgDly); rval |= ((40 & SDXC_CfgDly) | SDXC_EnableDly); writel(rval, &priv->reg->samp_dl); printf("hs200 samp 0x%x\n", readl(&priv->reg->samp_dl)); } else if (mmc->speed_mode == HSSDR52_SDR25 && (tmode == SUNXI_MMC_TIMING_MODE_4)) { u32 rval = 0; rval = readl(&priv->reg->samp_dl); rval &= (~SDXC_CfgDly); rval |= ((38 & SDXC_CfgDly) | SDXC_EnableDly); writel(rval, &priv->reg->samp_dl); printf("hssdr samp 0x%x\n", readl(&priv->reg->samp_dl)); } else if (mmc->speed_mode == HS400 && (tmode == SUNXI_MMC_TIMING_MODE_4)) { u32 rval = 0; rval = readl(&priv->reg->ds_dl); rval &= (~SDXC_CfgDly); rval |= ((31 & SDXC_CfgDly) | SDXC_EnableDly); writel(rval, &priv->reg->ds_dl); printf("hs400 ds 0x%x\n", readl(&priv->reg->ds_dl)); } } #endif #if 0 /*read ds dly Traversal test*/ if ((uboot_spare_head.boot_data.work_mode == WORK_MODE_BOOT) && ((cmd->cmdidx == 17) || (cmd->cmdidx == 18))) { unsigned tmode = priv->timing_mode; int work_mode = uboot_spare_head.boot_data.work_mode; if (work_mode != WORK_MODE_BOOT || (mmc->do_tuning == 0x1 && mmc->tuning_end == 0x0) || (mmc->cfg->sample_mode != AUTO_SAMPLE_MODE)) { } else { printf("===========There is read opration!========\n"); if (mmc->speed_mode == HS200_SDR104 && (tmode == SUNXI_MMC_TIMING_MODE_4)) { u32 rval = 0; rval = readl(&priv->reg->samp_dl); rval &= (~SDXC_CfgDly); rval |= ((40 & SDXC_CfgDly) | SDXC_EnableDly); writel(rval, &priv->reg->samp_dl); printf("hs200 samp 0x%x\n", readl(&priv->reg->samp_dl)); } else if (mmc->speed_mode == HSSDR52_SDR25 && (tmode == SUNXI_MMC_TIMING_MODE_4)) { u32 rval = 0; rval = readl(&priv->reg->samp_dl); rval &= (~SDXC_CfgDly); rval |= ((38 & SDXC_CfgDly) | SDXC_EnableDly); writel(rval, &priv->reg->samp_dl); printf("hssdr samp 0x%x\n", readl(&priv->reg->samp_dl)); } else if (mmc->speed_mode == HS400 && (tmode == SUNXI_MMC_TIMING_MODE_4)) { u32 rval = 0; rval = readl(&priv->reg->ds_dl); rval &= (~SDXC_CfgDly); rval |= ((30 & SDXC_CfgDly) | SDXC_EnableDly); writel(rval, &priv->reg->ds_dl); printf("hs400 ds 0x%x\n", readl(&priv->reg->ds_dl)); } } } #endif #if 0 /*read ds retry test*/ if ((uboot_spare_head.boot_data.work_mode == WORK_MODE_BOOT) && ((cmd->cmdidx == 18) || (cmd->cmdidx == 17))) { unsigned tmode = priv->timing_mode; int work_mode = uboot_spare_head.boot_data.work_mode; static int i = 1; if (work_mode != WORK_MODE_BOOT || (mmc->do_tuning == 0x1 && mmc->tuning_end == 0x0) || (mmc->cfg->sample_mode != AUTO_SAMPLE_MODE)) { } else { printf("===========There is read opration!========\n"); if ((i < 5) && (tmode == HS400)) { u32 rval = 0; rval = readl(&priv->reg->ds_dl); rval &= (~SDXC_CfgDly); rval |= ((40 & SDXC_CfgDly) | SDXC_EnableDly); writel(rval, &priv->reg->ds_dl); MMCINFO("ds %x\n", readl(&priv->reg->ds_dl)); i++; } else MMCINFO("end mauanl failed%d\n", i); } } #endif #if 0 /*write ds delay retry test*/ if ((uboot_spare_head.boot_data.work_mode == WORK_MODE_BOOT) && ((cmd->cmdidx == 24) || (cmd->cmdidx == 25))) { unsigned tmode = priv->timing_mode; int work_mode = uboot_spare_head.boot_data.work_mode; static int i = 1; if (work_mode != WORK_MODE_BOOT || (mmc->do_tuning == 0x1 && mmc->tuning_end == 0x0) || (mmc->cfg->sample_mode != AUTO_SAMPLE_MODE)) { } else { if ((i < 5) && (tmode == HS400)) { u32 rval = 0; rval = readl(&priv->reg->ds_dl); rval &= (~SDXC_CfgDly); rval |= ((58 & SDXC_CfgDly) | SDXC_EnableDly); writel(rval, &priv->reg->ds_dl); printf("ds %x\n", readl(&priv->reg->ds_dl)); i++; } else MMCINFO("end\n"); } } #endif #if 0 /*sdlay retry test*/ if ((uboot_spare_head.boot_data.work_mode == WORK_MODE_BOOT) && ((cmd->cmdidx == 24) || (cmd->cmdidx == 25))) { unsigned tmode = priv->timing_mode; int work_mode = uboot_spare_head.boot_data.work_mode; static int i = 1; if (work_mode != WORK_MODE_BOOT || (mmc->do_tuning == 0x1 && mmc->tuning_end == 0x0) || (mmc->cfg->sample_mode != AUTO_SAMPLE_MODE)) { } else { if ((i < 3) && (tmode == SUNXI_MMC_TIMING_MODE_4) && (mmc->speed_mode == HS200_SDR104)) { u32 rval = 0; rval = readl(&priv->reg->samp_dl); rval &= (~SDXC_CfgDly); rval |= ((54 & SDXC_CfgDly) | SDXC_EnableDly); writel(rval, &priv->reg->samp_dl); MMCINFO("***********samp %x\n", readl(&priv->reg->samp_dl)); i++; } else MMCINFO("end %d tm %d\n", i, tmode); } } #endif #if 0 /*sdly Traversal write*/ if ((uboot_spare_head.boot_data.work_mode == WORK_MODE_BOOT) && ((cmd->cmdidx == 24) || (cmd->cmdidx == 25))) { unsigned tmode = priv->timing_mode; int work_mode = uboot_spare_head.boot_data.work_mode; if (work_mode != WORK_MODE_BOOT || (mmc->do_tuning == 0x1 && mmc->tuning_end == 0x0) || (mmc->cfg->sample_mode != AUTO_SAMPLE_MODE)) { } else { printf("===========there is write opration!========\n"); if (mmc->speed_mode == HS200_SDR104 && (tmode == SUNXI_MMC_TIMING_MODE_4)) { u32 rval = 0; rval = readl(&priv->reg->samp_dl); rval &= (~SDXC_CfgDly); rval |= ((40 & SDXC_CfgDly) | SDXC_EnableDly); writel(rval, &priv->reg->samp_dl); printf("hs200 samp 0x%x\n", readl(&priv->reg->samp_dl)); } else if (mmc->speed_mode == HSSDR52_SDR25 && (tmode == SUNXI_MMC_TIMING_MODE_4)) { u32 rval = 0; rval = readl(&priv->reg->samp_dl); rval &= (~SDXC_CfgDly); rval |= ((45 & SDXC_CfgDly) | SDXC_EnableDly); writel(rval, &priv->reg->samp_dl); printf("hssdr samp 0x%x\n", readl(&priv->reg->samp_dl)); } } } #endif #if 0 /*sdly Traversal read*/ if ((uboot_spare_head.boot_data.work_mode == WORK_MODE_BOOT) && ((cmd->cmdidx == 17) || (cmd->cmdidx == 18))) { unsigned tmode = priv->timing_mode; int work_mode = uboot_spare_head.boot_data.work_mode; if (work_mode != WORK_MODE_BOOT || (mmc->do_tuning == 0x1 && mmc->tuning_end == 0x0) || (mmc->cfg->sample_mode != AUTO_SAMPLE_MODE)) { } else { printf("===========There is read opration!========\n"); if (mmc->speed_mode == HS200_SDR104 && (tmode == SUNXI_MMC_TIMING_MODE_4)) { u32 rval = 0; rval = readl(&priv->reg->samp_dl); rval &= (~SDXC_CfgDly); rval |= ((40 & SDXC_CfgDly) | SDXC_EnableDly); writel(rval, &priv->reg->samp_dl); printf("hs200 samp 0x%x\n", readl(&priv->reg->samp_dl)); } else if (mmc->speed_mode == HSSDR52_SDR25 && (tmode == SUNXI_MMC_TIMING_MODE_4)) { u32 rval = 0; rval = readl(&priv->reg->samp_dl); rval &= (~SDXC_CfgDly); rval |= ((47 & SDXC_CfgDly) | SDXC_EnableDly); writel(rval, &priv->reg->samp_dl); printf("hssdr samp 0x%x\n", readl(&priv->reg->samp_dl)); } else if (mmc->speed_mode == HS400 && (tmode == SUNXI_MMC_TIMING_MODE_4)) { u32 rval = 0; rval = readl(&priv->reg->samp_dl); rval &= (~SDXC_CfgDly); rval |= ((53 & SDXC_CfgDly) | SDXC_EnableDly); writel(rval, &priv->reg->samp_dl); printf("hs400 samp 0x%x\n", readl(&priv->reg->samp_dl)); } } } #endif #if 0 /*read samply delay retry test*/ if ((uboot_spare_head.boot_data.work_mode == WORK_MODE_BOOT) && ((cmd->cmdidx == 18) || (cmd->cmdidx == 17))) { unsigned tmode = priv->timing_mode; int work_mode = uboot_spare_head.boot_data.work_mode; static int i = 1; if (work_mode != WORK_MODE_BOOT || (mmc->do_tuning == 0x1 && mmc->tuning_end == 0x0) || (mmc->cfg->sample_mode != AUTO_SAMPLE_MODE)) { } else { printf("===========There is read opration!========\n"); if ((i < 4) && (tmode == HS400)) { u32 rval = 0; rval = readl(&priv->reg->samp_dl); rval &= (~SDXC_CfgDly); rval |= ((53 & SDXC_CfgDly) | SDXC_EnableDly); #ifdef FPGA_PLATFORM rval &= (~0x7); #endif writel(rval, &priv->reg->samp_dl); MMCINFO("samp %x\n", readl(&priv->reg->samp_dl)); i++; } else MMCINFO("end mauanl failed%d\n", i); } } #endif if (data) { if ((u32)(long)data->dest & 0x3) { error = -1; MMCINFO("%s,%d,dest is not 4 aligned\n", __FUNCTION__, __LINE__); goto out; } cmdval |= SUNXI_MMC_CMD_DATA_EXPIRE|SUNXI_MMC_CMD_WAIT_PRE_OVER; if (data->flags & MMC_DATA_WRITE) cmdval |= SUNXI_MMC_CMD_WRITE; if (data->blocks > 1) cmdval |= SUNXI_MMC_CMD_AUTO_STOP; writel(data->blocksize, &priv->reg->blksz); writel(data->blocks * data->blocksize, &priv->reg->bytecnt); } else { if (cmd->cmdidx == 12 && mmc->manual_stop_flag == 1) { /*stop current data transferin progress.*/ cmdval |= SUNXI_MMC_CMD_STOP_ABORT; /*Send command at once, even if previous data transfer has not completed*/ cmdval &= ~SUNXI_MMC_CMD_WAIT_PRE_OVER; } } MMCDBG("mmc %d, cmd %d(0x%08x), arg 0x%08x\n", priv->mmc_no, cmd->cmdidx, cmdval | cmd->cmdidx, cmd->cmdarg); writel(cmd->cmdarg, &priv->reg->arg); if (!data) writel(cmdval | cmd->cmdidx, &priv->reg->cmd); /* * transfer data and check status * STATREG[2] : FIFO empty * STATREG[3] : FIFO full */ if (data) { int ret = 0; /*dto set to 200ms*/ sunxi_mmc_set_rdtmout_reg(priv, mmc, DTO_MAX); bytecnt = data->blocksize * data->blocks; MMCDBG("trans data %d bytes\n", bytecnt); #ifdef CONFIG_MMC_SUNXI_USE_DMA if (bytecnt > 64) { #else if (0) { #endif usedma = 1; writel(readl(&priv->reg->gctrl) & (~SUNXI_MMC_GCTRL_ACCESS_BY_AHB), &priv->reg->gctrl); ret = mmc_trans_data_by_dma(priv, mmc, data); writel(cmdval | cmd->cmdidx, &priv->reg->cmd); } else { writel(readl(&priv->reg->gctrl) | SUNXI_MMC_GCTRL_ACCESS_BY_AHB, &priv->reg->gctrl); writel(cmdval | cmd->cmdidx, &priv->reg->cmd); ret = mmc_trans_data_by_cpu(priv, mmc, data); } if (ret) { error = readl(&priv->reg->rint) & SUNXI_MMC_RINT_INTERRUPT_ERROR_BIT; error = -ETIMEDOUT; goto out; } } error = mmc_rint_wait(priv, mmc, 1000, SUNXI_MMC_RINT_COMMAND_DONE, "cmd", usedma); if (error) { goto out; } if (data) { timeout_msecs = 6000; MMCDBG("cacl timeout %x msec\n", timeout_msecs); error = mmc_rint_wait(priv, mmc, timeout_msecs, data->blocks > 1 ? SUNXI_MMC_RINT_AUTO_COMMAND_DONE : SUNXI_MMC_RINT_DATA_OVER, "data", usedma); if (error) { goto out; } } if ((cmd->resp_type & MMC_RSP_BUSY) || ((data) && (data->flags & MMC_DATA_WRITE))) { unsigned long start = get_timer(0); if ((cmd->cmdidx == MMC_CMD_ERASE) || ((cmd->cmdidx == MMC_CMD_SWITCH) && (((cmd->cmdarg >> 16) & 0xFF) == EXT_CSD_SANITIZE_START))) timeout_msecs = 0x1fffffff; else timeout_msecs = 2000; do { status = readl(&priv->reg->status); if (get_timer(start) > timeout_msecs) { MMCDBG("busy timeout\n"); error = -ETIMEDOUT; goto out; } } while (status & SUNXI_MMC_STATUS_CARD_DATA_BUSY); if ((cmd->cmdidx == MMC_CMD_ERASE) || ((cmd->cmdidx == MMC_CMD_SWITCH) && (((cmd->cmdarg >> 16) & 0xFF) == EXT_CSD_SANITIZE_START))) MMCINFO("%s: cmd %d wait rsp busy 0x%x ms \n", __FUNCTION__, cmd->cmdidx, get_timer(start)); } if (cmd->resp_type & MMC_RSP_136) { cmd->response[0] = readl(&priv->reg->resp3); cmd->response[1] = readl(&priv->reg->resp2); cmd->response[2] = readl(&priv->reg->resp1); cmd->response[3] = readl(&priv->reg->resp0); MMCDBG("mmc resp 0x%08x 0x%08x 0x%08x 0x%08x\n", cmd->response[3], cmd->response[2], cmd->response[1], cmd->response[0]); } else { cmd->response[0] = readl(&priv->reg->resp0); MMCDBG("mmc resp 0x%08x\n", cmd->response[0]); } out: if (error) { priv->raw_int_bak = readl(&priv->reg->rint) & SUNXI_MMC_RINT_INTERRUPT_ERROR_BIT; mmc_dump_errinfo(priv, cmd); #if 0 if (cmd->cmdidx == 8) { dumphex32("mmc", (char *)priv->reg, 0x200); dumphex32("ccmu_mmc0", (char *)SUNXI_CCM_BASE + 0x830, 0x4); dumphex32("ccmu_pll", (char *)SUNXI_PRCM_BASE + 0x1010, 0x10); dumphex32("ccmu_bgr", (char *)SUNXI_CCM_BASE + 0x84C, 0x4); dumphex32("ccmu_srr", (char *)SUNXI_CCM_BASE + 0x84C, 0x4); dumphex32("gpio_config", (char *)SUNXI_PIO_BASE + 0x48, 0x10); dumphex32("gpio_pull", (char *)SUNXI_PIO_BASE + 0x64, 0x8); } #endif } if (data && usedma) { status = readl(&priv->reg->idst); writel(status, &priv->reg->idst); writel(0, &priv->reg->idie); writel(0, &priv->reg->dmac); writel(readl(&priv->reg->gctrl) & (~(1 << 5)), &priv->reg->gctrl); } if (error < 0) { /* during tuning sample point, some sample point may cause timing problem. for example, if a RTO error occurs, host may stop clock and device may still output data. we need to read all data(512bytes) from device to avoid to update clock fail. */ signed int timeout = 0; if (mmc->do_tuning && data && (data->flags&MMC_DATA_READ) && (bytecnt == 512)) { writel(readl(&priv->reg->gctrl)|0x80000000, &priv->reg->gctrl); writel(0xdeb, &priv->reg->dbgc); timeout = 1000; MMCMSG(mmc, "Read remain data\n"); while (readl(&priv->reg->bbcr) < 512) { unsigned int tmp = readl(priv->reg->fifo); tmp = tmp + 1; MMCDBG("Read data 0x%x, bbcr 0x%x\n", tmp, readl(&priv->reg->bbcr)); __usdelay(1); if (!(timeout--)) { MMCMSG(mmc, "Read remain data timeout\n"); break; } } } writel(0x7, &priv->reg->gctrl); while (readl(&priv->reg->gctrl)&0x7) { MMCDBG("mmc reset dma fifo and fifo\n"); }; { mmc_save_regs(priv); mmc_clk_io_onoff(priv->mmc_no, 0, 0); MMCMSG(mmc, "mmc %d close bus gating and reset\n", priv->mmc_no); mmc_clk_io_onoff(priv->mmc_no, 1, 0); mmc_restore_regs(priv); writel(0x7, &priv->reg->gctrl); while (readl(&priv->reg->gctrl)&0x7) { MMCDBG("mmc reset dma fifo and fifo\n"); }; } writel(SUNXI_MMC_GCTRL_RESET, &priv->reg->gctrl); mmc_update_clk(priv); } writel(0xffffffff, &priv->reg->rint); writel(readl(&priv->reg->gctrl) | SUNXI_MMC_GCTRL_FIFO_RESET, &priv->reg->gctrl); if (data && (data->flags&MMC_DATA_READ) && usedma) { unsigned char *buff = (unsigned char *)data->dest; unsigned byte_cnt = data->blocksize * data->blocks; invalidate_dcache_range((unsigned long)buff, ((unsigned long)buff + ALIGN((unsigned long)byte_cnt, CONFIG_SYS_CACHELINE_SIZE))); MMCDBG("invald cache after read complete\n"); } return error; } static int mmc_raw_send_manual_stop(struct mmc *mmc) { int ret = 0; struct mmc_cmd cmd; struct sunxi_mmc_priv *priv = mmc->priv; cmd.cmdidx = MMC_CMD_STOP_TRANSMISSION; cmd.cmdarg = 0; cmd.resp_type = MMC_RSP_R1b; mmc->manual_stop_flag = 1; ret = sunxi_mmc_do_send_cmd_common(priv, mmc, &cmd, NULL); if (ret) MMCINFO("bsp send manual stop failed\n"); return ret; } static int mmc_check_r1_ready(struct mmc *mmc, u32 timeout_us) { struct sunxi_mmc_priv *priv = mmc->priv; struct mmc_reg_v4p1 *reg = priv->reg; int error = 0; unsigned int status = 0; do { status = readl(®->status); if (!timeout_us--) { error = -1; MMCINFO("mmc %d check busy timeout %u\n", priv->mmc_no, timeout_us); goto out; } __usdelay(1); } while (status & (1 << 9)); out: MMCDBG("host bsp ready\n"); return error; } static int sunxi_mmc_send_cmd_common(struct sunxi_mmc_priv *priv, struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data) { int work_mode = uboot_spare_head.boot_data.work_mode; int err = 0; int has_reinit = 0; host_retry: err = sunxi_mmc_do_send_cmd_common(priv, mmc, cmd, data); if (work_mode != WORK_MODE_BOOT || (mmc->do_tuning == 0x1 && mmc->tuning_end == 0x0) || (mmc->cfg->sample_mode != AUTO_SAMPLE_MODE)) { return err; } if (err) { if (!has_reinit) { if (sunxi_need_rty(mmc)) { #if 0 MMCINFO("start reinit\n"); struct mmc_config *cfg = (struct mmc_config *) (mmc->cfg); mmc->has_init = 0; cfg->host_caps &= ~(MMC_MODE_HS400 | MMC_MODE_HS200 | MMC_MODE_DDR_52MHz); mmc->speed_mode = 0; mmc->cfg->ops->init(mmc); mmc_set_bus_width(mmc, 1); mmc_set_clock(mmc, 1, false); mmc_init(mmc); has_reinit = 1; goto host_retry; #else MMCINFO("give up reinit\n"); mmc->cfg->ops->decide_retry(mmc, 0, 1); return err; #endif } else { MMCINFO("host retry\n"); mmc_raw_send_manual_stop(mmc); mmc_check_r1_ready(mmc, 1000*1000); mmc->cfg->ops->set_ios(mmc); goto host_retry; } } else { MMCINFO("retry giveup!\n"); mmc->cfg->ops->decide_retry(mmc, 0, 1); } } else { MMCDBG("Reset retry cnt\n"); mmc->cfg->ops->decide_retry(mmc, 0, 1); } return err; } #if !CONFIG_IS_ENABLED(DM_MMC) static int sunxi_mmc_set_ios_legacy(struct mmc *mmc) { struct sunxi_mmc_priv *priv = mmc->priv; return sunxi_mmc_set_ios_common(priv, mmc); } static int sunxi_mmc_send_cmd_legacy(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data) { struct sunxi_mmc_priv *priv = mmc->priv; return sunxi_mmc_send_cmd_common(priv, mmc, cmd, data); } static int sunxi_mmc_getcd_legacy(struct mmc *mmc) { struct sunxi_mmc_priv *priv = mmc->priv; int cd_pin; cd_pin = sunxi_mmc_getcd_gpio(priv->mmc_no); if (cd_pin < 0) return 1; return !gpio_get_value(cd_pin); } static int sunxi_tm4_retry(struct mmc *mmc, u8 tm4_retry_gap, u8 type) { struct sunxi_mmc_priv *priv = (struct sunxi_mmc_priv *)mmc->priv; u32 spd_md, freq; u8 *sdly; spd_md = priv->tm4.cur_spd_md; freq = priv->tm4.cur_freq; if (type == 1) { sdly = &priv->tm4.sdly[spd_md*MAX_CLK_FREQ_NUM+freq]; MMCINFO("Current spd_md %d freq_id %d sdly %d\n", spd_md, freq, *sdly); priv->sd_retry_cnt++; if (priv->sd_retry_cnt * tm4_retry_gap < MMC_CLK_SAMPLE_POINIT_MODE_4) { if ((*sdly + tm4_retry_gap) < MMC_CLK_SAMPLE_POINIT_MODE_4) { *sdly = *sdly + tm4_retry_gap; } else { *sdly = *sdly + tm4_retry_gap - MMC_CLK_SAMPLE_POINIT_MODE_4; } MMCINFO("Get next samply point %d at spd_md %d freq_id %d\n", *sdly, spd_md, freq); } else { MMCINFO("Beyond the sdly retry times\n"); return -1; } } else { sdly = &priv->tm4.dsdly[freq]; MMCINFO("Current spd_md %d freq_id %d dsdly %d\n", spd_md, freq, *sdly); priv->dsd_retry_cnt++; if (priv->dsd_retry_cnt * tm4_retry_gap < MMC_CLK_SAMPLE_POINIT_MODE_4) { if ((*sdly + tm4_retry_gap) < MMC_CLK_SAMPLE_POINIT_MODE_4) { *sdly = *sdly + tm4_retry_gap; } else { *sdly = *sdly + tm4_retry_gap - MMC_CLK_SAMPLE_POINIT_MODE_4; } MMCINFO("Get next ds point %d at spd_md %d freq_id %d\n", *sdly, spd_md, freq); } else { MMCINFO("Beyond the dsdly retry times\n"); return -1; } } return 0; } static int sunxi_decide_rty(struct mmc *mmc, int err_no, uint rst_cnt) { struct sunxi_mmc_priv *priv = (struct sunxi_mmc_priv *)mmc->priv; unsigned tmode = priv->timing_mode; u32 spd_md, freq; u8 *sdly; u8 tm1_retry_gap = 1; u8 tm3_retry_gap = 8; #ifdef SUNXI_MMC_RETRY_TEST u8 tm4_retry_gap = 32; #else u8 tm4_retry_gap = 2; #endif if (rst_cnt) { priv->sd_retry_cnt = 0; priv->dsd_retry_cnt = 0; } if (err_no && (!(err_no & SDXC_RespTimeout) || (err_no == 0xffffffff))) { if (tmode == SUNXI_MMC_TIMING_MODE_1) { spd_md = priv->tm1.cur_spd_md; freq = priv->tm1.cur_freq; sdly = &priv->tm1.sdly[spd_md*MAX_CLK_FREQ_NUM+freq]; priv->sd_retry_cnt++; if (priv->sd_retry_cnt * tm1_retry_gap < MMC_CLK_SAMPLE_POINIT_MODE_1) { if ((*sdly + tm1_retry_gap) < MMC_CLK_SAMPLE_POINIT_MODE_1) { *sdly = *sdly + tm1_retry_gap; } else { *sdly = *sdly + tm1_retry_gap - MMC_CLK_SAMPLE_POINIT_MODE_1; } MMCINFO("Get next samply point %d at spd_md %d freq_id %d\n", *sdly, spd_md, freq); } else { MMCINFO("Beyond the retry times\n"); return -1; } } else if (tmode == SUNXI_MMC_TIMING_MODE_3) { spd_md = priv->tm3.cur_spd_md; freq = priv->tm3.cur_freq; sdly = &priv->tm3.sdly[spd_md*MAX_CLK_FREQ_NUM+freq]; priv->sd_retry_cnt++; if (priv->sd_retry_cnt * tm3_retry_gap < MMC_CLK_SAMPLE_POINIT_MODE_3) { if ((*sdly + tm3_retry_gap) < MMC_CLK_SAMPLE_POINIT_MODE_3) { *sdly = *sdly + tm3_retry_gap; } else { *sdly = *sdly + tm3_retry_gap - MMC_CLK_SAMPLE_POINIT_MODE_3; } MMCINFO("Get next samply point %d at spd_md %d freq_id %d\n", *sdly, spd_md, freq); } else { MMCINFO("Beyond the retry times\n"); return -1; } } else if (tmode == SUNXI_MMC_TIMING_MODE_4) { spd_md = priv->tm4.cur_spd_md; freq = priv->tm4.cur_freq; if (spd_md == HS400) { if ((err_no == 0xffffffff)) { if (sunxi_tm4_retry(mmc, tm4_retry_gap, 1)) { if (sunxi_tm4_retry(mmc, tm4_retry_gap, 0)) return -1; } } else if ((err_no & (SDXC_RespErr | SDXC_RespCRCErr))) { if (sunxi_tm4_retry(mmc, tm4_retry_gap, 1)) return -1; } else { if (sunxi_tm4_retry(mmc, tm4_retry_gap, 0)) return -1; } } else { if (sunxi_tm4_retry(mmc, tm4_retry_gap, 1)) return -1; } } priv->raw_int_bak = 0; return 0; } MMCDBG("rto or no error or software timeout,no need retry\n"); return -1; } static int sunxi_detail_errno(struct mmc *mmc) { struct sunxi_mmc_priv *priv = (struct sunxi_mmc_priv *)mmc->priv; u32 err_no = priv->raw_int_bak; priv->raw_int_bak = 0; return err_no; } static const struct mmc_ops sunxi_mmc_ops = { .send_cmd = sunxi_mmc_send_cmd_legacy, .set_ios = sunxi_mmc_set_ios_legacy, .init = sunxi_mmc_core_init, .getcd = sunxi_mmc_getcd_legacy, .decide_retry = sunxi_decide_rty, .get_detail_errno = sunxi_detail_errno, }; int sunxi_mmcno_to_devnum(int sdc_no) { struct mmc *m; struct sunxi_mmc_priv *ppriv = &mmc_host[sdc_no]; if (ppriv->mmc_no != sdc_no) { printk("error,card no error\n"); return -1; } // m = container_of((void *)ppriv, struct mmc, priv); m = ppriv->mmc; if (m == NULL) { printk("error card no error\n"); return -1; } MMCDBG("%s ,devnum %d\n", __FUNCTION__, m->block_dev.devnum); MMCDBG("devnum %d, pprv %x, bdesc %x\n", m->block_dev.devnum, PT_TO_PHU(ppriv), PT_TO_PHU(&m->block_dev)); MMCDBG("m %x, ppriv %x", PT_TO_PHU(m), PT_TO_PHU(ppriv)); return m->block_dev.devnum; } struct mmc *sunxi_mmc_init(int sdc_no) { __attribute__((unused)) struct sunxi_ccm_reg *ccm = (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; struct sunxi_mmc_priv *priv = &mmc_host[sdc_no]; struct mmc_config *cfg = &priv->cfg; int ret; int version; struct mmc *mmc = NULL; memset(priv, 0, sizeof(struct sunxi_mmc_priv)); MMCINFO("mmc driver ver %s\n", DRIVER_VER); memset(&mmc_host_reg_bak[sdc_no], 0, sizeof(struct mmc_reg_v4p1)); priv->reg_bak = &mmc_host_reg_bak[sdc_no]; if ((sdc_no == 2)) { cfg->odly_spd_freq = &ext_odly_spd_freq[0]; cfg->sdly_spd_freq = &ext_sdly_spd_freq[0]; } else if (sdc_no == 0) { cfg->odly_spd_freq = &ext_odly_spd_freq_sdc0[0]; cfg->sdly_spd_freq = &ext_sdly_spd_freq_sdc0[0]; } cfg->name = "SUNXI SD/MMC"; cfg->host_no = sdc_no; cfg->ops = &sunxi_mmc_ops; cfg->voltages = MMC_VDD_32_33 | MMC_VDD_33_34; cfg->host_caps |= MMC_MODE_HS_52MHz | MMC_MODE_HS | MMC_MODE_DDR_52MHz; cfg->b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT; /* default f_min and f_max */ cfg->f_min = 400000;/* 400K */ cfg->f_max = 50000000;/* 50M */ /* default timing mode */ priv->timing_mode = SUNXI_MMC_TIMING_MODE_1; priv->pdes = memalign(CONFIG_SYS_CACHELINE_SIZE, 256 * 1024); if (priv->pdes == NULL) { MMCINFO("get mem for descripter failed !\n"); return NULL; } else { MMCDBG("get mem for descripter OK !\n"); } if (sunxi_host_mmc_config(sdc_no) != 0) { MMCINFO("sunxi host mmc config failed!\n"); return NULL; } if (sunxi_mmc_pin_set(sdc_no) != 0) { MMCINFO("sunxi mmc pin set failed!\n"); return NULL; } if (mmc_resource_init(sdc_no) != 0) { MMCINFO("mmc resourse init failed!\n"); return NULL; } mmc_clk_io_onoff(sdc_no, 1, 1); version = readl(&priv->reg->vers); MMCINFO("SUNXI SDMMC Controller Version:0x%x\n", version); priv->version = version; if (cfg->io_is_1v8) { MMCDBG("io is 1.8V\n"); cfg->host_caps |= MMC_MODE_HS200; if (cfg->host_caps & MMC_MODE_8BIT) cfg->host_caps |= MMC_MODE_HS400; } if (cfg->host_caps_mask) { u32 mask = cfg->host_caps_mask; if (mask & DRV_PARA_DISABLE_MMC_MODE_HS400) cfg->host_caps &= (~MMC_MODE_HS400); if (mask & DRV_PARA_DISABLE_MMC_MODE_HS200) cfg->host_caps &= (~(MMC_MODE_HS200 | MMC_MODE_HS400)); if (mask & DRV_PARA_DISABLE_MMC_MODE_DDR_52MHz) cfg->host_caps &= (~(MMC_MODE_DDR_52MHz | MMC_MODE_HS400 | MMC_MODE_HS200)); if (mask & DRV_PARA_DISABLE_MMC_MODE_HS_52MHz) cfg->host_caps &= (~(MMC_MODE_HS_52MHz | MMC_MODE_DDR_52MHz | MMC_MODE_HS400 | MMC_MODE_HS200)); if (mask & DRV_PARA_DISABLE_MMC_MODE_8BIT) cfg->host_caps &= (~(MMC_MODE_8BIT | MMC_MODE_HS400)); if (mask & DRV_PARA_ENABLE_EMMC_HW_RST) cfg->host_caps |= DRV_PARA_ENABLE_EMMC_HW_RST; } MMCDBG("host_caps:0x%x\n", cfg->host_caps); #ifdef FPGA_PLATFORM int i = 0; if (sdc_no == 0) { for (i = 0; i < 6; i++) { sunxi_gpio_set_cfgpin(SUNXI_GPF(i), SUNXI_GPF_SDC0); sunxi_gpio_set_pull(SUNXI_GPF(i), 1); sunxi_gpio_set_drv(SUNXI_GPF(i), 2); } } else { unsigned int pin; for (pin = SUNXI_GPC(0); pin <= SUNXI_GPF(25); pin++) { sunxi_gpio_set_cfgpin(pin, 2); sunxi_gpio_set_pull(pin, SUNXI_GPIO_PULL_UP); sunxi_gpio_set_drv(pin, 2); } } #endif /* config ahb clock */ MMCDBG("init mmc %d clock and io\n", sdc_no); #if !defined(CONFIG_MACH_SUN50I_H6) && !defined(CONFIG_MACH_SUN8IW16)\ && !defined(CONFIG_MACH_SUN50IW9) && !defined(CONFIG_MACH_SUN8IW19)\ && !defined(CONFIG_MACH_SUN50IW10) && !defined(CONFIG_MACH_SUN8IW15)\ && !defined(CONFIG_MACH_SUN50IW11) && !defined(CONFIG_MACH_SUN50IW12)\ && !defined(CONFIG_MACH_SUN20IW1) && !defined(CONFIG_MACH_SUN8IW20)\ && !defined(CONFIG_MACH_SUN8IW21) && !defined(CONFIG_MACH_SUN50IW5)\ && !defined(CONFIG_MACH_SUN55IW3) && !defined(CONFIG_MACH_SUN60IW1) setbits_le32(&ccm->ahb_gate0, 1 << AHB_GATE_OFFSET_MMC(sdc_no)); #ifdef CONFIG_SUNXI_GEN_SUN6I /* unassert reset */ setbits_le32(&ccm->ahb_reset0_cfg, 1 << AHB_RESET_OFFSET_MMC(sdc_no)); #endif #if defined(CONFIG_MACH_SUN9I) /* sun9i has a mmc-common module, also set the gate and reset there */ writel(SUNXI_MMC_COMMON_CLK_GATE | SUNXI_MMC_COMMON_RESET, SUNXI_MMC_COMMON_BASE + 4 * sdc_no); #endif #elif defined CONFIG_MACH_SUN50I_H6 /* CONFIG_MACH_SUN50I_H6 */ setbits_le32(&ccm->sd_gate_reset, 1 << sdc_no); /* unassert reset */ setbits_le32(&ccm->sd_gate_reset, 1 << (RESET_SHIFT + sdc_no)); #endif mmc = mmc_create(cfg, priv); if (!mmc) return NULL; ret = mmc_set_mod_clk(priv, 24000000); if (ret) return NULL; return mmc; } #else static int sunxi_mmc_set_ios(struct udevice *dev) { struct sunxi_mmc_plat *plat = dev_get_platdata(dev); struct sunxi_mmc_priv *priv = dev_get_priv(dev); return sunxi_mmc_set_ios_common(priv, &plat->mmc); } static int sunxi_mmc_send_cmd(struct udevice *dev, struct mmc_cmd *cmd, struct mmc_data *data) { struct sunxi_mmc_plat *plat = dev_get_platdata(dev); struct sunxi_mmc_priv *priv = dev_get_priv(dev); return sunxi_mmc_send_cmd_common(priv, &plat->mmc, cmd, data); } static int sunxi_mmc_getcd(struct udevice *dev) { struct sunxi_mmc_priv *priv = dev_get_priv(dev); if (dm_gpio_is_valid(&priv->cd_gpio)) { int cd_state = dm_gpio_get_value(&priv->cd_gpio); return cd_state ^ priv->cd_inverted; } return 1; } static const struct dm_mmc_ops sunxi_mmc_ops = { .send_cmd = sunxi_mmc_send_cmd, .set_ios = sunxi_mmc_set_ios, .get_cd = sunxi_mmc_getcd, }; static int sunxi_mmc_probe(struct udevice *dev) { struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev); struct sunxi_mmc_plat *plat = dev_get_platdata(dev); struct sunxi_mmc_priv *priv = dev_get_priv(dev); struct mmc_config *cfg = &plat->cfg; struct ofnode_phandle_args args; u32 *gate_reg; int bus_width, ret; cfg->name = dev->name; bus_width = dev_read_u32_default(dev, "bus-width", 1); cfg->voltages = MMC_VDD_32_33 | MMC_VDD_33_34; cfg->host_caps = 0; if (bus_width == 8) cfg->host_caps |= MMC_MODE_8BIT; if (bus_width >= 4) cfg->host_caps |= MMC_MODE_4BIT; cfg->host_caps |= MMC_MODE_HS_52MHz | MMC_MODE_HS; cfg->b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT; cfg->f_min = 400000; cfg->f_max = 52000000; priv->reg = (void *)dev_read_addr(dev); /* We don't have a sunxi clock driver so find the clock address here */ ret = dev_read_phandle_with_args(dev, "clocks", "#clock-cells", 0, 1, &args); if (ret) return ret; priv->mclkreg = (u32 *)ofnode_get_addr(args.node); ret = dev_read_phandle_with_args(dev, "clocks", "#clock-cells", 0, 0, &args); if (ret) return ret; gate_reg = (u32 *)ofnode_get_addr(args.node); setbits_le32(gate_reg, 1 << args.args[0]); priv->mmc_no = args.args[0] - 8; ret = mmc_set_mod_clk(priv, 24000000); if (ret) return ret; /* This GPIO is optional */ if (!gpio_request_by_name(dev, "cd-gpios", 0, &priv->cd_gpio, GPIOD_IS_IN)) { int cd_pin = gpio_get_number(&priv->cd_gpio); sunxi_gpio_set_pull(cd_pin, SUNXI_GPIO_PULL_UP); } /* Check if card detect is inverted */ priv->cd_inverted = dev_read_bool(dev, "cd-inverted"); upriv->mmc = &plat->mmc; /* Reset controller */ writel(SUNXI_MMC_GCTRL_RESET, &priv->reg->gctrl); udelay(1000); return 0; } static int sunxi_mmc_bind(struct udevice *dev) { struct sunxi_mmc_plat *plat = dev_get_platdata(dev); return mmc_bind(dev, &plat->mmc, &plat->cfg); } static const struct udevice_id sunxi_mmc_ids[] = { { .compatible = "allwinner,sun5i-a13-mmc" }, { } }; U_BOOT_DRIVER(sunxi_mmc_drv) = { .name = "sunxi_mmc", .id = UCLASS_MMC, .of_match = sunxi_mmc_ids, .bind = sunxi_mmc_bind, .probe = sunxi_mmc_probe, .ops = &sunxi_mmc_ops, .platdata_auto_alloc_size = sizeof(struct sunxi_mmc_plat), .priv_auto_alloc_size = sizeof(struct sunxi_mmc_priv), }; #endif