NAND驱动分析--(二)

来源:互联网 发布:hadoop源码有多少 编辑:程序博客网 时间:2024/05/17 02:34

在上一篇nand驱动分析中,大概描述了nand flash驱动加载时的初始化流程,接下来对其调用的一些函数进行进一步的阐述。

首先,上一篇说到调用了fsl_elbc_chip_init()函数,此函数还是在drivers\mtd\nand\fsl_elbc_nand.c文件中被定义,其代码如下所示:

static int fsl_elbc_chip_init(struct fsl_elbc_mtd *priv){    struct fsl_lbc_ctrl *ctrl = priv->ctrl;     struct fsl_lbc_regs __iomem *lbc = ctrl->regs; //将寄存器基址赋值给lbc    struct nand_chip *chip = &priv->chip; //将priv结构的chip成员地址传递给此函数中的chip指针,这样就可以对priv结构中的chip成员进行一系列初始化    printk("eLBC Set Information for bank %d\n", priv->bank);    priv->mtd.priv = chip; //将chip指针传递给主分区mtd中的私有数据,其类型为nand_chip    priv->mtd.owner = THIS_MODULE;    priv->fmr = in_be32(&lbc->fmr) & FMR_ECCM;    /* 初始化nand_chip中的一些基本操作函数 */    chip->read_byte = fsl_elbc_read_byte; //读数据函数    chip->write_buf = fsl_elbc_write_buf;    chip->read_buf = fsl_elbc_read_buf;    chip->verify_buf = fsl_elbc_verify_buf;    chip->select_chip = fsl_elbc_select_chip;    chip->cmdfunc = fsl_elbc_cmdfunc; //写命令函数    chip->waitfunc = fsl_elbc_wait; //等待命令执行完毕函数    chip->bbt_td = &bbt_main_descr;    chip->bbt_md = &bbt_mirror_descr;    chip->options = NAND_NO_READRDY | NAND_NO_AUTOINCR |            NAND_USE_FLASH_BBT;    chip->controller = &elbc_fcm_ctrl->controller;    chip->priv = priv;    chip->ecc.read_page = fsl_elbc_read_page;    chip->ecc.write_page = fsl_elbc_write_page;    if ((in_be32(&lbc->bank[priv->bank].br) & BR_DECC) ==        BR_DECC_CHK_GEN) {        chip->ecc.mode = NAND_ECC_HW;        /* put in small page settings and adjust later if needed */        #if 0        chip->ecc.layout = (priv->fmr & FMR_ECCM) ?                &fsl_elbc_oob_sp_eccm1 : &fsl_elbc_oob_sp_eccm0;        #else        chip->ecc.layout = (priv->fmr & FMR_ECCM) ?                &fsl_elbc_oob_lp_eccm1 : &fsl_elbc_oob_lp_eccm0;        #endif        chip->ecc.size = 512;        chip->ecc.bytes = 3;    } else {        chip->ecc.mode = NAND_ECC_SOFT;    }    chip->ecc.mode = NAND_ECC_NONE;    return 0;}

以上代码的主要作用有两点:1、将主分区的mtd_info结构中的priv指针,指向nand_chip;2、初始化nand_chip结构体中的一些基本操作函数。
而在nand_chip结构体中最重要的一个操作函数是chip->cmdfunc,所以接下来以此函数进行分析,而其他的函数就不再进行详细分析。代码如下所示:

/* cmdfunc send commands to the FCM */static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned int command,                             int column, int page_addr){    struct nand_chip *chip = mtd->priv;    struct fsl_elbc_mtd *priv = chip->priv;    struct fsl_lbc_ctrl *ctrl = priv->ctrl;    struct fsl_lbc_regs __iomem *lbc = ctrl->regs;    elbc_fcm_ctrl->use_mdr = 0;    /* clear the read buffer */    elbc_fcm_ctrl->read_bytes = 0;    if (command != NAND_CMD_PAGEPROG)        elbc_fcm_ctrl->index = 0;    switch (command) //根据发送的命令来执行相应的操作    {        /* READ0 and READ1 read the entire buffer to use hardware ECC. */        case NAND_CMD_READ1:            column += 256;        /* fall-through */        case NAND_CMD_READ0: //读数据命令            out_be32(&lbc->fbcr, 0); /* read entire page to enable ECC */            set_addr(mtd, 0, page_addr, 0); //设置要读取数据的首地址,即向elbc的块、页和列地址寄存器写值            elbc_fcm_ctrl->read_bytes = mtd->writesize + mtd->oobsize; //计算出每次读取字节的总数            elbc_fcm_ctrl->index += column;            fsl_elbc_do_read(chip, 0); //向FCM模式的本地总线发送读取nand flash数据的指令            fsl_elbc_run_command(mtd); //使cpu运行刚才向elbc发送的指令            return;        /* READOOB reads only the OOB because no ECC is performed. */        case NAND_CMD_READOOB:            out_be32(&lbc->fbcr, mtd->oobsize - column);            set_addr(mtd, column, page_addr, 1);            elbc_fcm_ctrl->read_bytes = mtd->writesize + mtd->oobsize;            fsl_elbc_do_read(chip, 1);            fsl_elbc_run_command(mtd);            return;        /* READID must read all 5 possible bytes while CEB is active */        case NAND_CMD_READID: //读取芯片ID            /* 向fir寄存器写入想要执行指令的步骤和顺序 */            out_be32(&lbc->fir, (FIR_OP_CM0 << FIR_OP0_SHIFT) | //执行fcr中第一条指令                                (FIR_OP_UA  << FIR_OP1_SHIFT) | //使用用户默认地址                                (FIR_OP_RBW << FIR_OP2_SHIFT)); //等待并读取fbcr寄存器的值            out_be32(&lbc->fcr, NAND_CMD_READID << FCR_CMD0_SHIFT); //写入读取设备ID指令到fcr寄存器中的第一条指令的位置            /* 5 bytes for manuf, device and exts */            out_be32(&lbc->fbcr, 5); //向数据计数寄存器中写入要读取数据总数的值            elbc_fcm_ctrl->read_bytes = 5;            elbc_fcm_ctrl->use_mdr = 1;            elbc_fcm_ctrl->mdr = 0; //将FCM数据寄存器的值先清零            set_addr(mtd, 0, 0, 0); //设置地址,此时是读设备ID,所以地址为0            fsl_elbc_run_command(mtd); //开始运行fir以及fcr寄存器中的指令            return;        /* 从此处可知,擦除命令分为两个步骤,NAND_CMD_ERASE1命令用于设置想要擦除内容的首地址,NAND_CMD_ERASE2命令用于擦除步骤1中设置的地址所指向的内容 */        /* ERASE1 stores the block and page address */        case NAND_CMD_ERASE1: //擦除命令1            set_addr(mtd, 0, page_addr, 0); //设置要擦除nand块的首地址            return;        /* ERASE2 uses the block and page address from ERASE1 */        case NAND_CMD_ERASE2: //擦除命令2            out_be32(&lbc->fir,                 (FIR_OP_CM0 << FIR_OP0_SHIFT) |                     (FIR_OP_PA  << FIR_OP1_SHIFT) |                 (FIR_OP_CM2 << FIR_OP2_SHIFT) |                 (FIR_OP_CW1 << FIR_OP3_SHIFT) |                 (FIR_OP_RS  << FIR_OP4_SHIFT));            out_be32(&lbc->fcr,                     (NAND_CMD_ERASE1 << FCR_CMD0_SHIFT) |                 (NAND_CMD_STATUS << FCR_CMD1_SHIFT) |                 (NAND_CMD_ERASE2 << FCR_CMD2_SHIFT));            out_be32(&lbc->fbcr, 0);            elbc_fcm_ctrl->read_bytes = 0;            elbc_fcm_ctrl->use_mdr = 1;            fsl_elbc_run_command(mtd);            return;        /* RESET without waiting for the ready line */        case NAND_CMD_RESET: //nand flash芯片复位操作            out_be32(&lbc->fir, FIR_OP_CM0 << FIR_OP0_SHIFT);            out_be32(&lbc->fcr, NAND_CMD_RESET << FCR_CMD0_SHIFT);            fsl_elbc_run_command(mtd);            return;        default:            dev_err(priv->dev,                    "fsl_elbc_cmdfunc: error, unsupported command 0x%x.\n",                    command);    }}

从上述代码可以看出,elbc是如何通过数据、地址总线向nand flash发送命令、地址等数据的。

操作nand flash的方法,如下所示:
eLBC_FIReLBC_FCR这两个寄存器是操作nand flash的关键。
1、eLBC_FIR寄存器,向此寄存器中写入一些操作码,即可实现相应的功能,其功能码如下所示:

/* FIR寄存器一共有8个可编程指令序列,而此指令为4bit位的操作码 */#define FIR_OP0      0xF0000000#define FIR_OP0_SHIFT        28#define FIR_OP1      0x0F000000#define FIR_OP1_SHIFT        24#define FIR_OP2      0x00F00000#define FIR_OP2_SHIFT        20#define FIR_OP3      0x000F0000#define FIR_OP3_SHIFT        16#define FIR_OP4      0x0000F000#define FIR_OP4_SHIFT        12#define FIR_OP5      0x00000F00#define FIR_OP5_SHIFT         8#define FIR_OP6      0x000000F0#define FIR_OP6_SHIFT         4#define FIR_OP7      0x0000000F#define FIR_OP7_SHIFT         0/* No operation and end of sequence */#define FIR_OP_NOP   0x0    /* Issue current column address */#define FIR_OP_CA    0x1  /* Issue current block+page address */      #define FIR_OP_PA    0x2        /* Issue user defined address */#define FIR_OP_UA    0x3  /* Issue command from FCR[CMD0] */      #define FIR_OP_CM0   0x4  /* Issue command from FCR[CMD1] */      #define FIR_OP_CM1   0x5 /* Issue command from FCR[CMD2] */       #define FIR_OP_CM2   0x6 /* Issue command from FCR[CMD3] */       #define FIR_OP_CM3   0x7   /* Write FBCR bytes from FCM buffer */     #define FIR_OP_WB    0x8  /* Write 1 or 2 bytes from MDR[AS] */      #define FIR_OP_WS    0x9   /* Read FBCR bytes to FCM buffer */     #define FIR_OP_RB    0xA    /* Read 1 or 2 bytes to MDR[AS] */    #define FIR_OP_RS    0xB     /* Wait then issue FCR[CMD0] */   #define FIR_OP_CW0   0xC    /* Wait then issue FCR[CMD1] */    #define FIR_OP_CW1   0xD        /* Wait then read FBCR bytes */#define FIR_OP_RBW   0xE        /* Wait then read 1 or 2 bytes */#define FIR_OP_RSW   0xE        

2、eLBC_FCR寄存器,而此寄存器中则主要根据nand芯片手册的一些操作命令来写入nand flash芯片的一些指令,如0x90表示读取芯片ID等。
最后再运行fsl_elbc_run_command(mtd);函数即可根据上面的两个寄存器的值来进行nand操作。

接下来就会执行到nand_scan (&priv->mtd, 1)函数,此函数用于扫描连接到elbc的外部nand flash,具体代码如下所示:

int nand_scan (struct mtd_info *mtd, int maxchips){    int ret;    int i, j, nand_maf_id, nand_dev_id, busw;    struct nand_chip *this = mtd->priv;    /* 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 */    this->select_chip(mtd, 0);    /* Send the command for reading device ID */    this->cmdfunc (mtd, NAND_CMD_READID, 0x00, -1); //读取nand设备ID号    /* Read manufacturer and device IDs */    nand_maf_id = this->read_byte(mtd); //将读出的ID号赋值给nand_maf_id    nand_dev_id = this->read_byte(mtd); //将读出的ID号赋值给nand_dev_id     /* Print and store flash device information */    /* 轮询定义的芯片信息有没有与读出的设备ID一致的芯片 */    for (i = 0; nand_flash_ids[i].name != NULL; i++) {        if (nand_dev_id != nand_flash_ids[i].id)             continue;        /* 查找到对应的芯片信息后,根据芯片信息将nand_chip结构体补充完整 */        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);            mtd->writesize = 1024 << (extid & 0x3);            extid >>= 2;            /* Calc oobsize */            mtd->oobsize = (8 << (extid & 0x01)) * (mtd->writesize >> 9);            extid >>= 2;            /* Calc blocksize. Blocksize is multiples of 64KiB */            mtd->erasesize = (64 * 1024)  << (extid & 0x03);            extid >>= 2;            /* Get buswidth information */            busw = (extid & 0x01) ? NAND_BUSWIDTH_16 : 0;        } else {            /* Old devices have 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;            mtd->writesize = nand_flash_ids[i].pagesize;        }        /* 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;        /* Convert chipsize to number of pages per chip -1. */        this->pagemask = (this->chipsize >> this->page_shift) - 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;    }    if (!nand_flash_ids[i].name) {        printk (KERN_WARNING "No NAND device found!!!\n");        this->select_chip(mtd, -1);        return 1;    }    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 */    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_fsl;            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);    /*      * check ECC mode, default to software     * if 3byte/512byte hardware ECC is selected and we have 256 byte pagesize     * fallback to software ECC     */    this->eccsize = 256;    /* set default eccsize */       this->eccbytes = 3;    switch (this->eccmode) {    case NAND_ECC_HW12_2048:        if (mtd->oobblock < 2048) {            printk(KERN_WARNING "2048 byte HW ECC not possible on %d byte page size, fallback to SW ECC\n",                   mtd->oobblock);            this->eccmode = NAND_ECC_SOFT;            this->calculate_ecc = nand_calculate_ecc;            this->correct_data = nand_correct_data;        } else            this->eccsize = 2048;        break;    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;        } else             this->eccsize = 512; /* set eccsize to 512 */        break;    case NAND_ECC_HW3_256:        break;    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();      }       /* Check hardware ecc function availability and adjust number of ecc bytes per      * calculation step    */    switch (this->eccmode) {    case NAND_ECC_HW12_2048:        this->eccbytes += 4;    case NAND_ECC_HW8_512:         this->eccbytes += 2;    case NAND_ECC_HW6_512:         this->eccbytes += 3;    case NAND_ECC_HW3_512:     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();      }    mtd->eccsize = this->eccsize;    /* Set the number of read / write steps for one page to ensure ECC generation */    switch (this->eccmode) {    case NAND_ECC_HW12_2048:        this->eccsteps = mtd->oobblock / 2048;        break;    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_info结构体,每一个分区都包含了一个mtd_info结构体,所以每一个分区都可以调用mtd_info结构体中的一些功能函数,对分区进行操作 */    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;    fsl_elbc_chip_init_tail(mtd);    /* Build bad block table */    return this->scan_bbt (mtd);}

nand_scan函数的主要作用就是用于扫描外部nand flash,并与代码中已有的nand flash设备ID号进行匹配,如果匹配成功,则利用对应的nand设备信息将nand_chip结构体和mtd_info结构体填充完整。

到程序的最后,将调用add_mtd_partitions(&priv->mtd, p1020_partition_info, 3)函数来进行nand flash的分区,并将priv->mtd作为主分区,来将其它分区的mtd_info结构补充完整。其代码分析如下:

int add_mtd_partitions(struct mtd_info *master,                const struct mtd_partition *parts,               int nbparts){    struct mtd_part *slave;    u_int32_t cur_offset = 0;    int i;    printk (KERN_NOTICE "Creating %d MTD partitions on \"%s\":\n", nbparts, master->name);    /* 根据nbparts参数来确定nand存储器将划分为几个分区 */    for (i = 0; i < nbparts; i++) {        /* allocate the partition structure */        slave = kmalloc (sizeof(*slave), GFP_KERNEL);        if (!slave) {            printk ("memory allocation error while creating partitions for \"%s\"\n",                master->name);            del_mtd_partitions(master);            return -ENOMEM;        }        memset(slave, 0, sizeof(*slave));        list_add(&slave->list, &mtd_partitions);        /* set up the MTD object for this partition */        /* 根据主分区的mtd_info结构信息来初始化各个分区的mtd_info信息 */        slave->mtd.type = master->type;        slave->mtd.flags = master->flags & ~parts[i].mask_flags;        slave->mtd.size = parts[i].size;        slave->mtd.oobblock = master->oobblock;        slave->mtd.oobsize = master->oobsize;        slave->mtd.ecctype = master->ecctype;        slave->mtd.eccsize = master->eccsize;        slave->mtd.name = parts[i].name;        slave->mtd.bank_size = master->bank_size;        slave->mtd.owner = master->owner;        slave->mtd.read = part_read;        slave->mtd.write = part_write;        if(master->point && master->unpoint){            slave->mtd.point = part_point;            slave->mtd.unpoint = part_unpoint;        }        if (master->read_ecc)            slave->mtd.read_ecc = part_read_ecc;        if (master->write_ecc)            slave->mtd.write_ecc = part_write_ecc;        if (master->read_oob)            slave->mtd.read_oob = part_read_oob;        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->sync)            slave->mtd.sync = part_sync;        if (!i && master->suspend && master->resume) {                slave->mtd.suspend = part_suspend;                slave->mtd.resume = part_resume;        }        if (master->writev)            slave->mtd.writev = part_writev;        if (master->readv)            slave->mtd.readv = part_readv;        if (master->writev_ecc)            slave->mtd.writev_ecc = part_writev_ecc;        if (master->readv_ecc)            slave->mtd.readv_ecc = part_readv_ecc;        if (master->lock)            slave->mtd.lock = part_lock;        if (master->unlock)            slave->mtd.unlock = part_unlock;        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 = parts[i].offset;        slave->index = i;        if (slave->offset == MTDPART_OFS_APPEND)            slave->offset = cur_offset;        if (slave->offset == MTDPART_OFS_NXTBLK) {            u_int32_t emask = master->erasesize-1;            slave->offset = (cur_offset + emask) & ~emask;            if (slave->offset != cur_offset) {                printk(KERN_NOTICE "Moving partition %d: "                       "0x%08x -> 0x%08x\n", i,                       cur_offset, slave->offset);            }        }        if (slave->mtd.size == MTDPART_SIZ_FULL)            slave->mtd.size = master->size - slave->offset;        cur_offset = slave->offset + slave->mtd.size;        printk (KERN_NOTICE "0x%08x-0x%08x : \"%s\"\n", slave->offset,             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 ("mtd: partition \"%s\" is out of reach -- disabled\n",                parts[i].name);        }        if (slave->offset + slave->mtd.size > master->size) {            slave->mtd.size = master->size - slave->offset;            printk ("mtd: partition \"%s\" extends beyond the end of device \"%s\" -- size truncated to %#x\n",                parts[i].name, master->name, slave->mtd.size);        }        if (master->numeraseregions>1) {            /* Deal with variable erase size stuff */            int i;            struct mtd_erase_region_info *regions = master->eraseregions;            /* Find the first erase regions which is part of this partition. */            for (i=0; i < master->numeraseregions && slave->offset >= regions[i].offset; i++)                ;            for (i--; i < master->numeraseregions && slave->offset + slave->mtd.size > regions[i].offset; i++) {                if (slave->mtd.erasesize < regions[i].erasesize) {                    slave->mtd.erasesize = regions[i].erasesize;                }            }        } else {            /* Single erase size */            slave->mtd.erasesize = master->erasesize;        }        if ((slave->mtd.flags & MTD_WRITEABLE) &&             (slave->offset % slave->mtd.erasesize)) {            /* 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 ("mtd: partition \"%s\" doesn't start on an erase block boundary -- force read-only\n",                parts[i].name);        }        if ((slave->mtd.flags & MTD_WRITEABLE) &&             (slave->mtd.size % slave->mtd.erasesize)) {            slave->mtd.flags &= ~MTD_WRITEABLE;            printk ("mtd: partition \"%s\" doesn't end on an erase block -- force read-only\n",                parts[i].name);        }        /* copy oobinfo from master */         memcpy(&slave->mtd.oobinfo, &master->oobinfo, sizeof(slave->mtd.oobinfo));        if(parts[i].mtdp)        {   /* store the object pointer (caller may or may not register it */            *parts[i].mtdp = &slave->mtd;            slave->registered = 0;        }        else        {            /* register our partition */            /* 最后再根据从分区的mtd_info结构来注册分区 */            add_mtd_device(&slave->mtd);            slave->registered = 1;        }    }    return 0;}

所以此函数的主要作用就是向系统注册nand flash的分区。

此时,nand flash的硬件驱动层就分析完毕。下一篇文章将讲述mtd设备层。

原创粉丝点击