1. 程式人生 > 其它 >Qcom 平臺 LK 階段配置 I2C

Qcom 平臺 LK 階段配置 I2C

技術標籤:I2C物聯網androidqualcomm

qcom平臺LK 階段配置IIC

版本號:V 1.0

作者:Leo

目錄

目錄... 1

前言... 4

1. 確定硬體... 4

2. LK I2C 函式介面... 5

2.1 qup irq 中斷號賦值... 6

2.2 qup base addr 賦值... 6

2.3 blsp6 gpio 初始化... 7

2.4 blsp6 時鐘源的配置... 9

3. 總結... 11

前言

本文著重介紹,如何在qcom 平臺的LK 階段配置和使用I2C。

硬體平臺:msm8909

軟體平臺:Android5.0、Android8.0

I2C裝置:ADV7533

參考部落格和文件:

80-np408-2x_c_msm8909_msm8209_msm8208_hardware_register_description_document_for_oems.pdf

80-nu767-1_h_linux_bam_low-speed_peripherals_configuration_and_debug_guide.pdf

https://blog.csdn.net/eliot_shao/article/details/53351759

1.確定硬體

檢視原理圖確定gpio

確定晶片I2C內部的qup地址、中斷號、通道等引數

根據文件:80-nu767-1_h_linux_bam_low-speed_peripherals_configuration_and_debug_guide.pdf

從以上資訊可以確定:

  1. 我們要使用的是 gpio10、gpio11對應的IIC
  2. 晶片內部引數是
    1. QUP ID: BLSP6
    2. QUP BASE Addr: 78BA000
    3. IRQ#: 100
    4. src clk:qup6_i2c_apps_clk

2.LK I2C 函式介面

根據qcom文件:

80-nu767-1_h_linux_bam_low-speed_peripherals_configuration_and_debug_guide.pdf

可知:qcom LK 階段的I2C 函式介面為:qup_blsp_i2c_init

函式定義:

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)

引數含義:

uint8_t blsp_id: (msm8909 上用 BLSP_ID_1)

enum {
	BLSP_ID_1 = 1,
	BLSP_ID_2,
} blsp_instance;

uint8_t qup_id:

enum {
	QUP_ID_0 = 0,
	QUP_ID_1,
	QUP_ID_2,
	QUP_ID_3,
	QUP_ID_4,
	QUP_ID_5,
} qup_instance;

(具體多少,以qupbase addr 為準。如使用IIC-6對應地址78ba000,那麼傳參QUP_ID_5)

uint32_t clk_freq:

IIC匯流排速率,預設 100K

uint32_t src_clk_freq:

時鐘源頻率,預設19.2MHz

老規矩,先分析下這個函式的實現流程。

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 (dev_addr != NULL) {
		return dev_addr;
	}

	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. */
	dev_addr = dev;

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

從以上流程看,有5個需要關注的地方

1. dev->qup_irq = BLSP_QUP_IRQ(blsp_id, qup_id);
2. dev->qup_base = BLSP_QUP_BASE(blsp_id, qup_id);
3. gpio_config_blsp_i2c(blsp_id, qup_id);
4. clock_config_blsp_i2c(blsp_id, qup_id);
5. qup_i2c_sec_init(dev, clk_freq, src_clk_freq); // 略過,I2C的初始化過程

一個一個分析:

2.1 qup irq 中斷號賦值

dev->qup_irq = BLSP_QUP_IRQ(blsp_id, qup_id);

中斷號,使用 BLSP_QUP_IRQ 這個巨集來賦值,那麼找到它的定義:

bootable\bootloader\lk\platform\msm8909\include\platform\irqs.h

#define GIC_SPI_START 32

#define BLSP_QUP_IRQ(blsp_id, qup_id) (GIC_SPI_START + 95 + qup_id)

從章節1的qup 資訊可知,qup6的中斷號為100,那麼傳參qup_id 為5,中斷號為

GIC_SPI_START + 95 + 5, GIC_SPI_START 是一個偏移量,不用管它。

這裡大概可以推測,qup_id 應該傳參為5

2.2 qup base addr 賦值

dev->qup_base = BLSP_QUP_BASE(blsp_id, qup_id);

qup 基地址的賦值,使用 BLSP_QUP_BASE來賦值,找到它的定義

bootable\bootloader\lk\platform\msm8909\include\platform\iomap.h

#define PERIPH_SS_BASE 0x07800000

bootable\bootloader\lk\platform\msm8909\include\platform\iomap.h

#define BLSP_QUP_BASE(blsp_id, qup_id) (PERIPH_SS_BASE + 0xB5000 + 0x1000 * qup_id)

