linux nandflash

本文深入解析NandFlash的布局及操作原理,介绍其硬件特性、ECC校验机制,并探讨NandFlash驱动中的关键结构体及函数实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


1. 前言:

1.1 先分析下nandflash的布局、操作结构


由图可以看出一片Nand flash为一个设备(device),其数据存储分层为:

a.  1个设备(device)=1024个块(Blocks),块也是Nand flash擦除操作的最小单位。

b.  1个块(block) = 64页(Pages),页是Nand flash写入的最小单位,对于每一个页,由数据块区域和空闲区域。数据区,也容易理解,就是存储一些数据,而对于空闲区,一般也叫做OOB(out of Band),这个区域,是基于Nand flash的硬件特性设计的,Nand flash在数据读写的时候很容易错误,所以为了保证数据的正确性,必须要有对应的检测和纠错的机制,此机制就被叫做ECC,所以设计了该多余的区域,用来放置数据的校验值。

c.  1个页(page) = 数据块大小(2K)+OOB块大小(64Bytes)

那么通过上面我们就可以计算出怎么访问一个物理地址:块大小*块号+页大小*页号+页内地址。

从硬件的图来看,对于K9F1G08X0C这款芯片,其容量为132MB,那么就应该需要28条地址先,而现在只有8条地址线,对于Nand flash就导入了地址周期的概念,对于该款flash,所以需要4个周期:2个列地址(Column)周期和2个行地址(ROW)周期。从下面的功能框图来看,对于列地址A0--A11,就是页内地址,地址范围就是0--4094,与页内地址(2K+64)吻合,其实对于页内地址,其实只需要A0-A10,而对于多出来的A11,是用来表示页内的oob区域。那么对于A12-A27就是用来表示属于哪一个块和块里面的哪一个页号。

1.2 要对nandflash操作,比如读取一页数据、写入一页数据,都要发送对应的命令,而且要符合硬件的规定,如图:


从上图可以看出,在Read读取数据时需两个周期Cycle,即分两次发送对应的命令,第一次是0x00,第二次是0x30,而且两次中间要发送对应的地址行地址(页)+列地址(页内地址),这个要特别注意,在下面的源码读取数据时会分析到。

由于篇幅原因,关于nandflash中oob、bbt、ecc,详见:点击打开链接

2. nandflash相关结构体

a. struct nuc970_nand_info

struct nuc970_nand_info {
    struct nand_hw_control  controller; //nandflash驱动控制器操作结构体
    struct mtd_info         mtd; //nandflash分区操作结构体
    struct nand_chip        chip; //芯片操作结构体
    struct mtd_partition    *parts;     //nandflash分区数组信息
    int                     nr_parts;   //nandflash分区数组个数
    struct platform_device  *pdev; //指向dev.c中的设备
    struct clk              *clk; //nandflash控制器时钟
    struct clk              *fmi_clk; //fmi控制器时钟

    void __iomem            *reg; //寄存器
    int                     eBCHAlgo; //BCH ECC算法
    int                     m_i32SMRASize; //见datasheet  FMI__NANDRACTL  表示oob的字节数
    int                     m_ePageSize; //页大小

    unsigned char *         pnand_vaddr; //nandflash虚拟地址
    unsigned char *         pnand_phyaddr; //nandflash物理地址

    int                     m_i32MyShowTime; //???相当于信号量
    spinlock_t              lock;
};

b. struct nand_hw_control

struct nand_hw_control {
	spinlock_t lock;
	struct nand_chip *active; //芯片操作结构体
	wait_queue_head_t wq;
};

c. struct nand_chip

struct nand_chip {
	void __iomem *IO_ADDR_R; 
	void __iomem *IO_ADDR_W;

	uint8_t (*read_byte)(struct mtd_info *mtd); //读一个byte
	u16 (*read_word)(struct mtd_info *mtd); //读一个word
	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); //读一个缓冲区数据
	void (*select_chip)(struct mtd_info *mtd, int chip); //片选
	int (*block_bad)(struct mtd_info *mtd, loff_t ofs, int getchip); //坏块
	int (*block_markbad)(struct mtd_info *mtd, loff_t ofs); //坏块标记
	void (*cmd_ctrl)(struct mtd_info *mtd, int dat, unsigned int ctrl); //命令控制
	int (*init_size)(struct mtd_info *mtd, struct nand_chip *this,
			u8 *id_data); //???
	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); //bbt:bad blockta(即坏块表),扫描坏块表
	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,
			uint32_t offset, int data_len, const uint8_t *buf,
			int oob_required, int page, int cached, int raw);
	int (*onfi_set_features)(struct mtd_info *mtd, struct nand_chip *chip,
			int feature_addr, uint8_t *subfeature_para);
	int (*onfi_get_features)(struct mtd_info *mtd, struct nand_chip *chip,
			int feature_addr, uint8_t *subfeature_para);

	int chip_delay; //芯片时序特性
	unsigned int options;
	unsigned int bbt_options; //坏块表选项

	int page_shift;
	int phys_erase_shift;
	int bbt_erase_shift;
	int chip_shift;
	int numchips;
	uint64_t chipsize;
	int pagemask;
	int pagebuf;
	unsigned int pagebuf_bitflips;
	int subpagesize;
	uint8_t cellinfo;
	int badblockpos;
	int badblockbits;

	int onfi_version; //ONFI版本
	struct nand_onfi_params	onfi_params;

	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;

	uint8_t *bbt;
	struct nand_bbt_descr *bbt_td; //坏块表
	struct nand_bbt_descr *bbt_md;

	struct nand_bbt_descr *badblock_pattern;

	void *priv;
};

d. struct mtd_partition

struct mtd_partition {
	char *name;			/* identifier string */
	uint64_t size;			/* partition size */
	uint64_t offset;		/* offset within the master MTD space */
	uint32_t mask_flags;		/* master MTD flags to mask out for this partition */
	struct nand_ecclayout *ecclayout;	/* out of band layout for this partition (NAND only) */
};

e. struct nand_ecclayout

struct nand_ecclayout {
	__u32 eccbytes; //表示使用几个ecc字节
	__u32 eccpos[MTD_MAX_ECCPOS_ENTRIES_LARGE]; //表示ecc占用的位置,因为现在一版大页面4kbyte也就128个,所以数组为128,以后有更大页面的,这里也要改了。
	__u32 oobavail; //有几个oob可用,这个跟下面的成员有点像,一般用下面的
	struct nand_oobfree oobfree[MTD_MAX_OOBFREE_ENTRIES_LARGE]; //定义oob有效个数,从哪开始等
};

d. struct nand_oobfree

struct nand_oobfree {
	__u32 offset;
	__u32 length;
};

e. struct mtd_info

struct mtd_info {
	u_char type; //flash类型,如MTD_NORFLASH,MTD_NANDFLASH
	uint32_t flags; //MTD属性标志,MTD_WRITEABLE,MTD_NO_ERASE等
	uint64_t size;	 //mtd设备的大小

	/* "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
	 */
	uint32_t erasesize; //MTD设备的擦除单元大小,对于NandFlash来说就是Block的大小
	/* 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.
	 */
	uint32_t writesize; //写大小, 对于norFlash是字节,对nandFlash为一页

	/*
	 * Size of the write buffer used by the MTD. MTD devices having a write
	 * buffer can write multiple writesize chunks at a time. E.g. while
	 * writing 4 * writesize bytes to a device with 2 * writesize bytes
	 * buffer the MTD driver can (but doesn't have to) do 2 writesize
	 * operations, but not 4. Currently, all NANDs have writebufsize
	 * equivalent to writesize (NAND page size). Some NOR flashes do have
	 * writebufsize greater than writesize.
	 */
	uint32_t writebufsize; //

	uint32_t oobsize;   // Amount of OOB data per block (e.g. 16)  OOB字节数
	uint32_t oobavail;  // Available OOB bytes per block 可用的OOB字节数

	/*
	 * If erasesize is a power of 2 then the shift is stored in
	 * erasesize_shift otherwise erasesize_shift is zero. Ditto writesize.
	 */
	unsigned int erasesize_shift;
	unsigned int writesize_shift;
	/* Masks based on erasesize_shift and writesize_shift */
	unsigned int erasesize_mask;
	unsigned int writesize_mask;

	/*
	 * read ops return -EUCLEAN if max number of bitflips corrected on any
	 * one region comprising an ecc step equals or exceeds this value.
	 * Settable by driver, else defaults to ecc_strength.  User can override
	 * in sysfs.  N.B. The meaning of the -EUCLEAN return code has changed;
	 * see Documentation/ABI/testing/sysfs-class-mtd for more detail.
	 */
	unsigned int bitflip_threshold;

	// Kernel-only stuff starts here.
	const char *name;
	int index;

	/* ECC layout structure pointer - read only! */
	struct nand_ecclayout *ecclayout;

	/* max number of correctible bit errors per ecc step */
	unsigned int ecc_strength;

	/* 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; //可变擦除区域

	/*
	 * Do not call via these pointers, use corresponding mtd_*()
	 * wrappers instead.
	 */

	//擦除函数
	int (*_erase) (struct mtd_info *mtd, struct erase_info *instr);
	int (*_point) (struct mtd_info *mtd, loff_t from, size_t len,
		       size_t *retlen, void **virt, resource_size_t *phys);
	int (*_unpoint) (struct mtd_info *mtd, loff_t from, size_t len);
	unsigned long (*_get_unmapped_area) (struct mtd_info *mtd,
					     unsigned long len,
					     unsigned long offset,
					     unsigned long flags);

	// 读写flash函数 					 
	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);
	int (*_panic_write) (struct mtd_info *mtd, loff_t to, size_t len,
			     size_t *retlen, const u_char *buf);

	//带oob读写Flash函数			 
	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 (*_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 to,
				     size_t len, size_t *retlen, u_char *buf);
	int (*_lock_user_prot_reg) (struct mtd_info *mtd, loff_t from,
				    size_t len);
	int (*_writev) (struct mtd_info *mtd, const struct kvec *vecs,
			unsigned long count, loff_t to, size_t *retlen);
	void (*_sync) (struct mtd_info *mtd);
	int (*_lock) (struct mtd_info *mtd, loff_t ofs, uint64_t len);
	int (*_unlock) (struct mtd_info *mtd, loff_t ofs, uint64_t len);
	int (*_is_locked) (struct mtd_info *mtd, loff_t ofs, uint64_t len);
	int (*_block_isbad) (struct mtd_info *mtd, loff_t ofs);
	int (*_block_markbad) (struct mtd_info *mtd, loff_t ofs);
	int (*_suspend) (struct mtd_info *mtd);
	void (*_resume) (struct mtd_info *mtd);
	/*
	 * If the driver is something smart, like UBI, it may need to maintain
	 * its own reference counting. The below functions are only for driver.
	 */
	int (*_get_device) (struct mtd_info *mtd);
	void (*_put_device) (struct mtd_info *mtd);

	/* Backing device capabilities for this device
	 * - provides mmap capabilities
	 */
	struct backing_dev_info *backing_dev_info;

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

	/* ECC status information */
	struct mtd_ecc_stats ecc_stats;
	/* Subpage shift (NAND) */
	int subpage_sft;

	void *priv; //指向chip,     struct nand_chip *chip

	struct module *owner;
	struct device dev;
	int usecount;
};

3. struct platform_device 

static u64 nuc970_device_fmi_dmamask = 0xffffffffUL;
struct platform_device nuc970_device_fmi = {
        .name		  = "nuc970-fmi",
        .id		  = -1,
        .num_resources	  = ARRAY_SIZE(nuc970_fmi_resource),
        .resource	  = nuc970_fmi_resource,
	    .dev              = {
		.dma_mask = &nuc970_device_fmi_dmamask,
		.coherent_dma_mask = 0xffffffffUL
	}
};

static struct resource nuc970_fmi_resource[] = {
        [0] = {
                .start = NUC970_PA_FMI,
                .end   = NUC970_PA_FMI + NUC970_SZ_FMI - 1,
                .flags = IORESOURCE_MEM,
        },
        [1] = {
                .start = IRQ_FMI,
                .end   = IRQ_FMI,
                .flags = IORESOURCE_IRQ,
        }
};

static u64 nuc970_device_fmi_dmamask = 0xffffffffUL;
platform_add_devices(&nuc970_device_fmi, 1);

fmi设备注册。

4. static struct platform_driver

static struct platform_driver nuc970_nand_driver = {
        .driver = {
        .name   = "nuc970-fmi",
        .owner  = THIS_MODULE,
        },
        .probe      = nuc970_nand_probe,
        .remove     = nuc970_nand_remove,
};

设备和驱动通过名称“nuc970-fmi”匹配,调用.probe=nuc970_nand_probe;

5. probe(...)

在分析nuc970_nand_probe(...)函数之前,先看下驱动的几个API

nandflash硬件ECC初始化:

static void nuc970_nand_hwecc_init (struct mtd_info *mtd)
{
    struct nuc970_nand_info *nand = container_of(mtd, struct nuc970_nand_info, mtd);

    writel ( readl(REG_SMCSR)|0x1, REG_SMCSR);    // reset SM controller

    // Redundant area size
    writel( nand->m_i32SMRASize , REG_SMREACTL );

    // Protect redundant 3 bytes
    // because we need to implement write_oob function to partial data to oob available area.
    // Please note we skip 4 bytes
    writel( readl(REG_SMCSR) | 0x100, REG_SMCSR);

    // To read/write the ECC parity codes automatically from/to NAND Flash after data area field written.
    writel( readl(REG_SMCSR) | 0x10, REG_SMCSR);

    if ( nand->eBCHAlgo >= 0 ) {
        // Set BCH algorithm  设置ECC算法
        writel( (readl(REG_SMCSR) & (~0x007C0000)) | g_i32BCHAlgoIdx[nand->eBCHAlgo], REG_SMCSR);

        // Enable H/W ECC, ECC parity check enable bit during read page
        //使能ECC算法 | 使能读取页数据后,使用字段ecc检查
        writel( readl(REG_SMCSR) | 0x00800080, REG_SMCSR);

    } else  {
        // Disable H/W ECC / ECC parity check enable bit during read page
        //禁止ECC | 读取数据后,禁止使用字段
        writel( readl(REG_SMCSR) & (~0x00800000) &(~0x80), REG_SMCSR);
    }
}

硬件ECC禁止:

static void nuc970_nand_hwecc_fini (struct mtd_info *mtd)
{
    struct nand_chip *chip = mtd->priv;
    if ( chip->ecc.mode == NAND_ECC_HW_OOB_FIRST )
        writel(readl(REG_SMCSR)&(~0x00800000), REG_SMCSR); // ECC disable
}

NandFlash初始化,包括nandflash选择使能,通讯时序设置,片选CS0,写保护寄存器开启,软复位nandflash:

static void nuc970_nand_initialize ( void )
{
    ENTER() ;

    // Enable SM_EN  该寄存器还包括eMMC,这里使能nandflash功能
    writel( NAND_EN, REG_NAND_FMICSR );

    // Timing control
    // writel(0x3050b, REG_SMTCR);
    // tCLS= (2+1)TAHB,
    // tCLH= (2*2+2)TAHB,
    // tALS= (2*1+1)TAHB,
    // tALH= (2*2+2)TAHB,
    writel(0x20305, REG_SMTCR); //时序控制

    // Enable SM_CS0 片选CS0
    writel((readl(REG_SMCSR)&(~0x06000000))|0x04000000, REG_SMCSR);

	//nandflash 写保护寄存器, 1表示没有写保护,数据是可写的
    writel(0x1, REG_NFECR); /* un-lock write protect */

    // NAND Reset 软复位
    writel(readl(REG_SMCSR) | 0x1, REG_SMCSR);    // software reset

    LEAVE();
}

页大小决定了ecc空间,具体如下:

static const int g_i32ParityNum[ePageSize_CNT][eBCH_CNT] = { 
    { 8,    15,     23,     29,     -1  },  // For 512
    { 32,   60,     92,     116,    90  },  // For 2K
    { 64,   120,    184,    232,    180 },  // For 4K
    { 128,  240,    368,    464,    360 },  // For 8K
};

硬件ecc选择bch算法:

static int nuc970_hwecc_choice_bch_algorithm ( E_PAGESIZE ePagesize, int oob_size, int oob_available )
{
    int i;
    /* Rule: select a BCH algorithm as the oob_size/2 large than parity number */
    for ( i=eBCH_T24; i>=0; i-- )
        if ( g_i32ParityNum[ePagesize][i] > 0 )
			//oob_size-oob_available-DEF_RESERVER_OOB_SIZE_FOR_MARKER=存储ecc校验空间,
			//如果g_i32ParityNum[ePagesize][i]值小于该空间表示可以存储ecc的校验值,即
			//可选择该硬件bch算法.
            if ( g_i32ParityNum[ePagesize][i] <= (oob_size-oob_available-DEF_RESERVER_OOB_SIZE_FOR_MARKER) )  // use half ecc size
                break;
    return i;
}

nuc970选择ECC算法:

static void nuc970_choice_bch_algo ( struct mtd_info *mtd, int page )
{
    int i;
    struct nuc970_nand_info *nand = container_of(mtd, struct nuc970_nand_info, mtd);
    struct mtd_partition *part;
    struct nand_ecclayout *part_ecclayout;
    int BCHAlgoIdx=0;
    int SMRASize=0;

    u32 page_addr = page * mtd->writesize; //页地址*1页的大小

    for ( i=0; i<nand->nr_parts; i++ )
    {
        part = &nand->parts[i];
        part_ecclayout = part->ecclayout;
        if ( part_ecclayout && part_ecclayout != mtd->ecclayout ) { //ecclayout是否匹配?
            if ( part->offset <= page_addr && page_addr < part->offset+part->size ) //判定page_addr地址是否在当前分区范围内?
            {
                struct nuc970_nand_ecclayout * pSNUC970NandEccLayout=(struct nuc970_nand_ecclayout *)part_ecclayout;
                BCHAlgoIdx = pSNUC970NandEccLayout->m_BCHAlgoidx;
                SMRASize = pSNUC970NandEccLayout->m_SMRASize;
                break;
            }
        }
    }
    if ( i == nand->nr_parts ) //相等表示未找到对应的分区
    {
		//chp  BCHAlgoIdx = nuc970_hwecc_choice_bch_algorithm ( nand->m_ePageSize, mtd->oobsize, mtd->oobavail );
        BCHAlgoIdx = nuc970_nand_SYSTEM_oob.m_BCHAlgoidx;
        SMRASize = mtd->oobsize;
    }

    if ( BCHAlgoIdx != nand->eBCHAlgo ) //bch算法进行了更新?
    {
        if ( BCHAlgoIdx >= 0 )
            printk("(0x%08X)BCH change to %s: %dB\n",  page_addr, g_pcBCHAlgoIdx[BCHAlgoIdx],  g_i32ParityNum[nand->m_ePageSize][BCHAlgoIdx] );
        else
            printk("(0x%08X)BCH change to No-ECC\n",  page_addr );
    }
    nand->eBCHAlgo = BCHAlgoIdx;

    if ( SMRASize != nand->m_i32SMRASize )
        printk("(0x%08X)SMRA size change to %dB\n",  page_addr, SMRASize );
    nand->m_i32SMRASize = SMRASize; 
}

nandflash ecc数据纠正:

void fmiSM_CorrectData_BCH(u8 ucFieidIndex, u8 ucErrorCnt, u8* pDAddr)
{
    u32 uaData[24], uaAddr[24];
    u32 uaErrorData[4];
    u8  ii, jj;
    u32 uPageSize;
    u32 field_len, padding_len, parity_len;
    u32 total_field_num;
    u8  *smra_index;

    ENTER();

    //--- assign some parameters for different BCH and page size
    switch (readl(REG_SMCSR) & 0x007C0000) //获取nandflash寄存器中BCH算法
    {
        case BCH_T24:
            field_len   = 1024;
            padding_len = BCH_PADDING_LEN_1024;
            parity_len  = BCH_PARITY_LEN_T24;
            break;
        case BCH_T15:
            field_len   = 512;
            padding_len = BCH_PADDING_LEN_512;
            parity_len  = BCH_PARITY_LEN_T15;
            break;
        case BCH_T12:
            field_len   = 512;
            padding_len = BCH_PADDING_LEN_512;
            parity_len  = BCH_PARITY_LEN_T12;
            break;
        case BCH_T8:
            field_len   = 512;
            padding_len = BCH_PADDING_LEN_512;
            parity_len  = BCH_PARITY_LEN_T8;
            break;
        case BCH_T4:
            field_len   = 512;
            padding_len = BCH_PADDING_LEN_512;
            parity_len  = BCH_PARITY_LEN_T4;
            break;
        default:
            printk("NAND ERROR: %s(): invalid SMCR_BCH_TSEL = 0x%08X\n", __FUNCTION__, (u32)(readl(REG_SMCSR) & 0x7C0000));
            LEAVE();
            return;
    }

    uPageSize = readl(REG_SMCSR) & 0x00030000; //获取nandflash中页大小
    switch (uPageSize)
    {
        case 0x30000:  total_field_num = 8192 / field_len; break;
        case 0x20000:  total_field_num = 4096 / field_len; break;
        case 0x10000:  total_field_num = 2048 / field_len; break;
        case 0x00000:  total_field_num =  512 / field_len; break;
        default:
            printk("NAND ERROR: %s(): invalid SMCR_PSIZE = 0x%08X\n", __FUNCTION__, uPageSize);
            LEAVE();
            return;
    }

    //--- got valid BCH_ECC_DATAx and parse them to uaData[]
    // got the valid register number of BCH_ECC_DATAx since one register include 4 error bytes
    jj = ucErrorCnt/4;
    jj ++;
    if (jj > 6)
        jj = 6;     // there are 6 BCH_ECC_DATAx registers to support BCH T24

    for(ii=0; ii<jj; ii++)
    {
        uaErrorData[ii] = readl(REG_BCH_ECC_DATA0 + ii*4); //读取ECC数据
    }

    for(ii=0; ii<jj; ii++)
    {
        uaData[ii*4+0] = uaErrorData[ii] & 0xff;
        uaData[ii*4+1] = (uaErrorData[ii]>>8) & 0xff;
        uaData[ii*4+2] = (uaErrorData[ii]>>16) & 0xff;
        uaData[ii*4+3] = (uaErrorData[ii]>>24) & 0xff;
    }

    //--- got valid REG_BCH_ECC_ADDRx and parse them to uaAddr[]
    // got the valid register number of REG_BCH_ECC_ADDRx since one register include 2 error addresses
    jj = ucErrorCnt/2;
    jj ++;
    if (jj > 12)
        jj = 12;    // there are 12 REG_BCH_ECC_ADDRx registers to support BCH T24

    for(ii=0; ii<jj; ii++)
    {
        uaAddr[ii*2+0] = readl(REG_BCH_ECC_ADDR0 + ii*4) & 0x07ff;   // 11 bits for error address   nandflash ecc错误字节地址
        uaAddr[ii*2+1] = (readl(REG_BCH_ECC_ADDR0 + ii*4)>>16) & 0x07ff;
    }

    //--- pointer to begin address of field that with data error
    pDAddr += (ucFieidIndex-1) * field_len;

    //--- correct each error bytes
    for(ii=0; ii<ucErrorCnt; ii++)
    {
        // for wrong data in field
        if (uaAddr[ii] < field_len)
        {
#ifdef NUC970_NAND_DEBUG
            printk("BCH error corrected for data: address 0x%08X, data [0x%02X] --> ",
                (unsigned int)(pDAddr+uaAddr[ii]), (unsigned int)(*(pDAddr+uaAddr[ii])));
#endif
            *(pDAddr+uaAddr[ii]) ^= uaData[ii]; //异或,计算ECC,即读取数据的ECC和nandflash中的ecc进行异或比较

#ifdef NUC970_NAND_DEBUG
            printk("[0x%02X]\n", *(pDAddr+uaAddr[ii]));
#endif
        }
        // for wrong first-3-bytes in redundancy area
        else if (uaAddr[ii] < (field_len+3))
        {
            uaAddr[ii] -= field_len;
            uaAddr[ii] += (parity_len*(ucFieidIndex-1));    // field offset

#ifdef NUC970_NAND_DEBUG
            printk("BCH error corrected for 3 bytes: address 0x%08X, data [0x%02X] --> ",
                (unsigned int)((u8 *)REG_SMRA0 + uaAddr[ii]), (unsigned int)(*((u8 *)REG_SMRA0 + uaAddr[ii])));
#endif
            *((u8 *)REG_SMRA0 + uaAddr[ii]) ^= uaData[ii];

#ifdef NUC970_NAND_DEBUG
            printk("[0x%02X]\n", *((u8 *)REG_SMRA0+uaAddr[ii]));
#endif
        }
		//以下代码为纠错代码,老衲没有看明白.......!!!!!
        // for wrong parity code in redundancy area
        else
        {
            // BCH_ERR_ADDRx = [data in field] + [3 bytes] + [xx] + [parity code]
            //                                   |<--     padding bytes      -->|
            // The BCH_ERR_ADDRx for last parity code always = field size + padding size.
            // So, the first parity code = field size + padding size - parity code length.
            // For example, for BCH T12, the first parity code = 512 + 32 - 23 = 521.
            // That is, error byte address offset within field is
            uaAddr[ii] = uaAddr[ii] - (field_len + padding_len - parity_len);

            // smra_index point to the first parity code of first field in register SMRA0~n
            smra_index = (u8 *)
                         (REG_SMRA0 + (readl(REG_SMREACTL) & 0x1ff) - // bottom of all parity code -
                          (parity_len * total_field_num)                             // byte count of all parity code
                         );

            // final address = first parity code of first field +
            //                 offset of fields +
            //                 offset within field

#ifdef NUC970_NAND_DEBUG
            printk("BCH error corrected for parity: address 0x%08X, data [0x%02X] --> ",
                (unsigned int)(smra_index + (parity_len * (ucFieidIndex-1)) + uaAddr[ii]),
                (unsigned int)(*(smra_index + (parity_len * (ucFieidIndex-1)) + uaAddr[ii])));
#endif
            *((u8 *)smra_index + (parity_len * (ucFieidIndex-1)) + uaAddr[ii]) ^= uaData[ii];

#ifdef NUC970_NAND_DEBUG
            printk("[0x%02X]\n",
                *((u8 *)smra_index + (parity_len * (ucFieidIndex-1)) + uaAddr[ii]));
#endif
        }
    }   // end of for (ii<ucErrorCnt)
    LEAVE();
}

