基于MTD的NANDFLASH设备驱动底层实现原理分析(五)

转载地址:http://blog.csdn.net/eilianlau/article/details/6963225

Linux内核在MTD的下层实现了通用的NAND驱动(/driver/mtd/nand/nand_base.c)因此芯片级的驱动实现不再需要我们关心mtd中的那些成员函数了主题转移到nand_chip数据结构中

先了解了解nand_chip结构体

struct nand_chip {
    void  __iomem    *IO_ADDR_R;    //读8位I/O线的地址
    void  __iomem    *IO_ADDR_W;   //写8位I/O线的地址
    uint8_t        (*read_byte)(struct mtd_info *mtd);//从芯片读一个字节
    u16        (*read_word)(struct mtd_info *mtd);//从芯片读一个字
    void        (*write_buf)(struct mtd_info *mtd, const uint8_t *buf, int len);//将缓冲区的数据写入芯片
    void        (*read_buf)(struct mtd_info *mtd, uint8_t *buf, int len);//将芯片中的数据独到缓冲区中

    int        (*verify_buf)(struct mtd_info *mtd, const uint8_t *buf, int len); //验证芯片和写入缓冲区中的数据

   int         (*block_bad)(struct mtd_info *mtd, loff_t ofs, int getchip);//检查是否坏块
    int        (*block_markbad)(struct mtd_info *mtd, loff_t ofs);//标记坏块

    void        (*select_chip)(struct mtd_info *mtd, int chip);          //实现选中芯片
    void        (*cmd_ctrl)(struct mtd_info *mtd, int dat,              
                    unsigned int ctrl);//控制ALE/CLE/nCE,也用于写命令和地址
    int        (*dev_ready)(struct mtd_info *mtd);//设备就绪
    void        (*cmdfunc)(struct mtd_info *mtd, unsigned command, int column, int page_addr); //实现命令发送

    int        (*waitfunc)(struct mtd_info *mtd, struct nand_chip *this);
    void        (*erase_cmd)(struct mtd_info *mtd, int page);//擦除命令的处理
    int        (*scan_bbt)(struct mtd_info *mtd);//扫描坏块
    int        (*errstat)(struct mtd_info *mtd, struct nand_chip *this, int state, int status, int page);

    int        (*write_page)(struct mtd_info *mtd, struct nand_chip *chip,
                      const uint8_t *buf, int page, int cached, int raw);//写一页

    int        chip_delay;//有板决定的延迟时间
   unsigned int    options;//与具体的NAND芯片相关的一些选项,如NAND_NO_AUTOINCR,NAND_BUSWIDTH_16等,至于这些选项具体表示什么含义,可以参考<linux/mtd/nand.h>,那里有较为详细的说明;

    int        page_shift;//用位表示的NAND芯片的page大小,如某片NAND芯片的一个page有512个字节,那么page_shift就是9;
    int        phys_erase_shift;//用位表示的NAND芯片的每次可擦除的大小,如某片NAND芯片每次可擦除16K字节(通常就是一个block的大小),那么phys_erase_shift就是14;
    int        bbt_erase_shift;//用位表示的bad block table的大小,通常一个bbt占用一个block,所以bbt_erase_shift通常与phys_erase_shift相等;
    int        chip_shift;用位表示的NAND芯片的容量;
    int        numchips;表示系统中有多少片NAND芯片;
    uint64_t    chipsize;//NAND芯片的大小;
    int        pagemask;//计算page number时的掩码,总是等于chipsize/page大小- 1;
    int        pagebuf;用来保存当前读取的NAND芯片的page number,这样一来,下次读取的数据若还是属于同一个page,就不必再从NAND芯片读取了,而是从data_buf中直接得到;
    int        subpagesize;
    uint8_t        cellinfo;
    int        badblockpos;
    int        badblockbits;

    flstate_t    state;

    uint8_t        *oob_poi;
    struct nand_hw_control  *controller;
    struct nand_ecclayout    *ecclayout;

    struct nand_ecc_ctrl ecc;
    struct nand_buffers *buffers;
    struct nand_hw_control hwcontrol;

    struct mtd_oob_ops ops;

    uint8_t        *bbt;
    struct nand_bbt_descr    *bbt_td;
    struct nand_bbt_descr    *bbt_md;

