龙芯俱乐部开源技术社区

 找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
热搜: 活动 交友 discuz
查看: 3945|回复: 0

【PMON 研究】【02】PMON 中读取 DDR 内存 SPD 信息的代码

[复制链接]

57

主题

83

帖子

1万

积分

论坛元老

Rank: 8Rank: 8

积分
10412
发表于 2017-1-21 18:10:42 | 显示全部楼层 |阅读模式
本帖最后由 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,调试试验的结果
代码讲解


  1. /*
  2.      * Now determine DRAM configuration and size by
  3.      * reading the I2C EEROM (SPD) on the DIMMS (DDR)
  4.      */
  5.     PRINTSTR("DIMM read\r\n")

  6.     /* only one memory slot, slave address is 10100001b */
  7.     li  a1, 0x0
  8. 1:
  9.     li    a0, 0xa1    /* a0: slave address, a1: reg index to read */
  10.     bal    i2cread
  11.     nop

  12.         /* save a1 */
  13.     move t1, a1

  14.     /* print */
  15.     move a0, v0
  16.     bal  hexserial
  17.     nop

  18.     PRINTSTR("\r\n")   

  19.         /* restore a1 */
  20.     move  a1,t1
  21.     addiu a1,a1,1
  22.     li   v0, 0x20
  23.     bleu  a1, v0, 1b        /* repeat for 32 times */
  24.     nop

  25.     li    msize, 0            /* msize is register s2 */
  26.     /* set some parameters for DDR333
  27.         rank number and DDR type field will be filled later
  28.         to check: fix TCAS?
  29.     */
  30.     li    sdCfg, 0x341043df        /* sdCfg is register s6 */

  31.     /* read DIMM memory type (must be DDRAM) */
  32. #if 0
  33.     li    a0,0xa1
  34.     li    a1,2
  35.     bal    i2cread
  36.     nop
  37.     bne    v0,7,.nodimm
  38.     nop
  39.     PRINTSTR("read memory type\r\n")
  40. #endif

  41.     /* read DIMM number of rows */
  42.     li    a0, 0xa1
  43.     li    a1, 3
  44.     bal    i2cread
  45.     nop   
  46.     move    a0, v0        // v0 is the return value register
  47.     subu    v0, 12
  48.     move    s1, v0        // save for later use

  49.     bgtu    v0, 2, .nodimm        // if v0 > 2 then jump to .nodimm
  50.     nop
  51.     PRINTSTR("read number of rows\r\n")

  52. 2:    /* read DIMM number of cols */
  53.     li    a0, 0xa1
  54.     li    a1, 4
  55.     bal    i2cread
  56.     nop

  57.     subu    v0, 8                // v0 saved the return value
  58.     bgtu    v0, 4, .nodimm
  59.     nop

  60.     // read and check ddr type, the combination of t1 and v0 represents a ddr type
  61.     move    t1, s1
  62.     bne    t1, 0, 10f
  63.     nop
  64.     bne    v0, 2, 20f
  65.     nop
  66.     li    v0, 0
  67.     b    .ddrtype
  68.     nop
  69. 20:    bne    v0, 1, 21f
  70.     nop
  71.     li    v0, 1
  72.     b    .ddrtype
  73.     nop
  74. 21:    bne    v0, 0, 22f
  75.     nop
  76.     li    v0, 2
  77.     b    .ddrtype
  78.     nop
  79. 22:    bne    v0, 3, 33f
  80.     nop
  81.     li    v0, 3
  82.     b    .ddrtype
  83.     nop
  84. 10:    bne    t1, 1, 11f
  85.     nop
  86.     bne    v0, 3, 20f
  87.     nop
  88.     li    v0, 4
  89.     b    .ddrtype
  90.     nop
  91. 20:    bne    v0, 2, 21f
  92.     nop
  93.     li    v0, 5
  94.     b    .ddrtype
  95.     nop
  96. 21:    bne    v0, 1, 22f
  97.     nop
  98.     li    v0, 6
  99.     b    .ddrtype
  100.     nop
  101. 22:    bne    v0, 4, 33f
  102.     nop
  103.     li    v0, 7
  104.     b    .ddrtype
  105.     nop
  106. 11:    bne    t1, 2, 33f
  107.     nop
  108.     bne    v0, 4, 20f
  109.     nop
  110.     li    v0, 8
  111.     b    .ddrtype
  112.     nop
  113. 20:    bne    v0, 3, 21f
  114.     nop
  115.     li    v0, 9
  116.     b    .ddrtype
  117.     nop
  118. 21:    bne    v0, 2, 33f
  119.     nop
  120.     li    v0, 10
  121.     b    .ddrtype
  122.     nop
  123. 33:    PRINTSTR("DDR type not supported!\r\n");
  124. 34:    b    34b
  125.     nop

  126. .ddrtype:
  127.     #bit 25:22 is DDR type field
  128.     sll    v0, 22
  129.     and    v0, 0x03c00000
  130.     or    sdCfg, v0

  131.     /* read DIMM memory size per side */
  132.     li    a0, 0xa1
  133.     li    a1, 31
  134.     bal    i2cread
  135.     nop
  136.     beqz    v0,.nodimm
  137.     nop
  138.     sll    tmpsize,v0,22        # multiply by 4M
  139.     PRINTSTR("read memory size per side\r\n")

  140. 2:    /* read DIMM number of blocks-per-ddrram */
  141.     li    a1,17
  142.     bal    i2cread
  143.     nop
  144.     beq    v0,2,2f
  145.     nop
  146.     bne    v0,4,.nodimm
  147.     nop
  148.     PRINTSTR("read blocks per ddrram\r\n")

  149. 2:    /* read DIMM number of sides (banks) */
  150.     li    a1,5
  151.     bal    i2cread
  152.     nop
  153.     beq    v0,1,2f
  154.     nop
  155.     bne    v0,2,.nodimm
  156.     nop
  157.     sll    tmpsize,1    # msize *= 2   
  158.     or  sdCfg, 0x1<<27
  159.     PRINTSTR("read number of sides\r\n")

  160. 2:    /* read DIMM width */
  161.     li    a1,6
  162.     bal    i2cread
  163.     nop
  164.     bleu    v0,36,2f
  165.     nop
  166.     bgtu    v0,72,.nodimm
  167.     nop
  168.     PRINTSTR("read width\r\n")

  169. 2:    addu    msize,tmpsize
  170.     b    2f
  171.     nop   

  172. .nodimm:
  173.     move    dbg,a0        // dbg is s5
  174.     PRINTSTR ("\r\nNo DIMM in slot ")
  175.     move    a0,dbg
  176.     bal    hexserial
  177.     nop
  178.     PRINTSTR("\r\n")
  179.     move    a0,dbg
  180.     #li  msize,0x10000000
  181.     #li    sdCfg,0x3d9043df    #~133MHz
  182.     li  msize,0x20000000
  183.     li    sdCfg,0x3d5043df     #~133MHz

  184. 2:
  185.     PRINTSTR("DIMM SIZE=")
  186.     move    a0,msize
  187.     bal    hexserial
  188.     nop
  189.     PRINTSTR("\r\n")

  190.     li    t0, 0xbff00008
  191.     sd    sdCfg, 0(t0)
  192.     nop
  193.     nop

  194.     /* (uint32_t *)0xbfe00040 = 0x80000000
  195.      * means only address below 1G will be sent to CPU
  196.      */
  197.     lui    t0, 0xbfe0
  198.     li    t1, 0x80000000
  199.     sw    t1, 0x40(t0)
  200.     nop

  201.     #### gx 2006-03-17: mode ####
  202.     #li    t1,0x20
  203.     li    t1,0x28
  204.     li    t0, 0xbff00000
  205.     sd    t1,0(t0)
  206.     nop
  207.     li    t1,0x0
  208.     li    t0, 0xbff00000
  209.     sd    t1,0x30(t0)
  210.     nop

  211.     ##fixed base address reg##
  212.     sd    zero, 0x10(t0)
  213.     nop
  214.     lui    t1,0x2000
  215.     sd    t1,0x20(t0)
  216.     nop

  217.     li      t1, 0x10000000
  218.         blt     msize, t1, 1f
  219.     nop

  220.     ####bigger than 256MB####
  221.     sd    t1, 0x18(t0)
  222.     nop
  223.     move    a0, msize
  224.     subu    a0, t1
  225.     nop
  226.     nop
  227.     nop
  228.     sd    a0, 0x28(t0)
  229.     nop
  230.     b    2f

  231. 1:
  232.     nop
  233.     nop
  234.     sd    msize, 0x18(t0)
  235.     nop
  236.     nop
  237.     nop
  238.     sd    zero, 0x28(t0)
  239.     nop
  240.     nop
  241.     nop

  242. 2:
  243.     PRINTSTR("sdcfg=");
  244.     move    a0,sdCfg
  245.     bal    hexserial
  246.     nop
  247.     PRINTSTR("\r\n");
  248.     PRINTSTR("msize=");
  249.     move    a0,msize
  250.     bal    hexserial
  251.     nop
  252.     PRINTSTR("\r\n")

  253. skipdimm:

  254.     li    t1,0        # accumulate pcimembasecfg settings

  255.     /* set bar0 mask and translation to point to SDRAM */
  256.     sub    t0,msize,1
  257.     not    t0
  258.     srl    t0,BONITO_PCIMEMBASECFG_ASHIFT-BONITO_PCIMEMBASECFG_MEMBASE0_MASK_SHIFT
  259.     and    t0,BONITO_PCIMEMBASECFG_MEMBASE0_MASK
  260.     or    t1,t0

  261.     li    t0,0x00000000
  262.     srl    t0,BONITO_PCIMEMBASECFG_ASHIFT-BONITO_PCIMEMBASECFG_MEMBASE0_TRANS_SHIFT
  263.     and    t0,BONITO_PCIMEMBASECFG_MEMBASE0_TRANS
  264.     or    t1,t0
  265.     or    t1,BONITO_PCIMEMBASECFG_MEMBASE0_CACHED

  266.     /* set bar1 to minimum size to conserve PCI space */
  267.     li    t0, ~0
  268.     srl    t0,BONITO_PCIMEMBASECFG_ASHIFT-BONITO_PCIMEMBASECFG_MEMBASE1_MASK_SHIFT
  269.     and    t0,BONITO_PCIMEMBASECFG_MEMBASE1_MASK
  270.     or    t1,t0

  271.     li    t0,0x00000000
  272.     srl    t0,BONITO_PCIMEMBASECFG_ASHIFT-BONITO_PCIMEMBASECFG_MEMBASE1_TRANS_SHIFT
  273.     and    t0,BONITO_PCIMEMBASECFG_MEMBASE1_TRANS
  274.     or    t1,t0
  275.     or    t1,BONITO_PCIMEMBASECFG_MEMBASE1_CACHED

  276.     sw    t1,BONITO_PCIMEMBASECFG(bonito)

  277.     /* enable configuration cycles now */
  278.     lw    t0,BONITO_BONPONCFG(bonito)
  279.     and    t0,~BONITO_BONPONCFG_CONFIG_DIS
  280.     sw    t0,BONITO_BONPONCFG(bonito)

  281.     PRINTSTR("Init SDRAM Done!\r\n");

  282. =====================
  283. 解释:
  284.     /* only one memory slot, slave address is 10100001b */
  285.     li  a1, 0x0
  286. 1:
  287.     li    a0, 0xa1    /* a0: slave address, a1: reg index to read */
  288.     bal    i2cread
  289.     nop
  290. 上面这段代码,把 0 设置给 a1,然后把 0xa1 设置给 a0,然后就调用 I2C 的子函数来读取数据。a0 和 a1 寄存器是 i2cread 这个函数的两个参数。
