注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

还东国的博客

行之苟有恒,久久自芬芳

 
 
 

日志

 
 

Linux MTD子系统学习5—s3c24xx_nand_probe()函数之四  

2012-01-15 11:26:39|  分类: LINUX内核驱动 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |
 

Linux MTD子系统学习5—s3c24xx_nand_probe()函数之四

从今天开始,再回过头来接着讲probe函数,毕竟是讲驱动呢么?这个题目的顺序排列有些个问题,为了少改动些,直接从2跳到5,如果有可以写的文章,插进去就插进去,没有就这样吧。

说回正题,上回讲完了s3c2410_nand_init_chip这个函数,重新回到s3c24xx_nand_probe这个函数中,开始进入另外一个重要的函数:

/**

 * nand_scan_ident - [NAND Interface] Scan for the NAND device

 * @mtd:         MTD device structure

 * @maxchips:        Number of chips to scan for

 *

 * This is the first phase of the normal nand_scan() function. It

 * reads the flash ID and sets up MTD fields accordingly.

 *

 * The mtd->owner field must be set to the module of the caller.

 */

int nand_scan_ident(struct mtd_info *mtd, int maxchips)

{

    int i, busw, nand_maf_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 */

    type = nand_get_flash_type(mtd, chip, busw, &nand_maf_id);

 

    if (IS_ERR(type)) {

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

        chip->select_chip(mtd, -1);

        return PTR_ERR(type);

    }

 

    /* Check for a chip array */

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

        chip->select_chip(mtd, i);

        /* See comment in nand_get_flash_type for reset */

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

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

            type->id != chip->read_byte(mtd))

            break;

    }

    if (i > 1)

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

 

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

    chip->numchips = i;

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

 

    return 0;

}

这个函数起什么作用呢?这个函数主要用来扫描NAND设备,设置其默认的参数,获得物理设备的型号以及相对应的特性参数,其中很多重要的值都会在这个函数里进行计算并设定。下来就一步步的分析他,零敲牛皮糖吧。

第一个重要的就是nand_set_defaults(chip, busw);这个函数,

/*

 * Set default functions

 */

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 (!chip->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 = busw ? nand_write_buf16 : nand_write_buf;

         if (!chip->read_buf)

                   chip->read_buf = busw ? nand_read_buf16 : nand_read_buf;

         if (!chip->verify_buf)

                   chip->verify_buf = busw ? nand_verify_buf16 : nand_verify_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);

         }

 

}

一看这密密麻麻的都是对nand_chip这个结构体的参数进行赋值,原来设置的就设置了,没有设置的就给一个默认的,这样不至于出现裸奔的情况。写内核就这样,想到的得想到,想不到的,也得想着,至于你想到想不到,谁也不知道。注意的是这里判断的时候儿都加上了!这个符号,和probe这个函数判断是正好相反,当初也是想当然了,唉。不说那个咧。

和原来的初始化的时候儿对比一下,在刚刚讲过的s3c2410_nand_init_chip函数中,下面只举一小部分,大家自己去看其它:

         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;

 

         switch (info->cpu_type) {

         case TYPE_S3C2410:

                   chip->IO_ADDR_W = regs + S3C2410_NFDATA;

                   info->sel_reg   = regs + S3C2410_NFCONF;

                   info->sel_bit  = S3C2410_NFCONF_nFCE;

                   chip->cmd_ctrl  = s3c2410_nand_hwcontrol;

                   chip->dev_ready = s3c2410_nand_devready;

                   break;

其中默认给定的那个chip->cmdfunc = nand_command;很有用,下面会用到。其实LINUX内核的复杂也在这儿,不断的通过抽象出来数据结构,一层层的赋值,有你自己写的,有官方默认的,有别人写的,再加上来回的类似的C++一样的重载,匹配。一开始不晕菜的才是真正的牛人,我辈自叹弗如啊。

好,再回来看调用的这个函数:type = nand_get_flash_type(mtd, chip, busw, &nand_maf_id);

/*

 * Get the flash and manufacturer id and lookup if the type is supported

 */

static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,

                                                          struct nand_chip *chip,

                                                          int busw, int *maf_id)

