硬件平台:s3c2440
内核版本:Linux3.4.2
最近在做s5pv210的nand flash移植时发现以前看过的关于在s3c2440平台下的nand flash驱动框架都忘了,这次准备再次看一遍。从mach-mini2440.c中文件看起。找到这些platform_device数组,实际就每一个外设接口。很容易看到 &s3c_device_nand。
static struct platform_device *mini2440_devices[] __initdata = {
&s3c_device_ohci,
&s3c_device_wdt,
&s3c_device_i2c0,
&s3c_device_rtc,
&s3c_device_usbgadget,
&mini2440_device_eth,
&mini2440_led1,
&mini2440_led2,
&mini2440_led3,
&mini2440_led4,
&mini2440_button_device,
&s3c_device_nand,
&s3c_device_sdi,
&s3c_device_iis,
&uda1340_codec,
&mini2440_audio,
&samsung_asoc_dma,
};
然后会在devs.c文件中定位到s3c_device_nand结构体的定义和初始化。可以看到platform_device类型的结构体变量s3c_device_nand 定义如下, 同时在后面还可以看到如何设置nand的硬件参数:s3c_nand_set_platdata(&mini2440_nand_info);此函数是在mach-mini2440.c中mini2440_init()函数被调用。但是mini2440_nand_info是在mach-mini2440.c中定义的, mini2440_nand_info是关于硬件时序和nand flash分区等方面的参数。通过s3c_nand_set_platdata(&mini2440_nand_info)函数将mini2440_nand_info存储在s3c_device_nand.dev.platform_data,如下:
#ifdef CONFIG_S3C_DEV_NAND
static struct resource s3c_nand_resource[] = {
[0] = DEFINE_RES_MEM(S3C_PA_NAND, SZ_1M),
};
struct platform_device s3c_device_nand = {
.name = "s3c2410-nand",
.id = -1,
.num_resources = ARRAY_SIZE(s3c_nand_resource),
.resource = s3c_nand_resource,
};
/*
* s3c_nand_copy_set() - copy nand set data
* @set: The new structure, directly copied from the old.
*
* Copy all the fields from the NAND set field from what is probably __initdata
* to new kernel memory. The code returns 0 if the copy happened correctly or
* an error code for the calling function to display.
*
* Note, we currently do not try and look to see if we've already copied the
* data in a previous set.
*/
static int __init s3c_nand_copy_set(struct s3c2410_nand_set *set)
{
void *ptr;
int size;
size = sizeof(struct mtd_partition) * set->nr_partitions;
if (size) {
ptr = kmemdup(set->partitions, size, GFP_KERNEL);
set->partitions = ptr;
if (!ptr)
return -ENOMEM;
}
if (set->nr_map && set->nr_chips) {
size = sizeof(int) * set->nr_chips;
ptr = kmemdup(set->nr_map, size, GFP_KERNEL);
set->nr_map = ptr;
if (!ptr)
return -ENOMEM;
}
if (set->ecc_layout) {
ptr = kmemdup(set->ecc_layout,
sizeof(struct nand_ecclayout), GFP_KERNEL);
set->ecc_layout = ptr;
if (!ptr)
return -ENOMEM;
}
return 0;
}
void __init s3c_nand_set_platdata(struct s3c2410_platform_nand *nand)
{
struct s3c2410_platform_nand *npd;
int size;
int ret;
/* note, if we get a failure in allocation, we simply drop out of the
* function. If there is so little memory available at initialisation
* time then there is little chance the system is going to run.
*/
npd = s3c_set_platdata(nand, sizeof(struct s3c2410_platform_nand),
&s3c_device_nand);
/************************************************************************/
#if 0
函数实现,方便看
void __init *s3c_set_platdata(void *pd, size_t pdsize,
struct platform_device *pdev)
{
void *npd;
npd = kmemdup(pd, pdsize, GFP_KERNEL);
if (!npd) {
printk(KERN_ERR "%s: cannot clone platform data\n", pdev->name);
return NULL;
}
pdev->dev.platform_data = npd;
return npd;
}
#endif
/************************************************************************/
if (!npd)
return;
/* now see if we need to copy any of the nand set data */
size = sizeof(struct s3c2410_nand_set) * npd->nr_sets;
if (size)
{
struct s3c2410_nand_set *from = npd->sets;
struct s3c2410_nand_set *to;
int i;
to = kmemdup(from, size, GFP_KERNEL);
npd->sets = to; /* set, even if we failed */
if (!to) {
printk(KERN_ERR "%s: no memory for sets\n", __func__);
return;
}
for (i = 0; i < npd->nr_sets; i++)
{
ret = s3c_nand_copy_set(to);
if (ret) {
printk(KERN_ERR "%s: failed to copy set %d\n",
__func__, i);
return;
}
to++;
}
}
}
#endif /* CONFIG_S3C_DEV_NAND */
/***********************************************************************************/
/* NAND Flash on MINI2440 board */
static struct mtd_partition mini2440_default_nand_part[] __initdata = {
[0] = {
.name = "u-boot",
.size = SZ_256K,
.offset = 0,
},
[1] = {
.name = "u-boot-env",
.size = SZ_128K,
.offset = SZ_256K,
},
[2] = {
.name = "kernel",
/* 5 megabytes, for a kernel with no modules
* or a uImage with a ramdisk attached */
.size = 0x00500000,
.offset = SZ_256K + SZ_128K,
},
[3] = {
.name = "root",
.offset = SZ_256K + SZ_128K + 0x00500000,
.size = MTDPART_SIZ_FULL,
},
};
static struct s3c2410_nand_set mini2440_nand_sets[] __initdata = {
[0] = {
.name = "nand",
.nr_chips = 1,
.nr_partitions = ARRAY_SIZE(mini2440_default_nand_part),
.partitions = mini2440_default_nand_part,
.flash_bbt = 1, /* we use u-boot to create a BBT */
},
};
static struct s3c2410_platform_nand mini2440_nand_info __initdata = {
.tacls = 0,
.twrph0 = 25,
.twrph1 = 15,
.nr_sets = ARRAY_SIZE(mini2440_nand_sets),
.sets = mini2440_nand_sets,
.ignore_unset_ecc = 1,
};
根据platform_bus可知,这里注册完platform_device之后,还需要有platform_driver的注册,根据platform_device s3c_device_nand中.name = "s3c2410-nand",可匹配到driver/mtd/nand/s3c2410.c
/* driver device registration */
static struct platform_device_id s3c24xx_driver_ids[] = {
{
.name = "s3c2410-nand",
.driver_data = TYPE_S3C2410,
}, {
.name = "s3c2440-nand",
.driver_data = TYPE_S3C2440,
}, {
.name = "s3c2412-nand",
.driver_data = TYPE_S3C2412,
}, {
.name = "s3c6400-nand",
.driver_data = TYPE_S3C2412, /* compatible with 2412 */
},
};
static struct platform_driver s3c24xx_nand_driver = {
.probe = s3c24xx_nand_probe,
.remove = s3c24xx_nand_remove,
.suspend = s3c24xx_nand_suspend,
.resume = s3c24xx_nand_resume,
.id_table = s3c24xx_driver_ids,
.driver = {
.name = "s3c24xx-nand",
.owner = THIS_MODULE,
},
};
static int __init s3c2410_nand_init(void)
{
printk("S3C24XX NAND Driver, (c) 2004 Simtec Electronics\n");
return platform_driver_register(&s3c24xx_nand_driver);
}
当platform_driver变量中的name 和platform_device变量中的name相同时,就会调用paltform_driver中probe函数。即s3c24xx_nand_probe函数,其中完成的内容大致为:
1、nand flash控制器寄存器地址映射。
2、nand flash控制器硬件初始化,主要是在s3c2410_nand_inithw中调用s3c2410_nand_setrate函数来设置硬件时序。
3、获取Soc中nand模块的时钟频率。
4、根据设定的参数来算出有几块nand flash(一般是一块),然后初始化操作nand flash方法。初始化内容为,nand flash的读写方法,片选方法,发送地址,命令方法和硬件ECC校验等。
5、开始识别nand flash型号,在nand_ids.c中有一个全局数组,内部存储了各厂家各种版本的nand flash型号,识别过程是与该全局数组进行一一匹配。成功返回该类型,失败错误码。
6、解析为nand flash设置的分区信息(记录在mach-mini2440.c -> mini2440_default_nand_part数组中)。
/* s3c24xx_nand_probe
*
* called by device layer when it finds a device matching
* one our driver can handled. This code checks to see if
* it can allocate all necessary resources then calls the
* nand layer to look for devices
*/
static int s3c24xx_nand_probe(struct platform_device *pdev) // pdev = s3c_device_nand
{
/*
static struct resource s3c_nand_resource[] = {
[0] = DEFINE_RES_MEM(S3C_PA_NAND, SZ_1M),
};
struct platform_device s3c_device_nand = {
.name = "s3c2410-nand",
.id = -1,
.num_resources = ARRAY_SIZE(s3c_nand_resource),
.resource = s3c_nand_resource,
};
*/
/*
static struct s3c2410_nand_set mini2440_nand_sets[] __initdata = {
[0] = {
.name = "nand",
.nr_chips = 1,
.nr_partitions = ARRAY_SIZE(mini2440_default_nand_part),
.partitions = mini2440_default_nand_part,
.flash_bbt = 1, // we use u-boot to create a BBT
},
};
static struct s3c2410_platform_nand mini2440_nand_info __initdata = {
.tacls = 0,
.twrph0 = 25,
.twrph1 = 15,
.nr_sets = ARRAY_SIZE(mini2440_nand_sets),
.sets = mini2440_nand_sets,
.ignore_unset_ecc = 1,
};
plat = mini2440_nand_info
*/
struct s3c2410_platform_nand *plat = to_nand_plat(pdev);// pdev = s3c_device_nand ;
//plat = mini2440_nand_info
enum s3c_cpu_type cpu_type;
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;
cpu_type = platform_get_device_id(pdev)->driver_data;//pdev = s3c_device_nand
info = kzalloc(sizeof(*info), GFP_KERNEL);
platform_set_drvdata(pdev, info); //pdev = s3c_device_nand
//pdev->dev->p->driver_data = info
/* get the clock source and enable it */
info->clk = clk_get(&pdev->dev, "nand");
s3c2410_nand_clk_set_state(info, CLOCK_ENABLE);
/* allocate and map the resource */
/* currently we assume we have the one resource */
/*
static struct resource s3c_nand_resource[] = {
[0] = DEFINE_RES_MEM(S3C_PA_NAND, SZ_1M),
};
struct platform_device s3c_device_nand = {
.name = "s3c2410-nand",
.id = -1,
.num_resources = ARRAY_SIZE(s3c_nand_resource),
.resource = s3c_nand_resource,
};
*/
res = pdev->resource;
size = resource_size(res);
info->area = request_mem_region(res->start, size, pdev->name);
info->device = &pdev->dev;
info->platform = plat; // info->platform = plat,plat = mini2440_nand_info
info->regs = ioremap(res->start, size);
info->cpu_type = cpu_type;
/* initialise the hardware */
err = s3c2410_nand_inithw(info);
if (err != 0)
goto exit_error;
/*
static struct s3c2410_nand_set mini2440_nand_sets[] __initdata = {
[0] = {
.name = "nand",
.nr_chips = 1,
.nr_partitions = ARRAY_SIZE(mini2440_default_nand_part),
.partitions = mini2440_default_nand_part,
.flash_bbt = 1, // we use u-boot to create a BBT
},
};
static struct s3c2410_platform_nand mini2440_nand_info __initdata = {
.tacls = 0,
.twrph0 = 25,
.twrph1 = 15,
.nr_sets = ARRAY_SIZE(mini2440_nand_sets),
.sets = mini2440_nand_sets,
.ignore_unset_ecc = 1,
};
下面的plat = mini2440_nand_info
*/
sets = (plat != NULL) ? plat->sets : NULL;//plat->sets =mini2440_nand_info->sets = mini2440_nand_sets
nr_sets = (plat != NULL) ? plat->nr_sets : 1;
info->mtd_count = nr_sets;
/* allocate our information */
size = nr_sets * sizeof(*info->mtds);
info->mtds = kzalloc(size, GFP_KERNEL);
/* 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 = info->mtds;而info->mtds被刚刚动态分配,没有实质性内容
nmtd->scan_res = nand_scan_ident(&nmtd->mtd,(sets) ? sets->nr_chips : 1, NULL);//成功返回0
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++;
}
return err;
}
根据probe函数中添加的nand flash操作方法,当需要进行读写数据时,可先发地址、命令,然后根据相应的读写函数即可以正常读写nand flash。
顺便把自己看代码时整理的粗糙的流程图贴出来,这样可能看起来会整体框架把握较为清晰。
![](https://img-blog.csdnimg.cn/20190531103900797.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MTY4MjE2OQ==,size_16,color_FFFFFF,t_70)