int fmiSMCorrectData (struct mtd_info *mtd, unsigned long uDAddr )
{
    int uStatus, ii, jj, i32FieldNum=0;
    volatile int uErrorCnt = 0;

    ENTER();

    if ( readl ( REG_SMISR ) & 0x4 ) //只读,表明ECC校验值是否出错
    {
        if ( ( readl(REG_SMCSR) & 0x7C0000) == BCH_T24 ) //BCH_T24的页为1024字节,其他的为512字节
            i32FieldNum = mtd->writesize / 1024;    // Block=1024 for BCH
        else
            i32FieldNum = mtd->writesize / 512;

		//每一页的大小,我使用的是每页=2K,而计算ecc是每512字节计算一个oob,所以这里有四个
        if ( i32FieldNum < 4 ) //每一页会计算ECC校
            i32FieldNum  = 1;
        else
            i32FieldNum /= 4;

        for ( jj=0; jj<i32FieldNum; jj++ )
        {
        	//REG_SMECC_ST0+jj*4: 该寄存器每8个bit表示一个ECC状态,Bit0~1是否有ECC错误,Bit2~7 表示ECC错误的个数是多少?
            uStatus = readl ( REG_SMECC_ST0+jj*4 ); 
            if ( !uStatus )
                continue;

            for ( ii=1; ii<5; ii++ )
            {
                if ( !(uStatus & 0x03) ) { // No error   Bit0~1
                    uStatus >>= 8;
                    continue;
                } else if ( (uStatus & 0x03)==0x01 ) { // Correctable error  错误,数据纠正
                    uErrorCnt = (uStatus >> 2) & 0x1F;
#ifdef NUC970_NAND_DEBUG
                    printk("Field (%d, %d) have %d error!!\n", jj, ii, uErrorCnt);
#endif
                    fmiSM_CorrectData_BCH(jj*4+ii, uErrorCnt, (char*)uDAddr);
                    break;
                } else // uncorrectable error or ECC error
                {
#ifdef NUC970_NAND_DEBUG
                    printk("SM uncorrectable error is encountered, 0x%4x !!\n", uStatus);
#endif
                    LEAVE();
                    return -1;
                }
                uStatus >>= 8;
            }
        } //jj
    }
    LEAVE();
    return uErrorCnt;
}

使能硬件ECC:

void nuc970_nand_enable_hwecc(struct mtd_info *mtd, int mode)
{
    ENTER();
#ifdef NUC970_NAND_DEBUG
    {
        char * ptr=REG_SMRA0;
        int i=0;
        if( mode == NAND_ECC_READ )
            printk("[R]=\n");
        else
            printk("[W]=\n");

        for(i=0; i<mtd->oobsize; i++)
        {
            printk("%X ",  *(ptr+i) );
            if ( i % 32 == 31)
                printk("\n");
        }
        printk("\n");
    }
#endif
    LEAVE();
}

DMA使能、禁止:

static void nuc970_nand_dmac_init( void )
{
    // DMAC enable
    writel( readl(REG_NAND_DMACCSR) | 0x3, REG_NAND_DMACCSR);
    writel( readl(REG_NAND_DMACCSR) & (~0x2), REG_NAND_DMACCSR);

    // Clear DMA finished flag
    writel( readl(REG_SMISR) | 0x1, REG_SMISR);

    // Disable Interrupt
    writel(readl(REG_SMIER) & ~(0x1), REG_SMIER);
}

/*
 * nuc970_nand_dmac_fini - Finalize dma controller
 */
static void nuc970_nand_dmac_fini(void)
{
    // Clear DMA finished flag
    writel(readl(REG_SMISR) | 0x1, REG_SMISR);
}

读nandflash字节:

static unsigned char nuc970_nand_read_byte(struct mtd_info *mtd)
{
    unsigned char ret;
    struct nuc970_nand_info *nand;

    ENTER() ;

    if ( nuc970_wait_sem(mtd) < 0 )
        return -1;
    nand = container_of(mtd, struct nuc970_nand_info, mtd);
    ret = (unsigned char)read_data_reg(nand);
    nuc970_release_sem(mtd);

    LEAVE();
    return ret;
}

批量读nandflash字节:

static void nuc970_nand_read_buf(struct mtd_info *mtd, unsigned char *buf, int len)
{
    int i;
    struct nuc970_nand_info *nand;
    nand = container_of(mtd, struct nuc970_nand_info, mtd);

    ENTER() ;

    if ( nuc970_wait_sem(mtd) < 0 )
        return;
    for (i = 0; i < len; i++)
        buf[i] = (unsigned char)read_data_reg(nand);
    nuc970_release_sem(mtd);

    LEAVE();
}

批量写nandflash字节:

static void nuc970_nand_write_buf(struct mtd_info *mtd, const unsigned char *buf, int len)
{
    int i;
    struct nuc970_nand_info *nand;
    nand = container_of(mtd, struct nuc970_nand_info, mtd);

    ENTER() ;

    if ( nuc970_wait_sem(mtd) < 0 )
        return;
    for (i = 0; i < len; i++)
        write_data_reg(nand, buf[i]);
    nuc970_release_sem(mtd);

    LEAVE();
}

DMA对nandflash的读、写操作:

static inline int _nuc970_nand_dma_transfer(struct mtd_info *mtd, const u_char *addr, unsigned int len, int is_write)
{
    struct nuc970_nand_info *nand = container_of(mtd, struct nuc970_nand_info, mtd);
    dma_addr_t dma_addr = (dma_addr_t)nand->pnand_phyaddr;

    ENTER() ;

    // For save, wait DMAC to ready
    while ( readl(REG_NAND_DMACCSR) & 0x200 );

    // Reinitial dmac
    nuc970_nand_dmac_init();

    // Fill dma_addr
    writel((unsigned long)dma_addr, REG_NAND_DMACSAR);

    // Enable target abort interrupt generation during DMA transfer.
    writel( 0x1, REG_NAND_DMACIER);

    // Clear Ready/Busy 0 Rising edge detect flag
    writel(0x400, REG_SMISR);

    // Set which BCH algorithm
    if ( nand->eBCHAlgo >= 0 ) {
        // Set BCH algorithm
        writel( (readl(REG_SMCSR) & (~0x7C0000)) | g_i32BCHAlgoIdx[nand->eBCHAlgo], REG_SMCSR);
        // Enable H/W ECC, ECC parity check enable bit during read page
        writel( readl(REG_SMCSR) | 0x00800000 | 0x80, REG_SMCSR);

    } else  {
        // Disable H/W ECC / ECC parity check enable bit during read page
        writel( readl(REG_SMCSR) & (~0x00800080), REG_SMCSR);
    }

    writel( nand->m_i32SMRASize , REG_SMREACTL );

    writel( readl(REG_SMIER) & (~0x4), REG_SMIER );
    writel ( 0x4, REG_SMISR );

    // Enable SM_CS0
    writel((readl(REG_SMCSR)&(~0x06000000))|0x04000000, REG_SMCSR);
    /* setup and start DMA using dma_addr */

    if ( is_write ) {
        register char * ptr=REG_SMRA0; //register 是提示编译器将这个变量不放在栈内,放在寄存器中进行操作
        // To mark this page as dirty.
        if ( ptr[3] == 0xFF )
            ptr[3] = 0;
        if ( ptr[2] == 0xFF )
            ptr[2] = 0;

        if ( addr )
            memcpy( (void*)nand->pnand_vaddr, (void*)addr, len);

        writel ( readl(REG_SMCSR) | 0x4, REG_SMCSR ); //DMA写使能
        while ( !(readl(REG_SMISR) & 0x1) ); //DMA读、写数据完成中断标识

    } else {
        // Blocking for reading
        // Enable DMA Read

        writel ( readl(REG_SMCSR) | 0x2, REG_SMCSR); //DMA读使能
        if ( readl(REG_SMCSR) & 0x80 ) {
            do {
                int stat=0;
                if ( (stat=fmiSMCorrectData ( mtd,  (unsigned long)nand->pnand_vaddr)) < 0 ) //重要,待分析!!!
                {
                    mtd->ecc_stats.failed++;
                    writel ( 0x4, REG_SMISR ); //清除ECC校验出错中断标识
                    writel ( 0x3, REG_NAND_DMACCSR);          // reset DMAC
                    writel ( readl(REG_SMCSR)|0x1, REG_SMCSR);    // reset SM controller
                    break;
                }
                else if ( stat > 0 ) {
                    //mtd->ecc_stats.corrected += stat; //Occure: MLC UBIFS mount error
                    writel ( 0x4, REG_SMISR );
                }

            } while ( !(readl(REG_SMISR) & 0x1) || (readl(REG_SMISR) & 0x4) );
        } else
            while ( !(readl(REG_SMISR) & 0x1) );

        if ( addr )
            memcpy( (void*)addr, (void*)nand->pnand_vaddr,  len );
    }

    nuc970_nand_dmac_fini();
    LEAVE();
    return 0;
}
从DMA中nandflash批量读:
static void nuc970_read_buf_dma(struct mtd_info *mtd, u_char *buf, int len)
{
    ENTER();

    if ( nuc970_wait_sem(mtd) < 0 )
        return;

    if ( len == mtd->writesize ) /* start transfer in DMA mode */
        _nuc970_nand_dma_transfer ( mtd, buf, len, 0x0);
    else {
        nuc970_nand_read_buf(mtd, buf, len);

#ifdef NUC970_NAND_DEBUG
        {
        int i;
        printk("R OOB %d\n", len );
        for ( i=0; i<len; i++ )
        {
            printk("%02X ", buf[i] );
            if ( i%32 == 31 )   printk("\n");
        }
        printk("\n");
        }
#endif
    }
    nuc970_release_sem(mtd);
    LEAVE();
}
向DMA中nandflash批量写:
static void nuc970_write_buf_dma(struct mtd_info *mtd, const u_char *buf, int len)
{
    ENTER();

    if ( nuc970_wait_sem(mtd) < 0 )
        return;

    if ( len == mtd->writesize ) /* start transfer in DMA mode */
        _nuc970_nand_dma_transfer(mtd, (u_char *)buf, len, 0x1);
    else
    {
#ifdef NUC970_NAND_DEBUG
        int i;
        printk("W OOB %d\n", len);
        for ( i=0; i<len; i++ )
        {
            printk("%02X ", buf[i] );
            if ( i%32 == 31 )   printk("\n");
        }
#endif
        nuc970_nand_write_buf(mtd, buf, len);
    }

    nuc970_release_sem(mtd);

    LEAVE();
}

校验nandflash是否busy状态:

static int nuc970_check_rb(struct nuc970_nand_info *nand)
{
    unsigned int val;

    ENTER();
    spin_lock(&nand->lock);
    val = readl(REG_SMISR) & READYBUSY;
    spin_unlock(&nand->lock);
    LEAVE();

    return val;
}

static int nuc970_nand_devready(struct mtd_info *mtd)
{
    struct nuc970_nand_info *nand;
    int ready;

    ENTER() ;

    if ( nuc970_wait_sem(mtd) < 0 )
        return -1;

    nand = container_of(mtd, struct nuc970_nand_info, mtd);
    ready = (nuc970_check_rb(nand)) ? 1 : 0;
    nuc970_release_sem(mtd);

    LEAVE();
    return ready;
}

命令操作接口,对应ONFI中的标准,这里特别强调下命令操作的步骤,例如读数据操作,内容如下:第一次是0x00h,第二次是0x30h,而两次命令中间,需要发送对应的你所要读取的页的地址,在发送地址截至,接下来调用读数据接口接收数据:

static void nuc970_nand_command_lp(struct mtd_info *mtd, unsigned int command, int column, int page_addr)
{
    register struct nand_chip *chip = mtd->priv;
    struct nuc970_nand_info *nand;

    ENTER() ;

    if ( nuc970_wait_sem(mtd) < 0 ) //获取mtd分区是否加锁?
    {
        LEAVE();
        return;
    }
    nand = container_of(mtd, struct nuc970_nand_info, mtd);

	//页地址有效 && 对应有效的命令 = 选择有效的bch算法
    if ( page_addr != -1 && ( command == NAND_CMD_SEQIN || command == NAND_CMD_READ0 || command == NAND_CMD_READOOB ) )
        nuc970_choice_bch_algo ( mtd, page_addr );

    if (command == NAND_CMD_READOOB) { //如何理解if语句内部???解析如下
        column += mtd->writesize; //oob位于每一页的后面,这里的column表示列地址(nuc970_nand_read_oob_hwecc(...)函数在
        						  //读取oob时,column=0),要读取出对应的oob开始地址为“页+cloumn”
        command = NAND_CMD_READ0;
    }

	//以下部分要理解,需看懂时序,详解《linux_nand_driver.pdf》第20页时序图
    write_cmd_reg(nand, command & 0xff); //启动写命令到nandflash控制器  

    if (column != -1 || page_addr != -1) {
        if (column != -1) {
            write_addr_reg(nand, (column&0xFF) ); //列地址,即页内地址 低8bit
            if ( page_addr != -1 )
                write_addr_reg(nand, (column >> 8) ); //列地址,即页内地址 高8bit
            else
                write_addr_reg(nand, (column >> 8) | ENDADDR); //ENDADDR, FMI_NANDADDR的第31bit,表明本次的地址结束

        }

        if (page_addr != -1) {
            write_addr_reg(nand, (page_addr&0xFF) ); //页地址,具体可以定位到哪一页

            if ( chip->chipsize > (128 << 20) ) {
                write_addr_reg(nand, (page_addr >> 8)&0xFF );
                write_addr_reg(nand, ((page_addr >> 16)&0xFF)|ENDADDR );
            } else {
                write_addr_reg(nand, ((page_addr >> 8)&0xFF)|ENDADDR );
            }
        }
    }

    switch (command) {
    case NAND_CMD_ERASE1: //case语句成立,就表示当前命令执行完,直接释放锁,退出即可!
    case NAND_CMD_ERASE2:
    case NAND_CMD_CACHEDPROG:
    case NAND_CMD_PAGEPROG:
    case NAND_CMD_SEQIN:
    case NAND_CMD_RNDIN:
    case NAND_CMD_STATUS:
        LEAVE();
        nuc970_release_sem(mtd);
        return;

    case NAND_CMD_RESET: //待分析工作原理???
        if (chip->dev_ready)
            break;

        if ( chip->chip_delay )
            udelay(chip->chip_delay);

        write_cmd_reg(nand, NAND_CMD_STATUS);
        write_cmd_reg(nand, command);

        while (!nuc970_check_rb(nand)) ;

        LEAVE();
        nuc970_release_sem(mtd);
        return;

    case NAND_CMD_RNDOUT: //待分析工作原理???
        write_cmd_reg(nand, NAND_CMD_RNDOUTSTART); //启动随机读取,外面调用该nuc970_nand_command_lp(...)函数,待该函数返回会有相应的接口读取数据
        nuc970_release_sem(mtd);
        LEAVE();
        return;

    case NAND_CMD_READ0: //read0命令
        write_cmd_reg(nand, NAND_CMD_READSTART); //见《linux_nand_driver.pdf》第18页,内容如下:“第一次是0x00h,第二次是0x30h,而两次命令中间,需要发送对应的你所要
												 //读取的页的地址...”,外面调用该nuc970_nand_command_lp(...)函数,待该函数返回会有相应的接口读取数据
        break;
    default:
        if (!chip->dev_ready) {
            if ( chip->chip_delay )
                udelay(chip->chip_delay);
            LEAVE();
            nuc970_release_sem(mtd);
            return;
        }
    }

    while (!nuc970_check_rb(nand)) ;

    nuc970_release_sem(mtd);
    LEAVE();
}

nandflash硬件ECC写操作:

static void nuc970_nand_write_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, const uint8_t *buf, int oob_required)
{
    uint8_t *ecc_calc = chip->buffers->ecccalc;
    uint32_t hweccbytes=chip->ecc.layout->eccbytes;
    register char * ptr=REG_SMRA0;

    ENTER();

    if ( nuc970_wait_sem(mtd) < 0 )
    {
        LEAVE();
        return ;
    }

    memset ( (void*)ptr, 0xFF, mtd->oobsize );
    memcpy ( (void*)ptr, (void*)chip->oob_poi,  mtd->oobsize - chip->ecc.total );

    _nuc970_nand_dma_transfer( mtd, buf, mtd->writesize , 0x1);

    // Copy parity code in SMRA to calc
    memcpy ( (void*)ecc_calc,  (void*)( REG_SMRA0 + ( mtd->oobsize - chip->ecc.total ) ), chip->ecc.total );

    // Copy parity code in calc to oob_poi
    memcpy ( (void*)(chip->oob_poi+hweccbytes), (void*)ecc_calc, chip->ecc.total);

    nuc970_release_sem(mtd);

    LEAVE();
}

硬件ecc,先读取oob,确定ecc是否出错,纠错正常才读取数据:

static int nuc970_nand_read_page_hwecc_oob_first(struct mtd_info *mtd, struct nand_chip *chip, uint8_t *buf, int oob_required, int page)
{
    int eccsize = chip->ecc.size;
    uint8_t *p = buf;
    char * ptr=REG_SMRA0;

    ENTER();

    if ( nuc970_wait_sem(mtd) < 0 )
    {
        LEAVE();
        return -1;
    }
	//printk("smcsr 0x%x\n", readl(REG_SMCSR));
    //nuc970_choice_bch_algo ( mtd, page );

    /* At first, read the OOB area  */
    nuc970_nand_command_lp(mtd, NAND_CMD_READOOB, 0, page);
    nuc970_nand_read_buf(mtd, chip->oob_poi, mtd->oobsize);

    // Second, copy OOB data to SMRA for page read
    memcpy ( (void*)ptr, (void*)chip->oob_poi, mtd->oobsize );

    // Third, read data from nand
    nuc970_nand_command_lp(mtd, NAND_CMD_READ0, 0, page);
    _nuc970_nand_dma_transfer(mtd, p, eccsize, 0x0);

    // Fouth, restore OOB data from SMRA
    memcpy ( (void*)chip->oob_poi, (void*)ptr, mtd->oobsize );

    nuc970_release_sem(mtd);

    LEAVE();

    return 0;
}

oob(out-of-band)参数配置:

static void nuc970_layout_oob_table ( struct nand_ecclayout* pNandOOBTbl, int oobsize , int eccbytes )
{
	//ecc使用字节数
    pNandOOBTbl->eccbytes = eccbytes; 

	//oob中多余的字节数 = oob总空间 - DEF_RESERVER_OOB_SIZE_FOR_MARKER - ecc占用的字节数
    pNandOOBTbl->oobavail = oobsize - DEF_RESERVER_OOB_SIZE_FOR_MARKER - eccbytes ; 

	//下面我的理解是,坏块标记,至于如何使用,待分析????
	pNandOOBTbl->oobfree[0].offset = DEF_RESERVER_OOB_SIZE_FOR_MARKER;  // Bad block marker size
    pNandOOBTbl->oobfree[0].length = oobsize - eccbytes - pNandOOBTbl->oobfree[0].offset ;
}

硬件ecc读取oob,这里多说一句,oob读取流程[命令 + 地址(列地址+行地址) + 等待nandflash忙 + 开始数据读取]
a. 发送读取命令NAND_CMD_READOOB,读取oob,实际上在ONFI协议中并没有NAND_CMD_READOOB命令,在函数nuc970_nand_command_lp(...)内部进行了转换为读命令NAND_CMD_READ0,oob的偏移位置为“每一页的尾部”,所以该函数的形参中column=0;  
b. 读取数据, nuc970_nand_read_buf(...):

static int nuc970_nand_read_oob_hwecc(struct mtd_info *mtd, struct nand_chip *chip, int page)
{
    char * ptr=REG_SMRA0;

    ENTER();

    if ( nuc970_wait_sem(mtd) < 0 )
    {
        LEAVE();
        return -1;
    }
	
    nuc970_nand_command_lp(mtd, NAND_CMD_READOOB, 0, page);
//    nuc970_choice_bch_algo ( mtd, page );

    nuc970_nand_read_buf(mtd, &chip->oob_poi[0], mtd->oobsize);

    // Second, copy OOB data to SMRA for page read
    memcpy ( (void*)ptr, (void*)chip->oob_poi, mtd->oobsize );

    // Third, read data from nand
    nuc970_nand_command_lp(mtd, NAND_CMD_READ0, 0, page);
    _nuc970_nand_dma_transfer(mtd, NULL, mtd->writesize, 0x0);

    // Fouth, recovery OOB data for SMRA
    memcpy ( (void*)chip->oob_poi, (void*)ptr, mtd->oobsize );

    nuc970_release_sem(mtd);

    return 0;
}

按顺序写数据到nandflash:

static int nuc970_nand_write_page(struct mtd_info *mtd, struct nand_chip *chip, uint32_t offset, int data_len, 
                                  const uint8_t *buf, int oob_required, int page, int cached, int raw)
{
    int status;

    if ( nuc970_wait_sem(mtd) < 0 )
    {
        LEAVE();
        return -1;
    }

    nuc970_nand_command_lp(mtd, NAND_CMD_SEQIN, 0x00, page);

    if (unlikely(raw))
        chip->ecc.write_page_raw(mtd, chip, buf, 0);
    else
    {
        if ( page >= 0 && page < (1<<(chip->phys_erase_shift+2)) / mtd->writesize ) // four blocks
        {
            // Special pattern
            char * ptr=REG_SMRA0;
            memset ( (void*)ptr, 0xFF, mtd->oobsize );
            ptr[3] = 0x00;
            ptr[2] = 0x00;
            ptr[1] = 0xFF;
            _nuc970_nand_dma_transfer( mtd, buf, mtd->writesize , 0x1 );
        }
        else
        {
            nuc970_nand_write_page_hwecc ( mtd, chip, buf, oob_required );
        }
    }

    /*
     * Cached progamming disabled for now, Not sure if its worth the
     * trouble. The speed gain is not very impressive. (2.3->2.6Mib/s)
     */
    cached = 0;

    if (!cached || !(chip->options & NAND_CACHEPRG)) {
        nuc970_nand_command_lp(mtd, NAND_CMD_PAGEPROG, -1, -1);
        status = chip->waitfunc(mtd, chip);
        /*
         * See if operation failed and additional status checks are
         * available
         */
        if ((status & NAND_STATUS_FAIL) && (chip->errstat))
            status = chip->errstat(mtd, chip, FL_WRITING, status, page);

        if (status & NAND_STATUS_FAIL)
        {
            nuc970_release_sem(mtd);
            return -EIO;
        }
    }
    else {
        nuc970_nand_command_lp(mtd, NAND_CMD_CACHEDPROG, -1, -1);
        status = chip->waitfunc(mtd, chip);
    }

#if 0 // for verify
    /* Send command to read back the data */
    nuc970_nand_command_lp(mtd, NAND_CMD_READ0, 0, page);

    if (nuc970_verify_buf(mtd, buf, mtd->writesize))
    {
        nuc970_release_sem(mtd);
        return -EIO;
    }
#endif

    nuc970_release_sem(mtd);
    return 0;
}
对底层硬件操作的函数调用分析完了,接下来正真开始驱动注册:
static int nuc970_nand_probe(struct platform_device *pdev)
{
    struct nand_chip *chip;
    struct nuc970_nand_info *nuc970_nand;
    struct mtd_info *mtd;
    struct pinctrl *p;

    int retval=0;
    E_PAGESIZE ePageSize;

    ENTER() ;

    nuc970_nand = devm_kzalloc(&pdev->dev, sizeof(struct nuc970_nand_info), GFP_KERNEL);
    if (!nuc970_nand)
        return -ENOMEM;

	//分配虚拟地址
    nuc970_nand->pnand_vaddr = (unsigned char *) dma_alloc_writecombine(NULL, 512*16, (dma_addr_t *)&nuc970_nand->pnand_phyaddr, GFP_KERNEL);
    if(nuc970_nand->pnand_vaddr == NULL){
        printk(KERN_ERR "nuc970_nand: failed to allocate ram for nand data.\n");
        return -ENOMEM;
    }

    platform_set_drvdata(pdev, nuc970_nand); //将nuc970_nand绑定为pdev的私有数据!

    spin_lock_init(&nuc970_nand->controller.lock);
    init_waitqueue_head(&nuc970_nand->controller.wq);

    nuc970_nand->pdev = pdev;

	//这里有个疑问是mtd包含chip???我猜mtd应该是对应上层(如nandflash_erase命令就是和MTD相关),chip是对底层硬件操作
	
    mtd = &nuc970_nand->mtd; //分区操作结构体
    chip = &(nuc970_nand->chip); //芯片操作结构体

    nuc970_nand->mtd.priv   = chip; //将chip绑定到mtd的私有成员
    nuc970_nand->mtd.owner  = THIS_MODULE;
    spin_lock_init(&nuc970_nand->lock);

    /*
     * Get Clock
     */
    nuc970_nand->fmi_clk = clk_get(NULL, "fmi_hclk");
    if (IS_ERR(nuc970_nand->fmi_clk)) {
        printk("no fmi_clk?\n");
        retval = -ENXIO;
        goto fail1;
    }

    nuc970_nand->clk = clk_get(NULL, "nand_hclk");
    if (IS_ERR(nuc970_nand->clk)) {
        printk("no nand_clk?\n");
        goto fail2;
    }

    clk_prepare(nuc970_nand->fmi_clk);
    clk_enable(nuc970_nand->fmi_clk);
    clk_prepare(nuc970_nand->clk);
    clk_enable(nuc970_nand->clk);

    nuc970_nand->chip.controller = &nuc970_nand->controller;

	//绑定chip的接口操作函数,即chip对应底层硬件的操作方式
    chip->cmdfunc     = nuc970_nand_command_lp; //nandflash命令操作
    chip->read_byte   = nuc970_nand_read_byte; //读字节
    chip->select_chip = nuc970_nand_select_chip; //chip片选
    chip->read_buf  = nuc970_read_buf_dma; //从DMA中读取nandflash数据
    chip->write_buf = nuc970_write_buf_dma; //通过DMA写数据到nandflash

    // Check NAND device NBUSY0 pin
    chip->dev_ready     = nuc970_nand_devready; //读nandflash控制器是否busy
    /* set up nand options */
    chip->bbt_options = NAND_BBT_USE_FLASH | NAND_BBT_NO_OOB;
    //chip->options     |= NAND_SKIP_BBTSCAN;

    nuc970_nand->reg    = 0x00;

    // Read OOB data first, then HW read page
    chip->write_page    = nuc970_nand_write_page; //页写操作
    chip->ecc.mode      = NAND_ECC_HW_OOB_FIRST; //???
    chip->ecc.hwctl     = nuc970_nand_enable_hwecc; //使能硬件ECC
    chip->ecc.calculate = nuc970_nand_calculate_ecc; //计算ECC
    chip->ecc.correct   = nuc970_nand_correct_data; //纠正数据
    chip->ecc.write_page= nuc970_nand_write_page_hwecc; //硬件ECC页写操作
    chip->ecc.read_page = nuc970_nand_read_page_hwecc_oob_first; //读页数据前先读取oob(out of bound)
    chip->ecc.read_oob  = nuc970_nand_read_oob_hwecc; //读取数据
    chip->ecc.layout    = &nuc970_nand_oob;

	//配置gpio管脚为nandflash功能
#if defined (CONFIG_NUC970_NAND_PC)
    p = devm_pinctrl_get_select(&pdev->dev, "nand-PC");

#elif defined (CONFIG_NUC970_NAND_PI)
    p = devm_pinctrl_get_select(&pdev->dev, "nand-PI");
#endif
    if (IS_ERR(p))
    {
        dev_err(&pdev->dev, "unable to reserve pin\n");
        retval = PTR_ERR(p);
    }
    nuc970_nand_initialize( );

    nuc970_nand->m_i32MyShowTime = 0;

    /* first scan to find the device and get the page size */
    if (nand_scan_ident(&(nuc970_nand->mtd), 1, NULL)) {
        retval = -ENXIO;
        goto fail3;
    }

    //Set PSize bits of SMCSR register to select NAND card page size
    switch (mtd->writesize) { //每一页的大小,我使用的是2K,而计算ecc是每512字节计算一个oob,所以这里有四个
        case 2048:
            writel( (readl(REG_SMCSR)&(~0x30000)) + 0x10000, REG_SMCSR);
            ePageSize = ePageSize_2048;
            break;

        case 4096:
            writel( (readl(REG_SMCSR)&(~0x30000)) + 0x20000, REG_SMCSR);
            ePageSize = ePageSize_4096;
            break;

        case 8192:
            writel( (readl(REG_SMCSR)&(~0x30000)) + 0x30000, REG_SMCSR);
            ePageSize = ePageSize_8192;
            break;

        /* Not support now. */
        case 512:
            //writel( (readl(REG_SMCSR)&(~0x30000)) + 0, REG_SMCSR);
            //ePageSize = ePageSize_512;
            //break;

        default:
            printk("NUC970 NAND CONTROLLER IS NOT SUPPORT THE PAGE SIZE. (%d, %d)\n", mtd->writesize, mtd->oobsize );
            goto fail3;
    }
    nuc970_nand->m_ePageSize = ePageSize;
    {
        // System area
        int i32eBCHAlgo = 0;
        switch(ePageSize) {
            case 0:
            case 1: i32eBCHAlgo = eBCH_T4;  break;
            case 2: i32eBCHAlgo = eBCH_T8;  break;
            case 3: i32eBCHAlgo = eBCH_T12; break;
            default:
                break;
        }
        nuc970_nand_SYSTEM_oob.m_BCHAlgoidx = i32eBCHAlgo;
        nuc970_nand_SYSTEM_oob.m_SMRASize   = g_SYSAREA_NAND_EXTRA_SIZE[ePageSize];
        nuc970_layout_oob_table ( (struct nand_ecclayout*)&nuc970_nand_SYSTEM_oob, nuc970_nand_SYSTEM_oob.m_SMRASize, g_i32ParityNum[ePageSize][i32eBCHAlgo] );
        printk("SYSTEM: USE %s HWECC algorithm(SMRA size: %d, Parity number:%d bytes)\n",
                g_pcBCHAlgoIdx[i32eBCHAlgo],nuc970_nand_SYSTEM_oob.m_SMRASize,g_i32ParityNum[ePageSize][i32eBCHAlgo] );

		//chp   i32eBCHAlgo = nuc970_hwecc_choice_bch_algorithm ( ePageSize, mtd->oobsize, 0 );
        i32eBCHAlgo = nuc970_nand_SYSTEM_oob.m_BCHAlgoidx;
        nuc970_nand_EXECUTE_oob.m_BCHAlgoidx = i32eBCHAlgo;

        if ( ePageSize==3 && mtd->oobsize >= g_SYSAREA_NAND_EXTRA_SIZE[ePageSize] ) // 8K, oob >= 376Byte
        {
            nuc970_nand_EXECUTE_oob.m_SMRASize  = g_SYSAREA_NAND_EXTRA_SIZE[ePageSize];
            mtd->oobsize                = nuc970_nand_EXECUTE_oob.m_SMRASize;
        }
        else if ( ePageSize==1 && mtd->oobsize >= g_SYSAREA_NAND_EXTRA_SIZE[ePageSize] ) // 2K, oob >= 64Byte
        {
            nuc970_nand_EXECUTE_oob.m_SMRASize  = g_SYSAREA_NAND_EXTRA_SIZE[ePageSize];
            mtd->oobsize                = nuc970_nand_EXECUTE_oob.m_SMRASize;
        }
        else
            nuc970_nand_EXECUTE_oob.m_SMRASize  = mtd->oobsize;

        if ( i32eBCHAlgo >= 0 ) {
            nuc970_layout_oob_table ( (struct nand_ecclayout*)&nuc970_nand_EXECUTE_oob, nuc970_nand_EXECUTE_oob.m_SMRASize, g_i32ParityNum[ePageSize][i32eBCHAlgo] );
            printk("EXECUTE: USE %s HWECC algorithm(SMRA size: %d, Parity number:%d bytes)\n",
                    g_pcBCHAlgoIdx[i32eBCHAlgo], nuc970_nand_EXECUTE_oob.m_SMRASize, g_i32ParityNum[ePageSize][i32eBCHAlgo] );
        }
    }

#ifndef CONFIG_MTD_CMDLINE_PARTS
    nuc970_nand->parts = (struct mtd_partition*)partitions;
    nuc970_nand->nr_parts = ARRAY_SIZE(partitions);
#endif

    nuc970_nand->m_i32SMRASize  = mtd->oobsize;
	//chp    nuc970_nand->eBCHAlgo = nuc970_hwecc_choice_bch_algorithm ( ePageSize,   mtd->oobsize,  0 );
    nuc970_nand->eBCHAlgo = nuc970_nand_SYSTEM_oob.m_BCHAlgoidx;
    if ( nuc970_nand->eBCHAlgo < 0 ) {
        nuc970_layout_oob_table ( &nuc970_nand_oob, mtd->oobsize, 0 );
        chip->ecc.bytes = 0;
        chip->ecc.size  = mtd->writesize;
        printk("Choice BCH algorithm: No match.\n");
    } else {
        nuc970_layout_oob_table ( &nuc970_nand_oob, mtd->oobsize, g_i32ParityNum[ePageSize][nuc970_nand->eBCHAlgo] );
        chip->ecc.bytes = nuc970_nand_oob.eccbytes;
        chip->ecc.size  = mtd->writesize;
        printk("USE %s HWECC algorithm(Parity number:%d bytes)\n", g_pcBCHAlgoIdx[nuc970_nand->eBCHAlgo],  g_i32ParityNum[ePageSize][nuc970_nand->eBCHAlgo] );
    }

    /* set BCH Tn */
    switch (nuc970_nand->eBCHAlgo)
    {
        case eBCH_T4:
            chip->ecc.strength  = 4;
            break;
        case eBCH_T8:
            chip->ecc.strength  = 8;
            break;
        case eBCH_T12:
            chip->ecc.strength  = 12;
            break;
        case eBCH_T15:
            chip->ecc.strength  = 15;
            break;
        case eBCH_T24:
            chip->ecc.strength  = 24;
            break;
        default:
            ;
    }

    /* add mtd-id. The string should same as uboot definition */
    mtd->name = "nand0";

    /* second phase scan */
    if ( nand_scan_tail( &(nuc970_nand->mtd) ) ) {
        retval = -ENXIO;
        goto fail3;
    }

    if ( nuc970_nand->eBCHAlgo >= 0 )
        nuc970_nand_hwecc_init (&(nuc970_nand->mtd));
    else
        nuc970_nand_hwecc_fini (&(nuc970_nand->mtd));

    dump_chip_info( chip );

    /* First look for RedBoot table or partitions on the command
     * line, these take precedence over device tree information */
    mtd_device_parse_register(&(nuc970_nand->mtd), NULL, NULL, nuc970_nand->parts, nuc970_nand->nr_parts);

    LEAVE();

    dump_regs(__LINE__);

    printk("fmi-sm: registered successfully! mtdid=%s\n", mtd->name);
    return retval;
fail3:
fail2:
fail1:  kfree(nuc970_nand);
    return retval;
}

probe(...)探测函数内部已基本有注释,现在主要分析几个重要的的函数,如下,

nandflash扫描识别,包括厂商ID,芯片ID,是否符合ONFI标准....

    if (nand_scan_ident(&(nuc970_nand->mtd), 1, NULL)) {
        retval = -ENXIO;
        goto fail3;
    }
int nand_scan_ident(struct mtd_info *mtd, int maxchips,
		    struct nand_flash_dev *table)
{
	int i, busw, nand_maf_id, nand_dev_id;
	struct nand_chip *chip = mtd->priv;
	struct nand_flash_dev *type;

	/* Get buswidth to select the correct functions */
	busw = chip->options & NAND_BUSWIDTH_16;
	/* Set the default functions */
	nand_set_defaults(chip, busw); //设置默认函数接口,包括坏块扫描函数 

	/* Read the flash type */
	//读取flash的类型,包括厂商ID,设备ID
	type = nand_get_flash_type(mtd, chip, busw,
				&nand_maf_id, &nand_dev_id, table);

	if (IS_ERR(type)) {
		if (!(chip->options & NAND_SCAN_SILENT_NODEV))
			pr_warn("No NAND device found\n");
		chip->select_chip(mtd, -1);
		return PTR_ERR(type);
	}

	chip->select_chip(mtd, -1);

	/* Check for a chip array */
	for (i = 1; i < maxchips; i++) {
		//片选使能
		chip->select_chip(mtd, i); 

		//复位nandflash
		/* See comment in nand_get_flash_type for reset */
		chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);

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

		//读取厂商ID和设备ID
		/* Read manufacturer and device IDs */
		if (nand_maf_id != chip->read_byte(mtd) ||
		    nand_dev_id != chip->read_byte(mtd)) {
			chip->select_chip(mtd, -1); //片选禁止
			break;
		}
		chip->select_chip(mtd, -1);
	}
	if (i > 1)
		pr_info("%d NAND chips detected\n", i);

	//保存当前片选的nandflash
	/* Store the number of chips and calc total size for mtd */
	chip->numchips = i;
	mtd->size = i * chip->chipsize;

	return 0;
}

nandflash默认接口设置,函数形参 @busw决定了外围总线宽度是8位还是16位,我这里是8位,默认设置的函数我们在probe()探测函数内部绑定,这里主要分析坏块扫描接口绑定:

static void nand_set_defaults(struct nand_chip *chip, int busw)
{
	/* check for proper chip_delay setup, set 20us if not */
	if (!chip->chip_delay)
		chip->chip_delay = 20;

	/* check, if a user supplied command function given */
	if (chip->cmdfunc == NULL)
		chip->cmdfunc = nand_command;

	/* check, if a user supplied wait function given */
	if (chip->waitfunc == NULL)
		chip->waitfunc = nand_wait;

	if (!chip->select_chip)
		chip->select_chip = nand_select_chip;

	/* If called twice, pointers that depend on busw may need to be reset */
	if (!chip->read_byte || chip->read_byte == nand_read_byte)
		chip->read_byte = busw ? nand_read_byte16 : nand_read_byte;
	if (!chip->read_word)
		chip->read_word = nand_read_word;
	if (!chip->block_bad)
		chip->block_bad = nand_block_bad;
	if (!chip->block_markbad)
		chip->block_markbad = nand_default_block_markbad;
	if (!chip->write_buf || chip->write_buf == nand_write_buf)
		chip->write_buf = busw ? nand_write_buf16 : nand_write_buf;
	if (!chip->read_buf || chip->read_buf == nand_read_buf)
		chip->read_buf = busw ? nand_read_buf16 : nand_read_buf;
	if (!chip->scan_bbt)
		chip->scan_bbt = nand_default_bbt; //绑定坏块扫描函数

	if (!chip->controller) {
		chip->controller = &chip->hwcontrol;
		spin_lock_init(&chip->controller->lock);
		init_waitqueue_head(&chip->controller->wq);
	}

}

先看下坏块表的结构体:

struct nand_bbt_descr {
	int options; //选项,具体见赋值
	int pages[NAND_MAX_CHIPS]; //nandflash芯片个数,我们这里为1块
	int offs; //坏块标记存放在oob区域,offs是相对oob的偏移位置
	int veroffs; 
	uint8_t version[NAND_MAX_CHIPS];
	int len; //坏块标记占用的字节数
	int maxblocks;
	int reserved_block_code;
	uint8_t *pattern; //如果坏块标记的是0xff,则说明这个block是好的
};

nandflash默认坏块表:

int nand_default_bbt(struct mtd_info *mtd)
{
	struct nand_chip *this = mtd->priv;

	/* Is a flash based bad block table requested? */
	if (this->bbt_options & NAND_BBT_USE_FLASH) {
		/* Use the default pattern descriptors */
		if (!this->bbt_td) {
			if (this->bbt_options & NAND_BBT_NO_OOB) {
				this->bbt_td = &bbt_main_no_oob_descr;
				this->bbt_md = &bbt_mirror_no_oob_descr;
			} else {
				this->bbt_td = &bbt_main_descr;
				this->bbt_md = &bbt_mirror_descr;
			}
		}
	} else {
		this->bbt_td = NULL;
		this->bbt_md = NULL;
	}

	if (!this->badblock_pattern)
		nand_create_badblock_pattern(this); //建立一个bad block坏块表模式

	return nand_scan_bbt(mtd, this->badblock_pattern); //扫描坏块表
}

建立一个坏块表模版:

static int nand_create_badblock_pattern(struct nand_chip *this)
{
	struct nand_bbt_descr *bd;
	if (this->badblock_pattern) {
		pr_warn("Bad block pattern already allocated; not replacing\n");
		return -EINVAL;
	}
	bd = kzalloc(sizeof(*bd), GFP_KERNEL);
	if (!bd)
		return -ENOMEM;
	bd->options = this->bbt_options & BADBLOCK_SCAN_MASK; //坏块扫描
	bd->offs = this->badblockpos; //坏块标记存放在每个page的oob里面
	bd->len = (this->options & NAND_BUSWIDTH_16) ? 2 : 1; //根据总线的宽度决定坏块标记在oob的字节数
	bd->pattern = scan_ff_pattern; //如果坏块标记的是0xff,则说明这个block是好的
	bd->options |= NAND_BBT_DYNAMICSTRUCT; //表明bbt是动态结构体
	this->badblock_pattern = bd;
	return 0;
}

扫描坏块,以下函数说明已经很详细了,就是如果flash已经存在bbt了,就读取到内存中,如果没有,就扫描全部flash芯片建立bbt;那最初的bbt从何而来?2种来源,一种是bootloader启动的时候,创建了bbt并保存到flash中;另外就是kernel第一次启动的时候如果没有找到合法的bbt就扫描flash并建立bbt

int nand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd)
{
	struct nand_chip *this = mtd->priv;
	int len, res = 0;
	uint8_t *buf;
	struct nand_bbt_descr *td = this->bbt_td;
	struct nand_bbt_descr *md = this->bbt_md;

	len = mtd->size >> (this->bbt_erase_shift + 2);
	/*
	 * Allocate memory (2bit per block) and clear the memory bad block
	 * table.
	 */
	this->bbt = kzalloc(len, GFP_KERNEL);
	if (!this->bbt)
		return -ENOMEM;

	/*
	 * If no primary table decriptor is given, scan the device to build a
	 * memory based bad block table.
	 */
	if (!td) {
		if ((res = nand_memory_bbt(mtd, bd))) {
			pr_err("nand_bbt: can't scan flash and build the RAM-based BBT\n");
			kfree(this->bbt);
			this->bbt = NULL;
		}
		return res;
	}
	verify_bbt_descr(mtd, td);
	verify_bbt_descr(mtd, md);

	/* Allocate a temporary buffer for one eraseblock incl. oob */
	len = (1 << this->bbt_erase_shift);
	len += (len >> this->page_shift) * mtd->oobsize;
	buf = vmalloc(len);
	if (!buf) {
		kfree(this->bbt);
		this->bbt = NULL;
		return -ENOMEM;
	}

	/* Is the bbt at a given page? */
	if (td->options & NAND_BBT_ABSPAGE) { //
		read_abs_bbts(mtd, buf, td, md);
	} else {
		/* Search the bad block table using a pattern in oob */
		search_read_bbts(mtd, buf, td, md);
	}

	res = check_create(mtd, buf, bd);

	/* Prevent the bbt regions from erasing / writing */
	mark_bbt_region(mtd, td);
	if (md)
		mark_bbt_region(mtd, md);

	vfree(buf);
	return res;
}

搜索坏块表:

static void search_read_bbts(struct mtd_info *mtd, uint8_t *buf,
			     struct nand_bbt_descr *td,
			     struct nand_bbt_descr *md)
{
	/* Search the primary table */
	search_bbt(mtd, buf, td); //扫描主要的表

	/* Search the mirror table */
	if (md)
		search_bbt(mtd, buf, md); //扫描镜像表
}
static int search_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *td)
{
	struct nand_chip *this = mtd->priv;
	int i, chips;
	int bits, startblock, block, dir;
	int scanlen = mtd->writesize + mtd->oobsize; //数据区+oob区
	int bbtblocks;
	int blocktopage = this->bbt_erase_shift - this->page_shift;

	/* Search direction top -> down? */
	if (td->options & NAND_BBT_LASTBLOCK) {
		startblock = (mtd->size >> this->bbt_erase_shift) - 1;
		dir = -1;
	} else {
		startblock = 0;
		dir = 1;
	}

	/* Do we have a bbt per chip? */
	if (td->options & NAND_BBT_PERCHIP) {
		chips = this->numchips;
		bbtblocks = this->chipsize >> this->bbt_erase_shift;
		startblock &= bbtblocks - 1;
	} else {
		chips = 1;
		bbtblocks = mtd->size >> this->bbt_erase_shift;
	}

	/* Number of bits for each erase block in the bbt */
	bits = td->options & NAND_BBT_NRBITS_MSK;

	for (i = 0; i < chips; i++) {
		/* Reset version information */
		td->version[i] = 0;
		td->pages[i] = -1;
		/* Scan the maximum number of blocks */
		for (block = 0; block < td->maxblocks; block++) { //遍历chip的全部blocks

			int actblock = startblock + dir * block; //计算哪个block
			loff_t offs = (loff_t)actblock << this->bbt_erase_shift; //计算block在nandflash中的的偏移

			/* Read first page */
			scan_read(mtd, buf, offs, mtd->writesize, td);
			if (!check_pattern(buf, scanlen, mtd->writesize, td)) { //校验坏块
				td->pages[i] = actblock << blocktopage; //计算实际有坏块的页
				if (td->options & NAND_BBT_VERSION) {
					offs = bbt_get_ver_offs(mtd, td);
					td->version[i] = buf[offs];
				}
				break;
			}
		}
		startblock += this->chipsize >> this->bbt_erase_shift;
	}
	/* Check, if we found a bbt for each requested chip */
	for (i = 0; i < chips; i++) {
		if (td->pages[i] == -1)
			pr_warn("Bad block table not found for chip %d\n", i);
		else
			pr_info("Bad block table found at page %d, version "
				 "0x%02X\n", td->pages[i], td->version[i]);
	}
	return 0;
}

如果出现坏块,内核在启动时会打印如下的信息:

Bad block table found at page 65472, version 0x01
Bad block table found at page 65408, version 0x01

关于nandflash坏块表,后续专门写一篇文章,待续......

再重新回到nand_scan_ident(...)函数中的nand_get_flash_type(...)获取厂商ID、设备ID:

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

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

	/* Read manufacturer and device IDs */
	*maf_id = chip->read_byte(mtd);
	*dev_id = chip->read_byte(mtd);

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

	/* Read entire ID string */
	for (i = 0; i < 8; i++)
		id_data[i] = chip->read_byte(mtd);

	if (id_data[0] != *maf_id || id_data[1] != *dev_id) {
		pr_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)
		type = nand_flash_ids;

	for (; type->name != NULL; type++) {
		if (is_full_id_nand(type)) {
			if (find_full_id_nand(mtd, chip, type, id_data, &busw))
				goto ident_done;
		} else if (*dev_id == type->dev_id) {
				break;
		}
	}

	chip->onfi_version = 0;
	if (!type->name || !type->pagesize) {
		//探测是否是ONFI标准,如果是就读取chip的大小、页擦除大小、页尺寸....
		/* Check is chip is ONFI compliant */
		if (nand_flash_detect_onfi(mtd, chip, &busw))
			goto ident_done;
	}

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

	if (!mtd->name)
		mtd->name = type->name;

	chip->chipsize = (uint64_t)type->chipsize << 20;

	if (!type->pagesize && chip->init_size) {
		/* Set the pagesize, oobsize, erasesize by the driver */
		busw = chip->init_size(mtd, chip, id_data);
	} else if (!type->pagesize) {
		/* Decode parameters from extended ID */
		nand_decode_ext_id(mtd, chip, id_data, &busw);
	} else {
		nand_decode_id(mtd, chip, type, id_data, &busw);
	}
	/* Get chip options */
	chip->options |= type->options;

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

	/* Try to identify manufacturer */
	for (maf_idx = 0; nand_manuf_ids[maf_idx].id != 0x0; maf_idx++) {
		if (nand_manuf_ids[maf_idx].id == *maf_id)
			break;
	}

	if (chip->options & NAND_BUSWIDTH_AUTO) {
		WARN_ON(chip->options & NAND_BUSWIDTH_16);
		chip->options |= busw;
		nand_set_defaults(chip, busw);
	} else if (busw != (chip->options & NAND_BUSWIDTH_16)) {
		/*
		 * Check, if buswidth is correct. Hardware drivers should set
		 * chip correct!
		 */
		pr_info("NAND device: Manufacturer ID:"
			" 0x%02x, Chip ID: 0x%02x (%s %s)\n", *maf_id,
			*dev_id, nand_manuf_ids[maf_idx].name, mtd->name);
		pr_warn("NAND bus width %d instead %d bit\n",
			   (chip->options & NAND_BUSWIDTH_16) ? 16 : 8,
			   busw ? 16 : 8);
		return ERR_PTR(-EINVAL);
	}

	nand_decode_bbm_options(mtd, chip, id_data);

	/* Calculate the address shift from the page size */
	chip->page_shift = ffs(mtd->writesize) - 1;
	/* Convert chipsize to number of pages per chip -1 */
	chip->pagemask = (chip->chipsize >> chip->page_shift) - 1;

	chip->bbt_erase_shift = chip->phys_erase_shift =
		ffs(mtd->erasesize) - 1;
	if (chip->chipsize & 0xffffffff)
		chip->chip_shift = ffs((unsigned)chip->chipsize) - 1;
	else {
		chip->chip_shift = ffs((unsigned)(chip->chipsize >> 32));
		chip->chip_shift += 32 - 1;
	}

	chip->badblockbits = 8;
	chip->erase_cmd = single_erase_cmd;

	/* Do not replace user supplied command function! */
	if (mtd->writesize > 512 && chip->cmdfunc == nand_command)
		chip->cmdfunc = nand_command_lp;

	pr_info("NAND device: Manufacturer ID: 0x%02x, Chip ID: 0x%02x (%s %s),"
		" %dMiB, page size: %d, OOB size: %d\n",
		*maf_id, *dev_id, nand_manuf_ids[maf_idx].name,
		chip->onfi_version ? chip->onfi_params.model : type->name,
		(int)(chip->chipsize >> 20), mtd->writesize, mtd->oobsize);

	return type;
}

