一.硬件资源
RK3588 芯片拥有 9 个 通用I2C 控制器,分别复用在几个不同的电源域,用后缀_M0/_M1/_M2/_M3/_M4
区分不同复用位置。_M0/_M1/_M2/_M3/_M4 不能同时使用,分配时只能选择其中一组,例如:不能选择了 I2C1_M0,又选择了 I2C1_M1 或其它 M*。
i2c的电平通过VCCIOx来设置,比如下图:i2c5和i2c8,电平都是1.8V,因为VCCIO4的电平设置为1.8V。
二.软件配置
1.i2c的dts配置。
下面以i2c3 m0 为例,下面的i2c的dts设置方法。
i2c节点定义,一般在rk3588.dtsi 中定义。文件路径为/nvr/kernel/arch/arm64/boot/dts/rockchip/rk3588.dtsi。
i2c3: i2c@feab0000 {
compatible = "rockchip,rk3588-i2c", "rockchip,rk3399-i2c";
reg = <0x0 0xfeab0000 0x0 0x1000>;
clocks = <&cru CLK_I2C3>, <&cru PCLK_I2C3>;
clock-names = "i2c", "pclk";
interrupts = <GIC_SPI 320 IRQ_TYPE_LEVEL_HIGH>;
pinctrl-names = "default";
pinctrl-0 = <&i2c3m0_xfer>;
#address-cells = <1>;
#size-cells = <0>;
status = "disabled";
};
i2c的dts引用
&i2c3 {
status = "okay";
pinctrl-names = "default";
pinctrl-0 = <&i2c3m0_xfer>;
/*i2c设备*/
}
对于i2c的dts配置,需要注意i2c信号的复用,是否有别的复用功能在使用,否则会无法编译通过,或者不能正常工作。
2.上拉配置。
i2c要想正常工作,一定需要配置上拉电阻,可以通过外部配置,或者通过内核来内部配置。
下面是在内核把i2c5设为上拉。文件位置在nvr/u-boot/arch/arm/dts/rk3588s-pinctrl.dtsi
i2c5m0_xfer: i2c5m0-xfer {
rockchip,pins =
/* i2c5_scl_m0 */
<3 RK_PC7 9 &pcfg_pull_up>,
/* i2c5_sda_m0 */
<3 RK_PD0 9 &pcfg_pull_up>;
};
3.i2c常用调试命令
列出所有i2c资源
i2cdetect -l
查询i2c总线上识别的芯片id
i2cdetect -y x
查询io的复用功能可以通过下面的命令
cat /sys/kernel/debug/pinctrl/pinctrl-rockchip-pinctrl/pinmux-pins
4.i2c在sensor驱动中使用
i2c 添加完dts之后,就可以在驱动中或者应用代码里进行调用了。下面为使用例子。
sensor驱动里的调用
主要是读写函数的实现,下面为实现代码。
写函数实现
static int i2c_wr8(struct v4l2_subdev *sd, u8 reg, u8 val)
{
struct imx415 *imx415 = to_state(sd);
struct i2c_client *client = imx415->i2c_client;
struct i2c_msg msgs[1];
int err;
u8 buf[2];
buf[0] = reg;
buf[1] = val;
msgs[0].addr = client->addr;
msgs[0].flags = 0;
msgs[0].len = 2;
msgs[0].buf = buf;
err = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
if (err < 0) {
# if DEBUG_INFO
v4l2_err(sd, "%s: writing register 0x%x from 0x%x failed\n",
__func__, reg, client->addr);
#endif
return -EIO;
}
return 0;
}
读函数实现
static u8 i2c_rd8(struct v4l2_subdev *sd, u8 reg)
{
struct imx415 *imx415 = to_state(sd);
struct i2c_client *client = imx415->i2c_client;
struct i2c_msg msgs[2];
int err;
u8 val;
msgs[0].addr = client->addr;
msgs[0].flags = 0;
msgs[0].len = 1;
msgs[0].buf = ®
msgs[1].addr = client->addr;
msgs[1].flags = I2C_M_RD;
msgs[1].len = 1;
msgs[1].buf = &val;
err = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
if (err != ARRAY_SIZE(msgs)) {
# if DEBUG_INFO
v4l2_err(sd, "%s: reading register 0x%x from 0x%x failed\n",
__func__, reg, client->addr);
#endif
return 0;
}
return val;
}
5.i2c 在应用代码里使用。
对于有些应用,需要在app里实现i2c功能。因为在应用里实现比较灵活,也方便进行快速的调试测试。
i2c初始化代码,以i2c5为例。
初始化函数
int i2c_fd;
int i2c_init(void)
{
int ret;
i2c_fd = open("/dev/i2c-5", O_RDWR);
if (i2c_fd < 0) {
printf("Open /dev/i2c-5 error!\n");
return -1;
}
printf("Open /dev/i2c-7 ok!\n");
printf("i2c_fd =%d\n", i2c_fd);
ret = ioctl(i2c_fd, I2C_SLAVE_FORCE, LT9211_I2C_ADDR);
if (ret < 0) {
printf("I2C_SLAVE_FORCE error!\n");
return ret;
}
return 0;
}
/*i2c读函数
only can read one byte
*/
int read_register(unsigned int reg_addr, uint16_t *val)
{
int retval;
unsigned char buf[4];
static struct i2c_rdwr_ioctl_data rdwr;
static struct i2c_msg msg[2];
unsigned int dev_addr = LT9211_I2C_ADDR;
retval = ioctl(i2c_fd, I2C_SLAVE_FORCE, dev_addr);
if (retval < 0) {
printf("addr error!\n");
return -1;
}
msg[0].addr = dev_addr;
msg[0].flags = 0;
msg[0].len = 1;
msg[0].buf = buf;
msg[1].addr = dev_addr;
msg[1].flags = 0;
msg[1].flags |= I2C_M_RD;
msg[1].len = 1;
msg[1].buf = buf;
rdwr.msgs = &msg[0];
rdwr.nmsgs = (__uint32_t)2;
buf[0] = reg_addr & 0xff;
retval = ioctl(i2c_fd, I2C_RDWR, &rdwr);
if (retval != 2) {
printf("CMD_I2C_READ error!\n");
return -1;
}
*val = (uint16_t)(buf[0]);
retval = 0;
// printf("read addr:0x%02x date:0x%02x\n", reg_addr, buf[0]);
return retval;
}
/*i2c写函数*/
int write_register(int addr, int data)
{
int idx = 0;
int ret;
char buf[8];
buf[idx] = addr & 0xff;
idx++;
buf[idx] = data & 0xff;
idx++;
printf("write addr:0x%02x date:0x%02x\n", addr, data);
ret = write(i2c_fd, buf, 2);
if (ret < 0) {
printf("I2C_WRITE error!\n");
return -1;
}
return 0;
}
static uint8_t i2c_rd8(uint8_t reg)
{
uint16_t val;
read_register(reg, &val);
return val & 0xff;
}
static int i2c_wr8(uint8_t reg, uint8_t val)
{
return write_register(reg, val);
}