当前位置:文档之家› linux的I2C子系统详解

linux的I2C子系统详解

linux的I2C子系统详解
linux的I2C子系统详解

Linux中I2C驱动子系统详解

Author :Aston

Mail : astonqa@https://www.doczj.com/doc/e713297927.html,

写在前面

首先,本教程不来自于权威书籍或文献,完全为本人个人理解的描述。限于水平,其中若有错误及不当,敬请指正、不胜感激。

其次,本教程撰写过程中虽参考了网络上各种资料,但不同于网上大部分资料的

粗略介绍或只关注一个点。相反,本教程从全局出发,全面描述了I2C总线及设备在linux系统中的实现方法,适合于希望对linux中I2C子系统进行详细、彻底学习的人士共享与阅读。

第一部分:框架描述

第二部分:I2C总线与实际硬件介绍

第三部分:linux中I2C子系统与主要数据结构分析

一个I2C设备要在linux下使用,必须包含两个驱动:I2C总线驱动和I2C设备驱动。涉及到的主要数据结构有i2c_adapter, i2c_client, i2c_driver,

i2c_algorithm(均包含在include/linux/i2c.h中),这几个数据结构是I2C驱动中主

要的数据抽象,搞清楚这几个数据结构的意义是理解I2C驱动架构的前提。

i2c_adapter用来虚拟I2C适配器,所谓的适配器即用来匹配其两端连接的设备的器件。在这里,I2C适配器是一个虚拟的器件,该器件用来匹配I2C总线上连接的设备和I2C主控制器。因此可以认为:i2c_adapter对应的物理实体就是SoC内部的I2C控制器。

i2c_algorithm是一个纯软件层面的数据抽象,没有对应的物理实体。它用来描述一个i2c_adapter(实质是一个I2C控制器)的通信时序,这其实很好理解。大家都知道,I2C总线上的通信双方要完成通信需要按照一定的时序吧,如下图即是S3C2440中I2C控制器的主发送时序(即2440作为I2C主设备向外部连接的从设备发送信息)。

i2c_algorithm中封装的方法旨在描述该种时序,I2C总线驱动中最终需要靠这里封装

的方法来操作I2C控制器硬件发送通信时序。

核心数据结构分析详细分析如下:

1、i2c_adapter

1.1、说明:

I2c_adapter是linux I2C驱动模型的核心数据结构(i2c_adapter, i2c_client, i2c_driver, i2c_algorithm)之一,i2c_adapter用来隶属于I2C总线驱动部分,抽

象含义为I2C适配器,可以认为其对应于真实设备中SoC内部的I2C控制器(例如

S3C2440,S5PV210等SoC内部都集成了数个I2C控制器作为内部外设,用户可以通过

寄存器来控制这些内部外设,以和外部I2C设备进行通信)。

I2C总线驱动层调用此接口来添加一个adapter到系统中,因此该接口属于I2C总线驱动部分(不属于I2C设备驱动部分),再细分的话就是I2C总线驱动部分的I2C core部分(core来实现,具体的I2C总线驱动,如

drivers/i2c/buses/i2c_s3c2410.c来调用)

1.2、主要接口

1.2.1、注册接口

总线级驱动程序(如drivers/i2c/buses/i2c_s3c2410.c)可以通过调用以下两个接口来注册一个i2c_adapter:

int i2c_add_adapter(struct i2c_adapter *adapter)

添加一个i2c_adapter,使用动态分配的bus number。这种情况一般是针对动态

安装的I2C控制器(如USB转I2C控制器等),此时系统会从

__i2c_first_dynamic_bus_num起自动分配一个bus number给该i2c_adapter。

int i2c_add_numbered_adapter(struct i2c_adapter *adap)

添加一个i2c_adapter,使用静态分配注册的bus number。这种情况一般针对SoC 内部的I2C控制器,此时bus number是在系统初始化时静态分配的,移植时可以通过

i2c_board_info来声明,i2c_register_board_info()函数来注册。

以上两个接口除了在bus number确定方面有差异外,其余很类似。在

i2c_s3c2410.c中使用了i2c_add_numbered_adapter接口(因为该总线驱动要描述的

是S3C2440内部自带的I2C控制器),具体的调用关系如下:

i2c_add_numbered_adapter

i2c_register_adapter

i2c_do_add_adapter

i2c_detect

其中,i2c_detect函数用来负责检测该i2c_adapter可以适配的设备列表。可见,当I2C总线驱动中安装了某一i2c_adapter后,实际上i2c_core后执行该

i2c_adapter的注册,并且会将挂在同一I2C总线下的devices列表与该i2c_adapter

进行遍历适配。

1.2.2、注销接口

static int i2c_do_del_adapter(struct device_driver *d, void *data)

int i2c_del_adapter(struct i2c_adapter *adap)

i2c_core中也定义了注销一个i2c_adapter的方法,即i2c_del_adapter。其内部通过调用i2c_do_del_adapter来完成注销。注销工作是注册公司的逆过程,要注意的主要是一些细节清理工作。好在这部分代码已经在i2c_core中实现好了,根本不用我们去处理,所以省事不少。

2、关于i2c_algorithm

2.1、说明

I2c_algorithm用于封装一个具体的I2C控制器(在linux中用i2c_adapter数据结构来代表)的通信时序。因此,实现一个i2c_algorithm主要是为了实现一个

i2c_adapter而服务。数据原型定义见 include/linux/i2c.h文件中,如下所示:

struct i2c_algorithm {

/* If an adapter algorithm can't do I2C-level access, set

master_xfer

to NULL. If an adapter algorithm can do SMBus access, set

smbus_xfer. If set to NULL, the SMBus protocol is simulated

using common I2C messages */

/* master_xfer should return the number of messages successfully

processed, or a negative value on error */

int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs,

int num);

int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr,

unsigned short flags, char read_write,

u8 command, int size, union i2c_smbus_data *data);

/* To determine what the adapter supports */

u32 (*functionality) (struct i2c_adapter *);

};

可见,i2c_algorithm中,实际只包含三个函数指针。其中,master_xfer用来描述该I2C控制器主设备发送时序;smbus_xfer用来描述该I2C控制器SMBus协议下的发送时序(可以认为SMBus是基于I2C的一种传输协议,因此可以在I2C硬件控制器的基础上实现SMBus传输);functionality用来描述该I2C控制器都支持哪些功能,功能码由i2c.h中定义的一系列I2C_FUNC_开头的宏来表述。

从i2c_algorithm的功能可知,定义并实现一个i2c_algorithm数据结构是一个I2C总线驱动的任务。因此在我们的示例里,该数据结构定义在i2c_s3c2410.c中,如下:

static const struct i2c_algorithm s3c24xx_i2c_algorithm = {

.master_xfer = s3c24xx_i2c_xfer,

.functionality = s3c24xx_i2c_func,

};

其中,s3c24xx_i2c_xfer为重点。该函数及其所调用的一系列函数中包含了

