Commit | Line | Data |
---|---|---|
920dae64 AT |
1 | /* |
2 | * ========== Copyright Header Begin ========================================== | |
3 | * | |
4 | * Hypervisor Software File: ssi.s | |
5 | * | |
6 | * Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved. | |
7 | * | |
8 | * - Do no alter or remove copyright notices | |
9 | * | |
10 | * - Redistribution and use of this software in source and binary forms, with | |
11 | * or without modification, are permitted provided that the following | |
12 | * conditions are met: | |
13 | * | |
14 | * - Redistribution of source code must retain the above copyright notice, | |
15 | * this list of conditions and the following disclaimer. | |
16 | * | |
17 | * - Redistribution in binary form must reproduce the above copyright notice, | |
18 | * this list of conditions and the following disclaimer in the | |
19 | * documentation and/or other materials provided with the distribution. | |
20 | * | |
21 | * Neither the name of Sun Microsystems, Inc. or the names of contributors | |
22 | * may be used to endorse or promote products derived from this software | |
23 | * without specific prior written permission. | |
24 | * | |
25 | * This software is provided "AS IS," without a warranty of any kind. | |
26 | * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, | |
27 | * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A | |
28 | * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN | |
29 | * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR | |
30 | * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR | |
31 | * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN | |
32 | * OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR | |
33 | * FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE | |
34 | * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, | |
35 | * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF | |
36 | * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. | |
37 | * | |
38 | * You acknowledge that this software is not designed, licensed or | |
39 | * intended for use in the design, construction, operation or maintenance of | |
40 | * any nuclear facility. | |
41 | * | |
42 | * ========== Copyright Header End ============================================ | |
43 | */ | |
44 | /* | |
45 | * Copyright 2007 Sun Microsystems, Inc. All rights reserved. | |
46 | * Use is subject to license terms. | |
47 | */ | |
48 | ||
49 | .ident "@(#)ssi.s 1.2 07/05/04 SMI" | |
50 | ||
51 | .file "intr.s" | |
52 | ||
53 | #include <sys/asm_linkage.h> | |
54 | #include <sys/htypes.h> | |
55 | #include <hprivregs.h> | |
56 | #include <asi.h> | |
57 | #include <fpga.h> | |
58 | #include <ldc.h> | |
59 | #include <iob.h> | |
60 | ||
61 | #include <offsets.h> | |
62 | #include <vcpu.h> | |
63 | #include <abort.h> | |
64 | #include <util.h> | |
65 | ||
66 | /* | |
67 | * fpga_mondo | |
68 | * | |
69 | * %g1 - cpup | |
70 | */ | |
71 | ENTRY(fpga_intr) | |
72 | FPGA_MBOX_INT_DISABLE(IRQ_QUEUE_IN|IRQ_QUEUE_OUT|IRQ_LDC_OUT, %g2, %g3) | |
73 | CLEAR_INT_CTL_PEND(%g2, %g3) | |
74 | ||
75 | #ifdef CONFIG_SVC | |
76 | ba,pt %xcc, svc_isr | |
77 | nop | |
78 | #endif | |
79 | ||
80 | retry | |
81 | SET_SIZE(fpga_intr) | |
82 | ||
83 | ||
84 | /* | |
85 | * ssi_intr_redistribution | |
86 | * | |
87 | * If this cpu is handling ssi intrs, move them to the tgt cpu. | |
88 | * Once the registers are reprogrammed, we send an FPGA intr | |
89 | * to the tgt cpu. We do this to prevent any intrs from being | |
90 | * lost | |
91 | * | |
92 | * %g1 - this cpu id | |
93 | * %g2 - tgt cpu id | |
94 | */ | |
95 | ENTRY_NP(ssi_intr_redistribution) | |
96 | STRAND_PUSH(%g7, %g3, %g4) | |
97 | setx IOBBASE, %g3, %g4 | |
98 | ! %g4 = IOB Base address | |
99 | ||
100 | /* SSI Error interrupt */ | |
101 | .ssi_err_change_cpu: | |
102 | ldx [%g4 + INT_MAN + INT_MAN_DEV_OFF(IOBDEV_SSIERR)], %g3 | |
103 | srlx %g3, INT_MAN_CPU_SHIFT, %g5 ! int_man.cpu | |
104 | and %g5, INT_MAN_CPU_MASK, %g5 | |
105 | cmp %g5, %g1 | |
106 | bne,pt %xcc, .ssi_intr_change_cpu | |
107 | nop | |
108 | ||
109 | ! clear the current cpu value | |
110 | mov INT_MAN_CPU_MASK, %g5 | |
111 | sllx %g5, INT_MAN_CPU_SHIFT, %g5 | |
112 | andn %g3, %g5, %g3 | |
113 | sllx %g2, INT_MAN_CPU_SHIFT, %g6 | |
114 | or %g6, %g3, %g3 | |
115 | ||
116 | stx %g3, [%g4 + INT_MAN + INT_MAN_DEV_OFF(IOBDEV_SSIERR)] | |
117 | ||
118 | /* SSI Interrupt */ | |
119 | .ssi_intr_change_cpu: | |
120 | ldx [%g4 + INT_MAN + INT_MAN_DEV_OFF(IOBDEV_SSI)], %g3 | |
121 | srlx %g3, INT_MAN_CPU_SHIFT, %g5 ! int_man.cpu | |
122 | and %g5, INT_MAN_CPU_MASK, %g5 | |
123 | cmp %g5, %g1 | |
124 | bne,pt %xcc, .mondo_errs_intr_change_cpu | |
125 | nop | |
126 | ||
127 | ! clear the current cpu value | |
128 | mov INT_MAN_CPU_MASK, %g5 | |
129 | sllx %g5, INT_MAN_CPU_SHIFT, %g5 | |
130 | andn %g3, %g5, %g3 | |
131 | sllx %g2, INT_MAN_CPU_SHIFT, %g6 | |
132 | or %g6, %g3, %g3 | |
133 | ||
134 | stx %g3, [%g4 + INT_MAN + INT_MAN_DEV_OFF(IOBDEV_SSI)] | |
135 | ||
136 | ! send a ssi intr to other cpu so we don't miss any intrs | |
137 | sllx %g2, INT_VEC_DIS_VCID_SHIFT, %g5 | |
138 | or %g5, VECINTR_FPGA, %g5 | |
139 | stxa %g5, [%g0]ASI_INTR_UDB_W | |
140 | .mondo_errs_intr_change_cpu: | |
141 | ||
142 | STRAND_POP(%g7, %g3) | |
143 | HVRET | |
144 | SET_SIZE(ssi_intr_redistribution) | |
145 | ||
146 | #ifdef CONFIG_FPGA /* { */ | |
147 | ||
148 | #define INT_ENABLE(x) \ | |
149 | ba isr_common ;\ | |
150 | mov x, %g1 | |
151 | ||
152 | /* | |
153 | * Handles the LDC interrupt from the SP. | |
154 | */ | |
155 | ENTRY_NP(svc_ldc_tx_intr) | |
156 | #ifdef INTR_DEBUG | |
157 | PRINT("got LDX TX\r\n") | |
158 | #endif | |
159 | ||
160 | setx FPGA_LDCOUT_BASE, %g3, %g5 | |
161 | ldub [%g5 + FPGA_Q_STATUS], %g3 ! read status | |
162 | ||
163 | btst SP_LDC_STATE_CHG, %g3 ! Notification for channel | |
164 | bz %xcc, 1f ! state change on the SP side | |
165 | nop | |
166 | set SP_LDC_STATE_CHG, %g1 | |
167 | stub %g1, [%g5 + FPGA_Q_STATUS] ! clear this bit | |
168 | ||
169 | HVCALL(svc_ldc_reset_intr) | |
170 | 1: | |
171 | setx FPGA_LDCOUT_BASE, %g3, %g5 | |
172 | ldub [%g5 + FPGA_Q_STATUS], %g3 ! read status | |
173 | ||
174 | btst SP_LDC_DATA, %g3 ! Notification for data packets | |
175 | bz %xcc, 1f ! available in SRAM | |
176 | nop | |
177 | set SP_LDC_DATA, %g1 | |
178 | stub %g1, [%g5 + FPGA_Q_STATUS] ! clear this bit | |
179 | ||
180 | #ifdef INTR_DEBUG | |
181 | PRINT("LDX TX data available\r\n") | |
182 | #endif | |
183 | HVCALL(svc_ldc_data_available) | |
184 | 1: | |
185 | setx FPGA_LDCOUT_BASE, %g3, %g5 | |
186 | ldub [%g5 + FPGA_Q_STATUS], %g3 ! read status | |
187 | ||
188 | btst SP_LDC_SPACE, %g3 ! Notification that the SP has | |
189 | bz %xcc, 1f ! made room for us in the SRAM | |
190 | nop ! for us to send more data. | |
191 | set SP_LDC_SPACE, %g1 | |
192 | stub %g1, [%g5 + FPGA_Q_STATUS] ! clear this bit | |
193 | ||
194 | HVCALL(svc_ldc_space_available) | |
195 | 1: | |
196 | INT_ENABLE(IRQ_LDC_OUT) | |
197 | SET_SIZE(svc_ldc_tx_intr) | |
198 | ||
199 | ||
200 | /* | |
201 | * Handles the LDC BUSY interrupt from the SP which indicates that the | |
202 | * SP has just sent us one or more LDC packets across the SRAM. | |
203 | * | |
204 | * For guest<->SP connections, we simply check to see if there is | |
205 | * any data available on any of the incoming SRAM queues. If so, | |
206 | * we send notification to the appropriate guest (if the guest's queue | |
207 | * is currently empty). | |
208 | * | |
209 | * %g7 - holds calling pc value. | |
210 | */ | |
211 | ENTRY_NP(svc_ldc_data_available) | |
212 | ||
213 | ROOT_STRUCT(%g1) | |
214 | ldx [%g1 + CONFIG_SP_LDC_MAX_CID], %g2 | |
215 | ldx [%g1 + CONFIG_SP_LDCS], %g1 ! get SP endpoint array | |
216 | mulx %g2, SP_LDC_ENDPOINT_SIZE, %g2 | |
217 | add %g1, %g2, %g1 ! pointer to last SP endpoint | |
218 | ||
219 | ! %g1 = last SP endpoint | |
220 | ! %g7 = return %pc | |
221 | ||
222 | .one_more_ldc_rx_channel: | |
223 | ||
224 | ! %g1 = SP endpoint | |
225 | ! %g7 = return %pc | |
226 | ||
227 | ldub [%g1 + SP_LDC_IS_LIVE], %g2 ! is channel open? | |
228 | brz %g2, .next_ldc_rx_channel | |
229 | nop | |
230 | ||
231 | ! check to see whether there are any packets | |
232 | ! available on this channel. | |
233 | ||
234 | ldx [ %g1 + SP_LDC_RX_QD_PA ], %g2 | |
235 | ldub [ %g2 + SRAM_LDC_HEAD ], %g3 | |
236 | ldub [ %g2 + SRAM_LDC_TAIL ], %g4 | |
237 | ||
238 | cmp %g3, %g4 | |
239 | be %xcc, .next_ldc_rx_channel ! nothing to pick up | |
240 | nop | |
241 | ||
242 | ldub [ %g1 + SP_LDC_TARGET_TYPE ], %g3 | |
243 | cmp %g3, LDC_GUEST_ENDPOINT | |
244 | be .svc_ldc_guest_target | |
245 | nop | |
246 | ||
247 | ! This is a SP<->HV channel so we must read out each packet | |
248 | ! and call the appropriate callback routine for each one. | |
249 | ||
250 | ! NOTE: There is no need to grab the RX_LOCK in this situation | |
251 | ! because we are executing with FPGA interrupts off and this | |
252 | ! is the only routine used for receiving data from the SRAM | |
253 | ! since it is a SP<->HV channel. | |
254 | ||
255 | ! %g1 = sp endpoint | |
256 | ! %g7 = return %pc | |
257 | ||
258 | ! snapshot queue state into our scratch register area | |
259 | ! since we will be copying the data in possibly several | |
260 | ! passes. | |
261 | ||
262 | ldx [ %g1 + SP_LDC_RX_QD_PA ], %g5 | |
263 | ldub [ %g5 + SRAM_LDC_HEAD ], %g3 | |
264 | LDC_SRAM_IDX_TO_OFFSET(%g3) | |
265 | stw %g3, [ %g1 + SP_LDC_RX_SCR_TXHEAD ] ! TX head | |
266 | ldub [ %g5 + SRAM_LDC_TAIL ], %g3 | |
267 | LDC_SRAM_IDX_TO_OFFSET(%g3) | |
268 | stw %g3, [ %g1 + SP_LDC_RX_SCR_TXTAIL ] ! TX tail | |
269 | set (SRAM_LDC_QENTRY_SIZE * SRAM_LDC_ENTRIES_PER_QUEUE), %g3 | |
270 | stx %g3, [ %g1 + SP_LDC_RX_SCR_TXSIZE ] ! TX size | |
271 | ||
272 | ! %g1 = sp endpoint | |
273 | ! %g5 = SRAM Queue base PA (SRAM Queue Descriptor) | |
274 | ! %g7 = return %pc | |
275 | ||
276 | .read_more_sram_pkts: | |
277 | ||
278 | ! %g1 = sp endpoint | |
279 | ! %g5 = SRAM Queue base PA (SRAM Queue Descriptor) | |
280 | ! %g7 = return %pc | |
281 | ||
282 | lduw [ %g1 + SP_LDC_RX_SCR_TXHEAD ], %g2 | |
283 | lduw [ %g1 + SP_LDC_RX_SCR_TXTAIL ], %g3 | |
284 | ldx [ %g1 + SP_LDC_RX_SCR_TXSIZE ], %g4 | |
285 | ||
286 | LDC_QUEUE_DATA_AVAILABLE(%g2, %g3, %g4) | |
287 | LDC_SRAM_OFFSET_TO_IDX(%g3) | |
288 | ||
289 | ! %g1 = sp endpoint | |
290 | ! %g2 = TX head offset | |
291 | ! %g3 = packets of data to copy | |
292 | ! %g5 = SRAM Queue base PA (SRAM Queue Descriptor) | |
293 | ! %g7 = return %pc | |
294 | ||
295 | brlez %g3, .done_read_sram_pkts | |
296 | nop | |
297 | add %g2, %g5, %g2 ! PA of TX queue data | |
298 | add %g1, SP_LDC_RX_SCR_PKT, %g4 ! PA of RX scratch buffer | |
299 | ||
300 | ! %g1 = sp endpoint | |
301 | ! %g2 = TX head PA | |
302 | ! %g4 = payload buffer | |
303 | ! %g5 = SRAM Queue base PA (SRAM Queue Descriptor) | |
304 | ! %g7 = return %pc | |
305 | ||
306 | LDC_COPY_PKT_FROM_SRAM(%g2, %g4, %g3, %g6) | |
307 | ||
308 | ! %g1 = sp endpoint | |
309 | ! %g2 = new TX head PA | |
310 | ! %g5 = SRAM Queue base PA (SRAM Queue Descriptor) | |
311 | ! %g7 = return %pc | |
312 | ||
313 | ! Now we need to update our scratchpad head pointer | |
314 | sub %g2, %g5, %g2 ! New TX head offset | |
315 | ldx [ %g1 + SP_LDC_RX_SCR_TXSIZE ], %g6 | |
316 | cmp %g2, %g6 | |
317 | move %xcc, 0, %g2 ! check for wrap around | |
318 | stw %g2, [ %g1 + SP_LDC_RX_SCR_TXHEAD ] | |
319 | ||
320 | ! %g1 = sp endpoint | |
321 | ! %g7 = return %pc | |
322 | ||
323 | VCPU_STRUCT(%g3) | |
324 | ldx [%g3 + CPU_ROOT], %g3 | |
325 | ldx [%g3 + CONFIG_HV_LDCS], %g3 ! get HV endpoint array | |
326 | ldx [%g1 + SP_LDC_TARGET_CHANNEL], %g6 ! and target endpoint | |
327 | mulx %g6, LDC_ENDPOINT_SIZE, %g4 | |
328 | add %g3, %g4, %g4 ! and its struct | |
329 | ||
330 | ldx [%g4 + LDC_RX_CB], %g6 ! get the callback | |
331 | brz,pn %g6, .done_read_sram_pkts ! if none, drop pkt | |
332 | nop | |
333 | ||
334 | STRAND_PUSH(%g1, %g2, %g3) | |
335 | STRAND_PUSH(%g7, %g2, %g3) | |
336 | ||
337 | add %g1, SP_LDC_RX_SCR_PKT, %g2 ! payload | |
338 | ldx [%g4 + LDC_RX_CBARG], %g1 ! load the argument | |
339 | ||
340 | ! %g1 = call back arg | |
341 | ! %g2 = payload PA | |
342 | ! %g6 = callback | |
343 | ||
344 | jmp %g6 ! invoke callback | |
345 | rd %pc, %g7 | |
346 | ||
347 | ! Assume all %g registers clobbered | |
348 | ||
349 | STRAND_POP(%g7, %g2) | |
350 | STRAND_POP(%g1, %g2) | |
351 | ldx [%g1 + SP_LDC_RX_QD_PA], %g5 | |
352 | ||
353 | ! %g1 = sp endpoint | |
354 | ! %g5 = SRAM Queue base PA (SRAM Queue Descriptor) | |
355 | ! %g7 = return %pc | |
356 | ||
357 | ba .read_more_sram_pkts | |
358 | nop | |
359 | ||
360 | .done_read_sram_pkts: | |
361 | ||
362 | ! %g1 = sp endpoint | |
363 | ! %g5 = SRAM Queue base PA (SRAM Queue Descriptor) | |
364 | ! %g7 = return %pc | |
365 | ||
366 | lduw [ %g1 + SP_LDC_RX_SCR_TXHEAD ], %g3 | |
367 | LDC_SRAM_OFFSET_TO_IDX(%g3) | |
368 | stb %g3, [ %g5 + SRAM_LDC_HEAD ] ! commit the new TX head | |
369 | ||
370 | ! %g1 target endpoint | |
371 | LDC_SEND_SP_INTR(%g1, %g3, %g4, SP_LDC_SPACE) | |
372 | ||
373 | ! %g1 = SP endpoint | |
374 | ! %g7 = return %pc | |
375 | ||
376 | ! At this point, since we just updated the SRAM head index, we | |
377 | ! need re-read the head/tail value from SRAM and make sure no new | |
378 | ! packets were added while we were processing the last one. | |
379 | ba,a .one_more_ldc_rx_channel | |
380 | ||
381 | .svc_ldc_guest_target: | |
382 | ||
383 | ! %g1 = SP endpoint | |
384 | ! %g7 = return %pc | |
385 | ||
386 | ldx [ %g1 + SP_LDC_TARGET_GUEST ], %g3 | |
387 | brz %g3, .next_ldc_rx_channel | |
388 | nop | |
389 | ||
390 | ldx [ %g1 + SP_LDC_TARGET_CHANNEL ], %g6 | |
391 | mulx %g6, LDC_ENDPOINT_SIZE, %g4 | |
392 | set GUEST_LDC_ENDPOINT, %g5 | |
393 | add %g5, %g3, %g5 | |
394 | add %g4, %g5, %g3 | |
395 | ||
396 | ! %g1 = SP endpoint | |
397 | ! %g3 = guest endpoint | |
398 | ! %g7 = return %pc | |
399 | ||
400 | ! See if we need to send an interrupt to the recipient | |
401 | ldx [ %g3 + LDC_RX_MAPREG + LDC_MAPREG_CPUP ], %g6 | |
402 | brnz,pt %g6, 1f | |
403 | nop | |
404 | ||
405 | ! if no target CPU specified, is there a vdev interrupt we | |
406 | ! need to generate? | |
407 | ldx [ %g3 + LDC_RX_VINTR_COOKIE ], %g6 | |
408 | brz,pn %g6, .next_ldc_rx_channel ! if not, we are done. | |
409 | nop | |
410 | ||
411 | STRAND_PUSH(%g1, %g2, %g4) | |
412 | STRAND_PUSH(%g3, %g2, %g4) | |
413 | STRAND_PUSH(%g7, %g2, %g4) | |
414 | ||
415 | mov %g6, %g1 | |
416 | HVCALL(vdev_intr_generate) | |
417 | ||
418 | STRAND_POP(%g7, %g2) | |
419 | STRAND_POP(%g3, %g2) | |
420 | STRAND_POP(%g1, %g2) | |
421 | ||
422 | ! %g1 = SP endpoint | |
423 | ! %g3 = guest endpoint | |
424 | ! %g7 = return %pc | |
425 | ||
426 | ba .next_ldc_rx_channel | |
427 | nop | |
428 | 1: | |
429 | ! %g1 = SP endpoint | |
430 | ! %g3 = guest endpoint | |
431 | ! %g6 = target cpu struct | |
432 | ! %g7 = return %pc | |
433 | ||
434 | ! Only need to send notification if guest RX queue is empty. | |
435 | ! No synchronization issues with respect to lost notification here | |
436 | ! because the guest's rx_set_qhead routine pulls data from the SRAM | |
437 | ! after updating the head pointer with the guest specified value. | |
438 | lduw [ %g3 + LDC_RX_QHEAD ], %g4 | |
439 | lduw [ %g3 + LDC_RX_QTAIL ], %g5 | |
440 | cmp %g4, %g5 | |
441 | bne %xcc, .next_ldc_rx_channel | |
442 | nop | |
443 | ||
444 | ! %g1 = SP endpoint | |
445 | ! %g3 = guest endpoint | |
446 | ! %g6 = target cpu struct | |
447 | ! %g7 = return %pc | |
448 | STRAND_PUSH(%g1, %g2, %g4) | |
449 | STRAND_PUSH(%g7, %g2, %g4) | |
450 | STRAND_PUSH(%g6, %g2, %g4) | |
451 | HVCALL(hv_ldc_cpu_notify) | |
452 | STRAND_POP(%g6, %g2) | |
453 | STRAND_POP(%g7, %g2) | |
454 | STRAND_POP(%g1, %g2) | |
455 | ||
456 | ! %g1 = SP endpoint | |
457 | ! %g7 = return %pc | |
458 | ||
459 | .next_ldc_rx_channel: | |
460 | ||
461 | ! %g1 = SP endpoint | |
462 | ! %g7 = return %pc | |
463 | ||
464 | ROOT_STRUCT(%g2) | |
465 | ldx [%g2 + CONFIG_SP_LDCS], %g2 ! first SP endpoint | |
466 | cmp %g1, %g2 ! did we just process it? | |
467 | bgu,pt %xcc, .one_more_ldc_rx_channel | |
468 | sub %g1, SP_LDC_ENDPOINT_SIZE, %g1 | |
469 | ||
470 | HVRET | |
471 | SET_SIZE(svc_ldc_data_available) | |
472 | ||
473 | ||
474 | /* | |
475 | * Handles the LDC ACK interrupt from the SP which indicates that the | |
476 | * SP has freed up some room in the SRAM LDC queues for us so that we | |
477 | * may once again send more packets if needed. | |
478 | * | |
479 | * %g7 - holds calling pc value. | |
480 | */ | |
481 | ENTRY_NP(svc_ldc_space_available) | |
482 | ||
483 | ROOT_STRUCT(%g1) | |
484 | ldx [%g1 + CONFIG_SP_LDC_MAX_CID], %g2 | |
485 | ldx [%g1 + CONFIG_SP_LDCS], %g1 ! get SP endpoint array | |
486 | mulx %g2, SP_LDC_ENDPOINT_SIZE, %g2 | |
487 | add %g1, %g2, %g1 ! pointer to last SP endpoint | |
488 | clr %g6 ! SP notification flag | |
489 | ||
490 | ! %g1 = last SP endpoint | |
491 | ! %g6 = 0 (notification flag) | |
492 | ! %g7 = return PC | |
493 | ||
494 | .one_more_ldc_tx_channel: | |
495 | ||
496 | ! %g1 = SP endpoint | |
497 | ! %g6 = (notification flag) | |
498 | ! %g7 = return PC | |
499 | ||
500 | ldub [%g1 + SP_LDC_IS_LIVE], %g2 ! is channel open? | |
501 | brz %g2, .next_ldc_tx_channel | |
502 | nop | |
503 | ||
504 | ! If this is a SP <-> HV connection (guest ptr==NULL) then | |
505 | ! there is nothing really to do. | |
506 | ldx [ %g1 + SP_LDC_TARGET_GUEST ], %g3 | |
507 | brz %g3, .next_ldc_tx_channel | |
508 | nop | |
509 | ||
510 | ! SP <-> Guest channel | |
511 | ||
512 | ldx [ %g1 + SP_LDC_TARGET_CHANNEL ], %g4 | |
513 | mulx %g4, LDC_ENDPOINT_SIZE, %g4 | |
514 | set GUEST_LDC_ENDPOINT, %g5 | |
515 | add %g5, %g3, %g5 | |
516 | add %g4, %g5, %g3 | |
517 | ||
518 | ! %g1 = SP endpoint | |
519 | ! %g3 = guest endpoint | |
520 | ! %g6 = (notification flag) | |
521 | ! %g7 = return PC | |
522 | ||
523 | ! Nothing to send if guest TX queue is empty | |
524 | lduw [ %g3 + LDC_TX_QHEAD ], %g4 | |
525 | lduw [ %g3 + LDC_TX_QTAIL ], %g5 | |
526 | cmp %g4, %g5 | |
527 | be %xcc, .next_ldc_tx_channel | |
528 | nop | |
529 | ||
530 | ! %g1 = SP endpoint | |
531 | ! %g3 = guest endpoint | |
532 | ||
533 | mov %g3, %g2 | |
534 | mov %g1, %g3 | |
535 | ||
536 | ! %g2 = guest endpoint | |
537 | ! %g3 = sp endpoint | |
538 | ||
539 | STRAND_PUSH(%g2, %g1, %g4) ! save guest endpoint | |
540 | ||
541 | add %g3, SP_LDC_TX_LOCK, %g5 | |
542 | SPINLOCK_ENTER(%g5, %g1, %g4) | |
543 | ||
544 | STRAND_PUSH(%g7, %g4, %g5) | |
545 | HVCALL(sram_ldc_push_data) ! %g3 (sp endpoint) preserved | |
546 | STRAND_POP(%g7, %g5) | |
547 | ||
548 | add %g3, SP_LDC_TX_LOCK, %g5 | |
549 | SPINLOCK_EXIT(%g5) | |
550 | ||
551 | mov %g3, %g1 | |
552 | ||
553 | clr %g3 | |
554 | movrnz %g2, 1, %g3 ! %g2 = send interrupt flag | |
555 | or %g6, %g3, %g6 | |
556 | ||
557 | ! %g1 = sp endpoint | |
558 | ! %g6 = (notification flag) | |
559 | ! %g7 = return PC | |
560 | ||
561 | STRAND_POP(%g2, %g3) ! restore guest endpoint | |
562 | ||
563 | ! %g2 = sender's (guest) endpoint | |
564 | ||
565 | ! We might need to send a 'queue no longer full' interrupt | |
566 | ! in certain situations. | |
567 | ldub [ %g2 + LDC_TXQ_FULL ], %g3 | |
568 | brz,pt %g3, .next_ldc_tx_channel | |
569 | nop | |
570 | stb %g0, [ %g2 + LDC_TXQ_FULL ] | |
571 | ||
572 | ldx [%g2 + LDC_RX_VINTR_COOKIE], %g2 | |
573 | brz %g2, .next_ldc_tx_channel | |
574 | nop | |
575 | ||
576 | ! save off registers | |
577 | STRAND_PUSH(%g1, %g3, %g4) | |
578 | STRAND_PUSH(%g6, %g3, %g4) | |
579 | STRAND_PUSH(%g7, %g3, %g4) | |
580 | ||
581 | mov %g2, %g1 | |
582 | HVCALL(vdev_intr_generate) | |
583 | ||
584 | ! restore registers | |
585 | STRAND_POP(%g7, %g3) | |
586 | STRAND_POP(%g6, %g3) | |
587 | STRAND_POP(%g1, %g3) | |
588 | ||
589 | .next_ldc_tx_channel: | |
590 | ||
591 | ! %g1 = SP endpoint | |
592 | ! %g6 = (notification flag) | |
593 | ! %g7 = return PC | |
594 | ||
595 | ROOT_STRUCT(%g2) | |
596 | ldx [%g2 + CONFIG_SP_LDCS], %g2 ! first SP endpoint | |
597 | cmp %g1, %g2 ! did we just process it? | |
598 | bgu,pt %xcc, .one_more_ldc_tx_channel | |
599 | sub %g1, SP_LDC_ENDPOINT_SIZE, %g1 | |
600 | ||
601 | ! Done checking all SRAM queues | |
602 | btst 1, %g6 | |
603 | bz %xcc, 1f ! skip TX notification if flag is clear | |
604 | nop | |
605 | ||
606 | ! %g1 target endpoint | |
607 | LDC_SEND_SP_INTR(%g1, %g6, %g4, SP_LDC_DATA) | |
608 | 1: | |
609 | HVRET | |
610 | SET_SIZE(svc_ldc_space_available) | |
611 | ||
612 | ||
613 | /* | |
614 | * Called when the SP sends us notification for a channel reset so that | |
615 | * we can forward the notification interrupt to a guest if necessary. | |
616 | * | |
617 | * %g7 - holds calling pc value. | |
618 | */ | |
619 | ENTRY_NP(svc_ldc_reset_intr) | |
620 | ROOT_STRUCT(%g1) | |
621 | ldx [%g1 + CONFIG_SP_LDC_MAX_CID], %g2 | |
622 | ldx [%g1 + CONFIG_SP_LDCS], %g1 ! get SP endpoint array | |
623 | ||
624 | mulx %g2, SP_LDC_ENDPOINT_SIZE, %g2 | |
625 | add %g1, %g2, %g1 ! pointer to last SP endpoint | |
626 | ||
627 | .one_more_sram_channel: | |
628 | ||
629 | ! %g1 = SP endpoint | |
630 | ||
631 | ldub [%g1 + SP_LDC_IS_LIVE], %g2 ! is channel open? | |
632 | brz %g2, .next_ldc_sram_channel | |
633 | nop | |
634 | ||
635 | ! check to see whether there is a reset notification pending | |
636 | ! for this channel. | |
637 | ldx [ %g1 + SP_LDC_TX_QD_PA ], %g2 | |
638 | ldub [ %g2 + SRAM_LDC_STATE_NOTIFY ], %g3 | |
639 | brz,pt %g3, .next_ldc_sram_channel | |
640 | nop | |
641 | ||
642 | ! reset notification is pending for this channel | |
643 | stb %g0, [ %g2 + SRAM_LDC_STATE_NOTIFY ] ! clear the flag | |
644 | ||
645 | ! For SP<->HV connections (guest ptr==NULL), there is nothing to | |
646 | ! do at this point. | |
647 | ldx [ %g1 + SP_LDC_TARGET_GUEST ], %g3 | |
648 | brz %g3, .next_ldc_sram_channel | |
649 | nop | |
650 | ||
651 | ldx [ %g1 + SP_LDC_TARGET_CHANNEL ], %g4 | |
652 | mulx %g4, LDC_ENDPOINT_SIZE, %g4 | |
653 | set GUEST_LDC_ENDPOINT, %g5 | |
654 | add %g5, %g3, %g5 | |
655 | add %g4, %g5, %g3 | |
656 | ||
657 | ! %g1 = SP endpoint | |
658 | ! %g3 = guest endpoint | |
659 | ||
660 | ! skip notification if no CPU is specified to handle interrupts | |
661 | ldx [ %g3 + LDC_RX_MAPREG + LDC_MAPREG_CPUP ], %g6 | |
662 | brz,pn %g6, .next_ldc_sram_channel | |
663 | nop | |
664 | ||
665 | ! %g1 = SP endpoint | |
666 | ! %g3 = guest endpoint | |
667 | ! %g6 = target cpu struct | |
668 | STRAND_PUSH(%g1, %g2, %g4) | |
669 | STRAND_PUSH(%g7, %g2, %g4) | |
670 | STRAND_PUSH(%g6, %g2, %g4) | |
671 | HVCALL(hv_ldc_cpu_notify) | |
672 | STRAND_POP(%g6, %g2) | |
673 | STRAND_POP(%g7, %g2) | |
674 | STRAND_POP(%g1, %g2) | |
675 | ! %g1 = SP endpoint | |
676 | ||
677 | .next_ldc_sram_channel: | |
678 | ||
679 | ROOT_STRUCT(%g2) | |
680 | ldx [%g2 + CONFIG_SP_LDCS], %g2 ! first SP endpoint | |
681 | cmp %g1, %g2 ! did we just process it? | |
682 | bgu,pt %xcc, .one_more_sram_channel | |
683 | sub %g1, SP_LDC_ENDPOINT_SIZE, %g1 | |
684 | ||
685 | HVRET | |
686 | SET_SIZE(svc_ldc_reset_intr) | |
687 | ||
688 | #endif /* CONFIG_FPGA } */ | |
689 |