Linux I2C 驱动 24C256 E2PROM

Posted “逛丢一只鞋”

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Linux I2C 驱动 24C256 E2PROM相关的知识,希望对你有一定的参考价值。

I2C 总线驱动

之前类似于input输入设备都是挂载在platform总线上,platform 是虚拟出来的一条总线,目的是为了实现总线、设备、驱动框架。

对于I2C 而言,不需要虚拟出一条总线,直接使用I2C总线即可。

I2C 总线驱动重点是I2C 适配器(也就是SOC 的I2C 接口控制器)驱动,这里要用到两个重要的数据结构:i2c_adapteri2c_algorithm,Linux 内核将SOC 的I2C 适配器(控制器)抽象成i2c_adapteri2c_adapter 结构体定义在include/linux/i2c.h 文件中,结构体内容如下:

i2c_algorithm 类型的指针变量algo,对于一个I2C 适配器,肯定要对外提供读写API 函数,设备驱动程序可以使用这些API 函数来完成读写操作。i2c_algorithm 就是I2C 适配器与IIC 设备进行通信的方法。

i2c_algorithm 结构体定义在include/linux/i2c.h 文件中,内容如下:

master_xfer 就是I2C 适配器的传输函数,可以通过此函数来完成与IIC 设备之间的通信。

smbus_xfer 就是SMBUS 总线的传输函数

综上所述,I2C 总线驱动,或者说I2C 适配器驱动的主要工作就是初始化i2c_adapter 结构体变量,然后设置i2c_algorithm 中的master_xfer 函数。完成以后通过i2c_add_numbered_adapter或i2c_add_adapter 这两个函数向系统注册设置好的i2c_adapter,这两个函数的原型如下:

int i2c_add_adapter(struct i2c_adapter *adapter)
int i2c_add_numbered_adapter(struct i2c_adapter *adap)

这两个函数的区别在于i2c_add_adapter 使用动态的总线号,而i2c_add_numbered_adapter使用静态总线号。函数参数和返回值含义如下:

adapteradap:要添加到Linux 内核中的i2c_adapter,也就是I2C 适配器。

返回值:0,成功;负值,失败。

如果要删除I2C 适配器的话使用i2c_del_adapter 函数即可,函数原型如下:

void i2c_del_adapter(struct i2c_adapter * adap)

函数参数和返回值含义如下:

adap:要删除的I2C 适配器。

返回值:无。

关于I2C 的总线(控制器或适配器)驱动就讲解到这里,一般SOC 的I2C 总线驱动都是由半导体厂商编写的,比如I.MX6U 的I2C 适配器驱动NXP 已经编写好了,这个不需要用户去编写。因此I2C 总线驱动对我们这些SOC 使用者来说是被屏蔽掉的,我们只要专注于I2C 设备驱动即可.

I2C 设备驱动

I2C 设备驱动重点关注两个数据结构:i2c_clienti2c_driver,根据总线、设备和驱动模型,I2C 总线上一小节已经讲了。还剩下设备和驱动,i2c_client 就是描述设备信息的,i2c_driver 描述驱动内容,类似于platform_driver

i2c_client 结构体

i2c_client 结构体定义在include/linux/i2c.h 文件中,内容如下:

一个设备对应一个i2c_client,每检测到一个I2C 设备就会给这个I2C 设备分配一个i2c_client

i2c_driver 结构体

i2c_driver 类似platform_driver,是我们编写I2C 设备驱动重点要处理的内容,i2c_driver 结构体定义在include/linux/i2c.h 文件中,内容如下:

当I2C 设备和驱动匹配成功以后probe 函数就会执行,和platform 驱动一样。

device_driver 驱动结构体,如果使用设备树的话,需要设置device_driverof_match_table 成员变量,也就是驱动的兼容(compatible)属性。

id_table 是传统的、未使用设备树的设备匹配ID 表。

