您的位置:首页 > 运维架构 > Linux

我的内核学习笔记12:linux i2c-gpio驱动应用实例

2016-12-09 21:15 627 查看
linux内核的i2c-gpio是使用GPIO模拟I2C协议的驱动,只需要配置2根GPIO即可使用。Linux的I2C子系统比较复杂,笔者暂时还没有研究。本着“实用”的目的,介绍一下如何使用这个驱动及一些注意事项。

一、概述

Linux内核很多驱动都使用到I2C子系统。如EEPROM、RTC等。

GPIO模拟I2C协议的驱动位于drivers/i2c/busses目录。驱动名称为“i2c-gpio”,驱动文件为drivers/i2c/busses/i2c-gpio.c。

二、内核配置

本文基于linux 3.17.1版本内核进行分析。

内核配置(make menuconfig)信息如下:

Device Drivers->
    I2C support  --->
        I2C Hardware Bus support  --->
            <*> GPIO-based bitbanging I2C


从配置中看到将驱动整合到内核中,而不是module形式。这样能保证在其它I2C板级信息注册之前,已经存在了i2c总线。另外,还需要GPIO库支持:

[*] GPIO Support  --->  

否则无法不会出现选项“GPIO-based bitbanging I2C”。

三、设备注册及使用

3.1 I2C相关结构体

本文不是深入I2C子系统的,所以抛开原理方面的描述。看一下i2c平台数据结构i2c_gpio_platform_data的声明:

/**
* struct i2c_gpio_platform_data - Platform-dependent data for i2c-gpio
* @sda_pin: GPIO pin ID to use for SDA
* @scl_pin: GPIO pin ID to use for SCL
* @udelay: signal toggle delay. SCL frequency is (500 / udelay) kHz
* @timeout: clock stretching timeout in jiffies. If the slave keeps
*    SCL low for longer than this, the transfer will time out.
* @sda_is_open_drain: SDA is configured as open drain, i.e. the pin
*    isn't actively driven high when setting the output value high.
*    gpio_get_value() must return the actual pin state even if the
*    pin is configured as an output.
* @scl_is_open_drain: SCL is set up as open drain. Same requirements
*    as for sda_is_open_drain apply.
* @scl_is_output_only: SCL output drivers cannot be turned off.
*/
struct i2c_gpio_platform_data {
unsigned int    sda_pin;
unsigned int    scl_pin;
int        udelay;
int        timeout;
unsigned int    sda_is_open_drain:1;
unsigned int    scl_is_open_drain:1;
unsigned int    scl_is_output_only:1;
};
重要的是sda_pin和scl_pin,分别表示I2C的SDA、SCL信号引脚。udelay可控制SCL频率,计算公式为:500/udelay kHZ。timeout为超时时间,单位为jiffies。sda_is_open_drain和scl_is_open_drain分别表示SDA和SCL是否为开漏电路,对此方面研究不深,不再描述。
一个实例如下:

static struct i2c_gpio_platform_data i2c_gpio_data = {
.sda_pin = 68,
.scl_pin = 88,
.timeout = 100,
.udelay  = 2,
};
例子中使用的引脚分别为68和88,是由主板硬件确定的。

3.2 平台设备

i2c-gpio驱动定义入口代码如下(drivers/i2c/busses/i2c-gpio.c):

static struct platform_driver i2c_gpio_driver = {
.driver        = {
.name    = "i2c-gpio",
.owner    = THIS_MODULE,
.of_match_table    = of_match_ptr(i2c_gpio_dt_ids),
},
.probe        = i2c_gpio_probe,
.remove        = i2c_gpio_remove,
};

static int __init i2c_gpio_init(void)
{
int ret;

ret = platform_driver_register(&i2c_gpio_driver);
if (ret)
printk(KERN_ERR "i2c-gpio: probe failed: %d\n", ret);

return ret;
}
subsys_initcall(i2c_gpio_init);

从代码分析知,这里将GPIO模拟I2C总线当作平台设备处理。而从i2c_gpio_driver结构体中可以看到驱动名称为i2c-gpio。因此要使用这个驱动,必须另外定义一个platform设备,并调用函数platform_device_register注册。本文实例如下

