TQ2440学习笔记——Linux上I2C驱动的两种实现方法(1)

作者:彭东林

邮箱:[email protected]

内核版本: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 <[email protected]> */
  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: [[email protected] /]# cd /sys/bus/i2c/drivers/at24/0-0050/
   2: [[email protected] 0-0050]# ls
   3: driver     eeprom     modalias   name       power      subsystem  uevent
   4: [[email protected] 0-0050]# dd if=/dev/zero of=eeprom 
   5: ^C
   6: [[email protected] 0-0050]# cat eeprom 
   7: [[email protected] 0-0050]# echo "pengdonglin" > eeprom 
   8: [[email protected] 0-0050]# cat eeprom 
   9: pengdonglin
  10: [[email protected] 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 <[email protected]> */
  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: [[email protected] /]# cd /sys/bus/i2c/drivers/at24/0-0050/
   2: [[email protected] 0-0050]# ls
   3: driver     eeprom     modalias   name       power      subsystem  uevent
   4: [[email protected] 0-0050]# echo "[email protected]" > eeprom 
   5: [[email protected] 0-0050]# cat eeprom 
   6: [email protected]
   7: [[email protected] 0-0050]# 

完!

时间: 2024-10-13 21:43:02

TQ2440学习笔记——Linux上I2C驱动的两种实现方法(1)的相关文章

linux中防CC攻击两种实现方法(转)

CC攻击就是说攻击者利用服务器或代理服务器指向被攻击的主机,然后模仿DDOS,和伪装方法网站,这种CC主要是用来攻击页面的,导致系统性能用完而主机挂掉了,下面我们来看linux中防CC攻击方法. 什么是CC攻击 cc攻击简单就是(ChallengeCollapsar) CC攻击的原理就是攻击者控制某些主机不停地发大量数据包给对方服务器造成服务器资源耗尽,一直到宕机崩溃.CC主要是用来攻击页面的,每个人都有 这样的体验:当一个网页访问的人数特别多的时候,打开网页就慢了,CC就是模拟多个用户(多少线

MySQL学习笔记(2) - 修改MySQL提示符的两种方法

学习于慕课网 http://www.imooc.com/video/1806 1.方法一: cmd中处于未登录状态时,输入 mysql -uroot -p自己的密码 --prompt 新的提示符 示例:mysql -uroot -p111111 --prompt \h 111111为自己的密码,\h指的是localhost 2.方法二: cmd中处于登录状态时,输入 prompt 新的提示符 示例: 3.提示符可以用的参数: 1)\D :完整的日期 2)\d :当前数据库 3)\h :当前主机名

react学习笔记1之声明组件的两种方式

//定义组件有两种方式,函数和类 function Welcome(props) { return <h1>Hello, {props.name}</h1>; } class Welcome extends React.Component{ render(){ return <h1>Hello, {this.props.name}</h1>; } } ReactDOM.render( <Welcome name="kevin"/&g

Linux的i2c驱动详解

目录(?)[-] 简介 架构 设备注册 I2C关键数据结构和详细注册流程 关键数据结构 详细注册流程 使用I2C子系统资源函数操作I2C设备 Gpio模拟i2c总线的通用传输算法 总结 理清i2c中的个结构体关系 i2c驱动的编写建议 1 简介 I2C 总线仅仅使用 SCL . SDA 两根信号线就实现了设备之间的数据交互,极大地简化对硬件资源和 PCB 板布线空间的占用.因此, I2C 总线被非常广泛地应用在 EEPROM .实时钟.小型 LCD 等设备与 CPU 的接口中. Linux I2

51CTO学习笔记--Linux运维故障排查思路与系统调优技巧视频课程(高俊峰)

51CTO学习笔记--Linux运维故障排查思路与系统调优技巧视频课程 第一课 Linux运维经验分享与思路 1.一般把主机名,写到hosts下    127.0.0.1    hostname,因为很多应用要解析到本地.oracle没有这个解析可能启动不了. 2.注释掉UUID以及MAC地址,需要绑定网卡的时候,这个可能会有影响. 3.磁盘满了无法启动,  var下木有空间,无法创创建PID等文件,导致文件无法启动,按e   进入single  然后b  重启进入单用户模式. 4.ssh登陆系

Linux 程序设计学习笔记----Linux下文件类型和属性管理

转载请注明出处:http://blog.csdn.net/suool/article/details/38318225 部分内容整理自网络,在此感谢各位大神. Linux文件类型和权限 数据表示 文件属性存储结构体Inode的成员变量i_mode存储着该文件的文件类型和权限信息.该变量为short int类型. 这个16位变量的各个位功能划分为: 第0-8位为权限位,为别对应拥有者(user),同组其他用户(group)和其他用户(other)的读R写W和执行X权限. 第9-11位是权限修饰位,

在开发板Linux上挂载"驱动"挂载不成功,出现提示server 172.27.52.100 not responding, still trying

1.在开发板具体操作步骤如下: 1.1 :设置IP ifconfig eth0 172.27.52.200 1.2 :ping通 虚拟机Linux 主机Linux ping XXX.XXX.XXX.XXX 1.3.挂接 mount -t nfs -o nolock  XXX.XXX.XXX.XXX:/work/nfs_root/first_fs  /mnt // 例如:mount -t nfs -o nolock  172.27.52.100:/work/nfs_root/first_fs  /

马哥linux学习(linux上的包管理)

我们知道linux有许多服务,但这些服务都需要安装.升级或卸载.查询及效验,那就需要用到包管理工具,包管理器主要用于打包.包管理(安装.升级.卸载.查询及效验). 打包:就是按照既定规范存放于一个单一的归档文件中. 安装:把一个包展开,对应的安放于某些位置. 卸载:安装后的每一个文件都给找到,收集删除. 升级:把新版本的应用程序替换旧版本. 查询:可以查询安装的程序一些相关的信息. 效验:效验程序是否被篡改过. 下面我们说下linux里面最常用的两种包管理工具,RPM和YUM工具. 1.  RP

20150503 imx257下实现I2C驱动的四种方法

20150503 imx257下实现I2C驱动的四种方法 2015-05-3 Lover雪儿 时间过得好快,转眼间五一假期就即将结束了,假期期间,大家都潇洒的去玩了,徒留辛辛苦苦的程序员还是窝在宿舍乖乖的敲着代码... 好啦,开开玩笑,辛酸史每家都有一大本,还是要积极的面对生活!!! 今天我们的任务是简单的入门linux内核下i2c设备驱动分离的四种写法. 一.一个简单的i2c驱动 和以前的驱动程序不同,i2c驱动分为drv驱动和dev设备驱动两个文件,不懂的可以参考我以前写的<20150313