作者:彭东林

邮箱:pengdonglin137@163.com

内核版本: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)的更多相关文章

  1. linux安装mysql服务分两种安装方法:

    linux安装mysql服务分两种安装方法: ①源码安装,优点是安装包比较小,只有十多M,缺点是安装依赖的库多,安装编译时间长,安装步骤复杂容易出错: ②使用官方编译好的二进制文件安装,优点是安装速度 ...

  2. 菜鸡的Java笔记 第十三 String 类的两种实例化方法

    String 类的两种实例化方法 String 类的两种实例化方式的区别 String 类对象的比较 Stirng 类对象的使用分析 /*    1.String 类的两种实例化方式的区别       ...

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

    CC攻击就是说攻击者利用服务器或代理服务器指向被攻击的主机,然后模仿DDOS,和伪装方法网站,这种CC主要是用来攻击页面的,导致系统性能用完而主机挂掉了,下面我们来看linux中防CC攻击方法. 什么 ...

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

    学习于慕课网 http://www.imooc.com/video/1806 1.方法一: cmd中处于未登录状态时,输入 mysql -uroot -p自己的密码 --prompt 新的提示符 示例 ...

  5. C# Emgu CV学习笔记二之图像读写的两种方法

    http://blog.csdn.net/marvinhong/article/details/6800450 图像显示在控件loadPictureBox上 方法一 //读取图像001.jpg Int ...

  6. Android(java)学习笔记147:textView 添加超链接(两种实现方式,,区别于WebView)

    1.方式1: LinearLayout layout = new LinearLayout(this); LinearLayout.LayoutParams params = new LinearLa ...

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

    //定义组件有两种方式,函数和类 function Welcome(props) { return <h1>Hello, {props.name}</h1>; } class ...

  8. angular学习笔记(三)-视图绑定数据的两种方式

    绑定数据有两种方式: <!DOCTYPE html> <html ng-app> <head> <title>2.2显示文本</title> ...

  9. Android(java)学习笔记90:TextView 添加超链接(两种实现方式)

    1. TextView添加超链接: TextView添加超链接有两种方式,它们有区别于WebView: (1)方式1: LinearLayout layout = new LinearLayout(t ...

随机推荐

  1. 河南省第十届省赛 Binary to Prime

    题目描述: To facilitate the analysis of  a DNA sequence,  a DNA sequence is represented by a binary  num ...

  2. 基于x64的处理器意思

    基于x64的处理器意思是CPU的架构是X64的,也是64位的CPU. 基本简介: "x86-64",有时会简称为"x64",是64位微处理器架构及其相应指令集的 ...

  3. Swift 学习之二十一:?和 !(详解)

    http://blog.csdn.net/woaifen3344/article/details/30244201 Swift语言使用var定义变量,但和别的语言不同,Swift里不会自动给变量赋初始 ...

  4. Day 14 python 之 字符串练习

    一.字符串总结与练习 #! /usr/bin/env python # -*- coding: utf-8 -*- # __author__ = "DaChao" # Date: ...

  5. 搜索引擎--范例:SAE创建新应用,SVN管理代码

    最初接触的平台是新浪SAE平台,虽然限制多得要命,速度也不怎么样,但无论怎么样,人家是“免费的”,免费的东西你还想怎么样?是不是? 1:注册登录新浪SAE,这个不用多说,相信你们的智商 2:创建一个新 ...

  6. 【C++】默认构造函数

    参考文献: 1.黄邦勇帅 2.http://www.cnblogs.com/graphics/archive/2012/10/02/2710340.html 3.http://blog.csdn.ne ...

  7. linux awk学习笔记

    awk学习笔记 awk语法格式 awk '{pattern + action}' {filenames} awk作用 awk的最基本功能是在文件或者字符串中基于指定规则浏览和抽取信息,awk抽取信息后 ...

  8. k8s的deployment应用

    Kubernetes 通过各种 Controller 来管理 Pod 的生命周期.为了满足不同业务场景,Kubernetes 开发了 Deployment.ReplicaSet.DaemonSet.S ...

  9. MATLAB求函数零点与极值

    1.      roots函数 针对多项式求零点(详见MATLAB多项式及多项式拟合) 2.      fzero函数 返回一元函数在某个区间内的的零点. x0 = fzero(@(x)x.^2-3* ...

  10. zoj3256

    好题,由m的范围知道这肯定是矩阵乘法加速插头dp,关键是怎么写 以往插头dp常用逐格递推,而这道题要求整行逐列递推 这样我们才能构造转移矩阵. 我们可以通过假象一个第0列来将路径转化为回路问题 逐列递 ...