1. 程式人生 > >S5PV210 I2C裝置驅動

S5PV210 I2C裝置驅動

對於一個i2c裝置來說,其裝置檔案是最簡單也是最複雜的,說它簡單是因為很裝置廠商會提供linux下的程式碼,這樣就簡單了;但是也有很多廠商它不提供或不完整提供linux下的程式碼,這樣的話當然就複雜了。那麼這個我現在這裡就不說了,下面說說做了幾個I2C裝置(以ISA1200為例)後發現,不管裝置檔案如何總是要自己來做的一些事情,這大概就是所謂的移植吧。

       當然這個工作都是在板檔案中進行的。以mach-s5pv210.c為例來說一下:

       先說下用板子自己帶的I2C實現驅動載入:

       首先在板檔案中建立ISA1200的資訊:

  1. staticint isa1200_power(int on)  
  2. {       
  3.        if(on){  
  4.               gpio_direction_output(S5PV210_GPJ3(1), 1);  
  5.               gpio_direction_output(S5PV210_GPJ3(0), 1);  
  6.        }else{  
  7.               gpio_direction_output(S5PV210_GPJ3(1), 0);  
  8.               gpio_direction_output(S5PV210_GPJ3(0), 0);  
  9.        }  
  10.        return 0;  
  11. }  
  12. staticstruct isa1200_platform_data isa1200_1_pdata = {  
  13.        .name = "isa1200",  
  14.        .power_on = isa1200_power,  
  15.        .pwm_ch_id = 1,   
  16.        .hap_en_gpio = S5PV210_GPH3(1),  
  17.        .max_timeout = 60000,  
  18. };  
  19. staticvoid isa1200_init(
    void)  
  20. {  
  21.        gpio_direction_output(S5PV210_GPJ3(7), 1);  
  22.        gpio_direction_output(S5PV210_GPJ3(1), 1);  
  23.        gpio_direction_output(S5PV210_GPJ3(0), 1);  
  24.        /*i2c_register_board_info(3, isa1200_board_info, 
  25.               ARRAY_SIZE(isa1200_board_info));*/
  26.        return;  
  27. }  

以及i2c_board_info結構體:

  1. {  
  2.               I2C_BOARD_INFO("isa1200_1", 0x90>>1),/*這個是I2C裝置的從機地址*/
  3.               .platform_data = &isa1200_1_pdata,  
  4.        },  

