二 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這個函數可不是打雜的.