S5PV210裸机(七):Nand和iNand

        本文主要探讨210Nand和iNand相关知识。

NandFlash
        型号与命
                
K9F2G08:K9F为发行商,2G为Nand大小是2Gbit(256MB),08为Nand是8位(8数据线即接口为8位:传输数据,地址,命令)

        功能
                
Nand是矩阵式存储,每块可存1bit位
                Nand单次访问最小单元为Page,Page大小是2KB+64B,每次读写n*2KB,random read模式可读1字节
                210中 1 block = 64 page,1 Device = 2028 block = 2048 × 64 × 2K = 256MB
                Page是读写最小单位,Block是擦除最小单位
                Nand芯片:Nand存储 + Nand接口
                Nand读写时地址通过IO线发送,地址有30位而IO只有8位,需要多个cycle才能完成
                SoC通过发送命令、地址、数据等信给Nand控制器来访问Nand(接口)

                 I/O8~15用在X16设备且仅用于输入命令和地址,用于数据输入和接口输出

        ECC(软件或硬件寄存器)
                2KB存储数据,64用于管理功能(存储ECC数据、存储坏块标志···),
                读取或写入前后会做校验,校验不通过则证明已损坏,会将坏块信息存储信息到Nand时会产生        

                ECC(校验信息)存储到64字节内,下次读写跳过坏块

        Nand操作流程
              
  坏块检查
                        nand使用前要擦除(块单位),擦除完后全是1,擦除后检测是否为0xff可知是否为坏块

                页写
                        写前需要擦除
                SoC写Flash通过命令线在IO线依次发送写入开始命令、要写入的页地址、要写入数据,写入结束命令
                SOC与Nand建立写入连接后,写入一页数据发给Nand接口,接口接收数据缓冲区,再写入Nand存储区
                nand接收和写入数据需要延时,通过状态寄存器判断是否写完,写完后再发送写入结束命令,再做ECC校验写入是否正常

        擦除
                擦除读写要发送对齐地址

        页读 

        210Nand相关寄存器

                gpio  

                nand configure 

 

 

demo1: 

                nand功能

nand.h

void nand_init(void);

void nand_read_id(void);

int nand_block_erase(unsigned long block_num);

int copy_nand_to_sdram(unsigned char *sdram_addr, unsigned long nand_addr, unsigned long length);

int copy_sdram_to_nand(unsigned char *sdram_addr, unsigned long nand_addr, unsigned long length);

int nand_page_read(unsigned int pgaddr, unsigned char *buf, unsigned int length);

int nand_page_write(unsigned int pgaddr, const unsigned char *buf, unsigned int length);

int nand_random_read(unsigned long pgaddr,unsigned short offset, unsigned char *data);

int nand_random_write(unsigned long pgaddr,unsigned short offset,unsigned char wrdata);

nand.c 

#include "nand.h"
#include "stdio.h"

#define rNFCONF 			( *((volatile unsigned long *)0xB0E00000) )
#define rNFCONT 			( *((volatile unsigned long *)0xB0E00004) )
#define rNFCMMD 			( *((volatile unsigned long *)0xB0E00008) )
#define rNFADDR 			( *((volatile unsigned long *)0xB0E0000C) )
#define rNFDATA 			( *((volatile unsigned long *)0xB0E00010) )
#define rNFDATA8 			( *((volatile unsigned char *)0xB0E00010) )
#define rNFSTAT 			( *((volatile unsigned long *)0xB0E00028) )

#define rMP0_1CON 			( *((volatile unsigned long *)0xE02002E0) )
#define rMP0_2CON 			( *((volatile unsigned long *)0xE0200300) )
#define rMP0_3CON 			( *((volatile unsigned long *)0xE0200320) )

#define MAX_NAND_BLOCK  			  8192 			
#define NAND_PAGE_SIZE  			  2048 			
#define NAND_BLOCK_SIZE 			  64  			

//dealy time(12ns)
#define TACLS    					  1				
#define TWRPH0   					  4
#define TWRPH1   					  1

//command
#define NAND_CMD_READ_1st             0x00			
#define NAND_CMD_READ_2st             0x30

#define NAND_CMD_READ_CB_1st          0x00
#define NAND_CMD_READ_CB_2st          0x35

#define NAND_CMD_RANDOM_WRITE         0x85
#define NAND_CMD_RANDOM_READ_1st      0x05
#define NAND_CMD_RANDOM_READ_2st      0xe0

#define NAND_CMD_READ_ID              0x90
#define NAND_CMD_RESET                0xff
#define NAND_CMD_READ_STATUS          0x70

#define NAND_CMD_WRITE_PAGE_1st       0x80
#define NAND_CMD_WRITE_PAGE_2st       0x10

#define NAND_CMD_BLOCK_ERASE_1st      0x60
#define NAND_CMD_BLOCK_ERASE_2st      0xd0


#define ECC_EN						  (1<<4)
#define CONTROL_EN					  (1<<0)


static void nand_reset(void);
static void nand_wait_idle(void);
static void nand_select_chip(void);
static void nand_deselect_chip(void);
static void nand_send_cmd(unsigned long cmd);
static void nand_send_addr(unsigned long addr);
static unsigned char nand_read8(void);
static void nand_write8(unsigned char data);
static unsigned int nand_read32(void);
static void nand_write32(unsigned int data);

typedef struct nand_id_info
{
	//marker code
	unsigned char IDm; 
	//device code
	unsigned char IDd; 
	unsigned char ID3rd;
	unsigned char ID4th;
	unsigned char ID5th;
}nand_id_info;

//reset  
void nand_reset(void)
{
	nand_select_chip();
	nand_send_cmd(NAND_CMD_RESET);
	nand_wait_idle();
	nand_deselect_chip();
}

//waite busy or read status  over
void nand_wait_idle(void)
{
	unsigned long i;
	while( !(rNFSTAT & (1<<4)) )
		for(i=0; i<10; i++);
}

//connect nand  
void nand_select_chip(void)
{
	unsigned long i;
	rNFCONT &= ~(1<<1);
	for(i=0; i<10; i++);
}

//disconnect nand  
void nand_deselect_chip(void)
{
	unsigned long i = 0;
	rNFCONT |= (1<<1);
	for(i=0; i<10; i++);
}

//send command 
void nand_send_cmd(unsigned long cmd)
{
	unsigned long i = 0;

	rNFCMMD = cmd;
	for(i=0; i<10; i++);
}

//send address  
void nand_send_addr(unsigned long addr)
{
	unsigned long i;
	unsigned long col, row;

	//address of interior page  
	col = addr % NAND_PAGE_SIZE;		
	//address of page  			
	row = addr / NAND_PAGE_SIZE;

	//write Column Address A0~A7  
	rNFADDR = col & 0xff;			
	for(i=0; i<10; i++);		

	//write  Column Address A8~A11  
	rNFADDR = (col >> 8) & 0x0f; 		
	for(i=0; i<10; i++);

	//write Row Address A12~A19	
	rNFADDR = row & 0xff;			
	for(i=0; i<10; i++);

	//write Row Address A20~A27	
	rNFADDR = (row >> 8) & 0xff;
	for(i=0; i<10; i++);

	//write Row Address A28~A30(A28,low level)	
	rNFADDR = (row >> 16) & 0xff;
	for(i=0; i<10; i++);
}

//read nand(word)
unsigned int nand_read32(void)
{
	return rNFDATA;
}

//write nand(word)
void nand_write32(unsigned int data)
{
	rNFDATA = data;
}

//read nand(half word)  
unsigned char nand_read8(void)
{
	return rNFDATA8;
}

//write nand(half word) 
void nand_write8(unsigned char data)
{
	rNFDATA8 = data;
}

//get nand status
unsigned char nand_read_status(void)
{
	unsigned char ch;
	int i;

	//connect nand 
	nand_select_chip();

	//get nand status  
	nand_send_cmd(NAND_CMD_READ_STATUS);
	for(i=0; i<10; i++);
	//read nand
	ch = nand_read8();

	//disconnect nand
	nand_deselect_chip();
	return ch;
}

// nand init
void nand_init(void)
{

	//set read nand signal dealy 
	rNFCONF = (TACLS<<12)|(TWRPH0<<8)|(TWRPH1<<4)|(0<<3)|(0<<2)|(1<<1)|(0<<0);
	//set nand control,chip,set ECC,close read/busy status interrupt,close soft lock
	rNFCONT = (0<<18)|(0<<17)|(0<<16)|(0<<10)|(0<<9)|(0<<8)|(0<<7)|(0<<6)|(0x3<<1)|(1<<0);

	//set gpio nandflash
	rMP0_1CON = 0x22333322;
	rMP0_2CON = 0x00002222;
	rMP0_3CON = 0x22222222;

	//reset 
	nand_reset();
}

