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