I.MX6ULL的I2C控制器的驱动分析说明一

2024-04-04 18:12

本文主要是介绍I.MX6ULL的I2C控制器的驱动分析说明一,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

一.  简介

Linux 内核也将 I2C 驱动分为两部分:
(1)  I2C 总线驱动, I2C 总线驱动就是 SOC 的 I2C 控制器驱动,也叫做 I2C 适配器驱动。
(2)  I2C 设备驱动, I2C 设备驱动就是针对具体的 I2C 设备而编写的驱动。

前面几篇文章学习了I2C驱动框架。

本文来简单分析一下 IMX6ULL的IMX6ULL的I2C控制器的驱动。

二.  I.MX6ULL的I2C控制器的驱动分析

1.  查找 IMX6ULL的I2C控制器的驱动

我们讲解了 Linux 下的 I2C 驱动框架,重点分为 I2C 适配器驱动和 I2C 设备驱动, 其中 I2C 适配器驱动就是 SOC I2C 控制器驱动。 I2C 设备驱动是需要用户根据不同的 I2C 备去编写。
I2C 适配器驱动一般都是 SOC 厂商去编写的,比如, NXP 就编写好了 I.MX6U I2C 适配器驱动。在 imx6ull.dtsi 文件中找到 I.MX6U I2C1 控制器节点,节点内容如下所示:
			i2c1: i2c@021a0000 {#address-cells = <1>;#size-cells = <0>;compatible = "fsl,imx6ul-i2c", "fsl,imx21-i2c";reg = <0x021a0000 0x4000>;interrupts = <GIC_SPI 36 IRQ_TYPE_LEVEL_HIGH>;clocks = <&clks IMX6UL_CLK_I2C1>;status = "disabled";};
重点关注 i2c1 节点的 compatible 属性值,因为通过 compatible 属性值可以在 Linux 源码里
面找到对应的驱动文件。
这里 i2c1 节点的 compatible 属性值有两个:“ fsl,imx6ul-i2c ” 和 “ fsl,imx21- i2c ”,在 Linux 源码中搜索这两个字符串即可找到对应的驱动文件。
I.MX6U I2C 适配器驱动 的 驱动文件为 drivers/i2c/busses/i2c-imx.c ,在此文件中有如下内容:
static struct platform_device_id imx_i2c_devtype[] = {{.name = "imx1-i2c",.driver_data = (kernel_ulong_t)&imx1_i2c_hwdata,}, {.name = "imx21-i2c",.driver_data = (kernel_ulong_t)&imx21_i2c_hwdata,}, {/* sentinel */}
};
MODULE_DEVICE_TABLE(platform, imx_i2c_devtype);static const struct of_device_id i2c_imx_dt_ids[] = {{ .compatible = "fsl,imx1-i2c", .data = &imx1_i2c_hwdata, },{ .compatible = "fsl,imx21-i2c", .data = &imx21_i2c_hwdata, },{ .compatible = "fsl,vf610-i2c", .data = &vf610_i2c_hwdata, },{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, i2c_imx_dt_ids);
........................
static struct platform_driver i2c_imx_driver = {.probe = i2c_imx_probe,.remove = i2c_imx_remove,.driver	= {.name = DRIVER_NAME,.owner = THIS_MODULE,.of_match_table = i2c_imx_dt_ids,.pm = IMX_I2C_PM,},.id_table	= imx_i2c_devtype,
};static int __init i2c_adap_imx_init(void)
{return platform_driver_register(&i2c_imx_driver);
}
subsys_initcall(i2c_adap_imx_init);static void __exit i2c_adap_imx_exit(void)
{platform_driver_unregister(&i2c_imx_driver);
}
module_exit(i2c_adap_imx_exit);

可以看出, I.MX6U I2C 适配器驱动是个标准的 platform 驱动,由此 可以看出,虽然 I2C 总线为别的设备提供了一种总线驱动框架,但是 I2C 适配器却是 platform 驱动。
6 行,“ fsl,imx21-i2c ”属性值,设备树中 i2c1 节点的 compatible 属性值就是与此匹配 上的。因此 i2c-imx.c 文件就是 I.MX6U I2C 适配器驱动文件。
23 行,当设备和驱动匹配成功以后, i2c_imx_probe 函数就会执行,i2c_imx_probe 函数就会完成 I2C 适配器初始化工作。

2.  i2c_imx_probe 函数

i2c_imx_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;dev_dbg(&pdev->dev, "<%s>\n", __func__);irq = platform_get_irq(pdev, 0);
..........................res = platform_get_resource(pdev, IORESOURCE_MEM, 0);base = devm_ioremap_resource(&pdev->dev, res);if (IS_ERR(base))return PTR_ERR(base);phy_addr = (dma_addr_t)res->start;i2c_imx = devm_kzalloc(&pdev->dev, sizeof(*i2c_imx), GFP_KERNEL);if (!i2c_imx)return -ENOMEM;if (of_id)i2c_imx->hwdata = of_id->data;elsei2c_imx->hwdata = (struct imx_i2c_hwdata *)platform_get_device_id(pdev)->driver_data;/* Setup i2c_imx driver structure */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_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);
...................../* 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);/* Set up clock divider */i2c_imx->bitrate = IMX_I2C_BIT_RATE;ret = of_property_read_u32(pdev->dev.of_node,"clock-frequency", &i2c_imx->bitrate);if (ret < 0 && pdata && pdata->bitrate)i2c_imx->bitrate = pdata->bitrate;/* Set up chip registers to defaults */imx_i2c_write_reg(i2c_imx->hwdata->i2cr_ien_opcode ^ I2CR_IEN,i2c_imx, IMX_I2C_I2CR);imx_i2c_write_reg(i2c_imx->hwdata->i2sr_clr_opcode, i2c_imx, IMX_I2C_I2SR);/* Add I2C adapter */ret = i2c_add_numbered_adapter(&i2c_imx->adapter);if (ret < 0) {dev_err(&pdev->dev, "registration failed\n");goto clk_disable;}/* Set up platform driver data */platform_set_drvdata(pdev, i2c_imx);clk_disable_unprepare(i2c_imx->clk);......................../* Init DMA config if supported */i2c_imx_dma_request(i2c_imx, phy_addr);return 0;   /* Return OK */clk_disable:clk_disable_unprepare(i2c_imx->clk);return ret;
}
14 行,调用 platform_get_irq 函数获取中断号。
17~18 行,调用 platform_get_resource 函数从设备树中获取 I2C1 控制器寄存器物理基 地址,也就是 0X021A0000 。获取到寄存器基地址以后,使用 devm_ioremap_resource 函数对其进 行内存映射,得到可以在 Linux 内核中使用的虚拟地址。
23 行, NXP 使用 imx_i2c_struct 结构体来表示 I.MX 系列 SOC I2C 控制器,这里使用 devm_kzalloc 函数来申请内存。
35~40 行, imx_i2c_struct 结构体要有个叫做 adapter 的成员变量, adapter 就是 i2c_adapter ,这里初始化 i2c_adapter
36 行,设置 i2c_adapteralgo 成员变量 i2c_imx_algo也就是设置 i2c_algorithm
1028~1029 行,注册 I2C 控制器中断,中断服务函数为 i2c_imx_isr
1042~1044 行,设置 I2C 频率默认为 IMX_I2C_BIT_RATE=100KHz ,如果设备树节点设 置了“ clock-frequency ”属性的话 I2C 频率就使用 clock-frequency 属性值。
49~50 行,设置 I2C1 控制的 I2CR I2SR 寄存器。
72 行,调用 i2c_add_numbered_adapter 函数,向 Linux 内核注册 i2c_adapter
85 行,申请 DMA ,看来 I.MX I2C 适配器驱动采用了 DMA 方式。

