You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nuttx.apache.org by xi...@apache.org on 2022/02/06 09:09:20 UTC

[incubator-nuttx] 01/03: arch/stm32: add FDCAN SocketCAN support

This is an automated email from the ASF dual-hosted git repository.

xiaoxiang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-nuttx.git

commit 341bbe38d5b15d2aaad83a89108bd6285b7543aa
Author: raiden00pl <ra...@railab.me>
AuthorDate: Sat Jan 22 15:10:27 2022 +0100

    arch/stm32: add FDCAN SocketCAN support
---
 arch/arm/src/stm32/Kconfig            |   12 +-
 arch/arm/src/stm32/Make.defs          |    3 +
 arch/arm/src/stm32/stm32_fdcan.h      |   22 +
 arch/arm/src/stm32/stm32_fdcan_sock.c | 3196 +++++++++++++++++++++++++++++++++
 4 files changed, 3226 insertions(+), 7 deletions(-)

diff --git a/arch/arm/src/stm32/Kconfig b/arch/arm/src/stm32/Kconfig
index 6ba361f..c8d8c03 100644
--- a/arch/arm/src/stm32/Kconfig
+++ b/arch/arm/src/stm32/Kconfig
@@ -2769,14 +2769,12 @@ config STM32_BKPSRAM
 config STM32_CAN1
 	bool "CAN1"
 	default n
-	select CAN
 	select STM32_CAN
 	depends on STM32_HAVE_CAN1
 
 config STM32_CAN2
 	bool "CAN2"
 	default n
-	select CAN
 	select STM32_CAN
 	depends on STM32_HAVE_CAN2
 
@@ -3391,11 +3389,9 @@ config STM32_I2C
 
 config STM32_CAN
 	bool
-	select ARCH_HAVE_CAN_ERRORS
 
 config STM32_FDCAN
 	bool
-	select ARCH_HAVE_CAN_ERRORS
 
 config STM32_TIM
 	bool
@@ -10863,6 +10859,7 @@ choice
 config STM32_CAN_CHARDRIVER
 	bool "STM32 CAN character driver support"
 	select ARCH_HAVE_CAN_ERRORS
+	select CAN
 
 config STM32_CAN_SOCKET
 	bool "STM32 CAN SocketCAN support"
@@ -10915,11 +10912,12 @@ choice
 
 config STM32_FDCAN_CHARDRIVER
 	bool "STM32 FDCAN character driver support"
-	select ARCH_HAVE_FDCAN_ERRORS
+	select ARCH_HAVE_CAN_ERRORS
+	select CAN
 
 config STM32_FDCAN_SOCKET
-	bool "STM32 FDCAN SocketCAN support (not supported yet)"
-	select NET_FDCAN_HAVE_ERRORS
+	bool "STM32 FDCAN SocketCAN support"
+	select NET_CAN_HAVE_ERRORS
 
 endchoice # FDCAN character driver or SocketCAN support
 
diff --git a/arch/arm/src/stm32/Make.defs b/arch/arm/src/stm32/Make.defs
index 3328db8..e484536 100644
--- a/arch/arm/src/stm32/Make.defs
+++ b/arch/arm/src/stm32/Make.defs
@@ -249,6 +249,9 @@ ifeq ($(CONFIG_STM32_FDCAN),y)
 ifeq ($(CONFIG_STM32_FDCAN_CHARDRIVER),y)
 CHIP_CSRCS += stm32_fdcan.c
 endif
+ifeq ($(CONFIG_STM32_FDCAN_SOCKET),y)
+CHIP_CSRCS += stm32_fdcan_sock.c
+endif
 endif
 
 ifeq ($(CONFIG_STM32_IWDG),y)
diff --git a/arch/arm/src/stm32/stm32_fdcan.h b/arch/arm/src/stm32/stm32_fdcan.h
index 34d2794..e895e2b 100644
--- a/arch/arm/src/stm32/stm32_fdcan.h
+++ b/arch/arm/src/stm32/stm32_fdcan.h
@@ -65,6 +65,8 @@ extern "C"
  * Public Function Prototypes
  ****************************************************************************/
 
+#ifdef CONFIG_STM32_FDCAN_CHARDRIVER
+
 /****************************************************************************
  * Name: stm32_fdcaninitialize
  *
@@ -80,6 +82,26 @@ extern "C"
  ****************************************************************************/
 
 FAR struct can_dev_s *stm32_fdcaninitialize(int port);
