S5PV210 I2C设备驱动

对于一个i2c设备来说,其设备文件是最简单也是最复杂的,说它简单是因为很设备厂商会提供linux下的代码,这样就简单了;但是也有很多厂商它不提供或不完整提供linux下的代码,这样的话当然就复杂了。那么这个我现在这里就不说了,下面说说做了几个I2C设备(以ISA1200为例)后发现,不管设备文件如何总是要自己来做的一些事情,这大概就是所谓的移植吧。

       当然这个工作都是在板文件中进行的。以mach-s5pv210.c为例来说一下:

       先说下用板子自己带的I2C实现驱动加载:

       首先在板文件中建立ISA1200的信息:

  1. static int isa1200_power(int on)  
  2.    
  3. {       
  4.   
  5.        if(on){  
  6.    
  7.               gpio_direction_output(S5PV210_GPJ3(1), 1);  
  8.    
  9.               gpio_direction_output(S5PV210_GPJ3(0), 1);  
  10.    
  11.        }else{  
  12.    
  13.               gpio_direction_output(S5PV210_GPJ3(1), 0);  
  14.    
  15.               gpio_direction_output(S5PV210_GPJ3(0), 0);  
  16.    
  17.        }  
  18.    
  19.        return 0;  
  20.    
  21. }  
  22.    
  23.    
  24.    
  25. static struct isa1200_platform_data isa1200_1_pdata = {  
  26.    
  27.        .name = "isa1200",  
  28.    
  29.        .power_on = isa1200_power,  
  30.    
  31.        .pwm_ch_id = 1,   
  32.   
  33.        .hap_en_gpio = S5PV210_GPH3(1),  
  34.    
  35.        .max_timeout = 60000,  
  36.    
  37. };  
  38.    
  39.    
  40.    
  41. static void isa1200_init(void)  
  42.    
  43. {  
  44.    
  45.        gpio_direction_output(S5PV210_GPJ3(7), 1);  
  46.    
  47.        gpio_direction_output(S5PV210_GPJ3(1), 1);  
  48.    
  49.        gpio_direction_output(S5PV210_GPJ3(0), 1);  
  50.    
  51.          
  52.   
  53.        /*i2c_register_board_info(3, isa1200_board_info, 
  54.   
  55.               ARRAY_SIZE(isa1200_board_info));*/  
  56.    
  57.        return;  
  58.    
  59. }  

以及i2c_board_info结构体:

  1. {  
  2.    
  3.               I2C_BOARD_INFO("isa1200_1", 0x90>>1),/*这个是I2C设备的从机地址*/  
  4.    
  5.               .platform_data = &isa1200_1_pdata,  
  6.    
  7.        },  

