arm-linux东东之nand

 

arm-linux东东nand:前记.

由于刚开始写blog.且本人也只不过是在校的一个学生.难免出错.如果错了请告诉我一下,谢谢

 

,nand 初始化 :

环境 :linux2.6.26,S3C2440 

LINUX NAND 驱动是在 DRIVERS/MTD 目录中 .

Makefile 指示 nand 是构架在 MTD 之上的 .

MTD 有字驱动 , 块驱动之分 , 还有一个是分区 . NAND 是块驱动 . 多数的 NAND 512 为一个寻址单位 . 32 512 为一个 block. 这是从 Makefile 开始

obj-$(CONFIG_MTD)          += mtd.o

mtd-y                           := mtdcore.o mtdsuper.o

mtd-$(CONFIG_MTD_PARTITIONS) += mtdpart.o

obj-$(CONFIG_MTD_CONCAT)  += mtdconcat.o

obj-$(CONFIG_MTD_REDBOOT_PARTS) += redboot.o

obj-$(CONFIG_MTD_CMDLINE_PARTS) += cmdlinepart.o

obj-$(CONFIG_MTD_AFS_PARTS)    += afs.o

obj-$(CONFIG_MTD_AR7_PARTS)    += ar7part.o

obj-$(CONFIG_MTD_OF_PARTS)      += ofpart.o

# 'Users' - code which presents functionality to userspace.

obj-$(CONFIG_MTD_CHAR)             += mtdchar.o

obj-$(CONFIG_MTD_BLKDEVS)       += mtd_blkdevs.o

obj-$(CONFIG_MTD_BLOCK)           += mtdblock.o

obj-$(CONFIG_MTD_BLOCK_RO)     += mtdblock_ro.o

obj-$(CONFIG_FTL)           += ftl.o

obj-$(CONFIG_NFTL)         += nftl.o

obj-$(CONFIG_INFTL)        += inftl.o

obj-$(CONFIG_RFD_FTL)          += rfd_ftl.o

obj-$(CONFIG_SSFDC)              += ssfdc.o

obj-$(CONFIG_MTD_OOPS)             += mtdoops.o

nftl-objs         := nftlcore.o nftlmount.o

inftl-objs         := inftlcore.o inftlmount.o

obj-y              += chips/ maps/ devices/ nand/ onenand/

obj-$(CONFIG_MTD_UBI)          += ubi/

看到没有如果定义了 CONFIG_MTD_PARTITIONS 就说明支持分区 . 我们所要看的目录以及文件就是 mtd.o, mtdpart.o mtd_blkdevs.o

而首先看的是 devices 目录下的 s3c2410.c 这个文件 .

module_init(s3c2410_nand_init);

module_exit(s3c2410_nand_exit);

于是从 s3c2410_nand_init 开始

 

static int __i static struct platform_driver s3c2440_nand_driver = {

       .probe            = s3c2440_nand_probe,

       .remove          = s3c2410_nand_remove,

       .suspend  = s3c24xx_nand_suspend,

       .resume          = s3c24xx_nand_resume,

       .driver            = {

              .name      = "s3c2440-nand",

              .owner    = THIS_MODULE,

       },

};

nit s3c2410_nand_init(void)

{

       printk("S3C24XX NAND Driver, (c) 2004 Simtec Electronics/n");

 

       platform_driver_register(&s3c2412_nand_driver);

       platform_driver_register(&s3c2440_nand_driver);

       return platform_driver_register(&s3c2410_nand_driver);

}

很简单 . 就是打印一些东西 . 然后调用 platform_driver_register.

所有 platform 的构架都会调用 .probe. 但是有一个条件 : 就是 s3c2440_nand_driver 遇到了属于它自已的设备 .

在很久很久以前设备已经被注册进去了 . 只是我们的眼睛没有觉察到而已 .

linux-2.6.26/arch/arm/plat-s3c24xx/common-smdk.c

static struct mtd_partition smdk_default_nand_part[] = {

       [0] = {

              .name      = "Boot Agent",

              .size = SZ_16K,

              .offset     = 0,

       },

       [1] = {

              .name      = "S3C2410 flash partition 1",

              .offset = 0,

              .size = SZ_2M,

       },

       [2] = {

              .name      = "S3C2410 flash partition 2",

              .offset = SZ_4M,

              .size = SZ_4M,

       },

       [3] = {

              .name      = "S3C2410 flash partition 3",

              .offset     = SZ_8M,

              .size = SZ_2M,

       },

       [4] = {

              .name      = "S3C2410 flash partition 4",

              .offset = SZ_1M * 10,

              .size = SZ_4M,

       },

       [5] = {

              .name      = "S3C2410 flash partition 5",

              .offset     = SZ_1M * 14,

              .size = SZ_1M * 10,

       },

       [6] = {

              .name      = "S3C2410 flash partition 6",

              .offset     = SZ_1M * 24,

              .size = SZ_1M * 24,

       },

       [7] = {

              .name      = "S3C2410 flash partition 7",

              .offset = SZ_1M * 48,

              .size = SZ_16M,

       }

};

 

static struct s3c2410_nand_set smdk_nand_sets[] = {

       [0] = {

              .name             = "NAND",

              .nr_chips = 1,

              .nr_partitions  = ARRAY_SIZE(smdk_default_nand_part),

              .partitions       = smdk_default_nand_part,

             

       },

};

 

/* choose a set of timings which should suit most 512Mbit

  * chips and beyond.

*/

 

static struct s3c2410_platform_nand smdk_nand_info = {

       .tacls              = 20,

       .twrph0          = 60,

       .twrph1          = 20,

       .nr_sets   = ARRAY_SIZE(smdk_nand_sets),

       .sets        = smdk_nand_sets,

};

 

/* devices we initialise */

 

static struct platform_device __initdata *smdk_devs[] = {

       &s3c_device_nand,

       &smdk_led4,

       &smdk_led5,

       &smdk_led6,

       &smdk_led7,

};

platform_add_devices(smdk_devs, ARRAY_SIZE(smdk_devs));

看到没有 s3c_device_nand. 这就是设备 . 没有错 .

这里有几个结构体 : smdk_default_nand_part 表示分区 . 移植的同志们对这个是很敏感的 .

smdk_nand_sets 表示有几块 NAND.

请记着有这么些东西 . 因为在后面我们会回来 .

 

 

  3c2440_nand_probe

如果你没有意见 . 我们开始进入 3c2440_nand_probe. 这人函数可是干活的家伙 . 故事就是从这里开始的 .

static int s3c2440_nand_probe(struct platform_device *dev)

{

       return s3c24xx_nand_probe(dev, TYPE_S3C2440);

}

static int s3c24xx_nand_probe(struct platform_device *pdev,

                           enum s3c_cpu_type cpu_type)

{

       struct s3c2410_platform_nand *plat = to_nand_plat(pdev);

       struct s3c2410_nand_info *info;

       struct s3c2410_nand_mtd *nmtd;

       struct s3c2410_nand_set *sets;

       struct resource *res;

       int err = 0;

       int size;

       int nr_sets;

       int setno;

 

       pr_debug("s3c2410_nand_probe(%p)/n", pdev);

 

       info = kmalloc(sizeof(*info), GFP_KERNEL);

/*

   

*/

       if (info == NULL) {

       dev_err(&pdev->dev, "no memory for flash info/n");

       err = -ENOMEM;

       goto exit_error;

       }

 

       memzero(info, sizeof(*info));

 

      

       platform_set_drvdata(pdev, info);

 

       spin_lock_init(&info->controller.lock);

       init_waitqueue_head(&info->controller.wq);

 

       /* get the clock source and enable it */

 

       info->clk = clk_get(&pdev->dev, "nand");

       if (IS_ERR(info->clk)) {

       dev_err(&pdev->dev, "failed to get clock/n");

       err = -ENOENT;

       goto exit_error;

       }

 

       clk_enable(info->clk);

 

       /* allocate and map the resource */

 

       /* currently we assume we have the one resource */

       res  = pdev->resource;

       size = res->end - res->start + 1;

/////////////////////////////////////////////////////

       info->area = request_mem_region(res->start, size, pdev->name);

 

       if (info->area == NULL) {

       dev_err(&pdev->dev, "cannot reserve register region/n");

       err = -ENOENT;

       goto exit_error;

       }

 

       info->device     = &pdev->dev;

       info->platform   = plat;

       info->regs       = ioremap(res->start, size);

       info->cpu_type   = cpu_type;

 

       if (info->regs == NULL) {

       dev_err(&pdev->dev, "cannot reserve register region/n");

       err = -EIO;

       goto exit_error;

       }

 

       dev_dbg(&pdev->dev, "mapped registers at %p/n", info->regs);

 

