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