Linux内核驱动:gpio模拟i2c驱动

目录

  • 一、配置
    • 1. config配置
    • 2. dts配置
  • 二、代码分析
  • 三、应用读写

本文基于linux 4.19 版本内核进行分析。
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
Linux内核驱动:gpio模拟i2c驱动_第1张图片

一、配置

1. config配置

1.1. GPIO支持要先保证是选上的,一般这个选项都会选上的。
Linux内核驱动:gpio模拟i2c驱动_第2张图片
如果不选这一项,1.2中不会出现选项“GPIO-based bitbanging I2C”。
1.2. 配置 GPIO的I2C
在defconfig中,设置CONFIG_I2C_GPIO=y
对应的选项为:

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

从配置中看到将驱动整合到内核中,而不是module形式。这样能保证在其它I2C板级信息注册之前,已经存在了i2c总线。(所以,要设为CONFIG_I2C_GPIO=y,而不是CONFIG_I2C_GPIO=m)

2. dts配置

方法一:
首先需要在dtsi文件加上节点
路径:kernel-4.9/arch/arm64/boot/dts/mediatek/t19-auto2712p1v1-common.dtsi
(1)

/ {
}

中添加:

i2c_gpio:i2c6 {
	compatible = "i2c-gpio";
	gpios = <&pio 156 GPIO_ACTIVE_HIGH>, /* sda  SPI0_MO:GPIO156*/
			<&pio 155 GPIO_ACTIVE_HIGH>; /* scl SPI0_CK:GPIO155*/
	i2c-gpio,sda-open-drain;
	i2c-gpio,scl-open-drain;
	i2c-gpio,delay-us = <2>;	/* ~100 kHz */
	#address-cells = <1>;
	#size-cells = <0>;
	
	bu32107: bu32107@40 {
		compatible = "rohm,bu32107";
		reg = <0x40>;
		pinctrl-0 = <&bu32107_pins_default>;
		stb-gpio = <&pio 153 1>;
	};
};

这里要根据实际情况,修改gpios中对应的SDA和SCL的GPIO号。
同时,也可以修改I2C的速率,i2c-gpio,delay-us = <2>;对应I2C的速率为100kHz。2us是设置一位后的延时时间,即clk的时间。
(2)

&pio {
}

中添加:

bu32107_pins_default:bu32107pinsdefault{
	pins_cmd_dat {
		pinmux = <MT2712_PIN_153_SPI0_CSN__FUNC_GPIO153>;
		input-disable;
		bias-disable;
	};
};

方法二:
首先需要在dtsi文件加上节点

i2c-gpio: i2c-gpio {
        compatible = "i2c-gpio";
        status = "disabled";
    };

在dts文件加上器件描述:

&i2c-gpio {
    pinctrl-names = "default";
    status = "okay";
  eeprom: m24c08@23 {
        compatible = "st,m24512";
        reg = <0x23>;
        status = "okay";
    };
};

二、代码分析

从最底层到对接I2C框架:
(1)i2c-gpio.c文件中提供的接口:

static void i2c_gpio_setsda_dir(void *data, int state)
static void i2c_gpio_setsda_val(void *data, int state)
static void i2c_gpio_setscl_dir(void *data, int state)
static void i2c_gpio_setscl_val(void *data, int state)
static int i2c_gpio_getsda(void *data)
static int i2c_gpio_getscl(void *data)

这里就是实现gpio的sda,scl的设置,仅此而已。
(2)在kernel/driver/i2c/algos/i2c-algo-bit.c中是系统中默认提供的gpio模拟i2c的代码。

static inline void sdalo(struct i2c_algo_bit_data *adap)
static inline void sdahi(struct i2c_algo_bit_data *adap)
static inline void scllo(struct i2c_algo_bit_data *adap)
static int sclhi(struct i2c_algo_bit_data *adap)
static void i2c_start(struct i2c_algo_bit_data *adap)
static void i2c_repstart(struct i2c_algo_bit_data *adap)
static void i2c_stop(struct i2c_algo_bit_data *adap)
static int i2c_outb(struct i2c_adapter *i2c_adap, unsigned char c)
static int i2c_inb(struct i2c_adapter *i2c_adap)
static int test_bus(struct i2c_algo_bit_data *adap, char *name)
static int try_address(struct i2c_adapter *i2c_adap,
                          unsigned char addr, int retries)
static int sendbytes(struct i2c_adapter *i2c_adap, struct i2c_msg *msg)
static int acknak(struct i2c_adapter *i2c_adap, int is_ack)
static int readbytes(struct i2c_adapter *i2c_adap, struct i2c_msg *msg)
static int bit_doAddress(struct i2c_adapter *i2c_adap, struct i2c_msg *msg)
static int bit_xfer(struct i2c_adapter *i2c_adap,
		    struct i2c_msg msgs[], int num)
/* -----exported algorithm data: -------------------------------------   */
 
const struct i2c_algorithm i2c_bit_algo = {
	.master_xfer	= bit_xfer,
	.functionality	= bit_func,
};

