linux的I2C总线框架

目录

关键数据结构

i2c设备注册和匹配

i2c驱动注册和匹配

再看看platform_driver的注册

设备树的匹配方式(of)

i2c adapter/Algorithm

参考链接


IIC的全称是Inter-Integrated Circuit,由Philips公司推出,是近年来在微电子通信控制制领域广泛采用的一种新型总线标准。它是同步通信的一种特殊形式,仅需两根线(SLC、SDA)即可完成信息传输,并且通信速率较高。在主从通信中,可以有多个IIC设备同时接到IIC总线上,通过地址来识别通信对象。

Linux的IIC体系结构分为3个组成部分,分别是IIC核心、IIC总线驱动和IIC设备驱动!

(1)IIC核心

提供了IIC总线驱动与IIC设备驱动的注册、注销方法和与具体IIC控制器无关的代码,该部分用来管理IIC总线驱动与IIC设备驱动。

(2)IIC总线驱动

IIC总线驱动是适配器(IIC控制器)的驱动程序,包含了适配器(IIC控制器)的数据描述结构i2c_adapter、通信方法数据结构i2c_algorithm、控制适配器产生通信时序的函数。

(3)IIC设备驱动

是具体IIC设备的驱动,因为不同的IIC设备可能有不同的读写时序要求,比如at24cxx读写需要发送16位地址,而tmp75只需要8位地址即可。IIC设备驱动通过i2c_algorithm通信方法驱动适配器驱动程序去访问具体的IIC设备。


架构层次分类:

  第一层:提供i2c adapter的硬件驱动,探测、初始化i2c adapter(如申请i2c的io地址和中断号),驱动soc控制的i2c adapter在硬件上产生信号(start、stop、ack)以及处理i2c中断。图中的硬件实现控制层。

  第二层:提供i2c adapter的algorithm,用具体适配器的xxx_xfer()函数来填充i2c_algorithm的master_xfer函数指针,并把赋值后的i2c_algorithm再赋值给i2c_adapter的algo指针。图中的访问抽象层。

  第三层:实现i2c设备驱动中的i2c_driver接口,用具体的i2c device设备的probe()、remove()方法赋值给i2c_driver的成员函数指针。实现设备device与总线(或者叫adapter)的匹配挂接。图中的driver驱动层。

  第四层:实现i2c设备所对应的具体device的驱动,i2c_driver只是实现设备与总线的挂接,而挂接在总线上的设备则是千差万别的,所以要实现具体设备device的write()、read()、ioctl()等方法,赋值给file_operations,然后注册字符设备(多数是字符设备)。
 

关键数据结构

i2c_bus_type:

struct bus_type i2c_bus_type = {
    .name        = "i2c",                //总线的名称
    .match       = i2c_device_match,     /* iic设备与驱动注册时候会调用这个函数进行匹配操作,里面有匹配规则,目前是通过IIC设备i2c_client结构体中名字与驱动i2c_driver结构体中id_table匹配,注意:并不会匹配驱动名字! */
    .probe       = i2c_device_probe,     //匹配成功后会调用总线的probe函数,在里面进一步调用iic驱动的prob函数(也就是自己写的)
    .remove      = i2c_device_remove,    //与上面一样
    .shutdown    = i2c_device_shutdown,
    .pm          = &i2c_device_pm_ops,    //电源管理
};

I2C总线对应着/bus下的一条总线,这个i2c总线结构体管理着i2c设备与I2C驱动的匹配,删除等操作,I2C总线会调用i2c_device_match函数看I2C设备和I2C驱动是否匹配,如果匹配就调用i2c_device_probe函数,进而调用I2C驱动的probe函数。

注意:i2c_device_match会管理I2C设备和I2C总线匹配规则,这将和如何编写I2C驱动程序息息相关

i2c_adapter:

struct i2c_adapter {
    struct module *owner;                //所有者
    unsigned int class;                  /*适配器支持的从设备类型 */
    const struct i2c_algorithm *algo;    /* 适配器的通信方法,也就是上面的i2c_algorithm */
    void *algo_data;                     //algo私有数据
    int timeout;                         /* 超时时间 */
    int retries;                         //重传次数,在通信方法中使用
    struct device dev;                   /* 表明这是一个设备,挂载在I2C总线上 */
    int nr;                              //适配器(总线)的编号
    char name[48];                       //适配器名称
    struct list_head userspace_clients;  //IIC设备的client依附链表头,只有在用户层echo name addr > /sys/bus/i2c/devices/i2c-x/new_device时候创建的client才会依附在此链表
    …//省略部分无关成员
};

描述一个适配器(IIC控制器),用于向IIC核心注册。设备数据结构i2c_client中的一个对象指向i2c_adapter,这样就可以通过其中的方法(i2c_adapter内部的i2c_algorithm对象)以及i2c物理控制器来和一个i2c总线的物理设备进行交互。

i2c_algorithm:

struct i2c_algorithm {
	int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs,
			   int num);

	int (*master_xfer_atomic)(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);

	int (*smbus_xfer_atomic)(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 *adap);
};

描述一个适配器的通信方法,用于产生I 2 C访问周期需要的信号,该类的对象algo是i2c_adapter的一个域,其中的master_xfer()注册的函数最终被设备驱动端的i2c_transfer()回调。在构建i2c_adapter数据结构时要填充这个数据结构,否则i2c_adapter什么也做不了。

i2c_client:

struct i2c_client {
    unsigned short flags;               //I2C_CLIENT_TEN表示设备使用10bit从地址,I2C_CLIENT_PEC表示设备使用SMBus检错
    unsigned short addr;                /*IIC设备基地址,7bit。这里说一下为什么是7位,因为最后以为0表示写,1表示读,通过对这个7bit地址移位处理即可。addr<<1 & 0x0即写,addr<<1 | 0x01即读*/
    char name[I2C_NAME_SIZE];           //iic设备名字,用于匹配iic驱动
    struct i2c_adapter *adapter;        /*iic设备是挂在哪个适配器(总线)上        */
    struct device dev;                  /* 表明这是一个设备,挂在I2C总线上        */
    int irq;                            /* 中断号,用于申请中断,一般是在自己实现的iic驱动的probe函数中使用*/
    struct list_head detected;          //是i2c_adapter结构体或i2c_driver结构体中链表的节点
};
 

struct i2c_board_info {
    char        type[I2C_NAME_SIZE];   //设备名,最长20个字符,最终安装到client的name上
    unsigned short    flags;           //最终安装到client.flags
    unsigned short    addr;            //设备从地址slave address,最终安装到client.addr上
    void        *platform_data;        //设备数据,最终存储到i2c_client.dev.platform_data上
    struct dev_archdata    *archdata;
    struct device_node *of_node;       //OpenFirmware设备节点指针
    struct acpi_dev_node acpi_node;
    int        irq;                    //设备采用的中断号,最终存储到i2c_client.irq上
};

描述一个挂接在硬件i2c总线上的设备的设备信息,即i2c设备的设备对象,与i2c_driver对象匹配成功后通过detected和i2c_driver相连。一般通过i2c_client_new_device()创建。

i2c_driver:


struct i2c_driver {
    unsigned int class;
    int (*attach_adapter)(struct i2c_adapter *) __deprecated;           //老的匹配函数
    /* Standard driver model interfaces */
    int (*probe)(struct i2c_client *, const struct i2c_device_id *);    //当前匹配成功后执行函数,一般是申请资源,注册字符驱动
    int (*remove)(struct i2c_client *);       //当前移除设备或驱动时执行的移除函数,一般是释放资源
    int (*probe_new)(struct i2c_client *);    //未来匹配成功后的执行函数
    void (*shutdown)(struct i2c_client *);    //关闭设备
    void (*alert)(struct i2c_client *, enum i2c_alert_protocol protocol,unsigned int data);
    int (*command)(struct i2c_client *client, unsigned int cmd, void *arg);
    struct device_driver driver;             //表明这是一个驱动,驱动模型用来挂在I2C总线上
    const struct i2c_device_id *id_table;    //设备匹配列表,非常重要,IIC设备的名字与这个列表匹配
    int (*detect)(struct i2c_client *, struct i2c_board_info *);
    const unsigned short *address_list;      //该驱动所支持的所有(次设备)的地址数组,用于注册驱动时自动去匹配
    struct list_head clients;                //用来挂接与该i2c_driver匹配成功的i2c_client (次设备)的一个链表头
    bool disable_i2c_core_irq_mapping;
};

描述一个挂接在硬件i2c总线上的设备的驱动方法,即i2c设备的驱动对象,通过i2c_bus_type和设备信息i2c_client匹配,匹配成功后通过clients和i2c_client对象相连。

i2c_msg:


struct i2c_msg {
    __u16 addr;                                    /* IIC设备的基地址,7位 */
    __u16 flags;                                  //操作标志位
    #define I2C_M_RD                0x0001        /* 设置了这个标志位表示本次通信i2c控制器是处于接收方,否则就是发送方 */
    #define I2C_M_TEN                0x0010       /* 设置了这个标志位表示从设备的地址是10bit */
    #define I2C_M_DMA_SAFE                0x0200   /* the buffer of this message is DMA safe */
    /* makes only sense in kernelspace */
    /* userspace buffers are copied anyway */
    #define I2C_M_RECV_LEN                0x0400        /* length will be first received byte */
    #define I2C_M_NO_RD_ACK                0x0800        /* 设置这个标志位表示在读操作中主机不用ACK */
    #define I2C_M_IGNORE_NAK        0x1000          /* 设置这个标志意味当前i2c_msg忽略I2C器件的ack和nack信号 */
    #define I2C_M_REV_DIR_ADDR        0x2000        /* if I2C_FUNC_PROTOCOL_MANGLING */
    #define I2C_M_NOSTART                0x4000        /* if I2C_FUNC_NOSTART */
    #define I2C_M_STOP                0x8000        /* if I2C_FUNC_PROTOCOL_MANGLING */
    __u16 len;                /* 读写数据的长度 */
    __u8 *buf;                /* 装有数据的缓冲区 */

}

描述一个在设备端和主机端之间进行流动的数据,在设备驱动中打包并通过i2c_transfer()发送。

linux的I2C总线框架_第1张图片

i2c设备注册和匹配

内核通过i2c_client_new_device方法(drivers/i2c/i2c-core-base.c)注册i2c设备

struct i2c_client *
i2c_new_client_device(struct i2c_adapter *adap, struct i2c_board_info const *info)

需要指定i2c设配器和设备信息,一般可以通过i2c_client的adapter指针拿到;

i2c_adapter可以通过i2c_get_adapter来获取

struct i2c_adapter *i2c_get_adapter(int nr)

i2c_get_adapter可以获取i2c控制器,获取哪一个i2c控制器取决于你的i2c设备接在哪条i2c总线上;

i2c_client_new_device方法

struct i2c_client *
i2c_new_client_device(struct i2c_adapter *adap, struct i2c_board_info const *info)
{
    struct i2c_client	*client;
    
    client->adapter = adap; // 设定设备的设配器
	client->dev.platform_data = info->platform_data;
	client->flags = info->flags;
	client->addr = info->addr;

	client->init_irq = info->irq;
    
	client->dev.parent = &client->adapter->dev;
	client->dev.bus = &i2c_bus_type;        // 绑定总线
	client->dev.type = &i2c_client_type;    //设置设备类型,后面用于匹配
	client->dev.of_node = of_node_get(info->of_node);
	client->dev.fwnode = info->fwnode;

    client->dev.bus = &i2c_bus_type; 
    
    status = device_register(&client->dev); // 向总线注册设备
    
    return client;
}

展开方法device_register的内容:(drivers/base/core.c)

int device_register(struct device *dev)
{
	device_initialize(dev);
	return device_add(dev);
}
EXPORT_SYMBOL_GPL(device_register);
int device_add(struct device *dev)
{
    ...
        error = bus_add_device(dev);
    bus_probe_device(dev);
    ...
}
int bus_add_device(struct device *dev)
{
    /*将设备添加到总线的设备链表中(bus->p->klist_devices)*/
	klist_add_tail(&dev->p->knode_bus, &bus->p->klist_devices);
}
void bus_probe_device(struct device *dev)
{
	struct bus_type *bus = dev->bus;
	struct subsys_interface *sif;

	if (!bus)
		return;

	if (bus->p->drivers_autoprobe)
		device_initial_probe(dev);

	mutex_lock(&bus->p->mutex);
	list_for_each_entry(sif, &bus->p->interfaces, node)
		if (sif->add_dev)
			sif->add_dev(dev, sif);
	mutex_unlock(&bus->p->mutex);
}

如果总线设置了自动为新加设备探测匹配的驱动,调用device_initial_probe方法(drivers/base/dd.c)

void device_initial_probe(struct device *dev)
{
	__device_attach(dev, true);
}
static int __device_attach(struct device_driver *drv, void *data)
{
    ret = bus_for_each_drv(dev->bus, NULL, &data,
			__device_attach_driver);
}
int bus_for_each_drv(struct bus_type *bus, struct device_driver *start,
		     void *data, int (*fn)(struct device_driver *, void *))
{
	...
	while ((drv = next_driver(&i)) && !error)
		error = fn(drv, data);
	...
	return error;
}
EXPORT_SYMBOL_GPL(bus_for_each_drv);

__device_attach方法(driver/base/bus.c),完成对dev的总线上所有的drv进行匹配探测,匹配的规则由fn的决定,即__device_attach_driver方法;

static int __device_attach_driver(struct device_driver *drv, void *_data)
{
    ret = driver_match_device(drv, dev);
    ret = driver_probe_device(drv, dev);
}

