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.4 07/06/20 SMI" | |
50 | ||
51 | .file "ssi.s" | |
52 | ||
53 | #ifdef CONFIG_FPGA | |
54 | ||
55 | #include <sys/asm_linkage.h> | |
56 | #include <sys/htypes.h> | |
57 | #include <asi.h> | |
58 | #include <offsets.h> | |
59 | #include <util.h> | |
60 | #include <fpga.h> | |
61 | #include <ldc.h> | |
62 | #include <intr.h> | |
63 | #include <vdev_ops.h> | |
64 | ||
65 | /* | |
66 | * fpga_intr | |
67 | * | |
68 | * %g1 - cpup | |
69 | */ | |
70 | ENTRY(fpga_intr) | |
71 | ||
72 | FPGA_MBOX_INT_DISABLE(IRQ_QUEUE_IN|IRQ_QUEUE_OUT|IRQ_LDC_OUT, %g2, %g3) | |
73 | ||
74 | #ifdef CONFIG_FPGA_UART | |
75 | mov DEVOPS_FPGA, %g1 | |
76 | GUEST_STRUCT(%g4) | |
77 | DEVINST2COOKIE(%g4, %g1, %g1, %g5, badtrap) | |
78 | ||
79 | ||
80 | HVCALL(fpga_uart_mondo_receive) | |
81 | ||
82 | VCPU_STRUCT(%g1) | |
83 | #endif /* CONFIG_FPGA_UART */ | |
84 | ||
85 | #ifdef CONFIG_SVC | |
86 | ba,pt %xcc, svc_isr | |
87 | nop | |
88 | #else | |
89 | retry | |
90 | #endif | |
91 | SET_SIZE(fpga_intr) | |
92 | ||
93 | /* | |
94 | * Move SSI interrupts from CPU %g1 onto CPU %g2 | |
95 | */ | |
96 | ENTRY(ssi_redistribute_interrupts) | |
97 | ||
98 | setx NCU_BASE, %g3, %g4 | |
99 | ldx [%g4 + INT_MAN + INT_MAN_DEV_OFF(NCUDEV_SSIERR)], %g5 | |
100 | srl %g5, INTRTGT_DEVSHIFT, %g5 | |
101 | and %g5, INTRTGT_CPUMASK, %g5 | |
102 | ! %g5 current interrupt target | |
103 | ! %g1 CPU in error | |
104 | cmp %g1, %g5 | |
105 | be,pt %xcc, 1f | |
106 | nop | |
107 | ||
108 | /* | |
109 | * The CPU in error does not have any interrupts targetted at | |
110 | * it so just exit | |
111 | */ | |
112 | HVRET | |
113 | 1: | |
114 | ||
115 | mov %g2, %g1 ! %g1 new interrupt target | |
116 | sllx %g1, 1 * INTRTGT_DEVSHIFT, %g2 | |
117 | sllx %g1, 2 * INTRTGT_DEVSHIFT, %g3 | |
118 | or %g3, %g2, %g3 | |
119 | ROOT_STRUCT(%g2) | |
120 | stx %g3, [%g2 + CONFIG_INTRTGT] | |
121 | ||
122 | /* | |
123 | * setup the map registers for the SSI | |
124 | * %g1 intrtgt CPUID array (8-bits per INT_MAN target) | |
125 | * %g4 NCU Base address | |
126 | */ | |
127 | ||
128 | /* | |
129 | * SSI Error interrupt | |
130 | */ | |
131 | srl %g1, INTRTGT_DEVSHIFT, %g1 ! get dev1 bits in bottom | |
132 | and %g1, INTRTGT_CPUMASK, %g3 | |
133 | sllx %g3, INT_MAN_CPU_SHIFT, %g3 ! int_man.cpu | |
134 | or %g3, VECINTR_SSIERR, %g3 ! int_man.vecnum | |
135 | stx %g3, [%g4 + INT_MAN + INT_MAN_DEV_OFF(NCUDEV_SSIERR)] | |
136 | ||
137 | /* | |
138 | * SSI Interrupt | |
139 | */ | |
140 | srl %g1, INTRTGT_DEVSHIFT, %g1 ! get dev2 bits in bottom | |
141 | and %g1, INTRTGT_CPUMASK, %g3 | |
142 | sllx %g3, INT_MAN_CPU_SHIFT, %g3 ! int_man.cpu | |
143 | or %g3, VECINTR_FPGA, %g3 ! int_man.vecnum | |
144 | stx %g3, [%g4 + INT_MAN + INT_MAN_DEV_OFF(NCUDEV_SSI)] | |
145 | ||
146 | HVRET | |
147 | SET_SIZE(ssi_redistribute_interrupts) | |
148 | ||
149 | #define INT_ENABLE(x) \ | |
150 | ba isr_common ;\ | |
151 | mov x, %g1 | |
152 | ||
153 | /* | |
154 | * Handles the LDC interrupt from the SP. | |
155 | */ | |
156 | ENTRY_NP(svc_ldc_tx_intr) | |
157 | ||
158 | #ifdef INTR_DEBUG | |
159 | PRINT("got LDX TX\r\n") | |
160 | #endif | |
161 | ||
162 | setx FPGA_LDCIN_BASE, %g3, %g5 | |
163 | ld [%g5 + FPGA_LDC_RECV_REG], %g3 ! read recv | |
164 | ||
165 | ! %g3 RECV register | |
166 | .ldc_recv_intrs: | |
167 | ||
168 | ! If no more interrupts are pending, exit | |
169 | brz,pn %g3, .svc_ldc_tx_intr_exit | |
170 | .empty | |
171 | ||
172 | /* | |
173 | * The RECV register bits [30:16] are for data notifications | |
174 | * for the corresponding channel. | |
175 | * | |
176 | * The RECV register bits [14:0] are for space available | |
177 | * notifications for the corresponding channel. | |
178 | * | |
179 | * RECV register bit[31] is for STATE_CHG notification. | |
180 | */ | |
181 | ||
182 | ! Find the first bit set in the RECV register | |
183 | neg %g3, %g4 | |
184 | xnor %g3, %g4, %g6 | |
185 | popc %g6, %g4 | |
186 | dec %g4 | |
187 | ||
188 | ! %g3 RECV register contents | |
189 | ! %g4 first bit set | |
190 | ! %g5 FPGA_LDCIN_BASE | |
191 | ||
192 | /* | |
193 | * Clear bit %g4 in RECV register (%g3) | |
194 | */ | |
195 | mov 1, %g6 | |
196 | sllx %g6, %g4, %g6 | |
197 | st %g6, [%g5 + FPGA_LDC_RECV_REG] ! RW1C | |
198 | ||
199 | cmp %g4, FPGA_LDC_RECV_TX_CHANNELS ! space notification | |
200 | bl %xcc, .ldc_tx_intr | |
201 | cmp %g4, FPGA_LDC_RECV_RX_CHANNELS ! data notification | |
202 | bl %xcc, .ldc_rx_intr | |
203 | sub %g4, FPGA_LDC_RECV_TX_CHANNELS + 1, %g4 ! bit 16 -> channel 0 ... | |
204 | ||
205 | /* FALLTHRU */ | |
206 | ||
207 | .ldc_reset_intr: | |
208 | ||
209 | #ifdef INTR_DEBUG | |
210 | PRINT("LDX Reset\r\n") | |
211 | #endif | |
212 | ||
213 | HVCALL(svc_ldc_reset_intr) | |
214 | ||
215 | setx FPGA_LDCIN_BASE, %g3, %g5 | |
216 | ba,pt %xcc, .ldc_recv_intrs | |
217 | ld [%g5 + FPGA_LDC_RECV_REG], %g3 ! read recv | |
218 | ||
219 | .ldc_rx_intr: | |
220 | ||
221 | #ifdef INTR_DEBUG | |
222 | PRINT("LDX TX data available\r\n") | |
223 | #endif | |
224 | ||
225 | ! %g4 channel number | |
226 | HVCALL(svc_ldc_data_available) | |
227 | ||
228 | ! next notification | |
229 | setx FPGA_LDCIN_BASE, %g3, %g5 | |
230 | ba,pt %xcc, .ldc_recv_intrs | |
231 | ld [%g5 + FPGA_LDC_RECV_REG], %g3 ! read recv | |
232 | ||
233 | .ldc_tx_intr: | |
234 | ||
235 | #ifdef INTR_DEBUG | |
236 | PRINT("LDX TX space available\r\n") | |
237 | #endif | |
238 | ||
239 | ! %g4 channel number | |
240 | HVCALL(svc_ldc_space_available) | |
241 | ||
242 | ! next notification | |
243 | setx FPGA_LDCIN_BASE, %g3, %g5 | |
244 | ba,pt %xcc, .ldc_recv_intrs | |
245 | ld [%g5 + FPGA_LDC_RECV_REG], %g3 ! read recv | |
246 | ||
247 | .svc_ldc_tx_intr_exit: | |
248 | ||
249 | ! back to ISR handling | |
250 | INT_ENABLE(IRQ_LDC_OUT) | |
251 | /* NOTREACHED */ | |
252 | ||
253 | SET_SIZE(svc_ldc_tx_intr) | |
254 | ||
255 | ||
256 | /* | |
257 | * Handles the LDC BUSY interrupt from the SP which indicates that the | |
258 | * SP has just sent us one or more LDC packets across the SRAM. | |
259 | * | |
260 | * For guest<->SP connections, we simply check to see if there is | |
261 | * any data available on any of the incoming SRAM queues. If so, | |
262 | * we send notification to the appropriate guest (if the guest's queue | |
263 | * is currently empty). | |
264 | * | |
265 | * %g4 - channel | |
266 | * %g7 - holds calling pc value. | |
267 | */ | |
268 | ENTRY_NP(svc_ldc_data_available) | |
269 | ||
270 | ROOT_STRUCT(%g1) | |
271 | ldx [%g1 + CONFIG_SP_LDCS], %g1 ! get SP endpoint array | |
272 | mulx %g4, SP_LDC_ENDPOINT_SIZE, %g2 | |
273 | add %g1, %g2, %g1 ! pointer to SP endpoint | |
274 | ||
275 | ! %g1 = SP endpoint which received the notification | |
276 | ! %g7 = return %pc | |
277 | ||
278 | ||
279 | ||
280 | ldub [%g1 + SP_LDC_IS_LIVE], %g2 ! is channel open? | |
281 | brz %g2, .exit_ldc_rx_channel | |
282 | nop | |
283 | ||
284 | .one_more_ldc_rx_channel: | |
285 | ||
286 | ! check to see whether there are any packets | |
287 | ! available on this channel. | |
288 | ||
289 | ldx [ %g1 + SP_LDC_RX_QD_PA ], %g2 | |
290 | ldub [ %g2 + SRAM_LDC_HEAD ], %g3 | |
291 | ldub [ %g2 + SRAM_LDC_TAIL ], %g4 | |
292 | ||
293 | cmp %g3, %g4 | |
294 | be %xcc, .exit_ldc_rx_channel ! nothing to pick up | |
295 | nop | |
296 | ||
297 | ldub [ %g1 + SP_LDC_TARGET_TYPE ], %g3 | |
298 | cmp %g3, LDC_GUEST_ENDPOINT | |
299 | be .svc_ldc_guest_target | |
300 | nop | |
301 | ||
302 | ! This is a SP<->HV channel so we must read out each packet | |
303 | ! and call the appropriate callback routine for each one. | |
304 | ||
305 | ! NOTE: There is no need to grab the RX_LOCK in this situation | |
306 | ! because we are executing with FPGA interrupts off and this | |
307 | ! is the only routine used for receiving data from the SRAM | |
308 | ! since it is a SP<->HV channel. | |
309 | ||
310 | ! %g1 = sp endpoint | |
311 | ! %g7 = return %pc | |
312 | ||
313 | ! snapshot queue state into our scratch register area | |
314 | ! since we will be copying the data in possibly several | |
315 | ! passes. | |
316 | ||
317 | ldx [ %g1 + SP_LDC_RX_QD_PA ], %g5 | |
318 | ldub [ %g5 + SRAM_LDC_HEAD ], %g3 | |
319 | LDC_SRAM_IDX_TO_OFFSET(%g3) | |
320 | stw %g3, [ %g1 + SP_LDC_RX_SCR_TXHEAD ] ! TX head | |
321 | ldub [ %g5 + SRAM_LDC_TAIL ], %g3 | |
322 | LDC_SRAM_IDX_TO_OFFSET(%g3) | |
323 | stw %g3, [ %g1 + SP_LDC_RX_SCR_TXTAIL ] ! TX tail | |
324 | set (SRAM_LDC_QENTRY_SIZE * SRAM_LDC_ENTRIES_PER_QUEUE), %g3 | |
325 | stx %g3, [ %g1 + SP_LDC_RX_SCR_TXSIZE ] ! TX size | |
326 | ||
327 | ! %g1 = sp endpoint | |
328 | ! %g5 = SRAM Queue base PA (SRAM Queue Descriptor !CONFIG_SPLIT_SRAM) | |
329 | ! %g7 = return %pc | |
330 | ||
331 | .read_more_sram_pkts: | |
332 | ||
333 | ! %g1 = sp endpoint | |
334 | ! %g5 = SRAM Queue base PA (SRAM Queue Descriptor !CONFIG_SPLIT_SRAM) | |
335 | ! %g7 = return %pc | |
336 | ||
337 | lduw [ %g1 + SP_LDC_RX_SCR_TXHEAD ], %g2 | |
338 | lduw [ %g1 + SP_LDC_RX_SCR_TXTAIL ], %g3 | |
339 | ldx [ %g1 + SP_LDC_RX_SCR_TXSIZE ], %g4 | |
340 | ||
341 | LDC_QUEUE_DATA_AVAILABLE(%g2, %g3, %g4) | |
342 | LDC_SRAM_OFFSET_TO_IDX(%g3) | |
343 | ||
344 | ! %g1 = sp endpoint | |
345 | ! %g2 = TX head offset | |
346 | ! %g3 = packets of data to copy | |
347 | ! %g5 = SRAM Queue base PA (SRAM Queue Descriptor !CONFIG_SPLIT_SRAM) | |
348 | ! %g7 = return %pc | |
349 | ||
350 | brlez %g3, .done_read_sram_pkts | |
351 | nop | |
352 | ||
353 | #ifdef CONFIG_SPLIT_SRAM | |
354 | ldx [ %g1 + SP_LDC_RX_Q_DATA_PA ], %g5 ! queue data PA | |
355 | #endif | |
356 | ||
357 | add %g2, %g5, %g2 ! PA of TX queue data | |
358 | add %g1, SP_LDC_RX_SCR_PKT, %g4 ! PA of RX scratch buffer | |
359 | ||
360 | ! %g1 = sp endpoint | |
361 | ! %g2 = TX head PA | |
362 | ! %g4 = payload buffer | |
363 | ! %g5 = SRAM Queue base PA (SRAM Queue Descriptor !CONFIG_SPLIT_SRAM) | |
364 | ! - or - | |
365 | ! %g5 = SRAM Queue Data PA (CONFIG_SPLIT_SRAM) | |
366 | ! %g7 = return %pc | |
367 | ||
368 | LDC_COPY_PKT_FROM_SRAM(%g2, %g4, %g3, %g6) | |
369 | ||
370 | ! %g1 = sp endpoint | |
371 | ! %g2 = new TX head PA | |
372 | ! %g5 = SRAM Queue base PA (SRAM Queue Descriptor) | |
373 | ! %g7 = return %pc | |
374 | ||
375 | ! Now we need to update our scratchpad head pointer | |
376 | sub %g2, %g5, %g2 ! New TX head offset | |
377 | ldx [ %g1 + SP_LDC_RX_SCR_TXSIZE ], %g6 | |
378 | cmp %g2, %g6 | |
379 | move %xcc, 0, %g2 ! check for wrap around | |
380 | stw %g2, [ %g1 + SP_LDC_RX_SCR_TXHEAD ] | |
381 | ||
382 | ! %g1 = sp endpoint | |
383 | ! %g7 = return %pc | |
384 | ||
385 | VCPU_STRUCT(%g3) | |
386 | ldx [%g3 + CPU_ROOT], %g3 | |
387 | ldx [%g3 + CONFIG_HV_LDCS], %g3 ! get HV endpoint array | |
388 | ldx [%g1 + SP_LDC_TARGET_CHANNEL], %g6 ! and target endpoint | |
389 | mulx %g6, LDC_ENDPOINT_SIZE, %g4 | |
390 | add %g3, %g4, %g4 ! and its struct | |
391 | ||
392 | ldx [%g4 + LDC_RX_CB], %g6 ! get the callback | |
393 | brz,pn %g6, .done_read_sram_pkts ! if none, drop pkt | |
394 | nop | |
395 | ||
396 | STRAND_PUSH(%g1, %g2, %g3) | |
397 | STRAND_PUSH(%g7, %g2, %g3) | |
398 | ||
399 | add %g1, SP_LDC_RX_SCR_PKT, %g2 ! payload | |
400 | ldx [%g4 + LDC_RX_CBARG], %g1 ! load the argument | |
401 | ||
402 | ! %g1 = call back arg | |
403 | ! %g2 = payload PA | |
404 | ! %g6 = callback | |
405 | ||
406 | jmp %g6 ! invoke callback | |
407 | rd %pc, %g7 | |
408 | ||
409 | ! Assume all %g registers clobbered | |
410 | ||
411 | STRAND_POP(%g7, %g2) | |
412 | STRAND_POP(%g1, %g2) | |
413 | ||
414 | #ifndef CONFIG_SPLIT_SRAM | |
415 | ldx [%g1 + SP_LDC_RX_QD_PA], %g5 | |
416 | #endif | |
417 | ||
418 | ! %g1 = sp endpoint | |
419 | ! %g5 = SRAM Queue base PA (SRAM Queue Descriptor !CONFIG_SPLIT_SRAM) | |
420 | ! %g7 = return %pc | |
421 | ||
422 | ba .read_more_sram_pkts | |
423 | nop | |
424 | ||
425 | .done_read_sram_pkts: | |
426 | ||
427 | ! %g1 = sp endpoint | |
428 | ! %g5 = SRAM Queue base PA (SRAM Queue Descriptor !CONFIG_SPLIT_SRAM) | |
429 | ! %g7 = return %pc | |
430 | ||
431 | lduw [ %g1 + SP_LDC_RX_SCR_TXHEAD ], %g3 | |
432 | LDC_SRAM_OFFSET_TO_IDX(%g3) | |
433 | #ifdef CONFIG_SPLIT_SRAM | |
434 | ldx [%g1 + SP_LDC_RX_QD_PA], %g5 ! queue data PA | |
435 | #endif | |
436 | stb %g3, [ %g5 + SRAM_LDC_HEAD ] ! commit the new TX head | |
437 | ||
438 | STRAND_PUSH(%g1, %g2, %g3) | |
439 | ! %g1 target endpoint (clobbered) | |
440 | LDC_SEND_SP_INTR(%g1, %g3, %g4, SP_LDC_SPACE) | |
441 | STRAND_POP(%g1, %g2) | |
442 | ||
443 | ! %g1 = SP endpoint | |
444 | ! %g7 = return %pc | |
445 | ||
446 | ! At this point, since we just updated the SRAM head index, we | |
447 | ! need re-read the head/tail value from SRAM and make sure no new | |
448 | ! packets were added while we were processing the last one. | |
449 | ba,a .one_more_ldc_rx_channel | |
450 | nop | |
451 | ||
452 | .svc_ldc_guest_target: | |
453 | ||
454 | ! %g1 = SP endpoint | |
455 | ! %g7 = return %pc | |
456 | ||
457 | ldx [ %g1 + SP_LDC_TARGET_GUEST ], %g3 | |
458 | brz %g3, .exit_ldc_rx_channel | |
459 | nop | |
460 | ||
461 | ldx [ %g1 + SP_LDC_TARGET_CHANNEL ], %g6 | |
462 | mulx %g6, LDC_ENDPOINT_SIZE, %g4 | |
463 | set GUEST_LDC_ENDPOINT, %g5 | |
464 | add %g5, %g3, %g5 | |
465 | add %g4, %g5, %g3 | |
466 | ||
467 | ! %g1 = SP endpoint | |
468 | ! %g3 = guest endpoint | |
469 | ! %g7 = return %pc | |
470 | ||
471 | ! See if we need to send an interrupt to the recipient | |
472 | ldx [ %g3 + LDC_RX_MAPREG + LDC_MAPREG_CPUP ], %g6 | |
473 | brnz,pt %g6, 1f | |
474 | nop | |
475 | ||
476 | ! if no target CPU specified, is there a vdev interrupt we | |
477 | ! need to generate? | |
478 | ldx [ %g3 + LDC_RX_VINTR_COOKIE ], %g6 | |
479 | brz,pn %g6, .exit_ldc_rx_channel ! if not, we are done. | |
480 | nop | |
481 | ||
482 | STRAND_PUSH(%g7, %g2, %g4) | |
483 | ||
484 | mov %g6, %g1 | |
485 | HVCALL(vdev_intr_generate) | |
486 | ||
487 | STRAND_POP(%g7, %g2) | |
488 | ||
489 | ! %g1 = SP endpoint | |
490 | ! %g3 = guest endpoint | |
491 | ! %g7 = return %pc | |
492 | ||
493 | ba .exit_ldc_rx_channel | |
494 | nop | |
495 | 1: | |
496 | ! %g1 = SP endpoint | |
497 | ! %g3 = guest endpoint | |
498 | ! %g6 = target cpu struct | |
499 | ! %g7 = return %pc | |
500 | ||
501 | ! Only need to send notification if guest RX queue is empty. | |
502 | ! No synchronization issues with respect to lost notification here | |
503 | ! because the guest's rx_set_qhead routine pulls data from the SRAM | |
504 | ! after updating the head pointer with the guest specified value. | |
505 | lduw [ %g3 + LDC_RX_QHEAD ], %g4 | |
506 | lduw [ %g3 + LDC_RX_QTAIL ], %g5 | |
507 | cmp %g4, %g5 | |
508 | bne %xcc, .exit_ldc_rx_channel | |
509 | nop | |
510 | ||
511 | ! %g1 = SP endpoint | |
512 | ! %g3 = guest endpoint | |
513 | ! %g6 = target cpu struct | |
514 | ! %g7 = return %pc | |
515 | STRAND_PUSH(%g7, %g2, %g4) | |
516 | HVCALL(hv_ldc_cpu_notify) | |
517 | STRAND_POP(%g7, %g2) | |
518 | ||
519 | ! %g7 = return %pc | |
520 | ||
521 | .exit_ldc_rx_channel: | |
522 | ||
523 | HVRET | |
524 | SET_SIZE(svc_ldc_data_available) | |
525 | ||
526 | ||
527 | /* | |
528 | * Handles the LDC ACK interrupt from the SP which indicates that the | |
529 | * SP has freed up some room in the SRAM LDC queues for us so that we | |
530 | * may once again send more packets if needed. | |
531 | * | |
532 | * %g4 - channel which received the notification | |
533 | * %g7 - holds calling pc value. | |
534 | */ | |
535 | ENTRY_NP(svc_ldc_space_available) | |
536 | ||
537 | STRAND_PUSH(%g7, %g1, %g2) | |
538 | ||
539 | ROOT_STRUCT(%g1) | |
540 | ldx [%g1 + CONFIG_SP_LDCS], %g1 ! get SP endpoint array | |
541 | mulx %g4, SP_LDC_ENDPOINT_SIZE, %g2 | |
542 | add %g1, %g2, %g1 ! pointer to SP endpoint | |
543 | clr %g6 ! SP notification flag | |
544 | ||
545 | ! %g1 = SP endpoint which received t henotication | |
546 | ! %g6 = 0 (notification flag) | |
547 | ! %g7 = return PC | |
548 | ||
549 | ldub [%g1 + SP_LDC_IS_LIVE], %g2 ! is channel open? | |
550 | brz %g2, .exit_ldc_tx_channel | |
551 | nop | |
552 | ||
553 | ! If this is a SP <-> HV connection (guest ptr==NULL) then | |
554 | ! there is nothing really to do. | |
555 | ldx [ %g1 + SP_LDC_TARGET_GUEST ], %g3 | |
556 | brz %g3, .exit_ldc_tx_channel | |
557 | nop | |
558 | ||
559 | ! SP <-> Guest channel | |
560 | ||
561 | ldx [ %g1 + SP_LDC_TARGET_CHANNEL ], %g4 | |
562 | mulx %g4, LDC_ENDPOINT_SIZE, %g4 | |
563 | set GUEST_LDC_ENDPOINT, %g5 | |
564 | add %g5, %g3, %g5 | |
565 | add %g4, %g5, %g3 | |
566 | ||
567 | ! %g1 = SP endpoint | |
568 | ! %g3 = guest endpoint | |
569 | ! %g6 = (notification flag) | |
570 | ! %g7 = return PC | |
571 | ||
572 | ! Nothing to send if guest TX queue is empty | |
573 | lduw [ %g3 + LDC_TX_QHEAD ], %g4 | |
574 | lduw [ %g3 + LDC_TX_QTAIL ], %g5 | |
575 | cmp %g4, %g5 | |
576 | be %xcc, .exit_ldc_tx_channel | |
577 | nop | |
578 | ||
579 | ! %g1 = SP endpoint | |
580 | ! %g3 = guest endpoint | |
581 | ||
582 | mov %g3, %g2 | |
583 | mov %g1, %g3 | |
584 | ||
585 | ! %g2 = guest endpoint | |
586 | ! %g3 = sp endpoint | |
587 | ||
588 | STRAND_PUSH(%g2, %g1, %g4) ! save guest endpoint | |
589 | ||
590 | add %g3, SP_LDC_TX_LOCK, %g5 | |
591 | SPINLOCK_ENTER(%g5, %g1, %g4) | |
592 | ||
593 | HVCALL(sram_ldc_push_data) ! %g3 (sp endpoint) preserved | |
594 | ||
595 | add %g3, SP_LDC_TX_LOCK, %g5 | |
596 | SPINLOCK_EXIT(%g5) | |
597 | ||
598 | mov %g3, %g1 | |
599 | ||
600 | clr %g3 | |
601 | movrnz %g2, 1, %g3 ! %g2 = send interrupt flag | |
602 | or %g6, %g3, %g6 | |
603 | ||
604 | ! %g1 = sp endpoint | |
605 | ! %g6 = (notification flag) | |
606 | ||
607 | STRAND_POP(%g2, %g3) ! restore guest endpoint | |
608 | ||
609 | ! %g2 = sender's (guest) endpoint | |
610 | ||
611 | ! We might need to send a 'queue no longer full' interrupt | |
612 | ! in certain situations. | |
613 | ldub [ %g2 + LDC_TXQ_FULL ], %g3 | |
614 | brz,pt %g3, .exit_ldc_tx_channel | |
615 | nop | |
616 | stb %g0, [ %g2 + LDC_TXQ_FULL ] | |
617 | ||
618 | ldx [%g2 + LDC_RX_VINTR_COOKIE], %g2 | |
619 | brz %g2, .exit_ldc_tx_channel | |
620 | nop | |
621 | ||
622 | ! save off registers | |
623 | STRAND_PUSH(%g1, %g3, %g4) | |
624 | STRAND_PUSH(%g6, %g3, %g4) | |
625 | ||
626 | mov %g2, %g1 | |
627 | HVCALL(vdev_intr_generate) | |
628 | ||
629 | ! restore registers | |
630 | STRAND_POP(%g6, %g3) | |
631 | STRAND_POP(%g1, %g3) | |
632 | ||
633 | .exit_ldc_tx_channel: | |
634 | ||
635 | ! Done checking all SRAM queues | |
636 | btst 1, %g6 | |
637 | bz %xcc, 1f ! skip TX notification if flag is clear | |
638 | nop | |
639 | ||
640 | ! %g1 target endpoint (clobbered) | |
641 | LDC_SEND_SP_INTR(%g1, %g6, %g4, SP_LDC_DATA) | |
642 | 1: | |
643 | STRAND_POP(%g7, %g5) | |
644 | HVRET | |
645 | SET_SIZE(svc_ldc_space_available) | |
646 | ||
647 | ||
648 | /* | |
649 | * Called when the SP sends us notification for a channel reset so that | |
650 | * we can forward the notification interrupt to a guest if necessary. | |
651 | * | |
652 | * %g7 - holds calling pc value. | |
653 | */ | |
654 | ENTRY_NP(svc_ldc_reset_intr) | |
655 | ROOT_STRUCT(%g1) | |
656 | ldx [%g1 + CONFIG_SP_LDC_MAX_CID], %g2 | |
657 | ldx [%g1 + CONFIG_SP_LDCS], %g1 ! get SP endpoint array | |
658 | ||
659 | mulx %g2, SP_LDC_ENDPOINT_SIZE, %g2 | |
660 | add %g1, %g2, %g1 ! pointer to last SP endpoint | |
661 | ||
662 | .one_more_sram_channel: | |
663 | ||
664 | ! %g1 = SP endpoint | |
665 | ||
666 | ldub [%g1 + SP_LDC_IS_LIVE], %g2 ! is channel open? | |
667 | brz %g2, .next_ldc_sram_channel | |
668 | nop | |
669 | ||
670 | ! check to see whether there is a reset notification pending | |
671 | ! for this channel. | |
672 | ldx [ %g1 + SP_LDC_TX_QD_PA ], %g2 | |
673 | ldub [ %g2 + SRAM_LDC_STATE_NOTIFY ], %g3 | |
674 | brz,pt %g3, .next_ldc_sram_channel | |
675 | nop | |
676 | ||
677 | ! reset notification is pending for this channel | |
678 | stb %g0, [ %g2 + SRAM_LDC_STATE_NOTIFY ] ! clear the flag | |
679 | ||
680 | ! For SP<->HV connections (guest ptr==NULL), there is nothing to | |
681 | ! do at this point. | |
682 | ldx [ %g1 + SP_LDC_TARGET_GUEST ], %g3 | |
683 | brz %g3, .next_ldc_sram_channel | |
684 | nop | |
685 | ||
686 | ldx [ %g1 + SP_LDC_TARGET_CHANNEL ], %g4 | |
687 | mulx %g4, LDC_ENDPOINT_SIZE, %g4 | |
688 | set GUEST_LDC_ENDPOINT, %g5 | |
689 | add %g5, %g3, %g5 | |
690 | add %g4, %g5, %g3 | |
691 | ||
692 | ! %g1 = SP endpoint | |
693 | ! %g3 = guest endpoint | |
694 | ||
695 | ! skip notification if no CPU is specified to handle interrupts | |
696 | ldx [ %g3 + LDC_RX_MAPREG + LDC_MAPREG_CPUP ], %g6 | |
697 | brz,pn %g6, .next_ldc_sram_channel | |
698 | nop | |
699 | ||
700 | ! %g1 = SP endpoint | |
701 | ! %g3 = guest endpoint | |
702 | ! %g6 = target cpu struct | |
703 | STRAND_PUSH(%g1, %g2, %g4) | |
704 | STRAND_PUSH(%g7, %g2, %g4) | |
705 | STRAND_PUSH(%g6, %g2, %g4) | |
706 | HVCALL(hv_ldc_cpu_notify) | |
707 | STRAND_POP(%g6, %g2) | |
708 | STRAND_POP(%g7, %g2) | |
709 | STRAND_POP(%g1, %g2) | |
710 | ! %g1 = SP endpoint | |
711 | ||
712 | .next_ldc_sram_channel: | |
713 | ||
714 | ROOT_STRUCT(%g2) | |
715 | ldx [%g2 + CONFIG_SP_LDCS], %g2 ! first SP endpoint | |
716 | cmp %g1, %g2 ! did we just process it? | |
717 | bgu,pt %xcc, .one_more_sram_channel | |
718 | sub %g1, SP_LDC_ENDPOINT_SIZE, %g1 | |
719 | ||
720 | HVRET | |
721 | SET_SIZE(svc_ldc_reset_intr) | |
722 | ||
723 | #endif /* CONFIG_FPGA */ |