Chinaunix首页 | 论坛 | 博客
  • 博客访问: 10373
  • 博文数量: 2
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 10
  • 用 户 组: 普通用户
  • 注册时间: 2014-09-08 21:21
文章分类
文章存档

2014年(2)

我的朋友
最近访客

分类: LINUX

2014-12-17 22:06:18

原文地址:linux下I2C驱动分析 作者:kine1314

这几篇文章来分析下linux2.6I2C驱动的写法,对个人也是一个学习的过程。

       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_clientadapter变量指向其所接的适配器

       tw_i2c_client.adapter = adapter;

//调用i2c_attach_client函数将i2c_client依附到适配器,就是将该client接到其对应adapterclient链表上面。

       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;

//构造了3I2C消息,注意这里构造多少条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);

//中断处理,通过IMX27datasheet可知,有三种情况可以触发中断: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_algorithmmaster_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);

}

可见还是调用了algothmfunctionlity函数。

我们来看看该驱动中这个函数的实现:

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的功能。

阅读(1327) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~