13093fb0fcdfc4e6abe3098d686b4e194e10f2ae
[coreboot.git] / util / romcc / tests / linux_test5.c
1 #include "linux_syscall.h"
2 #include "linux_console.h"
3
4 inline int log2(int value)
5 {
6         /* __builtin_bsr is a exactly equivalent to the x86 machine
7          * instruction with the exception that it returns -1  
8          * when the value presented to it is zero.
9          * Otherwise __builtin_bsr returns the zero based index of
10          * the highest bit set.
11          */
12         return __builtin_bsr(value);
13 }
14
15
16 static int smbus_read_byte(unsigned device, unsigned address)
17 {
18         static const unsigned char dimm[] = {
19 0x80, 0x08, 0x07, 0x0d, 0x0a, 0x02, 0x48, 0x00, 0x04, 0x60, 0x70, 0x02, 0x82, 0x08, 0x08, 0x01,
20 0x0e, 0x04, 0x0c, 0x01, 0x02, 0x20, 0x00, 0x75, 0x70, 0x00, 0x00, 0x48, 0x30, 0x48, 0x2a, 0x40,
21 0x80, 0x80, 0x45, 0x45, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
22 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x33,
23 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
24 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
25 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
26 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
27 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
28 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
29 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
30 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
31 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
32 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
33 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
34 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
35
36 0x80, 0x08, 0x07, 0x0d, 0x0a, 0x02, 0x48, 0x00, 0x04, 0x60, 0x70, 0x02, 0x82, 0x08, 0x08, 0x01,
37 0x0e, 0x04, 0x0c, 0x01, 0x02, 0x20, 0x00, 0x75, 0x70, 0x00, 0x00, 0x48, 0x30, 0x48, 0x2a, 0x40,
38 0x80, 0x80, 0x45, 0x45, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
39 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x33,
40 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
41 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
42 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
43 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
44 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
45 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
46 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
47 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
48 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
49 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
50 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
51 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
52         };
53         return dimm[(device << 8) + address];
54 }
55
56 #define SMBUS_MEM_DEVICE_START 0x00
57 #define SMBUS_MEM_DEVICE_END   0x01
58 #define SMBUS_MEM_DEVICE_INC   1
59
60 /* Function 2 */
61 #define DRAM_CONFIG_HIGH   0x94
62 #define  DCH_MEMCLK_SHIFT  20
63 #define  DCH_MEMCLK_MASK   7
64 #define  DCH_MEMCLK_100MHZ 0
65 #define  DCH_MEMCLK_133MHZ 2
66 #define  DCH_MEMCLK_166MHZ 5
67 #define  DCH_MEMCLK_200MHZ 7
68
69 /* Function 3 */
70 #define NORTHBRIDGE_CAP    0xE8
71 #define  NBCAP_128Bit         0x0001
72 #define  NBCAP_MP             0x0002
73 #define  NBCAP_BIG_MP         0x0004
74 #define  NBCAP_ECC            0x0004
75 #define  NBCAP_CHIPKILL_ECC   0x0010
76 #define  NBCAP_MEMCLK_SHIFT   5
77 #define  NBCAP_MEMCLK_MASK    3
78 #define  NBCAP_MEMCLK_100MHZ  3
79 #define  NBCAP_MEMCLK_133MHZ  2
80 #define  NBCAP_MEMCLK_166MHZ  1
81 #define  NBCAP_MEMCLK_200MHZ  0
82 #define  NBCAP_MEMCTRL        0x0100
83
84 typedef unsigned char uint8_t;
85 typedef unsigned int uint32_t;
86
87 static unsigned spd_to_dimm(unsigned device)
88 {
89         return (device - SMBUS_MEM_DEVICE_START);
90 }
91
92 static void disable_dimm(unsigned index)
93 {
94         print_debug("disabling dimm"); 
95         print_debug_hex8(index); 
96         print_debug("\r\n");
97 #if 0
98         pci_write_config32(PCI_DEV(0, 0x18, 2), DRAM_CSBASE + (((index << 1)+0)<<2), 0);
99         pci_write_config32(PCI_DEV(0, 0x18, 2), DRAM_CSBASE + (((index << 1)+1)<<2), 0);
100 #endif
101 }
102
103
104 struct mem_param {
105         uint8_t cycle_time;
106         uint32_t dch_memclk;
107 };
108
109 static const struct mem_param *get_mem_param(unsigned min_cycle_time)
110 {
111         static const struct mem_param speed[] = {
112                 {
113                         .cycle_time = 0xa0,
114                         .dch_memclk = DCH_MEMCLK_100MHZ << DCH_MEMCLK_SHIFT,
115                 },
116                 {
117                         .cycle_time = 0x75,
118                         .dch_memclk = DCH_MEMCLK_133MHZ << DCH_MEMCLK_SHIFT,
119                 },
120                 {
121                         .cycle_time = 0x60,
122                         .dch_memclk = DCH_MEMCLK_166MHZ << DCH_MEMCLK_SHIFT,
123                 },
124                 {
125                         .cycle_time = 0x50,
126                         .dch_memclk = DCH_MEMCLK_200MHZ << DCH_MEMCLK_SHIFT,
127                 },
128                 {
129                         .cycle_time = 0x00,
130                 },
131         };
132         const struct mem_param *param;
133         for(param = &speed[0]; param->cycle_time ; param++) {
134                 if (min_cycle_time > (param+1)->cycle_time) {
135                         break;
136                 }
137         }
138         if (!param->cycle_time) {
139                 die("min_cycle_time to low");
140         }
141         return param;
142 }
143
144 #if 1
145 static void debug(int c)
146 {
147         print_debug_char(c);
148         print_debug_char('\r');
149         print_debug_char('\n');
150 }
151 #endif
152 static const struct mem_param *spd_set_memclk(void)
153 {
154         /* Compute the minimum cycle time for these dimms */
155         const struct mem_param *param;
156         unsigned min_cycle_time, min_latency;
157         unsigned device;
158         uint32_t value;
159
160         static const int latency_indicies[] = { 26, 23, 9 };
161         static const unsigned char min_cycle_times[] = {
162                 [NBCAP_MEMCLK_200MHZ] = 0x50, /* 5ns */
163                 [NBCAP_MEMCLK_166MHZ] = 0x60, /* 6ns */
164                 [NBCAP_MEMCLK_133MHZ] = 0x75, /* 7.5ns */
165                 [NBCAP_MEMCLK_100MHZ] = 0xa0, /* 10ns */
166         };
167
168
169 #if 0
170         value = pci_read_config32(PCI_DEV(0, 0x18, 3), NORTHBRIDGE_CAP);
171 #else
172         value = 0x50;
173 #endif
174         min_cycle_time = min_cycle_times[(value >> NBCAP_MEMCLK_SHIFT) & NBCAP_MEMCLK_MASK];
175         min_latency = 2;
176
177 #if 1
178         print_debug("min_cycle_time: "); 
179         print_debug_hex8(min_cycle_time); 
180         print_debug(" min_latency: ");
181         print_debug_hex8(min_latency);
182         print_debug("\r\n");
183 #endif
184
185         /* Compute the least latency with the fastest clock supported
186          * by both the memory controller and the dimms.
187          */
188         for(device = SMBUS_MEM_DEVICE_START;
189                 device <= SMBUS_MEM_DEVICE_END;
190                 device += SMBUS_MEM_DEVICE_INC)
191         {
192                 int new_cycle_time, new_latency;
193                 int index;
194                 int latencies;
195                 int latency;
196
197                 debug('A');
198                 /* First find the supported CAS latencies
199                  * Byte 18 for DDR SDRAM is interpreted:
200                  * bit 0 == CAS Latency = 1.0
201                  * bit 1 == CAS Latency = 1.5
202                  * bit 2 == CAS Latency = 2.0
203                  * bit 3 == CAS Latency = 2.5
204                  * bit 4 == CAS Latency = 3.0
205                  * bit 5 == CAS Latency = 3.5
206                  * bit 6 == TBD
207                  * bit 7 == TBD
208                  */
209                 new_cycle_time = 0xa0;
210                 new_latency = 5;
211
212                 latencies = smbus_read_byte(device, 18);
213                 if (latencies <= 0) continue;
214
215                 debug('B');
216                 /* Compute the lowest cas latency supported */
217                 latency = log2(latencies) -2;
218
219                 /* Loop through and find a fast clock with a low latency */
220                 for(index = 0; index < 3; index++, latency++) {
221                         int value;
222                         debug('C');
223                         if ((latency < 2) || (latency > 4) ||
224                                 (!(latencies & (1 << latency)))) {
225                                 continue;
226                         }
227                         debug('D');
228                         value = smbus_read_byte(device, latency_indicies[index]);
229                         if (value < 0) continue;
230
231                         debug('E');
232                         /* Only increase the latency if we decreas the clock */
233                         if ((value >= min_cycle_time) && (value < new_cycle_time)) {
234                                 new_cycle_time = value;
235                                 new_latency = latency;
236 #if 1
237                                 print_debug("device: ");
238                                 print_debug_hex8(device);
239                                 print_debug(" new_cycle_time: "); 
240                                 print_debug_hex8(new_cycle_time); 
241                                 print_debug(" new_latency: ");
242                                 print_debug_hex8(new_latency);
243                                 print_debug("\r\n");
244 #endif
245                         }
246                         debug('G');
247                 }
248                 debug('H');
249 #if 1
250                 print_debug("device: ");
251                 print_debug_hex8(device);
252                 print_debug(" new_cycle_time: "); 
253                 print_debug_hex8(new_cycle_time); 
254                 print_debug(" new_latency: ");
255                 print_debug_hex8(new_latency);
256                 print_debug("\r\n");
257 #endif
258                 if (new_latency > 4){
259                         continue;
260                 }
261                 debug('I');
262                 /* Does min_latency need to be increased? */
263                 if (new_cycle_time > min_cycle_time) {
264                         min_cycle_time = new_cycle_time;
265                 }
266                 /* Does min_cycle_time need to be increased? */
267                 if (new_latency > min_latency) {
268                         min_latency = new_latency;
269                 }
270 #if 1
271                 print_debug("device: ");
272                 print_debug_hex8(device);
273                 print_debug(" min_cycle_time: "); 
274                 print_debug_hex8(min_cycle_time); 
275                 print_debug(" min_latency: ");
276                 print_debug_hex8(min_latency);
277                 print_debug("\r\n");
278 #endif
279         }
280         /* Make a second pass through the dimms and disable
281          * any that cannot support the selected memclk and cas latency.
282          */
283         for(device = SMBUS_MEM_DEVICE_START;
284                 device <= SMBUS_MEM_DEVICE_END;
285                 device += SMBUS_MEM_DEVICE_INC)
286         {
287                 int latencies;
288                 int latency;
289                 int index;
290                 int value;
291                 int dimm;
292                 latencies = smbus_read_byte(device, 18);
293                 if (latencies <= 0) {
294                         goto dimm_err;
295                 }
296
297                 /* Compute the lowest cas latency supported */
298                 latency = log2(latencies) -2;
299
300                 /* Walk through searching for the selected latency */
301                 for(index = 0; index < 3; index++, latency++) {
302                         if (!(latencies & (1 << latency))) {
303                                 continue;
304                         }
305                         if (latency == min_latency)
306                                 break;
307                 }
308                 /* If I can't find the latency or my index is bad error */
309                 if ((latency != min_latency) || (index >= 3)) {
310                         goto dimm_err;
311                 }
312                 
313                 /* Read the min_cycle_time for this latency */
314                 value = smbus_read_byte(device, latency_indicies[index]);
315                 
316                 /* All is good if the selected clock speed 
317                  * is what I need or slower.
318                  */
319                 if (value <= min_cycle_time) {
320                         continue;
321                 }
322                 /* Otherwise I have an error, disable the dimm */
323         dimm_err:
324                 disable_dimm(spd_to_dimm(device));
325         }
326 #if 1
327         print_debug("min_cycle_time: "); 
328         print_debug_hex8(min_cycle_time); 
329         print_debug(" min_latency: ");
330         print_debug_hex8(min_latency);
331         print_debug("\r\n");
332 #endif
333         /* Now that I know the minimum cycle time lookup the memory parameters */
334         param = get_mem_param(min_cycle_time);
335
336 #if 0
337         /* Update DRAM Config High with our selected memory speed */
338         value = pci_read_config32(PCI_DEV(0, 0x18, 2), DRAM_CONFIG_HIGH);
339         value &= ~(DCH_MEMCLK_MASK << DCH_MEMCLK_SHIFT);
340         value |= param->dch_memclk;
341         pci_write_config32(PCI_DEV(0, 0x18, 2), DRAM_CONFIG_HIGH, value);
342
343         static const unsigned latencies[] = { 1, 5, 2 };
344         /* Update DRAM Timing Low wiht our selected cas latency */
345         value = pci_read_config32(PCI_DEV(0, 0x18, 2), DRAM_CONFIG_LOW);
346         value &= ~7;
347         value |= latencies[min_latency - 2];
348         pci_write_config32(PCI_DEV(0, 0x18, 2), DRAM_CONFIG_LOW, value);
349 #endif
350         
351         return param;
352 }
353
354 static void main(void)
355 {
356         const struct mem_param *param;
357         param = spd_set_memclk();
358         _exit(0);
359 }