以MSM8953为例。
原文(有删改):https://blog.csdn.net/qq_29890089/article/details/108294710
项目场景
因为项目需要,需要在高通MSM8953平台的LK阶段使用I2C。本文只介绍在LK阶段配置使用I2C5的方法。
在调试SLM753某客户项目LCM时,客户使用LVDS的LCM,而msm8916只有一个mipi的接口,所以就是用到了mipi-2-lvds转换芯片:icn6202。这颗芯片需要使用I2C进行配置LVDS屏的时钟和分辨率等信息,以至于LVDS屏可以正常显示。
Kernel阶段i2c比较容易使用,只需在dts中配置一个i2c设备即可以使用对应的i2c接口进行数据传输,但是LK阶段的代码就显得蹩脚了,默认只配置了i2c0接口!其他的i2c都不能使用,因此需要进行有关的调试。
调试准备
文档: 80-nu767-1_k_bam_low-speed_peripherals_(blsp)_user_guide
查看文档,有I2C介绍如下:
I2c-3对应的:
- 物理地址为
0x78B7000
- 中断IRQ:97
- 钟信号 clk :clk_gcc_blsp1_qup3_i2c_apps_clk
I2c-8对应的:
- 物理地址为
0x7AF8000
- 中断IRQ:302
- 时钟信号 clk :clk_gcc_blsp2_qup4_i2c_apps_clk
查看产品配置表有:
I2C3:gpio10&gpio11;
i2c8:gpio10&gpio11;gpio98&gpio99;
I2C Description :
1、 arg: BLSP ID can be BLSP_ID_1 or BLSP_ID_2
2、 arg: QUP ID can be QUP_ID_0:QUP_ID_5
3、 arg: I2C CLK. should be 100KHZ, or 400KHz
4、 arg: Source clock, should be set @ 19.2MHz
步骤
为了符合规范,有些内容以对应的路径为准。
由于这里的步骤是为了满足“LVDS转MIPI”,因此添加的文件与mipi有关。
初始化I2C总线
创建platform/msm_shared/mipi_dsi_i2c.c
文件
#define I2C_CLK_FREQ 100000
#define I2C_SRC_CLK_FREQ 19200000
int mipi_dsi_i2c_device_init(uint8_t blsp_id, uint8_t qup_id)
{
if(BLSP_ID_2 == blsp_id) {
// qup_blsp_i2c_init(BLSP_ID_1, QUP_ID_0, 100000, 19200000);
i2c8_dev = qup_blsp_i2c_init(blsp_id, qup_id,
I2C_CLK_FREQ, I2C_SRC_CLK_FREQ);
if(!i2c8_dev) {
dprintf(CRITICAL, "mipi_dsi_i2c_device_init() failed
");
return ERR_NOT_VALID;
}
}
else
{
i2c_dev = qup_blsp_i2c_init(blsp_id, qup_id,
I2C_CLK_FREQ, I2C_SRC_CLK_FREQ);
if(!i2c_dev) {
dprintf(CRITICAL, "mipi_dsi_i2c_device_init() failed
");
return ERR_NOT_VALID;
}
}
return NO_ERROR;
}
对应的驱动platform/msm_shared/i2c_qup.c
做如下修改,主要是添加:
- i2c设备所需的头文件
- 设备对象
- 初始化时的特殊判断
// 新引入的头文件
#include <blsp_qup.h>
#include <platform.h>
static struct qup_i2c_dev *dev_addr = NULL;
// 创建新的设备对象
static struct qup_i2c_dev *dev8_addr = NULL;
struct qup_i2c_dev *qup_blsp_i2c_init(uint8_t blsp_id, uint8_t qup_id,
uint32_t clk_freq, uint32_t src_clk_freq)
{
struct qup_i2c_dev *dev;
#if 0
if (dev_addr != NULL) {
return dev_addr;
}
#else
// 针对i2c-8的特殊处理
if(BLSP_ID_2 == blsp_id)
dev = dev8_addr;
else
dev = dev_addr;
if (dev != NULL)
return dev;
#endif
dev = malloc(sizeof(struct qup_i2c_dev));
if (!dev) {
return NULL;
}
dev = memset(dev, 0, sizeof(struct qup_i2c_dev));
/* Platform uses BLSP */
dev->qup_irq = BLSP_QUP_IRQ(blsp_id, qup_id);
dev->qup_base = BLSP_QUP_BASE(blsp_id, qup_id);
/* This must be done for qup_i2c_interrupt to work. */
#if 0
dev_addr = dev;
#else
if(BLSP_ID_2 == blsp_id)
dev8_addr = dev;
else
dev_addr = dev;
#endif
/* Initialize the GPIO for BLSP i2c */
gpio_config_blsp_i2c(blsp_id, qup_id);
clock_config_blsp_i2c(blsp_id, qup_id);
qup_i2c_sec_init(dev, clk_freq, src_clk_freq);
return dev;
}
在platform/msm_shared/i2c_qup.c
添加新的I2C-8操作函数,包括读写函数。
int mipi_dsi_i2c8_read_bytes(uint8_t addr, uint8_t *reg, uint8_t *buf, uint8_t len)
{
if (!buf)
return ERR_INVALID_ARGS;
if(!i2c8_dev)
return ERR_NOT_VALID;
struct i2c_msg rd_buf[] = {
{addr, I2C_M_WR, 2, reg},
{addr, I2C_M_RD, len, buf}
};
int err = qup_i2c_xfer(i2c8_dev, rd_buf, 2);
if (err < 0) {
dprintf(CRITICAL, "Read reg %x failed
", (int)reg[0]);
return err;
}
return NO_ERROR;
}
int mipi_dsi_i2c8_write_bytes(uint8_t addr, uint8_t *reg, uint32_t len)
{
if (!i2c8_dev)
return ERR_NOT_VALID;
struct i2c_msg msg_buf[] = {
{addr, I2C_M_WR, len, reg},
};
int err = qup_i2c_xfer(i2c8_dev, msg_buf, 1);
if (err < 0) {
dprintf(CRITICAL, "Write reg %x failed
", (int)reg[0]);
return err;
}
return NO_ERROR;
}
int mipi_dsi_i2c8_read(uint8_t addr, uint8_t reg, uint8_t *buf, uint8_t len)
{
if (!buf)
return ERR_INVALID_ARGS;
if(!i2c8_dev)
return ERR_NOT_VALID;
struct i2c_msg rd_buf[] = {
{addr, I2C_M_WR, 1, ®},
{addr, I2C_M_RD, len, buf}
};
int err = qup_i2c_xfer(i2c8_dev, rd_buf, 2);
if (err < 0) {
dprintf(CRITICAL, "Read reg %x failed
", reg);
return err;
}
return NO_ERROR;
}
int mipi_dsi_i2c8_read_byte(uint8_t addr, uint8_t reg, uint8_t *buf)
{
if (!buf)
return ERR_INVALID_ARGS;
return mipi_dsi_i2c8_read(addr, reg, buf, 1);
}
int mipi_dsi_i2c8_write_byte(uint8_t addr, uint8_t reg, uint8_t val)
{
if (!i2c8_dev)
return ERR_NOT_VALID;
unsigned char buf[2] = {reg, val};
struct i2c_msg msg_buf[] = {
{addr, I2C_M_WR, 2, buf},
};
int err = qup_i2c_xfer(i2c8_dev, msg_buf, 1);
if (err < 0) {
dprintf(CRITICAL, "Write reg %x failed
", reg);
return err;
}
return NO_ERROR;
}
在platform/msm_shared/include/i2c_qup.h
新增下列函数声明
int mipi_dsi_i2c8_read_bytes(uint8_t addr, uint8_t *reg, uint8_t *buf, uint8_t len);
int mipi_dsi_i2c8_write_bytes(uint8_t addr, uint8_t *reg, uint32_t len);
int mipi_dsi_i2c8_read_byte(uint8_t addr, uint8_t reg, uint8_t *buf);
int mipi_dsi_i2c8_write_byte(uint8_t addr, uint8_t reg, uint8_t val);
int mipi_dsi_i2c8_read(uint8_t addr, uint8_t reg, uint8_t *buf, uint8_t len);
配置GPIO为I2C
// platform/msm8953/gpio.c
#define GPIO_BLSP1_ACTIVE_1 10
#define GPIO_BLSP1_ACTIVE_2 11
#define GPIO_BLSP2_ACTIVE_1 98
#define GPIO_BLSP2_ACTIVE_2 99
void gpio_config_blsp_i2c(uint8_t blsp_id, uint8_t qup_id)
{
if(blsp_id == BLSP_ID_1) {
switch (qup_id) {
case QUP_ID_2:
/* configure I2C SDA gpio */
gpio_tlmm_config(GPIO_BLSP1_ACTIVE_1, 2, GPIO_OUTPUT,
GPIO_NO_PULL, GPIO_6MA, GPIO_ENABLE);
/* configure I2C SCL gpio */
gpio_tlmm_config(GPIO_BLSP1_ACTIVE_2, 2, GPIO_OUTPUT,
GPIO_NO_PULL, GPIO_6MA, GPIO_ENABLE);
break;
default:
dprintf(CRITICAL, "Incorrect QUP id %d
", qup_id);
ASSERT(0);
};
}
else if(blsp_id == BLSP_ID_2) {
switch (qup_id) {
case QUP_ID_3:
/* configure I2C SDA gpio */
gpio_tlmm_config(GPIO_BLSP2_ACTIVE_1, 1, GPIO_OUTPUT,
GPIO_PULL_UP, GPIO_6MA, GPIO_ENABLE);
/* configure I2C SCL gpio */
gpio_tlmm_config(GPIO_BLSP2_ACTIVE_2, 1, GPIO_OUTPUT,
GPIO_PULL_UP, GPIO_6MA, GPIO_ENABLE);
break;
default:
dprintf(CRITICAL, "Incorrect QUP id %d
", qup_id);
ASSERT(0);
};
}
else {
dprintf(CRITICAL, "Incorrect BLSP id %d
",blsp_id);
ASSERT(0);
}
}
开启I2C对应的时钟
在bootable/bootloader/lk/platform/msm8953/acpuclock.c
中新增下列函数
void clock_config_blsp_i2c(uint8_t blsp_id, uint8_t qup_id)
{
uint8_t ret = 0;
char clk_name[64];
struct clk *qup_clk;
if((blsp_id != BLSP_ID_1 && blsp_id != BLSP_ID_2)) {
dprintf(CRITICAL, "Incorrect BLSP-%d or QUP-%d configuration
",
blsp_id, qup_id);
ASSERT(0);
}
if(blsp_id == BLSP_ID_1){
if (qup_id == QUP_ID_2) {
snprintf(clk_name, sizeof(clk_name), "blsp1_qup3_ahb_iface_clk");
}
else if (qup_id == QUP_ID_3) {
snprintf(clk_name, sizeof(clk_name), "blsp1_qup4_ahb_iface_clk");
}
}
if(blsp_id == BLSP_ID_2){
if (qup_id == QUP_ID_3) {
snprintf(clk_name, sizeof(clk_name), "blsp2_qup4_ahb_iface_clk");
}
}
ret = clk_get_set_enable(clk_name, 0 , 1);
if (ret) {
dprintf(CRITICAL, "Failed to enable %s clock
", clk_name);
return;
}
if(blsp_id == BLSP_ID_1){
if (qup_id == QUP_ID_2) {
snprintf(clk_name, sizeof(clk_name), "gcc_blsp1_qup3_i2c_apps_clk");
}
else if (qup_id == QUP_ID_3) {
snprintf(clk_name, sizeof(clk_name), "gcc_blsp1_qup4_i2c_apps_clk");
}
}
if(blsp_id == BLSP_ID_2){
if (qup_id == QUP_ID_3) {
snprintf(clk_name, sizeof(clk_name), "gcc_blsp2_qup4_i2c_apps_clk");
}
}
qup_clk = clk_get(clk_name);
if (!qup_clk) {
dprintf(CRITICAL, "Failed to get %s
", clk_name);
return;
}
ret = clk_enable(qup_clk);
if (ret) {
dprintf(CRITICAL, "Failed to enable %s
", clk_name);
return;
}
}
在platform/msm8953/msm8953-clock.c
添加进时钟序列中
// 新增
static struct vote_clk gcc_blsp2_ahb_clk = {
.cbcr_reg = (uint32_t *) BLSP2_AHB_CBCR,
.vote_reg = (uint32_t *) APCS_CLOCK_BRANCH_ENA_VOTE,
.en_mask = BIT(20),
.c = {
.dbg_name = "gcc_blsp2_ahb_clk",
.ops = &clk_ops_vote,
},
};
// 新增
static struct clk_freq_tbl ftbl_gcc_blsp1_qup2_i2c_apps_clk_src[] = {
F( 96000, cxo, 10, 1, 2),
F( 4800000, cxo, 4, 0, 0),
F( 9600000, cxo, 2, 0, 0),
F( 16000000, gpll0, 10, 1, 5),
F( 19200000, gpll0, 1, 0, 0),
F( 25000000, gpll0, 16, 1, 2),
F( 50000000, gpll0, 16, 0, 0),
F_END
};
// 新增
static struct rcg_clk gcc_blsp1_qup2_i2c_apps_clk_src = {
.cmd_reg = (uint32_t *) GCC_BLSP1_QUP2_CMD_RCGR,
.cfg_reg = (uint32_t *) GCC_BLSP1_QUP2_CFG_RCGR,
.set_rate = clock_lib2_rcg_set_rate_hid,
.freq_tbl = ftbl_gcc_blsp1_qup2_i2c_apps_clk_src,
.current_freq = &rcg_dummy_freq,
.c = {
.dbg_name = "gcc_blsp1_qup2_i2c_apps_clk_src",
.ops = &clk_ops_rcg,
},
};
// 新增
static struct branch_clk gcc_blsp1_qup2_i2c_apps_clk = {
.cbcr_reg = (uint32_t *) GCC_BLSP1_QUP2_APPS_CBCR,
.parent = &gcc_blsp1_qup2_i2c_apps_clk_src.c,
.c = {
.dbg_name = "gcc_blsp1_qup2_i2c_apps_clk",
.ops = &clk_ops_branch,
},
};
// 新增
static struct clk_freq_tbl ftbl_gcc_blsp1_qup3_i2c_apps_clk_src[] = {
F( 96000, cxo, 10, 1, 2),
F( 4800000, cxo, 4, 0, 0),
F( 9600000, cxo, 2, 0, 0),
F( 16000000, gpll0, 10, 1, 5),
F( 19200000, gpll0, 1, 0, 0),
F( 25000000, gpll0, 16, 1, 2),
F( 50000000, gpll0, 16, 0, 0),
F_END
};
#if 0
static struct clk_freq_tbl ftbl_gcc_blsp2_qup4_i2c_apps_clk_src[] = {
F( 96000, cxo, 10, 1, 2),
F( 4800000, cxo, 4, 0, 0),
F( 9600000, cxo, 2, 0, 0),
F( 16000000, gpll0, 10, 1, 5),
F( 19200000, gpll0, 1, 0, 0),
F( 25000000, gpll0, 16, 1, 2),
F( 50000000, gpll0, 16, 0, 0),
F_END
};
#endif
// 新增
static struct rcg_clk gcc_blsp1_qup3_i2c_apps_clk_src = {
.cmd_reg = (uint32_t *) GCC_BLSP1_QUP3_CMD_RCGR,
.cfg_reg = (uint32_t *) GCC_BLSP1_QUP3_CFG_RCGR,
.set_rate = clock_lib2_rcg_set_rate_hid,
.freq_tbl = ftbl_gcc_blsp1_qup3_i2c_apps_clk_src,
.current_freq = &rcg_dummy_freq,
.c = {
.dbg_name = "gcc_blsp1_qup3_i2c_apps_clk_src",
.ops = &clk_ops_rcg,
},
};
// 新增
static struct rcg_clk gcc_blsp2_qup4_i2c_apps_clk_src = {
.cmd_reg = (uint32_t *) GCC_BLSP2_QUP4_CMD_RCGR,
.cfg_reg = (uint32_t *) GCC_BLSP2_QUP4_CFG_RCGR,
.set_rate = clock_lib2_rcg_set_rate_hid,
// .freq_tbl = ftbl_gcc_blsp2_qup4_i2c_apps_clk_src,
.freq_tbl = ftbl_gcc_blsp1_qup3_i2c_apps_clk_src,
.current_freq = &rcg_dummy_freq,
.c = {
.dbg_name = "gcc_blsp2_qup4_i2c_apps_clk_src",
.ops = &clk_ops_rcg,
},
};
// 新增
static struct branch_clk gcc_blsp1_qup3_i2c_apps_clk = {
.cbcr_reg = (uint32_t *) GCC_BLSP1_QUP3_APPS_CBCR,
.parent = &gcc_blsp1_qup3_i2c_apps_clk_src.c,
.c = {
.dbg_name = "gcc_blsp1_qup3_i2c_apps_clk",
.ops = &clk_ops_branch,
},
};
// 新增
static struct branch_clk gcc_blsp2_qup4_i2c_apps_clk = {
.cbcr_reg = (uint32_t *) GCC_BLSP2_QUP4_APPS_CBCR,
.parent = &gcc_blsp2_qup4_i2c_apps_clk_src.c,
.c = {
.dbg_name = "gcc_blsp2_qup4_i2c_apps_clk",
.ops = &clk_ops_branch,
},
};
// 新增
static struct clk_freq_tbl ftbl_mdss_esc1_1_clk[] = {
F_MM(19200000, cxo, 1, 0, 0),
F_END
};
// 新增
static struct rcg_clk dsi_esc1_clk_src = {
.cmd_reg = (uint32_t *) DSI_ESC1_CMD_RCGR,
.cfg_reg = (uint32_t *) DSI_ESC1_CFG_RCGR,
.set_rate = clock_lib2_rcg_set_rate_hid,
.freq_tbl = ftbl_mdss_esc1_1_clk,
.c = {
.dbg_name = "dsi_esc1_clk_src",
.ops = &clk_ops_rcg,
},
};
// 新增
static struct branch_clk mdss_esc1_clk = {
.cbcr_reg = (uint32_t *) DSI_ESC1_CBCR,
.parent = &dsi_esc1_clk_src.c,
.has_sibling = 0,
.c = {
.dbg_name = "mdss_esc1_clk",
.ops = &clk_ops_branch,
},
};
// 在这个时钟组中添加这么一段
static struct clk_lookup msm_clocks_8953[] =
{
... // 维持不变
/*add start by Yubel for blsp 20200730 */
/* BLSP CLOCKS FOR I2C-8*/
CLK_LOOKUP("blsp1_qup2_ahb_iface_clk", gcc_blsp1_ahb_clk.c),
CLK_LOOKUP("gcc_blsp1_qup2_i2c_apps_clk_src",
gcc_blsp1_qup2_i2c_apps_clk_src.c),
CLK_LOOKUP("gcc_blsp1_qup2_i2c_apps_clk",
gcc_blsp1_qup2_i2c_apps_clk.c),
CLK_LOOKUP("blsp1_qup3_ahb_iface_clk", gcc_blsp1_ahb_clk.c),
CLK_LOOKUP("gcc_blsp1_qup3_i2c_apps_clk_src",
gcc_blsp1_qup3_i2c_apps_clk_src.c),
CLK_LOOKUP("gcc_blsp1_qup3_i2c_apps_clk",
gcc_blsp1_qup3_i2c_apps_clk.c),
CLK_LOOKUP("blsp2_qup4_ahb_iface_clk", gcc_blsp2_ahb_clk.c),
CLK_LOOKUP("gcc_blsp2_qup4_i2c_apps_clk_src",
gcc_blsp2_qup4_i2c_apps_clk_src.c),
CLK_LOOKUP("gcc_blsp2_qup4_i2c_apps_clk",
gcc_blsp2_qup4_i2c_apps_clk.c),
/*add end by Yubel 20200730 */
};