hisi3520 uboot源代码,emmc初始化,在读取emmc里的uboot,kernel代码到ddr运行前时,需要初始化emmc
emmc通过标准的cmd命令,读写数据。往emmc控制器的寄存器写入cmd指令,进行数据的读写。
Hi3521DV100_SDK_V1.0.4.0/osdrv/opensource/uboot/u-boot-2010.06/arch/arm/cpu/hi3559/emmc_boot.c
/*
* this file is only for emmc start.
*/
#include <config.h>
#include <asm/arch/platform.h>
#include <himciv200_reg.h>
#define MCI_SRC_CLK (25*1000*1000) /* 25MHz */
#define MCI_ID_CLK (400*1000) /* 400KHz */
#define MCI_BOOT_CLK (25*1000*1000) /* 25MHz */
#define DELAY_US (1000)
/* mmc.h */
#define MMC_CMD_GO_IDLE_STATE 0
#define MMC_CMD_SEND_OP_COND 1
#define MMC_CMD_ALL_SEND_CID 2
#define MMC_CMD_SET_RELATIVE_ADDR 3
#define MMC_CMD_SET_DSR 4
#define MMC_CMD_SWITCH 6
#define MMC_CMD_SELECT_CARD 7
#define MMC_CMD_SEND_EXT_CSD 8
#define MMC_CMD_SEND_CSD 9
#define MMC_CMD_SEND_CID 10
#define MMC_CMD_STOP_TRANSMISSION 12
#define MMC_CMD_SEND_STATUS 13
#define MMC_CMD_SET_BLOCKLEN 16
#define MMC_CMD_READ_SINGLE_BLOCK 17
#define MMC_CMD_READ_MULTIPLE_BLOCK 18
#define MMC_CMD_WRITE_SINGLE_BLOCK 24
#define MMC_CMD_WRITE_MULTIPLE_BLOCK 25
#define MMC_CMD_APP_CMD 55
#define SD_CMD_SEND_IF_COND 8
#define SD_CMD_APP_SEND_OP_COND 41
#define SD_CMD_APP_SET_BUS_WIDTH 6
#define OCR_BUSY 0x80000000
#define OCR_HCS 0x40000000
#define MMC_VDD_32_33 0x00100000 /* VDD voltage 3.2 ~ 3.3 */
#define MMC_VDD_33_34 0x00200000 /* VDD voltage 3.3 ~ 3.4 */
#define MMC_SWITCH_MODE_WRITE_BYTE 0x03 /* Set target byte to value */
#define EXT_CSD_BUS_WIDTH 183 /* R/W */
#define EXT_CSD_HS_TIMING 185 /* R/W */
#define EXT_CSD_BUS_WIDTH_1 0 /* Card is in 1 bit mode */
#define EXT_CSD_BUS_WIDTH_4 1 /* Card is in 4 bit mode */
#define EXT_CSD_BUS_WIDTH_8 2 /* Card is in 8 bit mode */
#define CP_STEP1_SIZE 0x4f00
#define MMC_BOOT_COMPATIBLE 0x1
static inline unsigned int himci_readl(unsigned addr)
{
return *((volatile unsigned *) (addr));
}
static inline void himci_writel(unsigned val, unsigned addr)
{
(*(volatile unsigned *) (addr)) = (val);
}
static inline void delay(unsigned int cnt)
{
while (cnt--)
__asm__ __volatile__("nop");
}
static inline void emmc_io_init(void)
{
}
#define REG_PERI_CRG49 0xC4
#define EMMC_SRST_REQ (0x1 << 16)
#define EMMC_CKEN (0x1 << 17)
static inline void emmc_sys_init(void)
{
unsigned int tmp_reg;
/* emmc clock phase */
tmp_reg = himci_readl(CRG_REG_BASE + REG_PERI_CRG49);
/* emmc soft reset */
tmp_reg |= EMMC_SRST_REQ;
himci_writel(tmp_reg, CRG_REG_BASE + REG_PERI_CRG49);
delay(1000 * DELAY_US);
tmp_reg &= ~EMMC_SRST_REQ;
tmp_reg |= EMMC_CKEN;
himci_writel(tmp_reg, CRG_REG_BASE + REG_PERI_CRG49);
}
static void emmc_update_clk(void)
{
unsigned int tmp_reg;
unsigned int mmc_base = EMMC_BASE_REG;
tmp_reg = (START_CMD | USE_HOLD_REG | UP_CLK_ONLY);
himci_writel(tmp_reg, mmc_base + MCI_CMD);
do {
tmp_reg = himci_readl(mmc_base + MCI_CMD);
} while (tmp_reg & START_CMD);
}
static void emmc_send_cmd(unsigned cmd, unsigned arg, unsigned wait_busy)
{
unsigned int tmp_reg;
unsigned int mmc_base = EMMC_BASE_REG;
himci_writel(ALL_INT_CLR, mmc_base + MCI_RINTSTS);
himci_writel(arg, mmc_base + MCI_CMDARG);
himci_writel(cmd | USE_HOLD_REG | WT_PD_CPT | START_CMD,
mmc_base + MCI_CMD);
do {
tmp_reg = himci_readl(mmc_base + MCI_CMD);
} while (tmp_reg & START_CMD);
do {
tmp_reg = himci_readl(mmc_base + MCI_RINTSTS);
} while (!(tmp_reg & CD_INT_STATUS));
if (wait_busy) {
do {
tmp_reg = himci_readl(mmc_base + MCI_STATUS);
} while ((tmp_reg & DATA_BUSY));
}
}
static unsigned int emmc_init(void)
{
unsigned int tmp_reg;
unsigned int mmc_base = EMMC_BASE_REG;
unsigned int hcs;
/* set drv/smpl phase shift */
tmp_reg = himci_readl(mmc_base + MCI_UHS_REG_EXT);
tmp_reg |= DRV_PHASE_SHIFT | SMPL_PHASE_SHIFT;
himci_writel(tmp_reg, mmc_base + MCI_UHS_REG_EXT);
/* clear MMC host intr */
himci_writel(ALL_INT_CLR, mmc_base + MCI_RINTSTS);
/*read write threshold*/
himci_writel(RW_THRESHOLD_SIZE, mmc_base + MMC_CARDTHRCTL);
/* MASK MMC host intr */
tmp_reg = himci_readl(mmc_base + MCI_INTMASK);
tmp_reg &= ~ALL_INT_MASK;
himci_writel(tmp_reg, mmc_base + MCI_INTMASK);
/* disable inner DMA mode and close intr of MMC host controller */
tmp_reg = himci_readl(mmc_base + MCI_CTRL);
tmp_reg &= ~(INTR_EN | USE_INTERNAL_DMA);
himci_writel(tmp_reg, mmc_base + MCI_CTRL);
/* set timeout param */
himci_writel(DATA_TIMEOUT | RESPONSE_TIMEOUT, mmc_base + MCI_TIMEOUT);
/* set FIFO param */
himci_writel(BURST_SIZE | RX_WMARK | TX_WMARK, mmc_base + MCI_FIFOTH);
/* set data width */
himci_writel(CARD_WIDTH_1BIT, mmc_base + MCI_CTYPE);
/* clk update */
himci_writel(0, mmc_base + MCI_CLKENA);
emmc_update_clk();
tmp_reg = MCI_SRC_CLK / (MCI_ID_CLK * 2);
if (MCI_SRC_CLK % (MCI_ID_CLK * 2))
tmp_reg++;
if (tmp_reg > 0xFF)
tmp_reg = 0xFF;
himci_writel(tmp_reg, mmc_base + MCI_CLKDIV);
emmc_update_clk();
himci_writel(0x1, mmc_base + MCI_CLKENA);
emmc_update_clk();
/* Send CMD0 */
delay(1000 * DELAY_US);
emmc_send_cmd(MMC_CMD_GO_IDLE_STATE | SEND_INIT, 0, 0);
delay(2000 * DELAY_US);
/* Send CMD1 */
do {
emmc_send_cmd(MMC_CMD_SEND_OP_COND | RESP_EXPECT,
OCR_HCS | MMC_VDD_32_33 | MMC_VDD_33_34, 0);
tmp_reg = himci_readl(mmc_base + MCI_RESP0);
delay(1000 * DELAY_US);
} while (!(tmp_reg & OCR_BUSY));
hcs = ((tmp_reg & OCR_HCS) == OCR_HCS);
/* Send CMD2 */
emmc_send_cmd(MMC_CMD_ALL_SEND_CID | RESP_EXPECT | RESP_LENGTH
| CHECK_RESP_CRC, 0, 0);
/* Send CMD3 */
emmc_send_cmd(MMC_CMD_SET_RELATIVE_ADDR | RESP_EXPECT
| CHECK_RESP_CRC, 0, 0);
/* Send CMD7 */
emmc_send_cmd(MMC_CMD_SELECT_CARD | RESP_EXPECT | CHECK_RESP_CRC,
0, 1);
/* Send CMD6 */
emmc_send_cmd(MMC_CMD_SWITCH | RESP_EXPECT | CHECK_RESP_CRC,
(MMC_SWITCH_MODE_WRITE_BYTE << 24) |
(EXT_CSD_BUS_WIDTH << 16) | (EXT_CSD_BUS_WIDTH_8 << 8),
1);
/* set data width */
himci_writel(CARD_WIDTH_8BIT, mmc_base + MCI_CTYPE);
if (MCI_SRC_CLK > MCI_BOOT_CLK) {
tmp_reg = MCI_SRC_CLK / (MCI_BOOT_CLK * 2);
if (MCI_SRC_CLK % (MCI_BOOT_CLK * 2))
tmp_reg++;
} else {
tmp_reg = 0;
}
himci_writel(tmp_reg, mmc_base + MCI_CLKDIV);
emmc_update_clk();
himci_writel(0x1, mmc_base + MCI_CLKENA);
emmc_update_clk();
return hcs;
}
static void emmc_deinit(void)
{
unsigned int mmc_base = EMMC_BASE_REG;
unsigned int val;
val = himci_readl(mmc_base + MCI_CTRL);
val |= (CTRL_RESET | FIFO_RESET | DMA_RESET);
himci_writel(val, mmc_base + MCI_CTRL);
/* clear MMC host intr */
himci_writel(ALL_INT_CLR, mmc_base + MCI_RINTSTS);
}
static void emmc_read(void *ptr, unsigned int size, unsigned int hcs)
{
unsigned int count, val, tmp_reg = 0;
unsigned int mmc_base = EMMC_BASE_REG;
unsigned int *buf, data;
himci_writel(MMC_BLK_SZ, mmc_base + MCI_BLKSIZ);
himci_writel((size + MMC_BLK_SZ - 1) & (~(MMC_BLK_SZ - 1)),
mmc_base + MCI_BYTCNT);
/* Send CMD7 */
emmc_send_cmd(MMC_CMD_SET_BLOCKLEN | RESP_EXPECT | CHECK_RESP_CRC,
MMC_BLK_SZ, 0);
/* Send CMD18 */
if (hcs)
emmc_send_cmd(MMC_CMD_READ_MULTIPLE_BLOCK | RESP_EXPECT
| CHECK_RESP_CRC | DATA_EXPECT,
CONFIG_MMC_BOOT_ADDR/MMC_BLK_SZ, 0);
else
emmc_send_cmd(MMC_CMD_READ_MULTIPLE_BLOCK | RESP_EXPECT
| CHECK_RESP_CRC | DATA_EXPECT,
CONFIG_MMC_BOOT_ADDR, 0);
buf = ptr;
size >>= 2;
while (size > 0) {
tmp_reg = himci_readl(mmc_base + MCI_STATUS);
count = (tmp_reg >> FIFO_COUNT) & FIFO_COUNT_MASK;
if (count > size)
count = size;
/*start to read data*/
while (1) {
val = (DCRC_INT_STATUS | DRTO_INT_STATUS | HTO_INT_STATUS
| FRUN_INT_STATUS | HLE_INT_STATUS | SBE_INT_STATUS
| EBE_INT_STATUS);
tmp_reg = himci_readl(mmc_base + MCI_RINTSTS);
if (tmp_reg & val)
return;
if (tmp_reg & RXDR_INT_STATUS)
break;
}
himci_writel(ALL_INT_CLR, mmc_base + MCI_RINTSTS);
for (; count != 0; --count) {
data = himci_readl(mmc_base + MCI_FIFO_START);
*buf = data;
buf++;
--size;
}
}
/* Send CMD12 */
emmc_send_cmd(MMC_CMD_STOP_TRANSMISSION | RESP_EXPECT | CHECK_RESP_CRC,
0, 1);
}
static void memcpy_4(unsigned int *dst, unsigned int *src, unsigned int size)
{
unsigned int i;
for (i = 0; i < (size >> 2); i++)
*dst++ = *src++;
}
static void emmc_boot_mode_read(void *ptr, unsigned int size, unsigned int hcs)
{
unsigned int count, val, tmp_reg = 0;
unsigned int mmc_base = EMMC_BASE_REG;
unsigned int *buf, data;
if (size <= CP_STEP1_SIZE) {
memcpy_4(ptr, (void *)RAM_START_ADRS, size);
return;
} else {
memcpy_4(ptr, (void *)RAM_START_ADRS, CP_STEP1_SIZE);
buf = (unsigned int *)(ptr + CP_STEP1_SIZE);
size -= CP_STEP1_SIZE;
}
size >>= 2;
while (size > 0) {
tmp_reg = himci_readl(mmc_base + MCI_STATUS);
count = (tmp_reg >> FIFO_COUNT) & FIFO_COUNT_MASK;
if (count > size)
count = size;
/*start to read data*/
while (1) {
val = (DCRC_INT_STATUS | FRUN_INT_STATUS | HLE_INT_STATUS
| SBE_INT_STATUS | EBE_INT_STATUS);
tmp_reg = himci_readl(mmc_base + MCI_RINTSTS);
if (tmp_reg & val)
return;
if (tmp_reg & RXDR_INT_STATUS)
break;
}
himci_writel(ALL_INT_CLR, mmc_base + MCI_RINTSTS);
for (; count != 0; --count) {
data = himci_readl(mmc_base + MCI_FIFO_START);
*buf = data;
buf++;
--size;
}
}
himci_writel((0x1<<31) | (0x1<<26) | (0x1<<14), mmc_base + MCI_CMD);
count = 1000;
do {
delay(DELAY_US);
count--;
tmp_reg = himci_readl(mmc_base + MCI_CMD);
} while ((tmp_reg & START_CMD) && count);
count = 1000;
do {
delay(DELAY_US);
count--;
tmp_reg = himci_readl(mmc_base + MCI_RINTSTS);
} while (!(tmp_reg & DTO_INT_STATUS) && count);
}
void emmc_boot_mode_deinit(void)
{
unsigned int mmc_base = EMMC_BASE_REG;
himci_writel((0x1<<31) | (0x1<<26) | (0x1<<14), mmc_base + MCI_CMD);
}
void emmc_boot_read(void *ptr, unsigned int size)
{
unsigned int hcs = 0;
unsigned int reg;
reg = himci_readl(SYS_CTRL_REG_BASE + REG_SC_GEN5);
if (reg == MMC_BOOT_COMPATIBLE) {
emmc_boot_mode_deinit();
hcs = emmc_init();
emmc_read(ptr, size, hcs);
} else {
emmc_boot_mode_read(ptr, size, hcs);
}
emmc_deinit();
}