内核自带模拟I2C驱动框架
参考资料:
Linux文档
Linux-5.4\Documentation\devicetree\bindings\i2c\i2c-gpio.yamlLinux-4.9.88\Documentation\devicetree\bindings\i2c\i2c-gpio.txt
Linux驱动源码
Linux-5.4\drivers\i2c\busses\i2c-gpio.cLinux-4.9.88\drivers\i2c\busses\i2c-gpio.c
驱动程序分析
内核自带的模拟i2c驱动也是使用平台总线驱动模型:

从上图可知设备树为我们提供平台设备,当平台驱动在insmod的时候,如果匹配到设备树里面的compatible =i2c-gpio时候,就会调用平台驱动的i2c_gpio_probe函数。
设备树
I2C-GPIO层次

怎么使用I2C-GPIO
参见工程【19.i2c/ap3216_5】,工程目录如下:

使用说明:
配置内核,使能内核自带的模拟i2c驱动程序,这里选择编译出模块,如下所示,保存并退出

切换到【19.i2c/ap3216_5】目录,执行
make,会自动到内核目录下执行make modules编译过程如下:

注意:我们使能的内核自带的模拟i2c的模块位置如下:
drivers/i2c/busses/i2c-gpio.ko测试:这里使用i2c-tools来进行测试模拟的i2c驱动
[root@100ask:~]# i2cdetect -l // 加载i2c-gpio.ko前只看到2条I2C BUS i2c-1 i2c 21a4000.i2c I2C adapter i2c-0 i2c 21a0000.i2c I2C adapter [root@100ask:~]# [root@100ask:~]# insmod i2c-gpio.ko [ 45.067602] i2c-gpio i2c_gpio_100ask: using pins 116 (SDA) and 117 (SCL) [root@100ask:~]# i2cdetect -l // 加载i2c-gpio.ko后看到3条I2C BUS i2c-1 i2c 21a4000.i2c I2C adapter i2c-4 i2c i2c_gpio_100ask I2C adapter i2c-0 i2c 21a0000.i2c I2C adapter [root@100ask:~]# [root@100ask:~]# i2cdetect -y 4 // 检测到0x50的设备 0 1 2 3 4 5 6 7 8 9 a b c d e f 00: -- -- -- -- -- -- -- -- -- -- -- -- -- 10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 50: 50 -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 70: -- -- -- -- -- -- -- -- [root@100ask:~]# [root@100ask:~]# i2cset -f -y 4 0x50 0 0x55 // 往0地址写入0x55 [root@100ask:~]# i2cget -f -y 4 0x50 0 // 读0地址 0x55
源码分析
【19.i2c/ap3216_5/Makefile】文件

