Qcom platform LK stage configuration I2C

Qcom platform LK stage configuration IIC

Version number: V 1.0 

Author: Leo 

Preface

This article focuses on how to configure and use I2C in the LK phase of the qcom platform.

Hardware platform: msm8909

Software platform: Android5.0, Android8.0

I2C device: ADV7533

 

Reference blog and documentation:

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.  Determine the hardware

Check the schematic diagram to determine the gpio

Determine the qup address, interrupt number, channel and other parameters inside the chip I2C

According to the document: 80-nu767-1_h_linux_bam_low-speed_peripherals_configuration_and_debug_guide.pdf

From the above information, it can be determined:

  1. We are going to use the IIC corresponding to gpio10 and gpio11
  2. The internal parameters of the chip are
    1. Qup ID: BLSP6
    2. QUP BASE Addr: 78BA000
    3. IRQ#: 100
    4. src clk:qup6_i2c_apps_clk

2.   LK I2C function interface

According to the qcom documentation:

80-nu767-1_h_linux_bam_low-speed_peripherals_configuration_and_debug_guide.pdf

It can be seen that the I2C function interface of qcom LK stage is: qup_blsp_i2c_init

Function definition:

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)

Parameter meaning:

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;

 (For details, refer to the base addr of qup . If you use IIC-6 corresponding to the address 78ba000 , then pass QUP_ID_5)

uint32_t clk_freq:

IIC总线速率,默认 100K

uint32_t src_clk_freq:

时钟源频率,默认19.2MHz

Old rules, first analyze the implementation process of this function.

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

From the above process, there are 5 areas to pay attention to

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的初始化过程

 

Analysis one by one:

2.1 qup irq interrupt number assignment

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

Interrupt number, use the BLSP_QUP_IRQ macro to assign a value, then find its definition:

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)

From the qup information in Chapter 1, we can see that the interrupt number of qup6 is 100, then the passed parameter qup_id is 5, and the interrupt number is

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

It can be guessed here that qup_id should be passed as 5 .

 

2.2 qup base addr assignment

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

Qup base address assignment, use BLSP_QUP_BASE to assign, find its definition

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)

According to the qup information diagram in Chapter 1, the base addr of qup6 is 78ba000, so the qup_id parameter here should be 5

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

So far, from qup irq information and the qup base addr confirmation message, then the I2C-. 6 (GPIO10 , GPIO11) corresponding Qup transmission parameters should be QUP_ID_5 .

 

2.3 blsp6 gpio initialization

/* Initialize the GPIO for BLSP i2c */

gpio_config_blsp_i2c(blsp_id, qup_id);

Look at the gpio_config_blsp_i2c function definition.

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

It can be seen from the above calling process that the whole process is to initialize different gpio based on BLSP_ID and QUP_ID.

So here, we need to determine whether the initial configuration of the gpio pins (gpio10, gpio11) we need is included in case QUP_ID_5.

Code modification:

/* 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) */

Obviously not, so you need to add gpio10 and gpio11 initialization in case QUP_ID_5.

Note here: here is configured as function 2 , which is multiplexed as i2c function

in accordance with:

80-np408-2x_c_msm8909_msm8209_msm8208_hardware_register_description_document_for_oems.pdf

page-2624 Introduction:

Configured as function 2 is an IIC function.

 

2.4 blsp6 clock source configuration

clock_config_blsp_i2c(blsp_id, qup_id);

Look at the function definition:

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

 

First of all, qup_id = qup_id + 1; Here, qup is self-incremented by 1, then the corresponding clock source parameter below becomes blsp6.

And it can be seen that what is needed is blsp1_qup6_ahb_iface_clk and gcc_blsp1_qup6_i2c_apps_clk, these two clock sources.

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

See the definition of clk_get_set_enable and clk_get

clk_get_set_enable

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

Function definition

 

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

Analysis: All information is obtained from static struct clk_list msm_clk_list; global clk list in bootable\bootloader\lk\platform\msm_shared\clock.c .

Find where it was initialized:

路径:
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;
    }
}

Find where it was called:

路径:

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

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

The clk structure array msm_clocks_msm8909 is the target we want to modify.

This needs to correspond to the analysis in the clock_config_blsp_i2c function.

In the end, we need three clock source configurations, which are modified according to the actual code.

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. Summary

So in summary, all configuration changes.

1. Call the I2C initialization function

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. Check gpio configuration

Check whether the gpio configuration corresponding to I2C is included, otherwise add it

/* 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. Check the I2C src clk configuration, add if not

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 read function demo. (The register address and data are both 8bit as an example)

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

 

Guess you like

Origin blog.csdn.net/FANG_YISHAO/article/details/113180983
I2C