This allows to print the DDR3 SPD data with spd_decode command. The code
is ported from the decode-dimms program of i2c-tools. The output of the
command is almost exactly the same as of the original program with a few
minor differences: lacking commas in one place, and manufacturer ID
being output as a hexadecimal value instead of a decoded string. The
logic is mostly the same too, but some changes in how calculations are
made were required so as not to use floating point arithmetic.

Signed-off-by: Denis Orlov <denorl2...@gmail.com>
---
 common/ddr_spd.c | 556 ++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 548 insertions(+), 8 deletions(-)

diff --git a/common/ddr_spd.c b/common/ddr_spd.c
index 1c1d5826e0..b7693f3fd2 100644
--- a/common/ddr_spd.c
+++ b/common/ddr_spd.c
@@ -231,11 +231,31 @@ static int ddr2_sdram_ctime(uint8_t byte)
        return ctime;
 }
 
+static void spd_print_manufacturing_date(uint8_t year, uint8_t week)
+{
+       /*
+        * According to JEDEC Standard the year/week bytes must be in BCD
+        * format. However, that is not always true for actual DIMMs out
+        * there, so fall back to binary format if it makes more sense.
+        */
+
+       printf("%-48s ", "Manufacturing Date");
+       if ((year & 0xf0) <= 0x90 && (year & 0xf) <= 0x9
+           && (week & 0xf0) <= 0x90 && (week & 0xf) <= 0x9) {
+               printf("20%02X-W%02X", year, week);
+       } else if (year <= 99 && week >= 1 && week <= 53) {
+               printf("20%02d-W%02d", year, week);
+       } else {
+               printf("0x%02X%02X", year, week);
+       }
+       printf("\n");
+}
+
 /*
  * Based on
  * https://github.com/groeck/i2c-tools/blob/master/eeprom/decode-dimms
  */
-void ddr_spd_print(uint8_t *record)
+static void ddr2_spd_print(uint8_t *record)
 {
        int highestCAS = 0;
        int i, i_i, k, x, y;
@@ -246,11 +266,6 @@ void ddr_spd_print(uint8_t *record)
        char *ref, *sum;
        struct ddr2_spd_eeprom *s = (struct ddr2_spd_eeprom *)record;
 
-       if (s->mem_type != SPD_MEMTYPE_DDR2) {
-               printf("Can't dump information for non-DDR2 memory\n");
-               return;
-       }
-
        ctime = ddr2_sdram_ctime(s->clk_cycle);
        ddrclk = 2 * (100000 / ctime);
        tbits = (s->res_7 << 8) + (s->dataw);
@@ -420,14 +435,539 @@ void ddr_spd_print(uint8_t *record)
                        printf("%d", record[i]);
        }
        printf("\n");
-       printf("%-48s 20%d-W%d\n", "Manufacturing Date", record[93],
-               record[94]);
+       spd_print_manufacturing_date(record[93], record[94]);
        printf("%-48s 0x", "Assembly Serial Number");
        for (i = 95; i < 99; i++)
                printf("%02X", record[i]);
        printf("\n");
 }
 
