• Linux ARM平台开发系列讲解(IIC) 2.7.3 I2C设备驱动分析


    1. 概述

    学习完IIC总线驱动模型后,重点还是I2C设备驱动,因为一般芯片原厂已经把总线驱动给实现好了,在实际开发过程中,设备开发才是重点,相比总线驱动而已,也是必须掌握的。

    2. I2C设备驱动

    I2C 设备驱动重点关注两个数据结构:i2c_clienti2c_driver,根据总线、设备和驱动模型,I2C 总线上一小节已经讲了。还剩下设备和驱动,i2c_client 就是描述设备信息的,i2c_driver 描述驱动内容,类似于 platform_driver

    2.1 I2C设备树(以RK3399为例)

    &i2c1 { 
    	status = "okay"; 
    	i2c-scl-rising-time-ns = <265>;
    	i2c-scl-falling-time-ns = <11>; 
    	clock-frequency = <400000>;
    	es8316: es8316@10 {
    		#sound-dai-cells = <0>; 
    		compatible = "everest,es8316"; 
    		reg = <0x10>;
    		clocks = <&cru SCLK_I2S_8CH_OUT>; 
    		clock-names = "mclk";
    		spk-con-gpio = <&gpio0 11 GPIO_ACTIVE_HIGH>;
    		hp-det-gpio = <&gpio4 28 GPIO_ACTIVE_LOW>;
    	};
    };
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • clock-frequency: 默认 frequency100k 可不配置,其它 I2C 频率需要配置,最大可配置频率由 i2c-scl-rising-time-ns 决定;例如配置 400kclock-frequency=<400000>
    • i2c-scl-rising-time-nsSCL 上升沿时间由硬件决定,改变上拉电阻可调节该时间,需通过示波器
      量测,参考下图;例如测得 SCL 上升沿 365nsi2c-scl-rising-time-ns=<365>。(默认可以不配
      置,但必须保证当前的上升沿时间不能超过所配置频率下的 I2C 标准所定义的最大上升沿时间)
      i2c-scl-falling-time-ns: SCL 下降沿时间, 一般不变, 等同于 i2c-sda-falling-time-ns。(默认也可以
      不配置)
    • es8316为I2C从设备信息
      • compatible :用于匹配设备driver
      • reg:从设备的从机地址

    在这里插入图片描述

    3. i2c_client 结构体

    一个设备对应一个 i2c_client,每检测到一个 I2C 设备就会给这个 I2C 设备分配一个i2c_clienti2c_client 结构体定义在 include/linux/i2c.h 文件中,内容如下:

    struct i2c_client {
    	unsigned short flags;		/* div., see below		*/
    #define I2C_CLIENT_PEC		0x04	/* Use Packet Error Checking */
    #define I2C_CLIENT_TEN		0x10	/* we have a ten bit chip address */
    					/* Must equal I2C_M_TEN below */
    #define I2C_CLIENT_SLAVE	0x20	/* we are the slave */
    #define I2C_CLIENT_HOST_NOTIFY	0x40	/* We want to use I2C host notify */
    #define I2C_CLIENT_WAKE		0x80	/* for board_info; true iff can wake */
    #define I2C_CLIENT_SCCB		0x9000	/* Use Omnivision SCCB protocol */
    					/* Must match I2C_M_STOP|IGNORE_NAK */
    
    	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
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • flags: 标志
    • addr: 芯片地址,7 位,存在低 7
    • name:名字
    • adapter: 对应的 I2C 适配器
    • dev:设备结构体
    • irq :中断

    4. i2c_driver 结构体

    i2c_driver 类似 platform_driver,是我们编写 I2C 设备驱动重点要处理的内容,i2c_driver
    构体定义在 include/linux/i2c.h 文件中,内容如下:

    struct i2c_driver {
    	unsigned int class;
    
    	/* Standard driver model interfaces */
    	int (*probe)(struct i2c_client *client, const struct i2c_device_id *id);
    	int (*remove)(struct i2c_client *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 *client);
    
    	/* driver model interfaces that don't relate to enumeration  */
    	void (*shutdown)(struct i2c_client *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 *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 *client, struct i2c_board_info *info);
    	const unsigned short *address_list;
    	struct list_head clients;
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • probe:当 I2C 设备和驱动匹配成功以后 probe 函数就会执行,和 platform 驱动一样。
    • device_driver: 驱动结构体,如果使用设备树的话,需要设置 device_driverof_match_table 成员变量,也就是驱动的兼容(compatible)属性。
    • id_table 是传统的、未使用设备树的设备匹配 ID

    4.1 i2c_driver的注册

    对于我们 I2C 设备驱动编写人来说,重点工作就是构建 i2c_driver,构建完成以后需要向Linux 内核注册这个 i2c_driveri2c_driver 注册函数为 int i2c_register_driver,此函数原型如下:

    int i2c_register_driver(struct module *owner, struct i2c_driver *driver)
    
    • 1

    函数参数和返回值含义如下:

    • owner:一般为 THIS_MODULE
    • driver:要注册的 i2c_driver
    • 返回值0,成功;负值,失败。

    除了i2c_register_driver外,i2c_add_driver 也常常用于注册 i2c_driveri2c_add_driver 是一个宏,i2c_add_driver 就是对 i2c_register_driver 做了一个简单的封装,只有一个参数,就是要注册的 i2c_driver。定义如下:

    #define i2c_add_driver(driver) \
    i2c_register_driver(THIS_MODULE, driver)
    
    • 1
    • 2

    4.2 i2c_driver的注销

    注销 I2C 设备驱动的时候需要将前面注册的 i2c_driverLinux 内核中注销掉,需要用到
    i2c_del_driver 函数,此函数原型如下:

    void i2c_del_driver(struct i2c_driver *driver)
    
    • 1

    函数参数和返回值含义如下:

    • driver:要注销的 i2c_driver
    • 返回值:无。

    4.3 i2c_driver的注册示例代码如下(出自正点原子):

    /* i2c 驱动的 probe 函数 */
    static int xxx_probe(struct i2c_client *client,const struct i2c_device_id *id) 
    { 
    /* 函数具体程序 */
    	return 0; 
    } 
    /* i2c 驱动的 remove 函数 */
    static int xxx_remove(struct i2c_client *client)
    {
    	/* 函数具体程序 */
    	return 0;
    }
    
     /* 传统匹配方式 ID 列表 */
    static const struct i2c_device_id xxx_id[] = {
    	{"xxx", 0}, 
    	{}
    };
    
     /* 设备树匹配列表 */
     static const struct of_device_id xxx_of_match[] = {
    	{ .compatible = "xxx" },
    	{ /* Sentinel */ }
    };
    
     /* i2c 驱动结构体 */
     static struct i2c_driver xxx_driver = {
    	.probe = xxx_probe,
    	.remove = xxx_remove,
    	.driver = {
    	.owner = THIS_MODULE,
    	.name = "xxx",
    	.of_match_table = xxx_of_match,
    	},
    	.id_table = xxx_id,
    };
     
     /* 驱动入口函数 */
    static int __init xxx_init(void)
    {
    	int ret = 0;
    
    	ret = i2c_add_driver(&xxx_driver);
    	return ret;
    }
    
    /* 驱动出口函数 */
    static void __exit xxx_exit(void)
    {
    i2c_del_driver(&xxx_driver);
    }
    
    module_init(xxx_init);
    module_exit(xxx_exit);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54

    5. I2C 设备和驱动匹配过程

    I2C 设备和驱动的匹配过程是由 I2C 核心来完成的,drivers/i2c/i2c-core.c 就是 I2C 的核心部分,I2C 核心提供了一些与具体硬件无关的 API 函数,比如前面提到的i2c注册和注销函数:
    ①:i2c_adapter 注册/注销函数

    int i2c_add_adapter(struct i2c_adapter *adapter)
    int i2c_add_numbered_adapter(struct i2c_adapter *adap)
    void i2c_del_adapter(struct i2c_adapter * adap)
    
    • 1
    • 2
    • 3

    ②:i2c_driver 注册/注销函数

    int i2c_register_driver(struct module *owner, struct i2c_driver *driver)
    int i2c_add_driver (struct i2c_driver *driver)
    void i2c_del_driver(struct i2c_driver *driver)
    
    • 1
    • 2
    • 3

    设备和驱动的匹配过程也是由 I2C 总线完成的,I2C 总线的数据结构为 i2c_bus_type,定义
    drivers/i2c/i2c-core.c 文件,i2c_bus_type 内容如下:

    struct bus_type i2c_bus_type = {
    	.name		= "i2c",
    	.match		= i2c_device_match,
    	.probe		= i2c_device_probe,
    	.remove		= i2c_device_remove,
    	.shutdown	= i2c_device_shutdown,
    };
    EXPORT_SYMBOL_GPL(i2c_bus_type);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • match 就是 I2C 总线的设备和驱动匹配函数,在这里就是 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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    函数分析如下:

    • of_driver_match_device:函数用于完成设备树设备和驱动匹配。比较 I2C 设备节点的 compatible 属性和 of_device_id 中的 compatible 属性是否相等,如果相当的话就表示 I2C设备和驱动匹配
    • acpi_driver_match_device:函数用于 ACPI 形式的匹配。
    • i2c_match_id:函数用于传统的、无设备树的 I2C 设备和驱动匹配过程。比较 I2C设备名字和 i2c_device_idname 字段是否相等,相等的话就说明 I2C 设备和驱动匹配。

    返回总目录

  • 相关阅读:
    Vue项目中的字段格式化工具(进阶版)
    input禁止输入
    【数据结构】归并排序、基数排序算法的学习知识点总结
    【C++从0到王者】第四十六站:图的深度优先与广度优先
    Programming Differential Privacy第十五章Synthetic Data合成数据
    Qt实现Qchart的打印和打印预览的几种方法
    Virtual File System了解
    React 组件的状态下移和内容提升
    xss跨站脚本攻击姿势大全
    Oracle 2019c安装闪退解决过程
  • 原文地址:https://blog.csdn.net/DSMGUOGUO/article/details/125422136