这里就是对接标准的I2C框架
这里说下出现的struct i2c_algorithm,其中的master_xfer就是我们调i2c_master_send/i2c_master_recv的时候最终调用的接口。

三、应用读写

实测,测试用例
注意:前面的dts中设置GPIO I2C为i2c-6,所以open的设备为"/dev/i2c-6"。

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

typedef struct _REG_CMD {
	unsigned short addr;
	unsigned char data;
} REG_CMD_16;

static REG_CMD_16 reg_cmd[1];

#define BU32107_MAGIC	0xD1
#define BU32107_IOCTL_WRITEREG	_IOW(BU32107_MAGIC, 0x16, REG_CMD_16)
#define BU32107_IOCTL_READREG	_IOW(BU32107_MAGIC, 0x17, REG_CMD_16)

#define BU32107_I2C_DEV_PATH "/dev/i2c-6"   //BU32107 
#define BU32107_IC_ADDR                 0x40

#define RETRY_COUNT     20

int32_t BU32107_read(int fd, unsigned short addr, void *buff, size_t len)
{
	uint32_t retry = 0;

    if (-1 == fd)
    {  
        printf("---------- %s : %d ------------\n", __func__, __LINE__);
        return -1;
    }

	unsigned char *temp_buff = alloca(len+3);

	if (temp_buff == NULL)
		return -1;

	temp_buff[0] = 0xD0;
	temp_buff[1] = 0X00;
	temp_buff[2] = (addr >> 8)& 0xff;
	temp_buff[3] = addr & 0xff;
	//memcpy(temp_buff+2, buff, size);
	printf("---------- %s : %d ------------sizeof(temp_buff):%ld\n", __func__, __LINE__,sizeof(temp_buff));

	while ( write(fd, temp_buff, len+3) != (int)(len+3))
	{
        perror("BU32107_read error, write addr faild.");
		usleep(10);

		if (retry++ >= RETRY_COUNT)
		{
			printf("[BU32107]  BU32107_read error, write reg faild!\n");
			return -1;
		}	
	}

	retry = 0;
	while (read(fd, buff, len) != (int)len)
	{
        printf("BU32107_read error, reg = %XH , %d, %s\n", addr, errno, strerror(errno));
		usleep(20);

		if (retry++ >= RETRY_COUNT)
		{
			printf("[BU32107]  BU32107_read error, reg = %02X, %d, %s\n", addr, errno, strerror(errno));
			return -1;
		}
			
	}
    return 0;
}

int32_t BU32107_write(int fd, unsigned short addr, void *buff, size_t size)
{
	uint32_t retry = 0;
	int ret = 0;
	uint8_t *temp_buff = alloca(size+ 2);

	if (temp_buff == NULL)
		return -1;

	temp_buff[0] = (addr >> 8)& 0xff;
	temp_buff[1] = addr & 0xff;
	memcpy(temp_buff+2, buff, size);

	ret = write(fd, temp_buff, size+2);
	printf("[bu32107] %s (%XH, %XH),%XH ret:%d\n",__FUNCTION__, temp_buff[0], temp_buff[1], temp_buff[2], ret);

	while ( write(fd, temp_buff, size+2) != (int)(size+2) )
	{
		printf("[BU32107]  BU32107_write error.\n");
		usleep(200);

		if (retry++ >= RETRY_COUNT)
			return -1;
	}
	
	return 0;
}

int main(int argc, char **argv)
{
	int fd, i = 0;
	uint8_t value = 0xa5;
	char * pEnd;
	unsigned short addr = 0x0801;
	int ret = 0;
	uint8_t value1 = 0;

	printf("[BU32107] test_audio %d params:", argc);
	while(i < argc)
	{
		printf("%s  ",argv[i]);
		i++;
	}
	printf("\n");

	fd = open(BU32107_I2C_DEV_PATH, O_RDWR);

	if (-1 == fd)
	{
		printf("[BU32107] open dev faild!\n");
		return -1;
	}
	else
	{
		printf("[BU32107] open dev ok!\n");
	}

	if (ioctl(fd, I2C_SLAVE_FORCE, BU32107_IC_ADDR) < 0)
	{
		printf("[BU32107] ioctl I2C_SLAVE_FORCE failed\n");
		close(fd);
		fd = -1;
		return -1;
	}
	else
	{
		printf("[BU32107] ioctl I2C_SLAVE_FORCE ok\n");
	}

	if (0 != BU32107_write(fd, addr, &value, sizeof(value)))
	{
		printf("BU32107_write addr: %#X fail!\n", addr);
		return -1;
	}
	else
	{
		printf("BU32107_write addr: %#X value:%#X ok!\n", addr, value);
	}

    if (0 != BU32107_read(fd, addr, &value1, sizeof(value1)))
    {
		printf("BU32107_read addr: %#X fail!\n", addr);
        return -1;
    }
	else
	{
		printf("BU32107_read addr: %#X value: %#X ok!\n", addr, value1);
	}
	
	close(fd);
	return 0;
}

参考:
[1]:https://blog.csdn.net/skywalkzf/article/details/6575773

你可能感兴趣的:(linux_驱动,I2C,linux驱动,I2C)