(请保留->发布地址: http://yiiyee.cn/blog/author/luobing/ )
SMBus是1995年由Intel提出的一种双线通信专利技术,它完全符合系统管理总线规范1.1版,与I2C串行总线兼容。与当前流行的高速串行协议相比,SMBus的速度比较慢,但因其使用硬件少,支持此协议的产品非常多,在当前的计算机行业仍然有很大的应用面。
在开发双网隔离计算机的过程中,需要解决三方通信问题。即Windows/Linux层App、控制卡Firmware和BIOS(包括BIOS oprom)三方的通信。实际上需要解决的是控制卡Firmware与Windows/Linux层App、控制卡Firmware与BIOS通信的问题。
最早我采用PCI/PCIE芯片。问题是厂商提供的驱动不完善,总是有各种问题;加上芯片上的控制引脚不够,无法实现产品目标。
后期换为C8051F320,价格也比较合适。底层的通信(控制卡Firmware与BIOS)定为SMBUS总线通信方式;上层的通信则使用了USB HID协议。
由此开始了我辗转在各种平台上开发SMBUS通信代码的历程。
1 SMBUS协议简介
SMBus标准是以Philips公司的I2C总线为基础,面向于不同系统间的通信。随着其标准的不断完善与更新,它已经广泛的应用于IT产品之中。从2.0开始,SMBus又被规范到7层OSI网络模型的前三层物理层、数据链层和网络层之中。
SMBUS数据线SDA和时钟线SCL都是双向的。SMBus 接口的工作电压可以在3.0V 和5.0V 之间,总线上不同器件的工作电压可以不同。SCL(串行时钟)和SDA(串行数据)线是双向的,必须通过一个上拉电阻或等效电路将它们连到电源电压。连接在总线上的每个器件的SCL 和SDA 都必须是漏极开路或集电极开路的,因此当总线空闲时,这两条线都被拉到高电平。
SMBus的标准传输速率是1OOKHz~200KHz。但实际上最大可达系统时钟频率的十分之一。这取决于用户的设置。当总线上接有不同速度的器件时。可以采用延长SCL低电平时间的方法来同步它们之间的通讯。
有两种可能的数据传输类型:从主发送器到所寻址的从接收器(写)和从被寻址的从发送器到主接收器(读)。
两种数据传输是都由主器件启动,主器件还在SCL 上提供串行时钟。SMBus 接口可以工作在主方式或从方式,总线上可以有多个主器件。
如果两个或多个主器件同时启动数据传输,仲裁机制将保证有一个主器件会赢得总线。协议规定:任何一个发送起始条件(START)和从器件地址的器件就成为该次数据传输的主器件。
更多的细节可以找相关的Spec读一读。
2 实验设计
遇到的最大问题是,如何找一个支持SMBUS的设备来进行访问。我用过两种方法,一是写代码直接访问内存的SPD;二是自己做一个支持SMBUS协议的控制板卡。
在我实际开发的过程中,控制卡插在PCI/PCIE槽上(并非PCIE设备,只是取电)。控制卡固件实现了SMUBS协议的访问支持,PC上的程序(主要是BIOS或者BIOS Oprom)可以通过SMBUS与之通信。 之所以能够这样访问,也是因为PCI协议上支持了SMBUS。在PCI提供的引脚中,提供了SMBCLK和SMBDAT。
这样就从硬件层面上打通了PC与控制卡的SMBUS通信通道。我一般采用的方式是PC机作为主,控制卡作为从机工作。在之前的博客《UEFI开发探索07 – 关于SMBUS的开发故事》中,我较为详细的介绍了控制卡的工作方式。
当然,在没有控制卡的时候,我也是用程序去读内存SPD,来确定代码是否工作正常。一般内存的SMBUS地址为A0、A2…,找一个芯片组的Spec查一下就知道了。
3 读写过程
对照SMBus协议,主机(PC机)产生时钟信号和终止信号,从机(控制卡固件)提供数据。从应用考虑,存在主机“读”与“写”,即主机读从机和主机写从机两个工作。
主机读从机采用的是复合格式,如图3所示。图中SLA是指从机地址,通过查阅C8051F32x的编程手册,可以知道其我所使用控制卡的SMBUS地址是0xF0。程序流程可以概括如下:
I 主机发送Slave address和数据字节1;
II 总线上产生的数据发送流程如图所示;
III 主机接收数据字节2并发送停止命令。
如图4,给出了详细的主机写从机时序图。概述其对应的程序流程如下:
I 主机发送Slave address、Register Index、欲写的数据字节,发送写命令;
II 总线上产生的数据发送流程如图所示;
III 主机发送停止命令。
4 UEFI对SMBUS的支持
在UEFI Spec中我并没有找到支持SMBUS的Protocol,不过,在UDK中可以找到相应的文件。本着实用为先的态度,我就从这儿入手。为什么Spec不提供支持?我目前也没有找到答案。
可以参考的代码为\MdePkg\Include\Protocol\SmbusHc.h。另外,有两个文档:《174_SmbusHostCont.pdf》和《175_SmbusPpi.pdf》,也可以参考。不知道文档是不是有更新,我最早应该是在Sourceforge.net上下载的,现在是不是移到github上去了。有兴趣的可以找找。
当我看到Protocol说明的时候,眼泪都快出来了。被汇编折腾这么多年,读着各种时序写代码,换一代主板又得重写一次的日子终于结束了。
这就是让我泪流满面的Protocol:(亲人哪!)
SMBus协议本身比较简单,提供的函数也很容易懂。我们主要关注Execute()函数,其函数原型如下:
我所使用的控制卡SlaveAddress=0xF0,Command和Operation参数可以参考《175_SmbusPpi.pdf》,也即图中所说的Intel Platform Innovation Framework for EFI SMBus PPI Specification。
根据说明,代码比较容易编写了。我主要实现了主机读和主机写的函数。
5 编译运行
在TianoCore模拟环境中是没法运行的,因为实际上没有此硬件存在。手边也没有以前调试用的控制卡,我只能随便找台机器演示下运行效果了。
从程序打印的信息可以看出,这是很久以前我为项目所做的开发工具。与用户交互的代码都没改,直接移植过来的。最早是用AMI Aptio开发的,之后才移植到UDK上了。
程序中固定了访问控制卡的地址0xf0,如果需要访问内存SPD,改为0xA0、0xA2…就可以了。什么时候到公司找一块控制卡,实际运行下,再来补照片吧。
6 One More Thing
从与方正开发双网隔离计算机(945芯片组)开始,一直到与联想开发H110芯片组的产品,Smbus的调试工具不断更新。最早是Legacy bios,每块主板的访问时序都有点不同。我只能请BIOS工程师帮我找源码里的smbus访问代码,自己修改移植。
直到UEFI出现,一劳永逸的解决了这个问题。再也不用每次开发一个项目,都要先去准备调试工具了。
最早的代码都是用汇编写的,后面慢慢的改为了C混合汇编,里面有不少访问硬件和混合编程的小技巧。
以后不大可能再去用这些代码了,读读还是比较有意思的。所以把我开发的最后一款非UEFI的SMBus调试工具的代码也一起上传了,防止以后找不到。
(补上程序在实际机器上的运行照片)
首先将控制卡的内部版本读取处理,协议是我很久以前制定的。当主机程序读取Slaveaddress=0xf0,Register=0x01~0x04的时候,控制卡固件把相应的内容返回去。
另外,控制卡内部维持了一个一字节的内部数据,不同的位(D0~D7)代表不同的含义。D7为保留位,允许主机程序设置、清除、读取。通过SmbusWrite来访问此内部字节:
1) 向Slaveaddress=0xf0,register=0x00写入字节0xB,则控制卡固件返回D7的内容;
2) 向Slaveaddress=0xf0,register=0x00写入字节0xD,则控制卡固件将D7设置为1,返回的内容无效;
3) 向Slaveaddress=0xf0,register=0x00写入字节0xC,则控制卡固件将D7设置为0,返回的内容无效;
图中演示了置位D7的过程。
百度云链接:https://pan.baidu.com/s/1gccSosw8_UAGTI5gZPnLCA
取码:dx23
代码在 14 SMBus HC-tool下。
/Luo4: UEFI下SMBus例子;
/SY_Intel_SMB(通用的sy底层调试工具): Legacy BIOS的Smbus工具,支持
intel平台。Borland C++3.1编译
4,710 total views, 1 views today