写在前面

由于IIC总线只需要两根线就可以完成读写操作,而且通信协议简单,一条总线上可以挂载多个设备,因此被广泛使用。但是IIC总线有一个缺点,就是传输速率比较低。本文基于Linux-2.6.36版本,说说IIC子系统在Linux中的实现。

借用某书上的IIC子系统的体系结构图:

Linux IIC子系统体系结构

下面开始分析IIC子系统。

IIC子系统的初始化在drivers/i2c/i2c-core.c文件中的i2c_init函数中:

 static int __init i2c_init(void)
{
int retval; retval = bus_register(&i2c_bus_type);
if (retval)
return retval;
#ifdef CONFIG_I2C_COMPAT
i2c_adapter_compat_class = class_compat_register("i2c-adapter");
if (!i2c_adapter_compat_class) {
retval = -ENOMEM;
goto bus_err;
}
#endif
retval = i2c_add_driver(&dummy_driver);
if (retval)
goto class_err;
return ; class_err:
#ifdef CONFIG_I2C_COMPAT
class_compat_unregister(i2c_adapter_compat_class);
bus_err:
#endif
bus_unregister(&i2c_bus_type);
return retval;
}

1225行,向系统注册IIC总线,其中i2c_bus_type的定义为:

 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,
};

345行,i2c_device_match函数时用来匹配IIC总线上的设备和设备驱动的,下面看下它的定义:

 static int i2c_device_match(struct device *dev, struct device_driver *drv)
{
struct i2c_client *client = i2c_verify_client(dev);
struct i2c_driver *driver; if (!client)
return ; /* Attempt an OF style match */
if (of_driver_match_device(dev, drv))
return ; driver = to_i2c_driver(drv);
/* match on an id table if there is one */
if (driver->id_table)
return i2c_match_id(driver->id_table, client) != NULL; return ;
}

在IIC子系统中,用struct i2c_client来描述一个具体的IIC设备(IIC从机)。73行,如果没有IIC设备的话就直接返回0,表示匹配不成功。

77行,用of的方式进行匹配,应该是设备树方面的,具体没了解过。

82行,如果驱动的id table存在则调用83行的i2c_match_id函数进行匹配:

 static const struct i2c_device_id *i2c_match_id(const struct i2c_device_id *id,
const struct i2c_client *client)
{
while (id->name[]) {
if (strcmp(client->name, id->name) == )
return id;
id++;
}
return NULL;
}

很简单,就是拿驱动的id table中的每一项与i2c_client的name成员进行比较,如果它们的名字相同就表示匹配成功,否则匹配失败,返回NULL。从这里也可以看出IIC的总线匹配方式与platform总线的匹配方式是不一样,一般情况下,IIC总线的匹配方式是根据设备名字和驱动中的id table,而platfrom总线的匹配方式是根据设备名字和驱动名字。下面看i2c_device_probe函数:

 static int i2c_device_probe(struct device *dev)
{
struct i2c_client *client = i2c_verify_client(dev);
struct i2c_driver *driver;
int status; if (!client)
return ; driver = to_i2c_driver(dev->driver);
if (!driver->probe || !driver->id_table)
return -ENODEV;
client->driver = driver;
if (!device_can_wakeup(&client->dev))
device_init_wakeup(&client->dev,
client->flags & I2C_CLIENT_WAKE);
dev_dbg(dev, "probe\n"); status = driver->probe(client, i2c_match_id(driver->id_table, client));
if (status) {
client->driver = NULL;
i2c_set_clientdata(client, NULL);
}
return status;
}

112行,检查IIC设备是否存在。

119至121行,电源管理方面的,IIC在电源管理方面做得还是不错的,有兴趣可以看一下。

124行,重要,调用IIC设备驱动中probe函数。

下面以tiny6410为具体平台去说IIC子系统的其他内容。S3c6410的IIC控制器驱动位于drivers/i2c/busses/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);

