/*
nand 设备先于nand 驱动注册,所以,在nand driver注册的时候会按照名字去匹配
paltform bus上注册的所有设备,(一个driver可以管理多个devices,但一个device只能被
一个driver管理),匹配ok后,会执行nand driver的probe函数,这里就是pxa3xx_nand_probe()
*/
// 该函数传入参数是&pxa3xx_device_nand
static int pxa3xx_nand_probe(struct platform_device *pdev)
{
struct pxa3xx_nand_platform_data *pdata; // nand chip 分区和分区数 - pxa3xx_nand.h
struct nand_chip *this; // include/linux/mtd/nand.h
struct pxa3xx_nand_info *info; // pxa3xx_nand.c
struct resource *res; // ioport.h
struct clk *clk = NULL, *smc_clk = NULL;
int status = -1;
struct mtd_partition *parts; // partitions.h
unsigned int data_buf_len;
#ifdef CONFIG_MTD_NAND_PXA3xx_DMA
unsigned int buf_len;
#endif
int i, ret = 0;
int block_size, page_size;
pdata = pdev->dev.platform_data;
/* nand的分区信息存放在struct pxa3xx_nand_platform_data benzina_nand_info
中,而pxa3xx_device_nand.dev.platform_data = &benzina_nand_info;
*/
...
/**** 关于系统中时钟的统一管理
arch/arm/mach-pxa/pxa3xx.c
static const struct clkops clk_pxa3xx_nand_ops = {
.enable = clk_pxa3xx_cken_enable,
.disable = clk_pxa3xx_cken_disable,
.getrate = clk_pxa3xx_nand_getrate,
};
static const struct clkops clk_pxa3xx_smc_ops = {
.enable = clk_pxa3xx_cken_null,
.disable = clk_pxa3xx_cken_null,
.getrate = clk_pxa3xx_smc_getrate,
};
static DEFINE_PXA3_CK(pxa3xx_nand, NAND, &clk_pxa3xx_nand_ops);
...
static DEFINE_PXA3_CK(pxa3xx_smc, SMC, &clk_pxa3xx_smc_ops);
++++++++++++++++++++++++++++++++++++
arch/arm/mach-pxa/clock.h
#define DEFINE_PXA3_CK(_name, _cken, _ops) \
struct clk clk_##_name = { \
.ops = _ops, \
.cken = CKEN_##_cken, \
}
所以这里实际上定义了一个结构体:
struct clk clk_pxa3xx_nand = {
.ops = &clk_pxa3xx_nand_ops,// 操作集
.cken = CKEN_NAND, // 4 - Clock Enable Bit
}
++++++++++++++++++++++++++++++++++++
static struct clk_lookup pxa3xx_clkregs[] = {
...
INIT_CLKREG(&clk_pxa3xx_nand, NULL, "NANDCLK"),
...
INIT_CLKREG(&clk_pxa3xx_smc, NULL, "SMCCLK"),
...
}
++++++++++++++++++++++++++++++++++++
arch/arm/include/asm/clkdev.h
struct clk_lookup {
struct list_head node;
const char *dev_id;
const char *con_id;
struct clk *clk;
};
arch/arm/mach-pxa/clock.h
#define INIT_CLKREG(_clk,_devname,_conname) \
{ \
.clk = _clk, \
.dev_id = _devname, \
.con_id = _conname, \
}
++++++++++++++++++++++++++++++++++++
注册这些时钟信息是在函数pxa3xx_init()中(arch/arm/mach-pxa/pxa3xx.c):
clks_register(pxa3xx_clkregs, ARRAY_SIZE(pxa3xx_clkregs));
****/
/* Enable CKEN_SMC */
smc_clk = clk_get(NULL, "SMCCLK"); // 取得对应的struct clk结构体指针
dfc_context.smc_clk = smc_clk;
clk_enable(smc_clk); /* clock disable and enable function is NULL */
/* Enable CKEN_NAND */
clk = clk_get(NULL, "NANDCLK");
dfc_context.clk = clk;
clk_enable(clk); /* D0CKENA[4] = 1 */
/* 全局结构体对象dfc_context定义来源:
struct dfc_mode {
int enable_dma; /* DMA, or nonDMA mode
int enable_ecc; /* ECC on/off
int enable_spare; /* Spare enable
int chip_select; /* CS0 or CS1
};
struct dfc_context {
struct clk *clk; /* clock of NAND
struct clk *smc_clk; /* clock of SMC
unsigned char __iomem *membase; /* DFC register base
struct dfc_mode *dfc_mode; /* DFC mode
int data_dma_ch; /* Data DMA channel number
int cmd_dma_ch; /* CMD DMA channel number
struct dfc_flash_info *flash_info; /* Flash Spec
};
static struct dfc_mode dfc_mode =
{
#ifdef CONFIG_MTD_NAND_PXA3xx_DMA
1, // enable DMA
#else
0, // disable DMA
#endif
1, // enable ECC
1, // enable SPARE
0, // CS0
};
struct dfc_context dfc_context =
{
.dfc_mode = &dfc_mode,
};
定义于pxa3xx_nand.c中的全局结构体struct dfc_context对象
*/
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);// 获取设备pdev的IORESOURCE_MEM类型的资源结构体指针
/* struct platform_device pxa3xx_device_nand定义和资源定义
static u64 pxa3xx_nand_dma_mask = DMA_BIT_MASK(32);
static struct resource pxa3xx_resource_nand[] = {
[0] = {
.start = 0x43100000,
.end = 0x431000ff,
.flags = IORESOURCE_MEM,
},
[1] = {
.start = IRQ_NAND,
.end = IRQ_NAND,
.flags = IORESOURCE_IRQ,
},
};
struct platform_device pxa3xx_device_nand = {
.name = "pxa3xx-nand",
.id = -1,
.dev = {
.dma_mask = &pxa3xx_nand_dma_mask,
.coherent_dma_mask = DMA_BIT_MASK(32),
},
.resource = pxa3xx_resource_nand, // see up
.num_resources = ARRAY_SIZE(pxa3xx_resource_nand),
};
*/
dfc_context.membase = ioremap(res->start, res->end - res->start + 1);
// IO内存映射,将nand控制器的寄存器列表从物理内存地址映射到虚拟地址上去,保存在dfc_context.membase中
printk(KERN_ERR "MANOJMALVIYA dfc_context.membase = <%p>, res->start=<%p>,
res->end=<%p>\n",dfc_context.membase, res->start, res->end);
...
/******* 匹配nand特定结构体 *********/
for (i = DFC_FLASH_NULL + 1; i < DFC_FLASH_END; i++)
{
uint32_t id;
status = dfc_init(&dfc_context, i);
/* 依次用type_info数组中的dfc_flash_info结构体信息来初始化DFC控制器 */
// dfc_context.flash_info = &hynix4GbX16
// 至此,dfc_context.data_dma_ch和dfc_context.cmd_dma_ch还未初始化
if (status)
continue;
status = dfc_readid(&dfc_context, &id); // read NAND ID, 0xbcad
if (status)
continue;
printk(KERN_DEBUG "id:0x%x, chipid:0x%x\n",
id, dfc_context.flash_info->chip_id);
if (id == dfc_context.flash_info->chip_id) // match ID
break;
}
if(i == DFC_FLASH_END) {
printk(KERN_ALERT "Monahans NAND device:"
"Nand Flash initialize failure! id=%d\n", i);
ret = -ENXIO;
goto out_nand;
}
flash_config = i;
// 标记找到的对应的dfc_flash_info数据结构的索引号,这里是12
// 分配mtd_info、nand_chip、pxa3xx_nand_info的空间
monahans_mtd = kzalloc(sizeof(struct mtd_info) + sizeof(struct nand_chip) +
sizeof(struct pxa3xx_nand_info) , GFP_KERNEL);
...
this = (struct nand_chip *)((void *)monahans_mtd + sizeof(struct mtd_info));
info = (struct pxa3xx_nand_info *)((void *)this + sizeof(struct nand_chip));
// monahans_mtd, this, info赋值
monahans_mtd->priv = this;
this->priv = info;
data_buf_len = dfc_context.flash_info->page_size +
dfc_context.flash_info->oob_size; // 2048+64 = 2112
info->state = STATE_READY; // ready = 2
init_completion(&info->cmd_complete); // cmd完成描述符初始化
#ifdef CONFIG_MTD_NAND_PXA3xx_DMA // def
info->dma_mask = 0xffffffffUL;
pdev->dev.dma_mask = &info->dma_mask;
pdev->dev.coherent_dma_mask = 0xffffffffUL;
#ifndef CONFIG_MTD_NAND_PXA3xx_FIX1 // def
/* alloc dma data buffer for data
* buffer + 2*descriptor + command buffer
*/
buf_len = ALIGN(2*sizeof(struct pxa_dma_desc), 32) +
ALIGN(data_buf_len, 32) + ALIGN(NAND_CMD_DMA_LEN, 32);
#else // CONFIG_MTD_NAND_PXA3xx_FIX1
buf_len = ALIGN(sizeof(struct pxa_dma_desc), 32) +
ALIGN(data_buf_len, 32); // 2144
#endif
printk(KERN_INFO "Try to allocate dma buffer(len:%d)"
"for data buffer + 2*descriptor + command buffer\n", buf_len); // bug_len = 2144
info->data_desc = (struct pxa_dma_desc*)dma_alloc_writecombine(&pdev->dev,
buf_len, &info->data_desc_addr, GFP_KERNEL);
// 申请DMA使用的内存空间info->data_desc, info->data_desc_addr
/*dma_alloc_writecombine()和dma_alloc_coherent()函数的区别:
// Mark the prot value as uncacheable and unbufferable.
#define pgprot_noncached(prot) \
__pgprot((pgprot_val(prot) & ~L_PTE_MT_MASK) | L_PTE_MT_UNCACHED)
#define pgprot_writecombine(prot) \
__pgprot((pgprot_val(prot) & ~L_PTE_MT_MASK) | L_PTE_MT_BUFFERABLE)
void *
dma_alloc_writecombine(struct device *dev, size_t size, dma_addr_t *handle, gfp_t gfp)
{
return __dma_alloc(dev, size, handle, gfp,
pgprot_writecombine(pgprot_kernel));
} // dma_alloc_writecombine()函数使用了pgprot_writecombine(pgprot_kernel),表明由该函数分配出来的DMA内存
// 不使用缓存,但是却可以使用写缓冲区
void *
dma_alloc_coherent(struct device *dev, size_t size, dma_addr_t *handle, gfp_t gfp)
{
...
return __dma_alloc(dev, size, handle, gfp,
pgprot_noncached(pgprot_kernel));
} // dma_alloc_coherent()函数使用了pgprot_noncached(pgprot_kernel),表明由该函数分配出来的DMA内存
// 既不使用缓存,也不使用使用写缓冲区,
因为DMA只认识物理地址,所以在使用函数dma_alloc_writecombine()分配内存的时候,需要取得kernel使用的虚拟地址,
也需要取得DMA使用的物理地址,如:
info->data_desc = (struct pxa_dma_desc*)dma_alloc_writecombine(&pdev->dev,
buf_len, &info->data_desc_addr, GFP_KERNEL);
info->data_desc: 分配的DMA内存对应的虚拟地址,kernel使用
&pdev->dev: 使用DMA的设备指针,该函数需要知道dma_mask之类的参数
buf_len: 分配的DMA内存的实际大小
&info->data_desc_addr: 保存分配内存的物理地址,DMA使用
info->data_desc和info->data_desc_addr表示的地址是同一空间。
*/
...
info->data_buf = (char *)info->data_desc +
ALIGN(2*sizeof(struct pxa_dma_desc), 32);
info->data_buf_addr = (dma_addr_t)((char *)info->data_desc_addr +
ALIGN(2*sizeof(struct pxa_dma_desc), 32));
...
/*小结
info->data_desc DMA描述符的虚拟地址(32字节对齐,实际就是32字节)
info->data_desc_addr DMA描述符的物理地址(32字节对齐,实际就是32字节)
info->data_buf DMA数据buf的虚拟地址
info->data_buf_addr DMA数据buf的物理地址
*/
info->data_dma = pxa_request_dma("NAND DATA", DMA_PRIO_LOW,
pxa3xx_nand_data_dma_irq, info);
/*注册一个DMA通道,返回找到的DMA通道号存储于info->data_dma
ARM平台对DMA操作做了一次抽象,它让DMA操作可以独立于具体硬件平台,这样驱动程序具有更好的可移植性,
但不清楚什么原因,marvell的DMA实现并没有按照这个标准的方式去做。
在mach-pxa/dma.c文件中实现了marvell平台的DMA操作:(mach-pxa/include/mach/dma.h)
int __init pxa_init_dma(int num_ch) //初始化,在pxa3xx.c中的pxa3xx_init()函数中调用
static irqreturn_t dma_irq_handler(int irq, void *dev_id)// DMA中断处理函数,会执行pxa_request_dma()
// 函数注册DMA通道的时候注册的中断函数irq_handler
int pxa_request_dma (char *name, pxa_dma_prio prio,
void (*irq_handler)(int, void *),
void *data)
//注册一个DMA通道,其参数有名称、优先级、中断处理函数和中断处理函数的参数
void pxa_free_dma (int dma_ch) // 释放一个已经注册的DMA通道
*/
dfc_context.data_dma_ch = info->data_dma;
// 将得到的data DMA通道号保存在结构体dfc_context中的data_dma_ch域。
/***注意
实际上nand的驱动可以对data使用DMA传输,也可以对command使用DMA传输,初始化command所使用的DMA通道和前面差不多
这里是使用了一个宏CONFIG_MTD_NAND_PXA3xx_FIX1来控制是否使用command DMA传输。
我们这里是定义了这个宏,所以就没有使用command的DMA传输
所以就不存在对dfc_context.cmd_dma_ch赋值的可能。
***/
ret = request_irq(IRQ_NAND, pxa3xx_nand_irq, IRQF_DISABLED, pdev->name, info);
// 为名为pxa3xx-nand的设备注册一个nand中断,中断号,中断句柄,中断类型,设备名,中断函数的参数
/* set address of NAND IO lines */
this->options = ( (dfc_context.flash_info->flash_width == 16)? \
NAND_BUSWIDTH_16: 0 ) | NAND_USE_FLASH_BBT;
// this->options = NAND_BUSWIDTH_16 | NAND_USE_FLASH_BBT
this->waitfunc = pxa3xx_nand_waitfunc;
this->select_chip = pxa3xx_nand_select_chip;
this->dev_ready = pxa3xx_nand_dev_ready;
this->cmdfunc = pxa3xx_nand_command;
this->read_word= pxa3xx_nand_read_word;
this->read_byte = pxa3xx_nand_read_byte;
this->read_buf = pxa3xx_nand_read_buf;
this->write_buf = pxa3xx_nand_write_buf;
this->verify_buf = pxa3xx_nand_verify_buf;
this->ecc.mode = NAND_ECC_HW;
this->ecc.hwctl = pxa3xx_nand_enable_hwecc;
this->ecc.calculate = pxa3xx_nand_calculate_ecc;
this->ecc.correct = pxa3xx_nand_correct_data;
this->block_bad = pxa3xx_nand_block_bad;
this->block_markbad = pxa3xx_nand_block_markbad;
this->scan_bbt = pxa3xx_nand_scan_bbt;
this->chip_delay= 25;
this->bbt_td = &monahans_bbt_main;
this->bbt_md = &monahans_bbt_mirror;
parts = pdata->parts; // 分区表
if (dfc_context.flash_info->oob_size > 16) { // 64
this->ecc.layout = &monahans_lb_nand_oob;
/*static struct nand_ecclayout monahans_lb_nand_oob
struct nand_ecclayout {
uint32_t eccbytes;
uint32_t eccpos[64];
uint32_t oobavail;
struct nand_oobfree oobfree[MTD_MAX_OOBFREE_ENTRIES];
};
static struct nand_ecclayout monahans_lb_nand_oob = {
.eccbytes = 24,
.eccpos = {
40, 41, 42, 43, 44, 45, 46, 47,
48, 49, 50, 51, 52, 53, 54, 55,
56, 57, 58, 59, 60, 61, 62, 63},
.oobfree = { {2, 38} }
};
*/
this->ecc.size = 2048; // data bytes per ecc step
this->bbt_td->offs = 2; // main bbt签名在OOB区域的偏移位置
this->bbt_td->veroffs = 6; // main bbt版本号在OOB区域的偏移位置
this->bbt_td->pages[0] =
( (parts[pdata->nr_parts - 1].offset >> 17) +
this->bbt_md->maxblocks +
this->bbt_td->maxblocks - 1) << 6; // 4015*64 = 256960
/*
nand分区表总将最后的一个分区作为BBT存放空间(total blocks:4096,预留了最后的80块:block4016~4095 保留)
mirror bbt:
block4012 0x1f580000
block4013 0x1f5a0000 --
main bbt:
block4014 0x1f5c0000
block4015 0x1f5e0000 --
*/
this->bbt_md->offs = 2;
this->bbt_md->veroffs = 6;
this->bbt_md->pages[0] =
( (parts[pdata->nr_parts - 1].offset >> 17) +
this->bbt_md->maxblocks - 1) << 6; // 4013*64 = 256832
this->badblockpos = NAND_LARGE_BADBLOCK_POS; // 0
monahans_bbt_default.offs = NAND_LARGE_BADBLOCK_POS; // ???
monahans_bbt_default.len = 2; // ???
/* when scan_bbt() is executed, bbt version can get */
monahans_bbt_default.veroffs = 2; // ???
}else
{
samll page
...
}
info->context = &dfc_context; // 关联dfc_context和info
platform_set_drvdata(pdev, monahans_mtd);
// pdev->dev->driver_data = monahans_mtd
pxa3xx_bbm = alloc_pxa3xx_bbm();
/* static struct pxa3xx_bbm *pxa3xx_bbm;
struct pxa3xx_bbm {
int flash_type;
u32 current_slot;
u32 max_reloc_entry;
void *data_buf;
int page_shift;
int erase_shift;
unsigned int table_init;
struct reloc_table *table;
struct reloc_item *reloc;
int (*init)(struct mtd_info *mtd, struct pxa3xx_bbm *bbm);
int (*uninit)(struct mtd_info *mtd, struct pxa3xx_bbm *bbm);
int (*search)(struct mtd_info *mtd, struct pxa3xx_bbm *bbm,
unsigned int block);
int (*markbad)(struct mtd_info *mtd, struct pxa3xx_bbm *bbm,
unsigned int block);
};
struct pxa3xx_bbm* alloc_pxa3xx_bbm(void)
{
struct pxa3xx_bbm *bbm;
bbm = kzalloc(sizeof(struct pxa3xx_bbm), GFP_KERNEL);
if (!bbm)
return NULL;
bbm->init = pxa3xx_init_reloc_tb;
bbm->uninit = pxa3xx_uninit_reloc_tb;
bbm->search = pxa3xx_search_reloc_tb;
bbm->markbad = pxa3xx_mark_reloc_tb;
return bbm;
}
*/
nand_scan(monahans_mtd, 1); // 重要函数 note1
// nand_scan()函数主要做了如下工作:
// mtd,this,chip指向的结构体一些没有初始化的域被初始化
// reloc table 和 BBT表的读取和转换,这里采取的是指定了绝对存放地址
disable_nand_clock(info->context); // 关闭时钟,smc 和 nand
return add_mtd_partitions(monahans_mtd, pdata->parts, pdata->nr_parts); // note2
// 对每个分区向上层注册,最终注册成块设备,每个分区对应一个,其中在该层驱动中可见的是每个分区对应一个mtd
// 原始设备,存放在mtd_table[]中。
}
/** note1 nand_scan() **/
int nand_scan(struct mtd_info *mtd, int maxchips)
{
int ret;
/* Many callers got this wrong, so check for it for a while... */
if (!mtd->owner && caller_is_module()) {
printk(KERN_CRIT "nand_scan() called with NULL mtd->owner!\n");
BUG();
}
ret = nand_scan_ident(mtd, maxchips); // note1-1
if (!ret)
ret = nand_scan_tail(mtd); // note1-2
return ret;
}
/** note1 nand_scan() **/
/**** note1-1 nand_scan_ident() ****/
int nand_scan_ident(struct mtd_info *mtd, int maxchips)
{
int i, busw, nand_maf_id;
struct nand_chip *chip = mtd->priv;
struct nand_flash_dev *type;
/* Get buswidth to select the correct functions */
busw = chip->options & NAND_BUSWIDTH_16; // NAND_BUSWIDTH_16
/* Set the default functions */
nand_set_defaults(chip, busw); // note1-1-1
/* Read the flash type */
type = nand_get_flash_type(mtd, chip, busw, &nand_maf_id); // note1-1-2 重要函数
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++) { // maxchips = 1
chip->select_chip(mtd, i);
/* See comment in nand_get_flash_type for reset */
chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
/* 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; // 1
mtd->size = i * chip->chipsize; // total size
return 0;
}
/**** note1-1 nand_scan_ident() ****/
/****** note1-1-1 nand_set_defaults() ******/ // struct nand_chip结构体对象一些初始化
static void nand_set_defaults(struct nand_chip *chip, int busw)
{
/* check for proper chip_delay setup, set 20us if not */
if (!chip->chip_delay)
chip->chip_delay = 20;
/* check, if a user supplied command function given */
if (chip->cmdfunc == NULL) // pxa3xx_nand_command
chip->cmdfunc = nand_command;
/* check, if a user supplied wait function given */
if (chip->waitfunc == NULL) // pxa3xx_nand_waitfunc
chip->waitfunc = nand_wait;
if (!chip->select_chip) // pxa3xx_nand_select_chip
chip->select_chip = nand_select_chip;
if (!chip->read_byte) // pxa3xx_nand_read_byte
chip->read_byte = busw ? nand_read_byte16 : nand_read_byte;
if (!chip->read_word) // pxa3xx_nand_read_word
chip->read_word = nand_read_word;
if (!chip->block_bad) // pxa3xx_nand_block_bad
chip->block_bad = nand_block_bad;
if (!chip->block_markbad) // pxa3xx_nand_block_markbad
chip->block_markbad = nand_default_block_markbad;
if (!chip->write_buf) // pxa3xx_nand_write_buf
chip->write_buf = busw ? nand_write_buf16 : nand_write_buf;
if (!chip->read_buf) // pxa3xx_nand_read_buf
chip->read_buf = busw ? nand_read_buf16 : nand_read_buf;
if (!chip->verify_buf) // pxa3xx_nand_verify_buf
chip->verify_buf = busw ? nand_verify_buf16 : nand_verify_buf;
if (!chip->scan_bbt) // pxa3xx_nand_scan_bbt
chip->scan_bbt = nand_default_bbt;
// 如果上面的这些函数指针没有被初始化,那么设置默认的函数
if (!chip->controller) {
chip->controller = &chip->hwcontrol;
spin_lock_init(&chip->controller->lock);
init_waitqueue_head(&chip->controller->wq);
}
// 实际上只初始化了chip->controller
}
/****** note1-1-1 nand_set_defaults() ******/
/****** note1-1-2 nand_get_flash_type() ******/
/*
* Get the flash and manufacturer id and lookup if the type is supported
* 读出nand device的ID,判断系统是否支持,并且设置mtd_info和nand_chip结构体的一些信息
*/
static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
struct nand_chip *chip,
int busw, int *maf_id)
{
struct nand_flash_dev *type = NULL;
int i, dev_id, maf_idx;
int tmp_id, tmp_manf;
/* Select the device */
chip->select_chip(mtd, 0); // pxa3xx_nand_select_chip - CS0 or CS1
/*
* Reset the chip, required by some chips (e.g. Micron MT29FxGxxxxx)
* after power-up
*/
/*chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1); */
/* Send the command for reading device ID */
chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);
// pxa3xx_nand_command(mtd, NAND_CMD_READID, 0x00, -1);
// ID 已经读出来了,存放在mtd->priv->priv->data_buf中的
/* Read manufacturer and device IDs */
*maf_id = chip->read_byte(mtd); // pxa3xx_nand_read_byte() 0xAD
dev_id = chip->read_byte(mtd); // pxa3xx_nand_read_byte() 0xBC
/* 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);
}
// 读两次进行确认,看是否是随机数
/* 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; // "NAND 512MiB 1,8V 16-bit"
chip->chipsize = (uint64_t)type->chipsize << 20; // 0x20000000 = 512MB
/* Newer devices have all the information in additional id bytes */
if (!type->pagesize) {
int extid;
/* The 3rd id byte holds MLC / multichip data */
chip->cellinfo = chip->read_byte(mtd); // 0x10
/* The 4th id byte is the important one */
extid = chip->read_byte(mtd); // 0x55
/* Calc pagesize */
mtd->writesize = 1024 << (extid & 0x3); // 2048B
extid >>= 2;
/* Calc oobsize */
mtd->oobsize = (8 << (extid & 0x01)) * (mtd->writesize >> 9); // 16B*4 = 64
extid >>= 2;
/* Calc blocksize. Blocksize is multiples of 64KiB */
mtd->erasesize = (64 * 1024) << (extid & 0x03); // 64KB*2 = 128KB
extid >>= 2;
/* Get buswidth information */
busw = (extid & 0x01) ? NAND_BUSWIDTH_16 : 0; // NAND_BUSWIDTH_16
} 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;
}
/* Try to identify manufacturer */
for (maf_idx = 0; nand_manuf_ids[maf_idx].id != 0x0; maf_idx++) {
if (nand_manuf_ids[maf_idx].id == *maf_id)
break;
}
/*
* Check, if buswidth is correct. Hardware drivers should set
* chip correct !
*/
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; // 11
/* Convert chipsize to number of pages per chip -1. */
chip->pagemask = (chip->chipsize >> chip->page_shift) - 1; // 0x3ffff = total_pages - 1
chip->bbt_erase_shift = chip->phys_erase_shift =
ffs(mtd->erasesize) - 1; // 17
if (chip->chipsize & 0xffffffff)
chip->chip_shift = ffs((unsigned)chip->chipsize) - 1; // 0x20000000 29-1 = 28
else
chip->chip_shift = ffs((unsigned)(chip->chipsize >> 32)) + 32 - 1;
/* Set the bad block position */
chip->badblockpos = mtd->writesize > 512 ?
NAND_LARGE_BADBLOCK_POS : NAND_SMALL_BADBLOCK_POS; // 坏块标记位置 0
/* Get chip options, preserve non chip based options */
chip->options &= ~NAND_CHIPOPTIONS_MSK;
chip->options |= type->options & NAND_CHIPOPTIONS_MSK;
// type->options = NAND_SAMSUNG_LP_OPTIONS | NAND_NO_READRDY | NAND_NO_AUTOINCR | NAND_BUSWIDTH_16
/*
* Set chip as a default. Board drivers can override it, if necessary
*/
chip->options |= NAND_NO_AUTOINCR;
/* Check if chip is a not a samsung device. Do not clear the
* options for chips which are not having an extended id.
*/
if (*maf_id != NAND_MFR_SAMSUNG && !type->pagesize)
chip->options &= ~NAND_SAMSUNG_LP_OPTIONS;
// chip->options = NAND_NO_READRDY | NAND_NO_AUTOINCR | NAND_BUSWIDTH_16 | NAND_CHIPOPTIONS_MSK
/* 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;
}
/****** note1-1-2 nand_get_flash_type() ******/
/**** note1-2 nand_scan_tail() ****/
int nand_scan_tail(struct mtd_info *mtd)
{
int i;
struct nand_chip *chip = mtd->priv;
// chip->options = 0x10103
// chip->options = NAND_USE_FLASH_BBT | NAND_NO_READRDY | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR
if (!(chip->options & NAND_OWN_BUFFERS))
chip->buffers = kmalloc(sizeof(*chip->buffers), GFP_KERNEL);
/*
struct nand_buffers {
uint8_t ecccalc[NAND_MAX_OOBSIZE]; // 计算得出的ecc值
uint8_t ecccode[NAND_MAX_OOBSIZE]; // nand中保存的ecc值
uint8_t databuf[NAND_MAX_PAGESIZE + NAND_MAX_OOBSIZE]; // nand 数据 + OOB
};
*/
if (!chip->buffers)
return -ENOMEM;
/* Set the internal oob buffer location, just after the page data */
chip->oob_poi = chip->buffers->databuf + mtd->writesize;
// 将oob_poi指向上面刚刚分配的buffer中的databuf中的OOB区域
/*
* If no default placement scheme is given, select an appropriate one
*/
if (!chip->ecc.layout) {
// chip->ecc.layout 已经在上层函数中初始化了,&monahans_lb_nand_oob
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) // NULL
chip->ecc.read_page_raw = nand_read_page_raw;
if (!chip->ecc.write_page_raw) // NULL
chip->ecc.write_page_raw = nand_write_page_raw;
switch (chip->ecc.mode) { // 上层函数中被初始化成 NAND_ECC_HW
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.read_subpage = nand_read_subpage;
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();
}
/*
* The number of bytes available for a client to place data into
* the out of band area
*/
// &monahans_lb_nand_oob
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; // 38
mtd->oobavail = chip->ecc.layout->oobavail; // 38
/*
* Set the number of read / write steps for one page depending on ECC
* mode
*/
chip->ecc.steps = mtd->writesize / chip->ecc.size; // 2048/2048 = 1
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;
// chip->ecc {.mode = 2 , .steps = 1 , .size = 2048 , .bytes = 0 , .total = 0 }
/*
* Allow subpage writes up to ecc.steps. Not possible for MLC
* FLASH.
*/
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; // 2048
/* Initialize state */
chip->state = FL_READY;
/* De-select the device */
chip->select_chip(mtd, -1); // 不选择任何nand设备
// pxa3xx_nand_select_chip
/* Invalidate the pagebuffer reference */
chip->pagebuf = -1;
/* Fill in remaining MTD driver data */
mtd->type = MTD_NANDFLASH;
mtd->flags = MTD_CAP_NANDFLASH; // MTD_WRITEABLE
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); /* pxa3xx_nand_scan_bbt(mtd) note1-2-1*/
}
/**** note1-2 nand_scan_tail() ****/
/****** note1-2-1 chip->scan_bbt(mtd)///pxa3xx_nand_scan_bbt(mtd) ******/
static int pxa3xx_nand_scan_bbt(struct mtd_info *mtd)
{
struct nand_chip *this = (struct nand_chip *)mtd->priv;
int ret;
// pxa3xx_bbm是一个独立的指针,和前面的mtd没有任何联系,主要是和坏块映射表有关的数据
// pxa3xx_bbm是指向结构体struct pxa3xx_bbm的指针,在alloc_pxa3xx_bbm()函数中被初始化
// 而坏块表BBT的内容保存在nand_chip结构体中
if (pxa3xx_bbm && pxa3xx_bbm->init && !pxa3xx_bbm->table_init) {
pxa3xx_bbm->flash_type = FLASH_NAND;
pxa3xx_bbm->page_shift = this->page_shift; // 11
pxa3xx_bbm->erase_shift = this->phys_erase_shift; // 17
ret = pxa3xx_bbm->init(mtd, pxa3xx_bbm);
// pxa3xx_init_reloc_tb(mtd, pxa3xx_bbm) // 查找正确的reloc table note1-2-1-1
if (ret) {
return ret;
}
}
nand_scan_bbt(mtd, &monahans_bbt_default); // note1-2-1-2
#ifdef CONFIG_MTD_NAND_PXA3xx_DEBUG
dump_bbt_mem(mtd);
#endif
return 0;
}
/****** note1-2-1 chip->scan_bbt(mtd)///pxa3xx_nand_scan_bbt(mtd) ******/
/******** note1-2-1-1 pxa3xx_init_reloc_tb(mtd, bbm) ********/
static int pxa3xx_init_reloc_tb(struct mtd_info *mtd, struct pxa3xx_bbm *bbm)
{
int size = mtd->writesize + mtd->oobsize; // 2048+64 = 2112
int page_per_block = 1 << (bbm->erase_shift - bbm->page_shift); // 64
bbm->max_reloc_entry = (int)(((int)mtd->size / (1 << bbm->erase_shift)) * 2) / 100;
// 81
bbm->table_init = 0; // reloc table 初始化标志
if (page_per_block == 64){
max_bbt_slots = 40;
bbm->max_reloc_entry = max_bbt_slots;
} else {
max_bbt_slots = 8;
bbm->max_reloc_entry = max_bbt_slots;
}
// bbm->max_reloc_entry = 40 , max_bbt_slots = 40
bbm->data_buf = kzalloc(size, GFP_KERNEL); // 2112, one page
if (!bbm->data_buf) {
return -ENOMEM;
}
bbm->table = (struct reloc_table *)bbm->data_buf;
memset(bbm->table, 0x0, sizeof(struct reloc_table));
bbm->reloc = (struct reloc_item *)((uint8_t *)bbm->data_buf +
sizeof (struct reloc_item));
memset(bbm->reloc, 0x0,
sizeof(struct reloc_item) * bbm->max_reloc_entry);
// reloc table 表头和表体指针初始化
// LB - reloc table有511个表项,而SB - reloc table只有127个表项
return pxa3xx_scan_reloc_tb(mtd, bbm); // note1-2-1-1-1 reloc table 查找
}
/******** note1-2-1-1 pxa3xx_init_reloc_tb(mtd, bbm) ********/
/********** note1-2-1-1-1 pxa3xx_scan_reloc_tb(mtd, bbm) **********/
static int pxa3xx_scan_reloc_tb(struct mtd_info *mtd, struct pxa3xx_bbm *bbm)
{
struct reloc_table *table = bbm->table; // reloc table表头
int page, maxslot, obm, valid = 0;
int retlen, ret;
obm = calc_obm_ver(); // return MHN_OBM_V3
if (obm == MHN_OBM_V2) {
/* On MOBM V2, the relocation table resides in the last page
* of the first block.
*/
page = (1 << (bbm->erase_shift - bbm->page_shift)) - 1;
memset(bbm->data_buf, 0, mtd->writesize + mtd->oobsize);
ret = mtd->read(mtd, page << bbm->page_shift, mtd->writesize,
&retlen, bbm->data_buf);
if (ret == 0) {
if (table->header == NAND_RELOC_HEADER)
valid = 1;
}
} else if (obm == MHN_OBM_V3) {
/* On MOBM V3, there're several relocation tables in the first
* block.
* When new bad blocks are found, a new relocation table will
* be generated and written back to the first block. But the
* original relocation table won't be erased. Even if the new
* relocation table is written wrong, system can still find an
* old one.
* One page contains one slot.
*/
maxslot = 1 << (bbm->erase_shift - bbm->page_shift); // 64
page = maxslot - max_bbt_slots; // 24
for (; page < maxslot; page++) { // page < 64
memset(bbm->data_buf, 0,
mtd->writesize + mtd->oobsize); // clear bbm->data_buf
ret = mtd->read(mtd, (page << bbm->page_shift),
mtd->writesize, &retlen, bbm->data_buf);
/* 从某个地址读取一定长度的数据
ret = nand_read(mtd, (page << bbm->page_shift),
2048, &retlen, bbm->data_buf); // 重要函数
*/
if (ret == 0) {
if (table->header != NAND_RELOC_HEADER) {
continue; // 检查该页中是否存放的是reloc table
} else {
bbm->current_slot = maxslot - page - 1;
// page63 -- slot0 ; page62 -- slot1 ; ...
valid = 1; // 有效标志置1
break;
}
}
}
} else {
printk(KERN_ERR "The version of MOBM isn't supported\n");
}
if (valid) {
printk("relocation table at page:%d\n", page);
bbm->table_init = 1;
// 设置全局nand_info结构体的table_init域,表示relo table已经正确初始化
dump_reloc_table(bbm); // 打印reloc table内容
} else {
#if defined(CONFIG_MTD_CREATE_BBT)
...
// 创建BBT和reloc table
#else
/* There should be a valid relocation table slot at least. */
printk(KERN_ERR "NO VALID relocation table can be \
recognized\n");
printk(KERN_ERR "CAUTION: It may cause unpredicated error\n");
printk(KERN_ERR "Please re-initialize the NAND flash.\n");
memset((unsigned char *)bbm->table, 0,
sizeof(struct reloc_table));
bbm->table_init = 0;
return -EINVAL;
#endif
}
return 0;
}
/********** note1-2-1-1-1 pxa3xx_scan_reloc_tb(mtd, bbm) **********/
/******** note1-2-1-2 nand_scan_bbt() ********/
/**
* nand_scan_bbt - [NAND Interface] scan, find, read and maybe create bad block table(s)
* @mtd: MTD device structure
* @bd: descriptor for the good/bad block search pattern
*
* The function checks, if a bad block table(s) is/are already
* available. If not it scans the device for manufacturer
* marked good / bad blocks and writes the bad block table(s) to
* the selected place.
*
* The bad block table memory is allocated here. It must be freed
* by calling the nand_free_bbt function.
*
*/
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; // main bbt
struct nand_bbt_descr *md = this->bbt_md; // mirror bbt
len = mtd->size >> (this->bbt_erase_shift + 2); // len = 4096/4 = 1024
/* Allocate memory (2bit per block) and clear the memory bad block table */
this->bbt = kzalloc(len, GFP_KERNEL); // 2bits 表示一个块的状态
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))) { // bd = &monahans_bbt_default
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); // 128KB
len += (len >> this->page_shift) * mtd->oobsize; // 64*64 = 4096KB
buf = vmalloc(len); // 申请一个块所需的OOB空间
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) { // yes
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);
// 如果真需要像blob中那样去找BBT的话,该函数中有错误
}
if (res)
res = check_create(mtd, buf, bd); // 填充内存BBT
/* Prevent the bbt regions from erasing / writing */
mark_bbt_region(mtd, td);
// 在内存的BBT中标记存放BBT块为坏块,以防止不经意破坏数据
if (md)
mark_bbt_region(mtd, md);
vfree(buf);
return res;
}
/******** note1-2-1-2 nand_scan_bbt() ********/
/** note2 add_mtd_partitions() **/
int add_mtd_partitions(struct mtd_info *master,
const struct mtd_partition *parts,
int nbparts)
{
struct mtd_part *slave;
uint64_t cur_offset = 0;
int i;
printk(KERN_NOTICE "Creating %d MTD partitions on \"%s\":\n", nbparts, master->name);
for (i = 0; i < nbparts; i++) {
slave = add_one_partition(master, parts + i, i, cur_offset); // note2-1
if (!slave)
return -ENOMEM;
cur_offset = slave->offset + slave->mtd.size;
}
return 0;
}
/** note2 add_mtd_partitions() **/
/**** note2-1 add_one_partition() ****/
static LIST_HEAD(mtd_partitions);
/* Our partition node structure */
struct mtd_part {
struct mtd_info mtd;
struct mtd_info *master;
uint64_t offset;
int index;
struct list_head list;
int registered;
};
// 用来描述一个分区的所有信息
static struct mtd_part *add_one_partition(struct mtd_info *master,
const struct mtd_partition *part, int partno,
uint64_t cur_offset)
{
struct mtd_part *slave;
/* allocate the partition structure */
slave = kzalloc(sizeof(*slave), GFP_KERNEL);
if (!slave) {
printk(KERN_ERR"memory allocation error while creating partitions for \"%s\"\n",
master->name);
del_mtd_partitions(master);
return NULL;
}
list_add(&slave->list, &mtd_partitions);
// mtd_partitions 用于在MTD原始设备层统一管理所有分区信息
// static LIST_HEAD(mtd_partitions) 本文件中定义
/* set up the MTD object for this partition */
slave->mtd.type = master->type;
slave->mtd.flags = master->flags & ~part->mask_flags;
slave->mtd.size = part->size; // 分区大小
slave->mtd.writesize = master->writesize;
slave->mtd.oobsize = master->oobsize;
slave->mtd.oobavail = master->oobavail;
slave->mtd.subpage_sft = master->subpage_sft;
slave->mtd.name = part->name; // 分区名字
slave->mtd.owner = master->owner;
slave->mtd.read = part_read; // 分区读写函数
slave->mtd.write = part_write;
if (master->panic_write)
slave->mtd.panic_write = part_panic_write;
if (master->point && master->unpoint) {
slave->mtd.point = part_point;
slave->mtd.unpoint = part_unpoint;
}
if (master->read_oob)
slave->mtd.read_oob = part_read_oob;
if (master->write_oob)
slave->mtd.write_oob = part_write_oob;
if (master->read_user_prot_reg)
slave->mtd.read_user_prot_reg = part_read_user_prot_reg;
if (master->read_fact_prot_reg)
slave->mtd.read_fact_prot_reg = part_read_fact_prot_reg;
if (master->write_user_prot_reg)
slave->mtd.write_user_prot_reg = part_write_user_prot_reg;
if (master->lock_user_prot_reg)
slave->mtd.lock_user_prot_reg = part_lock_user_prot_reg;
if (master->get_user_prot_info)
slave->mtd.get_user_prot_info = part_get_user_prot_info;
if (master->get_fact_prot_info)
slave->mtd.get_fact_prot_info = part_get_fact_prot_info;
if (master->sync)
slave->mtd.sync = part_sync;
if (!partno && master->suspend && master->resume) {
slave->mtd.suspend = part_suspend;
slave->mtd.resume = part_resume;
}
if (master->writev)
slave->mtd.writev = part_writev;
if (master->lock)
slave->mtd.lock = part_lock;
if (master->unlock)
slave->mtd.unlock = part_unlock;
if (master->block_isbad)
slave->mtd.block_isbad = part_block_isbad;
if (master->block_markbad)
slave->mtd.block_markbad = part_block_markbad;
slave->mtd.erase = part_erase;
slave->master = master; // 该分区的主分区
slave->offset = part->offset; // 该分区偏移
slave->index = partno; // 分区索引
if (slave->offset == MTDPART_OFS_APPEND)
slave->offset = cur_offset;
if (slave->offset == MTDPART_OFS_NXTBLK) {
slave->offset = cur_offset;
if (mtd_mod_by_eb(cur_offset, master) != 0) {
/* Round up to next erasesize */
slave->offset = (mtd_div_by_eb(cur_offset, master) + 1) * master->erasesize;
printk(KERN_NOTICE "Moving partition %d: "
"0x%012llx -> 0x%012llx\n", partno,
(unsigned long long)cur_offset, (unsigned long long)slave->offset);
}
}
if (slave->mtd.size == MTDPART_SIZ_FULL)
slave->mtd.size = master->size - slave->offset;
printk(KERN_NOTICE "0x%012llx-0x%012llx : \"%s\"\n", (unsigned long long)slave->offset,
(unsigned long long)(slave->offset + slave->mtd.size), slave->mtd.name);
// 打印分区表信息
/* let's do some sanity checks */
if (slave->offset >= master->size) {
/* let's register it anyway to preserve ordering */
slave->offset = 0;
slave->mtd.size = 0;
printk(KERN_ERR"mtd: partition \"%s\" is out of reach -- disabled\n",
part->name);
goto out_register;
}
if (slave->offset + slave->mtd.size > master->size) {
slave->mtd.size = master->size - slave->offset;
printk(KERN_WARNING"mtd: partition \"%s\" extends beyond the end of device \"%s\" -- size truncated to %#llx\n",
part->name, master->name, (unsigned long long)slave->mtd.size);
}
if (master->numeraseregions > 1) {
/* Deal with variable erase size stuff */
int i, max = master->numeraseregions;
u64 end = slave->offset + slave->mtd.size;
struct mtd_erase_region_info *regions = master->eraseregions;
/* Find the first erase regions which is part of this
* partition. */
for (i = 0; i < max && regions[i].offset <= slave->offset; i++)
;
/* The loop searched for the region _behind_ the first one */
i--;
/* Pick biggest erasesize */
for (; i < max && regions[i].offset < end; i++) {
if (slave->mtd.erasesize < regions[i].erasesize) {
slave->mtd.erasesize = regions[i].erasesize;
}
}
BUG_ON(slave->mtd.erasesize == 0);
} else {
/* Single erase size */
slave->mtd.erasesize = master->erasesize; // 分区擦除大小赋值
}
if ((slave->mtd.flags & MTD_WRITEABLE) &&
mtd_mod_by_eb(slave->offset, &slave->mtd)) {
/* Doesn't start on a boundary of major erase size */
/* FIXME: Let it be writable if it is on a boundary of
* _minor_ erase size though */
slave->mtd.flags &= ~MTD_WRITEABLE;
printk(KERN_WARNING"mtd: partition \"%s\" doesn't start on an erase block boundary -- force read-only\n",
part->name);
}
if ((slave->mtd.flags & MTD_WRITEABLE) &&
mtd_mod_by_eb(slave->mtd.size, &slave->mtd)) {
slave->mtd.flags &= ~MTD_WRITEABLE;
printk(KERN_WARNING"mtd: partition \"%s\" doesn't end on an erase block -- force read-only\n",
part->name);
}
slave->mtd.ecclayout = master->ecclayout;
if (master->block_isbad) {
uint64_t offs = 0;
while (offs < slave->mtd.size) {
if (master->block_isbad(master,
offs + slave->offset))
slave->mtd.ecc_stats.badblocks++; // 分区内坏块检查统计
offs += slave->mtd.erasesize;
}
}
out_register:
if (part->mtdp) {
/* store the object pointer (caller may or may not register it*/
*part->mtdp = &slave->mtd;
slave->registered = 0;
} else {
/* register our partition */
add_mtd_device(&slave->mtd); // note2-1-1
// 将该从分区作为MTD原始设备加入到mtd_table中,成功返回0
// MTD原始设备层和MTD设备层就是依靠mtd_table来联系的
slave->registered = 1;
}
return slave;
}
/**** note2-1 add_one_partition() ****/
/****** note2-1-1 add_mtd_device() ******/
int add_mtd_device(struct mtd_info *mtd)
{
int i;
BUG_ON(mtd->writesize == 0);
mutex_lock(&mtd_table_mutex);
for (i=0; i < MAX_MTD_DEVICES; i++)
if (!mtd_table[i]) {
struct mtd_notifier *not;
mtd_table[i] = mtd; // MTD原始设备层和MTD设备在此处联系上
mtd->index = i; // mtd_table[]数组下标
mtd->usecount = 0;
if (is_power_of_2(mtd->erasesize))
mtd->erasesize_shift = ffs(mtd->erasesize) - 1;
else
mtd->erasesize_shift = 0;
if (is_power_of_2(mtd->writesize))
mtd->writesize_shift = ffs(mtd->writesize) - 1;
else
mtd->writesize_shift = 0;
mtd->erasesize_mask = (1 << mtd->erasesize_shift) - 1;
mtd->writesize_mask = (1 << mtd->writesize_shift) - 1;
/* Some chips always power up locked. Unlock them now */
if ((mtd->flags & MTD_WRITEABLE)
&& (mtd->flags & MTD_POWERUP_LOCK) && mtd->unlock) {
if (mtd->unlock(mtd, 0, mtd->size))
printk(KERN_WARNING
"%s: unlock failed, "
"writes may not work\n",
mtd->name);
}
DEBUG(0, "mtd: Giving out device %d to %s\n",i, mtd->name);
/* No need to get a refcount on the module containing
the notifier, since we hold the mtd_table_mutex */
list_for_each_entry(not, &mtd_notifiers, list)
not->add(mtd);
// 调用全局用户通知器链表中的通知器向上层所有接口用户注册块设备
// 更加详细的分析参见前几篇文档
mutex_unlock(&mtd_table_mutex);
/* We _know_ we aren't being removed, because
our caller is still holding us here. So none
of this try_ nonsense, and no bitching about it
either. :) */
__module_get(THIS_MODULE);
return 0;
}
mutex_unlock(&mtd_table_mutex);
return 1;
}
/****** note2-1-1 add_mtd_device() ******/