核心下gpio模擬i2c驅動修改

qq_26620783發表於2020-09-27

Linux核心很多驅動都使用到I2C子系統。EEPROM、RTC,電池,tp等。

inux核心的i2c-gpio是使用GPIO模擬I2C協議的驅動,在核心中已經實現了,我們要做的只需要配置2根GPIO即可。

GPIO模擬I2C協議的驅動位於drivers/i2c/busses目錄。驅動名稱為“i2c-gpio”,驅動檔案為drivers/i2c/busses/i2c-gpio.c

1. GPIO支援要先保證是選上的

2.  先配置核心

Device Drivers->
    I2C support  --->
        I2C Hardware Bus support  --->
            <*> GPIO-based bitbanging I2C 

3. 修改核心檔案

static struct i2c_gpio_platform_data da850_gpio_i2c_pdata = {
	.sda_pin	= DA850_GPIO_I2C_SDA,   //通用的gpio引腳
	.scl_pin    = DA850_GPIO_I2C_SCL,   //通用的gpio引腳
	.udelay		= 10,			/* 50 KHz */
	.timeout 	= 100,
};

static struct platform_device da850_gpio_i2c = {
	.name		= "i2c-gpio",
	.id		    = 2,    //生成 i2c-gpio.2   
	.dev		= {
		.platform_data	= &da850_gpio_i2c_pdata,
	},
};

平臺初始化

static __init void da850_evm_init(void)
{
	..........
    ..........

	/*
	 * Though bootloader takes care to set emif clock at allowed
	 * possible rate. Kernel needs to reconfigure this rate to
	 * support platforms requiring fixed emif clock rate.
	 */
	ret = da850_set_emif_clk_rate();
	if (ret)
		pr_warning("da850_evm_init: Failed to set rate of pll0_sysclk3/emif clock: %d\n",
				ret);

	ret = da850_register_edma(da850_edma_rsv);
	if (ret)
		pr_warning("da850_evm_init: edma registration failed: %d\n",
				ret);

	ret = davinci_cfg_reg_list(da850_i2c0_pins);
	if (ret)
		pr_warning("da850_evm_init: i2c0 mux setup failed: %d\n",
				ret);


	platform_device_register(&da850_gpio_i2c);   //註冊平臺裝置

	ret = da8xx_register_i2c(0, &da850_evm_i2c_pdata);
	if (ret) {
	    pr_warning("da850_evm_init: i2c0 mux setup failed: %d\n",
		    ret);
	    return;
	}
    .........
	i2c_register_board_info(2, da850_gpio_i2c_devices,     //gpio-i2c
                        ARRAY_SIZE(da850_gpio_i2c_devices));
    ........
}

i2c_board_info :這是個列舉型別的陣列,把要加入的GPIO加入到這個陣列裡面。

static struct i2c_board_info __initdata da850_gpio_i2c_devices[] = {
	{                
		I2C_BOARD_INFO("bq20z75", 0x0b),
    },
};

這樣就實現了gpio-i2c功能了,但是在使用前需要先確保當期使用的io口是作為GPIO使用的,我就踩了這個坑。

對於引腳配置成GPIO過程

OMAP-L138 C6000 DSP+ARM Processor Technical Reference Manual (Rev.A).pdf 244頁有相應的管腳配置說明

引腳配置複用的位置在\linux-3.3\arch\arm\mach-davinci\da850.c的 結構體static const struct mux_config da850_pins[] = {}

static const struct mux_config da850_pins[] = {
    #ifdef CONFIG_DAVINCI_MUX
    ..............
    MUX_CFG(DA850, EMA_A_22,    10,     4,    15,    1,    false)
    MUX_CFG(DA850, EMA_A_20,   10,     12,    15,    1,    false)

    ...............
}

引數說明:  MUX_CFG(DA850, EMA_A_22,    4,    24,    15,    2,    false)

MUX_CFG :這個巨集在arch/arm/mach-davinci/mux.h定義

#define MUX_CFG(soc, desc, muxreg, mode_offset, mode_mask, mux_mode, dbg)

DA850: 平臺

EMA_A_22: 管腳描述

4: 第4組pinmux暫存器(引腳複用暫存器的名稱,展開為 PINMUX4 )

