aboutsummaryrefslogtreecommitdiffstats
path: root/arch/mips/loongson64
diff options
context:
space:
mode:
authorWe-unite <3205135446@qq.com>2025-03-08 22:04:20 +0800
committerWe-unite <3205135446@qq.com>2025-03-08 22:04:20 +0800
commita07bb8fd1299070229f0e8f3dcb57ffd5ef9870a (patch)
tree84f21bd0bf7071bc5fc7dd989e77d7ceb5476682 /arch/mips/loongson64
downloadohosKernel-a07bb8fd1299070229f0e8f3dcb57ffd5ef9870a.tar.gz
ohosKernel-a07bb8fd1299070229f0e8f3dcb57ffd5ef9870a.zip
Initial commit: OpenHarmony-v4.0-ReleaseOpenHarmony-v4.0-Release
Diffstat (limited to 'arch/mips/loongson64')
-rw-r--r--arch/mips/loongson64/Kconfig15
-rw-r--r--arch/mips/loongson64/Makefile13
-rw-r--r--arch/mips/loongson64/Platform37
-rw-r--r--arch/mips/loongson64/cop2-ex.c341
-rw-r--r--arch/mips/loongson64/cpucfg-emul.c227
-rw-r--r--arch/mips/loongson64/dma.c28
-rw-r--r--arch/mips/loongson64/env.c223
-rw-r--r--arch/mips/loongson64/hpet.c285
-rw-r--r--arch/mips/loongson64/init.c149
-rw-r--r--arch/mips/loongson64/numa.c252
-rw-r--r--arch/mips/loongson64/platform.c42
-rw-r--r--arch/mips/loongson64/pm.c104
-rw-r--r--arch/mips/loongson64/reset.c59
-rw-r--r--arch/mips/loongson64/setup.c46
-rw-r--r--arch/mips/loongson64/smp.c806
-rw-r--r--arch/mips/loongson64/smp.h31
-rw-r--r--arch/mips/loongson64/time.c23
-rw-r--r--arch/mips/loongson64/vbios_quirk.c29
18 files changed, 2710 insertions, 0 deletions
diff --git a/arch/mips/loongson64/Kconfig b/arch/mips/loongson64/Kconfig
new file mode 100644
index 000000000..517f1f8e8
--- /dev/null
+++ b/arch/mips/loongson64/Kconfig
@@ -0,0 +1,15 @@
1# SPDX-License-Identifier: GPL-2.0
2if MACH_LOONGSON64
3
4config RS780_HPET
5 bool "RS780/SBX00 HPET Timer"
6 depends on MACH_LOONGSON64
7 depends on BROKEN
8 select MIPS_EXTERNAL_TIMER
9 help
10 This option enables the hpet timer of AMD RS780/SBX00.
11
12 Note: This driver is doing some dangerous hack. Please only enable
13 it on RS780E systems.
14
15endif # MACH_LOONGSON64
diff --git a/arch/mips/loongson64/Makefile b/arch/mips/loongson64/Makefile
new file mode 100644
index 000000000..39c06f52b
--- /dev/null
+++ b/arch/mips/loongson64/Makefile
@@ -0,0 +1,13 @@
1# SPDX-License-Identifier: GPL-2.0-only
2#
3# Makefile for Loongson-3 family machines
4#
5obj-$(CONFIG_MACH_LOONGSON64) += cop2-ex.o platform.o dma.o \
6 setup.o init.o env.o time.o reset.o \
7
8obj-$(CONFIG_SMP) += smp.o
9obj-$(CONFIG_NUMA) += numa.o
10obj-$(CONFIG_RS780_HPET) += hpet.o
11obj-$(CONFIG_SUSPEND) += pm.o
12obj-$(CONFIG_PCI_QUIRKS) += vbios_quirk.o
13obj-$(CONFIG_CPU_LOONGSON3_CPUCFG_EMULATION) += cpucfg-emul.o
diff --git a/arch/mips/loongson64/Platform b/arch/mips/loongson64/Platform
new file mode 100644
index 000000000..e2354e128
--- /dev/null
+++ b/arch/mips/loongson64/Platform
@@ -0,0 +1,37 @@
1#
2# Loongson Processors' Support
3#
4
5
6cflags-$(CONFIG_CPU_LOONGSON64) += -Wa,--trap
7
8#
9# binutils from v2.25 on and gcc starting from v4.9.0 treat -march=loongson3a
10# as MIPS64 R2; older versions as just R1. This leaves the possibility open
11# that GCC might generate R2 code for -march=loongson3a which then is rejected
12# by GAS. The cc-option can't probe for this behaviour so -march=loongson3a
13# can't easily be used safely within the kbuild framework.
14#
15ifeq ($(call cc-ifversion, -ge, 0409, y), y)
16 ifeq ($(call ld-ifversion, -ge, 225000000, y), y)
17 cflags-$(CONFIG_CPU_LOONGSON64) += \
18 $(call cc-option,-march=loongson3a -U_MIPS_ISA -D_MIPS_ISA=_MIPS_ISA_MIPS64)
19 else
20 cflags-$(CONFIG_CPU_LOONGSON64) += \
21 $(call cc-option,-march=mips64r2,-mips64r2 -U_MIPS_ISA -D_MIPS_ISA=_MIPS_ISA_MIPS64)
22 endif
23else
24 cflags-$(CONFIG_CPU_LOONGSON64) += \
25 $(call cc-option,-march=mips64r2,-mips64r2 -U_MIPS_ISA -D_MIPS_ISA=_MIPS_ISA_MIPS64)
26endif
27
28# Some -march= flags enable MMI instructions, and GCC complains about that
29# support being enabled alongside -msoft-float. Thus explicitly disable MMI.
30cflags-y += $(call cc-option,-mno-loongson-mmi)
31
32#
33# Loongson Machines' Support
34#
35
36cflags-$(CONFIG_MACH_LOONGSON64) += -I$(srctree)/arch/mips/include/asm/mach-loongson64 -mno-branch-likely
37load-$(CONFIG_CPU_LOONGSON64) += 0xffffffff80200000
diff --git a/arch/mips/loongson64/cop2-ex.c b/arch/mips/loongson64/cop2-ex.c
new file mode 100644
index 000000000..00055d4b6
--- /dev/null
+++ b/arch/mips/loongson64/cop2-ex.c
@@ -0,0 +1,341 @@
1/*
2 * This file is subject to the terms and conditions of the GNU General Public
3 * License. See the file "COPYING" in the main directory of this archive
4 * for more details.
5 *
6 * Copyright (C) 2014 Lemote Corporation.
7 * written by Huacai Chen <chenhc@lemote.com>
8 *
9 * based on arch/mips/cavium-octeon/cpu.c
10 * Copyright (C) 2009 Wind River Systems,
11 * written by Ralf Baechle <ralf@linux-mips.org>
12 */
13#include <linux/init.h>
14#include <linux/sched.h>
15#include <linux/notifier.h>
16#include <linux/ptrace.h>
17#include <linux/uaccess.h>
18#include <linux/sched/signal.h>
19
20#include <asm/fpu.h>
21#include <asm/cop2.h>
22#include <asm/inst.h>
23#include <asm/branch.h>
24#include <asm/current.h>
25#include <asm/mipsregs.h>
26#include <asm/unaligned-emul.h>
27
28static int loongson_cu2_call(struct notifier_block *nfb, unsigned long action,
29 void *data)
30{
31 unsigned int res, fpu_owned;
32 unsigned long ra, value, value_next;
33 union mips_instruction insn;
34 int fr = !test_thread_flag(TIF_32BIT_FPREGS);
35 struct pt_regs *regs = (struct pt_regs *)data;
36 void __user *addr = (void __user *)regs->cp0_badvaddr;
37 unsigned int __user *pc = (unsigned int __user *)exception_epc(regs);
38
39 ra = regs->regs[31];
40 __get_user(insn.word, pc);
41
42 switch (action) {
43 case CU2_EXCEPTION:
44 preempt_disable();
45 fpu_owned = __is_fpu_owner();
46 if (!fr)
47 set_c0_status(ST0_CU1 | ST0_CU2);
48 else
49 set_c0_status(ST0_CU1 | ST0_CU2 | ST0_FR);
50 enable_fpu_hazard();
51 KSTK_STATUS(current) |= (ST0_CU1 | ST0_CU2);
52 if (fr)
53 KSTK_STATUS(current) |= ST0_FR;
54 else
55 KSTK_STATUS(current) &= ~ST0_FR;
56 /* If FPU is owned, we needn't init or restore fp */
57 if (!fpu_owned) {
58 set_thread_flag(TIF_USEDFPU);
59 init_fp_ctx(current);
60 _restore_fp(current);
61 }
62 preempt_enable();
63
64 return NOTIFY_STOP; /* Don't call default notifier */
65
66 case CU2_LWC2_OP:
67 if (insn.loongson3_lswc2_format.ls == 0)
68 goto sigbus;
69
70 if (insn.loongson3_lswc2_format.fr == 0) { /* gslq */
71 if (!access_ok(addr, 16))
72 goto sigbus;
73
74 LoadDW(addr, value, res);
75 if (res)
76 goto fault;
77
78 LoadDW(addr + 8, value_next, res);
79 if (res)
80 goto fault;
81
82 regs->regs[insn.loongson3_lswc2_format.rt] = value;
83 regs->regs[insn.loongson3_lswc2_format.rq] = value_next;
84 compute_return_epc(regs);
85 } else { /* gslqc1 */
86 if (!access_ok(addr, 16))
87 goto sigbus;
88
89 lose_fpu(1);
90 LoadDW(addr, value, res);
91 if (res)
92 goto fault;
93
94 LoadDW(addr + 8, value_next, res);
95 if (res)
96 goto fault;
97
98 set_fpr64(&current->thread.fpu.fpr[insn.loongson3_lswc2_format.rt], 0, value);
99 set_fpr64(&current->thread.fpu.fpr[insn.loongson3_lswc2_format.rq], 0, value_next);
100 compute_return_epc(regs);
101 own_fpu(1);
102 }
103 return NOTIFY_STOP; /* Don't call default notifier */
104
105 case CU2_SWC2_OP:
106 if (insn.loongson3_lswc2_format.ls == 0)
107 goto sigbus;
108
109 if (insn.loongson3_lswc2_format.fr == 0) { /* gssq */
110 if (!access_ok(addr, 16))
111 goto sigbus;
112
113 /* write upper 8 bytes first */
114 value_next = regs->regs[insn.loongson3_lswc2_format.rq];
115
116 StoreDW(addr + 8, value_next, res);
117 if (res)
118 goto fault;
119 value = regs->regs[insn.loongson3_lswc2_format.rt];
120
121 StoreDW(addr, value, res);
122 if (res)
123 goto fault;
124
125 compute_return_epc(regs);
126 } else { /* gssqc1 */
127 if (!access_ok(addr, 16))
128 goto sigbus;
129
130 lose_fpu(1);
131 value_next = get_fpr64(&current->thread.fpu.fpr[insn.loongson3_lswc2_format.rq], 0);
132
133 StoreDW(addr + 8, value_next, res);
134 if (res)
135 goto fault;
136
137 value = get_fpr64(&current->thread.fpu.fpr[insn.loongson3_lswc2_format.rt], 0);
138
139 StoreDW(addr, value, res);
140 if (res)
141 goto fault;
142
143 compute_return_epc(regs);
144 own_fpu(1);
145 }
146 return NOTIFY_STOP; /* Don't call default notifier */
147
148 case CU2_LDC2_OP:
149 switch (insn.loongson3_lsdc2_format.opcode1) {
150 /*
151 * Loongson-3 overridden ldc2 instructions.
152 * opcode1 instruction
153 * 0x1 gslhx: load 2 bytes to GPR
154 * 0x2 gslwx: load 4 bytes to GPR
155 * 0x3 gsldx: load 8 bytes to GPR
156 * 0x6 gslwxc1: load 4 bytes to FPR
157 * 0x7 gsldxc1: load 8 bytes to FPR
158 */
159 case 0x1:
160 if (!access_ok(addr, 2))
161 goto sigbus;
162
163 LoadHW(addr, value, res);
164 if (res)
165 goto fault;
166
167 compute_return_epc(regs);
168 regs->regs[insn.loongson3_lsdc2_format.rt] = value;
169 break;
170 case 0x2:
171 if (!access_ok(addr, 4))
172 goto sigbus;
173
174 LoadW(addr, value, res);
175 if (res)
176 goto fault;
177
178 compute_return_epc(regs);
179 regs->regs[insn.loongson3_lsdc2_format.rt] = value;
180 break;
181 case 0x3:
182 if (!access_ok(addr, 8))
183 goto sigbus;
184
185 LoadDW(addr, value, res);
186 if (res)
187 goto fault;
188
189 compute_return_epc(regs);
190 regs->regs[insn.loongson3_lsdc2_format.rt] = value;
191 break;
192 case 0x6:
193 die_if_kernel("Unaligned FP access in kernel code", regs);
194 BUG_ON(!used_math());
195 if (!access_ok(addr, 4))
196 goto sigbus;
197
198 lose_fpu(1);
199 LoadW(addr, value, res);
200 if (res)
201 goto fault;
202
203 set_fpr64(&current->thread.fpu.fpr[insn.loongson3_lsdc2_format.rt], 0, value);
204 compute_return_epc(regs);
205 own_fpu(1);
206
207 break;
208 case 0x7:
209 die_if_kernel("Unaligned FP access in kernel code", regs);
210 BUG_ON(!used_math());
211 if (!access_ok(addr, 8))
212 goto sigbus;
213
214 lose_fpu(1);
215 LoadDW(addr, value, res);
216 if (res)
217 goto fault;
218
219 set_fpr64(&current->thread.fpu.fpr[insn.loongson3_lsdc2_format.rt], 0, value);
220 compute_return_epc(regs);
221 own_fpu(1);
222 break;
223
224 }
225 return NOTIFY_STOP; /* Don't call default notifier */
226
227 case CU2_SDC2_OP:
228 switch (insn.loongson3_lsdc2_format.opcode1) {
229 /*
230 * Loongson-3 overridden sdc2 instructions.
231 * opcode1 instruction
232 * 0x1 gsshx: store 2 bytes from GPR
233 * 0x2 gsswx: store 4 bytes from GPR
234 * 0x3 gssdx: store 8 bytes from GPR
235 * 0x6 gsswxc1: store 4 bytes from FPR
236 * 0x7 gssdxc1: store 8 bytes from FPR
237 */
238 case 0x1:
239 if (!access_ok(addr, 2))
240 goto sigbus;
241
242 compute_return_epc(regs);
243 value = regs->regs[insn.loongson3_lsdc2_format.rt];
244
245 StoreHW(addr, value, res);
246 if (res)
247 goto fault;
248
249 break;
250 case 0x2:
251 if (!access_ok(addr, 4))
252 goto sigbus;
253
254 compute_return_epc(regs);
255 value = regs->regs[insn.loongson3_lsdc2_format.rt];
256
257 StoreW(addr, value, res);
258 if (res)
259 goto fault;
260
261 break;
262 case 0x3:
263 if (!access_ok(addr, 8))
264 goto sigbus;
265
266 compute_return_epc(regs);
267 value = regs->regs[insn.loongson3_lsdc2_format.rt];
268
269 StoreDW(addr, value, res);
270 if (res)
271 goto fault;
272
273 break;
274
275 case 0x6:
276 die_if_kernel("Unaligned FP access in kernel code", regs);
277 BUG_ON(!used_math());
278
279 if (!access_ok(addr, 4))
280 goto sigbus;
281
282 lose_fpu(1);
283 value = get_fpr64(&current->thread.fpu.fpr[insn.loongson3_lsdc2_format.rt], 0);
284
285 StoreW(addr, value, res);
286 if (res)
287 goto fault;
288
289 compute_return_epc(regs);
290 own_fpu(1);
291
292 break;
293 case 0x7:
294 die_if_kernel("Unaligned FP access in kernel code", regs);
295 BUG_ON(!used_math());
296
297 if (!access_ok(addr, 8))
298 goto sigbus;
299
300 lose_fpu(1);
301 value = get_fpr64(&current->thread.fpu.fpr[insn.loongson3_lsdc2_format.rt], 0);
302
303 StoreDW(addr, value, res);
304 if (res)
305 goto fault;
306
307 compute_return_epc(regs);
308 own_fpu(1);
309
310 break;
311 }
312 return NOTIFY_STOP; /* Don't call default notifier */
313 }
314
315 return NOTIFY_OK; /* Let default notifier send signals */
316
317fault:
318 /* roll back jump/branch */
319 regs->regs[31] = ra;
320 regs->cp0_epc = (unsigned long)pc;
321 /* Did we have an exception handler installed? */
322 if (fixup_exception(regs))
323 return NOTIFY_STOP; /* Don't call default notifier */
324
325 die_if_kernel("Unhandled kernel unaligned access", regs);
326 force_sig(SIGSEGV);
327
328 return NOTIFY_STOP; /* Don't call default notifier */
329
330sigbus:
331 die_if_kernel("Unhandled kernel unaligned access", regs);
332 force_sig(SIGBUS);
333
334 return NOTIFY_STOP; /* Don't call default notifier */
335}
336
337static int __init loongson_cu2_setup(void)
338{
339 return cu2_notifier(loongson_cu2_call, 0);
340}
341early_initcall(loongson_cu2_setup);
diff --git a/arch/mips/loongson64/cpucfg-emul.c b/arch/mips/loongson64/cpucfg-emul.c
new file mode 100644
index 000000000..630927e46
--- /dev/null
+++ b/arch/mips/loongson64/cpucfg-emul.c
@@ -0,0 +1,227 @@
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
12static 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
27static u32 get_loongson_fprev(struct cpuinfo_mips *c)
28{
29 return c->fpu_id & LOONGSON_FPREV_MASK;
30}
31
32static 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
50static void probe_uca(struct cpuinfo_mips *c)
51{
52 if (cpu_has_uca())
53 c->loongson3_cpucfg_data[0] |= LOONGSON_CFG1_LSUCA;
54}
55
56static 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
68static 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
86static 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
107static 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
124void 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
222have_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}
diff --git a/arch/mips/loongson64/dma.c b/arch/mips/loongson64/dma.c
new file mode 100644
index 000000000..364f2f27c
--- /dev/null
+++ b/arch/mips/loongson64/dma.c
@@ -0,0 +1,28 @@
1// SPDX-License-Identifier: GPL-2.0
2#include <linux/dma-direct.h>
3#include <linux/init.h>
4#include <linux/swiotlb.h>
5#include <boot_param.h>
6
7dma_addr_t phys_to_dma(struct device *dev, phys_addr_t paddr)
8{
9 /* We extract 2bit node id (bit 44~47, only bit 44~45 used now) from
10 * Loongson-3's 48bit address space and embed it into 40bit */
11 long nid = (paddr >> 44) & 0x3;
12
13 return ((nid << 44) ^ paddr) | (nid << node_id_offset);
14}
15
16phys_addr_t dma_to_phys(struct device *dev, dma_addr_t daddr)
17{
18 /* We extract 2bit node id (bit 44~47, only bit 44~45 used now) from
19 * Loongson-3's 48bit address space and embed it into 40bit */
20 long nid = (daddr >> node_id_offset) & 0x3;
21
22 return ((nid << node_id_offset) ^ daddr) | (nid << 44);
23}
24
25void __init plat_swiotlb_setup(void)
26{
27 swiotlb_init(1);
28}
diff --git a/arch/mips/loongson64/env.c b/arch/mips/loongson64/env.c
new file mode 100644
index 000000000..134cb8e9e
--- /dev/null
+++ b/arch/mips/loongson64/env.c
@@ -0,0 +1,223 @@
1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * Based on Ocelot Linux port, which is
4 * Copyright 2001 MontaVista Software Inc.
5 * Author: jsun@mvista.com or jsun@junsun.net
6 *
7 * Copyright 2003 ICT CAS
8 * Author: Michael Guo <guoyi@ict.ac.cn>
9 *
10 * Copyright (C) 2007 Lemote Inc. & Institute of Computing Technology
11 * Author: Fuxin Zhang, zhangfx@lemote.com
12 *
13 * Copyright (C) 2009 Lemote Inc.
14 * Author: Wu Zhangjin, wuzhangjin@gmail.com
15 */
16#include <linux/export.h>
17#include <linux/pci_ids.h>
18#include <asm/bootinfo.h>
19#include <loongson.h>
20#include <boot_param.h>
21#include <builtin_dtbs.h>
22#include <workarounds.h>
23
24#define HOST_BRIDGE_CONFIG_ADDR ((void __iomem *)TO_UNCAC(0x1a000000))
25
26u32 cpu_clock_freq;
27EXPORT_SYMBOL(cpu_clock_freq);
28struct efi_memory_map_loongson *loongson_memmap;
29struct loongson_system_configuration loongson_sysconf;
30
31u64 loongson_chipcfg[MAX_PACKAGES] = {0xffffffffbfc00180};
32u64 loongson_chiptemp[MAX_PACKAGES];
33u64 loongson_freqctrl[MAX_PACKAGES];
34
35unsigned long long smp_group[4];
36
37const char *get_system_type(void)
38{
39 return "Generic Loongson64 System";
40}
41
42void __init prom_init_env(void)
43{
44 struct boot_params *boot_p;
45 struct loongson_params *loongson_p;
46 struct system_loongson *esys;
47 struct efi_cpuinfo_loongson *ecpu;
48 struct irq_source_routing_table *eirq_source;
49 u32 id;
50 u16 vendor, device;
51
52 /* firmware arguments are initialized in head.S */
53 boot_p = (struct boot_params *)fw_arg2;
54 loongson_p = &(boot_p->efi.smbios.lp);
55
56 esys = (struct system_loongson *)
57 ((u64)loongson_p + loongson_p->system_offset);
58 ecpu = (struct efi_cpuinfo_loongson *)
59 ((u64)loongson_p + loongson_p->cpu_offset);
60 eirq_source = (struct irq_source_routing_table *)
61 ((u64)loongson_p + loongson_p->irq_offset);
62 loongson_memmap = (struct efi_memory_map_loongson *)
63 ((u64)loongson_p + loongson_p->memory_offset);
64
65 cpu_clock_freq = ecpu->cpu_clock_freq;
66 loongson_sysconf.cputype = ecpu->cputype;
67 switch (ecpu->cputype) {
68 case Legacy_3A:
69 case Loongson_3A:
70 loongson_sysconf.cores_per_node = 4;
71 loongson_sysconf.cores_per_package = 4;
72 smp_group[0] = 0x900000003ff01000;
73 smp_group[1] = 0x900010003ff01000;
74 smp_group[2] = 0x900020003ff01000;
75 smp_group[3] = 0x900030003ff01000;
76 loongson_chipcfg[0] = 0x900000001fe00180;
77 loongson_chipcfg[1] = 0x900010001fe00180;
78 loongson_chipcfg[2] = 0x900020001fe00180;
79 loongson_chipcfg[3] = 0x900030001fe00180;
80 loongson_chiptemp[0] = 0x900000001fe0019c;
81 loongson_chiptemp[1] = 0x900010001fe0019c;
82 loongson_chiptemp[2] = 0x900020001fe0019c;
83 loongson_chiptemp[3] = 0x900030001fe0019c;
84 loongson_freqctrl[0] = 0x900000001fe001d0;
85 loongson_freqctrl[1] = 0x900010001fe001d0;
86 loongson_freqctrl[2] = 0x900020001fe001d0;
87 loongson_freqctrl[3] = 0x900030001fe001d0;
88 loongson_sysconf.ht_control_base = 0x90000EFDFB000000;
89 loongson_sysconf.workarounds = WORKAROUND_CPUFREQ;
90 break;
91 case Legacy_3B:
92 case Loongson_3B:
93 loongson_sysconf.cores_per_node = 4; /* One chip has 2 nodes */
94 loongson_sysconf.cores_per_package = 8;
95 smp_group[0] = 0x900000003ff01000;
96 smp_group[1] = 0x900010003ff05000;
97 smp_group[2] = 0x900020003ff09000;
98 smp_group[3] = 0x900030003ff0d000;
99 loongson_chipcfg[0] = 0x900000001fe00180;
100 loongson_chipcfg[1] = 0x900020001fe00180;
101 loongson_chipcfg[2] = 0x900040001fe00180;
102 loongson_chipcfg[3] = 0x900060001fe00180;
103 loongson_chiptemp[0] = 0x900000001fe0019c;
104 loongson_chiptemp[1] = 0x900020001fe0019c;
105 loongson_chiptemp[2] = 0x900040001fe0019c;
106 loongson_chiptemp[3] = 0x900060001fe0019c;
107 loongson_freqctrl[0] = 0x900000001fe001d0;
108 loongson_freqctrl[1] = 0x900020001fe001d0;
109 loongson_freqctrl[2] = 0x900040001fe001d0;
110 loongson_freqctrl[3] = 0x900060001fe001d0;
111 loongson_sysconf.ht_control_base = 0x90001EFDFB000000;
112 loongson_sysconf.workarounds = WORKAROUND_CPUHOTPLUG;
113 break;
114 default:
115 loongson_sysconf.cores_per_node = 1;
116 loongson_sysconf.cores_per_package = 1;
117 loongson_chipcfg[0] = 0x900000001fe00180;
118 }
119
120 loongson_sysconf.nr_cpus = ecpu->nr_cpus;
121 loongson_sysconf.boot_cpu_id = ecpu->cpu_startup_core_id;
122 loongson_sysconf.reserved_cpus_mask = ecpu->reserved_cores_mask;
123 if (ecpu->nr_cpus > NR_CPUS || ecpu->nr_cpus == 0)
124 loongson_sysconf.nr_cpus = NR_CPUS;
125 loongson_sysconf.nr_nodes = (loongson_sysconf.nr_cpus +
126 loongson_sysconf.cores_per_node - 1) /
127 loongson_sysconf.cores_per_node;
128
129 loongson_sysconf.pci_mem_start_addr = eirq_source->pci_mem_start_addr;
130 loongson_sysconf.pci_mem_end_addr = eirq_source->pci_mem_end_addr;
131 loongson_sysconf.pci_io_base = eirq_source->pci_io_start_addr;
132 loongson_sysconf.dma_mask_bits = eirq_source->dma_mask_bits;
133 if (loongson_sysconf.dma_mask_bits < 32 ||
134 loongson_sysconf.dma_mask_bits > 64)
135 loongson_sysconf.dma_mask_bits = 32;
136
137 loongson_sysconf.restart_addr = boot_p->reset_system.ResetWarm;
138 loongson_sysconf.poweroff_addr = boot_p->reset_system.Shutdown;
139 loongson_sysconf.suspend_addr = boot_p->reset_system.DoSuspend;
140
141 loongson_sysconf.vgabios_addr = boot_p->efi.smbios.vga_bios;
142 pr_debug("Shutdown Addr: %llx, Restart Addr: %llx, VBIOS Addr: %llx\n",
143 loongson_sysconf.poweroff_addr, loongson_sysconf.restart_addr,
144 loongson_sysconf.vgabios_addr);
145
146 memset(loongson_sysconf.ecname, 0, 32);
147 if (esys->has_ec)
148 memcpy(loongson_sysconf.ecname, esys->ec_name, 32);
149 loongson_sysconf.workarounds |= esys->workarounds;
150
151 loongson_sysconf.nr_uarts = esys->nr_uarts;
152 if (esys->nr_uarts < 1 || esys->nr_uarts > MAX_UARTS)
153 loongson_sysconf.nr_uarts = 1;
154 memcpy(loongson_sysconf.uarts, esys->uarts,
155 sizeof(struct uart_device) * loongson_sysconf.nr_uarts);
156
157 loongson_sysconf.nr_sensors = esys->nr_sensors;
158 if (loongson_sysconf.nr_sensors > MAX_SENSORS)
159 loongson_sysconf.nr_sensors = 0;
160 if (loongson_sysconf.nr_sensors)
161 memcpy(loongson_sysconf.sensors, esys->sensors,
162 sizeof(struct sensor_device) * loongson_sysconf.nr_sensors);
163 pr_info("CpuClock = %u\n", cpu_clock_freq);
164
165 /* Read the ID of PCI host bridge to detect bridge type */
166 id = readl(HOST_BRIDGE_CONFIG_ADDR);
167 vendor = id & 0xffff;
168 device = (id >> 16) & 0xffff;
169
170 switch (vendor) {
171 case PCI_VENDOR_ID_LOONGSON:
172 pr_info("The bridge chip is LS7A\n");
173 loongson_sysconf.bridgetype = LS7A;
174 loongson_sysconf.early_config = ls7a_early_config;
175 break;
176 case PCI_VENDOR_ID_AMD:
177 case PCI_VENDOR_ID_ATI:
178 pr_info("The bridge chip is RS780E or SR5690\n");
179 loongson_sysconf.bridgetype = RS780E;
180 loongson_sysconf.early_config = rs780e_early_config;
181 break;
182 default:
183 pr_info("The bridge chip is VIRTUAL\n");
184 loongson_sysconf.bridgetype = VIRTUAL;
185 loongson_sysconf.early_config = virtual_early_config;
186 loongson_fdt_blob = __dtb_loongson64v_4core_virtio_begin;
187 break;
188 }
189
190 if ((read_c0_prid() & PRID_IMP_MASK) == PRID_IMP_LOONGSON_64C) {
191 switch (read_c0_prid() & PRID_REV_MASK) {
192 case PRID_REV_LOONGSON3A_R1:
193 case PRID_REV_LOONGSON3A_R2_0:
194 case PRID_REV_LOONGSON3A_R2_1:
195 case PRID_REV_LOONGSON3A_R3_0:
196 case PRID_REV_LOONGSON3A_R3_1:
197 switch (loongson_sysconf.bridgetype) {
198 case LS7A:
199 loongson_fdt_blob = __dtb_loongson64c_4core_ls7a_begin;
200 break;
201 case RS780E:
202 loongson_fdt_blob = __dtb_loongson64c_4core_rs780e_begin;
203 break;
204 default:
205 break;
206 }
207 break;
208 case PRID_REV_LOONGSON3B_R1:
209 case PRID_REV_LOONGSON3B_R2:
210 if (loongson_sysconf.bridgetype == RS780E)
211 loongson_fdt_blob = __dtb_loongson64c_8core_rs780e_begin;
212 break;
213 default:
214 break;
215 }
216 } else if ((read_c0_prid() & PRID_IMP_MASK) == PRID_IMP_LOONGSON_64G) {
217 if (loongson_sysconf.bridgetype == LS7A)
218 loongson_fdt_blob = __dtb_loongson64g_4core_ls7a_begin;
219 }
220
221 if (!loongson_fdt_blob)
222 pr_err("Failed to determine built-in Loongson64 dtb\n");
223}
diff --git a/arch/mips/loongson64/hpet.c b/arch/mips/loongson64/hpet.c
new file mode 100644
index 000000000..e42825925
--- /dev/null
+++ b/arch/mips/loongson64/hpet.c
@@ -0,0 +1,285 @@
1// SPDX-License-Identifier: GPL-2.0
2#include <linux/init.h>
3#include <linux/pci.h>
4#include <linux/percpu.h>
5#include <linux/delay.h>
6#include <linux/spinlock.h>
7#include <linux/interrupt.h>
8
9#include <asm/hpet.h>
10#include <asm/time.h>
11
12#define SMBUS_CFG_BASE (loongson_sysconf.ht_control_base + 0x0300a000)
13#define SMBUS_PCI_REG40 0x40
14#define SMBUS_PCI_REG64 0x64
15#define SMBUS_PCI_REGB4 0xb4
16
17#define HPET_MIN_CYCLES 16
18#define HPET_MIN_PROG_DELTA (HPET_MIN_CYCLES * 12)
19
20static DEFINE_SPINLOCK(hpet_lock);
21DEFINE_PER_CPU(struct clock_event_device, hpet_clockevent_device);
22
23static unsigned int smbus_read(int offset)
24{
25 return *(volatile unsigned int *)(SMBUS_CFG_BASE + offset);
26}
27
28static void smbus_write(int offset, int data)
29{
30 *(volatile unsigned int *)(SMBUS_CFG_BASE + offset) = data;
31}
32
33static void smbus_enable(int offset, int bit)
34{
35 unsigned int cfg = smbus_read(offset);
36
37 cfg |= bit;
38 smbus_write(offset, cfg);
39}
40
41static int hpet_read(int offset)
42{
43 return *(volatile unsigned int *)(HPET_MMIO_ADDR + offset);
44}
45
46static void hpet_write(int offset, int data)
47{
48 *(volatile unsigned int *)(HPET_MMIO_ADDR + offset) = data;
49}
50
51static void hpet_start_counter(void)
52{
53 unsigned int cfg = hpet_read(HPET_CFG);
54
55 cfg |= HPET_CFG_ENABLE;
56 hpet_write(HPET_CFG, cfg);
57}
58
59static void hpet_stop_counter(void)
60{
61 unsigned int cfg = hpet_read(HPET_CFG);
62
63 cfg &= ~HPET_CFG_ENABLE;
64 hpet_write(HPET_CFG, cfg);
65}
66
67static void hpet_reset_counter(void)
68{
69 hpet_write(HPET_COUNTER, 0);
70 hpet_write(HPET_COUNTER + 4, 0);
71}
72
73static void hpet_restart_counter(void)
74{
75 hpet_stop_counter();
76 hpet_reset_counter();
77 hpet_start_counter();
78}
79
80static void hpet_enable_legacy_int(void)
81{
82 /* Do nothing on Loongson-3 */
83}
84
85static int hpet_set_state_periodic(struct clock_event_device *evt)
86{
87 int cfg;
88
89 spin_lock(&hpet_lock);
90
91 pr_info("set clock event to periodic mode!\n");
92 /* stop counter */
93 hpet_stop_counter();
94
95 /* enables the timer0 to generate a periodic interrupt */
96 cfg = hpet_read(HPET_T0_CFG);
97 cfg &= ~HPET_TN_LEVEL;
98 cfg |= HPET_TN_ENABLE | HPET_TN_PERIODIC | HPET_TN_SETVAL |
99 HPET_TN_32BIT;
100 hpet_write(HPET_T0_CFG, cfg);
101
102 /* set the comparator */
103 hpet_write(HPET_T0_CMP, HPET_COMPARE_VAL);
104 udelay(1);
105 hpet_write(HPET_T0_CMP, HPET_COMPARE_VAL);
106
107 /* start counter */
108 hpet_start_counter();
109
110 spin_unlock(&hpet_lock);
111 return 0;
112}
113
114static int hpet_set_state_shutdown(struct clock_event_device *evt)
115{
116 int cfg;
117
118 spin_lock(&hpet_lock);
119
120 cfg = hpet_read(HPET_T0_CFG);
121 cfg &= ~HPET_TN_ENABLE;
122 hpet_write(HPET_T0_CFG, cfg);
123
124 spin_unlock(&hpet_lock);
125 return 0;
126}
127
128static int hpet_set_state_oneshot(struct clock_event_device *evt)
129{
130 int cfg;
131
132 spin_lock(&hpet_lock);
133
134 pr_info("set clock event to one shot mode!\n");
135 cfg = hpet_read(HPET_T0_CFG);
136 /*
137 * set timer0 type
138 * 1 : periodic interrupt
139 * 0 : non-periodic(oneshot) interrupt
140 */
141 cfg &= ~HPET_TN_PERIODIC;
142 cfg |= HPET_TN_ENABLE | HPET_TN_32BIT;
143 hpet_write(HPET_T0_CFG, cfg);
144
145 spin_unlock(&hpet_lock);
146 return 0;
147}
148
149static int hpet_tick_resume(struct clock_event_device *evt)
150{
151 spin_lock(&hpet_lock);
152 hpet_enable_legacy_int();
153 spin_unlock(&hpet_lock);
154
155 return 0;
156}
157
158static int hpet_next_event(unsigned long delta,
159 struct clock_event_device *evt)
160{
161 u32 cnt;
162 s32 res;
163
164 cnt = hpet_read(HPET_COUNTER);
165 cnt += (u32) delta;
166 hpet_write(HPET_T0_CMP, cnt);
167
168 res = (s32)(cnt - hpet_read(HPET_COUNTER));
169
170 return res < HPET_MIN_CYCLES ? -ETIME : 0;
171}
172
173static irqreturn_t hpet_irq_handler(int irq, void *data)
174{
175 int is_irq;
176 struct clock_event_device *cd;
177 unsigned int cpu = smp_processor_id();
178
179 is_irq = hpet_read(HPET_STATUS);
180 if (is_irq & HPET_T0_IRS) {
181 /* clear the TIMER0 irq status register */
182 hpet_write(HPET_STATUS, HPET_T0_IRS);
183 cd = &per_cpu(hpet_clockevent_device, cpu);
184 cd->event_handler(cd);
185 return IRQ_HANDLED;
186 }
187 return IRQ_NONE;
188}
189
190/*
191 * hpet address assignation and irq setting should be done in bios.
192 * but pmon don't do this, we just setup here directly.
193 * The operation under is normal. unfortunately, hpet_setup process
194 * is before pci initialize.
195 *
196 * {
197 * struct pci_dev *pdev;
198 *
199 * pdev = pci_get_device(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_SBX00_SMBUS, NULL);
200 * pci_write_config_word(pdev, SMBUS_PCI_REGB4, HPET_ADDR);
201 *
202 * ...
203 * }
204 */
205static void hpet_setup(void)
206{
207 /* set hpet base address */
208 smbus_write(SMBUS_PCI_REGB4, HPET_ADDR);
209
210 /* enable decoding of access to HPET MMIO*/
211 smbus_enable(SMBUS_PCI_REG40, (1 << 28));
212
213 /* HPET irq enable */
214 smbus_enable(SMBUS_PCI_REG64, (1 << 10));
215
216 hpet_enable_legacy_int();
217}
218
219void __init setup_hpet_timer(void)
220{
221 unsigned long flags = IRQF_NOBALANCING | IRQF_TIMER;
222 unsigned int cpu = smp_processor_id();
223 struct clock_event_device *cd;
224
225 hpet_setup();
226
227 cd = &per_cpu(hpet_clockevent_device, cpu);
228 cd->name = "hpet";
229 cd->rating = 100;
230 cd->features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT;
231 cd->set_state_shutdown = hpet_set_state_shutdown;
232 cd->set_state_periodic = hpet_set_state_periodic;
233 cd->set_state_oneshot = hpet_set_state_oneshot;
234 cd->tick_resume = hpet_tick_resume;
235 cd->set_next_event = hpet_next_event;
236 cd->irq = HPET_T0_IRQ;
237 cd->cpumask = cpumask_of(cpu);
238 clockevent_set_clock(cd, HPET_FREQ);
239 cd->max_delta_ns = clockevent_delta2ns(0x7fffffff, cd);
240 cd->max_delta_ticks = 0x7fffffff;
241 cd->min_delta_ns = clockevent_delta2ns(HPET_MIN_PROG_DELTA, cd);
242 cd->min_delta_ticks = HPET_MIN_PROG_DELTA;
243
244 clockevents_register_device(cd);
245 if (request_irq(HPET_T0_IRQ, hpet_irq_handler, flags, "hpet", NULL))
246 pr_err("Failed to request irq %d (hpet)\n", HPET_T0_IRQ);
247 pr_info("hpet clock event device register\n");
248}
249
250static u64 hpet_read_counter(struct clocksource *cs)
251{
252 return (u64)hpet_read(HPET_COUNTER);
253}
254
255static void hpet_suspend(struct clocksource *cs)
256{
257}
258
259static void hpet_resume(struct clocksource *cs)
260{
261 hpet_setup();
262 hpet_restart_counter();
263}
264
265static struct clocksource csrc_hpet = {
266 .name = "hpet",
267 /* mips clocksource rating is less than 300, so hpet is better. */
268 .rating = 300,
269 .read = hpet_read_counter,
270 .mask = CLOCKSOURCE_MASK(32),
271 /* oneshot mode work normal with this flag */
272 .flags = CLOCK_SOURCE_IS_CONTINUOUS,
273 .suspend = hpet_suspend,
274 .resume = hpet_resume,
275 .mult = 0,
276 .shift = 10,
277};
278
279int __init init_hpet_clocksource(void)
280{
281 csrc_hpet.mult = clocksource_hz2mult(HPET_FREQ, csrc_hpet.shift);
282 return clocksource_register_hz(&csrc_hpet, HPET_FREQ);
283}
284
285arch_initcall(init_hpet_clocksource);
diff --git a/arch/mips/loongson64/init.c b/arch/mips/loongson64/init.c
new file mode 100644
index 000000000..052cce6a8
--- /dev/null
+++ b/arch/mips/loongson64/init.c
@@ -0,0 +1,149 @@
1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * Copyright (C) 2009 Lemote Inc.
4 * Author: Wu Zhangjin, wuzhangjin@gmail.com
5 */
6
7#include <linux/irqchip.h>
8#include <linux/logic_pio.h>
9#include <linux/memblock.h>
10#include <linux/of.h>
11#include <linux/of_address.h>
12#include <asm/bootinfo.h>
13#include <asm/traps.h>
14#include <asm/smp-ops.h>
15#include <asm/cacheflush.h>
16#include <asm/fw/fw.h>
17
18#include <loongson.h>
19#include <boot_param.h>
20
21#define NODE_ID_OFFSET_ADDR ((void __iomem *)TO_UNCAC(0x1001041c))
22
23u32 node_id_offset;
24
25static void __init mips_nmi_setup(void)
26{
27 void *base;
28 extern char except_vec_nmi[];
29
30 base = (void *)(CAC_BASE + 0x380);
31 memcpy(base, except_vec_nmi, 0x80);
32 flush_icache_range((unsigned long)base, (unsigned long)base + 0x80);
33}
34
35void ls7a_early_config(void)
36{
37 node_id_offset = ((readl(NODE_ID_OFFSET_ADDR) >> 8) & 0x1f) + 36;
38}
39
40void rs780e_early_config(void)
41{
42 node_id_offset = 37;
43}
44
45void virtual_early_config(void)
46{
47 node_id_offset = 44;
48}
49
50void __init prom_init(void)
51{
52 fw_init_cmdline();
53 prom_init_env();
54
55 /* init base address of io space */
56 set_io_port_base(PCI_IOBASE);
57
58 loongson_sysconf.early_config();
59
60 prom_init_numa_memory();
61
62 /* Hardcode to CPU UART 0 */
63 setup_8250_early_printk_port(TO_UNCAC(LOONGSON_REG_BASE + 0x1e0), 0, 1024);
64
65 register_smp_ops(&loongson3_smp_ops);
66 board_nmi_handler_setup = mips_nmi_setup;
67}
68
69void __init prom_free_prom_memory(void)
70{
71}
72
73static int __init add_legacy_isa_io(struct fwnode_handle *fwnode, resource_size_t hw_start,
74 resource_size_t size)
75{
76 int ret = 0;
77 struct logic_pio_hwaddr *range;
78 unsigned long vaddr;
79
80 range = kzalloc(sizeof(*range), GFP_ATOMIC);
81 if (!range)
82 return -ENOMEM;
83
84 range->fwnode = fwnode;
85 range->size = size = round_up(size, PAGE_SIZE);
86 range->hw_start = hw_start;
87 range->flags = LOGIC_PIO_CPU_MMIO;
88
89 ret = logic_pio_register_range(range);
90 if (ret) {
91 kfree(range);
92 return ret;
93 }
94
95 /* Legacy ISA must placed at the start of PCI_IOBASE */
96 if (range->io_start != 0) {
97 logic_pio_unregister_range(range);
98 kfree(range);
99 return -EINVAL;
100 }
101
102 vaddr = PCI_IOBASE + range->io_start;
103
104 ioremap_page_range(vaddr, vaddr + size, hw_start, pgprot_device(PAGE_KERNEL));
105
106 return 0;
107}
108
109static __init void reserve_pio_range(void)
110{
111 struct device_node *np;
112
113 for_each_node_by_name(np, "isa") {
114 struct of_range range;
115 struct of_range_parser parser;
116
117 pr_info("ISA Bridge: %pOF\n", np);
118
119 if (of_range_parser_init(&parser, np)) {
120 pr_info("Failed to parse resources.\n");
121 break;
122 }
123
124 for_each_of_range(&parser, &range) {
125 switch (range.flags & IORESOURCE_TYPE_BITS) {
126 case IORESOURCE_IO:
127 pr_info(" IO 0x%016llx..0x%016llx -> 0x%016llx\n",
128 range.cpu_addr,
129 range.cpu_addr + range.size - 1,
130 range.bus_addr);
131 if (add_legacy_isa_io(&np->fwnode, range.cpu_addr, range.size))
132 pr_warn("Failed to reserve legacy IO in Logic PIO\n");
133 break;
134 case IORESOURCE_MEM:
135 pr_info(" MEM 0x%016llx..0x%016llx -> 0x%016llx\n",
136 range.cpu_addr,
137 range.cpu_addr + range.size - 1,
138 range.bus_addr);
139 break;
140 }
141 }
142 }
143}
144
145void __init arch_init_irq(void)
146{
147 reserve_pio_range();
148 irqchip_init();
149}
diff --git a/arch/mips/loongson64/numa.c b/arch/mips/loongson64/numa.c
new file mode 100644
index 000000000..e4c461df3
--- /dev/null
+++ b/arch/mips/loongson64/numa.c
@@ -0,0 +1,252 @@
1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * Copyright (C) 2010 Loongson Inc. & Lemote Inc. &
4 * Institute of Computing Technology
5 * Author: Xiang Gao, gaoxiang@ict.ac.cn
6 * Huacai Chen, chenhc@lemote.com
7 * Xiaofu Meng, Shuangshuang Zhang
8 */
9#include <linux/init.h>
10#include <linux/kernel.h>
11#include <linux/mm.h>
12#include <linux/mmzone.h>
13#include <linux/export.h>
14#include <linux/nodemask.h>
15#include <linux/swap.h>
16#include <linux/memblock.h>
17#include <linux/pfn.h>
18#include <linux/highmem.h>
19#include <asm/page.h>
20#include <asm/pgalloc.h>
21#include <asm/sections.h>
22#include <linux/irq.h>
23#include <asm/bootinfo.h>
24#include <asm/mc146818-time.h>
25#include <asm/time.h>
26#include <asm/wbflush.h>
27#include <boot_param.h>
28
29static struct pglist_data prealloc__node_data[MAX_NUMNODES];
30unsigned char __node_distances[MAX_NUMNODES][MAX_NUMNODES];
31EXPORT_SYMBOL(__node_distances);
32struct pglist_data *__node_data[MAX_NUMNODES];
33EXPORT_SYMBOL(__node_data);
34
35cpumask_t __node_cpumask[MAX_NUMNODES];
36EXPORT_SYMBOL(__node_cpumask);
37
38static void enable_lpa(void)
39{
40 unsigned long value;
41
42 value = __read_32bit_c0_register($16, 3);
43 value |= 0x00000080;
44 __write_32bit_c0_register($16, 3, value);
45 value = __read_32bit_c0_register($16, 3);
46 pr_info("CP0_Config3: CP0 16.3 (0x%lx)\n", value);
47
48 value = __read_32bit_c0_register($5, 1);
49 value |= 0x20000000;
50 __write_32bit_c0_register($5, 1, value);
51 value = __read_32bit_c0_register($5, 1);
52 pr_info("CP0_PageGrain: CP0 5.1 (0x%lx)\n", value);
53}
54
55static void cpu_node_probe(void)
56{
57 int i;
58
59 nodes_clear(node_possible_map);
60 nodes_clear(node_online_map);
61 for (i = 0; i < loongson_sysconf.nr_nodes; i++) {
62 node_set_state(num_online_nodes(), N_POSSIBLE);
63 node_set_online(num_online_nodes());
64 }
65
66 pr_info("NUMA: Discovered %d cpus on %d nodes\n",
67 loongson_sysconf.nr_cpus, num_online_nodes());
68}
69
70static int __init compute_node_distance(int row, int col)
71{
72 int package_row = row * loongson_sysconf.cores_per_node /
73 loongson_sysconf.cores_per_package;
74 int package_col = col * loongson_sysconf.cores_per_node /
75 loongson_sysconf.cores_per_package;
76
77 if (col == row)
78 return LOCAL_DISTANCE;
79 else if (package_row == package_col)
80 return 40;
81 else
82 return 100;
83}
84
85static void __init init_topology_matrix(void)
86{
87 int row, col;
88
89 for (row = 0; row < MAX_NUMNODES; row++)
90 for (col = 0; col < MAX_NUMNODES; col++)
91 __node_distances[row][col] = -1;
92
93 for_each_online_node(row) {
94 for_each_online_node(col) {
95 __node_distances[row][col] =
96 compute_node_distance(row, col);
97 }
98 }
99}
100
101static void __init szmem(unsigned int node)
102{
103 u32 i, mem_type;
104 static unsigned long num_physpages;
105 u64 node_id, node_psize, start_pfn, end_pfn, mem_start, mem_size;
106
107 /* Parse memory information and activate */
108 for (i = 0; i < loongson_memmap->nr_map; i++) {
109 node_id = loongson_memmap->map[i].node_id;
110 if (node_id != node)
111 continue;
112
113 mem_type = loongson_memmap->map[i].mem_type;
114 mem_size = loongson_memmap->map[i].mem_size;
115 mem_start = loongson_memmap->map[i].mem_start;
116
117 switch (mem_type) {
118 case SYSTEM_RAM_LOW:
119 start_pfn = ((node_id << 44) + mem_start) >> PAGE_SHIFT;
120 node_psize = (mem_size << 20) >> PAGE_SHIFT;
121 end_pfn = start_pfn + node_psize;
122 num_physpages += node_psize;
123 pr_info("Node%d: mem_type:%d, mem_start:0x%llx, mem_size:0x%llx MB\n",
124 (u32)node_id, mem_type, mem_start, mem_size);
125 pr_info(" start_pfn:0x%llx, end_pfn:0x%llx, num_physpages:0x%lx\n",
126 start_pfn, end_pfn, num_physpages);
127 memblock_add_node(PFN_PHYS(start_pfn),
128 PFN_PHYS(node_psize), node);
129 break;
130 case SYSTEM_RAM_HIGH:
131 start_pfn = ((node_id << 44) + mem_start) >> PAGE_SHIFT;
132 node_psize = (mem_size << 20) >> PAGE_SHIFT;
133 end_pfn = start_pfn + node_psize;
134 num_physpages += node_psize;
135 pr_info("Node%d: mem_type:%d, mem_start:0x%llx, mem_size:0x%llx MB\n",
136 (u32)node_id, mem_type, mem_start, mem_size);
137 pr_info(" start_pfn:0x%llx, end_pfn:0x%llx, num_physpages:0x%lx\n",
138 start_pfn, end_pfn, num_physpages);
139 memblock_add_node(PFN_PHYS(start_pfn),
140 PFN_PHYS(node_psize), node);
141 break;
142 case SYSTEM_RAM_RESERVED:
143 pr_info("Node%d: mem_type:%d, mem_start:0x%llx, mem_size:0x%llx MB\n",
144 (u32)node_id, mem_type, mem_start, mem_size);
145 memblock_reserve(((node_id << 44) + mem_start),
146 mem_size << 20);
147 break;
148 }
149 }
150}
151
152static void __init node_mem_init(unsigned int node)
153{
154 unsigned long node_addrspace_offset;
155 unsigned long start_pfn, end_pfn;
156
157 node_addrspace_offset = nid_to_addrbase(node);
158 pr_info("Node%d's addrspace_offset is 0x%lx\n",
159 node, node_addrspace_offset);
160
161 get_pfn_range_for_nid(node, &start_pfn, &end_pfn);
162 pr_info("Node%d: start_pfn=0x%lx, end_pfn=0x%lx\n",
163 node, start_pfn, end_pfn);
164
165 __node_data[node] = prealloc__node_data + node;
166
167 NODE_DATA(node)->node_start_pfn = start_pfn;
168 NODE_DATA(node)->node_spanned_pages = end_pfn - start_pfn;
169
170 if (node == 0) {
171 /* kernel end address */
172 unsigned long kernel_end_pfn = PFN_UP(__pa_symbol(&_end));
173
174 /* used by finalize_initrd() */
175 max_low_pfn = end_pfn;
176
177 /* Reserve the kernel text/data/bss */
178 memblock_reserve(start_pfn << PAGE_SHIFT,
179 ((kernel_end_pfn - start_pfn) << PAGE_SHIFT));
180
181 /* Reserve 0xfe000000~0xffffffff for RS780E integrated GPU */
182 if (node_end_pfn(0) >= (0xffffffff >> PAGE_SHIFT))
183 memblock_reserve((node_addrspace_offset | 0xfe000000),
184 32 << 20);
185
186 /* Reserve pfn range 0~node[0]->node_start_pfn */
187 memblock_reserve(0, PAGE_SIZE * start_pfn);
188 }
189}
190
191static __init void prom_meminit(void)
192{
193 unsigned int node, cpu, active_cpu = 0;
194
195 cpu_node_probe();
196 init_topology_matrix();
197
198 for (node = 0; node < loongson_sysconf.nr_nodes; node++) {
199 if (node_online(node)) {
200 szmem(node);
201 node_mem_init(node);
202 cpumask_clear(&__node_cpumask[node]);
203 }
204 }
205 max_low_pfn = PHYS_PFN(memblock_end_of_DRAM());
206
207 for (cpu = 0; cpu < loongson_sysconf.nr_cpus; cpu++) {
208 node = cpu / loongson_sysconf.cores_per_node;
209 if (node >= num_online_nodes())
210 node = 0;
211
212 if (loongson_sysconf.reserved_cpus_mask & (1<<cpu))
213 continue;
214
215 cpumask_set_cpu(active_cpu, &__node_cpumask[node]);
216 pr_info("NUMA: set cpumask cpu %d on node %d\n", active_cpu, node);
217
218 active_cpu++;
219 }
220}
221
222void __init paging_init(void)
223{
224 unsigned long zones_size[MAX_NR_ZONES] = {0, };
225
226 pagetable_init();
227 zones_size[ZONE_DMA32] = MAX_DMA32_PFN;
228 zones_size[ZONE_NORMAL] = max_low_pfn;
229 free_area_init(zones_size);
230}
231
232void __init mem_init(void)
233{
234 high_memory = (void *) __va(get_num_physpages() << PAGE_SHIFT);
235 memblock_free_all();
236 setup_zero_pages(); /* This comes from node 0 */
237 mem_init_print_info(NULL);
238}
239
240/* All PCI device belongs to logical Node-0 */
241int pcibus_to_node(struct pci_bus *bus)
242{
243 return 0;
244}
245EXPORT_SYMBOL(pcibus_to_node);
246
247void __init prom_init_numa_memory(void)
248{
249 enable_lpa();
250 prom_meminit();
251}
252EXPORT_SYMBOL(prom_init_numa_memory);
diff --git a/arch/mips/loongson64/platform.c b/arch/mips/loongson64/platform.c
new file mode 100644
index 000000000..9674ae136
--- /dev/null
+++ b/arch/mips/loongson64/platform.c
@@ -0,0 +1,42 @@
1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * Copyright (C) 2009 Lemote Inc.
4 * Author: Wu Zhangjin, wuzhangjin@gmail.com
5 * Xiang Yu, xiangy@lemote.com
6 * Chen Huacai, chenhc@lemote.com
7 */
8
9#include <linux/err.h>
10#include <linux/slab.h>
11#include <linux/platform_device.h>
12#include <asm/bootinfo.h>
13#include <boot_param.h>
14#include <loongson_hwmon.h>
15#include <workarounds.h>
16
17static int __init loongson3_platform_init(void)
18{
19 int i;
20 struct platform_device *pdev;
21
22 if (loongson_sysconf.ecname[0] != '\0')
23 platform_device_register_simple(loongson_sysconf.ecname, -1, NULL, 0);
24
25 for (i = 0; i < loongson_sysconf.nr_sensors; i++) {
26 if (loongson_sysconf.sensors[i].type > SENSOR_FAN)
27 continue;
28
29 pdev = kzalloc(sizeof(struct platform_device), GFP_KERNEL);
30 if (!pdev)
31 return -ENOMEM;
32
33 pdev->name = loongson_sysconf.sensors[i].name;
34 pdev->id = loongson_sysconf.sensors[i].id;
35 pdev->dev.platform_data = &loongson_sysconf.sensors[i];
36 platform_device_register(pdev);
37 }
38
39 return 0;
40}
41
42arch_initcall(loongson3_platform_init);
diff --git a/arch/mips/loongson64/pm.c b/arch/mips/loongson64/pm.c
new file mode 100644
index 000000000..7c8556f09
--- /dev/null
+++ b/arch/mips/loongson64/pm.c
@@ -0,0 +1,104 @@
1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * loongson-specific suspend support
4 *
5 * Copyright (C) 2009 Lemote Inc.
6 * Author: Wu Zhangjin <wuzhangjin@gmail.com>
7 */
8#include <linux/suspend.h>
9#include <linux/interrupt.h>
10#include <linux/pm.h>
11
12#include <asm/i8259.h>
13#include <asm/mipsregs.h>
14
15#include <loongson.h>
16
17static unsigned int __maybe_unused cached_master_mask; /* i8259A */
18static unsigned int __maybe_unused cached_slave_mask;
19static unsigned int __maybe_unused cached_bonito_irq_mask; /* bonito */
20
21void arch_suspend_disable_irqs(void)
22{
23 /* disable all mips events */
24 local_irq_disable();
25
26#ifdef CONFIG_I8259
27 /* disable all events of i8259A */
28 cached_slave_mask = inb(PIC_SLAVE_IMR);
29 cached_master_mask = inb(PIC_MASTER_IMR);
30
31 outb(0xff, PIC_SLAVE_IMR);
32 inb(PIC_SLAVE_IMR);
33 outb(0xff, PIC_MASTER_IMR);
34 inb(PIC_MASTER_IMR);
35#endif
36 /* disable all events of bonito */
37 cached_bonito_irq_mask = LOONGSON_INTEN;
38 LOONGSON_INTENCLR = 0xffff;
39 (void)LOONGSON_INTENCLR;
40}
41
42void arch_suspend_enable_irqs(void)
43{
44 /* enable all mips events */
45 local_irq_enable();
46#ifdef CONFIG_I8259
47 /* only enable the cached events of i8259A */
48 outb(cached_slave_mask, PIC_SLAVE_IMR);
49 outb(cached_master_mask, PIC_MASTER_IMR);
50#endif
51 /* enable all cached events of bonito */
52 LOONGSON_INTENSET = cached_bonito_irq_mask;
53 (void)LOONGSON_INTENSET;
54}
55
56/*
57 * Setup the board-specific events for waking up loongson from wait mode
58 */
59void __weak setup_wakeup_events(void)
60{
61}
62
63void __weak mach_suspend(void)
64{
65}
66
67void __weak mach_resume(void)
68{
69}
70
71static int loongson_pm_enter(suspend_state_t state)
72{
73 mach_suspend();
74
75 mach_resume();
76
77 return 0;
78}
79
80static int loongson_pm_valid_state(suspend_state_t state)
81{
82 switch (state) {
83 case PM_SUSPEND_ON:
84 case PM_SUSPEND_STANDBY:
85 case PM_SUSPEND_MEM:
86 return 1;
87
88 default:
89 return 0;
90 }
91}
92
93static const struct platform_suspend_ops loongson_pm_ops = {
94 .valid = loongson_pm_valid_state,
95 .enter = loongson_pm_enter,
96};
97
98static int __init loongson_pm_init(void)
99{
100 suspend_set_ops(&loongson_pm_ops);
101
102 return 0;
103}
104arch_initcall(loongson_pm_init);
diff --git a/arch/mips/loongson64/reset.c b/arch/mips/loongson64/reset.c
new file mode 100644
index 000000000..3bb8a1ed9
--- /dev/null
+++ b/arch/mips/loongson64/reset.c
@@ -0,0 +1,59 @@
1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 *
4 * Copyright (C) 2007 Lemote, Inc. & Institute of Computing Technology
5 * Author: Fuxin Zhang, zhangfx@lemote.com
6 * Copyright (C) 2009 Lemote, Inc.
7 * Author: Zhangjin Wu, wuzhangjin@gmail.com
8 */
9#include <linux/init.h>
10#include <linux/pm.h>
11
12#include <asm/idle.h>
13#include <asm/reboot.h>
14
15#include <loongson.h>
16#include <boot_param.h>
17
18static void loongson_restart(char *command)
19{
20
21 void (*fw_restart)(void) = (void *)loongson_sysconf.restart_addr;
22
23 fw_restart();
24 while (1) {
25 if (cpu_wait)
26 cpu_wait();
27 }
28}
29
30static void loongson_poweroff(void)
31{
32 void (*fw_poweroff)(void) = (void *)loongson_sysconf.poweroff_addr;
33
34 fw_poweroff();
35 while (1) {
36 if (cpu_wait)
37 cpu_wait();
38 }
39}
40
41static void loongson_halt(void)
42{
43 pr_notice("\n\n** You can safely turn off the power now **\n\n");
44 while (1) {
45 if (cpu_wait)
46 cpu_wait();
47 }
48}
49
50static int __init mips_reboot_setup(void)
51{
52 _machine_restart = loongson_restart;
53 _machine_halt = loongson_halt;
54 pm_power_off = loongson_poweroff;
55
56 return 0;
57}
58
59arch_initcall(mips_reboot_setup);
diff --git a/arch/mips/loongson64/setup.c b/arch/mips/loongson64/setup.c
new file mode 100644
index 000000000..6fe3ffffc
--- /dev/null
+++ b/arch/mips/loongson64/setup.c
@@ -0,0 +1,46 @@
1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * Copyright (C) 2007 Lemote Inc. & Institute of Computing Technology
4 * Author: Fuxin Zhang, zhangfx@lemote.com
5 */
6#include <linux/export.h>
7#include <linux/init.h>
8
9#include <asm/wbflush.h>
10#include <asm/bootinfo.h>
11#include <linux/libfdt.h>
12#include <linux/of_fdt.h>
13
14#include <asm/prom.h>
15
16#include <loongson.h>
17
18void *loongson_fdt_blob;
19
20static void wbflush_loongson(void)
21{
22 asm(".set\tpush\n\t"
23 ".set\tnoreorder\n\t"
24 ".set mips3\n\t"
25 "sync\n\t"
26 "nop\n\t"
27 ".set\tpop\n\t"
28 ".set mips0\n\t");
29}
30
31void (*__wbflush)(void) = wbflush_loongson;
32EXPORT_SYMBOL(__wbflush);
33
34void __init plat_mem_setup(void)
35{
36 if (loongson_fdt_blob)
37 __dt_setup_arch(loongson_fdt_blob);
38}
39
40void __init device_tree_init(void)
41{
42 if (!initial_boot_params)
43 return;
44
45 unflatten_and_copy_device_tree();
46}
diff --git a/arch/mips/loongson64/smp.c b/arch/mips/loongson64/smp.c
new file mode 100644
index 000000000..e744e1bee
--- /dev/null
+++ b/arch/mips/loongson64/smp.c
@@ -0,0 +1,806 @@
1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * Copyright (C) 2010, 2011, 2012, Lemote, Inc.
4 * Author: Chen Huacai, chenhc@lemote.com
5 */
6
7#include <irq.h>
8#include <linux/init.h>
9#include <linux/cpu.h>
10#include <linux/sched.h>
11#include <linux/sched/hotplug.h>
12#include <linux/sched/task_stack.h>
13#include <linux/smp.h>
14#include <linux/cpufreq.h>
15#include <linux/kexec.h>
16#include <asm/processor.h>
17#include <asm/time.h>
18#include <asm/tlbflush.h>
19#include <asm/cacheflush.h>
20#include <loongson.h>
21#include <loongson_regs.h>
22#include <workarounds.h>
23
24#include "smp.h"
25
26DEFINE_PER_CPU(int, cpu_state);
27
28#define LS_IPI_IRQ (MIPS_CPU_IRQ_BASE + 6)
29
30static void *ipi_set0_regs[16];
31static void *ipi_clear0_regs[16];
32static void *ipi_status0_regs[16];
33static void *ipi_en0_regs[16];
34static void *ipi_mailbox_buf[16];
35static uint32_t core0_c0count[NR_CPUS];
36
37/* read a 32bit value from ipi register */
38#define loongson3_ipi_read32(addr) readl(addr)
39/* read a 64bit value from ipi register */
40#define loongson3_ipi_read64(addr) readq(addr)
41/* write a 32bit value to ipi register */
42#define loongson3_ipi_write32(action, addr) \
43 do { \
44 writel(action, addr); \
45 __wbflush(); \
46 } while (0)
47/* write a 64bit value to ipi register */
48#define loongson3_ipi_write64(action, addr) \
49 do { \
50 writeq(action, addr); \
51 __wbflush(); \
52 } while (0)
53
54u32 (*ipi_read_clear)(int cpu);
55void (*ipi_write_action)(int cpu, u32 action);
56
57static u32 csr_ipi_read_clear(int cpu)
58{
59 u32 action;
60
61 /* Load the ipi register to figure out what we're supposed to do */
62 action = csr_readl(LOONGSON_CSR_IPI_STATUS);
63 /* Clear the ipi register to clear the interrupt */
64 csr_writel(action, LOONGSON_CSR_IPI_CLEAR);
65
66 return action;
67}
68
69static void csr_ipi_write_action(int cpu, u32 action)
70{
71 unsigned int irq = 0;
72
73 while ((irq = ffs(action))) {
74 uint32_t val = CSR_IPI_SEND_BLOCK;
75 val |= (irq - 1);
76 val |= (cpu << CSR_IPI_SEND_CPU_SHIFT);
77 csr_writel(val, LOONGSON_CSR_IPI_SEND);
78 action &= ~BIT(irq - 1);
79 }
80}
81
82static u32 legacy_ipi_read_clear(int cpu)
83{
84 u32 action;
85
86 /* Load the ipi register to figure out what we're supposed to do */
87 action = loongson3_ipi_read32(ipi_status0_regs[cpu_logical_map(cpu)]);
88 /* Clear the ipi register to clear the interrupt */
89 loongson3_ipi_write32(action, ipi_clear0_regs[cpu_logical_map(cpu)]);
90
91 return action;
92}
93
94static void legacy_ipi_write_action(int cpu, u32 action)
95{
96 loongson3_ipi_write32((u32)action, ipi_set0_regs[cpu]);
97}
98
99static void csr_ipi_probe(void)
100{
101 if (cpu_has_csr() && csr_readl(LOONGSON_CSR_FEATURES) & LOONGSON_CSRF_IPI) {
102 ipi_read_clear = csr_ipi_read_clear;
103 ipi_write_action = csr_ipi_write_action;
104 } else {
105 ipi_read_clear = legacy_ipi_read_clear;
106 ipi_write_action = legacy_ipi_write_action;
107 }
108}
109
110static void ipi_set0_regs_init(void)
111{
112 ipi_set0_regs[0] = (void *)
113 (SMP_CORE_GROUP0_BASE + SMP_CORE0_OFFSET + SET0);
114 ipi_set0_regs[1] = (void *)
115 (SMP_CORE_GROUP0_BASE + SMP_CORE1_OFFSET + SET0);
116 ipi_set0_regs[2] = (void *)
117 (SMP_CORE_GROUP0_BASE + SMP_CORE2_OFFSET + SET0);
118 ipi_set0_regs[3] = (void *)
119 (SMP_CORE_GROUP0_BASE + SMP_CORE3_OFFSET + SET0);
120 ipi_set0_regs[4] = (void *)
121 (SMP_CORE_GROUP1_BASE + SMP_CORE0_OFFSET + SET0);
122 ipi_set0_regs[5] = (void *)
123 (SMP_CORE_GROUP1_BASE + SMP_CORE1_OFFSET + SET0);
124 ipi_set0_regs[6] = (void *)
125 (SMP_CORE_GROUP1_BASE + SMP_CORE2_OFFSET + SET0);
126 ipi_set0_regs[7] = (void *)
127 (SMP_CORE_GROUP1_BASE + SMP_CORE3_OFFSET + SET0);
128 ipi_set0_regs[8] = (void *)
129 (SMP_CORE_GROUP2_BASE + SMP_CORE0_OFFSET + SET0);
130 ipi_set0_regs[9] = (void *)
131 (SMP_CORE_GROUP2_BASE + SMP_CORE1_OFFSET + SET0);
132 ipi_set0_regs[10] = (void *)
133 (SMP_CORE_GROUP2_BASE + SMP_CORE2_OFFSET + SET0);
134 ipi_set0_regs[11] = (void *)
135 (SMP_CORE_GROUP2_BASE + SMP_CORE3_OFFSET + SET0);
136 ipi_set0_regs[12] = (void *)
137 (SMP_CORE_GROUP3_BASE + SMP_CORE0_OFFSET + SET0);
138 ipi_set0_regs[13] = (void *)
139 (SMP_CORE_GROUP3_BASE + SMP_CORE1_OFFSET + SET0);
140 ipi_set0_regs[14] = (void *)
141 (SMP_CORE_GROUP3_BASE + SMP_CORE2_OFFSET + SET0);
142 ipi_set0_regs[15] = (void *)
143 (SMP_CORE_GROUP3_BASE + SMP_CORE3_OFFSET + SET0);
144}
145
146static void ipi_clear0_regs_init(void)
147{
148 ipi_clear0_regs[0] = (void *)
149 (SMP_CORE_GROUP0_BASE + SMP_CORE0_OFFSET + CLEAR0);
150 ipi_clear0_regs[1] = (void *)
151 (SMP_CORE_GROUP0_BASE + SMP_CORE1_OFFSET + CLEAR0);
152 ipi_clear0_regs[2] = (void *)
153 (SMP_CORE_GROUP0_BASE + SMP_CORE2_OFFSET + CLEAR0);
154 ipi_clear0_regs[3] = (void *)
155 (SMP_CORE_GROUP0_BASE + SMP_CORE3_OFFSET + CLEAR0);
156 ipi_clear0_regs[4] = (void *)
157 (SMP_CORE_GROUP1_BASE + SMP_CORE0_OFFSET + CLEAR0);
158 ipi_clear0_regs[5] = (void *)
159 (SMP_CORE_GROUP1_BASE + SMP_CORE1_OFFSET + CLEAR0);
160 ipi_clear0_regs[6] = (void *)
161 (SMP_CORE_GROUP1_BASE + SMP_CORE2_OFFSET + CLEAR0);
162 ipi_clear0_regs[7] = (void *)
163 (SMP_CORE_GROUP1_BASE + SMP_CORE3_OFFSET + CLEAR0);
164 ipi_clear0_regs[8] = (void *)
165 (SMP_CORE_GROUP2_BASE + SMP_CORE0_OFFSET + CLEAR0);
166 ipi_clear0_regs[9] = (void *)
167 (SMP_CORE_GROUP2_BASE + SMP_CORE1_OFFSET + CLEAR0);
168 ipi_clear0_regs[10] = (void *)
169 (SMP_CORE_GROUP2_BASE + SMP_CORE2_OFFSET + CLEAR0);
170 ipi_clear0_regs[11] = (void *)
171 (SMP_CORE_GROUP2_BASE + SMP_CORE3_OFFSET + CLEAR0);
172 ipi_clear0_regs[12] = (void *)
173 (SMP_CORE_GROUP3_BASE + SMP_CORE0_OFFSET + CLEAR0);
174 ipi_clear0_regs[13] = (void *)
175 (SMP_CORE_GROUP3_BASE + SMP_CORE1_OFFSET + CLEAR0);
176 ipi_clear0_regs[14] = (void *)
177 (SMP_CORE_GROUP3_BASE + SMP_CORE2_OFFSET + CLEAR0);
178 ipi_clear0_regs[15] = (void *)
179 (SMP_CORE_GROUP3_BASE + SMP_CORE3_OFFSET + CLEAR0);
180}
181
182static void ipi_status0_regs_init(void)
183{
184 ipi_status0_regs[0] = (void *)
185 (SMP_CORE_GROUP0_BASE + SMP_CORE0_OFFSET + STATUS0);
186 ipi_status0_regs[1] = (void *)
187 (SMP_CORE_GROUP0_BASE + SMP_CORE1_OFFSET + STATUS0);
188 ipi_status0_regs[2] = (void *)
189 (SMP_CORE_GROUP0_BASE + SMP_CORE2_OFFSET + STATUS0);
190 ipi_status0_regs[3] = (void *)
191 (SMP_CORE_GROUP0_BASE + SMP_CORE3_OFFSET + STATUS0);
192 ipi_status0_regs[4] = (void *)
193 (SMP_CORE_GROUP1_BASE + SMP_CORE0_OFFSET + STATUS0);
194 ipi_status0_regs[5] = (void *)
195 (SMP_CORE_GROUP1_BASE + SMP_CORE1_OFFSET + STATUS0);
196 ipi_status0_regs[6] = (void *)
197 (SMP_CORE_GROUP1_BASE + SMP_CORE2_OFFSET + STATUS0);
198 ipi_status0_regs[7] = (void *)
199 (SMP_CORE_GROUP1_BASE + SMP_CORE3_OFFSET + STATUS0);
200 ipi_status0_regs[8] = (void *)
201 (SMP_CORE_GROUP2_BASE + SMP_CORE0_OFFSET + STATUS0);
202 ipi_status0_regs[9] = (void *)
203 (SMP_CORE_GROUP2_BASE + SMP_CORE1_OFFSET + STATUS0);
204 ipi_status0_regs[10] = (void *)
205 (SMP_CORE_GROUP2_BASE + SMP_CORE2_OFFSET + STATUS0);
206 ipi_status0_regs[11] = (void *)
207 (SMP_CORE_GROUP2_BASE + SMP_CORE3_OFFSET + STATUS0);
208 ipi_status0_regs[12] = (void *)
209 (SMP_CORE_GROUP3_BASE + SMP_CORE0_OFFSET + STATUS0);
210 ipi_status0_regs[13] = (void *)
211 (SMP_CORE_GROUP3_BASE + SMP_CORE1_OFFSET + STATUS0);
212 ipi_status0_regs[14] = (void *)
213 (SMP_CORE_GROUP3_BASE + SMP_CORE2_OFFSET + STATUS0);
214 ipi_status0_regs[15] = (void *)
215 (SMP_CORE_GROUP3_BASE + SMP_CORE3_OFFSET + STATUS0);
216}
217
218static void ipi_en0_regs_init(void)
219{
220 ipi_en0_regs[0] = (void *)
221 (SMP_CORE_GROUP0_BASE + SMP_CORE0_OFFSET + EN0);
222 ipi_en0_regs[1] = (void *)
223 (SMP_CORE_GROUP0_BASE + SMP_CORE1_OFFSET + EN0);
224 ipi_en0_regs[2] = (void *)
225 (SMP_CORE_GROUP0_BASE + SMP_CORE2_OFFSET + EN0);
226 ipi_en0_regs[3] = (void *)
227 (SMP_CORE_GROUP0_BASE + SMP_CORE3_OFFSET + EN0);
228 ipi_en0_regs[4] = (void *)
229 (SMP_CORE_GROUP1_BASE + SMP_CORE0_OFFSET + EN0);
230 ipi_en0_regs[5] = (void *)
231 (SMP_CORE_GROUP1_BASE + SMP_CORE1_OFFSET + EN0);
232 ipi_en0_regs[6] = (void *)
233 (SMP_CORE_GROUP1_BASE + SMP_CORE2_OFFSET + EN0);
234 ipi_en0_regs[7] = (void *)
235 (SMP_CORE_GROUP1_BASE + SMP_CORE3_OFFSET + EN0);
236 ipi_en0_regs[8] = (void *)
237 (SMP_CORE_GROUP2_BASE + SMP_CORE0_OFFSET + EN0);
238 ipi_en0_regs[9] = (void *)
239 (SMP_CORE_GROUP2_BASE + SMP_CORE1_OFFSET + EN0);
240 ipi_en0_regs[10] = (void *)
241 (SMP_CORE_GROUP2_BASE + SMP_CORE2_OFFSET + EN0);
242 ipi_en0_regs[11] = (void *)
243 (SMP_CORE_GROUP2_BASE + SMP_CORE3_OFFSET + EN0);
244 ipi_en0_regs[12] = (void *)
245 (SMP_CORE_GROUP3_BASE + SMP_CORE0_OFFSET + EN0);
246 ipi_en0_regs[13] = (void *)
247 (SMP_CORE_GROUP3_BASE + SMP_CORE1_OFFSET + EN0);
248 ipi_en0_regs[14] = (void *)
249 (SMP_CORE_GROUP3_BASE + SMP_CORE2_OFFSET + EN0);
250 ipi_en0_regs[15] = (void *)
251 (SMP_CORE_GROUP3_BASE + SMP_CORE3_OFFSET + EN0);
252}
253
254static void ipi_mailbox_buf_init(void)
255{
256 ipi_mailbox_buf[0] = (void *)
257 (SMP_CORE_GROUP0_BASE + SMP_CORE0_OFFSET + BUF);
258 ipi_mailbox_buf[1] = (void *)
259 (SMP_CORE_GROUP0_BASE + SMP_CORE1_OFFSET + BUF);
260 ipi_mailbox_buf[2] = (void *)
261 (SMP_CORE_GROUP0_BASE + SMP_CORE2_OFFSET + BUF);
262 ipi_mailbox_buf[3] = (void *)
263 (SMP_CORE_GROUP0_BASE + SMP_CORE3_OFFSET + BUF);
264 ipi_mailbox_buf[4] = (void *)
265 (SMP_CORE_GROUP1_BASE + SMP_CORE0_OFFSET + BUF);
266 ipi_mailbox_buf[5] = (void *)
267 (SMP_CORE_GROUP1_BASE + SMP_CORE1_OFFSET + BUF);
268 ipi_mailbox_buf[6] = (void *)
269 (SMP_CORE_GROUP1_BASE + SMP_CORE2_OFFSET + BUF);
270 ipi_mailbox_buf[7] = (void *)
271 (SMP_CORE_GROUP1_BASE + SMP_CORE3_OFFSET + BUF);
272 ipi_mailbox_buf[8] = (void *)
273 (SMP_CORE_GROUP2_BASE + SMP_CORE0_OFFSET + BUF);
274 ipi_mailbox_buf[9] = (void *)
275 (SMP_CORE_GROUP2_BASE + SMP_CORE1_OFFSET + BUF);
276 ipi_mailbox_buf[10] = (void *)
277 (SMP_CORE_GROUP2_BASE + SMP_CORE2_OFFSET + BUF);
278 ipi_mailbox_buf[11] = (void *)
279 (SMP_CORE_GROUP2_BASE + SMP_CORE3_OFFSET + BUF);
280 ipi_mailbox_buf[12] = (void *)
281 (SMP_CORE_GROUP3_BASE + SMP_CORE0_OFFSET + BUF);
282 ipi_mailbox_buf[13] = (void *)
283 (SMP_CORE_GROUP3_BASE + SMP_CORE1_OFFSET + BUF);
284 ipi_mailbox_buf[14] = (void *)
285 (SMP_CORE_GROUP3_BASE + SMP_CORE2_OFFSET + BUF);
286 ipi_mailbox_buf[15] = (void *)
287 (SMP_CORE_GROUP3_BASE + SMP_CORE3_OFFSET + BUF);
288}
289
290/*
291 * Simple enough, just poke the appropriate ipi register
292 */
293static void loongson3_send_ipi_single(int cpu, unsigned int action)
294{
295 ipi_write_action(cpu_logical_map(cpu), (u32)action);
296}
297
298static void
299loongson3_send_ipi_mask(const struct cpumask *mask, unsigned int action)
300{
301 unsigned int i;
302
303 for_each_cpu(i, mask)
304 ipi_write_action(cpu_logical_map(i), (u32)action);
305}
306
307
308static irqreturn_t loongson3_ipi_interrupt(int irq, void *dev_id)
309{
310 int i, cpu = smp_processor_id();
311 unsigned int action, c0count;
312
313 action = ipi_read_clear(cpu);
314
315 if (action & SMP_RESCHEDULE_YOURSELF)
316 scheduler_ipi();
317
318 if (action & SMP_CALL_FUNCTION) {
319 irq_enter();
320 generic_smp_call_function_interrupt();
321 irq_exit();
322 }
323
324 if (action & SMP_ASK_C0COUNT) {
325 BUG_ON(cpu != 0);
326 c0count = read_c0_count();
327 c0count = c0count ? c0count : 1;
328 for (i = 1; i < nr_cpu_ids; i++)
329 core0_c0count[i] = c0count;
330 __wbflush(); /* Let others see the result ASAP */
331 }
332
333 return IRQ_HANDLED;
334}
335
336#define MAX_LOOPS 800
337/*
338 * SMP init and finish on secondary CPUs
339 */
340static void loongson3_init_secondary(void)
341{
342 int i;
343 uint32_t initcount;
344 unsigned int cpu = smp_processor_id();
345 unsigned int imask = STATUSF_IP7 | STATUSF_IP6 |
346 STATUSF_IP3 | STATUSF_IP2;
347
348 /* Set interrupt mask, but don't enable */
349 change_c0_status(ST0_IM, imask);
350
351 for (i = 0; i < num_possible_cpus(); i++)
352 loongson3_ipi_write32(0xffffffff, ipi_en0_regs[cpu_logical_map(i)]);
353
354 per_cpu(cpu_state, cpu) = CPU_ONLINE;
355 cpu_set_core(&cpu_data[cpu],
356 cpu_logical_map(cpu) % loongson_sysconf.cores_per_package);
357 cpu_data[cpu].package =
358 cpu_logical_map(cpu) / loongson_sysconf.cores_per_package;
359
360 i = 0;
361 core0_c0count[cpu] = 0;
362 loongson3_send_ipi_single(0, SMP_ASK_C0COUNT);
363 while (!core0_c0count[cpu]) {
364 i++;
365 cpu_relax();
366 }
367
368 if (i > MAX_LOOPS)
369 i = MAX_LOOPS;
370 if (cpu_data[cpu].package)
371 initcount = core0_c0count[cpu] + i;
372 else /* Local access is faster for loops */
373 initcount = core0_c0count[cpu] + i/2;
374
375 write_c0_count(initcount);
376}
377
378static void loongson3_smp_finish(void)
379{
380 int cpu = smp_processor_id();
381
382 write_c0_compare(read_c0_count() + mips_hpt_frequency/HZ);
383 local_irq_enable();
384 loongson3_ipi_write64(0,
385 ipi_mailbox_buf[cpu_logical_map(cpu)] + 0x0);
386 pr_info("CPU#%d finished, CP0_ST=%x\n",
387 smp_processor_id(), read_c0_status());
388}
389
390static void __init loongson3_smp_setup(void)
391{
392 int i = 0, num = 0; /* i: physical id, num: logical id */
393
394 init_cpu_possible(cpu_none_mask);
395
396 /* For unified kernel, NR_CPUS is the maximum possible value,
397 * loongson_sysconf.nr_cpus is the really present value */
398 while (i < loongson_sysconf.nr_cpus) {
399 if (loongson_sysconf.reserved_cpus_mask & (1<<i)) {
400 /* Reserved physical CPU cores */
401 __cpu_number_map[i] = -1;
402 } else {
403 __cpu_number_map[i] = num;
404 __cpu_logical_map[num] = i;
405 set_cpu_possible(num, true);
406 num++;
407 }
408 i++;
409 }
410 pr_info("Detected %i available CPU(s)\n", num);
411
412 while (num < loongson_sysconf.nr_cpus) {
413 __cpu_logical_map[num] = -1;
414 num++;
415 }
416
417 csr_ipi_probe();
418 ipi_set0_regs_init();
419 ipi_clear0_regs_init();
420 ipi_status0_regs_init();
421 ipi_en0_regs_init();
422 ipi_mailbox_buf_init();
423 cpu_set_core(&cpu_data[0],
424 cpu_logical_map(0) % loongson_sysconf.cores_per_package);
425 cpu_data[0].package = cpu_logical_map(0) / loongson_sysconf.cores_per_package;
426}
427
428static void __init loongson3_prepare_cpus(unsigned int max_cpus)
429{
430 if (request_irq(LS_IPI_IRQ, loongson3_ipi_interrupt,
431 IRQF_PERCPU | IRQF_NO_SUSPEND, "SMP_IPI", NULL))
432 pr_err("Failed to request IPI IRQ\n");
433 init_cpu_present(cpu_possible_mask);
434 per_cpu(cpu_state, smp_processor_id()) = CPU_ONLINE;
435}
436
437/*
438 * Setup the PC, SP, and GP of a secondary processor and start it runing!
439 */
440static int loongson3_boot_secondary(int cpu, struct task_struct *idle)
441{
442 unsigned long startargs[4];
443
444 pr_info("Booting CPU#%d...\n", cpu);
445
446 /* startargs[] are initial PC, SP and GP for secondary CPU */
447 startargs[0] = (unsigned long)&smp_bootstrap;
448 startargs[1] = (unsigned long)__KSTK_TOS(idle);
449 startargs[2] = (unsigned long)task_thread_info(idle);
450 startargs[3] = 0;
451
452 pr_debug("CPU#%d, func_pc=%lx, sp=%lx, gp=%lx\n",
453 cpu, startargs[0], startargs[1], startargs[2]);
454
455 loongson3_ipi_write64(startargs[3],
456 ipi_mailbox_buf[cpu_logical_map(cpu)] + 0x18);
457 loongson3_ipi_write64(startargs[2],
458 ipi_mailbox_buf[cpu_logical_map(cpu)] + 0x10);
459 loongson3_ipi_write64(startargs[1],
460 ipi_mailbox_buf[cpu_logical_map(cpu)] + 0x8);
461 loongson3_ipi_write64(startargs[0],
462 ipi_mailbox_buf[cpu_logical_map(cpu)] + 0x0);
463 return 0;
464}
465
466#ifdef CONFIG_HOTPLUG_CPU
467
468static int loongson3_cpu_disable(void)
469{
470 unsigned long flags;
471 unsigned int cpu = smp_processor_id();
472
473 if (cpu == 0)
474 return -EBUSY;
475
476 set_cpu_online(cpu, false);
477 calculate_cpu_foreign_map();
478 local_irq_save(flags);
479 irq_cpu_offline();
480 clear_c0_status(ST0_IM);
481 local_irq_restore(flags);
482 local_flush_tlb_all();
483
484 return 0;
485}
486
487
488static void loongson3_cpu_die(unsigned int cpu)
489{
490 while (per_cpu(cpu_state, cpu) != CPU_DEAD)
491 cpu_relax();
492
493 mb();
494}
495
496/* To shutdown a core in Loongson 3, the target core should go to CKSEG1 and
497 * flush all L1 entries at first. Then, another core (usually Core 0) can
498 * safely disable the clock of the target core. loongson3_play_dead() is
499 * called via CKSEG1 (uncached and unmmaped) */
500static void loongson3_type1_play_dead(int *state_addr)
501{
502 register int val;
503 register long cpuid, core, node, count;
504 register void *addr, *base, *initfunc;
505
506 __asm__ __volatile__(
507 " .set push \n"
508 " .set noreorder \n"
509 " li %[addr], 0x80000000 \n" /* KSEG0 */
510 "1: cache 0, 0(%[addr]) \n" /* flush L1 ICache */
511 " cache 0, 1(%[addr]) \n"
512 " cache 0, 2(%[addr]) \n"
513 " cache 0, 3(%[addr]) \n"
514 " cache 1, 0(%[addr]) \n" /* flush L1 DCache */
515 " cache 1, 1(%[addr]) \n"
516 " cache 1, 2(%[addr]) \n"
517 " cache 1, 3(%[addr]) \n"
518 " addiu %[sets], %[sets], -1 \n"
519 " bnez %[sets], 1b \n"
520 " addiu %[addr], %[addr], 0x20 \n"
521 " li %[val], 0x7 \n" /* *state_addr = CPU_DEAD; */
522 " sw %[val], (%[state_addr]) \n"
523 " sync \n"
524 " cache 21, (%[state_addr]) \n" /* flush entry of *state_addr */
525 " .set pop \n"
526 : [addr] "=&r" (addr), [val] "=&r" (val)
527 : [state_addr] "r" (state_addr),
528 [sets] "r" (cpu_data[smp_processor_id()].dcache.sets));
529
530 __asm__ __volatile__(
531 " .set push \n"
532 " .set noreorder \n"
533 " .set mips64 \n"
534 " mfc0 %[cpuid], $15, 1 \n"
535 " andi %[cpuid], 0x3ff \n"
536 " dli %[base], 0x900000003ff01000 \n"
537 " andi %[core], %[cpuid], 0x3 \n"
538 " sll %[core], 8 \n" /* get core id */
539 " or %[base], %[base], %[core] \n"
540 " andi %[node], %[cpuid], 0xc \n"
541 " dsll %[node], 42 \n" /* get node id */
542 " or %[base], %[base], %[node] \n"
543 "1: li %[count], 0x100 \n" /* wait for init loop */
544 "2: bnez %[count], 2b \n" /* limit mailbox access */
545 " addiu %[count], -1 \n"
546 " ld %[initfunc], 0x20(%[base]) \n" /* get PC via mailbox */
547 " beqz %[initfunc], 1b \n"
548 " nop \n"
549 " ld $sp, 0x28(%[base]) \n" /* get SP via mailbox */
550 " ld $gp, 0x30(%[base]) \n" /* get GP via mailbox */
551 " ld $a1, 0x38(%[base]) \n"
552 " jr %[initfunc] \n" /* jump to initial PC */
553 " nop \n"
554 " .set pop \n"
555 : [core] "=&r" (core), [node] "=&r" (node),
556 [base] "=&r" (base), [cpuid] "=&r" (cpuid),
557 [count] "=&r" (count), [initfunc] "=&r" (initfunc)
558 : /* No Input */
559 : "a1");
560}
561
562static void loongson3_type2_play_dead(int *state_addr)
563{
564 register int val;
565 register long cpuid, core, node, count;
566 register void *addr, *base, *initfunc;
567
568 __asm__ __volatile__(
569 " .set push \n"
570 " .set noreorder \n"
571 " li %[addr], 0x80000000 \n" /* KSEG0 */
572 "1: cache 0, 0(%[addr]) \n" /* flush L1 ICache */
573 " cache 0, 1(%[addr]) \n"
574 " cache 0, 2(%[addr]) \n"
575 " cache 0, 3(%[addr]) \n"
576 " cache 1, 0(%[addr]) \n" /* flush L1 DCache */
577 " cache 1, 1(%[addr]) \n"
578 " cache 1, 2(%[addr]) \n"
579 " cache 1, 3(%[addr]) \n"
580 " addiu %[sets], %[sets], -1 \n"
581 " bnez %[sets], 1b \n"
582 " addiu %[addr], %[addr], 0x20 \n"
583 " li %[val], 0x7 \n" /* *state_addr = CPU_DEAD; */
584 " sw %[val], (%[state_addr]) \n"
585 " sync \n"
586 " cache 21, (%[state_addr]) \n" /* flush entry of *state_addr */
587 " .set pop \n"
588 : [addr] "=&r" (addr), [val] "=&r" (val)
589 : [state_addr] "r" (state_addr),
590 [sets] "r" (cpu_data[smp_processor_id()].dcache.sets));
591
592 __asm__ __volatile__(
593 " .set push \n"
594 " .set noreorder \n"
595 " .set mips64 \n"
596 " mfc0 %[cpuid], $15, 1 \n"
597 " andi %[cpuid], 0x3ff \n"
598 " dli %[base], 0x900000003ff01000 \n"
599 " andi %[core], %[cpuid], 0x3 \n"
600 " sll %[core], 8 \n" /* get core id */
601 " or %[base], %[base], %[core] \n"
602 " andi %[node], %[cpuid], 0xc \n"
603 " dsll %[node], 42 \n" /* get node id */
604 " or %[base], %[base], %[node] \n"
605 " dsrl %[node], 30 \n" /* 15:14 */
606 " or %[base], %[base], %[node] \n"
607 "1: li %[count], 0x100 \n" /* wait for init loop */
608 "2: bnez %[count], 2b \n" /* limit mailbox access */
609 " addiu %[count], -1 \n"
610 " ld %[initfunc], 0x20(%[base]) \n" /* get PC via mailbox */
611 " beqz %[initfunc], 1b \n"
612 " nop \n"
613 " ld $sp, 0x28(%[base]) \n" /* get SP via mailbox */
614 " ld $gp, 0x30(%[base]) \n" /* get GP via mailbox */
615 " ld $a1, 0x38(%[base]) \n"
616 " jr %[initfunc] \n" /* jump to initial PC */
617 " nop \n"
618 " .set pop \n"
619 : [core] "=&r" (core), [node] "=&r" (node),
620 [base] "=&r" (base), [cpuid] "=&r" (cpuid),
621 [count] "=&r" (count), [initfunc] "=&r" (initfunc)
622 : /* No Input */
623 : "a1");
624}
625
626static void loongson3_type3_play_dead(int *state_addr)
627{
628 register int val;
629 register long cpuid, core, node, count;
630 register void *addr, *base, *initfunc;
631
632 __asm__ __volatile__(
633 " .set push \n"
634 " .set noreorder \n"
635 " li %[addr], 0x80000000 \n" /* KSEG0 */
636 "1: cache 0, 0(%[addr]) \n" /* flush L1 ICache */
637 " cache 0, 1(%[addr]) \n"
638 " cache 0, 2(%[addr]) \n"
639 " cache 0, 3(%[addr]) \n"
640 " cache 1, 0(%[addr]) \n" /* flush L1 DCache */
641 " cache 1, 1(%[addr]) \n"
642 " cache 1, 2(%[addr]) \n"
643 " cache 1, 3(%[addr]) \n"
644 " addiu %[sets], %[sets], -1 \n"
645 " bnez %[sets], 1b \n"
646 " addiu %[addr], %[addr], 0x40 \n"
647 " li %[addr], 0x80000000 \n" /* KSEG0 */
648 "2: cache 2, 0(%[addr]) \n" /* flush L1 VCache */
649 " cache 2, 1(%[addr]) \n"
650 " cache 2, 2(%[addr]) \n"
651 " cache 2, 3(%[addr]) \n"
652 " cache 2, 4(%[addr]) \n"
653 " cache 2, 5(%[addr]) \n"
654 " cache 2, 6(%[addr]) \n"
655 " cache 2, 7(%[addr]) \n"
656 " cache 2, 8(%[addr]) \n"
657 " cache 2, 9(%[addr]) \n"
658 " cache 2, 10(%[addr]) \n"
659 " cache 2, 11(%[addr]) \n"
660 " cache 2, 12(%[addr]) \n"
661 " cache 2, 13(%[addr]) \n"
662 " cache 2, 14(%[addr]) \n"
663 " cache 2, 15(%[addr]) \n"
664 " addiu %[vsets], %[vsets], -1 \n"
665 " bnez %[vsets], 2b \n"
666 " addiu %[addr], %[addr], 0x40 \n"
667 " li %[val], 0x7 \n" /* *state_addr = CPU_DEAD; */
668 " sw %[val], (%[state_addr]) \n"
669 " sync \n"
670 " cache 21, (%[state_addr]) \n" /* flush entry of *state_addr */
671 " .set pop \n"
672 : [addr] "=&r" (addr), [val] "=&r" (val)
673 : [state_addr] "r" (state_addr),
674 [sets] "r" (cpu_data[smp_processor_id()].dcache.sets),
675 [vsets] "r" (cpu_data[smp_processor_id()].vcache.sets));
676
677 __asm__ __volatile__(
678 " .set push \n"
679 " .set noreorder \n"
680 " .set mips64 \n"
681 " mfc0 %[cpuid], $15, 1 \n"
682 " andi %[cpuid], 0x3ff \n"
683 " dli %[base], 0x900000003ff01000 \n"
684 " andi %[core], %[cpuid], 0x3 \n"
685 " sll %[core], 8 \n" /* get core id */
686 " or %[base], %[base], %[core] \n"
687 " andi %[node], %[cpuid], 0xc \n"
688 " dsll %[node], 42 \n" /* get node id */
689 " or %[base], %[base], %[node] \n"
690 "1: li %[count], 0x100 \n" /* wait for init loop */
691 "2: bnez %[count], 2b \n" /* limit mailbox access */
692 " addiu %[count], -1 \n"
693 " ld %[initfunc], 0x20(%[base]) \n" /* get PC via mailbox */
694 " beqz %[initfunc], 1b \n"
695 " nop \n"
696 " ld $sp, 0x28(%[base]) \n" /* get SP via mailbox */
697 " ld $gp, 0x30(%[base]) \n" /* get GP via mailbox */
698 " ld $a1, 0x38(%[base]) \n"
699 " jr %[initfunc] \n" /* jump to initial PC */
700 " nop \n"
701 " .set pop \n"
702 : [core] "=&r" (core), [node] "=&r" (node),
703 [base] "=&r" (base), [cpuid] "=&r" (cpuid),
704 [count] "=&r" (count), [initfunc] "=&r" (initfunc)
705 : /* No Input */
706 : "a1");
707}
708
709void play_dead(void)
710{
711 int prid_imp, prid_rev, *state_addr;
712 unsigned int cpu = smp_processor_id();
713 void (*play_dead_at_ckseg1)(int *);
714
715 idle_task_exit();
716
717 prid_imp = read_c0_prid() & PRID_IMP_MASK;
718 prid_rev = read_c0_prid() & PRID_REV_MASK;
719
720 if (prid_imp == PRID_IMP_LOONGSON_64G) {
721 play_dead_at_ckseg1 =
722 (void *)CKSEG1ADDR((unsigned long)loongson3_type3_play_dead);
723 goto out;
724 }
725
726 switch (prid_rev) {
727 case PRID_REV_LOONGSON3A_R1:
728 default:
729 play_dead_at_ckseg1 =
730 (void *)CKSEG1ADDR((unsigned long)loongson3_type1_play_dead);
731 break;
732 case PRID_REV_LOONGSON3B_R1:
733 case PRID_REV_LOONGSON3B_R2:
734 play_dead_at_ckseg1 =
735 (void *)CKSEG1ADDR((unsigned long)loongson3_type2_play_dead);
736 break;
737 case PRID_REV_LOONGSON3A_R2_0:
738 case PRID_REV_LOONGSON3A_R2_1:
739 case PRID_REV_LOONGSON3A_R3_0:
740 case PRID_REV_LOONGSON3A_R3_1:
741 play_dead_at_ckseg1 =
742 (void *)CKSEG1ADDR((unsigned long)loongson3_type3_play_dead);
743 break;
744 }
745
746out:
747 state_addr = &per_cpu(cpu_state, cpu);
748 mb();
749 play_dead_at_ckseg1(state_addr);
750}
751
752static int loongson3_disable_clock(unsigned int cpu)
753{
754 uint64_t core_id = cpu_core(&cpu_data[cpu]);
755 uint64_t package_id = cpu_data[cpu].package;
756
757 if ((read_c0_prid() & PRID_REV_MASK) == PRID_REV_LOONGSON3A_R1) {
758 LOONGSON_CHIPCFG(package_id) &= ~(1 << (12 + core_id));
759 } else {
760 if (!(loongson_sysconf.workarounds & WORKAROUND_CPUHOTPLUG))
761 LOONGSON_FREQCTRL(package_id) &= ~(1 << (core_id * 4 + 3));
762 }
763 return 0;
764}
765
766static int loongson3_enable_clock(unsigned int cpu)
767{
768 uint64_t core_id = cpu_core(&cpu_data[cpu]);
769 uint64_t package_id = cpu_data[cpu].package;
770
771 if ((read_c0_prid() & PRID_REV_MASK) == PRID_REV_LOONGSON3A_R1) {
772 LOONGSON_CHIPCFG(package_id) |= 1 << (12 + core_id);
773 } else {
774 if (!(loongson_sysconf.workarounds & WORKAROUND_CPUHOTPLUG))
775 LOONGSON_FREQCTRL(package_id) |= 1 << (core_id * 4 + 3);
776 }
777 return 0;
778}
779
780static int register_loongson3_notifier(void)
781{
782 return cpuhp_setup_state_nocalls(CPUHP_MIPS_SOC_PREPARE,
783 "mips/loongson:prepare",
784 loongson3_enable_clock,
785 loongson3_disable_clock);
786}
787early_initcall(register_loongson3_notifier);
788
789#endif
790
791const struct plat_smp_ops loongson3_smp_ops = {
792 .send_ipi_single = loongson3_send_ipi_single,
793 .send_ipi_mask = loongson3_send_ipi_mask,
794 .init_secondary = loongson3_init_secondary,
795 .smp_finish = loongson3_smp_finish,
796 .boot_secondary = loongson3_boot_secondary,
797 .smp_setup = loongson3_smp_setup,
798 .prepare_cpus = loongson3_prepare_cpus,
799#ifdef CONFIG_HOTPLUG_CPU
800 .cpu_disable = loongson3_cpu_disable,
801 .cpu_die = loongson3_cpu_die,
802#endif
803#ifdef CONFIG_KEXEC
804 .kexec_nonboot_cpu = kexec_nonboot_cpu_jump,
805#endif
806};
diff --git a/arch/mips/loongson64/smp.h b/arch/mips/loongson64/smp.h
new file mode 100644
index 000000000..957bde81e
--- /dev/null
+++ b/arch/mips/loongson64/smp.h
@@ -0,0 +1,31 @@
1/* SPDX-License-Identifier: GPL-2.0 */
2#ifndef __LOONGSON_SMP_H_
3#define __LOONGSON_SMP_H_
4
5/* for Loongson-3 smp support */
6extern unsigned long long smp_group[4];
7
8/* 4 groups(nodes) in maximum in numa case */
9#define SMP_CORE_GROUP0_BASE (smp_group[0])
10#define SMP_CORE_GROUP1_BASE (smp_group[1])
11#define SMP_CORE_GROUP2_BASE (smp_group[2])
12#define SMP_CORE_GROUP3_BASE (smp_group[3])
13
14/* 4 cores in each group(node) */
15#define SMP_CORE0_OFFSET 0x000
16#define SMP_CORE1_OFFSET 0x100
17#define SMP_CORE2_OFFSET 0x200
18#define SMP_CORE3_OFFSET 0x300
19
20/* ipi registers offsets */
21#define STATUS0 0x00
22#define EN0 0x04
23#define SET0 0x08
24#define CLEAR0 0x0c
25#define STATUS1 0x10
26#define MASK1 0x14
27#define SET1 0x18
28#define CLEAR1 0x1c
29#define BUF 0x20
30
31#endif
diff --git a/arch/mips/loongson64/time.c b/arch/mips/loongson64/time.c
new file mode 100644
index 000000000..91e842b58
--- /dev/null
+++ b/arch/mips/loongson64/time.c
@@ -0,0 +1,23 @@
1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * Copyright (C) 2007 Lemote, Inc. & Institute of Computing Technology
4 * Author: Fuxin Zhang, zhangfx@lemote.com
5 *
6 * Copyright (C) 2009 Lemote Inc.
7 * Author: Wu Zhangjin, wuzhangjin@gmail.com
8 */
9
10#include <asm/time.h>
11#include <asm/hpet.h>
12
13#include <loongson.h>
14
15void __init plat_time_init(void)
16{
17 /* setup mips r4k timer */
18 mips_hpt_frequency = cpu_clock_freq / 2;
19
20#ifdef CONFIG_RS780_HPET
21 setup_hpet_timer();
22#endif
23}
diff --git a/arch/mips/loongson64/vbios_quirk.c b/arch/mips/loongson64/vbios_quirk.c
new file mode 100644
index 000000000..9a29e94d3
--- /dev/null
+++ b/arch/mips/loongson64/vbios_quirk.c
@@ -0,0 +1,29 @@
1// SPDX-License-Identifier: GPL-2.0+
2
3#include <linux/pci.h>
4#include <loongson.h>
5
6static void pci_fixup_radeon(struct pci_dev *pdev)
7{
8 struct resource *res = &pdev->resource[PCI_ROM_RESOURCE];
9
10 if (res->start)
11 return;
12
13 if (!loongson_sysconf.vgabios_addr)
14 return;
15
16 pci_disable_rom(pdev);
17 if (res->parent)
18 release_resource(res);
19
20 res->start = virt_to_phys((void *) loongson_sysconf.vgabios_addr);
21 res->end = res->start + 256*1024 - 1;
22 res->flags = IORESOURCE_MEM | IORESOURCE_ROM_SHADOW |
23 IORESOURCE_PCI_FIXED;
24
25 dev_info(&pdev->dev, "BAR %d: assigned %pR for Radeon ROM\n",
26 PCI_ROM_RESOURCE, res);
27}
28DECLARE_PCI_FIXUP_CLASS_FINAL(PCI_VENDOR_ID_ATI, 0x9615,
29 PCI_CLASS_DISPLAY_VGA, 8, pci_fixup_radeon);