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

Archive Download this file

Revision: 92