当前位置: 首页 > news >正文

linux I2C核心、总线与设备驱动

一、        linux I2C体系结构

        linux的I2C体系结构分为3个组成部分

        1)I2C核心

        I2C核心提供了I2C总线驱动与设备驱动的注册、注销方法,I2C通信方法(即Algorithm)上层的与具体适配器无关的代码及其探测设备、检测设备地址的上层代码等,如图:

        2)I2C总线驱动

        I2C总线驱动是对I2C硬件体系结构中适配器端的实现,适配器可由CPU控制,甚至可以直接集成在CPU内部。

        I2C总线驱动 主要包含I2C适配器数据结构I2C_adaper、I2C适配器的Algorithm数据结构I2C_algorithm和控制I2C适配器产生通信信号的函数。经I2C总线驱动的代码,我们可以控制I2C适配器以主控方式产生开始位、停止位、读写周期,以及以从设备方式被读写、产生ACK等。

        3)I2C设备驱动

        I2C设备驱动(也称客户驱动)是对I2C硬件体系结构设备端的实现,设备一般挂接在受CPU控制的I2C的适配器上,通过I2C适配器与CPU交换数据。

        I2C设备驱动主要包含数据结构I2c_driver和i2c_client,我们需要根据具体设备实现其中的成员函数。

        在linux kernel中,所有的i2c设备都在sysfs文件系统中显示,存于/sys/bus/i2c/目录下,以适配器地址和芯片的地址的形式列出,例如:

        

        在linux内核源码中的drivers目录下有一个i2c目录,而在I2C目录下包含如下文件和文件夹

        1)i2c-core.c

        这个文件实现了i2C核心的功能以及/proc/bus/i2c*接口 。

        2)i2c-dev.c

        实现了I2c适配器设备文件的功能,每个I2c适配器都被分配一个设备。通过适配器访问设备时的主设备号都为89,次设备号为0-255。应用程序通过“i2c-%d”(i2c-0,i2c-1,...,i2c-10,...)文件名并使用文件操作接口open()、write()、read()、ioctl()、和close()等来访问这个设备。

        i2c-dev.c并不是针对特定都设备而设计都,只是提供了通用都read()、write()和ioctl()等接口,应用层可以借用这些接口访问挂接在适配器上的i2c设备的存储空间或寄存器,并控制i2c设备的工作方式。

        3)busses文件夹

        这个文件包含了一些I2c主机控制权的驱动,如i2c-tegra.c、i2c-omap.c、i2c-versatile.c、i2c-s3c2410.c等

        4)algos文件夹

        实现了一些i2c总线适配器的通信方法

        此外,内核中i2c.h文件对i2c_adapter、i2c_algorithm、i2c_driver和i2c_client这4个数据结构进行了定义。理解这四个结构体的作用十分重要,他们的定义位于include/linux/i2c.h文件中

 719 struct i2c_adapter {720     struct module *owner;721     unsigned int class;       /* classes to allow probing for */722     const struct i2c_algorithm *algo; /* the algorithm to access the bus */723     void *algo_data;724 725     /* data fields that are valid for all devices   */726     const struct i2c_lock_operations *lock_ops;727     struct rt_mutex bus_lock;728     struct rt_mutex mux_lock;729 730     int timeout;            /* in jiffies */731     int retries;732     struct device dev;      /* the adapter device */733     unsigned long locked_flags; /* owned by the I2C core */734 #define I2C_ALF_IS_SUSPENDED        0735 #define I2C_ALF_SUSPEND_REPORTED    1736 737     int nr;738     char name[48];739     struct completion dev_released;740 741     struct mutex userspace_clients_lock;742     struct list_head userspace_clients;743 744     struct i2c_bus_recovery_info *bus_recovery_info;745     const struct i2c_adapter_quirks *quirks;746 747     struct irq_domain *host_notify_domain;748     struct regulator *bus_regulator;749 };
 541 struct i2c_algorithm {542     /*543      * If an adapter algorithm can't do I2C-level access, set master_xfer544      * to NULL. If an adapter algorithm can do SMBus access, set545      * smbus_xfer. If set to NULL, the SMBus protocol is simulated546      * using common I2C messages.547      *548      * master_xfer should return the number of messages successfully549      * processed, or a negative value on error550      */551     int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs,552                int num);553     int (*master_xfer_atomic)(struct i2c_adapter *adap,554                    struct i2c_msg *msgs, int num);555     int (*smbus_xfer)(struct i2c_adapter *adap, u16 addr,556               unsigned short flags, char read_write,557               u8 command, int size, union i2c_smbus_data *data);558     int (*smbus_xfer_atomic)(struct i2c_adapter *adap, u16 addr,559                  unsigned short flags, char read_write,560                  u8 command, int size, union i2c_smbus_data *data);561 562     /* To determine what the adapter supports */563     u32 (*functionality)(struct i2c_adapter *adap);564 565 #if IS_ENABLED(CONFIG_I2C_SLAVE)566     int (*reg_slave)(struct i2c_client *client);567     int (*unreg_slave)(struct i2c_client *client);568 #endif569 };

        上述代码551行master_xfer对应为I2c传输函数指针,i2c主机驱动的大部分工作也聚集在这里。上述第555行代码对应为SMbus传输函数指针,SMbus不需要增加额外引脚,与i2c总线相比,在访问时序上也有一定的差异。 

 271 struct i2c_driver {272     unsigned int class;273 274     /* Standard driver model interfaces */275     int (*probe)(struct i2c_client *client);276     void (*remove)(struct i2c_client *client);277 278 279     /* driver model interfaces that don't relate to enumeration  */280     void (*shutdown)(struct i2c_client *client);281 282     /* Alert callback, for example for the SMBus alert protocol.283      * The format and meaning of the data value depends on the protocol.284      * For the SMBus alert protocol, there is a single bit of data passed285      * as the alert response's low bit ("event flag").286      * For the SMBus Host Notify protocol, the data corresponds to the287      * 16-bit payload data reported by the slave device acting as master.288      */289     void (*alert)(struct i2c_client *client, enum i2c_alert_protocol protocol,290               unsigned int data);291 292     /* a ioctl like command that can be used to perform specific functions293      * with the device.294      */295     int (*command)(struct i2c_client *client, unsigned int cmd, void *arg);296 297     struct device_driver driver;298     const struct i2c_device_id *id_table;299 300     /* Device detection callback for automatic device creation */301     int (*detect)(struct i2c_client *client, struct i2c_board_info *info);302     const unsigned short *address_list;303     struct list_head clients;304 305     u32 flags;306 };
 330 struct i2c_client {331     unsigned short flags;       /* div., see below      */332 #define I2C_CLIENT_PEC      0x04    /* Use Packet Error Checking */333 #define I2C_CLIENT_TEN      0x10    /* we have a ten bit chip address */334                     /* Must equal I2C_M_TEN below */335 #define I2C_CLIENT_SLAVE    0x20    /* we are the slave */336 #define I2C_CLIENT_HOST_NOTIFY  0x40    /* We want to use I2C host notify */337 #define I2C_CLIENT_WAKE     0x80    /* for board_info; true iff can wake */338 #define I2C_CLIENT_SCCB     0x9000  /* Use Omnivision SCCB protocol */339                     /* Must match I2C_M_STOP|IGNORE_NAK */340 341     unsigned short addr;        /* chip address - NOTE: 7bit    */342                     /* addresses are stored in the  */343                     /* _LOWER_ 7 bits       */344     char name[I2C_NAME_SIZE];345     struct i2c_adapter *adapter;    /* the adapter we sit on    */346     struct device dev;      /* the device structure     */347     int init_irq;           /* irq set at initialization    */348     int irq;            /* irq issued by device     */349     struct list_head detected;350 #if IS_ENABLED(CONFIG_I2C_SLAVE)351     i2c_slave_cb_t slave_cb;    /* callback for slave mode  */352 #endif353     void *devres_group_id;      /* ID of probe devres group */354 };

        下面分析这4个数据结构的作用及盘根错节的关系

        1) i2c_adapter(适配器)与i2_algorithm(算法,通信方法)

        i2c_adapter对应于物理上的一个适配器,而i2c_algorithm对应一套通信方法。一个i2c适配器需要i2c_algorithm提供的通信函数来控制适配器产生特定的访问周期。缺少i2c_algorithm的i2c_adapter什么也做不了,因此i2c_adapter中包含所使用的i2c_algorithm的指针。

        i2c_algorithm的关键函数master_xfer()用于产生访问周期需要的信号,以i2c_msg(即i2c信息)为单位。i2c_msg结构体也是非常重要的,它定义于include/uapi/linux/i2c.h(在uapi目录下,证明用户空间的应用也可能用这个结构体)中,下图给出了它的定义,其中的成员表明了i2c的传输地址、方向、缓冲区、缓冲区长度等信息。

 73 struct i2c_msg {74     __u16 addr;75     __u16 flags;76 #define I2C_M_RD        0x0001  /* guaranteed to be 0x0001! */77 #define I2C_M_TEN       0x0010  /* use only if I2C_FUNC_10BIT_ADDR */78 #define I2C_M_DMA_SAFE      0x0200  /* use only in kernel space */79 #define I2C_M_RECV_LEN      0x0400  /* use only if I2C_FUNC_SMBUS_READ_BLOCK_DATA */80 #define I2C_M_NO_RD_ACK     0x0800  /* use only if I2C_FUNC_PROTOCOL_MANGLING */81 #define I2C_M_IGNORE_NAK    0x1000  /* use only if I2C_FUNC_PROTOCOL_MANGLING */82 #define I2C_M_REV_DIR_ADDR  0x2000  /* use only if I2C_FUNC_PROTOCOL_MANGLING */83 #define I2C_M_NOSTART       0x4000  /* use only if I2C_FUNC_NOSTART */84 #define I2C_M_STOP      0x8000  /* use only if I2C_FUNC_PROTOCOL_MANGLING */85     __u16 len;86     __u8 *buf;87 };

        2)i2c_driver与i2c_client

        i2c_driver对应于一套驱动方法,其主要成员函数是probe()、remove()、suspen()、resumu()等,另外,struct i2c_device_id形式的id_table是该驱动所支持的i2c设备的ID表。i2c_client对应于真实的物理设备,每个i2c设备都需要一个i2c_client来描述。i2c_driver与i2c_client的关系是一对多,一个i2c_driver可以支持多个同类型的i2c_clinet 。

        i2c_client的信息通常在BSP的板文件中通过i2c_board_info填充,如下:

        在i2c总线驱动i2c_bus_type的match()函数i2c_device_match()中,会调用i2c_match_id()函数匹配在板文件中定义的ID和i2c_driver所支持的ID表 。  

        3)i2c_adapter与i2c_client

        i2c_adapter与i2c_cilent的关系与i2c硬件体系中适配器和设备的关系一致,即i2c_clinet依附于i2c_adapter。由于一个适配器可以连接多个i2c设备,所有一个i2c_adapter也可以被多个i2c_client依附,i2c_adapter中包含依附它的i2c_client的链表。

       假设i2c总线适配器xxx上有两个使用相同驱动程序的yyyI2c设备,在打开该I2c总线的设备节点后,相关数据结构之间的逻辑组织关系如图:

       

         从上面的分析可知,虽然i2c硬件体系结构比较简单,但是i2c体系结构在linux中的实现却相当复杂。当工程师拿到实际的电路板时 ,面对复杂的linux i2c子系统,应该如何下手写驱动呢?究竟有哪些是需要亲自做的,哪些是内核已经提供的呢?理清这个问题非常有意义,可以使我们做面对具体问题时迅速抓住重点。

        一方面,适配器驱动可能是linux内核本身还不包含的;另一方面,挂接做适配器上的具体设备驱动可能也是linux内核还不包含的。因此,工程师要实现的主要工作如下

        1)提供I2c硬件适配器的硬件驱动,探测、初始化i2c适配器(如申请i2c的i/O地址和中断号)、驱动cpu控制的i2c适配器从硬件上产生各种信号以及处理i2c中断等。

        2)提供i2c适配器的Algorithm,用具体适配器的xxx_xfer()函数填充i2c_algorithm的master_xfer指针,并把i2c_algorithm指针赋值给i2c_adapter的algo指针。

        3)实现i2c设备驱动中的i2c_driver接口,具体设备yyy的yyy_probe()、yyy_remove()、yyy_suspen()、yyy_resume()函数指针和i2c_device_id设备ID表赋值给i2c_driver的probe、remove、suspend、resume和id_table指针。

        4)实现i2c设备所对应类型的具体驱动,i2c_driver只是实现设备与总线的挂接,而挂接在总线上的设备则千差万别。例如,如果是字符设备,就实现文件操作接口,即实现具体设备yyy的yyy_read()、yyy_write()和yyy_ioctl函数等;如果是声卡就实现ALSA驱动。

        上述工作中前两个属于I2c总线驱动,后两个属于i2c设备驱动。