输出的调试信息为:

NAND device: Manufacturer ID: 0x01, Chip ID: 0xf1 (AMD/Spansion S34ML01G2), 128MiB, page size: 2048, OOB size: 64S

nuc970_probe(...)-->nand_scan_tail(...),该函数主要完成一些未初始化的函数,并扫描整个chip的坏块:

int nand_scan_tail(struct mtd_info *mtd)
{
	int i;
	struct nand_chip *chip = mtd->priv;

	/* New bad blocks should be marked in OOB, flash-based BBT, or both */
	BUG_ON((chip->bbt_options & NAND_BBT_NO_OOB_BBM) &&
			!(chip->bbt_options & NAND_BBT_USE_FLASH));

	if (!(chip->options & NAND_OWN_BUFFERS))
		chip->buffers = kmalloc(sizeof(*chip->buffers), GFP_KERNEL);
	if (!chip->buffers)
		return -ENOMEM;

	/* Set the internal oob buffer location, just after the page data */
	chip->oob_poi = chip->buffers->databuf + mtd->writesize;

	/*
	 * If no default placement scheme is given, select an appropriate one.
	 */
	if (!chip->ecc.layout && (chip->ecc.mode != NAND_ECC_SOFT_BCH)) {
		switch (mtd->oobsize) { //oob中的布局
		case 8:
			chip->ecc.layout = &nand_oob_8;
			break;
		case 16:
			chip->ecc.layout = &nand_oob_16;
			break;
		case 64:
			chip->ecc.layout = &nand_oob_64;
			break;
		case 128:
			chip->ecc.layout = &nand_oob_128;
			break;
		default:
			pr_warn("No oob scheme defined for oobsize %d\n",
				   mtd->oobsize);
			BUG();
		}
	}

	if (!chip->write_page)
		chip->write_page = nand_write_page;

	/* set for ONFI nand */
	if (!chip->onfi_set_features)
		chip->onfi_set_features = nand_onfi_set_features;
	if (!chip->onfi_get_features)
		chip->onfi_get_features = nand_onfi_get_features;

	/*
	 * Check ECC mode, default to software if 3byte/512byte hardware ECC is
	 * selected and we have 256 byte pagesize fallback to software ECC
	 */

	switch (chip->ecc.mode) {
	case NAND_ECC_HW_OOB_FIRST:
		/* Similar to NAND_ECC_HW, but a separate read_page handle */
		if (!chip->ecc.calculate || !chip->ecc.correct ||
		     !chip->ecc.hwctl) {
			pr_warn("No ECC functions supplied; "
				   "hardware ECC not possible\n");
			BUG();
		}
		if (!chip->ecc.read_page)
			chip->ecc.read_page = nand_read_page_hwecc_oob_first;

	case NAND_ECC_HW:
		/* Use standard hwecc read page function? */
		if (!chip->ecc.read_page)
			chip->ecc.read_page = nand_read_page_hwecc;
		if (!chip->ecc.write_page)
			chip->ecc.write_page = nand_write_page_hwecc;
		if (!chip->ecc.read_page_raw)
			chip->ecc.read_page_raw = nand_read_page_raw;
		if (!chip->ecc.write_page_raw)
			chip->ecc.write_page_raw = nand_write_page_raw;
		if (!chip->ecc.read_oob)
			chip->ecc.read_oob = nand_read_oob_std;
		if (!chip->ecc.write_oob)
			chip->ecc.write_oob = nand_write_oob_std;
		if (!chip->ecc.read_subpage)
			chip->ecc.read_subpage = nand_read_subpage;
		if (!chip->ecc.write_subpage)
			chip->ecc.write_subpage = nand_write_subpage_hwecc;

	case NAND_ECC_HW_SYNDROME:
		if ((!chip->ecc.calculate || !chip->ecc.correct ||
		     !chip->ecc.hwctl) &&
		    (!chip->ecc.read_page ||
		     chip->ecc.read_page == nand_read_page_hwecc ||
		     !chip->ecc.write_page ||
		     chip->ecc.write_page == nand_write_page_hwecc)) {
			pr_warn("No ECC functions supplied; "
				   "hardware ECC not possible\n");
			BUG();
		}
		/* Use standard syndrome read/write page function? */
		if (!chip->ecc.read_page)
			chip->ecc.read_page = nand_read_page_syndrome;
		if (!chip->ecc.write_page)
			chip->ecc.write_page = nand_write_page_syndrome;
		if (!chip->ecc.read_page_raw)
			chip->ecc.read_page_raw = nand_read_page_raw_syndrome;
		if (!chip->ecc.write_page_raw)
			chip->ecc.write_page_raw = nand_write_page_raw_syndrome;
		if (!chip->ecc.read_oob)
			chip->ecc.read_oob = nand_read_oob_syndrome;
		if (!chip->ecc.write_oob)
			chip->ecc.write_oob = nand_write_oob_syndrome;

		if (mtd->writesize >= chip->ecc.size) {
			if (!chip->ecc.strength) {
				pr_warn("Driver must set ecc.strength when using hardware ECC\n");
				BUG();
			}
			break;
		}
		pr_warn("%d byte HW ECC not possible on "
			   "%d byte page size, fallback to SW ECC\n",
			   chip->ecc.size, mtd->writesize);
		chip->ecc.mode = NAND_ECC_SOFT;

	case NAND_ECC_SOFT:
		chip->ecc.calculate = nand_calculate_ecc;
		chip->ecc.correct = nand_correct_data;
		chip->ecc.read_page = nand_read_page_swecc;
		chip->ecc.read_subpage = nand_read_subpage;
		chip->ecc.write_page = nand_write_page_swecc;
		chip->ecc.read_page_raw = nand_read_page_raw;
		chip->ecc.write_page_raw = nand_write_page_raw;
		chip->ecc.read_oob = nand_read_oob_std;
		chip->ecc.write_oob = nand_write_oob_std;
		if (!chip->ecc.size)
			chip->ecc.size = 256;
		chip->ecc.bytes = 3;
		chip->ecc.strength = 1;
		break;

	case NAND_ECC_SOFT_BCH:
		if (!mtd_nand_has_bch()) {
			pr_warn("CONFIG_MTD_ECC_BCH not enabled\n");
			BUG();
		}
		chip->ecc.calculate = nand_bch_calculate_ecc;
		chip->ecc.correct = nand_bch_correct_data;
		chip->ecc.read_page = nand_read_page_swecc;
		chip->ecc.read_subpage = nand_read_subpage;
		chip->ecc.write_page = nand_write_page_swecc;
		chip->ecc.read_page_raw = nand_read_page_raw;
		chip->ecc.write_page_raw = nand_write_page_raw;
		chip->ecc.read_oob = nand_read_oob_std;
		chip->ecc.write_oob = nand_write_oob_std;
		/*
		 * Board driver should supply ecc.size and ecc.bytes values to
		 * select how many bits are correctable; see nand_bch_init()
		 * for details. Otherwise, default to 4 bits for large page
		 * devices.
		 */
		if (!chip->ecc.size && (mtd->oobsize >= 64)) {
			chip->ecc.size = 512;
			chip->ecc.bytes = 7;
		}
		chip->ecc.priv = nand_bch_init(mtd,
					       chip->ecc.size,
					       chip->ecc.bytes,
					       &chip->ecc.layout);
		if (!chip->ecc.priv) {
			pr_warn("BCH ECC initialization failed!\n");
			BUG();
		}
		chip->ecc.strength =
			chip->ecc.bytes * 8 / fls(8 * chip->ecc.size);
		break;

	case NAND_ECC_NONE:
		pr_warn("NAND_ECC_NONE selected by board driver. "
			   "This is not recommended!\n");
		chip->ecc.read_page = nand_read_page_raw;
		chip->ecc.write_page = nand_write_page_raw;
		chip->ecc.read_oob = nand_read_oob_std;
		chip->ecc.read_page_raw = nand_read_page_raw;
		chip->ecc.write_page_raw = nand_write_page_raw;
		chip->ecc.write_oob = nand_write_oob_std;
		chip->ecc.size = mtd->writesize;
		chip->ecc.bytes = 0;
		chip->ecc.strength = 0;
		break;

	default:
		pr_warn("Invalid NAND_ECC_MODE %d\n", chip->ecc.mode);
		BUG();
	}

	/* For many systems, the standard OOB write also works for raw */
	if (!chip->ecc.read_oob_raw)
		chip->ecc.read_oob_raw = chip->ecc.read_oob;
	if (!chip->ecc.write_oob_raw)
		chip->ecc.write_oob_raw = chip->ecc.write_oob;

	/*
	 * The number of bytes available for a client to place data into
	 * the out of band area.
	 */
	chip->ecc.layout->oobavail = 0;
	for (i = 0; chip->ecc.layout->oobfree[i].length
			&& i < ARRAY_SIZE(chip->ecc.layout->oobfree); i++)
		chip->ecc.layout->oobavail +=
			chip->ecc.layout->oobfree[i].length;
	mtd->oobavail = chip->ecc.layout->oobavail;

	/*
	 * Set the number of read / write steps for one page depending on ECC
	 * mode.
	 */
	chip->ecc.steps = mtd->writesize / chip->ecc.size;
	if (chip->ecc.steps * chip->ecc.size != mtd->writesize) {
		pr_warn("Invalid ECC parameters\n");
		BUG();
	}
	chip->ecc.total = chip->ecc.steps * chip->ecc.bytes;

	/* Allow subpage writes up to ecc.steps. Not possible for MLC flash */
	if (!(chip->options & NAND_NO_SUBPAGE_WRITE) &&
	    !(chip->cellinfo & NAND_CI_CELLTYPE_MSK)) {
		switch (chip->ecc.steps) {
		case 2:
			mtd->subpage_sft = 1;
			break;
		case 4:
		case 8:
		case 16:
			mtd->subpage_sft = 2;
			break;
		}
	}
	chip->subpagesize = mtd->writesize >> mtd->subpage_sft;

	/* Initialize state */
	chip->state = FL_READY;

	/* Invalidate the pagebuffer reference */
	chip->pagebuf = -1;

	/* Large page NAND with SOFT_ECC should support subpage reads */
	if ((chip->ecc.mode == NAND_ECC_SOFT) && (chip->page_shift > 9))
		chip->options |= NAND_SUBPAGE_READ;

	/* 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->_panic_write = panic_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->_suspend = nand_suspend;
	mtd->_resume = nand_resume;
	mtd->_block_isbad = nand_block_isbad;
	mtd->_block_markbad = nand_block_markbad;
	mtd->writebufsize = mtd->writesize;

	/* propagate ecc info to mtd_info */
	mtd->ecclayout = chip->ecc.layout;
	mtd->ecc_strength = chip->ecc.strength;
	/*
	 * Initialize bitflip_threshold to its default prior scan_bbt() call.
	 * scan_bbt() might invoke mtd_read(), thus bitflip_threshold must be
	 * properly set.
	 */
	if (!mtd->bitflip_threshold)
		mtd->bitflip_threshold = mtd->ecc_strength;

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

	/* Build bad block table */
	return chip->scan_bbt(mtd); 
}
EXPORT_SYMBOL(nand_scan_tail);
最后调用“chip->scan_bbt(mtd)”扫描坏块、并且建立坏块表,函数内部上面已经分析过,这里不在赘述!

6. 分区表创建

nandflash分区结构体定义:

static struct mtd_partition partitions[] = {
    {
        .name = "u-boot",
        .offset = 0,
        .size = 2 * 1024 * 1024,
        .ecclayout = (struct nand_ecclayout*)&nuc970_nand_SYSTEM_oob
    },
    {
        .name = "Kernel",
        .size = 20 * 1024 * 1024,
        .offset = MTDPART_OFS_APPEND,
        .ecclayout = (struct nand_ecclayout*)&nuc970_nand_EXECUTE_oob
    },
    {
        .name = "user",
        .offset = MTDPART_OFS_APPEND,
        .size = MTDPART_SIZ_FULL
    }
};

分区结构注册:

mtd_device_parse_register(&(nuc970_nand->mtd), NULL, NULL, nuc970_nand->parts, nuc970_nand->nr_parts);
int mtd_device_parse_register(struct mtd_info *mtd, const char * const *types,
			      struct mtd_part_parser_data *parser_data,
			      const struct mtd_partition *parts,
			      int nr_parts)
{
	int err;
	struct mtd_partition *real_parts;

	err = parse_mtd_partitions(mtd, types, &real_parts, parser_data);
	if (err <= 0 && nr_parts && parts) {
		//复制parts分区到内存
		real_parts = kmemdup(parts, sizeof(*parts) * nr_parts,
				     GFP_KERNEL);
		if (!real_parts)
			err = -ENOMEM;
		else
			err = nr_parts; //分区个数
	}

	if (err > 0) { //err > 0条件成立
		err = add_mtd_partitions(mtd, real_parts, err); //增加MTD分区
		kfree(real_parts);
	} else if (err == 0) {
		err = add_mtd_device(mtd); //增加MTD设备,见下面
		if (err == 1)
			err = -ENODEV;
	}

	return err;
}
int add_mtd_partitions(struct mtd_info *master,
		       const struct mtd_partition *parts,
		       int nbparts)
{
	struct mtd_part *slave;
	uint64_t cur_offset = 0;
	int i;

	printk(KERN_NOTICE "Creating %d MTD partitions on \"%s\":\n", nbparts, master->name);

	for (i = 0; i < nbparts; i++) {
		slave = allocate_partition(master, parts + i, i, cur_offset); //分配一个分区,见下面
		if (IS_ERR(slave))
			return PTR_ERR(slave);

		mutex_lock(&mtd_partitions_mutex);
		list_add(&slave->list, &mtd_partitions); //将分区添加到mtd_partitions全局链表中
		mutex_unlock(&mtd_partitions_mutex);

		add_mtd_device(&slave->mtd); //将mtd添加到设备对象中,见下面

		cur_offset = slave->offset + slave->mtd.size;
	}

	return 0;
}

动态分配一个分区allocate_partitions(...),包括函数的对mtd操作的函数初始化:

static struct mtd_part *allocate_partition(struct mtd_info *master,
			const struct mtd_partition *part, int partno,
			uint64_t cur_offset)
{
	struct mtd_part *slave;
	char *name;

	/* allocate the partition structure */
	slave = kzalloc(sizeof(*slave), GFP_KERNEL);
	name = kstrdup(part->name, GFP_KERNEL); //分区名称
	if (!name || !slave) {
		printk(KERN_ERR"memory allocation error while creating partitions for \"%s\"\n",
		       master->name);
		kfree(name);
		kfree(slave);
		return ERR_PTR(-ENOMEM);
	}

	/* set up the MTD object for this partition */
	slave->mtd.type = master->type; //flash类型,如MTD_NORFLASH
	slave->mtd.flags = master->flags & ~part->mask_flags;
	slave->mtd.size = part->size; //分区大小
	slave->mtd.writesize = master->writesize; //分区最小写单元,NOR-FLASH为1个字节,NAND-FLASH为一页
	slave->mtd.writebufsize = master->writebufsize;
	slave->mtd.oobsize = master->oobsize; //OOB大小,包括ECC检验
	slave->mtd.oobavail = master->oobavail;
	slave->mtd.subpage_sft = master->subpage_sft;

	slave->mtd.name = name; //分区名称,如内核“kernel”,文件系统“rootfs”
	slave->mtd.owner = master->owner;
	slave->mtd.backing_dev_info = master->backing_dev_info;

	/* NOTE:  we don't arrange MTDs as a tree; it'd be error-prone
	 * to have the same data be in two different partitions.
	 */
	slave->mtd.dev.parent = master->dev.parent;

	slave->mtd._read = part_read; //分区读
	slave->mtd._write = part_write; //分区写

	if (master->_panic_write)
		slave->mtd._panic_write = part_panic_write; //内核恐慌(崩溃)时写操作

	if (master->_point && master->_unpoint) {
		slave->mtd._point = part_point;
		slave->mtd._unpoint = part_unpoint;
	}

	if (master->_get_unmapped_area)
		slave->mtd._get_unmapped_area = part_get_unmapped_area;
	if (master->_read_oob)
		slave->mtd._read_oob = part_read_oob; //读oob ecc校验
	if (master->_write_oob)
		slave->mtd._write_oob = part_write_oob;
	if (master->_read_user_prot_reg)
		slave->mtd._read_user_prot_reg = part_read_user_prot_reg;
	if (master->_read_fact_prot_reg)
		slave->mtd._read_fact_prot_reg = part_read_fact_prot_reg;
	if (master->_write_user_prot_reg)
		slave->mtd._write_user_prot_reg = part_write_user_prot_reg;
	if (master->_lock_user_prot_reg)
		slave->mtd._lock_user_prot_reg = part_lock_user_prot_reg;
	if (master->_get_user_prot_info)
		slave->mtd._get_user_prot_info = part_get_user_prot_info;
	if (master->_get_fact_prot_info)
		slave->mtd._get_fact_prot_info = part_get_fact_prot_info;
	if (master->_sync)
		slave->mtd._sync = part_sync;
	if (!partno && !master->dev.class && master->_suspend &&
	    master->_resume) {
			slave->mtd._suspend = part_suspend;
			slave->mtd._resume = part_resume;
	}
	if (master->_writev)
		slave->mtd._writev = part_writev;
	if (master->_lock)
		slave->mtd._lock = part_lock;
	if (master->_unlock)
		slave->mtd._unlock = part_unlock;
	if (master->_is_locked)
		slave->mtd._is_locked = part_is_locked;
	if (master->_block_isbad)
		slave->mtd._block_isbad = part_block_isbad;
	if (master->_block_markbad)
		slave->mtd._block_markbad = part_block_markbad;
	slave->mtd._erase = part_erase; //分区擦除
	slave->master = master;
	slave->offset = part->offset;

	if (slave->offset == MTDPART_OFS_APPEND)
		slave->offset = cur_offset;
	if (slave->offset == MTDPART_OFS_NXTBLK) {
		slave->offset = cur_offset;
		if (mtd_mod_by_eb(cur_offset, master) != 0) {
			/* Round up to next erasesize */
			slave->offset = (mtd_div_by_eb(cur_offset, master) + 1) * master->erasesize;
			printk(KERN_NOTICE "Moving partition %d: "
			       "0x%012llx -> 0x%012llx\n", partno,
			       (unsigned long long)cur_offset, (unsigned long long)slave->offset);
		}
	}
	if (slave->offset == MTDPART_OFS_RETAIN) {
		slave->offset = cur_offset;
		if (master->size - slave->offset >= slave->mtd.size) {
			slave->mtd.size = master->size - slave->offset
							- slave->mtd.size;
		} else {
			printk(KERN_ERR "mtd partition \"%s\" doesn't have enough space: %#llx < %#llx, disabled\n",
				part->name, master->size - slave->offset,
				slave->mtd.size);
			/* register to preserve ordering */
			goto out_register;
		}
	}
	if (slave->mtd.size == MTDPART_SIZ_FULL)
		slave->mtd.size = master->size - slave->offset;
        //注意这里内核会打印分区的地址范围
	printk(KERN_NOTICE "0x%012llx-0x%012llx : \"%s\"\n", (unsigned long long)slave->offset,
		(unsigned long long)(slave->offset + slave->mtd.size), slave->mtd.name);

	/* let's do some sanity checks */
	if (slave->offset >= master->size) {
		/* let's register it anyway to preserve ordering */
		slave->offset = 0;
		slave->mtd.size = 0;
		printk(KERN_ERR"mtd: partition \"%s\" is out of reach -- disabled\n",
			part->name);
		goto out_register;
	}
	if (slave->offset + slave->mtd.size > master->size) {
		slave->mtd.size = master->size - slave->offset;
		printk(KERN_WARNING"mtd: partition \"%s\" extends beyond the end of device \"%s\" -- size truncated to %#llx\n",
			part->name, master->name, (unsigned long long)slave->mtd.size);
	}
	if (master->numeraseregions > 1) {
		/* Deal with variable erase size stuff */
		int i, max = master->numeraseregions;
		u64 end = slave->offset + slave->mtd.size;
		struct mtd_erase_region_info *regions = master->eraseregions;

		/* Find the first erase regions which is part of this
		 * partition. */
		for (i = 0; i < max && regions[i].offset <= slave->offset; i++)
			;
		/* The loop searched for the region _behind_ the first one */
		if (i > 0)
			i--;

		/* Pick biggest erasesize */
		for (; i < max && regions[i].offset < end; i++) {
			if (slave->mtd.erasesize < regions[i].erasesize) {
				slave->mtd.erasesize = regions[i].erasesize;
			}
		}
		BUG_ON(slave->mtd.erasesize == 0);
	} else {
		/* Single erase size */
		slave->mtd.erasesize = master->erasesize;
	}

	if ((slave->mtd.flags & MTD_WRITEABLE) &&
	    mtd_mod_by_eb(slave->offset, &slave->mtd)) {
		/* Doesn't start on a boundary of major erase size */
		/* FIXME: Let it be writable if it is on a boundary of
		 * _minor_ erase size though */
		slave->mtd.flags &= ~MTD_WRITEABLE;
		printk(KERN_WARNING"mtd: partition \"%s\" doesn't start on an erase block boundary -- force read-only\n",
			part->name);
	}
	if ((slave->mtd.flags & MTD_WRITEABLE) &&
	    mtd_mod_by_eb(slave->mtd.size, &slave->mtd)) {
		slave->mtd.flags &= ~MTD_WRITEABLE;
		printk(KERN_WARNING"mtd: partition \"%s\" doesn't end on an erase block -- force read-only\n",
			part->name);
	}

	slave->mtd.ecclayout = master->ecclayout;
	slave->mtd.ecc_strength = master->ecc_strength;
	slave->mtd.bitflip_threshold = master->bitflip_threshold;

	if (master->_block_isbad) {
		uint64_t offs = 0;

		while (offs < slave->mtd.size) {
			if (mtd_block_isbad(master, offs + slave->offset))
				slave->mtd.ecc_stats.badblocks++;
			offs += slave->mtd.erasesize;
		}
	}

out_register:
	return slave;
}

上面函数执行完后,会输出如下报文:

Creating 3 MTD partitions on "nand0":
0x000000000000-0x000000200000 : "u-boot"
0x000000200000-0x000000700000 : "kernel"
0x000000700000-0x000008000000 : "rootfs"

增加mtd设备:

int add_mtd_device(struct mtd_info *mtd)
{
	struct mtd_notifier *not;
	int i, error;

	if (!mtd->backing_dev_info) {
		switch (mtd->type) {
		case MTD_RAM:
			mtd->backing_dev_info = &mtd_bdi_rw_mappable;
			break;
		case MTD_ROM:
			mtd->backing_dev_info = &mtd_bdi_ro_mappable;
			break;
		default:
			mtd->backing_dev_info = &mtd_bdi_unmappable;
			break;
		}
	}

	BUG_ON(mtd->writesize == 0);
	mutex_lock(&mtd_table_mutex);

	i = idr_alloc(&mtd_idr, mtd, 0, 0, GFP_KERNEL);
	if (i < 0)
		goto fail_locked;

	mtd->index = i;
	mtd->usecount = 0;

	/* default value if not set by driver */
	if (mtd->bitflip_threshold == 0)
		mtd->bitflip_threshold = mtd->ecc_strength;

	if (is_power_of_2(mtd->erasesize))
		mtd->erasesize_shift = ffs(mtd->erasesize) - 1;
	else
		mtd->erasesize_shift = 0;

	if (is_power_of_2(mtd->writesize))
		mtd->writesize_shift = ffs(mtd->writesize) - 1;
	else
		mtd->writesize_shift = 0;

	mtd->erasesize_mask = (1 << mtd->erasesize_shift) - 1;
	mtd->writesize_mask = (1 << mtd->writesize_shift) - 1;

	/* Some chips always power up locked. Unlock them now */
	if ((mtd->flags & MTD_WRITEABLE) && (mtd->flags & MTD_POWERUP_LOCK)) {
		error = mtd_unlock(mtd, 0, mtd->size);
		if (error && error != -EOPNOTSUPP)
			printk(KERN_WARNING
			       "%s: unlock failed, writes may not work\n",
			       mtd->name);
	}

	/* Caller should have set dev.parent to match the
	 * physical device.
	 */
	mtd->dev.type = &mtd_devtype;
	mtd->dev.class = &mtd_class;
	mtd->dev.devt = MTD_DEVT(i);
	dev_set_name(&mtd->dev, "mtd%d", i);
	dev_set_drvdata(&mtd->dev, mtd);
	if (device_register(&mtd->dev) != 0)
		goto fail_added;

	if (MTD_DEVT(i))
		device_create(&mtd_class, mtd->dev.parent,
			      MTD_DEVT(i) + 1,
			      NULL, "mtd%dro", i);

	pr_debug("mtd: Giving out device %d to %s\n", i, mtd->name);
	/* No need to get a refcount on the module containing
	   the notifier, since we hold the mtd_table_mutex */
	list_for_each_entry(not, &mtd_notifiers, list)
		not->add(mtd);

	mutex_unlock(&mtd_table_mutex);
	/* We _know_ we aren't being removed, because
	   our caller is still holding us here. So none
	   of this try_ nonsense, and no bitching about it
	   either. :) */
	__module_get(THIS_MODULE);
	return 0;

fail_added:
	idr_remove(&mtd_idr, i);
fail_locked:
	mutex_unlock(&mtd_table_mutex);
	return 1;
}

7. 未完成的问题:

1. nandflash bbt表的扫描分析;

2. 文件系统对nandflash访问的层次问题;


可以参考这篇文章:点击打开链接


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值