       /* initialise the hardware */

 

       err = s3c2410_nand_inithw(info, pdev);

       if (err != 0)

              goto exit_error;

       sets = (plat != NULL) ? plat->sets : NULL;

       nr_sets = (plat != NULL) ? plat->nr_sets : 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) {

       dev_err(&pdev->dev, "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);

 

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

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

 

       if (nmtd->scan_res == 0) {

       s3c2410_nand_update_chip(info, nmtd);

       nand_scan_tail(&nmtd->mtd);

       s3c2410_nand_add_partition(info, nmtd, sets);

       }

 

       if (sets != NULL)

       sets++;

       }

 

       if (allow_clk_stop(info)) {

       dev_info(&pdev->dev, "clock idle support enabled/n");

       clk_disable(info->clk);

       }

 

       pr_debug("initialised ok/n");

       return 0;

 

  exit_error:

       s3c2410_nand_remove(pdev);

 

       if (err == 0)

              err = -EINVAL;

       return err;

}    

好家伙 . 那么长 . 吓人一大跳 . 还好很多函数都是明白的 . Plat 就当初我们说的

static struct s3c2410_platform_nand smdk_nand_info = {

       .tacls              = 20,

       .twrph0          = 60,

       .twrph1          = 20,

       .nr_sets   = ARRAY_SIZE(smdk_nand_sets),

       .sets        = smdk_nand_sets,

};

info = kmalloc(sizeof(*info), GFP_KERNEL); info 分配内存 .

platform_set_drvdata(pdev, info);pdev info 关联 .

这些都是比较统一函数结构 .

来看下这个函数 : s3c2410_nand_inithw

static int s3c2410_nand_inithw(struct s3c2410_nand_info *info,

                            struct platform_device *pdev)

{

       struct s3c2410_platform_nand *plat = to_nand_plat(pdev);

       unsigned long clkrate = clk_get_rate(info->clk);

       int tacls_max = (info->cpu_type == TYPE_S3C2412) ? 8 : 4;

       int tacls, twrph0, twrph1;

       unsigned long cfg = 0;

 

       /* calculate the timing information for the controller */

 

       clkrate /= 1000;      /* turn clock into kHz for ease of use */

if (plat != NULL) {

              tacls = s3c_nand_calc_rate(plat->tacls, clkrate, tacls_max);

              twrph0 = s3c_nand_calc_rate(plat->twrph0, clkrate, 8);

              twrph1 = s3c_nand_calc_rate(plat->twrph1, clkrate, 8);

       } else {

              /* default timings */

              tacls = tacls_max;

              twrph0 = 8;

              twrph1 = 8;

       }

 

       if (tacls < 0 || twrph0 < 0 || twrph1 < 0) {

              dev_err(info->device, "cannot get suitable timings/n");

              return -EINVAL;

       }

 

       dev_info(info->device, "Tacls=%d, %dns Twrph0=%d %dns, Twrph1=%d %dns/n",

              tacls, to_ns(tacls, clkrate), twrph0, to_ns(twrph0, clkrate), twrph1, to_ns(twrph1, clkrate));

 

       switch (info->cpu_type) {

       case TYPE_S3C2410:

              cfg = S3C2410_NFCONF_EN;

              cfg |= S3C2410_NFCONF_TACLS(tacls - 1);

              cfg |= S3C2410_NFCONF_TWRPH0(twrph0 - 1);

              cfg |= S3C2410_NFCONF_TWRPH1(twrph1 - 1);

              break;

 

       case TYPE_S3C2440:

       case TYPE_S3C2412:

              cfg = S3C2440_NFCONF_TACLS(tacls - 1);

              cfg |= S3C2440_NFCONF_TWRPH0(twrph0 - 1);

              cfg |= S3C2440_NFCONF_TWRPH1(twrph1 - 1);

 

              /* enable the controller and de-assert nFCE */

 

              writel(S3C2440_NFCONT_ENABLE, info->regs + S3C2440_NFCONT);

       }

 

       dev_dbg(info->device, "NF_CONF is 0x%lx/n", cfg);

 

       writel(cfg, info->regs + S3C2410_NFCONF);

       return 0;

}

这个函数是总线的设定 . 如何设定呢 ? 先来说下几个数据 .

 

 

 

 

 
 

Tacls 是当 CLE/ALE 有效时过了多少时间后 nwe 才有效 .

TWRPH0 nwe 的有效时间 .

TWRPH1 是当 nWE 无效后 DATA 的保持时间 .

 

这里 tcs,twp,tclh 就是我们要的

       .tacls              = 20,

       .twrph0          = 60,

       .twrph1          = 20,

注意单位是 ns.

好回到我们的 s3c2410_nand_inithw 中来 .

tacls_max 是怎么算的呢 ? 还不是 S3C2440 TACLS 只占了二位 . 二位就是 4. ===== 真叨

clkrate NAND 的源时钟 ./1000 把它转换成 KHZ

plat 是不为 NULL , 于是

#define NS_IN_KHZ 1000000

 

static int s3c_nand_calc_rate(int wanted, unsigned long clk, int max)

{

       int result;

 

       result = (wanted * clk) / NS_IN_KHZ;

       result++;

 

       pr_debug("result %d from %ld, %d/n", result, clk, wanted);

 

       if (result > max) {

              printk("%d ns is too big for current clock rate %ld/n", wanted, clk);

              return -1;

       }

 

       if (result < 1)

              result = 1;

 

       return result;

}

这里怎么算呢 . 举个例子 :(1/HZ)*n=20ns

于是 n=HZ*(20ns) 由于这里的单位是 ns 所于除了 NS_IN_KHZ 纯数学问题 very simple.

Result 算出来以后就返回了 .

好返回 s3c2410_nand_inithw 中来 :

twrph0 = s3c_nand_calc_rate(plat->twrph0, clkrate, 8);

twrph1 = s3c_nand_calc_rate(plat->twrph1, clkrate, 8);

这两个也是一样的 .

来看一下 switch(info->cpu_type) 这个句子

 

       case TYPE_S3C2440:

       case TYPE_S3C2412:

              cfg = S3C2440_NFCONF_TACLS(tacls - 1);

              cfg |= S3C2440_NFCONF_TWRPH0(twrph0 - 1);

              cfg |= S3C2440_NFCONF_TWRPH1(twrph1 - 1);

这里为什么减一呢 . 原来是这样的

 

 
 
 
 

算的时候它自动加 1 . 三星的东西很多都是这样的 . 这里算的时候也不是很严的时间限制 . 有没有注意到上面 result++. 不管怎么样误差不是太大就行了 .

writel(S3C2440_NFCONT_ENABLE, info->regs + S3C2440_NFCONT);

#define S3C2440_NFCONT_ENABLE         (1<<0)

NFCONF 有个开始 / 禁止控制位 . 这里开启使 nand 控制器跑起来 .

s3c2410_nand_inithw 完了 . 返回到 s3c24xx_nand_probe 中来 .

sets = (plat != NULL) ? plat->sets : NULL;

nr_sets = (plat != NULL) ? plat->nr_sets : 1;

set 就是开始那个 .

set _sets 就是 1, 表示我只有一块 NAND.

 

805 行分配一个 mtd. Info 是这么样的一个结构体 .

struct s3c2410_nand_info {

       /* mtd info */

       struct nand_hw_control        controller;

       struct s3c2410_nand_mtd            *mtds;

       struct s3c2410_platform_nand      *platform;

 

       /* device info */

       struct device                 *device;

       struct resource                     *area;

       struct clk               *clk;

       void __iomem               *regs;

       void __iomem               *sel_reg;

       int                         sel_bit;

       int                         mtd_count;

       unsigned long                save_sel;

 

       enum s3c_cpu_type              cpu_type;

};

struct s3c2410_nand_mtd {

       struct mtd_info                    mtd;

       struct nand_chip           chip;

       struct s3c2410_nand_set              *set;

       struct s3c2410_nand_info     *info;

       int                         scan_res;

};

 

其中 mtds 里面的 mtd 就表示 NAND. 它表示所有分区的 master. 如果没有分区的话 . 这个 mtd 就会添加到分区表中去 . 如果有分区 . 则不会添加到分区中 . 而是作为所有分区的 master.

816 nmtd 指向我们刚才分配的 mtds

 

 
 
 
 

到了 818 . 那个 for 只会循环一次 . 因为我们的 nr_sets 1 .

进入 s3c2410_nand_init_chip 一段一段来 :

 

 

 
 
 

602 行使 chip 指向 nmtd 内的 chip.chip 就表示一块芯片 . 这是高一层的结构体 .