二、        linux I2c核心 

        I2c核心(drivers/i2c/i2c-core-base.c) 中提供了一组不依赖于硬件平台的接口函数,这个文件一般不需要被工程师修改,但是理解其中的主要函数非常关键,因为i2c总线驱动和设备驱动以i2c核心作为纽带。i2c核心的主要函数如下。

        1)增加、删除i2c_adapter

        

    

        2)增加/删除i2c_dirver

         

        3)i2c传输、发送和接收

        i2c_transfer()函数用于进行i2c适配器和i2c设备之间的一组消息交互,其中第2个参数是指向i2c_msg数组的指针,所以i2c_transfer()一次可以传输多个i2c_msg(考虑到很多外设的读写波形比较复杂,比如读寄存器可能要先写,所以需要两个以上的消息)。而对于时序比较简单的外设,i2c_maste_send()函数和i2c_master_recv()函数内部都会调用i2c_transfer()函数分别完成一条写消息和读消息。

  

        i2c_transfer()函数本身不具备驱动适配器物理硬件以完成消息交互的能力,它只是寻找到与i2c_adapter对应的i2c_algorithm,并使用i2c_algorithm的master_xfer()函数真正驱动硬件流程。

三、        linux I2c适配器驱动

        3.1         I2c适配器驱动的注册与注销

        由于I2c总线控制器通常是在内存上的,所以本身也连接在platform总线上,要通过platform_driver和platform_device的匹配来执行。因此尽管i2c适配器给别人提供了总线,它自己也被认为是接在platform总线上的一个客户。linux的总线、设备和驱动模型实际上是一个树形结构,每个节点虽然可能成为别人的总线控制器,但是自己也被认为是从上一级总线枚举出来的。

        通常我们会在与I2c适配器所对应的platform_driver的probe()函数中完成两个工作。

        1)初始化I2c适配器所使用的硬件资源,如申请I/O地址、中断号、时钟等

        2)   通过i2c_add_adapter()添加i2c_adapter的数据结构,当然这个i2c_adapter数据结构的成员已经被xxx适配器的相应函数指针初始化。

        通常我们会在platform_driver的remove()函数中完成与加载函数相反的工作

        3)释放i2c适配器所使用的硬件资源,如释放I/O地址、中断号、时钟等。

        4)通过i2c_del_adapter()删除i2c_adapter的数据结构

        上诉代码中的xxx_adpater_hw_init()和xxx_adpater_hw_free()函数的实现都与具体的CPU和i2c适配硬件直接相关。

        3.2        I2c总线的通信方法 

        我们需要为特定的i2c适配器实现通信方法,主要是实现i2c_algorithm的functionality()函数和master_xfer()函数。

        functionality()函数非常,用于返回algorithm所支持的通信协议,如I2C_FUNC_I2C、I2C_FUNC_10BIT_ADDR、I2C_FUNC_SMBUS_READ_BYTE、I2C_FUNC_SMBUS_WRITE_BYTE等。

        master_xfer()函数在i2c适配器上完成传递给它的i2c_msg数组中的每个I2C消息。

 