    struct nand_bbt_descr    *badblock_pattern;

    void        *priv;

};


这上面有一个与ECC相关的结构体 struct nand_ecc_ctrl

struct nand_ecc_ctrl {
    nand_ecc_modes_t    mode;
    int            steps;
    int            size;
    int            bytes;
    int            total;
    int            prepad;
    int            postpad;
    struct nand_ecclayout    *layout;
    void            (*hwctl)(struct mtd_info *mtd, int mode);/**控制硬件ECC*/
    int            (*calculate)(struct mtd_info *mtd,
                         const uint8_t *dat,
                         uint8_t *ecc_code);/**根据data计算ecc值**/
    int            (*correct)(struct mtd_info *mtd, uint8_t *dat,
                       uint8_t *read_ecc,
                       uint8_t *calc_ecc);
    int            (*read_page_raw)(struct mtd_info *mtd,
                         struct nand_chip *chip,
                         uint8_t *buf, int page);/**向NANDFLASH芯片读一个页的原始数据*/
    void            (*write_page_raw)(struct mtd_info *mtd,
                          struct nand_chip *chip,
                          const uint8_t *buf);
    int            (*read_page)(struct mtd_info *mtd,
                         struct nand_chip *chip,
                         uint8_t *buf, int page);/**读一个页但包含ecc校验*/
    int            (*read_subpage)(struct mtd_info *mtd,
                         struct nand_chip *chip,
                         uint32_t offs, uint32_t len,
                         uint8_t *buf);
    void            (*write_page)(struct mtd_info *mtd,
                          struct nand_chip *chip,
                          const uint8_t *buf);
    int            (*read_oob)(struct mtd_info *mtd,
                        struct nand_chip *chip,
                        int page,
                        int sndcmd);/**读OOB但不包含MAIN部分**/
    int            (*write_oob)(struct mtd_info *mtd,
                         struct nand_chip *chip,
                         int page);
};


hwctl:这个函数用来控制硬件产生ecc,其实它主要的工作就是控制NAND controller向NAND芯片发出NAND_ECC_READ、NAND_ECC_WRITE和NAND_ECC_READSYN等命令,与struct nand_chip结构体中的cmdfunc类似,只不过发起的命令是ECC相关的罢了;
 
calculate:根据data计算ecc值;
 
correct:根据ecc值,判断读写数据时是否有错误发生,若有错,则立即试着纠正,纠正失败则返回错误;
 
read_page_raw和write_page_raw:从NAND芯片中读取一个page的原始数据和向NAND芯片写入一个page的原始数据,所谓的原始数据,即不对读写的数据做ecc处理,该读写什么值就读写什么值。另外,这两个函数会读写整个page中的所有内容,即不但会读写一个page中MAIN部分,还会读写OOB部分。
 
read_page和write_page:与read_page_raw和write_page_raw类似,但不同的是,read_page和write_page在读写过程中会加入ecc的计算,校验,和纠正等处理。
read_oob和write_oob:读写oob中的内容,不包括MAIN部分。
 
其实,以上提到的这几个read_xxx和write_xxx函数,最终都会调用struct nand_chip中的read_buf和write_buf这两个函数,所以如果没有特殊需求的话,我认为不必自己实现,使用MTD提供的default的函数即可。


下面这个结构体用来ECC在oob中布局的一个结构体。

struct nand_ecclayout {
    __u32 eccbytes;//ecc字节数,对于512字节/page的NANDflash,eccbytes=3,如果需要额外用到oob中的数据,那么也可以大于3.
    __u32 eccpos[64];ECC数据在oob中的位置,这里之所以是个64字节的数组,是因为对于2K/页NAND来说,它的oob64个字节。而对于512字节/pageNAND来说,可以而且只可以定义它的前16个字节。
    __u32 oobavail;OOB中可用的字节数,不需要对该成员赋值,MTD会根据其他三个成员计算出来
    struct nand_oobfree oobfree[MTD_MAX_OOBFREE_ENTRIES];//显示定义空闲的OOB字节
};

