summaryrefslogtreecommitdiffstats
path: root/c/src/lib/libbsp/sparc/shared/1553/gr1553bc.c
diff options
context:
space:
mode:
Diffstat (limited to 'c/src/lib/libbsp/sparc/shared/1553/gr1553bc.c')
-rw-r--r--c/src/lib/libbsp/sparc/shared/1553/gr1553bc.c1674
1 files changed, 1674 insertions, 0 deletions
diff --git a/c/src/lib/libbsp/sparc/shared/1553/gr1553bc.c b/c/src/lib/libbsp/sparc/shared/1553/gr1553bc.c
new file mode 100644
index 0000000000..4133200709
--- /dev/null
+++ b/c/src/lib/libbsp/sparc/shared/1553/gr1553bc.c
@@ -0,0 +1,1674 @@
+/* GR1553B BC driver
+ *
+ * COPYRIGHT (c) 2010.
+ * Cobham Gaisler AB.
+ *
+ * The license and distribution terms for this file may be
+ * found in the file LICENSE in this distribution or at
+ * http://www.rtems.com/license/LICENSE.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <drvmgr/drvmgr.h>
+#include <drvmgr/ambapp_bus.h>
+
+#include <gr1553b.h>
+#include <gr1553bc.h>
+
+#define GR1553BC_WRITE_MEM(adr, val) *(volatile uint32_t *)(adr) = (uint32_t)(val)
+#define GR1553BC_READ_MEM(adr) (*(volatile uint32_t *)(adr))
+
+#define GR1553BC_WRITE_REG(adr, val) *(volatile uint32_t *)(adr) = (uint32_t)(val)
+#define GR1553BC_READ_REG(adr) (*(volatile uint32_t *)(adr))
+
+#ifndef IRQ_GLOBAL_PREPARE
+ #define IRQ_GLOBAL_PREPARE(level) rtems_interrupt_level level
+#endif
+
+#ifndef IRQ_GLOBAL_DISABLE
+ #define IRQ_GLOBAL_DISABLE(level) rtems_interrupt_disable(level)
+#endif
+
+#ifndef IRQ_GLOBAL_ENABLE
+ #define IRQ_GLOBAL_ENABLE(level) rtems_interrupt_enable(level)
+#endif
+
+/* Needed by list for data pinter and BD translation */
+struct gr1553bc_priv {
+ struct drvmgr_dev **pdev;
+ struct gr1553b_regs *regs;
+ struct gr1553bc_list *list;
+ struct gr1553bc_list *alist;
+ int started;
+
+ /* IRQ log management */
+ void *irq_log_p;
+ uint32_t *irq_log_base;
+ uint32_t *irq_log_curr;
+ uint32_t *irq_log_end;
+ uint32_t *irq_log_base_hw;
+
+ /* Standard IRQ handler function */
+ bcirq_func_t irq_func;
+ void *irq_data;
+};
+
+
+/*************** LIST HANDLING ROUTINES ***************/
+
+/* This marks that the jump is a jump to next Minor.
+ * It is important that it sets one of the two LSB
+ * so that we can separate it from a JUMP-IRQ function,
+ * function pointers must be aligned to 4bytes.
+ *
+ * This marker is used to optimize the INDICATION process,
+ * from a descriptor pointer we can step to next Jump that
+ * has this MARKER set, then we know that the MID is stored
+ * there.
+ *
+ * The marker is limited to 1 byte.
+ */
+#define NEXT_MINOR_MARKER 0x01
+
+/* To separate ASYNC list from SYNC list we mark them differently, but with
+ * LSB always set. This can be used to get the list the descriptor is a part
+ * of.
+ */
+#define NEXT_MINOR_MARKER_ASYNC 0x80
+
+struct gr1553bc_list_cfg gr1553bc_def_cfg =
+{
+ .rt_timeout =
+ {
+ 20, 20, 20, 20,
+ 20, 20, 20, 20,
+ 20, 20, 20, 20,
+ 20, 20, 20, 20,
+ 20, 20, 20, 20,
+ 20, 20, 20, 20,
+ 20, 20, 20, 20,
+ 20, 20, 20
+ },
+ .bc_timeout = 30,
+ .tropt_irq_on_err = 0,
+ .tropt_pause_on_err = 0,
+ .async_list = 0,
+};
+
+int gr1553bc_list_alloc(struct gr1553bc_list **list, int max_major)
+{
+ int size;
+ struct gr1553bc_list *l;
+
+ size = sizeof(struct gr1553bc_list) + max_major * sizeof(void *);
+ l = malloc(size);
+ if ( l == NULL )
+ return -1;
+ memset(l, 0, size);
+
+ l->major_cnt = max_major;
+ *list = l;
+
+ /* Set default options:
+ * - RT timeout tolerance 20us
+ * - Global transfer options used when generating transfer descriptors
+ * - No BC device, note that this only works when no translation is
+ * required
+ */
+ if ( gr1553bc_list_config(l, &gr1553bc_def_cfg, NULL) ) {
+ free(l);
+ return -1;
+ }
+
+ return 0;
+}
+
+void gr1553bc_list_free(struct gr1553bc_list *list)
+{
+ gr1553bc_list_table_free(list);
+ free(list);
+}
+
+int gr1553bc_list_config
+ (
+ struct gr1553bc_list *list,
+ struct gr1553bc_list_cfg *cfg,
+ void *bc
+ )
+{
+ int timeout, i, tropts;
+
+ /* RT Time Tolerances */
+ for (i=0; i<31; i++) {
+ /* 0=14us, 1=18us ... 0xf=74us
+ * round upwards: 15us will be 18us
+ */
+ timeout = ((cfg->rt_timeout[i] + 1) - 14) / 4;
+ if ( (timeout > 0xf) || (timeout < 0) )
+ return -1;
+ list->rt_timeout[i] = timeout;
+ }
+ timeout = ((cfg->bc_timeout + 1) - 14) / 4;
+ if ( timeout > 0xf )
+ return -1;
+ list->rt_timeout[i] = timeout;
+
+ /* Transfer descriptor generation options */
+ tropts = 0;
+ if ( cfg->tropt_irq_on_err )
+ tropts |= 1<<28;
+ if ( cfg->tropt_pause_on_err )
+ tropts |= 1<<26;
+ list->tropts = tropts;
+
+ list->async_list = cfg->async_list;
+ list->bc = bc;
+
+ return 0;
+}
+
+void gr1553bc_list_link_major(
+ struct gr1553bc_major *major,
+ struct gr1553bc_major *next
+ )
+{
+ if ( major ) {
+ major->next = next;
+ if ( next ) {
+ major->minors[major->cfg->minor_cnt-1]->next =
+ next->minors[0];
+ } else {
+ major->minors[major->cfg->minor_cnt-1]->next = NULL;
+ }
+ }
+}
+
+int gr1553bc_list_set_major(
+ struct gr1553bc_list *list,
+ struct gr1553bc_major *major,
+ int no)
+{
+ struct gr1553bc_major *prev, *next;
+
+ if ( no >= list->major_cnt )
+ return -1;
+
+ list->majors[no] = major;
+
+ /* Link previous Major frame with this one */
+ if ( no > 0 ) {
+ prev = list->majors[no-1];
+ } else {
+ /* First Major is linked with last major */
+ prev = list->majors[list->major_cnt-1];
+ }
+
+ /* Link to next Major if not the last one and if there is
+ * a next major
+ */
+ if ( no == list->major_cnt-1 ) {
+ /* The last major, assume that it is connected with the first */
+ next = list->majors[0];
+ } else {
+ next = list->majors[no+1];
+ }
+
+ /* Link previous frame to jump into this */
+ gr1553bc_list_link_major(prev, major);
+
+ /* Link This frame to jump into the next */
+ gr1553bc_list_link_major(major, next);
+
+ return 0;
+}
+
+/* Translate Descriptor address from CPU-address to Hardware Address */
+static inline union gr1553bc_bd *gr1553bc_bd_cpu2hw
+ (
+ struct gr1553bc_list *list,
+ union gr1553bc_bd *bd
+ )
+{
+ return (union gr1553bc_bd *)(((unsigned int)bd - list->table_cpu) +
+ list->table_hw);
+}
+
+/* Translate Descriptor address from HW-address to CPU Address */
+static inline union gr1553bc_bd *gr1553bc_bd_hw2cpu
+ (
+ struct gr1553bc_list *list,
+ union gr1553bc_bd *bd
+ )
+{
+ return (union gr1553bc_bd *)(((unsigned int)bd - list->table_hw) +
+ list->table_cpu);
+}
+
+int gr1553bc_minor_table_size(struct gr1553bc_minor *minor)
+{
+ struct gr1553bc_minor_cfg *mincfg = minor->cfg;
+ int slot_cnt;
+
+ /* SLOTS + JUMP */
+ slot_cnt = mincfg->slot_cnt + 1;
+ if ( mincfg->timeslot ) {
+ /* time management requires 1 extra slot */
+ slot_cnt++;
+ }
+
+ return slot_cnt * GR1553BC_BD_SIZE;
+}
+
+int gr1553bc_list_table_size(struct gr1553bc_list *list)
+{
+ struct gr1553bc_major *major;
+ int i, j, minor_cnt, size;
+
+ size = 0;
+ for (i=0; i<list->major_cnt; i++) {
+ major = list->majors[i];
+ minor_cnt = major->cfg->minor_cnt;
+ for (j=0; j<minor_cnt; j++) {
+ /* 128-bit Alignment required by HW */
+ size += (GR1553BC_BD_ALIGN -
+ (size & (GR1553BC_BD_ALIGN-1))) &
+ ~(GR1553BC_BD_ALIGN-1);
+
+ /* Size required by descriptors */
+ size += gr1553bc_minor_table_size(major->minors[j]);
+ }
+ }
+
+ return size;
+}
+
+int gr1553bc_list_table_alloc
+ (
+ struct gr1553bc_list *list,
+ void *bdtab_custom
+ )
+{
+ struct gr1553bc_major *major;
+ int i, j, minor_cnt, size;
+ unsigned int table;
+ struct gr1553bc_priv *bcpriv = list->bc;
+
+ /* Free previous allocated descriptor table */
+ gr1553bc_list_table_free(list);
+
+ /* Remember user's settings for uninitialization */
+ list->_table_custom = bdtab_custom;
+
+ /* Get Size required for descriptors */
+ size = gr1553bc_list_table_size(list);
+
+ if ((unsigned int)bdtab_custom & 0x1) {
+ /* Address given in Hardware accessible address, we
+ * convert it into CPU-accessible address.
+ */
+ list->table_hw = (unsigned int)bdtab_custom & ~0x1;
+ list->_table = bdtab_custom;
+ drvmgr_translate_check(
+ *bcpriv->pdev,
+ DMAMEM_TO_CPU,
+ (void *)list->table_hw,
+ (void **)&list->table_cpu,
+ size);
+ } else {
+ if (bdtab_custom == NULL) {
+ /* Allocate descriptors */
+ list->_table = malloc(size + (GR1553BC_BD_ALIGN-1));
+ if ( list->_table == NULL )
+ return -1;
+ } else {
+ /* Custom address, given in CPU-accessible address */
+ list->_table = bdtab_custom;
+ }
+ /* 128-bit Alignment required by HW */
+ list->table_cpu =
+ (((unsigned int)list->_table + (GR1553BC_BD_ALIGN-1)) &
+ ~(GR1553BC_BD_ALIGN-1));
+
+ /* We got CPU accessible descriptor table address, now we
+ * translate that into an address that the Hardware can
+ * understand
+ */
+ if (bcpriv) {
+ drvmgr_translate_check(
+ *bcpriv->pdev,
+ CPUMEM_TO_DMA,
+ (void *)list->table_cpu,
+ (void **)&list->table_hw,
+ size
+ );
+ } else {
+ list->table_hw = list->table_cpu;
+ }
+ }
+
+ /* Write End-Of-List all over the descriptor table here,
+ * For debugging/safety?
+ */
+
+ /* Assign descriptors to all minor frames. The addresses is
+ * CPU-accessible addresses.
+ */
+ table = list->table_cpu;
+ for (i=0; i<list->major_cnt; i++) {
+ major = list->majors[i];
+ minor_cnt = major->cfg->minor_cnt;
+ for (j=0; j<minor_cnt; j++) {
+ /* 128-bit Alignment required by HW */
+ table = (table + (GR1553BC_BD_ALIGN-1)) &
+ ~(GR1553BC_BD_ALIGN-1);
+ major->minors[j]->bds = (union gr1553bc_bd *)table;
+
+ /* Calc size required by descriptors */
+ table += gr1553bc_minor_table_size(major->minors[j]);
+ }
+ }
+
+ return 0;
+}
+
+void gr1553bc_list_table_free(struct gr1553bc_list *list)
+{
+ if ( (list->_table_custom == NULL) && list->_table ) {
+ free(list->_table);
+ }
+ list->_table = NULL;
+ list->_table_custom = NULL;
+ list->table_cpu = 0;
+ list->table_hw = 0;
+}
+
+/* Init descriptor table provided by each minor frame,
+ * we link them together using unconditional JUMP.
+ */
+int gr1553bc_list_table_build(struct gr1553bc_list *list)
+{
+ struct gr1553bc_major *major;
+ struct gr1553bc_minor *minor;
+ struct gr1553bc_minor_cfg *mincfg;
+ int i, j, k, minor_cnt, marker;
+ union gr1553bc_bd *bds, *hwbd;
+
+ marker = NEXT_MINOR_MARKER;
+ if ( list->async_list )
+ marker |= NEXT_MINOR_MARKER_ASYNC;
+
+ /* Create Major linking */
+ for (i=0; i<list->major_cnt; i++) {
+ major = list->majors[i];
+ minor_cnt = major->cfg->minor_cnt;
+ for (j=0; j<minor_cnt; j++) {
+ minor = major->minors[j];
+ mincfg = minor->cfg;
+ bds = minor->bds;
+
+ /* BD[0..SLOTCNT-1] = message slots
+ * BD[SLOTCNT+0] = END
+ * BD[SLOTCNT+1] = JUMP
+ *
+ * or if no optional time slot handling:
+ *
+ * BD[0..SLOTCNT-1] = message slots
+ * BD[SLOTCNT] = JUMP
+ */
+
+ /* BD[0..SLOTCNT-1] */
+ for (k=0; k<mincfg->slot_cnt; k++) {
+ gr1553bc_bd_tr_init(
+ &bds[k].tr,
+ GR1553BC_TR_DUMMY_0,
+ GR1553BC_TR_DUMMY_1,
+ 0,
+ 0);
+ }
+
+ /* BD[SLOTCNT] (OPTIONAL)
+ * If a minor frame is configured to be executed in
+ * certain time (given a time slot), this descriptor
+ * sums up all unused time. The time slot is
+ * decremented when messages are inserted into the
+ * minor frame and increased when messages are removed.
+ */
+ if ( mincfg->timeslot > 0 ) {
+ gr1553bc_bd_tr_init(
+ &bds[k].tr,
+ GR1553BC_TR_DUMMY_0 | (mincfg->timeslot >> 2),
+ GR1553BC_TR_DUMMY_1,
+ 0,
+ 0);
+ k++;
+ }
+
+ /* Last descriptor is a jump to next minor frame, to a
+ * synchronization point. If chain ends here, the list
+ * is marked with a "end-of-list" marker.
+ *
+ */
+ if ( minor->next ) {
+ /* Translate CPU address of BD into HW address */
+ hwbd = gr1553bc_bd_cpu2hw(
+ list,
+ &minor->next->bds[0]
+ );
+ gr1553bc_bd_init(
+ &bds[k],
+ 0xf,
+ GR1553BC_UNCOND_JMP,
+ (uint32_t)hwbd,
+ ((GR1553BC_ID(i,j,k) << 8) | marker),
+ 0
+ );
+ } else {
+ gr1553bc_bd_init(
+ &bds[k],
+ 0xf,
+ GR1553BC_TR_EOL,
+ 0,
+ ((GR1553BC_ID(i,j,k) << 8) | marker),
+ 0);
+ }
+ }
+ }
+
+ return 0;
+}
+
+void gr1553bc_bd_init(
+ union gr1553bc_bd *bd,
+ unsigned int flags,
+ uint32_t word0,
+ uint32_t word1,
+ uint32_t word2,
+ uint32_t word3
+ )
+{
+ struct gr1553bc_bd_raw *raw = &bd->raw;
+
+ if ( flags & 0x1 ) {
+ if ( (flags & KEEP_TIMESLOT) &&
+ ((word0 & GR1553BC_BD_TYPE) == 0) ) {
+ /* Don't touch timeslot previously allocated */
+ word0 &= ~GR1553BC_TR_TIME;
+ word0 |= GR1553BC_READ_MEM(&raw->words[0]) &
+ GR1553BC_TR_TIME;
+ }
+ GR1553BC_WRITE_MEM(&raw->words[0], word0);
+ }
+ if ( flags & 0x2 )
+ GR1553BC_WRITE_MEM(&raw->words[1], word1);
+ if ( flags & 0x4 )
+ GR1553BC_WRITE_MEM(&raw->words[2], word2);
+ if ( flags & 0x8 )
+ GR1553BC_WRITE_MEM(&raw->words[3], word3);
+}
+
+/* Alloc a Major frame according to the configuration structure */
+int gr1553bc_major_alloc_skel
+ (
+ struct gr1553bc_major **major,
+ struct gr1553bc_major_cfg *cfg
+ )
+{
+ struct gr1553bc_major *maj;
+ struct gr1553bc_minor *minor;
+ int size, i;
+
+ if ( (cfg == NULL) || (major == NULL) || (cfg->minor_cnt <= 0) )
+ return -1;
+
+ /* Allocate Major Frame description, but no descriptors */
+ size = sizeof(struct gr1553bc_major) + cfg->minor_cnt *
+ (sizeof(struct gr1553bc_minor) + sizeof(void *));
+ maj = (struct gr1553bc_major *)malloc(size);
+ if ( maj == NULL )
+ return -1;
+
+ maj->cfg = cfg;
+ maj->next = NULL;
+
+ /* Create links between minor frames, and from minor frames
+ * to configuration structure.
+ */
+ minor = (struct gr1553bc_minor *)&maj->minors[cfg->minor_cnt];
+ for (i=0; i<cfg->minor_cnt; i++, minor++) {
+ maj->minors[i] = minor;
+ minor->next = minor + 1;
+ minor->cfg = &cfg->minor_cfgs[i];
+ minor->alloc = 0;
+ minor->bds = NULL;
+ }
+ /* last Minor should point to next Major frame's first minor,
+ * we do that somewhere else.
+ */
+ (minor - 1)->next = NULL;
+
+ *major = maj;
+
+ return 0;
+}
+
+struct gr1553bc_major *gr1553bc_major_from_id
+ (
+ struct gr1553bc_list *list,
+ int mid
+ )
+{
+ int major_no;
+
+ /* Find Minor Frame from MID */
+ major_no = GR1553BC_MAJID_FROM_ID(mid);
+
+ if ( major_no >= list->major_cnt )
+ return NULL;
+ return list->majors[major_no];
+}
+
+struct gr1553bc_minor *gr1553bc_minor_from_id
+ (
+ struct gr1553bc_list *list,
+ int mid
+ )
+{
+ int minor_no;
+ struct gr1553bc_major *major;
+
+ /* Get Major from ID */
+ major = gr1553bc_major_from_id(list, mid);
+ if ( major == NULL )
+ return NULL;
+
+ /* Find Minor Frame from MID */
+ minor_no = GR1553BC_MINID_FROM_ID(mid);
+
+ if ( minor_no >= major->cfg->minor_cnt )
+ return NULL;
+ return major->minors[minor_no];
+}
+
+union gr1553bc_bd *gr1553bc_slot_bd
+ (
+ struct gr1553bc_list *list,
+ int mid
+ )
+{
+ struct gr1553bc_minor *minor;
+ int slot_no;
+
+ /*** look up BD ***/
+
+ /* Get minor */
+ minor = gr1553bc_minor_from_id(list, mid);
+ if ( minor == NULL )
+ return NULL;
+
+ /* Get Slot */
+ slot_no = GR1553BC_SLOTID_FROM_ID(mid);
+ if ( slot_no >= 0xff )
+ slot_no = 0;
+
+ /* Get BD address */
+ return &minor->bds[slot_no];
+}
+
+int gr1553bc_minor_first_avail(struct gr1553bc_minor *minor)
+{
+ int slot_num;
+ uint32_t alloc;
+
+ alloc = minor->alloc;
+ if ( alloc == 0xffffffff ) {
+ /* No free */
+ return -1;
+ }
+ slot_num = 0;
+ while ( alloc & 1 ) {
+ alloc = alloc >> 1;
+ slot_num++;
+ }
+ if ( slot_num >= minor->cfg->slot_cnt ) {
+ /* no free */
+ return -1;
+ }
+ return slot_num;
+}
+
+int gr1553bc_slot_alloc(
+ struct gr1553bc_list *list,
+ int *mid,
+ int timeslot,
+ union gr1553bc_bd **bd
+ )
+{
+ struct gr1553bc_minor *minor = gr1553bc_minor_from_id(list, *mid);
+
+ return gr1553bc_slot_alloc2(minor, mid, timeslot, bd);
+}
+
+/* Same as gr1553bc_slot_alloc but identifies a minor instead of list.
+ * The major/minor part of MID is ignored.
+ */
+int gr1553bc_slot_alloc2(
+ struct gr1553bc_minor *minor,
+ int *mid,
+ int timeslot,
+ union gr1553bc_bd **bd
+ )
+{
+ int slot_no;
+ uint32_t set0;
+ int timefree;
+ struct gr1553bc_bd_tr *trbd;
+ struct gr1553bc_minor_cfg *mincfg;
+
+ if ( minor == NULL )
+ return -1;
+
+ mincfg = minor->cfg;
+
+ /* Find first free slot if not a certain slot is requested */
+ slot_no = GR1553BC_SLOTID_FROM_ID(*mid);
+ if ( slot_no == 0xff ) {
+ slot_no = gr1553bc_minor_first_avail(minor);
+ if ( slot_no < 0 )
+ return -1;
+ } else {
+ /* Allocate a certain slot, check that it is free */
+ if ( slot_no >= mincfg->slot_cnt )
+ return -1;
+ if ( (1<<slot_no) & minor->alloc )
+ return -1;
+ }
+
+ /* Ok, we got our slot. Lets allocate time for slot if requested by user
+ * and time management is enabled for this Minor Frame.
+ */
+ if ( timeslot > 0 ) {
+ /* Make timeslot on a 4us boundary (time resolution of core) */
+ timeslot = (timeslot + 0x3) >> 2;
+
+ if ( mincfg->timeslot ) {
+ /* Subtract requested time from free time */
+ trbd = &minor->bds[mincfg->slot_cnt].tr;
+ set0 = GR1553BC_READ_MEM(&trbd->settings[0]);
+ timefree = set0 & GR1553BC_TR_TIME;
+ if ( timefree < timeslot ) {
+ /* Not enough time left to schedule slot in minor */
+ return -1;
+ }
+ /* Store back the time left */
+ timefree -= timeslot;
+ set0 = (set0 & ~GR1553BC_TR_TIME) | timefree;
+ GR1553BC_WRITE_MEM(&trbd->settings[0], set0);
+ /* Note: at the moment the minor frame can be executed faster
+ * than expected, we hurry up writing requested
+ * descriptor.
+ */
+ }
+ }
+
+ /* Make the allocated descriptor be an empty slot with the
+ * timeslot requested.
+ */
+ trbd = &minor->bds[slot_no].tr;
+ gr1553bc_bd_tr_init(
+ trbd,
+ GR1553BC_TR_DUMMY_0 | timeslot,
+ GR1553BC_TR_DUMMY_1,
+ 0,
+ 0);
+
+ /* Allocate slot */
+ minor->alloc |= 1<<slot_no;
+
+ if ( bd )
+ *bd = (union gr1553bc_bd *)trbd;
+ *mid = GR1553BC_ID_SET_SLOT(*mid, slot_no);
+
+ return 0;
+}
+
+/* Return time slot freed (if time is managed by driver), negative on error */
+int gr1553bc_slot_free(struct gr1553bc_list *list, int mid)
+{
+ struct gr1553bc_minor *minor = gr1553bc_minor_from_id(list, mid);
+
+ return gr1553bc_slot_free2(minor, mid);
+}
+
+/* Return time slot freed (if time is managed by driver), negative on error */
+int gr1553bc_slot_free2(struct gr1553bc_minor *minor, int mid)
+{
+ union gr1553bc_bd *bd;
+ struct gr1553bc_bd_tr *endbd;
+ struct gr1553bc_minor_cfg *mincfg;
+ int slot_no, timeslot, timefree;
+ uint32_t word0, set0;
+
+ if ( minor == NULL )
+ return -1;
+
+ slot_no = GR1553BC_SLOTID_FROM_ID(mid);
+
+ if ( (minor->alloc & (1<<slot_no)) == 0 )
+ return -1;
+
+ bd = &minor->bds[slot_no];
+
+ /* If the driver handles time for this minor frame, return
+ * time if previuosly requested.
+ */
+ timeslot = 0;
+ mincfg = minor->cfg;
+ if ( mincfg->timeslot > 0 ) {
+ /* Find out if message slot had time allocated */
+ word0 = GR1553BC_READ_MEM(&bd->raw.words[0]);
+ if ( word0 & GR1553BC_BD_TYPE ) {
+ /* Condition ==> no time slot allocated */
+ } else {
+ /* Transfer descriptor, may have time slot */
+ timeslot = word0 & GR1553BC_TR_TIME;
+ if ( timeslot > 0 ) {
+ /* Return previously allocated time to END
+ * TIME descriptor.
+ */
+ endbd = &minor->bds[mincfg->slot_cnt].tr;
+ set0 = GR1553BC_READ_MEM(&endbd->settings[0]);
+ timefree = set0 & GR1553BC_TR_TIME;
+ timefree += timeslot;
+ set0 = (set0 & ~GR1553BC_TR_TIME) | timefree;
+ GR1553BC_WRITE_MEM(&endbd->settings[0], set0);
+ /* Note: at the moment the minor frame can be
+ * executed slower than expected, the
+ * timeslot is at two locations.
+ */
+ }
+ }
+ }
+
+ /* Make slot an empty message */
+ gr1553bc_bd_tr_init(
+ &bd->tr,
+ GR1553BC_TR_DUMMY_0,
+ GR1553BC_TR_DUMMY_1,
+ 0,
+ 0);
+
+ /* unallocate descriptor */
+ minor->alloc &= ~(1<<slot_no);
+
+ /* Return time freed in microseconds */
+ return timeslot << 2;
+}
+
+int gr1553bc_list_freetime(struct gr1553bc_list *list, int mid)
+{
+ struct gr1553bc_minor *minor = gr1553bc_minor_from_id(list, mid);
+
+ return gr1553bc_minor_freetime(minor);
+}
+
+int gr1553bc_minor_freetime(struct gr1553bc_minor *minor)
+{
+ struct gr1553bc_bd_tr *endbd;
+ struct gr1553bc_minor_cfg *mincfg;
+ int timefree;
+ uint32_t set0;
+
+ if ( minor == NULL )
+ return -1;
+
+ /* If the driver handles time for this minor frame, return
+ * time if previuosly requested.
+ */
+ timefree = 0;
+ mincfg = minor->cfg;
+ if ( mincfg->timeslot > 0 ) {
+ /* Return previously allocated time to END
+ * TIME descriptor.
+ */
+ endbd = &minor->bds[mincfg->slot_cnt].tr;
+ set0 = GR1553BC_READ_MEM(&endbd->settings[0]);
+ timefree = (set0 & GR1553BC_TR_TIME) << 2;
+ }
+
+ /* Return time freed */
+ return timefree;
+}
+
+int gr1553bc_slot_raw
+ (
+ struct gr1553bc_list *list,
+ int mid,
+ unsigned int flags,
+ uint32_t word0,
+ uint32_t word1,
+ uint32_t word2,
+ uint32_t word3
+ )
+{
+ struct gr1553bc_minor *minor;
+ union gr1553bc_bd *bd;
+ int slot_no;
+
+ minor = gr1553bc_minor_from_id(list, mid);
+ if ( minor == NULL )
+ return -1;
+
+ /* Get Slot */
+ slot_no = GR1553BC_SLOTID_FROM_ID(mid);
+ if ( slot_no >= minor->cfg->slot_cnt ) {
+ return -1;
+ }
+
+ /* Get descriptor */
+ bd = &minor->bds[slot_no];
+
+ /* Build empty descriptor. */
+ gr1553bc_bd_init(
+ bd,
+ flags,
+ word0,
+ word1,
+ word2,
+ word3);
+
+ return 0;
+}
+
+/* Create unconditional IRQ customly defined location
+ * The IRQ is disabled, enable it with gr1553bc_slot_irq_enable().
+ */
+int gr1553bc_slot_irq_prepare
+ (
+ struct gr1553bc_list *list,
+ int mid,
+ bcirq_func_t func,
+ void *data
+ )
+{
+ union gr1553bc_bd *bd;
+ int slot_no, to_mid;
+
+ /* Build unconditional IRQ descriptor. The padding is used
+ * for identifying the MINOR frame and function and custom data.
+ *
+ * The IRQ is disabled at first, a unconditional jump to next
+ * descriptor in table.
+ */
+
+ /* Get BD address of jump destination */
+ slot_no = GR1553BC_SLOTID_FROM_ID(mid);
+ to_mid = GR1553BC_ID_SET_SLOT(mid, slot_no + 1);
+ bd = gr1553bc_slot_bd(list, to_mid);
+ if ( bd == NULL )
+ return -1;
+ bd = gr1553bc_bd_cpu2hw(list, bd);
+
+ return gr1553bc_slot_raw(
+ list,
+ mid,
+ 0xF,
+ GR1553BC_UNCOND_JMP,
+ (uint32_t)bd,
+ (uint32_t)func,
+ (uint32_t)data
+ );
+}
+
+/* Enable previously prepared unconditional IRQ */
+int gr1553bc_slot_irq_enable(struct gr1553bc_list *list, int mid)
+{
+ /* Leave word1..3 untouched:
+ * 1. Unconditional Jump address
+ * 2. Function
+ * 3. Custom Data
+ *
+ * Since only one bit is changed in word0 (Condition word),
+ * no hardware/software races will exist ==> it is safe
+ * to enable/disable IRQ at any time independent of where
+ * hardware is in table.
+ */
+ return gr1553bc_slot_raw(
+ list,
+ mid,
+ 0x1, /* change only WORD0 */
+ GR1553BC_UNCOND_IRQ,
+ 0,
+ 0,
+ 0);
+}
+
+/* Disable unconditional IRQ point, changed to unconditional JUMP
+ * to descriptor following.
+ * After disabling it it can be enabled again, or freed.
+ */
+int gr1553bc_slot_irq_disable(struct gr1553bc_list *list, int mid)
+{
+ return gr1553bc_slot_raw(
+ list,
+ mid,
+ 0x1, /* change only WORD0, JUMP address already set */
+ GR1553BC_UNCOND_JMP,
+ 0,
+ 0,
+ 0);
+}
+
+int gr1553bc_slot_empty(struct gr1553bc_list *list, int mid)
+{
+ return gr1553bc_slot_raw(
+ list,
+ mid,
+ 0xF | KEEP_TIMESLOT,
+ GR1553BC_TR_DUMMY_0,
+ GR1553BC_TR_DUMMY_1,
+ 0,
+ 0);
+}
+
+int gr1553bc_slot_exttrig(struct gr1553bc_list *list, int mid)
+{
+ return gr1553bc_slot_raw(
+ list,
+ mid,
+ 0xF | KEEP_TIMESLOT,
+ GR1553BC_TR_DUMMY_0 | GR1553BC_TR_EXTTRIG,
+ GR1553BC_TR_DUMMY_1,
+ 0,
+ 0);
+}
+
+int gr1553bc_slot_jump
+ (
+ struct gr1553bc_list *list,
+ int mid,
+ uint32_t condition,
+ int to_mid
+ )
+{
+ union gr1553bc_bd *bd;
+
+ /* Get BD address */
+ bd = gr1553bc_slot_bd(list, to_mid);
+ if ( bd == NULL )
+ return -1;
+ /* Convert into an address that the HW understand */
+ bd = gr1553bc_bd_cpu2hw(list, bd);
+
+ return gr1553bc_slot_raw(
+ list,
+ mid,
+ 0xF,
+ condition,
+ (uint32_t)bd,
+ 0,
+ 0);
+}
+
+int gr1553bc_slot_transfer(
+ struct gr1553bc_list *list,
+ int mid,
+ int options,
+ int tt,
+ uint16_t *dptr)
+{
+ uint32_t set0, set1;
+ union gr1553bc_bd *bd;
+ int rx_rtadr, tx_rtadr, timeout;
+
+ /* Get BD address */
+ bd = gr1553bc_slot_bd(list, mid);
+ if ( bd == NULL )
+ return -1;
+
+ /* Translate Data pointer from CPU-local to 1553-core accessible
+ * address if user wants that. This may be useful for AMBA-over-PCI
+ * cores.
+ */
+ if ( (unsigned int)dptr & 0x1 ) {
+ struct gr1553bc_priv *bcpriv = list->bc;
+
+ drvmgr_translate(
+ *bcpriv->pdev,
+ CPUMEM_TO_DMA,
+ (void *)((unsigned int)dptr & ~0x1),
+ (void **)&dptr);
+ }
+
+ /* It is assumed that the descriptor has already been initialized
+ * as a empty slot (Dummy bit set), so to avoid races the dummy
+ * bit is cleared last.
+ *
+ * If we knew that the write would do a burst (for example over SpW)
+ * it would be safe to write in order.
+ */
+
+ /* Preserve timeslot */
+ set0 = GR1553BC_READ_MEM(&bd->tr.settings[0]);
+ set0 &= GR1553BC_TR_TIME;
+ set0 |= options & 0x61f00000;
+ set0 |= list->tropts; /* Global options */
+
+ /* Set transfer type, bus and let RT tolerance table descide
+ * responce tolerance.
+ *
+ * If a destination address is specified the longest timeout
+ * tolerance is taken.
+ */
+ rx_rtadr = (tt >> 22) & 0x1f;
+ tx_rtadr = (tt >> 12) & 0x1f;
+ if ( (tx_rtadr != 0x1f) &&
+ (list->rt_timeout[rx_rtadr] < list->rt_timeout[tx_rtadr]) ) {
+ timeout = list->rt_timeout[tx_rtadr];
+ } else {
+ timeout = list->rt_timeout[rx_rtadr];
+ }
+ set1 = ((timeout & 0xf) << 27) | (tt & 0x27ffffff) | ((options & 0x3)<<30);
+
+ GR1553BC_WRITE_MEM(&bd->tr.settings[0], set0);
+ GR1553BC_WRITE_MEM(&bd->tr.dptr, (uint32_t)dptr);
+ /* Write UNUSED BIT, when cleared it Indicates that BC has written it */
+ GR1553BC_WRITE_MEM(&bd->tr.status, 0x80000000);
+ GR1553BC_WRITE_MEM(&bd->tr.settings[1], set1);
+
+ return 0;
+}
+
+int gr1553bc_slot_update
+ (
+ struct gr1553bc_list *list,
+ int mid,
+ uint16_t *dptr,
+ unsigned int *stat
+ )
+{
+ union gr1553bc_bd *bd;
+ unsigned int status;
+ unsigned int dataptr = (unsigned int)dptr;
+
+ /* Get BD address */
+ bd = gr1553bc_slot_bd(list, mid);
+ if ( bd == NULL )
+ return -1;
+
+ /* Write new Data Pointer if needed */
+ if ( dataptr ) {
+ struct gr1553bc_priv *bcpriv = list->bc;
+
+ /* Translate Data pointer from CPU-local to 1553-core accessible
+ * address if user wants that. This may be useful for AMBA-over-PCI
+ * cores.
+ */
+ if ( dataptr & 0x1 ) {
+ drvmgr_translate(
+ *bcpriv->pdev,
+ CPUMEM_TO_DMA,
+ (void *)(dataptr & ~0x1),
+ (void **)&dptr
+ );
+ }
+
+ /* Update Data Pointer */
+ GR1553BC_WRITE_MEM(&bd->tr.dptr, dataptr);
+ }
+
+ /* Get status of transfer descriptor */
+ if ( stat ) {
+ status = *stat;
+ *stat = GR1553BC_READ_MEM(&bd->tr.status);
+ if ( status ) {
+ /* Clear status fields user selects, then
+ * or bit31 if user wants that. The bit31
+ * may be used to indicate if the BC has
+ * performed the access.
+ */
+ status = (*stat & (status & 0xffffff)) |
+ (status & (1<<31));
+ GR1553BC_WRITE_MEM(&bd->tr.status, status);
+ }
+ }
+
+ return 0;
+}
+
+int gr1553bc_slot_dummy(
+ struct gr1553bc_list *list,
+ int mid,
+ unsigned int *dummy)
+{
+ union gr1553bc_bd *bd;
+ unsigned int set1, new_set1;
+
+ /* Get BD address */
+ bd = gr1553bc_slot_bd(list, mid);
+ if ( bd == NULL )
+ return -1;
+ /* Update the Dummy Bit */
+ set1 = GR1553BC_READ_MEM(&bd->tr.settings[1]);
+ new_set1 = (set1 & ~GR1553BC_TR_DUMMY_1) | (*dummy & GR1553BC_TR_DUMMY_1);
+ GR1553BC_WRITE_MEM(&bd->tr.settings[1], new_set1);
+
+ *dummy = set1;
+
+ return 0;
+}
+
+/* Find MID from Descriptor pointer */
+int gr1553bc_mid_from_bd(
+ union gr1553bc_bd *bd,
+ int *mid,
+ int *async
+ )
+{
+ int i, bdmid, slot_no;
+ uint32_t word0, word2;
+
+ /* Find Jump to next Minor Frame or End-Of-List,
+ * at those locations we have stored a MID
+ *
+ * GR1553BC_SLOT_MAX+2 = Worst case, BD is max distance from jump
+ * descriptor. 2=END and Jump descriptors.
+ */
+ for (i=0; i<GR1553BC_SLOT_MAX+2; i++) {
+ word0 = GR1553BC_READ_MEM(&bd->raw.words[0]);
+ if ( word0 & GR1553BC_BD_TYPE ) {
+ if ( word0 == GR1553BC_UNCOND_JMP ) {
+ /* May be a unconditional IRQ set by user. In
+ * that case the function is stored in WORD3,
+ * functions must be aligned to 4 byte boudary.
+ */
+ word2 = GR1553BC_READ_MEM(&bd->raw.words[2]);
+ if ( word2 & NEXT_MINOR_MARKER ) {
+ goto found_mid;
+ }
+ } else if ( word0 == GR1553BC_TR_EOL ) {
+ /* End-Of-List, does contain a MID */
+ word2 = GR1553BC_READ_MEM(&bd->raw.words[2]);
+ goto found_mid;
+ }
+ }
+ bd++;
+ }
+
+ return -1;
+
+found_mid:
+ /* Get MID of JUMP descriptor */
+ bdmid = word2 >> 8;
+ /* Subtract distance from JUMP descriptor to find MID
+ * of requested BD.
+ */
+ slot_no = GR1553BC_SLOTID_FROM_ID(bdmid);
+ slot_no -= i;
+ bdmid = GR1553BC_ID_SET_SLOT(bdmid, slot_no);
+
+ if ( mid )
+ *mid = bdmid;
+
+ /* Determine which list BD belongs to: async or sync */
+ if ( async )
+ *async = word2 & NEXT_MINOR_MARKER_ASYNC;
+
+ return 0;
+}
+
+/*************** END OF LIST HANDLING ROUTINES ***************/
+
+/*************** DEVICE HANDLING ROUTINES ***************/
+
+void gr1553bc_device_init(struct gr1553bc_priv *priv);
+void gr1553bc_device_uninit(struct gr1553bc_priv *priv);
+void gr1553bc_isr(void *data);
+
+/*** GR1553BC driver ***/
+
+void gr1553bc_register(void)
+{
+ /* The BC driver rely on the GR1553B Driver */
+ gr1553_register();
+}
+
+void gr1553bc_isr_std(union gr1553bc_bd *bd, void *data)
+{
+ /* Do nothing */
+}
+
+/* Take a GR1553BC hardware device identified by minor.
+ * A pointer is returned that is used internally by the GR1553BC
+ * driver, it is used as an input paramter 'bc' to all other
+ * functions that manipulate the hardware.
+ */
+void *gr1553bc_open(int minor)
+{
+ struct drvmgr_dev **pdev = NULL;
+ struct gr1553bc_priv *priv = NULL;
+ struct amba_dev_info *ambadev;
+ struct ambapp_core *pnpinfo;
+ void *irq_log_p = NULL;
+
+ /* Allocate requested device */
+ pdev = gr1553_bc_open(minor);
+ if ( pdev == NULL )
+ goto fail;
+
+ irq_log_p = malloc(GR1553BC_IRQLOG_SIZE*2);
+ if ( irq_log_p == NULL )
+ goto fail;
+
+ priv = malloc(sizeof(struct gr1553bc_priv));
+ if ( priv == NULL )
+ goto fail;
+ memset(priv, 0, sizeof(struct gr1553bc_priv));
+
+ /* Init BC device */
+ priv->pdev = pdev;
+ (*pdev)->priv = priv;
+ priv->irq_log_p = irq_log_p;
+ priv->started = 0;
+
+ /* Get device information from AMBA PnP information */
+ ambadev = (struct amba_dev_info *)(*pdev)->businfo;
+ pnpinfo = &ambadev->info;
+ priv->regs = (struct gr1553b_regs *)pnpinfo->apb_slv->start;
+
+ gr1553bc_device_init(priv);
+
+ /* Register ISR handler (unmask at IRQ controller) */
+ if ( drvmgr_interrupt_register(*priv->pdev, 0, "gr1553bc",
+ gr1553bc_isr, priv) ) {
+ goto fail;
+ }
+
+ return priv;
+
+fail:
+ if ( pdev )
+ gr1553_bc_close(pdev);
+ if ( irq_log_p )
+ free(irq_log_p);
+ if ( priv )
+ free(priv);
+ return NULL;
+}
+
+void gr1553bc_close(void *bc)
+{
+ struct gr1553bc_priv *priv = bc;
+
+ /* Stop Hardware */
+ gr1553bc_stop(bc, 0x3);
+
+ gr1553bc_device_uninit(priv);
+
+ /* Remove interrupt handler (mask IRQ at IRQ controller) */
+ drvmgr_interrupt_unregister(*priv->pdev, 0, gr1553bc_isr, priv);
+
+ /* Free device */
+ gr1553_bc_close(priv->pdev);
+ free(priv->irq_log_p);
+ free(priv);
+}
+
+/* Return Current Minor frame number */
+int gr1553bc_indication(void *bc, int async, int *mid)
+{
+ struct gr1553bc_priv *priv = bc;
+ union gr1553bc_bd *bd;
+
+ /* Get current descriptor pointer */
+ if ( async ) {
+ bd = (union gr1553bc_bd *)
+ GR1553BC_READ_REG(&priv->regs->bc_aslot);
+ bd = gr1553bc_bd_hw2cpu(priv->alist, bd);
+ } else {
+ bd = (union gr1553bc_bd *)
+ GR1553BC_READ_REG(&priv->regs->bc_slot);
+ bd = gr1553bc_bd_hw2cpu(priv->list, bd);
+ }
+
+ return gr1553bc_mid_from_bd(bd, mid, NULL);
+}
+
+/* Start major frame processing, wait for TimerManager tick or start directly */
+int gr1553bc_start(void *bc, struct gr1553bc_list *list, struct gr1553bc_list *list_async)
+{
+ struct gr1553bc_priv *priv = bc;
+ union gr1553bc_bd *bd = NULL, *bd_async = NULL;
+ uint32_t ctrl, irqmask;
+ IRQ_GLOBAL_PREPARE(oldLevel);
+
+ if ( (list == NULL) && (list_async == NULL) )
+ return 0;
+
+ /* Find first descriptor in list, the descriptor
+ * first to be executed.
+ */
+ ctrl = GR1553BC_KEY;
+ if ( list ) {
+ bd = gr1553bc_slot_bd(list, GR1553BC_ID(0,0,0));
+ if ( bd == NULL )
+ return -1;
+ bd = gr1553bc_bd_cpu2hw(list, bd);
+ ctrl |= GR1553B_BC_ACT_SCSRT;
+ }
+ if ( list_async ) {
+ bd_async = gr1553bc_slot_bd(list_async, GR1553BC_ID(0,0,0));
+ if ( bd_async == NULL )
+ return -1;
+ bd_async = gr1553bc_bd_cpu2hw(list_async, bd_async);
+ ctrl |= GR1553B_BC_ACT_ASSRT;
+ }
+
+ /* Do "hot-swapping" of lists */
+ IRQ_GLOBAL_DISABLE(oldLevel);
+ if ( list ) {
+ priv->list = list;
+ GR1553BC_WRITE_REG(&priv->regs->bc_bd, (uint32_t)bd);
+ }
+ if ( list_async ) {
+ priv->alist = list_async;
+ GR1553BC_WRITE_REG(&priv->regs->bc_abd, (uint32_t)bd_async);
+ }
+
+ /* If not enabled before, we enable it now. */
+ GR1553BC_WRITE_REG(&priv->regs->bc_ctrl, ctrl);
+
+ /* Enable IRQ */
+ if ( priv->started == 0 ) {
+ priv->started = 1;
+ irqmask = GR1553BC_READ_REG(&priv->regs->imask);
+ irqmask |= GR1553B_IRQEN_BCEVE|GR1553B_IRQEN_BCDE|GR1553B_IRQEN_BCWKE;
+ GR1553BC_WRITE_REG(&priv->regs->imask, irqmask);
+ }
+
+ IRQ_GLOBAL_ENABLE(oldLevel);
+
+ return 0;
+}
+
+/* Pause GR1553 BC transfers */
+int gr1553bc_pause(void *bc)
+{
+ struct gr1553bc_priv *priv = bc;
+ uint32_t ctrl;
+ IRQ_GLOBAL_PREPARE(oldLevel);
+
+ /* Do "hot-swapping" of lists */
+ IRQ_GLOBAL_DISABLE(oldLevel);
+ ctrl = GR1553BC_KEY | GR1553B_BC_ACT_SCSUS;
+ GR1553BC_WRITE_REG(&priv->regs->bc_ctrl, ctrl);
+ IRQ_GLOBAL_ENABLE(oldLevel);
+
+ return 0;
+}
+
+/* Restart GR1553 BC transfers, after being paused */
+int gr1553bc_restart(void *bc)
+{
+ struct gr1553bc_priv *priv = bc;
+ uint32_t ctrl;
+ IRQ_GLOBAL_PREPARE(oldLevel);
+
+ IRQ_GLOBAL_DISABLE(oldLevel);
+ ctrl = GR1553BC_KEY | GR1553B_BC_ACT_SCSRT;
+ GR1553BC_WRITE_REG(&priv->regs->bc_ctrl, ctrl);
+ IRQ_GLOBAL_ENABLE(oldLevel);
+
+ return 0;
+}
+
+/* Stop BC transmission */
+int gr1553bc_stop(void *bc, int options)
+{
+ struct gr1553bc_priv *priv = bc;
+ uint32_t ctrl;
+ IRQ_GLOBAL_PREPARE(oldLevel);
+
+ ctrl = GR1553BC_KEY;
+ if ( options & 0x1 )
+ ctrl |= GR1553B_BC_ACT_SCSTP;
+ if ( options & 0x2 )
+ ctrl |= GR1553B_BC_ACT_ASSTP;
+
+ IRQ_GLOBAL_DISABLE(oldLevel);
+ GR1553BC_WRITE_REG(&priv->regs->bc_ctrl, ctrl);
+ priv->started = 0;
+ IRQ_GLOBAL_ENABLE(oldLevel);
+
+ return 0;
+}
+
+/* Reset software and BC hardware into a known "unused/init" state */
+void gr1553bc_device_init(struct gr1553bc_priv *priv)
+{
+/* RESET HARDWARE REGISTERS */
+ /* Stop BC if not already stopped */
+ GR1553BC_WRITE_REG(&priv->regs->bc_ctrl, GR1553BC_KEY | 0x0204);
+
+ /* Since RT can not be used at the same time as BC, we stop
+ * RT rx, it should already be stopped...
+ */
+ GR1553BC_WRITE_REG(&priv->regs->rt_cfg, GR1553RT_KEY);
+
+ /* Clear some registers */
+ GR1553BC_WRITE_REG(&priv->regs->bc_bd, 0);
+ GR1553BC_WRITE_REG(&priv->regs->bc_abd, 0);
+ GR1553BC_WRITE_REG(&priv->regs->bc_timer, 0);
+ GR1553BC_WRITE_REG(&priv->regs->bc_wake, 0);
+ GR1553BC_WRITE_REG(&priv->regs->bc_irqptr, 0);
+ GR1553BC_WRITE_REG(&priv->regs->bc_busmsk, 0);
+
+/* PUT SOFTWARE INTO INITIAL STATE */
+ priv->list = NULL;
+ priv->alist = NULL;
+
+ priv->irq_log_base = (uint32_t *)
+ (((uint32_t)priv->irq_log_p + (GR1553BC_IRQLOG_SIZE-1)) &
+ ~(GR1553BC_IRQLOG_SIZE-1));
+ /* Translate into a hardware accessible address */
+ drvmgr_translate_check(
+ *priv->pdev,
+ CPUMEM_TO_DMA,
+ (void *)priv->irq_log_base,
+ (void **)&priv->irq_log_base_hw,
+ GR1553BC_IRQLOG_SIZE);
+ priv->irq_log_curr = priv->irq_log_base;
+ priv->irq_log_end = &priv->irq_log_base[GR1553BC_IRQLOG_CNT-1];
+ priv->irq_func = gr1553bc_isr_std;
+ priv->irq_data = NULL;
+
+ GR1553BC_WRITE_REG(&priv->regs->bc_irqptr,(uint32_t)priv->irq_log_base_hw);
+}
+
+void gr1553bc_device_uninit(struct gr1553bc_priv *priv)
+{
+ uint32_t irqmask;
+
+ /* Stop BC if not already stopped */
+ GR1553BC_WRITE_REG(&priv->regs->bc_ctrl, GR1553BC_KEY | 0x0204);
+
+ /* Since RT can not be used at the same time as BC, we stop
+ * RT rx, it should already be stopped...
+ */
+ GR1553BC_WRITE_REG(&priv->regs->rt_cfg, GR1553RT_KEY);
+
+ /* Turn off IRQ generation */
+ irqmask=GR1553BC_READ_REG(&priv->regs->imask);
+ irqmask&=~(GR1553B_IRQEN_BCEVE|GR1553B_IRQEN_BCDE|GR1553B_IRQEN_BCWKE);
+ GR1553BC_WRITE_REG(&priv->regs->irq, irqmask);
+}
+
+/* Interrupt handler */
+void gr1553bc_isr(void *arg)
+{
+ struct gr1553bc_priv *priv = arg;
+ uint32_t *curr, *pos, word0, word2;
+ union gr1553bc_bd *bd;
+ bcirq_func_t func;
+ void *data;
+ int handled, irq;
+
+ /* Did core make IRQ */
+ irq = GR1553BC_READ_REG(&priv->regs->irq);
+ irq &= (GR1553B_IRQEN_BCEVE|GR1553B_IRQEN_BCDE|GR1553B_IRQEN_BCWKE);
+ if ( irq == 0 )
+ return; /* Shared IRQ: some one else may have caused the IRQ */
+
+ /* Clear handled IRQs */
+ GR1553BC_WRITE_REG(&priv->regs->irq, irq);
+
+ /* DMA error. This IRQ does not affect the IRQ log.
+ * We let standard IRQ handle handle it.
+ */
+ if ( irq & GR1553B_IRQEN_BCDE ) {
+ priv->irq_func(NULL, priv->irq_data);
+ }
+
+ /* Get current posistion in hardware */
+ pos = (uint32_t *)GR1553BC_READ_REG(&priv->regs->bc_irqptr);
+ /* Convertin into CPU address */
+ pos = priv->irq_log_base +
+ ((unsigned int)pos - (unsigned int)priv->irq_log_base_hw)/4;
+
+ /* Step in IRQ log until we reach the end. */
+ handled = 0;
+ curr = priv->irq_log_curr;
+ while ( curr != pos ) {
+ bd = (union gr1553bc_bd *)(GR1553BC_READ_MEM(curr) & ~1);
+ GR1553BC_WRITE_MEM(curr, 0x2); /* Mark Handled */
+
+ /* Convert Descriptor in IRQ log into CPU address. In order
+ * to convert we must know which list the descriptor belongs
+ * to, we compare the address of the bd to the ASYNC list
+ * descriptor table area.
+ */
+ if ( priv->alist && ((unsigned int)bd>=priv->alist->table_hw) &&
+ ((unsigned int)bd <
+ (priv->alist->table_hw + priv->alist->table_size))) {
+ /* BD in async list */
+ bd = gr1553bc_bd_hw2cpu(priv->alist, bd);
+ } else {
+ /* BD in sync list */
+ bd = gr1553bc_bd_hw2cpu(priv->list, bd);
+ }
+
+ /* Handle Descriptor that cased IRQ
+ *
+ * If someone have inserted an IRQ descriptor and tied
+ * that to a custom function we call that function, otherwise
+ * we let the standard IRQ handle handle it.
+ */
+ word0 = GR1553BC_READ_MEM(&bd->raw.words[0]);
+ if ( word0 == GR1553BC_UNCOND_IRQ ) {
+ word2 = GR1553BC_READ_MEM(&bd->raw.words[2]);
+ if ( (word2 & 0x3) == 0 ) {
+ func = (bcirq_func_t)(word2 & ~0x3);
+ data = (void *)
+ GR1553BC_READ_MEM(&bd->raw.words[3]);
+ func(bd, data);
+ handled = 1;
+ }
+ }
+
+ if ( handled == 0 ) {
+ /* Let standard IRQ handle handle it */
+ priv->irq_func(bd, priv->irq_data);
+ } else {
+ handled = 0;
+ }
+
+ /* Increment to next entry in IRQ LOG */
+ if ( curr == priv->irq_log_end )
+ curr = priv->irq_log_base;
+ else
+ curr++;
+ }
+ priv->irq_log_curr = curr;
+}
+
+int gr1553bc_irq_setup
+ (
+ void *bc,
+ bcirq_func_t func,
+ void *data
+ )
+{
+ struct gr1553bc_priv *priv = bc;
+
+ if ( func == NULL )
+ priv->irq_func = gr1553bc_isr_std;
+ else
+ priv->irq_func = func;
+ priv->irq_data = data;
+
+ return 0;
+}
+
+void gr1553bc_ext_trig(void *bc, int trig)
+{
+ struct gr1553bc_priv *priv = bc;
+ unsigned int trigger;
+
+ if ( trig )
+ trigger = GR1553B_BC_ACT_SETT;
+ else
+ trigger = GR1553B_BC_ACT_CLRT;
+
+ GR1553BC_WRITE_REG(&priv->regs->bc_ctrl, GR1553BC_KEY | trigger);
+}
+
+void gr1553bc_status(void *bc, struct gr1553bc_status *status)
+{
+ struct gr1553bc_priv *priv = bc;
+
+ status->status = GR1553BC_READ_REG(&priv->regs->bc_stat);
+ status->time = GR1553BC_READ_REG(&priv->regs->bc_timer);
+}
+
+/*** DEBUGGING HELP FUNCTIONS ***/
+
+#include <stdio.h>
+
+void gr1553bc_show_list(struct gr1553bc_list *list, int options)
+{
+ struct gr1553bc_major *major;
+ struct gr1553bc_minor *minor;
+ int i, j, minor_cnt, timefree;
+
+ printf("LIST\n");
+ printf(" major cnt: %d\n", list->major_cnt);
+ for (i=0; i<32; i++) {
+ printf(" RT[%d] timeout: %d\n", i, 14+(list->rt_timeout[i]*4));
+ }
+
+ for (i=0; i<list->major_cnt; i++) {
+ major = list->majors[i];
+ minor_cnt = major->cfg->minor_cnt;
+ printf(" MAJOR[%d]\n", i);
+ printf(" minor count: %d\n", minor_cnt);
+
+ for (j=0; j<minor_cnt; j++) {
+ minor = major->minors[j];
+
+ printf(" MINOR[%d]\n", j);
+ printf(" bd: 0x%08x (HW:0x%08x)\n",
+ (unsigned int)&minor->bds[0],
+ (unsigned int)gr1553bc_bd_cpu2hw(list,
+ &minor->bds[0]));
+ printf(" slot cnt: %d\n", minor->cfg->slot_cnt);
+ if ( minor->cfg->timeslot ) {
+ timefree = gr1553bc_minor_freetime(minor);
+ printf(" timefree: %d\n", timefree);
+ printf(" timetotal: %d\n",
+ minor->cfg->timeslot);
+ } else {
+ printf(" no time mgr\n");
+ }
+ }
+ }
+}