/**

  * struct nand_chip - NAND Private Flash Chip Data

  * @IO_ADDR_R:        [BOARDSPECIFIC] address to read the 8 I/O lines of the flash device

  * @IO_ADDR_W:              [BOARDSPECIFIC] address to write the 8 I/O lines of the flash device

  * @read_byte:             [REPLACEABLE] read one byte from the chip

  * @read_word:           [REPLACEABLE] read one word from the chip

  * @write_buf:             [REPLACEABLE] write data from the buffer to the chip

  * @read_buf:              [REPLACEABLE] read data from the chip into the buffer

  * @verify_buf:            [REPLACEABLE] verify buffer contents against the chip data

  * @select_chip:    [REPLACEABLE] select chip nr

  * @block_bad:            [REPLACEABLE] check, if the block is bad

  * @block_markbad:     [REPLACEABLE] mark the block bad

  * @cmd_ctrl:              [BOARDSPECIFIC] hardwarespecific funtion for controlling

  *                  ALE/CLE/nCE. Also used to write command and address

  * @dev_ready:            [BOARDSPECIFIC] hardwarespecific function for accesing device ready/busy line

  *                  If set to NULL no access to ready/busy is available and the ready/busy information

  *                  is read from the chip status register

  * @cmdfunc:              [REPLACEABLE] hardwarespecific function for writing commands to the chip

  * @waitfunc:              [REPLACEABLE] hardwarespecific function for wait on ready

  * @ecc:        [BOARDSPECIFIC] ecc control ctructure

  * @buffers:         buffer structure for read/write

  * @hwcontrol:            platform-specific hardware control structure

  * @ops:        oob operation operands

  * @erase_cmd:           [INTERN] erase command write function, selectable due to AND support

  * @scan_bbt:              [REPLACEABLE] function to scan bad block table

  * @chip_delay:            [BOARDSPECIFIC] chip dependent delay for transfering data from array to read regs (tR)

  * @wq:               [INTERN] wait queue to sleep on if a NAND operation is in progress

  * @state:             [INTERN] the current state of the NAND device

  * @oob_poi:        poison value buffer

  * @page_shift:            [INTERN] number of address bits in a page (column address bits)

  * @phys_erase_shift:   [INTERN] number of address bits in a physical eraseblock

  * @bbt_erase_shift:     [INTERN] number of address bits in a bbt entry

  * @chip_shift:             [INTERN] number of address bits in one chip

  * @datbuf:           [INTERN] internal buffer for one page + oob

  * @oobbuf:          [INTERN] oob buffer for one eraseblock

  * @oobdirty:        [INTERN] indicates that oob_buf must be reinitialized

  * @data_poi:        [INTERN] pointer to a data buffer

  * @options:         [BOARDSPECIFIC] various chip options. They can partly be set to inform nand_scan about

  *                  special functionality. See the defines for further explanation

  * @badblockpos:  [INTERN] position of the bad block marker in the oob area

  * @cellinfo:         [INTERN] MLC/multichip data from chip ident

  * @numchips:             [INTERN] number of physical chips

  * @chipsize:        [INTERN] the size of one chip for multichip arrays

  * @pagemask:             [INTERN] page number mask = number of (pages / chip) - 1

  * @pagebuf:        [INTERN] holds the pagenumber which is currently in data_buf

  * @subpagesize:   [INTERN] holds the subpagesize

  * @ecclayout:             [REPLACEABLE] the default ecc placement scheme

  * @bbt:        [INTERN] bad block table pointer

  * @bbt_td:           [REPLACEABLE] bad block table descriptor for flash lookup

  * @bbt_md:         [REPLACEABLE] bad block table mirror descriptor

  * @badblock_pattern:  [REPLACEABLE] bad block scan pattern used for initial bad block scan

  * @controller:             [REPLACEABLE] a pointer to a hardware controller structure

  *                  which is shared among multiple independend devices

  * @priv:              [OPTIONAL] pointer to private chip date

  * @errstat:           [OPTIONAL] hardware specific function to perform additional error status checks

  *                  (determine if errors are correctable)

  * @write_page:           [REPLACEABLE] High-level page write function

  */

 

struct nand_chip {

          // 数据写地址

       void  __iomem      *IO_ADDR_R;

       // 数据读地址

       void  __iomem      *IO_ADDR_W;

 

       uint8_t    (*read_byte)(struct mtd_info *mtd);

       ////////////////////////

       u16         (*read_word)(struct mtd_info *mtd);

       ///////////////////////

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

       int           (*verify_buf)(struct mtd_info *mtd, const 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           (*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);

       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,

                                  const uint8_t *buf, int page, int cached, int raw);

 

       int           chip_delay;

       unsigned int    options;

 

       int           page_shift;

       int           phys_erase_shift;

       int           bbt_erase_shift;

       int           chip_shift;

       int           numchips;

       unsigned long  chipsize;

       int           pagemask;

       int           pagebuf;

       int           subpagesize;

       uint8_t           cellinfo;

       int           badblockpos;

 

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

 

       struct mtd_oob_ops ops;

 

       uint8_t           *bbt;

       struct nand_bbt_descr   *bbt_td;

       struct nand_bbt_descr   *bbt_md;

 

       struct nand_bbt_descr   *badblock_pattern;

 

       void        *priv;

};

如果不是在大学混了三年 . 看到这样的结构休 . 我老早就溜了 .

还好俺也算知识混子

605 612 chip 初始化 . 这些值用到的时候我们再来说 . 我会时时提醒你 .

624 IO_ADDR_W 写地址 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;

NFCONT 有这么样一个位 :

 

 

0 表示 Enable chip select

s3c2410_nand_select_chip 中我们会用上的 . 待得瞧 .

回来看 s3c2410_nand_init_chip:

chip->IO_ADDR_R = chip->IO_ADDR_W;

 

nmtd->info        = info;

nmtd->mtd.priv         = chip;

nmtd->mtd.owner    = THIS_MODULE;

nmtd->set          = set;

读地址与写地址是一样 .

传说中的 ECC 出来了 .

static int hardware_ecc = 1; 表示要 ECC. 什么是 ECC: 就是对数据的保护 . 数据传送有没有出错 .ECC 有两种 . 一种是通过软件计算出的 . 一种是通过硬件算出来的 . S3C2440 来说它的 ECC 是硬件算出来的 . 怎么算 ???? 当写数据时 : 例如写 512 个数据到 NAND , 512 写完以后 ECC 就会被算出 , 放在 NFMCCD0-NFMCCD2 . 这个过程是全自动的 .

 

 

但有人就会问生成以后呢 .?? 有没有注意到上面 . 512 来说其实 1 page=528

多了 16 .ECC 就是放在这 16 个当中的 .

NAND_ECC_SOFT 表示软件生成 ECC.

683 set->disable_ecc 如果为 1 就表示是hi ECC你不用检测了. .

我们顺便把 s3c2410_nand_calculate_ecc 也看了 .

static int s3c2410_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code)

{

       struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);

 

       ecc_code[0] = readb(info->regs + S3C2410_NFECC + 0);

       ecc_code[1] = readb(info->regs + S3C2410_NFECC + 1);

       ecc_code[2] = readb(info->regs + S3C2410_NFECC + 2);

 

       pr_debug("%s: returning ecc %02x%02x%02x/n", __func__,

                ecc_code[0], ecc_code[1], ecc_code[2]);

 

       return 0;

}

这个函数就是读一下 ECC 寄存器 .

s3c2410_nand_init_chip 就这样完了 . chip 还没有完 . 回到 s3c24xx_nand_probe 中来

823 行调用 nand_scan_ident 这个函数可不是打杂的 .

 

 

 

 

 

 

nand_scan_ident 出生

读这样的代码 . 会不会觉得我 很无聊 . 不过还得继续 nand_scan_ident.

2403 行如果你的 NAND 16 , 就必须在进入 nand_scan_ident 之前做这样的一个语句 :

Chip->options | =NAND_BUSWIDTH_16, 表示我的 NAND 16 数据宽的 .

2405 行调用 nand_set_defaults

 

 

 
 

chip . 没有给的 . 这里给 defaults.

对照一下 s3c2410_nand_init_chip

 

 
 
 
 
 

好回到 nand_scan_ident :

 
 

2404 行调用 nand_get_flash_type

 
 

2243 行调用 select_chip. 不知道你记不记得以前我们说过的

       chip->select_chip  = s3c2410_nand_select_chip;

于是进入 s3c2410_nand_select_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);
}
 

这里 chip 就是 0

250 行就会被调用

251 行读寄存器 NFCONF. 还记得不 :

       info->sel_reg   = regs + S3C2440_NFCONT;

       info->sel_bit   = S3C2440_NFCONT_nFCE;

265 行取反 . 也就是以前我们说的 . Enable chip select

经过这么一调用 NAND 控制器就跑起来了 .

回到 nand_get_flash_type 中来 .

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