对于我们I2C 设备驱动编写人来说,重点工作就是构建i2c_driver,构建完成以后需要向Linux 内核注册这个i2c_driver。i2c_driver 注册函数为int i2c_register_driver,此函数原型如下:

int i2c_register_driver(struct module *owner, struct i2c_driver *driver)

函数参数和返回值含义如下:

owner:一般为THIS_MODULE。
driver:要注册的i2c_driver。
返回值:0,成功;负值,失败。

另外i2c_add_driver 也常常用于注册i2c_driver,i2c_add_driver 是一个宏,定义如下:

#define i2c_add_driver(driver) \\
	i2c_register_driver(THIS_MODULE, driver)

i2c_add_driver 就是对i2c_register_driver 做了一个简单的封装,只有一个参数,就是要注册的i2c_driver。

注销I2C 设备驱动的时候需要将前面注册的i2c_driver 从Linux 内核中注销掉,需要用到i2c_del_driver 函数,此函数原型如下:

void i2c_del_driver(struct i2c_driver *driver)

函数参数和返回值含义如下:

driver:要注销的i2c_driver。
返回值:无。

i2c_driver 的注册示例代码如下:

1 /* i2c 驱动的probe 函数 */
2 static int xxx_probe(struct i2c_client *client, const struct i2c_device_id *id)
3 {
4 /* 函数具体程序 */
5 return 0;
6 }
7
8 /* i2c 驱动的remove 函数 */
9 static int ap3216c_remove(struct i2c_client *client)
10 {
11 /* 函数具体程序 */
12 return 0;
13 }
14
15 /* 传统匹配方式ID 列表 */
16 static const struct i2c_device_id xxx_id[] = {
17 {"xxx", 0},
18 {}
19 };
20
21 /* 设备树匹配列表 */
22 static const struct of_device_id xxx_of_match[] = {
23 { .compatible = "xxx" },
24 { /* Sentinel */ }
25 };
26
27 /* i2c 驱动结构体 */
28 static struct i2c_driver xxx_driver = {
29 .probe = xxx_probe,
30 .remove = xxx_remove,
31 .driver = {
32 .owner = THIS_MODULE,
33 .name = "xxx",
34 .of_match_table = xxx_of_match,
35 },
36 .id_table = xxx_id,
37 };
38
39 /* 驱动入口函数 */
40 static int __init xxx_init(void)
41 {
42 int ret = 0;
43
44 ret = i2c_add_driver(&xxx_driver);
45 return ret;
46 }
47
48 /* 驱动出口函数 */
49 static void __exit xxx_exit(void)
50 {
51 i2c_del_driver(&xxx_driver);
52 }
53
54 module_init(xxx_init);
55 module_exit(xxx_exit);

第16~19 行,i2c_device_id,无设备树的时候匹配ID 表。
第22~25 行,of_device_id,设备树所使用的匹配表。
第28~37 行,i2c_driver,当I2C 设备和I2C 驱动匹配成功以后probe 函数就会执行,这些和platform 驱动一样,probe 函数里面基本就是标准的字符设备驱动那一套了。

I2C 设备和驱动匹配过程

I2C 设备和驱动的匹配过程是由I2C 核心来完成的,drivers/i2c/i2c-core.c 就是I2C 的核心部分,I2C 核心提供了一些与具体硬件无关的API 函数,比如前面讲过的:

1、i2c_adapter 注册/注销函数

int i2c_add_adapter(struct i2c_adapter *adapter)
int i2c_add_numbered_adapter(struct i2c_adapter *adap)
void i2c_del_adapter(struct i2c_adapter * adap)

2、i2c_driver 注册/注销函数

int i2c_register_driver(struct module *owner, struct i2c_driver *driver)
int i2c_add_driver (struct i2c_driver *driver)
void i2c_del_driver(struct i2c_driver *driver)

