stm32: add CAN error support

This commit is contained in:
raiden00pl 2022-01-05 09:57:18 +01:00 committed by Xiang Xiao
parent a8ca1ec026
commit 5b9b3814f8
2 changed files with 266 additions and 7 deletions

View File

@ -3388,6 +3388,7 @@ config STM32_I2C
config STM32_CAN
bool
select ARCH_HAVE_CAN_ERRORS
config STM32_TIM
bool

View File

@ -67,6 +67,14 @@
# undef CONFIG_STM32_CAN_REGDEBUG
#endif
/* CAN error interrupts */
#ifdef CONFIG_CAN_ERRORS
# define STM32_CAN_ERRINT (CAN_IER_LECIE | CAN_IER_ERRIE | \
CAN_IER_BOFIE | CAN_IER_EPVIE | \
CAN_IER_EWGIE)
#endif
/****************************************************************************
* Private Types
****************************************************************************/
@ -76,6 +84,9 @@ struct stm32_can_s
uint8_t port; /* CAN port number (1 or 2) */
uint8_t canrx[2]; /* CAN RX FIFO 0/1 IRQ number */
uint8_t cantx; /* CAN TX IRQ number */
#ifdef CONFIG_CAN_ERRORS
uint8_t cansce; /* CAN SCE IRQ number */
#endif
uint8_t filter; /* Filter number */
uint32_t base; /* Base address of the CAN control registers */
uint32_t fbase; /* Base address of the CAN filter registers */
@ -138,12 +149,19 @@ static int stm32can_send(FAR struct can_dev_s *dev,
static bool stm32can_txready(FAR struct can_dev_s *dev);
static bool stm32can_txempty(FAR struct can_dev_s *dev);
#ifdef CONFIG_CAN_ERRORS
static void stm32can_errint(FAR struct can_dev_s *dev, bool enable);
#endif
/* CAN interrupt handling */
static int stm32can_rxinterrupt(FAR struct can_dev_s *dev, int rxmb);
static int stm32can_rx0interrupt(int irq, FAR void *context, FAR void *arg);
static int stm32can_rx1interrupt(int irq, FAR void *context, FAR void *arg);
static int stm32can_txinterrupt(int irq, FAR void *context, FAR void *arg);
#ifdef CONFIG_CAN_ERRORS
static int stm32can_sceinterrupt(int irq, FAR void *context, FAR void *arg);
#endif
/* Initialization */
@ -187,6 +205,9 @@ static struct stm32_can_s g_can1priv =
STM32_IRQ_CAN1RX1,
},
.cantx = STM32_IRQ_CAN1TX,
#ifdef CONFIG_CAN_ERRORS
.cansce = STM32_IRQ_CAN1SCE,
#endif
.filter = 0,
.base = STM32_CAN1_BASE,
.fbase = STM32_CAN1_BASE,
@ -210,6 +231,9 @@ static struct stm32_can_s g_can2priv =
STM32_IRQ_CAN2RX1,
},
.cantx = STM32_IRQ_CAN2TX,
#ifdef CONFIG_CAN_ERRORS
.cansce = STM32_IRQ_CAN2SCE,
#endif
.filter = CAN_NFILTERS / 2,
.base = STM32_CAN2_BASE,
.fbase = STM32_CAN1_BASE,
@ -615,9 +639,16 @@ static int stm32can_setup(FAR struct can_dev_s *dev)
FAR struct stm32_can_s *priv = dev->cd_priv;
int ret;
caninfo("CAN%" PRIu8 " RX0 irq: %" PRIu8 " RX1 irq: %" PRIu8
" TX irq: %" PRIu8 "\n",
priv->port, priv->canrx[0], priv->canrx[1], priv->cantx);
#ifdef CONFIG_CAN_ERRORS
ninfo("CAN%" PRIu8 " RX0 irq: %" PRIu8 " RX1 irq: %" PRIu8
" TX irq: %" PRIu8 " SCE irq: %" PRIu8 "\n",
priv->port, priv->canrx[0], priv->canrx[1], priv->cantx,
priv->cansce);
#else
ninfo("CAN%" PRIu8 " RX0 irq: %" PRIu8 " RX1 irq: %" PRIu8
" TX irq: %" PRIu8 "\n",
priv->port, priv->canrx[0], priv->canrx[1], priv->cantx);
#endif
/* CAN cell initialization */
@ -672,6 +703,20 @@ static int stm32can_setup(FAR struct can_dev_s *dev)
return ret;
}
#ifdef CONFIG_CAN_ERRORS
ret = irq_attach(priv->cansce, stm32can_sceinterrupt, dev);
if (ret < 0)
{
nerr("ERROR: Failed to attach CAN%" PRIu8 " SCE IRQ (%" PRIu8 ")",
priv->port, priv->cansce);
return ret;
}
/* Enable CAN error interrupts */
stm32can_errint(dev, true);
#endif
/* Enable the interrupts at the NVIC. Interrupts are still disabled in
* the CAN module. Since we coming out of reset here, there should be
* no pending interrupts.
@ -680,6 +725,9 @@ static int stm32can_setup(FAR struct can_dev_s *dev)
up_enable_irq(priv->canrx[0]);
up_enable_irq(priv->canrx[1]);
up_enable_irq(priv->cantx);
#ifdef CONFIG_CAN_ERRORS
up_enable_irq(priv->cansce);
#endif
return OK;
}
@ -704,17 +752,23 @@ static void stm32can_shutdown(FAR struct can_dev_s *dev)
caninfo("CAN%" PRIu8 "\n", priv->port);
/* Disable the RX FIFO 0/1 and TX interrupts */
/* Disable the RX FIFO 0/1, TX and SCE interrupts */
up_disable_irq(priv->canrx[0]);
up_disable_irq(priv->canrx[1]);
up_disable_irq(priv->cantx);
#ifdef CONFIG_CAN_ERRORS
up_disable_irq(priv->cansce);
#endif
/* Detach the RX FIFO 0/1 and TX interrupts */
/* Detach the RX FIFO 0/1, TX and SCE interrupts */
irq_detach(priv->canrx[0]);
irq_detach(priv->canrx[1]);
irq_detach(priv->cantx);
#ifdef CONFIG_CAN_ERRORS
irq_detach(priv->cansce);
#endif
/* And reset the hardware */
@ -740,7 +794,7 @@ static void stm32can_rxint(FAR struct can_dev_s *dev, bool enable)
FAR struct stm32_can_s *priv = dev->cd_priv;
uint32_t regval;
caninfo("CAN%" PRIu8 " enable: %d\n", priv->port, enable);
caninfo("CAN%" PRIu8 " rxint enable: %d\n", priv->port, enable);
/* Enable/disable the FIFO 0/1 message pending interrupt */
@ -776,7 +830,7 @@ static void stm32can_txint(FAR struct can_dev_s *dev, bool enable)
FAR struct stm32_can_s *priv = dev->cd_priv;
uint32_t regval;
caninfo("CAN%" PRIu8 " enable: %d\n", priv->port, enable);
caninfo("CAN%" PRIu8 " txint enable: %d\n", priv->port, enable);
/* Support only disabling the transmit mailbox interrupt */
@ -788,6 +842,44 @@ static void stm32can_txint(FAR struct can_dev_s *dev, bool enable)
}
}
#ifdef CONFIG_CAN_ERRORS
/****************************************************************************
* Name: stm32can_errint
*
* Description:
* Call to enable or disable CAN error interrupts.
*
* Input Parameters:
* dev - An instance of the "upper half" can driver state structure.
*
* Returned Value:
* None
*
****************************************************************************/
static void stm32can_errint(FAR struct can_dev_s *dev, bool enable)
{
FAR struct stm32_can_s *priv = dev->cd_priv;
uint32_t regval = 0;
caninfo("CAN%" PRIu8 " errint enable: %d\n", priv->port, enable);
/* Enable/disable the transmit mailbox interrupt */
regval = stm32can_getreg(priv, STM32_CAN_IER_OFFSET);
if (enable)
{
regval |= STM32_CAN_ERRINT;
}
else
{
regval &= ~STM32_CAN_ERRINT;
}
stm32can_putreg(priv, STM32_CAN_IER_OFFSET, regval);
}
#endif
/****************************************************************************
* Name: stm32can_ioctl
*
@ -1649,6 +1741,172 @@ static int stm32can_txinterrupt(int irq, FAR void *context, FAR void *arg)
return OK;
}
#ifdef CONFIG_CAN_ERRORS
/****************************************************************************
* Name: stm32can_sceinterrupt
*
* Description:
* CAN status change interrupt handler
*
* Input Parameters:
* irq - The IRQ number of the interrupt.
* context - The register state save array at the time of the interrupt.
*
* Returned Value:
* Zero on success; a negated errno on failure
*
****************************************************************************/
static int stm32can_sceinterrupt(int irq, FAR void *context, FAR void *arg)
{
FAR struct can_dev_s *dev = (FAR struct can_dev_s *)arg;
FAR struct stm32_can_s *priv = NULL;
struct can_hdr_s hdr;
uint32_t regval = 0;
uint16_t errbits = 0;
uint8_t data[CAN_ERROR_DLC];
int ret = OK;
DEBUGASSERT(dev != NULL && dev->cd_priv != NULL);
priv = dev->cd_priv;
/* Check Error Interrupt flag */
regval = stm32can_getreg(priv, STM32_CAN_MSR_OFFSET);
if (regval & CAN_MSR_ERRI)
{
/* Encode error bits */
errbits = 0;
memset(data, 0, sizeof(data));
/* Get Error statur register */
regval = stm32can_getreg(priv, STM32_CAN_ESR_OFFSET);
if (regval & CAN_ESR_EWGF)
{
/* Error warning flag */
data[1] |= (CAN_ERROR1_RXWARNING | CAN_ERROR1_TXWARNING);
errbits |= CAN_ERROR_CONTROLLER;
}
if (regval & CAN_ESR_EPVF)
{
/* Error passive flag */
data[1] |= (CAN_ERROR1_RXPASSIVE | CAN_ERROR1_TXPASSIVE);
errbits |= CAN_ERROR_CONTROLLER;
}
if (regval & CAN_ESR_BOFF)
{
/* Bus-off flag */
errbits |= CAN_ERROR_BUSOFF;
}
/* Last error code */
if (regval & CAN_ESR_LEC_MASK)
{
if (regval & CAN_ESR_STUFFERROR)
{
/* Stuff Error */
errbits |= CAN_ERROR_PROTOCOL;
data[2] |= CAN_ERROR2_STUFF;
}
else if (regval & CAN_ESR_FORMERROR)
{
/* Format Error */
errbits |= CAN_ERROR_PROTOCOL;
data[2] |= CAN_ERROR2_FORM;
}
else if (regval & CAN_ESR_ACKERROR)
{
/* Acknowledge Error */
errbits |= CAN_ERROR_NOACK;
}
else if (regval & CAN_ESR_BRECERROR)
{
/* Bit recessive Error */
errbits |= CAN_ERROR_PROTOCOL;
data[2] |= CAN_ERROR2_BIT1;
}
else if (regval & CAN_ESR_BDOMERROR)
{
/* Bit dominant Error */
errbits |= CAN_ERROR_PROTOCOL;
data[2] |= CAN_ERROR2_BIT0;
}
else if (regval & CAN_ESR_CRCERRPR)
{
/* Receive CRC Error */
errbits |= CAN_ERROR_PROTOCOL;
data[3] |= CAN_ERROR3_CRCSEQ;
}
}
/* Get transmit status register */
regval = stm32can_getreg(priv, STM32_CAN_TSR_OFFSET);
if (regval & CAN_TSR_ALST0 || regval & CAN_TSR_ALST1 ||
regval & CAN_TSR_ALST2)
{
/* Lost arbitration Error */
errbits |= CAN_ERROR_LOSTARB;
}
/* Clear TSR register */
stm32can_putreg(priv, STM32_CAN_TSR_OFFSET, regval);
/* Clear ERRI flag */
stm32can_putreg(priv, STM32_CAN_MSR_OFFSET, CAN_MSR_ERRI);
}
/* TODO: RX overflow and TX overflow */
/* Report a CAN error */
if (errbits != 0)
{
canerr("ERROR: errbits = %08" PRIx16 "\n", errbits);
/* Format the CAN header for the error report. */
hdr.ch_id = errbits;
hdr.ch_dlc = CAN_ERROR_DLC;
hdr.ch_rtr = 0;
hdr.ch_error = 1;
#ifdef CONFIG_CAN_EXTID
hdr.ch_extid = 0;
#endif
hdr.ch_unused = 0;
/* And provide the error report to the upper half logic */
ret = can_receive(dev, &hdr, data);
if (ret < 0)
{
canerr("ERROR: can_receive failed: %d\n", ret);
}
}
return ret;
}
#endif
/****************************************************************************
* Name: stm32can_bittiming
*