Chameleon

Chameleon Svn Source Tree

Root/branches/meklort/i386/libsaio/spd.c

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

Archive Download this file

Revision: 429