//get nand id 
void nand_read_id(void)
{
	nand_id_info nand_id;
	//connect nand 
	nand_select_chip();

	//get nand id  
	nand_send_cmd(NAND_CMD_READ_ID);
	nand_send_addr(0x00);
	nand_wait_idle();
	
	nand_id.IDm = 	nand_read8();
	nand_id.IDd = 	nand_read8();
	nand_id.ID3rd = nand_read8();
	nand_id.ID4th = nand_read8();
	nand_id.ID5th = nand_read8();

	printf("nandflash: makercode = %x\r\n devicecode = %x\r\n ID3rd = %x\r\n ID4rd = %x\r\n ID5rd = %x\r\n", nand_id.IDm, nand_id.IDd, nand_id.ID3rd, nand_id.ID4th, nand_id.ID5th);
	
	//disconnect nand
	nand_deselect_chip();
}

//earse the num of block(0 ~ MAX_NAND_BLOCK-1)  
int nand_block_erase(unsigned long block_num)
{
	unsigned long i = 0;

	//get the starting page address of block(block starting address)  
	unsigned long row = block_num * NAND_BLOCK_SIZE;

	//connect nand  
	nand_select_chip();
	//send earse command (0x60)  
	nand_send_cmd(NAND_CMD_BLOCK_ERASE_1st);
	//dealy time
	for(i=0; i<10; i++);
	
	//set erase address
	//Row Address A12~A19	
	rNFADDR = row & 0xff;							
	for(i=0; i<10; i++);
	// Row Address A20~A27  
	rNFADDR = (row >> 8) & 0xff;
	for(i=0; i<10; i++);
	// Row Address A28~A30  
	rNFADDR = (row >> 16) & 0xff;	
	
	//clear RnB bit,aim to send second command
	rNFSTAT |= (1<<4);			 
	
	//send second command(0xd0)
	nand_send_cmd(NAND_CMD_BLOCK_ERASE_2st);
	for(i=0; i<10; i++);
	//waite busy or read status  over 
	nand_wait_idle();

	//judge read/busy status,0 earse success 
	unsigned char status = nand_read_status();
	if (status & 1 )
	{
		//disconnect nand
		nand_deselect_chip();						
		printf("masking bad block %d\r\n", block_num);
		return -1;
	}
	else
	{
		nand_deselect_chip();
		return 0;
	}
}

//erase the address of nand
int nand_erase(unsigned long block_addr)
{
	int i = 0;
	//judge write protect 
	if((nand_read_status() & 0x80) == 0) 
	{
	    printf("Write protected.\n");
	    return -1;
	}
	
	unsigned long row = block_addr >> 18;
	//connect nand  
	nand_select_chip();
	// send erase command(0x60)
	nand_send_cmd(NAND_CMD_BLOCK_ERASE_1st);
	//dealy time
	for(i=0; i<10; i++);
	
	//set erase address
	// Row Address A12~A19	
	rNFADDR = row & 0xff;							
	for(i=0; i<10; i++);
	// Row Address A20~A27  
	rNFADDR = (row >> 8) & 0xff;
	for(i=0; i<10; i++);
	// Row Address A28~A30  
	rNFADDR = (row >> 16) & 0x01;	
	for(i=0; i<10; i++);
	
	//clear RnB bit,aim to send second command
	rNFSTAT |= (1<<4);			
	
	//send second command(0xd0)
	nand_send_cmd(NAND_CMD_BLOCK_ERASE_2st);
	for(i=0; i<10; i++);
	//waite busy or read status  over 
	nand_wait_idle();

	//judge erase ,0 sucess 
	unsigned char status = nand_read_status();
	if (status & 1)
	{
		nand_deselect_chip();						
		printf("masking bad block %d\r\n", block_addr);
		return -1;
	}
	else
	{
		nand_deselect_chip();
		return 0;
	}
}

//copy nand to sdram 
int copy_nand_to_sdram(unsigned char *sdram_addr, unsigned long nand_addr, unsigned long length)
{
	unsigned long i = 0;

	//connect nand
	nand_select_chip();

	while(length)
	{
		//send read command(0x00)
		nand_send_cmd(NAND_CMD_READ_1st);
		//send read starting address
		nand_send_addr(nand_addr);
		//clear RnB bit,aim to send second command
		rNFSTAT = (rNFSTAT)|(1<<4);
		//send second command (0x30)
		nand_send_cmd(NAND_CMD_READ_2st);
		//waite busy or read status  over 
		nand_wait_idle();

		//get the address of start page
		unsigned long col = nand_addr % NAND_PAGE_SIZE;
		i = col;
		//read one page,pre one time copy 1byte,need 2048 times,when length equal 0  over 
		for(; i<NAND_PAGE_SIZE && length!=0; i++,length--)
		{
			*sdram_addr = nand_read8();
			sdram_addr++;
			nand_addr++;
		}
	}

	//judge copy result  
	unsigned char status = nand_read_status();
	if (status & 1 )
	{
		nand_deselect_chip();
		printf("copy nand to sdram fail\r\n");
		return -1;
	}
	else
	{
		nand_deselect_chip();
		return 0;
	}
}

//copy sdram to nand 
int copy_sdram_to_nand(unsigned char *sdram_addr, unsigned long nand_addr, unsigned long length)
{
	unsigned long i = 0;

	//connect nand 
	nand_select_chip();

	 
	while(length)
	{
		//send write command(0x80) 
		nand_send_cmd(NAND_CMD_WRITE_PAGE_1st);
		//send read starting address
		nand_send_addr(nand_addr);
		//get the address of start page  
		unsigned long col = nand_addr % NAND_PAGE_SIZE;
		i = col;
		//write one page,pre one time write 1byte,need 2048 times,when length equal 0  over
		for(; i<NAND_PAGE_SIZE && length!=0; i++,length--)
		{
			nand_write8(*sdram_addr);
			sdram_addr++;
			nand_addr++;
		}
		//clear RnB bit,aim to send second command
		rNFSTAT = (rNFSTAT)|(1<<4);
		//send second command (0x10)
		nand_send_cmd(NAND_CMD_WRITE_PAGE_2st);
		//waite busy or read status  over
		nand_wait_idle();
	}
	//judge copy result 
	unsigned char status = nand_read_status();
	if (status & 1 )
	{
		nand_deselect_chip();
		printf("copy sdram to nand fail\r\n");
		return -1;
	}
	else
	{
		nand_deselect_chip();
		return 0;
	}
}

//read bit on page
int nand_page_read(unsigned int pgaddr, unsigned char *buf, unsigned int length)
{
	int i = 0;
	
	//connect nand  
	nand_select_chip();

	//send read command(0x00) 
	nand_send_cmd(NAND_CMD_READ_1st);
	
	//set read address
	rNFADDR = 0;										
	rNFADDR = 0;
	rNFADDR = pgaddr&0xff;
	rNFADDR = (pgaddr>>8)&0xff;
	rNFADDR = (pgaddr>>16)&0xff;
	
	//clear RnB bit,aim to send second command
	rNFSTAT |= (1<<4);
	
	//send second command(0x30) 
	nand_send_cmd(NAND_CMD_READ_2st);
	
	//waite busy or read status  over
	nand_wait_idle();
	
	//read 2KB data area and 64  spare area(1byte)
	for (i=0; (i<NAND_PAGE_SIZE) && (length!=0); i++,length--)
		*buf++ = nand_read8();
	
	//judge read result
	unsigned char status = nand_read_status();
	if (status & 1 )
	{  
		nand_deselect_chip();
		printf("nand random read fail\r\n");
		return -1;
	}
	else
	{
		nand_deselect_chip();
		return 0;
	}
}

//read word on page
int nand_page_read32(unsigned int pgaddr, unsigned int *buf, unsigned int lengthB)
{
	int i = 0;
	
	//connect nand
	nand_select_chip();

	//send read command 
	nand_send_cmd(NAND_CMD_READ_1st);
	
	//write read starting address
	rNFADDR = 0;										
	rNFADDR = 0;
	rNFADDR = pgaddr&0xff;
	rNFADDR = (pgaddr>>8)&0xff;
	rNFADDR = (pgaddr>>16)&0xff;
	
	//clear RnB bit,aim to send second command
	rNFSTAT |= (1<<4);
	
	//send second command(0x30) 
	nand_send_cmd(NAND_CMD_READ_2st);
	
	//waite busy or read status  over
	nand_wait_idle();
	
	//read 2KB + 64B spare area(1 word)
	for (i=0; (i<NAND_PAGE_SIZE/4) && (lengthB!=0); i++,lengthB--)
		*buf++ = nand_read32();
	
	//judge read result 
	unsigned char status = nand_read_status();
	if (status & 1 )
	{  
		nand_deselect_chip();
		printf("nand random read fail\r\n");
		return -1;
	}
	else
	{
		nand_deselect_chip();
		return 0;
	}
}