接下来要说 NAND 协议了 . 请注意 .

于是调用

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

 

我们来看下参数 .mtd 没有问题吧 .command 是命令 . 大概就有这么些命令 :

#define NAND_CMD_READ0             0

#define NAND_CMD_READ1             1

#define NAND_CMD_RNDOUT          5

#define NAND_CMD_PAGEPROG      0x10

#define NAND_CMD_READOOB 0x50

#define NAND_CMD_ERASE1            0x60

#define NAND_CMD_STATUS            0x70

#define NAND_CMD_STATUS_MULTI      0x71

#define NAND_CMD_SEQIN              0x80

#define NAND_CMD_RNDIN             0x85

#define NAND_CMD_READID           0x90

#define NAND_CMD_ERASE2            0xd0

#define NAND_CMD_RESET             0xff

而这里的命令就是 : NAND_CMD_READID

Column 是列地址 . page_addr 是页地址 . 很多 NAND 一页就是 512+16.

写一页数据怎么写呢 ?

一页分 A /B /C

A 区就是 512 的上半部就是前一个 256

B 区就是 512 的下半部就是后一个 256

C 区就是那 16

通过命令选中区以后就可以通过 Column 来选择那个字

OK. 很多 NAND 又分块 . 一块有几个页

 

 
 

这个图说明 : 一个块有 32 个页也就是说在 page_addr 分出 5 位来对页寻址 . 其他的就对块寻址 . 于是就这样发地址 :

 

 

A0-A7 就是 Column 地址 A9-A13 就是页地址 A14-A25 就块地址 . 当然你的 NAND 可能与我的不同 . 我的是 64M .*L 表示为 0.

 

 

 

Arm-linux 东东 nand 4: nand_command

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

我们一段一段来 . 因为不能添图片了 , 实在大多图了 .

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;

       }

NAND_CTRL_CLE 表示是发送命令 . 等下会看到如果设了 NAND_CTRL_CLE S3C2440 就会将数据写到命令寄存器中去 . NAND_CTRL_CHANGE 用的比较少 . 表示 CHANGE.

如果命令是 NAND_CMD_SEQIN 就表示有数据了 . 还记不记得说过一页分为 A B C . 那总该选一下吧 . 如果 column 大于 writesize 假设是 512 的页那么就是大于 512 就表示 C . 就发送这样的命令给它 NAND_CMD_READOOB    0x50, 其他以此类推 . 就不说了 .

有些人就会问了 . 选中了一个区 . 而我的数据超过了那个区的容量 . 那怎么办呀 :

 
 

看到没有如果是 A 区开始 It depends on how many data are inputted 也就是可以超界 . 谁叫我是 A 区呢 . 从图可以看到只要下面还有区就可以超过 .

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

ctrl &= ~NAND_CTRL_CHANGE;

这两句就是发送命令选择区了 .ctrl 就更改了值 . 表示 chage. 关于怎么样发送 . 后面说 .

 

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

       }

接下来就要发送地址了 . NAND_CTRL_ALE 就表示地址 .column -1 就表示没有地址 . 这里很好理解了 . 来看一下这个 :

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

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

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

如果 NAND 的容量超过了 32M 才会有第三个页地址 .

…………………….

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 的命令图这一段看懂应该没有问题

chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1); 这个命令来说 :

ECH 就是厂商 ID,Device Code 就设备 ID. 这是三星的 NAND

nand_command 就这样完了 . 完了 .

还有一个就是 : chip->cmd_ctrl

对于 S3C2440 来说就是 :

static void s3c2440_nand_hwcontrol(struct mtd_info *mtd, int cmd,

                               unsigned int ctrl)

{

       struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);

 

       if (cmd == NAND_CMD_NONE)

              return;

 

       if (ctrl & NAND_CLE)

              writeb(cmd, info->regs + S3C2440_NFCMD);

       else

              writeb(cmd, info->regs + S3C2440_NFADDR);

}

NFCMD 就是命令寄存器 .NFADDR 就是地址寄存器 .

#define NAND_CLE             0x02

/* Select the address latch by setting ALE to high */

#define NAND_ALE             0x04

 

#define NAND_CTRL_CLE          (NAND_NCE | NAND_CLE)

#define NAND_CTRL_ALE          (NAND_NCE | NAND_ALE)

 

NAND_CTRL_CLE       就是 NAND_CLE |   NAND_NCE

返回了 . 真的完了 .回来再看 nand_get_flash_type 

 

 

 

Arm-linux 东东之 5: nand_get_flash_type

……………………………………………..

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

       }

命令发完了就可以读了 . 然后再发一次同样的命令 . 如果两次获得的 ID 不同就说明出错了 . 没用的 NAND.

……………………….

       /* 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 = type->chipsize << 20;

………………………………………

Nand_flash_id 就是这些 :

struct nand_flash_dev nand_flash_ids[] = {

…………………

 

       {"NAND 64MiB 1,8V 8-bit",  0x36, 512, 64, 0x4000, 0},

       {"NAND 64MiB 3,3V 8-bit",  0x76, 512, 64, 0x4000, 0},

       {"NAND 64MiB 1,8V 16-bit",       0x46, 512, 64, 0x4000, NAND_BUSWIDTH_16},

       {"NAND 64MiB 3,3V 16-bit",       0x56, 512, 64, 0x4000, NAND_BUSWIDTH_16},

 

       {"NAND 128MiB 1,8V 8-bit",       0x78, 512, 128, 0x4000, 0},

       {"NAND 128MiB 1,8V 8-bit",       0x39, 512, 128, 0x4000, 0},

       {"NAND 128MiB 3,3V 8-bit",       0x79, 512, 128, 0x4000, 0},

       {"NAND 128MiB 1,8V 16-bit",     0x72, 512, 128, 0x4000, NAND_BUSWIDTH_16},

       {"NAND 128MiB 1,8V 16-bit",     0x49, 512, 128, 0x4000, NAND_BUSWIDTH_16},

       {"NAND 128MiB 3,3V 16-bit",     0x74, 512, 128, 0x4000, NAND_BUSWIDTH_16},

       {"NAND 128MiB 3,3V 16-bit",     0x59, 512, 128, 0x4000, NAND_BUSWIDTH_16},

/*

         * Renesas AND 1 Gigabit. Those chips do not support extended id and

         * have a strange page/block layout !  The chosen minimum erasesize is

         * 4 * 2 * 2048 = 16384 Byte, as those chips have an array of 4 page

         * planes 1 block = 2 pages, but due to plane arrangement the blocks

         * 0-3 consists of page 0 + 4,1 + 5, 2 + 6, 3 + 7 Anyway JFFS2 would

         * increase the eraseblock size so we chose a combined one which can be

         * erased in one go There are more speed improvements for reads and

         * writes possible, but not implemented now

         */

       {"AND 128MiB 3,3V 8-bit",  0x01, 2048, 128, 0x4000,

         NAND_IS_AND | NAND_NO_AUTOINCR |NAND_NO_READRDY | NAND_4PAGE_ARRAY |

         BBT_AUTO_REFRESH

       },

 

       {NULL,}

};

每一种类 NAND 在这里都有个表 . 如果在这里找不到属于你 NAND 的表就 game over

struct nand_flash_dev {

       char *name;

       int id;

       unsigned long pagesize;

       unsigned long chipsize;

       unsigned long erasesize;

       unsigned long options;

}; 来看一下上面这个结构体 .pagesize 就是一页的大小 . 一般就是 512. 注意呀不是 528.chipsize 就是容量 .64M 的就是 64.erasesize 就是咱们前面说的块大小 .NAND 是以块为 erase 单位的 .

Options 就是一些宏的组合 .

{"AND 128MiB 3,3V 8-bit",  0x01, 2048, 128, 0x4000,

         NAND_IS_AND | NAND_NO_AUTOINCR |NAND_NO_READRDY | NAND_4PAGE_ARRAY |

         BBT_AUTO_REFRESH

       },

看到这个比较变态的 NAND 没有 ?? 属于 options 的宏都用上了 . 这里不说这个 NAND, 因为主流不是它 .

chip->chipsize = type->chipsize << 20;

这句计算容量 .1<<20 就是 1M.64<<20 就是 64M.

 

…………………………

f (!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;

       }

………………………………………

Pagesize 我们是有的 . 所以跳到 else 来了 .

来说下这个 :

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

512/32 就是 16. 就是上面说的 C .

……………

 

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

              if (nand_manuf_ids[maf_idx].id == *maf_id)

                     break;

       }

 

