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