從章節1的qup資訊圖得知,qup6的 base addr 為78ba000,那麼這裡qup_id 傳參應該為5

即:0x70800000 + 0x0B5000 + 0x1000 * 5 = 0x78bA000

到此為止,從qup irq的資訊和qup base addr的資訊確認,那麼I2C-6(gpio10gpio11)對應的qup 傳參應該是 QUP_ID_5

2.3 blsp6 gpio 初始化

/* Initialize the GPIO for BLSP i2c */

gpio_config_blsp_i2c(blsp_id, qup_id);

看gpio_config_blsp_i2c函式定義.

void gpio_config_blsp_i2c(uint8_t blsp_id, uint8_t qup_id)
{
#if DSI2DPI_TC358762
	uint32_t hw_id = board_hardware_id();
	uint32_t hw_subtype = board_hardware_subtype();
#endif
	if(blsp_id == BLSP_ID_1) {
		switch (qup_id) {
			case QUP_ID_1:
				/* configure I2C SDA gpio */
				gpio_tlmm_config(6, 3, GPIO_OUTPUT, GPIO_NO_PULL,
					GPIO_8MA, GPIO_DISABLE);

				/* configure I2C SCL gpio */
				gpio_tlmm_config(7, 3, GPIO_OUTPUT, GPIO_NO_PULL,
					GPIO_8MA, GPIO_DISABLE);
			break;
			case QUP_ID_2:
				/* configure I2C SDA gpio */
				gpio_tlmm_config(111, 3, GPIO_OUTPUT, GPIO_NO_PULL,
					GPIO_8MA, GPIO_DISABLE);

				/* configure I2C SCL gpio */
				gpio_tlmm_config(112, 3, GPIO_OUTPUT, GPIO_NO_PULL,
					GPIO_8MA, GPIO_DISABLE);
			break;
			case QUP_ID_3:
				/* configure I2C SDA gpio */
				gpio_tlmm_config(29, 3, GPIO_OUTPUT, GPIO_NO_PULL,
					GPIO_8MA, GPIO_DISABLE);

				/* configure I2C SCL gpio */
				gpio_tlmm_config(30, 3, GPIO_OUTPUT, GPIO_NO_PULL,
					GPIO_8MA, GPIO_DISABLE);
			break;
			case QUP_ID_4:
#if DSI2DPI_TC358762
				if ((HW_PLATFORM_SUBTYPE_LR3001 == hw_subtype)
					&& (HW_PLATFORM_MTP == hw_id)) {
					/* configure I2C SDA gpio */
					gpio_tlmm_config(18, 2, GPIO_OUTPUT, GPIO_NO_PULL,
							GPIO_2MA, GPIO_DISABLE);

					/* configure I2C SCL gpio */
					gpio_tlmm_config(19, 2, GPIO_OUTPUT, GPIO_NO_PULL,
							GPIO_2MA, GPIO_DISABLE);
				} else {
#endif
					/* configure I2C SDA gpio */
					gpio_tlmm_config(14, 3, GPIO_OUTPUT, GPIO_NO_PULL,
							GPIO_8MA, GPIO_DISABLE);
					/* configure I2C SCL gpio */

					gpio_tlmm_config(15, 3, GPIO_OUTPUT, GPIO_NO_PULL,
							GPIO_8MA, GPIO_DISABLE);
#if DSI2DPI_TC358762
				}
#endif
			break;
			case QUP_ID_5:
				/* configure I2C SDA gpio */
				gpio_tlmm_config(18, 3, GPIO_OUTPUT, GPIO_NO_PULL,
					GPIO_8MA, GPIO_DISABLE);

				/* configure I2C SCL gpio */
				gpio_tlmm_config(19, 3, GPIO_OUTPUT, GPIO_NO_PULL,
					GPIO_8MA, GPIO_DISABLE);
/* Begin: add by leo for add i2c-6(gpio_10 & gpio_11) */
#if 1
				/* configure I2C SDA gpio */
				gpio_tlmm_config(10, 2, GPIO_OUTPUT, GPIO_NO_PULL,
					GPIO_8MA, GPIO_DISABLE);

				/* configure I2C SCL gpio */
				gpio_tlmm_config(11, 2, GPIO_OUTPUT, GPIO_NO_PULL,
					GPIO_8MA, GPIO_DISABLE);
				dprintf(CRITICAL, "leo i2c-%d (gpio10 & gpio11)\n", qup_id + 1);
#endif
/* End: add by leo for add i2c-6(gpio_10 & gpio_11) */
			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);
	}
}

從以上呼叫流程可以看出,整個流程就是根據BLSP_ID 和QUP_ID 去初始化不同的gpio。

所以在這裡,需要判斷在 case QUP_ID_5 中是否已經包含我們需要的gpio管腳(gpio10、gpio11)的初配置。

程式碼修改:

/* Begin: add by leo for add i2c-6(gpio_10 & gpio_11) */
#if 1
				/* configure I2C SDA gpio */
				gpio_tlmm_config(10, 2, GPIO_OUTPUT, GPIO_NO_PULL,
					GPIO_8MA, GPIO_DISABLE);

				/* configure I2C SCL gpio */
				gpio_tlmm_config(11, 2, GPIO_OUTPUT, GPIO_NO_PULL,
					GPIO_8MA, GPIO_DISABLE);
				dprintf(CRITICAL, "leo i2c-%d (gpio10 & gpio11)\n", qup_id + 1);
#endif
/* End: add by leo for add i2c-6(gpio_10 & gpio_11) */

顯然沒有,那麼就需要在case QUP_ID_5:新增 gpio10、gpio11的初始化。

注意這裡:這裡配置為功能2,是複用為i2c功能

依據:

80-np408-2x_c_msm8909_msm8209_msm8208_hardware_register_description_document_for_oems.pdf

page-2624 介紹:

配置為功能 2 是 IIC功能。

2.4 blsp6 時鐘源的配置

clock_config_blsp_i2c(blsp_id, qup_id);

看函式定義:

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;
	qup_id = qup_id + 1;

	if((blsp_id != BLSP_ID_1)) {
		dprintf(CRITICAL, "Incorrect BLSP-%d configuration\n", blsp_id);
		ASSERT(0);
	}

	snprintf(clk_name, sizeof(clk_name), "blsp1_qup%u_ahb_iface_clk", qup_id);
	dprintf(CRITICAL, "leo i2c-%d clk_name=%s \n", qup_id, clk_name);

	ret = clk_get_set_enable(clk_name, 0 , 1);

	if (ret) {
		dprintf(CRITICAL, "Failed to enable %s clock\n", clk_name);
		return;
	}

	snprintf(clk_name, sizeof(clk_name), "gcc_blsp1_qup%u_i2c_apps_clk", qup_id);
	dprintf(CRITICAL, "leo i2c-%d clk_name=%s \n", qup_id, clk_name);

	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;
	}
}

首先,qup_id = qup_id + 1; 這裡,對qup進行了自加1,那麼下面對應的時鐘源引數就變成了blsp6。

且能看出,需要的是 blsp1_qup6_ahb_iface_clk 和 gcc_blsp1_qup6_i2c_apps_clk,這兩個時鐘源。

	snprintf(clk_name, sizeof(clk_name), "blsp1_qup%u_ahb_iface_clk", qup_id);
	dprintf(CRITICAL, "leo i2c-%d clk_name=%s \n", qup_id, clk_name);

	ret = clk_get_set_enable(clk_name, 0 , 1);

	if (ret) {
		dprintf(CRITICAL, "Failed to enable %s clock\n", clk_name);
		return;
	}

	snprintf(clk_name, sizeof(clk_name), "gcc_blsp1_qup%u_i2c_apps_clk", qup_id);
	dprintf(CRITICAL, "leo i2c-%d clk_name=%s \n", qup_id, clk_name);

	qup_clk = clk_get(clk_name);

可以看clk_get_set_enable 和 clk_get 的定義

clk_get_set_enable

bootable\bootloader\lk\platform\msm_shared\clock.c

函式定義略

clk_get

bootable\bootloader\lk\platform\msm_shared\clock.c

struct clk *clk_get (const char * cid)
{
	unsigned i;
	struct clk_lookup *cl= msm_clk_list.clist;
	unsigned num = msm_clk_list.num;

	if(!cl || !num)
	{
		dprintf (CRITICAL, "Alert!! clock list not defined!\n");
		return NULL;
	}
	for(i=0; i < num; i++, cl++)
	{
		if(!strcmp(cl->con_id, cid))
		{
			return cl->clk;
		}
	}

	dprintf(CRITICAL, "Alert!! Requested clock \"%s\" is not supported!\n", cid);
	return NULL;
}

分析:都是從 bootable\bootloader\lk\platform\msm_shared\clock.c 中的static struct clk_list msm_clk_list; 全域性clk list 中獲取的資訊。

找到在哪初始化的:

路徑:
bootable\bootloader\lk\platform\msm_shared\clock.c

void clk_init(struct clk_lookup *clist, unsigned num)
{
 if(clist && num)
 {
  msm_clk_list.clist = (struct clk_lookup *)clist;
  msm_clk_list.num = num;
 }
}

找到在哪呼叫的:

路徑:

bootable\bootloader\lk\platform\msm8909\msm8909-clock.c

void platform_clock_init(void)
{
 clk_init(msm_clocks_msm8909, ARRAY_SIZE(msm_clocks_msm8909));
}

其中msm_clocks_msm8909 這個clk 結構體陣列,就是我們要修改的目標 。

此處需對應 clock_config_blsp_i2c 函式中的分析。

最終我們需要的有三個時鐘源配置,根據實際程式碼去修改。

bootable\bootloader\lk\platform\msm8909\msm8909-clock.c
msm_clocks_msm8909 陣列中

#else
	CLK_LOOKUP("blsp1_qup6_ahb_iface_clk", gcc_blsp1_ahb_clk.c),
	CLK_LOOKUP("gcc_blsp1_qup6_i2c_apps_clk_src", gcc_blsp1_qup6_i2c_apps_clk_src.c),
	CLK_LOOKUP("gcc_blsp1_qup6_i2c_apps_clk", gcc_blsp1_qup6_i2c_apps_clk.c),
#endif

4. 總結

那麼總結下,所有的配置修改。

1. 呼叫I2C 初始化函式

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)

2. 檢查gpio配置

檢視I2C對應的gpio配置是否包含,否則去新增

/* Begin: add by leo for add i2c-6(gpio_10 & gpio_11) */
#if 1
				/* configure I2C SDA gpio */
				gpio_tlmm_config(10, 2, GPIO_OUTPUT, GPIO_NO_PULL,
					GPIO_8MA, GPIO_DISABLE);

				/* configure I2C SCL gpio */
				gpio_tlmm_config(11, 2, GPIO_OUTPUT, GPIO_NO_PULL,
					GPIO_8MA, GPIO_DISABLE);
				dprintf(CRITICAL, "leo i2c-%d (gpio10 & gpio11)\n", qup_id + 1);
#endif
/* End: add by leo for add i2c-6(gpio_10 & gpio_11) */

3. 檢查I2C src clk 配置,沒有則新增

bootable\bootloader\lk\platform\msm8909\msm8909-clock.c
msm_clocks_msm8909 陣列中

#else
	CLK_LOOKUP("blsp1_qup6_ahb_iface_clk", gcc_blsp1_ahb_clk.c),
	CLK_LOOKUP("gcc_blsp1_qup6_i2c_apps_clk_src", gcc_blsp1_qup6_i2c_apps_clk_src.c),
	CLK_LOOKUP("gcc_blsp1_qup6_i2c_apps_clk", gcc_blsp1_qup6_i2c_apps_clk.c),
#endif

4. I2C 讀函式demo。 (暫存器地址和資料都以8bit為例)

static uint8_t adv7533_i2c_read(uint8_t slave_addr, uint8_t reg)
{
	int ret;
	int err_return = -1;
	uint8_t value;

	dprintf(SPEW, "%s: Leo enter \n", __func__);

	uint8_t tx_data[] = {
		reg & 0xff,
	};

	uint8_t rx_data[1];
	struct i2c_msg msgs[] = {
		{
			.addr = slave_addr,
			.flags = I2C_M_WR,
			.buf = tx_data,
			.len = ADV_ARRAY_SIZE(tx_data),
		},
		{
			.addr = slave_addr,
			.flags = I2C_M_RD,
			.buf = rx_data,
			.len = ADV_ARRAY_SIZE(rx_data),
		}
	};

	ret = qup_i2c_xfer(i2c6_dev, msgs, ADV_ARRAY_SIZE(msgs));
	if (ret < 0) {
		dprintf(CRITICAL, "%s: reg 0x%04x error %d\n", __func__, reg, ret);
		return ret;
	}
	if (ret < ADV_ARRAY_SIZE(msgs)) {
		dprintf(CRITICAL, "%s: reg 0x%04x msgs %d\n", __func__, reg, ret);
		return err_return;
	}

	value = rx_data[0];

	dprintf(CRITICAL, "%s: reg 0x%x 0x%x\n", __func__, reg, value);
	return value;
}

5. I2C 寫函式 demo

static int adv7533_i2c_write(uint8_t slave_addr, uint8_t reg, uint8_t value)
{
	int ret = 0;
	uint8_t tx_data[2] = {reg, value};

	/* Send commands via I2C */
	struct i2c_msg msgs[1] = {
		{
			.addr = slave_addr,
			.flags = I2C_M_WR, // 0
			.len = 2,
			.buf = tx_data,
		},
	};

	ret = qup_i2c_xfer(i2c6_dev, msgs, 1);
	if (ret < 0) {
		dprintf(CRITICAL, "%s failed ret=%d\n", __func__, ret);
		return ret;
	}
	mdelay(2);
	return 0;
}