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 | |
12 | static 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 | |
27 | static u32 get_loongson_fprev(struct cpuinfo_mips *c) |
28 | { |
29 | return c->fpu_id & LOONGSON_FPREV_MASK; |
30 | } |
31 | |
32 | static 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 | |
50 | static void probe_uca(struct cpuinfo_mips *c) |
51 | { |
52 | if (cpu_has_uca()) |
53 | c->loongson3_cpucfg_data[0] |= LOONGSON_CFG1_LSUCA; |
54 | } |
55 | |
56 | static 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 | |
68 | static 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 | |
86 | static 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 | |
107 | static 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 | |
124 | void 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 | |