driver_match_device是一个inline函数(drivers/base/base.h),i2c device_driver匹配过程中调用drv->bus的match函数,即一开始的关键数据结构bus_type的match方法

static inline int driver_match_device(struct device_driver *drv,
				      struct device *dev)
{
	return drv->bus->match ? drv->bus->match(dev, drv) : 1;
}

复习一下bus_type的结构,(drivers/i2c/i2c-core-base.c)

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,
};
EXPORT_SYMBOL_GPL(i2c_bus_type);

i2c_device_match方法:一共有三种方式,设备树、ACPI、非设备树;

static int i2c_device_match(struct device *dev, struct device_driver *drv)
{
	struct i2c_client	*client = i2c_verify_client(dev);
	struct i2c_driver	*driver;
/*三种匹配方式*/
/*1. 设备树的匹配方式,系统在初始化时会将所有设备树子节点转换成由struct device_node描述的节点,
匹配方式是分别比较两者的name、type、和compatible字符串,三者要同时相同
(一般name、和type为空,只比较compatible字符串,
比较compatible时,是比较整个字符串,不管字符串里的逗号的,直接compare整个字符串)*/
	/* Attempt an OF style match */
	if (i2c_of_match_device(drv->of_match_table, client))
		return 1;

	/* Then ACPI style match */
	if (acpi_driver_match_device(dev, drv))
		return 1;

	driver = to_i2c_driver(drv);

	/* Finally an I2C match */
    /*3. 比较设备的name和id_table的name字段是否一致 strcmp*/
	if (i2c_match_id(driver->id_table, client))
		return 1;

	return 0;
}

#define to_i2c_driver(d) container_of(d, struct i2c_driver, driver)

匹配完成之后,接着看driver_probe_device方法,是在找到符合match条件的第一个drv后,对device和driver进行关联操作,并调用probe函数;

static int __driver_probe_device(struct device_driver *drv, struct device *dev)
{
	int ret = 0;
    //判断设备是否注册
	if (dev->p->dead || !device_is_registered(dev))
		return -ENODEV;
	if (dev->driver)
		return -EBUSY;
    ...
    if (initcall_debug)
		ret = really_probe_debug(dev, drv);
	else
		ret = really_probe(dev, drv);
}
static int really_probe(struct device *dev, struct device_driver *drv)
{
    ret = call_driver_probe(dev, drv);
}

static int call_driver_probe(struct device *dev, struct device_driver *drv)
{
	int ret = 0;

	if (dev->bus->probe)
		ret = dev->bus->probe(dev);
	else if (drv->probe)
		ret = drv->probe(dev);
	return ret;
}

probe函数的调用启动了;

i2c驱动注册和匹配

接下来看一下编写驱动的过程中,通过i2c_add_driver函数来注册驱动,

#define i2c_add_driver(driver)  i2c_register_driver(THIS_MODULE, driver)

int i2c_register_driver(struct module *owner, struct i2c_driver *driver)
{
	int res;
	/* add the driver to the list of i2c drivers in the driver core */
	driver->driver.owner = owner;
	driver->driver.bus = &i2c_bus_type;  //绑定i2C总线
	INIT_LIST_HEAD(&driver->clients);

	/* 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;

    //遍历总线上所有的设备,调用__process_new_driver函数
	/* Walk the adapters that are already present */
	i2c_for_each_dev(driver, __process_new_driver);

	return 0;
}
EXPORT_SYMBOL(i2c_register_driver);

上面的程序可以看到,在调用i2c_add_driver后做了三件事,第一绑定总线,要记住这个总线结构体,第二向总线注册驱动,这算是I2C驱动框架的重点,第三遍历总线的设备,调用__process_new_driver函数

乍一看,会绝对第三件事比较重要,好像匹配规则在这里面,其实这里面有一部分匹配规则,但并不是最重要的,最重要的是在driver_register函数中
drivers/base/drivers.c 文件中的 driver_register方法,

int driver_register(struct device_driver *drv)
{
	int ret;
	struct device_driver *other;
    //判断驱动是否已经注册,如果注册则不再继续
	other = driver_find(drv->name, drv->bus);
	if (other) {
		pr_err("Error: Driver '%s' is already registered, "
			"aborting...\n", drv->name);
		return -EBUSY;
	}

	ret = bus_add_driver(drv);
	if (ret)
		return ret;
	ret = driver_add_groups(drv, drv->groups);
	if (ret) {
		bus_remove_driver(drv);
		return ret;
	}
	kobject_uevent(&drv->p->kobj, KOBJ_ADD);

	return ret;
}
EXPORT_SYMBOL_GPL(driver_register);

drivers/base/bus.c 中的bus_add_driver方法

