blob: 1550bb8b6acd379aefec00284c7fb96fec79704f [file] [log] [blame]
/*
* MemTest86+ V5 Specific code (GPL V2.0)
* By Samuel DEMEULEMEESTER, sdemeule@memtest.org
* http://www.canardpc.com - http://www.memtest.org
*/
#include "stdint.h"
#include "test.h"
#include "io.h"
#include "pci.h"
#include "msr.h"
#include "spd.h"
#include "screen_buffer.h"
#include "jedec_id.h"
#define NULL 0
#define SMBHSTSTS smbusbase
#define SMBHSTCNT (smbusbase + 2)
#define SMBHSTCMD (smbusbase + 3)
#define SMBHSTADD (smbusbase + 4)
#define SMBHSTDAT (smbusbase + 5)
#define AMD_INDEX_IO_PORT 0xCD6
#define AMD_DATA_IO_PORT 0xCD7
#define AMD_SMBUS_BASE_REG 0x2C
#define AMD_PM_DECODE_EN_REG 0x00
static int get_ddr2_module_size(int rank_density_byte, int rank_num_byte);
static char *convert_hex_to_char(unsigned hex_org);
static void sb800_get_smb(void);
static int get_ddr3_module_size(int sdram_capacity, int prim_bus_width,
int sdram_width, int ranks);
static int smbdev, smbfun;
static unsigned short smbusbase;
static unsigned char spd_raw[256];
static void ich5_get_smb(void)
{
unsigned long x;
int result;
result = pci_conf_read(0, smbdev, smbfun, 0x20, 2, &x);
if (result == 0)
smbusbase = (unsigned short)x & 0xFFFE;
}
static void piix4_get_smb(void)
{
unsigned long x;
int result;
result = pci_conf_read(0, smbdev, smbfun, 0x08, 1, &x);
if (x < 0x40) {
// SB600/700
result = pci_conf_read(0, smbdev, smbfun, 0x90, 2, &x);
if (result == 0)
smbusbase = (unsigned short)x & 0xFFFE;
} else {
// SB800
sb800_get_smb();
}
}
static void sb800_get_smb(void)
{
int lbyte, hbyte, result;
unsigned long x;
result = pci_conf_read(0, smbdev, smbfun, 0x08, 1, &x);
/* if processor revision is ML_A0 or ML_A1 use different way for SMBus
* IO base calculation */
if (x == 0x42 || x == 0x41) {
/* read PMx00+1 to get SmbusAsfIoBase */
__outb(AMD_PM_DECODE_EN_REG + 1, AMD_INDEX_IO_PORT);
lbyte = __inb(AMD_DATA_IO_PORT);
/* SMBus IO base is defined as {Smbus0AsfIoBase[7:0], 0x00} */
smbusbase = (lbyte & 0xF) << 8;
} else {
__outb(AMD_SMBUS_BASE_REG + 1, AMD_INDEX_IO_PORT);
lbyte = __inb(AMD_DATA_IO_PORT);
__outb(AMD_SMBUS_BASE_REG, AMD_INDEX_IO_PORT);
hbyte = __inb(AMD_DATA_IO_PORT);
smbusbase = lbyte;
smbusbase <<= 8;
smbusbase += hbyte;
smbusbase &= 0xFFE0;
}
if (smbusbase == 0xFFE0) {
smbusbase = 0;
}
}
static unsigned char ich5_smb_read_byte(unsigned char adr, unsigned char cmd)
{
int l1, h1, l2, h2;
uint64_t t;
uint64_t toval = 10 * v->clks_msec;
__outb(0x1f, SMBHSTSTS); // reset SMBus Controller
__outb(0xff, SMBHSTDAT);
while (__inb(SMBHSTSTS) & 0x01)
; // wait until ready
__outb(cmd, SMBHSTCMD);
__outb((adr << 1) | 0x01, SMBHSTADD);
__outb(0x48, SMBHSTCNT);
rdtsc(l1, h1);
//cprint(POP2_Y, POP2_X + 16, s + cmd % 8); // progress bar
while (!(__inb(SMBHSTSTS) & 0x02)) { // wait til command finished
rdtsc(l2, h2);
t = ((uint64_t) (h2 - h1) * 0xffffffff + (l2 - l1));
if (t > toval)
break; // break after 10ms
}
return __inb(SMBHSTDAT);
}
static int ich5_read_spd(int dimmadr)
{
int x;
spd_raw[0] = ich5_smb_read_byte(0x50 + dimmadr, 0);
if (spd_raw[0] == 0xff)
return -1; // no spd here
for (x = 1; x < 256; x++) {
spd_raw[x] =
ich5_smb_read_byte(0x50 + dimmadr, (unsigned char)x);
}
return 0;
}
static void us15w_get_smb(void)
{
unsigned long x;
int result;
result = pci_conf_read(0, 0x1f, 0, 0x40, 2, &x);
if (result == 0)
smbusbase = (unsigned short)x & 0xFFC0;
}
static unsigned char us15w_smb_read_byte(unsigned char adr, unsigned char cmd)
{
int l1, h1, l2, h2;
uint64_t t;
uint64_t toval = 10 * v->clks_msec;
//__outb(0x00, smbusbase + 1); // reset SMBus Controller
//__outb(0x00, smbusbase + 6);
//while((__inb(smbusbase + 1) & 0x08) != 0)
// ; // wait until ready
__outb(0x02, smbusbase + 0); // Byte read
__outb(cmd, smbusbase + 5); // Command
__outb(0x07, smbusbase + 1); // Clear status
__outb((adr << 1) | 0x01, smbusbase + 4); // DIMM address
__outb(0x12, smbusbase + 0); // Start
//while (((__inb(smbusbase + 1) & 0x08) == 0))
// ; // wait until busy
rdtsc(l1, h1);
static const char s[] = { '/', 0, '-', 0, '\\', 0, '|', 0 };
cprint(POP2_Y, POP2_X + 16, s + cmd % 8); // progress bar
// wait til command finished
while (((__inb(smbusbase + 1) & 0x01) == 0) || ((__inb(smbusbase + 1) & 0x08) != 0)) {
rdtsc(l2, h2);
t = ((uint64_t) (h2 - h1) * 0xffffffff + (l2 - l1));
if (t > toval)
break; // break after 10ms
}
return __inb(smbusbase + 6);
}
static int us15w_read_spd(int dimmadr)
{
int x;
spd_raw[0] = us15w_smb_read_byte(0x50 + dimmadr, 0);
if (spd_raw[0] == 0xff)
return -1; // no spd here
for (x = 1; x < 256; x++) {
spd_raw[x] =
us15w_smb_read_byte(0x50 + dimmadr, (unsigned char)x);
}
return 0;
}
struct pci_smbus_controller {
unsigned vendor;
unsigned device;
char *name;
void (*get_adr) (void);
int (*read_spd) (int dimmadr);
};
static struct pci_smbus_controller smbcontrollers[] = {
// Intel SMBUS
{0x8086, 0x9C22, "Intel HSW-ULT", ich5_get_smb, ich5_read_spd},
{0x8086, 0x8C22, "Intel HSW", ich5_get_smb, ich5_read_spd},
{0x8086, 0x1E22, "Intel Z77", ich5_get_smb, ich5_read_spd},
{0x8086, 0x1C22, "Intel P67", ich5_get_smb, ich5_read_spd},
{0x8086, 0x3B30, "Intel P55", ich5_get_smb, ich5_read_spd},
{0x8086, 0x3A60, "Intel ICH10B", ich5_get_smb, ich5_read_spd},
{0x8086, 0x3A30, "Intel ICH10R", ich5_get_smb, ich5_read_spd},
{0x8086, 0x2930, "Intel ICH9", ich5_get_smb, ich5_read_spd},
{0x8086, 0x283E, "Intel ICH8", ich5_get_smb, ich5_read_spd},
{0x8086, 0x27DA, "Intel ICH7", ich5_get_smb, ich5_read_spd},
{0x8086, 0x266A, "Intel ICH6", ich5_get_smb, ich5_read_spd},
{0x8086, 0x24D3, "Intel ICH5", ich5_get_smb, ich5_read_spd},
{0x8086, 0x24C3, "Intel ICH4", ich5_get_smb, ich5_read_spd},
{0x8086, 0x25A4, "Intel 6300ESB", ich5_get_smb, ich5_read_spd},
{0x8086, 0x269B, "Intel ESB2", ich5_get_smb, ich5_read_spd},
{0x8086, 0x8119, "Intel US15W", us15w_get_smb, us15w_read_spd},
{0x8086, 0x5032, "Intel EP80579", ich5_get_smb, ich5_read_spd},
{0x8086, 0x0f12, "Intel E3800", ich5_get_smb, ich5_read_spd},
// AMD SMBUS
{0x1002, 0x4385, "AMD SB600/700", piix4_get_smb, ich5_read_spd},
{0x1022, 0x780B, "AMD SB800/900", sb800_get_smb, ich5_read_spd},
{0, 0, "", NULL, NULL}
};
static int find_smb_controller(void)
{
int i = 0;
unsigned long valuev, valued;
for (smbdev = 0; smbdev < 32; smbdev++) {
for (smbfun = 0; smbfun < 8; smbfun++) {
pci_conf_read(0, smbdev, smbfun, 0, 2, &valuev);
if (valuev != 0xFFFF) { // if there is something look what's it..
for (i = 0; smbcontrollers[i].vendor > 0; i++) { // check if this is a known smbus controller
if (valuev == smbcontrollers[i].vendor) {
pci_conf_read(0, smbdev, smbfun, 2, 2, &valued); // read the device id
if (valued == smbcontrollers[i].device) {
return i;
}
}
}
}
}
}
return -1;
}
void get_spd_spec(void)
{
int index;
int h, i, j, z;
int k = 0;
int module_size;
int curcol;
int temp_nbd;
int tck;
index = find_smb_controller();
if (index == -1) {
// Unknown SMBUS Controller, exit
return;
}
smbcontrollers[index].get_adr();
cprint(LINE_SPD - 2, 0, "Memory SPD Informations");
cprint(LINE_SPD - 1, 0, "--------------------------");
for (j = 0; j < 8; j++) {
if (smbcontrollers[index].read_spd(j) == 0) {
curcol = 1;
if (spd_raw[2] == 0x0b) {
// We are here if DDR3 present
// First print slot#, module capacity
cprint(LINE_SPD + k, curcol, " - Slot :");
dprint(LINE_SPD + k, curcol + 8, k, 1, 0);
module_size =
get_ddr3_module_size(spd_raw[4] & 0xF,
spd_raw[8] & 0x7,
spd_raw[7] & 0x7,
spd_raw[7] >> 3);
temp_nbd = getnum(module_size);
curcol += 12;
dprint(LINE_SPD + k, curcol, module_size,
temp_nbd, 0);
curcol += temp_nbd;
cprint(LINE_SPD + k, curcol, " MB");
curcol += 4;
// If XMP is supported, check Tck in XMP reg
if (spd_raw[176] == 0x0C && spd_raw[177] == 0x4A
&& spd_raw[12]) {
tck = spd_raw[186];
} else {
tck = spd_raw[12];
}
// Then module jedec speed
switch (tck) {
default:
cprint(LINE_SPD + k, curcol,
"DDR3-????");
break;
case 20:
cprint(LINE_SPD + k, curcol,
"DDR3-800");
curcol--;
break;
case 15:
cprint(LINE_SPD + k, curcol,
"DDR3-1066");
break;
case 12:
cprint(LINE_SPD + k, curcol,
"DDR3-1333");
break;
case 10:
cprint(LINE_SPD + k, curcol,
"DDR3-1600");
break;
case 9:
cprint(LINE_SPD + k, curcol,
"DDR3-1866");
break;
case 8:
cprint(LINE_SPD + k, curcol,
"DDR3-2133");
break;
case 7:
cprint(LINE_SPD + k, curcol,
"DDR3-2400");
break;
case 6:
cprint(LINE_SPD + k, curcol,
"DDR3-2533");
break;
case 5:
cprint(LINE_SPD + k, curcol,
"DDR3-2666");
break;
}
curcol += 10;
if ((spd_raw[8] >> 3) == 1) {
cprint(LINE_SPD + k, curcol, "ECC");
curcol += 4;
}
// Then print module infos (manufacturer & part number)
spd_raw[117] &= 0x0F; // Parity odd or even
for (i = 0; jep106[i].cont_code < 9; i++) {
if (spd_raw[117] == jep106[i].cont_code &&
spd_raw[118] == jep106[i].hex_byte) {
// We are here if a Jedec manufacturer is detected
cprint(LINE_SPD + k,
curcol,
"-");
curcol += 2;
cprint(LINE_SPD + k,
curcol,
jep106[i].name);
for (z = 0; jep106[i].name[z] != '\0'; z++) {
curcol++;
}
curcol++;
// Display module serial number
for (h = 128; h < 146; h++) {
cprint(LINE_SPD + k,
curcol,
convert_hex_to_char
(spd_raw[h]));
curcol++;
}
// Detect Week and Year of Manufacturing (Think to upgrade after 2015 !!!)
if (curcol <= 72
&& spd_raw[120] > 3
&& spd_raw[120] < 16
&& spd_raw[121] < 55) {
cprint(LINE_SPD + k,
curcol, "(W");
dprint(LINE_SPD + k,
curcol + 2,
spd_raw[121],
2,
0);
if (spd_raw[121] < 10) {
cprint(LINE_SPD + k,
curcol + 2,
"0");
}
cprint(LINE_SPD + k,
curcol + 4,
"'");
dprint(LINE_SPD + k,
curcol + 5,
spd_raw[120],
2,
0);
if (spd_raw[120] < 10) {
cprint(LINE_SPD
+ k,
curcol +
5, "0");
}
cprint(LINE_SPD + k,
curcol + 7, ")");
curcol += 9;
}
// Detect XMP Memory
if (spd_raw[176] == 0x0C
&& spd_raw[177] == 0x4A) {
cprint(LINE_SPD + k,
curcol, "*XMP*");
}
}
}
}
// We enter this function if DDR2 is detected
if (spd_raw[2] == 0x08) {
// First print slot#, module capacity
cprint(LINE_SPD + k, curcol, " - Slot :");
dprint(LINE_SPD + k, curcol + 8, k, 1, 0);
module_size = get_ddr2_module_size(spd_raw[31],
spd_raw[5]);
temp_nbd = getnum(module_size);
curcol += 12;
dprint(LINE_SPD + k, curcol, module_size,
temp_nbd, 0);
curcol += temp_nbd;
cprint(LINE_SPD + k, curcol, " MB");
curcol += 4;
// Then module jedec speed
float ddr2_speed, byte1, byte2;
byte1 = (spd_raw[9] >> 4) * 10;
byte2 = spd_raw[9] & 0xF;
ddr2_speed = 1 / (byte1 + byte2) * 10000 * 2;
temp_nbd = getnum(ddr2_speed);
cprint(LINE_SPD + k, curcol, "DDR2-");
curcol += 5;
dprint(LINE_SPD + k, curcol, ddr2_speed,
temp_nbd, 0);
curcol += temp_nbd;
if ((spd_raw[11] >> 1) == 1) {
cprint(LINE_SPD + k, curcol + 1, "ECC");
curcol += 4;
}
// Then print module infos (manufacturer & part number)
int ccode = 0;
for (i = 64; i < 72; i++) {
if (spd_raw[i] == 0x7F) {
ccode++;
}
}
curcol++;
for (i = 0; jep106[i].cont_code < 9; i++) {
if (ccode == jep106[i].cont_code
&& spd_raw[64 + ccode] ==
jep106[i].hex_byte) {
// We are here if a Jedec manufacturer is detected
cprint(LINE_SPD + k, curcol,
"-");
curcol += 2;
cprint(LINE_SPD + k, curcol,
jep106[i].name);
for (z = 0;
jep106[i].name[z] != '\0';
z++) {
curcol++;
}
curcol++;
// Display module serial number
for (h = 73; h < 91; h++) {
cprint(LINE_SPD + k,
curcol,
convert_hex_to_char
(spd_raw[h]));
curcol++;
}
}
}
}
k++;
}
}
}
void show_spd(void)
{
int index;
int i, j;
int flag = 0;
wait_keyup();
index = find_smb_controller();
if (index == -1) {
cprint(POP2_Y, POP2_X + 1, "SMBus Controller not known");
while (!get_key()) ;
wait_keyup();
return;
} else {
cprint(POP2_Y, POP2_X + 1, "SPD Data: Slot");
}
smbcontrollers[index].get_adr();
for (j = 0; j < 16; j++) {
if (smbcontrollers[index].read_spd(j) == 0) {
dprint(POP2_Y, POP2_X + 15, j, 2, 0);
for (i = 0; i < 256; i++) {
hprint2(2 + POP2_Y + i / 16,
3 + POP2_X + (i % 16) * 3, spd_raw[i],
2);
}
flag = 0;
while (!flag) {
if (get_key())
flag++;
}
wait_keyup();
}
}
}
static int get_ddr3_module_size(int sdram_capacity, int prim_bus_width,
int sdram_width, int ranks)
{
int module_size;
switch (sdram_capacity) {
case 0:
module_size = 256;
break;
case 1:
module_size = 512;
break;
default:
case 2:
module_size = 1024;
break;
case 3:
module_size = 2048;
break;
case 4:
module_size = 4096;
break;
case 5:
module_size = 8192;
break;
case 6:
module_size = 16384;
break;
}
module_size /= 8;
switch (prim_bus_width) {
case 0:
module_size *= 8;
break;
case 1:
module_size *= 16;
break;
case 2:
module_size *= 32;
break;
case 3:
module_size *= 64;
break;
}
switch (sdram_width) {
case 0:
module_size /= 4;
break;
case 1:
module_size /= 8;
break;
case 2:
module_size /= 16;
break;
case 3:
module_size /= 32;
break;
}
module_size *= (ranks + 1);
return module_size;
}
static int get_ddr2_module_size(int rank_density_byte, int rank_num_byte)
{
int module_size;
switch (rank_density_byte) {
case 1:
module_size = 1024;
break;
case 2:
module_size = 2048;
break;
case 4:
module_size = 4096;
break;
case 8:
module_size = 8192;
break;
case 16:
module_size = 16384;
break;
case 32:
module_size = 128;
break;
case 64:
module_size = 256;
break;
default:
case 128:
module_size = 512;
break;
}
module_size *= (rank_num_byte & 7) + 1;
return module_size;
}
struct ascii_map {
unsigned hex_code;
char *name;
};
static char *convert_hex_to_char(unsigned hex_org)
{
static char buf[2] = " ";
if (hex_org >= 0x20 && hex_org < 0x80) {
buf[0] = hex_org;
} else {
//buf[0] = '\0';
buf[0] = ' ';
}
return buf;
}