RM新时代网站-首页

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

Uboot下關(guān)于Nor Flash的驅(qū)動問題

ss ? 作者:工程師譚軍 ? 2018-09-19 10:23 ? 次閱讀

本文主要是關(guān)于Nor Flash的相關(guān)介紹,并著重對Nor Flash的編寫及驅(qū)動程序進(jìn)行了相近的闡述。

Nor Flash驅(qū)動編寫

1.

Bottom/Top Boot Sect(底部/頂部 啟動塊)

所謂的boot sect,是指的是Nor Flash和Nand Flash不太一樣。Nand Flash從開始到最后,都是由同樣大小的page所組成的。

而Nor Flash,一般都是有個boot sect,好像是由于歷史原因,常將Nor Flash用于作為存儲啟動代碼的設(shè)備,也就是從Nor Flash啟動,所以,這個boot sect塊,專門設(shè)計出來,用于存放啟動代碼。如果詳細(xì)解釋,按照datasheet中的描述就是,第一個或最后一個,此處是bottom sect,所以是最后一個64KB大小的塊,被分為4個獨立的塊。第一個16KB,用于少量的系統(tǒng)初始化代碼。而2個8KB的塊用于存儲參數(shù),余下的32KB的塊叫做Main Block,用于存儲應(yīng)用程序的代碼。

2.

Sector(扇區(qū))

此處的sector(扇區(qū))也就是flash里面的最小的擦除單位:塊(block)。

所以sector count,也就是有多少個block。

3.

Sector Count (扇區(qū)數(shù))和Sector List

此處的Nor Flash,M29W320DB,一共有正常的63個64KB的block,加上上面提到的原先是正常的1個64KB分成的4個小塊,所以是63+4=67個。

而所謂的驅(qū)動中的sector list,也就是block list,代碼注釋寫的也很清楚了:

ulong

start[CFG_MAX_FLASH_SECT];

/* physical sector start addresses */

用于存儲每一個塊的起始地址的。也是需要你驅(qū)動初始化的。對于這里的M29W320DB,也很簡單了,從開始順序加上塊大小64K,直到最后3個,計算一下對應(yīng)地址,填進(jìn)去就可以了。

4.

Protect(寫保護(hù))

Nor Flash從軟件的寄存器配置和硬件上,都提供了對應(yīng)的保護(hù)機(jī)制,目的是,防止有意或無意間,把那些不希望被改動/刪除的數(shù)據(jù)破壞了。比如有些機(jī)制把Flash的一些系統(tǒng)啟動參數(shù)存儲Nor Flash,或者是其他某種原因,只允許你使用部分Nor Flash的空間,所以,把這類需要保護(hù)的部分,在uboot的flash_info_t的結(jié)構(gòu)體中,把對應(yīng)的位設(shè)置成1:

uchar

protect[CFG_MAX_FLASH_SECT]; /* sector protection status

*/

這樣,之后程序就可以避免有意無意的擦除有用的數(shù)據(jù)了。

【寫Nor Flash驅(qū)動時的一些注意事項】

1.

位寬(bitwidth,X8/X16/X32)

在Nor Flash控制器,此處我這里用的是,ARM的PromeCell PL172,MPMC(MultiPort Memory Controller),可以接多種不同的存儲設(shè)備,比如SRAM(Static Memory),Nor Flash,而其中又可以分別設(shè)置是否支持Page Mode,Extended wait和寫保護(hù)(啟用寫保護(hù),就可以看出是ROM了)等。

在硬件上MPMC和Nor Flash連接好了之后。在使用Nor Flash之前,要初始化MPMC。

這里說的,要注意位寬,是因為我開始就沒注意到,所以,在開始初始化MPMC的時候,設(shè)置MPMCStaticConfig寄存器的時候,設(shè)置成了X16(16位),但是,后來去uboot中找到別人的Nor Flash的驅(qū)動(參考的是oardoxcflash.c),發(fā)出的命令去讀ID,也都是X8(8位)的:

addr[0x0AAA] = 0xAA;

addr[0x0555] = 0x55;

addr[0x0AAA] = 0x90;

