分类: LINUX
2010-04-04 13:36:17
一 NAND设备及资源的定义和注册
我们通过MACHINE_START定义了smdk2410的machine_desc对象,这个对象里面有个init_machine的函数指针, 这里指向smdk_machine_init(), 我们的NAND设备就是在这个函数里注册到系统的.
void __init smdk_machine_init(void)
{
….
s3c_device_nand.dev.platform_data = &smdk_nand_info;
platform_add_device(smdk_devs, ARRAY_SIZE(smdk_devs)); //这里就把设备注册到系统里去了
…
}
Static struct platform_device __initdata *smdk_devs[] =
{
&s3c_device_nand, //这样在上面的函数里我们的nand设备就注册好了.
...
}
其他设备我们也可以在这里注册进系统.
struct platform_device s3c_device_nand =
{
.name = “s3c2410-nand”, /*名字很重要*/
.id = -1,
. num_resources = ARRAY_SIZE(s3c_nand_resource),
.resource = s3c_nand_resource, //这个是NAND占用的资源.
};
Static struct s3c2410_platform_nand smdk_nand_info = {
.tacks = 20, /*在datasheet上有描述*/
.twrph0 = 60, /*在datasheet上有描述*/
.twrph1 = 20, /*在datasheet上有描述*/
.nr_sets = ARRAY_SIZE(smdk_nand_sets),
.sets = smdk_nand_sets
}
static struct s3c2410_nand_set smdk_nand_sets[] = {
[0] = {
.name = "NAND",
.nr_chips = 1,
.nr_partitions = ARRAY_SIZE(smdk_default_nand_part),
.partitions = smdk_default_nand_part, /*nand的分区信息*/
},
};
/*分区信息, 我们可以在这里修改分区内容*/
static struct mtd_partition smdk_default_nand_part[] = {
[0] = {
.name = "Boot Agent",
.size = SZ_16K,
.offset = 0,
},
[1] = {
.name = "S3C2410 flash partition 1",
.offset = 0,
.size = SZ_2M,
},
[2] = {
.name = "S3C2410 flash partition 2",
.offset = SZ_4M,
.size = SZ_4M,
},
[3] = {
.name = "S3C2410 flash partition 3",
.offset = SZ_8M,
.size = SZ_2M,
},
[4] = {
.name = "S3C2410 flash partition 4",
.offset = SZ_1M * 10,
.size = SZ_4M,
},
[5] = {
.name = "S3C2410 flash partition 5",
.offset = SZ_1M * 14,
.size = SZ_1M * 10,
},
[6] = {
.name = "S3C2410 flash partition 6",
.offset = SZ_1M * 24,
.size = SZ_1M * 24,
},
[7] = {
.name = "S3C2410 flash partition 7",
.offset = SZ_1M * 48,
.size = SZ_16M,
}
};
这样NAND设备(连同设备的详细信息)就注册进了系统, 以后在nand的驱动注册后就会probe到并使用这里定义的资源信息.
二 NAND驱动流程
2410的驱动实现在driver\mtd\nand\S3c2410.c里
首先和其他驱动一样先通过module_init(), module_exit()注册一个初始化/卸载函数,
module_init(s3c2410_nand_init);
module_exit(s3c2410_nand_exit);
系统初始化时该函数被调用
static int __init s3c2410_nand_init(void)
{
printk("S3C24XX NAND Driver, (c) 2004 Simtec Electronics\n");
platform_driver_register(&s3c2412_nand_driver); /*注册nand驱动*/
platform_driver_register(&s3c2440_nand_driver); /*注册nand驱动*/
return platform_driver_register(&s3c2410_nand_driver); /*注册nand驱动*/
}
从上面可以看到我们注册了3个驱动程序, 但在系统probe时它只会匹配到s3c2410_nand_driver的驱动, 因为各个驱动的名字是不一样的, 而系统是安名字来probe的.
static struct platform_driver s3c2410_nand_driver = {
.probe = s3c2410_nand_probe,
.remove = s3c2410_nand_remove,
.suspend = s3c24xx_nand_suspend,
.resume = s3c24xx_nand_resume,
.driver = {
.name = "s3c2410-nand", /*这里的名字一定要与设备定义的名字相同*/
.owner = THIS_MODULE,
},
};
当系统probe到我们刚才注册的nand设备后即调用s3c2410_nand_probe函数
static int s3c2410_nand_probe(struct platform_device *dev)
{
return s3c24xx_nand_probe(dev, TYPE_S3C2410);
}
static int s3c24xx_nand_probe(struct platform_device *pdev, /*nand设备,前面已经列出*/
enum s3c_cpu_type cpu_type /*TYPE_S3C2410*/)
{
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);
/*该变量用来保存nand详细信息,以后访问nand信息都将从这个变量里得到*/
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"); /*获取用于nand的clock(nand也要时钟信号的哦)*/
if (IS_ERR(info->clk)) {
dev_err(&pdev->dev, "failed to get clock");
err = -ENOENT;
goto exit_error;
}
clk_enable(info->clk); /*使能该clock,实际上就是设置CLKCON的第四位(详见2410datasheet)*/
/* allocate and map the resource */
/* currently we assume we have the one resource */
res = pdev->resource; /*nand资源,见前面的定义*/
size = res->end - res->start + 1;
/*
* 请求指定的memory区域(实际上是nand的寄存器区域), 这是物理内存, 实际上只是检测该区域
* 是否空闲的
*/
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;
}
/*保存nand信息*/
info->device = &pdev->dev;
info->platform = plat;
/*虚实地址映射,以后程序里就可以直接访问nand的寄存器了*/
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); /*初始化nand硬件设备*/
if (err != 0)
goto exit_error;
/*接下来是MTD方面的初始化*/
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);
/*初始化nand chip实例*/
s3c2410_nand_init_chip(info, nmtd, sets);
/*初始化mtd?