四、        linux I2c设备驱动 

         I2c设备驱动要使用i2c_driver和i2c_client数据结构并填充i2c_driver中的成员函数。i2c_client一般被包含在设备的私有信息结构体yyy_data中,而i2c_driver则适合被定义为全局变量并初始化。

        

        4.1        linux I2c设备驱动的模块加载与卸载

        i2c设备驱动的模块加载函数通过i2c核心的i2c_add_driver()API函数添加i2c_driver的工作,而模块卸载函数需要做相反的工作:通过i2c核心的i2c_del_driver()函数删除i2c_dirver 。模板如下:

        

        4.2、        linux I2c设备驱动的数据传输 

        在i2c设备上读写数据的时序且数据通常通过i2c_msg数组进行组织,最后通过i2c_transfer()函数完成。 

        

        4.3、 linux的i2c-dev.c文件分析

         i2c-dev.c完全可以被看作一个i2c设备驱动,不过,它实现的i2c-client是虚拟的,临时的。主要是为了方便从用户空间操作i2c外设。i2c-dev.c针对每个i2c适配器生成一个主设备号为89的设备文件,实现了i2c_driver的成员函数及其文件操作接口,因此i2c-dev.c的主体是“i2c_driver成员函数+字符设备驱动”。

        i2c-dev.c提供的i2cdev_read()、i2cdec_write()函数对应于用户空间要使用的read()和write()文件操作接口,这两个函数分别调用i2c核心的i2c_maser_recv()和i2c_master_send()函数来构造一条i2c消息并引发适配器Algorithm通信函数的调用,以完成消息的传输,它对应下面的时序:

        

        但是,很遗憾,大多数稍微复杂一点的i2c设备的读写流程并不对应一条消息,往往需要两条甚至多条消息来进行一次读写周期(即下面图示模式),在这种情况下,在应用层仍然调用read()、write()文件API来读写I2c设备,将不能正确地读写。

        

        鉴于以上原因,i2c-dev.c中i2cdev_read()和i2cdec_write()函数不具备太强的通用性,没有太大的实用价值,只能适用非RepStart模式的情况。对于由两条以上消息组成的读写 ,在用户空间需要组织i2c_msg消息数组并调用I2C_RDWR IOCTL命令。

        

         

        常用的IOCTL包括I2C_SLAVE(设置从设备地址)、I2C_RETRIES(没有收到设备ACK情况下的重试次数,默认为1)、I2C_TIMEOU(超时)以及I2C_RDWR。

