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

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

commit e14ba2612b1489eb56bb34a56df761eb37e15a05
Author: raiden00pl <[email protected]>
AuthorDate: Mon Oct 12 16:33:54 2026 +0200

    arch/arm/stm32h7: Add FDCAN character driver.
    
    Upper-half CAN driver for FDCAN1-3, selected with
    STM32_FDCAN_CHARDRIVER.  SocketCAN stays the default on H7, so
    existing configurations are not affected.
    
    Based on the STM32H5 FDCAN character driver (arch/arm/src/stm32h5/
    stm32_fdcan.c): driver structure, upper-half ops, filters, error
    reporting and automatic bit timing.  Unlike H5, the H7 FDCAN is the
    full Bosch M_CAN with a configurable message RAM, so the RAM layout,
    register fields and clocking follow the H7 SocketCAN driver
    (stm32_fdcan_sock.c).
    
    * per-instance message RAM regions with 64 byte elements for CAN FD
    * filters change at runtime via reserved accept-all elements
    * TX completion counted from TXBTO of pending buffers only
    * RX FIFOs drained completely on every interrupt
    * connection modes (loopback, silent) via ioctl and Kconfig loopback
    * automatic bit timing from the FDCAN kernel clock (STM32_FDCANCLK)
    
    SocketCAN-only Kconfig options now depend on STM32_FDCAN_SOCKET.
    
    Tested on stm32h735g-dk (FDCAN1/2, internal loopback): examples/can
    with CAN FD and BRS up to 64 bytes, standard and extended filters on
    both RX FIFOs, bursts of 20000 frames without loss.
    
    Assisted-by: Claude:claude-opus-5-5
    Signed-off-by: raiden00pl <[email protected]>
---
 arch/arm/src/common/stm32/Kconfig.fdcan     |   27 +-
 arch/arm/src/stm32h7/CMakeLists.txt         |    6 +-
 arch/arm/src/stm32h7/Make.defs              |    6 +-
 arch/arm/src/stm32h7/hardware/stm32_fdcan.h |  132 ++
 arch/arm/src/stm32h7/stm32_fdcan.c          | 3254 +++++++++++++++++++++++++++
 arch/arm/src/stm32h7/stm32_fdcan.h          |   95 +
 6 files changed, 3511 insertions(+), 9 deletions(-)

diff --git a/arch/arm/src/common/stm32/Kconfig.fdcan 
b/arch/arm/src/common/stm32/Kconfig.fdcan
index 7910ef3b354..71d948cd008 100644
--- a/arch/arm/src/common/stm32/Kconfig.fdcan
+++ b/arch/arm/src/common/stm32/Kconfig.fdcan
@@ -3,7 +3,7 @@
 #
 
 menu "FDCAN Driver Configuration"
-       depends on STM32_HAVE_FDCAN_H7 && (STM32_FDCAN1 || STM32_FDCAN2 || 
STM32_FDCAN3)
+       depends on STM32_HAVE_FDCAN_H7 && STM32_FDCAN_SOCKET && (STM32_FDCAN1 
|| STM32_FDCAN2 || STM32_FDCAN3)
 
 menu "FDCAN1 Configuration"
        depends on STM32_FDCAN1
@@ -70,6 +70,7 @@ endmenu # FDCAN Driver
 choice
        prompt "FDCAN character driver or SocketCAN support"
        depends on STM32_FDCAN
+       default STM32_FDCAN_SOCKET if ARCH_CHIP_STM32H7
        default STM32_FDCAN_CHARDRIVER
 
 config STM32_FDCAN_CHARDRIVER
@@ -183,7 +184,7 @@ config STM32_FDCAN_QUEUE_MODE
 
 config STM32_FDCAN_LOOPBACK
        bool "Enable FDCAN loopback mode"
-       depends on ARCH_CHIP_STM32H7 && STM32_FDCAN
+       depends on ARCH_CHIP_STM32H7 && STM32_FDCAN_SOCKET
        default n
        ---help---
                Enable the FDCAN local loopback mode for testing purposes.
@@ -217,7 +218,7 @@ endchoice # FDCAN Loopback Mode
 
 choice
        prompt "FDCAN WorkQueue Selection"
-       depends on ARCH_CHIP_STM32H7 && STM32_FDCAN
+       depends on ARCH_CHIP_STM32H7 && STM32_FDCAN_SOCKET
        default STM32_FDCAN_LPWORK
 
 config STM32_FDCAN_LPWORK
@@ -250,7 +251,7 @@ config STM32_FDCAN1_BITRATE
 
 config STM32_FDCAN1_AUTO_BIT_TIMING
        bool "FDCAN1 automatic bit timing"
-       depends on ARCH_CHIP_STM32H5
+       depends on ARCH_CHIP_STM32H5 || ARCH_CHIP_STM32H7
        default y
        ---help---
                Automatically determine FDCAN1 bit timing (nominal and data)
@@ -335,7 +336,7 @@ config STM32_FDCAN2_BITRATE
 
 config STM32_FDCAN2_AUTO_BIT_TIMING
        bool "FDCAN2 automatic bit timing"
-       depends on ARCH_CHIP_STM32H5
+       depends on ARCH_CHIP_STM32H5 || ARCH_CHIP_STM32H7
        default y
        ---help---
                Automatically determine FDCAN2 bit timing (nominal and data)
@@ -458,6 +459,16 @@ config STM32_FDCAN3_BITRATE
        ---help---
                FDCAN3 bitrate in bits per second.  Required if STM32_FDCAN3 is 
defined.
 
+config STM32_FDCAN3_AUTO_BIT_TIMING
+       bool "FDCAN3 automatic bit timing"
+       depends on ARCH_CHIP_STM32H7
+       default y
+       ---help---
+               Automatically determine FDCAN3 bit timing (nominal and data)
+               based on bitrate.
+
+if !STM32_FDCAN3_AUTO_BIT_TIMING
+
 comment "FDCAN3 nominal bit timing"
 
 config STM32_FDCAN3_NTSEG1
@@ -481,6 +492,8 @@ config STM32_FDCAN3_NSJW
        ---help---
                The length of the bit time is Tquanta * (SyncSeg + PropSeg + 
PhaseSeg1 + PhaseSeg2).
 
+endif # !STM32_FDCAN3_AUTO_BIT_TIMING
+
 config STM32_FDCAN3_DBITRATE
        int "FDCAN3 data bitrate"
        depends on CAN_FD && STM32_FDCAN3_FD_BRS
@@ -488,7 +501,7 @@ config STM32_FDCAN3_DBITRATE
        ---help---
                FDCAN3 bitrate in bits per second.  Required if operating in FD 
mode with bit rate switching (BRS).
 
-if CAN_FD && STM32_FDCAN3_FD_BRS
+if CAN_FD && STM32_FDCAN3_FD_BRS && !STM32_FDCAN3_AUTO_BIT_TIMING
 
 comment "FDCAN3 data bit timing"
 
@@ -513,6 +526,6 @@ config STM32_FDCAN3_DSJW
        ---help---
                The duration of a synchronization jump is Tcan_clk x DSJW.
 
-endif # CAN_FD && STM32_FDCAN3_FD_BRS
+endif # CAN_FD && STM32_FDCAN3_FD_BRS && !STM32_FDCAN3_AUTO_BIT_TIMING
 
 endif # STM32_FDCAN3
diff --git a/arch/arm/src/stm32h7/CMakeLists.txt 
b/arch/arm/src/stm32h7/CMakeLists.txt
index 7729b756c58..02a83c4a23e 100644
--- a/arch/arm/src/stm32h7/CMakeLists.txt
+++ b/arch/arm/src/stm32h7/CMakeLists.txt
@@ -72,7 +72,11 @@ if(CONFIG_STM32_ADC)
   list(APPEND SRCS stm32_adc.c)
 endif()
 
-if(CONFIG_STM32_FDCAN)
+if(CONFIG_STM32_FDCAN_CHARDRIVER)
+  list(APPEND SRCS stm32_fdcan.c)
+endif()
+
+if(CONFIG_STM32_FDCAN_SOCKET)
   list(APPEND SRCS stm32_fdcan_sock.c)
 endif()
 
diff --git a/arch/arm/src/stm32h7/Make.defs b/arch/arm/src/stm32h7/Make.defs
index 903b4e5c879..6262a0e7dee 100644
--- a/arch/arm/src/stm32h7/Make.defs
+++ b/arch/arm/src/stm32h7/Make.defs
@@ -75,7 +75,11 @@ ifeq ($(CONFIG_STM32_DAC),y)
 CHIP_CSRCS += stm32_dac.c
 endif
 
-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
 
diff --git a/arch/arm/src/stm32h7/hardware/stm32_fdcan.h 
b/arch/arm/src/stm32h7/hardware/stm32_fdcan.h
index f8b1dc57e01..264c222761f 100644
--- a/arch/arm/src/stm32h7/hardware/stm32_fdcan.h
+++ b/arch/arm/src/stm32h7/hardware/stm32_fdcan.h
@@ -1589,4 +1589,136 @@
 #define FDCANCCU_IE_CSCE_MASK        (0x1U << FDCANCCU_IE_CSCE_SHIFT)          
 /* 0x00000002 */
 #define FDCANCCU_IE_CSCE             FDCANCCU_IE_CSCE_MASK                     
 /* Calibration State Changed Enable       */
 