24: 在暫存器PINMUX4中的bit24到bit27位,相應位的偏移值

15: 就是pinmux相應位全為1的值(掩碼值)

2 : mux_mode(設定這個複用暫存器的GPIO引腳功能)

現在需要將da850.c使用為EMA_A的預設的引腳複用為GPIO引腳,檢視管腳配置pdf說明是否支援,

//需要注意將使用該暫存器複用功能的地方全註釋,不然編譯會報錯

da850.c , include/mach/mux.h , board-da850-evm.c

  管腳描述改為當前的修改後的描述,我當前修改描述為GPIO4_4

 

 

///

管腳配置完成修改完成後對核心修改驗證方式

用i2cdetect檢測有幾組i2c匯流排在系統上,輸入:./i2cdetect  -l

用i2cdetect檢測掛載在i2c匯流排上器件,

i2cdetect -r -y 2(檢測i2c-1上的掛載情況)

 

由上圖可知 0x0b地址有掛載。而這幾個分別是0x0b(bq20z75)

i2cset -f -y 1 0x0b 0x09 0x3f (設定i2c-1上0x0b 器件的0x09 暫存器值為0x3f)

i2cget -f -y 1 0x0b 0x09     (讀取i2c-1上0x0b 器件的0x09     暫存器值)

 

i2cdetect -r -y 2 這個命令能確定i2c-2匯流排上那個地址是掛載裝置的

此方式驗證i2c功能的從裝置地址是否正確,如果寫入的從裝置地址不是實際地址則寫入時是不生效的

i2c功能資料讀寫

//test.c

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <linux/i2c.h>
#include <sys/ioctl.h>
#include <linux/i2c-dev.h>
#include <errno.h>
#include <string.h>

unsigned char i2c_read(unsigned char device_addr, unsigned char sub_addr, unsigned char *buff, int ByteNo)
{
    int fd, ret, i;
    unsigned char buftmp[32];
    struct i2c_rdwr_ioctl_data i2c_data;
    const char *i2c_dev = "/dev/i2c-2";

    device_addr >>= 1;
    //init
    fd = open(i2c_dev, O_RDWR);
    if (fd<0)
    {
        printf("not have /dev/i2c-1 t\r\n");
        return -1;
    }

    i2c_data.nmsgs = 2;
    i2c_data.msgs = (struct i2c_msg *)malloc(i2c_data.nmsgs *sizeof(struct i2c_msg));
    if (i2c_data.msgs == NULL)
    {
        printf("malloc error");
        close(fd);
        return -1;
    }

    ioctl(fd, I2C_TIMEOUT, 1);
    ioctl(fd, I2C_RETRIES, 2);
    寫i2c暫存器直接寫,讀i2c暫存器之前需要先向要讀取的暫存器寫,我這個只有讀操作

    //write reg
    memset(buftmp, 0, 32);
    buftmp[0] = sub_addr;
    i2c_data.msgs[0].len = 1;
    i2c_data.msgs[0].addr = device_addr;
    i2c_data.msgs[0].flags = 0;     // 0: write 1:read
    i2c_data.msgs[0].buf = buftmp;
    //read data
    i2c_data.msgs[1].len = ByteNo;
    i2c_data.msgs[1].addr = device_addr;
    i2c_data.msgs[1].flags = 1;     // 0: write 1:read
    i2c_data.msgs[1].buf = buff;


    ret = ioctl(fd, I2C_RDWR, (unsigned long)&i2c_data);
    if (ret < 0)
    {
        printf("read data %x %x error\r\n", device_addr, sub_addr);
        close(fd);
        free(i2c_data.msgs);
        return 1;
    }
    free(i2c_data.msgs);
    close(fd);

    printf("i2c_read from: 0x%02x:\t",buftmp[0]);

    //讀取的是2byte資料,先低8位,後高8位,拼接起來就是實際資料,根據需要對資料進行操作
    int data = buff[1] *256 + buff[0];  
    printf(" %d\n",data);
    for (i = 0; i < ByteNo; i++)
    {
        printf(" 0x%02x",buff[i]);
    }
    printf("\n");
    return 0;
}

int main(void)  
{
    unsigned char buf[10]= {0};
    i2c_read(0x16, 0x16, buf, 2);     //10 is on charging, 50 is not charging
}

 

相關文章