I2C子系统分析

文章详细阐述了Linux内核中I2C总线的注册过程,包括i2c_bus_type的初始化,i2c_adapter和i2c_client的生成。通过设备树配置,i2c子设备被转换为i2c_client并注册。同时,介绍了i2c_driver的生成过程,以及如何通过i2c_add_driver进行驱动注册和匹配。整个流程涉及到了平台设备、设备树、驱动匹配和设备注册等多个层面。
摘要由CSDN通过智能技术生成

一、i2c总线(i2c_bus_type)注册过程

路径:drivers\i2c\i2c-core.c

struct bus_type i2c_bus_type = {
	.name		= "i2c",
	.match		= i2c_device_match,
	.probe		= i2c_device_probe,
	.remove		= i2c_device_remove,
	.shutdown	= i2c_device_shutdown,
};

postcore_initcall(i2c_init);	//kernel启动自动运行 i2c_init() 函数

static int __init i2c_init(void)
{
   /* ... */

	retval = bus_register(&i2c_bus_type);	//注册i2c总线bus
	/* ... */
}

二、i2c子系统框架分析

1、i2c_adapter、i2c_client 生成过程

整体过程:

        设备树/i2c节点一般表示i2c控制器,它会被转换为platform_device,在内核中有对应的platform_driver,一般为厂商所配套的platform_driver文件。platform_driver的probe函数中会调用i2c_add_numbered_adapter,注册I2C适配器,然后将I2C子设备都转换为i2c_client。之后会将 i2c_client 和 所有i2c_driver做匹配, 如果匹配就调用 i2c_driver的 .probe 函数。.probe 函数就是注册字符设备的那一套流程了。

  • i2c_adapter:dts中每个i2c节点对应一个i2c适配器(芯片厂商一般已经做好了)
  • i2c_client:dts中i2c节点下的子设备(我们通过配置设备树添加i2c_client)
  • i2c_driver:i2c的设备驱动,与i2c_client 的 compatible 需匹配上(由我们自己编写)

函数执行流程:

设备树i2c节点生成platform_device和厂商提供的platform_driver匹配后,调用probe函数,会注册i2c_adapter。

i2c_imx_probe
    i2c_add_numbered_adapter /* 添加I2C控制器 */
        __i2c_add_numbered_adapter
            i2c_register_adapter /* 注册I2C控制器*/
                device_register /* 注册 I2C控制器adapter,如 I2C-0  */
                of_i2c_register_devices /* 查找注册设备树控制器下面的子设备节点 */
                    of_i2c_register_device /*解析注册设备树i2c子节点属性*/
                        i2c_new_device	/* 设备树中的i2c子节点被转换为i2c_client */
                            client->dev.bus = &i2c_bus_type;
                            device_register /* 添加设备I2C从设备 */
                                device_add()
                i2c_scan_static_board_info /* 查找静态表,有些I2C设备是在代码中写死的,不是通过设备树的形式 */
                    i2c_new_device
                        client->dev.bus = &i2c_bus_type;
                        device_register /* 添加设备I2C从设备 */
  	                      device_add()

i2c_new_device 注册i2c_client(放入i2c_bus_type的dev链表), 这会和i2c_driver的id_table比较,如果匹配,调用i2c_driver的.probe。

2、i2c_driver 生成过程

i2c_driver侧一般由我们自己编写,i2c_add_driver()调用driver_register()和 i2c_for_each_dev(driver, __process_new_driver)。

  • driver_register():注册i2c_driver(添加到i2c_bus_type的drv链表),将i2c_bus_type的dev链表取出每一项i2c_client做匹配,匹配上则调用.probe。
  • i2c_for_each_dev(driver, __process_new_driver):对于每一个适配器,调用它的函数确定address_list里的设备是否存在,如果存在,再调用i2c_driver的.detect函数进一步确定、设置,然后i2c_new_device()注册i2c_client。

i2c的发送函数在i2c_adapter中,下图很好的解释了它们的关系。

三、i2c子系统源码分析

以 imx6ull 为例

设备树文件

i2c1: i2c@021a0000 { 
    #address-cells = <1>;

    #size-cells = <0>;

    compatible = "fsl,imx6ul-i2c", "fsl,imx21-i2c";
    reg = <0x021a0000 0x4000>; 
    interrupts = <GIC_SPI 36 IRQ_TYPE_LEVEL_HIGH>; 
    clocks = <&clks IMX6UL_CLK_I2C1>; 
    status = "disabled"; 
};

&i2c1 { 
    clock-frequency = <100000>; 
    pinctrl-names = "default"; 
    pinctrl-0 = <&pinctrl_i2c1>; 
    status = "okay"; 

    mag3110@0e { 
    compatible = "fsl,mag3110"; 
    reg = <0x0e>;

    };

};

Linux内核I2C的platform_driver代码(drivers\i2c\busses\i2c-imx.c)

