1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * |
4 | * Copyright (C) 2004, 05, 06 MIPS Technologies, Inc. |
5 | * Elizabeth Clarke (beth@mips.com) |
6 | * Ralf Baechle (ralf@linux-mips.org) |
7 | * Copyright (C) 2006 Ralf Baechle (ralf@linux-mips.org) |
8 | */ |
9 | #include <linux/kernel.h> |
10 | #include <linux/sched.h> |
11 | #include <linux/cpumask.h> |
12 | #include <linux/interrupt.h> |
13 | #include <linux/compiler.h> |
14 | #include <linux/sched/task_stack.h> |
15 | #include <linux/smp.h> |
16 | |
17 | #include <linux/atomic.h> |
18 | #include <asm/cacheflush.h> |
19 | #include <asm/cpu.h> |
20 | #include <asm/processor.h> |
21 | #include <asm/hardirq.h> |
22 | #include <asm/mmu_context.h> |
23 | #include <asm/time.h> |
24 | #include <asm/mipsregs.h> |
25 | #include <asm/mipsmtregs.h> |
26 | #include <asm/mips_mt.h> |
27 | #include <asm/mips-cps.h> |
28 | |
29 | static void __init smvp_copy_vpe_config(void) |
30 | { |
31 | write_vpe_c0_status( |
32 | (read_c0_status() & ~(ST0_IM | ST0_IE | ST0_KSU)) | ST0_CU0); |
33 | |
34 | /* set config to be the same as vpe0, particularly kseg0 coherency alg */ |
35 | write_vpe_c0_config( read_c0_config()); |
36 | |
37 | /* make sure there are no software interrupts pending */ |
38 | write_vpe_c0_cause(0); |
39 | |
40 | /* Propagate Config7 */ |
41 | write_vpe_c0_config7(read_c0_config7()); |
42 | |
43 | write_vpe_c0_count(read_c0_count()); |
44 | } |
45 | |
46 | static unsigned int __init smvp_vpe_init(unsigned int tc, unsigned int mvpconf0, |
47 | unsigned int ncpu) |
48 | { |
49 | if (tc >= smp_max_threads || |
50 | (tc > ((mvpconf0 & MVPCONF0_PVPE) >> MVPCONF0_PVPE_SHIFT))) |
51 | return ncpu; |
52 | |
53 | /* Deactivate all but VPE 0 */ |
54 | if (tc != 0) { |
55 | unsigned long tmp = read_vpe_c0_vpeconf0(); |
56 | |
57 | tmp &= ~VPECONF0_VPA; |
58 | |
59 | /* master VPE */ |
60 | tmp |= VPECONF0_MVP; |
61 | write_vpe_c0_vpeconf0(tmp); |
62 | |
63 | /* Record this as available CPU */ |
64 | set_cpu_possible(cpu: tc, possible: true); |
65 | set_cpu_present(cpu: tc, present: true); |
66 | __cpu_number_map[tc] = ++ncpu; |
67 | __cpu_logical_map[ncpu] = tc; |
68 | } |
69 | |
70 | /* Disable multi-threading with TC's */ |
71 | write_vpe_c0_vpecontrol(read_vpe_c0_vpecontrol() & ~VPECONTROL_TE); |
72 | |
73 | if (tc != 0) |
74 | smvp_copy_vpe_config(); |
75 | |
76 | cpu_set_vpe_id(&cpu_data[ncpu], tc); |
77 | |
78 | return ncpu; |
79 | } |
80 | |
81 | static void __init smvp_tc_init(unsigned int tc, unsigned int mvpconf0) |
82 | { |
83 | unsigned long tmp; |
84 | |
85 | if (!tc) |
86 | return; |
87 | |
88 | /* bind a TC to each VPE, May as well put all excess TC's |
89 | on the last VPE */ |
90 | if (tc >= (((mvpconf0 & MVPCONF0_PVPE) >> MVPCONF0_PVPE_SHIFT)+1)) |
91 | write_tc_c0_tcbind(read_tc_c0_tcbind() | ((mvpconf0 & MVPCONF0_PVPE) >> MVPCONF0_PVPE_SHIFT)); |
92 | else { |
93 | write_tc_c0_tcbind(read_tc_c0_tcbind() | tc); |
94 | |
95 | /* and set XTC */ |
96 | write_vpe_c0_vpeconf0(read_vpe_c0_vpeconf0() | (tc << VPECONF0_XTC_SHIFT)); |
97 | } |
98 | |
99 | tmp = read_tc_c0_tcstatus(); |
100 | |
101 | /* mark not allocated and not dynamically allocatable */ |
102 | tmp &= ~(TCSTATUS_A | TCSTATUS_DA); |
103 | tmp |= TCSTATUS_IXMT; /* interrupt exempt */ |
104 | write_tc_c0_tcstatus(tmp); |
105 | |
106 | write_tc_c0_tchalt(TCHALT_H); |
107 | } |
108 | |
109 | static void vsmp_init_secondary(void) |
110 | { |
111 | /* This is Malta specific: IPI,performance and timer interrupts */ |
112 | if (mips_gic_present()) |
113 | change_c0_status(ST0_IM, STATUSF_IP2 | STATUSF_IP3 | |
114 | STATUSF_IP4 | STATUSF_IP5 | |
115 | STATUSF_IP6 | STATUSF_IP7); |
116 | else |
117 | change_c0_status(ST0_IM, STATUSF_IP0 | STATUSF_IP1 | |
118 | STATUSF_IP6 | STATUSF_IP7); |
119 | } |
120 | |
121 | static void vsmp_smp_finish(void) |
122 | { |
123 | /* CDFIXME: remove this? */ |
124 | write_c0_compare(read_c0_count() + (8* mips_hpt_frequency/HZ)); |
125 | |
126 | #ifdef CONFIG_MIPS_MT_FPAFF |
127 | /* If we have an FPU, enroll ourselves in the FPU-full mask */ |
128 | if (cpu_has_fpu) |
129 | cpumask_set_cpu(smp_processor_id(), &mt_fpu_cpumask); |
130 | #endif /* CONFIG_MIPS_MT_FPAFF */ |
131 | |
132 | local_irq_enable(); |
133 | } |
134 | |
135 | /* |
136 | * Setup the PC, SP, and GP of a secondary processor and start it |
137 | * running! |
138 | * smp_bootstrap is the place to resume from |
139 | * __KSTK_TOS(idle) is apparently the stack pointer |
140 | * (unsigned long)idle->thread_info the gp |
141 | * assumes a 1:1 mapping of TC => VPE |
142 | */ |
143 | static int vsmp_boot_secondary(int cpu, struct task_struct *idle) |
144 | { |
145 | struct thread_info *gp = task_thread_info(idle); |
146 | dvpe(); |
147 | set_c0_mvpcontrol(MVPCONTROL_VPC); |
148 | |
149 | settc(cpu); |
150 | |
151 | /* restart */ |
152 | write_tc_c0_tcrestart((unsigned long)&smp_bootstrap); |
153 | |
154 | /* enable the tc this vpe/cpu will be running */ |
155 | write_tc_c0_tcstatus((read_tc_c0_tcstatus() & ~TCSTATUS_IXMT) | TCSTATUS_A); |
156 | |
157 | write_tc_c0_tchalt(0); |
158 | |
159 | /* enable the VPE */ |
160 | write_vpe_c0_vpeconf0(read_vpe_c0_vpeconf0() | VPECONF0_VPA); |
161 | |
162 | /* stack pointer */ |
163 | write_tc_gpr_sp( __KSTK_TOS(idle)); |
164 | |
165 | /* global pointer */ |
166 | write_tc_gpr_gp((unsigned long)gp); |
167 | |
168 | flush_icache_range(start: (unsigned long)gp, |
169 | end: (unsigned long)(gp + sizeof(struct thread_info))); |
170 | |
171 | /* finally out of configuration and into chaos */ |
172 | clear_c0_mvpcontrol(MVPCONTROL_VPC); |
173 | |
174 | evpe(EVPE_ENABLE); |
175 | |
176 | return 0; |
177 | } |
178 | |
179 | /* |
180 | * Common setup before any secondaries are started |
181 | * Make sure all CPU's are in a sensible state before we boot any of the |
182 | * secondaries |
183 | */ |
184 | static void __init vsmp_smp_setup(void) |
185 | { |
186 | unsigned int mvpconf0, ntc, tc, ncpu = 0; |
187 | unsigned int nvpe; |
188 | |
189 | #ifdef CONFIG_MIPS_MT_FPAFF |
190 | /* If we have an FPU, enroll ourselves in the FPU-full mask */ |
191 | if (cpu_has_fpu) |
192 | cpumask_set_cpu(0, &mt_fpu_cpumask); |
193 | #endif /* CONFIG_MIPS_MT_FPAFF */ |
194 | if (!cpu_has_mipsmt) |
195 | return; |
196 | |
197 | /* disable MT so we can configure */ |
198 | dvpe(); |
199 | dmt(); |
200 | |
201 | /* Put MVPE's into 'configuration state' */ |
202 | set_c0_mvpcontrol(MVPCONTROL_VPC); |
203 | |
204 | mvpconf0 = read_c0_mvpconf0(); |
205 | ntc = (mvpconf0 & MVPCONF0_PTC) >> MVPCONF0_PTC_SHIFT; |
206 | |
207 | nvpe = ((mvpconf0 & MVPCONF0_PVPE) >> MVPCONF0_PVPE_SHIFT) + 1; |
208 | smp_num_siblings = nvpe; |
209 | |
210 | /* we'll always have more TC's than VPE's, so loop setting everything |
211 | to a sensible state */ |
212 | for (tc = 0; tc <= ntc; tc++) { |
213 | settc(tc); |
214 | |
215 | smvp_tc_init(tc, mvpconf0); |
216 | ncpu = smvp_vpe_init(tc, mvpconf0, ncpu); |
217 | } |
218 | |
219 | /* Release config state */ |
220 | clear_c0_mvpcontrol(MVPCONTROL_VPC); |
221 | |
222 | /* We'll wait until starting the secondaries before starting MVPE */ |
223 | |
224 | printk(KERN_INFO "Detected %i available secondary CPU(s)\n" , ncpu); |
225 | } |
226 | |
227 | static void __init vsmp_prepare_cpus(unsigned int max_cpus) |
228 | { |
229 | mips_mt_set_cpuoptions(); |
230 | } |
231 | |
232 | const struct plat_smp_ops vsmp_smp_ops = { |
233 | .send_ipi_single = mips_smp_send_ipi_single, |
234 | .send_ipi_mask = mips_smp_send_ipi_mask, |
235 | .init_secondary = vsmp_init_secondary, |
236 | .smp_finish = vsmp_smp_finish, |
237 | .boot_secondary = vsmp_boot_secondary, |
238 | .smp_setup = vsmp_smp_setup, |
239 | .prepare_cpus = vsmp_prepare_cpus, |
240 | }; |
241 | |
242 | |