[coreboot-gerrit] Patch set updated for coreboot: device/dram/ddr2: Add common ddr2 spd decoder

Patrick Rudolph (siro@das-labor.org) gerrit at coreboot.org
Sat Feb 4 13:39:57 CET 2017


Patrick Rudolph (siro at das-labor.org) just uploaded a new patch set to gerrit, which you can find at https://review.coreboot.org/18273

-gerrit

commit d320c10cf3197c90a3ff02ae42e692513b0ad4af
Author: Patrick Rudolph <siro at das-labor.org>
Date:   Tue Jan 31 19:43:17 2017 +0100

    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.
    
    Change-Id: I741f0e61ab23e3999ae9e31f57228ba034c2509e
    Signed-off-by: Patrick Rudolph <siro at das-labor.org>
---
 src/device/Kconfig             |   4 +
 src/device/dram/Makefile.inc   |   1 +
 src/device/dram/ddr2.c         | 596 +++++++++++++++++++++++++++++++++++++++++
 src/include/device/dram/ddr2.h | 201 ++++++++++++++
 4 files changed, 802 insertions(+)

diff --git a/src/device/Kconfig b/src/device/Kconfig
index d1f5694..7e8e50f 100644
--- a/src/device/Kconfig
+++ b/src/device/Kconfig
@@ -214,6 +214,10 @@ config SPD_CACHE
 	bool
 	default n
 
+config SPD_DDR2
+	bool
+	default n
+
 config PCI
 	bool
 	default n
diff --git a/src/device/dram/Makefile.inc b/src/device/dram/Makefile.inc
index 05f440b..9b0016a 100644
--- a/src/device/dram/Makefile.inc
+++ b/src/device/dram/Makefile.inc
@@ -1 +1,2 @@
 romstage-$(CONFIG_SPD_CACHE) += spd_cache.c ddr3.c
