单页淘宝客网站,dz可以做门户网站吗,公司网站版面怎么设计,学校网站建设开发商往期内容 I2C子系统专栏#xff1a; I2C#xff08;IIC#xff09;协议讲解-CSDN博客SMBus 协议详解-CSDN博客I2C相关结构体讲解:i2c_adapter、i2c_algorithm、i2c_msg-CSDN博客内核提供的通用I2C设备驱动I2c-dev.c分析#xff1a;注册篇内核提供的通用I2C设备驱动I2C-dev.…往期内容 I2C子系统专栏 I2CIIC协议讲解-CSDN博客SMBus 协议详解-CSDN博客I2C相关结构体讲解:i2c_adapter、i2c_algorithm、i2c_msg-CSDN博客内核提供的通用I2C设备驱动I2c-dev.c分析注册篇内核提供的通用I2C设备驱动I2C-dev.c分析file_ops篇设备驱动与设备树匹配机制详解编写一个通用的i2c设备驱动框架编写一个通用的i2c控制器驱动框架 总线和设备树专栏 总线和设备树_憧憬一下的博客-CSDN博客设备树与 Linux 内核设备驱动模型的整合-CSDN博客 前言
在上一章节(编写一个通用的i2c控制器驱动框架)讲了i2c_adapter。这里再继之前的文章 编写一个通用的i2c设备驱动框架进行补充讲一下l2c_cleint的设备形态等
1. 两种设备形态 可以看出是有两种你描述的这两种设备形态反映了设备通过不同的总线进行交互的差异并且在设备树DTS和驱动程序的编写上有不同的表现。根据设备的主要功能和数据传输方式它们在 Linux 设备模型中的位置不同从而影响了 DTS 文件中的结构和驱动程序的设计。我们来分析这两种设备形态。
形态 1: I2C-only 设备图左侧
特点这类设备和 CPU 之间的所有数据交互都是通过 I2C 总线完成的没有其他传输方式。DTS 描述设备直接作为 I2C 控制器的子节点存在。在 DTS 中设备的定义紧随其所在的 I2C 总线节点之后。例如 PMIC它是 I2C 总线上的一个设备因此直接在 I2C 节点的子节点中定义。
i2c1 {clock-frequency 100000;pinctrl-names default;pinctrl-0 pinctrl_i2c1;status okay;pmic: pf010008 {compatible fsl,pfuze100;reg 0x08;...};
};驱动模型I2C 核心负责处理设备的创建和注册。驱动程序是以 I2C slave 设备的方式来实现I2C 驱动框架通过 i2c_driver 结构和 i2c_device_id 来与设备匹配。设备的 probe、remove 等函数由 I2C 核心进行管理。
static const struct i2c_device_id pmic_i2c_ids[] {{ pfuze100, 0 },{ /* sentinel */ }
};static struct i2c_driver pmic_driver {.driver {.name pfuze100,.of_match_table pmic_of_match,},.probe pmic_probe,.remove pmic_remove,.id_table pmic_i2c_ids,
};I2C 是主数据交互总线设备完全依赖 I2C所有操作通过 I2C 完成。
驱动注册流程通过 I2C 子系统的 i2c_driver 完成设备由 I2C 核心管理。 形态 2: 复合设备I2C 作为辅助接口图右侧
特点这类设备通过多种方式与 CPU 交互例如主要的数据传输如音视频数据通过 TDMS 接口而配置信息或控制信息通过 I2CDDC接口。这类设备的核心功能决定了它们的主要位置在其他总线如平台总线 platform bus上而 I2C 只是作为一个辅助接口。DTS 描述HDMI 设备作为 platform device 定义并且通过 ddc-i2c-bus 属性引用 I2C 总线用于 DDCDisplay Data Channel接口的 I2C 通信。
hdmi {ddc-i2c-bus i2c2;status okay;
};驱动模型在这种情况下HDMI 控制器是主要设备注册为 platform_device。I2C 通信是 HDMI 控制器的一部分用来处理 EDID 等信息读取。在驱动程序中I2C 总线是通过 DTS 中的 ddc-i2c-bus 属性传递给 HDMI 驱动的驱动程序会通过 of_find_i2c_adapter_by_node() 获取并绑定 I2C 控制器。
struct i2c_adapter *ddc;
struct device_node *ddc_node;ddc_node of_parse_phandle(dev-of_node, ddc-i2c-bus, 0);
if (!ddc_node) {dev_err(dev, failed to find ddc-i2c-bus node\n);return -ENODEV;
}ddc of_find_i2c_adapter_by_node(ddc_node);
of_node_put(ddc_node);
if (!ddc)return -EPROBE_DEFER;/* Use ddc for I2C transactions */I2C 是辅助总线用于少量数据交互如 EDID、配置信息传输主要功能通过其他总线如 TDMS完成。
驱动以 platform_device 形式注册I2C 仅作为其中一个通信接口I2C 控制器通过设备树中的引用绑定。
2. 驱动编写
根据上面所说的形态去编写设备不同的i2c_driver
形态1: 完全依赖I2C的设备如PMIC
确定I2C adapter 根据设备硬件连接的实际情况确定该设备所从属的I2C controller即I2C adapter在I2C framework中I2C controller通常被称为I2C adapter。例如PMIC设备可能连接在 i2c1 总线上。在DTS中添加设备描述 在设备树的I2C adapter节点中添加I2C从设备的描述。这个从设备的定义格式与普通的platform device一致例如
i2c1 {pmic: pfuze10008 {compatible fsl,pfuze100;reg 0x08;// 其他字段...};
};compatible 字段的使用 DTS 中的 compatible 字段用于设备驱动的匹配和 probe。I2C驱动会根据 compatible 字段匹配到合适的驱动程序例如 compatible fsl,pfuze100;。编写I2C设备的驱动程序
示例
static const struct of_device_id pmic_of_match[] {{ .compatible fsl,pfuze100, },{ /* sentinel */ }
};static struct i2c_driver pmic_driver {.driver {.name pfuze100,.of_match_table pmic_of_match,},.probe pmic_probe,.remove pmic_remove,
};
static int __init i2c_driver_XXX_init(void)
{printk(%s %s %d\n, __FILE__, __FUNCTION__, __LINE__);return i2c_add_driver(i2c_XXX_driver);
}
module_init(i2c_driver_XXX_init);定义并注册 i2c_driver编写一个 struct i2c_driver 结构体并使用 i2c_add_driver将其注册到I2C core中。匹配设备在 i2c_driver 结构体中定义 of_match_table使其可以通过 compatible 字段进行匹配。实现 probe 回调编写 probe 函数用于设备初始化。
I2C core处理设备注册 当I2C adapter注册时I2C framework的核心代码会自动为其下的所有I2C从设备创建 struct i2c_client 结构体并根据设备树中的 compatible 字段匹配合适的 i2c_driver然后调用驱动程序的 probe 函数。在 probe 中 当设备被I2C framework匹配到时调用此函数进行设备初始化。主要负责获取设备资源如设备地址、I2C Adapter设置硬件寄存器或者初始化其他系统资源如注册字符设备。
static int ap3216c_probe(struct i2c_client *client, const struct i2c_device_id *id)
{// 打印日志确认 probe 触发printk(KERN_INFO ap3216c i2c device probe.\n);// 设置I2C client指针ap3216c_client client;// 执行设备初始化如设置初始寄存器值i2c_smbus_write_byte_data(client, 0x00, 0x03); // 设置某些寄存器// 注册字符设备或其他资源major register_chrdev(0, ap3216c, ap3216c_ops);ap3216c_class class_create(THIS_MODULE, ap3216c_class);device_create(ap3216c_class, NULL, MKDEV(major, 0), NULL, ap3216c);return 0;
}在注册的ap3216c_ops中有关read和write的函数使用到I2C数据有关的接口就有两类
一类是以i2c client为参数进行简单的数据收发。该方法只可以通过标准方式发送或者接收一定数量的数据。另一类是以i2c adapter和i2c msg为参数可以更为灵活的read或者write数据包括i2c_transfer。使用该方法可以以struct i2c_msg为参数一次读取、或者写入、或者读取加写入一定数量的数据。具体的函数去看\Linux-4.9.88\include\linux\i2c.h 形态2: 复合型设备如HDMI
确定I2C adapter 根据设备硬件的连接方式找到设备使用的I2C adapter。例如HDMI 设备可以使用 i2c2 作为其 DDC 通道。将设备作为平台设备描述 将设备作为平台设备platform device进行描述。其 DTS 节点并不是直接位于I2C adapter下而是位于根目录。例如
hdmi {ddc-i2c-bus i2c2;status okay;
};获取I2C adapter的引用 在DTS描述中使用 ddc-i2c-bus 属性引用I2C adapter节点。该属性通过 of_parse_phandle 来解析并在驱动中获取相应的I2C adapter的指针。在 probe 函数中获取I2C adapter 在平台设备的 probe 函数中通过 of_parse_phandle 来获取I2C adapter节点并通过 of_find_i2c_adapter_by_node() 获取对应的 struct i2c_adapter 指针。如下代码示例
struct i2c_adapter *ddc;
struct device_node *ddc_node;ddc_node of_parse_phandle(dev-of_node, ddc-i2c-bus, 0);
if (!ddc_node) {dev_err(dev, failed to find ddc-i2c-bus node\n);return -ENODEV;
}ddc of_find_i2c_adapter_by_node(ddc_node);
of_node_put(ddc_node);
if (!ddc)return -EPROBE_DEFER;使用 i2c_transfer 进行读写操作 获取I2C adapter之后便可以使用 i2c_transfer() 接口来进行I2C读写操作。例如通过DDC接口读取EDID信息时
struct i2c_msg msgs[] {{.addr ddc_addr,.flags I2C_M_RD,.len 128,.buf edid_buf,},
};ret i2c_transfer(ddc, msgs, 1); // 读取EDID数据3. i2c_client
i2c设备中用i2c_client结构体来指代
/*** struct i2c_client - represent an I2C slave device* flags: I2C_CLIENT_TEN indicates the device uses a ten bit chip address;* I2C_CLIENT_PEC indicates it uses SMBus Packet Error Checking* addr: Address used on the I2C bus connected to the parent adapter.* name: Indicates the type of the device, usually a chip name thats* generic enough to hide second-sourcing and compatible revisions.* adapter: manages the bus segment hosting this I2C device* dev: Driver model device node for the slave.* irq: indicates the IRQ generated by this device (if any)* detected: member of an i2c_driver.clients list or i2c-cores* userspace_devices list* slave_cb: Callback when I2C slave mode of an adapter is used. The adapter* calls it to pass on slave events to the slave driver.** An i2c_client identifies a single device (i.e. chip) connected to an* i2c bus. The behaviour exposed to Linux is defined by the driver* managing the device.*/
struct i2c_client {unsigned short flags; /* div., see below */unsigned short addr; /* chip address - NOTE: 7bit *//* addresses are stored in the *//* _LOWER_ 7 bits */char name[I2C_NAME_SIZE];struct i2c_adapter *adapter; /* the adapter we sit on */struct device dev; /* the device structure */int irq; /* irq issued by device */struct list_head detected;
#if IS_ENABLED(CONFIG_I2C_SLAVE)i2c_slave_cb_t slave_cb; /* callback for slave mode */
#endif
};flags: 表示I2C设备的标志位。可以包含以下选项 I2C_CLIENT_TEN表示设备使用的是十位地址而不是常见的七位地址。I2C_CLIENT_PEC表示设备使用SMBus包错误校验Packet Error CheckingPEC这是一种错误检测机制。
addr:
I2C设备的地址。地址位于I2C总线上的低7位或低10位如果 flags 指示设备使用十位地址的话。
name:
一个字符数组表示设备的名称通常是设备的芯片名称或类型。名称尽可能保持通用以适应同一设备的不同版本或兼容的设备。
adapter:
指向设备所在的I2C总线适配器i2c_adapter的指针。每个I2C设备都挂载在一个I2C适配器上适配器管理与设备的通信。
dev:
Linux驱动模型中的设备节点struct device用于表示I2C从设备。这是Linux设备模型的标准数据结构提供了与设备类、驱动程序以及电源管理等相关的接口。
irq:
表示设备所使用的中断号如果该设备可以生成中断。不使用中断的设备此字段可能为0或无效值。
detected:
用于链表结构中的节点。在内核中这个字段用于将该设备连接到 i2c_driver.clients 列表或 i2c-core 的 userspace_devices 列表中。
slave_cb:
仅在启用了 I2C Slave 模式即 CONFIG_I2C_SLAVE 选项启用时时有效。这是用于适配器从模式的回调函数指针。当I2C适配器处于从模式时适配器调用这个回调函数将从模式的事件传递给从设备的驱动程序。
i2c_client一般是在register adapter的时候解析adapter的child node自行创建的这个在对上一章节中对i2c_add_adapte函数进行分析的时候有提到过