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:19 UTC

[incubator-nuttx] branch master updated (e7953d8 -> bc17834)

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

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


    from e7953d8  semaphore: Change FS_NAMED_SEMPATH from "/var/lock" to "/var/sem"
     new 341bbe3  arch/stm32: add FDCAN SocketCAN support
     new 4f7ab47  boards/arm/stm32g4: normalize CAN configs
     new bc17834  net/can: add an option to control CAN EXTID support

The 3 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 arch/arm/src/stm32/Kconfig                         |   12 +-
 arch/arm/src/stm32/Make.defs                       |    3 +
 arch/arm/src/stm32/stm32_can_sock.c                |    6 +-
 arch/arm/src/stm32/stm32_fdcan.h                   |   22 +
 .../stm32/{stm32_fdcan.c => stm32_fdcan_sock.c}    | 2602 +++++++++-----------
 .../arm/stm32/b-g431b-esc1/configs/can/defconfig   |    1 -
 .../stm32/nucleo-f446re/configs/cansock/defconfig  |    2 +-
 .../arm/stm32/nucleo-g431rb/configs/can/defconfig  |    1 -
 net/can/Kconfig                                    |    7 +
 9 files changed, 1204 insertions(+), 1452 deletions(-)
 copy arch/arm/src/stm32/{stm32_fdcan.c => stm32_fdcan_sock.c} (61%)

[incubator-nuttx] 02/03: boards/arm/stm32g4: normalize CAN configs

Posted by xi...@apache.org.
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 4f7ab47443d495d0166fca3a62b8d1c5b4c6244a
Author: raiden00pl <ra...@railab.me>
AuthorDate: Sat Feb 5 14:46:34 2022 +0100

    boards/arm/stm32g4: normalize CAN configs
---
 boards/arm/stm32/b-g431b-esc1/configs/can/defconfig  | 1 -
 boards/arm/stm32/nucleo-g431rb/configs/can/defconfig | 1 -
 2 files changed, 2 deletions(-)

diff --git a/boards/arm/stm32/b-g431b-esc1/configs/can/defconfig b/boards/arm/stm32/b-g431b-esc1/configs/can/defconfig
index 9908b9d..01fd056 100644
--- a/boards/arm/stm32/b-g431b-esc1/configs/can/defconfig
+++ b/boards/arm/stm32/b-g431b-esc1/configs/can/defconfig
@@ -23,7 +23,6 @@ CONFIG_ARCH_STACKDUMP=y
 CONFIG_BOARD_LOOPSPERMSEC=8499
 CONFIG_BOARD_STM32_BG431BESC1_USE_HSE=y
 CONFIG_BUILTIN=y
-CONFIG_CAN=y
 CONFIG_CAN_ERRORS=y
 CONFIG_CAN_EXTID=y
 CONFIG_DEBUG_FULLOPT=y
diff --git a/boards/arm/stm32/nucleo-g431rb/configs/can/defconfig b/boards/arm/stm32/nucleo-g431rb/configs/can/defconfig
index dd204c9..daf9a6b 100644
--- a/boards/arm/stm32/nucleo-g431rb/configs/can/defconfig
+++ b/boards/arm/stm32/nucleo-g431rb/configs/can/defconfig
@@ -23,7 +23,6 @@ CONFIG_ARCH_STACKDUMP=y
 CONFIG_BOARD_LOOPSPERMSEC=8499
 CONFIG_BOARD_NUCLEO_G431RB_USE_HSE=y
 CONFIG_BUILTIN=y
-CONFIG_CAN=y
 CONFIG_CAN_ERRORS=y
 CONFIG_CAN_EXTID=y
 CONFIG_DEBUG_FULLOPT=y

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

Posted by xi...@apache.org.
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;
+}

[incubator-nuttx] 03/03: net/can: add an option to control CAN EXTID support

Posted by xi...@apache.org.
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 bc178344a9d023f85a89ceb4fa6a83388ccce9b8
Author: raiden00pl <ra...@railab.me>
AuthorDate: Sat Feb 5 16:40:43 2022 +0100

    net/can: add an option to control CAN EXTID support
---
 arch/arm/src/stm32/stm32_can_sock.c                      | 6 +++---
 arch/arm/src/stm32/stm32_fdcan_sock.c                    | 8 ++++----
 boards/arm/stm32/nucleo-f446re/configs/cansock/defconfig | 2 +-
 net/can/Kconfig                                          | 7 +++++++
 4 files changed, 15 insertions(+), 8 deletions(-)

diff --git a/arch/arm/src/stm32/stm32_can_sock.c b/arch/arm/src/stm32/stm32_can_sock.c
index ff13b54..8b35726 100644
--- a/arch/arm/src/stm32/stm32_can_sock.c
+++ b/arch/arm/src/stm32/stm32_can_sock.c
@@ -881,7 +881,7 @@ static int stm32can_transmit(FAR struct stm32_can_s *priv)
 
   /* Set up the ID, standard 11-bit or extended 29-bit. */
 
