linux驱动移植-I2C总线设备驱动
----------------------------------------------------------------------------------------------------------------------------
内核版本:linux 5.2.8
根文件系统:busybox 1.25.0
u-boot:2016.05
----------------------------------------------------------------------------------------------------------------------------
在通信协议-I2C小节,我们已经对I2C协议进行了详细的介绍,并在Mini2440裸机开发之I2C(AT24C08)小节中介绍了通过I2C协议去读写AT24C08 EEPROM。在这一节将会学习I2C总线设备驱动模型。
一、I2C驱动框架
1.1 I2C框架
I2C总线设备驱动模型由I2C核心、I2C总线驱动(或者说I2C适配器驱动、I2C控制器驱动)、I2C设备驱动组成。
对于linux系统来说,支持各式各样的SOC,并且还想要支持各种I2C硬件芯片,就必须将一些公共的部分抽离出来,这样就抽象出了:
- i2c client:描述具体的I2C设备,每个i2c_client对应一个实际的I2C设备,比如AT24C08、MPU6050、SSD1306 OLED等;
- /sys/devices/platform/s3c2440-i2c.0/i2c-0/0-<addr>:I2C设备注册时创建;0为I2C适配器编号,addr为4位16进制数字,比如具有0x50地址的设备连接到0号I2C总线,则设备名为0-0050;
- i2c driver:描述一个I2C设备驱动,每个i2c_driver描述一种I2C设备的驱动;
- i2c dev:I2C字符设备;
- /dev/i2c-0:I2C字符设备文件;0为控制器的编号;
- /sys/class/i2c-dev/i2c-0:I2C字符设备所属类名为i2c-dev;
- i2c adapter:描述SOC的一个I2C控制器;
- /sys/devices/platform/s3c2440-i2c.0/i2c-0:I2C适配器设备注册时创建;0为I2C适配器编号,如果有多个I2C控制器注册,/sys/devices/platform/s3c2440-i2c.0目录下将会有多个i2c-%d目录;
注意:/sys/devices/platform/s3c2440-i2c.0的subsystem指向了/sys/bus/platform。而/sys/devices/platform/s3c2440-i2c.0/i2c.0的subsystem指向了/sys/bus/i2c。
- /sys/class/i2c-adapter:I2C控制器设备类为i2c-adatper,下面存储的都是I2C控制器相关的符号链接;
-
- /sys/bus/i2c/devices/i2c-0:I2C适配器设备注册时创建;0为I2C适配器编号,如果有多个I2C控制器注册,/sys/bus/devices目录下将会有多个i2c-%链接文件;其指向/sys/devices/platform/s3c2440-i2c.0/i2c-0;
- i2c algorithm:I2C通信算法,用于操作实际的I2C控制器,产生 I2C硬件波形;
在一个SOC上可能有多条I2C总线,一条总线对应一个I2C总线驱动,每一条总线上又可以接多个I2C设备。
假如一个SOC上有3个I2C适配器,每个I2C适配器,外接3个I2C设备,想要3个适配器分别都能驱动3个I2C设备,我们只需写3个适配器代码,3个I2C设备代码即可。
注意:关于/sys目录下各个文件的意义可以参考:/sys目录下各个子目录的具体说明。
1.1.1 I2C核心
I2C核心是linux内核用来维护和管理I2C的核心部分。I2C core提供接口函数,允许一个i2c_adapter、i2c_driver和i2c_client初始化时在I2C core中注册,以及退出时进行卸载,同时还提供了I2C总线读写访问的接口。
1.1.2 I2C总线驱动
I2C总线驱动包含了I2C适配器数据结构i2c_adapter、I2C通信算法i2c_algorithm和控制I2C适配器产生通信信号的函数。
经由I2C总线驱动的代码,我们可以控制I2C适配器以主控方式产生开始位、停止位、读写周期,以及以从设备方式被读写、产生ACK等。
1.1.3 I2C设备驱动
I2C设备驱动主要包含了数据结构i2c_driver和i2c_client,我们需要根据具体设备实现其中的成员函数。
1.2 目录结构
linux内核将I2C驱动相关的代码放在drivers/i2c目录下,这下面的文件还是比较多的,我们大概了解一下即可。
1.2.1 algos
保存I2C通信方面的算法。
1.2.2 busses
保存SoC厂家提供的I2C控制器驱动相关的文件,比如i2c-stm32.c、i2c-stm32f4.c、i2c-stm32f7.c、 i2c-versatile.c、 i2c-s3c2410.c等。
1.2.3 muxes
实现I2C mux驱动相关的文件,比如i2c-mux-pca954x.c。
mux包含如下功能:
- switch:在配置为 1:N 时,可以为每个子通道选择和设计。换句话说,它可以选择一个或多个;
- muliplexor:在配置为 1:N 或 N:1 时,可以选择和设计一个子通道。选择时,一次只能有一个通道。
1.2.4 i2c-core-base.c
这个文件实现了I2C核心的功能,I2C总线的初始化、注册和适配器添加和注销等相关工作。
1.2.5 i2c-dev.c
实现了I2C适配器设备文件的功能,每一个I2C适配器都被分配为一个设备。通过适配器访问设备的主设备号都为89,次设备号为0~255。
应用程序通过“i2c-%d”(i2c-0、i2c-1、i2c-2...)文件名并使用文件操作结构open()、write()、read()、ioctl()和close()等来访问这个设备。
i2c-dev.c并没有针对特定的设备而设计,只是提供了通用read()等接口,应用程序可以借用这些接口访问适配器上的I2C设备的存储空间或寄存器,并控制I2C设备的工作方式。
二、I2C总线注册
在linux驱动移植-platform总线设备驱动中我们已经介绍了platfrom总线的注册流程,I2C总线类型注册和platfrom总线注册类似。
2.1 I2C总线类型定义
在linux 设备模型中,总线类型由bus_type 结构表示,我们所用的 I2C、SPI、USB 都是用这个结构体来定义的。该结构体定义在 include/linux/device.h文件中:
/** * struct bus_type - The bus type of the device * * @name: The name of the bus. * @dev_name: Used for subsystems to enumerate devices like ("foo%u", dev->id). * @dev_root: Default device to use as the parent. * @bus_groups: Default attributes of the bus. * @dev_groups: Default attributes of the devices on the bus. * @drv_groups: Default attributes of the device drivers on the bus. * @match: Called, perhaps multiple times, whenever a new device or driver * is added for this bus. It should return a positive value if the * given device can be handled by the given driver and zero * otherwise. It may also return error code if determining that * the driver supports the device is not possible. In case of * -EPROBE_DEFER it will queue the device for deferred probing. * @uevent: Called when a device is added, removed, or a few other things * that generate uevents to add the environment variables. * @probe: Called when a new device or driver add to this bus, and callback * the specific driver's probe to initial the matched device. * @remove: Called when a device removed from this bus. * @shutdown: Called at shut-down time to quiesce the device. * * @online: Called to put the device back online (after offlining it). * @offline: Called to put the device offline for hot-removal. May fail. * * @suspend: Called when a device on this bus wants to go to sleep mode. * @resume: Called to bring a device on this bus out of sleep mode. * @num_vf: Called to find out how many virtual functions a device on this * bus supports. * @dma_configure: Called to setup DMA configuration on a device on * this bus. * @pm: Power management operations of this bus, callback the specific * device driver's pm-ops. * @iommu_ops: IOMMU specific operations for this bus, used to attach IOMMU * driver implementations to a bus and allow the driver to do * bus-specific setup * @p: The private data of the driver core, only the driver core can * touch this. * @lock_key: Lock class key for use by the lock validator * @need_parent_lock: When probing or removing a device on this bus, the * device core should lock the device's parent. * * A bus is a channel between the processor and one or more devices. For the * purposes of the device model, all devices are connected via a bus, even if * it is an internal, virtual, "platform" bus. Buses can plug into each other. * A USB controller is usually a PCI device, for example. The device model * represents the actual connections between buses and the devices they control. * A bus is represented by the bus_type structure. It contains the name, the * default attributes, the bus' methods, PM operations, and the driver core's * private data. */ struct bus_type { const char *name; const char *dev_name; struct device *dev_root; const struct attribute_group **bus_groups; const struct attribute_group **dev_groups; const struct attribute_group **drv_groups; int (*match)(struct device *dev, struct device_driver *drv); int (*uevent)(struct device *dev, struct kobj_uevent_env *env); int (*probe)(struct device *dev); int (*remove)(struct device *dev); void (*shutdown)(struct device *dev); int (*online)(struct device *dev); int (*offline)(struct device *dev); int (*suspend)(struct device *dev, pm_message_t state); int (*resume)(struct device *dev); int (*num_vf)(struct device *dev); int (*dma_configure)(struct device *dev); const struct dev_pm_ops *pm; const struct iommu_ops *iommu_ops; struct subsys_private *p; struct lock_class_key lock_key; bool need_parent_lock; };
其中部分字段的含义如下:
- name:总线名称;
- bus_groups:总线属性;
- dev_groups:该总线上所有设备的默认属性;
- drv_groups:该总线上所有驱动的默认属性;
- match:当有新的设备或驱动添加到总线上时match函数被调用,如果设备和驱动可以匹配,返回0;
- uevent:当一个设备添加、移除或添加环境变量时,函数调用;
- probe:当有新设备或驱动添加时,probe函数调用,并且回调该驱动的probe函数来初始化相关联的设备;
- remove:设备移除时调用remove函数;
- shutdown:设备关机时调用shutdown函数;
- suspend:设备进入睡眠时调用suspend函数;
- resume:设备唤醒时调用resume函数;
- pm:总线的电源管理选项,并回调设备驱动的电源管理模块;
- p: 驱动核心的私有数据,只有驱动核心才可以访问。使用struct subsys_private可以将struct bus_type中的部分细节屏蔽掉,利于外界使用bus_type;struct driver_private和struct device_private都有类似的功能。
i2c_bus_type是 bus_type 类型的全局变量,这个变量已经被 linux 内核赋值好了,其结构体成员对应的函数也已经在内核里面写好,定义在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, };
这里我们重点关注I2C匹配函数i2c_device_match即可。
2.2 I2C设备和驱动匹配
i2c_bus_type 中的 i2c_device_match就是我们常说的做驱动和设备匹配的函数,不同的总线对应的match函数肯定不一样,这个我们不用管,内核都会写好。我们所用的 I2C 总线对应的 match 函数是 i2c_device_match函数,该函数定义在drivers/i2c/i2c-core-base.c:
static int i2c_device_match(struct device *dev, struct device_driver *drv) { struct i2c_client *client = i2c_verify_client(dev); struct i2c_driver *driver; /* 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 */ if (i2c_match_id(driver->id_table, client)) return 1; return 0; }
该函数有两个参数:设备和设备驱动,该函数主要做了一下事情:
- 将设备转为I2C从设备类型;
- 将驱动转为I2C驱动类型;
- 调用i2c_of_driver_match_device进行设备树OF类型匹配;
- 调用acpi_driver_match_device进行ACPI类型匹配;
- 如果设置值了pdrv->id_table,进行id_table匹配;
通过对上面匹配函数的一个简单分析,我们知道匹配函数做匹配的顺序是先匹配设备树,然后匹配id_table表:
const struct i2c_device_id *i2c_match_id(const struct i2c_device_id *id, const struct i2c_client *client) { if (!(id && client)) return NULL; while (id->name[0]) { if (strcmp(client->name, id->name) == 0) return id; id++; } return NULL; }
对于支持设备树的 Linux 版本,我们一上来做设备树匹配就完事,不支持设备树时,我们就得定义I2C设备,再用id_tabale表匹配。
2.3 I2C总线注册
I2C子系统的初始化是由i2c_init函数完成的。I2C总线以模块的方式注册到内核。i2c_init定义在drivers/i2c/i2c-core-base.c文件中:
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; is_registered = true; #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)); if (IS_ENABLED(CONFIG_ACPI)) WARN_ON(acpi_reconfig_notifier_register(&i2c_acpi_notifier)); return 0; class_err: #ifdef CONFIG_I2C_COMPAT class_compat_unregister(i2c_adapter_compat_class); bus_err: #endif is_registered = false; bus_unregister(&i2c_bus_type); return retval; }
我们重点关注bus_register总线注册函数,传入的参数就是我们上面介绍的i2c_bus_type。
bus_register函数调用后,就会在用户空间/sys/bus目录下生成具有总线名称的目录,同时在该目录下创建devices、drivers文件夹,创建uevent、drivers_autoprobe、drivers_probe等文件。
执行如下命令:
/sys/bus/i2c/devices里用来存放的是I2C设备链接,/sys/bus/i2c/drivers里用来存放的是I2C驱动链接。需要注意的是在i2c_bus_type总线链表里面存放的除了I2C从设备,还有I2C适配器。
从其他博文找到一张总线创建的说明图,下图是foo总线创建的过程:
三、I2C核心数据结构
学习I2C驱动,首先要了解驱动框架涉及到的数据结构,知道每个数据结构以及成员的含义之后,再去看源码就容易了。
3.1 struct i2c_adapter
struct i2c_adapter抽象了控制器硬件,在SOC中的指的就是内部I2C控制器,当向I2C核心层注册一个I2C适配器时就需要提供这样的一个结构体变量。它的定义在 include/linux/i2c.h 文件,如下:
/* * i2c_adapter is the structure used to identify a physical i2c bus along * with the access algorithms necessary to access it. */ struct i2c_adapter { struct module *owner; unsigned int class; /* classes to allow probing for */ const struct i2c_algorithm *algo; /* the algorithm to access the bus */ void *algo_data; /* data fields that are valid for all devices */ const struct i2c_lock_operations *lock_ops; struct rt_mutex bus_lock; struct rt_mutex mux_lock; int timeout; /* in jiffies */ int retries; struct device dev; /* the adapter device */ unsigned long locked_flags; /* owned by the I2C core */ #define I2C_ALF_IS_SUSPENDED 0 #define I2C_ALF_SUSPEND_REPORTED 1 int nr; char name[48]; struct completion dev_released; struct mutex userspace_clients_lock; struct list_head userspace_clients; struct i2c_bus_recovery_info *bus_recovery_info; const struct i2c_adapter_quirks *quirks; struct irq_domain *host_notify_domain; };
其中部分参数含义如下:
- algo:适配器与从设备的通信算法,即访问I2C总线的算法;
- class:适配器支持的从设备的类型;
- I2C_CLASS_HWMON:硬件监控类;
- I2C_CLASS_DDC:数字显示通道,通常用于显示设备;
- I2C_CLASS_SPD:内存设备;
- I2C_CLASS_DEPRECATED:已废弃;不在使用的class;如果设置为这个,表示该I2C控制将不支持自动检测I2C从设备,即不再支持i2c_detect(这个后面会介绍);
- ...
- bus_lock:控制并发访问的互斥锁;
- timeout:超时时间;为I2C控制器发送一次消息所需的最长时间,包括重传次数所消耗的总时间,如果在timeout时间内没有完成发送,则表示这次消息发送失败;
- reties:重试次数;比如I2C控制器在传输过程中由于仲裁丢失、总线忙等情况而发送失败,则会根据retries的值来规定最大的重传次数;
- dev:设备驱动模型中的设备,可以把i2c_adapter看做是device的子类;
- bus为i2c_bus_type;
- type为i2c_adapter_type :指定了适配器的类型,该类型中也定义了专有的属性文件;
- kobj.name(设备名称)为i2c-%d:指定在/sys/devices/platform/s3c2440-i2c.0目录下生成的适配器目录名;
- nr:适配器的ID号;即I2C控制器的ID号,用来区分是属于SOC的第几个I2C控制器;
- name:适配器名称;即I2C控制器的名字;
- dev_released:用来实现同步,实现的机制为complete,作用跟信号量一样,只不过该信号解决了信号量在多进程操作中的一些缺点,在I2C控制器销毁过程中执行;
- userspace_client:client链表头;专门存储i2c_client类型的设备,这些设备也就是像EEPROM、MPU6050这类的I2C设备。在设备树中定义了很多 IIC 设备,将来都会被转换成 i2c_client 类型的结构体,但是userspace_clients并不链接这些 I2C 设备。userspace_clients链接的是我们在应用层通过访问 I2C 控制器的属性文件创建的 I2C 设备;
- userspace_clients_lock:就是在向链表添加过程中使用的互斥锁;
3.1.1 struct i2c_algorithm
algo是struct i2c_adatper中的核心成员,它包含了操作SOC I2C 控制器的函数集,也就是直接对接到实际的 SOC 的 I2C 控制器的操作(寄存器配置)。
algo类型为struct i2c_algorithm,定义在 include/linux/i2c.h 文件,如下:
/** * struct i2c_algorithm - represent I2C transfer method * @master_xfer: Issue a set of i2c transactions to the given I2C adapter * defined by the msgs array, with num messages available to transfer via * the adapter specified by adap. * @master_xfer_atomic: same as @master_xfer. Yet, only using atomic context * so e.g. PMICs can be accessed very late before shutdown. Optional. * @smbus_xfer: Issue smbus transactions to the given I2C adapter. If this * is not present, then the bus layer will try and convert the SMBus calls * into I2C transfers instead. * @smbus_xfer_atomic: same as @smbus_xfer. Yet, only using atomic context * so e.g. PMICs can be accessed very late before shutdown. Optional. * @functionality: Return the flags that this algorithm/adapter pair supports * from the I2C_FUNC_* flags. * @reg_slave: Register given client to I2C slave mode of this adapter * @unreg_slave: Unregister given client from I2C slave mode of this adapter * * The following structs are for those who like to implement new bus drivers: * i2c_algorithm is the interface to a class of hardware solutions which can * be addressed using the same bus algorithms - i.e. bit-banging or the PCF8584 * to name two of the most common. * * The return codes from the @master_xfer{_atomic} fields should indicate the * type of error code that occurred during the transfer, as documented in the * Kernel Documentation file Documentation/i2c/fault-codes. */ 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 (*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); #if IS_ENABLED(CONFIG_I2C_SLAVE) int (*reg_slave)(struct i2c_client *client); int (*unreg_slave)(struct i2c_client *client); #endif };
其中部分参数含义如下:
- master_xfer:为I2C控制器的消息传递函数,最终驱动层调用的i2c_transfer函数,就是将master_xfer进行一层封装。该函数返回成功处理的消息数,返回负数表示错误;
- functionality:获取I2C控制器支持的功能,比如是否支持 I2C 总线或者 SMBUS;返回一个能力集,高速调用者该I2C适配器支持的功能:
- I2C_FUNC_I2C:支持传统的I2C功能;
- I2C_FUNC_10BIT_ADDR:支持10位地址;
- I2C_FUNC_PROTOCOL_MANGLING:支持非标准的协议行为;
- I2C_FUNC_NOSTART:支持不需要发送START信号的I2C传输;
- I2C_FUNC_SMBUS_xzxx:SMBUS相关的功能;
- .......
这两个参数都为函数指针,可以根据不同的 SOC 的 I2C 控制器编写相应的通信方法。
3.1.2 struct device
struct deivce这个结构已经很熟悉了,linux 系统中每一个设备都用 device 结构的一个实例来表示。i2c_adapter.dev部分成员会初始化为:
struct device dev // 适配器设备 { struct bus_type *bus = &i2c_bus_type; struct device_type *type = &i2c_adapter_type; struct kobject kobj.name = "i2c-%d";
struct device parent = &pdev->dev ; // 一般设置为platform设备dev ... }
i2c_bus_type为I2C总线,上面已经介绍过了。
i2c_adapter_type 表示该设备属于 i2c_adapter 设备类型,该结构体中包含了 i2c_adapter 中专有的属性文件,当设备注册成功后,将会在设备文件中生成相应的属性文件,这样就可以通过sysfs接口访问属性文件从而操作 I2C 设备。
kobj.name为i2c-%d:指定在/sys/devices/platform/s3c2440-i2c.0目录下生成的文件夹名称;
3.2 struct i2c_board_info
struct i2c_board_info描述的是具体的I2C从设备,定义在include/linux/i2c.h文件中,如下:
/** * struct i2c_board_info - template for device creation * @type: chip type, to initialize i2c_client.name * @flags: to initialize i2c_client.flags * @addr: stored in i2c_client.addr * @dev_name: Overrides the default <busnr>-<addr> dev_name if set * @platform_data: stored in i2c_client.dev.platform_data * @of_node: pointer to OpenFirmware device node * @fwnode: device node supplied by the platform firmware * @properties: additional device properties for the device * @resources: resources associated with the device * @num_resources: number of resources in the @resources array * @irq: stored in i2c_client.irq * * I2C doesn't actually support hardware probing, although controllers and * devices may be able to use I2C_SMBUS_QUICK to tell whether or not there's * a device at a given address. Drivers commonly need more information than * that, such as chip type, configuration, associated IRQ, and so on. * * i2c_board_info is used to build tables of information listing I2C devices * that are present. This information is used to grow the driver model tree. * For mainboards this is done statically using i2c_register_board_info(); * bus numbers identify adapters that aren't yet available. For add-on boards, * i2c_new_device() does this dynamically with the adapter already known. */ struct i2c_board_info { char type[I2C_NAME_SIZE]; unsigned short flags; unsigned short addr; const char *dev_name; void *platform_data; struct device_node *of_node; struct fwnode_handle *fwnode; const struct property_entry *properties; const struct resource *resources; unsigned int num_resources; int irq; };
其中部分参数含义如下:
- type:I2C从设备名称,后面I2C从设备驱动和I2C设备匹配时会用到,直接决定了I2C设备驱动能否匹配上进而进入probe探测函数;编写I2C设备驱动需要配置的参数;
- flags:标志;
- I2C_CLIENT_TEN:表示设备使用十位芯片地址;
- I2C_CLIENT_PEC:表示它使用SMBus数据包错误检查;
- I2C_CLIENT_SLAVA:从设备;
- I2C_CLIENT_WAK:需要唤醒以启用节能功能时使用;
- I2C_CLIENT_SCCB;
- I2C_CLIENT_HOST_NOTIFY;
- addr:从设备地址,7位地址不包含读写位,比如AT24C08地址为0x50;编写I2C设备驱动需要配置的参数;
- dev_name:设备名称;
- platform_data:在开发过程中,可用来存储一些数据;
- of_node:从设备设备节点;
- resources:资源,该设备的资源描述,指向一个资源数组;编写I2C从设备驱动一般不需要配置这个;
- num_resource:资源的数量;编写I2C从设备驱动一般不需要配置这个;
- irq:中断编号;编写I2C从设备驱动一般不需要配置这个;
i2c_board_info 中的大部分成员都是通过解析设备树获得,而i2c_board_info 将来会被转换成 i2c_client 类型,i2c_client 和 i2c_bard_info 内部很多成员都是相似的,i2c_client 中的很多成员变量值都继承于i2c_board_info。
- type 将初始化 i2c_client.name,最后会与I2C从设备驱动中i2c_device_id的 name 做匹配,直接决定了I2C设备驱动能否匹配上进而进入probe探测函数;
- addr 将初始化 i2c_client.addr;
- flags 将初始化 i2c_client.flags;
- platform_data 将初始化 i2c_client.dev.platform_data;
- irq 将初始化 i2c_client.irq 。
3.2.1 i2c_register_board_info
在编写I2C设备驱动时,我们可以定义一个i2c_board_info 结构体,在结构体中包含有名字和设备地址,根据这些信息来构造一个i2c_client。
i2c_register_board_info函数定义如下:
/** * i2c_register_board_info - statically declare I2C devices * @busnum: identifies the bus to which these devices belong * @info: vector of i2c device descriptors * @len: how many descriptors in the vector; may be zero to reserve * the specified bus number. * * Systems using the Linux I2C driver stack can declare tables of board info * while they initialize. This should be done in board-specific init code * near arch_initcall() time, or equivalent, before any I2C adapter driver is * registered. For example, mainboard init code could define several devices, * as could the init code for each daughtercard in a board stack. * * The I2C devices will be created later, after the adapter for the relevant * bus has been registered. After that moment, standard driver model tools * are used to bind "new style" I2C drivers to the devices. The bus number * for any device declared using this routine is not available for dynamic * allocation. * * The board info passed can safely be __initdata, but be careful of embedded * pointers (for platform_data, functions, etc) since that won't be copied. * Device properties are deep-copied though. */ int i2c_register_board_info(int busnum, struct i2c_board_info const *info, unsigned len) // 第一个参数为I2C适配器编号 { int status; down_write(&__i2c_board_lock); /* dynamic bus numbers will be assigned after the last static one */ if (busnum >= __i2c_first_dynamic_bus_num) __i2c_first_dynamic_bus_num = busnum + 1; for (status = 0; len; len--, info++) { struct i2c_devinfo *devinfo; devinfo = kzalloc(sizeof(*devinfo), GFP_KERNEL); if (!devinfo) { pr_debug("i2c-core: can't register boardinfo!\n"); status = -ENOMEM; break; } devinfo->busnum = busnum; // 设置I2C适配器编号 devinfo->board_info = *info; // 保存I2C从设备信息 if (info->properties) { devinfo->board_info.properties = property_entries_dup(info->properties); if (IS_ERR(devinfo->board_info.properties)) { status = PTR_ERR(devinfo->board_info.properties); kfree(devinfo); break; } } if (info->resources) { devinfo->board_info.resources = kmemdup(info->resources, info->num_resources * sizeof(*info->resources), GFP_KERNEL); if (!devinfo->board_info.resources) { status = -ENOMEM; kfree(devinfo); break; } } list_add_tail(&devinfo->list, &__i2c_board_list); // 将当前元素添加到链表 } up_write(&__i2c_board_lock); return status; }
这里仅仅是将i2c_board_info追加到__i2c_board_list链表。当我们去注册一个I2C适配器时,会调用i2c_scan_static_board_info函数:
static void i2c_scan_static_board_info(struct i2c_adapter *adapter) // 扫描i2c_board_info链表, { struct i2c_devinfo *devinfo; down_read(&__i2c_board_lock); list_for_each_entry(devinfo, &__i2c_board_list, list) { if (devinfo->busnum == adapter->nr // I2C从设备如果是连接在该I2C适配器上 && !i2c_new_device(adapter, // I2C从设备注册 &devinfo->board_info)) dev_err(&adapter->dev, "Can't create device at 0x%02x\n", devinfo->board_info.addr); } up_read(&__i2c_board_lock); }
在该函数内部遍历__i2c_board_list链表,对链表的每一个成员都调用一个i2c_new_device,通过i2c_new_device构造一个i2c_client。
因此这种方法有个限制,使用前,必须在i2c_register_adapter之前调用 i2c_register_board_info注册i2c_board_info。
3.3 struct i2c_client
struct i2c_client抽象了连接到I2C总线上的I2C从设备,表示从设备的最终形态, 包含与从设备关联的完整信息。它内部成员的值很多来自于 i2c_board_info。它的定义在 include/linux/i2c.h 文件,如下:
/** * struct i2c_client - represent an I2C slave device * @flags: I2C_CLIENT_TEN indicates the device uses a ten bit chip address; * I2C_CLIENT_PEC indicates it uses SMBus Packet Error Checking * @addr: Address used on the I2C bus connected to the parent adapter. * @name: Indicates the type of the device, usually a chip name that's * generic enough to hide second-sourcing and compatible revisions. * @adapter: manages the bus segment hosting this I2C device * @dev: Driver model device node for the slave. * @irq: indicates the IRQ generated by this device (if any) * @detected: member of an i2c_driver.clients list or i2c-core's * userspace_devices list * @slave_cb: Callback when I2C slave mode of an adapter is used. The adapter * calls it to pass on slave events to the slave driver. * * An i2c_client identifies a single device (i.e. chip) connected to an * i2c bus. The behaviour exposed to Linux is defined by the driver * managing the device. */ struct i2c_client { unsigned short flags; /* div., see below */ unsigned short addr; /* chip address - NOTE: 7bit */ /* addresses are stored in the */ /* _LOWER_ 7 bits */ char name[I2C_NAME_SIZE]; struct i2c_adapter *adapter; /* the adapter we sit on */ struct device dev; /* the device structure */ int init_irq; /* irq set at initialization */ int irq; /* irq issued by device */ struct list_head detected; #if IS_ENABLED(CONFIG_I2C_SLAVE) i2c_slave_cb_t slave_cb; /* callback for slave mode */ #endif };
其中部分参数含义如下:
- flags:标志;
- I2C_CLIENT_TEN:表示设备使用10位设备地址;
- I2C_CLIENT_PEC:表示它使用SMBus数据包错误检查;
- I2C_CLIENT_SLAVA:从设备;
- I2C_CLIENT_WAK:需要唤醒以启用节能功能时使用;
- I2C_CLIENT_SCCB;
- I2C_CLIENT_HOST_NOTIFY;
- addr:I2C从设备地址,7位地址不包含读写位,比如AT24C08地址为0x50;
- name:设备名称,后面I2C从设备驱动和I2C设备匹配时会用到,直接决定了I2C设备驱动能否匹配上进而进入probe探测函数;
- adapter:I2C从设备连接到SOC的I2C控制器;
- dev:设备驱动模型中的设备,可以把i2c_client看做是device的子类;
- parent为&client->adapter->dev,建立从设备与I2C控制器的父子关系;这样设备注册时将会在/sys/devices/platform/s3c2440-i2c.0/i2c-0目录下生成从设备目录;
- bus为i2c_bus_type;
- type为i2c_client_type:指定了从设备的类型,该类型中也定义了从设备专有的属性文件;
- kobj.name(设备名称)为%d-%04x,第一个数字为I2C适配器编号,第二个数字为从设备地址:指定在/sys/devices/platform/s3c2440-i2c.0/i2c-0目录下生成的从设备目录名;
- init_irq:初始化时设置的中断编号;
- irq:I2C从设备所使用的的中断编号;
- detected:用于构建双向链表,在应用层创建的 I2C 从设备就是通过该成员链接到i2c_adapter中的userspace_clients;
3.4 struct i2c_devinfo
struct i2c_devinfo 和 struct i2c_board_info 类似,同样用于描述具体的 I2C 从设备,它的定义在drivers/i2c/i2c-core.h文件,如下:
struct i2c_devinfo { struct list_head list; // 用于构建双向链表 int busnum; // I2C控制器编号 struct i2c_board_info board_info; };
i2c_devinfo 中包含了 i2c_board_info,那么这两者有什么联系呢?
- 其实 i2c_devinfo 在如今采用设备树的内核驱动已经很少被用到,在还没有设备树时,都是开发人员自己去定义一个 i2c_devinfo ,然后通过 i2c_register_board_info 函数注册到 i2c_board_list 总链表上;
- 最后在注册 I2C 控制器的时候,遍历 i2c_board_list 总链表,将上面挂接的每个 i2c_devinfo 转换为 i2c_client;
- 然而在如今设备树的内核版本,都是解析设备树生成 i2c_board_info,然后把 i2c_board_info 转换为 i2c_client。
busnum表示该从设备是属于SOC哪一个 I2C控制器,list将被挂接到 i2c_board_list 总链表上。
3.5 struct i2c_msg
struct i2c_msg描述一个I2C传输的数据,定义在 include/uapi/linux/i2c.h文件,如下:
/** * struct i2c_msg - an I2C transaction segment beginning with START * @addr: Slave address, either seven or ten bits. When this is a ten * bit address, I2C_M_TEN must be set in @flags and the adapter * must support I2C_FUNC_10BIT_ADDR. * @flags: I2C_M_RD is handled by all adapters. No other flags may be * provided unless the adapter exported the relevant I2C_FUNC_* * flags through i2c_check_functionality(). * @len: Number of data bytes in @buf being read from or written to the * I2C slave address. For read transactions where I2C_M_RECV_LEN * is set, the caller guarantees that this buffer can hold up to * 32 bytes in addition to the initial length byte sent by the * slave (plus, if used, the SMBus PEC); and this value will be * incremented by the number of block data bytes received. * @buf: The buffer into which data is read, or from which it's written. * * An i2c_msg is the low level representation of one segment of an I2C * transaction. It is visible to drivers in the @i2c_transfer() procedure, * to userspace from i2c-dev, and to I2C adapter drivers through the * @i2c_adapter.@master_xfer() method. * * Except when I2C "protocol mangling" is used, all I2C adapters implement * the standard rules for I2C transactions. Each transaction begins with a * START. That is followed by the slave address, and a bit encoding read * versus write. Then follow all the data bytes, possibly including a byte * with SMBus PEC. The transfer terminates with a NAK, or when all those * bytes have been transferred and ACKed. If this is the last message in a * group, it is followed by a STOP. Otherwise it is followed by the next * @i2c_msg transaction segment, beginning with a (repeated) START. * * Alternatively, when the adapter supports I2C_FUNC_PROTOCOL_MANGLING then * passing certain @flags may have changed those standard protocol behaviors. * Those flags are only for use with broken/nonconforming slaves, and with * adapters which are known to support the specific mangling options they * need (one or more of IGNORE_NAK, NO_RD_ACK, NOSTART, and REV_DIR_ADDR). */ struct i2c_msg { __u16 addr; /* slave address */ __u16 flags; #define I2C_M_RD 0x0001 /* read data, from slave to master */ /* I2C_M_RD is guaranteed to be 0x0001! */ #define I2C_M_TEN 0x0010 /* this is a ten bit chip address */ #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 /* if I2C_FUNC_PROTOCOL_MANGLING */ #define I2C_M_IGNORE_NAK 0x1000 /* if I2C_FUNC_PROTOCOL_MANGLING */ #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; /* msg length */ __u8 *buf; /* pointer to msg data */ };
I2C传输数据都是以i2c_msg类型为一个数据包,参数:
- addr:从设备地址;7位地址,比如AT24C08地址为0x50;
- flags:标志位;
- I2C_M_RD:此次传输是读操作;
- I2C_M_REV_DIR_ADDR:将读写flag翻转,即读的时候发W信号,写的时候发R信号;
- I2C_M_IGNORE_NAK:写操作的时候,忽略从设备返回的NA;
- I2C_M_NO_RD_ACK:读操作的时候,忽略所有的NACK/ACK信号;
- I2C_M_NOSTART: 读写混合操作的情况下,假如要传输多个msg(以2个为例),如果第二个msg携带了该标志,则不再发送起始信号和从设备地址;
- I2C_M_RECV_LEN: SMBUS的一个flag;
- ....
- buf:发送或接收的数据;需要注意,如果是发送数据,buf里存放需要发送的数据即可,不包含从设备地址;
- len:发送或接收的数据长度;
3.6 struct i2c_driver
struct i2c_driver描述一个I2C设备驱动,定义在 include/linux/i2c.h文件,如下:
/** * struct i2c_driver - represent an I2C device driver * @class: What kind of i2c device we instantiate (for detect) * @probe: Callback for device binding - soon to be deprecated * @probe_new: New callback for device binding * @remove: Callback for device unbinding * @shutdown: Callback for device shutdown * @alert: Alert callback, for example for the SMBus alert protocol * @command: Callback for bus-wide signaling (optional) * @driver: Device driver model driver * @id_table: List of I2C devices supported by this driver * @detect: Callback for device detection * @address_list: The I2C addresses to probe (for detect) * @clients: List of detected clients we created (for i2c-core use only) * @disable_i2c_core_irq_mapping: Tell the i2c-core to not do irq-mapping * * The driver.owner field should be set to the module owner of this driver. * The driver.name field should be set to the name of this driver. * * For automatic device detection, both @detect and @address_list must * be defined. @class should also be set, otherwise only devices forced * with module parameters will be created. The detect function must * fill at least the name field of the i2c_board_info structure it is * handed upon successful detection, and possibly also the flags field. * * If @detect is missing, the driver will still work fine for enumerated * devices. Detected devices simply won't be supported. This is expected * for the many I2C/SMBus devices which can't be detected reliably, and * the ones which can always be enumerated in practice. * * The i2c_client structure which is handed to the @detect callback is * not a real i2c_client. It is initialized just enough so that you can * call i2c_smbus_read_byte_data and friends on it. Don't do anything * else with it. In particular, calling dev_dbg and friends on it is * not allowed. */ struct i2c_driver { unsigned int class; /* Standard driver model interfaces */ int (*probe)(struct i2c_client *, const struct i2c_device_id *); int (*remove)(struct i2c_client *); /* New driver model interface to aid the seamless removal of the * current probe()'s, more commonly unused than used second parameter. */ int (*probe_new)(struct i2c_client *); /* driver model interfaces that don't relate to enumeration */ void (*shutdown)(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"). * For the SMBus Host Notify protocol, the data corresponds to the * 16-bit payload data reported by the slave device acting as master. */ void (*alert)(struct i2c_client *, enum i2c_alert_protocol protocol, 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; bool disable_i2c_core_irq_mapping; };
其中部分参数含义如下:
- class:表示当前从设备驱动使用的适配器的类型,它将与上面 i2c_adapter的class进行匹配判断;
- probe:当驱动和设备信息匹配成功之后,就会调用probe函数,驱动所有的资源的注册和初始化全部放在probe函数中;
- remove:设备被移除了,或者驱动被卸载了,全部要释放,释放资源的操作就放在该函数中;
- driver:驱动基类,内核维护的所有的驱动必须包含该成员;
- bus为i2c_bus_type;
- id_table:往往一个驱动可能能同时支持多个硬件,这些硬件的名字都放在该结构体数组中;
- address_list:指向从设备地址表,该表存放了需要创建的从设备地址,该表由开发人员自己定义;
如果是有新的适配器注册,将会遍历I2C 总线上的所有I2C驱动,将新的i2c_adapter中的class成员与遍历到的i2c_driver的class成员匹配:
- 如果匹配成功,将会使用i2c_detect_address函数来确定能否找到address_list里的从设备,如果找到了,则会回调detect方法,并会自动注册新的i2c_client从设备。
其中clients就是专门链接此过程中创建的i2c_client从设备。
如果是有新的I2C设备驱动注册,将会遍历I2C总线上的所有适配器,查看有没有适配器与自身i2c_driver的class成员匹配,如果有,则与上面说的过程一样。
这里为什么要提供一个2C从设备自动探测的功能的目的是什么呢?比如在一些场景下,一个SOC可能存在多个I2C控制器,同时连接了多个I2C从设备,在不知道I2C从设备分别连接到哪个I2C控制器上的时候,我们就可以使用这种方法。当我们去注册一个I2C适配器或I2C设备驱动驱动时就会触发这个自动探测的流程,根据我们驱动中的地址,去判断I2C设备是否存在,如果存在,就自动将注册发现的I2C从设备,这样就可以省去我们手动注册I2C从设备的过程。
需要注意的是,当I2C适配器开启了自动探测时,将会调用i2c_detect_address去判断从设备地址的合法性(包括地址校验,通过I2C通信是否有响应等手段),这部分代码内核已经帮我们实现了,我们要做的只是在从设备探测成功后,在detect回调函数中进行一些初始化工作,比如必须初始化i2c_board_info的type成员。
3.7 数据结构关系图
下图展示了i2c_client、i2c_driver、i2c_adapter、i2c_bus_type以及相关注册函数之间的关系:
四、I2C框架API
I2C框架对外提供的API几乎都可以在include/linux/i2c.h中找,其实现代码一般都位于drivers/i2c/i2c-core-base.c文件,主要都是围着上面数据结构进行的。
4.1 I2C子系统初始化
I2C子系统的初始化函数i2c_init,定义在drivers/i2c/i2c-core-base.c文件中:
static int __init i2c_init(void) { int retval; retval = of_alias_get_highest_id("i2c"); // 获取SOC中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); // I2C总线注册,这个上面已经介绍过了,会sys文件系统创建/sys/bus/i2c目录以及相关文件 if (retval) return retval; is_registered = true; // 标志位 #ifdef CONFIG_I2C_COMPAT i2c_adapter_compat_class = class_compat_register("i2c-adapter"); // class注册 会在sys文件系统创建/sys/class/i2c-adapter目录 if (!i2c_adapter_compat_class) { retval = -ENOMEM; goto bus_err; } #endif retval = i2c_add_driver(&dummy_driver); // 注册dummy_driver驱动 if (retval) goto class_err; if (IS_ENABLED(CONFIG_OF_DYNAMIC)) // 使能设备树 WARN_ON(of_reconfig_notifier_register(&i2c_of_notifier)); if (IS_ENABLED(CONFIG_ACPI)) // 使用CPI WARN_ON(acpi_reconfig_notifier_register(&i2c_acpi_notifier)); return 0; class_err: #ifdef CONFIG_I2C_COMPAT class_compat_unregister(i2c_adapter_compat_class); bus_err: #endif is_registered = false; bus_unregister(&i2c_bus_type); return retval; }
4.2 I2C适配器注册
下图显示了注册I2C适配器设备和检测I2C总线上I2C设备的过程:
当有新的适配器注册,将会遍历I2C总线上的所有设备驱动,将新的i2c_adapter中的class成员与遍历到的i2c_driver的class成员匹配:如果匹配成功,将会调用i2c_detect_address函数(在该函数中使用当前i2c_adapter发送起始信号和从设备地址并得到ACK响应来判定从设备是否存在)来确定能否找到address_list里的从设备,如果找到了,则将自动注册新的i2c_client从设备。
i2c_add_adapter用于I2C适配器注册,定义在drivers/i2c/i2c-core-base.c文件中:
/** * i2c_add_adapter - declare i2c adapter, use dynamic bus number * @adapter: the adapter to add * Context: can sleep * * This routine is used to declare an I2C adapter when its bus number * doesn't matter or when its bus number is specified by an dt alias. * Examples of bases when the bus number doesn't matter: I2C adapters * dynamically added by USB links or PCI plugin cards. * * When this returns zero, a new bus number was allocated and stored * in adap->nr, and the specified adapter became available for clients. * Otherwise, a negative errno value is returned. */ int i2c_add_adapter(struct i2c_adapter *adapter) { struct device *dev = &adapter->dev; // 获取适配器device对象 int id; if (dev->of_node) { // 设备节点 id = of_alias_get_id(dev->of_node, "i2c"); // 如果设备树中存在i2c节点,并且指定了id,则使用该编号注册适配器 if (id >= 0) { adapter->nr = id; return __i2c_add_numbered_adapter(adapter); //在i2c_adapter_idr树中新增节点,节点ID为id } } mutex_lock(&core_lock); // 获取互斥锁 id = idr_alloc(&i2c_adapter_idr, adapter, // 为i2c_adapter_idr树新增一个节点,该节点在i2c_adapter_idr树中有唯一的节点ID,并且将这个节点和adapter关联,通过id就可以定位到这个adapter __i2c_first_dynamic_bus_num, 0, GFP_KERNEL); mutex_unlock(&core_lock); // 释放互斥锁 if (WARN(id < 0, "couldn't get idr")) return id; adapter->nr = id; return i2c_register_adapter(adapter); // 注册I2C适配器 }
从上面我们可以看到i2c_add_adapter与i2c_register_adapter唯一的区别在于,i2c_add_adapter为I2C适配器分配了一个唯一的编号。
4.2.1 i2c_register_adapter
i2c_register_adapter用于I2C适配器注册,定义在drivers/i2c/i2c-core-base.c文件中:
static int i2c_register_adapter(struct i2c_adapter *adap) { int res = -EINVAL; /* Can't register until after driver model init */ if (WARN_ON(!is_registered)) { // i2c_init()执行前,都是未注册状态 res = -EAGAIN; goto out_list; } /* Sanity checks */ if (WARN(!adap->name[0], "i2c adapter has no name")) // 未指定I2C适配器名称 goto out_list; if (!adap->algo) { // 未指定I2C算法 pr_err("adapter '%s': no algo supplied!\n", adap->name); goto out_list; } if (!adap->lock_ops) // 未初始化lock_ops adap->lock_ops = &i2c_adapter_lock_ops; adap->locked_flags = 0; rt_mutex_init(&adap->bus_lock); // 初始化各种锁 rt_mutex_init(&adap->mux_lock); mutex_init(&adap->userspace_clients_lock); INIT_LIST_HEAD(&adap->userspace_clients); // 初始化双向链表 /* Set default timeout to 1 second if not already set */ if (adap->timeout == 0) // 设定超时时间,默认为1s adap->timeout = HZ; /* register soft irqs for Host Notify */ res = i2c_setup_host_notify_irq_domain(adap); // 注册软中断 if (res) { pr_err("adapter '%s': can't create Host Notify IRQs (%d)\n", adap->name, res); goto out_list; } // 初始化适配器device成员 dev_set_name(&adap->dev, "i2c-%d", adap->nr); // 设定适配器dev.kobj.name为i2c-编号 adap->dev.bus = &i2c_bus_type; adap->dev.type = &i2c_adapter_type; res = device_register(&adap->dev); // 注册设备 会在sys文件系统下创建/sys/devices/platform/s3c2440-i2c.0/i2c-0目录,并创建链接文件/sys/bus/i2c/devices/i2c-0指向该目录 if (res) { pr_err("adapter '%s': can't register device (%d)\n", adap->name, res); goto out_list; } res = of_i2c_setup_smbus_alert(adap); if (res) goto out_reg; dev_dbg(&adap->dev, "adapter [%s] registered\n", adap->name); pm_runtime_no_callbacks(&adap->dev); pm_suspend_ignore_children(&adap->dev, true); pm_runtime_enable(&adap->dev); #ifdef CONFIG_I2C_COMPAT res = class_compat_create_link(i2c_adapter_compat_class, &adap->dev, // 在sys文件系统创建链接 /sys/class/i2c-adapter/i2c-0指向/sys/devices/platform/s3c2440-i2c.0/i2c.0 adap->dev.parent); if (res) dev_warn(&adap->dev, "Failed to create compatibility class link\n"); #endif i2c_init_recovery(adap); /* create pre-declared device nodes */ of_i2c_register_devices(adap); // 通过设备树节点注册该控制器下的所有从设备 i2c_acpi_register_devices(adap); //通过mach文件注册该控制器下的所有从设备 i2c_acpi_install_space_handler(adap); if (adap->nr < __i2c_first_dynamic_bus_num) i2c_scan_static_board_info(adap); // 扫描i2c_board_info链表,注册I2C从设备 /* Notify drivers */ mutex_lock(&core_lock); bus_for_each_drv(&i2c_bus_type, NULL, adap, __process_new_adapter); //遍历i2c总线i2c_bus_type驱动链表上的每个驱动,对每个驱动调用__process_new_adapter mutex_unlock(&core_lock); return 0; out_reg: init_completion(&adap->dev_released); device_unregister(&adap->dev); wait_for_completion(&adap->dev_released); out_list: mutex_lock(&core_lock); idr_remove(&i2c_adapter_idr, adap->nr); mutex_unlock(&core_lock); return res; }
4.2.2 device_register
device_register函数主要做了以下三件事情:
- 在sys文件系统下创建/sys/devices/platform/s3c2440-i2c.0/i2c-0目录;创建链接文件/sys/bus/i2c/devices/i2c-0指向/sys/devices/platform/s3c2440-i2c.0/i2c-0;
- 将I2C适配器设备挂载到I2C总线i2c_bus_type的设备链表上;
- 遍历I2C总线i2c_bus_type的驱动链表中每个I2C驱动,如果匹配将会指向驱动的probe函数;
4.2.3 of_i2c_register_devices
of_i2c_register_devices定义在drivers/i2c/i2c-core-of.c,通过设备树节点注册该控制器下的所有从设备;
void of_i2c_register_devices(struct i2c_adapter *adap) { struct device_node *bus, *node; struct i2c_client *client; /* Only register child devices if the adapter has a node pointer set */ if (!adap->dev.of_node) // 如果没有使用设备树,直接返回 return; dev_dbg(&adap->dev, "of_i2c: walking child nodes\n"); bus = of_get_child_by_name(adap->dev.of_node, "i2c-bus"); // 如果使用设备树,则获取设备节点i2c@54000000子节点i2c-bus if (!bus) // i2c-bus节点不存在,则进入 bus = of_node_get(adap->dev.of_node); // 获取平台设备节点,即设备节点i2c@54000000 for_each_available_child_of_node(bus, node) { // 遍历总线节点下的子设备节点,从而达到注册每一个子设备的作用 if (of_node_test_and_set_flag(node, OF_POPULATED)) continue; client = of_i2c_register_device(adap, node); // 注册I2C从设备 if (IS_ERR(client)) { dev_err(&adap->dev, "Failed to create I2C device for %pOF\n", node); of_node_clear_flag(node, OF_POPULATED); } } of_node_put(bus); }
如果使用了设备树,这里实际上就是解析I2C控制器设备节点的子设备节点,从而调用of_i2c_register_device完成I2C从设备的注册,比如 i2c@54000000设备节点:
i2c@54000000 { compatible = "samsung,s3c2440-i2c"; clocks = <&clocks PCLK_I2C>; clock-names = "i2c"; pinctrl-names = "default"; pinctrl-0 = <&i2c0_bus>; status = "okay"; samsung,i2c-max-bus-freq = <200000>; eeprom@50 { compatible = "24c02"; reg = <0x50>; pagesize = <32>; status = "okay"; }; };
of_i2c_register_device函数定义在drivers/i2c/i2c-core-of.c:
static struct i2c_client *of_i2c_register_device(struct i2c_adapter *adap, // I2C控制器 struct device_node *node) // I2C从设备节点,比如eeprom@50 { struct i2c_client *client; struct i2c_board_info info; int ret; dev_dbg(&adap->dev, "of_i2c: register %pOF\n", node); ret = of_i2c_get_board_info(&adap->dev, node, &info); if (ret) return ERR_PTR(ret); client = i2c_new_device(adap, &info); // 注册I2C从设备 if (!client) { dev_err(&adap->dev, "of_i2c: Failure registering %pOF\n", node); return ERR_PTR(-EINVAL); } return client; }
首先调用of_i2c_get_board_info解析I2C从设备节点,从而初始化info成员;
int of_i2c_get_board_info(struct device *dev, struct device_node *node, // I2C从设备节点,比如eeprom@50 struct i2c_board_info *info) // 保存I2C从设备信息 { u32 addr; int ret; memset(info, 0, sizeof(*info));
// 找到i2c控制器的子节点的compatible字段,如果有",",取","后面的字段,否则取compatible字段的值,因此这里最终把"24c02"返回,赋值在info.type变量中 if (of_modalias_node(node, info->type, sizeof(info->type)) < 0) { dev_err(dev, "of_i2c: modalias failure on %pOF\n", node); return -EINVAL; } ret = of_property_read_u32(node, "reg", &addr); // 获取reg属性值,比如0x50,即I2C从设备地址(位[0-6],不包含读写位) if (ret) { dev_err(dev, "of_i2c: invalid reg on %pOF\n", node); return ret; } if (addr & I2C_TEN_BIT_ADDRESS) { // 宏的值为1<<31 10位设备地址 addr &= ~I2C_TEN_BIT_ADDRESS; info->flags |= I2C_CLIENT_TEN; } if (addr & I2C_OWN_SLAVE_ADDRESS) { // 宏的值1<<30 addr &= ~I2C_OWN_SLAVE_ADDRESS; info->flags |= I2C_CLIENT_SLAVE; } info->addr = addr; // 设置I2C从设备地址 info->of_node = node; // I2C设备设备节点 if (of_property_read_bool(node, "host-notify")) info->flags |= I2C_CLIENT_HOST_NOTIFY; if (of_get_property(node, "wakeup-source", NULL)) info->flags |= I2C_CLIENT_WAKE; return 0; }
然后调用i2c_new_device注册I2C从设备,这个函数后面介绍。
4.2.4 bus_for_each_drv
调用bus_for_each_drv遍历I2C总线i2c_bus_type的驱动链表中的每个I2C驱动,对每个驱动调用__process_new_adapter,告诉每个驱动有新的I2C适配器注册,该函数定义在drivers/base/bus.c,代码如下:
/** * bus_for_each_drv - driver iterator * @bus: bus we're dealing with. * @start: driver to start iterating on. * @data: data to pass to the callback. * @fn: function to call for each driver. * * This is nearly identical to the device iterator above. * We iterate over each driver that belongs to @bus, and call * @fn for each. If @fn returns anything but 0, we break out * and return it. If @start is not NULL, we use it as the head * of the list. * * NOTE: we don't return the driver that returns a non-zero * value, nor do we leave the reference count incremented for that * driver. If the caller needs to know that info, it must set it * in the callback. It must also be sure to increment the refcount * so it doesn't disappear before returning to the caller. */ int bus_for_each_drv(struct bus_type *bus, struct device_driver *start, void *data, int (*fn)(struct device_driver *, void *)) { struct klist_iter i; struct device_driver *drv; int error = 0; if (!bus) return -EINVAL; klist_iter_init_node(&bus->p->klist_drivers, &i, // 遍历bus的驱动链表 start ? &start->p->knode_bus : NULL); while ((drv = next_driver(&i)) && !error) error = fn(drv, data); klist_iter_exit(&i); return error; }
4.2.5 __process_new_adapter
__process_new_adapter函数定义在drivers/i2c/i2c-core-base.c文件中:
static int __process_new_adapter(struct device_driver *d, void *data) { return i2c_do_add_adapter(to_i2c_driver(d), data); } 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在当前适配器对应的的I2C总线上,进行I2C设备的检测。
4.2.6 i2c_detect
i2c_detect函数定义在/i2c/i2c-core-base.c文件:
static int i2c_detect(struct i2c_adapter *adapter, struct i2c_driver *driver) { const unsigned short *address_list; struct i2c_client *temp_client; int i, err = 0; int adap_id = i2c_adapter_id(adapter); address_list = driver->address_list; // 获取驱动中指定的地址列表 if (!driver->detect || !address_list) // 未指定detect、address_list 返回 return 0; /* Warn that the adapter lost class based instantiation */ if (adapter->class == I2C_CLASS_DEPRECATED) { // I2C控制将不支持自动检测I2C从设备 输出调试消息,并退出 dev_dbg(&adapter->dev, "This adapter dropped support for I2C classes and won't auto-detect %s devices anymore. " "If you need it, check 'Documentation/i2c/instantiating-devices' for alternatives.\n", driver->driver.name); return 0; } /* Stop here if the classes do not match */ if (!(adapter->class & driver->class)) // 适配器和I2C驱动所属class不匹配 return 0; /* Set up a temporary client to help detect callback */ temp_client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL); // 创建一个i2c_client if (!temp_client) return -ENOMEM; temp_client->adapter = adapter; // 指定其所属适配器 for (i = 0; address_list[i] != I2C_CLIENT_END; i += 1) { // 遍历地址列表 dev_dbg(&adapter->dev, "found normal entry for adapter %d, addr 0x%02x\n", adap_id, address_list[i]); temp_client->addr = address_list[i]; // 为其分配从设备地址 err = i2c_detect_address(temp_client, driver); // 检测从设备地址是否可用 if (unlikely(err)) break; } kfree(temp_client); return err; }
将新的i2c_adapter中的class成员与遍历到的i2c_driver的class成员匹配:如果匹配成功,用i2c_detect_address函数来确定能否找到address_list里的从设备。
4.2.7 i2c_detect_address
i2c_detect_address函数定义在drivers/i2c/i2c-core-base.c文件:
static int i2c_detect_address(struct i2c_client *temp_client, struct i2c_driver *driver) { struct i2c_board_info info; struct i2c_adapter *adapter = temp_client->adapter; int addr = temp_client->addr; int err; /* Make sure the address is valid */ err = i2c_check_7bit_addr_validity_strict(addr); // 检测目标地址,不能超出0x08~0x77范围 成功返回0 if (err) { dev_warn(&adapter->dev, "Invalid probe address 0x%02x\n", addr); return err; } /* Skip if already in use (7 bit, no need to encode flags) */ if (i2c_check_addr_busy(adapter, addr)) // 目标地址检查 好像是和mux有关,忽略即可 return 0; /* Make sure there is something at this address */ if (!i2c_default_probe(adapter, addr)) // 进行目标地址探测,通过I2C通信检查这个地址是否有回应 探测成功范围1,失败返回0 return 0; /* Finally call the custom detection function */ memset(&info, 0, sizeof(struct i2c_board_info)); // 初始化info,全部填充0 info.addr = addr; // 设置从设备地址 err = driver->detect(temp_client, &info); // 设备探测成功后的回调函数 if (err) { /* -ENODEV is returned if the detection fails. We catch it here as this isn't an error. */ return err == -ENODEV ? 0 : err; } /* Consistency check */ if (info.type[0] == '\0') { dev_err(&adapter->dev, "%s detection function provided no name for 0x%x\n", driver->driver.name, addr); } else { // 探测到I2C从设备,注册I2C从设备 struct i2c_client *client; /* Detection succeeded, instantiate the device */ if (adapter->class & I2C_CLASS_DEPRECATED) dev_warn(&adapter->dev, "This adapter will soon drop class based instantiation of devices. " "Please make sure client 0x%02x gets instantiated by other means. " "Check 'Documentation/i2c/instantiating-devices' for details.\n", info.addr); dev_dbg(&adapter->dev, "Creating %s at 0x%02x\n", info.type, info.addr); client = i2c_new_device(adapter, &info); // 注册I2C从设备 if (client) list_add_tail(&client->detected, &driver->clients); // 将当前I2C设备添加到driver->clients链表 else dev_err(&adapter->dev, "Failed creating %s at 0x%02x\n", info.type, info.addr); } return 0; }
在这个函数里面首先检查有效性、是否被使用、是否有设备回应,之后初始化了i2c_board_info结构,注意只初始化了地址(实例化设备必须还要名字),然后调用了i2c驱动中的detect函数,如果成功则调用 i2c_new_device函数真正实例化i2c设备,并且将i2c设备挂在i2c驱动的链表上。
仔细思考上面就能发现,I2C驱动中的detect函数必须要填写i2c_board_info结构体中type,i2c_new_device才能实例化i2c设备。
4.3 I2C从设备注册
i2c_new_device函数定义在drivers/i2c/i2c-core-base.c文件中:
/** * i2c_new_device - instantiate an i2c device * @adap: the adapter managing the device * @info: describes one I2C device; bus_num is ignored * Context: can sleep * * This deprecated function has the same functionality as * @i2c_new_client_device, it just returns NULL instead of an ERR_PTR in case of * an error for compatibility with current I2C API. It will be removed once all * users are converted. * * This returns the new i2c client, which may be saved for later use with * i2c_unregister_device(); or NULL to indicate an error. */ struct i2c_client * i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info) { struct i2c_client *ret; ret = i2c_new_client_device(adap, info); return IS_ERR(ret) ? NULL : ret; } /** * i2c_new_client_device - instantiate an i2c device * @adap: the adapter managing the device * @info: describes one I2C device; bus_num is ignored * Context: can sleep * * Create an i2c device. Binding is handled through driver model * probe()/remove() methods. A driver may be bound to this device when we * return from this function, or any later moment (e.g. maybe hotplugging will * load the driver module). This call is not appropriate for use by mainboard * initialization logic, which usually runs during an arch_initcall() long * before any i2c_adapter could exist. * * This returns the new i2c client, which may be saved for later use with * i2c_unregister_device(); or an ERR_PTR to describe the error. */ static struct i2c_client * i2c_new_client_device(struct i2c_adapter *adap, struct i2c_board_info const *info) { struct i2c_client *client; int status; client = kzalloc(sizeof *client, GFP_KERNEL); // 申请i2c_client if (!client) return ERR_PTR(-ENOMEM); client->adapter = adap; // 设置其I2C适配器 client->dev.platform_data = info->platform_data; // 使用info初始化client client->flags = info->flags; client->addr = info->addr; client->init_irq = info->irq; if (!client->init_irq) // 如果未设定中断号,将从资源中查找中断 client->init_irq = i2c_dev_irq_from_resources(info->resources, info->num_resources); client->irq = client->init_irq; strlcpy(client->name, info->type, sizeof(client->name)); // 设置client->name名称 status = i2c_check_addr_validity(client->addr, client->flags); // 检查地址是否有效 如果是10位地址,则地址不能超过0x3ff;如果是7位地址,则地址不能超过0x01到0x7f if (status) { dev_err(&adap->dev, "Invalid %d-bit I2C address 0x%02hx\n", client->flags & I2C_CLIENT_TEN ? 10 : 7, client->addr); goto out_err_silent; } /* Check for address business */ status = i2c_check_addr_busy(adap, i2c_encode_flags_to_addr(client)); // 检查地址 if (status) goto out_err; client->dev.parent = &client->adapter->dev; // 初始化设备基类dev,因此是从设备被是挂载适配器设备目录下 /sys/devices/platform/s3c2440-i2c.0/i2c-0/设备名称 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; i2c_dev_set_name(adap, client, info); // 设置client->dev.kobj.name为info->dev_name或者i2c-%d或者%d-%04x if (info->properties) { status = device_add_properties(&client->dev, info->properties); if (status) { dev_err(&adap->dev, "Failed to add properties to client %s: %d\n", client->name, status); goto out_err_put_of_node; } } status = device_register(&client->dev); // 注册设备 会在sys文件系统下创建/sys/devices/platform/s3c2440-i2c.0/i2c-0/%d-%04x目录
if (status) goto out_free_props; dev_dbg(&adap->dev, "client [%s] registered with bus id %s\n", client->name, dev_name(&client->dev)); return client; out_free_props: if (info->properties) device_remove_properties(&client->dev); out_err_put_of_node: of_node_put(info->of_node); out_err: dev_err(&adap->dev, "Failed to register i2c client %s at 0x%02x (%d)\n", client->name, client->addr, status); out_err_silent: kfree(client); return ERR_PTR(status); }
4.4 I2C驱动注册
i2c_register_driver用于I2C驱动注册,定义在drivers/i2c/i2c-core-base.c文件中:
/* * An i2c_driver is used with one or more i2c_client (device) nodes to access * i2c slave chips, on a bus instance associated with some i2c_adapter. */ int i2c_register_driver(struct module *owner, struct i2c_driver *driver) { int res; /* Can't register until after driver model init */ if (WARN_ON(!is_registered)) // i2c_init()执行前,都是未注册状态 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; // 驱动总线 INIT_LIST_HEAD(&driver->clients); // 初始化I2C从设备链表 /* 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("driver [%s] registered\n", driver->driver.name); /* Walk the adapters that are already present */ i2c_for_each_dev(driver, __process_new_driver); // 遍历I2C总线上的所有设备 return 0; }
这个函数首先执行了I2C驱动的注册,然后调用i2c_for_each_dev遍历I2C总线i2c_bus_type上的所有设备,然后根据dev->type过滤出适配器。
对每个适配器调用__process_new_driver,查看有没有适配器与自身i2c_driver的class成员匹配。
如果匹配成功,将会使用i2c_detect_address函数来确定能否找到address_list里的从设备,如果找到了,则会回调detect方法,并会自动注册新的i2c_client从设备。
__process_new_driver函数定义在drivers/i2c/i2c-core-base.c文件中:
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)); }
i2c_do_add_adatper之前已经介绍过了,不再重复了。
4.5 I2C从设备和驱动注册流程
可以看到无论是I2C从设备注册还是驱动注册,当I2C从设备和I2C驱动匹配成功,函数最终执行了i2c_device_probe,而在i2c_device_probe中调用了I2C驱动的probe/probe_new函数,一般我们会在probe函数中实现一套I2C从设备字符设备驱动逻辑。
如果单独看上面这张图,这张图是没有关于I2C适配器的任何信息的,如果我们仅仅注册I2C从设备、I2C驱动,不注册I2C适配器可以么,显然是不行的,我们回归一下i2c_new_device以及i2c_register_driver函数参数:
struct i2c_client * i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info); int i2c_register_driver(struct module *owner, struct i2c_driver *driver);
其中I2C从设备i2c_client的注册是依赖于I2C适配器的,也就是说我们必须要指定I2C从设备所连接的I2C适配器,只有这样我们才能通过I2C适配器来对I2C从设备进行数据的读写操作。
五、I2C字符设备驱动的注册
在前面介绍了I2C适配器、从设备以及驱动的注册。那问题来了,应用层如何去访问从设备呢,在过去我们一般都是在应用层通过open函数访问设备节点文件。
同理内核为我们提供了应用层操作I2C从设备的方式,可以让我们在应用层操作该 I2C 总线上的任意从设备。其实现思路是为了每个I2C适配器注册一个字符设备驱动。
这样我们就可以通过该字符设备对I2C控制器进行读写操作,这里简单说下怎么在应用层操作从设备的方法,首先通过 open 函数打开一个设备,然后通过ioctl设置你要操作的从设备地址,当然ioctl函数不仅可以设置从设备地址,还可以设置你的重传次数,超时时间等,最后就可以通过write 和read 函数接收和发送。
我们定位到drivers/i2c/i2c-dev.c文件:
/* * module load/unload record keeping */ static int __init i2c_dev_init(void) { int res; printk(KERN_INFO "i2c /dev entries driver\n"); res = register_chrdev_region(MKDEV(I2C_MAJOR, 0), I2C_MINORS, "i2c"); // 静态注册设备编号 起始设备编号为0,个数为256 if (res) goto out; i2c_dev_class = class_create(THIS_MODULE, "i2c-dev"); // 注册i2c-dev类 会在sys文件系统创建/sys/class/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); // 监听I2C总线,当有设备新增、删除事件,执行i2dev_attach_adapter、i2dev_detach_adapter if (res) goto out_unreg_class; /* Bind to already existing adapters right away */ i2c_for_each_dev(NULL, i2cdev_attach_adapter); // 遍历I2C总线上的所有设备,根据dev->type筛选出适配器,执行i2cdev_attach_adapter函数 return 0; out_unreg_class: class_destroy(i2c_dev_class); out_unreg_chrdev: unregister_chrdev_region(MKDEV(I2C_MAJOR, 0), I2C_MINORS); out: printk(KERN_ERR "%s: Driver Initialisation failed\n", __FILE__); return res; }
该函数主要流程如下:
- register_chrdev_region函数注册了一个 I2C 字符设备,主设备号为 I2C_MAJOR;
- 然后创建了一个"i2c-dev"类,这个就是/sys/class/i2c-dev目录的由来,而 i2c_groups 里面包含了该类中专有的属性文件;
- 遍历I2C总线上的适配器,调用i2cdev_attach_adapter,为每个I2C适配器注册一个字符设备驱动;
这样就可以操作/dev/i2c-%d的设备节点,来完成对该设备下的所有从设备的操作。
5.1 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) // 过滤I2C适配器,这里主要是把I2C从设备排除掉 return 0; adap = to_i2c_adapter(dev); // 转换为I2C适配器类型 i2c_dev = get_free_i2c_dev(adap); // 这里应该是动态分配i2c_dev if (IS_ERR(i2c_dev)) return PTR_ERR(i2c_dev); cdev_init(&i2c_dev->cdev, &i2cdev_fops); // 初始化字符设备结构体 i2cdev_fops为函数操作集 i2c_dev->cdev.owner = THIS_MODULE; res = cdev_add(&i2c_dev->cdev, MKDEV(I2C_MAJOR, adap->nr), 1); // 将字符设备添加到系统,次设备号为适配器编号 if (res) goto error_cdev; /* register this i2c device with the driver core */ i2c_dev->dev = device_create(i2c_dev_class, &adap->dev, // 注册设备 会在/sys/class/i2c-dev/下生成i2c-%d链接文件 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: cdev_del(&i2c_dev->cdev); error_cdev: put_i2c_dev(i2c_dev); return res; }
该函树主要作用:
- 为每一个I2C适配器初始化一个字符设备;主设备编号为I2C_MAJOR,次设备编号为I2C适配器编号;
- 将字符设备注册到系统;
- 调用device_create注册设备,并创建/dev/i2c-%d设备节点;
当我们对设备节点进行读写操作时,会调用i2cdev_fops操作集的方法。
5.2 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, .compat_ioctl = compat_i2cdev_ioctl, .open = i2cdev_open, .release = i2cdev_release, };
这里我们依次来看一下i2cdev_open、i2cdev_read、i2cdev_write、i2cdev_release方法。
5.2.1. i2cdev_open
static int i2cdev_open(struct inode *inode, struct file *file) { unsigned int minor = iminor(inode); // 获取次设备号 struct i2c_client *client; struct i2c_adapter *adap; adap = i2c_get_adapter(minor); // 由于字符设备的次设备号为I2C适配器的编号,因此根据次设备号查询i2c_adapter_idr树就能获取到I2C适配器, 同时对client->adapter.dev引用计数+1 if (!adap) return -ENODEV; /* This creates an anonymous i2c_client, which may later be * pointed to some address using I2C_SLAVE or I2C_SLAVE_FORCE. * * This client is ** NEVER REGISTERED ** with the driver model * or I2C core code!! It just holds private copies of addressing * information and maybe a PEC flag. */ client = kzalloc(sizeof(*client), GFP_KERNEL); // 动态分配I2C从设备 if (!client) { i2c_put_adapter(adap); return -ENOMEM; } snprintf(client->name, I2C_NAME_SIZE, "i2c-dev %d", adap->nr); client->adapter = adap; // 初始化adapater file->private_data = client; // 保存I2C从设备 return 0; }
5.2.2 i2cdev_read
/* * After opening an instance of this character special file, a file * descriptor starts out associated only with an i2c_adapter (and bus). * * Using the I2C_RDWR ioctl(), you can then *immediately* issue i2c_msg * traffic to any devices on the bus used by that adapter. That's because * the i2c_msg vectors embed all the addressing information they need, and * are submitted directly to an i2c_adapter. However, SMBus-only adapters * don't support that interface. * * To use read()/write() system calls on that file descriptor, or to use * SMBus interfaces (and work with SMBus-only hosts!), you must first issue * an I2C_SLAVE (or I2C_SLAVE_FORCE) ioctl. That configures an anonymous * (never registered) i2c_client so it holds the addressing information * needed by those system calls and by this SMBus interface. */ 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; // 获取I2C从设备 if (count > 8192) count = 8192; tmp = kmalloc(count, GFP_KERNEL); // 动态分配内存,长度为count if (tmp == NULL) return -ENOMEM; pr_debug("i2c-dev: i2c-%d reading %zu bytes.\n", iminor(file_inode(file)), count); ret = i2c_master_recv(client, tmp, count); // 实现从I2C设备读取数据 if (ret >= 0) ret = copy_to_user(buf, tmp, count) ? -EFAULT : ret; // 从内核空间拷贝数据到用户空间 kfree(tmp); // 释放内存 return ret; }
5.2.3 i2cdev_write
static ssize_t i2cdev_write(struct file *file, const char __user *buf, size_t count, loff_t *offset) { int ret; char *tmp; struct i2c_client *client = file->private_data; // 获取I2C从设备 if (count > 8192) count = 8192; tmp = memdup_user(buf, count); // 从用户空间拷贝数据到内核空间 if (IS_ERR(tmp)) return PTR_ERR(tmp); pr_debug("i2c-dev: i2c-%d writing %zu bytes.\n", iminor(file_inode(file)), count); ret = i2c_master_send(client, tmp, count); // 实现I2C从设备的数据写入 kfree(tmp); // 释放内存 return ret; }
5.2.4 i2cdev_release
static int i2cdev_release(struct inode *inode, struct file *file) { struct i2c_client *client = file->private_data; // 获取I2C从设备 i2c_put_adapter(client->adapter); // 对client->adapter.dev引用计数-1 kfree(client); // 释放I2C从设备 file->private_data = NULL; return 0; }
5.3 i2c_master_send
这个函数就是在应用层 可以通过 read函数来操作 I2C控制器接收数据的原因,本质就是对 i2c_transfer 函数的封装。
int i2c_master_send(const struct i2c_client *client, const char *buf, int count) { int ret; struct i2c_adapter *adap = client->adapter; // 获取I2C从设备连接的I2C适配器 struct i2c_msg msg; // 用于构建I2C数据包 msg.addr = client->addr; // 从设备地址 msg.flags = client->flags & I2C_M_TEN; // 10位地址? msg.len = count; // 待发送数据长度 msg.buf = (char *)buf; // 待发送的数据 ret = i2c_transfer(adap, &msg, 1); // 实现数据的发送 return (ret == 1) ? count : ret; }
5.4 i2c_master_recv
这两个函数就是在应用层可以通过write函数来操作I2C控制器接发送数据的原因,本质就是对 i2c_transfer 函数的封装。
int i2c_master_recv(const struct i2c_client *client, char *buf, int count) { struct i2c_adapter *adap = client->adapter; // 获取I2C从设备连接的I2C适配器 struct i2c_msg msg; // 用于构建I2C数据包 int ret; msg.addr = client->addr; // 从设备地址 msg.flags = client->flags & I2C_M_TEN; // 10位地址 msg.flags |= I2C_M_RD; // 主设备接收模式 msg.len = count; // 读取数据长度 msg.buf = buf; // 读取到的数据 ret = i2c_transfer(adap, &msg, 1); // 实现数据的读取 return (ret == 1) ? count : ret; }
5.5 i2c_transfer
下图显示了I2C数据传输过程:
5.5.1 i2c_transfer
i2c_transfer定义在drivers/i2c/i2c-core-base.c文件:
/** * i2c_transfer - execute a single or combined I2C message * @adap: Handle to I2C bus * @msgs: One or more messages to execute before STOP is issued to * terminate the operation; each message begins with a START. * @num: Number of messages to be executed. * * Returns negative errno, else the number of messages executed. * * Note that there is no requirement that each message be sent to * the same slave address, although that is the most common model. */ int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) // mgs为需要传输的数据包数组 { int ret; if (!adap->algo->master_xfer) { dev_dbg(&adap->dev, "I2C level transfers not supported\n"); return -EOPNOTSUPP; } /* REVISIT the fault reporting model here is weak: * * - When we get an error after receiving N bytes from a slave, * there is no way to report "N". * * - When we get a NAK after transmitting N bytes to a slave, * there is no way to report "N" ... or to let the master * continue executing the rest of this combined message, if * that's the appropriate response. * * - When for example "num" is two and we successfully complete * the first message but get an error part way through the * second, it's unclear whether that should be reported as * one (discarding status on the second message) or errno * (discarding status on the first one). */ ret = __i2c_lock_bus_helper(adap); if (ret) return ret; ret = __i2c_transfer(adap, msgs, num); i2c_unlock_bus(adap, I2C_LOCK_SEGMENT); return ret; }
5.5.2 __i2c_transfer
/** * __i2c_transfer - unlocked flavor of i2c_transfer * @adap: Handle to I2C bus * @msgs: One or more messages to execute before STOP is issued to * terminate the operation; each message begins with a START. * @num: Number of messages to be executed. * * Returns negative errno, else the number of messages executed. * * Adapter lock must be held when calling this function. No debug logging * takes place. adap->algo->master_xfer existence isn't checked. */ int __i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) // num为1 { unsigned long orig_jiffies; int ret, try; if (WARN_ON(!msgs || num < 1)) return -EINVAL; ret = __i2c_check_suspended(adap); if (ret) return ret; if (adap->quirks && i2c_check_for_quirks(adap, msgs, num)) return -EOPNOTSUPP; /* * i2c_trace_msg_key gets enabled when tracepoint i2c_transfer gets * enabled. This is an efficient way of keeping the for-loop from * being executed when not needed. */ if (static_branch_unlikely(&i2c_trace_msg_key)) { int i; for (i = 0; i < num; i++) if (msgs[i].flags & I2C_M_RD) trace_i2c_read(adap, &msgs[i], i); else trace_i2c_write(adap, &msgs[i], i); } /* Retry automatically on arbitration loss */ orig_jiffies = jiffies; for (ret = 0, try = 0; try <= adap->retries; try++) { // 重试 if (i2c_in_atomic_xfer_mode() && adap->algo->master_xfer_atomic) ret = adap->algo->master_xfer_atomic(adap, msgs, num); else ret = adap->algo->master_xfer(adap, msgs, num); // 数据传输 if (ret != -EAGAIN) break; if (time_after(jiffies, orig_jiffies + adap->timeout)) break; } if (static_branch_unlikely(&i2c_trace_msg_key)) { int i; for (i = 0; i < ret; i++) if (msgs[i].flags & I2C_M_RD) trace_i2c_reply(adap, &msgs[i], i); trace_i2c_result(adap, num, ret); } return ret; }
master_xfer(adap, msgs, num)函数就与SOC的I2C控制器寄存器设置相关了,基本是裸机相关的内容,无非就是产生 I2C 通信时序,如开始信号,停止信号、应答信号等。
这里我们以S3C2440为例,当注册了I2C适配器之后,adap->algo被设置为了&s3c24xx_i2c_algorithm:
/* i2c bus registration info */ static const struct i2c_algorithm s3c24xx_i2c_algorithm = { .master_xfer = s3c24xx_i2c_xfer, .functionality = s3c24xx_i2c_func, };
s3c24xx_i2c_xfer函数执行流程如下:
- s3c24xx_i2c_xfer中会调用s3c24xx_i2c_doxfer进行数据的传输;
- s3c24xx_i2c_doxfer中向总线发送开始信号和I2C从设备地址后,便会调用wait_event_timeout函数进程进入睡眠状态;
- 从设备地址发送完成时,将产生中断,并调用实现注册的中断处理函数s3c24xx_i2c_irq;
- s3c24xx_i2c_irq调用下一个字节传输函数i2s_s3c_irq_nextbyte来传输数据;
- 当I2C数据传输完成后,会调用 s3c24xx_i2c_stop;
- 最后调用wake_up唤醒等待队列,完成数据的传输过程;
5.5.3 s3c24xx_i2c_xfer
s3c24xx_i2c_xfer函数定义在drivers/i2c/busses/i2c-s3c2410.c文件:
/* * first port of call from the i2c bus code when an message needs * transferring across the i2c bus. */ static int s3c24xx_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) // num为1 { struct s3c24xx_i2c *i2c = (struct s3c24xx_i2c *)adap->algo_data; // 获取私有数据 int retry; int ret; ret = clk_enable(i2c->clk); // 时钟使能 if (ret) return ret; for (retry = 0; retry < adap->retries; retry++) { // 失败重试 ret = s3c24xx_i2c_doxfer(i2c, msgs, num); if (ret != -EAGAIN) { clk_disable(i2c->clk); // 禁止时钟 return ret; } dev_dbg(i2c->dev, "Retrying transmission (%d)\n", retry); udelay(100); } clk_disable(i2c->clk); // 禁止时钟 return -EREMOTEIO; }
这里I2C数据传输又调用了s3c24xx_i2c_doxfer:
/* * this starts an i2c transfer */ static int s3c24xx_i2c_doxfer(struct s3c24xx_i2c *i2c, struct i2c_msg *msgs, int num) { unsigned long timeout; int ret; ret = s3c24xx_i2c_set_master(i2c); if (ret != 0) { dev_err(i2c->dev, "cannot get bus (error %d)\n", ret); ret = -EAGAIN; goto out; } i2c->msg = msgs; // I2C数据包 i2c->msg_num = num; // 一般只有一个数据包 i2c->msg_ptr = 0; i2c->msg_idx = 0; // 当前数据包索引 i2c->state = STATE_START; // 开始标志 s3c24xx_i2c_enable_irq(i2c); // i2c中断使能 IICCON位[5]设置位1 s3c24xx_i2c_message_start(i2c, msgs); // 实际进行数据传输的函数 if (i2c->quirks & QUIRK_POLL) { // 轮询方式,不会进入 ret = i2c->msg_idx; if (ret != num) dev_dbg(i2c->dev, "incomplete xfer (%d)\n", ret); goto out; } timeout = wait_event_timeout(i2c->wait, i2c->msg_num == 0, HZ * 5); // 等待传输完成或超时,进程进入睡眠 ret = i2c->msg_idx; /* * Having these next two as dev_err() makes life very * noisy when doing an i2cdetect */ if (timeout == 0) dev_dbg(i2c->dev, "timeout\n"); else if (ret != num) dev_dbg(i2c->dev, "incomplete xfer (%d)\n", ret); /* For QUIRK_HDMIPHY, bus is already disabled */ if (i2c->quirks & QUIRK_HDMIPHY) goto out; s3c24xx_i2c_wait_idle(i2c); // 等待I2C总线空闲 s3c24xx_i2c_disable_bus(i2c); out: i2c->state = STATE_IDLE; // 空闲 return ret; }
到了这一步,我们终于看到I2C数据传输的函数了s3c24xx_i2c_message_start。
5.5.4 s3c24xx_i2c_message_start
s3c24xx_i2c_message_start函数用于I2C数据包的传输:
/* * put the start of a message onto the bus */ static void s3c24xx_i2c_message_start(struct s3c24xx_i2c *i2c, struct i2c_msg *msg) // I2C数据包数组 { unsigned int addr = (msg->addr & 0x7f) << 1; unsigned long stat; unsigned long iiccon; stat = 0; stat |= S3C2410_IICSTAT_TXRXEN; if (msg->flags & I2C_M_RD) { // 主接口模式 stat |= S3C2410_IICSTAT_MASTER_RX; addr |= 1; } else // 主发送发送 stat |= S3C2410_IICSTAT_MASTER_TX; if (msg->flags & I2C_M_REV_DIR_ADDR) // 位0 设置发送/接收 addr ^= 1; /* todo - check for whether ack wanted or not */ s3c24xx_i2c_enable_ack(i2c); // 1. 使能应答 IICCON位[7] iiccon = readl(i2c->regs + S3C2410_IICCON); writel(stat, i2c->regs + S3C2410_IICSTAT); // 2/ 设置工作模式 dev_dbg(i2c->dev, "START: %08lx to IICSTAT, %02x to DS\n", stat, addr); writeb(addr, i2c->regs + S3C2410_IICDS); // 3. 发送从设备地址 /* * delay here to ensure the data byte has gotten onto the bus * before the transaction is started */ ndelay(i2c->tx_setup); // 延时 dev_dbg(i2c->dev, "iiccon, %08lx\n", iiccon); writel(iiccon, i2c->regs + S3C2410_IICCON); stat |= S3C2410_IICSTAT_START; // 位[4]设置为1 writel(stat, i2c->regs + S3C2410_IICSTAT); // 4. 使能串行输出,主设备发送START信号 if (i2c->quirks & QUIRK_POLL) { // I2C数据传输采用轮询方式,才会进来 这里我们用的中断传输方式,不会进入该方法 while ((i2c->msg_num != 0) && is_ack(i2c)) { // 数据包未传输完&&收到应答 i2c_s3c_irq_nextbyte(i2c, stat); // 发送下一个字节 stat = readl(i2c->regs + S3C2410_IICSTAT); if (stat & S3C2410_IICSTAT_ARBITR) // 仲裁失败 dev_err(i2c->dev, "deal with arbitration loss\n"); } } }
这个函数主要就是在I2C数据传输之前的初始化工作:
- 配置IICCON位[7]使能应答;
- 配置IICSTAT位[7:6],设置模式;
- 设置IICDS:发送从设备地址,数据发送完成,触发中断;
- 进入中断处理函数,s3c24xx_i2c_irq调用下一个字节传输函数i2s_s3c_irq_nextbyte来传输I2C数据包中的数据;
5.5.5 i2c_s3c_irq_nextbyte
这段代码实际上就是进行I2C数据包的发送了,这里实现了多个I2C数据包的发送,包括改变I2C数据传输方向的逻辑、I2C数据传输停止、清除I2C中断挂起标志等:
/* * process an interrupt and work out what to do */ static int i2c_s3c_irq_nextbyte(struct s3c24xx_i2c *i2c, unsigned long iicstat) { unsigned long tmp; unsigned char byte; int ret = 0; switch (i2c->state) { case STATE_IDLE: dev_err(i2c->dev, "%s: called in STATE_IDLE\n", __func__); goto out; case STATE_STOP: dev_err(i2c->dev, "%s: called in STATE_STOP\n", __func__); s3c24xx_i2c_disable_irq(i2c); goto out_ack; case STATE_START: /* * last thing we did was send a start condition on the * bus, or started a new i2c message */ if (iicstat & S3C2410_IICSTAT_LASTBIT && !(i2c->msg->flags & I2C_M_IGNORE_NAK)) { /* ack was not received... */ dev_dbg(i2c->dev, "ack was not received\n"); s3c24xx_i2c_stop(i2c, -ENXIO); goto out_ack; } if (i2c->msg->flags & I2C_M_RD) // 主接收模式 i2c->state = STATE_READ; else // 主发送模式 i2c->state = STATE_WRITE; /* * Terminate the transfer if there is nothing to do * as this is used by the i2c probe to find devices. */ if (is_lastmsg(i2c) && i2c->msg->len == 0) { // 无数据需要传输 发送停止信号 s3c24xx_i2c_stop(i2c, 0); goto out_ack; } if (i2c->state == STATE_READ) goto prepare_read; /* * fall through to the write state, as we will need to * send a byte as well */ case STATE_WRITE: /* * we are writing data to the device... check for the * end of the message, and if so, work out what to do */ if (!(i2c->msg->flags & I2C_M_IGNORE_NAK)) { // 没有设置NAK标志进入,也就是要主设备发送一个字节数据后要收到应答 if (iicstat & S3C2410_IICSTAT_LASTBIT) { // 主设备没有收到应答 dev_dbg(i2c->dev, "WRITE: No Ack\n"); s3c24xx_i2c_stop(i2c, -ECONNREFUSED); // 发送停止信号 goto out_ack; } } retry_write: if (!is_msgend(i2c)) { // 不是当前I2C数据包的最后一个字节,则传输下一个字节 byte = i2c->msg->buf[i2c->msg_ptr++]; // 获取当前需要发送的字节 writeb(byte, i2c->regs + S3C2410_IICDS); // 发送数据 /* * delay after writing the byte to allow the * data setup time on the bus, as writing the * data to the register causes the first bit * to appear on SDA, and SCL will change as * soon as the interrupt is acknowledged */ ndelay(i2c->tx_setup); // 等待 } else if (!is_lastmsg(i2c)) { // 是当前I2C数据包的最后一个字节,但不是最后一个I2C数据包,则传输下一个数据包 /* we need to go to the next i2c message */ dev_dbg(i2c->dev, "WRITE: Next Message\n"); i2c->msg_ptr = 0; i2c->msg_idx++; i2c->msg++; /* check to see if we need to do another message */ if (i2c->msg->flags & I2C_M_NOSTART) { // 如果不需要发送I2C START信号 if (i2c->msg->flags & I2C_M_RD) { // 主接收模式 即进行I2C读操作 之前是写、现在变成读,改变了I2C传输方向 /* * cannot do this, the controller * forces us to send a new START * when we change direction */ s3c24xx_i2c_stop(i2c, -EINVAL); } goto retry_write; } else { // 新的I2C数据包开始传输,需要进行新的数据传输流程 /* send the new start */ s3c24xx_i2c_message_start(i2c, i2c->msg); i2c->state = STATE_START; } } else { /* send stop */ s3c24xx_i2c_stop(i2c, 0); // 发送停止信号 } break; case STATE_READ: /* * we have a byte of data in the data register, do * something with it, and then work out whether we are * going to do any more read/write */ byte = readb(i2c->regs + S3C2410_IICDS); i2c->msg->buf[i2c->msg_ptr++] = byte; /* Add actual length to read for smbus block read */ if (i2c->msg->flags & I2C_M_RECV_LEN && i2c->msg->len == 1) i2c->msg->len += byte; prepare_read: if (is_msglast(i2c)) { /* last byte of buffer */ if (is_lastmsg(i2c)) s3c24xx_i2c_disable_ack(i2c); } else if (is_msgend(i2c)) { /* * ok, we've read the entire buffer, see if there * is anything else we need to do */ if (is_lastmsg(i2c)) { /* last message, send stop and complete */ dev_dbg(i2c->dev, "READ: Send Stop\n"); s3c24xx_i2c_stop(i2c, 0); } else { /* go to the next transfer */ dev_dbg(i2c->dev, "READ: Next Transfer\n"); i2c->msg_ptr = 0; i2c->msg_idx++; i2c->msg++; } } break; } /* acknowlegde the IRQ and get back on with the work */ out_ack: tmp = readl(i2c->regs + S3C2410_IICCON); tmp &= ~S3C2410_IICCON_IRQPEND; writel(tmp, i2c->regs + S3C2410_IICCON); // 清除I2C中断挂起标志 out: return ret; }
参考文章
[1]二十二、Linux驱动之IIC驱动(基于linux2.6.22.6内核)
[4]fs4412 I2C驱动基于Cortex-A9,mpu6050裸机程序,驱动,I2C架构,有这一篇够了
[8]I2C Subsystem -3- (Transfer)
[9]I2C Subsystem -4- (I2C-Mux)
[10]Device & Driver -2- (Bus & Class)
[11] s3c2440.dtsi