聊聊i2c框架

1,聊聊i2c
2,那就开始吧
3,其实我不想贴代码
    3.1板级支持包
    3.2 内核注册i2c设备
    3.3 去找driver吧


1,聊聊i2c

    以前一直觉得linux i2c好复杂,
    做android驱动开发的时候,用的最多的就是i2c了,各种sensor都是i2c接口。在mtk平台上调试一个sensor,发现i2c最多发送7个字节,超过7个字节就出错。项目紧加上没看过i2c平台的源码,解不出来。于是找mtk帮助,然后mtk的开发人员,自己封装了一个使用dma的i2c api接口给我,然后就通信成功。当时感叹很多,,,
    于是下决心把i2c的源码框架看一遍,但是都中途放弃了,主要原因是看不懂,,,终于在看了6、7次后的一天半夜,把代码马马虎虎的看了一遍,然后过了几天去一个公司面试的时候,面试官说聊聊设备平台总线吧,于是我就聊起了i2c,然后面过了,哈哈

    总的意思就是i2c使用的这么频繁,既然是搞研发,怎么说也得把i2c框架了解一下吧。

2,那就开始吧
    i2c这个框架中有几个名词 adaptr, algorithm, client
    1. adapter : 这个对应一个i2c适配器,就是你的三星、mtk、高通啦这些soc里的i2c硬件模块
    2. algorithm: 算法的意思,有点高大上的感觉。接地气的说法就是对应控制i2c发送start end等信号的东西,就当是i2c硬件模块中的控制模块
    3. client:对应外设i2c器件
以上的说法比较俗,也不是非常的完善,暂时先有个大体的框架

我们再来说说这几个名词的对应关系吧
    adapter这个肯定是独一无二的了,那么algorithm也是独一无二的
adapter <---> algorithm
    这个client,外设吗!外设肯定不止一个了,一个i2c线上可以挂好多设备啊
adapter <---> 多个client

algorithm <---> 多个client

聊聊i2c框架_第1张图片

3,其实我不想贴代码
    说来说去,还是贴代码来的的实在,一是说服力强,二是占空间!!!
3.1板级支持包

    板级支持包里,一般都会有这样的一段代码(请原谅我还没有使用dts...) 

static struct i2c_board_info i2c0_boardinfo[] = {
	{
		//I2C_BOARD_INFO("tlv320aic3x", 0x18),
		I2C_BOARD_INFO("wm8523", 0x1a),
	}
}
对就是这个i2c_board_info
type = "wm8523" //i2c外设的名字
addr = 0x1a     //i2c外设的地址(7位地址)

而对于这个结构体,我们使用下面的函数来注册
i2c_registr_board_info(0, i2c_boardinfo, 1);

这个意思是我要把这个"wm8523"i2c设备挂在i2c0上

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

	down_write(&__i2c_board_lock);

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

	up_write(&__i2c_board_lock);

	return status;
}
这个函数做了啥?就是把外设的信息,外设要挂载的信息通过一个链表挂在了
__i2c_board_list这个链表上,然后就完了,对完了。。。

和i2c还没产生啥关系啊。。。

3.2 内核注册i2c设备
既然要使用i2c,那么就去创建i2c的框架吧

删删删 终于把代码删减到现在这么多了

static int __devinit
omap_i2c_probe(struct platform_device *pdev)
{
    /* dev的类似和平台厂商有关,不用过多纠结 */
    struct omap_i2c_dev    *dev;
    /* 这个就是adapter了,实例化的i2c硬件模块 */
    struct i2c_adapter *adap;
    ...
    dev = kzalloc(sizeof(struct omap_i2c_dev), GFP_KERNEL);
    ...
    /* 初始化i2c的设备 不关心 */
    omap_i2c_init(dev);
    ...
    /* 申请i2c的中断 不关心 */
    r = request_irq(dev->irq, isr, IRQF_NO_SUSPEND, pdev->name, dev);
    ...
    adap = &dev->adapter;
    ...
 /* 这个就是algorithm了,发送、结束、应答全靠它! */
    adap->algo = &omap_i2c_algo;
    ...
    /* 增加一个有编号的i2c适配器,
     * 可以理解 soc还有好几个i2c呢
     * i2c0,i2c1,i2c2.... i2cX
     */
    adap->nr = pdev->id;
    r = i2c_add_numbered_adapter(adap);
...
}

看看i2c_add_numbered_adapter,再删删

int i2c_add_numbered_adapter(struct i2c_adapter *adap)
{
        status = i2c_register_adapter(adap);
}
static int i2c_register_adapter(struct i2c_adapter *adap)
{
 ...
    dev_set_name(&adap->dev, "i2c-%d", adap->nr);
 
    /* 这里成功注册一个 /sys/bus/platform/devices/xxxxx/i2c-1 */
    res = device_register(&adap->dev);
    ...
    /* create pre-declared device nodes */
    /* 上面板级支持包里挂在在__i2c_board_list上信息要起作用了 */
    if (adap->nr < __i2c_first_dynamic_bus_num)
        i2c_scan_static_board_info(adap);
    ...	
    /* Notify drivers */
    /* 这里主要是找driver */
    bus_for_each_drv(&i2c_bus_type, NULL, adap, __process_new_adapter);
    ...
}

ok 我们看i2c_scan_static_board_info

static void i2c_scan_static_board_info(struct i2c_adapter *adapter)
{
	struct i2c_devinfo	*devinfo;

	down_read(&__i2c_board_lock);
	list_for_each_entry(devinfo, &__i2c_board_list, list) {
		if (devinfo->busnum == adapter->nr
				&& !i2c_new_device(adapter,
						&devinfo->board_info))
			dev_err(&adapter->dev,
				"Can't create device at 0x%02x\n",
				devinfo->board_info.addr);
	}
	up_read(&__i2c_board_lock);
}

