对i2c子系统的理解

    驱动i2c控制器归根到底是对IIC控制器的寄存器进行读写,因此,理解了linux中是怎样通过层层调用来操作IIC的寄存器,便理解了整个IIC子系统的轮廓。
下面以公司使用的重力传感器(bma250)驱动为例来描述这个轮廓。
首先介绍三个比较重要的驱动文件:
bma250.c(Drivers/Gsensor/),I2c_core.c(Drivers/I2c/),I2c-sunxi.c(/Drivers/I2c/busses)。
    bma250.c与外接的传感器bma250有关(即client端), I2c_sunxi.c与主控的IIC控制器有关(即adapter端,是对IIC寄存器操作的所在文件), I2c_core.c则起到为前面两个文件进行关联的作用(实际上就是总线管理的作用,而这里是IIC总线)。区分开的一个很大的好处便是移植性强,比如bma250.c可以用于一切支持linux的硬件平台,而 I2c_sunxi.c可用于A10的所有IIC外围器件,硬件变动下, I2c_core.c则基本不用改变。
     在I2c_sunxi.c的模块初始化中定义了一个平台设备,用于获取A10的IIC寄存器地址和中断向量,而在平台设备匹配成功后调用的 i2c_sunxi_probe函数中,初始化了一个 i2c_adapter结构体,这个结构体用于描述A10的IIC控制器,并通过 i2c_add_numbered_adapter函数向IIC总线添加了添加了这个adapter。 i2c_adapter结构体有个很重要的成员,即 i2c_algorithm结构,该结构下又有一个很重要的成员: master_xferI2c_sunxi.c在初始化 i2c_adapter时将 master_xfer初始化为 i2c_sunxi_xfer,而 i2c_sunxi_xfer正是对A10的IIC寄存器进行操作的开始。在此函数中,通过直接操作IIC寄存器,使能了IIC中断,发出了IIC的开始信号,并通过 wait_event_timeout函数让进程进入休眠,接下来通过层层中断(比如IIC开始信号发出完成后会发生中断,发送完一个字节也会发生中断)将 i2c结构体中的 msg数据发送出去,此处 i2ci2c_adapter结构体中的 algo_data成员,要发送的数据都放在这个msg结构体里。
    因此,只需要注册并调用 i2c_adapter->algo->smbus_xfer,便可以让IIC控制器按要求发出或接收数据。
     I2c_core.c在完成建设IIC总线的同时,还完成了对 i2c_adapter->algo->smbus_xfer的封装,即 i2c_transfer,而 i2c_smbus_read_i2c_block_data之类的函数又对i2c_transfer进行了封装。因而只需调用 i2c_smbus_read_i2c_block_data等函数,并传入对应的 i2c_adapter,便可以让控制器按要求发出或接收数据。
    在 bma250.c的模块初始化中,注册了一个 i2c_driver:bma250_driver,该步骤引发了bma250(client)与A10的IIC接口(adapter)的匹配。匹配中一个非常关键的因素是bma250_driver中的 address_list,即bma250的IIC地址,新版本的内核隐藏了这一步的实现,变得自动化,只需要给出的 address_list正确,硬件电路正确,linux的IIC总线上挂有对应的adapter,变可以匹配成功,匹配成功后,linux新建了一个client结构,并将结构里的adapter初始化为匹配成功的那个adapter,至此,总线上,bma250便与A10的IIC接口配对,bma250.c中的函数所用到的参数adapter,都是配对成功的那个adapter.
    这里说一下旧版本(2.6和2.6之前)内核client和adapter的匹配过程。在调用IIC驱动注册函数时( i2c_add_driver),引发了 i2c_probe函数的调用,在该函数中,把注册在IIC总线上的adapter逐个拿出来做实验,实验过程如下:将IIC有可能的地址0~0x7F,逐个与 address_list里的地址进行对比,如果相同则调用 i2c_smbus_xfer,该函数实际操作了目前这个被实验中的adatper的寄存器,发出信号与bma250通讯。若bma250发出了应答信号,则匹配成功。匹配的实现算法与硬件无关,并且算法应该是比较成熟了,所以新内核将其直接拿来用,并将其隐藏起来,不再需要驱动开发者重写过匹配算法。
    到这里总结一下: I2c_core.c建设了一条IIC总线,I2c-sunxi.c向总线添加了一个adapter,而 bma250.c通过向总线注册一个IIC驱动,探测总线上适合(硬件上连接)的adapter,探测成功后bma250便与A10建立联系, bma250.c通过调用 I2c_core.c的函数从而调用I2c-sunxi.c里的函数,完成了IIC硬件上的实现。
     下面以 bma250.c为例,简单说一下上层到下层的调用过程。向总线注册完IIC驱动后,将匹配到的adapter作为后续函数的参数。Bma250驱动通过一工作队列定时调用函数 bma250_read_accel_xyz,该函数读出了bma250里的三轴数据,通过输入子系统,将数据上报给系统。整个调用路线如下:
bma250_read_accel_xyz → bma250_smbus_read_byte_block →i2c_smbus_read_i2c_block_data(进入I2c_core.c)→ i2c_smbus_xfer → adapter->algo->smbus_xfer(i2c_sunxi_xfer) → i2c_sunxi_do_xfer(引发中断)→ i2c_sunxi_handler(IIC中断函数)→i2c_sunxi_core_process→aw_twi_put_byte/aw_twi_get_byte等 →readl/ writel(此函数正是操作A10寄存器的函数)
这便是整个IIC子系统的实现轮廓。


以下总结编写IIC驱动所需做的工作:
1、通过平台设备或其他手段获取主控IIC控制器的资源(寄存器地址和中断向量号),申请中断并实现中断函数。
2、向IIC总线添加一个初始化好的 i2c_adapter结构体。而初始化一个 i2c_adapter结构体时最重要的一步是实现 i2c_adapter中的 algo成员,在algo的成员函数中通过操作寄存器,发出IIC起始信号引发后续数据的发送与接收(通过中断,里面也是通过操作寄存器实现)。
3、向IIC总线添加一个初始化好的 i2c_driver结构体。初始化时比较重要的是给出IIC设备的地址。在其 probe函数中完成向应用层接口的初始化,如初始化一个输入子系统,用于上报数据。
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值