展讯智能机平台代码 i2c设备驱动解读(下)

来源:互联网 发布:windows10网络驱动下载 编辑:程序博客网 时间:2024/06/01 09:11
 
上回我们说到,I2C设备是在板级代码中通过i2c_register_board_info来注册(其实我觉得理解成声明似乎更易解释)的。下面,咱们来了解这个函数:

/**

 * i2c_register_board_info - statically declare I2C devices

 * @busnum: identifies the bus to which these devices belong

 * @info: vector of i2c device descriptors

 * @len: how many descriptors in the vector; may be zero to reserve

 *     the specified bus number.

 *

 * Systems using the Linux I2C driver stack can declare tables of board info

 * while they initialize.  This should be done in board-specific init code

 * near arch_initcall() time, or equivalent, before any I2C adapter driver is

 * registered.  For example, mainboard init code could define several devices,

 * as could the init code for each daughtercard in a board stack.

 *

 * The I2C devices will be created later, after the adapter for the relevant

 * bus has been registered.  After that moment, standard driver model tools

 * are used to bind "new style" I2C drivers to the devices.  The bus number

 * for any device declared using this routine is not available for dynamic

 * allocation.

 *

 * The board info passed can safely be __initdata, but be careful of embedded

 * pointers (for platform_data, functions, etc) since that won't be copied.

 */

int __init i2c_register_board_info(int busnum,struct i2c_board_info const *info, unsigned len)

{

         int status;

 

         down_write(&__i2c_board_lock);//锁写操作,在解锁前devinfo不能再被写操作

 

         /* dynamic bus numbers will be assigned after the last static one */

         if (busnum >= __i2c_first_dynamic_bus_num)

                   __i2c_first_dynamic_bus_num = busnum + 1;

 

         for (status = 0; len; len--, info++) {

                   struct i2c_devinfo    *devinfo;

 

                   devinfo = kzalloc(sizeof(*devinfo), GFP_KERNEL);

                   if (!devinfo) {

                            pr_debug("i2c-core: can't register boardinfo!\n");

                            status = -ENOMEM;

                            break;

                   }

 

                   devinfo->busnum = busnum;

                   devinfo->board_info = *info;

         list_add_tail(&devinfo->list, &__i2c_board_list); //加入到__i2c_board_list链表中(尾部)

         }

 

         up_write(&__i2c_board_lock);//解除写操作的锁

 

         return status;

}

由上述代码看出,i2c_register_board_info的作用就是将总线号及设备信息加入到__i2c_board_list链表中。这就完了吗,这就是设备注册了吗?说实话,我也没看明白。看看注释吧,内核解释:i2c_register_board_info,静态声明I2C设备。I2C设备信息在板级代码初始化期间,也就是arch_initcal()这个时间点附件,进行注册(声明),这个注册须在I2C适配器驱动注册前完成!

(但在此时,实际上I2C设备并未真正存在,仅仅是设备信息被声明了),而在I2C适配器驱动注册完成后,I2C device设备才真正被注册了。在这之后,如果要加入新的I2C设备,就需要新的方式来绑定I2C设备和驱动了。

 

走到这,你也许要问了,如果我们不使用在板级代码中注册这种方式,那么我们该如何操作呢?i2c_new_device这个函数闪耀登场了。

在gslX680_ts.c这个TP驱动文件中,有对i2c_new_device函数做了很好的调用示范。且使用了I2C_BOARD_INFO_METHOD这个宏来让你决定是使用在BOARD中注册还是使用i2c_new_device这种方式。从gslX680_ts_init函数调用中我们也了解到,i2c_register_board_info与i2c_new_device这两种方式的区别:i2c_register_board_info的形参需要的是总线号,i2c_new_device的形参需要的直接是适配器的指针。说了这么多,如果没有代码,似乎没有说服力,那么咱们就把代码贴出来吧:

 

static int __init gslX680_ts_init(void)

{

         int gslX680_irq;

 

         LDO_SetVoltLevel(LDO_LDO_SIM2, LDO_VOLT_LEVEL0);

         LDO_TurnOnLDO(LDO_LDO_SIM2);

 

//      sc8810_i2c_set_clk(2,400000);//add by hzy for debug gsx680,20121220

         

         printk("%s\n", __func__);

         

         gslX680_irq=gslX680_ts_config_pins();

         gslX680_ts_setup.i2c_bus = 0;

         gslX680_ts_setup.i2c_address = GSLX680_TS_ADDR;

         strcpy (gslX680_ts_setup.type,GSLX680_TS_NAME);

         gslX680_ts_setup.irq = gslX680_irq;

         return sprd_add_i2c_device(&gslX680_ts_setup, &gslX680_ts_driver);

}

 

gslX680_ts_init为模块初始化函数,通过module_init(gslX680_ts_init)被系统调用。在该函数中,我们可以看到I2C设备名及设备地址,中断号等关键信息被直接定义,并最后被sprd_add_i2c_device这个函数调用来注册I2C设备。sprd_add_i2c_device函数内容不少,咱们挑关键的进行注释,你一看就明白了:

 

 

