162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci 362306a36Sopenharmony_ci#include <linux/smp.h> 462306a36Sopenharmony_ci#include <linux/types.h> 562306a36Sopenharmony_ci#include <asm/cpu.h> 662306a36Sopenharmony_ci#include <asm/cpu-info.h> 762306a36Sopenharmony_ci#include <asm/elf.h> 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <loongson_regs.h> 1062306a36Sopenharmony_ci#include <cpucfg-emul.h> 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_cistatic bool is_loongson(struct cpuinfo_mips *c) 1362306a36Sopenharmony_ci{ 1462306a36Sopenharmony_ci switch (c->processor_id & PRID_COMP_MASK) { 1562306a36Sopenharmony_ci case PRID_COMP_LEGACY: 1662306a36Sopenharmony_ci return ((c->processor_id & PRID_IMP_MASK) == 1762306a36Sopenharmony_ci PRID_IMP_LOONGSON_64C); 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci case PRID_COMP_LOONGSON: 2062306a36Sopenharmony_ci return true; 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci default: 2362306a36Sopenharmony_ci return false; 2462306a36Sopenharmony_ci } 2562306a36Sopenharmony_ci} 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_cistatic u32 get_loongson_fprev(struct cpuinfo_mips *c) 2862306a36Sopenharmony_ci{ 2962306a36Sopenharmony_ci return c->fpu_id & LOONGSON_FPREV_MASK; 3062306a36Sopenharmony_ci} 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_cistatic bool cpu_has_uca(void) 3362306a36Sopenharmony_ci{ 3462306a36Sopenharmony_ci u32 diag = read_c0_diag(); 3562306a36Sopenharmony_ci u32 new_diag; 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci if (diag & LOONGSON_DIAG_UCAC) 3862306a36Sopenharmony_ci /* UCA is already enabled. */ 3962306a36Sopenharmony_ci return true; 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci /* See if UCAC bit can be flipped on. This should be safe. */ 4262306a36Sopenharmony_ci new_diag = diag | LOONGSON_DIAG_UCAC; 4362306a36Sopenharmony_ci write_c0_diag(new_diag); 4462306a36Sopenharmony_ci new_diag = read_c0_diag(); 4562306a36Sopenharmony_ci write_c0_diag(diag); 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci return (new_diag & LOONGSON_DIAG_UCAC) != 0; 4862306a36Sopenharmony_ci} 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_cistatic void probe_uca(struct cpuinfo_mips *c) 5162306a36Sopenharmony_ci{ 5262306a36Sopenharmony_ci if (cpu_has_uca()) 5362306a36Sopenharmony_ci c->loongson3_cpucfg_data[0] |= LOONGSON_CFG1_LSUCA; 5462306a36Sopenharmony_ci} 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_cistatic void decode_loongson_config6(struct cpuinfo_mips *c) 5762306a36Sopenharmony_ci{ 5862306a36Sopenharmony_ci u32 config6 = read_c0_config6(); 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci if (config6 & LOONGSON_CONF6_SFBEN) 6162306a36Sopenharmony_ci c->loongson3_cpucfg_data[0] |= LOONGSON_CFG1_SFBP; 6262306a36Sopenharmony_ci if (config6 & LOONGSON_CONF6_LLEXC) 6362306a36Sopenharmony_ci c->loongson3_cpucfg_data[0] |= LOONGSON_CFG1_LLEXC; 6462306a36Sopenharmony_ci if (config6 & LOONGSON_CONF6_SCRAND) 6562306a36Sopenharmony_ci c->loongson3_cpucfg_data[0] |= LOONGSON_CFG1_SCRAND; 6662306a36Sopenharmony_ci} 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_cistatic void patch_cpucfg_sel1(struct cpuinfo_mips *c) 6962306a36Sopenharmony_ci{ 7062306a36Sopenharmony_ci u64 ases = c->ases; 7162306a36Sopenharmony_ci u64 options = c->options; 7262306a36Sopenharmony_ci u32 data = c->loongson3_cpucfg_data[0]; 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci if (options & MIPS_CPU_FPU) { 7562306a36Sopenharmony_ci data |= LOONGSON_CFG1_FP; 7662306a36Sopenharmony_ci data |= get_loongson_fprev(c) << LOONGSON_CFG1_FPREV_OFFSET; 7762306a36Sopenharmony_ci } 7862306a36Sopenharmony_ci if (ases & MIPS_ASE_LOONGSON_MMI) 7962306a36Sopenharmony_ci data |= LOONGSON_CFG1_MMI; 8062306a36Sopenharmony_ci if (ases & MIPS_ASE_MSA) 8162306a36Sopenharmony_ci data |= LOONGSON_CFG1_MSA1; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci c->loongson3_cpucfg_data[0] = data; 8462306a36Sopenharmony_ci} 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_cistatic void patch_cpucfg_sel2(struct cpuinfo_mips *c) 8762306a36Sopenharmony_ci{ 8862306a36Sopenharmony_ci u64 ases = c->ases; 8962306a36Sopenharmony_ci u64 options = c->options; 9062306a36Sopenharmony_ci u32 data = c->loongson3_cpucfg_data[1]; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci if (ases & MIPS_ASE_LOONGSON_EXT) 9362306a36Sopenharmony_ci data |= LOONGSON_CFG2_LEXT1; 9462306a36Sopenharmony_ci if (ases & MIPS_ASE_LOONGSON_EXT2) 9562306a36Sopenharmony_ci data |= LOONGSON_CFG2_LEXT2; 9662306a36Sopenharmony_ci if (options & MIPS_CPU_LDPTE) 9762306a36Sopenharmony_ci data |= LOONGSON_CFG2_LSPW; 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci if (ases & MIPS_ASE_VZ) 10062306a36Sopenharmony_ci data |= LOONGSON_CFG2_LVZP; 10162306a36Sopenharmony_ci else 10262306a36Sopenharmony_ci data &= ~LOONGSON_CFG2_LVZREV; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci c->loongson3_cpucfg_data[1] = data; 10562306a36Sopenharmony_ci} 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_cistatic void patch_cpucfg_sel3(struct cpuinfo_mips *c) 10862306a36Sopenharmony_ci{ 10962306a36Sopenharmony_ci u64 ases = c->ases; 11062306a36Sopenharmony_ci u32 data = c->loongson3_cpucfg_data[2]; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci if (ases & MIPS_ASE_LOONGSON_CAM) { 11362306a36Sopenharmony_ci data |= LOONGSON_CFG3_LCAMP; 11462306a36Sopenharmony_ci } else { 11562306a36Sopenharmony_ci data &= ~LOONGSON_CFG3_LCAMREV; 11662306a36Sopenharmony_ci data &= ~LOONGSON_CFG3_LCAMNUM; 11762306a36Sopenharmony_ci data &= ~LOONGSON_CFG3_LCAMKW; 11862306a36Sopenharmony_ci data &= ~LOONGSON_CFG3_LCAMVW; 11962306a36Sopenharmony_ci } 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci c->loongson3_cpucfg_data[2] = data; 12262306a36Sopenharmony_ci} 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_civoid loongson3_cpucfg_synthesize_data(struct cpuinfo_mips *c) 12562306a36Sopenharmony_ci{ 12662306a36Sopenharmony_ci /* Only engage the logic on Loongson processors. */ 12762306a36Sopenharmony_ci if (!is_loongson(c)) 12862306a36Sopenharmony_ci return; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci /* CPUs with CPUCFG support don't need to synthesize anything. */ 13162306a36Sopenharmony_ci if (cpu_has_cfg()) 13262306a36Sopenharmony_ci goto have_cpucfg_now; 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci c->loongson3_cpucfg_data[0] = 0; 13562306a36Sopenharmony_ci c->loongson3_cpucfg_data[1] = 0; 13662306a36Sopenharmony_ci c->loongson3_cpucfg_data[2] = 0; 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci /* Add CPUCFG features non-discoverable otherwise. */ 13962306a36Sopenharmony_ci switch (c->processor_id & (PRID_IMP_MASK | PRID_REV_MASK)) { 14062306a36Sopenharmony_ci case PRID_IMP_LOONGSON_64R | PRID_REV_LOONGSON2K_R1_0: 14162306a36Sopenharmony_ci case PRID_IMP_LOONGSON_64R | PRID_REV_LOONGSON2K_R1_1: 14262306a36Sopenharmony_ci case PRID_IMP_LOONGSON_64R | PRID_REV_LOONGSON2K_R1_2: 14362306a36Sopenharmony_ci case PRID_IMP_LOONGSON_64R | PRID_REV_LOONGSON2K_R1_3: 14462306a36Sopenharmony_ci decode_loongson_config6(c); 14562306a36Sopenharmony_ci probe_uca(c); 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci c->loongson3_cpucfg_data[0] |= (LOONGSON_CFG1_LSLDR0 | 14862306a36Sopenharmony_ci LOONGSON_CFG1_LSSYNCI | LOONGSON_CFG1_LLSYNC | 14962306a36Sopenharmony_ci LOONGSON_CFG1_TGTSYNC); 15062306a36Sopenharmony_ci c->loongson3_cpucfg_data[1] |= (LOONGSON_CFG2_LBT1 | 15162306a36Sopenharmony_ci LOONGSON_CFG2_LBT2 | LOONGSON_CFG2_LPMP | 15262306a36Sopenharmony_ci LOONGSON_CFG2_LPM_REV2); 15362306a36Sopenharmony_ci c->loongson3_cpucfg_data[2] = 0; 15462306a36Sopenharmony_ci break; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci case PRID_IMP_LOONGSON_64C | PRID_REV_LOONGSON3A_R1: 15762306a36Sopenharmony_ci c->loongson3_cpucfg_data[0] |= (LOONGSON_CFG1_LSLDR0 | 15862306a36Sopenharmony_ci LOONGSON_CFG1_LSSYNCI | LOONGSON_CFG1_LSUCA | 15962306a36Sopenharmony_ci LOONGSON_CFG1_LLSYNC | LOONGSON_CFG1_TGTSYNC); 16062306a36Sopenharmony_ci c->loongson3_cpucfg_data[1] |= (LOONGSON_CFG2_LBT1 | 16162306a36Sopenharmony_ci LOONGSON_CFG2_LPMP | LOONGSON_CFG2_LPM_REV1); 16262306a36Sopenharmony_ci c->loongson3_cpucfg_data[2] |= ( 16362306a36Sopenharmony_ci LOONGSON_CFG3_LCAM_REV1 | 16462306a36Sopenharmony_ci LOONGSON_CFG3_LCAMNUM_REV1 | 16562306a36Sopenharmony_ci LOONGSON_CFG3_LCAMKW_REV1 | 16662306a36Sopenharmony_ci LOONGSON_CFG3_LCAMVW_REV1); 16762306a36Sopenharmony_ci break; 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci case PRID_IMP_LOONGSON_64C | PRID_REV_LOONGSON3B_R1: 17062306a36Sopenharmony_ci case PRID_IMP_LOONGSON_64C | PRID_REV_LOONGSON3B_R2: 17162306a36Sopenharmony_ci c->loongson3_cpucfg_data[0] |= (LOONGSON_CFG1_LSLDR0 | 17262306a36Sopenharmony_ci LOONGSON_CFG1_LSSYNCI | LOONGSON_CFG1_LSUCA | 17362306a36Sopenharmony_ci LOONGSON_CFG1_LLSYNC | LOONGSON_CFG1_TGTSYNC); 17462306a36Sopenharmony_ci c->loongson3_cpucfg_data[1] |= (LOONGSON_CFG2_LBT1 | 17562306a36Sopenharmony_ci LOONGSON_CFG2_LPMP | LOONGSON_CFG2_LPM_REV1); 17662306a36Sopenharmony_ci c->loongson3_cpucfg_data[2] |= ( 17762306a36Sopenharmony_ci LOONGSON_CFG3_LCAM_REV1 | 17862306a36Sopenharmony_ci LOONGSON_CFG3_LCAMNUM_REV1 | 17962306a36Sopenharmony_ci LOONGSON_CFG3_LCAMKW_REV1 | 18062306a36Sopenharmony_ci LOONGSON_CFG3_LCAMVW_REV1); 18162306a36Sopenharmony_ci break; 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci case PRID_IMP_LOONGSON_64C | PRID_REV_LOONGSON3A_R2_0: 18462306a36Sopenharmony_ci case PRID_IMP_LOONGSON_64C | PRID_REV_LOONGSON3A_R2_1: 18562306a36Sopenharmony_ci case PRID_IMP_LOONGSON_64C | PRID_REV_LOONGSON3A_R3_0: 18662306a36Sopenharmony_ci case PRID_IMP_LOONGSON_64C | PRID_REV_LOONGSON3A_R3_1: 18762306a36Sopenharmony_ci decode_loongson_config6(c); 18862306a36Sopenharmony_ci probe_uca(c); 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci c->loongson3_cpucfg_data[0] |= (LOONGSON_CFG1_CNT64 | 19162306a36Sopenharmony_ci LOONGSON_CFG1_LSLDR0 | LOONGSON_CFG1_LSPREF | 19262306a36Sopenharmony_ci LOONGSON_CFG1_LSPREFX | LOONGSON_CFG1_LSSYNCI | 19362306a36Sopenharmony_ci LOONGSON_CFG1_LLSYNC | LOONGSON_CFG1_TGTSYNC); 19462306a36Sopenharmony_ci c->loongson3_cpucfg_data[1] |= (LOONGSON_CFG2_LBT1 | 19562306a36Sopenharmony_ci LOONGSON_CFG2_LBT2 | LOONGSON_CFG2_LBTMMU | 19662306a36Sopenharmony_ci LOONGSON_CFG2_LPMP | LOONGSON_CFG2_LPM_REV1 | 19762306a36Sopenharmony_ci LOONGSON_CFG2_LVZ_REV1); 19862306a36Sopenharmony_ci c->loongson3_cpucfg_data[2] |= (LOONGSON_CFG3_LCAM_REV1 | 19962306a36Sopenharmony_ci LOONGSON_CFG3_LCAMNUM_REV1 | 20062306a36Sopenharmony_ci LOONGSON_CFG3_LCAMKW_REV1 | 20162306a36Sopenharmony_ci LOONGSON_CFG3_LCAMVW_REV1); 20262306a36Sopenharmony_ci break; 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci default: 20562306a36Sopenharmony_ci /* It is possible that some future Loongson cores still do 20662306a36Sopenharmony_ci * not have CPUCFG, so do not emulate anything for these 20762306a36Sopenharmony_ci * cores. 20862306a36Sopenharmony_ci */ 20962306a36Sopenharmony_ci return; 21062306a36Sopenharmony_ci } 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci /* This feature is set by firmware, but all known Loongson-64 systems 21362306a36Sopenharmony_ci * are configured this way. 21462306a36Sopenharmony_ci */ 21562306a36Sopenharmony_ci c->loongson3_cpucfg_data[0] |= LOONGSON_CFG1_CDMAP; 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci /* Patch in dynamically probed bits. */ 21862306a36Sopenharmony_ci patch_cpucfg_sel1(c); 21962306a36Sopenharmony_ci patch_cpucfg_sel2(c); 22062306a36Sopenharmony_ci patch_cpucfg_sel3(c); 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_cihave_cpucfg_now: 22362306a36Sopenharmony_ci /* We have usable CPUCFG now, emulated or not. 22462306a36Sopenharmony_ci * Announce CPUCFG availability to userspace via hwcap. 22562306a36Sopenharmony_ci */ 22662306a36Sopenharmony_ci elf_hwcap |= HWCAP_LOONGSON_CPUCFG; 22762306a36Sopenharmony_ci} 228