IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 系统运维 -> 具体芯片的I2C_Adapter驱动分析 -> 正文阅读

[系统运维]具体芯片的I2C_Adapter驱动分析

参考资料

  • Linux内核真正的I2C驱动控制器驱动程序。drivers\i2c\busses\i2c-imx.c
  • 芯片手册:IMX6ULL:Chapter 31: I2C Controller (I2C)

一、I2C控制器内部结构

I2C单独控制的话,占用CPU的时间非常高,这样不利于系统高效运转,所以一般的芯片里面都会有I2C控制器。

1.1 通用的简化结构

请添加图片描述

I2C控制器里面一般会有很多的寄存器。比如:

  • controller_register可以设置I2C控制器的频率
  • tx_register、rx_register控制数据的传输
  • status_register控制I2C的状态
  • int_register控制I2C的中断

当CPU需要发送数据时,只需要写给tx_register。后面的I2C控制器会自动帮忙做好,在SDA上发送数据。
当程序写入tx_register后,就不用管了。直到数据发送结束,会产生一个发送完成的中断。

1.2 IMX6ULL的I2C控制器内部结构

请添加图片描述imx6ull有

  • I2C_IFDR(频率寄存器)
  • I2C_I2CR(控制寄存器
  • I2C_I2SR(状态寄存器)
  • I2C_I2DR(数据I/O寄存器)
  • I2C_IADR(地址寄存器)

1.3 STM32MP157的I2C控制器内部结构体

请添加图片描述

  • i2c_ker_ck时钟
  • Data control(数据控制),shift register(移位寄存器)
    当CPU把数据写入移位寄存器后,通过寄存器、通过SDA发送出去。接收数据则是相反的路径。
  • Clock control(时钟控制)

二、I2C控制器操作方法

  1. 使能使用、设置时钟
  2. 发送数据:
    • 把数据写入tx_register,等待中断发生
    • 中断发生后,判断状态:是否发生错误、是否得到回应信号(ACK)
    • 把下一个数据写入tx_register,等待中断:如此循环
  3. 接收数据:
    • 设置controller_register,进入接收模式,启动接收,等待中断发生
    • 中断发生后,判断状态,读取tx_register得到数据
    • 如此循环

三、分析代码

3.1 设备树

  1. imx6ull:arch/arm/boot/dts/imx6ull.dtsi
i2c1: i2c@02221a0000 {
	#address-cells = <1>;
	#size-cells = <0>;
	compatible = <"fsl,imx6ul-i2c", "fsl,imx21-i2c";
	reg = <0x021a0000 0x40000>;
	interrupts = <GIC_SPI 36 IRQ_TYPE_LEVEL_HIGH>;
	clocks = <&clks IMX6UL_CLK_I2C1>;
	status = "disabled";		//在100ask_imx6ull-14x14.dts 把它改成了"okay"
};
  1. STM32MP157:arch/arm/boot/dts/stm32mp151.dtsi
  i2c1: i2c@40012000 {
  		compatible = "st,stm32mp15-i2c";
  		reg = <0x40012000 0x400>;
  		interrupt-names = "event", "error";
  		interrupts-extended = <&exti 21 IRQ_TYPE_LEVEL_HIGH>,
  							  <&intc GIC_SPI 32 IRQ_TYPE_LEVEL_HIGH>;
  		clocks = <&rcc I2C1_K>;
  		resets = <&rcc I2C1_R>;
  		#address-cells = <1>;
  		#size-cells = <0>;
  		dmas = <&dmamux1 33 0x400 0x80000001>,
  			   <&dmamux1 34 0x400 0x80000001>;
  		dma-names = "rx", "tx";
  		power-domains = <&pd_core>;
  		st,syscfg-fmp = <&syscfg 0x4 0x1>;
  		wakeup-source;
  		status = "disabled";   // 在stm32mp15xx-100ask.dtsi把它改为了"okay"
  };

3.2 驱动程序分析

读I2C数据时,要先发出设备地址,这是写操作,然后再发起读操作,涉及写、读操作。
所以可以以读I2C数据为例程学习核心代码。

  1. IMX6ULL
    在文件drivers/i2c/busses/i2c-imx.c中,分析i2c的具体程序:
static struct platform_driver i2c_imx_driver = {
    .probe = i2c_imx_probe,
    .remove = i2c_imx_remove,
    .driver = {
        .name = DRIVER_NAME,
        .pm = I2C_IMX_PM_OPS,
        .of_match_table = i2c_imx_dt_ids,					//与设备树匹配后调用probe函数
    },
    .id_table = imx_i2c_devtype,
};

//----probe函数-----
static int i2c_imx_probe(struct platform_device *pdev)
{
	const struct of_device_id *of_id = of_match_device(i2c_imx_dt_ids,
							   &pdev->dev);
	struct imx_i2c_struct *i2c_imx;
	struct resource *res;
	struct imxi2c_platform_data *pdata = dev_get_platdata(&pdev->dev);
	void __iomem *base;
	int irq, ret;
	dma_addr_t phy_addr;

	irq = platform_get_irq(pdev, 0);							//获取中断号
	//...
	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);		//获取寄存器
	base = devm_ioremap_resource(&pdev->dev, res);			//映射资源
	//...

	phy_addr = (dma_addr_t)res->start;
	i2c_imx = devm_kzalloc(&pdev->dev, sizeof(*i2c_imx), GFP_KERNEL);	//分配i2c_imx结构体
	//...

	/* Setup i2c_imx driver structure 设置i2c_imx结构体*/
	strlcpy(i2c_imx->adapter.name, pdev->name, sizeof(i2c_imx->adapter.name));
	i2c_imx->adapter.owner		= THIS_MODULE;
	i2c_imx->adapter.algo		= &i2c_imx_algo;					//设置I2C传输算法
	i2c_imx->adapter.dev.parent	= &pdev->dev;
	i2c_imx->adapter.nr		= pdev->id;
	i2c_imx->adapter.dev.of_node	= pdev->dev.of_node;
	i2c_imx->base			= base;

	/* Get I2C clock */
	i2c_imx->clk = devm_clk_get(&pdev->dev, NULL);
	ret = clk_prepare_enable(i2c_imx->clk);		//获取clk频率并激活
	//...
	
	/* Request IRQ */
	ret = devm_request_irq(&pdev->dev, irq, i2c_imx_isr,
			       IRQF_NO_SUSPEND, pdev->name, i2c_imx);		//注册中断函数

	/* Init queue */
	init_waitqueue_head(&i2c_imx->queue);

	/* Set up adapter data */
	i2c_set_adapdata(&i2c_imx->adapter, i2c_imx);			//设置adapter data

	/* Set up platform driver data */
	platform_set_drvdata(pdev, i2c_imx);			//设置platform driver data

	//.....

	return 0;   /* Return OK */
//....
}

