source: rtems/c/src/lib/libbsp/sparc/shared/slink/grslink.c @ 5823bae8

4.11
Last change on this file since 5823bae8 was 5823bae8, checked in by Daniel Hellstrom <daniel@…>, on Feb 27, 2015 at 1:03:15 PM

LEON: move driver headers to bsp/ directory

  • 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.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 */
47typedef 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
59typedef 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
65typedef 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
74typedef 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
88static 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 */
99static 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 */
152static 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 */
177static 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 */
196static 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 */
210static 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 */
236static 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 */
339int 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 */
422void 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 */
431void 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 */
449int 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 */
486int 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 */
509int 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 */
546void 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 */
567int 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 */
579int 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 */
593int 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 */
605int 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 */
633int 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 */
654SLINK_stats *SLINK_statistics(void)
655{
656#ifdef SLINK_COLLECT_STATISTICS
657        return cfg->stats;
658#else
659        return NULL;
660#endif
661}
Note: See TracBrowser for help on using the repository browser.