1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* cpu.c: Dinky routines to look for the kind of Sparc cpu |
3 | * we are on. |
4 | * |
5 | * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu) |
6 | */ |
7 | |
8 | #include <linux/seq_file.h> |
9 | #include <linux/kernel.h> |
10 | #include <linux/export.h> |
11 | #include <linux/init.h> |
12 | #include <linux/smp.h> |
13 | #include <linux/threads.h> |
14 | #include <linux/pgtable.h> |
15 | |
16 | #include <asm/spitfire.h> |
17 | #include <asm/oplib.h> |
18 | #include <asm/setup.h> |
19 | #include <asm/page.h> |
20 | #include <asm/head.h> |
21 | #include <asm/psr.h> |
22 | #include <asm/mbus.h> |
23 | #include <asm/cpudata.h> |
24 | |
25 | #include "kernel.h" |
26 | #include "entry.h" |
27 | |
28 | DEFINE_PER_CPU(cpuinfo_sparc, __cpu_data) = { 0 }; |
29 | EXPORT_PER_CPU_SYMBOL(__cpu_data); |
30 | |
31 | int ncpus_probed; |
32 | unsigned int fsr_storage; |
33 | |
34 | struct cpu_info { |
35 | int psr_vers; |
36 | const char *name; |
37 | const char *pmu_name; |
38 | }; |
39 | |
40 | struct fpu_info { |
41 | int fp_vers; |
42 | const char *name; |
43 | }; |
44 | |
45 | #define NOCPU 8 |
46 | #define NOFPU 8 |
47 | |
48 | struct manufacturer_info { |
49 | int psr_impl; |
50 | struct cpu_info cpu_info[NOCPU]; |
51 | struct fpu_info fpu_info[NOFPU]; |
52 | }; |
53 | |
54 | #define CPU(ver, _name) \ |
55 | { .psr_vers = ver, .name = _name } |
56 | |
57 | #define CPU_PMU(ver, _name, _pmu_name) \ |
58 | { .psr_vers = ver, .name = _name, .pmu_name = _pmu_name } |
59 | |
60 | #define FPU(ver, _name) \ |
61 | { .fp_vers = ver, .name = _name } |
62 | |
63 | static const struct manufacturer_info __initconst manufacturer_info[] = { |
64 | { |
65 | 0, |
66 | /* Sun4/100, 4/200, SLC */ |
67 | .cpu_info = { |
68 | CPU(0, "Fujitsu MB86900/1A or LSI L64831 SparcKIT-40" ), |
69 | /* borned STP1012PGA */ |
70 | CPU(4, "Fujitsu MB86904" ), |
71 | CPU(5, "Fujitsu TurboSparc MB86907" ), |
72 | CPU(-1, NULL) |
73 | }, |
74 | .fpu_info = { |
75 | FPU(0, "Fujitsu MB86910 or Weitek WTL1164/5" ), |
76 | FPU(1, "Fujitsu MB86911 or Weitek WTL1164/5 or LSI L64831" ), |
77 | FPU(2, "LSI Logic L64802 or Texas Instruments ACT8847" ), |
78 | /* SparcStation SLC, SparcStation1 */ |
79 | FPU(3, "Weitek WTL3170/2" ), |
80 | /* SPARCstation-5 */ |
81 | FPU(4, "Lsi Logic/Meiko L64804 or compatible" ), |
82 | FPU(-1, NULL) |
83 | } |
84 | },{ |
85 | 1, |
86 | .cpu_info = { |
87 | /* SparcStation2, SparcServer 490 & 690 */ |
88 | CPU(0, "LSI Logic Corporation - L64811" ), |
89 | /* SparcStation2 */ |
90 | CPU(1, "Cypress/ROSS CY7C601" ), |
91 | /* Embedded controller */ |
92 | CPU(3, "Cypress/ROSS CY7C611" ), |
93 | /* Ross Technologies HyperSparc */ |
94 | CPU(0xf, "ROSS HyperSparc RT620" ), |
95 | CPU(0xe, "ROSS HyperSparc RT625 or RT626" ), |
96 | CPU(-1, NULL) |
97 | }, |
98 | .fpu_info = { |
99 | FPU(0, "ROSS HyperSparc combined IU/FPU" ), |
100 | FPU(1, "Lsi Logic L64814" ), |
101 | FPU(2, "Texas Instruments TMS390-C602A" ), |
102 | FPU(3, "Cypress CY7C602 FPU" ), |
103 | FPU(-1, NULL) |
104 | } |
105 | },{ |
106 | 2, |
107 | .cpu_info = { |
108 | /* ECL Implementation, CRAY S-MP Supercomputer... AIEEE! */ |
109 | /* Someone please write the code to support this beast! ;) */ |
110 | CPU(0, "Bipolar Integrated Technology - B5010" ), |
111 | CPU(-1, NULL) |
112 | }, |
113 | .fpu_info = { |
114 | FPU(-1, NULL) |
115 | } |
116 | },{ |
117 | 3, |
118 | .cpu_info = { |
119 | CPU(0, "LSI Logic Corporation - unknown-type" ), |
120 | CPU(-1, NULL) |
121 | }, |
122 | .fpu_info = { |
123 | FPU(-1, NULL) |
124 | } |
125 | },{ |
126 | PSR_IMPL_TI, |
127 | .cpu_info = { |
128 | CPU(0, "Texas Instruments, Inc. - SuperSparc-(II)" ), |
129 | /* SparcClassic -- borned STP1010TAB-50*/ |
130 | CPU(1, "Texas Instruments, Inc. - MicroSparc" ), |
131 | CPU(2, "Texas Instruments, Inc. - MicroSparc II" ), |
132 | CPU(3, "Texas Instruments, Inc. - SuperSparc 51" ), |
133 | CPU(4, "Texas Instruments, Inc. - SuperSparc 61" ), |
134 | CPU(5, "Texas Instruments, Inc. - unknown" ), |
135 | CPU(-1, NULL) |
136 | }, |
137 | .fpu_info = { |
138 | /* SuperSparc 50 module */ |
139 | FPU(0, "SuperSparc on-chip FPU" ), |
140 | /* SparcClassic */ |
141 | FPU(4, "TI MicroSparc on chip FPU" ), |
142 | FPU(-1, NULL) |
143 | } |
144 | },{ |
145 | 5, |
146 | .cpu_info = { |
147 | CPU(0, "Matsushita - MN10501" ), |
148 | CPU(-1, NULL) |
149 | }, |
150 | .fpu_info = { |
151 | FPU(0, "Matsushita MN10501" ), |
152 | FPU(-1, NULL) |
153 | } |
154 | },{ |
155 | 6, |
156 | .cpu_info = { |
157 | CPU(0, "Philips Corporation - unknown" ), |
158 | CPU(-1, NULL) |
159 | }, |
160 | .fpu_info = { |
161 | FPU(-1, NULL) |
162 | } |
163 | },{ |
164 | 7, |
165 | .cpu_info = { |
166 | CPU(0, "Harvest VLSI Design Center, Inc. - unknown" ), |
167 | CPU(-1, NULL) |
168 | }, |
169 | .fpu_info = { |
170 | FPU(-1, NULL) |
171 | } |
172 | },{ |
173 | 8, |
174 | .cpu_info = { |
175 | CPU(0, "Systems and Processes Engineering Corporation (SPEC)" ), |
176 | CPU(-1, NULL) |
177 | }, |
178 | .fpu_info = { |
179 | FPU(-1, NULL) |
180 | } |
181 | },{ |
182 | 9, |
183 | .cpu_info = { |
184 | /* Gallium arsenide 200MHz, BOOOOGOOOOMIPS!!! */ |
185 | CPU(0, "Fujitsu or Weitek Power-UP" ), |
186 | CPU(1, "Fujitsu or Weitek Power-UP" ), |
187 | CPU(2, "Fujitsu or Weitek Power-UP" ), |
188 | CPU(3, "Fujitsu or Weitek Power-UP" ), |
189 | CPU(-1, NULL) |
190 | }, |
191 | .fpu_info = { |
192 | FPU(3, "Fujitsu or Weitek on-chip FPU" ), |
193 | FPU(-1, NULL) |
194 | } |
195 | },{ |
196 | PSR_IMPL_LEON, /* Aeroflex Gaisler */ |
197 | .cpu_info = { |
198 | CPU(3, "LEON" ), |
199 | CPU(-1, NULL) |
200 | }, |
201 | .fpu_info = { |
202 | FPU(2, "GRFPU" ), |
203 | FPU(3, "GRFPU-Lite" ), |
204 | FPU(-1, NULL) |
205 | } |
206 | },{ |
207 | 0x17, |
208 | .cpu_info = { |
209 | CPU_PMU(0x10, "TI UltraSparc I (SpitFire)" , "ultra12" ), |
210 | CPU_PMU(0x11, "TI UltraSparc II (BlackBird)" , "ultra12" ), |
211 | CPU_PMU(0x12, "TI UltraSparc IIi (Sabre)" , "ultra12" ), |
212 | CPU_PMU(0x13, "TI UltraSparc IIe (Hummingbird)" , "ultra12" ), |
213 | CPU(-1, NULL) |
214 | }, |
215 | .fpu_info = { |
216 | FPU(0x10, "UltraSparc I integrated FPU" ), |
217 | FPU(0x11, "UltraSparc II integrated FPU" ), |
218 | FPU(0x12, "UltraSparc IIi integrated FPU" ), |
219 | FPU(0x13, "UltraSparc IIe integrated FPU" ), |
220 | FPU(-1, NULL) |
221 | } |
222 | },{ |
223 | 0x22, |
224 | .cpu_info = { |
225 | CPU_PMU(0x10, "TI UltraSparc I (SpitFire)" , "ultra12" ), |
226 | CPU(-1, NULL) |
227 | }, |
228 | .fpu_info = { |
229 | FPU(0x10, "UltraSparc I integrated FPU" ), |
230 | FPU(-1, NULL) |
231 | } |
232 | },{ |
233 | 0x3e, |
234 | .cpu_info = { |
235 | CPU_PMU(0x14, "TI UltraSparc III (Cheetah)" , "ultra3" ), |
236 | CPU_PMU(0x15, "TI UltraSparc III+ (Cheetah+)" , "ultra3+" ), |
237 | CPU_PMU(0x16, "TI UltraSparc IIIi (Jalapeno)" , "ultra3i" ), |
238 | CPU_PMU(0x18, "TI UltraSparc IV (Jaguar)" , "ultra3+" ), |
239 | CPU_PMU(0x19, "TI UltraSparc IV+ (Panther)" , "ultra4+" ), |
240 | CPU_PMU(0x22, "TI UltraSparc IIIi+ (Serrano)" , "ultra3i" ), |
241 | CPU(-1, NULL) |
242 | }, |
243 | .fpu_info = { |
244 | FPU(0x14, "UltraSparc III integrated FPU" ), |
245 | FPU(0x15, "UltraSparc III+ integrated FPU" ), |
246 | FPU(0x16, "UltraSparc IIIi integrated FPU" ), |
247 | FPU(0x18, "UltraSparc IV integrated FPU" ), |
248 | FPU(0x19, "UltraSparc IV+ integrated FPU" ), |
249 | FPU(0x22, "UltraSparc IIIi+ integrated FPU" ), |
250 | FPU(-1, NULL) |
251 | } |
252 | }}; |
253 | |
254 | /* In order to get the fpu type correct, you need to take the IDPROM's |
255 | * machine type value into consideration too. I will fix this. |
256 | */ |
257 | |
258 | static const char *sparc_cpu_type; |
259 | static const char *sparc_fpu_type; |
260 | const char *sparc_pmu_type; |
261 | |
262 | |
263 | static void __init set_cpu_and_fpu(int psr_impl, int psr_vers, int fpu_vers) |
264 | { |
265 | const struct manufacturer_info *manuf; |
266 | int i; |
267 | |
268 | sparc_cpu_type = NULL; |
269 | sparc_fpu_type = NULL; |
270 | sparc_pmu_type = NULL; |
271 | manuf = NULL; |
272 | |
273 | for (i = 0; i < ARRAY_SIZE(manufacturer_info); i++) |
274 | { |
275 | if (psr_impl == manufacturer_info[i].psr_impl) { |
276 | manuf = &manufacturer_info[i]; |
277 | break; |
278 | } |
279 | } |
280 | if (manuf != NULL) |
281 | { |
282 | const struct cpu_info *cpu; |
283 | const struct fpu_info *fpu; |
284 | |
285 | cpu = &manuf->cpu_info[0]; |
286 | while (cpu->psr_vers != -1) |
287 | { |
288 | if (cpu->psr_vers == psr_vers) { |
289 | sparc_cpu_type = cpu->name; |
290 | sparc_pmu_type = cpu->pmu_name; |
291 | sparc_fpu_type = "No FPU" ; |
292 | break; |
293 | } |
294 | cpu++; |
295 | } |
296 | fpu = &manuf->fpu_info[0]; |
297 | while (fpu->fp_vers != -1) |
298 | { |
299 | if (fpu->fp_vers == fpu_vers) { |
300 | sparc_fpu_type = fpu->name; |
301 | break; |
302 | } |
303 | fpu++; |
304 | } |
305 | } |
306 | if (sparc_cpu_type == NULL) |
307 | { |
308 | printk(KERN_ERR "CPU: Unknown chip, impl[0x%x] vers[0x%x]\n" , |
309 | psr_impl, psr_vers); |
310 | sparc_cpu_type = "Unknown CPU" ; |
311 | } |
312 | if (sparc_fpu_type == NULL) |
313 | { |
314 | printk(KERN_ERR "FPU: Unknown chip, impl[0x%x] vers[0x%x]\n" , |
315 | psr_impl, fpu_vers); |
316 | sparc_fpu_type = "Unknown FPU" ; |
317 | } |
318 | if (sparc_pmu_type == NULL) |
319 | sparc_pmu_type = "Unknown PMU" ; |
320 | } |
321 | |
322 | #ifdef CONFIG_SPARC32 |
323 | static int show_cpuinfo(struct seq_file *m, void *__unused) |
324 | { |
325 | seq_printf(m, |
326 | "cpu\t\t: %s\n" |
327 | "fpu\t\t: %s\n" |
328 | "promlib\t\t: Version %d Revision %d\n" |
329 | "prom\t\t: %d.%d\n" |
330 | "type\t\t: %s\n" |
331 | "ncpus probed\t: %d\n" |
332 | "ncpus active\t: %d\n" |
333 | #ifndef CONFIG_SMP |
334 | "CPU0Bogo\t: %lu.%02lu\n" |
335 | "CPU0ClkTck\t: %ld\n" |
336 | #endif |
337 | , |
338 | sparc_cpu_type, |
339 | sparc_fpu_type , |
340 | romvec->pv_romvers, |
341 | prom_rev, |
342 | romvec->pv_printrev >> 16, |
343 | romvec->pv_printrev & 0xffff, |
344 | &cputypval[0], |
345 | ncpus_probed, |
346 | num_online_cpus() |
347 | #ifndef CONFIG_SMP |
348 | , cpu_data(0).udelay_val/(500000/HZ), |
349 | (cpu_data(0).udelay_val/(5000/HZ)) % 100, |
350 | cpu_data(0).clock_tick |
351 | #endif |
352 | ); |
353 | |
354 | #ifdef CONFIG_SMP |
355 | smp_bogo(m); |
356 | #endif |
357 | mmu_info(m); |
358 | #ifdef CONFIG_SMP |
359 | smp_info(m); |
360 | #endif |
361 | return 0; |
362 | } |
363 | #endif /* CONFIG_SPARC32 */ |
364 | |
365 | #ifdef CONFIG_SPARC64 |
366 | unsigned int dcache_parity_tl1_occurred; |
367 | unsigned int icache_parity_tl1_occurred; |
368 | |
369 | |
370 | static int show_cpuinfo(struct seq_file *m, void *__unused) |
371 | { |
372 | seq_printf(m, |
373 | "cpu\t\t: %s\n" |
374 | "fpu\t\t: %s\n" |
375 | "pmu\t\t: %s\n" |
376 | "prom\t\t: %s\n" |
377 | "type\t\t: %s\n" |
378 | "ncpus probed\t: %d\n" |
379 | "ncpus active\t: %d\n" |
380 | "D$ parity tl1\t: %u\n" |
381 | "I$ parity tl1\t: %u\n" |
382 | #ifndef CONFIG_SMP |
383 | "Cpu0ClkTck\t: %016lx\n" |
384 | #endif |
385 | , |
386 | sparc_cpu_type, |
387 | sparc_fpu_type, |
388 | sparc_pmu_type, |
389 | prom_version, |
390 | ((tlb_type == hypervisor) ? |
391 | "sun4v" : |
392 | "sun4u" ), |
393 | ncpus_probed, |
394 | num_online_cpus(), |
395 | dcache_parity_tl1_occurred, |
396 | icache_parity_tl1_occurred |
397 | #ifndef CONFIG_SMP |
398 | , cpu_data(0).clock_tick |
399 | #endif |
400 | ); |
401 | cpucap_info(m); |
402 | #ifdef CONFIG_SMP |
403 | smp_bogo(m); |
404 | #endif |
405 | mmu_info(m); |
406 | #ifdef CONFIG_SMP |
407 | smp_info(m); |
408 | #endif |
409 | return 0; |
410 | } |
411 | #endif /* CONFIG_SPARC64 */ |
412 | |
413 | static void *c_start(struct seq_file *m, loff_t *pos) |
414 | { |
415 | /* The pointer we are returning is arbitrary, |
416 | * it just has to be non-NULL and not IS_ERR |
417 | * in the success case. |
418 | */ |
419 | return *pos == 0 ? &c_start : NULL; |
420 | } |
421 | |
422 | static void *c_next(struct seq_file *m, void *v, loff_t *pos) |
423 | { |
424 | ++*pos; |
425 | return c_start(m, pos); |
426 | } |
427 | |
428 | static void c_stop(struct seq_file *m, void *v) |
429 | { |
430 | } |
431 | |
432 | const struct seq_operations cpuinfo_op = { |
433 | .start =c_start, |
434 | .next = c_next, |
435 | .stop = c_stop, |
436 | .show = show_cpuinfo, |
437 | }; |
438 | |
439 | #ifdef CONFIG_SPARC32 |
440 | static int __init cpu_type_probe(void) |
441 | { |
442 | int psr_impl, psr_vers, fpu_vers; |
443 | int psr; |
444 | |
445 | psr_impl = ((get_psr() >> PSR_IMPL_SHIFT) & PSR_IMPL_SHIFTED_MASK); |
446 | psr_vers = ((get_psr() >> PSR_VERS_SHIFT) & PSR_VERS_SHIFTED_MASK); |
447 | |
448 | psr = get_psr(); |
449 | put_psr(psr | PSR_EF); |
450 | |
451 | if (psr_impl == PSR_IMPL_LEON) |
452 | fpu_vers = get_psr() & PSR_EF ? ((get_fsr() >> 17) & 0x7) : 7; |
453 | else |
454 | fpu_vers = ((get_fsr() >> 17) & 0x7); |
455 | |
456 | put_psr(psr); |
457 | |
458 | set_cpu_and_fpu(psr_impl, psr_vers, fpu_vers); |
459 | |
460 | return 0; |
461 | } |
462 | #endif /* CONFIG_SPARC32 */ |
463 | |
464 | #ifdef CONFIG_SPARC64 |
465 | static void __init sun4v_cpu_probe(void) |
466 | { |
467 | switch (sun4v_chip_type) { |
468 | case SUN4V_CHIP_NIAGARA1: |
469 | sparc_cpu_type = "UltraSparc T1 (Niagara)" ; |
470 | sparc_fpu_type = "UltraSparc T1 integrated FPU" ; |
471 | sparc_pmu_type = "niagara" ; |
472 | break; |
473 | |
474 | case SUN4V_CHIP_NIAGARA2: |
475 | sparc_cpu_type = "UltraSparc T2 (Niagara2)" ; |
476 | sparc_fpu_type = "UltraSparc T2 integrated FPU" ; |
477 | sparc_pmu_type = "niagara2" ; |
478 | break; |
479 | |
480 | case SUN4V_CHIP_NIAGARA3: |
481 | sparc_cpu_type = "UltraSparc T3 (Niagara3)" ; |
482 | sparc_fpu_type = "UltraSparc T3 integrated FPU" ; |
483 | sparc_pmu_type = "niagara3" ; |
484 | break; |
485 | |
486 | case SUN4V_CHIP_NIAGARA4: |
487 | sparc_cpu_type = "UltraSparc T4 (Niagara4)" ; |
488 | sparc_fpu_type = "UltraSparc T4 integrated FPU" ; |
489 | sparc_pmu_type = "niagara4" ; |
490 | break; |
491 | |
492 | case SUN4V_CHIP_NIAGARA5: |
493 | sparc_cpu_type = "UltraSparc T5 (Niagara5)" ; |
494 | sparc_fpu_type = "UltraSparc T5 integrated FPU" ; |
495 | sparc_pmu_type = "niagara5" ; |
496 | break; |
497 | |
498 | case SUN4V_CHIP_SPARC_M6: |
499 | sparc_cpu_type = "SPARC-M6" ; |
500 | sparc_fpu_type = "SPARC-M6 integrated FPU" ; |
501 | sparc_pmu_type = "sparc-m6" ; |
502 | break; |
503 | |
504 | case SUN4V_CHIP_SPARC_M7: |
505 | sparc_cpu_type = "SPARC-M7" ; |
506 | sparc_fpu_type = "SPARC-M7 integrated FPU" ; |
507 | sparc_pmu_type = "sparc-m7" ; |
508 | break; |
509 | |
510 | case SUN4V_CHIP_SPARC_M8: |
511 | sparc_cpu_type = "SPARC-M8" ; |
512 | sparc_fpu_type = "SPARC-M8 integrated FPU" ; |
513 | sparc_pmu_type = "sparc-m8" ; |
514 | break; |
515 | |
516 | case SUN4V_CHIP_SPARC_SN: |
517 | sparc_cpu_type = "SPARC-SN" ; |
518 | sparc_fpu_type = "SPARC-SN integrated FPU" ; |
519 | sparc_pmu_type = "sparc-sn" ; |
520 | break; |
521 | |
522 | case SUN4V_CHIP_SPARC64X: |
523 | sparc_cpu_type = "SPARC64-X" ; |
524 | sparc_fpu_type = "SPARC64-X integrated FPU" ; |
525 | sparc_pmu_type = "sparc64-x" ; |
526 | break; |
527 | |
528 | default: |
529 | printk(KERN_WARNING "CPU: Unknown sun4v cpu type [%s]\n" , |
530 | prom_cpu_compatible); |
531 | sparc_cpu_type = "Unknown SUN4V CPU" ; |
532 | sparc_fpu_type = "Unknown SUN4V FPU" ; |
533 | sparc_pmu_type = "Unknown SUN4V PMU" ; |
534 | break; |
535 | } |
536 | } |
537 | |
538 | static int __init cpu_type_probe(void) |
539 | { |
540 | if (tlb_type == hypervisor) { |
541 | sun4v_cpu_probe(); |
542 | } else { |
543 | unsigned long ver; |
544 | int manuf, impl; |
545 | |
546 | __asm__ __volatile__("rdpr %%ver, %0" : "=r" (ver)); |
547 | |
548 | manuf = ((ver >> 48) & 0xffff); |
549 | impl = ((ver >> 32) & 0xffff); |
550 | set_cpu_and_fpu(manuf, impl, impl); |
551 | } |
552 | return 0; |
553 | } |
554 | #endif /* CONFIG_SPARC64 */ |
555 | |
556 | early_initcall(cpu_type_probe); |
557 | |