之前提到nand驱动的初始化分析,有一个结构体 struct mtd_info始终贯穿这些代码

再来分析一下这个结构体的基本功能,如何初始化,如何使用

一、分析过程

看看结构体的出现和使用方式

第一次出现在文件\u-boot-sunxi-sunxi\drivers\mtd\nand\nand.c内:

#ifndef CONFIG_SYS_NAND_SELF_INIT
static void nand_init_chip(int i)
{
struct mtd_info *mtd= &nand_info[i];
struct nand_chip *nand = &nand_chip[i];
ulong base_addr = base_address[i];
int maxchips = CONFIG_SYS_NAND_MAX_CHIPS;

if (maxchips < 1)
maxchips = 1;

mtd->priv = nand;
nand->IO_ADDR_R = nand->IO_ADDR_W = (void  __iomem *)base_addr;

if (board_nand_init(nand))
return;

if (nand_scan(mtd, maxchips))
return;

nand_register(i);
}
#endif

看这句代码:

struct mtd_info *mtd = &nand_info[i];

结构体指向全局变量nand_info,这个变量就是nand设备的信息

再看初始化:

mtd->priv = nand;

mtd的私有数据就是一个struct nand_chip类型的结构体

从编程的角度来说,一个硬件驱动应该有两个面,一个面向上层,提供接口;一个面向底层,提供硬件操作

广义上来看:

struct mtd_info就是面向上层,提供数据接口

struct nand_chip面向nand设备,提供硬件接口

假如:mtd->priv = nand; 初始化为另外一种设备的结构体,例如nor flash,那么mtd就是一种nor  flash的驱动,

用户实现nor flash相关的操作即可。

struct   mtd_info结构体来自linux内核的MTD子系统,u-boot使用时进行了一些简化使用,毕竟不是操作系统,很多问题可以不用考虑

MTD的全称是memory  technology  device,主要针对是用于访问memory设备(ROM、flash),其目的就是简化驱动的更新,

例如cubieboard接了一个nand flash,型号是K9GBG08U0A,如果没有这个驱动如何简单添加呢?

从人的正常思考角度,加了一个nand,无外乎读,写,刷新几种主要操作,而MTD就提供了这几种操作,

用户在使用时需要实现这个接口就可以了,至于数据的格式,支持什么文件格式yaffs,ext3,ext4用户无须关心

MTD上层已经实现了,用户提供读写,刷新等等基本操作就可以了。大大简化了一个驱动的开发工作量。

基于这种思路,来看看struct   mtd_info结构体

struct mtd_info {
u_char type;     // 设备类型,指示这是一种什么设备,MTD支持多种设备

u_int32_t flags;
uint64_t size;/* Total size of the MTD */   // 总容量,假设板上有5颗nand flash,容量之和就是这个数

/* "Major" erase size for the device. Na茂ve users may take this
* to be the only erase size available, or may use the more detailed
* information below if they desire
*/
u_int32_t erasesize;  // 刷新大小,对于nand,一块为单位刷新,对于cubieboard上的K9GBG08U0A,一个块就是1M大小

                            //对于nand来说,这个大小就是块大小了

/* Minimal writable flash unit size. In case of NOR flash it is 1 (even


* though individual bits can be cleared), in case of NAND flash it is


* one NAND page (or half, or one-fourths of it), in case of ECC-ed NOR


* it is of ECC block size, etc. It is illegal to have writesize = 0.


* Any driver registering a struct mtd_info must ensure a writesize of


* 1 or larger.


*/


u_int32_t writesize;  // 一次写的最小字节数,对于nand而言,一次写一页或半页,等等

u_int32_t oobsize;   /* Amount of OOB data per block (e.g. 16) */


u_int32_t oobavail;  /* Available OOB bytes per block */

/* Kernel-only stuff starts here. */


const char *name;


int index;

/* ecc layout structure pointer - read only ! */


struct nand_ecclayout *ecclayout;

/* Data for variable erase regions. If numeraseregions is zero,


* it means that the whole device has erasesize as given above.


*/


int numeraseregions;


struct mtd_erase_region_info *eraseregions;

/*


* Erase is an asynchronous operation.  Device drivers are supposed


* to call instr->callback() whenever the operation completes, even


* if it completes with a failure.


* Callers are supposed to pass a callback function and wait for it


* to be called before writing to the block.


*/


int (*erase) (struct mtd_info *mtd, struct erase_info *instr);  // 刷新函数指针,用户实现自己的函数之后,初始化到这个指针

/* This stuff for eXecute-In-Place */


/* phys is optional and may be set to NULL */


int (*point) (struct mtd_info *mtd, loff_t from, size_t len,


size_t *retlen, void **virt, phys_addr_t *phys);

/* We probably shouldn't allow XIP if the unpoint isn't a NULL */


void (*unpoint) (struct mtd_info *mtd, loff_t from, size_t len);

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); // 写函数指针,用户实现自己的函数之后,初始化到这个指针

