Qcom 平台 LK 阶段配置 I2C[通俗易懂]

Qcom 平台 LK 阶段配置 I2C[通俗易懂]本文着重介绍,如何在qcom平台的LK阶段配置和使用I2C。硬件平台:msm8909软件平台:Android5.0、Android8.0I2C设备:ADV7533

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全系列IDE稳定放心使用

qcom平台LK 阶段配置IIC

版本号:V 1.0 

作者:Leo 

前言

本文着重介绍,如何在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

Qcom 平台 LK 阶段配置 I2C[通俗易懂]

Qcom 平台 LK 阶段配置 I2C[通俗易懂]

确定芯片I2C内部的qup地址、中断号、通道等参数

根据文档:80-nu767-1_h_linux_bam_low-speed_peripherals_configuration_and_debug_guide.pdf

Qcom 平台 LK 阶段配置 I2C[通俗易懂]

从以上信息可以确定:

  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

Qcom 平台 LK 阶段配置 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)

参数含义:

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功能

Qcom 平台 LK 阶段配置 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;
}

 

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/180338.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)
blank

相关推荐

  • 基于深度学习的人脸性别识别系统(含UI界面,Python代码)「建议收藏」

    基于深度学习的人脸性别识别系统(含UI界面,Python代码)「建议收藏」摘要:人脸性别识别是人脸识别领域的一个热门方向,本文详细介绍基于深度学习的人脸性别识别系统,在介绍算法原理的同时,给出Python的实现代码以及PyQt的UI界面。在界面中可以选择人脸图片、视频进行检测识别,也可通过电脑连接的摄像头设备进行实时识别人脸性别;可对图像中存在的多张人脸进行性别识别,可选择任意一张人脸框选显示结果,检测速度快、识别精度高。博文提供了完整的Python代码和使用教程,适合新入门的朋友参考,完整代码资源文件请转至文末的下载链接。

  • c语言二维数组传参数_c语言数组传参

    c语言二维数组传参数_c语言数组传参初遇二维数组作函数参数,宛如自己化身为了大头儿子。很头大。不禁大声呐喊:该怎么声明定义,该怎么调用,又该怎么函数中操作元素和地址?在此,我要拨开这些问题的一些迷雾。我相信,有心人看完后,再遇就不会怕了。其实声明,定义是一样的。因此,只写声明。同时,把元素外层*()剥去就代表地址。因此只写元素。最后有总结。二维数组作函数参数,依我看来,至少可以分成三种。事先,在main函数中int…

    2022年10月31日
  • 面试技巧|“唇枪舌剑”之十大招式[通俗易懂]

    面试技巧|“唇枪舌剑”之十大招式

  • mysql之group_concat函数详解[通俗易懂]

    mysql之group_concat函数详解[通俗易懂]函数语法:group_concat([DISTINCT]要连接的字段[OrderBY排序字段ASC/DESC][Separator‘分隔符’])下面举例说明:selectid,pricefromgoods;以id分组,把price字段的值在同一行打印出来,逗号分隔(默认)selectid,group_concat(price…

  • N的阶乘(大数阶乘算法)

    N的阶乘(大数阶乘算法)题目描述输入一个正整数N,输出N的阶乘。输入描述:正整数N(0<=N<=1000)输出描述:输入可能包括多组数据,对于每一组输入数据,输出N的阶乘输入例子:4515输出例子:241201307674368000#include<iostream>#include<cstring>usingnames…

  • IGMP协议_igmp协议常用3种报文

    IGMP协议_igmp协议常用3种报文IGMP介绍

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号