i2c_imx_probe 函数主要的工作就是以下两点:

(1)  初始化 i2c_adapter ,设置 i2c_algorithm i2c_imx_algo ,最后向 Linux 内核注册
i2c_adapter
(2)  初始化 I2C1 控制器的相关寄存器。
i2c_imx_algo 包含 I2C1 适配器与 I2C 设备的通信函数: master_xfer()函数。
i2c_imx_algo 结构体定 义如下:
static struct i2c_algorithm i2c_imx_algo = {.master_xfer	= i2c_imx_xfer,.functionality	= i2c_imx_func,
};

functionality 用于返回此 I2C适配器支持什么样的通信协议。这里是 i2c_imx_func 函数。
i2c_imx_xfer 函数很重要,最终就是通过此函数来完成与 I2C 设备通信的。

接下来重点看一下 i2c_imx_xfer 函数的实现。

这篇关于I.MX6ULL的I2C控制器的驱动分析说明一的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/876499

相关文章

Linux join命令的使用及说明

《Linuxjoin命令的使用及说明》`join`命令用于在Linux中按字段将两个文件进行连接,类似于SQL的JOIN,它需要两个文件按用于匹配的字段排序,并且第一个文件的换行符必须是LF,`jo... 目录一. 基本语法二. 数据准备三. 指定文件的连接key四.-a输出指定文件的所有行五.-o指定输出

Nginx分布式部署流程分析

《Nginx分布式部署流程分析》文章介绍Nginx在分布式部署中的反向代理和负载均衡作用,用于分发请求、减轻服务器压力及解决session共享问题,涵盖配置方法、策略及Java项目应用,并提及分布式事... 目录分布式部署NginxJava中的代理代理分为正向代理和反向代理正向代理反向代理Nginx应用场景

Redis中Hash从使用过程到原理说明

《Redis中Hash从使用过程到原理说明》RedisHash结构用于存储字段-值对,适合对象数据,支持HSET、HGET等命令,采用ziplist或hashtable编码,通过渐进式rehash优化... 目录一、开篇:Hash就像超市的货架二、Hash的基本使用1. 常用命令示例2. Java操作示例三

Redis中Set结构使用过程与原理说明

《Redis中Set结构使用过程与原理说明》本文解析了RedisSet数据结构,涵盖其基本操作(如添加、查找)、集合运算(交并差)、底层实现(intset与hashtable自动切换机制)、典型应用场... 目录开篇:从购物车到Redis Set一、Redis Set的基本操作1.1 编程常用命令1.2 集

Redis中的有序集合zset从使用到原理分析

《Redis中的有序集合zset从使用到原理分析》Redis有序集合(zset)是字符串与分值的有序映射,通过跳跃表和哈希表结合实现高效有序性管理,适用于排行榜、延迟队列等场景,其时间复杂度低,内存占... 目录开篇:排行榜背后的秘密一、zset的基本使用1.1 常用命令1.2 Java客户端示例二、zse

Redis中的AOF原理及分析

《Redis中的AOF原理及分析》Redis的AOF通过记录所有写操作命令实现持久化,支持always/everysec/no三种同步策略,重写机制优化文件体积,与RDB结合可平衡数据安全与恢复效率... 目录开篇:从日记本到AOF一、AOF的基本执行流程1. 命令执行与记录2. AOF重写机制二、AOF的

Python sys模块的使用及说明

《Pythonsys模块的使用及说明》Pythonsys模块是核心工具,用于解释器交互与运行时控制,涵盖命令行参数处理、路径修改、强制退出、I/O重定向、系统信息获取等功能,适用于脚本开发与调试,需... 目录python sys 模块详解常用功能与代码示例获取命令行参数修改模块搜索路径强制退出程序标准输入

MyBatis Plus大数据量查询慢原因分析及解决

《MyBatisPlus大数据量查询慢原因分析及解决》大数据量查询慢常因全表扫描、分页不当、索引缺失、内存占用高及ORM开销,优化措施包括分页查询、流式读取、SQL优化、批处理、多数据源、结果集二次... 目录大数据量查询慢的常见原因优化方案高级方案配置调优监控与诊断总结大数据量查询慢的常见原因MyBAT

分析 Java Stream 的 peek使用实践与副作用处理方案

《分析JavaStream的peek使用实践与副作用处理方案》StreamAPI的peek操作是中间操作,用于观察元素但不终止流,其副作用风险包括线程安全、顺序混乱及性能问题,合理使用场景有限... 目录一、peek 操作的本质:有状态的中间操作二、副作用的定义与风险场景1. 并行流下的线程安全问题2. 顺

MyBatis/MyBatis-Plus同事务循环调用存储过程获取主键重复问题分析及解决

《MyBatis/MyBatis-Plus同事务循环调用存储过程获取主键重复问题分析及解决》MyBatis默认开启一级缓存,同一事务中循环调用查询方法时会重复使用缓存数据,导致获取的序列主键值均为1,... 目录问题原因解决办法如果是存储过程总结问题myBATis有如下代码获取序列作为主键IdMappe