然后在以下三个I2C总线中找到一条如i2c_devs1[]

  1. /* I2C0 */  
  2.    
  3. static struct i2c_board_info i2c_devs0[] __initdata = {  
  4.    
  5.        {  
  6.    
  7.               I2C_BOARD_INFO("act8937", 0x5B),  
  8.    
  9.               .platform_data = &act8937_platform_data,  
  10.    
  11.        },  
  12.    
  13.        {  
  14.    
  15.               I2C_BOARD_INFO("wm8580", 0x1b),  
  16.    
  17.        },  
  18.    
  19. };  
  20.    
  21.    
  22.    
  23. /* I2C1 */  
  24.    
  25. static struct i2c_board_info i2c_devs1[] __initdata = {  
  26.    
  27. #ifdef CONFIG_VIDEO_TV20  
  28.    
  29.        {  
  30.    
  31.               I2C_BOARD_INFO("s5p_ddc", (0x74>>1)),  
  32.    
  33.        },  
  34.    
  35. #endif  
  36.    
  37. };  
  38.    
  39.    
  40.    
  41. /* I2C2 */  
  42.    
  43. static struct i2c_board_info i2c_devs2[] __initdata = {  
  44.    
  45. #ifdef CONFIG_REGULATOR_MAX8698  
  46.    
  47.        {  
  48.    
  49.               /* The address is 0xCC used since SRAD = 0 */  
  50.    
  51.               I2C_BOARD_INFO("max8698", (0xCC >> 1)),  
  52.    
  53.               .platform_data = &max8698_platform_data,  
  54.    
  55.        },  
  56.    
  57. #endif  

将i2c_board_info往里一填

  1. /* I2C1 */  
  2.    
  3. static struct i2c_board_info i2c_devs1[] __initdata = {  
  4.    
  5. #ifdef CONFIG_VIDEO_TV20  
  6.    
  7.        {  
  8.    
  9.               I2C_BOARD_INFO("s5p_ddc", (0x74>>1)),  
  10.    
  11.        },  
  12.    
  13.        {  
  14.    
  15.               I2C_BOARD_INFO("isa1200_1", 0x90>>1),/*这个是I2C设备的从机地址*/  
  16.    
  17.               .platform_data = &isa1200_1_pdata,  
  18.    
  19.        },  
  20.    
  21. #endif  
  22.    
  23. };  

这就算是把ISA1200挂接到了 I2C1上了,自己所做的事情也就完成了。接下来就是总线自己的事了:

首先它会把自己再加入到platform_device中,也就是注册到platform_device 总线上:

  1. static struct platform_device *smdkv210_devices[] __initdata = {  
  2.    
  3. ……  
  4.    
  5.        &s3c_device_i2c1,  
  6.    
  7. ……  
  8.    
  9. }  

再在设备初始化中加入I2C1总线 的i2c_register_board_info让它把总线I2C1上的设备(也就是注册到i2c_board_info i2c_devs1[] 上的所有设备)加入I2C1列表。

  1. static void __init smdkv210_machine_init(void)  
  2.    
  3. {  
  4.    
  5. ……         
  6.   
  7. i2c_register_board_info(1, i2c_devs1, ARRAY_SIZE(i2c_devs1));  
  8.    
  9. ……  
  10.    
  11. }  


-----------------------------

 

下面再说说GPIO模拟I2C实现驱动加载:

这里最重要的当然是成功的注册一个i2c_gpio_w380:

首先是找到CLK和SDA对应GPIO口:

CLK:GPA1[3]

SDA:GPA1[2]

然后建立i2c-gpio的platform_device结构体:

  1. static struct i2c_gpio_platform_data i2c_gpio_w380_data = {  
  2.    
  3.         .scl_pin  = S5PV210_GPA1(3),   
  4.   
  5.         .sda_pin  = S5PV210_GPA1(2),  
  6.    
  7. };  
  8.    
  9.    
  10.    
  11. static struct platform_device i2c_gpio_w380= {  
  12.    
  13.         .name   = "i2c-gpio",/*这个名字要和I2c-gpio.c里platform_driver里的名字要一致,换句话说这个gpio的i2c要用的driver是I2c-gpio中实现的定义的*/  
  14.    
  15.         .id   = 3,/*这个编号要顺系统原有的0,1,2写下来,再有一个要用4,依此递推*/  
  16.    
  17.         .dev = {  
  18.    
  19.          .platform_data = &i2c_gpio_w380_data,  
  20.    
  21.         },  
  22.    
  23. };  
  24.    
  25.    

完成了这些也就是完成了将两个GPIO口注册为一个I2C总线的工作。

接下来就和板子自己带的I2C实现驱动加载的方法一样了:

首先也是在板文件中建立ISA1200的信息:

  1. static int isa1200_power(int on)  
  2.    
  3. {       
  4.   
  5.        if(on){  
  6.    
  7.               gpio_direction_output(S5PV210_GPJ3(1), 1);  
  8.    
  9.               gpio_direction_output(S5PV210_GPJ3(0), 1);  
  10.    
  11.        }else{  
  12.    
  13.               gpio_direction_output(S5PV210_GPJ3(1), 0);  
  14.    
  15.               gpio_direction_output(S5PV210_GPJ3(0), 0);  
  16.    
  17.        }  
  18.    
  19.        return 0;  
  20.    
  21. }  
  22.    
  23.    
  24.    
  25. static struct isa1200_platform_data isa1200_1_pdata = {  
  26.    
  27.        .name = "isa1200",  
  28.    
  29.        .power_on = isa1200_power,  
  30.    
  31.        .pwm_ch_id = 1,   
  32.   
  33.        .hap_en_gpio = S5PV210_GPH3(1),  
  34.    
  35.        .max_timeout = 60000,  
  36.    
  37. };  
  38.    
  39.    
  40.    
  41. static void isa1200_init(void)  
  42.    
  43. {  
  44.    
  45.        gpio_direction_output(S5PV210_GPJ3(1), 1);  
  46.    
  47.        gpio_direction_output(S5PV210_GPJ3(0), 1);  
  48.    
  49.          
  50.   
  51.        /*i2c_register_board_info(3, isa1200_board_info, 
  52.   
  53.               ARRAY_SIZE(isa1200_board_info));*/  
  54.    
  55.        return;  
  56.    
  57. }  

以及i2c_board_info结构体:

  1. {  
  2.   
  3.           I2C_BOARD_INFO("isa1200_1", 0x90>>1),/*这个是I2C设备的从机地址*/  
  4.   
  5.           .platform_data = &isa1200_1_pdata,  
  6.   
  7.    },  

然后在i2c_gpio_w380总线中加入一条isa1200的i2c_board_info[] 

  1. /* I2C-GPIO*/  
  2.    
  3. static struct i2c_board_info i2c_devs3[] __initdata= {  
  4.    
  5.        {  
  6.    
  7.               I2C_BOARD_INFO("isa1200_1", 0x90>>1),  
  8.    
  9.               .platform_data = &isa1200_1_pdata,  
  10.    
  11.        },  
  12.    
  13. };  

这就算是把ISA1200挂接到了i2c_gpio_w380上了,自己所做的事情也就完成了。接下来就是总线自己的事了:

首先它会把自己i2c_gpio_w380再加入到platform_device中,也就是注册到platform_device 总线上:

  1. static struct platform_device *smdkv210_devices[] __initdata = {  
  2.    
  3. ……  
  4.    
  5.        &i2c_gpio_w380,  
  6.    
  7. ……  
  8.    
  9. }  

再在设备初始化中加入i2c_gpio_w380总线的i2c_register_board_info让它把总线i2c_gpio_w380上的设备(也就是注册到i2c_board_info i2c_devs3[] 上的所有设备)加入i2c_gpio_w380的列表。

  1. static void __init smdkv210_machine_init(void)  
  2.    
  3. {  
  4.    
  5. ……  
  6.    
  7.        i2c_register_board_info(3, i2c_devs3, ARRAY_SIZE(i2c_devs3));  
  8.    
  9. ……  
  10.    
  11. }  

以上也就是完了把一个设备ISA1200挂接在GPIO模拟的I2C总线i2c_gpio_w380上了。到这里设备ISA1200的设备文件isa1200.c里就可以通过调用i2c的i2c_smbus_write_byte_data,i2c_smbus_read_byte_data等函数了。

 

如又要把两个GPIO口再做成I2C总线注册成一个i2c_gpio_w380_1:

首先是找到CLK和SDA对应GPIO口:

CLK:GPC0[1]

SDA:GPC0[2],

可以这样做:

  1. static struct i2c_gpio_platform_data i2c_gpio_w380_1_data= {  
  2.    
  3.        .sda_pin = S5PV210_GPC0(2),  
  4.    
  5.        .scl_pin = S5PV210_GPC0(1),  
  6.    
  7. };  
  8.    
  9. static struct platform_device i2c_gpio_w380_1= {  
  10.    
  11.        .name             = "i2c-gpio",/*还是用了i2c-gpio的驱动*/  
  12.    
  13.        .id          = 4, /* 上面注册了3,顺延到了4*/  
  14.    
  15.        .dev = {  
  16.    
  17.               .platform_data = &i2c_gpio_w380_1_data,  
  18.    
  19.        }  
  20.    
  21. };      
  22.   
  23. static struct i2c_board_info i2c_devs4[] __initdata= {  
  24.    
  25.        {  
  26.    
  27.               I2C_BOARD_INFO("al3000", ADDRESS),  
  28.    
  29.        },  
  30.    
  31. };  
  32.    
  33.    
  34.    
  35. static struct platform_device *smdkv210_devices[] __initdata = {  
  36.    
  37. ……  
  38.    
  39.        &i2c_gpio_w380_1,  
  40.    
  41. ……  
  42.    
  43. }  
  44.    
  45. static void __init smdkv210_machine_init(void)  
  46.    
  47. {  
  48.    
  49. ……  
  50.    
  51.        i2c_register_board_info(4, i2c_devs4, ARRAY_SIZE(i2c_devs4));  
  52.    
  53. ……  
  54.    
  55. }  
  56.    
  57.    

另还有一种是用gpio来模拟i2c时序,它就是单独在设备文件中完成的!如:

  1. /*****stop previous seccession and generate START seccession *********************/  
  2.    
  3. void I2C_start(void)               
  4.   
  5. { set_I2C_SCL_high();           
  6.   
  7.   set_I2C_SDA_low();  
  8.    
  9.   set_I2C_SDA_output();            // SDA = 0;  
  10.    
  11.   set_I2C_SDA_high();        // SDA = 1, Stop previous I2C r/w action  
  12.    
  13.   set_I2C_SDA_low();         // I2C Start Condition   
  14.   
  15. }  
  16.    
  17.    
  18.    
  19. /***************** generate I2C Repeat Start **************/  
  20.    
  21. void RepeatStart(void)  
  22.    
  23. { set_I2C_SCL_low();  
  24.    
  25.   set_I2C_SDA_high();  
  26.    
  27.   set_I2C_SDA_output();  
  28.    
  29.   set_I2C_SCL_high();  
  30.    
  31.   set_I2C_SDA_low();           
  32.   
  33. }  
  34.    
  35.    
  36.    
  37. /********************* generate I2C STOP ******************/  
  38.    
  39. void I2C_stop(void)               
  40.   
  41. { set_I2C_SCL_low();  
  42.    
  43.   set_I2C_SDA_low();  
  44.    
  45.   set_I2C_SDA_output();  
  46.    
  47.   set_I2C_SCL_high();  
  48.    
  49.   set_I2C_SDA_high();  
  50.    
  51. }     
  52.   
  53.    
  54.    
  55. /*************** Test Slave Device Acknowledge status ********************/  
  56.    
  57. unsigned char slave_ack(void)  
  58.    
  59. { set_I2C_SDA_input();              // SDA Input            
  60.   
  61.   set_I2C_SCL_high();        // Test Acknowledge  
  62.    
  63.   if (I2C_SDA_PIN)  
  64.    
  65.     return(FALSE);                    // return error if no acknowledge from slave device  
  66.    
  67.   else  
  68.    
  69.     return(TRUE);                     // return ok if got acknowledge from slave device  
  70.    
  71. }   
  72.   
  73.    
  74.    
  75. /*************** send Ack to Slave Device ********************/  
  76.    
  77. void master_ack(void)            
  78.   
  79. { set_I2C_SDA_high();  
  80.    
  81.   set_I2C_SDA_output();  
  82.    
  83.   set_I2C_SCL_high();  
  84.    
  85. }  


 直接用GPIO口模拟I2C时序和利用内核模块i2c-gpio虚拟i2c总线的区别:

1GPIO口模拟I2C时序不需要在系统启动时注册I2C总线,只需要在I2C设备驱动中单独实现。i2c-gpio模块虚拟i2c总线需要在系统启动时注册新的I2C总线,并将i2c设备挂载到新的i2c总线,涉及的范围较广。

2GPIO口模拟I2C时序,代码操作较繁琐,且不方便挂载多个i2c设备。i2c-gpio模块可以完全模拟i2c总线,可以挂载多个设备。

3 i2c 读写操作时,用 GPIO 口模拟 I2C 时序需要每次根据读 / 写操作发送器件地址 <<1+1/0 ,然后再发送寄存器地址。 i2c-gpio 模块相当于直接在 i2c 总线上操作,在系统启动挂载 i2c 设备时已经告诉了 i2c 总线它的地址,在该设备自己的驱动中,只需要通过 i2c_add_driver 操作即可以得到其地址等诸多信息,读写操作只需要发送寄存器地址即可
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值