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