Chameleon

Chameleon Svn Source Tree

Root/tags/2.0/i386/libsaio/spd.c

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

Archive Download this file

Revision: 1808