+#endif
+
+#ifdef CONFIG_STM32_FDCAN_SOCKET
+
+/****************************************************************************
+ * Name: stm32_fdcansockinitialize
+ *
+ * Description:
+ *   Initialize the selected FDCAN port as SocketCAN interface
+ *
+ * Input Parameters:
+ *   Port number (for hardware that has multiple FDCAN interfaces)
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+int stm32_fdcansockinitialize(int port);
+#endif
 
 #undef EXTERN
 #if defined(__cplusplus)
diff --git a/arch/arm/src/stm32/stm32_fdcan_sock.c b/arch/arm/src/stm32/stm32_fdcan_sock.c
new file mode 100644
index 0000000..fafecbe
--- /dev/null
+++ b/arch/arm/src/stm32/stm32_fdcan_sock.c
@@ -0,0 +1,3196 @@
+/****************************************************************************
+ * arch/arm/src/stm32/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
+ *s
+ * 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 <inttypes.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <assert.h>
+#include <errno.h>
+#include <debug.h>
+
+#include <arch/board/board.h>
+#include <nuttx/irq.h>
+#include <nuttx/arch.h>
+
+#include <nuttx/wqueue.h>
+#include <nuttx/can.h>
+#include <nuttx/net/netdev.h>
+#include <nuttx/net/can.h>
+#include <netpacket/can.h>
+
+#include "arm_internal.h"
+#include "arm_arch.h"
+
+#include "stm32_fdcan.h"
+#include "hardware/stm32_pinmap.h"
+#include "stm32_gpio.h"
+#include "stm32_rcc.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Pool configuration *******************************************************/
+
+#define POOL_SIZE  (1)
+
+/* Work queue support is required. */
+
+#if !defined(CONFIG_SCHED_WORKQUEUE)
+#  error Work queue support is required
+#endif
+
+/* The low priority work queue is preferred.  If it is not enabled, LPWORK
+ * will be the same as HPWORK.
+ *
+ * NOTE:  However, the network should NEVER run on the high priority work
+ * queue!  That queue is intended only to service short back end interrupt
+ * processing that never suspends.  Suspending the high priority work queue
+ * may bring the system to its knees!
+ */
+
+#define CANWORK LPWORK
+
+/* Clock source *************************************************************/
+
+#define FDCANCLK_PDIV              (0)
+
+#if FDCANCLK_PDIV == 0
+#  define STM32_FDCANCLK_FREQUENCY (STM32_FDCAN_FREQUENCY / (1))
+#else
+#  define STM32_FDCANCLK_FREQUENCY (STM32_FDCAN_FREQUENCY / (2 * FDCANCLK_PDIV))
+#endif
+
+/* General Configuration ****************************************************/
+
+#if defined(CONFIG_STM32_STM32G4XXX)
+
+/* FDCAN Message RAM */
+
+#  define FDCAN_MSGRAM_WORDS         (212)
+#  define STM32_CANRAM1_BASE         (STM32_CANRAM_BASE + 0x0000)
+#  define STM32_CANRAM2_BASE         (STM32_CANRAM_BASE + 1*(FDCAN_MSGRAM_WORDS * 4) + 4)
+#  define STM32_CANRAM3_BASE         (STM32_CANRAM_BASE + 2*(FDCAN_MSGRAM_WORDS * 4) + 4)
+
+#  ifdef CONFIG_STM32_FDCAN1
+#    define FDCAN1_STDFILTER_SIZE    (28)
+#    define FDCAN1_EXTFILTER_SIZE    (8)
+#    define FDCAN1_RXFIFO0_SIZE      (3)
+#    define FDCAN1_RXFIFO1_SIZE      (3)
+#    define FDCAN1_TXEVENTFIFO_SIZE  (3)
+#    define FDCAN1_TXFIFIOQ_SIZE     (3)
+
+#    define FDCAN1_STDFILTER_WORDS   (28)
+#    define FDCAN1_EXTFILTER_WORDS   (16)
+#    define FDCAN1_RXFIFO0_WORDS     (54)
+#    define FDCAN1_RXFIFO1_WORDS     (54)
+#    define FDCAN1_TXEVENTFIFO_WORDS (6)
+#    define FDCAN1_TXFIFIOQ_WORDS    (54)
+#  endif
+#  ifdef CONFIG_STM32_FDCAN2
+#    define FDCAN2_STDFILTER_SIZE    (28)
+#    define FDCAN2_EXTFILTER_SIZE    (8)
+#    define FDCAN2_RXFIFO0_SIZE      (3)
+#    define FDCAN2_RXFIFO1_SIZE      (3)
+#    define FDCAN2_TXEVENTFIFO_SIZE  (3)
+#    define FDCAN2_TXFIFIOQ_SIZE     (3)
+
+#    define FDCAN2_STDFILTER_WORDS   (28)
+#    define FDCAN2_EXTFILTER_WORDS   (16)
+#    define FDCAN2_RXFIFO0_WORDS     (54)
+#    define FDCAN2_RXFIFO1_WORDS     (54)
+#    define FDCAN2_TXEVENTFIFO_WORDS (6)
+#    define FDCAN2_TXFIFIOQ_WORDS    (54)
+#  endif
+#  ifdef CONFIG_STM32_FDCAN3
+#    define FDCAN3_STDFILTER_SIZE    (28)
+#    define FDCAN3_EXTFILTER_SIZE    (8)
+#    define FDCAN3_RXFIFO0_SIZE      (3)
+#    define FDCAN3_RXFIFO1_SIZE      (3)
+#    define FDCAN3_TXEVENTFIFO_SIZE  (3)
+#    define FDCAN3_TXFIFIOQ_SIZE     (3)
+
+#    define FDCAN3_STDFILTER_WORDS   (28)
+#    define FDCAN3_EXTFILTER_WORDS   (16)
+#    define FDCAN3_RXFIFO0_WORDS     (54)
+#    define FDCAN3_RXFIFO1_WORDS     (54)
+#    define FDCAN3_TXEVENTFIFO_WORDS (6)
+#    define FDCAN3_TXFIFIOQ_WORDS    (54)
+#  endif
+#else
+#  error
+#endif
+
+/* FDCAN1 Configuration *****************************************************/
+
+#ifdef CONFIG_STM32_FDCAN1
+
+/* Bit timing */
+
+#  define FDCAN1_NTSEG1  (CONFIG_STM32_FDCAN1_NTSEG1 - 1)
+#  define FDCAN1_NTSEG2  (CONFIG_STM32_FDCAN1_NTSEG2 - 1)
+#  define FDCAN1_NBRP    ((STM32_FDCANCLK_FREQUENCY /             \
+                           ((FDCAN1_NTSEG1 + FDCAN1_NTSEG2 + 3) * \
+                            CONFIG_STM32_FDCAN1_BITRATE)) - 1)
+#  define FDCAN1_NSJW    (CONFIG_STM32_FDCAN1_NSJW - 1)
+
+#  if FDCAN1_NTSEG1 > FDCAN_NBTP_NTSEG1_MAX
+#    error Invalid FDCAN1 NTSEG1
+#  endif
+#  if FDCAN1_NTSEG2 > FDCAN_NBTP_NTSEG2_MAX
+#    error Invalid FDCAN1 NTSEG2
+#  endif
+#  if FDCAN1_NSJW > FDCAN_NBTP_NSJW_MAX
+#    error Invalid FDCAN1 NSJW
+#  endif
+#  if FDCAN1_NBRP > FDCAN_NBTP_NBRP_MAX
+#    error Invalid FDCAN1 NBRP
+#  endif
+
+#  ifdef CONFIG_STM32_FDCAN1_FD_BRS
+#    define FDCAN1_DTSEG1 (CONFIG_STM32_FDCAN1_DTSEG1 - 1)
+#    define FDCAN1_DTSEG2 (CONFIG_STM32_FDCAN1_DTSEG2 - 1)
+#    define FDCAN1_DBRP   ((STM32_FDCANCLK_FREQUENCY /             \
+                            ((FDCAN1_DTSEG1 + FDCAN1_DTSEG2 + 3) * \
+                             CONFIG_STM32_FDCAN1_DBITRATE)) - 1)
+#    define FDCAN1_DSJW   (CONFIG_STM32_FDCAN1_DSJW - 1)
+#  else
+#    define FDCAN1_DTSEG1 1
+#    define FDCAN1_DTSEG2 1
+#    define FDCAN1_DBRP   1
+#    define FDCAN1_DSJW   1
+#  endif /* CONFIG_STM32_FDCAN1_FD_BRS */
+
+#  if FDCAN1_DTSEG1 > FDCAN_DBTP_DTSEG1_MAX
+#    error Invalid FDCAN1 DTSEG1
+#  endif
+#  if FDCAN1_DTSEG2 > FDCAN_DBTP_DTSEG2_MAX
+#    error Invalid FDCAN1 DTSEG2
+#  endif
+#  if FDCAN1_DBRP > FDCAN_DBTP_DBRP_MAX
+#    error Invalid FDCAN1 DBRP
+#  endif
+#  if FDCAN1_DSJW > FDCAN_DBTP_DSJW_MAX
+#    error Invalid FDCAN1 DSJW
+#  endif
+
+/* FDCAN1 Message RAM Configuration *****************************************/
+
+/* FDCAN1 Message RAM Layout */
+
+#  define FDCAN1_STDFILTER_INDEX   0
+#  define FDCAN1_EXTFILTERS_INDEX  (FDCAN1_STDFILTER_INDEX + FDCAN1_STDFILTER_WORDS)
+#  define FDCAN1_RXFIFO0_INDEX     (FDCAN1_EXTFILTERS_INDEX + FDCAN1_EXTFILTER_WORDS)
+#  define FDCAN1_RXFIFO1_INDEX     (FDCAN1_RXFIFO0_INDEX + FDCAN1_RXFIFO0_WORDS)
+#  define FDCAN1_TXEVENTFIFO_INDEX (FDCAN1_RXFIFO1_INDEX + FDCAN1_RXFIFO1_WORDS)
+#  define FDCAN1_TXFIFOQ_INDEX     (FDCAN1_TXEVENTFIFO_INDEX + FDCAN1_TXEVENTFIFO_WORDS)
+#  define FDCAN1_MSGRAM_WORDS      (FDCAN1_TXFIFOQ_INDEX + FDCAN1_TXFIFIOQ_WORDS)
+
+#endif /* CONFIG_STM32_FDCAN1 */
+
+/* FDCAN2 Configuration *****************************************************/
+
+#ifdef CONFIG_STM32_FDCAN2
+
+/* Bit timing */
+
+#  define FDCAN2_NTSEG1  (CONFIG_STM32_FDCAN2_NTSEG1 - 1)
+#  define FDCAN2_NTSEG2  (CONFIG_STM32_FDCAN2_NTSEG2 - 1)
+#  define FDCAN2_NBRP    (((STM32_FDCANCLK_FREQUENCY /              \
+                            ((FDCAN2_NTSEG1 + FDCAN2_NTSEG2 + 3) *  \
+                             CONFIG_STM32_FDCAN2_BITRATE)) - 1))
+#  define FDCAN2_NSJW    (CONFIG_STM32_FDCAN2_NSJW - 1)
+
+#  if FDCAN2_NTSEG1 > FDCAN_NBTP_NTSEG1_MAX
+#    error Invalid FDCAN2 NTSEG1
+#  endif
+#  if FDCAN2_NTSEG2 > FDCAN_NBTP_NTSEG2_MAX
+#    error Invalid FDCAN2 NTSEG2
+#  endif
+#  if FDCAN2_NSJW > FDCAN_NBTP_NSJW_MAX
+#    error Invalid FDCAN2 NSJW
+#  endif
+#  if FDCAN2_NBRP > FDCAN_NBTP_NBRP_MAX
+#    error Invalid FDCAN1 NBRP
+#  endif
+
+#  ifdef CONFIG_STM32_FDCAN2_FD_BRS
+#    define FDCAN2_DTSEG1 (CONFIG_STM32_FDCAN2_DTSEG1 - 1)
+#    define FDCAN2_DTSEG2 (CONFIG_STM32_FDCAN2_DTSEG2 - 1)
+#    define FDCAN2_DBRP   (((STM32_FDCANCLK_FREQUENCY /                 \
+                             ((FDCAN2_DTSEG1 + FDCAN2_DTSEG2 + 3) *     \
+                              CONFIG_STM32_FDCAN2_DBITRATE)) - 1))
+#    define FDCAN2_DSJW   (CONFIG_STM32_FDCAN2_DSJW - 1)
+#  else
+#    define FDCAN2_DTSEG1 1
+#    define FDCAN2_DTSEG2 1
+#    define FDCAN2_DBRP   1
+#    define FDCAN2_DSJW   1
+#  endif /* CONFIG_STM32_FDCAN2_FD_BRS */
+
+#  if FDCAN2_DTSEG1 > FDCAN_DBTP_DTSEG1_MAX
+#    error Invalid FDCAN2 DTSEG1
+#  endif
+#  if FDCAN2_DTSEG2 > FDCAN_DBTP_DTSEG2_MAX
+#    error Invalid FDCAN2 DTSEG2
+#  endif
+#  if FDCAN2_DBRP > FDCAN_DBTP_DBRP_MAX
+#    error Invalid FDCAN2 DBRP
+#  endif
+#  if FDCAN2_DSJW > FDCAN_DBTP_DSJW_MAX
+#    error Invalid FDCAN2 DSJW
+#  endif
+
+/* FDCAN2 Message RAM Configuration *****************************************/
+
+/* FDCAN2 Message RAM Layout */
+
+#  define FDCAN2_STDFILTER_INDEX   0
+#  define FDCAN2_EXTFILTERS_INDEX  (FDCAN2_STDFILTER_INDEX + FDCAN2_STDFILTER_WORDS)
+#  define FDCAN2_RXFIFO0_INDEX     (FDCAN2_EXTFILTERS_INDEX + FDCAN2_EXTFILTER_WORDS)
+#  define FDCAN2_RXFIFO1_INDEX     (FDCAN2_RXFIFO0_INDEX + FDCAN2_RXFIFO0_WORDS)
+#  define FDCAN2_TXEVENTFIFO_INDEX (FDCAN2_RXFIFO1_INDEX + FDCAN2_RXFIFO1_WORDS)
+#  define FDCAN2_TXFIFOQ_INDEX     (FDCAN2_TXEVENTFIFO_INDEX + FDCAN2_TXEVENTFIFO_WORDS)
+#  define FDCAN2_MSGRAM_WORDS      (FDCAN2_TXFIFOQ_INDEX + FDCAN2_TXFIFIOQ_WORDS)
+
+#endif /* CONFIG_STM32_FDCAN2 */
+
+/* FDCAN3 Configuration *****************************************************/
+
+#ifdef CONFIG_STM32_FDCAN3
+
+/* Bit timing */
+
+#  define FDCAN3_NTSEG1  (CONFIG_STM32_FDCAN3_NTSEG1 - 1)
+#  define FDCAN3_NTSEG2  (CONFIG_STM32_FDCAN3_NTSEG2 - 1)
+#  define FDCAN3_NBRP    (((STM32_FDCANCLK_FREQUENCY /              \
+                            ((FDCAN3_NTSEG1 + FDCAN3_NTSEG2 + 3) *  \
+                             CONFIG_STM32_FDCAN3_BITRATE)) - 1))
+#  define FDCAN3_NSJW    (CONFIG_STM32_FDCAN3_NSJW - 1)
+
+#  if FDCAN3_NTSEG1 > FDCAN_NBTP_NTSEG1_MAX
+#    error Invalid FDCAN3 NTSEG1
+#  endif
+#  if FDCAN3_NTSEG2 > FDCAN_NBTP_NTSEG2_MAX
+#    error Invalid FDCAN3 NTSEG2
+#  endif
+#  if FDCAN3_NSJW > FDCAN_NBTP_NSJW_MAX
+#    error Invalid FDCAN3 NSJW
+#  endif
+#  if FDCAN3_NBRP > FDCAN_NBTP_NBRP_MAX
+#    error Invalid FDCAN1 NBRP
+#  endif
+
+#  ifdef CONFIG_STM32_FDCAN3_FD_BRS
+#    define FDCAN3_DTSEG1 (CONFIG_STM32_FDCAN3_DTSEG1 - 1)
+#    define FDCAN3_DTSEG2 (CONFIG_STM32_FDCAN3_DTSEG2 - 1)
+#    define FDCAN3_DBRP   (((STM32_FDCANCLK_FREQUENCY /                 \
+                             ((FDCAN3_DTSEG1 + FDCAN3_DTSEG2 + 3) *     \
+                              CONFIG_STM32_FDCAN3_DBITRATE)) - 1))
+#    define FDCAN3_DSJW   (CONFIG_STM32_FDCAN3_DSJW - 1)
+#  else
+#    define FDCAN3_DTSEG1 1
+#    define FDCAN3_DTSEG2 1
+#    define FDCAN3_DBRP   1
+#    define FDCAN3_DSJW   1
+#  endif /* CONFIG_STM32_FDCAN3_FD_BRS */
+
+#  if FDCAN3_DTSEG1 > FDCAN_DBTP_DTSEG1_MAX
+#    error Invalid FDCAN3 DTSEG1
+#  endif
+#  if FDCAN3_DTSEG2 > FDCAN_DBTP_DTSEG2_MAX
+#    error Invalid FDCAN3 DTSEG2
+#  endif
+#  if FDCAN3_DBRP > FDCAN_DBTP_DBRP_MAX
+#    error Invalid FDCAN3 DBRP
+#  endif
+#  if FDCAN3_DSJW > FDCAN_DBTP_DSJW_MAX
+#    error Invalid FDCAN3 DSJW
+#  endif
+
+/* FDCAN3 Message RAM Configuration *****************************************/
+
+/* FDCAN3 Message RAM Layout */
+
+#  define FDCAN3_STDFILTER_INDEX   0
+#  define FDCAN3_EXTFILTERS_INDEX  (FDCAN3_STDFILTER_INDEX + FDCAN3_STDFILTER_WORDS)
+#  define FDCAN3_RXFIFO0_INDEX     (FDCAN3_EXTFILTERS_INDEX + FDCAN3_EXTFILTER_WORDS)
+#  define FDCAN3_RXFIFO1_INDEX     (FDCAN3_RXFIFO0_INDEX + FDCAN3_RXFIFO0_WORDS)
+#  define FDCAN3_TXEVENTFIFO_INDEX (FDCAN3_RXFIFO1_INDEX + FDCAN3_RXFIFO1_WORDS)
+#  define FDCAN3_TXFIFOQ_INDEX     (FDCAN3_TXEVENTFIFO_INDEX + FDCAN3_TXEVENTFIFO_WORDS)
+#  define FDCAN3_MSGRAM_WORDS      (FDCAN3_TXFIFOQ_INDEX + FDCAN3_TXFIFIOQ_WORDS)
+
+#endif /* CONFIG_STM32_FDCAN3 */
+
+/* Loopback mode */
+
+#undef STM32_FDCAN_LOOPBACK
+#if defined(CONFIG_STM32_FDCAN1_LOOPBACK) ||   \
+    defined(CONFIG_STM32_FDCAN2_LOOPBACK) ||   \
+    defined(CONFIG_STM32_FDCAN3_LOOPBACK)
+#  define STM32_FDCAN_LOOPBACK 1
+#endif
+
+/* Interrupts ***************************************************************/
+
+/* Common interrupts
+ *
+ *   FDCAN_INT_TSW  - Timestamp Wraparound
+ *   FDCAN_INT_MRAF - Message RAM Access Failure
+ *   FDCAN_INT_TOO  - Timeout Occurred
+ *   FDCAN_INT_ELO  - Error Logging Overflow
+ *   FDCAN_INT_EP   - Error Passive
+ *   FDCAN_INT_EW   - Warning Status
+ *   FDCAN_INT_BO   - Bus_Off Status
+ *   FDCAN_INT_WDI  - Watchdog Interrupt
+ *   FDCAN_INT_PEA  - Protocol Error in Arbritration Phase
+ *   FDCAN_INT_PED  - Protocol Error in Data Phase
+ */
+
+#define FDCAN_CMNERR_INTS   (FDCAN_INT_MRAF | FDCAN_INT_TOO | FDCAN_INT_EP | \
+                            FDCAN_INT_BO | FDCAN_INT_WDI | FDCAN_INT_PEA | \
+                            FDCAN_INT_PED)
+
+/* RXFIFO mode interrupts
+ *
+ *   FDCAN_INT_RF0N - Receive FIFO 0 New Message
+ *   FDCAN_INT_RF0F - Receive FIFO 0 Full
+ *   FDCAN_INT_RF0L - Receive FIFO 0 Message Lost
+ *   FDCAN_INT_RF1N - Receive FIFO 1 New Message
+ *   FDCAN_INT_RF1F - Receive FIFO 1 Full
+ *   FDCAN_INT_RF1L - Receive FIFO 1 Message Lost
+ *   FDCAN_INT_HPM  - High Priority Message Received
+ *
+ */
+
+#define FDCAN_RXFIFO0_INTS  (FDCAN_INT_RF0N | FDCAN_INT_RF0L)
+#define FDCAN_RXFIFO1_INTS  (FDCAN_INT_RF1N | FDCAN_INT_RF1L)
+
+#define FDCAN_RXERR_INTS    (FDCAN_INT_RF0L | FDCAN_INT_RF1L)
+
+/* TX FIFOQ mode interrupts
+ *
+ *   FDCAN_INT_TFE  - Tx FIFO Empty
+ *
+ * TX Event FIFO interrupts
+ *
+ *   FDCAN_INT_TEFN - Tx Event FIFO New Entry
+ *   FDCAN_INT_TEFF - Tx Event FIFO Full
+ *   FDCAN_INT_TEFL - Tx Event FIFO Element Lost
+ *
+ * Mode-independent TX-related interrupts
+ *
+ *   FDCAN_INT_TC   - Transmission Completed
+ *   FDCAN_INT_TCF  - Transmission Cancellation Finished
+ */
+
+#define FDCAN_TXCOMMON_INTS (FDCAN_INT_TC | FDCAN_INT_TCF)
+#define FDCAN_TXFIFOQ_INTS  (FDCAN_INT_TFE | FDCAN_TXCOMMON_INTS)
+#define FDCAN_TXEVFIFO_INTS (FDCAN_INT_TEFN | FDCAN_INT_TEFF | \
+                             FDCAN_INT_TEFL)
+
+#define FDCAN_TXERR_INTS    (FDCAN_INT_TEFL | FDCAN_INT_PEA | FDCAN_INT_PED)
+
+/* Common-, TX- and RX-Error-Mask */
+
+#define FDCAN_ANYERR_INTS (FDCAN_CMNERR_INTS | FDCAN_RXERR_INTS | FDCAN_TXERR_INTS)
+
+/* Convenience macro for clearing all interrupts */
+
+#define FDCAN_INT_ALL     0x3fcfffff
+
+/* Debug ********************************************************************/
+
+/* Debug configurations that may be enabled just for testing FDCAN */
+
+#ifndef CONFIG_DEBUG_NET_INFO
+#  undef CONFIG_STM32_FDCAN_REGDEBUG
+#endif
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/* CAN frame format */
+
+enum stm32_frameformat_e
+{
+  FDCAN_ISO11898_1_FORMAT      = 0,  /* Frame format according to ISO11898-1 */
+  FDCAN_NONISO_BOSCH_V1_FORMAT = 1   /* Frame format according to Bosch CAN FD V1.0 */
+};
+
+/* CAN mode of operation */
+
+enum stm32_canmode_e
+{
+  FDCAN_CLASSIC_MODE = 0,   /* Classic CAN operation */
+#ifdef CONFIG_CAN_FD
+  FDCAN_FD_MODE      = 1,   /* CAN FD operation */
+  FDCAN_FD_BRS_MODE  = 2    /* CAN FD operation with bit rate switching */
+#endif
+};
+
+/* CAN driver state */
+
+enum can_state_s
+{
+  FDCAN_STATE_UNINIT = 0,   /* Not yet initialized */
+  FDCAN_STATE_RESET,        /* Initialized, reset state */
+  FDCAN_STATE_SETUP,        /* fdcan_setup() has been called */
+  FDCAN_STATE_DISABLED      /* Disabled by a fdcan_shutdown() */
+};
+
+/* This structure describes the FDCAN message RAM layout */
+
+struct stm32_msgram_s
+{
+  volatile uint32_t *stdfilters;     /* Standard filters */
+  volatile uint32_t *extfilters;     /* Extended filters */
+  volatile uint32_t *rxfifo0;        /* RX FIFO0 */
+  volatile uint32_t *rxfifo1;        /* RX FIFO1 */
+  volatile uint32_t *txeventfifo;    /* TX event FIFO */
+  volatile uint32_t *txfifoq;        /* TX FIFO queue */
+};
+
+/* This structure provides the constant configuration of a FDCAN peripheral */
+
+struct stm32_config_s
+{
+  uint32_t rxpinset;        /* RX pin configuration */
+  uint32_t txpinset;        /* TX pin configuration */
+  uintptr_t base;           /* Base address of the FDCAN registers */
+  uint32_t baud;            /* Configured baud */
+  uint32_t nbtp;            /* Nominal bit timing/prescaler register setting */
+  uint32_t dbtp;            /* Data bit timing/prescaler register setting */
+  uint8_t port;             /* FDCAN port number (1 or 2) */
+  uint8_t irq0;             /* FDCAN peripheral IRQ number for interrupt line 0 */
+  uint8_t irq1;             /* FDCAN peripheral IRQ number for interrupt line 1 */
+  uint8_t mode;             /* See enum stm32_canmode_e */
+  uint8_t format;           /* See enum stm32_frameformat_e */
+  uint8_t nstdfilters;      /* Number of standard filters */
+  uint8_t nextfilters;      /* Number of extended filters */
+  uint8_t nrxfifo0;         /* Number of RX FIFO0 elements */
+  uint8_t nrxfifo1;         /* Number of RX FIFO1 elements */
+  uint8_t ntxeventfifo;     /* Number of TXevent FIFO elements */
+  uint8_t ntxfifoq;         /* Number of TX FIFO queue elements */
+  uint8_t rxfifo0esize;     /* RX FIFO0 element size (words) */
+  uint8_t rxfifo1esize;     /* RX FIFO1 element size (words) */
+  uint8_t txeventesize;     /* TXevent element size (words) */
+  uint8_t txbufferesize;    /* TX buffer element size (words) */
+#ifdef STM32_FDCAN_LOOPBACK
+  bool loopback;            /* True: Loopback mode */
+#endif
+
+  /* FDCAN message RAM layout */
+
+  struct stm32_msgram_s msgram;
+};
+
+/* This structure provides the current state of a FDCAN peripheral */
+
+struct stm32_fdcan_s
+{
+  /* The constant configuration */
+
+  const struct stm32_config_s *config;
+
+  uint8_t state;            /* See enum can_state_s */
+#ifdef CONFIG_CAN_EXTID
+  uint8_t nextalloc;        /* Number of allocated extended filters */
+#endif
+  uint8_t nstdalloc;        /* Number of allocated standard filters */
+  uint32_t nbtp;            /* Current nominal bit timing */
+  uint32_t dbtp;            /* Current data bit timing */
+
+#ifdef CONFIG_CAN_EXTID
+  uint32_t extfilters[2];   /* Extended filter bit allocator.  2*32=64 */
+#endif
+  uint32_t stdfilters[4];   /* Standard filter bit allocator.  4*32=128 */
+
+#ifdef CONFIG_STM32_FDCAN_REGDEBUG
+  uintptr_t regaddr;        /* Last register address read */
+  uint32_t regval;          /* Last value read from the register */
+  unsigned int count;       /* Number of times that the value was read */
+#endif
+
+  bool bifup;               /* true:ifup false:ifdown */
+  struct net_driver_s dev;  /* Interface understood by the network */
+
+  struct work_s irqwork;    /* For deferring interrupt work to the wq */
+  struct work_s pollwork;   /* For deferring poll work to the work wq */
+
+  /* A pointers to the list of TX/RX descriptors */
+
+  FAR struct can_frame *txdesc;
+  FAR struct can_frame *rxdesc;
+
+  /* TX/RX pool */
+
+  uint8_t tx_pool[sizeof(struct can_frame)*POOL_SIZE];
+  uint8_t rx_pool[sizeof(struct can_frame)*POOL_SIZE];
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* FDCAN Register access */
+
+static uint32_t fdcan_getreg(FAR struct stm32_fdcan_s *priv, int offset);
+static void fdcan_putreg(FAR struct stm32_fdcan_s *priv, int offset,
+                         uint32_t regval);
+#ifdef CONFIG_STM32_FDCAN_REGDEBUG
+static void fdcan_dumpregs(FAR struct stm32_fdcan_s *priv,
+                           FAR const char *msg);
+static void fdcan_dumprxregs(FAR struct stm32_fdcan_s *priv,
+                             FAR const char *msg);
+static void fdcan_dumptxregs(FAR struct stm32_fdcan_s *priv,
+                             FAR const char *msg);
+static void fdcan_dumpramlayout(FAR struct stm32_fdcan_s *priv);
+#else
+#  define fdcan_dumpregs(priv,msg)
+#  define fdcan_dumprxregs(priv,msg)
+#  define fdcan_dumptxregs(priv,msg)
+#  define fdcan_dumpramlayout(priv)
+#endif
+
+/* CAN interrupt enable functions */
+
+static void fdcan_rx0int(FAR struct stm32_fdcan_s *priv, bool enable);
+static void fdcan_rx1int(FAR struct stm32_fdcan_s *priv, bool enable);
+static void fdcan_txint(FAR struct stm32_fdcan_s *priv, bool enable);
+#ifdef CONFIG_NET_CAN_ERRORS
+static void fdcan_errint(FAR struct stm32_fdcan_s *priv, bool enable);
+#endif
+
+/* Common TX logic */
+
+static int  fdcan_send(FAR struct stm32_fdcan_s *priv);
+static bool fdcan_txready(FAR struct stm32_fdcan_s *priv);
+static int  fdcan_txpoll(struct net_driver_s *dev);
+
+/* CAN RX interrupt handling */
+
+static void fdcan_rx0interrupt_work(FAR void *arg);
+static void fdcan_rx1interrupt_work(FAR void *arg);
+
+/* CAN TX interrupt handling */
+
+static void fdcan_txdone_work(FAR void *arg);
+static void fdcan_txdone(FAR struct stm32_fdcan_s *priv);
+
+#ifdef CONFIG_NET_CAN_ERRORS
+/* CAN errors interrupt handling */
+
+static void fdcan_error_work(FAR void *arg);
+#endif
+
+/* FDCAN interrupt handling */
+
+#ifdef CONFIG_NET_CAN_ERRORS
+static void fdcan_error(FAR struct stm32_fdcan_s *priv, uint32_t status);
+#endif
+static void fdcan_receive(FAR struct stm32_fdcan_s *priv,
+                          FAR volatile uint32_t *rxbuffer,
+                          unsigned long nwords);
+static int  fdcan_interrupt(int irq, void *context, FAR void *arg);
+
+/* Initialization */
+
+static void fdcan_reset(FAR struct stm32_fdcan_s *priv);
+static int  fdcan_setup(FAR struct stm32_fdcan_s *priv);
+static void fdcan_shutdown(FAR struct stm32_fdcan_s *priv);
+
+/* FDCAN helpers */
+
+static uint8_t fdcan_dlc2bytes(FAR struct stm32_fdcan_s *priv, uint8_t dlc);
+
+#if 0 /* not used for now */
+static int
+fdcan_start_busoff_recovery_sequence(FAR struct stm32_fdcan_s *priv);
+#endif
+
+/* Hardware initialization */
+
+static int  fdcan_hw_initialize(FAR struct stm32_fdcan_s *priv);
+
+/* NuttX 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
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+#ifdef CONFIG_STM32_FDCAN1
+/* Message RAM allocation */
+
+/* Constant configuration */
+
+static const struct stm32_config_s g_fdcan1const =
+{
+  .rxpinset         = GPIO_FDCAN1_RX,
+  .txpinset         = GPIO_FDCAN1_TX,
+  .base             = STM32_FDCAN1_BASE,
+  .baud             = CONFIG_STM32_FDCAN1_BITRATE,
+  .nbtp             = FDCAN_NBTP_NBRP(FDCAN1_NBRP) |
+                      FDCAN_NBTP_NTSEG1(FDCAN1_NTSEG1) |
+                      FDCAN_NBTP_NTSEG2(FDCAN1_NTSEG2) |
+                      FDCAN_NBTP_NSJW(FDCAN1_NSJW),
+  .dbtp             = FDCAN_DBTP_DBRP(FDCAN1_DBRP) |
+                      FDCAN_DBTP_DTSEG1(FDCAN1_DTSEG1) |
+                      FDCAN_DBTP_DTSEG2(FDCAN1_DTSEG2) |
+                      FDCAN_DBTP_DSJW(FDCAN1_DSJW),
+  .port             = 1,
+  .irq0             = STM32_IRQ_FDCAN1_0,
+  .irq1             = STM32_IRQ_FDCAN1_1,
+#if defined(CONFIG_STM32_FDCAN1_CLASSIC)
+  .mode             = FDCAN_CLASSIC_MODE,
+#elif defined(CONFIG_STM32_FDCAN1_FD)
+  .mode             = FDCAN_FD_MODE,
+#else
+  .mode             = FDCAN_FD_BRS_MODE,
+#endif
+#if defined(CONFIG_STM32_FDCAN1_NONISO_FORMAT)
+  .format           = FDCAN_NONISO_BOSCH_V1_FORMAT,
+#else
+  .format           = FDCAN_ISO11898_1_FORMAT,
+#endif
+  .nstdfilters      = FDCAN1_STDFILTER_SIZE,
+  .nextfilters      = FDCAN1_EXTFILTER_SIZE,
+  .nrxfifo0         = FDCAN1_RXFIFO0_SIZE,
+  .nrxfifo1         = FDCAN1_RXFIFO1_SIZE,
+  .ntxeventfifo     = FDCAN1_TXEVENTFIFO_SIZE,
+  .ntxfifoq         = FDCAN1_TXFIFIOQ_SIZE,
+  .rxfifo0esize     = (FDCAN1_RXFIFO0_WORDS / FDCAN1_RXFIFO0_SIZE),
+  .rxfifo1esize     = (FDCAN1_RXFIFO1_WORDS / FDCAN1_RXFIFO1_SIZE),
+  .txeventesize     = (FDCAN1_TXEVENTFIFO_WORDS / FDCAN1_TXEVENTFIFO_SIZE),
+  .txbufferesize    = (FDCAN1_TXFIFIOQ_WORDS / FDCAN1_TXFIFIOQ_SIZE),
+
+#ifdef CONFIG_STM32_FDCAN1_LOOPBACK
+  .loopback         = true,
+#endif
+
+  /* FDCAN1 Message RAM */
+
+  .msgram =
+  {
+    (uint32_t *)(STM32_CANRAM1_BASE + (FDCAN1_STDFILTER_INDEX << 2)),
+    (uint32_t *)(STM32_CANRAM1_BASE + (FDCAN1_EXTFILTERS_INDEX << 2)),
+    (uint32_t *)(STM32_CANRAM1_BASE + (FDCAN1_RXFIFO0_INDEX << 2)),
+    (uint32_t *)(STM32_CANRAM1_BASE + (FDCAN1_RXFIFO1_INDEX << 2)),
+    (uint32_t *)(STM32_CANRAM1_BASE + (FDCAN1_TXEVENTFIFO_INDEX << 2)),
+    (uint32_t *)(STM32_CANRAM1_BASE + (FDCAN1_TXFIFOQ_INDEX << 2))
+  }
+};
+
+/* FDCAN1 variable driver state */
+
+static struct stm32_fdcan_s g_fdcan1priv;
+
+#endif /* CONFIG_STM32_FDCAN1 */
+
+#ifdef CONFIG_STM32_FDCAN2
+/* FDCAN2 message RAM allocation */
+
+/* FDCAN2 constant configuration */
+
+static const struct stm32_config_s g_fdcan2const =
+{
+  .rxpinset         = GPIO_FDCAN2_RX,
+  .txpinset         = GPIO_FDCAN2_TX,
+  .base             = STM32_FDCAN2_BASE,
+  .baud             = CONFIG_STM32_FDCAN2_BITRATE,
+  .nbtp             = FDCAN_NBTP_NBRP(FDCAN2_NBRP) |
+                      FDCAN_NBTP_NTSEG1(FDCAN2_NTSEG1) |
+                      FDCAN_NBTP_NTSEG2(FDCAN2_NTSEG2) |
+                      FDCAN_NBTP_NSJW(FDCAN2_NSJW),
+  .dbtp             = FDCAN_DBTP_DBRP(FDCAN2_DBRP) |
+                      FDCAN_DBTP_DTSEG1(FDCAN2_DTSEG1) |
+                      FDCAN_DBTP_DTSEG2(FDCAN2_DTSEG2) |
+                      FDCAN_DBTP_DSJW(FDCAN2_DSJW),
+  .port             = 2,
+  .irq0             = STM32_IRQ_FDCAN2_0,
+  .irq1             = STM32_IRQ_FDCAN2_1,
+#if defined(CONFIG_STM32_FDCAN2_CLASSIC)
+  .mode             = FDCAN_CLASSIC_MODE,
+#elif defined(CONFIG_STM32_FDCAN2_FD)
+  .mode             = FDCAN_FD_MODE,
+#else
+  .mode             = FDCAN_FD_BRS_MODE,
+#endif
+#if defined(CONFIG_STM32_FDCAN2_NONISO_FORMAT)
+  .format           = FDCAN_NONISO_BOSCH_V1_FORMAT,
+#else
+  .format           = FDCAN_ISO11898_1_FORMAT,
+#endif
+  .nstdfilters      = FDCAN2_STDFILTER_SIZE,
+  .nextfilters      = FDCAN2_EXTFILTER_SIZE,
+  .nrxfifo0         = FDCAN2_RXFIFO0_SIZE,
+  .nrxfifo1         = FDCAN2_RXFIFO1_SIZE,
+  .ntxeventfifo     = FDCAN2_TXEVENTFIFO_SIZE,
+  .ntxfifoq         = FDCAN2_TXFIFIOQ_SIZE,
+  .rxfifo0esize     = (FDCAN2_RXFIFO0_WORDS / FDCAN2_RXFIFO0_SIZE),
+  .rxfifo1esize     = (FDCAN2_RXFIFO1_WORDS / FDCAN2_RXFIFO1_SIZE),
+  .txeventesize     = (FDCAN2_TXEVENTFIFO_WORDS / FDCAN2_TXEVENTFIFO_SIZE),
+  .txbufferesize    = (FDCAN2_TXFIFIOQ_WORDS / FDCAN2_TXFIFIOQ_SIZE),
+
+#ifdef CONFIG_STM32_FDCAN2_LOOPBACK
+  .loopback         = true,
+#endif
+
+  /* FDCAN2 Message RAM */
+
+  .msgram =
+  {
+    (uint32_t *)(STM32_CANRAM2_BASE + (FDCAN2_STDFILTER_INDEX << 2)),
+    (uint32_t *)(STM32_CANRAM2_BASE + (FDCAN2_EXTFILTERS_INDEX << 2)),
+    (uint32_t *)(STM32_CANRAM2_BASE + (FDCAN2_RXFIFO0_INDEX << 2)),
+    (uint32_t *)(STM32_CANRAM2_BASE + (FDCAN2_RXFIFO1_INDEX << 2)),
+    (uint32_t *)(STM32_CANRAM2_BASE + (FDCAN2_TXEVENTFIFO_INDEX << 2)),
+    (uint32_t *)(STM32_CANRAM2_BASE + (FDCAN2_TXFIFOQ_INDEX << 2))
+  }
+};
+
+/* FDCAN2 variable driver state */
+
+static struct stm32_fdcan_s g_fdcan2priv;
+
+#endif /* CONFIG_STM32_FDCAN2 */
+
+#ifdef CONFIG_STM32_FDCAN3
+/* FDCAN3 message RAM allocation */
+
+/* FDCAN3 constant configuration */
+
+static const struct stm32_config_s g_fdcan3const =
+{
+  .rxpinset         = GPIO_FDCAN3_RX,
+  .txpinset         = GPIO_FDCAN3_TX,
+  .base             = STM32_FDCAN3_BASE,
+  .baud             = CONFIG_STM32_FDCAN3_BITRATE,
+  .nbtp             = FDCAN_NBTP_NBRP(FDCAN3_NBRP) |
+                      FDCAN_NBTP_NTSEG1(FDCAN3_NTSEG1) |
+                      FDCAN_NBTP_NTSEG2(FDCAN3_NTSEG2) |
+                      FDCAN_NBTP_NSJW(FDCAN3_NSJW),
+  .dbtp             = FDCAN_DBTP_DBRP(FDCAN3_DBRP) |
+                      FDCAN_DBTP_DTSEG1(FDCAN3_DTSEG1) |
+                      FDCAN_DBTP_DTSEG2(FDCAN3_DTSEG2) |
+                      FDCAN_DBTP_DSJW(FDCAN3_DSJW),
+  .port             = 3,
+  .irq0             = STM32_IRQ_FDCAN3_0,
+  .irq1             = STM32_IRQ_FDCAN3_1,
+#if defined(CONFIG_STM32_FDCAN3_CLASSIC)
+  .mode             = FDCAN_CLASSIC_MODE,
+#elif defined(CONFIG_STM32_FDCAN3_FD)
+  .mode             = FDCAN_FD_MODE,
+#else
+  .mode             = FDCAN_FD_BRS_MODE,
+#endif
+#if defined(CONFIG_STM32_FDCAN3_NONISO_FORMAT)
+  .format           = FDCAN_NONISO_BOSCH_V1_FORMAT,
+#else
+  .format           = FDCAN_ISO11898_1_FORMAT,
+#endif
+  .nstdfilters      = FDCAN3_STDFILTER_SIZE,
+  .nextfilters      = FDCAN3_EXTFILTER_SIZE,
+  .nrxfifo0         = FDCAN3_RXFIFO0_SIZE,
+  .nrxfifo1         = FDCAN3_RXFIFO1_SIZE,
+  .ntxeventfifo     = FDCAN3_TXEVENTFIFO_SIZE,
+  .ntxfifoq         = FDCAN3_TXFIFIOQ_SIZE,
+  .rxfifo0esize     = (FDCAN3_RXFIFO0_WORDS / FDCAN3_RXFIFO0_SIZE),
+  .rxfifo1esize     = (FDCAN3_RXFIFO1_WORDS / FDCAN3_RXFIFO1_SIZE),
+  .txeventesize     = (FDCAN3_TXEVENTFIFO_WORDS / FDCAN3_TXEVENTFIFO_SIZE),
+  .txbufferesize    = (FDCAN3_TXFIFIOQ_WORDS / FDCAN3_TXFIFIOQ_SIZE),
+
+#ifdef CONFIG_STM32_FDCAN3_LOOPBACK
+  .loopback         = true,
+#endif
+
+  /* FDCAN3 Message RAM */
+
+  .msgram =
+  {
+    (uint32_t *)(STM32_CANRAM3_BASE + (FDCAN3_STDFILTER_INDEX << 2)),
+    (uint32_t *)(STM32_CANRAM3_BASE + (FDCAN3_EXTFILTERS_INDEX << 2)),
+    (uint32_t *)(STM32_CANRAM3_BASE + (FDCAN3_RXFIFO0_INDEX << 2)),
+    (uint32_t *)(STM32_CANRAM3_BASE + (FDCAN3_RXFIFO1_INDEX << 2)),
+    (uint32_t *)(STM32_CANRAM3_BASE + (FDCAN3_TXEVENTFIFO_INDEX << 2)),
+    (uint32_t *)(STM32_CANRAM3_BASE + (FDCAN3_TXFIFOQ_INDEX << 2))
+  }
+};
+
+/* FDCAN3 variable driver state */
+
+static struct stm32_fdcan_s g_fdcan3priv;
+
+#endif /* CONFIG_STM32_FDCAN3 */
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: fdcan_getreg
+ *
+ * Description:
+ *   Read the value of a FDCAN register.
+ *
+ * Input Parameters:
+ *   priv - A reference to the FDCAN peripheral state
+ *   offset - The offset to the register to read
+ *
+ * Returned Value:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_STM32_FDCAN_REGDEBUG
+static uint32_t fdcan_getreg(FAR struct stm32_fdcan_s *priv, int offset)
+{
+  FAR const struct stm32_config_s *config  = priv->config;
+  uintptr_t                        regaddr = 0;
+  uint32_t                         regval  = 0;
+
+  /* Read the value from the register */
+
+  regaddr = config->base + offset;
+  regval  = getreg32(regaddr);
+
+  /* Is this the same value that we read from the same register last time?
+   * Are we polling the register?  If so, suppress some of the output.
+   */
+
+  if (regaddr == priv->regaddr && regval == priv->regval)
+    {
+      if (priv->count == 0xffffffff || ++priv->count > 3)
+        {
+          if (priv->count == 4)
+            {
+              ninfo("...\n");
+            }
+
+          return regval;
+        }
+    }
+
+  /* No this is a new address or value */
+
+  else
+    {
+      /* Did we print "..." for the previous value? */
+
+      if (priv->count > 3)
+        {
+          /* Yes.. then show how many times the value repeated */
+
+          ninfo("[repeats %d more times]\n", priv->count - 3);
+        }
+
+      /* Save the new address, value, and count */
+
+      priv->regaddr = regaddr;
+      priv->regval  = regval;
+      priv->count   = 1;
+    }
+
+  /* Show the register value read */
+
+  ninfo("%08" PRIx32 "->%08" PRIx32 "\n", regaddr, regval);
+  return regval;
+}
+
+#else
+static uint32_t fdcan_getreg(FAR struct stm32_fdcan_s *priv, int offset)
+{
+  FAR const struct stm32_config_s *config = priv->config;
+  return getreg32(config->base + offset);
+}
+
+#endif
+
+/****************************************************************************
+ * Name: fdcan_putreg
+ *
+ * Description:
+ *   Set the value of a FDCAN register.
+ *
+ * Input Parameters:
+ *   priv - A reference to the FDCAN peripheral state
+ *   offset - The offset to the register to write
+ *   regval - The value to write to the register
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_STM32_FDCAN_REGDEBUG
+static void fdcan_putreg(FAR struct stm32_fdcan_s *priv, int offset,
+                         uint32_t regval)
+{
+  FAR const struct stm32_config_s *config = priv->config;
+  uintptr_t regaddr = config->base + offset;
+
+  /* Show the register value being written */
+
+  ninfo("%08" PRIx32 "->%08" PRIx32 "\n", regaddr, regval);
+
+  /* Write the value */
+
+  putreg32(regval, regaddr);
+}
+
+#else
+static void fdcan_putreg(FAR struct stm32_fdcan_s *priv, int offset,
+                         uint32_t regval)
+{
+  FAR const struct stm32_config_s *config = priv->config;
+  putreg32(regval, config->base + offset);
+}
+
+#endif
+
+/****************************************************************************
+ * Name: fdcan_dumpctrlregs
+ *
+ * Description:
+ *   Dump the contents of all CAN control registers
+ *
+ * Input Parameters:
+ *   priv - A reference to the CAN block status
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_STM32_FDCAN_REGDEBUG
+static void fdcan_dumpregs(FAR struct stm32_fdcan_s *priv,
+                           FAR const char *msg)
+{
+  FAR const struct stm32_config_s *config = priv->config;
+
+  ninfo("CAN%d Control and Status Registers: %s\n", config->port, msg);
+  ninfo("  Base:  %08" PRIx32 "\n", config->base);
+
+  /* CAN control and status registers */
+
+  ninfo("  CCCR:  %08" PRIx32 "   TEST:  %08" PRIx32 "\n",
+          getreg32(config->base + STM32_FDCAN_CCCR_OFFSET),
+          getreg32(config->base + STM32_FDCAN_TEST_OFFSET));
+
+  ninfo("  NBTP:  %08" PRIx32 "   DBTP:  %08" PRIx32 "\n",
+          getreg32(config->base + STM32_FDCAN_NBTP_OFFSET),
+          getreg32(config->base + STM32_FDCAN_DBTP_OFFSET));
+
+  ninfo("  IE:    %08" PRIx32 "   TIE:   %08" PRIx32 "\n",
+          getreg32(config->base + STM32_FDCAN_IE_OFFSET),
+          getreg32(config->base + STM32_FDCAN_TXBTIE_OFFSET));
+
+  ninfo("  ILE:   %08" PRIx32 "   ILS:   %08" PRIx32 "\n",
+          getreg32(config->base + STM32_FDCAN_ILE_OFFSET),
+          getreg32(config->base + STM32_FDCAN_ILS_OFFSET));
+
+  ninfo("  TXBC:  %08" PRIx32 "\n",
+          getreg32(config->base + STM32_FDCAN_TXBC_OFFSET));
+}
+#endif
+
+/****************************************************************************
+ * Name: fdcan_dumprxregs
+ *
+ * Description:
+ *   Dump the contents of all Rx status registers
+ *
+ * Input Parameters:
+ *   priv - A reference to the CAN block status
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_STM32_FDCAN_REGDEBUG
+static void fdcan_dumprxregs(FAR struct stm32_fdcan_s *priv,
+                             FAR const char *msg)
+{
+  FAR const struct stm32_config_s *config = priv->config;
+
+  ninfo("CAN%d Rx Registers: %s\n", config->port, msg);
+  ninfo("  Base:  %08" PRIx32 "\n", config->base);
+
+  ninfo("  PSR:   %08" PRIx32 "   ECR:   %08" PRIx32
+          "   HPMS: %08" PRIx32 "\n",
+          getreg32(config->base + STM32_FDCAN_PSR_OFFSET),
+          getreg32(config->base + STM32_FDCAN_ECR_OFFSET),
+          getreg32(config->base + STM32_FDCAN_HPMS_OFFSET));
+
+  ninfo("  RXF0S: %08" PRIx32 "   RXF0A: %08" PRIx32 "\n",
+          getreg32(config->base + STM32_FDCAN_RXF0S_OFFSET),
+          getreg32(config->base + STM32_FDCAN_RXF0A_OFFSET));
+
+  ninfo("  RXF1S: %08" PRIx32 "   RXF1A: %08" PRIx32 "\n",
+          getreg32(config->base + STM32_FDCAN_RXF1S_OFFSET),
+          getreg32(config->base + STM32_FDCAN_RXF1A_OFFSET));
+
+  ninfo("  IR:    %08" PRIx32 "   IE:    %08" PRIx32 "\n",
+          getreg32(config->base + STM32_FDCAN_IR_OFFSET),
+          getreg32(config->base + STM32_FDCAN_IE_OFFSET));
+}
+#endif
+
+/****************************************************************************
+ * Name: fdcan_dumptxregs
+ *
+ * Description:
+ *   Dump the contents of all Tx buffer registers
+ *
+ * Input Parameters:
+ *   priv - A reference to the CAN block status
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_STM32_FDCAN_REGDEBUG
+static void fdcan_dumptxregs(FAR struct stm32_fdcan_s *priv,
+                             FAR const char *msg)
+{
+  FAR const struct stm32_config_s *config = priv->config;
+
+  ninfo("CAN%d Tx Registers: %s\n", config->port, msg);
+  ninfo("  Base:  %08" PRIx32 "\n", config->base);
+
+  ninfo("  PSR:   %08" PRIx32 "   ECR:   %08" PRIx32 "\n",
+          getreg32(config->base + STM32_FDCAN_PSR_OFFSET),
+          getreg32(config->base + STM32_FDCAN_ECR_OFFSET));
+
+  ninfo("  TXQFS: %08" PRIx32 "   TXBAR: %08" PRIx32
+          "   TXBRP: %08" PRIx32 "\n",
+          getreg32(config->base + STM32_FDCAN_TXFQS_OFFSET),
+          getreg32(config->base + STM32_FDCAN_TXBAR_OFFSET),
+          getreg32(config->base + STM32_FDCAN_TXBRP_OFFSET));
+
+  ninfo("  TXBTO: %08" PRIx32 "   TXBCR: %08" PRIx32 "\n",
+          getreg32(config->base + STM32_FDCAN_TXBTO_OFFSET),
+          getreg32(config->base + STM32_FDCAN_TXBCR_OFFSET));
+
+  ninfo("   TXEFS: %08" PRIx32 "   TXEFA: %08" PRIx32 "\n",
+          getreg32(config->base + STM32_FDCAN_TXEFS_OFFSET),
+          getreg32(config->base + STM32_FDCAN_TXEFA_OFFSET));
+
+  ninfo("  IR:    %08" PRIx32 "   IE:    %08" PRIx32
+          "   TIE:   %08" PRIx32 "\n",
+          getreg32(config->base + STM32_FDCAN_IR_OFFSET),
+          getreg32(config->base + STM32_FDCAN_IE_OFFSET),
+          getreg32(config->base + STM32_FDCAN_TXBTIE_OFFSET));
+}
+#endif
+
+/****************************************************************************
+ * Name: fdcan_dumpramlayout
+ *
+ * Description:
+ *   Print the layout of the message RAM
+ *
+ * Input Parameters:
+ *   priv - A reference to the CAN block status
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_STM32_FDCAN_REGDEBUG
+static void fdcan_dumpramlayout(FAR struct stm32_fdcan_s *priv)
+{
+  FAR const struct stm32_config_s *config = priv->config;
+
+  ninfo(" ******* FDCAN%d Message RAM layout *******\n", config->port);
+  ninfo("                Start     # Elmnt  Elmnt size (words)\n");
+
+  if (config->nstdfilters > 0)
+    {
+      ninfo("STD filters   %p   %4d        %2d\n",
+              config->msgram.stdfilters,
+              config->nstdfilters,
+              1);
+    }
+
+  if (config->nextfilters > 0)
+    {
+      ninfo("EXT filters   %p   %4d        %2d\n",
+              config->msgram.extfilters,
+              config->nextfilters,
+              2);
+    }
+
+  if (config->nrxfifo0 > 0)
+    {
+      ninfo("RX FIFO 0     %p   %4d        %2d\n",
+              config->msgram.rxfifo0,
+              config->nrxfifo0,
+              config->rxfifo0esize);
+    }
+
+  if (config->nrxfifo1 > 0)
+    {
+      ninfo("RX FIFO 1     %p   %4d        %2d\n",
+              config->msgram.rxfifo1,
+              config->nrxfifo1,
+              config->rxfifo1esize);
+    }
+
+  if (config->ntxeventfifo > 0)
+    {
+      ninfo("TX EVENT      %p   %4d        %2d\n",
+              config->msgram.txeventfifo,
+              config->ntxeventfifo,
+              config->txeventesize);
+    }
+
+  if (config->ntxfifoq > 0)
+    {
+      ninfo("TX FIFO       %p   %4d        %2d\n",
+              config->msgram.txfifoq,
+              config->ntxfifoq,
+              config->txbufferesize);
+    }
+}
+#endif
+
+/****************************************************************************
+ * Name: fdcan_dlc2bytes
+ *
+ * Description:
+ *   In the CAN FD format, the coding of the DLC differs from the standard
+ *   CAN format. The DLC codes 0 to 8 have the same coding as in standard
+ *   CAN.  But the codes 9 to 15 all imply a data field of 8 bytes with
+ *   standard CAN.  In CAN FD mode, the values 9 to 15 are encoded to values
+ *   in the range 12 to 64.
+ *
+ * Input Parameters:
+ *   dlc    - the DLC value to convert to a byte count
+ *
+ * Returned Value:
+ *   The number of bytes corresponding to the DLC value.
+ *
+ ****************************************************************************/
+
+static uint8_t fdcan_dlc2bytes(FAR struct stm32_fdcan_s *priv, uint8_t dlc)
+{
+  if (dlc > 8)
+    {
+#ifdef CONFIG_CAN_FD
+      if (priv->config->mode == FDCAN_CLASSIC_MODE)
+        {
+          return 8;
+        }
+      else
+        {
+          switch (dlc)
+            {
+              case 9:
+                return 12;
+              case 10:
+                return 16;
+              case 11:
+                return 20;
+              case 12:
+                return 24;
+              case 13:
+                return 32;
+              case 14:
+                return 48;
+              default:
+              case 15:
+                return 64;
+            }
+        }
+#else
+      return 8;
+#endif
+    }
+
+  return dlc;
+}
+
+/****************************************************************************
+ * Name: fdcan_start_busoff_recovery_sequence
+ *
+ * Description:
+ *   This function initiates the BUS-OFF recovery sequence.
+ *   CAN Specification Rev. 2.0 or ISO11898-1:2015.
+ *   According the STM32G4 datasheet section 44.3.2 Software initialziation.
+ *
+ * Input Parameters:
+ *   priv - An instance of the FDCAN driver state structure.
+ *
+ * Returned Value:
+ *   Zero (OK) is returned on success.  Otherwise a negated errno value is
+ *   returned to indicate the nature of the error.
+ *
+ ****************************************************************************/
+
+#if 0 /* not used for now */
+static int
+fdcan_start_busoff_recovery_sequence(FAR struct stm32_fdcan_s *priv)
+{
+  uint32_t regval = 0;
+
+  DEBUGASSERT(priv);
+
+  /* Only start BUS-OFF recovery if we are in BUS-OFF state */
+
+  regval = fdcan_getreg(priv, STM32_FDCAN_PSR_OFFSET);
+  if ((regval & FDCAN_PSR_BO) == 0)
+    {
+      return -EPERM;
+    }
+
+  /* Disable initialization mode to issue the recovery sequence */
+
+  regval = fdcan_getreg(priv, STM32_FDCAN_CCCR_OFFSET);
+  regval &= ~FDCAN_CCCR_INIT;
+  fdcan_putreg(priv, STM32_FDCAN_CCCR_OFFSET, regval);
+
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Name: fdcan_reset
+ *
+ * Description:
+ *   Reset the FDCAN device.  Called early to initialize the hardware. This
+ *   function is called, before fdcan_setup() and on error conditions.
+ *
+ * Input Parameters:
+ *   dev - An instance of the "upper half" can driver state structure.
+ *
+ * Returned Value:
+ *  None
+ *
+ ****************************************************************************/
+
+static void fdcan_reset(FAR struct stm32_fdcan_s *priv)
+{
+  FAR const struct stm32_config_s *config = NULL;
+  uint32_t                         regval = 0;
+  irqstate_t                       flags;
+
+  DEBUGASSERT(priv);
+  config = priv->config;
+  DEBUGASSERT(config);
+
+  ninfo("FDCAN%d\n", config->port);
+  UNUSED(config);
+
+  /* Disable all interrupts */
+
+  fdcan_putreg(priv, STM32_FDCAN_IE_OFFSET, 0);
+  fdcan_putreg(priv, STM32_FDCAN_TXBTIE_OFFSET, 0);
+
+  /* Make sure that all buffers are released.
+   *
+   * REVISIT: What if a thread is waiting for a buffer?  The following
+   * will not wake up any waiting threads.
+   */
+
+  /* Disable the FDCAN controller.
+   * REVISIT: Should fdcan_shutdown() be called here?
+   */
+
+  /* Reset the FD CAN.
+   * REVISIT:  Since there is only a single reset for both FDCAN
+   * controllers, do we really want to use the RCC reset here?
+   * This will nuke operation of the second controller if another
+   * device is registered.
+   */
+
+  flags = enter_critical_section();
+  regval  = getreg32(STM32_RCC_APB1RSTR1);
+  regval |= RCC_APB1RSTR1_FDCANRST;
+  putreg32(regval, STM32_RCC_APB1RSTR1);
+
+  regval &= ~RCC_APB1RSTR1_FDCANRST;
+  putreg32(regval, STM32_RCC_APB1RSTR1);
+  leave_critical_section(flags);
+
+  priv->state = FDCAN_STATE_RESET;
+}
+
+/****************************************************************************
+ * Name: fdcan_setup
+ *
+ * Description:
+ *   Configure the FDCAN. This method is called the first time that the FDCAN
+ *   device is opened.  This will occur when the port is first opened.
+ *   This setup includes configuring and attaching FDCAN interrupts.
+ *   All FDCAN interrupts are disabled upon return.
+ *
+ * Input Parameters:
+ *   dev - An instance of the "upper half" can driver state structure.
+ *
+ * Returned Value:
+ *   Zero on success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+static int fdcan_setup(FAR struct stm32_fdcan_s *priv)
+{
+  FAR const struct stm32_config_s *config = NULL;
+  int                              ret    = 0;
+
+  DEBUGASSERT(priv);
+  config = priv->config;
+  DEBUGASSERT(config);
+
+  ninfo("FDCAN%d\n", config->port);
+
+  /* FDCAN hardware initialization */
+
+  ret = fdcan_hw_initialize(priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: FDCAN%d H/W initialization failed: %d\n",
+             config->port, ret);
+      return ret;
+    }
+
+  fdcan_dumpregs(priv, "After hardware initialization");
+
+  /* Attach the FDCAN interrupt handlers */
+
+  ret = irq_attach(config->irq0, fdcan_interrupt, priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to attach FDCAN%d line 0 IRQ (%d)",
+      config->port, config->irq0);
+      return ret;
+    }
+
+  ret = irq_attach(config->irq1, fdcan_interrupt, priv);
+  if (ret < 0)
+    {
+      nerr("ERROR: Failed to attach FDCAN%d line 1 IRQ (%d)",
+      config->port, config->irq1);
+      return ret;
+    }
+
+  priv->state = FDCAN_STATE_SETUP;
+
+  /* Enable the interrupts at the NVIC (they are still disabled at the FDCAN
+   * peripheral).
+   */
+
+  up_enable_irq(config->irq0);
+  up_enable_irq(config->irq1);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: fdcan_shutdown
+ *
+ * Description:
+ *   Disable the FDCAN.  This method is called when the FDCAN device
+ *   is closed. This method reverses the operation the setup method.
+ *
+ * Input Parameters:
+ *   dev - An instance of the "upper half" can driver state structure.
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void fdcan_shutdown(FAR struct stm32_fdcan_s *priv)
+{
+  FAR const struct stm32_config_s *config = NULL;
+  uint32_t                         regval = 0;
+
+  DEBUGASSERT(priv);
+  config = priv->config;
+  DEBUGASSERT(config);
+
+  ninfo("FDCAN%d\n", config->port);
+
+  /* Disable FDCAN interrupts at the NVIC */
+
+  up_disable_irq(config->irq0);
+  up_disable_irq(config->irq1);
+
+  /* Disable all interrupts from the FDCAN peripheral */
+
+  fdcan_putreg(priv, STM32_FDCAN_IE_OFFSET, 0);
+  fdcan_putreg(priv, STM32_FDCAN_TXBTIE_OFFSET, 0);
+
+  /* Detach the FDCAN interrupt handler */
+
+  irq_detach(config->irq0);
+  irq_detach(config->irq1);
+
+  /* Disable device by setting the Clock Stop Request bit */
+
+  regval = fdcan_getreg(priv, STM32_FDCAN_CCCR_OFFSET);
+  regval |= FDCAN_CCCR_CSR;
+  fdcan_putreg(priv, STM32_FDCAN_CCCR_OFFSET, regval);
+
+  /* Wait for Init and Clock Stop Acknowledge bits to verify
+   * device is in the powered down state
+   */
+
+  while ((fdcan_getreg(priv, STM32_FDCAN_CCCR_OFFSET) & FDCAN_CCCR_INIT)
+         == 0);
+  while ((fdcan_getreg(priv, STM32_FDCAN_CCCR_OFFSET) & FDCAN_CCCR_CSA)
+         == 0);
+  priv->state = FDCAN_STATE_DISABLED;
+}
+
+/****************************************************************************
+ * Name: fdcan_rx0int
+ *
+ * Description:
+ *   Call to enable or disable RX0 interrupts.
+ *
+ * Input Parameters:
+ *   priv - reference to the private CAN driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void fdcan_rx0int(FAR struct stm32_fdcan_s *priv, bool enable)
+{
+  FAR const struct stm32_config_s *config = NULL;
+  uint32_t regval = 0;
+
+  DEBUGASSERT(priv);
+  config = priv->config;
+  DEBUGASSERT(config);
+
+  ninfo("CAN%" PRIu8 "RX0 enable: %d\n", config->port, enable);
+
+  /* Enable/disable the FIFO 0 message pending interrupt */
+
+  regval = fdcan_getreg(priv, STM32_FDCAN_IE_OFFSET);
+
+  if (enable)
+    {
+      regval |= FDCAN_RXFIFO0_INTS;
+    }
+  else
+    {
+      regval &= ~FDCAN_RXFIFO0_INTS;
+    }
+
+  fdcan_putreg(priv, STM32_FDCAN_IE_OFFSET, regval);
+}
+
+/****************************************************************************
+ * Name: fdcan_rx1int
+ *
+ * Description:
+ *   Call to enable or disable RX1 interrupts.
+ *
+ * Input Parameters:
+ *   priv - reference to the private CAN driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void fdcan_rx1int(FAR struct stm32_fdcan_s *priv, bool enable)
+{
+  FAR const struct stm32_config_s *config = NULL;
+  uint32_t regval = 0;
+
+  DEBUGASSERT(priv);
+  config = priv->config;
+  DEBUGASSERT(config);
+
+  ninfo("CAN%" PRIu8 "RX1 enable: %d\n", config->port, enable);
+
+  /* Enable/disable the FIFO 1 message pending interrupt */
+
+  regval = fdcan_getreg(priv, STM32_FDCAN_IE_OFFSET);
+
+  if (enable)
+    {
+      regval |= FDCAN_RXFIFO1_INTS;
+    }
+  else
+    {
+      regval &= ~FDCAN_RXFIFO1_INTS;
+    }
+
+  fdcan_putreg(priv, STM32_FDCAN_IE_OFFSET, regval);
+}
+
+/****************************************************************************
+ * Name: fdcan_txint
+ *
+ * Description:
+ *   Call to enable or disable TX interrupts.
+ *
+ * Input Parameters:
+ *   dev - An instance of the "upper half" can driver state structure.
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void fdcan_txint(FAR struct stm32_fdcan_s *priv, bool enable)
+{
+  FAR const struct stm32_config_s *config = NULL;
+  uint32_t regval = 0;
+
+  DEBUGASSERT(priv);
+  config = priv->config;
+  DEBUGASSERT(config);
+
+  ninfo("CAN%" PRIu8 "TX enable: %d\n", config->port, enable);
+
+  /* Enable/disable the receive interrupts */
+
+  regval = fdcan_getreg(priv, STM32_FDCAN_IE_OFFSET);
+
+  if (enable)
+    {
+      regval |= FDCAN_TXFIFOQ_INTS;
+    }
+  else
+    {
+      regval &= ~FDCAN_TXFIFOQ_INTS;
+    }
+
+  fdcan_putreg(priv, STM32_FDCAN_IE_OFFSET, regval);
+}
+
+#ifdef CONFIG_NET_CAN_ERRORS
+/****************************************************************************
+ * Name: fdcan_txint
+ *
+ * Description:
+ *   Call to enable or disable CAN SCE interrupts.
+ *
+ * Input Parameters:
+ *   priv - reference to the private CAN driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void fdcan_errint(FAR struct stm32_fdcan_s *priv, bool enable)
+{
+  FAR const struct stm32_config_s *config = NULL;
+  uint32_t regval = 0;
+
+  DEBUGASSERT(priv);
+  config = priv->config;
+  DEBUGASSERT(config);
+
+  ninfo("CAN%" PRIu8 "ERR enable: %d\n", config->port, enable);
+
+  /* Enable/disable the transmit mailbox interrupt */
+
+  regval  = fdcan_getreg(priv, STM32_FDCAN_IE_OFFSET);
+  if (enable)
+    {
+      regval |= FDCAN_ANYERR_INTS;
+    }
+  else
+    {
+      regval &= ~FDCAN_ANYERR_INTS;
+    }
+
+  fdcan_putreg(priv, STM32_FDCAN_IE_OFFSET, regval);
+}
+#endif
+
+/****************************************************************************
+ * Name: fdcan_send
+ *
+ * Description:
+ *    Send one can message.
+ *
+ *    One CAN-message consists of a maximum of 10 bytes.  A message is
+ *    composed of at least the first 2 bytes (when there are no data bytes).
+ *
+ *    Byte 0:      Bits 0-7: Bits 3-10 of the 11-bit CAN identifier
+ *    Byte 1:      Bits 5-7: Bits 0-2 of the 11-bit CAN identifier
+ *                 Bit 4:    Remote Transmission Request (RTR)
+ *                 Bits 0-3: Data Length Code (DLC)
+ *    Bytes 2-10: CAN data
+ *
+ * Input Parameters:
+ *   dev - An instance of the "upper half" can driver state structure.
+ *
+ * Returned Value:
+ *   Zero on success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+static int fdcan_send(FAR struct stm32_fdcan_s *priv)
+{
+  struct can_frame                *frame      = NULL;
+  FAR const struct stm32_config_s *config     = NULL;
+  FAR volatile uint32_t           *txbuffer   = NULL;
+  FAR const uint8_t               *src        = NULL;
+  FAR uint32_t                    *dest       = NULL;
+  uint32_t                         regval     = 0;
+  unsigned int                     ndx        = 0;
+  unsigned int                     nbytes     = 0;
+  uint32_t                         wordbuffer = 0;
+  unsigned int                     i          = 0;
+
+  DEBUGASSERT(priv);
+  config = priv->config;
+  DEBUGASSERT(config);
+
+  frame = (struct can_frame *)priv->dev.d_buf;
+
+  ninfo("CAN%" PRIu8 " ID: %" PRIu32 " DLC: %" PRIu8 "\n",
+        config->port, (uint32_t)frame->can_id, frame->can_dlc);
+
+  fdcan_dumptxregs(priv, "Before send");
+
+  /* That that FIFO elements were configured */
+
+  DEBUGASSERT(config->ntxfifoq > 0);
+
+  /* Get our reserved Tx FIFO/queue put index */
+
+  regval = fdcan_getreg(priv, STM32_FDCAN_TXFQS_OFFSET);
+  DEBUGASSERT((regval & FDCAN_TXFQS_TFQF) == 0);
+
+  ndx = (regval & FDCAN_TXFQS_TFQPI_MASK) >> FDCAN_TXFQS_TFQPI_SHIFT;
+
+  /* And the TX buffer corresponding to this index */
+
+  txbuffer = (config->msgram.txfifoq + ndx * config->txbufferesize);
+
+  /* Format the TX FIFOQ entry
+   *
+   * Format word T0:
+   *   Transfer message ID (ID)          - Value from message structure
+   *   Remote Transmission Request (RTR) - Value from message structure
+   *   Extended Identifier (XTD)         - Depends on configuration.
+   */
+
+#ifdef CONFIG_CAN_EXTID
+  if (frame->can_id & CAN_EFF_FLAG)
+    {
+      DEBUGASSERT(frame->can_id < (1 << 29));
+
+      regval = BUFFER_R0_EXTID(frame->can_id) | BUFFER_R0_XTD;
+    }
+  else
+#endif
+    {
+      DEBUGASSERT(frame->can_id < (1 << 11));
+
+      regval = BUFFER_R0_STDID(frame->can_id);
+    }
+
+  if (frame->can_id & CAN_RTR_FLAG)
+    {
+      regval |= BUFFER_R0_RTR;
+    }
+
+  txbuffer[0] = regval;
+
+  /* Format word T1:
+   *   Data Length Code (DLC)            - Value from message structure
+   *   Event FIFO Control (EFC)          - Do not store events.
+   *   Message Marker (MM)               - Always zero
+   */
+
+  txbuffer[1] = BUFFER_R1_DLC(frame->can_dlc);
+
+  /* Followed by the amount of data corresponding to the DLC (T2..) */
+
+  dest   = (FAR uint32_t *)&txbuffer[2];
+  src    = frame->data;
+  nbytes = fdcan_dlc2bytes(priv, frame->can_dlc);
+
+  /* Writes must be word length */
+
+  for (i = 0; i < nbytes; i += 4)
+    {
+      /* Little endian is assumed */
+
+      wordbuffer = src[0] |
+                  (src[1] << 8) |
+                  (src[2] << 16) |
+                  (src[3] << 24);
+      src += 4;
+
+      *dest++ = wordbuffer;
+    }
+
+  /* Enable transmit interrupts from the TX FIFOQ buffer by setting TC
+   * interrupt bit in IR (also requires that the TC interrupt is enabled)
+   */
+
+  fdcan_putreg(priv, STM32_FDCAN_TXBTIE_OFFSET, (1 << ndx));
+
+  /* And request to send the packet */
+
+  fdcan_putreg(priv, STM32_FDCAN_TXBAR_OFFSET, (1 << ndx));
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: fdcan_txready
+ *
+ * Description:
+ *   Return true if the FDCAN hardware can accept another TX message.
+ *
+ * Input Parameters:
+ *   dev - An instance of the "upper half" can driver state structure.
+ *
+ * Returned Value:
+ *   True if the FDCAN hardware is ready to accept another TX message.
+ *
+ ****************************************************************************/
+
+static bool fdcan_txready(FAR struct stm32_fdcan_s *priv)
+{
+  uint32_t regval  = 0;
+  bool     notfull = false;
+
+  /* Return the state of the TX FIFOQ.  Return TRUE if the TX FIFO/Queue is
+   * not full.
+   */
+
+  regval  = fdcan_getreg(priv, STM32_FDCAN_TXFQS_OFFSET);
+  notfull = ((regval & FDCAN_TXFQS_TFQF) == 0);
+
+  return notfull;
+}
+
+/****************************************************************************
+ * Name: fdcan_rx0interrupt_work
+ *
+ * Description:
+ *   CAN RX FIFO 0 worker
+ *
+ ****************************************************************************/
+
+static void fdcan_rx0interrupt_work(FAR void *arg)
+{
+  FAR struct stm32_fdcan_s        *priv   = (FAR struct stm32_fdcan_s *)arg;
+  FAR const struct stm32_config_s *config = NULL;
+  uint32_t                         regval = 0;
+  unsigned int                     nelem  = 0;
+  unsigned int                     ndx    = 0;
+
+  DEBUGASSERT(priv);
+  config = priv->config;
+  DEBUGASSERT(config);
+
+  /* Clear the RX FIFO0 new message interrupt */
+
+  fdcan_putreg(priv, STM32_FDCAN_IR_OFFSET, FDCAN_INT_RF0N);
+
+  regval = fdcan_getreg(priv, STM32_FDCAN_RXF0S_OFFSET);
+  nelem  = (regval & FDCAN_RXFS_FFL_MASK) >> FDCAN_RXFS_FFL_SHIFT;
+  if (nelem > 0)
+    {
+      /* Handle the newly received message in FIFO0 */
+
+      ndx = (regval & FDCAN_RXFS_FGI_MASK) >> FDCAN_RXFS_FGI_SHIFT;
+
+      if ((regval & FDCAN_RXFS_RFL) != 0)
+        {
+          nerr("ERROR: Message lost: %08" PRIx32 "\n", regval);
+        }
+      else
+        {
+          fdcan_receive(priv,
+                        config->msgram.rxfifo0 +
+                        (ndx * priv->config->rxfifo0esize),
+                        priv->config->rxfifo0esize);
+
+#ifdef CONFIG_NET_CAN_ERRORS
+          /* Turning back on all configured RX error interrupts */
+
+          regval = fdcan_getreg(priv, STM32_FDCAN_IE_OFFSET);
+          regval |= FDCAN_RXERR_INTS;
+          fdcan_putreg(priv, STM32_FDCAN_IE_OFFSET, regval);
+#endif
+        }
+
+      /* Acknowledge reading the FIFO entry */
+
+      fdcan_putreg(priv, STM32_FDCAN_RXF0A_OFFSET, ndx);
+    }
+
+  /* Re-enable CAN RX interrupts */
+
+  fdcan_rx0int(priv, true);
+}
+
+/****************************************************************************
+ * Name: fdcan_rx1interrupt_work
+ *
+ * Description:
+ *   CAN RX FIFO 1 worker
+ *
+ ****************************************************************************/
+
+static void fdcan_rx1interrupt_work(FAR void *arg)
+{
+  FAR struct stm32_fdcan_s        *priv   = (FAR struct stm32_fdcan_s *)arg;
+  FAR const struct stm32_config_s *config = NULL;
+  uint32_t                         regval = 0;
+  unsigned int                     nelem  = 0;
+  unsigned int                     ndx    = 0;
+
+  DEBUGASSERT(priv);
+  config = priv->config;
+  DEBUGASSERT(config);
+
+  /* Clear the RX FIFO1 new message interrupt */
+
+  fdcan_putreg(priv, STM32_FDCAN_IR_OFFSET, FDCAN_INT_RF1N);
+
+  /* Check if there is anything in RX FIFO1 */
+
+  regval = fdcan_getreg(priv, STM32_FDCAN_RXF1S_OFFSET);
+  nelem  = (regval & FDCAN_RXFS_FFL_MASK) >> FDCAN_RXFS_FFL_SHIFT;
+  if (nelem == 0)
+    {
+      /* Clear the RX FIFO1 interrupt (and all other FIFO1-related
+       * interrupts)
+       */
+
+      /* Handle the newly received message in FIFO1 */
+
+      ndx = (regval & FDCAN_RXFS_FGI_MASK) >> FDCAN_RXFS_FGI_SHIFT;
+
+      if ((regval & FDCAN_RXFS_RFL) != 0)
+        {
+          nerr("ERROR: Message lost: %08" PRIx32 "\n", regval);
+        }
+      else
+        {
+          fdcan_receive(priv,
+                        config->msgram.rxfifo1 +
+                        (ndx * priv->config->rxfifo1esize),
+                        priv->config->rxfifo1esize);
+
+#ifdef CONFIG_NET_CAN_ERRORS
+          /* Turning back on all configured RX error interrupts */
+
+          regval = fdcan_getreg(priv, STM32_FDCAN_IE_OFFSET);
+          regval |= FDCAN_RXERR_INTS;
+          fdcan_putreg(priv, STM32_FDCAN_IE_OFFSET, regval);
+#endif
+        }
+
+      /* Acknowledge reading the FIFO entry */
+
+      fdcan_putreg(priv, STM32_FDCAN_RXF1A_OFFSET, ndx);
+    }
+
+  /* Re-enable CAN RX interrupts */
+
+  fdcan_rx1int(priv, true);
+}
+
+/****************************************************************************
+ * Name: fdcan_txdone_work
+ ****************************************************************************/
+
+static void fdcan_txdone_work(FAR void *arg)
+{
+  FAR struct stm32_fdcan_s *priv = (FAR struct stm32_fdcan_s *)arg;
+
+  fdcan_txdone(priv);
+
+  /* There should be space for a new TX in any event.  Poll the network for
+   * new XMIT data
+   */
+
+  net_lock();
+  devif_timer(&priv->dev, 0, fdcan_txpoll);
+  net_unlock();
+}
+
+/****************************************************************************
+ * Name: fdcan_txdone
+ ****************************************************************************/
+
+static void fdcan_txdone(FAR struct stm32_fdcan_s *priv)
+{
+  FAR const struct stm32_config_s *config = NULL;
+  unsigned int                     ndx    = 0;
+  uint32_t                         regval = 0;
+
+  DEBUGASSERT(priv);
+  config = priv->config;
+  DEBUGASSERT(config);
+
+  /* Clear the pending TX completion interrupt (and all
+   * other TX-related interrupts)
+   */
+
+  fdcan_putreg(priv, STM32_FDCAN_IR_OFFSET, FDCAN_TXFIFOQ_INTS);
+
+  /* Check all TX buffers */
+
+  regval = fdcan_getreg(priv, STM32_FDCAN_TXBTO_OFFSET);
+  for (ndx = 0; ndx < config->ntxfifoq; ndx++)
+    {
+      if ((regval & (1 << ndx)) != 0)
+        {
+          /* Tell the upper half that the transfer is finished. */
+
+          NETDEV_TXDONE(&priv->dev);
+        }
+    }
+
+#ifdef CONFIG_NET_CAN_ERRORS
+  /* Turning back on PEA and PED error interrupts */
+
+  regval = fdcan_getreg(priv, STM32_FDCAN_IE_OFFSET);
+  regval |= (FDCAN_INT_PEA | FDCAN_INT_PED);
+  fdcan_putreg(priv, STM32_FDCAN_IE_OFFSET, regval);
+#endif
+
+  /* Re-enable TX interrupts */
+
+  fdcan_txint(priv, true);
+}
+
+#ifdef CONFIG_NET_CAN_ERRORS
+/****************************************************************************
+ * Name: fdcan_error_work
+ ****************************************************************************/
+
+static void fdcan_error_work(FAR void *arg)
+{
+  FAR struct stm32_fdcan_s *priv    = (FAR struct stm32_fdcan_s *)arg;
+  uint32_t                  pending = 0;
+  uint32_t                  ir      = 0;
+  uint32_t                  ie      = 0;
+  uint32_t                  psr     = 0;
+
+  /* Get the set of pending interrupts. */
+
+  ir = fdcan_getreg(priv, STM32_FDCAN_IR_OFFSET);
+  ie = fdcan_getreg(priv, STM32_FDCAN_IE_OFFSET);
+
+  pending = (ir & ie);
+
+  /* Check for common errors */
+
+  if ((pending & FDCAN_CMNERR_INTS) != 0)
+    {
+      /* When a protocol error ocurrs, the problem is recorded in
+       * the LEC/DLEC fields of the PSR register. In lieu of
+       * seprate interrupt flags for each error, the hardware
+       * groups procotol errors under a single interrupt each for
+       * arbitration and data phases.
+       *
+       * These errors have a tendency to flood the system with
+       * interrupts, so they are disabled here until we get a
+       * successful transfer/receive on the hardware
+       */
+
+      psr = fdcan_getreg(priv, STM32_FDCAN_PSR_OFFSET);
+
+      if ((psr & FDCAN_PSR_LEC_MASK) != 0)
+        {
+          ie &= ~(FDCAN_INT_PEA | FDCAN_INT_PED);
+          fdcan_putreg(priv, STM32_FDCAN_IE_OFFSET, ie);
+        }
+
+      /* Clear the error indications */
+
+      fdcan_putreg(priv, STM32_FDCAN_IR_OFFSET, FDCAN_CMNERR_INTS);
+    }
+
+  /* Check for transmission errors */
+
+  if ((pending & FDCAN_TXERR_INTS) != 0)
+    {
+      /* An Acknowledge-Error will occur if for example the device
+       * is not connected to the bus.
+       *
+       * The CAN-Standard states that the Chip has to retry the
+       * message forever, which will produce an ACKE every time.
+       * To prevent this Interrupt-Flooding and the high CPU-Load
+       * we disable the ACKE here as long we didn't transfer at
+       * least one message successfully (see FDCAN_INT_TC below).
+       */
+
+      /* Clear the error indications */
+
+      fdcan_putreg(priv, STM32_FDCAN_IR_OFFSET, FDCAN_TXERR_INTS);
+    }
+
+  /* Check for reception errors */
+
+  if ((pending & FDCAN_RXERR_INTS) != 0)
+    {
+      /* To prevent Interrupt-Flooding the current active
+       * RX error interrupts are disabled. After successfully
+       * receiving at least one CAN packet all RX error interrupts
+       * are turned back on.
+       *
+       * The Interrupt-Flooding can for example occur if the
+       * configured CAN speed does not match the speed of the other
+       * CAN nodes in the network.
+       */
+
+      ie &= ~(pending & FDCAN_RXERR_INTS);
+      fdcan_putreg(priv, STM32_FDCAN_IE_OFFSET, ie);
+
+      /* Clear the error indications */
+
+      fdcan_putreg(priv, STM32_FDCAN_IR_OFFSET, FDCAN_RXERR_INTS);
+    }
+
+  /* Report errors */
+
+  fdcan_error(priv, pending & FDCAN_ANYERR_INTS);
+
+  /* Re-enable ERROR interrupts */
+
+  fdcan_errint(priv, true);
+}
+
+/****************************************************************************
+ * Name: fdcan_error
+ *
+ * Description:
+ *   Report a CAN error
+ *
+ * Input Parameters:
+ *   dev        - CAN-common state data
+ *   status     - Interrupt status with error bits set
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void fdcan_error(FAR struct stm32_fdcan_s *priv, uint32_t status)
+{
+  struct can_frame *frame   = (struct can_frame *)priv->rxdesc;
+  uint32_t          psr     = 0;
+  uint16_t          errbits = 0;
+  uint8_t           data[CAN_ERR_DLC];
+
+  DEBUGASSERT(priv != NULL);
+
+  /* Encode error bits */
+
+  errbits = 0;
+  memset(data, 0, sizeof(data));
+
+  /* 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.
+   */
+
+  psr = fdcan_getreg(priv, STM32_FDCAN_PSR_OFFSET);
+  if ((psr & FDCAN_PSR_EP) != 0)
+    {
+      data[1] |= (CAN_ERR_CRTL_RX_PASSIVE | CAN_ERR_CRTL_TX_PASSIVE);
+    }
+
+  if ((psr & FDCAN_PSR_EW) != 0)
+    {
+      data[1] |= (CAN_ERR_CRTL_RX_WARNING | CAN_ERR_CRTL_TX_WARNING);
+    }
+
+  if ((status & (FDCAN_INT_EP | FDCAN_INT_EW)) != 0)
+    {
+      /* "Error Passive" or "Error Warning" status changed */
+
+      errbits |= CAN_ERR_CRTL;
+    }
+
+  if ((status & FDCAN_INT_PEA) != 0)
+    {
+      /* Protocol Error in Arbitration Phase */
+
+      if ((psr & FDCAN_PSR_LEC_MASK) != 0)
+        {
+          /* Error code present */
+
+          if ((psr & FDCAN_PSR_LEC(FDCAN_PSR_EC_STUFF_ERROR)) != 0)
+            {
+              /* Stuff Error */
+
+              errbits |= CAN_ERR_PROT;
+              data[2] |= CAN_ERR_PROT_STUFF;
+            }
+
+          if ((psr & FDCAN_PSR_LEC(FDCAN_PSR_EC_FORM_ERROR)) != 0)
+            {
+              /* Format Error */
+
+              errbits |= CAN_ERR_PROT;
+              data[2] |= CAN_ERR_PROT_FORM;
+            }
+
+          if ((psr & FDCAN_PSR_LEC(FDCAN_PSR_EC_ACK_ERROR)) != 0)
+            {
+              /* Acknowledge Error */
+
+              errbits |= CAN_ERR_ACK;
+            }
+
+          if ((psr & FDCAN_PSR_LEC(FDCAN_PSR_EC_BIT0_ERROR)) != 0)
+            {
+              /* Bit0 Error */
+
+              errbits |= CAN_ERR_PROT;
+              data[2] |= CAN_ERR_PROT_BIT0;
+            }
+
+          if ((psr & FDCAN_PSR_LEC(FDCAN_PSR_EC_BIT1_ERROR)) != 0)
+            {
+              /* Bit1 Error */
+
+              errbits |= CAN_ERR_PROT;
+              data[2] |= CAN_ERR_PROT_BIT1;
+            }
+
+          if ((psr & FDCAN_PSR_LEC(FDCAN_PSR_EC_CRC_ERROR)) != 0)
+            {
+              /* Receive CRC Error */
+
+              errbits |= CAN_ERR_PROT;
+              data[3] |= (CAN_ERR_PROT_LOC_CRCSEQ | CAN_ERR_PROT_LOC_CRCDEL);
+            }
+
+          if ((psr & FDCAN_PSR_LEC(FDCAN_PSR_EC_NO_CHANGE)) != 0)
+            {
+              /* No Change in Error */
+
+              errbits |= CAN_ERR_PROT;
+              data[2] |= CAN_ERR_PROT_UNSPEC;
+            }
+        }
+    }
+
+  if ((status & FDCAN_INT_PED) != 0)
+    {
+      /* Protocol Error in Data Phase */
+
+      if ((psr & FDCAN_PSR_DLEC_MASK) != 0)
+        {
+          /* Error code present */
+
+          if ((psr & FDCAN_PSR_DLEC(FDCAN_PSR_EC_STUFF_ERROR)) != 0)
+            {
+              /* Stuff Error */
+
+              errbits |= CAN_ERR_PROT;
+              data[2] |= CAN_ERR_PROT_STUFF;
+            }
+
+          if ((psr & FDCAN_PSR_DLEC(FDCAN_PSR_EC_FORM_ERROR)) != 0)
+            {
+              /* Format Error */
+
+              errbits |= CAN_ERR_PROT;
+              data[2] |= CAN_ERR_PROT_FORM;
+            }
+
+          if ((psr & FDCAN_PSR_DLEC(FDCAN_PSR_EC_ACK_ERROR)) != 0)
+            {
+              /* Acknowledge Error */
+
+              errbits |= CAN_ERR_ACK;
+            }
+
+          if ((psr & FDCAN_PSR_DLEC(FDCAN_PSR_EC_BIT0_ERROR)) != 0)
+            {
+              /* Bit0 Error */
+
+              errbits |= CAN_ERR_PROT;
+              data[2] |= CAN_ERR_PROT_BIT0;
+            }
+
+          if ((psr & FDCAN_PSR_DLEC(FDCAN_PSR_EC_BIT1_ERROR)) != 0)
+            {
+              /* Bit1 Error */
+
+              errbits |= CAN_ERR_PROT;
+              data[2] |= CAN_ERR_PROT_BIT1;
+            }
+
+          if ((psr & FDCAN_PSR_DLEC(FDCAN_PSR_EC_CRC_ERROR)) != 0)
+            {
+              /* Receive CRC Error */
+
+              errbits |= CAN_ERR_PROT;
+              data[3] |= (CAN_ERR_PROT_LOC_CRCSEQ | CAN_ERR_PROT_LOC_CRCDEL);
+            }
+
+          if ((psr & FDCAN_PSR_DLEC(FDCAN_PSR_EC_NO_CHANGE)) != 0)
+            {
+              /* No Change in Error */
+
+              errbits |= CAN_ERR_PROT;
+              data[2] |= CAN_ERR_PROT_UNSPEC;
+            }
+        }
+    }
+
+  if ((status & FDCAN_INT_BO) != 0)
+    {
+      /* Bus_Off Status changed */
+
+      if ((psr & FDCAN_PSR_BO) != 0)
+        {
+          errbits |= CAN_ERR_BUSOFF;
+        }
+      else
+        {
+          errbits |= CAN_ERR_RESTARTED;
+        }
+    }
+
+  if ((status & (FDCAN_INT_RF0L | FDCAN_INT_RF1L)) != 0)
+    {
+      /* Receive FIFO 0/1 Message Lost
+       * Receive FIFO 1 Message Lost
+       */
+
+      errbits |= CAN_ERR_CRTL;
+      data[1] |= CAN_ERR_CRTL_RX_OVERFLOW;
+    }
+
+  if ((status & FDCAN_INT_TEFL) != 0)
+    {
+      /* Tx Event FIFO Element Lost */
+
+      errbits |= CAN_ERR_CRTL;
+      data[1] |= CAN_ERR_CRTL_TX_OVERFLOW;
+    }
+
+  if ((status & FDCAN_INT_TOO) != 0)
+    {
+      /* Timeout Occurred */
+
+      errbits |= CAN_ERR_TXTIMEOUT;
+    }
+
+  if ((status & (FDCAN_INT_MRAF | FDCAN_INT_ELO)) != 0)
+    {
+      /* Message RAM Access Failure
+       * Error Logging Overflow
+       */
+
+      errbits |= CAN_ERR_CRTL;
+      data[1] |= CAN_ERR_CRTL_UNSPEC;
+    }
+
+  if (errbits != 0)
+    {
+      nerr("ERROR: errbits = %08" PRIx16 "\n", errbits);
+
+      /* Copy frame */
+
+      frame->can_id  = errbits;
+      frame->can_dlc = CAN_ERR_DLC;
+
+      memcpy(frame->data, data, CAN_ERR_DLC);
+
+      /* 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 */
+
+      NETDEV_ERRORS(&priv->dev);
+
+      can_input(&priv->dev);
+
+      /* Point the packet buffer back to the next Tx buffer that will be
+       * used during the next write.  If the write queue is full, then
+       * this will point at an active buffer, which must not be written
+       * to.  This is OK because devif_poll won't be called unless the
+       * queue is not full.
+       */
+
+      priv->dev.d_buf = (uint8_t *)priv->txdesc;
+    }
+}
+#endif /* CONFIG_NET_CAN_ERRORS */
+
+/****************************************************************************
+ * Name: fdcan_receive
+ *
+ * Description:
+ *   Receive an FDCAN messages
+ *
+ * Input Parameters:
+ *   dev      - CAN-common state data
+ *   rxbuffer - The RX buffer containing the received messages
+ *   nwords   - The length of the RX buffer (element size in words).
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void fdcan_receive(FAR struct stm32_fdcan_s *priv,
+                          FAR volatile uint32_t *rxbuffer,
+                          unsigned long nwords)
+{
+  FAR struct can_frame *frame  = (struct can_frame *)priv->rxdesc;
+  uint32_t              regval = 0;
+
+  fdcan_dumprxregs(dev->cd_priv, "Before receive");
+
+  /* Format the CAN header */
+
+  /* Work R0 contains the CAN ID */
+
+  regval = *rxbuffer++;
+
+  /* Extract the RTR bit */
+
+  if ((regval & BUFFER_R0_RTR) != 0)
+    {
+      frame->can_id |= CAN_RTR_FLAG;
+    }
+
+#ifdef CONFIG_CAN_EXTID
+  if ((regval & BUFFER_R0_XTD) != 0)
+    {
+      /* Save the extended ID of the newly received message */
+
+      frame->can_id  = (regval & BUFFER_R0_EXTID_MASK) >>
+                        BUFFER_R0_EXTID_SHIFT;
+      frame->can_id |= CAN_EFF_FLAG;
+    }
+  else
+    {
+      frame->can_id  = (regval & BUFFER_R0_STDID_MASK) >>
+                        BUFFER_R0_STDID_SHIFT;
+      frame->can_id &= ~CAN_EFF_FLAG;
+    }
+#else
+  if ((regval & BUFFER_R0_XTD) != 0)
+    {
+      /* Drop any messages with extended IDs */
+
+      return;
+    }
+
+  /* Save the standard ID of the newly received message */
+
+  frame->can_id = (regval & BUFFER_R0_STDID_MASK) >> BUFFER_R0_STDID_SHIFT;
+#endif
+
+  /* Word R1 contains the DLC and timestamp */
+
+  regval = *rxbuffer++;
+
+  frame->can_dlc = (regval & BUFFER_R1_DLC_MASK) >> BUFFER_R1_DLC_SHIFT;
+
+  /* Save the message data */
+
+  memcpy(frame->data, (void *)rxbuffer, frame->can_dlc);
+
+  /* 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 */
+
+  NETDEV_RXPACKETS(&priv->dev);
+
+  can_input(&priv->dev);
+
+  /* Point the packet buffer back to the next Tx buffer that will be
+   * used during the next write.  If the write queue is full, then
+   * this will point at an active buffer, which must not be written
+   * to.  This is OK because devif_poll won't be called unless the
+   * queue is not full.
+   */
+
+  priv->dev.d_buf = (uint8_t *)priv->txdesc;
+}
+
+/****************************************************************************
+ * Name: fdcan_interrupt
+ *
+ * Description:
+ *   Common FDCAN interrupt handler
+ *
+ *   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 fdcan_interrupt(int irq, void *context, FAR void *arg)
+{
+  FAR struct stm32_fdcan_s *priv    = (FAR struct stm32_fdcan_s *)arg;
+  uint32_t                  pending = 0;
+
+  DEBUGASSERT(priv != NULL);
+
+  /* Get the set of pending interrupts. */
+
+  pending = fdcan_getreg(priv, STM32_FDCAN_IR_OFFSET);
+
+#ifdef CONFIG_NET_CAN_ERRORS
+  /* Check for any errors */
+
+  if ((pending & FDCAN_ANYERR_INTS) != 0)
+    {
+      /* Disable further CAN ERROR interrupts and schedule to perform the
+       * interrupt processing on the worker thread
+       */
+
+      fdcan_errint(priv, false);
+      work_queue(CANWORK, &priv->irqwork, fdcan_error_work, priv, 0);
+    }
+#endif
+
+  /* Check for successful completion of a transmission */
+
+  if ((pending & FDCAN_INT_TC) != 0)
+    {
+      /* Disable further TX CAN interrupts. here can be no race
+       * condition here.
+       */
+
+      fdcan_txint(priv, false);
+      work_queue(CANWORK, &priv->irqwork, fdcan_txdone_work, priv, 0);
+    }
+  else if ((pending & FDCAN_TXFIFOQ_INTS) != 0)
+    {
+      /* Clear unhandled TX events */
+
+      fdcan_putreg(priv, STM32_FDCAN_IR_OFFSET, FDCAN_TXFIFOQ_INTS);
+    }
+
+  if (pending & FDCAN_INT_RF1N)
+    {
+      /* Disable further CAN RX interrupts and schedule to perform the
+       * interrupt processing on the worker thread
+       */
+
+      fdcan_rx1int(priv, false);
+      work_queue(CANWORK, &priv->irqwork,
+                 fdcan_rx1interrupt_work, priv, 0);
+    }
+
+  /* Clear the RX FIFO0 new message interrupt */
+
+  if (pending & FDCAN_INT_RF0N)
+    {
+      /* Disable further CAN RX interrupts and schedule to perform the
+       * interrupt processing on the worker thread
+       */
+
+      fdcan_rx0int(priv, false);
+      work_queue(CANWORK, &priv->irqwork,
+                 fdcan_rx0interrupt_work, priv, 0);
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: fdcan_hw_initialize
+ *
+ * Description:
+ *   FDCAN hardware initialization
+ *
+ * Input Parameters:
+ *   priv - A pointer to the private data structure for this FDCAN peripheral
+ *
+ * Returned Value:
+ *   Zero on success; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+static int fdcan_hw_initialize(struct stm32_fdcan_s *priv)
+{
+  FAR const struct stm32_config_s *config = NULL;
+  FAR volatile uint32_t           *msgram = NULL;
+  uint32_t                         regval = 0;
+  uint32_t                         cntr   = 0;
+
+  DEBUGASSERT(priv);
+  config = priv->config;
+  DEBUGASSERT(config);
+
+  ninfo("FDCAN%d\n", config->port);
+
+  /* Clean message RAM */
+
+  msgram = config->msgram.stdfilters;
+  cntr   = (FDCAN_MSGRAM_WORDS + 1);
+  while (cntr > 0)
+    {
+      *msgram++ = 0;
+      cntr--;
+    }
+
+  /* Configure FDCAN pins */
+
+  stm32_configgpio(config->rxpinset);
+  stm32_configgpio(config->txpinset);
+
+  /* Renable device if previosuly disabled in fdcan_shutdown() */
+
+  if (priv->state == FDCAN_STATE_DISABLED)
+    {
+      /* Reset Clock Stop Request bit */
+
+      regval  = fdcan_getreg(priv, STM32_FDCAN_CCCR_OFFSET);
+      regval &= ~FDCAN_CCCR_CSR;
+      fdcan_putreg(priv, STM32_FDCAN_CCCR_OFFSET, regval);
+
+      /* Wait for Clock Stop Acknowledge bit reset to indicate
+       * device is operational
+       */
+
+      while ((fdcan_getreg(priv, STM32_FDCAN_CCCR_OFFSET) & FDCAN_CCCR_CSA)
+             != 0);
+    }
+
+  /* Enable the Initialization state */
+
+  regval  = fdcan_getreg(priv, STM32_FDCAN_CCCR_OFFSET);
+  regval |= FDCAN_CCCR_INIT;
+  fdcan_putreg(priv, STM32_FDCAN_CCCR_OFFSET, regval);
+
+  /* Wait for initialization mode to take effect */
+
+  while ((fdcan_getreg(priv, STM32_FDCAN_CCCR_OFFSET) & FDCAN_CCCR_INIT)
+         == 0);
+
+  /* Enable writing to configuration registers */
+
+  regval  = fdcan_getreg(priv, STM32_FDCAN_CCCR_OFFSET);
+  regval |= FDCAN_CCCR_CCE;
+  fdcan_putreg(priv, STM32_FDCAN_CCCR_OFFSET, regval);
+
+  /* Global Filter Configuration:
+   *
+   *   ANFS=0: Store all non matching standard frame in RX FIFO0
+   *   ANFE=0: Store all non matching extended frame in RX FIFO0
+   */
+
+  regval = FDCAN_RXGFC_ANFE_RX_FIFO0 | FDCAN_RXGFC_ANFS_RX_FIFO0;
+  fdcan_putreg(priv, STM32_FDCAN_RXGFC_OFFSET, regval);
+
+  /* Extended ID Filter AND mask  */
+
+  fdcan_putreg(priv, STM32_FDCAN_XIDAM_OFFSET, 0x1fffffff);
+
+  /* Disable all interrupts  */
+
+  fdcan_putreg(priv, STM32_FDCAN_IE_OFFSET, 0);
+  fdcan_putreg(priv, STM32_FDCAN_TXBTIE_OFFSET, 0);
+
+  /* All interrupts directed to Line 0.  But disable both interrupt lines 0
+   * and 1 for now.
+   *
+   * REVISIT: Only interrupt line 0 is used by this driver.
+   */
+
+  fdcan_putreg(priv, STM32_FDCAN_ILS_OFFSET, 0);
+  fdcan_putreg(priv, STM32_FDCAN_ILE_OFFSET, 0);
+
+  /* Clear all pending interrupts. */
+
+  fdcan_putreg(priv, STM32_FDCAN_IR_OFFSET, FDCAN_INT_ALL);
+
+  /* Configure FDCAN bit timing */
+
+  fdcan_putreg(priv, STM32_FDCAN_NBTP_OFFSET, priv->nbtp);
+  fdcan_putreg(priv, STM32_FDCAN_DBTP_OFFSET, priv->dbtp);
+
+  /* Configure message RAM starting addresses and sizes. */
+
+  regval = FDCAN_RXGFC_LSS(config->nstdfilters);
+  regval |= FDCAN_RXGFC_LSE(config->nextfilters);
+  fdcan_putreg(priv, STM32_FDCAN_RXGFC_OFFSET, regval);
+
+  /* Dump RAM layout */
+
+  fdcan_dumpramlayout(priv);
+
+  /* Configure Message Filters */
+
+  /* Disable all standard filters */
+
+  msgram = config->msgram.stdfilters;
+  cntr   = config->nstdfilters;
+  while (cntr > 0)
+    {
+      *msgram++ = STDFILTER_S0_SFEC_DISABLE;
+      cntr--;
+    }
+
+  /* Disable all extended filters */
+
+  msgram = config->msgram.extfilters;
+  cntr = config->nextfilters;
+  while (cntr > 0)
+    {
+      *msgram = EXTFILTER_F0_EFEC_DISABLE;
+      msgram = msgram + 2;
+      cntr--;
+    }
+
+  /* Input clock divider configuration */
+
+  regval = FDCANCLK_PDIV;
+  fdcan_putreg(priv, STM32_FDCAN_CKDIV_OFFSET, regval);
+
+  /* CC control register */
+
+  regval  = fdcan_getreg(priv, STM32_FDCAN_CCCR_OFFSET);
+  regval &= ~(FDCAN_CCCR_NISO | FDCAN_CCCR_FDOE | FDCAN_CCCR_BRSE);
+
+  /* Select ISO11898-1 or Non ISO Bosch CAN FD Specification V1.0 */
+
+  switch (config->format)
+    {
+      case FDCAN_ISO11898_1_FORMAT:
+        {
+          break;
+        }
+
+      case FDCAN_NONISO_BOSCH_V1_FORMAT:
+        {
+          regval |= FDCAN_CCCR_NISO;
+          break;
+        }
+
+      default:
+        {
+          return -EINVAL;
+        }
+    }
+
+  /* Select Classic CAN mode or FD mode with or without fast bit rate
+   * switching
+   */
+
+  switch (config->mode)
+    {
+      case FDCAN_CLASSIC_MODE:
+        {
+          break;
+        }
+
+#ifdef CONFIG_CAN_FD
+      case FDCAN_FD_MODE:
+        {
+          regval |= FDCAN_CCCR_FDOE;
+          break;
+        }
+
+      case FDCAN_FD_BRS_MODE:
+        {
+          regval |= (FDCAN_CCCR_FDOE | FDCAN_CCCR_BRSE);
+          break;
+        }
+#endif
+
+      default:
+        {
+          return -EINVAL;
+        }
+    }
+
+  /* Set the initial CAN mode */
+
+  fdcan_putreg(priv, STM32_FDCAN_CCCR_OFFSET, regval);
+
+  /* Enable FIFO/Queue mode */
+
+  regval  = fdcan_getreg(priv, STM32_FDCAN_TXBC_OFFSET);
+#ifdef CONFIG_STM32_FDCAN_QUEUE_MODE
+  regval |= FDCAN_TXBC_TFQM;
+#else
+  regval &= ~FDCAN_TXBC_TFQM;
+#endif
+  fdcan_putreg(priv, STM32_FDCAN_TXBC_OFFSET, regval);
+
+#ifdef STM32_FDCAN_LOOPBACK
+  /* Is loopback mode selected for this peripheral? */
+
+  if (config->loopback)
+    {
+      /* FDCAN_CCCR_TEST  - Test mode enable
+       * FDCAN_CCCR_MON   - Bus monitoring mode (for internal loopback)
+       * FDCAN_TEST_LBCK  - Loopback mode
+       */
+
+      regval = fdcan_getreg(priv, STM32_FDCAN_CCCR_OFFSET);
+      regval |= (FDCAN_CCCR_TEST | FDCAN_CCCR_MON);
+      fdcan_putreg(priv, STM32_FDCAN_CCCR_OFFSET, regval);
+
+      regval = fdcan_getreg(priv, STM32_FDCAN_TEST_OFFSET);
+      regval |= FDCAN_TEST_LBCK;
+      fdcan_putreg(priv, STM32_FDCAN_TEST_OFFSET, regval);
+    }
+#endif
+
+  /* Configure interrupt lines */
+
+  /* Direct all interrupts to Line 0.
+   *
+   * Bits in the ILS register correspond to each FDCAN interrupt; A bit
+   * set to '1' is directed to interrupt line 1; a bit cleared to '0'
+   * is directed interrupt line 0.
+   *
+   * REVISIT: Nothing is done here.  Only interrupt line 0 is used by
+   * this driver and ILS was already cleared above.
+   */
+
+  /* Enable only interrupt line 0. */
+
+  fdcan_putreg(priv, STM32_FDCAN_ILE_OFFSET, FDCAN_ILE_EINT0);
+
+  /* Disable initialization mode to enable normal operation */
+
+  regval  = fdcan_getreg(priv, STM32_FDCAN_CCCR_OFFSET);
+  regval &= ~FDCAN_CCCR_INIT;
+  fdcan_putreg(priv, STM32_FDCAN_CCCR_OFFSET, regval);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: fdcan_ifup
+ *
+ * Description:
+ *   NuttX Callback: Bring up the Ethernet interface when an IP address is
+ *   provided
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int fdcan_ifup(struct net_driver_s *dev)
+{
+  FAR struct stm32_fdcan_s *priv =
+    (FAR struct stm32_fdcan_s *)dev->d_private;
+  FAR const struct stm32_config_s *config = NULL;
+
+  DEBUGASSERT(priv);
+  config = priv->config;
+  DEBUGASSERT(config);
+
+  /* Setup CAN */
+
+  fdcan_setup(priv);
+
+  /* Enable interrupts */
+
+  fdcan_rx0int(priv, true);
+  fdcan_rx1int(priv, true);
+  fdcan_txint(priv, true);
+#ifdef CONFIG_NET_CAN_ERRORS
+  fdcan_errint(priv, true);
+#endif
+
+  /* Enable the interrupts at the NVIC */
+
+  up_enable_irq(config->irq0);
+  up_enable_irq(config->irq1);
+
+  priv->bifup = true;
+
+  priv->txdesc = (struct can_frame *)priv->tx_pool;
+  priv->rxdesc = (struct can_frame *)priv->rx_pool;
+
+  priv->dev.d_buf = (uint8_t *)priv->txdesc;
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: fdcan_ifdown
+ *
+ * Description:
+ *   NuttX Callback: Stop the interface.
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int fdcan_ifdown(struct net_driver_s *dev)
+{
+  FAR struct stm32_fdcan_s *priv =
+    (FAR struct stm32_fdcan_s *)dev->d_private;
+
+  /* Disable CAN interrupts */
+
+  fdcan_shutdown(priv);
+
+  /* Reset CAN */
+
+  fdcan_reset(priv);
+
+  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 stm32_fdcan_s *priv =
+    (FAR struct stm32_fdcan_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))
+        {
+          fdcan_txdone(priv);
+
+          /* Send the packet */
+
+          fdcan_send(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_txready(priv) == false)
+            {
+              return -EBUSY;
+            }
+        }
+    }
+
+  /* If zero is returned, the polling will continue until all connections
+   * have been examined.
+   */
+
+  return 0;
+}
+
+/****************************************************************************
+ * Function: fdcan_txavail_work
+ *
+ * Description:
+ *   Perform an out-of-cycle poll on the worker thread.
+ *
+ * Input Parameters:
+ *   arg - Reference to the NuttX driver state structure (cast to void*)
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Called on the higher priority worker thread.
+ *
+ ****************************************************************************/
+
+static void fdcan_txavail_work(FAR void *arg)
+{
+  FAR struct stm32_fdcan_s *priv = (FAR struct stm32_fdcan_s *)arg;
+
+  /* Ignore the notification if the interface is not yet up */
+
+  net_lock();
+  if (priv->bifup)
+    {
+      /* Check if there is room in the hardware to hold another outgoing
+       * packet.
+       */
+
+      if (fdcan_txready(priv))
+        {
+          /* No, there is space for another transfer.  Poll the network for
+           * new XMIT data.
+           */
+
+          devif_timer(&priv->dev, 0, fdcan_txpoll);
+        }
+    }
+
+  net_unlock();
+}
+
+/****************************************************************************
+ * Function: fdcan_txavail
+ *
+ * Description:
+ *   Driver callback invoked when new TX data is available.  This is a
+ *   stimulus perform an out-of-cycle poll and, thereby, reduce the TX
+ *   latency.
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Called in normal user mode
+ *
+ ****************************************************************************/
+
+static int fdcan_txavail(struct net_driver_s *dev)
+{
+  FAR struct stm32_fdcan_s *priv =
+    (FAR struct stm32_fdcan_s *)dev->d_private;
+
+  /* Is our single work structure available?  It may not be if there are
+   * pending interrupt actions and we will have to ignore the Tx
+   * availability action.
+   */
+
+  if (work_available(&priv->pollwork))
+    {
+      /* Schedule to serialize the poll on the worker thread. */
+
+      fdcan_txavail_work(priv);
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Function: fdcan_ioctl
+ *
+ * Description:
+ *   PHY ioctl command handler
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *   cmd  - ioctl command
+ *   arg  - Argument accompanying the command
+ *
+ * Returned Value:
+ *   Zero (OK) on success; a negated errno value on failure.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NETDEV_IOCTL
+static int  fdcan_netdev_ioctl(struct net_driver_s *dev, int cmd,
+                               unsigned long arg);
+{
+  FAR struct stm32_fdcan_s *priv =
+    (FAR struct stm32_fdcan_s *)dev->d_private;
+  int                       ret  = OK;
+
+  DEBUGASSERT(priv);
+
+  switch (cmd)
+    {
+      /* TODO */
+
+      default:
+        ret = -ENOTTY;
+        break;
+    }
+
+  return ret;
+}
+#endif  /* CONFIG_NETDEV_IOCTL */
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: stm32_cansockinitialize
+ *
+ * Description:
+ *   Initialize the selected FDCAN port as CAN socket interface
+ *
+ * Input Parameters:
+ *   Port number (for hardware that has multiple FDCAN interfaces)
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ ****************************************************************************/
+
+int stm32_fdcansockinitialize(int port)
+{
+  FAR struct stm32_fdcan_s        *priv   = NULL;
+  FAR const struct stm32_config_s *config = NULL;
+  int                              ret    = OK;
+
+  ninfo("FDCAN%d\n", port);
+
+  /* Select FDCAN peripheral to be initialized */
+
+#ifdef CONFIG_STM32_FDCAN1
+  if (port == FDCAN1)
+    {
+      /* Select the FDCAN1 device structure */
+
+      priv   = &g_fdcan1priv;
+      config = &g_fdcan1const;
+    }
+  else
+#endif
+#ifdef CONFIG_STM32_FDCAN2
+  if (port == FDCAN2)
+    {
+      /* Select the FDCAN2 device structure */
+
+      priv   = &g_fdcan2priv;
+      config = &g_fdcan2const;
+    }
+  else
+#endif
+#ifdef CONFIG_STM32_FDCAN3
+  if (port == FDCAN3)
+    {
+      /* Select the FDCAN3 device structure */
+
+      priv   = &g_fdcan3priv;
+      config = &g_fdcan3const;
+    }
+  else
+#endif
+    {
+      nerr("ERROR: Unsupported port %d\n", port);
+      ret = -EINVAL;
+      goto errout;
+    }
+
+  /* Perform one time data initialization */
+
+  memset(priv, 0, sizeof(struct stm32_fdcan_s));
+  priv->config = config;
+
+  /* Set the initial bit timing.  This might change subsequently
+   * due to IOCTL command processing.
+   */
+
+  priv->nbtp   = config->nbtp;
+  priv->dbtp   = config->dbtp;
+
+  /* Initialize the driver structure */
+
+  priv->dev.d_ifup    = fdcan_ifup;
+  priv->dev.d_ifdown  = fdcan_ifdown;
+  priv->dev.d_txavail = fdcan_txavail;
+#ifdef CONFIG_NETDEV_IOCTL
+  priv->dev.d_ioctl   = fdcan_netdev_ioctl;
+#endif
+  priv->dev.d_private = priv;
+
+  /* Put the interface in the down state.  This usually amounts to resetting
+   * the device and/or calling fdcan_ifdown().
+   */
+
+  ninfo("callbacks done\n");
+
+  fdcan_ifdown(&priv->dev);
+
+  /* Register the device with the OS so that socket IOCTLs can be performed */
+
+  ret = netdev_register(&priv->dev, NET_LL_CAN);
+
+errout:
+  return ret;
+}