|
本帖最后由 lophyxp 于 2017-1-21 18:13 编辑
心映真的空间
苦心励志 技术强国
让我们面对现实 让我们忠于理想
欢迎来到唐刚的首页
PMON 中读取 DDR 内存 SPD 信息的代码
龙芯相关 » PMON 研究 » PMON 中读取 DDR 内存 SPD 信息的代码
本文讲述用I2C协议从内存条上的SPD(eeprom)中读取内存参数。
SPD 信息简述
SPD(eeprom)中,定义了很多参数,这里给出一个简单的说明。
第0字节 表示厂商使用的字节数。
第1字节 表示EERPOM存储容量。
第2字节 表示内存类型。
第3字节 表示行地址位数。
第4字节 表示列地址位数。
第5字节 表示排数。
第6字节 表示数据宽度(低字节)。
第7字节 表示数据宽度(高字节)。
第8字节 表示信号电平。
第9字节 表示SDRAM最高时钟频率。
第10字节 表示SDRAM访问时间。
第11字节 表示配置类型。
第12字节 表示刷行率/类型。
第13字节 表示最小SDRAM颗粒数据宽度。
第16字节 表示支持突发传输长度。
第17字节 表示逻辑BANK数。
第18字节 表示CAS延时。
第23字节 表示SDRM时钟。是2的最大指数倍。
第24字节 表示SDRAM访问时间。
第34字节 表示输入数据建立时间。
第35字节 表示输入数据保持时间。
第62字节 表示SPD版本号。
其它的字节,就要参考SPD文档了。后面一大段程序就是实现了读取这些参数,然后根据这些参数来设置龙芯内存的SDRAM寄存器。
PMON 中对内存SPD读取及处理的方法
读取spd
● (offset 3)读取行地址数目
● (offset 4)读取列地址数目
● 根据行列地址数目判断ddrtype
● (offset 31)读取每个片选的容量
– 用该值初始化内存大小tmpsize
● (offset 17)读取芯片bank,一般为4
● (offset 5)读取片选数(相当于module数)
– 按照片选数,计算内存大小tmpsize
– 按照读出的片选数设置sdcfg寄存器
● (offset 6)读取位宽
● 其中sdcfg的初始值是按照ddr333设置的
(注释表明,没有详细验证)
初始化内存控制器
● 经过spd的读取过程我们获得sdcfg配置和
内存大小memsize
● 设置CPU内部sdcfg寄存器(0x1ff00008)
● 按照memsize设置内存窗口
● 1ff0 0000偏移开始的四个寄存器
– 0x10/0x20(mem window base)
– 0x18/0x28(mem window size)
– e.g. 128MB内存
● 0x10=0,0x18=128MB,0x20=512MB,0x28=0
– e.g. 512MB内存
● 0x10=0,0x18=256MB,0x20=512MB,0x28=256MB
– 有很多的nop,调试试验的结果
代码讲解
- /*
- * Now determine DRAM configuration and size by
- * reading the I2C EEROM (SPD) on the DIMMS (DDR)
- */
- PRINTSTR("DIMM read\r\n")
- /* only one memory slot, slave address is 10100001b */
- li a1, 0x0
- 1:
- li a0, 0xa1 /* a0: slave address, a1: reg index to read */
- bal i2cread
- nop
- /* save a1 */
- move t1, a1
- /* print */
- move a0, v0
- bal hexserial
- nop
- PRINTSTR("\r\n")
- /* restore a1 */
- move a1,t1
- addiu a1,a1,1
- li v0, 0x20
- bleu a1, v0, 1b /* repeat for 32 times */
- nop
- li msize, 0 /* msize is register s2 */
- /* set some parameters for DDR333
- rank number and DDR type field will be filled later
- to check: fix TCAS?
- */
- li sdCfg, 0x341043df /* sdCfg is register s6 */
- /* read DIMM memory type (must be DDRAM) */
- #if 0
- li a0,0xa1
- li a1,2
- bal i2cread
- nop
- bne v0,7,.nodimm
- nop
- PRINTSTR("read memory type\r\n")
- #endif
- /* read DIMM number of rows */
- li a0, 0xa1
- li a1, 3
- bal i2cread
- nop
- move a0, v0 // v0 is the return value register
- subu v0, 12
- move s1, v0 // save for later use
- bgtu v0, 2, .nodimm // if v0 > 2 then jump to .nodimm
- nop
- PRINTSTR("read number of rows\r\n")
- 2: /* read DIMM number of cols */
- li a0, 0xa1
- li a1, 4
- bal i2cread
- nop
- subu v0, 8 // v0 saved the return value
- bgtu v0, 4, .nodimm
- nop
- // read and check ddr type, the combination of t1 and v0 represents a ddr type
- move t1, s1
- bne t1, 0, 10f
- nop
- bne v0, 2, 20f
- nop
- li v0, 0
- b .ddrtype
- nop
- 20: bne v0, 1, 21f
- nop
- li v0, 1
- b .ddrtype
- nop
- 21: bne v0, 0, 22f
- nop
- li v0, 2
- b .ddrtype
- nop
- 22: bne v0, 3, 33f
- nop
- li v0, 3
- b .ddrtype
- nop
- 10: bne t1, 1, 11f
- nop
- bne v0, 3, 20f
- nop
- li v0, 4
- b .ddrtype
- nop
- 20: bne v0, 2, 21f
- nop
- li v0, 5
- b .ddrtype
- nop
- 21: bne v0, 1, 22f
- nop
- li v0, 6
- b .ddrtype
- nop
- 22: bne v0, 4, 33f
- nop
- li v0, 7
- b .ddrtype
- nop
- 11: bne t1, 2, 33f
- nop
- bne v0, 4, 20f
- nop
- li v0, 8
- b .ddrtype
- nop
- 20: bne v0, 3, 21f
- nop
- li v0, 9
- b .ddrtype
- nop
- 21: bne v0, 2, 33f
- nop
- li v0, 10
- b .ddrtype
- nop
- 33: PRINTSTR("DDR type not supported!\r\n");
- 34: b 34b
- nop
- .ddrtype:
- #bit 25:22 is DDR type field
- sll v0, 22
- and v0, 0x03c00000
- or sdCfg, v0
- /* read DIMM memory size per side */
- li a0, 0xa1
- li a1, 31
- bal i2cread
- nop
- beqz v0,.nodimm
- nop
- sll tmpsize,v0,22 # multiply by 4M
- PRINTSTR("read memory size per side\r\n")
- 2: /* read DIMM number of blocks-per-ddrram */
- li a1,17
- bal i2cread
- nop
- beq v0,2,2f
- nop
- bne v0,4,.nodimm
- nop
- PRINTSTR("read blocks per ddrram\r\n")
- 2: /* read DIMM number of sides (banks) */
- li a1,5
- bal i2cread
- nop
- beq v0,1,2f
- nop
- bne v0,2,.nodimm
- nop
- sll tmpsize,1 # msize *= 2
- or sdCfg, 0x1<<27
- PRINTSTR("read number of sides\r\n")
- 2: /* read DIMM width */
- li a1,6
- bal i2cread
- nop
- bleu v0,36,2f
- nop
- bgtu v0,72,.nodimm
- nop
- PRINTSTR("read width\r\n")
- 2: addu msize,tmpsize
- b 2f
- nop
- .nodimm:
- move dbg,a0 // dbg is s5
- PRINTSTR ("\r\nNo DIMM in slot ")
- move a0,dbg
- bal hexserial
- nop
- PRINTSTR("\r\n")
- move a0,dbg
- #li msize,0x10000000
- #li sdCfg,0x3d9043df #~133MHz
- li msize,0x20000000
- li sdCfg,0x3d5043df #~133MHz
- 2:
- PRINTSTR("DIMM SIZE=")
- move a0,msize
- bal hexserial
- nop
- PRINTSTR("\r\n")
- li t0, 0xbff00008
- sd sdCfg, 0(t0)
- nop
- nop
- /* (uint32_t *)0xbfe00040 = 0x80000000
- * means only address below 1G will be sent to CPU
- */
- lui t0, 0xbfe0
- li t1, 0x80000000
- sw t1, 0x40(t0)
- nop
- #### gx 2006-03-17: mode ####
- #li t1,0x20
- li t1,0x28
- li t0, 0xbff00000
- sd t1,0(t0)
- nop
- li t1,0x0
- li t0, 0xbff00000
- sd t1,0x30(t0)
- nop
- ##fixed base address reg##
- sd zero, 0x10(t0)
- nop
- lui t1,0x2000
- sd t1,0x20(t0)
- nop
- li t1, 0x10000000
- blt msize, t1, 1f
- nop
- ####bigger than 256MB####
- sd t1, 0x18(t0)
- nop
- move a0, msize
- subu a0, t1
- nop
- nop
- nop
- sd a0, 0x28(t0)
- nop
- b 2f
- 1:
- nop
- nop
- sd msize, 0x18(t0)
- nop
- nop
- nop
- sd zero, 0x28(t0)
- nop
- nop
- nop
- 2:
- PRINTSTR("sdcfg=");
- move a0,sdCfg
- bal hexserial
- nop
- PRINTSTR("\r\n");
- PRINTSTR("msize=");
- move a0,msize
- bal hexserial
- nop
- PRINTSTR("\r\n")
- skipdimm:
- li t1,0 # accumulate pcimembasecfg settings
- /* set bar0 mask and translation to point to SDRAM */
- sub t0,msize,1
- not t0
- srl t0,BONITO_PCIMEMBASECFG_ASHIFT-BONITO_PCIMEMBASECFG_MEMBASE0_MASK_SHIFT
- and t0,BONITO_PCIMEMBASECFG_MEMBASE0_MASK
- or t1,t0
- li t0,0x00000000
- srl t0,BONITO_PCIMEMBASECFG_ASHIFT-BONITO_PCIMEMBASECFG_MEMBASE0_TRANS_SHIFT
- and t0,BONITO_PCIMEMBASECFG_MEMBASE0_TRANS
- or t1,t0
- or t1,BONITO_PCIMEMBASECFG_MEMBASE0_CACHED
- /* set bar1 to minimum size to conserve PCI space */
- li t0, ~0
- srl t0,BONITO_PCIMEMBASECFG_ASHIFT-BONITO_PCIMEMBASECFG_MEMBASE1_MASK_SHIFT
- and t0,BONITO_PCIMEMBASECFG_MEMBASE1_MASK
- or t1,t0
- li t0,0x00000000
- srl t0,BONITO_PCIMEMBASECFG_ASHIFT-BONITO_PCIMEMBASECFG_MEMBASE1_TRANS_SHIFT
- and t0,BONITO_PCIMEMBASECFG_MEMBASE1_TRANS
- or t1,t0
- or t1,BONITO_PCIMEMBASECFG_MEMBASE1_CACHED
- sw t1,BONITO_PCIMEMBASECFG(bonito)
- /* enable configuration cycles now */
- lw t0,BONITO_BONPONCFG(bonito)
- and t0,~BONITO_BONPONCFG_CONFIG_DIS
- sw t0,BONITO_BONPONCFG(bonito)
- PRINTSTR("Init SDRAM Done!\r\n");
- =====================
- 解释:
- /* only one memory slot, slave address is 10100001b */
- li a1, 0x0
- 1:
- li a0, 0xa1 /* a0: slave address, a1: reg index to read */
- bal i2cread
- nop
- 上面这段代码,把 0 设置给 a1,然后把 0xa1 设置给 a0,然后就调用 I2C 的子函数来读取数据。a0 和 a1 寄存器是 i2cread 这个函数的两个参数。
复制代码
i2cread 函数的实现
下面来看 i2cread 函数的内容
- /* a0: slave address
- a1: reg off
- */
- LEAF(i2cread)
- /* set device address */
- li v0, 0xbfd00000 + SMBUS_HOST_ADDRESS
- li a0, 0xa1
- sb a0, 0(v0);
- /* store register offset */
- li v0, 0xbfd00000 + SMBUS_HOST_COMMAND
- sb a1, 0(v0);
- /* read byte data protocol */
- li v0, 0x08
- li v1, 0xbfd00000 + SMBUS_HOST_CONTROL
- sb v0, 0(v1);
- /* make sure SMB host ready to start, important!--zfx */
- li v1, 0xbfd00000 + SMBUS_HOST_STATUS
- lbu v0, 0(v1)
- andi v0,v0, 0x1f
- beqz v0,1f
- nop
- sb v0, 0(v1)
- lbu v0, 0(v1) #flush the write
- 1:
- /* start */
- li v1, 0xbfd00000 + SMBUS_HOST_CONTROL
- lbu v0, 0(v1)
- ori v0, v0, 0x40
- sb v0, 0(v1);
- /* wait */
- li v1, 0xbfd00000 + SMBUS_HOST_STATUS
- li a1, 0x1000
- 1:
- #if 1
- /* delay */
- li a0, 0x1000
- 2:
- bnez a0,2b
- addiu a0, -1
- #endif
- addiu a1, -1
- beqz a1, 1f
- nop
- lbu v0, 0(v1)
- andi v0, SMBUS_HOST_STATUS_BUSY
- bnez v0, 1b #IDEL ?
- nop
- 1:
- li v1, 0xbfd00000 + SMBUS_HOST_STATUS
- lbu v0, 0(v1)
- andi v0,v0, 0x1f
- beqz v0,1f
- nop
- sb v0, 0(v1) #reset
- lbu v0, 0(v1) #flush the write
- 1:
- li v1, 0xbfd00000 + SMBUS_HOST_DATA0
- lbu v0, 0(v1)
- jr ra
- nop
- END(i2cread)
- =====================
- 解释:
- /* set device address */
- li v0, 0xbfd00000 + SMBUS_HOST_ADDRESS
- li a0, 0xa1
- sb a0, 0(v0);
- 上面代码是输出从设备的地址。
- /* store register offset */
- li v0, 0xbfd00000 + SMBUS_HOST_COMMAND
- sb a1, 0(v0);
- 上面代码是输出从设备的寄存器偏移量。
- /* read byte data protocol */
- li v0, 0x08
- li v1, 0xbfd00000 + SMBUS_HOST_CONTROL
- sb v0, 0(v1);
- /* make sure SMB host ready to start, important!--zfx */
- li v1, 0xbfd00000 + SMBUS_HOST_STATUS
- lbu v0, 0(v1)
- andi v0,v0, 0x1f
- beqz v0,1f
- nop
- sb v0, 0(v1)
- lbu v0, 0(v1) #flush the write
- 上面代码是查看数据总线是否准备好数据。
- 1:
- /* start */
- li v1, 0xbfd00000 + SMBUS_HOST_CONTROL
- lbu v0, 0(v1)
- ori v0, v0, 0x40
- sb v0, 0(v1);
- /* wait */
- li v1, 0xbfd00000 + SMBUS_HOST_STATUS
- li a1, 0x1000
- 1:
- #if 1
- /* delay */
- li a0, 0x1000
- 2:
- bnez a0,2b
- addiu a0, -1
- #endif
- addiu a1, -1
- beqz a1, 1f
- nop
- lbu v0, 0(v1)
- andi v0, SMBUS_HOST_STATUS_BUSY
- bnez v0, 1b #IDEL ?
- nop
- 上面代码是查看总线是否在忙状态。
- 1:
- li v1, 0xbfd00000 + SMBUS_HOST_STATUS
- lbu v0, 0(v1)
- andi v0,v0, 0x1f
- beqz v0,1f
- nop
- sb v0, 0(v1) #reset
- lbu v0, 0(v1) #flush the write
- 1:
- li v1, 0xbfd00000 + SMBUS_HOST_DATA0
- lbu v0, 0(v1)
- jr ra
- nop
- 上面代码是已经把命令成功发送出去,然后成功地读取回来数据,保存在v0寄存里。
复制代码
通过上面的子函数,就可以通过I2C总线去读取内存条上的EEPROM参数,以便后面进行内存初始化。
在这里第一次读取是第一个字节。
|
|