+/* Message RAM Definitions **************************************************/
+
+/* Common Buffer and FIFO element bit definitions:
+ *
+ *   --------------- ------------------- --------------------------------
+ *   RESOURCE               R0                        R1
+ *   --------------- ------------------- --------------------------------
+ *   RX FIFO:        ESI, XTD, RTR, ID,  ANMF, FIDX, EDL, BRS, DLC, RXTS
+ *   TX buffer:           XTD, RTR, ID,  MM,   EFC,            DLC
+ *   TX Event FIFO:  ESI, XTD, RTR, ID,  MM,   ET,   EDL, BRS, DLC, TXTS
+ *   --------------- ------------------- --------------------------------
+ */
+
+/* Common */
+
+#define BUFFER_R0_EXTID_SHIFT      (0)                                     /* 
Bits 0-28: Extended identifier */
+#define BUFFER_R0_EXTID_MASK       (0x1fffffff << BUFFER_R0_EXTID_SHIFT)
+#  define BUFFER_R0_EXTID(n)       ((uint32_t)(n) << BUFFER_R0_EXTID_SHIFT)
+#define BUFFER_R0_STDID_SHIFT      (18)                                    /* 
Bits 18-28: Standard identifier */
+#define BUFFER_R0_STDID_MASK       (0x7ff << BUFFER_R0_STDID_SHIFT)
+#  define BUFFER_R0_STDID(n)       ((uint32_t)(n) << BUFFER_R0_STDID_SHIFT)
+#define BUFFER_R0_RTR              (1 << 29)                               /* 
Bit 29: Remote Transmission Request */
+#define BUFFER_R0_XTD              (1 << 30)                               /* 
Bit 30: Extended Identifier */
+#define BUFFER_R0_ESI              (1 << 31)                               /* 
Bit 31: Error State Indicator */
+
+/* Common */
+
+#define BUFFER_R1_DLC_SHIFT        (16)                                    /* 
Bits 16-19: Date length code */
+#define BUFFER_R1_DLC_MASK         (15 << BUFFER_R1_DLC_SHIFT)
+#  define BUFFER_R1_DLC(n)         ((uint32_t)(n) << BUFFER_R1_DLC_SHIFT)
+#define BUFFER_R1_BRS              (1 << 20)                               /* 
Bit 20: Bit Rate Switch */
+#define BUFFER_R1_FDF              (1 << 21)                               /* 
Bit 21: FD Format */
+
+/* RX buffer/RX FIFOs */
+
+#define BUFFER_R1_RXTS_SHIFT       (0)                                     /* 
Bits 0-15: RX Timestamp */
+#define BUFFER_R1_RXTS_MASK        (0xffff << BUFFER_R1_RXTS_SHIFT)
+#  define BUFFER_R1_RXTS(n)        ((uint32_t)(n) << BUFFER_R1_RXTS_SHIFT)
+#define BUFFER_R1_FIDX_SHIFT       (24)                                    /* 
Bits 24-30: Filter index */
+#define BUFFER_R1_FIDX_MASK        (0x7f << BUFFER_R1_FIDX_SHIFT)
+#  define BUFFER_R1_FIDX(n)        ((uint32_t)(n) << BUFFER_R1_FIDX_SHIFT)
+#define BUFFER_R1_ANMF             (1 << 31)                               /* 
Bit 31: Accepted Non-matching Frame */
+
+/* TX buffer/TX Event FIFO */
+
+#define BUFFER_R1_MM_SHIFT         (24)                                    /* 
Bits 24-31: Message Marker */
+#define BUFFER_R1_MM_MASK          (0xff << BUFFER_R1_MM_SHIFT)
+#  define BUFFER_R1_MM(n)          ((uint32_t)(n) << BUFFER_R1_MM_SHIFT)
+
+/* TX buffer */
+
+#define BUFFER_R1_EFC              (1 << 23)                               /* 
Bit 23: Event FIFO Control */
+
+/* TX Event FIFO */
+
+#define BUFFER_R1_TXTS_SHIFT       (0)                                     /* 
Bits 0-15: TX Timestamp */
+#define BUFFER_R1_TXTS_MASK        (0xffff << BUFFER_R1_TXTS_SHIFT)
+#  define BUFFER_R1_TXTS(n)        ((uint32_t)(n) << BUFFER_R1_TXTS_SHIFT)
+#define BUFFER_R1_EDL              (1 << 21)                               /* 
Bit 21: Extended Data Length */
+#define BUFFER_R1_ET_SHIFT         (22)                                    /* 
Bits 22-23: Event Type */
+#define BUFFER_R1_ET_MASK          (3 << BUFFER_R1_ET_SHIFT)
+#  define BUFFER_R1_ET_TXEVENT     (1 << BUFFER_R1_ET_SHIFT)               /* 
Tx event */
+#  define BUFFER_R1_ET_TXCANCEL    (2 << BUFFER_R1_ET_SHIFT)               /* 
Transmission despite cancellation */
+
+/* Standard Message ID Filter Element */
+
+#define STDFILTER_S0_SFID2_SHIFT      (0)                                  /* 
Bits 0-10: Standard Filter ID 2 */
+#define STDFILTER_S0_SFID2_MASK       (0x7ff << STDFILTER_S0_SFID2_SHIFT)
+#  define STDFILTER_S0_SFID2(n   )    ((uint32_t)(n) << 
STDFILTER_S0_SFID2_SHIFT)
+#define STDFILTER_S0_BUFFER_SHIFT     (0)                                  /* 
Bits 0-5: RX buffer start address */
+#define STDFILTER_S0_BUFFER_MASK      (63 << STDFILTER_S0_BUFFER_SHIFT)
+#  define STDFILTER_S0_BUFFER(n)      ((uint32_t)(n) << 
STDFILTER_S0_BUFFER_SHIFT)
+#define STDFILTER_S0_ACTION_SHIFT     (9)                                  /* 
Bits 9-10: Action taken */
+#define STDFILTER_S0_ACTION_MASK      (3 << STDFILTER_S0_ACTION_SHIFT)
+#  define STDFILTER_S0_RXBUFFER       (0 << STDFILTER_S0_ACTION_SHIFT)     /* 
Store message in a Rx buffer */
+#  define STDFILTER_S0_DEBUGA         (1 << STDFILTER_S0_ACTION_SHIFT)     /* 
Debug Message A */
+#  define STDFILTER_S0_DEBUGB         (2 << STDFILTER_S0_ACTION_SHIFT)     /* 
Debug Message B */
+#  define STDFILTER_S0_DEBUGC         (3 << STDFILTER_S0_ACTION_SHIFT)     /* 
Debug Message C */
+#define STDFILTER_S0_SFID1_SHIFT      (16)                                 /* 
Bits 16-26: Standard Filter ID 2 */
+#define STDFILTER_S0_SFID1_MASK       (0x7ff << STDFILTER_S0_SFID1_SHIFT)
+#  define STDFILTER_S0_SFID1(n)       ((uint32_t)(n) << 
STDFILTER_S0_SFID1_SHIFT)
+#define STDFILTER_S0_SFEC_SHIFT       (27)                                 /* 
Bits 27-29: Standard Filter Element Configuration */
+#define STDFILTER_S0_SFEC_MASK        (7 << STDFILTER_S0_SFEC_SHIFT)
+#  define STDFILTER_S0_SFEC_DISABLE   (0 << STDFILTER_S0_SFEC_SHIFT)       /* 
Disable filter element */
+#  define STDFILTER_S0_SFEC_FIFO0     (1 << STDFILTER_S0_SFEC_SHIFT)       /* 
Store in Rx FIFO 0 on match */
+#  define STDFILTER_S0_SFEC_FIFO1     (2 << STDFILTER_S0_SFEC_SHIFT)       /* 
Store in Rx FIFO 1 on match */
+#  define STDFILTER_S0_SFEC_REJECT    (3 << STDFILTER_S0_SFEC_SHIFT)       /* 
Reject ID on match */
+#  define STDFILTER_S0_SFEC_PRIORITY  (4 << STDFILTER_S0_SFEC_SHIFT)       /* 
Set priority ion match */
+#  define STDFILTER_S0_SFEC_PRIOFIFO0 (5 << STDFILTER_S0_SFEC_SHIFT)       /* 
Set priority and store in FIFO 0 on match */
+#  define STDFILTER_S0_SFEC_PRIOFIFO1 (6 << STDFILTER_S0_SFEC_SHIFT)       /* 
Set priority and store in FIFO 1 on match */
+#  define STDFILTER_S0_SFEC_BUFFER    (7 << STDFILTER_S0_SFEC_SHIFT)       /* 
Store into Rx Buffer or as debug message */
+#define STDFILTER_S0_SFT_SHIFT        (30)                                 /* 
Bits 30-31: Standard Filter Type */
+#define STDFILTER_S0_SFT_MASK         (3 << STDFILTER_S0_SFT_SHIFT)
+#  define STDFILTER_S0_SFT_RANGE      (0 << STDFILTER_S0_SFT_SHIFT)        /* 
Range filter from SF1ID to SF2ID */
+#  define STDFILTER_S0_SFT_DUAL       (1 << STDFILTER_S0_SFT_SHIFT)        /* 
Dual ID filter for SF1ID or SF2ID */
+#  define STDFILTER_S0_SFT_CLASSIC    (2 << STDFILTER_S0_SFT_SHIFT)        /* 
Classic filter: SF1ID=filter SF2ID=mask */
+
+/* Extended Message ID Filter Element */
+
+#define EXTFILTER_F0_EFID1_SHIFT      (0)                                  /* 
Bits 0-28: Extended Filter ID 1 */
+#define EXTFILTER_F0_EFID1_MASK       (0x1fffffff << EXTFILTER_F0_EFID1_SHIFT)
+#  define EXTFILTER_F0_EFID1(n)       ((uint32_t)(n) << 
EXTFILTER_F0_EFID1_SHIFT)
+#define EXTFILTER_F0_EFEC_SHIFT       (29)                                 /* 
Bits 29-31: Extended Filter Element Configuration */
+#define EXTFILTER_F0_EFEC_MASK        (7 << EXTFILTER_F0_EFEC_SHIFT)
+#  define EXTFILTER_F0_EFEC_DISABLE   (0 << EXTFILTER_F0_EFEC_SHIFT)       /* 
Disable filter element */
+#  define EXTFILTER_F0_EFEC_FIFO0     (1 << EXTFILTER_F0_EFEC_SHIFT)       /* 
Store in Rx FIFO 0 on match */
+#  define EXTFILTER_F0_EFEC_FIFO1     (2 << EXTFILTER_F0_EFEC_SHIFT)       /* 
Store in Rx FIFO 1 on match */
+#  define EXTFILTER_F0_EFEC_REJECT    (3 << EXTFILTER_F0_EFEC_SHIFT)       /* 
Reject ID on match */
+#  define EXTFILTER_F0_EFEC_PRIORITY  (4 << EXTFILTER_F0_EFEC_SHIFT)       /* 
Set priority on match */
+#  define EXTFILTER_F0_EFEC_PRIOFIFO0 (5 << EXTFILTER_F0_EFEC_SHIFT)       /* 
Set priority and store in FIFO 0 on match */
+#  define EXTFILTER_F0_EFEC_PRIOFIFO1 (6 << EXTFILTER_F0_EFEC_SHIFT)       /* 
Set priority and store in FIFO 1 on match */
+#  define EXTFILTER_F0_EFEC_BUFFER    (7 << EXTFILTER_F0_EFEC_SHIFT)       /* 
Store into Rx Buffer or as debug message */
+
+#define EXTFILTER_F1_EFID2_SHIFT      (0)                                  /* 
Bits 0-28: Extended Filter ID 2 */
+#define EXTFILTER_F1_EFID2_MASK       (0x1fffffff << EXTFILTER_F1_EFID2_SHIFT)
+#  define EXTFILTER_F1_EFID2(n)       ((uint32_t)(n) << 
EXTFILTER_F1_EFID2_SHIFT)
+#define EXTFILTER_F1_BUFFER_SHIFT     (0)                                  /* 
Bits 0-5: RX buffer start address */
+#define EXTFILTER_F1_BUFFER_MASK      (63 << EXTFILTER_F1_BUFFER_SHIFT)
+#  define EXTFILTER_F1_BUFFER(n)      ((uint32_t)(n) << 
EXTFILTER_F1_BUFFER_SHIFT)
+#define EXTFILTER_F1_ACTION_SHIFT     (9)                                  /* 
Bits 9-10: Action taken */
+#define EXTFILTER_F1_ACTION_MASK      (3 << EXTFILTER_F1_ACTION_SHIFT)
+#  define EXTFILTER_F1_RXBUFFER       (0 << EXTFILTER_F1_ACTION_SHIFT)     /* 
Store message in a Rx buffer */
+#  define EXTFILTER_F1_DEBUGA         (1 << EXTFILTER_F1_ACTION_SHIFT)     /* 
Debug Message A */
+#  define EXTFILTER_F1_DEBUGB         (2 << EXTFILTER_F1_ACTION_SHIFT)     /* 
Debug Message B */
+#  define EXTFILTER_F1_DEBUGC         (3 << EXTFILTER_F1_ACTION_SHIFT)     /* 
Debug Message C */
+#define EXTFILTER_F1_EFT_SHIFT        (30)                                 /* 
Bits 30-31: Extended Filter Type */
+#define EXTFILTER_F1_EFT_MASK         (3 << EXTFILTER_F1_EFT_SHIFT)
+#  define EXTFILTER_F1_EFT_RANGE      (0 << EXTFILTER_F1_EFT_SHIFT)        /* 
Range filter from SF1ID to SF2ID */
+#  define EXTFILTER_F1_EFT_DUAL       (1 << EXTFILTER_F1_EFT_SHIFT)        /* 
Dual ID filter for SF1ID or SF2ID */
+#  define EXTFILTER_F1_EFT_CLASSIC    (2 << EXTFILTER_F1_EFT_SHIFT)        /* 
Classic filter: SF1ID=filter SF2ID=mask */
+#  define EXTFILTER_F1_EFT_NOXIDAM    (3 << EXTFILTER_F1_EFT_SHIFT)        /* 
Range filter from EF1ID to EF2ID, no XIDAM */
+
 #endif /* __ARCH_ARM_SRC_STM32H7_HARDWARE_STM32_FDCAN_H */
