高通 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\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 */
};


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM