forked from Imagelibrary/rtems
The new GRCAN_FD IP supports CAN FD standard and is mostly backwards compatible with GRCAN SW interface. The GRCAN driver have been extended to support the GRCANFD IP using the same driver. Additional functions have been added that uses a new CAN FD frame format and read/write/baud-rate functions that supports both GRCANFD and GRCAN. To keep the SW API fully backwards compatible with GRCAN, the old functions remain. Update #4307.
2414 lines
56 KiB
C
2414 lines
56 KiB
C
/*
|
|
* 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 <bsp.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <assert.h>
|
|
#include <ctype.h>
|
|
#include <rtems/bspIo.h>
|
|
|
|
#include <grlib/grcan.h>
|
|
#include <grlib/canbtrs.h>
|
|
#include <drvmgr/drvmgr.h>
|
|
#include <grlib/ambapp_bus.h>
|
|
#include <grlib/ambapp.h>
|
|
|
|
#include <grlib/grlib_impl.h>
|
|
|
|
/* Maximum number of GRCAN devices supported by driver */
|
|
#define GRCAN_COUNT_MAX 8
|
|
|
|
#define WRAP_AROUND_TX_MSGS 1
|
|
#define WRAP_AROUND_RX_MSGS 2
|
|
#define GRCAN_MSG_SIZE sizeof(struct grcan_msg)
|
|
#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
|
|
|
|
#ifndef GRCAN_DEFAULT_BAUD
|
|
/* default to 500kbits/s */
|
|
#define GRCAN_DEFAULT_BAUD 500000
|
|
#endif
|
|
|
|
#ifndef GRCAN_SAMPLING_POINT
|
|
#define GRCAN_SAMPLING_POINT 80
|
|
#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 <grlib/debug_defs.h>
|
|
|
|
/*********************************************************/
|
|
|
|
int state2err[4] = {
|
|
/* STATE_STOPPED */ GRCAN_RET_NOTSTARTED,
|
|
/* STATE_STARTED */ GRCAN_RET_OK,
|
|
/* STATE_BUSOFF */ GRCAN_RET_BUSOFF,
|
|
/* STATE_AHBERR */ GRCAN_RET_AHBERR
|
|
};
|
|
|
|
struct grcan_msg {
|
|
unsigned int head[2];
|
|
unsigned char data[8];
|
|
};
|
|
|
|
struct grcanfd_bd0 {
|
|
uint32_t head[2];
|
|
uint64_t data0; /* variable size, from 1 to 8 dwords */
|
|
};
|
|
|
|
struct grcanfd_bd1 {
|
|
unsigned long long data[2];
|
|
};
|
|
|
|
struct grcan_config {
|
|
struct grcan_timing timing;
|
|
struct grcanfd_timing timing_fd;
|
|
struct grcan_selection selection;
|
|
int abort;
|
|
int silent;
|
|
};
|
|
|
|
struct grcan_priv {
|
|
struct drvmgr_dev *dev; /* Driver manager device */
|
|
char devName[32]; /* Device Name */
|
|
unsigned int baseaddr, ram_base;
|
|
struct grcan_regs *regs;
|
|
int irq;
|
|
int minor;
|
|
int open;
|
|
int started;
|
|
unsigned int channel;
|
|
int flushing;
|
|
unsigned int corefreq_hz;
|
|
int fd_capable;
|
|
|
|
/* Circular DMA buffers */
|
|
void *_rx, *_rx_hw;
|
|
void *_tx, *_tx_hw;
|
|
void *txbuf_adr;
|
|
void *rxbuf_adr;
|
|
struct grcan_msg *rx;
|
|
struct grcan_msg *tx;
|
|
unsigned int rxbuf_size; /* requested RX buf size in bytes */
|
|
unsigned int txbuf_size; /* requested TX buf size in bytes */
|
|
|
|
int txblock, rxblock;
|
|
int txcomplete, rxcomplete;
|
|
|
|
struct grcan_filter sfilter;
|
|
struct grcan_filter afilter;
|
|
int config_changed; /* 0=no changes, 1=changes ==> a Core reset is needed */
|
|
struct grcan_config config;
|
|
struct grcan_stats stats;
|
|
|
|
rtems_id rx_sem, tx_sem, txempty_sem, dev_sem;
|
|
SPIN_DECLARE(devlock);
|
|
};
|
|
|
|
static void __inline__ grcan_hw_reset(struct grcan_regs *regs);
|
|
|
|
static int grcan_hw_read_try(
|
|
struct grcan_priv *pDev,
|
|
struct grcan_regs *regs,
|
|
CANMsg *buffer,
|
|
int max);
|
|
|
|
static int grcan_hw_write_try(
|
|
struct grcan_priv *pDev,
|
|
struct grcan_regs *regs,
|
|
CANMsg *buffer,
|
|
int count);
|
|
|
|
static int grcan_hw_write_try_fd(
|
|
struct grcan_priv *pDev,
|
|
struct grcan_regs *regs,
|
|
CANFDMsg *buffer,
|
|
int count);
|
|
|
|
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);
|
|
|
|
#ifdef GRCAN_REG_BYPASS_CACHE
|
|
#define READ_REG(address) grlib_read_uncached32((unsigned int)(address))
|
|
#else
|
|
#define READ_REG(address) (*(volatile unsigned int *)(address))
|
|
#endif
|
|
|
|
#ifdef GRCAN_DMA_BYPASS_CACHE
|
|
#define READ_DMA_DOUBLE(address) grlib_read_uncached64((uint64_t *)(address))
|
|
#define READ_DMA_WORD(address) grlib_read_uncached32((unsigned int)(address))
|
|
#define READ_DMA_BYTE(address) grlib_read_uncached8((unsigned int)(address))
|
|
#else
|
|
#define READ_DMA_DOUBLE(address) (*(volatile uint64_t *)(address))
|
|
#define READ_DMA_WORD(address) (*(volatile unsigned int *)(address))
|
|
#define READ_DMA_BYTE(address) (*(volatile unsigned char *)(address))
|
|
#endif
|
|
|
|
#define NELEM(a) ((int) (sizeof (a) / sizeof (a[0])))
|
|
|
|
/* GRCAN nominal boundaries for baud-rate paramters */
|
|
static 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;
|
|
}
|
|
|
|
static unsigned int grcan_hw_rxavail(
|
|
unsigned int rp,
|
|
unsigned int wp, unsigned int size
|
|
)
|
|
{
|
|
if (rp == wp) {
|
|
/* read pointer and write pointer is equal only
|
|
* when RX buffer is empty.
|
|
*/
|
|
return 0;
|
|
}
|
|
|
|
if (wp > rp) {
|
|
return (wp - rp) / GRCAN_MSG_SIZE;
|
|
} else {
|
|
return (size - (rp - wp)) / GRCAN_MSG_SIZE;
|
|
}
|
|
}
|
|
|
|
static unsigned int grcan_hw_txspace(
|
|
unsigned int rp,
|
|
unsigned int wp,
|
|
unsigned int size
|
|
)
|
|
{
|
|
unsigned int left;
|
|
|
|
if (rp == wp) {
|
|
/* read pointer and write pointer is equal only
|
|
* when TX buffer is empty.
|
|
*/
|
|
return size / GRCAN_MSG_SIZE - WRAP_AROUND_TX_MSGS;
|
|
}
|
|
|
|
/* size - 4 - abs(read-write) */
|
|
if (wp > rp) {
|
|
left = size - (wp - rp);
|
|
} else {
|
|
left = rp - wp;
|
|
}
|
|
|
|
return left / GRCAN_MSG_SIZE - WRAP_AROUND_TX_MSGS;
|
|
}
|
|
|
|
static int grcan_hw_read_try(
|
|
struct grcan_priv *pDev,
|
|
struct grcan_regs *regs,
|
|
CANMsg * buffer,
|
|
int max
|
|
)
|
|
{
|
|
int i, j;
|
|
CANMsg *dest;
|
|
struct grcan_msg *source, tmp;
|
|
unsigned int wp, rp, size, rxmax, addr;
|
|
int trunk_msg_cnt;
|
|
|
|
FUNCDBG();
|
|
|
|
wp = READ_REG(®s->rx0wr);
|
|
rp = READ_REG(®s->rx0rd);
|
|
|
|
/*
|
|
* Due to hardware wrap around simplification write pointer will
|
|
* never reach the read pointer, at least a gap of 8 bytes.
|
|
* The only time they are equal is when the read pointer has
|
|
* reached the write pointer (empty buffer)
|
|
*
|
|
*/
|
|
if (wp != rp) {
|
|
/* Not empty, we have received chars...
|
|
* Read as much as possible from DMA buffer
|
|
*/
|
|
size = READ_REG(®s->rx0size);
|
|
|
|
/* Get number of bytes available in RX buffer */
|
|
trunk_msg_cnt = grcan_hw_rxavail(rp, wp, size);
|
|
|
|
/* truncate size if user space buffer hasn't room for
|
|
* all received chars.
|
|
*/
|
|
if (trunk_msg_cnt > max)
|
|
trunk_msg_cnt = max;
|
|
|
|
/* Read until i is 0 */
|
|
i = trunk_msg_cnt;
|
|
|
|
addr = (unsigned int)pDev->rx;
|
|
source = (struct grcan_msg *)(addr + rp);
|
|
dest = buffer;
|
|
rxmax = addr + (size - GRCAN_MSG_SIZE);
|
|
|
|
/* Read as many can messages as possible */
|
|
while (i > 0) {
|
|
/* Read CAN message from DMA buffer */
|
|
tmp.head[0] = READ_DMA_WORD(&source->head[0]);
|
|
tmp.head[1] = READ_DMA_WORD(&source->head[1]);
|
|
if (tmp.head[1] & 0x4) {
|
|
DBGC(DBG_RX, "overrun\n");
|
|
}
|
|
if (tmp.head[1] & 0x2) {
|
|
DBGC(DBG_RX, "bus-off mode\n");
|
|
}
|
|
if (tmp.head[1] & 0x1) {
|
|
DBGC(DBG_RX, "error-passive mode\n");
|
|
}
|
|
/* Convert one grcan CAN message to one "software" CAN message */
|
|
dest->extended = tmp.head[0] >> 31;
|
|
dest->rtr = (tmp.head[0] >> 30) & 0x1;
|
|
if (dest->extended) {
|
|
dest->id = tmp.head[0] & 0x3fffffff;
|
|
} else {
|
|
dest->id = (tmp.head[0] >> 18) & 0xfff;
|
|
}
|
|
dest->len = tmp.head[1] >> 28;
|
|
for (j = 0; j < dest->len; j++)
|
|
dest->data[j] = READ_DMA_BYTE(&source->data[j]);
|
|
|
|
/* wrap around if neccessary */
|
|
source =
|
|
((unsigned int)source >= rxmax) ?
|
|
(struct grcan_msg *)addr : source + 1;
|
|
dest++; /* straight user buffer */
|
|
i--;
|
|
}
|
|
{
|
|
/* A bus off interrupt may have occured after checking pDev->started */
|
|
SPIN_IRQFLAGS(oldLevel);
|
|
|
|
SPIN_LOCK_IRQ(&pDev->devlock, oldLevel);
|
|
if (pDev->started == STATE_STARTED) {
|
|
regs->rx0rd = (unsigned int) source - addr;
|
|
regs->rx0ctrl = GRCAN_RXCTRL_ENABLE;
|
|
} else {
|
|
DBGC(DBG_STATE, "cancelled due to a BUS OFF error\n");
|
|
trunk_msg_cnt = state2err[pDev->started];
|
|
}
|
|
SPIN_UNLOCK_IRQ(&pDev->devlock, oldLevel);
|
|
}
|
|
return trunk_msg_cnt;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int grcan_hw_write_try(
|
|
struct grcan_priv *pDev,
|
|
struct grcan_regs *regs,
|
|
CANMsg * buffer,
|
|
int count
|
|
)
|
|
{
|
|
unsigned int rp, wp, size, txmax, addr;
|
|
int ret;
|
|
struct grcan_msg *dest;
|
|
CANMsg *source;
|
|
int space_left;
|
|
unsigned int tmp;
|
|
int i;
|
|
|
|
DBGC(DBG_TX, "\n");
|
|
/*FUNCDBG(); */
|
|
|
|
rp = READ_REG(®s->tx0rd);
|
|
wp = READ_REG(®s->tx0wr);
|
|
size = READ_REG(®s->tx0size);
|
|
|
|
space_left = grcan_hw_txspace(rp, wp, size);
|
|
|
|
/* is circular fifo full? */
|
|
if (space_left < 1)
|
|
return 0;
|
|
|
|
/* Truncate size */
|
|
if (space_left > count)
|
|
space_left = count;
|
|
ret = space_left;
|
|
|
|
addr = (unsigned int)pDev->tx;
|
|
|
|
dest = (struct grcan_msg *)(addr + wp);
|
|
source = (CANMsg *) buffer;
|
|
txmax = addr + (size - GRCAN_MSG_SIZE);
|
|
|
|
while (space_left > 0) {
|
|
/* Convert and write CAN message to DMA buffer */
|
|
if (source->extended) {
|
|
tmp = (1 << 31) | (source->id & 0x3fffffff);
|
|
} else {
|
|
tmp = (source->id & 0xfff) << 18;
|
|
}
|
|
if (source->rtr)
|
|
tmp |= (1 << 30);
|
|
dest->head[0] = tmp;
|
|
dest->head[1] = source->len << 28;
|
|
for (i = 0; i < source->len; i++)
|
|
dest->data[i] = source->data[i];
|
|
source++; /* straight user buffer */
|
|
dest =
|
|
((unsigned int)dest >= txmax) ?
|
|
(struct grcan_msg *)addr : dest + 1;
|
|
space_left--;
|
|
}
|
|
|
|
{
|
|
/* A bus off interrupt may have occured after checking pDev->started */
|
|
SPIN_IRQFLAGS(oldLevel);
|
|
|
|
SPIN_LOCK_IRQ(&pDev->devlock, oldLevel);
|
|
if (pDev->started == STATE_STARTED) {
|
|
regs->tx0wr = (unsigned int) dest - addr;
|
|
regs->tx0ctrl = GRCAN_TXCTRL_ENABLE;
|
|
} else {
|
|
DBGC(DBG_STATE, "cancelled due to a BUS OFF error\n");
|
|
ret = state2err[pDev->started];
|
|
}
|
|
SPIN_UNLOCK_IRQ(&pDev->devlock, oldLevel);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static uint8_t dlc2len[16] = {
|
|
0, 1, 2, 3,
|
|
4, 5, 6, 7,
|
|
8, 12, 16, 20,
|
|
24, 32, 48, 64
|
|
};
|
|
|
|
static uint8_t len2fddlc[14] = {
|
|
/* 12,13 */ 0x9,
|
|
/* 16,17 */ 0xA,
|
|
/* 20,21 */ 0xB,
|
|
/* 24,25 */ 0xC,
|
|
/* 28,29 */ -1,
|
|
/* 32,33 */ 0xD,
|
|
/* 36,37 */ -1,
|
|
/* 40,41 */ -1,
|
|
/* 44,45 */ -1,
|
|
/* 48,49 */ 0xE,
|
|
/* 52,53 */ -1,
|
|
/* 56,57 */ -1,
|
|
/* 60,61 */ -1,
|
|
/* 64,65 */ 0xF,
|
|
};
|
|
|
|
/* Convert length in bytes to descriptor length field */
|
|
static inline uint8_t grcan_len2dlc(int len)
|
|
{
|
|
if (len <= 8)
|
|
return len;
|
|
if (len > 64)
|
|
return -1;
|
|
if (len & 0x3)
|
|
return -1;
|
|
return len2fddlc[(len - 12) >> 2];
|
|
}
|
|
|
|
static inline int grcan_numbds(int len)
|
|
{
|
|
return 1 + ((len + 7) >> 4);
|
|
}
|
|
|
|
static int grcan_hw_read_try_fd(
|
|
struct grcan_priv *pDev,
|
|
struct grcan_regs *regs,
|
|
CANFDMsg * buffer,
|
|
int max)
|
|
{
|
|
int j;
|
|
CANFDMsg *dest;
|
|
struct grcanfd_bd0 *source, tmp, *rxmax;
|
|
unsigned int wp, rp, size, addr;
|
|
int bds_hw_avail, bds_tot, bds, ret, dlc;
|
|
uint64_t *dp;
|
|
SPIN_IRQFLAGS(oldLevel);
|
|
|
|
FUNCDBG();
|
|
|
|
wp = READ_REG(®s->rx0wr);
|
|
rp = READ_REG(®s->rx0rd);
|
|
|
|
/*
|
|
* Due to hardware wrap around simplification write pointer will
|
|
* never reach the read pointer, at least a gap of 8 bytes.
|
|
* The only time they are equal is when the read pointer has
|
|
* reached the write pointer (empty buffer)
|
|
*
|
|
*/
|
|
if (wp != rp) {
|
|
/* Not empty, we have received chars...
|
|
* Read as much as possible from DMA buffer
|
|
*/
|
|
size = READ_REG(®s->rx0size);
|
|
|
|
/* Get number of bytes available in RX buffer */
|
|
bds_hw_avail = grcan_hw_rxavail(rp, wp, size);
|
|
|
|
addr = (unsigned int)pDev->rx;
|
|
source = (struct grcanfd_bd0 *)(addr + rp);
|
|
dest = buffer;
|
|
rxmax = (struct grcanfd_bd0 *)(addr + size);
|
|
ret = bds_tot = 0;
|
|
|
|
/* Read as many can messages as possible */
|
|
while ((ret < max) && (bds_tot < bds_hw_avail)) {
|
|
/* Read CAN message from DMA buffer */
|
|
*(uint64_t *)&tmp = READ_DMA_DOUBLE(source);
|
|
if (tmp.head[1] & 0x4) {
|
|
DBGC(DBG_RX, "overrun\n");
|
|
}
|
|
if (tmp.head[1] & 0x2) {
|
|
DBGC(DBG_RX, "bus-off mode\n");
|
|
}
|
|
if (tmp.head[1] & 0x1) {
|
|
DBGC(DBG_RX, "error-passive mode\n");
|
|
}
|
|
/* Convert one grcan CAN message to one "software" CAN message */
|
|
dest->extended = tmp.head[0] >> 31;
|
|
dest->rtr = (tmp.head[0] >> 30) & 0x1;
|
|
if (dest->extended) {
|
|
dest->id = tmp.head[0] & 0x3fffffff;
|
|
} else {
|
|
dest->id = (tmp.head[0] >> 18) & 0xfff;
|
|
}
|
|
dest->fdopts = (tmp.head[1] >> 25) & GRCAN_FDMASK;
|
|
dlc = tmp.head[1] >> 28;
|
|
if (dest->fdopts & GRCAN_FDOPT_FDFRM) {
|
|
dest->len = dlc2len[dlc];
|
|
} else {
|
|
dest->len = dlc;
|
|
if (dlc > 8)
|
|
dest->len = 8;
|
|
}
|
|
|
|
dp = (uint64_t *)&source->data0;
|
|
for (j = 0; j < ((dest->len + 7) / 8); j++) {
|
|
dest->data.dwords[j] = READ_DMA_DOUBLE(dp);
|
|
if (++dp >= (uint64_t *)rxmax)
|
|
dp = (uint64_t *)addr; /* wrap around */
|
|
}
|
|
|
|
/* wrap around if neccessary */
|
|
bds = grcan_numbds(dest->len);
|
|
source += bds;
|
|
if (source >= rxmax) {
|
|
source = (struct grcanfd_bd0 *)
|
|
((void *)source - size);
|
|
}
|
|
dest++; /* straight user buffer */
|
|
ret++;
|
|
bds_tot += bds;
|
|
}
|
|
|
|
/* A bus off interrupt may have occured after checking pDev->started */
|
|
SPIN_LOCK_IRQ(&pDev->devlock, oldLevel);
|
|
if (pDev->started == STATE_STARTED) {
|
|
regs->rx0rd = (unsigned int) source - addr;
|
|
regs->rx0ctrl = GRCAN_RXCTRL_ENABLE;
|
|
} else {
|
|
DBGC(DBG_STATE, "cancelled due to a BUS OFF error\n");
|
|
ret = state2err[pDev->started];
|
|
}
|
|
SPIN_UNLOCK_IRQ(&pDev->devlock, oldLevel);
|
|
|
|
return ret;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int grcan_hw_write_try_fd(
|
|
struct grcan_priv *pDev,
|
|
struct grcan_regs *regs,
|
|
CANFDMsg *buffer,
|
|
int count)
|
|
{
|
|
unsigned int rp, wp, size, addr;
|
|
int ret;
|
|
struct grcanfd_bd0 *dest, *txmax;
|
|
CANFDMsg *source = (CANFDMsg *) buffer;
|
|
int space_left;
|
|
unsigned int tmp;
|
|
int i, bds;
|
|
uint64_t *dp;
|
|
uint8_t dlc;
|
|
SPIN_IRQFLAGS(oldLevel);
|
|
|
|
DBGC(DBG_TX, "\n");
|
|
|
|
rp = READ_REG(®s->tx0rd);
|
|
wp = READ_REG(®s->tx0wr);
|
|
size = READ_REG(®s->tx0size);
|
|
space_left = grcan_hw_txspace(rp, wp, size);
|
|
|
|
addr = (unsigned int)pDev->tx;
|
|
dest = (struct grcanfd_bd0 *)(addr + wp);
|
|
txmax = (struct grcanfd_bd0 *)(addr + size);
|
|
ret = 0;
|
|
|
|
while (source < &buffer[count]) {
|
|
/* Get the number of descriptors to wait for */
|
|
if (source->fdopts & GRCAN_FDOPT_FDFRM)
|
|
bds = grcan_numbds(source->len); /* next msg's buffers */
|
|
else
|
|
bds = 1;
|
|
if (space_left < bds)
|
|
break;
|
|
|
|
/* Convert and write CAN message to DMA buffer */
|
|
dlc = grcan_len2dlc(source->len);
|
|
if (dlc < 0) {
|
|
/* Bad user input. Report the number of written messages
|
|
* or an error when non sent.
|
|
*/
|
|
if (ret <= 0)
|
|
return GRCAN_RET_INVARG;
|
|
break;
|
|
}
|
|
dest->head[1] = (dlc << 28) |
|
|
((source->fdopts & GRCAN_FDMASK) << 25);
|
|
dp = &dest->data0;
|
|
for (i = 0; i < ((source->len + 7) / 8); i++) {
|
|
*dp++ = source->data.dwords[i];
|
|
if (dp >= (uint64_t *)txmax)
|
|
dp = (uint64_t *)addr; /* wrap around */
|
|
}
|
|
if (source->extended) {
|
|
tmp = (1 << 31) | (source->id & 0x3fffffff);
|
|
} else {
|
|
tmp = (source->id & 0xfff) << 18;
|
|
}
|
|
if (source->rtr)
|
|
tmp |= (1 << 30);
|
|
dest->head[0] = tmp;
|
|
source++; /* straight user buffer */
|
|
dest += bds;
|
|
if (dest >= txmax)
|
|
dest = (struct grcanfd_bd0 *)((void *)dest - size);
|
|
space_left -= bds;
|
|
ret++;
|
|
}
|
|
|
|
/* A bus off interrupt may have occured after checking pDev->started */
|
|
SPIN_LOCK_IRQ(&pDev->devlock, oldLevel);
|
|
if (pDev->started == STATE_STARTED) {
|
|
regs->tx0wr = (unsigned int) dest - addr;
|
|
regs->tx0ctrl = GRCAN_TXCTRL_ENABLE;
|
|
} else {
|
|
DBGC(DBG_STATE, "cancelled due to a BUS OFF error\n");
|
|
ret = state2err[pDev->started];
|
|
}
|
|
SPIN_UNLOCK_IRQ(&pDev->devlock, oldLevel);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static 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.
|
|
*/
|
|
static 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;
|
|
|
|
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 */
|
|
grlib_canbtrs_calc_timing(
|
|
GRCAN_DEFAULT_BAUD, pDev->corefreq_hz,
|
|
GRCAN_SAMPLING_POINT, &grcan_btrs_ranges,
|
|
(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_read(void *d, CANMsg *msg, size_t ucount)
|
|
{
|
|
struct grcan_priv *pDev = d;
|
|
CANMsg *dest;
|
|
unsigned int count, left;
|
|
int nread;
|
|
int req_cnt;
|
|
|
|
FUNCDBG();
|
|
|
|
dest = msg;
|
|
req_cnt = ucount;
|
|
|
|
if ( (!dest) || (req_cnt<1) )
|
|
return GRCAN_RET_INVARG;
|
|
|
|
if (pDev->started != STATE_STARTED) {
|
|
return GRCAN_RET_NOTSTARTED;
|
|
}
|
|
|
|
DBGC(DBG_RX, "grcan_read [%p]: buf: %p len: %u\n", d, msg, (unsigned int) ucount);
|
|
|
|
nread = grcan_hw_read_try(pDev,pDev->regs,dest,req_cnt);
|
|
if (nread < 0) {
|
|
return nread;
|
|
}
|
|
count = nread;
|
|
if ( !( pDev->rxblock && pDev->rxcomplete && (count!=req_cnt) ) ){
|
|
if ( count > 0 ) {
|
|
/* Successfully received messages (at least one) */
|
|
return count;
|
|
}
|
|
|
|
/* nothing read, shall we block? */
|
|
if ( !pDev->rxblock ) {
|
|
/* non-blocking mode */
|
|
return GRCAN_RET_TIMEOUT;
|
|
}
|
|
}
|
|
|
|
while (count == 0 || (pDev->rxcomplete && (count!=req_cnt))) {
|
|
if (!pDev->rxcomplete) {
|
|
left = 1; /* return as soon as there is one message available */
|
|
} else {
|
|
left = req_cnt - count; /* return as soon as all data are available */
|
|
|
|
/* never wait for more than the half the maximum size of the receive buffer
|
|
* Why? We need some time to copy buffer before to catch up with hw,
|
|
* otherwise we would have to copy everything when the data has been
|
|
* received.
|
|
*/
|
|
if (left > ((pDev->rxbuf_size/GRCAN_MSG_SIZE) / 2)){
|
|
left = (pDev->rxbuf_size/GRCAN_MSG_SIZE) / 2;
|
|
}
|
|
}
|
|
|
|
nread = grcan_wait_rxdata(pDev, left);
|
|
if (nread) {
|
|
/* The wait has been aborted, probably due to
|
|
* the device driver has been closed by another
|
|
* thread or a bus-off. Return error code.
|
|
*/
|
|
return nread;
|
|
}
|
|
|
|
/* Try read bytes from circular buffer */
|
|
nread = grcan_hw_read_try(
|
|
pDev,
|
|
pDev->regs,
|
|
dest+count,
|
|
req_cnt-count);
|
|
|
|
if (nread < 0) {
|
|
/* The read was aborted by bus-off. */
|
|
return nread;
|
|
}
|
|
count += nread;
|
|
}
|
|
/* no need to unmask IRQ as IRQ Handler do that for us. */
|
|
return count;
|
|
}
|
|
|
|
int grcanfd_read(void *d, CANFDMsg *msg, size_t ucount)
|
|
{
|
|
struct grcan_priv *pDev = d;
|
|
CANFDMsg *dest;
|
|
unsigned int count, left;
|
|
int nread;
|
|
int req_cnt;
|
|
|
|
FUNCDBG();
|
|
|
|
dest = msg;
|
|
req_cnt = ucount;
|
|
|
|
if ( (!dest) || (req_cnt<1) )
|
|
return GRCAN_RET_INVARG;
|
|
|
|
if (pDev->started != STATE_STARTED) {
|
|
return GRCAN_RET_NOTSTARTED;
|
|
}
|
|
|
|
DBGC(DBG_RX, "grcan_read [%p]: buf: %p len: %u\n", d, msg, (unsigned int) ucount);
|
|
|
|
nread = grcan_hw_read_try_fd(pDev,pDev->regs,dest,req_cnt);
|
|
if (nread < 0) {
|
|
return nread;
|
|
}
|
|
count = nread;
|
|
if ( !( pDev->rxblock && pDev->rxcomplete && (count!=req_cnt) ) ){
|
|
if ( count > 0 ) {
|
|
/* Successfully received messages (at least one) */
|
|
return count;
|
|
}
|
|
|
|
/* nothing read, shall we block? */
|
|
if ( !pDev->rxblock ) {
|
|
/* non-blocking mode */
|
|
return GRCAN_RET_TIMEOUT;
|
|
}
|
|
}
|
|
|
|
while (count == 0 || (pDev->rxcomplete && (count!=req_cnt))) {
|
|
if (!pDev->rxcomplete) {
|
|
left = 1; /* return as soon as there is one message available */
|
|
} else {
|
|
left = req_cnt - count; /* return as soon as all data are available */
|
|
|
|
/* never wait for more than the half the maximum size of the receive buffer
|
|
* Why? We need some time to copy buffer before to catch up with hw,
|
|
* otherwise we would have to copy everything when the data has been
|
|
* received.
|
|
*/
|
|
if (left > ((pDev->rxbuf_size/GRCAN_MSG_SIZE) / 2)){
|
|
left = (pDev->rxbuf_size/GRCAN_MSG_SIZE) / 2;
|
|
}
|
|
}
|
|
|
|
nread = grcan_wait_rxdata(pDev, left);
|
|
if (nread) {
|
|
/* The wait has been aborted, probably due to
|
|
* the device driver has been closed by another
|
|
* thread or a bus-off. Return error code.
|
|
*/
|
|
return nread;
|
|
}
|
|
|
|
/* Try read bytes from circular buffer */
|
|
nread = grcan_hw_read_try_fd(
|
|
pDev,
|
|
pDev->regs,
|
|
dest+count,
|
|
req_cnt-count);
|
|
|
|
if (nread < 0) {
|
|
/* The read was aborted by bus-off. */
|
|
return nread;
|
|
}
|
|
count += nread;
|
|
}
|
|
/* no need to unmask IRQ as IRQ Handler do that for us. */
|
|
return count;
|
|
}
|
|
|
|
int grcan_write(void *d, CANMsg *msg, size_t ucount)
|
|
{
|
|
struct grcan_priv *pDev = d;
|
|
CANMsg *source;
|
|
unsigned int count, left;
|
|
int nwritten;
|
|
int req_cnt;
|
|
|
|
DBGC(DBG_TX,"\n");
|
|
|
|
if ((pDev->started != STATE_STARTED) || pDev->config.silent || pDev->flushing)
|
|
return GRCAN_RET_NOTSTARTED;
|
|
|
|
req_cnt = ucount;
|
|
source = (CANMsg *) msg;
|
|
|
|
/* check proper length and buffer pointer */
|
|
if (( req_cnt < 1) || (source == NULL) ){
|
|
return GRCAN_RET_INVARG;
|
|
}
|
|
|
|
nwritten = grcan_hw_write_try(pDev,pDev->regs,source,req_cnt);
|
|
if (nwritten < 0) {
|
|
return nwritten;
|
|
}
|
|
count = nwritten;
|
|
if ( !(pDev->txblock && pDev->txcomplete && (count!=req_cnt)) ) {
|
|
if ( count > 0 ) {
|
|
/* Successfully transmitted chars (at least one char) */
|
|
return count;
|
|
}
|
|
|
|
/* nothing written, shall we block? */
|
|
if ( !pDev->txblock ) {
|
|
/* non-blocking mode */
|
|
return GRCAN_RET_TIMEOUT;
|
|
}
|
|
}
|
|
|
|
/* if in txcomplete mode we need to transmit all chars */
|
|
while((count == 0) || (pDev->txcomplete && (count!=req_cnt)) ){
|
|
/*** block until room to fit all or as much of transmit buffer as possible
|
|
* IRQ comes. Set up a valid IRQ point so that an IRQ is received
|
|
* when we can put a chunk of data into transmit fifo
|
|
*/
|
|
if ( !pDev->txcomplete ){
|
|
left = 1; /* wait for anything to fit buffer */
|
|
}else{
|
|
left = req_cnt - count; /* wait for all data to fit in buffer */
|
|
|
|
/* never wait for more than the half the maximum size of the transmit
|
|
* buffer
|
|
* Why? We need some time to fill buffer before hw catches up.
|
|
*/
|
|
if ( left > ((pDev->txbuf_size/GRCAN_MSG_SIZE)/2) ){
|
|
left = (pDev->txbuf_size/GRCAN_MSG_SIZE)/2;
|
|
}
|
|
}
|
|
|
|
nwritten = grcan_wait_txspace(pDev,left);
|
|
/* Wait until more room in transmit buffer */
|
|
if ( nwritten ) {
|
|
/* The wait has been aborted, probably due to
|
|
* the device driver has been closed by another
|
|
* thread. To avoid deadlock we return directly
|
|
* with error status.
|
|
*/
|
|
return nwritten;
|
|
}
|
|
|
|
/* Try read bytes from circular buffer */
|
|
nwritten = grcan_hw_write_try(
|
|
pDev,
|
|
pDev->regs,
|
|
source+count,
|
|
req_cnt-count);
|
|
|
|
if (nwritten < 0) {
|
|
/* Write was aborted by bus-off. */
|
|
return nwritten;
|
|
}
|
|
count += nwritten;
|
|
}
|
|
/* no need to unmask IRQ as IRQ Handler do that for us. */
|
|
|
|
return count;
|
|
}
|
|
|
|
int grcanfd_write(
|
|
void *d,
|
|
CANFDMsg *msg,
|
|
size_t ucount)
|
|
{
|
|
struct grcan_priv *pDev = d;
|
|
CANFDMsg *source, *curr;
|
|
unsigned int count, left;
|
|
int nwritten;
|
|
int req_cnt;
|
|
|
|
DBGC(DBG_TX,"\n");
|
|
|
|
if ((pDev->started != STATE_STARTED) || pDev->config.silent || pDev->flushing)
|
|
return GRCAN_RET_NOTSTARTED;
|
|
|
|
req_cnt = ucount;
|
|
curr = source = (CANFDMsg *) msg;
|
|
|
|
/* check proper length and buffer pointer */
|
|
if (( req_cnt < 1) || (source == NULL) ){
|
|
return GRCAN_RET_INVARG;
|
|
}
|
|
|
|
nwritten = grcan_hw_write_try_fd(pDev,pDev->regs,source,req_cnt);
|
|
if (nwritten < 0) {
|
|
return nwritten;
|
|
}
|
|
count = nwritten;
|
|
if ( !(pDev->txblock && pDev->txcomplete && (count!=req_cnt)) ) {
|
|
if ( count > 0 ) {
|
|
/* Successfully transmitted chars (at least one char) */
|
|
return count;
|
|
}
|
|
|
|
/* nothing written, shall we block? */
|
|
if ( !pDev->txblock ) {
|
|
/* non-blocking mode */
|
|
return GRCAN_RET_TIMEOUT;
|
|
}
|
|
}
|
|
|
|
/* if in txcomplete mode we need to transmit all chars */
|
|
while((count == 0) || (pDev->txcomplete && (count!=req_cnt)) ){
|
|
/*** block until room to fit all or as much of transmit buffer
|
|
* as possible before IRQ comes. Set up a valid IRQ point so
|
|
* that an IRQ is triggered when we can put a chunk of data
|
|
* into transmit fifo.
|
|
*/
|
|
|
|
/* Get the number of descriptors to wait for */
|
|
curr = &source[count];
|
|
if (curr->fdopts & GRCAN_FDOPT_FDFRM)
|
|
left = grcan_numbds(curr->len); /* next msg's buffers */
|
|
else
|
|
left = 1;
|
|
|
|
if (pDev->txcomplete) {
|
|
/* Wait for all messages to fit into descriptor table.
|
|
* Assume all following msgs are single descriptors.
|
|
*/
|
|
left += req_cnt - count - 1;
|
|
if (left > ((pDev->txbuf_size/GRCAN_MSG_SIZE)/2)) {
|
|
left = (pDev->txbuf_size/GRCAN_MSG_SIZE)/2;
|
|
}
|
|
|
|
}
|
|
|
|
nwritten = grcan_wait_txspace(pDev,left);
|
|
/* Wait until more room in transmit buffer */
|
|
if ( nwritten ) {
|
|
/* The wait has been aborted, probably due to
|
|
* the device driver has been closed by another
|
|
* thread. To avoid deadlock we return directly
|
|
* with error status.
|
|
*/
|
|
return nwritten;
|
|
}
|
|
|
|
/* Try read bytes from circular buffer */
|
|
nwritten = grcan_hw_write_try_fd(
|
|
pDev,
|
|
pDev->regs,
|
|
source+count,
|
|
req_cnt-count);
|
|
|
|
if (nwritten < 0) {
|
|
/* Write was aborted by bus-off. */
|
|
return nwritten;
|
|
}
|
|
count += nwritten;
|
|
}
|
|
/* no need to unmask IRQ as IRQ Handler do that for us. */
|
|
|
|
return count;
|
|
}
|
|
|
|
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_speed(void *d, unsigned int speed)
|
|
{
|
|
struct grcan_priv *pDev = d;
|
|
struct grcan_timing timing;
|
|
int ret;
|
|
|
|
FUNCDBG();
|
|
|
|
/* cannot change speed during run mode */
|
|
if ((pDev->started == STATE_STARTED) || pDev->fd_capable)
|
|
return -1;
|
|
|
|
/* get speed rate from argument */
|
|
ret = grlib_canbtrs_calc_timing(
|
|
speed, pDev->corefreq_hz, GRCAN_SAMPLING_POINT,
|
|
&grcan_btrs_ranges, (struct grlib_canbtrs_timing *)&timing);
|
|
if (ret)
|
|
return -2;
|
|
|
|
/* save timing/speed */
|
|
pDev->config.timing = timing;
|
|
pDev->config_changed = 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int grcan_set_btrs(void *d, const struct grcan_timing *timing)
|
|
{
|
|
struct grcan_priv *pDev = d;
|
|
|
|
FUNCDBG();
|
|
|
|
/* Set BTR registers manually
|
|
* Read GRCAN/HurriCANe Manual.
|
|
*/
|
|
if ((pDev->started == STATE_STARTED) || pDev->fd_capable)
|
|
return -1;
|
|
|
|
if ( !timing )
|
|
return -2;
|
|
|
|
pDev->config.timing = *timing;
|
|
pDev->config_changed = 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int grcanfd_set_speed(void *d, unsigned int nom_hz, unsigned int fd_hz)
|
|
{
|
|
struct grcan_priv *pDev = d;
|
|
struct grlib_canbtrs_timing nom, fd;
|
|
int ret;
|
|
|
|
FUNCDBG();
|
|
|
|
/* cannot change speed during run mode */
|
|
if ((pDev->started == STATE_STARTED) || !pDev->fd_capable)
|
|
return -1;
|
|
|
|
/* get speed rate from argument */
|
|
ret = grlib_canbtrs_calc_timing(
|
|
nom_hz, pDev->corefreq_hz, GRCAN_SAMPLING_POINT,
|
|
&grcanfd_nom_btrs_ranges, &nom);
|
|
if ( ret )
|
|
return -2;
|
|
ret = grlib_canbtrs_calc_timing(
|
|
fd_hz, pDev->corefreq_hz, GRCAN_SAMPLING_POINT,
|
|
&grcanfd_fd_btrs_ranges, &fd);
|
|
if ( ret )
|
|
return -2;
|
|
|
|
/* save timing/speed */
|
|
pDev->config.timing = *(struct grcan_timing *)&nom;
|
|
pDev->config.timing_fd.scaler = fd.scaler;
|
|
pDev->config.timing_fd.ps1 = fd.ps1;
|
|
pDev->config.timing_fd.ps2 = fd.ps2;
|
|
pDev->config.timing_fd.sjw = fd.rsj;
|
|
pDev->config.timing_fd.resv_zero = 0;
|
|
pDev->config_changed = 1;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
int grcanfd_set_btrs(
|
|
void *d,
|
|
const struct grcanfd_timing *nominal,
|
|
const struct grcanfd_timing *fd)
|
|
{
|
|
struct grcan_priv *pDev = d;
|
|
|
|
FUNCDBG();
|
|
|
|
/* Set BTR registers manually
|
|
* Read GRCAN/HurriCANe Manual.
|
|
*/
|
|
if ((pDev->started == STATE_STARTED) || !pDev->fd_capable)
|
|
return -1;
|
|
|
|
if (!nominal)
|
|
return -2;
|
|
|
|
pDev->config.timing.scaler = nominal->scaler;
|
|
pDev->config.timing.ps1 = nominal->ps1;
|
|
pDev->config.timing.ps2 = nominal->ps2;
|
|
pDev->config.timing.rsj = nominal->sjw;
|
|
pDev->config.timing.bpr = 0;
|
|
if (fd) {
|
|
pDev->config.timing_fd = *fd;
|
|
} else {
|
|
memset(&pDev->config.timing_fd, 0,
|
|
sizeof(struct grcanfd_timing));
|
|
}
|
|
pDev->config_changed = 1;
|
|
|
|
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;
|
|
}
|