实践Linux的理论
全部博文(61)
分类:
2014-05-01 20:09:38
针对2.6.13中nand注册进platform的跟踪式情景分析
主要分析:
1.struct platform_device s3c_device_nand 和 struct device_driver s3c2440_nand_driver的对应,是靠名字对应的
2. 这里主要分析的是 s3c_device_nand 的这个platform_device的初始化和注册过程,这个过程不在驱动里面,具体的实现过程是在板级支持里面实现了
3. kernel 2.6.13 struct platform_device -------------------- struct device_driver
driver_register(struct device_driver * drv) --------------------driver_unregister(struct device_driver * drv)
paltform_device_register() -------------------- platform_device_unregister()
kernel 2.6.29.6 struct platform_device -------------------- struct platform_driver
platform_driver_register() -------------------- platform_driver_unregister()
platform_device_register() -------------------- platform_deivce_unregister()
4. 针对2.6.13中platform_device_register()的源码的分析,在2.6.29.6中已经大变样了
5. 对分区的支持,如何体现分区的,分区如何被注册进内核的
drivers/mtd/nand/s3c2410.c
kernel 2.6.13
1.struct platform_device s3c_device_nand 和 struct device_driver s3c2440_nand_driver的对应,是靠名字对应的
702 static struct device_driver s3c2440_nand_driver = {nand的驱动的结构
703 .name = "s3c2440-nand",
704 .bus = &platform_bus_type,
705 .probe = s3c2440_nand_probe,
706 .remove = s3c2410_nand_remove,
707 };
709 static int __init s3c2410_nand_init(void)
710 {
711 printk("S3C24XX NAND Driver, (c) 2004 Simtec Electronics\n");
712
713 driver_register(&s3c2440_nand_driver);
714 return driver_register(&s3c2410_nand_driver);
715 }
--------------------------------------------------------------------------------------
arch/arm/mach-s3c2410/devs.c 基本上定义了所有的板级支持设备结构
140 /* NAND Controller */
141
142 static struct resource s3c_nand_resource[] = {
143 [0] = {
144 .start = S3C2410_PA_NAND, //0x4E000000
145 .end = S3C2410_PA_NAND + S3C24XX_SZ_NAND, //0x4E000000 + 0x00100000(1MB)
146 .flags = IORESOURCE_MEM, //memory source
147 }
148 };
149
150 struct platform_device s3c_device_nand = {nand的设备是在这里定义的
151 .name = "s3c2410-nand",
152 .id = -1,
153 .num_resources = ARRAY_SIZE(s3c_nand_resource),
154 .resource = s3c_nand_resource,
155 };
156
157 EXPORT_SYMBOL(s3c_device_nand);
arch/arm/mach-s3c2410/s3c2440.c
192 void __init s3c2440_map_io(struct map_desc *mach_desc, int size)
193 {
194 /* register our io-tables */
195
196 iotable_init(s3c2440_iodesc, ARRAY_SIZE(s3c2440_iodesc));
197 iotable_init(mach_desc, size);
198
199 /* rename any peripherals used differing from the s3c2410 */
200
201 s3c_device_i2c.name = "s3c2440-i2c";设备在这里针对s3c2440重新命名,对应驱动s3c2440_nand_driver
202 s3c_device_nand.name = "s3c2440-nand";
203
204 /* change irq for watchdog */
205
206 s3c_device_wdt.resource[1].start = IRQ_S3C2440_WDT;
207 s3c_device_wdt.resource[1].end = IRQ_S3C2440_WDT;
208 }
2 . 这里主要分析的是 s3c_device_nand 的这个platform_device的初始化和注册过程,这个过程不在驱动里面,具体的实现过程是在板级支持里面实现了
倒着追溯的
arch/arm/mach-s3c2410/cpu.c nand设备最终在这个文件中被注册
208 static int __init s3c_arch_init(void)
209 {
210 int ret;
211
212 // do the correct init for cpu
213
214 if (cpu == NULL)
215 panic("s3c_arch_init: NULL cpu\n");
216
217 ret = (cpu->init)();
218 if (ret != 0)
219 return ret;
220
221 if (board != NULL) {
222 struct platform_device **ptr = board->devices;这里的board是struct s3c24xx_board,在本文件cpu.c中,是全局变量,他的初始化是在
223 int i; 本文件中的s3c24xx_set_board()函数中完成的,
224
225 for (i = 0; i < board->devices_count; i++, ptr++) {
226 ret = platform_device_register(*ptr);就是在这里完成对各种设备(包括上面提到的s3c_device_nand)的注册的,
227 这是板级的支持,在板级的初始化中完成的
228 if (ret) {
229 printk(KERN_ERR "s3c24xx: failed to add board device %s (%d) @%p\n", (*ptr)- >name, ret, *ptr);
230 }
231 }
232
233 /* mask any error, we may not need all these board
234 * devices */
235 ret = 0;
236 }
237
238 return ret;
239 }
240
241 arch_initcall(s3c_arch_init);
上面用到的board来自这里
arch/arm/mach-s3c2410/cpu.h
52 struct s3c24xx_board {
53 struct platform_device **devices;
54 unsigned int devices_count;
55
56 struct clk **clocks;
57 unsigned int clocks_count;
58 };
arch/arm/mach-s3c2410/cpu.c
127 /* board information */
128
129 static struct s3c24xx_board *board;在文件cup.c中是全局变量,在s3c24xx_set_board()被初始化,在
130 s3c_arch_init()中被注册
131 void s3c24xx_set_board(struct s3c24xx_board *b)
132 {
133 int i;
134
135 board = b; 在这里定义成了全局变量,所以才在s3c_arch_init()中可用
136 这里之所以能对struct s3c24xx_board * board初始化,是因为在文件
137 if (b->clocks_count != 0) { arch/arm/mach-s3c2410/mach-bit2440.c +438 中的 void __init bit2440_map_io(void)
138 struct clk **ptr = b->clocks;; 对s3c24xx_set_board()的调用,,这个b就是从那里传递过去的,具体见下面
139
140 for (i = b->clocks_count; i > 0; i--, ptr++)
141 s3c24xx_register_clock(*ptr);
142 }
143 }
arch/arm/mach-s3c2410/mach-bit2440.c
389 static struct platform_device *bit2440_devices[] __initdata = {
在这里加载必要的设备,这些设备会在启动时被加载
390 &s3c_device_usb,
391 &s3c_device_lcd,
392 &s3c_device_wdt,
393 &s3c_device_i2c,
394 &s3c_device_iis,
395 &s3c_device_sdi,
396 &s3c_device_usbgadget,
397 &s3c_device_ts,
398 &s3c_device_nand,
399 &s3c_device_sound,
400 &s3c_device_buttons,
401 &s3c_device_rtc,
402 };
404 static struct s3c24xx_board bit2440_board _ _initdata = { 将所有的设备,加入到板级支持里面
405 .devices = bit2440_devices,
406 .devices_count = ARRAY_SIZE(bit2440_devices)
407 };
408
409 void __init bit2440_map_io(void)
410 {
411 u32 val;//yu
412
413 s3c24xx_init_io(bit2440_iodesc, ARRAY_SIZE(bit2440_iodesc));
414
415 #ifdef CONFIG_S3C2440_INCLK12
416 s3c24xx_init_clocks(12000000);
417 #else
418 s3c24xx_init_clocks(16934400);
419 #endif
420
421 //Add by yu
422 val = readl(S3C2410_GPHCON);
423 //printk("old gphcon = %x\n\n\n", val);
424 val &= ~(0xfff << 4);
425 val |= (0xaaa << 4);
426 writel(val, S3C2410_GPHCON);//inialize RxD2 and TxD2
427
428 val = readl(S3C2410_GPHUP);
429 val |= 0x3f << 2;
430 writel(val, S3C2410_GPHUP);
431 val = readl(S3C2410_GPHUP);
432 //printk("new gphup = %x\n", val);
433 ///////////////////////////
434
435 s3c24xx_init_uarts(bit2440_uartcfgs, ARRAY_SIZE(bit2440_uartcfgs));
436 s3c24xx_set_board(&bit2440_board);bit2440_board就是在这里被初始化,然后被s3c24xx_set_board()传到cpu.c 中,进而被
437 s3c_arch_init()完成注册
438 s3c_device_nand.dev.platform_data = &bit_nand_info;
439 }
而bit2440_map_io()是在这里最终别调用的
476 MACHINE_START(BIT2440, "QT-2440")
477 .phys_ram = S3C2410_SDRAM_PA,
478 .phys_io = S3C2410_PA_UART,
479 .io_pg_offst = (((u32)S3C24XX_VA_UART) >> 18) & 0xfffc,
480 .boot_params = S3C2410_SDRAM_PA + 0x100,
481
482 .init_irq = bit2440_init_irq,
483 .map_io = bit2440_map_io,
484 .init_machine = bit2440_init,
485 .timer = &s3c24xx_timer,
486 MACHINE_END
通过这个宏最终实现了建立板级的结构struct machine_desc
再往下说,就是linux启动的过程的分析了
内核启动时将运行head.S,再其中将调用.../init/main.c中的start_kernel的函数
asmlinkage void __init start_kernel(void)
{
......
setup_arch(&command_line);
// 1.找到所属的machine_desc,即在arch/arm/mach-s3c2410/qt2410.c中最后定义的一个结构。
// 2.paging_init(&meminfo, mdesc)->devicemaps_init(mdesc)->mdesc->map_io();
// 3.找到该machine_desc定义的初始化函数句柄:
init_arch_irq = mdesc->init_irq;
system_timer = mdesc->timer;
init_machine = mdesc->init_machine;
......
==============================================================================
4. 针对2.6.13中platform_device_register()的源码的分析,在2.6.29.6中已经大变样了
116 /**
117 * platform_device_register - add a platform-level device
118 * @pdev: platform device we're adding
119 *
120 */
121 int platform_device_register(struct platform_device * pdev)
122 {
123 int i, ret = 0;
124
125 if (!pdev)
126 return -EINVAL;
127 //关联父设备,如果没有父设备,那么就设platform_bus为其父设备
128 if (!pdev->dev.parent)
129 pdev->dev.parent = &platform_bus;
130
131 pdev->dev.bus = &platform_bus_type;
132 //id==-1,只是名字的不同
133 if (pdev->id != -1)
134 snprintf(pdev->dev.bus_id, BUS_ID_SIZE, "%s.%u", pdev->name, pdev->id);
135 else
136 strlcpy(pdev->dev.bus_id, pdev->name, BUS_ID_SIZE);
137 //请求分配一些资源
138 for (i = 0; i < pdev->num_resources; i++) {
139 struct resource *p, *r = &pdev->resource[i];
140
141 if (r->name == NULL)
142 r->name = pdev->dev.bus_id;
143
144 p = r->parent;
145 if (!p) {
146 if (r->flags & IORESOURCE_MEM)
147 p = &iomem_resource;
148 else if (r->flags & IORESOURCE_IO)
149 p = &ioport_resource;
150 }
151
152 if (p && request_resource(p, r)) {
153 printk(KERN_ERR
154 "%s: failed to claim resource %d\n",
155 pdev->dev.bus_id, i);
156 ret = -EBUSY;
157 goto failed;
158 }
159 }
160
161 pr_debug("Registering platform device '%s'. Parent at %s\n",
162 pdev->dev.bus_id, pdev->dev.parent->bus_id);
163 //将设备注册进bus中,可以在sys/bus/platform中看到
164 ret = device_register(&pdev->dev);
165 if (ret == 0)
166 return ret;
167
168 failed:
169 while (--i >= 0)
170 if (pdev->resource[i].flags & (IORESOURCE_MEM|IORESOURCE_IO))
171 release_resource(&pdev->resource[i]);
172 return ret;
173 }
5. 对分区的支持,如何体现分区的,分区设备信息如何被注册进内核的,但没有被注册分区
注册分区的完成是驱动的事情,也就是这个过程虽然完成了分区信息的填充,但在系统中体
现不出来,只能是在驱动调用add_mtd_partions之后才可以看到分区
这里的分区是如何被s3c24xx_nand_probe()中的功能给加载上去的
Nand Flash 设备添加时数据结构包含关系
struct mtd_partition bit_default_nand_part[]
--> struct s3c2410_nand_set bit_nand_set[]
--> struct s3c2410_platform_nand bit_nand_info
--> struct platform_device s3c_device_nand
在该数据结构的 name 字段的初始化值"s3c2410-nand",必须
与 Nand Flash 设备驱动注册时struct device_driver结构中的
name 字段相同,因为 platfrom bus 是依靠名字来匹配的.
--> struct platform_device *bit_2410_devices[]
arch/arm/mach-s3c2410/mach-bit2440.c 在这里分区被初始化
278 static int chip0_map[] = { 0 }; //add by lili
279 struct mtd_partition bit_default_nand_part[] = { struct mtd_partitio bit_default_nand_part[]
280 [0] = {
281 .name = "bootloader",
282 .offset = 0x00000000,
283 .size = 0x00030000,
284 },
285 [1] = {
286 .name = "kernel",
287 .offset = 0x00030000,
288 .size = 0x00200000,
289 },
290 [2] = {
291 .name = "root",
292 .offset = 0x00230000,
293 .size = 0x03dcc000,
294 }
295 };
296
297 /* the bit has 1 selectable slots for nand-flash, the three
298 * on-board chip areas, as well as the external SmartMedia
299 * slot.
300 *
301 * Note, there is no current hot-plug support for the SmartMedia
302 * socket.
303 */
304
305 static struct s3c2410_nand_set bit_nand_sets[] = { struct s3c2410_nand_set bit_nand_sets[]
分区信息被包含在这个结构体中
306 [0] = {
307 .name = "chip0",
308 .nr_chips = 1,
309 .nr_map = chip0_map,
310 .nr_partitions = ARRAY_SIZE(bit_default_nand_part),
311 .partitions = bit_default_nand_part
312 },
313 };
314
315
316 static struct s3c2410_platform_nand bit_nand_info = { struct s3c2410_platform_nand bit_nand_info
最终被包含在bit_nand_info中
317 .tacls = 0,
318 .twrph0 = 30,
319 .twrph1 = 0,
320 .nr_sets = ARRAY_SIZE(bit_nand_sets),
321 .sets = bit_nand_sets,
322 };
arch/arm/mach-s3c2410/devs.c
struct platform_device s3c_device_nand = { struct platform_device s3c_device_nand
.name = "s3c2410-nand"
.id = -1,
.num_resources = ARRAY_SIZE(s3c_nand_resource),
.resource = s3c_nand_resource,
};
也可以这样的
----------------------------------------------
struct platform_device s3c_device_nand = {
.name = "s3c2410-nand",
.id = -1,
.num_resources = ARRAY_SIZE(s3c_nand_resource),
.resource = s3c_nand_resource,
.dev = {
.platform_data = &bit_nand_info
}
};
如果这样的话,那么在bit2440_map_io()中也就不用再此给dev.platform赋值了
-----------------------------------------------
而bit_nand_info是在这个函数中被加入到s3c_device_nand 当中的
409 void __init bit2440_map_io(void)
410 {
…...
…...
435 s3c24xx_init_uarts(bit2440_uartcfgs, ARRAY_SIZE(bit2440_uartcfgs));
436 s3c24xx_set_board(&bit2440_board);
437
438 s3c_device_nand.dev.platform_data = &bit_nand_info;
439 } 就是这里,将s3c2410_platform_nand 结构(其中包括mtd_pat分区信息)
放在(void *)dev->platform_data中,而这个dev又包含在s3c_device_nand中,
这样分区信息就被包含在s3c_device_nand 设备中被加入到了板级支持中,
这些过程在板子启动过程中就完成了(驱动加载之前完成的)
加载驱动的时候,调用platform_driver_register(&s3c2440_nand_driver),此时
此时驱动s3c2440_nand_driver会在platform 总线上调用总线的match函数
,依据名字来查找和自己名字相同的设备,这里我们的驱动的名字是
“s3c2440-nand”, 由于我们在板级支持里已经将名字为“s3c2440-nand”的设
备s3c_device_nand完成了注册,所以这里能找到。
一但驱动s3c2440_nand_probe找到和他名字向匹配的设备,会马上调用
s3c2440_nand_probe(struct device *dev)-->s3c24xx_nand_probe(sturct device
*dev ,int is_s3c2440),
这里注意probe函数的参数是struct device *dev ,这一点很好理解,如果驱
提前能知道他需要设备一定存在,还用platform总线的match函数匹配干
吗!!嘿嘿
还有一点,就是在设备注册的过程中,你一定还记得在 bit2440_map_io()
中有:s3c_device_nand.dev.platform_data = &bit_nand_info.到这里一切才终
于真想大白,你在probe函数里只需
struct platform_device *pdev = to_platform_device(dev);
//get platform_device(continer_of())
struct s3c2410_platform_nand *plat = to_nand_plat(dev);
//get dev->platform_data
这样你就可以很轻而易举的在驱动中享受设备中已经定义好的资源了
上面是针对2.6.13,版本有点来,到了2.6.29.6中,s3c24xx_nand_probe()的参
书就成了platform_device *dev, 当然这样更好,省的再像老版本中的那样需要
struct platform_device *pdev = to_platform_device(dev);
//get platform_device(continer_of())
才能得到platform_device 这个结构体,嘿嘿,只要明白大概的原理,基本上对新
内核也是不成问题的
----------------------------------------------------------------------------------------------------------------
接下来,s3c_device_nand 是这样被加入到板级支持中的
389 static struct platform_device *bit2440_devices[] __initdata = { struct platform_device *bit2440_devices[]
…....
…....
397 &s3c_device_ts,
398 &s3c_device_nand,
…....
402 };
403
404 static struct s3c24xx_board bit2440_board __initdata = {
405 .devices = bit2440_devices,
406 .devices_count = ARRAY_SIZE(bit2440_devices)
407 };
------------------------------------------------------------------------------------------------------------------
最终在这里通过这个宏,建立板级支持,
476 MACHINE_START(BIT2440, "QT-2440")
477 .phys_ram = S3C2410_SDRAM_PA,
478 .phys_io = S3C2410_PA_UART,
479 .io_pg_offst = (((u32)S3C24XX_VA_UART) >> 18) & 0xfffc,
480 .boot_params = S3C2410_SDRAM_PA + 0x100,
481
482 .init_irq = bit2440_init_irq,
483 .map_io = bit2440_map_io,
484 .init_machine = bit2440_init,
485 .timer = &s3c24xx_timer,
486 MACHINE_END
一旦这样,那么nand的分区信息就体现在设备中了,最终nand驱动在匹配device
的时候,就能知道有几个nand,每个nand是怎样分区的了,最终在s3c24xx_nand_probe()
中,完成每个nand,每个分区的注册
platform_device与驱动的联系
首先你需要为SOC的各个功能部分定义他的一些资源.例如可用于访问的寄存器地址.中断号,DMA什么的。然后将这些资源(resource) 作为 platform 的dev .通过platform_add_devices函数将你定义的paltform_device变量注册到系统的dev里面.。或者你可以象我这样将你需要的驱动添加:
static struct platform_device *smdk2410_devices[] __initdata = {
&s3c_device_usb,
&s3c_device_lcd,
&s3c_device_bl,
&s3c_device_wdt,
&s3c_device_i2c,
&s3c_device_iis,
&s3c_device_sdi,
&s3c_device_adc,
&s3c_device_nand,
&s3c_device_usbgadget,
&s3c_device_ts,
&s3c_device_buttons,
&s3c_device_rtc,
&s3c_device_spi0,
&s3c_device_timer1,//add by cefanty for battery charging
};
这样你的硬件的信息和资源就会注册到系统中.
说了半天,这回该说这有什么用了。
你编写的驱动或者移植别人的驱动,一般在驱动里有这样的代码,例如:
static struct platform_driver s3c2410sdi_driver =
{
.probe = s3c2410sdi_probe,
.remove = s3c2410sdi_remove,
.suspend= s3c2410mci_suspend,
.resume= s3c2410mci_resume,
.driver={
.name= "s3c2410-sdi",
.bus = &platform_bus_type,
.owner= THIS_MODULE,
},
};
看到 .name= "s3c2410-sdi",这条关键的语句没有??,它和我在上面注册的&s3c_device_sdi,里的device的名称是一致的.我这里展开我的s3c_device_sdi,的内容
:
/* SDI */
static struct resource s3c_sdi_resource[] = {
[0] = {
.start = S3C2410_PA_SDI,
.end = S3C2410_PA_SDI + S3C24XX_SZ_SDI - 1,
.flags = IORESOURCE_MEM,
},
[1] = {
.start = IRQ_SDI,
.end = IRQ_SDI,
.flags = IORESOURCE_IRQ,
},
[2] = {
.start = 3,
.end = 3,
.flags = IORESOURCE_DMA,
}
};
struct platform_device s3c_device_sdi = {
.name = "s3c2410-sdi",
.id = -1,
.num_resources = ARRAY_SIZE(s3c_sdi_resource),
.resource = s3c_sdi_resource,
};
在驱动程序里的init代码大致如下:
static int __init s3c2410sdi_init(void)
{
return platform_driver_register(&s3c2410sdi_driver);
}
用platform_driver_register 向系统注册这个驱动程序.而这个函数会在s3c2410sdi_driver的信息里提取name为搜索内容,搜索系统注册的device中有没有这个 platform_device。如果有注册,那么接着会执行platform_driver 里probe函数.在这里显然是s3c2410sdi_probe函数 在probe函数里,用的最多和刚才platform_device有关的语句是platform_get_resource,这条语句用于获取 platform_device里的resource资料.例如映射的IO地址,中断等.剩下等得就是ioremap,和 request_irq等的事情了.