//write page
int nand_page_write(unsigned int pgaddr, const unsigned char *buf, unsigned int length)
{
	int i = 0;
	
	//connect nand
	nand_select_chip();

	//send write command(0x80) 
	nand_send_cmd(NAND_CMD_WRITE_PAGE_1st);
	
	//write start address
	rNFADDR = 0;
	rNFADDR = 0;
	rNFADDR = pgaddr&0xff;
	rNFADDR = (pgaddr>>8)&0xff;
	rNFADDR = (pgaddr>>16)&0xff;

	//write page(1byte)
	for(; i<NAND_PAGE_SIZE && length!=0; i++,length--)
		nand_write8(*buf++);
	
	//clear RnB bit,aim to send second command
	rNFSTAT = (rNFSTAT)|(1<<4);
	
	//sens second command
	nand_send_cmd(NAND_CMD_WRITE_PAGE_2st);
	
	//wait busy or read status  over
	nand_wait_idle();
	
	//judge write result 
	unsigned char status = nand_read_status();
	if (status & 1 )
	{
		nand_deselect_chip();
		printf("nand random write fail\r\n");
		return -1;
	}
	else
	{
		nand_deselect_chip();
		return 0;
	}
	
}
 

//read random 
int nand_random_read(unsigned long pgaddr,unsigned short offset, unsigned char *data)
{
	unsigned char readdata;

	//connect nand 
	nand_select_chip();

	//send read command(0x00)
	nand_send_cmd(NAND_CMD_READ_1st);
	//write start address
	rNFADDR = 0;										
	rNFADDR = 0;
	rNFADDR = pgaddr&0xff;
	rNFADDR = (pgaddr>>8)&0xff;
	rNFADDR = (pgaddr>>16)&0xff;
	
	//clear RnB bit,aim to send second command
	rNFSTAT |= (1<<4);
	//second second command
	nand_send_cmd(NAND_CMD_READ_2st);
	//wait busy or read status  over
	nand_wait_idle();

	//send read command(0x05)
	nand_send_cmd(NAND_CMD_RANDOM_READ_1st);
	//write the address of page that offset address
	rNFADDR = offset&0xff; 							
	rNFADDR = (offset>>8)&0xff;
	
	//clear RnB bit,aim to send second command	
	rNFSTAT = (rNFSTAT)|(1<<4);
	//second second command(0xe0)
	nand_send_cmd(NAND_CMD_RANDOM_READ_2st);

	//read data
	readdata = nand_read8();

	//judge read result  
	unsigned char status = nand_read_status();
	if (status & 1 )
	{
		nand_deselect_chip();
		printf("nand random read fail\r\n");
		return -1;
	}
	else
	{
		nand_deselect_chip();
		*data = readdata;
		return 0;
	}
}

//random write
int nand_random_write(unsigned long pgaddr,unsigned short offset,unsigned char wrdata)
{
	//connect nand
	nand_select_chip();

	//send write command(0x80) 
	nand_send_cmd(NAND_CMD_WRITE_PAGE_1st);
	//write start address
	rNFADDR = 0;
	rNFADDR = 0;
	rNFADDR = pgaddr&0xff;
	rNFADDR = (pgaddr>>8)&0xff;
	rNFADDR = (pgaddr>>16)&0xff;
	
	//send write command (0x80)
	nand_send_cmd(NAND_CMD_RANDOM_WRITE);
	//write the address of page that offset address
	rNFADDR = offset&0xff; 					
	rNFADDR = (offset>>8)&0xff;
	//write data(1byte)
	nand_write8(wrdata);
	//clear RnB bit,aim to send second command
	rNFSTAT = (rNFSTAT)|(1<<4);
	//send second command
	nand_send_cmd(NAND_CMD_WRITE_PAGE_2st);
	//wait busy or read status  over
	nand_wait_idle();

	//judge write result  
	unsigned char status = nand_read_status();
	if (status & 1 )
	{ 
		nand_deselect_chip();
		printf("nand random write fail\r\n");
		return -1;
	}
	else
	{
		nand_deselect_chip();
		return 0;
	}
}

iNand
        iNand/eMMC/SDCard/MMCCard
                mmc对于nandflash的优势:卡片化便于拆装,接口协议Hmmc协议)统一便于封装
                SD卡(SD协议、SPI协议)兼容性好且有写保护、速率快、容量大

        iNand/eMMC与NandFlash
                iNand和nand内部由存储和接口组成
                iNand接口支持eMMC协议、ECC校验,使用ML,有cache 机制,速度快
                NandFlash:SLC 更稳定,容量小价格高;MLC易出错容量大价格低

        SD/iNand/eMMC
                210是4通道SD/MMC,SD/MMC0接iNand,SD/MMC2-3接SD卡
                SD卡是4IO线支持1、4、8线并行传输,iNand是8IO线支持1、4线并行传输
                硬件接口:DATA(数据)、CLK(时钟,25Mhz)、CMD(命令)
                SD协议是多标准命令,命令有使用条件和响应,少数命令没有响应
                SD卡命令是周期性组合(组合命令),在命令周期中,发送CMD到SD卡,SD卡执行命令并且返回响应,主机接收响应后再发下一条
                CMD和ACMD:SD协议命令:CMDx、ACMDx;CMD是单命令,ACMDy = CMDy + CMDz(y一般是55)
                SD卡处于某种状态(空闲状态、准备好状态、读写状态、出错状态····),接受某种执行命令(跳转命令)并返回响应,跳转到另一状态

 

 

 

       210SD/iNand寄存器
                RCA(相对地址寄存器)

 

                gpio

 

 

         

      

                设置时钟 

 

 

             状态寄存器(命令,写入,读取,中断...)   

 

                cmd

 

 

 

                命令参数

 

                错误状态 

 

 

                设置每次读取block大小 

 

                设置每次读取block数量 

 

                配置数据传输 

                命令控制寄存器 

                

                32位数据端口寄存器访问内部缓冲区

                sd响应

                设置中断状态

                sd命令参数 

                设置软件功能(是SD不是soc)

                设置超时

                设置错误中断状态

demo2:

                sd/emmc功能

Hsmmc.h

#ifndef __HSMMC_H__
#define __HSMMC_H__

#ifdef __cplusplus
extern "C" {
#endif
	
#include "stdint.h"

// SD协议规定的命令码	
#define	CMD0	0
#define	CMD1	1
#define	CMD2	2	
#define	CMD3	3	
#define	CMD6	6
#define	CMD7	7
#define	CMD8	8
#define	CMD9	9
#define	CMD13	13
#define	CMD16	16
#define	CMD17	17
#define	CMD18	18
#define	CMD23	23	
#define	CMD24	24
#define	CMD25	25	
#define	CMD32	32	
#define	CMD33	33	
#define	CMD38	38	
#define	CMD41	41	
#define CMD51	51
#define	CMD55	55	

// 卡类型
#define UNUSABLE		0
#define SD_V1			1	
#define	SD_V2			2	
#define	SD_HC			3
#define	MMC				4
	
// 卡状态
#define CARD_IDLE		0			// 空闲态
#define CARD_READY		1			// 准备好
#define CARD_IDENT		2
#define CARD_STBY		3
#define CARD_TRAN		4
#define CARD_DATA		5
#define CARD_RCV		6
#define CARD_PRG		7			// 卡编程状态
#define CARD_DIS		8			// 断开连接

// 卡回复类型	
#define CMD_RESP_NONE	0			// 无回复
#define CMD_RESP_R1		1
#define CMD_RESP_R2		2
#define CMD_RESP_R3		3
#define CMD_RESP_R4		4
#define CMD_RESP_R5		5
#define CMD_RESP_R6		6
#define CMD_RESP_R7		7
#define CMD_RESP_R1B	8
	
typedef struct {
	uint32_t RESERVED1;
	uint32_t RESERVED2 : 16;	
	uint32_t SD_BUS_WIDTHS : 4;
	uint32_t SD_SECURITY : 3;	
	uint32_t DATA_STAT_AFTER_ERASE : 1;
	uint32_t SD_SPEC : 4;	
	uint32_t SCR_STRUCTURE : 4;
} SD_SCR;	

int32_t Hsmmc_Init(void);
int32_t Hsmmc_GetCardState(void);
int32_t Hsmmc_GetSdState(uint8_t *pStatus);
int32_t Hsmmc_Get_SCR(SD_SCR *pSCR);
int32_t Hsmmc_Get_CSD(uint8_t *pCSD);
int32_t Hsmmc_EraseBlock(uint32_t StartBlock, uint32_t EndBlock);
int32_t Hsmmc_WriteBlock(uint8_t *pBuffer, uint32_t BlockAddr, uint32_t BlockNumber);
int32_t Hsmmc_ReadBlock(uint8_t *pBuffer, uint32_t BlockAddr, uint32_t BlockNumber);
	
#ifdef __cplusplus
}
#endif

