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