1022行,注册平台驱动,看下s3c24xx_i2c_driver的定义可以发现.driver.name的值与板文件中定义的platform device的name不一样,所以这里采用的是id table方式进行匹配。我们知道,当此驱动与设备匹配后,驱动中的probe函数将会被调用,那么下面看probe函数的定义:

 static int s3c24xx_i2c_probe(struct platform_device *pdev)
{
struct s3c24xx_i2c *i2c;
struct s3c2410_platform_i2c *pdata;
struct resource *res;
int ret; pdata = pdev->dev.platform_data;
if (!pdata) {
dev_err(&pdev->dev, "no platform data\n");
return -EINVAL;
} i2c = kzalloc(sizeof(struct s3c24xx_i2c), GFP_KERNEL);
if (!i2c) {
dev_err(&pdev->dev, "no memory for state\n");
return -ENOMEM;
} strlcpy(i2c->adap.name, "s3c2410-i2c", sizeof(i2c->adap.name));
i2c->adap.owner = THIS_MODULE;
i2c->adap.algo = &s3c24xx_i2c_algorithm;
i2c->adap.retries = ;
i2c->adap.class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
i2c->tx_setup = ; spin_lock_init(&i2c->lock);
init_waitqueue_head(&i2c->wait); /* find the clock and enable it */ i2c->dev = &pdev->dev;
i2c->clk = clk_get(&pdev->dev, "i2c");
if (IS_ERR(i2c->clk)) {
dev_err(&pdev->dev, "cannot get clock\n");
ret = -ENOENT;
goto err_noclk;
} dev_dbg(&pdev->dev, "clock source %p\n", i2c->clk); clk_enable(i2c->clk); /* map the registers */ res = platform_get_resource(pdev, IORESOURCE_MEM, );
if (res == NULL) {
dev_err(&pdev->dev, "cannot find IO resource\n");
ret = -ENOENT;
goto err_clk;
} i2c->ioarea = request_mem_region(res->start, resource_size(res),
pdev->name); if (i2c->ioarea == NULL) {
dev_err(&pdev->dev, "cannot request IO\n");
ret = -ENXIO;
goto err_clk;
} i2c->regs = ioremap(res->start, resource_size(res)); if (i2c->regs == NULL) {
dev_err(&pdev->dev, "cannot map IO\n");
ret = -ENXIO;
goto err_ioarea;
} dev_dbg(&pdev->dev, "registers %p (%p, %p)\n",
i2c->regs, i2c->ioarea, res); /* setup info block for the i2c core */ i2c->adap.algo_data = i2c;
i2c->adap.dev.parent = &pdev->dev; /* initialise the i2c controller */ ret = s3c24xx_i2c_init(i2c);
if (ret != )
goto err_iomap; /* find the IRQ for this unit (note, this relies on the init call to
00000874 * ensure no current IRQs pending
00000875 */ i2c->irq = ret = platform_get_irq(pdev, );
if (ret <= ) {
dev_err(&pdev->dev, "cannot find IRQ\n");
goto err_iomap;
} ret = request_irq(i2c->irq, s3c24xx_i2c_irq, IRQF_DISABLED,
dev_name(&pdev->dev), i2c); if (ret != ) {
dev_err(&pdev->dev, "cannot claim IRQ %d\n", i2c->irq);
goto err_iomap;
} ret = s3c24xx_i2c_register_cpufreq(i2c);
if (ret < ) {
dev_err(&pdev->dev, "failed to register cpufreq notifier\n");
goto err_irq;
} /* Note, previous versions of the driver used i2c_add_adapter()
00000898 * to add the bus at any number. We now pass the bus number via
00000899 * the platform data, so if unset it will now default to always
00000900 * being bus 0.
00000901 */ i2c->adap.nr = pdata->bus_num; ret = i2c_add_numbered_adapter(&i2c->adap);
if (ret < ) {
dev_err(&pdev->dev, "failed to add bus to i2c core\n");
goto err_cpufreq;
} platform_set_drvdata(pdev, i2c); dev_info(&pdev->dev, "%s: S3C I2C adapter\n", dev_name(&i2c->adap.dev));
return ; err_cpufreq:
s3c24xx_i2c_deregister_cpufreq(i2c); err_irq:
free_irq(i2c->irq, i2c); err_iomap:
iounmap(i2c->regs); err_ioarea:
release_resource(i2c->ioarea);
kfree(i2c->ioarea); err_clk:
clk_disable(i2c->clk);
clk_put(i2c->clk); err_noclk:
kfree(i2c);
return ret;
}

797至801行,没有平台数据是不行的。

803至807行,为具体平台的IIC控制器数据结构申请内存,一般来说,不仅是IIC控制器,每一个控制器都会有一个结构体来描述。struct s3c24xx_i2c的定义也是在drivers/i2c/busses/i2c-s3c2410.c中:

 struct s3c24xx_i2c {
spinlock_t lock;
wait_queue_head_t wait;
unsigned int suspended:; struct i2c_msg *msg;
unsigned int msg_num;
unsigned int msg_idx;
unsigned int msg_ptr; unsigned int tx_setup;
unsigned int irq; enum s3c24xx_i2c_state state;
unsigned long clkrate; void __iomem *regs;
struct clk *clk;
struct device *dev;
struct resource *ioarea;
struct i2c_adapter adap; #ifdef CONFIG_CPU_FREQ
struct notifier_block freq_transition;
#endif
};