//--------I2C传输函数分析-------
static struct i2c_algorithm i2c_imx_algo = {
    .master_xfer    = i2c_imx_xfer,
    .functionality  = i2c_imx_func,
};

static int i2c_imx_xfer(struct i2c_adapter *adapter,
						struct i2c_msg *msgs, int num)
{
	//...
	/* Start I2C transfer */
	result = i2c_imx_start(i2c_imx);				//开始i2c传输,这里设置I2CR_MSTA为1的时候就发送start信号
	//...

	/* read/write data */
	for (i = 0; i < num; i++) {
		if (i == num - 1)								//最后一个msg
			is_lastmsg = true;

		if (i) {					//中间的数据
			dev_dbg(&i2c_imx->adapter.dev,
				"<%s> repeated start\n", __func__);
			temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR);
			temp |= I2CR_RSTA;
			imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR);
			result = i2c_imx_bus_busy(i2c_imx, 1);
			if (result)
				goto fail0;
		}
		dev_dbg(&i2c_imx->adapter.dev,
			"<%s> transfer message: %d\n", __func__, i);
		/* write/read data */
		//....
		if (msgs[i].flags & I2C_M_RD)			//如果是第一个msg,如果是READ操作
			result = i2c_imx_read(i2c_imx, &msgs[i], is_lastmsg);		//会写地址+读位,然后依次读取数据
		else {							//如果是写
			if (i2c_imx->dma && msgs[i].len >= DMA_THRESHOLD)
				result = i2c_imx_dma_write(i2c_imx, &msgs[i]);
			else
				result = i2c_imx_write(i2c_imx, &msgs[i]);
		}
		if (result)
			goto fail0;
	}

fail0:
	/* Stop I2C transfer */
	i2c_imx_stop(i2c_imx);				//当数据全部处理完毕后,就发送一个P信号。和start类似,I2CR_MSTA清零就会发送停止信号

	pm_runtime_mark_last_busy(i2c_imx->adapter.dev.parent);
	pm_runtime_put_autosuspend(i2c_imx->adapter.dev.parent);

out:
	//...
}

请添加图片描述

  1. STM32MP157

请添加图片描述
请添加图片描述

  1. rk3288
    在文件drivers/i2c/busses/i2c-rk3x.c中,分析i2c的具体程序:
static struct platform_driver rk3x_i2c_driver = {
    .probe   = rk3x_i2c_probe,
    .remove  = rk3x_i2c_remove,
    .driver  = {
        .name  = "rk3x-i2c",
        .of_match_table = rk3x_i2c_match,		//当这里和设备树里的compatible匹配时,probe函数就会被调用
        .pm = &rk3x_i2c_pm_ops,
    },
};

module_platform_driver(rk3x_i2c_driver);

//-------------------------------------

  系统运维 最新文章
配置小型公司网络WLAN基本业务(AC通过三层
如何在交付运维过程中建立风险底线意识,提
快速传输大文件,怎么通过网络传大文件给对
从游戏服务端角度分析移动同步(状态同步)
MySQL使用MyCat实现分库分表
如何用DWDM射频光纤技术实现200公里外的站点
国内顺畅下载k8s.gcr.io的镜像
自动化测试appium
ctfshow ssrf
Linux操作系统学习之实用指令(Centos7/8均
上一篇文章      下一篇文章      查看所有文章
加:2021-09-13 09:39:24  更:2021-09-13 09:41:47 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/15 16:26:05-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码