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