所以,導(dǎo)致讀取Manufacture ID 和Device ID,都無法讀正確,讀的都是0xFF。后來重新去確認(rèn),在配置MPMCStaticConfig的時候,是配置的X16模式,然后再發(fā)命令,也對應(yīng)的是按照X16模式發(fā)命令,可以參考:oardmvs1flash.c中的代碼,讀取ID時是:

addr[0x0555] = 0x00AA;

addr[0x02AA]= 0x0055;

addr[0x0555] = 0x0090;

才能正確讀取到期望出的ID:

value = addr[0];

/* manufacturer ID

*/

讀出來的是0x20h。

value = addr[1];

/* device ID

*/

讀出來的是0x22CB

和datasheet中的匹配:

– Manufacturer Code: 0020h

– Bottom Device Code M29W320DB: 22CBh

2.

不同位寬對應(yīng)不同的時序

此處說的時序,也就是上面提到的,X8和X16發(fā)的地址,是不一樣的,而且順序也不同。

而且還有一小點要注意的是,記得轉(zhuǎn)換地址成對應(yīng)的類型:

X8是vu_char *

X16是 vu_short *

這樣再寫入對應(yīng)的地址和數(shù)值,就可以了。

3.

reset命令

看了uboot中的代碼,好像是其他設(shè)備,多數(shù)的reset命令,都是0xFF。

而這里用到的是STM(STMicroelectronics,后來好像改成Intel和ST合資的恒憶(Numonyx)了。。。)的 Nor Flash,M29W320DB (32 M, bottom boot sect)

,比較特殊些,是0xF0。

4.

boot sector的位置

剛剛看了下datasheet,很汗的是,本以為bottom sect是底部的sect,是地址最大的那個,結(jié)果datasheet中的是倒敘計算開始處的,也就是地址最大的那個塊,是第一個塊,所以,此處的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ū)動問題

nor flash 的使用特點是 : 讀操作可以按地址讀, 寫之前必須進(jìn)行擦除, 一旦擦除必須擦除整個扇區(qū)。

新型的flash 使用3V 的電壓便可以進(jìn)行整個扇區(qū)的擦除和寫入操作

任何芯片的使用, 都離不開驅(qū)動的支持。 uboot下的nor flash的驅(qū)動邏輯非常簡單。 而且, 對于符合 CFI ( Common Flash Interface )規(guī)范的flash芯片,驅(qū)動有很大的通用性。

uboot 提供了很好的 flash 驅(qū)動邏輯 和 flash的使用范例, 這些基本的使用方法在linux里也是同樣的邏輯,只不過linux下需要加上一層分區(qū)信息。 結(jié)合flash 芯片手冊, 可以對nor flash的使用邏輯有較為清晰的理解。

nor flash的驅(qū)動初始化部分:

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 這個宏, 所以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ī)定 , 前三個字符應(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)行賦值, 這兩個地址分別表示8位寬的地址和16位寬的地址, 可以實現(xiàn)byte和word的操作。

//一般地, 我們只使用addr_unlock1

//在一些代碼里, 這兩個數(shù)值就通過宏定義來實現(xiàn)的

info-》addr_unlock1 = 0xaaa;

info-》addr_unlock2 = 0x555;

}

下面是flash 芯片手冊里CFI 規(guī)范查詢的信息:

Uboot下關(guān)于Nor Flash的驅(qū)動問題

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é)寫典型超時

u8 buf_write_timeout_typ; //緩存寫典型超時

u8 block_erase_timeout_typ; //塊擦除典型超時

u8 chip_erase_timeout_typ; //整片擦除典型超時

u8 word_write_timeout_max; //字節(jié)寫最大超時

u8 buf_write_timeout_max; //緩存寫最大超時

u8 block_erase_timeout_max; //塊寫最大超時

u8 chip_erase_timeout_max; //整片擦除最大超時

u8 dev_size; //芯片大小

u16 interface_desc; //接口描述

u16 max_buf_write_size; //最大緩存寫長度

u8 num_erase_regions; //擦除塊扇區(qū)數(shù)量

