diff options
author | 2025-03-08 22:04:20 +0800 | |
---|---|---|
committer | 2025-03-08 22:04:20 +0800 | |
commit | a07bb8fd1299070229f0e8f3dcb57ffd5ef9870a (patch) | |
tree | 84f21bd0bf7071bc5fc7dd989e77d7ceb5476682 /arch/mips/loongson64 | |
download | ohosKernel-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/Kconfig | 15 | ||||
-rw-r--r-- | arch/mips/loongson64/Makefile | 13 | ||||
-rw-r--r-- | arch/mips/loongson64/Platform | 37 | ||||
-rw-r--r-- | arch/mips/loongson64/cop2-ex.c | 341 | ||||
-rw-r--r-- | arch/mips/loongson64/cpucfg-emul.c | 227 | ||||
-rw-r--r-- | arch/mips/loongson64/dma.c | 28 | ||||
-rw-r--r-- | arch/mips/loongson64/env.c | 223 | ||||
-rw-r--r-- | arch/mips/loongson64/hpet.c | 285 | ||||
-rw-r--r-- | arch/mips/loongson64/init.c | 149 | ||||
-rw-r--r-- | arch/mips/loongson64/numa.c | 252 | ||||
-rw-r--r-- | arch/mips/loongson64/platform.c | 42 | ||||
-rw-r--r-- | arch/mips/loongson64/pm.c | 104 | ||||
-rw-r--r-- | arch/mips/loongson64/reset.c | 59 | ||||
-rw-r--r-- | arch/mips/loongson64/setup.c | 46 | ||||
-rw-r--r-- | arch/mips/loongson64/smp.c | 806 | ||||
-rw-r--r-- | arch/mips/loongson64/smp.h | 31 | ||||
-rw-r--r-- | arch/mips/loongson64/time.c | 23 | ||||
-rw-r--r-- | arch/mips/loongson64/vbios_quirk.c | 29 |
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 | ||
2 | if MACH_LOONGSON64 | ||
3 | |||
4 | config 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 | |||
15 | endif # 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 | # | ||
5 | obj-$(CONFIG_MACH_LOONGSON64) += cop2-ex.o platform.o dma.o \ | ||
6 | setup.o init.o env.o time.o reset.o \ | ||
7 | |||
8 | obj-$(CONFIG_SMP) += smp.o | ||
9 | obj-$(CONFIG_NUMA) += numa.o | ||
10 | obj-$(CONFIG_RS780_HPET) += hpet.o | ||
11 | obj-$(CONFIG_SUSPEND) += pm.o | ||
12 | obj-$(CONFIG_PCI_QUIRKS) += vbios_quirk.o | ||
13 | obj-$(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 | |||
6 | cflags-$(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 | # | ||
15 | ifeq ($(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 | ||
23 | else | ||
24 | cflags-$(CONFIG_CPU_LOONGSON64) += \ | ||
25 | $(call cc-option,-march=mips64r2,-mips64r2 -U_MIPS_ISA -D_MIPS_ISA=_MIPS_ISA_MIPS64) | ||
26 | endif | ||
27 | |||
28 | # Some -march= flags enable MMI instructions, and GCC complains about that | ||
29 | # support being enabled alongside -msoft-float. Thus explicitly disable MMI. | ||
30 | cflags-y += $(call cc-option,-mno-loongson-mmi) | ||
31 | |||
32 | # | ||
33 | # Loongson Machines' Support | ||
34 | # | ||
35 | |||
36 | cflags-$(CONFIG_MACH_LOONGSON64) += -I$(srctree)/arch/mips/include/asm/mach-loongson64 -mno-branch-likely | ||
37 | load-$(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 | |||
28 | static 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(¤t->thread.fpu.fpr[insn.loongson3_lswc2_format.rt], 0, value); | ||
99 | set_fpr64(¤t->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(¤t->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(¤t->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(¤t->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(¤t->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(¤t->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(¤t->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 | |||
317 | fault: | ||
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 | |||
330 | sigbus: | ||
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 | |||
337 | static int __init loongson_cu2_setup(void) | ||
338 | { | ||
339 | return cu2_notifier(loongson_cu2_call, 0); | ||
340 | } | ||
341 | early_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 | |||
12 | static bool is_loongson(struct cpuinfo_mips *c) | ||
13 | { | ||
14 | switch (c->processor_id & PRID_COMP_MASK) { | ||
15 | case PRID_COMP_LEGACY: | ||
16 | return ((c->processor_id & PRID_IMP_MASK) == | ||
17 | PRID_IMP_LOONGSON_64C); | ||
18 | |||
19 | case PRID_COMP_LOONGSON: | ||
20 | return true; | ||
21 | |||
22 | default: | ||
23 | return false; | ||
24 | } | ||
25 | } | ||
26 | |||
27 | static u32 get_loongson_fprev(struct cpuinfo_mips *c) | ||
28 | { | ||
29 | return c->fpu_id & LOONGSON_FPREV_MASK; | ||
30 | } | ||
31 | |||
32 | static bool cpu_has_uca(void) | ||
33 | { | ||
34 | u32 diag = read_c0_diag(); | ||
35 | u32 new_diag; | ||
36 | |||
37 | if (diag & LOONGSON_DIAG_UCAC) | ||
38 | /* UCA is already enabled. */ | ||
39 | return true; | ||
40 | |||
41 | /* See if UCAC bit can be flipped on. This should be safe. */ | ||
42 | new_diag = diag | LOONGSON_DIAG_UCAC; | ||
43 | write_c0_diag(new_diag); | ||
44 | new_diag = read_c0_diag(); | ||
45 | write_c0_diag(diag); | ||
46 | |||
47 | return (new_diag & LOONGSON_DIAG_UCAC) != 0; | ||
48 | } | ||
49 | |||
50 | static void probe_uca(struct cpuinfo_mips *c) | ||
51 | { | ||
52 | if (cpu_has_uca()) | ||
53 | c->loongson3_cpucfg_data[0] |= LOONGSON_CFG1_LSUCA; | ||
54 | } | ||
55 | |||
56 | static void decode_loongson_config6(struct cpuinfo_mips *c) | ||
57 | { | ||
58 | u32 config6 = read_c0_config6(); | ||
59 | |||
60 | if (config6 & LOONGSON_CONF6_SFBEN) | ||
61 | c->loongson3_cpucfg_data[0] |= LOONGSON_CFG1_SFBP; | ||
62 | if (config6 & LOONGSON_CONF6_LLEXC) | ||
63 | c->loongson3_cpucfg_data[0] |= LOONGSON_CFG1_LLEXC; | ||
64 | if (config6 & LOONGSON_CONF6_SCRAND) | ||
65 | c->loongson3_cpucfg_data[0] |= LOONGSON_CFG1_SCRAND; | ||
66 | } | ||
67 | |||
68 | static void patch_cpucfg_sel1(struct cpuinfo_mips *c) | ||
69 | { | ||
70 | u64 ases = c->ases; | ||
71 | u64 options = c->options; | ||
72 | u32 data = c->loongson3_cpucfg_data[0]; | ||
73 | |||
74 | if (options & MIPS_CPU_FPU) { | ||
75 | data |= LOONGSON_CFG1_FP; | ||
76 | data |= get_loongson_fprev(c) << LOONGSON_CFG1_FPREV_OFFSET; | ||
77 | } | ||
78 | if (ases & MIPS_ASE_LOONGSON_MMI) | ||
79 | data |= LOONGSON_CFG1_MMI; | ||
80 | if (ases & MIPS_ASE_MSA) | ||
81 | data |= LOONGSON_CFG1_MSA1; | ||
82 | |||
83 | c->loongson3_cpucfg_data[0] = data; | ||
84 | } | ||
85 | |||
86 | static void patch_cpucfg_sel2(struct cpuinfo_mips *c) | ||
87 | { | ||
88 | u64 ases = c->ases; | ||
89 | u64 options = c->options; | ||
90 | u32 data = c->loongson3_cpucfg_data[1]; | ||
91 | |||
92 | if (ases & MIPS_ASE_LOONGSON_EXT) | ||
93 | data |= LOONGSON_CFG2_LEXT1; | ||
94 | if (ases & MIPS_ASE_LOONGSON_EXT2) | ||
95 | data |= LOONGSON_CFG2_LEXT2; | ||
96 | if (options & MIPS_CPU_LDPTE) | ||
97 | data |= LOONGSON_CFG2_LSPW; | ||
98 | |||
99 | if (ases & MIPS_ASE_VZ) | ||
100 | data |= LOONGSON_CFG2_LVZP; | ||
101 | else | ||
102 | data &= ~LOONGSON_CFG2_LVZREV; | ||
103 | |||
104 | c->loongson3_cpucfg_data[1] = data; | ||
105 | } | ||
106 | |||
107 | static void patch_cpucfg_sel3(struct cpuinfo_mips *c) | ||
108 | { | ||
109 | u64 ases = c->ases; | ||
110 | u32 data = c->loongson3_cpucfg_data[2]; | ||
111 | |||
112 | if (ases & MIPS_ASE_LOONGSON_CAM) { | ||
113 | data |= LOONGSON_CFG3_LCAMP; | ||
114 | } else { | ||
115 | data &= ~LOONGSON_CFG3_LCAMREV; | ||
116 | data &= ~LOONGSON_CFG3_LCAMNUM; | ||
117 | data &= ~LOONGSON_CFG3_LCAMKW; | ||
118 | data &= ~LOONGSON_CFG3_LCAMVW; | ||
119 | } | ||
120 | |||
121 | c->loongson3_cpucfg_data[2] = data; | ||
122 | } | ||
123 | |||
124 | void loongson3_cpucfg_synthesize_data(struct cpuinfo_mips *c) | ||
125 | { | ||
126 | /* Only engage the logic on Loongson processors. */ | ||
127 | if (!is_loongson(c)) | ||
128 | return; | ||
129 | |||
130 | /* CPUs with CPUCFG support don't need to synthesize anything. */ | ||
131 | if (cpu_has_cfg()) | ||
132 | goto have_cpucfg_now; | ||
133 | |||
134 | c->loongson3_cpucfg_data[0] = 0; | ||
135 | c->loongson3_cpucfg_data[1] = 0; | ||
136 | c->loongson3_cpucfg_data[2] = 0; | ||
137 | |||
138 | /* Add CPUCFG features non-discoverable otherwise. */ | ||
139 | switch (c->processor_id & (PRID_IMP_MASK | PRID_REV_MASK)) { | ||
140 | case PRID_IMP_LOONGSON_64R | PRID_REV_LOONGSON2K_R1_0: | ||
141 | case PRID_IMP_LOONGSON_64R | PRID_REV_LOONGSON2K_R1_1: | ||
142 | case PRID_IMP_LOONGSON_64R | PRID_REV_LOONGSON2K_R1_2: | ||
143 | case PRID_IMP_LOONGSON_64R | PRID_REV_LOONGSON2K_R1_3: | ||
144 | decode_loongson_config6(c); | ||
145 | probe_uca(c); | ||
146 | |||
147 | c->loongson3_cpucfg_data[0] |= (LOONGSON_CFG1_LSLDR0 | | ||
148 | LOONGSON_CFG1_LSSYNCI | LOONGSON_CFG1_LLSYNC | | ||
149 | LOONGSON_CFG1_TGTSYNC); | ||
150 | c->loongson3_cpucfg_data[1] |= (LOONGSON_CFG2_LBT1 | | ||
151 | LOONGSON_CFG2_LBT2 | LOONGSON_CFG2_LPMP | | ||
152 | LOONGSON_CFG2_LPM_REV2); | ||
153 | c->loongson3_cpucfg_data[2] = 0; | ||
154 | break; | ||
155 | |||
156 | case PRID_IMP_LOONGSON_64C | PRID_REV_LOONGSON3A_R1: | ||
157 | c->loongson3_cpucfg_data[0] |= (LOONGSON_CFG1_LSLDR0 | | ||
158 | LOONGSON_CFG1_LSSYNCI | LOONGSON_CFG1_LSUCA | | ||
159 | LOONGSON_CFG1_LLSYNC | LOONGSON_CFG1_TGTSYNC); | ||
160 | c->loongson3_cpucfg_data[1] |= (LOONGSON_CFG2_LBT1 | | ||
161 | LOONGSON_CFG2_LPMP | LOONGSON_CFG2_LPM_REV1); | ||
162 | c->loongson3_cpucfg_data[2] |= ( | ||
163 | LOONGSON_CFG3_LCAM_REV1 | | ||
164 | LOONGSON_CFG3_LCAMNUM_REV1 | | ||
165 | LOONGSON_CFG3_LCAMKW_REV1 | | ||
166 | LOONGSON_CFG3_LCAMVW_REV1); | ||
167 | break; | ||
168 | |||
169 | case PRID_IMP_LOONGSON_64C | PRID_REV_LOONGSON3B_R1: | ||
170 | case PRID_IMP_LOONGSON_64C | PRID_REV_LOONGSON3B_R2: | ||
171 | c->loongson3_cpucfg_data[0] |= (LOONGSON_CFG1_LSLDR0 | | ||
172 | LOONGSON_CFG1_LSSYNCI | LOONGSON_CFG1_LSUCA | | ||
173 | LOONGSON_CFG1_LLSYNC | LOONGSON_CFG1_TGTSYNC); | ||
174 | c->loongson3_cpucfg_data[1] |= (LOONGSON_CFG2_LBT1 | | ||
175 | LOONGSON_CFG2_LPMP | LOONGSON_CFG2_LPM_REV1); | ||
176 | c->loongson3_cpucfg_data[2] |= ( | ||
177 | LOONGSON_CFG3_LCAM_REV1 | | ||
178 | LOONGSON_CFG3_LCAMNUM_REV1 | | ||
179 | LOONGSON_CFG3_LCAMKW_REV1 | | ||
180 | LOONGSON_CFG3_LCAMVW_REV1); | ||
181 | break; | ||
182 | |||
183 | case PRID_IMP_LOONGSON_64C | PRID_REV_LOONGSON3A_R2_0: | ||
184 | case PRID_IMP_LOONGSON_64C | PRID_REV_LOONGSON3A_R2_1: | ||
185 | case PRID_IMP_LOONGSON_64C | PRID_REV_LOONGSON3A_R3_0: | ||
186 | case PRID_IMP_LOONGSON_64C | PRID_REV_LOONGSON3A_R3_1: | ||
187 | decode_loongson_config6(c); | ||
188 | probe_uca(c); | ||
189 | |||
190 | c->loongson3_cpucfg_data[0] |= (LOONGSON_CFG1_CNT64 | | ||
191 | LOONGSON_CFG1_LSLDR0 | LOONGSON_CFG1_LSPREF | | ||
192 | LOONGSON_CFG1_LSPREFX | LOONGSON_CFG1_LSSYNCI | | ||
193 | LOONGSON_CFG1_LLSYNC | LOONGSON_CFG1_TGTSYNC); | ||
194 | c->loongson3_cpucfg_data[1] |= (LOONGSON_CFG2_LBT1 | | ||
195 | LOONGSON_CFG2_LBT2 | LOONGSON_CFG2_LBTMMU | | ||
196 | LOONGSON_CFG2_LPMP | LOONGSON_CFG2_LPM_REV1 | | ||
197 | LOONGSON_CFG2_LVZ_REV1); | ||
198 | c->loongson3_cpucfg_data[2] |= (LOONGSON_CFG3_LCAM_REV1 | | ||
199 | LOONGSON_CFG3_LCAMNUM_REV1 | | ||
200 | LOONGSON_CFG3_LCAMKW_REV1 | | ||
201 | LOONGSON_CFG3_LCAMVW_REV1); | ||
202 | break; | ||
203 | |||
204 | default: | ||
205 | /* It is possible that some future Loongson cores still do | ||
206 | * not have CPUCFG, so do not emulate anything for these | ||
207 | * cores. | ||
208 | */ | ||
209 | return; | ||
210 | } | ||
211 | |||
212 | /* This feature is set by firmware, but all known Loongson-64 systems | ||
213 | * are configured this way. | ||
214 | */ | ||
215 | c->loongson3_cpucfg_data[0] |= LOONGSON_CFG1_CDMAP; | ||
216 | |||
217 | /* Patch in dynamically probed bits. */ | ||
218 | patch_cpucfg_sel1(c); | ||
219 | patch_cpucfg_sel2(c); | ||
220 | patch_cpucfg_sel3(c); | ||
221 | |||
222 | have_cpucfg_now: | ||
223 | /* We have usable CPUCFG now, emulated or not. | ||
224 | * Announce CPUCFG availability to userspace via hwcap. | ||
225 | */ | ||
226 | elf_hwcap |= HWCAP_LOONGSON_CPUCFG; | ||
227 | } | ||
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 | |||
7 | dma_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 | |||
16 | phys_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 | |||
25 | void __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 | |||
26 | u32 cpu_clock_freq; | ||
27 | EXPORT_SYMBOL(cpu_clock_freq); | ||
28 | struct efi_memory_map_loongson *loongson_memmap; | ||
29 | struct loongson_system_configuration loongson_sysconf; | ||
30 | |||
31 | u64 loongson_chipcfg[MAX_PACKAGES] = {0xffffffffbfc00180}; | ||
32 | u64 loongson_chiptemp[MAX_PACKAGES]; | ||
33 | u64 loongson_freqctrl[MAX_PACKAGES]; | ||
34 | |||
35 | unsigned long long smp_group[4]; | ||
36 | |||
37 | const char *get_system_type(void) | ||
38 | { | ||
39 | return "Generic Loongson64 System"; | ||
40 | } | ||
41 | |||
42 | void __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 | |||
20 | static DEFINE_SPINLOCK(hpet_lock); | ||
21 | DEFINE_PER_CPU(struct clock_event_device, hpet_clockevent_device); | ||
22 | |||
23 | static unsigned int smbus_read(int offset) | ||
24 | { | ||
25 | return *(volatile unsigned int *)(SMBUS_CFG_BASE + offset); | ||
26 | } | ||
27 | |||
28 | static void smbus_write(int offset, int data) | ||
29 | { | ||
30 | *(volatile unsigned int *)(SMBUS_CFG_BASE + offset) = data; | ||
31 | } | ||
32 | |||
33 | static 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 | |||
41 | static int hpet_read(int offset) | ||
42 | { | ||
43 | return *(volatile unsigned int *)(HPET_MMIO_ADDR + offset); | ||
44 | } | ||
45 | |||
46 | static void hpet_write(int offset, int data) | ||
47 | { | ||
48 | *(volatile unsigned int *)(HPET_MMIO_ADDR + offset) = data; | ||
49 | } | ||
50 | |||
51 | static 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 | |||
59 | static 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 | |||
67 | static void hpet_reset_counter(void) | ||
68 | { | ||
69 | hpet_write(HPET_COUNTER, 0); | ||
70 | hpet_write(HPET_COUNTER + 4, 0); | ||
71 | } | ||
72 | |||
73 | static void hpet_restart_counter(void) | ||
74 | { | ||
75 | hpet_stop_counter(); | ||
76 | hpet_reset_counter(); | ||
77 | hpet_start_counter(); | ||
78 | } | ||
79 | |||
80 | static void hpet_enable_legacy_int(void) | ||
81 | { | ||
82 | /* Do nothing on Loongson-3 */ | ||
83 | } | ||
84 | |||
85 | static 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 | |||
114 | static 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 | |||
128 | static 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 | |||
149 | static 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 | |||
158 | static 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 | |||
173 | static 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 | */ | ||
205 | static 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 | |||
219 | void __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 | |||
250 | static u64 hpet_read_counter(struct clocksource *cs) | ||
251 | { | ||
252 | return (u64)hpet_read(HPET_COUNTER); | ||
253 | } | ||
254 | |||
255 | static void hpet_suspend(struct clocksource *cs) | ||
256 | { | ||
257 | } | ||
258 | |||
259 | static void hpet_resume(struct clocksource *cs) | ||
260 | { | ||
261 | hpet_setup(); | ||
262 | hpet_restart_counter(); | ||
263 | } | ||
264 | |||
265 | static 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 | |||
279 | int __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 | |||
285 | arch_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 | |||
23 | u32 node_id_offset; | ||
24 | |||
25 | static 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 | |||
35 | void ls7a_early_config(void) | ||
36 | { | ||
37 | node_id_offset = ((readl(NODE_ID_OFFSET_ADDR) >> 8) & 0x1f) + 36; | ||
38 | } | ||
39 | |||
40 | void rs780e_early_config(void) | ||
41 | { | ||
42 | node_id_offset = 37; | ||
43 | } | ||
44 | |||
45 | void virtual_early_config(void) | ||
46 | { | ||
47 | node_id_offset = 44; | ||
48 | } | ||
49 | |||
50 | void __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 | |||
69 | void __init prom_free_prom_memory(void) | ||
70 | { | ||
71 | } | ||
72 | |||
73 | static 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 | |||
109 | static __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 | |||
145 | void __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 | |||
29 | static struct pglist_data prealloc__node_data[MAX_NUMNODES]; | ||
30 | unsigned char __node_distances[MAX_NUMNODES][MAX_NUMNODES]; | ||
31 | EXPORT_SYMBOL(__node_distances); | ||
32 | struct pglist_data *__node_data[MAX_NUMNODES]; | ||
33 | EXPORT_SYMBOL(__node_data); | ||
34 | |||
35 | cpumask_t __node_cpumask[MAX_NUMNODES]; | ||
36 | EXPORT_SYMBOL(__node_cpumask); | ||
37 | |||
38 | static 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 | |||
55 | static 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 | |||
70 | static 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 | |||
85 | static 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 | |||
101 | static 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 | |||
152 | static 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 | |||
191 | static __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 | |||
222 | void __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 | |||
232 | void __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 */ | ||
241 | int pcibus_to_node(struct pci_bus *bus) | ||
242 | { | ||
243 | return 0; | ||
244 | } | ||
245 | EXPORT_SYMBOL(pcibus_to_node); | ||
246 | |||
247 | void __init prom_init_numa_memory(void) | ||
248 | { | ||
249 | enable_lpa(); | ||
250 | prom_meminit(); | ||
251 | } | ||
252 | EXPORT_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 | |||
17 | static 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 | |||
42 | arch_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 | |||
17 | static unsigned int __maybe_unused cached_master_mask; /* i8259A */ | ||
18 | static unsigned int __maybe_unused cached_slave_mask; | ||
19 | static unsigned int __maybe_unused cached_bonito_irq_mask; /* bonito */ | ||
20 | |||
21 | void 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 | |||
42 | void 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 | */ | ||
59 | void __weak setup_wakeup_events(void) | ||
60 | { | ||
61 | } | ||
62 | |||
63 | void __weak mach_suspend(void) | ||
64 | { | ||
65 | } | ||
66 | |||
67 | void __weak mach_resume(void) | ||
68 | { | ||
69 | } | ||
70 | |||
71 | static int loongson_pm_enter(suspend_state_t state) | ||
72 | { | ||
73 | mach_suspend(); | ||
74 | |||
75 | mach_resume(); | ||
76 | |||
77 | return 0; | ||
78 | } | ||
79 | |||
80 | static 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 | |||
93 | static const struct platform_suspend_ops loongson_pm_ops = { | ||
94 | .valid = loongson_pm_valid_state, | ||
95 | .enter = loongson_pm_enter, | ||
96 | }; | ||
97 | |||
98 | static int __init loongson_pm_init(void) | ||
99 | { | ||
100 | suspend_set_ops(&loongson_pm_ops); | ||
101 | |||
102 | return 0; | ||
103 | } | ||
104 | arch_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 | |||
18 | static 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 | |||
30 | static 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 | |||
41 | static 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 | |||
50 | static 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 | |||
59 | arch_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 | |||
18 | void *loongson_fdt_blob; | ||
19 | |||
20 | static 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 | |||
31 | void (*__wbflush)(void) = wbflush_loongson; | ||
32 | EXPORT_SYMBOL(__wbflush); | ||
33 | |||
34 | void __init plat_mem_setup(void) | ||
35 | { | ||
36 | if (loongson_fdt_blob) | ||
37 | __dt_setup_arch(loongson_fdt_blob); | ||
38 | } | ||
39 | |||
40 | void __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 | |||
26 | DEFINE_PER_CPU(int, cpu_state); | ||
27 | |||
28 | #define LS_IPI_IRQ (MIPS_CPU_IRQ_BASE + 6) | ||
29 | |||
30 | static void *ipi_set0_regs[16]; | ||
31 | static void *ipi_clear0_regs[16]; | ||
32 | static void *ipi_status0_regs[16]; | ||
33 | static void *ipi_en0_regs[16]; | ||
34 | static void *ipi_mailbox_buf[16]; | ||
35 | static 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 | |||
54 | u32 (*ipi_read_clear)(int cpu); | ||
55 | void (*ipi_write_action)(int cpu, u32 action); | ||
56 | |||
57 | static 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 | |||
69 | static 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 | |||
82 | static 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 | |||
94 | static void legacy_ipi_write_action(int cpu, u32 action) | ||
95 | { | ||
96 | loongson3_ipi_write32((u32)action, ipi_set0_regs[cpu]); | ||
97 | } | ||
98 | |||
99 | static 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 | |||
110 | static 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 | |||
146 | static 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 | |||
182 | static 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 | |||
218 | static 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 | |||
254 | static 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 | */ | ||
293 | static void loongson3_send_ipi_single(int cpu, unsigned int action) | ||
294 | { | ||
295 | ipi_write_action(cpu_logical_map(cpu), (u32)action); | ||
296 | } | ||
297 | |||
298 | static void | ||
299 | loongson3_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 | |||
308 | static 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 | */ | ||
340 | static 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 | |||
378 | static 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 | |||
390 | static 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 | |||
428 | static 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 | */ | ||
440 | static 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 | |||
468 | static 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 | |||
488 | static 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) */ | ||
500 | static 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 | |||
562 | static 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 | |||
626 | static 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 | |||
709 | void 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 | |||
746 | out: | ||
747 | state_addr = &per_cpu(cpu_state, cpu); | ||
748 | mb(); | ||
749 | play_dead_at_ckseg1(state_addr); | ||
750 | } | ||
751 | |||
752 | static 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 | |||
766 | static 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 | |||
780 | static 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 | } | ||
787 | early_initcall(register_loongson3_notifier); | ||
788 | |||
789 | #endif | ||
790 | |||
791 | const 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 */ | ||
6 | extern 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 | |||
15 | void __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 | |||
6 | static 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 | } | ||
28 | DECLARE_PCI_FIXUP_CLASS_FINAL(PCI_VENDOR_ID_ATI, 0x9615, | ||
29 | PCI_CLASS_DISPLAY_VGA, 8, pci_fixup_radeon); | ||