设备和驱动的匹配过程也是由I2C 总线完成的,I2C 总线的数据结构为i2c_bus_type,定义在drivers/i2c/i2c-core.c 文件,i2c_bus_type 内容如下:

struct bus_type i2c_bus_type = {
.name = "i2c",
.match = i2c_device_match,
.probe = i2c_device_probe,
.remove = i2c_device_remove,
.shutdown = i2c_device_shutdown,
};

.match 就是I2C 总线的设备和驱动匹配函数,在这里就是i2c_device_match 这个函数,此函数内容如下:

static int i2c_device_match(struct device *dev, struct device_driver *drv)
 {
 struct i2c_client *client = i2c_verify_client(dev);
 struct i2c_driver *driver;

 if (!client)
 return 0;

 /* Attempt an OF style match */
 if (of_driver_match_device(dev, drv))
 return 1;

 /* Then ACPI style match */
 if (acpi_driver_match_device(dev, drv))
 return 1;

 driver = to_i2c_driver(drv);
 /* match on an id table if there is one */
 if (driver->id_table)
 return i2c_match_id(driver->id_table, client) != NULL;

 return 0;
 }

of_driver_match_device 函数用于完成设备树设备和驱动匹配。比较I2C 设备节点的compatible 属性和of_device_id 中的compatible 属性是否相等,如果相当的话就表示I2C设备和驱动匹配。

acpi_driver_match_device 函数用于ACPI 形式的匹配。

i2c_match_id 函数用于传统的、无设备树的I2C 设备和驱动匹配过程。比较I2C设备名字和i2c_device_idname 字段是否相等,相等的话就说明I2C 设备和驱动匹配。

I.MX6q 的I2C 适配器驱动分析

I2C 设备驱动是需要用户根据不同的I2C 设备去编写,而I2C 适配器驱动一般都是SOC 厂商去编写的,比如NXP 就编写好了I.MX6q 的I2C 适配器驱动。在imx6qdl.dtsi 文件中找到I.MX6q 的I2C1 控制器节点,节点内容如下所示:

i2c1: i2c@021a0000 {
		#address-cells = <1>;
		#size-cells = <0>;
		compatible = "fsl,imx6q-i2c", "fsl,imx21-i2c";
		reg = <0x021a0000 0x4000>;
		interrupts = <0 36 IRQ_TYPE_LEVEL_HIGH>;
		clocks = <&clks IMX6QDL_CLK_I2C1>;
		status = "disabled";
	};

重点关注i2c1 节点的compatible 属性值,因为通过compatible 属性值可以在Linux 源码里面找到对应的驱动文件。

这里i2c1 节点的compatible 属性值有两个:“fsl,imx6q-i2c”, “fsl,imx21-i2c”,在Linux 源码中搜索这两个字符串即可找到对应的驱动文件。

grep -rn "fsl,imx6q-i2c" *

grep -rn "fsl,imx21-i2c" *

或者

grep -rn "fsl,imx21-i2c"&"fsl,imx6q-i2c" *
 
* : 表示当前目录所有文件,也可以是某个文件名

-r 是递归查找

-n 是显示行号

-R 查找所有文件包含子目录

-i 忽略大小写


从搜索结果中可以看出

I.MX6q 的I2C 适配器驱动驱动文件为drivers/i2c/busses/i2c-imx.c,在此文件中有如下内容:

可以看出,I.MX6的I2C 适配器驱动是个标准的platform 驱动,由此可以看出,虽然I2C 总线为别的设备提供了一种总线驱动框架,但是I2C 适配器却是platform驱动

“fsl,imx21-i2c”属性值,设备树中 i2c1 节点的compatible 属性值就是与此匹配上的。因此i2c-imx.c 文件就是I.MX6 的I2C 适配器驱动文件。

当设备和驱动匹配成功以后i2c_imx_probe 函数就会执行,i2c_imx_probe 函数就会完成I2C 适配器初始化工作。

i2c_imx_probe 函数内容如下所示(有省略):

