Linux I2C framework(4) -- I2C slave device

I2C设备的4种构建方法:

  • i2c_register_board_info
  • i2c_new_device 或i2c_new_probed_device
  • 用户空间直接调用
  • 查询所有adapter

1.静态注册设备( i2c_register_board_info)

相关结构体:

   8 struct i2c_devinfo {
   9     struct list_head    list;                                                                          
   10     int         busnum;
   11     struct i2c_board_info   board_info;
   12 };
   
   403 struct i2c_board_info {
   404     char        type[I2C_NAME_SIZE];
   405     unsigned short  flags;
   406     unsigned short  addr;
   407     const char  *dev_name;
   408     void        *platform_data;
   409     struct device_node *of_node;
   410     struct fwnode_handle *fwnode;
   411     const struct property_entry *properties;
   412     const struct resource *resources;
   413     unsigned int    num_resources;                                                                    
   414     int     irq;
   415 };

1).定义一个 i2c_board_info 结构体,有名字,和设备地址

static struct i2c_board_info  my_i2c_dev_info = {
	I2C_BOARD_INFO("my_i2c_dev", 0x2d),
};

2).注册设备

i2c_register_board_info(int busnum, struct i2c_board_info const * info, unsigned n)
* busnum:哪一条总线,也就是选择哪一个i2c控制器
* info:i2c设备信息
* n:info中有几个设备

函数i2c_register_board_info:静态声明i2c device

i2c_register_board_info(int busnum,	struct i2c_board_info const *info, unsigned len)
{
    devinfo->busnum = busnum; /* 选择i2c总线 */
    devinfo->board_info = *info; /* 绑定设备信息 */
    list_add_tail(&devinfo->list, &__i2c_board_list); /* 将设备信息添加进链表中 */
}

  将设备信息添加到链表__i2c_board_list之后,在哪里使用了 __i2c_board_list 这个链表?

i2c_scan_static_board_info(struct i2c_adapter *adapter)
    list_for_each_entry(devinfo, &__i2c_board_list, list) /* 遍历i2c设备链表 */
        i2c_new_device(adapter,	&devinfo->board_info); /* 创建一个i2c_client设备 */
            struct i2c_client	*client;

  哪里调用 i2c_scan_static_board_info?

  注册i2c adapter的时候,调用i2c_add_adapter或者i2c_add_numbered_adapter,该函数调用i2c_register_adapter,最终调用i2c_new_device创建i2c device。

int i2c_register_adapter(struct i2c_adapter *adap)
{
      if (adap->nr < __i2c_first_dynamic_bus_num)
	        i2c_scan_static_board_info(adap);
}

  具体调用关系如下:

 int i2c_register_adapter(struct i2c_adapter *adap)
        i2c_scan_static_board_info(adap)
            list_for_each_entry(devinfo, &__i2c_board_list, list)
               i2c_new_device    //实例化i2c device

  所以使用这个方法添加i2c设备,每次都要重新编译内核,无法动态注册i2c设备,不适合我们动态加载insmod

2.i2c_new_device 或 i2c_new_probed_device

  i2c_new_device: 认为设备肯定存在

i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info)
* adap:指定i2c设备器,以后访问设备的时候,使用过哪一个设备器(i2c主机控制器)去访问
* info:指定i2c设备信息

  i2c_new_probed_device:对于"已经识别出来的设备"(probed_device),才会创建(“new”)

struct i2c_client *i2c_new_probed_device(struct i2c_adapter *adap,
		      struct i2c_board_info *info,
		      unsigned short const *addr_list,
		      int (*probe)(struct i2c_adapter *, unsigned short addr))

* adap:设配器,设备在哪一条总线上
* info:设备的名字信息
* addr_list:设备的地址列表
* probe:会调用这个函数去判断设备是否真正存在,如果不提供此函数的话,有默认的函数

2.1.i2c_new_device 注册i2c设备代码

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/i2c.h>
#include <linux/err.h>
#include <linux/slab.h>

static struct i2c_board_info my_i2c_dev_info = {
    I2C_BOARD_INFO("my_i2c_dev", 0x50),     /* 这个名字和地址很重要 */
};

static struct i2c_client *my_i2c_client;

static int i2c_dev_init(void)
{
    struct i2c_adapter *i2c_adap;

    i2c_adap = i2c_get_adapter(0); /* 得到i2c设备器,i2c设备在哪条总线上 */
    my_i2c_client = i2c_new_device(i2c_adap, &my_i2c_dev_info);
    i2c_put_adapter(i2c_adap);

    return 0;
}

static void i2c_dev_exit(void)
{ 
    i2c_unregister_device(my_i2c_client);
}

module_init(i2c_dev_init);
module_exit(i2c_dev_exit);
MODULE_LICENSE("GPL");

2.2.i2c_new_probe_device注册i2c设备代码

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/i2c.h>
#include <linux/err.h>
#include <linux/regmap.h>
#include <linux/slab.h>

static struct i2c_client *my_i2c_client;

static const unsigned short addr_list[] = { 0x55, 0x50, I2C_CLIENT_END };

static int my_i2c_dev_init(void)
{
	struct i2c_adapter *i2c_adap;
	struct i2c_board_info my_i2c_dev_info;

	memset(&my_i2c_dev_info, 0, sizeof(struct i2c_board_info));	
	strlcpy(my_i2c_dev_info.type, "my_i2c_dev", I2C_NAME_SIZE);

	i2c_adap = i2c_get_adapter(0);
	my_i2c_client = i2c_new_probed_device(i2c_adap, &my_i2c_dev_info, addr_list, NULL);
	i2c_put_adapter(i2c_adap);

	if (my_i2c_client )
		return 0;
	else
		return -ENODEV;
}