…struct nand_manufacturers nand_manuf_ids[] = {

       {NAND_MFR_TOSHIBA, "Toshiba"},

       {NAND_MFR_SAMSUNG, "Samsung"},

       {NAND_MFR_FUJITSU, "Fujitsu"},

       {NAND_MFR_NATIONAL, "National"},

       {NAND_MFR_RENESAS, "Renesas"},

       {NAND_MFR_STMICRO, "ST Micro"},

       {NAND_MFR_HYNIX, "Hynix"},

       {NAND_MFR_MICRON, "Micron"},

       {NAND_MFR_AMD, "AMD"},

       {0x0, "Unknown"}

};…………..

  这个没有问题 . 就是找厂商吗 !!!!

………………………………………………..

       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;

       chip->chip_shift = ffs(chip->chipsize) - 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;

………………………………………………….

Busw 是传进来的参数 . 如果传进来的参数与表中的不一致就出错 .

chip->page_shift = ffs(mtd->writesize) - 1;

后面会看到当给定一个 32 的地址 .add>>chip->page_shift. 因为 NAND 是页寻址的 .512 的页就是右移 9 . Pagemask 就是用来限制地址高位 .64M 的就只用 17 位有效地址所以高于 17 mask .chip_shift 是用于多 NAND 中的 . 其他的我们用到的时候再说 . 好吧 !!

……………………………

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;

}

………………….

我们的 erase_cmd 是等于 single_erase_cmd ;

nand_get_flash_type 就这样完了 .

回到 nand_scan_ident 中来 .

……………….

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

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

}

…………………..

这里呀 maxchips 是为 1 . 那个 for 不会执行的 . 不过多于一块的 NAND 也容易搞懂 .

又回到了 s3c24xx_nand_probe 中了 .

………………………………..

if (nmtd->scan_res == 0) {

                     s3c2410_nand_update_chip(info, nmtd);

                     nand_scan_tail(&nmtd->mtd);

                     s3c2410_nand_add_partition(info, nmtd, sets);

              }

……………………………………

只余三个函数了 . 不过千万不要小看这三个函数 . 个个都他妈的难 .

 

 

 

 

Arm-linux 东东 nand 之6 : s3c2410_nand_update_chip

先来看简单的 s3c2410_nand_update_chip

static void s3c2410_nand_update_chip(struct s3c2410_nand_info *info,

                                 struct s3c2410_nand_mtd *nmtd)

{

       struct nand_chip *chip = &nmtd->chip;

 

       printk("%s: chip %p: %d/n", __func__, chip, chip->page_shift);

 

       if (hardware_ecc) {

              /* change the behaviour depending on wether we are using

                * the large or small page nand device */

 

              if (chip->page_shift > 10) {

                     chip->ecc.size     = 256;

                     chip->ecc.bytes         = 3;

              } else {

                     chip->ecc.size     = 512;

                     chip->ecc.bytes         = 3;

                     chip->ecc.layout    = &nand_hw_eccoob;

              }

       }

}

这里跑出这么样的结构体 :

/**

  * struct nand_ecc_ctrl - Control structure for ecc

  * @mode:     ecc mode

  * @steps:     number of ecc steps per page

  * @size:       data bytes per ecc step

  * @bytes:     ecc bytes per step

  * @total:       total number of ecc bytes per page

  * @prepad:   padding information for syndrome based ecc generators

  * @postpad:  padding information for syndrome based ecc generators

  * @layout:    ECC layout control struct pointer

  * @hwctl:     function to control hardware ecc generator. Must only

  *           be provided if an hardware ECC is available

  * @calculate:       function for ecc calculation or readback from ecc hardware

  * @correct:  function for ecc correction, matching to ecc generator (sw/hw)

  * @read_page_raw:     function to read a raw page without ECC

  * @write_page_raw:    function to write a raw page without ECC

  * @read_page:     function to read a page according to the ecc generator requirements

  * @write_page:    function to write a page according to the ecc generator requirements

  * @read_oob:       function to read chip OOB data

  * @write_oob:     function to write chip OOB data

  */

struct nand_ecc_ctrl {

       nand_ecc_modes_t mode;

       int                  steps;

       int                  size;

       int                  bytes;

       int                  total;

       int                  prepad;

       int                  postpad;

       struct nand_ecclayout    *layout;

       void               (*hwctl)(struct mtd_info *mtd, int mode);

       int                  (*calculate)(struct mtd_info *mtd,

                                        const uint8_t *dat,

                                        uint8_t *ecc_code);

       int                  (*correct)(struct mtd_info *mtd, uint8_t *dat,

                                      uint8_t *read_ecc,

                                      uint8_t *calc_ecc);

       int                  (*read_page_raw)(struct mtd_info *mtd,

                                            struct nand_chip *chip,

                                            uint8_t *buf);

       void               (*write_page_raw)(struct mtd_info *mtd,

                                            struct nand_chip *chip,

                                            const uint8_t *buf);

       int                  (*read_page)(struct mtd_info *mtd,

                                        struct nand_chip *chip,

                                        uint8_t *buf);

       void               (*write_page)(struct mtd_info *mtd,

                                         struct nand_chip *chip,

                                         const uint8_t *buf);

       int                  (*read_oob)(struct mtd_info *mtd,

                                       struct nand_chip *chip,

                                       int page,

                                       int sndcmd);

       int                  (*write_oob)(struct mtd_info *mtd,

                                        struct nand_chip *chip,

                                        int page);

};

好长呀 . 这里 else 语句会执行 .

ecc.size 就是计算一次 ECC 的时候的大小 . 什么意思呀 >>

举个例子 . 我的硬件没有那么高级 . 只能算 256 Byte ECC. 512 是不是要分两次来发送 . 不然的话我没有办法计算 .

ecc.bytes 就是算一次 ECC 有多少字节 . 不同的算法有不同的 ECC 长度 . 这个好理解 .

static struct nand_ecclayout nand_hw_eccoob = {

       .eccbytes = 3,

       .eccpos = {0, 1, 2},

       .oobfree = {{8, 8}}

};

这个用到再说 . 反正就是一些值 .

这个函数就这样了 .

 

 

 

Arm-linux 东东 nand 7: nand_scan_tail

. 进入这个函数请大家打十二分精神 …… 因为这个函数会跑 MFC 那样长的路

………………….

int nand_scan_tail(struct mtd_info *mtd)

