zoukankan      html  css  js  c++  java
  • 高通 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/
  • 相关阅读:
    自己感受,如何解读别人的好的PHP代码
    34. RunAs Authentication Replacement运行身份验证替换
    32.3.3 Authenticating to a Stateless Service with CAS(使用CAS向无状态服务进行身份验证)
    33.2 Adding X.509 Authentication to Your Web Application
    35.2 Encryptors加密器
    32.3.2 Single Logout(单点退出)
    33. X.509 Authentication
    34.2 Configuration
    32.3.4 Proxy Ticket Authentication代理票证认证
    35.3 Key Generators密钥生成器
  • 原文地址:https://www.cnblogs.com/schips/p/using_i2c_in_qualcomm_little_kernel.html
Copyright © 2011-2022 走看看