source: rtems/bsps/sparc/shared/slink/grslink.c @ 11f3b9a

5
Last change on this file since 11f3b9a was 11f3b9a, checked in by Sebastian Huber <sebastian.huber@…>, on 11/26/18 at 14:55:38

bsps/sparc: Add grlib_malloc(), grlib_calloc()

This avoids a dependency to errno in device driver code.

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