二 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这个函数可不是打杂的.