Commit | Line | Data |
---|---|---|
920dae64 AT |
1 | /* |
2 | * ========== Copyright Header Begin ========================================== | |
3 | * | |
4 | * Hypervisor Software File: cpu-seeprom.c | |
5 | * | |
6 | * Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved. | |
7 | * | |
8 | * - Do no alter or remove copyright notices | |
9 | * | |
10 | * - Redistribution and use of this software in source and binary forms, with | |
11 | * or without modification, are permitted provided that the following | |
12 | * conditions are met: | |
13 | * | |
14 | * - Redistribution of source code must retain the above copyright notice, | |
15 | * this list of conditions and the following disclaimer. | |
16 | * | |
17 | * - Redistribution in binary form must reproduce the above copyright notice, | |
18 | * this list of conditions and the following disclaimer in the | |
19 | * documentation and/or other materials provided with the distribution. | |
20 | * | |
21 | * Neither the name of Sun Microsystems, Inc. or the names of contributors | |
22 | * may be used to endorse or promote products derived from this software | |
23 | * without specific prior written permission. | |
24 | * | |
25 | * This software is provided "AS IS," without a warranty of any kind. | |
26 | * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, | |
27 | * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A | |
28 | * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN | |
29 | * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR | |
30 | * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR | |
31 | * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN | |
32 | * OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR | |
33 | * FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE | |
34 | * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, | |
35 | * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF | |
36 | * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. | |
37 | * | |
38 | * You acknowledge that this software is not designed, licensed or | |
39 | * intended for use in the design, construction, operation or maintenance of | |
40 | * any nuclear facility. | |
41 | * | |
42 | * ========== Copyright Header End ============================================ | |
43 | */ | |
44 | /* | |
45 | * id: @(#)cpu-seeprom.c 1.10 05/11/15 | |
46 | * purpose: | |
47 | * copyright: Copyright 2005 Sun Microsystems, Inc. All Rights Reserved | |
48 | * copyright: Use is subject to license terms. | |
49 | */ | |
50 | ||
51 | #include <stdio.h> | |
52 | #include <stdlib.h> | |
53 | #include <strings.h> | |
54 | #include "gen-seeprom.h" | |
55 | #include "cpu-seeprom.h" | |
56 | #include "prototypes.h" | |
57 | ||
58 | #define ULTRA3_MODE 1 /* Ultra3 */ | |
59 | #define ULTRA3_PLUS 2 /* Ultra3+ in 2way E$ mode */ | |
60 | #define ULTRA3_PLUS_DM 3 /* Ultra3+ in direct mapped mode */ | |
61 | ||
62 | int format_mode, id_magic; | |
63 | int clk_div_set = 0; | |
64 | static int speed_ratio_max = 6; | |
65 | static int speed_ratio_min = 4; | |
66 | static int cpu_ratios_min = 3; | |
67 | static int cpu_ratios_bits = 8; /* # of bits in cpu ratios fru parameter */ | |
68 | ||
69 | extern int do_checksum; | |
70 | extern int system_type, type; | |
71 | extern struct fixed_seeprom *seeprom_data; | |
72 | ||
73 | void | |
74 | set_magic_bytes(void *data) | |
75 | { | |
76 | if ((int)data != CPU_DEFAULT_MAGIC) { | |
77 | do_checksum = 1; | |
78 | } | |
79 | id_magic = (int)data; | |
80 | } | |
81 | ||
82 | void | |
83 | set_format_byte(void *data) | |
84 | { | |
85 | format_mode = (int)data; | |
86 | } | |
87 | ||
88 | void | |
89 | set_clk_div_byte(void *data) | |
90 | { | |
91 | clk_div_set = 1; | |
92 | } | |
93 | ||
94 | struct fixed_seeprom cpu_seeprom_data[] = { | |
95 | /* name size type value */ | |
96 | { "id_magic", 2, NUM, 0, set_magic_bytes }, | |
97 | { "id_format", 1, NUM, 0, set_format_byte }, | |
98 | { "cpu_ratios", 1, NUM, 0, NULL }, | |
99 | { "checksum", 2, NUM, 0, NULL }, | |
100 | { "clk_divisor", 1, NUM, 0, set_clk_div_byte }, | |
101 | { "cpu_id", 1, NUM, 0, NULL }, | |
102 | { "dcr", 8, NUM, 0, NULL }, | |
103 | { "lsucr", 8, NUM, 0, NULL }, | |
104 | { "ecache_size", 4, NUM, 0, NULL }, | |
105 | { "stick_divisor", 1, NUM, 0, NULL }, | |
106 | { "speed_table_width", 1, NUM, 0, NULL }, | |
107 | { "num_ecache_cfgs", 1, NUM, 0, NULL }, | |
108 | /* | |
109 | on excal, the next entry is a pad byte. Instead of defining | |
110 | 2 different structs with different names for this byte, we use | |
111 | num_memc_cfgs and just leave it as 0 for excal. | |
112 | */ | |
113 | { "num_memc_cfgs", 1, NUM, 0, NULL }, | |
114 | /* { "pad", 1, NUM, 0, NULL }, */ | |
115 | { NULL, 0, 0, 0, NULL }, | |
116 | }; | |
117 | ||
118 | struct fixed_seeprom gm_cpu_seeprom_data[] = { | |
119 | /* name size type value */ | |
120 | { "id_magic", 2, NUM, 0, set_magic_bytes }, | |
121 | { "id_format", 1, NUM, 0, set_format_byte }, | |
122 | { "cpu_ratios", 1, NUM, 0, NULL }, | |
123 | { "checksum", 2, NUM, 0, NULL }, | |
124 | { "clk_divisor", 1, NUM, 0, set_clk_div_byte }, | |
125 | { "cpu_id", 1, NUM, 0, NULL }, | |
126 | { "dcr", 8, NUM, 0, NULL }, | |
127 | { "lsucr", 8, NUM, 0, NULL }, | |
128 | { "core_config", 8, NUM, 0, NULL }, | |
129 | { "ecache_size", 4, NUM, 0, NULL }, | |
130 | { "stick_divisor", 1, NUM, 0, NULL }, | |
131 | { "speed_table_width", 1, NUM, 0, NULL }, | |
132 | { "num_ecache_cfgs", 1, NUM, 0, NULL }, | |
133 | { "num_memc_cfgs", 1, NUM, 0, NULL }, | |
134 | { NULL, 0, 0, 0, NULL }, | |
135 | }; | |
136 | ||
137 | struct fixed_seeprom serrano_cpu_seeprom_data[] = { | |
138 | /* name size type value */ | |
139 | { "id_magic", 2, NUM, 0, set_magic_bytes }, | |
140 | { "id_format", 1, NUM, 0, set_format_byte }, | |
141 | { "cpu_id", 1, NUM, 0, NULL }, | |
142 | { "checksum", 2, NUM, 0, NULL }, | |
143 | { "cpu_ratios", 2, NUM, 0, NULL }, | |
144 | { "dcr", 8, NUM, 0, NULL }, | |
145 | { "lsucr", 8, NUM, 0, NULL }, | |
146 | { "ecache_size", 4, NUM, 0, NULL }, | |
147 | { "clk_divisor", 1, NUM, 0, set_clk_div_byte }, | |
148 | { "stick_divisor", 1, NUM, 0, NULL }, | |
149 | { "speed_table_width", 1, NUM, 0, NULL }, | |
150 | { "num_ecache_cfgs", 1, NUM, 0, NULL }, | |
151 | { "num_memc_cfgs", 1, NUM, 0, NULL }, | |
152 | { "pad", 7, NUM, 0, NULL }, | |
153 | { NULL, 0, 0, 0, NULL }, | |
154 | }; | |
155 | ||
156 | struct fixed_seeprom cpu_rw_data[] = { | |
157 | /* name size type value */ | |
158 | { "id_magic", 2, NUM, 0, NULL }, | |
159 | { "id_format", 1, NUM, 0, set_format_byte }, | |
160 | { "num_memc_cfgs", 1, NUM, 0, NULL }, | |
161 | { NULL, 0, 0, 0, NULL }, | |
162 | }; | |
163 | ||
164 | ||
165 | data_reg dcr[] = { | |
166 | /* field pos size */ | |
167 | { "obs", 6, 3 }, | |
168 | { "bpe", 5, 1 }, | |
169 | { "rpe", 4, 1 }, | |
170 | { "si", 3, 1 }, | |
171 | { "ms", 0, 1 }, | |
172 | { NULL, 0, 0 }, | |
173 | }; | |
174 | ||
175 | data_reg lsucr[] = { | |
176 | /* field pos size */ | |
177 | { "cp", 49, 1 }, | |
178 | { "cv", 48, 1 }, | |
179 | { "me", 47, 1 }, | |
180 | { "re", 46, 1 }, | |
181 | { "pe", 45, 1 }, | |
182 | { "hpe", 44, 1 }, | |
183 | { "spe", 43, 1 }, | |
184 | { "sl", 42, 1 }, | |
185 | { "we", 41, 1 }, | |
186 | { "pm", 33, 8 }, | |
187 | { "vm", 25, 8 }, | |
188 | { "pr", 24, 1 }, | |
189 | { "pw", 23, 1 }, | |
190 | { "vr", 22, 1 }, | |
191 | { "vw", 21, 1 }, | |
192 | { "fm", 4, 16 }, | |
193 | { "dm", 3, 1 }, | |
194 | { "im", 2, 1 }, | |
195 | { "dc", 1, 1 }, | |
196 | { "ic", 0, 1 }, | |
197 | { NULL, 0, 0 }, | |
198 | }; | |
199 | ||
200 | static struct ecache_entry *cpu_ecache_table = NULL; | |
201 | static struct speed_entry *speed_table = NULL; | |
202 | static struct fiesta_memc_entry *fiesta_memc = NULL; | |
203 | ||
204 | static int | |
205 | calculate_mspd(unsigned short mclk, unsigned long long mcr2, | |
206 | unsigned int cpuspeed) | |
207 | { | |
208 | ||
209 | #define MEM_SPEED(a, b) ((cpuspeed * a) / b) | |
210 | ||
211 | int rc = NO_ERROR; | |
212 | unsigned int mspeed, mcr2_ratio; | |
213 | ||
214 | if (system_type == SERRANO) { | |
215 | mcr2_ratio = mcr2 & 0x1f; | |
216 | } else { | |
217 | mcr2_ratio = mcr2 & 0xf; | |
218 | } | |
219 | switch (mcr2_ratio) { | |
220 | case 0: | |
221 | mspeed = MEM_SPEED(1, 8); | |
222 | break; | |
223 | case 2: | |
224 | mspeed = MEM_SPEED(1, 9); | |
225 | break; | |
226 | case 4: | |
227 | mspeed = MEM_SPEED(1, 10); | |
228 | break; | |
229 | case 5: | |
230 | mspeed = MEM_SPEED(2, 21); | |
231 | break; | |
232 | case 6: | |
233 | mspeed = MEM_SPEED(1, 11); | |
234 | break; | |
235 | case 8: | |
236 | mspeed = MEM_SPEED(1, 12); | |
237 | break; | |
238 | case 9: | |
239 | mspeed = MEM_SPEED(2, 25); | |
240 | break; | |
241 | case 0xa: | |
242 | mspeed = MEM_SPEED(1, 13); | |
243 | break; | |
244 | case 0xb: | |
245 | mspeed = MEM_SPEED(2, 27); | |
246 | break; | |
247 | case 0xc: | |
248 | mspeed = MEM_SPEED(1, 14); | |
249 | break; | |
250 | case 0xe: | |
251 | mspeed = MEM_SPEED(1, 15); | |
252 | break; | |
253 | /* following entries with bit 4 set are Serrano only */ | |
254 | case 0x10: | |
255 | mspeed = MEM_SPEED(1, 16); | |
256 | break; | |
257 | case 0x11: | |
258 | mspeed = MEM_SPEED(2, 33); | |
259 | break; | |
260 | case 0x13: | |
261 | mspeed = MEM_SPEED(2, 35); | |
262 | break; | |
263 | case 0x14: | |
264 | mspeed = MEM_SPEED(1, 18); | |
265 | break; | |
266 | default: | |
267 | sprintf(err_string, | |
268 | "memc_entry: Invalid CPU_SDRAM clk ratio %x", | |
269 | mcr2_ratio); | |
270 | rc = ERROR; | |
271 | break; | |
272 | } | |
273 | if (mspeed > mclk) { | |
274 | sprintf(err_string, | |
275 | "memc_entry: calculated mem speed %d exceeds mclk %hd", | |
276 | mspeed, mclk); | |
277 | rc = ERROR; | |
278 | } | |
279 | return (rc); | |
280 | } | |
281 | ||
282 | /* | |
283 | * MCR0_CTL_MASK is used to check if following control bits are set | |
284 | * in the memc_entries, they should be 0: | |
285 | * X4DIMM, Addr_Gen, DIMMX_bankY, AUTO_REFRESH_EN, CKE_EN, CLK_UPDATE, | |
286 | * PRECHG_ALL, SET_MODE_REG | |
287 | */ | |
288 | ||
289 | #define MCR0_CTL_MASK -1 ^ 0x0ffffb03ff803fffLL | |
290 | ||
291 | /* | |
292 | * Get fiesta_memc_entries parameters of format: | |
293 | * memc_entry bus_max(Mhz) bus_min(Mhz) ratio mspeed mcr0 mcr1 mcr2 | |
294 | */ | |
295 | ||
296 | static unsigned int | |
297 | fiesta_memc_input(char *line, struct fiesta_memc_entry *entry) | |
298 | { | |
299 | char parameter[MAXNAMESIZE]; | |
300 | unsigned short bus_hi, bus_lo, entry_id, ratio, mclk; | |
301 | unsigned long long mcr[3]; | |
302 | int i, entries; | |
303 | ||
304 | for (i = 0; i < 3; i++) | |
305 | mcr[i] = 0; | |
306 | ||
307 | entries = sscanf(line, "%32s %hd %hd %hd %hd %hd %llx %llx %llx", | |
308 | parameter, &bus_hi, &bus_lo, &entry_id, &ratio, &mclk, &mcr[0], | |
309 | &mcr[1], &mcr[2]); | |
310 | if ((entries != 9)) { | |
311 | print_error("memc_entry line incorrectly defined"); | |
312 | return (ERROR); | |
313 | } | |
314 | if (bus_hi < bus_lo) { | |
315 | print_error("memc_entry.bus_hi must be greater than bus_lo!"); | |
316 | return (ERROR); | |
317 | } | |
318 | if (entry_id > FIESTA_MAX_MEMC) { | |
319 | sprintf(err_string, "memc_entry: id cannot exceed %d", | |
320 | FIESTA_MAX_MEMC); | |
321 | print_error(err_string); | |
322 | return (ERROR); | |
323 | } | |
324 | ||
325 | entry->bus_hi = bus_hi; | |
326 | entry->bus_lo = bus_lo; | |
327 | entry->entry_id = entry_id; | |
328 | entry->ratio = ratio; | |
329 | ||
330 | if (calculate_mspd(mclk, mcr[1], bus_hi * ratio)) { | |
331 | print_error(err_string); | |
332 | sprintf(err_string, "Error in memc_entry %d", entry_id); | |
333 | print_error(err_string); | |
334 | return (ERROR); | |
335 | } else { | |
336 | entry->memclk = mclk; | |
337 | } | |
338 | if (system_type != SERRANO && mclk != 133) { | |
339 | print_error("memc_entry.mspd must be 133 only !"); | |
340 | return (ERROR); | |
341 | } | |
342 | if (mcr[0] & ((unsigned long long) MCR0_CTL_MASK)) { | |
343 | sprintf(err_string, "memc_entry id %d: " | |
344 | "MCR0 control bits not zero. " | |
345 | "Should be %llx\n", entry_id, | |
346 | mcr[0] & (-1 ^ ((unsigned long long) MCR0_CTL_MASK))); | |
347 | print_error(err_string); | |
348 | return (ERROR); | |
349 | } | |
350 | for (i = 0; i < 3; i++) { | |
351 | entry->mcr[i] = mcr[i]; | |
352 | } | |
353 | entry->next = NULL; | |
354 | return (NO_ERROR); | |
355 | } | |
356 | ||
357 | /* | |
358 | * Get ecache_ctrl_reg parameters of format: | |
359 | * ecache_ctrl_reg cpu_max(Mhz) cpu_min(Mhz) ecache_ctrl_reg_default | |
360 | */ | |
361 | static unsigned int | |
362 | get_ecache_input(char *line, struct ecache_entry *entry) | |
363 | { | |
364 | char parameter[MAXNAMESIZE]; | |
365 | unsigned short cpu_max, cpu_min; | |
366 | unsigned long long ecache[2]; | |
367 | int entries; | |
368 | ||
369 | ecache[0] = 0; | |
370 | ecache[1] = 0; | |
371 | entries = sscanf(line, "%32s %hd %hd %llx %llx", | |
372 | parameter, &cpu_max, &cpu_min, &ecache[0], &ecache[1]); | |
373 | if ((entries < 4) || (entries > 5)) { | |
374 | print_error("ecache_ctrl.reg line incorrectly defined"); | |
375 | return (0); | |
376 | } | |
377 | if (cpu_max < cpu_min) { | |
378 | print_error("ecache_ctrl.reg.cpu_max must be greater" | |
379 | "than cpu_min!"); | |
380 | return (0); | |
381 | } | |
382 | entry->cpu_max = cpu_max; | |
383 | entry->cpu_min = cpu_min; | |
384 | entry->ecache_cfg[0] = ecache[0]; | |
385 | if (entries == 5) entry->ecache_cfg[1] = ecache[1]; | |
386 | entry->next = NULL; | |
387 | return (1); | |
388 | } | |
389 | ||
390 | /* | |
391 | * Add fiesta_memc_entry to data structure | |
392 | */ | |
393 | static void | |
394 | update_fiesta_memc(struct fiesta_memc_entry *entry) | |
395 | { | |
396 | struct fiesta_memc_entry *ptr; | |
397 | unsigned long long temp; | |
398 | ||
399 | ptr = fiesta_memc; | |
400 | if (ptr == NULL) { | |
401 | fiesta_memc = malloc(sizeof (struct fiesta_memc_entry)); | |
402 | *fiesta_memc = *entry; | |
403 | } else { | |
404 | while (ptr->next != NULL) | |
405 | ptr = ptr->next; | |
406 | ptr->next = malloc(sizeof (struct fiesta_memc_entry)); | |
407 | *ptr->next = *entry; | |
408 | } | |
409 | temp = get_seeprom("num_memc_cfgs"); | |
410 | update_seeprom("num_memc_cfgs", temp+1); | |
411 | } | |
412 | ||
413 | /* | |
414 | * Add ecache_ctrl_reg entry to data structure | |
415 | */ | |
416 | static void | |
417 | update_ecache_table(struct ecache_entry *entry) | |
418 | { | |
419 | struct ecache_entry *ptr; | |
420 | unsigned long long temp; | |
421 | ||
422 | ptr = cpu_ecache_table; | |
423 | if (ptr == NULL) { | |
424 | cpu_ecache_table = malloc(sizeof (struct ecache_entry)); | |
425 | *cpu_ecache_table = *entry; | |
426 | } else { | |
427 | while (ptr->next != NULL) | |
428 | ptr = ptr->next; | |
429 | ptr->next = malloc(sizeof (struct ecache_entry)); | |
430 | *ptr->next = *entry; | |
431 | } | |
432 | temp = get_seeprom("num_ecache_cfgs"); | |
433 | update_seeprom("num_ecache_cfgs", temp+1); | |
434 | } | |
435 | ||
436 | /* | |
437 | * Remove duplicate speed entries and sort from lowest to highest | |
438 | */ | |
439 | static int | |
440 | sort_speed(unsigned short *speed_list, int num_speeds) | |
441 | { | |
442 | unsigned short sort_list[MAXSPEEDLIST]; | |
443 | int i, j, done; | |
444 | unsigned short lowest; | |
445 | ||
446 | if (num_speeds == 1) { | |
447 | if (speed_list[0] > MAX_SAFARI_SPEED) { | |
448 | sprintf(err_string, | |
449 | "cpu_sys_ratio.safari_speed must be <= %dMhz!", | |
450 | MAX_SAFARI_SPEED); | |
451 | print_error(err_string); | |
452 | return (-1); | |
453 | } else | |
454 | return (1); | |
455 | } | |
456 | ||
457 | i = done = 0; | |
458 | while (!done) { | |
459 | done = 1; | |
460 | lowest = MAX_SAFARI_SPEED; | |
461 | for (j = 0; j < num_speeds; j++) { | |
462 | if (speed_list[j] != 0) { | |
463 | if (speed_list[j] > MAX_SAFARI_SPEED) { | |
464 | sprintf(err_string, | |
465 | "cpu_sys_ratio.safari_speed " | |
466 | "must be <= %dMhz!", | |
467 | MAX_SAFARI_SPEED); | |
468 | print_error(err_string); | |
469 | return (-1); | |
470 | } else { | |
471 | lowest = (lowest <= speed_list[j]) ? | |
472 | lowest : speed_list[j]; | |
473 | done = 0; | |
474 | } | |
475 | } | |
476 | } | |
477 | if (!done) { | |
478 | sort_list[i++] = lowest; | |
479 | for (j = 0; j < num_speeds; j++) { | |
480 | if (speed_list[j] == lowest) { | |
481 | speed_list[j] = 0; | |
482 | } | |
483 | } | |
484 | } | |
485 | } | |
486 | for (j = 0; j < i; j++) | |
487 | speed_list[j] = sort_list[j]; | |
488 | return (i); | |
489 | } | |
490 | ||
491 | /* | |
492 | * Update data structure with new speed entry | |
493 | */ | |
494 | static void | |
495 | update_speeds(unsigned short *speeds, unsigned short ratio, int num_speeds) | |
496 | { | |
497 | struct speed_entry *ptr, *prev, *temp_ptr; | |
498 | unsigned long long cpu_ratios; | |
499 | ||
500 | int i; | |
501 | ||
502 | /* | |
503 | * if we already have entries for this ratio, delete them | |
504 | */ | |
505 | if (get_seeprom("cpu_ratios") & (1 << (ratio - cpu_ratios_min))) { | |
506 | ptr = prev = speed_table; | |
507 | while (ptr->ratio != ratio) { | |
508 | prev = ptr; | |
509 | ptr = ptr->next; | |
510 | } | |
511 | while ((ptr != NULL) && (ptr->ratio == ratio)) { | |
512 | temp_ptr = ptr; | |
513 | ptr = ptr->next; | |
514 | free(temp_ptr); | |
515 | } | |
516 | if (prev == speed_table) | |
517 | speed_table = ptr; | |
518 | else | |
519 | prev->next = ptr; | |
520 | } | |
521 | ||
522 | /* | |
523 | * add speeds for this ratio to the list | |
524 | */ | |
525 | ||
526 | for (i = 0; i < num_speeds; i++) { | |
527 | temp_ptr = malloc(sizeof (struct speed_entry)); | |
528 | temp_ptr->ratio = ratio; | |
529 | temp_ptr->speed_hi = temp_ptr->speed_lo = speeds[i]; | |
530 | temp_ptr->next = NULL; | |
531 | if (speed_table == NULL) { | |
532 | speed_table = temp_ptr; | |
533 | } else { | |
534 | ptr = speed_table; | |
535 | while (ptr->next != NULL) { | |
536 | ptr = ptr->next; | |
537 | } | |
538 | ptr->next = temp_ptr; | |
539 | } | |
540 | } | |
541 | cpu_ratios = get_seeprom("cpu_ratios"); | |
542 | cpu_ratios |= (1 << (ratio - cpu_ratios_min)); | |
543 | update_seeprom("cpu_ratios", cpu_ratios); | |
544 | } | |
545 | ||
546 | /* | |
547 | Get cpu_sys_ratio parameters of format: | |
548 | cpu_sys_ratio cpu/sys_ratio Valid_Safari_speed(Mhz) | |
549 | ||
550 | Multiple valid Safari speeds may be separated by commas. | |
551 | */ | |
552 | static unsigned int | |
553 | get_speed_input(char *line, unsigned short *speeds, | |
554 | unsigned short *ratio, int *num_speeds) | |
555 | { | |
556 | char parameter[MAXNAMESIZE], speedinfo[MAXLINE]; | |
557 | char *temp; | |
558 | int i; | |
559 | ||
560 | if (sscanf(line, "%32s %hd %s", parameter, ratio, speedinfo) == 3) { | |
561 | if ((*ratio > speed_ratio_max) || (*ratio < speed_ratio_min)) { | |
562 | sprintf(err_string, "Invalid cpu_sys_ratio: %d", | |
563 | *ratio); | |
564 | print_error(err_string); | |
565 | return (0); | |
566 | } | |
567 | temp = strtok(speedinfo, ","); | |
568 | i = 0; | |
569 | while (temp != NULL) { | |
570 | if (sscanf(temp, "%hd", &speeds[i++])) { | |
571 | temp = strtok(NULL, ","); | |
572 | } else { | |
573 | sprintf(err_string, | |
574 | "Invalid cpu_sys_ratio.safari_speed: %s", | |
575 | temp); | |
576 | print_error(err_string); | |
577 | return (0); | |
578 | } | |
579 | } | |
580 | *num_speeds = sort_speed(speeds, i); | |
581 | if (*num_speeds == -1) | |
582 | return (0); | |
583 | else | |
584 | return (1); | |
585 | } | |
586 | return (0); | |
587 | } | |
588 | ||
589 | /* | |
590 | Calculate speed table width | |
591 | */ | |
592 | static unsigned long long | |
593 | speed_table_width(void) | |
594 | { | |
595 | struct speed_entry *ptr; | |
596 | unsigned short count, biggest; | |
597 | ||
598 | ptr = speed_table; | |
599 | biggest = count = 1; | |
600 | while (ptr->next != NULL) { | |
601 | if (ptr->ratio == ptr->next->ratio) { | |
602 | count++; | |
603 | if (count > biggest) biggest = count; | |
604 | } else { | |
605 | count = 1; | |
606 | } | |
607 | ptr = ptr->next; | |
608 | } | |
609 | return (biggest); | |
610 | } | |
611 | ||
612 | int | |
613 | cpu_dynamic(char *parameter, char *line) | |
614 | { | |
615 | if (strcmp(parameter, "ecache_ctrl_reg") == 0) { | |
616 | if (system_type != GMFIESTA) { | |
617 | struct ecache_entry new_ecache_entry; | |
618 | if (get_ecache_input(line, &new_ecache_entry)) | |
619 | update_ecache_table(&new_ecache_entry); | |
620 | else | |
621 | return (ERROR); | |
622 | } | |
623 | } else if (strcmp(parameter, "cpu_sys_ratio") == 0) { | |
624 | unsigned short speed_list[MAXSPEEDLIST]; | |
625 | unsigned short ratio; | |
626 | int num_speeds; | |
627 | switch (system_type) { | |
628 | case FIESTA: | |
629 | case GMFIESTA: | |
630 | speed_ratio_max = 10; | |
631 | speed_ratio_min = 6; | |
632 | cpu_ratios_min = 3; | |
633 | cpu_ratios_bits = 8; | |
634 | break; | |
635 | case SERRANO: | |
636 | speed_ratio_max = 16; | |
637 | speed_ratio_min = 6; | |
638 | cpu_ratios_min = 6; | |
639 | cpu_ratios_bits = 16; | |
640 | break; | |
641 | default: | |
642 | speed_ratio_max = 10; | |
643 | speed_ratio_min = 4; | |
644 | cpu_ratios_min = 3; | |
645 | cpu_ratios_bits = 8; | |
646 | break; | |
647 | } | |
648 | if (get_speed_input(line, speed_list, &ratio, &num_speeds)) | |
649 | update_speeds(speed_list, ratio, num_speeds); | |
650 | else | |
651 | return (ERROR); | |
652 | } else if (strcmp(parameter, "memc_entry") == 0) { | |
653 | switch (system_type) { | |
654 | struct fiesta_memc_entry new_memc_entry; | |
655 | case FIESTA: | |
656 | case GMFIESTA: | |
657 | case SERRANO: | |
658 | if (!fiesta_memc_input(line, &new_memc_entry)) | |
659 | update_fiesta_memc(&new_memc_entry); | |
660 | else | |
661 | return (ERROR); | |
662 | default: | |
663 | break; | |
664 | } | |
665 | } else { | |
666 | return (UNKNOWN); | |
667 | } | |
668 | return (NO_ERROR); | |
669 | } | |
670 | ||
671 | static void | |
672 | free_cpu(void) | |
673 | { | |
674 | struct ecache_entry *ecache, *tmp1; | |
675 | struct speed_entry *speed, *tmp2; | |
676 | struct fiesta_memc_entry *memc, *tmp3; | |
677 | ||
678 | if (cpu_ecache_table != NULL) { | |
679 | ecache = cpu_ecache_table; | |
680 | while (ecache->next != NULL) { | |
681 | tmp1 = ecache->next; | |
682 | free(ecache); | |
683 | ecache = tmp1; | |
684 | } | |
685 | free(ecache); | |
686 | } | |
687 | ||
688 | if (speed_table != NULL) { | |
689 | speed = speed_table; | |
690 | while (speed->next != NULL) { | |
691 | tmp2 = speed->next; | |
692 | free(speed); | |
693 | speed = tmp2; | |
694 | } | |
695 | free(speed); | |
696 | } | |
697 | ||
698 | if (fiesta_memc != NULL) { | |
699 | memc = fiesta_memc; | |
700 | while (memc->next != NULL) { | |
701 | tmp3 = memc->next; | |
702 | free(memc); | |
703 | memc = tmp3; | |
704 | } | |
705 | free(memc); | |
706 | } | |
707 | } | |
708 | ||
709 | void | |
710 | write_cpu(unsigned char **ptr) | |
711 | { | |
712 | struct ecache_entry *ecache; | |
713 | struct speed_entry *speed; | |
714 | struct fiesta_memc_entry *memc; | |
715 | unsigned short ratio; | |
716 | int i; | |
717 | ||
718 | ecache = cpu_ecache_table; | |
719 | while (ecache != NULL) { | |
720 | store_bytes(2, (unsigned long long) ecache->cpu_max, ptr); | |
721 | store_bytes(2, (unsigned long long) ecache->cpu_min, ptr); | |
722 | store_bytes(8, ecache->ecache_cfg[0], ptr); | |
723 | if (format_mode == 2 || format_mode == 3) { | |
724 | store_bytes(8, ecache->ecache_cfg[1], ptr); | |
725 | } | |
726 | ecache = ecache->next; | |
727 | } | |
728 | ||
729 | if (fixed_parameter("speed_table_width")) { | |
730 | int width = (int)get_seeprom("speed_table_width"); | |
731 | int found = 0; | |
732 | ||
733 | for (i = 0; i < cpu_ratios_bits; i++, found = 0) { | |
734 | if (get_seeprom("cpu_ratios") & (1 << i)) { | |
735 | ratio = i+ cpu_ratios_min; | |
736 | speed = speed_table; | |
737 | /* | |
738 | * Stay at this ratio until all found and | |
739 | * stored. | |
740 | */ | |
741 | while (speed != NULL) { | |
742 | if (speed->ratio == ratio) { | |
743 | store_bytes(1, speed->speed_hi, | |
744 | ptr); | |
745 | store_bytes(1, speed->speed_lo, | |
746 | ptr); | |
747 | speed->ratio = 0; | |
748 | found++; | |
749 | } | |
750 | speed = speed->next; | |
751 | } | |
752 | while (found++ < width) { | |
753 | store_bytes(2, 0, ptr); | |
754 | } | |
755 | } | |
756 | } | |
757 | } | |
758 | memc = fiesta_memc; | |
759 | while (memc != NULL) { | |
760 | store_bytes(2, (unsigned long long) memc->bus_hi, ptr); | |
761 | store_bytes(2, (unsigned long long) memc->bus_lo, ptr); | |
762 | store_bytes(1, (unsigned long long) memc->entry_id, ptr); | |
763 | store_bytes(1, (unsigned long long) memc->ratio, ptr); | |
764 | store_bytes(2, (unsigned long long) memc->memclk, ptr); | |
765 | for (i = 0; i < 3; i++) | |
766 | store_bytes(8, memc->mcr[i], ptr); | |
767 | memc = memc->next; | |
768 | } | |
769 | free_cpu(); | |
770 | } | |
771 | ||
772 | void | |
773 | dump_cpu(void) | |
774 | { | |
775 | struct ecache_entry *ecache; | |
776 | struct speed_entry *speed; | |
777 | struct fiesta_memc_entry *memc; | |
778 | ||
779 | ecache = cpu_ecache_table; | |
780 | if (ecache != NULL) | |
781 | printf("ecache_ctrl_reg: " | |
782 | "cpu_max cpu_min ecache_cfg\n"); | |
783 | while (ecache != NULL) { | |
784 | printf(" %hd" | |
785 | " %hd 0x%llx", | |
786 | ecache->cpu_max, ecache->cpu_min, ecache->ecache_cfg[0]); | |
787 | if (format_mode == 2 || format_mode == 3) | |
788 | printf(" 0x%llx", ecache->ecache_cfg[1]); | |
789 | printf("\n"); | |
790 | ecache = ecache->next; | |
791 | } | |
792 | ||
793 | speed = speed_table; | |
794 | if (speed != NULL) | |
795 | printf("cpu_sys_ratio: ratio speed\n"); | |
796 | while (speed != NULL) { | |
797 | printf(" %hd %hd\n", | |
798 | speed->ratio, speed->speed_hi); | |
799 | speed = speed->next; | |
800 | } | |
801 | ||
802 | memc = fiesta_memc; | |
803 | if (memc != NULL) { | |
804 | printf("memc_entries:\n"); | |
805 | printf("hi lo id ratio memclk"); | |
806 | printf(" mcr1 mcr2 mcr3\n"); | |
807 | while (memc != NULL) { | |
808 | printf("%-4hd %-4hd %-1hd %-1hd %-4hd", | |
809 | memc->bus_hi, memc->bus_lo, memc->entry_id, | |
810 | memc->ratio, memc->memclk); | |
811 | printf(" 0x%016llx 0x%016llx 0x%016llx\n", | |
812 | memc->mcr[0], memc->mcr[1], memc->mcr[2]); | |
813 | memc = memc->next; | |
814 | } | |
815 | } | |
816 | } | |
817 | ||
818 | int | |
819 | match_ecache_cfg(unsigned short speed) | |
820 | { | |
821 | struct ecache_entry *ecache; | |
822 | ||
823 | ecache = cpu_ecache_table; | |
824 | while (ecache != NULL) { | |
825 | if ((speed >= ecache->cpu_min) && (speed <= ecache->cpu_max)) { | |
826 | return (1); | |
827 | } | |
828 | ecache = ecache->next; | |
829 | } | |
830 | sprintf(err_string, "No ecache cfg entry for cpu speed %d", speed); | |
831 | print_error(err_string); | |
832 | return (0); | |
833 | } | |
834 | ||
835 | static int | |
836 | ecache_fmt(unsigned long long e_size, int format) | |
837 | { | |
838 | struct ecache_entry *ecache; | |
839 | unsigned int ecache_code; | |
840 | int error = NO_ERROR; | |
841 | int size_pos = 0; | |
842 | ||
843 | switch (system_type) { | |
844 | case EXCALIBUR: | |
845 | switch (format) { | |
846 | case 1: | |
847 | case 2: | |
848 | case 3: | |
849 | switch (e_size) { | |
850 | case 0x100000: | |
851 | case 0x400000: | |
852 | case 0x800000: | |
853 | size_pos = 13; | |
854 | ecache_code = e_size / 0x400000; | |
855 | ecache_code <<= size_pos; | |
856 | break; | |
857 | default: | |
858 | error = ERROR; | |
859 | break; | |
860 | } | |
861 | break; | |
862 | default: | |
863 | error = ERROR; | |
864 | break; | |
865 | } | |
866 | break; | |
867 | default: | |
868 | error = ERROR; | |
869 | break; | |
870 | } | |
871 | ||
872 | if (error == NO_ERROR) { | |
873 | ecache = cpu_ecache_table; | |
874 | while (ecache != NULL) { | |
875 | int cfg = ecache->ecache_cfg[0] & | |
876 | (3 << size_pos); | |
877 | if (cfg != ecache_code) { | |
878 | fprintf(stderr, | |
879 | "WARNING: Changing " | |
880 | "ecache_cfg.EC_SIZE " | |
881 | "to match ecache_size!\n"); | |
882 | } | |
883 | ecache->ecache_cfg[0] &= (-1^(3 << size_pos)); | |
884 | ecache->ecache_cfg[0] |= ecache_code; | |
885 | ecache = ecache->next; | |
886 | } | |
887 | } else { | |
888 | printf("Invalid id_format or ecache size!\n"); | |
889 | } | |
890 | return (error); | |
891 | } | |
892 | ||
893 | int | |
894 | match_memc_cfg(unsigned short speed, unsigned short ratio) | |
895 | { | |
896 | struct fiesta_memc_entry *memc_table; | |
897 | ||
898 | memc_table = fiesta_memc; | |
899 | while (memc_table != NULL) { | |
900 | if ((memc_table->bus_hi == speed) && | |
901 | (memc_table->ratio == ratio)) { | |
902 | return (1); | |
903 | } | |
904 | memc_table = memc_table->next; | |
905 | } | |
906 | sprintf(err_string, "No memc cfg entry for ratio %d sys speed %d", | |
907 | ratio, speed); | |
908 | print_error(err_string); | |
909 | return (0); | |
910 | } | |
911 | ||
912 | int | |
913 | check_cpu(void) | |
914 | { | |
915 | int error = 0; | |
916 | struct speed_entry *speed_ptr; | |
917 | unsigned long long temp; | |
918 | ||
919 | if (type == CPU) { | |
920 | if (speed_table == NULL) { | |
921 | print_error("Must specify a cpu_sys_ratio entry!"); | |
922 | error = ERROR; | |
923 | } else { | |
924 | update_seeprom("speed_table_width", | |
925 | speed_table_width()); | |
926 | speed_ptr = speed_table; | |
927 | while (speed_ptr != NULL) { | |
928 | if (system_type != GMFIESTA && | |
929 | !match_ecache_cfg(speed_ptr->speed_hi* | |
930 | speed_ptr->ratio)) | |
931 | error = ERROR; | |
932 | if (system_type == FIESTA || | |
933 | system_type == GMFIESTA || | |
934 | system_type == SERRANO) | |
935 | if (!match_memc_cfg(speed_ptr->speed_hi, | |
936 | speed_ptr->ratio)) | |
937 | error = ERROR; | |
938 | speed_ptr = speed_ptr->next; | |
939 | } | |
940 | } | |
941 | } | |
942 | if (type == CPU_RW && | |
943 | (system_type == FIESTA || system_type == GMFIESTA || | |
944 | system_type == SERRANO)) { | |
945 | if (format_mode != 1) { | |
946 | print_error("Invalid id_format!"); | |
947 | error = ERROR; | |
948 | } | |
949 | } | |
950 | if (fiesta_memc == NULL && | |
951 | (system_type == FIESTA || system_type == GMFIESTA || | |
952 | system_type == SERRANO)) { | |
953 | print_error("Must specify a memc entry!"); | |
954 | error = ERROR; | |
955 | } | |
956 | ||
957 | if (fixed_parameter("ecache_size") && (system_type == EXCALIBUR)) { | |
958 | if (ecache_fmt(get_seeprom("ecache_size"), format_mode)) | |
959 | error = ERROR; | |
960 | } | |
961 | if ((type == CPU) && (id_magic != CPU_DEFAULT_MAGIC)) { | |
962 | if (!clk_div_set) { | |
963 | print_error("Must specify clk_divisor!"); | |
964 | error = ERROR; | |
965 | } | |
966 | } | |
967 | return (error); | |
968 | } | |
969 | ||
970 | int | |
971 | reg_cpu(char *s1, data_reg **reg) | |
972 | { | |
973 | int count = 0; | |
974 | if (strcmp(s1, "lsucr") == 0) { | |
975 | *reg = &lsucr[0]; | |
976 | count = sizeof (lsucr) / sizeof (data_reg); | |
977 | } else if (strcmp(s1, "dcr") == 0) { | |
978 | *reg = &dcr[0]; | |
979 | count = sizeof (dcr) / sizeof (data_reg); | |
980 | } | |
981 | return (count); | |
982 | } |