你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

Linux内核中I2C子系统的整体视图

[复制链接]
gaosmile 发布时间:2020-6-25 15:28
本文通过阅读内核代码,来梳理一下I2C子系统的整体视图。在开发I2C设备驱动程序时,往往缺乏对于系统整体的认识,没有一个清晰的思路。所以从高层级来分析一下I2C系统的设计思路,将有助于设计调试具体的驱动程序。
I2C/SMBUS基础
I2C是一种芯片间通讯总线技术,最早由Philips设计制定。下面内容参考 I2C 2.1 规格书
  • 半双工通信方式,通信采用主/从结构
  • 支持多主模式,下图来源于I2C 2.1规格书
    ; M2 r$ D; B$ g8 m: m
微信图片_20200625152108.jpg
  • 其内部电气实现采用集电极开路(Open-collector)/漏极开路(open-drain)结构以实现线与功能,这是总线的实现基础,多芯片通过查询总线状态实现介质仲裁以实现总线控制。

    7 r( N4 N% a7 e1 [* Y& q$ j
    微信图片_20200625152113.png
  • 总线信号由两线实现,串行时钟线SCL(Serial Clock Line)/串行数据线SDA(serial Data Line)。
  • 具有三种通讯速率模式:

    - i+ J6 U4 B; i% P; O3 b
    • standard mode:0-100 kbps  (bps: bit/s)
    • Fast mode:0-400 kbps
    • High-speed mode : 0-3.4Mbps
      0 Q3 O& o# h- F8 n) y5 H$ H* y
  • 可支持混速模式
    ; ~. H1 y* Y( N& r4 J' E
微信图片_20200625152119.png
  • 不同的速率在硬件设计时需要注意信号的完整性,I2C总线等效电容Cx,主要需要考虑PCB布线,以及上拉电阻选取。
  • 支持7bit/10bit 两种芯片地址模式
  • I2C总线电气特性,这个非常重要,须严格遵守标准的电气特性
    0 Y3 z  Z: E" a; Q
微信图片_20200625152123.png
微信图片_20200625152126.png
  • SMBUS(system management bus) 。大多数SMBus系统也符合I2C,电气约束对于SMBus更为严格,并且它标准化了特定的协议消息和习惯用语。支持I2C的控制器也可以支持大多数SMBus操作,但是SMBus控制器并不支持I2C控制器将支持的所有协议选项。通过使用I2C原语或通过向不支持这些I2C操作的i2c_adapter设备发出SMBus命令,可以执行各种SMBus协议操作。[color=var(--weui-LINK)]http://smbus.org/4 X* V4 Z& w, Y3 ?. g
  • I2C bus(Inter-Integrated Circuit bus),[color=var(--weui-LINK)]http://www.i2c-bus.org/! N. q6 z+ t2 E. [, u6 L& G
I2C 在Linux设备中的拓扑结构
  • 在PC体系中,大体如下拓扑:
    ; \/ H# P7 {! g4 K, P4 |' o7 r
微信图片_20200625152130.png
PC体系中通过桥接芯片,扩展出PCI,在由PCI扩展出I2C适配器,进而得到I2C总线,或者桥接芯片直接扩展出SMBUS/I2C总线。, d& P2 {- B) q) |) r4 ~
  • 在嵌入式应用中,则可能为:
    % v* M; k4 G/ b. [
微信图片_20200625152133.png
嵌入式应用中,则可能更多的情况是处理器内置了I2C/SMBUS总线控制器,直接可得到I2C/SMBUS总线。嵌入式系统中常常会设计很多传感器挂载在I2C总线上,比如温度检测,压力检测等等,又或者诸如电容触摸屏、电源管理IC等等。
# B4 A5 N9 a+ C8 b, j9 i, Q# G
代码实现
  • I2C 的core实现位于./drivers/i2c/下,实现了I2C总线设备以及驱动(适配器)和设备驱动的注册、注销方法,I2C通信方法algorithm抽象,以及与具体硬件无关的代码
  • I2C主控制器驱动位于 ./drivers/i2c/busses/,这里主要实现总线控制器,具体体现为i2c_adapter的实现。负责I2C适配器与从设备通信。I2C总线驱动由i2c_adapter和i2c_algorithm来抽象描述。
  • 设备驱动则分散在./driver/下,这取决于具体的实现,种类繁多。
    * E6 h% {" z$ J' o+ S
    • i2c-dev,大多位于drivers/i2c/i2c-dev.c,这种方法只是封装了主机(I2Cmaster,一般是SoC中内置的I2C控制器)的I2C基本操作,并且向应用层提供相应的操作接口,应用层代码需要自己去实现对slave的控制和操作,所以这种I2C驱动相当于提供给应用层可以访问slave硬件设备的接口,本身并未对硬件做任何操作,应用需要实现对硬件的操作。这种模式也称为应用驱动程序。
    • 另一种I2C驱动是将所有的代码都放在驱动层实现,直接向应用层提供最终结果。应用层甚至不需要知道这里面有I2C存在,譬如电容式触摸屏驱动,直接向应用层提供/dev/input/event1的操作接口,应用层编程的人根本不知道event1中涉及到了I2C。
      / A+ F4 h! U  _: _) Y/ U
微信图片_20200625152144.png
I2C子系统的主要目的是,对I2C总线以及设备利用面向对象编程思想实现统一建模,以高内聚-低耦合的软件工程思想,实现一个分层体系结构,以便于内核统一管理I2C设备,从而可以更容易的在linux下实现I2C设备以及高可移植。! S; R  A6 e4 B6 V: `; o8 b
主要数据结构
其内部有几个关键数据结构,来梳理一下:
  • i2c_client, 用于抽象挂载在I2C总线上的从设备
  • i2c_driver,用于驱动挂载在I2C总线的从设备,也即从设备的设备驱动程序
  • i2c_adapter,用于抽象I2C的主设备
  • i2c_algorithm,抽象I2C总线操作接口

    . s" P& a, x  H
微信图片_20200625152147.png
i2c_devinfo. K7 y( e' _# Y% m2 }% P
该结构体主要用于板级I2C信息管理
微信图片_20200625152151.png
i2c_msg该结构体主要用于抽象I2C报文,其内容如下: 微信图片_20200625152154.png i2c_timings
主要用于抽象I2C电气特性,对于支持设备树的系统构建而言,主要通过以下内核接口函数,从设备树解析电气特性参数。
void i2c_parse_fw_timings(struct device *dev, struct i2c_timings *t, bool use_defaults)  |. q) s0 D- z5 X
{. N$ n7 N0 w( ~: Y, e* n
int ret;6 l$ m- I7 J6 b

) ]: Y6 k- \! F4 x/ F1 y memset(t, 0, sizeof(*t));
" j, m7 k+ e! Y5 j
+ a/ {2 |9 B# L. Z6 ?% P ret = device_property_read_u32(dev, "clock-frequency", &t->bus_freq_hz);
* D& I) }+ J2 ~# Y if (ret && use_defaults)# j- X" y" o' X4 g9 D! Z' I
  t->bus_freq_hz = 100000;3 a( Q. @, Y8 A

) U" H: N# u4 e- U, U ret = device_property_read_u32(dev, "i2c-scl-rising-time-ns", &t->scl_rise_ns);
: K0 X: \( t. ?) ]4 v if (ret && use_defaults) {
4 B" D) K1 Z( ^- C7 ~  if (t->bus_freq_hz <= 100000)% ~! F8 v& B. K6 K2 `$ S
   t->scl_rise_ns = 1000;; k4 d( \0 y- v8 ^8 L1 Z
  else if (t->bus_freq_hz <= 400000)
  Z, U4 z* Q* V1 J   t->scl_rise_ns = 300;& Q- g& s* U# e! [- L' I4 h* F
  else
' }% E8 c+ k- V- i   t->scl_rise_ns = 120;0 f+ v/ u. `' v% t$ m" e4 f
}
& X' d- [. S( ?4 n- _5 W- S: w% n
ret = device_property_read_u32(dev, "i2c-scl-falling-time-ns", &t->scl_fall_ns);) s) W& R( ^  c: ]& U$ O* I
if (ret && use_defaults) {# q- I7 X% i( l8 @8 B; e+ ?
  if (t->bus_freq_hz <= 400000)
: w% }( ]' ]2 n+ \# l5 r/ v/ E  k( P   t->scl_fall_ns = 300;
* @$ D$ H2 M1 `: ~/ s' b% v  else$ Y, l5 O2 e9 G$ N
   t->scl_fall_ns = 120;# x( ^7 }, C& c8 }2 N
}
4 Y6 |/ d3 K# j8 h# X+ x
$ d, _; D1 j$ w$ A4 x# u device_property_read_u32(dev, "i2c-scl-internal-delay-ns", &t->scl_int_delay_ns);
/ n  t5 R) N4 K) r
0 J3 F7 V: E4 { ret = device_property_read_u32(dev, "i2c-sda-falling-time-ns", &t->sda_fall_ns);
1 {. B1 I) }$ R if (ret && use_defaults)
9 ?" m' N; ?* \9 g1 e$ D  t->sda_fall_ns = t->scl_fall_ns;1 q3 s, d! q5 L5 Z; E4 `, \, O

