针对Linux,RK3568平台下,I2C驱动的一点小小的领悟

总结:

I2C驱动子系统的实现是如下说明:以下说明仅针对硬件I2C;若所需为软件I2C的设备、驱动都需要我们自己编写

I2C驱动由:

i2c_client(i2c设备)、i2c_driver(i2c驱动)

i2c core:i2c核心层

i2c_adapter(i2c控制器/i2c适配器)、i2c_algorithm(i2c算法/i2c通信协议/i2c通信时序构成)

其中:i2c_client(i2c设备)、i2c_driver(i2c驱动),由我们驱动工程师做

i2c core:为硬件无关代码,主要作用是将i2c_client(i2c设备)、i2c_driver(i2c驱动)等的操作转换为i2c_adapter所需要的:简单理解为中间件/翻译等

i2c_adapter(i2c控制器/i2c适配器)、i2c_algorithm(i2c算法/i2c通信协议/i2c通信时序构成):由原厂工程师编写,也就是SOC的工程师:瑞芯微、海思等

其中I2C是具有自己的总线的,i2c_client、i2c_driver注册在I2C总线上

总线结构体如下:

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,
};

但反直觉的是,i2c_adapter并不是直接通过i2c总线去匹配注册的,而是转了一道,通过platform里面的probe去进行的注册

c 复制代码
static struct platform_driver rk3x_i2c_driver = {
	.probe   = rk3x_i2c_probe,
	.remove  = rk3x_i2c_remove,
	.driver  = {
		.name  = "rk3x-i2c",
		.of_match_table = rk3x_i2c_match,
		.pm = &rk3x_i2c_pm_ops,
	},
};

#ifdef CONFIG_ROCKCHIP_THUNDER_BOOT
static int __init rk3x_i2c_driver_init(void)
{
	return platform_driver_register(&rk3x_i2c_driver);
}
subsys_initcall_sync(rk3x_i2c_driver_init);

static void __exit rk3x_i2c_driver_exit(void)
{
	platform_driver_unregister(&rk3x_i2c_driver);
}
module_exit(rk3x_i2c_driver_exit);
#else
module_platform_driver(rk3x_i2c_driver);
#endif

不过也不难以理解,设备树中指定了硬件i2c的设备,然后编写驱动,通过platform_driver去匹配注册,然后再在对应的Probe内进行i2c_adapter、i2c_client的注册

i2c_adapter的注册在i2c_add_adapter申请空间,剩下的位置填充,最后赋给旗下的i2c_client

i2c_client的注册:i2c_add_adapter->i2c_register_adapter->of_i2c_register_devices->...->i2c_new_device

也就是说i2c_client的注册是随着i2c_adapter的注册一同完成的,不过也不难以理解,i2c_adapter是用来控制i2c设备的

误区:误以为i2c-core-base.c文件中的dummy:

c 复制代码
static struct i2c_driver dummy_driver = {
	.driver.name	= "dummy",
	.probe		= dummy_probe,
	.remove		= dummy_remove,
	.id_table	= dummy_id,
};

是注册的i2c驱动,实际上并不是,这个只是针对同一设备具有多地址时的一个优化,具体作用还没理解清楚。

误区2:i2c-dev.c文件中的const struct file_operations i2cdev_fops以为是注册了实际的设备

实际并不是,稍微追一下就可以发现是直接调用了i2c_adapter,可以很简单的理解为,通过这个玩意可以跳过编写i2c的驱动,在启动了对于管脚的i2c_adapter后,就可以在应用层编写应用,进行一定的处理,不需要在内核里面增加新的i2c_client、i2c_driver。

总结,对于非soc驱动工程师来说,针对硬件I2C,只需要关注i2c_client、i2c_driver的编写即可,甚至i2c_client直接使用设备树,而去编写i2c_driver就行。

核心思想依然是:总线、驱动、设备模型;
i2c_bus_type(总线)、i2c_driver(驱动)、i2c_client(设备)
platform_bus_type(总线)、platform_driver rk3x_i2c_driver(驱动)、rk3x_i2c_match/设备树中指定的i2c节点(设备)

相关推荐
无小道1 小时前
深入解析操作系统文件缓冲区:页缓存、基数树与f_pos的协同设计
linux·缓存·文件缓冲区
AOwhisky2 小时前
Linux(CentOS)系统管理入门笔记(第十二期)——系统管理工具与软件包管理(上篇):Cockpit 与 RPM 包管理
linux·运维·笔记·centos·云计算
寒水馨2 小时前
Linux下载、安装godot-4.7.1-stable(附安装包Godot_v4.7.1-stable_linux.x86_64.zip)
linux·游戏引擎·godot·游戏开发·2d游戏·3d游戏·godot engine
念恒123062 小时前
Socket编程UDP(中)
linux·网络协议·udp
深耕运维十八载云架构实务2 小时前
系统故障玄学之内存明明显示空闲,服务器却频繁卡顿卡死?拆解被忽略的 Linux 隐性内存陷阱
linux
qetfw3 小时前
CentOS 7 搭建 Sendmail + Dovecot 邮件服务器:SMTP、POP3、IMAP 与 TLS
linux·centos
明日清晨3 小时前
printf(“%p\n“,&((struct A *)NULL)->m_float)的原理
linux·服务器·前端
Android系统攻城狮4 小时前
Linux PipeWire深度解析之pw_context_add_spa_lib调用流程与实战(二十九)
linux·运维·服务器·音频进阶·pipewire音频进阶
Dovis(誓平步青云)4 小时前
《如何在CentOS 7中添加Plex官方软件源:解决文件磁盘难管理难题》
linux·运维·服务器·后端·生成对抗网络·centos
草莓熊Lotso5 小时前
【Linux网络】深入理解Linux IO多路复用:select服务器完善、内核原理与poll实战
linux·运维·服务器·c语言·网络·c++