+static const char * const ddr3_spd_moduletypes[] = {
+       "Undefined",
+       "RDIMM",
+       "UDIMM",
+       "SO-DIMM",
+       "Micro-DIMM",
+       "Mini-RDIMM",
+       "Mini-UDIMM",
+       "Mini-CDIMM",
+       "72b-SO-UDIMM",
+       "72b-SO-RDIMM",
+       "72b-SO-CDIMM",
+       "LRDIMM",
+       "16b-SO-DIMM",
+       "32b-SO-DIMM"
+};
+
+static const char * const ddr3_spd_moduletypes_width[] = {
+       "Unknown",
+       "133.35 mm",
+       "133.35 mm",
+       "67.6 mm",
+       "TBD",
+       "82.0 mm",
+       "82.0 mm",
+       "67.6 mm",
+       "67.6 mm",
+       "67.6 mm",
+       "67.6 mm",
+       "133.35 mm",
+       "67.6 mm",
+       "67.6 mm"
+};
+
+#define DDR3_UNBUFFERED                1
+#define DDR3_REGISTERED                2
+#define DDR3_CLOCKED           3
+#define DDR3_LOAD_REDUCED      4
+
+static const uint8_t ddr3_spd_moduletypes_family[] = {
+       0,
+       DDR3_REGISTERED,
+       DDR3_UNBUFFERED,
+       DDR3_UNBUFFERED,
+       DDR3_UNBUFFERED,
+       DDR3_REGISTERED,
+       DDR3_UNBUFFERED,
+       DDR3_CLOCKED,
+       DDR3_UNBUFFERED,
+       DDR3_REGISTERED,
+       DDR3_CLOCKED,
+       DDR3_LOAD_REDUCED,
+       DDR3_UNBUFFERED,
+       DDR3_UNBUFFERED
+};
+
+static const int ddr3_std_speeds[] = {
+       1000 * 7.5 / 8,
+       1000 * 7.5 / 7,
+       1000 * 1.25,
+       1000 * 1.5,
+       1000 * 1.875,
+       1000 * 2.5
+};
+
+static const char * const ddr3_maximum_activated_counts[] = {
+       "Untested",
+       "700 K",
+       "600 K",
+       "500 K",
+       "400 K",
+       "300 K",
+       "200 K",
+       "Reserved",
+       "Unlimited",
+};
+
+static int ddr3_timing_from_mtb_ftb(uint16_t txx, int8_t fine_txx,
+                                   uint8_t mtb_dividend, uint8_t mtb_divisor,
+                                   uint8_t ftb_div)
+{
+       /*
+        * Given mtb in ns and ftb in ps, return the result in ps,
+        * carefully rounding to the nearest picosecond.
+        */
+       int result = txx * 10000 * mtb_dividend / mtb_divisor
+                    + fine_txx * (ftb_div >> 4) / (ftb_div & 0xf);
+       return DIV_ROUND_CLOSEST(result, 10);
+}
+
+static int ddr3_adjust_ctime(int ctime, uint8_t ftb_div)
+{
+       uint8_t ftb_divisor = ftb_div >> 4;
+       uint8_t ftb_dividend = ftb_div & 0xf;
+       int i;
+
+       /*
+        * Starting with DDR3-1866, vendors may start approximating the
+        * minimum cycle time. Try to guess what they really meant so
+        * that the reported speed matches the standard.
+        */
+       for (i = 7; i < 15; i++) {
+               int test = ctime * ftb_divisor - (int)(1000 * 7.5) * 
ftb_divisor / i;
+
+               if (test > -(int)ftb_dividend && test < ftb_dividend)
+                       return (int)(1000 * 7.5) / i;
+       }
+
+       return ctime;
+}
+
+static void ddr3_print_reference_card(uint8_t ref_raw_card, uint8_t mod_height)
+{
+       const char alphabet[] = "ABCDEFGHJKLMNPRTUVWY";
+       uint8_t ref = ref_raw_card & 0x1f;
+       uint8_t revision = mod_height >> 5;
+       char ref_card[3] = { 0 };
+
+       if (ref == 0x1f) {
+               printf("%-48s %s\n", "Module Reference Card", "ZZ");
+               return;
+       }
+
+       if (ref_raw_card & 0x80)
+               ref += 0x1f;
+       if (!revision)
+               revision = ((ref_raw_card >> 5) & 0x3);
+
+       if (ref < ARRAY_SIZE(alphabet) - 1) {
+               /* One letter reference card */
+               ref_card[0] = alphabet[ref];
+       } else {
+               /* Two letter reference card */
+               uint8_t ref1 = ref / (ARRAY_SIZE(alphabet) - 1);
+
+               ref -= (ARRAY_SIZE(alphabet) - 1) * ref1;
+               ref_card[0] = alphabet[ref1];
+               ref_card[1] = alphabet[ref];
+       }
+
+       printf("%-48s %s revision %u\n", "Module Reference Card", ref_card, 
revision);
+}
+
+static void ddr3_print_revision_number(const char *name, uint8_t rev)
+{
+       uint8_t h = rev >> 4;
+       uint8_t l = rev & 0xf;
+
+       /* Decode as suggested by JEDEC Standard 21-C */
+       if (!h)
+               printf("%-48s %d\n", name, l);
+       if (h < 0xa)
+               printf("%-48s %d.%d\n", name, h, l);
+       else
+               printf("%-48s %c%d\n", name, 'A' + h - 0xa, l);
+}
+
+static void ddr3_spd_print(uint8_t *record)
+{
+       struct ddr3_spd_eeprom *s = (struct ddr3_spd_eeprom *)record;
+       const char *sum;
+       uint8_t spd_len;
+       int size, bytes_written;
+       int ctime, ddrclk;
+       uint8_t tbits;
+       int pcclk;
+       uint8_t cap, k;
+       int taa, trcd, trp, tras;
+       uint16_t cas_sup;
+       int twr, trrd, trc, trfc, twtr, trtp, tfaw;
+       uint8_t die_count, loading;
+       uint8_t mac;
+       uint8_t family;
+       int i;
+
+       sum = ddr3_spd_check(s) ? "ERR" : "OK";
+
+       printf("\n---=== SPD EEPROM Information ===---\n");
+
+       printf("EEPROM CRC of bytes 0-%3d %22s %s (0x%02X%02X)\n",
+              (s->info_size_crc & 0x80) ? 116 : 125, "", sum, s->crc[1], 
s->crc[0]);
+
+       spd_len = (s->info_size_crc >> 4) & 0x7;
+       size = 64 << (s->info_size_crc & 0xf);
+       if (spd_len == 0) {
+               bytes_written = 128;
+       } else if (spd_len == 1) {
+               bytes_written = 176;
+       } else if (spd_len == 2) {
+               bytes_written = 256;
+       } else {
+               size = 64;
+               bytes_written = 64;
+       }
+       printf("%-48s %d\n", "# of bytes written to SDRAM EEPROM", 
bytes_written);
+       printf("%-48s %d\n", "Total number of bytes in EEPROM", size);
+       printf("%-48s %s\n", "Fundamental Memory type", type_list[s->mem_type]);
+
+       if (s->spd_rev != 0xff)
+               printf("%-48s %x.%x\n", "SPD Revision", s->spd_rev >> 4,
+                      s->spd_rev & 0xf);
+
+       printf("%-48s ", "Module Type");
+       if (s->module_type <= ARRAY_SIZE(ddr3_spd_moduletypes))
+               printf("%s\n", ddr3_spd_moduletypes[s->module_type]);
+       else
+               printf("Reserved (0x%02x)\n", s->module_type);
+
+       if ((s->ftb_div & 0x0f) == 0 || s->mtb_divisor == 0) {
+               printf("Invalid time base divisor, can't decode\n");
+               return;
+       }
+
+       printf("\n---=== Memory Characteristics ===---\n");
+
+       ctime = ddr3_timing_from_mtb_ftb(s->tck_min, s->fine_tck_min,
+                                        s->mtb_dividend, s->mtb_divisor, 
s->ftb_div);
+       ctime = ddr3_adjust_ctime(ctime, s->ftb_div);
+
+       ddrclk = 2 * 1000 * 1000 / ctime;
+       tbits = 1 << ((s->bus_width & 0x7) + 3);
+       pcclk = ddrclk * tbits / 8;
+       pcclk = pcclk - (pcclk % 100);  /* Round down to comply with JEDEC */
+       printf("%-48s %d MT/s (PC3-%d)\n", "Maximum module speed", ddrclk, 
pcclk);
+
+       cap = (s->density_banks & 0xf) + 28 + (s->bus_width & 0x7) + 3
+             - ((s->organization & 0x7) + 2) - (20 + 3);
+       k = (s->organization >> 3) + 1;
+       printf("%-48s %d MB\n", "Size", (1 << cap) * k);
+
+       printf("%-48s %d x %d x %d x %d\n", "Banks x Rows x Columns x Bits",
+              1 << (((s->density_banks >> 4) & 0x7) + 3),
+              (((s->addressing >> 3) & 0x1f) + 12),
+              ((s->addressing & 7) + 9),
+              (1 << ((s->bus_width & 0x7) + 3)));
+
+       printf("%-48s %d\n", "Ranks", k);
+
+       printf("%-48s %d bits\n", "SDRAM Device Width",
+              (1 << ((s->organization & 0x7) + 2)));
+
+       printf("%-48s %d bits\n", "Primary Bus Width",
+              (8 << (s->bus_width & 0x7)));
+       if (s->bus_width & 0x18)
+               printf("%-48s %d bits\n", "Bus Width Extension", s->bus_width & 
0x18);
+
+       taa = ddr3_timing_from_mtb_ftb(s->taa_min, s->fine_taa_min,
+                                      s->mtb_dividend, s->mtb_divisor, 
s->ftb_div);
+       trcd = ddr3_timing_from_mtb_ftb(s->trcd_min, s->fine_trcd_min,
+                                       s->mtb_dividend, s->mtb_divisor, 
s->ftb_div);
+       trp = ddr3_timing_from_mtb_ftb(s->trp_min, s->fine_trp_min,
+                                      s->mtb_dividend, s->mtb_divisor, 
s->ftb_div);
+       tras = (((s->tras_trc_ext & 0xf) << 8) + s->tras_min_lsb)
+              * 1000 * s->mtb_dividend / s->mtb_divisor;
+
+       printf("%-48s %d-%d-%d-%d\n", "tCL-tRCD-tRP-tRAS",
+              DIV_ROUND_UP(taa, ctime), DIV_ROUND_UP(trcd, ctime),
+              DIV_ROUND_UP(trp, ctime), DIV_ROUND_UP(tras, ctime));
+
+       printf("%-48s", "Supported CAS Latencies (tCL)");
+       cas_sup = (s->caslat_msb << 8) + s->caslat_lsb;
+       for (i = 14; i >= 0; i--) {
+               if (cas_sup & (1 << i))
+                       printf(" %dT", i + 4);
+       }
+       printf("\n");
+
+       printf("\n---=== Timings at Standard Speeds ===---\n");
+
+       for (i = 0; i < ARRAY_SIZE(ddr3_std_speeds); i++) {
+               int ctime_at_speed = ddr3_std_speeds[i];
+               int best_cas = 0;
+               int j;
+
+               /* Find min CAS latency at this speed */
+               for (j = 14; j >= 0; j--) {
+                       if (!(cas_sup & (1 << j)))
+                               continue;
+                       if (DIV_ROUND_UP(taa, ctime_at_speed) <= j + 4) {
+                               best_cas = j + 4;
+                       }
+               }
+
+               if (!best_cas || ctime_at_speed < ctime)
+                       continue;
+
+               printf("tCL-tRCD-tRP-tRAS as DDR3-%-4d %17s %d-%d-%d-%d\n",
+                      2000 * 1000 / ctime_at_speed, "", best_cas,
+                      DIV_ROUND_UP(trcd, ctime_at_speed),
+                      DIV_ROUND_UP(trp, ctime_at_speed),
+                      DIV_ROUND_UP(tras, ctime_at_speed));
+       }
+
+       printf("\n---=== Timing Parameters ===---\n");
+
+       printf("%-48s %d.%03d ns\n", "Minimum Cycle Time (tCK)",
+              ctime / 1000, ctime % 1000);
+       printf("%-48s %d.%03d ns\n", "Minimum CAS Latency Time (tAA)",
+              taa / 1000, taa % 1000);
+       twr = s->twr_min * 1000 * s->mtb_dividend / s->mtb_divisor;
+       printf("%-48s %d.%03d ns\n", "Minimum Write Recovery time (tWR)",
+              twr / 1000, twr % 1000);
+       printf("%-48s %d.%03d ns\n", "Minimum RAS# to CAS# Delay (tRCD)",
+              trcd / 1000, trcd % 1000);
+       trrd = s->trrd_min * 1000 * s->mtb_dividend / s->mtb_divisor;
+       printf("%-48s %d.%03d ns\n", "Minimum Row Active to Row Active Delay 
(tRRD)",
+              trrd / 1000, trrd % 1000);
+       printf("%-48s %d.%03d ns\n", "Minimum Row Precharge Delay (tRP)",
+              trp / 1000, trp % 1000);
+       printf("%-48s %d.%03d ns\n", "Minimum Active to Precharge Delay (tRAS)",
+              tras / 1000, tras % 1000);
+       trc = ddr3_timing_from_mtb_ftb(((s->tras_trc_ext & 0xf0) << 4) + 
s->trc_min_lsb,
+                                      s->fine_trc_min, s->mtb_dividend, 
s->mtb_divisor,
+                                      s->ftb_div);
+       printf("%-48s %d.%03d ns\n", "Minimum Active to Auto-Refresh Delay 
(tRC)",
+              trc / 1000, trc % 1000);
+       trfc = ((s->trfc_min_msb << 8) + s->trfc_min_lsb) * 1000
+              * s->mtb_dividend / s->mtb_divisor;
+       printf("%-48s %d.%03d ns\n", "Minimum Recovery Delay (tRFC)",
+              trfc / 1000, trfc % 1000);
+       twtr = s->twtr_min * 1000 * s->mtb_dividend / s->mtb_divisor;
+       printf("%-48s %d.%03d ns\n", "Minimum Write to Read CMD Delay (tWTR)",
+              twtr / 1000, twtr % 1000);
+       trtp = s->trtp_min * 1000 * s->mtb_dividend / s->mtb_divisor;
+       printf("%-48s %d.%03d ns\n", "Minimum Read to Pre-charge CMD Delay 
(tRTP)",
+              trtp / 1000, trtp % 1000);
+       tfaw = (((s->tfaw_msb & 0xf) << 8) + s->tfaw_min) * 1000
+              * s->mtb_dividend / s->mtb_divisor;
+       printf("%-48s %d.%03d ns\n", "Minimum Four Activate Window Delay 
(tFAW)",
+              tfaw / 1000, tfaw % 1000);
+
+       printf("\n---=== Optional Features ===---\n");
+
+       printf("%-48s 1.5V%s%s%s\n", "Operable voltages",
+              (s->module_vdd & 0x1) ? " tolerant" : "",
+              (s->module_vdd & 0x2) ? ", 1.35V" : "",
+              (s->module_vdd & 0x4) ? ", 1.2X V" : "");
+       printf("%-48s %s\n", "RZQ/6 supported?",
+              (s->opt_features & 1) ? "Yes" : "No");
+       printf("%-48s %s\n", "RZQ/7 supported?",
+              (s->opt_features & 2) ? "Yes" : "No");
+       printf("%-48s %s\n", "DLL-Off Mode supported?",
+              (s->opt_features & 0x80) ? "Yes" : "No");
+       printf("%-48s 0-%d degrees C\n", "Operating temperature range",
+              (s->therm_ref_opt & 0x1) ? 95 : 85);
+       if (s->therm_ref_opt & 0x1)
+               printf("%-48s %s\n", "Refresh Rate in extended temp range",
+                      (s->therm_ref_opt & 0x2) ? "1X" : "2X");
+       printf("%-48s %s\n", "Auto Self-Refresh?",
+              (s->therm_ref_opt & 0x4) ? "Yes" : "No");
+       printf("%-48s %s\n", "On-Die Thermal Sensor readout?",
+              (s->therm_ref_opt & 0x8) ? "Yes" : "No");
+       printf("%-48s %s\n", "Partial Array Self-Refresh?",
+              (s->therm_ref_opt & 0x80) ? "Yes" : "No");
+       printf("%-48s %s\n", "Module Thermal Sensor",
+              (s->therm_sensor & 0x80) ? "Yes" : "No");
+
+       printf("%-48s %s\n", "SDRAM Device Type",
+              (s->device_type & 0x80) ? "Non-Standard" : "Standard 
Monolithic");
+
+       die_count = (s->device_type >> 4) & 0x7;
+       if (die_count == 1)
+               printf("%-48s Single die\n", "");
+       else if (die_count == 2)
+               printf("%-48s 2 die\n", "");
+       else if (die_count == 3)
+               printf("%-48s 4 die\n", "");
+       else if (die_count == 4)
+               printf("%-48s 8 die\n", "");
+
+       loading = (s->device_type >> 2) & 0x3;
+       if (loading == 1)
+               printf("%-48s Multi load stack\n", "");
+       else if (loading == 2)
+               printf("%-48s Single load stack\n", "");
+
+       mac = s->res_39_59[2] & 0xf;
+       if (mac < ARRAY_SIZE(ddr3_maximum_activated_counts))
+               printf("%-48s %s\n", "Maximum Activate Count (MAC)",
+                      ddr3_maximum_activated_counts[mac]);
+
+       /*
+        * The following bytes are type-specific, so don't continue if the type
+        * is unknown.
+        */
+       if (!s->module_type || s->module_type > 
ARRAY_SIZE(ddr3_spd_moduletypes))
+               return;
+
+       family = ddr3_spd_moduletypes_family[s->module_type];
+       if (family == DDR3_UNBUFFERED || family == DDR3_REGISTERED
+           || family == DDR3_CLOCKED || family == DDR3_LOAD_REDUCED) {
+               printf("\n---=== Physical Characteristics ===---\n");
+               printf("%-48s %d mm\n", "Module Height",
+                      ((s->mod_section.unbuffered.mod_height & 0x1f) + 15));
+               printf("%-48s %d mm front, %d mm back\n", "Module Thickness",
+                      (s->mod_section.unbuffered.mod_thickness & 0xf) + 1,
+                      ((s->mod_section.unbuffered.mod_thickness >> 4) & 0xf) + 
1);
+               printf("%-48s %s\n", "Module Width",
+                      ddr3_spd_moduletypes_width[s->module_type]);
+               
ddr3_print_reference_card(s->mod_section.unbuffered.ref_raw_card,
+                                         s->mod_section.unbuffered.mod_height);
+       }
+
+       if (family == DDR3_UNBUFFERED)
+               printf("%-48s %s\n", "Rank 1 Mapping",
+                      (s->mod_section.unbuffered.addr_mapping) ? "Mirrored" : 
"Standard");
+
+       if (family == DDR3_REGISTERED) {
+               uint8_t rows = (s->mod_section.registered.modu_attr >> 2) & 0x3;
+               uint8_t registers = s->mod_section.registered.modu_attr & 0x3;
+
+               printf("\n---=== Registered DIMM ===---\n");
+
+               if (!rows)
+                       printf("%-48s Undefined\n", "# DRAM Rows");
+               else
+                       printf("%-48s %u\n", "# DRAM Rows", 1 << (rows - 1));
+
+               if (!registers)
+                       printf("%-48s Undefined\n", "# Registers");
+               else
+                       printf("%-48s %u\n", "# Registers", 1 << (registers - 
1));
+               printf("%-48s 0x%02x%02x\n", "Register manufacturer ID",
+                      s->mod_section.registered.reg_id_hi,
+                      s->mod_section.registered.reg_id_lo);
+               printf("%-48s %s\n", "Register device type",
+                      (!s->mod_section.registered.reg_type) ? "SSTE32882" : 
"Undefined");
+               if (s->mod_section.registered.reg_rev != 0xff)
+                       ddr3_print_revision_number("Register revision",
+                                                  
s->mod_section.registered.reg_rev);
+               printf("%-48s %s\n", "Heat spreader",
+                      (s->mod_section.registered.thermal & 0x80) ? "Yes" : 
"No");
+       }
+
+       if (family == DDR3_LOAD_REDUCED) {
+               uint8_t modu_attr = s->mod_section.loadreduced.modu_attr;
+               uint8_t rows = (modu_attr >> 2) & 0x3;
+               static const char *mirroring[] = {
+                       "None", "Odd ranks", "Reserved", "Reserved"
+               };
+
+               printf("\n---=== Load Reduced DIMM ===---\n");
+
+               if (!rows)
+                       printf("%-48s Undefined\n", "# DRAM Rows");
+               else if (rows == 0x3)
+                       printf("%-48s Reserved\n", "# DRAM Rows");
+               else
+                       printf("%-48s %u\n", "# DRAM Rows", 1 << (rows - 1));
+
+               printf("%-48s %s\n", "Mirroring",
+                      mirroring[modu_attr & 0x3]);
+
+               printf("%-48s %s\n", "Rank Numbering",
+                      (modu_attr & 0x20) ? "Even only" : "Contiguous");
+               printf("%-48s %s\n", "Buffer Orientation",
+                      (modu_attr & 0x10) ? "Horizontal" : "Vertical");
+               printf("%-48s 0x%02x%02x\n", "Register Manufacturer ID",
+                       s->mod_section.loadreduced.buf_id_hi,
+                       s->mod_section.loadreduced.buf_id_lo);
+               if (s->mod_section.loadreduced.buf_rev_id != 0xff)
+                       ddr3_print_revision_number("Buffer Revision",
+                                                  
s->mod_section.loadreduced.buf_rev_id);
+               printf("%-48s %s\n", "Heat spreader",
+                      (s->mod_section.loadreduced.modu_attr & 0x80) ? "Yes" : 
"No");
+       }
+
+       printf("\n---=== Manufacturer Data ===---\n");
+
+       printf("%-48s 0x%02x%02x\n", "Module Manufacturer ID",
+              s->mmid_msb, s->mmid_lsb);
+
+       if (!((s->dmid_lsb == 0xff) && (s->dmid_msb == 0xff))
+           && !((s->dmid_lsb == 0x0) && (s->dmid_msb == 0x0)))
+               printf("%-48s 0x%02x%02x\n", "DRAM Manufacturer ID",
+                      s->dmid_msb, s->dmid_lsb);
+
+       if (!(s->mloc == 0xff) && !(s->mloc == 0x0))
+               printf("%-48s 0x%02x\n", "Manufacturing Location Code",
+                      s->mloc);
+
+       if (!((s->mdate[0] == 0xff) && (s->mdate[1] == 0xff))
+           && !((s->mdate[0] == 0x0) && (s->mdate[1] == 0x0)))
+               spd_print_manufacturing_date(s->mdate[0], s->mdate[1]);
+
+       if ((s->sernum[0] != s->sernum[1])
+           && (s->sernum[0] != s->sernum[2])
+           && (s->sernum[1] != s->sernum[3])
+           && ((s->sernum[0] != 0xff) || (s->sernum[0] != 0x0)))
+               printf("%-48s 0x%02X%02X%02X%02X\n", "Assembly Serial Number",
+                      s->sernum[0], s->sernum[1], s->sernum[2], s->sernum[3]);
+
+       printf("%-48s ", "Part Number");
+       if (!(s->mpart[0] >= 32 && s->mpart[0] < 127)) {
+               printf("Undefined\n");
+       } else {
+               for (i = 0; i < ARRAY_SIZE(s->mpart); i++) {
+                       if (s->mpart[i] >= 32 && s->mpart[i] < 127)
+                               printf("%c", s->mpart[i]);
+                       else
+                               break;
+               }
+               printf("\n");
+       }
+
+       if (!((s->mrev[0] == 0xff) && (s->mrev[1] == 0xff))
+           && !((s->mrev[0] == 0x0) && (s->mrev[1] == 0x0)))
+               printf("%-48s 0x%02X%02X\n", "Revision Code", s->mrev[0], 
s->mrev[1]);
+}
+
+void ddr_spd_print(uint8_t *record)
+{
+       uint8_t mem_type = record[2];
+
+       switch (mem_type) {
+       case SPD_MEMTYPE_DDR2:
+               ddr2_spd_print(record);
+               break;
+       case SPD_MEMTYPE_DDR3:
+               ddr3_spd_print(record);
+               break;
+       default:
+               printf("Can only dump SPD information for DDR2 and DDR3 memory 
types\n");
+       }
+}
+
 #define SPD_SPA0_ADDRESS        0x36
 #define SPD_SPA1_ADDRESS        0x37
 
-- 
2.44.0


Reply via email to