#endif /*__HSMMC_H__*/

Hsmmc.c

#include "ProjectConfig.h"
#include "Hsmmc.h"

#define HSMMC_NUM		2

#if (HSMMC_NUM == 0)
#define HSMMC_BASE	(0xEB000000)
#elif (HSMMC_NUM == 1)
#define HSMMC_BASE	(0xEB100000)
#elif (HSMMC_NUM == 2)
#define HSMMC_BASE	(0xEB200000)
#elif (HSMMC_NUM == 3)
#define HSMMC_BASE	(0xEB300000)
#else
#error "Configure HSMMC: HSMMC0 ~ HSMM3(0 ~ 3)"
#endif

#define MAX_BLOCK  65535

#define SWRST_OFFSET 0x2F



static uint8_t CardType; // 卡类型
static uint32_t RCA; // 卡相对地址

static void Hsmmc_ClockOn(uint8_t On)
{
	uint32_t Timeout;
	if (On) {
		__REGw(HSMMC_BASE+CLKCON_OFFSET) |= (1<<2); // sd时钟使能
		Timeout = 1000; // Wait max 10 ms
		while (!(__REGw(HSMMC_BASE+CLKCON_OFFSET) & (1<<3))) {
			// 等待SD输出时钟稳定
			if (Timeout == 0) {
				return;
			}
			Timeout--;
			Delay_us(10);
		}
	} else {
		__REGw(HSMMC_BASE+CLKCON_OFFSET) &= ~(1<<2); // sd时钟禁止
	}
}

static void Hsmmc_SetClock(uint32_t Clock)
{
	uint32_t Temp;
	uint32_t Timeout;
	uint32_t i;
	Hsmmc_ClockOn(0); // 关闭时钟	
	Temp = __REG(HSMMC_BASE+CONTROL2_OFFSET);
	// Set SCLK_MMC(48M) from SYSCON as a clock source	
	Temp = (Temp & (~(3<<4))) | (2<<4);
	Temp |= (1u<<31) | (1u<<30) | (1<<8);
	if (Clock <= 500000) {
		Temp &= ~((1<<14) | (1<<15));
		__REG(HSMMC_BASE+CONTROL3_OFFSET) = 0;
	} else {
		Temp |= ((1<<14) | (1<<15));
		__REG(HSMMC_BASE+CONTROL3_OFFSET) = (1u<<31) | (1<<23);
	}
	__REG(HSMMC_BASE+CONTROL2_OFFSET) = Temp;
	
	for (i=0; i<=8; i++) {
		if (Clock >= (48000000/(1<<i))) {
			break;
		}
	}
	Temp = ((1<<i) / 2) << 8; // clock div
	Temp |= (1<<0); // Internal Clock Enable
	__REGw(HSMMC_BASE+CLKCON_OFFSET) = Temp;
	Timeout = 1000; // Wait max 10 ms
	while (!(__REGw(HSMMC_BASE+CLKCON_OFFSET) & (1<<1))) {
		// 等待内部时钟振荡稳定
		if (Timeout == 0) {
			return;
		}
		Timeout--;
		Delay_us(10);
	}
	
	Hsmmc_ClockOn(1); // 使能时钟
}

static int32_t Hsmmc_WaitForBufferReadReady(void)
{	
	int32_t ErrorState;
	while (1) {
		if (__REGw(HSMMC_BASE+NORINTSTS_OFFSET) & (1<<15)) { // 出现错误
			break;
		}
		if (__REGw(HSMMC_BASE+NORINTSTS_OFFSET) & (1<<5)) { // 读缓存准备好
			__REGw(HSMMC_BASE+NORINTSTS_OFFSET) |= (1<<5); // 清除准备好标志
			return 0;
		}				
	}
			
	ErrorState = __REGw(HSMMC_BASE+ERRINTSTS_OFFSET) & 0x1ff; // 可能通信错误,CRC检验错误,超时等
	__REGw(HSMMC_BASE+NORINTSTS_OFFSET) = __REGw(HSMMC_BASE+NORINTSTS_OFFSET); // 清除中断标志	
	__REGw(HSMMC_BASE+ERRINTSTS_OFFSET) = __REGw(HSMMC_BASE+ERRINTSTS_OFFSET); // 清除错误中断标志
	Debug("Read buffer error, NORINTSTS: %04x\r\n", ErrorState);
	return ErrorState;
}

static int32_t Hsmmc_WaitForBufferWriteReady(void)
{
	int32_t ErrorState;
	while (1) {
		if (__REGw(HSMMC_BASE+NORINTSTS_OFFSET) & (1<<15)) { // 出现错误
			break;
		}
		if (__REGw(HSMMC_BASE+NORINTSTS_OFFSET) & (1<<4)) { // 写缓存准备好
			__REGw(HSMMC_BASE+NORINTSTS_OFFSET) |= (1<<4); // 清除准备好标志
			return 0;
		}				
	}
			
	ErrorState = __REGw(HSMMC_BASE+ERRINTSTS_OFFSET) & 0x1ff; // 可能通信错误,CRC检验错误,超时等
	__REGw(HSMMC_BASE+NORINTSTS_OFFSET) = __REGw(HSMMC_BASE+NORINTSTS_OFFSET); // 清除中断标志
	__REGw(HSMMC_BASE+ERRINTSTS_OFFSET) = __REGw(HSMMC_BASE+ERRINTSTS_OFFSET); // 清除错误中断标志
	Debug("Write buffer error, NORINTSTS: %04x\r\n", ErrorState);
	return ErrorState;
}
	
static int32_t Hsmmc_WaitForCommandDone(void)
{
	uint32_t i;	
	int32_t ErrorState;
	// 等待命令发送完成
	for (i=0; i<0x20000000; i++) {
		if (__REGw(HSMMC_BASE+NORINTSTS_OFFSET) & (1<<15)) { // 出现错误
			break;
		}
		if (__REGw(HSMMC_BASE+NORINTSTS_OFFSET) & (1<<0)) {
			do {
				__REGw(HSMMC_BASE+NORINTSTS_OFFSET) = (1<<0); // 清除命令完成位
			} while (__REGw(HSMMC_BASE+NORINTSTS_OFFSET) & (1<<0));				
			return 0; // 命令发送成功
		}
	}
	
	ErrorState = __REGw(HSMMC_BASE+ERRINTSTS_OFFSET) & 0x1ff; // 可能通信错误,CRC检验错误,超时等
	__REGw(HSMMC_BASE+NORINTSTS_OFFSET) = __REGw(HSMMC_BASE+NORINTSTS_OFFSET); // 清除中断标志
	__REGw(HSMMC_BASE+ERRINTSTS_OFFSET) = __REGw(HSMMC_BASE+ERRINTSTS_OFFSET); // 清除错误中断标志	
	do {
		__REGw(HSMMC_BASE+NORINTSTS_OFFSET) = (1<<0); // 清除命令完成位
	} while (__REGw(HSMMC_BASE+NORINTSTS_OFFSET) & (1<<0));
	
	Debug("Command error, ERRINTSTS = 0x%x ", ErrorState);	
	return ErrorState; // 命令发送出错	
}

static int32_t Hsmmc_WaitForTransferDone(void)
{
	int32_t ErrorState;
	uint32_t i;
	// 等待数据传输完成
	for (i=0; i<0x20000000; i++) {
		if (__REGw(HSMMC_BASE+NORINTSTS_OFFSET) & (1<<15)) { // 出现错误
			break;
		}											
		if (__REGw(HSMMC_BASE+NORINTSTS_OFFSET) & (1<<1)) { // 数据传输完								
			do {
				__REGw(HSMMC_BASE+NORINTSTS_OFFSET) |= (1<<1); // 清除传输完成位
			} while (__REGw(HSMMC_BASE+NORINTSTS_OFFSET) & (1<<1));									
			return 0;
		}
	}

	ErrorState = __REGw(HSMMC_BASE+ERRINTSTS_OFFSET) & 0x1ff; // 可能通信错误,CRC检验错误,超时等
	__REGw(HSMMC_BASE+NORINTSTS_OFFSET) = __REGw(HSMMC_BASE+NORINTSTS_OFFSET); // 清除中断标志
	__REGw(HSMMC_BASE+ERRINTSTS_OFFSET) = __REGw(HSMMC_BASE+ERRINTSTS_OFFSET); // 清除错误中断标志
	Debug("Transfer error, rHM1_ERRINTSTS = 0x%04x\r\n", ErrorState);	
	do {
		__REGw(HSMMC_BASE+NORINTSTS_OFFSET) = (1<<1); // 出错后清除数据完成位
	} while (__REGw(HSMMC_BASE+NORINTSTS_OFFSET) & (1<<1));
	
	return ErrorState; // 数据传输出错		
}

