/* * MemTest86+ V5 Specific code (GPL V2.0) * By Samuel DEMEULEMEESTER, sdemeule@memtest.org * http://www.canardpc.com - http://www.memtest.org */ #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 extern void wait_keyup(); int smbdev, smbfun; unsigned short smbusbase; unsigned char spd_raw[256]; char s[] = {'/', 0, '-', 0, '\\', 0, '|', 0}; 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(); } } void sb800_get_smb(void) { int lbyte, hbyte; __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; } } unsigned char ich5_smb_read_byte(unsigned char adr, unsigned char cmd) { int l1, h1, l2, h2; unsigned long long t; __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 = ((h2 - h1) * 0xffffffff + (l2 - l1)) / vv->clks_msec; if (t > 10) 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; } unsigned char us15w_smb_read_byte(unsigned char adr, unsigned char cmd) { int l1, h1, l2, h2; unsigned long long t; //__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 til busy rdtsc(l1, h1); cprint(POP2_Y, POP2_X + 16, s + cmd % 8); // progress bar while (((__inb(smbusbase + 1) & 0x01) == 0) || ((__inb(smbusbase + 1) & 0x08) != 0)) { // wait til command finished rdtsc(l2, h2); t = ((h2 - h1) * 0xffffffff + (l2 - l1)) / vv->clks_msec; if (t > 10) 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}, // 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} }; 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; pop2up(); wait_keyup(); index = find_smb_controller(); if (index == -1) { cprint(POP2_Y, POP2_X+1, "SMBus Controller not known"); while (!get_key()); wait_keyup(); pop2down(); 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(); } } pop2down(); } 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; } 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; }; 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; }