mirror of
https://github.com/CloverHackyColor/CloverBootloader.git
synced 2024-12-26 16:47:40 +01:00
948 lines
31 KiB
C++
948 lines
31 KiB
C++
/*
|
|
* spd.c - serial presence detect memory information
|
|
implementation for reading memory spd
|
|
|
|
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 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
*
|
|
* Originally restored from pcefi10.5 by netkas
|
|
* Dynamic mem detection original impl. by Rekursor
|
|
* System profiler fix and other fixes by Mozodojo.
|
|
* Slice 2011 remade for UEFI
|
|
* XMP detection - apianti
|
|
*/
|
|
|
|
#include <Platform.h> // Only use angled for Platform, else, xcode project won't compile
|
|
#include "../Platform/Settings.h"
|
|
#include "spd.h"
|
|
#include "memvendors.h"
|
|
#include "cpu.h"
|
|
#include "smbios.h"
|
|
|
|
#ifndef DEBUG_SPD
|
|
#ifndef DEBUG_ALL
|
|
#define DEBUG_SPD 0
|
|
#else
|
|
#define DEBUG_SPD DEBUG_ALL
|
|
#endif
|
|
#endif
|
|
|
|
#if DEBUG_SPD == 0
|
|
#define DBG(...)
|
|
#else
|
|
#define DBG(...) DebugLog(DEBUG_SPD, __VA_ARGS__)
|
|
#endif
|
|
|
|
|
|
BOOLEAN smbIntel;
|
|
UINT8 smbPage;
|
|
|
|
CONST CHAR8 *spd_memory_types[] =
|
|
{
|
|
"RAM", /* 00h Undefined */
|
|
"FPM", /* 01h FPM */
|
|
"EDO", /* 02h EDO */
|
|
"", /* 03h PIPELINE NIBBLE */
|
|
"SDRAM", /* 04h SDRAM */
|
|
"", /* 05h MULTIPLEXED ROM */
|
|
"DDR SGRAM", /* 06h SGRAM DDR */
|
|
"DDR SDRAM", /* 07h SDRAM DDR */
|
|
"DDR2 SDRAM", /* 08h SDRAM DDR 2 */
|
|
"", /* 09h Undefined */
|
|
"", /* 0Ah Undefined */
|
|
"DDR3 SDRAM", /* 0Bh SDRAM DDR 3 */
|
|
"DDR4 SDRAM" /* 0Ch SDRAM DDR 4 */
|
|
};
|
|
|
|
#define UNKNOWN_MEM_TYPE 2
|
|
UINT8 spd_mem_to_smbios[] =
|
|
{
|
|
UNKNOWN_MEM_TYPE, /* 00h Undefined */
|
|
UNKNOWN_MEM_TYPE, /* 01h FPM */
|
|
UNKNOWN_MEM_TYPE, /* 02h EDO */
|
|
UNKNOWN_MEM_TYPE, /* 03h PIPELINE NIBBLE */
|
|
SMB_MEM_TYPE_SDRAM, /* 04h SDRAM */
|
|
SMB_MEM_TYPE_ROM, /* 05h MULTIPLEXED ROM */
|
|
SMB_MEM_TYPE_SGRAM, /* 06h SGRAM DDR */
|
|
SMB_MEM_TYPE_DDR, /* 07h SDRAM DDR */
|
|
SMB_MEM_TYPE_DDR2, /* 08h SDRAM DDR 2 */
|
|
UNKNOWN_MEM_TYPE, /* 09h Undefined */
|
|
UNKNOWN_MEM_TYPE, /* 0Ah Undefined */
|
|
SMB_MEM_TYPE_DDR3, /* 0Bh SDRAM DDR 3 */
|
|
SMB_MEM_TYPE_DDR4 /* 0Ch SDRAM DDR 4 */
|
|
};
|
|
#define SPD_TO_SMBIOS_SIZE (sizeof(spd_mem_to_smbios)/sizeof(UINT8))
|
|
|
|
//define rdtsc(low,high) UINT64=AsmReadTsc()
|
|
//define outb(port, val) IoWrite8(port, val)
|
|
//define val=inb(port) val=IoRead(port)
|
|
|
|
// Intel SMB reg offsets
|
|
#define SMBHSTSTS 0
|
|
#define SMBHSTCNT 2
|
|
#define SMBHSTCMD 3
|
|
#define SMBHSTADD 4
|
|
#define SMBHSTDAT 5
|
|
#define SMBHSTDAT1 6
|
|
#define SBMBLKDAT 7
|
|
// MCP and nForce SMB reg offsets
|
|
#define SMBHPRTCL_NV 0 /* protocol, PEC */
|
|
#define SMBHSTSTS_NV 1 /* status */
|
|
#define SMBHSTADD_NV 2 /* address */
|
|
#define SMBHSTCMD_NV 3 /* command */
|
|
#define SMBHSTDAT_NV 4 /* 32 data registers */
|
|
//
|
|
|
|
// XMP memory profile
|
|
#define SPD_XMP_SIG1 176
|
|
#define SPD_XMP_SIG1_VALUE 0x0C
|
|
#define SPD_XMP_SIG2 177
|
|
#define SPD_XMP_SIG2_VALUE 0x4A
|
|
#define SPD_XMP_PROFILES 178
|
|
#define SPD_XMP_VERSION 179
|
|
#define SPD_XMP_PROF1_DIVISOR 180
|
|
#define SPD_XMP_PROF1_DIVIDEND 181
|
|
#define SPD_XMP_PROF2_DIVISOR 182
|
|
#define SPD_XMP_PROF2_DIVIDEND 183
|
|
#define SPD_XMP_PROF1_RATIO 186
|
|
#define SPD_XMP_PROF2_RATIO 221
|
|
|
|
|
|
#define SPD_XMP20_SIG1 0x180
|
|
#define SPD_XMP20_SIG2 0x181
|
|
#define SPD_XMP20_PROFILES 0x182
|
|
#define SPD_XMP20_VERSION 0x183
|
|
/* 0x189 */
|
|
#define SPD_XMP20_PROF1_MINCYCLE 0x18C
|
|
#define SPD_XMP20_PROF1_FINEADJUST 0x1AF
|
|
/* 0x1B8 */
|
|
#define SPD_XMP20_PROF2_MINCYCLE 0x1BB
|
|
#define SPD_XMP20_PROF2_FINEADJUST 0x1DE
|
|
/* 0x1E7 */
|
|
|
|
UINT16 spd_indexes_ddr[] = {
|
|
/* 3 */ SPD_NUM_ROWS, /* ModuleSize */
|
|
/* 4 */ SPD_NUM_COLUMNS,
|
|
/* 5 */ SPD_NUM_DIMM_BANKS,
|
|
/* 17 */ SPD_NUM_BANKS_PER_SDRAM,
|
|
9, /* Frequency */
|
|
64, /* Manufacturer */
|
|
95,96,97,98, /* UIS */
|
|
0
|
|
};
|
|
|
|
UINT16 spd_indexes_ddr3[] = {
|
|
4,7,8, /* ModuleSize */
|
|
10,11,12, /* Frequency */
|
|
/* 0x75, 0x76 */ SPD_DDR3_MEMORY_BANK, SPD_DDR3_MEMORY_CODE, /* Manufacturer */
|
|
122,123,124,125, /* UIS */
|
|
/* XMP */
|
|
SPD_XMP_SIG1,
|
|
SPD_XMP_SIG2,
|
|
SPD_XMP_PROFILES,
|
|
SPD_XMP_VERSION,
|
|
SPD_XMP_PROF1_DIVISOR,
|
|
SPD_XMP_PROF1_DIVIDEND,
|
|
SPD_XMP_PROF2_DIVISOR,
|
|
SPD_XMP_PROF2_DIVIDEND,
|
|
SPD_XMP_PROF1_RATIO,
|
|
SPD_XMP_PROF2_RATIO,
|
|
0
|
|
};
|
|
|
|
UINT16 spd_indexes_ddr4[] = {
|
|
4,6,12,13, /* ModuleSize */
|
|
18,125, /* Frequency */
|
|
SPD_DDR4_MANUFACTURER_ID_BANK, SPD_DDR4_MANUFACTURER_ID_CODE, /* Manufacturer */
|
|
325,326,327,328, /* UIS */
|
|
/* XMP 2.0 */
|
|
SPD_XMP20_SIG1,
|
|
SPD_XMP20_SIG2,
|
|
SPD_XMP20_PROFILES,
|
|
SPD_XMP20_VERSION,
|
|
SPD_XMP20_PROF1_MINCYCLE,
|
|
SPD_XMP20_PROF1_FINEADJUST,
|
|
SPD_XMP20_PROF2_MINCYCLE,
|
|
SPD_XMP20_PROF2_FINEADJUST,
|
|
0
|
|
};
|
|
|
|
/** Read one byte from i2c, used for reading SPD */
|
|
|
|
UINT8 smb_read_byte(UINT32 base, UINT8 adr, UINT16 cmd)
|
|
{
|
|
// INTN l1, h1, l2, h2;
|
|
UINT64 t, t1, t2;
|
|
UINT8 page;
|
|
UINT8 c;
|
|
// UINT8 p;
|
|
|
|
if (smbIntel) {
|
|
IoWrite8(base + SMBHSTSTS, 0x1f); // reset SMBus Controller (set busy)
|
|
IoWrite8(base + SMBHSTDAT, 0xff);
|
|
|
|
t1 = AsmReadTsc(); //rdtsc(l1, h1);
|
|
while ( IoRead8(base + SMBHSTSTS) & 0x01) { // wait until host is not busy
|
|
t2 = AsmReadTsc(); //rdtsc(l2, h2);
|
|
t = DivU64x64Remainder((t2 - t1),
|
|
DivU64x32(gCPUStructure.TSCFrequency, 1000),
|
|
0);
|
|
if (t > 5) {
|
|
DBG("host is busy for too long for byte %2hhX:%d!\n", adr, cmd);
|
|
return 0xFF; // break
|
|
}
|
|
}
|
|
|
|
page = (cmd >> 8) & 1;
|
|
if (page != smbPage) {
|
|
// p = 0xFF;
|
|
IoWrite8(base + SMBHSTCMD, 0x00);
|
|
IoWrite8(base + SMBHSTADD, 0x6C + (page << 1)); // Set SPD Page Address
|
|
#if 0
|
|
IoWrite8(base + SMBHSTCNT, 0x48); // Start + Byte Data Write
|
|
// Don't use "Byte Data Write" because status goes from 0x41 (Busy) -> 0x44 (Error)
|
|
#else
|
|
IoWrite8(base + SMBHSTCNT, 0x40); // Start + Quick Write
|
|
// status goes from 0x41 (Busy) -> 0x42 (Completed)
|
|
#endif
|
|
smbPage = page;
|
|
|
|
t1 = AsmReadTsc();
|
|
while (!( (c=IoRead8(base + SMBHSTSTS)) & 0x02)) { // wait until command finished
|
|
t2 = AsmReadTsc();
|
|
t = DivU64x64Remainder((t2 - t1), DivU64x32(gCPUStructure.TSCFrequency, 1000), 0);
|
|
/*
|
|
if (c != p) {
|
|
DBG("%02d %02hhX spd page change status %2hhX\n", t, cmd, c);
|
|
p = c;
|
|
}
|
|
*/
|
|
if (c & 4) {
|
|
DBG("spd page change error for byte %2hhX:%d!\n", adr, cmd);
|
|
break;
|
|
}
|
|
if (t > 5) {
|
|
DBG("spd page change taking too long for byte %2hhX:%d!\n", adr, cmd);
|
|
break; // break after 5ms
|
|
}
|
|
}
|
|
return smb_read_byte(base, adr, cmd);
|
|
}
|
|
|
|
// p = 0xFF;
|
|
IoWrite8(base + SMBHSTCMD, (UINT8)(cmd & 0xFF)); // SMBus uses 8 bit commands
|
|
IoWrite8(base + SMBHSTADD, (adr << 1) | 0x01 ); // read from spd
|
|
IoWrite8(base + SMBHSTCNT, 0x48 ); // Start + Byte Data Read
|
|
// status goes from 0x41 (Busy) -> 0x42 (Completed) or 0x44 (Error)
|
|
|
|
t1 = AsmReadTsc();
|
|
while (!( (c=IoRead8(base + SMBHSTSTS)) & 0x02)) { // wait until command finished
|
|
t2 = AsmReadTsc();
|
|
t = DivU64x64Remainder((t2 - t1), DivU64x32(gCPUStructure.TSCFrequency, 1000), 0);
|
|
/*
|
|
if (c != p) {
|
|
DBG("%02d %02hhX spd read status %2hhX\n", t, cmd, c);
|
|
p = c;
|
|
}
|
|
*/
|
|
if (c & 4) {
|
|
// This alway happens when trying to read the memory type (cmd 2) of an empty slot
|
|
// DBG("spd byte read error for byte %2hhX:%d!\n", adr, cmd);
|
|
break;
|
|
}
|
|
if (t > 5) {
|
|
// if (cmd != 2)
|
|
DBG("spd byte read taking too long for byte %2hhX:%d!\n", adr, cmd);
|
|
break; // break after 5ms
|
|
}
|
|
}
|
|
return IoRead8(base + SMBHSTDAT);
|
|
}
|
|
else {
|
|
IoWrite8(base + SMBHSTSTS_NV, 0x1f); // reset SMBus Controller
|
|
IoWrite8(base + SMBHSTDAT_NV, 0xff);
|
|
|
|
t1 = AsmReadTsc(); //rdtsc(l1, h1);
|
|
while ( IoRead8(base + SMBHSTSTS_NV) & 0x01) { // wait until read
|
|
t2 = AsmReadTsc(); //rdtsc(l2, h2);
|
|
t = DivU64x64Remainder((t2 - t1),
|
|
DivU64x32(gCPUStructure.TSCFrequency, 1000),
|
|
0);
|
|
if (t > 5)
|
|
return 0xFF; // break
|
|
}
|
|
|
|
IoWrite8(base + SMBHSTSTS_NV, 0x00); // clear status register
|
|
IoWrite16(base + SMBHSTCMD_NV, cmd);
|
|
IoWrite8(base + SMBHSTADD_NV, (adr << 1) | 0x01 );
|
|
IoWrite8(base + SMBHPRTCL_NV, 0x07 );
|
|
t1 = AsmReadTsc();
|
|
|
|
while (!( IoRead8(base + SMBHSTSTS_NV) & 0x9F)) { // wait till command finished
|
|
t2 = AsmReadTsc();
|
|
t = DivU64x64Remainder((t2 - t1),
|
|
DivU64x32(gCPUStructure.TSCFrequency, 1000),
|
|
0);
|
|
if (t > 5)
|
|
break; // break after 5ms
|
|
}
|
|
return IoRead8(base + SMBHSTDAT_NV);
|
|
}
|
|
}
|
|
|
|
/* SPD i2c read optimization: prefetch only what we need, read non prefetcheable bytes on the fly */
|
|
#define READ_SPD(spd, base, slot, x) spd[x] = smb_read_byte(base, 0x50 + slot, x)
|
|
|
|
|
|
/** Read from spd *used* values only*/
|
|
void init_spd(UINT16* spd_indexes, UINT8* spd, UINT32 base, UINT8 slot)
|
|
{
|
|
UINT16 i;
|
|
for (i=0; spd_indexes[i]; i++) {
|
|
READ_SPD(spd, base, slot, spd_indexes[i]);
|
|
}
|
|
|
|
#if 0
|
|
DBG("Reading entire spd data\n");
|
|
for (i = 0; i < 512; i++) {
|
|
UINT8 b = smb_read_byte(base, 0x50 + slot, i);
|
|
DBG("%02hhX", b);
|
|
}
|
|
DBG(".\n");
|
|
#endif
|
|
}
|
|
|
|
// Get Vendor Name from spd, 3 cases handled DDR3, DDR4 and DDR2,
|
|
// have different formats, always return a valid ptr.
|
|
CONST CHAR8* getVendorName(RAM_SLOT_INFO* slot, UINT8 *spd, UINT32 base, UINT8 slot_num)
|
|
{
|
|
UINT8 bank = 0;
|
|
UINT8 code = 0;
|
|
UINT8 parity;
|
|
UINT8 testbit;
|
|
//UINT8 * spd = (UINT8 *) slot->spd;
|
|
if (spd[SPD_MEMORY_TYPE]==SPD_MEMORY_TYPE_SDRAM_DDR4) { // DDR4
|
|
bank = spd[SPD_DDR4_MANUFACTURER_ID_BANK];
|
|
code = spd[SPD_DDR4_MANUFACTURER_ID_CODE];
|
|
parity = bank;
|
|
testbit = bank;
|
|
for (INTN i=6; i >= 0; i--) { parity ^= (testbit <<= 1); }
|
|
if ( (parity & 0x80) == 0 ) {
|
|
DBG("Bad parity bank=0x%2hhX code=0x%2hhX\n", bank, code);
|
|
}
|
|
bank &= 0x7f;
|
|
for (UINTN i=0; i < VEN_MAP_SIZE; i++) {
|
|
if (bank==vendorMap[i].bank && code==vendorMap[i].code) {
|
|
return vendorMap[i].name;
|
|
}
|
|
}
|
|
DBG("Unknown vendor bank=0x%2hhX code=0x%2hhX\n", bank, code);
|
|
} else if (spd[SPD_MEMORY_TYPE]==SPD_MEMORY_TYPE_SDRAM_DDR3) { // DDR3
|
|
bank = spd[SPD_DDR3_MEMORY_BANK]; // constructors like Patriot use b7=1
|
|
code = spd[SPD_DDR3_MEMORY_CODE];
|
|
parity = bank;
|
|
testbit = bank;
|
|
for (INTN i=6; i >= 0; i--) { parity ^= (testbit <<= 1); }
|
|
if ( (parity & 0x80) == 0 ) {
|
|
DBG("Bad parity bank=0x%2hhX code=0x%2hhX\n", bank, code);
|
|
}
|
|
bank &= 0x7f;
|
|
|
|
for (UINTN i=0; i < VEN_MAP_SIZE; i++) {
|
|
if (bank==vendorMap[i].bank && code==vendorMap[i].code) {
|
|
return vendorMap[i].name;
|
|
}
|
|
}
|
|
} else if (spd[SPD_MEMORY_TYPE]==SPD_MEMORY_TYPE_SDRAM_DDR2 ||
|
|
spd[SPD_MEMORY_TYPE]==SPD_MEMORY_TYPE_SDRAM_DDR) {
|
|
if(spd[64]==0x7f) {
|
|
UINTN i;
|
|
for (i=64; i<72 && spd[i]==0x7f;i++) {
|
|
bank++;
|
|
READ_SPD(spd, base, slot_num, (UINT8)(i+1)); // prefetch next spd byte to read for next loop
|
|
}
|
|
READ_SPD(spd, base, slot_num, (UINT8)i);
|
|
code = spd[i];
|
|
} else {
|
|
code = spd[64];
|
|
bank = 0;
|
|
}
|
|
for (UINTN i=0; i < VEN_MAP_SIZE; i++) {
|
|
if (bank==vendorMap[i].bank && code==vendorMap[i].code) {
|
|
return vendorMap[i].name;
|
|
}
|
|
}
|
|
}
|
|
/* OK there is no vendor id here lets try to match the partnum if it exists */
|
|
if ( slot->PartNo.startWithOrEqualTo("GU332") ) { // Unifosa fingerprint
|
|
return "Unifosa";
|
|
}
|
|
return "NoName";
|
|
}
|
|
|
|
/** Get Default Memory Module Speed (no overclocking handled) */
|
|
UINT16 getDDRspeedMhz(UINT8 * spd)
|
|
{
|
|
UINT16 frequency = 0; // default freq for unknown types //shit! DDR1 = 533
|
|
UINT16 xmpFrequency1 = 0, xmpFrequency2 = 0;
|
|
UINT8 xmpVersion = 0;
|
|
UINT8 xmpProfiles = 0;
|
|
|
|
if (spd[SPD_MEMORY_TYPE] == SPD_MEMORY_TYPE_SDRAM_DDR4) {
|
|
UINT16 mincycle = spd[18];
|
|
INT8 fineadjust = spd[125];
|
|
frequency = (UINT16)(2000000 / (mincycle * 125 + fineadjust));
|
|
|
|
// Check if module supports XMP
|
|
if ((spd[SPD_XMP20_SIG1] == SPD_XMP_SIG1_VALUE) &&
|
|
(spd[SPD_XMP20_SIG2] == SPD_XMP_SIG2_VALUE)) {
|
|
xmpVersion = spd[SPD_XMP20_VERSION];
|
|
xmpProfiles = spd[SPD_XMP20_PROFILES] & 3;
|
|
|
|
if ((xmpProfiles & 1) == 1) {
|
|
// Check the first profile
|
|
mincycle = spd[SPD_XMP20_PROF1_MINCYCLE];
|
|
fineadjust = spd[SPD_XMP20_PROF1_FINEADJUST];
|
|
xmpFrequency1 = (UINT16)(2000000 / (mincycle * 125 + fineadjust));
|
|
DBG("XMP Profile1: %d*125 %d ns\n", mincycle, fineadjust);
|
|
}
|
|
if ((xmpProfiles & 2) == 2) {
|
|
// Check the second profile
|
|
mincycle = spd[SPD_XMP20_PROF2_MINCYCLE];
|
|
fineadjust = spd[SPD_XMP20_PROF2_FINEADJUST];
|
|
xmpFrequency2 = (UINT16)(2000000 / (mincycle * 125 + fineadjust));
|
|
DBG("XMP Profile2: %d*125 %d ns\n", mincycle, fineadjust);
|
|
}
|
|
}
|
|
} else if (spd[SPD_MEMORY_TYPE] == SPD_MEMORY_TYPE_SDRAM_DDR3) {
|
|
// This should be multiples of MTB converted to MHz- apianti
|
|
UINT16 divisor = spd[10];
|
|
UINT16 dividend = spd[11];
|
|
UINT16 ratio = spd[12];
|
|
frequency = (((dividend != 0) && (divisor != 0) && (ratio != 0)) ?
|
|
((2000 * dividend) / (divisor * ratio)) : 0);
|
|
|
|
// Check if module supports XMP
|
|
if ((spd[SPD_XMP_SIG1] == SPD_XMP_SIG1_VALUE) &&
|
|
(spd[SPD_XMP_SIG2] == SPD_XMP_SIG2_VALUE)) {
|
|
xmpVersion = spd[SPD_XMP_VERSION];
|
|
xmpProfiles = spd[SPD_XMP_PROFILES] & 3;
|
|
|
|
if ((xmpProfiles & 1) == 1) {
|
|
// Check the first profile
|
|
divisor = spd[SPD_XMP_PROF1_DIVISOR];
|
|
dividend = spd[SPD_XMP_PROF1_DIVIDEND];
|
|
ratio = spd[SPD_XMP_PROF1_RATIO];
|
|
xmpFrequency1 = (((dividend != 0) && (divisor != 0) && (ratio != 0)) ?
|
|
((2000 * dividend) / (divisor * ratio)) : 0);
|
|
DBG("XMP Profile1: %d*%d/%dns\n", ratio, divisor, dividend);
|
|
}
|
|
if ((xmpProfiles & 2) == 2) {
|
|
// Check the second profile
|
|
divisor = spd[SPD_XMP_PROF2_DIVISOR];
|
|
dividend = spd[SPD_XMP_PROF2_DIVIDEND];
|
|
ratio = spd[SPD_XMP_PROF2_RATIO];
|
|
xmpFrequency2 = (((dividend != 0) && (divisor != 0) && (ratio != 0)) ?
|
|
((2000 * dividend) / (divisor * ratio)) : 0);
|
|
DBG("XMP Profile2: %d*%d/%dns\n", ratio, divisor, dividend);
|
|
}
|
|
}
|
|
} else if ((spd[SPD_MEMORY_TYPE] == SPD_MEMORY_TYPE_SDRAM_DDR2) ||
|
|
(spd[SPD_MEMORY_TYPE] == SPD_MEMORY_TYPE_SDRAM_DDR)) {
|
|
switch(spd[9]) {
|
|
case 0x50:
|
|
return 400;
|
|
case 0x3d:
|
|
return 533;
|
|
case 0x30:
|
|
return 667;
|
|
case 0x25:
|
|
default:
|
|
return 800;
|
|
case 0x1E:
|
|
return 1066;
|
|
}
|
|
}
|
|
|
|
if (xmpProfiles) {
|
|
MsgLog("Found module with XMP version %d.%d\n", (xmpVersion >> 4) & 0xF, xmpVersion & 0xF);
|
|
|
|
switch (gSettings.Boot.XMPDetection) {
|
|
case -1:
|
|
MsgLog("XMPDetection deactivated in config.plist\n");
|
|
break;
|
|
case 0:
|
|
// Detect the better XMP profile
|
|
if (xmpFrequency1 >= xmpFrequency2) {
|
|
if (xmpFrequency1 >= frequency) {
|
|
MsgLog("Using XMP Profile1 instead of standard frequency %dMHz\n", frequency);
|
|
frequency = xmpFrequency1;
|
|
}
|
|
} else if (xmpFrequency2 >= frequency) {
|
|
MsgLog("Using XMP Profile2 instead of standard frequency %dMHz\n", frequency);
|
|
frequency = xmpFrequency2;
|
|
}
|
|
break;
|
|
|
|
case 1:
|
|
// Use first profile if present
|
|
if ((xmpProfiles & 1) == 1) {
|
|
frequency = xmpFrequency1;
|
|
MsgLog("Using XMP Profile1 instead of standard frequency %dMHz\n", frequency);
|
|
} else {
|
|
MsgLog("Not using XMP Profile1 because it is not present\n");
|
|
}
|
|
break;
|
|
|
|
case 2:
|
|
// Use second profile
|
|
if ((xmpProfiles & 2) == 2) {
|
|
frequency = xmpFrequency2;
|
|
MsgLog("Using XMP Profile2 instead of standard frequency %dMHz\n", frequency);
|
|
} else {
|
|
MsgLog("Not using XMP Profile2 because it is not present\n");
|
|
}
|
|
break;
|
|
|
|
default:
|
|
MsgLog("XMPDetection invalid value '%d' in config.plist\n", gSettings.Boot.XMPDetection);
|
|
break;
|
|
}
|
|
} else {
|
|
// Print out XMP not detected
|
|
switch (gSettings.Boot.XMPDetection) {
|
|
case -1:
|
|
MsgLog("XMP is not present, XMPDetection deactivated in config.plist\n");
|
|
break;
|
|
|
|
case 0:
|
|
MsgLog("Not using XMP because it is not present\n");
|
|
break;
|
|
|
|
case 1:
|
|
case 2:
|
|
MsgLog("Not using XMP Profile%d because it is not present\n", gSettings.Boot.XMPDetection);
|
|
break;
|
|
|
|
default:
|
|
MsgLog("XMP is not present, XMPDetection has invalid value '%d' config.plist, \n", gSettings.Boot.XMPDetection);
|
|
break;
|
|
}
|
|
}
|
|
return frequency;
|
|
}
|
|
|
|
#define SMST(a) ((UINT8)((spd[a] & 0xf0) >> 4))
|
|
#define SLST(a) ((UINT8)(spd[a] & 0x0f))
|
|
|
|
/** Get DDR3 or DDR2 serial number, 0 most of the times, always return a valid ptr */
|
|
CHAR8* getDDRSerial(UINT8* spd)
|
|
{
|
|
CHAR8* asciiSerial; //[16];
|
|
asciiSerial = (__typeof__(asciiSerial))AllocatePool(17);
|
|
if (spd[SPD_MEMORY_TYPE]==SPD_MEMORY_TYPE_SDRAM_DDR4) { // DDR4
|
|
snprintf(asciiSerial, 17, "%01hhX%01hhX%01hhX%01hhX%01hhX%01hhX%01hhX%01hhX", SMST(325) /*& 0x7*/, SLST(325), SMST(326), SLST(326), SMST(327), SLST(327), SMST(328), SLST(328));
|
|
} else if (spd[SPD_MEMORY_TYPE]==SPD_MEMORY_TYPE_SDRAM_DDR3) { // DDR3
|
|
snprintf(asciiSerial, 17, "%01hhX%01hhX%01hhX%01hhX%01hhX%01hhX%01hhX%01hhX", SMST(122) /*& 0x7*/, SLST(122), SMST(123), SLST(123), SMST(124), SLST(124), SMST(125), SLST(125));
|
|
} else if (spd[SPD_MEMORY_TYPE]==SPD_MEMORY_TYPE_SDRAM_DDR2 ||
|
|
spd[SPD_MEMORY_TYPE]==SPD_MEMORY_TYPE_SDRAM_DDR) { // DDR2 or DDR
|
|
snprintf(asciiSerial, 17, "%01hhX%01hhX%01hhX%01hhX%01hhX%01hhX%01hhX%01hhX", SMST(95) /*& 0x7*/, SLST(95), SMST(96), SLST(96), SMST(97), SLST(97), SMST(98), SLST(98));
|
|
} else {
|
|
AsciiStrCpyS(asciiSerial, 17, "0000000000000000");
|
|
}
|
|
|
|
return asciiSerial;
|
|
}
|
|
|
|
/** Get DDR2 or DDR3 or DDR4 Part Number, always return a valid ptr */
|
|
CHAR8* getDDRPartNum(UINT8* spd, UINT32 base, UINT8 slot)
|
|
{
|
|
UINT16 i, start=0, index = 0;
|
|
CHAR8 c;
|
|
CHAR8* asciiPartNo = (__typeof__(asciiPartNo))AllocatePool(32); //[32];
|
|
|
|
if (spd[SPD_MEMORY_TYPE] == SPD_MEMORY_TYPE_SDRAM_DDR4) {
|
|
start = 329;
|
|
} else if (spd[SPD_MEMORY_TYPE] == SPD_MEMORY_TYPE_SDRAM_DDR3) {
|
|
start = 128;
|
|
} else if (spd[SPD_MEMORY_TYPE] == SPD_MEMORY_TYPE_SDRAM_DDR2 ||
|
|
spd[SPD_MEMORY_TYPE] == SPD_MEMORY_TYPE_SDRAM_DDR) {
|
|
start = 73;
|
|
}
|
|
|
|
// Check that the spd part name is zero terminated and that it is ascii:
|
|
ZeroMem(asciiPartNo, 32); //sizeof(asciiPartNo));
|
|
for (i = start; i < start + 20; i++) {
|
|
READ_SPD(spd, base, slot, (UINT16)i); // only read once the corresponding model part (ddr3 or ddr2)
|
|
c = spd[i];
|
|
if (IS_ALFA(c) || IS_DIGIT(c) || IS_PUNCT(c)) // It seems that System Profiler likes only letters and digits...
|
|
asciiPartNo[index++] = c;
|
|
else if (c < 0x20)
|
|
break;
|
|
}
|
|
|
|
return asciiPartNo;
|
|
}
|
|
|
|
//INTN mapping []= {0,2,1,3,4,6,5,7,8,10,9,11};
|
|
#define PCI_COMMAND_OFFSET 0x04
|
|
|
|
/** Read from smbus the SPD content and interpret it for detecting memory attributes */
|
|
STATIC void read_smb(EFI_PCI_IO_PROTOCOL *PciIo, UINT16 vid, UINT16 did)
|
|
{
|
|
// EFI_STATUS Status;
|
|
UINT16 speed;
|
|
UINT8 i;// spd_size, spd_type;
|
|
UINT32 base, mmio, hostc;
|
|
UINT16 Command;
|
|
//RAM_SLOT_INFO* slot;
|
|
//BOOLEAN fullBanks;
|
|
UINT8* spdbuf;
|
|
// UINT16 vid, did;
|
|
|
|
UINT8 TotalSlotsCount;
|
|
|
|
smbPage = 0; // valid pages are 0 and 1; assume the first page (page 0) is already selected
|
|
// vid = gPci->Hdr.VendorId;
|
|
// did = gPci->Hdr.DeviceId;
|
|
|
|
/*Status = */PciIo->Pci.Read (
|
|
PciIo,
|
|
EfiPciIoWidthUint16,
|
|
PCI_COMMAND_OFFSET,
|
|
1,
|
|
&Command
|
|
);
|
|
|
|
Command |= 1;
|
|
/*Status = */PciIo->Pci.Write (
|
|
PciIo,
|
|
EfiPciIoWidthUint16,
|
|
PCI_COMMAND_OFFSET,
|
|
1,
|
|
&Command
|
|
);
|
|
|
|
DBG("SMBus CmdReg: 0x%hX\n", Command);
|
|
|
|
/*Status = */PciIo->Pci.Read (
|
|
PciIo,
|
|
EfiPciIoWidthUint32,
|
|
0x10,
|
|
1,
|
|
&mmio
|
|
);
|
|
if (vid == 0x8086) {
|
|
/*Status = */PciIo->Pci.Read (
|
|
PciIo,
|
|
EfiPciIoWidthUint32,
|
|
0x20,
|
|
1,
|
|
&base
|
|
);
|
|
|
|
base &= 0xFFFE;
|
|
smbIntel = TRUE;
|
|
}
|
|
else {
|
|
/*Status = */PciIo->Pci.Read (
|
|
PciIo,
|
|
EfiPciIoWidthUint32,
|
|
0x24, // iobase offset 0x24 on MCP
|
|
1,
|
|
&base
|
|
);
|
|
base &= 0xFFFC;
|
|
smbIntel = FALSE;
|
|
}
|
|
/*Status = */PciIo->Pci.Read (
|
|
PciIo,
|
|
EfiPciIoWidthUint32,
|
|
0x40,
|
|
1,
|
|
&hostc
|
|
);
|
|
|
|
|
|
MsgLog("Scanning SMBus [%04hX:%04hX], mmio: 0x%X, ioport: 0x%X, hostc: 0x%X\n",
|
|
vid, did, mmio, base, hostc);
|
|
|
|
// needed at least for laptops
|
|
//fullBanks = (gDMI->MemoryModules == gDMI->CntMemorySlots);
|
|
|
|
spdbuf = (__typeof__(spdbuf))AllocateZeroPool(MAX_SPD_SIZE);
|
|
|
|
TotalSlotsCount = 8; //MAX_RAM_SLOTS; -- spd can read only 8 slots
|
|
DBG("Slots to scan [%d]...\n", TotalSlotsCount);
|
|
for (i = 0; i < TotalSlotsCount; i++){
|
|
//<==
|
|
ZeroMem(spdbuf, MAX_SPD_SIZE);
|
|
READ_SPD(spdbuf, base, i, SPD_MEMORY_TYPE);
|
|
if (spdbuf[SPD_MEMORY_TYPE] == 0xFF) {
|
|
//DBG("SPD[%d]: Empty\n", i);
|
|
continue;
|
|
}
|
|
else if (spdbuf[SPD_MEMORY_TYPE] == 0) {
|
|
// First 0x40 bytes of DDR4 spd second page is 0. Maybe we need to change page, so do that and retry.
|
|
DBG("SPD[%d]: Got invalid type %d @0x%X. Will set page and retry.\n", i, spdbuf[SPD_MEMORY_TYPE], 0x50 + i);
|
|
smbPage = 0xFF; // force page to be set
|
|
READ_SPD(spdbuf, base, i, SPD_MEMORY_TYPE);
|
|
}
|
|
|
|
// Copy spd data into buffer
|
|
DBG("SPD[%d]: Type %d @0x%X\n", i, spdbuf[SPD_MEMORY_TYPE], 0x50 + i);
|
|
switch (spdbuf[SPD_MEMORY_TYPE]) {
|
|
case SPD_MEMORY_TYPE_SDRAM_DDR:
|
|
init_spd(spd_indexes_ddr, spdbuf, base, i);
|
|
|
|
gRAM.SPD[i].Type = MemoryTypeDdr;
|
|
gRAM.SPD[i].ModuleSize = (((1 << ((spdbuf[SPD_NUM_ROWS] & 0x0f)
|
|
+ (spdbuf[SPD_NUM_COLUMNS] & 0x0f) - 17)) *
|
|
((spdbuf[SPD_NUM_DIMM_BANKS] & 0x7) + 1) *
|
|
spdbuf[SPD_NUM_BANKS_PER_SDRAM])/3)*2;
|
|
break;
|
|
|
|
case SPD_MEMORY_TYPE_SDRAM_DDR2:
|
|
init_spd(spd_indexes_ddr, spdbuf, base, i);
|
|
|
|
gRAM.SPD[i].Type = MemoryTypeDdr2;
|
|
gRAM.SPD[i].ModuleSize = ((1 << ((spdbuf[SPD_NUM_ROWS] & 0x0f)
|
|
+ (spdbuf[SPD_NUM_COLUMNS] & 0x0f) - 17)) *
|
|
((spdbuf[SPD_NUM_DIMM_BANKS] & 0x7) + 1) *
|
|
spdbuf[SPD_NUM_BANKS_PER_SDRAM]);
|
|
break;
|
|
|
|
case SPD_MEMORY_TYPE_SDRAM_DDR3:
|
|
init_spd(spd_indexes_ddr3, spdbuf, base, i);
|
|
|
|
gRAM.SPD[i].Type = MemoryTypeDdr3;
|
|
gRAM.SPD[i].ModuleSize = ((spdbuf[4] & 0x0f) + 28 ) + ((spdbuf[8] & 0x7) + 3 );
|
|
gRAM.SPD[i].ModuleSize -= (spdbuf[7] & 0x7) + 25;
|
|
gRAM.SPD[i].ModuleSize = ((1 << gRAM.SPD[i].ModuleSize) * (((spdbuf[7] >> 3) & 0x1f) + 1));
|
|
|
|
break;
|
|
|
|
case SPD_MEMORY_TYPE_SDRAM_DDR4:
|
|
init_spd(spd_indexes_ddr4, spdbuf, base, i);
|
|
|
|
gRAM.SPD[i].Type = MemoryTypeDdr4;
|
|
|
|
gRAM.SPD[i].ModuleSize
|
|
= (1 << ((spdbuf[4] & 0x0f) + 8 /* Mb */ - 3 /* MB */)) // SDRAM Capacity
|
|
* (1 << ((spdbuf[13] & 0x07) + 3)) // Primary Bus Width
|
|
/ (1 << ((spdbuf[12] & 0x07) + 2)) // SDRAM Width
|
|
* (((spdbuf[12] >> 3) & 0x07) + 1) // Logical Ranks per DIMM
|
|
* (((spdbuf[6] & 0x03) == 2) ? (((spdbuf[6] >> 4) & 0x07) + 1) : 1);
|
|
|
|
/*
|
|
Total = SDRAM Capacity / 8 * Primary Bus Width / SDRAM Width * Logical Ranks per DIMM
|
|
where:
|
|
: SDRAM Capacity = SPD byte 4 bits 3~0
|
|
: Primary Bus Width = SPD byte 13 bits 2~0
|
|
: SDRAM Width = SPD byte 12 bits 2~0
|
|
: Logical Ranks per DIMM =
|
|
for SDP, DDP, QDP: = SPD byte 12 bits 5~3
|
|
for 3DS: = SPD byte 12 bits 5~3
|
|
times SPD byte 6 bits 6~4 (Die Count)
|
|
|
|
SDRAM Capacity
|
|
|
|
0 0000 = 256 Mb
|
|
1 0001 = 512 Mb
|
|
2 0010 = 1 Gb
|
|
3 0011 = 2 Gb
|
|
4 0100 = 4 Gb
|
|
5 0101 = 8 Gb
|
|
6 0110 = 16 Gb
|
|
7 0111 = 32 Gb
|
|
|
|
|
|
Primary Bus Width
|
|
|
|
000 = 8 bits
|
|
001 = 16 bits
|
|
010 = 32 bits
|
|
011 = 64 bits
|
|
|
|
|
|
SDRAM Device Width
|
|
|
|
000 = 4 bits
|
|
001 = 8 bits
|
|
010 = 16 bits
|
|
011 = 32 bits
|
|
|
|
|
|
Logical Ranks per DIMM for SDP, DDP, QDP
|
|
|
|
000 = 1 Package Rank
|
|
001 = 2 Package Ranks
|
|
010 = 3 Package Ranks
|
|
011 = 4 Package Ranks
|
|
|
|
|
|
Die Count for 3DS
|
|
|
|
000 = Single die 001 = 2 die
|
|
010 = 3 die
|
|
011 = 4 die
|
|
100 = 5 die
|
|
101 = 6 die
|
|
110 = 7 die
|
|
111 = 8 die
|
|
*/
|
|
break;
|
|
|
|
default:
|
|
gRAM.SPD[i].ModuleSize = 0;
|
|
break;
|
|
}
|
|
|
|
if (gRAM.SPD[i].ModuleSize == 0) continue;
|
|
//spd_type = (slot->spd[SPD_MEMORY_TYPE] < ((UINT8) 12) ? slot->spd[SPD_MEMORY_TYPE] : 0);
|
|
//gRAM Type = spd_mem_to_smbios[spd_type];
|
|
gRAM.SPD[i].PartNo.takeValueFrom(getDDRPartNum(spdbuf, base, i));
|
|
gRAM.SPD[i].PartNo.trim();
|
|
gRAM.SPD[i].Vendor.takeValueFrom(getVendorName(&(gRAM.SPD[i]), spdbuf, base, i));
|
|
gRAM.SPD[i].Vendor.trim();
|
|
gRAM.SPD[i].SerialNo.takeValueFrom(getDDRSerial(spdbuf));
|
|
gRAM.SPD[i].SerialNo.trim();
|
|
//XXX - when we can FreePool allocated for these buffers? No this is pointer copy
|
|
// determine spd speed
|
|
speed = getDDRspeedMhz(spdbuf);
|
|
DBG("DDR speed %dMHz\n", speed);
|
|
if (gRAM.SPD[i].Frequency<speed) gRAM.SPD[i].Frequency = speed;
|
|
|
|
#if 0
|
|
// pci memory controller if available, is more reliable
|
|
if (gRAM.Frequency > 0) {
|
|
UINT32 freq = (UINT32)DivU64x32(gRAM.Frequency, 500000);
|
|
// now round off special cases
|
|
UINT32 fmod100 = freq %100;
|
|
switch(fmod100) {
|
|
case 1: freq--; break;
|
|
case 32: freq++; break;
|
|
case 65: freq++; break;
|
|
case 98: freq+=2;break;
|
|
case 99: freq++; break;
|
|
}
|
|
gRAM.SPD[i].Frequency = freq;
|
|
DBG("RAM speed %dMHz\n", freq);
|
|
}
|
|
#endif
|
|
|
|
MsgLog("Slot: %d Type %d %dMB %dMHz Vendor=%s PartNo=%s SerialNo=%s\n",
|
|
i,
|
|
(int)gRAM.SPD[i].Type,
|
|
gRAM.SPD[i].ModuleSize,
|
|
gRAM.SPD[i].Frequency,
|
|
gRAM.SPD[i].Vendor.c_str(),
|
|
gRAM.SPD[i].PartNo.c_str(),
|
|
gRAM.SPD[i].SerialNo.c_str());
|
|
|
|
gRAM.SPD[i].InUse = TRUE;
|
|
++(gRAM.SPDInUse);
|
|
//}
|
|
|
|
// laptops sometimes show slot 0 and 2 with slot 1 empty when only 2 slots are presents so:
|
|
//gDMI->DIMM[i]= (UINT32)((i>0 && gRAM->DIMM[1].InUse==FALSE && !fullBanks && TotalCount == 2)?mapping[i] : i); // for laptops case, mapping setup would need to be more generic than this
|
|
|
|
//slot->spd = NULL;
|
|
|
|
} // for
|
|
if (smbPage != 0) {
|
|
READ_SPD(spdbuf, base, 0, 0); // force first page when we're done
|
|
}
|
|
}
|
|
|
|
void ScanSPD()
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_HANDLE *HandleBuffer = NULL;
|
|
// EFI_GUID **ProtocolGuidArray;
|
|
EFI_PCI_IO_PROTOCOL *PciIo = NULL;
|
|
UINTN HandleCount;
|
|
// UINTN ArrayCount;
|
|
UINTN Index;
|
|
// UINTN ProtocolIndex;
|
|
PCI_TYPE00 gPci;
|
|
|
|
DbgHeader("ScanSPD");
|
|
|
|
// Scan PCI handles
|
|
Status = gBS->LocateHandleBuffer (
|
|
ByProtocol,
|
|
&gEfiPciIoProtocolGuid,
|
|
NULL,
|
|
&HandleCount,
|
|
&HandleBuffer
|
|
);
|
|
|
|
if (!EFI_ERROR(Status)) {
|
|
for (Index = 0; Index < HandleCount; ++Index) {
|
|
Status = gBS->HandleProtocol(HandleBuffer[Index], &gEfiPciIoProtocolGuid, (void **)&PciIo);
|
|
if (!EFI_ERROR(Status)) {
|
|
// Read PCI BUS
|
|
//PciIo->GetLocation (PciIo, &Segment, &Bus, &Device, &Function);
|
|
Status = PciIo->Pci.Read (
|
|
PciIo,
|
|
EfiPciIoWidthUint32,
|
|
0,
|
|
sizeof (gPci) / sizeof (UINT32),
|
|
&gPci
|
|
);
|
|
//SmBus controller has class = 0x0c0500
|
|
if ((gPci.Hdr.ClassCode[2] == 0x0c) && (gPci.Hdr.ClassCode[1] == 5)
|
|
&& (gPci.Hdr.ClassCode[0] == 0) && (gPci.Hdr.VendorId == 0x8086 || gPci.Hdr.VendorId == 0x10DE)) {
|
|
DBG("SMBus device : %04hX %04hX class=%02hhX%02hhX%02hhX status=%s\n",
|
|
gPci.Hdr.VendorId,
|
|
gPci.Hdr.DeviceId,
|
|
gPci.Hdr.ClassCode[2],
|
|
gPci.Hdr.ClassCode[1],
|
|
gPci.Hdr.ClassCode[0],
|
|
efiStrError(Status)
|
|
);
|
|
read_smb(PciIo, gPci.Hdr.VendorId, gPci.Hdr.DeviceId);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// Scan PCI BUS For SmBus controller
|
|
/* Status = gBS->LocateHandleBuffer(AllHandles,NULL,NULL,&HandleCount,&HandleBuffer);
|
|
if (!EFI_ERROR(Status)) {
|
|
for (HandleIndex = 0; HandleIndex < HandleCount; HandleIndex++) {
|
|
Status = gBS->ProtocolsPerHandle(HandleBuffer[HandleIndex],&ProtocolGuidArray,&ArrayCount);
|
|
if (!EFI_ERROR(Status)) {
|
|
for (ProtocolIndex = 0; ProtocolIndex < ArrayCount; ProtocolIndex++) {
|
|
if (CompareGuid(&gEfiPciIoProtocolGuid, ProtocolGuidArray[ProtocolIndex])) {
|
|
Status = gBS->OpenProtocol(HandleBuffer[HandleIndex],&gEfiPciIoProtocolGuid,(void **)&PciIo,gImageHandle,NULL,EFI_OPEN_PROTOCOL_GET_PROTOCOL);
|
|
|
|
if (!EFI_ERROR(Status)) {
|
|
// Read PCI BUS
|
|
Status = PciIo->Pci.Read (
|
|
PciIo,
|
|
EfiPciIoWidthUint32,
|
|
0,
|
|
sizeof (gPci) / sizeof (UINT32),
|
|
&gPci
|
|
);
|
|
|
|
//SmBus controller has class = 0x0c0500
|
|
if ((gPci.Hdr.ClassCode[2] == 0x0c) && (gPci.Hdr.ClassCode[1] == 5)
|
|
&& (gPci.Hdr.ClassCode[0] == 0) && (gPci.Hdr.VendorId == 0x8086 || gPci.Hdr.VendorId == 0x10DE)) {
|
|
read_smb(PciIo);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
*/
|
|
}
|