本文基于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
1.1. GPIO支持要先保证是选上的,一般这个选项都会选上的。
如果不选这一项,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)
方法一:
首先需要在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