复制代码


i2cread 函数的实现

下面来看 i2cread 函数的内容

  1. /* a0: slave address
  2.    a1: reg off
  3. */
  4. LEAF(i2cread)
  5.     /* set device address */
  6.         li  v0, 0xbfd00000 + SMBUS_HOST_ADDRESS
  7.     li  a0, 0xa1
  8.     sb  a0, 0(v0);

  9.     /* store register offset */
  10.         li  v0, 0xbfd00000 + SMBUS_HOST_COMMAND
  11.     sb  a1, 0(v0);

  12.     /* read byte data protocol */
  13.     li  v0, 0x08
  14.     li  v1, 0xbfd00000 + SMBUS_HOST_CONTROL
  15.     sb  v0, 0(v1);

  16.     /* make sure SMB host ready to start, important!--zfx */
  17.     li  v1, 0xbfd00000 + SMBUS_HOST_STATUS
  18.     lbu v0, 0(v1)
  19.     andi v0,v0, 0x1f
  20.     beqz  v0,1f
  21.     nop
  22.     sb  v0, 0(v1)
  23.     lbu v0, 0(v1)   #flush the write

  24. 1:
  25.     /* start */
  26.     li  v1, 0xbfd00000 + SMBUS_HOST_CONTROL
  27.     lbu v0, 0(v1)
  28.     ori v0, v0, 0x40
  29.     sb  v0, 0(v1);

  30.     /* wait */
  31.     li  v1, 0xbfd00000 + SMBUS_HOST_STATUS
  32.     li  a1, 0x1000
  33. 1:

  34. #if 1
  35.     /* delay */
  36.     li a0, 0x1000   
  37. 2:            
  38.     bnez    a0,2b
  39.     addiu    a0, -1
  40. #endif
  41.     addiu    a1, -1
  42.     beqz    a1, 1f
  43.     nop

  44.     lbu  v0, 0(v1)
  45.     andi v0, SMBUS_HOST_STATUS_BUSY
  46.     bnez  v0, 1b  #IDEL ?
  47.     nop

  48. 1:

  49.     li  v1, 0xbfd00000 + SMBUS_HOST_STATUS
  50.     lbu v0, 0(v1)
  51.     andi v0,v0, 0x1f
  52.     beqz  v0,1f
  53.     nop
  54.     sb  v0, 0(v1)   #reset
  55.     lbu v0, 0(v1)   #flush the write
  56. 1:

  57.     li  v1, 0xbfd00000 + SMBUS_HOST_DATA0
  58.     lbu  v0, 0(v1)

  59.     jr    ra
  60.     nop
  61. END(i2cread)   

  62. =====================
  63. 解释:
  64.     /* set device address */
  65.         li  v0, 0xbfd00000 + SMBUS_HOST_ADDRESS
  66.     li  a0, 0xa1
  67.     sb  a0, 0(v0);
  68. 上面代码是输出从设备的地址。
  69.     /* store register offset */
  70.         li  v0, 0xbfd00000 + SMBUS_HOST_COMMAND
  71.     sb  a1, 0(v0);
  72. 上面代码是输出从设备的寄存器偏移量。
  73.     /* read byte data protocol */
  74.     li  v0, 0x08
  75.     li  v1, 0xbfd00000 + SMBUS_HOST_CONTROL
  76.     sb  v0, 0(v1);

  77.     /* make sure SMB host ready to start, important!--zfx */
  78.     li  v1, 0xbfd00000 + SMBUS_HOST_STATUS
  79.     lbu v0, 0(v1)
  80.     andi v0,v0, 0x1f
  81.     beqz  v0,1f
  82.     nop
  83.     sb  v0, 0(v1)
  84.     lbu v0, 0(v1)   #flush the write
  85. 上面代码是查看数据总线是否准备好数据。
  86. 1:
  87.     /* start */
  88.     li  v1, 0xbfd00000 + SMBUS_HOST_CONTROL
  89.     lbu v0, 0(v1)
  90.     ori v0, v0, 0x40
  91.     sb  v0, 0(v1);

  92.     /* wait */
  93.     li  v1, 0xbfd00000 + SMBUS_HOST_STATUS
  94.     li  a1, 0x1000
  95. 1:
  96. #if 1
  97.     /* delay */
  98.     li a0, 0x1000   
  99. 2:            
  100.     bnez    a0,2b
  101.     addiu    a0, -1
  102. #endif
  103.     addiu    a1, -1
  104.     beqz    a1, 1f
  105.     nop

  106.     lbu  v0, 0(v1)
  107.     andi v0, SMBUS_HOST_STATUS_BUSY
  108.     bnez  v0, 1b  #IDEL ?
  109.     nop
  110. 上面代码是查看总线是否在忙状态。
  111. 1:
  112.     li  v1, 0xbfd00000 + SMBUS_HOST_STATUS
  113.     lbu v0, 0(v1)
  114.     andi v0,v0, 0x1f
  115.     beqz  v0,1f
  116.     nop
  117.     sb  v0, 0(v1)   #reset
  118.     lbu v0, 0(v1)   #flush the write
  119. 1:

  120.     li  v1, 0xbfd00000 + SMBUS_HOST_DATA0
  121.     lbu  v0, 0(v1)

  122.     jr    ra
  123.     nop
  124. 上面代码是已经把命令成功发送出去,然后成功地读取回来数据,保存在v0寄存里。
复制代码

通过上面的子函数,就可以通过I2C总线去读取内存条上的EEPROM参数,以便后面进行内存初始化。
在这里第一次读取是第一个字节。

=====================================
这是一条神奇的小尾巴~~~~~
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Archiver|手机版|小黑屋|龙芯俱乐部开源技术社区

GMT+8, 2024-4-18 17:10 , Processed in 0.105020 second(s), 21 queries .

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表