SAMV7 MCAN: use FIFO mode instead of QUEUE mode; improve error reporting

When using QUEUE mode sometimes the counting semaphore indicates there is no space left in the TX buffers, but in fact there is.  This leads to a situation, where all TX buffers are empty and the driver
still waits for space in the buffers.  The switch from QUEUE mode to FIFO mode is just a workarround to make the semaphore counting self repairing.

The Error reporting is changed due to some Error Interrupts not reporting states, they are reporting state changes. To keep this into Account the static Error conditions like WARNING, PASSIVE or BUS_OFF are filled in
every time.
This commit is contained in:
Frank Benkert 2016-03-04 10:15:35 -06:00 committed by Gregory Nutt
parent 7fd57d1591
commit a115e13e06
2 changed files with 80 additions and 25 deletions

View File

@ -1829,6 +1829,10 @@ endmenu # USB High Speed Device Controller driver (DCD) options
if SAMV7_MCAN
config SAMV7_MCAN_QUEUE_MODE
bool "MCAN QUEUE mode (vs FIFO mode)"
default n
menu "MCAN device driver options"
choice

View File

@ -1438,6 +1438,9 @@ static void mcan_buffer_reserve(FAR struct sam_mcan_s *priv)
irqstate_t flags;
uint32_t txfqs1;
uint32_t txfqs2;
#ifndef CONFIG_SAMV7_MCAN_QUEUE_MODE
int tffl;
#endif
int sval;
int ret;
@ -1483,6 +1486,7 @@ static void mcan_buffer_reserve(FAR struct sam_mcan_s *priv)
leave_critical_section(flags);
}
#ifdef CONFIG_SAMV7_MCAN_QUEUE_MODE
/* We only have one useful bit of information in the TXFQS:
* Is the TX FIFOQ full or not? We can only do limited checks
* with that single bit of information.
@ -1535,6 +1539,28 @@ static void mcan_buffer_reserve(FAR struct sam_mcan_s *priv)
leave_critical_section(flags);
return;
}
#else
/* Tx FIFO Free Level */
tffl = (txfqs1 & MCAN_TXFQS_TFFL_MASK) >> MCAN_TXFQS_TFFL_SHIFT;
/* Check if the configured number is less than the number of buffers
* in the chip
*/
if (tffl > priv->config->ntxfifoq)
{
candbg("ERROR: TX FIFO reports %d but max is %d\n",
tffl, priv->config->ntxfifoq);
tffl = priv->config->ntxfifoq;
}
if (sval != tffl)
{
candbg("ERROR: TX FIFO reports %d but txfsem is %d\n", tffl, sval);
sem_init(&priv->txfsem, 0, tffl);
}
#endif
/* The semaphore value is reasonable. Wait for the next TC interrupt. */
@ -2846,7 +2872,11 @@ static bool mcan_txempty(FAR struct can_dev_s *dev)
{
FAR struct sam_mcan_s *priv = dev->cd_priv;
uint32_t regval;
#ifdef CONFIG_SAMV7_MCAN_QUEUE_MODE
int sval;
#else
int tffl;
#endif
bool empty;
DEBUGASSERT(priv != NULL && priv->config != NULL);
@ -2868,6 +2898,7 @@ static bool mcan_txempty(FAR struct can_dev_s *dev)
return false;
}
#ifdef CONFIG_SAMV7_MCAN_QUEUE_MODE
/* The TX FIFO/Queue is not full, but is it empty? The txfsem should
* track the number of elements the TX FIFO/queue in use.
*
@ -2880,6 +2911,13 @@ static bool mcan_txempty(FAR struct can_dev_s *dev)
DEBUGASSERT(sval > 0 && sval <= priv->config->ntxfifoq);
empty = (sval == priv->config->ntxfifoq);
#else
/* Tx FIFO Free Level */
tffl = (regval & MCAN_TXFQS_TFFL_MASK) >> MCAN_TXFQS_TFFL_SHIFT;
empty = (tffl >= priv->config->ntxfifoq);
#endif
mcan_dev_unlock(priv);
return empty;
}
@ -2938,9 +2976,11 @@ bool mcan_dedicated_rxbuffer_available(FAR struct sam_mcan_s *priv, int bufndx)
static void mcan_error(FAR struct can_dev_s *dev, uint32_t status,
uint32_t oldstatus)
{
FAR struct sam_mcan_s *priv = dev->cd_priv;
struct can_hdr_s hdr;
uint8_t data[CAN_ERROR_DLC];
uint32_t psr;
uint16_t errbits;
uint8_t data[CAN_ERROR_DLC];
int ret;
/* Encode error bits */
@ -2948,35 +2988,42 @@ static void mcan_error(FAR struct can_dev_s *dev, uint32_t status,
errbits = 0;
memset(data, 0, sizeof(data));
if ((status & MCAN_INT_EP) != 0)
{
/* Error Passive */
/* Always fill in "static" error conditions, but set the signaling bit
* only if the condition has changed (see IRQ-Flags below)
* They have to be filled in every time CAN_ERROR_CONTROLLER is set.
*/
errbits |= CAN_ERROR_CONTROLLER;
psr = mcan_getreg(priv, SAM_MCAN_PSR_OFFSET);
if (psr & MCAN_PSR_BO)
{
errbits |= CAN_ERROR_BUSOFF;
}
if (psr & MCAN_PSR_EP)
{
data[1] |= (CAN_ERROR1_RXPASSIVE | CAN_ERROR1_TXPASSIVE);
}
else if ((oldstatus & MCAN_INT_EP) != 0)
{
errbits |= CAN_ERROR_CONTROLLER;
}
if ((status & MCAN_INT_EW) != 0)
if (psr & MCAN_PSR_EW)
{
/* Warning Status */
errbits |= CAN_ERROR_CONTROLLER;
data[1] |= (CAN_ERROR1_RXWARNING | CAN_ERROR1_TXWARNING);
}
else if ((oldstatus & MCAN_INT_EW) != 0)
if ((status & (MCAN_INT_EP | MCAN_INT_EW)) != 0)
{
/* "Error Passive" or "Error Warning" status changed */
errbits |= CAN_ERROR_CONTROLLER;
}
if ((status & MCAN_INT_BO) != 0)
{
/* Bus_Off Status */
/* Bus_Off Status changed */
errbits |= CAN_ERROR_BUSOFF;
if (!(psr & MCAN_PSR_BO))
{
errbits |= CAN_ERROR_RESTARTED;
}
}
if ((status & (MCAN_INT_RF0L | MCAN_INT_RF1L)) != 0)
@ -3070,7 +3117,7 @@ static void mcan_error(FAR struct can_dev_s *dev, uint32_t status,
if ((status & MCAN_INT_STE) != 0)
{
/* Stuff Error */
errbits |= CAN_ERROR_PROTOCOL;
data[2] |= CAN_ERROR2_STUFF;
}
@ -3262,14 +3309,14 @@ static void mcan_interrupt(FAR struct can_dev_s *dev)
mcan_putreg(priv, SAM_MCAN_IR_OFFSET, MCAN_TXERR_INTS);
/* REVISIT: Will MCAN_INT_TC also be set in the event of
* a transmission error? Each write must conclude with a
* call to man_buffer_release(), whether or not the write
* was successful.
*
* We assume that MCAN_INT_TC will be called for each
* message buffer. Except the transfer is cancelled.
* TODO: add handling for MCAN_INT_TCF
*/
* a transmission error? Each write must conclude with a
* call to man_buffer_release(), whether or not the write
* was successful.
*
* We assume that MCAN_INT_TC will be called for each
* message buffer. Except the transfer is cancelled.
* TODO: add handling for MCAN_INT_TCF
*/
}
/* Check for reception errors */
@ -3733,7 +3780,11 @@ static int mcan_hw_initialize(struct sam_mcan_s *priv)
*/
regval = mcan_getreg(priv, SAM_MCAN_TXBC_OFFSET);
#ifdef CONFIG_SAMV7_MCAN_QUEUE_MODE
regval |= MCAN_TXBC_TFQM;
#else
regval &= ~MCAN_TXBC_TFQM;
#endif
mcan_putreg(priv, SAM_MCAN_TXBC_OFFSET, regval);
#ifdef SAMV7_MCAN_LOOPBACK