/* * This file contains the GRSPW SpaceWire Driver for LEON2 and LEON3. * * COPYRIGHT (c) 2006 * Cobham Gaisler AB. * * The license and distribution terms for this file may be * found in the file LICENSE in this distribution or at * http://www.rtems.org/license/LICENSE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #define DBGSPW_IOCALLS 1 #define DBGSPW_TX 2 #define DBGSPW_RX 4 #define DBGSPW_IOCTRL 1 #define DBGSPW_DUMP 16 #define DEBUG_SPACEWIRE_FLAGS (DBGSPW_IOCALLS | DBGSPW_TX | DBGSPW_RX ) /* #define DEBUG_SPACEWIRE_ONOFF */ #ifdef DEBUG_SPACEWIRE_ONOFF #define SPACEWIRE_DBG(fmt, args...) do { { printk(" : %03d @ %18s()]:" fmt , __LINE__,__FUNCTION__,## args); }} while(0) #define SPACEWIRE_DBG2(fmt) do { { printk(" : %03d @ %18s()]:" fmt , __LINE__,__FUNCTION__); }} while(0) #define SPACEWIRE_DBGC(c,fmt, args...) do { if (DEBUG_SPACEWIRE_FLAGS & c) { printk(" : %03d @ %18s()]:" fmt , __LINE__,__FUNCTION__,## args); }} while(0) #else #define SPACEWIRE_DBG(fmt, args...) #define SPACEWIRE_DBG2(fmt, args...) #define SPACEWIRE_DBGC(c, fmt, args...) #endif typedef struct { volatile unsigned int ctrl; volatile unsigned int status; volatile unsigned int nodeaddr; volatile unsigned int clkdiv; volatile unsigned int destkey; volatile unsigned int time; volatile unsigned int timer; volatile unsigned int pad; volatile unsigned int dma0ctrl; volatile unsigned int dma0rxmax; volatile unsigned int dma0txdesc; volatile unsigned int dma0rxdesc; /* For GRSPW core 2 and onwards */ volatile unsigned int dma0addr; } LEON3_SPACEWIRE_Regs_Map; typedef struct { volatile unsigned int ctrl; volatile unsigned int addr; } SPACEWIRE_RXBD; typedef struct { volatile unsigned int ctrl; volatile unsigned int addr_header; volatile unsigned int len; volatile unsigned int addr_data; } SPACEWIRE_TXBD; #define SPACEWIRE_INIT_TIMEOUT 10 #define SPACEWIRE_BDTABLE_SIZE 0x400 #define SPACEWIRE_TXD_SIZE 1024 #define SPACEWIRE_TXH_SIZE 64 #define SPACEWIRE_RXPCK_SIZE 1024 #define SPACEWIRE_TXBUFS_NR 64 #define SPACEWIRE_RXBUFS_NR 128 #define BUFMEM_PER_LINK (SPACEWIRE_TXBUFS_NR*(SPACEWIRE_TXD_SIZE+SPACEWIRE_TXH_SIZE) + SPACEWIRE_RXBUFS_NR*SPACEWIRE_RXPCK_SIZE) typedef struct { /* configuration parameters */ struct drvmgr_dev *dev; /* Driver manager device */ char devName[32]; /* Device Name */ LEON3_SPACEWIRE_Regs_Map *regs; spw_config config; unsigned int tx_all_in_use; unsigned int tx_sent; unsigned int tx_cur; unsigned int rxcur; unsigned int rxbufcur; unsigned int txdbufsize; unsigned int txhbufsize; unsigned int rxbufsize; unsigned int txbufcnt; unsigned int rxbufcnt; /* DMA Area set by user */ unsigned int rx_dma_area; unsigned int tx_data_dma_area; unsigned int tx_hdr_dma_area; unsigned int bd_dma_area; /* statistics */ spw_stats stat; unsigned int _ptr_rxbuf0; char *ptr_rxbuf0; char *ptr_txdbuf0; char *ptr_txhbuf0; char *_ptr_bd0, *ptr_bd0; char *ptr_rxbuf0_remote; char *ptr_txdbuf0_remote; char *ptr_txhbuf0_remote; char *ptr_bd0_remote; unsigned int irq; int minor; int core_ver; int open; int running; unsigned int core_freq_khz; unsigned int rtimeout; /* semaphores*/ rtems_id txsp; rtems_id rxsp; SPACEWIRE_RXBD *rx; SPACEWIRE_TXBD *tx; unsigned int rx_remote; unsigned int tx_remote; } GRSPW_DEV; /* Function pointer called upon timecode receive */ void (*grspw_timecode_callback) (void *pDev, void *regs, int minor, unsigned int tc) = NULL; #ifdef GRSPW_DONT_BYPASS_CACHE #define _SPW_READ(address) (*(volatile unsigned int *)(address)) #define _MEM_READ8(address) (*(volatile unsigned char *)(address)) #define _MEM_READ32(address) (*(volatile unsigned int *)(address)) #else #define _SPW_READ(address) grlib_read_uncached32((unsigned int) address) #define _MEM_READ8(address) grlib_read_uncached8((unsigned int) address) #define _MEM_READ32(address) grlib_read_uncached32((unsigned int) address) #endif #define MEM_READ8(addr) _MEM_READ8((volatile void *)(addr)) #define MEM_READ32(addr) _MEM_READ32((volatile void *)(addr)) #define SPW_READ(addr) _SPW_READ((volatile void *)(addr)) #define SPW_WRITE(addr,v) (*(volatile unsigned int *)addr)=v #define SPW_REG(c,r) (c->regs->r) #define SPW_REG_CTRL(c) SPW_REG(c,ctrl) #define SPW_REG_STATUS(c) SPW_REG(c,status) #define SPW_REG_NODEADDR(c) SPW_REG(c,nodeaddr) #define SPW_CTRL_READ(c) SPW_READ(&SPW_REG_CTRL(c)) #define SPW_CTRL_WRITE(c,v) SPW_WRITE(&SPW_REG_CTRL(c),v) #define SPW_STATUS_READ(c) SPW_READ(&SPW_REG_STATUS(c)) #define SPW_STATUS_WRITE(c,v) SPW_WRITE(&SPW_REG_STATUS(c),v) #define SPW_LINKSTATE(c) (((c) >> 21) & 0x7) #define SPACEWIRE_RXNR(c) ((c&~(SPACEWIRE_BDTABLE_SIZE-1))>>3) #define SPACEWIRE_TXNR(c) ((c&~(SPACEWIRE_BDTABLE_SIZE-1))>>4) #define SPW_RXBD_LENGTH 0x1ffffff #define SPW_RXBD_EN (1 << 25) #define SPW_RXBD_WR (1 << 26) #define SPW_RXBD_IE (1 << 27) #define SPW_RXBD_EEP (1 << 28) #define SPW_RXBD_EHC (1 << 29) #define SPW_RXBD_EDC (1 << 30) #define SPW_RXBD_ETR (1 << 31) #define SPW_RXBD_ERROR (SPW_RXBD_EEP | \ SPW_RXBD_ETR) #define SPW_RXBD_RMAPERROR (SPW_RXBD_EHC | SPW_RXBD_EDC) #define SPW_TXBD_LENGTH 0xffffff #define SPW_TXBD_EN (1 << 12) #define SPW_TXBD_WR (1 << 13) #define SPW_TXBD_IE (1 << 14) #define SPW_TXBD_LE (1 << 15) #define SPW_TXBD_HC (1 << 16) #define SPW_TXBD_DC (1 << 17) #define SPW_TXBD_ERROR (SPW_TXBD_LE) #define SPW_CTRL_LINKDISABLED (1 << 0) #define SPW_CTRL_LINKSTART (1 << 1) #define SPW_CTRL_AUTOSTART (1 << 2) #define SPW_CTRL_IE (1 << 3) #define SPW_CTRL_TI (1 << 4) #define SPW_CTRL_PM (1 << 5) #define SPW_CTRL_RESET (1 << 6) #define SPW_CTRL_TQ (1 << 8) #define SPW_CTRL_LI (1 << 9) #define SPW_CTRL_TT (1 << 10) #define SPW_CTRL_TR (1 << 11) #define SPW_CTRL_RE (1 << 16) #define SPW_CTRL_RD (1 << 17) #define SPW_CTRL_RC (1 << 29) #define SPW_CTRL_RX (1 << 30) #define SPW_CTRL_RA (1 << 31) #define SPW_STATUS_TO (1 << 0) #define SPW_STATUS_CE (1 << 1) #define SPW_STATUS_ER (1 << 2) #define SPW_STATUS_DE (1 << 3) #define SPW_STATUS_PE (1 << 4) #define SPW_STATUS_WE (1 << 6) #define SPW_STATUS_IA (1 << 7) #define SPW_STATUS_EE (1 << 8) #define SPW_DMACTRL_TXEN (1 << 0) #define SPW_DMACTRL_RXEN (1 << 1) #define SPW_DMACTRL_TXIE (1 << 2) #define SPW_DMACTRL_RXIE (1 << 3) #define SPW_DMACTRL_AI (1 << 4) #define SPW_DMACTRL_PS (1 << 5) #define SPW_DMACTRL_PR (1 << 6) #define SPW_DMACTRL_TA (1 << 7) #define SPW_DMACTRL_RA (1 << 8) #define SPW_DMACTRL_AT (1 << 9) #define SPW_DMACTRL_RX (1 << 10) #define SPW_DMACTRL_RD (1 << 11) #define SPW_DMACTRL_NS (1 << 12) #define SPW_PREPAREMASK_TX (SPW_DMACTRL_RXEN | SPW_DMACTRL_RXIE | SPW_DMACTRL_PS | SPW_DMACTRL_TA | SPW_DMACTRL_RD | SPW_DMACTRL_NS) #define SPW_PREPAREMASK_RX (SPW_DMACTRL_TXEN | SPW_DMACTRL_TXIE | SPW_DMACTRL_AI | SPW_DMACTRL_PR | SPW_DMACTRL_RA) static int grspw_hw_init(GRSPW_DEV *pDev); static int grspw_hw_send(GRSPW_DEV *pDev, unsigned int hlen, char *hdr, unsigned int dlen, char *data, unsigned int options); static int grspw_hw_receive(GRSPW_DEV *pDev,char *b,int c); static int grspw_hw_startup (GRSPW_DEV *pDev, int timeout); static int grspw_hw_stop (GRSPW_DEV *pDev, int rx, int tx); static void grspw_hw_wait_rx_inactive(GRSPW_DEV *pDev); static int grspw_hw_waitlink (GRSPW_DEV *pDev, int timeout); static void grspw_hw_reset(GRSPW_DEV *pDev); static void grspw_hw_read_config(GRSPW_DEV *pDev); static void check_rx_errors(GRSPW_DEV *pDev, int ctrl); static void grspw_rxnext(GRSPW_DEV *pDev); static void grspw_interrupt(void *arg); static int grspw_buffer_alloc(GRSPW_DEV *pDev); static rtems_device_driver grspw_initialize( rtems_device_major_number major, rtems_device_minor_number minor, void * arg ); static rtems_device_driver grspw_open( rtems_device_major_number major, rtems_device_minor_number minor, void * arg ); static rtems_device_driver grspw_close( rtems_device_major_number major, rtems_device_minor_number minor, void * arg ); static rtems_device_driver grspw_read( rtems_device_major_number major, rtems_device_minor_number minor, void * arg ); static rtems_device_driver grspw_write( rtems_device_major_number major, rtems_device_minor_number minor, void * arg ); static rtems_device_driver grspw_control( rtems_device_major_number major, rtems_device_minor_number minor, void * arg ); #define GRSPW_DRIVER_TABLE_ENTRY \ { grspw_initialize, \ grspw_open, \ grspw_close, \ grspw_read, \ grspw_write, \ grspw_control } static rtems_driver_address_table grspw_driver = GRSPW_DRIVER_TABLE_ENTRY; static int grspw_driver_io_registered = 0; static rtems_device_major_number grspw_driver_io_major = 0; /******************* Driver manager interface ***********************/ /* Driver prototypes */ int grspw_register_io(rtems_device_major_number *m); int grspw_device_init(GRSPW_DEV *pDev); int grspw_init2(struct drvmgr_dev *dev); int grspw_init3(struct drvmgr_dev *dev); struct drvmgr_drv_ops grspw_ops = { .init = {NULL, grspw_init2, grspw_init3, NULL}, .remove = NULL, .info = NULL }; struct amba_dev_id grspw_ids[] = { {VENDOR_GAISLER, GAISLER_SPW}, {VENDOR_GAISLER, GAISLER_SPW2}, {VENDOR_GAISLER, GAISLER_SPW2_DMA}, {0, 0} /* Mark end of table */ }; struct amba_drv_info grspw_drv_info = { { DRVMGR_OBJ_DRV, /* Driver */ NULL, /* Next driver */ NULL, /* Device list */ DRIVER_AMBAPP_GAISLER_GRSPW_ID, /* Driver ID */ "GRSPW_DRV", /* Driver Name */ DRVMGR_BUS_TYPE_AMBAPP, /* Bus Type */ &grspw_ops, NULL, /* Funcs */ 0, /* No devices yet */ 0, }, &grspw_ids[0] }; void grspw_register_drv (void) { SPACEWIRE_DBG("Registering GRSPW driver\n"); drvmgr_drv_register(&grspw_drv_info.general); } int grspw_init2(struct drvmgr_dev *dev) { GRSPW_DEV *priv; SPACEWIRE_DBG("GRSPW[%d] on bus %s\n", dev->minor_drv, dev->parent->dev->name); priv = dev->priv = grlib_calloc(1, sizeof(*priv)); if ( !priv ) return DRVMGR_NOMEM; priv->dev = dev; /* This core will not find other cores, so we wait for init2() */ return DRVMGR_OK; } int grspw_init3(struct drvmgr_dev *dev) { GRSPW_DEV *priv; char prefix[32]; rtems_status_code status; priv = dev->priv; /* Do initialization */ if ( grspw_driver_io_registered == 0) { /* Register the I/O driver only once for all cores */ if ( grspw_register_io(&grspw_driver_io_major) ) { /* Failed to register I/O driver */ free(dev->priv); dev->priv = NULL; return DRVMGR_FAIL; } grspw_driver_io_registered = 1; } /* I/O system registered and initialized * Now we take care of device initialization. */ /* Get frequency in Hz */ if ( drvmgr_freq_get(dev, DEV_APB_SLV, &priv->core_freq_khz) ) { return DRVMGR_FAIL; } /* Convert from Hz -> kHz */ priv->core_freq_khz = priv->core_freq_khz / 1000; if ( grspw_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/grspw%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/%sgrspw%d", prefix, dev->minor_bus); } /* Register Device */ status = rtems_io_register_name(priv->devName, grspw_driver_io_major, dev->minor_drv); if (status != RTEMS_SUCCESSFUL) { return DRVMGR_FAIL; } return DRVMGR_OK; } /******************* Driver Implementation ***********************/ int grspw_register_io(rtems_device_major_number *m) { rtems_status_code r; if ((r = rtems_io_register_driver(0, &grspw_driver, m)) == RTEMS_SUCCESSFUL) { SPACEWIRE_DBG("GRSPW driver successfully registered, major: %d\n", *m); } else { switch(r) { case RTEMS_TOO_MANY: printk("GRSPW rtems_io_register_driver failed: RTEMS_TOO_MANY\n"); return -1; case RTEMS_INVALID_NUMBER: printk("GRSPW rtems_io_register_driver failed: RTEMS_INVALID_NUMBER\n"); return -1; case RTEMS_RESOURCE_IN_USE: printk("GRSPW rtems_io_register_driver failed: RTEMS_RESOURCE_IN_USE\n"); return -1; default: printk("GRSPW rtems_io_register_driver failed\n"); return -1; } } return 0; } int grspw_device_init(GRSPW_DEV *pDev) { struct amba_dev_info *ambadev; struct ambapp_core *pnpinfo; union drvmgr_key_value *value; /* Get device information from AMBA PnP information */ ambadev = (struct amba_dev_info *)pDev->dev->businfo; if ( ambadev == NULL ) { return -1; } pnpinfo = &ambadev->info; pDev->irq = pnpinfo->irq; pDev->regs = (LEON3_SPACEWIRE_Regs_Map *)pnpinfo->apb_slv->start; pDev->minor = pDev->dev->minor_drv; /* Get SpaceWire core version */ switch( pnpinfo->device ) { case GAISLER_SPW: pDev->core_ver = 1; break; case GAISLER_SPW2: pDev->core_ver = 2; break; case GAISLER_SPW2_DMA: pDev->core_ver = 3; break; default: return -1; } /* initialize the code with some resonable values, * actual initialization is done later using ioctl(fd) * on the opened device */ pDev->config.rxmaxlen = SPACEWIRE_RXPCK_SIZE; pDev->txdbufsize = SPACEWIRE_TXD_SIZE; pDev->txhbufsize = SPACEWIRE_TXH_SIZE; pDev->rxbufsize = SPACEWIRE_RXPCK_SIZE; pDev->txbufcnt = SPACEWIRE_TXBUFS_NR; pDev->rxbufcnt = SPACEWIRE_RXBUFS_NR; pDev->_ptr_rxbuf0 = 0; pDev->ptr_rxbuf0 = 0; pDev->ptr_txdbuf0 = 0; pDev->ptr_txhbuf0 = 0; pDev->ptr_bd0 = 0; pDev->rx_dma_area = 0; pDev->tx_data_dma_area = 0; pDev->tx_hdr_dma_area = 0; pDev->bd_dma_area = 0; /* Get Configuration from Bus resources (Let user override defaults) */ value = drvmgr_dev_key_get(pDev->dev, "txBdCnt", DRVMGR_KT_INT); if ( value ) pDev->txbufcnt = value->i; value = drvmgr_dev_key_get(pDev->dev, "rxBdCnt", DRVMGR_KT_INT); if ( value ) pDev->rxbufcnt = value->i; value = drvmgr_dev_key_get(pDev->dev, "txDataSize", DRVMGR_KT_INT); if ( value ) pDev->txdbufsize = value->i; value = drvmgr_dev_key_get(pDev->dev, "txHdrSize", DRVMGR_KT_INT); if ( value ) pDev->txhbufsize = value->i; value = drvmgr_dev_key_get(pDev->dev, "rxPktSize", DRVMGR_KT_INT); if ( value ) { pDev->rxbufsize = value->i; pDev->config.rxmaxlen = pDev->rxbufsize; } value = drvmgr_dev_key_get(pDev->dev, "rxDmaArea", DRVMGR_KT_INT); if ( value ) pDev->rx_dma_area = value->i; value = drvmgr_dev_key_get(pDev->dev, "txDataDmaArea", DRVMGR_KT_INT); if ( value ) pDev->tx_data_dma_area = value->i; value = drvmgr_dev_key_get(pDev->dev, "txHdrDmaArea", DRVMGR_KT_INT); if ( value ) pDev->tx_hdr_dma_area = value->i; value = drvmgr_dev_key_get(pDev->dev, "bdDmaArea", DRVMGR_KT_INT); if ( value ) pDev->bd_dma_area = value->i; if (grspw_buffer_alloc(pDev)) return RTEMS_NO_MEMORY; /* Create semaphores */ rtems_semaphore_create( rtems_build_name('T', 'x', 'S', '0' + pDev->minor), 0, RTEMS_FIFO | RTEMS_SIMPLE_BINARY_SEMAPHORE | RTEMS_NO_INHERIT_PRIORITY | \ RTEMS_NO_PRIORITY_CEILING, 0, &(pDev->txsp)); rtems_semaphore_create( rtems_build_name('R', 'x', 'S', '0' + pDev->minor), 0, RTEMS_FIFO | RTEMS_SIMPLE_BINARY_SEMAPHORE | RTEMS_NO_INHERIT_PRIORITY | \ RTEMS_NO_PRIORITY_CEILING, 0, &(pDev->rxsp)); grspw_hw_init(pDev); return 0; } /* Get a value at least 6.4us in number of clock cycles */ static unsigned int grspw_calc_timer64(int freq_khz){ unsigned int timer64 = (freq_khz*64+9999)/10000; return timer64 & 0xfff; } /* Get a value at least 850ns in number of clock cycles - 3 */ static unsigned int grspw_calc_disconnect(int freq_khz){ unsigned int disconnect = ((freq_khz*85+99999)/100000) - 3; return disconnect & 0x3ff; } static int grspw_buffer_alloc(GRSPW_DEV *pDev) { /* RX DMA AREA */ if (pDev->rx_dma_area & 1) { /* Address given in remote address */ pDev->ptr_rxbuf0_remote = (char *)(pDev->rx_dma_area & ~1); drvmgr_translate_check( pDev->dev, DMAMEM_TO_CPU, (void *)pDev->ptr_rxbuf0_remote, (void **)&pDev->ptr_rxbuf0, pDev->rxbufsize * pDev->rxbufcnt); } else { if (pDev->rx_dma_area == 0) { if (pDev->_ptr_rxbuf0) free((void *)pDev->_ptr_rxbuf0); pDev->_ptr_rxbuf0 = (unsigned int) grlib_malloc( pDev->rxbufsize * pDev->rxbufcnt+4); pDev->ptr_rxbuf0 = (char *)((pDev->_ptr_rxbuf0+7)&~7); if ( !pDev->ptr_rxbuf0 ) return 1; } else { pDev->ptr_rxbuf0 = (char *)pDev->rx_dma_area; } drvmgr_translate_check( pDev->dev, CPUMEM_TO_DMA, (void *)pDev->ptr_rxbuf0, (void **)&pDev->ptr_rxbuf0_remote, pDev->rxbufsize * pDev->rxbufcnt); } /* TX-DATA DMA AREA */ if (pDev->tx_data_dma_area & 1) { /* Address given in remote address */ pDev->ptr_txdbuf0_remote = (char*)(pDev->tx_data_dma_area & ~1); drvmgr_translate_check( pDev->dev, DMAMEM_TO_CPU, (void *)pDev->ptr_txdbuf0_remote, (void **)&pDev->ptr_txdbuf0, pDev->txdbufsize * pDev->txbufcnt); } else { if (pDev->tx_data_dma_area == 0) { if (pDev->ptr_txdbuf0) free(pDev->ptr_txdbuf0); pDev->ptr_txdbuf0 = (char *) grlib_malloc( pDev->txdbufsize * pDev->txbufcnt); if (!pDev->ptr_txdbuf0) return 1; } else { pDev->ptr_txdbuf0 = (char *)pDev->tx_data_dma_area; } drvmgr_translate_check( pDev->dev, CPUMEM_TO_DMA, (void *)pDev->ptr_txdbuf0, (void **)&pDev->ptr_txdbuf0_remote, pDev->txdbufsize * pDev->txbufcnt); } /* TX-HEADER DMA AREA */ if (pDev->tx_hdr_dma_area & 1) { /* Address given in remote address */ pDev->ptr_txhbuf0_remote = (char *)(pDev->tx_hdr_dma_area & ~1); drvmgr_translate_check( pDev->dev, DMAMEM_TO_CPU, (void *)pDev->ptr_txhbuf0_remote, (void **)&pDev->ptr_txhbuf0, pDev->txhbufsize * pDev->txbufcnt); } else { if (pDev->tx_hdr_dma_area == 0) { if (pDev->ptr_txhbuf0) free(pDev->ptr_txhbuf0); pDev->ptr_txhbuf0 = (char *) grlib_malloc( pDev->txhbufsize * pDev->txbufcnt); if (!pDev->ptr_txhbuf0) return 1; } else { pDev->ptr_txhbuf0 = (char *)pDev->tx_hdr_dma_area; } drvmgr_translate_check( pDev->dev, CPUMEM_TO_DMA, (void *)pDev->ptr_txhbuf0, (void **)&pDev->ptr_txhbuf0_remote, pDev->txhbufsize * pDev->txbufcnt); } /* DMA DESCRIPTOR TABLES */ if (pDev->bd_dma_area & 1) { /* Address given in remote address */ pDev->ptr_bd0_remote = (char *)(pDev->bd_dma_area & ~1); drvmgr_translate_check( pDev->dev, DMAMEM_TO_CPU, (void *)pDev->ptr_bd0_remote, (void **)&pDev->ptr_bd0, 2 * SPACEWIRE_BDTABLE_SIZE); } else { if (pDev->bd_dma_area == 0) { if (pDev->_ptr_bd0) free(pDev->_ptr_bd0); pDev->_ptr_bd0 = rtems_heap_allocate_aligned_with_boundary( SPACEWIRE_BDTABLE_SIZE*2, 1024, 0); if (!pDev->_ptr_bd0) return 1; pDev->ptr_bd0 = (char *)pDev->_ptr_bd0; } else { pDev->ptr_bd0 = (char *)pDev->bd_dma_area; } drvmgr_translate_check( pDev->dev, CPUMEM_TO_DMA, (void *)pDev->ptr_bd0, (void **)&pDev->ptr_bd0_remote, 2 * SPACEWIRE_BDTABLE_SIZE); } return 0; } static void grspw_interrupt(void *arg) { GRSPW_DEV *pDev = (GRSPW_DEV *)arg; int dmactrl; int status; int ctrl; unsigned int timecode; status = SPW_STATUS_READ(pDev); /*SPW_STATUS_WRITE(pDev, SPW_STATUS_CE | SPW_STATUS_ER | SPW_STATUS_DE | SPW_STATUS_PE | SPW_STATUS_WE | SPW_STATUS_IA | SPW_STATUS_EE | SPW_STATUS_TO);*/ SPW_STATUS_WRITE(pDev, status & (SPW_STATUS_CE | SPW_STATUS_ER | SPW_STATUS_DE | SPW_STATUS_PE | SPW_STATUS_WE | SPW_STATUS_IA | SPW_STATUS_EE)); /* Make sure to put the timecode handling first in order to get the smallest * possible interrupt latency */ if ( (status & SPW_STATUS_TO) && (grspw_timecode_callback != NULL) ) { /* Timecode received. Let custom function handle this */ SPW_STATUS_WRITE(pDev, SPW_STATUS_TO); timecode = SPW_READ(&pDev->regs->time); (grspw_timecode_callback)(pDev,pDev->regs,pDev->minor,timecode); } /* Clear SPW_DMACTRL_PR if set */ dmactrl = SPW_READ(&pDev->regs->dma0ctrl); /*SPW_WRITE(&pDev->regs->dma0ctrl, dmactrl | SPW_DMACTRL_PR);*/ SPW_WRITE(&pDev->regs->dma0ctrl, dmactrl); /* If linkinterrupts are enabled check if it was a linkerror irq and then send an event to the process set in the config */ if (pDev->config.link_err_irq) { if (status & (SPW_STATUS_CE | SPW_STATUS_ER | SPW_STATUS_DE | SPW_STATUS_PE | SPW_STATUS_WE)) { rtems_event_send(pDev->config.event_id, SPW_LINKERR_EVENT); if (pDev->config.disable_err) { /* disable link*/ SPW_CTRL_WRITE(pDev, (SPW_CTRL_READ(pDev) & 0xFFFFFFFC) | SPW_CTRL_LINKDISABLED); pDev->config.linkdisabled = 1; pDev->config.linkstart = 0; pDev->running = 0; } } } if (status & SPW_STATUS_CE) { pDev->stat.credit_err++; } if (status & SPW_STATUS_ER) { pDev->stat.escape_err++; } if (status & SPW_STATUS_DE) { pDev->stat.disconnect_err++; } if (status & SPW_STATUS_PE) { pDev->stat.parity_err++; } if (status & SPW_STATUS_WE) { pDev->stat.write_sync_err++; } if (status & SPW_STATUS_IA) { pDev->stat.invalid_address++; } if (status & SPW_STATUS_EE) { pDev->stat.early_ep++; } /* Check for tx interrupts */ while( (pDev->tx_sent != pDev->tx_cur) || pDev->tx_all_in_use) { /* Has this descriptor been sent? */ ctrl = SPW_READ((volatile void *)&pDev->tx[pDev->tx_sent].ctrl); if ( ctrl & SPW_TXBD_EN ) { break; } /* Yes, increment status counters & tx_sent so we can use this descriptor to send more packets with */ pDev->stat.packets_sent++; rtems_semaphore_release(pDev->txsp); if ( ctrl & SPW_TXBD_LE ) { pDev->stat.tx_link_err++; } /* step to next descriptor */ pDev->tx_sent = (pDev->tx_sent + 1) % pDev->txbufcnt; pDev->tx_all_in_use = 0; /* not all of the descriptors can be in use since we just freed one. */ } /* Check for rx interrupts */ if (dmactrl & SPW_DMACTRL_PR) { rtems_semaphore_release(pDev->rxsp); } } static rtems_device_driver grspw_initialize( rtems_device_major_number major, rtems_device_minor_number minor, void *arg ) { /* Initialize device-common data structures here */ return RTEMS_SUCCESSFUL; } static rtems_device_driver grspw_open( rtems_device_major_number major, rtems_device_minor_number minor, void * arg ) { GRSPW_DEV *pDev; struct drvmgr_dev *dev; SPACEWIRE_DBGC(DBGSPW_IOCALLS, "open [%i,%i]\n", major, minor); if ( drvmgr_get_dev(&grspw_drv_info.general, minor, &dev) ) { SPACEWIRE_DBG("Wrong minor %d\n", minor); return RTEMS_INVALID_NAME; } pDev = (GRSPW_DEV *)dev->priv; if ( pDev->open ) return RTEMS_RESOURCE_IN_USE; /* Mark device open */ pDev->open = 1; pDev->stat.tx_link_err = 0; pDev->stat.rx_rmap_header_crc_err = 0; pDev->stat.rx_rmap_data_crc_err = 0; pDev->stat.rx_eep_err = 0; pDev->stat.rx_truncated = 0; pDev->stat.parity_err = 0; pDev->stat.escape_err = 0; pDev->stat.credit_err = 0; pDev->stat.write_sync_err = 0; pDev->stat.disconnect_err = 0; pDev->stat.early_ep = 0; pDev->stat.invalid_address = 0; pDev->stat.packets_sent = 0; pDev->stat.packets_received = 0; pDev->config.rm_prot_id = 0; pDev->config.keep_source = 0; pDev->config.check_rmap_err = 0; pDev->config.tx_blocking = 0; pDev->config.tx_block_on_full = 0; pDev->config.rx_blocking = 0; pDev->config.disable_err = 0; pDev->config.link_err_irq = 0; pDev->config.event_id = 0; pDev->config.rtimeout = 0; pDev->running = 0; pDev->core_freq_khz = 0; /* Reset Core */ grspw_hw_reset(pDev); /* Read default configuration */ grspw_hw_read_config(pDev); return RTEMS_SUCCESSFUL; } static rtems_device_driver grspw_close( rtems_device_major_number major, rtems_device_minor_number minor, void * arg ) { GRSPW_DEV *pDev; struct drvmgr_dev *dev; if ( drvmgr_get_dev(&grspw_drv_info.general, minor, &dev) ) { return RTEMS_INVALID_NAME; } pDev = (GRSPW_DEV *)dev->priv; SPACEWIRE_DBGC(DBGSPW_IOCALLS, "close [%i,%i]\n", major, minor); rtems_semaphore_delete(pDev->txsp); rtems_semaphore_delete(pDev->rxsp); grspw_hw_stop(pDev,1,1); grspw_hw_reset(pDev); /* Mark device closed - not open */ pDev->open = 0; return RTEMS_SUCCESSFUL; } static rtems_device_driver grspw_read( rtems_device_major_number major, rtems_device_minor_number minor, void * arg ) { rtems_libio_rw_args_t *rw_args; unsigned int count = 0; GRSPW_DEV *pDev; struct drvmgr_dev *dev; int status; if ( drvmgr_get_dev(&grspw_drv_info.general, minor, &dev) ) { return RTEMS_INVALID_NAME; } pDev = (GRSPW_DEV *)dev->priv; rw_args = (rtems_libio_rw_args_t *) arg; /* is link up? */ if ( !pDev->running ) { return RTEMS_INVALID_NAME; } if ((rw_args->count < 1) || (rw_args->buffer == NULL)) { return RTEMS_INVALID_NAME; } SPACEWIRE_DBGC(DBGSPW_IOCALLS, "read [%i,%i]: buf:0x%x len:%i \n", major, minor, (unsigned int)rw_args->buffer, rw_args->count); while ( (count = grspw_hw_receive(pDev, rw_args->buffer, rw_args->count)) == 0) { /* wait a moment for any descriptors to get available * * Semaphore is signaled by interrupt handler */ if (pDev->config.rx_blocking) { SPACEWIRE_DBG2("Rx blocking\n"); if ( pDev->config.rtimeout ) { status = rtems_semaphore_obtain(pDev->rxsp, RTEMS_WAIT, pDev->config.rtimeout); if ( status == RTEMS_TIMEOUT ) return RTEMS_TIMEOUT; } else { rtems_semaphore_obtain(pDev->rxsp, RTEMS_WAIT, RTEMS_NO_TIMEOUT); } } else { SPACEWIRE_DBG2("Rx non blocking\n"); return RTEMS_RESOURCE_IN_USE; } } #ifdef DEBUG_SPACEWIRE_ONOFF if (DEBUG_SPACEWIRE_FLAGS & DBGSPW_DUMP) { int k; for (k = 0; k < count; k++){ if (k % 16 == 0) { printf ("\n"); } printf ("%.2x(%c) ", rw_args->buffer[k] & 0xff, isprint(rw_args->buffer[k] & 0xff) ? rw_args->buffer[k] & 0xff : ' '); } printf ("\n"); } #endif rw_args->bytes_moved = count; return RTEMS_SUCCESSFUL; } static rtems_device_driver grspw_write( rtems_device_major_number major, rtems_device_minor_number minor, void * arg ) { rtems_libio_rw_args_t *rw_args; GRSPW_DEV *pDev; struct drvmgr_dev *dev; if ( drvmgr_get_dev(&grspw_drv_info.general, minor, &dev) ) { return RTEMS_INVALID_NAME; } pDev = (GRSPW_DEV *)dev->priv; rw_args = (rtems_libio_rw_args_t *) arg; SPACEWIRE_DBGC(DBGSPW_IOCALLS, "write [%i,%i]: buf:0x%x len:%i\n", major, minor, (unsigned int)rw_args->buffer, rw_args->count); /* is link up? */ if ( !pDev->running ) { return RTEMS_INVALID_NAME; } if ((rw_args->count > pDev->txdbufsize) || (rw_args->count < 1) || (rw_args->buffer == NULL)) { return RTEMS_INVALID_NAME; } while ((rw_args->bytes_moved = grspw_hw_send(pDev, 0, NULL, rw_args->count, rw_args->buffer, 0)) == 0) { if (pDev->config.tx_block_on_full == 1) { SPACEWIRE_DBG2("Tx Block on full \n"); rtems_semaphore_obtain(pDev->txsp, RTEMS_WAIT, RTEMS_NO_TIMEOUT); } else { SPACEWIRE_DBG2("Tx non blocking return when full \n"); return RTEMS_RESOURCE_IN_USE; } } return RTEMS_SUCCESSFUL; } static rtems_device_driver grspw_control( rtems_device_major_number major, rtems_device_minor_number minor, void * arg ) { spw_ioctl_pkt_send *args; spw_ioctl_packetsize *ps; int status; unsigned int tmp,mask,nodeaddr,nodemask; int timeout; rtems_device_driver ret; rtems_libio_ioctl_args_t *ioarg = (rtems_libio_ioctl_args_t *) arg; GRSPW_DEV *pDev; struct drvmgr_dev *dev; SPACEWIRE_DBGC(DBGSPW_IOCALLS, "ctrl [%i,%i]\n", major, minor); if ( drvmgr_get_dev(&grspw_drv_info.general, minor, &dev) ) { return RTEMS_INVALID_NAME; } pDev = (GRSPW_DEV *)dev->priv; if (!ioarg) return RTEMS_INVALID_NAME; ioarg->ioctl_return = 0; switch(ioarg->command) { case SPACEWIRE_IOCTRL_SET_NODEADDR: /*set node address*/ SPACEWIRE_DBGC(DBGSPW_IOCTRL, "SPACEWIRE_IOCTRL_SET_NODEADDR %i\n",(unsigned int)ioarg->buffer); if ((unsigned int)ioarg->buffer > 255) { return RTEMS_INVALID_NAME; } nodeaddr = ((unsigned int)ioarg->buffer) & 0xff; tmp = SPW_READ(&pDev->regs->nodeaddr); tmp &= 0xffffff00; /* Remove old address */ tmp |= nodeaddr; SPW_WRITE(&pDev->regs->nodeaddr, tmp); if ((SPW_READ(&pDev->regs->nodeaddr)&0xff) != nodeaddr) { return RTEMS_IO_ERROR; } pDev->config.nodeaddr = nodeaddr; break; case SPACEWIRE_IOCTRL_SET_NODEMASK: /*set node address*/ SPACEWIRE_DBGC(DBGSPW_IOCTRL, "SPACEWIRE_IOCTRL_SET_NODEMASK %i\n",(unsigned int)ioarg->buffer); if ( pDev->core_ver > 1 ){ if ((unsigned int)ioarg->buffer > 255) { return RTEMS_INVALID_NAME; } nodemask = ((unsigned int)ioarg->buffer) & 0xff; tmp = SPW_READ(&pDev->regs->nodeaddr); tmp &= 0xffff00ff; /* Remove old mask */ tmp |= nodemask<<8; SPW_WRITE(&pDev->regs->nodeaddr, tmp); if (((SPW_READ(&pDev->regs->nodeaddr)>>8)&0xff) != nodemask) { return RTEMS_IO_ERROR; } pDev->config.nodemask = nodemask; }else{ SPACEWIRE_DBG("SPACEWIRE_IOCTRL_SET_NODEMASK: not implemented in GRSPW1 HW\n"); } break; case SPACEWIRE_IOCTRL_SET_RXBLOCK: SPACEWIRE_DBGC(DBGSPW_IOCTRL, "SPACEWIRE_IOCTRL_SET_RXBLOCK %i \n", (unsigned int)ioarg->buffer); if ((unsigned int)ioarg->buffer > 1) { return RTEMS_INVALID_NAME; } pDev->config.rx_blocking = (unsigned int)ioarg->buffer; break; case SPACEWIRE_IOCTRL_SET_DESTKEY: SPACEWIRE_DBGC(DBGSPW_IOCTRL,"SPACEWIRE_IOCTRL_SET_DESTKEY %i\n", (unsigned int)ioarg->buffer); if (!pDev->config.is_rmap) { return RTEMS_NOT_IMPLEMENTED; } if ((unsigned int)ioarg->buffer > 255) { return RTEMS_INVALID_NAME; } SPW_WRITE(&pDev->regs->destkey, (unsigned int)ioarg->buffer); if (SPW_READ(&pDev->regs->destkey) != (unsigned int)ioarg->buffer) { return RTEMS_IO_ERROR; } pDev->config.destkey = (unsigned int)ioarg->buffer; break; case SPACEWIRE_IOCTRL_SET_CLKDIV: SPACEWIRE_DBGC(DBGSPW_IOCTRL,"SPACEWIRE_IOCTRL_SET_CLKDIV %i\n", (unsigned int)ioarg->buffer); if ((unsigned int)ioarg->buffer > 255) { return RTEMS_INVALID_NAME; } if ( pDev->core_ver == 3 ) break; tmp = SPW_READ(&pDev->regs->clkdiv); tmp &= ~0xff; /* Remove old Clockdiv Setting */ tmp |= ((unsigned int)ioarg->buffer) & 0xff; /* add new clockdiv setting */ SPW_WRITE(&pDev->regs->clkdiv, tmp); if (SPW_READ(&pDev->regs->clkdiv) != tmp) { return RTEMS_IO_ERROR; } pDev->config.clkdiv = tmp; break; case SPACEWIRE_IOCTRL_SET_CLKDIVSTART: SPACEWIRE_DBGC(DBGSPW_IOCTRL,"SPACEWIRE_IOCTRL_SET_CLKDIVSTART %i\n", (unsigned int)ioarg->buffer); if ((unsigned int)ioarg->buffer > 255) { return RTEMS_INVALID_NAME; } if ( pDev->core_ver == 3 ) break; tmp = SPW_READ(&pDev->regs->clkdiv); tmp &= ~0xff00; /* Remove old Clockdiv Start Setting */ tmp |= (((unsigned int)ioarg->buffer) & 0xff)<<8; /* add new clockdiv start setting */ SPW_WRITE(&pDev->regs->clkdiv, tmp); if (SPW_READ(&pDev->regs->clkdiv) != tmp) { return RTEMS_IO_ERROR; } pDev->config.clkdiv = tmp; break; case SPACEWIRE_IOCTRL_SET_TIMER: SPACEWIRE_DBGC(DBGSPW_IOCTRL,"SPACEWIRE_IOCTRL_SET_TIMER %i\n", (unsigned int)ioarg->buffer); if ( pDev->core_ver <= 1 ) { if ((unsigned int)ioarg->buffer > 4095) { return RTEMS_INVALID_NAME; } SPW_WRITE(&pDev->regs->timer, (SPW_READ(&pDev->regs->timer) & 0xFFFFF000) | ((unsigned int)ioarg->buffer & 0xFFF)); if ((SPW_READ(&pDev->regs->timer) & 0xFFF) != (unsigned int)ioarg->buffer) { return RTEMS_IO_ERROR; } pDev->config.timer = (unsigned int)ioarg->buffer; }else{ SPACEWIRE_DBG("SPACEWIRE_IOCTRL_SET_TIMER: removed in GRSPW2 HW\n"); } break; case SPACEWIRE_IOCTRL_SET_DISCONNECT: SPACEWIRE_DBGC(DBGSPW_IOCTRL,"SPACEWIRE_IOCTRL_SET_DISCONNECT %i\n", (unsigned int)ioarg->buffer); if ( pDev->core_ver <= 1 ) { if ((unsigned int)ioarg->buffer > 1023) { return RTEMS_INVALID_NAME; } SPW_WRITE(&pDev->regs->timer, (SPW_READ(&pDev->regs->timer) & 0xFFC00FFF) | (((unsigned int)ioarg->buffer & 0x3FF) << 12)); if (((SPW_READ(&pDev->regs->timer) >> 12) & 0x3FF) != (unsigned int)ioarg->buffer) { return RTEMS_IO_ERROR; } pDev->config.disconnect = (unsigned int)ioarg->buffer; }else{ SPACEWIRE_DBG("SPACEWIRE_IOCTRL_SET_DISCONNECT: not implemented for GRSPW2\n"); } break; case SPACEWIRE_IOCTRL_SET_PROMISCUOUS: SPACEWIRE_DBGC(DBGSPW_IOCTRL,"SPACEWIRE_IOCTRL_SET_PROMISCUOUS %i \n", (unsigned int)ioarg->buffer); if ((unsigned int)ioarg->buffer > 1) { return RTEMS_INVALID_NAME; } SPW_CTRL_WRITE(pDev, SPW_CTRL_READ(pDev) | ((unsigned int)ioarg->buffer << 5)); if (((SPW_CTRL_READ(pDev) >> 5) & 1) != (unsigned int)ioarg->buffer) { return RTEMS_IO_ERROR; } pDev->config.promiscuous = (unsigned int)ioarg->buffer; break; case SPACEWIRE_IOCTRL_SET_RMAPEN: SPACEWIRE_DBGC(DBGSPW_IOCTRL,"SPACEWIRE_IOCTRL_SET_RMAPEN %i \n", (unsigned int)ioarg->buffer); if ((unsigned int)ioarg->buffer > 1) { return RTEMS_INVALID_NAME; } SPW_CTRL_WRITE(pDev, (SPW_CTRL_READ(pDev) & 0xFFFEFFFF) | ((unsigned int)ioarg->buffer << 16)); if (((SPW_CTRL_READ(pDev) >> 16) & 1) != (unsigned int)ioarg->buffer) { return RTEMS_IO_ERROR; } pDev->config.rmapen = (unsigned int)ioarg->buffer; break; case SPACEWIRE_IOCTRL_SET_RMAPBUFDIS: SPACEWIRE_DBGC(DBGSPW_IOCTRL,"SPACEWIRE_IOCTRL_SET_RMAPBUFDIS %i \n", (unsigned int)ioarg->buffer); if ((unsigned int)ioarg->buffer > 1) { return RTEMS_INVALID_NAME; } SPW_CTRL_WRITE(pDev, (SPW_CTRL_READ(pDev) & 0xFFFDFFFF) | ((unsigned int)ioarg->buffer << 17)); if (((SPW_CTRL_READ(pDev) >> 17) & 1) != (unsigned int)ioarg->buffer) { return RTEMS_IO_ERROR; } pDev->config.rmapbufdis = (unsigned int)ioarg->buffer; break; case SPACEWIRE_IOCTRL_SET_CHECK_RMAP: SPACEWIRE_DBGC(DBGSPW_IOCTRL,"SPACEWIRE_IOCTRL_SET_CHECK_RMAP %i \n", (unsigned int)ioarg->buffer); if ((unsigned int)ioarg->buffer > 1) { return RTEMS_INVALID_NAME; } pDev->config.check_rmap_err = (unsigned int)ioarg->buffer; break; case SPACEWIRE_IOCTRL_SET_RM_PROT_ID: SPACEWIRE_DBGC(DBGSPW_IOCTRL, "SPACEWIRE_IOCTRL_SET_RM_PROT_ID %i \n", (unsigned int)ioarg->buffer); if ((unsigned int)ioarg->buffer > 1) { return RTEMS_INVALID_NAME; } pDev->config.rm_prot_id = (unsigned int)ioarg->buffer; break; case SPACEWIRE_IOCTRL_SET_KEEP_SOURCE: SPACEWIRE_DBGC(DBGSPW_IOCTRL, "SPACEWIRE_IOCTRL_SET_KEEP_SOURCE %i \n", (unsigned int)ioarg->buffer); if ((unsigned int)ioarg->buffer > 1) { return RTEMS_INVALID_NAME; } pDev->config.keep_source = (unsigned int)ioarg->buffer; break; case SPACEWIRE_IOCTRL_SET_TXBLOCK: SPACEWIRE_DBGC(DBGSPW_IOCTRL, "SPACEWIRE_IOCTRL_SET_TXBLOCK %i \n", (unsigned int)ioarg->buffer); if ((unsigned int)ioarg->buffer > 1) { return RTEMS_INVALID_NAME; } pDev->config.tx_blocking = (unsigned int)ioarg->buffer; break; case SPACEWIRE_IOCTRL_SET_TXBLOCK_ON_FULL: SPACEWIRE_DBGC(DBGSPW_IOCTRL, "SPACEWIRE_IOCTRL_SET_TXBLOCK_ON_FULL %i \n", (unsigned int)ioarg->buffer); if ((unsigned int)ioarg->buffer > 1) { return RTEMS_INVALID_NAME; } pDev->config.tx_block_on_full = (unsigned int)ioarg->buffer; break; case SPACEWIRE_IOCTRL_SET_DISABLE_ERR: SPACEWIRE_DBGC(DBGSPW_IOCTRL, "SPACEWIRE_IOCTRL_SET_DISABLE_ERR %i \n", (unsigned int)ioarg->buffer); if ((unsigned int)ioarg->buffer > 1) { return RTEMS_INVALID_NAME; } pDev->config.disable_err = (unsigned int)ioarg->buffer; break; case SPACEWIRE_IOCTRL_SET_LINK_ERR_IRQ: SPACEWIRE_DBGC(DBGSPW_IOCTRL, "SPACEWIRE_IOCTRL_SET_LINK_ERR_IRQ %i \n", (unsigned int)ioarg->buffer); SPACEWIRE_DBGC(DBGSPW_IOCTRL, "CTRL REG: %x\n", SPW_CTRL_READ(pDev)); if ((unsigned int)ioarg->buffer > 1) { return RTEMS_INVALID_NAME; } tmp = (SPW_CTRL_READ(pDev) & 0xFFFFFDF7) | ((unsigned int)ioarg->buffer << 9); if (tmp & (SPW_CTRL_LI|SPW_CTRL_TQ)) tmp |= SPW_CTRL_IE; SPW_CTRL_WRITE(pDev, tmp); SPACEWIRE_DBGC(DBGSPW_IOCTRL, "CTRL REG: %x\n", SPW_CTRL_READ(pDev)); if (((SPW_CTRL_READ(pDev) >> 9) & 1) != (unsigned int)ioarg->buffer) { return RTEMS_IO_ERROR; } pDev->config.link_err_irq = (unsigned int)ioarg->buffer; break; case SPACEWIRE_IOCTRL_SET_EVENT_ID: SPACEWIRE_DBGC(DBGSPW_IOCTRL, "SPACEWIRE_IOCTRL_SET_EVENT_ID %i \n", (unsigned int)ioarg->buffer); pDev->config.event_id = (rtems_id)ioarg->buffer; SPACEWIRE_DBGC(DBGSPW_IOCTRL, "Event id: %i\n", pDev->config.event_id); break; /* Change MAX Packet size by: * - stop RX/TX (if on) * - wait for hw to complete RX DMA (if on) * - reallocate buffers with new size * - tell hw about new size & start RX/TX again (if previously on) */ case SPACEWIRE_IOCTRL_SET_PACKETSIZE: if (ioarg->buffer == NULL) return RTEMS_INVALID_NAME; ps = (spw_ioctl_packetsize*) ioarg->buffer; SPACEWIRE_DBGC(DBGSPW_IOCTRL,"SPACEWIRE_IOCTRL_SET_RXPACKETSIZE %i \n", (unsigned int)ioarg->buffer); tmp = pDev->running; if ( pDev->running ){ /* Stop RX */ grspw_hw_stop(pDev,1,1); /* If packetsize fails it is good to know if in running mode */ pDev->running = 0; /* Wait for Receiver to finnish pending DMA transfers if any */ grspw_hw_wait_rx_inactive(pDev); } /* Save new buffer sizes */ pDev->rxbufsize = ((ps->rxsize+7)&~7); pDev->txdbufsize = ps->txdsize; pDev->txhbufsize = ps->txhsize; pDev->config.rxmaxlen = pDev->rxbufsize; /* Free previous buffers & allocate buffers with new size */ if (grspw_buffer_alloc(pDev)) return RTEMS_NO_MEMORY; /* if RX was actived before, we reactive it again */ if ( tmp ) { if ( (status = grspw_hw_startup(pDev,-1)) != RTEMS_SUCCESSFUL ) { return status; } pDev->running = 1; } #if 0 /* Rewrite previous config which was wasted due to reset in hw_startup */ SPW_WRITE(&pDev->regs->nodeaddr, pDev->config.nodeaddr); SPW_WRITE(&pDev->regs->destkey, pDev->config.destkey); SPW_WRITE(&pDev->regs->clkdiv, pDev->config.clkdiv); SPW_WRITE(&pDev->regs->timer, pDev->config.timer | ( (pDev->config.disconnect & 0x3FF) << 12) ); SPW_CTRL_WRITE(pDev, (SPW_CTRL_READ(pDev) & !(SPW_CTRL_LINKSTART | SPW_CTRL_PM | SPW_CTRL_RE | SPW_CTRL_RD | SPW_CTRL_TT | SPW_CTRL_TR)) | \ (pDev->config.promiscuous << 5) | (pDev->config.rmapen << 16) | (pDev->config.rmapbufdis << 17) | \ (pDev->config.linkdisabled) | (pDev->config.linkstart << 1)); #endif break; case SPACEWIRE_IOCTRL_GET_CONFIG: if (ioarg->buffer == NULL) return RTEMS_INVALID_NAME; SPACEWIRE_DBG2("SPACEWIRE_IOCTRL_GET_CONFIG \n"); (*(spw_config *)ioarg->buffer).nodeaddr = pDev->config.nodeaddr; (*(spw_config *)ioarg->buffer).nodemask = pDev->config.nodemask; (*(spw_config *)ioarg->buffer).destkey = pDev->config.destkey; (*(spw_config *)ioarg->buffer).clkdiv = pDev->config.clkdiv; (*(spw_config *)ioarg->buffer).rxmaxlen = pDev->config.rxmaxlen; (*(spw_config *)ioarg->buffer).timer = pDev->config.timer; (*(spw_config *)ioarg->buffer).disconnect = pDev->config.disconnect; (*(spw_config *)ioarg->buffer).promiscuous = pDev->config.promiscuous; (*(spw_config *)ioarg->buffer).rmapen = pDev->config.rmapen; (*(spw_config *)ioarg->buffer).rmapbufdis = pDev->config.rmapbufdis; (*(spw_config *)ioarg->buffer).check_rmap_err = pDev->config.check_rmap_err; (*(spw_config *)ioarg->buffer).rm_prot_id = pDev->config.rm_prot_id; (*(spw_config *)ioarg->buffer).tx_blocking = pDev->config.tx_blocking; (*(spw_config *)ioarg->buffer).disable_err = pDev->config.disable_err; (*(spw_config *)ioarg->buffer).link_err_irq = pDev->config.link_err_irq; (*(spw_config *)ioarg->buffer).event_id = pDev->config.event_id; (*(spw_config *)ioarg->buffer).is_rmap = pDev->config.is_rmap; (*(spw_config *)ioarg->buffer).is_rmapcrc = pDev->config.is_rmapcrc; (*(spw_config *)ioarg->buffer).is_rxunaligned = pDev->config.is_rxunaligned; (*(spw_config *)ioarg->buffer).linkdisabled = pDev->config.linkdisabled; (*(spw_config *)ioarg->buffer).linkstart = pDev->config.linkstart; (*(spw_config *)ioarg->buffer).rx_blocking = pDev->config.rx_blocking; (*(spw_config *)ioarg->buffer).tx_block_on_full = pDev->config.tx_block_on_full; (*(spw_config *)ioarg->buffer).keep_source = pDev->config.keep_source; (*(spw_config *)ioarg->buffer).rtimeout = pDev->config.rtimeout; break; case SPACEWIRE_IOCTRL_GET_LINK_STATUS: SPACEWIRE_DBGC(DBGSPW_IOCTRL,"SPACEWIRE_IOCTRL_GET_STATUS=%i \n", (unsigned int)((SPW_STATUS_READ(pDev) >> 21) & 0x7)); *(unsigned int *)ioarg->buffer = (unsigned int )((SPW_STATUS_READ(pDev) >> 21) & 0x7); break; case SPACEWIRE_IOCTRL_GET_STATISTICS: if (ioarg->buffer == NULL) return RTEMS_INVALID_NAME; SPACEWIRE_DBG2("SPACEWIRE_IOCTRL_GET_STATISTICS \n"); (*(spw_stats *)ioarg->buffer).tx_link_err = pDev->stat.tx_link_err; (*(spw_stats *)ioarg->buffer).rx_rmap_header_crc_err = pDev->stat.rx_rmap_header_crc_err; (*(spw_stats *)ioarg->buffer).rx_rmap_data_crc_err = pDev->stat.rx_rmap_data_crc_err; (*(spw_stats *)ioarg->buffer).rx_eep_err = pDev->stat.rx_eep_err; (*(spw_stats *)ioarg->buffer).rx_truncated = pDev->stat.rx_truncated; (*(spw_stats *)ioarg->buffer).parity_err = pDev->stat.parity_err; (*(spw_stats *)ioarg->buffer).escape_err = pDev->stat.escape_err; (*(spw_stats *)ioarg->buffer).credit_err = pDev->stat.credit_err; (*(spw_stats *)ioarg->buffer).write_sync_err = pDev->stat.write_sync_err; (*(spw_stats *)ioarg->buffer).disconnect_err = pDev->stat.disconnect_err; (*(spw_stats *)ioarg->buffer).early_ep = pDev->stat.early_ep; (*(spw_stats *)ioarg->buffer).invalid_address = pDev->stat.invalid_address; (*(spw_stats *)ioarg->buffer).packets_sent = pDev->stat.packets_sent; (*(spw_stats *)ioarg->buffer).packets_received = pDev->stat.packets_received; break; case SPACEWIRE_IOCTRL_CLR_STATISTICS: SPACEWIRE_DBG2("SPACEWIRE_IOCTRL_CLR_STATISTICS \n"); pDev->stat.tx_link_err = 0; pDev->stat.rx_rmap_header_crc_err = 0; pDev->stat.rx_rmap_data_crc_err = 0; pDev->stat.rx_eep_err = 0; pDev->stat.rx_truncated = 0; pDev->stat.parity_err = 0; pDev->stat.escape_err = 0; pDev->stat.credit_err = 0; pDev->stat.write_sync_err = 0; pDev->stat.disconnect_err = 0; pDev->stat.early_ep = 0; pDev->stat.invalid_address = 0; pDev->stat.packets_sent = 0; pDev->stat.packets_received = 0; break; case SPACEWIRE_IOCTRL_SEND: if (ioarg->buffer == NULL) return RTEMS_INVALID_NAME; args = (spw_ioctl_pkt_send *)ioarg->buffer; args->sent = 0; /* is link up? */ if ( !pDev->running ) { return RTEMS_INVALID_NAME; } SPACEWIRE_DBGC(DBGSPW_IOCALLS, "write [%i,%i]: hlen: %i hbuf:0x%x dlen:%i dbuf:0x%x\n", major, minor, (unsigned int)args->hlen, (int)args->hdr,(unsigned int)args->dlen, (int)args->data); if ((args->hlen > pDev->txhbufsize) || (args->dlen > pDev->txdbufsize) || ((args->hlen+args->dlen) < 1) || ((args->hdr == NULL) && (args->hlen != 0)) || ((args->data == NULL) && (args->dlen != 0))) { return RTEMS_INVALID_NAME; } while ((args->sent = grspw_hw_send(pDev, args->hlen, args->hdr, args->dlen, args->data, args->options)) == 0) { if (pDev->config.tx_block_on_full == 1) { SPACEWIRE_DBG2("Tx Block on full \n"); rtems_semaphore_obtain(pDev->txsp, RTEMS_WAIT, RTEMS_NO_TIMEOUT); } else { SPACEWIRE_DBG2("Tx non blocking return when full \n"); return RTEMS_RESOURCE_IN_USE; } } SPACEWIRE_DBGC(DBGSPW_IOCALLS, "Tx ioctl return: %i \n", args->sent); break; case SPACEWIRE_IOCTRL_LINKDISABLE: pDev->config.linkdisabled = 1; pDev->config.linkstart = 0; if ( pDev->core_ver != 3 ) { SPW_CTRL_WRITE(pDev, (SPW_CTRL_READ(pDev) & 0xFFFFFFFC) | 1); if ((SPW_CTRL_READ(pDev) & 3) != 1) { return RTEMS_IO_ERROR; } } break; case SPACEWIRE_IOCTRL_LINKSTART: pDev->config.linkdisabled = 0; pDev->config.linkstart = 1; if ( pDev->core_ver != 3 ) { SPW_CTRL_WRITE(pDev, (SPW_CTRL_READ(pDev) & 0xFFFFFFFC) | 2); if ((SPW_CTRL_READ(pDev) & 3) != 2) { return RTEMS_IO_ERROR; } } break; /* Calculate timer register from GRSPW Core frequency * Also possible to set disconnect and timer64 from * - SPACEWIRE_IOCTRL_SET_DISCONNECT * - SPACEWIRE_IOCTRL_SET_TIMER */ case SPACEWIRE_IOCTRL_SET_COREFREQ: pDev->core_freq_khz = (unsigned int)ioarg->buffer; if ( pDev->core_freq_khz == 0 ){ /* Get GRSPW clock frequency from system clock. * System clock has been read from timer inited * by RTEMS loader (mkprom) */ drvmgr_freq_get(pDev->dev, DEV_APB_SLV, &pDev->core_freq_khz); /* Convert from Hz -> kHz */ pDev->core_freq_khz = pDev->core_freq_khz / 1000; } /* Only GRSPW1 needs the Timer64 and Disconnect values * GRSPW2 and onwards doesn't have this register. */ if ( pDev->core_ver <= 1 ){ /* Calculate Timer64 & Disconnect */ pDev->config.timer = grspw_calc_timer64(pDev->core_freq_khz); pDev->config.disconnect = grspw_calc_disconnect(pDev->core_freq_khz); /* Set Timer64 & Disconnect Register */ SPW_WRITE(&pDev->regs->timer, (SPW_READ(&pDev->regs->timer) & 0xFFC00000) | ((pDev->config.disconnect & 0x3FF)<<12) | (pDev->config.timer & 0xFFF)); /* Check that the registers were written successfully */ tmp = SPW_READ(&pDev->regs->timer) & 0x003fffff; if ( ((tmp & 0xFFF) != pDev->config.timer) || (((tmp >> 12) & 0x3FF) != pDev->config.disconnect) ) { return RTEMS_IO_ERROR; } } break; case SPACEWIRE_IOCTRL_START: if ( pDev->running ){ return RTEMS_INVALID_NAME; } /* Get timeout from userspace * timeout: * ¤ -1 = Default timeout * ¤ less than -1 = forever * ¤ 0 = no wait, proceed if link is up * ¤ positive = specifies number of system clock ticks that * startup will wait for link to enter ready mode. */ timeout = (int)ioarg->buffer; if ( (ret=grspw_hw_startup(pDev,timeout)) != RTEMS_SUCCESSFUL ) { return ret; } pDev->running = 1; /* Register interrupt routine and unmask IRQ */ drvmgr_interrupt_register(pDev->dev, 0, "grspw", grspw_interrupt, pDev); break; case SPACEWIRE_IOCTRL_STOP: if ( !pDev->running ){ return RTEMS_INVALID_NAME; } /* Disable interrupts */ drvmgr_interrupt_unregister(dev, 0, grspw_interrupt, pDev); pDev->running = 0; /* Stop Receiver and transmitter */ grspw_hw_stop(pDev,1,1); break; /* Set time-code control register bits, and Enables/Disables * Time code interrupt, make sure to connect the callback * grspw_timecode_callback if using interrupts. */ case SPACEWIRE_IOCTRL_SET_TCODE_CTRL: tmp = (unsigned int)ioarg->buffer; mask = tmp & (SPACEWIRE_TCODE_CTRL_IE_MSK|SPACEWIRE_TCODE_CTRL_TT_MSK|SPACEWIRE_TCODE_CTRL_TR_MSK); mask <<= 8; tmp &= mask; tmp = (SPW_CTRL_READ(pDev) & ~(mask | SPW_CTRL_IE)) | tmp; if (tmp & (SPW_CTRL_LI|SPW_CTRL_TQ)) tmp |= SPW_CTRL_IE; SPW_CTRL_WRITE(pDev, tmp); break; /* Set time register and optionaly send a time code */ case SPACEWIRE_IOCTRL_SET_TCODE: tmp = (unsigned int)ioarg->buffer; /* Set timecode register */ if (tmp & SPACEWIRE_TCODE_SET) { SPW_WRITE(&pDev->regs->time, ((SPW_READ(&pDev->regs->time) & ~(0xff)) | (tmp & SPACEWIRE_TCODE_TCODE))); } /* Send timecode directly (tick-in) ? */ if (tmp & SPACEWIRE_TCODE_TX) { SPW_CTRL_WRITE(pDev, ((SPW_CTRL_READ(pDev) & ~(SPW_CTRL_TI)) | SPW_CTRL_TI)); } break; /* Read time code register and tick-out status bit */ case SPACEWIRE_IOCTRL_GET_TCODE: tmp = (unsigned int)ioarg->buffer; if ( !tmp ){ return RTEMS_INVALID_NAME; } /* Copy timecode register */ if (SPW_READ(&pDev->regs->status) & SPW_STATUS_TO) { *(unsigned int *)tmp = (1 << 8) | SPW_READ(&pDev->regs->time); } else { *(unsigned int *)tmp = SPW_READ(&pDev->regs->time); } break; case SPACEWIRE_IOCTRL_SET_READ_TIMEOUT: pDev->config.rtimeout = (unsigned int)ioarg->buffer; break; default: return RTEMS_NOT_IMPLEMENTED; } SPACEWIRE_DBGC(DBGSPW_IOCALLS, "SPW_IOCTRL Return\n"); return RTEMS_SUCCESSFUL; } /* ============================================================================== */ static int grspw_set_rxmaxlen(GRSPW_DEV *pDev) { unsigned int rxmax; SPW_WRITE(&pDev->regs->dma0rxmax,pDev->config.rxmaxlen); /*set rx maxlength*/ rxmax = SPW_READ(&pDev->regs->dma0rxmax); if (rxmax != pDev->config.rxmaxlen) { return 0; } return 1; } static int grspw_hw_init(GRSPW_DEV *pDev) { unsigned int ctrl; ctrl = SPW_CTRL_READ(pDev); pDev->rx = (SPACEWIRE_RXBD *) pDev->ptr_bd0; pDev->tx = (SPACEWIRE_TXBD *) (pDev->ptr_bd0 + SPACEWIRE_BDTABLE_SIZE); /* Set up remote addresses */ pDev->rx_remote = (unsigned int)pDev->ptr_bd0_remote; pDev->tx_remote = pDev->rx_remote + SPACEWIRE_BDTABLE_SIZE; SPACEWIRE_DBG("hw_init [minor %i]\n", pDev->minor); pDev->config.is_rmap = ctrl & SPW_CTRL_RA; pDev->config.is_rxunaligned = ctrl & SPW_CTRL_RX; pDev->config.is_rmapcrc = ctrl & SPW_CTRL_RC; return 0; } static int grspw_hw_waitlink (GRSPW_DEV *pDev, int timeout) { int j; /* No actual link interface on a DMA-only GRSPW2 connected to the * SPW router */ if (pDev->core_ver == 3) return 0; if ( timeout == -1 ){ /* Wait default timeout */ timeout = SPACEWIRE_INIT_TIMEOUT; } j=0; while (SPW_LINKSTATE(SPW_STATUS_READ(pDev)) != 5) { if ( timeout < -1 ) { /* wait forever */ }else if ( j >= timeout ){ /* timeout reached, return fail */ return 1; } /* Sleep for 10 ticks */ rtems_task_wake_after(10); j+=10; } return 0; } static void grspw_hw_reset(GRSPW_DEV *pDev) { SPW_CTRL_WRITE(pDev, SPW_CTRL_RESET); /*reset core*/ SPW_STATUS_WRITE(pDev, SPW_STATUS_TO | SPW_STATUS_CE | SPW_STATUS_ER | SPW_STATUS_DE | SPW_STATUS_PE | SPW_STATUS_WE | SPW_STATUS_IA | SPW_STATUS_EE); /*clear status*/ /* Add extra writes to make sure we wait the number of clocks required * after reset */ SPW_STATUS_WRITE(pDev, SPW_STATUS_TO | SPW_STATUS_CE | SPW_STATUS_ER | SPW_STATUS_DE | SPW_STATUS_PE | SPW_STATUS_WE | SPW_STATUS_IA | SPW_STATUS_EE); /*clear status*/ SPW_STATUS_WRITE(pDev, SPW_STATUS_TO | SPW_STATUS_CE | SPW_STATUS_ER | SPW_STATUS_DE | SPW_STATUS_PE | SPW_STATUS_WE | SPW_STATUS_IA | SPW_STATUS_EE); /*clear status*/ SPW_STATUS_WRITE(pDev, SPW_STATUS_TO | SPW_STATUS_CE | SPW_STATUS_ER | SPW_STATUS_DE | SPW_STATUS_PE | SPW_STATUS_WE | SPW_STATUS_IA | SPW_STATUS_EE); /*clear status*/ SPW_STATUS_WRITE(pDev, SPW_STATUS_TO | SPW_STATUS_CE | SPW_STATUS_ER | SPW_STATUS_DE | SPW_STATUS_PE | SPW_STATUS_WE | SPW_STATUS_IA | SPW_STATUS_EE); /*clear status*/ SPW_CTRL_WRITE(pDev, SPW_CTRL_LINKSTART); /*start link core*/ } static void grspw_hw_read_config(GRSPW_DEV *pDev) { unsigned int tmp; tmp = SPW_READ(&pDev->regs->nodeaddr); pDev->config.nodeaddr = 0xFF & tmp; pDev->config.nodemask = 0xFF & (tmp>>8); pDev->config.destkey = 0xFF & SPW_READ(&pDev->regs->destkey); pDev->config.clkdiv = 0xFFFF & SPW_READ(&pDev->regs->clkdiv); tmp = SPW_CTRL_READ(pDev); pDev->config.promiscuous = 1 & (tmp >> 5); pDev->config.rmapen = 1 & (tmp >> 16); pDev->config.rmapbufdis = 1 & (tmp >> 17); pDev->config.is_rmap = 1 & (tmp >> 31); pDev->config.is_rxunaligned = 1 & (tmp >> 30); pDev->config.is_rmapcrc = 1 & (tmp >> 29); pDev->config.linkdisabled = 1 & tmp; pDev->config.linkstart = 1 & (tmp >> 1); if ( pDev->core_ver <= 1 ){ tmp = SPW_READ(&pDev->regs->timer); pDev->config.timer = 0xFFF & tmp; pDev->config.disconnect = 0x3FF & (tmp >> 12); }else{ pDev->config.timer = 0; pDev->config.disconnect = 0; } return; } /* timeout is given in ticks */ static int grspw_hw_startup (GRSPW_DEV *pDev, int timeout) { int i; unsigned int dmactrl; SPW_WRITE(&pDev->regs->status, (SPW_STATUS_TO|SPW_STATUS_CE|SPW_STATUS_ER|SPW_STATUS_DE|SPW_STATUS_PE|SPW_STATUS_WE|SPW_STATUS_IA|SPW_STATUS_EE)); /*clear status*/ if (grspw_hw_waitlink(pDev,timeout)) { SPACEWIRE_DBG2("Device open. Link is not up\n"); return RTEMS_TIMEOUT; } SPW_WRITE(&pDev->regs->dma0ctrl, SPW_DMACTRL_PS | SPW_DMACTRL_PR | SPW_DMACTRL_TA | SPW_DMACTRL_RA); /*clear status, set ctrl*/ if ((dmactrl = SPW_READ(&pDev->regs->dma0ctrl)) != 0) { SPACEWIRE_DBG2("DMACtrl is not cleared\n"); return RTEMS_IO_ERROR; } /* prepare transmit buffers */ for (i = 0; i < pDev->txbufcnt; i++) { pDev->tx[i].ctrl = 0; pDev->tx[i].addr_header = ((unsigned int)&pDev->ptr_txhbuf0_remote[0]) + (i * pDev->txhbufsize); pDev->tx[i].addr_data = ((unsigned int)&pDev->ptr_txdbuf0_remote[0]) + (i * pDev->txdbufsize); } pDev->tx_cur = 0; pDev->tx_sent = 0; pDev->tx_all_in_use = 0; /* prepare receive buffers */ for (i = 0; i < pDev->rxbufcnt; i++) { if (i+1 == pDev->rxbufcnt) { pDev->rx[i].ctrl = SPW_RXBD_IE | SPW_RXBD_EN | SPW_RXBD_WR; } else { pDev->rx[i].ctrl = SPW_RXBD_IE | SPW_RXBD_EN; } pDev->rx[i].addr = ((unsigned int)&pDev->ptr_rxbuf0_remote[0]) + (i * pDev->rxbufsize); } pDev->rxcur = 0; pDev->rxbufcur = -1; grspw_set_rxmaxlen(pDev); SPW_WRITE(&pDev->regs->dma0txdesc, pDev->tx_remote); SPW_WRITE(&pDev->regs->dma0rxdesc, pDev->rx_remote); /* start RX */ dmactrl = SPW_READ(&pDev->regs->dma0ctrl); SPW_WRITE(&pDev->regs->dma0ctrl, (dmactrl & SPW_PREPAREMASK_RX) | SPW_DMACTRL_RD | SPW_DMACTRL_RXEN | SPW_DMACTRL_NS | SPW_DMACTRL_TXIE | SPW_DMACTRL_RXIE); SPACEWIRE_DBGC(DBGSPW_TX,"0x%x: setup complete\n", (unsigned int)pDev->regs); return RTEMS_SUCCESSFUL; } /* Wait until the receiver is inactive */ static void grspw_hw_wait_rx_inactive(GRSPW_DEV *pDev) { while( SPW_READ(&pDev->regs->dma0ctrl) & SPW_DMACTRL_RX ){ /* switching may be needed: * - low frequency GRSPW * - mega packet incoming */ rtems_task_wake_after(1); } } /* Stop the rx or/and tx by disabling the receiver/transmitter */ static int grspw_hw_stop (GRSPW_DEV *pDev, int rx, int tx) { unsigned int dmactrl; /* stop rx and/or tx */ dmactrl = SPW_READ(&pDev->regs->dma0ctrl); if ( rx ) { dmactrl &= ~(SPW_DMACTRL_RXEN|SPW_DMACTRL_RXIE|SPW_DMACTRL_RD); } if ( tx ) { dmactrl &= ~(SPW_DMACTRL_TXEN|SPW_DMACTRL_TXIE); } /*SPW_WRITE(&pDev->regs->dma0ctrl, (dmactrl & SPW_PREPAREMASK_RX) & ~(SPW_DMACTRL_RD | SPW_DMACTRL_RXEN) & ~(SPW_DMACTRL_TXEN));*/ /* don't clear status flags */ dmactrl &= ~(SPW_DMACTRL_RA|SPW_DMACTRL_PR|SPW_DMACTRL_AI); SPW_WRITE(&pDev->regs->dma0ctrl, dmactrl); return RTEMS_SUCCESSFUL; } int grspw_hw_send(GRSPW_DEV *pDev, unsigned int hlen, char *hdr, unsigned int dlen, char *data, unsigned int options) { unsigned int dmactrl, ctrl; #ifdef DEBUG_SPACEWIRE_ONOFF unsigned int k; #endif rtems_interrupt_level level; unsigned int cur = pDev->tx_cur, bdctrl; char *txh = pDev->ptr_txhbuf0 + (cur * pDev->txhbufsize); char *txd = pDev->ptr_txdbuf0 + (cur * pDev->txdbufsize); char *txh_remote = pDev->ptr_txhbuf0_remote + (cur * pDev->txhbufsize); char *txd_remote = pDev->ptr_txdbuf0_remote + (cur * pDev->txdbufsize); ctrl = SPW_READ((volatile void *)&pDev->tx[cur].ctrl); if (ctrl & SPW_TXBD_EN) { return 0; } memcpy(&txd[0], data, dlen); memcpy(&txh[0], hdr, hlen); #ifdef DEBUG_SPACEWIRE_ONOFF if (DEBUG_SPACEWIRE_FLAGS & DBGSPW_DUMP) { for (k = 0; k < hlen; k++){ if (k % 16 == 0) { printf ("\n"); } printf ("%.2x(%c) ",txh[k] & 0xff,isprint(txh[k] & 0xff) ? txh[k] & 0xff : ' '); } printf ("\n"); } if (DEBUG_SPACEWIRE_FLAGS & DBGSPW_DUMP) { for (k = 0; k < dlen; k++){ if (k % 16 == 0) { printf ("\n"); } printf ("%.2x(%c) ",txd[k] & 0xff,isprint(txd[k] & 0xff) ? txd[k] & 0xff : ' '); } printf ("\n"); } #endif pDev->tx[cur].addr_header = (unsigned int)txh_remote; pDev->tx[cur].len = dlen; pDev->tx[cur].addr_data = (unsigned int)txd_remote; bdctrl = SPW_TXBD_IE | SPW_TXBD_EN | hlen; if ( options & GRSPW_PKTSEND_OPTION_HDR_CRC ) bdctrl |= SPW_TXBD_HC; if ( options & GRSPW_PKTSEND_OPTION_DATA_CRC ) bdctrl |= SPW_TXBD_DC; bdctrl |= options & GRSPW_PKTSEND_OPTION_NOCRCLEN_MASK; /* Update counters */ rtems_interrupt_disable(level); if (pDev->tx_cur == (pDev->txbufcnt - 1) ) { bdctrl |= SPW_TXBD_WR; } pDev->tx[cur].ctrl = bdctrl; dmactrl = SPW_READ(&pDev->regs->dma0ctrl); SPW_WRITE(&pDev->regs->dma0ctrl, (dmactrl & SPW_PREPAREMASK_TX) | SPW_DMACTRL_TXEN | SPW_DMACTRL_TXIE); pDev->tx_cur = (pDev->tx_cur + 1) % pDev->txbufcnt; if (pDev->tx_cur == pDev->tx_sent) { pDev->tx_all_in_use = 1; } rtems_interrupt_enable(level); /* In blocking mode wait until message is sent */ if (pDev->config.tx_blocking) { while ( SPW_READ(&pDev->regs->dma0ctrl) & SPW_DMACTRL_TXEN) { /* if changed to blocking mode */ SPACEWIRE_DBGC(DBGSPW_TX, "Tx blocking\n"); rtems_semaphore_obtain(pDev->txsp, RTEMS_WAIT, RTEMS_NO_TIMEOUT); } } SPACEWIRE_DBGC(DBGSPW_TX, "0x%x: transmitted <%i> bytes\n", (unsigned int) pDev->regs, dlen+hlen); return hlen + dlen; } static int grspw_hw_receive(GRSPW_DEV *pDev, char *b, int c) { unsigned int len, rxlen, ctrl; unsigned int cur; unsigned int tmp; unsigned int dump_start_len; int i; char *rxb; if ( pDev->config.promiscuous || pDev->config.keep_source ) { dump_start_len = 0; /* make sure address and prot can be read in promiscuous mode */ } else if (pDev->config.rm_prot_id) { dump_start_len = 2; /* skip source address and protocol id */ } else { dump_start_len = 1; /* default: skip only source address */ } rxlen = 0; cur = pDev->rxcur; rxb = pDev->ptr_rxbuf0 + (cur * pDev->rxbufsize); SPACEWIRE_DBGC(DBGSPW_RX, "0x%x: waitin packet at pos %i\n", (unsigned int) pDev->regs, cur); ctrl = SPW_READ((volatile void *)&pDev->rx[cur].ctrl); if (ctrl & SPW_RXBD_EN) { return rxlen; } SPACEWIRE_DBGC(DBGSPW_RX, "checking packet\n"); len = SPW_RXBD_LENGTH & ctrl; if (!((ctrl & SPW_RXBD_ERROR) || (pDev->config.check_rmap_err && (ctrl & SPW_RXBD_RMAPERROR)))) { if (pDev->rxbufcur == -1) { SPACEWIRE_DBGC(DBGSPW_RX, "incoming packet len %i\n", len); pDev->stat.packets_received++; pDev->rxbufcur = dump_start_len; } rxlen = tmp = len - pDev->rxbufcur; SPACEWIRE_DBGC(DBGSPW_RX, "C %i\n", c); SPACEWIRE_DBGC(DBGSPW_RX, "Dump %i\n", dump_start_len); SPACEWIRE_DBGC(DBGSPW_RX, "Bufcur %i\n", pDev->rxbufcur); SPACEWIRE_DBGC(DBGSPW_RX, "Rxlen %i\n", rxlen ); if (rxlen > c) { rxlen = c; } if (GRLIB_DMA_IS_CACHE_COHERENT) { /* if ( 1 ) {*/ /*printf("RX_MEMCPY(0x%x, 0x%x, 0x%x)\n", (unsigned int)b, (unsigned int)(rxb+pDev->rxbufcur), (unsigned int)rxlen);*/ memcpy(b, rxb+pDev->rxbufcur, rxlen); } else { int left = rxlen; /* Copy word wise if Aligned */ if ( (((int)b & 3) == 0) && (((int)(rxb+pDev->rxbufcur) & 3) == 0) ){ while(left>=32){ *(int *)(b+0) = MEM_READ32(rxb+pDev->rxbufcur+0); *(int *)(b+4) = MEM_READ32(rxb+pDev->rxbufcur+4); *(int *)(b+8) = MEM_READ32(rxb+pDev->rxbufcur+8); *(int *)(b+12) = MEM_READ32(rxb+pDev->rxbufcur+12); *(int *)(b+16) = MEM_READ32(rxb+pDev->rxbufcur+16); *(int *)(b+20) = MEM_READ32(rxb+pDev->rxbufcur+20); *(int *)(b+24) = MEM_READ32(rxb+pDev->rxbufcur+24); *(int *)(b+28) = MEM_READ32(rxb+pDev->rxbufcur+28); rxb+=32; b+=32; left-=32; } while(left>=4){ *(int *)b = MEM_READ32(rxb+pDev->rxbufcur); rxb+=4; b+=4; left-=4; } } for(i = 0; i < left; i++) { b[i] = MEM_READ8(rxb+pDev->rxbufcur+i); } } pDev->rxbufcur += rxlen; if (c >= tmp) { SPACEWIRE_DBGC(DBGSPW_RX, "Next descriptor\n"); grspw_rxnext(pDev); } } else { check_rx_errors(pDev, ctrl); grspw_rxnext(pDev); } return rxlen; } static void grspw_rxnext(GRSPW_DEV *pDev) { unsigned int dmactrl; unsigned int cur = pDev->rxcur; unsigned int ctrl = 0; rtems_interrupt_level level; rtems_interrupt_disable(level); if (cur == (pDev->rxbufcnt - 1)) { pDev->rx[cur].ctrl = ctrl | SPW_RXBD_EN | SPW_RXBD_IE | SPW_RXBD_WR; cur = 0; } else { pDev->rx[cur].ctrl = ctrl | SPW_RXBD_EN | SPW_RXBD_IE; cur++; } pDev->rxcur = cur; pDev->rxbufcur = -1; /* start RX */ dmactrl = SPW_READ(&pDev->regs->dma0ctrl); SPW_WRITE(&pDev->regs->dma0ctrl, (dmactrl & SPW_PREPAREMASK_RX) | SPW_DMACTRL_RD | SPW_DMACTRL_RXEN | SPW_DMACTRL_RXIE | SPW_DMACTRL_NS); rtems_interrupt_enable(level); } static void check_rx_errors(GRSPW_DEV *pDev, int ctrl) { if (ctrl & SPW_RXBD_EEP) { pDev->stat.rx_eep_err++; } if (ctrl & SPW_RXBD_EHC) { if (pDev->config.check_rmap_err) { pDev->stat.rx_rmap_header_crc_err++; } } if (ctrl & SPW_RXBD_EDC) { if (pDev->config.check_rmap_err) { pDev->stat.rx_rmap_data_crc_err++; } } if (ctrl & SPW_RXBD_ETR) { pDev->stat.rx_truncated++; } } static void grspw_print_dev(struct drvmgr_dev *dev, int options) { GRSPW_DEV *pDev = dev->priv; /* Print */ printf("--- GRSPW %s ---\n", pDev->devName); printf(" REGS: 0x%x\n", (unsigned int)pDev->regs); printf(" IRQ: %d\n", pDev->irq); printf(" CORE VERSION: %d\n", pDev->core_ver); printf(" CTRL: 0x%x\n", pDev->regs->ctrl); printf(" STATUS: 0x%x\n", pDev->regs->status); printf(" DMA0CTRL: 0x%x\n", pDev->regs->dma0ctrl); printf(" TXBD: 0x%x\n", (unsigned int)pDev->tx); printf(" RXBD: 0x%x\n", (unsigned int)pDev->rx); } void grspw_print(int options) { struct amba_drv_info *drv = &grspw_drv_info; struct drvmgr_dev *dev; dev = drv->general.dev; while(dev) { grspw_print_dev(dev, options); dev = dev->next_in_drv; } }