大家都知道OOB但是OOB里面究竟存了些什么,OOB主要用来存储两种信息:坏块信息和ECC数据,对于小页的NANDFLASH一般坏块占据一个字节(并且是在第6个字节),ECC占3个字节,上面这个结构体就是起到了这个作用告诉那些与操作ECC无关的函数,在OOB区域里那部分是来存储ECC的(不可他用),那些字节是空闲的

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Go语言(也称为Golang)是由Google开发的一种静态强类型、编译型的编程语言。它旨在成为一门简单、高效、安全和并发的编程语言,特别适用于构建高性能的服务器和分布式系统。以下是Go语言的一些主要特点和优势: 简洁性:Go语言的语法简单直观,易于学习和使用。它避免了复杂的语法特性,如继承、重载等,转而采用组合和接口来实现代码的复用和扩展。 高性能:Go语言具有出色的性能,可以媲美C和C++。它使用静态类型系统和编译型语言的优势,能够生成高效的机器码。 并发性:Go语言内置了对并发的支持,通过轻量级的goroutine和channel机制,可以轻松实现并发编程。这使得Go语言在构建高性能的服务器和分布式系统时具有天然的优势。 安全性:Go语言具有强大的类型系统和内存管理机制,能够减少运行时错误和内存泄漏等问题。它还支持编译时检查,可以在编译阶段就发现潜在的问题。 标准库:Go语言的标准库非常丰富,包含了大量的实用功能和工具,如网络编程、文件操作、加密解密等。这使得开发者可以更加专注于业务逻辑的实现,而无需花费太多时间在底层功能的实现上。 跨平台:Go语言支持多种操作系统和平台,包括Windows、Linux、macOS等。它使用统一的构建系统(如Go Modules),可以轻松地跨平台编译和运行代码。 开源和社区支持:Go语言是开源的,具有庞大的社区支持和丰富的资源。开发者可以通过社区获取帮助、分享经验和学习资料。 总之,Go语言是一种简单、高效、安全、并发的编程语言,特别适用于构建高性能的服务器和分布式系统。如果你正在寻找一种易于学习和使用的编程语言,并且需要处理大量的并发请求和数据,那么Go语言可能是一个不错的选择。
Go语言(也称为Golang)是由Google开发的一种静态强类型、编译型的编程语言。它旨在成为一门简单、高效、安全和并发的编程语言,特别适用于构建高性能的服务器和分布式系统。以下是Go语言的一些主要特点和优势: 简洁性:Go语言的语法简单直观,易于学习和使用。它避免了复杂的语法特性,如继承、重载等,转而采用组合和接口来实现代码的复用和扩展。 高性能:Go语言具有出色的性能,可以媲美C和C++。它使用静态类型系统和编译型语言的优势,能够生成高效的机器码。 并发性:Go语言内置了对并发的支持,通过轻量级的goroutine和channel机制,可以轻松实现并发编程。这使得Go语言在构建高性能的服务器和分布式系统时具有天然的优势。 安全性:Go语言具有强大的类型系统和内存管理机制,能够减少运行时错误和内存泄漏等问题。它还支持编译时检查,可以在编译阶段就发现潜在的问题。 标准库:Go语言的标准库非常丰富,包含了大量的实用功能和工具,如网络编程、文件操作、加密解密等。这使得开发者可以更加专注于业务逻辑的实现,而无需花费太多时间在底层功能的实现上。 跨平台:Go语言支持多种操作系统和平台,包括Windows、Linux、macOS等。它使用统一的构建系统(如Go Modules),可以轻松地跨平台编译和运行代码。 开源和社区支持:Go语言是开源的,具有庞大的社区支持和丰富的资源。开发者可以通过社区获取帮助、分享经验和学习资料。 总之,Go语言是一种简单、高效、安全、并发的编程语言,特别适用于构建高性能的服务器和分布式系统。如果你正在寻找一种易于学习和使用的编程语言,并且需要处理大量的并发请求和数据,那么Go语言可能是一个不错的选择。
Go语言(也称为Golang)是由Google开发的一种静态强类型、编译型的编程语言。它旨在成为一门简单、高效、安全和并发的编程语言,特别适用于构建高性能的服务器和分布式系统。以下是Go语言的一些主要特点和优势: 简洁性:Go语言的语法简单直观,易于学习和使用。它避免了复杂的语法特性,如继承、重载等,转而采用组合和接口来实现代码的复用和扩展。 高性能:Go语言具有出色的性能,可以媲美C和C++。它使用静态类型系统和编译型语言的优势,能够生成高效的机器码。 并发性:Go语言内置了对并发的支持,通过轻量级的goroutine和channel机制,可以轻松实现并发编程。这使得Go语言在构建高性能的服务器和分布式系统时具有天然的优势。 安全性:Go语言具有强大的类型系统和内存管理机制,能够减少运行时错误和内存泄漏等问题。它还支持编译时检查,可以在编译阶段就发现潜在的问题。 标准库:Go语言的标准库非常丰富,包含了大量的实用功能和工具,如网络编程、文件操作、加密解密等。这使得开发者可以更加专注于业务逻辑的实现,而无需花费太多时间在底层功能的实现上。 跨平台:Go语言支持多种操作系统和平台,包括Windows、Linux、macOS等。它使用统一的构建系统(如Go Modules),可以轻松地跨平台编译和运行代码。 开源和社区支持:Go语言是开源的,具有庞大的社区支持和丰富的资源。开发者可以通过社区获取帮助、分享经验和学习资料。 总之,Go语言是一种简单、高效、安全、并发的编程语言,特别适用于构建高性能的服务器和分布式系统。如果你正在寻找一种易于学习和使用的编程语言,并且需要处理大量的并发请求和数据,那么Go语言可能是一个不错的选择。
以下是一个简单的SPI NAND Flash驱动代码的示例,基于Linux内核的MTD框架: ``` #include <linux/mtd/mtd.h> #include <linux/mtd/nand.h> #include <linux/spi/spi.h> #define SPI_NAND_CMD_READ 0x03 #define SPI_NAND_CMD_READID 0x9F #define SPI_NAND_CMD_RESET 0xFF #define SPI_NAND_PAGE_SIZE 2048 #define SPI_NAND_BLOCK_SIZE (64 * 1024) #define SPI_NAND_CHIP_SIZE (1024 * 1024 * 8) struct spi_nand_chip { struct mtd_info mtd; struct spi_device *spi; u8 *buf; }; static int spi_nand_read_buf(struct spi_nand_chip *chip, u32 addr, u8 *buf, u32 len) { u8 cmd[4]; int ret; cmd[0] = SPI_NAND_CMD_READ; cmd[1] = addr >> 16; cmd[2] = addr >> 8; cmd[3] = addr; ret = spi_write_then_read(chip->spi, cmd, sizeof(cmd), buf, len); if (ret < 0) { dev_err(&chip->spi->dev, "SPI NAND read error: %d\n", ret); return ret; } return 0; } static int spi_nand_read_id(struct spi_nand_chip *chip) { u8 cmd = SPI_NAND_CMD_READID; u8 id[5]; int ret; ret = spi_write_then_read(chip->spi, &cmd, sizeof(cmd), id, sizeof(id)); if (ret < 0) { dev_err(&chip->spi->dev, "SPI NAND read ID error: %d\n", ret); return ret; } dev_info(&chip->spi->dev, "SPI NAND ID: %02x %02x %02x %02x %02x\n", id[0], id[1], id[2], id[3], id[4]); return 0; } static int spi_nand_probe(struct spi_device *spi) { struct spi_nand_chip *chip; struct mtd_info *mtd; int ret; chip = devm_kzalloc(&spi->dev, sizeof(*chip), GFP_KERNEL); if (!chip) return -ENOMEM; chip->buf = devm_kmalloc(&spi->dev, SPI_NAND_PAGE_SIZE, GFP_KERNEL); if (!chip->buf) return -ENOMEM; mtd = &chip->mtd; mtd->name = "spi-nand"; mtd->type = MTD_NANDFLASH; mtd->flags = MTD_CAP_NANDFLASH; mtd->writesize = SPI_NAND_PAGE_SIZE; mtd->erasesize = SPI_NAND_BLOCK_SIZE; mtd->size = SPI_NAND_CHIP_SIZE; mtd->_erase = nand_erase; mtd->_read = nand_read; ret = spi_setup(spi); if (ret) return ret; chip->spi = spi; ret = spi_nand_read_id(chip); if (ret) return ret; return mtd_device_register(mtd, NULL, 0); } static int spi_nand_remove(struct spi_device *spi) { struct mtd_info *mtd = spi_get_drvdata(spi); mtd_device_unregister(m

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值