From c450dea6e5e4b04af4b10a4c5444fd57447aec86 Mon Sep 17 00:00:00 2001 From: raiden00pl Date: Tue, 11 Jan 2022 17:59:10 +0100 Subject: [PATCH] stm32: add FDCAN support based on PR #2987 --- arch/arm/src/stm32/Kconfig | 382 +- arch/arm/src/stm32/Make.defs | 6 + arch/arm/src/stm32/hardware/stm32_fdcan.h | 586 +++ .../stm32/hardware/stm32g4xxxx_memorymap.h | 2 +- arch/arm/src/stm32/stm32_fdcan.c | 3453 +++++++++++++++++ arch/arm/src/stm32/stm32_fdcan.h | 90 + arch/arm/src/stm32/stm32g4xxxx_rcc.c | 9 + 7 files changed, 4526 insertions(+), 2 deletions(-) create mode 100644 arch/arm/src/stm32/hardware/stm32_fdcan.h create mode 100644 arch/arm/src/stm32/stm32_fdcan.c create mode 100644 arch/arm/src/stm32/stm32_fdcan.h diff --git a/arch/arm/src/stm32/Kconfig b/arch/arm/src/stm32/Kconfig index 567cc9a8b6f..77a3efed1a4 100644 --- a/arch/arm/src/stm32/Kconfig +++ b/arch/arm/src/stm32/Kconfig @@ -2914,16 +2914,19 @@ config STM32_FDCAN1 bool "FDCAN1" default n depends on STM32_HAVE_FDCAN1 + select STM32_FDCAN config STM32_FDCAN2 bool "FDCAN2" default n depends on STM32_HAVE_FDCAN2 + select STM32_FDCAN config STM32_FDCAN3 bool "FDCAN3" default n depends on STM32_HAVE_FDCAN3 + select STM32_FDCAN config STM32_FSMC bool "FSMC" @@ -3390,6 +3393,10 @@ config STM32_CAN bool select ARCH_HAVE_CAN_ERRORS +config STM32_FDCAN + bool + select ARCH_HAVE_CAN_ERRORS + config STM32_TIM bool default n @@ -10884,7 +10891,380 @@ config STM32_CAN_REGDEBUG Output detailed register-level CAN device debug information. Requires also CONFIG_DEBUG_CAN_INFO. -endmenu +endmenu # "CAN driver configuration" + +menu "FDCAN driver configuration" + depends on STM32_FDCAN + +choice + prompt "FDCAN character driver or SocketCAN support" + default STM32_FDCAN_CHARDRIVER + +config STM32_FDCAN_CHARDRIVER + bool "STM32 FDCAN character driver support" + select ARCH_HAVE_FDCAN_ERRORS + +config STM32_FDCAN_SOCKET + bool "STM32 FDCAN SocketCAN support (not supported yet)" + select NET_FDCAN_HAVE_ERRORS + +endchoice # FDCAN character driver or SocketCAN support + +config STM32_FDCAN_REGDEBUG + bool "CAN Register level debug" + depends on DEBUG_CAN_INFO + default n + ---help--- + Output detailed register-level CAN device debug information. + Requires also CONFIG_DEBUG_CAN_INFO. + +config STM32_FDCAN_QUEUE_MODE + bool "FDCAN QUEUE mode (vs FIFO mode)" + default n + +menu "FDCAN1 device driver options" + depends on STM32_FDCAN1 + +choice + prompt "FDCAN1 frame format" + default STM32_FDCAN1_ISO11898_1 + +config STM32_FDCAN1_ISO11898_1 + bool "ISO11898-1" + ---help--- + Enable ISO11898-1 frame format + +config STM32_FDCAN1_NONISO_FORMAT + bool "Non ISO" + ---help--- + Enable Non ISO, Bosch CAN FD Specification V1.0 + +endchoice # FDCAN1 frame format + +choice + prompt "FDCAN1 mode" + default STM32_FDCAN1_CLASSIC + +config STM32_FDCAN1_CLASSIC + bool "Classic CAN" + ---help--- + Enable Clasic CAN mode + +config STM32_FDCAN1_FD + bool "CAN FD" + depends on CAN_FD || NET_CAN_CANFD + ---help--- + Enable CAN FD mode + +config STM32_FDCAN1_FD_BRS + bool "CAN FD with fast bit rate switching" + depends on CAN_FD || NET_CAN_CANFD + ---help--- + Enable CAN FD mode with fast bit rate switching mode. + +endchoice # FDCAN1 mode + +config STM32_FDCAN1_LOOPBACK + bool "Enable FDCAN1 loopback mode" + default n + ---help--- + Enable the FDCAN1 local loopback mode for testing purposes. + +comment "Nominal Bit Timing" + +config STM32_FDCAN1_BITRATE + int "FDCAN bitrate" + default 500000 + range 0 1000000 + ---help--- + FDCAN1 bitrate in bits per second. Required if STM32_FDCAN1 is defined. + +config STM32_FDCAN1_NTSEG1 + int "FDCAN1 NTSEG1 (PropSeg + PhaseSeg1)" + default 6 + range 1 256 if STM32_STM32G4XXX + ---help--- + The length of the bit time is Tquanta * (SyncSeg + PropSeg + PhaseSeg1 + PhaseSeg2). + +config STM32_FDCAN1_NTSEG2 + int "FDCAN1 NTSEG2 (PhaseSeg2)" + default 7 + range 1 128 if STM32_STM32G4XXX + ---help--- + The length of the bit time is Tquanta * (SyncSeg + PropSeg + PhaseSeg1 + PhaseSeg2). + +config STM32_FDCAN1_NSJW + int "FDCAN1 synchronization jump width" + default 1 + range 1 128 if STM32_STM32G4XXX + ---help--- + The length of the bit time is Tquanta * (SyncSeg + PropSeg + PhaseSeg1 + PhaseSeg2). + +comment "Data Bit Timing" + depends on CAN_FD && STM32_FDCAN1_FD_BRS + +config STM32_FDCAN1_DBITRATE + int "FDCAN1 data bitrate" + default 2000000 + depends on CAN_FD && STM32_FDCAN1_FD_BRS + ---help--- + FDCAN1 bitrate in bits per second. Required if operating in FD mode with bit rate switching (BRS). + +config STM32_FDCAN1_DTSEG1 + int "FDCAN1 DTSEG1 (PropSeg + PhaseSeg1 of data phase)" + default 4 + range 1 31 if STM32_STM32G4XXX + depends on CAN_FD && STM32_FDCAN1_FD_BRS + ---help--- + The length of the bit time is Tquanta * (SyncSeg + PropSeg + PhaseSeg1 + PhaseSeg2). + +config STM32_FDCAN1_DTSEG2 + int "FDCAN1 DTSEG2 (PhaseSeg2 of data phase)" + default 4 + range 1 15 if STM32_STM32G4XXX + depends on CAN_FD && STM32_FDCAN1_FD_BRS + ---help--- + The length of the bit time is Tquanta * (SyncSeg + PropSeg + PhaseSeg1 + PhaseSeg2). + +config STM32_FDCAN1_DSJW + int "FDCAN1 fast synchronization jump width" + default 2 + range 1 15 if STM32_STM32G4XXX + depends on CAN_FD && STM32_FDCAN1_FD_BRS + ---help--- + The duration of a synchronization jump is Tcan_clk x DSJW. + +endmenu # FDCAN1 device driver options + +menu "FDCAN2 device driver options" + depends on STM32_FDCAN2 + +choice + prompt "FDCAN2 frame format" + default STM32_FDCAN2_ISO11898_1 + +config STM32_FDCAN2_ISO11898_1 + bool "ISO11898-1" + ---help--- + Enable ISO11898-1 frame format + +config STM32_FDCAN2_NONISO_FORMAT + bool "Non ISO" + ---help--- + Enable Non ISO, Bosch CAN FD Specification V1.0 + +endchoice # FDCAN2 frame format + +choice + prompt "FDCAN2 mode" + default STM32_FDCAN2_CLASSIC + +config STM32_FDCAN2_CLASSIC + bool "Classic CAN" + ---help--- + Enable Clasic CAN mode + +config STM32_FDCAN2_FD + bool "CAN FD" + depends on CAN_FD || NET_CAN_CANFD + ---help--- + Enable CAN FD mode + +config STM32_FDCAN2_FD_BRS + bool "CAN FD with fast bit rate switching" + depends on CAN_FD || NET_CAN_CANFD + ---help--- + Enable CAN FD mode with fast bit rate switching mode. + +endchoice # FDCAN2 mode + +config STM32_FDCAN2_LOOPBACK + bool "Enable FDCAN2 loopback mode" + default n + ---help--- + Enable the FDCAN2 local loopback mode for testing purposes. + +comment "Nominal Bit Timing" + +config STM32_FDCAN2_BITRATE + int "FDCAN bitrate" + default 500000 + range 0 1000000 + ---help--- + FDCAN2 bitrate in bits per second. Required if STM32_FDCAN2 is defined. + +config STM32_FDCAN2_NTSEG1 + int "FDCAN2 NTSEG1 (PropSeg + PhaseSeg1)" + default 6 + range 1 256 if STM32_STM32G4XXX + ---help--- + The length of the bit time is Tquanta * (SyncSeg + PropSeg + PhaseSeg1 + PhaseSeg2). + +config STM32_FDCAN2_NTSEG2 + int "FDCAN2 NTSEG2 (PhaseSeg2)" + default 7 + range 1 128 if STM32_STM32G4XXX + ---help--- + The length of the bit time is Tquanta * (SyncSeg + PropSeg + PhaseSeg1 + PhaseSeg2). + +config STM32_FDCAN2_NSJW + int "FDCAN2 synchronization jump width" + default 1 + range 1 128 if STM32_STM32G4XXX + ---help--- + The length of the bit time is Tquanta * (SyncSeg + PropSeg + PhaseSeg1 + PhaseSeg2). + +comment "Data Bit Timing" + depends on CAN_FD && STM32_FDCAN2_FD_BRS + +config STM32_FDCAN2_DBITRATE + int "FDCAN2 data bitrate" + default 2000000 + depends on CAN_FD && STM32_FDCAN2_FD_BRS + ---help--- + FDCAN2 bitrate in bits per second. Required if operating in FD mode with bit rate switching (BRS). + +config STM32_FDCAN2_DTSEG1 + int "FDCAN2 DTSEG1 (PropSeg + PhaseSeg1 of data phase)" + default 4 + range 1 31 if STM32_STM32G4XXX + depends on CAN_FD && STM32_FDCAN2_FD_BRS + ---help--- + The length of the bit time is Tquanta * (SyncSeg + PropSeg + PhaseSeg1 + PhaseSeg2). + +config STM32_FDCAN2_DTSEG2 + int "FDCAN2 DTSEG2 (PhaseSeg2 of data phase)" + default 4 + range 1 15 if STM32_STM32G4XXX + depends on CAN_FD && STM32_FDCAN2_FD_BRS + ---help--- + The length of the bit time is Tquanta * (SyncSeg + PropSeg + PhaseSeg1 + PhaseSeg2). + +config STM32_FDCAN2_DSJW + int "FDCAN2 fast synchronization jump width" + default 2 + range 1 15 if STM32_STM32G4XXX + depends on CAN_FD && STM32_FDCAN2_FD_BRS + ---help--- + The duration of a synchronization jump is Tcan_clk x DSJW. + +endmenu # FDCAN2 device driver options + +menu "FDCAN3 device driver options" + depends on STM32_FDCAN3 + +choice + prompt "FDCAN3 frame format" + default STM32_FDCAN3_ISO11898_1 + +config STM32_FDCAN3_ISO11898_1 + bool "ISO11898-1" + ---help--- + Enable ISO11898-1 frame format + +config STM32_FDCAN3_NONISO_FORMAT + bool "Non ISO" + ---help--- + Enable Non ISO, Bosch CAN FD Specification V1.0 + +endchoice # FDCAN3 frame format + +choice + prompt "FDCAN3 mode" + default STM32_FDCAN3_CLASSIC + +config STM32_FDCAN3_CLASSIC + bool "Classic CAN" + ---help--- + Enable Clasic CAN mode + +config STM32_FDCAN3_FD + bool "CAN FD" + depends on CAN_FD || NET_CAN_CANFD + ---help--- + Enable CAN FD mode + +config STM32_FDCAN3_FD_BRS + bool "CAN FD with fast bit rate switching" + depends on CAN_FD || NET_CAN_CANFD + ---help--- + Enable CAN FD mode with fast bit rate switching mode. + +endchoice # FDCAN3 mode + +config STM32_FDCAN3_LOOPBACK + bool "Enable FDCAN3 loopback mode" + default n + ---help--- + Enable the FDCAN3 local loopback mode for testing purposes. + +comment "Nominal Bit Timing" + +config STM32_FDCAN3_BITRATE + int "FDCAN bitrate" + default 500000 + range 0 1000000 + ---help--- + FDCAN3 bitrate in bits per second. Required if STM32_FDCAN3 is defined. + +config STM32_FDCAN3_NTSEG1 + int "FDCAN3 NTSEG1 (PropSeg + PhaseSeg1)" + default 6 + range 1 256 if STM32_STM32G4XXX + ---help--- + The length of the bit time is Tquanta * (SyncSeg + PropSeg + PhaseSeg1 + PhaseSeg2). + +config STM32_FDCAN3_NTSEG2 + int "FDCAN3 NTSEG2 (PhaseSeg2)" + default 7 + range 1 128 if STM32_STM32G4XXX + ---help--- + The length of the bit time is Tquanta * (SyncSeg + PropSeg + PhaseSeg1 + PhaseSeg2). + +config STM32_FDCAN3_NSJW + int "FDCAN3 synchronization jump width" + default 1 + range 1 128 if STM32_STM32G4XXX + ---help--- + The length of the bit time is Tquanta * (SyncSeg + PropSeg + PhaseSeg1 + PhaseSeg2). + +comment "Data Bit Timing" + depends on CAN_FD && STM32_FDCAN3_FD_BRS + +config STM32_FDCAN3_DBITRATE + int "FDCAN3 data bitrate" + default 2000000 + depends on CAN_FD && STM32_FDCAN3_FD_BRS + ---help--- + FDCAN3 bitrate in bits per second. Required if operating in FD mode with bit rate switching (BRS). + +config STM32_FDCAN3_DTSEG1 + int "FDCAN3 DTSEG1 (PropSeg + PhaseSeg1 of data phase)" + default 4 + range 1 31 if STM32_STM32G4XXX + depends on CAN_FD && STM32_FDCAN3_FD_BRS + ---help--- + The length of the bit time is Tquanta * (SyncSeg + PropSeg + PhaseSeg1 + PhaseSeg2). + +config STM32_FDCAN3_DTSEG2 + int "FDCAN3 DTSEG2 (PhaseSeg2 of data phase)" + default 4 + range 1 15 if STM32_STM32G4XXX + depends on CAN_FD && STM32_FDCAN3_FD_BRS + ---help--- + The length of the bit time is Tquanta * (SyncSeg + PropSeg + PhaseSeg1 + PhaseSeg2). + +config STM32_FDCAN3_DSJW + int "FDCAN3 fast synchronization jump width" + default 2 + range 1 15 if STM32_STM32G4XXX + depends on CAN_FD && STM32_FDCAN3_FD_BRS + ---help--- + The duration of a synchronization jump is Tcan_clk x DSJW. + +endmenu # FDCAN3 device driver options + +endmenu # "FDCAN driver configuration" if STM32_LTDC diff --git a/arch/arm/src/stm32/Make.defs b/arch/arm/src/stm32/Make.defs index 14e91e1dea3..f97419b0430 100644 --- a/arch/arm/src/stm32/Make.defs +++ b/arch/arm/src/stm32/Make.defs @@ -249,6 +249,12 @@ CHIP_CSRCS += stm32_can_sock.c endif endif +ifeq ($(CONFIG_STM32_FDCAN),y) +ifeq ($(CONFIG_STM32_FDCAN_CHARDRIVER),y) +CHIP_CSRCS += stm32_fdcan.c +endif +endif + ifeq ($(CONFIG_STM32_IWDG),y) CHIP_CSRCS += stm32_iwdg.c endif diff --git a/arch/arm/src/stm32/hardware/stm32_fdcan.h b/arch/arm/src/stm32/hardware/stm32_fdcan.h new file mode 100644 index 00000000000..d135ab3137e --- /dev/null +++ b/arch/arm/src/stm32/hardware/stm32_fdcan.h @@ -0,0 +1,586 @@ +/**************************************************************************** + * arch/arm/src/stm32/hardware/stm32_fdcan.h + * + * 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_STM32_HARDWARE_STM32_FDCAN_H +#define __ARCH_ARM_SRC_STM32_HARDWARE_STM32_FDCAN_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include "chip.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Only for STM32G4 */ + +#ifndef CONFIG_STM32_STM32G4XXX +# error STM32 FDCAN was tested only for STM32G4 +#endif + +/* Register Offsets *********************************************************/ + +#define STM32_FDCAN_CREL_OFFSET 0x0000 /* FDCAN core release register */ +#define STM32_FDCAN_ENDN_OFFSET 0x0004 /* FDCAN endian register */ + /* 0x0008 Reserved */ +#define STM32_FDCAN_DBTP_OFFSET 0x000c /* FDCAN data bit timing and prescaler register */ +#define STM32_FDCAN_TEST_OFFSET 0x0010 /* FDCAN test register */ +#define STM32_FDCAN_RWD_OFFSET 0x0014 /* FDCAN RAM watchdog register */ +#define STM32_FDCAN_CCCR_OFFSET 0x0018 /* FDCAN CC control register */ +#define STM32_FDCAN_NBTP_OFFSET 0x001c /* FDCAN nominal bit timing and prescaler register */ +#define STM32_FDCAN_TSCC_OFFSET 0x0020 /* FDCAN timestamp counter configuration register */ +#define STM32_FDCAN_TSCV_OFFSET 0x0024 /* FDCAN timestamp counter value register */ +#define STM32_FDCAN_TOCC_OFFSET 0x0028 /* FDCAN timeout counter configuration register */ +#define STM32_FDCAN_TOCV_OFFSET 0x002c /* FDCAN timeout counter value register */ + /* 0x0030 to 0x003c Reserved */ +#define STM32_FDCAN_ECR_OFFSET 0x0040 /* FDCAN error counter register */ +#define STM32_FDCAN_PSR_OFFSET 0x0044 /* FDCAN protocol status register */ +#define STM32_FDCAN_TDCR_OFFSET 0x0048 /* FDCAN transmitter delay compensation register */ + /* 0x004c Reserved */ +#define STM32_FDCAN_IR_OFFSET 0x0050 /* FDCAN interrupt register */ +#define STM32_FDCAN_IE_OFFSET 0x0054 /* FDCAN interrupt enable register */ +#define STM32_FDCAN_ILS_OFFSET 0x0058 /* FDCAN interrupt line select register */ +#define STM32_FDCAN_ILE_OFFSET 0x005c /* FDCAN interrupt line enable register */ + /* 0x0060 to 0x007c Reserved */ +#define STM32_FDCAN_RXGFC_OFFSET 0x0080 /* FDCAN global filter configuration register */ +#define STM32_FDCAN_XIDAM_OFFSET 0x0084 /* FDCAN extended ID and mask register */ +#define STM32_FDCAN_HPMS_OFFSET 0x0088 /* FDCAN high-priority message status register */ +#define STM32_FDCAN_RXFS_OFFSET(f) (0x0090 + ((f) << 3) +#define STM32_FDCAN_RXFA_OFFSET(f) (0x0094 + ((f) << 3) +#define STM32_FDCAN_RXF0S_OFFSET 0x0090 /* FDCAN Rx FIFO 0 status register */ +#define STM32_FDCAN_RXF0A_OFFSET 0x0094 /* CAN Rx FIFO 0 acknowledge register */ +#define STM32_FDCAN_RXF1S_OFFSET 0x0098 /* FDCAN Rx FIFO 1 status register */ +#define STM32_FDCAN_RXF1A_OFFSET 0x009c /* FDCAN Rx FIFO 1 acknowledge register */ + /* 0x00a0 to 0x00bc Reserved */ +#define STM32_FDCAN_TXBC_OFFSET 0x00c0 /* FDCAN Tx buffer configuration register */ +#define STM32_FDCAN_TXFQS_OFFSET 0x00c4 /* FDCAN Tx FIFO/queue status register */ +#define STM32_FDCAN_TXBRP_OFFSET 0x00c8 /* FDCAN Tx buffer request pending register */ +#define STM32_FDCAN_TXBAR_OFFSET 0x00cc /* FDCAN Tx buffer add request register */ +#define STM32_FDCAN_TXBCR_OFFSET 0x00d0 /* FDCAN Tx buffer cancellation request register */ +#define STM32_FDCAN_TXBTO_OFFSET 0x00d4 /* FDCAN Tx buffer transmission occurred register */ +#define STM32_FDCAN_TXBCNF_OFFSET 0x00d8 /* FDCAN Tx buffer cancellation finished register */ +#define STM32_FDCAN_TXBTIE_OFFSET 0x00dc /* FDCAN Tx buffer transmission interrupt enable register */ +#define STM32_FDCAN_TXBCIE_OFFSET 0x00e0 /* FDCAN Tx buffer cancellation finished interrupt enable register */ +#define STM32_FDCAN_TXEFS_OFFSET 0x00e4 /* FDCAN Tx event FIFO status register */ +#define STM32_FDCAN_TXEFA_OFFSET 0x00e8 /* FDCAN Tx event FIFO acknowledge register */ +#define STM32_FDCAN_CKDIV_OFFSET 0x0100 /* FDCAN CFG clock divider register */ + +/* Register Bitfield Definitions ********************************************/ + +/* FDCAN core release register */ + +#define FDCAN_CREL_DAY_SHIFT (0) /* Bits 0-7: DAY */ +#define FDCAN_CREL_DAY_MASK (0xff << FDCAN_CREL_DAY_SHIFT) +#define FDCAN_CREL_MON_SHIFT (8) /* Bits 8-15: MON */ +#define FDCAN_CREL_MON_MASK (0xff << FDCAN_CREL_MON_SHIFT) +#define FDCAN_CREL_YEAR_SHIFT (16) /* Bits 8-15: YEAR */ +#define FDCAN_CREL_YEAR_MASK (0x0f << FDCAN_CREL_YEAR_SHIFT) +#define FDCAN_CREL_SUBSTEP_SHIFT (20) /* Bits 20-23: SUBSTEP */ +#define FDCAN_CREL_SUBSTEP_MASK (0x0f << FDCAN_CREL_SUBSTEP_SHIFT) +#define FDCAN_CREL_STEP_SHIFT (24) /* Bits 24-27: STEP */ +#define FDCAN_CREL_STEP_MASK (0x0f << FDCAN_CREL_STEP_SHIFT) +#define FDCAN_CREL_REL_SHIFT (28) /* Bits 28-31: REL */ +#define FDCAN_CREL_REL_MASK (0x0f << FDCAN_CREL_REL_SHIFT) + +/* FDCAN data bit timing and prescaler register */ + +#define FDCAN_DBTP_DSJW_SHIFT (0) /* Bits 0-3: Synchronization jump width */ +#define FDCAN_DBTP_DSJW_MASK (0x0f << FDCAN_DBTP_DSJW_SHIFT) +# define FDCAN_DBTP_DSJW(value) ((value) << FDCAN_DBTP_DSJW_SHIFT) +# define FDCAN_DBTP_DSJW_MAX (15) +#define FDCAN_DBTP_DTSEG2_SHIFT (4) /* Bits 4-7: Data time segment after sample point*/ +#define FDCAN_DBTP_DTSEG2_MASK (0x0f << FDCAN_DBTP_DTSEG2_SHIFT) +# define FDCAN_DBTP_DTSEG2(value) ((value) << FDCAN_DBTP_DTSEG2_SHIFT) +# define FDCAN_DBTP_DTSEG2_MAX (15) +#define FDCAN_DBTP_DTSEG1_SHIFT (8) /* Bits 8-12: Data time segment before sample point*/ +#define FDCAN_DBTP_DTSEG1_MASK (0x1f << FDCAN_DBTP_DTSEG1_SHIFT) +# define FDCAN_DBTP_DTSEG1(value) ((value) << FDCAN_DBTP_DTSEG1_SHIFT) +# define FDCAN_DBTP_DTSEG1_MAX (31) +#define FDCAN_DBTP_DBRP_SHIFT (16) /* Bits 16-20: Data bitrate prescaler */ +#define FDCAN_DBTP_DBRP_MASK (0x1f << FDCAN_DBTP_DBRP_SHIFT) +# define FDCAN_DBTP_DBRP(value) ((value) << FDCAN_DBTP_DBRP_SHIFT) +# define FDCAN_DBTP_DBRP_MAX (31) +#define FDCAN_DBTP_TDC_EN (1 << 23) /* Bit 23: Transceiver delay compensation enable */ + +/* FDCAN test register */ + +#define FDCAN_TEST_LBCK (1 << 4) /* Bit 4: Loop back mode */ +#define FDCAN_TEST_TX_SHIFT (5) /* Bits 5-6: Control of transmit pin */ +#define FDCAN_TEST_TX_MASK (0x3 << FDCAN_TEST_TX_SHIFT) +# define FDCAN_TEST_TX_RESET (0 << FDCAN_TEST_TX_SHIFT) /* 00: TX is controlled by CAN core */ +# define FDCAN_TEST_TX_SP (1 << FDCAN_TEST_TX_SHIFT) /* 01: Sample point can be monitored at TX pin */ +# define FDCAN_TEST_TX_DLVL (2 << FDCAN_TEST_TX_SHIFT) /* 10: Dominant (0) level at TX pin */ +# define FDCAN_TEST_TX_RLVL (3 << FDCAN_TEST_TX_SHIFT) /* 11: Recesive (1) level at TX pin */ +#define FDCAN_TEST_RX (1 << 7) /* Bit 7: Receive pin */ + +/* FDCAN RAM watchdog register */ + +#define FDCAN_RWD_WDC_SHIFT (0) /* Bits 0-7: RAM watchdog counter start value */ +#define FDCAN_RWD_WDC_MASK (0xff << FDCAN_RWD_WDC_SHIFT) +# define FDCAN_RWD_WDC_DIS (0 << FDCAN_RWD_WDC_SHIFT) /* Counter disabled */ +# define FDCAN_RWD_WDC(value) ((value) << FDCAN_RWD_WDC_SHIFT) +#define FDCAN_RWD_WDV_SHIFT (8) /* Bits 8-15: RAM watchdog counter value */ +#define FDCAN_RWD_WDV_MASK (0xff << FDCAN_RWD_WDV_SHIFT) + +/* FDCAN CC control register */ + +#define FDCAN_CCCR_INIT (1 << 0) /* Bit 0: Initialization */ +#define FDCAN_CCCR_CCE (1 << 1) /* Bit 1: Configuration change enable */ +#define FDCAN_CCCR_ASM (1 << 2) /* Bit 2: ASM restricted operation mode */ +#define FDCAN_CCCR_CSA (1 << 3) /* Bit 3: Clock stop acknowledge */ +#define FDCAN_CCCR_CSR (1 << 4) /* Bit 4: Clock stop request */ +#define FDCAN_CCCR_MON (1 << 5) /* Bit 5: Bus monitoring mode */ +#define FDCAN_CCCR_DAR (1 << 6) /* Bit 6: Disable automatic retransmission */ +#define FDCAN_CCCR_TEST (1 << 7) /* Bit 7: Test mode enable */ +#define FDCAN_CCCR_FDOE (1 << 8) /* Bit 8: FD operation enable */ +#define FDCAN_CCCR_BRSE (1 << 9) /* Bit 9: FDCAN Bitrate switching */ + /* Bits 10-11: Reserved */ +#define FDCAN_CCCR_PXHD (1 << 12) /* Bit 12: Protocol exception handling disable */ +#define FDCAN_CCCR_EFBI (1 << 13) /* Bit 13: Edge filtering during bus integration */ +#define FDCAN_CCCR_TXP (1 << 14) /* Bit 14: Tx pause */ +#define FDCAN_CCCR_NISO (1 << 15) /* Bit 15: Non ISO operation */ + +/* FDCAN nominal bit timing and prescaler register */ + +#define FDCAN_NBTP_NTSEG2_SHIFT (0) /* Bits 0-6: Nominal time segment after sample point */ +#define FDCAN_NBTP_NTSEG2_MASK (0x7f << FDCAN_NBTP_NTSEG2_SHIFT) +# define FDCAN_NBTP_NTSEG2(value) ((value) << FDCAN_NBTP_NTSEG2_SHIFT) +# define FDCAN_NBTP_NTSEG2_MAX (127) +#define FDCAN_NBTP_NTSEG1_SHIFT (8) /* Bits 8-15: Nominal time segment before sample point */ +#define FDCAN_NBTP_NTSEG1_MASK (0xff << FDCAN_NBTP_NTSEG1_SHIFT) +# define FDCAN_NBTP_NTSEG1(value) ((value) << FDCAN_NBTP_NTSEG1_SHIFT) +# define FDCAN_NBTP_NTSEG1_MAX (255) +#define FDCAN_NBTP_NBRP_SHIFT (16) /* Bits 16-24: Bitrate prescaler */ +#define FDCAN_NBTP_NBRP_MASK (0x1ff << FDCAN_NBTP_NBRP_SHIFT) +# define FDCAN_NBTP_NBRP(value) ((value) << FDCAN_NBTP_NBRP_SHIFT) +# define FDCAN_NBTP_NBRP_MAX (511) +#define FDCAN_NBTP_NSJW_SHIFT (25) /* Bits 25-31: Nominal (re)synchronization jump width */ +#define FDCAN_NBTP_NSJW_MASK (0x7f << FDCAN_NBTP_NSJW_SHIFT) +# define FDCAN_NBTP_NSJW(value) ((value) << FDCAN_NBTP_NSJW_SHIFT) +# define FDCAN_NBTP_NSJW_MAX (127) + +/* FDCAN timestamp counter configuration register */ + +#define FDCAN_TSCC_TSS_SHIFT (0) /* Bits 0-1: Timestamp counter select */ +#define FDCAN_TSCC_TSS_MASK (0x3 << FDCAN_TSCC_TSS_SHIFT) +# define FDCAN_TSCC_TSS_ZERO (0 << FDCAN_TSCC_TSS_SHIFT) /* 00: Always 0 */ +# define FDCAN_TSCC_TSS_TCP (1 << FDCAN_TSCC_TSS_SHIFT) /* 01: Incremented based on TCP */ +# define FDCAN_TSCC_TSS_TIM3 (2 << FDCAN_TSCC_TSS_SHIFT) /* 10: Value from TIM3 used */ +#define FDCAN_TSCC_TCP_SHIFT (16) /* Bits 16-19: Timestamp counter prescaler */ +#define FDCAN_TSCC_TCP_MASK (0x0f << FDCAN_TSCC_TCP_SHIFT) +# define FDCAN_TSCC_TCP(value) ((value) << FDCAN_TSCC_TCP_SHIFT) + +/* FDCAN timestamp counter value register */ + +#define FDCAN_TSCV_TSC_SHIFT (0) /* Bits 0-15: Timestamp counter */ +#define FDCAN_TSCV_TSC_MASK (0xffff << FDCAN_TSCV_TSC_SHIFT) + +/* FDCAN timeout counter configuration register */ + +#define FDCAN_TOCC_ETOC (1 << 0) /* Bit 0: Enable timeout counter */ +#define FDCAN_TOCC_TOS_SHIFT (1) /* Bits 1-2: Timeout select */ +#define FDCAN_TOCC_TOS_MASK (0x03 << FDCAN_TOCC_TOS_SHIFT) +# define FDCAN_TOCC_TOS_CONT (0 << FDCAN_TOCC_TOS_SHIFT) /* 00: Continuous operation */ +# define FDCAN_TOCC_TOS_TXFIFO (1 << FDCAN_TOCC_TOS_SHIFT) /* 01: Tx event FIFO */ +# define FDCAN_TOCC_TOS_RX_FIFO0 (2 << FDCAN_TOCC_TOS_SHIFT) /* 10: Rx FIFO 0 */ +# define FDCAN_TOCC_TOS_RX_FIFO1 (3 << FDCAN_TOCC_TOS_SHIFT) /* 11: Rx FIFO 1 */ +#define FDCAN_TOCC_TOP_SHIFT (16) /* Bits 16-31: Timeout period counter start value */ +#define FDCAN_TOCC_TOP_MASK (0xffff << FDCAN_TOCC_TOP_SHIFT) +# define FDCAN_TOCC_TOP(value) ((value) << FDCAN_TOCC_TOP_SHIFT) + +/* FDCAN timeout counter value register */ + +#define FDCAN_TOCV_TOC_SHIFT (0) /* Bits 0-15: Timestamp counter */ +#define FDCAN_TOCV_TOC_MASK (0xffff << FDCAN_TOCV_TOC_SHIFT) + +/* FDCAN error counter register */ + +#define FDCAN_ECR_TEC_SHIFT (0) /* Bits 0-7: Transmit error counter */ +#define FDCAN_CR_TEC_MASK (0xff << FDCAN_ECR_TEC_SHIFT) +#define FDCAN_ECR_REC_SHIFT (8) /* Bits 8-14: Receive error counter */ +#define FDCAN_ECR_REC_MASK (0x7f << FDCAN_ECR_REC_SHIFT) +#define FDCAN_ECR_RP (1 << 15) /* Bit 15: Receive error passive */ +#define FDCAN_ECR_CEL_SHIFT (16) /* Bits 16-23: CAN error logging */ +#define FDCAN_ECR_CEL_MASK (0xff << FDCAN_ECR_CEL_SHIFT) + +/* FDCAN protocol status register */ + +/* Error codes */ + +#define FDCAN_PSR_EC_NO_ERROR (0) /* No error occurred since LEC has been reset */ +#define FDCAN_PSR_EC_STUFF_ERROR (1) /* More than 5 equal bits in a sequence */ +#define FDCAN_PSR_EC_FORM_ERROR (2) /* Part of a received frame has wrong format */ +#define FDCAN_PSR_EC_ACK_ERROR (3) /* Message not acknowledged by another node */ +#define FDCAN_PSR_EC_BIT1_ERROR (4) /* Send with recessive level, but bus value was dominant */ +#define FDCAN_PSR_EC_BIT0_ERROR (5) /* Send with dominant level, but bus value was recessive */ +#define FDCAN_PSR_EC_CRC_ERROR (6) /* CRC received message incorrect */ +#define FDCAN_PSR_EC_NO_CHANGE (7) /* No CAN bus event was detected since last read */ + +#define FDCAN_PSR_LEC_SHIFT (0) /* Bits 0-2: Last error code */ +#define FDCAN_PSR_LEC_MASK (0x7 << FDCAN_PSR_LEC_SHIFT) +# define FDCAN_PSR_LEC(n) ((uint32_t)(n) << FDCAN_PSR_LEC_SHIFT) /* See error codes above */ +#define FDCAN_PSR_ACT_SHIFT (3) /* Bits 3-4: Activity */ +#define FDCAN_PSR_ACT_MASK (3 << FDCAN_PSR_ACT_SHIFT) +# define FDCAN_PSR_ACT_SYNC (0 << FDCAN_PSR_ACT_SHIFT) /* 00: Synchronizing */ +# define FDCAN_PSR_ACT_IDLE (1 << FDCAN_PSR_ACT_SHIFT) /* 01: Idle */ +# define FDCAN_PSR_ACT_RECV (2 << FDCAN_PSR_ACT_SHIFT) /* 10: Receiver */ +# define FDCAN_PSR_ACT_TRANS (3 << FDCAN_PSR_ACT_SHIFT) /* 11: Transmitter */ +#define FDCAN_PSR_EP (1 << 5) /* Bit 5: Error passive */ +#define FDCAN_PSR_EW (1 << 6) /* Bit 6: Warning status */ +#define FDCAN_PSR_BO (1 << 7) /* Bit 7: Bus_off status */ +#define FDCAN_PSR_DLEC_SHIFT (8) /* Bits 8-10: Data last error code */ +#define FDCAN_PSR_DLEC_MASK (0x7 << FDCAN_PSR_DLEC_SHIFT) +# define FDCAN_PSR_DLEC(n) ((uint32_t)(n) << FDCAN_PSR_DLEC_SHIFT) /* See error codes above */ +#define FDCAN_PSR_RESI (1 << 11) /* Bit 11: ESI flag of last message */ +#define FDCAN_PSR_RBRS (1 << 12) /* Bit 12: BRS flag of last message */ +#define FDCAN_PSR_REDL (1 << 13) /* Bit 13: Received message */ +#define FDCAN_PSR_PXE (1 << 14) /* Bit 14: Protocol exception event */ +#define FDCAN_PSR_TDCV_SHIFT (16) /* Bits 16-22: Transmitter delay compensation */ +#define FDCAN_PSR_TDCV_MASK (0x7f << FDCAN_PSR_TDCV_SHIFT) + +/* FDCAN transmitter delay compensation register */ + +#define FDCAN_TDCR_TDCF_SHIFT (0) /* Bits 0-6: Transmitter delay compensation filter window length */ +#define FDCAN_TDCR_TDCF_MASK (0x7f << FDCAN_TDCR_TDCF_SHIFT) +# define FDCAN_TDCR_TDCF(value) ((value) << FDCAN_TDCR_TDCF_SHIFT) +#define FDCAN_TDCR_TDCO_SHIFT (8) /* Bits 8-14: Transmiiter delay compensation offset */ +#define FDCAN_TDCR_TDCO_MASK (0x7f << FDCAN_TDCR_TDCO_SHIFT) +# define FDCAN_TDCR_TDCO(value) ((value) << FDCAN_TDCR_TDCO_SHIFT) + +/* FDCAN interrupt register and interrupt enable register */ + +#define FDCAN_INT_RF0N (1 << 0) /* Bit 0: Rx FIFO 0 new message */ +#define FDCAN_INT_RF0F (1 << 1) /* Bit 1: Rx FIFO 0 full */ +#define FDCAN_INT_RF0L (1 << 2) /* Bit 2: Rx FIFO 0 message lost */ +#define FDCAN_INT_RF1N (1 << 3) /* Bit 3: Rx FIFO 1 new message */ +#define FDCAN_INT_RF1F (1 << 4) /* Bit 4: Rx FIFO 1 full */ +#define FDCAN_INT_RF1L (1 << 5) /* Bit 5: Rx FIFO 1 message lost */ +#define FDCAN_INT_HPM (1 << 6) /* Bit 6: High priority message */ +#define FDCAN_INT_TC (1 << 7) /* Bit 7: Transmission completed */ +#define FDCAN_INT_TCF (1 << 8) /* Bit 8: Transmission cancellation finished */ +#define FDCAN_INT_TFE (1 << 9) /* Bit 9: Tx FIFO empty */ +#define FDCAN_INT_TEFN (1 << 10) /* Bit 10: Tx event FIFO new entry */ +#define FDCAN_INT_TEFF (1 << 11) /* Bit 11: Tx event FIFO full */ +#define FDCAN_INT_TEFL (1 << 12) /* Bit 12: Tx event FIFO element lost */ +#define FDCAN_INT_TSW (1 << 13) /* Bit 13: Timestamp wraparound */ +#define FDCAN_INT_MRAF (1 << 14) /* Bit 14: Message RAM access failure */ +#define FDCAN_INT_TOO (1 << 15) /* Bit 15: Timeout occurred */ +#define FDCAN_INT_ELO (1 << 16) /* Bit 16: Error logging overflow */ +#define FDCAN_INT_EP (1 << 17) /* Bit 17: Error_passive status */ +#define FDCAN_INT_EW (1 << 18) /* Bit 18: Error_warning status */ +#define FDCAN_INT_BO (1 << 19) /* Bit 19: Buss_off status */ +#define FDCAN_INT_WDI (1 << 20) /* Bit 20: Watchdog interrupt */ +#define FDCAN_INT_PEA (1 << 21) /* Bit 21: Protocol error arbitration phase */ +#define FDCAN_INT_PED (1 << 22) /* Bit 22: Protocol error data phase */ +#define FDCAN_INT_ARA (1 << 23) /* Bit 23: Access to reserved address */ + +/* FDCAN interrupt line select register */ + +#define FDCAN_ILS_RXFIFO0 (1 << 0) /* Bit 0: RXFIFO 0 */ +#define FDCAN_ILS_RXFIFO1 (1 << 1) /* Bit 1: RXFIFO 1 */ +#define FDCAN_ILS_SMG (1 << 2) /* Bit 2: SMSG */ +#define FDCAN_ILS_TFERR (1 << 3) /* Bit 3: TFERR */ +#define FDCAN_ILS_MISC (1 << 4) /* Bit 4: MISC */ +#define FDCAN_ILS_BERR (1 << 5) /* Bit 5: BERR */ +#define FDCAN_ILS_PERR (1 << 6) /* Bit 6: PERR */ + +/* FDCAN interrupt line enable register */ + +#define FDCAN_ILE_EINT0 (1 << 0) /* Bit 0: Enable interrupt line 0 */ +#define FDCAN_ILE_EINT1 (1 << 1) /* Bit 1: Enable interrupt line 1 */ + +/* FDCAN global filter configuration register */ + +#define FDCAN_RXGFC_RRFE (1 << 0) /* Bit 0: Reject remote frames ext */ +#define FDCAN_RXGFC_RRFS (1 << 1) /* Bit 1: Reject remote frames std */ +#define FDCAN_RXGFC_ANFE_SHIFT (2) /* Bits 2-3: Accept non-matching frames ext */ +#define FDCAN_RXGFC_ANFE_MASK (0x3 << FDCAN_RXGFC_ANFE_SHIFT) +# define FDCAN_RXGFC_ANFE_RX_FIFO0 (0 << FDCAN_RXGFC_ANFE_SHIFT) /* 00: Accept in Rx FIFO 0 */ +# define FDCAN_RXGFC_ANFE_RX_FIFO1 (1 << FDCAN_RXGFC_ANFE_SHIFT) /* 01: Accept in Rx FIFO 1 */ +# define FDCAN_RXGFC_ANFE_REJECTED (2 << FDCAN_RXGFC_ANFE_SHIFT) /* 10: Reject */ +#define FDCAN_RXGFC_ANFS_SHIFT (4) /* Bits 5-4: Accept non-matching frames std */ +#define FDCAN_RXGFC_ANFS_MASK (0x3 << FDCAN_RXGFC_ANFS_SHIFT) +# define FDCAN_RXGFC_ANFS_RX_FIFO0 (0 << FDCAN_RXGFC_ANFS_SHIFT) /* 00: Accept in Rx FIFO 0 */ +# define FDCAN_RXGFC_ANFS_RX_FIFO1 (1 << FDCAN_RXGFC_ANFS_SHIFT) /* 01: Accept in Rx FIFO 1 */ +# define FDCAN_RXGFC_ANFS_REJECTED (2 << FDCAN_RXGFC_ANFS_SHIFT) /* 10: Reject */ +#define FDCAN_RXGFC_F1OM (1 << 8) /* Bit 8: FIFO 1 operation mode */ +#define FDCAN_RXGFC_F0OM (1 << 9) /* Bit 9: FIFO 0 operation mode */ +#define FDCAN_RXGFC_LSS_SHIFT (16) /* Bits 16-20: List size std */ +#define FDCAN_RXGFC_LSS_MASK (0x1f << FDCAN_RXGFC_LSS_SHIFT) +# define FDCAN_RXGFC_LSS(value) ((value) << FDCAN_RXGFC_LSS_SHIFT) +# define FDCAN_RXGFC_LSS_MAX (28) +#define FDCAN_RXGFC_LSE_SHIFT (24) /* Bits 24-27: List size ext */ +#define FDCAN_RXGFC_LSE_MASK (0x1f << FDCAN_RXGFC_LSE_SHIFT) +# define FDCAN_RXGFC_LSE(value) ((value) << FDCAN_RXGFC_LSE_SHIFT) +# define FDCAN_RXGFC_LSE_MAX (8) + +/* FDCAN extended ID and mask register */ + +#define FDCAN_XIDAM_EIDM_SHIFT (0) /* Bits 0-28: Extended ID mask */ +#define FDCAN_XIDAM_EIDM_MASK (0x1fffffff << FDCAN_XIDAM_EIDM_SHIFT) + +/* FDCAN high-priority message status register */ + +#define FDCAN_HPMS_BIDX_SHIFT (0) /* Bits 0-2: Buffer index */ +#define FDCAN_HPMS_BIDX_MASK (0x7 << FDCAN_HPMS_BIDX_SHIFT) +# define FDCAN_HPMS_BIDX(value) ((value) << FDCAN_HPMS_BIDX_SHIFT) +#define FDCAN_HPMS_MSI_SHIFT (6) /* Bits 6-7: Message storage indicator */ +#define FDCAN_HPMS_MSI_MASK (0x3 << FDCAN_HPMS_MSI_SHIFT) +# define FDCAN_HPMS_MSI(value) ((value) << FDCAN_HPMS_MSI_SHIFT) +#define FDCAN_HPMS_FIDX_SHIFT (8) /* Bits 8-12: Filter index */ +#define FDCAN_HPMS_FIDX_MASK (0x1f << FDCAN_HPMS_FIDX_SHIFT) +# define FDCAN_HPMS_FIDX(value) ((value) << FDCAN_HPMS_FIDX_SHIFT) +#define FDCAN_HPMS_FLST (1 << 15) /* Bit 15: Filter list */ + +/* FDCAN Rx FIFO x status register */ + +#define FDCAN_RXFS_FFL_SHIFT (0) /* Bits 0-3: FIFO fill level */ +#define FDCAN_RXFS_FFL_MASK (0xf << FDCAN_RXFS_FFL_SHIFT) +# define FDCAN_RXFS_FFL(value) ((value) << FDCAN_RXFS_FFL_SHIFT) +#define FDCAN_RXFS_FGI_SHIFT (8) /* Bits 8-9: FIFO get index */ +#define FDCAN_RXFS_FGI_MASK (0x3 << FDCAN_RXFS_FGI_SHIFT) +# define FDCAN_RXFS_FGI(value) ((value) << FDCAN_RXFS_FGI_SHIFT) +#define FDCAN_RXFS_FPI_SHIFT (16) /* Bits 16-17: FIFO put index */ +#define FDCAN_RXFS_FPI_MASK (0x3 << FDCAN_RXFS_FPI_SHIFT) +# define FDCAN_RXFS_FPI(value) ((value) << FDCAN_RXFS_FPI_SHIFT) +#define FDCAN_RXFS_FF (1 << 24) /* Bit 24: FIFO full */ +#define FDCAN_RXFS_RFL (1 << 25) /* Bit 25: FIFO message lost */ + +/* FDCAN Rx FIFO x acknowledge register */ + +#define FDCAN_RXFA_FAI_SHIFT (0) /* Bits 0-2: FIFO 0 acknowledge index */ +#define FDCAN_RXFA_FAI_MASK (0x7 << FDCAN_RXFA_FAI_SHIFT) + +/* FDCAN Tx buffer configuration register */ + +#define FDCAN_TXBC_TFQM (1 << 24) /* Bit 24: FIFO/queue mode */ + +/* FDCAN Tx FIFO/queue status register */ + +#define FDCAN_TXFQS_TFFL_SHIFT (0) /* Bits 0-2: FIFO free level */ +#define FDCAN_TXFQS_TFFL_MASK (0x7 << FDCAN_TXFQS_TFFL_SHIFT) +#define FDCAN_TXFQS_TFGI_SHIFT (8) /* Bits 8-9: FIFO get index */ +#define FDCAN_TXFQS_TFGI_MASK (0x3 << FDCAN_TXFQS_TFGI_SHIFT) +#define FDCAN_TXFQS_TFQPI_SHIFT (16) /* Bits 20-16: FIFO/queue put index */ +#define FDCAN_TXFQS_TFQPI_MASK (0x3 << FDCAN_TXFQS_TFQPI_SHIFT) +#define FDCAN_TXFQS_TFQF (1 << 21) /* Bit 21: FIFO/queue full */ + +/* FDCAN Tx buffer request pending register */ + +#define FDCAN_TXBRP_TRP_SHIFT (0) /* Bits 0-2: Transmission request pending */ +#define FDCAN_TXBRP_TRP_MASK (0x7 << FDCAN_TXBRP_TRP_SHIFT) +# define FDCAN_TXBRP_TRP(value) ((value) << FDCAN_TXBRP_TRP_SHIFT) + +/* FDCAN Tx buffer add request register */ + +#define FDCAN_TXBAR_AR_SHIFT (0) /* Bits 0-2: Add request */ +#define FDCAN_TXBAR_AR_MASK (0x7 << FDCAN_TXBAR_AR_SHIFT) +# define FDCAN_TXBAR_AR(value) ((value) << FDCAN_TXBAR_AR_SHIFT) + +/* FDCAN Tx buffer cancellation request register */ + +#define FDCAN_TXBCR_CR_SHIFT (0) /* Bits 0-2: Cancellation request */ +#define FDCAN_TXBCR_CR_MASK (0x7 << FDCAN_TXBCR_CR_SHIFT) +# define FDCAN_TXBCR_CR(value) ((value) << FDCAN_TXBCR_CR_SHIFT) + +/* FDCAN Tx buffer transmission occurred register */ + +#define FDCAN_TXBTO_TO_SHIFT (0) /* Bits 0-2: Transmission occurred */ +#define FDCAN_TXBTO_TO_MASK (0x7 << FDCAN_TXBTO_TO_SHIFT) + +/* FDCAN Tx buffer cancellation finished register */ + +#define FDCAN_TXBCF_CF_SHIFT (0) /* Bits 0-2: Cancellation finished */ +#define FDCAN_TXBCF_CF_MASK (0x7 << FDCAN_TXBCF_CF_SHIFT) + +/* FDCAN Tx buffer transmission interrupt enable register */ + +#define FDCAN_TXBTIE_TIE_SHIFT (0) /* Bits 0-2: Transmission interrupt enable */ +#define FDCAN_TXBTIE_TIE_MASK (0x7 << FDCAN_TXBTIE_TIE_SHIFT) +# define FDCAN_TXBTIE_TIE(value) ((value) << FDCAN_TXBTIE_TIE_SHIFT) + +/* FDCAN Tx buffer cancellation finished interrupt enable register */ + +#define FDCAN_TXBCIE_CFIE_SHIFT (0) /* Bits 0-2: Cancellation finished interrupt enable */ +#define FDCAN_TXBCIE_CFIE_MASK (0x7 << FDCAN_TXBCIE_CFIE_SHIFT) +# define FDCAN_TXBCIE_CFIE(value) ((value) << FDCAN_TXBCIE_CFIE_SHIFT) + +/* FDCAN Tx event FIFO status register */ + +#define FDCAN_TXEFS_EFFL_SHIFT (2) /* Bits 0-2: Event FIFO fill level */ +#define FDCAN_TXEFS_EFFL_MASK (0x7 << FDCAN_TXEFC_EFFL_SHIFT) +# define FDCAN_TXEFC_EFFL(value) ((value) << FDCAN_TXEFC_EFFL_SHIFT) +#define FDCAN_TXEFS_EFGI_SHIFT (8) /* Bits 8-9: Event FIFO get index */ +#define FDCAN_TXEFS_EFGI_MASK (0x3 << FDCAN_TXEFS_EFGI_SHIFT) +# define FDCAN_TXEFS_EFGI(value) ((value) << FDCAN_TXEFS_EFGI_SHIFT) +#define FDCAN_TXEFS_EFPI_SHIFT (16) /* Bits 16-17: Event FIFO put index */ +#define FDCAN_TXEFS_EFPI_MASK (0x3 << FDCAN_TXEFS_EFPI_SHIFT) +# define FDCAN_TXEFS_EFPI(value) ((value) << FDCAN_TXEFS_EFPI_SHIFT) +#define FDCAN_TXEFS_EFF (1 << 24) /* Bit 24: Event FIFO full */ +#define FDCAN_TXEFS_TEFL (1 << 25) /* Bit 25: Tx Event FIFO element lost */ + /* Bits 26-31: Reserved */ + +/* FDCAN Tx event FIFO acknowledge register */ + +#define FDCAN_TXEFA_EFAI_SHIFT (0) /* Bits 0-3: Event FIFO acknowledge index */ +#define FDCAN_TXEFA_EFAI_MASK (0x3 << FDCAN_TXEFA_EFAI_SHIFT) + +/* FDCAN CFG clock divider register */ + +#define FDCAN_CKDIV_PDIV_SHIFT (0) /* Bits 0-3: Input clock divider */ +#define FDCAN_CKDIV_PDIV_MASK (0xf << FDCAN_CKDIV_PDIV_SHIFT) + +/* 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_STM32_HARDWARE_STM32_FDCAN_H */ diff --git a/arch/arm/src/stm32/hardware/stm32g4xxxx_memorymap.h b/arch/arm/src/stm32/hardware/stm32g4xxxx_memorymap.h index 2ced9d421c1..036f550ba4b 100644 --- a/arch/arm/src/stm32/hardware/stm32g4xxxx_memorymap.h +++ b/arch/arm/src/stm32/hardware/stm32g4xxxx_memorymap.h @@ -112,7 +112,7 @@ #define STM32_LPUART1_BASE 0x40008000 /* 0x40008000-0x400083ff: LPUART1 */ #define STM32_I2C4_BASE 0x40008400 /* 0x40008400-0x400087ff: I2C4 */ #define STM32_UCPD1_BASE 0x4000a000 /* 0x4000a000-0x4000a3ff: UCPD1 */ -#define STM32_SRAMCAN_BASE 0x4000a400 /* 0x4000a400-0x4000afff: FDCANs Message RAM */ +#define STM32_CANRAM_BASE 0x4000a400 /* 0x4000a400-0x4000afff: FDCANs Message RAM */ /* APB2 Base Addresses ******************************************************/ diff --git a/arch/arm/src/stm32/stm32_fdcan.c b/arch/arm/src/stm32/stm32_fdcan.c new file mode 100644 index 00000000000..f1174892c95 --- /dev/null +++ b/arch/arm/src/stm32/stm32_fdcan.c @@ -0,0 +1,3453 @@ +/**************************************************************************** + * arch/arm/src/stm32/stm32_fdcan.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 + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#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 + ****************************************************************************/ + +/* 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) +#define FDCAN_COMMON_INTS FDCAN_CMNERR_INTS + +/* 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_RXCOMMON_INTS 0 +#define FDCAN_RXFIFO0_INTS (FDCAN_INT_RF0N | FDCAN_INT_RF0L) +#define FDCAN_RXFIFO1_INTS (FDCAN_INT_RF1N | FDCAN_INT_RF1L) +#define FDCAN_RXFIFO_INTS (FDCAN_RXFIFO0_INTS | FDCAN_RXFIFO1_INTS | \ + FDCAN_INT_HPM | FDCAN_RXCOMMON_INTS) + +#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_TXDEDBUF_INTS FDCAN_TXCOMMON_INTS + +#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_CAN_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 */ + 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 +{ + uint32_t *stdfilters; /* Standard filters */ + uint32_t *extfilters; /* Extended filters */ + uint32_t *rxfifo0; /* RX FIFO0 */ + uint32_t *rxfifo1; /* RX FIFO1 */ + uint32_t *txeventfifo; /* TX event FIFO */ + 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 */ + uint32_t rxints; /* Configured RX interrupts */ + uint32_t txints; /* Configured TX interrupts */ + +#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 +}; + +/**************************************************************************** + * 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 + +/* FDCAN helpers */ + +static uint8_t fdcan_dlc2bytes(FAR struct stm32_fdcan_s *priv, uint8_t dlc); + +#ifdef CONFIG_CAN_EXTID +static int fdcan_add_extfilter(FAR struct stm32_fdcan_s *priv, + FAR struct canioc_extfilter_s *extconfig); +static int fdcan_del_extfilter(FAR struct stm32_fdcan_s *priv, int ndx); +#endif +static int fdcan_add_stdfilter(FAR struct stm32_fdcan_s *priv, + FAR struct canioc_stdfilter_s *stdconfig); +static int fdcan_del_stdfilter(FAR struct stm32_fdcan_s *priv, int ndx); + +static int +fdcan_start_busoff_recovery_sequence(FAR struct stm32_fdcan_s *priv); + +/* CAN driver methods */ + +static void fdcan_reset(FAR struct can_dev_s *dev); +static int fdcan_setup(FAR struct can_dev_s *dev); +static void fdcan_shutdown(FAR struct can_dev_s *dev); +static void fdcan_rxint(FAR struct can_dev_s *dev, bool enable); +static void fdcan_txint(FAR struct can_dev_s *dev, bool enable); +static int fdcan_ioctl(FAR struct can_dev_s *dev, int cmd, + unsigned long arg); +static int fdcan_remoterequest(FAR struct can_dev_s *dev, uint16_t id); +static int fdcan_send(FAR struct can_dev_s *dev, FAR struct can_msg_s *msg); +static bool fdcan_txready(FAR struct can_dev_s *dev); +static bool fdcan_txempty(FAR struct can_dev_s *dev); + +/* FDCAN interrupt handling */ + +#ifdef CONFIG_CAN_ERRORS +static void fdcan_error(FAR struct can_dev_s *dev, uint32_t status); +#endif +static void fdcan_receive(FAR struct can_dev_s *dev, + FAR uint32_t *rxbuffer, + unsigned long nwords); +static int fdcan_interrupt(int irq, void *context, FAR void *arg); + +/* Hardware initialization */ + +static int fdcan_hw_initialize(FAR struct stm32_fdcan_s *priv); + +/**************************************************************************** + * 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, +}; + +#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; +static struct can_dev_s g_fdcan1dev; + +#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; +static struct can_dev_s g_fdcan2dev; + +#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; +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: + * + ****************************************************************************/ + +#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) + { + caninfo("...\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 */ + + caninfo("[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 */ + + caninfo("%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 */ + + caninfo("%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; + + caninfo("CAN%d Control and Status Registers: %s\n", config->port, msg); + caninfo(" Base: %08" PRIx32 "\n", config->base); + + /* CAN control and status registers */ + + caninfo(" CCCR: %08" PRIx32 " TEST: %08" PRIx32 "\n", + getreg32(config->base + STM32_FDCAN_CCCR_OFFSET), + getreg32(config->base + STM32_FDCAN_TEST_OFFSET)); + + caninfo(" NBTP: %08" PRIx32 " DBTP: %08" PRIx32 "\n", + getreg32(config->base + STM32_FDCAN_NBTP_OFFSET), + getreg32(config->base + STM32_FDCAN_DBTP_OFFSET)); + + caninfo(" IE: %08" PRIx32 " TIE: %08" PRIx32 "\n", + getreg32(config->base + STM32_FDCAN_IE_OFFSET), + getreg32(config->base + STM32_FDCAN_TXBTIE_OFFSET)); + + caninfo(" ILE: %08" PRIx32 " ILS: %08" PRIx32 "\n", + getreg32(config->base + STM32_FDCAN_ILE_OFFSET), + getreg32(config->base + STM32_FDCAN_ILS_OFFSET)); + + caninfo(" TXBC: %08" PRIx32 "\n", + getreg32(config->base + STM32_FDCAN_TXBC_OFFSET)); +} +#endif + +/**************************************************************************** + * Name: stm32can_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; + + caninfo("CAN%d Rx Registers: %s\n", config->port, msg); + caninfo(" Base: %08" PRIx32 "\n", config->base); + + caninfo(" 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)); + + caninfo(" RXF0S: %08" PRIx32 " RXF0A: %08" PRIx32 "\n", + getreg32(config->base + STM32_FDCAN_RXF0S_OFFSET), + getreg32(config->base + STM32_FDCAN_RXF0A_OFFSET)); + + caninfo(" RXF1S: %08" PRIx32 " RXF1A: %08" PRIx32 "\n", + getreg32(config->base + STM32_FDCAN_RXF1S_OFFSET), + getreg32(config->base + STM32_FDCAN_RXF1A_OFFSET)); + + caninfo(" IR: %08" PRIx32 " IE: %08" PRIx32 "\n", + getreg32(config->base + STM32_FDCAN_IR_OFFSET), + getreg32(config->base + STM32_FDCAN_IE_OFFSET)); +} +#endif + +/**************************************************************************** + * Name: stm32can_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; + + caninfo("CAN%d Tx Registers: %s\n", config->port, msg); + caninfo(" Base: %08" PRIx32 "\n", config->base); + + caninfo(" PSR: %08" PRIx32 " ECR: %08" PRIx32 "\n", + getreg32(config->base + STM32_FDCAN_PSR_OFFSET), + getreg32(config->base + STM32_FDCAN_ECR_OFFSET)); + + caninfo(" 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)); + + caninfo(" TXBTO: %08" PRIx32 " TXBCR: %08" PRIx32 "\n", + getreg32(config->base + STM32_FDCAN_TXBTO_OFFSET), + getreg32(config->base + STM32_FDCAN_TXBCR_OFFSET)); + + caninfo(" TXEFS: %08" PRIx32 " TXEFA: %08" PRIx32 "\n", + getreg32(config->base + STM32_FDCAN_TXEFS_OFFSET), + getreg32(config->base + STM32_FDCAN_TXEFA_OFFSET)); + + caninfo(" 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: stm32can_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; + + caninfo(" ******* FDCAN%d Message RAM layout *******\n", config->port); + caninfo(" Start # Elmnt Elmnt size (words)\n"); + + if (config->nstdfilters > 0) + { + caninfo("STD filters %p %4d %2d\n", + config->msgram.stdfilters, + config->nstdfilters, + 1); + } + + if (config->nextfilters) + { + caninfo("EXT filters %p %4d %2d\n", + config->msgram.extfilters, + config->nextfilters, + 2); + } + + if (config->nrxfifo0) + { + caninfo("RX FIFO 0 %p %4d %2d\n", + config->msgram.rxfifo0, + config->nrxfifo0, + config->rxfifo0esize); + } + + if (config->nrxfifo1) + { + caninfo("RX FIFO 1 %p %4d %2d\n", + config->msgram.rxfifo1, + config->nrxfifo1, + config->rxfifo1esize); + } + + if (config->ntxeventfifo) + { + caninfo("TX EVENT %p %4d %2d\n", + config->msgram.txeventfifo, + config->ntxeventfifo, + config->txeventesize); + } + + if (config->ntxfifoq) + { + caninfo("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_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(FAR struct stm32_fdcan_s *priv, + FAR struct canioc_extfilter_s *extconfig) +{ + FAR const struct stm32_config_s *config = NULL; + FAR uint32_t *extfilter = NULL; + uint32_t regval = 0; + int word = 0; + int bit = 0; + int ndx = 0; + + DEBUGASSERT(priv != NULL && priv->config != NULL && extconfig != NULL); + config = priv->config; + + /* Find an unused standard filter */ + + for (ndx = 0; ndx < config->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 < priv->config->nstdfilters); + priv->extfilters[word] |= (1 << bit); + priv->nextalloc++; + + extfilter = config->msgram.extfilters + (ndx << 1); + + /* Format and write filter word F0 */ + + DEBUGASSERT(extconfig->xf_id1 <= CAN_MAX_EXTMSGID); + regval = EXTFILTER_F0_EFID1(extconfig->xf_id1); + + if (extconfig->xf_prio == 0) + { + regval |= EXTFILTER_F0_EFEC_FIFO0; + } + else + { + regval |= EXTFILTER_F0_EFEC_FIFO0; + } + + extfilter[0] = regval; + + /* Format and write filter word F1 */ + + DEBUGASSERT(extconfig->xf_id2 <= CAN_MAX_EXTMSGID); + regval = EXTFILTER_F1_EFID2(extconfig->xf_id2); + + switch (extconfig->xf_type) + { + default: + case CAN_FILTER_DUAL: + regval |= EXTFILTER_F1_EFT_DUAL; + break; + + case CAN_FILTER_MASK: + regval |= EXTFILTER_F1_EFT_CLASSIC; + break; + case CAN_FILTER_RANGE: + regval |= EXTFILTER_F1_EFT_RANGE; + break; + } + + extfilter[1] = regval; + + /* Is this the first extended filter? */ + + if (priv->nextalloc == 1) + { + /* 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_INIT | FDCAN_CCCR_CCE); + fdcan_putreg(priv, STM32_FDCAN_CCCR_OFFSET, regval); + + /* Update the Global Filter Configuration so that received + * messages are rejected if they do not match the acceptance + * filter. + * + * ANFE=2: Discard all rejected frames + */ + + regval = fdcan_getreg(priv, STM32_FDCAN_RXGFC_OFFSET); + regval &= ~FDCAN_RXGFC_ANFE_MASK; + regval |= FDCAN_RXGFC_ANFE_REJECTED; + fdcan_putreg(priv, STM32_FDCAN_RXGFC_OFFSET, regval); + + /* Disable writing to configuration registers */ + + regval = fdcan_getreg(priv, STM32_FDCAN_CCCR_OFFSET); + regval &= ~(FDCAN_CCCR_INIT | FDCAN_CCCR_CCE); + fdcan_putreg(priv, STM32_FDCAN_CCCR_OFFSET, regval); + } + + return ndx; + } + } + + DEBUGASSERT(priv->nextalloc == priv->config->nextfilters); + + return -EAGAIN; +} +#endif + +/**************************************************************************** + * Name: fdcan_del_extfilter + * + * Description: + * Remove an address filter for a standard 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(FAR struct stm32_fdcan_s *priv, int ndx) +{ + FAR const struct stm32_config_s *config = NULL; + FAR uint32_t *extfilter = NULL; + uint32_t regval = 0; + int word = 0; + int bit = 0; + + DEBUGASSERT(priv != NULL && priv->config != NULL); + config = priv->config; + + /* Check user Parameters */ + + DEBUGASSERT(ndx >= 0 || ndx < config->nextfilters); + + if (ndx < 0 || ndx >= config->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--; + + /* Was that the last extended filter? */ + + if (priv->nextalloc == 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_INIT | FDCAN_CCCR_CCE); + fdcan_putreg(priv, STM32_FDCAN_CCCR_OFFSET, regval); + + /* If there are no extended filters, then modify Global Filter + * Configuration so that all rejected messages are places in RX + * FIFO0. + * + * ANFE=0: Store all rejected extended frame in RX FIFO0 + */ + + regval = fdcan_getreg(priv, STM32_FDCAN_RXGFC_OFFSET); + regval &= ~FDCAN_RXGFC_ANFE_MASK; + regval |= FDCAN_RXGFC_ANFE_RX_FIFO0; + fdcan_putreg(priv, STM32_FDCAN_RXGFC_OFFSET, regval); + + /* Disable writing to configuration registers */ + + regval = fdcan_getreg(priv, STM32_FDCAN_CCCR_OFFSET); + regval &= ~(FDCAN_CCCR_INIT | FDCAN_CCCR_CCE); + fdcan_putreg(priv, STM32_FDCAN_CCCR_OFFSET, regval); + } + + /* 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(FAR struct stm32_fdcan_s *priv, + FAR struct canioc_stdfilter_s *stdconfig) +{ + FAR const struct stm32_config_s *config = NULL; + FAR uint32_t *stdfilter = NULL; + uint32_t regval = 0; + int word = 0; + int bit = 0; + int ndx = 0; + + DEBUGASSERT(priv != NULL && priv->config != NULL); + config = priv->config; + + /* Find an unused standard filter */ + + for (ndx = 0; ndx < config->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 < priv->config->nstdfilters); + priv->stdfilters[word] |= (1 << bit); + priv->nstdalloc++; + + /* Format and write filter word S0 */ + + stdfilter = config->msgram.stdfilters + ndx; + + DEBUGASSERT(stdconfig->sf_id1 <= CAN_MAX_STDMSGID); + regval = STDFILTER_S0_SFID1(stdconfig->sf_id1); + + DEBUGASSERT(stdconfig->sf_id2 <= CAN_MAX_STDMSGID); + regval |= STDFILTER_S0_SFID2(stdconfig->sf_id2); + + if (stdconfig->sf_prio == 0) + { + regval |= STDFILTER_S0_SFEC_FIFO0; + } + else + { + regval |= STDFILTER_S0_SFEC_FIFO1; + } + + switch (stdconfig->sf_type) + { + default: + case CAN_FILTER_DUAL: + regval |= STDFILTER_S0_SFT_DUAL; + break; + + case CAN_FILTER_MASK: + regval |= STDFILTER_S0_SFT_CLASSIC; + break; + case CAN_FILTER_RANGE: + regval |= STDFILTER_S0_SFT_RANGE; + break; + } + + *stdfilter = regval; + + /* Is this the first standard filter? */ + + if (priv->nstdalloc == 1) + { + /* 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_INIT | FDCAN_CCCR_CCE); + fdcan_putreg(priv, STM32_FDCAN_CCCR_OFFSET, regval); + + /* Update the Global Filter Configuration so that received + * messages are rejected if they do not match the acceptance + * filter. + * + * ANFS=2: Discard all rejected frames + */ + + regval = fdcan_getreg(priv, STM32_FDCAN_RXGFC_OFFSET); + regval &= ~FDCAN_RXGFC_ANFS_MASK; + regval |= FDCAN_RXGFC_ANFS_REJECTED; + fdcan_putreg(priv, STM32_FDCAN_RXGFC_OFFSET, regval); + + /* Disable writing to configuration registers */ + + regval = fdcan_getreg(priv, STM32_FDCAN_CCCR_OFFSET); + regval &= ~(FDCAN_CCCR_INIT | FDCAN_CCCR_CCE); + fdcan_putreg(priv, STM32_FDCAN_CCCR_OFFSET, regval); + } + + return ndx; + } + } + + DEBUGASSERT(priv->nstdalloc == priv->config->nstdfilters); + return -EAGAIN; +} + +/**************************************************************************** + * Name: fdcan_del_stdfilter + * + * Description: + * Remove an address filter for a standard 29 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(FAR struct stm32_fdcan_s *priv, int ndx) +{ + FAR const struct stm32_config_s *config = NULL; + FAR uint32_t *stdfilter = NULL; + uint32_t regval = 0; + int word = 0; + int bit = 0; + + DEBUGASSERT(priv != NULL && priv->config != NULL); + config = priv->config; + + /* Check Userspace Parameters */ + + DEBUGASSERT(ndx >= 0 || ndx < config->nstdfilters); + + if (ndx < 0 || ndx >= config->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--; + + /* Was that the last standard filter? */ + + if (priv->nstdalloc == 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_INIT | FDCAN_CCCR_CCE); + fdcan_putreg(priv, STM32_FDCAN_CCCR_OFFSET, regval); + + /* If there are no standard filters, then modify Global Filter + * Configuration so that all rejected messages are places in RX + * FIFO0. + * + * ANFS=0: Store all rejected extended frame in RX FIFO0 + */ + + regval = fdcan_getreg(priv, STM32_FDCAN_RXGFC_OFFSET); + regval &= ~FDCAN_RXGFC_ANFS_MASK; + regval |= FDCAN_RXGFC_ANFS_RX_FIFO0; + fdcan_putreg(priv, STM32_FDCAN_RXGFC_OFFSET, regval); + + /* Disable writing to configuration registers */ + + regval = fdcan_getreg(priv, STM32_FDCAN_CCCR_OFFSET); + regval &= ~(FDCAN_CCCR_INIT | FDCAN_CCCR_CCE); + fdcan_putreg(priv, STM32_FDCAN_CCCR_OFFSET, regval); + } + + /* 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 + * According the SAMV71 datasheet: + * + * "If the device goes Bus_Off, it will set FDCAN_CCCR.INIT of its own + * accord, stopping all bus activities. Once FDCAN_CCCR.INIT has been + * cleared by the processor (application), the device will then wait for + * 129 occurrences of Bus Idle (129 * 11 consecutive recessive bits) + * before resuming normal operation. At the end of the Bus_Off recovery + * sequence, the Error Management Counters will be reset. During the + * waiting time after the resetting of FDCAN_CCCR.INIT, each time a + * sequence of 11 recessive bits has been monitored, a Bit0 Error code is + * written to FDCAN_PSR.LEC, enabling the processor to readily check up + * whether the CAN bus is stuck at dominant or continuously disturbed and + * to monitor the Bus_Off recovery sequence. FDCAN_ECR.REC is used to + * count these sequences." + * + * 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(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)) + { + 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_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 can_dev_s *dev) +{ + FAR struct stm32_fdcan_s *priv = NULL; + FAR const struct stm32_config_s *config = NULL; + uint32_t regval = 0; + irqstate_t flags; + + DEBUGASSERT(dev); + priv = dev->cd_priv; + DEBUGASSERT(priv); + config = priv->config; + DEBUGASSERT(config); + + caninfo("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 can_dev_s *dev) +{ + FAR struct stm32_fdcan_s *priv = NULL; + FAR 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; + } + + fdcan_dumpregs(priv, "After hardware initialization"); + + /* 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(FAR struct can_dev_s *dev) +{ + FAR struct stm32_fdcan_s *priv = NULL; + FAR 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); + + /* 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_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(FAR struct can_dev_s *dev, bool enable) +{ + FAR struct stm32_fdcan_s *priv = dev->cd_priv; + uint32_t regval = 0; + + DEBUGASSERT(priv && priv->config); + + caninfo("FDCAN%d enable: %d\n", priv->config->port, enable); + + /* Enable/disable the receive interrupts */ + + 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); +} + +/**************************************************************************** + * 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 can_dev_s *dev, bool enable) +{ + FAR struct stm32_fdcan_s *priv = dev->cd_priv; + uint32_t regval = 0; + + DEBUGASSERT(priv && priv->config); + + caninfo("FDCAN%d enable: %d\n", priv->config->port, enable); + + /* Enable/disable the receive interrupts */ + + 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); +} + +/**************************************************************************** + * 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(FAR struct can_dev_s *dev, int cmd, unsigned long arg) +{ + FAR 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: + { + FAR struct canioc_bittiming_s *bt = + (FAR struct canioc_bittiming_s *)arg; + uint32_t regval; + uint32_t nbrp; + + DEBUGASSERT(bt != NULL); + + regval = fdcan_getreg(priv, STM32_FDCAN_NBTP_OFFSET); + 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_NTSEG2_MASK) >> + FDCAN_NBTP_NTSEG2_SHIFT) + 1; + + nbrp = ((regval & FDCAN_NBTP_NBRP_MASK) >> + FDCAN_NBTP_NBRP_SHIFT) + 1; + bt->bt_baud = STM32_FDCANCLK_FREQUENCY / nbrp / + (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 + * + * REVISIT: There is probably a limitation here: If there are + * multiple threads trying to send CAN packets, when one of these + * threads reconfigures the bitrate, the FDCAN hardware will be reset + * and the context of operation will be lost. Hence, this IOCTL can + * only safely be executed in quiescent time periods. + */ + + case CANIOC_SET_BITTIMING: + { + FAR const struct canioc_bittiming_s *bt = + (FAR const struct canioc_bittiming_s *)arg; + uint32_t nbrp; + uint32_t ntseg1; + uint32_t ntseg2; + uint32_t nsjw; + uint32_t ie; + uint8_t state; + + DEBUGASSERT(bt != NULL); + DEBUGASSERT(bt->bt_baud < STM32_FDCANCLK_FREQUENCY); + DEBUGASSERT(bt->bt_sjw > 0 && bt->bt_sjw <= 16); + DEBUGASSERT(bt->bt_tseg1 > 1 && bt->bt_tseg1 <= 64); + DEBUGASSERT(bt->bt_tseg2 > 0 && bt->bt_tseg2 <= 16); + + /* Extract bit timing data */ + + ntseg1 = bt->bt_tseg1 - 1; + ntseg2 = bt->bt_tseg2 - 1; + nsjw = bt->bt_sjw - 1; + + nbrp = (uint32_t) + (((float) STM32_FDCANCLK_FREQUENCY / + ((float)(ntseg1 + ntseg2 + 3) * (float)bt->bt_baud)) - 1); + + /* Save the value of the new bit timing register */ + + priv->nbtp = FDCAN_NBTP_NBRP(nbrp) | FDCAN_NBTP_NTSEG1(ntseg1) | + FDCAN_NBTP_NTSEG2(ntseg2) | FDCAN_NBTP_NSJW(nsjw); + + /* We need to reset to instantiate the new timing. Save + * current state information so that recover to this + * state. + */ + + ie = fdcan_getreg(priv, STM32_FDCAN_IE_OFFSET); + state = priv->state; + + /* Reset the FDCAN */ + + fdcan_reset(dev); + ret = OK; + + /* If we have previously been setup, then setup again */ + + if (state == FDCAN_STATE_SETUP) + { + ret = fdcan_setup(dev); + } + + /* We we have successfully re-initialized, then restore the + * interrupt state. + * + * REVISIT: Since the hardware was reset, any pending TX + * activity was lost. Should we disable TX interrupts? + */ + + if (ret == OK) + { + fdcan_putreg(priv, STM32_FDCAN_IE_OFFSET, ie & ~priv->txints); + } + } + 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, + (FAR 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: + { + DEBUGASSERT(arg <= priv->config->nextfilters); + 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, + (FAR 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: + { + DEBUGASSERT(arg <= priv->config->nstdfilters); + 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(FAR struct can_dev_s *dev, uint16_t id) +{ + /* REVISIT: Remote request not implemented */ + + return -ENOSYS; +} + +/**************************************************************************** + * 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 can_dev_s *dev, FAR struct can_msg_s *msg) +{ + FAR struct stm32_fdcan_s *priv = NULL; + FAR const struct stm32_config_s *config = NULL; + FAR 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(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); + + 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 (msg->cm_hdr.ch_extid) + { + DEBUGASSERT(msg->cm_hdr.ch_id <= CAN_MAX_EXTMSGID); + + regval = BUFFER_R0_EXTID(msg->cm_hdr.ch_id) | BUFFER_R0_XTD; + } + else +#endif + { + DEBUGASSERT(msg->cm_hdr.ch_id <= CAN_MAX_STDMSGID); + + regval = BUFFER_R0_STDID(msg->cm_hdr.ch_id); + } + + if (msg->cm_hdr.ch_rtr) + { + 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(msg->cm_hdr.ch_dlc); + + /* Followed by the amount of data corresponding to the DLC (T2..) */ + + dest = &txbuffer[2]; + src = msg->cm_data; + nbytes = fdcan_dlc2bytes(priv, msg->cm_hdr.ch_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 can_dev_s *dev) +{ + FAR struct stm32_fdcan_s *priv = dev->cd_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_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(FAR struct can_dev_s *dev) +{ + FAR struct stm32_fdcan_s *priv = dev->cd_priv; + uint32_t regval = 0; + int tffl = 0; + bool empty = false; + + DEBUGASSERT(priv != NULL && priv->config != NULL); + + /* Return the state of the TX FIFOQ. Return TRUE if the TX FIFO/Queue is + * empty. We don't have a reliable indication that the FIFO is empty, so + * we have to use some heuristics. + */ + + regval = fdcan_getreg(priv, STM32_FDCAN_TXFQS_OFFSET); + if (((regval & FDCAN_TXFQS_TFQF) != 0)) + { + return false; + } + + /* Tx FIFO Free Level */ + + tffl = (regval & FDCAN_TXFQS_TFFL_MASK) >> FDCAN_TXFQS_TFFL_SHIFT; + empty = (tffl >= priv->config->ntxfifoq); + + return empty; +} + +/**************************************************************************** + * 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 + * + ****************************************************************************/ + +#ifdef CONFIG_CAN_ERRORS +static void fdcan_error(FAR struct can_dev_s *dev, uint32_t status) +{ + FAR struct stm32_fdcan_s *priv = dev->cd_priv; + struct can_hdr_s hdr; + uint32_t psr = 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. + */ + + psr = fdcan_getreg(priv, STM32_FDCAN_PSR_OFFSET); + if ((psr & FDCAN_PSR_EP) != 0) + { + data[1] |= (CAN_ERROR1_RXPASSIVE | CAN_ERROR1_TXPASSIVE); + } + + if (psr & FDCAN_PSR_EW) + { + data[1] |= (CAN_ERROR1_RXWARNING | CAN_ERROR1_TXWARNING); + } + + if ((status & (FDCAN_INT_EP | FDCAN_INT_EW)) != 0) + { + /* "Error Passive" or "Error Warning" status changed */ + + errbits |= CAN_ERROR_CONTROLLER; + } + + if ((status & FDCAN_INT_PEA) != 0) + { + /* Protocol Error in Arbitration Phase */ + + if (psr & FDCAN_PSR_LEC_MASK) + { + /* Error code present */ + + if ((psr & FDCAN_PSR_LEC(FDCAN_PSR_EC_STUFF_ERROR)) != 0) + { + /* Stuff Error */ + + errbits |= CAN_ERROR_PROTOCOL; + data[2] |= CAN_ERROR2_STUFF; + } + + if ((psr & FDCAN_PSR_LEC(FDCAN_PSR_EC_FORM_ERROR)) != 0) + { + /* Format Error */ + + errbits |= CAN_ERROR_PROTOCOL; + data[2] |= CAN_ERROR2_FORM; + } + + if ((psr & FDCAN_PSR_LEC(FDCAN_PSR_EC_ACK_ERROR)) != 0) + { + /* Acknowledge Error */ + + errbits |= CAN_ERROR_NOACK; + } + + if ((psr & FDCAN_PSR_LEC(FDCAN_PSR_EC_BIT0_ERROR)) != 0) + { + /* Bit0 Error */ + + errbits |= CAN_ERROR_PROTOCOL; + data[2] |= CAN_ERROR2_BIT0; + } + + if ((psr & FDCAN_PSR_LEC(FDCAN_PSR_EC_BIT1_ERROR)) != 0) + { + /* Bit1 Error */ + + errbits |= CAN_ERROR_PROTOCOL; + data[2] |= CAN_ERROR2_BIT1; + } + + if ((psr & FDCAN_PSR_LEC(FDCAN_PSR_EC_CRC_ERROR)) != 0) + { + /* Receive CRC Error */ + + errbits |= CAN_ERROR_PROTOCOL; + data[3] |= (CAN_ERROR3_CRCSEQ | CAN_ERROR3_CRCDEL); + } + + if ((psr & FDCAN_PSR_LEC(FDCAN_PSR_EC_NO_CHANGE)) != 0) + { + /* No Change in Error */ + + errbits |= CAN_ERROR_PROTOCOL; + data[2] |= CAN_ERROR2_UNSPEC; + } + } + } + + if ((status & FDCAN_INT_PED) != 0) + { + /* Protocol Error in Data Phase */ + + if (psr & FDCAN_PSR_DLEC_MASK) + { + /* Error code present */ + + if ((psr & FDCAN_PSR_DLEC(FDCAN_PSR_EC_STUFF_ERROR)) != 0) + { + /* Stuff Error */ + + errbits |= CAN_ERROR_PROTOCOL; + data[2] |= CAN_ERROR2_STUFF; + } + + if ((psr & FDCAN_PSR_DLEC(FDCAN_PSR_EC_FORM_ERROR)) != 0) + { + /* Format Error */ + + errbits |= CAN_ERROR_PROTOCOL; + data[2] |= CAN_ERROR2_FORM; + } + + if ((psr & FDCAN_PSR_DLEC(FDCAN_PSR_EC_ACK_ERROR)) != 0) + { + /* Acknowledge Error */ + + errbits |= CAN_ERROR_NOACK; + } + + if ((psr & FDCAN_PSR_DLEC(FDCAN_PSR_EC_BIT0_ERROR)) != 0) + { + /* Bit0 Error */ + + errbits |= CAN_ERROR_PROTOCOL; + data[2] |= CAN_ERROR2_BIT0; + } + + if ((psr & FDCAN_PSR_DLEC(FDCAN_PSR_EC_BIT1_ERROR)) != 0) + { + /* Bit1 Error */ + + errbits |= CAN_ERROR_PROTOCOL; + data[2] |= CAN_ERROR2_BIT1; + } + + if ((psr & FDCAN_PSR_DLEC(FDCAN_PSR_EC_CRC_ERROR)) != 0) + { + /* Receive CRC Error */ + + errbits |= CAN_ERROR_PROTOCOL; + data[3] |= (CAN_ERROR3_CRCSEQ | CAN_ERROR3_CRCDEL); + } + + if ((psr & FDCAN_PSR_DLEC(FDCAN_PSR_EC_NO_CHANGE)) != 0) + { + /* No Change in Error */ + + errbits |= CAN_ERROR_PROTOCOL; + data[2] |= CAN_ERROR2_UNSPEC; + } + } + } + + if ((status & FDCAN_INT_BO) != 0) + { + /* Bus_Off Status changed */ + + if ((psr & FDCAN_PSR_BO) != 0) + { + errbits |= CAN_ERROR_BUSOFF; + } + else + { + errbits |= CAN_ERROR_RESTARTED; + } + } + + if ((status & (FDCAN_INT_RF0L | FDCAN_INT_RF1L)) != 0) + { + /* Receive FIFO 0/1 Message Lost + * Receive FIFO 1 Message Lost + */ + + errbits |= CAN_ERROR_CONTROLLER; + data[1] |= CAN_ERROR1_RXOVERFLOW; + } + + if ((status & FDCAN_INT_TEFL) != 0) + { + /* Tx Event FIFO Element Lost */ + + errbits |= CAN_ERROR_CONTROLLER; + data[1] |= CAN_ERROR1_TXOVERFLOW; + } + + if ((status & FDCAN_INT_TOO) != 0) + { + /* Timeout Occurred */ + + errbits |= CAN_ERROR_TXTIMEOUT; + } + + if ((status & (FDCAN_INT_MRAF | FDCAN_INT_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. */ + + hdr.ch_id = errbits; + hdr.ch_dlc = CAN_ERROR_DLC; + hdr.ch_rtr = 0; + hdr.ch_error = 1; +#ifdef CONFIG_CAN_EXTID + hdr.ch_extid = 0; +#endif + hdr.ch_unused = 0; + + /* 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 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 can_dev_s *dev, FAR uint32_t *rxbuffer, + unsigned long nwords) +{ + struct can_hdr_s hdr; + uint32_t regval = 0; + int ret = 0; + + fdcan_dumprxregs(dev->cd_priv, "Before receive"); + + /* Format the CAN header */ + + /* Work R0 contains the CAN ID */ + + regval = *rxbuffer++; + +#ifdef CONFIG_CAN_ERRORS + hdr.ch_error = 0; +#endif + hdr.ch_unused = 0; + + if ((regval & BUFFER_R0_RTR) != 0) + { + hdr.ch_rtr = true; + } + else + { + hdr.ch_rtr = false; + } + +#ifdef CONFIG_CAN_EXTID + if ((regval & BUFFER_R0_XTD) != 0) + { + /* Save the extended ID of the newly received message */ + + hdr.ch_id = (regval & BUFFER_R0_EXTID_MASK) >> + BUFFER_R0_EXTID_SHIFT; + hdr.ch_extid = true; + } + else + { + hdr.ch_id = (regval & BUFFER_R0_STDID_MASK) >> + BUFFER_R0_STDID_SHIFT; + hdr.ch_extid = false; + } + +#else + if ((regval & BUFFER_R0_XTD) != 0) + { + /* Drop any messages with extended IDs */ + + return; + } + + /* Save the standard ID of the newly received message */ + + hdr.ch_id = (regval & BUFFER_R0_STDID_MASK) >> BUFFER_R0_STDID_SHIFT; +#endif + + /* Word R1 contains the DLC and timestamp */ + + regval = *rxbuffer++; + + hdr.ch_dlc = (regval & BUFFER_R1_DLC_MASK) >> BUFFER_R1_DLC_SHIFT; + + /* And provide the CAN message to the upper half logic */ + + ret = can_receive(dev, &hdr, (FAR uint8_t *)rxbuffer); + if (ret < 0) + { + canerr("ERROR: can_receive failed: %d\n", ret); + } +} + +/**************************************************************************** + * 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, FAR void *arg) +{ + FAR struct can_dev_s *dev = (FAR struct can_dev_s *)arg; + FAR struct stm32_fdcan_s *priv = NULL; + FAR const struct stm32_config_s *config = NULL; + uint32_t ir = 0; + uint32_t ie = 0; + uint32_t pending = 0; + uint32_t regval = 0; + uint32_t psr = 0; + unsigned int nelem = 0; + unsigned int ndx = 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) + { + /* Check for common errors */ + + if ((pending & FDCAN_CMNERR_INTS) != 0) + { + canerr("ERROR: Common %08" PRIx32 "\n", + pending & FDCAN_CMNERR_INTS); + + /* 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) + { + canerr("ERROR: PSR %08" PRIx32 "\n", psr); + ie &= ~(FDCAN_INT_PEA | FDCAN_INT_PED); + fdcan_putreg(priv, STM32_FDCAN_IE_OFFSET, ie); + caninfo("disabled protocol error intterupts\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); + + /* 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) + { + 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); +#endif + } + + /* Check for successful completion of a transmission */ + + if ((pending & FDCAN_INT_TC) != 0) + { + /* Check if we have disabled the ACKE in the error-handling above + * (see FDCAN_TXERR_INTS) to prevent Interrupt-Flooding and + * re-enable the error interrupt here again. + */ + + if ((ie & (FDCAN_INT_PEA | FDCAN_INT_PED)) == 0) + { + ie |= (FDCAN_INT_PEA | FDCAN_INT_PED); + fdcan_putreg(priv, STM32_FDCAN_IE_OFFSET, ie); + caninfo("Renabled protocol error intterupts\n"); + } + + /* Clear the pending TX completion interrupt (and all + * other TX-related interrupts) + */ + + fdcan_putreg(priv, STM32_FDCAN_IR_OFFSET, priv->txints); + + /* Check all TX buffers */ + + regval = fdcan_getreg(priv, STM32_FDCAN_TXBTO_OFFSET); + for (ndx = 0; ndx < config->ntxfifoq; ndx += 1) + { + if (regval & (1 << ndx)) + { + /* 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 FIFO1 new message interrupt */ + + fdcan_putreg(priv, STM32_FDCAN_IR_OFFSET, FDCAN_INT_RF1N); + pending &= ~FDCAN_INT_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. + */ + + for (; ; ) + { + /* 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) + { + /* Break out of the loop if RX FIFO1 is empty */ + + break; + } + + /* 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) + { + canerr("ERROR: Message lost: %08" PRIx32 "\n", regval); + } + else + { + fdcan_receive(dev, + config->msgram.rxfifo1 + + (ndx * priv->config->rxfifo1esize), + priv->config->rxfifo1esize); + + /* Turning back on all configured RX error interrupts */ + + ie |= (priv->rxints & FDCAN_RXERR_INTS); + fdcan_putreg(priv, STM32_FDCAN_IE_OFFSET, ie); + } + + /* Acknowledge reading the FIFO entry */ + + fdcan_putreg(priv, STM32_FDCAN_RXF1A_OFFSET, ndx); + } + + /* Check for successful reception of a new message in RX FIFO0 */ + + /* Clear the RX FIFO0 new message interrupt */ + + fdcan_putreg(priv, STM32_FDCAN_IR_OFFSET, FDCAN_INT_RF0N); + pending &= ~FDCAN_INT_RF0N; + + /* Check if there is anything in RX FIFO0 */ + + 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) + { + canerr("ERROR: Message lost: %08" PRIx32 "\n", regval); + } + else + { + fdcan_receive(dev, + config->msgram.rxfifo0 + + (ndx * priv->config->rxfifo0esize), + priv->config->rxfifo0esize); + + /* Turning back on all configured RX error interrupts */ + + ie |= (priv->rxints & FDCAN_RXERR_INTS); + fdcan_putreg(priv, STM32_FDCAN_IE_OFFSET, ie); + } + + /* Acknowledge reading the FIFO entry */ + + fdcan_putreg(priv, STM32_FDCAN_RXF0A_OFFSET, ndx); + } + + /* Clear unhandled RX interrupts */ + + if ((pending & priv->rxints) != 0) + { + fdcan_putreg(priv, STM32_FDCAN_IR_OFFSET, priv->rxints); + } + + 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 = priv->config; + FAR uint32_t *msgram = NULL; + uint32_t regval = 0; + uint32_t cntr = 0; + + caninfo("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) + { + default: + case FDCAN_ISO11898_1_FORMAT: + break; + + case FDCAN_NONISO_BOSCH_V1_FORMAT: + regval |= FDCAN_CCCR_NISO; + break; + } + + /* Select Classic CAN mode or FD mode with or without fast bit rate + * switching + */ + + switch (config->mode) + { + default: + 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 + } + + /* 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 */ + + /* Select RX-related interrupts */ + + priv->rxints = FDCAN_RXFIFO_INTS; + + /* Select TX-related interrupts */ + + priv->txints = FDCAN_TXFIFOQ_INTS; + + /* 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; +} + +/**************************************************************************** + * 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 + * + ****************************************************************************/ + +FAR struct can_dev_s *stm32_fdcaninitialize(int port) +{ + FAR struct can_dev_s *dev = NULL; + FAR struct stm32_fdcan_s *priv = NULL; + FAR const struct stm32_config_s *config = NULL; + + 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; + } + + /* 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; + + dev->cd_ops = &g_fdcanops; + dev->cd_priv = (FAR void *)priv; + + /* And put the hardware in the initial state */ + + fdcan_reset(dev); + + return dev; +} diff --git a/arch/arm/src/stm32/stm32_fdcan.h b/arch/arm/src/stm32/stm32_fdcan.h new file mode 100644 index 00000000000..34d2794f746 --- /dev/null +++ b/arch/arm/src/stm32/stm32_fdcan.h @@ -0,0 +1,90 @@ +/**************************************************************************** + * arch/arm/src/stm32/stm32_fdcan.h + * + * 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_STM32_STM32_FDCAN_H +#define __ARCH_ARM_SRC_STM32_STM32_FDCAN_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include "chip.h" +#include "hardware/stm32_fdcan.h" + +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Port numbers for use with stm32_fdcan_initialize() */ + +#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 + ****************************************************************************/ + +/**************************************************************************** + * 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 + * + ****************************************************************************/ + +FAR struct can_dev_s *stm32_fdcaninitialize(int port); + +#undef EXTERN +#if defined(__cplusplus) +} +#endif + +#endif /* __ASSEMBLY__ */ +#endif /* __ARCH_ARM_SRC_STM32_STM32_FDCAN_H */ diff --git a/arch/arm/src/stm32/stm32g4xxxx_rcc.c b/arch/arm/src/stm32/stm32g4xxxx_rcc.c index 7d5878b1e02..c2307427686 100644 --- a/arch/arm/src/stm32/stm32g4xxxx_rcc.c +++ b/arch/arm/src/stm32/stm32g4xxxx_rcc.c @@ -952,6 +952,15 @@ static void stm32_stdclockconfig(void) regval &= ~(RCC_CFGR_PPRE1_MASK | RCC_CFGR_PPRE2_MASK); regval |= (STM32_RCC_CFGR_PPRE1 | STM32_RCC_CFGR_PPRE2); putreg32(regval, STM32_RCC_CFGR); + + /* Configure FDCAN source clock */ + +#if defined(STM32_CCIPR_FDCANSRC) + regval = getreg32(STM32_RCC_CCIPR); + regval &= ~RCC_CCIPR_FDCANSEL_MASK; + regval |= STM32_CCIPR_FDCANSRC; + putreg32(regval, STM32_RCC_CCIPR); +#endif } /****************************************************************************