int sprd_add_i2c_device(struct sprd_i2c_setup_data *i2c_set_data, struct i2c_driver *driver)

{

         struct i2c_board_info info;

         struct i2c_adapter *adapter;

         struct i2c_client *client;

         int ret,err;

 

 

         printk("%s : i2c_bus=%d; slave_address=0x%x; i2c_name=%s",__func__,i2c_set_data->i2c_bus, \

                       i2c_set_data->i2c_address, i2c_set_data->type);

 

         memset(&info, 0, sizeof(struct i2c_board_info));// i2c_board_info这个结构体又出现了

         info.addr = i2c_set_data->i2c_address;//传入I2C设备地址

         strlcpy(info.type, i2c_set_data->type, I2C_NAME_SIZE);//I2C设备名也有了

         if(i2c_set_data->irq > 0)

                   info.irq = i2c_set_data->irq;

 

         adapter = i2c_get_adapter( i2c_set_data->i2c_bus);//通过总线获取到对应的适配器

         if (!adapter) {

                   printk("%s: can't get i2c adapter %d\n",

                            __func__,  i2c_set_data->i2c_bus);

                   err = -ENODEV;

                   goto err_driver;

         }

 

         client = i2c_new_device(adapter, &info);//根据适配器及i2c_board_info注册设备

         if (!client) {

                   printk("%s:  can't add i2c device at 0x%x\n",

                            __func__, (unsigned int)info.addr);

                   err = -ENODEV;

                   goto err_driver;

         }

 

         i2c_put_adapter(adapter);

 

         ret = i2c_add_driver(driver);//加入设备驱动

         if (ret != 0) {

                   printk("%s: can't add i2c driver\n", __func__);

                   err = -ENODEV;

                   goto err_driver;

         }        

 

         return 0;

 

err_driver:

         return err;

}

 

看了上面的代码,你就明白其实i2c_register_board_info与i2c_new_device都差不多,都用到了i2c_board_info。只是i2c_register_board_info用在适配器建立前,i2c_new_device用再适配器建立后。

 

 

I2C driver

再次简单总结下I2C 设备的驱动,下面是引用一位技术牛人的一段话:

I2C设备驱动。Linux内核给出的接口只有两个,一个是注册,另一个就是卸载。我们已经分析过module_i2c_driver这个宏定义,因为有它的存在,I2C设备驱动的开发可以不用在意你的I2C驱动需要如何注册以及如何卸载的,全部的精力都放在i2c_driver的完善上就可以了。

 

通过最开始的表单能明显察觉到,I2C子系统中I2C driver的开放接口最少,说白了就是需要驱动编写者完成完了i2c_driver放入module_i2c_driver宏中即可,而正因为如此,也恰恰说明,i2c_driver的灵活性是最高的。通常驱动会首先在意在用户空间的打开、关闭、读写等接口,但是对于i2c_driver来说,这些工作是I2C子系统已经做好的,关于常用的读写最终也是通过adapter实现的i2c_algorithm达到目的。好吧,再次说明了I2C子系统的完善程度,对于I2C设备及驱动开发来说是极其方便的。那么I2C驱动要实现什么呢?

 

为了让大家能够看得清楚,我们不断地重复、重复再重复这段代码,务求让大家能够重复、重复再重复地看个仔细。好了,再次回顾一下i2c_driver结构体:

static struct i2c_driver gslX680_ts_driver = {

         .probe                = gslX680_ts_probe,

         .remove            = gslX680_ts_remove,

         .id_table  = gslX680_ts_id,

         .driver      = {

                   .name       = GSLX680_NAME,

                   .owner     = THIS_MODULE,

         },

};

 

 

说完了driver,似乎我们应该再谈一谈i2c_client与i2c_board_info的关系,    对应关系在i2c_new_device中有完整体现。

 

i2c_client->dev.platform_data = i2c_board_info->platform_data;

i2c_client->dev.archdata = i2c_board_info->archdata;

i2c_client->flags = i2c_board_info->flags;

i2c_client->addr = i2c_board_info->addr;

i2c_client->irq = i2c_board_info->irq;

 

而i2c_new_device则被i2c_scan_static_board_info所调用。至于i2c_scan_static_board_info函数,则是被i2c_register_adapter适配器注册函数所调用。调用的条件如下:

if (adap->nr < __i2c_first_dynamic_bus_num)

                   i2c_scan_static_board_info(adap);

 

如果adapter的总线号小于动态分配的总线号的最小那个,说明是板级adapter。因为通过i2c_add_adapter加入的适配器所分配的总线号一定是比__i2c_first_dynamic_bus_num大的。这也从中说明了,通过板级代码注册设备,必须要先于adapter适配器注册,否则这个条件调用就没作用了。
 


1 0
原创粉丝点击