对于linux的IIC子系统,看了很多人的IIC源码及架构分析,看完了自己还是一头雾水,不知从何下手。因此只能看开机启动LOG分析各初始化函数,先看linux IIC子系统已经为我们做了些什么,然后再看还需要我们做些什么。 从而了解整个IIC子系统架构。
1.linux初始化函数的执行顺序
决定函数执行顺序的有两个因素:
(一)vmlinux.lds 链接脚本
(二)驱动目录下的Makefile 文件定义
1.1 vmlinux.lds 链接脚本
该脚本位于/arch/arm/kernel/ vmlinux.lds ,该脚本规定了不同代码段,如_init, test, data等不同属性代码存放的位置。
vimlinux.lds
OUTPUT_ARCH(arm)
353 ENTRY(stext)
354 jiffies = jiffies_64;
355 SECTIONS
356 {
357 . = 0xC0000000 + 0x00008000;
358 .text.head : {
359 _stext = .;。。。。。。
382 __initcall_start = .;
383 *(.initcallearly.init) __early_initcall_end = .; *(.initcall0.init) *(.initcall0s.init) *(.initcall1.init ) *(.initcall1s.init) *(.initcall2.init) *(.initcall2s.init) *(.initcall3.init) *(.initcall3s.init) *(.initc all4.init) *(.initcall4s.init) *(.initcall5.init) *(.initcall5s.init) *(.initcallrootfs.init) *(.initcall6.i nit) *(.initcall6s.init) *(.initcall7.init) *(.initcall7s.init)
384 __initcall_end = .;。。。。。。
402 }
看383行可以知道存放链接的顺序依次为:initcall0.init, initcall0s.init......
我们再看/include/linux/init.h中的定义:
183 * This only exists for built-in code, not for modules.
184 */
185 #define pure_initcall(fn) __define_initcall("0",fn,0)
186
187 #define core_initcall(fn) __define_initcall("1",fn,1)
188 #define core_initcall_sync(fn) __define_initcall("1s",fn,1s)
189 #define postcore_initcall(fn) __define_initcall("2",fn,2)
190 #define postcore_initcall_sync(fn) __define_initcall("2s",fn,2s)
191 #define arch_initcall(fn) __define_initcall("3",fn,3)
192 #define arch_initcall_sync(fn) __define_initcall("3s",fn,3s)
193 #define subsys_initcall(fn) __define_initcall("4",fn,4)
194 #define subsys_initcall_sync(fn) __define_initcall("4s",fn,4s)
195 #define fs_initcall(fn) __define_initcall("5",fn,5)
196 #define fs_initcall_sync(fn) __define_initcall("5s",fn,5s)
197 #define rootfs_initcall(fn) __define_initcall("rootfs",fn,rootfs)
198 #define device_initcall(fn) __define_initcall("6",fn,6)
199 #define device_initcall_sync(fn) __define_initcall("6s",fn,6s)
200 #define late_initcall(fn) __define_initcall("7",fn,7)
201 #define late_initcall_sync(fn) __define_initcall("7s",fn,7s)
从这里可以看出来,在实际驱动中,module_init(init_func) 最终展开后会将init_func做个initcall6.init的标记,最终该函数就被链接到initcall6.init的位置。在内核编译链接完成之后函数初始化的执行先后顺序就已经确定下了了。如果两个函数属性相同,比如module_init(inti_funcA); module_init(inti_funcB); 他们都属于initcall6.init,那么他们执行的先后顺序就跟Makefile文件有关了。
1.2 Makefile文件的控制
在/driver/i2c/Makefile:
1 # 2 # Makefile for the i2c core.3 #4 5 obj-$(CONFIG_I2C_BOARDINFO) += i2c-boardinfo.o6 obj-$(CONFIG_I2C) += i2c-core.o7 obj-$(CONFIG_I2C_CHARDEV) += i2c-dev.o8 obj-y += busses/ chips/ algos/
在Makefile中,函数的连接顺序是按函数的存放位置先后决定的。这里可以看到是i2c-boardinf, i2c-core,i2c-dev...
2. IIC子系统初始化顺序
根据上面的分析我们可以知道在linux系统中iic子系统的初始化顺序为:
1. /driver/i2c/i2c-core.c postcore_initcall(i2c_init);
2. /arch/arm/mach-s3c2440 MACHINE_START(S3C2440, "SMDK2440")
3. /drivers/i2c/busses/i2c-s3c2410.c subsys_initcall(i2c_adap_s3c_init);
4./driver/i2c/i2c-dev.c module_init(i2c_dev_init);
说明:
1.分析的内核版本是linux2.6.32.2
2.开发板为友善之臂的mini2440, 用的是ARM9(S3C2440A)处理器
3.链接的IIC设备是EEPROM(AT24C02)
4.按照内核I2C子系统的注册顺序分析。