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.pswp.cn/bicheng/93527.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/93527.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/93527.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

跑實驗記錄

1.下載git&#xff08;base) mqmq-MS-7A59:~/桌面$ sudo apt update && sudo apt install git2.克隆項目&#xff08;base) mqmq-MS-7A59:~/桌面$ sudo apt update && sudo apt install git3.canda創建環境(base) mqmq-MS-7A59:~$ conda create -n HyTE python…

微軟動手了,聯合OpenAI + Azure 云爭奪AI服務市場

?開頭還是介紹一下群&#xff0c;如果感興趣PolarDB ,MongoDB ,MySQL ,PostgreSQL ,Redis, OceanBase, Sql Server等有問題&#xff0c;有需求都可以加群群內有各大數據庫行業大咖&#xff0c;可以解決你的問題。加群請聯系 liuaustin3 &#xff0c;&#xff08;共3300人左右 …

Reading Coach-微軟推出的免費AI閱讀教練

本文轉載自&#xff1a;Reading Coach-微軟推出的免費AI閱讀教練 - Hello123工具導航 ** 一、智能閱讀輔助工具 Reading Coach 是微軟推出的 AI 驅動閱讀訓練平臺&#xff0c;通過個性化故事生成與實時發音反饋&#xff0c;幫助學生提升閱讀流利度與詞匯量。平臺采用自適應學…

《軟件工程導論》實驗報告五 設計建模工具的使用(一)類圖

目 錄 一、實驗目的 二、實驗環境 三、學時分配 四、實驗內容與步驟 1. 百度搜索1-2張類圖&#xff0c;請重新繪制它們&#xff0c;并回答以下問題&#xff1a; 2. 根據以下描述&#xff0c;提取這個問題涉及的類&#xff0c;定義各個類之間的關系&#xff0c;并畫出類圖…

智慧景區導覽系統:基于WebGL的手繪地圖導覽設計與應用,DeepSeek大模型賦能精準游客引導服務

本文面向 景區信息化負責人、后端開發者、全棧工程師&#xff0c;旨在解決傳統景區導覽系統靜態地圖信息有限、人工導游成本高、景區服務人員咨詢壓力大 的核心痛點&#xff0c;提供從技術選型到落地部署的全鏈路解決方案。如需獲取智慧景區導覽系統解決方案請前往文章最下方獲…

使用uniapp自定義組件雙重支付密碼

自定義組件雙重支付密碼父組件<template><view class"container"><view class"top"></view><navbar navTitle"修改支付密碼"></navbar><!-- 雙重支付密碼 --><view class"box">//核心…

C語言+安全函數+非安全函數

在C語言中&#xff0c;許多標準庫函數&#xff08;如 strcpy、scanf、gets 等&#xff09;由于缺乏邊界檢查&#xff0c;容易導致 ?緩沖區溢出&#xff08;Buffer Overflow&#xff09;?、內存越界訪問? 等安全問題。為了解決這些問題&#xff0c;C11標準引入了 ?安全函數&…

android build.gradle中的namespace和applicationId的區別

namespace 和 applicationId 確實容作用&#xff1a;1. namespace引入版本&#xff1a;Android Gradle Plugin (AGP) 7.0 開始引入&#xff0c;替代 AndroidManifest.xml 里的 package 屬性。作用&#xff1a; 用于 代碼中的 R 文件、BuildConfig 生成的 Java/Kotlin 包名。決定…

數據結構初階(15)排序算法—交換排序(快速排序)(動圖演示)

2.3 交換排序 2.3.0 基本思想交換排序的基本思想&#xff1a;基本思想根據序列中兩個記錄鍵值的比較結果來對換這兩個記錄在序列中的位置。&#xff08;比較結果→交換位置&#xff09;特點將鍵值較大的記錄向序列的尾部移動&#xff0c;鍵值較小的記錄向序列的前部移動。比 換…

Apache Hudi:數據湖的實時革命

Apache Hudi是一個開源的數據湖存儲格式和框架&#xff0c;它通過引入類似數據庫的事務機制&#xff0c;解決了傳統數據湖在實時更新、低延遲查詢和增量消費方面的痛點。Hudi最初由Uber于2016年開發并應用于生產環境&#xff0c;2017年開源&#xff0c;2019年成為Apache孵化項目…

深度解析和鯨社區熱門項目:電商雙 11 美妝數據分析的細節與價值

