以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\n");
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\n");
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\n", (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\n", (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, &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\n", 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\n", 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\n", 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\n", qup_id);
ASSERT(0);
};
}
else {
dprintf(CRITICAL, "Incorrect BLSP id %d\n",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\n",
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\n", 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\n", clk_name);
return;
} ret = clk_enable(qup_clk);
if (ret) {
dprintf(CRITICAL, "Failed to enable %s\n", 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 */
};

高通 LK阶段配置使用I2C-8的更多相关文章

  1. 高通lk屏幕向kernel传参

    LK把相关参数报存到cmdline上: 在Bootable\bootloader\lk\dev\gcdb\display\gcdb_display_param.c上gcdb_display_cmdli ...

  2. 高通spi 屏幕 -lk代码分析

    lk SPI驱动 1. 初始化时钟 在lk中,我们是从kmain开始执行下来的,而执行顺序则是先初始化时钟,也就是在platform_early_init函数中开始执行的: 在这里我们需要修改这个函数 ...

  3. 【转】高通平台android 环境配置编译及开发经验总结

    原文网址:http://blog.csdn.net/dongwuming/article/details/12784535 1.高通平台android开发总结 1.1 搭建高通平台环境开发环境 在高通 ...

  4. 高通APQ8074 spi 接口配置

    高通APQ8074 spi 接口配置 8074 平台含有两个BLSP(BAM Low-Speed Peripheral) , 每一个BLSP含有两个QUP, 每一个QUP可以被配置为I2C, SPI, ...

  5. 高通移植mipi LCD的过程LK代码

    lk部分:(实现LCD兼容) 1. 函数定位 aboot_init()来到target_display_init(): 这就是高通原生lk LCD 兼容的关键所在.至于你需要兼容多少LCD 就在whi ...

  6. 最新内核3.4)Linux 设备树加载I2C client adapter 的流程(内核3.4 高通)【转】

    转自:https://blog.csdn.net/lsn946803746/article/details/52515225 版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转 ...

  7. 高通平台msm8909 LK 实现LCD 兼容

    前段时间小米出现红米note2 换屏门,现在我们公司也要上演了:有两个供应商提供不同IC 的LCD panel. 软件区分的办法是读取LCD IC 的ID 寄存器,下面解析高通平台LK中LCD兼容的过 ...

  8. Linux加载DTS设备节点的过程(以高通8974平台为例)

    DTS是Device Tree Source的缩写,用来描述设备的硬件细节.在过去的ARM Linux中,arch/arm/plat-xxx和arch/arm/mach-xxx中充斥着大量的垃圾代码, ...

  9. 高通开发笔记---Yangtze worknote

    点击打开链接 1. repo init -u git://review.sonyericsson.net/platform/manifest -b volatile-jb-mr1-yangtze 2. ...

  10. 高通 MSM8K bootloader : SBL1 .

    一. MSM8K Boot Flow 图1: 高通MSM8K平台bootloader启动流程基本类似,但具体各平台,比如MSM8974.MSM8916.MSM8994等,会有微小区别. 从上图,可以看 ...

随机推荐

  1. ruby http请求组件

    github地址 https://github.com/rest-client/rest-client gemfile里添加 gem 'rest-client', '~> 2.0' 执行 bun ...

  2. java的jdbc插入的时候,遇到null情况报错问题

    分析原因: 在执行SQL时MyBatis会自动通过对象中的属性给SQL中参数赋值,它会自动将Java类型转换成数据库的类型.而一旦传入的是null它就无法准确判断这个类型应该是什么,就有可能将类型转换 ...

  3. 海康威视web插件安装后,谷歌浏览器还是不能看视频问题

    首先要根据弹出的信息提示,下载并安装视频播放插件, 安装完成后重新打开谷歌浏览器,重新登录系统,如果还是不能看视频,请按下面的方法设置: 步骤1:谷歌浏览器,地址栏中输入:chrome://flags ...

  4. js不同类型比较

    有布尔 先把布尔转为number 数字和字符串 字符串转number,如果前导为0会被忽略,空字符串转换成0,非数字字符串或其他转为NaN 对象和非对象 对象valueOf获取基本类型,对象转为字符串 ...

  5. 微信小程序长按识别二维码

    微信小程序长按识别二维码 image 组件中二维码/小程序码图片不支持长按识别.仅在 wx.previewImage 中支持长按识别示例代码

  6. UE4 InstancedStaticMesh使用

    在绘制大批量近似模型时,Unity有GPU Instancing(https://www.cnblogs.com/hont/p/7143626.html),而UE中有 HISM和ISM(Instanc ...

  7. Springboot+Mybatis+Clickhouse+jsp 搭建单体应用项目(二)(添加日志打印和源码地址)

    一.添加yaml设置 1 logging: 2 level: 3 com.mrliu.undertow.mapper : debug 二.添加pom的Hutool工具,完善日志打印处理 1 <d ...

  8. Selenium4自动化测试6--控件获取数据--下拉框级联选择、checkbox选择、时间选择器

    4-下拉框级联选择 import time from selenium.webdriver.support.select import Select #pip install selenium fro ...

  9. PageOffice 6 最简集成代码(VUE+Springboot)

    本文描述了PageOffice产品在(VUE+Springboot)前后端分离的项目中如何集成调用.调用PageOffice打开文件的主要核心代码是:后端Springboot项目中第6步和前端VUE项 ...

  10. redis 使用lua脚本 一次性获取多个hash key 字段值

    客户端命令行代码: eval "local rst={};local field='schoolid'; for i,v in pairs(KEYS) do rst[i]=redis.cal ...