mtd学习报告003

来源:互联网 发布:java微信开发获取code 编辑:程序博客网 时间:2024/04/27 16:44

 

下面我们就以s3c2410 nand flash为例来讲解如何在flash上划分分区, 及划分分区后的实现原理.

假设我们要在64Mflash上建立四个分区分别用来做不同的共用, 具体如下:

              分区       名称       大小          偏移

         1       bootloader    0x00100000     0x0                     1M

         2       kernel       0x00300000    0x00100000               3M

         3       root         0x02800000    0x00400000               40M

         4       user         0x00f00000     0x02d00000              20M

1. 建立分区表:

Static struct mtd_partition partition_info[] ={

       {  /*1M*/

              name: “bootloader”,

        size:   0x00100000,

        offset:  0x0,

}, {   /*3M*/

       name: “kernel”,

    size:   0x00300000,

    offset:  0x00100000,

}, { /*40M*/

                     name: “root”,

                     size:   0x02800000,

            offset:  0x00400000,

}, { /*20M*/

       name: “user”,

    size:   0x00f00000,

    offset:  0x02d00000,

}

};

2. 加入nand flash分区:

   Struct s3c2410_nand_set nandset = {

      nr_partitions: 4,   /*分区个数*/

     partitions:  partition_info.  /*分区表*/

};

3. 建立Nand Flash芯片支持

    Struct s3c2410_paltform_nand superlpplatform = {

              tacls: 0,  

        twrph0: 30,

        twrphl: 0,

        sets: &nandset,

        nr_sets: 1,

};

4. 加入Nand Flash芯片支持到nand flash驱动中.

     Struct platform_device s3c_device_nand = {

              .name = “s3c2410-nand”,

              .id   = -1,

        ….

        .dev = {

              .platform_data = &superlpplatform

}    

};

OK, 最后的这个s3c_device_nand将在系统启动的时候传到nand flash驱动的probe函数中去, s3c2410nand flash的驱动在drivers/mtd/nand/s3c2410.c, 关于nand flash驱动是如何被系统加载的,及流程可参考我的另一篇文章<< 2410NAND驱动流程>>. 这里我们只关心mtd方面的东西, 所以这里就直接看probe函数:

Drivers/mtd/nand/s3c2410.c:

static int s3c2410_nand_probe(struct device *dev)

{

    struct platform_device *pdev = to_platform_device(dev);  /*这就是上面的s3c_device_nand */

       struct s3c2410_platform_nand *plat = to_nand_plat(dev);  /*这就是上面的superlpplatform */

    struct s3c2410_nand_info *info;

       struct s3c2410_nand_mtd *nmtd;

       struct s3c2410_nand_set *sets;

 

……..

 

       sets = (plat != NULL) ? plat->sets : NULL;   /*这就是上面定义的那个nandset */

       nr_sets = (plat != NULL) ? plat->nr_sets : 1;  /*这里的值为1*/

 

       info->mtd_count = nr_sets;

 

       /* allocate our information */

 

       size = nr_sets * sizeof(*info->mtds);

       info->mtds = kmalloc(size, GFP_KERNEL);   /*分配内存*/

       if (info->mtds == NULL) {

              printk(KERN_ERR PFX "failed to allocate mtd storage/n");

              err = -ENOMEM;

              goto exit_error;

       }

 

       memzero(info->mtds, size);

 

       /* initialise all possible chips */

 

       nmtd = info->mtds;

 

       for (setno = 0; setno < nr_sets; setno++, nmtd++) {

              pr_debug("initialising set %d (%p, info %p)/n",

                      setno, nmtd, info);

             

              s3c2410_nand_init_chip(info, nmtd, sets);   /*chip初始化*/

 

    /*找到合适的nand设备并对mtd初始化*/

              nmtd->scan_res = nand_scan(&nmtd->mtd,

                                      (sets) ? sets->nr_chips : 1);

 

              if (nmtd->scan_res == 0) {

                     s3c2410_nand_add_partition(info, nmtd, sets);   /*添加分区*/

              }

 

              if (sets != NULL)

                     sets++;

       }

       ……

}

所有管理分区的初始化都是在这个函数里面进行的, 这个函数执行完后将会在设备文件列表里面看到我们每个分区的设备文件, 并且这些设备文件是已就绪可以使用的了, 所以我们着重分析这个函数.

Drivers/mtd/nand/s3c2410.c:

static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info,

                               struct s3c2410_nand_mtd *nmtd,

                               struct s3c2410_nand_set *set)

{

       struct nand_chip *chip = &nmtd->chip;

 

       chip->IO_ADDR_R         = (char *)info->regs + S3C2410_NFDATA;

       chip->IO_ADDR_W    = (char *)info->regs + S3C2410_NFDATA;

       chip->hwcontrol    = s3c2410_nand_hwcontrol;

       chip->dev_ready    = s3c2410_nand_devready;

       chip->cmdfunc      = s3c2410_nand_command;

       chip->write_buf    = s3c2410_nand_write_buf;

       chip->read_buf     = s3c2410_nand_read_buf;

       chip->select_chip  = s3c2410_nand_select_chip;

       chip->chip_delay   = 50;

       chip->priv         = nmtd;

       chip->options    = 0;

       chip->controller   = &info->controller;

 

       nmtd->info        = info;

       nmtd->mtd.priv        = chip;

       nmtd->set         = set;

 

       if (hardware_ecc) {

              chip->correct_data  = s3c2410_nand_correct_data;

              chip->enable_hwecc  = s3c2410_nand_enable_hwecc;

              chip->calculate_ecc = s3c2410_nand_calculate_ecc;

              chip->eccmode          = NAND_ECC_HW3_512;

              chip->autooob       = &nand_hw_eccoob;

       } else {

              chip->eccmode          = NAND_ECC_SOFT;

       }

}

这个函数主要初始化了chip对象, 这些回调函数会在相应的地方被调用, 由于和mtd关系不大所以不详细讲解了.

接着看nand_scan(), 这个函数非常大, 但非常重要, 我们的master(后面会解释)的初始化都是在这里完成的, 我们一段一段的看

Drivers/mtd/nand/nand_base.c:

int nand_scan (struct mtd_info *mtd, int maxchips)