S3C2440内部I2C控制器的硬件操作代码,这就是I2C总线驱动中真正在和硬件打交道的地方。因此,假如我们要为一款新的芯片来实现其I2C总线驱动,实际上主要的移植主要在该函数。

3、i2c_driver

3.1、说明

i2c_driver

4、i2c_client

第四部分:框架详细说明

首先,一个I2C设备(譬如I2C接口的EEPROM芯片AT24C08)要在linux系统中正常使用,必须有以下组件:

◆Linux I2C总线架构

◆S3C2440的内部I2C控制器的I2C总线驱动

◆AT24C08的设备驱动

1、I2C总线架构

linux I2C总线架构由linux本身提供。定义在i2c_core.c中,i2c_init函数之中,而i2c_init函数被postcore_initcall(i2c_init);因此可以知道,i2c_init会在系统初始化时被调用,从而在系统启动过程中完成I2C总线的安装。

2、I2C总线控制器

对于I2C总线控制器,则必须由SoC移植者提供。实际上linux中已经包含了这部分驱动代码,定义在drivers/i2c/buses/i2c_s3c2410.c文件中。

static struct platform_driver s3c24xx_i2c_driver = {

.probe = s3c24xx_i2c_probe,

.remove = s3c24xx_i2c_remove,

.id_table = s3c24xx_driver_ids,

.driver = {

.owner = THIS_MODULE,

.name = "s3c-i2c",

.pm = S3C24XX_DEV_PM_OPS,

},

};

static int __init i2c_adap_s3c_init(void)

{

return platform_driver_register(&s3c24xx_i2c_driver);

}

subsys_initcall(i2c_adap_s3c_init);

static void __exit i2c_adap_s3c_exit(void)

{

platform_driver_unregister(&s3c24xx_i2c_driver);

}

module_exit(i2c_adap_s3c_exit);

该总线级驱动属于platform平台驱动。这个很好理解,因为S3C2440内部I2C控制器属于SoC内部外设,因此可以使用平台设备模型来实现它的驱动。

既然是platform driver,那我们就按照平台驱动的规律去分析它就可以了。首先该驱动会要求与bsp文件中定义的一个platform device来匹配。与之匹配的平台设备为arch/arm/plat-s3c/dev-i2c0.c中定义的struct platform_device

s3c_device_i2c0。

这里有一个细节要说明,就是s3c_device_i2c0中的.name为"s3c2410-i2c",而s3c24xx_i2c_driver中的.name为"s3c-i2c",这里名称不对应又如何匹配呢?答案在于s3c24xx_i2c_driver中的.id_table子元素。只要s3c_device_i2c0的.name与

s3c24xx_i2c_driver中的.id_table中的某一个子元素的.name匹配上就可以配对使用了。这样实现的意义在于可以让一个驱动和多个设备匹配,而不是每个设备都需要一个彼此完全不同的驱动。这样处理符合现实中的情况,实际中经常有许多设备可以使用一个驱动(譬如24C02与24C08还有其他一些同系列的IC当然可以使用同一个驱动了)。

那如何验证这一点呢?其实并不复杂。大家可以查看platform平台总线的实现代码。在源码目录drivers/base/platform.c中,

struct bus_type platform_bus_type = {

.name = "platform",

.dev_attrs = platform_dev_attrs,

.match = platform_match,

.uevent = platform_uevent,

.pm = &platform_dev_pm_ops,

};

这其中的.match对应的函数便是platform总线用来匹配platform_driver与platform_device的方法。查看platform_match可知,其调用platform_match_id函数来做实际的适配。在platfrom_match_id中通过循环遍历platform_device_id数组来进行适配,这验证了上面我们所说。

说回正题,这里注册了platform_device和platform_driver之后,开机运行在/sys/bus/platform/devices/中查看,应该可以看到s3c2410-i2c。但是实际查看看到的是s3c2440-i2c,原因在于mach-mini2440.c中,mini2440_map_io()-

>s3c24xx_init_io()->s3c_init_cpu()->cpu_ids->s3c244x_map_io()-

>s3c_device_https://www.doczj.com/doc/e713297927.html, = "s3c2440-i2c"; 依次追踪即可看到系统初始化时将

s3c_device_i2c0结构的.name修改成了"s3c2440-i2c"。(这里可以看出,三星的体系结构移植实现的非常麻烦,很不直观。)

另一个值得仔细分析的函数是s3c24xx_i2c_probe,该函数在I2C总线驱动成功匹配I2C设备s3c_device_i2c0后被调用。该函数中首先实例化一个i2c_adapter数据结构,然后填充它,最后调用i2c_add_numbered_adapter注册该i2c_adapter。当然这其中还有一些resource的获取、注册等操作,请大家自行分析。

总结:I2C总线驱动涉及到的文件是i2c_s3c2410.c和i2c_core.c,本身以platform 驱动模型来实现(所以板文件中必须有相应的platform device,否则无法运行),主要任务是提供i2c_adapter。

3、I2C设备驱动

终于到了I2C设备驱动了,这部分其实才是跟我们写应用程序直接相关的部分。大家回忆下平时我们在应用程序中如何操作设备的?

一般来说有两条途径,一是/dev目录下的设备节点,一是/sys目录下的sysfs属性节点。前一种是较传统的做法,而sysfs的访问接口是比较新颖的方式。在我使用的内核版本(2.6.32.2)中,i2c-dev.c中使用了/dev设备节点及标准的字符设备模型,而AT24Cxx系列的EEPROM驱动就是使用的sysfs属性接口,后续我们再详细分析之。

设备模型方面的差异还不是最大区别,最大区别在于i2c-dev.c中只提供了主机侧(即S3C2440内部的I2C控制器)的I2C设备驱动,也就是说我们去操作/dev/i2c-0这个设备,它只是给我们提供了可以操作I2C控制器的接口,而我们这个I2C接口上连接的从设备是什么,如何操作,需要哪些时序(如何读,如何写等)并不包含在驱动中。因此,借用/dev/i2c-0这个设备节点,我们需要在用户层自己的应用程序中去具体处理AT24C08的读写操作(这种做法又被称为是应用层的驱动程序)。

而drivers/misc/eeprom/at24.c中提供的EEPROM驱动,则是针对从设备AT24C02而写的设备驱动,使用该驱动的应用层接口/sys/bus/i2c/devices/0-0050/eeprom,

我们可以直接去open,lseek,write,read该EEPROM芯片,而不用去研究EEPROM芯

片的读写方法,驱动会帮我们去处理这些细节。

做I2C设备驱动,一定要先分清楚这两种设备驱动的不同,并且搞清楚你的驱动

属于哪一类的。下面我们分别讲述两种设备驱动的实现细节。

3.1、只包含I2C主机侧的设备驱动

/dev下设备节点访问的方式,应用层的访问实际对应于驱动层注册的一个字符设备。对于I2C来说,我们要控制硬件(S3C2440内部的I2C控制器)发出信号从而与外部相连的设备(AT24C08)通信,就必须提供一个应用层的访问接口(譬如/dev/i2c-0),然后我们就可以在应用层去open、write、read、ioctl等来操作设备了。