u32 erase_region_info[NUM_ERASE_REGIONS]; //4個塊區(qū)的信息

} __attribute__((packed));

從上圖可以看到, 是獲取了CFI query identification string , System interface information , Device geometry definition 信息,對照手冊, 就可以知道成員的數(shù)值

其中, 最為重要的是擦寫扇區(qū)信息 erase_region_info, 對應(yīng)手冊的如下信息:

手冊給出了扇區(qū)的信息, 第一部分說明了扇區(qū)(block)的個數(shù) : 0xff + 1 = 256 個, 第二部分說明了一個扇區(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 , 即一個扇區(qū)的大小為0x2000字節(jié)。

擦寫扇區(qū)的個數(shù) erase_region_count為0x201, 即256個扇區(qū)

那么, 可以知道, 整個nor flash 總的容量為: 0x2000 * 256 = 33554432 字節(jié),

驗證一下: 33554432 / 1024 / 1024 = 32 M

sect_cnt = 0;

sector = base;//基地址為 0x1dc00000

那么會循環(huán)256次。

for (j = 0; j 《 erase_region_count; j++) {

。。

//在256次循環(huán)中, 256個start成員保存各個扇區(qū)的地址

info-》start[sect_cnt] =

(ulong)map_physmem(sector,

info-》portwidth,

MAP_NOCACHE);

//計算各個扇區(qū)的地址, 地址計算方法為, 扇區(qū)的大小 * size_ratio( 為 size_ratio = info-》portwidth / info-》chipwidth;,比值為1)

//可以看出, 各個扇區(qū)的地址相隔一個扇區(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里燒寫一個文件, 在知道文件的大小的情況下,就可以計算出要使用幾個扇區(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 就是按照如上的思路來實現(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

//計算出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

//計算出uboot的結(jié)束地址

addr_last = CONFIG_ENV_ADDR - 1;

// 驗證下載的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 里要使用幾個扇區(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;

/*

通過flash的起始地址和結(jié)束地址, 計算出起始扇區(qū)和結(jié)束扇區(qū), 以及要使用到的扇區(qū)個數(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是整個芯片的大小, 那么info-》start[0] + info-》size - 1的 含義就是 整個芯片的結(jié)束地址

b_end = info-》start[0] + info-》size - 1; /* bank end addr */

//最后一個扇區(qū)的標(biāo)號

s_end = info-》sector_count - 1; /* last sector */

//遍歷所有扇區(qū), 即256個扇區(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ū)的地址時, 直接判斷下個扇區(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é)束地址時, 將當(dāng)前扇區(qū)標(biāo)號保存到s_last[0]中。。 這個部分uboot的代碼需要優(yōu)化, 正常的邏輯下, 這個時候可以直接break了。 無須再進(jìn)入循環(huán)。 本人已經(jīng)驗證通過

if (addr_last == end) {

s_last[bank] = sect;

}

}

//如果s_first[0]有數(shù)值, 即查找成功的話, 計算出占有了幾個扇區(qū)。

if (s_first[bank] 》= 0) {

//如果沒有找到s_last, 有兩種情況, 如果目標(biāo)文件大于flash的大小, 那么設(shè)定s_last 為最后一個扇區(qū)。 否則是邏輯錯誤。

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)號小于起始扇區(qū)標(biāo)號, 也是邏輯錯誤

if (s_last[bank] 《 s_first[bank]) {

puts (“Error: end sector”

“ precedes start sector ”);

rcode = 1;

break;

}

//記錄結(jié)束扇區(qū)的編號。

sect = s_last[bank];

addr_first = (sect == s_end) ? b_end + 1: info-》start[sect + 1];

//s_last[bank] - s_first[bank] + 1 就是中間的扇區(qū)個數(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)識和將info-》protect 的對應(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ù)一樣, 也需要通過給定地址計算出要操作的扇區(qū)。 這個地方實在多余, 完全可以使用之前已經(jīng)獲取到的數(shù)據(jù)作為參數(shù)傳下來。

//總之 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), 將無法擦除

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ù)手冊, 扇區(qū)的擦寫動作指令為:

#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í)行完成。 這個過程是最耗時的。

