From 0b721ac21c7862294fcca619cf75cd0eec441baf Mon Sep 17 00:00:00 2001 From: Peter van der Perk <peter.vanderperk@nxp.com> Date: Fri, 4 Aug 2023 15:02:39 +0200 Subject: [PATCH] imxrt: flexcan use hpwork for receiving frames FlexCAN before used interrupts to process incoming frames. This patch adds a HPWORK workqueue to process incoming frames in the workqueue context instead. Also renamed mbi to txmb for better readability. --- arch/arm/src/imxrt/imxrt_flexcan.c | 90 +++++++++++++++++++++++------- 1 file changed, 71 insertions(+), 19 deletions(-) diff --git a/arch/arm/src/imxrt/imxrt_flexcan.c b/arch/arm/src/imxrt/imxrt_flexcan.c index cabd1161c0..71010c6720 100644 --- a/arch/arm/src/imxrt/imxrt_flexcan.c +++ b/arch/arm/src/imxrt/imxrt_flexcan.c @@ -66,7 +66,8 @@ * is required. */ -#define CANWORK LPWORK +#define CANWORK LPWORK +#define CANRCVWORK HPWORK /* CONFIG_IMXRT_FLEXCAN_NETHIFS determines the number of physical * interfaces that will be supported. @@ -268,6 +269,7 @@ struct imxrt_driver_s #ifdef TX_TIMEOUT_WQ struct wdog_s txtimeout[TXMBCOUNT]; /* TX timeout timer */ #endif + struct work_s rcvwork; /* For deferring interrupt work to the wq */ struct work_s irqwork; /* For deferring interrupt work to the wq */ struct work_s pollwork; /* For deferring poll work to the work wq */ struct canfd_frame *txdesc_fd; /* A pointer to the list of TX descriptor for FD frames */ @@ -482,6 +484,7 @@ static void imxrt_txdone(struct imxrt_driver_s *priv); static int imxrt_flexcan_interrupt(int irq, void *context, void *arg); +static void imxrt_flexcan_interrupt_work(void *arg); /* Watchdog timer expirations */ #ifdef TX_TIMEOUT_WQ @@ -578,6 +581,7 @@ static int imxrt_transmit(struct imxrt_driver_s *priv) #endif #ifdef CONFIG_NET_CAN_RAW_TX_DEADLINE int32_t timeout; + uint32_t txmb = 0; #endif mbi = RXMBCOUNT + 1; @@ -596,6 +600,9 @@ static int imxrt_transmit(struct imxrt_driver_s *priv) mb_bit <<= 1; mbi++; +#ifdef CONFIG_NET_CAN_RAW_TX_DEADLINE + txmb++; +#endif } if (mbi == TOTALMBCOUNT) @@ -613,7 +620,7 @@ static int imxrt_transmit(struct imxrt_driver_s *priv) { struct timeval *tv = (struct timeval *)(priv->dev.d_buf + priv->dev.d_len); - priv->txmb[mbi].deadline = *tv; + priv->txmb[txmb].deadline = *tv; timeout = (tv->tv_sec - ts.tv_sec)*CLK_TCK + ((tv->tv_usec - ts.tv_nsec / 1000)*CLK_TCK) / 1000000; if (timeout < 0) @@ -629,15 +636,15 @@ static int imxrt_transmit(struct imxrt_driver_s *priv) { timeout = ((CONFIG_NET_CAN_RAW_DEFAULT_TX_DEADLINE / 1000000) *CLK_TCK); - priv->txmb[mbi].deadline.tv_sec = ts.tv_sec + + priv->txmb[txmb].deadline.tv_sec = ts.tv_sec + CONFIG_NET_CAN_RAW_DEFAULT_TX_DEADLINE / 1000000; - priv->txmb[mbi].deadline.tv_usec = (ts.tv_nsec / 1000) + + priv->txmb[txmb].deadline.tv_usec = (ts.tv_nsec / 1000) + CONFIG_NET_CAN_RAW_DEFAULT_TX_DEADLINE % 1000000; } else { - priv->txmb[mbi].deadline.tv_sec = 0; - priv->txmb[mbi].deadline.tv_usec = 0; + priv->txmb[txmb].deadline.tv_sec = 0; + priv->txmb[txmb].deadline.tv_usec = 0; timeout = -1; } } @@ -726,7 +733,7 @@ static int imxrt_transmit(struct imxrt_driver_s *priv) if (timeout > 0) { - wd_start(&priv->txtimeout[mbi - RXMBCOUNT], timeout + 1, + wd_start(&priv->txtimeout[txmb], timeout + 1, imxrt_txtimeout_expiry, (wdparm_t)priv); } #endif @@ -981,6 +988,9 @@ static void imxrt_txdone(struct imxrt_driver_s *priv) uint32_t flags; uint32_t mbi; uint32_t mb_bit; +#ifdef TX_TIMEOUT_WQ + uint32_t txmb; +#endif flags = getreg32(priv->base + IMXRT_CAN_IFLAG1_OFFSET); flags &= IFLAG1_TX; @@ -989,8 +999,13 @@ static void imxrt_txdone(struct imxrt_driver_s *priv) /* Process TX completions */ - mb_bit = 1 << RXMBCOUNT; - for (mbi = 0; flags && mbi < TXMBCOUNT; mbi++) + mbi = RXMBCOUNT + 1; + mb_bit = 1 << mbi; +#ifdef TX_TIMEOUT_WQ + txmb = 0; +#endif + + while (mbi < TOTALMBCOUNT) { if (flags & mb_bit) { @@ -1003,13 +1018,17 @@ static void imxrt_txdone(struct imxrt_driver_s *priv) * mailbox be set to inactive */ - wd_cancel(&priv->txtimeout[mbi]); - struct mb_s *mb = flexcan_get_mb(priv, mbi + RXMBCOUNT); + wd_cancel(&priv->txtimeout[txmb]); + struct mb_s *mb = flexcan_get_mb(priv, mbi); mb->cs.code = CAN_TXMB_INACTIVE; #endif } mb_bit <<= 1; + mbi++; +#ifdef TX_TIMEOUT_WQ + txmb++; +#endif } } @@ -1046,6 +1065,43 @@ static void imxrt_txdone_work(void *arg) net_unlock(); } +/**************************************************************************** + * Function: imxrt_flexcan_interrupt_work + * + * Description: + * Three interrupt sources will vector this this function: + * 1. CAN MB transmit interrupt handler + * 2. CAN MB receive interrupt handler + * 3. + * + * Input Parameters: + * irq - Number of the IRQ that generated the interrupt + * context - Interrupt register state save info (architecture-specific) + * + * Returned Value: + * OK on success + * + * Assumptions: + * + ****************************************************************************/ + +static void imxrt_flexcan_interrupt_work(void *arg) +{ + struct imxrt_driver_s *priv = (struct imxrt_driver_s *)arg; + + uint32_t flags; + flags = getreg32(priv->base + IMXRT_CAN_IFLAG1_OFFSET); + flags &= IFLAG1_RX; + + net_lock(); + imxrt_receive(priv, flags); + net_unlock(); + + /* Mask MB again */ + + modifyreg32(priv->base + IMXRT_CAN_IMASK1_OFFSET, 0, IFLAG1_RX); +} + /**************************************************************************** * Function: imxrt_flexcan_interrupt * @@ -1079,11 +1135,9 @@ static int imxrt_flexcan_interrupt(int irq, void *context, if (flags) { - /* Process immediately since scheduling a workqueue is too slow - * which causes us to drop CAN frames - */ - - imxrt_receive(priv, flags); + modifyreg32(priv->base + IMXRT_CAN_IMASK1_OFFSET, IFLAG1_RX, 0); + work_queue(CANRCVWORK, &priv->rcvwork, + imxrt_flexcan_interrupt_work, priv, 0); } flags = getreg32(priv->base + IMXRT_CAN_IFLAG1_OFFSET); @@ -1095,9 +1149,7 @@ static int imxrt_flexcan_interrupt(int irq, void *context, * condition here. */ - flags = getreg32(priv->base + IMXRT_CAN_IMASK1_OFFSET); - flags &= ~(IFLAG1_TX); - putreg32(flags, priv->base + IMXRT_CAN_IMASK1_OFFSET); + modifyreg32(priv->base + IMXRT_CAN_IMASK1_OFFSET, IFLAG1_TX, 0); work_queue(CANWORK, &priv->irqwork, imxrt_txdone_work, priv, 0); } }