linux驱动移植-I2C适配器驱动移植
目录
在Mini2440裸机开发之I2C(AT24C08)我们介绍了S3C2440这款SOC的I2C结构,其内部只有一个I2C控制器,其中SCL连接GPE14引脚,SDA连接GPE15引脚。
这一节我们将研究S3C2440的I2C适配器驱动,或者说 I2C 总线驱动、I2C 控制器驱动。
I2C适配器驱动是基于platform模型的,主要提供一个algorithm底层的I2C协议的收发函数。在platform driver中probe函数中:
- 动态分配i2c_adapter,并进行成员初始化,包括设置algo;
- 初始化I2C总线所使用的的GPIO功能复用为I2C;
- 初始化S3C2440 I2C控制器相关的寄存器,设置从设备地址、以及I2C时钟预分频系数;
- 获取资源信息,并注册I2C中断处理函数;
- 最后将i2c_adapter注册到i2c_bus_type总线,并且注册时会调用i2c_scan_board_info,扫描并使用i2c_new_device注册I2C从设备。
一、 platform设备注册(s3c2410-i2c)
1.1 s3c_device_i2c0
向往常一样,我们依然是定位到arch/arm/mach-s3c24xx/mach-smdk2440.c文件,在该文我们可以找到platform设备s3c_device_i2c0变量,其定义在arch/arm/plat-samsung/devs.c文件:
/* I2C */ static struct resource s3c_i2c0_resource[] = { [0] = DEFINE_RES_MEM(S3C_PA_IIC, SZ_4K), [1] = DEFINE_RES_IRQ(IRQ_IIC), }; struct platform_device s3c_device_i2c0 = { // platform设备信息 .name = "s3c2410-i2c", .id = 0, .num_resources = ARRAY_SIZE(s3c_i2c0_resource), .resource = s3c_i2c0_resource, }; struct s3c2410_platform_i2c default_i2c_data __initdata = { // 用于初始化s3c_device_i2c0.dev.platform_data .flags = 0, .slave_addr = 0x10, // 从设备地址 .frequency = 100*1000, // 频率 100kb .sda_delay = 100, }; void __init s3c_i2c0_set_platdata(struct s3c2410_platform_i2c *pd) // 用于绑定s3c_device_i2c0.dev.platform_data和default_i2c_data的关系 { struct s3c2410_platform_i2c *npd; if (!pd) { pd = &default_i2c_data; pd->bus_num = 0; } npd = s3c_set_platdata(pd, sizeof(*npd), &s3c_device_i2c0); // s3c_device_i2c0.dev.platform_data = &default_i2c_data if (!npd->cfg_gpio): npd->cfg_gpio = s3c_i2c0_cfg_gpio; }
我们重点关注一下platform设备资源定义,这里定义了2个资源,第1个是内存资源、第2个是中断资源:
- 第一个资源是IO内存资源,起始地址是I2C寄存器寄基地址,即0x54000000,大小为4Kb;
- 第二个资源是中断资源,中断号位IRQ_IIC,即27;
我们已经定义了I2C控制器相关的platform_device设备s3c_device_i2c0,并进行了初始化,那platform设备啥时候注册的呢?
linux内核启动的时候会根据uboot中设置的机器id执行相应的初始化工作,比如.init_machine、.init_irq,我们首先定位到arch/arm/mach-s3c24xx/mach-smdk2440.c:
MACHINE_START(S3C2440, "SMDK2440") /* Maintainer: Ben Dooks <ben-linux@fluff.org> */ .atag_offset = 0x100, .init_irq = s3c2440_init_irq, .map_io = smdk2440_map_io, .init_machine = smdk2440_machine_init, .init_time = smdk2440_init_time, MACHINE_END
重点关注init_machine,init_machine中保存的是开发板资源注册的初始化代码。
1.2 smdk2440_machine_init
static void __init smdk2440_machine_init(void) { s3c24xx_fb_set_platdata(&smdk2440_fb_info); s3c_i2c0_set_platdata(NULL); // 初始化s3c_device_i2c0.dev.platform_data platform_add_devices(smdk2440_devices, ARRAY_SIZE(smdk2440_devices)); // s3c2440若干个platform设备注册 usb host controller、lcd、wdt等 smdk_machine_init(); // s3c24x0系列若干个platform设备注册(通用) }
这里利用platform_add_devices进行若干个platform设备的注册,该函数通过调用platform_device_register实现platform设备注册。
static struct platform_device *smdk2440_devices[] __initdata = { &s3c_device_ohci, &s3c_device_lcd, &s3c_device_wdt, &s3c_device_i2c0, &s3c_device_iis, &smdk2440_device_eth, };
platform设备s3c_device_i2c0是通过arch/arm/plat-samsung/include/plat/devs.h头文件引入的。
二、platform驱动注册(s3c2410-i2c)
I2C控制器platform驱动定义位于drivers/i2c/busses/i2c-s3c2410.c文件中。
2.1 入口和出口函数
我们在i2c-s3c2410.c文件定位到驱动模块的入口和出口:
static const struct platform_device_id s3c24xx_driver_ids[] = { { .name = "s3c2410-i2c", .driver_data = 0, }, { .name = "s3c2440-i2c", .driver_data = QUIRK_S3C2440, }, { .name = "s3c2440-hdmiphy-i2c", .driver_data = QUIRK_S3C2440 | QUIRK_HDMIPHY | QUIRK_NO_GPIO, }, { }, }; static const struct of_device_id s3c24xx_i2c_match[] = { // 设备树匹配时使用 { .compatible = "samsung,s3c2410-i2c", .data = (void *)0 }, { .compatible = "samsung,s3c2440-i2c", .data = (void *)QUIRK_S3C2440 }, { .compatible = "samsung,s3c2440-hdmiphy-i2c", .data = (void *)(QUIRK_S3C2440 | QUIRK_HDMIPHY | QUIRK_NO_GPIO) }, { .compatible = "samsung,exynos5-sata-phy-i2c", .data = (void *)(QUIRK_S3C2440 | QUIRK_POLL | QUIRK_NO_GPIO) }, {}, }; static struct platform_driver s3c24xx_i2c_driver = { .probe = s3c24xx_i2c_probe, .remove = s3c24xx_i2c_remove, .id_table = s3c24xx_driver_ids, .driver = { .name = "s3c-i2c", .pm = S3C24XX_DEV_PM_OPS, .of_match_table = of_match_ptr(s3c24xx_i2c_match), }, }; static int __init i2c_adap_s3c_init(void) { return platform_driver_register(&s3c24xx_i2c_driver); } subsys_initcall(i2c_adap_s3c_init); static void __exit i2c_adap_s3c_exit(void) { platform_driver_unregister(&s3c24xx_i2c_driver); } module_exit(i2c_adap_s3c_exit);
在plaftrom总线设备驱动模型中,我们知道当内核中有platform设备platform驱动匹配,会调用到platform_driver里的成员.probe,在这里就是s3c24xx_i2c_probe函数。
2.2 I2C相关数据结构
2.2.1 s3c24xx_i2c
在介绍s3c24xx_i2c_probe之前,先来看一下s3c24xx_i2c结构体定义,其定义在drivers/i2c/busses/i2c-s3c2410.c文件:
struct s3c24xx_i2c { // 用来表示SOC的一个I2C控制器 wait_queue_head_t wait; // 等待队列 kernel_ulong_t quirks; // I2C控制器的一些特殊行为 struct i2c_msg *msg; // 存放要发送的I2C数据包 unsigned int msg_num; // 数据包个数 实际只有1个 unsigned int msg_idx; // 当前发送的数据包索引 unsigned int msg_ptr; // 当前传输的字节在数据包的索引 unsigned int tx_setup; // 用来延时,等待SCL被释放 unsigned int irq; // 中断号 enum s3c24xx_i2c_state state; // I2C传输状态 STATE_START、STATE_IDEL、STATE_STOP、STATE_WRITE、STATE_READ unsigned long clkrate; void __iomem *regs; struct clk *clk; // 时钟 struct device *dev; // 存储platform设备的dev成员 struct i2c_adapter adap; // 描述该I2C控制器的适配器 struct s3c2410_platform_i2c *pdata; // 私有数据 默认初始化为&default_i2c_data int gpios[2]; struct pinctrl *pctrl; #if defined(CONFIG_ARM_S3C24XX_CPUFREQ) struct notifier_block freq_transition; #endif struct regmap *sysreg; unsigned int sys_i2c_cfg; };
2.2.2 s3c2410_platform_i2c
s3c2410_platform_i2c定义在include/linux/platform_data/i2c-s3c2410.h文件:
/** * struct s3c2410_platform_i2c - Platform data for s3c I2C. * @bus_num: The bus number to use (if possible). * @flags: Any flags for the I2C bus (E.g. S3C_IICFLK_FILTER). * @slave_addr: The I2C address for the slave device (if enabled). * @frequency: The desired frequency in Hz of the bus. This is * guaranteed to not be exceeded. If the caller does * not care, use zero and the driver will select a * useful default. * @sda_delay: The delay (in ns) applied to SDA edges. * @cfg_gpio: A callback to configure the pins for I2C operation. */ struct s3c2410_platform_i2c { int bus_num; // I2C控制器编号 unsigned int flags; unsigned int slave_addr; // 从设备地址 unsigned long frequency; // 时钟频率 unsigned int sda_delay; void (*cfg_gpio)(struct platform_device *dev); // 初始化gpio口 功能复用I2C };
2.3 s3c24xx_i2c_probe
亲爱的读者和支持者们,自动博客加入了打赏功能,陆陆续续收到了各位老铁的打赏。在此,我想由衷地感谢每一位对我们博客的支持和打赏。你们的慷慨与支持,是我们前行的动力与源泉。
日期 | 姓名 | 金额 |
---|---|---|
2023-09-06 | *源 | 19 |
2023-09-11 | *朝科 | 88 |
2023-09-21 | *号 | 5 |
2023-09-16 | *真 | 60 |
2023-10-26 | *通 | 9.9 |
2023-11-04 | *慎 | 0.66 |
2023-11-24 | *恩 | 0.01 |
2023-12-30 | I*B | 1 |
2024-01-28 | *兴 | 20 |
2024-02-01 | QYing | 20 |
2024-02-11 | *督 | 6 |
2024-02-18 | 一*x | 1 |
2024-02-20 | c*l | 18.88 |
2024-01-01 | *I | 5 |
2024-04-08 | *程 | 150 |
2024-04-18 | *超 | 20 |
2024-04-26 | .*V | 30 |
2024-05-08 | D*W | 5 |
2024-05-29 | *辉 | 20 |
2024-05-30 | *雄 | 10 |
2024-06-08 | *: | 10 |
2024-06-23 | 小狮子 | 666 |
2024-06-28 | *s | 6.66 |
2024-06-29 | *炼 | 1 |
2024-06-30 | *! | 1 |
2024-07-08 | *方 | 20 |
2024-07-18 | A*1 | 6.66 |
2024-07-31 | *北 | 12 |
2024-08-13 | *基 | 1 |
2024-08-23 | n*s | 2 |
2024-09-02 | *源 | 50 |
2024-09-04 | *J | 2 |
2024-09-06 | *强 | 8.8 |
2024-09-09 | *波 | 1 |
2024-09-10 | *口 | 1 |
2024-09-10 | *波 | 1 |
2024-09-12 | *波 | 10 |
2024-09-18 | *明 | 1.68 |
2024-09-26 | B*h | 10 |
2024-09-30 | 岁 | 10 |
2024-10-02 | M*i | 1 |
2024-10-14 | *朋 | 10 |
2024-10-22 | *海 | 10 |
2024-10-23 | *南 | 10 |
2024-10-26 | *节 | 6.66 |
2024-10-27 | *o | 5 |
2024-10-28 | W*F | 6.66 |
2024-10-29 | R*n | 6.66 |
2024-11-02 | *球 | 6 |
2024-11-021 | *鑫 | 6.66 |
2024-11-25 | *沙 | 5 |
2024-11-29 | C*n | 2.88 |

【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了