63行,表示IIC控制器是否已经挂起,挂起的话就不能操作IIC控制器了。

65行,struct i2c_msg用来描述一次读写操作包含的信息。定义在include/linux/i2c.h,比较简单:

 struct i2c_msg {
__u16 addr; /* slave address */
__u16 flags;
#define I2C_M_TEN 0x0010 /* this is a ten bit chip address */
#define I2C_M_RD 0x0001 /* read data, from slave to master */
#define I2C_M_NOSTART 0x4000 /* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_REV_DIR_ADDR 0x2000 /* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_IGNORE_NAK 0x1000 /* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_NO_RD_ACK 0x0800 /* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_RECV_LEN 0x0400 /* length will be first received byte */
__u16 len; /* msg length */
__u8 *buf; /* pointer to msg data */
};

508行,IIC从机的地址。

509行,flags的取值就是510至516行这些值。

517行,这次读写操作的数据长度。518行,读写数据的地址。

回到struct s3c24xx_i2c,66行,message的数量。67行,当前是第几个message。68行,缓冲区数组成员的索引值,表示当前要读写的是第几个数据。

70行,当数据写入IIC控制器的数据移位寄存器后需要延时多久,在s3c6410里的单位是ns。

71行,IIC控制器使用的中断号。

73行,IIC控制器的状态,具体来说有以下几种状态:

 enum s3c24xx_i2c_state {
STATE_IDLE,
STATE_START,
STATE_READ,
STATE_WRITE,
STATE_STOP
};

74行,IIC总线的速率。76行,IIC控制器寄存器起始地址。

77行,IIC控制器时钟。78行,设备模型相关的。79行,IO口资源。

80行,每一个IIC控制器对应一个adapter。struct i2c_adapter同样是在include/linux/i2c.h中定义:

 struct i2c_adapter {
struct module *owner;
unsigned int id;
unsigned int class; /* classes to allow probing for */
const struct i2c_algorithm *algo; /* the algorithm to access the bus */
void *algo_data; /* data fields that are valid for all devices */
struct rt_mutex bus_lock; int timeout; /* in jiffies */
int retries;
struct device dev; /* the adapter device */ int nr;
char name[];
struct completion dev_released; struct mutex userspace_clients_lock;
struct list_head userspace_clients;
};

355行,模块的所有者。

356行,此适配器的编号,第1个适配器的编号为0,以此类推。

358行,算法?第一眼看到的时候差点被吓倒了,其实就是定义了3个函数指针,这些函数来完成具体的读写操作。

364行,超时时间。365行,重试次数,一次读写操作不成功的话就多试几次。368行,适配器编号。369行,适配器的名字。

回到s3c24xx_i2c_probe函数,809行,设置适配器的名字。

811行,s3c6410的IIC控制器进行读写操作时所使用的逻辑,等碰到时再详细说吧。

813行,刚才在说struct i2c_adapter时被忽略的成员class就是在这里被赋值的。

814行,对于s3c6410的IIC控制器而言,数据被写入移位寄存器后需要延时50ns。

822至831行,获取IIC时钟并使能。

835至857行,获取IO口资源并进行映射。

869行,设置相应的IO口为IIC功能,并设置IICADD寄存器和IICCON寄存器。

877至889行,申请IIC中断,中断处理函数为s3c24xx_i2c_irq,后面会遇到,到时再说。

891至895行,CPU频率相关的,略过吧。

905行,“重头戏”啊,注册IIC适配器和注册IIC设备等都发生在里面,i2c_add_numbered_adapter函数在drivers/i2c/i2c-core.c里定义:

 int i2c_add_numbered_adapter(struct i2c_adapter *adap)
{
int id;
int status; if (adap->nr & ~MAX_ID_MASK)
return -EINVAL; retry:
if (idr_pre_get(&i2c_adapter_idr, GFP_KERNEL) == )
return -ENOMEM; mutex_lock(&core_lock);
/* "above" here means "above or equal to", sigh;
00000962 * we need the "equal to" result to force the result
00000963 */
status = idr_get_new_above(&i2c_adapter_idr, adap, adap->nr, &id);
if (status == && id != adap->nr) {
status = -EBUSY;
idr_remove(&i2c_adapter_idr, id);
}
mutex_unlock(&core_lock);
if (status == -EAGAIN)
goto retry; if (status == )
status = i2c_register_adapter(adap);
return status;
}

