可以在详细一点吗?最好加上伪代码示例
时间: 2023-11-03 17:04:17 浏览: 78
好的,下面是更详细的说明和伪代码示例。
1. 启动内核后,i2c-core 驱动被注册到系统中,并创建 i2c_bus_type 总线类型对象。
```c
/* i2c-core 驱动初始化 */
static int __init i2c_init(void)
{
/* 注册 i2c-bus 总线类型 */
i2c_bus_type = bus_register(&i2c_bus_type_ops);
if (!i2c_bus_type)
return -ENOMEM;
/* ... */
return 0;
}
/* i2c-bus 总线类型对象 */
static struct bus_type i2c_bus_type = {
.name = "i2c",
.dev_name = "i2c",
.match = i2c_device_match,
.uevent = i2c_device_uevent,
.suspend = i2c_device_suspend,
.resume = i2c_device_resume,
.pm = &i2c_device_pm,
};
/* i2c-bus 总线类型操作函数 */
static const struct bus_type_ops i2c_bus_type_ops = {
.bus_register = i2c_bus_register,
.bus_unregister = i2c_bus_unregister,
.device_add = i2c_device_add,
.device_remove = i2c_device_remove,
};
```
2. i2c-algo-* 驱动被注册到系统中,这些驱动封装了不同的 I2C 总线控制器,如 i2c-algo-bit、i2c-algo-pca 等。
```c
/* i2c-algo-bit 驱动初始化 */
static int __init i2c_algo_bit_init(void)
{
/* 注册 i2c-algo-bit 总线控制器 */
i2c_bit_algo_register(&i2c_bit_algo);
/* ... */
return 0;
}
/* i2c-algo-bit 总线控制器对象 */
static struct i2c_algorithm i2c_bit_algo = {
.name = "bit",
.id = I2C_ALGO_BIT,
.master_xfer = i2c_bit_xfer,
.functionality = i2c_bit_func,
};
/* i2c-algo-bit 总线控制器操作函数 */
static const struct i2c_algorithm i2c_bit_algo = {
.bit_xfer = i2c_bit_xfer,
.functionality = i2c_bit_func,
};
```
3. 在设备树中,定义了 I2C 总线控制器所使用的硬件资源,如 GPIO、时钟等。
```dts
&i2c1 {
clock-frequency = <400000>; /* I2C 时钟频率 */
pinctrl-names = "default";
pinctrl-0 = <&i2c1_pins>;
/* GPIO 硬件资源 */
scl-gpio = <&gpio0 1 GPIO_ACTIVE_HIGH>;
sda-gpio = <&gpio0 2 GPIO_ACTIVE_HIGH>;
/* I2C 设备节点 */
i2c-device1 {
compatible = "my-device1";
reg = <0x10>;
};
i2c-device2 {
compatible = "my-device2";
reg = <0x20>;
};
};
```
4. 当系统探测到 I2C 总线控制器时,会调用 i2c_add_numbered_adapter() 函数创建 i2c_adapter 对象,并将其挂载到 i2c_bus_type 总线类型对象上。
```c
/* 硬件资源定义 */
struct i2c_bit_data {
struct gpio_desc *sda_gpio;
struct gpio_desc *scl_gpio;
unsigned int scl_delay_us;
};
/* i2c-bit 总线控制器初始化 */
static int i2c_bit_add_bus(struct i2c_adapter *adapter)
{
struct i2c_bit_data *bit = i2c_bit_get_data(adapter);
int ret;
/* 创建 i2c_adapter 对象 */
adapter->algo_data = bit;
strlcpy(adapter->name, "i2c-algo-bit", sizeof(adapter->name));
adapter->owner = THIS_MODULE;
adapter->class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
/* 挂载到 i2c-bus 总线类型对象上 */
ret = bus_add_adapter(i2c_bus_type, adapter);
if (ret)
return ret;
/* ... */
return 0;
}
```
5. 然后,内核会扫描设备树,找到已经定义的 I2C 设备节点。
```c
/* I2C 设备节点初始化 */
static int __init i2c_device_init(void)
{
struct device_node *np;
int ret;
/* 遍历设备树 */
for_each_compatible_node(np, NULL, "my-device1") {
/* 创建 i2c_client 对象 */
struct i2c_client *client = i2c_new_device(&adapter, &info);
if (IS_ERR(client)) {
ret = PTR_ERR(client);
goto err;
}
/* ... */
}
/* ... */
return 0;
err:
/* ... */
}
```
6. 对于每个 I2C 设备节点,内核会解析其中的属性,并调用 i2c_new_device() 函数创建 i2c_client 对象,并将其挂载到 i2c_adapter 对象上。
```c
/* I2C 设备节点定义 */
static struct i2c_device_id my_device_id[] = {
{ "my-device1", 0 },
{ "my-device2", 0 },
{ }
};
/* I2C 设备信息 */
static struct i2c_board_info __initdata my_device_info[] = {
{
I2C_BOARD_INFO("my-device1", 0x10),
.platform_data = &my_device1_data,
},
{
I2C_BOARD_INFO("my-device2", 0x20),
.platform_data = &my_device2_data,
},
{ }
};
/* 创建 I2C 客户端对象 */
struct i2c_client *i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info)
{
/* 创建 i2c_client 对象 */
struct i2c_client *client = kzalloc(sizeof(*client), GFP_KERNEL);
if (!client)
return NULL;
/* 初始化 i2c_client 对象 */
client->adapter = adap;
client->addr = info->addr;
client->irq = info->irq;
client->dev.platform_data = info->platform_data;
/* 挂载到 i2c_adapter 对象上 */
i2c_attach_client(client);
/* ... */
return client;
}
```
总之,Linux 内核通过解析设备树中的节点信息,创建 i2c_client 对象,并将其挂载到 i2c_adapter 对象上,从而实现 I2C 总线的初始化和设备的挂载。以上是伪代码示例,可能并不完整或者存在一些语法错误,但是可以帮助你更好地理解 I2C 总线初始化的过程。
阅读全文