source: rtems/bsps/shared/grlib/slink/grslink.c @ 7eb606d3

5
Last change on this file since 7eb606d3 was 7eb606d3, checked in by Sebastian Huber <sebastian.huber@…>, on 12/22/18 at 17:31:04

grlib: Move source files

Update #3678.

  • Property mode set to 100644
File size: 18.0 KB
Line 
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 */
50typedef 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
62typedef 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
68typedef 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
77typedef 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
91static 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 */
102static 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 */
155static 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 */
180static 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 */
199static 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 */
213static 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 */
239static 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 */
342int 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 */
425void 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 */
434void 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 */
452int 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 */
489int 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 */
512int 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 */
549void 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 */
570int 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 */
582int 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 */
596int 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 */
608int 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 */
636int 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 */
657SLINK_stats *SLINK_statistics(void)
658{
659#ifdef SLINK_COLLECT_STATISTICS
660        return cfg->stats;
661#else
662        return NULL;
663#endif
664}
Note: See TracBrowser for help on using the repository browser.