static int32_t Hsmmc_IssueCommand(uint8_t Cmd, uint32_t Arg, uint8_t Data, uint8_t Response)
{
	uint32_t i;
	uint32_t Value;
	uint32_t ErrorState;
	// 检查CMD线是否准备好发送命令
	for (i=0; i<0x1000000; i++) {
		if (!(__REG(HSMMC_BASE+PRNSTS_OFFSET) & (1<<0))) {
			break;
		}
	}
	if (i == 0x1000000) {
		Debug("CMD line time out, PRNSTS: %04x\r\n", __REG(HSMMC_BASE+PRNSTS_OFFSET));
		return -1; // 命令超时
	}
	
	// 检查DAT线是否准备好
	if (Response == CMD_RESP_R1B) { // R1b回复通过DAT0反馈忙信号
		for (i=0; i<0x10000000; i++) {
			if (!(__REG(HSMMC_BASE+PRNSTS_OFFSET) & (1<<1))) {
				break;
			}		
		}
		if (i == 0x10000000) {
			Debug("Data line time out, PRNSTS: %04x\r\n", __REG(HSMMC_BASE+PRNSTS_OFFSET));			
			return -2;
		}		
	}
	
	__REG(HSMMC_BASE+ARGUMENT_OFFSET) = Arg; // 写入命令参数
	
	Value = (Cmd << 8); // command index
	// CMD12可终止传输
	if (Cmd == 0x12) {
		Value |= (0x3 << 6); // command type
	}
	if (Data) {
		Value |= (1 << 5); // 需使用DAT线作为传输等
	}	
	
	switch (Response) {
	case CMD_RESP_NONE:
		Value |= (0<<4) | (0<<3) | 0x0; // 没有回复,不检查命令及CRC
		break;
	case CMD_RESP_R1:
	case CMD_RESP_R5:
	case CMD_RESP_R6:
	case CMD_RESP_R7:		
		Value |= (1<<4) | (1<<3) | 0x2; // 检查回复中的命令,CRC
		break;
	case CMD_RESP_R2:
		Value |= (0<<4) | (1<<3) | 0x1; // 回复长度为136位,包含CRC
		break;
	case CMD_RESP_R3:
	case CMD_RESP_R4:
		Value |= (0<<4) | (0<<3) | 0x2; // 回复长度48位,不包含命令及CRC
		break;
	case CMD_RESP_R1B:
		Value |= (1<<4) | (1<<3) | 0x3; // 回复带忙信号,会占用Data[0]线
		break;
	default:
		break;	
	}
	
	__REGw(HSMMC_BASE+CMDREG_OFFSET) = Value;
	
	ErrorState = Hsmmc_WaitForCommandDone();
	if (ErrorState) {
		Debug("Command = %d\r\n", Cmd);
	}	
	return ErrorState; // 命令发送出错
}

int32_t Hsmmc_Switch(uint8_t Mode, int32_t Group, int32_t Function, uint8_t *pStatus)
{
	int32_t ErrorState;
	int32_t Temp;
	uint32_t i;
	
	__REGw(HSMMC_BASE+BLKSIZE_OFFSET) = (7<<12) | (64<<0); // 最大DMA缓存大小,block为512位64字节			
	__REGw(HSMMC_BASE+BLKCNT_OFFSET) = 1; // 写入这次读1 block的sd状态数据
	Temp = (Mode << 31U) | 0xffffff;
	Temp &= ~(0xf<<(Group * 4));
	Temp |= Function << (Group * 4);
	__REG(HSMMC_BASE+ARGUMENT_OFFSET) = Temp; // 写入命令参数	

	// DMA禁能,读单块		
	__REGw(HSMMC_BASE+TRNMOD_OFFSET) = (0<<5) | (1<<4) | (0<<2) | (1<<1) | (0<<0);	
	// 设置命令寄存器,SWITCH_FUNC CMD6,R1回复
	__REGw(HSMMC_BASE+CMDREG_OFFSET) = (CMD6<<8)|(1<<5)|(1<<4)|(1<<3)|0x2;
	ErrorState = Hsmmc_WaitForCommandDone();
	if (ErrorState) {
		Debug("CMD6 error\r\n");
		return ErrorState;
	}

	ErrorState = Hsmmc_WaitForBufferReadReady();
	if (ErrorState) {
		return ErrorState;
	}
	
	pStatus += 64 - 1;
	for (i=0; i<64/4; i++) {
		Temp = __REG(HSMMC_BASE+BDATA_OFFSET);
		*pStatus-- = (uint8_t)Temp;
		*pStatus-- = (uint8_t)(Temp>>8);
		*pStatus-- = (uint8_t)(Temp>>16);
		*pStatus-- = (uint8_t)(Temp>>24);			
	}
		
	ErrorState = Hsmmc_WaitForTransferDone();
	if (ErrorState) {
		Debug("Get sd status error\r\n");
		return ErrorState;
	}	
	return 0;	
}

// 512位的sd卡扩展状态位
int32_t Hsmmc_GetSdState(uint8_t *pStatus)
{
	int32_t ErrorState;
	uint32_t Temp;
	uint32_t i;
	if (CardType == SD_HC || CardType == SD_V2 || CardType == SD_V1) {
		if (Hsmmc_GetCardState() != CARD_TRAN) { // 必需在transfer status
			return -1; // 卡状态错误
		}		
		Hsmmc_IssueCommand(CMD55, RCA<<16, 0, CMD_RESP_R1);
		
		__REGw(HSMMC_BASE+BLKSIZE_OFFSET) = (7<<12) | (64<<0); // 最大DMA缓存大小,block为512位64字节			
		__REGw(HSMMC_BASE+BLKCNT_OFFSET) = 1; // 写入这次读1 block的sd状态数据
		__REG(HSMMC_BASE+ARGUMENT_OFFSET) = 0; // 写入命令参数	

		// DMA禁能,读单块		
		__REGw(HSMMC_BASE+TRNMOD_OFFSET) = (0<<5) | (1<<4) | (0<<2) | (1<<1) | (0<<0);	
		// 设置命令寄存器,读状态命令CMD13,R1回复
		__REGw(HSMMC_BASE+CMDREG_OFFSET) = (CMD13<<8)|(1<<5)|(1<<4)|(1<<3)|0x2;
		ErrorState = Hsmmc_WaitForCommandDone();
		if (ErrorState) {
			Debug("CMD13 error\r\n");
			return ErrorState;
		}
		
		ErrorState = Hsmmc_WaitForBufferReadReady();
		if (ErrorState) {
			return ErrorState;
		}
		
		pStatus += 64 - 1;
		for (i=0; i<64/4; i++) {
			Temp = __REG(HSMMC_BASE+BDATA_OFFSET);
			*pStatus-- = (uint8_t)Temp;
			*pStatus-- = (uint8_t)(Temp>>8);
			*pStatus-- = (uint8_t)(Temp>>16);
			*pStatus-- = (uint8_t)(Temp>>24);			
		}
		
		ErrorState = Hsmmc_WaitForTransferDone();
		if (ErrorState) {
			Debug("Get sd status error\r\n");
			return ErrorState;
		}
							
		return 0;
	}
	return -1; // 非sd卡
}