在數據驅動決策的時代&#xff0c;電商大促期間的行業數據分析總能為從業者和學習者提供寶貴參考。今天&#xff0c;我們來詳細拆解和鯨社區&#xff08;heywhale&#xff09;上一個備受關注的實戰項目 ——《電商雙 11 美妝數據分析》&#xff0c;看看它能給我們帶來哪些啟發。…

uniapp 開發微信小程序,獲取經緯度并且轉化詳細地址(單獨封裝版本)

目錄1、單獨抽離封裝2、使用示例3、前置條件和配置4、效果彈框1、單獨抽離封裝 // 騰訊地圖SDK引入&#xff08;需提前下載qqmap-wx-jssdk.min.js文件&#xff09; // 注意&#xff1a;使用前需在微信公眾平臺配置request合法域名https://apis.map.qq.com var QQMapWX requir…

深入理解 Python 元類中的 __prepare__ 方法:掌控類屬性定義順序的藝術

關鍵詞&#xff1a;元類、type、prepare、OrderedDict、屬性順序、數據建模在 Python 的高級編程中&#xff0c;元類&#xff08;metaclass&#xff09; 是一種強大而神秘的機制。它允許我們在類創建之前進行干預&#xff0c;從而實現諸如自動屬性驗證、字段序列化、ORM 映射等…

MATLAB基礎訓練實驗

MATLAB基礎訓練實驗 1. 標題 MATLAB 基礎訓練 2. 內容概括 本實驗旨在通過MATLAB基礎操作訓練,掌握數組創建與運算、矩陣操作、M文件編寫、流程控制、二維/三維繪圖等核心技能。實驗內容包括復數運算、矩陣變換、函數繪圖、結構體創建、電路方程求解、電流波形繪制、三維曲…

implement libwhich for Windows

因為windows沒有類似unix的which命令 現在實現盡量跨平臺&#xff0c;且stb 風格的libwhich // which.h #ifndef LIBWHICH_H #define LIBWHICH_H#ifdef __cplusplus extern "C" { #endif/** 查找可執行文件在系統中的路徑* 參數:* filename - 要查找的可執行文件名…

記與客戶端的一次“無謂之爭”

一、沖突今天&#xff0c;流程收尾時&#xff0c;客戶端為了統計時延&#xff0c;連發兩個接口&#xff1a;一個報開始時間&#xff0c;一個報結束時間。我因性能考慮&#xff0c;說&#xff1a;“明明一個接口能搞定&#xff01;”客戶端負責人說&#xff1a;“發送兩次更合理…

Java Condition 對象 wait 方法使用與修復方案

在 Java 中&#xff0c;java.util.concurrent.locks.Condition 接口提供了類似監視器的方法&#xff08;await(), signal(), signalAll()&#xff09;來實現線程間的協調。正確使用 Condition 對象需要遵循特定模式以避免常見問題。常見問題及修復方案1. 虛假喚醒問題問題&…

??金倉數據庫KingbaseES V9R1C10安裝教程 - Windows版詳細指南?

文章目錄一、前言二、軟件下載2.1 下載安裝包2.2 下載授權文件三. 安裝KingbaseES數據庫3.1 解壓安裝包3.2 運行安裝程序3.3 安裝步驟詳解步驟1&#xff1a;歡迎界面步驟2&#xff1a;許可協議步驟3&#xff1a;添加授權文件步驟4&#xff1a;選擇安裝路徑步驟5&#xff1a;選擇…

論文推薦|遷移學習+多模態特征融合

來gongzhonghao【圖靈學術計算機論文輔導】&#xff0c;快速拿捏更多計算機SCI/CCF發文資訊&#xff5e;在Cvpr、NeurIPS、AAAI等頂會中&#xff0c;遷移學習多模態特征融合正以“降成本、提性能、省標注”的絕對優勢成為最熱賽道。面對超大模型全量微調天價算力、異構模態對齊…

接口芯片斷電高阻態特性研究與應用分析

摘要&#xff1a; 本文以國科安芯推出的ASM1042 系列通訊接口芯片為例&#xff0c;深入探討接口芯片斷電高阻態特性&#xff0c;涵蓋其定義、原理、應用及設計注意事項。通過對相關技術資料的梳理與分析&#xff0c;結合具體芯片實例&#xff0c;闡述高阻態在電路穩定性、設備兼…