然後在以下三個I2C匯流排中找到一條如i2c_devs1[]

  1. /* I2C0 */
  2. staticstruct i2c_board_info i2c_devs0[] __initdata = {  
  3.        {  
  4.               I2C_BOARD_INFO("act8937", 0x5B),  
  5.               .platform_data = &act8937_platform_data,  
  6.        },  
  7.        {  
  8.               I2C_BOARD_INFO("wm8580", 0x1b),  
  9.        },  
  10. };  
  11. /* I2C1 */
  12. staticstruct i2c_board_info i2c_devs1[] __initdata = {  
  13. #ifdef CONFIG_VIDEO_TV20
  14.        {  
  15.               I2C_BOARD_INFO("s5p_ddc", (0x74>>1)),  
  16.        },  
  17. #endif
  18. };  
  19. /* I2C2 */
  20. staticstruct i2c_board_info i2c_devs2[] __initdata = {  
  21. #ifdef CONFIG_REGULATOR_MAX8698
  22.        {  
  23.               /* The address is 0xCC used since SRAD = 0 */
  24.               I2C_BOARD_INFO("max8698", (0xCC >> 1)),  
  25.               .platform_data = &max8698_platform_data,  
  26.        },  
  27. #endif

將i2c_board_info往裡一填

  1. /* I2C1 */
  2. staticstruct i2c_board_info i2c_devs1[] __initdata = {  
  3. #ifdef CONFIG_VIDEO_TV20
  4.        {  
  5.               I2C_BOARD_INFO("s5p_ddc", (0x74>>1)),  
  6.        },  
  7.        {  
  8.               I2C_BOARD_INFO("isa1200_1", 0x90>>1),/*這個是I2C裝置的從機地址*/
  9.               .platform_data = &isa1200_1_pdata,  
  10.        },  
  11. #endif
  12. };  

這就算是把ISA1200掛接到了 I2C1上了,自己所做的事情也就完成了。接下來就是匯流排自己的事了:

首先它會把自己再加入到platform_device中,也就是註冊到platform_device 總線上:

  1. staticstruct platform_device *smdkv210_devices[] __initdata = {  
  2. ……  
  3.        &s3c_device_i2c1,  
  4. ……  
  5. }  

再在裝置初始化中加入I2C1匯流排 的i2c_register_board_info讓它把匯流排I2C1上的裝置(也就是註冊到i2c_board_info i2c_devs1[] 上的所有裝置)加入I2C1列表。

  1. staticvoid __init smdkv210_machine_init(void)  
  2. {  
  3. ……         
  4. i2c_register_board_info(1, i2c_devs1, ARRAY_SIZE(i2c_devs1));  
  5. ……  
  6. }  


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

下面再說說GPIO模擬I2C實現驅動載入:

這裡最重要的當然是成功的註冊一個i2c_gpio_w380:

首先是找到CLK和SDA對應GPIO口:

CLK:GPA1[3]

SDA:GPA1[2]

然後建立i2c-gpio的platform_device結構體:

  1. staticstruct i2c_gpio_platform_data i2c_gpio_w380_data = {  
  2.         .scl_pin  = S5PV210_GPA1(3),   
  3.         .sda_pin  = S5PV210_GPA1(2),  
  4. };  
  5. staticstruct platform_device i2c_gpio_w380= {  
  6.         .name   = "i2c-gpio",/*這個名字要和I2c-gpio.c裡platform_driver裡的名字要一致,換句話說這個gpio的i2c要用的driver是I2c-gpio中實現的定義的*/
  7.         .id   = 3,/*這個編號要順系統原有的0,1,2寫下來,再有一個要用4,依此遞推*/
  8.         .dev = {  
  9.          .platform_data = &i2c_gpio_w380_data,  
  10.         },  
  11. };  

完成了這些也就是完成了將兩個GPIO口註冊為一個I2C匯流排的工作。

接下來就和板子自己帶的I2C實現驅動載入的方法一樣了:

首先也是在板檔案中建立ISA1200的資訊:

  1. staticint isa1200_power(int on)  
  2. {       
  3.        if(on){  
  4.               gpio_direction_output(S5PV210_GPJ3(1), 1);  
  5.               gpio_direction_output(S5PV210_GPJ3(0), 1);  
  6.        }else{  
  7.               gpio_direction_output(S5PV210_GPJ3(1), 0);  
  8.               gpio_direction_output(S5PV210_GPJ3(0), 0);  
  9.        }  
  10.        return 0;  
  11. }  
  12. staticstruct isa1200_platform_data isa1200_1_pdata = {  
  13.        .name = "isa1200",  
  14.        .power_on = isa1200_power,  
  15.        .pwm_ch_id = 1,   
  16.        .hap_en_gpio = S5PV210_GPH3(1),  
  17.        .max_timeout = 60000,  
  18. };  
  19. staticvoid isa1200_init(void)  
  20. {  
  21.        gpio_direction_output(S5PV210_GPJ3(1), 1);  
  22.        gpio_direction_output(S5PV210_GPJ3(0), 1);  
  23.        /*i2c_register_board_info(3, isa1200_board_info, 
  24.               ARRAY_SIZE(isa1200_board_info));*/
  25.        return;  
  26. }  

以及i2c_board_info結構體:

  1. {  
  2.           I2C_BOARD_INFO("isa1200_1", 0x90>>1),/*這個是I2C裝置的從機地址*/
  3.           .platform_data = &isa1200_1_pdata,  
  4.    },  

然後在i2c_gpio_w380匯流排中加入一條isa1200的i2c_board_info[] 

  1. /* I2C-GPIO*/
  2. staticstruct i2c_board_info i2c_devs3[] __initdata= {  
  3.        {  
  4.               I2C_BOARD_INFO("isa1200_1", 0x90>>1),  
  5.               .platform_data = &isa1200_1_pdata,  
  6.        },  
  7. };  

這就算是把ISA1200掛接到了i2c_gpio_w380上了,自己所做的事情也就完成了。接下來就是匯流排自己的事了:

首先它會把自己i2c_gpio_w380再加入到platform_device中,也就是註冊到platform_device 總線上:

  1. staticstruct platform_device *smdkv210_devices[] __initdata = {  
  2. ……  
  3.        &i2c_gpio_w380,  
  4. ……  
  5. }  

再在裝置初始化中加入i2c_gpio_w380匯流排的i2c_register_board_info讓它把匯流排i2c_gpio_w380上的裝置(也就是註冊到i2c_board_info i2c_devs3[] 上的所有裝置)加入i2c_gpio_w380的列表。

  1. staticvoid __init smdkv210_machine_init(void)  
  2. {  
  3. ……  
  4.        i2c_register_board_info(3, i2c_devs3, ARRAY_SIZE(i2c_devs3));  
  5. ……  
  6. }  

以上也就是完了把一個裝置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. staticstruct i2c_gpio_platform_data i2c_gpio_w380_1_data= {  
  2.        .sda_pin = S5PV210_GPC0(2),  
  3.        .scl_pin = S5PV210_GPC0(1),  
  4. };  
  5. staticstruct platform_device i2c_gpio_w380_1= {  
  6.        .name             = "i2c-gpio",/*還是用了i2c-gpio的驅動*/
  7.        .id          = 4, /* 上面註冊了3,順延到了4*/
  8.        .dev = {  
  9.               .platform_data = &i2c_gpio_w380_1_data,  
  10.        }  
  11. };      
  12. staticstruct i2c_board_info i2c_devs4[] __initdata= {  
  13.        {  
  14.               I2C_BOARD_INFO("al3000", ADDRESS),  
  15.        },  
  16. };  
  17. staticstruct platform_device *smdkv210_devices[] __initdata = {  
  18. ……  
  19.        &i2c_gpio_w380_1,  
  20. ……  
  21. }  
  22. staticvoid __init smdkv210_machine_init(void)  
  23. {  
  24. ……  
  25.        i2c_register_board_info(4, i2c_devs4, ARRAY_SIZE(i2c_devs4));  
  26. ……  
  27. }  

另還有一種是用gpio來模擬i2c時序,它就是單獨在裝置檔案中完成的!如:

  1. /*****stop previous seccession and generate START seccession *********************/
  2. void I2C_start(void)               
  3. { set_I2C_SCL_high();           
  4.   set_I2C_SDA_low();  
  5.   set_I2C_SDA_output();            // SDA = 0;
  6.   set_I2C_SDA_high();        // SDA = 1, Stop previous I2C r/w action
  7.   set_I2C_SDA_low();         // I2C Start Condition 
  8. }  
  9. /***************** generate I2C Repeat Start **************/
  10. void RepeatStart(void)  
  11. { set_I2C_SCL_low();  
  12.   set_I2C_SDA_high();  
  13.   set_I2C_SDA_output();  
  14.   set_I2C_SCL_high();  
  15.   set_I2C_SDA_low();           
  16. }  
  17. /********************* generate I2C STOP ******************/
  18. void I2C_stop(void)               
  19. { set_I2C_SCL_low();  
  20.   set_I2C_SDA_low();  
  21.   set_I2C_SDA_output();  
  22.   set_I2C_SCL_high();  
  23.   set_I2C_SDA_high();  
  24. }     
  25. /*************** Test Slave Device Acknowledge status ********************/
  26. unsigned char slave_ack(void)  
  27. { set_I2C_SDA_input();              // SDA Input          
  28.   set_I2C_SCL_high();        // Test Acknowledge
  29.   if (I2C_SDA_PIN)  
  30.     return(FALSE);                    // return error if no acknowledge from slave device
  31.   else
  32.     return(TRUE);                     // return ok if got acknowledge from slave device
  33. }   
  34. /*************** send Ack to Slave Device ********************/
  35. void master_ack(void)            
  36. { set_I2C_SDA_high();  
  37.   set_I2C_SDA_output();  
  38.   set_I2C_SCL_high();  
  39. }  


 直接用GPIO口模擬I2C時序和利用核心模組i2c-gpio虛擬i2c匯流排的區別:

1GPIO口模擬I2C時序不需要在系統啟動時註冊I2C匯流排,只需要在I2C裝置驅動中單獨實現。i2c-gpio模組虛擬i2c匯流排需要在系統啟動時註冊新的I2C匯流排,並將i2c裝置掛載到新的i2c匯流排,涉及的範圍較廣。

2GPIO口模擬I2C時序,程式碼操作較繁瑣,且不方便掛載多個i2c裝置。i2c-gpio模組可以完全模擬i2c匯流排,可以掛載多個裝置。

3i2c讀寫操作時,用GPIO口模擬I2C時序需要每次根據讀/寫操作傳送器件地址<<1+1/0,然後再發送暫存器地址。i2c-gpio模組相當於直接在i2c總線上操作,在系統啟動掛載i2c裝置時已經告訴了i2c匯流排它的地址,在該裝置自己的驅動中,只需要通過i2c_add_driver操作即可以得到其地址等諸多資訊,讀寫操作只需要傳送暫存器地址即可