I2C設備移植

轉自http://blog.csdn.net/wtao616/article/details/6147740

  前面轉載了很多關於i2c的文章,做完一個項目了,也該自己寫點i2c心得了,我這個可是純應用角度的,想看原理的,去看轉載的文章,人家寫的好多了!

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

      

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

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

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

static int isa1200_power(int on)

{    

       if(on){

              gpio_direction_output(S5PV210_GPJ3(1), 1);

              gpio_direction_output(S5PV210_GPJ3(0), 1);

       }else{

              gpio_direction_output(S5PV210_GPJ3(1), 0);

              gpio_direction_output(S5PV210_GPJ3(0), 0);

       }

       return 0;

}

 

static struct isa1200_platform_data isa1200_1_pdata = {

       .name = "isa1200",

       .power_on = isa1200_power,

       .pwm_ch_id = 1,

       .hap_en_gpio = S5PV210_GPH3(1),

       .max_timeout = 60000,

};

 

static void isa1200_init(void)

{

       gpio_direction_output(S5PV210_GPJ3(7), 1);

       gpio_direction_output(S5PV210_GPJ3(1), 1);

       gpio_direction_output(S5PV210_GPJ3(0), 1);

      

       /*i2c_register_board_info(3, isa1200_board_info,

              ARRAY_SIZE(isa1200_board_info));*/

       return;

}

以及i2c_board_info結構體:

       {

              I2C_BOARD_INFO("isa1200_1", 0x90>>1),/*這個是I2C設備的從機地址*/

              .platform_data = &isa1200_1_pdata,

       },

 

然後在以下三個I2C總線中找到一條如i2c_devs1[]

/* I2C0 */

static struct i2c_board_info i2c_devs0[] __initdata = {

       {

              I2C_BOARD_INFO("act8937", 0x5B),

              .platform_data = &act8937_platform_data,

       },

       {

              I2C_BOARD_INFO("wm8580", 0x1b),

       },

};

 

/* I2C1 */

static struct i2c_board_info i2c_devs1[] __initdata = {

#ifdef CONFIG_VIDEO_TV20

       {

              I2C_BOARD_INFO("s5p_ddc", (0x74>>1)),

       },

#endif

};

 

/* I2C2 */

static struct i2c_board_info i2c_devs2[] __initdata = {

#ifdef CONFIG_REGULATOR_MAX8698

       {

              /* The address is 0xCC used since SRAD = 0 */

              I2C_BOARD_INFO("max8698", (0xCC >> 1)),

              .platform_data = &max8698_platform_data,

       },

#endif

將i2c_board_info往裏一填

/* I2C1 */

static struct i2c_board_info i2c_devs1[] __initdata = {

#ifdef CONFIG_VIDEO_TV20

       {

              I2C_BOARD_INFO("s5p_ddc", (0x74>>1)),

       },

       {

              I2C_BOARD_INFO("isa1200_1", 0x90>>1),/*這個是I2C設備的從機地址*/

              .platform_data = &isa1200_1_pdata,

       },

#endif

};

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

 

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

static struct platform_device *smdkv210_devices[] __initdata = {

……

       &s3c_device_i2c1,

……

}

 

再在設備初始化中加入I2C1總線 的i2c_register_board_info讓它把總線I2C1上的設備(也就是註冊到i2c_board_info i2c_devs1[] 上的所有設備)加入I2C1列表。

 

static void __init smdkv210_machine_init(void)

{

……      

i2c_register_board_info(1, i2c_devs1, ARRAY_SIZE(i2c_devs1));

……

}

 

 

 

 

 

 

 

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

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

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

CLK:GPA1[3]

SDA:GPA1[2]

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

 

static struct i2c_gpio_platform_data i2c_gpio_w380_data = {

        .scl_pin  = S5PV210_GPA1(3),

        .sda_pin  = S5PV210_GPA1(2),

};

 

static struct platform_device i2c_gpio_w380= {

        .name   = "i2c-gpio",/*這個名字要和I2c-gpio.c裏platform_driver裏的名字要一致,換句話說這個gpio的i2c要用的driver是I2c-gpio中實現的定義的*/

        .id   = 3,/*這個編號要順系統原有的0,1,2寫下來,再有一個要用4,依此遞推*/

        .dev = {

         .platform_data = &i2c_gpio_w380_data,

        },

};

 

完成了這些也就是完成了將兩個GPIO口註冊爲一個I2C總線的工作。

 

 

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

 

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

static int isa1200_power(int on)

{    

       if(on){

              gpio_direction_output(S5PV210_GPJ3(1), 1);

              gpio_direction_output(S5PV210_GPJ3(0), 1);

       }else{

              gpio_direction_output(S5PV210_GPJ3(1), 0);

              gpio_direction_output(S5PV210_GPJ3(0), 0);

       }

       return 0;

}

 

static struct isa1200_platform_data isa1200_1_pdata = {

       .name = "isa1200",

       .power_on = isa1200_power,

       .pwm_ch_id = 1,

       .hap_en_gpio = S5PV210_GPH3(1),

       .max_timeout = 60000,

};

 

static void isa1200_init(void)

{

       gpio_direction_output(S5PV210_GPJ3(1), 1);

       gpio_direction_output(S5PV210_GPJ3(0), 1);

      

       /*i2c_register_board_info(3, isa1200_board_info,

              ARRAY_SIZE(isa1200_board_info));*/

       return;

}

以及i2c_board_info結構體:

       {

              I2C_BOARD_INFO("isa1200_1", 0x90>>1),/*這個是I2C設備的從機地址*/

              .platform_data = &isa1200_1_pdata,

       },

 

然後在i2c_gpio_w380總線中加入一條isa1200的i2c_board_info[]

/* I2C-GPIO*/

static struct i2c_board_info i2c_devs3[] __initdata= {

       {

              I2C_BOARD_INFO("isa1200_1", 0x90>>1),

              .platform_data = &isa1200_1_pdata,

       },

};

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

 

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

 

static struct platform_device *smdkv210_devices[] __initdata = {

……

       &i2c_gpio_w380,

……

}

再在設備初始化中加入i2c_gpio_w380總線的i2c_register_board_info讓它把總線i2c_gpio_w380上的設備(也就是註冊到i2c_board_info i2c_devs3[] 上的所有設備)加入i2c_gpio_w380的列表。

static void __init smdkv210_machine_init(void)

{

……

       i2c_register_board_info(3, i2c_devs3, ARRAY_SIZE(i2c_devs3));

……

}

以上也就是完了把一個設備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],

可以這樣做:

 

static struct i2c_gpio_platform_data i2c_gpio_w380_1_data= {

       .sda_pin = S5PV210_GPC0(2),

       .scl_pin = S5PV210_GPC0(1),

};

static struct platform_device i2c_gpio_w380_1= {

       .name             = "i2c-gpio",/*還是用了i2c-gpio的驅動*/

       .id          = 4, /* 上面註冊了3,順延到了4*/

       .dev = {

              .platform_data = &i2c_gpio_w380_1_data,

       }

};   

static struct i2c_board_info i2c_devs4[] __initdata= {

       {

              I2C_BOARD_INFO("al3000", ADDRESS),

       },

};

 

static struct platform_device *smdkv210_devices[] __initdata = {

……

       &i2c_gpio_w380_1,

……

}

static void __init smdkv210_machine_init(void)

{

……

       i2c_register_board_info(4, i2c_devs4, ARRAY_SIZE(i2c_devs4));

……

}

 

 

 

另還有一種是用gpio來模擬i2c時序,它就是單獨在設備文件中完成的!如:

 

/*****stop previous seccession and generate START seccession *********************/

void I2C_start(void)            

{ set_I2C_SCL_high();        

  set_I2C_SDA_low();

  set_I2C_SDA_output();            // SDA = 0;

  set_I2C_SDA_high();        // SDA = 1, Stop previous I2C r/w action

  set_I2C_SDA_low();         // I2C Start Condition

}

 

/***************** generate I2C Repeat Start **************/

void RepeatStart(void)

{ set_I2C_SCL_low();

  set_I2C_SDA_high();

  set_I2C_SDA_output();

  set_I2C_SCL_high();

  set_I2C_SDA_low();        

}

 

/********************* generate I2C STOP ******************/

void I2C_stop(void)            

{ set_I2C_SCL_low();

  set_I2C_SDA_low();

  set_I2C_SDA_output();

  set_I2C_SCL_high();

  set_I2C_SDA_high();

}  

 

/*************** Test Slave Device Acknowledge status ********************/

unsigned char slave_ack(void)

{ set_I2C_SDA_input();              // SDA Input         

  set_I2C_SCL_high();        // Test Acknowledge

  if (I2C_SDA_PIN)

    return(FALSE);                    // return error if no acknowledge from slave device

  else

    return(TRUE);                     // return ok if got acknowledge from slave device

}

 

/*************** send Ack to Slave Device ********************/

void master_ack(void)         

{ set_I2C_SDA_high();

  set_I2C_SDA_output();

  set_I2C_SCL_high();

}

 

 

直接用GPIO口模擬I2C時序和利用內核模塊i2c-gpio虛擬i2c總線的區別:

1 GPIO口模擬I2C時序不需要在系統啓動時註冊I2C總線,只需要在I2C設備驅動中單獨實現。i2c-gpio模塊虛擬i2c總線需要在系統啓動時註冊新的I2C總線,並將i2c設備掛載到新的i2c總線,涉及的範圍較廣。

2 GPIO口模擬I2C時序,代碼操作較繁瑣,且不方便掛載多個i2c設備。i2c-gpio模塊可以完全模擬i2c總線,可以掛載多個設備。

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


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章