I2C從驅動到應用(下篇)

I2C的應用實例一: 利用i2c讀取SPD的信息
緩存

下面以龍芯BIOS爲例,介紹如何利用I2C讀取內存條上的SPD信息。
1. 硬件鏈接
龍芯內部的i2c控制器包括時鐘發生器、字節命令控制器、狀態寄存器、發送寄存器、接收寄存器,結構以下圖:
 wKiom1YqED3zY94zAADMVttYhXI379.jpg

主要的寄存器介紹以下:
 wKioL1YqEIOAkQPCAAEo34do7rE662.jpg
ide

wKiom1YqEGbSowk8AAEFu-W3Xxg957.jpg

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讀寫的流程來處理這些差別。


內存

相關文章
相關標籤/搜索