// Reads the SD Configuration Register (SCR).
int32_t Hsmmc_Get_SCR(SD_SCR *pSCR)
{
	uint8_t *pBuffer;
	int32_t ErrorState;
	uint32_t Temp;
	uint32_t i;
	Hsmmc_IssueCommand(CMD55, RCA<<16, 0, CMD_RESP_R1); 
	__REGw(HSMMC_BASE+BLKSIZE_OFFSET) = (7<<12) | (8<<0); // 最大DMA缓存大小,block为64位8字节			
	__REGw(HSMMC_BASE+BLKCNT_OFFSET) = 1; // 写入这次读1 block的sd状态数据
	__REG(HSMMC_BASE+ARGUMENT_OFFSET) = 0; // 写入命令参数	

	// DMA禁能,读单块		
	__REGw(HSMMC_BASE+TRNMOD_OFFSET) = (0<<5) | (1<<4) | (0<<2) | (1<<1) | (0<<0);	
	// 设置命令寄存器,read SD Configuration CMD51,R1回复
	__REGw(HSMMC_BASE+CMDREG_OFFSET) = (CMD51<<8)|(1<<5)|(1<<4)|(1<<3)|0x2;
	ErrorState = Hsmmc_WaitForCommandDone();
	if (ErrorState) {
		Debug("CMD51 error\r\n");
		return ErrorState;
	}

	ErrorState = Hsmmc_WaitForBufferReadReady();
	if (ErrorState) {
		return ErrorState;
	}
	
	// Wide width data (SD Memory Register)
	pBuffer = (uint8_t *)pSCR + sizeof(SD_SCR) - 1;
	for (i=0; i<8/4; i++) {
		Temp = __REG(HSMMC_BASE+BDATA_OFFSET);
		*pBuffer-- = (uint8_t)Temp;
		*pBuffer-- = (uint8_t)(Temp>>8);
		*pBuffer-- = (uint8_t)(Temp>>16);
		*pBuffer-- = (uint8_t)(Temp>>24);		
	}
	
	ErrorState = Hsmmc_WaitForTransferDone();
	if (ErrorState) {
		Debug("Get SCR register error\r\n");
		return ErrorState;
	}

	return 0;
}

// Asks the selected card to send its cardspecific data
int32_t Hsmmc_Get_CSD(uint8_t *pCSD)
{
	uint32_t i;
	uint32_t Response[4];
	int32_t State = -1;

	if (CardType != SD_HC && CardType != SD_V1 && CardType != SD_V2) {
		return State; // 未识别的卡
	}
	// 取消卡选择,任何卡均不回复,已选择的卡通过RCA=0取消选择,
	// 卡回到stand-by状态
	Hsmmc_IssueCommand(CMD7, 0, 0, CMD_RESP_NONE);
	for (i=0; i<1000; i++) {
		if (Hsmmc_GetCardState() == CARD_STBY) { // CMD9命令需在standy-by status				
			break; // 状态正确
		}
		Delay_us(100);
	}
	if (i == 1000) {
		return State; // 状态错误
	}	
	
	// 请求已标记卡发送卡特定数据(CSD),获得卡信息
	if (!Hsmmc_IssueCommand(CMD9, RCA<<16, 0, CMD_RESP_R2)) {
		pCSD++; // 跳过第一字节,CSD中[127:8]位对位寄存器中的[119:0]
		Response[0] = __REG(HSMMC_BASE+RSPREG0_OFFSET);
		Response[1] = __REG(HSMMC_BASE+RSPREG1_OFFSET);
		Response[2] = __REG(HSMMC_BASE+RSPREG2_OFFSET);
		Response[3] = __REG(HSMMC_BASE+RSPREG3_OFFSET);	
		for (i=0; i<15; i++) { // 拷贝回复寄存器中的[119:0]到pCSD中
			*pCSD++ = ((uint8_t *)Response)[i];
		}	
		State = 0; // CSD获取成功
	}

	Hsmmc_IssueCommand(CMD7, RCA<<16, 0, CMD_RESP_R1); // 选择卡,卡回到transfer状态
	return State;
}

// R1回复中包含了32位的card state,卡识别后,可在任一状态通过CMD13获得卡状态
int32_t Hsmmc_GetCardState(void)
{
	if (Hsmmc_IssueCommand(CMD13, RCA<<16, 0, CMD_RESP_R1)) {
		return -1; // 卡出错
	} else {
		return ((__REG(HSMMC_BASE+RSPREG0_OFFSET)>>9) & 0xf); // 返回R1回复中的[12:9]卡状态
	}
}

static int32_t Hsmmc_SetBusWidth(uint8_t Width)
{
	int32_t State;
	if ((Width != 1) || (Width != 4)) {
		return -1;
	}
	State = -1; // 设置初始为未成功
	__REGw(HSMMC_BASE+NORINTSTSEN_OFFSET) &= ~(1<<8); // 关闭卡中断
	Hsmmc_IssueCommand(CMD55, RCA<<16, 0, CMD_RESP_R1);
	if (Width == 1) {
		if (!Hsmmc_IssueCommand(CMD6, 0, 0, CMD_RESP_R1)) { // 1位宽
			__REGb(HSMMC_BASE+HOSTCTL_OFFSET) &= ~(1<<1);
			State = 0; // 命令成功
		}
	} else {
		if (!Hsmmc_IssueCommand(CMD6, 2, 0, CMD_RESP_R1)) { // 4位宽
			__REGb(HSMMC_BASE+HOSTCTL_OFFSET) |= (1<<1);
			State = 0; // 命令成功
		}
	}
	__REGw(HSMMC_BASE+NORINTSTSEN_OFFSET) |= (1<<8); // 打开卡中断	
	return State; // 返回0为成功
}

int32_t Hsmmc_EraseBlock(uint32_t StartBlock, uint32_t EndBlock)
{
	uint32_t i;

	if (CardType == SD_V1 || CardType == SD_V2) {
		StartBlock <<= 9; // 标准卡为字节地址
		EndBlock <<= 9;
	} else if (CardType != SD_HC) {
		return -1; // 未识别的卡
	}	
	Hsmmc_IssueCommand(CMD32, StartBlock, 0, CMD_RESP_R1);
	Hsmmc_IssueCommand(CMD33, EndBlock, 0, CMD_RESP_R1);
	if (!Hsmmc_IssueCommand(CMD38, 0, 0, CMD_RESP_R1B)) {
		for (i=0; i<0x10000; i++) {
			if (Hsmmc_GetCardState() == CARD_TRAN) { // 擦除完成后返回到transfer状态
				Debug("Erasing complete!\r\n");
				return 0; // 擦除成功				
			}
			Delay_us(1000);			
		}		
	}		

	Debug("Erase block failed\r\n");
	return -1; // 擦除失败
}

int32_t Hsmmc_ReadBlock(uint8_t *pBuffer, uint32_t BlockAddr, uint32_t BlockNumber)
{
	uint32_t Address = 0;
	uint32_t ReadBlock;
	uint32_t i;
	uint32_t j;
	int32_t ErrorState;
	uint32_t Temp;
	
	if (pBuffer == 0 || BlockNumber == 0) {
		return -1;
	}

	__REGw(HSMMC_BASE+NORINTSTS_OFFSET) = __REGw(HSMMC_BASE+NORINTSTS_OFFSET); // 清除中断标志
	__REGw(HSMMC_BASE+ERRINTSTS_OFFSET) = __REGw(HSMMC_BASE+ERRINTSTS_OFFSET); // 清除错误中断标志	
	
	while (BlockNumber > 0) {
		if (BlockNumber <= MAX_BLOCK) {
			ReadBlock = BlockNumber; // 读取的块数小于65536 Block
			BlockNumber = 0; // 剩余读取块数为0
		} else {
			ReadBlock = MAX_BLOCK; // 读取的块数大于65536 Block,分多次读
			BlockNumber -= ReadBlock;
		}
		// 根据sd主机控制器标准,按顺序写入主机控制器相应的寄存器		
		__REGw(HSMMC_BASE+BLKSIZE_OFFSET) = (7<<12) | (512<<0); // 最大DMA缓存大小,block为512字节			
		__REGw(HSMMC_BASE+BLKCNT_OFFSET) = ReadBlock; // 写入这次读block数目
		
		if (CardType == SD_HC) {
			Address = BlockAddr; // SDHC卡写入地址为block地址
		} else if (CardType == SD_V1 || CardType == SD_V2) {
			Address = BlockAddr << 9; // 标准卡写入地址为字节地址
		}	
		BlockAddr += ReadBlock; // 下一次读块的地址
		__REG(HSMMC_BASE+ARGUMENT_OFFSET) = Address; // 写入命令参数		
		
		if (ReadBlock == 1) {
			// 设置传输模式,DMA禁能,读单块
			__REGw(HSMMC_BASE+TRNMOD_OFFSET) = (0<<5) | (1<<4) | (0<<2) | (1<<1) | (0<<0);
			// 设置命令寄存器,单块读CMD17,R1回复
			__REGw(HSMMC_BASE+CMDREG_OFFSET) = (CMD17<<8)|(1<<5)|(1<<4)|(1<<3)|0x2;
		} else {
			// 设置传输模式,DMA禁能,读多块			
			__REGw(HSMMC_BASE+TRNMOD_OFFSET) = (1<<5) | (1<<4) | (1<<2) | (1<<1) | (0<<0);
			// 设置命令寄存器,多块读CMD18,R1回复	
			__REGw(HSMMC_BASE+CMDREG_OFFSET) = (CMD18<<8)|(1<<5)|(1<<4)|(1<<3)|0x2;			
		}	
		ErrorState = Hsmmc_WaitForCommandDone();
		if (ErrorState) {
			Debug("Read Command error\r\n");
			return ErrorState;
		}	
		for (i=0; i<ReadBlock; i++) {
			ErrorState = Hsmmc_WaitForBufferReadReady();
			if (ErrorState) {
				return ErrorState;
			}
			if (((uint32_t)pBuffer & 0x3) == 0) {	
				for (j=0; j<512/4; j++) {
					*(uint32_t *)pBuffer = __REG(HSMMC_BASE+BDATA_OFFSET);
					pBuffer += 4;
				}
			} else {
				for (j=0; j<512/4; j++) {
					Temp = __REG(HSMMC_BASE+BDATA_OFFSET);
					*pBuffer++ = (uint8_t)Temp;
					*pBuffer++ = (uint8_t)(Temp>>8);
					*pBuffer++ = (uint8_t)(Temp>>16);
					*pBuffer++ = (uint8_t)(Temp>>24);
				}
			}						
		}
		
		ErrorState = Hsmmc_WaitForTransferDone();
		if (ErrorState) {
			Debug("Read block error\r\n");
			return ErrorState;
		}	
	}
	return 0; // 所有块读完
}