示例代码61.2.3 i2c_imx_probe 函数代码段
971 static int i2c_imx_probe(struct platform_device *pdev)
972 {
973 const struct of_device_id *of_id =
974 of_match_device(i2c_imx_dt_ids, &pdev->dev);
975 struct imx_i2c_struct *i2c_imx;
976 struct resource *res;
977 struct imxi2c_platform_data *pdata =
dev_get_platdata(&pdev->dev);
978 void __iomem *base;
979 int irq, ret;
980 dma_addr_t phy_addr;
981
982 dev_dbg(&pdev->dev, "<%s>\\n", __func__);
983
984 irq = platform_get_irq(pdev, 0);
......
990 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
991 base = devm_ioremap_resource(&pdev->dev, res);
992 if (IS_ERR(base))
993 return PTR_ERR(base);
994
995 phy_addr = (dma_addr_t)res->start;
996 i2c_imx = devm_kzalloc(&pdev->dev, sizeof(*i2c_imx),
GFP_KERNEL);
997 if (!i2c_imx)
998 return -ENOMEM;
999
1000 if (of_id)
1001 i2c_imx->hwdata = of_id->data;
1002 else
1003 i2c_imx->hwdata = (struct imx_i2c_hwdata *)
1004 platform_get_device_id(pdev)->driver_data;
1005
1006 /* Setup i2c_imx driver structure */
1007 strlcpy(i2c_imx->adapter.name, pdev->name,
sizeof(i2c_imx->adapter.name));
1008 i2c_imx->adapter.owner = THIS_MODULE;
1009 i2c_imx->adapter.algo = &i2c_imx_algo;
1010 i2c_imx->adapter.dev.parent = &pdev->dev;
1011 i2c_imx->adapter.nr = pdev->id;
1012 i2c_imx->adapter.dev.of_node = pdev->dev.of_node;
1013 i2c_imx->base = base;
1014
1015 /* Get I2C clock */
1016 i2c_imx->clk = devm_clk_get(&pdev->dev, NULL);
......
1022 ret = clk_prepare_enable(i2c_imx->clk);
......
1027 /* Request IRQ */
1028 ret = devm_request_irq(&pdev->dev, irq, i2c_imx_isr,
1029 IRQF_NO_SUSPEND, pdev->name, i2c_imx);
......
1035 /* Init queue */
1036 init_waitqueue_head(&i2c_imx->queue);
1037
1038 /* Set up adapter data */
1039 i2c_set_adapdata(&i2c_imx->adapter, i2c_imx);
1040
1041 /* Set up clock divider */
1042 i2c_imx->bitrate = IMX_I2C_BIT_RATE;
1043 ret = of_property_read_u32(pdev->dev.of_node,
1044 "clock-frequency", &i2c_imx->bitrate);
1045 if (ret < 0 && pdata && pdata->bitrate)
1046 i2c_imx->bitrate = pdata->bitrate;
1047
1048 /* Set up chip registers to defaults */
1049 imx_i2c_write_reg(i2c_imx->hwdata->i2cr_ien_opcode ^ I2CR_IEN,
1050 i2c_imx, IMX_I2C_I2CR);
1051 imx_i2c_write_reg(i2c_imx->hwdata->i2sr_clr_opcode, i2c_imx,
IMX_I2C_I2SR);
1052
1053 /* Add I2C adapter */
1054 ret = i2c_add_numbered_adapter(&i2c_imx->adapter);
1055 if (ret < 0) {
1056 dev_err(&pdev->dev, "registration failed\\n");
1057 goto clk_disable;
1058 }
1059
1060 /* Set up platform driver data */
1061 platform_set_drvdata(pdev, i2c_imx);
1062 clk_disable_unprepare(i2c_imx->clk);
......
1070 /* Init DMA config if supported */
1071 i2c_imx_dma_request(i2c_imx, phy_addr);
1072
1073 return 0; /* Return OK */
1074
1075 clk_disable:
1076 clk_disable_unprepare(i2c_imx->clk);
1077 return ret;
1078 }