五、Tegra I2c总线驱动实例

        NVIDIA Tegra I2c总线总线驱动位于drivers/i2c/busses/i2c-tegra.c文件中,这里不具体研究它的硬件细节,只看一下驱动框架和流程。

        I2c总线驱动是一个单独的驱动,在模块的加载和卸载函数中,只需要注册和注销platform_driver结构体。

1629 static const struct of_device_id tegra_i2c_of_match[] = {
1630     { .compatible = "nvidia,tegra194-i2c", .data = &tegra194_i2c_hw, },
1631     { .compatible = "nvidia,tegra186-i2c", .data = &tegra186_i2c_hw, },
1632 #if IS_ENABLED(CONFIG_ARCH_TEGRA_210_SOC)
1633     { .compatible = "nvidia,tegra210-i2c-vi", .data = &tegra210_i2c_hw, },
1634 #endif
1635     { .compatible = "nvidia,tegra210-i2c", .data = &tegra210_i2c_hw, },
1636     { .compatible = "nvidia,tegra124-i2c", .data = &tegra124_i2c_hw, },
1637     { .compatible = "nvidia,tegra114-i2c", .data = &tegra114_i2c_hw, },
1638     { .compatible = "nvidia,tegra30-i2c", .data = &tegra30_i2c_hw, },
1639     { .compatible = "nvidia,tegra20-i2c", .data = &tegra20_i2c_hw, },
1640 #if IS_ENABLED(CONFIG_ARCH_TEGRA_2x_SOC)
1641     { .compatible = "nvidia,tegra20-i2c-dvc", .data = &tegra20_i2c_hw, },
1642 #endif
1643     {},
1644 };
1645 MODULE_DEVICE_TABLE(of, tegra_i2c_of_match);1967 static struct platform_driver tegra_i2c_driver = {
1968     .probe = tegra_i2c_probe,
1969     .remove_new = tegra_i2c_remove,
1970     .driver = {
1971         .name = "tegra-i2c",
1972         .of_match_table = tegra_i2c_of_match,
1973         .acpi_match_table = tegra_i2c_acpi_match,
1974         .pm = &tegra_i2c_pm,
1975     },
1976 };
1977 module_platform_driver(tegra_i2c_driver);

        当在arch/arm/mach-tegra下创建一个名字为tegra-i2c的同名的platform_device,或者在tegra的设备树中添加了tegra_i2c_of_match匹配表兼容的节点后,上述platform_driver中的probe()函数就会执行。

        其中probe指针指向的tegra_i2c_probe()函数将被调用。以初始化,适配器硬件、申请适配器要的内存、时钟、中断等资源,最终注册适配器。

     

        上述代码中提到tegra_i2c_dev结构体可进行适配器所有信息的封装,类似私有信息结构体。下图是tegra_i2c_dev结构体的定义。我们在编程中要时刻牢记linux这个编程习惯,这实际上也是面向对象的一种体现。

 260 struct tegra_i2c_dev {261     struct device *dev;262     struct i2c_adapter adapter;263 264     const struct tegra_i2c_hw_feature *hw;265     struct reset_control *rst;266     unsigned int cont_id;267     unsigned int irq;268 269     phys_addr_t base_phys;270     void __iomem *base;271 272     struct clk_bulk_data clocks[2];273     unsigned int nclocks;274 275     struct clk *div_clk;276     struct i2c_timings timings;277 278     struct completion msg_complete;279     size_t msg_buf_remaining;280     unsigned int msg_len;281     int msg_err;282     u8 *msg_buf;283 284     struct completion dma_complete;285     struct dma_chan *dma_chan;286     unsigned int dma_buf_size;287     struct device *dma_dev;288     dma_addr_t dma_phys;289     void *dma_buf;290 291     bool multimaster_mode;292     bool atomic_mode;293     bool dma_mode;294     bool msg_read;295     bool is_dvc;296     bool is_vi;297 };

         tegra_i2c_probe()函数中的platform_set_drvdata(pdev,i2c_dev)和i2c_set_adapterdata(&I2C_dev->adapter,i2c_dev)已经把这个结构体的实例依附到了platform_device和i2c_adapter的私有数据上了,在其他地方只要用到相应的方法就可以把这个结构体的实例取出来。

        从前面的白图代码60行可以看出,于i2c适配器对应的i2c_algorithm结构体实例为tera_i2c_algo,下图给出定义

         

        上诉代码第一行指定了Tera I2c总线通信传输函数tegra_i2c_xfer(),这个函数非常关键,所有在i2c总线上对设备的访问最终应该由它完成。下图为这个重要函数以及其依赖的tegra_i2c_xfer_msg()函数的源码:

1213 static int tegra_i2c_xfer_msg(struct tegra_i2c_dev *i2c_dev,
1214                   struct i2c_msg *msg,
1215                   enum msg_end_type end_state)
1216 {
1217     unsigned long time_left, xfer_time = 100;
1218     size_t xfer_size;
1219     u32 int_mask;
1220     int err;
1221 
1222     err = tegra_i2c_flush_fifos(i2c_dev);
1223     if (err)
1224         return err;
1225 
1226     i2c_dev->msg_buf = msg->buf;
1227     i2c_dev->msg_len = msg->len;
1228 
1229     i2c_dev->msg_err = I2C_ERR_NONE;
1230     i2c_dev->msg_read = !!(msg->flags & I2C_M_RD);
1231     reinit_completion(&i2c_dev->msg_complete);
1232 
1233     /*
1234      * For SMBUS block read command, read only 1 byte in the first transfer.
1235      * Adjust that 1 byte for the next transfer in the msg buffer and msg
1236      * length.
1237      */
1238     if (msg->flags & I2C_M_RECV_LEN) {
1239         if (end_state == MSG_END_CONTINUE) {
1240             i2c_dev->msg_len = 1;
1241         } else {
1242             i2c_dev->msg_buf += 1;
1243             i2c_dev->msg_len -= 1;
1244         }
1245     }
1246 
1247     i2c_dev->msg_buf_remaining = i2c_dev->msg_len;
1248 
1249     if (i2c_dev->msg_read)
1250         xfer_size = i2c_dev->msg_len;
1251     else
1252         xfer_size = i2c_dev->msg_len + I2C_PACKET_HEADER_SIZE;
1253 
1254     xfer_size = ALIGN(xfer_size, BYTES_PER_FIFO_WORD);
1255 
1256     i2c_dev->dma_mode = xfer_size > I2C_PIO_MODE_PREFERRED_LEN &&
1257                 i2c_dev->dma_buf && !i2c_dev->atomic_mode;
1258 
1259     tegra_i2c_config_fifo_trig(i2c_dev, xfer_size);
1260 
1261     /*
1262      * Transfer time in mSec = Total bits / transfer rate
1263      * Total bits = 9 bits per byte (including ACK bit) + Start & stop bits
1264      */
1265     xfer_time += DIV_ROUND_CLOSEST(((xfer_size * 9) + 2) * MSEC_PER_SEC,
1266                        i2c_dev->timings.bus_freq_hz);
1267 
1268     int_mask = I2C_INT_NO_ACK | I2C_INT_ARBITRATION_LOST;
1269     tegra_i2c_unmask_irq(i2c_dev, int_mask);
1270 
1271     if (i2c_dev->dma_mode) {
1272         if (i2c_dev->msg_read) {
1273             dma_sync_single_for_device(i2c_dev->dma_dev,
1274                            i2c_dev->dma_phys,
1275                            xfer_size, DMA_FROM_DEVICE);
1276 
1277             err = tegra_i2c_dma_submit(i2c_dev, xfer_size);
1278             if (err)
1279                 return err;
1280         } else {
1281             dma_sync_single_for_cpu(i2c_dev->dma_dev,
1282                         i2c_dev->dma_phys,
1283                         xfer_size, DMA_TO_DEVICE);
1284         }
1285     }
1286 
1287     tegra_i2c_push_packet_header(i2c_dev, msg, end_state);
1288 
1289     if (!i2c_dev->msg_read) {
1290         if (i2c_dev->dma_mode) {
1291             memcpy(i2c_dev->dma_buf + I2C_PACKET_HEADER_SIZE,
1292                    msg->buf, i2c_dev->msg_len);
1293 
1294             dma_sync_single_for_device(i2c_dev->dma_dev,
1295                            i2c_dev->dma_phys,
1296                            xfer_size, DMA_TO_DEVICE);
1297 
1298             err = tegra_i2c_dma_submit(i2c_dev, xfer_size);
1299             if (err)
1300                 return err;
1301         } else {
1302             tegra_i2c_fill_tx_fifo(i2c_dev);
1303         }
1304     }
1305 
1306     if (i2c_dev->hw->has_per_pkt_xfer_complete_irq)
1307         int_mask |= I2C_INT_PACKET_XFER_COMPLETE;
1308 
1309     if (!i2c_dev->dma_mode) {
1310         if (msg->flags & I2C_M_RD)
1311             int_mask |= I2C_INT_RX_FIFO_DATA_REQ;
1312         else if (i2c_dev->msg_buf_remaining)
1313             int_mask |= I2C_INT_TX_FIFO_DATA_REQ;
1314     }
1315 
1316     tegra_i2c_unmask_irq(i2c_dev, int_mask);
1317     dev_dbg(i2c_dev->dev, "unmasked IRQ: %02x\n",
1318         i2c_readl(i2c_dev, I2C_INT_MASK));
1319 
1320     if (i2c_dev->dma_mode) {
1321         time_left = tegra_i2c_wait_completion(i2c_dev,
1322                               &i2c_dev->dma_complete,
1323                               xfer_time);
1324 
1325         /*
1326          * Synchronize DMA first, since dmaengine_terminate_sync()
1327          * performs synchronization after the transfer's termination
1328          * and we want to get a completion if transfer succeeded.
1329          */
1330         dmaengine_synchronize(i2c_dev->dma_chan);
1331         dmaengine_terminate_sync(i2c_dev->dma_chan);
1332 
1333         if (!time_left && !completion_done(&i2c_dev->dma_complete)) {
1334             dev_err(i2c_dev->dev, "DMA transfer timed out\n");
1335             tegra_i2c_init(i2c_dev);
1336             return -ETIMEDOUT;
1337         }
1338 
1339         if (i2c_dev->msg_read && i2c_dev->msg_err == I2C_ERR_NONE) {
1340             dma_sync_single_for_cpu(i2c_dev->dma_dev,
1341                         i2c_dev->dma_phys,
1342                         xfer_size, DMA_FROM_DEVICE);
1343 
1344             memcpy(i2c_dev->msg_buf, i2c_dev->dma_buf, i2c_dev->msg_len);
1345         }
1346     }
1347 
1348     time_left = tegra_i2c_wait_completion(i2c_dev, &i2c_dev->msg_complete,
1349                           xfer_time);
1350 
1351     tegra_i2c_mask_irq(i2c_dev, int_mask);
1352 
1353     if (time_left == 0) {
1354         dev_err(i2c_dev->dev, "I2C transfer timed out\n");
1355         tegra_i2c_init(i2c_dev);
1356         return -ETIMEDOUT;
1357     }
1358 
1359     dev_dbg(i2c_dev->dev, "transfer complete: %lu %d %d\n",
1360         time_left, completion_done(&i2c_dev->msg_complete),
1361         i2c_dev->msg_err);
1362 
1363     i2c_dev->dma_mode = false;
1364 
1365     err = tegra_i2c_error_recover(i2c_dev, msg);
1366     if (err)
1367         return err;
1368 
1369     return 0;
1370 }1372 static int tegra_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[],
1373               int num)
1374 {
1375     struct tegra_i2c_dev *i2c_dev = i2c_get_adapdata(adap);
1376     int i, ret;
1377 
1378     ret = pm_runtime_get_sync(i2c_dev->dev);
1379     if (ret < 0) {
1380         dev_err(i2c_dev->dev, "runtime resume failed %d\n", ret);
1381         pm_runtime_put_noidle(i2c_dev->dev);
1382         return ret;
1383     }
1384 
1385     for (i = 0; i < num; i++) {
1386         enum msg_end_type end_type = MSG_END_STOP;
1387 
1388         if (i < (num - 1)) {
1389             /* check whether follow up message is coming */
1390             if (msgs[i + 1].flags & I2C_M_NOSTART)
1391                 end_type = MSG_END_CONTINUE;
1392             else
1393                 end_type = MSG_END_REPEAT_START;
1394         }
1395         /* If M_RECV_LEN use ContinueXfer to read the first byte */
1396         if (msgs[i].flags & I2C_M_RECV_LEN) {
1397             ret = tegra_i2c_xfer_msg(i2c_dev, &msgs[i], MSG_END_CONTINUE);
1398             if (ret)
1399                 break;
1400             /* Set the msg length from first byte */
1401             msgs[i].len += msgs[i].buf[0];
1402             dev_dbg(i2c_dev->dev, "reading %d bytes\n", msgs[i].len);
1403         }
1404         ret = tegra_i2c_xfer_msg(i2c_dev, &msgs[i], end_type);
1405         if (ret)
1406             break;
1407     }
1408 
1409     pm_runtime_put(i2c_dev->dev);
1410 
1411     return ret ?: i;
1412 }

        从代码层面上看,第35行的for循环遍历所有的i2c_msg,每个i2c_msg则由tegra_i2c_xfer_msg()函数处理,它每次发起硬件操作后,实际上需要通过wait_for_completion_timeout()等待传输的完成,因此这里面就会有一个被调度出去的过程。中断到来且i2c的包传输结束的时候,就是唤醒这个睡眠进程的时候

  

