九鼎创展论坛中文版English
登录 | 立即注册 设为首页收藏本站 切换到宽版
查看: 3978|回复: 0

I2C子系统之内核中I2C子系统的结构

[复制链接]
发表于 2014-10-23 18:44:20 | 显示全部楼层 |阅读模式

本文开始,分析内核的i2c子系统。

说明:1.分析的内核版本为2.6.37.1

           2.开发板为TQ2440,板载ARM9(S3C2440)

           3.I2C设备为AT24C02

           4.分析顺序就是内核I2C子系统的注册顺序(即本系列文章发表的先后顺序)。

在正式进入代码分析前应摸清各初始化函数的执行先后顺序,清楚这个顺序后对i2c的整个框架也有就有数了。

1.初始化函数的执行顺序

1.1 函数执行顺序是如何确定的

内核编译链接完成后初始化函数的执行先后顺序就确定了。这是通过链接器在链接时调用链接脚本/arch/arm/kernel/vmlinux.lds来完成的。脚本规定了不同代码段,例如_init、text、data等不同属性的代码段存放的位置。假如同属_init函数A()和函数B()就全部放在脚本中定义的_init地址上,但是具体是A()函数在前还是B()函数在前呢?这是由目录下的Makefile文件来决定了。Makefile文件中函数存放的先后顺序来决定了,假如obj+y   = A()在obj+y    = B()之前,则最后连接的时候A函数就在B()函数之前执行了。所以决定函数执行的先后顺序是由1.vmlinux.lds链接脚本2.驱动目录下Makefile文件共同确定的

首先分析链接脚本中关于顺序的控制。

vmlinux.lds:

  • #ifndef __ARMEB__
  • jiffies = jiffies_64;
  • #else
  • jiffies = jiffies_64 + 4;
  • #endif
  • SECTIONS
  • {
  •                 。。。 。。。
  •                 INIT_CALL
  •                 CON_INITCALL
  •         SECURITY_INITCALL
  •                 。。。 。。。
  • }

其中INIT_CALLS为一个宏,定义如下:
  • #define INITCALLS                                                       \
  •     *(.initcallearly.init)                      \
  •     VMLINUX_SYMBOL(__early_initcall_end) = .;           \
  •     *(.initcall0.init)                      \
  •     *(.initcall0s.init)                     \
  •     *(.initcall1.init)                      \
  •     *(.initcall1s.init)                     \
  •     *(.initcall2.init)                      \
  •     *(.initcall2s.init)                     \
  •     *(.initcall3.init)                      \
  •     *(.initcall3s.init)                     \
  •     *(.initcall4.init)                      \
  •     *(.initcall4s.init)                     \
  •     *(.initcall5.init)                      \
  •     *(.initcall5s.init)                     \
  •     *(.initcallrootfs.init)                     \
  •     *(.initcall6.init)                      \
  •     *(.initcall6s.init)                     \
  •     *(.initcall7.init)                      \
  •     *(.initcall7s.init)
  • #define INIT_CALLS                          \
  •         VMLINUX_SYMBOL(__initcall_start) = .;           \
  •         INITCALLS                       \
  •         VMLINUX_SYMBOL(__initcall_end) = .;

可以看见存放链接的顺序依次为:.initcall0.init、initcall0s.init、initcall1.init... ...

这只是连接脚本的中的语法,函数中具体是需要宏来给函数做上标记的。

