From 1ca80a309e10ae8002267dfe40a3f01432d643b0 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Wed, 16 Feb 2011 15:08:25 +0000 Subject: [PATCH] Fix detection of SPD EEPROM on DDR3 memory modules. DDR3 uses CRC16 over 128 bytes instead of basic checksum over 64 bytes. Contributed by Clemens Ladisch. git-svn-id: http://lm-sensors.org/svn/lm-sensors/trunk@5930 7894878c-1315-0410-8ee3-d5d059ff63e0 --- CHANGES | 1 + prog/detect/sensors-detect | 39 +++++++++++++++++++++++++++++++------- 2 files changed, 33 insertions(+), 7 deletions(-) diff --git a/CHANGES b/CHANGES index cf23bba7..1f43061f 100644 --- a/CHANGES +++ b/CHANGES @@ -26,6 +26,7 @@ SVN HEAD Add detection of SMSC EMC1002, EMC1033, EMC1046, EMC1047, EMC1072, EMC1073, EMC1074, EMC1402, and EMC1424 Fixed address ranges checked for SMSC EMC1403 and EMC1404. + Fix detection of SPD EEPROM on DDR3 memory modules 3.2.0 (2010-10-10) libsensors: Increase MAX_SENSORS_PER_TYPE to 24 diff --git a/prog/detect/sensors-detect b/prog/detect/sensors-detect index 1d63d841..dbd7fbc1 100755 --- a/prog/detect/sensors-detect +++ b/prog/detect/sensors-detect @@ -5264,19 +5264,44 @@ sub it8712_i2c_detect } # Registers used: -# 0-63: SPD Data and Checksum +# 0-63: SPD Data and Checksum (up to DDR2) +# 0-127: SPD data and CRC (DDR3) sub eeprom_detect { my ($file, $addr) = @_; + my $device_type = i2c_smbus_read_byte_data($file, 2); my $checksum = 0; - # Check the checksum for validity (works for most DIMMs and RIMMs) - for (my $i = 0; $i <= 62; $i++) { - $checksum += i2c_smbus_read_byte_data($file, $i); - } - $checksum &= 255; + # Check the checksum or CRC16 for validity + if ($device_type >= 1 and $device_type <= 8) { + for (my $i = 0; $i < 63; $i++) { + $checksum += i2c_smbus_read_byte_data($file, $i); + } + $checksum &= 0xff; + + return 8 if $checksum == i2c_smbus_read_byte_data($file, 63); + } elsif ($device_type => 9 && $device_type <= 11) { + # see JEDEC 21-C 4.1.2.11 2.4 + my $crc_coverage = i2c_smbus_read_byte_data($file, 0); + $crc_coverage = ($crc_coverage & 0x80) ? 117 : 126; + for (my $i = 0; $i < $crc_coverage; $i++) { + $checksum ^= i2c_smbus_read_byte_data($file, $i) << 8; + for (my $bit = 0; $bit < 8; $bit++) { + if ($checksum & 0x8000) { + $checksum = ($checksum << 1) ^ 0x1021; + } else { + $checksum <<= 1; + } + } + } + $checksum &= 0xffff; + + return 8 if ($checksum & 0xff) == i2c_smbus_read_byte_data($file, 126) and + ($checksum >> 8) == i2c_smbus_read_byte_data($file, 127); + + # note: if bit 7 of byte 32 is set, a jc42 sensor is at $addr-0x38 + } - return 8 if $checksum == i2c_smbus_read_byte_data($file, 63); return; }