diff --git a/arch/arm/include/stm32f0l0g0/stm32c0_irq.h b/arch/arm/include/stm32f0l0g0/stm32c0_irq.h index 28b97f3ed92..5dc35a9ccbd 100644 --- a/arch/arm/include/stm32f0l0g0/stm32c0_irq.h +++ b/arch/arm/include/stm32f0l0g0/stm32c0_irq.h @@ -82,8 +82,8 @@ #define STM32_IRQ_USART2 (STM32_IRQ_EXTINT + 28) /* 28: USART2 */ #define STM32_IRQ_USART3 (STM32_IRQ_EXTINT + 29) /* 29: USART3 */ #define STM32_IRQ_USART4 (STM32_IRQ_EXTINT + 29) /* 29: USART4 */ -#define STM32_IRQ_FDCAN_IT0 (STM32_IRQ_EXTINT + 30) /* 30: FDCAN global interrupt 0 */ -#define STM32_IRQ_FDCAN_IT1 (STM32_IRQ_EXTINT + 31) /* 31: FDCAN global interrupt 1 */ +#define STM32_IRQ_FDCAN1_0 (STM32_IRQ_EXTINT + 30) /* 30: FDCAN global interrupt 0 */ +#define STM32_IRQ_FDCAN1_1 (STM32_IRQ_EXTINT + 31) /* 31: FDCAN global interrupt 1 */ #define STM32_IRQ_NEXTINT (32) diff --git a/arch/arm/src/stm32f0l0g0/CMakeLists.txt b/arch/arm/src/stm32f0l0g0/CMakeLists.txt index 713ce2811ee..0ebcf053ca0 100644 --- a/arch/arm/src/stm32f0l0g0/CMakeLists.txt +++ b/arch/arm/src/stm32f0l0g0/CMakeLists.txt @@ -111,4 +111,13 @@ if(CONFIG_STM32F0L0G0_WWDG) list(APPEND SRCS stm32_wwdg.c) endif() +if(CONFIG_STM32F0L0G0_FDCAN) + if(CONFIG_STM32F0L0G0_FDCAN_CHARDRIVER) + list(APPEND SRCS stm32_fdcan.c) + endif() + if(CONFIG_STM32F0L0G0_FDCAN_SOCKET) + list(APPEND SRCS stm32_fdcan_sock.c) + endif() +endif() + target_sources(arch PRIVATE ${SRCS}) diff --git a/arch/arm/src/stm32f0l0g0/Kconfig b/arch/arm/src/stm32f0l0g0/Kconfig index 7008d2b13e9..d672a44588e 100644 --- a/arch/arm/src/stm32f0l0g0/Kconfig +++ b/arch/arm/src/stm32f0l0g0/Kconfig @@ -1346,7 +1346,7 @@ config ARCH_CHIP_STM32C092XX select STM32F0L0G0_STM32C0 select STM32F0L0G0_HAVE_USART3 select STM32F0L0G0_HAVE_USART4 - select STM32F0L0G0_HAVE_FDCAN + select STM32F0L0G0_HAVE_FDCAN1 config STM32F0L0G0_DFU bool "DFU bootloader" @@ -1581,7 +1581,7 @@ config STM32F0L0G0_HAVE_OPAMP4 bool default n -config STM32F0L0G0_HAVE_FDCAN +config STM32F0L0G0_HAVE_FDCAN1 bool default n @@ -1680,6 +1680,12 @@ config STM32F0L0G0_DAC1 depends on STM32F0L0G0_HAVE_DAC1 select STM32F0L0G0_DAC +config STM32F0L0G0_FDCAN1 + bool "FDCAN1" + default n + depends on STM32F0L0G0_HAVE_FDCAN1 + select STM32F0L0G0_FDCAN + config STM32F0L0G0_FSMC bool "FSMC" default n @@ -1922,6 +1928,10 @@ config STM32F0L0G0_TIM bool default n +config STM32F0L0G0_FDCAN + bool + default n + config STM32F0L0G0_SERIALDRIVER bool default n @@ -2721,6 +2731,153 @@ config STM32F0L0G0_PWM_MULTICHAN endmenu # Timer Configuration +menu "FDCAN driver configuration" + depends on STM32F0L0G0_FDCAN + +choice + prompt "FDCAN character driver or SocketCAN support" + default STM32F0L0G0_FDCAN_CHARDRIVER + +config STM32F0L0G0_FDCAN_CHARDRIVER + bool "STM32 FDCAN character driver support" + select ARCH_HAVE_CAN_ERRORS + select CAN + +config STM32F0L0G0_FDCAN_SOCKET + bool "STM32 FDCAN SocketCAN support" + select NET_CAN_HAVE_ERRORS + select NET_CAN_HAVE_CANFD + +endchoice # FDCAN character driver or SocketCAN support + +config STM32F0L0G0_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 STM32F0L0G0_FDCAN_QUEUE_MODE + bool "FDCAN QUEUE mode (vs FIFO mode)" + default n + +menu "FDCAN1 device driver options" + depends on STM32F0L0G0_FDCAN1 + +choice + prompt "FDCAN1 frame format" + default STM32F0L0G0_FDCAN1_ISO11898_1 + +config STM32F0L0G0_FDCAN1_ISO11898_1 + bool "ISO11898-1" + ---help--- + Enable ISO11898-1 frame format + +config STM32F0L0G0_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 STM32F0L0G0_FDCAN1_CLASSIC + +config STM32F0L0G0_FDCAN1_CLASSIC + bool "Classic CAN" + ---help--- + Enable Classic CAN mode + +config STM32F0L0G0_FDCAN1_FD + bool "CAN FD" + depends on CAN_FD || NET_CAN_CANFD + ---help--- + Enable CAN FD mode + +config STM32F0L0G0_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 STM32F0L0G0_FDCAN1_LOOPBACK + bool "Enable FDCAN1 loopback mode" + default n + ---help--- + Enable the FDCAN1 local loopback mode for testing purposes. + +comment "Nominal Bit Timing" + +config STM32F0L0G0_FDCAN1_BITRATE + int "FDCAN bitrate" + default 500000 + range 0 1000000 + ---help--- + FDCAN1 bitrate in bits per second. Required if STM32F0L0G0_FDCAN1 is defined. + +config STM32F0L0G0_FDCAN1_NTSEG1 + int "FDCAN1 NTSEG1 (PropSeg + PhaseSeg1)" + default 6 + range 1 256 + ---help--- + The length of the bit time is Tquanta * (SyncSeg + PropSeg + PhaseSeg1 + PhaseSeg2). + +config STM32F0L0G0_FDCAN1_NTSEG2 + int "FDCAN1 NTSEG2 (PhaseSeg2)" + default 7 + range 1 128 + ---help--- + The length of the bit time is Tquanta * (SyncSeg + PropSeg + PhaseSeg1 + PhaseSeg2). + +config STM32F0L0G0_FDCAN1_NSJW + int "FDCAN1 synchronization jump width" + default 1 + range 1 128 + ---help--- + The length of the bit time is Tquanta * (SyncSeg + PropSeg + PhaseSeg1 + PhaseSeg2). + +comment "Data Bit Timing" + depends on CAN_FD && STM32F0L0G0_FDCAN1_FD_BRS + +config STM32F0L0G0_FDCAN1_DBITRATE + int "FDCAN1 data bitrate" + default 2000000 + depends on CAN_FD && STM32F0L0G0_FDCAN1_FD_BRS + ---help--- + FDCAN1 bitrate in bits per second. Required if operating in FD mode with bit rate switching (BRS). + +config STM32F0L0G0_FDCAN1_DTSEG1 + int "FDCAN1 DTSEG1 (PropSeg + PhaseSeg1 of data phase)" + default 4 + range 1 31 + depends on CAN_FD && STM32F0L0G0_FDCAN1_FD_BRS + ---help--- + The length of the bit time is Tquanta * (SyncSeg + PropSeg + PhaseSeg1 + PhaseSeg2). + +config STM32F0L0G0_FDCAN1_DTSEG2 + int "FDCAN1 DTSEG2 (PhaseSeg2 of data phase)" + default 4 + range 1 15 + depends on CAN_FD && STM32F0L0G0_FDCAN1_FD_BRS + ---help--- + The length of the bit time is Tquanta * (SyncSeg + PropSeg + PhaseSeg1 + PhaseSeg2). + +config STM32F0L0G0_FDCAN1_DSJW + int "FDCAN1 fast synchronization jump width" + default 2 + range 1 15 + depends on CAN_FD && STM32F0L0G0_FDCAN1_FD_BRS + ---help--- + The duration of a synchronization jump is Tcan_clk x DSJW. + +endmenu # FDCAN1 device driver options + +endmenu # "FDCAN driver configuration" + menu "U[S]ART Configuration" depends on STM32F0L0G0_USART diff --git a/arch/arm/src/stm32f0l0g0/Make.defs b/arch/arm/src/stm32f0l0g0/Make.defs index 24416451888..06109a32a1c 100644 --- a/arch/arm/src/stm32f0l0g0/Make.defs +++ b/arch/arm/src/stm32f0l0g0/Make.defs @@ -104,3 +104,12 @@ endif ifeq ($(CONFIG_STM32F0L0G0_WWDG),y) CHIP_CSRCS += stm32_wwdg.c endif + +ifeq ($(CONFIG_STM32F0L0G0_FDCAN),y) +ifeq ($(CONFIG_STM32F0L0G0_FDCAN_CHARDRIVER),y) +CHIP_CSRCS += stm32_fdcan.c +endif +ifeq ($(CONFIG_STM32F0L0G0_FDCAN_SOCKET),y) +CHIP_CSRCS += stm32_fdcan_sock.c +endif +endif diff --git a/arch/arm/src/stm32f0l0g0/hardware/stm32_fdcan.h b/arch/arm/src/stm32f0l0g0/hardware/stm32_fdcan.h new file mode 100644 index 00000000000..fa93956fc69 --- /dev/null +++ b/arch/arm/src/stm32f0l0g0/hardware/stm32_fdcan.h @@ -0,0 +1,582 @@ +/**************************************************************************** + * arch/arm/src/stm32f0l0g0/hardware/stm32_fdcan.h + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +#ifndef __ARCH_ARM_SRC_STM32F0L0G0_HARDWARE_STM32_FDCAN_H +#define __ARCH_ARM_SRC_STM32F0L0G0_HARDWARE_STM32_FDCAN_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include "chip.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* 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_STM32F0L0G0_HARDWARE_STM32_FDCAN_H */ diff --git a/arch/arm/src/stm32f0l0g0/hardware/stm32c0_memorymap.h b/arch/arm/src/stm32f0l0g0/hardware/stm32c0_memorymap.h index 4995435775d..c847f7ca050 100644 --- a/arch/arm/src/stm32f0l0g0/hardware/stm32c0_memorymap.h +++ b/arch/arm/src/stm32f0l0g0/hardware/stm32c0_memorymap.h @@ -73,7 +73,7 @@ #define STM32_PWR_BASE 0x40007000 /* 0x40007000-0x400073ff PWR */ #define STM32_USBRAM_BASE 0x40009800 /* 0x40009800-0x40008fff USBRAM */ #define STM32_FDCANSRAM_BASE 0x4000b800 /* 0x4000b800-0x4000cbff FDCAN scratch RAM */ -#define STM32_FDCANMRAM_BASE 0x4000b400 /* 0x4000b400-0x4000b7ff FDCAN message RAM */ +#define STM32_CANRAM_BASE 0x4000b400 /* 0x4000b400-0x4000b7ff FDCAN message RAM */ #define STM32_SYSCFG_BASE 0x40010000 /* 0x40010000-0x400103ff SYSCFG */ /* EXTI ??? */ #define STM32_ADC1_BASE 0x40012400 /* 0x40012400-0x400127ff ADC1 */ diff --git a/arch/arm/src/stm32f0l0g0/hardware/stm32c0_pinmap.h b/arch/arm/src/stm32f0l0g0/hardware/stm32c0_pinmap.h index 04277439d3e..476e3691447 100644 --- a/arch/arm/src/stm32f0l0g0/hardware/stm32c0_pinmap.h +++ b/arch/arm/src/stm32f0l0g0/hardware/stm32c0_pinmap.h @@ -114,6 +114,27 @@ #define GPIO_USART2_TX_2 (GPIO_ALT | GPIO_PULLUP | GPIO_AF1 | GPIO_PUSHPULL | GPIO_PORTA | GPIO_PIN14) #define GPIO_USART2_TX_3 (GPIO_ALT | GPIO_PULLUP | GPIO_AF1 | GPIO_PUSHPULL | GPIO_PORTD | GPIO_PIN6) +/* FDCAN1 */ + +#define GPIO_FDCAN1_RX_1 (GPIO_ALT | GPIO_AF4 | GPIO_PUSHPULL | GPIO_PORTA | GPIO_PIN11) +#define GPIO_FDCAN1_RX_2 (GPIO_ALT | GPIO_AF3 | GPIO_PUSHPULL | GPIO_PORTB | GPIO_PIN0) +#define GPIO_FDCAN1_RX_3 (GPIO_ALT | GPIO_AF4 | GPIO_PUSHPULL | GPIO_PORTB | GPIO_PIN5) +#define GPIO_FDCAN1_RX_4 (GPIO_ALT | GPIO_AF4 | GPIO_PUSHPULL | GPIO_PORTB | GPIO_PIN12) +#define GPIO_FDCAN1_RX_5 (GPIO_ALT | GPIO_AF8 | GPIO_PUSHPULL | GPIO_PORTB | GPIO_PIN8) +#define GPIO_FDCAN1_RX_6 (GPIO_ALT | GPIO_AF4 | GPIO_PUSHPULL | GPIO_PORTC | GPIO_PIN2) +#define GPIO_FDCAN1_RX_7 (GPIO_ALT | GPIO_AF4 | GPIO_PUSHPULL | GPIO_PORTC | GPIO_PIN4) +#define GPIO_FDCAN1_RX_8 (GPIO_ALT | GPIO_AF4 | GPIO_PUSHPULL | GPIO_PORTD | GPIO_PIN0) + +#define GPIO_FDCAN1_TX_1 (GPIO_ALT | GPIO_AF4 | GPIO_PUSHPULL | GPIO_PORTA | GPIO_PIN12) +#define GPIO_FDCAN1_TX_2 (GPIO_ALT | GPIO_AF3 | GPIO_PUSHPULL | GPIO_PORTB | GPIO_PIN1) +#define GPIO_FDCAN1_TX_3 (GPIO_ALT | GPIO_AF4 | GPIO_PUSHPULL | GPIO_PORTB | GPIO_PIN13) +#define GPIO_FDCAN1_TX_4 (GPIO_ALT | GPIO_AF15 | GPIO_PUSHPULL | GPIO_PORTB | GPIO_PIN6) +#define GPIO_FDCAN1_TX_5 (GPIO_ALT | GPIO_AF8 | GPIO_PUSHPULL | GPIO_PORTB | GPIO_PIN9) +#define GPIO_FDCAN1_TX_6 (GPIO_ALT | GPIO_AF4 | GPIO_PUSHPULL | GPIO_PORTC | GPIO_PIN3) +#define GPIO_FDCAN1_TX_7 (GPIO_ALT | GPIO_AF4 | GPIO_PUSHPULL | GPIO_PORTC | GPIO_PIN5) +#define GPIO_FDCAN1_TX_8 (GPIO_ALT | GPIO_AF13 | GPIO_PUSHPULL | GPIO_PORTC | GPIO_PIN14) +#define GPIO_FDCAN1_TX_9 (GPIO_ALT | GPIO_AF4 | GPIO_PUSHPULL | GPIO_PORTD | GPIO_PIN1) + /* TODO: missing pinmaps */ #endif /* __ARCH_ARM_SRC_STM32F0L0G0_HARDWARE_STM32C0_PINMAP_H */ diff --git a/arch/arm/src/stm32f0l0g0/hardware/stm32c0_rcc.h b/arch/arm/src/stm32f0l0g0/hardware/stm32c0_rcc.h index 1affcfaf74e..de3d045a5de 100644 --- a/arch/arm/src/stm32f0l0g0/hardware/stm32c0_rcc.h +++ b/arch/arm/src/stm32f0l0g0/hardware/stm32c0_rcc.h @@ -236,10 +236,10 @@ #define RCC_APB1RSTR_TIM2RST (1 << 0) /* Bit 0: Timer 2 reset */ #define RCC_APB1RSTR_TIM3RST (1 << 1) /* Bit 1: Timer 3 reset */ /* Bits 2-11: Reserved */ -#define RCC_APB1RSTR_FDCANRST (1 << 11) /* Bit 11: FDCAN reset */ - /* Bit 12: Reserved */ +#define RCC_APB1RSTR_FDCANRST (1 << 12) /* Bit 12: FDCAN reset */ +#define RCC_APB1RSTR_USBRST (1 << 13) /* Bit 13: USB reset */ #define RCC_APB1RSTR_SPI2RST (1 << 14) /* Bit 14: SPI 2 reset */ -#define RCC_APB1RSTR_USBRST (1 << 15) /* Bit 15: USB reset */ + /* Bit 15: Reserved */ #define RCC_APB1RSTR_CRCRST (1 << 16) /* Bit 15: CRC reset */ #define RCC_APB1RSTR_USART2RST (1 << 17) /* Bit 17: USART 2 reset */ #define RCC_APB1RSTR_USART3RST (1 << 18) /* Bit 18: USART 3 reset */ @@ -289,11 +289,11 @@ #define RCC_APB1ENR_TIM2EN (1 << 0) /* Bit 0: Timer 2 clock enable */ #define RCC_APB1ENR_TIM3EN (1 << 1) /* Bit 1: Timer 3 clock enable */ /* Bits 2-11: Reserved */ -#define RCC_APB1ENR_FDCANEN (1 << 11) /* Bit 11: FDCAN clock enable */ - /* Bit 12: Reserved */ +#define RCC_APB1ENR_FDCANEN (1 << 12) /* Bit 12: FDCAN clock enable */ +#define RCC_APB1ENR_USBEN (1 << 13) /* Bit 13: USB clock enable */ #define RCC_APB1ENR_SPI2EN (1 << 14) /* Bit 14: SPI 2 clock enable */ -#define RCC_APB1ENR_USBEN (1 << 15) /* Bit 15: USB clock enable */ -#define RCC_APB1ENR_CRCEN (1 << 16) /* Bit 15: CRC clock enable */ + /* Bit 15: Reserved */ +#define RCC_APB1ENR_CRCEN (1 << 16) /* Bit 16: CRC clock enable */ #define RCC_APB1ENR_USART2EN (1 << 17) /* Bit 17: USART 2 clock enable */ #define RCC_APB1ENR_USART3EN (1 << 18) /* Bit 18: USART 3 clock enable */ #define RCC_APB1ENR_USART4EN (1 << 19) /* Bit 19: USART 4 clock enable */ @@ -326,6 +326,35 @@ /* TODO: APB1 peripheral clock enable in Sleep mode register */ +/* RCC peripherals independent clock configuration register 1 */ + +#define RCC_CCIPR1_USART1SEL_SHIFT (0) /* Bits 0-1: USART1 clock source selection */ +# define RCC_CCIPR1_USART1SEL_PCLK (0 << RCC_CCIPR1_USART1SEL_SHIFT) +# define RCC_CCIPR1_USART1SEL_SYSCLK (1 << RCC_CCIPR1_USART1SEL_SHIFT) +# define RCC_CCIPR1_USART1SEL_HSIKER (2 << RCC_CCIPR1_USART1SEL_SHIFT) +# define RCC_CCIPR1_USART1SEL_LSE (3 << RCC_CCIPR1_USART1SEL_SHIFT) +#define RCC_CCIPR1_FDCAN1SEL_SHIFT (8) /* Bits 8-9: FDCAN1 clock source selection */ +# define RCC_CCIPR1_FDCAN1SEL_PCLK (0 << RCC_CCIPR1_FDCAN1SEL_SHIFT) +# define RCC_CCIPR1_FDCAN1SEL_SYSCLK (1 << RCC_CCIPR1_FDCAN1SEL_SHIFT) +# define RCC_CCIPR1_FDCAN1SEL_HSIKER (2 << RCC_CCIPR1_FDCAN1SEL_SHIFT) +#define RCC_CCIPR1_I2C1SEL_SHIFT (12) /* Bits 12-13: I2C1 clock source selection */ +# define RCC_CCIPR1_I2C1SEL_PCLK (0 << RCC_CCIPR1_I2C1SEL_SHIFT) +# define RCC_CCIPR1_I2C1SEL_SYSCLK (1 << RCC_CCIPR1_I2C1SEL_SHIFT) +# define RCC_CCIPR1_I2C1SEL_HSIKER (2 << RCC_CCIPR1_I2C1SEL_SHIFT) +#define RCC_CCIPR1_I2S1SEL_SHIFT (14) /* Bits 14-15: I2S1 clock source selection */ +# define RCC_CCIPR1_I2S1SEL_PCLK (0 << RCC_CCIPR1_I2S1SEL_SHIFT) +# define RCC_CCIPR1_I2S1SEL_HSIKER (2 << RCC_CCIPR1_I2S1SEL_SHIFT) +# define RCC_CCIPR1_I2S1SEL_I2S (3 << RCC_CCIPR1_I2S1SEL_SHIFT) +#define RCC_CCIPR1_ADC1SEL_SHIFT (30) /* Bits 30-31: ADC1 clock source selection */ +# define RCC_CCIPR1_ADC1SEL_SYSCLK (0 << RCC_CCIPR1_ADC1SEL_SHIFT) +# define RCC_CCIPR1_ADC1SEL_HSIKER (2 << RCC_CCIPR1_ADC1SEL_SHIFT) + +/* RCC peripherals independent clock configuration register 2 */ + +#define RCC_CCIPR2_USBSEL_SHIFT (12) /* Bit 12: SB clock source selection */ +#define RCC_CCIPR2_USBSEL_HSIUSB48 (0 << RCC_CCIPR2_USBSEL_SHIFT) +#define RCC_CCIPR2_USBSEL_HSE (1 << RCC_CCIPR2_USBSEL_SHIFT) + /* Clock configuration register 1 */ #define RCC_CSR1_LSEON (1 << 0) /* Bit 0: LSE enable */ diff --git a/arch/arm/src/stm32f0l0g0/stm32_fdcan.c b/arch/arm/src/stm32f0l0g0/stm32_fdcan.c new file mode 100644 index 00000000000..2ec9fb33bea --- /dev/null +++ b/arch/arm/src/stm32f0l0g0/stm32_fdcan.c @@ -0,0 +1,3224 @@ +/**************************************************************************** + * arch/arm/src/stm32f0l0g0/stm32_fdcan.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + *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 "stm32_fdcan.h" +#include "hardware/stm32_pinmap.h" +#include "stm32_gpio.h" +#include "stm32_rcc.h" + +/* Ported from arch/arm/src/stm32/stm32_fdcan.c */ + +/**************************************************************************** + * 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_ARCH_CHIP_STM32C0) + +/* FDCAN Message RAM */ + +# define FDCAN_MSGRAM_WORDS (212) +# define STM32_CANRAM1_BASE (STM32_CANRAM_BASE + 0x0000) + +# ifdef CONFIG_STM32F0L0G0_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 +#else +# error +#endif + +/* FDCAN1 Configuration *****************************************************/ + +#ifdef CONFIG_STM32F0L0G0_FDCAN1 + +/* Bit timing */ + +# define FDCAN1_NTSEG1 (CONFIG_STM32F0L0G0_FDCAN1_NTSEG1 - 1) +# define FDCAN1_NTSEG2 (CONFIG_STM32F0L0G0_FDCAN1_NTSEG2 - 1) +# define FDCAN1_NBRP ((STM32_FDCANCLK_FREQUENCY / \ + ((FDCAN1_NTSEG1 + FDCAN1_NTSEG2 + 3) * \ + CONFIG_STM32F0L0G0_FDCAN1_BITRATE)) - 1) +# define FDCAN1_NSJW (CONFIG_STM32F0L0G0_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_STM32F0L0G0_FDCAN1_FD_BRS +# define FDCAN1_DTSEG1 (CONFIG_STM32F0L0G0_FDCAN1_DTSEG1 - 1) +# define FDCAN1_DTSEG2 (CONFIG_STM32F0L0G0_FDCAN1_DTSEG2 - 1) +# define FDCAN1_DBRP ((STM32_FDCANCLK_FREQUENCY / \ + ((FDCAN1_DTSEG1 + FDCAN1_DTSEG2 + 3) * \ + CONFIG_STM32F0L0G0_FDCAN1_DBITRATE)) - 1) +# define FDCAN1_DSJW (CONFIG_STM32F0L0G0_FDCAN1_DSJW - 1) +# else +# define FDCAN1_DTSEG1 1 +# define FDCAN1_DTSEG2 1 +# define FDCAN1_DBRP 1 +# define FDCAN1_DSJW 1 +# endif /* CONFIG_STM32F0L0G0_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_STM32F0L0G0_FDCAN1 */ + +/* Loopback mode */ + +#undef STM32_FDCAN_LOOPBACK +#if defined(CONFIG_STM32F0L0G0_FDCAN1_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_STM32F0L0G0_FDCAN_REGDEBUG +#endif + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/* CAN frame format */ + +enum stm32_frameformat_e +{ + FDCAN_ISO11898_1_FORMAT = 0, /* Frame format according to ISO11898-1 */ + FDCAN_NONISO_BOSCH_V1_FORMAT = 1 /* Frame format according to Bosch CAN FD V1.0 */ +}; + +/* CAN mode of operation */ + +enum stm32_canmode_e +{ + FDCAN_CLASSIC_MODE = 0, /* Classic CAN operation */ +#ifdef CONFIG_CAN_FD + FDCAN_FD_MODE = 1, /* CAN FD operation */ + FDCAN_FD_BRS_MODE = 2 /* CAN FD operation with bit rate switching */ +#endif +}; + +/* CAN driver state */ + +enum can_state_s +{ + FDCAN_STATE_UNINIT = 0, /* Not yet initialized */ + FDCAN_STATE_RESET, /* Initialized, reset state */ + FDCAN_STATE_SETUP, /* fdcan_setup() has been called */ + FDCAN_STATE_DISABLED /* Disabled by a fdcan_shutdown() */ +}; + +/* This structure describes the FDCAN message RAM layout */ + +struct stm32_msgram_s +{ + volatile uint32_t *stdfilters; /* Standard filters */ + volatile uint32_t *extfilters; /* Extended filters */ + volatile uint32_t *rxfifo0; /* RX FIFO0 */ + volatile uint32_t *rxfifo1; /* RX FIFO1 */ + volatile uint32_t *txeventfifo; /* TX event FIFO */ + volatile uint32_t *txfifoq; /* TX FIFO queue */ +}; + +/* This structure provides the constant configuration of a FDCAN peripheral */ + +struct stm32_config_s +{ + uint32_t rxpinset; /* RX pin configuration */ + uint32_t txpinset; /* TX pin configuration */ + uintptr_t base; /* Base address of the FDCAN registers */ + uint32_t baud; /* Configured baud */ + uint32_t nbtp; /* Nominal bit timing/prescaler register setting */ + uint32_t dbtp; /* Data bit timing/prescaler register setting */ + uint8_t port; /* FDCAN port number (1 or 2) */ + uint8_t irq0; /* FDCAN peripheral IRQ number for interrupt line 0 */ + uint8_t irq1; /* FDCAN peripheral IRQ number for interrupt line 1 */ + uint8_t mode; /* See enum stm32_canmode_e */ + uint8_t format; /* See enum stm32_frameformat_e */ + uint8_t nstdfilters; /* Number of standard filters */ + uint8_t nextfilters; /* Number of extended filters */ + uint8_t nrxfifo0; /* Number of RX FIFO0 elements */ + uint8_t nrxfifo1; /* Number of RX FIFO1 elements */ + uint8_t ntxeventfifo; /* Number of TXevent FIFO elements */ + uint8_t ntxfifoq; /* Number of TX FIFO queue elements */ + uint8_t rxfifo0esize; /* RX FIFO0 element size (words) */ + uint8_t rxfifo1esize; /* RX FIFO1 element size (words) */ + uint8_t txeventesize; /* TXevent element size (words) */ + uint8_t txbufferesize; /* TX buffer element size (words) */ +#ifdef STM32_FDCAN_LOOPBACK + bool loopback; /* True: Loopback mode */ +#endif + + /* FDCAN message RAM layout */ + + struct stm32_msgram_s msgram; +}; + +/* This structure provides the current state of a FDCAN peripheral */ + +struct stm32_fdcan_s +{ + /* The constant configuration */ + + const struct stm32_config_s *config; + + uint8_t state; /* See enum can_state_s */ +#ifdef CONFIG_CAN_EXTID + uint8_t nextalloc; /* Number of allocated extended filters */ +#endif + uint8_t nstdalloc; /* Number of allocated standard filters */ + uint32_t nbtp; /* Current nominal bit timing */ + uint32_t dbtp; /* Current data bit timing */ + 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_STM32F0L0G0_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(struct stm32_fdcan_s *priv, int offset); +static void fdcan_putreg(struct stm32_fdcan_s *priv, int offset, + uint32_t regval); +#ifdef CONFIG_STM32F0L0G0_FDCAN_REGDEBUG +static void fdcan_dumpregs(struct stm32_fdcan_s *priv, + const char *msg); +static void fdcan_dumprxregs(struct stm32_fdcan_s *priv, + const char *msg); +static void fdcan_dumptxregs(struct stm32_fdcan_s *priv, + const char *msg); +static void fdcan_dumpramlayout(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(struct stm32_fdcan_s *priv, uint8_t dlc); + +#ifdef CONFIG_CAN_EXTID +static int fdcan_add_extfilter(struct stm32_fdcan_s *priv, + struct canioc_extfilter_s *extconfig); +static int fdcan_del_extfilter(struct stm32_fdcan_s *priv, int ndx); +#endif +static int fdcan_add_stdfilter(struct stm32_fdcan_s *priv, + struct canioc_stdfilter_s *stdconfig); +static int fdcan_del_stdfilter(struct stm32_fdcan_s *priv, int ndx); + +static int +fdcan_start_busoff_recovery_sequence(struct stm32_fdcan_s *priv); + +/* CAN driver methods */ + +static void fdcan_reset(struct can_dev_s *dev); +static int fdcan_setup(struct can_dev_s *dev); +static void fdcan_shutdown(struct can_dev_s *dev); +static void fdcan_rxint(struct can_dev_s *dev, bool enable); +static void fdcan_txint(struct can_dev_s *dev, bool enable); +static int fdcan_ioctl(struct can_dev_s *dev, int cmd, + unsigned long arg); +static int fdcan_remoterequest(struct can_dev_s *dev, uint16_t id); +static int fdcan_send(struct can_dev_s *dev, struct can_msg_s *msg); +static bool fdcan_txready(struct can_dev_s *dev); +static bool fdcan_txempty(struct can_dev_s *dev); + +/* FDCAN interrupt handling */ + +#ifdef CONFIG_CAN_ERRORS +static void fdcan_error(struct can_dev_s *dev, uint32_t status); +#endif +static void fdcan_receive(struct can_dev_s *dev, + volatile uint32_t *rxbuffer, + unsigned long nwords); +static int fdcan_interrupt(int irq, void *context, void *arg); + +/* Hardware initialization */ + +static int fdcan_hw_initialize(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_STM32F0L0G0_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_STM32F0L0G0_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_STM32F0L0G0_FDCAN1_CLASSIC) + .mode = FDCAN_CLASSIC_MODE, +#elif defined(CONFIG_STM32F0L0G0_FDCAN1_FD) + .mode = FDCAN_FD_MODE, +#else + .mode = FDCAN_FD_BRS_MODE, +#endif +#if defined(CONFIG_STM32F0L0G0_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_STM32F0L0G0_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_STM32F0L0G0_FDCAN1 */ + +/**************************************************************************** + * 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_STM32F0L0G0_FDCAN_REGDEBUG +static uint32_t fdcan_getreg(struct stm32_fdcan_s *priv, int offset) +{ + 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(struct stm32_fdcan_s *priv, int offset) +{ + 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_STM32F0L0G0_FDCAN_REGDEBUG +static void fdcan_putreg(struct stm32_fdcan_s *priv, int offset, + uint32_t regval) +{ + 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(struct stm32_fdcan_s *priv, int offset, + uint32_t regval) +{ + 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_STM32F0L0G0_FDCAN_REGDEBUG +static void fdcan_dumpregs(struct stm32_fdcan_s *priv, + const char *msg) +{ + 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_STM32F0L0G0_FDCAN_REGDEBUG +static void fdcan_dumprxregs(struct stm32_fdcan_s *priv, + const char *msg) +{ + 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_STM32F0L0G0_FDCAN_REGDEBUG +static void fdcan_dumptxregs(struct stm32_fdcan_s *priv, + const char *msg) +{ + 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_STM32F0L0G0_FDCAN_REGDEBUG +static void fdcan_dumpramlayout(struct stm32_fdcan_s *priv) +{ + 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 > 0) + { + caninfo("EXT filters %p %4d %2d\n", + config->msgram.extfilters, + config->nextfilters, + 2); + } + + if (config->nrxfifo0 > 0) + { + caninfo("RX FIFO 0 %p %4d %2d\n", + config->msgram.rxfifo0, + config->nrxfifo0, + config->rxfifo0esize); + } + + if (config->nrxfifo1 > 0) + { + caninfo("RX FIFO 1 %p %4d %2d\n", + config->msgram.rxfifo1, + config->nrxfifo1, + config->rxfifo1esize); + } + + if (config->ntxeventfifo > 0) + { + caninfo("TX EVENT %p %4d %2d\n", + config->msgram.txeventfifo, + config->ntxeventfifo, + config->txeventesize); + } + + if (config->ntxfifoq > 0) + { + 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(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(struct stm32_fdcan_s *priv, + struct canioc_extfilter_s *extconfig) +{ + const struct stm32_config_s *config = NULL; + volatile uint32_t *extfilter = NULL; + uint32_t regval = 0; + 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_FIFO1; + } + + 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) + { + 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; + } + + default: + { + return -EINVAL; + } + } + + 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(struct stm32_fdcan_s *priv, int ndx) +{ + const struct stm32_config_s *config = NULL; + volatile 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(struct stm32_fdcan_s *priv, + struct canioc_stdfilter_s *stdconfig) +{ + const struct stm32_config_s *config = NULL; + volatile 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) + { + 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; + } + + default: + { + return -EINVAL; + } + } + + *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(struct stm32_fdcan_s *priv, int ndx) +{ + const struct stm32_config_s *config = NULL; + volatile 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 STM32G4 datasheet section 44.3.2 Software initialziation. + * + * Input Parameters: + * priv - An instance of the FDCAN driver state structure. + * + * Returned Value: + * Zero (OK) is returned on success. Otherwise a negated errno value is + * returned to indicate the nature of the error. + * + ****************************************************************************/ + +static int +fdcan_start_busoff_recovery_sequence(struct stm32_fdcan_s *priv) +{ + uint32_t regval = 0; + + DEBUGASSERT(priv); + + /* Only start BUS-OFF recovery if we are in BUS-OFF state */ + + regval = fdcan_getreg(priv, STM32_FDCAN_PSR_OFFSET); + if ((regval & FDCAN_PSR_BO) == 0) + { + return -EPERM; + } + + /* Disable initialization mode to issue the recovery sequence */ + + regval = fdcan_getreg(priv, STM32_FDCAN_CCCR_OFFSET); + regval &= ~FDCAN_CCCR_INIT; + fdcan_putreg(priv, STM32_FDCAN_CCCR_OFFSET, regval); + + return OK; +} + +/**************************************************************************** + * Name: fdcan_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(struct can_dev_s *dev) +{ + struct stm32_fdcan_s *priv = NULL; + 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_APB1RSTR); + regval |= RCC_APB1RSTR_FDCANRST; + putreg32(regval, STM32_RCC_APB1RSTR); + + regval &= ~RCC_APB1RSTR_FDCANRST; + putreg32(regval, STM32_RCC_APB1RSTR); + 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(struct can_dev_s *dev) +{ + struct stm32_fdcan_s *priv = NULL; + const struct stm32_config_s *config = NULL; + int ret = 0; + + DEBUGASSERT(dev); + priv = dev->cd_priv; + DEBUGASSERT(priv); + config = priv->config; + DEBUGASSERT(config); + + caninfo("FDCAN%d\n", config->port); + + /* FDCAN hardware initialization */ + + ret = fdcan_hw_initialize(priv); + if (ret < 0) + { + canerr("ERROR: FDCAN%d H/W initialization failed: %d\n", + config->port, ret); + return ret; + } + + 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(struct can_dev_s *dev) +{ + struct stm32_fdcan_s *priv = NULL; + const struct stm32_config_s *config = NULL; + uint32_t regval = 0; + + DEBUGASSERT(dev); + priv = dev->cd_priv; + DEBUGASSERT(priv); + config = priv->config; + DEBUGASSERT(config); + + caninfo("FDCAN%d\n", config->port); + + /* Disable FDCAN interrupts at the NVIC */ + + up_disable_irq(config->irq0); + up_disable_irq(config->irq1); + + /* Disable all interrupts from the FDCAN peripheral */ + + fdcan_putreg(priv, STM32_FDCAN_IE_OFFSET, 0); + fdcan_putreg(priv, STM32_FDCAN_TXBTIE_OFFSET, 0); + + /* Detach the FDCAN interrupt handler */ + + irq_detach(config->irq0); + irq_detach(config->irq1); + + /* 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(struct can_dev_s *dev, bool enable) +{ + 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(struct can_dev_s *dev, bool enable) +{ + 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(struct can_dev_s *dev, int cmd, unsigned long arg) +{ + struct stm32_fdcan_s *priv = NULL; + int ret = -ENOTTY; + + caninfo("cmd=%04x arg=%lu\n", cmd, arg); + + DEBUGASSERT(dev && dev->cd_priv); + priv = dev->cd_priv; + + /* Handle the command */ + + switch (cmd) + { + /* CANIOC_GET_BITTIMING: + * Description: Return the current bit timing settings + * Argument: A pointer to a write-able instance of struct + * canioc_bittiming_s in which current bit timing + * values will be returned. + * Returned Value: Zero (OK) is returned on success. Otherwise -1 + * (ERROR) is returned with the errno variable set + * to indicate the nature of the error. + * Dependencies: None + */ + + case CANIOC_GET_BITTIMING: + { + struct canioc_bittiming_s *bt = + (struct canioc_bittiming_s *)arg; + uint32_t regval; + uint32_t nbrp; + + DEBUGASSERT(bt != NULL); + +#ifdef CONFIG_CAN_FD + if (bt->type == CAN_BITTIMING_DATA) + { + regval = fdcan_getreg(priv, STM32_FDCAN_DBTP_OFFSET); + bt->bt_sjw = ((regval & FDCAN_DBTP_DSJW_MASK) >> + FDCAN_DBTP_DSJW_SHIFT) + 1; + bt->bt_tseg1 = ((regval & FDCAN_DBTP_DTSEG1_MASK) >> + FDCAN_DBTP_DTSEG1_SHIFT) + 1; + bt->bt_tseg2 = ((regval & FDCAN_DBTP_DTSEG2_MASK) >> + FDCAN_DBTP_DTSEG2_SHIFT) + 1; + + nbrp = ((regval & FDCAN_DBTP_DBRP_MASK) >> + FDCAN_DBTP_DBRP_SHIFT) + 1; + bt->bt_baud = STM32_FDCANCLK_FREQUENCY / nbrp / + (bt->bt_tseg1 + bt->bt_tseg2 + 1); + } + else +#endif + { + 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: + { + const struct canioc_bittiming_s *bt = + (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 */ + +#ifdef CONFIG_CAN_FD + if (bt->type == CAN_BITTIMING_DATA) + { + priv->dbtp = FDCAN_NBTP_DBRP(nbrp) | + FDCAN_NBTP_DTSEG1(ntseg1) | + FDCAN_DBTP_DTSEG2(ntseg2) | + FDCAN_DBTP_DSJW(nsjw); + } + else +#endif + { + 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, + (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, + (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(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(struct can_dev_s *dev, struct can_msg_s *msg) +{ + struct stm32_fdcan_s *priv = NULL; + const struct stm32_config_s *config = NULL; + volatile uint32_t *txbuffer = NULL; + const uint8_t *src = NULL; + uint32_t *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. + * Error state indicator (ESI) - ESI bit in CAN FD + * + * Format word T1: + * Data Length Code (DLC) - Value from message structure + * Bit Rate Switch (BRS) - Bit rate switching for CAN FD + * FD format (FDF) - Frame transmitted in CAN FD format + * Event FIFO Control (EFC) - Do not store events. + * Message Marker (MM) - Always zero + */ + + txbuffer[0] = 0; + txbuffer[1] = 0; + +#ifdef CONFIG_CAN_EXTID + if (msg->cm_hdr.ch_extid == 1) + { + DEBUGASSERT(msg->cm_hdr.ch_id <= CAN_MAX_EXTMSGID); + + txbuffer[0] |= BUFFER_R0_EXTID(msg->cm_hdr.ch_id) | BUFFER_R0_XTD; + } + else +#endif + { + DEBUGASSERT(msg->cm_hdr.ch_id <= CAN_MAX_STDMSGID); + + txbuffer[0] |= BUFFER_R0_STDID(msg->cm_hdr.ch_id); + } + + if (msg->cm_hdr.ch_rtr == 1) + { + txbuffer[0] |= BUFFER_R0_RTR; + } + + txbuffer[1] |= BUFFER_R1_DLC(msg->cm_hdr.ch_dlc); + +#ifdef CONFIG_CAN_FD + /* CAN FD Format */ + + if (msg->cm_hdr.ch_edl == 1) + { + txbuffer[1] |= BUFFER_R1_FDF; + + if (msg->cm_hdr.ch_brs == 1) + { + txbuffer[1] |= BUFFER_R1_BRS; + } + + if (msg->cm_hdr.ch_esi == 1) + { + txbuffer[0] |= BUFFER_R0_ESI; + } + } + else +#else + { + txbuffer[0] &= ~BUFFER_R0_ESI; + txbuffer[1] &= ~BUFFER_R1_FDF; + txbuffer[1] &= ~BUFFER_R1_BRS; + } +#endif + + /* Followed by the amount of data corresponding to the DLC (T2..) */ + + dest = (uint32_t *)&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(struct can_dev_s *dev) +{ + 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(struct can_dev_s *dev) +{ + 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(struct can_dev_s *dev, uint32_t status) +{ + 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) != 0) + { + 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) != 0) + { + /* 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) != 0) + { + /* 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_tcf = 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(struct can_dev_s *dev, + volatile uint32_t *rxbuffer, + unsigned long nwords) +{ + struct can_hdr_s hdr; + int ret = 0; + + fdcan_dumprxregs(dev->cd_priv, "Before receive"); + + /* Format the CAN header */ + + /* Word R0 contains the CAN ID */ + +#ifdef CONFIG_CAN_ERRORS + hdr.ch_error = 0; +#endif + hdr.ch_tcf = 0; + + /* Extract the RTR bit */ + + hdr.ch_rtr = ((rxbuffer[0] & BUFFER_R0_RTR) != 0); + +#ifdef CONFIG_CAN_EXTID + if ((rxbuffer[0] & BUFFER_R0_XTD) != 0) + { + /* Save the extended ID of the newly received message */ + + hdr.ch_id = (rxbuffer[0] & BUFFER_R0_EXTID_MASK) >> + BUFFER_R0_EXTID_SHIFT; + hdr.ch_extid = 1; + } + else + { + hdr.ch_id = (rxbuffer[0] & BUFFER_R0_STDID_MASK) >> + BUFFER_R0_STDID_SHIFT; + hdr.ch_extid = 0; + } + +#else + if ((rxbuffer[0] & BUFFER_R0_XTD) != 0) + { + /* Drop any messages with extended IDs */ + + canerr("ERROR: Received message with extended identifier. Dropped\n"); + + return; + } + + /* Save the standard ID of the newly received message */ + + hdr.ch_id = (rxbuffer[0] & BUFFER_R0_STDID_MASK) >> BUFFER_R0_STDID_SHIFT; +#endif + + /* Word R1 contains the DLC and timestamp */ + + hdr.ch_dlc = (rxbuffer[1] & BUFFER_R1_DLC_MASK) >> BUFFER_R1_DLC_SHIFT; + +#ifdef CONFIG_CAN_FD + /* CAN FD format */ + + hdr.ch_esi = ((rxbuffer[0] & BUFFER_R0_ESI) != 0); + hdr.ch_edl = ((rxbuffer[1] & BUFFER_R1_FDF) != 0); + hdr.ch_brs = ((rxbuffer[1] & BUFFER_R1_BRS) != 0); +#else + if ((rxbuffer[1] & BUFFER_R1_FDF) != 0) + { + /* Drop any FD CAN messages if not supported */ + + canerr("ERROR: Received CAN FD message. Dropped\n"); + + return; + } +#endif + + /* And provide the CAN message to the upper half logic */ + + ret = can_receive(dev, &hdr, (uint8_t *)&rxbuffer[2]); + 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, void *arg) +{ + struct can_dev_s *dev = (struct can_dev_s *)arg; + struct stm32_fdcan_s *priv = NULL; + const struct stm32_config_s *config = NULL; + uint32_t ir = 0; + uint32_t ie = 0; + uint32_t pending = 0; + uint32_t 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 occurs, the problem is recorded in + * the LEC/DLEC fields of the PSR register. In lieu of + * separate interrupt flags for each error, the hardware + * groups protocol errors under a single interrupt each for + * arbitration and data phases. + * + * These errors have a tendency to flood the system with + * interrupts, so they are disabled here until we get a + * successful transfer/receive on the hardware + */ + + 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 interrupts\n"); + } + + /* Clear the error indications */ + + fdcan_putreg(priv, STM32_FDCAN_IR_OFFSET, FDCAN_CMNERR_INTS); + } + + /* Check for transmission errors */ + + if ((pending & FDCAN_TXERR_INTS) != 0) + { + canerr("ERROR: TX %08" PRIx32 "\n", + pending & FDCAN_TXERR_INTS); + + /* 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("Re-enabled protocol error interrupts\n"); + } + + /* Clear the pending TX completion interrupt (and all + * other TX-related interrupts) + */ + + fdcan_putreg(priv, STM32_FDCAN_IR_OFFSET, priv->txints); + + /* Check all TX buffers */ + + regval = fdcan_getreg(priv, STM32_FDCAN_TXBTO_OFFSET); + for (ndx = 0; ndx < config->ntxfifoq; ndx++) + { + if ((regval & (1 << ndx)) != 0) + { + /* Tell the upper half that the transfer is finished. */ + + 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) +{ + const struct stm32_config_s *config = priv->config; + volatile 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); + + /* Re-enabled device if previously disabled in fdcan_shutdown() */ + + if (priv->state == FDCAN_STATE_DISABLED) + { + /* Reset Clock Stop Request bit */ + + regval = fdcan_getreg(priv, STM32_FDCAN_CCCR_OFFSET); + regval &= ~FDCAN_CCCR_CSR; + fdcan_putreg(priv, STM32_FDCAN_CCCR_OFFSET, regval); + + /* Wait for Clock Stop Acknowledge bit reset to indicate + * device is operational + */ + + while ((fdcan_getreg(priv, STM32_FDCAN_CCCR_OFFSET) & FDCAN_CCCR_CSA) + != 0); + } + + /* Enable the Initialization state */ + + regval = fdcan_getreg(priv, STM32_FDCAN_CCCR_OFFSET); + regval |= FDCAN_CCCR_INIT; + fdcan_putreg(priv, STM32_FDCAN_CCCR_OFFSET, regval); + + /* Wait for initialization mode to take effect */ + + while ((fdcan_getreg(priv, STM32_FDCAN_CCCR_OFFSET) & FDCAN_CCCR_INIT) + == 0); + + /* Enable writing to configuration registers */ + + regval = fdcan_getreg(priv, STM32_FDCAN_CCCR_OFFSET); + regval |= FDCAN_CCCR_CCE; + fdcan_putreg(priv, STM32_FDCAN_CCCR_OFFSET, regval); + + /* Global Filter Configuration: + * + * ANFS=0: Store all non matching standard frame in RX FIFO0 + * ANFE=0: Store all non matching extended frame in RX FIFO0 + */ + + regval = FDCAN_RXGFC_ANFE_RX_FIFO0 | FDCAN_RXGFC_ANFS_RX_FIFO0; + fdcan_putreg(priv, STM32_FDCAN_RXGFC_OFFSET, regval); + + /* Extended ID Filter AND mask */ + + fdcan_putreg(priv, STM32_FDCAN_XIDAM_OFFSET, 0x1fffffff); + + /* Disable all interrupts */ + + fdcan_putreg(priv, STM32_FDCAN_IE_OFFSET, 0); + fdcan_putreg(priv, STM32_FDCAN_TXBTIE_OFFSET, 0); + + /* All interrupts directed to Line 0. But disable both interrupt lines 0 + * and 1 for now. + * + * REVISIT: Only interrupt line 0 is used by this driver. + */ + + fdcan_putreg(priv, STM32_FDCAN_ILS_OFFSET, 0); + fdcan_putreg(priv, STM32_FDCAN_ILE_OFFSET, 0); + + /* Clear all pending interrupts. */ + + fdcan_putreg(priv, STM32_FDCAN_IR_OFFSET, FDCAN_INT_ALL); + + /* Configure FDCAN bit timing */ + + fdcan_putreg(priv, STM32_FDCAN_NBTP_OFFSET, priv->nbtp); + fdcan_putreg(priv, STM32_FDCAN_DBTP_OFFSET, priv->dbtp); + + /* Configure message RAM starting addresses and sizes. */ + + regval = FDCAN_RXGFC_LSS(config->nstdfilters); + regval |= FDCAN_RXGFC_LSE(config->nextfilters); + fdcan_putreg(priv, STM32_FDCAN_RXGFC_OFFSET, regval); + + /* Dump RAM layout */ + + fdcan_dumpramlayout(priv); + + /* Configure Message Filters */ + + /* Disable all standard filters */ + + msgram = config->msgram.stdfilters; + cntr = config->nstdfilters; + while (cntr > 0) + { + *msgram++ = STDFILTER_S0_SFEC_DISABLE; + cntr--; + } + + /* Disable all extended filters */ + + msgram = config->msgram.extfilters; + cntr = config->nextfilters; + while (cntr > 0) + { + *msgram = EXTFILTER_F0_EFEC_DISABLE; + msgram = msgram + 2; + cntr--; + } + + /* Input clock divider configuration */ + + regval = FDCANCLK_PDIV; + fdcan_putreg(priv, STM32_FDCAN_CKDIV_OFFSET, regval); + + /* CC control register */ + + regval = fdcan_getreg(priv, STM32_FDCAN_CCCR_OFFSET); + regval &= ~(FDCAN_CCCR_NISO | FDCAN_CCCR_FDOE | FDCAN_CCCR_BRSE); + + /* Select ISO11898-1 or Non ISO Bosch CAN FD Specification V1.0 */ + + switch (config->format) + { + case FDCAN_ISO11898_1_FORMAT: + { + break; + } + + case FDCAN_NONISO_BOSCH_V1_FORMAT: + { + regval |= FDCAN_CCCR_NISO; + break; + } + + default: + { + return -EINVAL; + } + } + + /* Select Classic CAN mode or FD mode with or without fast bit rate + * switching + */ + + switch (config->mode) + { + case FDCAN_CLASSIC_MODE: + { + break; + } + +#ifdef CONFIG_CAN_FD + case FDCAN_FD_MODE: + { + regval |= FDCAN_CCCR_FDOE; + break; + } + + case FDCAN_FD_BRS_MODE: + { + regval |= (FDCAN_CCCR_FDOE | FDCAN_CCCR_BRSE); + break; + } +#endif + + default: + { + return -EINVAL; + } + } + + /* Set the initial CAN mode */ + + fdcan_putreg(priv, STM32_FDCAN_CCCR_OFFSET, regval); + + /* Enable FIFO/Queue mode */ + + regval = fdcan_getreg(priv, STM32_FDCAN_TXBC_OFFSET); +#ifdef CONFIG_STM32F0L0G0_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. + * + * Returned Value: + * Valid CAN device structure reference on success; a NULL on failure + * + ****************************************************************************/ + +struct can_dev_s *stm32_fdcaninitialize(int port) +{ + struct can_dev_s *dev = NULL; + struct stm32_fdcan_s *priv = NULL; + const struct stm32_config_s *config = NULL; + + caninfo("FDCAN%d\n", port); + + /* Select FDCAN peripheral to be initialized */ + +#ifdef CONFIG_STM32F0L0G0_FDCAN1 + if (port == FDCAN1) + { + /* Select the FDCAN1 device structure */ + + dev = &g_fdcan1dev; + priv = &g_fdcan1priv; + config = &g_fdcan1const; + } + 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 = (void *)priv; + + /* And put the hardware in the initial state */ + + fdcan_reset(dev); + + return dev; +} diff --git a/arch/arm/src/stm32f0l0g0/stm32_fdcan.h b/arch/arm/src/stm32f0l0g0/stm32_fdcan.h new file mode 100644 index 00000000000..b96772738a2 --- /dev/null +++ b/arch/arm/src/stm32f0l0g0/stm32_fdcan.h @@ -0,0 +1,112 @@ +/**************************************************************************** + * arch/arm/src/stm32f0l0g0/stm32_fdcan.h + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +#ifndef __ARCH_ARM_SRC_STM32F0L0G0_STM32_FDCAN_H +#define __ARCH_ARM_SRC_STM32F0L0G0_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 + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +#ifndef __ASSEMBLY__ + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +#undef EXTERN +#if defined(__cplusplus) +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +#ifdef CONFIG_STM32F0L0G0_FDCAN_CHARDRIVER + +/**************************************************************************** + * Name: stm32_fdcaninitialize + * + * Description: + * Initialize the selected FDCAN port + * + * Input Parameters: + * Port number (for hardware that has multiple FDCAN interfaces) + * + * Returned Value: + * Valid FDCAN device structure reference on success; a NULL on failure + * + ****************************************************************************/ + +struct can_dev_s *stm32_fdcaninitialize(int port); +#endif + +#ifdef CONFIG_STM32F0L0G0_FDCAN_SOCKET + +/**************************************************************************** + * Name: stm32_fdcansockinitialize + * + * Description: + * Initialize the selected FDCAN port as SocketCAN interface + * + * Input Parameters: + * Port number (for hardware that has multiple FDCAN interfaces) + * + * Returned Value: + * OK on success; Negated errno on failure. + * + ****************************************************************************/ + +int stm32_fdcansockinitialize(int port); +#endif + +#undef EXTERN +#if defined(__cplusplus) +} +#endif + +#endif /* __ASSEMBLY__ */ +#endif /* __ARCH_ARM_SRC_STM32F0L0G0_STM32_FDCAN_H */ diff --git a/arch/arm/src/stm32f0l0g0/stm32_fdcan_sock.c b/arch/arm/src/stm32f0l0g0/stm32_fdcan_sock.c new file mode 100644 index 00000000000..bd96780d042 --- /dev/null +++ b/arch/arm/src/stm32f0l0g0/stm32_fdcan_sock.c @@ -0,0 +1,2992 @@ +/**************************************************************************** + * arch/arm/src/stm32f0l0g0/stm32_fdcan_sock.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include "arm_internal.h" +#include "stm32_fdcan.h" +#include "hardware/stm32_pinmap.h" +#include "stm32_gpio.h" +#include "stm32_rcc.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Pool configuration *******************************************************/ + +#define POOL_SIZE (1) + +/* Work queue support is required. */ + +#if !defined(CONFIG_SCHED_WORKQUEUE) +# error Work queue support is required +#endif + +/* The low priority work queue is preferred. If it is not enabled, LPWORK + * will be the same as HPWORK. + * + * NOTE: However, the network should NEVER run on the high priority work + * queue! That queue is intended only to service short back end interrupt + * processing that never suspends. Suspending the high priority work queue + * may bring the system to its knees! + */ + +#define CANWORK LPWORK + +/* Clock source *************************************************************/ + +#define FDCANCLK_PDIV (0) + +#if FDCANCLK_PDIV == 0 +# define STM32_FDCANCLK_FREQUENCY (STM32_FDCAN_FREQUENCY / (1)) +#else +# define STM32_FDCANCLK_FREQUENCY (STM32_FDCAN_FREQUENCY / (2 * FDCANCLK_PDIV)) +#endif + +/* General Configuration ****************************************************/ + +#if defined(CONFIG_ARCH_CHIP_STM32C0) + +/* FDCAN Message RAM */ + +# define FDCAN_MSGRAM_WORDS (212) +# define STM32_CANRAM1_BASE (STM32_CANRAM_BASE + 0x0000) + +# ifdef CONFIG_STM32F0L0G0_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 +#else +# error +#endif + +/* FDCAN1 Configuration *****************************************************/ + +#ifdef CONFIG_STM32F0L0G0_FDCAN1 + +/* Bit timing */ + +# define FDCAN1_NTSEG1 (CONFIG_STM32F0L0G0_FDCAN1_NTSEG1 - 1) +# define FDCAN1_NTSEG2 (CONFIG_STM32F0L0G0_FDCAN1_NTSEG2 - 1) +# define FDCAN1_NBRP ((STM32_FDCANCLK_FREQUENCY / \ + ((FDCAN1_NTSEG1 + FDCAN1_NTSEG2 + 3) * \ + CONFIG_STM32F0L0G0_FDCAN1_BITRATE)) - 1) +# define FDCAN1_NSJW (CONFIG_STM32F0L0G0_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_STM32F0L0G0_FDCAN1_FD_BRS +# define FDCAN1_DTSEG1 (CONFIG_STM32F0L0G0_FDCAN1_DTSEG1 - 1) +# define FDCAN1_DTSEG2 (CONFIG_STM32F0L0G0_FDCAN1_DTSEG2 - 1) +# define FDCAN1_DBRP ((STM32_FDCANCLK_FREQUENCY / \ + ((FDCAN1_DTSEG1 + FDCAN1_DTSEG2 + 3) * \ + CONFIG_STM32F0L0G0_FDCAN1_DBITRATE)) - 1) +# define FDCAN1_DSJW (CONFIG_STM32F0L0G0_FDCAN1_DSJW - 1) +# else +# define FDCAN1_DTSEG1 1 +# define FDCAN1_DTSEG2 1 +# define FDCAN1_DBRP 1 +# define FDCAN1_DSJW 1 +# endif /* CONFIG_STM32F0L0G0_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_STM32F0L0G0_FDCAN1 */ + +/* Loopback mode */ + +#undef STM32_FDCAN_LOOPBACK +#if defined(CONFIG_STM32F0L0G0_FDCAN1_LOOPBACK) +# define STM32_FDCAN_LOOPBACK 1 +#endif + +/* Interrupts ***************************************************************/ + +/* Common interrupts + * + * FDCAN_INT_TSW - Timestamp Wraparound + * FDCAN_INT_MRAF - Message RAM Access Failure + * FDCAN_INT_TOO - Timeout Occurred + * FDCAN_INT_ELO - Error Logging Overflow + * FDCAN_INT_EP - Error Passive + * FDCAN_INT_EW - Warning Status + * FDCAN_INT_BO - Bus_Off Status + * FDCAN_INT_WDI - Watchdog Interrupt + * FDCAN_INT_PEA - Protocol Error in Arbritration Phase + * FDCAN_INT_PED - Protocol Error in Data Phase + */ + +#define FDCAN_CMNERR_INTS (FDCAN_INT_MRAF | FDCAN_INT_TOO | FDCAN_INT_EP | \ + FDCAN_INT_BO | FDCAN_INT_WDI | FDCAN_INT_PEA | \ + FDCAN_INT_PED) + +/* RXFIFO mode interrupts + * + * FDCAN_INT_RF0N - Receive FIFO 0 New Message + * FDCAN_INT_RF0F - Receive FIFO 0 Full + * FDCAN_INT_RF0L - Receive FIFO 0 Message Lost + * FDCAN_INT_RF1N - Receive FIFO 1 New Message + * FDCAN_INT_RF1F - Receive FIFO 1 Full + * FDCAN_INT_RF1L - Receive FIFO 1 Message Lost + * FDCAN_INT_HPM - High Priority Message Received + * + */ + +#define FDCAN_RXFIFO0_INTS (FDCAN_INT_RF0N | FDCAN_INT_RF0L) +#define FDCAN_RXFIFO1_INTS (FDCAN_INT_RF1N | FDCAN_INT_RF1L) + +#define FDCAN_RXERR_INTS (FDCAN_INT_RF0L | FDCAN_INT_RF1L) + +/* TX FIFOQ mode interrupts + * + * FDCAN_INT_TFE - Tx FIFO Empty + * + * TX Event FIFO interrupts + * + * FDCAN_INT_TEFN - Tx Event FIFO New Entry + * FDCAN_INT_TEFF - Tx Event FIFO Full + * FDCAN_INT_TEFL - Tx Event FIFO Element Lost + * + * Mode-independent TX-related interrupts + * + * FDCAN_INT_TC - Transmission Completed + * FDCAN_INT_TCF - Transmission Cancellation Finished + */ + +#define FDCAN_TXCOMMON_INTS (FDCAN_INT_TC | FDCAN_INT_TCF) +#define FDCAN_TXFIFOQ_INTS (FDCAN_INT_TFE | FDCAN_TXCOMMON_INTS) +#define FDCAN_TXEVFIFO_INTS (FDCAN_INT_TEFN | FDCAN_INT_TEFF | \ + FDCAN_INT_TEFL) + +#define FDCAN_TXERR_INTS (FDCAN_INT_TEFL | FDCAN_INT_PEA | FDCAN_INT_PED) + +/* Common-, TX- and RX-Error-Mask */ + +#define FDCAN_ANYERR_INTS (FDCAN_CMNERR_INTS | FDCAN_RXERR_INTS | FDCAN_TXERR_INTS) + +/* Convenience macro for clearing all interrupts */ + +#define FDCAN_INT_ALL 0x3fcfffff + +/* Debug ********************************************************************/ + +/* Debug configurations that may be enabled just for testing FDCAN */ + +#ifndef CONFIG_DEBUG_NET_INFO +# undef CONFIG_STM32F0L0G0_FDCAN_REGDEBUG +#endif + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/* CAN frame format */ + +enum stm32_frameformat_e +{ + FDCAN_ISO11898_1_FORMAT = 0, /* Frame format according to ISO11898-1 */ + FDCAN_NONISO_BOSCH_V1_FORMAT = 1 /* Frame format according to Bosch CAN FD V1.0 */ +}; + +/* CAN mode of operation */ + +enum stm32_canmode_e +{ + FDCAN_CLASSIC_MODE = 0, /* Classic CAN operation */ +#ifdef CONFIG_NET_CAN_CANFD + FDCAN_FD_MODE = 1, /* CAN FD operation */ + FDCAN_FD_BRS_MODE = 2 /* CAN FD operation with bit rate switching */ +#endif +}; + +/* CAN driver state */ + +enum can_state_s +{ + FDCAN_STATE_UNINIT = 0, /* Not yet initialized */ + FDCAN_STATE_RESET, /* Initialized, reset state */ + FDCAN_STATE_SETUP, /* fdcan_setup() has been called */ + FDCAN_STATE_DISABLED /* Disabled by a fdcan_shutdown() */ +}; + +/* This structure describes the FDCAN message RAM layout */ + +struct stm32_msgram_s +{ + volatile uint32_t *stdfilters; /* Standard filters */ + volatile uint32_t *extfilters; /* Extended filters */ + volatile uint32_t *rxfifo0; /* RX FIFO0 */ + volatile uint32_t *rxfifo1; /* RX FIFO1 */ + volatile uint32_t *txeventfifo; /* TX event FIFO */ + volatile uint32_t *txfifoq; /* TX FIFO queue */ +}; + +/* This structure provides the constant configuration of a FDCAN peripheral */ + +struct stm32_config_s +{ + uint32_t rxpinset; /* RX pin configuration */ + uint32_t txpinset; /* TX pin configuration */ + uintptr_t base; /* Base address of the FDCAN registers */ + uint32_t baud; /* Configured baud */ + uint32_t nbtp; /* Nominal bit timing/prescaler register setting */ + uint32_t dbtp; /* Data bit timing/prescaler register setting */ + uint8_t port; /* FDCAN port number (1 or 2) */ + uint8_t irq0; /* FDCAN peripheral IRQ number for interrupt line 0 */ + uint8_t irq1; /* FDCAN peripheral IRQ number for interrupt line 1 */ + uint8_t mode; /* See enum stm32_canmode_e */ + uint8_t format; /* See enum stm32_frameformat_e */ + uint8_t nstdfilters; /* Number of standard filters */ + uint8_t nextfilters; /* Number of extended filters */ + uint8_t nrxfifo0; /* Number of RX FIFO0 elements */ + uint8_t nrxfifo1; /* Number of RX FIFO1 elements */ + uint8_t ntxeventfifo; /* Number of TXevent FIFO elements */ + uint8_t ntxfifoq; /* Number of TX FIFO queue elements */ + uint8_t rxfifo0esize; /* RX FIFO0 element size (words) */ + uint8_t rxfifo1esize; /* RX FIFO1 element size (words) */ + uint8_t txeventesize; /* TXevent element size (words) */ + uint8_t txbufferesize; /* TX buffer element size (words) */ +#ifdef STM32_FDCAN_LOOPBACK + bool loopback; /* True: Loopback mode */ +#endif + + /* FDCAN message RAM layout */ + + struct stm32_msgram_s msgram; +}; + +/* This structure provides the current state of a FDCAN peripheral */ + +struct stm32_fdcan_s +{ + /* The constant configuration */ + + const struct stm32_config_s *config; + + uint8_t state; /* See enum can_state_s */ +#ifdef CONFIG_NET_CAN_EXTID + uint8_t nextalloc; /* Number of allocated extended filters */ +#endif + uint8_t nstdalloc; /* Number of allocated standard filters */ + uint32_t nbtp; /* Current nominal bit timing */ + uint32_t dbtp; /* Current data bit timing */ + +#ifdef CONFIG_NET_CAN_EXTID + uint32_t extfilters[2]; /* Extended filter bit allocator. 2*32=64 */ +#endif + uint32_t stdfilters[4]; /* Standard filter bit allocator. 4*32=128 */ + +#ifdef CONFIG_STM32F0L0G0_FDCAN_REGDEBUG + uintptr_t regaddr; /* Last register address read */ + uint32_t regval; /* Last value read from the register */ + unsigned int count; /* Number of times that the value was read */ +#endif + + bool bifup; /* true:ifup false:ifdown */ + struct net_driver_s dev; /* Interface understood by the network */ + + struct work_s irqwork; /* For deferring interrupt work to the wq */ + struct work_s pollwork; /* For deferring poll work to the work wq */ + + /* A pointers to the list of TX/RX descriptors */ + + struct can_frame *txdesc; + struct can_frame *rxdesc; + + /* TX/RX pool */ + +#ifdef CONFIG_NET_CAN_CANFD + uint8_t tx_pool[sizeof(struct canfd_frame)*POOL_SIZE]; + uint8_t rx_pool[sizeof(struct canfd_frame)*POOL_SIZE]; +#else + uint8_t tx_pool[sizeof(struct can_frame)*POOL_SIZE]; + uint8_t rx_pool[sizeof(struct can_frame)*POOL_SIZE]; +#endif +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +/* FDCAN Register access */ + +static uint32_t fdcan_getreg(struct stm32_fdcan_s *priv, int offset); +static void fdcan_putreg(struct stm32_fdcan_s *priv, int offset, + uint32_t regval); +#ifdef CONFIG_STM32F0L0G0_FDCAN_REGDEBUG +static void fdcan_dumpregs(struct stm32_fdcan_s *priv, + const char *msg); +static void fdcan_dumprxregs(struct stm32_fdcan_s *priv, + const char *msg); +static void fdcan_dumptxregs(struct stm32_fdcan_s *priv, + const char *msg); +static void fdcan_dumpramlayout(struct stm32_fdcan_s *priv); +#else +# define fdcan_dumpregs(priv,msg) +# define fdcan_dumprxregs(priv,msg) +# define fdcan_dumptxregs(priv,msg) +# define fdcan_dumpramlayout(priv) +#endif + +/* CAN interrupt enable functions */ + +static void fdcan_rx0int(struct stm32_fdcan_s *priv, bool enable); +static void fdcan_rx1int(struct stm32_fdcan_s *priv, bool enable); +static void fdcan_txint(struct stm32_fdcan_s *priv, bool enable); +#ifdef CONFIG_NET_CAN_ERRORS +static void fdcan_errint(struct stm32_fdcan_s *priv, bool enable); +#endif + +/* Common TX logic */ + +static int fdcan_send(struct stm32_fdcan_s *priv); +static bool fdcan_txready(struct stm32_fdcan_s *priv); +static int fdcan_txpoll(struct net_driver_s *dev); + +/* CAN RX interrupt handling */ + +static void fdcan_rx0interrupt_work(void *arg); +static void fdcan_rx1interrupt_work(void *arg); + +/* CAN TX interrupt handling */ + +static void fdcan_txdone_work(void *arg); +static void fdcan_txdone(struct stm32_fdcan_s *priv); + +#ifdef CONFIG_NET_CAN_ERRORS +/* CAN errors interrupt handling */ + +static void fdcan_error_work(void *arg); +#endif + +/* FDCAN interrupt handling */ + +#ifdef CONFIG_NET_CAN_ERRORS +static void fdcan_error(struct stm32_fdcan_s *priv, uint32_t status); +#endif +static void fdcan_receive(struct stm32_fdcan_s *priv, + volatile uint32_t *rxbuffer, + unsigned long nwords); +static int fdcan_interrupt(int irq, void *context, void *arg); + +/* Initialization */ + +static void fdcan_reset(struct stm32_fdcan_s *priv); +static int fdcan_setup(struct stm32_fdcan_s *priv); +static void fdcan_shutdown(struct stm32_fdcan_s *priv); + +/* FDCAN helpers */ + +#if 0 /* not used for now */ +static int +fdcan_start_busoff_recovery_sequence(struct stm32_fdcan_s *priv); +#endif + +/* Hardware initialization */ + +static int fdcan_hw_initialize(struct stm32_fdcan_s *priv); + +/* NuttX callback functions */ + +static int fdcan_ifup(struct net_driver_s *dev); +static int fdcan_ifdown(struct net_driver_s *dev); + +static void fdcan_txavail_work(void *arg); +static int fdcan_txavail(struct net_driver_s *dev); + +#ifdef CONFIG_NETDEV_IOCTL +static int fdcan_netdev_ioctl(struct net_driver_s *dev, int cmd, + unsigned long arg); +#endif + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +#ifdef CONFIG_STM32F0L0G0_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_STM32F0L0G0_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_STM32F0L0G0_FDCAN1_CLASSIC) + .mode = FDCAN_CLASSIC_MODE, +#elif defined(CONFIG_STM32F0L0G0_FDCAN1_FD) + .mode = FDCAN_FD_MODE, +#else + .mode = FDCAN_FD_BRS_MODE, +#endif +#if defined(CONFIG_STM32F0L0G0_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_STM32F0L0G0_FDCAN1_LOOPBACK + .loopback = true, +#endif + + /* FDCAN1 Message RAM */ + + .msgram = + { + (uint32_t *)(STM32_CANRAM1_BASE + (FDCAN1_STDFILTER_INDEX << 2)), + (uint32_t *)(STM32_CANRAM1_BASE + (FDCAN1_EXTFILTERS_INDEX << 2)), + (uint32_t *)(STM32_CANRAM1_BASE + (FDCAN1_RXFIFO0_INDEX << 2)), + (uint32_t *)(STM32_CANRAM1_BASE + (FDCAN1_RXFIFO1_INDEX << 2)), + (uint32_t *)(STM32_CANRAM1_BASE + (FDCAN1_TXEVENTFIFO_INDEX << 2)), + (uint32_t *)(STM32_CANRAM1_BASE + (FDCAN1_TXFIFOQ_INDEX << 2)) + } +}; + +/* FDCAN1 variable driver state */ + +static struct stm32_fdcan_s g_fdcan1priv; + +#endif /* CONFIG_STM32F0L0G0_FDCAN1 */ + +/**************************************************************************** + * 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_STM32F0L0G0_FDCAN_REGDEBUG +static uint32_t fdcan_getreg(struct stm32_fdcan_s *priv, int offset) +{ + const struct stm32_config_s *config = priv->config; + uintptr_t regaddr = 0; + uint32_t regval = 0; + + /* Read the value from the register */ + + regaddr = config->base + offset; + regval = getreg32(regaddr); + + /* Is this the same value that we read from the same register last time? + * Are we polling the register? If so, suppress some of the output. + */ + + if (regaddr == priv->regaddr && regval == priv->regval) + { + if (priv->count == 0xffffffff || ++priv->count > 3) + { + if (priv->count == 4) + { + ninfo("...\n"); + } + + return regval; + } + } + + /* No this is a new address or value */ + + else + { + /* Did we print "..." for the previous value? */ + + if (priv->count > 3) + { + /* Yes.. then show how many times the value repeated */ + + ninfo("[repeats %d more times]\n", priv->count - 3); + } + + /* Save the new address, value, and count */ + + priv->regaddr = regaddr; + priv->regval = regval; + priv->count = 1; + } + + /* Show the register value read */ + + ninfo("%08" PRIx32 "->%08" PRIx32 "\n", regaddr, regval); + return regval; +} + +#else +static uint32_t fdcan_getreg(struct stm32_fdcan_s *priv, int offset) +{ + 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_STM32F0L0G0_FDCAN_REGDEBUG +static void fdcan_putreg(struct stm32_fdcan_s *priv, int offset, + uint32_t regval) +{ + const struct stm32_config_s *config = priv->config; + uintptr_t regaddr = config->base + offset; + + /* Show the register value being written */ + + ninfo("%08" PRIx32 "->%08" PRIx32 "\n", regaddr, regval); + + /* Write the value */ + + putreg32(regval, regaddr); +} + +#else +static void fdcan_putreg(struct stm32_fdcan_s *priv, int offset, + uint32_t regval) +{ + 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_STM32F0L0G0_FDCAN_REGDEBUG +static void fdcan_dumpregs(struct stm32_fdcan_s *priv, + const char *msg) +{ + const struct stm32_config_s *config = priv->config; + + ninfo("CAN%d Control and Status Registers: %s\n", config->port, msg); + ninfo(" Base: %08" PRIx32 "\n", config->base); + + /* CAN control and status registers */ + + ninfo(" CCCR: %08" PRIx32 " TEST: %08" PRIx32 "\n", + getreg32(config->base + STM32_FDCAN_CCCR_OFFSET), + getreg32(config->base + STM32_FDCAN_TEST_OFFSET)); + + ninfo(" NBTP: %08" PRIx32 " DBTP: %08" PRIx32 "\n", + getreg32(config->base + STM32_FDCAN_NBTP_OFFSET), + getreg32(config->base + STM32_FDCAN_DBTP_OFFSET)); + + ninfo(" IE: %08" PRIx32 " TIE: %08" PRIx32 "\n", + getreg32(config->base + STM32_FDCAN_IE_OFFSET), + getreg32(config->base + STM32_FDCAN_TXBTIE_OFFSET)); + + ninfo(" ILE: %08" PRIx32 " ILS: %08" PRIx32 "\n", + getreg32(config->base + STM32_FDCAN_ILE_OFFSET), + getreg32(config->base + STM32_FDCAN_ILS_OFFSET)); + + ninfo(" TXBC: %08" PRIx32 "\n", + getreg32(config->base + STM32_FDCAN_TXBC_OFFSET)); +} +#endif + +/**************************************************************************** + * Name: fdcan_dumprxregs + * + * Description: + * Dump the contents of all Rx status registers + * + * Input Parameters: + * priv - A reference to the CAN block status + * + * Returned Value: + * None + * + ****************************************************************************/ + +#ifdef CONFIG_STM32F0L0G0_FDCAN_REGDEBUG +static void fdcan_dumprxregs(struct stm32_fdcan_s *priv, + const char *msg) +{ + const struct stm32_config_s *config = priv->config; + + ninfo("CAN%d Rx Registers: %s\n", config->port, msg); + ninfo(" Base: %08" PRIx32 "\n", config->base); + + ninfo(" PSR: %08" PRIx32 " ECR: %08" PRIx32 + " HPMS: %08" PRIx32 "\n", + getreg32(config->base + STM32_FDCAN_PSR_OFFSET), + getreg32(config->base + STM32_FDCAN_ECR_OFFSET), + getreg32(config->base + STM32_FDCAN_HPMS_OFFSET)); + + ninfo(" RXF0S: %08" PRIx32 " RXF0A: %08" PRIx32 "\n", + getreg32(config->base + STM32_FDCAN_RXF0S_OFFSET), + getreg32(config->base + STM32_FDCAN_RXF0A_OFFSET)); + + ninfo(" RXF1S: %08" PRIx32 " RXF1A: %08" PRIx32 "\n", + getreg32(config->base + STM32_FDCAN_RXF1S_OFFSET), + getreg32(config->base + STM32_FDCAN_RXF1A_OFFSET)); + + ninfo(" IR: %08" PRIx32 " IE: %08" PRIx32 "\n", + getreg32(config->base + STM32_FDCAN_IR_OFFSET), + getreg32(config->base + STM32_FDCAN_IE_OFFSET)); +} +#endif + +/**************************************************************************** + * Name: fdcan_dumptxregs + * + * Description: + * Dump the contents of all Tx buffer registers + * + * Input Parameters: + * priv - A reference to the CAN block status + * + * Returned Value: + * None + * + ****************************************************************************/ + +#ifdef CONFIG_STM32F0L0G0_FDCAN_REGDEBUG +static void fdcan_dumptxregs(struct stm32_fdcan_s *priv, + const char *msg) +{ + const struct stm32_config_s *config = priv->config; + + ninfo("CAN%d Tx Registers: %s\n", config->port, msg); + ninfo(" Base: %08" PRIx32 "\n", config->base); + + ninfo(" PSR: %08" PRIx32 " ECR: %08" PRIx32 "\n", + getreg32(config->base + STM32_FDCAN_PSR_OFFSET), + getreg32(config->base + STM32_FDCAN_ECR_OFFSET)); + + ninfo(" TXQFS: %08" PRIx32 " TXBAR: %08" PRIx32 + " TXBRP: %08" PRIx32 "\n", + getreg32(config->base + STM32_FDCAN_TXFQS_OFFSET), + getreg32(config->base + STM32_FDCAN_TXBAR_OFFSET), + getreg32(config->base + STM32_FDCAN_TXBRP_OFFSET)); + + ninfo(" TXBTO: %08" PRIx32 " TXBCR: %08" PRIx32 "\n", + getreg32(config->base + STM32_FDCAN_TXBTO_OFFSET), + getreg32(config->base + STM32_FDCAN_TXBCR_OFFSET)); + + ninfo(" TXEFS: %08" PRIx32 " TXEFA: %08" PRIx32 "\n", + getreg32(config->base + STM32_FDCAN_TXEFS_OFFSET), + getreg32(config->base + STM32_FDCAN_TXEFA_OFFSET)); + + ninfo(" IR: %08" PRIx32 " IE: %08" PRIx32 + " TIE: %08" PRIx32 "\n", + getreg32(config->base + STM32_FDCAN_IR_OFFSET), + getreg32(config->base + STM32_FDCAN_IE_OFFSET), + getreg32(config->base + STM32_FDCAN_TXBTIE_OFFSET)); +} +#endif + +/**************************************************************************** + * Name: fdcan_dumpramlayout + * + * Description: + * Print the layout of the message RAM + * + * Input Parameters: + * priv - A reference to the CAN block status + * + * Returned Value: + * None + * + ****************************************************************************/ + +#ifdef CONFIG_STM32F0L0G0_FDCAN_REGDEBUG +static void fdcan_dumpramlayout(struct stm32_fdcan_s *priv) +{ + const struct stm32_config_s *config = priv->config; + + ninfo(" ******* FDCAN%d Message RAM layout *******\n", config->port); + ninfo(" Start # Elmnt Elmnt size (words)\n"); + + if (config->nstdfilters > 0) + { + ninfo("STD filters %p %4d %2d\n", + config->msgram.stdfilters, + config->nstdfilters, + 1); + } + + if (config->nextfilters > 0) + { + ninfo("EXT filters %p %4d %2d\n", + config->msgram.extfilters, + config->nextfilters, + 2); + } + + if (config->nrxfifo0 > 0) + { + ninfo("RX FIFO 0 %p %4d %2d\n", + config->msgram.rxfifo0, + config->nrxfifo0, + config->rxfifo0esize); + } + + if (config->nrxfifo1 > 0) + { + ninfo("RX FIFO 1 %p %4d %2d\n", + config->msgram.rxfifo1, + config->nrxfifo1, + config->rxfifo1esize); + } + + if (config->ntxeventfifo > 0) + { + ninfo("TX EVENT %p %4d %2d\n", + config->msgram.txeventfifo, + config->ntxeventfifo, + config->txeventesize); + } + + if (config->ntxfifoq > 0) + { + ninfo("TX FIFO %p %4d %2d\n", + config->msgram.txfifoq, + config->ntxfifoq, + config->txbufferesize); + } +} +#endif + +/**************************************************************************** + * Name: fdcan_start_busoff_recovery_sequence + * + * Description: + * This function initiates the BUS-OFF recovery sequence. + * CAN Specification Rev. 2.0 or ISO11898-1:2015. + * According the STM32G4 datasheet section 44.3.2 Software initialziation. + * + * Input Parameters: + * priv - An instance of the FDCAN driver state structure. + * + * Returned Value: + * Zero (OK) is returned on success. Otherwise a negated errno value is + * returned to indicate the nature of the error. + * + ****************************************************************************/ + +#if 0 /* not used for now */ +static int +fdcan_start_busoff_recovery_sequence(struct stm32_fdcan_s *priv) +{ + uint32_t regval = 0; + + DEBUGASSERT(priv); + + /* Only start BUS-OFF recovery if we are in BUS-OFF state */ + + regval = fdcan_getreg(priv, STM32_FDCAN_PSR_OFFSET); + if ((regval & FDCAN_PSR_BO) == 0) + { + return -EPERM; + } + + /* Disable initialization mode to issue the recovery sequence */ + + regval = fdcan_getreg(priv, STM32_FDCAN_CCCR_OFFSET); + regval &= ~FDCAN_CCCR_INIT; + fdcan_putreg(priv, STM32_FDCAN_CCCR_OFFSET, regval); + + return OK; +} +#endif + +/**************************************************************************** + * Name: fdcan_reset + * + * Description: + * Reset the FDCAN device. Called early to initialize the hardware. This + * function is called, before fdcan_setup() and on error conditions. + * + * Input Parameters: + * dev - An instance of the "upper half" can driver state structure. + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void fdcan_reset(struct stm32_fdcan_s *priv) +{ + const struct stm32_config_s *config = NULL; + uint32_t regval = 0; + irqstate_t flags; + + DEBUGASSERT(priv); + config = priv->config; + DEBUGASSERT(config); + + ninfo("FDCAN%d\n", config->port); + UNUSED(config); + + /* Disable all interrupts */ + + fdcan_putreg(priv, STM32_FDCAN_IE_OFFSET, 0); + fdcan_putreg(priv, STM32_FDCAN_TXBTIE_OFFSET, 0); + + /* Make sure that all buffers are released. + * + * REVISIT: What if a thread is waiting for a buffer? The following + * will not wake up any waiting threads. + */ + + /* Disable the FDCAN controller. + * REVISIT: Should fdcan_shutdown() be called here? + */ + + /* Reset the FD CAN. + * REVISIT: Since there is only a single reset for both FDCAN + * controllers, do we really want to use the RCC reset here? + * This will nuke operation of the second controller if another + * device is registered. + */ + + flags = enter_critical_section(); + regval = getreg32(STM32_RCC_APB1RSTR); + regval |= RCC_APB1RSTR_FDCANRST; + putreg32(regval, STM32_RCC_APB1RSTR); + + regval &= ~RCC_APB1RSTR_FDCANRST; + putreg32(regval, STM32_RCC_APB1RSTR); + 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(struct stm32_fdcan_s *priv) +{ + const struct stm32_config_s *config = NULL; + int ret = 0; + + DEBUGASSERT(priv); + config = priv->config; + DEBUGASSERT(config); + + ninfo("FDCAN%d\n", config->port); + + /* FDCAN hardware initialization */ + + ret = fdcan_hw_initialize(priv); + if (ret < 0) + { + nerr("ERROR: FDCAN%d H/W initialization failed: %d\n", + config->port, ret); + return ret; + } + + fdcan_dumpregs(priv, "After hardware initialization"); + + /* Attach the FDCAN interrupt handlers */ + + ret = irq_attach(config->irq0, fdcan_interrupt, priv); + if (ret < 0) + { + nerr("ERROR: Failed to attach FDCAN%d line 0 IRQ (%d)", + config->port, config->irq0); + return ret; + } + + ret = irq_attach(config->irq1, fdcan_interrupt, priv); + if (ret < 0) + { + nerr("ERROR: Failed to attach FDCAN%d line 1 IRQ (%d)", + config->port, config->irq1); + return ret; + } + + priv->state = FDCAN_STATE_SETUP; + + /* Enable the interrupts at the NVIC (they are still disabled at the FDCAN + * peripheral). + */ + + up_enable_irq(config->irq0); + up_enable_irq(config->irq1); + + return OK; +} + +/**************************************************************************** + * Name: fdcan_shutdown + * + * Description: + * Disable the FDCAN. This method is called when the FDCAN device + * is closed. This method reverses the operation the setup method. + * + * Input Parameters: + * dev - An instance of the "upper half" can driver state structure. + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void fdcan_shutdown(struct stm32_fdcan_s *priv) +{ + const struct stm32_config_s *config = NULL; + uint32_t regval = 0; + + DEBUGASSERT(priv); + config = priv->config; + DEBUGASSERT(config); + + ninfo("FDCAN%d\n", config->port); + + /* Disable FDCAN interrupts at the NVIC */ + + up_disable_irq(config->irq0); + up_disable_irq(config->irq1); + + /* Disable all interrupts from the FDCAN peripheral */ + + fdcan_putreg(priv, STM32_FDCAN_IE_OFFSET, 0); + fdcan_putreg(priv, STM32_FDCAN_TXBTIE_OFFSET, 0); + + /* Detach the FDCAN interrupt handler */ + + irq_detach(config->irq0); + irq_detach(config->irq1); + + /* Disable device by setting the Clock Stop Request bit */ + + regval = fdcan_getreg(priv, STM32_FDCAN_CCCR_OFFSET); + regval |= FDCAN_CCCR_CSR; + fdcan_putreg(priv, STM32_FDCAN_CCCR_OFFSET, regval); + + /* Wait for Init and Clock Stop Acknowledge bits to verify + * device is in the powered down state + */ + + while ((fdcan_getreg(priv, STM32_FDCAN_CCCR_OFFSET) & FDCAN_CCCR_INIT) + == 0); + while ((fdcan_getreg(priv, STM32_FDCAN_CCCR_OFFSET) & FDCAN_CCCR_CSA) + == 0); + priv->state = FDCAN_STATE_DISABLED; +} + +/**************************************************************************** + * Name: fdcan_rx0int + * + * Description: + * Call to enable or disable RX0 interrupts. + * + * Input Parameters: + * priv - reference to the private CAN driver state structure + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void fdcan_rx0int(struct stm32_fdcan_s *priv, bool enable) +{ + const struct stm32_config_s *config = NULL; + uint32_t regval = 0; + + DEBUGASSERT(priv); + config = priv->config; + DEBUGASSERT(config); + + ninfo("CAN%" PRIu8 "RX0 enable: %d\n", config->port, enable); + + /* Enable/disable the FIFO 0 message pending interrupt */ + + regval = fdcan_getreg(priv, STM32_FDCAN_IE_OFFSET); + + if (enable) + { + regval |= FDCAN_RXFIFO0_INTS; + } + else + { + regval &= ~FDCAN_RXFIFO0_INTS; + } + + fdcan_putreg(priv, STM32_FDCAN_IE_OFFSET, regval); +} + +/**************************************************************************** + * Name: fdcan_rx1int + * + * Description: + * Call to enable or disable RX1 interrupts. + * + * Input Parameters: + * priv - reference to the private CAN driver state structure + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void fdcan_rx1int(struct stm32_fdcan_s *priv, bool enable) +{ + const struct stm32_config_s *config = NULL; + uint32_t regval = 0; + + DEBUGASSERT(priv); + config = priv->config; + DEBUGASSERT(config); + + ninfo("CAN%" PRIu8 "RX1 enable: %d\n", config->port, enable); + + /* Enable/disable the FIFO 1 message pending interrupt */ + + regval = fdcan_getreg(priv, STM32_FDCAN_IE_OFFSET); + + if (enable) + { + regval |= FDCAN_RXFIFO1_INTS; + } + else + { + regval &= ~FDCAN_RXFIFO1_INTS; + } + + fdcan_putreg(priv, STM32_FDCAN_IE_OFFSET, regval); +} + +/**************************************************************************** + * Name: fdcan_txint + * + * Description: + * Call to enable or disable TX interrupts. + * + * Input Parameters: + * dev - An instance of the "upper half" can driver state structure. + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void fdcan_txint(struct stm32_fdcan_s *priv, bool enable) +{ + const struct stm32_config_s *config = NULL; + uint32_t regval = 0; + + DEBUGASSERT(priv); + config = priv->config; + DEBUGASSERT(config); + + ninfo("CAN%" PRIu8 "TX enable: %d\n", config->port, enable); + + /* Enable/disable the receive interrupts */ + + regval = fdcan_getreg(priv, STM32_FDCAN_IE_OFFSET); + + if (enable) + { + regval |= FDCAN_TXFIFOQ_INTS; + } + else + { + regval &= ~FDCAN_TXFIFOQ_INTS; + } + + fdcan_putreg(priv, STM32_FDCAN_IE_OFFSET, regval); +} + +#ifdef CONFIG_NET_CAN_ERRORS +/**************************************************************************** + * Name: fdcan_txint + * + * Description: + * Call to enable or disable CAN SCE interrupts. + * + * Input Parameters: + * priv - reference to the private CAN driver state structure + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void fdcan_errint(struct stm32_fdcan_s *priv, bool enable) +{ + const struct stm32_config_s *config = NULL; + uint32_t regval = 0; + + DEBUGASSERT(priv); + config = priv->config; + DEBUGASSERT(config); + + ninfo("CAN%" PRIu8 "ERR enable: %d\n", config->port, enable); + + /* Enable/disable the transmit mailbox interrupt */ + + regval = fdcan_getreg(priv, STM32_FDCAN_IE_OFFSET); + if (enable) + { + regval |= FDCAN_ANYERR_INTS; + } + else + { + regval &= ~FDCAN_ANYERR_INTS; + } + + fdcan_putreg(priv, STM32_FDCAN_IE_OFFSET, regval); +} +#endif + +/**************************************************************************** + * Name: fdcan_send + * + * Description: + * Send one can message. + * + * One CAN-message consists of a maximum of 10 bytes. A message is + * composed of at least the first 2 bytes (when there are no data bytes). + * + * Byte 0: Bits 0-7: Bits 3-10 of the 11-bit CAN identifier + * Byte 1: Bits 5-7: Bits 0-2 of the 11-bit CAN identifier + * Bit 4: Remote Transmission Request (RTR) + * Bits 0-3: Data Length Code (DLC) + * Bytes 2-10: CAN data + * + * Input Parameters: + * dev - An instance of the "upper half" can driver state structure. + * + * Returned Value: + * Zero on success; a negated errno on failure + * + ****************************************************************************/ + +static int fdcan_send(struct stm32_fdcan_s *priv) +{ + const struct stm32_config_s *config = NULL; + volatile uint32_t *txbuffer = NULL; + const uint8_t *src = NULL; + uint32_t *dest = NULL; + uint32_t regval = 0; + unsigned int ndx = 0; + unsigned int nbytes = 0; + uint32_t wordbuffer = 0; + unsigned int i = 0; + + DEBUGASSERT(priv); + config = priv->config; + DEBUGASSERT(config); + + 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. + * Error state indicator (ESI) - ESI bit in CAN FD + * + * Format word T1: + * Data Length Code (DLC) - Value from message structure + * Bit Rate Switch (BRS) - Bit rate switching for CAN FD + * FD format (FDF) - Frame transmitted in CAN FD format + * Event FIFO Control (EFC) - Do not store events. + * Message Marker (MM) - Always zero + */ + + txbuffer[0] = 0; + txbuffer[1] = 0; + + /* CAN 2.0 or CAN FD */ + + if (priv->dev.d_len == sizeof(struct can_frame)) + { + struct can_frame *frame = NULL; + + frame = (struct can_frame *)priv->dev.d_buf; + + ninfo("CAN%" PRIu8 " 2.0 ID: %" PRIu32 " DLC: %" PRIu8 "\n", + config->port, (uint32_t)frame->can_id, frame->can_dlc); + + /* Extended or standard ID */ + +#ifdef CONFIG_NET_CAN_EXTID + if ((frame->can_id & CAN_EFF_FLAG) != 0) + { + DEBUGASSERT((frame->can_id ^ CAN_EFF_FLAG) < (1 << 29)); + + txbuffer[0] |= BUFFER_R0_EXTID(frame->can_id) | BUFFER_R0_XTD; + } + else +#endif + { + DEBUGASSERT(frame->can_id < (1 << 11)); + + txbuffer[0] |= BUFFER_R0_STDID(frame->can_id); + } + + /* Set DLC */ + + txbuffer[1] |= BUFFER_R1_DLC(frame->can_dlc); + + /* Set flags */ + + if ((frame->can_id & CAN_RTR_FLAG) != 0) + { + txbuffer[0] |= BUFFER_R0_RTR; + } + + /* Reset CAN FD bits */ + + txbuffer[0] &= ~BUFFER_R0_ESI; + txbuffer[1] &= ~BUFFER_R1_FDF; + txbuffer[1] &= ~BUFFER_R1_BRS; + + /* Followed by the amount of data corresponding to the DLC (T2..) */ + + src = frame->data; + nbytes = frame->can_dlc; + } +#ifdef CONFIG_NET_CAN_CANFD + else /* CAN FD frame */ + { + struct canfd_frame *frame = (struct canfd_frame *)priv->dev.d_buf; + + frame = (struct canfd_frame *)priv->dev.d_buf; + + ninfo("CAN%" PRIu8 " FD ID: %" PRIu32 " len: %" PRIu8 "\n", + config->port, (uint32_t)frame->can_id, frame->len); + + /* Extended or standard ID */ + +#ifdef CONFIG_NET_CAN_EXTID + if ((frame->can_id & CAN_EFF_FLAG) != 0) + { + DEBUGASSERT(frame->can_id < (1 << 29)); + + txbuffer[0] |= BUFFER_R0_EXTID(frame->can_id) | BUFFER_R0_XTD; + } + else +#endif + { + DEBUGASSERT(frame->can_id < (1 << 11)); + + txbuffer[0] |= BUFFER_R0_STDID(frame->can_id); + } + + /* CANFD frame */ + + txbuffer[1] |= BUFFER_R1_FDF; + + /* Set DLC */ + + txbuffer[1] |= BUFFER_R1_DLC(g_len_to_can_dlc[frame->len]); + + /* Set flags */ + + if ((frame->can_id & CAN_RTR_FLAG) != 0) + { + txbuffer[0] |= BUFFER_R0_RTR; + } + + if ((frame->flags & CANFD_BRS) != 0) + { + txbuffer[1] |= BUFFER_R1_BRS; + } + + if ((frame->flags & CANFD_ESI) != 0) + { + txbuffer[0] |= BUFFER_R0_ESI; + } + + /* Followed by the amount of data corresponding to the DLC (T2..) */ + + src = frame->data; + nbytes = frame->len; + } +#endif + + dest = (uint32_t *)&txbuffer[2]; + + /* 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(struct stm32_fdcan_s *priv) +{ + uint32_t regval = 0; + bool notfull = false; + + /* Return the state of the TX FIFOQ. Return TRUE if the TX FIFO/Queue is + * not full. + */ + + regval = fdcan_getreg(priv, STM32_FDCAN_TXFQS_OFFSET); + notfull = ((regval & FDCAN_TXFQS_TFQF) == 0); + + return notfull; +} + +/**************************************************************************** + * Name: fdcan_rx0interrupt_work + * + * Description: + * CAN RX FIFO 0 worker + * + ****************************************************************************/ + +static void fdcan_rx0interrupt_work(void *arg) +{ + struct stm32_fdcan_s *priv = (struct stm32_fdcan_s *)arg; + const struct stm32_config_s *config = NULL; + uint32_t regval = 0; + unsigned int nelem = 0; + unsigned int ndx = 0; + + DEBUGASSERT(priv); + config = priv->config; + DEBUGASSERT(config); + + /* Clear the RX FIFO0 new message interrupt */ + + fdcan_putreg(priv, STM32_FDCAN_IR_OFFSET, FDCAN_INT_RF0N); + + regval = fdcan_getreg(priv, STM32_FDCAN_RXF0S_OFFSET); + nelem = (regval & FDCAN_RXFS_FFL_MASK) >> FDCAN_RXFS_FFL_SHIFT; + if (nelem > 0) + { + /* Handle the newly received message in FIFO0 */ + + ndx = (regval & FDCAN_RXFS_FGI_MASK) >> FDCAN_RXFS_FGI_SHIFT; + + if ((regval & FDCAN_RXFS_RFL) != 0) + { + nerr("ERROR: Message lost: %08" PRIx32 "\n", regval); + } + else + { + fdcan_receive(priv, + config->msgram.rxfifo0 + + (ndx * priv->config->rxfifo0esize), + priv->config->rxfifo0esize); + +#ifdef CONFIG_NET_CAN_ERRORS + /* Turning back on all configured RX error interrupts */ + + regval = fdcan_getreg(priv, STM32_FDCAN_IE_OFFSET); + regval |= FDCAN_RXERR_INTS; + fdcan_putreg(priv, STM32_FDCAN_IE_OFFSET, regval); +#endif + } + + /* Acknowledge reading the FIFO entry */ + + fdcan_putreg(priv, STM32_FDCAN_RXF0A_OFFSET, ndx); + } + + /* Re-enable CAN RX interrupts */ + + fdcan_rx0int(priv, true); +} + +/**************************************************************************** + * Name: fdcan_rx1interrupt_work + * + * Description: + * CAN RX FIFO 1 worker + * + ****************************************************************************/ + +static void fdcan_rx1interrupt_work(void *arg) +{ + struct stm32_fdcan_s *priv = (struct stm32_fdcan_s *)arg; + const struct stm32_config_s *config = NULL; + uint32_t regval = 0; + unsigned int nelem = 0; + unsigned int ndx = 0; + + DEBUGASSERT(priv); + config = priv->config; + DEBUGASSERT(config); + + /* Clear the RX FIFO1 new message interrupt */ + + fdcan_putreg(priv, STM32_FDCAN_IR_OFFSET, FDCAN_INT_RF1N); + + /* Check if there is anything in RX FIFO1 */ + + regval = fdcan_getreg(priv, STM32_FDCAN_RXF1S_OFFSET); + nelem = (regval & FDCAN_RXFS_FFL_MASK) >> FDCAN_RXFS_FFL_SHIFT; + if (nelem == 0) + { + /* Clear the RX FIFO1 interrupt (and all other FIFO1-related + * interrupts) + */ + + /* Handle the newly received message in FIFO1 */ + + ndx = (regval & FDCAN_RXFS_FGI_MASK) >> FDCAN_RXFS_FGI_SHIFT; + + if ((regval & FDCAN_RXFS_RFL) != 0) + { + nerr("ERROR: Message lost: %08" PRIx32 "\n", regval); + } + else + { + fdcan_receive(priv, + config->msgram.rxfifo1 + + (ndx * priv->config->rxfifo1esize), + priv->config->rxfifo1esize); + +#ifdef CONFIG_NET_CAN_ERRORS + /* Turning back on all configured RX error interrupts */ + + regval = fdcan_getreg(priv, STM32_FDCAN_IE_OFFSET); + regval |= FDCAN_RXERR_INTS; + fdcan_putreg(priv, STM32_FDCAN_IE_OFFSET, regval); +#endif + } + + /* Acknowledge reading the FIFO entry */ + + fdcan_putreg(priv, STM32_FDCAN_RXF1A_OFFSET, ndx); + } + + /* Re-enable CAN RX interrupts */ + + fdcan_rx1int(priv, true); +} + +/**************************************************************************** + * Name: fdcan_txdone_work + ****************************************************************************/ + +static void fdcan_txdone_work(void *arg) +{ + struct stm32_fdcan_s *priv = (struct stm32_fdcan_s *)arg; + + fdcan_txdone(priv); + + /* There should be space for a new TX in any event. Poll the network for + * new XMIT data + */ + + net_lock(); + devif_poll(&priv->dev, fdcan_txpoll); + net_unlock(); +} + +/**************************************************************************** + * Name: fdcan_txdone + ****************************************************************************/ + +static void fdcan_txdone(struct stm32_fdcan_s *priv) +{ + const struct stm32_config_s *config = NULL; + unsigned int ndx = 0; + uint32_t regval = 0; + + DEBUGASSERT(priv); + config = priv->config; + DEBUGASSERT(config); + + /* Clear the pending TX completion interrupt (and all + * other TX-related interrupts) + */ + + fdcan_putreg(priv, STM32_FDCAN_IR_OFFSET, FDCAN_TXFIFOQ_INTS); + + /* Check all TX buffers */ + + regval = fdcan_getreg(priv, STM32_FDCAN_TXBTO_OFFSET); + for (ndx = 0; ndx < config->ntxfifoq; ndx++) + { + if ((regval & (1 << ndx)) != 0) + { + /* Tell the upper half that the transfer is finished. */ + + NETDEV_TXDONE(&priv->dev); + } + } + +#ifdef CONFIG_NET_CAN_ERRORS + /* Turning back on PEA and PED error interrupts */ + + regval = fdcan_getreg(priv, STM32_FDCAN_IE_OFFSET); + regval |= (FDCAN_INT_PEA | FDCAN_INT_PED); + fdcan_putreg(priv, STM32_FDCAN_IE_OFFSET, regval); +#endif + + /* Re-enable TX interrupts */ + + fdcan_txint(priv, true); +} + +#ifdef CONFIG_NET_CAN_ERRORS +/**************************************************************************** + * Name: fdcan_error_work + ****************************************************************************/ + +static void fdcan_error_work(void *arg) +{ + struct stm32_fdcan_s *priv = (struct stm32_fdcan_s *)arg; + uint32_t pending = 0; + uint32_t ir = 0; + uint32_t ie = 0; + uint32_t psr = 0; + + /* Get the set of pending interrupts. */ + + ir = fdcan_getreg(priv, STM32_FDCAN_IR_OFFSET); + ie = fdcan_getreg(priv, STM32_FDCAN_IE_OFFSET); + + pending = (ir & ie); + + /* Check for common errors */ + + if ((pending & FDCAN_CMNERR_INTS) != 0) + { + /* When a protocol error occurs, the problem is recorded in + * the LEC/DLEC fields of the PSR register. In lieu of + * separate interrupt flags for each error, the hardware + * groups protocol errors under a single interrupt each for + * arbitration and data phases. + * + * These errors have a tendency to flood the system with + * interrupts, so they are disabled here until we get a + * successful transfer/receive on the hardware + */ + + psr = fdcan_getreg(priv, STM32_FDCAN_PSR_OFFSET); + + if ((psr & FDCAN_PSR_LEC_MASK) != 0) + { + ie &= ~(FDCAN_INT_PEA | FDCAN_INT_PED); + fdcan_putreg(priv, STM32_FDCAN_IE_OFFSET, ie); + } + + /* Clear the error indications */ + + fdcan_putreg(priv, STM32_FDCAN_IR_OFFSET, FDCAN_CMNERR_INTS); + } + + /* Check for transmission errors */ + + if ((pending & FDCAN_TXERR_INTS) != 0) + { + /* An Acknowledge-Error will occur if for example the device + * is not connected to the bus. + * + * The CAN-Standard states that the Chip has to retry the + * message forever, which will produce an ACKE every time. + * To prevent this Interrupt-Flooding and the high CPU-Load + * we disable the ACKE here as long we didn't transfer at + * least one message successfully (see FDCAN_INT_TC below). + */ + + /* Clear the error indications */ + + fdcan_putreg(priv, STM32_FDCAN_IR_OFFSET, FDCAN_TXERR_INTS); + } + + /* Check for reception errors */ + + if ((pending & FDCAN_RXERR_INTS) != 0) + { + /* To prevent Interrupt-Flooding the current active + * RX error interrupts are disabled. After successfully + * receiving at least one CAN packet all RX error interrupts + * are turned back on. + * + * The Interrupt-Flooding can for example occur if the + * configured CAN speed does not match the speed of the other + * CAN nodes in the network. + */ + + ie &= ~(pending & FDCAN_RXERR_INTS); + fdcan_putreg(priv, STM32_FDCAN_IE_OFFSET, ie); + + /* Clear the error indications */ + + fdcan_putreg(priv, STM32_FDCAN_IR_OFFSET, FDCAN_RXERR_INTS); + } + + /* Report errors */ + + net_lock(); + fdcan_error(priv, pending & FDCAN_ANYERR_INTS); + net_unlock(); + + /* Re-enable ERROR interrupts */ + + fdcan_errint(priv, true); +} + +/**************************************************************************** + * Name: fdcan_error + * + * Description: + * Report a CAN error + * + * Input Parameters: + * dev - CAN-common state data + * status - Interrupt status with error bits set + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void fdcan_error(struct stm32_fdcan_s *priv, uint32_t status) +{ + struct can_frame *frame = (struct can_frame *)priv->rxdesc; + uint32_t psr = 0; + uint16_t errbits = 0; + uint8_t data[CAN_ERR_DLC]; + + DEBUGASSERT(priv != NULL); + + /* Encode error bits */ + + errbits = 0; + memset(data, 0, sizeof(data)); + + /* Always fill in "static" error conditions, but set the signaling bit + * only if the condition has changed (see IRQ-Flags below) + * They have to be filled in every time CAN_ERROR_CONTROLLER is set. + */ + + psr = fdcan_getreg(priv, STM32_FDCAN_PSR_OFFSET); + if ((psr & FDCAN_PSR_EP) != 0) + { + data[1] |= (CAN_ERR_CRTL_RX_PASSIVE | CAN_ERR_CRTL_TX_PASSIVE); + } + + if ((psr & FDCAN_PSR_EW) != 0) + { + data[1] |= (CAN_ERR_CRTL_RX_WARNING | CAN_ERR_CRTL_TX_WARNING); + } + + if ((status & (FDCAN_INT_EP | FDCAN_INT_EW)) != 0) + { + /* "Error Passive" or "Error Warning" status changed */ + + errbits |= CAN_ERR_CRTL; + } + + if ((status & FDCAN_INT_PEA) != 0) + { + /* Protocol Error in Arbitration Phase */ + + if ((psr & FDCAN_PSR_LEC_MASK) != 0) + { + /* Error code present */ + + if ((psr & FDCAN_PSR_LEC(FDCAN_PSR_EC_STUFF_ERROR)) != 0) + { + /* Stuff Error */ + + errbits |= CAN_ERR_PROT; + data[2] |= CAN_ERR_PROT_STUFF; + } + + if ((psr & FDCAN_PSR_LEC(FDCAN_PSR_EC_FORM_ERROR)) != 0) + { + /* Format Error */ + + errbits |= CAN_ERR_PROT; + data[2] |= CAN_ERR_PROT_FORM; + } + + if ((psr & FDCAN_PSR_LEC(FDCAN_PSR_EC_ACK_ERROR)) != 0) + { + /* Acknowledge Error */ + + errbits |= CAN_ERR_ACK; + } + + if ((psr & FDCAN_PSR_LEC(FDCAN_PSR_EC_BIT0_ERROR)) != 0) + { + /* Bit0 Error */ + + errbits |= CAN_ERR_PROT; + data[2] |= CAN_ERR_PROT_BIT0; + } + + if ((psr & FDCAN_PSR_LEC(FDCAN_PSR_EC_BIT1_ERROR)) != 0) + { + /* Bit1 Error */ + + errbits |= CAN_ERR_PROT; + data[2] |= CAN_ERR_PROT_BIT1; + } + + if ((psr & FDCAN_PSR_LEC(FDCAN_PSR_EC_CRC_ERROR)) != 0) + { + /* Receive CRC Error */ + + errbits |= CAN_ERR_PROT; + data[3] |= (CAN_ERR_PROT_LOC_CRC_SEQ | + CAN_ERR_PROT_LOC_CRC_DEL); + } + + if ((psr & FDCAN_PSR_LEC(FDCAN_PSR_EC_NO_CHANGE)) != 0) + { + /* No Change in Error */ + + errbits |= CAN_ERR_PROT; + data[2] |= CAN_ERR_PROT_UNSPEC; + } + } + } + + if ((status & FDCAN_INT_PED) != 0) + { + /* Protocol Error in Data Phase */ + + if ((psr & FDCAN_PSR_DLEC_MASK) != 0) + { + /* Error code present */ + + if ((psr & FDCAN_PSR_DLEC(FDCAN_PSR_EC_STUFF_ERROR)) != 0) + { + /* Stuff Error */ + + errbits |= CAN_ERR_PROT; + data[2] |= CAN_ERR_PROT_STUFF; + } + + if ((psr & FDCAN_PSR_DLEC(FDCAN_PSR_EC_FORM_ERROR)) != 0) + { + /* Format Error */ + + errbits |= CAN_ERR_PROT; + data[2] |= CAN_ERR_PROT_FORM; + } + + if ((psr & FDCAN_PSR_DLEC(FDCAN_PSR_EC_ACK_ERROR)) != 0) + { + /* Acknowledge Error */ + + errbits |= CAN_ERR_ACK; + } + + if ((psr & FDCAN_PSR_DLEC(FDCAN_PSR_EC_BIT0_ERROR)) != 0) + { + /* Bit0 Error */ + + errbits |= CAN_ERR_PROT; + data[2] |= CAN_ERR_PROT_BIT0; + } + + if ((psr & FDCAN_PSR_DLEC(FDCAN_PSR_EC_BIT1_ERROR)) != 0) + { + /* Bit1 Error */ + + errbits |= CAN_ERR_PROT; + data[2] |= CAN_ERR_PROT_BIT1; + } + + if ((psr & FDCAN_PSR_DLEC(FDCAN_PSR_EC_CRC_ERROR)) != 0) + { + /* Receive CRC Error */ + + errbits |= CAN_ERR_PROT; + data[3] |= (CAN_ERR_PROT_LOC_CRC_SEQ | + CAN_ERR_PROT_LOC_CRC_DEL); + } + + if ((psr & FDCAN_PSR_DLEC(FDCAN_PSR_EC_NO_CHANGE)) != 0) + { + /* No Change in Error */ + + errbits |= CAN_ERR_PROT; + data[2] |= CAN_ERR_PROT_UNSPEC; + } + } + } + + if ((status & FDCAN_INT_BO) != 0) + { + /* Bus_Off Status changed */ + + if ((psr & FDCAN_PSR_BO) != 0) + { + errbits |= CAN_ERR_BUSOFF; + } + else + { + errbits |= CAN_ERR_RESTARTED; + } + } + + if ((status & (FDCAN_INT_RF0L | FDCAN_INT_RF1L)) != 0) + { + /* Receive FIFO 0/1 Message Lost + * Receive FIFO 1 Message Lost + */ + + errbits |= CAN_ERR_CRTL; + data[1] |= CAN_ERR_CRTL_RX_OVERFLOW; + } + + if ((status & FDCAN_INT_TEFL) != 0) + { + /* Tx Event FIFO Element Lost */ + + errbits |= CAN_ERR_CRTL; + data[1] |= CAN_ERR_CRTL_TX_OVERFLOW; + } + + if ((status & FDCAN_INT_TOO) != 0) + { + /* Timeout Occurred */ + + errbits |= CAN_ERR_TX_TIMEOUT; + } + + if ((status & (FDCAN_INT_MRAF | FDCAN_INT_ELO)) != 0) + { + /* Message RAM Access Failure + * Error Logging Overflow + */ + + errbits |= CAN_ERR_CRTL; + data[1] |= CAN_ERR_CRTL_UNSPEC; + } + + if (errbits != 0) + { + nerr("ERROR: errbits = %08" PRIx16 "\n", errbits); + + /* Copy frame */ + + frame->can_id = errbits; + frame->can_dlc = CAN_ERR_DLC; + + memcpy(frame->data, data, CAN_ERR_DLC); + + /* Copy the buffer pointer to priv->dev.. Set amount of data + * in priv->dev.d_len + */ + + priv->dev.d_len = sizeof(struct can_frame); + priv->dev.d_buf = (uint8_t *)frame; + + /* Send to socket interface */ + + NETDEV_ERRORS(&priv->dev); + + can_input(&priv->dev); + + /* Point the packet buffer back to the next Tx buffer that will be + * used during the next write. If the write queue is full, then + * this will point at an active buffer, which must not be written + * to. This is OK because devif_poll won't be called unless the + * queue is not full. + */ + + priv->dev.d_buf = (uint8_t *)priv->txdesc; + } +} +#endif /* CONFIG_NET_CAN_ERRORS */ + +/**************************************************************************** + * Name: fdcan_receive + * + * Description: + * Receive an FDCAN messages + * + * Input Parameters: + * dev - CAN-common state data + * rxbuffer - The RX buffer containing the received messages + * nwords - The length of the RX buffer (element size in words). + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void fdcan_receive(struct stm32_fdcan_s *priv, + volatile uint32_t *rxbuffer, + unsigned long nwords) +{ + fdcan_dumprxregs(dev->cd_priv, "Before receive"); + + /* CAN 2.0 or CAN FD */ + +#ifdef CONFIG_NET_CAN_CANFD + if ((rxbuffer[1] & BUFFER_R1_FDF) != 0) + { + struct canfd_frame *frame = (struct canfd_frame *)priv->rxdesc; + + /* Format the CAN FD header */ + + /* Extract the RTR bit */ + + if ((rxbuffer[0] & BUFFER_R0_RTR) != 0) + { + frame->can_id |= CAN_RTR_FLAG; + } + +#ifdef CONFIG_NET_CAN_EXTID + if ((rxbuffer[0] & BUFFER_R0_XTD) != 0) + { + /* Save the extended ID of the newly received message */ + + frame->can_id = ((rxbuffer[0] & BUFFER_R0_EXTID_MASK) >> + BUFFER_R0_EXTID_SHIFT); + frame->can_id |= CAN_EFF_FLAG; + } + else + { + frame->can_id = ((rxbuffer[0] & BUFFER_R0_STDID_MASK) >> + BUFFER_R0_STDID_SHIFT); + frame->can_id &= ~CAN_EFF_FLAG; + } +#else + if ((rxbuffer[0] & BUFFER_R0_XTD) != 0) + { + /* Drop any messages with extended IDs */ + + return; + } + + /* Save the standard ID of the newly received message */ + + frame->can_id = ((rxbuffer[0] & BUFFER_R0_STDID_MASK) >> + BUFFER_R0_STDID_SHIFT); +#endif + + /* Word R1 contains the DLC and timestamp */ + + frame->len = g_can_dlc_to_len[((rxbuffer[1] & BUFFER_R1_DLC_MASK) >> + BUFFER_R1_DLC_SHIFT)]; + + /* Get CANFD flags */ + + frame->flags = 0; + + if ((rxbuffer[0] & BUFFER_R0_ESI) != 0) + { + frame->flags |= CANFD_ESI; + } + + if ((rxbuffer[1] & BUFFER_R1_BRS) != 0) + { + frame->flags |= CANFD_BRS; + } + + /* Save the message data */ + + memcpy(frame->data, (void *)&rxbuffer[2], frame->len); + + /* Copy the buffer pointer to priv->dev.. Set amount of data + * in priv->dev.d_len + */ + + priv->dev.d_len = sizeof(struct canfd_frame); + priv->dev.d_buf = (uint8_t *)frame; + } + else +#endif + { + struct can_frame *frame = (struct can_frame *)priv->rxdesc; + + /* Format the CAN header */ + + /* Extract the RTR bit */ + + if ((rxbuffer[0] & BUFFER_R0_RTR) != 0) + { + frame->can_id |= CAN_RTR_FLAG; + } + +#ifdef CONFIG_NET_CAN_EXTID + if ((rxbuffer[0] & BUFFER_R0_XTD) != 0) + { + /* Save the extended ID of the newly received message */ + + frame->can_id = ((rxbuffer[0] & BUFFER_R0_EXTID_MASK) >> + BUFFER_R0_EXTID_SHIFT); + frame->can_id |= CAN_EFF_FLAG; + } + else + { + frame->can_id = ((rxbuffer[0] & BUFFER_R0_STDID_MASK) >> + BUFFER_R0_STDID_SHIFT); + frame->can_id &= ~CAN_EFF_FLAG; + } +#else + if ((rxbuffer[0] & BUFFER_R0_XTD) != 0) + { + /* Drop any messages with extended IDs */ + + return; + } + + /* Save the standard ID of the newly received message */ + + frame->can_id = ((rxbuffer[0] & BUFFER_R0_STDID_MASK) >> + BUFFER_R0_STDID_SHIFT); +#endif + + /* Word R1 contains the DLC and timestamp */ + + frame->can_dlc = ((rxbuffer[1] & BUFFER_R1_DLC_MASK) >> + BUFFER_R1_DLC_SHIFT); + + /* Save the message data */ + + memcpy(frame->data, (void *)&rxbuffer[2], frame->can_dlc); + + /* Copy the buffer pointer to priv->dev.. Set amount of data + * in priv->dev.d_len + */ + + priv->dev.d_len = sizeof(struct can_frame); + priv->dev.d_buf = (uint8_t *)frame; + } + + /* Send to socket interface */ + + NETDEV_RXPACKETS(&priv->dev); + + can_input(&priv->dev); + + /* Point the packet buffer back to the next Tx buffer that will be + * used during the next write. If the write queue is full, then + * this will point at an active buffer, which must not be written + * to. This is OK because devif_poll won't be called unless the + * queue is not full. + */ + + priv->dev.d_buf = (uint8_t *)priv->txdesc; +} + +/**************************************************************************** + * Name: fdcan_interrupt + * + * Description: + * Common FDCAN interrupt handler + * + * irq - The IRQ number of the interrupt. + * context - The register state save array at the time of the interrupt. + * + * Returned Value: + * Zero on success; a negated errno on failure + * + ****************************************************************************/ + +static int fdcan_interrupt(int irq, void *context, void *arg) +{ + struct stm32_fdcan_s *priv = (struct stm32_fdcan_s *)arg; + uint32_t pending = 0; + + DEBUGASSERT(priv != NULL); + + /* Get the set of pending interrupts. */ + + pending = fdcan_getreg(priv, STM32_FDCAN_IR_OFFSET); + +#ifdef CONFIG_NET_CAN_ERRORS + /* Check for any errors */ + + if ((pending & FDCAN_ANYERR_INTS) != 0) + { + /* Disable further CAN ERROR interrupts and schedule to perform the + * interrupt processing on the worker thread + */ + + fdcan_errint(priv, false); + work_queue(CANWORK, &priv->irqwork, fdcan_error_work, priv, 0); + } +#endif + + /* Check for successful completion of a transmission */ + + if ((pending & FDCAN_INT_TC) != 0) + { + /* Disable further TX CAN interrupts. here can be no race + * condition here. + */ + + fdcan_txint(priv, false); + work_queue(CANWORK, &priv->irqwork, fdcan_txdone_work, priv, 0); + } + else if ((pending & FDCAN_TXFIFOQ_INTS) != 0) + { + /* Clear unhandled TX events */ + + fdcan_putreg(priv, STM32_FDCAN_IR_OFFSET, FDCAN_TXFIFOQ_INTS); + } + + if (pending & FDCAN_INT_RF1N) + { + /* Disable further CAN RX interrupts and schedule to perform the + * interrupt processing on the worker thread + */ + + fdcan_rx1int(priv, false); + work_queue(CANWORK, &priv->irqwork, + fdcan_rx1interrupt_work, priv, 0); + } + + /* Clear the RX FIFO0 new message interrupt */ + + if (pending & FDCAN_INT_RF0N) + { + /* Disable further CAN RX interrupts and schedule to perform the + * interrupt processing on the worker thread + */ + + fdcan_rx0int(priv, false); + work_queue(CANWORK, &priv->irqwork, + fdcan_rx0interrupt_work, priv, 0); + } + + return OK; +} + +/**************************************************************************** + * Name: fdcan_hw_initialize + * + * Description: + * FDCAN hardware initialization + * + * Input Parameters: + * priv - A pointer to the private data structure for this FDCAN peripheral + * + * Returned Value: + * Zero on success; a negated errno value on failure. + * + ****************************************************************************/ + +static int fdcan_hw_initialize(struct stm32_fdcan_s *priv) +{ + const struct stm32_config_s *config = NULL; + volatile uint32_t *msgram = NULL; + uint32_t regval = 0; + uint32_t cntr = 0; + + DEBUGASSERT(priv); + config = priv->config; + DEBUGASSERT(config); + + ninfo("FDCAN%d\n", config->port); + + /* Clean message RAM */ + + msgram = config->msgram.stdfilters; + cntr = (FDCAN_MSGRAM_WORDS + 1); + while (cntr > 0) + { + *msgram++ = 0; + cntr--; + } + + /* Configure FDCAN pins */ + + stm32_configgpio(config->rxpinset); + stm32_configgpio(config->txpinset); + + /* Re-enable device if previously disabled in fdcan_shutdown() */ + + if (priv->state == FDCAN_STATE_DISABLED) + { + /* Reset Clock Stop Request bit */ + + regval = fdcan_getreg(priv, STM32_FDCAN_CCCR_OFFSET); + regval &= ~FDCAN_CCCR_CSR; + fdcan_putreg(priv, STM32_FDCAN_CCCR_OFFSET, regval); + + /* Wait for Clock Stop Acknowledge bit reset to indicate + * device is operational + */ + + while ((fdcan_getreg(priv, STM32_FDCAN_CCCR_OFFSET) & FDCAN_CCCR_CSA) + != 0); + } + + /* Enable the Initialization state */ + + regval = fdcan_getreg(priv, STM32_FDCAN_CCCR_OFFSET); + regval |= FDCAN_CCCR_INIT; + fdcan_putreg(priv, STM32_FDCAN_CCCR_OFFSET, regval); + + /* Wait for initialization mode to take effect */ + + while ((fdcan_getreg(priv, STM32_FDCAN_CCCR_OFFSET) & FDCAN_CCCR_INIT) + == 0); + + /* Enable writing to configuration registers */ + + regval = fdcan_getreg(priv, STM32_FDCAN_CCCR_OFFSET); + regval |= FDCAN_CCCR_CCE; + fdcan_putreg(priv, STM32_FDCAN_CCCR_OFFSET, regval); + + /* Global Filter Configuration: + * + * ANFS=0: Store all non matching standard frame in RX FIFO0 + * ANFE=0: Store all non matching extended frame in RX FIFO0 + */ + + regval = FDCAN_RXGFC_ANFE_RX_FIFO0 | FDCAN_RXGFC_ANFS_RX_FIFO0; + fdcan_putreg(priv, STM32_FDCAN_RXGFC_OFFSET, regval); + + /* Extended ID Filter AND mask */ + + fdcan_putreg(priv, STM32_FDCAN_XIDAM_OFFSET, 0x1fffffff); + + /* Disable all interrupts */ + + fdcan_putreg(priv, STM32_FDCAN_IE_OFFSET, 0); + fdcan_putreg(priv, STM32_FDCAN_TXBTIE_OFFSET, 0); + + /* All interrupts directed to Line 0. But disable both interrupt lines 0 + * and 1 for now. + * + * REVISIT: Only interrupt line 0 is used by this driver. + */ + + fdcan_putreg(priv, STM32_FDCAN_ILS_OFFSET, 0); + fdcan_putreg(priv, STM32_FDCAN_ILE_OFFSET, 0); + + /* Clear all pending interrupts. */ + + fdcan_putreg(priv, STM32_FDCAN_IR_OFFSET, FDCAN_INT_ALL); + + /* Configure FDCAN bit timing */ + + fdcan_putreg(priv, STM32_FDCAN_NBTP_OFFSET, priv->nbtp); + fdcan_putreg(priv, STM32_FDCAN_DBTP_OFFSET, priv->dbtp); + + /* Configure message RAM starting addresses and sizes. */ + + regval = FDCAN_RXGFC_LSS(config->nstdfilters); + regval |= FDCAN_RXGFC_LSE(config->nextfilters); + fdcan_putreg(priv, STM32_FDCAN_RXGFC_OFFSET, regval); + + /* Dump RAM layout */ + + fdcan_dumpramlayout(priv); + + /* Configure Message Filters */ + + /* Disable all standard filters */ + + msgram = config->msgram.stdfilters; + cntr = config->nstdfilters; + while (cntr > 0) + { + *msgram++ = STDFILTER_S0_SFEC_DISABLE; + cntr--; + } + + /* Disable all extended filters */ + + msgram = config->msgram.extfilters; + cntr = config->nextfilters; + while (cntr > 0) + { + *msgram = EXTFILTER_F0_EFEC_DISABLE; + msgram = msgram + 2; + cntr--; + } + + /* Input clock divider configuration */ + + regval = FDCANCLK_PDIV; + fdcan_putreg(priv, STM32_FDCAN_CKDIV_OFFSET, regval); + + /* CC control register */ + + regval = fdcan_getreg(priv, STM32_FDCAN_CCCR_OFFSET); + regval &= ~(FDCAN_CCCR_NISO | FDCAN_CCCR_FDOE | FDCAN_CCCR_BRSE); + + /* Select ISO11898-1 or Non ISO Bosch CAN FD Specification V1.0 */ + + switch (config->format) + { + case FDCAN_ISO11898_1_FORMAT: + { + break; + } + + case FDCAN_NONISO_BOSCH_V1_FORMAT: + { + regval |= FDCAN_CCCR_NISO; + break; + } + + default: + { + return -EINVAL; + } + } + + /* Select Classic CAN mode or FD mode with or without fast bit rate + * switching + */ + + switch (config->mode) + { + case FDCAN_CLASSIC_MODE: + { + break; + } + +#ifdef CONFIG_NET_CAN_CANFD + case FDCAN_FD_MODE: + { + regval |= FDCAN_CCCR_FDOE; + break; + } + + case FDCAN_FD_BRS_MODE: + { + regval |= (FDCAN_CCCR_FDOE | FDCAN_CCCR_BRSE); + break; + } +#endif + + default: + { + return -EINVAL; + } + } + + /* Set the initial CAN mode */ + + fdcan_putreg(priv, STM32_FDCAN_CCCR_OFFSET, regval); + + /* Enable FIFO/Queue mode */ + + regval = fdcan_getreg(priv, STM32_FDCAN_TXBC_OFFSET); +#ifdef CONFIG_STM32F0L0G0_FDCAN_QUEUE_MODE + regval |= FDCAN_TXBC_TFQM; +#else + regval &= ~FDCAN_TXBC_TFQM; +#endif + fdcan_putreg(priv, STM32_FDCAN_TXBC_OFFSET, regval); + +#ifdef STM32_FDCAN_LOOPBACK + /* Is loopback mode selected for this peripheral? */ + + if (config->loopback) + { + /* FDCAN_CCCR_TEST - Test mode enable + * FDCAN_CCCR_MON - Bus monitoring mode (for internal loopback) + * FDCAN_TEST_LBCK - Loopback mode + */ + + regval = fdcan_getreg(priv, STM32_FDCAN_CCCR_OFFSET); + regval |= (FDCAN_CCCR_TEST | FDCAN_CCCR_MON); + fdcan_putreg(priv, STM32_FDCAN_CCCR_OFFSET, regval); + + regval = fdcan_getreg(priv, STM32_FDCAN_TEST_OFFSET); + regval |= FDCAN_TEST_LBCK; + fdcan_putreg(priv, STM32_FDCAN_TEST_OFFSET, regval); + } +#endif + + /* Configure interrupt lines */ + + /* Direct all interrupts to Line 0. + * + * Bits in the ILS register correspond to each FDCAN interrupt; A bit + * set to '1' is directed to interrupt line 1; a bit cleared to '0' + * is directed interrupt line 0. + * + * REVISIT: Nothing is done here. Only interrupt line 0 is used by + * this driver and ILS was already cleared above. + */ + + /* Enable only interrupt line 0. */ + + fdcan_putreg(priv, STM32_FDCAN_ILE_OFFSET, FDCAN_ILE_EINT0); + + /* Disable initialization mode to enable normal operation */ + + regval = fdcan_getreg(priv, STM32_FDCAN_CCCR_OFFSET); + regval &= ~FDCAN_CCCR_INIT; + fdcan_putreg(priv, STM32_FDCAN_CCCR_OFFSET, regval); + + return OK; +} + +/**************************************************************************** + * Function: fdcan_ifup + * + * Description: + * NuttX Callback: Bring up the Ethernet interface when an IP address is + * provided + * + * Input Parameters: + * dev - Reference to the NuttX driver state structure + * + * Returned Value: + * None + * + * Assumptions: + * + ****************************************************************************/ + +static int fdcan_ifup(struct net_driver_s *dev) +{ + struct stm32_fdcan_s *priv = + (struct stm32_fdcan_s *)dev->d_private; + const struct stm32_config_s *config = NULL; + + DEBUGASSERT(priv); + config = priv->config; + DEBUGASSERT(config); + + /* Setup CAN */ + + fdcan_setup(priv); + + /* Enable interrupts */ + + fdcan_rx0int(priv, true); + fdcan_rx1int(priv, true); + fdcan_txint(priv, true); +#ifdef CONFIG_NET_CAN_ERRORS + fdcan_errint(priv, true); +#endif + + /* Enable the interrupts at the NVIC */ + + up_enable_irq(config->irq0); + up_enable_irq(config->irq1); + + priv->bifup = true; + + priv->txdesc = (struct can_frame *)priv->tx_pool; + priv->rxdesc = (struct can_frame *)priv->rx_pool; + + priv->dev.d_buf = (uint8_t *)priv->txdesc; + + return OK; +} + +/**************************************************************************** + * Function: fdcan_ifdown + * + * Description: + * NuttX Callback: Stop the interface. + * + * Input Parameters: + * dev - Reference to the NuttX driver state structure + * + * Returned Value: + * None + * + * Assumptions: + * + ****************************************************************************/ + +static int fdcan_ifdown(struct net_driver_s *dev) +{ + struct stm32_fdcan_s *priv = + (struct stm32_fdcan_s *)dev->d_private; + + /* Disable CAN interrupts */ + + fdcan_shutdown(priv); + + /* Reset CAN */ + + fdcan_reset(priv); + + return OK; +} + +/**************************************************************************** + * Function: fdcan_txpoll + * + * Description: + * The transmitter is available, check if the network has any outgoing + * packets ready to send. This is a callback from devif_poll(). + * devif_poll() may be called: + * + * 1. When the preceding TX packet send is complete, + * 2. When the preceding TX packet send timesout and the interface is reset + * 3. During normal TX polling + * + * Input Parameters: + * dev - Reference to the NuttX driver state structure + * + * Returned Value: + * OK on success; a negated errno on failure + * + * Assumptions: + * May or may not be called from an interrupt handler. In either case, + * global interrupts are disabled, either explicitly or indirectly through + * interrupt handling logic. + * + ****************************************************************************/ + +static int fdcan_txpoll(struct net_driver_s *dev) +{ + struct stm32_fdcan_s *priv = + (struct stm32_fdcan_s *)dev->d_private; + + /* If the polling resulted in data that should be sent out on the network, + * the field d_len is set to a value > 0. + */ + + if (priv->dev.d_len > 0) + { + fdcan_txdone(priv); + + /* Send the packet */ + + fdcan_send(priv); + + /* Check if there is room in the device to hold another packet. If + * not, return a non-zero value to terminate the poll. + */ + + if (fdcan_txready(priv) == false) + { + return -EBUSY; + } + } + + /* If zero is returned, the polling will continue until all connections + * have been examined. + */ + + return 0; +} + +/**************************************************************************** + * Function: fdcan_txavail_work + * + * Description: + * Perform an out-of-cycle poll on the worker thread. + * + * Input Parameters: + * arg - Reference to the NuttX driver state structure (cast to void*) + * + * Returned Value: + * None + * + * Assumptions: + * Called on the higher priority worker thread. + * + ****************************************************************************/ + +static void fdcan_txavail_work(void *arg) +{ + struct stm32_fdcan_s *priv = (struct stm32_fdcan_s *)arg; + + /* Ignore the notification if the interface is not yet up */ + + net_lock(); + if (priv->bifup) + { + /* Check if there is room in the hardware to hold another outgoing + * packet. + */ + + if (fdcan_txready(priv)) + { + /* No, there is space for another transfer. Poll the network for + * new XMIT data. + */ + + devif_poll(&priv->dev, fdcan_txpoll); + } + } + + net_unlock(); +} + +/**************************************************************************** + * Function: fdcan_txavail + * + * Description: + * Driver callback invoked when new TX data is available. This is a + * stimulus perform an out-of-cycle poll and, thereby, reduce the TX + * latency. + * + * Input Parameters: + * dev - Reference to the NuttX driver state structure + * + * Returned Value: + * None + * + * Assumptions: + * Called in normal user mode + * + ****************************************************************************/ + +static int fdcan_txavail(struct net_driver_s *dev) +{ + struct stm32_fdcan_s *priv = + (struct stm32_fdcan_s *)dev->d_private; + + /* Is our single work structure available? It may not be if there are + * pending interrupt actions and we will have to ignore the Tx + * availability action. + */ + + if (work_available(&priv->pollwork)) + { + /* Schedule to serialize the poll on the worker thread. */ + + fdcan_txavail_work(priv); + } + + return OK; +} + +/**************************************************************************** + * Function: fdcan_ioctl + * + * Description: + * PHY ioctl command handler + * + * Input Parameters: + * dev - Reference to the NuttX driver state structure + * cmd - ioctl command + * arg - Argument accompanying the command + * + * Returned Value: + * Zero (OK) on success; a negated errno value on failure. + * + * Assumptions: + * + ****************************************************************************/ + +#ifdef CONFIG_NETDEV_IOCTL +static int fdcan_netdev_ioctl(struct net_driver_s *dev, int cmd, + unsigned long arg); +{ + struct stm32_fdcan_s *priv = + (struct stm32_fdcan_s *)dev->d_private; + int ret = OK; + + DEBUGASSERT(priv); + + switch (cmd) + { + /* TODO */ + + default: + ret = -ENOTTY; + break; + } + + return ret; +} +#endif /* CONFIG_NETDEV_IOCTL */ + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: stm32_cansockinitialize + * + * Description: + * Initialize the selected FDCAN port as CAN socket interface + * + * Input Parameters: + * Port number (for hardware that has multiple FDCAN interfaces) + * + * Returned Value: + * OK on success; Negated errno on failure. + * + ****************************************************************************/ + +int stm32_fdcansockinitialize(int port) +{ + struct stm32_fdcan_s *priv = NULL; + const struct stm32_config_s *config = NULL; + int ret = OK; + + ninfo("FDCAN%d\n", port); + + /* Select FDCAN peripheral to be initialized */ + +#ifdef CONFIG_STM32F0L0G0_FDCAN1 + if (port == FDCAN1) + { + /* Select the FDCAN1 device structure */ + + priv = &g_fdcan1priv; + config = &g_fdcan1const; + } + else +#endif + { + nerr("ERROR: Unsupported port %d\n", port); + ret = -EINVAL; + goto errout; + } + + /* Perform one time data initialization */ + + memset(priv, 0, sizeof(struct stm32_fdcan_s)); + priv->config = config; + + /* Set the initial bit timing. This might change subsequently + * due to IOCTL command processing. + */ + + priv->nbtp = config->nbtp; + priv->dbtp = config->dbtp; + + /* Initialize the driver structure */ + + priv->dev.d_ifup = fdcan_ifup; + priv->dev.d_ifdown = fdcan_ifdown; + priv->dev.d_txavail = fdcan_txavail; +#ifdef CONFIG_NETDEV_IOCTL + priv->dev.d_ioctl = fdcan_netdev_ioctl; +#endif + priv->dev.d_private = priv; + + /* Put the interface in the down state. This usually amounts to resetting + * the device and/or calling fdcan_ifdown(). + */ + + ninfo("callbacks done\n"); + + fdcan_ifdown(&priv->dev); + + /* Register the device with the OS so that socket IOCTLs can be performed */ + + ret = netdev_register(&priv->dev, NET_LL_CAN); + +errout: + return ret; +} + +/**************************************************************************** + * Name: arm_netinitialize + * + * Description: + * Initialize the CAN device interfaces. If there is more than one device + * interface in the chip, then board-specific logic will have to provide + * this function to determine which, if any, CAN interfaces should be + * initialized. + * + ****************************************************************************/ + +#if !defined(CONFIG_NETDEV_LATEINIT) +void arm_netinitialize(void) +{ +#ifdef CONFIG_STM32F0L0G0_CAN1 + stm32_fdcansockinitialize(FDCAN1); +#endif +} +#endif diff --git a/arch/arm/src/stm32f0l0g0/stm32c0_rcc.c b/arch/arm/src/stm32f0l0g0/stm32c0_rcc.c index 3194b8ef68c..70019d7eebe 100644 --- a/arch/arm/src/stm32f0l0g0/stm32c0_rcc.c +++ b/arch/arm/src/stm32f0l0g0/stm32c0_rcc.c @@ -176,7 +176,7 @@ static inline void rcc_enableapb1(void) #endif #endif -#ifdef CONFIG_STM32F0L0G0_FDCAN +#ifdef CONFIG_STM32F0L0G0_FDCAN1 /* FDCAN1 clock enable */ regval |= RCC_APB1ENR_FDCANEN; @@ -475,6 +475,14 @@ static void stm32_stdclockconfig(void) /* Wait until the selected source is used as the system clock source */ while ((getreg32(STM32_RCC_CFGR) & RCC_CFGR_SWS_MASK) != STM32_SYSCLK_SWS); + +#ifdef CONFIG_STM32F0L0G0_FDCAN1 + /* Configure FDCAN1 clock source */ + + regval = getreg32(STM32_RCC_CCIPR1); + regval |= STM32_FDCAN1_SEL; + putreg32(regval, STM32_RCC_CCIPR1); +#endif } #endif