xref: /netbsd/sys/arch/alpha/alpha/ipifuncs.c (revision 6550d01e)
1 /* $NetBSD: ipifuncs.c,v 1.45 2010/12/17 02:36:35 joerg Exp $ */
2 
3 /*-
4  * Copyright (c) 1998, 1999, 2000, 2001 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
9  * NASA Ames Research Center.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
21  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
24  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30  * POSSIBILITY OF SUCH DAMAGE.
31  */
32 
33 #include <sys/cdefs.h>			/* RCS ID & Copyright macro defns */
34 
35 __KERNEL_RCSID(0, "$NetBSD: ipifuncs.c,v 1.45 2010/12/17 02:36:35 joerg Exp $");
36 
37 /*
38  * Interprocessor interrupt handlers.
39  */
40 
41 #include <sys/param.h>
42 #include <sys/device.h>
43 #include <sys/proc.h>
44 #include <sys/systm.h>
45 #include <sys/reboot.h>
46 #include <sys/atomic.h>
47 #include <sys/cpu.h>
48 #include <sys/intr.h>
49 #include <sys/xcall.h>
50 
51 #include <uvm/uvm_extern.h>
52 
53 #include <machine/alpha_cpu.h>
54 #include <machine/alpha.h>
55 #include <machine/cpuvar.h>
56 #include <machine/rpb.h>
57 #include <machine/prom.h>
58 
59 typedef void (*ipifunc_t)(struct cpu_info *, struct trapframe *);
60 
61 void	alpha_ipi_halt(struct cpu_info *, struct trapframe *);
62 void	alpha_ipi_microset(struct cpu_info *, struct trapframe *);
63 void	alpha_ipi_imb(struct cpu_info *, struct trapframe *);
64 void	alpha_ipi_ast(struct cpu_info *, struct trapframe *);
65 void	alpha_ipi_synch_fpu(struct cpu_info *, struct trapframe *);
66 void	alpha_ipi_discard_fpu(struct cpu_info *, struct trapframe *);
67 void	alpha_ipi_pause(struct cpu_info *, struct trapframe *);
68 void	alpha_ipi_xcall(struct cpu_info *, struct trapframe *);
69 
70 /*
71  * NOTE: This table must be kept in order with the bit definitions
72  * in <machine/intr.h>.
73  */
74 ipifunc_t ipifuncs[ALPHA_NIPIS] = {
75 	alpha_ipi_halt,
76 	alpha_ipi_microset,
77 	pmap_do_tlb_shootdown,
78 	alpha_ipi_imb,
79 	alpha_ipi_ast,
80 	alpha_ipi_synch_fpu,
81 	alpha_ipi_discard_fpu,
82 	alpha_ipi_pause,
83 	alpha_ipi_xcall
84 };
85 
86 const char *ipinames[ALPHA_NIPIS] = {
87 	"halt ipi",
88 	"microset ipi",
89 	"shootdown ipi",
90 	"imb ipi",
91 	"ast ipi",
92 	"synch fpu ipi",
93 	"discard fpu ipi",
94 	"pause ipi",
95 	"xcall ipi"
96 };
97 
98 /*
99  * Initialize IPI state for a CPU.
100  *
101  * Note: the cpu_info softc pointer must be valid.
102  */
103 void
104 alpha_ipi_init(struct cpu_info *ci)
105 {
106 	struct cpu_softc *sc = ci->ci_softc;
107 	int i;
108 
109 	evcnt_attach_dynamic(&sc->sc_evcnt_ipi, EVCNT_TYPE_INTR,
110 	    NULL, sc->sc_dev.dv_xname, "ipi");
111 
112 	for (i = 0; i < ALPHA_NIPIS; i++) {
113 		evcnt_attach_dynamic(&sc->sc_evcnt_which_ipi[i],
114 		    EVCNT_TYPE_INTR, NULL, sc->sc_dev.dv_xname,
115 		    ipinames[i]);
116 	}
117 }
118 
119 /*
120  * Process IPIs for a CPU.
121  */
122 void
123 alpha_ipi_process(struct cpu_info *ci, struct trapframe *framep)
124 {
125 	struct cpu_softc *sc = ci->ci_softc;
126 	u_long pending_ipis, bit;
127 
128 #ifdef DIAGNOSTIC
129 	if (sc == NULL) {
130 		/* XXX panic? */
131 		printf("WARNING: no softc for ID %lu\n", ci->ci_cpuid);
132 		return;
133 	}
134 #endif
135 
136 	pending_ipis = atomic_swap_ulong(&ci->ci_ipis, 0);
137 
138 	/*
139 	 * For various reasons, it is possible to have spurious calls
140 	 * to this routine, so just bail out now if there are none
141 	 * pending.
142 	 */
143 	if (pending_ipis == 0)
144 		return;
145 
146 	sc->sc_evcnt_ipi.ev_count++;
147 
148 	for (bit = 0; bit < ALPHA_NIPIS; bit++) {
149 		if (pending_ipis & (1UL << bit)) {
150 			sc->sc_evcnt_which_ipi[bit].ev_count++;
151 			(*ipifuncs[bit])(ci, framep);
152 		}
153 	}
154 }
155 
156 /*
157  * Send an interprocessor interrupt.
158  */
159 void
160 alpha_send_ipi(u_long cpu_id, u_long ipimask)
161 {
162 
163 #ifdef DIAGNOSTIC
164 	if (cpu_id >= hwrpb->rpb_pcs_cnt ||
165 	    cpu_info[cpu_id] == NULL)
166 		panic("alpha_send_ipi: bogus cpu_id");
167 	if (((1UL << cpu_id) & cpus_running) == 0)
168 		panic("alpha_send_ipi: CPU %ld not running", cpu_id);
169 #endif
170 
171 	atomic_or_ulong(&cpu_info[cpu_id]->ci_ipis, ipimask);
172 	alpha_pal_wripir(cpu_id);
173 }
174 
175 /*
176  * Broadcast an IPI to all but ourselves.
177  */
178 void
179 alpha_broadcast_ipi(u_long ipimask)
180 {
181 	struct cpu_info *ci;
182 	CPU_INFO_ITERATOR cii;
183 	u_long cpu_id = cpu_number();
184 	u_long cpumask;
185 
186 	cpumask = cpus_running & ~(1UL << cpu_id);
187 
188 	for (CPU_INFO_FOREACH(cii, ci)) {
189 		if ((cpumask & (1UL << ci->ci_cpuid)) == 0)
190 			continue;
191 		alpha_send_ipi(ci->ci_cpuid, ipimask);
192 	}
193 }
194 
195 /*
196  * Send an IPI to all in the list but ourselves.
197  */
198 void
199 alpha_multicast_ipi(u_long cpumask, u_long ipimask)
200 {
201 	struct cpu_info *ci;
202 	CPU_INFO_ITERATOR cii;
203 
204 	cpumask &= cpus_running;
205 	cpumask &= ~(1UL << cpu_number());
206 	if (cpumask == 0)
207 		return;
208 
209 	for (CPU_INFO_FOREACH(cii, ci)) {
210 		if ((cpumask & (1UL << ci->ci_cpuid)) == 0)
211 			continue;
212 		alpha_send_ipi(ci->ci_cpuid, ipimask);
213 	}
214 }
215 
216 void
217 alpha_ipi_halt(struct cpu_info *ci, struct trapframe *framep)
218 {
219 	u_long cpu_id = ci->ci_cpuid;
220 	u_long wait_mask = (1UL << cpu_id);
221 
222 	/* Disable interrupts. */
223 	(void) splhigh();
224 
225 	if (cpu_id != hwrpb->rpb_primary_cpu_id) {
226 		/*
227 		 * If we're not the primary, we just halt now.
228 		 */
229 		cpu_halt();
230 	}
231 
232 	/*
233 	 * We're the primary.  We need to wait for all the other
234 	 * secondary CPUs to halt, then we can drop back to the
235 	 * console.
236 	 */
237 	alpha_mb();
238 	for (;;) {
239 		alpha_mb();
240 		if (cpus_running == wait_mask)
241 			break;
242 		delay(1000);
243 	}
244 
245 	prom_halt(boothowto & RB_HALT);
246 	/* NOTREACHED */
247 }
248 
249 void
250 alpha_ipi_microset(struct cpu_info *ci, struct trapframe *framep)
251 {
252 
253 	cc_calibrate_cpu(ci);
254 }
255 
256 void
257 alpha_ipi_imb(struct cpu_info *ci, struct trapframe *framep)
258 {
259 
260 	alpha_pal_imb();
261 }
262 
263 void
264 alpha_ipi_ast(struct cpu_info *ci, struct trapframe *framep)
265 {
266 
267 	if (ci->ci_curlwp != ci->ci_data.cpu_idlelwp)
268 		aston(ci->ci_curlwp);
269 }
270 
271 void
272 alpha_ipi_synch_fpu(struct cpu_info *ci, struct trapframe *framep)
273 {
274 
275 	if (ci->ci_flags & CPUF_FPUSAVE)
276 		return;
277 	fpusave_cpu(ci, 1);
278 }
279 
280 void
281 alpha_ipi_discard_fpu(struct cpu_info *ci, struct trapframe *framep)
282 {
283 
284 	if (ci->ci_flags & CPUF_FPUSAVE)
285 		return;
286 	fpusave_cpu(ci, 0);
287 }
288 
289 void
290 alpha_ipi_pause(struct cpu_info *ci, struct trapframe *framep)
291 {
292 	u_long cpumask = (1UL << ci->ci_cpuid);
293 	int s;
294 
295 	s = splhigh();
296 
297 	/* Point debuggers at our trapframe for register state. */
298 	ci->ci_db_regs = framep;
299 
300 	atomic_or_ulong(&ci->ci_flags, CPUF_PAUSED);
301 
302 	/* Spin with interrupts disabled until we're resumed. */
303 	do {
304 		alpha_mb();
305 	} while (cpus_paused & cpumask);
306 
307 	atomic_and_ulong(&ci->ci_flags, ~CPUF_PAUSED);
308 
309 	ci->ci_db_regs = NULL;
310 
311 	splx(s);
312 
313 	/* Do an IMB on the way out, in case the kernel text was changed. */
314 	alpha_pal_imb();
315 }
316 
317 /*
318  * MD support for xcall(9) interface.
319  */
320 
321 void
322 alpha_ipi_xcall(struct cpu_info *ci, struct trapframe *framep)
323 {
324 
325 	xc_ipi_handler();
326 }
327 
328 void
329 xc_send_ipi(struct cpu_info *ci)
330 {
331 
332 	KASSERT(kpreempt_disabled());
333 	KASSERT(curcpu() != ci);
334 
335 	if (ci) {
336 		/* Unicast: remote CPU. */
337 		alpha_send_ipi(ci->ci_cpuid, ALPHA_IPI_XCALL);
338 	} else {
339 		/* Broadcast: all, but local CPU (caller will handle it). */
340 		alpha_broadcast_ipi(ALPHA_IPI_XCALL);
341 	}
342 }
343