/* OC_CAN driver * * COPYRIGHT (c) 2007. * Gaisler Research. * * The license and distribution terms for this file may be * found in the file LICENSE in this distribution or at * http://www.rtems.com/license/LICENSE. * * Author: Daniel Hellström, Gaisler Research AB, www.gaisler.com */ #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 #ifndef OCCAN_PREFIX #define OCCAN_PREFIX(name) occan##name #endif #if !defined(OCCAN_DEVNAME) || !defined(OCCAN_DEVNAME_NO) #undef OCCAN_DEVNAME #undef OCCAN_DEVNAME_NO #define OCCAN_DEVNAME "/dev/occan0" #define OCCAN_DEVNAME_NO(devstr,no) ((devstr)[10]='0'+(no)) #endif #ifndef OCCAN_REG_INT #define OCCAN_REG_INT(handler,irq,arg) set_vector(handler,irq+0x10,1) #undef OCCAN_DEFINE_INTHANDLER #define OCCAN_DEFINE_INTHANDLER #endif /* Default to 40MHz system clock */ /*#ifndef SYS_FREQ_HZ #define SYS_FREQ_HZ 40000000 #endif*/ #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 /* 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 */ #ifdef OCCAN_BYTE_REGS 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; } pelican_regs; #else 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]; } pelican_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 { /* hardware shortcuts */ pelican_regs *regs; int irq; occan_speed_regs timing; int channel; /* 0=default, 1=second bus */ int single_mode; /* driver state */ rtems_id devsem; rtems_id txsem; rtems_id rxsem; int open; int started; int rxblk; int txblk; 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 int pelican_speed_auto(occan_priv *priv); 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); static void occan_interrupt(occan_priv *can); #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); #ifdef OCCAN_DEFINE_INTHANDLER static void occan_interrupt_handler(rtems_vector_number v); #endif static int can_cores; static occan_priv *cans; static amba_confarea_type *amba_bus; static unsigned int sys_freq_hz; /* Read byte bypassing */ #ifdef OCCAN_DONT_BYPASS_CACHE #define READ_REG(address) (*(volatile unsigned char *)(address)) #else /* Bypass cache */ #define READ_REG(address) _OCCAN_REG_READ((unsigned int)(address)) static __inline__ unsigned char _OCCAN_REG_READ(unsigned int addr) { unsigned char tmp; __asm__ (" lduba [%1]1, %0 " : "=r"(tmp) : "r"(addr) ); return tmp; } #endif #define WRITE_REG(address,data) (*(volatile unsigned char *)(address) = (data)) /* 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 void pelican_init(occan_priv *priv){ /* Reset core */ priv->regs->mode = PELICAN_MOD_RESET; /* wait for core to reset complete */ /*usleep(1);*/ } static void pelican_open(occan_priv *priv){ /* unsigned char tmp; */ 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 */ priv->regs->clkdiv = (1<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(sys_freq_hz,priv->speed,&priv->timing); } /* disable all interrupts */ priv->regs->inten = 0; /* clear pending interrupts by reading */ /* tmp = */ READ_REG(&priv->regs->intflags); } static int pelican_start(occan_priv *priv){ /* unsigned char tmp; */ /* 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; /* clear pending interrupts */ /* tmp = */ READ_REG(&priv->regs->intflags); /* clear error counters */ priv->regs->rx_err_cnt = 0; 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->regs->bustim0),READ_REG(&priv->regs->bustim1)); /* Set default acceptance filter */ pelican_set_accept(priv,priv->acode,priv->amask); /* turn on interrupts */ 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. */ priv->regs->mode = (priv->single_mode << 3); return 0; } static void pelican_stop(occan_priv *priv){ /* stop HW */ #ifdef DEBUG /* print setup before stopping */ pelican_regs_print(priv->regs); occan_stat_print(&priv->stats); #endif /* put core in reset mode */ priv->regs->mode = PELICAN_MOD_RESET; /* turn off interrupts */ priv->regs->inten = 0; priv->status |= OCCAN_STATUS_RESET; } /* 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,status; pelican_regs *regs = can->regs; /* is there room in send buffer? */ status = READ_REG(®s->status); if ( !(status & PELICAN_STAT_TXBUF) ){ /* tx fifo taken, we have to wait */ return -1; } tmp = msg->len & 0xf; if ( msg->rtr ) tmp |= 0x40; if ( msg->extended ){ /* Extended Frame */ regs->rx_fi_xff = 0x80 | tmp; WRITE_REG(®s->msg.tx_eff.id[0],(msg->id >> (5+8+8)) & 0xff); WRITE_REG(®s->msg.tx_eff.id[1],(msg->id >> (5+8)) & 0xff); WRITE_REG(®s->msg.tx_eff.id[2],(msg->id >> (5)) & 0xff); WRITE_REG(®s->msg.tx_eff.id[3],(msg->id << 3) & 0xf8); tmp = msg->len; while(tmp--){ WRITE_REG(®s->msg.tx_eff.data[tmp],msg->data[tmp]); } }else{ /* Standard Frame */ regs->rx_fi_xff = tmp; WRITE_REG(®s->msg.tx_sff.id[0],(msg->id >> 3) & 0xff); WRITE_REG(®s->msg.tx_sff.id[1],(msg->id << 5) & 0xe0); tmp = msg->len; while(tmp--){ WRITE_REG(®s->msg.tx_sff.data[tmp],msg->data[tmp]); } } /* let HW know of new message */ if ( msg->sshot ){ regs->cmd = PELICAN_CMD_TXREQ | PELICAN_CMD_ABORT; }else{ /* normal case -- try resend until sent */ regs->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 */ *acode0 = acode[0]; *acode1 = acode[1]; *acode2 = acode[2]; *acode3 = acode[3]; *amask0 = amask[0]; *amask1 = amask[1]; *amask2 = amask[2]; *amask3 = amask[3]; } #ifdef DEBUG static void pelican_regs_print(pelican_regs *regs){ printk("--- PELICAN 0x%lx ---\n\r",(unsigned int)regs); printk(" MODE: 0x%02x\n\r",READ_REG(®s->mode)); printk(" CMD: 0x%02x\n\r",READ_REG(®s->cmd)); printk(" STATUS: 0x%02x\n\r",READ_REG(®s->status)); /*printk(" INTFLG: 0x%02x\n\r",READ_REG(®s->intflags));*/ printk(" INTEN: 0x%02x\n\r",READ_REG(®s->inten)); printk(" BTR0: 0x%02x\n\r",READ_REG(®s->bustim0)); printk(" BTR1: 0x%02x\n\r",READ_REG(®s->bustim1)); printk(" ARBCODE: 0x%02x\n\r",READ_REG(®s->arbcode)); printk(" ERRCODE: 0x%02x\n\r",READ_REG(®s->errcode)); printk(" ERRWARN: 0x%02x\n\r",READ_REG(®s->errwarn)); printk(" RX_ERR_CNT: 0x%02x\n\r",READ_REG(®s->rx_err_cnt)); printk(" TX_ERR_CNT: 0x%02x\n\r",READ_REG(®s->tx_err_cnt)); if ( READ_REG(®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(®s->rx_fi_xff),®s->rx_fi_xff); printk(" ACR1: 0x%02x (0x%lx)\n\r",READ_REG(®s->msg.rst_accept.code[0]),(unsigned int)®s->msg.rst_accept.code[0]); printk(" ACR1: 0x%02x (0x%lx)\n\r",READ_REG(®s->msg.rst_accept.code[1]),(unsigned int)®s->msg.rst_accept.code[1]); printk(" ACR1: 0x%02x (0x%lx)\n\r",READ_REG(®s->msg.rst_accept.code[2]),(unsigned int)®s->msg.rst_accept.code[2]); printk(" AMR0: 0x%02x (0x%lx)\n\r",READ_REG(®s->msg.rst_accept.mask[0]),(unsigned int)®s->msg.rst_accept.mask[0]); printk(" AMR1: 0x%02x (0x%lx)\n\r",READ_REG(®s->msg.rst_accept.mask[1]),(unsigned int)®s->msg.rst_accept.mask[1]); printk(" AMR2: 0x%02x (0x%lx)\n\r",READ_REG(®s->msg.rst_accept.mask[2]),(unsigned int)®s->msg.rst_accept.mask[2]); printk(" AMR3: 0x%02x (0x%lx)\n\r",READ_REG(®s->msg.rst_accept.mask[3]),(unsigned int)®s->msg.rst_accept.mask[3]); }else{ printk(" RXFI_XFF: 0x%02x\n\r",READ_REG(®s->rx_fi_xff)); } printk(" RX_MSG_CNT: 0x%02x\n\r",READ_REG(®s->rx_msg_cnt)); printk(" CLKDIV: 0x%02x\n\r",READ_REG(®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 /* 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, best_rate=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; best_rate = clock/(brp*(1+tseg/2)); } } 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; priv->regs->bustim0 = timing->btr0; priv->regs->bustim1 = timing->btr1; return 0; } #if 0 static unsigned int pelican_speed_auto_steplist [] = { OCCAN_SPEED_500K, OCCAN_SPEED_250K, OCCAN_SPEED_125K, OCCAN_SPEED_75K, OCCAN_SPEED_50K, OCCAN_SPEED_25K, OCCAN_SPEED_10K, 0 }; #endif static int pelican_speed_auto(occan_priv *priv){ return -1; #if 0 int i=0; occan_speed_regs timing; unsigned int speed; unsigned char tmp; while ( (speed=pelican_speed_auto_steplist[i]) > 0){ /* Reset core */ priv->regs->mode = PELICAN_MOD_RESET; /* tell int handler about the auto speed detection test */ /* wait for a moment (10ms) */ /*usleep(10000);*/ /* No acceptance filter */ pelican_set_accept(priv); /* calc timing params for this */ if ( occan_calc_speedregs(sys_freq_hz,speed,&timing) ){ /* failed to get good timings for this frequency * test with next */ continue; } timing.sam = 0; /* set timing params for this speed */ occan_set_speedregs(priv,&timing); /* Empty previous messages in hardware RX fifo */ /* while( READ_REG(&priv->regs->) ){ } */ /* Clear pending interrupts */ tmp = READ_REG(&priv->regs->intflags); /* enable RX & ERR interrupt */ priv->regs->inten = /* Get out of reset state */ priv->regs->mode = PELICAN_MOD_LISTEN; /* wait for frames or errors */ while(1){ /* sleep 10ms */ } } #endif } static rtems_device_driver occan_initialize(rtems_device_major_number major, rtems_device_minor_number unused, void *arg){ int dev_cnt,minor,subcore_cnt,devi,subi,subcores; amba_ahb_device ambadev; occan_priv *can; char fs_name[20]; rtems_status_code status; strcpy(fs_name,OCCAN_DEVNAME); /* find device on amba bus */ dev_cnt = amba_get_number_ahbslv_devices(amba_bus,VENDOR_GAISLER,GAISLER_OCCAN); if ( dev_cnt < 1 ){ /* Failed to find any CAN cores! */ printk("OCCAN: Failed to find any CAN cores\n\r"); return -1; } /* Detect System Frequency from initialized timer */ #ifndef SYS_FREQ_HZ #if defined(LEON3) /* LEON3: find timer address via AMBA Plug&Play info */ { amba_apb_device gptimer; LEON3_Timer_Regs_Map *tregs; if ( amba_find_apbslv(&amba_conf,VENDOR_GAISLER,GAISLER_GPTIMER,&gptimer) == 1 ){ tregs = (LEON3_Timer_Regs_Map *)gptimer.start; sys_freq_hz = (tregs->scaler_reload+1)*1000*1000; DBG("OCCAN: detected %dHZ system frequency\n\r",sys_freq_hz); }else{ sys_freq_hz = 40000000; /* Default to 40MHz */ printk("OCCAN: Failed to detect system frequency\n\r"); } } #elif defined(LEON2) /* LEON2: use hardcoded address to get to timer */ { LEON_Register_Map *regs = (LEON_Register_Map *)0x80000000; sys_freq_hz = (regs->Scaler_Reload+1)*1000*1000; } #else #error CPU not supported for OC_CAN driver #endif #else /* Use hardcoded frequency */ sys_freq_hz = SYS_FREQ_HZ; #endif DBG("OCCAN: Detected %dHz system frequency\n\r",sys_freq_hz); /* OCCAN speciality: * Mulitple cores are supported through the same amba AHB interface. * The number of "sub cores" can be detected by decoding the AMBA * Plug&Play version information. verion = ncores. A maximum of 8 * sub cores are supported, each separeated with 0x100 inbetween. * * Now, lets detect sub cores. */ for(subcore_cnt=devi=0; deviregs = (void *)(ambadev.start[0] + OCCAN_NCORE_OFS*subi); #else /* regs is word regs, accessed 0x100 from base address */ can->regs = (void *)(ambadev.start[0] + OCCAN_NCORE_OFS*subi+ OCCAN_WORD_REG_OFS); #endif /* remember IRQ number */ can->irq = ambadev.irq+subi; /* bind filesystem name to device */ OCCAN_DEVNAME_NO(fs_name,minor); printk("OCCAN: Registering %s to [%d %d] @ 0x%lx irq %d\n\r",fs_name,major,minor,(unsigned int)can->regs,can->irq); status = rtems_io_register_name(fs_name, major, minor); if (RTEMS_SUCCESSFUL != status ) rtems_fatal_error_occurred(status); /* initialize software */ can->open = 0; can->rxfifo = NULL; can->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, &can->devsem); if ( status != RTEMS_SUCCESSFUL ){ printk("OCCAN: Failed to create dev semaphore for minor %d, (%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, &can->txsem); if ( status != RTEMS_SUCCESSFUL ){ printk("OCCAN: Failed to create tx semaphore for minor %d, (%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, &can->rxsem); if ( status != RTEMS_SUCCESSFUL ){ printk("OCCAN: Failed to create rx semaphore for minor %d, (%d)\n\r",minor,status); return RTEMS_UNSATISFIED; } /* hardware init/reset */ pelican_init(can); /* Setup interrupt handler for each channel */ OCCAN_REG_INT(OCCAN_PREFIX(_interrupt_handler), can->irq, can); minor++; #ifdef DEBUG_PRINT_REGMAP pelican_regadr_print(can->regs); #endif } } return RTEMS_SUCCESSFUL; } static rtems_device_driver occan_open(rtems_device_major_number major, rtems_device_minor_number minor, void *arg){ occan_priv *can; DBG("OCCAN: Opening %d\n\r",minor); if ( minor >= can_cores ) return RTEMS_UNSATISFIED; /* NODEV */ /* get can device */ can = &cans[minor]; /* 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); /* 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 = &cans[minor]; DBG("OCCAN: Closing %d\n\r",minor); /* stop if running */ if ( can->started ) pelican_stop(can); /* Enter Reset Mode */ 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; return RTEMS_SUCCESSFUL; } static rtems_device_driver occan_read(rtems_device_major_number major, rtems_device_minor_number minor, void *arg){ occan_priv *can = &cans[minor]; rtems_libio_rw_args_t *rw_args=(rtems_libio_rw_args_t *) arg; CANMsg *dstmsg, *srcmsg; rtems_interrupt_level oldLevel; int left; 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 */ rtems_interrupt_disable(oldLevel); /* A bus off interrupt may have occured after checking can->started */ if ( can->status & (OCCAN_STATUS_ERR_BUSOFF|OCCAN_STATUS_RESET) ){ rtems_interrupt_enable(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 */ rtems_interrupt_enable(oldLevel); break; } /* turn on interrupts again */ rtems_interrupt_enable(oldLevel); DBG("OCCAN: Waiting for RX int\n\r"); /* wait for incomming 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 */ rtems_interrupt_enable(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 = &cans[minor]; rtems_libio_rw_args_t *rw_args=(rtems_libio_rw_args_t *) arg; CANMsg *msg,*fifo_msg; rtems_interrupt_level oldLevel; int left; DBG("OCCAN: Writing %d bytes from 0x%lx (%d)\n\r",rw_args->count,rw_args->buffer,sizeof(CANMsg)); 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 */ rtems_interrupt_disable(oldLevel); /* A bus off interrupt may have occured after checking can->started */ if ( can->status & (OCCAN_STATUS_ERR_BUSOFF|OCCAN_STATUS_RESET) ){ rtems_interrupt_enable(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++; /* 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; */ rtems_interrupt_enable(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 */ } rtems_interrupt_disable(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++; /* 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); } rtems_interrupt_enable(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 = &cans[minor]; 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); 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(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; if ( can->started ) return RTEMS_RESOURCE_IN_USE; /* EBUSY */ if ( (speed=pelican_speed_auto(can)) < 0 ){ /* failed */ return RTEMS_IO_ERROR; } /* set new speed */ can->speed = speed; if ( (int *)ioarg->buffer ){ *(int *)ioarg->buffer = speed; } return RTEMS_SUCCESSFUL; break; 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; 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; } static void occan_interrupt(occan_priv *can){ 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; can->stats.ints++; while ( (iflags = READ_REG(&can->regs->intflags)) != 0 ){ /* still interrupts to handle */ 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(®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(®s->msg.rx_eff.id[0])<<(5+8+8) | READ_REG(®s->msg.rx_eff.id[1])<<(5+8) | READ_REG(®s->msg.rx_eff.id[2])<<5 | READ_REG(®s->msg.rx_eff.id[3])>>3; while(tmp--){ msg->data[tmp] = READ_REG(®s->msg.rx_eff.data[tmp]); } /* msg->data[0] = READ_REG(®s->msg.rx_eff.data[0]); msg->data[1] = READ_REG(®s->msg.rx_eff.data[1]); msg->data[2] = READ_REG(®s->msg.rx_eff.data[2]); msg->data[3] = READ_REG(®s->msg.rx_eff.data[3]); msg->data[4] = READ_REG(®s->msg.rx_eff.data[4]); msg->data[5] = READ_REG(®s->msg.rx_eff.data[5]); msg->data[6] = READ_REG(®s->msg.rx_eff.data[6]); msg->data[7] = READ_REG(®s->msg.rx_eff.data[7]); */ }else{ /* standard message */ msg->id = READ_REG(®s->msg.rx_sff.id[0])<<3 | READ_REG(®s->msg.rx_sff.id[1])>>5; while(tmp--){ msg->data[tmp] = READ_REG(®s->msg.rx_sff.data[tmp]); } /* msg->data[0] = READ_REG(®s->msg.rx_sff.data[0]); msg->data[1] = READ_REG(®s->msg.rx_sff.data[1]); msg->data[2] = READ_REG(®s->msg.rx_sff.data[2]); msg->data[3] = READ_REG(®s->msg.rx_sff.data[3]); msg->data[4] = READ_REG(®s->msg.rx_sff.data[4]); msg->data[5] = READ_REG(®s->msg.rx_sff.data[5]); msg->data[6] = READ_REG(®s->msg.rx_sff.data[6]); msg->data[7] = READ_REG(®s->msg.rx_sff.data[7]); */ } /* Re-Enable RX buffer for a new message */ regs->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++; } /* 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(®s->tx_err_cnt); rx_error_cnt = READ_REG(®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(®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) */ regs->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(®s->tx_err_cnt); rx_error_cnt = READ_REG(®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(®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(®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++; } } /* signal Binary semaphore, messages available! */ if ( signal_rx ){ rtems_semaphore_release(can->rxsem); } if ( signal_tx ){ rtems_semaphore_release(can->txsem); } } #ifdef OCCAN_DEFINE_INTHANDLER static void occan_interrupt_handler(rtems_vector_number v){ int minor; /* convert to */ for(minor = 0; minor < can_cores; minor++) { if ( v == (cans[minor].irq+0x10) ) { occan_interrupt(&cans[minor]); return; } } } #endif #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; int OCCAN_PREFIX(_register)(amba_confarea_type *bus){ rtems_status_code r; rtems_device_major_number m; amba_bus = bus; if ( !bus ) return 1; if ((r = rtems_io_register_driver(0, &occan_driver, &m)) == RTEMS_SUCCESSFUL) { DBG("OCCAN driver successfully registered, major: %d\n\r", m); }else{ switch(r) { case RTEMS_TOO_MANY: printk("OCCAN rtems_io_register_driver failed: RTEMS_TOO_MANY\n\r"); break; case RTEMS_INVALID_NUMBER: printk("OCCAN rtems_io_register_driver failed: RTEMS_INVALID_NUMBER\n\r"); break; case RTEMS_RESOURCE_IN_USE: printk("OCCAN rtems_io_register_driver failed: RTEMS_RESOURCE_IN_USE\n\r"); break; default: printk("OCCAN rtems_io_register_driver failed\n\r"); } return 1; } return 0; } /******************************************************************************* * 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; } /*******************************************************************************/