: t& W* _/ g( B3 M device_property_read_u32(dev, "i2c-sda-hold-time-ns", &t->sda_hold_ns);
/ `: C. S0 }' x. T& P2 i}3 A; S; v* @+ l; d+ z5 F- |# Q2 ?
EXPORT_SYMBOL_GPL(i2c_parse_fw_timings);
5 U8 j# W( J! M# h5 f9 s
微信图片_20200625152157.jpg i2c_device_identity
该结构体主要用于抽象I2C 设备的ID属性,通过内核接口函数i2c_get_device_id 以获取设备ID属性。
微信图片_20200625152200.png
总体框架概述
Linux I2C编程接口支持总线交互的主端和从端。从高层级看由两种驱动程序和两种设备构成:
  • 适配器设备与适配器设备驱动对:I2C 适配器驱动程序用于抽象控制器硬件;它绑定到一个物理设备(可能是一个PCI设备(PC体系多一些)或platform_device(嵌入式应用居多)),并构建i2c_adapter实体以呈现所管理的1个I2C总线段。
    , L, e* V9 H2 b: D) d! M# x
    /* ALI1535 device address register bits */  w) K0 p: P) \4 Q/ j1 g# E
    #define ALI1535_RD_ADDR  0x01 /* Read/Write Bit in Device */3 B4 d9 T+ m2 w  [/ p
         /*  Address field  */3 I6 a$ ?6 {. e. `7 ^
         /*  -> Write = 0  */
    9 _% Q2 k- ]; v5 n9 J  ^     /*  -> Read  = 1  */
    ; h, K! _: R0 P1 f; E& O#define ALI1535_SMBIO_EN 0x04 /* SMB I/O Space enable  */" I4 \% h, c1 f. x  n! y6 \
    /*PCI 设备驱动*/
      r+ H/ G( S% n2 Hstatic struct pci_driver ali1535_driver;5 v  ^. N' N! v3 d$ i- [9 A2 c1 k0 M
    static unsigned long ali1535_smba;1 @0 w7 t$ W1 ]8 F
    static unsigned short ali1535_offset;

    5 t7 O. W/ B% S. A9 v& _& v

    , G& a  {" Y- `4 y
    • pci-I2C 适配器设备。如在i2c-ali1535.c中:
    • platform_device。比如:i2c-s3c2410,如下:6 c. O( K) Q6 e$ T' }/ y6 Z
      : c( o  D, Q' x4 ^( s  f: |" x
      + w+ v6 O. `6 w6 U6 j6 [
        static const struct platform_device_id s3c24xx_driver_ids[] = {
  ?5 |$ n3 M0 o1 | {
5 A* ~- L  t/ h1 L  .name  = "s3c2410-i2c",
$ I) q! Q9 P& c  .driver_data = 0,
/ C* ?; o) @' I) c3 D; u; ?! ?" f3 E }, {
6 O6 E" l0 M, r' c  .name  = "s3c2440-i2c",% c" n1 y: e* t' {: o6 x! B
  .driver_data = QUIRK_S3C2440,# B6 j- U4 {! G$ G- Y7 I/ L
}, {
( X5 M5 `5 f5 S4 u' D  .name  = "s3c2440-hdmiphy-i2c",1 q/ ^6 W" N) i( C% r% Z
  .driver_data = QUIRK_S3C2440 | QUIRK_HDMIPHY | QUIRK_NO_GPIO,
! y5 Z! X7 `/ W, o$ u/ ~* T) [7 P1 P  \ }, { },, s6 I& S6 ~6 d+ g4 y9 S
};
: o0 o" g6 {& T6 J% ]MODULE_DEVICE_TABLE(platform, s3c24xx_driver_ids);
9 y* O' F& k* W  Z$ u9 A
  • I2C从设备及设备驱动:每个I2C总线段上将有一个由结构i2c_client表示的I2C设备。这些设备将被绑定到一个struct i2c_driver,遵循标准的Linux驱动程序模型。
    + W- q- p6 h2 U3 ~: s7 q
架构

图片来源:[color=var(--weui-LINK)]http://www.kernel.org/doc/html/latest/i2c/slave-interface.html


3 F* V6 F! r+ W; V/ c$ z

微信图片_20200625152203.png

  Y, o8 R5 T6 b8 Z! z( |% y8 E

主端总线驱动职责:
  • 适配器和算法驱动程序,见下面i2c_algorithm结构体详细描述
  • 管理I2C总线交互
    ' i' w2 }' U) C1 a! {, G
从端设备驱动职责:
  • i2c_client结构体具有设备的I2C总线地址以及适配器的驱动程序指针
    9 n/ Q+ b1 [# ?7 X7 l+ ]- k
当用户程序发出文件操作申请I2C事务时
  • i2C_transfer (i2C-core.c) 调用 adap_algo_master_xfer,数据或消息以i2c_msg结构体传入。
  • 适配器对硬件I / O地址进行读/写操作,实现底层的I2C读写设备操作。
    7 l3 C/ J- Q6 @3 I7 Z* ~
从应用程序直到底层的大致交互流程如下:
微信图片_20200625152207.png 总结一下* \2 G: N0 j' I. j  o- E0 M: X
I2C总线子系统在Linux内核中总线模型分为主/从两端,主端主要有适配器以及适配器驱动负责管理总线,从端主要有从设备抽象以及设备驱动,实现具体的从设备应用。主端适配器以两种形式存在于内核代码中,分为PCI桥接适配器或者platform_device形式。从总体理解I2C子系统的驱动模型,以及相应主要数据结构之间的关系,将有助于开发调试驱动程序,快速定位问题。
+ r4 {4 D. e' g; L' D6 Y1 j
收藏 评论0 发布时间:2020-6-25 15:28

举报

0个回答

所属标签

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版