int bus_add_driver(struct device_driver *drv)
{
    ...
    //将驱动添加到总线的驱动链表中(bus->p->klist_drivers)
	klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers);
	if (drv->bus->p->drivers_autoprobe) {
		error = driver_attach(drv);
		if (error)
			goto out_unregister;
	}
    ...
	return error;
}

看一下driver_attach方法,位于 drivers/base/dd.c文件中

int driver_attach(struct device_driver *drv)
{
	return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);
}
EXPORT_SYMBOL_GPL(driver_attach);

bus_for_each_dev方法中,

int bus_for_each_dev(struct bus_type *bus, struct device *start,
		     void *data, int (*fn)(struct device *, void *))
{
    //遍历总线上的所有设备表(bus->p->klist_devices)上所有的设备,执行fn函数
	while (!error && (dev = next_device(&i)))
		error = fn(dev, data);
}
EXPORT_SYMBOL_GPL(bus_for_each_dev);

回到上一个函数,bus_for_each_dev对应的fn函数是__driver_attach函数,所以会遍历总线上的所有设备执行__driver_attach函数,接下面来分析__driver_attach函数;

static int __driver_attach(struct device *dev, void *data)
{
	struct device_driver *drv = data;
	int ret;
    //判断驱动和设备是否匹配
	ret = driver_match_device(drv, dev);
	/* ret > 0 means positive match */

    //如果匹配成功,调用driver_probe_device函数
	__device_driver_lock(dev, dev->parent);
	driver_probe_device(drv, dev);
	__device_driver_unlock(dev, dev->parent);
	return 0;
}

所以又回到了device注册的过程,最终结果就是调用drv的probe函数。

回到i2c_register_driver方法中,在看看bus_for_each_dev函数,就是遍历总线上所有的设备和driver作为参数,执行__process_new_driver方法(drivers/ic2/i2c-core-base.c)

#define to_i2c_adapter(d) container_of(d, struct i2c_adapter, dev)
static int __process_new_driver(struct device *dev, void *data)
{
	if (dev->type != &i2c_adapter_type)
		return 0;
	return i2c_do_add_adapter(data, to_i2c_adapter(dev));
}

static int i2c_do_add_adapter(struct i2c_driver *driver,
			      struct i2c_adapter *adap)
{
	/* Detect supported devices on that bus, and instantiate them */
	i2c_detect(adap, driver);

	return 0;
}

根据调用关系,确认方法i2c_detect

static int i2c_detect(struct i2c_adapter *adapter, struct i2c_driver *driver)
{
    struct i2c_client *temp_client;
    //获取驱动指定的地址
    address_list = driver->address_list;
	temp_client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL);
	if (!temp_client)
		return -ENOMEM;
    //绑定适配备器
	temp_client->adapter = adapter;

    //使用适配器,去探测地址
	for (i = 0; address_list[i] != I2C_CLIENT_END; i += 1) {
		temp_client->addr = address_list[i];
		err = i2c_detect_address(temp_client, driver);
		if (unlikely(err))
			break;
	}

	kfree(temp_client);
	return err;
}
static int i2c_detect_address(struct i2c_client *temp_client,
			      struct i2c_driver *driver)
{
    if(i2c_check_addr_busy(adapter, addr)) return 0;
        
    if(!i2c_default_probe(adapter, addr)) return 0;
    
    /* 如果能到达这里,就说明该i2c控制器对应的总线上,该地址存在,调用驱动的detect函数进一步确认 */
    driver->detect(temp_client, &info);
    
    /* 如果驱动程序确认的话,生成i2c设备 */
    client = i2c_new_device(adapter, &info);
    list_add_tail(&client->detected, &driver->clients); // 将该设备添加到驱动程序的链表中
}

以上的做法是,遍历总线上的所有设备,拿到设备对应的设备器(I2C控制器),去给总线发送驱动指定好的地址,如果地址存在的话,就调用驱动的detect函数;

如果在不知道i2c设备在哪一条总线的情况下,这种方式就发挥了作用

总结device和driver的调用方式如下:

linux的I2C总线框架_第2张图片

再看看platform_driver的注册

struct platform_driver {
	int (*probe)(struct platform_device *);
	int (*remove)(struct platform_device *);
	void (*shutdown)(struct platform_device *);
	int (*suspend)(struct platform_device *, pm_message_t state);
	int (*resume)(struct platform_device *);
	struct device_driver driver;
	const struct platform_device_id *id_table;
	bool prevent_deferred_probe;
};

drivers/i2c/busses/i2c-cadence.c驱动为例,首先关注驱动入口,驱动加载后

module_platform_driver(cdns_i2c_drv);

MODULE_AUTHOR("Xilinx Inc.");
MODULE_DESCRIPTION("Cadence I2C bus driver");
MODULE_LICENSE("GPL");

其中module_platform_driver的宏就是将当前驱动注册进内核的方法

#define module_platform_driver(__platform_driver) \
	module_driver(__platform_driver, platform_driver_register, \
			platform_driver_unregister)

module_driver宏表示的就是常见的module_init和module_exit,即驱动加载和卸载执行入口,即将注册和解注册函数分别修改为platform_driver_register和platform_driver_unregister。

#define module_driver(__driver, __register, __unregister, ...) \
static int __init __driver##_init(void) \
{ \
	return __register(&(__driver) , ##__VA_ARGS__); \
} \
module_init(__driver##_init); \
static void __exit __driver##_exit(void) \
{ \
	__unregister(&(__driver) , ##__VA_ARGS__); \
} \
module_exit(__driver##_exit);

而platform_driver_register是将platform_driver结构中的device_driver传入到内核的驱动注册函数中,driver_register 细节上面已经分析过了。最终结果是,将匹配到的device结构作为参数并调用driver的probe方法。

drivers/base/platform.c

int __platform_driver_register(struct platform_driver *drv,
				struct module *owner)
{
	drv->driver.owner = owner;
	drv->driver.bus = &platform_bus_type;

	return driver_register(&drv->driver);
}
EXPORT_SYMBOL_GPL(__platform_driver_register);

platform_bus_type 是platform总线类型,根据上面driver的注册过程,最终会调用总线的probe函数,

struct bus_type platform_bus_type = {
	.name		= "platform",
	.dev_groups	= platform_dev_groups,
	.match		= platform_match,
	.uevent		= platform_uevent,
	.probe		= platform_probe,
	.remove		= platform_remove,
	.shutdown	= platform_shutdown,
	.dma_configure	= platform_dma_configure,
	.pm		= &platform_dev_pm_ops,
};
EXPORT_SYMBOL_GPL(platform_bus_type);

总线的probe函数,会得到匹配的platform_device,并调用driver的probe方法。

static int platform_probe(struct device *_dev)
{
	struct platform_driver *drv = to_platform_driver(_dev->driver);
	struct platform_device *dev = to_platform_device(_dev);
	int ret;

	ret = of_clk_set_defaults(_dev->of_node, false);
	if (ret < 0)
		return ret;

	ret = dev_pm_domain_attach(_dev, true);
	if (ret)
		goto out;

    //platform_driver调用probe函数,将platform_device作为参数传入
	if (drv->probe) {
		ret = drv->probe(dev);
		if (ret)
			dev_pm_domain_detach(_dev, true);
	}

	return ret;
}

设备树的匹配方式(of)

解释一下 CONFIG_OF 宏,OF是 Open Firmware的缩写,内核中对于是否使用设备树作为硬件的管理方式,是用这个宏来管理的。宏定义的具体位置在kernel/arch/arm/Kconfig或kernel/arch/arm64/Kconfig中,解释如下,

Open Firmware. This was invented long time ago when Apple was producing laptops based on PowerPC CPUs and Sun Microsystems workstations were still popular. Open Firmware provides a good description of the devices connected to the platform. In Linux kernel the part that works with device data is called Device Tree (DT). Device Tree

设备树的机制其实也是总线型的 Bus/Dev/Drv 模型,只是编写 Dev 的方式变了。即编写 设备树文件 .dts。dts文件会被编译成 dtb 文件。dtb文件会传给内核, 内核会解析dtb文件, 构造出一系列的 device_node 结构体,device_node 结构体会转换为 platform_device 结构体,所以开发者可以在dts文件中指定资源,不再需要在.c文件中设置platform_device结构体。

“来自dts的platform_device结构体” 与 “我们写的platform_driver” 的匹配过程:

  • "来自 dts 的 platform_device 结构体"里面有成员 “.dev.of_node”, 它里面含有各种属性, 比如 compatible, reg, pin
  • “我们写的 platform_driver” 里面有成员 “.driver.of_match_table”, 它表示能支持哪些来自于 dts 的platform_device

如果 “of_node 中的 compatible” 跟 “of_match_table 中的 compatible” 一致, 就表示匹配成功, 则调用 platform_driver中的probe函数; 在probe函数中, 可以继续从of_node中获得各种属性来确定硬件资源。

我们关注 \arch\arm64\boot\dts\xilinx\zynqmp.dtsi

		i2c0: i2c@ff020000 {
			compatible = "cdns,i2c-r1p14", "cdns,i2c-r1p10";
			status = "disabled";
			interrupt-parent = <&gic>;
			interrupts = <0 17 4>;
			reg = <0x0 0xff020000 0x0 0x1000>;
			#address-cells = <1>;
			#size-cells = <0>;
			power-domains = <&zynqmp_firmware PD_I2C_0>;
		};

		i2c1: i2c@ff030000 {
			compatible = "cdns,i2c-r1p14", "cdns,i2c-r1p10";
			status = "disabled";
			interrupt-parent = <&gic>;
			interrupts = <0 18 4>;
			reg = <0x0 0xff030000 0x0 0x1000>;
			#address-cells = <1>;
			#size-cells = <0>;
			power-domains = <&zynqmp_firmware PD_I2C_1>;
		};

drivers/i2c/i2c-core-of.c文件中主要是设备树操作的函数,对应头文件在include/linux/i2c.h中。

我们重点关注设备树匹配的逻辑和驱动从设备树能够获取的信息是什么?

/*
 * 设备树中用于匹配字段的数据结构
 * Struct used for matching a device
 */
struct of_device_id {
	char	name[32];
	char	type[32];
	char	compatible[128];
	const void *data;
};

首先回顾一下上面,总线的匹配函数i2c_device_match中参数为struct device *dev和struct device_driver *drv,调用设备树匹配方式

if (i2c_of_match_device(drv->of_match_table, client))
		return 1;

drv->of_match_table结构信息如下,

static const struct dev_pm_ops cdns_i2c_dev_pm_ops = {
	SET_RUNTIME_PM_OPS(cdns_i2c_runtime_suspend,
			   cdns_i2c_runtime_resume, NULL)
};

static const struct cdns_platform_data r1p10_i2c_def = {
	.quirks = CDNS_I2C_BROKEN_HOLD_BIT,
};

static const struct of_device_id cdns_i2c_of_match[] = {
	{ .compatible = "cdns,i2c-r1p10", .data = &r1p10_i2c_def },
	{ .compatible = "cdns,i2c-r1p14",},
	{ /* end of table */ }
};
MODULE_DEVICE_TABLE(of, cdns_i2c_of_match);


static struct platform_driver cdns_i2c_drv = {
	.driver = {
		.name  = DRIVER_NAME,
		.of_match_table = cdns_i2c_of_match,
		.pm = &cdns_i2c_dev_pm_ops,
	},
	.probe  = cdns_i2c_probe,
	.remove = cdns_i2c_remove,
};

module_platform_driver(cdns_i2c_drv);

MODULE_AUTHOR("Xilinx Inc.");
MODULE_DESCRIPTION("Cadence I2C bus driver");
MODULE_LICENSE("GPL");

接下来关注匹配逻辑

const struct of_device_id
*i2c_of_match_device(const struct of_device_id *matches,
		     struct i2c_client *client)
{
	const struct of_device_id *match;


	match = of_match_device(matches, &client->dev);
	if (match)
		return match;

	return i2c_of_match_device_sysfs(matches, client);
}
EXPORT_SYMBOL_GPL(i2c_of_match_device);

/* client是由dev结构container_of宏得到的
 * drivers/of/base.c
 * of_match_device--> of_match_node --> __of_match_node --> __of_device_is_compatible
 * 将对应的节点 按照 compatible type name顺序进行匹配,如果有多个满足,
会统计score计分,优选compatible匹配且在链表中靠前的,其次是type匹配的,最后是name;
 */
/*通过dev->of_node获得设备树文件解析得到的device_node结构*/
const struct of_device_id *of_match_device(const struct of_device_id *matches,
					   const struct device *dev)
{
	if ((!matches) || (!dev->of_node))
		return NULL;
	return of_match_node(matches, dev->of_node);
}
EXPORT_SYMBOL(of_match_device);
const struct of_device_id *of_match_node(const struct of_device_id *matches,
					 const struct device_node *node)
{
	const struct of_device_id *match;
	unsigned long flags;

	raw_spin_lock_irqsave(&devtree_lock, flags);

    //驱动的of_device_id和设备树device_node进行匹配
	match = __of_match_node(matches, node);
	raw_spin_unlock_irqrestore(&devtree_lock, flags);
	return match;
}
EXPORT_SYMBOL(of_match_node);
static
const struct of_device_id *__of_match_node(const struct of_device_id *matches,
					   const struct device_node *node)
{
	const struct of_device_id *best_match = NULL;
	int score, best_score = 0;

	if (!matches)
		return NULL;

    //对驱动的of_device_id结构进行循环,获取name type compatible字段进行比较
	for (; matches->name[0] || matches->type[0] || matches->compatible[0]; matches++) {
		score = __of_device_is_compatible(node, matches->compatible,
						  matches->type, matches->name);
		if (score > best_score) {
			best_match = matches;
			best_score = score;
		}
	}

	return best_match;
}
static int __of_device_is_compatible(const struct device_node *device,
				     const char *compat, const char *type, const char *name)
{
	struct property *prop;
	const char *cp;
	int index = 0, score = 0;

	/* Compatible match has highest priority 优先对compatible进行匹配*/
    /* 将device的compatible,即设备树中的字段,和compat进行比较*/
	if (compat && compat[0]) {
		prop = __of_find_property(device, "compatible", NULL);
		for (cp = of_prop_next_string(prop, NULL); cp;
		     cp = of_prop_next_string(prop, cp), index++) {
			if (of_compat_cmp(cp, compat, strlen(compat)) == 0) {
				score = INT_MAX/2 - (index << 2);
				break;
			}
		}
		if (!score)
			return 0;
	}

	/* Matching type is better than matching name */
	if (type && type[0]) {
		if (!__of_node_is_type(device, type))
			return 0;
		score += 2;
	}

	/* Matching name is a bit better than not */
	if (name && name[0]) {
		if (!of_node_name_eq(device, name))
			return 0;
		score++;
	}

	return score;
}

如果上述方法都比较失败了,则执行下面的比较,

接着看方法 i2c_of_match_device_sysfs

//主要是比较设备name字段
static const struct of_device_id*
i2c_of_match_device_sysfs(const struct of_device_id *matches,
				  struct i2c_client *client)
{
	const char *name;

	for (; matches->compatible[0]; matches++) {
		/*
		 * Adding devices through the i2c sysfs interface provides us
		 * a string to match which may be compatible with the device
		 * tree compatible strings, however with no actual of_node the
		 * of_match_device() will not match
		 */
		if (sysfs_streq(client->name, matches->compatible))
			return matches;
        /*比较of_device_id中compatible字段和设备name是否匹配,
            或者比较of_device_id的compatible字段‘,’后的内容*/
		name = strchr(matches->compatible, ',');
		if (!name)
			name = matches->compatible;
		else
			name++;

		if (sysfs_streq(client->name, name))
			return matches;
	}

	return NULL;
}

驱动的“compatible = "cdns,i2c-r1p14", "cdns,i2c-r1p10";”和设备树中的对应节点匹配上之后,驱动就可以通过device_node获取对应设备的参数对device进行控制;

i2c adapter/Algorithm

adapter是iic控制器的抽象,是直接决定对硬件控制的方法(寄存器参数、数据收发),所以大多数情况下,芯片厂商会写好。adapter中封装了device,对应了总线中device结构,并以此注册在i2c_bus_type上,而 i2c_algorithm是直接控制收发数据的方法;

static const struct i2c_algorithm cdns_i2c_algo = {
	.master_xfer	= cdns_i2c_master_xfer,
	.functionality	= cdns_i2c_func,
#if IS_ENABLED(CONFIG_I2C_SLAVE)
	.reg_slave	= cdns_reg_slave,
	.unreg_slave	= cdns_unreg_slave,
#endif
};

i2c-cadence drv的probe函数,

static int cdns_i2c_probe(struct platform_device *pdev)
{
	...
	ret = i2c_add_adapter(&id->adap);
	if (ret < 0)
		goto err_clk_dis;
    ...
}

i2c_add_adapter方法(drivers/i2c/i2c-core-base.c)中主要调用i2c_register_adapter

static int i2c_register_adapter(struct i2c_adapter *adap)
{
	dev_set_name(&adap->dev, "i2c-%d", adap->nr);
	adap->dev.bus = &i2c_bus_type;
	adap->dev.type = &i2c_adapter_type;
    //执行了adapter的device设备注册到总线上,细节上面介绍过了
	res = device_register(&adap->dev);
	if (res) {
		pr_err("adapter '%s': can't register device (%d)\n", adap->name, res);
		goto out_list;
	}

	/* create pre-declared device nodes */
	of_i2c_register_devices(adap);
	i2c_acpi_install_space_handler(adap);
	i2c_acpi_register_devices(adap);

	if (adap->nr < __i2c_first_dynamic_bus_num)
		i2c_scan_static_board_info(adap);

	/* Notify drivers */
	mutex_lock(&core_lock);
	bus_for_each_drv(&i2c_bus_type, NULL, adap, __process_new_adapter);
	mutex_unlock(&core_lock);
...
}

参考链接:

 linux 设备树详解

linux下i2c框架

Linux I2C驱动框架

Linux设备模型:kset, kobj, ktype

嵌入式开发zynqMp ---Zynq UltraScale+ MPSoC

Zynq UltraScale+ MPSoC

你可能感兴趣的:(驱动开发)