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,详见:点击打开链接
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_paramsonfi_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;
- };
- 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设备注册。
- 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;
在分析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)”扫描坏块、并且建立坏块表,函数内部上面已经分析过,这里不在赘述!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;
- }
1. nandflash bbt表的扫描分析;
2. 文件系统对nandflash访问的层次问题;
可以参考这篇文章:点击打开链接
联系客服