没错,这个/dev/i2c-0及其所对应的那个file_operations(字符设备驱动的实

质就是这个结构体中的方法),就是我们所谓的I2C设备驱动了。驱动文件为

drivers/i2c/i2c-dev.c,打开该文件,从下往上看:

module_init(i2c_dev_init);

module_exit(i2c_dev_exit);

static int __init i2c_dev_init(void)

{

······

res = register_chrdev(I2C_MAJOR, "i2c", &i2cdev_fops);

······

i2c_dev_class = class_create(THIS_MODULE, "i2c-dev");

······

res = i2c_add_driver(&i2cdev_driver);

······

}

static void __exit i2c_dev_exit(void)

{

i2c_del_driver(&i2cdev_driver);

class_destroy(i2c_dev_class);

unregister_chrdev(I2C_MAJOR,"i2c");

}

很明显,直接注册了主设备号为I2C_MAJOR(89)的字符设备i2c,并且绑定了一

个file_operations i2cdev_fops。依此追查,便可找到I2C设备驱动的各种执行函数及其具体实现,暂且按下不表。

并且创建了一个名为i2c-dev的类,后续可以调用device_create接口在该类下

注册设备,这样可以借用udev(嵌入式中的mdev)的功能在/dev下自动创建设备节点。(实际上在i2cdev_attach_adapter函数中就做了这些事情,这也是我们在/dev下看到的设备节点i2c-0的来历)

最后,调用了i2c_add_driver来向i2c总线层(确切的说是I2C总线层的

i2c_core部分)注册该i2c驱动

下面来看看i2cdev_fops,该结构内挂接的各种操作函数(i2cdev_open、

i2cdev_ioctl等)对应着我们在应用层对/dev/i2c-0设备进行操作的应用层open、ioctl等。

I2cdev_open函数对应着用户在应用层调用open(“/dev/i2c-0”, O_RDWR),其

主要的操作目标就是构建一个i2c_client的示例,并且填充之,最后将已经充分初始

化过的i2c_client存放在file->private_data,这样在后面的i2cdev_ioctl、

i2cdev_read等函数中就可以通过file->private_data得到这个i2c_client结构了。

I2c_client结构是I2C从设备的抽象,一个i2c_client代表一个I2C从设备(如我们这里的AT24C08,硬件上与S3C2440的I2C总线相连,在2440的I2C设备驱动中就使

用一个i2c_client来代表这个AT24C08芯片)。

I2cdev_ioctl函数中定义了一些命令,用户可以在应用层中使用ioctl系统调用

使用这些命令。譬如I2C_SLAVE、I2C_SLAVE_FORCE这两个命令用来在应用层设置要操

作的I2C从设备的地址(最好使用I2C_SLAVE_FORCE,否则有可能得到-EBUSY);

I2C_TENBIT命令可以将系统设置为10bit I2C地址模式(默认的是7bit地址模式)。

3.2、直接面向I2C从机侧的设备驱动

drivers/misc/eeprom/at24.c是一个与S3C2440 I2C总线连接的一个从机设备芯

片的驱动,该驱动仍然使用linux中I2C子系统的总线驱动层功能(注册驱动仍然使

用i2c_add_driver接口)

static struct i2c_driver at24_driver = {

.driver = {

.name = "at24",

.owner = THIS_MODULE,

},

.probe = at24_probe,

.remove = __devexit_p(at24_remove),

.id_table = at24_ids,

};

重点在于at24_probe,我们在板文件mach-mini2440.c中添加以下内容

static struct i2c_board_info i2c_devs0[] __initdata = {

{ I2C_BOARD_INFO("24c08", 0x50), },

};

static void __init mini2440_machine_init(void)

{

······

i2c_register_board_info(0, i2c_devs0, ARRAY_SIZE(i2c_devs0));

······

}

当系统启动时,总线检测到该i2c_board_info的信息,以i2c_board_info.type

去匹配at24.c中的at24_ids,当发现一个匹配项后则会调用at24_driver中的probe

函数,即at24_probe函数。

该函数内部创建了一个二进制属性eeprom,并将其添加到sysfs节点中。该

sysfs节点即是驱动开放给应用层的调用接口。

at24->https://www.doczj.com/doc/e713297927.html, = "eeprom";

at24->bin.attr.mode = chip.flags & AT24_FLAG_IRUGO ? S_IRUGO : S_IRUSR;

at24->bin.read = at24_bin_read;

at24->bin.size = chip.byte_len;

at24->macc.read = at24_macc_read;

······

err = sysfs_create_bin_file(&client->dev.kobj, &at24->bin);

为了在应用程序中使用EEPROM,我们可以在应用层打开/sys/···/eeprom节点,然后对其进行lseek定位,write、read来读写,即可完成对EEPROM的读写操作,示

例代码如下:

#include

#include

#include

#include

#include

#define DEVNAME "/sys/bus/i2c/devices/0-0050/eeprom"

#define READLEN 256

static unsigned char buf[READLEN];

int main(void)

{

int fd, ret;

fd = open(DEVNAME, O_RDWR);

if (fd < 0)

{

fprintf(stderr, "fail to open device: %s\n", DEVNAME);

exit(1);

}

lseek(fd, 50, SEEK_SET);

ret = read(fd, buf, READLEN);

printf("ret = %d\n", ret);

int i;

for (i=0; i<50; i++)

{

printf("buf[i] = %d\n", buf[i]);

}

return 0;

}

Linux下I2C驱动介绍

1、I2C概述 I2C是philips公司提供的外设总线,I2C有两条数据线,一条是串行数据线SDA、一条是时钟线SCL,使用SDA和SCL实现了数据的交换,便于布线。I2C总线方便用在EEPROM、实时钟、小型LCD等与CPU外部的接口上。 2、Linux下的驱动思路 Linux系统下编写I2c驱动主要有两种方法:一种是把I2C当做普通字符设备来使用;另一种利用Linux下驱动的体系结构来实现。 第一种方法: 优点:思路比较直接,不用花费大量时间去了解Linux系统下I2C体系结构 缺点:不仅对I2C设备操作要了解,还有了解I2C的适配器操作 不仅对I2C设备器和设备操作需要了解,编写的驱动移植性差,内核 提供的I2C设备器都没有用上。 第二种方法: 第一种的优点就是第二种的缺点,第一种的缺点就是第二种的优点。 3、I2C框架概述 Linux的I2C体系结构分为3部分: 1)I2C核心I2C核心提供了I2C总线驱动和设备驱动的注册和注销的方法,I2C 通信方法(algorithm)上层,与具体适配器无关的代码,检测设备上层的代 码等。 2)I2C总线驱动I2C总线驱动是对I2C硬件体系结构中适配器端的实现,适配器可以直接受CPU来控制。 3)I2C设备驱动I2C设备驱动是对I2C硬件体系结构中设备端的实现,设备端挂在受CPU控制的适配器上,通过I2C适配器与CPU交换数据。 Linux下的I2C体系结构: 1)Linux下的I2C体系结构 4、I2C设备驱动编写方法 首先让我们明白适配器驱动的作用是让我们能够通过它发出标准的I2C时序,在linux