*/

if (use_flash_status_poll(info)) {

cfiword_t cword = (cfiword_t)0xffffffffffffffffULL;

void *dest;

//獲取扇區(qū)的內(nèi)存地址

dest = flash_map(info, sect, 0);

//傳入的超時時間為 info-》erase_blk_tout, 這個數(shù)值為: (1 《《 qry.block_erase_timeout_typ) * (1 《《 qry.block_erase_timeout_max)

//根據(jù)手冊, 計算出扇區(qū)最大超時時間為: 4096s, 意味著, 如果4096s內(nèi)扇區(qū)還沒有擦寫完成, 那么就超時退出

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)飨聛韘rc的數(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 是要燒寫的長度

int flash_write (char *src, ulong addr, ulong cnt){

int i;

ulong end = addr + cnt - 1;

//在單個bank的flash里, 只有一個info, info_first等于info_last

flash_info_t *info_first = addr2info (addr);

flash_info_t *info_last = addr2info (end );

flash_info_t *info;

//在單個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;

//單個bank的flash調(diào)用 write_buf后返回操作結(jié)果

if ((i = write_buff(info, (uchar *)src, addr, len)) != 0) {

return (i);

}

//多個bank的情況

cnt -= len;

addr += len;

src += len;

}

return (ERR_OK);

}

//info 為flash的數(shù)據(jù)結(jié)構(gòu), src為源文件的內(nèi)存地址, addr 為目的flash 地址, cnt 為文件要寫的長度

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 長度為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é)。

//可以看到, 按緩存寫的話 , 總共會執(zhí)行 (文件長度 / 256 + 1 次) 。 如果要寫入的長度為 0xdffff, 那么要執(zhí)行的次數(shù)為 0xdffff / 256 + 1 = 3584 次。

i = buffered_size - (wp % buffered_size);

if (i 》 cnt)

i = cnt; //如果緩存寫長度大于剩余的要寫入的文件長度, 那么長度截為cnt

if ((rc = flash_write_cfibuffer (info, wp, src, i)) != ERR_OK)

return rc;

i -= i & (info-》portwidth - 1);

wp += i; //要寫入的內(nèi)容的地址移動 i 長度

src += i; //要寫入的文件的地址向后移動 i 長度

cnt -= i; //文件的剩余長度減去 i 長度

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);

}

對于字節(jié)寫和緩存寫, 分別 有flash_write_cfiword 和flash_write_cfibuffer 實現(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錯誤數(shù)值。提示沒有經(jīng)過擦寫。

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ān)閉全部中斷, 所有的中斷新號會被忽略

flag = disable_interrupts ();

//根據(jù)不同廠商,執(zhí)行對應(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é)語

關(guān)于Nor Flash的相關(guān)介紹就到這了,如有不足之處歡迎指正。

相關(guān)閱讀推薦:對于嵌入式為什么要有uboot的深度解析

相關(guān)閱讀推薦:nand和nor區(qū)別

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • 驅(qū)動
    +關(guān)注

    關(guān)注

    12

    文章

    1838

    瀏覽量

    85262
  • NOR flash
    +關(guān)注

    關(guān)注

    2

    文章

    90

    瀏覽量

    23003
