55 linux内核里基于GPIO口的I2C控制器驱动

时间:2021-01-27 17:56:23

当SOC里的I2C控制器不稳定,或I2C控制器不够用时,我们可以基于GPIO实现I2C控制器的功能.

在linux内核里已提供了相应的代码,是一个平台驱动,只需写平台设备描述相关资源即可.

    make menuconfig ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf-
Device Drivers --->
<*> I2C support --->
<*> I2C device interface
I2C Hardware Bus support --->
<*> GPIO-based bitbanging I2C //基于GPIO实现的I2C控制器驱动

驱动源码在”drivers/i2c/busses/i2c-gpio.c”里

static struct platform_driver i2c_gpio_driver = {
.driver = {
.name = "i2c-gpio", //匹配名为"i2c-gpio"的平台设备
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(i2c_gpio_dt_ids),
},
.probe = i2c_gpio_probe,
.remove = __devexit_p(i2c_gpio_remove),
};

//通过阅读平台驱动里的probe函数可以得知平台设备需要提供的参数
static int __devinit 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;
int ret;

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) { //此内核没用到设备树,这里不成立
ret = of_i2c_gpio_probe(pdev->dev.of_node, pdata);
if (ret)
return ret;
} else {
if (!pdev->dev.platform_data) //可以通过平台设备的platform_data成员提供struct i2c_gpio_platform_data类型的数据
return -ENXIO;
memcpy(pdata, pdev->dev.platform_data, sizeof(*pdata));
}
....

if (pdata->udelay)
bit_data->udelay = pdata->udelay;
else if (pdata->scl_is_output_only)
bit_data->udelay = 50; /* 10 kHz */
else
bit_data->udelay = 5; /* 100 kHz */

if (pdata->timeout)
bit_data->timeout = pdata->timeout;
else
bit_data->timeout = HZ / 10; /* 100 ms */
...
adap->nr = (pdev->id != -1) ? pdev->id : 0; //i2c_adapter对象的号使用平台设备的id
ret = i2c_bit_add_numbered_bus(adap);




struct i2c_gpio_platform_data {
unsigned int sda_pin; // SDA脚的IO口
unsigned int scl_pin; // SCL脚的IO口
int udelay; //通过此成员确定SCL的时钟频率, SCL frequency is (500 / udelay) kHz
int timeout; //多久没收到应答后认为传输失败
unsigned int sda_is_open_drain:1; //是否有上拉, 没有则设1 
unsigned int scl_is_open_drain:1;

unsigned int scl_is_output_only:1; //时钟信号是否只由主机提供
};

/////////////////////////////////////////////////

把PA9(SDA), PA10(SCL)这两个IO口实现I2C控制器的接口

myi2c9.c

#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/i2c-gpio.h>
#include <mach/gpio.h>

struct i2c_gpio_platform_data pdata = {
.sda_pin = GPIOA(9),
.scl_pin = GPIOA(10),
.udelay = 2, // 250Khz
.timeout = 0, //使用默认值,此内核里是100ms
};

struct platform_device mypdev = {
.name = "i2c-gpio",
.id = 9, // i2c_adapter对象的号就是9
.dev = {
.platform_data = &pdata,
},
};

module_driver(mypdev, platform_device_register, platform_device_unregister);
MODULE_LICENSE("GPL");

加模块后,应在”/dev”目录下有i2c-9设备文件
在”/sys/bus/i2c/devices”目录下有i2c-9子目录