static const struct of_device_id i2c_imx_dt_ids[] = { 
    { .compatible = "fsl,imx1-i2c", .data = &imx1_i2c_hwdata, },
    { .compatible = "fsl,imx21-i2c", .data = &imx21_i2c_hwdata, },
    { .compatible = "fsl,vf610-i2c", .data = &vf610_i2c_hwdata, },
    { /* sentinel */ } 
};

static struct platform_driver i2c_imx_driver = { 
    .probe = i2c_imx_probe,
    .remove = i2c_imx_remove,
    .driver = {
        .name = DRIVER_NAME,
        .owner = THIS_MODULE,
        .of_match_table = i2c_imx_dt_ids,
        .pm = IMX_I2C_PM,
    },
    .id_table = imx_i2c_devtype,
};

i2c1控制器转换为platform_device,它的 compatible 与 i2c_imx_driver 的 of_match_table 匹配,调用 .probe(i2c_imx_probe)。

i2c_imx_probe分析

static struct i2c_algorithm i2c_imx_algo = {
	.master_xfer	= i2c_imx_xfer,		//i2c发送函数
	.functionality	= i2c_imx_func,
};

static int i2c_imx_probe(struct platform_device *pdev) 
{ 
    /* 根据 platform_device 内容配置硬件i2c */
    /* ... */
    i2c_imx->adapter.algo = &i2c_imx_algo;  
    /* ... */
    ret = i2c_add_numbered_adapter(&i2c_imx->adapter);
    /* ... */
}

i2c_add_numbered_adapter

int i2c_add_numbered_adapter(struct i2c_adapter *adap) 
{ 
    /* i2c_add_adapter() 和 __i2c_add_numbered_adapter() 功能类似,
    	最终都是调用 i2c_register_adapter
    */
    if (adap->nr == -1) /* -1 means dynamically assign bus id */
    	return i2c_add_adapter(adap);

    return __i2c_add_numbered_adapter(adap);
}

__i2c_add_numbered_adapter

static int __i2c_add_numbered_adapter(struct i2c_adapter *adap) 
{ 
    int id; 
    mutex_lock(&core_lock);
    id = idr_alloc(&i2c_adapter_idr, adap, adap->nr, adap->nr + 1, 
    		GFP_KERNEL);
    mutex_unlock(&core_lock);

    if (id < 0)
    	return id == -ENOSPC ? -EBUSY : id;

    return i2c_register_adapter(adap);
}

i2c_register_adapter

static int i2c_register_adapter(struct i2c_adapter *adap) 
{ 
    /* ... */
    /* 注册I2C适配器adapter设备到 i2c_bus_type */
    /* 设置adap->dev.kobj.name 为 i2c-0,它将出现在sysfs */
    dev_set_name(&adap->dev, "i2c-%d", adap->nr);
    adap->dev.bus = &i2c_bus_type;		//绑定到i2c总线i2c_bus_type
    adap->dev.type = &i2c_adapter_type;	//adapter类型
    res = device_register(&adap->dev);	//注册i2c适配器
    /* ... */
    of_i2c_register_devices(adap);	/* 查找设备树控制器下面的子设备 */
    /* ... */

    /* 静态方式,循环必须 __i2c_board_list 中的设备,如果跟本适配器匹配。就注册 */
    if (adap->nr < __i2c_first_dynamic_bus_num)
    	i2c_scan_static_board_info(adap);
    /* ... */
}

of_i2c_register_devices

static void of_i2c_register_devices(struct i2c_adapter *adap) 
{ 
    /* adap->dev.of_node :i2c适配器节点
        node :每次for循环指向i2c适配器下的子节点
    */
    for_each_available_child_of_node(adap->dev.of_node, node) 
        of_i2c_register_device(adap, node);	/*解析设备树属性*/
} 

of_i2c_register_device函数,该函数主要用于解析i2c节点设备树内容。

static struct i2c_client *of_i2c_register_device(struct i2c_adapter *adap, 
    						struct device_node *node) 
{ 
    struct i2c_client *result; 
    struct i2c_board_info info = {};
    struct dev_archdata dev_ad = {};
    const __be32 *addr_be; 
    u32 addr; 
    int len;

    dev_dbg(&adap->dev, "of_i2c: register %s\n", node->full_name);

    //获取i2c节点中的compatible属性 拷贝到info.type中
    if (of_modalias_node(node, info.type, sizeof(info.type)) < 0)
    {
        dev_err(&adap->dev, "of_i2c: modalias failure on %s\n",
            	node->full_name);
        return ERR_PTR(-EINVAL);
    }

    //获取i2c节点中的reg属性(设备地址)
    addr_be = of_get_property(node, "reg", &len);
    if (!addr_be || (len < sizeof(*addr_be))) {
        dev_err(&adap->dev, "of_i2c: invalid reg on %s\n",
            	node->full_name);
        return ERR_PTR(-EINVAL);
    }