int32_t Hsmmc_WriteBlock(uint8_t *pBuffer, uint32_t BlockAddr, uint32_t BlockNumber)
{
	uint32_t Address = 0;
	uint32_t WriteBlock;
	uint32_t i;
	uint32_t j;
	int32_t ErrorState;
	
	if (pBuffer == 0 || BlockNumber == 0) {
		return -1; // 参数错误
	}	
	
	__REGw(HSMMC_BASE+NORINTSTS_OFFSET) = __REGw(HSMMC_BASE+NORINTSTS_OFFSET); // 清除中断标志
	__REGw(HSMMC_BASE+ERRINTSTS_OFFSET) = __REGw(HSMMC_BASE+ERRINTSTS_OFFSET); // 清除错误中断标志
	
	while (BlockNumber > 0) {	
		if (BlockNumber <= MAX_BLOCK) {
			WriteBlock = BlockNumber;// 写入的块数小于65536 Block
			BlockNumber = 0; // 剩余写入块数为0
		} else {
			WriteBlock = MAX_BLOCK; // 写入的块数大于65536 Block,分多次写
			BlockNumber -= WriteBlock;
		}
		if (WriteBlock > 1) { // 多块写,发送ACMD23先设置预擦除块数
			Hsmmc_IssueCommand(CMD55, RCA<<16, 0, CMD_RESP_R1);
			Hsmmc_IssueCommand(CMD23, WriteBlock, 0, CMD_RESP_R1);
		}
		
		// 根据sd主机控制器标准,按顺序写入主机控制器相应的寄存器			
		__REGw(HSMMC_BASE+BLKSIZE_OFFSET) = (7<<12) | (512<<0); // 最大DMA缓存大小,block为512字节		
		__REGw(HSMMC_BASE+BLKCNT_OFFSET) = WriteBlock; // 写入block数目	
		
		if (CardType == SD_HC) {
			Address = BlockAddr; // SDHC卡写入地址为block地址
		} else if (CardType == SD_V1 || CardType == SD_V2) {
			Address = BlockAddr << 9; // 标准卡写入地址为字节地址
		}
		BlockAddr += WriteBlock; // 下一次写地址
		__REG(HSMMC_BASE+ARGUMENT_OFFSET) = Address; // 写入命令参数			
		
		if (WriteBlock == 1) {
			// 设置传输模式,DMA禁能写单块
			__REGw(HSMMC_BASE+TRNMOD_OFFSET) = (0<<5) | (0<<4) | (0<<2) | (1<<1) | (0<<0);
			// 设置命令寄存器,单块写CMD24,R1回复
			__REGw(HSMMC_BASE+CMDREG_OFFSET) = (CMD24<<8)|(1<<5)|(1<<4)|(1<<3)|0x2;			
		} else {
			// 设置传输模式,DMA禁能写多块
			__REGw(HSMMC_BASE+TRNMOD_OFFSET) = (1<<5) | (0<<4) | (1<<2) | (1<<1) | (0<<0);
			// 设置命令寄存器,多块写CMD25,R1回复
			__REGw(HSMMC_BASE+CMDREG_OFFSET) = (CMD25<<8)|(1<<5)|(1<<4)|(1<<3)|0x2;					
		}
		ErrorState = Hsmmc_WaitForCommandDone();
		if (ErrorState) {
			Debug("Write Command error\r\n");
			return ErrorState;
		}	
		
		for (i=0; i<WriteBlock; i++) {
			ErrorState = Hsmmc_WaitForBufferWriteReady();
			if (ErrorState) {
				return ErrorState;
			}
			if (((uint32_t)pBuffer & 0x3) == 0) {
				for (j=0; j<512/4; j++) {
					__REG(HSMMC_BASE+BDATA_OFFSET) = *(uint32_t *)pBuffer;
					pBuffer += 4;
				}
			} else {
				for (j=0; j<512/4; j++) {
					__REG(HSMMC_BASE+BDATA_OFFSET) = pBuffer[0] + ((uint32_t)pBuffer[1]<<8) +
					((uint32_t)pBuffer[2]<<16) + ((uint32_t)pBuffer[3]<<24);
					pBuffer += 4;
				}
			}	
		}
		
		ErrorState = Hsmmc_WaitForTransferDone();
		if (ErrorState) {
			Debug("Write block error\r\n");
			return ErrorState;
		}
		for (i=0; i<0x10000000; i++) {
			if (Hsmmc_GetCardState() == CARD_TRAN) { // 需在transfer status
				break; // 状态正确
			}
		}
		if (i == 0x10000000) {
			return -3; // 状态错误或Programming超时
		}		
	}
	return 0; // 写完所有数据
}

