sdk-hwV1.3/lichee/brandy-2.0/u-boot-2018/drivers/mmc/sunxi_mmc.c

1577 lines
46 KiB
C

// SPDX-License-Identifier: GPL-2.0+
/*
* (C) Copyright 2007-2011
* Allwinner Technology Co., Ltd. <www.allwinnertech.com>
* Aaron <leafy.myeh@allwinnertech.com>
*
* MMC driver for allwinner sunxi platform.
*/
#include <common.h>
#include <dm.h>
#include <errno.h>
#include <malloc.h>
#include <mmc.h>
#include <asm/io.h>
#include <asm/arch/clock.h>
#include <asm/arch/timer.h>
#include <asm/arch/cpu.h>
#include <asm/arch/gpio.h>
#include <private_uboot.h>
#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(&reg->gctrl);
reg_bak->clkcr = readl(&reg->clkcr);
reg_bak->timeout = readl(&reg->timeout);
reg_bak->width = readl(&reg->width);
reg_bak->imask = readl(&reg->imask);
reg_bak->ftrglevel = readl(&reg->ftrglevel);
reg_bak->dbgc = readl(&reg->dbgc);
reg_bak->ntsr = readl(&reg->ntsr);
reg_bak->hwrst = readl(&reg->hwrst);
reg_bak->dmac = readl(&reg->dmac);
reg_bak->idie = readl(&reg->idie);
reg_bak->thldc = readl(&reg->thldc);
reg_bak->dsbd = readl(&reg->dsbd);
#if (!defined(CONFIG_MACH_SUN8IW7))
reg_bak->csdc = readl(&reg->csdc);
reg_bak->drv_dl = readl(&reg->drv_dl);
reg_bak->samp_dl = readl(&reg->samp_dl);
reg_bak->ds_dl = readl(&reg->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, &reg->gctrl);
writel(reg_bak->clkcr, &reg->clkcr);
writel(reg_bak->timeout, &reg->timeout);
writel(reg_bak->width, &reg->width);
writel(reg_bak->imask, &reg->imask);
writel(reg_bak->ftrglevel, &reg->ftrglevel);
if (reg_bak->dbgc)
writel(0xdeb, &reg->dbgc);
writel(reg_bak->ntsr, &reg->ntsr);
writel(reg_bak->hwrst, &reg->hwrst);
writel(reg_bak->dmac, &reg->dmac);
writel(reg_bak->idie, &reg->idie);
writel(reg_bak->thldc, &reg->thldc);
writel(reg_bak->dsbd, &reg->dsbd);
#if (!defined(CONFIG_MACH_SUN8IW7))
writel(reg_bak->csdc, &reg->csdc);
sunxi_r_op(mmchost, writel(reg_bak->drv_dl, &reg->drv_dl));
writel(reg_bak->samp_dl, &reg->samp_dl);
writel(reg_bak->ds_dl, &reg->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(&reg->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