public inbox for linux-i2c@vger.kernel.org
 help / color / mirror / Atom feed
* Preliminary support for DDR3 in decode-dimms (fwd)
@ 2008-11-23 15:04 Paul Goyette
       [not found] ` <Pine.NEB.4.64.0811230659540.29668-j58mV80z1I3O+KjX/08HYEEOCMrvLtNR@public.gmane.org>
  0 siblings, 1 reply; 9+ messages in thread
From: Paul Goyette @ 2008-11-23 15:04 UTC (permalink / raw)
  To: linux-i2c-u79uwXL29TY76Z2rM5mHXA

[-- Attachment #1: Type: TEXT/PLAIN, Size: 878 bytes --]

The attached diffs provide some preliminary decode of the DDR3 SPD... 
This work is based on the recently-published Appendix K to JESD21-C
(see http://www.jedec.org/download/search/4_01_02_11R18.pdf).

Warning:  I'm no perl expert.  The code I've written is probably ugly to
most of the readers of this list.  But it does work!

There's no attempt made here to decode the Intel XMP extension data 
(located beyond byte 176).  I still haven't been able to find a spec.

----------------------------------------------------------------------
|   Paul Goyette   | PGP DSS Key fingerprint: |  E-mail addresses:   |
| Customer Service | FA29 0E3B 35AF E8AE 6651 |  paul-3orOWTcw9wBWk0Htik3J/w@public.gmane.org   |
| Network Engineer | 0786 F758 55DE 53BA 7731 | pgoyette-3r7Miqu9kMnR7s880joybQ@public.gmane.org |
----------------------------------------------------------------------

[-- Attachment #2: Type: TEXT/PLAIN, Size: 8759 bytes --]

872a873,1035
> # Parameter: bytes 0-127
> sub decode_ddr3_sdram($)
> {
> 	my $bytes = shift;
> 	my $l;
> 	my $temp;
> 	my $ctime;
> 
> # SPD revision
> 	if ($bytes->[62] != 0xff) {
> 		printl "SPD Revision", ($bytes->[62] >> 4) . "." .
> 				       ($bytes->[62] & 0xf);
> 	}
> 
> # speed
> 	prints "Memory Characteristics";
> 
> 	$l = "Fine time base";
> 	my $dividend = ($bytes->[9] >> 4) & 15;
> 	my $divisor  = $bytes->[9] & 15;
> 	printl $l, sprintf("%.3f", $dividend / $divisor) . " ps";
> 
> 	$l = "Medium time base";
> 	$dividend = $bytes->[10];
> 	$divisor  = $bytes->[11];
> 	my $mtb = $dividend / $divisor;
> 	printl $l, tns($mtb);
> 
> 	$l = "Maximum module speed";
> 	$ctime = $bytes->[12] * $mtb;
> 	my $ddrclk = 2 * (1000 / $ctime);
> 	my $tbits = 1 << (($bytes->[8] & 7) + 3);
> 	my $pcclk = int ($ddrclk * $tbits / 8);
> 	$ddrclk = int ($ddrclk);
> 	printl $l, "${ddrclk}MHz (PC3-${pcclk})";
> 
> # Size computation
> 
> 	my $cap =  ($bytes->[4]       & 15) + 28;
> 	$cap   +=  ($bytes->[8]       & 7)  + 3;
> 	$cap   -=  ($bytes->[7]       & 7)  + 2;
> 	$cap   -= 20 + 3;
> 	my $k   = (($bytes->[7] >> 3) & 31) + 1;
> 	printl "Size", ((1 << $cap) * $k) . " MB";
> 
> 	printl "Banks x Rows x Columns x Bits",
> 	       join(' x ', 1 << ((($bytes->[4] >> 4) &  7) +  3),
> 			   ((($bytes->[5] >> 3) & 31) + 12),
> 			   ( ($bytes->[5]       &  7) +  9),
> 			   ( 1 << (($bytes->[6] &  7) + 3)) );
> 	printl "Ranks", $k;
> 
> 	printl "SDRAM Device Width", $bytes->[13]." bits";
> 
> 	my $taa;
> 	my $trcd;
> 	my $trp;
> 	my $tras;
> 
> 	$taa  = int($bytes->[16] / $bytes->[12]);
> 	$trcd = int($bytes->[18] / $bytes->[12]);
> 	$trp  = int($bytes->[20] / $bytes->[12]);
> 	$tras = int((($bytes->[21] >> 4) * 256 + $bytes->[22]) / $bytes->[12]);
> 
> 	printl "tCL-tRCD-tRP-tRAS", join("-", $taa, $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)) {
> 			$highestCAS = $ii + 4;
> 			$cas{$highestCAS}++;
> 		}
> 	}
> 	printl "Supported CAS Latencies (tCL)", cas_latencies(keys %cas);
> 
> # more timing information
> 	prints "Timing Parameters" ;
> 
> 	printl "Minimum Write Recovery time (tWRmin)", tns($bytes->[17] * $mtb);
> 	printl "Minimum Row Active-to-Row Active Delay (tRRDmin)",
> 		tns($bytes->[19] * $mtb);
> 	printl "Minimum Active-to-Active Refresh Delay (tRCmin)",
> 		tns((((($bytes->[21] >> 4) & 15) << 8) + $bytes->[23]) * $mtb);
> 	printl "Minimum Refresh Recovery Delay (tRFCmin)", 
> 		tns((($bytes->[25] << 8) + $bytes->[24]) * $mtb);
> 	printl "Minimum Write-to-Read Cmd Delay (tWTRmin)",
> 		tns($bytes->[26] * $mtb);
> 	printl "Minimum Read-to-Precharge Cmd Delay (tRTPmin)",
> 		tns($bytes->[27] * $mtb);
> 	printl "Minimum Four Activate Window Delay (tFAWmin)",
> 		tns((($bytes->[28] & 15) << 8 + $bytes->[29]) * $mtb);
> 
> # miscellaneous stuff
> 	prints "Optional Features";
> 
> 	printl "RZQ/6 supported?", ($bytes->[30] & 1) ? "Yes" : "No";
> 	printl "RZQ/7 supported?", ($bytes->[30] & 2) ? "Yes" : "No";
> 	printl "DLL-Off Mode supported?", ($bytes->[30] & 128) ? "Yes" : "No";
> 	printl "Extended (90-95C) operating range?",
> 		($bytes->[31] & 1) ? "Yes" : "No";
> 	printl "Refresh Rate in extended range",
> 		($bytes->[31] & 2) ? "2X" : "1X";
> 	printl "Auto Self-Refresh?", ($bytes->[31] & 4) ? "Yes" : "No";
> 	printl "On-Die Thermal Sensor readout?",
> 		($bytes->[31] & 8) ? "Yes" : "No";
> 	printl "Partial Array Self-Refresh?",
> 		($bytes->[31] & 128) ? "Yes" : "No";
> 	printl "Thermal Sensor Accuracy",
> 		($bytes->[32] & 128) ? sprintf($bytes->[32] & 127) :
> 					"Not implemented";
> 	printl "SDRAM Device Type",
> 		($bytes->[33] & 128) ? sprintf($bytes->[33] & 127) :
> 					"Standard Monolithic";
> 
> 	prints "Manufacturer Data";
> 
> 	$l = "Module Manufacturer Code";
> 	printl $l, sprintf("0x%.2X%.2X", $bytes->[118], $bytes->[117]);
> 
> 	$l = "DRAM Manufacturer Code";
> 	printl $l, sprintf("0x%.2X%.2X", $bytes->[148], $bytes->[149]);
> 
> 	$l = "Manufacturing Location";
> 	$temp = (chr($bytes->[8]) =~ m/^[\w\d]$/) ? chr($bytes->[8])
> 	      : sprintf("0x%.2X", $bytes->[8]);
> 	printl $l, $temp;
> 
> 	$l = "Part Number";
> 	$temp = "";
> 	for (my $i = 128; $i <= 145; $i++) {
> 		$temp .= chr($bytes->[$i]);
> 	};
> 	printl $l, $temp;
> 
> 	$l = "Revision";
> 	$temp = sprintf("0x%02X%02X\n", $bytes->[146], $bytes->[147]);
> 	printl $l, $temp;
> 
> 	$l = "Manufacturing Date";
> 	# In theory the year and week are in BCD format, but
> 	# this is not always true in practice :(
> 	if (($bytes->[120] & 0xf0) <= 0x90
> 	 && ($bytes->[120] & 0x0f) <= 0x09
> 	 && ($bytes->[121] & 0xf0) <= 0x90
> 	 && ($bytes->[121] & 0x0f) <= 0x09) {
> 		# Note that this heuristic will break in year 2080
> 		$temp = sprintf("%d%02X-W%02X\n",
> 				$bytes->[120] >= 0x80 ? 19 : 20,
> 				$bytes->[120], $bytes->[121]);
> 	} else {
> 		$temp = sprintf("0x%02X%02X\n", $bytes->[120], $bytes->[121]);
> 	}
> 	printl $l, $temp;
> 
> 	$l = "Assembly Serial Number";
> 	$temp = sprintf("0x%02X%02X%02X%02X\n", $bytes->[122], $bytes->[123]);
> 	printl $l, $temp;
> }
> 
1063a1227
> 	"DDR3 SDRAM"	=> \&decode_ddr3_sdram,
1181a1346,1371
> sub readfullspd($$) # reads all bytes from SPD-EEPROM
> {
> 	my ($size, $dimm_i) = @_;
> 	my @bytes;
> 	if ($use_hexdump) {
> 		@bytes = read_hexdump($dimm_i);
> 		return @bytes[0..$size];
> 	} elsif ($use_sysfs) {
> 		# Kernel 2.6 with sysfs
> 		sysopen(HANDLE, "/sys/bus/i2c/drivers/eeprom/$dimm_i/eeprom", O_RDONLY)
> 			or die "Cannot open /sys/bus/i2c/drivers/eeprom/$dimm_i/eeprom";
> 		binmode HANDLE;
> 		sysseek(HANDLE, 0, SEEK_SET);
> 		sysread(HANDLE, my $eeprom, $size);
> 		close HANDLE;
> 		@bytes = unpack(sprintf("C%d", $size), $eeprom);
> 	} else {
> 		# Kernel 2.4 with procfs
> 		for my $i (0 .. $size/16) {
> 			my $hexoff = sprintf('%02x', $i * 16);
> 			push @bytes, split(" ", `cat /proc/sys/dev/sensors/$dimm_i/$hexoff`);
> 		}
> 	}
> 	return @bytes;
> }
> 
1274,1276d1463
< 		next unless $bytes[63] == $dimm_checksum || $opt_igncheck;
< 		$dimm_count++;
< 
1295,1300d1481
< 		my $l = "EEPROM Checksum of bytes 0-62";
< 		printl $l, ($bytes[63] == $dimm_checksum ?
< 			sprintf("OK (0x%.2X)", $bytes[63]):
< 			sprintf("Bad\n(found 0x%.2X, calculated 0x%.2X)\n",
< 				$bytes[63], $dimm_checksum));
< 
1302c1483,1528
< 		my $is_rambus = $bytes[0] < 4;
---
> 		my $is_rambus = ($bytes[0] < 4 && $bytes[0] >= 0);
> 
> 		if ($is_rambus || $bytes[2] <= 8) {
> 			my $l = "EEPROM Checksum of bytes 0-62";
> 			printl $l, ($bytes[63] == $dimm_checksum ?
> 				sprintf("OK (0x%.2X)", $bytes[63]):
> 				sprintf("Bad\n(found 0x%.2X, calculated 0x%.2X)\n",
> 					$bytes[63], $dimm_checksum));
> 			next unless $bytes[63] == $dimm_checksum ||
> 				    $opt_igncheck;
> 			$dimm_count++;
> 
> 		} else {
> 			my @sizes = ( 128, 176, 256, 0, 0, 0, 0, 0);
> 			my $spdsize = $sizes[($bytes[0] >> 4) & 7];
> 
> 			@bytes = readfullspd($spdsize, $dimm_list[$i]);
> 			my $dimm_crc = 0;
> 			my $crc_cover = $bytes[0] & 0x80 ? 116 : 125;
> 			my $crc_ptr = 0;
> 			my $crc_bit;
> 			while ($crc_ptr <= $crc_cover) {
> 				$dimm_crc = $dimm_crc ^ ($bytes[$crc_ptr] << 8);
> 				for ($crc_bit = 0; $crc_bit < 8; $crc_bit++) {
> 					if ($dimm_crc & 0x8000) {
> 						$dimm_crc = ($dimm_crc << 1) ^
> 							0x1021;
> 					} else {
> 						$dimm_crc = $dimm_crc << 1
> 					}
> 				}
> 				$crc_ptr++;
> 			}
> 			$dimm_crc = $dimm_crc & 0xffff;
> 
> 			my $l = "EEPROM CRC of bytes 0-" .
> 				sprintf("%d", $crc_cover);
> 			my $crc_calc = $bytes[127] << 8 | $bytes[126];
> 			printl $l, ($dimm_crc == $crc_calc)?
> 				sprintf("OK (0x%.4X)", $dimm_crc):
> 				sprintf("Bad\n(found 0x%.4X, calculated 0x%.4X)\n",
> 					$crc_calc, $dimm_crc);
> 			next unless $crc_calc == $dimm_crc || $opt_igncheck;
> 			$dimm_count++;
> 		}
> 
1315c1541
< 		$l = "Total number of bytes in EEPROM";
---
> 		my $l = "Total number of bytes in EEPROM";
1335a1562,1564
> 			elsif ($bytes[2] == 9) { $type = "FB-DIMM"; }
> 			elsif ($bytes[2] == 10) { $type = "FB-DIMM PROBE"; }
> 			elsif ($bytes[2] == 11) { $type = "DDR3 SDRAM"; }
1342a1572,1576
> 		# DDR3 Manufacturer info is already decoded
> 		# (It's NOT common!)
> 
> 		next if ($type eq "DDR3 SDRAM");
> 
1346c1580,1584
< 		@bytes = readspd64(64, $dimm_list[$i]);
---
> 		if ($#bytes == 63) {
> 			@bytes = readspd64(64, $dimm_list[$i]);
> 		} else {
> 			@bytes = @bytes[64..$#bytes];
> 		}

^ permalink raw reply	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2008-12-09 14:02 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2008-11-23 15:04 Preliminary support for DDR3 in decode-dimms (fwd) Paul Goyette
     [not found] ` <Pine.NEB.4.64.0811230659540.29668-j58mV80z1I3O+KjX/08HYEEOCMrvLtNR@public.gmane.org>
