最近一直在忙着工作上的事情,好久都没有更新博客了,发现最近思想是比较混乱的。学任何东西都坚持不下去,既然选择驱动开发这条路就要坚持下去。
之前分析了Linux块设备驱动,是以内存块来模拟的虚拟块设备。现在重点来分析Linux NAND FLASH驱动,移植过uboot的朋友都知道,在uboot中NAND FLASH驱动正是从Linux内核中移植过去的,Linux内核中对FLASH的驱动都是以MTD来实现的,现在就来揭秘MTD驱动的神秘面纱。
在Linux内核启动阶段,总能看到这样的打印信息(“S3C24XX NAND Driver, (c) 2004 Simtec Electronics”),没错,这就是NAND FLASH驱动中打印出来,我们就顺藤摸瓜的分析下去。
注:Linux内核版本为2.6.36.4
在drivers/mtd/nand/s3c2410.c中的s3c2410_nand_init函数即为驱动的入口。
s3c2410_nand_init
--> platform_driver_register(&s3c24xx_nand_driver)
主要工作就是注册了一个平台驱动。以下为该平台驱动的实例:
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,
},
};
platform_driver_register
--> driver_register
--> bus_add_driver
--> driver_attach
--> __driver_attach
--> driver_match_device 调用平台总线驱动的match(drv->bus->match ? drv->bus->match(dev, drv) : 1;)
--> platform_match
--> platform_match_id 将用platform_driver.id_table的name与平台设备的name进行匹配
--> driver_probe_device
--> really_probe
--> 最终调用平台驱动的probe方法:s3c24xx_nand_probe
下面就从这个函数开始分析:
s3c24xx_nand_probe
1.分配一个s3c2410_nand_info结构空间
info =kzalloc(sizeof(*info), GFP_KERNEL);
2.将该结构设置为平台设备的私有数据
platform_set_drvdata(pdev, info);
3.获取时钟,使能时钟
clk_get(&pdev->dev, "nand");
clk_enable(info->clk);
4.设置分配的s3c2410_nand_info结构
info->area = request_mem_region(res->start, size, pdev->name);
info->device = &pdev->dev;
info->platform = plat;// 平台数据
info->regs = ioremap(res->start, size); // 对NAND FLASH控制器对应的I/O内存进行映射
info->cpu_type = cpu_type;
5.初始化NAND FLASH控制器
s3c2410_nand_inithw(info);
涉及的数据结构:
struct s3c2410_nand_info {
struct nand_hw_controlcontroller;
struct s3c2410_nand_mtd*mtds;
struct s3c2410_platform_nand*platform;
struct device*device;
struct resource*area;
struct clk*clk;
void __iomem*regs;
void __iomem*sel_reg;
intsel_bit;
intmtd_count;
unsigned longsave_sel;
unsigned longclk_rate;
enum s3c_cpu_typecpu_type;
#ifdef CONFIG_CPU_FREQ
struct notifier_blockfreq_transition;
#endif
};
struct nand_hw_control {
spinlock_t lock;
struct nand_chip *active;
wait_queue_head_t wq;
};
struct s3c2410_nand_mtd {
struct mtd_infomtd;
struct nand_chipchip;
struct s3c2410_nand_set *set;
struct s3c2410_nand_info*info;
int scan_res;
};
描述NAND FLASH的平台信息
struct s3c2410_platform_nand {
/* 控制器的时序信息,所有的都是以纳秒为单位 */
inttacls; 从CLE/ALE到nWE/nOE 信号的有效时间
inttwrph0; nWE/nOE的有效时间
inttwrph1; 从nWE/nOE失效到CLE/ALE释放的时间
unsigned intignore_unset_ecc:1;
intnr_sets; 支持多少种配置
struct s3c2410_nand_set *sets; 配置集合
void(*select_chip)(struct s3c2410_nand_set *, int chip); 选择芯片的函数指针
};
用于描述S3C2410平台的NAND FLASH 配置
struct s3c2410_nand_set {
unsigned intdisable_ecc:1;是否禁止ECC校验
unsigned intflash_bbt:1;是否使用FLASH来存储坏块表
unsigned intoptions; 可选的标志
intnr_chips; 有多少片NAND FLASH
intnr_partitions;有多个分区
char
*name; 名字
int*nr_map;
struct mtd_partition*partitions; MTD分区表指针
struct nand_ecclayout*ecc_layout; ECC布局图指针
};
用来描述一个MTD分区
struct mtd_partition {
char *name;
分区的名字
uint64_t size; 分区的大小
uint64_t offset; 在整个MTD设备中的偏移地址
uint32_t mask_flags; 主MTD设备掩码对于该分区不起左右的掩码位
struct nand_ecclayout *ecclayout; ECC布局
};
用于描述ECC布局
struct nand_ecclayout {
__u32 eccbytes; 用于ECC校验的数据字节数
__u32 eccpos[64]; ECC数据的存放位置
__u32 oobavail; OOB区可用大小
struct nand_oobfree oobfree[8]; 用来描述一个OOB空闲区
};
用来描述OOB空闲区
struct nand_oobfree {
__u32 offset; 空闲区的偏移地址
__u32 length; 空闲取的长度
};
在/arch/arm/plat-s3c24xx/common-smdk.c中有s3c2410_platform_nand的实例:smdk_nand_info
static structs3c2410_platform_nandsmdk_nand_info = {
.tacls
= 20,
.twrph0
= 60,
.twrph1
= 20,
.nr_sets
= ARRAY_SIZE(smdk_nand_sets), // = 1支持一种配置
.sets
=smdk_nand_sets,
};
static struct s3c2410_nand_setsmdk_nand_sets[] = {
[0] = {
.name
= "NAND",
.nr_chips
= 1,只有一块NAND FLASH
.nr_partitions= ARRAY_SIZE(smdk_default_nand_part), 该NAND FLASH上分区数
.partitions=smdk_default_nand_part, NAND FLASH上的分区表
},
};
static struct mtd_partitionsmdk_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,
},
};
原文:http://blog.csdn.net/lpjybn/article/details/21046353