I2C的应用实例一: 利用i2c读取SPD的信息
缓存
下面以龙芯BIOS为例,介绍如何利用I2C读取内存条上的SPD信息。
1. 硬件链接
龙芯内部的i2c控制器包括时钟发生器、字节命令控制器、状态寄存器、发送寄存器、接收寄存器,结构以下图:
主要的寄存器介绍以下:
ide
2. 利用i2c读取内存SPD信息
以读取芯片类型(DDR2,DDR3,DDR4)为例子,根据SPD规范,SPD偏移为2的寄存器表示DIMM Type,若是它的值是0x08,就表示DDR2,若是值为0xb则表示DDR3。
经过上面的寄存器说明,结合i2c读写的时序河代码,咱们能够写出以下进行i2c读写的流程:
1.初始化控制寄存器;
2.查询状态寄存器,直至TIP=0(表示i2c控制器空闲);
3.发送地址或者偏移到TX寄存器;
4.设置命令寄存器的START bit和读bit,启动传输;
5.查询状态寄存器直到TIP 和busy bit 都为0,读取接收寄存器。
实现的过程以下:函数
dli a1, 2
GET_I2C_NODE_ID_a2
blog
*i2c_send_b*/
/* load device address to write reg offset */
andi v1, a0, 0xfe
li v0, H2LS_I2C0_TXR_REG
sb v1, 0x0(v0)
/* send start frame */
li v1, CR_START | CR_WRITE
li v0, H2LS_I2C0_CR_REG
sb v1, 0x0(v0)
/* waite send finished */
// i2c_wait_tip
li v0, H2LS_I2C0_SR_REG
1:
lb v1, 0x0(v0)
andi v1, v1, SR_TIP
bnez v1, 1b
nop
/* send data to be written */
move v1, a1
li v0, H2LS_I2C0_TXR_REG
sb v1, 0x0(v0)
/* send data frame */
li v1, CR_WRITE
li v0, H2LS_I2C0_CR_REG
sb v1, 0x0(v0)
/* waite send finished */
// i2c_wait_tip
li v0, H2LS_I2C0_SR_REG
1:
lb v1, 0x0(v0)
andi v1, v1, SR_TIP
bnez v1, 1b
nop
/* i2c_read_b */
/* load device address */
ori v1, a0, 0x1
li v0, H2LS_I2C0_TXR_REG
sb v1, 0x0(v0)
/* send start frame */
li v1, CR_START | CR_WRITE
li v0, H2LS_I2C0_CR_REG
sb v1, 0x0(v0)
/* waite send finished */
// i2c_wait_tip
li v0, H2LS_I2C0_SR_REG
1:
lb v1, 0x0(v0)
andi v1, v1, SR_TIP
bnez v1, 1b
nop
/* receive data to fifo */
li v1, CR_READ | CR_ACK
li v0, H2LS_I2C0_CR_REG
sb v1, 0x0(v0)
// i2c_wait_tip
li v0, H2LS_I2C0_SR_REG
1:
lb v1, 0x0(v0)
andi v1, v1, SR_TIP
bnez v1, 1b
nop
/* read data from fifo */
li v0, H2LS_I2C0_RXR_REG
lb a1, 0x0(v0)
/* i2c_stop */
/* free i2c bus */
li v0, H2LS_I2C0_CR_REG
li v1, CR_STOP
sb v1, 0x0(v0)
1:
li v0, H2LS_I2C0_SR_REG
lb v1, 0x0(v0)
andi v1, v1, SR_BUSY
bnez v1, 1b
nop
move v0, a1
jr ra
nop
END(i2cread)
事务
( 1.写寄存器偏移到SPD;2.读指定偏移的SPD。其中上面的每一个步骤均可以再分解成两步:1.写地址到TX 寄存器,start,直到TIP变成0;2.用要读或者写的寄存器偏移填充TX,start,直到TIP变成0 。固然最后是经过清除状态寄存器的busy bit来设置当前控制器从busy状态转移到free状态)。
ip
实现了基本的i2cread读写函数以后,就能够利用它来读取SPD上任意偏移寄存器的信息了,下面摘取了一部分代码:
bal i2cread
nop
//only bit[7:0] used
andi v0, v0, 0xff
/* v0 should be 0xb or 0x8,else error DIMM type */
dli a1, 0x08
beq v0, a1, DDR2
nop
dli a1, 0x0B
beq v0, a1, DDR3
nop
================
上面的代码主要是调用i2cread函数去访问SPD.
I2C的应用实例二: 利用Integrated SMBus 读取HasWell SPD
和龙芯利用本身的i2c控制器读取SPD信息相似,Intel的HasWell处理器芯片系列也提供了SMBus来访问SPD。 HasWell芯片提供了下面的几个寄存器来专门读取SPD和TSOD的信息:
smb_stat: 重要的位段有
smb_rdo:表示在一次SMBus Read命令执行完成后,这个寄存器的接收数据位段保存读到的数据是有效的
smb_wod:写操做完成
smb_busy:表示当前有i2c或者SMBus的命令正在总线上执行
tsod_sa:保存有上次执行的读取TSOD指令的从地址
smb_rdata:当smb_rdo为1时,这些位段表示接收到的有效数据
smb_cmd: 重要的位段有
smb_cmd_trigger:设置这个bit为1以后,才来出发smbus发送命令
smb_word_access:控制是按照byte仍是word来访问device
smb_wrt_cmd:smbus读写控制选择控制的位段
smb_sa:slave address,这个位段决定要访问的SPD或者TSOD
smb_ba:要访问的设备的bus地址
smb_wdata:缓存要经过SPDW指令写入的数据
smb_cntl:重要的位段有
smb_dti (bit31~bit28):指定访问的是SPD仍是TSOD,若是访问的是SPD,须要把这个位段设置成2'b1010
smb_ckovrd: 以防任何等待的事务出现,或者使SMbus芯片从hang状态中解放出来
smb_soft_rst: 软件重启smb 控制器,用来终止全部以前还有待进行的传输事务,软件须要,和smb_ckovrd结合经过设置smb_ckovrd=0 且smb_soft_rst=1,维持35ms,来使得smb 控制器恢复空闲状态。
smb_rst_on_forcest: 控制是否让force self reset 信号重启smb 控制器。
了解了上述的寄存器的功能和 用法以后,咱们就不难整理出基于HasWell处理器的读取SPD的步骤:
初始化SMbus控制器:利用smb_cntl寄存器中的smb_soft_rst和smb_ckovrd字段来重启smbus控制器;
设置要读取的SPD的i2c 总线地址和从地址、设置按照byte访问、访问模式是写入,往smb_wdata里写入要访问SPD上的寄存器的偏移;
把smb_cmd_trigger设置成1,发送smbus command;
查询状态寄存器的smb_wod 和smb_busy bit,直至它们表示写操做已经完成;
设置要读取的SPD的i2c 总线地址和从地址、设置按照byte访问、访问模式是读出;
把smb_cmd_trigger设置成1,发送smbus command;
查询状态寄存器的smb_rdo 和smb_busy bit,直至它们表示读操做已经完成;
读取状态寄存器中的smb_rdata字段,返回给上层程序。
经过总结HasWell和龙芯访问SPD信息的步骤,咱们能够发现控制的流程是相同的,区别就在于不一样的Smbus/i2c控制器里相关寄存器的实现不同,这就要求工程师必定要根据具体的芯片手册,结合I2C/Smubus读写的流程来处理这些差别。
内存