/* 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.org/license/LICENSE. */ #include #include #include #include #include #include #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; imajor_cnt; i++) { major = list->majors[i]; minor_cnt = major->cfg->minor_cnt; for (j=0; jminors[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; imajor_cnt; i++) { major = list->majors[i]; minor_cnt = major->cfg->minor_cnt; for (j=0; jminors[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; imajor_cnt; i++) { major = list->majors[i]; minor_cnt = major->cfg->minor_cnt; for (j=0; jminors[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; kslot_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; iminor_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]; } static 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<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<alloc & (1<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<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; iraw.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(); } static 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 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; imajor_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; jminors[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"); } } } }