收藏 人收藏

    評論

    相關(guān)推薦

    arm9 nor flash 地址

    的地址重映射變成多少,網(wǎng)上有人說改成0x4000000 0x8000000 這兩個我都試了,改了nor text base宏都不對 ,開發(fā)板本生的uboot也繞過這個問題,它從nand啟動的uboot沒有修改擦除
    發(fā)表于 12-08 19:18

    uboot燒寫完LCD的程序后從nor flash啟動沒有引導(dǎo)界面

    uboot燒完了LCD的程序,撥碼開關(guān)撥回到nand flash,串口正常打印lcd例程的提示信息,我又把撥碼撥回到了nor,重啟開發(fā)板,此時串口不打印任何信息,重啟多次開發(fā)板,重新用jlink燒寫了
    發(fā)表于 03-21 07:45

    使用nor flashUBOOT下載代碼到nand flashuboot也被清除了?

    為啥使用nor flashUBOOT下載代碼到nand flash中,nor flash中的
    發(fā)表于 03-21 07:45

    請問nor flash上的uboot為什么會被覆蓋?

    uboot的bin文件燒到nor flash了,nor flash上的uboot被覆蓋了,沒有E
    發(fā)表于 04-04 05:02

    請問uboot1.1.6 NOR FLASH移植應(yīng)該做哪些修改?

    我正在移植NOR FLASH 28F128芯片,UBOOT1.1.6做哪些修改?
    發(fā)表于 07-04 05:45

    OMAPL138 NOR FLASH啟動 uboot移植的資料求分享?

    TI專家,大家好:手頭有塊板子,OMAPL138外接的是NOR FLASH,我想自己移植個uboot從NORFLASH啟動,我google了一下,發(fā)現(xiàn)OMAPL138 關(guān)于
    發(fā)表于 08-13 07:51

    i.MXRT上使能NOR Flash的Continuous read模式在軟復(fù)位后無法正常啟動問題的解決

      大家好,我是痞子衡,是正經(jīng)搞技術(shù)的痞子。今天痞子衡給大家介紹的是i.MXRT上使能NOR Flash的Continuous read模式在軟復(fù)位后無法正常啟動問題的解決經(jīng)驗?! ∠然仡櫳掀?/div>
    發(fā)表于 01-26 06:52

    關(guān)于NUC972 SPI NOR flash驅(qū)動問

    最近使用NUC972做了一塊板子,學(xué)習(xí)一下。在使用BSP驅(qū)動SPI NOR flash的時候遇到一個問題,請教大家能否指點一下。 我的板子改了SPI 為GD25Q256D,為32Mbytes在
    發(fā)表于 09-05 06:28

    旺宏串行NOR Flash簡介

    串行NOR Flash介紹,串行NOR Flash分類、串行NOR Flash選型以及串行
    發(fā)表于 03-10 14:52 ?30次下載

    關(guān)于Flash的認(rèn)識報告

    關(guān)于NOR NAND FLASH的簡單歸納整理
    發(fā)表于 11-13 15:43 ?2次下載

    關(guān)于Nor Flash的各種挑戰(zhàn)

    根據(jù)Nor Flash的原理,它是使用CHE來編碼,然后使用FN Tunning 來擦除,而電荷注入機(jī)制對這兩個因素的印象很大。
    發(fā)表于 10-06 09:37 ?5570次閱讀

    關(guān)于NOR Flash的幾大應(yīng)用領(lǐng)域淺析

    NOR Flash和NAND FLASH是目前兩種主要的非易失閃存技術(shù)。NAND FLASH具有“容量大、單位容量成本低”等特點是高數(shù)據(jù)存儲密度的理想解決方案。而
    的頭像 發(fā)表于 03-23 14:54 ?1.5w次閱讀

    NAND FlashNOR Flash的區(qū)別

    1.1接口差別NOR Flash帶有SRAM接口,有足夠的地址引腳來尋址,可以直接和CPU相連,CPU可以直接通過地址總線對NOR Flash進(jìn)行訪問,可以很容...
    發(fā)表于 01-26 17:12 ?16次下載
    NAND <b class='flag-5'>Flash</b>和<b class='flag-5'>NOR</b> <b class='flag-5'>Flash</b>的區(qū)別

    使用MM32F3270 FSMC驅(qū)動外部NOR Flash

    使用MM32F3270 FSMC驅(qū)動外部NOR Flash
    的頭像 發(fā)表于 09-21 17:37 ?935次閱讀
    使用MM32F3270 FSMC<b class='flag-5'>驅(qū)動</b>外部<b class='flag-5'>NOR</b> <b class='flag-5'>Flash</b>

    NAND FlashNOR Flash的區(qū)別

    NAND FlashNOR Flash是兩種常見的閃存類型。
    的頭像 發(fā)表于 11-30 13:53 ?2407次閱讀
    NAND <b class='flag-5'>Flash</b>和<b class='flag-5'>NOR</b> <b class='flag-5'>Flash</b>的區(qū)別
    RM新时代网站-首页