一、NAND FLASH的特点
S3C6410的NAND FLASH控制器有如下特点
1、自导入模式:复位后,引导代码被送入到8KB的STEPPINGSTONE中,引导代码移动完毕,引导代码将在STEPPINGSTONE中执行。导入期间,NAND FLASH控制器不支持ECC矫正。
2、NAND FLSH 控制器I/F:支持512字节和2KB页
3、软件模式:用户可以直接访问nand flash 控制器,该特性可以用于读/檫/编程nand flash 存储器。
1)写命令寄存器=NAND FLASH存储器命令周期
2)写地址寄存器=NAND FLASH存储器地址周期
3)写数据寄存器=写数据到NAND FLASH存储器(写周期)
4)读数据寄存器=从NAND FLASH 存储器数据(读周期)
5)读主ECC寄存器和备用ECC寄存器=从NAND FLASH存储器读数据
4、接口:8位NAND FLASH存储器接口总线
5、硬件:ECC产生、检测和标志(软件纠正)
6、支持SLC和MLC的NAND FLASH控制器:1位ECC用于SLC,4位ECC用于MLC的NAND FLASH
7、特殊功能寄存器I/F:支持字节/半字/字数据访问ECC的数据寄存器,用字来访问其他寄存器
8、STEPPINGSTONE I/F:支持字节/半字/字数据的访问
9、8KB的内部SRAM的缓冲器STEPPINGSTONE,在NAND FLASH引导后可做其他用途使用
该Tiny6410 开发板使用的NAND FLASH的类型为MLC大小为2G,型号K9GA08U0E-S
二、驱动设计
第一步:设置NAND FLASH的控制寄存器
通过S3C6410数据手册可知NNAD FLASH的控制寄存器为NFCONF
寄存器 地址
NFCONF 0x70200000
在NFCONF寄存器中主要用到TACLS、TWRPH0、TWRPH1,这三个变量。这三个变量用于配置nand flash 的时序。通过时序图
由上图可知,TACLS为CLE/ALE 有效到nWE有效之间的持续时间。TWRPH0 位nWE的有效持续时间。TWRPH1为nWE无效到CLE/ALE 无效之间的持续时间。这些时间都是以HCLK为单位的。
通过K9F2G08U0E数据手册(如下图)可知tWp 和TWRPH0相对应,(tcls-twp)与TALS相对应,tCLH与TWRPH1相对应。
K9F2G08U0给出的值都是最小值。故在取值是只要满足该最小值就可以了。因此在这里我将TACLS、TWRPH0、TWRPH1分别取值为0x2、0xF和0x7。
故将NFCONF寄存器的设值为((0x2<<12)|(0xf<<8)|(0x7<<4))
第二步:使能NAND FLASH
通过S3C6410的诗句手册可以知道使能NAND FLASH的寄存器为NFCONT
寄存器 地址
NFCONT 0x7020004
由寄存器的描述可知,NFCONT的bit0位是控制NAND FLASH的。故将NFCONT的第零位设值为1即使能了NAND FLASH。
第三步:读操作(页读)
如下图可知读操作主要分五步走
1、发片选:即设置NFCONT的bit1为0
2、发命令:0x00,即往NFCMD写0x00
3、发地址:由下图知地址需要分5个时钟周期来发送的,即往NFADDR写入地址,因为NFADDR一次只能接受8bit的数据
4、发读命令:0x30,即往NFCMD写0x30
5、连续读2048个字节,即连续读NFDATA寄存器2048次。
第四步:拷贝
调用NAND FLASH的读操作,直到把.bin文件完全的从NAND FLASH中拷贝到 DRAM中。
第五步:编码运行
主要代码实现如下
//start.S
.global _start
_start
//把外设告诉CPU
ldr r0, =0x70000000
orr r0,r0,#0x13
mcr p15,0,r0,c15,c2,4
//官看门狗
ldr r0,=0x7E004000
mov r1,#0
str r1,[r0]
//设置栈
ldr sp,=0x0c002000
//开启icaches
#ifdef CONFIG_SYS_ICACHE_OFF
bic r0,r0,#0x00001000
#else
orr r0,r0,#0x00001000
mcr p15,0,r0,c1,c0,0x00001000
#endif
//设置时钟
bl clock_init
//初始换sdram
bl sdram_init
//初始化nand flash
bl nand_init
//重定位,把代码、数据复制到他的链接中去
adr r0,_start @_start的当前地址
ldr r1,=_star @_start的链接地址
ldr r2,=bss_start
sub r2,r2,r1
cmp r0,r1
beq clean_bss
bl copy2ddr
cmp r0,#0
bne halt
//清理bss段,把bss对应的内存清零
clean_bss:
ldr r0,=bss_start
ldr r1,=bss_end
mov r3,#0
cmp r0,r1
beq on_ddr
clean_loop:
str r3,[r0],#4
cmp r0,r1
bne clean_loop
//跳转
on_ddr:
ldr pc,=main
halt:
b halt
//nand.c
#include "Tiny6410Addr.h"
//nand flash 的命令
#define NAND_CMD_READ0 0
#define NAND_CMD_READ1 1
#define NAND_CMD_RNDOUT 5
#define NAND_CMD_PAGEPROG 0x10
#define NAND_CMD_READOOB 0x50
#define NAND_CMD_ERASE1 0x60
#define NAND_CMD_STATUS 0x70
#define NAND_CMD_STATUS_MULTI 0x71
#define NAND_CMD_SEQIN 0x80
#define NAND_CMD_RNDIN 0x85
#define NAND_CMD_READID 0x90
#define NAND_CMD_ERASE2 0xd0
#define NAND_CMD_RESET 0xff
/*
typedef struct{
void (*nand_reset)(void);
void (*nand_select_chip)(void);
void (*nand_deselect_chip)(void);
void (*write_cmd)(int Cmd);
void (*read_cmd)(int Cmd);
void (*wait_idle)(void);
void (*write_addr)(unsigned int addr);
}NAND_CHIP;
static NAND_CHIP nand_chip;
*/
void nand_init(void);
//Tiny6410 nand Flash 的操作函数申明
static void Tiny6410_nand_reset(void); //重启
static void Tiny6410_nand_select_chip(void); //片选使能
static void Tiny6410_nand_diselect_chip(void); //关闭片选
static void Tiny6410_write_cmd(int cmd); //写命令
static void Tiny6410_read_cmd(int cmd); //读命令
static void Tiny6410_wait_idle(void); //等待
static void Tiny6410_write_addr(unsigned int addr); //写地址
/
//Tiny6410 nand Flash 的操作函数实现
static void Tiny6410_nand_reset(void)
{
Tiny6410_nand_select_chip();
Tiny6410_write_cmd(int cmd);
Tiny6410_wait_idle();
Tiny6410_nand_diselect_chip();
}
//片选使能
static void Tiny6410_nand_select_chip(void)
{
NFCONT &= ~(1 << 1);
}
//取消片选
static void Tiny6410_nand_diselect_chip(void)
{
NFCONT |= (1 << 1);
}
static void Tiny6410_write_cmd(int cmd)
{
}
//等待数据
static void Tiny6410_wait_idle(void)
{
do {
while(!(NFSTAT & (1 << 0)));
} while(0)
}
//读命令
static void Tiny6410_read_cmd(int cmd)
{
NFCMD = cmd;
}
//写命令
static void Tiny6410_write_cmd(int cmd)
{
//NFCMD = cmd;
}
static void Tiny6410_write_addr(unsigned long addr)
{
NFADDR = 0;
NFADDR = 0;
NFADDR = (addr) & 0xff;
NFADDR = (addr >> 8) & 0xff;
NFADDR = (addr >> 16) & 0xff;
}
///
//nand flash 初始化
void init_nand(void)
{
//设置NAND FLASH 控制器
NFCONF = ((0x2 << 12)|(0xf << 8)|(0x7 << 4));
NFCONT |= ((0x3 << 0));
}
//读以页数据
static int nand_read_page(unsigned char *buf,unsigned long addr)
{
int i;
//发出片选
Tiny6410_nand_select_chip();
//发送读命令
Tiny6410_read_cmd(NAND_CMD_READ0);
//发送地址
Tiny6410_write_addr(addr);
//发送读命令
Tiny6410_read_cmd(NAND_CMD_READSTART);
//等待数据
Tiny6410_wait_idle();
//连续读取2048个字节
for(i=0; i < page_size; i++)
{
*buf++ = NFDATA8_REG;
}
//取消片选
Tiny6410_nand_diselect_chip();
return 0;
}
//从NAND 中拷贝到DRAM
int copy2dram(unsigned int nand_start,unsigned int dram_start,unsigned int len)
{
unsigned char *buf = (unsigned char *)ddr_start;
int i;
unsigned int page_shift = 11;
//发片选
Tiny6410_nand_select_chip();
// 使len为2048的整数倍
len = (len/2048+1)*2048;
// 循环拷贝,每次拷贝一页数据
for (i = 0; i < (len>>page_shift); i++, buf+=(1<<page_shift))
{
// 读一页,即2048byte
nandll_read_page(buf, i);
}
return 0;
}
//nand.lds
SECTIONS {
. = 0x50000000;
.text : {
start.o
clock.o
sdram.o
nand.o
* (.text)
}
.rodata : {
* (.rodata)
}
.data : {
* (.data)
}
bss_start = .;
.bss ALIGN(4) : { *(.bss) *(COMMON) }
bss_end = .;
}
//TinyAddr.h
#ifndef _Tiny6410Addr_H
#define _Tiny6410Addr_H
//GPK
#define GPKIO_BASE (0x7F008800)
#define rGPKCON0 (*((volatile unsigned long *)(GPKIO_BASE+0x00)))
#define rGPKDAT (*((volatile unsigned long *)(GPKIO_BASE+0x08)))
//CLOCK
#define APLL_LOCK (*((volatile unsigned long *)0x7E00F000))
#define MPLL_LOCK (*((volatile unsigned long *)0x7E00F004))
#define EPLL_LOCK (*((volatile unsigned long *)0x7E00F008))
#define OTHERS (*((volatile unsigned long *)0x7e00f900))
#define CLK_DIV0 (*((volatile unsigned long *)0x7E00F020))
#define APLL_CON (*((volatile unsigned long *)0x7E00F00C))
#define MPLL_CON (*((volatile unsigned long *)0x7E00F010))
#define CLK_SRC (*((volatile unsigned long *)0x7E00F01C))
//GPA /uart
#define ULCON0 (*((volatile unsigned long *)0x7F005000))
#define UCON0 (*((volatile unsigned long *)0x7F005004))
#define UFCON0 (*((volatile unsigned long *)0x7F005008))
#define UMCON0 (*((volatile unsigned long *)0x7F00500C))
#define UTRSTAT0 (*((volatile unsigned long *)0x7F005010))
#define UFSTAT0 (*((volatile unsigned long *)0x7F005018))
#define UTXH0 (*((volatile unsigned char *)0x7F005020))
#define URXH0 (*((volatile unsigned char *)0x7F005024))
#define UBRDIV0 (*((volatile unsigned short *)0x7F005028))
#define UDIVSLOT0 (*((volatile unsigned short *)0x7F00502C))
#define GPACON (*((volatile unsigned long *)0x7F008000))
//Sdram
#define P1MEMSTAT (*((volatile unsigned long *)0x7e001000))
#define P1MEMCCMD (*((volatile unsigned long *)0x7e001004))
#define P1DIRECTCMD (*((volatile unsigned long *)0x7e001008))
//#define MEMCCMD (*((volatile unsigned long *)0x7e001004))
#define P1REFRESH (*((volatile unsigned long *)0x7e001010))
#define P1CASLAT (*((volatile unsigned long *)0x7e001014))
#define MEM_SYS_CFG (*((volatile unsigned long *)0x7e00f120))
#define P1MEMCFG (*((volatile unsigned long *)0x7e00100c))
#define P1T_DQSS (*((volatile unsigned long *)0x7e001018))
#define P1T_MRD (*((volatile unsigned long *)0x7e00101c))
#define P1T_RAS (*((volatile unsigned long *)0x7e001020))
#define P1T_RC (*((volatile unsigned long *)0x7e001024))
#define P1T_RCD (*((volatile unsigned long *)0x7e001028))
#define P1T_RFC (*((volatile unsigned long *)0x7e00102c))
#define P1T_RP (*((volatile unsigned long *)0x7e001030))
#define P1T_RRD (*((volatile unsigned long *)0x7e001034))
#define P1T_WR (*((volatile unsigned long *)0x7e001038))
#define P1T_WTR (*((volatile unsigned long *)0x7e00103c))
#define P1T_XP (*((volatile unsigned long *)0x7e001040))
#define P1T_XSR (*((volatile unsigned long *)0x7e001044))
#define P1T_ESR (*((volatile unsigned long *)0x7e001048))
#define P1MEMCFG2 (*((volatile unsigned long *)0X7e00104c))
#define P1_chip_0_cfg (*((volatile unsigned long *)0x7e001200))
//Nand
#define NAND_BASE (0x70200000)
#define NFCONF (*((volatile unsigned long *)NAND_BASE + 0x00))
#define NFCONT (*((volatile unsigned long *)NAND_BASE + 0x04))
#define NFCMMD (*((volatile unsigned long *)NAND_BASE + 0x08))
#define NFADDR (*((volatile unsigned long *)NAND_BASE + 0x0c))
#define NFDATA (*((volatile unsigned long *)NAND_BASE + 0x10))
#define NFMECCDATA0 (*((volatile unsigned long *)NAND_BASE + 0x14))
#define NFMECCDATA1 (*((volatile unsigned long *)NAND_BASE + 0x18))
#define NFSECCDATA0 (*((volatile unsigned long *)NAND_BASE + 0x1c))
#define NFSBLK (*((volatile unsigned long *)NAND_BASE + 0x20))
#define NFEBLK (*((volatile unsigned long *)NAND_BASE + 0x24))
#define NFSTAT (*((volatile unsigned long *)NAND_BASE + 0x28))
#define NFESTAT0 (*((volatile unsigned long *)NAND_BASE + 0x2c))
#define NFESTAT1 (*((volatile unsigned long *)NAND_BASE + 030))
#define NFMECC0 (*((volatile unsigned long *)NAND_BASE + 0x34))
#define NFMECC1 (*((volatile unsigned long *)NAND_BASE + 0x38))
#define NFSECC (*((volatile unsigned long *)NAND_BASE + 0x3c))
#define NFMLCBITPT (*((volatile unsigned long *)NAND_BASE + 0x40))
/*#define NF8ECCERR0 (*((volatile unsigned long *)NAND_BASE + 0x44))
#define NF8ECCERR1 (*((volatile unsigned long *)NAND_BASE + 0x48))
#define NF8ECCERR2 (*((volatile unsigned long *)NAND_BASE + 0x4c))
#define NFM8ECC0 (*((volatile unsigned long *)NAND_BASE + 0x50))
#define NFM8ECC1 (*((volatile unsigned long *)NAND_BASE + 0x54))
#define NFM8ECC2 (*((volatile unsigned long *)NAND_BASE + 0x58))
#define NFM8ECC3 (*((volatile unsigned long *)NAND_BASE + 0x5c))
#define NFMLC8BITPT0 (*((volatile unsigned long *)NAND_BASE + 0x60))
#define NFMLC8BITPT1 (*((volatile unsigned long *)NAND_BASE + 0x64))
*/
#endif
总结:
综上所述主要分为两个大步。
第一步:读S3C6410数据手册,了解NANDfLASH 的操作流程。
第二步:读NAND FLASH数据手册,了解各个寄存器的具体设置。