/* In blackbox flight recorder like scenarios we want to make successful


  writes in interrupt context. panic_write() is only intended to be


  called when its known the kernel is about to panic and we need the


  write to succeed. Since the kernel is not going to be running for much


  longer, this function can break locks and delay to ensure the write


  succeeds (but not sleep). */

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

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

/*


* Methods to access the protection register area, present in some


* flash devices. The user data is one time programmable but the


* factory data is read only.


*/


int (*get_fact_prot_info) (struct mtd_info *mtd, struct otp_info *buf, size_t len);


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


int (*get_user_prot_info) (struct mtd_info *mtd, struct otp_info *buf, size_t len);


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


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


int (*lock_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len);

/* XXX U-BOOT XXX */

#if 0


/* kvec-based read/write methods.


  NB: The 'count' parameter is the number of _vectors_, each of


  which contains an (ofs, len) tuple.


*/


int (*writev) (struct mtd_info *mtd, const struct kvec *vecs, unsigned long count, loff_t to, size_t *retlen);

#endif

/* Sync */


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

/* Chip-supported device locking */


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


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

/* Bad block management functions */


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


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

/* XXX U-BOOT XXX */

#if 0


struct notifier_block reboot_notifier;  /* default mode before reboot */

#endif

/* ECC status information */


struct mtd_ecc_stats ecc_stats;


/* Subpage shift (NAND) */


int subpage_sft;

void *priv;   // 这个指针就是用来指向某一种设备的结构体,实现硬件层与软件层的代码结合

struct module *owner;


int usecount;

/* If the driver is something smart, like UBI, it may need to maintain


* its own reference counting. The below functions are only for driver.


* The driver may register its callbacks. These callbacks are not


* supposed to be called by MTD users */


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


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

};

再来看看mtd里面这些参数是怎么初始化的,从代码流程分析一下

调用关系

nand_scan --> nand_scan_ident --> nand_get_flash_type

nand_scan 如何调用的需要读我前面一篇文章:http://blog.csdn.net/andy_wsj/article/details/9335755

nand_get_flash_type函数里面设置了几个变量,看看代码:

static const struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
 struct nand_chip *chip,
 int busw,
 int *maf_id, int *dev_id,
 const struct nand_flash_dev *type)
{
int i, maf_idx;
u8 id_data[8];
int ret;

/* Select the device */
chip->select_chip(mtd, 0);   //   片选函数,用户驱动自己实现

/*
* Reset the chip, required by some chips (e.g. Micron MT29FxGxxxxx)
* after power-up
*/
chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);   // 命令操作函数----复位,用户驱动自己实现

/* Send the command for reading device ID */
chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);   // 命令操作函数----读ID

/* Read manufacturer and device IDs */
*maf_id = chip->read_byte(mtd);    // 厂商ID
*dev_id = chip->read_byte(mtd);    // 设备id

/* Try again to make sure, as some systems the bus-hold or other
* interface concerns can cause random data which looks like a
* possibly credible NAND flash to appear. If the two results do
* not match, ignore the device completely.
*/

chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);

for (i = 0; i < 2; i++)    //  读两个字节,参考nand芯片资料,第1、2个字节就是厂商id和设备id
id_data[i] = chip->read_byte(mtd);

if (id_data[0] != *maf_id || id_data[1] != *dev_id) {
printk(KERN_INFO "%s: second ID read did not match "
      "%02x,%02x against %02x,%02x\n", __func__,
      *maf_id, *dev_id, id_data[0], id_data[1]);
return ERR_PTR(-ENODEV);
}

if (!type)                                            // 这个指针调用时传的是NULL,那么将指向nand_flash_ids
type = nand_flash_ids;         // nand_flash_ids就是MTD支持的nand设备列表,在文件\u-boot-sunxi-sunxi\drivers\mtd\nand\nand_ids.c内

