S3C4510B的UCLINUX I2C DRIVER 

作者:陆麟
转载请征得作者同意.
2003.5.9
patched 2003.8.19



S3C4510B提供了I2C BUS. 而目前市面上的LINUX则没有针对S3C4510的I2C BUS的驱动.
UCLINUX下的I2C概念分如下3类.
algorithm: 对I2C BUS的操作算法.
i2c_driver: BUS可能连接了数个能识别I2C协议的设备. i2c_driver提供对这些设备的控制.
adapter: 用于表名1个BUS的存在.

这3个概念其实仍然混乱. 我并不清楚LINUX是如何理清这些I2C逻辑的. 但是, 最终的结果是提供一个能让其他组件使用i2c_master_send(...); i2c_master_recv(...);的接口. 因此发展了一下自己的开发思路.

1. 用adapter表示1条I2C BUS. 如果有多个I2C BUS, 用多个adapter数据结构表示.
2. adapter使用S3C4510的I2C控制器对应的algorithm, 这个algorithm不是bit algorithm,也无法使用现有的任何LINUX现存的algorithm. 需要自行开发. 因为S3C4510并不让手动操作SCL和SDL.
3. 用i2c_driver来控制各种I2C BUS上的DEVICE. i2c_driver是个LINUX提供的数据结构, i2c_driver可以控制N个识别出来的I2C BUS.
4. 我们需要一个BUS ENUMERATOR. 用于发现BUS上连接的设备. 但是LINUX并没有BUS ENUMERATOR的概念. 需要自己去发展. 我在DRIVER初始化时,要对每个BUS进行一个BUS RESET动作,BUS RESET包括初始化BUS,申请中断等一些工作. 然后手工调用i2c_driver数据结构的attach_adapter函数, 来模拟一个找到BUS的动作.
5. 在attach_adapter别调用时, 需要ENUMERATE BUS上的DEVICE. 这样可以模拟出一PLUG AND PLAY的情况. 每次搜索到一个设备, 我们就调用i2c_attach_client一次. 从实际开发来讲. 我们预先知道有几个I2C DEVICE. 因此, 可以减少PROBE的次数. 直接从attach_adapter函数中调用i2c_attach_client N次(N=设备个数)就可.
6. 在所有设备均搜索到位后,调用i2c_add_driver. 将DRIVER挂到LINUX. 这样, LINUX的I2C的通用接口就能工作了.

以上便是我整理出的I2C BUS DRIVER FOR LINUX/UCLINUX的开发思路.

手头的BUS DRIVER已经通过测试, 并且连接了一个DS1307的TIME KEEPER. 用于提供本地时间的保存.:) 测试成功. BUS运作正常.

原代码涉及公司许可的问题, 无法在此披露. 如果有特殊需要, 也可联系我.

2003.8.19
经过一些交流, 发现本文描述的确实不在点子上. 今天再增加点描述. I2C协议本身分成2部分. 1部分是MASTER, 是发起传送的一方. 1部分是SLAVE, 是接受命令的一方. 多数的实现中1条I2C BUS只有1个MASTER, 而SLAVE最多可以有127个(有支持10BIT SLAVE ADDRESS的规格,不多见). 每个SLAVE靠SLAVE ADDRESS区分. 在上面提到的i2c_attach_client的动作, 是让向I2C CORE注册SLAVE的动作. I2C CORE会管理每条BUS上的SLAVE设备的数组.

I2C驱动在逻辑上被分解成I2C DEVICE DRIVER/I2C DRIVER/I2C DEV, I2C DEVICE DRIVER是SLAVE的驱动. 它知道SLAVE的数据应当如何解释. 例如SENSOR. 它解释SLAVE的输出, 成为温度. I2C DRIVER是BUS的驱动. 驱动的是I2C BUS. 不同的CHIP或许会有不同的操作. 有些CHIP提供SCL/SDL的操作. 而有些则将SCL/SDL操作归结成START/STOP/RESTART/ACK等操作,封装到某个REGISTER. 尤其是后面一种状况, I2C DRIVER一定是要自己动手写的. S3C4510B就属于这一类. I2C DEV是UCLINUX对USER MODE APPLICATION暴露的接口. 是独立的接口.

i2c_master_send(...); i2c_master_recv(...);等接口是LINUX CORE使用的接口, 而不是APPLICATION使用的接口.
APPLICATION需要使用 MAJOR=89 的设备来获得与I2C BUS的沟通. 而MINOR号码则是根据adapter注册来变化. I2C DEV在I2C BUS驱动调用attach_adapter时得到通知. 来更新自己维护的数据. 使得DEVICE MINOR号码能对应到相应的I2C的BUS.

操作I2C BUS需要的是对I2C DEV进行ioctl操作. 这点要求APPLICATION预先知道BUS ID. 也就是APPLICATION PROGRAMM必须知道(89 ,0)或者说(89,x)对应的到底是哪条I2C BUS.
ioctl的CMD应该是I2C_RDWR, 这样就可以对I2C BUS发送读写命令. 从KERNEL2.4.9以后的版本开始, I2C DEV支持SEEK操作. 但是SEEK操作对于I2C的SLAVE设备们意义并不大. 因此在UCLINUX/LINUX的I2C DEV驱动实际代码中会始终返回-ESPIPE. 在我接触的一个客户, 要求I2C支持随机文件操作. 解释了N次还是不懂. 真是[email protected]#$%^&*.

BUS上的SLAVE可能是个EEPROM. 这片EEPROM可以有N BYTES容量. I2C上的SLAVE ADDRESS和EEPROM的ADDRESS绝对不可以混淆. 要寻址EEPROM, 每个EEPROM厂商都有自己的做法. 有DATA SHEET为参考.

上文中虽然说到要模拟一个PNP动作, 搜索I2C SLAVES, 但是I2C协议不是PNP协议, SLAVE并没有自我识别机制. 因此, 最多只是检查一下对应地址是否有响应而已. 而无法知道SLAVE地址对应的是什么设备, PROGRAMMER应当在操作前明确的知道SLAVE地址对应的到底是个什么东西. 说不定对面SLAVE连接的是原子弹的爆炸按钮. 千万不可乱操作. :D