| // 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); |
| } |