1 | /* |
---|
2 | * This file contains the RTEMS GRSLINK SLINK master driver |
---|
3 | * |
---|
4 | * COPYRIGHT (c) 2009. |
---|
5 | * Cobham Gaisler AB. |
---|
6 | * |
---|
7 | * The license and distribution terms for this file may be |
---|
8 | * found in the file LICENSE in this distribution or at |
---|
9 | * http://www.rtems.com/license/LICENSE. |
---|
10 | * |
---|
11 | * Comments concerning current driver implementation: |
---|
12 | * |
---|
13 | * The SLINK specification says that there are three IO cards that are capable |
---|
14 | * of transmitting data. But these IO cards can have the address range 0 to 3, |
---|
15 | * and an 'For information only' comment explains that the current |
---|
16 | * implementation has receive buffers for ".. x 4 (IO cards)". |
---|
17 | * Because of this the driver has four queues, one for each IO card 0 - 3. |
---|
18 | * When the addressing convention used for the IO cards is known, the number of |
---|
19 | * queues may be lowered to three. |
---|
20 | * |
---|
21 | */ |
---|
22 | |
---|
23 | #include <stdlib.h> |
---|
24 | |
---|
25 | #include <bsp.h> |
---|
26 | #include <bsp/grslink.h> |
---|
27 | #include <ambapp.h> |
---|
28 | |
---|
29 | #ifndef GAISLER_SLINK |
---|
30 | #define GAISLER_SLINK 0x02F |
---|
31 | #endif |
---|
32 | |
---|
33 | /* Enable debug output? */ |
---|
34 | /* #define DEBUG */ |
---|
35 | |
---|
36 | #ifdef DEBUG |
---|
37 | #define DBG(x...) printk(x) |
---|
38 | #else |
---|
39 | #define DBG(x...) |
---|
40 | #endif |
---|
41 | |
---|
42 | /* Bits and fields in SLINK transmit word */ |
---|
43 | #define SLINK_RW (1 << 23) |
---|
44 | #define SLINK_CHAN_POS 16 |
---|
45 | |
---|
46 | /* Local types */ |
---|
47 | typedef struct { |
---|
48 | volatile unsigned int clockscale; |
---|
49 | volatile unsigned int ctrl; |
---|
50 | volatile unsigned int nullwrd; |
---|
51 | volatile unsigned int sts; |
---|
52 | volatile unsigned int msk; |
---|
53 | volatile unsigned int abase; |
---|
54 | volatile unsigned int bbase; |
---|
55 | volatile unsigned int td; |
---|
56 | volatile unsigned int rd; |
---|
57 | } SLINK_regs; |
---|
58 | |
---|
59 | typedef struct { |
---|
60 | char readstat; /* Status of READ operation */ |
---|
61 | char seqstat; /* Status of SEQUENCE operation */ |
---|
62 | unsigned char scnt; /* Number of SEQUENCE words transferred */ |
---|
63 | } SLINK_status; |
---|
64 | |
---|
65 | typedef struct { |
---|
66 | int size; |
---|
67 | unsigned int *buf; |
---|
68 | unsigned int *first; |
---|
69 | unsigned int *last; |
---|
70 | unsigned int *max; |
---|
71 | int full; |
---|
72 | } SLINK_queue; |
---|
73 | |
---|
74 | typedef struct { |
---|
75 | SLINK_regs *reg; /* Pointer to core registers */ |
---|
76 | SLINK_status *status; /* Driver status information */ |
---|
77 | void (*slink_irq_handler)(int); /* Handler for INTERRUPT */ |
---|
78 | void (*slink_seq_change)(int); /* Callback on SEQUENCE change */ |
---|
79 | int rword; /* Placeholder for READ response */ |
---|
80 | rtems_id read_sem; /* Semaphore for blocking SLINK_read */ |
---|
81 | SLINK_queue *queues; /* Receive queues */ |
---|
82 | #ifdef SLINK_COLLECT_STATISTICS |
---|
83 | SLINK_stats *stats; /* Core statistics, optional */ |
---|
84 | #endif |
---|
85 | } SLINK_cfg; |
---|
86 | |
---|
87 | |
---|
88 | static SLINK_cfg *cfg = NULL; |
---|
89 | |
---|
90 | /**** SLINK driver queues for unsolicited and INTERRUPT requests ****/ |
---|
91 | |
---|
92 | /* Function: SLINK_createqueues |
---|
93 | * Arguments: size: Number of elements in each queue |
---|
94 | * Returns: 0 on success, -1 on failure |
---|
95 | * Description: Creates SLINK_NUMQUEUES queues, one for each IO card |
---|
96 | * that can send data. The pointers to the queues is saved in the driver |
---|
97 | * config structure. |
---|
98 | */ |
---|
99 | static int SLINK_createqueues(int size) |
---|
100 | { |
---|
101 | SLINK_queue *q; |
---|
102 | int i, j; |
---|
103 | |
---|
104 | if ((q = malloc(SLINK_NUMQUEUES*sizeof(SLINK_queue))) == NULL) |
---|
105 | goto slink_qiniterr1; |
---|
106 | |
---|
107 | for (i = 0; i < SLINK_NUMQUEUES; i++) { |
---|
108 | q[i].size = size; |
---|
109 | if ((q[i].buf = malloc(size*sizeof(int))) == NULL) |
---|
110 | goto slink_qiniterr2; |
---|
111 | q[i].first = q[i].last = q[i].buf; |
---|
112 | q[i].max = q[i].buf + (size-1); |
---|
113 | q[i].full = 0; |
---|
114 | } |
---|
115 | |
---|
116 | cfg->queues = q; |
---|
117 | |
---|
118 | return 0; |
---|
119 | |
---|
120 | slink_qiniterr2: |
---|
121 | for (j = 0; j < i; j++) |
---|
122 | free(q[i].buf); |
---|
123 | free(q); |
---|
124 | slink_qiniterr1: |
---|
125 | return -1; |
---|
126 | } |
---|
127 | |
---|
128 | /* |
---|
129 | * Function: SLINK_destroyqueues |
---|
130 | * Arguments: None |
---|
131 | * Returns: Nothing |
---|
132 | * Description: Frees the memory occupied by the queues in cfg->queues |
---|
133 | */ |
---|
134 | /* |
---|
135 | static void SLINK_destroyqueues(void) |
---|
136 | { |
---|
137 | int i; |
---|
138 | |
---|
139 | for(i = 0; i < SLINK_NUMQUEUES; i++) |
---|
140 | free(cfg->queues[i].buf); |
---|
141 | |
---|
142 | free(cfg->queues); |
---|
143 | } |
---|
144 | */ |
---|
145 | |
---|
146 | /* |
---|
147 | * Function: SLINK_enqueue |
---|
148 | * Arguments: Received SLINK word |
---|
149 | * Returns: Nothing |
---|
150 | * Description: |
---|
151 | */ |
---|
152 | static void SLINK_enqueue(unsigned int slink_wrd) |
---|
153 | { |
---|
154 | SLINK_queue *ioq = cfg->queues + SLINK_WRD_CARDNUM(slink_wrd); |
---|
155 | |
---|
156 | if (!ioq->full && SLINK_WRD_CARDNUM(slink_wrd) < SLINK_NUMQUEUES) { |
---|
157 | *ioq->last = slink_wrd; |
---|
158 | ioq->last = (ioq->last >= ioq->max) ? ioq->buf : ioq->last+1; |
---|
159 | ioq->full = ioq->last == ioq->first; |
---|
160 | return; |
---|
161 | } |
---|
162 | #ifdef SLINK_COLLECT_STATISTICS |
---|
163 | cfg->stats->lostwords++; |
---|
164 | #endif |
---|
165 | } |
---|
166 | |
---|
167 | /**** SLINK driver helper functions ****/ |
---|
168 | |
---|
169 | /* |
---|
170 | * Function: SLINK_getaddr |
---|
171 | * Arguments: amba_conf |
---|
172 | * base: assigned to base of core registers |
---|
173 | * irq: assigned to core irq lines |
---|
174 | * Returns: Base address and IRQ via arguments, 0 if core is found, else -1 |
---|
175 | * Description: See above. |
---|
176 | */ |
---|
177 | static int SLINK_getaddr(int *base, int *irq) |
---|
178 | { |
---|
179 | struct ambapp_apb_info c; |
---|
180 | |
---|
181 | if (ambapp_find_apbslv(&ambapp_plb,VENDOR_GAISLER,GAISLER_SLINK,&c) == 1) { |
---|
182 | *base = c.start; |
---|
183 | *irq = c.irq; |
---|
184 | return 0; |
---|
185 | } |
---|
186 | return -1; |
---|
187 | } |
---|
188 | |
---|
189 | /* Function: SLINK_calcscaler |
---|
190 | * Arguments: sysfreq: System frequency in Hz |
---|
191 | * Returns: Clock scaler register value |
---|
192 | * Description: Calculates value for SLINK clock scaler register to attain |
---|
193 | * a SLINK bus frequency as close to 6 MHz as possible. Please see the IP core |
---|
194 | * documentation for a description of how clock scaling is implemented. |
---|
195 | */ |
---|
196 | static int SLINK_calcscaler(int sysfreq) |
---|
197 | { |
---|
198 | int fact = sysfreq / SLINK_FREQ_HZ; |
---|
199 | return ((fact/2-1) << 16) | (fact % 2 ? fact/2 : fact/2-1); |
---|
200 | } |
---|
201 | |
---|
202 | |
---|
203 | /* |
---|
204 | * Function: SLINK_getsysfreq |
---|
205 | * Arguments: None |
---|
206 | * Returns: System frequency in Hz, or 0 if system timer is not found. |
---|
207 | * Description: Looks at the timer to determine system frequency. Makes use |
---|
208 | * of AMBA Plug'n'Play. |
---|
209 | */ |
---|
210 | static int SLINK_getsysfreq(void) |
---|
211 | { |
---|
212 | struct ambapp_apb_info t; |
---|
213 | struct gptimer_regs *tregs; |
---|
214 | |
---|
215 | if (ambapp_find_apbslv(&ambapp_plb,VENDOR_GAISLER,GAISLER_GPTIMER,&t)==1) { |
---|
216 | tregs = (struct gptimer_regs *)t.start; |
---|
217 | DBG("SLINK_getsysfreq returning %d\n", |
---|
218 | (tregs->scaler_reload+1)*1000*1000); |
---|
219 | return (tregs->scaler_reload+1)*1000*1000; |
---|
220 | } |
---|
221 | return 0; |
---|
222 | } |
---|
223 | |
---|
224 | /* |
---|
225 | * Function: SLINK_interrupt_handler |
---|
226 | * Arguments: v: not used |
---|
227 | * Returns: Nothing |
---|
228 | * Description: Interrupt handles checks RNE, SEQUENCE and error status |
---|
229 | * bits. Reads word from receive queue and distinguishes between INTERRUPT, |
---|
230 | * READ responses and SLAVE-WORD-SEND. When an INTERRUPT transfer is detected |
---|
231 | * the handler calls the user specified slink_irq_handler with the received |
---|
232 | * word. READ responses are saved and given to SLINK_read via a private |
---|
233 | * variable. SLAVE-WORD-SEND transfers are placed in the IO card's receive |
---|
234 | * queue. |
---|
235 | */ |
---|
236 | static rtems_isr SLINK_interrupt_handler(rtems_vector_number v) |
---|
237 | { |
---|
238 | unsigned int sts; |
---|
239 | unsigned int wrd; |
---|
240 | |
---|
241 | /* Read all words from Receive queue */ |
---|
242 | while ((sts = cfg->reg->sts) & SLINK_S_RNE) { |
---|
243 | |
---|
244 | /* Read first word in receive queue */ |
---|
245 | wrd = cfg->reg->rd; |
---|
246 | |
---|
247 | /* Check channel value to determine action */ |
---|
248 | switch (SLINK_WRD_CHAN(wrd)) { |
---|
249 | case 0: /* Interrupt */ |
---|
250 | cfg->slink_irq_handler(wrd); |
---|
251 | #ifdef SLINK_COLLECT_STATISTICS |
---|
252 | cfg->stats->interrupts++; |
---|
253 | #endif |
---|
254 | break; |
---|
255 | case 3: /* Read response, if no active READ, fall-through */ |
---|
256 | if (cfg->status->readstat == SLINK_ACTIVE) { |
---|
257 | rtems_semaphore_release(cfg->read_sem); |
---|
258 | cfg->status->readstat = SLINK_COMPLETED; |
---|
259 | cfg->rword = wrd; |
---|
260 | break; |
---|
261 | } |
---|
262 | default: /* Unsolicited request */ |
---|
263 | SLINK_enqueue(wrd); |
---|
264 | break; |
---|
265 | } |
---|
266 | } |
---|
267 | |
---|
268 | /* Check sequence operation */ |
---|
269 | if (sts & SLINK_S_SC) { |
---|
270 | /* SEQUENCE completed */ |
---|
271 | cfg->status->seqstat = SLINK_COMPLETED; |
---|
272 | if (cfg->slink_seq_change) |
---|
273 | cfg->slink_seq_change(SLINK_COMPLETED); |
---|
274 | #ifdef SLINK_COLLECT_STATISTICS |
---|
275 | cfg->stats->seqcomp++; |
---|
276 | #endif |
---|
277 | } else if (sts & SLINK_S_SA) { |
---|
278 | /* SEQUENCE aborted */ |
---|
279 | cfg->status->seqstat = SLINK_ABORTED; |
---|
280 | cfg->status->scnt = (sts >> SLINK_S_SI_POS); |
---|
281 | if (cfg->slink_seq_change) |
---|
282 | cfg->slink_seq_change(SLINK_ABORTED); |
---|
283 | } |
---|
284 | |
---|
285 | /* Check error conditions */ |
---|
286 | if (sts & SLINK_S_PERR) { |
---|
287 | /* |
---|
288 | Parity error detected, set seqstat if there is an ongoing |
---|
289 | sequence so that the calling application can decide if the |
---|
290 | sequence should be aborted |
---|
291 | */ |
---|
292 | if (cfg->status->seqstat == SLINK_ACTIVE) { |
---|
293 | cfg->status->seqstat = SLINK_PARERR; |
---|
294 | if (cfg->slink_seq_change) |
---|
295 | cfg->slink_seq_change(SLINK_PARERR); |
---|
296 | } |
---|
297 | /* Abort READ operation */ |
---|
298 | if (cfg->status->readstat == SLINK_ACTIVE) { |
---|
299 | cfg->status->readstat = SLINK_PARERR; |
---|
300 | rtems_semaphore_release(cfg->read_sem); |
---|
301 | } |
---|
302 | #ifdef SLINK_COLLECT_STATISTICS |
---|
303 | cfg->stats->parerr++; |
---|
304 | #endif |
---|
305 | } |
---|
306 | if (sts & SLINK_S_AERR) { |
---|
307 | /* AMBA error response, sequence aborted */ |
---|
308 | cfg->status->seqstat = SLINK_AMBAERR; |
---|
309 | cfg->status->scnt = sts >> SLINK_S_SI_POS; |
---|
310 | if (cfg->slink_seq_change) |
---|
311 | cfg->slink_seq_change(SLINK_AMBAERR); |
---|
312 | } |
---|
313 | if (sts & SLINK_S_ROV) { |
---|
314 | /* Receive overflow, abort any ongoing READ */ |
---|
315 | if (cfg->status->readstat == SLINK_ACTIVE) { |
---|
316 | cfg->status->readstat = SLINK_ROV; |
---|
317 | rtems_semaphore_release(cfg->read_sem); |
---|
318 | } |
---|
319 | #ifdef SLINK_COLLECT_STATISICS |
---|
320 | cfg->status->recov++; |
---|
321 | #endif |
---|
322 | } |
---|
323 | |
---|
324 | /* Clear processed bits */ |
---|
325 | cfg->reg->sts = sts; |
---|
326 | } |
---|
327 | |
---|
328 | /**** SLINK driver interface starts here ****/ |
---|
329 | |
---|
330 | /* Function: SLINK_init |
---|
331 | * Arguments: nullwrd: NULL word |
---|
332 | * parity: Even (0) or Odd (1) parity |
---|
333 | * interrupt_trans_handler: Function that handles interrupt requests |
---|
334 | * sequence_callback: Callback on SEQUENCE status changes |
---|
335 | * qsize: Size of each receive queue |
---|
336 | * Returns: 0 on success, -1 on failure |
---|
337 | * Description: Initializes the SLINK core |
---|
338 | */ |
---|
339 | int SLINK_init(unsigned int nullwrd, int parity, int qsize, |
---|
340 | void (*interrupt_trans_handler)(int), |
---|
341 | void (*sequence_callback)(int)) |
---|
342 | { |
---|
343 | int base; |
---|
344 | int irq; |
---|
345 | rtems_status_code st; |
---|
346 | |
---|
347 | /* Allocate private config structure */ |
---|
348 | if (cfg == NULL && (cfg = malloc(sizeof(SLINK_cfg))) == NULL) { |
---|
349 | DBG("SLINK_init: Could not allocate cfg structure\n"); |
---|
350 | goto slink_initerr1; |
---|
351 | } |
---|
352 | |
---|
353 | /* Create simple binary semaphore for blocking SLINK_read */ |
---|
354 | st = rtems_semaphore_create(rtems_build_name('S', 'L', 'R', '0'), 0, |
---|
355 | (RTEMS_FIFO|RTEMS_SIMPLE_BINARY_SEMAPHORE| |
---|
356 | RTEMS_NO_INHERIT_PRIORITY|RTEMS_LOCAL| |
---|
357 | RTEMS_NO_PRIORITY_CEILING), 0, |
---|
358 | &cfg->read_sem); |
---|
359 | if (st != RTEMS_SUCCESSFUL) { |
---|
360 | DBG("SLINK_init: Could not create semaphore\n"); |
---|
361 | goto slink_initerr1; |
---|
362 | } |
---|
363 | |
---|
364 | /* Initialize pointer to SLINK core registers and get IRQ line */ |
---|
365 | if (SLINK_getaddr(&base, &irq) == -1) { |
---|
366 | DBG("SLINK_init: Could not find core\n"); |
---|
367 | goto slink_initerr2; |
---|
368 | } |
---|
369 | cfg->reg = (SLINK_regs*)base; |
---|
370 | |
---|
371 | /* Allocate status structure and initialize members */ |
---|
372 | if ((cfg->status = calloc(1, sizeof(SLINK_status))) == NULL) { |
---|
373 | DBG("SLINK_init: Could not allocate status structure\n"); |
---|
374 | goto slink_initerr2; |
---|
375 | } |
---|
376 | cfg->status->seqstat = SLINK_COMPLETED; |
---|
377 | cfg->status->readstat = SLINK_COMPLETED; |
---|
378 | |
---|
379 | #ifdef SLINK_COLLECT_STATISTICS |
---|
380 | /* Allocate statistics structure and initialize members */ |
---|
381 | if ((cfg->stats = calloc(1, sizeof(SLINK_stats))) == NULL) { |
---|
382 | DBG("SLINK_init: Could not allocate statistics structure\n"); |
---|
383 | goto slink_initerr3; |
---|
384 | } |
---|
385 | #endif |
---|
386 | |
---|
387 | /* Allocate and initialize queues */ |
---|
388 | if (SLINK_createqueues(qsize) == -1) { |
---|
389 | DBG("SLINK_init: Could not create queues\n"); |
---|
390 | goto slink_initerr3; |
---|
391 | } |
---|
392 | |
---|
393 | /* Configure core registers */ |
---|
394 | cfg->reg->clockscale = SLINK_calcscaler(SLINK_getsysfreq()); |
---|
395 | cfg->reg->ctrl = parity ? SLINK_C_PAR : 0; |
---|
396 | cfg->reg->nullwrd = nullwrd; |
---|
397 | cfg->reg->msk = (SLINK_M_PERRE | SLINK_M_AERRE | SLINK_M_ROVE | |
---|
398 | SLINK_M_RNEE | SLINK_M_SAE | SLINK_M_SCE); |
---|
399 | |
---|
400 | /* Set-up INTERRUPT transfer handling */ |
---|
401 | cfg->slink_irq_handler = interrupt_trans_handler; |
---|
402 | |
---|
403 | /* Save SEQUENCE callback */ |
---|
404 | cfg->slink_seq_change = sequence_callback; |
---|
405 | |
---|
406 | /* Set-up IRQ handling */ |
---|
407 | set_vector(SLINK_interrupt_handler,irq+0x10,2); |
---|
408 | |
---|
409 | return 0; |
---|
410 | |
---|
411 | slink_initerr3: |
---|
412 | free(cfg->status); |
---|
413 | slink_initerr2: |
---|
414 | free(cfg); |
---|
415 | slink_initerr1: |
---|
416 | return -1; |
---|
417 | } |
---|
418 | |
---|
419 | /* Function: SLINK_start |
---|
420 | * Description: Enables the core |
---|
421 | */ |
---|
422 | void SLINK_start(void) |
---|
423 | { |
---|
424 | if (cfg != NULL) |
---|
425 | cfg->reg->ctrl |= SLINK_C_SLE; |
---|
426 | } |
---|
427 | |
---|
428 | /* Function: SLINK_stop |
---|
429 | * Description: Disables the core |
---|
430 | */ |
---|
431 | void SLINK_stop(void) |
---|
432 | { |
---|
433 | if (cfg != NULL) |
---|
434 | cfg->reg->ctrl &= ~SLINK_C_SLE; |
---|
435 | } |
---|
436 | |
---|
437 | /* |
---|
438 | * Function: SLINK_read |
---|
439 | * Arguments: data: Payload of data word |
---|
440 | * channel: - |
---|
441 | * reply: Reply from IO card |
---|
442 | * Returns: 0 on success |
---|
443 | * -(SLINK_PARERR, SLINK_ROV) on error or -SLINK_QFULL if transmit queue |
---|
444 | * is full and software should try again. |
---|
445 | * Description: Reads one word and returns the response in *reply unless there |
---|
446 | * is an error. This function blocks until the READ operation is |
---|
447 | * completed or aborted. |
---|
448 | */ |
---|
449 | int SLINK_read(int data, int channel, int *reply) |
---|
450 | { |
---|
451 | DBG("SLINK_read: called.."); |
---|
452 | |
---|
453 | if (cfg->reg->sts & SLINK_S_TNF) { |
---|
454 | cfg->status->readstat = SLINK_ACTIVE; |
---|
455 | cfg->reg->td = SLINK_RW | channel << SLINK_CHAN_POS | data; |
---|
456 | } else { |
---|
457 | DBG("queue FULL\n"); |
---|
458 | return -SLINK_QFULL; /* Transmit queue full */ |
---|
459 | } |
---|
460 | |
---|
461 | /* Block until the operation has completed or has been aborted */ |
---|
462 | rtems_semaphore_obtain(cfg->read_sem, RTEMS_WAIT, RTEMS_NO_TIMEOUT); |
---|
463 | |
---|
464 | if (cfg->status->readstat == SLINK_COMPLETED) { |
---|
465 | *reply = cfg->rword; |
---|
466 | #ifdef SLINK_COLLECT_STATISTICS |
---|
467 | cfg->stats->reads++; |
---|
468 | #endif |
---|
469 | DBG("returning 0\n"); |
---|
470 | return 0; |
---|
471 | } else { |
---|
472 | DBG("returning error code\n"); |
---|
473 | return -cfg->status->readstat; |
---|
474 | } |
---|
475 | } |
---|
476 | |
---|
477 | /* |
---|
478 | * Function: SLINK_write |
---|
479 | * Arguments: data: Payload of SLINK data word |
---|
480 | * channel: Channel value (bits 22 downto 16) of receive |
---|
481 | * register word |
---|
482 | * Returns: 0 if command was placed in transmit queue |
---|
483 | * -SLINK_QFULL if transmit queue was full (software should retry) |
---|
484 | * Description: See above. |
---|
485 | */ |
---|
486 | int SLINK_write(int data, int channel) |
---|
487 | { |
---|
488 | if (cfg->reg->sts & SLINK_S_TNF) { |
---|
489 | cfg->reg->td = channel << SLINK_CHAN_POS | data; |
---|
490 | #ifdef SLINK_COLLECT_STATISTICS |
---|
491 | cfg->stats->writes++; |
---|
492 | #endif |
---|
493 | return 0; |
---|
494 | } |
---|
495 | |
---|
496 | return -SLINK_QFULL; |
---|
497 | } |
---|
498 | |
---|
499 | /* |
---|
500 | * Function: SLINK_sequence |
---|
501 | * Arguments: a: Array containing sequence commands |
---|
502 | * b: Array where SEQUENCE responses will be stored |
---|
503 | * n: Number of commands in a array |
---|
504 | * channel: Sequence Channel Number |
---|
505 | * reconly: Set to 1 if the SEQUENCE operation is receive only |
---|
506 | * Returns: 0 if SEQUENCE could be started (SUCCESS) |
---|
507 | * -1 if SEQUNCE was not started due to ongoing SEQUENCE |
---|
508 | */ |
---|
509 | int SLINK_seqstart(int *a, int *b, int n, int channel, int reconly) |
---|
510 | { |
---|
511 | /* Only start a new SEQUENCE of the former SEQUENCE has completed */ |
---|
512 | if (cfg->status->seqstat == SLINK_ACTIVE || |
---|
513 | cfg->status->seqstat == SLINK_PARERR) |
---|
514 | return -1; |
---|
515 | |
---|
516 | /* Tell core about arrays */ |
---|
517 | cfg->reg->abase = (int)a; |
---|
518 | cfg->reg->bbase = (int)b; |
---|
519 | |
---|
520 | /* As far as software is concerned the sequence is now active */ |
---|
521 | cfg->status->seqstat = SLINK_ACTIVE; |
---|
522 | |
---|
523 | /* Enable SEQUENCE operation with SCN = channel and SLEN = n-1 */ |
---|
524 | if (reconly == 1) { |
---|
525 | cfg->reg->ctrl = (((n-1) << SLINK_C_SLEN_POS) | SLINK_C_SRO | |
---|
526 | (channel << SLINK_C_SCN_POS) | |
---|
527 | SLINK_C_SE | (cfg->reg->ctrl & 0xC000000F)); |
---|
528 | } else { |
---|
529 | cfg->reg->ctrl = (((n-1) << SLINK_C_SLEN_POS) | |
---|
530 | (channel << SLINK_C_SCN_POS) | |
---|
531 | SLINK_C_SE | (cfg->reg->ctrl & 0xC000000F)); |
---|
532 | } |
---|
533 | |
---|
534 | #ifdef SLINK_COLLECT_STATISTICS |
---|
535 | cfg->stats->sequences++; |
---|
536 | #endif |
---|
537 | |
---|
538 | return 0; |
---|
539 | } |
---|
540 | |
---|
541 | |
---|
542 | /* Function: SLINK_seqabort |
---|
543 | * Description: This function aborts an ongoing SEQUENCE. Software can tell |
---|
544 | * when the SEQUENCE is aborted by polling SLINK_seqstat(). |
---|
545 | */ |
---|
546 | void SLINK_seqabort(void) |
---|
547 | { |
---|
548 | cfg->reg->ctrl = cfg->reg->ctrl | SLINK_C_AS; |
---|
549 | } |
---|
550 | |
---|
551 | |
---|
552 | /* |
---|
553 | * Function: SLINK_seqstatus |
---|
554 | * Returns: The current or status of the SEQUENCE operation: |
---|
555 | * SLINK_COMPLETED, SLINK_ACTIVE, SLINK_PARERR, SLINK_AMBAERR, |
---|
556 | * SLINK_ABORTED (these are defined in bsp/grslink.h) |
---|
557 | * Description: Meaning of returned values: |
---|
558 | * SLINK_ABORTED: Aborted before all operations completed. |
---|
559 | * SLINK_ACTIVE: The core is busy processing the SEQUENCE |
---|
560 | * SLINK_AMBAERR: The last SEQUENCE was aborted by an AMBA ERROR |
---|
561 | * SLINK_COMPLETED: All words were transferred in the last SEQUENCE |
---|
562 | * SLINK_PARERR: Parity error detected. Software may want to abort |
---|
563 | * |
---|
564 | * If the SEQUENCE was aborted SLINK_seqwrds() can be used to |
---|
565 | * determine the number of completed operations. |
---|
566 | */ |
---|
567 | int SLINK_seqstatus(void) |
---|
568 | { |
---|
569 | return cfg->status->seqstat; |
---|
570 | } |
---|
571 | |
---|
572 | /* |
---|
573 | * Function: SLINK_seqwrds |
---|
574 | * Returns: -1 for ongoing sequence |
---|
575 | * 0 if all words were transferred in the last sequence |
---|
576 | * number of words if the last SEQUENCE did not complete |
---|
577 | * (SLINK_AMBAERR or SLINK_ABORTED is reported ny SLINK_seqstatus()) |
---|
578 | */ |
---|
579 | int SLINK_seqwrds(void) |
---|
580 | { |
---|
581 | switch (cfg->status->seqstat) { |
---|
582 | case SLINK_COMPLETED: return 0; |
---|
583 | case SLINK_ACTIVE | SLINK_PARERR: return -1; |
---|
584 | default: return cfg->status->scnt; |
---|
585 | } |
---|
586 | } |
---|
587 | |
---|
588 | /* |
---|
589 | * Function: SLINK_hwstatus |
---|
590 | * Returns: The SLINK core's status register. The register values can be |
---|
591 | * interpreted with the help of macros defined in bsp/grslink.h. |
---|
592 | */ |
---|
593 | int SLINK_hwstatus(void) |
---|
594 | { |
---|
595 | return cfg->reg->sts; |
---|
596 | } |
---|
597 | |
---|
598 | /* |
---|
599 | * Function: SLINK_queuestatus |
---|
600 | * Arguments: iocard: Queue which to check status for |
---|
601 | * Returns: Number of elements in queue or -1 on non-existent queue |
---|
602 | * Description: SLINK_queuestatus(queue) returns the number of elements in |
---|
603 | * queue 'iocard' |
---|
604 | */ |
---|
605 | int SLINK_queuestatus(int iocard) |
---|
606 | { |
---|
607 | unsigned int first, last; |
---|
608 | SLINK_queue *ioq; |
---|
609 | |
---|
610 | if (iocard >= SLINK_NUMQUEUES) |
---|
611 | return -1; |
---|
612 | |
---|
613 | ioq = cfg->queues + iocard; |
---|
614 | |
---|
615 | if (ioq->full) |
---|
616 | return ioq->size; |
---|
617 | if (ioq->first == ioq->last) |
---|
618 | return 0; |
---|
619 | |
---|
620 | first = ((unsigned int)ioq->first)/sizeof(unsigned int); |
---|
621 | last = ((unsigned int)ioq->last)/sizeof(unsigned int); |
---|
622 | |
---|
623 | return first < last ? last - first : ioq->size - first + last; |
---|
624 | } |
---|
625 | |
---|
626 | /* |
---|
627 | * Function: SLINK_dequeue |
---|
628 | * Arguments: iocard: IO card number |
---|
629 | * elem: First element in IO card queue |
---|
630 | * Returns: 0 on success or -1 on empty or non-existent queue |
---|
631 | * Description: |
---|
632 | */ |
---|
633 | int SLINK_dequeue(int iocard, int *elem) |
---|
634 | { |
---|
635 | if (iocard >= SLINK_NUMQUEUES) |
---|
636 | return -1; |
---|
637 | |
---|
638 | SLINK_queue *ioq = cfg->queues + iocard; |
---|
639 | |
---|
640 | if (ioq->last != ioq->first || ioq->full) { |
---|
641 | *elem = *ioq->first; |
---|
642 | ioq->first = (ioq->first >= ioq->max) ? ioq->buf : ioq->first+1; |
---|
643 | ioq->full = 0; |
---|
644 | return 0; |
---|
645 | } |
---|
646 | return -1; |
---|
647 | } |
---|
648 | |
---|
649 | /* |
---|
650 | * Function: SLINK_statistics |
---|
651 | * Returns: If the core has statistics colletion enabled this function returns |
---|
652 | * a pointer to a struct containing statistics information, otherwise NULL. |
---|
653 | */ |
---|
654 | SLINK_stats *SLINK_statistics(void) |
---|
655 | { |
---|
656 | #ifdef SLINK_COLLECT_STATISTICS |
---|
657 | return cfg->stats; |
---|
658 | #else |
---|
659 | return NULL; |
---|
660 | #endif |
---|
661 | } |
---|