这里当注册i2c0的适配器adapter的时候,会到__i2c_board_list链表找到要挂在i2c0的外设"wm8523" 。

哎呀,好巧,那么就去i2c_new_device吧

这个i2c_new_device就会产生我们经常说的device和driver匹配的时候会调用probe函数,对就是那个device.

struct i2c_client *
i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info)
{
    struct i2c_client  *client;
    int         status;
    /* 其实这个就是我们在驱动里面操作的client, 这里早早的就出现了 */ 
    client = kzalloc(sizeof *client, GFP_KERNEL);
    if (!client)
        return NULL;
    /* 找到adapter 发信息离不开它 */
    client->adapter = adap;
    ...
    client->flags = info->flags;
    /* 外设自己的地址自己记住,只有自己知道 */
    client->addr = info->addr;
    client->irq = info->irq;
    /* client->name = "wm8523" */
    strlcpy(client->name, info->type, sizeof(client->name));
 
    /* Check for address validity */
    /* 检测地址是否有效 */
    status = i2c_check_client_addr_validity(client);
    /* Check for address business */
    /* 检测地址是否和这个i2c上的其他器件冲突 */
    status = i2c_check_addr_busy(adap, client->addr);
    /* 做人不能忘本,一定要记住自己挂在哪条总线
     * 因为device找到driver后的probe还要靠总线呢
     */
     client->dev.bus = &i2c_bus_type;
     client->dev.type = &i2c_client_type;

    /* For 10-bit clients, add an arbitrary offset to avoid collisions */
    /* 这里把dev的name命名成了 "0-001a",为什么不是"wm8523" 
     * 这样driver岂不是通过名字找不到device了???
     * 注意看上面红色的行
     * client->name = "wm8523"
     */
    dev_set_name(&client->dev, "%d-%04x", i2c_adapter_id(adap),
             client->addr | ((client->flags & I2C_CLIENT_TEN)
                     ? 0xa000 : 0));
    /* dev带着"0-001a"而不是"wm8523"的名字遗憾的去注册了 */
    status = device_register(&client->dev);
    if (status)
        goto out_err;
    /* return也没人用啊
     * client在这里了等待
     * 等到有设备找到带着"0-001a"名字注册的dev的时候,
     * 就是client再现江湖的时候
     */
    return client;
 
out_err:
    kfree(client);
    return NULL;
}
这里整理一下client有什么?
1. adapter有了,简介的algorithm简介也有了,数据传输不愁了
2. addr地址有了,发送数据到哪里确定了
3. dev有了,client再现江湖不远了
还差啥啊,driver

没有driver谁去操作client啊,所以去找driver吧

3.3 去找driver吧

static const struct i2c_device_id wm8523_i2c_id[] = {
	{ "wm8523", 0 },
	{ }
};
MODULE_DEVICE_TABLE(i2c, wm8523_i2c_id);

static struct i2c_driver wm8523_i2c_driver = {
	.driver = {
		.name = "wm8523-codec",
		.owner = THIS_MODULE,
		.of_match_table = wm8523_of_match,
	},
	.probe =    wm8523_i2c_probe,
	.remove =   __devexit_p(wm8523_i2c_remove),
	.id_table = wm8523_i2c_id,
};
#endif

static int __init wm8523_modinit(void)
{
	int ret;
#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
	ret = i2c_add_driver(&wm8523_i2c_driver);
	if (ret != 0) {
		printk(KERN_ERR "Failed to register WM8523 I2C driver: %d\n",
		       ret);
	}
#endif
	return 0;
}
代码是贴了但是probe怎么开始执行的?
#define i2c_add_driver(driver) \  
	i2c_register_driver(THIS_MODULE, driver)  

int i2c_register_driver(struct module *owner, struct i2c_driver *driver)
{
	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;

	/* When registration returns, the driver core
	 * will have called probe() for all matching-but-unbound devices.
	 */
	res = driver_register(&driver->driver);
	if (res)
		return res;

	...

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

	INIT_LIST_HEAD(&driver->clients);
	/* Walk the adapters that are already present */
	i2c_for_each_dev(driver, __process_new_driver);

	return 0;
}
记住不能忘本,不能忘记自己挂在i2c_bus_type上
简述一个driver找device的步骤
driver_register
    -> bus_add_driver
        -> driver_attach
            -> __driver_attach
                -> driver_match_device

                    -> i2c_bus_type.match <--> i2c_device_match

static int i2c_device_match(struct device *dev, struct device_driver *drv)
{
	struct i2c_client	*client = i2c_verify_client(dev);
	struct i2c_driver	*driver;

	if (!client)
		return 0;

	/* Attempt an OF style match */
	if (of_driver_match_device(dev, drv))
		return 1;

	driver = to_i2c_driver(drv);
	/* match on an id table if there is one */
	if (driver->id_table)
		return i2c_match_id(driver->id_table, client) != NULL;

	return 0;
}
看到没那个带着"0-001a"名字注册的dev找到了
i2c_verify_client(dev) 让client重出江湖,这样就client->name "vm8523"和driver->id_table中的名字匹配上了
接上面
__driver_attach
    -> driver_probe_device
        -> really_probe
            -> dev->bus->probe <--> i2c_bus_type.probe <--> i2c_device_probe
                -> driver->probe <--> wm8523_i2c_probe

在往后就算正常的设备操作了
就聊到这里吧!

你可能感兴趣的:(linux)