// cubieboard使用的K9GBG08U0A也在这个列表内,如果不再,自己定义,传过来也可以

for (; type->name != NULL; type++)  // 通过设备id找到对应的设备
if (*dev_id == type->id)
break;

chip->onfi_version = 0;
if (!type->name || !type->pagesize) {                           // 如果找到设备,看看是不是ONFI标准芯片,这是一个intel推出的nand接口标准
/* Check is chip is ONFI compliant */                        // 需要定义宏CONFIG_SYS_NAND_ONFI_DETECTION
ret = nand_flash_detect_onfi(mtd, chip, &busw);   //  不用看资料,三星的芯片不会去凑intel的标准,不是让人宰吗?没有定义上述宏,调用返回值是0
if (ret)                                                                               //  最后我忍不住看看K9GBG08U0A的资料,没有任何地方提到ONFI

goto ident_done;                                                          
}

// 若不是ONFI flash ,通过以下代码初始化

chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);

/* Read entire ID string */

for (i = 0; i < 8; i++)
id_data[i] = chip->read_byte(mtd);  // 读取8个字节id信息,这个就要了解nand的id信息组成和作用了,不清楚的可以去网上找一找

if (!type->name)
return ERR_PTR(-ENODEV);

if (!mtd->name)  //  初始化设备名, 操作mtd
mtd->name = type->name;

chip->chipsize = (uint64_t)type->chipsize << 20;   // 芯片容量,按MB计算

if (!type->pagesize && chip->init_size) {                                     // 若用户自己定义了初始化函数chip->init_size,则使用用户的初始化
/* set the pagesize, oobsize, erasesize by the driver*/   
busw = chip->init_size(mtd, chip, id_data);
} else if (!type->pagesize) {
int extid;
/* The 3rd id byte holds MLC / multichip data */
chip->cellinfo = id_data[2];
/* The 4th id byte is the important one */
extid = id_data[3];

/*
* Field definitions are in the following datasheets:
* Old style (4,5 byte ID): Samsung K9GAG08U0M (p.32)
* New style   (6 byte ID): Samsung K9GBG08U0M (p.40)
*
* Check for wraparound + Samsung ID + nonzero 6th byte
* to decide what to do.
*/
if (id_data[0] == id_data[6] && id_data[1] == id_data[7] &&
id_data[0] == NAND_MFR_SAMSUNG &&
(chip->cellinfo & NAND_CI_CELLTYPE_MSK) &&
id_data[5] != 0x00) {
/* Calc pagesize */
mtd->writesize = 2048 << (extid & 0x03);
extid >>= 2;
/* Calc oobsize */
switch (extid & 0x03) {
case 1:
mtd->oobsize = 128;
break;
case 2:
mtd->oobsize = 218;
break;
case 3:
mtd->oobsize = 400;
break;
default:
mtd->oobsize = 436;
break;
}
extid >>= 2;
/* Calc blocksize */
mtd->erasesize = (128 * 1024) <<
(((extid >> 1) & 0x04) | (extid & 0x03));
busw = 0;
} else {
/* Calc pagesize */
mtd->writesize = 1024 << (extid & 0x03);
extid >>= 2;
/* Calc oobsize */
mtd->oobsize = (8 << (extid & 0x01)) *
(mtd->writesize >> 9);
extid >>= 2;
/* Calc blocksize. Blocksize is multiples of 64KiB */
mtd->erasesize = (64 * 1024) << (extid & 0x03);
extid >>= 2;
/* Get buswidth information */
busw = (extid & 0x01) ? NAND_BUSWIDTH_16 : 0;
}
} else {
/*
* Old devices have chip data hardcoded in the device id table
*/
mtd->erasesize = type->erasesize;
mtd->writesize = type->pagesize;
mtd->oobsize = mtd->writesize / 32;
busw = type->options & NAND_BUSWIDTH_16;

/*
* Check for Spansion/AMD ID + repeating 5th, 6th byte since
* some Spansion chips have erasesize that conflicts with size
* listed in nand_ids table
* Data sheet (5 byte ID): Spansion S30ML-P ORNAND (p.39)
*/
if (*maf_id == NAND_MFR_AMD && id_data[4] != 0x00 &&
id_data[5] == 0x00 && id_data[6] == 0x00 &&
id_data[7] == 0x00 && mtd->writesize == 512) {
mtd->erasesize = 128 * 1024;
mtd->erasesize <<= ((id_data[3] & 0x03) << 1);
}
}
/* Get chip options, preserve non chip based options */
chip->options |= type->options;

