Chameleon

Chameleon Svn Source Tree

Root/trunk/i386/libsaio/spd.c

1/*
2 * spd.c - serial presence detect memory information
3 * (restored from pcefi10.5)
4 */
5
6#include "libsaio.h"
7#include "pci.h"
8#include "platform.h"
9#include "spd.h"
10#include "saio_internal.h"
11#include "bootstruct.h"
12#include "memvendors.h"
13
14#ifndef DEBUG_SPD
15#define DEBUG_SPD 0
16#endif
17
18#if DEBUG_SPD
19#define DBG(x...)printf(x)
20#else
21#define DBG(x...)
22#endif
23
24static const char *spd_memory_types[] =
25{
26"RAM", /* 00h Undefined */
27"FPM", /* 01h FPM */
28"EDO", /* 02h EDO */
29"",/* 03h PIPELINE NIBBLE */
30"SDRAM", /* 04h SDRAM */
31"",/* 05h MULTIPLEXED ROM */
32"DDR SGRAM",/* 06h SGRAM DDR */
33"DDR SDRAM",/* 07h SDRAM DDR */
34"DDR2 SDRAM", /* 08h SDRAM DDR 2 */
35"",/* 09h Undefined */
36"",/* 0Ah Undefined */
37"DDR3 SDRAM" /* 0Bh SDRAM DDR 3 */
38};
39
40#define UNKNOWN_MEM_TYPE 2
41static uint8_t spd_mem_to_smbios[] =
42{
43UNKNOWN_MEM_TYPE, /* 00h Undefined */
44UNKNOWN_MEM_TYPE, /* 01h FPM */
45UNKNOWN_MEM_TYPE, /* 02h EDO */
46UNKNOWN_MEM_TYPE, /* 03h PIPELINE NIBBLE */
47SMB_MEM_TYPE_SDRAM, /* 04h SDRAM */
48SMB_MEM_TYPE_ROM, /* 05h MULTIPLEXED ROM */
49SMB_MEM_TYPE_SGRAM, /* 06h SGRAM DDR */
50SMB_MEM_TYPE_DDR, /* 07h SDRAM DDR */
51SMB_MEM_TYPE_DDR2, /* 08h SDRAM DDR 2 */
52UNKNOWN_MEM_TYPE, /* 09h Undefined */
53UNKNOWN_MEM_TYPE, /* 0Ah Undefined */
54SMB_MEM_TYPE_DDR3 /* 0Bh SDRAM DDR 3 */
55};
56#define SPD_TO_SMBIOS_SIZE (sizeof(spd_mem_to_smbios)/sizeof(uint8_t))
57
58#define rdtsc(low,high) \
59__asm__ __volatile__("rdtsc" : "=a" (low), "=d" (high))
60
61#define SMBHSTSTS 0
62#define SMBHSTCNT 2
63#define SMBHSTCMD 3
64#define SMBHSTADD 4
65#define SMBHSTDAT 5
66
67/** Get Vendor Name from spd, 2 cases handled DDR3 and DDR2,
68 have different formats, always return a valid ptr.*/
69const char * getVendorName(RamSlotInfo_t* slot)
70{
71 uint8_t bank = 0;
72 uint8_t code = 0;
73 int i = 0;
74 const char * spd = slot->spd;
75
76 if (spd[2]==0x0b) { // DDR3
77 bank = spd[0x75];
78 code = spd[0x76];
79 for (i=0; i < VEN_MAP_SIZE; i++)
80 if (bank==vendorMap[i].bank && code==vendorMap[i].code)
81 return vendorMap[i].name;
82 }
83 else if (spd[2]==0x08 || spd[2]==0x07) { // DDR2 or DDR
84 if(spd[0x40]==0x7f) {
85 for (i=0x40; i<0x48 && spd[i]==0x7f;i++) bank++;
86 code = spd[i];
87 } else {
88 code = spd[0x40];
89 bank = 0;
90 }
91 for (i=0; i < VEN_MAP_SIZE; i++)
92 if (bank==vendorMap[i].bank && code==vendorMap[i].code)
93 return vendorMap[i].name;
94 }
95 /* OK there is no vendor id here lets try to match the partnum if it exists */
96 if (strstr(slot->PartNo,"GU332") == slot->PartNo) // Unifosa fingerprint
97 return "Unifosa";
98 return "NoName";
99}
100
101/** Get Default Memory Module Speed (no overclocking handled) */
102int getDDRspeedMhz(const char * spd)
103{
104 if (spd[2]==0x0b) { // DDR3
105 switch(spd[12]) {
106 case 0x0f:
107 return 1066;
108 case 0x0c:
109 return 1333;
110 case 0x0a:
111 return 1600;
112 case 0x14:
113 default:
114 return 800;
115 }
116 }
117 else if (spd[2]==0x08) { // DDR2
118 switch(spd[9]) {
119 case 0x50:
120 return 400;
121 case 0x3d:
122 return 533;
123 case 0x30:
124 return 667;
125 case 0x25:
126 default:
127 return 800;
128 }
129 }
130 return 800; // default freq for unknown types
131}
132
133#define UIS(a) ((uint32_t)spd[a])
134
135/** Get DDR3 or DDR2 serial number, 0 most of the times, always return a valid ptr */
136const char *getDDRSerial(const char* spd)
137{
138 static char asciiSerial[16];
139 static uint8_t serialnum=0;
140 uint32_t ret=0,i;
141
142 if (spd[2]==0x0b) {// DDR3
143 if ( isascii(spd[122]) && isascii(spd[123]) &&
144 isascii(spd[124]) && isascii(spd[125]) ) {
145 for(i=0; i<4; i++) asciiSerial[i] = spd[122+i];
146 asciiSerial[4] = 0;
147 return asciiSerial;
148 }
149 else // assume it is lsb to msb
150 ret = UIS(122) | (UIS(123)<<8) | (UIS(124)<<16) | ((UIS(125)&0x7f)<<24);
151 }
152 else if (spd[2]==0x08 || spd[2]==0x07) { // DDR2 or DDR
153 if ( isascii(spd[95]) && isascii(spd[96]) &&
154 isascii(spd[97]) && isascii(spd[98]) ) {
155 for (i=0; i<4; i++) asciiSerial[i] = spd[95+i];
156 asciiSerial[4] = 0;
157 return asciiSerial;
158 }
159 else
160 ret = UIS(95) | (UIS(96)<<8) | (UIS(97)<<16) | ((UIS(98)&0x7f)<<24);
161 }
162
163 if (!ret) sprintf(asciiSerial, "10000000%d", serialnum++);
164 else sprintf(asciiSerial, "%d", ret);
165
166 return asciiSerial;
167}
168
169/** Get DDR3 or DDR2 Part Number, always return a valid ptr */
170const char * getDDRPartNum(const char* spd)
171{
172 const char * sPart = NULL;
173 int i;
174 bool bZero = false;
175
176 if (spd[2]==0x0b) // DDR3
177 sPart = &spd[128];
178 else if (spd[2]==0x08 || spd[2]==0x07) // DDR2 or DDR
179 sPart = &spd[73];
180 if (sPart) { // Check that the spd part name is zero terminated and that it is ascii:
181 for (i=0; i<32; i++) {
182 if (sPart[i]==0) {
183 bZero = true;
184 break;
185 }
186 else if ( !isascii(sPart[i]) ) {
187 sPart = NULL;
188 break;
189 }
190 }
191 }
192 return ( sPart==NULL || !(*sPart) || !bZero ) ?
193 "N/A" : sPart;
194}
195
196/** Read one byte from the intel i2c, used for reading SPD on intel chipsets only. */
197unsigned char smb_read_byte_intel(uint32_t base, uint8_t adr, uint8_t cmd)
198{
199 int l1, h1, l2, h2;
200 unsigned long long t;
201
202 outb(base + SMBHSTSTS, 0x1f);// reset SMBus Controller
203 outb(base + SMBHSTDAT, 0xff);
204
205 while( inb(base + SMBHSTSTS) & 0x01);// wait until ready
206
207 outb(base + SMBHSTCMD, cmd);
208 outb(base + SMBHSTADD, (adr << 1) | 0x01 );
209 outb(base + SMBHSTCNT, 0x48 );
210
211 rdtsc(l1, h1);
212
213 while (!( inb(base + SMBHSTSTS) & 0x02))// wait til command finished
214{
215rdtsc(l2, h2);
216t = ((h2 - h1) * 0xffffffff + (l2 - l1)) / (Platform.CPU.TSCFrequency / 40);
217if (t > 10)
218break;// break after 10ms
219 }
220 return inb(base + SMBHSTDAT);
221}
222
223int mapping []= {0,2,1,3,4,6,5,7,8,10,9,11};
224
225/** Read from smbus the SPD content and interpret it for detecting memory attributes */
226static void read_smb_intel(pci_dt_t *smbus_dev)
227{
228 int i, x, speed;
229 uint8_t spd_size, spd_type;
230 uint32_t base;
231 bool dump = false;
232 RamSlotInfo_t* slot;
233
234 base = pci_config_read16(smbus_dev->dev.addr, 0x20) & 0xFFFE;
235 DBG("Scanning smbus_dev <%04x, %04x> ...\n",smbus_dev->vendor_id, smbus_dev->device_id);
236
237 getBoolForKey("DumpSPD", &dump, &bootInfo->bootConfig);
238 bool fullBanks = // needed at least for laptops
239 Platform.DMI.MemoryModules == Platform.DMI.MaxMemorySlots;
240 // Search MAX_RAM_SLOTS slots
241 for (i = 0; i < MAX_RAM_SLOTS; i++){
242 slot = &Platform.RAM.DIMM[i];
243 spd_size = smb_read_byte_intel(base, 0x50 + i, 0);
244
245 // Check spd is present
246 if (spd_size && (spd_size != 0xff) ) {
247 slot->spd = malloc(spd_size);
248 if (slot->spd == NULL) continue;
249
250 slot->InUse = true;
251
252 bzero(slot->spd, spd_size);
253
254 // Copy spd data into buffer
255 for (x = 0; x < spd_size; x++)
256 slot->spd[x] = smb_read_byte_intel(base, 0x50 + i, x);
257
258 switch (slot->spd[SPD_MEMORY_TYPE]) {
259 case SPD_MEMORY_TYPE_SDRAM_DDR2:
260
261 slot->ModuleSize = ((1 << (slot->spd[SPD_NUM_ROWS] & 0x0f) + (slot->spd[SPD_NUM_COLUMNS] & 0x0f) - 17) *
262 ((slot->spd[SPD_NUM_DIMM_BANKS] & 0x7) + 1) * slot->spd[SPD_NUM_BANKS_PER_SDRAM]);
263 break;
264
265 case SPD_MEMORY_TYPE_SDRAM_DDR3:
266
267 slot->ModuleSize = ((slot->spd[4] & 0x0f) + 28 ) + ((slot->spd[8] & 0x7) + 3 );
268 slot->ModuleSize -= (slot->spd[7] & 0x7) + 25;
269 slot->ModuleSize = ((1 << slot->ModuleSize) * (((slot->spd[7] >> 3) & 0x1f) + 1));
270
271 break;
272 }
273
274 spd_type = (slot->spd[SPD_MEMORY_TYPE] < ((char) 12) ? slot->spd[SPD_MEMORY_TYPE] : 0);
275 slot->Type = spd_mem_to_smbios[spd_type];
276 slot->PartNo = strdup(getDDRPartNum(slot->spd) );
277 slot->Vendor = strdup(getVendorName(slot) );
278 slot->SerialNo = strdup(getDDRSerial(slot->spd));
279
280 // determine spd speed
281 speed = getDDRspeedMhz(slot->spd);
282 if (speed > slot->Frequency) slot->Frequency = speed; // just in case dmi wins on spd
283 if(dump) {
284 printf("Slot %d Type %d %dMB (%s) %dMHz Vendor=%s, PartNo=%s SerialNo=%s\n",
285 i,
286 (int)slot->Type,
287 slot->ModuleSize,
288 spd_memory_types[spd_type],
289 slot->Frequency,
290 slot->Vendor,
291 slot->PartNo,
292 slot->SerialNo);
293 dumpPhysAddr("spd content: ",slot->spd, spd_size);
294 getc();
295 }
296 }
297
298 // laptops sometimes show slot 0 and 2 with slot 1 empty when only 2 slots are presents so:
299 Platform.DMI.DIMM[i]=
300 i>0 && Platform.RAM.DIMM[1].InUse==false && fullBanks && Platform.DMI.MaxMemorySlots==2 ?
301 mapping[i] : i; // for laptops case, mapping setup would need to be more generic than this
302
303 if (slot->spd) {
304 free(slot->spd);
305 slot->spd = NULL;
306 }
307
308 } // for
309}
310
311static struct smbus_controllers_t smbus_controllers[] = {
312
313{0x8086, 0x269B, "ESB2", read_smb_intel },
314{0x8086, 0x25A4, "6300ESB", read_smb_intel },
315{0x8086, 0x24C3, "ICH4", read_smb_intel },
316{0x8086, 0x24D3, "ICH5", read_smb_intel },
317{0x8086, 0x266A, "ICH6", read_smb_intel },
318{0x8086, 0x27DA, "ICH7", read_smb_intel },
319{0x8086, 0x283E, "ICH8", read_smb_intel },
320{0x8086, 0x2930, "ICH9", read_smb_intel },
321{0x8086, 0x3A30, "ICH10R", read_smb_intel },
322{0x8086, 0x3A60, "ICH10B", read_smb_intel },
323{0x8086, 0x3B30, "P55", read_smb_intel },
324{0x8086, 0x5032, "EP80579", read_smb_intel }
325
326};
327
328// initial call : pci_dt = root_pci_dev;
329// find_and_read_smbus_controller(root_pci_dev);
330bool find_and_read_smbus_controller(pci_dt_t* pci_dt)
331{
332 pci_dt_t*current = pci_dt;
333 int i;
334
335 while (current) {
336#if DEBUG_SPD
337 printf("%02x:%02x.%x [%04x] [%04x:%04x] :: %s\n",
338 current->dev.bits.bus, current->dev.bits.dev, current->dev.bits.func,
339 current->class_id, current->vendor_id, current->device_id,
340 get_pci_dev_path(current));
341#endif
342for ( i = 0; i < sizeof(smbus_controllers) / sizeof(smbus_controllers[0]); i++ )
343 {
344 if (current->vendor_id == smbus_controllers[i].vendor &&
345 current->device_id == smbus_controllers[i].device)
346 {
347 smbus_controllers[i].read_smb(current); // read smb
348 return true;
349 }
350 }
351 find_and_read_smbus_controller(current->children);
352 current = current->next;
353 }
354 return false; // not found
355}
356
357void scan_spd(PlatformInfo_t *p)
358{
359 find_and_read_smbus_controller(root_pci_dev);
360}
361

Archive Download this file

Revision: 103