1 I2C 设备和驱动匹配过程
I2C
设备和驱动的匹配过程是由
I2C
核心来完成的,
drivers/i2c/i2c-core.c
就是
I2C
的核心
部分,
I2C
核心提供了一些与具体硬件无关的
API
函数,比如前面讲过的:
1
、
i2c_adapter
注册
/
注销函数
int i2c_add_adapter(struct i2c_adapter *adapter)
int i2c_add_numbered_adapter(struct i2c_adapter *adap)
void i2c_del_adapter(struct i2c_adapter * adap)
2
、
i2c_driver
注册
/
注销函数
int i2c_register_driver(struct module *owner, struct i2c_driver *driver)
int i2c_add_driver (struct i2c_driver *driver)
void i2c_del_driver(struct i2c_driver *driver)
设备和驱动的匹配过程也是由
I2C
总线完成的,
I2C
总线的数据结构为
i2c_bus_type
,定义
在
drivers/i2c/i2c-core.c
文件,
i2c_bus_type
内容如下:
736 struct bus_type i2c_bus_type = {
737 .name = "i2c",
738 .match = i2c_device_match,
739 .probe = i2c_device_probe,
740 .remove = i2c_device_remove,
741 .shutdown = i2c_device_shutdown,
742 };
.match
就是
I2C
总线的设备和驱动匹配函数,在这里就是
i2c_device_match
这个函数,此
函数内容如下:
457 static int i2c_device_match(struct device *dev, struct
device_driver *drv)
458 {
459 struct i2c_client *client = i2c_verify_client(dev);
460 struct i2c_driver *driver;
461
462 if (!client)
463 return 0;
464
465 /* Attempt an OF style match */
466 if (of_driver_match_device(dev, drv))
467 return 1;
468
469 /* Then ACPI style match */
470 if (acpi_driver_match_device(dev, drv))
471 return 1;
472
473 driver = to_i2c_driver(drv);
474 /* match on an id table if there is one */
475 if (driver->id_table)
476 return i2c_match_id(driver->id_table, client) != NULL;
477
478 return 0;
479 }
第
466
行,
of_driver_match_device
函数用于完成设备树设备和驱动匹配。比较
I2C
设备节
点的
compatible
属性和
of_device_id
中的
compatible
属性是否相等,如果相当的话就表示
I2C
设备和驱动匹配。
第
470
行,
acpi_driver_match_device
函数用于
ACPI
形式的匹配。
第
476
行,
i2c_match_id
函数用于传统的、无设备树的
I2C
设备和驱动匹配过程。比较
I2C
设备名字和
i2c_device_id
的
name
字段是否相等,相等的话就说明
I2C
设备和驱动匹配。
2 I2C 设备驱动编写流程

