/* 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 #include #include #include #include #include #include /* printk */ #include #include #include /* 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<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<btr1 = (0<<7) | (tseg2<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; } /******************************************************************************/