{

       int i;

       struct nand_chip *chip = mtd->priv;

 

       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;

chip 里有个 buffers:

struct nand_buffers {

       uint8_t    ecccalc[NAND_MAX_OOBSIZE];

       uint8_t    ecccode[NAND_MAX_OOBSIZE];

       uint8_t databuf[NAND_MAX_PAGESIZE + NAND_MAX_OOBSIZE];

};

这里 oob_poi 就是指向 databuf 的第 512 个字节 . 这个地址是放 ECC 数据的 .. 下面会看到 .

……………………      

if (!chip->ecc.layout) {

              switch (mtd->oobsize) {

              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;

              default:

                     printk(KERN_WARNING "No oob scheme defined for "

                            "oobsize %d/n", mtd->oobsize);

                     BUG();

              }

       }

 

       if (!chip->write_page)

              chip->write_page = nand_write_page;

 

       /*

         * check ECC mode, default to software if 3byte/512byte hardware ECC is

         * selected and we have 256 byte pagesize fallback to software ECC

         */

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

layout 是有值的 . 接下来就是 ecc 内的函数指针定值 . 还是用到再说 .

……………………….

switch (chip->ecc.mode) {

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

                     chip->ecc.read_oob = nand_read_oob_std;

              if (!chip->ecc.write_oob)

                     chip->ecc.write_oob = nand_write_oob_std;

 

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

                     printk(KERN_WARNING "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_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)

                     break;

              printk(KERN_WARNING "%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.write_page = nand_write_page_swecc;

              chip->ecc.read_oob = nand_read_oob_std;

              chip->ecc.write_oob = nand_write_oob_std;

              chip->ecc.size = 256;

              chip->ecc.bytes = 3;

              break;

 

       case NAND_ECC_NONE:

              printk(KERN_WARNING "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.write_oob = nand_write_oob_std;

              chip->ecc.size = mtd->writesize;

              chip->ecc.bytes = 0;

              break;

 

       default:

              printk(KERN_WARNING "Invalid NAND_ECC_MODE %d/n",

                     chip->ecc.mode);

              BUG();

       }

……………………….

ECC 又来了 . 还记得在 s3c2410_nand_init_chip 中有一句这样的吗 :

              chip->ecc.mode         = NAND_ECC_HW;

这里顺便说下 :ECC 检验错的问题 . 如果用硬件算 ECC 会出错 . 很多众人就干脆不用 . 为什么呢 . 因为 bootloader ECC 算法与这里硬件的算法不同 . 所以就出错 . 建议在 bootloader 中的 ECC 算法改成由硬件来算 .

先把下面的说完 . 等下还会回来的

……………………

chip->ecc.layout->oobavail = 0;

       for (i = 0; chip->ecc.layout->oobfree[i].length; 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) {

              printk(KERN_WARNING "Invalid ecc parameters/n");

              BUG();

       }

       chip->ecc.total = chip->ecc.steps * chip->ecc.bytes;

……………………..

Ecc.steps 就是算 ECC 有多少步了 . 如果 ecc.size 256 的那么就需要 2 . 这里的 ecc.size 512 的所以一步就行了 . ecc.total 就是所有步下来生成的 ecc 大小 …..

: 来看上面了 . 对于硬件算 ECC 来说

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

                     chip->ecc.read_oob = nand_read_oob_std;

              if (!chip->ecc.write_oob)

                     chip->ecc.write_oob = nand_write_oob_std;

这里只说二个函数就是 : nand_write_page_hwecc,, nand_read_page_hwecc

…………………………………………………………….

static void nand_write_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,

                              const uint8_t *buf)

{

       int i, eccsize = chip->ecc.size;

       int eccbytes = chip->ecc.bytes;

       int eccsteps = chip->ecc.steps;

       uint8_t *ecc_calc = chip->buffers->ecccalc;

       const uint8_t *p = buf;

       uint32_t *eccpos = chip->ecc.layout->eccpos;

 

       for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {

              chip->ecc.hwctl(mtd, NAND_ECC_WRITE);

              chip->write_buf(mtd, p, eccsize);

              chip->ecc.calculate(mtd, p, &ecc_calc[i]);

       }

 

       for (i = 0; i < chip->ecc.total; i++)

              chip->oob_poi[eccpos[i]] = ecc_calc[i];

 

       chip->write_buf(mtd, chip->oob_poi, mtd->oobsize);

}

……………………………………………………….

有多少步 for 的循环多少次 . 我没有骗你吧 .    write_buff 一次就算一下 ECC.

对于 nand_read_page_hwecc 就比较难了 .

……………………………

static int nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,

                            uint8_t *buf)

{

       int i, eccsize = chip->ecc.size;

       int eccbytes = chip->ecc.bytes;

       int eccsteps = chip->ecc.steps;

       uint8_t *p = buf;

       uint8_t *ecc_calc = chip->buffers->ecccalc;

       uint8_t *ecc_code = chip->buffers->ecccode;

       uint32_t *eccpos = chip->ecc.layout->eccpos;

 

       for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {

              chip->ecc.hwctl(mtd, NAND_ECC_READ);

              chip->read_buf(mtd, p, eccsize);

              chip->ecc.calculate(mtd, p, &ecc_calc[i]);

       }

       chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);

 

       for (i = 0; i < chip->ecc.total; i++)

              ecc_code[i] = chip->oob_poi[eccpos[i]];

 

       eccsteps = chip->ecc.steps;

       p = buf;

…………………………

这里红色的 read_buf 就是读上面写进去的 ECC. 放在 chip->oob_poi

上面的那个 ecc.calculate 就是在 ECC 寄存器的数据 .

ecc.layout 这里就用到了 . 拿出对照一下就清楚了 . 这里就不说了 .

………………..

for (i = 0 ; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {

              int stat;

 

              stat = chip->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]);

              if (stat < 0)

                     mtd->ecc_stats.failed++;

              else

                     mtd->ecc_stats.corrected += stat;

       }

       return 0;

…………………..

接下来就要看一下两个 ECC 的值能不能对的上 .

stat = chip->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]); 就是调用 :

s3c2410_nand_correct_data

static int s3c2410_nand_correct_data(struct mtd_info *mtd, u_char *dat,

                                 u_char *read_ecc, u_char *calc_ecc)

{

       struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);

       unsigned int diff0, diff1, diff2;

       unsigned int bit, byte;

 

       pr_debug("%s(%p,%p,%p,%p)/n", __func__, mtd, dat, read_ecc, calc_ecc);

 

       diff0 = read_ecc[0] ^ calc_ecc[0];

       diff1 = read_ecc[1] ^ calc_ecc[1];

       diff2 = read_ecc[2] ^ calc_ecc[2];

 

       pr_debug("%s: rd %02x%02x%02x calc %02x%02x%02x diff %02x%02x%02x/n",

                __func__,

                read_ecc[0], read_ecc[1], read_ecc[2],

                calc_ecc[0], calc_ecc[1], calc_ecc[2],

                diff0, diff1, diff2);

 

       if (diff0 == 0 && diff1 == 0 && diff2 == 0)

              return 0;         /* ECC is ok */

这里 diff0 = read_ecc[0] ^ calc_ecc[0]; 异或是吧 . 大一的时候老师说了 : 两个相同的值异或就是 0 如果 read_ecc[0] calc_ecc[0] 相同 . 那么 diff0 就是 0

       if (diff0 == 0 && diff1 == 0 && diff2 == 0)

              return 0;         /* ECC is ok */

如果个个都为 0. 那么很好 ECC 没有问题 ….

如果不为 0 ? 接着看 :

……………..

if (read_ecc[0] == 0xff && read_ecc[1] == 0xff && read_ecc[2] == 0xff

           && info->platform->ignore_unset_ecc)

              return 0;

………………….

为什么是等于 0XFF 就说明 ECC 没有问题呢 ?

NAND 出厂的时候由于没有写过那么所有的内容就是 0xFF. 如果某个 bootleader 在写 NAND 的时候并没有用 ECC, 那么其内容是不是还是 0xFF…OK

……………………

if (((diff0 ^ (diff0 >> 1)) & 0x55) == 0x55 &&

           ((diff1 ^ (diff1 >> 1)) & 0x55) == 0x55 &&

           ((diff2 ^ (diff2 >> 1)) & 0x55) == 0x55) {

              /* calculate the bit position of the error */

 

              bit  = ((diff2 >> 3) & 1) |

                     ((diff2 >> 4) & 2) |

                     ((diff2 >> 5) & 4);

 

              /* calculate the byte position of the error */

 

              byte = ((diff2 << 7) & 0x100) |

                     ((diff1 << 0) & 0x80)  |

                     ((diff1 << 1) & 0x40)  |

                     ((diff1 << 2) & 0x20)  |

                     ((diff1 << 3) & 0x10)  |

                     ((diff0 >> 4) & 0x08)  |

                     ((diff0 >> 3) & 0x04)  |

                     ((diff0 >> 2) & 0x02)  |

                     ((diff0 >> 1) & 0x01);

 

              dev_dbg(info->device, "correcting error bit %d, byte %d/n",

                     bit, byte);

 

              dat[byte] ^= (1 << bit);

              return 1;

       }

 

       /* if there is only one bit difference in the ECC, then

         * one of only a row or column parity has changed, which

         * means the error is most probably in the ECC itself */

 

       diff0 |= (diff1 << 8);

       diff0 |= (diff2 << 16);

 

       if ((diff0 & ~(1<<fls(diff0))) == 0)

              return 1;

 

       return -1;

}

…………………………………….

接下来就是有问题的了 . 但是还能够用 ECC 把数据恢复过来 , 谢天谢地 .

如果你的程序到了这里 , 相信这块 NAND 也不长命的了 ..

这里的算法不是很懂 . 所以没有办法说了 .

s3c2410_nand_correct_data 就完了 . 一直返回到 nand_scan_tail

中来 ………………..

 

 

 

Arm-linux 东东 nand 8: nand_scan_tail

应该叹一口气了 . 不得不佩服 nand_scan_tail. 因为下面要说坏块扫描了 .

…………….

       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:

                     mtd->subpage_sft = 2;

                     break;

              }

       }

       chip->subpagesize = mtd->writesize >> mtd->subpage_sft;

 

       /* Initialize state */

       chip->state = FL_READY;

 

       /* De-select the device */

       chip->select_chip(mtd, -1);

 

       /* Invalidate the pagebuffer reference */

       chip->pagebuf = -1;

 

       /* Fill in remaining MTD driver data */

       mtd->type = MTD_NANDFLASH;

       mtd->flags = MTD_CAP_NANDFLASH;

       mtd->erase = nand_erase;

       mtd->point = NULL;

       mtd->unpoint = NULL;

       mtd->read = nand_read;

       mtd->write = 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;

 

       /* propagate ecc.layout to mtd_info */

       mtd->ecclayout = chip->ecc.layout;

 

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

………………..

都是一些赋值没什么 . 看最后一句 . 当时我还以后它应该不会很长 . 可惜错了

nand_set_defaults 中应该有下面这个吧

       if (!chip->scan_bbt)

              chip->scan_bbt = nand_default_bbt;

………………

于是 :

int nand_default_bbt(struct mtd_info *mtd)

