source: rtems/c/src/lib/libbsp/sparc/shared/1553/gr1553bm.c @ 6fdded2c

5
Last change on this file since 6fdded2c was 6fdded2c, checked in by Daniel Hellstrom <daniel@…>, on 05/10/17 at 10:39:23

leon, gr1553bm: SMP support by using spin-locks

  • Property mode set to 100644
File size: 13.5 KB
Line 
1/*  GR1553B BM driver
2 *
3 *  COPYRIGHT (c) 2010.
4 *  Cobham Gaisler AB.
5 *
6 *  The license and distribution terms for this file may be
7 *  found in the file LICENSE in this distribution or at
8 *  http://www.rtems.org/license/LICENSE.
9 */
10
11#include <stdlib.h>
12#include <string.h>
13#include <drvmgr/drvmgr.h>
14#include <drvmgr/ambapp_bus.h>
15
16#include <bsp/gr1553b.h>
17#include <bsp/gr1553bm.h>
18
19
20#define GR1553BM_WRITE_MEM(adr, val) *(volatile uint32_t *)(adr) = (uint32_t)(val)
21#define GR1553BM_READ_MEM(adr) (*(volatile uint32_t *)(adr))
22
23#define GR1553BM_WRITE_REG(adr, val) *(volatile uint32_t *)(adr) = (uint32_t)(val)
24#define GR1553BM_READ_REG(adr) (*(volatile uint32_t *)(adr))
25
26/* Use interrupt lock privmitives compatible with SMP defined in
27 * RTEMS 4.11.99 and higher.
28 */
29#if (((__RTEMS_MAJOR__ << 16) | (__RTEMS_MINOR__ << 8) | __RTEMS_REVISION__) >= 0x040b63)
30
31/* map via rtems_interrupt_lock_* API: */
32#define SPIN_DECLARE(lock) RTEMS_INTERRUPT_LOCK_MEMBER(lock)
33#define SPIN_INIT(lock, name) rtems_interrupt_lock_initialize(lock, name)
34#define SPIN_LOCK(lock, level) rtems_interrupt_lock_acquire_isr(lock, &level)
35#define SPIN_LOCK_IRQ(lock, level) rtems_interrupt_lock_acquire(lock, &level)
36#define SPIN_UNLOCK(lock, level) rtems_interrupt_lock_release_isr(lock, &level)
37#define SPIN_UNLOCK_IRQ(lock, level) rtems_interrupt_lock_release(lock, &level)
38#define SPIN_IRQFLAGS(k) rtems_interrupt_lock_context k
39#define SPIN_ISR_IRQFLAGS(k) SPIN_IRQFLAGS(k)
40#define SPIN_FREE(lock) rtems_interrupt_lock_destroy(lock)
41
42#else
43
44/* maintain single-core compatibility with older versions of RTEMS: */
45#define SPIN_DECLARE(name)
46#define SPIN_INIT(lock, name)
47#define SPIN_LOCK(lock, level)
48#define SPIN_LOCK_IRQ(lock, level) rtems_interrupt_disable(level)
49#define SPIN_UNLOCK(lock, level)
50#define SPIN_UNLOCK_IRQ(lock, level) rtems_interrupt_enable(level)
51#define SPIN_IRQFLAGS(k) rtems_interrupt_level k
52#define SPIN_ISR_IRQFLAGS(k)
53#define SPIN_FREE(lock)
54
55#ifdef RTEMS_SMP
56#error SMP mode not compatible with these interrupt lock primitives
57#endif
58
59#endif
60
61struct gr1553bm_priv {
62        struct drvmgr_dev **pdev;
63        struct gr1553b_regs *regs;
64        SPIN_DECLARE(devlock);
65
66        void *buffer;
67        unsigned int buffer_base_hw;
68        unsigned int buffer_base;
69        unsigned int buffer_end;
70        unsigned int buffer_size;
71        unsigned int read_pos;
72        int started;
73        struct gr1553bm_config cfg;
74
75        /* Time updated by IRQ when 24-bit Time counter overflows */
76        volatile uint64_t time;
77};
78
79void gr1553bm_isr(void *data);
80
81/* Default Driver configuration */
82struct gr1553bm_config gr1553bm_default_config =
83{
84        /* Highest resolution, use Time overflow IRQ to track */
85        .time_resolution = 0,
86        .time_ovf_irq = 1,
87
88        /* No filtering, log all */
89        .filt_error_options = GR1553BM_ERROPTS_ALL,
90        .filt_rtadr = 0xffffffff,
91        .filt_subadr = 0xffffffff,
92        .filt_mc = 0x0007ffff,
93
94        /* 128Kbyte dynamically allocated buffer. */
95        .buffer_size = 128*1024,
96        .buffer_custom = NULL,
97};
98
99void gr1553bm_register(void)
100{
101        /* The BM driver rely on the GR1553B Driver */
102        gr1553_register();
103}
104
105static void gr1553bm_hw_start(struct gr1553bm_priv *priv)
106{
107        SPIN_IRQFLAGS(irqflags);
108
109        /* Enable IRQ source and mark running state */
110        SPIN_LOCK_IRQ(&priv->devlock, irqflags);
111
112        priv->started = 1;
113
114        /* Clear old IRQ flags */
115        priv->regs->irq = GR1553B_IRQ_BMD | GR1553B_IRQ_BMTOF;
116
117        /* Unmask IRQ sources */
118        if ( priv->cfg.time_ovf_irq ) {
119                priv->regs->imask |= GR1553B_IRQEN_BMDE | GR1553B_IRQEN_BMTOE;
120        } else {
121                priv->regs->imask |= GR1553B_IRQEN_BMDE;
122        }
123
124        /* Start logging */
125        priv->regs->bm_ctrl =
126                (priv->cfg.filt_error_options &
127                (GR1553B_BM_CTRL_MANL|GR1553B_BM_CTRL_UDWL|GR1553B_BM_CTRL_IMCL))
128                | GR1553B_BM_CTRL_BMEN;
129
130        SPIN_UNLOCK_IRQ(&priv->devlock, irqflags);
131}
132
133static void gr1553bm_hw_stop(struct gr1553bm_priv *priv)
134{
135        SPIN_IRQFLAGS(irqflags);
136
137        SPIN_LOCK_IRQ(&priv->devlock, irqflags);
138
139        /* Stop Logging */
140        priv->regs->bm_ctrl = 0;
141
142        /* Stop IRQ source */
143        priv->regs->imask &= ~(GR1553B_IRQEN_BMDE|GR1553B_IRQEN_BMTOE);
144
145        /* Clear IRQ flags */
146        priv->regs->irq = GR1553B_IRQ_BMD | GR1553B_IRQ_BMTOF;
147
148        priv->started = 0;
149
150        SPIN_UNLOCK_IRQ(&priv->devlock, irqflags);
151}
152
153/* Open device by number */
154void *gr1553bm_open(int minor)
155{
156        struct drvmgr_dev **pdev = NULL;
157        struct gr1553bm_priv *priv = NULL;
158        struct amba_dev_info *ambadev;
159        struct ambapp_core *pnpinfo;
160
161        /* Allocate requested device */
162        pdev = gr1553_bm_open(minor);
163        if ( pdev == NULL )
164                goto fail;
165
166        priv = malloc(sizeof(struct gr1553bm_priv));
167        if ( priv == NULL )
168                goto fail;
169        memset(priv, 0, sizeof(struct gr1553bm_priv));
170
171        /* Init BC device */
172        priv->pdev = pdev;
173        (*pdev)->priv = priv;
174
175        /* Get device information from AMBA PnP information */
176        ambadev = (struct amba_dev_info *)(*pdev)->businfo;
177        pnpinfo = &ambadev->info;
178        priv->regs = (struct gr1553b_regs *)pnpinfo->apb_slv->start;
179        SPIN_INIT(&priv->devlock, "gr1553bm");
180
181        /* Start with default configuration */
182        priv->cfg = gr1553bm_default_config;
183
184        /* Unmask IRQs */
185        gr1553bm_hw_stop(priv);
186
187        return priv;
188
189fail:
190        if ( pdev )
191                gr1553_bm_close(pdev);
192        if ( priv )
193                free(priv);
194        return NULL;
195}
196
197/* Close previously */
198void gr1553bm_close(void *bm)
199{
200        struct gr1553bm_priv *priv = bm;
201
202        if ( priv->started ) {
203                gr1553bm_stop(bm);
204        }
205
206        if ( (priv->cfg.buffer_custom == NULL) && priv->buffer )
207                free(priv->buffer);
208
209        gr1553_bm_close(priv->pdev);
210        free(priv);
211}
212
213/* Configure the BM driver */
214int gr1553bm_config(void *bm, struct gr1553bm_config *cfg)
215{
216        struct gr1553bm_priv *priv = bm;
217
218        if ( priv->started )
219                return -1;
220
221        /* Check Config validity? */
222/*#warning IMPLEMENT.*/
223
224        /* Free old buffer if dynamically allocated */
225        if ( (priv->cfg.buffer_custom == NULL) && priv->buffer ) {
226                free(priv->buffer);
227                priv->buffer = NULL;
228        }
229        priv->buffer_size = cfg->buffer_size & ~0x7; /* on 8 byte bounadry */
230        if ((unsigned int)cfg->buffer_custom & 1) {
231                /* Custom Address Given in Remote address. We need
232                 * to convert it intoTranslate into Hardware a
233                 * hardware accessible address
234                 */
235                priv->buffer_base_hw = (unsigned int)cfg->buffer_custom & ~1;
236                priv->buffer = cfg->buffer_custom;
237                drvmgr_translate_check(
238                        *priv->pdev,
239                        DMAMEM_TO_CPU,
240                        (void *)priv->buffer_base_hw,
241                        (void **)&priv->buffer_base,
242                        priv->buffer_size);
243        } else {
244                if (cfg->buffer_custom == NULL) {
245                        /* Allocate new buffer dynamically */
246                        priv->buffer = malloc(priv->buffer_size + 8);
247                        if (priv->buffer == NULL)
248                                return -1;
249                } else {
250                        /* Address given in CPU accessible address, no
251                         * translation required.
252                         */
253                        priv->buffer = cfg->buffer_custom;
254                }
255                /* Align to 16 bytes */
256                priv->buffer_base = ((unsigned int)priv->buffer + (8-1)) &
257                                        ~(8-1);
258                /* Translate address of buffer base into address that Hardware must
259                 * use to access the buffer.
260                 */
261                drvmgr_translate_check(
262                        *priv->pdev,
263                        CPUMEM_TO_DMA,
264                        (void *)priv->buffer_base,
265                        (void **)&priv->buffer_base_hw,
266                        priv->buffer_size);
267               
268        }
269
270        /* Copy valid config */
271        priv->cfg = *cfg;
272
273        return 0;
274}
275
276/* Start logging */
277int gr1553bm_start(void *bm)
278{
279        struct gr1553bm_priv *priv = bm;
280
281        if ( priv->started )
282                return -1;
283        if ( priv->buffer == NULL )
284                return -2;
285
286        /* Start at Time = 0 */
287        priv->regs->bm_ttag =
288                priv->cfg.time_resolution << GR1553B_BM_TTAG_RES_BIT;
289
290        /* Configure Filters */
291        priv->regs->bm_adr = priv->cfg.filt_rtadr;
292        priv->regs->bm_subadr = priv->cfg.filt_subadr;
293        priv->regs->bm_mc = priv->cfg.filt_mc;
294
295        /* Set up buffer */
296        priv->regs->bm_start = priv->buffer_base_hw;
297        priv->regs->bm_end = priv->buffer_base_hw + priv->cfg.buffer_size - 4;
298        priv->regs->bm_pos = priv->buffer_base_hw;
299        priv->read_pos = priv->buffer_base;
300        priv->buffer_end = priv->buffer_base + priv->cfg.buffer_size;
301
302        /* Register ISR handler and unmask IRQ source at IRQ controller */
303        if (drvmgr_interrupt_register(*priv->pdev, 0, "gr1553bm", gr1553bm_isr, priv))
304                return -3;
305
306        /* Start hardware and set priv->started */
307        gr1553bm_hw_start(priv);
308
309        return 0;
310}
311
312/* Stop logging */
313void gr1553bm_stop(void *bm)
314{
315        struct gr1553bm_priv *priv = bm;
316
317        /* Stop Hardware */
318        gr1553bm_hw_stop(priv);
319
320        /* At this point the hardware must be stopped and IRQ
321         * sources unmasked.
322         */
323
324        /* Unregister ISR handler and unmask 1553 IRQ source at IRQ ctrl */
325        drvmgr_interrupt_unregister(*priv->pdev, 0, gr1553bm_isr, priv);
326}
327
328int gr1553bm_started(void *bm)
329{
330        return ((struct gr1553bm_priv *)bm)->started;
331}
332
333/* Get 64-bit 1553 Time.
334 *
335 * Update software time counters and return the current time.
336 */
337void gr1553bm_time(void *bm, uint64_t *time)
338{
339        struct gr1553bm_priv *priv = bm;
340        unsigned int hwtime, hwtime2;
341
342resample:
343        if ( priv->started && (priv->cfg.time_ovf_irq == 0) ) {
344                /* Update Time overflow counter. The carry bit from Time counter
345                 * is located in IRQ Flag.
346                 *
347                 * When IRQ is not used this function must be called often
348                 * enough to avoid that the Time overflows and the carry
349                 * bit is already set. The frequency depends on the Time
350                 * resolution.
351                 */
352                if ( priv->regs->irq & GR1553B_IRQ_BMTOF ) {
353                        /* Clear carry bit */
354                        priv->regs->irq = GR1553B_IRQ_BMTOF;
355                        priv->time += (GR1553B_BM_TTAG_VAL + 1);
356                }
357        }
358
359        /* Report current Time, even if stopped */
360        hwtime = priv->regs->bm_ttag & GR1553B_BM_TTAG_VAL;
361        if ( time )
362                *time = priv->time | hwtime;
363
364        if ( priv->cfg.time_ovf_irq ) {
365                /* Detect wrap around */
366                hwtime2 = priv->regs->bm_ttag & GR1553B_BM_TTAG_VAL;
367                if ( hwtime > hwtime2 ) {
368                        /* priv->time and hwtime may be out of sync if
369                         * IRQ updated priv->time just after bm_ttag was read
370                         * here, we resample if we detect inconsistancy.
371                         */
372                        goto resample;
373                }
374        }
375}
376
377/* Number of entries available in DMA buffer */
378int gr1553bm_available(void *bm, int *nentries)
379{
380        struct gr1553bm_priv *priv = bm;
381        unsigned int top, bot, pos;
382
383        if ( !priv->started )
384                return -1;
385
386        /* Get BM posistion in log */
387        pos = priv->regs->bm_pos;
388
389        /* Convert into CPU accessible address */
390        pos = priv->buffer_base + (pos - priv->buffer_base_hw);
391
392        if ( pos >= priv->read_pos ) {
393                top = (pos - priv->read_pos)/sizeof(struct gr1553bm_entry);
394                bot = 0;
395        } else {
396                top = (priv->buffer_end - priv->read_pos)/sizeof(struct gr1553bm_entry);
397                bot = (pos - priv->buffer_base)/sizeof(struct gr1553bm_entry);
398        }
399
400        if ( nentries )
401                *nentries = top+bot;
402
403        return 0;
404}
405
406/* Read a maximum number of entries from LOG buffer. */
407int gr1553bm_read(void *bm, struct gr1553bm_entry *dst, int *max)
408{
409        struct gr1553bm_priv *priv = bm;
410        unsigned int dest, pos, left, newPos, len;
411        unsigned int topAdr, botAdr, topLen, botLen;
412
413        if ( !priv || !priv->started )
414                return -1;
415
416        left = *max;
417        pos = priv->regs->bm_pos & ~0x7;
418
419        /* Convert into CPU accessible address */
420        pos = priv->buffer_base + (pos - priv->buffer_base_hw);
421
422        if ( (pos == priv->read_pos) || (left < 1) ) {
423                /* No data available */
424                *max = 0;
425                return 0;
426        }
427        newPos = 0;
428
429        /* Addresses and lengths of BM log buffer */
430        if ( pos >= priv->read_pos ) {
431                /* Read Top only */
432                topAdr = priv->read_pos;
433                botAdr = 0;
434                topLen = (pos - priv->read_pos)/sizeof(struct gr1553bm_entry);
435                botLen = 0;
436        } else {
437                /* Read Top and Bottom */
438                topAdr = priv->read_pos;
439                botAdr = priv->buffer_base;
440                topLen = (priv->buffer_end - priv->read_pos)/sizeof(struct gr1553bm_entry);
441                botLen = (pos - priv->buffer_base)/sizeof(struct gr1553bm_entry);
442        }
443
444        dest = (unsigned int)dst;
445        if ( topLen > 0 ) {
446                /* Copy from top area first */
447                if ( topLen > left ) {
448                        len = left;
449                        left = 0;
450                } else {
451                        len = topLen;
452                        left -= topLen;
453                }
454                newPos = topAdr + (len * sizeof(struct gr1553bm_entry));
455                if ( newPos >= priv->buffer_end )
456                        newPos -= priv->buffer_size;
457                if ( priv->cfg.copy_func ) {
458                        dest += priv->cfg.copy_func(
459                                dest,                   /*Optional Destination*/
460                                (void *)topAdr,         /* DMA start address */
461                                len,                    /* Number of entries */
462                                priv->cfg.copy_func_arg /* Custom ARG */
463                                );
464                } else {
465                        memcpy( (void *)dest,
466                                (void *)topAdr,
467                                len * sizeof(struct gr1553bm_entry));
468                        dest += len * sizeof(struct gr1553bm_entry);
469                }
470        }
471
472        if ( (botLen > 0) && (left > 0) ) {
473                /* Copy bottom area last */
474                if ( botLen > left ) {
475                        len = left;
476                        left = 0;
477                } else {
478                        len = botLen;
479                        left -= botLen;
480                }
481                newPos = botAdr + (len * sizeof(struct gr1553bm_entry));
482
483                if ( priv->cfg.copy_func ) {
484                        priv->cfg.copy_func(
485                                dest,                   /*Optional Destination*/
486                                (void *)botAdr,         /* DMA start address */
487                                len,                    /* Number of entries */
488                                priv->cfg.copy_func_arg /* Custom ARG */
489                                );
490                } else {
491                        memcpy( (void *)dest,
492                                (void *)botAdr,
493                                len * sizeof(struct gr1553bm_entry));
494                }
495        }
496
497        /* Remember last read posistion in buffer */
498        /*printf("New pos: 0x%08x (0x%08x), %d\n", newPos, priv->read_pos, *max - left);*/
499        priv->read_pos = newPos;
500
501        /* Return number of entries read */
502        *max = *max - left;
503
504        return 0;
505}
506
507/* Note: This is a shared interrupt handler, with BC/RT driver
508 *       we must determine the cause of IRQ before handling it.
509 */
510void gr1553bm_isr(void *data)
511{
512        struct gr1553bm_priv *priv = data;
513        uint32_t irqflag;
514
515        /* Get Causes */
516        irqflag = priv->regs->irq & (GR1553B_IRQ_BMD | GR1553B_IRQ_BMTOF);
517
518        /* Check spurious IRQs */
519        if ( (irqflag == 0) || (priv->started == 0) )
520                return;
521
522        if ( (irqflag & GR1553B_IRQ_BMTOF) && priv->cfg.time_ovf_irq ) {
523                /* 1553 Time Over flow. Time is 24-bits */
524                priv->time += (GR1553B_BM_TTAG_VAL + 1);
525
526                /* Clear cause handled */
527                priv->regs->irq = GR1553B_IRQ_BMTOF;
528        }
529
530        if ( irqflag & GR1553B_IRQ_BMD ) {
531                /* BM DMA ERROR. Fatal error, we stop BM hardware and let
532                 * user take care of it. From now on all calls will result
533                 * in an error because the BM is stopped (priv->started=0).
534                 */
535
536                /* Clear cause handled */
537                priv->regs->irq = GR1553B_IRQ_BMD;
538
539                if ( priv->cfg.dma_error_isr )
540                        priv->cfg.dma_error_isr(data, priv->cfg.dma_error_arg);
541
542                gr1553bm_hw_stop(priv);
543        }
544}
Note: See TracBrowser for help on using the repository browser.