内核源代码中driver/I2C/buss包含一些适配器的驱动,例如s3c2410的驱动I2C-s3c2410.c,适配器被加载到内核中,接下的任务就是实现设备驱动的编写。编写设备驱动的方法主要分为两种方法: 第一种:利用设备提供的I2C-dev.c来实现I2C适配器设备文件,然后通过上层应用程序来操作I2C设备器来控制I2C设备。 第二种:为I2C设备独立编写一个设备驱动 注意:第二种方法不能用设备提供的I2C-dev.c 5、I2C系统下的文件架构 在linux下driver下面有个I2C目录,在I2C目录下包含以下文件和文件夹 1)I2C-core.c 这个文件实现I2C核心功能以及/proc/bus/I2C*接口 2)I2C-dev.c 实现I2C适配器设备文件的功能,每个I2C适配器被分配一个设备,通过 适配器访问设备的时候,主设备号是89,此设备号是0-255. I2C-dev.c并没有针对特定设备而设计,只提供了read() write()和ioctl()等接口,应用层可以通过这些接口访问挂在适配器上的I2C设备存储空间和寄存器,并控制I2C设备的工作方式。 3)Chips 这个文件下面包含特定的I2C设备驱动。 4)Busses 这个文件包含一些I2C总线驱动。 5)Algos文件夹下实现了I2C总线适配器的algorithm 6、重要结构体 1)在内核中的I2C.h这个头文件中对I2C_driver;I2C_client;I2C_adapter和I2C_algorithm 这个四个结构体进行了定义。理解这4个结构体的作用十分关键。 i2c_adapter结构体 struct i2c_adapter { struct module *owner; //所属模块 unsigned int id; //algorithm的类型,定义于i2c-id.h, unsigned int class; const struct i2c_algorithm *algo; //总线通信方法结构体指针 void *algo_data;//algorithm数据 struct rt_mutex bus_lock; //控制并发访问的自旋锁 int timeout; int retries; //重试次数 struct device dev; //适配器设备 int nr; char name[48]; //适配器名称 struct completion dev_released; //用于同步 struct list_head userspace_clients; //client链表头

Linux下I2C驱动架构全面分析概要

Linux下I2C驱动架构全面分析 I2C概述 I2C是philips提岀的外设总线. I2C只有两条线,一条串行数据线:SDA, —条是时钟线SCL,使用SCL , SDA这两根信号线就实现了设备之间的数据交互,它方便了工程师的布线。 因此,I2C总线被非常广泛地应用在EEPROM,实时钟,小型LCD等设备与CPU的接口中。 linux下的驱动思路 在linux系统下编写I2C驱动,目前主要有两种方法,一种是把I2C设备当作一个普通的字符设备来处理,另一种是利用linux下I2C驱动体系结构来完成。下面比较下这两种方法: 第一种方法: 优点:思路比较直接,不需要花很多时间去了解linux中复杂的I2C子系统的操作方法。 缺点: 要求工程师不仅要对I2C设备的操作熟悉,而且要熟悉I2C的适配器(I2C控制器)操作。要求工程师对I2C的设备器及I2C的设备操作方法都比较熟悉,最重要的是写岀的程序可以移植性差。 对内核的资源无法直接使用,因为内核提供的所有I2C设备器以及设备驱动都是基于I2C 子系统的格式。 第一种方法的优点就是第二种方法的缺点, 第一种方法的缺点就是第二种方法的优点。 I2C架构概述 Linux的I2C体系结构分为3个组成部分: I2C核心:I2C核心提供了I2C总线驱动和设备驱动的注册,注销方法,I2C通信方法 (” algorithm 上层的,与具体适配器无关的代码以及探测设备,检测设备地址的上层代码等。 I2C总线驱动:I2C总线驱动是对I2C硬件体系结构中适配器端的实现,适配器可由CPU控制,甚至可以直接集成在CPU内部。 I2C设备驱动:I2C设备驱动(也称为客户驱动)是对I2C硬件体系结构中设备端的实现,设备一般挂接在受CPU控制的I2C适配器上,通过I2C适配器与CPU交换数据。

linux下iic(i2c)读写AT24C02

https://www.doczj.com/doc/e713297927.html,/jammy_lee/ https://www.doczj.com/doc/e713297927.html, linux下iic(i2c)读写AT24C02 linux驱动2010-02-09 16:02:03 阅读955 评论3 字号:大中小订阅 linux内核上已有iic的驱动,因此只需要对该iic设备文件进行读写则能够控制外围的iic器件。这里以AT24C02为对象,编写一个简单的读写应用程序。iic设备文件在我的开发板上/dev/i2c/0 ,打开文件为可读写。AT24C02的器件地址为0x50 ,既是iic总线上从器件的地址,每次只读写一字节数据。 /************************************************************/ //文件名:app_at24c02.c //功能:测试linux下iic读写at24c02程序 //使用说明: (1) // (2) // (3) // (4) //作者:jammy-lee //日期:2010-02-08 /************************************************************/ //包含头文件 #include #include #include #include #include #include #include

#include #include #include //宏定义 #define Address 0x50 //at24c02地址 #define I2C_RETRIES 0x0701 #define I2C_TIMEOUT 0x0702 #define I2C_SLAVE 0x0703 //IIC从器件的地址设置 #define I2C_BUS_MODE 0x0780 typedef unsigned char uint8; uint8 rbuf[8] = {0x00}; //读出缓存 uint8 wbuf[8] = {0x01,0x05,0x06,0x04,0x01,0x01,0x03,0x0d}; //写入缓存int fd = -1; //函数声明 static uint8 AT24C02_Init(void); static uint8 i2c_write(int fd, uint8 reg, uint8 val); static uint8 i2c_read(int fd, uint8 reg, uint8 *val); static uint8 printarray(uint8 Array[], uint8 Num); //at24c02初始化 static uint8 AT24C02_Init(void) { fd = open("/dev/i2c/0", O_RDWR); //允许读写 if(fd < 0) { perror("Can't open /dev/nrf24l01 \n"); //打开iic设备文件失败 exit(1);

实例解析linux内核I2C体系结构(2)

实例解析linux内核I2C体系结构(2) 华清远见刘洪涛四、在内核里写i2c设备驱动的两种方式 前文介绍了利用/dev/i2c-0在应用层完成对i2c设备的操作,但很多时候我们还是习惯为i2c设备在内核层编写驱动程序。目前内核支持两种编写i2c驱动程序的方式。下面分别介绍这两种方式的实现。这里分别称这两种方式为“Adapter方式(LEGACY)”和“Probe方式(new style)”。 (1)Adapter方式(LEGACY) (下面的实例代码是在2.6.27内核的pca953x.c基础上修改的,原始代码采用的是本文将要讨论的第2种方式,即Probe方式) ●构建i2c_driver static struct i2c_driver pca953x_driver = { .driver = { .name= "pca953x", //名称 }, .id= ID_PCA9555,//id号 .attach_adapter= pca953x_attach_adapter, //调用适配器连接设备 .detach_client= pca953x_detach_client,//让设备脱离适配器 }; ●注册i2c_driver static int __init pca953x_init(void) { return i2c_add_driver(&pca953x_driver); } module_init(pca953x_init); ●attach_adapter动作 执行i2c_add_driver(&pca953x_driver)后会,如果内核中已经注册了i2c适配器,则顺序调用这些适配器来连接我们的i2c设备。此过程是通过调用i2c_driver中的attach_adapter方法完成的。具体实现形式如下: static int pca953x_attach_adapter(struct i2c_adapter *adapter) { return i2c_probe(adapter, &addr_data, pca953x_detect); /* adapter:适配器 addr_data:地址信息 pca953x_detect:探测到设备后调用的函数 */ } 地址信息addr_data是由下面代码指定的。 /* Addresses to scan */ static unsigned short normal_i2c[] = {0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,I2C_CLIENT_END}; I2C_CLIENT_INSMOD;

Linux_I2C总线分析(主要是probe的方式)1

Linux I2C 总线浅析 ㈠ Overview 内核空间层次! i2c adapter 是一个struct, 用来抽象一个物理i2c bus ,而且还和linux 设备驱动架构柔和在一起.. 如果只说硬件的话,就是在CPU内部集成的一个I2C控制器(提供给用户的就是那几个register),硬件上并没的所谓的adapter,client这些东东,,adapter和client都是linux 驱动软件抽象出来的东西 资料帖子: i2c_algorithm { /* If an adapter algorithm can't do I2C-level access, set master_xfer to NULL. If an adapter algorithm can do SMBus access, set smbus_xfer. If set to NULL, the SMBus protocol is simulated

using common I2C messages */ /* master_xfer should return the number of messages successfully processed, or a negative value on error */ i nt (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs, int num); i nt (*smbus_xfer) (struct i2c_adapter *adap, u16 addr, unsigned short flags, char read_write, u8 command, int size, union i2c_smbus_data *data); /* To determine what the adapter supports */ u32 (*functionality) (struct i2c_adapter *); }; /* * i2c_adapter is the structure used to identify a physical i2c bus along * with the access algorithms necessary to access it. */ struct i2c_adapter { s truct module *owner; u nsigned int id; u nsigned int class; /* classes to allow probing for */ c onst struct i2c_algorithm *algo; /* the algorithm to access the bus */ v oid *algo_data; /* data fields that are valid for all devices */ u8 level; /* nesting level for lockdep */ s truct mutex bus_lock; i nt timeout; /* in jiffies */ i nt retries; s truct device dev; /* the adapter device */ i nt nr; c har name[48]; s truct completion dev_released; }; Linux的I2C体系结构分为3个组成部分: 1·I2C核心: I2C核心提供了I2C总线驱动和设备驱动的注册、注销方法,I2C通信方法(即“algorithm”)上层的、与具体适配器无关的代码以及探测设备、检测设备地址的上层代码等。这部分是与平台无关的。 2·I2C总线驱动: I2C总线驱动是对I2C硬件体系结构中适配器端的实现。I2C总线驱动主要包含了I2C适配

实例解析linux内核I2C体系结构

实例解析linux内核I2C体系结构 作者:刘洪涛,华清远见嵌入式学院讲师。 一、概述 谈到在linux系统下编写I2C驱动,目前主要有两种方式,一种是把I2C 设备当作一个普通的字符设备来处理,另一种是利用linux I2C驱动体系结构来完成。下面比较下这两种驱动。 第一种方法的好处(对应第二种方法的劣势)有: ●思路比较直接,不需要花时间去了解linux内核中复杂的I2C子系统的操作方法。 第一种方法问题(对应第二种方法的好处)有: ●要求工程师不仅要对I2C设备的操作熟悉,而且要熟悉I2C的适配器操作; ●要求工程师对I2C的设备器及I2C的设备操作方法都比较熟悉,最重要的是写出的程序可移植性差; ●对内核的资源无法直接使用。因为内核提供的所有I2C设备器及设备驱动都是基于I2C子系统的格式。I2C适配器的操作简单还好,如果遇到复杂的I2C适配器(如:基于PCI的I2C适配器),工作量就会大很多。 本文针对的对象是熟悉I2C协议,并且想使用linux内核子系统的开发人员。 网络和一些书籍上有介绍I2C子系统的源码结构。但发现很多开发人员看了这些文章后,还是不清楚自己究竟该做些什么。究其原因还是没弄清楚I2C子系统为我们做了些什么,以及我们怎样利用I2C子系统。本文首先要解决是如何利用现有内核支持的I2C适配器,完成对I2C设备的操作,然后再过度到适配器代码的编写。本文主要从解决问题的角度去写,不会涉及特别详细的代码跟踪。 二、I2C设备驱动程序编写 首先要明确适配器驱动的作用是让我们能够通过它发出符合I2C标准协议的时序。 在Linux内核源代码中的drivers/i2c/busses目录下包含着一些适配器的驱动。如S3C2410的驱动i2c-s3c2410.c。当适配器加载到内核后,接下来的工作就要针对具体的设备编写设备驱动了。

linux i2c驱动

linux i2c驱动 1. i2c-dev interface I2C dev-interface 通常,i2c设备由某个内核驱动控制。但是在用户空间,也可以访问某个I2C设备:你需要 加载i2c-dev模块。 每个被注册的i2c适配器(控制器)会获得一个数字号,从0开始。你可以检查/sys/class/i2c-dev,来查看适配器对应哪个数字号。你也可以通过命令 "i2cdetect -l"获 取你的当前系统的所有I2c适配器的列表。i2cdetct是i2c-tool包中的一个工具。 i2c设备文件是字符设备,主设备号是89,次设备号的分配如上所述。设备文件名通常被 规定为"i2c-%d"(i2c-0, i2c-1, ...,i2c-10, ...)i2c设备文件是字符设备, 主设备号是 89,次设备号的分配如上所述。设备文件名通常被规定为"i2c-%d"(i2c-0, i2c-1, ...,i2c-10, ...).所有256个次设备号都保留给i2c使用。 C example ========= 假定你要在你的C应用程序中访问i2c适配器。第一件事情就是包含头文件 "#include "。注意,存在两个"i2c-dev.h"文件: 一个属于Linux kernel,用于 内核驱动中;一个由i2c-tools发布,用于用户程序。显然,这里需要使用第二个 i2c-dev.h文件。 现在,你需要确定访问哪个适配器。你需要通过查看/sys/class/i2c-dev/或者运行 "i2cdetect -l"确定。适配器号时常是动态分配的,你无法预先假定某个值。因为它们甚 至会在系统重启后变为不同的值。 下一步,打开设备文件,如下: int file; int adapter_nr = 2; /*probably dynamically determined */ char filename[20];

Linux驱动之i2c用户态调用

一、概述 I2C只有两条线,一条串行数据线:SDA,一条是时钟线SCL.正因为这样,它方便了工程人员的布线. 二、用户态实现设备驱动 在Linux内核代码文件i2c-dev.c中实现了I2C适配器设备文件的功能,针对每个适配器生成一个主设备号为89的设备节点(次设备号为0-255),I2c-dev.c并没有针对特定的设备而设计,只是提供了通用的read(),write(),和ioctl()等文件操作接口,在用户空间的应用层就可以借用这些接口访问挂接在适配器上的I2C设备的存储空间或寄存器,并控制I2C设备的工作方式。 i2c适配器的设备节点是/dev/i2c-x,其中x是数字。由于适配器编号是动态分配的(和注册次序有关),所以想了解哪一个适配器对应什么编号,可以查看/sys/class/i2c-dev/目录下的文件内容。 三、用户态调用 3.1、i2c-dev 用户空间操作i2c,需要包含以下头文件。 打开适配器对应的设备节点

i2c-dev为打开的线程建立一个i2c_client,但是这个i2c_client并不加到i2c_adapter的client链表当中。他是一个虚拟的临时client,当用户打开设备节点时,它自动产生,当用户关闭设备节点时,它自动被释放。 3.2、ioctl() 查看include/linux/i2c-dev.h文件,可以看到i2c支持的IOCTL命令1.#define I2C_RETRIES0x0701 /*设置收不到ACK时的重试次数*/ 2.#define I2C_TIMEOUT0x0702/*设置超 时时限的jiffies*/ 3.#define I2C_SLAVE0x0703/ *设置从机地址*/ 4.#define I2C_SLAVE_FORCE0x0706/*强制设置从机地 址*/ 5.#define I2C_TENBIT0x0704/* 选择地址位长:=0for7bit,!=0for10bit*/ 6.#define I2C_FUNCS0x0705/* 获取适配器支持的功能*/ 7.#define I2C_RDWR0x0707 /*Combin ed R/W transfer(one STOP only)*/ 8.#define I2C_PEC0 x0708/* !=0to use PEC with SMBus*/ 9.#define I2C_SMBUS0x0720 /*SMBus transfer*/

Android I2C精析

Android I2C精析 基于linux内核开发的arm系统,会用到很多components。要让这些components正常的工作,我们必须了解它们的接口,懂得如何去注册总线,初始化芯片,进而让芯片正常的工作。下面我会介绍在arm开发过程中使用最频繁的一些接口和总线的原理,以及如何在开发的过程中去使用它们。 1 I2C总线与接口 I2C总线具有结构简单,使用方便的特点。下面我会描述linux下I2C驱动的结构,幷给出I2C设备驱动和应用的实现。 1.1 I2C总线概述 I2C(Inter-Integrated Circuit)总线是由PHILIPS公司开发的两线式串行总线,用于连接微控制器及外围设备。是微电子通信控制领域广泛采用的一种总线标准。它是同步通信的一种特殊形式,具有接口线少,控制方式简单,器件封装形式小,通信速率较高等优点。 I2C总线是由数据线SDA和时钟SCL构成的串行总线,可发送和接收数据,每个器件都有一个惟一的地址识别。I2C 规程运用主/从双向通讯。器件发送数据到总线上,则定义为发送器,器件接收数据则定义为接收器。主器件和从器件都可以工作于接收和发送状态。总线必须由主器件(通常为微控制器)控制,主器件产生串行时钟(SCL)控制总线的传输方向,并产生起始和停止条件。SDA 线上的数据状态仅在SCL为低电平的期间才能改变,SCL为高电平的期间,SDA 状态的改变被用来表示起始和停止条件。 从理论上说一根I2C总线上可以挂载128个I2C设备,但是通常情况下,由于有些设备在传输数据时占用的I2C总线带宽频繁,所以我们在一根I2C总线上挂载的设备是越少越好。下面给出I2C总线的连线图:

Linux I2C设备驱动编写

Linux I2C设备驱动编写(一) 在Linux驱动中I2C系统中主要包含以下几个成员: 如果一个I2C适配器不支持I2C通道,那么就将master_xfer成员设为NULL。如果适配器支持SMBUS 协议,那么需要去实现smbus_xfer,如果smbus_xfer指针被设为NULL,那么当使用SMBUS协议的时候将会通过I2C通道进行仿真。master_xfer指向的函数的返回值应该是已经成功处理的消息数,或者返回负数表示出错了。functionality指针很简单,告诉询问着这个I2C主控器都支持什么功能。 在内核的drivers/i2c/i2c-stub.c中实现了一个i2c adapter的例子,其中实现的是更为复杂的SMBUS。 SMBus 与I2C的区别 通常情况下,I2C和SMBus是兼容的,但是还是有些微妙的区别的。

时钟速度对比: 在电气特性上他们也有所不同,SMBus要求的电压范围更低。 I2C driver 具体的I2C设备驱动,如相机、传感器、触摸屏、背光控制器常见硬件设备大多都有或都是通过I2C 协议与主机进行数据传输、控制。结构体如下:

如同普通设备的驱动能够驱动多个设备一样,一个I2C driver也可以对应多个I2C client。以重力传感器AXLL34X为例,其实现的I2C驱动为:

这里要说明一下module_i2c_driver宏定义(i2c.h): module_driver(): 理解上述宏定义后,将module_i2c_driver(adxl34x_driver)展开就可以得到:

这一句宏就解决了模块module安装卸载的复杂代码。这样驱动开发者在实现I2C驱动时只要将i2c_driver结构体填充进来就可以了,无需关心设备的注册与反注册过程。 I2C client 即I2C设备。I2C设备的注册一般在板级代码中,在解析实例前还是先熟悉几个定义: 下面还是以adxl34x为例:

Linux_I2C设备驱动文件操作接口

I2C适配器驱动被作为一个单独的模块被加载进内核,在模块的加载和卸载函数中,只需注册和注销一个platform_driver结构体,如代码清单15.25。 代码清单15.25 S3C2410 I2C总线驱动模块加载与卸载 1 static int __init i2c_adap_s3c_init(void) 2 { 3 int ret; 4 5 ret = platform_driver_register(&s3c2410_i2c_driver); 6 if (ret == 0) { 7 ret = platform_driver_register(&s3c2440_i2c_driver); 8 if (ret) 9 platform_driver_unregister(&s3c2410_i2c_driver); 10 } 11 12 return ret; 13 } 14 15 static void __exit i2c_adap_s3c_exit(void) 16 { 17 platform_driver_unregister(&s3c2410_i2c_driver); 18 platform_driver_unregister(&s3c2440_i2c_driver);

—— 19 } 20 module_init(i2c_adap_s3c_init); 21 module_exit(i2c_adap_s3c_exit); platform_driver结构体包含了具体适配器的probe()函数、remove()函数、resume()函数指针等信息,它需要被定义和赋值,如代码清单15.26。 代码清单15.26 platform_driver结构体 1 static struct platform_driver s3c2410_i2c_driver = { 2 .probe = s3c24xx_i2c_probe, 3 .remove = s3c24xx_i2c_remove, 4 .resume = s3c24xx_i2c_resume, 5 .driver = { 6 .owner = THIS_MODULE, 7 .name = "s3c2410-i2c", 8 }, 9 }; 当通过Linux内核源代码/drivers/base/platform.c文件中定义platform_driver_unregister()函数注册platform_driver结构体时,其中probe指针指向的s3c24xx_i2c_probe()函数将被调用以初始化适配器硬件,如代码清单15.27。 代码清单15.27 S3C2410 I2C总线驱动中的s3c24xx_i2c_probe函数 1 static int s3c24xx_i2c_probe(struct platform_device *pdev)

学会嵌入式Linux下I2C的接口调试

学会嵌入式Linux下I2C的接口调试 1). 简介 I2C是嵌入式设备最为常用的接口之一,常用于如下面这些应用场景,因此本文就基于嵌入式Linux演示在User Space进行I2C设备调试。 - Digital to Analog converter - EEPROM - Real Time Clock - Touch screen LCD - Audio codec 本文所演示的平台来自于Toradex Apalis iMX6Q ARM嵌入式平台,这是一个基于NXP iMX6Q ARM处理器,支持四核心Cortex-A9。 2. 准备 a).Apalis iMX6Q ARM核心版配合Apalis Ixora载板,连接调试串口UART1到开发主机方便调试,同时配置好Ubuntu开发主机开发环境,具体操作方法可以参考这里。 b).Apalis iMX6Q系统使用Toradex Linux Release V2.6.1,下载和更新方法请参考这里。 3). I2C总线user space操作命令测试 a). Apalis iMX6Q核心版默认定义提供了三个I2C总线可供外部使用(i2c-2为核心板内部电源管理使用),如下所示,其中i2c-1为通用I2C接口;i2c-0为DDC接口,用于连接HDMI DDC/EDID接口,不能用做通用I2C接口;而i2c-3通常用于连接camera接口使用,不过也可以用做通用I2C接口。 b). 本文演示示例则通过读写Apalis Ixora载板连接在i2c-1总线上面的2Kb EEPROM c). User Space下通过I2C tools直接操作i2c-1总线进行访问EEPROM ./ 查看Apalis iMX6Q的所有I2C总线 --------------------------------- root@apalis-imx6:~# ls -l /dev/i2c-*