第984 行,调用platform_get_irq 函数获取中断号

第990~991 行,调用platform_get_resource 函数从设备树中获取I2C1 控制器寄存器物理基地址,也就是0X021A0000。获取到寄存器基地址以后使用devm_ioremap_resource 函数对其进行内存映射,得到可以在Linux 内核中使用的虚拟地址。

第996 行,NXP 使用imx_i2c_struct 结构体来表示I.MX 系列SOC 的I2C 控制器,这里使用devm_kzalloc 函数来申请内存。

第1008~1013 行,imx_i2c_struct 结构体要有个叫做adapter 的成员变量,adapter 就是i2c_adapter,这里初始化i2c_adapter。第1009 行设置i2c_adapter 的algo 成员变量为i2c_imx_algo,也就是设置i2c_algorithm。

第1028~1029 行,注册I2C 控制器中断,中断服务函数为i2c_imx_isr

第1042~1044 行,设置I2C 频率默认为IMX_I2C_BIT_RATE=100KHz,如果设备树节点设置了“clock-frequency”属性的话I2C 频率就使用clock-frequency 属性值。

第1049~1051 行,设置I2C1 控制的I2CRI2SR 寄存器。

第1054 行,调用i2c_add_numbered_adapter 函数向Linux 内核注册i2c_adapter。

第1071 行,申请DMA,看来I.MX 的I2C 适配器驱动采用了DMA 方式。
i2c_imx_probe 函数主要的工作就是一下两点:
①、初始化i2c_adapter,设置i2c_algorithmi2c_imx_algo,最后向Linux 内核注册i2c_adapter。
②、初始化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,functionality用于返回此I2C适配器支持什么样的通信协议,在这里functionality 就是i2c_imx_func 函数,i2c_imx_func 函数内容如下:

static u32 i2c_imx_func(struct i2c_adapter *adapter)
{
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL
| I2C_FUNC_SMBUS_READ_BLOCK_DATA;
}

重点来看一下i2c_imx_xfer 函数,因为最终就是通过此函数来完成与I2C 设备通信的,此
函数内容如下(有省略):

888 static int i2c_imx_xfer(struct i2c_adapter *adapter,
889 struct i2c_msg *msgs, int num)
890 {
891 unsigned int i, temp;
892 int result;
893 bool is_lastmsg = false;
894 struct imx_i2c_struct *i2c_imx = i2c_get_adapdata(adapter);
895
896 dev_dbg(&i2c_imx->adapter.dev, "<%s>\\n", __func__);
897
898 /* Start I2C transfer */
899 result = i2c_imx_start(i2c_imx);
900 if (result)
901 goto fail0;
902
903 /* read/write data */
904 for (i = 0; i < num; i++) {
905 if (i == num - 1)
906 is_lastmsg = true;
907
908 if (i) {
909 dev_dbg(&i2c_imx->adapter.dev,
910 "<%s> repeated start\\n", __func__);
911 temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR);
912 temp |= I2CR_RSTA;
913 imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR);
914 result = i2c_imx_bus_busy(i2c_imx, 1);
915 if (result)
916 goto fail0;
917 }
918 dev_dbg(&i2c_imx->adapter.dev,
919 "<%s> transfer message: %d\\n", __func__, i);
920 /* write/read data */
......
938 if (msgs[i].flags & I2C_M_RD)
939 result = i2c_imx_read(i2c_imx, &msgs[i], is_lastmsg);
940 else {
941 if (i2c_imx->dma && msgs[i].len >= DMA_THRESHOLD)
Linux驱动开发I2C

Linux驱动开发I2C

Linux驱动之I2C驱动架构

linux驱动之i2c学习

Linux I2C 驱动实验

Smart210学习记录-----Linux i2c驱动