一. nand devices分析
1.1 platform_devices的添加
在arch/arm/plat-samsun/dev-nand.c中
static struct resource s3c_nand_resource[] = {
[0] = {
.start = S3C_PA_NAND,
.end = S3C_PA_NAND + SZ_1M,
.flags = IORESOURCE_MEM,
}
};
struct platform_device s3c_device_nand = {
.name = “s3c2410-nand”,
.id = -1,
.num_resources = ARRAY_SIZE(s3c_nand_resource),
.resource = s3c_nand_resource,
};
EXPORT_SYMBOL(s3c_device_nand);
别看这儿是s3c2410-nand后来又设置为s3c6410-nand
arch/arm/mach-s3c64xx/s3c6410.c中
void __init s3c6410_map_io(void)
{
s3c_device_nand.name = “s3c6410-nand”;
}
1.1 分区信息的添加
在arch/arm/mach-s3c64xx/mach-smdk6410.c中
struct mtd_partition ok6410_nand_part[] = {
{
.name = “Bootloader”,
.offset = 0,
.size = (1 * SZ_1M),
.mask_flags = MTD_CAP_NANDFLASH,
},
{
.name = “Kernel”,
.offset = (1 * SZ_1M),
.size = (5*SZ_1M) ,
.mask_flags = MTD_CAP_NANDFLASH,
},
{
.name = "User",
.offset = (6 * SZ_1M),
.size = (200*SZ_1M) ,
},
{
.name = "File System",
.offset = MTDPART_OFS_APPEND,
.size = MTDPART_SIZ_FULL,
}
};
static struct s3c2410_nand_set ok6410_nand_sets[] = {
[0] = {
.name = “nand”,
.nr_chips = 1,
.nr_partitions = ARRAY_SIZE(ok6410_nand_part),
.partitions = ok6410_nand_part,
},
};
static struct s3c2410_platform_nand ok6410_nand_info = {
.tacls = 25,
.twrph0 = 55,
.twrph1 = 40,
.nr_sets = ARRAY_SIZE(ok6410_nand_sets),
.sets = ok6410_nand_sets,
};
然后在smdk6410_machine_init中添加
s3c_nand_set_platdata(&ok6410_nand_info);
void __init s3c_nand_set_platdata(struct s3c2410_platform_nand *nand)
{
s3c_device_nand.dev.platform_data = nand;
}
二. nand driver分析
在drivers/mtd/nand/s3c_nand.c中
module_init(s3c_nand_init);
–> s3c_nand_init
static int __init s3c_nand_init(void)
{
printk(“S3C NAND Driver, (c) 2008 Samsung Electronics\n”);
return platform_driver_register(&s3c6410_nand_driver);
}
匹配name
static struct platform_driver s3c6410_nand_driver = {
.probe = s3c6410_nand_probe,
.remove = s3c_nand_remove,
.suspend = s3c_nand_suspend,
.resume = s3c_nand_resume,
.driver = {
.name = “s3c6410-nand”,
.owner = THIS_MODULE,
},
};
进入probe函数
static int s3c6410_nand_probe(struct platform_device *dev)
{
return s3c_nand_probe(dev, TYPE_S3C6410);
}
再次进入调用
static int s3c_nand_probe(struct platform_device *pdev, enum s3c_cpu_type cpu_type)
{
struct s3c2410_platform_nand *plat = pdev->dev.platform_data;
struct s3c2410_nand_set *sets;
struct nand_chip *nand;
struct resource *res;
struct nand_flash_dev *type = NULL;
//打开nand flash 时钟
s3c_nand.clk = clk_get(&pdev->dev, "nand");
clk_enable(s3c_nand.clk);
//ioremap Nand flash控制寄存器
res = pdev->resource;
size = res->end - res->start + 1;
s3c_nand.area = request_mem_region(res->start, size, pdev->name);
s3c_nand.cpu_type = cpu_type;
s3c_nand.device = &pdev->dev;
s3c_nand.regs = ioremap(res->start, size);
s3c_nand.platform = plat;
sets = (plat != NULL) ? plat->sets : NULL; //sets.partitions就是分区信息
nr_sets = (plat != NULL) ? plat->nr_sets : 1; //nr_sets=1
s3c_nand.mtd_count = nr_sets;
//分配内存大小为: mtd_info + nand_chip,并初始化
s3c_mtd = kmalloc(sizeof(struct mtd_info) + sizeof(struct nand_chip), GFP_KERNEL);
nand = (struct nand_chip *) (&s3c_mtd[1]); //所以nand_chip的起始地址是&mtd_info[1]
memset((char *) s3c_mtd, 0, sizeof(struct mtd_info));
memset((char *) nand, 0, sizeof(struct nand_chip));
s3c_mtd->priv = nand;
for (i = 0; i < sets->nr_chips; i++) { //sets->nr_chips=1
nand->IO_ADDR_R = (char *)(s3c_nand.regs + S3C_NFDATA);
nand->IO_ADDR_W = (char *)(s3c_nand.regs + S3C_NFDATA);
nand->cmd_ctrl = s3c_nand_hwcontrol;
nand->dev_ready = s3c_nand_device_ready;
nand->scan_bbt = s3c_nand_scan_bbt;
nand->options = 0;
nand->ecc.mode = NAND_ECC_HW;
nand->ecc.hwctl = s3c_nand_enable_hwecc;
nand->ecc.calculate = s3c_nand_calculate_ecc;
nand->ecc.correct = s3c_nand_correct_data;
//发起读取设备ID流程
s3c_nand_hwcontrol(0, NAND_CMD_READID, NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE);
s3c_nand_hwcontrol(0, 0x00, NAND_CTRL_CHANGE | NAND_NCE | NAND_ALE);
s3c_nand_hwcontrol(0, 0x00, NAND_NCE | NAND_ALE);
s3c_nand_hwcontrol(0, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE);
s3c_nand_device_ready(0);
//第1次读取的是device code=0xD5
tmp = readb(nand->IO_ADDR_R);
dev_id = tmp = readb(nand->IO_ADDR_R);
//查表获取 nand flash的硬件信息
// name id pagesize chipsize erasesize option
// {"NAND 2GiB 3,3V 8-bit", 0xD5, 4096, 2048, 512*1024, LP_OPTIONS},
for (j = 0; nand_flash_ids[j].name != NULL; j++) {
if (tmp == nand_flash_ids[j].id) {
type = &nand_flash_ids[j];
break;
}
}
nand->cellinfo = readb(nand->IO_ADDR_R);
tmp = readb(nand->IO_ADDR_R);
if (!type->pagesize) {
if (((nand->cellinfo >> 2) & 0x3) == 0) {
} else {
nand_type = S3C_NAND_TYPE_MLC_4BIT;
nand->options |= NAND_NO_SUBPAGE_WRITE;
nand->ecc.read_page = s3c_nand_read_page_4bit;
nand->ecc.write_page = s3c_nand_write_page_4bit;
nand->ecc.size = 512;
nand->ecc.bytes = 8;
nand->ecc.layout = &s3c_nand_oob_mlc_64;
if(dev_id == 0xd5)
{
printk("dev_id == 0xd5 select s3c_nand_oob_mlc_128\n");
nand_type = S3C_NAND_TYPE_MLC_8BIT;
nand->ecc.read_page = s3c_nand_read_page_8bit;
nand->ecc.write_page = s3c_nand_write_page_8bit;
nand->ecc.size = 512;
nand->ecc.bytes = 13;
nand->ecc.layout = &s3c_nand_oob_mlc_128_8bit;
}
}
}
nand_scan(s3c_mtd, 1);
add_mtd_partitions(s3c_mtd, sets->partitions, sets->nr_partitions);
}
return 0;
}
nand_scan
–>nand_scan_ident
–> nand_set_defaults 设置chip的指针
–> nand_get_flash_type 获取flash的类型
nand_scan
–> nand_scan_tail 设置chip->ecc的指针 及 mtd的一些指针
–> chip->scan_bbt 最后调用scan_bbt,不过这儿直接返回0
int add_mtd_partitions(struct mtd_info *master, const struct mtd_partition *parts, int nbparts)
{
struct mtd_part *slave;
uint64_t cur_offset = 0;
for (i = 0; i < nbparts; i++) {
slave = add_one_partition(master, parts + i, i, cur_offset);
cur_offset = slave->offset + slave->mtd.size;
}
return 0;
}
EXPORT_SYMBOL(add_mtd_partitions);