Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 | // SPDX-License-Identifier: GPL-2.0-or-later /* * Processor capabilities determination functions. * * Copyright (C) xxxx the Anonymous * Copyright (C) 1994 - 2006 Ralf Baechle * Copyright (C) 2003, 2004 Maciej W. Rozycki * Copyright (C) 2001, 2004, 2011, 2012 MIPS Technologies, Inc. */ #include <linux/init.h> #include <linux/kernel.h> #include <linux/ptrace.h> #include <linux/smp.h> #include <linux/stddef.h> #include <linux/export.h> #include <asm/bugs.h> #include <asm/cpu.h> #include <asm/cpu-features.h> #include <asm/cpu-type.h> #include <asm/fpu.h> #include <asm/mipsregs.h> #include <asm/elf.h> #include <asm/traps.h> #include "fpu-probe.h" /* Hardware capabilities */ unsigned int elf_hwcap __read_mostly; EXPORT_SYMBOL_GPL(elf_hwcap); void __init check_bugs32(void) { } /* * Probe whether cpu has config register by trying to play with * alternate cache bit and see whether it matters. * It's used by cpu_probe to distinguish between R3000A and R3081. */ static inline int cpu_has_confreg(void) { #ifdef CONFIG_CPU_R3000 extern unsigned long r3k_cache_size(unsigned long); unsigned long size1, size2; unsigned long cfg = read_c0_conf(); size1 = r3k_cache_size(ST0_ISC); write_c0_conf(cfg ^ R30XX_CONF_AC); size2 = r3k_cache_size(ST0_ISC); write_c0_conf(cfg); return size1 != size2; #else return 0; #endif } static inline void set_elf_platform(int cpu, const char *plat) { if (cpu == 0) __elf_platform = plat; } const char *__cpu_name[NR_CPUS]; const char *__elf_platform; const char *__elf_base_platform; void cpu_probe(void) { struct cpuinfo_mips *c = ¤t_cpu_data; unsigned int cpu = smp_processor_id(); /* * Set a default elf platform, cpu probe may later * overwrite it with a more precise value */ set_elf_platform(cpu, "mips"); c->processor_id = PRID_IMP_UNKNOWN; c->fpu_id = FPIR_IMP_NONE; c->cputype = CPU_UNKNOWN; c->writecombine = _CACHE_UNCACHED; c->fpu_csr31 = FPU_CSR_RN; c->fpu_msk31 = FPU_CSR_RSVD | FPU_CSR_ABS2008 | FPU_CSR_NAN2008 | FPU_CSR_CONDX | FPU_CSR_FS; c->srsets = 1; c->processor_id = read_c0_prid(); switch (c->processor_id & (PRID_COMP_MASK | PRID_IMP_MASK)) { case PRID_COMP_LEGACY | PRID_IMP_R2000: c->cputype = CPU_R2000; __cpu_name[cpu] = "R2000"; c->options = MIPS_CPU_TLB | MIPS_CPU_3K_CACHE | MIPS_CPU_NOFPUEX; if (__cpu_has_fpu()) c->options |= MIPS_CPU_FPU; c->tlbsize = 64; break; case PRID_COMP_LEGACY | PRID_IMP_R3000: if ((c->processor_id & PRID_REV_MASK) == PRID_REV_R3000A) { if (cpu_has_confreg()) { c->cputype = CPU_R3081E; __cpu_name[cpu] = "R3081"; } else { c->cputype = CPU_R3000A; __cpu_name[cpu] = "R3000A"; } } else { c->cputype = CPU_R3000; __cpu_name[cpu] = "R3000"; } c->options = MIPS_CPU_TLB | MIPS_CPU_3K_CACHE | MIPS_CPU_NOFPUEX; if (__cpu_has_fpu()) c->options |= MIPS_CPU_FPU; c->tlbsize = 64; break; } BUG_ON(!__cpu_name[cpu]); BUG_ON(c->cputype == CPU_UNKNOWN); /* * Platform code can force the cpu type to optimize code * generation. In that case be sure the cpu type is correctly * manually setup otherwise it could trigger some nasty bugs. */ BUG_ON(current_cpu_type() != c->cputype); if (mips_fpu_disabled) c->options &= ~MIPS_CPU_FPU; if (c->options & MIPS_CPU_FPU) cpu_set_fpu_opts(c); else cpu_set_nofpu_opts(c); reserve_exception_space(0, 0x400); } void cpu_report(void) { struct cpuinfo_mips *c = ¤t_cpu_data; pr_info("CPU%d revision is: %08x (%s)\n", smp_processor_id(), c->processor_id, cpu_name_string()); if (c->options & MIPS_CPU_FPU) pr_info("FPU revision is: %08x\n", c->fpu_id); } |