{

       struct nand_chip *this = mtd->priv;

 

       /* Default for AG-AND. We must use a flash based

         * bad block table as the devices have factory marked

         * _good_ blocks. Erasing those blocks leads to loss

         * of the good / bad information, so we _must_ store

         * this information in a good / bad table during

         * startup

         */

       if (this->options & NAND_IS_AND ) {

              /* Use the default pattern descriptors */

              if (!this->bbt_td) {

                     this->bbt_td = &bbt_main_descr;

                     this->bbt_md = &bbt_mirror_descr;

              }

              this->options |= NAND_USE_FLASH_BBT;

              return nand_scan_bbt(mtd, &agand_flashbased);

       }

 

       /* Is a flash based bad block table requested ? */

       if (this->options & NAND_USE_FLASH_BBT ) {

              /* Use the default pattern descriptors */

              if (!this->bbt_td) {

                     this->bbt_td = &bbt_main_descr;

                     this->bbt_md = &bbt_mirror_descr;

              }

              if (!this->badblock_pattern) {

this->badblock_pattern = (mtd->writesize > 512) ? &largepage_flashbased : &smallpage_flashbased;

              }

       } else {

              this->bbt_td = NULL;

              this->bbt_md = NULL;

              if (!this->badblock_pattern) {

                     this->badblock_pattern = (mtd->writesize > 512) ?

                         &largepage_memorybased : &smallpage_memorybased;

              }

       }

       return nand_scan_bbt(mtd, this->badblock_pattern);

}

static struct nand_bbt_descr smallpage_memorybased = {

       .options = NAND_BBT_SCAN2NDPAGE,

       .offs = 5,

       .len = 1,

       .pattern = scan_ff_pattern

};

红色的那几个标志我们没有吧 . 因为它们是上面说的 变态 ”NAND 的专利 .

OK 最后 else 会执行 .

badblock_pattern 是指向 smallpage_memorybased;

go go

 

Go go

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

              printk(KERN_ERR "nand_scan_bbt: Out of memory/n");

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

                     printk(KERN_ERR "nand_bbt: Can't scan flash and build the RAM-based BBT/n");

                     kfree(this->bbt);

                     this->bbt = NULL;

              }

              return res;

       }

 

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

              printk(KERN_ERR "nand_bbt: Out of memory/n");

              kfree(this->bbt);

              this->bbt = NULL;

              return -ENOMEM;

       }

 

       /* Is the bbt at a given page ? */

       if (td->options & NAND_BBT_ABSPAGE) {

              res = read_abs_bbts(mtd, buf, td, md);

       } else {

              /* Search the bad block table using a pattern in oob */

              res = search_read_bbts(mtd, buf, td, md);

       }

 

       if (res)

              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;

}

…………………………..

. 真长呀 . 在大学学了三年的 C 语言 . 书上从来没有看过这么长的函数 .

看红色的那一段 .

现在要扫描坏快 . 你总应该分出一些内存来记着那块是坏的吧 .

本来一块一字节很好理解

mtd->size >> this->bbt_erase_shift 就这样 .

但本着节约的精神 . 人家说一块 2 bit. 于是除于 4. 8bit 4 2bit 组成吗 !

我就不理解了 , 为什么再省一点一块 1bit . 我的 64M RAM 也不多呀 .

于是就成了这样子 : len = mtd->size >> (this->bbt_erase_shift + 2);

看褐色的那一段, 能成立吧 . 上面刚做过这样的 this->bbt_td = NULL;

看到 return res; , 下面的不用看了 .

…………….

static inline int nand_memory_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd)

{

       struct nand_chip *this = mtd->priv;

 

       bd->options &= ~NAND_BBT_SCANEMPTY;

       return create_bbt(mtd, this->buffers->databuf, bd, -1);

}

……………………………………………………

请注意这里取消了 NAND_BBT_SCANEMPTY.

…………………….

static int create_bbt(struct mtd_info *mtd, uint8_t *buf,

       struct nand_bbt_descr *bd, int chip)

{

       struct nand_chip *this = mtd->priv;

       int i, numblocks, len, scanlen;

       int startblock;

       loff_t from;

       size_t readlen;

 

       printk(KERN_INFO "Scanning device for bad blocks/n");

 

       if (bd->options & NAND_BBT_SCANALLPAGES)

              len = 1 << (this->bbt_erase_shift - this->page_shift);

       else {

              if (bd->options & NAND_BBT_SCAN2NDPAGE)

                     len = 2;

              else

                     len = 1;

       }

…………………….

 

一块不是由几个页组成的吧 . 那扫描一块是不是要全部页都要检查一下呢 . 还是 ?

NAND_BBT_SCANALLPAGES 表示全部页都要检查 . 这里没有这个标志 .

NAND_BBT_SCAN2NDPAGE 表示头二页检查一下就行了 . 这里设了 .

static struct nand_bbt_descr smallpage_memorybased = {

       .options = NAND_BBT_SCAN2NDPAGE,

记得不 .

………………………

if (!(bd->options & NAND_BBT_SCANEMPTY )) {

              /* We need only read few bytes from the OOB area */

              scanlen = 0;

              readlen = bd->len;

       } else {

              /* Full page content should be read */

              scanlen = mtd->writesize + mtd->oobsize;

              readlen = len * mtd->writesize;

       }

………………………………………

以前取消掉了这个 NAND_BBT_SCANEMPTY

看下这个 : We need only read few bytes from the OOB area.OK

……………….

if (chip == -1) {

              /* Note that numblocks is 2 * (real numblocks) here, see i+=2

                * below as it makes shifting and masking less painful */

              numblocks = mtd->size >> (this->bbt_erase_shift - 1);

              startblock = 0;

              from = 0;

       } else {

              if (chip >= this->numchips) {

                     printk(KERN_WARNING "create_bbt(): chipnr (%d) > available chips (%d)/n",

                            chip + 1, this->numchips);

                     return -EINVAL;

              }

              numblocks = this->chipsize >> (this->bbt_erase_shift - 1);

              startblock = chip * numblocks;

              numblocks += startblock;

              from = startblock << (this->bbt_erase_shift - 1);

       }

………………

Chip 是不等于 -1 .numblocks = mtd->size >> (this->bbt_erase_shift - 1); 这里乘以 2, 因为少移一位了 .

startblock = 0; 表示第 0 块开始

………………..

for (i = startblock; i < numblocks;) {

              int ret;

 

              if (bd->options & NAND_BBT_SCANALLPAGES)

                     ret = scan_block_full(mtd, bd, from, buf, readlen,

                                         scanlen, len);

              else

                     ret = scan_block_fast(mtd, bd, from, buf, len);

 

              if (ret < 0)

                     return ret;

 

              if (ret) {

                     this->bbt[i >> 3] |= 0x03 << (i & 0x6);

                     printk(KERN_WARNING "Bad eraseblock %d at 0x%08x/n",

                            i >> 1, (unsigned int)from);

                     mtd->ecc_stats.badblocks++;

              }

 

              i += 2;

              from += (1 << this->bbt_erase_shift);

       }

       return 0;

…………………

进入 for 循环了 . 注意了 i+=2 是吧 . 为什么上面乘以 2 这里又每次加 2 .

看下面这个句子 :

this->bbt[i >> 3] |= 0x03 << (i & 0x6);

闭着眼睛都能知道就是找到那个块 2 bit , 如果还不懂 . 那就把数值代进去 .

From 每次就加一个块值了 .

这里执行

       else

                     ret = scan_block_fast(mtd, bd, from, buf, len);

于是就要看 scan_block_fast.

 

 

 

Arm-linux 东东 nand 9: nand_scan_tail :

static int scan_block_fast(struct mtd_info *mtd, struct nand_bbt_descr *bd,

                        loff_t offs, uint8_t *buf, int len)

{

       struct mtd_oob_ops ops;

       int j, ret;

 

       ops.ooblen = mtd->oobsize;

       ops.oobbuf = buf;

       ops.ooboffs = 0;

       ops.datbuf = NULL;

       ops.mode = MTD_OOB_PLACE;

 

       for (j = 0; j < len; j++) {

              /*

                * Read the full oob until read_oob is fixed to

                * handle single byte reads for 16 bit

                * buswidth

                */

              ret = mtd->read_oob(mtd, offs, &ops);

              if (ret)

                     return ret;

 

              if (check_short_pattern(buf, bd))

                     return 1;

 

              offs += mtd->writesize;

       }

       return 0;

}

Len 是为 2 . 也就只要扫描头二页就行了 . 红色的那一句就是加一页了 .

 

/**

  * struct mtd_oob_ops - oob operation operands

  * @mode:     operation mode

  *

  * @len:  number of data bytes to write/read

  *

  * @retlen:     number of data bytes written/read

  *

  * @ooblen:   number of oob bytes to write/read

  * @oobretlen:       number of oob bytes written/read

  * @ooboffs:  offset of oob data in the oob area (only relevant when

  *           mode = MTD_OOB_PLACE)

  * @datbuf:    data buffer - if NULL only oob data are read/written

  * @oobbuf:   oob data buffer

  *

  * Note, it is allowed to read more then one OOB area at one go, but not write.

  * The interface assumes that the OOB write requests program only one page's

  * OOB area.

  */

struct mtd_oob_ops {

