Linux IIC 驱动分析(1) — 框架分析
目录
IIC 属于较为常用的总线,一般会集成到 SoC 上,作为一个通用外设而存在,IIC 总线会接有类似 EEPROM、TP (触摸屏)等设备,他们通过 IIC 协议与 SoC 进行通信,将数据传输/接收到 SoC。关于总线协议部分参考
IIC 总线协议详解
,这里不在多说协议部分,着重关注 Linux 的 IIC 驱动
框架
部分;
1、框架
大的软件框架如上图所示:
1、应用层通过标准的 open 调用进行 IIC 设备的操作;
2、每一个 i2c_client 对应一个实际硬件上的 IIC device(比如 EEPROM)
3、每一个 i2c_driver 描述一种 IIC 设备的驱动
4、i2c_dev 是注册的字符类型的设备
5、i2c_core 是 IIC 核心层,提供了总线、驱动、通信方式等的注册和钩子函数的设置
6、i2c_adapter 用于描述一个 SoC 的一个 IIC 控制器
7、i2c_algorithm 用于底层对接实际的控制器,产生 IIC 硬件波形的函数
8、下面对接的就是实际的 SoC 的 IIC 控制器(寄存器)和硬件
IIC 的 Linux 软件结构与 Linux SPI 部分(
Linux SPI 驱动分析(1)— 结构框架
)的基本一致,主要的代码实现集中在:***drivers/i2c/***目录下,首先我们先认识一下 Linux 是怎么抽象 IIC 的,这对于更好的理解代码,至关重要:
这张图,是在描述软件对硬件的抽象过程,非红的部分,是硬件,SoC 上挂了 N 个 IIC 的控制器,每个控制器上,挂了 M 个设备。红色部分,是 Linux 针对硬件抽象出来的软件结构,由此可以看到,Linux 驱动中:
1、**i2c_adatper:**描述一个实际的 IIC 物理硬件
2、**i2c_algorithm:**函数指针集,钩子函数,用于描述特定 SoC 硬件的 IIC 模块产生通信波形的方法
3、**i2c_client:**描述一个挂接到 IIC 总线上的具体物理设备
4、**i2c_driver:**用于描述一个 IIC 设备的驱动
有了抽象出来的
数据结构
后,在结合 Linux 设备驱动的框架(Bus、Device、Driver),我们来看看在软件层次上,每个层次与上下层之间的关系,以及抽象出来的接口。
2、数据结构&软件层次
有了第一章的部分,我们来看看围绕着几个数据结构划分的几个软件层次
2.1、
i2c_adatper
i2c_adatper
用于描述一个实际的 IIC 控制器,它的定义在 *
include/linux/i2c.h*
文件,如下:
struct i2c_adapter {
struct module *owner; // 所属模块
unsigned int class; /* classes to allow probing for */
const struct i2c_algorithm *algo; // 总线通信方法结构体指针
void *algo_data; // algorithm数据
/* data fields that are valid for all devices */
struct rt_mutex bus_lock; //控制并发访问的自旋锁
int timeout; /* in jiffies */
int retries; // 重试次数
struct device dev; // 适配器设备
int nr;
char name[48]; // 适配器名称
struct completion dev_released;
struct mutex userspace_clients_lock;
struct list_head userspace_clients; // client 链表头
};
它定义了一些基本的成员,核心成员 *algo,它包含了操作这个 IIC 控制器的函数集,也就是直接对接到实际的 SoC 的 IIC 控制器的操作(寄存器配置);针对每款 CPU 来说,IIC 控制器可能不止一个,所以 i2c_adapter 也可能不止一个,他们都需要在系统上电的时候,进行初始化,所以,这些 i2c_adapter 结构,是通过 platform 总线连接,并在上电的时候通过定义多个 platform device 设备和 platform_driver,靠 platform_bus 连接起来,并进行多次的 probe 调用,进行初始化的!
比如:
static struct platform_device_id s3c24xx_driver_ids[] = {
{
.name = "s3c2410-i2c",
.driver_data = TYPE_S3C2410,
}, {
.name = "s3c2440-i2c",
.driver_data = TYPE_S3C2440,
}, {
.name = "s3c2440-hdmiphy-i2c",
.driver_data = TYPE_S3C2440_HDMIPHY,
}, { },
};
MODULE_DEVICE_TABLE(platform, s3c24xx_driver_ids);
static struct platform_driver s3c24xx_i2c_driver = {
.probe = s3c24xx_i2c_probe,
.remove = s3c24xx_i2c_remove,
.id_table = s3c24xx_driver_ids,
.driver = {
.owner = THIS_MODULE,
.name = "s3c-i2c",
.pm = S3C24XX_DEV_PM_OPS,
},
};
static int __init i2c_adap_s3c_init(void)
{
return platform_driver_register(&s3c24xx_i2c_driver);
}
subsys_initcall(i2c_adap_s3c_init);
这里定义并注册了针对该款芯片的的 platform driver,同时:
void __init exynos4_map_io(void)
{
....
/* The I2C bus controllers are directly compatible with s3c2440 */
s3c_i2c0_setname("s3c2440-i2c");
s3c_i2c1_setname("s3c2440-i2c");
s3c_i2c2_setname("s3c2440-i2c");
....
}
所以这款 CPU 有 3 个 IIC 硬件控制器,这个 s3c24xx_i2c_probe 会被匹配 3 次,也就是调用 3 次;
既然 i2c_adapter 结构和具体的硬件相关,那么必然的,不同 CPU 需要有不同的实例化,所以,在每个 CPU 的 xxx_i2c_probe 函数中,就是对 i2c_adapter 最好的实例化的地方;
static int s3c24xx_i2c_probe(struct platform_device *pdev)
{
struct s3c24xx_i2c *i2c;
struct s3c2410_platform_i2c *pdata;
struct resource *res;
int ret;
....
i2c->adap.owner = THIS_MODULE;
i2c->adap.algo = &s3c24xx_i2c_algorithm;
i2c->adap.retries = 2;
i2c->adap.class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
i2c->tx_setup = 50;
spin_lock_init(&i2c->lock);
init_waitqueue_head(&i2c->wait);
....
ret = request_irq(i2c->irq, s3c24xx_i2c_irq, IRQF_DISABLED,
dev_name(&pdev->dev), i2c);
....
i2c->adap.nr = pdata->bus_num;
ret = i2c_add_numbered_adapter(&i2c->adap);
if (ret < 0) {
dev_err(&pdev->dev, "failed to add bus to i2c core\n");
goto err_cpufreq;
}
platform_set_drvdata(pdev, i2c);
....
return ret;
}
在 probe 中,一般的,需要申请 IRQ 资源,获取寄存器资源,这里主要关注的是,
初始化了一个 i2c_adapter 结构,并将它注册到了 i2c 核心
!
2.2、
i2c_algorithm
i2c_algorithm
代表了和硬件对接的一组 IIC 控制器操作集合:
struct i2c_algorithm {
/* If an adapter algorithm can't do I2C-level access, set master_xfer
to NULL. If an adapter algorithm can do SMBus access, set
smbus_xfer. If set to NULL, the SMBus protocol is simulated
using common I2C messages */
/* master_xfer should return the number of messages successfully
processed, or a negative value on error */
int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs,
int num);
int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr,
unsigned short flags, char read_write,
u8 command, int size, union i2c_smbus_data *data);
/* To determine what the adapter supports */
u32 (*functionality) (struct i2c_adapter *);
};
可以看到,master_xfer 函数用于数据传送和读取,functionality 用来获取 IIC 控制器支持情况,根据 xxx_i2c_probe 分析可知,在调用这个 xxx_i2c_probe 的时候,已经将其操作集 i2c_algorithm 挂接到了 i2c_adapter 结构,一并注册到了 i2c 核心。
/* i2c bus registration info */
static const struct i2c_algorithm s3c24xx_i2c_algorithm = {
.master_xfer = s3c24xx_i2c_xfer,
.functionality = s3c24xx_i2c_func,
};
static int s3c24xx_i2c_probe(struct platform_device *pdev)
{
...
i2c->adap.owner = THIS_MODULE;
i2c->adap.algo = &s3c24xx_i2c_algorithm;
i2c->adap.retries = 2;
i2c->adap.class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
i2c->tx_setup = 50;
...
return ret;
}
2.3、i2c_bus_type
现在我们有了对接底层的那套东西,并已经注册到了 i2c 核心,现在我们转战 i2c 核心;
根据 Linux 的设备、驱动、总线的思想,i2c 设备应该通过 Linux 的 i2c 总线(bus)挂接上去,并和其固定的 driver 进行匹配,所以在 i2c 核心初始化的时候,必须先要注册 i2c bus,在 i2c-core.c 中:
static int __init i2c_init(void)
{
int retval;
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;
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;
}
static void __exit i2c_exit(void)
{
i2c_del_driver(&dummy_driver);
#ifdef CONFIG_I2C_COMPAT
class_compat_unregister(i2c_adapter_compat_class);
#endif
bus_unregister(&i2c_bus_type);
}
/* We must initialize early, because some subsystems register i2c drivers
* in subsys_initcall() code, but are linked (and initialized) before i2c.
*/
postcore_initcall(i2c_init);
module_exit(i2c_exit);
这里建立起了 i2c bus:
struct bus_type i2c_bus_type = {
.name = "i2c",
.match = i2c_device_match,
.probe = i2c_device_probe,
.remove = i2c_device_remove,
.shutdown = i2c_device_shutdown,
.pm = &i2c_device_pm_ops,
};
i2c bus 定义 match 的规则:
static int i2c_device_match(struct device *dev, struct device_driver *drv)
{
struct i2c_client *client = i2c_verify_client(dev);
struct i2c_driver *driver;
if (!client)
return 0;
/* Attempt an OF style match */
if (of_driver_match_device(dev, drv))
return 1;
driver = to_i2c_driver(drv);
/* match on an id table if there is one */
if (driver->id_table)
return i2c_match_id(driver->id_table, client) != NULL;
return 0;
}
可以看到,这里是通过 match 它的 id_table 来进行匹配的。
到这里,i2c 核心,建立起来了 i2c bus,就等着挂接 device 和 driver 上去了:
2.4、i2c_client
现在 i2c 控制器,i2c 操作硬件函数,i2c 总线(软件抽象的)已经准备完毕,接下来就需要添加 i2c 设备进总线,i2c 设备使用 *
i2c_client*
结构来描述:
struct i2c_client {
unsigned int flags; /* 标志 */
unsigned short addr; /* 低 7 位为芯片地址 */
char name[I2C_NAME_SIZE]; /* 设备名称 */
struct i2c_adapter *adapter; /*依附的 i2c_adapter*/
struct i2c_driver *driver; /*依附的 i2c_driver */
int irq;
struct device dev; /* 设备结构体 */
struct list_head detected; /* 链表头 */
};
比如一个 EEPROM 的 IIC 设备,那么它就需要首先实现这个 *
i2c_client*
结构,并将其注册(添加)到 i2c 核心,添加的函数为:
1、i2c_new_device
2、i2c_register_board_info
3、i2c_scan_static_board_info
4、i2c_detect_address
四种方式各有千秋,下一篇文章分析其用法
2.5、i2c_board_info
用于描述具体的 IIC 设备,和上面一个差不多,不再多讲:
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;
int irq;
};
2.6、i2c_driver
现在就剩下 driver 了,一个 IIC 外设的驱动用一个 *
i2c_driver*
结构描述:
struct i2c_driver {
unsigned int class;
/* Notifies the driver that a new bus has appeared or is about to be
* removed. You should avoid using this, it will be removed in a
* near future.
*/
int (*attach_adapter)(struct i2c_adapter *) __deprecated; /*依附 i2c_adapter 函数指针 */
int (*detach_adapter)(struct i2c_adapter *) __deprecated;
/* Standard driver model interfaces */
int (*probe)(struct i2c_client *, const struct i2c_device_id *);
int (*remove)(struct i2c_client *);
/* driver model interfaces that don't relate to enumeration */
void (*shutdown)(struct i2c_client *);
int (*suspend)(struct i2c_client *, pm_message_t mesg);
int (*resume)(struct i2c_client *);
/* Alert callback, for example for the SMBus alert protocol.
* The format and meaning of the data value depends on the protocol.
* For the SMBus alert protocol, there is a single bit of data passed
* as the alert response's low bit ("event flag").
*/
void (*alert)(struct i2c_client *, unsigned int data);
/* a ioctl like command that can be used to perform specific functions
* with the device.
*/
int (*command)(struct i2c_client *client, unsigned int cmd, void *arg);
struct device_driver driver;
const struct i2c_device_id *id_table;
/* Device detection callback for automatic device creation */
int (*detect)(struct i2c_client *, struct i2c_board_info *);
const unsigned short *address_list;
struct list_head clients;
};
具体的设备,需要实现对应的 i2c_driver,并注册到 i2c 核心,比如 EEPROM 设备,在 drivers/misc/eeprom.c:
static const struct i2c_device_id eeprom_id[] = {
{ "eeprom", 0 },
{ }
};
static struct i2c_driver eeprom_driver = {
.driver = {
.name = "eeprom",
},
.probe = eeprom_probe,
.remove = eeprom_remove,
.id_table = eeprom_id,
.class = I2C_CLASS_DDC | I2C_CLASS_SPD,
.detect = eeprom_detect,
.address_list = normal_i2c,
};
static int __init eeprom_init(void)
{
return i2c_add_driver(&eeprom_driver);
}
通过
i2c_add_driver
函数注册到 i2c 核心
2.7、i2c_msg
万事俱备只欠东风,东西都准备好了,那么开始传输数据吧,内核中使用 *
i2c_msg*
来描述一个 i2c 数据构成:
struct i2c_msg {
__u16 addr; /* slave address */
__u16 flags;
#define I2C_M_TEN 0x0010 /* this is a ten bit chip address */
#define I2C_M_RD 0x0001 /* read data, from slave to master */
#define I2C_M_NOSTART 0x4000 /* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_REV_DIR_ADDR 0x2000 /* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_IGNORE_NAK 0x1000 /* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_NO_RD_ACK 0x0800 /* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_RECV_LEN 0x0400 /* length will be first received byte */
__u16 len; /* msg length */
__u8 *buf; /* pointer to msg data */
};
数据传输,主要就是指定设备地址,数据长度,以及数据 buffer,flags 标记了传输的一些属性。
当数据准备好了,即填充好 i2c_msg 后,可以调用 i2c 核心层提供的函数进行数据的收发:
1、**i2c_transfer:**调用 adap->algo->master_xfer 进行数据收发
2、**i2c_master_send:**包装了 i2c_transfer 调用,填充 i2c_msg 进行数据发送一次
3、**i2c_master_recv:**包装了 i2c_transfer 调用,填充 i2c_msg 进行数据接收一次
2.8、i2c_dev
虽然万事 OK,但是还是需要给驱动层留一个访问 i2c 的口子吧?所以有了 i2c_dev,i2c 核心只是做了一些管理和提供接口的工作,那么具体的支撑用户层进行访问的算是这个 i2c_dev 了,在 i2c_dev.c 文件中,首先进行 i2c-dev 的分配和注册:
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;
}
/* 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;
}
同时提供了用户层的操作集合 *
i2cdev_fops*
:
static const struct file_operations i2cdev_fops = {
.owner = THIS_MODULE,
.llseek = no_llseek,
.read = i2cdev_read,
.write = i2cdev_write,
.unlocked_ioctl = i2cdev_ioctl,
.open = i2cdev_open,
.release = i2cdev_release,
};
用户层调用的 open、read、write、ioclt,都会先走到这里,我们看一个 i2cdev_read:
static ssize_t i2cdev_read(struct file *file, char __user *buf, size_t count,
loff_t *offset)
{
char *tmp;
int ret;
struct i2c_client *client = file->private_data;
if (count > 8192)
count = 8192;
tmp = kmalloc(count, GFP_KERNEL);
if (tmp == NULL)
return -ENOMEM;
pr_debug("i2c-dev: i2c-%d reading %zu bytes.\n",
iminor(file->f_path.dentry->d_inode), count);
ret = i2c_master_recv(client, tmp, count);
if (ret >= 0)
ret = copy_to_user(buf, tmp, count) ? -EFAULT : ret;
kfree(tmp);
return ret;
}
调用到和 i2c 核心层的
i2c_master_recv
函数后,将结果通过
copy_to_user
返回给用户层
3、小结
至此,i2c 的基本结构和最基本的流程分析差不多了,数据结构之间的关系为:
大致的结构之间的注册和初始化为:
用户层调用的流程大致为:
参考文档:
https://www.cnblogs.com/lifexy/p/7816324.html
https://blog.csdn.net/tech_pro/article/details/72902883
https://blog.csdn.net/m0_37661202/article/details/75949790
https://blog.csdn.net/shenlong1356/article/details/89212234
https://blog.csdn.net/fulinus/article/details/9008191
https://blog.csdn.net/W1107101310/article/details/79871029
https://blog.csdn.net/weixin_43542305/article/details/86479106