diff options
Diffstat (limited to 'c/src/lib/libbsp/sparc/shared/1553/gr1553rt.c')
-rw-r--r-- | c/src/lib/libbsp/sparc/shared/1553/gr1553rt.c | 1256 |
1 files changed, 1256 insertions, 0 deletions
diff --git a/c/src/lib/libbsp/sparc/shared/1553/gr1553rt.c b/c/src/lib/libbsp/sparc/shared/1553/gr1553rt.c new file mode 100644 index 0000000000..ff05ce5d73 --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/1553/gr1553rt.c @@ -0,0 +1,1256 @@ +/* GR1553B RT 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 <rtems.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include <gr1553b.h> +#include <gr1553rt.h> + +#include <drvmgr/drvmgr.h> +#include <drvmgr/ambapp_bus.h> + +#define GR1553RT_WRITE_MEM(adr, val) *(volatile uint32_t *)(adr) = (val) +#define GR1553RT_READ_MEM(adr) (*(volatile uint32_t *)(adr)) + +#define GR1553RT_WRITE_REG(adr, val) *(volatile uint32_t *)(adr) = (val) +#define GR1553RT_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 + +/* Software representation of one hardware descriptor */ +struct gr1553rt_sw_bd { + unsigned short this_next;/* Next entry or this entry. 0xffff: no next */ + unsigned char listid; /* ListID of List the descriptor is attached */ + char unused; +} __attribute__((packed)); + +/* Software description of a subaddress */ +struct gr1553rt_subadr { + /* RX LIST */ + unsigned char rxlistid; + /* TX LIST */ + unsigned char txlistid; +}; + +struct gr1553rt_irqerr { + gr1553rt_irqerr_t func; + void *data; +}; + +struct gr1553rt_irqmc { + gr1553rt_irqmc_t func; + void *data; +}; + +struct gr1553rt_irq { + gr1553rt_irq_t func; + void *data; +}; + +struct gr1553rt_priv { + /* Pointer to Hardware registers */ + struct gr1553b_regs *regs; + + /* Software State */ + int started; + struct gr1553rt_cfg cfg; + + /* Handle to GR1553B RT device layer */ + struct drvmgr_dev **pdev; + + /* Each Index represents one RT Subaddress. 31 = Broadcast */ + struct gr1553rt_subadr subadrs[32]; + + /* Pointer to array of Software's description of a hardware + * descriptor. + */ +#if (RTBD_MAX == 0) + struct gr1553rt_sw_bd *swbds; +#else + struct gr1553rt_sw_bd swbds[RTBD_MAX]; +#endif + + /* List of Free descriptors */ + unsigned short swbd_free; + int swbd_free_cnt; + + /* Hardware SubAddress descriptors given for CPU and Hardware */ + void *satab_buffer; + struct gr1553rt_sa *sas_cpu; /* Translated for CPU */ + struct gr1553rt_sa *sas_hw; /* Translated for Hardware */ + + /* Hardware descriptors address given for CPU and hardware */ + void *bd_buffer; + int bds_cnt; /* Number of descriptors */ + struct gr1553rt_bd *bds_cpu; /* Translated for CPU */ + struct gr1553rt_bd *bds_hw; /* Translated for Hardware */ + + + /* Event Log buffer in */ + void *evlog_buffer; + unsigned int *evlog_cpu_next; /* Next LOG entry to be handled */ + unsigned int *evlog_cpu_base; /* First Entry in LOG */ + unsigned int *evlog_cpu_end; /* Last+1 Entry in LOG */ + unsigned int *evlog_hw_base; /* Translated for Hardware */ + + /* Each Index represents a LIST ID */ + struct gr1553rt_list *lists[RTLISTID_MAX]; + + /* IRQ handlers, one per SUBADDRESS */ + struct gr1553rt_irq irq_rx[32]; + struct gr1553rt_irq irq_tx[32]; + + /* ISR called when an ERROR IRQ is received */ + struct gr1553rt_irqerr irq_err; + + /* ISR called when an Mode Code is received */ + struct gr1553rt_irqmc irq_mc; +}; + +void gr1553rt_sw_init(struct gr1553rt_priv *priv); +void gr1553rt_sw_free(struct gr1553rt_priv *priv); +int gr1553rt_sw_alloc(struct gr1553rt_priv *priv); + +/* Assign and ID to the list. An LIST ID is needed before scheduling list + * on an RT subaddress. + * + * Only 64 lists can be registered at a time on the same device. + */ +int gr1553rt_list_reg(struct gr1553rt_list *list) +{ + struct gr1553rt_priv *priv = list->rt; + int i; + + /* Find first free list ID */ + for ( i=0; i<RTLISTID_MAX; i++) { + if ( priv->lists[i] == NULL ) { + priv->lists[i] = list; + list->listid = i; + return i; + } + } + + /* No available LIST IDs */ + list->listid = -1; + + return -1; +} + +/* Unregister List from device */ +void gr1553rt_list_unreg(struct gr1553rt_list *list) +{ + struct gr1553rt_priv *priv = list->rt; + + priv->lists[list->listid] = NULL; + list->listid = -1; +} + +static int gr1553rt_bdid(void *rt, struct gr1553rt_sw_bd *bd) +{ + struct gr1553rt_priv *priv = rt; + + unsigned short index; + + /* Get Index of Software BD */ + index = ((unsigned int)bd - (unsigned int)&priv->swbds[0]) / + sizeof(struct gr1553rt_sw_bd); + + return index; +} + +void gr1553rt_bd_alloc_init(void *rt, int count) +{ + struct gr1553rt_priv *priv = rt; + int i; + + for (i=0; i<count-1; i++) { + priv->swbds[i].this_next = i+1; + } + priv->swbds[count-1].this_next = 0xffff; + priv->swbd_free = 0; + priv->swbd_free_cnt = count; +} + +/* Allocate a Chain of descriptors */ +int gr1553rt_bd_alloc(void *rt, struct gr1553rt_sw_bd **bd, int cnt) +{ + struct gr1553rt_priv *priv = rt; + struct gr1553rt_sw_bd *curr; + int i; + + if ((priv->swbd_free_cnt < cnt) || (cnt <= 0)) { + *bd = NULL; + return -1; + } + + *bd = &priv->swbds[priv->swbd_free]; + for (i=0; i<cnt; i++) { + if ( i == 0) { + curr = &priv->swbds[priv->swbd_free]; + } else { + curr = &priv->swbds[curr->this_next]; + } + if ( curr->this_next == 0xffff ) { + *bd = NULL; + return -1; + } + } + priv->swbd_free = curr->this_next; + priv->swbd_free_cnt -= cnt; + curr->this_next = 0xffff; /* Mark end of chain on last entry */ + + return 0; +} + +void gr1553rt_bd_free(void *rt, struct gr1553rt_sw_bd *bd) +{ + struct gr1553rt_priv *priv = rt; + unsigned short index; + + /* Get Index of Software BD */ + index = gr1553rt_bdid(priv, bd); + + /* Insert first in list */ + bd->this_next = priv->swbd_free; + priv->swbd_free = index; + priv->swbd_free_cnt++; +} + +int gr1553rt_list_init + ( + void *rt, + struct gr1553rt_list **plist, + struct gr1553rt_list_cfg *cfg + ) +{ + struct gr1553rt_priv *priv = rt; + int i, size; + struct gr1553rt_sw_bd *swbd; + unsigned short index; + struct gr1553rt_list *list; + + /* The user may provide a pre allocated LIST, or + * let the driver handle allocation by using malloc() + * + * If the IN/OUT plist argument points to NULL a list + * dynamically allocated here. + */ + list = *plist; + if ( list == NULL ) { + /* Dynamically allocate LIST */ + size = offsetof(struct gr1553rt_list, bds) + + (cfg->bd_cnt * sizeof(unsigned short)); + list = (struct gr1553rt_list *)malloc(size); + if ( list == NULL ) + return -1; + *plist = list; + } + + list->rt = rt; + list->subadr = -1; + list->listid = gr1553rt_list_reg(list); + if ( list->listid == -1 ) + return -2; /* Too many lists */ + list->cfg = cfg; + list->bd_cnt = cfg->bd_cnt; + + /* Allocate all BDs needed by list */ + if ( gr1553rt_bd_alloc(rt, &swbd, list->bd_cnt) ) { + return -3; /* Too few descriptors */ + } + + /* Get ID/INDEX of Software BDs */ + index = gr1553rt_bdid(rt, swbd); + list->bds[0] = index; + for (i=1; i<list->bd_cnt; i++) { + list->bds[i] = priv->swbds[list->bds[i-1]].this_next; + } + + /* Now that the next pointer has fullfilled it's job and not + * needed anymore, we use it as list entry pointer instead. + * The this_next pointer is a list entry number. + */ + for (i=0; i<list->bd_cnt; i++) { + priv->swbds[list->bds[i]].this_next = i; + } + + return 0; +} + +int gr1553rt_bd_init( + struct gr1553rt_list *list, + unsigned short entry_no, + unsigned int flags, + uint16_t *dptr, + unsigned short next + ) +{ + struct gr1553rt_priv *priv; + unsigned short bdid; + struct gr1553rt_bd *bd; + unsigned int nextbd, dataptr; + IRQ_GLOBAL_PREPARE(oldLevel); + + if ( entry_no >= list->bd_cnt ) + return -1; + + /* Find Descriptor */ + bdid = list->bds[entry_no]; + priv = list->rt; + bd = &priv->bds_cpu[bdid]; + + if ( next == 0xfffe ) { + next = entry_no + 1; + if ( next >= list->bd_cnt ) + next = 0; + } + + /* Find next descriptor in address space that the + * Hardware understand. + */ + if ( next >= 0xffff ) { + nextbd = 0x3; /* End of list */ + } else if ( next >= list->bd_cnt ) { + return -1; + } else { + bdid = list->bds[next]; + nextbd = (unsigned int)&priv->bds_hw[bdid]; + } + + dataptr = (unsigned int)dptr; + if ( dataptr & 1 ) { + /* Translate address from CPU-local into remote */ + dataptr &= ~1; + drvmgr_translate( + *priv->pdev, + CPUMEM_TO_DMA, + (void *)dataptr, + (void **)&dataptr + ); + } + + /* Get current status, and clear */ + IRQ_GLOBAL_DISABLE(oldLevel); + bd->ctrl = flags & GR1553RT_BD_FLAGS_IRQEN; + bd->dptr = (unsigned int)dptr; + bd->next = nextbd; + IRQ_GLOBAL_ENABLE(oldLevel); + + return 0; +} + +int gr1553rt_bd_update( + struct gr1553rt_list *list, + int entry_no, + unsigned int *status, + uint16_t **dptr + ) +{ + struct gr1553rt_priv *priv; + unsigned short bdid; + struct gr1553rt_bd *bd; + unsigned int tmp, dataptr; + IRQ_GLOBAL_PREPARE(oldLevel); + + if ( entry_no >= list->bd_cnt ) + return -1; + + /* Find Descriptor */ + bdid = list->bds[entry_no]; + priv = list->rt; + bd = &priv->bds_cpu[bdid]; + + /* Prepare translation if needed */ + if ( dptr && (dataptr=(unsigned int)*dptr) ) { + if ( dataptr & 1 ) { + /* Translate address from CPU-local into remote. May + * be used when RT core is accessed over the PCI bus. + */ + dataptr &= ~1; + drvmgr_translate( + *priv->pdev, + CPUMEM_TO_DMA, + (void *)dataptr, + (void **)&dataptr + ); + } + } + + /* Get current status, and clear */ + IRQ_GLOBAL_DISABLE(oldLevel); + /* READ/WRITE Status/Control word */ + if ( status ) { + tmp = bd->ctrl; + if ( *status ) { + bd->ctrl = *status; + } + *status = tmp; + } + /* READ/WRITE Data-Pointer word */ + if ( dptr ) { + tmp = bd->dptr; + if ( dataptr ) { + bd->dptr = dataptr; + } + *dptr = (uint16_t *)tmp; + } + IRQ_GLOBAL_ENABLE(oldLevel); + + return 0; +} + +int gr1553rt_irq_err + ( + void *rt, + gr1553rt_irqerr_t func, + void *data + ) +{ + struct gr1553rt_priv *priv = rt; + IRQ_GLOBAL_PREPARE(oldLevel); + + IRQ_GLOBAL_DISABLE(oldLevel); + priv->irq_err.func = func; + priv->irq_err.data = data; + IRQ_GLOBAL_ENABLE(oldLevel); + + return 0; +} + +int gr1553rt_irq_mc + ( + void *rt, + gr1553rt_irqmc_t func, + void *data + ) +{ + struct gr1553rt_priv *priv = rt; + IRQ_GLOBAL_PREPARE(oldLevel); + + IRQ_GLOBAL_DISABLE(oldLevel); + priv->irq_mc.func = func; + priv->irq_mc.data = data; + IRQ_GLOBAL_ENABLE(oldLevel); + + return 0; +} + +int gr1553rt_irq_sa + ( + void *rt, + int subadr, + int tx, + gr1553rt_irq_t func, + void *data + ) +{ + struct gr1553rt_priv *priv = rt; + IRQ_GLOBAL_PREPARE(oldLevel); + + IRQ_GLOBAL_DISABLE(oldLevel); + if ( tx ) { + priv->irq_tx[subadr].func = func; + priv->irq_tx[subadr].data = data; + } else { + priv->irq_rx[subadr].func = func; + priv->irq_rx[subadr].data = data; + } + IRQ_GLOBAL_ENABLE(oldLevel); + + return 0; +} + +/* GR1553-RT Interrupt Service Routine */ +void gr1553rt_isr(void *data) +{ + struct gr1553rt_priv *priv = data; + unsigned int firstirq, lastpos; + int index; + unsigned int *last, *curr, entry, hwbd; + int type, samc, mcode, subadr; + int listid; + struct gr1553rt_irq *isr; + struct gr1553rt_irqerr *isrerr; + struct gr1553rt_irqmc *isrmc; + unsigned int irq; + + /* Ack IRQ before reading current write pointer, but after + * reading current IRQ pointer. This is because RT_EVIRQ + * may be updated after we ACK the IRQ source. + */ + irq = priv->regs->irq & + (GR1553B_IRQ_RTTE|GR1553B_IRQ_RTD|GR1553B_IRQ_RTEV); + if ( irq == 0 ) + return; + + firstirq = priv->regs->rt_evirq; + priv->regs->irq = irq; + lastpos = priv->regs->rt_evlog; + + /* Quit if nothing has been added to the log */ + if ( lastpos == firstirq ) + return; + + if ( irq & (GR1553B_IRQ_RTTE|GR1553B_IRQ_RTD) ) { + isrerr = &priv->irq_err; + if ( isrerr->func ) { + isrerr->func(irq, isrerr->data); + } + + /* Stop Hardware and enter non-started mode. This will + * make all future calls to driver result in an error. + */ + gr1553rt_stop(priv); + } + + /* Step between first log entry causing an IRQ to last + * entry. Each entry that has caused an IRQ will be handled + * by calling user-defined function. + * + * We convert hardware addresses into CPU accessable addresses + * first. + */ + index = (firstirq - (unsigned int)priv->evlog_hw_base) / + sizeof(unsigned int); + curr = priv->evlog_cpu_base + index; + index = (lastpos - (unsigned int)priv->evlog_hw_base) / + sizeof(unsigned int); + last = priv->evlog_cpu_base + index; + + do { + /* Process one entry */ + entry = *curr; + + if ( entry & 0x80000000 ) { + /* Entry caused IRQ */ + type = (entry >> 29) & 0x3; + samc = (entry >> 24) & 0x1f; + if ( (type & 0x2) == 0 ) { + /* Transmit/Receive Data */ + subadr = samc; + if ( type ) { + /* Receive */ + listid = priv->subadrs[subadr].rxlistid; + hwbd = priv->sas_cpu[subadr].rxptr; + isr = &priv->irq_rx[subadr]; + } else { + /* Transmit */ + listid = priv->subadrs[subadr].txlistid; + hwbd = priv->sas_cpu[subadr].txptr; + isr = &priv->irq_tx[subadr]; + } + + index = ((unsigned int)hwbd - (unsigned int) + priv->bds_hw)/sizeof(struct gr1553rt_bd); + + /* Call user ISR of RX/TX transfer */ + if ( isr->func ) { + isr->func( + priv->lists[listid], + entry, + priv->swbds[index].this_next, + isr->data + ); + } + } else if ( type == 0x2) { + /* Modecode */ + mcode = samc; + isrmc = &priv->irq_mc; + + /* Call user ISR of ModeCodes RX/TX */ + if ( isrmc->func ) { + isrmc->func( + mcode, + entry, + isrmc->data + ); + } + } else { + /* ERROR OF SOME KIND, EVLOG OVERWRITTEN? */ + exit(-1); + } + } + + /* Calc next entry posistion */ + curr++; + if ( curr == priv->evlog_cpu_end ) + curr = priv->evlog_cpu_base; + + } while ( curr != last ); +} + +int gr1553rt_indication(void *rt, int subadr, int *txeno, int *rxeno) +{ + struct gr1553rt_priv *priv = rt; + struct gr1553rt_sa *sa; + unsigned int bd, index; + + /* Sub address valid */ + if ( (subadr < 0) || (subadr > 31) ) + return -1; + + /* Get SubAddress Descriptor address as accessed from CPU */ + sa = &priv->sas_cpu[subadr]; + + /* Indication of TX descriptor? */ + if ( txeno ) { + bd = sa->txptr; + /* Get Index of Hardware BD */ + index = ((unsigned int)bd - (unsigned int)&priv->bds_hw[0]) / + sizeof(struct gr1553rt_bd); + *txeno = priv->swbds[index].this_next; + } + + /* Indication of RX descriptor? */ + if ( rxeno ) { + bd = sa->rxptr; + /* Get Index of Hardware BD */ + index = ((unsigned int)bd - (unsigned int)&priv->bds_hw[0]) / + sizeof(struct gr1553rt_bd); + *rxeno = priv->swbds[index].this_next; + } + + return 0; +} + +#if 0 +int gr1553rt_bd_irq( + struct gr1553rt_list *list, + unsigned short entry_no, + void (*func)(struct gr1553rt_list *list, int entry, void *data), + void *data + ) +{ + struct gr1553rt_priv *priv = list->rt; + int irqid; + int ret; + IRQ_GLOBAL_PREPARE(oldLevel); + + if ( entry_no == 0xffff ) { + /* Default interrupt for all list entries without + * assigned IRQ function. + */ + list->irqs[0].func = func; + list->irqs[0].data = data; + return 0; + } + + if ( entry_no >= list->bd_cnt ) { + return -1; + } + + bdid = list->bds[entry_no]; + irqid = priv->swbds[bdid].irqid; + + ret = 0; + IRQ_GLOBAL_DISABLE(oldLevel); + if ( (irqid != 0) && (func == 0) ) { + /* Unassign IRQ function */ + list->irqs[irqid].func = NULL; + list->irqs[irqid].data = NULL; + irqid = 0; /* Disable IRQ (default handler) */ + } else if ( priv->swbds[bdid].irqid != 0 ) { + /* reassign IRQ function */ + list->irqs[irqid].func = func; + list->irqs[irqid].data = data; + } else { + /* Find free IRQ spot. If no free irqid=0 (general IRQ) */ + ret = -1; + for (i=0; i<list->cfg->maxirq; i++) { + if ( list->irqs[i].func == NULL ) { + irqid = i; + list->irqs[i].func = func; + list->irqs[i].data = data; + ret = 0; + break; + } + } + } + priv->swbds[bdid].irqid = irqid; + IRQ_GLOBAL_ENABLE(oldLevel); + + return ret; +} +#endif + +void gr1553rt_hw_stop(struct gr1553rt_priv *priv); + +void gr1553rt_register(void) +{ + /* The RT driver rely on the GR1553B Driver */ + gr1553_register(); +} + +void *gr1553rt_open(int minor) +{ + struct drvmgr_dev **pdev = NULL; + struct gr1553rt_priv *priv = NULL; + struct amba_dev_info *ambadev; + struct ambapp_core *pnpinfo; + + /* Allocate requested device */ + pdev = gr1553_rt_open(minor); + if ( pdev == NULL ) + goto fail; + + priv = malloc(sizeof(struct gr1553rt_priv)); + if ( priv == NULL ) + goto fail; + memset(priv, 0, sizeof(struct gr1553rt_priv)); + + /* Assign a device private to RT device */ + priv->pdev = pdev; + (*pdev)->priv = priv; + + /* 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; + + /* Start with default configuration */ + /*priv->cfg = gr1553rt_default_config;*/ + + /* Unmask IRQs and so */ + gr1553rt_hw_stop(priv); + + /* Register ISR handler. hardware mask IRQ, so it is safe to unmask + * at IRQ controller. + */ + if (drvmgr_interrupt_register(*priv->pdev, 0, "gr1553rt", gr1553rt_isr, priv)) + goto fail; + + return priv; + +fail: + if ( pdev ) + gr1553_rt_close(pdev); + if ( priv ) + free(priv); + return NULL; +} + +void gr1553rt_close(void *rt) +{ + struct gr1553rt_priv *priv = rt; + + if ( priv->started ) { + gr1553rt_stop(priv); + } + + /* Remove ISR handler */ + drvmgr_interrupt_unregister(*priv->pdev, 0, gr1553rt_isr, priv); + + /* Free dynamically allocated buffers if any */ + gr1553rt_sw_free(priv); + + /* Return RT/BC device */ + gr1553_rt_close(priv->pdev); +} + +/* Stop Hardware and disable IRQ */ +void gr1553rt_hw_stop(struct gr1553rt_priv *priv) +{ + uint32_t irqmask; + + /* Disable RT */ + GR1553RT_WRITE_REG(&priv->regs->rt_cfg, GR1553RT_KEY); + + /* Stop BC if not already stopped: BC can not be used simultaneously + * as the RT anyway + */ + GR1553RT_WRITE_REG(&priv->regs->bc_ctrl, GR1553BC_KEY | 0x0204); + + /* Turn off RT IRQ generation */ + irqmask=GR1553RT_READ_REG(&priv->regs->imask); + irqmask&=~(GR1553B_IRQEN_RTEVE|GR1553B_IRQEN_RTDE); + GR1553RT_WRITE_REG(&priv->regs->irq, irqmask); +} + +/* Free dynamically allocated buffers, if any */ +void gr1553rt_sw_free(struct gr1553rt_priv *priv) +{ + /* Event log */ + if ( (priv->cfg.evlog_buffer == NULL) && priv->evlog_buffer ) { + free(priv->evlog_buffer); + priv->evlog_buffer = NULL; + } + + /* RX/TX Descriptors */ + if ( (priv->cfg.bd_buffer == NULL) && priv->bd_buffer ) { + free(priv->bd_buffer); + priv->bd_buffer = NULL; + } + +#if (RTBD_MAX == 0) + if ( priv->swbds ) { + free(priv->swbds); + priv->swbds = NULL; + } +#endif + + /* Sub address table */ + if ( (priv->cfg.satab_buffer == NULL) && priv->satab_buffer ) { + free(priv->satab_buffer); + priv->satab_buffer = NULL; + } +} + +/* Free dynamically allocated buffers, if any */ +int gr1553rt_sw_alloc(struct gr1553rt_priv *priv) +{ + int size; + + /* Allocate Event log */ + if ((unsigned int)priv->cfg.evlog_buffer & 1) { + /* Translate Address from HARDWARE (REMOTE) to CPU-LOCAL */ + priv->evlog_hw_base = (unsigned int *) + ((unsigned int)priv->cfg.evlog_buffer & ~0x1); + priv->evlog_buffer = priv->cfg.evlog_buffer; + drvmgr_translate_check( + *priv->pdev, + DMAMEM_TO_CPU, + (void *)priv->evlog_hw_base, + (void **)&priv->evlog_cpu_base, + priv->cfg.evlog_size + ); + } else { + if (priv->cfg.evlog_buffer == NULL) { + priv->evlog_buffer = malloc(priv->cfg.evlog_size * 2); + if (priv->evlog_buffer == NULL) + return -1; + } else { + /* Addess already CPU-LOCAL */ + priv->evlog_buffer = priv->cfg.evlog_buffer; + } + /* Align to SIZE bytes boundary */ + priv->evlog_cpu_base = (unsigned int *) + (((unsigned int)priv->evlog_buffer + + (priv->cfg.evlog_size-1)) & ~(priv->cfg.evlog_size-1)); + + drvmgr_translate_check( + *priv->pdev, + CPUMEM_TO_DMA, + (void *)priv->evlog_cpu_base, + (void **)&priv->evlog_hw_base, + priv->cfg.evlog_size + ); + } + priv->evlog_cpu_end = priv->evlog_cpu_base + + priv->cfg.evlog_size/sizeof(unsigned int *); + + /* Allocate Transfer Descriptors */ + priv->bds_cnt = priv->cfg.bd_count; + size = priv->bds_cnt * sizeof(struct gr1553rt_bd); + if ((unsigned int)priv->cfg.bd_buffer & 1) { + /* Translate Address from HARDWARE (REMOTE) to CPU-LOCAL */ + priv->bds_hw = (unsigned int)priv->cfg.bd_buffer & ~0x1; + priv->bd_buffer = priv->cfg.bd_buffer; + drvmgr_translate_check( + *priv->pdev, + DMAMEM_TO_CPU, + (void *)priv->bds_hw, + (void **)&priv->bds_cpu, + size + ); + } else { + if ( priv->cfg.bd_buffer == NULL ) { + priv->bd_buffer = malloc(size + 0xf); + if (priv->bd_buffer == NULL) + return -1; + } else { + /* Addess already CPU-LOCAL */ + priv->bd_buffer = priv->cfg.bd_buffer; + } + /* Align to 16 bytes boundary */ + priv->bds_cpu = (struct gr1553rt_bd *) + (((unsigned int)priv->bd_buffer + 0xf) & ~0xf); + + /* Translate from CPU address to hardware address */ + drvmgr_translate_check( + *priv->pdev, + CPUMEM_TO_DMA, + (void *)priv->bds_cpu, + (void **)&priv->bds_hw, + size + ); + } + +#if (RTBD_MAX == 0) + /* Allocate software description of */ + priv->swbds = malloc(priv->cfg.bd_count * sizeof(struct gr1553rt_sw_bd)); + if ( priv->swbds == NULL ) { + return -1; + } +#endif + + /* Allocate Sub address table */ + if ((unsigned int)priv->cfg.satab_buffer & 1) { + /* Translate Address from HARDWARE (REMOTE) to CPU-LOCAL */ + priv->sas_hw = (unsigned int)priv->cfg.satab_buffer & ~0x1; + priv->satab_buffer = priv->cfg.satab_buffer; + drvmgr_translate_check( + *priv->pdev, + DMAMEM_TO_CPU, + (void *)priv->sas_hw, + (void **)&priv->sas_cpu, + 16 * 32); + } else { + if (priv->cfg.satab_buffer == NULL) { + priv->satab_buffer = malloc((16 * 32) * 2); + if (priv->satab_buffer == NULL) + return -1; + } else { + /* Addess already CPU-LOCAL */ + priv->satab_buffer = priv->cfg.satab_buffer; + } + /* Align to 512 bytes boundary */ + priv->sas_cpu = (struct gr1553rt_sa *) + (((unsigned int)priv->satab_buffer + 0x1ff) & + ~0x1ff); + + /* Translate Address from CPU-LOCAL to HARDWARE (REMOTE) */ + drvmgr_translate_check( + *priv->pdev, + CPUMEM_TO_DMA, + (void *)priv->sas_cpu, + (void **)&priv->sas_hw, + 16 * 32); + } + + return 0; +} + +void gr1553rt_sw_init(struct gr1553rt_priv *priv) +{ + int i; + + /* Clear Sub Address table */ + memset(priv->sas_cpu, 0, 512); + + /* Clear Transfer descriptors */ + memset(priv->bds_cpu, 0, priv->bds_cnt * 16); + + /* Clear the Event log */ + memset(priv->evlog_cpu_base, 0, priv->cfg.evlog_size); + + /* Init descriptor allocation algorithm */ + gr1553rt_bd_alloc_init(priv, priv->bds_cnt); + + /* Init table used to convert from sub address to list. + * Currently non assigned. + */ + for (i=0; i<32; i++) { + priv->subadrs[i].rxlistid = 0xff; + priv->subadrs[i].txlistid = 0xff; + } + + /* Clear all previous IRQ handlers */ + for (i=0; i<32; i++) { + priv->irq_rx[i].func = NULL; + priv->irq_tx[i].data = NULL; + } + priv->irq_err.func = NULL; + priv->irq_err.data = NULL; + priv->irq_mc.func = NULL; + priv->irq_mc.data = NULL; + + /* Clear LIST to LISTID table */ + for (i=0; i<RTLISTID_MAX; i++) { + priv->lists[i] = NULL; + } +} + +int gr1553rt_config(void *rt, struct gr1553rt_cfg *cfg) +{ + struct gr1553rt_priv *priv = rt; + + if ( priv->started ) + return -1; + + /*** Free dynamically allocated buffers ***/ + + gr1553rt_sw_free(priv); + + /*** Check new config ***/ + if ( cfg->rtaddress > 30 ) + return -1; + if ( (cfg->evlog_size & (cfg->evlog_size-1)) != 0) + return -1; /* SIZE: Not aligned to a power of 2 */ + if ( ((unsigned int)priv->cfg.evlog_buffer & (cfg->evlog_size-1)) != 0 ) + return -1; /* Buffer: Not aligned to size */ +#if (RTBD_MAX > 0) + if ( cfg->bd_count > RTBD_MAX ) + return -1; +#endif + + /*** Make new config current ***/ + priv->cfg = *cfg; + + /*** Adapt to new config ***/ + + if ( gr1553rt_sw_alloc(priv) != 0 ) + return -1; + + gr1553rt_sw_init(priv); + + return 0; +} + +int gr1553rt_start(void *rt) +{ + struct gr1553rt_priv *priv = rt; + IRQ_GLOBAL_PREPARE(oldLevel); + + if ( priv->started ) + return -1; + + /*** Initialize software Pointers and stuff ***/ + + if ( !priv->satab_buffer || !priv->bd_buffer || !priv->evlog_buffer ) + return -2; + + priv->evlog_cpu_next = priv->evlog_cpu_base; + + /*** Initialize Registers ***/ + + /* Subaddress table base */ + priv->regs->rt_tab = (unsigned int)priv->sas_hw; + + /* Mode code configuration */ + priv->regs->rt_mcctrl = priv->cfg.modecode; + + /* RT Time Tag resolution */ + priv->regs->rt_ttag = priv->cfg.time_res << 16; + + /* Event LOG base and size */ + priv->regs->rt_evsz = ~(priv->cfg.evlog_size - 1); + priv->regs->rt_evlog = (unsigned int)priv->evlog_hw_base; + priv->regs->rt_evirq = 0; + + /* Clear and old IRQ flag and Enable IRQ */ + IRQ_GLOBAL_DISABLE(oldLevel); + priv->regs->irq = GR1553B_IRQ_RTEV|GR1553B_IRQ_RTD|GR1553B_IRQ_RTTE; + priv->regs->imask |= GR1553B_IRQEN_RTEVE | GR1553B_IRQEN_RTDE | + GR1553B_IRQEN_RTTEE; + + /* Enable and Set RT address */ + priv->regs->rt_cfg = GR1553RT_KEY | + (priv->cfg.rtaddress << GR1553B_RT_CFG_RTADDR_BIT) | + GR1553B_RT_CFG_RTEN; + + /* Tell software RT is started */ + priv->started = 1; + IRQ_GLOBAL_ENABLE(oldLevel); + + return 0; +} + +void gr1553rt_stop(void *rt) +{ + struct gr1553rt_priv *priv = rt; + IRQ_GLOBAL_PREPARE(oldLevel); + + IRQ_GLOBAL_DISABLE(oldLevel); + + /* Stop Hardware */ + gr1553rt_hw_stop(priv); + + /* Software state */ + priv->started = 0; + + IRQ_GLOBAL_ENABLE(oldLevel); +} + +void gr1553rt_sa_schedule( + void *rt, + int subadr, + int tx, + struct gr1553rt_list *list + ) +{ + struct gr1553rt_priv *priv = rt; + unsigned short bdid; + struct gr1553rt_bd *bd; + + if ( !list || (list->listid == -1) ) + return; + + /* Get Hardware address of first descriptor in list */ + bdid = list->bds[0]; + if ( bdid == 0xffff ) + return; + bd = &priv->bds_hw[bdid]; + + list->subadr = subadr; + + /* Update Sub address table */ + if ( tx ) { + list->subadr |= 0x100; + priv->subadrs[subadr].txlistid = list->listid; + priv->sas_cpu[subadr].txptr = (unsigned int)bd; + } else { + priv->subadrs[subadr].rxlistid = list->listid; + priv->sas_cpu[subadr].rxptr = (unsigned int)bd; + } +} + +void gr1553rt_sa_setopts( + void *rt, + int subadr, + unsigned int mask, + unsigned int options + ) +{ + struct gr1553rt_priv *priv = rt; + unsigned int ctrl; + + if ( (subadr > 31) || (priv->sas_cpu == NULL) ) + return; + + ctrl = priv->sas_cpu[subadr].ctrl; + priv->sas_cpu[subadr].ctrl = (ctrl & ~mask) | options; +} + +void gr1553rt_set_vecword(void *rt, unsigned int mask, unsigned int words) +{ + struct gr1553rt_priv *priv = rt; + unsigned int vword; + + if ( mask == 0 ) + return; + + vword = priv->regs->rt_statw; + + priv->regs->rt_statw = (vword & ~mask) | (words & mask); +} + +void gr1553rt_set_bussts(void *rt, unsigned int mask, unsigned int sts) +{ + struct gr1553rt_priv *priv = rt; + unsigned int stat; + + stat = priv->regs->rt_stat2; + priv->regs->rt_stat2 = (stat & ~mask) | (mask & sts); +} + +void gr1553rt_status(void *rt, struct gr1553rt_status *status) +{ + struct gr1553rt_priv *priv = rt; + struct gr1553b_regs *regs = priv->regs; + unsigned int tmp; + IRQ_GLOBAL_PREPARE(oldLevel); + + IRQ_GLOBAL_DISABLE(oldLevel); + status->status = regs->rt_stat; + status->bus_status = regs->rt_stat2; + + tmp = regs->rt_sync; + status->synctime = tmp >> 16; + status->syncword = tmp & 0xffff; + + tmp = regs->rt_ttag; + status->time_res = tmp >> 16; + status->time = tmp & 0xffff; + + IRQ_GLOBAL_ENABLE(oldLevel); +} + +void gr1553rt_list_sa(struct gr1553rt_list *list, int *subadr, int *tx) +{ + int sa, trt; + + if ( list->subadr == -1 ) { + sa = -1; + trt = -1; + } else { + sa = list->subadr & 0xff; + trt = (list->subadr & 0x100) >> 8; + } + + if ( subadr ) + *subadr = sa; + if ( tx ) + *tx = trt; +} + +int gr1553rt_evlog_read(void *rt, unsigned int *dst, int max) +{ + struct gr1553rt_priv *priv = rt; + int cnt, top, bot, left; + unsigned int *hwpos; + + /* Get address of hardware's current working entry */ + hwpos = (unsigned int *)priv->regs->rt_evlog; + + /* Convert into CPU address */ + hwpos = (unsigned int *) + ((unsigned int)hwpos - (unsigned int)priv->evlog_hw_base + + (unsigned int)priv->evlog_cpu_base); + + if ( priv->evlog_cpu_next == hwpos ) + return 0; + + if ( priv->evlog_cpu_next > hwpos ) { + top = (unsigned int)priv->evlog_cpu_end - + (unsigned int)priv->evlog_cpu_next; + bot = (unsigned int)hwpos - (unsigned int)priv->evlog_cpu_base; + } else { + top = (unsigned int)hwpos - (unsigned int)priv->evlog_cpu_next; + bot = 0; + } + top = top / 4; + bot = bot / 4; + + left = max; + if ( top > 0 ) { + if ( top > left ) { + cnt = left; + } else { + cnt = top; + } + memcpy(dst, priv->evlog_cpu_next, cnt*4); + dst += cnt; + left -= cnt; + } + + if ( (bot > 0) && (left > 0) ) { + if ( bot > left ) { + cnt = left; + } else { + cnt = bot; + } + memcpy(dst, priv->evlog_cpu_base, cnt*4); + left -= cnt; + } + + cnt = max - left; + priv->evlog_cpu_next += cnt; + if ( priv->evlog_cpu_next >= priv->evlog_cpu_end ) { + priv->evlog_cpu_next = (unsigned int *) + ((unsigned int)priv->evlog_cpu_base + + ((unsigned int)priv->evlog_cpu_next - + (unsigned int)priv->evlog_cpu_end )); + } + + return max - left; +} |