本文主要是關(guān)于Nor Flash的相關(guān)介紹,并著重對(duì)Nor Flash的編寫及驅(qū)動(dòng)程序進(jìn)行了相近的闡述。
Nor Flash驅(qū)動(dòng)編寫
1.
Bottom/Top Boot Sect(底部/頂部 啟動(dòng)塊)
所謂的boot sect,是指的是Nor Flash和Nand Flash不太一樣。Nand Flash從開始到最后,都是由同樣大小的page所組成的。
而Nor Flash,一般都是有個(gè)boot sect,好像是由于歷史原因,常將Nor Flash用于作為存儲(chǔ)啟動(dòng)代碼的設(shè)備,也就是從Nor Flash啟動(dòng),所以,這個(gè)boot sect塊,專門設(shè)計(jì)出來(lái),用于存放啟動(dòng)代碼。如果詳細(xì)解釋,按照datasheet中的描述就是,第一個(gè)或最后一個(gè),此處是bottom sect,所以是最后一個(gè)64KB大小的塊,被分為4個(gè)獨(dú)立的塊。第一個(gè)16KB,用于少量的系統(tǒng)初始化代碼。而2個(gè)8KB的塊用于存儲(chǔ)參數(shù),余下的32KB的塊叫做Main Block,用于存儲(chǔ)應(yīng)用程序的代碼。
2.
Sector(扇區(qū))
此處的sector(扇區(qū))也就是flash里面的最小的擦除單位:塊(block)。
所以sector count,也就是有多少個(gè)block。
3.
Sector Count (扇區(qū)數(shù))和Sector List
此處的Nor Flash,M29W320DB,一共有正常的63個(gè)64KB的block,加上上面提到的原先是正常的1個(gè)64KB分成的4個(gè)小塊,所以是63+4=67個(gè)。
而所謂的驅(qū)動(dòng)中的sector list,也就是block list,代碼注釋寫的也很清楚了:
ulong
start[CFG_MAX_FLASH_SECT];
/* physical sector start addresses */
用于存儲(chǔ)每一個(gè)塊的起始地址的。也是需要你驅(qū)動(dòng)初始化的。對(duì)于這里的M29W320DB,也很簡(jiǎn)單了,從開始順序加上塊大小64K,直到最后3個(gè),計(jì)算一下對(duì)應(yīng)地址,填進(jìn)去就可以了。
4.
Protect(寫保護(hù))
Nor Flash從軟件的寄存器配置和硬件上,都提供了對(duì)應(yīng)的保護(hù)機(jī)制,目的是,防止有意或無(wú)意間,把那些不希望被改動(dòng)/刪除的數(shù)據(jù)破壞了。比如有些機(jī)制把Flash的一些系統(tǒng)啟動(dòng)參數(shù)存儲(chǔ)Nor Flash,或者是其他某種原因,只允許你使用部分Nor Flash的空間,所以,把這類需要保護(hù)的部分,在uboot的flash_info_t的結(jié)構(gòu)體中,把對(duì)應(yīng)的位設(shè)置成1:
uchar
protect[CFG_MAX_FLASH_SECT]; /* sector protection status
*/
這樣,之后程序就可以避免有意無(wú)意的擦除有用的數(shù)據(jù)了。
【寫Nor Flash驅(qū)動(dòng)時(shí)的一些注意事項(xiàng)】
1.
位寬(bitwidth,X8/X16/X32)
在Nor Flash控制器,此處我這里用的是,ARM的PromeCell PL172,MPMC(MultiPort Memory Controller),可以接多種不同的存儲(chǔ)設(shè)備,比如SRAM(Static Memory),Nor Flash,而其中又可以分別設(shè)置是否支持Page Mode,Extended wait和寫保護(hù)(啟用寫保護(hù),就可以看出是ROM了)等。
在硬件上MPMC和Nor Flash連接好了之后。在使用Nor Flash之前,要初始化MPMC。
這里說(shuō)的,要注意位寬,是因?yàn)槲议_始就沒注意到,所以,在開始初始化MPMC的時(shí)候,設(shè)置MPMCStaticConfig寄存器的時(shí)候,設(shè)置成了X16(16位),但是,后來(lái)去uboot中找到別人的Nor Flash的驅(qū)動(dòng)(參考的是oardoxcflash.c),發(fā)出的命令去讀ID,也都是X8(8位)的:
addr[0x0AAA] = 0xAA;
addr[0x0555] = 0x55;
addr[0x0AAA] = 0x90;
所以,導(dǎo)致讀取Manufacture ID 和Device ID,都無(wú)法讀正確,讀的都是0xFF。后來(lái)重新去確認(rèn),在配置MPMCStaticConfig的時(shí)候,是配置的X16模式,然后再發(fā)命令,也對(duì)應(yīng)的是按照X16模式發(fā)命令,可以參考:oardmvs1flash.c中的代碼,讀取ID時(shí)是:
addr[0x0555] = 0x00AA;
addr[0x02AA]= 0x0055;
addr[0x0555] = 0x0090;
才能正確讀取到期望出的ID:
value = addr[0];
/* manufacturer ID
*/
讀出來(lái)的是0x20h。
value = addr[1];
/* device ID
*/
讀出來(lái)的是0x22CB
和datasheet中的匹配:
– Manufacturer Code: 0020h
– Bottom Device Code M29W320DB: 22CBh
2.
不同位寬對(duì)應(yīng)不同的時(shí)序
此處說(shuō)的時(shí)序,也就是上面提到的,X8和X16發(fā)的地址,是不一樣的,而且順序也不同。
而且還有一小點(diǎn)要注意的是,記得轉(zhuǎn)換地址成對(duì)應(yīng)的類型:
X8是vu_char *
X16是 vu_short *
這樣再寫入對(duì)應(yīng)的地址和數(shù)值,就可以了。
3.
reset命令
看了uboot中的代碼,好像是其他設(shè)備,多數(shù)的reset命令,都是0xFF。
而這里用到的是STM(STMicroelectronics,后來(lái)好像改成Intel和ST合資的恒憶(Numonyx)了。。。)的 Nor Flash,M29W320DB (32 M, bottom boot sect)
,比較特殊些,是0xF0。
4.
boot sector的位置
剛剛看了下datasheet,很汗的是,本以為bottom sect是底部的sect,是地址最大的那個(gè),結(jié)果datasheet中的是倒敘計(jì)算開始處的,也就是地址最大的那個(gè)塊,是第一個(gè)塊,所以,此處的boot sector 是塊0-3:
# Size (KByte/KWord) Address Range(x8 )/ Address Range (x16)
66 64/32 3F0000h-3FFFFFh
1F8000h-1FFFFFh
。。。。。
。。。。。
3 32/16 008000h-00FFFFh
004000h-007FFFh
2 8/4
006000h-007FFFh
003000h-003FFFh
1 8/4
004000h-005FFFh
002000h-002FFFh
0 16/8 000000h-003FFFh
000000h-001FFFh
Uboot下關(guān)于Nor Flash的驅(qū)動(dòng)問題
nor flash 的使用特點(diǎn)是 : 讀操作可以按地址讀, 寫之前必須進(jìn)行擦除, 一旦擦除必須擦除整個(gè)扇區(qū)。
新型的flash 使用3V 的電壓便可以進(jìn)行整個(gè)扇區(qū)的擦除和寫入操作
任何芯片的使用, 都離不開驅(qū)動(dòng)的支持。 uboot下的nor flash的驅(qū)動(dòng)邏輯非常簡(jiǎn)單。 而且, 對(duì)于符合 CFI ( Common Flash Interface )規(guī)范的flash芯片,驅(qū)動(dòng)有很大的通用性。
uboot 提供了很好的 flash 驅(qū)動(dòng)邏輯 和 flash的使用范例, 這些基本的使用方法在linux里也是同樣的邏輯,只不過(guò)linux下需要加上一層分區(qū)信息。 結(jié)合flash 芯片手冊(cè), 可以對(duì)nor flash的使用邏輯有較為清晰的理解。
nor flash的驅(qū)動(dòng)初始化部分:
arch/mips/cpu/octeon/start.S
board_init_r -》 flash_init()
drivers/mtd/cfi_flash.c
unsigned long flash_init (void){
for (i = 0; i 《 CONFIG_SYS_MAX_FLASH_BANKS; ++i) {
flash_info[i].flash_id = FLASH_UNKNOWN;
…
//由于使用的flash 是新型的CFI 規(guī)范的flash, 沒有使用 CONFIG_FLASH_CFI_LEGACY 這個(gè)宏, 所以flash_detect_legacy直接返回0
if (!flash_detect_legacy(cfi_flash_bank_addr(i), i))
flash_get_size(cfi_flash_bank_addr(i), i);
size += flash_info[i].size;
ulong flash_get_size (phys_addr_t base, int banknum)
{
flash_info_t *info = &flash_info[banknum];
int i, j;
flash_sect_t sect_cnt;
phys_addr_t sector;
unsigned long tmp;
int size_ratio;
uchar num_erase_regions;
int erase_region_size;
int erase_region_count;
struct cfi_qry qry;
unsigned long max_size;
memset(&qry, 0, sizeof(qry));
info-》ext_addr = 0;
info-》cfi_version = 0;
#ifdef CONFIG_SYS_FLASH_PROTECTION
info-》legacy_unlock = 0;
#endif
info-》start[0] = (ulong)map_physmem(base, info-》portwidth, MAP_NOCACHE);
//如果是CFI 接口, 那么有統(tǒng)一的查詢規(guī)范, 將查詢到的信息保存到 qry中
if (flash_detect_cfi (info, &qry)) {
info-》vendor = le16_to_cpu(qry.p_id);
info-》ext_addr = le16_to_cpu(qry.p_adr) * 2;
debug(“extended address is 0x%x ”, info-》ext_addr);
num_erase_regions = qry.num_erase_regions;
if (info-》ext_addr) {
#define FLASH_OFFSET_CFI_RESP 0x20
flash_detect_cfi -》
static int __flash_detect_cfi (flash_info_t * info, struct cfi_qry *qry)
{
int cfi_offset;
for (cfi_offset=0;
cfi_offset 《 sizeof(flash_offset_cfi) / sizeof(uint);
cfi_offset++) {
/* Issue FLASH reset command */
flash_cmd_reset(info);
flash_write_cmd (info, 0, flash_offset_cfi[cfi_offset],
FLASH_CMD_CFI);
//向0x20 地址進(jìn)行查詢, CFI 規(guī)定 , 前三個(gè)字符應(yīng)該是 Q, R, Y
if (flash_isequal (info, 0, FLASH_OFFSET_CFI_RESP, ‘Q’)
&& flash_isequal (info, 0, FLASH_OFFSET_CFI_RESP + 2, ‘R’)
&& flash_isequal (info, 0, FLASH_OFFSET_CFI_RESP + 4, ‘Y’)) {
//如果確認(rèn)為CFI 規(guī)范, 那么就按照 struct cfi_qry數(shù)據(jù)結(jié)構(gòu)進(jìn)行查詢
flash_read_cfi(info, qry, FLASH_OFFSET_CFI_RESP,
sizeof(struct cfi_qry));
…
//在進(jìn)行CFI 規(guī)范查詢之后, 還要將addr_unlock1 , addr_unlock2 進(jìn)行賦值, 這兩個(gè)地址分別表示8位寬的地址和16位寬的地址, 可以實(shí)現(xiàn)byte和word的操作。
//一般地, 我們只使用addr_unlock1
//在一些代碼里, 這兩個(gè)數(shù)值就通過(guò)宏定義來(lái)實(shí)現(xiàn)的
info-》addr_unlock1 = 0xaaa;
info-》addr_unlock2 = 0x555;
…
}
下面是flash 芯片手冊(cè)里CFI 規(guī)范查詢的信息:
cfi_qry 定義:
struct cfi_qry {
u8 qry[3]; //保存 Q, R, Y
u16 p_id; //Primary algorithm
u16 p_adr; //Address for primary algorithm
u16 a_id; //Alternate
u16 a_adr; //Address for alternate
u8 vcc_min; // 最小Vcc
u8 vcc_max; //最大Vcc
u8 vpp_min; //最小Vpp
u8 vpp_max; //最大Vpp
u8 word_write_timeout_typ; //字節(jié)寫典型超時(shí)
u8 buf_write_timeout_typ; //緩存寫典型超時(shí)
u8 block_erase_timeout_typ; //塊擦除典型超時(shí)
u8 chip_erase_timeout_typ; //整片擦除典型超時(shí)
u8 word_write_timeout_max; //字節(jié)寫最大超時(shí)
u8 buf_write_timeout_max; //緩存寫最大超時(shí)
u8 block_erase_timeout_max; //塊寫最大超時(shí)
u8 chip_erase_timeout_max; //整片擦除最大超時(shí)
u8 dev_size; //芯片大小
u16 interface_desc; //接口描述
u16 max_buf_write_size; //最大緩存寫長(zhǎng)度
u8 num_erase_regions; //擦除塊扇區(qū)數(shù)量
u32 erase_region_info[NUM_ERASE_REGIONS]; //4個(gè)塊區(qū)的信息
} __attribute__((packed));
從上圖可以看到, 是獲取了CFI query identification string , System interface information , Device geometry definition 信息,對(duì)照手冊(cè), 就可以知道成員的數(shù)值
其中, 最為重要的是擦寫扇區(qū)信息 erase_region_info, 對(duì)應(yīng)手冊(cè)的如下信息:
手冊(cè)給出了扇區(qū)的信息, 第一部分說(shuō)明了扇區(qū)(block)的個(gè)數(shù) : 0xff + 1 = 256 個(gè), 第二部分說(shuō)明了一個(gè)扇區(qū)(block)大?。?0x200 * 256 =131072, 即128K字節(jié)
我們的flash, 為00ff, 和0200 。那么uint32_t的tmp 的數(shù)值應(yīng)該為: 0x020000ff
tmp = le32_to_cpu(qry.erase_region_info[i]);
debug(“erase region %u: 0x%08lx ”, i, tmp);
erase_region_count = (tmp & 0xffff) + 1;
tmp 》》= 16;
erase_region_size = (tmp & 0xffff) ? ((tmp & 0xffff) * 256) : 128;
tmp = qry.erase_region_info[i] = 0x20000ff
tmp 》》=16 后, tmp = 0x200
擦寫扇區(qū)的大小 erase_region_size = (tmp & 0xffff) * 256 = 0x20000 , 即一個(gè)扇區(qū)的大小為0x2000字節(jié)。
擦寫扇區(qū)的個(gè)數(shù) erase_region_count為0x201, 即256個(gè)扇區(qū)
那么, 可以知道, 整個(gè)nor flash 總的容量為: 0x2000 * 256 = 33554432 字節(jié),
驗(yàn)證一下: 33554432 / 1024 / 1024 = 32 M
sect_cnt = 0;
sector = base;//基地址為 0x1dc00000
…
那么會(huì)循環(huán)256次。
for (j = 0; j 《 erase_region_count; j++) {
。。
//在256次循環(huán)中, 256個(gè)start成員保存各個(gè)扇區(qū)的地址
info-》start[sect_cnt] =
(ulong)map_physmem(sector,
info-》portwidth,
MAP_NOCACHE);
//計(jì)算各個(gè)扇區(qū)的地址, 地址計(jì)算方法為, 扇區(qū)的大小 * size_ratio( 為 size_ratio = info-》portwidth / info-》chipwidth;,比值為1)
//可以看出, 各個(gè)扇區(qū)的地址相隔一個(gè)扇區(qū)的大小
sector += (erase_region_size * size_ratio);
…
sect_cnt++;
}
info-》sector_count = sect_cnt;
//buffer_size 為 1 《《 8 , 256
info-》buffer_size = 1 《《 (8 * info-》portwidth);
…
}
循環(huán)結(jié)束后, sect_cnt 的數(shù)值為 256
現(xiàn)在, 所有扇區(qū)的地址都保存到了init-》start數(shù)組里。 那么現(xiàn)在如果要向flash里燒寫一個(gè)文件, 在知道文件的大小的情況下,就可以計(jì)算出要使用幾個(gè)扇區(qū)。
include/flash.h:
#define CONFIG_SYS_MAX_FLASH_SECT (256)
typedef struct {
ulong size; /* total bank size in bytes */
ushort sector_count; /* number of erase units */
ulong flash_id; /* combined device & manufacturer code */
ulong start[CONFIG_SYS_MAX_FLASH_SECT]; /* virtual sector start address */
uchar protect[CONFIG_SYS_MAX_FLASH_SECT]; /* sector protection status */
#ifdef CONFIG_SYS_FLASH_CFI
uchar portwidth; /* the width of the port */
uchar chipwidth; /* the width of the chip */
ushort buffer_size; /* # of bytes in write buffer */
ulong erase_blk_tout; /* maximum block erase timeout */
ulong write_tout; /* maximum write timeout */
ulong buffer_write_tout; /* maximum buffer write timeout */
ushort vendor; /* the primary vendor id */
ushort cmd_reset; /* vendor specific reset command */
ushort interface; /* used for x8/x16 adjustments */
ushort legacy_unlock; /* support Intel legacy (un)locking */
ushort manufacturer_id; /* manufacturer id */
ushort device_id; /* device id */
ushort device_id2; /* extended device id */
ushort ext_addr; /* extended query table address */
ushort cfi_version; /* cfi version */
ushort cfi_offset; /* offset for cfi query */
ulong addr_unlock1; /* unlock address 1 for AMD flash roms */
ulong addr_unlock2; /* unlock address 2 for AMD flash roms */
const char *name; /* human-readable name */
#endif
} flash_info_t;
uboot 就是按照如上的思路來(lái)實(shí)現(xiàn)uboot的更新, common/cmd_flash.c 有很好的flash使用范例:
int do_upgrade (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
int rcode = 0;
ulong addr, addr_first, addr_last;
const bootloader_header_t *header;
if (argc != 4) {
if (argc == 2 || argc == 3) {
if (strcmp(argv[1], “uboot”) != 0)
return cmd_usage(cmdtp);
//獲取環(huán)境變量loadaddr的數(shù)值, 這是要更新的文件在內(nèi)存里的起始地址
if (getenv(“l(fā)oadaddr”) != NULL)
addr = simple_strtoul(getenv(“l(fā)oadaddr”), NULL, 16);
else
return cmd_usage(cmdtp);
//(0x1fc00000 - CONFIG_SYS_FLASH_SIZE) = 0x1dc00000
//計(jì)算出uboot的起始地址
addr_first = CONFIG_SYS_FLASH_BASE;
if (argc == 3 && strcmp(argv[2], “all”) == 0) {
addr_last = addr_first + CONFIG_BOOT_SIZE - 1;
}else
//CONFIG_ENV_ADDR = 0x1fbe0000
//addr_last = 0x1fbdffff
//計(jì)算出uboot的結(jié)束地址
addr_last = CONFIG_ENV_ADDR - 1;
// 驗(yàn)證下載的uboot 釋放符合bootload 的格式。
header = (void *)addr;
if (validate_header(header)) {
printf(“Image does not have valid header form addr:0x%lx ”, addr);
return 1;
}
。。.
//知道了uboot的起始,結(jié)束地址, 就可以知道uboot在flash 里要使用幾個(gè)扇區(qū)。
/*
一, 先取消要使用的扇區(qū)保護(hù), 參數(shù)0 表示取消保護(hù)
*/
if ((rcode = flash_sect_protect(0, addr_first, addr_last)) != 0)
return rcode;
//擦除要使用到的扇區(qū)
if ((rcode = flash_sect_erase(addr_first, addr_last)) != 0)
return rcode;
//向要使用到的扇區(qū)寫入數(shù)據(jù)
puts (“Copy to Flash.。。 ”);
if ((rcode = flash_write((char *)addr, addr_first, addr_last - addr_first)) != 0) {
flash_perror(rcode);
return 1;
}
puts (“done ”);
return 0;
}
int flash_sect_protect (int p, ulong addr_first, ulong addr_last)
{
flash_info_t *info;
ulong bank;
int s_first[CONFIG_SYS_MAX_FLASH_BANKS], s_last[CONFIG_SYS_MAX_FLASH_BANKS];
int protected, i;
int planned;
int rcode;
/*
通過(guò)flash的起始地址和結(jié)束地址, 計(jì)算出起始扇區(qū)和結(jié)束扇區(qū), 以及要使用到的扇區(qū)個(gè)數(shù), 分別保存到s_first, s_last, planned 中。
*/
rcode = flash_fill_sect_ranges( addr_first, addr_last, s_first, s_last, &planned );
…
static int
flash_fill_sect_ranges (ulong addr_first, ulong addr_last,
int *s_first, int *s_last,
int *s_count )
{
flash_info_t *info;
ulong bank;
int rcode = 0;
*s_count = 0;
//初始化參數(shù)
for (bank=0; bank 《 CONFIG_SYS_MAX_FLASH_BANKS; ++bank) {
s_first[bank] = -1; /* first sector to erase */
s_last [bank] = -1; /* last sector to erase */
}
//只有一次循環(huán)
for (bank=0,info = &flash_info[0];
(bank 《 CONFIG_SYS_MAX_FLASH_BANKS) && (addr_first 《= addr_last);
++bank, ++info) {
ulong b_end;
int sect;
short s_end;
if (info-》flash_id == FLASH_UNKNOWN) {
continue;
}
//start[0]保存的是flash的起始地址 , size是整個(gè)芯片的大小, 那么info-》start[0] + info-》size - 1的 含義就是 整個(gè)芯片的結(jié)束地址
b_end = info-》start[0] + info-》size - 1; /* bank end addr */
//最后一個(gè)扇區(qū)的標(biāo)號(hào)
s_end = info-》sector_count - 1; /* last sector */
//遍歷所有扇區(qū), 即256個(gè)扇區(qū)
for (sect=0; sect 《 info-》sector_count; ++sect) {
ulong end; /* last address in current sect */
//當(dāng)前扇區(qū)的最后地址
end = (sect == s_end) ? b_end : info-》start[sect + 1] - 1;
if (addr_first 》 end)
continue;
//當(dāng)uboot的結(jié)束地址小于當(dāng)前扇區(qū)的地址時(shí), 直接判斷下個(gè)扇區(qū)。 目的是快速找到uboot的結(jié)束地址所在flash的扇區(qū)。
if (addr_last 《 info-》start[sect])
continue;
//當(dāng)文件起始地址等于扇區(qū)起始地址, 將當(dāng)前扇區(qū)地址保存到s_first[0] 中。
if (addr_first == info-》start[sect]) {
s_first[bank] = sect;
}
//當(dāng)文件結(jié)束地址等于當(dāng)前扇區(qū)結(jié)束地址時(shí), 將當(dāng)前扇區(qū)標(biāo)號(hào)保存到s_last[0]中。。 這個(gè)部分uboot的代碼需要優(yōu)化, 正常的邏輯下, 這個(gè)時(shí)候可以直接break了。 無(wú)須再進(jìn)入循環(huán)。 本人已經(jīng)驗(yàn)證通過(guò)
if (addr_last == end) {
s_last[bank] = sect;
}
}
//如果s_first[0]有數(shù)值, 即查找成功的話, 計(jì)算出占有了幾個(gè)扇區(qū)。
if (s_first[bank] 》= 0) {
//如果沒有找到s_last, 有兩種情況, 如果目標(biāo)文件大于flash的大小, 那么設(shè)定s_last 為最后一個(gè)扇區(qū)。 否則是邏輯錯(cuò)誤。
if (s_last[bank] 《 0) {
if (addr_last 》 b_end) {
s_last[bank] = s_end;
} else {
puts (“Error: end address”
“ not on sector boundary ”);
rcode = 1;
break;
}
} //如果得到的結(jié)果是結(jié)束的扇區(qū)標(biāo)號(hào)小于起始扇區(qū)標(biāo)號(hào), 也是邏輯錯(cuò)誤
if (s_last[bank] 《 s_first[bank]) {
puts (“Error: end sector”
“ precedes start sector ”);
rcode = 1;
break;
}
//記錄結(jié)束扇區(qū)的編號(hào)。
sect = s_last[bank];
addr_first = (sect == s_end) ? b_end + 1: info-》start[sect + 1];
//s_last[bank] - s_first[bank] + 1 就是中間的扇區(qū)個(gè)數(shù)
(*s_count) += s_last[bank] - s_first[bank] + 1;
} else if (addr_first 》= info-》start[0] && addr_first 《 b_end) {
puts (“Error: start address not on sector boundary ”);
rcode = 1;
break;
} else if (s_last[bank] 》= 0) {
puts (“Error: cannot span across banks when they are”
“ mapped in reverse order ”);
rcode = 1;
break;
}
}
return rcode;
}
回到:
#ifndef CONFIG_SYS_NO_FLASH
int flash_sect_protect (int p, ulong addr_first, ulong addr_last)
{
flash_info_t *info;
ulong bank;
int s_first[CONFIG_SYS_MAX_FLASH_BANKS], s_last[CONFIG_SYS_MAX_FLASH_BANKS];
int protected, i;
int planned;
int rcode;
rcode = flash_fill_sect_ranges( addr_first, addr_last, s_first, s_last, &planned );
protected = 0;
if (planned && (rcode == 0)) {
for (bank=0,info = &flash_info[0]; bank 《 CONFIG_SYS_MAX_FLASH_BANKS; ++bank, ++info) {
if (info-》flash_id == FLASH_UNKNOWN) {
continue;
}
if (s_first[bank]》=0 && s_first[bank]《=s_last[bank]) {
debug (“%sProtecting sectors %d..%d in bank %ld ”,
p ? “” : “Un-”,
s_first[bank], s_last[bank], bank+1);
protected += s_last[bank] - s_first[bank] + 1;
//為獲取到的扇區(qū)取消保護(hù)
for (i=s_first[bank]; i《=s_last[bank]; ++i) {
#if defined(CONFIG_SYS_FLASH_PROTECTION)
//就是 改變 info-》addr_unlock1 的標(biāo)識(shí)和將info-》protect 的對(duì)應(yīng)成員置0, 否則后面不能 erase 和write
if (flash_real_protect(info, i, p))
rcode = 1;
putc (‘。’);
#else
info-》protect[i] = p;
#endif /* CONFIG_SYS_FLASH_PROTECTION */
}
}
}
#if defined(CONFIG_SYS_FLASH_PROTECTION)
puts (“ done ”);
#endif /* CONFIG_SYS_FLASH_PROTECTION */
printf (“%sProtected %d sectors ”,
p ? “” : “Un-”, protected);
} else if (rcode == 0) {
puts (“Error: start and/or end address”
“ not on sector boundary ”);
rcode = 1;
}
return rcode;
}
#ifndef CONFIG_SYS_NO_FLASH
int flash_sect_erase (ulong addr_first, ulong addr_last)
{
flash_info_t *info;
ulong bank;
int s_first[CONFIG_SYS_MAX_FLASH_BANKS], s_last[CONFIG_SYS_MAX_FLASH_BANKS];
int erased = 0;
int planned;
int rcode = 0;
//跟之前取消保護(hù)一樣, 也需要通過(guò)給定地址計(jì)算出要操作的扇區(qū)。 這個(gè)地方實(shí)在多余, 完全可以使用之前已經(jīng)獲取到的數(shù)據(jù)作為參數(shù)傳下來(lái)。
//總之 flash_sect_erase 和 flash_sect_protect 的重復(fù)度太高
rcode = flash_fill_sect_ranges (addr_first, addr_last,
s_first, s_last, &planned );
if (planned && (rcode == 0)) {
for (bank=0,info = &flash_info[0];
(bank 《 CONFIG_SYS_MAX_FLASH_BANKS) && (rcode == 0);
++bank, ++info) {
if (s_first[bank]》=0) {
erased += s_last[bank] - s_first[bank] + 1;
debug (“Erase Flash from 0x%08lx to 0x%08lx ”
“in Bank # %ld ”,
info-》start[s_first[bank]],
(s_last[bank] == info-》sector_count) ?
info-》start[0] + info-》size - 1:
info-》start[s_last[bank]+1] - 1,
bank+1);
//flash_erase 是drivers/mtd/cfi_flash.c 提供的flash 擦除接口。
rcode = flash_erase (info, s_first[bank], s_last[bank]);
}
}
printf (“Erased %d sectors ”, erased);
} else if (rcode == 0) {
puts (“Error: start and/or end address”
“ not on sector boundary ”);
rcode = 1;
}
return rcode;
}
#endi
int flash_erase (flash_info_t * info, int s_first, int s_last)
{
…
for (sect = s_first; sect 《= s_last; sect++) {
////如果扇區(qū)處于保護(hù)狀態(tài), 將無(wú)法擦除
if (info-》protect[sect] == 0) { /* not protected */
switch (info-》vendor) {
…
break;
case CFI_CMDSET_AMD_STANDARD:
case CFI_CMDSET_AMD_EXTENDED:
flash_write_cmd (info, 0, 0, AMD_CMD_RESET); // (1)
flash_unlock_seq (info, sect); //(2)
flash_write_cmd (info, sect, info-》addr_unlock1,AMD_CMD_ERASE_START); //(3)
flash_unlock_seq (info, sect);//(4)
flash_write_cmd (info, sect, 0,AMD_CMD_ERASE_SECTOR);//(5)
break;
…
}
/*
根據(jù)手冊(cè), 扇區(qū)的擦寫動(dòng)作指令為:
#define AMD_CMD_UNLOCK_START 0xAA
#define AMD_CMD_UNLOCK_ACK 0x55
static void flash_unlock_seq (flash_info_t * info, flash_sect_t sect){
flash_write_cmd (info, sect, info-》addr_unlock1, AMD_CMD_UNLOCK_START);
flash_write_cmd (info, sect, info-》addr_unlock2, AMD_CMD_UNLOCK_ACK);
}
全部擦寫的操作是,
__RESET
1, 向 0xaaa 寫入 aa
2, 向 0x555 寫入 55
3, 向 0xaaa 寫入80
4, 向 0xaaa 寫入aa
5, 向0x555 寫入55
6, 向扇區(qū)地址 寫入30
__RESET 由(1) 完成
1,2 由 (2) 完成
3 由 (3)完成
4,5由(4)完成
6 由 (5)完成
*/
/*
指令的下發(fā)后, 還要使用狀態(tài)查詢函數(shù), 等待指令的完成, 即硬件的執(zhí)行完成。 這個(gè)過(guò)程是最耗時(shí)的。
*/
if (use_flash_status_poll(info)) {
cfiword_t cword = (cfiword_t)0xffffffffffffffffULL;
void *dest;
//獲取扇區(qū)的內(nèi)存地址
dest = flash_map(info, sect, 0);
//傳入的超時(shí)時(shí)間為 info-》erase_blk_tout, 這個(gè)數(shù)值為: (1 《《 qry.block_erase_timeout_typ) * (1 《《 qry.block_erase_timeout_max)
//根據(jù)手冊(cè), 計(jì)算出扇區(qū)最大超時(shí)時(shí)間為: 4096s, 意味著, 如果4096s內(nèi)扇區(qū)還沒有擦寫完成, 那么就超時(shí)退出
st = flash_status_poll(info, &cword, dest, info-》erase_blk_tout, “erase”);
flash_unmap(info, sect, 0, dest);
} else
st = flash_full_status_check(info, sect,
info-》erase_blk_tout,
“erase”);
if (st)
rcode = 1;
else if (flash_verbose)
putc (‘。’);
if (ctrlc()) {
puts(“ Interrupted ”);
return 1;
}
}
}
if (flash_verbose)
puts (“ done ”);
return rcode;
}
static int flash_status_poll(flash_info_t *info, void *src, void *dst,
ulong tout, char *prompt)
{
#ifdef CONFIG_SYS_CFI_FLASH_STATUS_POLL
ulong start;
int ready;
…
start = get_timer(0);
WATCHDOG_RESET();
while (1) {
switch (info-》portwidth) {
case FLASH_CFI_8BIT:
/*根據(jù)flash 的位寬(portwidth), 判斷目的地址的數(shù)值是否等于src地址的數(shù)值。 上面?zhèn)飨聛?lái)src的數(shù)值為全f, dst地址是當(dāng)前扇區(qū)的0地址,
那么flash_erase 的擦寫指令完成的判斷條件是: 當(dāng)前扇區(qū)的0地址的數(shù)值為0xff
如果判斷條件成立后跳出循環(huán), 否則udelay后, 再次進(jìn)入循環(huán) */
ready = flash_read8(dst) == flash_read8(src);
break;
case FLASH_CFI_16BIT:
ready = flash_read16(dst) == flash_read16(src);
break;
case FLASH_CFI_32BIT:
ready = flash_read32(dst) == flash_read32(src);
break;
case FLASH_CFI_64BIT:
ready = flash_read64(dst) == flash_read64(src);
break;
default:
ready = 0;
break;
}
if (ready)
break;
if (get_timer(start) 》 tout) {
printf(“Flash %s timeout at address %lx data %lx ”,
prompt, (ulong)dst, (ulong)flash_read8(dst));
return ERR_TIMOUT;
}
udelay(1); /* also triggers watchdog */
}
#endif /* CONFIG_SYS_CFI_FLASH_STATUS_POLL */
return ERR_OK;
}
回到do_upgrade, 扇區(qū)擦寫完成后, 調(diào)用flash_write 進(jìn)行寫入操作
code = flash_write((char *)addr, addr_first, addr_last - addr_first)) != 0) {
src 是要燒些的文件的起始, addr 是要燒寫到flash的目的地址, cnt 是要燒寫的長(zhǎng)度
int flash_write (char *src, ulong addr, ulong cnt){
int i;
ulong end = addr + cnt - 1;
//在單個(gè)bank的flash里, 只有一個(gè)info, info_first等于info_last
flash_info_t *info_first = addr2info (addr);
flash_info_t *info_last = addr2info (end );
flash_info_t *info;
…
//在單個(gè)bank的flash里, 只有一次循環(huán)
for (info = info_first; info 《= info_last; ++info) {
ulong b_end = info-》start[0] + info-》size; /* bank end addr */
short s_end = info-》sector_count - 1;
for (i=0; i《info-》sector_count; ++i) {
ulong e_addr = (i == s_end) ? b_end : info-》start[i + 1];
//如果要操作的扇區(qū)沒有取消保護(hù), 直接返回
if ((end 》= info-》start[i]) && (addr 《 e_addr) &&
(info-》protect[i] != 0) ) {
return (ERR_PROTECTED);
}
}
}
/* finally write data to flash */
for (info = info_first; info 《= info_last && cnt》0; ++info) {
ulong len;
len = info-》start[0] + info-》size - addr;
if (len 》 cnt)
len = cnt;
//單個(gè)bank的flash調(diào)用 write_buf后返回操作結(jié)果
if ((i = write_buff(info, (uchar *)src, addr, len)) != 0) {
return (i);
}
//多個(gè)bank的情況
cnt -= len;
addr += len;
src += len;
}
return (ERR_OK);
}
//info 為flash的數(shù)據(jù)結(jié)構(gòu), src為源文件的內(nèi)存地址, addr 為目的flash 地址, cnt 為文件要寫的長(zhǎng)度
int write_buff (flash_info_t * info, uchar * src, ulong addr, ulong cnt)
{
ulong wp;
uchar *p;
int aln;
cfiword_t cword;
int i, rc;
#ifdef CONFIG_SYS_FLASH_USE_BUFFER_WRITE
int buffered_size;
#endif
#ifdef CONFIG_FLASH_SHOW_PROGRESS
int digit = CONFIG_FLASH_SHOW_PROGRESS;
int scale = 0;
int dots = 0;
/*
* Suppress if there are fewer than CONFIG_FLASH_SHOW_PROGRESS writes.
*/
if (cnt 》= CONFIG_FLASH_SHOW_PROGRESS) {
scale = (int)((cnt + CONFIG_FLASH_SHOW_PROGRESS - 1) /
CONFIG_FLASH_SHOW_PROGRESS);
}
#endif
//wp的數(shù)值為addr
wp = (addr & ~(info-》portwidth - 1));
…
buffered_size = (info-》portwidth / info-》chipwidth);
buffered_size *= info-》buffer_size;
//buffered_size 為256
while (cnt 》= info-》portwidth) {
//buffer_size 長(zhǎng)度為1的情況,就是按字節(jié)寫的情況
if (info-》buffer_size == 1) {
cword.l = 0;
for (i = 0; i 《 info-》portwidth; i++)
flash_add_byte (info, &cword, *src++);
if ((rc = flash_write_cfiword (info, wp, cword)) != 0)
return rc;
wp += info-》portwidth;
cnt -= info-》portwidth;
continue;
}
//buffer_size 不為1, 按buffer 寫的情況
//如果地址為buffer_size 的整數(shù)倍, 那么i 就等于 buffer_size.256 字節(jié)。
//可以看到, 按緩存寫的話 , 總共會(huì)執(zhí)行 (文件長(zhǎng)度 / 256 + 1 次) 。 如果要寫入的長(zhǎng)度為 0xdffff, 那么要執(zhí)行的次數(shù)為 0xdffff / 256 + 1 = 3584 次。
i = buffered_size - (wp % buffered_size);
if (i 》 cnt)
i = cnt; //如果緩存寫長(zhǎng)度大于剩余的要寫入的文件長(zhǎng)度, 那么長(zhǎng)度截為cnt
if ((rc = flash_write_cfibuffer (info, wp, src, i)) != ERR_OK)
return rc;
i -= i & (info-》portwidth - 1);
wp += i; //要寫入的內(nèi)容的地址移動(dòng) i 長(zhǎng)度
src += i; //要寫入的文件的地址向后移動(dòng) i 長(zhǎng)度
cnt -= i; //文件的剩余長(zhǎng)度減去 i 長(zhǎng)度
FLASH_SHOW_PROGRESS(scale, dots, digit, i);
}
…
if (cnt == 0) {
return (0);
}
/*
* handle unaligned tail bytes
*/
cword.l = 0;
p = (uchar *)wp;
for (i = 0; (i 《 info-》portwidth) && (cnt 》 0); ++i) {
flash_add_byte (info, &cword, *src++);
--cnt;
}
for (; i 《 info-》portwidth; ++i)
flash_add_byte (info, &cword, flash_read8(p + i));
return flash_write_cfiword (info, wp, cword);
}
對(duì)于字節(jié)寫和緩存寫, 分別 有flash_write_cfiword 和flash_write_cfibuffer 實(shí)現(xiàn)
static int flash_write_cfiword (flash_info_t * info, ulong dest,
cfiword_t cword)
{
void *dstaddr = (void *)dest;
int flag;
flash_sect_t sect = 0;
char sect_found = 0;
//根據(jù)端口寬度 , 判斷要操作的地址上的數(shù)值是否為cword的數(shù)值。
//上面?zhèn)鞯腸word 為0 , 那么要判斷要寫的地址的數(shù)值是否為0 , 如果判斷結(jié)果為假,那么退出,返回ERR_NOT_ERASE錯(cuò)誤數(shù)值。提示沒有經(jīng)過(guò)擦寫。
switch (info-》portwidth) {
case FLASH_CFI_8BIT:
flag = ((flash_read8(dstaddr) & cword.c) == cword.c);
break;
case FLASH_CFI_16BIT:
flag = ((flash_read16(dstaddr) & cword.w) == cword.w);
break;
case FLASH_CFI_32BIT:
flag = ((flash_read32(dstaddr) & cword.l) == cword.l);
break;
case FLASH_CFI_64BIT:
flag = ((flash_read64(dstaddr) & cword.ll) == cword.ll);
break;
default:
flag = 0;
break;
}
if (!flag)
return ERR_NOT_ERASED;
//上面看到, flash在執(zhí)行燒些前, 要先取消保護(hù), 再進(jìn)行擦除, 當(dāng)兩者都成功后, 才可以進(jìn)行write
//在執(zhí)行燒些過(guò)程中, 關(guān)閉全部中斷, 所有的中斷新號(hào)會(huì)被忽略
flag = disable_interrupts ();
//根據(jù)不同廠商,執(zhí)行對(duì)應(yīng)的指令。
switch (info-》vendor) {
case CFI_CMDSET_INTEL_PROG_REGIONS:
case CFI_CMDSET_INTEL_EXTENDED:
case CFI_CMDSET_INTEL_STANDARD://intel 的規(guī)范
flash_write_cmd (info, 0, 0, FLASH_CMD_CLEAR_STATUS);
flash_write_cmd (info, 0, 0, FLASH_CMD_WRITE);
break;
case CFI_CMDSET_AMD_EXTENDED:
case CFI_CMDSET_AMD_STANDARD: //AMD 的規(guī)范
//根據(jù)目的地址找到要操作的扇區(qū)
sect = find_sector(info, dest);
//解鎖扇區(qū)
flash_unlock_seq (info, sect);
//輸入write 指令
flash_write_cmd (info, sect, info-》addr_unlock1, AMD_CMD_WRITE);
sect_found = 1;
break;
…
}
//等待指令完成
switch (info-》portwidth) {
case FLASH_CFI_8BIT:
flash_write8(cword.c, dstaddr);
if (info-》vendor != 1) {
while (flash_read8(dstaddr) != cword.c)
;
}
break;
case FLASH_CFI_16BIT:
flash_write16(cword.w, dstaddr);
if (info-》vendor != 1) {
while (flash_read16(dstaddr) != cword.w)
;
}
break;
case FLASH_CFI_32BIT:
flash_write32(cword.l, dstaddr);
case FLASH_CFI_64BIT:
flash_write64(cword.ll, dstaddr);
if (info-》vendor != 1) {
while (flash_read64(dstaddr) != cword.ll)
;
}
break;
}
//恢復(fù)中斷
if (flag)
enable_interrupts ();
if (!sect_found)
sect = find_sector (info, dest);
if (use_flash_status_poll(info))
return flash_status_poll(info, &cword, dstaddr,
info-》write_tout, “write”);
else
return flash_full_status_check(info, sect,
info-》write_tout, “write”);
}
flash_write_cfibuffer 使用了同樣的邏輯 , 不同的指令
結(jié)語(yǔ)
關(guān)于Nor Flash的相關(guān)介紹就到這了,如有不足之處歡迎指正。
相關(guān)閱讀推薦:對(duì)于嵌入式為什么要有uboot的深度解析
-
驅(qū)動(dòng)
+關(guān)注
關(guān)注
12文章
1882瀏覽量
86389 -
NOR flash
+關(guān)注
關(guān)注
2文章
93瀏覽量
23263
發(fā)布評(píng)論請(qǐng)先 登錄
arm9 nor flash 地址
從uboot燒寫完LCD的程序后從nor flash啟動(dòng)沒有引導(dǎo)界面
使用nor flash中UBOOT下載代碼到nand flash中uboot也被清除了?
請(qǐng)問nor flash上的uboot為什么會(huì)被覆蓋?
請(qǐng)問uboot1.1.6 NOR FLASH移植應(yīng)該做哪些修改?
OMAPL138 NOR FLASH啟動(dòng) uboot移植的資料求分享?
i.MXRT上使能NOR Flash的Continuous read模式在軟復(fù)位后無(wú)法正常啟動(dòng)問題的解決
關(guān)于NUC972 SPI NOR flash驅(qū)動(dòng)問題
關(guān)于Nor Flash的各種挑戰(zhàn)
關(guān)于NOR Flash的幾大應(yīng)用領(lǐng)域淺析
NAND Flash和NOR Flash的區(qū)別

評(píng)論