现在的位置: 首页 > 操作系统 > 正文

uclinux内核中的I2C驱动学习

2019年11月23日 操作系统 ⁄ 共 7777字 ⁄ 字号 评论关闭

快乐虾

http://blog.csdn.net/lights_joy/

lights@hb165.com

 

 

本文适用于

ADSP-BF561

uclinux-2008r1.5-rc3 (smp patch)

Visual DSP++ 5.0(update 5)

BF561-EZKIT

 

欢迎转载,但请保留作者信息

1       整体结构
在uclinux内核的documentation/i2c目录下,提供了i2c驱动的简单说明,其summary文件这样解释i2c驱动的几个组成部分:

When we talk about I2C, we use the following terms:

  Bus    -> Algorithm

            Adapter

  Device -> Driver

            Client

 

An Algorithm driver contains general code that can be used for a whole class

of I2C adapters. Each specific adapter driver depends on one algorithm

driver.

 

A Driver driver (yes, this sounds ridiculous, sorry) contains the general

code to access some type of device. Each detected device gets its own

data in the Client structure. Usually, Driver and Client are more closely

integrated than Algorithm and Adapter.

 

For a given configuration, you will need a driver for your I2C bus (usually

a separate Adapter and Algorithm driver), and drivers for your I2C devices

(usually one driver for each device). There are no I2C device drivers

in this package. See the lm_sensors project http://www.lm-sensors.nu

for device drivers.

 

At this time, Linux only operates I2C (or SMBus) in master mode; you can't

use these APIs to make a Linux system behave as a slave/device, either to

speak a custom protocol or to emulate some other device.

从这段话让人留下几个印象:

l         在i2c总线上,可以实现i2c或者SMBus协议,对于每一种协议都用一种algorithm进行表示。

l         每一种algorithm都必须和一个adapter相关连,这个adapter应该就是指CPU实现I2C总线的方式,比如对于561,它的实现方式是通过GPIO来模拟的,因而就用一个adapter来表示它。

l         对于挂在i2c总线上的设备,每一个设备都必须有一个驱动,此设备称之为Client。

l         由于这些总线上的设备操作相似,其驱动也类似,因此用一个模块来对这些驱动进行统一管理,这个模块可以看成是这些I2C设备驱动程序的驱动,也就是文中的Driver driver。

也不知道猜得准不准,看代码,验证一下。

2       adapter驱动
在drivers/i2c/buses目录下,有一个叫i2c-gpio.c的文件,猜测这个文件就是561平台上的adapter驱动,看看它的初始化函数:

static int __init i2c_gpio_init(void)

{

     int ret;

 

     ret = platform_driver_probe(&i2c_gpio_driver, i2c_gpio_probe);

     if (ret)

         printk(KERN_ERR "i2c-gpio: probe failed: %d/n", ret);

 

     return ret;

}

怎么这么熟悉啊!

原来它使用了platform driver和platform device结合的驱动结构,这里i2c_gpio_driver定义为:

static struct platform_driver i2c_gpio_driver = {

     .driver       = {

         .name    = "i2c-gpio",

         .owner   = THIS_MODULE,

     },

     .remove       = __exit_p(i2c_gpio_remove),

};

 

赶紧地到arch/mach-bf561/boards/ezkit.c中看看它对应的资源定义:

static struct i2c_gpio_platform_data i2c_gpio_data = {

     .sda_pin      = CONFIG_SDA_PIN,

     .scl_pin      = CONFIG_SCL_PIN,

     .sda_is_open_drain = 0,

     .scl_is_open_drain = 0,

     .udelay            = 40,

};

 

static struct platform_device i2c_gpio_device = {

     .name         = "i2c-gpio",

     .id      = 0,

     .dev     = {

         .platform_data     = &i2c_gpio_data,

     },

};

在platform_driver_probe的运作下,此驱动的初始化函数将转向执行i2c_gpio_probe:

static int __init i2c_gpio_probe(struct platform_device *pdev)

{

     struct i2c_gpio_platform_data *pdata;

     struct i2c_algo_bit_data *bit_data;

     struct i2c_adapter *adap;

     int ret;

 

     pdata = pdev->dev.platform_data;

     if (!pdata)

         return -ENXIO;

 

     ret = -ENOMEM;

     adap = kzalloc(sizeof(struct i2c_adapter), GFP_KERNEL);

     if (!adap)

         goto err_alloc_adap;

     bit_data = kzalloc(sizeof(struct i2c_algo_bit_data), GFP_KERNEL);

     if (!bit_data)

         goto err_alloc_bit_data;

 

     ret = gpio_request(pdata->sda_pin, "sda");

     if (ret)

         goto err_request_sda;

     ret = gpio_request(pdata->scl_pin, "scl");

     if (ret)

         goto err_request_scl;

……………..

 

     adap->owner = THIS_MODULE;

     snprintf(adap->name, sizeof(adap->name), "i2c-gpio%d", pdev->id);

     adap->algo_data = bit_data;

     adap->dev.parent = &pdev->dev;

 

     ret = i2c_bit_add_bus(adap);

     if (ret)

         goto err_add_bus;

………………….

}

这个函数主要做了三件事,首先构造了一个i2c_adapter结构体,然后申请gpio资源,最后进行了一个关键调用i2c_bit_add_bus,这是一个在drivers/i2c/algos/i2c-algo-bit.c中定义的函数,猜测就是通过这样的一个调用,将adapter和algorithm结合起来!

