@@ -0,0 +1,365 @@
+Subject: decode-dimms: Improve DDR3 support
+Upstream: yes, r6132 to r6149
+
+This is my hackweek 9 project:
+https://github.com/SUSE/hackweek/wiki/DDR3-SPD-Information-Decoding
+---
+ eeprom/decode-dimms | 271 +++++++++++++++++++++++++++++++++++++---------------
+ 1 file changed, 195 insertions(+), 76 deletions(-)
+
+--- a/eeprom/decode-dimms
++++ b/eeprom/decode-dimms
+@@ -1186,36 +1186,137 @@ sub decode_ddr2_sdram($)
+ printl("PLL Relock Time", $bytes->[46] . " us") if ($bytes->[46]);
+ }
+
++# Return combined time in ns
++sub ddr3_mtb_ftb($$$$)
++{
++ my ($byte1, $byte2, $mtb, $ftb) = @_;
++
++ # byte1 is unsigned in ns, but byte2 is signed in ps
++ $byte2 -= 0x100 if $byte2 & 0x80;
++
++ return $byte1 * $mtb + $byte2 * $ftb / 1000;
++}
++
++sub ddr3_reference_card($$)
++{
++ my ($rrc, $ext) = @_;
++ my $alphabet = "ABCDEFGHJKLMNPRTUVWY";
++ my $ref = $rrc & 0x1f;
++ my $revision = $ext >> 5;
++ my $ref_card;
++
++ return "ZZ" if $ref == 0x1f;
++ $ref += 0x1f if $rrc & 0x80;
++ $revision = (($rrc >> 5) & 0x03) if $revision == 0;
++
++ if ($ref < length($alphabet)) {
++ # One letter reference card
++ $ref_card = substr($alphabet, $ref, 1);
++ } else {
++ # Two letter reference card
++ my $ref1 = int($ref / (length($alphabet)));
++ $ref -= length($alphabet) * $ref1;
++ $ref_card = substr($alphabet, $ref1, 1) .
++ substr($alphabet, $ref, 1);
++ }
++
++ return "$ref_card revision $revision";
++}
++
++sub ddr3_revision_number($)
++{
++ my $h = $_[0] >> 4;
++ my $l = $_[0] & 0x0f;
++
++ # Decode as suggested by JEDEC Standard 21-C
++ return sprintf("%d", $l) if $h == 0;
++ return sprintf("%d.%d", $h, $l) if $h < 0xa;
++ return sprintf("%c%d", ord('A') + $h - 0xa, $l);
++}
++
++sub ddr3_device_type($)
++{
++ my $byte = shift;
++ my $type = $byte & 0x80 ? "Non-Standard" : "Standard Monolithic";
++ my $die_count = ($byte >> 4) & 0x07;
++ my $loading = ($byte >> 2) & 0x03;
++
++ if ($die_count == 1) {
++ $type .= "\nSingle die";
++ } elsif ($die_count == 2) {
++ $type .= "\n2 die";
++ } elsif ($die_count == 3) {
++ $type .= "\n4 die";
++ } elsif ($die_count == 4) {
++ $type .= "\n8 die";
++ }
++
++ if ($loading == 1) {
++ $type .= "\nMulti load stack";
++ } elsif ($loading == 2) {
++ $type .= "\nSingle load stack";
++ }
++
++ return $type;
++}
++
++use constant DDR3_UNBUFFERED => 1;
++use constant DDR3_REGISTERED => 2;
++use constant DDR3_CLOCKED => 3;
++use constant DDR3_LOAD_REDUCED => 4;
++
+ # Parameter: EEPROM bytes 0-127 (using 3-76)
+ sub decode_ddr3_sdram($)
+ {
+ my $bytes = shift;
+ my $temp;
+ my $ctime;
++ my ($ftb, $mtb);
++ my $ii;
+
+- my @module_types = ("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");
++ my @module_types = (
++ { type => "Undefined", width => "Unknown" },
++ { type => "RDIMM", width => "133.35 mm", family => DDR3_REGISTERED },
++ { type => "UDIMM", width => "133.35 mm", family => DDR3_UNBUFFERED },
++ { type => "SO-DIMM", width => "67.6 mm", family => DDR3_UNBUFFERED },
++ { type => "Micro-DIMM", width => "TBD", family => DDR3_UNBUFFERED },
++ { type => "Mini-RDIMM", width => "82.0 mm", family => DDR3_REGISTERED },
++ { type => "Mini-UDIMM", width => "82.0 mm", family => DDR3_UNBUFFERED },
++ { type => "Mini-CDIMM", width => "67.6 mm", family => DDR3_CLOCKED },
++ { type => "72b-SO-UDIMM", width => "67.6 mm", family => DDR3_UNBUFFERED },
++ { type => "72b-SO-RDIMM", width => "67.6 mm", family => DDR3_REGISTERED },
++ { type => "72b-SO-CDIMM", width => "67.6 mm", family => DDR3_CLOCKED },
++ { type => "LRDIMM", width => "133.35 mm", family => DDR3_LOAD_REDUCED },
++ { type => "16b-SO-DIMM", width => "67.6 mm", family => DDR3_UNBUFFERED },
++ { type => "32b-SO-DIMM", width => "67.6 mm", family => DDR3_UNBUFFERED },
++ );
+
+ printl("Module Type", ($bytes->[3] <= $#module_types) ?
+- $module_types[$bytes->[3]] :
++ $module_types[$bytes->[3]]->{type} :
+ sprintf("Reserved (0x%.2X)", $bytes->[3]));
+
++# time bases
++ if (($bytes->[9] & 0x0f) == 0 || $bytes->[11] == 0) {
++ print STDERR "Invalid time base divisor, can't decode\n";
++ return;
++ }
++ $ftb = ($bytes->[9] >> 4) / ($bytes->[9] & 0x0f);
++ $mtb = $bytes->[10] / $bytes->[11];
++
+ # speed
+ prints("Memory Characteristics");
+
+- my $dividend = ($bytes->[9] >> 4) & 15;
+- my $divisor = $bytes->[9] & 15;
+- printl("Fine time base", sprintf("%.3f", $dividend / $divisor) . " ps");
+-
+- $dividend = $bytes->[10];
+- $divisor = $bytes->[11];
+- my $mtb = $dividend / $divisor;
+- printl("Medium time base", tns3($mtb));
++ $ctime = ddr3_mtb_ftb($bytes->[12], $bytes->[34], $mtb, $ftb);
++ # 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 ($ii = 7; $ii < 15; $ii++) {
++ if ($ctime > 7.5/$ii - $ftb/1000 && $ctime < 7.5/$ii + $ftb/1000) {
++ $ctime = 7.5/$ii;
++ last;
++ }
++ }
+
+- $ctime = $bytes->[12] * $mtb;
+ my $ddrclk = 2 * (1000 / $ctime);
+ my $tbits = 1 << (($bytes->[8] & 7) + 3);
+ my $pcclk = int ($ddrclk * $tbits / 8);
+@@ -1249,17 +1350,16 @@ sub decode_ddr3_sdram($)
+ my $trp;
+ my $tras;
+
+- $taa = ceil($bytes->[16] / $bytes->[12]);
+- $trcd = ceil($bytes->[18] / $bytes->[12]);
+- $trp = ceil($bytes->[20] / $bytes->[12]);
+- $tras = ceil(((($bytes->[21] & 0x0f) << 8) + $bytes->[22]) / $bytes->[12]);
++ $taa = ddr3_mtb_ftb($bytes->[16], $bytes->[35], $mtb, $ftb);
++ $trcd = ddr3_mtb_ftb($bytes->[18], $bytes->[36], $mtb, $ftb);
++ $trp = ddr3_mtb_ftb($bytes->[20], $bytes->[37], $mtb, $ftb);
++ $tras = ((($bytes->[21] & 0x0f) << 8) + $bytes->[22]) * $mtb;
+
+- printl("tCL-tRCD-tRP-tRAS", join("-", $taa, $trcd, $trp, $tras));
++ printl("tCL-tRCD-tRP-tRAS", ddr_core_timings(ceil($taa / $ctime), $ctime, $trcd, $trp, $tras));
+
+ # latencies
+ my $highestCAS = 0;
+ my %cas;
+- my $ii;
+ my $cas_sup = ($bytes->[15] << 8) + $bytes->[14];
+ for ($ii = 0; $ii < 15; $ii++) {
+ if ($cas_sup & (1 << $ii)) {
+@@ -1269,14 +1369,38 @@ sub decode_ddr3_sdram($)
+ }
+ printl("Supported CAS Latencies (tCL)", cas_latencies(keys %cas));
+
++# standard DDR3 speeds
++ prints("Timings at Standard Speeds");
++ foreach my $ctime_at_speed (7.5/8, 7.5/7, 1.25, 1.5, 1.875, 2.5) {
++ my $best_cas = 0;
++
++ # Find min CAS latency at this speed
++ for ($ii = 14; $ii >= 0; $ii--) {
++ next unless ($cas_sup & (1 << $ii));
|