Linux设备驱动剖析之IIC(一)的更多相关文章

  1. Linux设备驱动剖析之IIC(二)

    953行,适配器的编号大于MAX_ID_MASK是不行的,MAX_ID_MASK是一个宏,展开后的值为61. 957至968行,关于管理小整形ID数的,没怎么了解,略过. 974行,调用i2c_reg ...

  2. Linux设备驱动剖析之IIC(四)

    558行,又重试2次. 560行,调用s3c24xx_i2c_doxfer函数: static int s3c24xx_i2c_doxfer(struct s3c24xx_i2c *i2c, stru ...

  3. Linux设备驱动剖析之IIC(三)

    下面以eeprom用户程序调用ioctl函数的写操作为例追踪IIC子系统的调用过程.eeprom的用户测试是大部分开发板都自带的.看写一个字节数据的eeprom_write_byte函数的定义: in ...

  4. Linux设备驱动剖析之SPI(一)

    写在前面 初次接触SPI是因为几年前玩单片机的时候,由于普通的51单片机没有SPI控制器,所以只好用IO口去模拟.最近一次接触SPI是大三时参加的校内选拔赛,当时需要用2440去控制nrf24L01, ...

  5. Linux设备驱动剖析之SPI(三)

    572至574行,分配内存,注意对象的类型是struct spidev_data,看下它在drivers/spi/spidev.c中的定义: struct spidev_data { dev_t de ...

  6. Linux设备驱动剖析之SPI(二)

    957至962行,一个SPI控制器用一个master来描述.这里使用SPI核心的spi_alloc_master函数请求分配master.它在drivers/spi/spi.c文件中定义: struc ...

  7. Linux设备驱动剖析之Input(四)

    static void input_pass_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) ...

  8. Linux设备驱动剖析之Input(一)

    前言 以前在移植Qt到开发板上时只知道在配置文件中需要指定触摸屏的设备文件/dev/input/event0,仅此而已.直到一年半前突然想到用红外遥控器控制Tiny6410开发板上的Android系统 ...

  9. Linux设备驱动剖析之Input(二)

    分别是总线类型.厂商号.产品号和版本号. 1156行,evbit,设备支持的事件类型的位图,每一位代表一种事件,比如EV_KEY.EV_REL事件等等.BITS_TO_LONGS(nr)是一个宏,假设 ...

随机推荐

  1. 第三百八十五节,Django+Xadmin打造上线标准的在线教育平台—登录功能实现,回填数据以及错误提示html

    第三百八十五节,Django+Xadmin打造上线标准的在线教育平台—登录功能实现 1,配置登录路由 from django.conf.urls import url, include # 导入dja ...

  2. e833. 获得JTabbedPane中的卡片

    This example retrieves all the tabs in a tabbed pane: // To create a tabbed pane, see e828 创建JTabbed ...

  3. vs2015配置mysql数据库时,mysql.data、mysql.data.entity、EntityFramework的安装错误问题

    vs2015连接mysql数据库常见问题 最近在vs2015用asp.net开发一个网站,要连接mysql数据库,于是百度了一下相关配置的文章,有好几篇文章说了相关步骤,但是我装的时候还是遇到了问题, ...

  4. 面试的角度诠释Java工程师(二)

    续言: 相信每一位简书的作者,都会有我这样的思考:怎么写好一篇文章?或者怎么写好一篇技术类的文章?我就先说说我的感悟吧,写文章其实和写程序是一样的.为什么我会说它们是一样的?简单思考一下...... ...

  5. .NET Best Practices

    Before starting with best practices tobe followed, it is good to have clear understanding of how mem ...

  6. 阿里云centos7安装桌面环境

    centos7. 1.安装X11.yum groupinstall "X Window System". 2.安装gnome. 全安装:yum groupinstall -y &q ...

  7. Oracle 初始化参数 二三事,随记

    (1) alter system set log_archive_dest_n='location=d:\一个存在的目录';  ---- 预期 但是如果“d:\一个存在的目录”不是一个有效的目录,则“ ...

  8. 破解IT运维成本困境,专业化分工是妙方

    随着IT建设的不断深入和发展,IT运维成为了企业运营的必需品.许多企业的IT预算相比于去年虽然有了很大的提高,但总体来说还是非常紧张.上周,我参加了一个CIO沙龙研讨会,现场调查问到目前CIO在IT运 ...

  9. Linux下的ssh远程访问

    准备工作:首先需要在windows系统中安装虚拟机,并在虚拟机中安装好linux操作系统,这里安装的是vmware player虚拟机和ubuntu版本的操作系统.关于该部分的安装在作者的其他经验中有 ...

  10. EXCEPTION:FATAL: UNABLE TO CREATE ‘…GIT/INDEX.LOCK’ FILE EXISTS

    FATAL: UNABLE TO CREATE ‘…GIT/INDEX.LOCK’ FILE EXISTS Hi, Today I will share you my other experience ...