This article is mainly about the introduction of Nor Flash, and focuses on the preparation of Nor Flash and the similar elaboration of the driver.
Nor Flash driver writing1.
Bottom/Top Boot Sect (bottom/top boot block)
The so-called boot sect refers to the difference between Nor Flash and Nand Flash. Nand Flash is composed of pages of the same size from the beginning to the end.
Nor Flash generally has a boot sect. It seems that due to historical reasons, Nor Flash is often used as a device for storing boot code, that is, booting from Nor Flash. Therefore, this boot sect block is specially designed to use To store the startup code. If you explain in detail, according to the description in the datasheet, it is the first or last one, here is the bottom sect, so it is the last 64KB block, which is divided into 4 independent blocks. The first 16KB is used for a small amount of system initialization code. The two 8KB blocks are used to store parameters, and the remaining 32KB block is called Main Block, which is used to store application code.
2.
Sector
The sector here is also the smallest erase unit in the flash: block.
So the sector count is how many blocks there are.
3.
Sector Count and Sector List
The Nor Flash here, M29W320DB, has a total of 63 normal blocks of 64KB, plus the 4 small blocks that were originally normal 1 64KB as mentioned above, so it is 63+4=67.
And the so-called sector list in the driver, that is, the block list, the code comments are also very clear:
ulong
start [CFG_MAX_FLASH_SECT];
/* physical sector start addresses */
Used to store the starting address of each block. It also requires you to drive initialization. For the M29W320DB here, it is also very simple. From the beginning, add the block size of 64K to the last 3, calculate the corresponding address, and fill it in.
4.
Protect (write protection)
Nor Flash provides corresponding protection mechanisms from the register configuration of the software and the hardware. The purpose is to prevent accidentally or unintentionally from destroying the data that you don't want to be changed/deleted. For example, some mechanisms store some system startup parameters of Flash in Nor Flash, or for some other reason, only allow you to use part of the Nor Flash space. Therefore, put such parts that need to be protected in the flash_info_t structure of uboot. Set the corresponding bit to 1:
uchar
protectï¼»CFG_MAX_FLASH_SECTï¼½; /* sector protection status
*/
In this way, the program can avoid erasing useful data intentionally or unintentionally.
[Some notes when writing Nor Flash driver]
1.
Bit width (bitwidth, X8/X16/X32)
In the Nor Flash controller, here I use ARM's PromeCell PL172, MPMC (MultiPort Memory Controller), which can be connected to a variety of different storage devices, such as SRAM (Static Memory), Nor Flash, and they can be separate Set whether to support Page Mode, Extended wait and write protection (enable write protection, you can see that it is ROM), etc.
After connecting MPMC and Nor Flash on the hardware. Before using Nor Flash, MPMC must be initialized.
What I said here is to pay attention to the bit width because I didn’t notice it at the beginning. Therefore, when initializing MPMC, when setting the MPMCStaticConfig register, it was set to X16 (16 bits), but later I went to uboot to find someone else The Nor Flash driver (refer to \board\oxc\flash.c), the commands issued to read the ID are also X8 (8 bits):
addr [0x0AAA] = 0xAA;
addr [0x0555] = 0x55;
addr [0x0AAA] = 0x90;
Therefore, the Manufacture ID and Device ID cannot be read correctly, and the reads are all 0xFF. Later, I reconfirmed that when configuring MPMCStaticConfig, it was configured in X16 mode, and then the command was sent, which also corresponds to sending the command in X16 mode. You can refer to the code in \board\mvs1\flash.c to read the ID. When is:
addr[0x0555] = 0x00AA;
addrï¼»0x02AAï¼½= 0x0055;
addr[0x0555] = 0x0090;
In order to correctly read the expected ID:
value = addrï¼»0ï¼½;
/* manufacturer ID
*/
What is read out is 0x20h.
value = addrï¼»1ï¼½;
/* device ID
*/
What is read out is 0x22CB
And the match in the datasheet:
– Manufacturer Code: 0020h
– Bottom Device Code M29W320DB: 22CBh
2.
Different bit width corresponds to different timing
The timing mentioned here, as mentioned above, the addresses sent by X8 and X16 are different, and the order is also different.
And one more thing to note is that remember to convert the address to the corresponding type:
X8 is vu_char*
X16 is vu_short *
In this way, write the corresponding address and value again, and it will be fine.
3.
reset command
Looking at the code in uboot, it seems to be other devices. Most reset commands are 0xFF.
And what is used here is STM (STMicroelectronics, later seems to be changed to Intel and ST joint venture Numonyx (Numonyx)...) Nor Flash, M29W320DB (32 M, bottom boot sect)
, More special, is 0xF0.
4.
The location of the boot sector
I just looked at the datasheet. I was sweaty. I thought the bottom sect was the bottom sect, the one with the largest address. As a result, the datasheet in the datasheet was the beginning of the flashback calculation, that is, the block with the largest address, which was the first block. , So, the boot sector here is block 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
About the Nor Flash driver problem under UbootThe characteristics of the use of nor flash are: The read operation can be read by address, it must be erased before writing, and the entire sector must be erased once erased.
The new flash uses a voltage of 3V to erase and write the entire sector
The use of any chip is inseparable from the support of the driver. The drive logic of nor flash under uboot is very simple. Moreover, for flash chips that comply with the CFI (Common Flash Interface) specification, the driver has great versatility.
uboot provides good flash driver logic and flash usage examples. These basic usage methods are also the same logic in linux, except that a layer of partition information needs to be added under linux. Combined with the flash chip manual, you can have a clearer understanding of the logic of using nor flash.
The driver initialization part of nor flash:
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;
…
//Because the flash used is a new CFI standard flash, the CONFIG_FLASH_CFI_LEGACY macro is not used, so flash_detect_legacy directly returns 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);
//If it is a CFI interface, then there is a unified query specification, save the queried information in 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);
//Check address 0x20, CFI stipulates that the first three characters should be 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')) {
//If it is confirmed as the CFI specification, then query according to the struct cfi_qry data structure
flash_read_cfi(info, qry, FLASH_OFFSET_CFI_RESP,
sizeof(struct cfi_qry));
…
//After the CFI specification query, addr_unlock1 and addr_unlock2 are also assigned. These two addresses represent an 8-bit wide address and a 16-bit wide address, respectively, which can implement byte and word operations.
//Generally, we only use addr_unlock1
//In some codes, these two values ​​are realized by macro definitions
info-》addr_unlock1 = 0xaaa;
info-》addr_unlock2 = 0x555;
…
}
The following is the information of the CFI specification query in the flash chip manual:
cfi_qry definition:
struct cfi_qry {
u8 qryï¼»3ï¼½; //Save 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; // minimum Vcc
u8 vcc_max; //Max Vcc
u8 vpp_min; //Minimum Vpp
u8 vpp_max; //Max Vpp
u8 word_write_timeout_typ; //Byte write typical timeout
u8 buf_write_timeout_typ; //Cache write typical timeout
u8 block_erase_timeout_typ; //Block erase typical timeout
u8 chip_erase_timeout_typ; //Typical timeout for whole chip erasure
u8 word_write_timeout_max; //Byte write maximum timeout
u8 buf_write_timeout_max; //Cache write maximum timeout
u8 block_erase_timeout_max; //Maximum timeout for block write
u8 chip_erase_timeout_max; //Maximum timeout of whole chip erase
u8 dev_size; //chip size
u16 interface_desc; //interface description
u16 max_buf_write_size; //Maximum buffer write length
u8 num_erase_regions; //Number of erase block sectors
u32 erase_region_infoï¼»NUM_ERASE_REGIONSï¼½; //Information of 4 blocks
} __attribute__((packed));
As you can see from the figure above, you have obtained the CFI query identification string, System interface information, and Device geometry definition information. By comparing the manual, you can know the value of the member.
Among them, the most important is erase_region_info, which corresponds to the following information in the manual:
The manual gives the sector information, the first part explains the number of sectors (block): 0xff + 1 = 256, the second part explains the size of a sector (block): 0x200 * 256 = 131072, which is 128K byte
Our flash is 00ff and 0200. Then the value of tmp of uint32_t should be: 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
The size of the erased sector erase_region_size = (tmp & 0xffff) * 256 = 0x20000, that is, the size of a sector is 0x2000 bytes.
The number of erased sectors erase_region_count is 0x201, that is, 256 sectors
Then, we can know that the total capacity of the entire nor flash is: 0x2000 * 256 = 33554432 bytes,
Verify it: 33554432/1024/1024 = 32 M
sect_cnt = 0;
sector = base;//The base address is 0x1dc00000
…
Then it will loop 256 times.
for (j = 0; j "erase_region_count; j++) {
. .
//In 256 cycles, 256 start members save the address of each sector
info-》start[sect_cnt] =
(Ulong) map_physmem (sector,
info-》portwidth,
MAP_NOCACHE);
//Calculate the address of each sector, the address calculation method is, the size of the sector * size_ratio (size_ratio = info-"portwidth / info-"chipwidth;, the ratio is 1)
//It can be seen that the addresses of each sector are separated by the size of a sector
sector += (erase_region_size * size_ratio);
…
sect_cnt++;
}
info-》sector_count = sect_cnt;
//buffer_size is 1 "" 8, 256
info-"buffer_size = 1 "" (8 * info-"portwidth);
…
}
After the loop, the value of sect_cnt is 256
Now, the addresses of all sectors are saved in the init-》start array. So now if you want to write a file to the flash, you can calculate how many sectors to use when you know the size of the file.
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 implements the update of uboot according to the above ideas, common/cmd_flash.c has a good example of flash usage:
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);
//Get the value of the environment variable loadaddr, which is the starting address in the memory of the file to be updated
if (getenv("loadaddr")!= NULL)
addr = simple_strtoul(getenv("loadaddr"), NULL, 16);
else
return cmd_usage(cmdtp);
//(0x1fc00000-CONFIG_SYS_FLASH_SIZE) = 0x1dc00000
//Calculate the starting address of 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
//Calculate the end address of uboot
addr_last = CONFIG_ENV_ADDR-1;
// Verify that the downloaded uboot release complies with the bootload format.
header = (void *)addr;
if (validate_header(header)) {
printf("Image does not have valid header form addr:0x%lx", addr);
return 1;
}
. . .
//If you know the start and end addresses of uboot, you can know how many sectors uboot will use in flash.
/*
1. First cancel the sector protection to be used, parameter 0 means cancel the protection
*/
if ((rcode = flash_sect_protect(0, addr_first, addr_last)) != 0)
return rcode;
//Erase the sector to be used
if ((rcode = flash_sect_erase(addr_first, addr_last)) != 0)
return rcode;
//Write data to the sector to be used
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;
/*
According to the start address and end address of the flash, calculate the start sector and end sector, as well as the number of sectors to be used, and save them in s_first, s_last, and planned respectively.
*/
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;
//Initialization parameters
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 */
}
//Only one loop
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] saves the start address of the flash, and size is the size of the entire chip, then info-"start[0] + info-"size-1 means the end address of the entire chip
b_end = info-》start[0] + info-》size-1; /* bank end addr */
//The label of the last sector
s_end = info-》sector_count-1; /* last sector */
//Traverse all sectors, that is, 256 sectors
for (sect=0; sect "info-" sector_count; ++sect) {
ulong end; /* last address in current sect */
//The last address of the current sector
end = (sect == s_end)? b_end: info-"start [sect + 1]-1;
if (addr_first》 end)
continue;
//When the end address of uboot is less than the address of the current sector, directly judge the next sector. The purpose is to quickly find the flash sector where the end address of uboot is located.
if (addr_last "info-" start [sect])
continue;
//When the file start address is equal to the sector start address, save the current sector address in s_first[0].
if (addr_first == info-"start [sect]) {
s_first [bank] = sect;
}
//When the file end address is equal to the current sector end address, save the current sector label to s_last[0]. . This part of the uboot code needs to be optimized. Under normal logic, you can break directly at this time. No need to enter the loop again. I have been verified
if (addr_last == end) {
s_last [bank] = sect;
}
}
//If s_first [0] has a value, that is, if the search is successful, calculate how many sectors are occupied.
if (s_first[bank] 》= 0) {
//If s_last is not found, there are two cases. If the target file is larger than the size of the flash, then set s_last as the last sector. Otherwise it is a logical error.
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;
}
} //If the result is that the ending sector label is less than the starting sector label, it is also a logical error
if (s_last[bank] "s_first[bank]) {
puts ("Error: end sector"
"Precedes start sector");
rcode = 1;
break;
}
//Record the number of the ending sector.
sect = s_lastï¼»bankï¼½;
addr_first = (sect == s_end)? b_end + 1: info-"start [sect + 1];
//s_last[bank]-s_first[bank] + 1 is the number of sectors in the middle
(*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;
}
Back to:
#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;
//Unprotect the acquired sectors
for (i=s_first[bank]; i《=s_last[bank]; ++i) {
#if defined(CONFIG_SYS_FLASH_PROTECTION)
//It is to change the logo of info-"addr_unlock1 and set the corresponding member of info-"protect to 0, otherwise you cannot erase and write later
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;
//Same as before canceling the protection, it is also necessary to calculate the sector to be operated by the given address. This place is really superfluous, you can use the data that has been obtained before as a parameter to pass down.
//In short, the repetition of flash_sect_erase and flash_sect_protect is too high
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 is the flash erase interface provided by drivers/mtd/cfi_flash.c.
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++) {
////If the sector is protected, it cannot be erased
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;
…
}
/*
According to the manual, the instructions for erasing and writing sectors are:
#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);
}
The operation of all erase and write is,
__RESET
1. Write aa to 0xaaa
2. Write 55 to 0x555
3. Write 80 to 0xaaa
4. Write aa to 0xaaa
5. Write 55 to 0x555
6. Write 30 to the sector address
__RESET is completed by (1)
1, 2 is completed by (2)
3 is completed by (3)
4, 5 is completed by (4)
6 is completed by (5)
*/
/*
After the instruction is issued, the status query function is also used to wait for the completion of the instruction, that is, the completion of the hardware execution. This process is the most time-consuming.
*/
if (use_flash_status_poll(info)) {
cfiword_t cword = (cfiword_t) 0xffffffffffffffffULL;
void *dest;
//Get the memory address of the sector
dest = flash_map(info, sect, 0);
//The incoming timeout time is info-"erase_blk_tout, this value is: (1《》 qry.block_erase_timeout_typ) * (1《》 qry.block_erase_timeout_max)
//According to the manual, the maximum timeout time of the sector is calculated as: 4096s, which means that if the sector has not been erased and written within 4096s, then it will time out and exit
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:
/* Determine whether the value of the destination address is equal to the value of the src address according to the bit width of the flash (portwidth). The value of src passed down above is all f, and the dst address is the 0 address of the current sector.
Then the judgment condition for the completion of the flash_erase erase command is: The value of the 0 address of the current sector is 0xff
If the judgment condition is satisfied, jump out of the loop, otherwise after udelay, enter the loop again*/
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;
}
Back to do_upgrade, after the sector erase and write is completed, call flash_write to perform the write operation
code = flash_write((char *)addr, addr_first, addr_last-addr_first))! = 0) {
src is the start of the file to be burned, addr is the destination address to be burned to the flash, and cnt is the length to be burned
int flash_write (char *src, ulong addr, ulong cnt) {
int i;
ulong end = addr + cnt-1;
//In the flash of a single bank, there is only one info, info_first equals info_last
flash_info_t *info_first = addr2info (addr);
flash_info_t *info_last = addr2info (end);
flash_info_t *info;
…
//In the flash of a single bank, there is only one cycle
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];
//If the sector to be operated is not unprotected, return directly
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;
//The flash of a single bank calls write_buf and returns the operation result
if ((i = write_buff(info, (uchar *)src, addr, len)) != 0) {
return (i);
}
//The case of multiple banks
cnt -= len;
addr += len;
src += len;
}
return (ERR_OK);
}
//info is the data structure of the flash, src is the memory address of the source file, addr is the destination flash address, and cnt is the length of the file to be written
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 value is addr
wp = (addr & ~(info-》portwidth-1));
…
buffered_size = (info-"portwidth / info-"chipwidth);
buffered_size *= info-》buffer_size;
//buffered_size is 256
while (cnt "= info-"portwidth) {
//When the buffer_size length is 1, it is the case of writing by byte
if (info-"buffer_size == 1) {
cword.l = 0;
for (i = 0; i "info-" portwidth; i++)
flash_add_byte (info, &c++word, *src++);
if ((rc = flash_write_cfiword (info, wp, cword)) != 0)
return rc;
wp += info-》portwidth;
cnt -= info-》portwidth;
continue;
}
//buffer_size is not 1, according to the situation of buffer writing
//If the address is an integer multiple of buffer_size, then i is equal to buffer_size.256 bytes.
//You can see that if you write in the cache, it will be executed in total (file length / 256 + 1 times). If the length to be written is 0xdffff, then the number of executions is 0xdffff / 256 + 1 = 3584 times.
i = buffered_size-(wp% buffered_size);
if (i》 cnt)
i = cnt; //If the cache write length is greater than the remaining file length to be written, then the length is truncated to cnt
if ((rc = flash_write_cfibuffer (info, wp, src, i)) != ERR_OK)
return rc;
i -= i & (info-》portwidth-1);
wp += i; //The address of the content to be written moves the length of i
src += i; //The address of the file to be written moves backward by i length
cnt -= i; //The remaining length of the file minus the length of 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);
}
For byte write and cache write, there are flash_write_cfiword and flash_write_cfibuffer implementations respectively
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;
//According to the port width, judge whether the value on the address to be operated is the value of cword.
//The word passed above is 0, then it is necessary to judge whether the value of the address to be written is 0, if the judgment result is false, then exit and return the ERR_NOT_ERASE error value. The prompt has not been erased.
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;
//As seen above, before the flash is burned, the protection must be cancelled and then erased. When both are successful, the write can be performed
//In the process of burning some, close all interrupts, all new interrupt numbers will be ignored
flag = disable_interrupts ();
//According to different manufacturers, execute the corresponding instructions.
switch (info-》vendor) {
case CFI_CMDSET_INTEL_PROG_REGIONS:
case CFI_CMDSET_INTEL_EXTENDED:
case CFI_CMDSET_INTEL_STANDARD://intel specification
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's specification
//Find the sector to be operated according to the destination address
sect = find_sector(info, dest);
//Unlock sector
flash_unlock_seq (info, sect);
//Enter the write command
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;
}
//æ¢å¤ä¸æ–
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 使用了åŒæ ·çš„逻辑, ä¸åŒçš„指令
Conclusion关于Nor Flash的相关介ç»å°±åˆ°è¿™äº†ï¼Œå¦‚有ä¸è¶³ä¹‹å¤„欢迎指æ£ã€‚
相关阅读推è:对于嵌入å¼ä¸ºä»€ä¹ˆè¦æœ‰uboot的深度解æž
相关阅读推è:nandå’Œnor区别
51V Battery Pack,Portable Battery Box,Portable Battery Bank,Ac Battery Pack
Zhejiang Casnovo Materials Co., Ltd. , https://www.casnovonewenergy.com