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