-#ifdef CONFIG_CAN_EXTID
+#ifdef CONFIG_NET_CAN_EXTID
   regval &= ~CAN_TIR_EXID_MASK;
   if (frame->can_id & CAN_EFF_FLAG)
     {
@@ -1205,7 +1205,7 @@ static int stm32can_rxinterrupt_work(FAR struct stm32_can_s *priv, int rxmb)
 
   regval = stm32can_getreg(priv, STM32_CAN_RIR_OFFSET(rxmb));
 
-#ifdef CONFIG_CAN_EXTID
+#ifdef CONFIG_NET_CAN_EXTID
   if ((regval & CAN_RIR_IDE) != 0)
     {
       frame->can_id  = (regval & CAN_RIR_EXID_MASK) >> CAN_RIR_EXID_SHIFT;
@@ -1277,7 +1277,7 @@ static int stm32can_rxinterrupt_work(FAR struct stm32_can_s *priv, int rxmb)
 
   /* Release the FIFO */
 
-#ifndef CONFIG_CAN_EXTID
+#ifndef CONFIG_NET_CAN_EXTID
 errout:
 #endif
   regval  = stm32can_getreg(priv, STM32_CAN_RFR_OFFSET(rxmb));
diff --git a/arch/arm/src/stm32/stm32_fdcan_sock.c b/arch/arm/src/stm32/stm32_fdcan_sock.c
index fafecbe..6d61d06 100644
--- a/arch/arm/src/stm32/stm32_fdcan_sock.c
+++ b/arch/arm/src/stm32/stm32_fdcan_sock.c
@@ -520,14 +520,14 @@ struct stm32_fdcan_s
   const struct stm32_config_s *config;
 
   uint8_t state;            /* See enum can_state_s */
-#ifdef CONFIG_CAN_EXTID
+#ifdef CONFIG_NET_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
+#ifdef CONFIG_NET_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 */
@@ -1718,7 +1718,7 @@ static int fdcan_send(FAR struct stm32_fdcan_s *priv)
    *   Extended Identifier (XTD)         - Depends on configuration.
    */
 
-#ifdef CONFIG_CAN_EXTID
+#ifdef CONFIG_NET_CAN_EXTID
   if (frame->can_id & CAN_EFF_FLAG)
     {
       DEBUGASSERT(frame->can_id < (1 << 29));
@@ -2409,7 +2409,7 @@ static void fdcan_receive(FAR struct stm32_fdcan_s *priv,
       frame->can_id |= CAN_RTR_FLAG;
     }
 
-#ifdef CONFIG_CAN_EXTID
+#ifdef CONFIG_NET_CAN_EXTID
   if ((regval & BUFFER_R0_XTD) != 0)
     {
       /* Save the extended ID of the newly received message */
diff --git a/boards/arm/stm32/nucleo-f446re/configs/cansock/defconfig b/boards/arm/stm32/nucleo-f446re/configs/cansock/defconfig
index 462bdba..96e4263 100644
--- a/boards/arm/stm32/nucleo-f446re/configs/cansock/defconfig
+++ b/boards/arm/stm32/nucleo-f446re/configs/cansock/defconfig
@@ -24,7 +24,6 @@ CONFIG_ARCH_STACKDUMP=y
 CONFIG_BOARD_LATE_INITIALIZE=y
 CONFIG_BOARD_LOOPSPERMSEC=8499
 CONFIG_BUILTIN=y
-CONFIG_CAN_EXTID=y
 CONFIG_DEBUG_FULLOPT=y
 CONFIG_DEBUG_SYMBOLS=y
 CONFIG_FS_PROCFS=y
@@ -37,6 +36,7 @@ CONFIG_NETDEVICES=y
 CONFIG_NETDEV_IFINDEX=y
 CONFIG_NETDEV_LATEINIT=y
 CONFIG_NET_CAN=y
+CONFIG_NET_CAN_EXTID=y
 CONFIG_NET_SOCKOPTS=y
 CONFIG_NET_STATISTICS=y
 CONFIG_NSH_BUILTIN_APPS=y
diff --git a/net/can/Kconfig b/net/can/Kconfig
index cd29c36..85c77c0 100644
--- a/net/can/Kconfig
+++ b/net/can/Kconfig
@@ -36,6 +36,13 @@ config CAN_CONNS
 	---help---
 		Maximum number of CAN connections (all tasks).
 
+config NET_CAN_EXTID
+	bool "Enable CAN extended IDs"
+	default n
+	---help---
+		Enables support for the 29-bit extended ID in SocketCAN stack.
+		Default Standard 11-bit IDs.
+
 config NET_CAN_CANFD
 	bool "Enable CAN FD support"
 	default y