TQ2440学习笔记——Linux上I2C驱动的两种实现方法(1)
作者:彭东林
内核版本:Linux-3.14
u-boot版本:U-Boot 2015.04
硬件:TQ2440 (NorFlash:2M NandFlash:256M 内存:64M)
摘要
这里并不深入分析Linux下I2C驱动的实现,只是以TQ2440硬件平台为例分析I2C驱动的两种方法。
第一种方法:
使用S3C2440自带的I2C控制器实现,这个kernel已经支持,我们只需要配置即可。
第二种方法:
使用GPIO模拟,这个在kernel中已经集成,实现代码在drivers/i2c/busses/i2c-gpio.c。
在TQ2440平台上有一个EEPROM,型号是:AT24C02C。我们就以驱动at24c02c为例。
硬件原理
EEPROM:
引脚:
可以看到:
I2CSDA ---- GPE15
I2CSCL ---- GPE14
实现
下面开始介绍这两种方法。
方法一、使用I2C控制器实现
kernel已经提供了这种实现,我们需要做的仅仅是配置,此外,kernel中也提供了at24c02的驱动,我们也是只要配置就行。
驱动框架
配置I2C控制器驱动
文件:arch/arm/mach-s3c24xx/mach-tq2440.c:
1: static struct platform_device *tq2440_devices[] __initdata = {
2: ......
3: &s3c_device_i2c0,
4: ......
5: };
6:
7: static void __init tq2440_machine_init(void)
8: {
9: .......
10: platform_add_devices(tq2440_devices, ARRAY_SIZE(tq2440_devices));
11: ......
12: }
13:
14: MACHINE_START(TQ2440, "TQ2440")
15: .atag_offset = 0x100,
16: .init_irq = s3c2440_init_irq,
17: .map_io = tq2440_map_io,
18: .init_machine = tq2440_machine_init,
19: .init_time = samsung_timer_init,
20: .restart = s3c244x_restart,
21: MACHINE_END
第3行的变量s3c_device_i2c0是Samsung已经定义好的,代码位置 arch/arm/plat-samsung/devs.c
1: static struct resource s3c_i2c0_resource[] = {
2: [0] = DEFINE_RES_MEM(S3C_PA_IIC, SZ_4K),
3: [1] = DEFINE_RES_IRQ(IRQ_IIC),
4: };
5:
6: struct platform_device s3c_device_i2c0 = {
7: .name = "s3c2410-i2c",
8: .id = 0,
9: .num_resources = ARRAY_SIZE(s3c_i2c0_resource),
10: .resource = s3c_i2c0_resource,
11: };
12:
13: struct s3c2410_platform_i2c default_i2c_data __initdata = {
14: .flags = 0,
15: .slave_addr = 0x10,
16: .frequency = 100*1000,
17: .sda_delay = 100,
18: };
第13行的结构体default_i2c_data的存放的是I2C的时序信息。
配置好上面,那么kernel在启动时就会将I2C控制器的硬件信息注册到系统。此外,还需要配置I2C控制器驱动。
代码位置:drivers/i2c/busses/i2c-s3c2410.c
1: static struct platform_device_id s3c24xx_driver_ids[] = {
2: {
3: .name = "s3c2410-i2c",
4: .driver_data = 0,
5: }, {
6: .name = "s3c2440-i2c",
7: .driver_data = QUIRK_S3C2440,
8: }, {
9: .name = "s3c2440-hdmiphy-i2c",
10: .driver_data = QUIRK_S3C2440 | QUIRK_HDMIPHY | QUIRK_NO_GPIO,
11: }, { },
12: };
13: MODULE_DEVICE_TABLE(platform, s3c24xx_driver_ids);
14:
15: /* device driver for platform bus bits */
16:
17: static struct platform_driver s3c24xx_i2c_driver = {
18: .probe = s3c24xx_i2c_probe,
19: .remove = s3c24xx_i2c_remove,
20: .id_table = s3c24xx_driver_ids,
21: .driver = {
22: .owner = THIS_MODULE,
23: .name = "s3c-i2c",
24: .pm = S3C24XX_DEV_PM_OPS,
25: .of_match_table = of_match_ptr(s3c24xx_i2c_match),
26: },
27: };
28:
29: static int __init i2c_adap_s3c_init(void)
30: {
31: return platform_driver_register(&s3c24xx_i2c_driver);
32: }
33: subsys_initcall(i2c_adap_s3c_init);
34:
35: static void __exit i2c_adap_s3c_exit(void)
36: {
37: platform_driver_unregister(&s3c24xx_i2c_driver);
38: }
39: module_exit(i2c_adap_s3c_exit);
第20行里的id_table存放的是要匹配的名称。
在drivers/i2c/busses/Makefile中:
obj-$(CONFIG_I2C_S3C2410) += i2c-s3c2410.o
所以需要配置CONFIG_I2C_S3C2410
make menuconfig
然后搜索CONFIG_I2C_S3C2410
可以看到下面红色框住的地方,意思是我们直接点击键盘上的1就可以跳转到搜索到的地方。
下图就是搜索到的位置:
至此,I2C控制器驱动(即I2C adapter驱动)就配置完成了。
EEPROM驱动配置
在内核中已经支持了at24c02c的驱动,我们需要做的是填充at24c02c的硬件信息。
文件arch/arm/mach-s3c24xx/mach-tq2440.c:
1: /* AT24C02 */
2: static struct at24_platform_data tq2440_platform_data = {
3: .byte_len = SZ_1K*2/8,
4: .page_size = 8,
5: .flags = AT24_FLAG_TAKE8ADDR,
6: };
7:
8: static struct i2c_board_info tq2440_at24c02[] = {
9: {
10: I2C_BOARD_INFO("24c02", 0x50),
11: .platform_data = &tq2440_platform_data,
12: },
13: };
14:
15: static void __init tq2440_machine_init(void)
16: {
17: ......
18: i2c_register_board_info(0, tq2440_at24c02, 1);
19: ......
20: }
21:
22: MACHINE_START(TQ2440, "TQ2440")
23: /* Maintainer: Ben Dooks <ben-linux@fluff.org> */
24: .atag_offset = 0x100,
25:
26: .init_irq = s3c2440_init_irq,
27: .map_io = tq2440_map_io,
28: .init_machine = tq2440_machine_init,
29: .init_time = samsung_timer_init,
30: .restart = s3c244x_restart,
31: MACHINE_END
第18行注册at24c02的板级信息(硬件信息的填写可以参考at24c02驱动解析板级信息的过程),可以看到,使用的busnum是0,即i2c_adapter的编号。
接下来需要配置at24c02的驱动:drivers/misc/eeprom/at24.c
1: static const struct i2c_device_id at24_ids[] = {
2: ......
3: { "24c01", AT24_DEVICE_MAGIC(1024 / 8, 0) },
4: { "24c02", AT24_DEVICE_MAGIC(2048 / 8, 0) },
5: ......
6: { "at24", 0 },
7: };
8: MODULE_DEVICE_TABLE(i2c, at24_ids);
9:
10: static struct i2c_driver at24_driver = {
11: .driver = {
12: .name = "at24",
13: .owner = THIS_MODULE,
14: },
15: .probe = at24_probe,
16: .remove = at24_remove,
17: .id_table = at24_ids,
18: };
19:
20: static int __init at24_init(void)
21: {
22: if (!io_limit) {
23: pr_err("at24: io_limit must not be 0!\n");
24: return -EINVAL;
25: }
26:
27: io_limit = rounddown_pow_of_two(io_limit);
28: return i2c_add_driver(&at24_driver);
29: }
30: module_init(at24_init);
在drivers/misc/eeprom/Makefile中:
obj-$(CONFIG_EEPROM_AT24) += at24.o
所以需要配置CONFIG_EEPROM_AT24,搜索过程前面已经说过。
至此,就配置完成了,重新编译内核。
验证
1: [root@TQ2440 /]# cd /sys/bus/i2c/drivers/at24/0-0050/
2: [root@TQ2440 0-0050]# ls
3: driver eeprom modalias name power subsystem uevent
4: [root@TQ2440 0-0050]# dd if=/dev/zero of=eeprom
5: ^C
6: [root@TQ2440 0-0050]# cat eeprom
7: [root@TQ2440 0-0050]# echo "pengdonglin" > eeprom
8: [root@TQ2440 0-0050]# cat eeprom
9: pengdonglin
10: [root@TQ2440 0-0050]#
方法二、使用GPIO模拟
这部分在kernel中也已经支持了,我们只需要配置即可。
驱动框架
跟上面的对比,EEPROM驱动不用修改,配置方法参考方法一,我们需要配置的只是将原来的I2C控制器驱动替换为I2C-GPIO驱动即可,其实kernel是利用这个两个GPIO引脚(一个是SDA(对应GPIOE15),另一个是SCL(对应GPIOE14))模拟出来一个I2C控制器,然后将这个控制器注册到系统,这一点对于EEPROM驱动来说是完全透明的,这里需要保证使用GPIO模拟出来的I2C控制器的编号必须跟at24c02注册板级信息时指定的busnum一致,而且不能再注册方法一中的I2C控制器硬件信息了,否则会冲突。
配置I2C_GPIO硬件信息
这个主要是告诉I2C_GPIO控制器使用哪两个GPIO作为SDA和SCL,如何填写配置信息可以参考i2c-gpio.c解析配置信息的过程。
文件 arch/arm/mach-s3c24xx/mach-tq2440.c:
1: static struct i2c_gpio_platform_data s3c_gpio_i2c_platdata = {
2: .sda_pin = S3C2410_GPE(15),
3: .scl_pin = S3C2410_GPE(14),
4: //.sda_is_open_drain = 1,
5: //.scl_is_open_drain = 1,
6: //.scl_is_output_only = 1,
7: //.udelay = 100, // 低电平和高电平的持续时间都是100us,那么频率就是5KHz
8: };
9:
10: struct platform_device s3c_device_gpio_i2c = {
11: .name = "i2c-gpio",
12: .id = 0,
13: .dev = {
14: .platform_data = &s3c_gpio_i2c_platdata,
15: }
16: };
17: #endif
18:
19: static struct platform_device *tq2440_devices[] __initdata = {
20: ......
21: #ifdef CONFIG_TQ2440_EEPROM_USE_GPIO_I2C
22: &s3c_device_gpio_i2c,
23: #else
24: &s3c_device_i2c0,
25: #endif
26: ......
27: };
28:
29: static void __init tq2440_machine_init(void)
30: {
31: ......
32: platform_add_devices(tq2440_devices, ARRAY_SIZE(tq2440_devices));
33: i2c_register_board_info(0, tq2440_at24c02, 1);
34: ......
35: }
36:
37: MACHINE_START(TQ2440, "TQ2440")
38: /* Maintainer: Ben Dooks <ben-linux@fluff.org> */
39: .atag_offset = 0x100,
40:
41: .init_irq = s3c2440_init_irq,
42: .map_io = tq2440_map_io,
43: .init_machine = tq2440_machine_init,
44: .init_time = samsung_timer_init,
45: .restart = s3c244x_restart,
46: MACHINE_END
第1行结构体s3c_gpio_i2c_platdata中指定了要用的GPIO引脚,以及udelay信息,用于控制I2C通信时的时序,因为SCL拉高拉低都需要延迟一段时间等待数据稳定;
第12行中的id表示的模拟出的i2c控制器的编号;
配置I2C-GPIO驱动
文件drivers/i2c/busses/i2c-gpio.c:
1: static struct platform_driver i2c_gpio_driver = {
2: .driver = {
3: .name = "i2c-gpio",
4: .owner = THIS_MODULE,
5: .of_match_table = of_match_ptr(i2c_gpio_dt_ids),
6: },
7: .probe = i2c_gpio_probe,
8: .remove = i2c_gpio_remove,
9: };
10:
11: static int __init i2c_gpio_init(void)
12: {
13: int ret;
14:
15: ret = platform_driver_register(&i2c_gpio_driver);
16: if (ret)
17: printk(KERN_ERR "i2c-gpio: probe failed: %d\n", ret);
18:
19: return ret;
20: }
文件drivers/i2c/busses/Makefile:
obj-$(CONFIG_I2C_GPIO) += i2c-gpio.o
所以需要配置CONFIG_I2C_GPIO。
至此,就配置完成了,重新编译内核。
验证
1: [root@TQ2440 /]# cd /sys/bus/i2c/drivers/at24/0-0050/
2: [root@TQ2440 0-0050]# ls
3: driver eeprom modalias name power subsystem uevent
4: [root@TQ2440 0-0050]# echo "pengdonglin137@163.com" > eeprom
5: [root@TQ2440 0-0050]# cat eeprom
6: pengdonglin137@163.com
7: [root@TQ2440 0-0050]#
完!
TQ2440学习笔记——Linux上I2C驱动的两种实现方法(1)的更多相关文章
- linux安装mysql服务分两种安装方法:
linux安装mysql服务分两种安装方法: ①源码安装,优点是安装包比较小,只有十多M,缺点是安装依赖的库多,安装编译时间长,安装步骤复杂容易出错: ②使用官方编译好的二进制文件安装,优点是安装速度 ...
- 菜鸡的Java笔记 第十三 String 类的两种实例化方法
String 类的两种实例化方法 String 类的两种实例化方式的区别 String 类对象的比较 Stirng 类对象的使用分析 /* 1.String 类的两种实例化方式的区别 ...
- linux中防CC攻击两种实现方法(转)
CC攻击就是说攻击者利用服务器或代理服务器指向被攻击的主机,然后模仿DDOS,和伪装方法网站,这种CC主要是用来攻击页面的,导致系统性能用完而主机挂掉了,下面我们来看linux中防CC攻击方法. 什么 ...
- MySQL学习笔记(2) - 修改MySQL提示符的两种方法
学习于慕课网 http://www.imooc.com/video/1806 1.方法一: cmd中处于未登录状态时,输入 mysql -uroot -p自己的密码 --prompt 新的提示符 示例 ...
- C# Emgu CV学习笔记二之图像读写的两种方法
http://blog.csdn.net/marvinhong/article/details/6800450 图像显示在控件loadPictureBox上 方法一 //读取图像001.jpg Int ...
- Android(java)学习笔记147:textView 添加超链接(两种实现方式,,区别于WebView)
1.方式1: LinearLayout layout = new LinearLayout(this); LinearLayout.LayoutParams params = new LinearLa ...
- react学习笔记1之声明组件的两种方式
//定义组件有两种方式,函数和类 function Welcome(props) { return <h1>Hello, {props.name}</h1>; } class ...
- angular学习笔记(三)-视图绑定数据的两种方式
绑定数据有两种方式: <!DOCTYPE html> <html ng-app> <head> <title>2.2显示文本</title> ...
- Android(java)学习笔记90:TextView 添加超链接(两种实现方式)
1. TextView添加超链接: TextView添加超链接有两种方式,它们有区别于WebView: (1)方式1: LinearLayout layout = new LinearLayout(t ...
随机推荐
- 51Nod 1256 求乘法逆元--扩展欧几里德
#include<stdio.h> int exgcd(int a,int b,int &x,int &y) { ) { x=; y=; return a; } int r ...
- 【uva11468-Substring】AC自动机+dp
http://acm.hust.edu.cn/vjudge/problem/31655 题意:给定k个模板串,n个字符以及选择它的概率pro[i],要构造一个长度问L的字符串s,问s不包含任意一个模板 ...
- 类似web风格的 Winform 分页控件
背景 最近做一个Winform的小程序,需要用到分页,由于之前一直在用 TonyPagerForWinForm.dll ,但该库没有源代码,网上找的也不全面,索性就准备自己改造一个.在园子里翻了一下, ...
- Linux 查看文件和文件夹大小
当磁盘大小超过标准时会有报警提示,这时如果掌握df和du命令是非常明智的选择. df可以查看一级文件夹大小.使用比例.档案系统及其挂入点,但对文件却无能为力. du可以查看文件及文件夹的大小. ...
- 在生成的Debug中test.exe的同级目录下创建一个文件,如TestLog.log
在上次编写一个日志类库时,想在.exe的同级目录下创建.log文件,对于这个路径的获得很简单,调用GetModuleFileName()函数即可.但是要去掉.exe而换成.log时,由于对字符串处理不 ...
- servlet+forward和direct区别
Servlet:是用于 java 编写的服务器端程序,其使用 java servlet API,当客户机发送请求到服务器时,服务器可以将请求信息发送给 servlet,并让 servlet 建立起服务 ...
- Mybatis三剑客
1.Mybatis-generator 自动化生成数据库交互代码->dao+pojo+xml 2.Mybatis-plugin dao文件和xml自动跳转,验证正确性,在xml中只能提示等功能 ...
- HDU-5451
Best Solver Time Limit: 1500/1000 MS (Java/Others) Memory Limit: 65535/102400 K (Java/Others)Tota ...
- vs code 体验
今天用了一下 vs code, 第一感觉非常棒.用过 sublime text 和 vs 的经验.对vs code有一种曾相识的感觉. 在界面体验上,比目前用的 sublime text的感觉要好,比 ...
- MATLAB中的符号运算
1. syms命令 可以替换sym和symfun,另外可以定义符号变量的类型,如 syms x positive; 限定x为正数. 若要取消这个限定,则可以用命令 syms x clear; ...