int Hsmmc_Init(void)
{
	int32_t Timeout;
	uint32_t Capacity;
	uint32_t i;
	uint32_t OCR;
	uint32_t Temp;
	uint8_t SwitchStatus[64];
	SD_SCR SCR;
	uint8_t CSD[16];
	uint32_t c_size, c_size_multi, read_bl_len;	

	// 设置HSMMC的接口引脚配置
#if (HSMMC_NUM == 0)
	// channel 0,GPG0[0:6] = CLK, CMD, CDn, DAT[0:3]
	GPG0CON_REG = 0x2222222;
	// pull up enable
	GPG0PUD_REG = 0x2aaa;
	GPG0DRV_REG = 0x3fff;
	// channel 0 clock src = SCLKEPLL = 96M
	CLK_SRC4_REG = (CLK_SRC4_REG & (~(0xf<<0))) | (0x7<<0);
	// channel 0 clock = SCLKEPLL/2 = 48M
	CLK_DIV4_REG = (CLK_DIV4_REG & (~(0xf<<0))) | (0x1<<0);	
	
#elif (HSMMC_NUM == 1)
	// channel 1,GPG1[0:6] = CLK, CMD, CDn, DAT[0:3]
	GPG1CON_REG = 0x2222222;
	// pull up enable
	GPG1PUD_REG = 0x2aaa;
	GPG1DRV_REG = 0x3fff;
	// channel 1 clock src = SCLKEPLL = 96M
	CLK_SRC4_REG = (CLK_SRC4_REG & (~(0xf<<4))) | (0x7<<4);
	// channel 1 clock = SCLKEPLL/2 = 48M
	CLK_DIV4_REG = (CLK_DIV4_REG & (~(0xf<<4))) | (0x1<<4);
	
#elif (HSMMC_NUM == 2)
	// channel 2,GPG2[0:6] = CLK, CMD, CDn, DAT[0:3]
	GPG2CON_REG = 0x2222222;
	// pull up enable
	GPG2PUD_REG = 0x2aaa;
	GPG2DRV_REG = 0x3fff;
	// channel 2 clock src = SCLKEPLL = 96M
	CLK_SRC4_REG = (CLK_SRC4_REG & (~(0xf<<8))) | (0x7<<8);
	// channel 2 clock = SCLKEPLL/2 = 48M
	CLK_DIV4_REG = (CLK_DIV4_REG & (~(0xf<<8))) | (0x1<<8);	
	
#elif (HSMMC_NUM == 3)
	// channel 3,GPG3[0:6] = CLK, CMD, CDn, DAT[0:3]
	GPG3CON_REG = 0x2222222;
	// pull up enable
	GPG3PUD_REG = 0x2aaa;
	GPG3DRV_REG = 0x3fff;
	// channel 3 clock src = SCLKEPLL = 96M
	CLK_SRC4_REG = (CLK_SRC4_REG & (~(0xf<<12))) | (0x7<<12);
	// channel 3 clock = SCLKEPLL/2 = 48M
	CLK_DIV4_REG = (CLK_DIV4_REG & (~(0xf<<12))) | (0x1<<12);	
	
#endif
	// software reset for all 复位主机SoC控制器,而不是复位SD卡
	__REGb(HSMMC_BASE+SWRST_OFFSET) = 0x1;
	Timeout = 1000; // Wait max 10 ms
	while (__REGb(HSMMC_BASE+SWRST_OFFSET) & (1<<0)) {
		if (Timeout == 0) {
			return -1; // reset timeout
		}
		Timeout--;
		Delay_us(10);
	}	
	// 上面设置的是SoC的SD控制器的时钟,现在设置的是SD卡的时钟
	Hsmmc_SetClock(400000); // 400k
	__REGb(HSMMC_BASE+TIMEOUTCON_OFFSET) = 0xe; // 最大超时时间
	__REGb(HSMMC_BASE+HOSTCTL_OFFSET) &= ~(1<<2); // 正常速度模式
	// 清除正常中断状态标志
	__REGw(HSMMC_BASE+NORINTSTS_OFFSET) = __REGw(HSMMC_BASE+NORINTSTS_OFFSET);
	// 清除错误中断状态标志
	__REGw(HSMMC_BASE+ERRINTSTS_OFFSET) = __REGw(HSMMC_BASE+ERRINTSTS_OFFSET);
	__REGw(HSMMC_BASE+NORINTSTSEN_OFFSET) = 0x7fff; // [14:0]中断状态使能
	__REGw(HSMMC_BASE+ERRINTSTSEN_OFFSET) = 0x3ff; // [9:0]错误中断状态使能
	__REGw(HSMMC_BASE+NORINTSIGEN_OFFSET) = 0x7fff; // [14:0]中断信号使能	
	__REGw(HSMMC_BASE+ERRINTSIGEN_OFFSET) = 0x3ff; // [9:0]错误中断信号使能
	
	// 从这里开始和SD卡通信,通信其实就是发命令然后收响应
	Hsmmc_IssueCommand(CMD0, 0, 0, CMD_RESP_NONE); // 复位所有卡到空闲状态	
		
	CardType = UNUSABLE; // 卡类型初始化不可用
	if (Hsmmc_IssueCommand(CMD8, 0x1aa, 0, CMD_RESP_R7)) { // 没有回复,MMC/sd v1.x/not card
			for (i=0; i<100; i++) {
				// CMD55 + CMD41 = ACMD41
				Hsmmc_IssueCommand(CMD55, 0, 0, CMD_RESP_R1);
				if (!Hsmmc_IssueCommand(CMD41, 0, 0, CMD_RESP_R3)) { // CMD41有回复说明为sd卡
					OCR = __REG(HSMMC_BASE+RSPREG0_OFFSET); // 获得回复的OCR(操作条件寄存器)值
					if (OCR & 0x80000000) { // 卡上电是否完成上电流程,是否busy
						CardType = SD_V1; // 正确识别出sd v1.x卡
						Debug("SD card version 1.x is detected\r\n");
						break;
					}
				} else {
					// MMC卡识别
					Debug("MMC card is not supported\r\n");
					return -1;
				}
				Delay_us(1000);				
			}
	} else { // sd v2.0
		Temp = __REG(HSMMC_BASE+RSPREG0_OFFSET);
		if (((Temp&0xff) == 0xaa) && (((Temp>>8)&0xf) == 0x1)) { // 判断卡是否支持2.7~3.3v电压
			OCR = 0;
			for (i=0; i<100; i++) {
				OCR |= (1<<30);
				Hsmmc_IssueCommand(CMD55, 0, 0, CMD_RESP_R1);
				Hsmmc_IssueCommand(CMD41, OCR, 0, CMD_RESP_R3); // reday态
				OCR = __REG(HSMMC_BASE+RSPREG0_OFFSET);
				if (OCR & 0x80000000) { // 卡上电是否完成上电流程,是否busy
					if (OCR & (1<<30)) { // 判断卡为标准卡还是高容量卡
						CardType = SD_HC; // 高容量卡
						Debug("SDHC card is detected\r\n");
					} else {
						CardType = SD_V2; // 标准卡
						Debug("SD version 2.0 standard card is detected\r\n");
					}
					break;
				}
				Delay_us(1000);
			}
		}
	}
	if (CardType == SD_HC || CardType == SD_V1 || CardType == SD_V2) {
		Hsmmc_IssueCommand(CMD2, 0, 0, CMD_RESP_R2); // 请求卡发送CID(卡ID寄存器)号,进入ident
		Hsmmc_IssueCommand(CMD3, 0, 0, CMD_RESP_R6); // 请求卡发布新的RCA(卡相对地址),Stand-by状态
		RCA = (__REG(HSMMC_BASE+RSPREG0_OFFSET) >> 16) & 0xffff; // 从卡回复中得到卡相对地址
		Hsmmc_IssueCommand(CMD7, RCA<<16, 0, CMD_RESP_R1); // 选择已标记的卡,transfer状态
		
		Hsmmc_Get_SCR(&SCR);
		if (SCR.SD_SPEC == 0) { // sd 1.0 - sd 1.01
			// Version 1.0 doesn't support switching
			Hsmmc_SetClock(24000000); // 设置SDCLK = 48M/2 = 24M			
		} else { // sd 1.10 / sd 2.0
			Temp = 0;
			for (i=0; i<4; i++) {
				if (Hsmmc_Switch(0, 0, 1, SwitchStatus) == 0) { // switch check
					if (!(SwitchStatus[34] & (1<<1))) { // Group 1, function 1 high-speed bit 273					
						// The high-speed function is ready
						if (SwitchStatus[50] & (1<<1)) { // Group, function 1 high-speed support bit 401
							// high-speed is supported
							if (Hsmmc_Switch(1, 0, 1, SwitchStatus) == 0) { // switch
								if ((SwitchStatus[47] & 0xf) == 1) { // function switch in group 1 is ok?
									// result of the switch high-speed in function group 1
									Debug("Switch to high speed mode: CLK @ 50M\r\n");
									Hsmmc_SetClock(48000000); // 设置SDCLK = 48M	
									Temp = 1;									
								}
							}
						}
						break;
					}
				} 
			}
			if (Temp == 0) {
				Hsmmc_SetClock(24000000); // 设置SDCLK = 48M/2 = 24M
			}
		}
			
		if (!Hsmmc_SetBusWidth(4)) {
			Debug("Set bus width error\r\n");
			return -1; // 位宽设置出错
		}
		if (Hsmmc_GetCardState() == CARD_TRAN) { // 此时卡应在transfer态
			if (!Hsmmc_IssueCommand(CMD16, 512, 0, CMD_RESP_R1)) { // 设置块长度为512字节
				__REGw(HSMMC_BASE+NORINTSTS_OFFSET) = 0xffff; // 清除中断标志
				Hsmmc_Get_CSD(CSD);
				if ((CSD[15]>>6) == 0) { // CSD v1.0->sd V1.x, sd v2.00 standar
					read_bl_len = CSD[10] & 0xf; // [83:80]
					c_size_multi = ((CSD[6] & 0x3) << 1) + ((CSD[5] & 0x80) >> 7); // [49:47]
					c_size = ((int32_t)(CSD[9]&0x3) << 10) + ((int32_t)CSD[8]<<2) + (CSD[7]>>6); // [73:62]				
					Capacity = (c_size + 1) << ((c_size_multi + 2) + (read_bl_len-9)); // block(512 byte)
				} else {
					c_size = ((CSD[8]&0x3f) << 16) + (CSD[7]<<8) + CSD[6]; // [69:48]
					// 卡容量为字节(c_size+1)*512K byte,以1扇区512 byte字,卡的扇区数为		
					Capacity = (c_size+1) << 10;// block (512 byte)
				}
				Debug("Card Initialization succeed\r\n");				
				Debug("Capacity: %ldMB\r\n", Capacity / (1024*1024 / 512));
				return 0; // 初始化成功							
			}
		}
	}
	Debug("Card Initialization failed\r\n");
	return -1; // 卡工作异常
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值