device/dram/ddr2: Add common ddr2 spd decoder
Decode DDR2 SPD similar to DDR3 SPD decoder to ease readability, reduce code complexity and reduce size of maintainable code. Rename dimm_is_registered to spd_dimm_is_registered_ddr3 to avoid compilation errors. Change-Id: I741f0e61ab23e3999ae9e31f57228ba034c2509e Signed-off-by: Patrick Rudolph <siro@das-labor.org> Reviewed-on: https://review.coreboot.org/18273 Reviewed-by: Arthur Heymans <arthur@aheymans.xyz> Tested-by: build bot (Jenkins)
This commit is contained in:
parent
7e4d12c5b1
commit
6e53ae6f5c
|
@ -1 +1 @@
|
||||||
romstage-y += ddr3.c
|
romstage-y += ddr3.c ddr2.c
|
||||||
|
|
|
@ -0,0 +1,630 @@
|
||||||
|
/*
|
||||||
|
* This file is part of the coreboot project.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2017 Patrick Rudolph <siro@das-labor.org>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file ddr2.c
|
||||||
|
*
|
||||||
|
* \brief Utilities for decoding DDR2 SPDs
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <console/console.h>
|
||||||
|
#include <device/device.h>
|
||||||
|
#include <device/dram/ddr2.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
/*==============================================================================
|
||||||
|
* = DDR2 SPD decoding helpers
|
||||||
|
*----------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Checks if the DIMM is Registered based on byte[20] of the SPD
|
||||||
|
*
|
||||||
|
* Tells if the DIMM type is registered or not.
|
||||||
|
*
|
||||||
|
* @param type DIMM type. This is byte[20] of the SPD.
|
||||||
|
*/
|
||||||
|
int spd_dimm_is_registered_ddr2(enum spd_dimm_type type)
|
||||||
|
{
|
||||||
|
if ((type == SPD_DIMM_TYPE_RDIMM)
|
||||||
|
| (type == SPD_DIMM_TYPE_72B_SO_RDIMM))
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Calculate the checksum of a DDR2 SPD unique identifier
|
||||||
|
*
|
||||||
|
* @param spd pointer to raw SPD data
|
||||||
|
* @param len length of data in SPD
|
||||||
|
*
|
||||||
|
* @return the checksum of SPD data bytes 63, or 0 when spd data is truncated.
|
||||||
|
*/
|
||||||
|
u8 spd_ddr2_calc_checksum(u8 *spd, int len)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
u8 c = 0;
|
||||||
|
|
||||||
|
if (len < 63)
|
||||||
|
/* Not enough bytes available to get the checksum */
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
for (i = 0; i < 63; i++)
|
||||||
|
c += spd[i];
|
||||||
|
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Return size of SPD.
|
||||||
|
*
|
||||||
|
* Returns size of SPD. Usually 128 Byte.
|
||||||
|
*/
|
||||||
|
u32 spd_decode_spd_size_ddr2(u8 byte0)
|
||||||
|
{
|
||||||
|
return MIN(byte0, SPD_SIZE_MAX_DDR2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Return size of eeprom.
|
||||||
|
*
|
||||||
|
* Returns size of eeprom. Usually 256 Byte.
|
||||||
|
*/
|
||||||
|
u32 spd_decode_eeprom_size_ddr2(u8 byte1)
|
||||||
|
{
|
||||||
|
if (!byte1)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (byte1 > 0x0e)
|
||||||
|
return 0x3fff;
|
||||||
|
|
||||||
|
return 1 << byte1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Return index of MSB set
|
||||||
|
*
|
||||||
|
* Returns the index fof MSB set.
|
||||||
|
*/
|
||||||
|
static u8 spd_get_msbs(u8 c)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
for (i = 7; i >= 0; i--)
|
||||||
|
if (c & (1 << i))
|
||||||
|
return i;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Decode SPD tck cycle time
|
||||||
|
*
|
||||||
|
* Decodes a raw SPD data from a DDR2 DIMM.
|
||||||
|
* Returns cycle time in 1/256th ns.
|
||||||
|
*/
|
||||||
|
static u32 spd_decode_tck_time(u8 c)
|
||||||
|
{
|
||||||
|
u8 high, low;
|
||||||
|
|
||||||
|
high = c >> 4;
|
||||||
|
|
||||||
|
switch (c & 0xf) {
|
||||||
|
case 0xa:
|
||||||
|
low = 25;
|
||||||
|
break;
|
||||||
|
case 0xb:
|
||||||
|
low = 33;
|
||||||
|
break;
|
||||||
|
case 0xc:
|
||||||
|
low = 66;
|
||||||
|
break;
|
||||||
|
case 0xd:
|
||||||
|
low = 75;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
low = (c & 0xf) * 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ((high * 100 + low) << 8) / 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Decode SPD bcd style timings
|
||||||
|
*
|
||||||
|
* Decodes a raw SPD data from a DDR2 DIMM.
|
||||||
|
* Returns cycle time in 1/256th ns.
|
||||||
|
*/
|
||||||
|
static u32 spd_decode_bcd_time(u8 c)
|
||||||
|
{
|
||||||
|
u8 high, low;
|
||||||
|
|
||||||
|
high = c >> 4;
|
||||||
|
low = c & 0xf;
|
||||||
|
|
||||||
|
return ((high * 10 + low) << 8) / 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Decode SPD tRP, tRRP cycle time
|
||||||
|
*
|
||||||
|
* Decodes a raw SPD data from a DDR2 DIMM.
|
||||||
|
* Returns cycle time in 1/256th ns.
|
||||||
|
*/
|
||||||
|
static u32 spd_decode_quarter_time(u8 c)
|
||||||
|
{
|
||||||
|
u8 high, low;
|
||||||
|
|
||||||
|
high = c >> 2;
|
||||||
|
low = 25 * (c & 0x3);
|
||||||
|
|
||||||
|
return ((high * 100 + low) << 8) / 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Decode SPD tRR time
|
||||||
|
*
|
||||||
|
* Decodes a raw SPD data from a DDR2 DIMM.
|
||||||
|
* Returns cycle time in 1/256th us.
|
||||||
|
*/
|
||||||
|
static u32 spd_decode_tRR_time(u8 c)
|
||||||
|
{
|
||||||
|
switch (c) {
|
||||||
|
default:
|
||||||
|
case 0:
|
||||||
|
return 15625 << 8;
|
||||||
|
case 1:
|
||||||
|
return 15625 << 6;
|
||||||
|
case 2:
|
||||||
|
return 15625 << 7;
|
||||||
|
case 3:
|
||||||
|
return 15625 << 9;
|
||||||
|
case 4:
|
||||||
|
return 15625 << 10;
|
||||||
|
case 5:
|
||||||
|
return 15625 << 11;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Decode SPD tRC,tRFC time
|
||||||
|
*
|
||||||
|
* Decodes a raw SPD data from a DDR2 DIMM.
|
||||||
|
* Returns cycle time in 1/256th us.
|
||||||
|
*/
|
||||||
|
static void spd_decode_tRCtRFC_time(u8 *spd_40_41_42, u32 *tRC, u32 *tRFC)
|
||||||
|
{
|
||||||
|
u8 b40, b41, b42;
|
||||||
|
|
||||||
|
b40 = spd_40_41_42[0];
|
||||||
|
b41 = spd_40_41_42[1];
|
||||||
|
b42 = spd_40_41_42[2];
|
||||||
|
|
||||||
|
*tRC = b41 * 100;
|
||||||
|
*tRFC = b42 * 100;
|
||||||
|
|
||||||
|
if (b40 & 0x01)
|
||||||
|
*tRFC += 256 * 100;
|
||||||
|
|
||||||
|
switch ((b40 >> 1) & 0x07) {
|
||||||
|
case 1:
|
||||||
|
*tRFC += 25;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
*tRFC += 33;
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
*tRFC += 50;
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
*tRFC += 66;
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
|
*tRFC += 75;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch ((b40 >> 4) & 0x07) {
|
||||||
|
case 1:
|
||||||
|
*tRC += 25;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
*tRC += 33;
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
*tRC += 50;
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
*tRC += 66;
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
|
*tRC += 75;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Convert to 1/256th us */
|
||||||
|
*tRC = (*tRC << 8) / 100;
|
||||||
|
*tRFC = (*tRFC << 8) / 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Decode the raw SPD data
|
||||||
|
*
|
||||||
|
* Decodes a raw SPD data from a DDR2 DIMM, and organizes it into a
|
||||||
|
* @ref dimm_attr structure. The SPD data must first be read in a contiguous
|
||||||
|
* array, and passed to this function.
|
||||||
|
*
|
||||||
|
* @param dimm pointer to @ref dimm_attr structure where the decoded data is to
|
||||||
|
* be stored
|
||||||
|
* @param spd array of raw data previously read from the SPD.
|
||||||
|
*
|
||||||
|
* @return @ref spd_status enumerator
|
||||||
|
* SPD_STATUS_OK -- decoding was successful
|
||||||
|
* SPD_STATUS_INVALID -- invalid SPD or not a DDR2 SPD
|
||||||
|
* SPD_STATUS_CRC_ERROR -- CRC did not verify
|
||||||
|
* SPD_STATUS_INVALID_FIELD -- A field with an invalid value was
|
||||||
|
* detected.
|
||||||
|
*/
|
||||||
|
int spd_decode_ddr2(struct dimm_attr_st *dimm, u8 spd[SPD_SIZE_MAX_DDR2])
|
||||||
|
{
|
||||||
|
u8 spd_size, cl, reg8;
|
||||||
|
u16 eeprom_size;
|
||||||
|
int ret = SPD_STATUS_OK;
|
||||||
|
|
||||||
|
memset(dimm, 0, sizeof(*dimm));
|
||||||
|
|
||||||
|
spd_size = spd_decode_spd_size_ddr2(spd[0]);
|
||||||
|
eeprom_size = spd_decode_eeprom_size_ddr2(spd[1]);
|
||||||
|
|
||||||
|
printram("EEPROM with 0x%04x bytes\n", eeprom_size);
|
||||||
|
printram("SPD contains 0x%02x bytes\n", spd_size);
|
||||||
|
|
||||||
|
if (spd_size < 64 || eeprom_size < 64) {
|
||||||
|
printram("ERROR: SPD to small\n");
|
||||||
|
dimm->dram_type = SPD_MEMORY_TYPE_UNDEFINED;
|
||||||
|
return SPD_STATUS_INVALID;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (spd_ddr2_calc_checksum(spd, spd_size) != spd[63]) {
|
||||||
|
printram("ERROR: SPD checksum error\n");
|
||||||
|
dimm->dram_type = SPD_MEMORY_TYPE_UNDEFINED;
|
||||||
|
return SPD_STATUS_CRC_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
reg8 = spd[62];
|
||||||
|
if ((reg8 & 0xf0) != 0x10) {
|
||||||
|
printram("ERROR: Unsupported SPD revision %01x.%01x\n",
|
||||||
|
reg8 >> 4, reg8 & 0xf);
|
||||||
|
dimm->dram_type = SPD_MEMORY_TYPE_UNDEFINED;
|
||||||
|
return SPD_STATUS_INVALID;
|
||||||
|
}
|
||||||
|
dimm->rev = reg8;
|
||||||
|
printram(" Revision : %01x.%01x\n", dimm->rev >> 4, dimm->rev & 0xf);
|
||||||
|
|
||||||
|
reg8 = spd[2];
|
||||||
|
printram(" Type : 0x%02x\n", reg8);
|
||||||
|
if (reg8 != 0x08) {
|
||||||
|
printram("ERROR: Unsupported SPD type %x\n", reg8);
|
||||||
|
dimm->dram_type = SPD_MEMORY_TYPE_UNDEFINED;
|
||||||
|
return SPD_STATUS_INVALID;
|
||||||
|
}
|
||||||
|
dimm->dram_type = SPD_MEMORY_TYPE_SDRAM_DDR2;
|
||||||
|
|
||||||
|
dimm->row_bits = spd[3];
|
||||||
|
printram(" Rows : %u\n", dimm->row_bits);
|
||||||
|
if ((dimm->row_bits > 31) ||
|
||||||
|
((dimm->row_bits > 15) && (dimm->rev < 0x13))) {
|
||||||
|
printram(" Invalid number of memory rows\n");
|
||||||
|
ret = SPD_STATUS_INVALID_FIELD;
|
||||||
|
}
|
||||||
|
|
||||||
|
dimm->col_bits = spd[4];
|
||||||
|
printram(" Columns : %u\n", dimm->col_bits);
|
||||||
|
if (dimm->col_bits > 15) {
|
||||||
|
printram(" Invalid number of memory columns\n");
|
||||||
|
ret = SPD_STATUS_INVALID_FIELD;
|
||||||
|
}
|
||||||
|
|
||||||
|
dimm->ranks = (spd[5] & 0x7) + 1;
|
||||||
|
printram(" Ranks : %u\n", dimm->ranks);
|
||||||
|
|
||||||
|
dimm->mod_width = spd[6];
|
||||||
|
printram(" Module data width : x%u\n", dimm->mod_width);
|
||||||
|
if (!dimm->mod_width) {
|
||||||
|
printram(" Invalid module data width\n");
|
||||||
|
ret = SPD_STATUS_INVALID_FIELD;
|
||||||
|
}
|
||||||
|
|
||||||
|
dimm->width = spd[13];
|
||||||
|
printram(" SDRAM width : x%u\n", dimm->width);
|
||||||
|
if (!dimm->width) {
|
||||||
|
printram(" Invalid SDRAM width\n");
|
||||||
|
ret = SPD_STATUS_INVALID_FIELD;
|
||||||
|
}
|
||||||
|
|
||||||
|
dimm->banks = spd[17];
|
||||||
|
printram(" Banks : %u\n", dimm->banks);
|
||||||
|
if (!dimm->banks) {
|
||||||
|
printram(" Invalid module banks count\n");
|
||||||
|
ret = SPD_STATUS_INVALID_FIELD;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (spd[8]) {
|
||||||
|
case 0:
|
||||||
|
dimm->flags.operable_5_00V = 1;
|
||||||
|
printram(" Voltage : 5.0V\n");
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
dimm->flags.operable_3_33V = 1;
|
||||||
|
printram(" Voltage : 3.3V\n");
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
dimm->flags.operable_1_50V = 1;
|
||||||
|
printram(" Voltage : 1.5V\n");
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
dimm->flags.operable_3_33V = 1;
|
||||||
|
printram(" Voltage : 3.3V\n");
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
dimm->flags.operable_2_50V = 1;
|
||||||
|
printram(" Voltage : 2.5V\n");
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
|
dimm->flags.operable_1_80V = 1;
|
||||||
|
printram(" Voltage : 1.8V\n");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
printram(" Unknown voltage level.\n");
|
||||||
|
ret = SPD_STATUS_INVALID_FIELD;
|
||||||
|
}
|
||||||
|
|
||||||
|
dimm->cas_supported = spd[18];
|
||||||
|
if ((dimm->cas_supported & 0x3) || !dimm->cas_supported) {
|
||||||
|
printram(" Invalid CAS support advertised.\n");
|
||||||
|
ret = SPD_STATUS_INVALID_FIELD;
|
||||||
|
}
|
||||||
|
printram(" Supported CAS mask : 0x%x\n", dimm->cas_supported);
|
||||||
|
|
||||||
|
if ((dimm->rev < 0x13) && (dimm->cas_supported & 0x80)) {
|
||||||
|
printram(" Invalid CAS support advertised.\n");
|
||||||
|
ret = SPD_STATUS_INVALID_FIELD;
|
||||||
|
}
|
||||||
|
if ((dimm->rev < 0x12) && (dimm->cas_supported & 0x40)) {
|
||||||
|
printram(" Invalid CAS support advertised.\n");
|
||||||
|
ret = SPD_STATUS_INVALID_FIELD;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* CL=X */
|
||||||
|
cl = spd_get_msbs(dimm->cas_supported);
|
||||||
|
|
||||||
|
/* SDRAM Cycle time at Maximum Supported CAS Latency (CL), CL=X */
|
||||||
|
dimm->cycle_time[cl] = spd_decode_tck_time(spd[9]);
|
||||||
|
/* SDRAM Access from Clock */
|
||||||
|
dimm->access_time[cl] = spd_decode_bcd_time(spd[10]);
|
||||||
|
|
||||||
|
if (dimm->cas_supported & (1 << (cl - 1))) {
|
||||||
|
/* Minimum Clock Cycle at CLX-1 */
|
||||||
|
dimm->cycle_time[cl - 1] = spd_decode_tck_time(spd[23]);
|
||||||
|
/* Maximum Data Access Time (tAC) from Clock at CLX-1 */
|
||||||
|
dimm->access_time[cl - 1] = spd_decode_bcd_time(spd[24]);
|
||||||
|
}
|
||||||
|
if (dimm->cas_supported & (1 << (cl - 2))) {
|
||||||
|
/* Minimum Clock Cycle at CLX-2 */
|
||||||
|
dimm->cycle_time[cl - 2] = spd_decode_tck_time(spd[25]);
|
||||||
|
/* Maximum Data Access Time (tAC) from Clock at CLX-2 */
|
||||||
|
dimm->access_time[cl - 2] = spd_decode_bcd_time(spd[26]);
|
||||||
|
}
|
||||||
|
|
||||||
|
reg8 = (spd[31] >> 5) | (spd[31] << 3);
|
||||||
|
if (!reg8) {
|
||||||
|
printram(" Invalid rank density.\n");
|
||||||
|
ret = SPD_STATUS_INVALID_FIELD;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Rank density */
|
||||||
|
dimm->ranksize_mb = 128 * reg8;
|
||||||
|
/* Module density */
|
||||||
|
dimm->size_mb = dimm->ranksize_mb * dimm->ranks;
|
||||||
|
if (dimm->size_mb < 1024)
|
||||||
|
printram(" Capacity : %u MB\n", dimm->size_mb);
|
||||||
|
else
|
||||||
|
printram(" Capacity : %u GB\n", dimm->size_mb >> 10);
|
||||||
|
|
||||||
|
/* SDRAM Maximum Cycle Time (tCKmax) */
|
||||||
|
dimm->tCK = spd_decode_tck_time(spd[43]);
|
||||||
|
/* Minimum Write Recovery Time (tWRmin) */
|
||||||
|
dimm->tWR = spd_decode_quarter_time(spd[36]);
|
||||||
|
/* Minimum RAS# to CAS# Delay Time (tRCDmin) */
|
||||||
|
dimm->tRCD = spd_decode_quarter_time(spd[29]);
|
||||||
|
/* Minimum Row Active to Row Active Delay Time (tRRDmin) */
|
||||||
|
dimm->tRRD = spd_decode_quarter_time(spd[28]);
|
||||||
|
/* Minimum Row Precharge Delay Time (tRPmin) */
|
||||||
|
dimm->tRP = spd_decode_quarter_time(spd[27]);
|
||||||
|
/* Minimum Active to Precharge Delay Time (tRASmin) */
|
||||||
|
dimm->tRAS = spd[30] << 8;
|
||||||
|
/* Minimum Active to Active/Refresh Delay Time (tRCmin) */
|
||||||
|
/* Minimum Refresh Recovery Delay Time (tRFCmin) */
|
||||||
|
spd_decode_tRCtRFC_time(&spd[40], &dimm->tRC, &dimm->tRFC);
|
||||||
|
/* Minimum Internal Write to Read Command Delay Time (tWTRmin) */
|
||||||
|
dimm->tWTR = spd_decode_quarter_time(spd[37]);
|
||||||
|
/* Minimum Internal Read to Precharge Command Delay Time (tRTPmin) */
|
||||||
|
dimm->tRTP = spd_decode_quarter_time(spd[38]);
|
||||||
|
/* Data Input Setup Time Before Strobe */
|
||||||
|
dimm->tDS = spd_decode_bcd_time(spd[34]);
|
||||||
|
/* Data Input Hold Time After Strobe */
|
||||||
|
dimm->tDH = spd_decode_bcd_time(spd[35]);
|
||||||
|
/* SDRAM Device DQS-DQ Skew for DQS and associated DQ signals */
|
||||||
|
dimm->tDQSQ = (spd[44] << 8) / 100;
|
||||||
|
/* SDRAM Device Maximum Read Data Hold Skew Factor */
|
||||||
|
dimm->tQHS = (spd[45] << 8) / 100;
|
||||||
|
/* PLL Relock Time in us */
|
||||||
|
dimm->tPLL = spd[46] << 8;
|
||||||
|
/* Refresh rate in us */
|
||||||
|
dimm->tRR = spd_decode_tRR_time(spd[12]);
|
||||||
|
|
||||||
|
/* Number of PLLs on DIMM */
|
||||||
|
if (dimm->rev >= 0x11)
|
||||||
|
dimm->plls = (spd[21] >> 2) & 0x3;
|
||||||
|
|
||||||
|
/* SDRAM Thermal and Refresh Options */
|
||||||
|
printram(" General features :");
|
||||||
|
if ((dimm->rev >= 0x12) && (spd[22] & 0x04)) {
|
||||||
|
dimm->flags.pasr = 1;
|
||||||
|
printram(" PASR");
|
||||||
|
}
|
||||||
|
if ((dimm->rev >= 0x12) && (spd[22] & 0x02)) {
|
||||||
|
dimm->flags.terminate_50ohms = 1;
|
||||||
|
printram(" 50Ohm");
|
||||||
|
}
|
||||||
|
if (spd[22] & 0x01) {
|
||||||
|
dimm->flags.weak_driver = 1;
|
||||||
|
printram(" WEAK_DRIVER");
|
||||||
|
}
|
||||||
|
printram("\n");
|
||||||
|
|
||||||
|
/* SDRAM Supported Burst length */
|
||||||
|
printram(" Burst length :");
|
||||||
|
if (spd[16] & 0x06) {
|
||||||
|
dimm->flags.bl8 = 1;
|
||||||
|
printram(" BL8");
|
||||||
|
}
|
||||||
|
if (spd[22] & 0x04) {
|
||||||
|
dimm->flags.bl4 = 1;
|
||||||
|
printram(" BL4");
|
||||||
|
}
|
||||||
|
printram("\n");
|
||||||
|
|
||||||
|
dimm->dimm_type = spd[20] & SPD_DIMM_TYPE_MASK;
|
||||||
|
printram(" Dimm type : %x\n", dimm->dimm_type);
|
||||||
|
|
||||||
|
dimm->flags.is_ecc = !!(spd[11] & 0x3);
|
||||||
|
printram(" ECC support : %x\n", dimm->flags.is_ecc);
|
||||||
|
|
||||||
|
dimm->flags.stacked = !!(spd[5] & 0x10);
|
||||||
|
printram(" Package : %s\n",
|
||||||
|
dimm->flags.stacked ? "stack" : "planar");
|
||||||
|
|
||||||
|
if (spd_size > 71) {
|
||||||
|
memcpy(&dimm->manufacturer_id, &spd[64], 4);
|
||||||
|
printram(" Manufacturer ID : %x\n", dimm->manufacturer_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (spd_size > 90) {
|
||||||
|
dimm->part_number[16] = 0;
|
||||||
|
memcpy(dimm->part_number, &spd[73], 16);
|
||||||
|
printram(" Part number : %s\n", dimm->part_number);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (spd_size > 94) {
|
||||||
|
dimm->year = spd[93] + 2000;
|
||||||
|
dimm->weeks = spd[94];
|
||||||
|
printram(" Date : %d week %d\n", dimm->year, dimm->weeks);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (spd_size > 98) {
|
||||||
|
memcpy(&dimm->serial, &spd[95], 4);
|
||||||
|
printram(" Serial number : 0x%08x\n", dimm->serial);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The information printed below has a more informational character, and is not
|
||||||
|
* necessarily tied in to RAM init debugging. Hence, we stop using printram(),
|
||||||
|
* and use the standard printk()'s below.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static void print_ns(const char *msg, u32 val)
|
||||||
|
{
|
||||||
|
u32 mant, fp;
|
||||||
|
mant = val / 256;
|
||||||
|
fp = (val % 256) * 1000 / 256;
|
||||||
|
|
||||||
|
printk(BIOS_INFO, "%s%3u.%.3u ns\n", msg, mant, fp);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void print_us(const char *msg, u32 val)
|
||||||
|
{
|
||||||
|
u32 mant, fp;
|
||||||
|
mant = val / 256;
|
||||||
|
fp = (val % 256) * 1000 / 256;
|
||||||
|
|
||||||
|
printk(BIOS_INFO, "%s%3u.%.3u us\n", msg, mant, fp);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Print the info in DIMM
|
||||||
|
*
|
||||||
|
* Print info about the DIMM. Useful to use when CONFIG_DEBUG_RAM_SETUP is
|
||||||
|
* selected, or for a purely informative output.
|
||||||
|
*
|
||||||
|
* @param dimm pointer to already decoded @ref dimm_attr structure
|
||||||
|
*/
|
||||||
|
void dram_print_spd_ddr2(const struct dimm_attr_st *dimm)
|
||||||
|
{
|
||||||
|
char buf[32];
|
||||||
|
int i;
|
||||||
|
|
||||||
|
printk(BIOS_INFO, " Row addr bits : %u\n", dimm->row_bits);
|
||||||
|
printk(BIOS_INFO, " Column addr bits : %u\n", dimm->col_bits);
|
||||||
|
printk(BIOS_INFO, " Number of ranks : %u\n", dimm->ranks);
|
||||||
|
printk(BIOS_INFO, " DIMM Capacity : %u MB\n", dimm->size_mb);
|
||||||
|
printk(BIOS_INFO, " Width : x%u\n", dimm->width);
|
||||||
|
printk(BIOS_INFO, " Banks : %u\n", dimm->banks);
|
||||||
|
|
||||||
|
/* CAS Latencies Supported */
|
||||||
|
printk(BIOS_INFO, " CAS latencies :");
|
||||||
|
for (i = 2; i < 8; i++) {
|
||||||
|
if (dimm->cas_supported & (1 << i))
|
||||||
|
printk(BIOS_INFO, " %u", i);
|
||||||
|
}
|
||||||
|
printk(BIOS_INFO, "\n");
|
||||||
|
|
||||||
|
for (i = 2; i < 8; i++) {
|
||||||
|
if (!(dimm->cas_supported & (1 << i)))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
strcpy(buf, " tCK at CLx : ");
|
||||||
|
/* Simple snprintf replacement */
|
||||||
|
buf[11] = '0' + i;
|
||||||
|
print_ns(buf, dimm->cycle_time[i]);
|
||||||
|
|
||||||
|
strcpy(buf, " tAC at CLx : ");
|
||||||
|
/* Simple snprintf replacement */
|
||||||
|
buf[11] = '0' + i;
|
||||||
|
print_ns(buf, dimm->access_time[i]);
|
||||||
|
}
|
||||||
|
print_ns(" tCKmax : ", dimm->tCK);
|
||||||
|
print_ns(" tWRmin : ", dimm->tWR);
|
||||||
|
print_ns(" tRCDmin : ", dimm->tRCD);
|
||||||
|
print_ns(" tRRDmin : ", dimm->tRRD);
|
||||||
|
print_ns(" tRPmin : ", dimm->tRP);
|
||||||
|
print_ns(" tRASmin : ", dimm->tRAS);
|
||||||
|
print_ns(" tRCmin : ", dimm->tRC);
|
||||||
|
print_ns(" tRFCmin : ", dimm->tRFC);
|
||||||
|
print_ns(" tWTRmin : ", dimm->tWTR);
|
||||||
|
print_ns(" tRTPmin : ", dimm->tRTP);
|
||||||
|
print_ns(" tDS : ", dimm->tDS);
|
||||||
|
print_ns(" tDH : ", dimm->tDH);
|
||||||
|
print_ns(" tDQSQmax : ", dimm->tDQSQ);
|
||||||
|
print_ns(" tQHSmax : ", dimm->tQHS);
|
||||||
|
print_us(" tPLL : ", dimm->tPLL);
|
||||||
|
print_us(" tRR : ", dimm->tRR);
|
||||||
|
}
|
|
@ -36,7 +36,7 @@
|
||||||
*
|
*
|
||||||
* @param type DIMM type. This is byte[3] of the SPD.
|
* @param type DIMM type. This is byte[3] of the SPD.
|
||||||
*/
|
*/
|
||||||
int dimm_is_registered(enum spd_dimm_type type)
|
int spd_dimm_is_registered_ddr3(enum spd_dimm_type type)
|
||||||
{
|
{
|
||||||
if ((type == SPD_DIMM_TYPE_RDIMM)
|
if ((type == SPD_DIMM_TYPE_RDIMM)
|
||||||
| (type == SPD_DIMM_TYPE_MINI_RDIMM)
|
| (type == SPD_DIMM_TYPE_MINI_RDIMM)
|
||||||
|
|
|
@ -0,0 +1,218 @@
|
||||||
|
/*
|
||||||
|
* This file is part of the coreboot project.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2017 Patrick Rudolph <siro@das-labor.org>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* JEDEC Standard No. 21-C
|
||||||
|
* Annex J: Annex J: Serial Presence Detects for DDR2 SDRAM (Revision 1.3)
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef DEVICE_DRAM_DDR2L_H
|
||||||
|
#define DEVICE_DRAM_DDR2L_H
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file ddr2.h
|
||||||
|
*
|
||||||
|
* \brief Utilities for decoding DDR2 SPDs
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <spd.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Convenience definitions for TCK values
|
||||||
|
*
|
||||||
|
* Different values for tCK, representing standard DDR2 frequencies.
|
||||||
|
* These values are in 1/256 ns units.
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
#define TCK_800MHZ 320
|
||||||
|
#define TCK_700MHZ 365
|
||||||
|
#define TCK_666MHZ 384
|
||||||
|
#define TCK_533MHZ 480
|
||||||
|
#define TCK_400MHZ 640
|
||||||
|
#define TCK_333MHZ 768
|
||||||
|
#define TCK_266MHZ 960
|
||||||
|
#define TCK_200MHZ 1280
|
||||||
|
/** @} */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Convenience macro for enabling printk with CONFIG_DEBUG_RAM_SETUP
|
||||||
|
*
|
||||||
|
* Use this macro instead of printk(); for verbose RAM initialization messages.
|
||||||
|
* When CONFIG_DEBUG_RAM_SETUP is not selected, these messages are automatically
|
||||||
|
* disabled.
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
#if IS_ENABLED(CONFIG_DEBUG_RAM_SETUP)
|
||||||
|
#define printram(x, ...) printk(BIOS_DEBUG, x, ##__VA_ARGS__)
|
||||||
|
#else
|
||||||
|
#define printram(x, ...)
|
||||||
|
#endif
|
||||||
|
/** @} */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Module type (byte 20, bits 5:0) of SPD
|
||||||
|
* This definition is specific to DDR2. DDR3 SPDs have a different structure.
|
||||||
|
*/
|
||||||
|
enum spd_dimm_type {
|
||||||
|
SPD_DIMM_TYPE_UNDEFINED = 0x00,
|
||||||
|
SPD_DIMM_TYPE_RDIMM = 0x01,
|
||||||
|
SPD_DIMM_TYPE_UDIMM = 0x02,
|
||||||
|
SPD_DIMM_TYPE_SO_DIMM = 0x04,
|
||||||
|
SPD_DIMM_TYPE_72B_SO_CDIMM = 0x06,
|
||||||
|
SPD_DIMM_TYPE_72B_SO_RDIMM = 0x07,
|
||||||
|
SPD_DIMM_TYPE_MICRO_DIMM = 0x08,
|
||||||
|
SPD_DIMM_TYPE_MINI_DIMM = 0x10,
|
||||||
|
SPD_DIMM_TYPE_MINI_UDIMM = 0x20,
|
||||||
|
/* Masks to bits 5:0 to give the dimm type */
|
||||||
|
SPD_DIMM_TYPE_MASK = 0x3f,
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief DIMM flags
|
||||||
|
*
|
||||||
|
* Characteristic flags for the DIMM, as presented by the SPD
|
||||||
|
*/
|
||||||
|
union dimm_flags_st {
|
||||||
|
/* The whole point of the union/struct construct is to allow us to clear
|
||||||
|
* all the bits with one line: flags.raw = 0.
|
||||||
|
* We do not care how these bits are ordered */
|
||||||
|
struct {
|
||||||
|
/* Module can work at 5.00V */
|
||||||
|
unsigned operable_5_00V:1;
|
||||||
|
/* Module can work at 3.33V */
|
||||||
|
unsigned operable_3_33V:1;
|
||||||
|
/* Module can work at 2.50V */
|
||||||
|
unsigned operable_2_50V:1;
|
||||||
|
/* Module can work at 1.80V - All DIMMS must be 1.8V operable */
|
||||||
|
unsigned operable_1_80V:1;
|
||||||
|
/* Module can work at 1.50V */
|
||||||
|
unsigned operable_1_50V:1;
|
||||||
|
/* Module can work at 1.35V */
|
||||||
|
unsigned operable_1_35V:1;
|
||||||
|
/* Module can work at 1.20V */
|
||||||
|
unsigned operable_1_25V:1;
|
||||||
|
/* Has an 8-bit bus extension, meaning the DIMM supports ECC */
|
||||||
|
unsigned is_ecc:1;
|
||||||
|
/* Supports weak driver */
|
||||||
|
unsigned weak_driver:1;
|
||||||
|
/* Supports terminating at 50 Ohm */
|
||||||
|
unsigned terminate_50ohms:1;
|
||||||
|
/* Partial Array Self Refresh */
|
||||||
|
unsigned pasr:1;
|
||||||
|
/* Supports burst length 8 */
|
||||||
|
unsigned bl8:1;
|
||||||
|
/* Supports burst length 4 */
|
||||||
|
unsigned bl4:1;
|
||||||
|
/* DIMM Package is stack */
|
||||||
|
unsigned stacked:1;
|
||||||
|
};
|
||||||
|
unsigned int raw;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief DIMM characteristics
|
||||||
|
*
|
||||||
|
* The characteristics of each DIMM, as presented by the SPD
|
||||||
|
*/
|
||||||
|
struct dimm_attr_st {
|
||||||
|
enum spd_memory_type dram_type;
|
||||||
|
enum spd_dimm_type dimm_type;
|
||||||
|
/* BCD SPD revision */
|
||||||
|
u8 rev;
|
||||||
|
/* Supported CAS mask, bit 0 == CL0 .. bit7 == CL7 */
|
||||||
|
u8 cas_supported;
|
||||||
|
/* Maximum cloclk to data cycle times for various CAS.
|
||||||
|
* Fields 0 and 1 are unused. */
|
||||||
|
u32 cycle_time[8];
|
||||||
|
/* Maximum data access times for various CAS.
|
||||||
|
* Fields 0 and 1 are unused. */
|
||||||
|
u32 access_time[8];
|
||||||
|
/* Flags extracted from SPD */
|
||||||
|
union dimm_flags_st flags;
|
||||||
|
/* Number of banks */
|
||||||
|
u8 banks;
|
||||||
|
/* SDRAM width */
|
||||||
|
u8 width;
|
||||||
|
/* Module width */
|
||||||
|
u8 mod_width;
|
||||||
|
/* Number of ranks */
|
||||||
|
u8 ranks;
|
||||||
|
/* Number or row address bits */
|
||||||
|
u8 row_bits;
|
||||||
|
/* Number or column address bits */
|
||||||
|
u8 col_bits;
|
||||||
|
/* Number of PLLs on module */
|
||||||
|
u8 plls;
|
||||||
|
/* Size of module in MiB */
|
||||||
|
u16 size_mb;
|
||||||
|
/* Size of one rank in MiB */
|
||||||
|
u16 ranksize_mb;
|
||||||
|
/* Latencies are in units of 1/256 ns */
|
||||||
|
u32 tCK;
|
||||||
|
u32 tWR;
|
||||||
|
u32 tRCD;
|
||||||
|
u32 tRRD;
|
||||||
|
u32 tRP;
|
||||||
|
u32 tRAS;
|
||||||
|
u32 tIS;
|
||||||
|
u32 tIH;
|
||||||
|
u32 tDS;
|
||||||
|
u32 tDH;
|
||||||
|
|
||||||
|
u32 tRC;
|
||||||
|
u32 tRFC;
|
||||||
|
u32 tWTR;
|
||||||
|
u32 tRTP;
|
||||||
|
u32 tDQSQ;
|
||||||
|
u32 tQHS;
|
||||||
|
|
||||||
|
/* Latencies are in units of 1/256 us */
|
||||||
|
u32 tPLL;
|
||||||
|
u32 tRR;
|
||||||
|
|
||||||
|
/* Manufacturer ID */
|
||||||
|
u32 manufacturer_id;
|
||||||
|
/* ASCII part number - NULL terminated */
|
||||||
|
u8 part_number[17];
|
||||||
|
/* Year manufactured */
|
||||||
|
u16 year;
|
||||||
|
/* Week manufactured */
|
||||||
|
u8 weeks;
|
||||||
|
/* Unique serial number */
|
||||||
|
u32 serial;
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Result of the SPD decoding process */
|
||||||
|
enum spd_status {
|
||||||
|
SPD_STATUS_OK = 0,
|
||||||
|
SPD_STATUS_INVALID,
|
||||||
|
SPD_STATUS_CRC_ERROR,
|
||||||
|
SPD_STATUS_INVALID_FIELD,
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Maximum SPD size supported */
|
||||||
|
#define SPD_SIZE_MAX_DDR2 128
|
||||||
|
|
||||||
|
int spd_dimm_is_registered_ddr2(enum spd_dimm_type type);
|
||||||
|
u8 spd_ddr2_calc_checksum(u8 *spd, int len);
|
||||||
|
u32 spd_decode_spd_size_ddr2(u8 byte0);
|
||||||
|
u32 spd_decode_eeprom_size_ddr2(u8 byte1);
|
||||||
|
int spd_decode_ddr2(struct dimm_attr_st *dimm, u8 spd[SPD_SIZE_MAX_DDR2]);
|
||||||
|
void dram_print_spd_ddr2(const struct dimm_attr_st *dimm);
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* DEVICE_DRAM_DDR2L_H */
|
|
@ -201,7 +201,7 @@ typedef u8 spd_raw_data[256];
|
||||||
u16 spd_ddr3_calc_crc(u8 *spd, int len);
|
u16 spd_ddr3_calc_crc(u8 *spd, int len);
|
||||||
u16 spd_ddr3_calc_unique_crc(u8 *spd, int len);
|
u16 spd_ddr3_calc_unique_crc(u8 *spd, int len);
|
||||||
int spd_decode_ddr3(dimm_attr * dimm, spd_raw_data spd_data);
|
int spd_decode_ddr3(dimm_attr * dimm, spd_raw_data spd_data);
|
||||||
int dimm_is_registered(enum spd_dimm_type type);
|
int spd_dimm_is_registered_ddr3(enum spd_dimm_type type);
|
||||||
void dram_print_spd_ddr3(const dimm_attr * dimm);
|
void dram_print_spd_ddr3(const dimm_attr * dimm);
|
||||||
int spd_xmp_decode_ddr3(dimm_attr * dimm,
|
int spd_xmp_decode_ddr3(dimm_attr * dimm,
|
||||||
spd_raw_data spd,
|
spd_raw_data spd,
|
||||||
|
|
|
@ -703,7 +703,7 @@ static void vx900_dram_freq(ramctr_timing * ctrl)
|
||||||
pci_mod_config8(MCU, 0x6b, 0x80, 0x00);
|
pci_mod_config8(MCU, 0x6b, 0x80, 0x00);
|
||||||
|
|
||||||
/* Step 8 - If we have registered DIMMs, we need to set bit[0] */
|
/* Step 8 - If we have registered DIMMs, we need to set bit[0] */
|
||||||
if (dimm_is_registered(ctrl->dimm_type)) {
|
if (spd_dimm_is_registered_ddr3(ctrl->dimm_type)) {
|
||||||
printram("Enabling RDIMM support in memory controller\n");
|
printram("Enabling RDIMM support in memory controller\n");
|
||||||
pci_mod_config8(MCU, 0x6c, 0x00, 0x01);
|
pci_mod_config8(MCU, 0x6c, 0x00, 0x01);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue