/* * GRCAN driver * * COPYRIGHT (c) 2007-2019. * Cobham Gaisler AB. * * The license and distribution terms for this file may be * found in the file LICENSE in this distribution or at * http://www.rtems.org/license/LICENSE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "grcan_internal.h" /* Maximum number of GRCAN devices supported by driver */ #define GRCAN_COUNT_MAX 8 #define BLOCK_SIZE (16*4) /* grcan needs to have it buffers aligned to 1k boundaries */ #define BUFFER_ALIGNMENT_NEEDS 1024 /* Default Maximium buffer size for statically allocated buffers */ #ifndef TX_BUF_SIZE #define TX_BUF_SIZE (BLOCK_SIZE*16) #endif /* Make receiver buffers bigger than transmitt */ #ifndef RX_BUF_SIZE #define RX_BUF_SIZE ((3*BLOCK_SIZE)*16) #endif #ifndef IRQ_CLEAR_PENDING #define IRQ_CLEAR_PENDING(irqno) #endif #ifndef IRQ_UNMASK #define IRQ_UNMASK(irqno) #endif #ifndef IRQ_MASK #define IRQ_MASK(irqno) #endif /* Uncomment for debug output */ /****************** DEBUG Definitions ********************/ #define DBG_TX 2 #define DBG_RX 4 #define DBG_STATE 8 #define DEBUG_FLAGS (DBG_STATE | DBG_RX | DBG_TX ) /* #define DEBUG #define DEBUGFUNCS */ #include /*********************************************************/ int state2err[4] = { /* STATE_STOPPED */ GRCAN_RET_NOTSTARTED, /* STATE_STARTED */ GRCAN_RET_OK, /* STATE_BUSOFF */ GRCAN_RET_BUSOFF, /* STATE_AHBERR */ GRCAN_RET_AHBERR }; static void __inline__ grcan_hw_reset(struct grcan_regs *regs); static void grcan_hw_config( struct grcan_priv *pDev, struct grcan_config *conf); static void grcan_hw_accept( struct grcan_regs *regs, struct grcan_filter *afilter); static void grcan_hw_sync( struct grcan_regs *regs, struct grcan_filter *sfilter); static void grcan_interrupt(void *arg); #define NELEM(a) ((int) (sizeof (a) / sizeof (a[0]))) /* GRCAN nominal boundaries for baud-rate paramters */ struct grlib_canbtrs_ranges grcan_btrs_ranges = { .max_scaler = 256*8, /* scaler is multiplied by BPR in steps 1,2,4,8 */ .has_bpr = 1, .divfactor = 2, .min_tseg1 = 1, .max_tseg1 = 15, .min_tseg2 = 2, .max_tseg2 = 8, }; /* GRCANFD nominal boundaries */ struct grlib_canbtrs_ranges grcanfd_nom_btrs_ranges = { .max_scaler = 256, .has_bpr = 0, .divfactor = 1, .min_tseg1 = 2, .max_tseg1 = 63, .min_tseg2 = 2, .max_tseg2 = 16, }; /* GRCANFD flexible baud-rate boundaries */ struct grlib_canbtrs_ranges grcanfd_fd_btrs_ranges = { .max_scaler = 256, .has_bpr = 0, .divfactor = 1, .min_tseg1 = 1, .max_tseg1 = 15, .min_tseg2 = 2, .max_tseg2 = 8, }; static int grcan_count = 0; static struct grcan_priv *priv_tab[GRCAN_COUNT_MAX]; /******************* Driver manager interface ***********************/ /* Driver prototypes */ int grcan_device_init(struct grcan_priv *pDev); int grcan_init2(struct drvmgr_dev *dev); int grcan_init3(struct drvmgr_dev *dev); struct drvmgr_drv_ops grcan_ops = { .init = {NULL, grcan_init2, grcan_init3, NULL}, .remove = NULL, .info = NULL }; struct amba_dev_id grcan_ids[] = { {VENDOR_GAISLER, GAISLER_GRCAN}, {VENDOR_GAISLER, GAISLER_GRHCAN}, {VENDOR_GAISLER, GAISLER_GRCANFD}, {0, 0} /* Mark end of table */ }; struct amba_drv_info grcan_drv_info = { { DRVMGR_OBJ_DRV, /* Driver */ NULL, /* Next driver */ NULL, /* Device list */ DRIVER_AMBAPP_GAISLER_GRCAN_ID, /* Driver ID */ "GRCAN_DRV", /* Driver Name */ DRVMGR_BUS_TYPE_AMBAPP, /* Bus Type */ &grcan_ops, NULL, /* Funcs */ 0, /* No devices yet */ 0, }, &grcan_ids[0] }; void grcan_register_drv (void) { DBG("Registering GRCAN driver\n"); drvmgr_drv_register(&grcan_drv_info.general); } int grcan_init2(struct drvmgr_dev *dev) { struct grcan_priv *priv; DBG("GRCAN[%d] on bus %s\n", dev->minor_drv, dev->parent->dev->name); if (GRCAN_COUNT_MAX <= grcan_count) return DRVMGR_ENORES; priv = dev->priv = grlib_calloc(1, sizeof(*priv)); if ( !priv ) return DRVMGR_NOMEM; priv->dev = dev; /* This core will not find other cores, so we wait for init2() */ return DRVMGR_OK; } int grcan_init3(struct drvmgr_dev *dev) { struct grcan_priv *priv; char prefix[32]; priv = dev->priv; /* * Now we take care of device initialization. */ if ( grcan_device_init(priv) ) { return DRVMGR_FAIL; } priv_tab[grcan_count] = priv; grcan_count++; /* 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, "grcan%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, "%sgrcan%d", prefix, dev->minor_bus); } return DRVMGR_OK; } int grcan_device_init(struct grcan_priv *pDev) { struct amba_dev_info *ambadev; struct ambapp_core *pnpinfo; /* 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 = (struct grcan_regs *)pnpinfo->apb_slv->start; pDev->minor = pDev->dev->minor_drv; if (ambadev->id.device == GAISLER_GRCANFD) pDev->fd_capable = 1; /* Get frequency in Hz */ if ( drvmgr_freq_get(pDev->dev, DEV_APB_SLV, &pDev->corefreq_hz) ) { return -1; } DBG("GRCAN frequency: %d Hz\n", pDev->corefreq_hz); /* Reset Hardware before attaching IRQ handler */ grcan_hw_reset(pDev->regs); /* RX Semaphore created with count = 0 */ if ( rtems_semaphore_create(rtems_build_name('G', 'C', 'R', '0' + pDev->minor), 0, RTEMS_FIFO|RTEMS_SIMPLE_BINARY_SEMAPHORE|RTEMS_NO_INHERIT_PRIORITY|\ RTEMS_LOCAL|RTEMS_NO_PRIORITY_CEILING, 0, &pDev->rx_sem) != RTEMS_SUCCESSFUL ) { return RTEMS_INTERNAL_ERROR; } /* TX Semaphore created with count = 0 */ if ( rtems_semaphore_create(rtems_build_name('G', 'C', 'T', '0' + pDev->minor), 0, RTEMS_FIFO|RTEMS_SIMPLE_BINARY_SEMAPHORE|RTEMS_NO_INHERIT_PRIORITY|\ RTEMS_LOCAL|RTEMS_NO_PRIORITY_CEILING, 0, &pDev->tx_sem) != RTEMS_SUCCESSFUL ) { return RTEMS_INTERNAL_ERROR; } /* TX Empty Semaphore created with count = 0 */ if ( rtems_semaphore_create(rtems_build_name('G', 'C', 'E', '0' + pDev->minor), 0, RTEMS_FIFO|RTEMS_SIMPLE_BINARY_SEMAPHORE|RTEMS_NO_INHERIT_PRIORITY|\ RTEMS_LOCAL|RTEMS_NO_PRIORITY_CEILING, 0, &pDev->txempty_sem) != RTEMS_SUCCESSFUL ) { return RTEMS_INTERNAL_ERROR; } /* Device Semaphore created with count = 1 */ if ( rtems_semaphore_create(rtems_build_name('G', 'C', 'A', '0' + pDev->minor), 1, RTEMS_FIFO|RTEMS_SIMPLE_BINARY_SEMAPHORE|RTEMS_NO_INHERIT_PRIORITY|\ RTEMS_LOCAL|RTEMS_NO_PRIORITY_CEILING, 0, &pDev->dev_sem) != RTEMS_SUCCESSFUL ) { return RTEMS_INTERNAL_ERROR; } return 0; } static void __inline__ grcan_hw_reset(struct grcan_regs *regs) { regs->ctrl = GRCAN_CTRL_RESET; } static rtems_device_driver grcan_hw_start(struct grcan_priv *pDev) { /* * tmp is set but never used. GCC gives a warning for this * and we need to tell GCC not to complain. */ unsigned int tmp RTEMS_UNUSED; SPIN_IRQFLAGS(oldLevel); FUNCDBG(); /* Check that memory has been allocated successfully */ if (!pDev->tx || !pDev->rx) return RTEMS_NO_MEMORY; /* Configure FIFO configuration register * and Setup timing */ if (pDev->config_changed) { grcan_hw_config(pDev, &pDev->config); pDev->config_changed = 0; } /* Setup receiver */ pDev->regs->rx0addr = (unsigned int)pDev->_rx_hw; pDev->regs->rx0size = pDev->rxbuf_size; /* Setup Transmitter */ pDev->regs->tx0addr = (unsigned int)pDev->_tx_hw; pDev->regs->tx0size = pDev->txbuf_size; /* Setup acceptance filters */ grcan_hw_accept(pDev->regs, &pDev->afilter); /* Sync filters */ grcan_hw_sync(pDev->regs, &pDev->sfilter); /* Clear status bits */ tmp = READ_REG(&pDev->regs->stat); pDev->regs->stat = 0; /* Setup IRQ handling */ /* Clear all IRQs */ tmp = READ_REG(&pDev->regs->pir); pDev->regs->picr = 0x1ffff; /* unmask TxLoss|TxErrCntr|RxErrCntr|TxAHBErr|RxAHBErr|OR|OFF|PASS */ pDev->regs->imr = 0x1601f; /* Enable routing of the IRQs */ SPIN_LOCK_IRQ(&pDev->devlock, oldLevel); IRQ_UNMASK(pDev->irq + GRCAN_IRQ_TXSYNC); IRQ_UNMASK(pDev->irq + GRCAN_IRQ_RXSYNC); IRQ_UNMASK(pDev->irq + GRCAN_IRQ_IRQ); SPIN_UNLOCK_IRQ(&pDev->devlock, oldLevel); /* Enable receiver/transmitter */ pDev->regs->rx0ctrl = GRCAN_RXCTRL_ENABLE; pDev->regs->tx0ctrl = GRCAN_TXCTRL_ENABLE; /* Enable HurriCANe core */ pDev->regs->ctrl = GRCAN_CTRL_ENABLE; /* Leave transmitter disabled, it is enabled when * trying to send something. */ return RTEMS_SUCCESSFUL; } static void grcan_hw_stop(struct grcan_priv *pDev) { FUNCDBG(); /* Mask all IRQs */ pDev->regs->imr = 0; IRQ_MASK(pDev->irq + GRCAN_IRQ_TXSYNC); IRQ_MASK(pDev->irq + GRCAN_IRQ_RXSYNC); IRQ_MASK(pDev->irq + GRCAN_IRQ_IRQ); /* Disable receiver & transmitter */ pDev->regs->rx0ctrl = 0; pDev->regs->tx0ctrl = 0; } static void grcan_sw_stop(struct grcan_priv *pDev) { /* * Release semaphores to wake all threads waiting for an IRQ. * The threads that * get woken up must check started state in * order to determine that they should return to * user space with error status. * * Entering into started mode again will reset the * semaphore count. */ rtems_semaphore_release(pDev->rx_sem); rtems_semaphore_release(pDev->tx_sem); rtems_semaphore_release(pDev->txempty_sem); } static void grcan_hw_config(struct grcan_priv *pDev, struct grcan_config *conf) { unsigned int config = 0; struct grcan_regs *regs = pDev->regs; /* Reset HurriCANe Core */ regs->ctrl = 0; if (conf->silent) config |= GRCAN_CFG_SILENT; if (conf->abort) config |= GRCAN_CFG_ABORT; if (conf->selection.selection) config |= GRCAN_CFG_SELECTION; if (conf->selection.enable0) config |= GRCAN_CFG_ENABLE0; if (conf->selection.enable1) config |= GRCAN_CFG_ENABLE1; /* Timing */ if (!pDev->fd_capable) { config |= (conf->timing.bpr << GRCAN_CFG_BPR_BIT) & GRCAN_CFG_BPR; config |= (conf->timing.rsj << GRCAN_CFG_RSJ_BIT) & GRCAN_CFG_RSJ; config |= (conf->timing.ps1 << GRCAN_CFG_PS1_BIT) & GRCAN_CFG_PS1; config |= (conf->timing.ps2 << GRCAN_CFG_PS2_BIT) & GRCAN_CFG_PS2; config |= (conf->timing.scaler << GRCAN_CFG_SCALER_BIT) & GRCAN_CFG_SCALER; } else { regs->nbtr = (conf->timing.scaler << GRCANFD_NBTR_SCALER_BIT) | (conf->timing.ps1 << GRCANFD_NBTR_PS1_BIT) | (conf->timing.ps2 << GRCANFD_NBTR_PS2_BIT) | (conf->timing.rsj << GRCANFD_NBTR_SJW_BIT); regs->fdbtr = (conf->timing_fd.scaler << GRCANFD_FDBTR_SCALER_BIT) | (conf->timing_fd.ps1 << GRCANFD_FDBTR_PS1_BIT) | (conf->timing_fd.ps2 << GRCANFD_FDBTR_PS2_BIT) | (conf->timing_fd.sjw << GRCANFD_FDBTR_SJW_BIT); } /* Write configuration */ regs->conf = config; /* Enable HurriCANe Core */ regs->ctrl = GRCAN_CTRL_ENABLE; } static void grcan_hw_accept( struct grcan_regs *regs, struct grcan_filter *afilter ) { /* Disable Sync mask totaly (if we change scode or smask * in an unfortunate way we may trigger a sync match) */ regs->rx0mask = 0xffffffff; /* Set Sync Filter in a controlled way */ regs->rx0code = afilter->code; regs->rx0mask = afilter->mask; } static void grcan_hw_sync(struct grcan_regs *regs, struct grcan_filter *sfilter) { /* Disable Sync mask totaly (if we change scode or smask * in an unfortunate way we may trigger a sync match) */ regs->smask = 0xffffffff; /* Set Sync Filter in a controlled way */ regs->scode = sfilter->code; regs->smask = sfilter->mask; } int grcan_wait_rxdata(struct grcan_priv *pDev, int min) { unsigned int wp, rp, size, irq; unsigned int irq_trunk, dataavail; int wait, state; SPIN_IRQFLAGS(oldLevel); FUNCDBG(); /*** block until receive IRQ received * Set up a valid IRQ point so that an IRQ is received * when one or more messages are received */ SPIN_LOCK_IRQ(&pDev->devlock, oldLevel); state = pDev->started; /* A bus off interrupt may have occured after checking pDev->started */ if (state != STATE_STARTED) { SPIN_UNLOCK_IRQ(&pDev->devlock, oldLevel); if (state == STATE_BUSOFF) { DBGC(DBG_STATE, "cancelled due to a BUS OFF error\n"); } else if (state == STATE_AHBERR) { DBGC(DBG_STATE, "cancelled due to a AHB error\n"); } else { DBGC(DBG_STATE, "cancelled due to STOP (unexpected) \n"); } return state2err[state]; } size = READ_REG(&pDev->regs->rx0size); rp = READ_REG(&pDev->regs->rx0rd); wp = READ_REG(&pDev->regs->rx0wr); /**** Calculate IRQ Pointer ****/ irq = wp + min * GRCAN_MSG_SIZE; /* wrap irq around */ if (irq >= size) { irq_trunk = irq - size; } else irq_trunk = irq; /* init IRQ HW */ pDev->regs->rx0irq = irq_trunk; /* Clear pending Rx IRQ */ pDev->regs->picr = GRCAN_RXIRQ_IRQ; wp = READ_REG(&pDev->regs->rx0wr); /* Calculate messages available */ dataavail = grcan_hw_rxavail(rp, wp, size); if (dataavail < min) { /* Still empty, proceed with sleep - Turn on IRQ (unmask irq) */ pDev->regs->imr = READ_REG(&pDev->regs->imr) | GRCAN_RXIRQ_IRQ; wait = 1; } else { /* enough message has been received, abort sleep - don't unmask interrupt */ wait = 0; } SPIN_UNLOCK_IRQ(&pDev->devlock, oldLevel); /* Wait for IRQ to fire only if has been triggered */ if (wait) { rtems_semaphore_obtain(pDev->rx_sem, RTEMS_WAIT, RTEMS_NO_TIMEOUT); /* * The semaphore is released either due to the expected IRQ * condition or by BUSOFF, AHBERROR or another thread calling * grcan_stop(). In either case, state2err[] has the correnct * return value. */ return state2err[pDev->started]; } return 0; } /* Wait for TX circular buffer to have room for min CAN messagges. TXIRQ is used to pin * point the location of the CAN message corresponding to min. * * min must be at least WRAP_AROUND_TX_MSGS less than max buffer capacity * (pDev->txbuf_size/GRCAN_MSG_SIZE) for this algo to work. */ int grcan_wait_txspace(struct grcan_priv *pDev, int min) { int wait, state; unsigned int irq, rp, wp, size, space_left; unsigned int irq_trunk; SPIN_IRQFLAGS(oldLevel); DBGC(DBG_TX, "\n"); /*FUNCDBG(); */ SPIN_LOCK_IRQ(&pDev->devlock, oldLevel); state = pDev->started; /* A bus off interrupt may have occured after checking pDev->started */ if (state != STATE_STARTED) { SPIN_UNLOCK_IRQ(&pDev->devlock, oldLevel); if (state == STATE_BUSOFF) { DBGC(DBG_STATE, "cancelled due to a BUS OFF error\n"); } else if (state == STATE_AHBERR) { DBGC(DBG_STATE, "cancelled due to a AHB error\n"); } else { DBGC(DBG_STATE, "cancelled due to STOP (unexpected)\n"); } return state2err[state]; } pDev->regs->tx0ctrl = GRCAN_TXCTRL_ENABLE; size = READ_REG(&pDev->regs->tx0size); wp = READ_REG(&pDev->regs->tx0wr); rp = READ_REG(&pDev->regs->tx0rd); /**** Calculate IRQ Pointer ****/ irq = rp + min * GRCAN_MSG_SIZE; /* wrap irq around */ if (irq >= size) { irq_trunk = irq - size; } else irq_trunk = irq; /* trigger HW to do a IRQ when enough room in buffer */ pDev->regs->tx0irq = irq_trunk; /* Clear pending Tx IRQ */ pDev->regs->picr = GRCAN_TXIRQ_IRQ; /* One problem, if HW already gone past IRQ place the IRQ will * never be received resulting in a thread hang. We check if so * before proceeding. * * has the HW already gone past the IRQ generation place? * == does min fit info tx buffer? */ rp = READ_REG(&pDev->regs->tx0rd); space_left = grcan_hw_txspace(rp, wp, size); if (space_left < min) { /* Still too full, proceed with sleep - Turn on IRQ (unmask irq) */ pDev->regs->imr = READ_REG(&pDev->regs->imr) | GRCAN_TXIRQ_IRQ; wait = 1; } else { /* There are enough room in buffer, abort wait - don't unmask interrupt */ wait = 0; } SPIN_UNLOCK_IRQ(&pDev->devlock, oldLevel); /* Wait for IRQ to fire only if it has been triggered */ if (wait) { rtems_semaphore_obtain(pDev->tx_sem, RTEMS_WAIT, RTEMS_NO_TIMEOUT); return state2err[pDev->started]; } /* At this point the TxIRQ has been masked, we need not to mask it */ return 0; } static int grcan_tx_flush(struct grcan_priv *pDev) { int wait, state; unsigned int rp, wp; SPIN_IRQFLAGS(oldLevel); FUNCDBG(); /* loop until all data in circular buffer has been read by hw. * (write pointer != read pointer ) * * Hardware doesn't update write pointer - we do */ while ( (wp = READ_REG(&pDev->regs->tx0wr)) != (rp = READ_REG(&pDev->regs->tx0rd)) ) { /* Wait for TX empty IRQ */ SPIN_LOCK_IRQ(&pDev->devlock, oldLevel); state = pDev->started; /* A bus off interrupt may have occured after checking pDev->started */ if (state != STATE_STARTED) { SPIN_UNLOCK_IRQ(&pDev->devlock, oldLevel); if (state == STATE_BUSOFF) { DBGC(DBG_STATE, "cancelled due to a BUS OFF error\n"); } else if (state == STATE_AHBERR) { DBGC(DBG_STATE, "cancelled due to a AHB error\n"); } else { DBGC(DBG_STATE, "cancelled due to STOP (unexpected)\n"); } return state2err[state]; } /* Clear pending TXEmpty IRQ */ pDev->regs->picr = GRCAN_TXEMPTY_IRQ; if (wp != READ_REG(&pDev->regs->tx0rd)) { /* Still not empty, proceed with sleep - Turn on IRQ (unmask irq) */ pDev->regs->imr = READ_REG(&pDev->regs->imr) | GRCAN_TXEMPTY_IRQ; wait = 1; } else { /* TX fifo is empty */ wait = 0; } SPIN_UNLOCK_IRQ(&pDev->devlock, oldLevel); if (!wait) break; /* Wait for IRQ to wake us */ rtems_semaphore_obtain(pDev->txempty_sem, RTEMS_WAIT, RTEMS_NO_TIMEOUT); state = pDev->started; if (state != STATE_STARTED) { return state2err[state]; } } return 0; } static int grcan_alloc_buffers(struct grcan_priv *pDev, int rx, int tx) { unsigned int adr; FUNCDBG(); if ( tx ) { adr = (unsigned int)pDev->txbuf_adr; if (adr & 0x1) { /* User defined "remote" address. Translate it into * a CPU accessible address */ pDev->_tx_hw = (void *)(adr & ~0x1); drvmgr_translate_check( pDev->dev, DMAMEM_TO_CPU, (void *)pDev->_tx_hw, (void **)&pDev->_tx, pDev->txbuf_size); pDev->tx = (struct grcan_msg *)pDev->_tx; } else { if (adr == 0) { pDev->_tx = grlib_malloc(pDev->txbuf_size + BUFFER_ALIGNMENT_NEEDS); if (!pDev->_tx) return -1; } else { /* User defined "cou-local" address. Translate * it into a CPU accessible address */ pDev->_tx = (void *)adr; } /* Align TX buffer */ pDev->tx = (struct grcan_msg *) (((unsigned int)pDev->_tx + (BUFFER_ALIGNMENT_NEEDS-1)) & ~(BUFFER_ALIGNMENT_NEEDS-1)); /* Translate address into an hardware accessible * address */ drvmgr_translate_check( pDev->dev, CPUMEM_TO_DMA, (void *)pDev->tx, (void **)&pDev->_tx_hw, pDev->txbuf_size); } } if ( rx ) { adr = (unsigned int)pDev->rxbuf_adr; if (adr & 0x1) { /* User defined "remote" address. Translate it into * a CPU accessible address */ pDev->_rx_hw = (void *)(adr & ~0x1); drvmgr_translate_check( pDev->dev, DMAMEM_TO_CPU, (void *)pDev->_rx_hw, (void **)&pDev->_rx, pDev->rxbuf_size); pDev->rx = (struct grcan_msg *)pDev->_rx; } else { if (adr == 0) { pDev->_rx = grlib_malloc(pDev->rxbuf_size + BUFFER_ALIGNMENT_NEEDS); if (!pDev->_rx) return -1; } else { /* User defined "cou-local" address. Translate * it into a CPU accessible address */ pDev->_rx = (void *)adr; } /* Align RX buffer */ pDev->rx = (struct grcan_msg *) (((unsigned int)pDev->_rx + (BUFFER_ALIGNMENT_NEEDS-1)) & ~(BUFFER_ALIGNMENT_NEEDS-1)); /* Translate address into an hardware accessible * address */ drvmgr_translate_check( pDev->dev, CPUMEM_TO_DMA, (void *)pDev->rx, (void **)&pDev->_rx_hw, pDev->rxbuf_size); } } return 0; } static void grcan_free_buffers(struct grcan_priv *pDev, int rx, int tx) { FUNCDBG(); if (tx && pDev->_tx) { free(pDev->_tx); pDev->_tx = NULL; pDev->tx = NULL; } if (rx && pDev->_rx) { free(pDev->_rx); pDev->_rx = NULL; pDev->rx = NULL; } } int grcan_dev_count(void) { return grcan_count; } void *grcan_open_by_name(char *name, int *dev_no) { int i; for (i = 0; i < grcan_count; i++){ struct grcan_priv *pDev; pDev = priv_tab[i]; if (NULL == pDev) { continue; } if (strncmp(pDev->devName, name, NELEM(pDev->devName)) == 0) { if (dev_no) *dev_no = i; return grcan_open(i); } } return NULL; } void *grcan_open(int dev_no) { struct grcan_priv *pDev; void *ret; union drvmgr_key_value *value; struct grlib_canbtrs_ranges *br; FUNCDBG(); if (grcan_count == 0 || (grcan_count <= dev_no)) { return NULL; } pDev = priv_tab[dev_no]; /* Wait until we get semaphore */ if (rtems_semaphore_obtain(pDev->dev_sem, RTEMS_WAIT, RTEMS_NO_TIMEOUT) != RTEMS_SUCCESSFUL) { return NULL; } /* is device busy/taken? */ if ( pDev->open ) { ret = NULL; goto out; } SPIN_INIT(&pDev->devlock, pDev->devName); /* Mark device taken */ pDev->open = 1; pDev->txblock = pDev->rxblock = 1; pDev->txcomplete = pDev->rxcomplete = 0; pDev->started = STATE_STOPPED; pDev->config_changed = 1; pDev->config.silent = 0; pDev->config.abort = 0; pDev->config.selection.selection = 0; pDev->config.selection.enable0 = 0; pDev->config.selection.enable1 = 1; pDev->flushing = 0; pDev->rx = pDev->_rx = NULL; pDev->tx = pDev->_tx = NULL; pDev->txbuf_adr = 0; pDev->rxbuf_adr = 0; pDev->txbuf_size = TX_BUF_SIZE; pDev->rxbuf_size = RX_BUF_SIZE; /* Override default buffer sizes if available from bus resource */ value = drvmgr_dev_key_get(pDev->dev, "txBufSize", DRVMGR_KT_INT); if ( value ) pDev->txbuf_size = value->i; value = drvmgr_dev_key_get(pDev->dev, "rxBufSize", DRVMGR_KT_INT); if ( value ) pDev->rxbuf_size = value->i; value = drvmgr_dev_key_get(pDev->dev, "txBufAdr", DRVMGR_KT_POINTER); if ( value ) pDev->txbuf_adr = value->ptr; value = drvmgr_dev_key_get(pDev->dev, "rxBufAdr", DRVMGR_KT_POINTER); if ( value ) pDev->rxbuf_adr = value->ptr; DBG("Defaulting to rxbufsize: %d, txbufsize: %d\n",RX_BUF_SIZE,TX_BUF_SIZE); /* Default to accept all messages */ pDev->afilter.mask = 0x00000000; pDev->afilter.code = 0x00000000; /* Default to disable sync messages (only trigger when id is set to all ones) */ pDev->sfilter.mask = 0xffffffff; pDev->sfilter.code = 0x00000000; /* Calculate default timing register values */ if (pDev->fd_capable) br = &grcanfd_nom_btrs_ranges; else br = &grcan_btrs_ranges; grlib_canbtrs_calc_timing( GRCAN_DEFAULT_BAUD, pDev->corefreq_hz, GRCAN_SAMPLING_POINT, br, (struct grlib_canbtrs_timing *)&pDev->config.timing); if ( grcan_alloc_buffers(pDev,1,1) ) { ret = NULL; goto out; } /* Clear statistics */ memset(&pDev->stats,0,sizeof(struct grcan_stats)); ret = pDev; out: rtems_semaphore_release(pDev->dev_sem); return ret; } int grcan_close(void *d) { struct grcan_priv *pDev = d; FUNCDBG(); grcan_stop(d); grcan_hw_reset(pDev->regs); grcan_free_buffers(pDev,1,1); /* Mark Device as closed */ pDev->open = 0; return 0; } int grcan_canfd_capable(void *d) { struct grcan_priv *pDev = d; return pDev->fd_capable; } int grcan_start(void *d) { struct grcan_priv *pDev = d; FUNCDBG(); if (grcan_get_state(d) == STATE_STARTED) { return -1; } if ( (grcan_hw_start(pDev)) != RTEMS_SUCCESSFUL ){ return -2; } /* Clear semaphore state. This is to avoid effects from previous * bus-off/stop where semahpores where flushed() but the count remained. */ rtems_semaphore_obtain(pDev->rx_sem, RTEMS_NO_WAIT, 0); rtems_semaphore_obtain(pDev->tx_sem, RTEMS_NO_WAIT, 0); rtems_semaphore_obtain(pDev->txempty_sem, RTEMS_NO_WAIT, 0); /* Read and write are now open... */ pDev->started = STATE_STARTED; DBGC(DBG_STATE, "STOPPED|BUSOFF|AHBERR->STARTED\n"); /* Register interrupt routine and enable IRQ at IRQ ctrl */ drvmgr_interrupt_register(pDev->dev, 0, pDev->devName, grcan_interrupt, pDev); return 0; } int grcan_stop(void *d) { struct grcan_priv *pDev = d; SPIN_IRQFLAGS(oldLevel); int do_sw_stop; FUNCDBG(); if (pDev->started == STATE_STOPPED) return -1; SPIN_LOCK_IRQ(&pDev->devlock, oldLevel); if (pDev->started == STATE_STARTED) { grcan_hw_stop(pDev); do_sw_stop = 1; DBGC(DBG_STATE, "STARTED->STOPPED\n"); } else { /* * started == STATE_[STOPPED|BUSOFF|AHBERR] so grcan_hw_stop() * might already been called from ISR. */ DBGC(DBG_STATE, "[STOPPED|BUSOFF|AHBERR]->STOPPED\n"); do_sw_stop = 0; } pDev->started = STATE_STOPPED; SPIN_UNLOCK_IRQ(&pDev->devlock, oldLevel); if (do_sw_stop) grcan_sw_stop(pDev); /* Disable interrupts */ drvmgr_interrupt_unregister(pDev->dev, 0, grcan_interrupt, pDev); return 0; } int grcan_get_state(void *d) { struct grcan_priv *pDev = d; FUNCDBG(); return pDev->started; } int grcan_flush(void *d) { struct grcan_priv *pDev = d; int tmp; FUNCDBG(); if ((pDev->started != STATE_STARTED) || pDev->flushing || pDev->config.silent) return -1; pDev->flushing = 1; tmp = grcan_tx_flush(pDev); pDev->flushing = 0; if ( tmp ) { /* The wait has been aborted, probably due to * the device driver has been closed by another * thread. */ return -1; } return 0; } int grcan_set_silent(void* d, int silent) { struct grcan_priv *pDev = d; FUNCDBG(); if (pDev->started == STATE_STARTED) return -1; pDev->config.silent = silent; pDev->config_changed = 1; return 0; } int grcan_set_abort(void* d, int abort) { struct grcan_priv *pDev = d; FUNCDBG(); if (pDev->started == STATE_STARTED) return -1; pDev->config.abort = abort; /* This Configuration parameter doesn't need HurriCANe reset * ==> no pDev->config_changed = 1; */ return 0; } int grcan_set_selection(void *d, const struct grcan_selection *selection) { struct grcan_priv *pDev = d; FUNCDBG(); if (pDev->started == STATE_STARTED) return -1; if ( !selection ) return -2; pDev->config.selection = *selection; pDev->config_changed = 1; return 0; } int grcan_set_rxblock(void *d, int block) { struct grcan_priv *pDev = d; FUNCDBG(); pDev->rxblock = block; return 0; } int grcan_set_txblock(void *d, int block) { struct grcan_priv *pDev = d; FUNCDBG(); pDev->txblock = block; return 0; } int grcan_set_txcomplete(void *d, int complete) { struct grcan_priv *pDev = d; FUNCDBG(); pDev->txcomplete = complete; return 0; } int grcan_set_rxcomplete(void *d, int complete) { struct grcan_priv *pDev = d; FUNCDBG(); pDev->rxcomplete = complete; return 0; } int grcan_get_stats(void *d, struct grcan_stats *stats) { struct grcan_priv *pDev = d; SPIN_IRQFLAGS(oldLevel); FUNCDBG(); if ( !stats ) return -1; SPIN_LOCK_IRQ(&pDev->devlock, oldLevel); *stats = pDev->stats; SPIN_UNLOCK_IRQ(&pDev->devlock, oldLevel); return 0; } int grcan_clr_stats(void *d) { struct grcan_priv *pDev = d; SPIN_IRQFLAGS(oldLevel); FUNCDBG(); SPIN_LOCK_IRQ(&pDev->devlock, oldLevel); memset(&pDev->stats,0,sizeof(struct grcan_stats)); SPIN_UNLOCK_IRQ(&pDev->devlock, oldLevel); return 0; } int grcan_set_afilter(void *d, const struct grcan_filter *filter) { struct grcan_priv *pDev = d; FUNCDBG(); if ( !filter ){ /* Disable filtering - let all messages pass */ pDev->afilter.mask = 0x0; pDev->afilter.code = 0x0; }else{ /* Save filter */ pDev->afilter = *filter; } /* Set hardware acceptance filter */ grcan_hw_accept(pDev->regs,&pDev->afilter); return 0; } int grcan_set_sfilter(void *d, const struct grcan_filter *filter) { struct grcan_priv *pDev = d; SPIN_IRQFLAGS(oldLevel); FUNCDBG(); if ( !filter ){ /* disable TX/RX SYNC filtering */ pDev->sfilter.mask = 0xffffffff; pDev->sfilter.mask = 0; /* disable Sync interrupt */ SPIN_LOCK_IRQ(&pDev->devlock, oldLevel); pDev->regs->imr = READ_REG(&pDev->regs->imr) & ~(GRCAN_RXSYNC_IRQ|GRCAN_TXSYNC_IRQ); SPIN_UNLOCK_IRQ(&pDev->devlock, oldLevel); }else{ /* Save filter */ pDev->sfilter = *filter; /* Enable Sync interrupt */ SPIN_LOCK_IRQ(&pDev->devlock, oldLevel); pDev->regs->imr = READ_REG(&pDev->regs->imr) | (GRCAN_RXSYNC_IRQ|GRCAN_TXSYNC_IRQ); SPIN_UNLOCK_IRQ(&pDev->devlock, oldLevel); } /* Set Sync RX/TX filter */ grcan_hw_sync(pDev->regs,&pDev->sfilter); return 0; } int grcan_get_status(void* d, unsigned int *data) { struct grcan_priv *pDev = d; FUNCDBG(); if ( !data ) return -1; /* Read out the statsu register from the GRCAN core */ data[0] = READ_REG(&pDev->regs->stat); return 0; } /* Error indicators */ #define GRCAN_IRQ_ERRORS \ (GRCAN_RXAHBERR_IRQ | GRCAN_TXAHBERR_IRQ | GRCAN_OFF_IRQ) #define GRCAN_STAT_ERRORS (GRCAN_STAT_AHBERR | GRCAN_STAT_OFF) /* Warning & RX/TX sync indicators */ #define GRCAN_IRQ_WARNS \ (GRCAN_ERR_IRQ | GRCAN_OR_IRQ | GRCAN_TXLOSS_IRQ | \ GRCAN_RXSYNC_IRQ | GRCAN_TXSYNC_IRQ) #define GRCAN_STAT_WARNS (GRCAN_STAT_OR | GRCAN_STAT_PASS) /* Handle the IRQ */ static void grcan_interrupt(void *arg) { struct grcan_priv *pDev = arg; unsigned int status = READ_REG(&pDev->regs->pimsr); unsigned int canstat = READ_REG(&pDev->regs->stat); unsigned int imr_clear; SPIN_ISR_IRQFLAGS(irqflags); /* Spurious IRQ call? */ if ( !status && !canstat ) return; if (pDev->started != STATE_STARTED) { DBGC(DBG_STATE, "not STARTED (unexpected interrupt)\n"); pDev->regs->picr = status; return; } FUNCDBG(); if ( (status & GRCAN_IRQ_ERRORS) || (canstat & GRCAN_STAT_ERRORS) ) { /* Bus-off condition interrupt * The link is brought down by hardware, we wake all threads * that is blocked in read/write calls and stop futher calls * to read/write until user has called ioctl(fd,START,0). */ SPIN_LOCK(&pDev->devlock, irqflags); DBGC(DBG_STATE, "STARTED->BUSOFF|AHBERR\n"); pDev->stats.ints++; if ((status & GRCAN_OFF_IRQ) || (canstat & GRCAN_STAT_OFF)) { /* CAN Bus-off interrupt */ DBGC(DBG_STATE, "BUSOFF: status: 0x%x, canstat: 0x%x\n", status, canstat); pDev->started = STATE_BUSOFF; pDev->stats.busoff_cnt++; } else { /* RX or Tx AHB Error interrupt */ printk("AHBERROR: status: 0x%x, canstat: 0x%x\n", status, canstat); pDev->started = STATE_AHBERR; pDev->stats.ahberr_cnt++; } grcan_hw_stop(pDev); /* this mask all IRQ sources */ pDev->regs->picr = 0x1ffff; /* clear all interrupts */ /* * Prevent driver from affecting bus. Driver can be started * again with grcan_start(). */ SPIN_UNLOCK(&pDev->devlock, irqflags); /* Release semaphores to wake blocked threads. */ grcan_sw_stop(pDev); /* * NOTE: Another interrupt may be pending now so ISR could be * executed one more time aftert this (first) return. */ return; } /* Mask interrupts in one place under spin-lock. */ imr_clear = status & (GRCAN_RXIRQ_IRQ | GRCAN_TXIRQ_IRQ | GRCAN_TXEMPTY_IRQ); SPIN_LOCK(&pDev->devlock, irqflags); /* Increment number of interrupts counter */ pDev->stats.ints++; if ((status & GRCAN_IRQ_WARNS) || (canstat & GRCAN_STAT_WARNS)) { if ( (status & GRCAN_ERR_IRQ) || (canstat & GRCAN_STAT_PASS) ) { /* Error-Passive interrupt */ pDev->stats.passive_cnt++; } if ( (status & GRCAN_OR_IRQ) || (canstat & GRCAN_STAT_OR) ) { /* Over-run during reception interrupt */ pDev->stats.overrun_cnt++; } if ( status & GRCAN_TXLOSS_IRQ ) { pDev->stats.txloss_cnt++; } if ( status & GRCAN_TXSYNC_IRQ ) { /* TxSync message transmitted interrupt */ pDev->stats.txsync_cnt++; } if ( status & GRCAN_RXSYNC_IRQ ) { /* RxSync message received interrupt */ pDev->stats.rxsync_cnt++; } } if (imr_clear) { pDev->regs->imr = READ_REG(&pDev->regs->imr) & ~imr_clear; SPIN_UNLOCK(&pDev->devlock, irqflags); if ( status & GRCAN_RXIRQ_IRQ ) { /* RX IRQ pointer interrupt */ rtems_semaphore_release(pDev->rx_sem); } if ( status & GRCAN_TXIRQ_IRQ ) { /* TX IRQ pointer interrupt */ rtems_semaphore_release(pDev->tx_sem); } if (status & GRCAN_TXEMPTY_IRQ ) { rtems_semaphore_release(pDev->txempty_sem); } } else { SPIN_UNLOCK(&pDev->devlock, irqflags); } /* Clear IRQs */ pDev->regs->picr = status; }