static struct platform_device i2c_gpio_device = {
.name       = "i2c-gpio",
.id         = 0, // first bus for "i2c-gpio", so --> 0
.dev        = {
.platform_data = &i2c_gpio_data,
.release = platformdev_release,
},
};


其中name表示设备名称,这里必须为“leds-gpio”,platform_data即为前面定义的i2c_gpio_data。id表示i2c-gpio的第几条I2C总线。驱动正常工作后,将生成/sys/bus/platform/devices/i2c-gpio.id目录,里面有挂载在此总线上的设备地址。
最后,注册设备——建议在板子的GPIO正常工作之后再进行注册。

platform_device_register(&i2c_gpio_device);


四、I2C板级信息注册

i2c设备的板级信息由i2c_board_info结构体描述,其声明如下:

struct i2c_board_info {
char        type[I2C_NAME_SIZE];
unsigned short    flags;
unsigned short    addr;
void        *platform_data;
struct dev_archdata    *archdata;
struct device_node *of_node;
struct acpi_dev_node acpi_node;
int        irq;
};
该结构体包括了I2C设备名称、标志、地址等等信息。
比如主板上有2个I2C设备:LM75和EEPROM,地址分别为0x48、0x50——地址根据datasheet说明及硬件接线来确定。下面定义板级信息:

static struct at24_platform_data at24_eeprom = {
.byte_len        = 2 * 1024 / 8,
.page_size        = 16,
.flags            = 0,
};

static struct i2c_board_info my_i2c_boardinfo[] = {
{
I2C_BOARD_INFO("lm75", 0x48),
},
{
I2C_BOARD_INFO("24c02", 0x50), // 24c02 == at24 driver
.platform_data    = &at24_eeprom,
},
};


2C_BOARD_INFO宏定义如下:
#define I2C_BOARD_INFO(dev_type, dev_addr) \
.type = dev_type, .addr = (dev_addr)


常见注册方法有分静态注册i2c_register_board_info和动态注册i2c_new_device。大部分ARM框架都使用静态注册,使用很简单,如下:

i2c_register_board_info(0, i2c_info, ARRAY_SIZE(i2c_info));


不过如果以modules形式编译,则会提示i2c_register_board_info未定义:

WARNING: "i2c_register_board_info" [drivers/gpio/gpio-misc.ko] undefined!

这里使用第二种动态注册的方式。先调用i2c_get_adapter获取适配器(参数为i2c总线,根据前面i2c_gpio_device定义的id确定),然后调用i2c_new_device添加到该适配器中。代码片段如下:

struct i2c_adapter* adap = NULL;
struct i2c_client* client = NULL;

adap = i2c_get_adapter(i2c_gpio_device.id);
if (adap)
{
for (i = 0; i < ARRAY_SIZE(my_i2c_boardinfo); i++)
{
client = i2c_new_device(adap, &my_i2c_boardinfo[i]);
pr_info("Add %s to adapter %s %s.\n", my_i2c_boardinfo[i].type,
adap->name, client?"ok":"failed");
}
}
else
{
pr_info("i2c bus %d found no adapter...\n", i2c_gpio_device.id);
}


五、用户空间

驱动正常工作后,会生成/sys/bus/platform/devices/i2c-gpio.0目录(以本文为例)。里面存放着对应驱动映射的文件。比如/sys/bus/platform/devices/i2c-gpio.0/i2c-0/0-0050/eeprom即为eeprom内容。该文件属性为可读写,因此修改此文件内容,即为修改EEPROM内容(此方法有一定危险性,就不直接写出来了)。
而查看主板温度,则可以用如下命令:

cat /sys/bus/platform/devices/i2c-gpio.0/i2c-0/0-0048/hwmon/hwmon0/temp1_input

注:目录出现的0-0050表示该I2C设备在第0条总线上的0x50地址。

六、总结

使用GPIO模拟I2C驱动前,最好保证系统的GPIO已能正常工作。

在驱动中可以注册多条i2c-gpio总线,驱动名称均为i2c-gpio,但根据id值来区别不的同总线。比如主板上有3条GPIO模拟总线,则可分别命名为0、1、2。

参考资源:

1、内核源码官网:https://www.kernel.org

2、内核源码查询:http://lxr.free-electrons.com/source/?v=3.17

李迟 2016.12.9 周五 晚
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: