以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. 01、Windows 排查

    Windows 分析排查 分析排查是指对 Windows 系统中的文件.进程.系统信息.日志记录等进行检测,挖掘 Windows 系统中是否具有异常情况 1.开机启动项检查 一般情况下,各种木马.病毒 ...

  2. 手把手教你整Win10的Linux子系统(Ubuntu)

    手把手教你整Win10的Linux子系统(Ubuntu) https://www.bilibili.com/read/cv7770224/ 设置root用户的密码 可以通过 sudo passwd r ...

  3. ruby rails 批量插入数据,bulk_insert-----Gem包使用

    Gemfile文件里添加 gem 'bulk_insert' #批量插入 命令行执行安装依赖 bundle install 数据源 ["1.180.3.187", 161, 260 ...

  4. nginx与location规则

    ========================================================================= 2018年3月28日 记录: location = ...

  5. JS实现下拉框切换和tab标签切换

    现在商城网页上会有下拉框切换内容,是如何实现的呢,研究了一天,在调整js代码和查找bug.最终完成了自己想要的效果,我没有写CSS样式,只是实现了基本功能,如果对你有所帮助,可以自己写css,使其更加 ...

  6. 从油猴脚本管理器的角度审视Chrome扩展

    从油猴脚本管理器的角度审视Chrome扩展 在之前一段时间,我需要借助Chrome扩展来完成一个需求,当时还在使用油猴脚本与浏览器扩展之间调研了一波,而此时恰好我又有一些做的还可以的油猴脚本 TKSc ...

  7. wxpython模块详解

    一.python gui(图形化)模块介绍: Tkinter :是python最简单的图形化模块,总共只有14种组建 Pyqt     :是python最复杂也是使用最广泛的图形化 Wx       ...

  8. 【wordpress开发必备】新增必填字段相关函数和钩子,适用6.1版本

    当表单包含多个必填字段时,它们的标签可能带有一个带有图例的星号,以说明这些字段是必填的.为了减少代码重复并帮助维护全局一致的标记,WordPress 有两个新函数:wp_required_field_ ...

  9. Windows系统命令行的最佳实践

    更多博文请关注:https://blog.bigcoder.cn 每次看到Mac生态中炫酷的命令行工具,我就一脸羡慕,但是奈何财力不足,整不起动辄上万的电脑,搬砖人就只能折腾折腾手里的这台window ...

  10. Supervisor 守护进程管理工具

    引言 Supervisor 是基于 Python 编程语言开发的一套通用的进程管理程序,它是通过 fork/exec 的方式把需要管理的进程作为子进程来管理. 安装 pip3 安装 superviso ...