From 3bb41226e0941b86d58ecb97f7d292677de573c8 Mon Sep 17 00:00:00 2001 From: Daniel Hellstrom Date: Mon, 23 Feb 2015 13:02:39 +0100 Subject: LEON: added new drivers to the LEON2/LEON3 BSPs Most drivers use the Driver Manager for device probing, they work on AMBA-over-PCI systems if PCI is big-endian. New APIs: * GPIO Library, interfaced to GRGPIO * GENIRQ, Generic interrupt service implementation helper New GRLIB Drivers: * ACTEL 1553 RT, user interface is similar to 1553 BRM driver * GR1553 (1553 BC, RT and BM core) * AHBSTAT (AHB error status core) * GRADCDAC (Core interfacing to ADC/DAC hardware) * GRGPIO (GPIO port accessed from GPIO Library) * MCTRL (Memory controller settings configuration) * GRETH (10/100/1000 Ethernet driver using Driver manager) * GRPWM (Pulse Width Modulation core) * SPICTRL (SPI master interface) * GRSPW_ROUTER (SpaceWire Router AMBA configuration interface) * GRCTM (SpaceCraft on-board Time Management core) * SPWCUC (Time distribution over SpaceWire) * GRTC (SpaceCraft up-link Tele core) * GRTM (SpaceCraft down-link Tele Metry core) GR712RC ASIC specific interfaces: * GRASCS * CANMUX (select between OCCAN and SATCAN) * SATCAN * SLINK --- c/src/lib/libbsp/sparc/shared/1553/b1553rt.c | 859 +++++++++++++ c/src/lib/libbsp/sparc/shared/1553/gr1553b.c | 305 +++++ c/src/lib/libbsp/sparc/shared/1553/gr1553bc.c | 1674 +++++++++++++++++++++++++ c/src/lib/libbsp/sparc/shared/1553/gr1553bm.c | 519 ++++++++ c/src/lib/libbsp/sparc/shared/1553/gr1553rt.c | 1256 +++++++++++++++++++ 5 files changed, 4613 insertions(+) create mode 100644 c/src/lib/libbsp/sparc/shared/1553/b1553rt.c create mode 100644 c/src/lib/libbsp/sparc/shared/1553/gr1553b.c create mode 100644 c/src/lib/libbsp/sparc/shared/1553/gr1553bc.c create mode 100644 c/src/lib/libbsp/sparc/shared/1553/gr1553bm.c create mode 100644 c/src/lib/libbsp/sparc/shared/1553/gr1553rt.c (limited to 'c/src/lib/libbsp/sparc/shared/1553') diff --git a/c/src/lib/libbsp/sparc/shared/1553/b1553rt.c b/c/src/lib/libbsp/sparc/shared/1553/b1553rt.c new file mode 100644 index 0000000000..3dfb40309c --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/1553/b1553rt.c @@ -0,0 +1,859 @@ +/* + * B1553RT driver implmenetation + * + * COPYRIGHT (c) 2009. + * 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 +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +/* Uncomment for debug output */ +/*#define DEBUG 1*/ + +/* + #define FUNCDEBUG 1*/ +/*#undef DEBUG*/ +#undef FUNCDEBUG + +/* EVENT_QUEUE_SIZE sets the size of the event queue + */ +#define EVENT_QUEUE_SIZE 1024 + + +#define INDEX(x) ( x&(EVENT_QUEUE_SIZE-1) ) + +#if 0 +#define DBG(x...) printk(x) +#else +#define DBG(x...) +#endif + +#ifdef FUNCDEBUG +#define FUNCDBG(x...) printk(x) +#else +#define FUNCDBG(x...) +#endif + +#define READ_DMA(address) _READ16((unsigned int)address) + +static __inline__ unsigned short _READ16(unsigned int addr) { + unsigned short tmp; + asm(" lduha [%1]1, %0 " + : "=r"(tmp) + : "r"(addr) + ); + return tmp; +} + +static rtems_device_driver rt_initialize(rtems_device_major_number major, rtems_device_minor_number minor, void *arg); +static rtems_device_driver rt_open(rtems_device_major_number major, rtems_device_minor_number minor, void *arg); +static rtems_device_driver rt_close(rtems_device_major_number major, rtems_device_minor_number minor, void *arg); +static rtems_device_driver rt_read(rtems_device_major_number major, rtems_device_minor_number minor, void *arg); +static rtems_device_driver rt_write(rtems_device_major_number major, rtems_device_minor_number minor, void *arg); +static rtems_device_driver rt_control(rtems_device_major_number major, rtems_device_minor_number minor, void *arg); + +#define RT_DRIVER_TABLE_ENTRY { rt_initialize, rt_open, rt_close, rt_read, rt_write, rt_control } + +static rtems_driver_address_table b1553rt_driver = RT_DRIVER_TABLE_ENTRY; + +typedef struct { + + struct drvmgr_dev *dev; /* Driver manager device */ + char devName[32]; /* Device Name */ + + struct rt_reg *regs; + unsigned int ctrl_copy; /* Local copy of config register */ + + unsigned int cfg_freq; + + unsigned int memarea_base; + unsigned int memarea_base_remote; + + volatile unsigned short *mem; + + /* Received events waiting to be read */ + struct rt_msg *rt_event; + unsigned int head, tail; + + int rx_blocking; + + rtems_id rx_sem, tx_sem, dev_sem; + int minor; + int irqno; + +#ifdef DEBUG + unsigned int log[EVENT_QUEUE_SIZE*4]; + unsigned int log_i; +#endif + + unsigned int status; + rtems_id event_id; /* event that may be signalled upon errors, needs to be set through ioctl command RT_SET_EVENTID */ + +} rt_priv; + +static void b1553rt_interrupt(void *arg); +static rtems_device_driver rt_init(rt_priv *rt); + +#define OFS(ofs) (((unsigned int)&ofs & 0x1ffff)>>1) + +static int b1553rt_driver_io_registered = 0; +static rtems_device_major_number b1553rt_driver_io_major = 0; + +/******************* Driver manager interface ***********************/ + +/* Driver prototypes */ +int b1553rt_register_io(rtems_device_major_number *m); +int b1553rt_device_init(rt_priv *pDev); + +int b1553rt_init2(struct drvmgr_dev *dev); +int b1553rt_init3(struct drvmgr_dev *dev); +int b1553rt_remove(struct drvmgr_dev *dev); + +struct drvmgr_drv_ops b1553rt_ops = +{ + .init = {NULL, b1553rt_init2, b1553rt_init3, NULL}, + .remove = b1553rt_remove, + .info = NULL +}; + +struct amba_dev_id b1553rt_ids[] = +{ + {VENDOR_GAISLER, GAISLER_B1553RT}, + {0, 0} /* Mark end of table */ +}; + +struct amba_drv_info b1553rt_drv_info = +{ + { + DRVMGR_OBJ_DRV, /* Driver */ + NULL, /* Next driver */ + NULL, /* Device list */ + DRIVER_AMBAPP_GAISLER_B1553RT_ID, /* Driver ID */ + "B1553RT_DRV", /* Driver Name */ + DRVMGR_BUS_TYPE_AMBAPP, /* Bus Type */ + &b1553rt_ops, + NULL, /* Funcs */ + 0, /* No devices yet */ + 0, + + }, + &b1553rt_ids[0] +}; + +void b1553rt_register_drv (void) +{ + DBG("Registering B1553RT driver\n"); + drvmgr_drv_register(&b1553rt_drv_info.general); +} + +int b1553rt_init2(struct drvmgr_dev *dev) +{ + rt_priv *priv; + + DBG("B1553RT[%d] on bus %s\n", dev->minor_drv, dev->parent->dev->name); + priv = dev->priv = malloc(sizeof(rt_priv)); + if ( !priv ) + return DRVMGR_NOMEM; + memset(priv, 0, sizeof(*priv)); + priv->dev = dev; + + /* This core will not find other cores, so we wait for init2() */ + + return DRVMGR_OK; +} + +int b1553rt_init3(struct drvmgr_dev *dev) +{ + rt_priv *priv; + char prefix[32]; + rtems_status_code status; + + priv = dev->priv; + + /* Do initialization */ + + if ( b1553rt_driver_io_registered == 0) { + /* Register the I/O driver only once for all cores */ + if ( b1553rt_register_io(&b1553rt_driver_io_major) ) { + /* Failed to register I/O driver */ + dev->priv = NULL; + return DRVMGR_FAIL; + } + + b1553rt_driver_io_registered = 1; + } + + /* I/O system registered and initialized + * Now we take care of device initialization. + */ + + if ( b1553rt_device_init(priv) ) { + return DRVMGR_FAIL; + } + + /* Get Filesystem name prefix */ + prefix[0] = '\0'; + if ( drvmgr_get_dev_prefix(dev, prefix) ) { + /* Failed to get prefix, make sure of a unique FS name + * by using the driver minor. + */ + sprintf(priv->devName, "/dev/b1553rt%d", dev->minor_drv); + } else { + /* Got special prefix, this means we have a bus prefix + * And we should use our "bus minor" + */ + sprintf(priv->devName, "/dev/%sb1553rt%d", prefix, dev->minor_bus); + } + + /* Register Device */ + status = rtems_io_register_name(priv->devName, b1553rt_driver_io_major, dev->minor_drv); + if (status != RTEMS_SUCCESSFUL) { + return DRVMGR_FAIL; + } + + return DRVMGR_OK; +} + +int b1553rt_remove(struct drvmgr_dev *dev) +{ + /* Stop more tasks to open driver */ + + /* Throw out all tasks using this driver */ + + /* Unregister I/O node */ + + /* Unregister and disable Interrupt */ + + /* Free device memory */ + + /* Return sucessfully */ + + return DRVMGR_FAIL; +} + +/******************* Driver Implementation ***********************/ + +int b1553rt_register_io(rtems_device_major_number *m) +{ + rtems_status_code r; + + if ((r = rtems_io_register_driver(0, &b1553rt_driver, m)) == RTEMS_SUCCESSFUL) { + DBG("B1553RT driver successfully registered, major: %d\n", *m); + } else { + switch(r) { + case RTEMS_TOO_MANY: + printk("B1553RT rtems_io_register_driver failed: RTEMS_TOO_MANY\n"); + return -1; + case RTEMS_INVALID_NUMBER: + printk("B1553RT rtems_io_register_driver failed: RTEMS_INVALID_NUMBER\n"); + return -1; + case RTEMS_RESOURCE_IN_USE: + printk("B1553RT rtems_io_register_driver failed: RTEMS_RESOURCE_IN_USE\n"); + return -1; + default: + printk("B1553RT rtems_io_register_driver failed\n"); + return -1; + } + } + return 0; +} + +int b1553rt_device_init(rt_priv *pDev) +{ + struct amba_dev_info *ambadev; + struct ambapp_core *pnpinfo; + union drvmgr_key_value *value; + unsigned int mem; + unsigned int sys_freq_hz; + + /* Get device information from AMBA PnP information */ + ambadev = (struct amba_dev_info *)pDev->dev->businfo; + if ( ambadev == NULL ) { + return -1; + } + pnpinfo = &ambadev->info; + pDev->irqno = pnpinfo->irq; + pDev->regs = (struct rt_reg *)pnpinfo->apb_slv->start; + pDev->minor = pDev->dev->minor_drv; + +#ifdef DEBUG + pDev->log_i = 0; + memset(pDev->log,0,sizeof(pDev->log)); + printf("LOG: 0x%x\n", &pDev->log[0]); + printf("LOG_I: 0x%x\n", &pDev->log_i); +#endif + + /* Get memory configuration from bus resources */ + value = drvmgr_dev_key_get(pDev->dev, "dmaBaseAdr", KEY_TYPE_POINTER); + if (value) + mem = (unsigned int)value->ptr; + + if (value && (mem & 1)) { + /* Remote address, address as RT looks at it. */ + + /* Translate the base address into an address that the the CPU can understand */ + pDev->memarea_base = mem & ~1; + drvmgr_translate_check(pDev->dev, DMAMEM_TO_CPU, + (void *)pDev->memarea_base_remote, + (void **)&pDev->memarea_base, + 4 * 1024); + } else { + if (!value) { + /* Use dynamically allocated memory, + * 4k DMA memory + 4k for alignment + */ + mem = (char *)malloc(4 * 1024 * 2); + if ( !mem ){ + printk("RT: Failed to allocate HW memory\n\r"); + return -1; + } + /* align memory to 4k boundary */ + pDev->memarea_base = (mem + 0xfff) & ~0xfff; + } else { + pDev->memarea_base = mem; + } + + /* Translate the base address into an address that the RT core can understand */ + drvmgr_translate_check(pDev->dev, CPUMEM_TO_DMA, + (void *)pDev->memarea_base, + (void **)&pDev->memarea_base_remote, + 4 * 1024); + } + + /* clear the used memory */ + memset((char *)pDev->memarea_base, 0, 4 * 1024); + + /* Set base address of all descriptors */ + pDev->memarea_base = (unsigned int)mem; + pDev->mem = (volatile unsigned short *)pDev->memarea_base; + + pDev->rt_event = NULL; + + /* The RT is always clocked at the same frequency as the bus + * If the frequency doesnt match it is defaulted to 24MHz, + * user can always override it. + */ + pDev->cfg_freq = RT_FREQ_24MHZ; + + /* Get frequency in Hz */ + if ( drvmgr_freq_get(pDev->dev, DEV_APB_SLV, &sys_freq_hz) == 0 ) { + if ( sys_freq_hz == 20000000 ) { + pDev->cfg_freq = RT_FREQ_20MHZ; + } else if ( sys_freq_hz == 16000000 ) { + pDev->cfg_freq = RT_FREQ_16MHZ; + } else if ( sys_freq_hz == 12000000 ) { + pDev->cfg_freq = RT_FREQ_12MHZ; + } + } + + value = drvmgr_dev_key_get(pDev->dev, "coreFreq", KEY_TYPE_INT); + if ( value ) { + pDev->cfg_freq = value->i & RT_FREQ_MASK; + } + + /* RX Semaphore created with count = 0 */ + if ( rtems_semaphore_create(rtems_build_name('R', 'T', '0', '0' + pDev->minor), + 0, + RTEMS_FIFO|RTEMS_SIMPLE_BINARY_SEMAPHORE|RTEMS_NO_INHERIT_PRIORITY|RTEMS_LOCAL|RTEMS_NO_PRIORITY_CEILING, + 0, + &pDev->rx_sem) != RTEMS_SUCCESSFUL ) { + printk("RT: Failed to create rx semaphore\n"); + return RTEMS_INTERNAL_ERROR; + } + + /* Device Semaphore created with count = 1 */ + if ( rtems_semaphore_create(rtems_build_name('R', 'T', '0', '0' + pDev->minor), + 1, + RTEMS_FIFO|RTEMS_SIMPLE_BINARY_SEMAPHORE|RTEMS_NO_INHERIT_PRIORITY|RTEMS_LOCAL|RTEMS_NO_PRIORITY_CEILING, + 0, + &pDev->dev_sem) != RTEMS_SUCCESSFUL ){ + printk("RT: Failed to create device semaphore\n"); + return RTEMS_INTERNAL_ERROR; + } + + /* Default to RT-mode */ + rt_init(pDev); + + return 0; +} + +static int odd_parity(unsigned int data) +{ + unsigned int i=0; + + while(data) + { + i++; + data &= (data - 1); + } + + return !(i&1); +} + +static void start_operation(rt_priv *rt) +{ + +} + +static void stop_operation(rt_priv *rt) +{ + +} + +static void set_extmdata_en(rt_priv *rt, int extmdata) +{ + if ( extmdata ) + extmdata = 1; + rt->ctrl_copy = (rt->ctrl_copy & ~(1<<16)) | (extmdata<<16); + rt->regs->ctrl = rt->ctrl_copy; +} + +static void set_vector_word(rt_priv *rt, unsigned short vword) +{ + rt->regs->vword = vword; +} + +/* Set clock speed */ +static void set_clkspd(rt_priv *rt, int spd) +{ + rt->ctrl_copy = (rt->ctrl_copy & ~0xC0) | (spd<<6); + rt->regs->ctrl = rt->ctrl_copy; + asm volatile("nop"::); + rt->regs->ctrl = rt->ctrl_copy | (1<<20); +} + +static void set_rtaddr(rt_priv *rt, int addr) +{ + rt->ctrl_copy = (rt->ctrl_copy & ~0x3F00) | (addr << 8) | (odd_parity(addr)<<13); + rt->regs->ctrl = rt->ctrl_copy; +} + +static void set_broadcast_en(rt_priv *rt, int data) +{ + rt->ctrl_copy = (rt->ctrl_copy & ~0x40000) | (data<<18); + rt->regs->ctrl = rt->ctrl_copy; +} + +static rtems_device_driver rt_init(rt_priv *rt) +{ + rt->rx_blocking = 1; + + if ( rt->rt_event ) + free(rt->rt_event); + rt->rt_event = NULL; + + rt->rt_event = (struct rt_msg *) malloc(EVENT_QUEUE_SIZE*sizeof(struct rt_msg)); + + if (rt->rt_event == NULL) { + DBG("RT driver failed to allocated memory."); + return RTEMS_NO_MEMORY; + } + + rt->ctrl_copy = rt->regs->ctrl & 0x3F00; /* Keep rtaddr and rtaddrp */ + rt->ctrl_copy |= 0x3C0D0; /* broadcast disabled, extmdata=1, writetsw = writecmd = 1 */ + rt->regs->ctrl = rt->ctrl_copy; + + /* Set Clock speed */ + set_clkspd(rt, rt->cfg_freq); + + rt->regs->addr = rt->memarea_base_remote; + rt->regs->ipm = 0x70000; /* Enable RT RX, MEM Failure and AHB Error interrupts */ + + DBG("B1553RT DMA_AREA: 0x%x\n", (unsigned int)rt->mem); + + return RTEMS_SUCCESSFUL; +} + + +static rtems_device_driver rt_initialize(rtems_device_major_number major, rtems_device_minor_number minor, void *arg) +{ + return RTEMS_SUCCESSFUL; +} + +static rtems_device_driver rt_open(rtems_device_major_number major, rtems_device_minor_number minor, void *arg) { + rt_priv *rt; + struct drvmgr_dev *dev; + + FUNCDBG("rt_open\n"); + + if ( drvmgr_get_dev(&b1553rt_drv_info.general, minor, &dev) ) { + DBG("Wrong minor %d\n", minor); + return RTEMS_UNSATISFIED; + } + rt = (rt_priv *)dev->priv; + + if (rtems_semaphore_obtain(rt->dev_sem, RTEMS_NO_WAIT, RTEMS_NO_TIMEOUT) != RTEMS_SUCCESSFUL) { + DBG("rt_open: resource in use\n"); + return RTEMS_RESOURCE_IN_USE; /* EBUSY */ + } + + /* Set defaults */ + rt->event_id = 0; + + start_operation(rt); + + /* Register interrupt routine */ + if (drvmgr_interrupt_register(rt->dev, 0, "b1553rt", b1553rt_interrupt, rt)) { + rtems_semaphore_release(rt->dev_sem); + return -1; + } + + + return RTEMS_SUCCESSFUL; +} + +static rtems_device_driver rt_close(rtems_device_major_number major, rtems_device_minor_number minor, void *arg) +{ + rt_priv *rt; + struct drvmgr_dev *dev; + + FUNCDBG("rt_close"); + + if ( drvmgr_get_dev(&b1553rt_drv_info.general, minor, &dev) ) { + return RTEMS_UNSATISFIED; + } + rt = (rt_priv *)dev->priv; + + drvmgr_interrupt_unregister(rt->dev, 0, b1553rt_interrupt, rt); + + stop_operation(rt); + rtems_semaphore_release(rt->dev_sem); + + return RTEMS_SUCCESSFUL; +} + +static int get_messages(rt_priv *rt, void *buf, unsigned int msg_count) +{ + + struct rt_msg *dest = (struct rt_msg *) buf; + int count = 0; + + if (rt->head == rt->tail) { + return 0; + } + + do { + + DBG("rt read - head: %d, tail: %d\n", rt->head, rt->tail); + dest[count++] = rt->rt_event[INDEX(rt->tail++)]; + + } while (rt->head != rt->tail && count < msg_count); + + return count; + +} +static rtems_device_driver rt_read(rtems_device_major_number major, rtems_device_minor_number minor, void *arg) +{ + rtems_libio_rw_args_t *rw_args; + int count = 0; + rt_priv *rt; + struct drvmgr_dev *dev; + + if ( drvmgr_get_dev(&b1553rt_drv_info.general, minor, &dev) ) { + return RTEMS_UNSATISFIED; + } + rt = (rt_priv *)dev->priv; + + rw_args = (rtems_libio_rw_args_t *) arg; + + FUNCDBG("rt_read [%i,%i]: buf: 0x%x, len: %i\n",major, minor, (unsigned int)rw_args->buffer, rw_args->count); + + while ( (count = get_messages(rt,rw_args->buffer, rw_args->count)) == 0 ) { + + if (rt->rx_blocking) { + rtems_semaphore_obtain(rt->rx_sem, RTEMS_WAIT, RTEMS_NO_TIMEOUT); + } else { + /* Translates to EBUSY */ + return RTEMS_RESOURCE_IN_USE; + } + } + + rw_args->bytes_moved = count; + return RTEMS_SUCCESSFUL; +} + +static rtems_device_driver rt_write(rtems_device_major_number major, rtems_device_minor_number minor, void *arg) +{ + rtems_libio_rw_args_t *rw_args; + struct rt_msg *source; + rt_priv *rt; + struct drvmgr_dev *dev; + unsigned int descriptor, suba, wc; + + if ( drvmgr_get_dev(&b1553rt_drv_info.general, minor, &dev) ) { + return RTEMS_UNSATISFIED; + } + rt = (rt_priv *)dev->priv; + + rw_args = (rtems_libio_rw_args_t *) arg; + + if ( rw_args->count != 1 ) { + return RTEMS_INVALID_NAME; + } + + source = (struct rt_msg *) rw_args->buffer; + + descriptor = source[0].desc & 0x7F; + suba = descriptor-32; + wc = source[0].miw >> 11; + wc = wc ? wc : 32; + + FUNCDBG("rt_write [%i,%i]: buf: 0x%x\n",major, minor, (unsigned int)rw_args->buffer); + + memcpy((void *)&rt->mem[0x400 + suba*32], &source[0].data[0], wc*2); + + rw_args->bytes_moved = 1; + + return RTEMS_SUCCESSFUL; + +} + +static rtems_device_driver rt_control(rtems_device_major_number major, rtems_device_minor_number minor, void *arg) +{ + rtems_libio_ioctl_args_t *ioarg = (rtems_libio_ioctl_args_t *) arg; + unsigned int *data = ioarg->buffer; + + rt_priv *rt; + struct drvmgr_dev *dev; + + FUNCDBG("rt_control[%d]: [%i,%i]\n", minor, major, minor); + + if ( drvmgr_get_dev(&b1553rt_drv_info.general, minor, &dev) ) { + return RTEMS_UNSATISFIED; + } + rt = (rt_priv *)dev->priv; + + if (!ioarg) { + DBG("rt_control: invalid argument\n"); + return RTEMS_INVALID_NAME; + } + + ioarg->ioctl_return = 0; + switch (ioarg->command) { + + case RT_SET_ADDR: + set_rtaddr(rt, data[0]); + break; + + case RT_SET_BCE: + set_broadcast_en(rt, data[0]); + break; + + case RT_SET_VECTORW: + set_vector_word(rt, data[0]); + break; + + case RT_SET_EXTMDATA: + set_extmdata_en(rt, data[0]); + break; + + case RT_RX_BLOCK: + rt->rx_blocking = data[0]; + break; + + case RT_CLR_STATUS: + rt->status = 0; + break; + + case RT_GET_STATUS: /* copy status */ + if ( !ioarg->buffer ) + return RTEMS_INVALID_NAME; + + *(unsigned int *)ioarg->buffer = rt->status; + break; + + case RT_SET_EVENTID: + rt->event_id = (rtems_id)ioarg->buffer; + break; + + default: + return RTEMS_NOT_IMPLEMENTED; + } + + return RTEMS_SUCCESSFUL; +} + +static void b1553rt_interrupt(void *arg) +{ + rt_priv *rt = arg; + unsigned short descriptor; + int signal_event=0, wake_rx_task=0; + unsigned int event_status=0; + unsigned int wc, irqv, cmd, tsw, suba, tx, miw, i; + unsigned int ipend; + + #define SET_ERROR_DESCRIPTOR(descriptor) (event_status = (event_status & 0x0000ffff) | descriptor<<16) + ipend = rt->regs->ipm; + + if (ipend == 0) { + /* IRQ mask has been cleared, we must have been reset */ + /* Restore ctrl registers */ + rt->regs->ctrl = rt->ctrl_copy; + rt->regs->addr = rt->memarea_base_remote; + rt->regs->ipm = 0x70000; + /* Send reset mode code event */ + if (rt->head - rt->tail != EVENT_QUEUE_SIZE) { + miw = (8<<11); + descriptor = 64 + 32 + 8; + rt->rt_event[INDEX(rt->head)].miw = miw; + rt->rt_event[INDEX(rt->head)].time = 0; + rt->rt_event[INDEX(rt->head)].desc = descriptor; + rt->head++; + } + } + + if ( ipend & 0x1 ) { + /* RT IRQ */ + if (rt->head - rt->tail != EVENT_QUEUE_SIZE) { + + irqv = rt->regs->irq; + cmd = irqv >> 7; + wc = cmd & 0x1F; /* word count / mode code */ + suba = irqv & 0x1F; /* sub address (0-31) */ + tx = (irqv >> 5) & 1; + + /* read status word */ + tsw = READ_DMA(&rt->mem[tx*0x3E0+suba]); + + /* Build Message Information Word (B1553BRM-style) */ + miw = (wc<<11) | (tsw&RT_TSW_BUS)>>4 | !(tsw&RT_TSW_OK)>>7 | (tsw&RT_TSW_ILL)>>5 | + (tsw&RT_TSW_PAR)>>5 | (tsw&RT_TSW_MAN)>>7; + + descriptor = (tx << 5) | suba; + + /* Mode codes */ + if (suba == 0 || suba == 31) { + descriptor = 64 + (tx*32) + wc; + } + + /* Data received or transmitted */ + if (descriptor < 64) { + wc = wc ? wc : 32; /* wc = 0 means 32 words transmitted */ + } + /* RX Mode code */ + else if (descriptor < 96) { + wc = (wc>>4); + } + /* TX Mode code */ + else if (descriptor < 128) { + wc = (wc>>4); + } + + /* Copy to event queue */ + rt->rt_event[INDEX(rt->head)].miw = miw; + rt->rt_event[INDEX(rt->head)].time = 0; + + for (i = 0; i < wc; i++) { + rt->rt_event[INDEX(rt->head)].data[i] = READ_DMA(&rt->mem[tx*0x400 + suba*32 + i]); + } + rt->rt_event[INDEX(rt->head)].desc = descriptor; + rt->head++; + + + /* Handle errors */ + if ( tsw & RT_TSW_ILL){ + FUNCDBG("RT: RT_ILLCMD\n\r"); + rt->status |= RT_ILLCMD_IRQ; + event_status |= RT_ILLCMD_IRQ; + SET_ERROR_DESCRIPTOR(descriptor); + signal_event=1; + } + + if ( !(tsw & RT_TSW_OK) ) { + FUNCDBG("RT: RT_MERR_IRQ\n\r"); + rt->status |= RT_MERR_IRQ; + event_status |= RT_MERR_IRQ; + SET_ERROR_DESCRIPTOR(descriptor); + signal_event=1; + } + + } + else { + /* Indicate overrun */ + rt->rt_event[INDEX(rt->head)].desc |= 0x8000; + } + } + + if ( ipend & 0x2 ) { + /* Memory failure IRQ */ + FUNCDBG("B1553RT: Memory failure\n"); + event_status |= RT_DMAF_IRQ; + signal_event=1; + } + + if ( ipend & 0x4 ) { + /* AHB Error */ + FUNCDBG("B1553RT: AHB ERROR\n"); + event_status |= RT_DMAF_IRQ; + signal_event=1; + } + +#ifdef DEBUG + rt->log[rt->log_i++ % EVENT_QUEUE_SIZE] = descriptor; + rt->log[rt->log_i++ % EVENT_QUEUE_SIZE] = cmd; + rt->log[rt->log_i++ % EVENT_QUEUE_SIZE] = miw; + rt->log[rt->log_i++ % EVENT_QUEUE_SIZE] = tsw; +#endif + + wake_rx_task = 1; + + /* Wake any blocked rx thread only on receive interrupts */ + if ( wake_rx_task ) { + rtems_semaphore_release(rt->rx_sem); + } + + /* Copy current mask to status mask */ + if ( event_status ) { + if ( event_status & 0xffff0000 ) + rt->status &= 0x0000ffff; + rt->status |= event_status; + } + + /* signal event once */ + if ( signal_event && (rt->event_id != 0) ) { + rtems_event_send(rt->event_id, event_status); + } + +} + +void b1553rt_print_dev(struct drvmgr_dev *dev, int options) +{ + rt_priv *pDev = dev->priv; + struct amba_dev_info *devinfo; + + devinfo = (struct amba_dev_info *)pDev->dev->businfo; + + /* Print */ + printf("--- B1553RT[%d] %s ---\n", pDev->minor, pDev->devName); + printf(" REGS: 0x%x\n", (unsigned int)pDev->regs); + printf(" IRQ: %d\n", pDev->irqno); + +} + +void b1553rt_print(int options) +{ + struct amba_drv_info *drv = &b1553rt_drv_info; + struct drvmgr_dev *dev; + + dev = drv->general.dev; + while(dev) { + b1553rt_print_dev(dev, options); + dev = dev->next_in_drv; + } +} diff --git a/c/src/lib/libbsp/sparc/shared/1553/gr1553b.c b/c/src/lib/libbsp/sparc/shared/1553/gr1553b.c new file mode 100644 index 0000000000..26d7b400c3 --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/1553/gr1553b.c @@ -0,0 +1,305 @@ +/* GR1553B driver, used by BC, RT and/or BM 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 +#include + +#include + +/* Driver Manager interface for BC, RT, BM, BRM, BC-BM and RT-BM */ + +#define GR1553B_WRITE_REG(adr, val) *(volatile uint32_t *)(adr) = (val) +#define GR1553B_READ_REG(adr) (*(volatile uint32_t *)(adr)) + +#define FEAT_BC 0x1 +#define FEAT_RT 0x2 +#define FEAT_BM 0x4 + +#define ALLOC_BC 0x1 +#define ALLOC_RT 0x2 +#define ALLOC_BM 0x4 + +struct gr1553_device { + struct drvmgr_dev *dev; + int features; + int alloc; +}; + +struct gr1553_device_feature { + struct gr1553_device_feature *next; + struct gr1553_device *dev; + int minor; +}; + +/* Device lists */ +struct gr1553_device_feature *gr1553_bm_root = NULL; +struct gr1553_device_feature *gr1553_rt_root = NULL; +struct gr1553_device_feature *gr1553_bc_root = NULL; + +/* Driver registered */ +int gr1553_driver_registerd = 0; + +/* Add 'feat' to linked list pointed to by 'root'. A minor is also assigned. */ +void gr1553_list_add + ( + struct gr1553_device_feature **root, + struct gr1553_device_feature *feat + ) +{ + int minor; + struct gr1553_device_feature *curr; + + if ( *root == NULL ) { + *root = feat; + feat->next = NULL; + feat->minor = 0; + return; + } + + minor = 0; +retry_new_minor: + curr = *root; + while ( curr->next ) { + if ( curr->minor == minor ) { + minor++; + goto retry_new_minor; + } + curr = curr->next; + } + + feat->next = NULL; + feat->minor = minor; + curr->next = feat; +} + +struct gr1553_device_feature *gr1553_list_find + ( + struct gr1553_device_feature *root, + int minor + ) +{ + struct gr1553_device_feature *curr = root; + while ( curr ) { + if ( curr->minor == minor ) { + return curr; + } + curr = curr->next; + } + return NULL; +} + +struct drvmgr_dev **gr1553_bc_open(int minor) +{ + struct gr1553_device_feature *feat; + + feat = gr1553_list_find(gr1553_bc_root, minor); + if ( feat == NULL ) + return NULL; + + /* Only possible to allocate is RT and BC is free, + * this is beacuse it is not possible to use the + * RT and the BC at the same time. + */ + if ( feat->dev->alloc & (ALLOC_BC|ALLOC_RT) ) + return NULL; + + /* Alloc BC device */ + feat->dev->alloc |= ALLOC_BC; + + return &feat->dev->dev; +} + +void gr1553_bc_close(struct drvmgr_dev **dev) +{ + struct gr1553_device *d = (struct gr1553_device *)dev; + + d->alloc &= ~ALLOC_BC; +} + +struct drvmgr_dev **gr1553_rt_open(int minor) +{ + struct gr1553_device_feature *feat; + + feat = gr1553_list_find(gr1553_rt_root, minor); + if ( feat == NULL ) + return NULL; + + /* Only possible to allocate is RT and BC is free, + * this is beacuse it is not possible to use the + * RT and the BC at the same time. + */ + if ( feat->dev->alloc & (ALLOC_BC|ALLOC_RT) ) + return NULL; + + /* Alloc RT device */ + feat->dev->alloc |= ALLOC_RT; + + return &feat->dev->dev; +} + +void gr1553_rt_close(struct drvmgr_dev **dev) +{ + struct gr1553_device *d = (struct gr1553_device *)dev; + + d->alloc &= ~ALLOC_RT; +} + +struct drvmgr_dev **gr1553_bm_open(int minor) +{ + struct gr1553_device_feature *feat; + + feat = gr1553_list_find(gr1553_bm_root, minor); + if ( feat == NULL ) + return NULL; + + /* Only possible to allocate is RT and BC is free, + * this is beacuse it is not possible to use the + * RT and the BC at the same time. + */ + if ( feat->dev->alloc & ALLOC_BM ) + return NULL; + + /* Alloc BM device */ + feat->dev->alloc |= ALLOC_BM; + + return &feat->dev->dev; +} + +void gr1553_bm_close(struct drvmgr_dev **dev) +{ + struct gr1553_device *d = (struct gr1553_device *)dev; + + d->alloc &= ~ALLOC_BM; +} + +int gr1553_init2(struct drvmgr_dev *dev) +{ + struct amba_dev_info *ambadev; + struct ambapp_core *pnpinfo; + struct gr1553b_regs *regs; + + /* Get device information from AMBA PnP information */ + ambadev = (struct amba_dev_info *)dev->businfo; + if ( ambadev == NULL ) { + return DRVMGR_FAIL; + } + pnpinfo = &ambadev->info; + regs = (struct gr1553b_regs *)pnpinfo->apb_slv->start; + + /* Stop IRQ */ + GR1553B_WRITE_REG(®s->imask, 0); + GR1553B_WRITE_REG(®s->irq, 0xffffffff); + /* Stop BC if not already stopped (just in case) */ + GR1553B_WRITE_REG(®s->bc_ctrl, 0x15520204); + /* Stop RT rx (just in case) */ + GR1553B_WRITE_REG(®s->rt_cfg, 0x15530000); + /* Stop BM logging (just in case) */ + GR1553B_WRITE_REG(®s->bm_ctrl, 0); + + return DRVMGR_OK; +} + +/* Register the different functionalities that the + * core supports. + */ +int gr1553_init3(struct drvmgr_dev *dev) +{ + struct amba_dev_info *ambadev; + struct ambapp_core *pnpinfo; + struct gr1553_device *priv; + struct gr1553_device_feature *feat; + struct gr1553b_regs *regs; + + priv = malloc(sizeof(struct gr1553_device)); + if ( priv == NULL ) + return DRVMGR_NOMEM; + priv->dev = dev; + priv->alloc = 0; + priv->features = 0; + dev->priv = NULL; /* Let higher level driver handle this */ + + /* Get device information from AMBA PnP information */ + ambadev = (struct amba_dev_info *)dev->businfo; + if ( ambadev == NULL ) { + return DRVMGR_FAIL; + } + pnpinfo = &ambadev->info; + regs = (struct gr1553b_regs *)pnpinfo->apb_slv->start; + + if ( GR1553B_READ_REG(®s->bm_stat) & GR1553B_BM_STAT_BMSUP ) { + priv->features |= FEAT_BM; + feat = malloc(sizeof(struct gr1553_device_feature)); + feat->dev = priv; + /* Init Minor and Next */ + gr1553_list_add(&gr1553_bm_root, feat); + } + + if ( GR1553B_READ_REG(®s->bc_stat) & GR1553B_BC_STAT_BCSUP ) { + priv->features |= FEAT_BC; + feat = malloc(sizeof(struct gr1553_device_feature)); + feat->dev = priv; + /* Init Minor and Next */ + gr1553_list_add(&gr1553_bc_root, feat); + } + + if ( GR1553B_READ_REG(®s->rt_stat) & GR1553B_RT_STAT_RTSUP ) { + priv->features |= FEAT_RT; + feat = malloc(sizeof(struct gr1553_device_feature)); + feat->dev = priv; + /* Init Minor and Next */ + gr1553_list_add(&gr1553_rt_root, feat); + } + + return DRVMGR_OK; +} + +struct drvmgr_drv_ops gr1553_ops = +{ + {NULL, gr1553_init2, gr1553_init3, NULL}, + NULL, + NULL +}; + +struct amba_dev_id gr1553_ids[] = +{ + {VENDOR_GAISLER, GAISLER_GR1553B}, + {0, 0} /* Mark end of table */ +}; + +struct amba_drv_info gr1553_drv_info = +{ + { + DRVMGR_OBJ_DRV, /* Driver */ + NULL, /* Next driver */ + NULL, /* Device list */ + DRIVER_AMBAPP_GAISLER_GR1553B_ID,/* Driver ID */ + "GR1553_DRV", /* Driver Name */ + DRVMGR_BUS_TYPE_AMBAPP, /* Bus Type */ + &gr1553_ops, + NULL, /* Funcs */ + 0, /* No devices yet */ + 0, + }, + &gr1553_ids[0] +}; + +/* Multiple drivers may call this function. The drivers that depends on + * this driver: + * - BM driver + * - BC driver + * - RT driver + */ +void gr1553_register(void) +{ + if ( gr1553_driver_registerd == 0 ) { + gr1553_driver_registerd = 1; + drvmgr_drv_register(&gr1553_drv_info.general); + } +} 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 +#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]; +} + +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(); +} + +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"); + } + } + } +} diff --git a/c/src/lib/libbsp/sparc/shared/1553/gr1553bm.c b/c/src/lib/libbsp/sparc/shared/1553/gr1553bm.c new file mode 100644 index 0000000000..1ce731ec43 --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/1553/gr1553bm.c @@ -0,0 +1,519 @@ +/* GR1553B BM 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 +#include +#include +#include + +#include +#include + + +#define GR1553BM_WRITE_MEM(adr, val) *(volatile uint32_t *)(adr) = (val) +#define GR1553BM_READ_MEM(adr) (*(volatile uint32_t *)(adr)) + +#define GR1553BM_WRITE_REG(adr, val) *(volatile uint32_t *)(adr) = (val) +#define GR1553BM_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 + +struct gr1553bm_priv { + struct drvmgr_dev **pdev; + struct gr1553b_regs *regs; + + void *buffer; + unsigned int buffer_base_hw; + unsigned int buffer_base; + unsigned int buffer_end; + unsigned int buffer_size; + unsigned int read_pos; + int started; + struct gr1553bm_config cfg; + + /* Time updated by IRQ when 24-bit Time counter overflows */ + volatile uint64_t time; +}; + +void gr1553bm_isr(void *data); + +/* Default Driver configuration */ +struct gr1553bm_config gr1553bm_default_config = +{ + /* Highest resolution, use Time overflow IRQ to track */ + .time_resolution = 0, + .time_ovf_irq = 1, + + /* No filtering, log all */ + .filt_error_options = GR1553BM_ERROPTS_ALL, + .filt_rtadr = 0xffffffff, + .filt_subadr = 0xffffffff, + .filt_mc = 0x0007ffff, + + /* 128Kbyte dynamically allocated buffer. */ + .buffer_size = 128*1024, + .buffer_custom = NULL, +}; + +void gr1553bm_register(void) +{ + /* The BM driver rely on the GR1553B Driver */ + gr1553_register(); +} + +static void gr1553bm_hw_start(struct gr1553bm_priv *priv) +{ + IRQ_GLOBAL_PREPARE(oldLevel); + + /* Enable IRQ source and mark running state */ + IRQ_GLOBAL_DISABLE(oldLevel); + + priv->started = 1; + + /* Clear old IRQ flags */ + priv->regs->irq = GR1553B_IRQ_BMD | GR1553B_IRQ_BMTOF; + + /* Unmask IRQ sources */ + if ( priv->cfg.time_ovf_irq ) { + priv->regs->imask |= GR1553B_IRQEN_BMDE | GR1553B_IRQEN_BMTOE; + } else { + priv->regs->imask |= GR1553B_IRQEN_BMDE; + } + + /* Start logging */ + priv->regs->bm_ctrl = + (priv->cfg.filt_error_options & + (GR1553B_BM_CTRL_MANL|GR1553B_BM_CTRL_UDWL|GR1553B_BM_CTRL_IMCL)) + | GR1553B_BM_CTRL_BMEN; + + IRQ_GLOBAL_ENABLE(oldLevel); +} + +static void gr1553bm_hw_stop(struct gr1553bm_priv *priv) +{ + IRQ_GLOBAL_PREPARE(oldLevel); + + IRQ_GLOBAL_DISABLE(oldLevel); + + /* Stop Logging */ + priv->regs->bm_ctrl = 0; + + /* Stop IRQ source */ + priv->regs->imask &= ~(GR1553B_IRQEN_BMDE|GR1553B_IRQEN_BMTOE); + + /* Clear IRQ flags */ + priv->regs->irq = GR1553B_IRQ_BMD | GR1553B_IRQ_BMTOF; + + priv->started = 0; + + IRQ_GLOBAL_ENABLE(oldLevel); +} + +/* Open device by number */ +void *gr1553bm_open(int minor) +{ + struct drvmgr_dev **pdev = NULL; + struct gr1553bm_priv *priv = NULL; + struct amba_dev_info *ambadev; + struct ambapp_core *pnpinfo; + + /* Allocate requested device */ + pdev = gr1553_bm_open(minor); + if ( pdev == NULL ) + goto fail; + + priv = malloc(sizeof(struct gr1553bm_priv)); + if ( priv == NULL ) + goto fail; + memset(priv, 0, sizeof(struct gr1553bm_priv)); + + /* Init BC 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 = gr1553bm_default_config; + + /* Unmask IRQs */ + gr1553bm_hw_stop(priv); + + return priv; + +fail: + if ( pdev ) + gr1553_bm_close(pdev); + if ( priv ) + free(priv); + return NULL; +} + +/* Close previously */ +void gr1553bm_close(void *bm) +{ + struct gr1553bm_priv *priv = bm; + + if ( priv->started ) { + gr1553bm_stop(bm); + } + + if ( (priv->cfg.buffer_custom == NULL) && priv->buffer ) + free(priv->buffer); + + gr1553_bm_close(priv->pdev); + free(priv); +} + +/* Configure the BM driver */ +int gr1553bm_config(void *bm, struct gr1553bm_config *cfg) +{ + struct gr1553bm_priv *priv = bm; + + if ( priv->started ) + return -1; + + /* Check Config validity? */ +/*#warning IMPLEMENT.*/ + + /* Free old buffer if dynamically allocated */ + if ( (priv->cfg.buffer_custom == NULL) && priv->buffer ) { + free(priv->buffer); + priv->buffer = NULL; + } + priv->buffer_size = cfg->buffer_size & ~0x7; /* on 8 byte bounadry */ + if ((unsigned int)cfg->buffer_custom & 1) { + /* Custom Address Given in Remote address. We need + * to convert it intoTranslate into Hardware a + * hardware accessible address + */ + priv->buffer_base_hw = (unsigned int)cfg->buffer_custom & ~1; + priv->buffer = cfg->buffer_custom; + drvmgr_translate_check( + *priv->pdev, + DMAMEM_TO_CPU, + (void *)priv->buffer_base_hw, + (void **)&priv->buffer_base, + priv->buffer_size); + } else { + if (cfg->buffer_custom == NULL) { + /* Allocate new buffer dynamically */ + priv->buffer = malloc(priv->buffer_size + 8); + if (priv->buffer == NULL) + return -1; + } else { + /* Address given in CPU accessible address, no + * translation required. + */ + priv->buffer = cfg->buffer_custom; + } + /* Align to 16 bytes */ + priv->buffer_base = ((unsigned int)priv->buffer + (8-1)) & + ~(8-1); + /* Translate address of buffer base into address that Hardware must + * use to access the buffer. + */ + drvmgr_translate_check( + *priv->pdev, + CPUMEM_TO_DMA, + (void *)priv->buffer_base, + (void **)&priv->buffer_base_hw, + priv->buffer_size); + + } + + /* Copy valid config */ + priv->cfg = *cfg; + + return 0; +} + +/* Start logging */ +int gr1553bm_start(void *bm) +{ + struct gr1553bm_priv *priv = bm; + + if ( priv->started ) + return -1; + if ( priv->buffer == NULL ) + return -2; + + /* Start at Time = 0 */ + priv->regs->bm_ttag = + priv->cfg.time_resolution << GR1553B_BM_TTAG_RES_BIT; + + /* Configure Filters */ + priv->regs->bm_adr = priv->cfg.filt_rtadr; + priv->regs->bm_subadr = priv->cfg.filt_subadr; + priv->regs->bm_mc = priv->cfg.filt_mc; + + /* Set up buffer */ + priv->regs->bm_start = priv->buffer_base_hw; + priv->regs->bm_end = priv->buffer_base_hw + priv->cfg.buffer_size - 4; + priv->regs->bm_pos = priv->buffer_base_hw; + priv->read_pos = priv->buffer_base; + priv->buffer_end = priv->buffer_base + priv->cfg.buffer_size; + + /* Register ISR handler and unmask IRQ source at IRQ controller */ + if (drvmgr_interrupt_register(*priv->pdev, 0, "gr1553bm", gr1553bm_isr, priv)) + return -3; + + /* Start hardware and set priv->started */ + gr1553bm_hw_start(priv); + + return 0; +} + +/* Stop logging */ +void gr1553bm_stop(void *bm) +{ + struct gr1553bm_priv *priv = bm; + + /* Stop Hardware */ + gr1553bm_hw_stop(priv); + + /* At this point the hardware must be stopped and IRQ + * sources unmasked. + */ + + /* Unregister ISR handler and unmask 1553 IRQ source at IRQ ctrl */ + drvmgr_interrupt_unregister(*priv->pdev, 0, gr1553bm_isr, priv); +} + +int gr1553bm_started(void *bm) +{ + return ((struct gr1553bm_priv *)bm)->started; +} + +/* Get 64-bit 1553 Time. + * + * Update software time counters and return the current time. + */ +void gr1553bm_time(void *bm, uint64_t *time) +{ + struct gr1553bm_priv *priv = bm; + unsigned int hwtime, hwtime2; + +resample: + if ( priv->started && (priv->cfg.time_ovf_irq == 0) ) { + /* Update Time overflow counter. The carry bit from Time counter + * is located in IRQ Flag. + * + * When IRQ is not used this function must be called often + * enough to avoid that the Time overflows and the carry + * bit is already set. The frequency depends on the Time + * resolution. + */ + if ( priv->regs->irq & GR1553B_IRQ_BMTOF ) { + /* Clear carry bit */ + priv->regs->irq = GR1553B_IRQ_BMTOF; + priv->time += (GR1553B_BM_TTAG_VAL + 1); + } + } + + /* Report current Time, even if stopped */ + hwtime = priv->regs->bm_ttag & GR1553B_BM_TTAG_VAL; + if ( time ) + *time = priv->time | hwtime; + + if ( priv->cfg.time_ovf_irq ) { + /* Detect wrap around */ + hwtime2 = priv->regs->bm_ttag & GR1553B_BM_TTAG_VAL; + if ( hwtime > hwtime2 ) { + /* priv->time and hwtime may be out of sync if + * IRQ updated priv->time just after bm_ttag was read + * here, we resample if we detect inconsistancy. + */ + goto resample; + } + } +} + +/* Number of entries available in DMA buffer */ +int gr1553bm_available(void *bm, int *nentries) +{ + struct gr1553bm_priv *priv = bm; + unsigned int top, bot, pos; + + if ( !priv->started ) + return -1; + + /* Get BM posistion in log */ + pos = priv->regs->bm_pos; + + /* Convert into CPU accessible address */ + pos = priv->buffer_base + (pos - priv->buffer_base_hw); + + if ( pos >= priv->read_pos ) { + top = (pos - priv->read_pos)/sizeof(struct gr1553bm_entry); + bot = 0; + } else { + top = (priv->buffer_end - priv->read_pos)/sizeof(struct gr1553bm_entry); + bot = (pos - priv->buffer_base)/sizeof(struct gr1553bm_entry); + } + + if ( nentries ) + *nentries = top+bot; + + return 0; +} + +/* Read a maximum number of entries from LOG buffer. */ +int gr1553bm_read(void *bm, struct gr1553bm_entry *dst, int *max) +{ + struct gr1553bm_priv *priv = bm; + unsigned int dest, pos, left, newPos, len; + unsigned int topAdr, botAdr, topLen, botLen; + + if ( !priv || !priv->started ) + return -1; + + left = *max; + pos = priv->regs->bm_pos & ~0x7; + + /* Convert into CPU accessible address */ + pos = priv->buffer_base + (pos - priv->buffer_base_hw); + + if ( (pos == priv->read_pos) || (left < 1) ) { + /* No data available */ + *max = 0; + return 0; + } + newPos = 0; + + /* Addresses and lengths of BM log buffer */ + if ( pos >= priv->read_pos ) { + /* Read Top only */ + topAdr = priv->read_pos; + botAdr = 0; + topLen = (pos - priv->read_pos)/sizeof(struct gr1553bm_entry); + botLen = 0; + } else { + /* Read Top and Bottom */ + topAdr = priv->read_pos; + botAdr = priv->buffer_base; + topLen = (priv->buffer_end - priv->read_pos)/sizeof(struct gr1553bm_entry); + botLen = (pos - priv->buffer_base)/sizeof(struct gr1553bm_entry); + } + + dest = (unsigned int)dst; + if ( topLen > 0 ) { + /* Copy from top area first */ + if ( topLen > left ) { + len = left; + left = 0; + } else { + len = topLen; + left -= topLen; + } + newPos = topAdr + (len * sizeof(struct gr1553bm_entry)); + if ( newPos >= priv->buffer_end ) + newPos -= priv->buffer_size; + if ( priv->cfg.copy_func ) { + dest += priv->cfg.copy_func( + dest, /*Optional Destination*/ + (void *)topAdr, /* DMA start address */ + len, /* Number of entries */ + priv->cfg.copy_func_arg /* Custom ARG */ + ); + } else { + memcpy( (void *)dest, + (void *)topAdr, + len * sizeof(struct gr1553bm_entry)); + dest += len * sizeof(struct gr1553bm_entry); + } + } + + if ( (botLen > 0) && (left > 0) ) { + /* Copy bottom area last */ + if ( botLen > left ) { + len = left; + left = 0; + } else { + len = botLen; + left -= botLen; + } + newPos = botAdr + (len * sizeof(struct gr1553bm_entry)); + + if ( priv->cfg.copy_func ) { + priv->cfg.copy_func( + dest, /*Optional Destination*/ + (void *)botAdr, /* DMA start address */ + len, /* Number of entries */ + priv->cfg.copy_func_arg /* Custom ARG */ + ); + } else { + memcpy( (void *)dest, + (void *)botAdr, + len * sizeof(struct gr1553bm_entry)); + } + } + + /* Remember last read posistion in buffer */ + /*printf("New pos: 0x%08x (0x%08x), %d\n", newPos, priv->read_pos, *max - left);*/ + priv->read_pos = newPos; + + /* Return number of entries read */ + *max = *max - left; + + return 0; +} + +/* Note: This is a shared interrupt handler, with BC/RT driver + * we must determine the cause of IRQ before handling it. + */ +void gr1553bm_isr(void *data) +{ + struct gr1553bm_priv *priv = data; + uint32_t irqflag; + + /* Get Causes */ + irqflag = priv->regs->irq & (GR1553B_IRQ_BMD | GR1553B_IRQ_BMTOF); + + /* Check spurious IRQs */ + if ( (irqflag == 0) || (priv->started == 0) ) + return; + + if ( (irqflag & GR1553B_IRQ_BMTOF) && priv->cfg.time_ovf_irq ) { + /* 1553 Time Over flow. Time is 24-bits */ + priv->time += (GR1553B_BM_TTAG_VAL + 1); + + /* Clear cause handled */ + priv->regs->irq = GR1553B_IRQ_BMTOF; + } + + if ( irqflag & GR1553B_IRQ_BMD ) { + /* BM DMA ERROR. Fatal error, we stop BM hardware and let + * user take care of it. From now on all calls will result + * in an error because the BM is stopped (priv->started=0). + */ + + /* Clear cause handled */ + priv->regs->irq = GR1553B_IRQ_BMD; + + if ( priv->cfg.dma_error_isr ) + priv->cfg.dma_error_isr(data, priv->cfg.dma_error_arg); + + gr1553bm_hw_stop(priv); + } +} 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 +#include +#include +#include + +#include +#include + +#include +#include + +#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; ilists[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; iswbds[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; iswbds[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; ibd_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; ibd_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; icfg->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; ilists[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; +} -- cgit v1.2.3