Chameleon Applications

Chameleon Applications Svn Source Tree

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

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

Archive Download this file

Revision: 214