基于Linux的I2C驱动组成结构

基于Linux的I2C驱动组成结构 Linux的I2C驱动架构 1. Linux的I2C驱动架构Linux中I2C总线的驱动分为两个部分,总线驱动(BUS)和设备驱动(DEVICE)。其中总线驱动的职责,是为系统中每个I2C总线增加相应的读写方法。但是总线驱动本身并不会进行任何的通讯,它只是存在在那里,等待设备驱动调用其函数。 设备驱动则是与挂在I2C总线上的具体的设备通讯的驱动。通过I2C总线驱动提供的函数,设备驱动可以忽略不同总线控制器的差异,不考虑其实现细节地与硬件设备通讯。 1.1 总线驱动在系统开机时,首先装载的是I2C总线驱动。一个总线驱动用于支持一条特定的I2C总线的读写。一个总线驱动通常需要两个模块,一个struct i2c_adapter和一个struct i2c_algorithm来描述: static struct i2c_adapter pb1550_board_adapter = { name: "pb1550 adapter", id: I2C_HW_AU1550_PSC, algo: NULL, algo_data: &pb1550_i2c_info, inc_use: pb1550_inc_use, dec_use: pb1550_dec_use, client_register: pb1550_reg, client_unregister: pb1550_unreg, client_count: 0, }; 这个样例挂接了一个叫做“pb1550 adapter”的驱动。但这个模块并未提供读写函数,具体的读写方法由第二个模块,struct i2c_algorithm提供。 static struct i2c_algorithm au1550_algo = { .name = "Au1550 algorithm", .id = I2C_ALGO_AU1550,

LinuxI2C总线详解

Linux I2C 总线详解 ㈠ Overview Linux的I2C体系结构分为3个组成部分: ·I2C核心: I2C核心提供了I2C总线驱动和设备驱动的注册、注销方法,I2C通信方法(即“algorithm”)上层的、与具体适配器无关的代码以及探测设备、检测设备地址的上层代码等。这部分是与平台无关的。 ·I2C总线驱动: I2C总线驱动是对I2C硬件体系结构中适配器端的实现。I2C总线驱动主要包含了I2C适配器数据结构i2c_adapter、I2C适配器的algorithm数据结构i2c_algorithm和控制I2C适配器产生通信信号的函数。经由I2C总线驱动的代码,我们可以控制I2C适配器以主控方式产生开始位、停止位、读写周期,以及以从设备方式被读写、产生ACK等。不同的CPU平台对应着不同的I2C 总线驱动。 总线驱动的职责,是为系统中每个I2C总线增加相应的读写方法。但是总线驱动本身并不会进行任何的通讯,它只是存在在那里,等待设备驱动调用其函数。 这部分在MTK 6516中是由MTK已经帮我们实现了的,不需要我们更改。 · I2C设备驱动: I2C设备驱动是对I2C硬件体系结构中设备端的实现。设备一般挂接在受CPU控制的I2C适配器上,通过I2C适配器与CPU交换数据。I2C设备驱动主要包含了数据结构i2c_driver和 i2c_client,我们需要根据具体设备实现其中的成员函数。在Linux内核源代码中的drivers 目录下的i2c_dev.c文件,实现了I2C适配器设备文件的功能,应用程序通过“i2c-%d”文件名并使用文件操作接口open()、write()、read()、ioctl()和close()等来访问这个设备。应用层可以借用这些接口访问挂接在适配器上的I2C设备的存储空间或寄存器并控制I2C设备的工作方式。 设备驱动则是与挂在I2C总线上的具体的设备通讯的驱动。通过I2C总线驱动提供的函数,设备驱动可以忽略不同总线控制器的差异,不考虑其实现细节地与硬件设备通讯。 这部分在MTK 6516中是由具体的设备实现的。(比如camera)

linux的I2C子系统详解

Linux中I2C驱动子系统详解 Author :Aston Mail : astonqa@https://www.doczj.com/doc/e713297927.html, 写在前面 首先,本教程不来自于权威书籍或文献,完全为本人个人理解的描述。限于水平,其中若有错误及不当,敬请指正、不胜感激。 其次,本教程撰写过程中虽参考了网络上各种资料,但不同于网上大部分资料的 粗略介绍或只关注一个点。相反,本教程从全局出发,全面描述了I2C总线及设备在linux系统中的实现方法,适合于希望对linux中I2C子系统进行详细、彻底学习的人士共享与阅读。 第一部分:框架描述 第二部分:I2C总线与实际硬件介绍 第三部分:linux中I2C子系统与主要数据结构分析 一个I2C设备要在linux下使用,必须包含两个驱动:I2C总线驱动和I2C设备驱动。涉及到的主要数据结构有i2c_adapter, i2c_client, i2c_driver, i2c_algorithm(均包含在include/linux/i2c.h中),这几个数据结构是I2C驱动中主 要的数据抽象,搞清楚这几个数据结构的意义是理解I2C驱动架构的前提。 i2c_adapter用来虚拟I2C适配器,所谓的适配器即用来匹配其两端连接的设备的器件。在这里,I2C适配器是一个虚拟的器件,该器件用来匹配I2C总线上连接的设备和I2C主控制器。因此可以认为:i2c_adapter对应的物理实体就是SoC内部的I2C控制器。 i2c_algorithm是一个纯软件层面的数据抽象,没有对应的物理实体。它用来描述一个i2c_adapter(实质是一个I2C控制器)的通信时序,这其实很好理解。大家都知道,I2C总线上的通信双方要完成通信需要按照一定的时序吧,如下图即是S3C2440中I2C控制器的主发送时序(即2440作为I2C主设备向外部连接的从设备发送信息)。 i2c_algorithm中封装的方法旨在描述该种时序,I2C总线驱动中最终需要靠这里封装 的方法来操作I2C控制器硬件发送通信时序。

linux下i2c驱动源码详解

Linux i2c驱动详细分析. 首先在S3C2440平台的初始化函数中,主要是将开发平台的设备注册进了系统,也就是将device注册到了platform虚拟的总线上,并进行了一些初始化的工作,这里我们只关注I2C的部分。 static void __init smdk2440_machine_init(void) { s3c24xx_fb_set_platdata(&smdk2440_fb_info); s3c_i2c0_set_platdata(NULL); platform_add_devices(smdk2440_devices, ARRAY_SIZE(smdk2440_devices)); smdk_machine_init(); } s3c_i2c0_set_platdata()函数将S3C2440上的I2C控制器进行了一些初始化,但是并没有写入硬件寄存器,仅仅是保存在了 s3c2410_platform_i2c结构体中。 void __init s3c_i2c0_set_platdata(struct s3c2410_platform_i2c *pd) { struct s3c2410_platform_i2c *npd; if (!pd) pd = &default_i2c_data0;

npd = kmemdup(pd, sizeof(struct s3c2410_platform_i2c), GFP_KERNEL); if (!npd) printk(KERN_ERR "%s: no memory for platform data\n", __func__); else if (!npd->cfg_gpio) npd->cfg_gpio = s3c_i2c0_cfg_gpio; /* s3c_i2c0_cfg_gpio为 配置I2C控制器GPIO函数指针 */ s3c_device_i2c0.dev.platform_data = npd; /*最后将struct device 中的platform_data指针直指向了初始化后的 s3c2410_platform_i2c结构体 */ } 函数s3c_i2c0_cfg_gpio()很简单,实际上就是配置GPIO为I2C的工作模式 void s3c_i2c0_cfg_gpio(struct platform_device *dev) { s3c2410_gpio_cfgpin(S3C2410_GPE(15), S3C2410_GPE15_IICSDA); s3c2410_gpio_cfgpin(S3C2410_GPE(14), S3C2410_GPE14_IICSCL); } s3c_i2c0_set_platdata(struct s3c2410_platform_i2c *pd)函数实际上就是把初始化数据段中的default_i2c_data0结构体复制过来,然后对GPIO进行配置的函数指针进行了初始化。default_i2c_data0结构体如下:

Linux I2C驱动分析

一:前言 I2c是philips提出的外设总线.I2C只有两条线,一条串行数据线:SDA,一条是时钟线SCL.正因为这样,它方便了工程人员的布线.另外,I2C是一种多主机控制总线.它和USB总线不同,USB是基于master-slave机制,任何设备的通信必须由主机发起才可以.而I2C是基于multi master机制.一同总线上可允许多个master.关于I2C协议的知识,这里不再赘述.可自行下载spec阅读即可. 二:I2C架构概述 在linux中,I2C驱动架构如下所示: 如上图所示,每一条I2C对应一个adapter.在kernel中,每一个adapter提供了一个描述的结构(struct i2c_adapter),也定义了adapter支持的操作(struct i2c_adapter).再通过i2c core层将i2c 设备与i2c adapter关联起来.

这个图只是提供了一个大概的框架.在下面的代码分析中,从下至上的来分析这个框架图.以下的代码分析是基于linux 2.6.26.分析的代码基本位于: linux-2.6.26.3/drivers/i2c/位置. 三:adapter注册 在kernel中提供了两个adapter注册接口,分别为i2c_add_adapter()和i2c_add_numbered_adapter().由于在系统中可能存在多个adapter,因为将每一条I2C总线对应一个编号,下文中称为I2C总线号.这个总线号的PCI中的总线号不同.它和硬件无关,只是软件上便于区分而已. 对于i2c_add_adapter()而言,它使用的是动态总线号,即由系统给其分析一个总线号,而i2c_add_numbered_adapter()则是自己指定总线号,如果这个总线号非法或者是被占用,就会注册失败. 分别来看一下这两个函数的代码: int i2c_add_adapter(struct i2c_adapter *adapter) { int id, res = 0; retry: if (idr_pre_get(&i2c_adapter_idr, GFP_KERNEL) == 0) return -ENOMEM; mutex_lock(&core_lock); /* "above" here means "above or equal to", sigh */ res = idr_get_new_above(&i2c_adapter_idr, adapter, __i2c_first_dynamic_bus_num, &id); mutex_unlock(&core_lock); if (res < 0) { if (res == -EAGAIN) goto retry; return res; } adapter->nr = id; return i2c_register_adapter(adapter); } 在这里涉及到一个idr结构.idr结构本来是为了配合page cache中的radix tree而设计的.在这里我们只需要知道,它是一种高效的搜索树,且这个树预先存放了一些内存.避免在内存不够的时候出现问题.所在,在往idr中插入结构的时候,首先要调用idr_pre_get()为它预留足够的空闲内存,然后再调用idr_get_new_above()将结构插入idr中,该函数以参数的形式返回一个id.以后凭这个id就可以在idr中找到相对应的结构了.对这个数据结构操作不太理解的可以查阅本站<< linux文件系统之文件的读写>>中有关radix tree的分析. 注意一下idr_get_new_above(&i2c_adapter_idr, adapter,__i2c_first_dynamic_bus_num, &id)的参数的含义,它是将adapter结构插入到i2c_adapter_idr中,存放位置的id必须要大于或者等于__i2c_first_dynamic_bus_num,

相关主题
文本预览
相关文档 最新文档