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
这里 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