1ec7a9318SWANG Xuerui // SPDX-License-Identifier: GPL-2.0 2ec7a9318SWANG Xuerui 3ec7a9318SWANG Xuerui #include <linux/smp.h> 4ec7a9318SWANG Xuerui #include <linux/types.h> 5ec7a9318SWANG Xuerui #include <asm/cpu.h> 6ec7a9318SWANG Xuerui #include <asm/cpu-info.h> 7f06da27eSWANG Xuerui #include <asm/elf.h> 8ec7a9318SWANG Xuerui 9ec7a9318SWANG Xuerui #include <loongson_regs.h> 10ec7a9318SWANG Xuerui #include <cpucfg-emul.h> 11ec7a9318SWANG Xuerui is_loongson(struct cpuinfo_mips * c)12ec7a9318SWANG Xueruistatic bool is_loongson(struct cpuinfo_mips *c) 13ec7a9318SWANG Xuerui { 14ec7a9318SWANG Xuerui switch (c->processor_id & PRID_COMP_MASK) { 15ec7a9318SWANG Xuerui case PRID_COMP_LEGACY: 16ec7a9318SWANG Xuerui return ((c->processor_id & PRID_IMP_MASK) == 17ec7a9318SWANG Xuerui PRID_IMP_LOONGSON_64C); 18ec7a9318SWANG Xuerui 19ec7a9318SWANG Xuerui case PRID_COMP_LOONGSON: 20ec7a9318SWANG Xuerui return true; 21ec7a9318SWANG Xuerui 22ec7a9318SWANG Xuerui default: 23ec7a9318SWANG Xuerui return false; 24ec7a9318SWANG Xuerui } 25ec7a9318SWANG Xuerui } 26ec7a9318SWANG Xuerui get_loongson_fprev(struct cpuinfo_mips * c)27ec7a9318SWANG Xueruistatic u32 get_loongson_fprev(struct cpuinfo_mips *c) 28ec7a9318SWANG Xuerui { 29ec7a9318SWANG Xuerui return c->fpu_id & LOONGSON_FPREV_MASK; 30ec7a9318SWANG Xuerui } 31ec7a9318SWANG Xuerui cpu_has_uca(void)32ec7a9318SWANG Xueruistatic bool cpu_has_uca(void) 33ec7a9318SWANG Xuerui { 34ec7a9318SWANG Xuerui u32 diag = read_c0_diag(); 35ec7a9318SWANG Xuerui u32 new_diag; 36ec7a9318SWANG Xuerui 37ec7a9318SWANG Xuerui if (diag & LOONGSON_DIAG_UCAC) 38ec7a9318SWANG Xuerui /* UCA is already enabled. */ 39ec7a9318SWANG Xuerui return true; 40ec7a9318SWANG Xuerui 41ec7a9318SWANG Xuerui /* See if UCAC bit can be flipped on. This should be safe. */ 42ec7a9318SWANG Xuerui new_diag = diag | LOONGSON_DIAG_UCAC; 43ec7a9318SWANG Xuerui write_c0_diag(new_diag); 44ec7a9318SWANG Xuerui new_diag = read_c0_diag(); 45ec7a9318SWANG Xuerui write_c0_diag(diag); 46ec7a9318SWANG Xuerui 47ec7a9318SWANG Xuerui return (new_diag & LOONGSON_DIAG_UCAC) != 0; 48ec7a9318SWANG Xuerui } 49ec7a9318SWANG Xuerui probe_uca(struct cpuinfo_mips * c)50ec7a9318SWANG Xueruistatic void probe_uca(struct cpuinfo_mips *c) 51ec7a9318SWANG Xuerui { 52ec7a9318SWANG Xuerui if (cpu_has_uca()) 53ec7a9318SWANG Xuerui c->loongson3_cpucfg_data[0] |= LOONGSON_CFG1_LSUCA; 54ec7a9318SWANG Xuerui } 55ec7a9318SWANG Xuerui decode_loongson_config6(struct cpuinfo_mips * c)56ec7a9318SWANG Xueruistatic void decode_loongson_config6(struct cpuinfo_mips *c) 57ec7a9318SWANG Xuerui { 58ec7a9318SWANG Xuerui u32 config6 = read_c0_config6(); 59ec7a9318SWANG Xuerui 6004ef32afSHuacai Chen if (config6 & LOONGSON_CONF6_SFBEN) 61ec7a9318SWANG Xuerui c->loongson3_cpucfg_data[0] |= LOONGSON_CFG1_SFBP; 6204ef32afSHuacai Chen if (config6 & LOONGSON_CONF6_LLEXC) 63ec7a9318SWANG Xuerui c->loongson3_cpucfg_data[0] |= LOONGSON_CFG1_LLEXC; 6404ef32afSHuacai Chen if (config6 & LOONGSON_CONF6_SCRAND) 65ec7a9318SWANG Xuerui c->loongson3_cpucfg_data[0] |= LOONGSON_CFG1_SCRAND; 66ec7a9318SWANG Xuerui } 67ec7a9318SWANG Xuerui patch_cpucfg_sel1(struct cpuinfo_mips * c)68ec7a9318SWANG Xueruistatic void patch_cpucfg_sel1(struct cpuinfo_mips *c) 69ec7a9318SWANG Xuerui { 70ec7a9318SWANG Xuerui u64 ases = c->ases; 71ec7a9318SWANG Xuerui u64 options = c->options; 72ec7a9318SWANG Xuerui u32 data = c->loongson3_cpucfg_data[0]; 73ec7a9318SWANG Xuerui 74ec7a9318SWANG Xuerui if (options & MIPS_CPU_FPU) { 75ec7a9318SWANG Xuerui data |= LOONGSON_CFG1_FP; 76ec7a9318SWANG Xuerui data |= get_loongson_fprev(c) << LOONGSON_CFG1_FPREV_OFFSET; 77ec7a9318SWANG Xuerui } 78ec7a9318SWANG Xuerui if (ases & MIPS_ASE_LOONGSON_MMI) 79ec7a9318SWANG Xuerui data |= LOONGSON_CFG1_MMI; 80ec7a9318SWANG Xuerui if (ases & MIPS_ASE_MSA) 81ec7a9318SWANG Xuerui data |= LOONGSON_CFG1_MSA1; 82ec7a9318SWANG Xuerui 83ec7a9318SWANG Xuerui c->loongson3_cpucfg_data[0] = data; 84ec7a9318SWANG Xuerui } 85ec7a9318SWANG Xuerui patch_cpucfg_sel2(struct cpuinfo_mips * c)86ec7a9318SWANG Xueruistatic void patch_cpucfg_sel2(struct cpuinfo_mips *c) 87ec7a9318SWANG Xuerui { 88ec7a9318SWANG Xuerui u64 ases = c->ases; 89ec7a9318SWANG Xuerui u64 options = c->options; 90ec7a9318SWANG Xuerui u32 data = c->loongson3_cpucfg_data[1]; 91ec7a9318SWANG Xuerui 92ec7a9318SWANG Xuerui if (ases & MIPS_ASE_LOONGSON_EXT) 93ec7a9318SWANG Xuerui data |= LOONGSON_CFG2_LEXT1; 94ec7a9318SWANG Xuerui if (ases & MIPS_ASE_LOONGSON_EXT2) 95ec7a9318SWANG Xuerui data |= LOONGSON_CFG2_LEXT2; 96ec7a9318SWANG Xuerui if (options & MIPS_CPU_LDPTE) 97ec7a9318SWANG Xuerui data |= LOONGSON_CFG2_LSPW; 98ec7a9318SWANG Xuerui 99ec7a9318SWANG Xuerui if (ases & MIPS_ASE_VZ) 100ec7a9318SWANG Xuerui data |= LOONGSON_CFG2_LVZP; 101ec7a9318SWANG Xuerui else 102ec7a9318SWANG Xuerui data &= ~LOONGSON_CFG2_LVZREV; 103ec7a9318SWANG Xuerui 104ec7a9318SWANG Xuerui c->loongson3_cpucfg_data[1] = data; 105ec7a9318SWANG Xuerui } 106ec7a9318SWANG Xuerui patch_cpucfg_sel3(struct cpuinfo_mips * c)107ec7a9318SWANG Xueruistatic void patch_cpucfg_sel3(struct cpuinfo_mips *c) 108ec7a9318SWANG Xuerui { 109ec7a9318SWANG Xuerui u64 ases = c->ases; 110ec7a9318SWANG Xuerui u32 data = c->loongson3_cpucfg_data[2]; 111ec7a9318SWANG Xuerui 112ec7a9318SWANG Xuerui if (ases & MIPS_ASE_LOONGSON_CAM) { 113ec7a9318SWANG Xuerui data |= LOONGSON_CFG3_LCAMP; 114ec7a9318SWANG Xuerui } else { 115ec7a9318SWANG Xuerui data &= ~LOONGSON_CFG3_LCAMREV; 116ec7a9318SWANG Xuerui data &= ~LOONGSON_CFG3_LCAMNUM; 117ec7a9318SWANG Xuerui data &= ~LOONGSON_CFG3_LCAMKW; 118ec7a9318SWANG Xuerui data &= ~LOONGSON_CFG3_LCAMVW; 119ec7a9318SWANG Xuerui } 120ec7a9318SWANG Xuerui 121ec7a9318SWANG Xuerui c->loongson3_cpucfg_data[2] = data; 122ec7a9318SWANG Xuerui } 123ec7a9318SWANG Xuerui loongson3_cpucfg_synthesize_data(struct cpuinfo_mips * c)124ec7a9318SWANG Xueruivoid loongson3_cpucfg_synthesize_data(struct cpuinfo_mips *c) 125ec7a9318SWANG Xuerui { 126ec7a9318SWANG Xuerui /* Only engage the logic on Loongson processors. */ 127ec7a9318SWANG Xuerui if (!is_loongson(c)) 128ec7a9318SWANG Xuerui return; 129ec7a9318SWANG Xuerui 130ec7a9318SWANG Xuerui /* CPUs with CPUCFG support don't need to synthesize anything. */ 131ec7a9318SWANG Xuerui if (cpu_has_cfg()) 132f06da27eSWANG Xuerui goto have_cpucfg_now; 133ec7a9318SWANG Xuerui 134ec7a9318SWANG Xuerui c->loongson3_cpucfg_data[0] = 0; 135ec7a9318SWANG Xuerui c->loongson3_cpucfg_data[1] = 0; 136ec7a9318SWANG Xuerui c->loongson3_cpucfg_data[2] = 0; 137ec7a9318SWANG Xuerui 13870768ebaSWANG Xuerui /* Add CPUCFG features non-discoverable otherwise. */ 13970768ebaSWANG Xuerui switch (c->processor_id & (PRID_IMP_MASK | PRID_REV_MASK)) { 140dd25ed73SWANG Xuerui case PRID_IMP_LOONGSON_64R | PRID_REV_LOONGSON2K_R1_0: 141dd25ed73SWANG Xuerui case PRID_IMP_LOONGSON_64R | PRID_REV_LOONGSON2K_R1_1: 142dd25ed73SWANG Xuerui case PRID_IMP_LOONGSON_64R | PRID_REV_LOONGSON2K_R1_2: 143dd25ed73SWANG Xuerui case PRID_IMP_LOONGSON_64R | PRID_REV_LOONGSON2K_R1_3: 144dd25ed73SWANG Xuerui decode_loongson_config6(c); 145dd25ed73SWANG Xuerui probe_uca(c); 146dd25ed73SWANG Xuerui 147dd25ed73SWANG Xuerui c->loongson3_cpucfg_data[0] |= (LOONGSON_CFG1_LSLDR0 | 148dd25ed73SWANG Xuerui LOONGSON_CFG1_LSSYNCI | LOONGSON_CFG1_LLSYNC | 149dd25ed73SWANG Xuerui LOONGSON_CFG1_TGTSYNC); 150dd25ed73SWANG Xuerui c->loongson3_cpucfg_data[1] |= (LOONGSON_CFG2_LBT1 | 151dd25ed73SWANG Xuerui LOONGSON_CFG2_LBT2 | LOONGSON_CFG2_LPMP | 152dd25ed73SWANG Xuerui LOONGSON_CFG2_LPM_REV2); 153dd25ed73SWANG Xuerui c->loongson3_cpucfg_data[2] = 0; 154dd25ed73SWANG Xuerui break; 155dd25ed73SWANG Xuerui 15670768ebaSWANG Xuerui case PRID_IMP_LOONGSON_64C | PRID_REV_LOONGSON3A_R1: 157ec7a9318SWANG Xuerui c->loongson3_cpucfg_data[0] |= (LOONGSON_CFG1_LSLDR0 | 158ec7a9318SWANG Xuerui LOONGSON_CFG1_LSSYNCI | LOONGSON_CFG1_LSUCA | 159ec7a9318SWANG Xuerui LOONGSON_CFG1_LLSYNC | LOONGSON_CFG1_TGTSYNC); 160ec7a9318SWANG Xuerui c->loongson3_cpucfg_data[1] |= (LOONGSON_CFG2_LBT1 | 161ec7a9318SWANG Xuerui LOONGSON_CFG2_LPMP | LOONGSON_CFG2_LPM_REV1); 162ec7a9318SWANG Xuerui c->loongson3_cpucfg_data[2] |= ( 163ec7a9318SWANG Xuerui LOONGSON_CFG3_LCAM_REV1 | 164ec7a9318SWANG Xuerui LOONGSON_CFG3_LCAMNUM_REV1 | 165ec7a9318SWANG Xuerui LOONGSON_CFG3_LCAMKW_REV1 | 166ec7a9318SWANG Xuerui LOONGSON_CFG3_LCAMVW_REV1); 167ec7a9318SWANG Xuerui break; 168ec7a9318SWANG Xuerui 16970768ebaSWANG Xuerui case PRID_IMP_LOONGSON_64C | PRID_REV_LOONGSON3B_R1: 17070768ebaSWANG Xuerui case PRID_IMP_LOONGSON_64C | PRID_REV_LOONGSON3B_R2: 171ec7a9318SWANG Xuerui c->loongson3_cpucfg_data[0] |= (LOONGSON_CFG1_LSLDR0 | 172ec7a9318SWANG Xuerui LOONGSON_CFG1_LSSYNCI | LOONGSON_CFG1_LSUCA | 173ec7a9318SWANG Xuerui LOONGSON_CFG1_LLSYNC | LOONGSON_CFG1_TGTSYNC); 174ec7a9318SWANG Xuerui c->loongson3_cpucfg_data[1] |= (LOONGSON_CFG2_LBT1 | 175ec7a9318SWANG Xuerui LOONGSON_CFG2_LPMP | LOONGSON_CFG2_LPM_REV1); 176ec7a9318SWANG Xuerui c->loongson3_cpucfg_data[2] |= ( 177ec7a9318SWANG Xuerui LOONGSON_CFG3_LCAM_REV1 | 178ec7a9318SWANG Xuerui LOONGSON_CFG3_LCAMNUM_REV1 | 179ec7a9318SWANG Xuerui LOONGSON_CFG3_LCAMKW_REV1 | 180ec7a9318SWANG Xuerui LOONGSON_CFG3_LCAMVW_REV1); 181ec7a9318SWANG Xuerui break; 182ec7a9318SWANG Xuerui 18370768ebaSWANG Xuerui case PRID_IMP_LOONGSON_64C | PRID_REV_LOONGSON3A_R2_0: 18470768ebaSWANG Xuerui case PRID_IMP_LOONGSON_64C | PRID_REV_LOONGSON3A_R2_1: 18570768ebaSWANG Xuerui case PRID_IMP_LOONGSON_64C | PRID_REV_LOONGSON3A_R3_0: 18670768ebaSWANG Xuerui case PRID_IMP_LOONGSON_64C | PRID_REV_LOONGSON3A_R3_1: 187ec7a9318SWANG Xuerui decode_loongson_config6(c); 188ec7a9318SWANG Xuerui probe_uca(c); 189ec7a9318SWANG Xuerui 190ec7a9318SWANG Xuerui c->loongson3_cpucfg_data[0] |= (LOONGSON_CFG1_CNT64 | 191ec7a9318SWANG Xuerui LOONGSON_CFG1_LSLDR0 | LOONGSON_CFG1_LSPREF | 192ec7a9318SWANG Xuerui LOONGSON_CFG1_LSPREFX | LOONGSON_CFG1_LSSYNCI | 193ec7a9318SWANG Xuerui LOONGSON_CFG1_LLSYNC | LOONGSON_CFG1_TGTSYNC); 194ec7a9318SWANG Xuerui c->loongson3_cpucfg_data[1] |= (LOONGSON_CFG2_LBT1 | 195ec7a9318SWANG Xuerui LOONGSON_CFG2_LBT2 | LOONGSON_CFG2_LBTMMU | 196ec7a9318SWANG Xuerui LOONGSON_CFG2_LPMP | LOONGSON_CFG2_LPM_REV1 | 197ec7a9318SWANG Xuerui LOONGSON_CFG2_LVZ_REV1); 198ec7a9318SWANG Xuerui c->loongson3_cpucfg_data[2] |= (LOONGSON_CFG3_LCAM_REV1 | 199ec7a9318SWANG Xuerui LOONGSON_CFG3_LCAMNUM_REV1 | 200ec7a9318SWANG Xuerui LOONGSON_CFG3_LCAMKW_REV1 | 201ec7a9318SWANG Xuerui LOONGSON_CFG3_LCAMVW_REV1); 202ec7a9318SWANG Xuerui break; 20370768ebaSWANG Xuerui 20470768ebaSWANG Xuerui default: 20570768ebaSWANG Xuerui /* It is possible that some future Loongson cores still do 20670768ebaSWANG Xuerui * not have CPUCFG, so do not emulate anything for these 20770768ebaSWANG Xuerui * cores. 20870768ebaSWANG Xuerui */ 20970768ebaSWANG Xuerui return; 210ec7a9318SWANG Xuerui } 211ec7a9318SWANG Xuerui 212ec7a9318SWANG Xuerui /* This feature is set by firmware, but all known Loongson-64 systems 213ec7a9318SWANG Xuerui * are configured this way. 214ec7a9318SWANG Xuerui */ 215ec7a9318SWANG Xuerui c->loongson3_cpucfg_data[0] |= LOONGSON_CFG1_CDMAP; 216ec7a9318SWANG Xuerui 217ec7a9318SWANG Xuerui /* Patch in dynamically probed bits. */ 218ec7a9318SWANG Xuerui patch_cpucfg_sel1(c); 219ec7a9318SWANG Xuerui patch_cpucfg_sel2(c); 220ec7a9318SWANG Xuerui patch_cpucfg_sel3(c); 221f06da27eSWANG Xuerui 222f06da27eSWANG Xuerui have_cpucfg_now: 223f06da27eSWANG Xuerui /* We have usable CPUCFG now, emulated or not. 224f06da27eSWANG Xuerui * Announce CPUCFG availability to userspace via hwcap. 225f06da27eSWANG Xuerui */ 226f06da27eSWANG Xuerui elf_hwcap |= HWCAP_LOONGSON_CPUCFG; 227ec7a9318SWANG Xuerui } 228