1982 lines
62 KiB
C
1982 lines
62 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/cpu.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 <smc.h>
|
|
#include <sys_config.h>
|
|
|
|
#include "mmc_def.h"
|
|
|
|
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;
|
|
|
|
printf("dump %s registers:", name);
|
|
for (i=0; i<len; i+=4) {
|
|
if (!(i&0xf))
|
|
printf("\n0x%p : ", base + i);
|
|
printf("0x%08x ", smc_readl((uint)base + i));
|
|
}
|
|
printf("\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)
|
|
|
|
|
|
#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
|
|
|
|
struct sunxi_mmc_clk_dly {
|
|
u32 mode;
|
|
u32 oclk_dly;
|
|
u32 sclk_dly;
|
|
};
|
|
|
|
|
|
#define MMC_CLK_SAMPLY_POINIT 8
|
|
struct sunxi_mmc_sdly_rty {
|
|
u32 mode;
|
|
u32 sclk_dly[MMC_CLK_SAMPLY_POINIT];
|
|
u32 rty_cnt;
|
|
};
|
|
|
|
struct sunxi_mmc_des {
|
|
u32 :1,
|
|
dic :1, /* disable interrupt on completion */
|
|
last_des :1, /* 1-this data buffer is the last buffer */
|
|
first_des :1, /* 1-data buffer is the first buffer,
|
|
0-data buffer contained in the next descriptor is 1st buffer */
|
|
des_chain :1, /* 1-the 2nd address in the descriptor is the next descriptor address */
|
|
end_of_ring :1, /* 1-last descriptor flag when using dual data buffer in descriptor */
|
|
:24,
|
|
card_err_sum :1, /* transfer error flag */
|
|
own :1; /* des owner:1-idma owns it, 0-host owns it */
|
|
#if defined CONFIG_SUN4I
|
|
#define SDXC_DES_NUM_SHIFT 12
|
|
#define SDXC_DES_BUFFER_MAX_LEN (1 << SDXC_DES_NUM_SHIFT)
|
|
u32 data_buf1_sz :13,
|
|
data_buf2_sz :13,
|
|
:6;
|
|
#elif defined CONFIG_ARCH_SUN7I
|
|
#define SDXC_DES_NUM_SHIFT 15
|
|
#define SDXC_DES_BUFFER_MAX_LEN (1 << SDXC_DES_NUM_SHIFT)
|
|
u32 data_buf1_sz :16,
|
|
data_buf2_sz :16;
|
|
#else
|
|
#define SDXC_DES_NUM_SHIFT 15
|
|
#define SDXC_DES_BUFFER_MAX_LEN (1 << SDXC_DES_NUM_SHIFT)
|
|
u32 data_buf1_sz :16,
|
|
data_buf2_sz :16;
|
|
#endif
|
|
u32 buf_addr_ptr1;
|
|
u32 buf_addr_ptr2;
|
|
};
|
|
|
|
struct sunxi_mmc_host {
|
|
unsigned mmc_no;
|
|
unsigned hclkbase;
|
|
#ifndef CONFIG_ARCH_SUN7I
|
|
unsigned hclkrst;
|
|
#endif
|
|
unsigned mclkbase;
|
|
unsigned database;
|
|
#if defined(CONFIG_ARCH_SUN9IW1P1)
|
|
unsigned commreg;
|
|
#endif
|
|
unsigned fatal_err;
|
|
unsigned mod_clk;
|
|
struct sunxi_mmc *reg;
|
|
struct sunxi_mmc_des* pdes;
|
|
/*sample delay and output deley setting*/
|
|
struct sunxi_mmc_clk_dly mmc_clk_dly[MMC_CLK_MOD_NUM];
|
|
struct sunxi_mmc_sdly_rty mmc_clk_sdly_rty_tbl[MMC_CLK_MOD_NUM];
|
|
u32 raw_int_bak ;
|
|
u32 samply_mode;
|
|
|
|
};
|
|
|
|
/* support 4 mmc hosts */
|
|
struct mmc mmc_dev[4];
|
|
struct sunxi_mmc_host mmc_host[4];
|
|
|
|
struct sunxi_mmc reg_bakup;
|
|
|
|
void mmc_dump_errinfo(struct sunxi_mmc_host* smc_host, struct mmc_cmd *cmd)
|
|
{
|
|
MMCINFO("smc %d err, cmd %d, %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_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_MMC0_BASE;
|
|
mmchost->mclkbase = CCM_SDC0_SCLK_CTRL;
|
|
break;
|
|
case 1:
|
|
mmchost->reg = (struct sunxi_mmc *)SUNXI_MMC1_BASE;
|
|
mmchost->mclkbase = CCM_SDC1_SCLK_CTRL;
|
|
break;
|
|
case 2:
|
|
mmchost->reg = (struct sunxi_mmc *)SUNXI_MMC2_BASE;
|
|
mmchost->mclkbase = CCM_SDC2_SCLK_CTRL;
|
|
break;
|
|
case 3:
|
|
mmchost->reg = (struct sunxi_mmc *)SUNXI_MMC3_BASE;
|
|
mmchost->mclkbase = CCM_SDC3_SCLK_CTRL;
|
|
break;
|
|
default:
|
|
MMCINFO("Wrong mmc number %d\n", sdc_no);
|
|
break;
|
|
}
|
|
#ifdef CONFIG_ARCH_SUN9IW1P1
|
|
mmchost->hclkbase = CCM_AHB0_GATE0_CTRL;
|
|
mmchost->hclkrst = CCM_AHB0_RST_REG0;
|
|
mmchost->commreg = SUNXI_MMC_COMMON_BASE + sdc_no*4;
|
|
#elif defined CONFIG_ARCH_SUN7I
|
|
mmchost->hclkbase = CCM_AHB_GATE0_CTRL;
|
|
#else
|
|
mmchost->hclkbase = CCM_AHB1_GATE0_CTRL;
|
|
mmchost->hclkrst = CCM_AHB1_RST_REG0;
|
|
#endif
|
|
mmchost->database = (unsigned int)mmchost->reg + MMC_REG_FIFO_OS;
|
|
mmchost->mmc_no = sdc_no;
|
|
|
|
#ifdef CONFIG_ARCH_SUN9IW1P1
|
|
mmchost->mmc_clk_dly[MMC_CLK_25M].mode = MMC_CLK_25M;
|
|
mmchost->mmc_clk_dly[MMC_CLK_25M].oclk_dly = 0;
|
|
mmchost->mmc_clk_dly[MMC_CLK_25M].sclk_dly = 5;
|
|
|
|
mmchost->mmc_clk_dly[MMC_CLK_50M].mode = MMC_CLK_50M;
|
|
mmchost->mmc_clk_dly[MMC_CLK_50M].oclk_dly = 5;
|
|
mmchost->mmc_clk_dly[MMC_CLK_50M].sclk_dly = 4;
|
|
|
|
#elif defined CONFIG_ARCH_SUN8IW6P1
|
|
mmchost->mmc_clk_dly[MMC_CLK_25M].mode = MMC_CLK_25M;
|
|
mmchost->mmc_clk_dly[MMC_CLK_25M].oclk_dly = 0;
|
|
mmchost->mmc_clk_dly[MMC_CLK_25M].sclk_dly = 7;
|
|
|
|
mmchost->mmc_clk_dly[MMC_CLK_50M].mode = MMC_CLK_50M;
|
|
mmchost->mmc_clk_dly[MMC_CLK_50M].oclk_dly = 6;
|
|
mmchost->mmc_clk_dly[MMC_CLK_50M].sclk_dly = 7;
|
|
#else
|
|
/**init retry table**/
|
|
mmchost->mmc_clk_sdly_rty_tbl[MMC_CLK_400K].mode = MMC_CLK_400K;
|
|
mmchost->mmc_clk_sdly_rty_tbl[MMC_CLK_400K].sclk_dly[0]= 0;
|
|
mmchost->mmc_clk_sdly_rty_tbl[MMC_CLK_400K].sclk_dly[1]= 7;
|
|
mmchost->mmc_clk_sdly_rty_tbl[MMC_CLK_400K].sclk_dly[2]= 6;
|
|
mmchost->mmc_clk_sdly_rty_tbl[MMC_CLK_400K].sclk_dly[3]= 5;
|
|
mmchost->mmc_clk_sdly_rty_tbl[MMC_CLK_400K].sclk_dly[4]= 4;
|
|
mmchost->mmc_clk_sdly_rty_tbl[MMC_CLK_400K].sclk_dly[5]= 3;
|
|
mmchost->mmc_clk_sdly_rty_tbl[MMC_CLK_400K].sclk_dly[6]= 2;
|
|
mmchost->mmc_clk_sdly_rty_tbl[MMC_CLK_400K].sclk_dly[7]= 1;
|
|
|
|
mmchost->mmc_clk_sdly_rty_tbl[MMC_CLK_25M].mode = MMC_CLK_25M;
|
|
mmchost->mmc_clk_sdly_rty_tbl[MMC_CLK_25M].sclk_dly[0]= 5;
|
|
mmchost->mmc_clk_sdly_rty_tbl[MMC_CLK_25M].sclk_dly[1]= 6;
|
|
mmchost->mmc_clk_sdly_rty_tbl[MMC_CLK_25M].sclk_dly[2]= 4;
|
|
mmchost->mmc_clk_sdly_rty_tbl[MMC_CLK_25M].sclk_dly[3]= 7;
|
|
mmchost->mmc_clk_sdly_rty_tbl[MMC_CLK_25M].sclk_dly[4]= 3;
|
|
mmchost->mmc_clk_sdly_rty_tbl[MMC_CLK_25M].sclk_dly[5]= 2;
|
|
mmchost->mmc_clk_sdly_rty_tbl[MMC_CLK_25M].sclk_dly[6]= 1;
|
|
mmchost->mmc_clk_sdly_rty_tbl[MMC_CLK_25M].sclk_dly[7]= 0;
|
|
|
|
mmchost->mmc_clk_sdly_rty_tbl[MMC_CLK_50M].mode = MMC_CLK_50M;
|
|
mmchost->mmc_clk_sdly_rty_tbl[MMC_CLK_50M].sclk_dly[0]= 4;
|
|
mmchost->mmc_clk_sdly_rty_tbl[MMC_CLK_50M].sclk_dly[1]= 5;
|
|
mmchost->mmc_clk_sdly_rty_tbl[MMC_CLK_50M].sclk_dly[2]= 3;
|
|
mmchost->mmc_clk_sdly_rty_tbl[MMC_CLK_50M].sclk_dly[3]= 6;
|
|
mmchost->mmc_clk_sdly_rty_tbl[MMC_CLK_50M].sclk_dly[4]= 2;
|
|
mmchost->mmc_clk_sdly_rty_tbl[MMC_CLK_50M].sclk_dly[5]= 7;
|
|
mmchost->mmc_clk_sdly_rty_tbl[MMC_CLK_50M].sclk_dly[6]= 1;
|
|
mmchost->mmc_clk_sdly_rty_tbl[MMC_CLK_50M].sclk_dly[7]= 0;
|
|
|
|
|
|
|
|
mmchost->mmc_clk_dly[MMC_CLK_400K].mode = MMC_CLK_400K;
|
|
mmchost->mmc_clk_dly[MMC_CLK_400K].oclk_dly = 0;
|
|
mmchost->mmc_clk_dly[MMC_CLK_400K].sclk_dly = mmchost->mmc_clk_sdly_rty_tbl[MMC_CLK_400K].sclk_dly[0];
|
|
|
|
mmchost->mmc_clk_dly[MMC_CLK_25M].mode = MMC_CLK_25M;
|
|
mmchost->mmc_clk_dly[MMC_CLK_25M].oclk_dly = 0;
|
|
mmchost->mmc_clk_dly[MMC_CLK_25M].sclk_dly = mmchost->mmc_clk_sdly_rty_tbl[MMC_CLK_25M].sclk_dly[0];
|
|
|
|
mmchost->mmc_clk_dly[MMC_CLK_50M].mode = MMC_CLK_50M;
|
|
mmchost->mmc_clk_dly[MMC_CLK_50M].oclk_dly = 3;
|
|
mmchost->mmc_clk_dly[MMC_CLK_50M].sclk_dly = mmchost->mmc_clk_sdly_rty_tbl[MMC_CLK_50M].sclk_dly[0];
|
|
|
|
|
|
mmchost->mmc_clk_dly[MMC_CLK_100M].mode = MMC_CLK_100M;
|
|
mmchost->mmc_clk_dly[MMC_CLK_100M].oclk_dly = 1;
|
|
mmchost->mmc_clk_dly[MMC_CLK_100M].sclk_dly = 4;
|
|
|
|
mmchost->mmc_clk_dly[MMC_CLK_200M].mode = MMC_CLK_200M;
|
|
mmchost->mmc_clk_dly[MMC_CLK_200M].oclk_dly = 1;
|
|
mmchost->mmc_clk_dly[MMC_CLK_200M].sclk_dly = 4;
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void get_fex_para(int sdc_no)
|
|
{
|
|
int rval;
|
|
int ret = 0;
|
|
struct sunxi_mmc_host* mmchost = &mmc_host[sdc_no];
|
|
|
|
if(sdc_no == 0)
|
|
{
|
|
gpio_request_simple("card0_boot_para", NULL);
|
|
|
|
ret = script_parser_fetch("card0_boot_para","sdc_wipe", &rval, 1);
|
|
if(ret < 0)
|
|
MMCINFO("get sdc_phy_wipe fail.\n");
|
|
else {
|
|
if (rval & DRV_PARA_DISABLE_SECURE_WIPE) {
|
|
MMCINFO("disable driver secure wipe operation.\n");
|
|
mmc_dev[sdc_no].drv_wipe_feature |= DRV_PARA_DISABLE_SECURE_WIPE;
|
|
} else if (rval & DRV_PARA_DISABLE_EMMC_SANITIZE) {
|
|
MMCINFO("disable emmc sanitize feature.\n");
|
|
mmc_dev[sdc_no].drv_wipe_feature |= DRV_PARA_DISABLE_EMMC_SANITIZE;
|
|
} else if (rval & DRV_PARA_DISABLE_EMMC_SECURE_PURGE) {
|
|
MMCINFO("disable emmc secure purge feature.\n");
|
|
mmc_dev[sdc_no].drv_wipe_feature |= DRV_PARA_DISABLE_EMMC_SECURE_PURGE;
|
|
} else if (rval & DRV_PARA_DISABLE_EMMC_TRIM) {
|
|
MMCINFO("disable emmc trim feature.\n");
|
|
mmc_dev[sdc_no].drv_wipe_feature |= DRV_PARA_DISABLE_EMMC_TRIM;
|
|
}
|
|
}
|
|
|
|
ret = script_parser_fetch("card0_boot_para", "sdc_erase", &rval, 1);
|
|
if(ret < 0)
|
|
MMCINFO("get sdc0 sdc_erase fail.\n");
|
|
else {
|
|
if (rval & DRV_PARA_DISABLE_EMMC_ERASE) {
|
|
MMCINFO("disable emmc erase.\n");
|
|
mmc_dev[sdc_no].drv_erase_feature |= DRV_PARA_DISABLE_EMMC_ERASE;
|
|
} else if (rval & DRV_PARA_ENABLE_EMMC_SANITIZE_WHEN_ERASE) {
|
|
MMCINFO("enable emmc sanitize when erase.\n");
|
|
mmc_dev[sdc_no].drv_erase_feature |= DRV_PARA_ENABLE_EMMC_SANITIZE_WHEN_ERASE;
|
|
}
|
|
}
|
|
|
|
ret = script_parser_fetch("card0_boot_para","sdc_f_max", &rval, 1);
|
|
if(ret < 0)
|
|
MMCINFO("get sdc_f_max fail,use default %dHz\n",mmc_dev[sdc_no].f_max);
|
|
else{
|
|
if((rval>mmc_dev[sdc_no].f_max)||(rval<mmc_dev[sdc_no].f_min)){
|
|
MMCINFO("input sdc_f_max wrong ,use default sdc_f_max %d (min %d)\n",
|
|
mmc_dev[sdc_no].f_max, mmc_dev[sdc_no].f_min);
|
|
}else{
|
|
mmc_dev[sdc_no].f_max = rval;
|
|
MMCINFO("get sdc_f_max ok, sdc_f_max = %d\n", mmc_dev[sdc_no].f_max);
|
|
}
|
|
}
|
|
|
|
|
|
ret = script_parser_fetch("card0_boot_para","sdc_ex_dly_used", &rval, 1);
|
|
if(ret < 0){
|
|
MMCINFO("get sdc_ex_dly_used fail,use default dly\n");
|
|
return;
|
|
}else{
|
|
if(rval == 1){ //maual sample point from fex
|
|
MMCINFO("get sdc_ex_dly_used ok\n");
|
|
MMCINFO("use manual sample point in fex\n");
|
|
}else{
|
|
MMCINFO("undefined value %d,use default dly\n",rval);
|
|
return;
|
|
}
|
|
}
|
|
/*************************25M dly*************************************/
|
|
ret = script_parser_fetch("card0_boot_para","sdc_odly_25M", &rval, 1);
|
|
if(ret < 0)
|
|
MMCINFO("get sdc_odly_25M fail,use default dly %d\n",mmchost->mmc_clk_dly[MMC_CLK_25M].oclk_dly);
|
|
else{
|
|
if((rval>7)||(rval<0)){
|
|
MMCINFO("input sdc_odly_25M wrong ,use default dly %d\n",mmchost->mmc_clk_dly[MMC_CLK_25M].oclk_dly);
|
|
}else{
|
|
mmchost->mmc_clk_dly[MMC_CLK_25M].oclk_dly = rval;
|
|
MMCINFO("get sdc_odly_25M ok, odly = %d\n", mmchost->mmc_clk_dly[MMC_CLK_25M].oclk_dly);
|
|
}
|
|
}
|
|
|
|
ret = script_parser_fetch("card0_boot_para","sdc_sdly_25M", &rval, 1);
|
|
if(ret < 0)
|
|
MMCINFO("get sdc_sdly_25M fail,use default dly %d\n",mmchost->mmc_clk_dly[MMC_CLK_25M].sclk_dly);
|
|
else{
|
|
if((rval>7)||(rval<0)){
|
|
MMCINFO("input sdc_sdly_25M wrong ,use default dly %d\n",mmchost->mmc_clk_dly[MMC_CLK_25M].sclk_dly);
|
|
}else{
|
|
mmchost->mmc_clk_dly[MMC_CLK_25M].sclk_dly = rval;
|
|
MMCINFO("get sdc_sdly_25M ok, sdly = %d\n", mmchost->mmc_clk_dly[MMC_CLK_25M].sclk_dly);
|
|
}
|
|
}
|
|
|
|
/*************************50M dly*************************************/
|
|
ret = script_parser_fetch("card0_boot_para","sdc_odly_50M", &rval, 1);
|
|
if(ret < 0)
|
|
MMCINFO("get sdc_odly_50M fail,use default dly %d\n",mmchost->mmc_clk_dly[MMC_CLK_50M].oclk_dly);
|
|
else{
|
|
if((rval>7)||(rval<0)){
|
|
MMCINFO("input sdc_odly_50M wrong, use default dly %d\n", mmchost->mmc_clk_dly[MMC_CLK_50M].oclk_dly);
|
|
}else{
|
|
mmchost->mmc_clk_dly[MMC_CLK_50M].oclk_dly = rval;
|
|
MMCINFO("get sdc_odly_50M ok, odly = %d\n", mmchost->mmc_clk_dly[MMC_CLK_50M].oclk_dly);
|
|
}
|
|
}
|
|
|
|
ret = script_parser_fetch("card0_boot_para","sdc_sdly_50M", &rval, 1);
|
|
if(ret < 0)
|
|
MMCINFO("get sdc_sdly_50M fail,use default dly %d\n",mmchost->mmc_clk_dly[MMC_CLK_50M].sclk_dly);
|
|
else{
|
|
if((rval>7)||(rval<0)){
|
|
MMCINFO("input sdc_sdly_50M wrong, use default dly %d\n", mmchost->mmc_clk_dly[MMC_CLK_50M].sclk_dly);
|
|
}else{
|
|
mmchost->mmc_clk_dly[MMC_CLK_50M].sclk_dly = rval;
|
|
MMCINFO("get sdc_sdly_50M ok, sdly = %d\n", mmchost->mmc_clk_dly[MMC_CLK_50M].sclk_dly);
|
|
}
|
|
}
|
|
|
|
ret = script_parser_fetch("card0_boot_para","sdc_test", &rval, 1);
|
|
if (ret < 0) {
|
|
MMCINFO("get sdc_test fail.\n");
|
|
mmc_dev[sdc_no].mmc_test = 0;
|
|
} else {
|
|
MMCINFO("get sdc_test 0x%x\n", rval);
|
|
mmc_dev[sdc_no].mmc_test = rval;
|
|
}
|
|
|
|
}else {// if(sdc_no == 2)
|
|
gpio_request_simple("card2_boot_para", NULL);
|
|
|
|
ret = script_parser_fetch("card2_boot_para","sdc_wipe", &rval, 1);
|
|
if(ret < 0)
|
|
MMCINFO("get sdc_phy_wipe fail.\n");
|
|
else {
|
|
if (rval & DRV_PARA_DISABLE_SECURE_WIPE) {
|
|
MMCINFO("disable driver secure wipe operation.\n");
|
|
mmc_dev[sdc_no].drv_wipe_feature |= DRV_PARA_DISABLE_SECURE_WIPE;
|
|
} else if (rval & DRV_PARA_DISABLE_EMMC_SANITIZE) {
|
|
MMCINFO("disable emmc sanitize feature.\n");
|
|
mmc_dev[sdc_no].drv_wipe_feature |= DRV_PARA_DISABLE_EMMC_SANITIZE;
|
|
} else if (rval & DRV_PARA_DISABLE_EMMC_SECURE_PURGE) {
|
|
MMCINFO("disable emmc secure purge feature.\n");
|
|
mmc_dev[sdc_no].drv_wipe_feature |= DRV_PARA_DISABLE_EMMC_SECURE_PURGE;
|
|
} else if (rval & DRV_PARA_DISABLE_EMMC_TRIM) {
|
|
MMCINFO("disable emmc trim feature.\n");
|
|
mmc_dev[sdc_no].drv_wipe_feature |= DRV_PARA_DISABLE_EMMC_TRIM;
|
|
}
|
|
}
|
|
|
|
ret = script_parser_fetch("card2_boot_para", "sdc_erase", &rval, 1);
|
|
if(ret < 0)
|
|
MMCINFO("get sdc0 sdc_erase fail.\n");
|
|
else {
|
|
if (rval & DRV_PARA_DISABLE_EMMC_ERASE) {
|
|
MMCINFO("disable emmc erase.\n");
|
|
mmc_dev[sdc_no].drv_erase_feature |= DRV_PARA_DISABLE_EMMC_ERASE;
|
|
} else if (rval & DRV_PARA_ENABLE_EMMC_SANITIZE_WHEN_ERASE) {
|
|
MMCINFO("enable emmc sanitize.\n");
|
|
mmc_dev[sdc_no].drv_erase_feature |= DRV_PARA_ENABLE_EMMC_SANITIZE_WHEN_ERASE;
|
|
}
|
|
}
|
|
|
|
ret = script_parser_fetch\
|
|
("card2_boot_para", "sdc_hw_rst", &rval, 1);
|
|
if (ret < 0) {
|
|
MMCINFO("get sdc2 sdc_hw_rst fail.\n");
|
|
mmc_dev[sdc_no].drv_hwrst_feature \
|
|
&= ~DRV_PARA_ENABLE_EMMC_HWRST;
|
|
} else {
|
|
if (rval & DRV_PARA_ENABLE_EMMC_HWRST) {
|
|
MMCINFO("enable emmc hw reset.\n");
|
|
mmc_dev[sdc_no].drv_hwrst_feature \
|
|
|= DRV_PARA_ENABLE_EMMC_HWRST;
|
|
} else {
|
|
MMCINFO("disable emmc hw reset.\n");
|
|
mmc_dev[sdc_no].drv_hwrst_feature\
|
|
&= ~DRV_PARA_ENABLE_EMMC_HWRST;
|
|
}
|
|
}
|
|
|
|
|
|
#if defined(CONFIG_ARCH_SUN8IW5P1) || defined(CONFIG_ARCH_SUN8IW6P1) || defined(CONFIG_ARCH_SUN8IW8P1)||(defined CONFIG_ARCH_SUN8IW7P1)|| (defined CONFIG_ARCH_SUN8IW9P1)
|
|
/*************************sdc_2xmode*************************************/
|
|
ret = script_parser_fetch("card2_boot_para","sdc_2xmode", &rval, 1);
|
|
if(ret < 0)
|
|
{
|
|
mmc_dev[sdc_no].host_func = MMC_NO_FUNC;
|
|
MMCINFO("get sdc_2xmode fail used = %d\n",mmc_dev[sdc_no].host_func);
|
|
}
|
|
else
|
|
{
|
|
if(rval == 1)
|
|
{
|
|
mmc_dev[sdc_no].host_func = MMC_HOST_2XMODE_FUNC;
|
|
}
|
|
else
|
|
{
|
|
mmc_dev[sdc_no].host_func = MMC_NO_FUNC;
|
|
}
|
|
MMCINFO("get sdc_2xmode ok, val = %d\n", mmc_dev[sdc_no].host_func);
|
|
}
|
|
/*************************sdc_ddrmode*************************************/
|
|
ret = script_parser_fetch("card2_boot_para","sdc_ddrmode", &rval, 1);
|
|
if(ret < 0)
|
|
{
|
|
mmc_dev[sdc_no].mmc_func_en.ddr_func_en = 0;
|
|
MMCINFO("get sdc_ddrmode fail used = %d\n",mmc_dev[sdc_no].mmc_func_en.ddr_func_en );
|
|
}
|
|
else
|
|
{
|
|
if(rval == 1)
|
|
{
|
|
mmc_dev[sdc_no].mmc_func_en.ddr_func_en = 1;
|
|
}
|
|
else
|
|
{
|
|
mmc_dev[sdc_no].mmc_func_en.ddr_func_en = 0;
|
|
}
|
|
MMCINFO("get sdc_ddrmode ok, val = %d\n", mmc_dev[sdc_no].mmc_func_en.ddr_func_en );
|
|
}
|
|
#endif
|
|
ret = script_parser_fetch("card2_boot_para","sdc_f_max", &rval, 1);
|
|
if(ret < 0)
|
|
MMCINFO("get sdc_f_max fail,use default %dHz\n", mmc_dev[sdc_no].f_max);
|
|
else {
|
|
if((rval>mmc_dev[sdc_no].f_max)||(rval<mmc_dev[sdc_no].f_min)){
|
|
MMCINFO("input sdc_f_max wrong %d,use default sdc_f_max %d (min %d)\n",
|
|
rval, mmc_dev[sdc_no].f_max, mmc_dev[sdc_no].f_min);
|
|
}else{
|
|
mmc_dev[sdc_no].f_max = rval;
|
|
MMCINFO("get sdc_f_max ok, sdc_f_max = %d\n", mmc_dev[sdc_no].f_max);
|
|
}
|
|
}
|
|
|
|
ret = script_parser_fetch("card2_boot_para","card_line", &rval, 1);
|
|
if(ret < 0)
|
|
MMCINFO("get card_line fail,use default card_line %d\n",4);
|
|
else{
|
|
#ifndef CONFIG_ARCH_SUN7I
|
|
if((rval!=8)&&(rval!=1)&&(rval!=4)){
|
|
MMCINFO("input card_line wrong ,use default card_line %d\n",4);
|
|
}else{
|
|
if(rval == 8 ){
|
|
mmc_dev[sdc_no].host_caps |= MMC_MODE_8BIT;
|
|
}else if(rval == 4){
|
|
mmc_dev[sdc_no].host_caps &= ~MMC_MODE_8BIT;
|
|
mmc_dev[sdc_no].host_caps |= MMC_MODE_4BIT;
|
|
}else{
|
|
mmc_dev[sdc_no].host_caps &= ~MMC_MODE_8BIT;
|
|
mmc_dev[sdc_no].host_caps &= ~MMC_MODE_4BIT;
|
|
}
|
|
MMCINFO("get card_line ok, card_line = %d\n", rval);
|
|
}
|
|
#else
|
|
if((rval!=1)&&(rval!=4)){
|
|
MMCINFO("input card_line wrong ,use default card_line %d\n",4);
|
|
}else{
|
|
if(rval == 4){
|
|
mmc_dev[sdc_no].host_caps &= ~MMC_MODE_8BIT;
|
|
mmc_dev[sdc_no].host_caps |= MMC_MODE_4BIT;
|
|
}else{
|
|
mmc_dev[sdc_no].host_caps &= ~MMC_MODE_8BIT;
|
|
mmc_dev[sdc_no].host_caps &= ~MMC_MODE_4BIT;
|
|
}
|
|
MMCINFO("get card_line ok, card_line = %d\n", rval);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
|
|
ret = script_parser_fetch("card2_boot_para","sdc_test", &rval, 1);
|
|
if (ret < 0) {
|
|
MMCINFO("get sdc_test fail.\n");
|
|
mmc_dev[sdc_no].mmc_test = 0;
|
|
} else {
|
|
MMCINFO("get sdc_test 0x%x\n", rval);
|
|
mmc_dev[sdc_no].mmc_test = rval;
|
|
}
|
|
|
|
|
|
ret = script_parser_fetch("card2_boot_para","sdc_ex_dly_used", &rval, 1);
|
|
if(ret < 0){
|
|
MMCINFO("get sdc_ex_dly_used fail,use default\n");
|
|
return;
|
|
}else{
|
|
int rval_ker =0;
|
|
ret = script_parser_fetch("mmc2_para","sdc_ex_dly_used", &rval_ker, 1);
|
|
int work_mode = uboot_spare_head.boot_data.work_mode;
|
|
//MMCINFO("ret%d,rval_ker%d,rval%d",ret,rval_ker,rval);
|
|
if((ret>=0)&&(rval_ker == 2)&&(rval == 2)){//only when kernal use auto sample,uboot will use auto sample.
|
|
struct tuning_sdly *sdly= (struct tuning_sdly *)uboot_spare_head.boot_data.sdcard_spare_data;
|
|
mmc_dev[sdc_no].sample_mode = AUTO_SAMPLE_MODE;
|
|
MMCINFO("get sdc_ex_dly_used ok\n");
|
|
MMCINFO("use auto sdly \n");
|
|
if(work_mode != WORK_MODE_BOOT){
|
|
//usb product will auto get sample point,so no need to get auto sdly,so first used default value
|
|
MMCINFO("Product will auto sample,not need to get auto sdly\n");
|
|
return;
|
|
}
|
|
MMCDBG("get auto sdly %d in 25M\n",sdly->sdly_25M);
|
|
mmchost->mmc_clk_dly[MMC_CLK_25M].sclk_dly = sdly->sdly_25M;
|
|
MMCDBG("get auto sdly %d in 50M\n",sdly->sdly_50M);
|
|
mmchost->mmc_clk_dly[MMC_CLK_50M].sclk_dly = sdly->sdly_50M;
|
|
MMCINFO("auto sdly %d in 25M \n", mmchost->mmc_clk_dly[MMC_CLK_25M].sclk_dly);
|
|
MMCINFO("auto sdly %d in 50M \n", mmchost->mmc_clk_dly[MMC_CLK_50M].sclk_dly);
|
|
return;
|
|
}else if(rval == 1){ //maual sample point from fex
|
|
MMCINFO("get sdc_ex_dly_used ok\n");
|
|
MMCINFO("use manual sample point in fex\n");
|
|
}else{
|
|
MMCINFO("undefined value %d or kernel not use auto sample,use default dly\n",rval);
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
/*************************25M dly*************************************/
|
|
ret = script_parser_fetch("card2_boot_para","sdc_odly_25M", &rval, 1);
|
|
if(ret < 0){
|
|
MMCINFO("get sdc_odly_25M fail,use default dly %d\n",mmchost->mmc_clk_dly[MMC_CLK_25M].oclk_dly);
|
|
}else{
|
|
if((rval>7)||(rval<0)){
|
|
MMCINFO("input sdc_odly_25M wrong ,use default dly %d\n",mmchost->mmc_clk_dly[MMC_CLK_25M].oclk_dly);
|
|
}else{
|
|
mmchost->mmc_clk_dly[MMC_CLK_25M].oclk_dly = rval;
|
|
MMCINFO("get sdc_odly_25M ok, odly = %d\n", mmchost->mmc_clk_dly[MMC_CLK_25M].oclk_dly);
|
|
}
|
|
}
|
|
|
|
ret = script_parser_fetch("card2_boot_para","sdc_sdly_25M", &rval, 1);
|
|
if(ret < 0)
|
|
MMCINFO("get sdc_sdly_25M fail,use default dly %d\n",mmchost->mmc_clk_dly[MMC_CLK_25M].sclk_dly);
|
|
else{
|
|
if((rval>7)||(rval<0)){
|
|
MMCINFO("input sdc_sdly_25M wrong ,use default dly %d\n",mmchost->mmc_clk_dly[MMC_CLK_25M].sclk_dly);
|
|
}else{
|
|
mmchost->mmc_clk_dly[MMC_CLK_25M].sclk_dly = rval;
|
|
MMCINFO("get sdc_sdly_25M ok, sdly = %d\n", mmchost->mmc_clk_dly[MMC_CLK_25M].sclk_dly);
|
|
}
|
|
}
|
|
|
|
/*************************50M dly*************************************/
|
|
ret = script_parser_fetch("card2_boot_para","sdc_odly_50M", &rval, 1);
|
|
if(ret < 0)
|
|
MMCINFO("get sdc_odly_50M fail,use default dly %d\n",mmchost->mmc_clk_dly[MMC_CLK_50M].oclk_dly);
|
|
else{
|
|
if((rval>7)||(rval<0)){
|
|
MMCINFO("input sdc_odly_50M wrong, use default dly %d\n", mmchost->mmc_clk_dly[MMC_CLK_50M].oclk_dly);
|
|
}else{
|
|
mmchost->mmc_clk_dly[MMC_CLK_50M].oclk_dly = rval;
|
|
MMCINFO("get sdc_odly_50M ok, odly = %d\n", mmchost->mmc_clk_dly[MMC_CLK_50M].oclk_dly);
|
|
}
|
|
}
|
|
|
|
ret = script_parser_fetch("card2_boot_para","sdc_sdly_50M", &rval, 1);
|
|
if(ret < 0)
|
|
MMCINFO("get sdc_sdly_50M fail,use default dly %d\n",mmchost->mmc_clk_dly[MMC_CLK_50M].sclk_dly);
|
|
else{
|
|
if((rval>7)||(rval<0)){
|
|
MMCINFO("input sdc_sdly_50M wrong, use default dly %d\n", mmchost->mmc_clk_dly[MMC_CLK_50M].sclk_dly);
|
|
}else{
|
|
mmchost->mmc_clk_dly[MMC_CLK_50M].sclk_dly = rval;
|
|
MMCINFO("get sdc_sdly_50M ok, sdly = %d\n", mmchost->mmc_clk_dly[MMC_CLK_50M].sclk_dly);
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
static void mmc_host_ahb_gate_rst_onoff(struct sunxi_mmc_host* mmchost, int on)
|
|
{
|
|
u32 rval = 0;
|
|
#ifndef CONFIG_ARCH_SUN9IW1P1
|
|
u32 sdc_no = mmchost->mmc_no;
|
|
#endif
|
|
|
|
#if defined (CONFIG_ARCH_SUN8IW1P1) || \
|
|
defined (CONFIG_ARCH_SUN8IW3P1) || \
|
|
defined (CONFIG_ARCH_SUN8IW5P1) || \
|
|
defined (CONFIG_ARCH_SUN8IW6P1) || \
|
|
defined (CONFIG_ARCH_SUN8IW7P1) || \
|
|
defined (CONFIG_ARCH_SUN8IW8P1) || \
|
|
defined (CONFIG_ARCH_SUN8IW9P1)
|
|
|
|
/* config ahb clock */
|
|
if(on)
|
|
{
|
|
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);
|
|
}
|
|
|
|
#elif defined(CONFIG_ARCH_SUN7I) || defined(CONFIG_ARCH_SUN5I)
|
|
/* config ahb clock */
|
|
if (on)
|
|
{
|
|
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);
|
|
}
|
|
|
|
#elif defined(CONFIG_ARCH_SUN9IW1P1)
|
|
|
|
/* config ahb clock */
|
|
if (on)
|
|
{
|
|
rval = smc_readl(mmchost->hclkbase);
|
|
rval |= (1 << 8);
|
|
smc_writel(rval, mmchost->hclkbase);
|
|
|
|
rval = smc_readl(mmchost->hclkrst);
|
|
rval |= (1 << 8);
|
|
smc_writel(rval, mmchost->hclkrst);
|
|
|
|
rval = smc_readl(mmchost->commreg);
|
|
rval |= (1<<16)|(1<<18);
|
|
smc_writel(rval, mmchost->commreg);
|
|
} else {
|
|
rval = smc_readl(mmchost->hclkbase);
|
|
rval &= (~(1 << 8));
|
|
smc_writel(rval, mmchost->hclkbase);
|
|
|
|
rval = smc_readl(mmchost->hclkrst);
|
|
rval &= (~(1 << 8));
|
|
smc_writel(rval, mmchost->hclkrst);
|
|
|
|
rval = smc_readl(mmchost->commreg);
|
|
rval &= (~((1<<16)|(1<<18)));
|
|
smc_writel(rval, mmchost->commreg);
|
|
}
|
|
|
|
#else
|
|
#error The platform is not seleted
|
|
#endif
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static int mmc_clk_io_onoff(int sdc_no,int onoff)
|
|
{
|
|
struct sunxi_mmc_host* mmchost = &mmc_host[sdc_no];
|
|
#if defined(CONFIG_ARCH_SUN9IW1P1)
|
|
if(gd->securemode == SUNXI_SECURE_MODE_NO_SECUREOS) //burn mode
|
|
{
|
|
if(sdc_no == 0)
|
|
{
|
|
gpio_request_simple("card0_boot_para", NULL);
|
|
}
|
|
else // if(sdc_no == 2)
|
|
{
|
|
gpio_request_simple("card2_boot_para", NULL);
|
|
}
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
get_fex_para(sdc_no);
|
|
|
|
mmc_host_ahb_gate_rst_onoff(mmchost, onoff);
|
|
|
|
/* config mod clock */
|
|
smc_writel(0x80000000, mmchost->mclkbase);
|
|
mmchost->mod_clk = 24000000;
|
|
dumphex32("ccmu", (char*)SUNXI_CCM_BASE, 0x310);
|
|
dumphex32("gpio", (char*)SUNXI_PIO_BASE, 0x100);
|
|
dumphex32("mmc", (char*)mmchost->reg, 0x100);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int mmc_update_clk(struct mmc *mmc)
|
|
{
|
|
struct sunxi_mmc_host* mmchost = (struct sunxi_mmc_host *)mmc->priv;
|
|
unsigned int cmd;
|
|
unsigned timeout = 1000;
|
|
|
|
cmd = (1U << 31) | (1 << 21) | (1 << 13);
|
|
writel(cmd, &mmchost->reg->cmd);
|
|
while((readl(&mmchost->reg->cmd)&0x80000000) && --timeout){
|
|
__usdelay(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->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->mmc_no== 2) && (mmc->host_func & MMC_HOST_2XMODE_FUNC) )
|
|
{
|
|
MMCINFO("mmc re-update_phase\n");
|
|
return mmc_update_clk(mmc);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
#if defined(CONFIG_ARCH_SUN8IW5P1) || defined(CONFIG_ARCH_SUN8IW6P1)||defined(CONFIG_ARCH_SUN8IW8P1) ||(defined CONFIG_ARCH_SUN8IW7P1) || (defined CONFIG_ARCH_SUN8IW9P1)
|
|
static int mmc_2xmode_config_clock(struct mmc *mmc, unsigned clk)
|
|
{
|
|
struct sunxi_mmc_host* mmchost = (struct sunxi_mmc_host *)mmc->priv;
|
|
unsigned rval = readl(&mmchost->reg->clkcr);
|
|
unsigned int clkdiv = 0;
|
|
unsigned int rntsr = readl(&mmchost->reg->ntsr);
|
|
unsigned int rgctrl = readl(&mmchost->reg->gctrl);
|
|
|
|
/* Disable Clock */
|
|
rval &= ~(1 << 16);
|
|
writel(rval, &mmchost->reg->clkcr);
|
|
if(mmc_update_clk(mmc))
|
|
return -1;
|
|
|
|
//disable mclk first
|
|
writel(0x4000000,mmchost->mclkbase);
|
|
MMCDBG("mmc %d mclkbase 0x%x\n",mmchost->mmc_no, readl(mmchost->mclkbase));
|
|
|
|
/*NTSR*/
|
|
rntsr |= (1U<<4); //(1U<<4); /* tx phase 1, rx phase 1 */
|
|
rntsr |= (1<<31);
|
|
MMCDBG("mmc %d rntsr 0x%x\n",mmchost->mmc_no,rntsr);
|
|
writel(rntsr, &mmchost->reg->ntsr);
|
|
|
|
|
|
/*set ddr mode*/
|
|
if(mmc->io_mode == MMC_MODE_DDR_52MHz){
|
|
MMCDBG("first %d rgctrl 0x%x\n",mmchost->mmc_no,rgctrl);
|
|
rgctrl |= 1 << 10;
|
|
writel(rgctrl, &mmchost->reg->gctrl);
|
|
MMCDBG("after %d rgctrl 0x%x\n",mmchost->mmc_no,readl(&mmchost->reg->gctrl));
|
|
}
|
|
else{
|
|
MMCDBG("mmc not set ddr mmc->io_mode = %x\n",mmc->io_mode);
|
|
}
|
|
|
|
if (clk <=400000) {
|
|
mmchost->mod_clk = 400000;
|
|
writel(0x4001000e, mmchost->mclkbase);
|
|
MMCDBG("mmc %d mclkbase 0x%x\n",mmchost->mmc_no, readl(mmchost->mclkbase));
|
|
} else {
|
|
u32 pllclk;
|
|
u32 n,m;
|
|
|
|
pllclk = sunxi_clock_get_pll6() * 1000000;
|
|
/*set ddr mode clock*/
|
|
if(mmc->io_mode == MMC_MODE_DDR_52MHz){
|
|
clkdiv = pllclk /( clk *4 ) - 1;
|
|
}else{
|
|
clkdiv = pllclk /( clk *2 ) - 1;
|
|
}
|
|
|
|
if (clkdiv < 16) {
|
|
n = 0;
|
|
m = clkdiv;
|
|
} else if (clkdiv < 32) {
|
|
n = 1;
|
|
m = clkdiv>>1;
|
|
} else {
|
|
n = 2;
|
|
m = clkdiv>>2;
|
|
}
|
|
mmchost->mod_clk = clk;
|
|
|
|
if (clk <= 26000000){
|
|
writel(0x41000000| (n << 16) | m, mmchost->mclkbase);
|
|
}
|
|
else{
|
|
writel(0x41000000 | (n << 16) | m, mmchost->mclkbase);
|
|
}
|
|
MMCDBG("init mmc %d pllclk %d, clk %d, mclkbase %x\n",mmchost->mmc_no,
|
|
pllclk, mmchost->mod_clk, readl(mmchost->mclkbase));
|
|
MMCDBG("Get round clk %d\n",pllclk/(1<<n)/(m+1));
|
|
if (mmc->io_mode == MMC_MODE_DDR_52MHz)
|
|
mmc->clock = pllclk/(1<<n)/(m+1)/2/2;
|
|
else
|
|
mmc->clock = pllclk/(1<<n)/(m+1)/2;
|
|
}
|
|
//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
|
|
*/
|
|
/* Change Divider Factor */
|
|
rval &= ~(0xFF);
|
|
if (mmc->io_mode == MMC_MODE_DDR_52MHz)
|
|
rval |= 0x1;
|
|
writel(rval, &mmchost->reg->clkcr);
|
|
if(mmc_update_clk(mmc)){
|
|
MMCINFO("mmc %d disable clock failed\n",mmchost->mmc_no);
|
|
return -1;
|
|
}
|
|
/* Re-enable Clock */
|
|
rval |= (3 << 16);
|
|
writel(rval, &mmchost->reg->clkcr);
|
|
if(mmc_update_clk(mmc)){
|
|
MMCINFO("mmc %d re-enable clock failed\n",mmchost->mmc_no);
|
|
return -1;
|
|
}
|
|
|
|
dumphex32("ccmu", (char*)SUNXI_CCM_BASE, 0x100);
|
|
dumphex32("gpio", (char*)SUNXI_PIO_BASE, 0x100);
|
|
dumphex32("mmc", (char*)mmchost->reg, 0x100);
|
|
|
|
MMCDBG("mmc %d ntsr 0x%x\n",mmchost->mmc_no,readl(&mmchost->reg->ntsr));
|
|
return 0;
|
|
}
|
|
|
|
static int mmc_2xmode_set_phase(struct mmc *mmc, u32 tx_phase, u32 rx_phase)
|
|
{
|
|
struct sunxi_mmc_host* mmchost = (struct sunxi_mmc_host *)mmc->priv;
|
|
unsigned int rval = readl(&mmchost->reg->ntsr);
|
|
int ret = 0;
|
|
|
|
if (!((mmchost->mmc_no == 2) && (mmc->host_func & MMC_HOST_2XMODE_FUNC)))
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
rval &=~((3<<4)|(3));
|
|
rval |= ((rx_phase&3)<<4)|(tx_phase&3);
|
|
rval &= ~(1<<31);
|
|
writel(rval, &mmchost->reg->ntsr);
|
|
rval |= (1<<31);
|
|
writel(rval, &mmchost->reg->ntsr);
|
|
MMCINFO("mmc %d set phase %x\n", mmchost->mmc_no, readl(&mmchost->reg->ntsr));
|
|
ret = mmc_update_clk(mmc);
|
|
if (ret) {
|
|
MMCINFO("mmc %d set phase udate clk fail! %x\n", mmchost->mmc_no, readl(&mmchost->reg->ntsr));
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
static int mmc_config_clock(struct mmc *mmc, unsigned clk)
|
|
{
|
|
struct sunxi_mmc_host* mmchost = (struct sunxi_mmc_host *)mmc->priv;
|
|
unsigned rval = readl(&mmchost->reg->clkcr);
|
|
unsigned int clkdiv = 0;
|
|
u32 sdly = 0;
|
|
u32 odly = 0;
|
|
|
|
MMCINFO("mmc_config_clock: clk %d\n", clk);
|
|
|
|
#if defined(CONFIG_ARCH_SUN8IW5P1) || defined(CONFIG_ARCH_SUN8IW6P1)||defined(CONFIG_ARCH_SUN8IW8P1)||(defined CONFIG_ARCH_SUN8IW7P1) || (defined CONFIG_ARCH_SUN8IW9P1)
|
|
if( (mmchost->mmc_no== 2) && (mmc->host_func & MMC_HOST_2XMODE_FUNC) )
|
|
{
|
|
MMCDBG("mmc %d 2xmode config clk\n",mmchost->mmc_no);
|
|
return ( mmc_2xmode_config_clock(mmc,clk) );
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
MMCDBG("mmc %d old config clk %d \n",mmchost->mmc_no,mmc->host_func);
|
|
/* Disable Clock */
|
|
rval &= ~(1 << 16);
|
|
writel(rval, &mmchost->reg->clkcr);
|
|
if(mmc_update_clk(mmc))
|
|
return -1;
|
|
|
|
//disable mclk first
|
|
smc_writel(0,mmchost->mclkbase);
|
|
MMCDBG("mmc %d mclkbase 0x%x\n",mmchost->mmc_no,smc_readl(mmchost->mclkbase));
|
|
if (clk <=400000) {
|
|
sdly = mmchost->mmc_clk_dly[MMC_CLK_400K].sclk_dly;
|
|
odly = mmchost->mmc_clk_dly[MMC_CLK_400K].oclk_dly;
|
|
mmchost->mod_clk = 400000;
|
|
smc_writel(0x0002000f|(sdly << 20)|(odly << 8), mmchost->mclkbase);
|
|
MMCDBG("mmc %d mclkbase 0x%x\n",mmchost->mmc_no,smc_readl(mmchost->mclkbase));
|
|
MMCDBG("Get round clk %d\n",400000);
|
|
mmc->clock = 400000;
|
|
mmc->pll_clock = 24000000;
|
|
} else {
|
|
u32 pllclk;
|
|
u32 n,m;
|
|
#if (defined(CONFIG_ARCH_SUN7I)|| defined(CONFIG_ARCH_SUN8IW1P1) || defined(CONFIG_ARCH_SUN8IW3P1) || defined(CONFIG_ARCH_SUN8IW5P1) || defined(CONFIG_ARCH_SUN8IW6P1) ||(defined CONFIG_ARCH_SUN8IW7P1) || defined(CONFIG_ARCH_SUN8IW8P1) )||(defined(CONFIG_ARCH_SUN8IW9P1))
|
|
pllclk = sunxi_clock_get_pll6() * 1000000;
|
|
#elif defined(CONFIG_ARCH_SUN9IW1P1)
|
|
pllclk = sunxi_clock_get_pll4_periph1() * 1000000;
|
|
#elif defined(CONFIG_ARCH_SUN5I)
|
|
pllclk = sunxi_clock_get_pll5() * 1000000;
|
|
|
|
#else
|
|
#error the platform is not config
|
|
#endif
|
|
clkdiv = pllclk / clk - 1;
|
|
if (clkdiv < 16) {
|
|
n = 0;
|
|
m = clkdiv;
|
|
} else if (clkdiv < 32) {
|
|
n = 1;
|
|
m = clkdiv>>1;
|
|
} else {
|
|
n = 2;
|
|
m = clkdiv>>2;
|
|
}
|
|
mmchost->mod_clk = clk;
|
|
#if defined(CONFIG_ARCH_SUN5I)
|
|
if (clk <= 26000000)
|
|
writel(0x02500000 | (n << 16) | m, mmchost->mclkbase);
|
|
else
|
|
writel(0x02500300 | (n << 16) | m, mmchost->mclkbase);
|
|
#else
|
|
if (clk <= 26000000){
|
|
sdly = mmchost->mmc_clk_dly[MMC_CLK_25M].sclk_dly;
|
|
odly = mmchost->mmc_clk_dly[MMC_CLK_25M].oclk_dly;
|
|
smc_writel(0x01000000 |(sdly << 20)|(odly << 8)| (n << 16) | m, mmchost->mclkbase);
|
|
}
|
|
else{
|
|
sdly = mmchost->mmc_clk_dly[MMC_CLK_50M].sclk_dly;
|
|
odly = mmchost->mmc_clk_dly[MMC_CLK_50M].oclk_dly;
|
|
smc_writel(0x01000000 | (sdly << 20) | (odly << 8) | (n << 16) | m, mmchost->mclkbase);
|
|
}
|
|
#endif
|
|
MMCDBG("init mmc %d pllclk %d, clk %d, mclkbase %x\n",mmchost->mmc_no,
|
|
pllclk, mmchost->mod_clk, smc_readl(mmchost->mclkbase));
|
|
MMCDBG("Get round clk %d\n",pllclk/(1<<n)/(m+1));
|
|
mmc->clock = pllclk/(1<<n)/(m+1);
|
|
mmc->pll_clock = pllclk;
|
|
}
|
|
//re-enable mclk
|
|
smc_writel(smc_readl(mmchost->mclkbase)|(1<<31),mmchost->mclkbase);
|
|
MMCDBG("mmc %d mclkbase 0x%x\n",mmchost->mmc_no,smc_readl(mmchost->mclkbase));
|
|
/*
|
|
* CLKCREG[7:0]: divider
|
|
* CLKCREG[16]: on/off
|
|
* CLKCREG[17]: power save
|
|
*/
|
|
/* Change Divider Factor */
|
|
rval &= ~(0xFF);
|
|
writel(rval, &mmchost->reg->clkcr);
|
|
if(mmc_update_clk(mmc)){
|
|
MMCINFO("mmc %d disable clock failed\n",mmchost->mmc_no);
|
|
return -1;
|
|
}
|
|
/* Re-enable Clock */
|
|
rval |= (3 << 16);
|
|
writel(rval, &mmchost->reg->clkcr);
|
|
if(mmc_update_clk(mmc)){
|
|
MMCINFO("mmc %d re-enable clock failed\n",mmchost->mmc_no);
|
|
return -1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
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\n", mmchost->mmc_no,mmc->bus_width, mmc->clock);
|
|
|
|
if (mmc->clock && mmc_config_clock(mmc, mmc->clock)) {
|
|
MMCINFO("[mmc]: mmc %d update clock failed\n",mmchost->mmc_no);
|
|
mmchost->fatal_err = 1;
|
|
return;
|
|
}
|
|
/* 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 %x\n",readl(&mmchost->reg->width));
|
|
}
|
|
|
|
static int mmc_core_init(struct mmc *mmc)
|
|
{
|
|
struct sunxi_mmc_host* mmchost = (struct sunxi_mmc_host *)mmc->priv;
|
|
#ifndef CONFIG_SUN7I
|
|
/* Reset controller */
|
|
writel(0x40000007, &mmchost->reg->gctrl);
|
|
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);
|
|
#else
|
|
writel(0x7, &mmchost->reg->gctrl);
|
|
while(readl(&mmchost->reg->gctrl)&0x7);
|
|
#endif
|
|
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 = MMC_TRANS_BY_CPU_TIMOUT_US;
|
|
|
|
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))){
|
|
__usdelay(1);
|
|
}
|
|
if (timeout <= 0)
|
|
goto out;
|
|
buff[i] = readl(mmchost->database);
|
|
timeout = MMC_TRANS_BY_CPU_TIMOUT_US;
|
|
}
|
|
} else {
|
|
buff = (unsigned int *)data->src;
|
|
for (i=0; i<(byte_cnt>>2); i++) {
|
|
while(--timeout && (readl(&mmchost->reg->status)&(1 << 3))){
|
|
__usdelay(1);
|
|
}
|
|
if (timeout <= 0)
|
|
goto out;
|
|
writel(buff[i], mmchost->database);
|
|
timeout = MMC_TRANS_BY_CPU_TIMOUT_US;
|
|
}
|
|
}
|
|
|
|
out:
|
|
if (timeout <= 0){
|
|
MMCINFO("transfer by cpu failed\n");
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
void sunxi_mci_regs_save(struct sunxi_mmc_host* mmchost)
|
|
{
|
|
memset((void*)®_bakup, 0, sizeof(struct sunxi_mmc));
|
|
|
|
reg_bakup.gctrl = readl(&mmchost->reg->gctrl);
|
|
reg_bakup.clkcr = readl(&mmchost->reg->clkcr);
|
|
reg_bakup.timeout = readl(&mmchost->reg->timeout);
|
|
reg_bakup.width = readl(&mmchost->reg->width);
|
|
reg_bakup.imask = readl(&mmchost->reg->imask);
|
|
reg_bakup.ftrglevel = readl(&mmchost->reg->ftrglevel);
|
|
reg_bakup.funcsel = readl(&mmchost->reg->funcsel);
|
|
reg_bakup.dbgc = readl(&mmchost->reg->dbgc);
|
|
reg_bakup.dmac = readl(&mmchost->reg->dmac);
|
|
#if defined(CONFIG_ARCH_SUN50IW1P1) \
|
|
|| defined(CONFIG_ARCH_SUN8IW10P1)\
|
|
|| defined(CONFIG_ARCH_SUN8IW11P1)
|
|
reg_bakup.thldc = readl(&mmchost->reg->thldc);
|
|
#endif
|
|
#if defined(CONFIG_ARCH_SUN8IW5P1) || defined(CONFIG_ARCH_SUN8IW6P1) \
|
|
|| defined(CONFIG_ARCH_SUN8IW8P1) || defined(CONFIG_ARCH_SUN8IW7P1) \
|
|
|| defined(CONFIG_ARCH_SUN8IW9P1)
|
|
reg_bakup.ntsr = readl(&mmchost->reg->ntsr);
|
|
#endif
|
|
|
|
#if 0
|
|
printf("======================= save regs ===========================\n");
|
|
{
|
|
__u32 i;
|
|
char* base = (char*)®_bakup;
|
|
char* name = "mmc";
|
|
u32 len = 0x100;
|
|
|
|
printf("dump %s registers:", name);
|
|
for (i=0; i<len; i+=4) {
|
|
if (!(i&0xf))
|
|
printf("\n0x%p : ", base + i);
|
|
printf("0x%08x ", smc_readl((uint)base + i));
|
|
}
|
|
printf("\n");
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void sunxi_mci_regs_restore(struct sunxi_mmc_host* mmchost)
|
|
{
|
|
writel(reg_bakup.gctrl , &mmchost->reg->gctrl);
|
|
writel(reg_bakup.clkcr , &mmchost->reg->clkcr);
|
|
writel(reg_bakup.timeout , &mmchost->reg->timeout);
|
|
writel(reg_bakup.width , &mmchost->reg->width);
|
|
writel(reg_bakup.imask , &mmchost->reg->imask);
|
|
writel(reg_bakup.ftrglevel, &mmchost->reg->ftrglevel);
|
|
writel(reg_bakup.funcsel , &mmchost->reg->funcsel);
|
|
writel(reg_bakup.dbgc , &mmchost->reg->dbgc);
|
|
writel(reg_bakup.dmac , &mmchost->reg->dmac);
|
|
#if defined(CONFIG_ARCH_SUN50IW1P1) \
|
|
|| defined(CONFIG_ARCH_SUN8IW10P1)\
|
|
|| defined(CONFIG_ARCH_SUN8IW11P1)
|
|
writel(reg_bakup.thldc , &mmchost->reg->thldc);
|
|
#endif
|
|
#if defined(CONFIG_ARCH_SUN8IW5P1) || defined(CONFIG_ARCH_SUN8IW6P1) \
|
|
|| defined(CONFIG_ARCH_SUN8IW8P1) || defined(CONFIG_ARCH_SUN8IW7P1) \
|
|
|| defined(CONFIG_ARCH_SUN8IW9P1)
|
|
writel(reg_bakup.ntsr , &mmchost->reg->ntsr);
|
|
#endif
|
|
|
|
#if 0
|
|
//dumphex32("mmc", (char*)mmchost->reg, 0x100);
|
|
printf("======================= restore regs -===========================\n");
|
|
{
|
|
__u32 i;
|
|
char* base = (char*)mmchost->reg;
|
|
char* name = "mmc";
|
|
u32 len = 0x100;
|
|
|
|
printf("dump %s registers:", name);
|
|
for (i=0; i<len; i+=4) {
|
|
if (!(i&0xf))
|
|
printf("\n0x%p : ", base + i);
|
|
printf("0x%08x ", smc_readl((uint)base + i));
|
|
}
|
|
printf("\n");
|
|
}
|
|
#endif
|
|
}
|
|
|
|
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 = (u32)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 = (u32)&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));
|
|
|
|
/*
|
|
* 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 */
|
|
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);
|
|
writel((2U<<28)|(7<<16)|8, &mmchost->reg->ftrglevel);
|
|
|
|
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");
|
|
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;
|
|
}
|
|
|
|
/* update clk for each cmd to clear 2xclk mode phase */
|
|
mmc_update_clk(mmc);
|
|
|
|
/*
|
|
* 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 ((u32)data->dest & 0x3) {
|
|
MMCINFO("mmc %d dest is not 4 byte align\n",mmchost->mmc_no);
|
|
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;
|
|
MMCINFO("mmc %d cmd %d err %x\n", mmchost->mmc_no, cmd->cmdidx, error);
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
timeout = 1000;
|
|
do {
|
|
status = readl(&mmchost->reg->rint);
|
|
if (!timeout-- || (status & 0xbfc2)) {
|
|
error = status & 0xbfc2;
|
|
if(!error)
|
|
error = 0xffffffff;//represet software timeout
|
|
MMCINFO("mmc %d cmd %d 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
|
|
MMCINFO("mmc %d cmd%d: data err %x\n",mmchost->mmc_no, cmd->cmdidx, error);
|
|
goto out;
|
|
}
|
|
if ((data->blocks > 1)&&!(cmd->flags&MMC_CMD_MANUAL))//not wait auto stop when MMC_CMD_MANUAL is set
|
|
done = status & (1 << 14);
|
|
else
|
|
done = status & (1 << 3);
|
|
__usdelay(1);
|
|
} while (!done);
|
|
|
|
if((data->flags & MMC_DATA_READ)&& usedma){
|
|
timeout = 0xffffff;
|
|
done = 0;
|
|
status = 0;
|
|
MMCDBG("mmc %d cacl rd dma timeout %x\n",mmchost->mmc_no, timeout);
|
|
do {
|
|
status = readl(&mmchost->reg->idst);
|
|
if (!timeout-- || (status & 0x234)) {
|
|
error = status & 0x1E34;
|
|
if(!error)
|
|
error = 0xffffffff;//represet software timeout
|
|
MMCINFO("mmc %d wait dma over err %x\n",mmchost->mmc_no, error);
|
|
goto out;
|
|
}
|
|
done = status & (1 << 1);
|
|
__usdelay(1);
|
|
} while (!done);
|
|
MMCDBG("idst *****0x%d******\n",readl(&mmchost->reg->idst));
|
|
}
|
|
|
|
}
|
|
|
|
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) {
|
|
if(data && (data->flags&MMC_DATA_READ)&&(bytecnt==512)){
|
|
writel(readl(&mmchost->reg->gctrl)|0x80000000, &mmchost->reg->gctrl);
|
|
writel(0xdeb, &mmchost->reg->dbgc);
|
|
timeout = 1000;
|
|
int tmp;
|
|
MMCINFO("Read remain data\n");
|
|
while(readl(&mmchost->reg->bbcr)<512){
|
|
tmp = readl(mmchost->database);
|
|
tmp = tmp;
|
|
MMCDBG("Read data %x,bbcr %x\n",tmp,readl(&mmchost->reg->bbcr));
|
|
__usdelay(1);
|
|
if(!(timeout--)){
|
|
MMCINFO("Read remain data timeout\n");
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
}
|
|
|
|
#if 1
|
|
sunxi_mci_regs_save(mmchost);
|
|
mmc_host_ahb_gate_rst_onoff(mmchost, 0);
|
|
udelay(500);
|
|
mmc_host_ahb_gate_rst_onoff(mmchost, 1);
|
|
sunxi_mci_regs_restore(mmchost);
|
|
#endif
|
|
|
|
#if 0 /* global reset will clear reg0x0 bit10 and clear ddr mode. */
|
|
writel(0x7, &mmchost->reg->gctrl);
|
|
while(readl(&mmchost->reg->gctrl)&0x7){
|
|
}
|
|
#endif
|
|
|
|
if (mmc_update_clk(mmc)) {
|
|
MMCINFO("mmc_send_cmd: udpate clock fail\n");
|
|
}
|
|
//MMCINFO("mmc %d mmc cmd %d err 0x%08x\n",mmchost->mmc_no, cmd->cmdidx, error);
|
|
}
|
|
writel(0xffffffff, &mmchost->reg->rint);
|
|
//writel(readl(&mmchost->reg->gctrl)|(1 << 1), &mmchost->reg->gctrl);
|
|
|
|
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;
|
|
int cnt= 0;
|
|
if(rst_cnt){
|
|
mmchost->mmc_clk_sdly_rty_tbl[MMC_CLK_400K].rty_cnt = 0;
|
|
mmchost->mmc_clk_sdly_rty_tbl[MMC_CLK_25M].rty_cnt = 0;
|
|
mmchost->mmc_clk_sdly_rty_tbl[MMC_CLK_50M].rty_cnt = 0;
|
|
return 0;
|
|
}
|
|
|
|
if(err_no && (!(err_no & SDXC_RespTimeout)||(err_no==0xffffffff))){
|
|
if(mmc->clock <= (400*1000)){
|
|
mmchost->mmc_clk_sdly_rty_tbl[MMC_CLK_400K].rty_cnt++;
|
|
cnt = mmchost->mmc_clk_sdly_rty_tbl[MMC_CLK_400K].rty_cnt;
|
|
if(cnt>7){
|
|
MMCINFO("Beyond the retry times\n");
|
|
return -1;
|
|
}
|
|
mmchost->mmc_clk_dly[MMC_CLK_400K].sclk_dly= mmchost->mmc_clk_sdly_rty_tbl[MMC_CLK_400K].sclk_dly[cnt];
|
|
MMCINFO("Get next samply point %d in clk %d\n",mmchost->mmc_clk_dly[MMC_CLK_400K].sclk_dly,mmc->clock);
|
|
}else if(mmc->clock <= (26*1000*1000)){
|
|
mmchost->mmc_clk_sdly_rty_tbl[MMC_CLK_25M].rty_cnt++;
|
|
cnt = mmchost->mmc_clk_sdly_rty_tbl[MMC_CLK_25M].rty_cnt;
|
|
if(cnt>7){
|
|
MMCINFO("Beyond the retry times\n");
|
|
return -1;
|
|
}
|
|
mmchost->mmc_clk_dly[MMC_CLK_25M] .sclk_dly= mmchost->mmc_clk_sdly_rty_tbl[MMC_CLK_25M].sclk_dly[cnt];
|
|
MMCINFO("Get next samply point %d in clk %d\n",mmchost->mmc_clk_dly[MMC_CLK_25M].sclk_dly,mmc->clock);
|
|
}else if(mmc->clock <= (52*1000*1000)){
|
|
mmchost->mmc_clk_sdly_rty_tbl[MMC_CLK_50M].rty_cnt++;
|
|
cnt = mmchost->mmc_clk_sdly_rty_tbl[MMC_CLK_50M].rty_cnt;
|
|
if(cnt>7){
|
|
MMCINFO("Beyond the retry times\n");
|
|
return -1;
|
|
}
|
|
mmchost->mmc_clk_dly[MMC_CLK_50M] .sclk_dly= mmchost->mmc_clk_sdly_rty_tbl[MMC_CLK_50M].sclk_dly[cnt];
|
|
MMCINFO("Get next samply point %d in clk %d\n",mmchost->mmc_clk_dly[MMC_CLK_50M].sclk_dly,mmc->clock);
|
|
}else{
|
|
MMCINFO("clk is over the retry clk\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_update_sdly(struct mmc *mmc,uint sdly)
|
|
{
|
|
struct sunxi_mmc_host* mmchost = (struct sunxi_mmc_host *)mmc->priv;
|
|
|
|
if(mmc->clock <= (400*1000)){
|
|
mmchost->mmc_clk_dly[MMC_CLK_400K].sclk_dly= sdly;
|
|
}else if(mmc->clock <= (26*1000*1000)){
|
|
mmchost->mmc_clk_dly[MMC_CLK_25M] .sclk_dly= sdly;
|
|
}else if(mmc->clock <= (52*1000*1000)){
|
|
mmchost->mmc_clk_dly[MMC_CLK_50M] .sclk_dly= sdly;
|
|
}else if(mmc->clock <= (100*1000*1000)){
|
|
mmchost->mmc_clk_dly[MMC_CLK_100M] .sclk_dly= sdly;
|
|
}else if(mmc->clock <= (200*1000*1000)){
|
|
mmchost->mmc_clk_dly[MMC_CLK_200M] .sclk_dly= sdly;
|
|
}else {
|
|
MMCINFO("clk %d is over the tuning clk %d\n",mmc->clock,200*1000*1000);
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
/*
|
|
void secure_test(struct mmc *mmc)
|
|
{
|
|
const char w_buf[512] = {
|
|
0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
|
|
0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
|
|
0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
|
|
0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
|
|
0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
|
|
0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
|
|
0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
|
|
0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
|
|
|
|
0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
|
|
0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
|
|
0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
|
|
0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
|
|
0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
|
|
0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
|
|
0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
|
|
0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
|
|
|
|
0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
|
|
0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
|
|
0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
|
|
0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
|
|
0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
|
|
0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
|
|
0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
|
|
0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
|
|
|
|
0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
|
|
0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
|
|
0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
|
|
0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
|
|
0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
|
|
0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
|
|
0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
|
|
0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
|
|
|
|
0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
|
|
0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
|
|
0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
|
|
0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
|
|
0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
|
|
0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
|
|
0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
|
|
0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
|
|
|
|
0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
|
|
0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
|
|
0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
|
|
0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
|
|
0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
|
|
0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
|
|
0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
|
|
0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
|
|
|
|
0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
|
|
0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
|
|
0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
|
|
0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
|
|
0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
|
|
0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
|
|
0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
|
|
0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
|
|
|
|
0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
|
|
0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
|
|
0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
|
|
0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
|
|
0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
|
|
0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
|
|
0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
|
|
0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
|
|
|
|
};
|
|
|
|
mmc_init(mmc);
|
|
char *buf = (char *)malloc((SDMMC_ITEM_SIZE*2*MAX_SECURE_STORAGE_MAX_ITEM+20)*512);
|
|
mmc->block_dev.block_read(2,SDMMC_SECURE_STORAGE_START_ADD,10,buf);
|
|
mmc->block_dev.block_write(2,SDMMC_SECURE_STORAGE_START_ADD,10,buf);
|
|
mmc->block_dev.block_read_mass_pro(2,SDMMC_SECURE_STORAGE_START_ADD,10,buf);
|
|
mmc->block_dev.block_write_mass_pro(2,SDMMC_SECURE_STORAGE_START_ADD,10,buf);
|
|
|
|
MMCINFO("*******************\n");
|
|
mmc->block_dev.block_read(2,SDMMC_SECURE_STORAGE_START_ADD+2,10,buf);
|
|
mmc->block_dev.block_write(2,SDMMC_SECURE_STORAGE_START_ADD-3,10,buf);
|
|
mmc->block_dev.block_read_mass_pro(2,SDMMC_SECURE_STORAGE_START_ADD+8,12,buf);
|
|
mmc->block_dev.block_write_mass_pro(2,SDMMC_SECURE_STORAGE_START_ADD-7,13,buf);
|
|
|
|
MMCINFO("*******************\n");
|
|
mmc->block_dev.block_read(2,SDMMC_SECURE_STORAGE_START_ADD+2,(SDMMC_ITEM_SIZE*2*MAX_SECURE_STORAGE_MAX_ITEM)+3,buf);
|
|
mmc->block_dev.block_write(2,SDMMC_SECURE_STORAGE_START_ADD-9,(SDMMC_ITEM_SIZE*2*MAX_SECURE_STORAGE_MAX_ITEM)+4,buf);
|
|
mmc->block_dev.block_read_mass_pro(2,SDMMC_SECURE_STORAGE_START_ADD+11,(SDMMC_ITEM_SIZE*2*MAX_SECURE_STORAGE_MAX_ITEM)+9,buf);
|
|
mmc->block_dev.block_write_mass_pro(2,SDMMC_SECURE_STORAGE_START_ADD-20,(SDMMC_ITEM_SIZE*2*MAX_SECURE_STORAGE_MAX_ITEM)+10,buf);
|
|
|
|
|
|
|
|
mmc->block_dev.block_write_secure(2,0,(void *)w_buf,1);
|
|
mmc->block_dev.block_read_secure(2,0,(u8 *)buf,1);
|
|
if(memcmp((void *)w_buf,(void *)buf,512)){
|
|
MMCINFO("cmp failed\n");
|
|
}else{
|
|
MMCINFO("cmp ok\n");
|
|
}
|
|
}
|
|
*/
|
|
|
|
|
|
int sunxi_mmc_init(int sdc_no)
|
|
{
|
|
struct mmc *mmc;
|
|
int work_mode = uboot_spare_head.boot_data.work_mode;
|
|
|
|
MMCINFO("mmc driver ver %s\n", DRIVER_VER);
|
|
|
|
memset(&mmc_dev[sdc_no], 0, sizeof(struct mmc));
|
|
memset(&mmc_host[sdc_no], 0, sizeof(struct sunxi_mmc_host));
|
|
mmc = &mmc_dev[sdc_no];
|
|
|
|
sprintf(mmc->name, "SUNXI SD/MMC");
|
|
mmc->priv = &mmc_host[sdc_no];
|
|
mmc->send_cmd = mmc_send_cmd;
|
|
mmc->set_ios = mmc_set_ios;
|
|
mmc->init = mmc_core_init;
|
|
mmc->control_num = sdc_no;
|
|
mmc->update_phase = mmc_update_phase;
|
|
#if defined(CONFIG_ARCH_SUN8IW5P1) || defined(CONFIG_ARCH_SUN8IW6P1)||defined(CONFIG_ARCH_SUN8IW8P1) ||(defined CONFIG_ARCH_SUN8IW7P1) || (defined CONFIG_ARCH_SUN8IW9P1)
|
|
mmc->set_phase = mmc_2xmode_set_phase;
|
|
#endif
|
|
|
|
mmc->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;
|
|
//bus width will be change in mmc_clk_io_onoff according to sys_config.fex
|
|
mmc->host_caps = MMC_MODE_4BIT | MMC_MODE_8BIT;
|
|
mmc->host_caps |= MMC_MODE_HS_52MHz | MMC_MODE_HS| MMC_MODE_HC | MMC_MODE_DDR_52MHz;
|
|
|
|
if(sdc_no == 0){
|
|
mmc->f_min = 400000;
|
|
#if defined(CONFIG_ARCH_SUN9IW1P1)
|
|
mmc->f_max = 48000000;
|
|
#else
|
|
mmc->f_max = 50000000;
|
|
#endif
|
|
}else if (sdc_no == 2){
|
|
mmc->f_min = 400000;
|
|
#if defined(CONFIG_ARCH_SUN9IW1P1)
|
|
mmc->f_max = 48000000;
|
|
#else
|
|
mmc->f_max = 50000000;
|
|
#endif
|
|
}
|
|
|
|
MMCDBG("mmc->host_caps %x\n",mmc->host_caps);
|
|
mmc_host[sdc_no].pdes = malloc(64 * 1024);
|
|
if(mmc_host[sdc_no].pdes == NULL){
|
|
MMCINFO("get mem for descriptor failed\n");
|
|
return -1;
|
|
}
|
|
mmc_resource_init(sdc_no);
|
|
mmc_clk_io_onoff(sdc_no,1);
|
|
|
|
#if defined CONFIG_ARCH_SUN9IW1P1
|
|
MMCINFO("PC Bias: 0x%08x 0x%08x\n", (0x6000800+0x308), *(volatile unsigned int *)(0x6000800+0x308));
|
|
#endif
|
|
if((mmc->sample_mode == AUTO_SAMPLE_MODE)
|
|
&&(work_mode != WORK_MODE_BOOT)){
|
|
mmc->decide_retry = sunxi_decide_rty;
|
|
mmc->update_sdly =sunxi_update_sdly;
|
|
mmc->get_detail_errno = sunxi_detail_errno;
|
|
}
|
|
|
|
/****************mmc function enable*********************/
|
|
if(mmc->mmc_func_en.ddr_func_en){
|
|
mmc->host_caps |= MMC_MODE_DDR_52MHz;
|
|
}
|
|
else{
|
|
mmc->host_caps &= ~(MMC_MODE_DDR_52MHz);
|
|
}
|
|
//while((*(volatile unsigned int *)0) != 1);
|
|
mmc_register(mmc);
|
|
//secure_test(mmc);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int sunxi_mmc_exit(int sdc_no)
|
|
{
|
|
mmc_clk_io_onoff(sdc_no, 0);
|
|
mmc_unregister(sdc_no);
|
|
//memset(&mmc_dev[sdc_no], 0, sizeof(struct mmc));
|
|
//memset(&mmc_host[sdc_no], 0, sizeof(struct sunxi_mmc_host));
|
|
MMCDBG("sunxi mmc%d exit\n",sdc_no);
|
|
return 0;
|
|
}
|