3       algorithm驱动
i2c中的algorithm实现都放在drivers/i2c/algos目录下,在此采用i2c-algo-bit.c文件。前面说道在adapter初始化时会调用i2c_bit_add_bus,姑且先看看这个函数:

 

int i2c_bit_add_bus(struct i2c_adapter *adap)

{

     int err;

 

     err = i2c_bit_prepare_bus(adap);

     if (err)

         return err;

 

     return i2c_add_adapter(adap);

}

第一个函数调用i2c_bit_prepare_bus由alorithm实现:

static const struct i2c_algorithm i2c_bit_algo = {

     .master_xfer  = bit_xfer,

     .functionality     = bit_func,

};

 

/*

 * registering functions to load algorithms at runtime

 */

static int i2c_bit_prepare_bus(struct i2c_adapter *adap)

{

     struct i2c_algo_bit_data *bit_adap = adap->algo_data;

 

     if (bit_test) {

         int ret = test_bus(bit_adap, adap->name);

         if (ret<0)

              return -ENODEV;

     }

 

     /* register new adapter to i2c module... */

     adap->algo = &i2c_bit_algo;

 

     adap->timeout = 100;   /* default values, should   */

     adap->retries = 3; /* be replaced by defines   */

 

     return 0;

}

也就是说,在这里algorithm通过i2c_bit_algo这一结构体给adapter提供了操作接口。在配置完接口后,紧接着调用i2c_add_adapter函数,由此进入到Driver driver的势力范围。

4       Driver driver
Driver driver的实现由drivers/i2c/i2c-core.c完成,它需要管理i2c的多种资源。

4.1    adapter注册
当adapter初始化的时候,它需要调用i2c_add_adapter函数注册自己:

/**

 * i2c_add_adapter - declare i2c adapter, use dynamic bus number

 * @adapter: the adapter to add

 *

 * This routine is used to declare an I2C adapter when its bus number

 * doesn't matter.  Examples: for I2C adapters dynamically added by

 * USB links or PCI plugin cards.

 *

 * When this returns zero, a new bus number was allocated and stored

 * in adap->nr, and the specified adapter became available for clients.

 * Otherwise, a negative errno value is returned.

 */

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_lists);

     /* "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_lists);

 

     if (res < 0) {

         if (res == -EAGAIN)

              goto retry;

         return res;

     }

 

     adapter->nr = id;

     return i2c_register_adapter(adapter);

}

很简单地分配一个ID,然后调用i2c_register_adapter进行实际的注册:

static int i2c_register_adapter(struct i2c_adapter *adap)

{

     int res = 0;

     struct list_head   *item;

     struct i2c_driver  *driver;

……………..

     list_add_tail(&adap->list, &adapters);

……………..

 

     /* let legacy drivers scan this bus for matching devices */

     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);

     }

………………….

}

两件主要的事情,先将传入的adapter指针放到adapters这一全局链表中,然后针对所有已经注册的i2c_driver调用其attach_adapter回调函数。

4.2    i2c_driver注册
对于每一个连接在I2C总线上的设备,都必须提供一个i2c_driver并调用i2c_add_driver进行注册:

static inline int i2c_add_driver(struct i2c_driver *driver)

{

     return i2c_register_driver(THIS_MODULE, driver);

}

很简单,直接调用i2c_register_driver:

/*

 * An i2c_driver is used with one or more i2c_client (device) nodes to access

 * i2c slave chips, on a bus instance associated with some i2c_adapter.  There

 * are two models for binding the driver to its device:  "new style" drivers

 * follow the standard Linux driver model and just respond to probe() calls

 * issued if the driver core sees they match(); "legacy" drivers create device

 * nodes themselves.

 */

 

int i2c_register_driver(struct module *owner, struct i2c_driver *driver)

{

     int res;

………………

     list_add_tail(&driver->list,&drivers);

     pr_debug("i2c-core: driver [%s] registered/n", driver->driver.name);

 

     /* legacy drivers scan i2c busses directly */

     if (driver->attach_adapter) {

         struct i2c_adapter *adapter;

 

         list_for_each_entry(adapter, &adapters, list) {

              driver->attach_adapter(adapter);

         }

     }

…………………

}

两件事,先将i2c_driver放到drivers的链表中,然后调用i2c_driver中的attach_adapter回调函数。

 

 

5       client driver
5.1    client driver注册
对于I2C总线上的每一个设备,都必须提供一个单独的驱动,比如blackfin_cam.c中的驱动:

static __init int bcap_init(void)

{

     int err;

…………..

 

     err = i2c_add_driver(&sensor_driver);

     if (err) {

         printk(KERN_WARNING "%s: could not add i2c driver: %i/n",

                sensor_name, err);

         return err;

     }

……………..

}

这里sensor_driver定义为:

static struct i2c_driver sensor_driver = {

     .driver = {

            .name = SENSOR_NAME,

            },

     .id = I2C_DRIVERID_BCAP,

     .attach_adapter = sensor_attach_adapter,

     .detach_client = sensor_detach_client,

     .command = sensor_command,

};

也就是说,每一个i2c设备都必须提供一个i2c_driver,然后向I2C管理模块注册。

 

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/lights_joy/archive/2009/07/03/4320575.aspx

抱歉!评论已关闭.