static void my_i2c_dev_exit(void)
{
	i2c_unregister_device(my_i2c_client );
}

module_init(my_i2c_dev_init);
module_exit(my_i2c_dev_exit);
MODULE_LICENSE("GPL");

3.从用户空间创建设备

创建设备:(调用i2c_new_device)

echo my_i2c_dev 0x55 > sys/devices/platform/s3c2440-i2c.0/i2c-0/new_device

删除设备:(调用i2c_unregister_device)

echo 0x55 > sys/devices/platform/s3c2440-i2c.0/i2c-0/delete_device

4.查询所有adapter

  前面的3种方法都要事先确定适配器(I2C总线,I2C控制器);如果事先并不知道这个I2C设备在哪个适配器上,怎么办?去class表示的所有的适配器上查找有一些I2C设备的地址是一样,怎么继续分配它是哪一款?通过使用detect函数。

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/i2c.h>
#include <linux/err.h>
#include <linux/regmap.h>
#include <linux/slab.h>

static int __devinit my_i2c_drv_probe(struct i2c_client *client,
				  const struct i2c_device_id *id)
{
	printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
	return 0;
}

static int __devexit my_i2c_drv_remove(struct i2c_client *client)
{
	printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
	return 0;
}

static const struct i2c_device_id my_dev_id_table[] = {
	{ "my_i2c_dev", 0 },
	{}
};

static int my_i2c_drv_detect(struct i2c_client *client,
		       struct i2c_board_info *info)
{
	/* 能运行到这里, 表示该addr的设备是存在的
	 * 但是有些设备单凭地址无法分辨(A芯片的地址是0x50, B芯片的地址也是0x50)
	 * 还需要进一步读写I2C设备来分辨是哪款芯片
	 * detect就是用来进一步分辨这个芯片是哪一款,并且设置info->type
	 */
	
	printk("my_i2c_drv_detect: addr = 0x%x\n", client->addr);

	/* 进一步判断是哪一款 */
	
	strlcpy(info->type, "my_i2c_dev", I2C_NAME_SIZE);
	return 0;
}

static const unsigned short addr_list[] = { 0x60, 0x50, I2C_CLIENT_END };

/* 1. 分配/设置i2c_driver */
static struct i2c_driver my_i2c_driver = {
	.class  = I2C_CLASS_HWMON, /* 表示去哪些适配器上找设备 */
	.driver	= {
		.name	= "my_i2c_dev",
		.owner	= THIS_MODULE,
	},
	.probe		= my_i2c_drv_probe,
	.remove		= __devexit_p(my_i2c_drv_remove),
	.id_table	= my_dev_id_table,
	.detect     = my_i2c_drv_detect,  /* 用这个函数来检测设备确实存在 */
	.address_list	= addr_list,   /* 这些设备的地址 */
};

module_i2c_driver(my_i2c_drv_init);
MODULE_LICENSE("GPL");

  上面的代码回去.class指定的这一类i2c设配器中,查找.address_list这些地址,如果设备存在,就会调用.detect函数,确认是哪一个i2c设备,然后调用i2c_new_device。

函数调用流程如下:

i2c_add_driver
    i2c_register_driver
        driver_register(&driver->driver); /* 会去dev链表中找到匹配的设备,调用probe函数 */

        /* 对于每个设配器调用__process_new_driver */
        i2c_for_each_dev(driver, __process_new_driver);
            __process_new_driver
                i2c_do_add_adapter
                    i2c_detect
                        address_list = driver->address_list; /* 得到要检测的所有地址 */
                        i2c_detect_address
                        i2c_check_addr_validity /* 检测地址是否存在 */
                        driver->detect(temp_client, &info); /* 如果地址存在,调用detect */
                        i2c_new_device /* 创建设备 */
                        	device_add
                         		i2c_device_probe
                         			driver->probe
                         			of_irq_get  //获取irq,填充 client->irq = irq;

关于i2c slave device irq:

   323 static int i2c_device_probe(struct device *dev)
   324 {
   325     struct i2c_client   *client = i2c_verify_client(dev);
   326     struct i2c_driver   *driver;
   327
   334     if (!client->irq && !driver->disable_i2c_core_irq_mapping) {
   335         int irq = -ENOENT;
   336 
   337         if (client->flags & I2C_CLIENT_HOST_NOTIFY) {
   338             dev_dbg(dev, "Using Host Notify IRQ\n");
   339             irq = i2c_smbus_host_notify_to_irq(client);
   340         } else if (dev->of_node) {
   341             irq = of_irq_get_byname(dev->of_node, "irq");
   342             if (irq == -EINVAL || irq == -ENODATA) {
   343                 irq = of_irq_get(dev->of_node, 0);
   345             }
   346         } else if (ACPI_COMPANION(dev)) {
   347             irq = acpi_dev_gpio_irq_get(ACPI_COMPANION(dev), 0);
   348         }

  如上所示,dtsi定义s5m8767-pmic中断号,调用i2c_device_probe中的of_irq_get获取该中断号,添加到client->irq = irq;

arch/arm/boot/dts/exynos4412-itop-elite.dts:
227 &i2c_1 {
228     ...
236     s5m8767: s5m8767-pmic@66 {                                                                           
237         compatible = "samsung,s5m8767-pmic";
238         reg = <0x66>;
239 
240         interrupt-parent = <&gpx1>;
241         interrupts = <7 IRQ_TYPE_NONE>;
242 

获取该中断号:

drivers/mfd/sec-irq.c:
sec_pmic_probe
->sec_pmic->irq = i2c->irq;

参考:

  • Documentation/i2c/instantiating-devices
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值