/* Check if chip is a not a samsung device. Do not clear the
* options for chips which are not having an extended id.
*/
if (*maf_id != NAND_MFR_SAMSUNG && !type->pagesize)
chip->options &= ~NAND_SAMSUNG_LP_OPTIONS;

ident_done:

............篇幅关系,部分代码略............

return type;
}

这里初始化了:

mtd->name

mtd->erasesize

mtd->writesize

mtd->oobsize

这些都是描述nand的数据

那么操作函数在哪初始化呢?

nand_scan --> nand_scan_tail

如果上面的操作成功,那么就会调用函数nand_scan_tail初始化结构体定义的各种操作,

这个函数在文件\u-boot-sunxi-sunxi\drivers\mtd\nand\nand_base.c内,

函数比较长,截取其中一部分:

int nand_scan_tail(struct mtd_info *mtd)
{
      .........略去N行..........

/* Fill in remaining MTD driver data */
mtd->type = MTD_NANDFLASH;
mtd->flags = (chip->options & NAND_ROM) ? MTD_CAP_ROM :
MTD_CAP_NANDFLASH;
mtd->erase = nand_erase;
mtd->point = NULL;
mtd->unpoint = NULL;
mtd->read = nand_read;
mtd->write = nand_write;
mtd->read_oob = nand_read_oob;
mtd->write_oob = nand_write_oob;
mtd->sync = nand_sync;
mtd->lock = NULL;
mtd->unlock = NULL;
mtd->block_isbad = nand_block_isbad;
mtd->block_markbad = nand_block_markbad;

/* propagate ecc.layout to mtd_info */
mtd->ecclayout = chip->ecc.layout;

/* Check, if we should skip the bad block table scan */
if (chip->options & NAND_SKIP_BBTSCAN)
chip->options |= NAND_BBT_SCANNED;

return 0;
}

可以看出,调用这个函数之后,一个MTD类型的nand驱动就建立了

上层,例如文件系统在写一个文件会调用mtd->write,从而进入函数nand_write();

至于mtd子系统如何与上层打交道,驱动工程师可以不关心。

就像我们知道一个CPU管脚的用法就可以了,不关心其内部电路结构也可以使用它。

当然,你要提升系统性能的时候,才需要每个点都去深究,那是是高级内容了。

这些初始化完成之后,通过以下调用路径加入处理链表中

nand_init --> nand_init_chip --> nand_register --> add_mtd_device

代码就是

#ifdef CONFIG_MTD_DEVICE
/*
* Add MTD device so that we can reference it later
* via the mtdcore infrastructure (e.g. ubi).
*/
add_mtd_device(mtd);
#endif

二、总结

如果需要初始化mtd模块

1、需要定义宏CONFIG_MTD_DEVICE

2、需要查找nand flash芯片资料,获取设备id,查找mtd是否默认支持,

若不支持,考虑自行添加确保初始化正确

3、初始化基本的操作,读、写、刷新等等

