Uboot下的Nor Flash的驱动以及使用

本文详细介绍了在Uboot环境下,如何使用和驱动遵循CFI(Common Flash Interface)规范的Nor Flash。主要内容包括初始化、驱动逻辑、擦除与写入操作,以及如何处理CFI查询信息来确定闪存的参数。通过对闪存芯片手册的分析,展示了如何计算扇区大小和数量,以及如何执行擦除和写入操作。
摘要由CSDN通过智能技术生成

Uboot 下 CFI Nor Flash 的使用

韩大卫@吉林师范大学

2015.1.23

Flash : Micron Technology. 32MB.

Uboot: 2_3_0

CPU平台: Cavium Inc

交叉编译器: mips64-octeon-linux-gnu-gcc (Cavium Inc. Version: 2_3_0 build 128) 4.3.3


nor flash 的使用特点是 :  读操作可以按地址读, 写之前必须进行擦除, 一旦擦除必须擦除整个扇区. 

新型的flash 使用3V 的电压便可以进行整个扇区的擦除和写入操作


任何芯片的使用, 都离不开驱动的支持. uboot下的nor flash的驱动逻辑非常简单. 而且, 对于符合 CFI ( Common Flash Interface )规范的flash芯片,驱动有很大的通用性. 

uboot 提供了很好的 flash 驱动逻辑 和 flash的使用范例, 这些基本的使用方法在linux里也是同样的逻辑,只不过linux下需要加上一层分区信息. 结合flash 芯片手册, 可以对nor flash的使用逻辑有较为清晰的理解. 


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;

                  

      //由于使用的flash 是新型的CFI 规范的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 接口, 那么有统一的查询规范, 将查询到的信息保存到 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\n", 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 地址进行查询,  CFI 规定 , 前三个字符应该是 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')) {


           //如果确认为CFI 规范, 那么就按照 struct cfi_qry数据结构进行查询 

            flash_read_cfi(info, qry, FLASH_OFFSET_CFI_RESP,                                                                                            

                    sizeof(struct cfi_qry));   

    //在进行CFI 规范查询之后, 还要将addr_unlock1 , addr_unlock2 进行赋值,  这两个地址分别表示8位宽的地址和16位宽的地址, 可以实现byte和word的操作. 

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

                               //在一些代码里, 这两个数值就通过宏定义来实现的

            info->addr_unlock1 = 0xaaa;

            info->addr_unlock2 = 0x555;         

}

下面是flash 芯片手册里CFI 规范查询的信息:









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;   //字节写典型超时

    u8  buf_write_timeout_typ;    //缓存写典型超时

    u8  block_erase_timeout_typ;  //块擦除典型超时

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

    u8  word_write_timeout_max;    //字节写最大超时

    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; //擦除块扇区数量

    u32 erase_region_info[NUM_ERASE_REGIONS];        //4个块区的信息

} __attribute__((packed));

从上图可以看到,  是获取了CFI query identification string  , System interface information , Device geometry definition  信息,对照手册,  就可以知道成员的数值

其中, 最为重要的是擦写扇区信息 erase_region_info, 对应手册的如下信息:



手册给出了扇区的信息, 第一部分说明了扇区(block)的个数 : 0xff + 1 = 256 个, 第二部分说明了一个扇区(block)大小: 0x200 * 256 =131072, 即128K字节   


我们的flash, 为00ff, 和0200 .那么uint32_t的tmp 的数值应该为:  0x020000ff


           tmp = le32_to_cpu(qry.erase_region_info[i]);  

            debug("erase region %u: 0x%08lx\n", 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

擦写扇区的大小 erase_region_size =  (tmp & 0xffff) * 256 = 0x20000 , 即一个扇区的大小为0x2000字节.

擦写扇区的个数 erase_region_count为0x201, 即256个扇区

那么, 可以知道, 整个nor flash 总的容量为: 0x2000 * 256 = 33554432  字节,  


验证一下:   33554432 / 1024 / 1024 = 32 M


        sect_cnt = 0;

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


那么会循环256次.


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

..

              //在256次循环中, 256个start成员保存各个扇区的地址

                info->start[sect_cnt] =

                    (ulong)map_physmem(sector,                                                                                                          

                               info->portwidth,

                               MAP_NOCACHE);

                //计算各个扇区的地址, 地址计算方法为, 扇区的大小 * size_ratio(  为 size_ratio = in

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值