xref: /titanic_41/usr/src/uts/sun4u/starfire/ml/idn_asm.s (revision fd9cb95cbb2f626355a60efb9d02c5f0a33c10e6)
1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License").  You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22/*
23 * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24 * Use is subject to license terms.
25 */
26
27#pragma ident	"%Z%%M%	%I%	%E% SMI"
28
29/*
30 * This file contains the low-level DMV interrupt
31 * handler for IDN cross-domain interrupts.
32 */
33
34#if defined(lint)
35#include <sys/types.h>
36#endif /* lint */
37
38#include <sys/asm_linkage.h>
39#include <sys/machasi.h>
40#include <sys/privregs.h>
41#include <sys/intreg.h>
42#include <sys/machthread.h>
43
44#include <sys/idn.h>
45
46#if !defined(lint)
47#include "idn_offsets.h"
48#endif /* !lint */
49
50#define	IDN_MONDO
51
52/*
53 * The IDN_DMV_CPU_SHIFT is based on the sizeof (idn_dmv_cpu_t)
54 * which must be a power of 2 to optimize calculating our
55 * entry into idn_dmv_cpu[].
56 */
57#define	IDN_DMV_CPU_SHIFT	4
58
59/*
60 *--------------------------------------------------------
61 */
62#if defined(lint)
63
64/*
65 * Would be nice to use init_mondo, but unforunately
66 * it assumes the first arg is 32-bits.
67 */
68/*ARGSUSED*/
69void
70idnxf_init_mondo(uint64_t arg0, uint64_t arg1, uint64_t arg2)
71{}
72
73#else /* lint */
74
75	.global _idn_dispatch_status_busy
76_idn_dispatch_status_busy:
77	.asciz	"ASI_INTR_DISPATCH_STATUS error: busy"
78	.align	4
79
80	ENTRY_NP(idnxf_init_mondo)
81#ifdef DEBUG
82	!
83	! IDSR should not be busy at the moment - borrowed from init_mondo
84	!
85	ldxa	[%g0]ASI_INTR_DISPATCH_STATUS, %g1
86	btst	IDSR_BUSY, %g1
87	bz,pt	%xcc, 1f
88	mov	ASI_INTR_DISPATCH, %asi
89	sethi	%hi(_idn_dispatch_status_busy), %o0
90	call	panic
91	or	%o0, %lo(_idn_dispatch_status_busy), %o0
92#endif /* DEBUG */
93
94	mov	ASI_INTR_DISPATCH, %asi
951:
96	stxa	%o0, [IDDR_0]%asi	! dmv_word0
97	stxa	%o1, [IDDR_1]%asi	! dmv_word1
98	stxa	%o2, [IDDR_2]%asi	! dmv_word2
99
100	retl
101	membar	#Sync
102
103	SET_SIZE(idnxf_init_mondo)
104
105#endif /* lint */
106/*
107 *--------------------------------------------------------
108 */
109#if defined(lint)
110
111/*
112 * Unfortunately, send_mondo is rather picky about getting
113 * a result from the cpu it sends an interrupt to.  If it
114 * doesn't get a result within a specific timeframe it
115 * will panic!  For IDN that's not cool since a cpu hungup
116 * in one could ultimately result in the demise of a cpu
117 * in another domain.  Instead of getting our panties in
118 * a bind, we simply bail out.
119 */
120/*ARGSUSED*/
121int
122idnxf_send_mondo(int upaid)
123{ return (0); }
124
125#else /* lint */
126
127	.seg	".data"
128
129	.global _idn_send_mondo_failure
130_idn_send_mondo_failure:
131	.word	0
132
133	.seg	".text"
134	ENTRY(idnxf_send_mondo)
135	!
136	! NOTE:
137	!	This is stolen from send_mondo.  The changes
138	!	are those ifdef'd with IDN_MONDO
139	!
140	! construct the interrupt dispatch command register in %g1
141	! also, get the dispatch out as SOON as possible
142	! (initial analysis puts the minimum dispatch time at around
143	!  30-60 cycles.  hence, we try to get the dispatch out quickly
144	!  and then start the rapid check loop).
145	!
146	rd	%tick, %o4			! baseline tick
147	sll	%o0, IDCR_PID_SHIFT, %g1	! IDCR<18:14> = upa port id
148	or	%g1, IDCR_OFFSET, %g1		! IDCR<13:0> = 0x70
149	stxa	%g0, [%g1]ASI_INTR_DISPATCH	! interrupt vector dispatch
150#if defined(SF_ERRATA_54)
151	membar	#Sync				! store must occur before load
152	mov	0x20, %g3			! UDBH Control Register Read
153	ldxa	[%g3]ASI_SDB_INTR_R, %g0
154#endif
155	membar	#Sync
156	clr	%o2				! clear NACK counter
157	clr	%o3				! clear BUSY counter
158
159	!
160	! how long, in ticks, are we willing to wait completely
161	!
162	sethi	%hi(xc_tick_limit), %g2
163	ldx	[%g2 + %lo(xc_tick_limit)], %g2
164	add	%g2, %o4, %o5			! compute the limit value
165
166	!
167	! check the dispatch status
168	!
169.check_dispatch:
170	ldxa	[%g0]ASI_INTR_DISPATCH_STATUS, %o1
171	brz,pn	%o1, .dispatch_complete
172	  rd	%tick, %g5
173
174	!
175	! see if we've gone beyond the limit
176	! (can tick ever overflow?)
177	!
178.timeout_primed:
179	sub	%o5, %g5, %g2			! limit - tick < 0 if timeout
180	brgez,pt %g2, .check_busy
181	  inc	%o3				! bump the BUSY counter
182
183#ifdef IDN_MONDO
184	!
185	! Within the context of IDN we don't want
186	! to panic just because we can't send_mondo.
187	! Clear the dispatch register and increment
188	! our count of failures.
189	!
190	stxa	%g0, [%g1]ASI_INTR_DISPATCH
191	sethi	%hi(_idn_send_mondo_failure), %o0
192	ld	[%o0 + %lo(_idn_send_mondo_failure)], %o1
193	inc	%o1
194	st	%o1, [%o0 + %lo(_idn_send_mondo_failure)]
195	retl
196	  mov	-1, %o0				! return (-1)
197#else /* IDN_MONDO */
198	!
199	! time to die, see if we are already panicing
200	!
201	mov	%o0, %o1			! save target
202	sethi	%hi(_send_mondo_nack), %o0
203	or	%o0, %lo(_send_mondo_nack), %o0
204	sethi	%hi(panicstr), %g2
205	ldn	[%g2 + %lo(panicstr)], %g2
206	brnz	%g2, .dispatch_complete		! skip if already in panic
207	  nop
208	call	panic
209	  nop
210#endif /* IDN_MONDO */
211
212.check_busy:
213	btst	IDSR_BUSY, %o1			! was it BUSY?
214	bnz,pt	%xcc, .check_dispatch
215	  nop
216
217	!
218	! we weren't busy, we must have been NACK'd
219	! wait a while and send again
220	! (this might need jitter)
221	!
222	sethi	%hi(sys_clock_mhz), %g2
223	lduw	[%g2 + %lo(sys_clock_mhz)], %g2
224	rd	%tick, %g4
225	add	%g2, %g4, %g2
226.delay:
227	cmp	%g2, %g4
228	bgu,pt	%xcc, .delay
229	rd	%tick, %g4
230
231	stxa	%g0, [%g1]ASI_INTR_DISPATCH	! interrupt vector dispatch
232#if defined(SF_ERRATA_54)
233	membar	#Sync				! store must occur before load
234	ldxa	[%g3]ASI_SDB_INTR_R, %g0
235#endif
236	membar	#Sync
237	clr	%o3				! reset BUSY counter
238	ba	.check_dispatch
239	  inc	%o2				! bump the NACK counter
240
241.dispatch_complete:
242#ifndef IDN_MONDO
243#ifdef SEND_MONDO_STATS
244	!
245	! Increment the appropriate entry in a send_mondo timeout array
246	! x_entry[CPU][MSB]++;
247	sub	%g5, %o4, %g5			! how long did we wait?
248	clr	%o1				! o1 is now bit counter
2491:	orcc	%g5, %g0, %g0			! any bits left?
250	srlx	%g5, 1, %g5			! bits to the right
251	bne,a,pt %xcc, 1b
252	  add	%o1, 4, %o1			! pointer increment
253
254	!
255	! now compute the base of the x_early entry for our cpu
256	!
257	CPU_INDEX(%o0, %g5)
258	sll	%o0, 8, %o0			! 64 * 4
259	add	%o0, %o1, %o1			! %o0 = &[CPU][delay]
260
261	!
262	! and increment the appropriate value
263	!
264	sethi	%hi(x_early), %o0
265	or	%o0, %lo(x_early), %o0
266	ld	[%o0 + %o1], %g5
267	inc	%g5
268	st	%g5, [%o0 + %o1]
269#endif	/* SEND_MONDO_STATS */
270#endif /* !IDN_MONDO */
271	retl
272#ifdef IDN_MONDO
273	  mov	%g0, %o0			! return (0)
274#else /* IDN_MONDO */
275	  nop
276#endif /* IDN_MONDO */
277	SET_SIZE(idnxf_send_mondo)
278
279#endif /* lint */
280/*
281 *--------------------------------------------------------
282 */
283#if defined(lint)
284
285/*ARGSUSED*/
286void
287idn_dmv_handler(void *arg)
288{}
289
290#else /* lint */
291
292	ENTRY_NP(idn_dmv_handler)
293	!
294	! On entry:
295	!	g1 = idn_dmv_data
296	!	g2 = word 0
297	!
298	ldx	[%g1 + IDN_DMV_QBASE], %g4	! g4 = idn_dmv_qbase
299	add	%g1, IDN_DMV_CPU, %g3		! g3 = &idn_dmv_cpu[0]
300
301	CPU_INDEX(%g6, %g5)		! g6 = cpuid
302
303	!
304	! g5 = cur = idn_dmv_cpu[cpuid]
305	!
306	sll	%g6, IDN_DMV_CPU_SHIFT, %g6	! g6 = cpuid * 8
307	add	%g3, IDN_DMV_CURRENT, %g3
308	ld	[%g6 + %g3], %g5
309	!
310	! g5 = idn_dmv_cpu[cpuid].idn_dmv_current
311	!      offset from idn_dmv_qbase
312	!
313	or	%g5, %g0, %g5		! get to 64-bits
314	add	%g5, %g4, %g5		! g5 = idn_dmv_current
315					!      actual address
316	ldstub	[%g5 + IV_INUSE], %g7	! cur->iv_inuse = 0xff
317	brz,pt	%g7, 1f			! did we get it?
318	sub	%g3, IDN_DMV_CURRENT, %g4
319
320	!
321	! Queue is FULL.  Drop interrupt.
322	!
323	add	%g4, IDN_DMV_LOSTINTR, %g3
324	ld	[%g6 + %g3], %g2
325	!
326	! g2 = idn_dmv_cpu[cpuid].idn_iv_lostintr++
327	!
328	inc	%g2
329	set	dmv_finish_intr, %g4
330	st	%g2, [%g3 + %g6]
331	jmp	%g4
332	mov	-1, %g1
333	!
334	! not reached
335	!
336
3371:
338	add	%g4, IDN_DMV_ACTIVE, %g7
339	!
340	! Move current pointer to next one.
341	! idn_dmv_current[cpuid] = cur->iv_next
342	!
343	ld	[%g5 + IV_NEXT], %g4
344	st	%g4, [%g3 + %g6]
345
346	!
347	! Start filling in structure with data.
348	!
349	stx	%g2, [%g5 + IV_HEAD]
350
351	mov	IRDR_1, %g2
352	mov	IRDR_2, %g4
353	ldxa	[%g2]ASI_INTR_RECEIVE, %g2	! g2 = xargs[0,1]
354	ldxa	[%g4]ASI_INTR_RECEIVE, %g4	! g4 = xargs[2,3]
355
356	stx	%g2, [%g5 + IV_XARGS0]
357	stx	%g4, [%g5 + IV_XARGS2]
358
359	membar	#StoreLoad|#StoreStore
360
361	clrb	[%g5 + IV_READY]	! cur->iv_ready = 0 (unlocked)
362
363	!
364	! See if we're already active, i.e. have things
365	! queued.  If so, don't bother generating a soft
366	! interrupt.  IDN interrupts could exhaust the
367	! intr_req structs for the given cpu and that code
368	! doesn't know how to survive with intr_req structs!
369	!
370	ldstub	[%g6 + %g7], %g7	! idn_dmv_active = 0xff
371	brz,a,pt %g7, 2f
372	ld	[%g1 + IDN_SOFT_INUM], %g7	! g7 = idn_soft_inum
373	mov	-1, %g7
3742:
375
376	!
377	! Setup to cause an IDN soft interrupt to occur,
378	! (if necessary).
379	!
380	set	dmv_finish_intr, %g3
381	jmp	%g3
382	mov	%g7, %g1
383
384	SET_SIZE(idn_dmv_handler)
385
386#endif /* lint */
387