MTD中的nand驱动初步分析---面向u-boot的更多相关文章

  1. MTD下的Nand驱动

    目录 MTD下的Nand驱动 引入 平台设备资源文件 关键数据结构 平台框架 s3c24xx_nand_probe nand_scan s3c2410_nand_add_partition add_m ...

  2. Android中Input型输入设备驱动原理分析(一)

    转自:http://blog.csdn.net/eilianlau/article/details/6969361 话说Android中Event输入设备驱动原理分析还不如说Linux输入子系统呢,反 ...

  3. Android中Input型输入设备驱动原理分析<一>

    话说Android中Event输入设备驱动原理分析还不如说Linux输入子系统呢,反正这个是没变的,在android的底层开发中对于Linux的基本驱动程序设计还是没变的,当然Android底层机制也 ...

  4. Linux-Nand Flash驱动(分析MTD层并制作NAND驱动)

    1.本节使用的nand flash型号为K9F2G08U0M,它的命令如下: 1.1我们以上图的read id(读ID)为例,它的时序图如下: 首先需要使能CE片选 1)使能CLE 2)发送0X90命 ...

  5. 24.Linux-Nand Flash驱动(分析MTD层并制作NAND驱动)

    1.本节使用的nand flash型号为K9F2G08U0M,它的命令如下: 1.1我们以上图的read id(读ID)为例,它的时序图如下: 首先需要使能CE片选 1)使能CLE 2)发送0X90命 ...

  6. 基于MTD的NAND驱动开发、K9F1G08 2K page、Yaffs2 Files System

    转载:http://hi.baidu.com/cui1206/item/1d4119e376132513585dd886 基于MTD的NAND驱动(linux-2.6.22.10内核),目前已可以在该 ...

  7. Linux内核中SPI总线驱动分析

    本文主要有两个大的模块:一个是SPI总线驱动的分析 (研究了具体实现的过程): 另一个是SPI总线驱动的编写(不用研究具体的实现过程). 1 SPI概述 SPI是英语Serial Peripheral ...

  8. u-boot的nand驱动写过程分析

    从命令说起,在u-boot输入下列命令: nand write 40008000 0 20000 命令的意思是将内存0x40008000开始的部分写入nand,从nand地址0开始写,写入长度是0x2 ...

  9. 十九、eMMC驱动框架分析

    一.MMC简介 eMMC在封装中集成了一个控制器,提供标准接口并管理Nand Flash,使得手机厂商就能专注于产品开发的其它部分,并缩短向市场推出产品的时间. 对于我们来说,eMMC就是在Nand ...

随机推荐

  1. 窗体区域绘制问题WS_CLIPCHILDREN与WS_CLIPSIBLINGS

    WS_CLIPCHILDREN,使得父窗体在绘制时留出其上的子窗体的位置不去画它,而那片区域留着子窗体去画.WS_CLIPSIBLINGS,必须用于子窗体,使得该子窗体在收到WM_PAINT时同时令其 ...

  2. W3C 、HTML 、CSS 发展介绍

    一.W3C W3C 指万维网联盟(World Wide Web Consortium),创建于1994年10月,由 Tim Berners-Lee (他是html的发明人)创建. W3C开始被创建的目 ...

  3. net use \\192.168.54.145 /user:administrator "12345qwert"无法连接,错误码1326

    1.在远程机的"控制面板-文件夹选项-查看-简单的文件共享",去掉选取,然后再尝试连接 2.控制面板\所有控制面板项\管理工具 下-->本地安全策略-->安全设置--& ...

  4. 01-IOSCore - NSString、NSFileManager、NSBundle、StringAndObjectConvert

    模型 1 将数据存储到硬盘,将硬盘上的数据在读回内存 2 文件存储: NSFileHandle 对文件的读写 NSData 二进制数据 NSString 表示文件路径 NSFileManager(对文 ...

  5. perl 传递对象到模块

    perl 中的对象 就是引用 通过new方法传递数据结构给各个模块 [root@wx03 test]# cat x1.pm package x1; use Data::Dumper; sub new ...

  6. Math.random与java.util.Random的差别

    今天在做一道习题时想到了Math.random()与Random类有什么区别,查阅了一些资料,感觉讲的不是太好. 首先两者的区别是一个是方法,一个是类. 其实前者的实现借助与后者.大家可以看一下Mat ...

  7. 一道经典的C++结构体的题目

    题目描述: 有10个学生,每个学生的数据包括学号.姓名.英语.数学.物理三门课的成绩,从键盘输入10个学生数据,要求打印出3门课程的总平均成绩,以及最高分的学生的数据(包括学号,姓名,3门课的平均成绩 ...

  8. bash on windows

    bash on windows 今年微软Build 2016大会最让开发人员兴奋的消息之一,就是在Windows上可以原生运行Linux bash,对开发人员来说,这是一个喜闻乐见的消息. 1 安装 ...

  9. 简单区分`:before`与`::before`的区别

    简单区分:before与::before的区别 :hover我们都知道,称作伪类,英文名pseudo-class,而我们此处提到的:before以及:after也是伪类,属于css2的内容,在ie8下 ...

  10. Ubuntu中查看硬盘分区UUID的方法(所有Linux目录的解释)

    在Ubuntu中UUID的两种获取方法,至于UUID是什么,你可以大概理解为分区的标识符,像条形码那样. 在终端中输入下面的命令就可心查看到分区UUID了.命令1.sudo blkid 命令2.ls ...