    //解析设备地址
    addr = be32_to_cpup(addr_be);
    if (addr & I2C_TEN_BIT_ADDRESS) {
        addr &= ~I2C_TEN_BIT_ADDRESS;
        info.flags |= I2C_CLIENT_TEN;
    }

    if (addr & I2C_OWN_SLAVE_ADDRESS) {
        addr &= ~I2C_OWN_SLAVE_ADDRESS;
        info.flags |= I2C_CLIENT_SLAVE;
    }

    //检查设备地址
    if (i2c_check_addr_validity(addr, info.flags)) {
    	dev_err(&adap->dev, "of_i2c: invalid addr=%x on %s\n",
    		addr, node->full_name);
    	return ERR_PTR(-EINVAL);
    }

    info.addr = addr;
    info.of_node = of_node_get(node); //将设备树节点存入info.of_node
    info.archdata = &dev_ad;

    if (of_get_property(node, "wakeup-source", NULL))
    	info.flags |= I2C_CLIENT_WAKE;

    //注册i2c子设备,生成i2c_client
    result = i2c_new_device(adap, &info);
    if (result == NULL) {
    	dev_err(&adap->dev, "of_i2c: Failure registering %s\n",
    		node->full_name);
    	of_node_put(node);
    	return ERR_PTR(-EINVAL);
    }
    return result;
}

i2c_new_device函数

struct i2c_client * 
i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info) 
{ 
    struct i2c_client *client; 

    /* 将i2c适配器子设备节点转换为client */
    client->adapter = adap; 	// 设定设备的适配器,之后会使用适配器的发送函数
    client->addr = info->addr; 	// 地址
    client->irq = info->irq; 	// 中断号

 	/* 设置 client->dev */
    client->dev.parent = &client->adapter->dev;
    client->dev.bus = &i2c_bus_type;	// 绑定总线
    client->dev.type = &i2c_client_type;	
    client->dev.of_node = info->of_node;
    client->dev.fwnode = info->fwnode;
 
    device_register(&client->dev); 	//向总线注册设备

    return client;
}

下面分析device_register(driver/base/core.c)的过程

int device_register(struct device *dev) 
{ 
    /*初始化dev,为了在sys下生成节点,会先初始化kobj和kset*/
    device_initialize(dev);
    return device_add(dev);
} 
int device_add(struct device *dev) 
{ 
    /* ... */
    bus_add_device(dev);
    /* ... */
    bus_probe_device(dev);
    /* ... */
}
int bus_add_device(struct device *dev) 
{ 
    /* 将设备添加到总线的设备链表中(bus->p->klist_devices) */
    klist_add_tail(&dev->p->knode_bus, &bus->p->klist_devices);
} 
void bus_probe_device(struct device *dev) 
{ 
    device_attach(dev);
} 
int device_attach(struct device *dev) 
{ 
    return __device_attach(dev, false);
}
int __device_attach(struct device *dev, bool allow_async)
{
    ret = bus_for_each_drv(dev->bus, NULL, &data,
					__device_attach_driver);    
}
int bus_for_each_drv(struct bus_type *bus, struct device_driver *start, 
    		void *data, int (*fn)(struct device_driver *, void *))

{ 
    /* 遍历总线的驱动链表上的所有驱动,调用fn函数 */
    while ((drv = next_driver(&i)) && !error) 
    	error = fn(drv, data);
}

这里的fn函数指的是__device_attach_driver函数

static inline int driver_match_device(struct device_driver *drv,
				      struct device *dev)
{
	return drv->bus->match ? drv->bus->match(dev, drv) : 1;
}

int __device_attach_driver(struct device_driver *drv, void *_data)
{
   ret = driver_match_device(drv, dev);	//通过i2c总线的match比较device和driver是否匹配
	if (ret == 0) {
		/* no match */
		return 0;
	} 

	return driver_probe_device(drv, dev);
}

可以看到匹配成功后,会调用driver_probe_device函数,下面看一看这个函数

int driver_probe_device(struct device_driver *drv, struct device *dev) 
{ 
    really_probe(dev, drv);
} 

static int really_probe(struct device *dev, struct device_driver *drv) 
{ 
    /* 调用了i2c总线的probe函数 */
    dev->bus->probe(dev);
} 

i2c总线的probe为 i2c_device_probe

struct bus_type i2c_bus_type = { 
    .name = "i2c", 
    .match = i2c_device_match, 
    .probe = i2c_device_probe, 
    .remove = i2c_device_remove, 
    .shutdown = i2c_device_shutdown,  
    .pm = &i2c_device_pm_ops, 
};

可以知道,总线的probe函数是i2c_device_probe

static int i2c_device_probe(struct device *dev) 
{ 
    /* 调用i2c_driver驱动的probe函数 */
    driver->probe(client, i2c_match_id(driver->id_table, client));
}

最终调用i2c_dirver的.probe函数。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值