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