I2C驱动分为I2C适配器驱动和I2C设备驱动,I2C适配器驱动芯片厂商已经帮我们实现好了,I2C设备驱动需要用户自己编写。下面我们主要就这两个方面进行分析。另外这里举例是用NXP的imx6ull芯片。
I2C适配器驱动
linux启动最先运行的I2C驱动相关函数是i2c_init(),这个函数是linux内核实现的,而不是厂商实现的,位于drivers\i2c\i2c-core.c文件第1879行:
static int __init i2c_init(void)
{
int retval;
retval = of_alias_get_highest_id("i2c");
down_write(&__i2c_board_lock);
if (retval >= __i2c_first_dynamic_bus_num)
__i2c_first_dynamic_bus_num = retval + 1;
up_write(&__i2c_board_lock);
retval = bus_register(&i2c_bus_type);
if (retval)
return retval;
#ifdef CONFIG_I2C_COMPAT
i2c_adapter_compat_class = class_compat_register("i2c-adapter");
if (!i2c_adapter_compat_class) {
retval = -ENOMEM;
goto bus_err;
}
#endif
retval = i2c_add_driver(&dummy_driver);
if (retval)
goto class_err;
if (IS_ENABLED(CONFIG_OF_DYNAMIC))
WARN_ON(of_reconfig_notifier_register(&i2c_of_notifier));
return 0;
class_err:
#ifdef CONFIG_I2C_COMPAT
class_compat_unregister(i2c_adapter_compat_class);
bus_err:
#endif
bus_unregister(&i2c_bus_type);
return retval;
}
postcore_initcall(i2c_init);
主要的作用是注册I2C总线,注册总线之后其他驱动程序才能向I2C总线注册驱动或者设备:
bus_register(&i2c_bus_type);
所以需要最早执行。
最后的postcore_initcall(i2c_init);和module_init宏定义作用类似,只不过放在上电执行优先级更高的内存区域。
接下来执行的是i2c_adap_imx_init(void),位于drivers/i2c/busses/i2c-imx.c文件中,第1131行,这个函数是芯片厂商实现的,如下:
static int __init i2c_adap_imx_init(void)
{
return platform_driver_register(&i2c_imx_driver);
}
subsys_initcall(i2c_adap_imx_init);
可以看到这是一个platform设备驱动,作用主要是添加I2C适配器,所以,I2C的总线初始化也是要借助platform。最后的subsys_initcall(i2c_adap_imx_init);也类似与module_init宏,执行优先级晚于postcore_initcall,早于module_init。
下面看一下这个platform设备的probe函数:
static int i2c_imx_probe(struct platform_device *pdev)
{
const struct of_device_id *of_id = of_match_device(i2c_imx_dt_ids,
&pdev->dev);
struct imx_i2c_struct *i2c_imx;
struct resource *res;
struct imxi2c_platform_data *pdata = dev_get_platdata(&pdev->dev);
void __iomem *base;
int irq, ret;
dma_addr_t phy_addr;
dev_dbg(&pdev->dev, "<%s>\n", __func__);
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
dev_err(&pdev->dev, "can't get irq number\n");
return irq;
}
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(base))
return PTR_ERR(base);
phy_addr = (dma_addr_t)res->start;
i2c_imx = devm_kzalloc(&pdev->dev, sizeof(*i2c_imx), GFP_KERNEL);
if (!i2c_imx)
return -ENOMEM;
if (of_id)
i2c_imx->hwdata = of_id->data;
else
i2c_imx->hwdata = (struct imx_i2c_hwdata *)
platform_get_device_id(pdev)->driver_data;
/* Setup i2c_imx driver structure */
strlcpy(i2c_imx->adapter.name, pdev->name, sizeof(i2c_imx->adapter.name));
i2c_imx->adapter.owner = THIS_MODULE;
i2c_imx->adapter.algo = &i2c_imx_algo;
i2c_imx->adapter.dev.parent = &pdev->dev;
i2c_imx->adapter.nr = pdev->id;
i2c_imx->adapter.dev.of_node = pdev->dev.of_node;
i2c_imx->base = base;
/* Get I2C clock */
i2c_imx->clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(i2c_imx->clk)) {
dev_err(&pdev->dev, "can't get I2C clock\n");
return PTR_ERR(i2c_imx->clk);
}
ret = clk_prepare_enable(i2c_imx->clk);
if (ret) {
dev_err(&pdev->dev, "can't enable I2C clock\n");
return ret;
}
/* Request IRQ */
ret = devm_request_irq(&pdev->dev, irq, i2c_imx_isr,
IRQF_NO_SUSPEND, pdev->name, i2c_imx);
if (ret) {
dev_err(&pdev->dev, "can't claim irq %d\n", irq);
goto clk_disable;
}
/* Init queue */
init_waitqueue_head(&i2c_imx->queue);
/* Set up adapter data */
i2c_set_adapdata(&i2c_imx->adapter, i2c_imx);
/* Set up clock divider */
i2c_imx->bitrate = IMX_I2C_BIT_RATE;
ret = of_property_read_u32(pdev->dev.of_node,
"clock-frequency", &i2c_imx->bitrate);
if (ret < 0 && pdata && pdata->bitrate)
i2c_imx->bitrate = pdata->bitrate;
/* Set up chip registers to defaults */
imx_i2c_write_reg(i2c_imx->hwdata->i2cr_ien_opcode ^ I2CR_IEN,
i2c_imx, IMX_I2C_I2CR);
imx_i2c_write_reg(i2c_imx->hwdata->i2sr_clr_opcode, i2c_imx, IMX_I2C_I2SR);
/* Add I2C adapter */
ret = i2c_add_numbered_adapter(&i2c_imx->adapter);
if (ret < 0) {
dev_err(&pdev->dev, "registration failed\n");
goto clk_disable;
}
/* Set up platform driver data */
platform_set_drvdata(pdev, i2c_imx);
clk_disable_unprepare(i2c_imx->clk);
dev_dbg(&i2c_imx->adapter.dev, "claimed irq %d\n", irq);
dev_dbg(&i2c_imx->adapter.dev, "device resources: %pR\n", res);
dev_dbg(&i2c_imx->adapter.dev, "adapter name: \"%s\"\n",
i2c_imx->adapter.name);
dev_info(&i2c_imx->adapter.dev, "IMX I2C adapter registered\n");
/* Init DMA config if supported */
i2c_imx_dma_request(i2c_imx, phy_addr);
return 0; /* Return OK */
clk_disable:
clk_disable_unprepare(i2c_imx->clk);
return ret;
}
可以看到NXP定义了一个struct imx_i2c_struct 结构体指针,并为它分配空间,struct imx_i2c_struct结构体中有一个非常重要的成员变量,也是一个结构体
struct i2c_adapter adapter;
在probe函数的第37行对其进行了简单的初始化赋值,37行有一个重要的函数i2c_imx->adapter.algo = &i2c_imx_algo; 函数i2c_imx_algo就是实际直接操作寄存器完成I2C通信的函数,感兴趣可以自行阅读源码,这个函数肯定要厂商自行实现,因为不同的芯片I2C寄存器肯定是不一样的,操作规则肯定也不一样。
probe函数我们不逐行分析了,主要是根据设备树中的信息进行一些设置,我们着重看84行的一个函数i2c_add_numbered_adapter(&i2c_imx->adapter);作用是添加I2C适配器,其调用关系如下:
i2c_add_numbered_adapter->
__i2c_add_numbered_adapter->
i2c_register_adapter
i2c_register_adapter定义中我们关注一个函数device_register:
static int i2c_register_adapter(struct i2c_adapter *adap)
{
int res = 0;
...
dev_set_name(&adap->dev, "i2c-%d", adap->nr);
adap->dev.bus = &i2c_bus_type;
adap->dev.type = &i2c_adapter_type;
res = device_register(&adap->dev);
if (res)
goto out_list;
...
exit_recovery:
/* create pre-declared device nodes */
of_i2c_register_devices(adap);
...
}
可以看到struct i2c_adapter结构体中有个struct device的结构体,我们将adap->dev这个设备添加到了I2C总线上,之后我们在驱动的其他部分就可以通过类似container_of的操作来获取完整的struct i2c_adapter结构体了。
其实I2C适配器struct i2c_adapter和用户在设备树中自定义的I2C设备都会被添加到I2C总线上,但是他们的dev.type不同,所以可以作为区分。
至此I2C适配器已经添加到总线上了,其实用户可以通过设备文件直接操作I2C适配器,但大多数情况下,用户是通过在自定义的I2C设备驱动中,操作I2C适配器,完成通信。
到这里我想插一些东西,通过上述i2c_register_adapter,我们成功将I2C适配器设备添加到了I2C总线上,但是同时还有其他的一些工作,I2C适配器节点的子节点,也就是用户自定义的I2C设备,也会在此函数中添加到I2C总线上:
上述代码中会调用一个函数of_i2c_register_devices(adap), 此函数下面的调用关系如下:
of_i2c_register_devices->
of_i2c_register_device->
i2c_new_device->
device_register
在i2c_new_device函数中,还会创建struct i2c_client 结构体(此结构体用户为自定义设备编写驱动时会用),最终调用device_register函数将I2C适配器节点下的子设备注册到I2C总线下面去。
下面我们接着I2C适配器设备来说,I2C适配器的驱动部分:
首先我们先来看用户直接通过设备文件操作I2C适配器部分的驱动是如何实现的:
文件drivers/i2c/i2c-dev.c,620行函数__init i2c_dev_init(void):
static int __init i2c_dev_init(void)
{
int res;
printk(KERN_INFO "i2c /dev entries driver\n");
res = register_chrdev(I2C_MAJOR, "i2c", &i2cdev_fops);
if (res)
goto out;
i2c_dev_class = class_create(THIS_MODULE, "i2c-dev");
if (IS_ERR(i2c_dev_class)) {
res = PTR_ERR(i2c_dev_class);
goto out_unreg_chrdev;
}
i2c_dev_class->dev_groups = i2c_groups;
/* Keep track of adapters which will be added or removed later */
res = bus_register_notifier(&i2c_bus_type, &i2cdev_notifier);
if (res)
goto out_unreg_class;
/* Bind to already existing adapters right away */
i2c_for_each_dev(NULL, i2cdev_attach_adapter);
return 0;
out_unreg_class:
class_destroy(i2c_dev_class);
out_unreg_chrdev:
unregister_chrdev(I2C_MAJOR, "i2c");
out:
printk(KERN_ERR "%s: Driver Initialisation failed\n", __FILE__);
return res;
}
module_init(i2c_dev_init);
此函数为I2C适配器创建设备文件,i2c_for_each_dev(NULL, i2cdev_attach_adapter);遍历I2C总线上的所有设备,每个设备都调用i2cdev_attach_adapter()函数,定义如下:
static int i2cdev_attach_adapter(struct device *dev, void *dummy)
{
struct i2c_adapter *adap;
struct i2c_dev *i2c_dev;
int res;
if (dev->type != &i2c_adapter_type)
return 0;
adap = to_i2c_adapter(dev);
i2c_dev = get_free_i2c_dev(adap);
if (IS_ERR(i2c_dev))
return PTR_ERR(i2c_dev);
/* register this i2c device with the driver core */
i2c_dev->dev = device_create(i2c_dev_class, &adap->dev,
MKDEV(I2C_MAJOR, adap->nr), NULL,
"i2c-%d", adap->nr);
if (IS_ERR(i2c_dev->dev)) {
res = PTR_ERR(i2c_dev->dev);
goto error;
}
pr_debug("i2c-dev: adapter [%s] registered as minor %d\n",
adap->name, adap->nr);
return 0;
error:
return_i2c_dev(i2c_dev);
return res;
}
可以看到第7行,只对I2C适配器对应的struct device做处理,如果是用户自定义的设备树驱动,直接返回了。16行就时创建设备文件。至此所有的I2C适配器在/dev目录下都有了自己的设备文件,用户就可以通过直接操作设备文件来操作I2C适配器了,struct file_operations i2cdev_fops,的open、read、write、ioctl函数就省略了,这些都是linux内核实现的,I2C驱动中用到的另一个重要的结构体struct i2c_client client是在open函数中创建的,感兴趣可以自行查看。这个结构体无论是I2C适配器还是自定义I2C设备都要使用,对于自定义的I2C设备下面还会详细讲述。
自定义I2C设备驱动
这里以I2C传感器ap3216c为例:
static int __init ap3216c_init(void){
return i2c_add_driver(&ap3216c);
}
static void __exit ap3216c_exit(void){
i2c_del_driver(&ap3216c);
}
module_init(ap3216c_init);
module_exit(ap3216c_exit);
其中i2c_add_driver完成整个I2C驱动和设备的匹配,以及调用i2c_driver->probe函数:
#define i2c_add_driver(driver) \
i2c_register_driver(THIS_MODULE, driver)
i2c_register_driver()函数定义如下:
int i2c_register_driver(struct module *owner, struct i2c_driver *driver)
{
int res;
/* Can't register until after driver model init */
if (unlikely(WARN_ON(!i2c_bus_type.p)))
return -EAGAIN;
/* add the driver to the list of i2c drivers in the driver core */
driver->driver.owner = owner;
driver->driver.bus = &i2c_bus_type;
/* When registration returns, the driver core
* will have called probe() for all matching-but-unbound devices.
*/
res = driver_register(&driver->driver);
if (res)
return res;
pr_debug("i2c-core: driver [%s] registered\n", driver->driver.name);
INIT_LIST_HEAD(&driver->clients);
/* Walk the adapters that are already present */
i2c_for_each_dev(driver, __process_new_driver);
return 0;
}
其中res = driver_register(&driver->driver);是通用函数,所有向所有类型的总线(不止I2C总线)添加driver的操作都会调用这个函数,所以不详细介绍,只简单给出调用路径:
driver_register-->
bus_add_driver-->
driver_attach(drv)-->
bus_for_each_dev--> (这里是遍历总线所有设备,并对每个设备调用__driver_attach函数)
-->__driver_attach
-->driver_match_device
-->drv->bus->match(此函数指针实际调用的是i2c_device_match)
-->driver_probe_device
-->really_probe
-->dev->bus->probe(此函数指针实际调用的是i2c_device_probe)
下面我们详细说明一下i2c_device_probe函数:
static int i2c_device_probe(struct device *dev)
{
struct i2c_client *client = i2c_verify_client(dev);
struct i2c_driver *driver;
int status;
if (!client)
return 0;
if (!client->irq && dev->of_node) {
int irq = of_irq_get(dev->of_node, 0);
if (irq == -EPROBE_DEFER)
return irq;
if (irq < 0)
irq = 0;
client->irq = irq;
}
driver = to_i2c_driver(dev->driver);
if (!driver->probe || !driver->id_table)
return -ENODEV;
if (!device_can_wakeup(&client->dev))
device_init_wakeup(&client->dev,
client->flags & I2C_CLIENT_WAKE);
dev_dbg(dev, "probe\n");
status = of_clk_set_defaults(dev->of_node, false);
if (status < 0)
return status;
status = dev_pm_domain_attach(&client->dev, true);
if (status != -EPROBE_DEFER) {
status = driver->probe(client, i2c_match_id(driver->id_table,
client));
if (status)
dev_pm_domain_detach(&client->dev, true);
}
return status;
}
第3行struct i2c_client *client = i2c_verify_client(dev);具体定义如下:
struct i2c_client *i2c_verify_client(struct device *dev)
{
return (dev->type == &i2c_client_type)
? to_i2c_client(dev)
: NULL;
}
可以看到,此probe函数只针对设备树中用户自定义的struct device有效,对I2C适配器不起作用。
另外I2C驱动中,另外一个重要的结构体struct i2c_client *client,就是在这里获取的。和I2C适配器不同,这里的struct i2c_client 不用像I2C适配器一样需要struct file_operation 中的open函数创建,而是内核从设备树提取I2C自定义设备信息的时候创建的,这里只是通过to_i2c_client(实质就是container_of)来获取。
获取到struct i2c_client结构体之后,会在36行 status = driver->probe(client, i2c_match_id(driver->id_table, client)); 调用用户自定义的probe函数,至此,就去执行用户添加的代码了。
这里给出一个自定义I2C设备驱动的例子(ap3216c芯片):
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/ide.h>
#include <linux/cdev.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <linux/of_irq.h>
#include <linux/poll.h>
#include <linux/wait.h>
#include <linux/platform_device.h>
#include <linux/input.h>
#include <linux/i2c.h>
#include <linux/delay.h>
#include "ap3216c.h"
#define AP3216C_CNT 1
#define AP3216C_NAME "ap3216c"
struct ap3216c_dev{
struct cdev cdev;
dev_t dev_id;
int major;
int minor;
struct class * cls;
struct device * device;
struct i2c_client * client;
};
static int ap3216c_read_regs(struct ap3216c_dev * dev, u8 reg, u8 * buf, int len){
struct i2c_client * client = dev->client;
struct i2c_msg msgs[] = {
{
.addr = client->addr,
.flags = 0,
.len = 1,
.buf = ®
},
{
.addr = client->addr,
.flags = I2C_M_RD,
.len = len,
.buf = buf
}
};
int ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
if(ret == ARRAY_SIZE(msgs)){
return 0;
}
else{
printk("i2c rd failed=%d reg=%06x len=%d\n", ret, reg, len);
return -EREMOTEIO;
}
}
static int ap3216c_write_regs(struct ap3216c_dev * dev, u8 reg, u8 * buf, int len){
struct i2c_client * client = dev->client;
u8 b[256];
int ret;
struct i2c_msg msgs[] = {
{
.addr = client->addr,
.flags = 0,
.len = len + 1,
.buf = b
},
};
b[0] = reg;
memcpy(&b[1], buf, len);
ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
if(ret == ARRAY_SIZE(msgs)){
return 0;
}
else{
printk("i2c rd failed=%d reg=%06x len=%d\n", ret, reg, len);
return -EREMOTEIO;
}
}
static int ap3216c_read_reg(struct ap3216c_dev *dev, u8 reg){
struct i2c_client * client = dev->client;
u8 data;
ap3216c_read_regs(dev, reg, &data, 1);
return data;
return i2c_smbus_read_byte_data(client, reg);
}
static int ap3216c_write_reg(struct ap3216c_dev * dev, u8 reg, u8 data){
struct i2c_client * client = dev->client;
return ap3216c_write_regs(dev, reg, &data, 1);
return i2c_smbus_write_byte_data(client, reg, data);
}
static int ap3216c_open(struct inode *inode, struct file *filp){
int ret = 0;
struct ap3216c_dev * dev = (struct ap3216c_dev *)container_of(inode->i_cdev, struct ap3216c_dev, cdev);
filp->private_data = dev;
ret = ap3216c_write_reg(dev, AP3216C_SYSTEMCONG, 0x04); /* 复位AP3216c */
if(ret < 0){
printk("ap3216c_write_reg failed ret = %d.\n", ret);
return ret;
}
mdelay(10);
ap3216c_write_reg(dev, AP3216C_SYSTEMCONG, 0x03); /* 开启ALS, PS+IR */
if(ret < 0)
return ret;
ret = ap3216c_read_reg(dev, AP3216C_SYSTEMCONG);
if(ret == 0x03)
return 0; /* 成功 */
else
return -EREMOTEIO; /* 失败 */
}
static int ap3216c_release(struct inode *inode, struct file *filp)
{
return 0;
}
ssize_t ap3216c_read(struct file *file, char __user *to, size_t size, loff_t *ppos){
unsigned char buf[6];
unsigned char i;
int ret = 0;
struct ap3216c_dev * dev = file->private_data;
unsigned short ir = 0, ps = 0, als = 0, data[3];
/* 循环读取所有传感器数据 */
for(i = 0; i < 6; i++)
{
ret = ap3216c_read_reg(dev, AP3216C_IRDATALOW + i);
if(ret < 0)
return -EREMOTEIO;
else
buf[i] = (unsigned char)ret;
}
if(buf[0] & 0X80) /* IR_OF位为1,则数据无效 */
ir = 0;
else /* 读取IR传感器的数据 */
ir = ((unsigned short)buf[1] << 2) | (buf[0] & 0X03);
als = ((unsigned short)buf[3] << 8) | buf[2]; /* 读取ALS传感器的数据 */
if(buf[4] & 0x40) /* IR_OF位为1,则数据无效 */
ps = 0;
else /* 读取PS传感器的数据 */
ps = ((unsigned short)(buf[5] & 0X3F) << 4) | (buf[4] & 0X0F);
data[0] = ir;
data[1] = als;
data[2] = ps;
ret = copy_to_user(to, data, sizeof(data));
if(ret)
return -EINVAL;
else
return sizeof(data);
}
static const struct file_operations ops = {
.owner = THIS_MODULE,
.read = ap3216c_read,
.open = ap3216c_open,
.release = ap3216c_release
};
static int ap3216c_probe(struct i2c_client * client, const struct i2c_device_id * id){
int ret = 0;
struct ap3216c_dev * dev = devm_kzalloc(&client->dev, sizeof(struct ap3216c_dev), GFP_KERNEL);
if(!dev)
return -ENOMEM;
dev->client = client;
if(dev->major){
dev->dev_id = MKDEV(dev->major, 0);
ret = register_chrdev_region(dev->dev_id, AP3216C_CNT, AP3216C_NAME);
dev->minor = MINOR(dev->dev_id);
}
else{
ret = alloc_chrdev_region(&dev->dev_id, 0, AP3216C_CNT, AP3216C_NAME);
dev->major = MAJOR(dev->dev_id);
dev->minor = MINOR(dev->dev_id);
}
if(ret){
printk("register_chrdev_region failed.\n");
return ret;
}
dev->cdev.owner = THIS_MODULE;
cdev_init(&dev->cdev, &ops);
ret = cdev_add(&dev->cdev, dev->dev_id, AP3216C_CNT);
if(ret){
printk("cdev_add failed.\n");
goto unreg_chrdev;
}
dev->cls = class_create(THIS_MODULE, AP3216C_NAME);
if(IS_ERR(dev->cls)){
printk("kernel class_create failed.\r\n");
ret = PTR_ERR(dev->cls);
goto failed_cdev_del;
}
dev->device = device_create(dev->cls, NULL, dev->dev_id, NULL, AP3216C_NAME);
if(IS_ERR(dev->cls)){
printk("kernel device_create failed.\r\n");
ret = PTR_ERR(dev->device);
goto failed_dest_class;
}
dev_set_drvdata(&client->dev, dev);
return 0;
failed_dest_class:
class_destroy(dev->cls);
failed_cdev_del:
cdev_del(&dev->cdev);
unreg_chrdev:
unregister_chrdev_region(dev->dev_id, AP3216C_CNT);
return ret;
}
static int ap3216c_remove(struct i2c_client * client){
struct ap3216c_dev * dev = dev_get_drvdata(&client->dev);
device_destroy(dev->cls, dev->dev_id);
class_destroy(dev->cls);
cdev_del(&dev->cdev);
unregister_chrdev_region(dev->dev_id, AP3216C_CNT);
return 0;
}
static const struct of_device_id ap3216c_of_match[] = {
{
.compatible = "sallenkey,ap3216c",
},
{ /* Sentinel */ }
};
static const struct i2c_device_id ap3216c_id[] = {
{.name = "sallenkey,ap3216c", 0},
{ /* Sentinel */ }
};
static struct i2c_driver ap3216c = {
.driver = {
.name = "ap3216c",
.of_match_table = ap3216c_of_match
},
.id_table = ap3216c_id,
.probe = ap3216c_probe,
.remove = ap3216c_remove,
};
static int __init ap3216c_init(void){
return i2c_add_driver(&ap3216c);
}
static void __exit ap3216c_exit(void){
i2c_del_driver(&ap3216c);
}
module_init(ap3216c_init);
module_exit(ap3216c_exit);
MODULE_AUTHOR("sallenkey");
MODULE_LICENSE("GPL");
源码在:源码
设备树可以参考:设备树文件