{

 

       int i, j, nand_maf_id, nand_dev_id, busw;

       struct nand_chip *this = mtd->priv;

   

    /*下面这些代码还是对那个chip初始化, 注册一堆的回调函数*/

       /* Get buswidth to select the correct functions*/

       busw = this->options & NAND_BUSWIDTH_16;

 

       /* check for proper chip_delay setup, set 20us if not */

       if (!this->chip_delay)

              this->chip_delay = 20;

 

       /* check, if a user supplied command function given */

       if (this->cmdfunc == NULL)

              this->cmdfunc = nand_command;

 

       /* check, if a user supplied wait function given */

       if (this->waitfunc == NULL)

              this->waitfunc = nand_wait;

 

       if (!this->select_chip)

              this->select_chip = nand_select_chip;

       if (!this->write_byte)

              this->write_byte = busw ? nand_write_byte16 : nand_write_byte;

       if (!this->read_byte)

              this->read_byte = busw ? nand_read_byte16 : nand_read_byte;

       if (!this->write_word)

              this->write_word = nand_write_word;

       if (!this->read_word)

              this->read_word = nand_read_word;

       if (!this->block_bad)

              this->block_bad = nand_block_bad;

       if (!this->block_markbad)

              this->block_markbad = nand_default_block_markbad;

       if (!this->write_buf)

              this->write_buf = busw ? nand_write_buf16 : nand_write_buf;

       if (!this->read_buf)

              this->read_buf = busw ? nand_read_buf16 : nand_read_buf;

       if (!this->verify_buf)

              this->verify_buf = busw ? nand_verify_buf16 : nand_verify_buf;

       if (!this->scan_bbt)

              this->scan_bbt = nand_default_bbt;

 

       /* Select the device */

/*这边调的就是在上面那个函数注册的那个回调函数了, 结合2410spec, 主要就是使能这个chip, mtd关系不大,我们不详细看了*/     

this->select_chip(mtd, 0); 

….

 

接着看nand_scan()

Drivers/mtd/nand/nand_base.c:

int nand_scan (struct mtd_info *mtd, int maxchips)

{

    …….

       /* Send the command for reading device ID */

    /*调用特定chipcommand函数, 来读取设备,厂商的ID, 读写过程跟flash有关*/

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

 

       /* Read manufacturer and device IDs */

       nand_maf_id = this->read_byte(mtd);   /*厂商ID*/

       nand_dev_id = this->read_byte(mtd);   /*设备ID*/

 

       /* Print and store flash device information */

    /*这个for循环主要是匹配系统支持的flash设备,并初始化mtd*/

       for (i = 0; nand_flash_ids[i].name != NULL; i++) {

                           

         /* nand_flash_ids 中保存了一个系统支持的设备列表*/

              if (nand_dev_id != nand_flash_ids[i].id)

                     continue;   /*不匹配,则直接循环*/

 

        /*初始化MTD相关信息*/

              if (!mtd->name) mtd->name = nand_flash_ids[i].name;  /*保存名字*/

              this->chipsize = nand_flash_ids[i].chipsize << 20;   /*大小*/

             

              /* New devices have all the information in additional id bytes */

              if (!nand_flash_ids[i].pagesize) {

                     int extid;

                     /* The 3rd id byte contains non relevant data ATM */

                     extid = this->read_byte(mtd);

                     /* The 4th id byte is the important one */

                     extid = this->read_byte(mtd);

                     /* Calc pagesize */

                     mtd->oobblock = 1024 << (extid & 0x3);

                     extid >>= 2;

                     /* Calc oobsize */

                     mtd->oobsize = (8 << (extid & 0x03)) * (mtd->oobblock / 512);

                     extid >>= 2;

                     /* Calc blocksize. Blocksize is multiples of 64KiB */

                     mtd->erasesize = (64 * 1024)  << (extid & 0x03);

                     extid >>= 2;

                     /* Get buswidth information */

                     busw = (extid & 0x01) ? NAND_BUSWIDTH_16 : 0;

             

              } else {

                     /* Old devices have this data hardcoded in the

                      * device id table */

                     mtd->erasesize = nand_flash_ids[i].erasesize;

                     mtd->oobblock = nand_flash_ids[i].pagesize;

                     mtd->oobsize = mtd->oobblock / 32;

                     busw = nand_flash_ids[i].options & NAND_BUSWIDTH_16;

              }

 

              /* Check, if buswidth is correct. Hardware drivers should set

               * this correct ! */

              if (busw != (this->options & NAND_BUSWIDTH_16)) {

                     printk (KERN_INFO "NAND device: Manufacturer ID:"

                            " 0x%02x, Chip ID: 0x%02x (%s %s)/n", nand_maf_id, nand_dev_id,

                            nand_manuf_ids[i].name , mtd->name);

                     printk (KERN_WARNING

                            "NAND bus width %d instead %d bit/n",

                                   (this->options & NAND_BUSWIDTH_16) ? 16 : 8,

                                   busw ? 16 : 8);

                     this->select_chip(mtd, -1);

                     return 1; 

              }

             

              /* Calculate the address shift from the page size */   

              this->page_shift = ffs(mtd->oobblock) - 1;

              this->bbt_erase_shift = this->phys_erase_shift = ffs(mtd->erasesize) - 1;

              this->chip_shift = ffs(this->chipsize) - 1;

 

              /* Set the bad block position */

              this->badblockpos = mtd->oobblock > 512 ?

                     NAND_LARGE_BADBLOCK_POS : NAND_SMALL_BADBLOCK_POS;

 

              /* Get chip options, preserve non chip based options */

              this->options &= ~NAND_CHIPOPTIONS_MSK;

              this->options |= nand_flash_ids[i].options & NAND_CHIPOPTIONS_MSK;

              /* Set this as a default. Board drivers can override it, if neccecary */

              this->options |= NAND_NO_AUTOINCR;

              /* Check if this is a not a samsung device. Do not clear the options

               * for chips which are not having an extended id.

               */ 

              if (nand_maf_id != NAND_MFR_SAMSUNG && !nand_flash_ids[i].pagesize)

                     this->options &= ~NAND_SAMSUNG_LP_OPTIONS;

             

              /* Check for AND chips with 4 page planes */

              if (this->options & NAND_4PAGE_ARRAY)

                     this->erase_cmd = multi_erase_cmd;

              else

                     this->erase_cmd = single_erase_cmd;

 

              /* Do not replace user supplied command function ! */

              if (mtd->oobblock > 512 && this->cmdfunc == nand_command)

                     this->cmdfunc = nand_command_lp;

                           

              /* Try to identify manufacturer */

              for (j = 0; nand_manuf_ids[j].id != 0x0; j++) {

                     if (nand_manuf_ids[j].id == nand_maf_id)

                            break;

              }

              printk (KERN_INFO "NAND device: Manufacturer ID:"

                     " 0x%02x, Chip ID: 0x%02x (%s %s)/n", nand_maf_id, nand_dev_id,

                     nand_manuf_ids[j].name , nand_flash_ids[i].name);

              break;

       }

…….

}

   OK, 上面这段代码主要是来检测该新设备是否是系统支持的, 如果支持则 初始化代表该设备的MTD对象.

注意, 到目前为止, 所有提到的MTD都是指的是原始MTD设备, 即代表整个flash设备. 接着我们会看到对于每个分区都有一个mtd对象与之对应, 这就是面向上层的MTD设备,可以把原始MTD设备看成是MTD设备的masterparent.

我们看下nand_flash_ids

Drivers/mtd/nand/nand_ids.c:

struct nand_flash_dev nand_flash_ids[] = {

       {"NAND 1MiB 5V 8-bit",          0x6e, 256, 1, 0x1000, 0},

       {"NAND 2MiB 5V 8-bit",          0x64, 256, 2, 0x1000, 0},

       …….

}

根据上面的代码分析, 如果我们要加个新的nand设备, 则必须要在这里增加一个新条目. 以使系统支持.

 

好了, 接着看nand_scan函数

Drivers/mtd/nand/nand_base.c:

int nand_scan (struct mtd_info *mtd, int maxchips)

{

    …….

       if (!nand_flash_ids[i].name) { 

              printk (KERN_WARNING "No NAND device found!!!/n");

              this->select_chip(mtd, -1);   /*disable掉片选*/

              return 1;  /*呵呵, 不在系统支持的设备列表里面, 则直接返回*/

       }

 

    /*使能每个chip, 这里我们只有一个chip*/

       for (i=1; i < maxchips; i++) {

              this->select_chip(mtd, i);

 

              /* Send the command for reading device ID */

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

 

              /* Read manufacturer and device IDs */

              if (nand_maf_id != this->read_byte(mtd) ||

                  nand_dev_id != this->read_byte(mtd))

                     break;

       }

       if (i > 1)

              printk(KERN_INFO "%d NAND chips detected/n", i);

      

       /* Allocate buffers, if neccecary */

    /*oobOut-of-band,我的理解是每个page spare部分, 即如果page 总大小为528, 存储数据的大小为512, 剩下的16存放ecc等的信息, 这里的oob就是指16这么个部分.*/

       if (!this->oob_buf) {

              size_t len;

              len = mtd->oobsize << (this->phys_erase_shift - this->page_shift);

              this->oob_buf = kmalloc (len, GFP_KERNEL);

              if (!this->oob_buf) {

                     printk (KERN_ERR "nand_scan(): Cannot allocate oob_buf/n");

                     return -ENOMEM;

              }

              this->options |= NAND_OOBBUF_ALLOC;

       }

      

       if (!this->data_buf) {

              size_t len;

              len = mtd->oobblock + mtd->oobsize;

              this->data_buf = kmalloc (len, GFP_KERNEL);

              if (!this->data_buf) {

                     if (this->options & NAND_OOBBUF_ALLOC)

                            kfree (this->oob_buf);

                     printk (KERN_ERR "nand_scan(): Cannot allocate data_buf/n");

                     return -ENOMEM;

              }

              this->options |= NAND_DATABUF_ALLOC;

       }

 

       /* Store the number of chips and calc total size for mtd */

       this->numchips = i;

       mtd->size = i * this->chipsize;

       /* Convert chipsize to number of pages per chip -1. */

       this->pagemask = (this->chipsize >> this->page_shift) - 1;

       /* Preset the internal oob buffer */

       memset(this->oob_buf, 0xff, mtd->oobsize << (this->phys_erase_shift - this->page_shift));

 

       /* If no default placement scheme is given, select an

        * appropriate one */

       if (!this->autooob) {

              /* Select the appropriate default oob placement scheme for

               * placement agnostic filesystems */

              switch (mtd->oobsize) {

              case 8:

                     this->autooob = &nand_oob_8;

                     break;

              case 16:

                     this->autooob = &nand_oob_16;

                     break;

              case 64:

                     this->autooob = &nand_oob_64;

                     break;

              default:

                     printk (KERN_WARNING "No oob scheme defined for oobsize %d/n",

                            mtd->oobsize);

                     BUG();

              }

       }

      

       /* The number of bytes available for the filesystem to place fs dependend

        * oob data */

       if (this->options & NAND_BUSWIDTH_16) {

              mtd->oobavail = mtd->oobsize - (this->autooob->eccbytes + 2);

              if (this->autooob->eccbytes & 0x01)

                     mtd->oobavail--;

       } else

              mtd->oobavail = mtd->oobsize - (this->autooob->eccbytes + 1);

…….

}

这上面这段代码就是初始化oob相关信息, 具体跟flash有关.

在接着往下看nand_scan()

Drivers/mtd/nand/nand_base.c:

int nand_scan (struct mtd_info *mtd, int maxchips)

{

    …….

       this->eccsize = 256;      /* set default eccsize */

   

    /*初始化ecc,oob等信息*/

       switch (this->eccmode) {

 

       case NAND_ECC_HW3_512:

       case NAND_ECC_HW6_512:

       case NAND_ECC_HW8_512:

              if (mtd->oobblock == 256) {

                     printk (KERN_WARNING "512 byte HW ECC not possible on 256 Byte pagesize, fallback to SW ECC /n");

                     this->eccmode = NAND_ECC_SOFT;

                     this->calculate_ecc = nand_calculate_ecc;

                     this->correct_data = nand_correct_data;

                     break;           

              } else

                     this->eccsize = 512; /* set eccsize to 512 and fall through for function check */

 

       case NAND_ECC_HW3_256:

              if (this->calculate_ecc && this->correct_data && this->enable_hwecc)

                     break;

              printk (KERN_WARNING "No ECC functions supplied, Hardware ECC not possible/n");

              BUG();  

 

       case NAND_ECC_NONE:

              printk (KERN_WARNING "NAND_ECC_NONE selected by board driver. This is not recommended !!/n");

              this->eccmode = NAND_ECC_NONE;

              break;

 

       case NAND_ECC_SOFT:   

              this->calculate_ecc = nand_calculate_ecc;

              this->correct_data = nand_correct_data;

              break;

 

       default:

              printk (KERN_WARNING "Invalid NAND_ECC_MODE %d/n", this->eccmode);

              BUG();  

       }    

      

       mtd->eccsize = this->eccsize;

      

       /* Set the number of read / write steps for one page to ensure ECC generation */

       switch (this->eccmode) {

       case NAND_ECC_HW3_512:

       case NAND_ECC_HW6_512:

       case NAND_ECC_HW8_512:

              this->eccsteps = mtd->oobblock / 512;

              break;

       case NAND_ECC_HW3_256:

       case NAND_ECC_SOFT:   

              this->eccsteps = mtd->oobblock / 256;

              break;

             

       case NAND_ECC_NONE:

              this->eccsteps = 1;

              break;

       }

      

       /* Initialize state, waitqueue and spinlock */

       this->state = FL_READY;

       init_waitqueue_head (&this->wq);

       spin_lock_init (&this->chip_lock);

 

       /* De-select the device */

       this->select_chip(mtd, -1);

 

       /* Invalidate the pagebuffer reference */

       this->pagebuf = -1;

 

       /* Fill in remaining MTD driver data */

    /*初始化MTD部分, 这里的MTD也是原始MTD, 这部分代码很重要,因为以后还会回来看的*/

       mtd->type = MTD_NANDFLASH;

       mtd->flags = MTD_CAP_NANDFLASH | MTD_ECC;

       mtd->ecctype = MTD_ECC_SW;

       mtd->erase = nand_erase;

       mtd->point = NULL;

       mtd->unpoint = NULL;

       mtd->read = nand_read;

       mtd->write = nand_write;

       mtd->read_ecc = nand_read_ecc;

       mtd->write_ecc = nand_write_ecc;

       mtd->read_oob = nand_read_oob;

       mtd->write_oob = nand_write_oob;

       mtd->readv = NULL;

       mtd->writev = nand_writev;

       mtd->writev_ecc = nand_writev_ecc;

       mtd->sync = nand_sync;

       mtd->lock = NULL;

       mtd->unlock = NULL;

       mtd->suspend = NULL;

       mtd->resume = NULL;

       mtd->block_isbad = nand_block_isbad;

       mtd->block_markbad = nand_block_markbad;

 

       /* and make the autooob the default one */

       memcpy(&mtd->oobinfo, this->autooob, sizeof(mtd->oobinfo));

 

       mtd->owner = THIS_MODULE;

 

       /* Build bad block table */

       return this->scan_bbt (mtd);   /*建立坏块列表*/

}

记住这里为MTD赋值的这些值, 包括回调函数等, 会在以后对flash操作中使用的.

实际上nand_scan()主要做的是flash方面的初始化, 涉及MTD的部分主要还是回调函数.