1、IO 修改或添加
首先肯定是要修改 IO,AP3216C 用到了 I2C1 接口,I.MX6U-ALPHA 开发板上的 I2C1 接 口使用到了 UART4_TXD 和 UART4_RXD,因此肯定要在设备树里面设置这两个 IO。如果要用 到 AP3216C 的中断功能的话还需要初始化 AP_INT 对应的 GIO1_IO01 这个 IO,本章实验我们 不使用中断功能。因此只需要设置 UART4_TXD 和 UART4_RXD 这两个 IO,NXP 其实已经将 他这两个 IO 设置好了,打开 imx6ull-alientek-emmc.dts,然后找到如下内容:
pinctrl_i2c1: i2c1grp {
fsl,pins = <
MX6UL_PAD_UART4_TX_DATA__I2C1_SCL 0x4001b8b0
MX6UL_PAD_UART4_RX_DATA__I2C1_SDA 0x4001b8b0
>;
};
pinctrl_i2c1
就是
I2C1
的
IO
节点,这里将
UART4_TXD
和
UART4_RXD
这两个
IO
分别
复用为
I2C1_SCL
和
I2C1_SDA
,电气属性都设置为
0x4001b8b0
。
2
、在
i2c1
节点追加
ap3216c
子节点
i2c1: i2c@021a0000 {
#address-cells = <1>;
#size-cells = <0>;
compatible = "fsl,imx6ul-i2c", "fsl,imx21-i2c";
reg = <0x021a0000 0x4000>;
interrupts = <GIC_SPI 36 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks IMX6UL_CLK_I2C1>;
status = "disabled";
};
AP3216C
是连接到
I2C1
上的,因此需要在
i2c1
节点下添加
ap3216c
的设备子节点,在
imx6ull-alientek-emmc.dts
文件中找到
i2c1
节点,此节点默认内容如下:
1 &i2c1 {
2 clock-frequency = <100000>;
3 pinctrl-names = "default";
4 pinctrl-0 = <&pinctrl_i2c1>;
5 status = "okay";
6
7 mag3110@0e {
8 compatible = "fsl,mag3110";
9 reg = <0x0e>;
10 position = <2>;
11 };
12
13 fxls8471@1e {
14 compatible = "fsl,fxls8471";
15 reg = <0x1e>;
16 position = <0>;
17 interrupt-parent = <&gpio5>;
18 interrupts = <0 8>;
19 };
20 };
第
2
行,
clock-frequency
属性为
I2C
频率,这里设置为
100KHz
。
第
4
行,
pinctrl-0
属性指定
I2C
所使用的
IO
为示例代码
61.5.1.1
中的
pinctrl_i2c1
子节
点。
第
7~11
行,
mag3110
是个磁力计,
NXP
官方的
EVK
开发板上接了
mag3110
,因此
NXP
在
i2c1
节点下添加了
mag3110 这个子节点。
第
13~19
行,
NXP
官方
EVK
开发板也接了一个
fxls8471。
将
i2c1
节点里面原有的
mag3110
和
fxls8471
这两个
I2C
子节点删除,然后添加
ap3216c
子节点信息,完成以后的
i2c1
节点内容如下所示:
&i2c1 {
clock-frequency = <100000>;
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_i2c1>;
status = "okay";
ap3216c@1e{
compatible = "alientek,ap3216c";
reg = <0x1e>;
};
};
第
7
行,
ap3216c
子节点,
@
后面的“
1e
”是
ap3216c
的器件地址。
第
8
行,设置
compatible
值为“
alientek,ap3216c
”。
第
9
行,
reg
属性也是设置
ap3216c
器件地址的,因此
reg
设置为
0x1e
。
设备树修改完成以后使用“
make dtbs
”重新编译一下,然后使用新的设备树启动
Linux
内
核。
/sys/bus/i2c/devices
目录下存放着所有
I2C
设备,如果设备树修改正确的话,会在
/sys/bus/i2c/devices
目录下看到一个名为“
0-001e
”的子目录,如图
61.5.1.1
所示:

3 编写驱动框架,
3.1I2C设备驱动框架和字符设备驱动框架
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/slab.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>
#include <linux/delay.h>
#include <linux/string.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/of_irq.h>
#include <linux/input.h>
#include <linux/i2c.h>
struct ap3216c_dev{
dev_t devid;
int major;
int minor;
struct cdev cdev;
struct class *class;
struct device *device;
struct device_node *nd;
};
struct ap3216c_dev ap3216cdev;
#define ap3216c_cnt 1
#define ap3216c__name "ap3216c"
static int ap3216cdev_open(struct inode *inode, struct file *filp)
{
printk("ap3216c open\r\n");
return 0;
}
static int ap3216cdev_release(struct inode *inode, struct file *filp)
{
printk("ap3216c release\r\n");
return 0;
}
static ssize_t ap3216c_read(struct file *filp, char __user *buf, size_t count, loff_t *ppos)
{
printk("ap3216c read\r\n");
return 0;
}
static const struct file_operations ap3216cdev_fops = {
.owner = THIS_MODULE,
.read = ap3216c_read ,
.open = ap3216cdev_open,
.release = ap3216cdev_release,
};
static int ap3216c_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
int ret = 0;
printk("ap3216 probe\n");
ap3216cdev.major = 0;
if(ap3216cdev.major) {
ap3216cdev.devid = MKDEV(ap3216cdev.major,0);
ret = register_chrdev_region(ap3216cdev.devid, ap3216c_cnt, ap3216c__name);
} else {
ret = alloc_chrdev_region(&ap3216cdev.devid, 0, ap3216c_cnt, ap3216c__name);
ap3216cdev.major = MAJOR(ap3216cdev.devid);
ap3216cdev.minor = MINOR(ap3216cdev.devid);
}
if(ret < 0) {
goto fail_devid;
}
printk("major = %d,minor = %d\r\n",ap3216cdev.major,ap3216cdev.minor);
ap3216cdev.cdev.owner = THIS_MODULE;
cdev_init(&ap3216cdev.cdev, &ap3216cdev_fops);
ret = cdev_add(&ap3216cdev.cdev, ap3216cdev.devid, ap3216c_cnt);
if(ret < 0) {
goto fail_cdev;
}
ap3216cdev.class = class_create (THIS_MODULE, ap3216c__name);
if (IS_ERR(ap3216cdev.class)) {
ret = PTR_ERR(ap3216cdev.class);
goto fail_class;
}
ap3216cdev.device = device_create(ap3216cdev.class, NULL, ap3216cdev.devid, NULL,ap3216c__name);
if (IS_ERR(ap3216cdev.device)) {
ret = PTR_ERR(ap3216cdev.device);
goto fail_device;
}
return 0;
fail_device:
class_destroy(ap3216cdev.class);
fail_class:
cdev_del(&ap3216cdev.cdev);
fail_cdev:
unregister_chrdev_region(ap3216cdev.devid, ap3216c_cnt);
fail_devid:
return ret;
}
static int ap3216c_remove(struct i2c_client *client)
{
printk("ap3216 remove\r\n");
device_destroy(ap3216cdev.class, ap3216cdev.devid);
class_destroy(ap3216cdev.class);
cdev_del(&ap3216cdev.cdev);
unregister_chrdev_region(ap3216cdev.devid, ap3216c_cnt);
return 0;
}
static const struct i2c_device_id ap3216c_id_table[] = {
{ "ysy_ap3216c", 0 },
{ },
};
static const struct of_device_id ap3216c_of_match_table[] = {
{.compatible = "alientek,ap3216c"},
{ /*sentinel*/},
};
static struct i2c_driver i2c_driver = {
.driver = {
.name = "ap3216c",
.owner = THIS_MODULE,
.of_match_table = ap3216c_of_match_table,
},
.probe = ap3216c_probe,
.remove = ap3216c_remove,
.id_table = ap3216c_id_table,
};
static int __init ap3216c_init(void)
{
return i2c_add_driver(&i2c_driver);
}
static void __exit ap3216c_exit(void)
{
i2c_del_driver(&i2c_driver);
}
module_init(ap3216c_init);
module_exit(ap3216c_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("YSY");
3.2 驱动编写
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/slab.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>
#include <linux/delay.h>
#include <linux/string.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/of_irq.h>
#include <linux/input.h>
#include <linux/i2c.h>
#include "ap3216creg.h"
struct ap3216c_dev{
dev_t devid;
int major;
int minor;
struct cdev cdev;
struct class *class;
struct device *device;
struct device_node *nd;
struct i2c_client *private;
short ir,ps,als;
};
struct ap3216c_dev ap3216cdev;
#define ap3216c_cnt 1
#define ap3216c__name "ap3216c"
/*读取ap3216c的N个寄存器值*/
static int ap3216c_read_regs(struct ap3216c_dev *dev, u8 reg, void *val, int len)
{
struct i2c_client *client = (struct i2c_client *)dev->private;
//struct i2c_msg msg[2];
/*msg[0]发送要读取的寄存器的首地址 msg[1]读取数据*/
struct i2c_msg msg[2] = {
{
.addr = client->addr,
.flags = 0,
.buf = ®,
.len = 1,
},
{
.addr = client->addr,
.flags = I2C_M_RD,
.buf = val,
.len = len,
},
};
#if 0
msg[0].addr = client->addr;
msg[0].flags = 0;
msg[0].buf = ®
msg[0].len = 1;
msg[1].addr = client->addr;
msg[1].flags = I2C_M_RD;
msg[1].buf = val;
msg[1].len = len;
#endif
return i2c_transfer(client->adapter, msg, 2);
}
/*向ap3216写N个寄存器值*/
static int ap3216c_write_regs(struct ap3216c_dev *dev, u8 reg, u8 *buf, int len)
{
u8 b[256];
struct i2c_client *client = (struct i2c_client *)dev->private;
//struct i2c_msg msg;
/*msg[0]发送要读取的寄存器的首地址 msg[1]读取数据*/
struct i2c_msg msg[1] = {
{
.addr = client->addr,
.flags = 0,
.buf = b,
.len = len+1,
},
};
b[0]=reg;
memcpy(&b[1], buf, len);
#if 0
b[0]=reg;
memcpy(&b[1], buf, len);
msg.addr = client->addr;
msg.flags = 0;
msg.buf = b;
msg.len = len+1;
#endif
return i2c_transfer(client->adapter, msg, 1);
}
static unsigned char ap3216c_read_reg(struct ap3216c_dev *dev, u8 reg)
{
u8 data = 0;
ap3216c_read_regs(dev, reg, &data, 1);
return data;
}
static void ap3216c_write_reg(struct ap3216c_dev *dev, u8 reg, u8 data)
{
u8 buf = 0;
buf = data;
ap3216c_write_regs(dev, reg, &data, 1);
}
static int ap3216cdev_open(struct inode *inode, struct file *filp)
{
int value = 0;
filp->private_data = &ap3216cdev;
ap3216c_write_reg(&ap3216cdev, AP3216C_SYSTEMCONG, 0X04);
mdelay(50);
ap3216c_write_reg(&ap3216cdev, AP3216C_SYSTEMCONG, 0X03);
value = ap3216c_read_reg(&ap3216cdev, AP3216C_SYSTEMCONG);
printk("value = %#x\r\n",value);
return 0;
}
void ap3216c_readdata(struct ap3216c_dev *dev)
{
unsigned char buf[6];
unsigned char i;
/* 循环读取所有传感器数据 */
for(i = 0; i < 6; i++)
{
buf[i] = ap3216c_read_reg(dev, AP3216C_IRDATALOW + i);
}
if(buf[0] & 0X80) /* IR_OF位为1,则数据无效 */
{
dev->ir = 0;
dev->ps = 0 ;
} else {
dev->ir = ((unsigned short)buf[1] << 2) | (buf[0] & 0X03);
dev->ps = ((unsigned short)(buf[5] & 0X3F) << 4) | (buf[4] & 0X0F);
}
dev->als = ((unsigned short)buf[3] << 8) | buf[2]; /* 读取ALS传感器的数据 */
}
static int ap3216cdev_release(struct inode *inode, struct file *filp)
{
printk("ap3216c release\r\n");
return 0;
}
static ssize_t ap3216c_read(struct file *filp, char __user *buf, size_t count, loff_t *ppos)
{
short data[3];
int err = 0;
struct ap3216c_dev *dev = (struct ap3216c_dev *)filp->private_data;
//printk("ap3216c read\r\n");
ap3216c_readdata(dev);
data[0] = dev->ir;
data[1] = dev->als;
data[2] = dev->ps;
err = copy_to_user(buf, data, sizeof(data));
return 0;
}
static const struct file_operations ap3216cdev_fops = {
.owner = THIS_MODULE,
.read = ap3216c_read ,
.open = ap3216cdev_open,
.release = ap3216cdev_release,
};
static int ap3216c_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
int ret = 0;
printk("ap3216 probe\n");
ap3216cdev.major = 0;
if(ap3216cdev.major) {
ap3216cdev.devid = MKDEV(ap3216cdev.major,0);
ret = register_chrdev_region(ap3216cdev.devid, ap3216c_cnt, ap3216c__name);
} else {
ret = alloc_chrdev_region(&ap3216cdev.devid, 0, ap3216c_cnt, ap3216c__name);
ap3216cdev.major = MAJOR(ap3216cdev.devid);
ap3216cdev.minor = MINOR(ap3216cdev.devid);
}
if(ret < 0) {
goto fail_devid;
}
printk("major = %d,minor = %d\r\n",ap3216cdev.major,ap3216cdev.minor);
ap3216cdev.cdev.owner = THIS_MODULE;
cdev_init(&ap3216cdev.cdev, &ap3216cdev_fops);
ret = cdev_add(&ap3216cdev.cdev, ap3216cdev.devid, ap3216c_cnt);
if(ret < 0) {
goto fail_cdev;
}
ap3216cdev.class = class_create (THIS_MODULE, ap3216c__name);
if (IS_ERR(ap3216cdev.class)) {
ret = PTR_ERR(ap3216cdev.class);
goto fail_class;
}
ap3216cdev.device = device_create(ap3216cdev.class, NULL, ap3216cdev.devid, NULL,ap3216c__name);
if (IS_ERR(ap3216cdev.device)) {
ret = PTR_ERR(ap3216cdev.device);
goto fail_device;
}
ap3216cdev.private = client;
return 0;
fail_device:
class_destroy(ap3216cdev.class);
fail_class:
cdev_del(&ap3216cdev.cdev);
fail_cdev:
unregister_chrdev_region(ap3216cdev.devid, ap3216c_cnt);
fail_devid:
return ret;
}
static int ap3216c_remove(struct i2c_client *client)
{
printk("ap3216 remove\r\n");
device_destroy(ap3216cdev.class, ap3216cdev.devid);
class_destroy(ap3216cdev.class);
cdev_del(&ap3216cdev.cdev);
unregister_chrdev_region(ap3216cdev.devid, ap3216c_cnt);
return 0;
}
static const struct i2c_device_id ap3216c_id_table[] = {
{ "ysy_ap3216c", 0 },
{ },
};
static const struct of_device_id ap3216c_of_match_table[] = {
{.compatible = "alientek,ap3216c"},
{ /*sentinel*/},
};
static struct i2c_driver i2c_driver = {
.driver = {
.name = "ap3216c",
.owner = THIS_MODULE,
.of_match_table = ap3216c_of_match_table,
},
.probe = ap3216c_probe,
.remove = ap3216c_remove,
.id_table = ap3216c_id_table,
};
static int __init ap3216c_init(void)
{
return i2c_add_driver(&i2c_driver);
}
static void __exit ap3216c_exit(void)
{
i2c_del_driver(&i2c_driver);
}
module_init(ap3216c_init);
module_exit(ap3216c_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("YSY");
3.3编写测试 APP
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <linux/input.h>
int main(int argc,char *argv[])
{
int fd;
int ret;
unsigned short data[3];
char *filename;
unsigned short ir,ps,als;
filename = argv[1];
if(argc != 2)
{
printf("Error usage!\r\n");
return -1;
}
fd=open(filename,O_RDWR);
if(fd<0)
{
printf("file %s open fialed !\r\n",filename);
return -1;
}
while(1)
{
ret = read(fd, &data, sizeof(data));
if(ret == 0) {
ir = data[0];
als = data[1];
ps = data[2];
printf("ir=%d, ps=%d, als=%d\r\n",ir,ps,als);
}
usleep(200000);
}
close(fd);
return 0;
}
ap3216cApp.c
文件内容很简单,就是在
while
循环中不断的读取
AP3216C
的设备文件,从
而得到
ir
、
als
和
ps
这三个数据值,然后将其输出到终端上。