六、        总结 

        linux的i2c驱动体系相当复杂,它主要由3部分组成,即i2c核心、i2c总线驱动和i2c设备驱动。I2c核心是i2c总线驱动和i2c设备驱动的中间枢纽,它以通用的、与平台的无关的接口是实现了i2c中设备与适配器的沟通。i2c总线驱动填充i2c_adapter和i2c_algorithm结构体,i2c设备驱动填充i2c_driver结构体并实现其本身所对应设备类型的驱动。

        另外,系统中i2c-dev.c文件定义的主设备号89的设备可以方便地给应用程序提供读写i2c设备寄存器的能力,使得工程师在大多数时候并不需要为具体的i2c设备驱动定义文件操作接口。

        

         

         

        

        

http://www.dtcms.com/a/334824.html

相关文章:

  • 扫地机器人(2025蓝桥杯省A组 H题)
  • [系统架构设计师]系统架构设计基础知识(七)
  • Conda创建py3.10环境(股票),并且安装程序包的命令
  • 《设计模式》装饰模式
  • python日志新姿势,Loguru库的应用
  • 后台管理系统-3-vue3之左侧菜单栏和头部导航栏的静态搭建
  • VUE+SPRINGBOOT从0-1打造前后端-前后台系统-用户管理
  • 力扣(LeetCode) ——142. 环形链表 II(C语言)
  • [Python]PTA:实验2-3-1-for 求1到100的和
  • ComfyUI-3D-Pack:3D创作的AI神器
  • 深度强化学习是否违背样本独立原则?
  • 使用IntersectionObserver实现页面右侧运营位区域固定,和页面列表数据分页加载
  • CSS复习
  • Euler 采样器在扩散模型中的原理解析:从数学公式到实践应用
  • Apereo CAS靶场渗透练习
  • 【Linux系统】进程间通信:System V IPC——共享内存
  • Flink 源码系列 - 前言
  • 如何使用5个时间步长创建移动平均特征
  • 自动驾驶中的传感器技术33——Lidar(8)
  • Leetcode 14 java
  • 数论之普通判别法、埃氏筛与线性筛的应用及其对比
  • PowerShell 第11章:过滤和比较(下)
  • 深度剖析Redisson分布式锁项目实战
  • redis存储原理与对象模型
  • 《A Practical Guide to Building Agents》文档学习
  • 数学建模:智能优化算法
  • PostgreSQL——事务处理与并发控制
  • CVE-2021-4300漏洞复现
  • 海康机器人3D相机的应用
  • ZKmall开源商城的数据校验之道:用规范守护业务基石