You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nuttx.apache.org by GitBox <gi...@apache.org> on 2022/04/19 07:10:45 UTC

[GitHub] [incubator-nuttx] PetervdPerk-NXP commented on a diff in pull request #6057: arch/stm32h7: Add SocketCAN FDCAN driver

PetervdPerk-NXP commented on code in PR #6057:
URL: https://github.com/apache/incubator-nuttx/pull/6057#discussion_r852668206


##########
arch/arm/src/stm32h7/stm32_fdcan_sock.c:
##########
@@ -0,0 +1,2422 @@
+/****************************************************************************
+ * arch/arm/src/stm32h7/stm32_fdcan_sock.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <debug.h>
+#include <errno.h>
+
+#include <nuttx/can.h>
+#include <nuttx/wdog.h>
+#include <nuttx/irq.h>
+#include <nuttx/arch.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/signal.h>
+#include <nuttx/net/netdev.h>
+#include <nuttx/net/can.h>
+
+#ifdef CONFIG_NET_CAN_RAW_TX_DEADLINE
+#include <sys/time.h>
+#endif
+
+#include <arch/board/board.h>
+
+#include "arm_internal.h"
+#include "chip.h"
+#include "stm32.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define CAN_STD_MASK        0x000007ff
+#define CAN_EXT_MASK        0x1fffffff
+#define CAN_EXT_FLAG        (1 << 31) /* Extended frame format */
+#define CAN_RTR_FLAG        (1 << 30) /* Remote transmission request */
+#define CAN_ERR_FLAG        (1 << 29) /* Error state indicator */
+
+/* General Configuration ****************************************************/
+
+#ifdef CONFIG_NET_CAN_RAW_TX_DEADLINE
+
+#  if !defined(CONFIG_SCHED_WORKQUEUE)
+#    error Work queue support is required
+#  endif
+
+#define TX_TIMEOUT_WQ
+#endif
+
+/* If processing is not done at the interrupt level, then work queue support
+ * is required.
+ */
+
+#define CANWORK LPWORK
+
+/* Message RAM Configuration ************************************************/
+
+#define WORD_LENGTH         4U
+
+/* Define number of Rx / Tx elements in message RAM; note that elements are
+ * given sizes in number of words (4-byte chunks)
+ *
+ * Up to 64 Rx elements and 32 Tx elements may be configured per interface
+ *
+ * Note there are a total of 2560 words available shared between all FDCAN
+ * interfaces for Rx, Tx, and filter storage
+ */
+
+#ifdef CONFIG_NET_CAN_CANFD
+#  define FIFO_ELEMENT_SIZE 18 /* size (in Words) of a FIFO element in message RAM (CANFD_MTU / 4) */
+#  define NUM_RX_FIFO0      14 /* 14 elements max for RX FIFO0 */
+#  define NUM_RX_FIFO1      0  /* No elements for RX FIFO1 */
+#  define NUM_TX_FIFO       7  /* 7 elements max for TX FIFO */
+#else
+#  define FIFO_ELEMENT_SIZE 4  /* size (in Words) of a FIFO element in message RAM (CAN_MTU / 4) */
+#  define NUM_RX_FIFO0      64 /* 64 elements max for RX FIFO0 */
+#  define NUM_RX_FIFO1      0  /* No elements for RX FIFO1 */
+#  define NUM_TX_FIFO       32 /* 32 elements max for TX FIFO */
+#endif
+
+/* Intermediate message buffering *******************************************/
+
+#define POOL_SIZE           1
+
+#ifdef CONFIG_NET_CAN_RAW_TX_DEADLINE
+#define MSG_DATA            sizeof(struct timeval)
+#else
+#define MSG_DATA            0
+#endif
+
+#ifdef CONFIG_NET_CAN_CANFD
+#  define FRAME_TYPE struct canfd_frame
+#else
+#  define FRAME_TYPE struct can_frame
+#endif
+
+/* CAN Clock Configuration **************************************************/
+
+#define STM32_FDCANCLK      STM32_HSE_FREQUENCY
+#define CLK_FREQ            STM32_FDCANCLK
+#define PRESDIV_MAX         256
+
+/* Interrupts ***************************************************************/
+
+#define FDCAN_IR_MASK 0x3fcfffff /* Mask of all non-reserved bits in FDCAN_IR */
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/* CAN ID word, as defined by FDCAN device (Note xtd/rtr/esi bit positions) */
+
+union can_id_u
+{
+  volatile uint32_t can_id;
+  struct
+  {
+    volatile uint32_t extid : 29;
+    volatile uint32_t resex : 3;
+  };
+  struct
+  {
+    volatile uint32_t res   : 18;
+    volatile uint32_t stdid : 11;
+    volatile uint32_t rtr   : 1;
+    volatile uint32_t xtd   : 1;
+    volatile uint32_t esi   : 1;
+  };
+};
+
+/* Union of 4 bytes as 1 register */
+
+union payload_u
+{
+  volatile uint32_t word;
+  struct
+  {
+    volatile uint32_t b00 : 8;
+    volatile uint32_t b01 : 8;
+    volatile uint32_t b02 : 8;
+    volatile uint32_t b03 : 8;
+  };
+};
+
+/* Message RAM Structures ***************************************************/
+
+/* Rx FIFO Element Header -- RM0433 pg 2536 */
+
+union rx_fifo_header_u
+{
+  struct
+  {
+    volatile uint32_t w0;
+    volatile uint32_t w1;
+  };
+
+  struct
+  {
+    /* First word */
+
+    union can_id_u id;
+
+    /* Second word */
+
+    volatile uint32_t rxts : 16; /* Rx timestamp */
+    volatile uint32_t dlc  : 4;  /* Data length code */
+    volatile uint32_t brs  : 1;  /* Bitrate switching */
+    volatile uint32_t fdf  : 1;  /* FD frame */
+    volatile uint32_t res  : 2;  /* Reserved for Tx Event */
+    volatile uint32_t fidx : 7;  /* Filter index */
+    volatile uint32_t anmf : 1;  /* Accepted non-matching frame */
+  };
+};
+
+/* Tx FIFO Element Header -- RM0433 pg 2538 */
+
+union tx_fifo_header_u
+{
+  struct
+  {
+    volatile uint32_t w0;
+    volatile uint32_t w1;
+  };
+
+  struct
+  {
+    /* First word */
+
+    union can_id_u id;
+
+    /* Second word */
+
+    volatile uint32_t res1 : 16; /* Reserved for Tx Event timestamp */
+    volatile uint32_t dlc  : 4;  /* Data length code */
+    volatile uint32_t brs  : 1;  /* Bitrate switching */
+    volatile uint32_t fdf  : 1;  /* FD frame */
+    volatile uint32_t res2 : 1;  /* Reserved for Tx Event */
+    volatile uint32_t efc  : 1;  /* Event FIFO control */
+    volatile uint32_t mm   : 8;  /* Message marker (user data; copied to Tx Event) */
+  };
+};
+
+/* Rx FIFO Element */
+
+struct rx_fifo_s
+{
+  union rx_fifo_header_u header;
+#ifdef CONFIG_NET_CAN_CANFD
+  union payload_u data[16]; /* 64-byte FD payload */
+#else
+  union payload_u data[2];  /* 8-byte Classic payload */
+#endif
+};
+
+/* Tx FIFO Element */
+
+struct tx_fifo_s
+{
+  union tx_fifo_header_u header;
+#ifdef CONFIG_NET_CAN_CANFD
+  union payload_u data[16]; /* 64-byte FD payload */
+#else
+  union payload_u data[2];  /* 8-byte Classic payload */
+#endif
+};
+
+/* Tx Mailbox Status Tracking */
+
+#define TX_ABORT -1
+#define TX_FREE   0
+#define TX_BUSY   1
+
+struct txmbstats
+{
+#ifdef CONFIG_NET_CAN_RAW_TX_DEADLINE
+  struct timeval deadline;
+  struct wdog_s txtimeout;
+#endif
+  int8_t pending;
+};
+
+/* FDCAN Device hardware configuration **************************************/
+
+struct fdcan_config_s
+{
+  uint32_t tx_pin;           /* GPIO configuration for TX */
+  uint32_t rx_pin;           /* GPIO configuration for RX */
+  uint32_t mb_irq[2];        /* FDCAN Interrupt 0, 1 (Rx, Tx) */
+};
+
+struct fdcan_bitseg
+{
+  uint32_t bitrate;
+  uint8_t sjw;
+  uint8_t bs1;
+  uint8_t bs2;
+  uint8_t prescaler;
+};
+
+struct fdcan_message_ram
+{
+  uint32_t filt_stdid_addr;
+  uint32_t filt_extid_addr;
+  uint32_t rxfifo0_addr;
+  uint32_t rxfifo1_addr;
+  uint32_t txfifo_addr;
+  uint8_t n_stdfilt;
+  uint8_t n_extfilt;
+  uint8_t n_rxfifo0;
+  uint8_t n_rxfifo1;
+  uint8_t n_txfifo;
+};
+
+/* FDCAN device structures **************************************************/
+
+#ifdef CONFIG_STM32H7_FDCAN1
+static const struct fdcan_config_s stm32_fdcan0_config =
+{
+  .tx_pin      = GPIO_CAN1_TX,
+  .rx_pin      = GPIO_CAN1_RX,
+  .mb_irq      =
+  {
+    STM32_IRQ_FDCAN1_0,
+    STM32_IRQ_FDCAN1_1,
+  },
+};
+#endif
+
+#ifdef CONFIG_STM32H7_FDCAN2
+static const struct fdcan_config_s stm32_fdcan1_config =
+{
+  .tx_pin      = GPIO_CAN2_TX,
+  .rx_pin      = GPIO_CAN2_RX,
+  .mb_irq      =
+  {
+    STM32_IRQ_FDCAN2_0,
+    STM32_IRQ_FDCAN2_1 ,
+  },
+};
+#endif
+
+#ifdef CONFIG_STM32H7_FDCAN3
+#  error "FDCAN3 support not yet added to stm32h7x3xx header files (pinmap, irq, etc.)"
+static const struct fdcan_config_s stm32_fdcan2_config =
+{
+  .tx_pin      = GPIO_CAN3_TX,
+  .rx_pin      = GPIO_CAN3_RX,
+  .mb_irq      =
+  {
+    STM32_IRQ_FDCAN3_0,
+    STM32_IRQ_FDCAN3_1 ,
+  },
+};
+#endif
+
+/* The fdcan_driver_s encapsulates all state information for a single
+ * hardware interface
+ */
+
+struct fdcan_driver_s
+{
+  const struct fdcan_config_s *config;  /* Pin config */
+  uint8_t iface_idx;                    /* FDCAN interface index (0 or 1) */
+  uint32_t base;                        /* FDCAN base address */
+
+  struct fdcan_bitseg arbi_timing;      /* Timing for arbitration phase */
+#ifdef CONFIG_NET_CAN_CANFD
+  struct fdcan_bitseg data_timing;      /* Timing for data phase */
+#endif
+
+  struct fdcan_message_ram message_ram; /* Start addresses for each reagion of Message RAM */
+  struct rx_fifo_s *rx;                 /* Pointer to Rx FIFO0 in Message RAM */
+  struct tx_fifo_s *tx;                 /* Pointer to Tx mailboxes in Message RAM */
+
+  /* Work queue configs for deferring interrupt and poll work */
+
+  struct work_s irqwork;
+  struct work_s pollwork;
+
+  /* Intermediate storage of Tx / Rx frames outside of Message RAM */
+
+  uint8_t tx_pool[(sizeof(FRAME_TYPE)+MSG_DATA)*POOL_SIZE];
+  uint8_t rx_pool[(sizeof(FRAME_TYPE)+MSG_DATA)*POOL_SIZE];
+
+  struct net_driver_s dev;              /* Interface understood by the network */
+  bool bifup;                           /* true:ifup false:ifdown */
+
+  struct txmbstats txmb[NUM_TX_FIFO];   /* Track deadline and status of every Tx entry */
+};
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+#ifdef CONFIG_STM32H7_FDCAN1
+static struct fdcan_driver_s g_fdcan0;
+#endif
+
+#ifdef CONFIG_STM32H7_FDCAN2
+static struct fdcan_driver_s g_fdcan1;
+#endif
+
+#ifdef CONFIG_STM32H7_FDCAN3
+static struct fdcan_driver_s g_fdcan2;
+#endif
+
+static bool g_apb1h_init = false;
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* Common TX logic */
+
+static bool fdcan_txringfull(FAR struct fdcan_driver_s *priv);
+static int  fdcan_transmit(FAR struct fdcan_driver_s *priv);
+static int  fdcan_txpoll(struct net_driver_s *dev);
+
+/* Helper functions */
+
+#ifdef CONFIG_STM32H7_FDCAN_REGDEBUG
+static void fdcan_dumpregs(FAR struct fdcan_driver_s *priv);
+#endif
+
+int32_t fdcan_bittiming(struct fdcan_bitseg *timing);
+
+static void fdcan_apb1hreset(void);
+static void fdcan_setinit(uint32_t base, uint32_t init);
+static void fdcan_setenable(uint32_t base, uint32_t enable);
+static void fdcan_setconfig(uint32_t base, uint32_t config_enable);
+static bool fdcan_waitccr_change(uint32_t base,
+                                 uint32_t mask,
+                                 uint32_t target_state);
+
+static void fdcan_enable_interrupts(struct fdcan_driver_s *priv);
+static void fdcan_disable_interrupts(struct fdcan_driver_s *priv);
+
+/* Interrupt handling */
+
+static void fdcan_receive(FAR struct fdcan_driver_s *priv);
+static void fdcan_txdone(FAR struct fdcan_driver_s *priv);
+
+static int  fdcan_interrupt(int irq, FAR void *context,
+                            FAR void *arg);
+
+static void fdcan_check_errors_isr(FAR struct fdcan_driver_s *priv);
+
+/* Watchdog timer expirations */
+
+#ifdef TX_TIMEOUT_WQ
+static void fdcan_txtimeout_work(FAR void *arg);
+static void fdcan_txtimeout_expiry(wdparm_t arg);
+#endif
+
+/* NuttX networking stack callback functions */
+
+static int fdcan_ifup(struct net_driver_s *dev);
+static int fdcan_ifdown(struct net_driver_s *dev);
+
+static void fdcan_txavail_work(FAR void *arg);
+static int  fdcan_txavail(struct net_driver_s *dev);
+
+#ifdef CONFIG_NETDEV_IOCTL
+static int  fdcan_netdev_ioctl(struct net_driver_s *dev, int cmd,
+                               unsigned long arg);
+#endif
+
+/* Initialization and Reset */
+
+static int  fdcan_initialize(struct fdcan_driver_s *priv);
+static void fdcan_reset(struct fdcan_driver_s *priv);
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: fdcan_dumpregs
+ *
+ * Dump common register values to the console for debugging purposes.
+ ****************************************************************************/
+
+#ifdef CONFIG_STM32H7_FDCAN_REGDEBUG
+static void fdcan_dumpregs(FAR struct fdcan_driver_s *priv)
+{
+  printf("-------------- FDCAN Reg Dump ----------------\n");
+  printf("CAN%d Base: 0x%lx\n", priv->iface_idx, priv->base);
+
+  uint32_t regval;
+  regval = getreg32(priv->base + STM32_FDCAN_CCCR_OFFSET);
+  printf("CCCR = 0x%lx\n", regval);
+  regval = getreg32(priv->base + STM32_FDCAN_ECR_OFFSET);
+  printf("ECR  = 0x%lx\n", regval);
+
+  regval = getreg32(priv->base + STM32_FDCAN_IR_OFFSET);
+  printf("IR   = 0x%lx\n", regval);
+  regval = getreg32(priv->base + STM32_FDCAN_IE_OFFSET);
+  printf("IE   = 0x%lx\n", regval);
+  regval = getreg32(priv->base + STM32_FDCAN_ILE_OFFSET);
+  printf("ILE = 0x%lx\n", regval);
+  regval = getreg32(priv->base + STM32_FDCAN_ILS_OFFSET);
+  printf("ILS = 0x%lx\n", regval);
+
+  regval = getreg32(priv->base + STM32_FDCAN_NBTP_OFFSET);
+  printf("NBTP = 0x%lx\n", regval);
+  regval = getreg32(priv->base + STM32_FDCAN_DBTP_OFFSET);
+  printf("DBTP = 0x%lx\n", regval);
+
+  regval = getreg32(priv->base + STM32_FDCAN_TXBC_OFFSET);
+  printf("TXBC = 0x%lx\n", regval);
+  regval = getreg32(priv->base + STM32_FDCAN_RXF0C_OFFSET);
+  printf("RXF0C = 0x%lx\n", regval);
+
+  regval = getreg32(priv->base + STM32_FDCAN_TXESC_OFFSET);
+  printf("TXESC = 0x%lx\n", regval);
+  regval = getreg32(priv->base + STM32_FDCAN_RXESC_OFFSET);
+  printf("RXESC = 0x%lx\n", regval);
+}
+#endif
+
+/****************************************************************************
+ * Name: fdcan_bittiming
+ *
+ * Description:
+ *   Convert desired bitrate to FDCAN bit segment values
+ *   The computed values apply to both data and arbitration phases
+ *
+ * Input Parameters:
+ *   timing - structure to store bit timing
+ *
+ * Returned Value:
+ *   OK on success; >0 on failure.
+ ****************************************************************************/
+
+int32_t fdcan_bittiming(struct fdcan_bitseg *timing)
+{
+  /* Implementation ported from PX4's uavcan_drivers/stm32[h7]
+   *
+   * Ref. "Automatic Baudrate Detection in CANopen Networks", U. Koppe
+   *  MicroControl GmbH & Co. KG
+   *  CAN in Automation, 2003
+   *
+   * According to the source, optimal quanta per bit are:
+   *   Bitrate        Optimal Maximum
+   *   1000 kbps      8       10
+   *   500  kbps      16      17
+   *   250  kbps      16      17
+   *   125  kbps      16      17
+   */
+
+  const uint32_t target_bitrate    = timing->bitrate;
+  static const int32_t max_bs1     = 16;
+  static const int32_t max_bs2     = 8;
+  const uint8_t max_quanta_per_bit = (timing->bitrate >= 1000000) ? 10 : 17;
+  static const int max_sp_location = 900;
+
+  /* Computing (prescaler * BS):
+   *   BITRATE = 1 / (PRESCALER * (1 / PCLK) * (1 + BS1 + BS2))
+   *   BITRATE = PCLK / (PRESCALER * (1 + BS1 + BS2))
+   * let:
+   *   BS = 1 + BS1 + BS2
+   *     (BS == total number of time quanta per bit)
+   *   PRESCALER_BS = PRESCALER * BS
+   * ==>
+   *   PRESCALER_BS = PCLK / BITRATE
+   */
+
+  const uint32_t prescaler_bs = CLK_FREQ / target_bitrate;
+
+  /* Find prescaler value such that the number of quanta per bit is highest */
+
+  uint8_t bs1_bs2_sum = max_quanta_per_bit - 1;
+
+  while ((prescaler_bs % (1 + bs1_bs2_sum)) != 0)
+    {
+      if (bs1_bs2_sum <= 2)
+        {
+          nerr("Target bitrate too high - no solution possible.");
+          return 1; /* No solution */
+        }
+
+      bs1_bs2_sum--;
+    }
+
+  const uint32_t prescaler = prescaler_bs / (1 + bs1_bs2_sum);
+
+  if ((prescaler < 1U) || (prescaler > 1024U))
+    {
+      nerr("Target bitrate invalid - bad prescaler.");
+      return 2; /* No solution */
+    }
+
+  /* Now we have a constraint: (BS1 + BS2) == bs1_bs2_sum.
+   * We need to find the values so that the sample point is as close as
+   * possible to the optimal value.
+   *
+   *   Solve[(1 + bs1)/(1 + bs1 + bs2) == 7/8, bs2]
+   *     (Where 7/8 is 0.875, the recommended sample point location)
+   *   {{bs2 -> (1 + bs1)/7}}
+   *
+   * Hence:
+   *   bs2 = (1 + bs1) / 7
+   *   bs1 = (7 * bs1_bs2_sum - 1) / 8
+   *
+   * Sample point location can be computed as follows:
+   *   Sample point location = (1 + bs1) / (1 + bs1 + bs2)
+   *
+   * Since the optimal solution is so close to the maximum, we prepare two
+   * solutions, and then pick the best one:
+   *   - With rounding to nearest
+   *   - With rounding to zero
+   */
+
+  /* First attempt with rounding to nearest */
+
+  uint8_t bs1 = (uint8_t)((7 * bs1_bs2_sum - 1) + 4) / 8;
+  uint8_t bs2 = (uint8_t)(bs1_bs2_sum - bs1);
+  uint16_t sample_point_permill =
+    (uint16_t)(1000 * (1 + bs1) / (1 + bs1 + bs2));
+
+  if (sample_point_permill > max_sp_location)
+    {
+      /* Second attempt with rounding to zero */
+
+      bs1 = (7 * bs1_bs2_sum - 1) / 8;
+      bs2 = bs1_bs2_sum - bs1;
+    }
+
+  bool valid = (bs1 >= 1) && (bs1 <= max_bs1) && (bs2 >= 1) &&
+    (bs2 <= max_bs2);
+
+  /* Final validation
+   * Helpful Python:
+   * def sample_point_from_btr(x):
+   *     assert 0b0011110010000000111111000000000 & x == 0
+   *     ts2,ts1,brp = (x>>20)&7, (x>>16)&15, x&511
+   *     return (1+ts1+1)/(1+ts1+1+ts2+1)
+   */
+
+  if (target_bitrate != (CLK_FREQ / (prescaler * (1 + bs1 + bs2))) || !valid)
+    {
+      nerr("Target bitrate invalid - solution does not match.");
+      return 3; /* Solution not found */
+    }
+
+#ifdef CONFIG_STM32H7_FDCAN_REGDEBUG
+  ninfo("[fdcan] CLK_FREQ %lu, target_bitrate %lu, prescaler %lu, bs1 %d"
+        ", bs2 %d\n", CLK_FREQ, target_bitrate, prescaler_bs, bs1 - 1,
+        bs2 - 1);
+#endif
+
+  timing->bs1 = (uint8_t)(bs1 - 1);
+  timing->bs2 = (uint8_t)(bs2 - 1);
+  timing->prescaler = (uint16_t)(prescaler - 1);
+  timing->sjw = 0; /* Which means one */
+
+  return 0;
+}
+
+/****************************************************************************
+ * Function: fdcan_txringfull
+ *
+ * Description:
+ *   Check if all of the TX descriptors are in use.
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *
+ * Returned Value:
+ *   true is the TX ring is full; false if there are free slots at the
+ *   head index.
+ *
+ ****************************************************************************/
+
+static bool fdcan_txringfull(FAR struct fdcan_driver_s *priv)
+{
+  /* TODO: Decide if this needs to be checked every time, or just during init
+   * Check that we even _have_ a Tx FIFO allocated
+   */
+
+  uint32_t regval = getreg32(priv->base + STM32_FDCAN_TXBC_OFFSET);
+  if ((regval & FDCAN_TXBC_TFQS) == 0)
+    {
+      nerr("No Tx FIFO buffers assigned?  Check your message RAM config\n");
+      return true;
+    }
+
+  /* Check if the Tx queue is full */
+
+  regval = getreg32(priv->base + STM32_FDCAN_TXFQS_OFFSET);
+  if ((regval & FDCAN_TXFQS_TFQF) == FDCAN_TXFQS_TFQF)
+    {
+      return true; /* Sorry, out of room, try back later */
+    }
+
+  return false;
+}
+
+/****************************************************************************
+ * Function: fdcan_transmit
+ *
+ * Description:
+ *   Start hardware transmission of the data contained in priv->d_buf. Called
+ *   either from the txdone interrupt handling or from watchdog based polling
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *
+ * Returned Value:
+ *   OK on success; a negated errno on failure
+ *
+ * Assumptions:
+ *   May or may not be called from an interrupt handler.  In either case,
+ *   global interrupts are disabled, either explicitly or indirectly through
+ *   interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static int fdcan_transmit(FAR struct fdcan_driver_s *priv)
+{
+  irqstate_t flags = enter_critical_section();
+
+  /* First, check if there are any slots available in the queue */
+
+  uint32_t regval = getreg32(priv->base + STM32_FDCAN_TXFQS_OFFSET);
+  if ((regval & FDCAN_TXFQS_TFQF) == FDCAN_TXFQS_TFQF)
+    {
+      /* Tx FIFO / Queue is full */
+
+      leave_critical_section(flags);
+      return -EBUSY;
+    }
+
+  /* Next, get the next available FIFO index from the controller */
+
+  regval = getreg32(priv->base + STM32_FDCAN_TXFQS_OFFSET);
+  const uint8_t mbi = (regval & FDCAN_TXFQS_TFQPI) >>
+                       FDCAN_TXFQS_TFQPI_SHIFT;
+
+  /* Now, we can copy the CAN frame to the FIFO (in message RAM) */
+
+  if (mbi >= NUM_TX_FIFO)
+    {
+      nerr("Invalid Tx mailbox index encountered in transmit\n");
+      leave_critical_section(flags);
+      return -EIO;
+    }
+
+  struct tx_fifo_s *mb = &priv->tx[mbi];
+
+  /* Setup timeout deadline if enabled */
+
+#ifdef CONFIG_NET_CAN_RAW_TX_DEADLINE
+  int32_t timeout = 0;
+  struct timespec ts;
+  clock_systime_timespec(&ts);
+
+  if (priv->dev.d_sndlen > priv->dev.d_len)
+    {
+      /* Tx deadline is stored in d_buf after frame data */
+
+      struct timeval *tv =
+             (struct timeval *)(priv->dev.d_buf + priv->dev.d_len);
+      priv->txmb[mbi].deadline = *tv;
+      timeout  = (tv->tv_sec - ts.tv_sec)*CLK_TCK
+                 + ((tv->tv_usec - ts.tv_nsec / 1000)*CLK_TCK) / 1000000;
+      if (timeout < 0)
+        {
+          leave_critical_section(flags);
+          return 0;  /* No transmission for you! */
+        }
+    }
+  else
+    {
+      /* Default TX deadline defined in NET_CAN_RAW_DEFAULT_TX_DEADLINE */
+
+      if (CONFIG_NET_CAN_RAW_DEFAULT_TX_DEADLINE > 0)
+        {
+          timeout = ((CONFIG_NET_CAN_RAW_DEFAULT_TX_DEADLINE / 1000000)
+              *CLK_TCK);
+          priv->txmb[mbi].deadline.tv_sec = ts.tv_sec +
+              CONFIG_NET_CAN_RAW_DEFAULT_TX_DEADLINE / 1000000;
+          priv->txmb[mbi].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;
+        }
+    }
+#endif
+
+  /* Attempt to write frame */
+
+  union tx_fifo_header_u header;
+
+  if (priv->dev.d_len == sizeof(struct can_frame))
+    {
+      struct can_frame *frame = (struct can_frame *)priv->dev.d_buf;
+
+      if (frame->can_id & CAN_EXT_FLAG)
+        {
+          header.id.xtd = 1;
+          header.id.extid = frame->can_id & CAN_EXT_MASK;
+        }
+      else
+        {
+          header.id.xtd = 0;
+          header.id.stdid = frame->can_id & CAN_STD_MASK;
+        }
+
+      header.id.esi = frame->can_id & CAN_ERR_FLAG ? 1 : 0;
+      header.id.rtr = frame->can_id & CAN_RTR_FLAG ? 1 : 0;
+      header.dlc = frame->can_dlc;
+      header.brs = 0;  /* No bitrate switching */
+      header.fdf = 0;  /* Classic CAN frame, not CAN-FD */
+      header.efc = 0;  /* Don't store Tx events */
+      header.mm = mbi; /* Mailbox Marker for our own use; just store FIFO index */
+
+      /* Store into message RAM */
+
+      mb->header.w0 = header.w0;
+      mb->header.w1 = header.w1;
+      mb->data[0].word = *(uint32_t *)&frame->data[0];
+      mb->data[1].word = *(uint32_t *)&frame->data[4];
+    }
+#ifdef CONFIG_NET_CAN_CANFD
+  else /* CAN FD frame */
+    {
+      struct canfd_frame *frame = (struct canfd_frame *)priv->dev.d_buf;
+
+      if (frame->can_id & CAN_EXT_FLAG)
+        {
+          header.id.xtd = 1;
+          header.id.extid = frame->can_id & CAN_EXT_MASK;
+        }
+      else
+        {
+          header.id.xtd = 0;
+          header.id.stdid = frame->can_id & CAN_STD_MASK;
+        }
+
+      const bool brs =
+        (priv->arbi_timing.bitrate == priv->data_timing.bitrate) ? 0 : 1;
+
+      header.id.esi = (frame->can_id & CAN_ERR_FLAG) ? 1 : 0;
+      header.id.rtr = (frame->can_id & CAN_RTR_FLAG) ? 1 : 0;
+      header.dlc = len_to_can_dlc[frame->len];
+      header.brs = brs; /* Bitrate switching */
+      header.fdf = 1;   /* CAN-FD frame */
+      header.efc = 0;   /* Don't store Tx events */
+      header.mm = mbi;  /* Mailbox Marker for our own use; just store FIFO index */
+
+      /* Store into message RAM */
+
+      mb->header.w1 = header.w1;
+      mb->header.w0 = header.w0;
+
+      uint32_t *frame_data_word = (uint32_t *)&frame->data[0];
+
+      for (int i = 0; i < (frame->len + 4 - 1) / 4; i++)
+        {
+          mb->data[i].word = frame_data_word[i];
+        }
+    }
+#endif
+
+  /* GO - Submit the transmission request for this element */
+
+  putreg32(1 << mbi, priv->base + STM32_FDCAN_TXBAR_OFFSET);
+
+  /* Increment statistics */
+
+  NETDEV_TXPACKETS(&priv->dev);
+
+  priv->txmb[mbi].pending = TX_BUSY;
+
+#ifdef TX_TIMEOUT_WQ
+  /* Setup the TX timeout watchdog (perhaps restarting the timer) */
+
+  if (timeout > 0)
+    {
+      wd_start(&priv->txmb[mbi].txtimeout, timeout + 1,
+               fdcan_txtimeout_expiry, (wdparm_t)priv);
+    }
+#endif
+
+  leave_critical_section(flags);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: fdcan_txpoll
+ *
+ * Description:
+ *   The transmitter is available, check if the network has any outgoing
+ *   packets ready to send.  This is a callback from devif_poll().
+ *   devif_poll() may be called:
+ *
+ *   1. When the preceding TX packet send is complete,
+ *   2. When the preceding TX packet send timesout and the interface is reset
+ *   3. During normal TX polling
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   OK on success; a negated errno on failure
+ *
+ * Assumptions:
+ *   May or may not be called from an interrupt handler.  In either case,
+ *   global interrupts are disabled, either explicitly or indirectly through
+ *   interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static int fdcan_txpoll(struct net_driver_s *dev)
+{
+  FAR struct fdcan_driver_s *priv =
+    (FAR struct fdcan_driver_s *)dev->d_private;
+
+  /* If the polling resulted in data that should be sent out on the network,
+   * the field d_len is set to a value > 0.
+   */
+
+  if (priv->dev.d_len > 0)
+    {
+      if (!devif_loopback(&priv->dev))
+        {
+          /* Send the packet */
+
+          fdcan_transmit(priv);
+
+          /* Check if there is room in the device to hold another packet. If
+           * not, return a non-zero value to terminate the poll.
+           */
+
+          if (fdcan_txringfull(priv))
+            {
+              return -EBUSY;
+            }
+        }
+    }
+
+  /* If zero is returned, the polling will continue until all connections
+   * have been examined.
+   */
+
+  return 0;
+}
+
+/****************************************************************************
+ * Function: fdcan_receive
+ *
+ * Description:
+ *   An interrupt was received indicating the availability of a new RX packet
+ *
+ * Input Parameters:
+ *   priv  - Reference to the driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Global interrupts are disabled by interrupt handling logic.
+ *
+ ****************************************************************************/
+
+static void fdcan_receive(FAR struct fdcan_driver_s *priv)
+{
+  uint32_t regval = getreg32(priv->base + STM32_FDCAN_IR_OFFSET);
+
+  const uint32_t ir_fifo0 = FDCAN_IR_RF0N | FDCAN_IR_RF0F;
+  const uint32_t ir_fifo1 = FDCAN_IR_RF1N | FDCAN_IR_RF1F;
+  uint8_t fifo_id;
+
+  if (regval & ir_fifo0)
+    {
+      regval = ir_fifo0;
+      fifo_id = 0;
+    }
+  else if (regval & ir_fifo1)
+    {
+      regval = ir_fifo1;
+      fifo_id = 1;
+    }
+  else
+    {
+      nerr("ERROR: Bad RX IR flags");
+      return;
+    }
+
+  /* Write the corresponding interrupt bits to reset them */
+
+  putreg32(regval, priv->base + STM32_FDCAN_IR_OFFSET);
+
+  /* Bitwise register definitions are the same for FIFO 0/1
+   *
+   *   FDCAN_RXFnC_F0S:   Rx FIFO Size
+   *   FDCAN_RXFnS_RF0L:  Rx Message Lost
+   *   FDCAN_RXFnS_F0FL:  Rx FIFO Fill Level
+   *   FDCAN_RXFnS_F0GI:  Rx FIFO Get Index
+   *
+   * So we will use only the RX FIFO0 register definitions for simplicity
+   */
+
+  uint32_t offset_rxfnc = (fifo_id == 0) ? STM32_FDCAN_RXF0C_OFFSET
+                                         : STM32_FDCAN_RXF1C_OFFSET;
+  uint32_t offset_rxfns = (fifo_id == 0) ? STM32_FDCAN_RXF0S_OFFSET
+                                         : STM32_FDCAN_RXF1S_OFFSET;
+  uint32_t offset_rxfna = (fifo_id == 0) ? STM32_FDCAN_RXF0A_OFFSET
+                                         : STM32_FDCAN_RXF1A_OFFSET;
+
+  volatile uint32_t *const rxfnc = (uint32_t *)(priv->base + offset_rxfnc);
+  volatile uint32_t *const rxfns = (uint32_t *)(priv->base + offset_rxfns);
+  volatile uint32_t *const rxfna = (uint32_t *)(priv->base + offset_rxfna);
+
+  /* Check number of elements in message RAM allocated to this FIFO */
+
+  if ((*rxfnc & FDCAN_RXF0C_F0S) == 0)
+    {
+      nerr("ERROR: No RX FIFO elements allocated");
+      return;
+    }
+
+  /* Check for message lost; count an error */
+
+  if ((*rxfns & FDCAN_RXF0S_RF0L) != 0)
+    {
+      NETDEV_RXERRORS(&priv->dev);
+    }
+
+  /* Check number of elements available (fill level) */
+
+  const uint8_t n_elem = (*rxfns & FDCAN_RXF0S_F0FL);
+
+  if (n_elem == 0)
+    {
+      nerr("RX interrupt but 0 frames available");
+      return;
+    }
+
+  struct rx_fifo_s *rf = NULL;
+
+  while ((*rxfns & FDCAN_RXF0S_F0FL) > 0)
+    {
+      /* Copy the frame from message RAM */
+
+      const uint8_t index = (*rxfns & FDCAN_RXF0S_F0GI) >>
+                             FDCAN_RXF0S_F0GI_SHIFT;
+
+      rf = &priv->rx[index];
+
+      /* Read the frame contents */
+
+#ifdef CONFIG_NET_CAN_CANFD
+      if (rf->header.fdf) /* CAN FD frame */
+        {
+          struct canfd_frame *frame = (struct canfd_frame *)priv->rx_pool;
+
+          if (rf->header.id.xtd)
+            {
+              frame->can_id  = CAN_EXT_MASK & rf->header.id.extid;
+              frame->can_id |= CAN_EXT_FLAG;
+            }
+          else
+            {
+              frame->can_id = CAN_STD_MASK & rf->header.id.stdid;
+            }
+
+          if (rf->header.id.rtr)
+            {
+              frame->can_id |= CAN_RTR_FLAG;
+            }
+
+          frame->len = can_dlc_to_len[rf->header.dlc];
+
+          uint32_t *frame_data_word = (uint32_t *)&frame->data[0];
+
+          for (int i = 0; i < (frame->len + 4 - 1) / 4; i++)
+            {
+              frame_data_word[i] = rf->data[i].word;
+            }
+
+          /* Acknowledge receipt of this FIFO element */
+
+          putreg32(index, rxfna);
+
+          /* Copy the buffer pointer to priv->dev
+           * Set amount of data in priv->dev.d_len
+           */
+
+          priv->dev.d_len = sizeof(struct canfd_frame);
+          priv->dev.d_buf = (uint8_t *)frame;
+        }
+      else /* CAN 2.0 Frame */
+#endif
+        {
+          struct can_frame *frame = (struct can_frame *)priv->rx_pool;
+
+          if (rf->header.id.xtd)
+            {
+              frame->can_id  = CAN_EXT_MASK & rf->header.id.extid;
+              frame->can_id |= CAN_EXT_FLAG;
+            }
+          else
+            {
+              frame->can_id = CAN_STD_MASK & rf->header.id.stdid;
+            }
+
+          if (rf->header.id.rtr)
+            {
+              frame->can_id |= CAN_RTR_FLAG;
+            }
+
+          frame->can_dlc = rf->header.dlc;
+
+          *(uint32_t *)&frame->data[0] = rf->data[0].word;
+          *(uint32_t *)&frame->data[4] = rf->data[1].word;
+
+          /* Acknowledge receipt of this FIFO element */
+
+          putreg32(index, rxfna);
+
+          /* Copy the buffer pointer to priv->dev
+           * Set amount of data in priv->dev.d_len
+           */
+
+          priv->dev.d_len = sizeof(struct can_frame);
+          priv->dev.d_buf = (uint8_t *)frame;
+        }
+
+      /* Send to socket interface */
+
+      can_input(&priv->dev);

Review Comment:
   It was mostly related to dropped CAN frames since the delay of the workqueue was so high that all my RX mailboxes were full before the workqueue logic could even empty them.
   
   Maybe this was regression in the workqueue logic, since this was tested with low system load and different priorities for the workqueue, @JacobCrabill might re-asses this.
   
   As alternative I was thinking to do a kthread just like they did for the Tiva CAN driver
   https://github.com/apache/incubator-nuttx/blob/7f80d4ded69dd7da752d3302c1dbd5c246392653/arch/arm/src/tiva/common/tiva_can.c#L410-L414
   
   But I didn't have time yet to test it and assess the peformance.
   
   



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org