{

         struct nand_flash_dev *type = NULL;

         int i, dev_id, maf_idx;

         int tmp_id, tmp_manf;

 

         /* 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 manufacturer and device IDs */

 

         tmp_manf = chip->read_byte(mtd);

         tmp_id = chip->read_byte(mtd);

 

         if (tmp_manf != *maf_id || tmp_id != dev_id) {

                   printk(KERN_INFO "%s: second ID read did not match "

                          "%02x,%02x against %02x,%02x\n", __func__,

                          *maf_id, dev_id, tmp_manf, tmp_id);

                   return ERR_PTR(-ENODEV);

         }

 

         /* Lookup the flash id */

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

                   if (dev_id == nand_flash_ids[i].id) {

                            type =  &nand_flash_ids[i];

                            break;

                   }

         }

 

         if (!type)

                   return ERR_PTR(-ENODEV);

 

         if (!mtd->name)

                   mtd->name = type->name;

 

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

 

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

         if (!type->pagesize) {

                   int extid;

                   /* The 3rd id byte holds MLC / multichip data */

                   chip->cellinfo = chip->read_byte(mtd);

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

                   extid = chip->read_byte(mtd);

                   /* Calc pagesize */

                   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 chip data hardcoded in the device id table

                    */

                   mtd->erasesize = type->erasesize;

                   mtd->writesize = type->pagesize;

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

                   busw = type->options & NAND_BUSWIDTH_16;

         }

 

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

         }

 

         /*

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

          * chip correct !

          */

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

                   printk(KERN_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);

                   printk(KERN_WARNING "NAND bus width %d instead %d bit\n",

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

                          busw ? 16 : 8);

                   return ERR_PTR(-EINVAL);

         }

 

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

 

         /* Set the bad block position */

         chip->badblockpos = mtd->writesize > 512 ?

                   NAND_LARGE_BADBLOCK_POS : NAND_SMALL_BADBLOCK_POS;

 

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

         chip->options &= ~NAND_CHIPOPTIONS_MSK;

         chip->options |= type->options & NAND_CHIPOPTIONS_MSK;

 

         /*

          * Set chip as a default. Board drivers can override it, if necessary

          */

         chip->options |= NAND_NO_AUTOINCR;

 

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

          * options for chips which are not having an extended id.

          */

         if (*maf_id != NAND_MFR_SAMSUNG && !type->pagesize)

                   chip->options &= ~NAND_SAMSUNG_LP_OPTIONS;

 

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

         if (chip->options & NAND_4PAGE_ARRAY)

                   chip->erase_cmd = multi_erase_cmd;

         else

                   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;

 

         printk(KERN_INFO "NAND device: Manufacturer ID:"

                " 0x%02x, Chip ID: 0x%02x (%s %s)\n", *maf_id, dev_id,

                nand_manuf_ids[maf_idx].name, type->name);

 

         return type;

}

这个函数开始就有:

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

当然的,select_chip这个函数在前面的赋值中已经给到了chip->select_chip  = s3c2410_nand_select_chip;

/**

 * s3c2410_nand_select_chip - select the given nand chip

 * @mtd: The MTD instance for this chip.

 * @chip: The chip number.

 *

 * This is called by the MTD layer to either select a given chip for the

 * @mtd instance, or to indicate that the access has finished and the

 * chip can be de-selected.

 *

 * The routine ensures that the nFCE line is correctly setup, and any

 * platform specific selection code is called to route nFCE to the specific

 * chip.

 */

static void s3c2410_nand_select_chip(struct mtd_info *mtd, int chip)

{

    struct s3c2410_nand_info *info;

    struct s3c2410_nand_mtd *nmtd;

    struct nand_chip *this = mtd->priv;

    unsigned long cur;

 

    nmtd = this->priv;

    info = nmtd->info;

 

    if (chip != -1 && allow_clk_stop(info))

        clk_enable(info->clk);

 

    cur = readl(info->sel_reg);

 

    if (chip == -1) {

        cur |= info->sel_bit;

    } else {

        if (nmtd->set != NULL && chip > nmtd->set->nr_chips) {

            dev_err(info->device, "invalid chip %d\n", chip);

            return;

        }

 

        if (info->platform != NULL) {

            if (info->platform->select_chip != NULL)

                (info->platform->select_chip) (nmtd->set, chip);

        }

 

        cur &= ~info->sel_bit;

    }

 

    writel(cur, info->sel_reg);

 

    if (chip == -1 && allow_clk_stop(info))

        clk_disable(info->clk);

}

 