2008-11-25  9:30   ` Jean Delvare
     [not found]     ` <20081125103039.2f14bac7-ig7AzVSIIG7kN2dkZ6Wm7A@public.gmane.org>
2008-11-25 12:10       ` Paul Goyette
     [not found]         ` <Pine.NEB.4.64.0811250408050.29070-j58mV80z1I3O+KjX/08HYEEOCMrvLtNR@public.gmane.org>
2008-12-08 13:59           ` Jean Delvare
     [not found]             ` <20081208145902.5962408c-ig7AzVSIIG7kN2dkZ6Wm7A@public.gmane.org>
2008-12-08 14:24               ` Paul Goyette
     [not found]                 ` <Pine.NEB.4.64.0812080602060.14436-j58mV80z1I3O+KjX/08HYEEOCMrvLtNR@public.gmane.org>
2008-12-08 18:05                   ` Paul Goyette
2008-12-09  9:12                   ` Jean Delvare
     [not found]                     ` <20081209101225.2ea39aae-ig7AzVSIIG7kN2dkZ6Wm7A@public.gmane.org>
2008-12-09 12:07                       ` Paul Goyette
     [not found]                         ` <Pine.NEB.4.64.0812090405080.8244-j58mV80z1I3O+KjX/08HYEEOCMrvLtNR@public.gmane.org>
2008-12-09 14:02                           ` Preliminary support for DDR3 in decode-dimms Jean Delvare

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox