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