       mtd_oob_mode_t   mode;

       size_t             len;

       size_t             retlen;

       size_t             ooblen;

       size_t             oobretlen;

       uint32_t   ooboffs;

       uint8_t           *datbuf;

       uint8_t           *oobbuf;

};

mtd_oob_ops 可以表示真的数据 , 也可以表示 OOB 数据 . 上面的解释很清楚了 . 再说你可能要骂我了 .

check_short_pattern 很简单 . 只要会 C 的就能看懂了 .

我们看这个 mtd->read_oob

记得不在 nand_scan_tail :

       mtd->read_oob = nand_read_oob;

…………………………………………………

static int nand_read_oob(struct mtd_info *mtd, loff_t from,

                       struct mtd_oob_ops *ops)

{

       struct nand_chip *chip = mtd->priv;

       int ret = -ENOTSUPP;

 

       ops->retlen = 0;

 

       /* Do not allow reads past end of device */

       if (ops->datbuf && (from + ops->len) > mtd->size) {

              DEBUG(MTD_DEBUG_LEVEL0, "nand_read_oob: "

                    "Attempt read beyond end of device/n");

              return -EINVAL;

       }

 

       nand_get_device(chip, mtd, FL_READING);

 

       switch(ops->mode) {

       case MTD_OOB_PLACE:

       case MTD_OOB_AUTO:

       case MTD_OOB_RAW:

              break;

 

       default:

              goto out;

       }

 

       if (!ops->datbuf)

              ret = nand_do_read_oob(mtd, from, ops);

       else

              ret = nand_do_read_ops(mtd, from, ops);

 

  out:

       nand_release_device(mtd);

       return ret;

}

……………………………………..

我们来说下主要的函数 :

关于 nand_get_device nand_release_device 这里不表

红色的那段是成立的 . 于是 :

…………………………

static int nand_do_read_oob(struct mtd_info *mtd, loff_t from,

                         struct mtd_oob_ops *ops)

{

       int page, realpage, chipnr, sndcmd = 1;

       struct nand_chip *chip = mtd->priv;

       int blkcheck = (1 << (chip->phys_erase_shift - chip->page_shift)) - 1;

       int readlen = ops->ooblen;

       int len;

       uint8_t *buf = ops->oobbuf;

 

       DEBUG(MTD_DEBUG_LEVEL3, "nand_read_oob: from = 0x%08Lx, len = %i/n",

             (unsigned long long)from, readlen);

 

       if (ops->mode == MTD_OOB_AUTO )

              len = chip->ecc.layout->oobavail;

       else

              len = mtd->oobsize;

 

       if (unlikely(ops->ooboffs >= len)) {

              DEBUG(MTD_DEBUG_LEVEL0, "nand_read_oob: "

                     "Attempt to start read outside oob/n");

              return -EINVAL;

       }

 

       /* Do not allow reads past end of device */

       if (unlikely(from >= mtd->size ||

                   ops->ooboffs + readlen > ((mtd->size >> chip->page_shift) -

                                   (from >> chip->page_shift)) * len)) {

              DEBUG(MTD_DEBUG_LEVEL0, "nand_read_oob: "

                     "Attempt read beyond end of device/n");

              return -EINVAL;

       }

 

       chipnr = (int)(from >> chip->chip_shift);

       chip->select_chip(mtd, chipnr);

 

       /* Shift to get page */

       realpage = (int)(from >> chip->page_shift);

       page = realpage & chip->pagemask;

…………………………

MTD_OOB_AUTO 没有设的于是 .len 就等于 mtd->oobsize. 那个 realpage 行吧 . 记得我以前说过 page_shitf 不记得回头看下 .

……………………

 

while(1) {

              sndcmd = chip->ecc.read_oob(mtd, chip, page, sndcmd);

 

              len = min(len, readlen);

              buf = nand_transfer_oob(chip, buf, ops, len);

 

              if (!(chip->options & NAND_NO_READRDY)) {

                     /*

                       * Apply delay or wait for ready/busy pin. Do this

                       * before the AUTOINCR check, so no problems arise if a

                       * chip which does auto increment is marked as

                       * NOAUTOINCR by the board driver.

                       */

                     if (!chip->dev_ready)

                            udelay(chip->chip_delay);

                     else

                            nand_wait_ready(mtd);

              }

 

              readlen -= len;

              if (!readlen)

                     break;

 

              /* Increment page address */

              realpage++;

 

              page = realpage & chip->pagemask;

              /* Check, if we cross a chip boundary */

              if (!page) {

                     chipnr++;

                     chip->select_chip(mtd, -1);

                     chip->select_chip(mtd, chipnr);

              }

 

              /* Check, if the chip supports auto page increment

                * or if we have hit a block boundary.

                */

              if (!NAND_CANAUTOINCR(chip) || !(page & blkcheck))

                     sndcmd = 1;

       }

………………………………………………………

进入这个 while 不过只会一次 . 执行到红色那里就退出了 .

因为 readlen 就等于 len;

ops.ooblen = mtd->oobsize;

       ops.oobbuf = buf;

       ops.ooboffs = 0;

       ops.datbuf = NULL;

       ops.mode = MTD_OOB_PLACE;

这一句 : sndcmd = chip->ecc.read_oob(mtd, chip, page, sndcmd);

对于硬件算 ECC 的来说就是

…………………

static int nand_read_oob_std(struct mtd_info *mtd, struct nand_chip *chip,

                          int page, int sndcmd)

{

       if (sndcmd) {

              chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page);

              sndcmd = 0;

       }

       chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);

       return sndcmd;

}

Sndcmd 是为 1 . 发送命令 . 然后就读 . 命令如何分发上面已经讲过了 .

回到 nand_do_read_oob 还有一个函数 nand_transfer_oob

static uint8_t *nand_transfer_oob(struct nand_chip *chip, uint8_t *oob,

                              struct mtd_oob_ops *ops, size_t len)

{

       switch(ops->mode) {

 

       case MTD_OOB_PLACE :

       case MTD_OOB_RAW:

              memcpy(oob, chip->oob_poi + ops->ooboffs, len);

              return oob + len;

 

       case MTD_OOB_AUTO: {

              struct nand_oobfree *free = chip->ecc.layout->oobfree;

              uint32_t boffs = 0, roffs = ops->ooboffs;

              size_t bytes = 0;

 

              for(; free->length && len; free++, len -= bytes) {

                     /* Read request not from offset 0 ? */

                     if (unlikely(roffs)) {

                            if (roffs >= free->length) {

                                   roffs -= free->length;

                                   continue;

                            }

                            boffs = free->offset + roffs;

                            bytes = min_t(size_t, len,

                                         (free->length - roffs));

                            roffs = 0;

                     } else {

                            bytes = min_t(size_t, len, free->length);

                            boffs = free->offset;

                     }

                     memcpy(oob, chip->oob_poi + boffs, bytes);

                     oob += bytes;

              }

              return oob;

       }

       default:

              BUG();

       }

       return NULL;

}

MTD_OOB_PLACE 是设了的 . 于是只有一个潇潇洒洒的一个函数 memcpy

回了,一直返回 . 这样一次扫描就完了 . 于在 nand_chip 中就有一个表来表示那些是坏块了

还有一个函数就是 :

int nand_isbad_bbt(struct mtd_info *mtd, loff_t offs, int allowbbt)

{

       struct nand_chip *this = mtd->priv;

       int block;

       uint8_t res;

 

       /* Get block number * 2 */

       block = (int)(offs >> (this->bbt_erase_shift - 1));

       res = (this->bbt[block >> 3] >> (block & 0x06)) & 0x03;

 

       DEBUG(MTD_DEBUG_LEVEL2, "nand_isbad_bbt(): bbt info for offs 0x%08x: (block %d) 0x%02x/n",

             (unsigned int)offs, block >> 1, res);

 

       switch ((int)res) {

       case 0x00:

              return 0;

       case 0x01:

              return 1;

       case 0x02:

              return allowbbt ? 0 : 1;

       }

       return 1;

}

没有了 . 就这样了 .

万物归宗又回到了 s3c24xx_nand_probe

还有一个函数就是 s3c2410_nand_add_partition

你可能感兴趣的:(struct,function,command,cmd,Flash,byte)