zoukankan      html  css  js  c++  java
  • norflash驱动编写笔记

    【部分转自】http://blog.csdn.net/ziyiyunmen/article/details/9744901

     

    1. 读数据
    md.b 0 
    
    2. 读ID
    NOR手册上:
    往地址555H写AAH
    往地址2AAH写55H
    往地址555H写90H
    读0地址得到厂家ID: C2H
    读1地址得到设备ID: 22DAH或225BH
    退出读ID状态: 给任意地址写F0H
    
    2440的A1接到NOR的A0,所以2440发出(555h<<1), NOR才能收到555h这个地址
    UBOOT怎么操作?
    
    往地址AAAH写AAH                      mw.w aaa aa
    往地址554写55H                       mw.w 554 55
    往地址AAAH写90H                      mw.w aaa 90
    读0地址得到厂家ID: C2H               md.w 0 1
    读2地址得到设备ID: 22DAH或225BH      md.w 2 1
    退出读ID状态:                        mw.w 0 f0
    
    3. NOR有两种规范, jedec, cfi(common flash interface)
       读取CFI信息
    
    NOR手册:   
    进入CFI模式    往55H写入98H
    读数据:        读10H得到0051
                   读11H得到0052
                   读12H得到0059
                   读27H得到容量
    
    2440的A1接到NOR的A0,所以2440发出(555h<<1), NOR才能收到555h这个地址
    UBOOT怎么操作?
    进入CFI模式    往AAH写入98H            mw.w aa 98
    读数据:        读20H得到0051           md.w 20 1
                   读22H得到0052           md.w 22 1
                   读24H得到0059           md.w 24 1
                   读4EH得到容量           md.w 4e 1
                   退出CFI模式             mw.w 0 f0
    
    4. 写数据: 在地址0x100000写入0x1234
    md.w 100000 1     // 得到ffff
    mw.w 100000 1234
    md.w 100000 1     // 还是ffff
    
    NOR手册:
    往地址555H写AAH 
    往地址2AAH写55H 
    往地址555H写A0H 
    往地址PA写PD
    
    2440的A1接到NOR的A0,所以2440发出(555h<<1), NOR才能收到555h这个地址
    UBOOT怎么操作?
    往地址AAAH写AAH               mw.w aaa aa
    往地址554H写55H               mw.w 554 55
    往地址AAAH写A0H               mw.w aaa a0
    往地址0x100000写1234h         mw.w 100000 1234
    
    
    
    
    NOR FLASH驱动程序框架
    
    
    测试1:通过配置内核支持NOR FLASH
    1. make menuconfig
    -> Device Drivers
      -> Memory Technology Device (MTD) support
        -> Mapping drivers for chip access
        <M> CFI Flash device in physical memory map 
        (0x0) Physical start address of flash mapping  // 物理基地址
        (0x1000000) Physical length of flash mapping   // 长度
        (2)   Bank width in octets (NEW)               // 位宽
        
    2. make modules
       cp drivers/mtd/maps/physmap.ko /work/nfs_root/first_fs
    3. 启动开发板
       ls /dev/mtd*
       insmod physmap.ko
       ls /dev/mtd*
       cat /proc/mtd
    
    测试2: 使用自己写的驱动程序:
    
    1. ls /dev/mtd*
    2. insmod s3c_nor.ko
    3. ls /dev/mtd*
    4. 格式化: flash_eraseall -j /dev/mtd1
    5. mount -t jffs2 /dev/mtdblock1 /mnt
       在/mnt目录下操作文件
       
       
    NOR FLASH识别过程:
    do_map_probe("cfi_probe", s3c_nor_map);
        drv = get_mtd_chip_driver(name)
        ret = drv->probe(map);  // cfi_probe.c
                cfi_probe
                    mtd_do_chip_probe(map, &cfi_chip_probe);
                        cfi = genprobe_ident_chips(map, cp);
                                    genprobe_new_chip(map, cp, &cfi)
                                        cp->probe_chip(map, 0, NULL, cfi)
                                                cfi_probe_chip
                                                    // 进入CFI模式
                                                    cfi_send_gen_cmd(0x98, 0x55, base, map, cfi, cfi->device_type, NULL);
                                                    // 看是否能读出"QRY"
                                                    qry_present(map,base,cfi)
                                                    .....
                                                    
    do_map_probe("jedec_probe", s3c_nor_map);
        drv = get_mtd_chip_driver(name)
        ret = drv->probe(map);  // jedec_probe
                jedec_probe
                    mtd_do_chip_probe(map, &jedec_chip_probe);
                        genprobe_ident_chips(map, cp);
                            genprobe_new_chip(map, cp, &cfi)
                                cp->probe_chip(map, 0, NULL, cfi)
                                        jedec_probe_chip
                                            // 解锁
                                            cfi_send_gen_cmd(0xaa, cfi->addr_unlock1, base, map, cfi, cfi->device_type, NULL);
                                            cfi_send_gen_cmd(0x55, cfi->addr_unlock2, base, map, cfi, cfi->device_type, NULL);
                                            
                                            // 读ID命令
                                            cfi_send_gen_cmd(0x90, cfi->addr_unlock1, base, map, cfi, cfi->device_type, NULL);                                      
                                
                                            // 得到厂家ID,设备ID
                                            cfi->mfr = jedec_read_mfr(map, base, cfi);
                                            cfi->id = jedec_read_id(map, base, cfi);
                                            
                                            // 和数组比较
                                            jedec_table     
    
                                                
                        
            
    

      

    一、Linux Flash驱动结构

     1、Linux MTD系统层次

    在Linux系统中,提供了MTD(内存技术设备)系统来建立Flash针对Linux的统一、抽象的接口。

    在引入MTD后,Linux系统中Flash设备驱动及接口可分为4层,从上到下依次是:设备节点、MTD设备层、MTD原始设备层和硬件驱动层。如下所示:

     

     

    1) 设备节点:通过mknod在/dev子目录下建立MTD字符设备节点(主设备号为90)和MTD块设备节点(主设备号为31),用户通过访问此设备节点即可访问MTD字符设备和块设备。

    2) MTD设备层:分为MTD字符设备(mtdchar.c)和MTD块设备(mtdblock.c),建立在MTD原始设备层之上,为应用程序提供访问Flash的接口。

    3) MTD原始设备层:MTD原始设备层由两部分组成,一部分是MTD原始设备的通用代码,另一部分是各个特定的Flash的数据,例如分区。

    4) 硬件驱动层:Flash 硬件驱动层负责Flash硬件设备的读、写、擦除。

     

      2、Linux MTD系统接口

        在引入MTD后,底层Flash驱动直接与MTD原始设备层交互,利用其提供的接口注册设备和分区。

    mtd_info是表示MTD原始设备的结构体,每个分区也被认为是一个mtd_info。例如:如果有两个MTD原始设备,而每个上有3个分区,在系统中就共有6个mtd_info结构体,这些mtd_info的指针被存放在名为mtd_table的数组里。

    struct mtd_info {

    u_char type;         /*内存技术的类型*/

    u_int32_t flags;     /*标志位*/

    u_int32_t size;      /*mtd设备的大小*/

    u_int32_t erasesize; /*主要的擦除块大小*/

    u_int32_t writesize; /*最小的可写单元的字节数*/

     

    u_int32_t oobsize;   /*OOB字节数*/

    u_int32_t oobavail;  /*可用的OOB字节数*/

     

    char *name;          /*分区的名字*/

    int index;           /*分区的索引号*/

     

    struct nand_ecclayout *ecclayout;   /*ECC布局结构体指针*/

     

            //不同的erasesize的区域

    int numeraseregions; /*不同的erasesize的区域的数目*/

    struct mtd_erase_region_info *eraseregions;

     

     

            //擦除函数

    int (*erase) (struct mtd_info *mtd, struct erase_info *instr);

     

            //读写函数

    int (*read) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);

    int (*write) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf);

     

            //oob读写函数

    int (*read_oob) (struct mtd_info *mtd, loff_t from,

     struct mtd_oob_ops *ops);

    int (*write_oob) (struct mtd_info *mtd, loff_t to,

     struct mtd_oob_ops *ops);

     

    //设备锁

    int (*lock) (struct mtd_info *mtd, loff_t ofs, size_t len);

    int (*unlock) (struct mtd_info *mtd, loff_t ofs, size_t len);

     

    //电源管理函数

    int (*suspend) (struct mtd_info *mtd);

    void (*resume) (struct mtd_info *mtd);

     

    //坏块管理函数

    int (*block_isbad) (struct mtd_info *mtd, loff_t ofs);

    int (*block_markbad) (struct mtd_info *mtd, loff_t ofs);

     

    void *priv;  /*私有数据*/

    };

    1) mtd_info的type字段给出底层物理设备的类型,包括MTD_RAM、MTD_ROM、MTD_NORFLASH、MTD_NANDFLASH等。

    2) flags字段标志可以是MTD_WRITEABLE、MTD_BIT_WRITEABLE、MTD_NO_ERASE、MTD_POWERUP_LOCK等的组合。

    3) mtd_info中的的read()、write()、read_oob()、write_oob()、erase()是MTD设备驱动要实现的主要函数。但是在NOR和NAND的驱动代码中几乎看不到mtd_info的成员函数,这是因为Linux在MTD的下层实现了针对NOR Flash和NAND Flash的通用的mtd_info成员函数。

     

    Flash驱动中使用如下的两个函数注册和注销MTD设备:

    int add_mtd_device(struct mtd_info *mtd);

    int del_mtd_device (struct mtd_info *mtd);

     

    mtd_part结构体用于表示分区(某一个分区),其mtd_info结构体成员用于描述该分区,它会被加入到mtd_table中。

    struct mtd_part {

    struct mtd_info mtd;     //分区的信息

    struct mtd_info *master;  //该分区的主分区

    u_int32_t offset;        //该分区的偏移地址

    int index;              //分区号

    struct list_head list;

    int registered;

    };

     

    在MTD原始设备层中维护着一个mtd_part链表mtd_partitions(Flash的整个分区)。

    struct mtd_partition {

    char *name;           //标识字符串

    u_int32_t size;       //分区大小

    u_int32_t offset;     //主MTD空间内的偏移  

    u_int32_t mask_flags; //掩码标志  

    struct nand_ecclayout *ecclayout;  //OOB布局

    struct mtd_info **mtdp;

    };

    Flash驱动中使用如下两个函数注册和注销分区:

    int add_mtd_partitions(struct mtd_info *master,

           const struct mtd_partition *parts,

           int nbparts);

    int del_mtd_partitions(struct mtd_info *master);

    ① add_mtd_partitions()会对每一个新建分区建立一个新的mtd_part结构体,将其加入mtd_partition中,并调用add_mtd_device()将此分区作为MTD设备加入mtd_table。

    ② del_mtd_partitions()的作用是对于mtd_partition上的每一个分区,如果它的主分区是master,则将它从mtd_partition和mtd_table中删除并释放掉,这个函数会调用del_mtd_device()。

     

    二、NOR Flash驱动结构

    在Linux系统中,实现了针对CFI(公共Flash接口)等接口的通用NOR驱动,这一层的驱动直接面向mtd_info的成员函数,这使得NOR的芯片级驱动变得非常的简单,只需要定义具体的内存映射情况结构体map_info并使用指定接口类型调用do_map_probe()。

     

     

    NOR Flash驱动的核心是定义map_info结构体,它指定了NOR Flash的基址、位宽、大小等信息以及Flash的读写函数。

    struct map_info {

    char *name;           /*NOR FLASH的名字*/

    unsigned long size;   /*NOR FLASH的大小*/

    resource_size_t phys; /*NOR FLASH的起始物理地址*/

     

    void __iomem *virt;   /*NOR FLASH的虚拟地址*/

    void *cached;

     

    int bankwidth;        /*NOR FLASH的总线宽度*/

     

            //缓存的虚拟地址

    void (*inval_cache)(struct map_info *, unsigned long, ssize_t);

     

    void (*set_vpp)(struct map_info *, int);

    };

     

    NOR Flash驱动在Linux中实现非常简单,如下图所示:

     

     

     

    ① 定义map_info的实例,初始化其中的成员,根据目标板的情况为name、size、bankwidth和phys赋值。

    ② 如果Flash要分区,则定义mtd_partition数组,将实际电路板中Flash分区信息记录于其中。

    ③ 以map_info和探测的接口类型(如"cfi_probe"等)为参数调用do_map_probe(),探测Flash得到mtd_info。

     

    三、NOR Flash驱动程序

    #include <linux/module.h>
    #include <linux/types.h>
    #include <linux/kernel.h>
    #include <linux/init.h>
    #include <linux/slab.h>
    #include <linux/device.h>
    #include <linux/platform_device.h>
    #include <linux/mtd/mtd.h>
    #include <linux/mtd/map.h>
    #include <linux/mtd/partitions.h>
    #include <asm/io.h>
    
    static struct map_info *s3c_map;
    static struct mtd_info *s3c_mtd;
    static struct mtd_partition s3c_parts[] = {
        [0] = {
            .name   = "bootloader_nor",
            .size   = 0x00040000,
            .offset    = 0,
        },
        [1] = {
            .name   = "root_nor",
            .offset = MTDPART_OFS_APPEND,
            .size   = MTDPART_SIZ_FULL,
        }
    };
    
    
    static int s3c_nor_init(void)
    {
        printk("s3c_nor_init
    ");
        
        /*1. 分配一个map_info结构体*/
        s3c_map = kzalloc(sizeof(struct map_info), GFP_KERNEL);
    
        /*2. 设置: 物理基地址(phys), 大小(size), 位宽(bankwidth), 虚拟基地址(virt) */
        s3c_map->name = "s3c_nor";
        s3c_map->phys = 0;
        s3c_map->size = 0x1000000;
        s3c_map->bankwidth = 2;
        s3c_map->virt = ioremap(s3c_map->phys, s3c_map->phys+s3c_map->size);
    
        /* 3. 使用: 调用NOR FLASH协议层提供的函数来识别 */
        simple_map_init(s3c_map);
        
        printk("use cfi_probe
    ");
        s3c_mtd=do_map_probe("cfi_probe", s3c_map);
        if (!s3c_mtd)
        {
            printk("use jedec_probe
    ");
            s3c_mtd = do_map_probe("jedec_probe", s3c_map);
        }
    
        if(!s3c_mtd)
        {
            
    iounmap(s3c_map->virt);
            kfree(s3c_map);
            return -EIO;
        }
        /* 4. add_mtd_partitions */
        add_mtd_partitions(s3c_mtd, s3c_parts, 2);
        return 0;
    }
    
    
    static void s3c_nor_exit(void)
    {
        printk("s3c_nor_exit
    ");
        iounmap(s3c_map->virt);
        kfree(s3c_map);
    }
    
    
    module_init(s3c_nor_init);
    module_exit(s3c_nor_exit);
    MODULE_LICENSE("GPL");
     
  • 相关阅读:
    Java学习开篇
    《我的姐姐》
    世上本无事,庸人自扰之
    这48小时
    补觉
    淡定
    es java api 设置index mapping 报错 mapping source must be pairs of fieldnames and properties definition.
    java mongodb groupby分组查询
    linux 常用命令
    mongodb too many users are authenticated
  • 原文地址:https://www.cnblogs.com/veryStrong/p/6155754.html
Copyright © 2011-2022 走看看