看这个函数中的调用下面几行:

    cur = readl(info->sel_reg);

 

    if (chip == -1) {

        cur |= info->sel_bit;

再用cur &= ~info->sel_bit;来取反,对照着三星的寄存器和设置,可以知道这是

Enable chip select,这样nand flash就可以使用了。

回到nand_get_flash_type中来.看chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);开始真正的开始与命令打交道了。(我们上面可提示过chip->cmdfunc,不会忘记吧)

/**

 * nand_command - [DEFAULT] Send command to NAND device

 * @mtd:    MTD device structure

 * @command:    the command to be sent

 * @column: the column address for this command, -1 if none

 * @page_addr:  the page address for this command, -1 if none

 *

 * Send command to NAND device. This function is used for small page

 * devices (256/512 Bytes per page)

 */

static void nand_command(struct mtd_info *mtd, unsigned int command,

             int column, int page_addr)

{

    register struct nand_chip *chip = mtd->priv;

    int ctrl = NAND_CTRL_CLE | NAND_CTRL_CHANGE;

 

    /*

     * Write out the command to the device.

     */

    if (command == NAND_CMD_SEQIN) {

        int readcmd;

 

        if (column >= mtd->writesize) {

            /* OOB area */

            column -= mtd->writesize;

            readcmd = NAND_CMD_READOOB;

        } else if (column < 256) {

            /* First 256 bytes --> READ0 */

            readcmd = NAND_CMD_READ0;

        } else {

            column -= 256;

            readcmd = NAND_CMD_READ1;

        }

        chip->cmd_ctrl(mtd, readcmd, ctrl);

        ctrl &= ~NAND_CTRL_CHANGE;

    }

    chip->cmd_ctrl(mtd, command, ctrl);

 

    /*

     * Address cycle, when necessary

     */

    ctrl = NAND_CTRL_ALE | NAND_CTRL_CHANGE;

    /* Serially input address */

    if (column != -1) {

        /* Adjust columns for 16 bit buswidth */

        if (chip->options & NAND_BUSWIDTH_16)

            column >>= 1;

        chip->cmd_ctrl(mtd, column, ctrl);

        ctrl &= ~NAND_CTRL_CHANGE;

    }

    if (page_addr != -1) {

        chip->cmd_ctrl(mtd, page_addr, ctrl);

        ctrl &= ~NAND_CTRL_CHANGE;

        chip->cmd_ctrl(mtd, page_addr >> 8, ctrl);

        /* One more address cycle for devices > 32MiB */

        if (chip->chipsize > (32 << 20))

            chip->cmd_ctrl(mtd, page_addr >> 16, ctrl);

    }

    chip->cmd_ctrl(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE);

 

    /*

     * program and erase have their own busy handlers

     * status and sequential in needs no delay

     */

    switch (command) {

 

    case NAND_CMD_PAGEPROG:

    case NAND_CMD_ERASE1:

    case NAND_CMD_ERASE2:

    case NAND_CMD_SEQIN:

    case NAND_CMD_STATUS:

        return;

 

    case NAND_CMD_RESET:

        if (chip->dev_ready)

            break;

        udelay(chip->chip_delay);

        chip->cmd_ctrl(mtd, NAND_CMD_STATUS,

                   NAND_CTRL_CLE | NAND_CTRL_CHANGE);

        chip->cmd_ctrl(mtd,

                   NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE);

        while (!(chip->read_byte(mtd) & NAND_STATUS_READY)) ;

        return;

 

        /* This applies to read commands */

    default:

        /*

         * If we don't have access to the busy pin, we apply the given

         * command delay

         */

        if (!chip->dev_ready) {

            udelay(chip->chip_delay);

            return;

        }

    }

    /* Apply this short delay always to ensure that we do wait tWB in

     * any case on any machine. */

    ndelay(100);

 

    nand_wait_ready(mtd);

}

具体的命令是怎么定义的,和你的NAND的DATASHEET中描述的硬件有关系,这里就不再讲了,否则也是抄别人的,没有具体的和广泛的意义,再说这个东西是硬性的,不会变,所以没有什么技术性,注意的就是延时和使用的方法。

命令定义在:include\linux\mtd\nand.h中。

这两天为163微博数据一直在纠结,直到今天发现是自己不会使用造成的,然后就明白:

无论对人还是对事,还是对自己,都不要急于下定论,都不要冲动,再冷静的观察、认真的思考后再下结论;少说多做。

最后的一句话:要善于学习,敏于接受别人的批评和意见,有则改之,无则加勉。

  评论这张
 
阅读(1176)| 评论(0)
推荐 转载

历史上的今天

在LOFTER的更多文章

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017