diff options
Diffstat (limited to 'c/src/lib/libbsp/sparc/shared/can/occan.c')
-rw-r--r-- | c/src/lib/libbsp/sparc/shared/can/occan.c | 1984 |
1 files changed, 0 insertions, 1984 deletions
diff --git a/c/src/lib/libbsp/sparc/shared/can/occan.c b/c/src/lib/libbsp/sparc/shared/can/occan.c deleted file mode 100644 index 2c0e49a2d5..0000000000 --- a/c/src/lib/libbsp/sparc/shared/can/occan.c +++ /dev/null @@ -1,1984 +0,0 @@ -/* OC_CAN driver - * - * COPYRIGHT (c) 2007. - * 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 <rtems.h> -#include <rtems/libio.h> -#include <stdlib.h> -#include <stdio.h> -#include <string.h> -#include <bsp.h> -#include <rtems/bspIo.h> /* printk */ - -#include <drvmgr/drvmgr.h> -#include <drvmgr/ambapp_bus.h> -#include <bsp/occan.h> - -/* RTEMS -> ERRNO decoding table - -rtems_assoc_t errno_assoc[] = { - { "OK", RTEMS_SUCCESSFUL, 0 }, - { "BUSY", RTEMS_RESOURCE_IN_USE, EBUSY }, - { "INVALID NAME", RTEMS_INVALID_NAME, EINVAL }, - { "NOT IMPLEMENTED", RTEMS_NOT_IMPLEMENTED, ENOSYS }, - { "TIMEOUT", RTEMS_TIMEOUT, ETIMEDOUT }, - { "NO MEMORY", RTEMS_NO_MEMORY, ENOMEM }, - { "NO DEVICE", RTEMS_UNSATISFIED, ENODEV }, - { "INVALID NUMBER", RTEMS_INVALID_NUMBER, EBADF}, - { "NOT RESOURCE OWNER", RTEMS_NOT_OWNER_OF_RESOURCE, EPERM}, - { "IO ERROR", RTEMS_IO_ERROR, EIO}, - { 0, 0, 0 }, -}; - -*/ - -/* -#undef DEBUG -#undef DEBUG_EXTRA -#undef DEBUG_PRINT_REGMAP -*/ - -/* default to byte regs */ -#ifndef OCCAN_WORD_REGS - #define OCCAN_BYTE_REGS -#else - #undef OCCAN_BYTE_REGS -#endif - -/* Enable Fixup code older OCCAN with a TX IRQ-FLAG bug */ -#define OCCAN_TX_IRQ_FLAG_FIXUP 1 - -#define OCCAN_WORD_REG_OFS 0x80 -#define OCCAN_NCORE_OFS 0x100 -#define DEFAULT_CLKDIV 0x7 -#define DEFAULT_EXTENDED_MODE 1 -#define DEFAULT_RX_FIFO_LEN 64 -#define DEFAULT_TX_FIFO_LEN 64 - -/* not implemented yet */ -#undef REDUNDANT_CHANNELS - -/* Define common debug macros */ -#ifdef DEBUG - #define DBG(fmt, vargs...) printk(fmt, ## vargs ) -#else - #define DBG(fmt, vargs...) -#endif - -/* Spin locks mapped via rtems_interrupt_lock_* API: */ -#define SPIN_DECLARE(lock) RTEMS_INTERRUPT_LOCK_MEMBER(lock) -#define SPIN_INIT(lock, name) rtems_interrupt_lock_initialize(lock, name) -#define SPIN_LOCK(lock, level) rtems_interrupt_lock_acquire_isr(lock, &level) -#define SPIN_LOCK_IRQ(lock, level) rtems_interrupt_lock_acquire(lock, &level) -#define SPIN_UNLOCK(lock, level) rtems_interrupt_lock_release_isr(lock, &level) -#define SPIN_UNLOCK_IRQ(lock, level) rtems_interrupt_lock_release(lock, &level) -#define SPIN_IRQFLAGS(k) rtems_interrupt_lock_context k -#define SPIN_ISR_IRQFLAGS(k) SPIN_IRQFLAGS(k) - -/* fifo interface */ -typedef struct { - int cnt; - int ovcnt; /* overwrite count */ - int full; /* 1 = base contain cnt CANMsgs, tail==head */ - CANMsg *tail, *head; - CANMsg *base; - char fifoarea[0]; -} occan_fifo; - -/* PELICAN */ - -typedef struct { - unsigned char - mode, - cmd, - status, - intflags, - inten, - resv0, - bustim0, - bustim1, - unused0[2], - resv1, - arbcode, - errcode, - errwarn, - rx_err_cnt, - tx_err_cnt, - rx_fi_xff; /* this is also acceptance code 0 in reset mode */ - union{ - struct { - unsigned char id[2]; - unsigned char data[8]; - unsigned char next_in_fifo[2]; - } rx_sff; - struct { - unsigned char id[4]; - unsigned char data[8]; - } rx_eff; - struct { - unsigned char id[2]; - unsigned char data[8]; - unsigned char unused[2]; - } tx_sff; - struct { - unsigned char id[4]; - unsigned char data[8]; - } tx_eff; - struct { - unsigned char code[3]; - unsigned char mask[4]; - } rst_accept; - } msg; - unsigned char rx_msg_cnt; - unsigned char unused1; - unsigned char clkdiv; -} pelican8_regs; - -typedef struct { - unsigned char - mode, unused0[3], - cmd, unused1[3], - status, unused2[3], - intflags, unused3[3], - inten, unused4[3], - resv0, unused5[3], - bustim0, unused6[3], - bustim1, unused7[3], - unused8[8], - resv1,unused9[3], - arbcode,unused10[3], - errcode,unused11[3], - errwarn,unused12[3], - rx_err_cnt,unused13[3], - tx_err_cnt,unused14[3], - rx_fi_xff, unused15[3]; /* this is also acceptance code 0 in reset mode */ - /* make sure to use pointers when writing (byte access) to these registers */ - union{ - struct { - unsigned int id[2]; - unsigned int data[8]; - unsigned int next_in_fifo[2]; - } rx_sff; - struct { - unsigned int id[4]; - unsigned int data[8]; - } rx_eff; - struct { - unsigned int id[2]; - unsigned int data[8]; - } tx_sff; - struct { - unsigned int id[4]; - unsigned int data[8]; - } tx_eff; - struct { - unsigned int code[3]; - unsigned int mask[4]; - } rst_accept; - } msg; - unsigned char rx_msg_cnt,unused16[3]; - unsigned char unused17[4]; - unsigned char clkdiv,unused18[3]; -} pelican32_regs; - -#ifdef OCCAN_BYTE_REGS -#define pelican_regs pelican8_regs -#else -#define pelican_regs pelican32_regs -#endif - - -#define MAX_TSEG2 7 -#define MAX_TSEG1 15 - -#if 0 -typedef struct { - unsigned char brp; - unsigned char sjw; - unsigned char tseg1; - unsigned char tseg2; - unsigned char sam; -} occan_speed_regs; -#endif -typedef struct { - unsigned char btr0; - unsigned char btr1; -} occan_speed_regs; - -typedef struct { - struct drvmgr_dev *dev; - char devName[32]; - SPIN_DECLARE(devlock); - - /* hardware shortcuts */ - pelican_regs *regs; - int byte_regs; - int irq; - occan_speed_regs timing; - int channel; /* 0=default, 1=second bus */ - int single_mode; - unsigned int sys_freq_hz; - - /* driver state */ - rtems_id devsem; - rtems_id txsem; - rtems_id rxsem; - int open; - int started; - int rxblk; - int txblk; - int sending; - unsigned int status; - occan_stats stats; - - /* rx&tx fifos */ - occan_fifo *rxfifo; - occan_fifo *txfifo; - - /* Config */ - unsigned int speed; /* speed in HZ */ - unsigned char acode[4]; - unsigned char amask[4]; -} occan_priv; - -/********** FIFO INTERFACE **********/ -static void occan_fifo_put(occan_fifo *fifo); -static CANMsg *occan_fifo_put_claim(occan_fifo *fifo, int force); -static occan_fifo *occan_fifo_create(int cnt); -static void occan_fifo_free(occan_fifo *fifo); -static int occan_fifo_full(occan_fifo *fifo); -static int occan_fifo_empty(occan_fifo *fifo); -static void occan_fifo_get(occan_fifo *fifo); -static CANMsg *occan_fifo_claim_get(occan_fifo *fifo); -static void occan_fifo_clr(occan_fifo *fifo); - -/**** Hardware related Interface ****/ -static int occan_calc_speedregs(unsigned int clock_hz, unsigned int rate, occan_speed_regs *result); -static int occan_set_speedregs(occan_priv *priv, occan_speed_regs *timing); -static void pelican_init(occan_priv *priv); -static void pelican_open(occan_priv *priv); -static int pelican_start(occan_priv *priv); -static void pelican_stop(occan_priv *priv); -static int pelican_send(occan_priv *can, CANMsg *msg); -static void pelican_set_accept(occan_priv *priv, unsigned char *acode, unsigned char *amask); -void occan_interrupt(void *arg); -#ifdef DEBUG_PRINT_REGMAP -static void pelican_regadr_print(pelican_regs *regs); -#endif - -/***** Driver related interface *****/ -static rtems_device_driver occan_ioctl(rtems_device_major_number major, rtems_device_minor_number minor, void *arg); -static rtems_device_driver occan_write(rtems_device_major_number major, rtems_device_minor_number minor, void *arg); -static rtems_device_driver occan_read(rtems_device_major_number major, rtems_device_minor_number minor, void *arg); -static rtems_device_driver occan_close(rtems_device_major_number major, rtems_device_minor_number minor, void *arg); -static rtems_device_driver occan_open(rtems_device_major_number major, rtems_device_minor_number minor, void *arg); -static rtems_device_driver occan_initialize(rtems_device_major_number major, rtems_device_minor_number unused, void *arg); - -#define OCCAN_DRIVER_TABLE_ENTRY { occan_initialize, occan_open, occan_close, occan_read, occan_write, occan_ioctl } -static rtems_driver_address_table occan_driver = OCCAN_DRIVER_TABLE_ENTRY; - - -/* Read byte bypassing */ - - -/* Bypass cache */ -#define READ_REG(priv, address) occan_reg_read(priv, (unsigned int)address) -#define WRITE_REG(priv, address, data) occan_reg_write(priv, (unsigned int)address, data) - -static unsigned int occan_reg_read(occan_priv *priv, unsigned int address) -{ - unsigned int adr; - if ( priv->byte_regs ) { - adr = address; - } else { - /* Word accessed registers */ - adr = (address & (~0x7f)) | ((address & 0x7f)<<2); - } - return *(volatile unsigned char *)adr; -} - -static void occan_reg_write( - occan_priv *priv, - unsigned int address, - unsigned char value) -{ - unsigned int adr; - if ( priv->byte_regs ) { - adr = address; - } else { - /* Word accessed registers */ - adr = (address & (~0x7f)) | ((address & 0x7f)<<2); - } - *(volatile unsigned char *)adr = value;; -} - -/* Mode register bit definitions */ -#define PELICAN_MOD_RESET 0x1 -#define PELICAN_MOD_LISTEN 0x2 -#define PELICAN_MOD_SELFTEST 0x4 -#define PELICAN_MOD_ACCEPT 0x8 - -/* Command register bit definitions */ -#define PELICAN_CMD_TXREQ 0x1 -#define PELICAN_CMD_ABORT 0x2 -#define PELICAN_CMD_RELRXBUF 0x4 -#define PELICAN_CMD_CLRDOVR 0x8 -#define PELICAN_CMD_SELFRXRQ 0x10 - -/* Status register bit definitions */ -#define PELICAN_STAT_RXBUF 0x1 -#define PELICAN_STAT_DOVR 0x2 -#define PELICAN_STAT_TXBUF 0x4 -#define PELICAN_STAT_TXOK 0x8 -#define PELICAN_STAT_RX 0x10 -#define PELICAN_STAT_TX 0x20 -#define PELICAN_STAT_ERR 0x40 -#define PELICAN_STAT_BUS 0x80 - -/* Interrupt register bit definitions */ -#define PELICAN_IF_RX 0x1 -#define PELICAN_IF_TX 0x2 -#define PELICAN_IF_ERRW 0x4 -#define PELICAN_IF_DOVR 0x8 -#define PELICAN_IF_ERRP 0x20 -#define PELICAN_IF_ARB 0x40 -#define PELICAN_IF_BUS 0x80 - -/* Interrupt Enable register bit definitions */ -#define PELICAN_IE_RX 0x1 -#define PELICAN_IE_TX 0x2 -#define PELICAN_IE_ERRW 0x4 -#define PELICAN_IE_DOVR 0x8 -#define PELICAN_IE_ERRP 0x20 -#define PELICAN_IE_ARB 0x40 -#define PELICAN_IE_BUS 0x80 - -/* Arbitration lost capture register bit definitions */ -#define PELICAN_ARB_BITS 0x1f - -/* register bit definitions */ -#define PELICAN_ECC_CODE_BIT 0x00 -#define PELICAN_ECC_CODE_FORM 0x40 -#define PELICAN_ECC_CODE_STUFF 0x80 -#define PELICAN_ECC_CODE_OTHER 0xc0 -#define PELICAN_ECC_CODE 0xc0 - -#define PELICAN_ECC_DIR 0x20 -#define PELICAN_ECC_SEG 0x1f - -/* Clock divider register bit definitions */ -#define PELICAN_CDR_DIV 0x7 -#define PELICAN_CDR_OFF 0x8 -#define PELICAN_CDR_MODE 0x80 -#define PELICAN_CDR_MODE_PELICAN 0x80 -#define PELICAN_CDR_MODE_BITS 7 -#define PELICAN_CDR_MODE_BASICAN 0x00 - - -/* register bit definitions */ -#define OCCAN_BUSTIM_SJW 0xc0 -#define OCCAN_BUSTIM_BRP 0x3f -#define OCCAN_BUSTIM_SJW_BIT 6 - -#define OCCAN_BUSTIM_SAM 0x80 -#define OCCAN_BUSTIM_TSEG2 0x70 -#define OCCAN_BUSTIM_TSEG2_BIT 4 -#define OCCAN_BUSTIM_TSEG1 0x0f - -/* register bit definitions */ -/* -#define PELICAN_S_ 0x1 -#define PELICAN_S_ 0x2 -#define PELICAN_S_ 0x4 -#define PELICAN_S_ 0x8 -#define PELICAN_S_ 0x10 -#define PELICAN_S_ 0x20 -#define PELICAN_S_ 0x40 -#define PELICAN_S_ 0x80 -*/ - -static int occan_driver_io_registered = 0; -static rtems_device_major_number occan_driver_io_major = 0; - -/******************* Driver manager interface ***********************/ - -/* Driver prototypes */ -int occan_register_io(rtems_device_major_number *m); -int occan_device_init(occan_priv *pDev); - -int occan_init2(struct drvmgr_dev *dev); -int occan_init3(struct drvmgr_dev *dev); - -struct drvmgr_drv_ops occan_ops = -{ - .init = {NULL, occan_init2, occan_init3, NULL}, - .remove = NULL, - .info = NULL -}; - -struct amba_dev_id occan_ids[] = -{ - {VENDOR_GAISLER, GAISLER_CANAHB}, - {0, 0} /* Mark end of table */ -}; - -struct amba_drv_info occan_drv_info = -{ - { - DRVMGR_OBJ_DRV, /* Driver */ - NULL, /* Next driver */ - NULL, /* Device list */ - DRIVER_AMBAPP_GAISLER_OCCAN_ID, /* Driver ID */ - "OCCAN_DRV", /* Driver Name */ - DRVMGR_BUS_TYPE_AMBAPP, /* Bus Type */ - &occan_ops, - NULL, /* Funcs */ - 0, /* No devices yet */ - 0, - }, - &occan_ids[0] -}; - -void occan_register_drv (void) -{ - DBG("Registering OCCAN driver\n"); - drvmgr_drv_register(&occan_drv_info.general); -} - -int occan_init2(struct drvmgr_dev *dev) -{ - occan_priv *priv; - - DBG("OCCAN[%d] on bus %s\n", dev->minor_drv, dev->parent->dev->name); - priv = dev->priv = malloc(sizeof(occan_priv)); - if ( !priv ) - return DRVMGR_NOMEM; - memset(priv, 0, sizeof(*priv)); - priv->dev = dev; - - return DRVMGR_OK; -} - -int occan_init3(struct drvmgr_dev *dev) -{ - occan_priv *priv; - char prefix[32]; - rtems_status_code status; - - priv = dev->priv; - - /* Do initialization */ - - if ( occan_driver_io_registered == 0) { - /* Register the I/O driver only once for all cores */ - if ( occan_register_io(&occan_driver_io_major) ) { - /* Failed to register I/O driver */ - dev->priv = NULL; - return DRVMGR_FAIL; - } - - occan_driver_io_registered = 1; - } - - /* I/O system registered and initialized - * Now we take care of device initialization. - */ - - if ( occan_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/occan%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/%soccan%d", prefix, dev->minor_bus); - } - - /* Register Device */ - DBG("OCCAN[%d]: Registering %s\n", dev->minor_drv, priv->devName); - status = rtems_io_register_name(priv->devName, occan_driver_io_major, dev->minor_drv); - if (status != RTEMS_SUCCESSFUL) { - return DRVMGR_FAIL; - } - - return DRVMGR_OK; -} - -/******************* Driver Implementation ***********************/ - -int occan_register_io(rtems_device_major_number *m) -{ - rtems_status_code r; - - if ((r = rtems_io_register_driver(0, &occan_driver, m)) == RTEMS_SUCCESSFUL) { - DBG("OCCAN driver successfully registered, major: %d\n", *m); - } else { - switch(r) { - case RTEMS_TOO_MANY: - printk("OCCAN rtems_io_register_driver failed: RTEMS_TOO_MANY\n"); - return -1; - case RTEMS_INVALID_NUMBER: - printk("OCCAN rtems_io_register_driver failed: RTEMS_INVALID_NUMBER\n"); - return -1; - case RTEMS_RESOURCE_IN_USE: - printk("OCCAN rtems_io_register_driver failed: RTEMS_RESOURCE_IN_USE\n"); - return -1; - default: - printk("OCCAN rtems_io_register_driver failed\n"); - return -1; - } - } - return 0; -} - -int occan_device_init(occan_priv *pDev) -{ - struct amba_dev_info *ambadev; - struct ambapp_core *pnpinfo; - rtems_status_code status; - int minor; - - /* 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 = (pelican_regs *)(pnpinfo->ahb_slv->start[0] + OCCAN_NCORE_OFS*pnpinfo->index); - pDev->byte_regs = 1; - minor = pDev->dev->minor_drv; - - /* Get frequency in Hz */ - if ( drvmgr_freq_get(pDev->dev, DEV_AHB_SLV, &pDev->sys_freq_hz) ) { - return -1; - } - - DBG("OCCAN frequency: %d Hz\n", pDev->sys_freq_hz); - - /* initialize software */ - pDev->open = 0; - pDev->started = 0; /* Needed for spurious interrupts */ - pDev->rxfifo = NULL; - pDev->txfifo = NULL; - status = rtems_semaphore_create( - rtems_build_name('C', 'd', 'v', '0'+minor), - 1, - RTEMS_FIFO | RTEMS_SIMPLE_BINARY_SEMAPHORE | RTEMS_NO_INHERIT_PRIORITY | \ - RTEMS_NO_PRIORITY_CEILING, - 0, - &pDev->devsem); - if ( status != RTEMS_SUCCESSFUL ){ - printk("OCCAN[%d]: Failed to create dev semaphore, (%d)\n\r",minor, status); - return RTEMS_UNSATISFIED; - } - status = rtems_semaphore_create( - rtems_build_name('C', 't', 'x', '0'+minor), - 0, - RTEMS_FIFO | RTEMS_SIMPLE_BINARY_SEMAPHORE | RTEMS_NO_INHERIT_PRIORITY | \ - RTEMS_NO_PRIORITY_CEILING, - 0, - &pDev->txsem); - if ( status != RTEMS_SUCCESSFUL ){ - printk("OCCAN[%d]: Failed to create tx semaphore, (%d)\n\r",minor, status); - return RTEMS_UNSATISFIED; - } - status = rtems_semaphore_create( - rtems_build_name('C', 'r', 'x', '0'+minor), - 0, - RTEMS_FIFO | RTEMS_SIMPLE_BINARY_SEMAPHORE | RTEMS_NO_INHERIT_PRIORITY | \ - RTEMS_NO_PRIORITY_CEILING, - 0, - &pDev->rxsem); - if ( status != RTEMS_SUCCESSFUL ){ - printk("OCCAN[%d]: Failed to create rx semaphore, (%d)\n\r",minor, status); - return RTEMS_UNSATISFIED; - } - - /* hardware init/reset */ - pelican_init(pDev); - -#ifdef DEBUG_PRINT_REGMAP - pelican_regadr_print(pDev->regs); -#endif - - return 0; -} - - -#ifdef DEBUG -static void pelican_regs_print(occan_priv *pDev){ - pelican_regs *regs = pDev->regs; - printk("--- PELICAN 0x%lx ---\n\r",(unsigned int)regs); - printk(" MODE: 0x%02x\n\r",READ_REG(pDev, ®s->mode)); - printk(" CMD: 0x%02x\n\r",READ_REG(pDev, ®s->cmd)); - printk(" STATUS: 0x%02x\n\r",READ_REG(pDev, ®s->status)); - /*printk(" INTFLG: 0x%02x\n\r",READ_REG(pDev, ®s->intflags));*/ - printk(" INTEN: 0x%02x\n\r",READ_REG(pDev, ®s->inten)); - printk(" BTR0: 0x%02x\n\r",READ_REG(pDev, ®s->bustim0)); - printk(" BTR1: 0x%02x\n\r",READ_REG(pDev, ®s->bustim1)); - printk(" ARBCODE: 0x%02x\n\r",READ_REG(pDev, ®s->arbcode)); - printk(" ERRCODE: 0x%02x\n\r",READ_REG(pDev, ®s->errcode)); - printk(" ERRWARN: 0x%02x\n\r",READ_REG(pDev, ®s->errwarn)); - printk(" RX_ERR_CNT: 0x%02x\n\r",READ_REG(pDev, ®s->rx_err_cnt)); - printk(" TX_ERR_CNT: 0x%02x\n\r",READ_REG(pDev, ®s->tx_err_cnt)); - if ( READ_REG(pDev, ®s->mode) & PELICAN_MOD_RESET ){ - /* in reset mode it is possible to read acceptance filters */ - printk(" ACR0: 0x%02x (0x%lx)\n\r",READ_REG(pDev, ®s->rx_fi_xff),®s->rx_fi_xff); - printk(" ACR1: 0x%02x (0x%lx)\n\r",READ_REG(pDev, ®s->msg.rst_accept.code[0]),(unsigned int)®s->msg.rst_accept.code[0]); - printk(" ACR1: 0x%02x (0x%lx)\n\r",READ_REG(pDev, ®s->msg.rst_accept.code[1]),(unsigned int)®s->msg.rst_accept.code[1]); - printk(" ACR1: 0x%02x (0x%lx)\n\r",READ_REG(pDev, ®s->msg.rst_accept.code[2]),(unsigned int)®s->msg.rst_accept.code[2]); - printk(" AMR0: 0x%02x (0x%lx)\n\r",READ_REG(pDev, ®s->msg.rst_accept.mask[0]),(unsigned int)®s->msg.rst_accept.mask[0]); - printk(" AMR1: 0x%02x (0x%lx)\n\r",READ_REG(pDev, ®s->msg.rst_accept.mask[1]),(unsigned int)®s->msg.rst_accept.mask[1]); - printk(" AMR2: 0x%02x (0x%lx)\n\r",READ_REG(pDev, ®s->msg.rst_accept.mask[2]),(unsigned int)®s->msg.rst_accept.mask[2]); - printk(" AMR3: 0x%02x (0x%lx)\n\r",READ_REG(pDev, ®s->msg.rst_accept.mask[3]),(unsigned int)®s->msg.rst_accept.mask[3]); - - }else{ - printk(" RXFI_XFF: 0x%02x\n\r",READ_REG(pDev, ®s->rx_fi_xff)); - } - printk(" RX_MSG_CNT: 0x%02x\n\r",READ_REG(pDev, ®s->rx_msg_cnt)); - printk(" CLKDIV: 0x%02x\n\r",READ_REG(pDev, ®s->clkdiv)); - printk("-------------------\n\r"); -} -#endif - -#ifdef DEBUG_PRINT_REGMAP -static void pelican_regadr_print(pelican_regs *regs){ - printk("--- PELICAN 0x%lx ---\n\r",(unsigned int)regs); - printk(" MODE: 0x%lx\n\r",(unsigned int)®s->mode); - printk(" CMD: 0x%lx\n\r",(unsigned int)®s->cmd); - printk(" STATUS: 0x%lx\n\r",(unsigned int)®s->status); - /*printk(" INTFLG: 0x%lx\n\r",®s->intflags);*/ - printk(" INTEN: 0x%lx\n\r",(unsigned int)®s->inten); - printk(" BTR0: 0x%lx\n\r",(unsigned int)®s->bustim0); - printk(" BTR1: 0x%lx\n\r",(unsigned int)®s->bustim1); - printk(" ARBCODE: 0x%lx\n\r",(unsigned int)®s->arbcode); - printk(" ERRCODE: 0x%lx\n\r",(unsigned int)®s->errcode); - printk(" ERRWARN: 0x%lx\n\r",(unsigned int)®s->errwarn); - printk(" RX_ERR_CNT: 0x%lx\n\r",(unsigned int)®s->rx_err_cnt); - printk(" TX_ERR_CNT: 0x%lx\n\r",(unsigned int)®s->tx_err_cnt); - - /* in reset mode it is possible to read acceptance filters */ - printk(" RXFI_XFF: 0x%lx\n\r",(unsigned int)®s->rx_fi_xff); - - /* reset registers */ - printk(" ACR0: 0x%lx\n\r",(unsigned int)®s->rx_fi_xff); - printk(" ACR1: 0x%lx\n\r",(unsigned int)®s->msg.rst_accept.code[0]); - printk(" ACR2: 0x%lx\n\r",(unsigned int)®s->msg.rst_accept.code[1]); - printk(" ACR3: 0x%lx\n\r",(unsigned int)®s->msg.rst_accept.code[2]); - printk(" AMR0: 0x%lx\n\r",(unsigned int)®s->msg.rst_accept.mask[0]); - printk(" AMR1: 0x%lx\n\r",(unsigned int)®s->msg.rst_accept.mask[1]); - printk(" AMR2: 0x%lx\n\r",(unsigned int)®s->msg.rst_accept.mask[2]); - printk(" AMR3: 0x%lx\n\r",(unsigned int)®s->msg.rst_accept.mask[3]); - - /* TX Extended */ - printk(" EFFTX_ID[0]: 0x%lx\n\r",(unsigned int)®s->msg.tx_eff.id[0]); - printk(" EFFTX_ID[1]: 0x%lx\n\r",(unsigned int)®s->msg.tx_eff.id[1]); - printk(" EFFTX_ID[2]: 0x%lx\n\r",(unsigned int)®s->msg.tx_eff.id[2]); - printk(" EFFTX_ID[3]: 0x%lx\n\r",(unsigned int)®s->msg.tx_eff.id[3]); - - printk(" EFFTX_DATA[0]: 0x%lx\n\r",(unsigned int)®s->msg.tx_eff.data[0]); - printk(" EFFTX_DATA[1]: 0x%lx\n\r",(unsigned int)®s->msg.tx_eff.data[1]); - printk(" EFFTX_DATA[2]: 0x%lx\n\r",(unsigned int)®s->msg.tx_eff.data[2]); - printk(" EFFTX_DATA[3]: 0x%lx\n\r",(unsigned int)®s->msg.tx_eff.data[3]); - printk(" EFFTX_DATA[4]: 0x%lx\n\r",(unsigned int)®s->msg.tx_eff.data[4]); - printk(" EFFTX_DATA[5]: 0x%lx\n\r",(unsigned int)®s->msg.tx_eff.data[5]); - printk(" EFFTX_DATA[6]: 0x%lx\n\r",(unsigned int)®s->msg.tx_eff.data[6]); - printk(" EFFTX_DATA[7]: 0x%lx\n\r",(unsigned int)®s->msg.tx_eff.data[7]); - - /* RX Extended */ - printk(" EFFRX_ID[0]: 0x%lx\n\r",(unsigned int)®s->msg.rx_eff.id[0]); - printk(" EFFRX_ID[1]: 0x%lx\n\r",(unsigned int)®s->msg.rx_eff.id[1]); - printk(" EFFRX_ID[2]: 0x%lx\n\r",(unsigned int)®s->msg.rx_eff.id[2]); - printk(" EFFRX_ID[3]: 0x%lx\n\r",(unsigned int)®s->msg.rx_eff.id[3]); - - printk(" EFFRX_DATA[0]: 0x%lx\n\r",(unsigned int)®s->msg.rx_eff.data[0]); - printk(" EFFRX_DATA[1]: 0x%lx\n\r",(unsigned int)®s->msg.rx_eff.data[1]); - printk(" EFFRX_DATA[2]: 0x%lx\n\r",(unsigned int)®s->msg.rx_eff.data[2]); - printk(" EFFRX_DATA[3]: 0x%lx\n\r",(unsigned int)®s->msg.rx_eff.data[3]); - printk(" EFFRX_DATA[4]: 0x%lx\n\r",(unsigned int)®s->msg.rx_eff.data[4]); - printk(" EFFRX_DATA[5]: 0x%lx\n\r",(unsigned int)®s->msg.rx_eff.data[5]); - printk(" EFFRX_DATA[6]: 0x%lx\n\r",(unsigned int)®s->msg.rx_eff.data[6]); - printk(" EFFRX_DATA[7]: 0x%lx\n\r",(unsigned int)®s->msg.rx_eff.data[7]); - - - /* RX Extended */ - printk(" SFFRX_ID[0]: 0x%lx\n\r",(unsigned int)®s->msg.rx_sff.id[0]); - printk(" SFFRX_ID[1]: 0x%lx\n\r",(unsigned int)®s->msg.rx_sff.id[1]); - - printk(" SFFRX_DATA[0]: 0x%lx\n\r",(unsigned int)®s->msg.rx_sff.data[0]); - printk(" SFFRX_DATA[1]: 0x%lx\n\r",(unsigned int)®s->msg.rx_sff.data[1]); - printk(" SFFRX_DATA[2]: 0x%lx\n\r",(unsigned int)®s->msg.rx_sff.data[2]); - printk(" SFFRX_DATA[3]: 0x%lx\n\r",(unsigned int)®s->msg.rx_sff.data[3]); - printk(" SFFRX_DATA[4]: 0x%lx\n\r",(unsigned int)®s->msg.rx_sff.data[4]); - printk(" SFFRX_DATA[5]: 0x%lx\n\r",(unsigned int)®s->msg.rx_sff.data[5]); - printk(" SFFRX_DATA[6]: 0x%lx\n\r",(unsigned int)®s->msg.rx_sff.data[6]); - printk(" SFFRX_DATA[7]: 0x%lx\n\r",(unsigned int)®s->msg.rx_sff.data[7]); - - /* TX Extended */ - printk(" SFFTX_ID[0]: 0x%lx\n\r",(unsigned int)®s->msg.tx_sff.id[0]); - printk(" SFFTX_ID[1]: 0x%lx\n\r",(unsigned int)®s->msg.tx_sff.id[1]); - - printk(" SFFTX_DATA[0]: 0x%lx\n\r",(unsigned int)®s->msg.tx_sff.data[0]); - printk(" SFFTX_DATA[1]: 0x%lx\n\r",(unsigned int)®s->msg.tx_sff.data[1]); - printk(" SFFTX_DATA[2]: 0x%lx\n\r",(unsigned int)®s->msg.tx_sff.data[2]); - printk(" SFFTX_DATA[3]: 0x%lx\n\r",(unsigned int)®s->msg.tx_sff.data[3]); - printk(" SFFTX_DATA[4]: 0x%lx\n\r",(unsigned int)®s->msg.tx_sff.data[4]); - printk(" SFFTX_DATA[5]: 0x%lx\n\r",(unsigned int)®s->msg.tx_sff.data[5]); - printk(" SFFTX_DATA[6]: 0x%lx\n\r",(unsigned int)®s->msg.tx_sff.data[6]); - printk(" SFFTX_DATA[7]: 0x%lx\n\r",(unsigned int)®s->msg.tx_sff.data[7]); - - printk(" RX_MSG_CNT: 0x%lx\n\r",(unsigned int)®s->rx_msg_cnt); - printk(" CLKDIV: 0x%lx\n\r",(unsigned int)®s->clkdiv); - printk("-------------------\n\r"); -} -#endif - -#ifdef DEBUG -static void occan_stat_print(occan_stats *stats){ - printk("----Stats----\n\r"); - printk("rx_msgs: %d\n\r",stats->rx_msgs); - printk("tx_msgs: %d\n\r",stats->tx_msgs); - printk("err_warn: %d\n\r",stats->err_warn); - printk("err_dovr: %d\n\r",stats->err_dovr); - printk("err_errp: %d\n\r",stats->err_errp); - printk("err_arb: %d\n\r",stats->err_arb); - printk("err_bus: %d\n\r",stats->err_bus); - printk("Int cnt: %d\n\r",stats->ints); - printk("tx_buf_err: %d\n\r",stats->tx_buf_error); - printk("-------------\n\r"); -} -#endif - -static void pelican_init(occan_priv *priv){ - /* Reset core */ - WRITE_REG(priv, &priv->regs->mode, PELICAN_MOD_RESET); - - /* wait for core to reset complete */ - /*usleep(1);*/ -} - -static void pelican_open(occan_priv *priv){ - int ret; - - /* Set defaults */ - priv->speed = OCCAN_SPEED_250K; - - /* set acceptance filters to accept all messages */ - priv->acode[0] = 0; - priv->acode[1] = 0; - priv->acode[2] = 0; - priv->acode[3] = 0; - priv->amask[0] = 0xff; - priv->amask[1] = 0xff; - priv->amask[2] = 0xff; - priv->amask[3] = 0xff; - - /* Set clock divider to extended mode, clkdiv not connected - */ - WRITE_REG(priv, &priv->regs->clkdiv, (1<<PELICAN_CDR_MODE_BITS) | (DEFAULT_CLKDIV & PELICAN_CDR_DIV)); - - ret = occan_calc_speedregs(priv->sys_freq_hz,priv->speed,&priv->timing); - if ( ret ){ - /* failed to set speed for this system freq, try with 50K instead */ - priv->speed = OCCAN_SPEED_50K; - occan_calc_speedregs(priv->sys_freq_hz, priv->speed, - &priv->timing); - } - - /* disable all interrupts */ - WRITE_REG(priv, &priv->regs->inten, 0); - - /* clear pending interrupts by reading */ - READ_REG(priv, &priv->regs->intflags); -} - -static int pelican_start(occan_priv *priv){ - /* Start HW communication */ - - if ( !priv->rxfifo || !priv->txfifo ) - return -1; - - /* In case we were started before and stopped we - * should empty the TX fifo or try to resend those - * messages. We make it simple... - */ - occan_fifo_clr(priv->txfifo); - - /* Clear status bits */ - priv->status = 0; - priv->sending = 0; - - /* clear pending interrupts */ - READ_REG(priv, &priv->regs->intflags); - - /* clear error counters */ - WRITE_REG(priv, &priv->regs->rx_err_cnt, 0); - WRITE_REG(priv, &priv->regs->tx_err_cnt, 0); - -#ifdef REDUNDANT_CHANNELS - if ( (priv->channel == 0) || (priv->channel >= REDUNDANT_CHANNELS) ){ - /* Select the first (default) channel */ - OCCAN_SET_CHANNEL(priv,0); - }else{ - /* set gpio bit, or something */ - OCCAN_SET_CHANNEL(priv,priv->channel); - } -#endif - /* set the speed regs of the CAN core */ - occan_set_speedregs(priv,&priv->timing); - - DBG("OCCAN: start: set timing regs btr0: 0x%x, btr1: 0x%x\n\r", - READ_REG(priv, &priv->regs->bustim0), - READ_REG(priv, &priv->regs->bustim1)); - - /* Set default acceptance filter */ - pelican_set_accept(priv,priv->acode,priv->amask); - - /* Nothing can fail from here, this must be set before interrupts are - * enabled */ - priv->started = 1; - - /* turn on interrupts */ - WRITE_REG(priv, &priv->regs->inten, - PELICAN_IE_RX | PELICAN_IE_TX | PELICAN_IE_ERRW | - PELICAN_IE_ERRP | PELICAN_IE_BUS); -#ifdef DEBUG - /* print setup before starting */ - pelican_regs_print(priv->regs); - occan_stat_print(&priv->stats); -#endif - - /* core already in reset mode, - * ¤ Exit reset mode - * ¤ Enter Single/Dual mode filtering. - */ - WRITE_REG(priv, &priv->regs->mode, (priv->single_mode << 3)); - - /* Register interrupt routine and unmask IRQ at IRQ controller */ - drvmgr_interrupt_register(priv->dev, 0, "occan", occan_interrupt, priv); - - return 0; -} - -static void pelican_stop(occan_priv *priv) -{ - /* stop HW */ - - drvmgr_interrupt_unregister(priv->dev, 0, occan_interrupt, priv); - -#ifdef DEBUG - /* print setup before stopping */ - pelican_regs_print(priv->regs); - occan_stat_print(&priv->stats); -#endif - - /* put core in reset mode */ - WRITE_REG(priv, &priv->regs->mode, PELICAN_MOD_RESET); - - /* turn off interrupts */ - WRITE_REG(priv, &priv->regs->inten, 0); - - priv->status |= OCCAN_STATUS_RESET; -} - -static inline int pelican_tx_ready(occan_priv *can) -{ - unsigned char status; - pelican_regs *regs = can->regs; - - /* is there room in send buffer? */ - status = READ_REG(can, ®s->status); - if ( !(status & PELICAN_STAT_TXBUF) ) { - /* tx fifo taken, we have to wait */ - return 0; - } - - return 1; -} - -/* Try to send message "msg", if hardware txfifo is - * full, then -1 is returned. - * - * Be sure to have disabled CAN interrupts when - * entering this function. - */ -static int pelican_send(occan_priv *can, CANMsg *msg){ - unsigned char tmp; - pelican_regs *regs = can->regs; - - /* is there room in send buffer? */ - if ( !pelican_tx_ready(can) ) { - /* tx fifo taken, we have to wait */ - return -1; - } - - tmp = msg->len & 0xf; - if ( msg->rtr ) - tmp |= 0x40; - - if ( msg->extended ){ - /* Extended Frame */ - WRITE_REG(can, ®s->rx_fi_xff, 0x80 | tmp); - WRITE_REG(can, ®s->msg.tx_eff.id[0],(msg->id >> (5+8+8)) & 0xff); - WRITE_REG(can, ®s->msg.tx_eff.id[1],(msg->id >> (5+8)) & 0xff); - WRITE_REG(can, ®s->msg.tx_eff.id[2],(msg->id >> (5)) & 0xff); - WRITE_REG(can, ®s->msg.tx_eff.id[3],(msg->id << 3) & 0xf8); - tmp = msg->len; - while(tmp--){ - WRITE_REG(can, ®s->msg.tx_eff.data[tmp], msg->data[tmp]); - } - }else{ - /* Standard Frame */ - WRITE_REG(can, ®s->rx_fi_xff, tmp); - WRITE_REG(can, ®s->msg.tx_sff.id[0],(msg->id >> 3) & 0xff); - WRITE_REG(can, ®s->msg.tx_sff.id[1],(msg->id << 5) & 0xe0); - tmp = msg->len; - while(tmp--){ - WRITE_REG(can, ®s->msg.tx_sff.data[tmp],msg->data[tmp]); - } - } - - /* let HW know of new message */ - if ( msg->sshot ){ - WRITE_REG(can, ®s->cmd, PELICAN_CMD_TXREQ | PELICAN_CMD_ABORT); - }else{ - /* normal case -- try resend until sent */ - WRITE_REG(can, ®s->cmd, PELICAN_CMD_TXREQ); - } - - return 0; -} - - -static void pelican_set_accept(occan_priv *priv, unsigned char *acode, unsigned char *amask) -{ - unsigned char *acode0, *acode1, *acode2, *acode3; - unsigned char *amask0, *amask1, *amask2, *amask3; - - acode0 = &priv->regs->rx_fi_xff; - acode1 = (unsigned char *)&priv->regs->msg.rst_accept.code[0]; - acode2 = (unsigned char *)&priv->regs->msg.rst_accept.code[1]; - acode3 = (unsigned char *)&priv->regs->msg.rst_accept.code[2]; - - amask0 = (unsigned char *)&priv->regs->msg.rst_accept.mask[0]; - amask1 = (unsigned char *)&priv->regs->msg.rst_accept.mask[1]; - amask2 = (unsigned char *)&priv->regs->msg.rst_accept.mask[2]; - amask3 = (unsigned char *)&priv->regs->msg.rst_accept.mask[3]; - - /* Set new mask & code */ - WRITE_REG(priv, acode0, acode[0]); - WRITE_REG(priv, acode1, acode[1]); - WRITE_REG(priv, acode2, acode[2]); - WRITE_REG(priv, acode3, acode[3]); - - WRITE_REG(priv, amask0, amask[0]); - WRITE_REG(priv, amask1, amask[1]); - WRITE_REG(priv, amask2, amask[2]); - WRITE_REG(priv, amask3, amask[3]); -} - - -/* This function calculates BTR0 and BTR1 values for a given bitrate. - * - * Set communication parameters. - * \param clock_hz OC_CAN Core frequency in Hz. - * \param rate Requested baud rate in bits/second. - * \param result Pointer to where resulting BTRs will be stored. - * \return zero if successful to calculate a baud rate. - */ -static int occan_calc_speedregs(unsigned int clock_hz, unsigned int rate, occan_speed_regs *result) -{ - int best_error = 1000000000; - int error; - int best_tseg=0, best_brp=0, brp=0; - int tseg=0, tseg1=0, tseg2=0; - int sjw = 0; - int clock = clock_hz / 2; - int sampl_pt = 90; - - if ( (rate<5000) || (rate>1000000) ){ - /* invalid speed mode */ - return -1; - } - - /* find best match, return -2 if no good reg - * combination is available for this frequency */ - - /* some heuristic specials */ - if (rate > ((1000000 + 500000) / 2)) - sampl_pt = 75; - - if (rate < ((12500 + 10000) / 2)) - sampl_pt = 75; - - if (rate < ((100000 + 125000) / 2)) - sjw = 1; - - /* tseg even = round down, odd = round up */ - for (tseg = (0 + 0 + 2) * 2; - tseg <= (MAX_TSEG2 + MAX_TSEG1 + 2) * 2 + 1; - tseg++) - { - brp = clock / ((1 + tseg / 2) * rate) + tseg % 2; - if ((brp == 0) || (brp > 64)) - continue; - - error = rate - clock / (brp * (1 + tseg / 2)); - if (error < 0) - { - error = -error; - } - - if (error <= best_error) - { - best_error = error; - best_tseg = tseg/2; - best_brp = brp-1; - } - } - - if (best_error && (rate / best_error < 10)) - { - printk("OCCAN: bitrate %d is not possible with %d Hz clock\n\r",rate, clock); - return -2; - }else if ( !result ) - return 0; /* nothing to store result in, but a valid bitrate can be calculated */ - - tseg2 = best_tseg - (sampl_pt * (best_tseg + 1)) / 100; - - if (tseg2 < 0) - { - tseg2 = 0; - } - - if (tseg2 > MAX_TSEG2) - { - tseg2 = MAX_TSEG2; - } - - tseg1 = best_tseg - tseg2 - 2; - - if (tseg1 > MAX_TSEG1) - { - tseg1 = MAX_TSEG1; - tseg2 = best_tseg - tseg1 - 2; - } - - result->btr0 = (sjw<<OCCAN_BUSTIM_SJW_BIT) | (best_brp&OCCAN_BUSTIM_BRP); - result->btr1 = (0<<7) | (tseg2<<OCCAN_BUSTIM_TSEG2_BIT) | tseg1; - - return 0; -} - -static int occan_set_speedregs(occan_priv *priv, occan_speed_regs *timing) -{ - if ( !timing || !priv || !priv->regs) - return -1; - - WRITE_REG(priv, &priv->regs->bustim0, timing->btr0); - WRITE_REG(priv, &priv->regs->bustim1, timing->btr1); - - return 0; -} - -static rtems_device_driver occan_initialize(rtems_device_major_number major, rtems_device_minor_number unused, void *arg) -{ - return RTEMS_SUCCESSFUL; -} - -static rtems_device_driver occan_open(rtems_device_major_number major, rtems_device_minor_number minor, void *arg) -{ - occan_priv *can; - struct drvmgr_dev *dev; - - DBG("OCCAN: Opening %d\n\r",minor); - - /* get can device */ - if ( drvmgr_get_dev(&occan_drv_info.general, minor, &dev) ) { - DBG("Wrong minor %d\n", minor); - return RTEMS_UNSATISFIED; /* NODEV */ - } - can = (occan_priv *)dev->priv; - - /* already opened? */ - rtems_semaphore_obtain(can->devsem, RTEMS_WAIT, RTEMS_NO_TIMEOUT); - if ( can->open ){ - rtems_semaphore_release(can->devsem); - return RTEMS_RESOURCE_IN_USE; /* EBUSY */ - } - can->open = 1; - rtems_semaphore_release(can->devsem); - - SPIN_INIT(&can->devlock, can->devName); - - /* allocate fifos */ - can->rxfifo = occan_fifo_create(DEFAULT_RX_FIFO_LEN); - if ( !can->rxfifo ){ - can->open = 0; - return RTEMS_NO_MEMORY; /* ENOMEM */ - } - - can->txfifo = occan_fifo_create(DEFAULT_TX_FIFO_LEN); - if ( !can->txfifo ){ - occan_fifo_free(can->rxfifo); - can->rxfifo= NULL; - can->open = 0; - return RTEMS_NO_MEMORY; /* ENOMEM */ - } - - DBG("OCCAN: Opening %d success\n\r",minor); - - can->started = 0; - can->channel = 0; /* Default to first can link */ - can->txblk = 1; /* Default to Blocking mode */ - can->rxblk = 1; /* Default to Blocking mode */ - can->single_mode = 1; /* single mode acceptance filter */ - - /* reset stat counters */ - memset(&can->stats,0,sizeof(occan_stats)); - - /* HW must be in reset mode here (close and initializes resets core...) - * - * 1. set default modes/speeds - */ - pelican_open(can); - - return RTEMS_SUCCESSFUL; -} - -static rtems_device_driver occan_close(rtems_device_major_number major, rtems_device_minor_number minor, void *arg) -{ - occan_priv *can; - struct drvmgr_dev *dev; - - DBG("OCCAN: Closing %d\n\r",minor); - - if ( drvmgr_get_dev(&occan_drv_info.general, minor, &dev) ) { - return RTEMS_INVALID_NAME; - } - can = (occan_priv *)dev->priv; - - /* stop if running */ - if ( can->started ) - pelican_stop(can); - - /* Enter Reset Mode */ - WRITE_REG(can, &can->regs->mode, PELICAN_MOD_RESET); - - /* free fifo memory */ - occan_fifo_free(can->rxfifo); - occan_fifo_free(can->txfifo); - - can->rxfifo = NULL; - can->txfifo = NULL; - - can->open = 0; - - return RTEMS_SUCCESSFUL; -} - -static rtems_device_driver occan_read(rtems_device_major_number major, rtems_device_minor_number minor, void *arg) -{ - occan_priv *can; - struct drvmgr_dev *dev; - rtems_libio_rw_args_t *rw_args=(rtems_libio_rw_args_t *) arg; - CANMsg *dstmsg, *srcmsg; - SPIN_IRQFLAGS(oldLevel); - int left; - - if ( drvmgr_get_dev(&occan_drv_info.general, minor, &dev) ) { - return RTEMS_INVALID_NAME; - } - can = (occan_priv *)dev->priv; - - if ( !can->started ){ - DBG("OCCAN: cannot read from minor %d when not started\n\r",minor); - return RTEMS_RESOURCE_IN_USE; /* -EBUSY*/ - } - - /* does at least one message fit */ - left = rw_args->count; - if ( left < sizeof(CANMsg) ){ - DBG("OCCAN: minor %d length of buffer must be at least %d, our is %d\n\r",minor,sizeof(CANMsg),left); - return RTEMS_INVALID_NAME; /* -EINVAL */ - } - - /* get pointer to start where to put CAN messages */ - dstmsg = (CANMsg *)rw_args->buffer; - if ( !dstmsg ){ - DBG("OCCAN: minor %d read: input buffer is NULL\n\r",minor); - return RTEMS_INVALID_NAME; /* -EINVAL */ - } - - while (left >= sizeof(CANMsg) ){ - - /* turn off interrupts */ - SPIN_LOCK_IRQ(&can->devlock, oldLevel); - - /* A bus off interrupt may have occured after checking can->started */ - if ( can->status & (OCCAN_STATUS_ERR_BUSOFF|OCCAN_STATUS_RESET) ){ - SPIN_UNLOCK_IRQ(&can->devlock, oldLevel); - DBG("OCCAN: read is cancelled due to a BUS OFF error\n\r"); - rw_args->bytes_moved = rw_args->count-left; - return RTEMS_IO_ERROR; /* EIO */ - } - - srcmsg = occan_fifo_claim_get(can->rxfifo); - if ( !srcmsg ){ - /* no more messages in reception fifo. - * Wait for incoming packets only if in - * blocking mode AND no messages been - * read before. - */ - if ( !can->rxblk || (left != rw_args->count) ){ - /* turn on interrupts again */ - SPIN_UNLOCK_IRQ(&can->devlock, oldLevel); - break; - } - - /* turn on interrupts again */ - SPIN_UNLOCK_IRQ(&can->devlock, oldLevel); - - DBG("OCCAN: Waiting for RX int\n\r"); - - /* wait for incoming messages */ - rtems_semaphore_obtain(can->rxsem, RTEMS_WAIT, - RTEMS_NO_TIMEOUT); - - /* did we get woken up by a BUS OFF error? */ - if ( can->status & (OCCAN_STATUS_ERR_BUSOFF|OCCAN_STATUS_RESET) ){ - DBG("OCCAN: Blocking read got woken up by BUS OFF error\n\r"); - /* At this point it should not matter how many messages we handled */ - rw_args->bytes_moved = rw_args->count-left; - return RTEMS_IO_ERROR; /* EIO */ - } - - /* no errors detected, it must be a message */ - continue; - } - - /* got message, copy it to userspace buffer */ - *dstmsg = *srcmsg; - - /* Return borrowed message, RX interrupt can use it again */ - occan_fifo_get(can->rxfifo); - - /* turn on interrupts again */ - SPIN_UNLOCK_IRQ(&can->devlock, oldLevel); - - /* increase pointers */ - left -= sizeof(CANMsg); - dstmsg++; - } - - /* save number of read bytes. */ - rw_args->bytes_moved = rw_args->count-left; - if ( rw_args->bytes_moved == 0 ){ - DBG("OCCAN: minor %d read would block, returning\n\r",minor); - return RTEMS_TIMEOUT; /* ETIMEDOUT should be EAGAIN/EWOULDBLOCK */ - } - return RTEMS_SUCCESSFUL; -} - -static rtems_device_driver occan_write(rtems_device_major_number major, rtems_device_minor_number minor, void *arg) -{ - occan_priv *can; - struct drvmgr_dev *dev; - rtems_libio_rw_args_t *rw_args=(rtems_libio_rw_args_t *) arg; - CANMsg *msg,*fifo_msg; - SPIN_IRQFLAGS(oldLevel); - int left; - - DBG("OCCAN: Writing %d bytes from 0x%lx (%d)\n\r",rw_args->count,rw_args->buffer,sizeof(CANMsg)); - - if ( drvmgr_get_dev(&occan_drv_info.general, minor, &dev) ) { - return RTEMS_INVALID_NAME; - } - can = (occan_priv *)dev->priv; - - if ( !can->started ) - return RTEMS_RESOURCE_IN_USE; /* EBUSY */ - - left = rw_args->count; - if ( (left < sizeof(CANMsg)) || (!rw_args->buffer) ){ - return RTEMS_INVALID_NAME; /* EINVAL */ - } - - msg = (CANMsg *)rw_args->buffer; - - /* limit CAN message length to 8 */ - msg->len = (msg->len > 8) ? 8 : msg->len; - -#ifdef DEBUG_VERBOSE - pelican_regs_print(can->regs); - occan_stat_print(&can->stats); -#endif - - /* turn off interrupts */ - SPIN_LOCK_IRQ(&can->devlock, oldLevel); - - /* A bus off interrupt may have occured after checking can->started */ - if ( can->status & (OCCAN_STATUS_ERR_BUSOFF|OCCAN_STATUS_RESET) ){ - SPIN_UNLOCK_IRQ(&can->devlock, oldLevel); - rw_args->bytes_moved = 0; - return RTEMS_IO_ERROR; /* EIO */ - } - - /* If no messages in software tx fifo, we will - * try to send first message by putting it directly - * into the HW TX fifo. - */ - if ( occan_fifo_empty(can->txfifo) ){ - /*pelican_regs_print(cans[minor+1].regs);*/ - if ( !pelican_send(can,msg) ) { - /* First message put directly into HW TX fifo - * This will turn TX interrupt on. - */ - left -= sizeof(CANMsg); - msg++; - -#ifdef OCCAN_TX_IRQ_FLAG_FIXUP - /* Mark that we have put at least one msg in TX FIFO */ - can->sending = 1; -#endif - - /* bump stat counters */ - can->stats.tx_msgs++; - - DBG("OCCAN: Sending direct via HW\n\r"); - } - } - - /* Put messages into software fifo */ - while ( left >= sizeof(CANMsg) ){ - - /* limit CAN message length to 8 */ - msg->len = (msg->len > 8) ? 8 : msg->len; - - fifo_msg = occan_fifo_put_claim(can->txfifo,0); - if ( !fifo_msg ){ - - DBG("OCCAN: FIFO is full\n\r"); - /* Block only if no messages previously sent - * and no in blocking mode - */ - if ( !can->txblk || (left != rw_args->count) ) - break; - - /* turn on interupts again and wait - INT_ON - WAIT FOR FREE BUF; - INT_OFF; - CHECK_IF_FIFO_EMPTY ==> SEND DIRECT VIA HW; - */ - SPIN_UNLOCK_IRQ(&can->devlock, oldLevel); - - DBG("OCCAN: Waiting for tx int\n\r"); - - rtems_semaphore_obtain(can->txsem, RTEMS_WAIT, RTEMS_NO_TIMEOUT); - - /* did we get woken up by a BUS OFF error? */ - if ( can->status & (OCCAN_STATUS_ERR_BUSOFF|OCCAN_STATUS_RESET) ){ - DBG("OCCAN: Blocking write got woken up by BUS OFF error or RESET event\n\r"); - /* At this point it should not matter how many messages we handled */ - rw_args->bytes_moved = rw_args->count-left; - return RTEMS_IO_ERROR; /* EIO */ - } - - SPIN_LOCK_IRQ(&can->devlock, oldLevel); - - if ( occan_fifo_empty(can->txfifo) ){ - if ( !pelican_send(can,msg) ) { - /* First message put directly into HW TX fifo - * This will turn TX interrupt on. - */ - left -= sizeof(CANMsg); - msg++; - -#ifdef OCCAN_TX_IRQ_FLAG_FIXUP - /* Mark that we have put at least one msg in TX FIFO */ - can->sending = 1; -#endif - - /* bump stat counters */ - can->stats.tx_msgs++; - - DBG("OCCAN: Sending direct2 via HW\n\r"); - } - } - continue; - } - - /* copy message into fifo area */ - *fifo_msg = *msg; - - /* tell interrupt handler about the message */ - occan_fifo_put(can->txfifo); - - DBG("OCCAN: Put info fifo SW\n\r"); - - /* Prepare insert of next message */ - msg++; - left-=sizeof(CANMsg); - } - - SPIN_UNLOCK_IRQ(&can->devlock, oldLevel); - - rw_args->bytes_moved = rw_args->count-left; - DBG("OCCAN: Sent %d\n\r",rw_args->bytes_moved); - - if ( left == rw_args->count ) - return RTEMS_TIMEOUT; /* ETIMEDOUT should be EAGAIN/EWOULDBLOCK */ - return RTEMS_SUCCESSFUL; -} - -static rtems_device_driver occan_ioctl(rtems_device_major_number major, rtems_device_minor_number minor, void *arg) -{ - int ret; - occan_speed_regs timing; - occan_priv *can; - struct drvmgr_dev *dev; - unsigned int speed; - rtems_libio_ioctl_args_t *ioarg = (rtems_libio_ioctl_args_t *) arg; - struct occan_afilter *afilter; - occan_stats *dststats; - unsigned int rxcnt,txcnt; - - DBG("OCCAN: IOCTL %d\n\r",ioarg->command); - - if ( drvmgr_get_dev(&occan_drv_info.general, minor, &dev) ) { - return RTEMS_INVALID_NAME; - } - can = (occan_priv *)dev->priv; - - ioarg->ioctl_return = 0; - switch(ioarg->command){ - case OCCAN_IOC_SET_SPEED: - - /* cannot change speed during run mode */ - if ( can->started ) - return RTEMS_RESOURCE_IN_USE; /* EBUSY */ - - /* get speed rate from argument */ - speed = (unsigned int)ioarg->buffer; - ret = occan_calc_speedregs(can->sys_freq_hz,speed,&timing); - if ( ret ) - return RTEMS_INVALID_NAME; /* EINVAL */ - - /* set the speed regs of the CAN core */ - /* occan_set_speedregs(can,timing); */ - - /* save timing/speed */ - can->speed = speed; - can->timing = timing; - break; - - case OCCAN_IOC_SET_BTRS: - /* Set BTR registers manually - * Read OCCAN Manual. - */ - if ( can->started ) - return RTEMS_RESOURCE_IN_USE; /* EBUSY */ - - can->speed = 0; /* custom */ - can->timing.btr1 = (unsigned int)ioarg->buffer & 0xff; - can->timing.btr0 = ((unsigned int)ioarg->buffer>>8) & 0xff; -/* - can->timing.sjw = (btr0 >> OCCAN_BUSTIM_SJW_BIT) & 0x3; - can->timing.brp = btr0 & OCCAN_BUSTIM_BRP; - can->timing.tseg1 = btr1 & 0xf; - can->timing.tseg2 = (btr1 >> OCCAN_BUSTIM_TSEG2_BIT) & 0x7; - can->timing.sam = (btr1 >> 7) & 0x1; - */ - break; - - case OCCAN_IOC_SPEED_AUTO: - return RTEMS_NOT_IMPLEMENTED; - - case OCCAN_IOC_SET_BUFLEN: - /* set rx & tx fifo buffer length */ - if ( can->started ) - return RTEMS_RESOURCE_IN_USE; /* EBUSY */ - - rxcnt = (unsigned int)ioarg->buffer & 0x0000ffff; - txcnt = (unsigned int)ioarg->buffer >> 16; - - occan_fifo_free(can->rxfifo); - occan_fifo_free(can->txfifo); - - /* allocate new buffers */ - can->rxfifo = occan_fifo_create(rxcnt); - can->txfifo = occan_fifo_create(txcnt); - - if ( !can->rxfifo || !can->txfifo ) - return RTEMS_NO_MEMORY; /* ENOMEM */ - break; - - case OCCAN_IOC_GET_CONF: - return RTEMS_NOT_IMPLEMENTED; - break; - - case OCCAN_IOC_GET_STATS: - dststats = (occan_stats *)ioarg->buffer; - if ( !dststats ) - return RTEMS_INVALID_NAME; /* EINVAL */ - - /* copy data stats into userspace buffer */ - if ( can->rxfifo ) - can->stats.rx_sw_dovr = can->rxfifo->ovcnt; - *dststats = can->stats; - break; - - case OCCAN_IOC_GET_STATUS: - /* return the status of the */ - if ( !ioarg->buffer ) - return RTEMS_INVALID_NAME; - - *(unsigned int *)ioarg->buffer = can->status; - break; - - /* Set physical link */ - case OCCAN_IOC_SET_LINK: -#ifdef REDUNDANT_CHANNELS - if ( can->started ) - return RTEMS_RESOURCE_IN_USE; /* EBUSY */ - - /* switch HW channel */ - can->channel = (unsigned int)ioargs->buffer; -#else - return RTEMS_NOT_IMPLEMENTED; -#endif - break; - - case OCCAN_IOC_SET_FILTER: - if ( can->started ) - return RTEMS_RESOURCE_IN_USE; /* EBUSY */ - - afilter = (struct occan_afilter *)ioarg->buffer; - - if ( !afilter ) - return RTEMS_INVALID_NAME; /* EINVAL */ - - /* copy acceptance filter */ - can->acode[0] = afilter->code[0]; - can->acode[1] = afilter->code[1]; - can->acode[2] = afilter->code[2]; - can->acode[3] = afilter->code[3]; - - can->amask[0] = afilter->mask[0]; - can->amask[1] = afilter->mask[1]; - can->amask[2] = afilter->mask[2]; - can->amask[3] = afilter->mask[3]; - - can->single_mode = ( afilter->single_mode ) ? 1 : 0; - - /* Acceptance filter is written to hardware - * when starting. - */ - /* pelican_set_accept(can,can->acode,can->amask);*/ - break; - - case OCCAN_IOC_SET_BLK_MODE: - can->rxblk = (unsigned int)ioarg->buffer & OCCAN_BLK_MODE_RX; - can->txblk = ((unsigned int)ioarg->buffer & OCCAN_BLK_MODE_TX) >> 1; - break; - - case OCCAN_IOC_START: - if ( can->started ) - return RTEMS_RESOURCE_IN_USE; /* EBUSY */ - if ( pelican_start(can) ) - return RTEMS_NO_MEMORY; /* failed because of no memory, can happen if SET_BUFLEN failed */ - /* can->started = 1; -- Is set in pelican_start due to interrupt may occur before we - * get here. - */ - break; - - case OCCAN_IOC_STOP: - if ( !can->started ) - return RTEMS_RESOURCE_IN_USE; /* EBUSY */ - pelican_stop(can); - can->started = 0; - break; - - default: - return RTEMS_NOT_DEFINED; - } - return RTEMS_SUCCESSFUL; -} - -void occan_interrupt(void *arg) -{ - occan_priv *can = arg; - unsigned char iflags; - pelican_regs *regs = can->regs; - CANMsg *msg; - int signal_rx=0, signal_tx=0; - unsigned char tmp, errcode, arbcode; - int tx_error_cnt,rx_error_cnt; - SPIN_ISR_IRQFLAGS(irqflags); - - if ( !can->started ) - return; /* Spurious Interrupt, do nothing */ - - SPIN_LOCK(&can->devlock, irqflags); - while (1) { - - iflags = READ_REG(can, &can->regs->intflags); - -#ifdef OCCAN_TX_IRQ_FLAG_FIXUP - /* TX IRQ may be cleared when reading regs->intflags due - * to a bug in some chips. Instead of looking at the TX_IRQ_FLAG - * the TX-fifo emoty register is looked at when something has - * been scheduled for transmission. - */ - if ((iflags & PELICAN_IF_TX) == 0) { - if (can->sending && pelican_tx_ready(can)) { - can->sending = 0; - iflags |= PELICAN_IF_TX; - } - } -#endif - - if (iflags == 0) - break; - /* still interrupts to handle */ - - can->stats.ints++; - - if ( iflags & PELICAN_IF_RX ){ - /* the rx fifo is not empty - * put 1 message into rxfifo for later use - */ - - /* get empty (or make room) message */ - msg = occan_fifo_put_claim(can->rxfifo,1); - tmp = READ_REG(can, ®s->rx_fi_xff); - msg->extended = tmp >> 7; - msg->rtr = (tmp >> 6) & 1; - msg->len = tmp = tmp & 0x0f; - - if ( msg->extended ){ - /* extended message */ - msg->id = READ_REG(can, ®s->msg.rx_eff.id[0])<<(5+8+8) | - READ_REG(can, ®s->msg.rx_eff.id[1])<<(5+8) | - READ_REG(can, ®s->msg.rx_eff.id[2])<<5 | - READ_REG(can, ®s->msg.rx_eff.id[3])>>3; - - while(tmp--){ - msg->data[tmp] = READ_REG(can, ®s->msg.rx_eff.data[tmp]); - } - /* - msg->data[0] = READ_REG(can, ®s->msg.rx_eff.data[0]); - msg->data[1] = READ_REG(can, ®s->msg.rx_eff.data[1]); - msg->data[2] = READ_REG(can, ®s->msg.rx_eff.data[2]); - msg->data[3] = READ_REG(can, ®s->msg.rx_eff.data[3]); - msg->data[4] = READ_REG(can, ®s->msg.rx_eff.data[4]); - msg->data[5] = READ_REG(can, ®s->msg.rx_eff.data[5]); - msg->data[6] = READ_REG(can, ®s->msg.rx_eff.data[6]); - msg->data[7] = READ_REG(can, ®s->msg.rx_eff.data[7]); - */ - }else{ - /* standard message */ - msg->id = READ_REG(can, ®s->msg.rx_sff.id[0])<<3 | - READ_REG(can, ®s->msg.rx_sff.id[1])>>5; - - while(tmp--){ - msg->data[tmp] = READ_REG(can, ®s->msg.rx_sff.data[tmp]); - } - /* - msg->data[0] = READ_REG(can, ®s->msg.rx_sff.data[0]); - msg->data[1] = READ_REG(can, ®s->msg.rx_sff.data[1]); - msg->data[2] = READ_REG(can, ®s->msg.rx_sff.data[2]); - msg->data[3] = READ_REG(can, ®s->msg.rx_sff.data[3]); - msg->data[4] = READ_REG(can, ®s->msg.rx_sff.data[4]); - msg->data[5] = READ_REG(can, ®s->msg.rx_sff.data[5]); - msg->data[6] = READ_REG(can, ®s->msg.rx_sff.data[6]); - msg->data[7] = READ_REG(can, ®s->msg.rx_sff.data[7]); - */ - } - - /* Re-Enable RX buffer for a new message */ - WRITE_REG(can, ®s->cmd, PELICAN_CMD_RELRXBUF); - - /* make message available to the user */ - occan_fifo_put(can->rxfifo); - - /* bump stat counters */ - can->stats.rx_msgs++; - - /* signal the semaphore only once */ - signal_rx = 1; - } - - if ( iflags & PELICAN_IF_TX ) { - - /* there is room in tx fifo of HW */ - - if ( !occan_fifo_empty(can->txfifo) ){ - /* send 1 more messages */ - msg = occan_fifo_claim_get(can->txfifo); - - if ( pelican_send(can,msg) ){ - /* ERROR! We got an TX interrupt telling us - * tx fifo is empty, yet it is not. - * - * Complain about this max 10 times - */ - if ( can->stats.tx_buf_error < 10 ){ - printk("OCCAN: got TX interrupt but TX fifo in not empty (%d)\n\r",can->stats.tx_buf_error); - } - can->status |= OCCAN_STATUS_QUEUE_ERROR; - can->stats.tx_buf_error++; - } -#ifdef OCCAN_TX_IRQ_FLAG_FIXUP - can->sending = 1; -#endif - - /* free software-fifo space taken by sent message */ - occan_fifo_get(can->txfifo); - - /* bump stat counters */ - can->stats.tx_msgs++; - - /* wake any sleeping thread waiting for "fifo not full" */ - signal_tx = 1; - } - } - - if ( iflags & PELICAN_IF_ERRW ){ - tx_error_cnt = READ_REG(can, ®s->tx_err_cnt); - rx_error_cnt = READ_REG(can, ®s->rx_err_cnt); - - /* 1. if bus off tx error counter = 127 */ - if ( (tx_error_cnt > 96) || (rx_error_cnt > 96) ){ - /* in Error Active Warning area or BUS OFF */ - can->status |= OCCAN_STATUS_WARN; - - /* check reset bit for reset mode */ - if ( READ_REG(can, ®s->mode) & PELICAN_MOD_RESET ){ - /* in reset mode ==> bus off */ - can->status |= OCCAN_STATUS_ERR_BUSOFF | OCCAN_STATUS_RESET; - - /***** pelican_stop(can) ****** - * turn off interrupts - * enter reset mode (HW already done that for us) - */ - WRITE_REG(can, ®s->inten,0); - - /* Indicate that we are not started any more. - * This will make write/read return with EBUSY - * on read/write attempts. - * - * User must issue a ioctl(START) to get going again. - */ - can->started = 0; - - /* signal any waiting read/write threads, so that they - * can handle the bus error. - */ - signal_rx = 1; - signal_tx = 1; - - /* ingnore any old pending interrupt */ - break; - } - - }else{ - /* not in Upper Error Active area any more */ - can->status &= ~(OCCAN_STATUS_WARN); - } - can->stats.err_warn++; - } - - if ( iflags & PELICAN_IF_DOVR){ - can->status |= OCCAN_STATUS_OVERRUN; - can->stats.err_dovr++; - DBG("OCCAN_INT: DOVR\n\r"); - } - - if ( iflags & PELICAN_IF_ERRP){ - /* Let the error counters decide what kind of - * interrupt it was. In/Out of EPassive area. - */ - tx_error_cnt = READ_REG(can, ®s->tx_err_cnt); - rx_error_cnt = READ_REG(can, ®s->rx_err_cnt); - - if ( (tx_error_cnt > 127) || (rx_error_cnt > 127) ){ - can->status |= OCCAN_STATUS_ERR_PASSIVE; - }else{ - can->status &= ~(OCCAN_STATUS_ERR_PASSIVE); - } - - /* increase Error Passive In/out interrupt counter */ - can->stats.err_errp++; - } - - if ( iflags & PELICAN_IF_ARB){ - arbcode = READ_REG(can, ®s->arbcode); - can->stats.err_arb_bitnum[arbcode & PELICAN_ARB_BITS]++; - can->stats.err_arb++; - DBG("OCCAN_INT: ARB (0x%x)\n\r",arbcode & PELICAN_ARB_BITS); - } - - if ( iflags & PELICAN_IF_BUS){ - /* Some kind of BUS error, only used for - * statistics. Error Register is decoded - * and put into can->stats. - */ - errcode = READ_REG(can, ®s->errcode); - switch( errcode & PELICAN_ECC_CODE ){ - case PELICAN_ECC_CODE_BIT: - can->stats.err_bus_bit++; - break; - case PELICAN_ECC_CODE_FORM: - can->stats.err_bus_form++; - break; - case PELICAN_ECC_CODE_STUFF: - can->stats.err_bus_stuff++; - break; - case PELICAN_ECC_CODE_OTHER: - can->stats.err_bus_other++; - break; - } - - /* Get Direction (TX/RX) */ - if ( errcode & PELICAN_ECC_DIR ){ - can->stats.err_bus_rx++; - }else{ - can->stats.err_bus_tx++; - } - - /* Get Segment in frame that went wrong */ - can->stats.err_bus_segs[errcode & PELICAN_ECC_SEG]++; - - /* total number of bus errors */ - can->stats.err_bus++; - } - } - SPIN_UNLOCK(&can->devlock, irqflags); - - /* signal Binary semaphore, messages available! */ - if ( signal_rx ){ - rtems_semaphore_release(can->rxsem); - } - - if ( signal_tx ){ - rtems_semaphore_release(can->txsem); - } -} - -/******************************************************************************* - * FIFO IMPLEMENTATION - */ - -static occan_fifo *occan_fifo_create(int cnt) -{ - occan_fifo *fifo; - fifo = malloc(sizeof(occan_fifo)+cnt*sizeof(CANMsg)); - if ( fifo ){ - fifo->cnt = cnt; - fifo->full = 0; - fifo->ovcnt = 0; - fifo->base = (CANMsg *)&fifo->fifoarea[0]; - fifo->tail = fifo->head = fifo->base; - /* clear CAN Messages */ - memset(fifo->base,0,cnt * sizeof(CANMsg)); - } - return fifo; -} - -static void occan_fifo_free(occan_fifo *fifo) -{ - if ( fifo ) - free(fifo); -} - -static int occan_fifo_full(occan_fifo *fifo) -{ - return fifo->full; -} - -static int occan_fifo_empty(occan_fifo *fifo) -{ - return (!fifo->full) && (fifo->head == fifo->tail); -} - -/* Stage 1 - get buffer to fill (never fails if force!=0) */ -static CANMsg *occan_fifo_put_claim(occan_fifo *fifo, int force) -{ - if ( !fifo ) - return NULL; - - if ( occan_fifo_full(fifo) ){ - - if ( !force ) - return NULL; - - /* all buffers already used ==> overwrite the oldest */ - fifo->ovcnt++; - occan_fifo_get(fifo); - } - - return fifo->head; -} - -/* Stage 2 - increment indexes */ -static void occan_fifo_put(occan_fifo *fifo) -{ - if ( occan_fifo_full(fifo) ) - return; - - /* wrap around */ - fifo->head = (fifo->head >= &fifo->base[fifo->cnt-1])? fifo->base : fifo->head+1; - - if ( fifo->head == fifo->tail ) - fifo->full = 1; -} - -static CANMsg *occan_fifo_claim_get(occan_fifo *fifo) -{ - if ( occan_fifo_empty(fifo) ) - return NULL; - - /* return oldest message */ - return fifo->tail; -} - - -static void occan_fifo_get(occan_fifo *fifo) -{ - if ( !fifo ) - return; - - if ( occan_fifo_empty(fifo) ) - return; - - /* increment indexes */ - fifo->tail = (fifo->tail >= &fifo->base[fifo->cnt-1]) ? - fifo->base : fifo->tail+1; - fifo->full = 0; -} - -static void occan_fifo_clr(occan_fifo *fifo) -{ - fifo->full = 0; - fifo->ovcnt = 0; - fifo->head = fifo->tail = fifo->base; -} - -/******************************************************************************/ |