+romstage-$(CONFIG_SPD_DDR2) += ddr2.c
diff --git a/src/device/dram/ddr2.c b/src/device/dram/ddr2.c
new file mode 100644
index 0000000..4a2994b
--- /dev/null
+++ b/src/device/dram/ddr2.c
@@ -0,0 +1,596 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2017 Patrick Rudolph <siro at 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 dimm_is_registered(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 byte0;
+}
+
+/**
+ * \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;
+	u8 div_100 = 0;
+
+	high = c >> 4;
+
+	switch (c & 0xf) {
+	case 0xa:
+		low = 25;
+		div_100 = 1;
+		break;
+	case 0xb:
+		low = 33;
+		div_100 = 1;
+		break;
+	case 0xc:
+		low = 66;
+		div_100 = 1;
+		break;
+	case 0xd:
+		low = 75;
+		div_100 = 1;
+		break;
+	default:
+		low = c & 0xf;
+		div_100 = 0;
+	}
+
+	if (div_100)
+		return (high << 8) + ((low << 8) / 100);
+	else
+		return (high << 8) + ((low << 8) / 10);
+}
+
+/**
+ * \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 << 8) / 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 << 8) + ((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 tRP,tRFC time
+ *
+ * Decodes a raw SPD data from a DDR2 DIMM.
+ * Returns cycle time in 1/256th us.
+ */
+static void spd_decode_tRPtRFC_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 << 8;
+	*tRFC = b42 << 8;
+
+	if (b40 & 0x01)
+		*tRFC += 256 << 8;
+
+	switch ((b40 >> 1) & 0x07) {
+	case 1:
+		*tRFC += (25 << 8) / 100;
+		break;
+	case 2:
+		*tRFC += (33 << 8) / 100;
+		break;
+	case 3:
+		*tRFC += (50 << 8) / 100;
+		break;
+	case 4:
+		*tRFC += (66 << 8) / 100;
+		break;
+	case 5:
+		*tRFC += (75 << 8) / 100;
+		break;
+	default:
+		break;
+	}
+
+	switch ((b40 >> 4) & 0x07) {
+	case 1:
+		*tRC += (25 << 8) / 100;
+		break;
+	case 2:
+		*tRC += (33 << 8) / 100;
+		break;
+	case 3:
+		*tRC += (50 << 8) / 100;
+		break;
+	case 4:
+		*tRC += (66 << 8) / 100;
+		break;
+	case 5:
+		*tRC += (75 << 8) / 100;
+		break;
+	default:
+		break;
+	}
+}
+
+/**
+ * \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(dimm_attr *dimm, spd_raw_data spd)
+{
+	int ret;
+	u16 eeprom_size;
+	u8 spd_size;
+	u8 cl;
+	u8 reg8;
+
+	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_INVALID;
+	}
+
+	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;
+	}
+	printram("  Revision           : %01x.%01x\n", reg8 >> 4, reg8 & 0xf);
+
+	reg8 = spd[2];
+	if (reg8 != 0x08) {
+		printram("ERROR: Unsupported SPD type %x\n", reg8);
+		dimm->dram_type = SPD_MEMORY_TYPE_UNDEFINED;
+		return SPD_STATUS_INVALID;
+	}
+	printram("  Type               : 0x%02x\n", reg8);
+	dimm->dram_type = SPD_MEMORY_TYPE_SDRAM_DDR2;
+
+	dimm->row_bits = spd[3];
+	if (dimm->row_bits > 31) {
+		printram("  Invalid number of memory rows\n");
+		ret = SPD_STATUS_INVALID_FIELD;
+	}
+	printram("  Rows               : %u\n", dimm->row_bits);
+
+	dimm->col_bits = spd[4];
+	if (dimm->col_bits > 16) {
+		printram("  Invalid number of memory columns\n");
+		ret = SPD_STATUS_INVALID_FIELD;
+	}
+	printram("  Columns            : %u\n", dimm->col_bits);
+
+	dimm->ranks = (spd[5] & 0x7) + 1;
+	printram("  Ranks              : %u\n", dimm->ranks);
+
+	dimm->mod_width = spd[6];
+	printram("  Module width       : x%u\n", dimm->mod_width);
+
+	dimm->width = spd[13];
+	printram("  Width              : x%u\n", dimm->width);
+
+	dimm->banks = spd[17];
+	printram("  Banks              : %u\n", dimm->banks);
+
+	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) {
+		printram("  Invalid CAS support advertised.\n");
+		ret = SPD_STATUS_INVALID_FIELD;
+	}
+	printram("  Supported CAS mask : 0x%x\n", dimm->cas_supported);
+
+	/* 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]);
+	}
+
+	/* Module Rank Density */
+	reg8 = (spd[31] >> 5) | (spd[31] << 3);
+	dimm->size_mb = 128 * reg8;
+	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_tRPtRFC_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]);
+
+	/* SDRAM Thermal and Refresh Options */
+	printram("  General features   :");
+	if (spd[22] & 0x04) {
+		dimm->flags.pasr = 1;
+		printram(" PASR");
+	}
+	if (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);
+
+	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);
+	}
+
+	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 dimm_attr *dimm)
+{
+	u8 val8, cl;
+	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 */
+	val8 = dimm->cas_supported;
+	printk(BIOS_INFO, "  CAS latencies     :");
+	i = 0;
+	cl = 0;
+	do {
+		if (val8 & 1) {
+			printk(BIOS_INFO, " %u", i);
+			cl = i;
+		}
+		i++;
+		val8 >>= 1;
+	} while (val8);
+	printk(BIOS_INFO, "\n");
+
+	for (i = cl - 2; i <= cl; i++) {
+		if (i < 0)
+			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);
+}
diff --git a/src/include/device/dram/ddr2.h b/src/include/device/dram/ddr2.h
new file mode 100644
index 0000000..026be4b
--- /dev/null
+++ b/src/include/device/dram/ddr2.h
@@ -0,0 +1,201 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2017 Patrick Rudolph <siro at 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
+ */
+typedef 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;
+	};
+	unsigned int raw;
+} dimm_flags_t;
+
+/**
+ * \brief DIMM characteristics
+ *
+ * The characteristics of each DIMM, as presented by the SPD
+ */
+typedef struct dimm_attr_st {
+	enum spd_memory_type dram_type;
+	enum spd_dimm_type dimm_type;
+	u8 cas_supported;
+
+	/* Maximum cloclk to data cycle times for various CAS */
+	u32 cycle_time[8];
+	/* Maximum data access times for various CAS */
+	u32 access_time[8];
+	/* Flags extracted from SPD */
+	dimm_flags_t 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;
+	/* Size of module in MiB */
+	u32 size_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];
+} dimm_attr;
+
+/** Result of the SPD decoding process */
+enum spd_status {
+	SPD_STATUS_OK = 0,
+	SPD_STATUS_INVALID,
+	SPD_STATUS_CRC_ERROR,
+	SPD_STATUS_INVALID_FIELD,
+};
+
+typedef u8 spd_raw_data[128];
+
+int dimm_is_registered(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(dimm_attr *dimm, spd_raw_data spd);
+void dram_print_spd_ddr2(const dimm_attr *dimm);
+
+
+#endif /* DEVICE_DRAM_DDR2L_H */



More information about the coreboot-gerrit mailing list