forked from Imagelibrary/rtems
leon, grcan: redesigned bus-off and AHB error handling
When bus-off condition is detected by the ISR, it sets the started flag to STATE_BUSOFF. This is monitored by the user functions grcan_read() and grcan_write() each time they want to enable DMA or update interrupt mask. If they detect that ISR has detected bus-off then they will return either with an error or with the number of CAN messages processed. Future calls to functions which require STARTED mode will be rejected and grcan_isstarted() will return 0. The next call to grcan_stop() will do the transition from BUSOFF->STOPPED and the device can then be started again with grcan_start(). Similar to a bus-off condition, the AHB error condition detected by the ISR will trigger the same shut-down logic of the driver. The difference is that the state entered is STATE_AHBERR and the routines will return a different value to indicate AHB error state. This commit also fixes an issue where ISR was not always unregistered on close. User functions can cause these transitions: STATE_STOPPED -> STATE_STARTED (grcan_start) STATE_STARTED -> STATE_STOPPED (grcan_stop) STATE_BUSOFF -> STATE_STOPPED (grcan_stop, grcan_close) STATE_AHBERR -> STATE_STOPPED (grcan_stop, grcan_close) ISR can cause these transition STATE_STARTED -> STATE_BUSOFF (grcan_interrupt) STATE_STARTED -> STATE_AHBERR (grcan_interrupt) STATE_BUSOFF/AHBERR is entered from ISR on bus-off condition. At transition the ISR disables DMA, masks all interrupts and flushes semaphores. Other related updates: * Statistics are updated from the ISR. Update is now spin-locked to ensure a consistent user view. * The debug output has been updated to include state changes. * For read/write/flush, return error (-4) if driver aborted the operation due to bus-off. Likewise if abourted due to AHB error -5 is returned. * Collect bus-off statistics Related to the new BUSOFF and AHBERR states the API has been updated to reflect the current SW driver state. The isstarted() function has been replaced with get_state().
This commit is contained in:
committed by
Daniel Hellstrom
parent
ca4c4164b5
commit
2d3d8f96b2
@@ -92,11 +92,11 @@
|
|||||||
|
|
||||||
/* Uncomment for debug output */
|
/* Uncomment for debug output */
|
||||||
/****************** DEBUG Definitions ********************/
|
/****************** DEBUG Definitions ********************/
|
||||||
#define DBG_IOCTRL 1
|
|
||||||
#define DBG_TX 2
|
#define DBG_TX 2
|
||||||
#define DBG_RX 4
|
#define DBG_RX 4
|
||||||
|
#define DBG_STATE 8
|
||||||
|
|
||||||
#define DEBUG_FLAGS (DBG_IOCTRL | DBG_RX | DBG_TX )
|
#define DEBUG_FLAGS (DBG_STATE | DBG_RX | DBG_TX )
|
||||||
/*
|
/*
|
||||||
#define DEBUG
|
#define DEBUG
|
||||||
#define DEBUGFUNCS
|
#define DEBUGFUNCS
|
||||||
@@ -105,6 +105,13 @@
|
|||||||
|
|
||||||
/*********************************************************/
|
/*********************************************************/
|
||||||
|
|
||||||
|
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 {
|
struct grcan_msg {
|
||||||
unsigned int head[2];
|
unsigned int head[2];
|
||||||
unsigned char data[8];
|
unsigned char data[8];
|
||||||
@@ -156,13 +163,13 @@ struct grcan_priv {
|
|||||||
|
|
||||||
static void __inline__ grcan_hw_reset(struct grcan_regs *regs);
|
static void __inline__ grcan_hw_reset(struct grcan_regs *regs);
|
||||||
|
|
||||||
static unsigned int grcan_hw_read_try(
|
static int grcan_hw_read_try(
|
||||||
struct grcan_priv *pDev,
|
struct grcan_priv *pDev,
|
||||||
struct grcan_regs *regs,
|
struct grcan_regs *regs,
|
||||||
CANMsg *buffer,
|
CANMsg *buffer,
|
||||||
int max);
|
int max);
|
||||||
|
|
||||||
static unsigned int grcan_hw_write_try(
|
static int grcan_hw_write_try(
|
||||||
struct grcan_priv *pDev,
|
struct grcan_priv *pDev,
|
||||||
struct grcan_regs *regs,
|
struct grcan_regs *regs,
|
||||||
CANMsg *buffer,
|
CANMsg *buffer,
|
||||||
@@ -601,17 +608,6 @@ static unsigned int grcan_hw_txspace(
|
|||||||
return left / GRCAN_MSG_SIZE - WRAP_AROUND_TX_MSGS;
|
return left / GRCAN_MSG_SIZE - WRAP_AROUND_TX_MSGS;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int grcan_hw_rx_ongoing(struct grcan_regs *regs)
|
|
||||||
{
|
|
||||||
return READ_REG(®s->rx0ctrl) & GRCAN_RXCTRL_ONGOING;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int grcan_hw_tx_ongoing(struct grcan_regs *regs)
|
|
||||||
{
|
|
||||||
return READ_REG(®s->tx0ctrl) & GRCAN_TXCTRL_ONGOING;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#define MIN_TSEG1 1
|
#define MIN_TSEG1 1
|
||||||
#define MIN_TSEG2 2
|
#define MIN_TSEG2 2
|
||||||
#define MAX_TSEG1 14
|
#define MAX_TSEG1 14
|
||||||
@@ -723,7 +719,7 @@ static int grcan_calc_timing(
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static unsigned int grcan_hw_read_try(
|
static int grcan_hw_read_try(
|
||||||
struct grcan_priv *pDev,
|
struct grcan_priv *pDev,
|
||||||
struct grcan_regs *regs,
|
struct grcan_regs *regs,
|
||||||
CANMsg * buffer,
|
CANMsg * buffer,
|
||||||
@@ -733,7 +729,8 @@ static unsigned int grcan_hw_read_try(
|
|||||||
int i, j;
|
int i, j;
|
||||||
CANMsg *dest;
|
CANMsg *dest;
|
||||||
struct grcan_msg *source, tmp;
|
struct grcan_msg *source, tmp;
|
||||||
unsigned int wp, rp, size, rxmax, addr, trunk_msg_cnt;
|
unsigned int wp, rp, size, rxmax, addr;
|
||||||
|
int trunk_msg_cnt;
|
||||||
|
|
||||||
FUNCDBG();
|
FUNCDBG();
|
||||||
|
|
||||||
@@ -775,6 +772,15 @@ static unsigned int grcan_hw_read_try(
|
|||||||
/* Read CAN message from DMA buffer */
|
/* Read CAN message from DMA buffer */
|
||||||
tmp.head[0] = READ_DMA_WORD(&source->head[0]);
|
tmp.head[0] = READ_DMA_WORD(&source->head[0]);
|
||||||
tmp.head[1] = READ_DMA_WORD(&source->head[1]);
|
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 */
|
/* Convert one grcan CAN message to one "software" CAN message */
|
||||||
dest->extended = tmp.head[0] >> 31;
|
dest->extended = tmp.head[0] >> 31;
|
||||||
dest->rtr = (tmp.head[0] >> 30) & 0x1;
|
dest->rtr = (tmp.head[0] >> 30) & 0x1;
|
||||||
@@ -794,29 +800,34 @@ static unsigned int grcan_hw_read_try(
|
|||||||
dest++; /* straight user buffer */
|
dest++; /* straight user buffer */
|
||||||
i--;
|
i--;
|
||||||
}
|
}
|
||||||
/* Increment Hardware READ pointer (mark read byte as read)
|
{
|
||||||
* ! wait for registers to be safely re-configurable
|
/* A bus off interrupt may have occured after checking pDev->started */
|
||||||
*/
|
SPIN_IRQFLAGS(oldLevel);
|
||||||
regs->rx0ctrl = 0; /* DISABLE RX CHANNEL */
|
|
||||||
i = 0;
|
SPIN_LOCK_IRQ(&pDev->devlock, oldLevel);
|
||||||
while (grcan_hw_rx_ongoing(regs) && (i < 1000)) {
|
if (pDev->started == STATE_STARTED) {
|
||||||
i++;
|
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);
|
||||||
}
|
}
|
||||||
regs->rx0rd = (unsigned int)source - addr;
|
|
||||||
regs->rx0ctrl = GRCAN_RXCTRL_ENABLE; /* ENABLE_RX_CHANNEL */
|
|
||||||
return trunk_msg_cnt;
|
return trunk_msg_cnt;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static unsigned int grcan_hw_write_try(
|
static int grcan_hw_write_try(
|
||||||
struct grcan_priv *pDev,
|
struct grcan_priv *pDev,
|
||||||
struct grcan_regs *regs,
|
struct grcan_regs *regs,
|
||||||
CANMsg * buffer,
|
CANMsg * buffer,
|
||||||
int count
|
int count
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
unsigned int rp, wp, size, txmax, addr, ret;
|
unsigned int rp, wp, size, txmax, addr;
|
||||||
|
int ret;
|
||||||
struct grcan_msg *dest;
|
struct grcan_msg *dest;
|
||||||
CANMsg *source;
|
CANMsg *source;
|
||||||
int space_left;
|
int space_left;
|
||||||
@@ -867,16 +878,20 @@ static unsigned int grcan_hw_write_try(
|
|||||||
space_left--;
|
space_left--;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Update write pointer
|
{
|
||||||
* ! wait for registers to be safely re-configurable
|
/* A bus off interrupt may have occured after checking pDev->started */
|
||||||
*/
|
SPIN_IRQFLAGS(oldLevel);
|
||||||
regs->tx0ctrl = 0; /* DISABLE TX CHANNEL */
|
|
||||||
i = 0;
|
SPIN_LOCK_IRQ(&pDev->devlock, oldLevel);
|
||||||
while ((grcan_hw_tx_ongoing(regs)) && i < 1000) {
|
if (pDev->started == STATE_STARTED) {
|
||||||
i++;
|
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);
|
||||||
}
|
}
|
||||||
regs->tx0wr = (unsigned int)dest - addr; /* Update write pointer */
|
|
||||||
regs->tx0ctrl = GRCAN_TXCTRL_ENABLE; /* ENABLE_TX_CHANNEL */
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -884,7 +899,7 @@ static int grcan_wait_rxdata(struct grcan_priv *pDev, int min)
|
|||||||
{
|
{
|
||||||
unsigned int wp, rp, size, irq;
|
unsigned int wp, rp, size, irq;
|
||||||
unsigned int irq_trunk, dataavail;
|
unsigned int irq_trunk, dataavail;
|
||||||
int wait;
|
int wait, state;
|
||||||
SPIN_IRQFLAGS(oldLevel);
|
SPIN_IRQFLAGS(oldLevel);
|
||||||
|
|
||||||
FUNCDBG();
|
FUNCDBG();
|
||||||
@@ -894,6 +909,20 @@ static int grcan_wait_rxdata(struct grcan_priv *pDev, int min)
|
|||||||
* when one or more messages are received
|
* when one or more messages are received
|
||||||
*/
|
*/
|
||||||
SPIN_LOCK_IRQ(&pDev->devlock, oldLevel);
|
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);
|
size = READ_REG(&pDev->regs->rx0size);
|
||||||
rp = READ_REG(&pDev->regs->rx0rd);
|
rp = READ_REG(&pDev->regs->rx0rd);
|
||||||
@@ -936,15 +965,18 @@ static int grcan_wait_rxdata(struct grcan_priv *pDev, int min)
|
|||||||
RTEMS_WAIT,
|
RTEMS_WAIT,
|
||||||
RTEMS_NO_TIMEOUT
|
RTEMS_NO_TIMEOUT
|
||||||
) == RTEMS_UNSATISFIED
|
) == RTEMS_UNSATISFIED
|
||||||
)
|
) {
|
||||||
return -1; /* Device driver has been closed or stopped, return with error status */
|
DBGC(DBG_STATE, "UNSATISFIED\n");
|
||||||
|
/* Device driver has been closed or stopped, return with error status */
|
||||||
|
return state2err[pDev->started];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Wait until min bytes available in TX circular buffer.
|
/* Wait until min bytes available in TX circular buffer. TXIRQ is used to pin
|
||||||
* The IRQ RxIrq is used to pin point the location of
|
* point the location of the CAN message corresponding to min.
|
||||||
*
|
*
|
||||||
* min must be at least WRAP_AROUND_TX_BYTES bytes less
|
* min must be at least WRAP_AROUND_TX_BYTES bytes less
|
||||||
* than max buffer for this algo to work.
|
* than max buffer for this algo to work.
|
||||||
@@ -952,7 +984,7 @@ static int grcan_wait_rxdata(struct grcan_priv *pDev, int min)
|
|||||||
*/
|
*/
|
||||||
static int grcan_wait_txspace(struct grcan_priv *pDev, int min)
|
static int grcan_wait_txspace(struct grcan_priv *pDev, int min)
|
||||||
{
|
{
|
||||||
int wait;
|
int wait, state;
|
||||||
unsigned int irq, rp, wp, size, space_left;
|
unsigned int irq, rp, wp, size, space_left;
|
||||||
unsigned int irq_trunk;
|
unsigned int irq_trunk;
|
||||||
SPIN_IRQFLAGS(oldLevel);
|
SPIN_IRQFLAGS(oldLevel);
|
||||||
@@ -961,6 +993,19 @@ static int grcan_wait_txspace(struct grcan_priv *pDev, int min)
|
|||||||
/*FUNCDBG(); */
|
/*FUNCDBG(); */
|
||||||
|
|
||||||
SPIN_LOCK_IRQ(&pDev->devlock, oldLevel);
|
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;
|
pDev->regs->tx0ctrl = GRCAN_TXCTRL_ENABLE;
|
||||||
|
|
||||||
@@ -1010,7 +1055,8 @@ static int grcan_wait_txspace(struct grcan_priv *pDev, int min)
|
|||||||
RTEMS_UNSATISFIED) {
|
RTEMS_UNSATISFIED) {
|
||||||
/* Device driver has flushed us, this may be due to another thread has
|
/* Device driver has flushed us, this may be due to another thread has
|
||||||
* closed the device, this is to avoid deadlock */
|
* closed the device, this is to avoid deadlock */
|
||||||
return -1;
|
DBGC(DBG_STATE, "UNSATISFIED\n");
|
||||||
|
return state2err[pDev->started];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1020,7 +1066,7 @@ static int grcan_wait_txspace(struct grcan_priv *pDev, int min)
|
|||||||
|
|
||||||
static int grcan_tx_flush(struct grcan_priv *pDev)
|
static int grcan_tx_flush(struct grcan_priv *pDev)
|
||||||
{
|
{
|
||||||
int wait;
|
int wait, state;
|
||||||
unsigned int rp, wp;
|
unsigned int rp, wp;
|
||||||
SPIN_IRQFLAGS(oldLevel);
|
SPIN_IRQFLAGS(oldLevel);
|
||||||
FUNCDBG();
|
FUNCDBG();
|
||||||
@@ -1036,6 +1082,21 @@ static int grcan_tx_flush(struct grcan_priv *pDev)
|
|||||||
) {
|
) {
|
||||||
/* Wait for TX empty IRQ */
|
/* Wait for TX empty IRQ */
|
||||||
SPIN_LOCK_IRQ(&pDev->devlock, oldLevel);
|
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 */
|
/* Clear pending TXEmpty IRQ */
|
||||||
pDev->regs->picr = GRCAN_TXEMPTY_IRQ;
|
pDev->regs->picr = GRCAN_TXEMPTY_IRQ;
|
||||||
|
|
||||||
@@ -1056,7 +1117,8 @@ static int grcan_tx_flush(struct grcan_priv *pDev)
|
|||||||
if (rtems_semaphore_obtain
|
if (rtems_semaphore_obtain
|
||||||
(pDev->txempty_sem, RTEMS_WAIT,
|
(pDev->txempty_sem, RTEMS_WAIT,
|
||||||
RTEMS_NO_TIMEOUT) == RTEMS_UNSATISFIED) {
|
RTEMS_NO_TIMEOUT) == RTEMS_UNSATISFIED) {
|
||||||
return -1;
|
DBGC(DBG_STATE, "UNSATISFIED\n");
|
||||||
|
return state2err[pDev->started];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
@@ -1231,7 +1293,7 @@ void *grcan_open(int dev_no)
|
|||||||
|
|
||||||
pDev->txblock = pDev->rxblock = 1;
|
pDev->txblock = pDev->rxblock = 1;
|
||||||
pDev->txcomplete = pDev->rxcomplete = 0;
|
pDev->txcomplete = pDev->rxcomplete = 0;
|
||||||
pDev->started = 0;
|
pDev->started = STATE_STOPPED;
|
||||||
pDev->config_changed = 1;
|
pDev->config_changed = 1;
|
||||||
pDev->config.silent = 0;
|
pDev->config.silent = 0;
|
||||||
pDev->config.abort = 0;
|
pDev->config.abort = 0;
|
||||||
@@ -1296,12 +1358,7 @@ int grcan_close(void *d)
|
|||||||
|
|
||||||
FUNCDBG();
|
FUNCDBG();
|
||||||
|
|
||||||
if ( pDev->started ) {
|
grcan_stop(d);
|
||||||
/* Disable interrupts */
|
|
||||||
drvmgr_interrupt_unregister(pDev->dev, 0, grcan_interrupt, pDev);
|
|
||||||
grcan_hw_stop(pDev);
|
|
||||||
pDev->started = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
grcan_hw_reset(pDev->regs);
|
grcan_hw_reset(pDev->regs);
|
||||||
|
|
||||||
@@ -1318,6 +1375,7 @@ int grcan_read(void *d, CANMsg *msg, size_t ucount)
|
|||||||
struct grcan_priv *pDev = d;
|
struct grcan_priv *pDev = d;
|
||||||
CANMsg *dest;
|
CANMsg *dest;
|
||||||
unsigned int count, left;
|
unsigned int count, left;
|
||||||
|
int nread;
|
||||||
int req_cnt;
|
int req_cnt;
|
||||||
|
|
||||||
FUNCDBG();
|
FUNCDBG();
|
||||||
@@ -1326,14 +1384,19 @@ int grcan_read(void *d, CANMsg *msg, size_t ucount)
|
|||||||
req_cnt = ucount;
|
req_cnt = ucount;
|
||||||
|
|
||||||
if ( (!dest) || (req_cnt<1) )
|
if ( (!dest) || (req_cnt<1) )
|
||||||
return -1;
|
return GRCAN_RET_INVARG;
|
||||||
|
|
||||||
if ( !pDev->started )
|
if (pDev->started != STATE_STARTED) {
|
||||||
return -2;
|
return GRCAN_RET_NOTSTARTED;
|
||||||
|
}
|
||||||
|
|
||||||
/*FUNCDBG("grcan_read [%i,%i]: buf: 0x%x len: %i\n",major, minor, (unsigned int)rw_args->buffer,rw_args->count);*/
|
DBGC(DBG_RX, "grcan_read [%p]: buf: %p len: %u\n", d, msg, (unsigned int) ucount);
|
||||||
|
|
||||||
count = grcan_hw_read_try(pDev,pDev->regs,dest,req_cnt);
|
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 ( !( pDev->rxblock && pDev->rxcomplete && (count!=req_cnt) ) ){
|
||||||
if ( count > 0 ) {
|
if ( count > 0 ) {
|
||||||
/* Successfully received messages (at least one) */
|
/* Successfully received messages (at least one) */
|
||||||
@@ -1343,7 +1406,7 @@ int grcan_read(void *d, CANMsg *msg, size_t ucount)
|
|||||||
/* nothing read, shall we block? */
|
/* nothing read, shall we block? */
|
||||||
if ( !pDev->rxblock ) {
|
if ( !pDev->rxblock ) {
|
||||||
/* non-blocking mode */
|
/* non-blocking mode */
|
||||||
return -3;
|
return GRCAN_RET_TIMEOUT;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1363,20 +1426,27 @@ int grcan_read(void *d, CANMsg *msg, size_t ucount)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (grcan_wait_rxdata(pDev, left)) {
|
nread = grcan_wait_rxdata(pDev, left);
|
||||||
|
if (nread) {
|
||||||
/* The wait has been aborted, probably due to
|
/* The wait has been aborted, probably due to
|
||||||
* the device driver has been closed by another
|
* the device driver has been closed by another
|
||||||
* thread.
|
* thread or a bus-off. Return error code.
|
||||||
*/
|
*/
|
||||||
return count;
|
return nread;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Try read bytes from circular buffer */
|
/* Try read bytes from circular buffer */
|
||||||
count += grcan_hw_read_try(
|
nread = grcan_hw_read_try(
|
||||||
pDev,
|
pDev,
|
||||||
pDev->regs,
|
pDev->regs,
|
||||||
dest+count,
|
dest+count,
|
||||||
req_cnt-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. */
|
/* no need to unmask IRQ as IRQ Handler do that for us. */
|
||||||
return count;
|
return count;
|
||||||
@@ -1387,22 +1457,27 @@ int grcan_write(void *d, CANMsg *msg, size_t ucount)
|
|||||||
struct grcan_priv *pDev = d;
|
struct grcan_priv *pDev = d;
|
||||||
CANMsg *source;
|
CANMsg *source;
|
||||||
unsigned int count, left;
|
unsigned int count, left;
|
||||||
|
int nwritten;
|
||||||
int req_cnt;
|
int req_cnt;
|
||||||
|
|
||||||
DBGC(DBG_TX,"\n");
|
DBGC(DBG_TX,"\n");
|
||||||
|
|
||||||
if ( !pDev->started || pDev->config.silent || pDev->flushing )
|
if ((pDev->started != STATE_STARTED) || pDev->config.silent || pDev->flushing)
|
||||||
return -2;
|
return GRCAN_RET_NOTSTARTED;
|
||||||
|
|
||||||
req_cnt = ucount;
|
req_cnt = ucount;
|
||||||
source = (CANMsg *) msg;
|
source = (CANMsg *) msg;
|
||||||
|
|
||||||
/* check proper length and buffer pointer */
|
/* check proper length and buffer pointer */
|
||||||
if (( req_cnt < 1) || (source == NULL) ){
|
if (( req_cnt < 1) || (source == NULL) ){
|
||||||
return -1;
|
return GRCAN_RET_INVARG;
|
||||||
}
|
}
|
||||||
|
|
||||||
count = grcan_hw_write_try(pDev,pDev->regs,source,req_cnt);
|
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 ( !(pDev->txblock && pDev->txcomplete && (count!=req_cnt)) ) {
|
||||||
if ( count > 0 ) {
|
if ( count > 0 ) {
|
||||||
/* Successfully transmitted chars (at least one char) */
|
/* Successfully transmitted chars (at least one char) */
|
||||||
@@ -1412,7 +1487,7 @@ int grcan_write(void *d, CANMsg *msg, size_t ucount)
|
|||||||
/* nothing written, shall we block? */
|
/* nothing written, shall we block? */
|
||||||
if ( !pDev->txblock ) {
|
if ( !pDev->txblock ) {
|
||||||
/* non-blocking mode */
|
/* non-blocking mode */
|
||||||
return -3;
|
return GRCAN_RET_TIMEOUT;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1436,14 +1511,15 @@ int grcan_write(void *d, CANMsg *msg, size_t ucount)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nwritten = grcan_wait_txspace(pDev,left);
|
||||||
/* Wait until more room in transmit buffer */
|
/* Wait until more room in transmit buffer */
|
||||||
if ( grcan_wait_txspace(pDev,left) ){
|
if ( nwritten ) {
|
||||||
/* The wait has been aborted, probably due to
|
/* The wait has been aborted, probably due to
|
||||||
* the device driver has been closed by another
|
* the device driver has been closed by another
|
||||||
* thread. To avoid deadlock we return directly
|
* thread. To avoid deadlock we return directly
|
||||||
* with error status.
|
* with error status.
|
||||||
*/
|
*/
|
||||||
return count;
|
return nwritten;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( pDev->txerror ){
|
if ( pDev->txerror ){
|
||||||
@@ -1455,11 +1531,17 @@ int grcan_write(void *d, CANMsg *msg, size_t ucount)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Try read bytes from circular buffer */
|
/* Try read bytes from circular buffer */
|
||||||
count += grcan_hw_write_try(
|
nwritten = grcan_hw_write_try(
|
||||||
pDev,
|
pDev,
|
||||||
pDev->regs,
|
pDev->regs,
|
||||||
source+count,
|
source+count,
|
||||||
req_cnt-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. */
|
/* no need to unmask IRQ as IRQ Handler do that for us. */
|
||||||
|
|
||||||
@@ -1469,19 +1551,20 @@ int grcan_write(void *d, CANMsg *msg, size_t ucount)
|
|||||||
int grcan_start(void *d)
|
int grcan_start(void *d)
|
||||||
{
|
{
|
||||||
struct grcan_priv *pDev = d;
|
struct grcan_priv *pDev = d;
|
||||||
rtems_device_driver status;
|
|
||||||
|
|
||||||
FUNCDBG();
|
FUNCDBG();
|
||||||
|
|
||||||
if ( pDev->started )
|
if (grcan_get_state(d) == STATE_STARTED) {
|
||||||
return -1;
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
if ( (status=grcan_hw_start(pDev)) != RTEMS_SUCCESSFUL ){
|
if ( (grcan_hw_start(pDev)) != RTEMS_SUCCESSFUL ){
|
||||||
return -2;
|
return -2;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Read and write are now open... */
|
/* Read and write are now open... */
|
||||||
pDev->started = 1;
|
pDev->started = STATE_STARTED;
|
||||||
|
DBGC(DBG_STATE, "STOPPED|BUSOFF|AHBERR->STARTED\n");
|
||||||
|
|
||||||
/* Register interrupt routine and enable IRQ at IRQ ctrl */
|
/* Register interrupt routine and enable IRQ at IRQ ctrl */
|
||||||
drvmgr_interrupt_register(pDev->dev, 0, pDev->devName,
|
drvmgr_interrupt_register(pDev->dev, 0, pDev->devName,
|
||||||
@@ -1493,22 +1576,34 @@ int grcan_start(void *d)
|
|||||||
int grcan_stop(void *d)
|
int grcan_stop(void *d)
|
||||||
{
|
{
|
||||||
struct grcan_priv *pDev = d;
|
struct grcan_priv *pDev = d;
|
||||||
|
SPIN_IRQFLAGS(oldLevel);
|
||||||
|
|
||||||
FUNCDBG();
|
FUNCDBG();
|
||||||
|
|
||||||
if ( !pDev->started )
|
if (pDev->started == STATE_STOPPED)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
|
SPIN_LOCK_IRQ(&pDev->devlock, oldLevel);
|
||||||
|
if (pDev->started == STATE_STARTED) {
|
||||||
|
grcan_hw_stop(pDev);
|
||||||
|
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");
|
||||||
|
}
|
||||||
|
pDev->started = STATE_STOPPED;
|
||||||
|
SPIN_UNLOCK_IRQ(&pDev->devlock, oldLevel);
|
||||||
|
|
||||||
/* Disable interrupts */
|
/* Disable interrupts */
|
||||||
drvmgr_interrupt_unregister(pDev->dev, 0, grcan_interrupt, pDev);
|
drvmgr_interrupt_unregister(pDev->dev, 0, grcan_interrupt, pDev);
|
||||||
|
|
||||||
grcan_hw_stop(pDev);
|
|
||||||
pDev->started = 0;
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int grcan_isstarted(void *d)
|
int grcan_get_state(void *d)
|
||||||
{
|
{
|
||||||
struct grcan_priv *pDev = d;
|
struct grcan_priv *pDev = d;
|
||||||
|
|
||||||
@@ -1524,7 +1619,7 @@ int grcan_flush(void *d)
|
|||||||
|
|
||||||
FUNCDBG();
|
FUNCDBG();
|
||||||
|
|
||||||
if ( !pDev->started || pDev->flushing || pDev->config.silent )
|
if ((pDev->started != STATE_STARTED) || pDev->flushing || pDev->config.silent)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
pDev->flushing = 1;
|
pDev->flushing = 1;
|
||||||
@@ -1547,7 +1642,7 @@ int grcan_set_silent(void* d, int silent)
|
|||||||
|
|
||||||
FUNCDBG();
|
FUNCDBG();
|
||||||
|
|
||||||
if ( pDev->started )
|
if (pDev->started == STATE_STARTED)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
pDev->config.silent = silent;
|
pDev->config.silent = silent;
|
||||||
@@ -1562,7 +1657,7 @@ int grcan_set_abort(void* d, int abort)
|
|||||||
|
|
||||||
FUNCDBG();
|
FUNCDBG();
|
||||||
|
|
||||||
if ( pDev->started )
|
if (pDev->started == STATE_STARTED)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
pDev->config.abort = abort;
|
pDev->config.abort = abort;
|
||||||
@@ -1579,7 +1674,7 @@ int grcan_set_selection(void *d, const struct grcan_selection *selection)
|
|||||||
|
|
||||||
FUNCDBG();
|
FUNCDBG();
|
||||||
|
|
||||||
if ( pDev->started )
|
if (pDev->started == STATE_STARTED)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
if ( !selection )
|
if ( !selection )
|
||||||
@@ -1675,7 +1770,7 @@ int grcan_set_speed(void *d, unsigned int speed)
|
|||||||
FUNCDBG();
|
FUNCDBG();
|
||||||
|
|
||||||
/* cannot change speed during run mode */
|
/* cannot change speed during run mode */
|
||||||
if ( pDev->started )
|
if (pDev->started == STATE_STARTED)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
/* get speed rate from argument */
|
/* get speed rate from argument */
|
||||||
@@ -1699,7 +1794,7 @@ int grcan_set_btrs(void *d, const struct grcan_timing *timing)
|
|||||||
/* Set BTR registers manually
|
/* Set BTR registers manually
|
||||||
* Read GRCAN/HurriCANe Manual.
|
* Read GRCAN/HurriCANe Manual.
|
||||||
*/
|
*/
|
||||||
if ( pDev->started )
|
if (pDev->started == STATE_STARTED)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
if ( !timing )
|
if ( !timing )
|
||||||
@@ -1777,94 +1872,129 @@ int grcan_get_status(void* d, unsigned int *data)
|
|||||||
return 0;
|
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 */
|
/* Handle the IRQ */
|
||||||
static void grcan_interrupt(void *arg)
|
static void grcan_interrupt(void *arg)
|
||||||
{
|
{
|
||||||
struct grcan_priv *pDev = arg;
|
struct grcan_priv *pDev = arg;
|
||||||
unsigned int status = READ_REG(&pDev->regs->pimsr);
|
unsigned int status = READ_REG(&pDev->regs->pimsr);
|
||||||
unsigned int canstat = READ_REG(&pDev->regs->stat);
|
unsigned int canstat = READ_REG(&pDev->regs->stat);
|
||||||
|
unsigned int imr_clear;
|
||||||
SPIN_ISR_IRQFLAGS(irqflags);
|
SPIN_ISR_IRQFLAGS(irqflags);
|
||||||
|
|
||||||
/* Spurious IRQ call? */
|
/* Spurious IRQ call? */
|
||||||
if ( !status && !canstat )
|
if ( !status && !canstat )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
FUNCDBG();
|
if (pDev->started != STATE_STARTED) {
|
||||||
|
DBGC(DBG_STATE, "not STARTED (unexpected interrupt)\n");
|
||||||
/* Increment number of interrupts counter */
|
pDev->regs->picr = status;
|
||||||
pDev->stats.ints++;
|
return;
|
||||||
|
|
||||||
if ( (status & GRCAN_ERR_IRQ) || (canstat & GRCAN_STAT_PASS) ){
|
|
||||||
/* Error-Passive interrupt */
|
|
||||||
pDev->stats.passive_cnt++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( (status & GRCAN_OFF_IRQ) || (canstat & GRCAN_STAT_OFF) ){
|
FUNCDBG();
|
||||||
/* Bus-off condition interrupt
|
|
||||||
|
if ( (status & GRCAN_IRQ_ERRORS) || (canstat & GRCAN_STAT_ERRORS) ) {
|
||||||
|
/* Bus-off condition interrupt
|
||||||
* The link is brought down by hardware, we wake all threads
|
* The link is brought down by hardware, we wake all threads
|
||||||
* that is blocked in read/write calls and stop futher calls
|
* that is blocked in read/write calls and stop futher calls
|
||||||
* to read/write until user has called ioctl(fd,START,0).
|
* to read/write until user has called ioctl(fd,START,0).
|
||||||
*/
|
*/
|
||||||
SPIN_LOCK(&pDev->devlock, irqflags);
|
SPIN_LOCK(&pDev->devlock, irqflags);
|
||||||
pDev->started = 0;
|
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 */
|
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);
|
SPIN_UNLOCK(&pDev->devlock, irqflags);
|
||||||
status=0x1ffff; /* clear all interrupts */
|
/*
|
||||||
goto out;
|
* NOTE: Another interrupt may be pending now so ISR could be
|
||||||
|
* executed one more time aftert this (first) return.
|
||||||
|
*/
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( (status & GRCAN_OR_IRQ) || (canstat & GRCAN_STAT_OR) ){
|
/* Mask interrupts in one place under spin-lock. */
|
||||||
/* Over-run during reception interrupt */
|
imr_clear = status & (GRCAN_RXIRQ_IRQ | GRCAN_TXIRQ_IRQ | GRCAN_TXEMPTY_IRQ);
|
||||||
pDev->stats.overrun_cnt++;
|
|
||||||
|
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 ( (status & GRCAN_RXAHBERR_IRQ) ||
|
if (imr_clear) {
|
||||||
(status & GRCAN_TXAHBERR_IRQ) ||
|
pDev->regs->imr = READ_REG(&pDev->regs->imr) & ~imr_clear;
|
||||||
(canstat & GRCAN_STAT_AHBERR) ){
|
|
||||||
/* RX or Tx AHB Error interrupt */
|
|
||||||
printk("AHBERROR: status: 0x%x, canstat: 0x%x\n",status,canstat);
|
|
||||||
pDev->stats.ahberr_cnt++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( status & GRCAN_TXLOSS_IRQ ) {
|
|
||||||
pDev->stats.txloss_cnt++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( status & GRCAN_RXIRQ_IRQ ){
|
|
||||||
/* RX IRQ pointer interrupt */
|
|
||||||
/*printk("RxIrq 0x%x\n",status);*/
|
|
||||||
SPIN_LOCK(&pDev->devlock, irqflags);
|
|
||||||
pDev->regs->imr = READ_REG(&pDev->regs->imr) & ~GRCAN_RXIRQ_IRQ;
|
|
||||||
SPIN_UNLOCK(&pDev->devlock, irqflags);
|
SPIN_UNLOCK(&pDev->devlock, irqflags);
|
||||||
rtems_semaphore_release(pDev->rx_sem);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( status & GRCAN_TXIRQ_IRQ ){
|
if ( status & GRCAN_RXIRQ_IRQ ) {
|
||||||
/* TX IRQ pointer interrupt */
|
/* RX IRQ pointer interrupt */
|
||||||
SPIN_LOCK(&pDev->devlock, irqflags);
|
rtems_semaphore_release(pDev->rx_sem);
|
||||||
pDev->regs->imr = READ_REG(&pDev->regs->imr) & ~GRCAN_TXIRQ_IRQ;
|
}
|
||||||
|
|
||||||
|
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);
|
SPIN_UNLOCK(&pDev->devlock, irqflags);
|
||||||
rtems_semaphore_release(pDev->tx_sem);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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 ( status & GRCAN_TXEMPTY_IRQ ){
|
|
||||||
SPIN_LOCK(&pDev->devlock, irqflags);
|
|
||||||
pDev->regs->imr = READ_REG(&pDev->regs->imr) & ~GRCAN_TXEMPTY_IRQ;
|
|
||||||
SPIN_UNLOCK(&pDev->devlock, irqflags);
|
|
||||||
rtems_semaphore_release(pDev->txempty_sem);
|
|
||||||
}
|
|
||||||
|
|
||||||
out:
|
|
||||||
/* Clear IRQs */
|
/* Clear IRQs */
|
||||||
pDev->regs->picr = status;
|
pDev->regs->picr = status;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -68,6 +68,7 @@ struct grcan_stats {
|
|||||||
unsigned int txloss_cnt;
|
unsigned int txloss_cnt;
|
||||||
unsigned int ahberr_cnt;
|
unsigned int ahberr_cnt;
|
||||||
unsigned int ints;
|
unsigned int ints;
|
||||||
|
unsigned int busoff_cnt;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct grcan_timing {
|
struct grcan_timing {
|
||||||
@@ -99,6 +100,39 @@ typedef struct {
|
|||||||
unsigned int id;
|
unsigned int id;
|
||||||
} CANMsg;
|
} CANMsg;
|
||||||
|
|
||||||
|
enum {
|
||||||
|
GRCAN_RET_OK = 0,
|
||||||
|
GRCAN_RET_INVARG = -1,
|
||||||
|
GRCAN_RET_NOTSTARTED = -2,
|
||||||
|
GRCAN_RET_TIMEOUT = -3,
|
||||||
|
/* Bus-off condition detected (request aborted by driver) */
|
||||||
|
GRCAN_RET_BUSOFF = -4,
|
||||||
|
/* AHB error condition detected (request aborted by driver) */
|
||||||
|
GRCAN_RET_AHBERR = -5,
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* User functions can cause these transitions:
|
||||||
|
* STATE_STOPPED -> STATE_STARTED
|
||||||
|
* STATE_STARTED -> STATE_STOPPED
|
||||||
|
* STATE_BUSOFF -> STATE_STOPPED
|
||||||
|
* STATE_AHBERR -> STATE_STOPPED
|
||||||
|
*
|
||||||
|
* ISR can cause these transition
|
||||||
|
* STATE_STARTED -> STATE_BUSOFF
|
||||||
|
* STATE_STARTED -> STATE_AHBERR
|
||||||
|
*
|
||||||
|
* STATE_BUSOFF is entered from ISR on bus-off condition. STATE_AHBERR is
|
||||||
|
* entered from ISR on AHB DMA errors on RX/TX operations. At transition the ISR
|
||||||
|
* disables DMA, masks all interrupts and releases semaphores.
|
||||||
|
*/
|
||||||
|
enum grcan_state {
|
||||||
|
STATE_STOPPED = 0,
|
||||||
|
STATE_STARTED = 1,
|
||||||
|
STATE_BUSOFF = 2,
|
||||||
|
STATE_AHBERR = 3,
|
||||||
|
};
|
||||||
|
|
||||||
#define GRCAN_CFG_ABORT 0x00000001
|
#define GRCAN_CFG_ABORT 0x00000001
|
||||||
#define GRCAN_CFG_ENABLE0 0x00000002
|
#define GRCAN_CFG_ENABLE0 0x00000002
|
||||||
#define GRCAN_CFG_ENABLE1 0x00000004
|
#define GRCAN_CFG_ENABLE1 0x00000004
|
||||||
@@ -198,13 +232,14 @@ extern int grcan_close(void *d);
|
|||||||
* count: Number of CAN messages to receive
|
* count: Number of CAN messages to receive
|
||||||
*
|
*
|
||||||
* return:
|
* return:
|
||||||
* >=0: Number of CAN messages received. This can be less than the
|
* >=0: Number of CAN messages received. This can be
|
||||||
* count parameter.
|
* less than the count parameter.
|
||||||
* -1: count parameter less than size of struct grcan_msg or NULL msg.
|
* GRCAN_RET_INVARG: count parameter less than one or NULL msg.
|
||||||
* -2: Device not in started mode
|
* GRCAN_RET_NOTSTARTED: Device not in started mode
|
||||||
* -3: Timeout in non-blocking mode
|
* GRCAN_RET_TIMEOUT: Timeout in non-blocking mode
|
||||||
* -4: A blocking read was interrupted by a Bus-off error. Device has
|
* GRCAN_RET_BUSOFF: A read was interrupted by a bus-off error.
|
||||||
* left started mode.
|
* Device has left started mode.
|
||||||
|
* GRCAN_RET_AHBERR: Similar to BUSOFF, but was caused by AHB Error.
|
||||||
*/
|
*/
|
||||||
extern int grcan_read(
|
extern int grcan_read(
|
||||||
void *d,
|
void *d,
|
||||||
@@ -222,12 +257,14 @@ extern int grcan_read(
|
|||||||
* count: Number of CAN messages to transmit
|
* count: Number of CAN messages to transmit
|
||||||
*
|
*
|
||||||
* return:
|
* return:
|
||||||
* >=0: Number of CAN messages transmitted. This can be less than the
|
* >=0: Number of CAN messages transmitted. This can be
|
||||||
* count parameter.
|
* less than the count parameter.
|
||||||
* -1: count parameter less than size of struct grcan_msg
|
* GRCAN_RET_INVARG: count parameter less than one.
|
||||||
* -2: Device not in started mode
|
* GRCAN_RET_NOTSTARTED: Device not in started mode
|
||||||
* -3: Timeout in non-blocking mode
|
* GRCAN_RET_TIMEOUT: Timeout in non-blocking mode
|
||||||
* -4: Bus-off error. Device has left started mode
|
* GRCAN_RET_BUSOFF: A write was interrupted by a Bus-off error.
|
||||||
|
* Device has left started mode
|
||||||
|
* GRCAN_RET_AHBERR: Similar to BUSOFF, but was caused by AHB Error.
|
||||||
*/
|
*/
|
||||||
extern int grcan_write(
|
extern int grcan_write(
|
||||||
void *d,
|
void *d,
|
||||||
@@ -235,6 +272,21 @@ extern int grcan_write(
|
|||||||
size_t count
|
size_t count
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns current GRCAN software state
|
||||||
|
*
|
||||||
|
* If STATE_BUSOFF or STATE_AHBERR is returned then the function grcan_stop()
|
||||||
|
* shall be called before continue using the driver.
|
||||||
|
*
|
||||||
|
* d: Device handle
|
||||||
|
* return:
|
||||||
|
* STATE_STOPPED Stopped
|
||||||
|
* STATE_STARTED Started
|
||||||
|
* STATE_BUSOFF Bus-off has been detected
|
||||||
|
* STATE_AHBERR AHB error has been detected
|
||||||
|
*/
|
||||||
|
extern int grcan_get_state(void *d);
|
||||||
|
|
||||||
/* The remaining functions return 0 on success and non-zero on failure. */
|
/* The remaining functions return 0 on success and non-zero on failure. */
|
||||||
|
|
||||||
/* Functions controlling operational
|
/* Functions controlling operational
|
||||||
@@ -244,8 +296,6 @@ extern int grcan_write(
|
|||||||
extern int grcan_start(void *d);
|
extern int grcan_start(void *d);
|
||||||
/* stop to change baud rate/config or closing down */
|
/* stop to change baud rate/config or closing down */
|
||||||
extern int grcan_stop(void *d);
|
extern int grcan_stop(void *d);
|
||||||
/* return 1 when started, othervise 0 */
|
|
||||||
extern int grcan_isstarted(void *d);
|
|
||||||
/* Wait until all TX messages have been sent */
|
/* Wait until all TX messages have been sent */
|
||||||
extern int grcan_flush(void *d);
|
extern int grcan_flush(void *d);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user