arm-linux东东之nand之2:3c2440_nand_probe
来源:互联网 发布:如何屏蔽淘宝推广广告 编辑:程序博客网 时间:2024/05/16 14:03
二 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这个函数可不是打杂的.
- arm-linux东东之nand之2:3c2440_nand_probe
- arm-linux东东之nand
- arm-linux东东之nand之一:nand 初始化
- ARM学习之Nand FLash控制器
- linux之NAND FLASH驱动程序
- linux NAND驱动之二:NAND存储原理
- linux NAND驱动之六:NAND驱动的probe流程
- linux NAND驱动之二:NAND存储原理
- linux NAND驱动之六:NAND驱动的probe流程
- linux NAND驱动之二:NAND存储原理
- linux NAND驱动之六:NAND驱动的probe流程
- Arm-Linux 之 tslib
- Arm-Linux 之 tslib
- ARM-LINUX之MMU
- linux块设备之nand flash
- linux系统移植之nand ecc错误
- 5,嵌入式Linux之Nand Flash
- 嵌入式linux之Nand flash驱动程序框架
- 按照《OSGi 原理与最佳实践》进行OSGi部署,发生的问题解决方法
- 数据库常用对象查询
- 数据造价
- java -- ant 发布详解
- 第六节 低级文件编程库
- arm-linux东东之nand之2:3c2440_nand_probe
- java 自动类型提升
- Android横竖屏切换的解决方法
- FreeTextBox按钮设置
- 一个GDI资源泄漏的错误 记录下来 以后用的到
- C#.net之反射初探
- 浅谈USB设备的VID和PID
- 这怎么搞的?这里又来了一个博客咯!郁闷着。
- Nboot中nand flash控制器参数TACLS、TWRPH0和TWRPH1的确定(基于K9F1208U0B)