source: rtems/bsps/shared/grlib/slink/grslink.c @ 5981c8ca

5
Last change on this file since 5981c8ca was 5981c8ca, checked in by Jiri Gaisler <jiri@…>, on 01/18/19 at 10:29:08

grlib: use rtems_interrupt_handler_install()

Update #3678.

  • Property mode set to 100644
File size: 18.1 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(void *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        rtems_interrupt_handler_install(irq, "slink",
411                        RTEMS_INTERRUPT_SHARED,
412                        SLINK_interrupt_handler, NULL);
413       
414        return 0;
415
416 slink_initerr3:
417        free(cfg->status);
418 slink_initerr2:
419        free(cfg);
420 slink_initerr1:
421        return -1;
422}
423
424/* Function: SLINK_start
425 * Description: Enables the core
426 */
427void SLINK_start(void)
428{   
429        if (cfg != NULL)
430                cfg->reg->ctrl |= SLINK_C_SLE;
431}
432
433/* Function: SLINK_stop
434 * Description: Disables the core
435 */
436void SLINK_stop(void)
437{
438        if (cfg != NULL)
439                cfg->reg->ctrl &= ~SLINK_C_SLE;
440}
441
442/*
443 * Function: SLINK_read
444 * Arguments: data: Payload of data word
445 *            channel: -
446 *            reply: Reply from IO card
447 * Returns: 0 on success
448 *          -(SLINK_PARERR, SLINK_ROV) on error or -SLINK_QFULL if transmit queue
449 *          is full and software should try again.
450 * Description: Reads one word and returns the response in *reply unless there
451 *              is an error. This function blocks until the READ operation is
452 *              completed or aborted.
453 */
454int SLINK_read(int data, int channel, int *reply)
455{       
456        DBG("SLINK_read: called..");
457
458        if (cfg->reg->sts & SLINK_S_TNF) {
459                cfg->status->readstat = SLINK_ACTIVE;
460                cfg->reg->td = SLINK_RW | channel << SLINK_CHAN_POS | data;
461        } else {
462                DBG("queue FULL\n");
463                return -SLINK_QFULL; /* Transmit queue full */
464        }
465
466        /* Block until the operation has completed or has been aborted */
467        rtems_semaphore_obtain(cfg->read_sem, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
468
469        if (cfg->status->readstat == SLINK_COMPLETED) {
470                *reply = cfg->rword;
471#ifdef SLINK_COLLECT_STATISTICS
472                cfg->stats->reads++;
473#endif       
474                DBG("returning 0\n");
475                return 0;
476        } else {
477                DBG("returning error code\n");
478                return -cfg->status->readstat;
479        }
480}
481
482/*
483 * Function: SLINK_write
484 * Arguments: data: Payload of SLINK data word
485 *            channel: Channel value (bits 22 downto 16) of receive
486 *                     register word
487 * Returns: 0 if command was placed in transmit queue
488 *          -SLINK_QFULL if transmit queue was full (software should retry)
489 * Description: See above.
490 */
491int SLINK_write(int data, int channel)
492{
493        if (cfg->reg->sts & SLINK_S_TNF) {
494                cfg->reg->td = channel << SLINK_CHAN_POS | data;
495#ifdef SLINK_COLLECT_STATISTICS
496                cfg->stats->writes++;
497#endif
498                return 0;
499        }
500
501        return -SLINK_QFULL;   
502}
503
504/*
505 * Function: SLINK_sequence
506 * Arguments: a: Array containing sequence commands
507 *            b: Array where SEQUENCE responses will be stored
508 *            n: Number of commands in a array
509 *            channel: Sequence Channel Number
510 *            reconly: Set to 1 if the SEQUENCE operation is receive only
511 * Returns: 0 if SEQUENCE could be started (SUCCESS)
512 *          -1 if SEQUNCE was not started due to ongoing SEQUENCE
513 */
514int SLINK_seqstart(int *a, int *b, int n, int channel, int reconly)
515
516        /* Only start a new SEQUENCE of the former SEQUENCE has completed */
517        if (cfg->status->seqstat == SLINK_ACTIVE ||
518            cfg->status->seqstat == SLINK_PARERR)
519                return -1;
520
521        /* Tell core about arrays */
522        cfg->reg->abase = (int)a;
523        cfg->reg->bbase = (int)b;
524       
525        /* As far as software is concerned the sequence is now active */
526        cfg->status->seqstat = SLINK_ACTIVE;
527
528        /* Enable SEQUENCE operation with SCN = channel and SLEN = n-1 */
529        if (reconly == 1) {
530           cfg->reg->ctrl = (((n-1) << SLINK_C_SLEN_POS) | SLINK_C_SRO |
531                             (channel << SLINK_C_SCN_POS) |
532                             SLINK_C_SE | (cfg->reg->ctrl & 0xC000000F));
533        } else {
534           cfg->reg->ctrl = (((n-1) << SLINK_C_SLEN_POS) |
535                             (channel << SLINK_C_SCN_POS) |
536                             SLINK_C_SE | (cfg->reg->ctrl & 0xC000000F));
537        }
538
539#ifdef SLINK_COLLECT_STATISTICS
540        cfg->stats->sequences++;
541#endif
542
543        return 0;
544}
545
546
547/* Function: SLINK_seqabort
548 * Description: This function aborts an ongoing SEQUENCE. Software can tell
549 * when the SEQUENCE is aborted by polling SLINK_seqstat().
550 */
551void SLINK_seqabort(void)
552{
553        cfg->reg->ctrl = cfg->reg->ctrl | SLINK_C_AS;
554}
555
556
557/*
558 * Function: SLINK_seqstatus
559 * Returns: The current or status of the SEQUENCE operation:
560 *          SLINK_COMPLETED, SLINK_ACTIVE, SLINK_PARERR, SLINK_AMBAERR,
561 *          SLINK_ABORTED (these are defined in bsp/grslink.h)
562 * Description: Meaning of returned values:
563 *              SLINK_ABORTED: Aborted before all operations completed.
564 *              SLINK_ACTIVE: The core is busy processing the SEQUENCE
565 *              SLINK_AMBAERR: The last SEQUENCE was aborted by an AMBA ERROR
566 *              SLINK_COMPLETED: All words were transferred in the last SEQUENCE
567 *              SLINK_PARERR: Parity error detected. Software may want to abort
568 *
569 *              If the SEQUENCE was aborted SLINK_seqwrds() can be used to
570 *              determine the number of completed operations.
571 */
572int SLINK_seqstatus(void)
573{
574        return cfg->status->seqstat;
575}
576
577/*
578 * Function: SLINK_seqwrds
579 * Returns: -1 for ongoing sequence
580 *          0 if all words were transferred in the last sequence
581 *          number of words if the last SEQUENCE did not complete
582 *          (SLINK_AMBAERR or SLINK_ABORTED is reported ny SLINK_seqstatus())
583 */
584int SLINK_seqwrds(void)
585{
586        switch (cfg->status->seqstat) {
587        case SLINK_COMPLETED: return 0;
588        case SLINK_ACTIVE | SLINK_PARERR: return -1;
589        default: return cfg->status->scnt;
590        }
591}
592
593/*
594 * Function: SLINK_hwstatus
595 * Returns: The SLINK core's status register. The register values can be
596 *          interpreted with the help of macros defined in bsp/grslink.h.
597 */
598int SLINK_hwstatus(void)
599{
600        return cfg->reg->sts;
601}
602
603/*
604 * Function: SLINK_queuestatus
605 * Arguments: iocard: Queue which to check status for
606 * Returns: Number of elements in queue or -1 on non-existent queue
607 * Description: SLINK_queuestatus(queue) returns the number of elements in
608 *              queue 'iocard'
609 */
610int SLINK_queuestatus(int iocard)
611{       
612        unsigned int first, last;
613        SLINK_queue *ioq;
614       
615        if (iocard >= SLINK_NUMQUEUES)
616                return -1;
617
618        ioq = cfg->queues + iocard;
619
620        if (ioq->full)
621                return ioq->size;
622        if (ioq->first == ioq->last)
623                return 0;
624
625        first = ((unsigned int)ioq->first)/sizeof(unsigned int);
626        last = ((unsigned int)ioq->last)/sizeof(unsigned int);
627       
628        return first < last ? last - first : ioq->size - first + last;
629}
630
631/*
632 * Function: SLINK_dequeue
633 * Arguments: iocard: IO card number
634 *            elem: First element in IO card queue
635 * Returns: 0 on success or -1 on empty or non-existent queue
636 * Description:
637 */
638int SLINK_dequeue(int iocard, int *elem)
639{       
640        if (iocard >= SLINK_NUMQUEUES)
641                return -1;
642       
643        SLINK_queue *ioq = cfg->queues + iocard;
644       
645        if (ioq->last != ioq->first || ioq->full) {
646                *elem = *ioq->first;
647                ioq->first = (ioq->first >= ioq->max) ? ioq->buf : ioq->first+1;
648                ioq->full = 0;
649                return 0;
650        }
651        return -1;
652}
653
654/*
655 * Function: SLINK_statistics
656 * Returns: If the core has statistics colletion enabled this function returns
657 * a pointer to a struct containing statistics information, otherwise NULL.
658 */
659SLINK_stats *SLINK_statistics(void)
660{
661#ifdef SLINK_COLLECT_STATISTICS
662        return cfg->stats;
663#else
664        return NULL;
665#endif
666}
Note: See TracBrowser for help on using the repository browser.