高通 LK阶段配置使用I2C-8

以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, &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
", 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 */
};
如果说我的文章对你有用,只不过是我站在巨人的肩膀上再继续努力罢了。
若在页首无特别声明,本篇文章由 Schips 经过整理后发布。
博客地址:https://www.cnblogs.com/schips/
原文地址:https://www.cnblogs.com/schips/p/using_i2c_in_qualcomm_little_kernel.html