S5PV210 I2C裝置驅動
當然這個工作都是在板檔案中進行的。以mach-s5pv210.c為例來說一下:
先說下用板子自己帶的I2C實現驅動載入:
首先在板檔案中建立ISA1200的資訊:
- staticint 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;
- }
- staticstruct 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,
- };
- staticvoid isa1200_init(
- {
- 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 */
- staticstruct i2c_board_info i2c_devs0[] __initdata = {
- {
- I2C_BOARD_INFO("act8937", 0x5B),
- .platform_data = &act8937_platform_data,
- },
- {
- I2C_BOARD_INFO("wm8580", 0x1b),
- },
- };
- /* I2C1 */
- staticstruct i2c_board_info i2c_devs1[] __initdata = {
- #ifdef CONFIG_VIDEO_TV20
- {
- I2C_BOARD_INFO("s5p_ddc", (0x74>>1)),
- },
- #endif
- };
- /* I2C2 */
- staticstruct 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 */
- staticstruct 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 總線上:
- staticstruct platform_device *smdkv210_devices[] __initdata = {
- ……
- &s3c_device_i2c1,
- ……
- }
再在裝置初始化中加入I2C1匯流排 的i2c_register_board_info讓它把匯流排I2C1上的裝置(也就是註冊到i2c_board_info i2c_devs1[] 上的所有裝置)加入I2C1列表。
- staticvoid __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結構體:
- staticstruct i2c_gpio_platform_data i2c_gpio_w380_data = {
- .scl_pin = S5PV210_GPA1(3),
- .sda_pin = S5PV210_GPA1(2),
- };
- staticstruct 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的資訊:
- staticint 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;
- }
- staticstruct 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,
- };
- staticvoid 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*/
- staticstruct 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 總線上:
- staticstruct 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的列表。
- staticvoid __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],
可以這樣做:
- staticstruct i2c_gpio_platform_data i2c_gpio_w380_1_data= {
- .sda_pin = S5PV210_GPC0(2),
- .scl_pin = S5PV210_GPC0(1),
- };
- staticstruct platform_device i2c_gpio_w380_1= {
- .name = "i2c-gpio",/*還是用了i2c-gpio的驅動*/
- .id = 4, /* 上面註冊了3,順延到了4*/
- .dev = {
- .platform_data = &i2c_gpio_w380_1_data,
- }
- };
- staticstruct i2c_board_info i2c_devs4[] __initdata= {
- {
- I2C_BOARD_INFO("al3000", ADDRESS),
- },
- };
- staticstruct platform_device *smdkv210_devices[] __initdata = {
- ……
- &i2c_gpio_w380_1,
- ……
- }
- staticvoid __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操作即可以得到其地址等諸多資訊,讀寫操作只需要傳送暫存器地址即可