设备树:【19.i2c/ap3216_5/imx6ull-14x14-evk.dts】文件跟根节点下添加下面的信息:
i2c_gpio_xym { compatible = "i2c-gpio"; gpios = <&gpio5 20 0 /* sda */ &gpio4 21 0 /* scl */ >; i2c-gpio,delay-us = <5>; /* ~100 kHz */ #address-cells = <1>; #size-cells = <0>; status="okey"; };
当我们执行
insmod i2c-gpio.ko的时候会自动和设备树的compatible = "i2c-gpio"属性进行匹配,如果匹配成功,就会调用i2c-gpio.c下的probe函数,在probe函数中又会出现i2c适配器注册的那一套分配适配器
struct i2c_gpio_private_data { struct i2c_adapter adap; struct i2c_algo_bit_data bit_data; struct i2c_gpio_platform_data pdata; }; struct i2c_gpio_private_data *priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
设置适配器
adap->algo_data = bit_data;// 算法结构体,很重要,注意:此时bit_data还没有具体的内容, adap->class = I2C_CLASS_HWMON | I2C_CLASS_SPD; adap->dev.parent = &pdev->dev; adap->dev.of_node = pdev->dev.of_node; adap->nr = pdev->id;
注意:
bit_data和重要下面会分析。注册适配器
i2c_bit_add_numbered_bus(adap);// 在该函数里面才会真正的实现adap->algo_data的注册,后面分析
整个probe函数如下:
static int i2c_gpio_probe(struct platform_device *pdev) { struct i2c_gpio_private_data *priv; struct i2c_gpio_platform_data *pdata; struct i2c_algo_bit_data *bit_data; struct i2c_adapter *adap; unsigned int sda_pin, scl_pin; int ret; /* * 1.下面这一段是从设备树拿到 sda和scl的gpio引脚,并初始化gpio功能 */ /* First get the GPIO pins; if it fails, we'll defer the probe. */ if (pdev->dev.of_node) { ret = of_i2c_gpio_get_pins(pdev->dev.of_node, &sda_pin, &scl_pin); if (ret) return ret; } else { if (!dev_get_platdata(&pdev->dev)) return -ENXIO; pdata = dev_get_platdata(&pdev->dev); sda_pin = pdata->sda_pin; scl_pin = pdata->scl_pin; } ret = devm_gpio_request(&pdev->dev, sda_pin, "sda"); if (ret) { if (ret == -EINVAL) ret = -EPROBE_DEFER; /* Try again later */ return ret; } ret = devm_gpio_request(&pdev->dev, scl_pin, "scl"); if (ret) { if (ret == -EINVAL) ret = -EPROBE_DEFER; /* Try again later */ return ret; } /* * 2.分配适配器 */ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; adap = &priv->adap; bit_data = &priv->bit_data; pdata = &priv->pdata; if (pdev->dev.of_node) { pdata->sda_pin = sda_pin; pdata->scl_pin = scl_pin; of_i2c_gpio_get_props(pdev->dev.of_node, pdata); } else { memcpy(pdata, dev_get_platdata(&pdev->dev), sizeof(*pdata)); } if (pdata->sda_is_open_drain) { gpio_direction_output(pdata->sda_pin, 1); bit_data->setsda = i2c_gpio_setsda_val; } else { gpio_direction_input(pdata->sda_pin); bit_data->setsda = i2c_gpio_setsda_dir; } if (pdata->scl_is_open_drain || pdata->scl_is_output_only) { gpio_direction_output(pdata->scl_pin, 1); bit_data->setscl = i2c_gpio_setscl_val; } else { gpio_direction_input(pdata->scl_pin); bit_data->setscl = i2c_gpio_setscl_dir; } if (!pdata->scl_is_output_only) bit_data->getscl = i2c_gpio_getscl; bit_data->getsda = i2c_gpio_getsda; /* * 3.计算i2c的时钟频率 */ if (pdata->udelay) bit_data->udelay = pdata->udelay; else if (pdata->scl_is_output_only) bit_data->udelay = 50; /* 10 kHz */ // 这里是计算i2c的时钟频率 else bit_data->udelay = 5; /* 100 kHz */ if (pdata->timeout) bit_data->timeout = pdata->timeout; else bit_data->timeout = HZ / 10; /* 100 ms */ bit_data->data = pdata; /* * 4.设置适配器 */ adap->owner = THIS_MODULE; if (pdev->dev.of_node) strlcpy(adap->name, dev_name(&pdev->dev), sizeof(adap->name)); else snprintf(adap->name, sizeof(adap->name), "i2c-gpio%d", pdev->id); adap->algo_data = bit_data;// 算法结构体,注意:此时bit_data还没有具体的内容, adap->class = I2C_CLASS_HWMON | I2C_CLASS_SPD; adap->dev.parent = &pdev->dev; adap->dev.of_node = pdev->dev.of_node; adap->nr = pdev->id; /* * 5.注册适配器到内核里面 */ ret = i2c_bit_add_numbered_bus(adap);// 注意:这里才会真正实现算法结构体的赋值 if (ret) return ret; platform_set_drvdata(pdev, priv); dev_info(&pdev->dev, "using pins %u (SDA) and %u (SCL%s)\n", pdata->sda_pin, pdata->scl_pin, pdata->scl_is_output_only ? ", no clock stretching" : ""); return 0; }
重点分下下算法结构体bit_data
上面介绍到,bit_data还没有具体的内容,会在probe调用
i2c_bit_add_numbered_bus函数里面实现具体的赋值,参见【drivers/i2c/algos/i2c-algo-bit.c】文件如下:
所以下面就重点分析下bit_xfer函数即可。

重点分析
bit_xfer里面如下:
红色框中的函数就会根据前面的gpio拉i2c的时序,如下所示:

