TERES/SOFTWARE/A64-TERES/u-boot_new/drivers/mmc/sunxi_mmc.c
Dimitar Gamishev 093685c7d8 u-boot
2017-10-13 14:02:55 +03:00

2445 lines
75 KiB
C

/*
* (C) Copyright 2007-2011
* Allwinner Technology Co., Ltd. <www.allwinnertech.com>
* Aaron <leafy.myeh@allwinnertech.com>
*
* MMC driver for allwinner sunxi platform.
*
* See file CREDITS for list of people who contributed to this
* project.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/
#include <common.h>
#include <asm/io.h>
#include <asm/arch/platform.h>
#include <asm/arch/clock.h>
#include <asm/arch/ccmu.h>
#include <asm/arch/mmc.h>
#include <asm/arch/timer.h>
#include <malloc.h>
#include <mmc.h>
#include <sys_config.h>
#include <libfdt.h>
#include <fdt_support.h>
#include "mmc_def.h"
#include "sunxi_mmc.h"
#include <fdt_support.h>
#undef readl
#define readl(a) *(volatile uint *)(ulong)(a)
#undef writel
#define writel(v, c) *(volatile uint *)(ulong)(c) = (v)
DECLARE_GLOBAL_DATA_PTR;
//#define SUNXI_MMCDBG
//#undef SUNXI_MMCDBG
//#define MMCINFO(fmt...) printf("[mmc]: "fmt)
#ifndef CONFIG_ARCH_SUN7I
#define MMC_REG_FIFO_OS (0x200)
#else
#define MMC_REG_FIFO_OS (0x100)
#endif
#ifdef SUNXI_MMCDBG
//#define MMCDBG(fmt...) printf("[mmc]: "fmt)
static void dumphex32(char* name, char* base, int len)
{
__u32 i;
MMCPRINT("dump %s registers:", name);
for (i=0; i<len; i+=4) {
if (!(i&0xf))
MMCPRINT("\n0x%p : ", base + i);
MMCPRINT("0x%08x ", readl((ulong)base + i));
}
MMCPRINT("\n");
}
/*
static void dumpmmcreg(struct sunxi_mmc *reg)
{
printf("dump mmc registers:\n");
printf("gctrl 0x%08x\n", reg->gctrl );
printf("clkcr 0x%08x\n", reg->clkcr );
printf("timeout 0x%08x\n", reg->timeout );
printf("width 0x%08x\n", reg->width );
printf("blksz 0x%08x\n", reg->blksz );
printf("bytecnt 0x%08x\n", reg->bytecnt );
printf("cmd 0x%08x\n", reg->cmd );
printf("arg 0x%08x\n", reg->arg );
printf("resp0 0x%08x\n", reg->resp0 );
printf("resp1 0x%08x\n", reg->resp1 );
printf("resp2 0x%08x\n", reg->resp2 );
printf("resp3 0x%08x\n", reg->resp3 );
printf("imask 0x%08x\n", reg->imask );
printf("mint 0x%08x\n", reg->mint );
printf("rint 0x%08x\n", reg->rint );
printf("status 0x%08x\n", reg->status );
printf("ftrglevel 0x%08x\n", reg->ftrglevel );
printf("funcsel 0x%08x\n", reg->funcsel );
printf("dmac 0x%08x\n", reg->dmac );
printf("dlba 0x%08x\n", reg->dlba );
printf("idst 0x%08x\n", reg->idst );
printf("idie 0x%08x\n", reg->idie );
printf("cbcr 0x%08x\n", reg->cbcr );
printf("bbcr 0x%08x\n", reg->bbcr );
}
*/
#else
//#define MMCDBG(fmt...)
#define dumpmmcreg(fmt...)
#define dumphex32(fmt...)
#endif /* SUNXI_MMCDBG */
#define BIT(x) (1<<(x))
/* Struct for Intrrrupt Information */
#define SDXC_RespErr BIT(1) //0x2
#define SDXC_CmdDone BIT(2) //0x4
#define SDXC_DataOver BIT(3) //0x8
#define SDXC_TxDataReq BIT(4) //0x10
#define SDXC_RxDataReq BIT(5) //0x20
#define SDXC_RespCRCErr BIT(6) //0x40
#define SDXC_DataCRCErr BIT(7) //0x80
#define SDXC_RespTimeout BIT(8) //0x100
#define SDXC_ACKRcv BIT(8) //0x100
#define SDXC_DataTimeout BIT(9) //0x200
#define SDXC_BootStart BIT(9) //0x200
#define SDXC_DataStarve BIT(10) //0x400
#define SDXC_VolChgDone BIT(10) //0x400
#define SDXC_FIFORunErr BIT(11) //0x800
#define SDXC_HardWLocked BIT(12) //0x1000
#define SDXC_StartBitErr BIT(13) //0x2000
#define SDXC_AutoCMDDone BIT(14) //0x4000
#define SDXC_EndBitErr BIT(15) //0x8000
#define SDXC_SDIOInt BIT(16) //0x10000
#define SDXC_CardInsert BIT(30) //0x40000000
#define SDXC_CardRemove BIT(31) //0x80000000
#define SDXC_IntErrBit (SDXC_RespErr | SDXC_RespCRCErr | SDXC_DataCRCErr \
| SDXC_RespTimeout | SDXC_DataTimeout | SDXC_FIFORunErr \
| SDXC_HardWLocked | SDXC_StartBitErr | SDXC_EndBitErr) //0xbfc2
/* IDMA status bit field */
#define SDXC_IDMACTransmitInt BIT(0)
#define SDXC_IDMACReceiveInt BIT(1)
#define SDXC_IDMACFatalBusErr BIT(2)
#define SDXC_IDMACDesInvalid BIT(4)
#define SDXC_IDMACCardErrSum BIT(5)
#define SDXC_IDMACNormalIntSum BIT(8)
#define SDXC_IDMACAbnormalIntSum BIT(9)
#define SDXC_IDMACHostAbtInTx BIT(10)
#define SDXC_IDMACHostAbtInRx BIT(10)
#define SDXC_IDMACIdle (0U << 13)
#define SDXC_IDMACSuspend (1U << 13)
#define SDXC_IDMACDESCRd (2U << 13)
#define SDXC_IDMACDESCCheck (3U << 13)
#define SDXC_IDMACRdReqWait (4U << 13)
#define SDXC_IDMACWrReqWait (5U << 13)
#define SDXC_IDMACRd (6U << 13)
#define SDXC_IDMACWr (7U << 13)
#define SDXC_IDMACDESCClose (8U << 13)
/* delay control */
#define SDXC_StartCal (1<<15)
#define SDXC_CalDone (1<<14)
#define SDXC_CalDly (0x3F<<8)
#define SDXC_EnableDly (1<<7)
#define SDXC_CfgDly (0x3F<<0)
#define MMC_CLK_400K 0
#define MMC_CLK_25M 1
#define MMC_CLK_50M 2
#define MMC_CLK_50MDDR 3
#define MMC_CLK_50MDDR_8BIT 4
#define MMC_CLK_100M 5
#define MMC_CLK_200M 6
#define MMC_CLK_MOD_NUM 7
extern char *spd_name[];
/* support 4 mmc hosts */
struct sunxi_mmc_host mmc_host[4];
struct sunxi_mmc mmc_host_reg_bak[4];
u8 ext_odly_spd_freq[MAX_SPD_MD_NUM*MAX_CLK_FREQ_NUM];
u8 ext_sdly_spd_freq[MAX_SPD_MD_NUM*MAX_CLK_FREQ_NUM];
u8 ext_odly_spd_freq_sdc0[MAX_SPD_MD_NUM*MAX_CLK_FREQ_NUM];
u8 ext_sdly_spd_freq_sdc0[MAX_SPD_MD_NUM*MAX_CLK_FREQ_NUM];
void mmc_dump_errinfo(struct sunxi_mmc_host* smc_host, struct mmc_cmd *cmd)
{
MMCMSG(smc_host->mmc, "smc %d err, cmd %d, %s%s%s%s%s%s%s%s%s%s%s\n",
smc_host->mmc_no, cmd? cmd->cmdidx: -1,
smc_host->raw_int_bak & SDXC_RespErr ? " RE" : "",
smc_host->raw_int_bak & SDXC_RespCRCErr ? " RCE" : "",
smc_host->raw_int_bak & SDXC_DataCRCErr ? " DCE" : "",
smc_host->raw_int_bak & SDXC_RespTimeout ? " RTO" : "",
smc_host->raw_int_bak & SDXC_DataTimeout ? " DTO" : "",
smc_host->raw_int_bak & SDXC_DataStarve ? " DS" : "",
smc_host->raw_int_bak & SDXC_FIFORunErr ? " FE" : "",
smc_host->raw_int_bak & SDXC_HardWLocked ? " HL" : "",
smc_host->raw_int_bak & SDXC_StartBitErr ? " SBE" : "",
smc_host->raw_int_bak & SDXC_EndBitErr ? " EBE" : "",
smc_host->raw_int_bak ==0 ? " STO" : ""
);
}
static int mmc_clear_timing_para(int sdc_no)
{
struct sunxi_mmc_host* mmchost = &mmc_host[sdc_no];
int i, j;
for (i=0; i<MAX_SPD_MD_NUM; i++)
{
for (j=0; j<MAX_CLK_FREQ_NUM; j++)
{
mmchost->tm1.odly[i*MAX_CLK_FREQ_NUM+j] = 0xFF;
mmchost->tm1.sdly[i*MAX_CLK_FREQ_NUM+j] = 0xFF;
mmchost->tm1.def_odly[i*MAX_CLK_FREQ_NUM+j] = 0xFF;
mmchost->tm1.def_sdly[i*MAX_CLK_FREQ_NUM+j] = 0xFF;
mmchost->tm3.odly[i*MAX_CLK_FREQ_NUM+j] = 0xFF;
mmchost->tm3.sdly[i*MAX_CLK_FREQ_NUM+j] = 0xFF;
mmchost->tm3.def_odly[i*MAX_CLK_FREQ_NUM+j] = 0xFF;
mmchost->tm3.def_sdly[i*MAX_CLK_FREQ_NUM+j] = 0xFF;
mmchost->tm4.odly[i*MAX_CLK_FREQ_NUM+j] = 0xFF;
mmchost->tm4.sdly[i*MAX_CLK_FREQ_NUM+j] = 0xFF;
mmchost->tm4.def_odly[i*MAX_CLK_FREQ_NUM+j] = 0xFF;
mmchost->tm4.def_sdly[i*MAX_CLK_FREQ_NUM+j] = 0xFF;
}
}
for (j=0; j<MAX_CLK_FREQ_NUM; j++)
{
mmchost->tm4.dsdly[j] = 0xFF;
mmchost->tm4.def_dsdly[j] = 0xFF;
}
return 0;
}
static int mmc_init_default_timing_para(int sdc_no)
{
struct sunxi_mmc_host* mmchost = &mmc_host[sdc_no];
//int i, j;
if (sdc_no == 0)
{
/* timing mode 1 */
mmchost->tm1.cur_spd_md = DS26_SDR12;
mmchost->tm1.cur_freq = CLK_400K;
mmchost->tm1.sample_point_cnt = MMC_CLK_SAMPLE_POINIT_MODE_1;
mmchost->tm1.def_odly[DS26_SDR12*MAX_CLK_FREQ_NUM+CLK_400K] = TM1_OUT_PH180;
mmchost->tm1.def_sdly[DS26_SDR12*MAX_CLK_FREQ_NUM+CLK_400K] = TM1_IN_PH180;
mmchost->tm1.def_odly[DS26_SDR12*MAX_CLK_FREQ_NUM+CLK_25M] = TM1_OUT_PH180;
mmchost->tm1.def_sdly[DS26_SDR12*MAX_CLK_FREQ_NUM+CLK_25M] = TM1_IN_PH180;
mmchost->tm1.def_odly[HSSDR52_SDR25*MAX_CLK_FREQ_NUM+CLK_25M] = TM1_OUT_PH180;
mmchost->tm1.def_sdly[HSSDR52_SDR25*MAX_CLK_FREQ_NUM+CLK_25M] = TM1_IN_PH90;
mmchost->tm1.def_odly[HSSDR52_SDR25*MAX_CLK_FREQ_NUM+CLK_50M] = TM1_OUT_PH180;
mmchost->tm1.def_sdly[HSSDR52_SDR25*MAX_CLK_FREQ_NUM+CLK_50M] = TM1_IN_PH90;
mmchost->tm1.def_odly[HSDDR52_DDR50*MAX_CLK_FREQ_NUM+CLK_25M] = TM1_OUT_PH90;
mmchost->tm1.def_sdly[HSDDR52_DDR50*MAX_CLK_FREQ_NUM+CLK_25M] = TM1_IN_PH180;
mmchost->tm1.def_odly[HSDDR52_DDR50*MAX_CLK_FREQ_NUM+CLK_50M] = TM1_OUT_PH90;
mmchost->tm1.def_sdly[HSDDR52_DDR50*MAX_CLK_FREQ_NUM+CLK_50M] = TM1_IN_PH180;
mmchost->tm1.def_odly[HS200_SDR104*MAX_CLK_FREQ_NUM+CLK_25M] = TM1_OUT_PH180;
mmchost->tm1.def_sdly[HS200_SDR104*MAX_CLK_FREQ_NUM+CLK_25M] = TM1_IN_PH90;
mmchost->tm1.def_odly[HS200_SDR104*MAX_CLK_FREQ_NUM+CLK_50M] = TM1_OUT_PH180;
mmchost->tm1.def_sdly[HS200_SDR104*MAX_CLK_FREQ_NUM+CLK_50M] = TM1_IN_PH90;
/* timing mode 3 */
mmchost->tm3.cur_spd_md = DS26_SDR12;
mmchost->tm3.cur_freq = CLK_400K;
mmchost->tm3.sample_point_cnt = MMC_CLK_SAMPLE_POINIT_MODE_3;
mmchost->tm3.def_odly[DS26_SDR12*MAX_CLK_FREQ_NUM+CLK_400K] = TM3_OUT_PH180;
mmchost->tm3.def_sdly[DS26_SDR12*MAX_CLK_FREQ_NUM+CLK_400K] = 0x0;
mmchost->tm3.def_odly[DS26_SDR12*MAX_CLK_FREQ_NUM+CLK_25M] = TM3_OUT_PH180;
mmchost->tm3.def_sdly[DS26_SDR12*MAX_CLK_FREQ_NUM+CLK_25M] = 0x0;
mmchost->tm3.def_odly[HSSDR52_SDR25*MAX_CLK_FREQ_NUM+CLK_25M] = TM3_OUT_PH180;
mmchost->tm3.def_sdly[HSSDR52_SDR25*MAX_CLK_FREQ_NUM+CLK_25M] = 0x0;
mmchost->tm3.def_odly[HSSDR52_SDR25*MAX_CLK_FREQ_NUM+CLK_50M] = TM3_OUT_PH180;
mmchost->tm3.def_sdly[HSSDR52_SDR25*MAX_CLK_FREQ_NUM+CLK_50M] = 0x0;
mmchost->tm3.def_odly[HSDDR52_DDR50*MAX_CLK_FREQ_NUM+CLK_25M] = TM1_OUT_PH90;
mmchost->tm3.def_sdly[HSDDR52_DDR50*MAX_CLK_FREQ_NUM+CLK_25M] = 0x0;
mmchost->tm3.def_odly[HSDDR52_DDR50*MAX_CLK_FREQ_NUM+CLK_50M] = TM1_OUT_PH90;
mmchost->tm3.def_sdly[HSDDR52_DDR50*MAX_CLK_FREQ_NUM+CLK_50M] = 0x0;
mmchost->tm3.def_odly[HS200_SDR104*MAX_CLK_FREQ_NUM+CLK_25M] = TM3_OUT_PH180;
mmchost->tm3.def_sdly[HS200_SDR104*MAX_CLK_FREQ_NUM+CLK_25M] = 0x0;
mmchost->tm3.def_odly[HS200_SDR104*MAX_CLK_FREQ_NUM+CLK_50M] = TM3_OUT_PH180;
mmchost->tm3.def_sdly[HS200_SDR104*MAX_CLK_FREQ_NUM+CLK_50M] = 0x0;
} else if (sdc_no == 2) {
/* timing mode 4 */
mmchost->tm4.cur_spd_md = DS26_SDR12;
mmchost->tm4.cur_freq = CLK_400K;
mmchost->tm4.sample_point_cnt = MMC_CLK_SAMPLE_POINIT_MODE_4;
mmchost->tm4.def_odly[DS26_SDR12*MAX_CLK_FREQ_NUM+CLK_400K] = TM4_OUT_PH180;
mmchost->tm4.def_sdly[DS26_SDR12*MAX_CLK_FREQ_NUM+CLK_400K] = 0x0;
mmchost->tm4.def_odly[DS26_SDR12*MAX_CLK_FREQ_NUM+CLK_25M] = TM4_OUT_PH180;
mmchost->tm4.def_sdly[DS26_SDR12*MAX_CLK_FREQ_NUM+CLK_25M] = 0x0;
mmchost->tm4.def_odly[HSSDR52_SDR25*MAX_CLK_FREQ_NUM+CLK_400K] = TM4_OUT_PH180;
mmchost->tm4.def_sdly[HSSDR52_SDR25*MAX_CLK_FREQ_NUM+CLK_400K] = 0;
mmchost->tm4.def_odly[HSSDR52_SDR25*MAX_CLK_FREQ_NUM+CLK_25M] = TM4_OUT_PH180;
mmchost->tm4.def_sdly[HSSDR52_SDR25*MAX_CLK_FREQ_NUM+CLK_25M] = 0;
mmchost->tm4.def_odly[HSSDR52_SDR25*MAX_CLK_FREQ_NUM+CLK_50M] = TM4_OUT_PH180;
mmchost->tm4.def_sdly[HSSDR52_SDR25*MAX_CLK_FREQ_NUM+CLK_50M] = 0;
mmchost->tm4.def_odly[HSDDR52_DDR50*MAX_CLK_FREQ_NUM+CLK_400K] = TM1_OUT_PH90;
mmchost->tm4.def_sdly[HSDDR52_DDR50*MAX_CLK_FREQ_NUM+CLK_400K] = 0xe;
mmchost->tm4.def_odly[HSDDR52_DDR50*MAX_CLK_FREQ_NUM+CLK_25M] = TM1_OUT_PH90;
mmchost->tm4.def_sdly[HSDDR52_DDR50*MAX_CLK_FREQ_NUM+CLK_25M] = 0xe;
mmchost->tm4.def_odly[HSDDR52_DDR50*MAX_CLK_FREQ_NUM+CLK_50M] = TM1_OUT_PH90;
mmchost->tm4.def_sdly[HSDDR52_DDR50*MAX_CLK_FREQ_NUM+CLK_50M] = 0xe;
mmchost->tm4.def_odly[HS200_SDR104*MAX_CLK_FREQ_NUM+CLK_25M] = TM4_OUT_PH180;
mmchost->tm4.def_sdly[HS200_SDR104*MAX_CLK_FREQ_NUM+CLK_25M] = 0x0;
mmchost->tm4.def_odly[HS200_SDR104*MAX_CLK_FREQ_NUM+CLK_50M] = TM4_OUT_PH180;
mmchost->tm4.def_sdly[HS200_SDR104*MAX_CLK_FREQ_NUM+CLK_50M] = 0x11;
mmchost->tm4.def_odly[HS200_SDR104*MAX_CLK_FREQ_NUM+CLK_100M] = TM4_OUT_PH180;
mmchost->tm4.def_sdly[HS200_SDR104*MAX_CLK_FREQ_NUM+CLK_100M] = 0x12;
mmchost->tm4.def_odly[HS200_SDR104*MAX_CLK_FREQ_NUM+CLK_150M] = TM4_OUT_PH180;
mmchost->tm4.def_sdly[HS200_SDR104*MAX_CLK_FREQ_NUM+CLK_150M] = 0x13;
mmchost->tm4.def_odly[HS200_SDR104*MAX_CLK_FREQ_NUM+CLK_200M] = TM4_OUT_PH180;
mmchost->tm4.def_sdly[HS200_SDR104*MAX_CLK_FREQ_NUM+CLK_200M] = 0x6;
mmchost->tm4.def_odly[HS400*MAX_CLK_FREQ_NUM+CLK_25M] = TM4_OUT_PH90;
mmchost->tm4.def_sdly[HS400*MAX_CLK_FREQ_NUM+CLK_25M] = 0x0;
mmchost->tm4.def_odly[HS400*MAX_CLK_FREQ_NUM+CLK_50M] = TM4_OUT_PH90;
mmchost->tm4.def_sdly[HS400*MAX_CLK_FREQ_NUM+CLK_50M] = 0x11;
mmchost->tm4.def_odly[HS400*MAX_CLK_FREQ_NUM+CLK_100M] = TM4_OUT_PH90;
mmchost->tm4.def_sdly[HS400*MAX_CLK_FREQ_NUM+CLK_100M] = 0x12;
mmchost->tm4.def_odly[HS400*MAX_CLK_FREQ_NUM+CLK_150M] = TM4_OUT_PH90;
mmchost->tm4.def_sdly[HS400*MAX_CLK_FREQ_NUM+CLK_150M] = 0x13;
mmchost->tm4.def_odly[HS400*MAX_CLK_FREQ_NUM+CLK_200M] = TM4_OUT_PH90;
mmchost->tm4.def_sdly[HS400*MAX_CLK_FREQ_NUM+CLK_200M] = 0x6;
mmchost->tm4.def_dsdly[CLK_25M] = 0x0;
mmchost->tm4.def_dsdly[CLK_50M] = 0x18;
mmchost->tm4.def_dsdly[CLK_100M] = 0xd;
mmchost->tm4.def_dsdly[CLK_150M] = 0x6;
mmchost->tm4.def_dsdly[CLK_200M] = 0x3;
}
#if 0
printf("0x%x 0x%x 0x%x 0x%x\n", (u32)mmchost->tm4.def_odly, (u32)mmchost->tm4.def_sdly, (u32)mmchost->tm4.sdly, (u32)mmchost->tm4.odly);
for (j=0; j<MAX_SPD_MD_NUM; j++)
{
for (i=0; i<MAX_CLK_FREQ_NUM; i++)
{
printf("%02x ", mmchost->tm4.def_odly[j*MAX_CLK_FREQ_NUM + i]);
}
printf("\n");
}
printf("\n\n");
for (j=0; j<MAX_SPD_MD_NUM; j++)
{
for (i=0; i<MAX_CLK_FREQ_NUM; i++)
{
printf("%02x ", mmchost->tm4.def_sdly[j*MAX_CLK_FREQ_NUM + i]);
}
printf("\n");
}
printf("\n");
#endif
return 0;
}
static int mmc_resource_init(int sdc_no)
{
struct sunxi_mmc_host* mmchost = &mmc_host[sdc_no];
MMCDBG("init mmc %d resource\n", sdc_no);
switch (sdc_no) {
case 0:
mmchost->reg = (struct sunxi_mmc *)SUNXI_SMHC0_BASE;
mmchost->database = SUNXI_SMHC0_BASE + MMC_REG_FIFO_OS;
mmchost->mclkbase = CCMU_SDMMC0_CLK_REG;
break;
case 1:
mmchost->reg = (struct sunxi_mmc *)SUNXI_SMHC1_BASE;
mmchost->database = SUNXI_SMHC1_BASE + MMC_REG_FIFO_OS;
mmchost->mclkbase = CCMU_SDMMC1_CLK_REG;
break;
case 2:
mmchost->reg = (struct sunxi_mmc *)SUNXI_SMHC2_BASE;
mmchost->database = SUNXI_SMHC2_BASE + MMC_REG_FIFO_OS;
mmchost->mclkbase = CCMU_SDMMC2_CLK_REG;
break;
default:
MMCINFO("Wrong mmc number %d\n", sdc_no);
break;
}
mmchost->hclkbase = CCMU_BUS_CLK_GATING_REG0;
mmchost->hclkrst = CCMU_BUS_SOFT_RST_REG0;
mmchost->mmc_no = sdc_no;
return 0;
}
static int mmc_get_sdly_auto_sample(int sdc_no)
{
struct sunxi_mmc_host* mmchost = &mmc_host[sdc_no];
u32 *p = (u32 *)&mmchost->cfg.platform_caps.sdly.tm4_smx_fx[0];
int spd_md, f;
u32 val;
int work_mode = uboot_spare_head.boot_data.work_mode;
if (sdc_no != 2) {
MMCINFO("don't support auto sample\n");
return -1;
}
/* sdly is invalid */
if (work_mode != WORK_MODE_BOOT)
return 0;
#if 0
for (f=0; f<5; f++)
MMCINFO("0x%x 0x%x\n", p[f*2 + 0], p[f*2 + 1]);
#endif
for (spd_md=0; spd_md<MAX_SPD_MD_NUM; spd_md++)
{
if (spd_md == HS400)
{
val = p[spd_md*2 + 0];
for (f=0; f<4; f++) {
mmchost->tm4.dsdly[f] = (val>>(f*8)) & 0xFF;
//MMCINFO("dsdly-0 0x%x\n", mmchost->tm4.dsdly[f]);
}
val = p[spd_md*2 + 1];
for (f=4; f<MAX_CLK_FREQ_NUM; f++) {
mmchost->tm4.dsdly[f] = (val>>((f-4)*8)) & 0xFF;
//MMCINFO("dsdly-1 0x%x\n", mmchost->tm4.dsdly[f]);
}
}
else
{
val = p[spd_md*2 + 0];
for (f=0; f<4; f++) {
mmchost->tm4.sdly[spd_md*MAX_CLK_FREQ_NUM + f] = (val>>(f*8)) & 0xFF;
//MMCINFO("sdly-0 0x%x\n", mmchost->tm4.sdly[spd_md*MAX_CLK_FREQ_NUM + f]);
}
val = p[spd_md*2 + 1];
for (f=4; f<MAX_CLK_FREQ_NUM; f++) {
mmchost->tm4.sdly[spd_md*MAX_CLK_FREQ_NUM + f] = (val>>((f-4)*8)) & 0xFF;
//MMCINFO("sdly-1 0x%x\n", mmchost->tm4.sdly[spd_md*MAX_CLK_FREQ_NUM + f]);
}
}
}
return 0;
}
static int mmc_get_dly_manual_sample(int sdc_no)
{
struct sunxi_mmc_host* mmchost = &mmc_host[sdc_no];
struct mmc_config *cfg = &mmchost->cfg;
int imd, ifreq;
if (sdc_no == 2)
{
if (mmchost->timing_mode == SUNXI_MMC_TIMING_MODE_4)
{
struct sunxi_mmc_timing_mode4 *p = &mmchost->tm4;
for (imd=0; imd<MAX_SPD_MD_NUM; imd++)
{
for (ifreq=0; ifreq<MAX_CLK_FREQ_NUM; ifreq++)
{
//MMCINFO("%d %d\n", cfg->platform_caps.odly_spd_freq[imd*MAX_CLK_FREQ_NUM + ifreq], cfg->platform_caps.sdly_spd_freq[imd*MAX_CLK_FREQ_NUM + ifreq]);
p->odly[imd*MAX_CLK_FREQ_NUM + ifreq] = cfg->platform_caps.odly_spd_freq[imd*MAX_CLK_FREQ_NUM + ifreq];
if (imd == HS400) {
p->dsdly[ifreq] = cfg->platform_caps.sdly_spd_freq[imd*MAX_CLK_FREQ_NUM + ifreq];
} else {
p->sdly[imd*MAX_CLK_FREQ_NUM + ifreq] = cfg->platform_caps.sdly_spd_freq[imd*MAX_CLK_FREQ_NUM + ifreq];
}
}
//MMCINFO("\n");
}
}
else
{
MMCINFO("sdc %d timing mode %d error\n", sdc_no, mmchost->timing_mode);
return -1;
}
}
else if (sdc_no == 0)
{
if (mmchost->timing_mode == SUNXI_MMC_TIMING_MODE_1)
{
struct sunxi_mmc_timing_mode1 *p = &mmchost->tm1;
for (imd=0; imd<MAX_SPD_MD_NUM; imd++)
{
for (ifreq=0; ifreq<MAX_CLK_FREQ_NUM; ifreq++)
{
//MMCINFO("%d %d\n", cfg->platform_caps.odly_spd_freq[imd*MAX_CLK_FREQ_NUM + ifreq], cfg->platform_caps.sdly_spd_freq[imd*MAX_CLK_FREQ_NUM + ifreq]);
p->odly[imd*MAX_CLK_FREQ_NUM + ifreq] = cfg->platform_caps.odly_spd_freq[imd*MAX_CLK_FREQ_NUM + ifreq];
p->sdly[imd*MAX_CLK_FREQ_NUM + ifreq] = cfg->platform_caps.sdly_spd_freq[imd*MAX_CLK_FREQ_NUM + ifreq];
}
//MMCINFO("\n");
}
}
else
{
MMCINFO("sdc %d timing mode %d error\n", sdc_no, mmchost->timing_mode);
return -1;
}
}
else
{
MMCINFO("error sdc_no %d\n", sdc_no);
return -1;
}
return 0;
}
static int mmc_update_timing_para(int sdc_no)
{
int ret = 0;
struct sunxi_mmc_host* mmchost = &mmc_host[sdc_no];
struct mmc_platform_caps *p = &mmchost->cfg.platform_caps;
if (p->sample_mode == AUTO_SAMPLE_MODE)
{
ret = mmc_get_sdly_auto_sample(sdc_no);
if (ret) {
MMCINFO("update auto sample timing para fail!\n");
}
}
else if (p->sample_mode == MAUNAL_SAMPLE_MODE)
{
ret = mmc_get_dly_manual_sample(sdc_no);
if (ret) {
MMCINFO("update manual sample timing para fail!\n");
}
}
else
{
/* use driver's default parameter */
if (sdc_no == 0)
{
mmchost->tm1.odly[HSDDR52_DDR50*MAX_CLK_FREQ_NUM + CLK_50M] =
mmchost->tm1.def_odly[HSDDR52_DDR50*MAX_CLK_FREQ_NUM + CLK_50M];
mmchost->tm1.sdly[HSDDR52_DDR50*MAX_CLK_FREQ_NUM + CLK_50M] =
mmchost->tm1.def_sdly[HSDDR52_DDR50*MAX_CLK_FREQ_NUM + CLK_50M];
}
else
{
}
}
return ret;
}
static void mmc_get_para_from_fex(int sdc_no)
{
int rval, ret = 0;
//int rval_ker, ret1 = 0;
struct sunxi_mmc_host* mmchost = &mmc_host[sdc_no];
struct mmc_config *cfg = &mmchost->cfg;
int nodeoffset=0;
int i, imd, ifreq;
char ctmp[30];
if (sdc_no == 0)
{
nodeoffset = fdt_path_offset(working_fdt,FDT_PATH_CARD0_BOOT_PARA);
if(nodeoffset < 0 )
{
MMCINFO("get card0 para fail\n");
return ;
}
if(fdt_set_all_pin(FDT_PATH_CARD0_BOOT_PARA,"pinctrl-0"))
{
MMCINFO("set card0 pin fail\n");
return ;
}
/* set gpio */
//gpio_request_simple("card0_boot_para", NULL);
//ret = script_parser_fetch("card0_boot_para", "sdc_wipe", &rval, 1);
ret = fdt_getprop_u32(working_fdt,nodeoffset,"sdc_wipe", (uint32_t*)(&rval));
if (ret < 0)
MMCDBG("get sdc_wipe fail.\n");
else {
if (rval & DRV_PARA_DISABLE_SECURE_WIPE) {
MMCINFO("disable driver secure wipe operation.\n");
cfg->platform_caps.drv_wipe_feature |= DRV_PARA_DISABLE_SECURE_WIPE;
}
if (rval & DRV_PARA_DISABLE_EMMC_SANITIZE) {
MMCINFO("disable emmc sanitize feature.\n");
cfg->platform_caps.drv_wipe_feature |= DRV_PARA_DISABLE_EMMC_SANITIZE;
}
if (rval & DRV_PARA_DISABLE_EMMC_SECURE_PURGE) {
MMCINFO("disable emmc secure purge feature.\n");
cfg->platform_caps.drv_wipe_feature |= DRV_PARA_DISABLE_EMMC_SECURE_PURGE;
}
if (rval & DRV_PARA_DISABLE_EMMC_TRIM) {
MMCINFO("disable emmc trim feature.\n");
cfg->platform_caps.drv_wipe_feature |= DRV_PARA_DISABLE_EMMC_TRIM;
}
}
//ret = script_parser_fetch("card0_boot_para", "sdc_erase", &rval, 1);
ret = fdt_getprop_u32(working_fdt,nodeoffset,"sdc_erase", (uint32_t*)(&rval));
if (ret < 0)
MMCDBG("get sdc0 sdc_erase fail.\n");
else {
if (rval & DRV_PARA_DISABLE_EMMC_ERASE) {
MMCINFO("disable emmc erase.\n");
cfg->platform_caps.drv_erase_feature |= DRV_PARA_DISABLE_EMMC_ERASE;
}
if (rval & DRV_PARA_ENABLE_EMMC_SANITIZE_WHEN_ERASE) {
MMCINFO("enable emmc sanitize when erase.\n");
cfg->platform_caps.drv_erase_feature |= DRV_PARA_ENABLE_EMMC_SANITIZE_WHEN_ERASE;
}
}
//ret = script_parser_fetch("card0_boot_para", "sdc_boot", &rval, 1);
ret = fdt_getprop_u32(working_fdt,nodeoffset,"sdc_boot", (uint32_t*)(&rval));
if (ret<0)
MMCDBG("get sdc0 sdc_boot fail.\n");
else {
if (rval & DRV_PARA_NOT_BURN_USER_PART) {
MMCINFO("don't burn boot0 to user part.\n");
cfg->platform_caps.drv_burn_boot_pos |= DRV_PARA_NOT_BURN_USER_PART;
}
if (rval & DRV_PARA_BURN_EMMC_BOOT_PART) {
MMCINFO("burn boot0 to emmc boot part.\n");
cfg->platform_caps.drv_burn_boot_pos |= DRV_PARA_BURN_EMMC_BOOT_PART;
}
}
ret = fdt_getprop_u32(working_fdt,nodeoffset,"sdc_odly_50M", (uint32_t*)(&rval));
if (ret<0) {
MMCDBG("get sdc0 sdc_odly_50M fail.\n");
cfg->platform_caps.boot_odly_50M = 0xff;
} else {
MMCINFO("get sdc0 sdc_odly_50M %d.\n", rval);
cfg->platform_caps.boot_odly_50M = rval;
}
ret = fdt_getprop_u32(working_fdt,nodeoffset,"sdc_sdly_50M", (uint32_t*)(&rval));
if (ret<0) {
MMCDBG("get sdc0 sdc_sdly_50M fail.\n");
cfg->platform_caps.boot_sdly_50M = 0xff;
} else {
MMCINFO("get sdc0 sdc_sdly_50M %d.\n", rval);
cfg->platform_caps.boot_sdly_50M = rval;
}
ret = fdt_getprop_u32(working_fdt,nodeoffset,"sdc_odly_50M_ddr", (uint32_t*)(&rval));
if (ret<0) {
MMCDBG("get sdc0 sdc_odly_50M_ddr fail.\n");
cfg->platform_caps.boot_odly_50M_ddr = 0xff;
} else {
MMCINFO("get sdc0 sdc_odly_50M_ddr %d.\n", rval);
cfg->platform_caps.boot_odly_50M_ddr = rval;
}
ret = fdt_getprop_u32(working_fdt,nodeoffset,"sdc_sdly_50M_ddr", (uint32_t*)(&rval));
if (ret<0) {
MMCDBG("get sdc0 sdc_sdly_50M_ddr fail.\n");
cfg->platform_caps.boot_sdly_50M_ddr = 0xff;
} else {
MMCINFO("get sdc0 sdc_sdly_50M_ddr %d.\n", rval);
cfg->platform_caps.boot_sdly_50M_ddr = rval;
}
ret = fdt_getprop_u32(working_fdt,nodeoffset,"sdc_freq", (uint32_t*)(&rval));
if (ret<0) {
MMCDBG("get sdc0 sdc_freq fail.\n");
cfg->platform_caps.boot_hs_f_max = 0x0;
} else {
if (rval >= 50)
cfg->platform_caps.boot_hs_f_max = 50;
else
cfg->platform_caps.boot_hs_f_max = rval;
MMCINFO("get sdc0 sdc_freq %d.%d\n", rval, cfg->platform_caps.boot_hs_f_max);
}
ret = fdt_getprop_u32(working_fdt,nodeoffset,"sdc_b0p", (uint32_t*)(&rval));
if (ret<0) {
MMCDBG("get sdc0 sdc_b0p fail.\n");
cfg->platform_caps.boot0_para = 0x0;
} else {
MMCINFO("get sdc0 sdc_b0p %d.\n", rval);
cfg->platform_caps.boot0_para = rval;
}
ret = fdt_getprop_u32(working_fdt,nodeoffset,"sdc_ex_dly_used", (uint32_t*)(&rval));
if (ret < 0) {
MMCDBG("get card0_boot_para:sdc_ex_dly_used fail\n");
} else {
if (rval == 1) { /* maual sample point from fex */
cfg->platform_caps.sample_mode = MAUNAL_SAMPLE_MODE;
MMCINFO("get sdc_ex_dly_used %d, use manual sdly in fex\n", rval);
} else {
cfg->platform_caps.sample_mode = 0x0;
MMCINFO("undefined value %d, use default dly\n", rval);
}
}
if (cfg->platform_caps.sample_mode == MAUNAL_SAMPLE_MODE)
{
for (imd=0; imd<MAX_SPD_MD_NUM; imd++)
{
for (ifreq=0; ifreq<MAX_CLK_FREQ_NUM; ifreq++)
{
sprintf(ctmp, "sdc_odly_%d_%d", imd, ifreq);
ret = fdt_getprop_u32(working_fdt, nodeoffset, ctmp, (uint32_t*)(&rval));
if (ret<0) {
MMCDBG("get sdc0 %s fail.\n", ctmp);
cfg->platform_caps.odly_spd_freq[imd*MAX_CLK_FREQ_NUM + ifreq] = 0x0;
} else {
cfg->platform_caps.odly_spd_freq[imd*MAX_CLK_FREQ_NUM + ifreq] = rval&0x1;
MMCINFO("get sdc0 %s 0x%x for %d-%s %d.\n", ctmp,
cfg->platform_caps.odly_spd_freq[imd*MAX_CLK_FREQ_NUM + ifreq], imd, spd_name[imd], ifreq);
}
sprintf(ctmp, "sdc_sdly_%d_%d", imd, ifreq);
ret = fdt_getprop_u32(working_fdt, nodeoffset, ctmp, (uint32_t*)(&rval));
if (ret<0) {
MMCDBG("get sdc0 %s fail.\n", ctmp);
cfg->platform_caps.sdly_spd_freq[imd*MAX_CLK_FREQ_NUM + ifreq] = 0xff;
} else {
cfg->platform_caps.sdly_spd_freq[imd*MAX_CLK_FREQ_NUM + ifreq] = rval&0xff;
MMCINFO("get sdc0 %s 0x%x for %d-%s %d.\n", ctmp,
cfg->platform_caps.sdly_spd_freq[imd*MAX_CLK_FREQ_NUM + ifreq], imd, spd_name[imd], ifreq);
}
}
}
}
}
else if (sdc_no == 2)
{
nodeoffset = fdt_path_offset(working_fdt,FDT_PATH_CARD2_BOOT_PARA);
if(nodeoffset < 0 )
{
MMCINFO("get card0 para fail\n");
return ;
}
if(fdt_set_all_pin(FDT_PATH_CARD2_BOOT_PARA,"pinctrl-0"))
{
MMCINFO("set card0 pin fail\n");
return ;
}
//gpio_request_simple("card2_boot_para", NULL);
//MMCINFO("=====~~~~~~~~~~~~0x%x 0x%x 0x%x\n", *(volatile u32 *)(0x1c20848), *(volatile u32 *)(0x1c2084C), *(volatile u32 *)(0x1c20850));
//ret = script_parser_fetch("card2_boot_para", "sdc_wipe", &rval, 1);
ret = fdt_getprop_u32(working_fdt,nodeoffset,"sdc_wipe", (uint32_t*)(&rval));
if (ret < 0)
MMCDBG("get sdc_wipe fail.\n");
else {
if (rval & DRV_PARA_DISABLE_SECURE_WIPE) {
MMCINFO("disable driver secure wipe operation.\n");
cfg->platform_caps.drv_wipe_feature |= DRV_PARA_DISABLE_SECURE_WIPE;
}
if (rval & DRV_PARA_DISABLE_EMMC_SANITIZE) {
MMCINFO("disable emmc sanitize feature.\n");
cfg->platform_caps.drv_wipe_feature |= DRV_PARA_DISABLE_EMMC_SANITIZE;
}
if (rval & DRV_PARA_DISABLE_EMMC_SECURE_PURGE) {
MMCINFO("disable emmc secure purge feature.\n");
cfg->platform_caps.drv_wipe_feature |= DRV_PARA_DISABLE_EMMC_SECURE_PURGE;
}
if (rval & DRV_PARA_DISABLE_EMMC_TRIM) {
MMCINFO("disable emmc trim feature.\n");
cfg->platform_caps.drv_wipe_feature |= DRV_PARA_DISABLE_EMMC_TRIM;
}
}
//ret = script_parser_fetch("card2_boot_para", "sdc_erase", &rval, 1);
ret = fdt_getprop_u32(working_fdt,nodeoffset,"sdc_erase", (uint32_t*)(&rval));
if (ret < 0)
MMCDBG("get sdc2 sdc_erase fail.\n");
else {
if (rval & DRV_PARA_DISABLE_EMMC_ERASE) {
MMCINFO("disable emmc erase.\n");
cfg->platform_caps.drv_erase_feature |= DRV_PARA_DISABLE_EMMC_ERASE;
}
if (rval & DRV_PARA_ENABLE_EMMC_SANITIZE_WHEN_ERASE) {
MMCINFO("enable emmc sanitize when erase.\n");
cfg->platform_caps.drv_erase_feature |= DRV_PARA_ENABLE_EMMC_SANITIZE_WHEN_ERASE;
}
}
//ret = script_parser_fetch("card2_boot_para", "sdc_boot", &rval, 1);
ret = fdt_getprop_u32(working_fdt,nodeoffset,"sdc_boot", (uint32_t*)(&rval));
if (ret<0)
MMCDBG("get sdc2 sdc_boot fail.\n");
else {
if (rval & DRV_PARA_NOT_BURN_USER_PART) {
MMCINFO("don't burn boot0 to user part.\n");
cfg->platform_caps.drv_burn_boot_pos |= DRV_PARA_NOT_BURN_USER_PART;
}
if (rval & DRV_PARA_BURN_EMMC_BOOT_PART) {
MMCINFO("burn boot0 to emmc boot part.\n");
cfg->platform_caps.drv_burn_boot_pos |= DRV_PARA_BURN_EMMC_BOOT_PART;
}
}
//ret = script_parser_fetch("card2_boot_para", "sdc_ex_dly_used", &rval, 1);
ret = fdt_getprop_u32(working_fdt,nodeoffset,"sdc_ex_dly_used", (uint32_t*)(&rval));
if (ret < 0) {
MMCDBG("get card2_boot_para:sdc_ex_dly_used fail\n");
/* fill invalid sample config */
memset(mmchost->cfg.platform_caps.sdly.tm4_smx_fx,
0xff, sizeof(mmchost->cfg.platform_caps.sdly.tm4_smx_fx));
}
//ret1 = script_parser_fetch("mmc2_para", "sdc_ex_dly_used", &rval_ker, 1);
//if (ret1 < 0)
// MMCDBG("get mmc2_para:sdc_ex_dly_used fail\n");
if (!(ret < 0)/* && !(ret1 < 0)*/)
{
int work_mode = uboot_spare_head.boot_data.work_mode;
struct boot_sdmmc_private_info_t *priv_info =
(struct boot_sdmmc_private_info_t *)(uboot_spare_head.boot_data.sdcard_spare_data);
struct tune_sdly *sdly = &(priv_info->tune_sdly);
u32 *p = (u32 *)&mmchost->cfg.platform_caps.sdly.tm4_smx_fx[0];
if (/*(rval_ker == 2) &&*/ (rval == 2)) { //only when kernal use auto sample,uboot will use auto sample.
cfg->platform_caps.sample_mode = AUTO_SAMPLE_MODE;
MMCINFO("get sdc_ex_dly_used %d, use auto tuning sdly\n", rval);
if (work_mode != WORK_MODE_BOOT) {
/*usb product will auto get sample point, no need to get auto sdly, so first used default value*/
MMCINFO("current is product mode, it will tune sdly later\n");
} else {
/* copy sample point cfg from uboot header to internal variable */
if (sdly != NULL)
memcpy(p, sdly, sizeof(struct tune_sdly));
else
MMCINFO("get sdly from uboot header fail\n");
for(i = 0; i < 10; ++i) {
sprintf(ctmp, "tm4_smx_fx_%d", i);
ret = fdt_getprop_u32(working_fdt,nodeoffset,ctmp, (uint32_t*)(&rval));
if (ret >= 0) {
p[i] = rval;
}
}
}
} else if (rval == 1) { /* maual sample point from fex */
cfg->platform_caps.sample_mode = MAUNAL_SAMPLE_MODE;
MMCINFO("get sdc_ex_dly_used %d, use manual sdly in fex\n", rval);
} else {
cfg->platform_caps.sample_mode = 0x0;
MMCINFO("undefined value %d, use default dly\n", rval);
}
}
ret = fdt_getprop_u32(working_fdt,nodeoffset,"sdc_io_1v8", (uint32_t*)(&rval));
if (ret < 0)
MMCDBG("get card2_boot_para:sdc_io_1v8 fail\n");
else {
if (rval == 1) {
MMCINFO("card2 io is 1.8V.\n");
cfg->platform_caps.io_is_1v8 = 1;
} else {
MMCDBG("card2 io is 3V.\n");
}
}
ret = fdt_getprop_u32(working_fdt,nodeoffset,"sdc_odly_50M", (uint32_t*)(&rval));
if (ret<0) {
MMCDBG("get sdc2 sdc_odly_50M fail.\n");
cfg->platform_caps.boot_odly_50M = 0xff;
} else {
MMCINFO("get sdc2 sdc_odly_50M %d.\n", rval);
cfg->platform_caps.boot_odly_50M = rval;
}
ret = fdt_getprop_u32(working_fdt,nodeoffset,"sdc_sdly_50M", (uint32_t*)(&rval));
if (ret<0) {
MMCDBG("get sdc2 sdc_sdly_50M fail.\n");
cfg->platform_caps.boot_sdly_50M = 0xff;
} else {
MMCINFO("get sdc2 sdc_sdly_50M %d.\n", rval);
cfg->platform_caps.boot_sdly_50M = rval;
}
ret = fdt_getprop_u32(working_fdt,nodeoffset,"sdc_odly_50M_ddr", (uint32_t*)(&rval));
if (ret<0) {
MMCDBG("get sdc2 sdc_odly_50M_ddr fail.\n");
cfg->platform_caps.boot_odly_50M_ddr = 0xff;
} else {
MMCINFO("get sdc2 sdc_odly_50M_ddr %d.\n", rval);
cfg->platform_caps.boot_odly_50M_ddr = rval;
}
ret = fdt_getprop_u32(working_fdt,nodeoffset,"sdc_sdly_50M_ddr", (uint32_t*)(&rval));
if (ret<0) {
MMCDBG("get sdc2 sdc_sdly_50M_ddr fail.\n");
cfg->platform_caps.boot_sdly_50M_ddr = 0xff;
} else {
MMCINFO("get sdc2 sdc_sdly_50M_ddr %d.\n", rval);
cfg->platform_caps.boot_sdly_50M_ddr = rval;
}
ret = fdt_getprop_u32(working_fdt,nodeoffset,"sdc_freq", (uint32_t*)(&rval));
if (ret<0) {
MMCDBG("get sdc2 sdc_freq fail.\n");
cfg->platform_caps.boot_hs_f_max = 0x0;
} else {
if (rval >= 50)
cfg->platform_caps.boot_hs_f_max = 50;
else
cfg->platform_caps.boot_hs_f_max = rval;
MMCINFO("get sdc2 sdc_freq %d.%d\n", rval, cfg->platform_caps.boot_hs_f_max);
}
ret = fdt_getprop_u32(working_fdt,nodeoffset,"sdc_b0p", (uint32_t*)(&rval));
if (ret<0) {
MMCDBG("get sdc2 sdc_b0p fail.\n");
cfg->platform_caps.boot0_para = 0x0;
} else {
cfg->platform_caps.boot0_para = rval;
MMCINFO("get sdc2 sdc_b0p %d.\n", cfg->platform_caps.boot0_para);
}
ret = fdt_getprop_u32(working_fdt,nodeoffset,"sdc_tm4_win_th", (uint32_t*)(&rval));
if (ret<0) {
MMCDBG("get sdc2 sdc_tm4_win_th fail.\n");
cfg->platform_caps.tm4_timing_window_th = 12;
} else {
if ((rval<4) || (rval>64))
cfg->platform_caps.tm4_timing_window_th = 12;
else
cfg->platform_caps.tm4_timing_window_th = rval;
MMCINFO("get sdc2 sdc_tm4_win_th %d.\n", cfg->platform_caps.tm4_timing_window_th);
}
ret = fdt_getprop_u32(working_fdt,nodeoffset,"sdc_tm4_r_cycle", (uint32_t*)(&rval));
if (ret<0) {
MMCDBG("get sdc2 sdc_tm4_r_cycle fail.\n");
cfg->platform_caps.tm4_tune_r_cycle= 0x0;
} else {
if ((rval<1) || (rval>40))
cfg->platform_caps.tm4_tune_r_cycle = 15;
else
cfg->platform_caps.tm4_tune_r_cycle = rval;
MMCINFO("get sdc2 sdc_tm4_r_cycle %d.\n", cfg->platform_caps.tm4_tune_r_cycle);
}
ret = fdt_getprop_u32(working_fdt,nodeoffset,"sdc_tm4_hs200_max_freq", (uint32_t*)(&rval));
if (ret<0) {
MMCDBG("get sdc2 sdc_tm4_hs200_max_freq fail.\n");
cfg->platform_caps.tm4_tune_hs200_max_freq = 0x0;
} else {
if ((rval<50) || (rval>200))
cfg->platform_caps.tm4_tune_hs200_max_freq = 0x0;
else
cfg->platform_caps.tm4_tune_hs200_max_freq = rval;
MMCINFO("get sdc2 sdc_tm4_hs200_max_freq %d.\n", cfg->platform_caps.tm4_tune_hs200_max_freq);
}
ret = fdt_getprop_u32(working_fdt,nodeoffset,"sdc_tm4_hs400_max_freq", (uint32_t*)(&rval));
if (ret<0) {
MMCDBG("get sdc2 sdc_tm4_hs400_max_freq fail.\n");
cfg->platform_caps.tm4_tune_hs400_max_freq = 0x0;
} else {
if ((rval<50) || (rval>200))
cfg->platform_caps.tm4_tune_hs400_max_freq = 0x0;
else
cfg->platform_caps.tm4_tune_hs400_max_freq = rval;
MMCINFO("get sdc2 sdc_tm4_hs400_max_freq %d.\n", cfg->platform_caps.tm4_tune_hs400_max_freq);
}
for (i=0; i<MAX_EXT_FREQ_POINT_NUM; i++)
{
sprintf(ctmp, "sdc_tm4_ext_freq_%d", i);
ret = fdt_getprop_u32(working_fdt,nodeoffset, ctmp, (uint32_t*)(&rval));
if (ret<0) {
MMCDBG("get sdc2 %s fail.\n", ctmp);
cfg->platform_caps.tm4_tune_ext_freq[i] = 0x0;
} else {
MMCINFO("get sdc2 %s 0x%x.\n", ctmp, rval);
if ((rval & 0xff) >= 25)
cfg->platform_caps.tm4_tune_ext_freq[i] = rval;
else {
cfg->platform_caps.tm4_tune_ext_freq[i] = 0x0;
MMCINFO("invalid freq %d MHz, discard this ext freq point\n", (rval & 0xff));
}
}
}
if (cfg->platform_caps.sample_mode == MAUNAL_SAMPLE_MODE)
{
for (imd=0; imd<MAX_SPD_MD_NUM; imd++)
{
for (ifreq=0; ifreq<MAX_CLK_FREQ_NUM; ifreq++)
{
sprintf(ctmp, "sdc_odly_%d_%d", imd, ifreq);
ret = fdt_getprop_u32(working_fdt, nodeoffset, ctmp, (uint32_t*)(&rval));
if (ret<0) {
MMCDBG("get sdc2 %s fail.\n", ctmp);
cfg->platform_caps.odly_spd_freq[imd*MAX_CLK_FREQ_NUM + ifreq] = 0x0;
} else {
cfg->platform_caps.odly_spd_freq[imd*MAX_CLK_FREQ_NUM + ifreq] = rval&0x1;
MMCINFO("get sdc2 %s 0x%x for %d-%s %d.\n", ctmp,
cfg->platform_caps.odly_spd_freq[imd*MAX_CLK_FREQ_NUM + ifreq], imd, spd_name[imd], ifreq);
}
sprintf(ctmp, "sdc_sdly_%d_%d", imd, ifreq);
ret = fdt_getprop_u32(working_fdt, nodeoffset, ctmp, (uint32_t*)(&rval));
if (ret<0) {
MMCDBG("get sdc2 %s fail.\n", ctmp);
cfg->platform_caps.sdly_spd_freq[imd*MAX_CLK_FREQ_NUM + ifreq] = 0xff;
} else {
cfg->platform_caps.sdly_spd_freq[imd*MAX_CLK_FREQ_NUM + ifreq] = rval&0xff;
MMCINFO("get sdc2 %s 0x%x for %d-%s %d.\n", ctmp,
cfg->platform_caps.sdly_spd_freq[imd*MAX_CLK_FREQ_NUM + ifreq], imd, spd_name[imd], ifreq);
}
}
}
}
/* speed mode caps */
ret = fdt_getprop_u32(working_fdt,nodeoffset,"sdc_dis_host_caps", (uint32_t*)(&rval));
if (ret<0) {
MMCDBG("get sdc2 sdc_dis_host_caps fail.\n");
cfg->platform_caps.host_caps_mask = 0x0;
} else {
cfg->platform_caps.host_caps_mask = rval;
MMCINFO("get sdc2 sdc_dis_host_caps 0x%x.\n", cfg->platform_caps.host_caps_mask);
}
/* fmax, fmax_ddr */
}
else {
MMCINFO("%s: input sdc_no error: %d\n", __FUNCTION__, sdc_no);
}
return ;
}
static void mmc_get_para_from_dtb(int sdc_no)
{
int ret = 0;
//struct sunxi_mmc_host* mmchost = &mmc_host[sdc_no];
//struct mmc_config *cfg = &mmchost->cfg;
int nodeoffset;
char prop_path[128] = {0};
//u32 prop_val = 0;
if (sdc_no == 0)
{
strcpy(prop_path, "mmc0");
nodeoffset = fdt_path_offset(working_fdt, prop_path);
if (nodeoffset < 0) {
MMCINFO("can't find node \"%s\",will add new node\n", prop_path);
goto __ERRRO_END;
}
}
else
{
}
return ;
__ERRRO_END:
printf ("fdt err returned %s\n", fdt_strerror(ret));
return ;
}
static int mmc_clk_io_onoff(int sdc_no, int onoff, int reset_clk)
{
int rval;
struct sunxi_mmc_host* mmchost = &mmc_host[sdc_no];
/* config ahb clock */
if (onoff)
{
rval = readl(mmchost->hclkrst);
rval |= (1 << (8 + sdc_no));
writel(rval, mmchost->hclkrst);
rval = readl(mmchost->hclkbase);
rval |= (1 << (8 + sdc_no));
writel(rval, mmchost->hclkbase);
}
else
{
rval = readl(mmchost->hclkbase);
rval &= ~(1 << (8 + sdc_no));
writel(rval, mmchost->hclkbase);
rval = readl(mmchost->hclkrst);
rval &= ~ (1 << (8 + sdc_no));
writel(rval, mmchost->hclkrst);
}
/* config mod clock */
if (reset_clk)
{
writel(0x80000000, mmchost->mclkbase);
mmchost->mod_clk = 24000000;
}
dumphex32("ccmu", (char*)SUNXI_CCM_BASE, 0x100);
dumphex32("gpio", (char*)SUNXI_PIO_BASE, 0x100);
dumphex32("mmc", (char*)mmchost->reg, 0x100);
return 0;
}
static int mmc_update_clk(struct sunxi_mmc_host* mmchost)
{
unsigned int cmd;
unsigned timeout = 1000;
writel(readl(&mmchost->reg->clkcr)|(0x1<<31), &mmchost->reg->clkcr);
cmd = (1U << 31) | (1 << 21) | (1 << 13);
writel(cmd, &mmchost->reg->cmd);
while((readl(&mmchost->reg->cmd)&0x80000000) && --timeout){
__msdelay(1);
}
if (!timeout){
MMCINFO("mmc %d update clk failed\n",mmchost->mmc_no);
dumphex32("mmc", (char*)mmchost->reg, 0x100);
return -1;
}
writel(readl(&mmchost->reg->clkcr) & (~(0x1<<31)), &mmchost->reg->clkcr);
writel(readl(&mmchost->reg->rint), &mmchost->reg->rint);
return 0;
}
static int mmc_update_phase(struct mmc *mmc)
{
struct sunxi_mmc_host* mmchost = (struct sunxi_mmc_host *)mmc->priv;
if (mmchost->timing_mode == SUNXI_MMC_TIMING_MODE_1)
{
MMCDBG("mmc re-update_phase\n");
return mmc_update_clk(mmchost);
}
return 0;
}
#ifndef FPGA_PLATFORM
static int mmc_set_mclk(struct sunxi_mmc_host* mmchost, u32 clk_hz)
{
unsigned n, m, div, src, sclk_hz = 0;
unsigned rval;
MMCDBG("%s: mod_clk %d\n", __FUNCTION__, clk_hz);
if (clk_hz <= 4000000) { //400000
src = 0;
sclk_hz = 24000000;
} else {
src = 1;
sclk_hz = sunxi_clock_get_pll6()*2*1000000; /*use 2x pll-per0 clock */
}
div = (2 * sclk_hz + clk_hz) / (2 * clk_hz);
div = (div==0) ? 1 : div;
if (div > 128) {
m = 1;
n = 0;
MMCINFO("%s: source clock is too high, clk %d, src %d!!!\n",
__FUNCTION__, clk_hz, sclk_hz);
} else if (div > 64) {
n = 3;
m = div >> 3;
} else if (div > 32) {
n = 2;
m = div >> 2;
} else if (div > 16) {
n = 1;
m = div >> 1;
} else {
n = 0;
m = div;
}
//rval = (1U << 31) | (src << 24) | (n << 16) | (m - 1);
rval = (src << 24) | (n << 16) | (m - 1);
writel(rval, mmchost->mclkbase);
return 0;
}
static unsigned mmc_get_mclk(struct sunxi_mmc_host* mmchost)
{
unsigned n, m, src, sclk_hz = 0;
unsigned rval = readl(mmchost->mclkbase);
m = rval & 0xf;
n = (rval>>16) & 0x3;
src = (rval>>24) & 0x3;
if (src == 0)
sclk_hz = 24000000;
else if (src == 1)
sclk_hz = sunxi_clock_get_pll6()*2*1000000; /* use 2x pll6 */
else if (src == 2) {
/*todo*/
} else {
MMCINFO("%s: wrong clock source %d\n",__func__, src);
}
return (sclk_hz / (1<<n) / (m+1) );
}
static unsigned mmc_config_delay(struct sunxi_mmc_host* mmchost)
{
unsigned rval = 0;
unsigned mode = mmchost->timing_mode;
unsigned spd_md, spd_md_bak, freq;
u8 odly, sdly, dsdly=0;
if (mode == SUNXI_MMC_TIMING_MODE_1)
{
spd_md = mmchost->tm1.cur_spd_md;
freq = mmchost->tm1.cur_freq;
if (mmchost->tm1.odly[spd_md*MAX_CLK_FREQ_NUM+freq] != 0xFF)
odly = mmchost->tm1.odly[spd_md*MAX_CLK_FREQ_NUM+freq];
else
odly = mmchost->tm1.def_odly[spd_md*MAX_CLK_FREQ_NUM+freq];
if (mmchost->tm1.sdly[spd_md*MAX_CLK_FREQ_NUM+freq] != 0xFF)
sdly = mmchost->tm1.sdly[spd_md*MAX_CLK_FREQ_NUM+freq];
else
sdly = mmchost->tm1.def_sdly[spd_md*MAX_CLK_FREQ_NUM+freq];
mmchost->tm1.cur_odly = odly;
mmchost->tm1.cur_sdly = sdly;
MMCDBG("%s: odly: %d sldy: %d\n", __FUNCTION__, odly, sdly);
rval = readl(&mmchost->reg->drv_dl);
rval &= (~(0x3<<16));
rval |= (((odly&0x1)<<16) | ((odly&0x1)<<17));
writel(rval, &mmchost->reg->drv_dl);
rval = readl(&mmchost->reg->ntsr);
rval &= (~(0x3<<4));
rval |= ((sdly&0x3)<<4);
writel(rval, &mmchost->reg->ntsr);
}
else if (mode == SUNXI_MMC_TIMING_MODE_3)
{
spd_md = mmchost->tm3.cur_spd_md;
freq = mmchost->tm3.cur_freq;
if (mmchost->tm3.odly[spd_md*MAX_CLK_FREQ_NUM+freq] != 0xFF)
odly = mmchost->tm3.odly[spd_md*MAX_CLK_FREQ_NUM+freq];
else
odly = mmchost->tm3.def_odly[spd_md*MAX_CLK_FREQ_NUM+freq];
if (mmchost->tm3.sdly[spd_md*MAX_CLK_FREQ_NUM+freq] != 0xFF)
sdly = mmchost->tm3.sdly[spd_md*MAX_CLK_FREQ_NUM+freq];
else
sdly = mmchost->tm3.def_sdly[spd_md*MAX_CLK_FREQ_NUM+freq];
mmchost->tm3.cur_odly = odly;
mmchost->tm3.cur_sdly = sdly;
MMCDBG("%s: odly: %d sldy: %d\n", __FUNCTION__, odly, sdly);
rval = readl(&mmchost->reg->drv_dl);
rval &= (~(0x3<<16));
rval |= (((odly&0x1)<<16) | ((odly&0x1)<<17));
writel(rval, &mmchost->reg->drv_dl);
rval = readl(&mmchost->reg->samp_dl);
rval &= (~SDXC_CfgDly);
rval |= ((sdly&SDXC_CfgDly) | SDXC_EnableDly);
writel(rval, &mmchost->reg->samp_dl);
}
else if (mode == SUNXI_MMC_TIMING_MODE_4)
{
spd_md = mmchost->tm4.cur_spd_md;
spd_md_bak = spd_md;
freq = mmchost->tm4.cur_freq;
if (spd_md == HS400)
spd_md = HS200_SDR104; /* use HS200's sdly for HS400's CMD line */
if (mmchost->tm4.sdly[spd_md*MAX_CLK_FREQ_NUM+freq] != 0xFF)
sdly = mmchost->tm4.sdly[spd_md*MAX_CLK_FREQ_NUM+freq];
else
sdly = mmchost->tm4.def_sdly[spd_md*MAX_CLK_FREQ_NUM+freq];
spd_md = spd_md_bak;
if (mmchost->tm4.odly[spd_md*MAX_CLK_FREQ_NUM+freq] != 0xFF)
odly = mmchost->tm4.odly[spd_md*MAX_CLK_FREQ_NUM+freq];
else
odly = mmchost->tm4.def_odly[spd_md*MAX_CLK_FREQ_NUM+freq];
mmchost->tm4.cur_odly = odly;
mmchost->tm4.cur_sdly = sdly;
rval = readl(&mmchost->reg->drv_dl);
rval &= (~(0x3<<16));
rval |= (((odly&0x1)<<16) | ((odly&0x1)<<17));
writel(rval, &mmchost->reg->drv_dl);
rval = readl(&mmchost->reg->samp_dl);
rval &= (~SDXC_CfgDly);
rval |= ((sdly&SDXC_CfgDly) | SDXC_EnableDly);
writel(rval, &mmchost->reg->samp_dl);
if (spd_md == HS400)
{
if (mmchost->tm4.dsdly[freq] != 0xFF)
dsdly = mmchost->tm4.dsdly[freq];
else
dsdly = mmchost->tm4.def_dsdly[freq];
mmchost->tm4.cur_dsdly = dsdly;
rval = readl(&mmchost->reg->ds_dl);
rval &= (~SDXC_CfgDly);
rval |= ((dsdly&SDXC_CfgDly) | SDXC_EnableDly);
#ifdef FPGA_PLATFORM
rval &= (~0x7);
#endif
writel(rval, &mmchost->reg->ds_dl);
}
MMCDBG("%s: spd_md:%d, freq:%d, odly: %d; sdly: %d; dsdly: %d\n", __FUNCTION__, spd_md, freq, odly, sdly, dsdly);
}
return 0;
}
#endif /*FPGA_PLATFORM*/
static int mmc_config_clock_modex(struct sunxi_mmc_host* mmchost, unsigned clk)
{
unsigned rval = 0;
struct mmc *mmc = mmchost->mmc;
unsigned mode = mmchost->timing_mode;
#ifndef FPGA_PLATFORM
unsigned freq_id;
/* disable mclk */
writel(0x0, mmchost->mclkbase);
MMCDBG("mmc %d mclkbase 0x%x\n", mmchost->mmc_no, readl(mmchost->mclkbase));
/* enable timing mode 1 */
if (mode == SUNXI_MMC_TIMING_MODE_1) {
rval = readl(&mmchost->reg->ntsr);
rval |= (1<<31);
writel(rval, &mmchost->reg->ntsr);
MMCDBG("mmc %d rntsr 0x%x\n", mmchost->mmc_no, rval);
} else
writel(0x0, &mmchost->reg->ntsr);
/* configure clock */
if ((mode == SUNXI_MMC_TIMING_MODE_1) || (mmc->speed_mode == HSDDR52_DDR50)) {
if (mmc->speed_mode == HSDDR52_DDR50)
mmchost->mod_clk = clk * 4;
else
mmchost->mod_clk = clk * 2;
} else if (mode == SUNXI_MMC_TIMING_MODE_4) {
if ((mmc->speed_mode == HSDDR52_DDR50)
&& (mmc->bus_width == 8))
mmchost->mod_clk = clk * 4; /* 4xclk: DDR8(HS) */
else
mmchost->mod_clk = clk * 2; /* 2xclk: SDR 1/4/8; DDR4(HS); DDR8(HS400) */
}
mmc_set_mclk(mmchost, mmchost->mod_clk);
/* get mclk */
if ((mode == SUNXI_MMC_TIMING_MODE_1) || (mode == SUNXI_MMC_TIMING_MODE_3)) {
if (mmc->speed_mode == HSDDR52_DDR50)
mmc->clock = mmc_get_mclk(mmchost) / 4;
else
mmc->clock = mmc_get_mclk(mmchost) / 2;
} else if (mode == SUNXI_MMC_TIMING_MODE_4) {
if ((mmc->speed_mode == HSDDR52_DDR50)
&& (mmc->bus_width == 8))
mmc->clock = mmc_get_mclk(mmchost) / 4;
else
mmc->clock = mmc_get_mclk(mmchost) / 2;
}
mmchost->clock = mmc->clock; /* bankup current clock frequency at host */
MMCDBG("get round card clk %d, mod_clk %d\n", mmc->clock, mmchost->mod_clk);
/* re-enable mclk */
writel(readl(mmchost->mclkbase)|(1<<31),mmchost->mclkbase);
MMCDBG("mmc %d mclkbase 0x%x\n", mmchost->mmc_no, readl(mmchost->mclkbase));
/*
* CLKCREG[7:0]: divider
* CLKCREG[16]: on/off
* CLKCREG[17]: power save
*/
rval = readl(&mmchost->reg->clkcr);
rval &= ~(0xFF);
if ((mode == SUNXI_MMC_TIMING_MODE_1)||(mode == SUNXI_MMC_TIMING_MODE_3)) {
if (mmc->speed_mode == HSDDR52_DDR50)
rval |= 0x1;
} else if (mode == SUNXI_MMC_TIMING_MODE_4) {
if ((mmc->speed_mode == HSDDR52_DDR50) && (mmc->bus_width == 8))
rval |= 0x1;
}
writel(rval, &mmchost->reg->clkcr);
if (mmc_update_clk(mmchost))
return -1;
/* configure delay for current frequency and speed mode */
if (clk <= 400000)
freq_id = CLK_400K;
else if (clk <= 26000000)
freq_id = CLK_25M;
else if (clk <= 52000000)
freq_id = CLK_50M;
else if (clk <= 100000000)
freq_id = CLK_100M;
else if (clk <= 150000000)
freq_id = CLK_150M;
else if (clk <= 200000000)
freq_id = CLK_200M;
else
freq_id = CLK_25M;
if (mode == SUNXI_MMC_TIMING_MODE_1) {
mmchost->tm1.cur_spd_md = mmchost->mmc->speed_mode;
mmchost->tm1.cur_freq = freq_id;
} else if (mode == SUNXI_MMC_TIMING_MODE_3) {
mmchost->tm3.cur_spd_md = mmchost->mmc->speed_mode;
mmchost->tm3.cur_freq = freq_id;
} else if (mode == SUNXI_MMC_TIMING_MODE_4) {
mmchost->tm4.cur_spd_md = mmchost->mmc->speed_mode;
mmchost->tm4.cur_freq = freq_id;
}
mmc_config_delay(mmchost);
#else
unsigned div, sclk= 24000000;
unsigned clk_2x = 0;
if (mode == SUNXI_MMC_TIMING_MODE_1)
{
div = (2 * sclk + clk) / (2 * clk);
rval = readl(&mmchost->reg->clkcr) & (~0xff);
if (mmc->io_mode == MMC_MODE_DDR_52MHz)
rval |= 0x1;
else
rval |= div >> 1;
writel(rval, &mmchost->reg->clkcr);
rval = readl(&mmchost->reg->ntsr);
rval |= (1<<31);
writel(rval, &mmchost->reg->ntsr);
MMCINFO("mmc %d ntsr 0x%x, ckcr 0x%x\n", mmchost->mmc_no,
readl(&mmchost->reg->ntsr), readl(&mmchost->reg->clkcr));
}
if ((mode == SUNXI_MMC_TIMING_MODE_3) || (mode == SUNXI_MMC_TIMING_MODE_4))
{
if (mode == SUNXI_MMC_TIMING_MODE_3) {
if (mmc->io_mode == MMC_MODE_DDR_52MHz)
clk_2x = clk << 2; //4xclk
else
clk_2x = clk << 1; //2xclk
} else if (mode == SUNXI_MMC_TIMING_MODE_4) {
if (mmc->io_mode == MMC_MODE_DDR_52MHz && mmc->bus_width == 8)
clk_2x = clk << 2; //4xclk: DDR8(HS)
else
clk_2x = clk << 1; //2xclk: SDR 1/4/8; DDR4(HS); DDR8(HS400)
}
div = (2 * sclk + clk_2x) / (2 * clk_2x);
rval = readl(&mmchost->reg->clkcr) & (~0xff);
if (mmc->io_mode == MMC_MODE_DDR_52MHz)
rval |= 0x1;
else
rval |= div >> 1;
writel(rval, &mmchost->reg->clkcr);
}
#if 0
{
unsigned freq=0;
/* configure delay for current frequency and speed mode */
if (clk <= 400000)
freq = CLK_400K;
else if (clk <= 26000000)
freq = CLK_25M;
else if (clk <= 52000000)
freq = CLK_50M;
else if (clk <= 100000000)
freq = CLK_100M;
else if (clk <= 150000000)
freq = CLK_150M;
else if (clk <= 200000000)
freq = CLK_200M;
else
freq = CLK_25M;
if (mode == SUNXI_MMC_TIMING_MODE_1) {
mmchost->tm1.cur_spd_md = mmchost->mmc->speed_mode;
mmchost->tm1.cur_freq = freq;
} else if (mode == SUNXI_MMC_TIMING_MODE_3) {
mmchost->tm3.cur_spd_md = mmchost->mmc->speed_mode;;
mmchost->tm3.cur_freq = freq;
} else if (mode == SUNXI_MMC_TIMING_MODE_4) {
mmchost->tm4.cur_spd_md = mmchost->mmc->speed_mode;;
mmchost->tm4.cur_freq = freq;
}
mmc_config_delay(mmchost);
}
#endif
#endif
//dumphex32("ccmu", (char*)SUNXI_CCM_BASE, 0x100);
//dumphex32("gpio", (char*)SUNXI_PIO_BASE, 0x100);
//dumphex32("mmc", (char*)mmchost->reg, 0x100);
return 0;
}
static int mmc_config_clock(struct sunxi_mmc_host* mmchost, unsigned clk)
{
unsigned rval = 0;
/* disable card clock */
rval = readl(&mmchost->reg->clkcr);
rval &= ~(1 << 16);
writel(rval, &mmchost->reg->clkcr);
if(mmc_update_clk(mmchost))
return -1;
if ((mmchost->timing_mode == SUNXI_MMC_TIMING_MODE_1)
|| (mmchost->timing_mode == SUNXI_MMC_TIMING_MODE_3)
|| (mmchost->timing_mode == SUNXI_MMC_TIMING_MODE_4) )
mmc_config_clock_modex(mmchost, clk);
else {
MMCINFO("mmc %d wrong timing mode: 0x%x\n",
mmchost->mmc_no, mmchost->timing_mode);
return -1;
}
/* Re-enable card clock */
rval = readl(&mmchost->reg->clkcr);
rval |= (0x1 << 16); //(3 << 16);
writel(rval, &mmchost->reg->clkcr);
if(mmc_update_clk(mmchost)){
MMCINFO("mmc %d re-enable clock failed\n",mmchost->mmc_no);
return -1;
}
return 0;
}
static int mmc_calibrate_delay_unit(struct sunxi_mmc_host* mmchost)
{
unsigned rval = 0;
unsigned result = 0;
MMCINFO("start %s, don't access device...\n", __FUNCTION__);
/* close card clock */
rval = readl(&mmchost->reg->clkcr);
rval &= ~(1 << 16);
writel(rval, &mmchost->reg->clkcr);
if(mmc_update_clk(mmchost))
return -1;
/* set card clock to 100MHz */
if ((mmchost->timing_mode == SUNXI_MMC_TIMING_MODE_1)
|| (mmchost->timing_mode == SUNXI_MMC_TIMING_MODE_3)
|| (mmchost->timing_mode == SUNXI_MMC_TIMING_MODE_4) )
mmc_config_clock_modex(mmchost, 100000000);
else {
MMCINFO("%s: mmc %d wrong timing mode: 0x%x\n",
__FUNCTION__, mmchost->mmc_no, mmchost->timing_mode);
return -1;
}
/* start carlibrate delay unit */
writel(0xA0, &mmchost->reg->samp_dl);
writel(0x0, &mmchost->reg->samp_dl);
rval = SDXC_StartCal;
writel(rval, &mmchost->reg->samp_dl);
while (!(readl(&mmchost->reg->samp_dl) & SDXC_CalDone));
if (mmchost->mmc_no == 2) {
writel(0xA0, &mmchost->reg->ds_dl);
writel(0x0, &mmchost->reg->ds_dl);
rval = SDXC_StartCal;
writel(rval, &mmchost->reg->ds_dl);
while (!(readl(&mmchost->reg->ds_dl) & SDXC_CalDone));
}
/* update result */
rval = readl(&mmchost->reg->samp_dl);
result = (rval & SDXC_CalDly) >> 8;
MMCDBG("ds_dl result: 0x%x\n", result);
/* 10ns= 10*1000 ps, mod_clk is 5ns */
if (result) {
rval = 5000 / result;
mmchost->tm3.sdly_unit_ps = rval;
mmchost->tm3.dly_calibrate_done = 1;
mmchost->tm4.sdly_unit_ps = rval;
MMCINFO("delay chain cal done, sample: %d(ps)\n", mmchost->tm3.sdly_unit_ps);
} else {
MMCINFO("%s: cal sample delay fail\n", __FUNCTION__);
}
if (mmchost->mmc_no == 2) {
rval = readl(&mmchost->reg->ds_dl);
result = (rval & SDXC_CalDly) >> 8;
MMCDBG("ds_dl result: 0x%x\n", result);
if (result) {
rval = 5000 / result;
mmchost->tm4.dsdly_unit_ps = rval;
mmchost->tm4.dly_calibrate_done = 1;
MMCINFO("delay chain cal done, ds: %d(ps)\n", mmchost->tm4.dsdly_unit_ps);
} else {
MMCINFO("%s: cal data strobe delay fail\n", __FUNCTION__);
}
}
return 0;
}
static void mmc_ddr_mode_onoff(struct mmc *mmc, int on)
{
struct sunxi_mmc_host* mmchost = (struct sunxi_mmc_host *)mmc->priv;
u32 rval = 0;
rval = readl(&mmchost->reg->gctrl);
rval &= (~(1U << 10));
if (on) {
rval |= (1U << 10);
writel(rval, &mmchost->reg->gctrl);
MMCDBG("set %d rgctrl 0x%x to enable ddr mode\n", mmchost->mmc_no, readl(&mmchost->reg->gctrl));
} else {
writel(rval, &mmchost->reg->gctrl);
MMCDBG("set %d rgctrl 0x%x to disable ddr mode\n", mmchost->mmc_no, readl(&mmchost->reg->gctrl));
}
}
static void mmc_hs400_mode_onoff(struct mmc *mmc, int on)
{
struct sunxi_mmc_host* mmchost = (struct sunxi_mmc_host *)mmc->priv;
u32 rval = 0;
if (mmchost->mmc_no != 2) {
return ;
}
rval = readl(&mmchost->reg->dsbd);
rval &= (~(1 << 31));
if (on) {
rval |= (1 << 31);
writel(rval, &mmchost->reg->dsbd);
MMCDBG("set %d dsbd 0x%x to enable hs400 mode\n", mmchost->mmc_no, readl(&mmchost->reg->dsbd));
} else {
writel(rval, &mmchost->reg->dsbd);
MMCDBG("set %d dsbd 0x%x to disable hs400 mode\n", mmchost->mmc_no, readl(&mmchost->reg->dsbd));
}
}
static void mmc_set_ios(struct mmc *mmc)
{
struct sunxi_mmc_host* mmchost = (struct sunxi_mmc_host *)mmc->priv;
MMCDBG("mmc %d ios: bus: %d, clock: %d, speed mode: %d\n", \
mmchost->mmc_no,mmc->bus_width, mmc->clock, mmc->speed_mode);
/* change clock */
if (mmc->clock && mmc_config_clock((struct sunxi_mmc_host*)mmc->priv, mmc->clock)) {
MMCINFO("[mmc]: mmc %d update clock failed\n",mmchost->mmc_no);
mmchost->fatal_err = 1;
return;
}
/* for card2, if clock frequency is greater than 100MHz, increase gpio dirver strength */
#if 0
if (mmc->clock >= 100000000) {
writel(0xFFFFFFFF, 0x01c2085C);
writel(0xFFFFFFFF, 0x01c20860);
} else {
writel(0x55555555, 0x01c2085C);
writel(0x55555555, 0x01c20860);
}
#endif
/* Change bus width */
if (mmc->bus_width == 8)
writel(2, &mmchost->reg->width);
else if (mmc->bus_width == 4)
writel(1, &mmchost->reg->width);
else
writel(0, &mmchost->reg->width);
MMCDBG("host bus width register 0x%x\n", readl(&mmchost->reg->width));
/* set ddr mode */
if (mmc->speed_mode == HSDDR52_DDR50) {
mmc_ddr_mode_onoff(mmc, 1);
mmc_hs400_mode_onoff(mmc, 0);
} else if (mmc->speed_mode == HS400) {
mmc_ddr_mode_onoff(mmc, 0);
mmc_hs400_mode_onoff(mmc, 1);
} else {
mmc_ddr_mode_onoff(mmc, 0);
mmc_hs400_mode_onoff(mmc, 0);
}
}
static int mmc_save_regs(struct sunxi_mmc_host* host)
{
host->reg_bak->gctrl = readl(&host->reg->gctrl );
host->reg_bak->clkcr = readl(&host->reg->clkcr );
host->reg_bak->timeout = readl(&host->reg->timeout);
host->reg_bak->width = readl(&host->reg->width );
host->reg_bak->imask = readl(&host->reg->imask );
host->reg_bak->ftrglevel = readl(&host->reg->ftrglevel);
host->reg_bak->dbgc = readl(&host->reg->dbgc );
host->reg_bak->csdc = readl(&host->reg->csdc );
host->reg_bak->ntsr = readl(&host->reg->ntsr );
host->reg_bak->hwrst = readl(&host->reg->hwrst );
host->reg_bak->dmac = readl(&host->reg->dmac );
host->reg_bak->idie = readl(&host->reg->idie );
host->reg_bak->thldc = readl(&host->reg->thldc );
host->reg_bak->dsbd = readl(&host->reg->dsbd );
host->reg_bak->drv_dl = readl(&host->reg->drv_dl );
host->reg_bak->samp_dl = readl(&host->reg->samp_dl);
host->reg_bak->ds_dl = readl(&host->reg->ds_dl );
return 0;
}
static int mmc_restore_regs(struct sunxi_mmc_host* host)
{
writel(host->reg_bak->gctrl , &host->reg->gctrl );
writel(host->reg_bak->clkcr , &host->reg->clkcr );
writel(host->reg_bak->timeout , &host->reg->timeout);
writel(host->reg_bak->width , &host->reg->width );
writel(host->reg_bak->imask , &host->reg->imask );
writel(host->reg_bak->ftrglevel , &host->reg->ftrglevel);
if (host->reg_bak->dbgc)
writel(0xdeb, &host->reg->dbgc);
writel(host->reg_bak->csdc , &host->reg->csdc );
writel(host->reg_bak->ntsr , &host->reg->ntsr );
writel(host->reg_bak->hwrst , &host->reg->hwrst );
writel(host->reg_bak->dmac , &host->reg->dmac );
writel(host->reg_bak->idie , &host->reg->idie );
writel(host->reg_bak->thldc , &host->reg->thldc );
writel(host->reg_bak->dsbd , &host->reg->dsbd );
writel(host->reg_bak->drv_dl , &host->reg->drv_dl );
writel(host->reg_bak->samp_dl , &host->reg->samp_dl);
writel(host->reg_bak->ds_dl , &host->reg->ds_dl );
return 0;
}
static int mmc_core_init(struct mmc *mmc)
{
struct sunxi_mmc_host* mmchost = (struct sunxi_mmc_host *)mmc->priv;
/* Reset controller */
writel(0x7, &mmchost->reg->gctrl); /*0x40000007*/
while(readl(&mmchost->reg->gctrl)&0x7);
/* release eMMC reset signal */
writel(1, &mmchost->reg->hwrst);
writel(0, &mmchost->reg->hwrst);
udelay(1000);
writel(1, &mmchost->reg->hwrst);
udelay(1000);
#if 1
#define SMC_DATA_TIMEOUT 0xffffffU
#define SMC_RESP_TIMEOUT 0xff
#else
#define SMC_DATA_TIMEOUT 0x1ffffU
#define SMC_RESP_TIMEOUT 0x2
#endif
writel((SMC_DATA_TIMEOUT<<8)|SMC_RESP_TIMEOUT, &mmchost->reg->timeout); //Set Data & Response Timeout Value
writel((512<<16)|(1U<<2)|(1U<<0), &mmchost->reg->thldc);
writel(3, &mmchost->reg->csdc);
writel(0xdeb, &mmchost->reg->dbgc);
mmc_calibrate_delay_unit(mmchost);
return 0;
}
static int mmc_trans_data_by_cpu(struct mmc *mmc, struct mmc_data *data)
{
struct sunxi_mmc_host* mmchost = (struct sunxi_mmc_host *)mmc->priv;
unsigned i;
unsigned byte_cnt = data->blocksize * data->blocks;
unsigned *buff;
unsigned timeout = 1000;
if (data->flags & MMC_DATA_READ) {
buff = (unsigned int *)data->dest;
for (i=0; i<(byte_cnt>>2); i++) {
while(--timeout && (readl(&mmchost->reg->status)&(1 << 2))){
__msdelay(1);
}
if (timeout <= 0)
goto out;
buff[i] = readl(mmchost->database);
timeout = 1000;
}
} else {
buff = (unsigned int *)data->src;
for (i=0; i<(byte_cnt>>2); i++) {
while(--timeout && (readl(&mmchost->reg->status)&(1 << 3))){
__msdelay(1);
}
if (timeout <= 0)
goto out;
writel(buff[i], mmchost->database);
timeout = 1000;
}
}
out:
if (timeout <= 0){
MMCINFO("transfer by cpu failed\n");
return -1;
}
return 0;
}
static int mmc_trans_data_by_dma(struct mmc *mmc, struct mmc_data *data)
{
struct sunxi_mmc_host* mmchost = (struct sunxi_mmc_host *)mmc->priv;
struct sunxi_mmc_des *pdes = mmchost->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, (unsigned long)byte_cnt);
for (i=0; i < buff_frag_num; i++, des_idx++) {
memset((void*)&pdes[des_idx], 0, sizeof(struct sunxi_mmc_des));
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;
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 {
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, (u32)&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, sizeof(struct sunxi_mmc_des) * (des_idx+1));
__asm("DSB");
__asm("ISB");
/*
* 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(&mmchost->reg->gctrl);
writel(rval|(1 << 5)|(1 << 2), &mmchost->reg->gctrl); /* dma enable */
writel((1 << 0), &mmchost->reg->dmac); /* idma reset */
while(readl(&mmchost->reg->dmac)& 0x1) {}; /* wait idma reset done */
writel((1 << 1) | (1 << 7), &mmchost->reg->dmac); /* idma on */
rval = readl(&mmchost->reg->idie) & (~3);
if (data->flags & MMC_DATA_WRITE)
rval |= (1 << 0);
else
rval |= (1 << 1);
writel(rval, &mmchost->reg->idie);
writel((unsigned long)pdes, &mmchost->reg->dlba);
if (mmchost->mmc_no == 2)
writel((3U<<28)|(15U<<16)|240, &mmchost->reg->ftrglevel); /* burst-16, rx/tx trigger level=15/240 */
else
writel((2U<<28)|(7U<<16)|248, &mmchost->reg->ftrglevel); /* burst-8, rx/tx trigger level=7/248 */
return 0;
}
static int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd,
struct mmc_data *data)
{
struct sunxi_mmc_host* mmchost = (struct sunxi_mmc_host *)mmc->priv;
unsigned int cmdval = 0x80000000;
signed int timeout = 0;
int error = 0;
unsigned int status = 0;
unsigned int usedma = 0;
unsigned int bytecnt = 0;
if (mmchost->fatal_err) {
MMCINFO("mmc %d Found fatal err,so no send cmd\n",mmchost->mmc_no);
return -1;
}
if (cmd->resp_type & MMC_RSP_BUSY)
MMCDBG("mmc %d mmc cmd %d check rsp busy\n", mmchost->mmc_no,cmd->cmdidx);
if ((cmd->cmdidx == 12)&&!(cmd->flags&MMC_CMD_MANUAL)){
MMCDBG("note we don't send stop cmd,only check busy here\n");
timeout = 500*1000;
do {
status = readl(&mmchost->reg->status);
if (!timeout--) {
error = -1;
MMCINFO("mmc %d cmd12 busy timeout\n",mmchost->mmc_no);
goto out;
}
__usdelay(1);
} while (status & (1 << 9));
return 0;
}
/*
* CMDREG
* CMD[5:0] : Command index
* CMD[6] : Has response
* CMD[7] : Long response
* CMD[8] : Check response CRC
* CMD[9] : Has data
* CMD[10] : Write
* CMD[11] : Steam mode
* CMD[12] : Auto stop
* CMD[13] : Wait previous over
* CMD[14] : About cmd
* CMD[15] : Send initialization
* CMD[21] : Update clock
* CMD[31] : Load cmd
*/
if (!cmd->cmdidx)
cmdval |= (1 << 15);
if (cmd->resp_type & MMC_RSP_PRESENT)
cmdval |= (1 << 6);
if (cmd->resp_type & MMC_RSP_136)
cmdval |= (1 << 7);
if (cmd->resp_type & MMC_RSP_CRC)
cmdval |= (1 << 8);
if (data) {
if ((ulong)data->dest & 0x3) {
MMCINFO("mmc %d dest is not 4 byte align: 0x%08lx\n",mmchost->mmc_no, (ulong)data->dest);
error = -1;
goto out;
}
cmdval |= (1 << 9) | (1 << 13);
if (data->flags & MMC_DATA_WRITE)
cmdval |= (1 << 10);
if (data->blocks > 1&&!(cmd->flags&MMC_CMD_MANUAL))
cmdval |= (1 << 12);
writel(data->blocksize, &mmchost->reg->blksz);
writel(data->blocks * data->blocksize, &mmchost->reg->bytecnt);
} else {
if ((cmd->cmdidx == 12)&&(cmd->flags&MMC_CMD_MANUAL)) {
cmdval |= 1<<14;//stop current data transferin progress.
cmdval &= ~(1 << 13);//Send command at once, even if previous data transfer has notcompleted
}
}
MMCDBG("mmc %d, cmd %d(0x%08x), arg 0x%08x\n",
mmchost->mmc_no, cmd->cmdidx, cmdval|cmd->cmdidx, cmd->cmdarg);
writel(cmd->cmdarg, &mmchost->reg->arg);
if (!data)
writel(cmdval|cmd->cmdidx, &mmchost->reg->cmd);
/*
* transfer data and check status
* STATREG[2] : FIFO empty
* STATREG[3] : FIFO full
*/
if (data) {
int ret = 0;
bytecnt = data->blocksize * data->blocks;
MMCDBG("mmc %d trans data %d bytes\n",mmchost->mmc_no, bytecnt);
#ifdef CONFIG_MMC_SUNXI_USE_DMA
if (bytecnt > 64) {
#else
if (0) {
#endif
usedma = 1;
writel(readl(&mmchost->reg->gctrl)&(~0x80000000), &mmchost->reg->gctrl);
ret = mmc_trans_data_by_dma(mmc, data);
writel(cmdval|cmd->cmdidx, &mmchost->reg->cmd);
} else {
writel(readl(&mmchost->reg->gctrl)|0x80000000, &mmchost->reg->gctrl);
writel(cmdval|cmd->cmdidx, &mmchost->reg->cmd);
ret = mmc_trans_data_by_cpu(mmc, data);
}
if (ret) {
MMCINFO("mmc %d Transfer failed\n",mmchost->mmc_no);
error = readl(&mmchost->reg->rint) & 0xbfc2;
if(!error)
error = 0xffffffff;
goto out;
}
}
timeout = 1000;
do {
status = readl(&mmchost->reg->rint);
if (!timeout-- || (status & 0xbfc2)) {
error = status & 0xbfc2;
if(!error)
error = 0xffffffff;//represet software timeout
MMCMSG(mmc, "mmc %d cmd %d timeout, err %x\n",mmchost->mmc_no, cmd->cmdidx, error);
goto out;
}
__usdelay(1);
} while (!(status&0x4));
if (data) {
unsigned done = 0;
timeout = usedma ? (50*bytecnt/25) : 0xffffff;//0.04us(25M)*2(4bit width)*25()
if(timeout < 0xffffff){
timeout = 0xffffff;
}
MMCDBG("mmc %d cacl timeout %x\n",mmchost->mmc_no, timeout);
do {
status = readl(&mmchost->reg->rint);
if (!timeout-- || (status & 0xbfc2)) {
error = status & 0xbfc2;
if(!error)
error = 0xffffffff;//represet software timeout
MMCMSG(mmc, "mmc %d data timeout %x\n",mmchost->mmc_no, error);
goto out;
}
if ((timeout == 0xFF0000) && mmc->do_tuning && (data->flags&MMC_DATA_READ) /*(bytecnt==512)*/) {
error = 0xffffffff;//represet software timeout
MMCMSG(mmc, "mmc %d data timeout %x -----------err\n",mmchost->mmc_no, error);
goto out;
}
if ((data->blocks > 1)&&!(cmd->flags&MMC_CMD_MANUAL))//not wait auto stop when MMC_CMD_MANUAL is set
{
if (usedma)
done = ((status & (1<<14)) && (readl(&mmchost->reg->idst) & 0x3)) ? 1 : 0;
else
done = status & (1<<14);
}
else
{
if (usedma)
done = ((status & (1<<3)) && (readl(&mmchost->reg->idst) & 0x3)) ? 1 : 0;
else
done = status & (1<<3);
}
__usdelay(1);
} while (!done);
}
if (cmd->resp_type & MMC_RSP_BUSY) {
if ((cmd->cmdidx == MMC_CMD_ERASE)
|| ((cmd->cmdidx == MMC_CMD_SWITCH)
&&(((cmd->cmdarg>>16)&0xFF) == EXT_CSD_SANITIZE_START)))
timeout = 0x1fffffff;
else
timeout = 500*1000;
do {
status = readl(&mmchost->reg->status);
if (!timeout--) {
error = -1;
MMCINFO("mmc %d busy timeout\n",mmchost->mmc_no);
goto out;
}
__usdelay(1);
} while (status & (1 << 9));
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 us \n",__FUNCTION__,
cmd->cmdidx, 0x1fffffff-timeout);
}
if (cmd->resp_type & MMC_RSP_136) {
cmd->response[0] = readl(&mmchost->reg->resp3);
cmd->response[1] = readl(&mmchost->reg->resp2);
cmd->response[2] = readl(&mmchost->reg->resp1);
cmd->response[3] = readl(&mmchost->reg->resp0);
MMCDBG("mmc %d mmc resp 0x%08x 0x%08x 0x%08x 0x%08x\n",
mmchost->mmc_no,
cmd->response[3], cmd->response[2],
cmd->response[1], cmd->response[0]);
} else {
cmd->response[0] = readl(&mmchost->reg->resp0);
MMCDBG("mmc %d mmc resp 0x%08x\n",mmchost->mmc_no, cmd->response[0]);
}
out:
if(error){
mmchost->raw_int_bak = readl(&mmchost->reg->rint )& 0xbfc2;
mmc_dump_errinfo(mmchost,cmd);
}
if (data && usedma) {
/* IDMASTAREG
* IDST[0] : idma tx int
* IDST[1] : idma rx int
* IDST[2] : idma fatal bus error
* IDST[4] : idma descriptor invalid
* IDST[5] : idma error summary
* IDST[8] : idma normal interrupt sumary
* IDST[9] : idma abnormal interrupt sumary
*/
status = readl(&mmchost->reg->idst);
writel(status, &mmchost->reg->idst);
writel(0, &mmchost->reg->idie);
writel(0, &mmchost->reg->dmac);
writel(readl(&mmchost->reg->gctrl)&(~(1 << 5)), &mmchost->reg->gctrl);
}
if (error) {
/* 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.
*/
if (mmc->do_tuning && data && (data->flags&MMC_DATA_READ) && (bytecnt==512)) {
writel(readl(&mmchost->reg->gctrl)|0x80000000, &mmchost->reg->gctrl);
writel(0xdeb, &mmchost->reg->dbgc);
timeout = 1000;
MMCMSG(mmc, "Read remain data\n");
while (readl(&mmchost->reg->bbcr)<512) {
unsigned int tmp = readl(mmchost->database);
tmp = tmp;
MMCDBG("Read data 0x%x, bbcr 0x%x\n", tmp, readl(&mmchost->reg->bbcr));
__usdelay(1);
if (!(timeout--)) {
MMCMSG(mmc, "Read remain data timeout\n");
break;
}
}
}
writel(0x7, &mmchost->reg->gctrl);
while(readl(&mmchost->reg->gctrl)&0x7) { };
{
mmc_save_regs(mmchost);
mmc_clk_io_onoff(mmchost->mmc_no, 0, 0);
MMCMSG(mmc, "mmc %d close bus gating and reset\n", mmchost->mmc_no);
mmc_clk_io_onoff(mmchost->mmc_no, 1, 0);
mmc_restore_regs(mmchost);
writel(0x7, &mmchost->reg->gctrl);
while(readl(&mmchost->reg->gctrl)&0x7) { };
}
mmc_update_clk(mmchost);
MMCMSG(mmc, "mmc %d mmc cmd %d err 0x%08x\n", mmchost->mmc_no, cmd->cmdidx, error);
}
writel(0xffffffff, &mmchost->reg->rint);
if (data && (data->flags&MMC_DATA_READ)) {
unsigned char *buff = (unsigned char *)data->dest;
unsigned byte_cnt = data->blocksize * data->blocks;
flush_cache((unsigned long)buff, (unsigned long)byte_cnt);
MMCDBG("invald cache after read complete\n");
}
if (error)
return -1;
else
return 0;
}
int sunxi_decide_rty(struct mmc *mmc, int err_no, uint rst_cnt)
{
struct sunxi_mmc_host* mmchost = (struct sunxi_mmc_host *)mmc->priv;
unsigned tmode = mmchost->timing_mode;
u32 spd_md, freq;
u8 *sdly;
u8 tm1_retry_gap = 1;
u8 tm3_retry_gap = 8;
u8 tm4_retry_gap = 8;
if (rst_cnt)
{
mmchost->retry_cnt = 0;
}
if (err_no && (!(err_no & SDXC_RespTimeout)||(err_no==0xffffffff)))
{
mmchost->retry_cnt++;
if (tmode == SUNXI_MMC_TIMING_MODE_1)
{
spd_md = mmchost->tm1.cur_spd_md;
freq = mmchost->tm1.cur_freq;
sdly = &mmchost->tm1.sdly[spd_md*MAX_CLK_FREQ_NUM+freq];
if (mmchost->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 = mmchost->tm3.cur_spd_md;
freq = mmchost->tm3.cur_freq;
sdly = &mmchost->tm3.sdly[spd_md*MAX_CLK_FREQ_NUM+freq];
if (mmchost->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 = mmchost->tm4.cur_spd_md;
freq = mmchost->tm4.cur_freq;
if (spd_md == HS400)
sdly = &mmchost->tm4.dsdly[freq];
else
sdly = &mmchost->tm4.sdly[spd_md*MAX_CLK_FREQ_NUM+freq];
MMCINFO("Current spd_md %d freq_id %d sldy %d\n", spd_md, freq, *sdly);
if (mmchost->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 retry times\n");
return -1;
}
}
mmchost->raw_int_bak = 0;
return 0;
}
MMCDBG("rto or no error or software timeout,no need retry\n");
return -1;
}
int sunxi_detail_errno(struct mmc *mmc)
{
struct sunxi_mmc_host* mmchost = (struct sunxi_mmc_host *)mmc->priv;
u32 err_no = mmchost->raw_int_bak;
mmchost->raw_int_bak = 0;
return err_no;
}
static const struct mmc_ops sunxi_mmc_ops = {
.send_cmd = mmc_send_cmd,
.set_ios = mmc_set_ios,
.init = mmc_core_init,
.decide_retry = sunxi_decide_rty,
.get_detail_errno = sunxi_detail_errno,
.update_phase = mmc_update_phase,
};
int sunxi_mmc_init(int sdc_no)
{
struct sunxi_mmc_host *host = NULL;
MMCINFO("mmc driver ver %s\n", DRIVER_VER);
if ((sdc_no != 2) && (sdc_no != 0)) {
MMCINFO("sdc_on error, %d\n", sdc_no);
return -1;
}
memset(&mmc_host[sdc_no], 0, sizeof(struct sunxi_mmc_host));
host = &mmc_host[sdc_no];
memset(&mmc_host_reg_bak[sdc_no], 0, sizeof(struct sunxi_mmc));
host->reg_bak = &mmc_host_reg_bak[sdc_no];
if (sdc_no == 2) {
host->cfg.platform_caps.odly_spd_freq = &ext_odly_spd_freq[0];
host->cfg.platform_caps.sdly_spd_freq = &ext_sdly_spd_freq[0];
} else if (sdc_no == 0) {
host->cfg.platform_caps.odly_spd_freq = &ext_odly_spd_freq_sdc0[0];
host->cfg.platform_caps.sdly_spd_freq = &ext_sdly_spd_freq_sdc0[0];
}
host->cfg.name = "SUNXI SD/MMC";
host->cfg.host_no = sdc_no;
host->cfg.ops = &sunxi_mmc_ops;
host->cfg.voltages = MMC_VDD_32_33 | MMC_VDD_33_34
| MMC_VDD_27_28 | MMC_VDD_28_29 | MMC_VDD_29_30
| MMC_VDD_30_31 | MMC_VDD_31_32 | MMC_VDD_34_35
| MMC_VDD_35_36;
host->cfg.b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT;
if (sdc_no == 0) {
host->cfg.f_min = 400000;
host->cfg.f_max = 50000000;
} else if (sdc_no == 2) {
host->cfg.f_min = 400000;
host->cfg.f_max = 200000000;
}
if ((sdc_no == 0) || (sdc_no == 1))
host->timing_mode = SUNXI_MMC_TIMING_MODE_1; //SUNXI_MMC_TIMING_MODE_3
else if (sdc_no == 2)
host->timing_mode = SUNXI_MMC_TIMING_MODE_4;
host->pdes = malloc(256 * 1024);
if (host->pdes == NULL) {
MMCINFO("%s: get mem for descriptor failed\n", __FUNCTION__);
return -1;
}
mmc_clear_timing_para(sdc_no);
mmc_init_default_timing_para(sdc_no);
mmc_get_para_from_fex(sdc_no);
mmc_get_para_from_dtb(sdc_no);
mmc_update_timing_para(sdc_no);
mmc_resource_init(sdc_no);
mmc_clk_io_onoff(sdc_no, 1, 1);
host->cfg.host_caps = MMC_MODE_4BIT | MMC_MODE_HS | MMC_MODE_HC \
| MMC_MODE_HS_52MHz | MMC_MODE_DDR_52MHz;
if (host->cfg.host_no == 2)
host->cfg.host_caps |= MMC_MODE_8BIT;
if (host->cfg.platform_caps.io_is_1v8) {
host->cfg.host_caps |= MMC_MODE_HS200;
if (host->cfg.host_caps & MMC_MODE_8BIT)
host->cfg.host_caps |= MMC_MODE_HS400;
}
if (host->cfg.platform_caps.host_caps_mask) {
u32 mask = host->cfg.platform_caps.host_caps_mask;
if (mask & DRV_PARA_DISABLE_MMC_MODE_HS400)
host->cfg.host_caps &= (~DRV_PARA_DISABLE_MMC_MODE_HS400);
if (mask & DRV_PARA_DISABLE_MMC_MODE_HS200)
host->cfg.host_caps &= (~(DRV_PARA_DISABLE_MMC_MODE_HS200
| DRV_PARA_DISABLE_MMC_MODE_HS400));
if (mask & DRV_PARA_DISABLE_MMC_MODE_DDR_52MHz)
host->cfg.host_caps &= (~(DRV_PARA_DISABLE_MMC_MODE_DDR_52MHz
| DRV_PARA_DISABLE_MMC_MODE_HS400
| DRV_PARA_DISABLE_MMC_MODE_HS200));
if (mask & DRV_PARA_DISABLE_MMC_MODE_HS_52MHz)
host->cfg.host_caps &= (~(DRV_PARA_DISABLE_MMC_MODE_HS_52MHz
| DRV_PARA_DISABLE_MMC_MODE_DDR_52MHz
| DRV_PARA_DISABLE_MMC_MODE_HS400
| DRV_PARA_DISABLE_MMC_MODE_HS200));
if (mask & DRV_PARA_DISABLE_MMC_MODE_8BIT)
host->cfg.host_caps &= (~(DRV_PARA_DISABLE_MMC_MODE_8BIT
| DRV_PARA_DISABLE_MMC_MODE_HS400));
}
MMCDBG("mmc->host_caps %x\n", host->cfg.host_caps);
host->mmc = mmc_create(&host->cfg, host);
if (host->mmc == NULL) {
MMCINFO("%s: register mmc %d failed\n", __FUNCTION__, sdc_no);
return -1;
} else
MMCDBG("%s: register mmc %d ok\n", __FUNCTION__, __LINE__);
return 0;
}
int sunxi_mmc_exit(int sdc_no)
{
mmc_clk_io_onoff(sdc_no, 0, 1);
//mmc_unregister(sdc_no);
memset(&mmc_host[sdc_no], 0, sizeof(struct sunxi_mmc_host));
MMCDBG("sunxi mmc%d exit\n", sdc_no);
return 0;
}