我的内核学习笔记12:linux i2c-gpio驱动应用实例
Posted 李迟
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了我的内核学习笔记12:linux i2c-gpio驱动应用实例相关的知识,希望对你有一定的参考价值。
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!
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 周五 晚
以上是关于我的内核学习笔记12:linux i2c-gpio驱动应用实例的主要内容,如果未能解决你的问题,请参考以下文章
20179219 《Linux内核原理与分析》第一周学习笔记