具体在/include/linux/init.h中,相关代码如下:

  • #define __define_initcall(level,fn,id) \
  •     static initcall_t __initcall_##fn##id __used \
  •     __attribute__((__section__(".initcall" level ".init"))) = fn
  • /*
  • * Early initcalls run before initializing SMP.
  • *
  • * Only for built-in code, not modules.
  • */
  • #define early_initcall(fn)      __define_initcall("early",fn,early)
  • /*
  • * A "pure" initcall has no dependencies on anything else, and purely
  • * initializes variables that couldn't be statically initialized.
  • *
  • * This only exists for built-in code, not for modules.
  • */
  • #define pure_initcall(fn)       __define_initcall("0",fn,0)
  • #define core_initcall(fn)       __define_initcall("1",fn,1)
  • #define core_initcall_sync(fn)      __define_initcall("1s",fn,1s)
  • #define postcore_initcall(fn)       __define_initcall("2",fn,2)
  • #define postcore_initcall_sync(fn)  __define_initcall("2s",fn,2s)
  • #define arch_initcall(fn)       __define_initcall("3",fn,3)
  • #define arch_initcall_sync(fn)      __define_initcall("3s",fn,3s)
  • #define subsys_initcall(fn)     __define_initcall("4",fn,4)
  • #define subsys_initcall_sync(fn)    __define_initcall("4s",fn,4s)
  • #define fs_initcall(fn)         __define_initcall("5",fn,5)
  • #define fs_initcall_sync(fn)        __define_initcall("5s",fn,5s)
  • #define rootfs_initcall(fn)     __define_initcall("rootfs",fn,rootfs)
  • #define device_initcall(fn)     __define_initcall("6",fn,6)
  • #define device_initcall_sync(fn)    __define_initcall("6s",fn,6s)
  • #define late_initcall(fn)       __define_initcall("7",fn,7)
  • #define late_initcall_sync(fn)      __define_initcall("7s",fn,7s)
  • #define __initcall(fn) device_initcall(fn)
  • ... ...
  • #define module_init(x)    __initcall(x);

这里就可以发现,在实际的驱动中,初始化函数都有如下形式:module_init(init_func)

原来module_init就是一个宏,可以发现module_init宏最终展开后会将init_func函数做个

initcall6.init的标记,最终此函数就被链接在此initcall6.init的位置,而那些序号大initcall6.init的函数

则存放在被module_init修饰的函数之前的位置。

Makefile文件的控制

i2c子系统的相关源码集中在/driver/i2c目录下

所以此处只从/driver/i2c目录下的Makefile文件分析

/driver/i2c/Makefile:
  • obj-$(CONFIG_I2C_BOARDINFO)     += i2c-boardinfo.o
  • obj-$(CONFIG_I2C)               += i2c-core.o
  • obj-$(CONFIG_I2C_SMBUS)         += i2c-smbus.o
  • obj-$(CONFIG_I2C_CHARDEV)       += i2c-dev.o
  • obj-$(CONFIG_I2C_MUX)           += i2c-mux.o
  • obj-y                           += algos/ busses/ muxes/
  • ccflags-$(CONFIG_I2C_DEBUG_CORE) := -DDEBUG

可见链接的顺序是i2c.-boardinfo、i2c-core、i2c-dev... ...

1.2 i2c子系统的初始化函数的执行先后顺序

结合vmlinux.lds和Makefile,可确定i2c初始化函数的执行顺序如下:

1./dricer/i2c/i2c-core.c中的函数:i2c_init()                                                                                    postcore_initcall级别

2./arch/arm/mach-s3c2440/mach-smdk2440.c中的函数:smdk2440_machine_init()      arch_initcall级别

3.driver/i2c/buses/i2c-s3c2410.c中的函数:i2c_adap_s3c_init()                                            subsys_initcall级别                    

4./driver/i2c/i2c-dev.c中的函数:i2c_dev_init()                                                                              module_init级别

2 内核中I2C子系统的模型

为了便于说明和理解,现简单介绍下内核中I2C的模型。I2C为主从结构,所以分为主机和从机。

内核用adapter来表示主机,client来表示从机,并且还要同时符合内核的设备模型(bus,device,driver)。

所以内核中的i2c子系统应该包括adapter设备、adapter的驱动,client设备,client的驱动,总线等。

此外首先想系统注册的是platform_device型的设备和对应的驱动platform_driver(其中platform_device中包含

adapter的相关信息),然后配对成功调用probe函数,最后在probe函数中利用platform_device中包含adapter信息

创建起adapter,然后client等等。

1.adapter

此处的adapter就是指s3c2440的i2c控制器。内核中通过platform_device来描述。具体如下:

struct platform_device s3c_device_i2c0 = {
    .name          = "s3c2410-i2c",
#ifdef CONFIG_S3C_DEV_I2C1
    .id          = 0,
#else
    .id          = -1,
#endif
    .num_resources      = ARRAY_SIZE(s3c_i2c_resource),
    .resource      = s3c_i2c_resource,
};

其中name最后在s3c244x_map_io()函数中会被修改为"s3c2440-i2c“

static struct s3c2410_platform_i2c default_i2c_data0 __initdata = {
    .flags        = 0,
    .slave_addr    = 0x50,
    .frequency    = 100*1000,
    .sda_delay    = 100,
};

static struct resource s3c_i2c_resource[] = {
    [0] = {
        .start = S3C_PA_IIC,
        .end   = S3C_PA_IIC + SZ_4K - 1,
        .flags = IORESOURCE_MEM,
    },
    [1] = {
        .start = IRQ_IIC,
        .end   = IRQ_IIC,
        .flags = IORESOURCE_IRQ,
    },
};


2.client

这里的client就是at24c02了。初始化之后的名字是0-0050,第一位表示总线号,后四位表示从机地址。

具体如下:

static struct at24_platform_data at24c02 = {
    .byte_len    = SZ_2K / 8,
    .page_size    = 8,
};

static struct i2c_board_info tq2440_i2c_devs[] __initdata = {
    {
        I2C_BOARD_INFO("24c02", 0x50),
        .platform_data = &at24c02,
    },
};


3.bus

在i2c子系统里面提到的bus并不是指哪个具体的i2c总线,这个总线是虚拟的总线用于管理,和平常说的i2c通信用的总线有区别。

平常说的i2c总线指得是adapter和client之间相连的具体的可以用于通信的线路,而这里的所谓的总线是用于管理的,上面挂有着

系统中所有的adapter和client。

struct bus_type i2c_bus_type = {
    .name        = "i2c",
    .match        = i2c_device_match,
    .probe        = i2c_device_probe,
    .remove        = i2c_device_remove,
    .shutdown    = i2c_device_shutdown,
    .pm        = &i2c_device_pm_ops,
};

相关阅读:

I2C子系统之at24c02读写测试 http://www.linuxidc.com/Linux/2012-08/68256.htm
I2C子系统之ioctl()  http://www.linuxidc.com/Linux/2012-08/68257.htm
I2C子系统之at24c02简介 http://www.linuxidc.com/Linux/2012-08/68258.htm
I2C子系统之总结 http://www.linuxidc.com/Linux/2012-08/68259.htm
I2C子系统之内核中I2C子系统的结构 http://www.linuxidc.com/Linux/2012-08/68260.htm
I2C子系统之I2C bus初始化——I2C_init() http://www.linuxidc.com/Linux/2012-08/68261.htm
I2C子系统之platfor_device初始化——smdk2440_machine_init() http://www.linuxidc.com/Linux/2012-08/68262.htm
I2C子系统之platform_driver初始化——I2C_adap_s3c_init() http://www.linuxidc.com/Linux/2012-08/68263.htm
I2C子系统之I2C总线时钟频率设置 http://www.linuxidc.com/Linux/2012-08/68264.htm
I2C子系统之adapter device和client device注册——I2C_add_number_adapter() http://www.linuxidc.com/Linux/2012-08/68265.htm
I2C子系统之__I2C_first_dynamic_bus_num变量的相关分析 http://www.linuxidc.com/Linux/2012-08/68266.htm
I2C子系统之 adapter driver注册——I2C_dev_init() http://www.linuxidc.com/Linux/2012-08/68267.htm
I2C子系统之write() http://www.linuxidc.com/Linux/2012-08/68268.htm


回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|手机版|小黑屋|深圳市九鼎创展科技官方论坛 ( 粤ICP备11028681号-2  

GMT+8, 2024-3-29 22:39 , Processed in 0.022904 second(s), 17 queries .

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表