diff --git a/arch/arm/src/stm32h7/stm32_fdcan.c 
b/arch/arm/src/stm32h7/stm32_fdcan.c
new file mode 100644
index 00000000000..ac166438dfa
--- /dev/null
+++ b/arch/arm/src/stm32h7/stm32_fdcan.c
@@ -0,0 +1,3254 @@
+/****************************************************************************
+ * arch/arm/src/stm32h7/stm32_fdcan.c
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <sys/types.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+#include <errno.h>
+#include <nuttx/debug.h>
+#include <assert.h>
+
+#include <arch/board/board.h>
+#include <nuttx/irq.h>
+#include <nuttx/arch.h>
+#include <nuttx/clock.h>
+#include <nuttx/can/can.h>
+
+#include "arm_internal.h"
+#include "stm32_fdcan.h"
+#include "hardware/stm32_pinmap.h"
+#include "stm32_gpio.h"
+#include "stm32_rcc.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* FDCAN kernel clock (fdcan_ker_ck), selected by the board with
+ * STM32_RCC_D2CCIP1R_FDCANSEL.  Defaults to HSE as after reset.
+ */
+
+#ifndef STM32_FDCANCLK
+#  define STM32_FDCANCLK           STM32_HSE_FREQUENCY
+#endif
+
+/* Bit timing register helpers */
+
+#define FDCAN_NBTP_VALUE(brp, tseg1, tseg2, sjw)          \
+  (((uint32_t)(brp) << FDCAN_NBTP_NBRP_SHIFT) |           \
+   ((uint32_t)(tseg1) << FDCAN_NBTP_NTSEG1_SHIFT) |       \
+   ((uint32_t)(tseg2) << FDCAN_NBTP_TSEG2_SHIFT) |        \
+   ((uint32_t)(sjw) << FDCAN_NBTP_NSJW_SHIFT))
+#define FDCAN_DBTP_VALUE(brp, tseg1, tseg2, sjw)          \
+  (((uint32_t)(brp) << FDCAN_DBTP_DBRP_SHIFT) |           \
+   ((uint32_t)(tseg1) << FDCAN_DBTP_DTSEG1_SHIFT) |       \
+   ((uint32_t)(tseg2) << FDCAN_DBTP_DTSEG2_SHIFT) |       \
+   ((uint32_t)(sjw) << FDCAN_DBTP_DSJW_SHIFT))
+
+#define FDCAN_NBTP_NBRP_MAX        (511)
+#define FDCAN_NBTP_NTSEG1_MAX      (255)
+#define FDCAN_NBTP_NTSEG2_MAX      (127)
+#define FDCAN_NBTP_NSJW_MAX        (127)
+#define FDCAN_DBTP_DBRP_MAX        (31)
+#define FDCAN_DBTP_DTSEG1_MAX      (31)
+#define FDCAN_DBTP_DTSEG2_MAX      (15)
+#define FDCAN_DBTP_DSJW_MAX        (15)
+#define FDCAN_TDCR_TDCO_MAX        (127)
+
+/* Message RAM **************************************************************/
+
+/* 2560 words are shared by all FDCAN instances.  Each enabled port gets a
+ * fixed region with the same layout.
+ */
+
+#define FDCAN_MSGRAM_TOTAL_WORDS   (2560)
+
+#ifdef CONFIG_CAN_FD
+#  define FDCAN_ELEMENT_WORDS      (18)  /* 2 header words + 64 data bytes */
+#  define FDCAN_ELEMENT_DS         (7)   /* RXESC/TXESC: 64 byte data field */
+#else
+#  define FDCAN_ELEMENT_WORDS      (4)   /* 2 header words + 8 data bytes */
+#  define FDCAN_ELEMENT_DS         (0)   /* RXESC/TXESC: 8 byte data field */
+#endif
+
+#define FDCAN_STDFILTER_SIZE       (32)
+#define FDCAN_EXTFILTER_SIZE       (16)
+#define FDCAN_RXFIFO0_SIZE         (16)
+#define FDCAN_RXFIFO1_SIZE         (8)
+#define FDCAN_TXFIFOQ_SIZE         (16)
+
+/* The last element of each filter list is a reserved accept-all filter,
+ * enabled while no user filter of that kind is allocated.
+ */
+
+#define FDCAN_NSTDFILTERS          (FDCAN_STDFILTER_SIZE - 1)
+#define FDCAN_NEXTFILTERS          (FDCAN_EXTFILTER_SIZE - 1)
+
+#define FDCAN_STDFILTER_INDEX      (0)
+#define FDCAN_EXTFILTER_INDEX      (FDCAN_STDFILTER_INDEX + 
FDCAN_STDFILTER_SIZE)
+#define FDCAN_RXFIFO0_INDEX        (FDCAN_EXTFILTER_INDEX + 2 * 
FDCAN_EXTFILTER_SIZE)
+#define FDCAN_RXFIFO1_INDEX        (FDCAN_RXFIFO0_INDEX + \
+                                    FDCAN_RXFIFO0_SIZE * FDCAN_ELEMENT_WORDS)
+#define FDCAN_TXFIFOQ_INDEX        (FDCAN_RXFIFO1_INDEX + \
+                                    FDCAN_RXFIFO1_SIZE * FDCAN_ELEMENT_WORDS)
+#define FDCAN_MSGRAM_WORDS         (FDCAN_TXFIFOQ_INDEX + \
+                                    FDCAN_TXFIFOQ_SIZE * FDCAN_ELEMENT_WORDS)
+
+#if 3 * FDCAN_MSGRAM_WORDS > FDCAN_MSGRAM_TOTAL_WORDS
+#  error FDCAN message RAM layout does not fit
+#endif
+
+#define FDCAN_MSGRAM_BASE(port)    (STM32_CANRAM_BASE + \
+                                    ((port) - 1) * FDCAN_MSGRAM_WORDS * 4)
+
+#define FDCAN_MSGRAM(port, index)  ((volatile uint32_t *) \
+                                    (FDCAN_MSGRAM_BASE(port) + ((index) << 2)))
+
+#define FDCAN_TXBUFFERS_ALL        ((1 << FDCAN_TXFIFOQ_SIZE) - 1)
+
+/* FDCAN1 Configuration *****************************************************/
+
+#ifdef CONFIG_STM32_FDCAN1
+#  ifndef CONFIG_STM32_FDCAN1_AUTO_BIT_TIMING
+#    define FDCAN1_NTSEG1  (CONFIG_STM32_FDCAN1_NTSEG1 - 1)
+#    define FDCAN1_NTSEG2  (CONFIG_STM32_FDCAN1_NTSEG2 - 1)
+#    define FDCAN1_NBRP    ((STM32_FDCANCLK /                        \
+                             ((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 /                        \
+                              ((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
+
+#    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
+#  endif
+#endif /* CONFIG_STM32_FDCAN1 */
+
+/* FDCAN2 Configuration *****************************************************/
+
+#ifdef CONFIG_STM32_FDCAN2
+#  ifndef CONFIG_STM32_FDCAN2_AUTO_BIT_TIMING
+#    define FDCAN2_NTSEG1  (CONFIG_STM32_FDCAN2_NTSEG1 - 1)
+#    define FDCAN2_NTSEG2  (CONFIG_STM32_FDCAN2_NTSEG2 - 1)
+#    define FDCAN2_NBRP    ((STM32_FDCANCLK /                        \
+                             ((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 FDCAN2 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 /                        \
+                              ((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
+
+#    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
+#  endif
+#endif /* CONFIG_STM32_FDCAN2 */
+
+/* FDCAN3 Configuration *****************************************************/
+
+#ifdef CONFIG_STM32_FDCAN3
+#  ifndef CONFIG_STM32_FDCAN3_AUTO_BIT_TIMING
+#    define FDCAN3_NTSEG1  (CONFIG_STM32_FDCAN3_NTSEG1 - 1)
+#    define FDCAN3_NTSEG2  (CONFIG_STM32_FDCAN3_NTSEG2 - 1)
+#    define FDCAN3_NBRP    ((STM32_FDCANCLK /                        \
+                             ((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 FDCAN3 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 /                        \
+                              ((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
+
+#    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
+#  endif
+#endif /* CONFIG_STM32_FDCAN3 */
+
+/* Interrupts ***************************************************************/
+
+/* IR and IE share the same bit layout; the IR names are used for both.
+ *
+ * Common interrupts
+ *
+ *   FDCAN_IR_MRAF - Message RAM Access Failure
+ *   FDCAN_IR_TOO  - Timeout Occurred
+ *   FDCAN_IR_EP   - Error Passive
+ *   FDCAN_IR_EW   - Warning Status
+ *   FDCAN_IR_BO   - Bus_Off Status
+ *   FDCAN_IR_WDI  - Watchdog Interrupt
+ *   FDCAN_IR_PEA  - Protocol Error in Arbitration Phase
+ *   FDCAN_IR_PED  - Protocol Error in Data Phase
+ */
+
+#define FDCAN_CMNERR_INTS   (FDCAN_IR_MRAF | FDCAN_IR_TOO | FDCAN_IR_EP | \
+                             FDCAN_IR_EW | FDCAN_IR_BO | FDCAN_IR_WDI | \
+                             FDCAN_IR_PEA | FDCAN_IR_PED)
+#define FDCAN_COMMON_INTS   FDCAN_CMNERR_INTS
+
+/* RX FIFO interrupts
+ *
+ *   FDCAN_IR_RF0N - Receive FIFO 0 New Message
+ *   FDCAN_IR_RF0L - Receive FIFO 0 Message Lost
+ *   FDCAN_IR_RF1N - Receive FIFO 1 New Message
+ *   FDCAN_IR_RF1L - Receive FIFO 1 Message Lost
+ */
+
+#define FDCAN_RXFIFO0_INTS  (FDCAN_IR_RF0N | FDCAN_IR_RF0L)
+#define FDCAN_RXFIFO1_INTS  (FDCAN_IR_RF1N | FDCAN_IR_RF1L)
+#define FDCAN_RXFIFO_INTS   (FDCAN_RXFIFO0_INTS | FDCAN_RXFIFO1_INTS)
+
+#define FDCAN_RXERR_INTS    (FDCAN_IR_RF0L | FDCAN_IR_RF1L)
+
+/* TX FIFO/queue interrupts
+ *
+ *   FDCAN_IR_TC   - Transmission Completed
+ *   FDCAN_IR_TCF  - Transmission Cancellation Finished
+ *   FDCAN_IR_TFE  - Tx FIFO Empty
+ */
+
+#define FDCAN_TXFIFOQ_INTS  (FDCAN_IR_TC | FDCAN_IR_TCF | FDCAN_IR_TFE)
+
+#define FDCAN_TXERR_INTS    (FDCAN_IR_TEFL | FDCAN_IR_PEA | FDCAN_IR_PED)
+
+/* Common-, TX- and RX-Error-Mask */
+
+#define FDCAN_ANYERR_INTS   (FDCAN_CMNERR_INTS | FDCAN_RXERR_INTS | \
+                             FDCAN_TXERR_INTS)
+
+/* All non-reserved IR bits */
+
+#define FDCAN_INT_ALL       0x3fcfffff
+
+/* Debug ********************************************************************/
+
+#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
+
+/****************************************************************************
+ * 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 */
+  FDCAN_FD_MODE      = 1,   /* CAN FD operation */
+  FDCAN_FD_BRS_MODE  = 2    /* CAN FD operation with bit rate switching */
+};
+
+/* 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 *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 data_baud;       /* Configured data 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, 2 or 3) */
+  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 */
+  bool autotiming;          /* True: compute bit timing from the bitrates */
+#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 */
+  bool loopback;            /* Connection mode: loopback */
+  bool silent;              /* Connection mode: bus monitoring */
+  uint32_t nbtp;            /* Current nominal bit timing */
+  uint32_t dbtp;            /* Current data bit timing */
+  uint32_t rxints;          /* Configured RX interrupts */
+  uint32_t txints;          /* Configured TX interrupts */
+  uint32_t txpending;       /* TX buffers waiting for completion */
+
+#ifdef CONFIG_CAN_EXTID
+  uint32_t extfilters[(FDCAN_EXTFILTER_SIZE + 31) / 32]; /* Extended filter 
bit allocator */
+#endif
+  uint32_t stdfilters[(FDCAN_STDFILTER_SIZE + 31) / 32]; /* Standard filter 
bit allocator */
+};
+
+struct fdcan_bitseg
+{
+  uint32_t bitrate;
+  uint8_t sjw;
+  uint8_t bs1;
+  uint8_t bs2;
+  uint16_t prescaler;
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* FDCAN Register access */
+
+static uint32_t fdcan_getreg(struct stm32_fdcan_s *priv, int offset);
+static void fdcan_putreg(struct stm32_fdcan_s *priv, int offset,
+                         uint32_t regval);
+
+/* FDCAN helpers */
+
+static uint8_t fdcan_dlc2bytes(struct stm32_fdcan_s *priv, uint8_t dlc);
+static void fdcan_initmode(struct stm32_fdcan_s *priv, bool enable);
+static void fdcan_acceptall(struct stm32_fdcan_s *priv);
+static void fdcan_clearfilters(struct stm32_fdcan_s *priv);
+
+#ifdef CONFIG_CAN_EXTID
+static int fdcan_add_extfilter(struct stm32_fdcan_s *priv,
+                               struct canioc_extfilter_s *extconfig);
+static int fdcan_del_extfilter(struct stm32_fdcan_s *priv, int ndx);
+#endif
+static int fdcan_add_stdfilter(struct stm32_fdcan_s *priv,
+                               struct canioc_stdfilter_s *stdconfig);
+static int fdcan_del_stdfilter(struct stm32_fdcan_s *priv, int ndx);
+
+static int
+fdcan_start_busoff_recovery_sequence(struct stm32_fdcan_s *priv);
+static int fdcan_restart(struct can_dev_s *dev);
+
+/* CAN driver methods */
+
+static void fdcan_reset(struct can_dev_s *dev);
+static int  fdcan_setup(struct can_dev_s *dev);
+static void fdcan_shutdown(struct can_dev_s *dev);
+static void fdcan_rxint(struct can_dev_s *dev, bool enable);
+static void fdcan_txint(struct can_dev_s *dev, bool enable);
+static int  fdcan_ioctl(struct can_dev_s *dev, int cmd,
+                        unsigned long arg);
+static int  fdcan_remoterequest(struct can_dev_s *dev, uint16_t id);
+static int  fdcan_send(struct can_dev_s *dev, struct can_msg_s *msg);
+static bool fdcan_txready(struct can_dev_s *dev);
+static bool fdcan_txempty(struct can_dev_s *dev);
+
+/* FDCAN interrupt handling */
+
+#ifdef CONFIG_CAN_ERRORS
+static void fdcan_error(struct can_dev_s *dev, uint32_t status,
+                        uint32_t psr);
+#endif
+static void fdcan_receive(struct can_dev_s *dev,
+                          volatile uint32_t *rxbuffer);
+static void fdcan_rxfifo(struct can_dev_s *dev, int offset_rxfs,
+                         int offset_rxfa, volatile uint32_t *rxfifo,
+                         uint32_t *ie);
+static int  fdcan_interrupt(int irq, void *context, void *arg);
+
+/* Hardware initialization */
+
+static int  fdcan_hw_initialize(struct stm32_fdcan_s *priv);
+static int  fdcan_bittiming(struct fdcan_bitseg *timing);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static const struct can_ops_s g_fdcanops =
+{
+  .co_reset         = fdcan_reset,
+  .co_setup         = fdcan_setup,
+  .co_shutdown      = fdcan_shutdown,
+  .co_rxint         = fdcan_rxint,
+  .co_txint         = fdcan_txint,
+  .co_ioctl         = fdcan_ioctl,
+  .co_remoterequest = fdcan_remoterequest,
+  .co_send          = fdcan_send,
+  .co_txready       = fdcan_txready,
+  .co_txempty       = fdcan_txempty,
+};
+
+/* The FDCAN instances share one RCC reset and one message RAM */
+
+static bool g_fdcan_rccreset;
+
+#ifdef CONFIG_STM32_FDCAN1
+/* FDCAN1 constant configuration */
+
+static const struct stm32_config_s g_fdcan1const =
+{
+  .rxpinset         = GPIO_CAN1_RX,
+  .txpinset         = GPIO_CAN1_TX,
+  .base             = STM32_FDCAN1_BASE,
+  .baud             = CONFIG_STM32_FDCAN1_BITRATE,
+#if defined(CONFIG_STM32_FDCAN1_FD_BRS)
+  .data_baud        = CONFIG_STM32_FDCAN1_DBITRATE,
+#endif
+#ifndef CONFIG_STM32_FDCAN1_AUTO_BIT_TIMING
+  .nbtp             = FDCAN_NBTP_VALUE(FDCAN1_NBRP, FDCAN1_NTSEG1,
+                                       FDCAN1_NTSEG2, FDCAN1_NSJW),
+  .dbtp             = FDCAN_DBTP_VALUE(FDCAN1_DBRP, FDCAN1_DTSEG1,
+                                       FDCAN1_DTSEG2, FDCAN1_DSJW),
+#else
+  .autotiming       = true,
+#endif
+  .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
+#ifdef CONFIG_STM32_FDCAN1_LOOPBACK
+  .loopback         = true,
+#endif
+
+  /* FDCAN1 Message RAM */
+
+  .msgram =
+  {
+    FDCAN_MSGRAM(1, FDCAN_STDFILTER_INDEX),
+    FDCAN_MSGRAM(1, FDCAN_EXTFILTER_INDEX),
+    FDCAN_MSGRAM(1, FDCAN_RXFIFO0_INDEX),
+    FDCAN_MSGRAM(1, FDCAN_RXFIFO1_INDEX),
+    FDCAN_MSGRAM(1, FDCAN_TXFIFOQ_INDEX)
+  }
+};
+
+/* FDCAN1 variable driver state */
+
+static struct stm32_fdcan_s g_fdcan1priv;
+static struct can_dev_s g_fdcan1dev;
+
+#endif /* CONFIG_STM32_FDCAN1 */
+
+#ifdef CONFIG_STM32_FDCAN2
+/* FDCAN2 constant configuration */
+
+static const struct stm32_config_s g_fdcan2const =
+{
+  .rxpinset         = GPIO_CAN2_RX,
+  .txpinset         = GPIO_CAN2_TX,
+  .base             = STM32_FDCAN2_BASE,
+  .baud             = CONFIG_STM32_FDCAN2_BITRATE,
+#if defined(CONFIG_STM32_FDCAN2_FD_BRS)
+  .data_baud        = CONFIG_STM32_FDCAN2_DBITRATE,
+#endif
+#ifndef CONFIG_STM32_FDCAN2_AUTO_BIT_TIMING
+  .nbtp             = FDCAN_NBTP_VALUE(FDCAN2_NBRP, FDCAN2_NTSEG1,
+                                       FDCAN2_NTSEG2, FDCAN2_NSJW),
+  .dbtp             = FDCAN_DBTP_VALUE(FDCAN2_DBRP, FDCAN2_DTSEG1,
+                                       FDCAN2_DTSEG2, FDCAN2_DSJW),
+#else
+  .autotiming       = true,
+#endif
+  .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
+#ifdef CONFIG_STM32_FDCAN2_LOOPBACK
+  .loopback         = true,
+#endif
+
+  /* FDCAN2 Message RAM */
+
+  .msgram =
+  {
+    FDCAN_MSGRAM(2, FDCAN_STDFILTER_INDEX),
+    FDCAN_MSGRAM(2, FDCAN_EXTFILTER_INDEX),
+    FDCAN_MSGRAM(2, FDCAN_RXFIFO0_INDEX),
+    FDCAN_MSGRAM(2, FDCAN_RXFIFO1_INDEX),
+    FDCAN_MSGRAM(2, FDCAN_TXFIFOQ_INDEX)
+  }
+};
+
+/* FDCAN2 variable driver state */
+
+static struct stm32_fdcan_s g_fdcan2priv;
+static struct can_dev_s g_fdcan2dev;
+
+#endif /* CONFIG_STM32_FDCAN2 */
+
+#ifdef CONFIG_STM32_FDCAN3
+/* FDCAN3 constant configuration */
+
+static const struct stm32_config_s g_fdcan3const =
+{
+  .rxpinset         = GPIO_CAN3_RX,
+  .txpinset         = GPIO_CAN3_TX,
+  .base             = STM32_FDCAN3_BASE,
+  .baud             = CONFIG_STM32_FDCAN3_BITRATE,
+#if defined(CONFIG_STM32_FDCAN3_FD_BRS)
+  .data_baud        = CONFIG_STM32_FDCAN3_DBITRATE,
+#endif
+#ifndef CONFIG_STM32_FDCAN3_AUTO_BIT_TIMING
+  .nbtp             = FDCAN_NBTP_VALUE(FDCAN3_NBRP, FDCAN3_NTSEG1,
+                                       FDCAN3_NTSEG2, FDCAN3_NSJW),
+  .dbtp             = FDCAN_DBTP_VALUE(FDCAN3_DBRP, FDCAN3_DTSEG1,
+                                       FDCAN3_DTSEG2, FDCAN3_DSJW),
+#else
+  .autotiming       = true,
+#endif
+  .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
+#ifdef CONFIG_STM32_FDCAN3_LOOPBACK
+  .loopback         = true,
+#endif
+
+  /* FDCAN3 Message RAM */
+
+  .msgram =
+  {
+    FDCAN_MSGRAM(3, FDCAN_STDFILTER_INDEX),
+    FDCAN_MSGRAM(3, FDCAN_EXTFILTER_INDEX),
+    FDCAN_MSGRAM(3, FDCAN_RXFIFO0_INDEX),
+    FDCAN_MSGRAM(3, FDCAN_RXFIFO1_INDEX),
+    FDCAN_MSGRAM(3, FDCAN_TXFIFOQ_INDEX)
+  }
+};
+
+/* FDCAN3 variable driver state */
+
+static struct stm32_fdcan_s g_fdcan3priv;
+static struct can_dev_s g_fdcan3dev;
+
+#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:
+ *   The register value
+ *
+ ****************************************************************************/
+
+static uint32_t fdcan_getreg(struct stm32_fdcan_s *priv, int offset)
+{
+  return getreg32(priv->config->base + offset);
+}
+
+/****************************************************************************
+ * 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
+ *
+ ****************************************************************************/
+
+static void fdcan_putreg(struct stm32_fdcan_s *priv, int offset,
+                         uint32_t regval)
+{
+  putreg32(regval, priv->config->base + offset);
+}
+
+/****************************************************************************
+ * 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, the codes 9 to 15, which in standard CAN all code a data field of
+ *   8 bytes, are encoded:
+ *
+ *     9->12, 10->16, 11->20, 12->24, 13->32, 14->48, 15->64
+ *
+ * Input Parameters:
+ *   priv - FDCAN instance state
+ *   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(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_initmode
+ *
+ * Description:
+ *   Enter (with configuration change enabled) or leave initialization mode.
+ *
+ * Input Parameters:
+ *   priv   - An instance of the FDCAN driver state structure.
+ *   enable - True: enter initialization mode
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void fdcan_initmode(struct stm32_fdcan_s *priv, bool enable)
+{
+  uint32_t regval;
+
+  if (enable)
+    {
+      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);
+    }
+  else
+    {
+      regval  = fdcan_getreg(priv, STM32_FDCAN_CCCR_OFFSET);
+      regval &= ~(FDCAN_CCCR_INIT | FDCAN_CCCR_CCE);
+      fdcan_putreg(priv, STM32_FDCAN_CCCR_OFFSET, regval);
+
+      /* Wait for normal operation (synchronized to the bus) */
+
+      while ((fdcan_getreg(priv, STM32_FDCAN_CCCR_OFFSET) &
+              FDCAN_CCCR_INIT) != 0);
+    }
+}
+
+/****************************************************************************
+ * Name: fdcan_acceptall
+ *
+ * Description:
+ *   Enable the reserved accept-all filters (to RX FIFO0) for each kind of
+ *   identifier that has no user filter allocated, disable them otherwise.
+ *   Filter elements can be changed at runtime, no initialization mode.
+ *
+ * Input Parameters:
+ *   priv - An instance of the FDCAN driver state structure.
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void fdcan_acceptall(struct stm32_fdcan_s *priv)
+{
+  const struct stm32_config_s *config    = priv->config;
+  volatile uint32_t           *stdfilter = NULL;
+  volatile uint32_t           *extfilter = NULL;
+  bool                         extall    = true;
+
+  stdfilter = config->msgram.stdfilters + FDCAN_NSTDFILTERS;
+  if (priv->nstdalloc == 0)
+    {
+      *stdfilter = STDFILTER_S0_SFT_CLASSIC | STDFILTER_S0_SFEC_FIFO0;
+    }
+  else
+    {
+      *stdfilter = STDFILTER_S0_SFEC_DISABLE;
+    }
+
+#ifdef CONFIG_CAN_EXTID
+  extall = (priv->nextalloc == 0);
+#endif
+
+  extfilter = config->msgram.extfilters + (FDCAN_NEXTFILTERS << 1);
+  if (extall)
+    {
+      extfilter[1] = EXTFILTER_F1_EFT_CLASSIC;
+      extfilter[0] = EXTFILTER_F0_EFEC_FIFO0;
+    }
+  else
+    {
+      extfilter[0] = EXTFILTER_F0_EFEC_DISABLE;
+    }
+}
+
+/****************************************************************************
+ * Name: fdcan_clearfilters
+ *
+ * Description:
+ *   Disable and release all filters of this instance.
+ *
+ * Input Parameters:
+ *   priv - An instance of the FDCAN driver state structure.
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void fdcan_clearfilters(struct stm32_fdcan_s *priv)
+{
+  volatile uint32_t *msgram = priv->config->msgram.stdfilters;
+  unsigned int       cntr   = 0;
+
+  for (cntr = 0; cntr < FDCAN_RXFIFO0_INDEX; cntr++)
+    {
+      *msgram++ = 0;
+    }
+
+  priv->nstdalloc = 0;
+  memset(priv->stdfilters, 0, sizeof(priv->stdfilters));
+#ifdef CONFIG_CAN_EXTID
+  priv->nextalloc = 0;
+  memset(priv->extfilters, 0, sizeof(priv->extfilters));
+#endif
+}
+
+/****************************************************************************
+ * Name: fdcan_add_extfilter
+ *
+ * Description:
+ *   Add an address filter for a extended 29 bit address.
+ *
+ * Input Parameters:
+ *   priv      - An instance of the FDCAN driver state structure.
+ *   extconfig - The configuration of the extended filter
+ *
+ * Returned Value:
+ *   A non-negative filter ID is returned on success.  Otherwise a negated
+ *   errno value is returned to indicate the nature of the error.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_CAN_EXTID
+static int fdcan_add_extfilter(struct stm32_fdcan_s *priv,
+                               struct canioc_extfilter_s *extconfig)
+{
+  const struct stm32_config_s *config    = NULL;
+  volatile uint32_t           *extfilter = NULL;
+  uint32_t                     regval    = 0;
+  uint32_t                     f1        = 0;
+  int                          word      = 0;
+  int                          bit       = 0;
+  int                          ndx       = 0;
+
+  DEBUGASSERT(priv != NULL && priv->config != NULL && extconfig != NULL);
+  config = priv->config;
+
+  if (extconfig->xf_id1 > CAN_MAX_EXTMSGID ||
+      extconfig->xf_id2 > CAN_MAX_EXTMSGID)
+    {
+      return -EINVAL;
+    }
+
+  /* Format filter word F1 */
+
+  f1 = EXTFILTER_F1_EFID2(extconfig->xf_id2);
+
+  switch (extconfig->xf_type)
+    {
+      case CAN_FILTER_DUAL:
+        {
+          f1 |= EXTFILTER_F1_EFT_DUAL;
+          break;
+        }
+
+      case CAN_FILTER_MASK:
+        {
+          f1 |= EXTFILTER_F1_EFT_CLASSIC;
+          break;
+        }
+
+      case CAN_FILTER_RANGE:
+        {
+          f1 |= EXTFILTER_F1_EFT_RANGE;
+          break;
+        }
+
+      default:
+        {
+          return -EINVAL;
+        }
+    }
+
+  /* Find an unused extended filter */
+
+  for (ndx = 0; ndx < FDCAN_NEXTFILTERS; ndx++)
+    {
+      /* Is this filter assigned? */
+
+      word = ndx >> 5;
+      bit  = ndx & 0x1f;
+
+      if ((priv->extfilters[word] & (1 << bit)) == 0)
+        {
+          /* No, assign the filter */
+
+          DEBUGASSERT(priv->nextalloc < FDCAN_NEXTFILTERS);
+          priv->extfilters[word] |= (1 << bit);
+          priv->nextalloc++;
+
+          extfilter = config->msgram.extfilters + (ndx << 1);
+
+          /* Format and write filter word F0 */
+
+          regval = EXTFILTER_F0_EFID1(extconfig->xf_id1);
+
+          if (extconfig->xf_prio == 0)
+            {
+              regval |= EXTFILTER_F0_EFEC_FIFO0;
+            }
+          else
+            {
+              regval |= EXTFILTER_F0_EFEC_FIFO1;
+            }
+
+          /* Write F1 first, F0 enables the element */
+
+          extfilter[1] = f1;
+          extfilter[0] = regval;
+
+          /* The first extended filter stops accepting all frames */
+
+          fdcan_acceptall(priv);
+
+          return ndx;
+        }
+    }
+
+  DEBUGASSERT(priv->nextalloc == FDCAN_NEXTFILTERS);
+  return -EAGAIN;
+}
+#endif
+
+/****************************************************************************
+ * Name: fdcan_del_extfilter
+ *
+ * Description:
+ *   Remove an address filter for a extended 29 bit address.
+ *
+ * Input Parameters:
+ *   priv - An instance of the FDCAN driver state structure.
+ *   ndx  - The filter index previously returned by the
+ *          fdcan_add_extfilter().
+ *
+ * Returned Value:
+ *   Zero (OK) is returned on success.  Otherwise a negated errno value is
+ *   returned to indicate the nature of the error.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_CAN_EXTID
+static int fdcan_del_extfilter(struct stm32_fdcan_s *priv, int ndx)
+{
+  const struct stm32_config_s *config    = NULL;
+  volatile uint32_t           *extfilter = NULL;
+  int                          word      = 0;
+  int                          bit       = 0;
+
+  DEBUGASSERT(priv != NULL && priv->config != NULL);
+  config = priv->config;
+
+  /* Check user Parameters */
+
+  if (ndx < 0 || ndx >= FDCAN_NEXTFILTERS)
+    {
+      return -EINVAL;
+    }
+
+  word = ndx >> 5;
+  bit  = ndx & 0x1f;
+
+  /* Check if this filter is really assigned */
+
+  if ((priv->extfilters[word] & (1 << bit)) == 0)
+    {
+      /* No, error out */
+
+      return -ENOENT;
+    }
+
+  /* Release the filter */
+
+  priv->extfilters[word] &= ~(1 << bit);
+
+  DEBUGASSERT(priv->nextalloc > 0);
+  priv->nextalloc--;
+
+  /* After the last extended filter accept all frames again */
+
+  fdcan_acceptall(priv);
+
+  /* Deactivate the filter last so that no messages are lost. */
+
+  extfilter    = config->msgram.extfilters + (ndx << 1);
+  *extfilter++ = 0;
+  *extfilter   = 0;
+
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Name: fdcan_add_stdfilter
+ *
+ * Description:
+ *   Add an address filter for a standard 11 bit address.
+ *
+ * Input Parameters:
+ *   priv      - An instance of the FDCAN driver state structure.
+ *   stdconfig - The configuration of the standard filter
+ *
+ * Returned Value:
+ *   A non-negative filter ID is returned on success.  Otherwise a negated
+ *   errno value is returned to indicate the nature of the error.
+ *
+ ****************************************************************************/
+
+static int fdcan_add_stdfilter(struct stm32_fdcan_s *priv,
+                               struct canioc_stdfilter_s *stdconfig)
+{
+  const struct stm32_config_s *config    = NULL;
+  volatile uint32_t           *stdfilter = NULL;
+  uint32_t                     s0        = 0;
+  int                          word      = 0;
+  int                          bit       = 0;
+  int                          ndx       = 0;
+
+  DEBUGASSERT(priv != NULL && priv->config != NULL && stdconfig != NULL);
+  config = priv->config;
+
+  if (stdconfig->sf_id1 > CAN_MAX_STDMSGID ||
+      stdconfig->sf_id2 > CAN_MAX_STDMSGID)
+    {
+      return -EINVAL;
+    }
+
+  /* Format filter word S0 */
+
+  s0  = STDFILTER_S0_SFID1(stdconfig->sf_id1);
+  s0 |= STDFILTER_S0_SFID2(stdconfig->sf_id2);
+
+  if (stdconfig->sf_prio == 0)
+    {
+      s0 |= STDFILTER_S0_SFEC_FIFO0;
+    }
+  else
+    {
+      s0 |= STDFILTER_S0_SFEC_FIFO1;
+    }
+
+  switch (stdconfig->sf_type)
+    {
+      case CAN_FILTER_DUAL:
+        {
+          s0 |= STDFILTER_S0_SFT_DUAL;
+          break;
+        }
+
+      case CAN_FILTER_MASK:
+        {
+          s0 |= STDFILTER_S0_SFT_CLASSIC;
+          break;
+        }
+
+      case CAN_FILTER_RANGE:
+        {
+          s0 |= STDFILTER_S0_SFT_RANGE;
+          break;
+        }
+
+      default:
+        {
+          return -EINVAL;
+        }
+    }
+
+  /* Find an unused standard filter */
+
+  for (ndx = 0; ndx < FDCAN_NSTDFILTERS; ndx++)
+    {
+      /* Is this filter assigned? */
+
+      word = ndx >> 5;
+      bit  = ndx & 0x1f;
+
+      if ((priv->stdfilters[word] & (1 << bit)) == 0)
+        {
+          /* No, assign the filter */
+
+          DEBUGASSERT(priv->nstdalloc < FDCAN_NSTDFILTERS);
+          priv->stdfilters[word] |= (1 << bit);
+          priv->nstdalloc++;
+
+          stdfilter  = config->msgram.stdfilters + ndx;
+          *stdfilter = s0;
+
+          /* The first standard filter stops accepting all frames */
+
+          fdcan_acceptall(priv);
+
+          return ndx;
+        }
+    }
+
+  DEBUGASSERT(priv->nstdalloc == FDCAN_NSTDFILTERS);
+  return -EAGAIN;
+}
+
+/****************************************************************************
+ * Name: fdcan_del_stdfilter
+ *
+ * Description:
+ *   Remove an address filter for a standard 11 bit address.
+ *
+ * Input Parameters:
+ *   priv - An instance of the FDCAN driver state structure.
+ *   ndx  - The filter index previously returned by the
+ *          fdcan_add_stdfilter().
+ *
+ * Returned Value:
+ *   Zero (OK) is returned on success.  Otherwise a negated errno value is
+ *   returned to indicate the nature of the error.
+ *
+ ****************************************************************************/
+
+static int fdcan_del_stdfilter(struct stm32_fdcan_s *priv, int ndx)
+{
+  const struct stm32_config_s *config    = NULL;
+  volatile uint32_t           *stdfilter = NULL;
+  int                          word      = 0;
+  int                          bit       = 0;
+
+  DEBUGASSERT(priv != NULL && priv->config != NULL);
+  config = priv->config;
+
+  /* Check Userspace Parameters */
+
+  if (ndx < 0 || ndx >= FDCAN_NSTDFILTERS)
+    {
+      return -EINVAL;
+    }
+
+  word = ndx >> 5;
+  bit  = ndx & 0x1f;
+
+  /* Check if this filter is really assigned */
+
+  if ((priv->stdfilters[word] & (1 << bit)) == 0)
+    {
+      /* No, error out */
+
+      return -ENOENT;
+    }
+
+  /* Release the filter */
+
+  priv->stdfilters[word] &= ~(1 << bit);
+
+  DEBUGASSERT(priv->nstdalloc > 0);
+  priv->nstdalloc--;
+
+  /* After the last standard filter accept all frames again */
+
+  fdcan_acceptall(priv);
+
+  /* Deactivate the filter last so that no messages are lost. */
+
+  stdfilter  = config->msgram.stdfilters + ndx;
+  *stdfilter = 0;
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: fdcan_start_busoff_recovery_sequence
+ *
+ * Description:
+ *   This function initiates the BUS-OFF recovery sequence.
+ *   CAN Specification Rev. 2.0 or ISO11898-1:2015.
+ *
+ * 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.
+ *
+ ****************************************************************************/
+
+static int
+fdcan_start_busoff_recovery_sequence(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;
+}
+
+/****************************************************************************
+ * Name: fdcan_restart
+ *
+ * Description:
+ *   Reset and, if it was set up, re-initialize the FDCAN so that new bit
+ *   timing or connection modes take effect.  Pending TX frames are lost.
+ *
+ * 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_restart(struct can_dev_s *dev)
+{
+  struct stm32_fdcan_s *priv  = dev->cd_priv;
+  uint32_t              ie    = 0;
+  uint8_t               state = 0;
+  int                   ret   = OK;
+
+  /* Save the current state so that we can recover to it */
+
+  ie    = fdcan_getreg(priv, STM32_FDCAN_IE_OFFSET);
+  state = priv->state;
+
+  fdcan_reset(dev);
+
+  if (state == FDCAN_STATE_SETUP)
+    {
+      ret = fdcan_setup(dev);
+      if (ret == OK)
+        {
+          /* Restore RX interrupts.  The hardware was reset, so TX
+           * interrupts are re-enabled by the next transmission.
+           */
+
+          fdcan_putreg(priv, STM32_FDCAN_IE_OFFSET, ie & ~priv->txints);
+        }
+    }
+
+  return ret;
+}
+
+/****************************************************************************
+ * 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.
+ *
+ *   The RCC reset is shared by all FDCAN instances, so only this instance
+ *   is stopped here.
+ *
+ * Input Parameters:
+ *   dev - An instance of the "upper half" can driver state structure.
+ *
+ * Returned Value:
+ *  None
+ *
+ ****************************************************************************/
+
+static void fdcan_reset(struct can_dev_s *dev)
+{
+  struct stm32_fdcan_s        *priv   = NULL;
+  const struct stm32_config_s *config = NULL;
+  irqstate_t                   flags;
+
+  DEBUGASSERT(dev);
+  priv = dev->cd_priv;
+  DEBUGASSERT(priv);
+  config = priv->config;
+  DEBUGASSERT(config);
+
+  caninfo("FDCAN%d\n", config->port);
+  UNUSED(config);
+
+  flags = enter_critical_section();
+
+  /* Disable all interrupts */
+
+  fdcan_putreg(priv, STM32_FDCAN_IE_OFFSET, 0);
+  fdcan_putreg(priv, STM32_FDCAN_ILE_OFFSET, 0);
+  fdcan_putreg(priv, STM32_FDCAN_TXBTIE_OFFSET, 0);
+
+  /* Stop the controller.  Entering configuration mode also clears pending
+   * TX requests.
+   */
+
+  if (priv->state != FDCAN_STATE_DISABLED)
+    {
+      fdcan_initmode(priv, true);
+    }
+
+  fdcan_putreg(priv, STM32_FDCAN_IR_OFFSET, FDCAN_INT_ALL);
+
+  priv->txpending = 0;
+  if (priv->state != FDCAN_STATE_DISABLED)
+    {
+      priv->state = FDCAN_STATE_RESET;
+    }
+
+  leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * 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(struct can_dev_s *dev)
+{
+  struct stm32_fdcan_s        *priv   = NULL;
+  const struct stm32_config_s *config = NULL;
+  int                          ret    = 0;
+
+  DEBUGASSERT(dev);
+  priv = dev->cd_priv;
+  DEBUGASSERT(priv);
+  config = priv->config;
+  DEBUGASSERT(config);
+
+  caninfo("FDCAN%d\n", config->port);
+
+  /* FDCAN hardware initialization */
+
+  ret = fdcan_hw_initialize(priv);
+  if (ret < 0)
+    {
+      canerr("ERROR: FDCAN%d H/W initialization failed: %d\n",
+             config->port, ret);
+      return ret;
+    }
+
+  /* Attach the FDCAN interrupt handlers */
+
+  ret = irq_attach(config->irq0, fdcan_interrupt, dev);
+  if (ret < 0)
+    {
+      canerr("ERROR: Failed to attach FDCAN%d line 0 IRQ (%d)",
+      config->port, config->irq0);
+      return ret;
+    }
+
+  ret = irq_attach(config->irq1, fdcan_interrupt, dev);
+  if (ret < 0)
+    {
+      canerr("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(struct can_dev_s *dev)
+{
+  struct stm32_fdcan_s        *priv   = NULL;
+  const struct stm32_config_s *config = NULL;
+  uint32_t                     regval = 0;
+
+  DEBUGASSERT(dev);
+  priv = dev->cd_priv;
+  DEBUGASSERT(priv);
+  config = priv->config;
+  DEBUGASSERT(config);
+
+  caninfo("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);
+
+  /* Enter initialization mode first, this aborts pending transmissions
+   * that would otherwise hold off the clock stop (e.g. no ACK on the bus).
+   */
+
+  fdcan_initmode(priv, true);
+
+  /* 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 Clock Stop Acknowledge to verify device is powered down */
+
+  while ((fdcan_getreg(priv, STM32_FDCAN_CCCR_OFFSET) & FDCAN_CCCR_CSA)
+         == 0);
+
+  /* Release all filters, the next setup accepts all frames again */
+
+  fdcan_clearfilters(priv);
+
+  priv->txpending = 0;
+  priv->state = FDCAN_STATE_DISABLED;
+}
+
+/****************************************************************************
+ * Name: fdcan_rxint
+ *
+ * Description:
+ *   Call to enable or disable RX interrupts.
+ *
+ * Input Parameters:
+ *   dev - An instance of the "upper half" can driver state structure.
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void fdcan_rxint(struct can_dev_s *dev, bool enable)
+{
+  struct stm32_fdcan_s *priv   = dev->cd_priv;
+  uint32_t              regval = 0;
+  irqstate_t            flags;
+
+  DEBUGASSERT(priv && priv->config);
+
+  caninfo("FDCAN%d enable: %d\n", priv->config->port, enable);
+
+  /* Enable/disable the receive interrupts */
+
+  flags  = enter_critical_section();
+  regval = fdcan_getreg(priv, STM32_FDCAN_IE_OFFSET);
+
+  if (enable)
+    {
+      regval |= priv->rxints | FDCAN_COMMON_INTS;
+    }
+  else
+    {
+      regval &= ~priv->rxints;
+    }
+
+  fdcan_putreg(priv, STM32_FDCAN_IE_OFFSET, regval);
+  leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * 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(struct can_dev_s *dev, bool enable)
+{
+  struct stm32_fdcan_s *priv   = dev->cd_priv;
+  uint32_t              regval = 0;
+  irqstate_t            flags;
+
+  DEBUGASSERT(priv && priv->config);
+
+  caninfo("FDCAN%d enable: %d\n", priv->config->port, enable);
+
+  /* Enable/disable the transmit interrupts */
+
+  flags  = enter_critical_section();
+  regval = fdcan_getreg(priv, STM32_FDCAN_IE_OFFSET);
+
+  if (enable)
+    {
+      regval |= priv->txints | FDCAN_COMMON_INTS;
+    }
+  else
+    {
+      regval &= ~priv->txints;
+    }
+
+  fdcan_putreg(priv, STM32_FDCAN_IE_OFFSET, regval);
+  leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: fdcan_ioctl
+ *
+ * Description:
+ *   All ioctl calls will be routed through this method
+ *
+ * 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_ioctl(struct can_dev_s *dev, int cmd, unsigned long arg)
+{
+  struct stm32_fdcan_s *priv = NULL;
+  int                   ret  = -ENOTTY;
+
+  caninfo("cmd=%04x arg=%lu\n", cmd, arg);
+
+  DEBUGASSERT(dev && dev->cd_priv);
+  priv = dev->cd_priv;
+
+  /* Handle the command */
+
+  switch (cmd)
+    {
+      /* CANIOC_GET_BITTIMING:
+       *   Description:    Return the current bit timing settings
+       *   Argument:       A pointer to a write-able instance of struct
+       *                   canioc_bittiming_s in which current bit timing
+       *                   values will be returned.
+       *   Returned Value: Zero (OK) is returned on success.  Otherwise -1
+       *                   (ERROR) is returned with the errno variable set
+       *                   to indicate the nature of the error.
+       *   Dependencies:   None
+       */
+
+      case CANIOC_GET_BITTIMING:
+        {
+          struct canioc_bittiming_s *bt =
+            (struct canioc_bittiming_s *)arg;
+          uint32_t regval;
+          uint32_t brp;
+
+          DEBUGASSERT(bt != NULL);
+
+#ifdef CONFIG_CAN_FD
+          if (bt->type == CAN_BITTIMING_DATA)
+            {
+              regval       = priv->dbtp;
+              bt->bt_sjw   = ((regval & FDCAN_DBTP_DSJW_MASK) >>
+                              FDCAN_DBTP_DSJW_SHIFT) + 1;
+              bt->bt_tseg1 = ((regval & FDCAN_DBTP_DTSEG1_MASK) >>
+                              FDCAN_DBTP_DTSEG1_SHIFT) + 1;
+              bt->bt_tseg2 = ((regval & FDCAN_DBTP_DTSEG2_MASK) >>
+                              FDCAN_DBTP_DTSEG2_SHIFT) + 1;
+              brp          = ((regval & FDCAN_DBTP_DBRP_MASK) >>
+                              FDCAN_DBTP_DBRP_SHIFT) + 1;
+            }
+          else
+#endif
+            {
+              regval       = priv->nbtp;
+              bt->bt_sjw   = ((regval & FDCAN_NBTP_NSJW_MASK) >>
+                              FDCAN_NBTP_NSJW_SHIFT) + 1;
+              bt->bt_tseg1 = ((regval & FDCAN_NBTP_NTSEG1_MASK) >>
+                              FDCAN_NBTP_NTSEG1_SHIFT) + 1;
+              bt->bt_tseg2 = ((regval & FDCAN_NBTP_TSEG2_MASK) >>
+                              FDCAN_NBTP_TSEG2_SHIFT) + 1;
+              brp          = ((regval & FDCAN_NBTP_NBRP_MASK) >>
+                              FDCAN_NBTP_NBRP_SHIFT) + 1;
+            }
+
+          bt->bt_baud = STM32_FDCANCLK / brp /
+                        (bt->bt_tseg1 + bt->bt_tseg2 + 1);
+          ret = OK;
+        }
+        break;
+
+      /* CANIOC_SET_BITTIMING:
+       *   Description:    Set new current bit timing values
+       *   Argument:       A pointer to a read-able instance of struct
+       *                   canioc_bittiming_s in which the new bit timing
+       *                   values are provided.
+       *   Returned Value: Zero (OK) is returned on success.  Otherwise -1
+       *                   (ERROR) is returned with the errno variable set
+       *                   to indicate the nature of the error.
+       *   Dependencies:   None
+       *
+       * The FDCAN is reset to apply the new timing, pending TX frames are
+       * lost.  Use only while the bus is quiet.
+       */
+
+      case CANIOC_SET_BITTIMING:
+        {
+          const struct canioc_bittiming_s *bt =
+            (const struct canioc_bittiming_s *)arg;
+          uint32_t ntq;
+          uint32_t brp;
+
+          DEBUGASSERT(bt != NULL);
+
+          if (bt->bt_baud == 0 || bt->bt_tseg1 == 0 || bt->bt_tseg2 == 0 ||
+              bt->bt_sjw == 0)
+            {
+              ret = -EINVAL;
+              break;
+            }
+
+          /* The bitrate must be an exact multiple of the time quanta */
+
+          ntq = bt->bt_tseg1 + bt->bt_tseg2 + 1;
+          if (STM32_FDCANCLK % (ntq * bt->bt_baud) != 0)
+            {
+              ret = -EINVAL;
+              break;
+            }
+
+          brp = STM32_FDCANCLK / (ntq * bt->bt_baud);
+
+#ifdef CONFIG_CAN_FD
+          if (bt->type == CAN_BITTIMING_DATA)
+            {
+              if (brp - 1 > FDCAN_DBTP_DBRP_MAX ||
+                  bt->bt_tseg1 - 1 > FDCAN_DBTP_DTSEG1_MAX ||
+                  bt->bt_tseg2 - 1 > FDCAN_DBTP_DTSEG2_MAX ||
+                  bt->bt_sjw - 1 > FDCAN_DBTP_DSJW_MAX)
+                {
+                  ret = -EINVAL;
+                  break;
+                }
+
+              priv->dbtp = FDCAN_DBTP_VALUE(brp - 1, bt->bt_tseg1 - 1,
+                                            bt->bt_tseg2 - 1,
+                                            bt->bt_sjw - 1);
+            }
+          else
+#endif
+            {
+              if (brp - 1 > FDCAN_NBTP_NBRP_MAX ||
+                  bt->bt_tseg1 - 1 > FDCAN_NBTP_NTSEG1_MAX ||
+                  bt->bt_tseg2 - 1 > FDCAN_NBTP_NTSEG2_MAX ||
+                  bt->bt_sjw - 1 > FDCAN_NBTP_NSJW_MAX)
+                {
+                  ret = -EINVAL;
+                  break;
+                }
+
+              priv->nbtp = FDCAN_NBTP_VALUE(brp - 1, bt->bt_tseg1 - 1,
+                                            bt->bt_tseg2 - 1,
+                                            bt->bt_sjw - 1);
+            }
+
+          ret = fdcan_restart(dev);
+        }
+        break;
+
+      /* CANIOC_GET_CONNMODES:
+       *   Description:    Get the current bus connection modes
+       *   Argument:       A pointer to a write-able instance of struct
+       *                   canioc_connmodes_s in which the new bus modes will
+       *                   be returned.
+       *   Returned Value: Zero (OK) is returned on success.  Otherwise -1
+       *                   (ERROR)is returned with the errno variable set
+       *                   to indicate the nature of the error.
+       *   Dependencies:   None
+       */
+
+      case CANIOC_GET_CONNMODES:
+        {
+          struct canioc_connmodes_s *bm =
+            (struct canioc_connmodes_s *)arg;
+
+          DEBUGASSERT(bm != NULL);
+
+          bm->bm_loopback = priv->loopback;
+          bm->bm_silent   = priv->silent;
+          ret = OK;
+        }
+        break;
+
+      /* CANIOC_SET_CONNMODES:
+       *   Description:    Set new bus connection modes values
+       *   Argument:       A pointer to a read-able instance of struct
+       *                   canioc_connmodes_s in which the new bus modes
+       *                   are provided.
+       *   Returned Value: Zero (OK) is returned on success.  Otherwise -1
+       *                   (ERROR) is returned with the errno variable set
+       *                   to indicate the nature of the error.
+       *   Dependencies:   None
+       */
+
+      case CANIOC_SET_CONNMODES:
+        {
+          const struct canioc_connmodes_s *bm =
+            (const struct canioc_connmodes_s *)arg;
+
+          DEBUGASSERT(bm != NULL);
+
+          priv->loopback = bm->bm_loopback;
+          priv->silent   = bm->bm_silent;
+
+          ret = fdcan_restart(dev);
+        }
+        break;
+
+#ifdef CONFIG_CAN_EXTID
+      /* CANIOC_ADD_EXTFILTER:
+       *   Description:    Add an address filter for a extended 29 bit
+       *                   address.
+       *   Argument:       A reference to struct canioc_extfilter_s
+       *   Returned Value: A non-negative filter ID is returned on success.
+       *                   Otherwise -1 (ERROR) is returned with the errno
+       *                   variable set to indicate the nature of the error.
+       */
+
+      case CANIOC_ADD_EXTFILTER:
+        {
+          DEBUGASSERT(arg != 0);
+
+          ret = fdcan_add_extfilter(priv,
+                                   (struct canioc_extfilter_s *)arg);
+        }
+        break;
+
+      /* CANIOC_DEL_EXTFILTER:
+       *   Description:    Remove an address filter for a standard 29 bit
+       *                   address.
+       *   Argument:       The filter index previously returned by the
+       *                   CANIOC_ADD_EXTFILTER command
+       *   Returned Value: Zero (OK) is returned on success.  Otherwise -1
+       *                   (ERROR) is returned with the errno variable set
+       *                   to indicate the nature of the error.
+       */
+
+      case CANIOC_DEL_EXTFILTER:
+        {
+          ret = fdcan_del_extfilter(priv, (int)arg);
+        }
+        break;
+#endif
+
+      /* CANIOC_ADD_STDFILTER:
+       *   Description:    Add an address filter for a standard 11 bit
+       *                   address.
+       *   Argument:       A reference to struct canioc_stdfilter_s
+       *   Returned Value: A non-negative filter ID is returned on success.
+       *                   Otherwise -1 (ERROR) is returned with the errno
+       *                   variable set to indicate the nature of the error.
+       */
+
+      case CANIOC_ADD_STDFILTER:
+        {
+          DEBUGASSERT(arg != 0);
+
+          ret = fdcan_add_stdfilter(priv,
+                                   (struct canioc_stdfilter_s *)arg);
+        }
+        break;
+
+      /* CANIOC_DEL_STDFILTER:
+       *   Description:    Remove an address filter for a standard 11 bit
+       *                   address.
+       *   Argument:       The filter index previously returned by the
+       *                   CANIOC_ADD_STDFILTER command
+       *   Returned Value: Zero (OK) is returned on success.  Otherwise -1
+       *                   (ERROR) is returned with the errno variable set
+       *                   to indicate the nature of the error.
+       */
+
+      case CANIOC_DEL_STDFILTER:
+        {
+          ret = fdcan_del_stdfilter(priv, (int)arg);
+        }
+        break;
+
+      /* CANIOC_BUSOFF_RECOVERY:
+       *   Description : Initiates the BUS - OFF recovery sequence
+       *   Argument : None
+       *   Returned Value : Zero (OK) is returned on success. Otherwise -1
+       *                    (ERROR) is returned with the errno variable set
+       *                    to indicate the nature of the error.
+       *   Dependencies : None
+       */
+
+      case CANIOC_BUSOFF_RECOVERY:
+        {
+          ret = fdcan_start_busoff_recovery_sequence(priv);
+        }
+        break;
+
+      /* Unsupported/unrecognized command */
+
+      default:
+        canerr("ERROR: Unrecognized command: %04x\n", cmd);
+        break;
+    }
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: fdcan_remoterequest
+ *
+ * Description:
+ *   Send a remote request
+ *
+ * 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_remoterequest(struct can_dev_s *dev, uint16_t id)
+{
+  /* REVISIT:  Remote request not implemented */
+
+  return -ENOSYS;
+}
+
+/****************************************************************************
+ * Name: fdcan_send
+ *
+ * Description:
+ *    Send one can message.
+ *
+ * Input Parameters:
+ *   dev - An instance of the "upper half" can driver state structure.
+ *   msg - The message to send
+ *
+ * Returned Value:
+ *   Zero on success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+static int fdcan_send(struct can_dev_s *dev, struct can_msg_s *msg)
+{
+  struct stm32_fdcan_s        *priv     = NULL;
+  const struct stm32_config_s *config   = NULL;
+  volatile uint32_t           *txbuffer = NULL;
+  const uint8_t               *src      = NULL;
+  uint32_t                     regval   = 0;
+  uint32_t                     t0       = 0;
+  uint32_t                     t1       = 0;
+  unsigned int                 ndx      = 0;
+  unsigned int                 nbytes   = 0;
+  unsigned int                 i        = 0;
+  irqstate_t                   flags;
+
+  DEBUGASSERT(dev);
+  priv = dev->cd_priv;
+  DEBUGASSERT(priv && priv->config);
+  config = priv->config;
+
+  caninfo("CAN%" PRIu8 " ID: %" PRIu32 " DLC: %" PRIu8 "\n",
+          config->port, (uint32_t)msg->cm_hdr.ch_id, msg->cm_hdr.ch_dlc);
+
+  /* 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.
+   *   Error state indicator (ESI)       - ESI bit in CAN FD
+   *
+   * Format word T1:
+   *   Data Length Code (DLC)            - Value from message structure
+   *   Bit Rate Switch (BRS)             - Bit rate switching for CAN FD
+   *   FD format (FDF)                   - Frame transmitted in CAN FD format
+   *   Event FIFO Control (EFC)          - Do not store events.
+   *   Message Marker (MM)               - Always zero
+   */
+
+#ifdef CONFIG_CAN_EXTID
+  if (msg->cm_hdr.ch_extid == 1)
+    {
+      DEBUGASSERT(msg->cm_hdr.ch_id <= CAN_MAX_EXTMSGID);
+
+      t0 = BUFFER_R0_EXTID(msg->cm_hdr.ch_id) | BUFFER_R0_XTD;
+    }
+  else
+#endif
+    {
+      DEBUGASSERT(msg->cm_hdr.ch_id <= CAN_MAX_STDMSGID);
+
+      t0 = BUFFER_R0_STDID(msg->cm_hdr.ch_id);
+    }
+
+  if (msg->cm_hdr.ch_rtr == 1)
+    {
+      t0 |= BUFFER_R0_RTR;
+    }
+
+  t1 = BUFFER_R1_DLC(msg->cm_hdr.ch_dlc);
+
+#ifdef CONFIG_CAN_FD
+  /* CAN FD Format */
+
+  if (msg->cm_hdr.ch_edl == 1)
+    {
+      t1 |= BUFFER_R1_FDF;
+
+      if (msg->cm_hdr.ch_brs == 1)
+        {
+          t1 |= BUFFER_R1_BRS;
+        }
+
+      if (msg->cm_hdr.ch_esi == 1)
+        {
+          t0 |= BUFFER_R0_ESI;
+        }
+    }
+#endif
+
+  flags = enter_critical_section();
+
+  /* Get our reserved Tx FIFO/queue put index */
+
+  regval = fdcan_getreg(priv, STM32_FDCAN_TXFQS_OFFSET);
+  if ((regval & FDCAN_TXFQS_TFQF) != 0)
+    {
+      leave_critical_section(flags);
+      return -EBUSY;
+    }
+
+  ndx = (regval & FDCAN_TXFQS_TFQPI_MASK) >> FDCAN_TXFQS_TFQPI_SHIFT;
+  DEBUGASSERT(ndx < FDCAN_TXFIFOQ_SIZE);
+
+  /* And the TX buffer corresponding to this index */
+
+  txbuffer    = config->msgram.txfifoq + ndx * FDCAN_ELEMENT_WORDS;
+  txbuffer[0] = t0;
+  txbuffer[1] = t1;
+
+  /* Followed by the amount of data corresponding to the DLC (T2..).
+   * Message RAM is written in words, little endian is assumed.
+   */
+
+  src    = msg->cm_data;
+  nbytes = fdcan_dlc2bytes(priv, msg->cm_hdr.ch_dlc);
+
+  for (i = 0; i < nbytes; i += 4, src += 4)
+    {
+      txbuffer[2 + (i >> 2)] = src[0] | (src[1] << 8) |
+                               (src[2] << 16) | ((uint32_t)src[3] << 24);
+    }
+
+  /* And request to send the packet */
+
+  priv->txpending |= (1 << ndx);
+  fdcan_putreg(priv, STM32_FDCAN_TXBAR_OFFSET, (1 << ndx));
+
+  leave_critical_section(flags);
+  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(struct can_dev_s *dev)
+{
+  struct stm32_fdcan_s *priv   = dev->cd_priv;
+  uint32_t              regval = 0;
+
+  /* Return TRUE if the TX FIFO/Queue is not full */
+
+  regval = fdcan_getreg(priv, STM32_FDCAN_TXFQS_OFFSET);
+  return (regval & FDCAN_TXFQS_TFQF) == 0;
+}
+
+/****************************************************************************
+ * Name: fdcan_txempty
+ *
+ * Description:
+ *   Return true if all message have been sent.  If for example, the FDCAN
+ *   hardware implements FIFOs, then this would mean the transmit FIFO is
+ *   empty.  This method is called when the driver needs to make sure that
+ *   all characters are "drained" from the TX hardware before calling
+ *   co_shutdown().
+ *
+ * Input Parameters:
+ *   dev - An instance of the "upper half" can driver state structure.
+ *
+ * Returned Value:
+ *   True if there are no pending TX transfers in the FDCAN hardware.
+ *
+ ****************************************************************************/
+
+static bool fdcan_txempty(struct can_dev_s *dev)
+{
+  struct stm32_fdcan_s *priv = dev->cd_priv;
+
+  DEBUGASSERT(priv != NULL && priv->config != NULL);
+
+  /* No pending transmission request and no completion left to report */
+
+  return priv->txpending == 0 &&
+         fdcan_getreg(priv, STM32_FDCAN_TXBRP_OFFSET) == 0;
+}
+
+/****************************************************************************
+ * Name: fdcan_error
+ *
+ * Description:
+ *   Report a CAN error
+ *
+ * Input Parameters:
+ *   dev        - CAN-common state data
+ *   status     - Interrupt status with error bits set
+ *   psr        - Protocol status read with the interrupt (LEC/DLEC are
+ *                reset on read)
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_CAN_ERRORS
+static void fdcan_error(struct can_dev_s *dev, uint32_t status,
+                        uint32_t psr)
+{
+  struct can_hdr_s      hdr;
+  uint32_t              lec     = 0;
+  uint16_t              errbits = 0;
+  uint8_t               data[CAN_ERROR_DLC];
+  int                   ret     = 0;
+
+  /* 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.
+   */
+
+  if ((psr & FDCAN_PSR_EP) != 0)
+    {
+      data[1] |= (CAN_ERROR1_RXPASSIVE | CAN_ERROR1_TXPASSIVE);
+    }
+
+  if ((psr & FDCAN_PSR_EW) != 0)
+    {
+      data[1] |= (CAN_ERROR1_RXWARNING | CAN_ERROR1_TXWARNING);
+    }
+
+  if ((status & (FDCAN_IR_EP | FDCAN_IR_EW)) != 0)
+    {
+      /* "Error Passive" or "Error Warning" status changed */
+
+      errbits |= CAN_ERROR_CONTROLLER;
+    }
+
+  /* Protocol error in arbitration or data phase.  LEC/DLEC hold an error
+   * code, not a bit mask.
+   */
+
+  if ((status & (FDCAN_IR_PEA | FDCAN_IR_PED)) != 0)
+    {
+      if ((status & FDCAN_IR_PED) != 0)
+        {
+          lec = (psr & FDCAN_PSR_DLEC_MASK) >> FDCAN_PSR_DLEC_SHIFT;
+        }
+      else
+        {
+          lec = (psr & FDCAN_PSR_LEC_MASK) >> FDCAN_PSR_LEC_SHIFT;
+        }
+
+      switch (lec)
+        {
+          case FDCAN_PSR_EC_STUFF_ERROR:
+            errbits |= CAN_ERROR_PROTOCOL;
+            data[2] |= CAN_ERROR2_STUFF;
+            break;
+
+          case FDCAN_PSR_EC_FORM_ERROR:
+            errbits |= CAN_ERROR_PROTOCOL;
+            data[2] |= CAN_ERROR2_FORM;
+            break;
+
+          case FDCAN_PSR_EC_ACK_ERROR:
+            errbits |= CAN_ERROR_NOACK;
+            break;
+
+          case FDCAN_PSR_EC_BIT0_ERROR:
+            errbits |= CAN_ERROR_PROTOCOL;
+            data[2] |= CAN_ERROR2_BIT0;
+            break;
+
+          case FDCAN_PSR_EC_BIT1_ERROR:
+            errbits |= CAN_ERROR_PROTOCOL;
+            data[2] |= CAN_ERROR2_BIT1;
+            break;
+
+          case FDCAN_PSR_EC_CRC_ERROR:
+            errbits |= CAN_ERROR_PROTOCOL;
+            data[3] |= (CAN_ERROR3_CRCSEQ | CAN_ERROR3_CRCDEL);
+            break;
+
+          default:
+            break;
+        }
+    }
+
+  if ((status & FDCAN_IR_BO) != 0)
+    {
+      /* Bus_Off Status changed */
+
+      if ((psr & FDCAN_PSR_BO) != 0)
+        {
+          errbits |= CAN_ERROR_BUSOFF;
+        }
+      else
+        {
+          errbits |= CAN_ERROR_RESTARTED;
+        }
+    }
+
+  if ((status & (FDCAN_IR_RF0L | FDCAN_IR_RF1L)) != 0)
+    {
+      /* Receive FIFO 0/1 Message Lost */
+
+      errbits |= CAN_ERROR_CONTROLLER;
+      data[1] |= CAN_ERROR1_RXOVERFLOW;
+    }
+
+  if ((status & FDCAN_IR_TEFL) != 0)
+    {
+      /* Tx Event FIFO Element Lost */
+
+      errbits |= CAN_ERROR_CONTROLLER;
+      data[1] |= CAN_ERROR1_TXOVERFLOW;
+    }
+
+  if ((status & FDCAN_IR_TOO) != 0)
+    {
+      /* Timeout Occurred */
+
+      errbits |= CAN_ERROR_TXTIMEOUT;
+    }
+
+  if ((status & (FDCAN_IR_MRAF | FDCAN_IR_ELO)) != 0)
+    {
+      /* Message RAM Access Failure
+       * Error Logging Overflow
+       */
+
+      errbits |= CAN_ERROR_CONTROLLER;
+      data[1] |= CAN_ERROR1_UNSPEC;
+    }
+
+  if (errbits != 0)
+    {
+      /* Format the CAN header for the error report. */
+
+      memset(&hdr, 0, sizeof(hdr));
+      hdr.ch_id     = errbits;
+      hdr.ch_dlc    = CAN_ERROR_DLC;
+      hdr.ch_error  = 1;
+
+      /* And provide the error report to the upper half logic */
+
+      ret = can_receive(dev, &hdr, data);
+      if (ret < 0)
+        {
+          canerr("ERROR: can_receive failed: %d\n", ret);
+        }
+    }
+}
+#endif /* CONFIG_CAN_ERRORS */
+
+/****************************************************************************
+ * Name: fdcan_receive
+ *
+ * Description:
+ *   Receive an FDCAN messages
+ *
+ * Input Parameters:
+ *   dev      - CAN-common state data
+ *   rxbuffer - The RX FIFO element containing the received message
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void fdcan_receive(struct can_dev_s *dev,
+                          volatile uint32_t *rxbuffer)
+{
+  struct stm32_fdcan_s *priv = dev->cd_priv;
+  struct can_hdr_s      hdr;
+  uint32_t              data[(FDCAN_ELEMENT_WORDS - 2)];
+  uint32_t              r0;
+  uint32_t              r1;
+  unsigned int          nwords;
+  unsigned int          i;
+  int                   ret;
+#ifdef CONFIG_CAN_TIMESTAMP
+  struct timespec       ts;
+
+  clock_systime_timespec(&ts);
+#endif
+
+  memset(&hdr, 0, sizeof(hdr));
+
+  /* Word R0 contains the CAN ID, word R1 the DLC and frame format */
+
+  r0 = rxbuffer[0];
+  r1 = rxbuffer[1];
+
+  /* Extract the RTR bit */
+
+  hdr.ch_rtr = ((r0 & BUFFER_R0_RTR) != 0);
+
+#ifdef CONFIG_CAN_EXTID
+  if ((r0 & BUFFER_R0_XTD) != 0)
+    {
+      /* Save the extended ID of the newly received message */
+
+      hdr.ch_id    = (r0 & BUFFER_R0_EXTID_MASK) >> BUFFER_R0_EXTID_SHIFT;
+      hdr.ch_extid = 1;
+    }
+  else
+    {
+      hdr.ch_id    = (r0 & BUFFER_R0_STDID_MASK) >> BUFFER_R0_STDID_SHIFT;
+      hdr.ch_extid = 0;
+    }
+
+#else
+  if ((r0 & BUFFER_R0_XTD) != 0)
+    {
+      /* Drop any messages with extended IDs */
+
+      canerr("ERROR: Received message with extended identifier.  Dropped\n");
+
+      return;
+    }
+
+  /* Save the standard ID of the newly received message */
+
+  hdr.ch_id = (r0 & BUFFER_R0_STDID_MASK) >> BUFFER_R0_STDID_SHIFT;
+#endif
+
+  hdr.ch_dlc = (r1 & BUFFER_R1_DLC_MASK) >> BUFFER_R1_DLC_SHIFT;
+
+#ifdef CONFIG_CAN_FD
+  /* CAN FD format */
+
+  hdr.ch_esi = ((r0 & BUFFER_R0_ESI) != 0);
+  hdr.ch_edl = ((r1 & BUFFER_R1_FDF) != 0);
+  hdr.ch_brs = ((r1 & BUFFER_R1_BRS) != 0);
+#else
+  if ((r1 & BUFFER_R1_FDF) != 0)
+    {
+      /* Drop any FD CAN messages if not supported */
+
+      canerr("ERROR: Received CAN FD message.  Dropped\n");
+
+      return;
+    }
+#endif
+
+#ifdef CONFIG_CAN_TIMESTAMP
+  hdr.ch_ts.tv_sec  = ts.tv_sec;
+  hdr.ch_ts.tv_usec = ts.tv_nsec / 1000;
+#endif
+
+  /* Copy the payload out of message RAM with word accesses */
+
+  nwords = (fdcan_dlc2bytes(priv, hdr.ch_dlc) + 3) >> 2;
+  for (i = 0; i < nwords; i++)
+    {
+      data[i] = rxbuffer[2 + i];
+    }
+
+  /* And provide the CAN message to the upper half logic */
+
+  ret = can_receive(dev, &hdr, (uint8_t *)data);
+  if (ret < 0)
+    {
+      canerr("ERROR: can_receive failed: %d\n", ret);
+    }
+}
+
+/****************************************************************************
+ * Name: fdcan_rxfifo
+ *
+ * Description:
+ *   Drain one RX FIFO
+ *
+ * Input Parameters:
+ *   dev         - CAN-common state data
+ *   offset_rxfs - RX FIFO status register offset
+ *   offset_rxfa - RX FIFO acknowledge register offset
+ *   rxfifo      - RX FIFO in message RAM
+ *   ie          - Current interrupt enable value (updated)
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static void fdcan_rxfifo(struct can_dev_s *dev, int offset_rxfs,
+                         int offset_rxfa, volatile uint32_t *rxfifo,
+                         uint32_t *ie)
+{
+  struct stm32_fdcan_s *priv   = dev->cd_priv;
+  uint32_t              regval = 0;
+  unsigned int          ndx    = 0;
+
+  /* RXF0S and RXF1S share the same layout */
+
+  for (; ; )
+    {
+      regval = fdcan_getreg(priv, offset_rxfs);
+      if ((regval & FDCAN_RXF0S_F0FL_MASK) == 0)
+        {
+          break;
+        }
+
+      ndx = (regval & FDCAN_RXF0S_F0GI_MASK) >> FDCAN_RXF0S_F0GI_SHIFT;
+
+      fdcan_receive(dev, rxfifo + ndx * FDCAN_ELEMENT_WORDS);
+
+      /* Acknowledge reading the FIFO entry */
+
+      fdcan_putreg(priv, offset_rxfa, ndx);
+
+      /* Turning back on all configured RX error interrupts */
+
+      if ((*ie & FDCAN_RXERR_INTS) != (priv->rxints & FDCAN_RXERR_INTS))
+        {
+          *ie |= (priv->rxints & FDCAN_RXERR_INTS);
+          fdcan_putreg(priv, STM32_FDCAN_IE_OFFSET, *ie);
+        }
+    }
+}
+
+/****************************************************************************
+ * Name: fdcan_interrupt
+ *
+ * Description:
+ *   Common FDCAN interrupt handler
+ *
+ * Input Parameters:
+ *   dev - CAN-common state data
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+static int fdcan_interrupt(int irq, void *context, void *arg)
+{
+  struct can_dev_s            *dev     = (struct can_dev_s *)arg;
+  struct stm32_fdcan_s        *priv    = NULL;
+  const struct stm32_config_s *config  = NULL;
+  uint32_t                     ir      = 0;
+  uint32_t                     ie      = 0;
+  uint32_t                     pending = 0;
+  uint32_t                     done    = 0;
+  uint32_t                     psr     = 0;
+
+  DEBUGASSERT(dev != NULL);
+  priv = dev->cd_priv;
+  DEBUGASSERT(priv && priv->config);
+  config = priv->config;
+
+  /* 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 any errors */
+
+  if ((pending & FDCAN_ANYERR_INTS) != 0)
+    {
+      /* LEC/DLEC are reset on read, read PSR once */
+
+      psr = fdcan_getreg(priv, STM32_FDCAN_PSR_OFFSET);
+
+      /* Check for common errors */
+
+      if ((pending & FDCAN_CMNERR_INTS) != 0)
+        {
+          canerr("ERROR: Common %08" PRIx32 "\n",
+                 pending & FDCAN_CMNERR_INTS);
+
+          /* When a protocol error occurs, the problem is recorded in
+           * the LEC/DLEC fields of the PSR register. In lieu of
+           * separate interrupt flags for each error, the hardware
+           * groups protocol 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
+           */
+
+          if ((psr & FDCAN_PSR_LEC_MASK) != 0)
+            {
+              canerr("ERROR: PSR %08" PRIx32 "\n", psr);
+              ie &= ~(FDCAN_IR_PEA | FDCAN_IR_PED);
+              fdcan_putreg(priv, STM32_FDCAN_IE_OFFSET, ie);
+              caninfo("disabled protocol error interrupts\n");
+            }
+
+          /* Clear the error indications */
+
+          fdcan_putreg(priv, STM32_FDCAN_IR_OFFSET, FDCAN_CMNERR_INTS);
+        }
+
+      /* Check for transmission errors */
+
+      if ((pending & FDCAN_TXERR_INTS) != 0)
+        {
+          canerr("ERROR: TX %08" PRIx32 "\n",
+                 pending & FDCAN_TXERR_INTS);
+
+          /* Clear the error indications */
+
+          fdcan_putreg(priv, STM32_FDCAN_IR_OFFSET, FDCAN_TXERR_INTS);
+        }
+
+      /* Check for reception errors */
+
+      if ((pending & FDCAN_RXERR_INTS) != 0)
+        {
+          canerr("ERROR: RX %08" PRIx32 "\n",
+                 pending & FDCAN_RXERR_INTS);
+
+          /* 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);
+        }
+
+#ifdef CONFIG_CAN_ERRORS
+      /* Report errors */
+
+      fdcan_error(dev, pending & FDCAN_ANYERR_INTS, psr);
+#endif
+    }
+
+  /* Check for successful completion of a transmission */
+
+  if ((pending & FDCAN_IR_TC) != 0)
+    {
+      /* Check if we have disabled the protocol error interrupts above
+       * to prevent Interrupt-Flooding and re-enable them here again.
+       */
+
+      if ((ie & (FDCAN_IR_PEA | FDCAN_IR_PED)) == 0)
+        {
+          ie |= (FDCAN_IR_PEA | FDCAN_IR_PED);
+          fdcan_putreg(priv, STM32_FDCAN_IE_OFFSET, ie);
+          caninfo("Re-enabled protocol error interrupts\n");
+        }
+
+      /* Clear the pending TX completion interrupt (and all
+       * other TX-related interrupts)
+       */
+
+      fdcan_putreg(priv, STM32_FDCAN_IR_OFFSET, priv->txints);
+
+      /* TXBTO stays set until a buffer is reused, so only count buffers
+       * that are still pending.  Release them all before notifying the
+       * upper half, which may queue new frames into the released buffers.
+       */
+
+      done = fdcan_getreg(priv, STM32_FDCAN_TXBTO_OFFSET) & priv->txpending;
+      priv->txpending &= ~done;
+
+      for (; done != 0; done &= done - 1)
+        {
+          /* Tell the upper half that the transfer is finished. */
+
+          can_txdone(dev);
+        }
+    }
+  else if ((pending & priv->txints) != 0)
+    {
+      /* Clear unhandled TX events */
+
+      fdcan_putreg(priv, STM32_FDCAN_IR_OFFSET, priv->txints);
+    }
+
+  /* Clear the RX new message interrupts before draining the FIFOs, so that
+   * a message arriving meanwhile raises a new interrupt.
+   */
+
+  fdcan_putreg(priv, STM32_FDCAN_IR_OFFSET, FDCAN_IR_RF0N | FDCAN_IR_RF1N);
+
+  /* We treat RX FIFO1 as the "high priority" queue:  We will process
+   * all messages in RX FIFO1 before processing any message from RX
+   * FIFO0.
+   */
+
+  fdcan_rxfifo(dev, STM32_FDCAN_RXF1S_OFFSET, STM32_FDCAN_RXF1A_OFFSET,
+               config->msgram.rxfifo1, &ie);
+  fdcan_rxfifo(dev, STM32_FDCAN_RXF0S_OFFSET, STM32_FDCAN_RXF0A_OFFSET,
+               config->msgram.rxfifo0, &ie);
+
+  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)
+{
+  const struct stm32_config_s *config = priv->config;
+  uint32_t                     regval = 0;
+  uint32_t                     dbtp   = 0;
+
+  caninfo("FDCAN%d\n", config->port);
+
+  /* Configure FDCAN pins */
+
+  stm32_configgpio(config->rxpinset);
+  stm32_configgpio(config->txpinset);
+
+  /* Re-enable device if previously 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 and configuration changes */
+
+  fdcan_initmode(priv, true);
+
+  /* Global Filter Configuration:
+   *
+   *   ANFS=2: Reject non matching standard frames
+   *   ANFE=2: Reject non matching extended frames
+   *
+   * Accepting all frames is done by the reserved filters, so filters can
+   * change without leaving normal operation.  Allocated filters are kept
+   * across a restart.
+   */
+
+  fdcan_putreg(priv, STM32_FDCAN_GFC_OFFSET,
+               (2 << FDCAN_GFC_ANFS_SHIFT) | (2 << FDCAN_GFC_ANFE_SHIFT));
+  fdcan_acceptall(priv);
+
+  /* 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.
+   */
+
+  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.  Transmitter delay compensation is needed
+   * for data phase bitrates above ~1 Mbit/s and works with a data
+   * prescaler of 1 or 2 only.  The offset is the sample point in mtq.
+   */
+
+  dbtp = priv->dbtp;
+  if (config->mode == FDCAN_FD_BRS_MODE)
+    {
+      uint32_t dbrp   = (dbtp & FDCAN_DBTP_DBRP_MASK) >>
+                        FDCAN_DBTP_DBRP_SHIFT;
+      uint32_t dtseg1 = (dbtp & FDCAN_DBTP_DTSEG1_MASK) >>
+                        FDCAN_DBTP_DTSEG1_SHIFT;
+      uint32_t tdco   = (dtseg1 + 2) * (dbrp + 1);
+
+      if (dbrp <= 1 && tdco <= FDCAN_TDCR_TDCO_MAX)
+        {
+          fdcan_putreg(priv, STM32_FDCAN_TDCR_OFFSET,
+                       tdco << FDCAN_TDCR_TDCO_SHIFT);
+          dbtp |= FDCAN_DBTP_TDC;
+        }
+    }
+
+  fdcan_putreg(priv, STM32_FDCAN_NBTP_OFFSET, priv->nbtp);
+  fdcan_putreg(priv, STM32_FDCAN_DBTP_OFFSET, dbtp);
+
+  /* Configure message RAM start addresses (byte offsets) and sizes */
+
+  regval  = ((uintptr_t)config->msgram.stdfilters - STM32_CANRAM_BASE) &
+            FDCAN_SIDFC_FLSSA_MASK;
+  regval |= FDCAN_STDFILTER_SIZE << FDCAN_SIDFC_LSS_SHIFT;
+  fdcan_putreg(priv, STM32_FDCAN_SIDFC_OFFSET, regval);
+
+  regval  = ((uintptr_t)config->msgram.extfilters - STM32_CANRAM_BASE) &
+            FDCAN_XIDFC_FLESA_MASK;
+  regval |= FDCAN_EXTFILTER_SIZE << FDCAN_XIDFC_LSE_SHIFT;
+  fdcan_putreg(priv, STM32_FDCAN_XIDFC_OFFSET, regval);
+
+  regval  = ((uintptr_t)config->msgram.rxfifo0 - STM32_CANRAM_BASE) &
+            FDCAN_RXF0C_F0SA_MASK;
+  regval |= FDCAN_RXFIFO0_SIZE << FDCAN_RXF0C_F0S_SHIFT;
+  fdcan_putreg(priv, STM32_FDCAN_RXF0C_OFFSET, regval);
+
+  regval  = ((uintptr_t)config->msgram.rxfifo1 - STM32_CANRAM_BASE) &
+            FDCAN_RXF1C_F1SA_MASK;
+  regval |= FDCAN_RXFIFO1_SIZE << FDCAN_RXF1C_F1S_SHIFT;
+  fdcan_putreg(priv, STM32_FDCAN_RXF1C_OFFSET, regval);
+
+  /* No dedicated RX buffers and no TX event FIFO */
+
+  fdcan_putreg(priv, STM32_FDCAN_RXBC_OFFSET, 0);
+  fdcan_putreg(priv, STM32_FDCAN_TXEFC_OFFSET, 0);
+
+  /* Element data field sizes */
+
+  regval = (FDCAN_ELEMENT_DS << FDCAN_RXESC_F0DS_SHIFT) |
+           (FDCAN_ELEMENT_DS << FDCAN_RXESC_F1DS_SHIFT) |
+           (FDCAN_ELEMENT_DS << FDCAN_RXESC_RBDS_SHIFT);
+  fdcan_putreg(priv, STM32_FDCAN_RXESC_OFFSET, regval);
+  fdcan_putreg(priv, STM32_FDCAN_TXESC_OFFSET,
+               FDCAN_ELEMENT_DS << FDCAN_TXESC_TBDS_SHIFT);
+
+  /* TX FIFO/queue, no dedicated TX buffers */
+
+  regval  = ((uintptr_t)config->msgram.txfifoq - STM32_CANRAM_BASE) &
+            FDCAN_TXBC_TBSA_MASK;
+  regval |= FDCAN_TXFIFOQ_SIZE << FDCAN_TXBC_TFQS_SHIFT;
+#ifdef CONFIG_STM32_FDCAN_QUEUE_MODE
+  regval |= FDCAN_TXBC_TFQM;
+#endif
+  fdcan_putreg(priv, STM32_FDCAN_TXBC_OFFSET, regval);
+
+  /* CC control register */
+
+  regval  = fdcan_getreg(priv, STM32_FDCAN_CCCR_OFFSET);
+  regval &= ~(FDCAN_CCCR_NISO | FDCAN_CCCR_FDOE | FDCAN_CCCR_BRSE |
+              FDCAN_CCCR_TEST | FDCAN_CCCR_MON);
+
+  /* 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;
+        }
+    }
+
+  /* Connection modes.  Loopback with bus monitoring is the internal
+   * loopback (TX and RX disconnected from the bus).
+   *
+   *   FDCAN_CCCR_TEST  - Test mode enable
+   *   FDCAN_CCCR_MON   - Bus monitoring mode
+   *   FDCAN_TEST_LBCK  - Loopback mode
+   */
+
+  if (priv->silent)
+    {
+      regval |= FDCAN_CCCR_MON;
+    }
+
+  if (priv->loopback)
+    {
+      regval |= FDCAN_CCCR_TEST;
+    }
+
+  fdcan_putreg(priv, STM32_FDCAN_CCCR_OFFSET, regval);
+  fdcan_putreg(priv, STM32_FDCAN_TEST_OFFSET,
+               priv->loopback ? FDCAN_TEST_LBCK : 0);
+
+  /* Select RX-related interrupts */
+
+  priv->rxints = FDCAN_RXFIFO_INTS;
+
+  /* Select TX-related interrupts */
+
+  priv->txints = FDCAN_TXFIFOQ_INTS;
+
+  /* Transmission completed interrupts for all TX buffers */
+
+  fdcan_putreg(priv, STM32_FDCAN_TXBTIE_OFFSET, FDCAN_TXBUFFERS_ALL);
+
+  /* Enable only interrupt line 0.  ILS was cleared above, so all
+   * interrupts are directed to line 0.
+   */
+
+  fdcan_putreg(priv, STM32_FDCAN_ILE_OFFSET, FDCAN_ILE_EINT0);
+
+  /* Disable initialization mode to enable normal operation */
+
+  priv->txpending = 0;
+  fdcan_initmode(priv, false);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: fdcan_bittiming
+ *
+ * Description:
+ *   Convert desired bitrate to FDCAN bit segment values
+ *   The computed values apply to both data and arbitration phases
+ *
+ * Input Parameters:
+ *   timing - structure to store bit timing
+ *
+ * Returned Value:
+ *   OK on success; >0 on failure.
+ ****************************************************************************/
+
+static int fdcan_bittiming(struct fdcan_bitseg *timing)
+{
+  /* Implementation ported from PX4's uavcan_drivers/stm32[h7]
+   *
+   * Ref. "Automatic Baudrate Detection in CANopen Networks", U. Koppe
+   *  MicroControl GmbH & Co. KG
+   *  CAN in Automation, 2003
+   *
+   * According to the source, optimal quanta per bit are:
+   *   Bitrate        Optimal Maximum
+   *   1000 kbps      8       10
+   *   500  kbps      16      17
+   *   250  kbps      16      17
+   *   125  kbps      16      17
+   */
+
+  const uint32_t target_bitrate    = timing->bitrate;
+  static const int32_t max_bs1     = 16;
+  static const int32_t max_bs2     = 8;
+  const uint8_t max_quanta_per_bit = (timing->bitrate >= 1000000) ? 10 : 17;
+  static const int max_sp_location = 900;
+
+  /* Computing (prescaler * BS):
+   *   BITRATE = 1 / (PRESCALER * (1 / PCLK) * (1 + BS1 + BS2))
+   *   BITRATE = PCLK / (PRESCALER * (1 + BS1 + BS2))
+   * let:
+   *   BS = 1 + BS1 + BS2
+   *     (BS == total number of time quanta per bit)
+   *   PRESCALER_BS = PRESCALER * BS
+   * ==>
+   *   PRESCALER_BS = PCLK / BITRATE
+   */
+
+  if (target_bitrate == 0)
+    {
+      return 1;
+    }
+
+  const uint32_t prescaler_bs = STM32_FDCANCLK / target_bitrate;
+
+  /* Find prescaler value such that the number of quanta per bit is highest */
+
+  uint8_t bs1_bs2_sum = max_quanta_per_bit - 1;
+
+  while ((prescaler_bs % (1 + bs1_bs2_sum)) != 0)
+    {
+      if (bs1_bs2_sum <= 2)
+        {
+          canerr("Target bitrate too high - no solution possible.");
+          return 1; /* No solution */
+        }
+
+      bs1_bs2_sum--;
+    }
+
+  const uint32_t prescaler = prescaler_bs / (1 + bs1_bs2_sum);
+
+  if ((prescaler < 1U) || (prescaler > 512U))
+    {
+      canerr("Target bitrate invalid - bad prescaler.");
+      return 2; /* No solution */
+    }
+
+  /* Now we have a constraint: (BS1 + BS2) == bs1_bs2_sum.
+   * We need to find the values so that the sample point is as close as
+   * possible to the optimal value.
+   *
+   *   Solve[(1 + bs1)/(1 + bs1 + bs2) == 7/8, bs2]
+   *     (Where 7/8 is 0.875, the recommended sample point location)
+   *   {{bs2 -> (1 + bs1)/7}}
+   *
+   * Hence:
+   *   bs2 = (1 + bs1) / 7
+   *   bs1 = (7 * bs1_bs2_sum - 1) / 8
+   *
+   * Sample point location can be computed as follows:
+   *   Sample point location = (1 + bs1) / (1 + bs1 + bs2)
+   *
+   * Since the optimal solution is so close to the maximum, we prepare two
+   * solutions, and then pick the best one:
+   *   - With rounding to nearest
+   *   - With rounding to zero
+   */
+
+  /* First attempt with rounding to nearest */
+
+  uint8_t bs1 = (uint8_t)((7 * bs1_bs2_sum - 1) + 4) / 8;
+  uint8_t bs2 = (uint8_t)(bs1_bs2_sum - bs1);
+  uint16_t sample_point_permill =
+    (uint16_t)(1000 * (1 + bs1) / (1 + bs1 + bs2));
+
+  if (sample_point_permill > max_sp_location)
+    {
+      /* Second attempt with rounding to zero */
+
+      bs1 = (7 * bs1_bs2_sum - 1) / 8;
+      bs2 = bs1_bs2_sum - bs1;
+    }
+
+  bool valid = (bs1 >= 1) && (bs1 <= max_bs1) && (bs2 >= 1) &&
+    (bs2 <= max_bs2);
+
+  /* Final validation */
+
+  if (target_bitrate != (STM32_FDCANCLK /
+                        (prescaler * (1 + bs1 + bs2))) || !valid)
+    {
+      canerr("Target bitrate invalid - solution does not match.");
+      return 3; /* Solution not found */
+    }
+
+  timing->bs1 = (uint8_t)(bs1 - 1);
+  timing->bs2 = (uint8_t)(bs2 - 1);
+  timing->prescaler = (uint16_t)(prescaler - 1);
+  timing->sjw = 0; /* Which means one */
+
+  return 0;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: stm32_fdcaninitialize
+ *
+ * Description:
+ *   Initialize the selected FDCAN port
+ *
+ * Input Parameters:
+ *   port - Port number (for hardware that has multiple FDCAN interfaces),
+ *          1=FDCAN1, 2=FDCAN2, 3=FDCAN3
+ *
+ * Returned Value:
+ *   Valid CAN device structure reference on success; a NULL on failure
+ *
+ ****************************************************************************/
+
+struct can_dev_s *stm32_fdcaninitialize(int port)
+{
+  struct can_dev_s            *dev    = NULL;
+  struct stm32_fdcan_s        *priv   = NULL;
+  const struct stm32_config_s *config = NULL;
+  struct fdcan_bitseg          timing;
+  irqstate_t                   flags;
+
+  caninfo("FDCAN%d\n", port);
+
+  /* Select FDCAN peripheral to be initialized */
+
+#ifdef CONFIG_STM32_FDCAN1
+  if (port == FDCAN1)
+    {
+      /* Select the FDCAN1 device structure */
+
+      dev    = &g_fdcan1dev;
+      priv   = &g_fdcan1priv;
+      config = &g_fdcan1const;
+    }
+  else
+#endif
+#ifdef CONFIG_STM32_FDCAN2
+  if (port == FDCAN2)
+    {
+      /* Select the FDCAN2 device structure */
+
+      dev    = &g_fdcan2dev;
+      priv   = &g_fdcan2priv;
+      config = &g_fdcan2const;
+    }
+  else
+#endif
+#ifdef CONFIG_STM32_FDCAN3
+  if (port == FDCAN3)
+    {
+      /* Select the FDCAN3 device structure */
+
+      dev    = &g_fdcan3dev;
+      priv   = &g_fdcan3priv;
+      config = &g_fdcan3const;
+    }
+  else
+#endif
+    {
+      canerr("ERROR: Unsupported port %d\n", port);
+      return NULL;
+    }
+
+  /* The RCC reset is shared by all instances, reset only once */
+
+  flags = enter_critical_section();
+  if (!g_fdcan_rccreset)
+    {
+      modifyreg32(STM32_RCC_APB1HRSTR, 0, RCC_APB1HRSTR_FDCANRST);
+      modifyreg32(STM32_RCC_APB1HRSTR, RCC_APB1HRSTR_FDCANRST, 0);
+      g_fdcan_rccreset = true;
+    }
+
+  leave_critical_section(flags);
+
+  /* Perform one time data initialization */
+
+  memset(priv, 0, sizeof(struct stm32_fdcan_s));
+  priv->config = config;
+  fdcan_clearfilters(priv);
+
+#ifdef STM32_FDCAN_LOOPBACK
+  /* Internal loopback: TX and RX disconnected from the bus */
+
+  priv->loopback = config->loopback;
+  priv->silent   = config->loopback;
+#endif
+
+  /* Set the initial bit timing.  This might change subsequently
+   * due to IOCTL command processing.
+   */
+
+  if (!config->autotiming)
+    {
+      priv->nbtp = config->nbtp;
+      priv->dbtp = config->dbtp;
+    }
+  else
+    {
+      timing.bitrate = config->baud;
+      if (fdcan_bittiming(&timing) != OK)
+        {
+          canerr("ERROR: Invalid CAN nominal phase timings\n");
+          return NULL;
+        }
+
+      priv->nbtp = FDCAN_NBTP_VALUE(timing.prescaler, timing.bs1,
+                                    timing.bs2, timing.sjw);
+
+      if (config->mode == FDCAN_FD_BRS_MODE)
+        {
+          timing.bitrate = config->data_baud;
+          if (fdcan_bittiming(&timing) != OK ||
+              timing.prescaler > FDCAN_DBTP_DBRP_MAX ||
+              timing.bs1 > FDCAN_DBTP_DTSEG1_MAX ||
+              timing.bs2 > FDCAN_DBTP_DTSEG2_MAX)
+            {
+              canerr("ERROR: Invalid CAN data phase timings\n");
+              return NULL;
+            }
+
+          priv->dbtp = FDCAN_DBTP_VALUE(timing.prescaler, timing.bs1,
+                                        timing.bs2, timing.sjw);
+        }
+      else
+        {
+          priv->dbtp = 0;
+        }
+    }
+
+  dev->cd_ops  = &g_fdcanops;
+  dev->cd_priv = (void *)priv;
+
+  /* And put the hardware in the initial state */
+
+  fdcan_reset(dev);
+
+  return dev;
+}
diff --git a/arch/arm/src/stm32h7/stm32_fdcan.h 
b/arch/arm/src/stm32h7/stm32_fdcan.h
new file mode 100644
index 00000000000..50d7327f1c7
--- /dev/null
+++ b/arch/arm/src/stm32h7/stm32_fdcan.h
@@ -0,0 +1,95 @@
+/****************************************************************************
+ * arch/arm/src/stm32h7/stm32_fdcan.h
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+#ifndef __ARCH_ARM_SRC_STM32H7_STM32_FDCAN_H
+#define __ARCH_ARM_SRC_STM32H7_STM32_FDCAN_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include "chip.h"
+#include "hardware/stm32_fdcan.h"
+
+#include <nuttx/can/can.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Port numbers for use with stm32_fdcaninitialize() */
+
+#define FDCAN1 1
+#define FDCAN2 2
+#define FDCAN3 3
+
+/****************************************************************************
+ * Public Types
+ ****************************************************************************/
+
+#ifndef __ASSEMBLY__
+
+/****************************************************************************
+ * Public Data
+ ****************************************************************************/
+
+#undef EXTERN
+#if defined(__cplusplus)
+#define EXTERN extern "C"
+extern "C"
+{
+#else
+#define EXTERN extern
+#endif
+
+/****************************************************************************
+ * Public Function Prototypes
+ ****************************************************************************/
+
+#ifdef CONFIG_STM32_FDCAN_CHARDRIVER
+
+/****************************************************************************
+ * Name: stm32_fdcaninitialize
+ *
+ * Description:
+ *   Initialize the selected FDCAN port
+ *
+ * Input Parameters:
+ *   Port number (for hardware that has multiple FDCAN interfaces)
+ *
+ * Returned Value:
+ *   Valid FDCAN device structure reference on success; a NULL on failure
+ *
+ ****************************************************************************/
+
+struct can_dev_s *stm32_fdcaninitialize(int port);
+#endif
+
+#undef EXTERN
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* __ASSEMBLY__ */
+#endif /* __ARCH_ARM_SRC_STM32H7_STM32_FDCAN_H */

Reply via email to