xref: /openbmc/linux/arch/mips/loongson64/cpucfg-emul.c (revision d8242e6a71bac37b6cde52c0add533615eef8c5e)
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