分类:
2012-06-05 14:37:06
原文地址:linux下I2C驱动分析 作者:kine1314
这几篇文章来分析下linux2.6下I2C驱动的写法,对个人也是一个学习的过程。
在linux系统下,i2c驱动架构分为三个部分:I2C核心、I2C总线驱动、I2C设备驱动,下面首先从设备驱动怎么写开始,主要是分析内核中的代码。本文中分析的内核代码是基于LINUX2.6.19版本。
首先看一个以前做过的I2C驱动。下面是一个设备驱动的模块加载和卸载函数。
static struct i2c_driver tw_i2c_driver = {
.driver = {
.owner = THIS_MODULE,
.name = "TW Client",
},
.attach_adapter = tw_attach,
.detach_client = tw_detach,
};
static __init int tw_init(void)
{
u8 err;
err = i2c_add_driver(&tw_i2c_driver);
return err;
}
static void __exit tw_clean(void)
{
i2c_del_driver(&tw_i2c_driver);
}
module_init(tw_init);
module_exit(tw_clean);
从上面模块加载函数中可以看到,调用了I2C核心函数i2c_add_driver,我们看看这个函数做了些什么。
static inline int i2c_add_driver(struct i2c_driver *driver)
{
return i2c_register_driver(THIS_MODULE, driver);
}
i2c_register_driver的作用是向系统注册该驱动,下面我们看这个函数的具体实现。
int i2c_register_driver(struct module *owner, struct i2c_driver *driver)
{
struct list_head *item;
struct i2c_adapter *adapter;
int res;
//将该设备插入到设备链表中,这部分属于设备管理模型中的内容。
/* add the driver to the list of i2c drivers in the driver core */
driver->driver.owner = owner;
driver->driver.bus = &i2c_bus_type;
res = driver_register(&driver->driver);
if (res)
return res;
mutex_lock(&core_lists);
list_add_tail(&driver->list,&drivers);
pr_debug("i2c-core: driver [%s] registered\n", driver->driver.name);
//下面将要找到该设备所接的适配器,调用i2c_driver结构体的attach_adapter
//函数。
/* now look for instances of driver on our adapters */
if (driver->attach_adapter) {
//注意下面是一个循环体,系统总所有的适配器,都放在链表adapters里了,这个
//工作是在I2C总线驱动里做的。
list_for_each(item,&adapters) {
adapter = list_entry(item, struct i2c_adapter, list);
//对于每一个适配器都要调用attach_adapter函数,这个attach_adpter函数是用
//户编写的,需要要在这个函数调用i2c_probe函数来探测该设备是否接在该适配器上面。
driver->attach_adapter(adapter);
}
}
mutex_unlock(&core_lists);
return 0;
}
下面我们看看driver->attach_adapter函数的实现:
static unsigned short normal_i2c[] = { TW_I2C_ADDRESS, I2C_CLIENT_END };
static int tw_attach(struct i2c_adapter *adap)
{
//调用了i2c_probe函数,注意参数addr_data已经在i2c.h中给出了模板定义的,这里只需要给出addr_data.normal_i2c即可,就是给出需要探测的设备从地址。
err = i2c_probe(adap, &addr_data, &tw_detect_client);
clk_disable(clk);
clk_put(clk);
return err;
}
我们在来跟踪i2c_probe函数的实现:
int i2c_probe(struct i2c_adapter *adapter,
struct i2c_client_address_data *address_data,
int (*found_proc) (struct i2c_adapter *, int, int))
{
int i, err;
//获得当前适配器的ID
int adap_id = i2c_adapter_id(adapter);
/* Force entries are done first, and are not affected by ignore
entries */
//首先扫描forces中的地址,这个是不受ignore影响的
if (address_data->forces) {
unsigned short **forces = address_data->forces;
int kind;
for (kind = 0; forces[kind]; kind++) {
for (i = 0; forces[kind][i] != I2C_CLIENT_END;
i += 2) {
if (forces[kind][i] == adap_id
|| forces[kind][i] == ANY_I2C_BUS) {
dev_dbg(&adapter->dev, "found force "
"parameter for adapter %d, "
"addr 0x%02x, kind %d\n",
adap_id, forces[kind][i + 1],
kind);
err = i2c_probe_address(adapter,
forces[kind][i + 1],
kind, found_proc);
if (err)
return err;
}
}
}
}
//下面是有关适配器具体功能支持方面的问题,暂且不管他
/* Stop here if we can't use SMBUS_QUICK */
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_QUICK)) {
if (address_data->probe[0] == I2C_CLIENT_END
&& address_data->normal_i2c[0] == I2C_CLIENT_END)
return 0;
dev_warn(&adapter->dev, "SMBus Quick command not supported, "
"can't probe for chips\n");
return -1;
}
/* Probe entries are done second, and are not affected by ignore
entries either */
//接着会扫描probe项
for (i = 0; address_data->probe[i] != I2C_CLIENT_END; i += 2) {
if (address_data->probe[i] == adap_id
|| address_data->probe[i] == ANY_I2C_BUS) {
dev_dbg(&adapter->dev, "found probe parameter for "
"adapter %d, addr 0x%02x\n", adap_id,
address_data->probe[i + 1]);
err = i2c_probe_address(adapter,
address_data->probe[i + 1],
-1, found_proc);
if (err)
return err;
}
}
//最为关键的地方,扫描normal项,一般我们设备的地址就是放在这里的。
/* Normal entries are done last, unless shadowed by an ignore entry */
for (i = 0; address_data->normal_i2c[i] != I2C_CLIENT_END; i += 1) {
int j, ignore;
ignore = 0;
//下面的地方就是确认扫描的地址不在ignore里面
for (j = 0; address_data->ignore[j] != I2C_CLIENT_END;
j += 2) {
if ((address_data->ignore[j] == adap_id ||
address_data->ignore[j] == ANY_I2C_BUS)
&& address_data->ignore[j + 1]
== address_data->normal_i2c[i]) {
dev_dbg(&adapter->dev, "found ignore "
"parameter for adapter %d, "
"addr 0x%02x\n", adap_id,
address_data->ignore[j + 1]);
ignore = 1;
break;
}
}
if (ignore)
continue;
//当该地址是有效地址后,就调用i2c_probe_address函数来判断该设备是否接在该适配器上面。
dev_dbg(&adapter->dev, "found normal entry for adapter %d, "
"addr 0x%02x\n", adap_id,
address_data->normal_i2c[i]);
err = i2c_probe_address(adapter, address_data->normal_i2c[i],
-1, found_proc);
if (err)
return err;
}
return 0;
}
下面我们来追踪下i2c_probe_address函数的实现过程:
/* ----------------------------------------------------
* the i2c address scanning function
* Will not work for 10-bit addresses!
* ----------------------------------------------------
*/
static int i2c_probe_address(struct i2c_adapter *adapter, int addr, int kind,
int (*found_proc) (struct i2c_adapter *, int, int))
{
int err;
//确定该地址为有效地址
/* Make sure the address is valid */
if (addr < 0x03 || addr > 0x77) {
dev_warn(&adapter->dev, "Invalid probe address 0x%02x\n",
addr);
return -EINVAL;
}
//如果该地址已经被用,则跳过
/* Skip if already in use */
if (i2c_check_addr(adapter, addr))
return 0;
//判断该总线上面是否真的接有这个设备,对于i2c_smbus_xfer在讲总线驱动的时候再详细介绍。
/* Make sure there is something at this address, unless forced */
if (kind < 0) {
if (i2c_smbus_xfer(adapter, addr, 0, 0, 0,
I2C_SMBUS_QUICK, NULL) < 0)
return 0;
/* prevent 24RF08 corruption */
if ((addr & ~0x0f) == 0x50)
i2c_smbus_xfer(adapter, addr, 0, 0, 0,
I2C_SMBUS_QUICK, NULL);
}
//如果有该设备就调用回调函数
/* Finally call the custom detection function */
err = found_proc(adapter, addr, kind);
/* -ENODEV can be returned if there is a chip at the given address
but it isn't supported by this chip driver. We catch it here as
this isn't an error. */
if (err == -ENODEV)
err = 0;
if (err)
dev_warn(&adapter->dev, "Client creation failed at 0x%x (%d)\n",
addr, err);
return err;
}
下面我们在看看回调函数做了什么:
/*!
* tw I2C detect_client function
*
* @param adapter struct i2c_adapter *
* @param address int
* @param kind int
*
* @return Error code indicating success or failure
*/
static int tw_detect_client(struct i2c_adapter *adapter, int address,
int kind)
{
//将i2c_client的adapter变量指向其所接的适配器
tw_i2c_client.adapter = adapter;
//调用i2c_attach_client函数将i2c_client依附到适配器,就是将该client接到其对应adapter的client链表上面。
if (i2c_attach_client(&tw_i2c_client)) {
tw_i2c_client.adapter = NULL;
printk(KERN_ERR
"tw_detect_client: i2c_attach_client failed\n");
return -1;
}
}
int i2c_attach_client(struct i2c_client *client)
{
struct i2c_adapter *adapter = client->adapter;
int res = 0;
mutex_lock(&adapter->clist_lock);
if (__i2c_check_addr(client->adapter, client->addr)) {
res = -EBUSY;
goto out_unlock;
}
//最关键的地方:将该client插到适配器的clients链表中
list_add_tail(&client->list,&adapter->clients);
client->usage_count = 0;
//下面的都是设备管理模型实现,以后再详细讨论
client->dev.parent = &client->adapter->dev;
client->dev.driver = &client->driver->driver;
client->dev.bus = &i2c_bus_type;
client->dev.release = &i2c_client_release;
snprintf(&client->dev.bus_id[0], sizeof(client->dev.bus_id),
"%d-%04x", i2c_adapter_id(adapter), client->addr);
dev_dbg(&adapter->dev, "client [%s] registered with bus id %s\n",
client->name, client->dev.bus_id);
res = device_register(&client->dev);
if (res)
goto out_list;
res = device_create_file(&client->dev, &dev_attr_client_name);
if (res)
goto out_unregister;
mutex_unlock(&adapter->clist_lock);
if (adapter->client_register) {
if (adapter->client_register(client)) {
dev_dbg(&adapter->dev, "client_register "
"failed for client [%s] at 0x%02x\n",
client->name, client->addr);
}
}
return 0;
out_unregister:
init_completion(&client->released); /* Needed? */
device_unregister(&client->dev);
wait_for_completion(&client->released);
out_list:
list_del(&client->list);
dev_err(&adapter->dev, "Failed to attach i2c client %s at 0x%02x "
"(%d)\n", client->name, client->addr, res);
out_unlock:
mutex_unlock(&adapter->clist_lock);
return res;
}
现在我们已经找到设备所接的适配器,并且将该设备依附到适配器链表中,接下来我们看看用户如何访问设备。看下面的函数:
/*
* Function definitions
*/
static int tw_i2c_client_xfer(unsigned int addr, u8 page, u8 reg,
char *buf, int num, int tran_flag)
{
struct i2c_msg msg[3];
int ret;
//构造了3条I2C消息,注意这里构造多少条I2C消息,是有芯片决定的。
msg[0].addr = addr;
msg[0].len = 1;
msg[0].buf = &page;
msg[0].flags = tran_flag;
msg[0].flags &= ~I2C_M_RD;
msg[1].addr = addr;
msg[1].len = 1;
msg[1].buf = ®
msg[1].flags = tran_flag;
msg[1].flags &= ~I2C_M_RD;
msg[2].addr = addr;
msg[2].len = num;
msg[2].buf = buf;
msg[2].flags = tran_flag;
if (tran_flag & MXC_I2C_FLAG_READ) {
msg[2].flags |= I2C_M_RD;
} else {
msg[2].flags &= ~I2C_M_RD;
}
//调用i2c_transfer访问设备
ret = i2c_transfer(tw_i2c_client.adapter, msg, 3);
if (ret >= 0)
return 0;
return ret;
}
我们跟踪i2c_transfer函数:
int i2c_transfer(struct i2c_adapter * adap, struct i2c_msg *msgs, int num)
{
int ret;
if (adap->algo->master_xfer) {
mutex_lock(&adap->bus_lock);
//调用了adap->algo->master_xfer函数,这个函数的实现是在i2c总线驱动里做的,在讲总线驱动的时候具体解释。
ret = adap->algo->master_xfer(adap,msgs,num);
mutex_unlock(&adap->bus_lock);
return ret;
} else {
dev_dbg(&adap->dev, "I2C level transfers not supported\n");
return -ENOSYS;
}
}
总结:我们来看看整个I2C设备驱动的设计流程。
1. i2c_add_driver:添加一个I2C驱动
2.i2c_register_driver:直接被调用,将该驱动向系统注册(设备管理模型)。对系统的每个适配器,都调用i2c_probe函数,直到找到设备所接的适配器。
3.i2c_probe:将合法的设备从地址传递给i2c_probe_address函数
4.i2c_probe_address:调用总线驱动函数探测该总线上面是否接有该设备,如果有则调用i2c_attach_client函数
5.i2c_attach_client:将该设备的 i2c_client结构体依附到对应的适配器
在前一篇文章中我们分析了I2C设备驱动的写法,下面我们结合IMX27内部I2C总线驱动的例子来分析适配器驱动也就是总线驱动的具体写法。
1. 首先我们看看总线驱动的模块加载函数:
static struct platform_driver mxci2c_driver = {
.driver = {
.name = "mxc_i2c",
.owner = THIS_MODULE,
},
.probe = mxci2c_probe,
.remove = mxci2c_remove,
};
static int __init mxc_i2c_init(void)
{
/* Register the device driver structure. */
return platform_driver_register(&mxci2c_driver);
}
/*!
* This function is used to cleanup all resources before the driver exits.
*/
static void __exit mxc_i2c_exit(void)
{
platform_driver_unregister(&mxci2c_driver);
}
subsys_initcall(mxc_i2c_init);
module_exit(mxc_i2c_exit);
subsys_initcall在驱动成长中相当于module_init函数。
可以看到该I2C总线驱动是用Platform虚拟平台总线来管理的,这样方便将系统资源与具体实现分开,便于驱动开发。当调用platform_driver_register函数注册平台驱动时,会引发其probe函数的执行,在这里即是mxci2c_probe函数,我们来逐条看看这个函数里做了些什么:
static int mxci2c_probe(struct platform_device *pdev)
{
//mxc_i2c_device结构是设备驱动私有结构体,主要用来存放该设备的相关信息。
mxc_i2c_device *mxc_i2c;
struct mxc_i2c_platform_data *i2c_plat_data = pdev->dev.platform_data;
struct resource *res;
int id = pdev->id;
u32 clk_freq;
int ret = 0;
int i;
mxc_i2c = kzalloc(sizeof(mxc_i2c_device), GFP_KERNEL);
if (!mxc_i2c) {
return -ENOMEM;
}
//获得I2C控制器在ARM芯片中的地址
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (res == NULL) {
ret = -ENODEV;
goto err1;
}
mxc_i2c->membase = IO_ADDRESS(res->start);
//获得I2C中断号
mxc_i2c->irq = platform_get_irq(pdev, 0);
if (mxc_i2c->irq < 0) {
ret = mxc_i2c->irq;
goto err1;
}
//向系统注册该中断,中断执行函数是mxc_i2c_handler
ret = request_irq(mxc_i2c->irq, mxc_i2c_handler,
0, pdev->name, mxc_i2c);
if (ret < 0) {
goto err1;
}
//初始化一个等待队列头,用于中断处理函数
init_waitqueue_head(&mxc_i2c->wq);
//激活GPIO
gpio_i2c_active(id);
//设置I2C适配器的时钟频率,注意在这个linux系统中,ARM内部各个模块的时钟由系统统一管理
mxc_i2c->clk = clk_get(&pdev->dev, "i2c_clk");
clk_freq = clk_get_rate(mxc_i2c->clk);
mxc_i2c->clkdiv = -1;
if (i2c_plat_data->i2c_clk) {
/* Calculate divider and round up any fractional part */
int div = (clk_freq + i2c_plat_data->i2c_clk - 1) /
i2c_plat_data->i2c_clk;
for (i = 0; i2c_clk_table[i].div != 0; i++) {
if (i2c_clk_table[i].div >= div) {
mxc_i2c->clkdiv = i2c_clk_table[i].reg_value;
break;
}
}
}
if (mxc_i2c->clkdiv == -1) {
i--;
mxc_i2c->clkdiv = 0x1F; /* Use max divider */
}
dev_dbg(&pdev->dev, "i2c speed is %d/%d = %d bps, reg val = 0x%02X\n",
clk_freq, i2c_clk_table[i].div,
clk_freq / i2c_clk_table[i].div, mxc_i2c->clkdiv);
//设置该适配器的i2c_adapter结构体
strcpy(mxc_i2c->adap.name, MXC_ADAPTER_NAME);
mxc_i2c->adap.id = id;
mxc_i2c->adap.algo = &mxc_i2c_algorithm;
mxc_i2c->adap.timeout = 1;
platform_set_drvdata(pdev, mxc_i2c);
//把mxc_i2c设置为适配器结构体的私有数据
i2c_set_adapdata(&mxc_i2c->adap, mxc_i2c);
//************关键部分:调用i2c_add_adapter函数向系统注册该适配器。
if ((ret = i2c_add_adapter(&mxc_i2c->adap)) < 0) {
goto err2;
}
printk(KERN_INFO "MXC I2C driver\n");
return 0;
err2:
free_irq(mxc_i2c->irq, mxc_i2c);
gpio_i2c_inactive(id);
err1:
dev_err(&pdev->dev, "failed to probe i2c adapter\n");
kfree(mxc_i2c);
return ret;
}
2.上面函数中的关键部分是 i2c_add_adapter,下面我们来跟踪该函数:
int i2c_add_adapter(struct i2c_adapter *adap)
{
int id, res = 0;
struct list_head *item;
struct i2c_driver *driver;
mutex_lock(&core_lists);
if (idr_pre_get(&i2c_adapter_idr, GFP_KERNEL) == 0) {
res = -ENOMEM;
goto out_unlock;
}
res = idr_get_new(&i2c_adapter_idr, adap, &id);
if (res < 0) {
if (res == -EAGAIN)
res = -ENOMEM;
goto out_unlock;
}
adap->nr = id & MAX_ID_MASK;
mutex_init(&adap->bus_lock);
mutex_init(&adap->clist_lock);
//其实很简单,就是将该适配器插入到链表中,由内核统一管理,adapters就是内核内部适配器链表的头。在前面一篇文章,i2c_probe函数最后通过这个链表头来逐一遍历系统中所有的适配器。
list_add_tail(&adap->list,&adapters);
INIT_LIST_HEAD(&adap->clients);
//下面就是属于设备管理模型的内容了,以后再详细解释
if (adap->dev.parent == NULL)
adap->dev.parent = &platform_bus;
sprintf(adap->dev.bus_id, "i2c-%d", adap->nr);
adap->dev.driver = &i2c_adapter_driver;
adap->dev.release = &i2c_adapter_dev_release;
res = device_register(&adap->dev);
if (res)
goto out_list;
res = device_create_file(&adap->dev, &dev_attr_name);
if (res)
goto out_unregister;
/* Add this adapter to the i2c_adapter class */
memset(&adap->class_dev, 0x00, sizeof(struct class_device));
adap->class_dev.dev = &adap->dev;
adap->class_dev.class = &i2c_adapter_class;
strlcpy(adap->class_dev.class_id, adap->dev.bus_id, BUS_ID_SIZE);
res = class_device_register(&adap->class_dev);
if (res)
goto out_remove_name;
dev_dbg(&adap->dev, "adapter [%s] registered\n", adap->name);
/* inform drivers of new adapters */
list_for_each(item,&drivers) {
driver = list_entry(item, struct i2c_driver, list);
if (driver->attach_adapter)
/* We ignore the return code; if it fails, too bad */
driver->attach_adapter(adap);
}
out_unlock:
mutex_unlock(&core_lists);
return res;
out_remove_name:
device_remove_file(&adap->dev, &dev_attr_name);
out_unregister:
init_completion(&adap->dev_released); /* Needed? */
device_unregister(&adap->dev);
wait_for_completion(&adap->dev_released);
out_list:
list_del(&adap->list);
idr_remove(&i2c_adapter_idr, adap->nr);
goto out_unlock;
}
3. I2C中断处理函数
在mxci2c_probe函数中我们注册了I2C中断,中断处理函数是:mxc_i2c_handler:
static irqreturn_t mxc_i2c_handler(int irq, void *dev_id)
{
mxc_i2c_device *dev = dev_id;
//sr,cr分别是i2c状态寄存器和i2c控制寄存器,必须用volatile修饰
volatile unsigned int sr, cr;
//读取两个寄存器的值
sr = readw(dev->membase + MXC_I2SR);
cr = readw(dev->membase + MXC_I2CR);
//清中断位
writew(0x0, dev->membase + MXC_I2SR);
//中断处理,通过IMX27的datasheet可知,有三种情况可以触发中断:1.一个字节传输完毕,包括发送完一个字节和接收到一个字节数据。2.当作为从设备的时候,接收到主设备发送过来的地址是该设备地址。3.丢失总线。所以首先要排除丢失总线的可能性。
if (sr & MXC_I2SR_IAL) {
printk(KERN_DEBUG "Bus Arbitration lost\n");
} else {
/* Interrupt due byte transfer completion */
tx_success = false;
/* Check if RXAK is received in Transmit mode */
//是否收到ACK确认信号
if ((cr & MXC_I2CR_MTX) && (!(sr & MXC_I2SR_RXAK))) {
tx_success = true;
}
transfer_done = true;
//*****************关键:唤醒等待队列
wake_up_interruptible(&dev->wq);
}
return IRQ_HANDLED;
}
4. 给adapter赋予具体的algorithm
在mxci2c_probe函数中配置i2c_adapter结构体时,有这样一条语句:
mxc_i2c->adap.algo = &mxc_i2c_algorithm;
即是给该adapter赋予具体的algorithm,即该适配器具体的通信方法。我们来看mxc_i2c_algorithm的具体实现:
static struct i2c_algorithm mxc_i2c_algorithm = {
.master_xfer = mxc_i2c_xfer,
.functionality = mxc_i2c_func
};
我们先讨论主要的mxc_i2c_xfer函数,对于 mxc_i2c_func我们最后结合前面一章的内容讨论。
static int mxc_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[],
int num)
{
//首先是过的私有结构体
mxc_i2c_device *dev = (mxc_i2c_device *) (i2c_get_adapdata(adap));
int i, ret = 0, addr_comp = 0;
volatile unsigned int sr;
//使能该i2c模块
mxc_i2c_module_en(dev, msgs[0].flags);
//读取i2c状态寄存器
sr = readw(dev->membase + MXC_I2SR);
//是否依然拥有总线控制权
if (sr & MXC_I2SR_IBB) {
mxc_i2c_module_dis(dev);
printk(KERN_DEBUG "Bus busy\n");
return -EREMOTEIO;
}
//gpio_i2c_active(dev->adap.id);
transfer_done = false;
tx_success = false;
//处理每一天i2c消息
for (i = 0; i < num && ret >= 0; i++) {
addr_comp = 0;
if (i == 0) {
//如果是第一条消息,必须发生开始信号
mxc_i2c_start(dev, &msgs[0]);
/* Wait for the address cycle to complete */
//等待确认信号
if (mxc_i2c_wait_for_tc(dev, msgs[0].flags)) {
mxc_i2c_stop(dev);
//gpio_i2c_inactive(dev->adap.id);
mxc_i2c_module_dis(dev);
return -EREMOTEIO;
}
addr_comp = 1;
} else {
/*
* Generate repeat start only if required i.e the address
* changed or the transfer direction changed
*/
//如果不是第一条消息,如果从设备地址改变,或者传输方向改变,则发送repstar信号,等待确认信号
if ((msgs[i].addr != msgs[i - 1].addr) ||
((msgs[i].flags & I2C_M_RD) !=
(msgs[i - 1].flags & I2C_M_RD))) {
mxc_i2c_repstart(dev, &msgs[i]);
/* Wait for the address cycle to complete */
if (mxc_i2c_wait_for_tc(dev, msgs[i].flags)) {
mxc_i2c_stop(dev);
//gpio_i2c_inactive(dev->adap.id);
mxc_i2c_module_dis(dev);
return -EREMOTEIO;
}
addr_comp = 1;
}
}
/* Transfer the data */
if (msgs[i].flags & I2C_M_RD) {
/* Read the data */
//调用mxc_i2c_readbytes接收数据
ret = mxc_i2c_readbytes(dev, &msgs[i], (i + 1 == num),
addr_comp);
if (ret < 0) {
printk(KERN_ERR "mxc_i2c_readbytes: fail.\n");
break;
}
} else {
/* Write the data */
//调用mxc_i2c_writebytes发送数据
ret = mxc_i2c_writebytes(dev, &msgs[i], (i + 1 == num));
if (ret < 0) {
printk(KERN_ERR "mxc_i2c_writebytes: fail.\n");
break;
}
}
}
//gpio_i2c_inactive(dev->adap.id);
mxc_i2c_module_dis(dev);
/*
* Decrease by 1 as we do not want Start message to be included in
* the count
*/
return (i - 1);
}
5.algothm中调用到得几个子函数,也就是i2c具体信号的产生以及数据传输函数
在上述函数中调用了几个子函数,下面我们分别在看看这几个子函数的实现过程:
//首先是发送开始信号的函数:首先是获得总线控制权,将该适配器设置为主设备,并且设置发送位,将处理后的从地址放入数据寄存器。
static void mxc_i2c_start(mxc_i2c_device * dev, struct i2c_msg *msg)
{
volatile unsigned int cr, sr;
unsigned int addr_trans;
int retry = 16;
/*
* Set the slave address and the requested transfer mode
* in the data register
*/
//将从地址左移一位,并且根据读写来确定最后一个bit
addr_trans = msg->addr << 1;
if (msg->flags & I2C_M_RD) {
addr_trans |= 0x01;
}
//将该适配器设置为主设备
/* Set the Master bit */
cr = readw(dev->membase + MXC_I2CR);
cr |= MXC_I2CR_MSTA;
writew(cr, dev->membase + MXC_I2CR);
//等总线空闲
/* Wait till the Bus Busy bit is set */
sr = readw(dev->membase + MXC_I2SR);
while (retry-- && (!(sr & MXC_I2SR_IBB))) {
udelay(3);
sr = readw(dev->membase + MXC_I2SR);
}
if (retry <= 0) {
printk(KERN_DEBUG "Could not grab Bus ownership\n");
}
//设置控制寄存器发送位
/* Set the Transmit bit */
cr = readw(dev->membase + MXC_I2CR);
cr |= MXC_I2CR_MTX;
writew(cr, dev->membase + MXC_I2CR);
//将处理过的从地址放入数据寄存器
writew(addr_trans, dev->membase + MXC_I2DR);
}
//下面看看等待ACK信号的函数:注意这里就用到中断处理函数中的等待队列了,就是根据中断处理函数中的处理来确定是否接收到ACK信号。
static int mxc_i2c_wait_for_tc(mxc_i2c_device * dev, int trans_flag)
{
int retry = 16;
while (retry-- && !transfer_done) {
//等待时间:retry*dev->adap.timeout
wait_event_interruptible_timeout(dev->wq,
transfer_done,
dev->adap.timeout);
}
transfer_done = false;
//下面是判断是否收到ACK信号
if (retry <= 0) {
/* Unable to send data */
printk(KERN_DEBUG "Data not transmitted\n");
return -1;
} else if (!(trans_flag & I2C_M_RD)) {
if (!tx_success) {
/* An ACK was not received for transmitted byte */
printk(KERN_DEBUG "ACK not received \n");
return -1;
}
}
return 0;
}
//下面看看产生restart信号的函数:其实很简单,就是当上次传输结束后,不用发送stop信号来接收对总线的拥有权,直接通过寄存器发送restart信号,并且将处理过后的地址发送到数据寄存器
static void mxc_i2c_repstart(mxc_i2c_device * dev, struct i2c_msg *msg)
{
volatile unsigned int cr;
unsigned int addr_trans;
//处理从设备地址
addr_trans = msg->addr << 1;
if (msg->flags & I2C_M_RD) {
addr_trans |= 0x01;
}
//设置控制寄存器
cr = readw(dev->membase + MXC_I2CR);
cr |= MXC_I2CR_RSTA;
writew(cr, dev->membase + MXC_I2CR);
udelay(3);
//将处理后的设备从地址写入数据寄存器
writew(addr_trans, dev->membase + MXC_I2DR);
}
//下面看看如何发出stop信号
static void mxc_i2c_stop(mxc_i2c_device * dev)
{
volatile unsigned int cr;
int retry = 16;
//写控制寄存器发送stop信号
cr = readw(dev->membase + MXC_I2CR);
cr &= ~(MXC_I2CR_MSTA | MXC_I2CR_MTX);
writew(cr, dev->membase + MXC_I2CR);
//延时确保总线空闲
for (;;) {
unsigned int sr = readw(dev->membase + MXC_I2SR);
if ((sr & MXC_I2SR_IBB) == 0) break;
if (retry-- <= 0) {
printk(KERN_DEBUG "Bus busy\n");
break;
}
udelay(3);
}
}
//最关键的读函数
/*!
* Read the received data. The function waits till data is available or times
* out. Generates a stop signal if this is the last message to be received.
* Sends an ack for all the bytes received except the last byte.
*
* @param dev the mxc i2c structure used to get to the right i2c device
* @param *msg pointer to a message structure that contains the slave
* address and a pointer to the receive buffer
* @param last indicates that this is the last message to be received
* @param addr_comp flag indicates that we just finished the address cycle
*
* @return The function returns the number of bytes read or -1 on time out.
*/
static int mxc_i2c_readbytes(mxc_i2c_device * dev, struct i2c_msg *msg,
int last, int addr_comp)
{
int i;
char *buf = msg->buf;
int len = msg->len;
volatile unsigned int cr;
// 设置相关控制寄存器
cr = readw(dev->membase + MXC_I2CR);
/*
* Clear MTX to switch to receive mode.
*/
cr &= ~MXC_I2CR_MTX;
//当只接收1个字节数据时,不发送ACK。差点被经验欺骗了,注意这里置1的时候不发送ACK,清0的时候发送ACK。
if (len == 1) {
cr |= MXC_I2CR_TXAK;
} else {
cr &= ~MXC_I2CR_TXAK;
}
writew(cr, dev->membase + MXC_I2CR);
/*
* Dummy read only at the end of an address cycle
*/
//如果刚发完地址,则要假读一次。这里不是很清楚,论坛上说照做,据说是协议要求。
if (addr_comp > 0) {
readw(dev->membase + MXC_I2DR);
}
for (i = 0; i < len; i++) {
/*
* Wait for data transmission to complete
*/
//接收到一个数据,产生一个中断
if (mxc_i2c_wait_for_tc(dev, msg->flags)) {
mxc_i2c_stop(dev);
return -1;
}
/*
* Do not generate an ACK for the last byte
*/
//接收到最后一个字节以后不发送ACK信号,如果这是最后一条i2c消息,则发送stop信号
if (i == (len - 2)) {
cr = readw(dev->membase + MXC_I2CR);
cr |= MXC_I2CR_TXAK;
writew(cr, dev->membase + MXC_I2CR);
} else if (i == (len - 1)) {
//注意这里,如果是最后一条i2c消息,则肯定会产生stop信号。如果不是,则肯定会因为从设备地址改变或者传输方向改变(比如片内寄存器地址改变,则要写寄存器地址,这个时候传输方向由读到写),则会产生restart信号。
if (last) {
mxc_i2c_stop(dev);
}
}
/* Read the data */
//将读出的数据存放到buf中
*buf++ = readw(dev->membase + MXC_I2DR);
}
return i;
}
//写函数
static int mxc_i2c_writebytes(mxc_i2c_device * dev, struct i2c_msg *msg,
int last)
{
int i;
char *buf = msg->buf;
int len = msg->len;
volatile unsigned int cr;
//配置I2C控制寄存器
cr = readw(dev->membase + MXC_I2CR);
/* Set MTX to switch to transmit mode */
cr |= MXC_I2CR_MTX;
writew(cr, dev->membase + MXC_I2CR);
for (i = 0; i < len; i++) {
/*
* Write the data
*/
//写好一个字节后等中断。
writew(*buf++, dev->membase + MXC_I2DR);
if (mxc_i2c_wait_for_tc(dev, msg->flags)) {
mxc_i2c_stop(dev);
return -1;
}
}
//如果是最后一个I2C消息,则发送stop信号。
if (last > 0) {
mxc_i2c_stop(dev);
}
return i;
}
用法总结
上面我们总结了i2c_algorithm的master_xfer成员函数的实现,我们正是调用该函数来实现i2c设备的访问的。一般我们在设备驱动中找到设备所接的适配器以后,同时也找到了该适配器的通信方法,然后我们会根据芯片的datasheet组织i2c消息,然后调用i2c核心提供的函数i2c_transfer来访问芯片。下面我们来跟踪下i2c_transfer函数,其实就是对i2c_algorithm->master_xfer的封装。
int i2c_transfer(struct i2c_adapter * adap, struct i2c_msg *msgs, int num)
{
int ret;
if (adap->algo->master_xfer) {
#ifdef DEBUG
for (ret = 0; ret < num; ret++) {
dev_dbg(&adap->dev, "master_xfer[%d] %c, addr=0x%02x, "
"len=%d\n", ret, msgs[ret].flags & I2C_M_RD ?
'R' : 'W', msgs[ret].addr, msgs[ret].len);
}
#endif
mutex_lock(&adap->bus_lock);
//可以看到就是直接调用master_xfer函数。
ret = adap->algo->master_xfer(adap,msgs,num);
mutex_unlock(&adap->bus_lock);
return ret;
} else {
dev_dbg(&adap->dev, "I2C level transfers not supported\n");
return -ENOSYS;
}
}
下面再来讨论下前面一章中i2c_probe函数中调用的i2c_check_functionality函数的实现。
I2c_probe函数中有这样一段:
/* Stop here if we can't use SMBUS_QUICK */
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_QUICK)) {
if (address_data->probe[0] == I2C_CLIENT_END
&& address_data->normal_i2c[0] == I2C_CLIENT_END)
return 0;
dev_warn(&adapter->dev, "SMBus Quick command not supported, "
"can't probe for chips\n");
return -1;
}
追踪i2c_check_functionality:
static inline int i2c_check_functionality(struct i2c_adapter *adap, u32 func)
{
return (func & i2c_get_functionality(adap)) == func;
}
static inline u32 i2c_get_functionality(struct i2c_adapter *adap)
{
return adap->algo->functionality(adap);
}
可见还是调用了algothm的functionlity函数。
我们来看看该驱动中这个函数的实现:
static struct i2c_algorithm mxc_i2c_algorithm = {
.master_xfer = mxc_i2c_xfer,
.functionality = mxc_i2c_func
};
static u32 mxc_i2c_func(struct i2c_adapter *adap)
{
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
}
可见就是要该适配器支持I2C_FUNC_SMBUS_QUICK的功能。