diff --git a/include/libopencm3/stm32/fdcan.h b/include/libopencm3/stm32/fdcan.h
new file mode 100644
index 00000000..61ac87d0
--- /dev/null
+++ b/include/libopencm3/stm32/fdcan.h
@@ -0,0 +1,892 @@
+/** @defgroup fdcan_defines FDCAN Defines
+
+@ingroup STM32G_defines
+
+@brief libopencm3 Defined Constants and Types for STM32 FD-CAN
+
+@author @htmlonly © @endhtmlonly 2021 Eduard Drusa
+
+LGPL License Terms @ref lgpl_license
+*/
+/*
+ * This file is part of the libopencm3 project.
+ *
+ * Copyright (C) 2021 Eduard Drusa
+ *
+ * This library is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library. If not, see .
+ */
+
+
+
+#ifndef LIBOPENCM3_FDCAN_H
+#define LIBOPENCM3_FDCAN_H
+
+#include
+#include
+
+/** @{ */
+
+/* FDCAN block base addresses. Used in functions to identify FDCAN block being manipulated. */
+
+/** @defgroup fdcan_block FDCAN block base addresses
+ * @{
+ */
+#define CAN1 FDCAN1_BASE
+#define CAN2 FDCAN2_BASE
+#define CAN3 FDCAN3_BASE
+/**@}*/
+
+
+/** @defgroup fdcan_fifo Named constants for FIFOs
+ * @{
+ */
+#define FDCAN_FIFO0 0
+#define FDCAN_FIFO1 1
+/**@}*/
+
+
+/** @defgroup FDCAN registers file in each FDCAN block. */
+
+#define FDCAN_CREL(can_base) MMIO32(can_base + 0x0000)
+#define FDCAN_ENDN(can_base) MMIO32(can_base + 0x0004)
+#define FDCAN_DBTP(can_base) MMIO32(can_base + 0x000C)
+#define FDCAN_TEST(can_base) MMIO32(can_base + 0x0010)
+#define FDCAN_RWD(can_base) MMIO32(can_base + 0x0014)
+#define FDCAN_CCCR(can_base) MMIO32(can_base + 0x0018)
+#define FDCAN_NBTP(can_base) MMIO32(can_base + 0x001C)
+#define FDCAN_TSCC(can_base) MMIO32(can_base + 0x0020)
+#define FDCAN_TSCV(can_base) MMIO32(can_base + 0x0024)
+#define FDCAN_TOCC(can_base) MMIO32(can_base + 0x0028)
+#define FDCAN_TOCV(can_base) MMIO32(can_base + 0x002C)
+#define FDCAN_ECR(can_base) MMIO32(can_base + 0x0040)
+#define FDCAN_PSR(can_base) MMIO32(can_base + 0x0044)
+#define FDCAN_TDCR(can_base) MMIO32(can_base + 0x0048)
+#define FDCAN_IR(can_base) MMIO32(can_base + 0x0050)
+#define FDCAN_IE(can_base) MMIO32(can_base + 0x0054)
+#define FDCAN_ILS(can_base) MMIO32(can_base + 0x0058)
+#define FDCAN_ILE(can_base) MMIO32(can_base + 0x005C)
+#define FDCAN_RXGFC(can_base) MMIO32(can_base + 0x0080)
+#define FDCAN_XIDAM(can_base) MMIO32(can_base + 0x0084)
+#define FDCAN_HPMS(can_base) MMIO32(can_base + 0x0088)
+
+/** Generic access to Rx FIFO status registers.
+ * @param can_base FDCAN block base address @ref fdcan_block
+ * @param fifo_id ID of FIFO, 0 or 1
+ */
+#define FDCAN_RXFIS(can_base, fifo_id) MMIO32(can_base + 0x0090 + (8 * fifo_id))
+#define FDCAN_RXF0S(can_base) FDCAN_RXFIS(can_base, 0)
+#define FDCAN_RXF1S(can_base) FDCAN_RXFIS(can_base, 1)
+
+/** Generic access to Rx FIFO acknowledge registers.
+ * @param can_base FDCAN block base address @ref fdcan_block
+ * @param fifo_id ID of FIFO, 0 or 1
+ */
+#define FDCAN_RXFIA(can_base, fifo_id) MMIO32(can_base + 0x0094 + (8 * fifo_id))
+#define FDCAN_RXF0A(can_base) FDCAN_RXFIA(can_base, 0)
+#define FDCAN_RXF1A(can_base) FDCAN_RXFIA(can_base, 1)
+
+#define FDCAN_TXBC(can_base) MMIO32(can_base + 0x00C0)
+#define FDCAN_TXFQS(can_base) MMIO32(can_base + 0x00C4)
+#define FDCAN_TXBRP(can_base) MMIO32(can_base + 0x00C8)
+#define FDCAN_TXBAR(can_base) MMIO32(can_base + 0x00CC)
+#define FDCAN_TXBCR(can_base) MMIO32(can_base + 0x00D0)
+#define FDCAN_TXBTO(can_base) MMIO32(can_base + 0x00D4)
+#define FDCAN_TXBCF(can_base) MMIO32(can_base + 0x00D8)
+#define FDCAN_TXBTIE(can_base) MMIO32(can_base + 0x00DC)
+#define FDCAN_TXBCIE(can_base) MMIO32(can_base + 0x00E0)
+#define FDCAN_TXEFS(can_base) MMIO32(can_base + 0x00E4)
+#define FDCAN_TXEFA(can_base) MMIO32(can_base + 0x00E8)
+#define FDCAN_CKDIV(can_base) MMIO32(can_base + 0x0100)
+
+/* DAY[7:0]: FDCAN core revision date */
+#define FDCAN_CREL_DAY_SHIFT 0
+#define FDCAN_CREL_DAY_MASK 0xFF
+
+/* MON[7:0]: FDCAN core revision month */
+#define FDCAN_CREL_MON_SHIFT 8
+#define FDCAN_CREL_MON_MASK 0xFF
+
+/* YEAR[3:0]: FDCAN core revision year */
+#define FDCAN_CREL_YEAR_SHIFT 16
+#define FDCAN_CREL_YEAR_MASK 0xF
+
+/* SUBSTEP[3:0]: FDCAN core release sub stepping */
+#define FDCAN_CREL_SUBSTEP_SHIFT 20
+#define FDCAN_CREL_SUBSTEP_MASK 0xF
+
+/* STEP[3:0]: FDCAN core release stepping */
+#define FDCAN_CREL_STEP_SHIFT 24
+#define FDCAN_CREL_STEP_MASK 0xF
+
+/* REL[3:0]: FDCAN core release number */
+#define FDCAN_CREL_REL_SHIFT 28
+#define FDCAN_CREL_REL_MASK 0xF
+
+
+/* DSJW[3:0]: Synchronization jump width */
+#define FDCAN_DBTP_DSJW_SHIFT 0
+#define FDCAN_DBTP_DSJW_MASK 0xF
+
+/* DTSEG2[3:0]: Data time segment after sample point */
+#define FDCAN_DBTP_DTSEG2_SHIFT 4
+#define FDCAN_DBTP_DTSEG2_MASK 0xF
+
+/* DTSEG1[4:0]: Data time segment before sample point */
+#define FDCAN_DBTP_DTSEG1_SHIFT 8
+#define FDCAN_DBTP_DTSEG1_MASK 0x1F
+
+/* DBRP[4:0]: Data bit rate prescaler */
+#define FDCAN_DBTP_DBRP_SHIFT 16
+#define FDCAN_DBTP_DBRP_MASK 0x1F
+
+#define FDCAN_DBTP_TDC (1 << 23)
+
+#define FDCAN_TEST_LBCK (1 << 4)
+/* TX[1:0]: Control of transmit pin */
+#define FDCAN_TEST_TX_SHIFT 5
+#define FDCAN_TEST_TX_MASK 0x3
+
+#define FDCAN_TEST_RX (1 << 7)
+
+/* WDC[7:0]: RAM watchdog configuration */
+#define FDCAN_RWD_WDC_SHIFT 0
+#define FDCAN_RWD_WDC_MASK 0xFF
+
+/* WDV[7:0]: RAM watchdog actual value */
+#define FDCAN_RWD_WDV_SHIFT 7
+#define FDCAN_RWD_WDV_MASK 0xFF
+
+/** @defgroup fdcan_cccr FDCAN CC control register bits
+ * @{
+ */
+#define FDCAN_CCCR_INIT (1 << 0)
+#define FDCAN_CCCR_CCE (1 << 1)
+#define FDCAN_CCCR_ASM (1 << 2)
+#define FDCAN_CCCR_CSA (1 << 3)
+#define FDCAN_CCCR_CSR (1 << 4)
+#define FDCAN_CCCR_MON (1 << 5)
+#define FDCAN_CCCR_DAR (1 << 6)
+#define FDCAN_CCCR_TEST (1 << 7)
+#define FDCAN_CCCR_FDOE (1 << 8)
+#define FDCAN_CCCR_BRSE (1 << 9)
+#define FDCAN_CCCR_PXHD (1 << 12)
+#define FDCAN_CCCR_EFBI (1 << 13)
+#define FDCAN_CCCR_TXP (1 << 14)
+#define FDCAN_CCCR_NISO (1 << 15)
+/**@}*/
+
+/** Timeout for FDCAN_CCCR register INIT bit to accept set value.
+ *
+ * This timeout is required because FDCAN uses two different clocks
+ * feeding two different portions of block. There can be slight delay
+ * based on how clocks are set up. While amount of FDCAN_clk /
+ * FDCAN_pclk combinations is high and clock speeds may vary a lot,
+ * following value has been choosen as sane default. You are free to
+ * use any timeout value you want.
+ */
+#define FDCAN_CCCR_INIT_TIMEOUT 0x0000FFFF
+
+/* NTSEG2[6:0]: Nominal timing segment after sample point length */
+#define FDCAN_NBTP_NTSEG2_SHIFT 0
+#define FDCAN_NBTP_NTSEG2_MASK 0x7F
+
+/* NTSEG1[7:0]: Nominal timing segment before sample point length */
+#define FDCAN_NBTP_NTSEG1_SHIFT 8
+#define FDCAN_NBTP_NTSEG1_MASK 0xFF
+
+/* NBRP[8:0]: Norminal timing bit rate prescaler */
+#define FDCAN_NBTP_NBRP_SHIFT 16
+#define FDCAN_NBTP_NBRP_MASK 0x1FF
+
+/* NSJW[6:0]: Norminal timing resynchronization jumb width*/
+#define FDCAN_NBTP_NSJW_SHIFT 25
+#define FDCAN_NBTP_NSJW_MASK 0x7F
+
+/* TSS[1:0]: Timestamp select */
+#define FDCAN_TSCC_TSS_SHIFT 0
+#define FDCAN_TSCC_TSS_MASK 0x3
+
+/* TCP[3:0]: Timestamp counter prescaler */
+#define FDCAN_TSCC_TCP_SHIFT 16
+#define FDCAN_TSCC_TCP_MASK 0xF
+
+
+/* TSC[15:0]: Timestamp counter value */
+#define FDCAN_TSCV_TSC_SHIFT 0
+#define FDCAN_TSCV_TSC_MASK 0xFFFF
+
+#define FDCAN_TOCC_ETOC (1 << 0)
+/* TOS[1:0]: Timeout select */
+#define FDCAN_TOCC_TOS_SHIFT 1
+#define FDCAN_TOCC_TOS_MASK 0x3
+
+/* TOP[15:0]: Timeout period */
+#define FDCAN_TOCC_TOP_SHIFT 16
+#define FDCAN_TOCC_TOP_MASK 0xFFFF
+
+/* TOC[15:0]: Timeout counter */
+#define FDCAN_TOCV_TOC_SHIFT 0
+#define FDCAN_TOCV_TOC_MASK 0xFFFF
+
+/* TEC[7:0]: Transmit error counter */
+#define FDCAN_ECR_TEC_SHIFT 0
+#define FDCAN_ECR_TEC_MASK 0xFF
+
+/* REC[6:0]: Receive error counter */
+#define FDCAN_ECR_REC_SHIFT 8
+#define FDCAN_ECR_REC_MASK 0x7F
+
+#define FDCAN_ECR_RP (1 << 15)
+/* CEL[7:0]: CAN error logging */
+#define FDCAN_ECR_CEL_SHIFT 16
+#define FDCAN_ECR_CEL_MASK 0xFF
+
+
+/* LEC[2:0]: Last error code */
+#define FDCAN_PSR_LEC_SHIFT 0
+#define FDCAN_PSR_LEC_MASK 0x7
+
+/* ACT[1:0]: CAN block activity */
+#define FDCAN_PSR_ACT_SHIFT 3
+#define FDCAN_PSR_ACT_MASK 0x3
+
+#define FDCAN_PSR_EP (1 << 5)
+#define FDCAN_PSR_EW (1 << 6)
+#define FDCAN_PSR_BO (1 << 7)
+/* DLEC[2:0]: Last error code in data section */
+#define FDCAN_PSR_DLEC_SHIFT 8
+#define FDCAN_PSR_DLEC_MASK 0x7
+
+#define FDCAN_PSR_RESI (1 << 11)
+
+/* the what? */
+#define FDCAN_PSR_RBRSRESI1 (1 << 12)
+#define FDCAN_PSR_REDL (1 << 13)
+#define FDCAN_PSR_PXE (1 << 14)
+
+/* TDCV[6:0]: Transmitter delay compensation value */
+#define FDCAN_PSR_TDCV_SHIFT 16
+#define FDCAN_PSR_TDCV_MASK 0x7F
+
+/* TDCF[6:0]: Transmitter delay compensation filter window length */
+#define FDCAN_TDCR_TDCF_SHIFT 0
+#define FDCAN_TDCR_TDCF_MASK 0x7F
+
+/* TDCO[6:0]: Transmitter delay compensation offset */
+#define FDCAN_TDCR_TDCO_SHIFT 8
+#define FDCAN_TDCR_TDCO_MASK 0x7F
+
+/** @defgroup fdcan_ir FDCAN interrupt register flags
+ * @{
+ */
+#define FDCAN_IR_RF0N (1 << 0)
+#define FDCAN_IR_RF0F (1 << 1)
+#define FDCAN_IR_RF0L (1 << 2)
+#define FDCAN_IR_RF1N (1 << 3)
+#define FDCAN_IR_RF1F (1 << 4)
+#define FDCAN_IR_RF1L (1 << 5)
+#define FDCAN_IR_HPM (1 << 6)
+#define FDCAN_IR_TC (1 << 7)
+#define FDCAN_IR_TCF (1 << 8)
+#define FDCAN_IR_TFE (1 << 9)
+#define FDCAN_IR_TEFN (1 << 10)
+#define FDCAN_IR_TEFF (1 << 11)
+#define FDCAN_IR_TEFL (1 << 12)
+#define FDCAN_IR_TSW (1 << 13)
+#define FDCAN_IR_MRAF (1 << 14)
+#define FDCAN_IR_TOO (1 << 15)
+#define FDCAN_IR_ELO (1 << 16)
+#define FDCAN_IR_EP (1 << 17)
+#define FDCAN_IR_EW (1 << 18)
+#define FDCAN_IR_BO (1 << 19)
+#define FDCAN_IR_WDI (1 << 20)
+#define FDCAN_IR_PEA (1 << 21)
+#define FDCAN_IR_PED (1 << 22)
+#define FDCAN_IR_ARA (1 << 23)
+/**@}*/
+
+/** @defgroup fdcan_ie FDCAN interrupt enable flags
+ * @{
+ */
+#define FDCAN_IE_RF0NE (1 << 0)
+#define FDCAN_IE_RF0FE (1 << 1)
+#define FDCAN_IE_RF0LE (1 << 2)
+#define FDCAN_IE_RF1NE (1 << 3)
+#define FDCAN_IE_RF1FE (1 << 4)
+#define FDCAN_IE_RF1LE (1 << 5)
+#define FDCAN_IE_HPME (1 << 6)
+#define FDCAN_IE_TCE (1 << 7)
+#define FDCAN_IE_TCFE (1 << 8)
+#define FDCAN_IE_TFEE (1 << 9)
+#define FDCAN_IE_TEFNE (1 << 10)
+#define FDCAN_IE_TEFFE (1 << 11)
+#define FDCAN_IE_TEFLE (1 << 12)
+#define FDCAN_IE_TSWE (1 << 13)
+#define FDCAN_IE_MRAFE (1 << 14)
+#define FDCAN_IE_TOOE (1 << 15)
+#define FDCAN_IE_ELOE (1 << 16)
+#define FDCAN_IE_EPE (1 << 17)
+#define FDCAN_IE_EWE (1 << 18)
+#define FDCAN_IE_BOE (1 << 19)
+#define FDCAN_IE_WDIE (1 << 20)
+#define FDCAN_IE_PEAE (1 << 21)
+#define FDCAN_IE_PEDE (1 << 22)
+#define FDCAN_IE_ARAE (1 << 23)
+/**@}*/
+
+/** @defgroup fdcan_ils FDCAN_ILS interrupt line select flags
+ * @{
+ */
+#define FDCAN_ILS_RxFIFO0 (1 << 0)
+#define FDCAN_ILS_RxFIFO1 (1 << 1)
+#define FDCAN_ILS_SMSG (1 << 2)
+#define FDCAN_ILS_TFERR (1 << 3)
+#define FDCAN_ILS_MISC (1 << 4)
+#define FDCAN_ILS_BERR (1 << 5)
+#define FDCAN_ILS_PERR (1 << 6)
+/**@}*/
+
+#define FDCAN_ILE_INT0 (1 << 0)
+#define FDCAN_ILE_INT1 (1 << 1)
+
+#define FDCAN_RXGFC_RRFE (1 << 0)
+#define FDCAN_RXGFC_RRFS (1 << 1)
+/* ANFE[1:0]: Accept non-matching frames w/ extended ID */
+#define FDCAN_RXGFC_ANFE_SHIFT 2
+#define FDCAN_RXGFC_ANFE_MASK 0x3
+
+/* ANFS[1:0]: Accept non-matching frames w/ standard ID */
+#define FDCAN_RXGFC_ANFS_SHIFT 4
+#define FDCAN_RXGFC_ANFS_MASK 0x3
+
+#define FDCAN_RXGFC_F1OM (1 << 8)
+#define FDCAN_RXGFC_F0OM (1 << 9)
+/* LSS[4:0]: List size of standard ID filters */
+#define FDCAN_RXGFC_LSS_SHIFT 16
+#define FDCAN_RXGFC_LSS_MASK 0x1F
+
+/* LSE[3:0]: List size of extended ID filters */
+#define FDCAN_RXGFC_LSE_SHIFT 24
+#define FDCAN_RXGFC_LSE_MASK 0xF
+
+
+/* EIDM[28:0]: Extended ID mask for filtering */
+#define FDCAN_XIDAM_EIDM_SHIFT 0
+#define FDCAN_XIDAM_EIDM_MASK 0x1FFFFFFF
+
+
+/* BIDX[2:0]: Buffer index */
+#define FDCAN_HPMS_BIDX_SHIFT 0
+#define FDCAN_HPMS_BIDX_MASK 0x7
+
+/* MSI[1:0]: Message storage indicator */
+#define FDCAN_HPMS_MSI_SHIFT 6
+#define FDCAN_HPMS_MSI_MASK 0x3
+
+/* FIDX[4:0]: Filter index */
+#define FDCAN_HPMS_FIDX_SHIFT 8
+#define FDCAN_HPMS_FIDX_MASK 0x1F
+
+#define FDCAN_HPMS_FLS (1 << 15)
+
+/* Fill level of Rx FIFOs */
+#define FDCAN_RXFIFO_FL_SHIFT 0
+#define FDCAN_RXFIFO_FL_MASK 0xF
+
+/* Get index of Rx FIFOs */
+#define FDCAN_RXFIFO_GI_SHIFT 8
+#define FDCAN_RXFIFO_GI_MASK 0x3
+
+/* Put index of Rx FIFOs */
+#define FDCAN_RXFIFO_PI_SHIFT 16
+#define FDCAN_RXFIFO_PI_MASK 0x3
+
+#define FDCAN_RXFIFO_FF (1 << 24)
+#define FDCAN_RXFIFO_RFL (1 << 25)
+
+/* F0FL[3:0]: Fill level of Rx FIFO 0 */
+#define FDCAN_RXF0S_F0FL_SHIFT FDCAN_RXFIFO_FL_SHIFT
+#define FDCAN_RXF0S_F0FL_MASK FDCAN_RXFIFO_FL_MASK
+
+/* F0GI[1:0]: Get index of Rx FIFO 0 */
+#define FDCAN_RXF0S_F0GI_SHIFT FDCAN_RXFIFO_GI_SHIFT
+#define FDCAN_RXF0S_F0GI_MASK FDCAN_RXFIFO_GI_MASK
+
+/* F0PI[1:0]: Put index of Rx FIFO 0 */
+#define FDCAN_RXF0S_F0PI_SHIFT FDCAN_RXFIFO_PI_SHIFT
+#define FDCAN_RXF0S_F0PI_MASK FDCAN_RXFIFO_PI_MASK
+
+#define FDCAN_RXF0S_F0F FDCAN_RXFIFO_FF
+#define FDCAN_RXF0S_RF0L FDCAN_RXFIFO_RFL
+
+/* Rx FIFOs acknowledge index */
+#define FDCAN_RXFIFO_AI_SHIFT 0
+#define FDCAN_RXFIFO_AI_MASK 0x7
+
+/* R0AI[2:0]: Rx FIFO 0 acknowledge index */
+#define FDCAN_RXF0A_R0AI_SHIFT FDCAN_RXFIFO_AI_SHIFT
+#define FDCAN_RXF0A_R0AI_MASK FDCAN_RXFIFO_AI_MASK
+
+/* F1FL[3:1]: Fill level of Rx FIFO 1 */
+#define FDCAN_RXF1S_F1FL_SHIFT FDCAN_RXFIFO_FL_SHIFT
+#define FDCAN_RXF1S_F1FL_MASK FDCAN_RXFIFO_FL_MASK
+
+/* F1GI[1:1]: Get index of Rx FIFO 1 */
+#define FDCAN_RXF1S_F1GI_SHIFT FDCAN_RXFIFO_GI_SHIFT
+#define FDCAN_RXF1S_F1GI_MASK FDCAN_RXFIFO_GI_MASK
+
+/* F1PI[1:1]: Put index of Rx FIFO 1 */
+#define FDCAN_RXF1S_F1PI_SHIFT FDCAN_RXFIFO_PI_SHIFT
+#define FDCAN_RXF1S_F1PI_MASK FDCAN_RXFIFO_PI_MASK
+
+#define FDCAN_RXF1S_F1F FDCAN_RXFIFO_FF
+#define FDCAN_RXF1S_RF1L FDCAN_RXFIFO_RFL
+
+/* R1AI[2:0]: Rx FIFO 1 acknowledge index */
+#define FDCAN_RXF1A_R1AI_SHIFT FDCAN_RXFIFO_AI_SHIFT
+#define FDCAN_RXF1A_R1AI_MASK FDCAN_RXFIFO_AI_MASK
+
+#define FDCAN_TXBC_TFQM (1 << 24)
+
+/* TFFL[2:0]: Tx FIFO free level */
+#define FDCAN_TXFQS_TFFL_SHIFT 0
+#define FDCAN_TXFQS_TFFL_MASK 0x7
+
+/* TFGI[1:0]: Tx FIFO get index */
+#define FDCAN_TXFQS_TFGI_SHIFT 0
+#define FDCAN_TXFQS_TFGI_MASK 0x3
+
+/* TFQPI[1:0]: Tx FIFO put index */
+#define FDCAN_TXFQS_TFQPI_SHIFT 0
+#define FDCAN_TXFQS_TFQPI_MASK 0x3
+
+#define FDCAN_TXFQS_TFQF (1 << 0)
+
+/** @defgroup fdcan_txbrp FDCAN_TXBRP Transmit request pending bits
+ * @{
+ */
+#define FDCAN_TXBRP_TRP0 (1 << 0)
+#define FDCAN_TXBRP_TRP1 (1 << 1)
+#define FDCAN_TXBRP_TRP2 (1 << 2)
+/**@}*/
+
+/** @defgroup fdcan_txbar FDCAN_TXBAR Transmit buffer add request bits
+ * @{
+ */
+#define FDCAN_TXBAR_AR0 (1 << 0)
+#define FDCAN_TXBAR_AR1 (1 << 1)
+#define FDCAN_TXBAR_AR2 (1 << 2)
+/**@}*/
+
+/** @defgroup fdcan_txbcr FDCAN_TXBCR Transmit buffer cancel request bits
+ * @{
+ */
+#define FDCAN_TXBCR_CR0 (1 << 0)
+#define FDCAN_TXBCR_CR1 (1 << 1)
+#define FDCAN_TXBCR_CR2 (1 << 2)
+/**@}*/
+
+/** @defgroup fdcan_txbto FDCAN_TXBTO Transmit buffer transfer occured bits
+ * @{
+ */
+#define FDCAN_TXBTO_TO0 (1 << 0)
+#define FDCAN_TXBTO_TO1 (1 << 1)
+#define FDCAN_TXBTO_TO2 (1 << 2)
+/**@}*/
+
+/** @defgroup fdcan_txbcf FDCAN_TXBCF Transmit buffer cancellation finished bits
+ * @{
+ */
+#define FDCAN_TXBCF_CF0 (1 << 0)
+#define FDCAN_TXBCF_CF1 (1 << 1)
+#define FDCAN_TXBCF_CF2 (1 << 2)
+/**@}*/
+
+/** @defgroup fdcan_txbtie FDCAN_TXBTIE Transmit interrupt enable bits
+ *
+ * Each bit enables or disables transmit interrupt for transmit buffer
+ * slot.
+ * @{
+ */
+#define FDCAN_TXBTIE_TIE0 (1 << 0)
+#define FDCAN_TXBTIE_TIE1 (1 << 1)
+#define FDCAN_TXBTIE_TIE2 (1 << 2)
+/**@}*/
+
+/** @defgroup fdcan_txbcie FDCAN_TXBCIE Transmit cancelled interrupt enable bits
+ *
+ * Each bit enables or disables transmit cancelled interrupt for transmit buffer
+ * slot.
+ * @{
+ */
+#define FDCAN_TXBCIE_CFIE0 (1 << 0)
+#define FDCAN_TXBCIE_CFIE1 (1 << 1)
+#define FDCAN_TXBCIE_CFIE2 (1 << 2)
+/**@}*/
+
+/* EFFL[2:0]: Event FIFO fill level*/
+#define FDCAN_TXEFS_EFFL_SHIFT 0
+#define FDCAN_TXEFS_EFFL_MASK 0x7
+
+/* EFG[1:0]: Event FIFO get index */
+#define FDCAN_TXEFS_EFGI_SHIFT 8
+#define FDCAN_TXEFS_EFGI_MASK 0x3
+
+/* EFPI[1:0]: Event FIFO put index */
+#define FDCAN_TXEFS_EFPI_SHIFT 16
+#define FDCAN_TXEFS_EFPI_MASK 0x3
+
+#define FDCAN_TXEFS_EFF (1 << 24)
+#define FDCAN_TXEFS_TEF (1 << 25)
+
+/* EFAI[1:0]: Event FIFO acknowledge index */
+#define FDCAN_TXEFA_EFAI_SHIFT 0
+#define FDCAN_TXEFA_EFAI_MASK 0x3
+
+
+/* PDIV[3:0]: Input clock divider */
+#define FDCAN_CKDIV_PDIV_SHIFT 0
+#define FDCAN_CKDIV_PDIV_MASK 0xF
+
+/* --- FD-CAN memory block defines------------------------------------------ */
+
+/** Structure describing standard ID filter.
+ * Standard ID filter is composed of one 32bit value.
+ * This region of memory cannot be accessed in quantities less than 32bits.
+ */
+struct fdcan_standard_filter {
+ /** Aggregate of filter type, filter action and two IDs */
+ uint32_t type_id1_conf_id2;
+};
+
+#define FDCAN_SFT_SHIFT 30
+#define FDCAN_SFT_MASK 0x3
+
+/** @defgroup fdcan_sft Standard ID filter match type
+ *
+ * Matching strategy for standard ID filters.
+ * @{
+ */
+/** Filter matches all messages in range from id1 to id2. */
+#define FDCAN_SFT_RANGE 0x0
+
+/** Filter matches messages with id1 or id2 */
+#define FDCAN_SFT_DUAL 0x1
+
+/** Filter matches messages which match id1 after being unmasked
+ * using id2. */
+#define FDCAN_SFT_ID_MASK 0x2
+
+/** Disable this filter. */
+#define FDCAN_SFT_DISABLE 0x3
+/**@}*/
+
+#define FDCAN_SFEC_SHIFT 27
+#define FDCAN_SFEC_MASK 0x7
+
+/** @defgroup fdcan_sfec Standard ID filter action
+ *
+ * Defines possible actions for standard ID filters. All actions except
+ * of @ref FDCAN_SFEC_PRIO cause filter matching to terminate immediately
+ * with desired outcome. FDCAN_SFEC_PRIO sets priority flag for message
+ * and continues processing remaining filters.
+ * @{
+ */
+
+/** Filter is disabled. No matchin occurrs. */
+#define FDCAN_SFEC_DISABLE 0x0
+
+/** Put message into FIFO0 */
+#define FDCAN_SFEC_FIFO0 0x1
+
+/** Put message into FIFO1 */
+#define FDCAN_SFEC_FIFO1 0x2
+
+/** Reject message */
+#define FDCAN_SFEC_REJECT 0x3
+
+/** Treat message as priority message (and continue processing further rules) */
+#define FDCAN_SFEC_PRIO 0x4
+
+/** Treat message as priority and put it into FIFO0 */
+#define FDCAN_SFEC_PRIO_FIFO0 0x5
+
+/** Treat message as priority and put it into FIFO1 */
+#define FDCAN_SFEC_PRIO_FIFO1 0x6
+/**@}*/
+
+/** Amount of standard filters allocated in Message RAM
+ * This number may vary between devices. 28 is value valid
+ * for STM32G4
+ **/
+#define FDCAN_SFT_MAX_NR 28
+
+/* SFEC = 0x7 is unused */
+
+#define FDCAN_SFID1_SHIFT 16
+#define FDCAN_SFID1_MASK 0x7FF
+
+#define FDCAN_SFID2_SHIFT 0
+#define FDCAN_SFID2_MASK 0x7FF
+
+/** Structure describing extended ID filters.
+ * Extended ID filter is composed of two 32bit values.
+ * This region of memory cannot be accessed in quantities less than 32bits.
+ */
+struct fdcan_extended_filter {
+ /** Aggregate of filter action and extended ID */
+ uint32_t conf_id1;
+ /** Aggregate of filter type and extended ID or mask */
+ uint32_t type_id2;
+};
+
+#define FDCAN_EFEC_SHIFT 29
+#define FDCAN_EFEC_MASK 0x7
+
+/** @defgroup fdcan_efec Extended ID filter action
+ *
+ * These are possible actions, extended filter can have. If filter is
+ * disabled, then no matching is performed. All other actions except of
+ * @ref FDCAN_EFEC_PRIO cause matching to terminate with required outcome.
+ * FDCAN_EFEC_PRIO marks message as priority and continues matching.
+ * @{
+ */
+
+/** Disable this filter. */
+#define FDCAN_EFEC_DISABLE 0x0
+
+/** Put message into FIFO0 */
+#define FDCAN_EFEC_FIFO0 0x1
+
+/** Put message into FIFO1 */
+#define FDCAN_EFEC_FIFO1 0x2
+
+/** Reject message */
+#define FDCAN_EFEC_REJECT 0x3
+
+/** Treat message as priority message (and continue processing further rules) */
+#define FDCAN_EFEC_PRIO 0x4
+
+/** Treat message as priority and put it into FIFO0 */
+#define FDCAN_EFEC_PRIO_FIFO0 0x5
+
+/** Treat message as priority and put it into FIFO1 */
+#define FDCAN_EFEC_PRIO_FIFO1 0x6
+/**@}*/
+
+#define FDCAN_EFID1_SHIFT 0
+#define FDCAN_EFID1_MASK 0x1FFFFFFF
+
+#define FDCAN_EFT_SHIFT 30
+#define FDCAN_EFT_MASK 0x3
+
+/** @defgroup fdcan_eft Extended ID filter match type
+ *
+ * Matching strategy for extended ID filters.
+ * @{
+ */
+/** Filter matches all messages in range from id1 to id2. */
+#define FDCAN_EFT_RANGE 0x0
+
+/** Filter matches messages with id1 or id2 */
+#define FDCAN_EFT_DUAL 0x1
+
+/** Filter matches messages which match id1 after being unmasked
+ * using id2. */
+#define FDCAN_EFT_ID_MASK 0x2
+
+/** Similar to @ref FDCAN_EFT_RANGE except of ignoring global mask
+ * set using @ref FDCAN_XIDAM register.
+ */
+#define FDCAN_EFT_RANGE_NOXIDAM 0x3
+/**@}*/
+
+#define FDCAN_EFID2_SHIFT 0
+#define FDCAN_EFID2_MASK 0x1FFFFFFF
+
+/** Amount of extended filters allocated in Message RAM
+ * This number may vary between devices. 8 is value valid
+ * for STM32G4
+ **/
+#define FDCAN_EFT_MAX_NR 8
+
+/** Structure describing receive FIFO element.
+ * Receive FIFO element consists of 2 32bit values for header
+ * and 16 32bit values for message payload.
+ * This area of memory can only be accessed in 32bit quantities
+ */
+struct fdcan_rx_fifo_element {
+ /** Aggregate of message identifier and flags. */
+ uint32_t identifier_flags;
+ /** Aggregate of filter match ID, transfer format, DLC and timestamp */
+ uint32_t filt_fmt_dlc_ts;
+ /** Message payload data */
+ uint32_t data[64 / sizeof(uint32_t)];
+};
+
+/** Structure describing transmit event element.
+ * Transmit event element consists of 2 32bit values.
+ * This area of memory can only be accessed in 32bit quantities
+ */
+struct fdcan_tx_event_element {
+ /** Aggregate of message identifier and flags. */
+ uint32_t identifier_flags;
+
+ /** Aggregate of event ID, transfer format, DLC and timestamp */
+ uint32_t evt_fmt_dlc_ts;
+};
+
+/** Structure describing transmit buffer element.
+ * Transmit buffer consists of 2 32bit values for header
+ * and 16 32bit values for message payload.
+ * This area of memory can only be accessed in 32bit quantities
+ */
+struct fdcan_tx_buffer_element {
+ /** Aggregate of message identifier and flags. */
+ uint32_t identifier_flags;
+
+ /** Aggregate of event ID, transfer format and DLC */
+ uint32_t evt_fmt_dlc_res;
+ /** Message payload data */
+ uint32_t data[64 / sizeof(uint32_t)];
+};
+
+/** @defgroup fdcan_fifo_flags FIFO / buffer flags
+ * @{
+ */
+#define FDCAN_FIFO_ESI (1 << 31)
+#define FDCAN_FIFO_XTD (1 << 20)
+#define FDCAN_FIFO_RTR (1 << 29)
+#define FDCAN_FIFO_EFC (1 << 23)
+#define FDCAN_FIFO_FDF (1 << 21)
+#define FDCAN_FIFO_BRS (1 << 20)
+/**@}*/
+
+#define FDCAN_FIFO_EID_SHIFT 0
+#define FDCAN_FIFO_EID_MASK 0x1FFFFFFF
+
+#define FDCAN_FIFO_SID_SHIFT 18
+#define FDCAN_FIFO_SID_MASK 0x7FF
+
+#define FDCAN_FIFO_DLC_SHIFT 16
+#define FDCAN_FIFO_DLC_MASK 0xF
+
+#define FDCAN_FIFO_MM_SHIFT 24
+#define FDCAN_FIFO_MM_MASK 0xFF
+
+#define FDCAN_FIFO_ANMF (1 << 31)
+#define FDCAN_FIFO_FIDX_SHIFT 24
+#define FDCAN_FIFO_FIDX_MASK 0x7F
+
+#define FDCAN_FIFO_RXTS_SHIFT 0
+#define FDCAN_FIFO_RXTS_MASK 0xFFFF
+
+/** Message RAM layout for one FDCAN block.
+ * There are as many memory blocks as there are FDCAN blocks
+ */
+struct fdcan_message_ram {
+ /* List of standard ID filters */
+ struct fdcan_standard_filter lfssa[FDCAN_SFT_MAX_NR];
+
+ /* List of extended ID filters */
+ struct fdcan_extended_filter lfesa[FDCAN_EFT_MAX_NR];
+
+ /* Buffer area for two receive FIFOs each having space for three messages */
+ struct fdcan_rx_fifo_element rx_fifo[2][3];
+
+ /* Buffer area for transmit event buffers */
+ struct fdcan_tx_event_element tx_event[3];
+
+ /* Buffer area for transmitted messages. May act either as FIFO or as queue
+ * depending on configuration
+ */
+ struct fdcan_tx_buffer_element tx_buffer[3];
+};
+
+
+/* --- FD-CAN error returns ------------------------------------------------- */
+
+/** FDCAN error return values
+ */
+enum fdcan_error {
+ /** No error. Operation finished successfully */
+ FDCAN_E_OK,
+
+ /** Value provided was out of range */
+ FDCAN_E_OUTOFRANGE,
+
+ /** Timeout waiting for FDCAN block to accept INIT bit change */
+ FDCAN_E_TIMEOUT,
+
+ /** Value provided was invalid (FIFO index, FDCAN block base address, length, etc.) */
+ FDCAN_E_INVALID,
+
+ /** Device is busy: Transmit buffer is full, unable to queue additional message or device
+ * is outside of INIT mode and cannot perform desired operation. */
+ FDCAN_E_BUSY,
+
+ /** Receive buffer is empty, unable to read any new message */
+ FDCAN_E_NOTAVAIL
+};
+
+/**@}*/
+
+/* --- FD-CAN functions ----------------------------------------------------- */
+
+BEGIN_DECLS
+
+int fdcan_init(uint32_t canport, uint32_t timeout);
+
+void fdcan_set_can(uint32_t canport, bool auto_retry_disable, bool rx_fifo_locked,
+ bool tx_queue_mode, bool silent, uint32_t n_sjw, uint32_t n_ts1, uint32_t n_ts2,
+ uint32_t n_br_presc);
+
+void fdcan_set_fdcan(uint32_t canport, bool brs_enable, bool fd_op_enable,
+ uint32_t f_sjw, uint32_t f_ts1, uint32_t f_ts2, uint32_t f_br_presc);
+
+void fdcan_set_test(uint32_t canport, bool testing, bool loopback);
+
+void fdcan_init_filter(uint32_t canport, uint8_t std_filt, uint8_t ext_filt);
+
+int fdcan_start(uint32_t canport, uint32_t timeout);
+
+int fdcan_get_init_state(uint32_t canport);
+
+void fdcan_set_std_filter(uint32_t canport, uint32_t nr,
+ uint8_t id_list_mode, uint32_t id1, uint32_t id2,
+ uint8_t action);
+
+void fdcan_set_ext_filter(uint32_t canport, uint32_t nr,
+ uint8_t id_list_mode, uint32_t id1, uint32_t id2,
+ uint8_t action);
+
+void fdcan_enable_irq(uint32_t canport, uint32_t irq);
+void fdcan_disable_irq(uint32_t canport, uint32_t irq);
+
+int fdcan_transmit(uint32_t canport, uint32_t id, bool ext, bool rtr,
+ bool fdcan_fmt, bool btr_switch, uint8_t length, const uint8_t *data);
+
+int fdcan_receive(uint32_t canport, uint8_t fifo, bool release, uint32_t *id,
+ bool *ext, bool *rtr, uint8_t *fmi, uint8_t *length,
+ uint8_t *data, uint16_t *timestamp);
+
+void fdcan_release_fifo(uint32_t canport, uint8_t fifo);
+
+bool fdcan_available_tx(uint32_t canport);
+bool fdcan_available_rx(uint32_t canport, uint8_t fifo);
+
+END_DECLS
+
+
+#endif
diff --git a/lib/stm32/common/fdcan.c b/lib/stm32/common/fdcan.c
new file mode 100644
index 00000000..30e6956a
--- /dev/null
+++ b/lib/stm32/common/fdcan.c
@@ -0,0 +1,779 @@
+/** @defgroup fdcan_file FDCAN peripheral API
+ *
+ * @ingroup peripheral_apis
+ *
+ * @brief libopencm3 STM32 FDCAN
+ *
+ * @version 1.0.0
+ *
+ * @author @htmlonly © @endhtmlonly 2021 Eduard Drusa
+ *
+ * Devices can have up to three FDCAN peripherals residing in one FDCAN block. The peripherals
+ * support both CAN 2.0 A and B standard and Bosch FDCAN standard. FDCAN frame format and
+ * bitrate switching is supported. The peripheral has several filters for incoming messages that
+ * can be distributed between two FIFOs and three transmit mailboxes. For transmitted messages
+ * it is possible to opt for event notification once message is transmitted.
+ *
+ * LGPL License Terms @ref lgpl_license
+*/
+
+/*
+ * This file is part of the libopencm3 project.
+ *
+ * Copyright (C) 2021 Eduard Drusa
+ *
+ * This library is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library. If not, see .
+ */
+
+#include
+#include
+#include
+
+
+/* --- FD-CAN internal functions -------------------------------------------- */
+
+/** Routine implementing FDCAN_CCCR's INIT bit manipulation.
+ *
+ * This routine will change INIT bit and wait for it to actually
+ * change its value. If change won't happen before timeout,
+ * error is signalized. If INIT bit already has value which
+ * should be set, this function will return immediately.
+ *
+ * @param [in] canport FDCAN block base address. See @ref fdcan_block.
+ * @param [in] set new value of INIT, true means set
+ * @param [in] timeout Amount of busyloop cycles, function will wait for FDCAN
+ * to switch it's state. If set to 0, then function returns immediately.
+ * @returns FDCAN_E_OK on success, FDCAN_E_TIMEOUT if INIT bit value
+ * didn't change before timeout has expired.
+ */
+static int fdcan_cccr_init_cfg(uint32_t canport, bool set, uint32_t timeout)
+{
+ uint32_t expected;
+ uint32_t wait_ack;
+
+ if (set) {
+ if ((FDCAN_CCCR(canport) & FDCAN_CCCR_INIT) == FDCAN_CCCR_INIT) {
+ /* Already there, sir */
+ return FDCAN_E_OK;
+ }
+
+ FDCAN_CCCR(canport) |= FDCAN_CCCR_INIT;
+ expected = FDCAN_CCCR_INIT;
+ } else {
+ if ((FDCAN_CCCR(canport) & FDCAN_CCCR_INIT) == 0) {
+ /* Already there, sir */
+ return FDCAN_E_OK;
+ }
+
+ FDCAN_CCCR(canport) &= ~FDCAN_CCCR_INIT;
+ expected = 0;
+ }
+
+ /* Wait, until INIT bit is acknowledged */
+ wait_ack = timeout;
+ while ((wait_ack--) &&
+ ((FDCAN_CCCR(canport) & FDCAN_CCCR_INIT) == expected));
+
+ if ((FDCAN_CCCR(canport) & FDCAN_CCCR_INIT) == expected) {
+ return FDCAN_E_OK;
+ } else {
+ return FDCAN_E_TIMEOUT;
+ }
+}
+
+/** Return ID of next free Tx buffer.
+ *
+ * Examines transmit buffer allocation in message RAM
+ * and returns ID of buffer, which is free.
+ *
+ * @param [in] canport FDCAN block base address. See @ref fdcan_block.
+ * @returns Non-negative number ID of Tx buffer which is free,
+ * or FDCAN_E_BUSY if no Tx buffer is available
+ */
+static int fdcan_get_free_txbuf(uint32_t canport)
+{
+ if ((FDCAN_TXBRP(canport) & FDCAN_TXBRP_TRP0) == 0) {
+ return 0;
+ } else if ((FDCAN_TXBRP(canport) & FDCAN_TXBRP_TRP1) == 0) {
+ return 1;
+ } else if ((FDCAN_TXBRP(canport) & FDCAN_TXBRP_TRP2) == 0) {
+ return 2;
+ }
+
+ return FDCAN_E_BUSY;
+}
+
+/** Returns fill state and next available get index from receive FIFO.
+ *
+ * Examines FDCAN receive FIFO and returns fill status of FIFO and ID of
+ * next message available for reading. If fill status is 0 (FIFO is empty),
+ * then get index is undefined.
+ *
+ * @param [in] canport FDCAN block base address. See @ref fdcan_block.
+ * @param [in] fifo_id ID of fifo queried (currently 0 or 1)
+ * @param [out] get_index Address of buffer where next get index will be stored
+ * @param [out] pending_frames Address of buffer where amount of pending frame will be stored.
+ */
+static void fdcan_get_fill_rxfifo(uint32_t canport, uint8_t fifo_id, unsigned *get_index,
+ unsigned *pending_frames)
+{
+ *get_index = (FDCAN_RXFIS(canport, fifo_id) >> FDCAN_RXFIFO_GI_SHIFT)
+ & FDCAN_RXFIFO_GI_MASK;
+
+ *pending_frames = (FDCAN_RXFIS(canport, fifo_id) >> FDCAN_RXFIFO_FL_SHIFT)
+ & FDCAN_RXFIFO_FL_MASK;
+}
+
+/** Obtain address of FDCAN Message RAM for certain FDCAN block.
+ *
+ * @param [in] canport identification of FDCAN block. See @ref fdcan_block.
+ * @return Address of Message RAM for given FDCAN block or null pointer
+ * if FDCAN block identification is invalid.
+ */
+static struct fdcan_message_ram *fdcan_get_msgram_addr(uint32_t canport)
+{
+ /* This piece of code may look wrong, after one examines
+ * STM32G4 datasheet and/or g4/memorymap.h. There are three
+ * memory regions defined for FDCANx_RAM_BASE. They are 0x400
+ * bytes apart as per chapter 2.2.2 of [RM0440].
+ *
+ * It turns out, that these addresses are not in line with what
+ * is specified later in chapter 44.3.3 of [RM0440]. There it is
+ * stated, that message RAMs are packed and in case of multiple
+ * FDCAN blocks, message RAM for n-th FDCAN starts at address
+ * end of (n-1)-th block + 4 (explicitly, offset 0x354).
+ *
+ * It turns out, that this statement is also false! In fact FDCAN
+ * message RAMs are packed tightly and n-th block starts immediately
+ * after (n-1)-th block ends. Thus offset is going to be computed
+ * using formula:
+ *
+ * FDCAN1_RAM_BASE + (block_id * sizeof(struct fdcan_message_ram))
+ */
+ if (canport == CAN1) {
+ return (struct fdcan_message_ram *) (FDCAN1_RAM_BASE + 0);
+ } else if (canport == CAN2) {
+ return (struct fdcan_message_ram *)
+ (FDCAN1_RAM_BASE + sizeof(struct fdcan_message_ram));
+ } else if (canport == CAN3) {
+ return (struct fdcan_message_ram *)
+ (FDCAN1_RAM_BASE + (2 * sizeof(struct fdcan_message_ram)));
+ }
+
+ return NULL;
+}
+
+/** Converts frame length to DLC value.
+ *
+ * Works for both CAN and FDCAN frame lengths. If length
+ * is invalid value, then returns 0xFF.
+ *
+ * @param [in] length intended frame payload length in bytes
+ * @returns DLC value representing lengths or 0xFF if length cannot
+ * be encoded into DLC format (applies only to FDCAN frame lengths)
+ */
+static uint32_t fdcan_length_to_dlc(uint8_t length)
+{
+ if (length <= 8) {
+ return length;
+ } else if (length <= 24) {
+ if ((length % 4) != 0) {
+ return 0xFF;
+ }
+ return 8 + ((length - 8) / 4);
+ } else {
+ if ((length % 16) != 0) {
+ return 0xFF;
+ }
+ return 11 + (length / 16);
+ }
+}
+
+/** Converts DLC value into frame payload length.
+ *
+ * Works for both CAN and FDCAN DLC values.
+ *
+ * @param [in] dlc DLC value
+ * @returns data payload length in bytes
+ */
+static uint8_t fdcan_dlc_to_length(uint32_t dlc)
+{
+ if (dlc <= 8) {
+ return dlc;
+ } else if (dlc <= 12) {
+ return 8 + ((dlc - 8) * 4);
+ } else {
+ return 16 + ((dlc - 12) * 16);
+ }
+}
+
+/* --- FD-CAN functions ----------------------------------------------------- */
+
+/** @ingroup fdcan_file */
+/**@{
+ * */
+
+/** Put FDCAN block into INIT mode for setup
+ *
+ * Initialize the selected CAN peripheral block. This function will switch CAN block
+ * into initialization mode. CAN block is then left in initialization mode in order to
+ * perform setup, which can't be adjusted once FDCAN block is started. It is mandatory
+ * to call at least @ref fdcan_set_can function to configure basic timing values for
+ * CAN 2.0 operation. Functions which only have effect, if FDCAN block is in INIT mode
+ * are:
+ * * @ref fdcan_set_can
+ * * @ref fdcan_set_fdcan
+ * * @ref fdcan_init_filter
+ * * @ref fdcan_set_test
+ *
+ * You can check if FDCAN block is in INIT mode or it is started using
+ * @ref fdcan_get_init_state.
+ *
+ * @param[in] canport CAN register base address. See @ref fdcan_block.
+ * @param [in] timeout Amount of empty busy loops, which routine should wait for FDCAN
+ * confirming that it entered INIT mode. If set to 0, function will return
+ * immediately.
+ * @returns Operation error status. See @ref fdcan_error.
+ */
+int fdcan_init(uint32_t canport, uint32_t timeout)
+{
+ if (fdcan_cccr_init_cfg(canport, true, timeout) != 0) {
+ return FDCAN_E_TIMEOUT;
+ }
+
+ FDCAN_CCCR(canport) |= FDCAN_CCCR_CCE;
+
+ return FDCAN_E_OK;
+}
+
+/** Set essential FDCAN block parameters for plain CAN operation
+ *
+ * Allows configuration of prescalers and essential transmit and FIFO behavior
+ * used during transmission in plain CAN 2.0 mode. In this mode FDCAN frame format
+ * is not available nor is possible to use fast bitrates.
+ * This function does neither enable FD-CAN mode after reset nor disable it
+ * after re-entering INIT mode of previously configured block. Timing values set
+ * here are valid for both arbitration phase of all frames and for data phase of
+ * both CAN and FDCAN frames, which don't use bitrate switching. This function can
+ * only be called after FDCAN block has been switched into INIT mode.
+ * It is possible to receive FDCAN frames even if FDCAN block is configured only using
+ * this function as long as bitrate switching is not used.
+ *
+ * @param [in] canport FDCAN block base address. See @ref fdcan_block.
+ * @param [in] auto_retry_disable Disable automatic frame retransmission on error
+ * @param [in] rx_fifo_locked Enable FIFO locked mode. Upon FIFO overflow all received
+ * messages are discarded.
+ * @param [in] tx_queue_mode Enable transmission queue mode. Otherwise transmission
+ * works in FIFO mode.
+ * @param [in] silent Enable silent mode. Transmitter stays recessive all the time.
+ * @param [in] n_sjw Resynchronization time quanta jump width
+ * @param [in] n_ts1 Time segment 1 time quanta
+ * @param [in] n_ts2 Time segment 2 time quanta
+ * @param [in] n_br_presc Arbitration phase / CAN mode bitrate prescaler
+ */
+void fdcan_set_can(uint32_t canport, bool auto_retry_disable, bool rx_fifo_locked,
+ bool tx_queue_mode, bool silent, uint32_t n_sjw, uint32_t n_ts1, uint32_t n_ts2,
+ uint32_t n_br_presc)
+{
+ FDCAN_NBTP(canport) = (n_sjw << FDCAN_NBTP_NSJW_SHIFT)
+ | (n_ts1 << FDCAN_NBTP_NTSEG1_SHIFT)
+ | (n_ts2 << FDCAN_NBTP_NTSEG2_SHIFT)
+ | (n_br_presc << FDCAN_NBTP_NBRP_SHIFT);
+
+ if (tx_queue_mode) {
+ FDCAN_TXBC(canport) |= FDCAN_TXBC_TFQM;
+ } else {
+ FDCAN_TXBC(canport) &= ~FDCAN_TXBC_TFQM;
+ }
+
+ if (auto_retry_disable) {
+ FDCAN_CCCR(canport) |= FDCAN_CCCR_DAR;
+ } else {
+ FDCAN_CCCR(canport) &= ~FDCAN_CCCR_DAR;
+ }
+
+ if (silent) {
+ FDCAN_CCCR(canport) |= FDCAN_CCCR_MON;
+ } else {
+ FDCAN_CCCR(canport) &= ~FDCAN_CCCR_MON;
+ }
+
+ if (rx_fifo_locked) {
+ FDCAN_RXGFC(canport) &= ~(FDCAN_RXGFC_F1OM | FDCAN_RXGFC_F0OM);
+ } else {
+ FDCAN_RXGFC(canport) |= FDCAN_RXGFC_F1OM | FDCAN_RXGFC_F0OM;
+ }
+
+}
+
+/** Set FDCAN block parameters for FDCAN transmission
+ *
+ * Enables and configures parameters related to FDCAN transmission. This function
+ * allows configuration of bitrate switching, FDCAN frame format and fast mode
+ * timing. This function can only be called if FDCAN block is in INIT mode.
+ * It is safe to call this function on previously configured block in order
+ * to enable/disable/change FDCAN mode parameters. Non-FDCAN parameters won't
+ * be affected.
+ *
+ * @param [in] canport FDCAN block base address. See @ref fdcan_block.
+ * @param [in] brs_enable Enable FDCAN bitrate switching for fast mode operation
+ * @param [in] fd_op_enable Enable transmission of FDCAN-formatted frames
+ * @param [in] f_sjw Resynchronization time quanta jump width in fast mode
+ * @param [in] f_ts1 Time segment 1 time quanta in fast mode
+ * @param [in] f_ts2 Time segment 2 time quanta in fast mode
+ * @param [in] f_br_presc Fast mode operation bitrate prescaler
+ */
+void fdcan_set_fdcan(uint32_t canport, bool brs_enable, bool fd_op_enable,
+ uint32_t f_sjw, uint32_t f_ts1, uint32_t f_ts2, uint32_t f_br_presc)
+{
+ FDCAN_DBTP(canport) = (f_sjw << FDCAN_DBTP_DSJW_SHIFT)
+ | (f_ts1 << FDCAN_DBTP_DTSEG1_SHIFT)
+ | (f_ts2 << FDCAN_DBTP_DTSEG2_SHIFT)
+ | (f_br_presc << FDCAN_DBTP_DBRP_SHIFT);
+
+ if (fd_op_enable) {
+ FDCAN_CCCR(canport) |= FDCAN_CCCR_FDOE;
+ } else {
+ FDCAN_CCCR(canport) &= ~FDCAN_CCCR_FDOE;
+ }
+
+ if (brs_enable) {
+ FDCAN_CCCR(canport) |= FDCAN_CCCR_BRSE;
+ } else {
+ FDCAN_CCCR(canport) &= ~FDCAN_CCCR_BRSE;
+ }
+}
+
+/** Set FDCAN block testing features.
+ *
+ * Configures self-test functions of FDCAN block. It is safe to call
+ * this function on fully configured interface. This function can
+ * only be called after FDCAN block is put into INIT mode.
+ *
+ * @param [in] canport FDCAN block base address. See @ref fdcan_block.
+ * @param [in] testing Enables testing mode of FDCAN block
+ * @param [in] loopback Enables transmission loopback
+ */
+void fdcan_set_test(uint32_t canport, bool testing, bool loopback)
+{
+ if (testing) {
+ FDCAN_CCCR(canport) |= FDCAN_CCCR_TEST;
+ if (loopback) {
+ FDCAN_TEST(canport) |= FDCAN_TEST_LBCK;
+ } else {
+ FDCAN_TEST(canport) &= ~FDCAN_TEST_LBCK;
+ }
+ } else {
+ FDCAN_CCCR(canport) &= ~FDCAN_CCCR_TEST;
+ /* FDCAN_TEST is automatically reset to default values by
+ * FDCAN at this point */
+ }
+}
+
+/** Enable FDCAN operation after FDCAN block has been set up.
+ *
+ * This function will disable FDCAN configuration effectively
+ * allowing FDCAN to sync up with the bus. After calling this function
+ * it is not possible to reconfigure amount of filter rules, yet
+ * it is possible to configure rules themselves. FDCAN block operation
+ * state can be checked using @ref fdcan_get_init_state.
+ *
+ * @param [in] canport FDCAN block base address. See @ref fdcan_block.
+ * @param [in] timeout Amount of empty busy loops, which routine should wait for FDCAN
+ * confirming that it left INIT mode. If set to 0, function will return
+ * immediately.
+ * @returns Operation error status. See @ref fdcan_error.
+ * @note If this function returns with timeout, it usually means that
+ * FDCAN_clk is not set up properly.
+ */
+int fdcan_start(uint32_t canport, uint32_t timeout)
+{
+ /* Error here usually means, that FDCAN_clk is not set up
+ * correctly, or at all. This usually can't be seen above
+ * when INIT is set to 1, because default value for INIT is
+ * 1 as long as one has FDCAN_pclk configured properly.
+ **/
+ if (fdcan_cccr_init_cfg(canport, false, timeout) != 0) {
+ return FDCAN_E_TIMEOUT;
+ }
+
+ return FDCAN_E_OK;
+}
+
+/** Return current FDCAN block operation state.
+ *
+ * This function effectively returns value of FDCAN_CCCR's INIT bit.
+ * @param [in] canport FDCAN block base address. See @ref fdcan_block.
+ * @returns 1 if FDCAN block is in INIT mode, 0 if it is already started.
+ */
+int fdcan_get_init_state(uint32_t canport)
+{
+ return ((FDCAN_CCCR(canport) & FDCAN_CCCR_INIT) == FDCAN_CCCR_INIT);
+}
+
+/** Configure amount of filters and initialize filtering block.
+ *
+ * This function allows to configure global amount of filters present.
+ * FDCAN block will only ever check as many filters as this function configures.
+ * Function will also clear all filter blocks to zero values. This function
+ * can be only called after @ref fdcan_init has already been called and
+ * @ref fdcan_start has not been called yet as registers holding filter
+ * count are write-protected unless FDCAN block is in INIT mode. It is possible
+ * to reconfigure filters (@ref fdcan_set_std_filter and @ref fdcan_set_ext_filter)
+ * after FDCAN block has already been started.
+ *
+ * @param [in] canport FDCAN block base address. See @ref fdcan_block.
+ * @param [in] std_filt requested amount of standard ID filter rules (0-28)
+ * @param [in] ext_filt requested amount of extended ID filter rules (0-8)
+ */
+void fdcan_init_filter(uint32_t canport, uint8_t std_filt, uint8_t ext_filt)
+{
+ struct fdcan_message_ram *ram = fdcan_get_msgram_addr(canport);
+
+ /* Only perform initialization of message RAM if there are
+ * any filters required
+ */
+ if (std_filt > 0) {
+ FDCAN_RXGFC(canport) =
+ (FDCAN_RXGFC(canport) & ~(FDCAN_RXGFC_LSS_MASK << FDCAN_RXGFC_LSS_SHIFT))
+ | (std_filt << FDCAN_RXGFC_LSS_SHIFT);
+
+
+ for (int q = 0; q < FDCAN_SFT_MAX_NR; ++q) {
+ ram->lfssa[q].type_id1_conf_id2 = 0;
+ }
+ } else {
+ /* Reset filter count to zero */
+ FDCAN_RXGFC(canport) =
+ (FDCAN_RXGFC(canport) & ~(FDCAN_RXGFC_LSS_MASK << FDCAN_RXGFC_LSS_SHIFT));
+ }
+
+ if (ext_filt > 0) {
+ FDCAN_RXGFC(canport) =
+ (FDCAN_RXGFC(canport) & ~(FDCAN_RXGFC_LSE_MASK << FDCAN_RXGFC_LSE_SHIFT))
+ | (ext_filt << FDCAN_RXGFC_LSE_SHIFT);
+
+ for (int q = 0; q < FDCAN_EFT_MAX_NR; ++q) {
+ ram->lfesa[q].conf_id1 = 0;
+ ram->lfesa[q].type_id2 = 0;
+ }
+ } else {
+ /* Reset filter count to zero */
+ FDCAN_RXGFC(canport) =
+ (FDCAN_RXGFC(canport) & ~(FDCAN_RXGFC_LSE_MASK << FDCAN_RXGFC_LSE_SHIFT));
+ }
+}
+
+/** Configure filter rule for standard ID frames.
+ *
+ * Sets up filter rule for frames having standard ID. Each FDCAN block can
+ * have its own set of filtering rules. It is only possible to configure as
+ * many filters as was configured previously using @ref fdcan_init_filter.
+ *
+ * @param [in] canport FDCAN block base address. See @ref fdcan_block.
+ * @param [in] nr number of filter to be configured
+ * @param [in] id_list_mode Mode in which id1 and id2 are used to match the rule.
+ * See @ref fdcan_sft.
+ * @param [in] id1 standard ID for matching. Used as exact value, lower bound or bit
+ * pattern depending on matching mode selected
+ * @param [in] id2 standard ID or bitmask. Used as exact value, upper bound or bit mask
+ * depending on matching mode selected
+ * @param [in] action Action performed if filtering rule matches frame ID.
+ * See @ref fdcan_sfec.
+ */
+void fdcan_set_std_filter(uint32_t canport, uint32_t nr,
+ uint8_t id_list_mode, uint32_t id1, uint32_t id2,
+ uint8_t action)
+{
+ struct fdcan_message_ram *ram = fdcan_get_msgram_addr(canport);
+
+ /* id_list_mode and action are passed unguarded. Simply use
+ * defines and it will be OK. id1 and id2 are masked for
+ * correct size, because it is way too simple to pass ID
+ * larger than 11 bits unintentionally. It then leads to all
+ * kind of extremely weird errors while excessive ID bits
+ * overflow into flags. This tends to be extremely time
+ * consuming to debug.
+ */
+ ram->lfssa[nr].type_id1_conf_id2 =
+ (id_list_mode << FDCAN_SFT_SHIFT)
+ | (action << FDCAN_SFEC_SHIFT)
+ | ((id1 & FDCAN_SFID1_MASK) << FDCAN_SFID1_SHIFT)
+ | ((id2 & FDCAN_SFID2_MASK) << FDCAN_SFID2_SHIFT);
+
+ return;
+}
+
+/** Configure filter rule for extended ID frames.
+ *
+ * Sets up filter rule for frames having extended ID. Each FDCAN block can
+ * have its own set of filtering rules. It is only possible to configure as
+ * many filters as was configured previously using @ref fdcan_init_filter.
+ *
+ * @param [in] canport FDCAN block base address. See @ref fdcan_block.
+ * @param [in] nr number of filter to be configured
+ * @param [in] id_list_mode mode in which id1 and id2 are used to match the rule.
+ * See @ref fdcan_eft.
+ * @param [in] id1 extended ID for matching. Used as exact value, lower bound or bit
+ * pattern depending on matching mode selected
+ * @param [in] id2 extended ID or bitmask. Used as exact value, upper bound or bit mask
+ * depending on matching mode selected
+ * @param [in] action Action performed if filtering rule matches frame ID.
+ * See @ref fdcan_efec.
+ */
+void fdcan_set_ext_filter(uint32_t canport, uint32_t nr,
+ uint8_t id_list_mode, uint32_t id1, uint32_t id2,
+ uint8_t action)
+{
+ struct fdcan_message_ram *ram = fdcan_get_msgram_addr(canport);
+
+ ram->lfesa[nr].conf_id1 =
+ (action << FDCAN_EFEC_SHIFT)
+ | ((id1 & FDCAN_EFID1_MASK) << FDCAN_EFID1_SHIFT);
+
+ ram->lfesa[nr].type_id2 =
+ (id_list_mode << FDCAN_EFT_SHIFT)
+ | ((id2 & FDCAN_EFID2_MASK) << FDCAN_EFID2_SHIFT);
+}
+
+/** Transmit Message using FDCAN
+ *
+ * @param [in] canport CAN block register base. See @ref fdcan_block.
+ * @param [in] id Message ID
+ * @param [in] ext Extended message ID
+ * @param [in] rtr Request transmit
+ * @param [in] fdcan_fmt Use FDCAN format
+ * @param [in] btr_switch Switch bitrate for data portion of frame
+ * @param [in] length Message payload length. Must be valid CAN or FDCAN frame length
+ * @param [in] data Message payload data
+ * @returns int 0, 1 or 2 on success and depending on which outgoing mailbox got
+ * selected. Otherwise returns error code. For error codes, see @ref fdcan_error.
+ */
+int fdcan_transmit(uint32_t canport, uint32_t id, bool ext, bool rtr,
+ bool fdcan_fmt, bool btr_switch, uint8_t length, const uint8_t *data)
+{
+ int mailbox;
+ uint32_t dlc, flags = 0;
+
+ mailbox = fdcan_get_free_txbuf(canport);
+
+ if (mailbox == FDCAN_E_BUSY) {
+ return mailbox;
+ }
+
+ struct fdcan_message_ram *ram = fdcan_get_msgram_addr(canport);
+
+ /* Early check: if FDCAN message lentgh is > 8, it must be
+ * a multiple of 4 *and* fdcan format must be enabled.
+ */
+ dlc = fdcan_length_to_dlc(length);
+
+ if (dlc == 0xFF) {
+ return FDCAN_E_INVALID;
+ }
+
+ if (ext) {
+ ram->tx_buffer[mailbox].identifier_flags = FDCAN_FIFO_XTD
+ | ((id & FDCAN_FIFO_EID_MASK) << FDCAN_FIFO_EID_SHIFT);
+ } else {
+ ram->tx_buffer[mailbox].identifier_flags =
+ (id & FDCAN_FIFO_SID_MASK) << FDCAN_FIFO_SID_SHIFT;
+ }
+
+ if (rtr) {
+ ram->tx_buffer[mailbox].identifier_flags |= FDCAN_FIFO_RTR;
+ }
+
+ if (fdcan_fmt) {
+ flags |= FDCAN_FIFO_FDF;
+ }
+
+ if (btr_switch) {
+ flags |= FDCAN_FIFO_BRS;
+ }
+
+ ram->tx_buffer[mailbox].evt_fmt_dlc_res =
+ (dlc << FDCAN_FIFO_DLC_SHIFT) | flags;
+
+ for (int q = 0; q < length; q += 4) {
+ ram->tx_buffer[mailbox].data[q / 4] = *((uint32_t *) &data[q]);
+ }
+
+ FDCAN_TXBAR(canport) |= 1 << mailbox;
+
+ return mailbox;
+}
+
+/** Receive Message from FDCAN FIFO
+ *
+ * Reads one message from receive FIFO. Returns message ID, type of ID, message length
+ * and message payload. It is mandatory to provide valid pointers to suitably sized buffers
+ * for these outputs. Additionally, it is optinally possible to provide non-zero pointer to
+ * obtain filter identification, request of transmission flag and message timestamp.
+ * If pointers provided for optional outputs are NULL, then no information is returned
+ * for given pointer.
+ *
+ * @param [in] canport FDCAN block base address. See @ref fdcan_block
+ * @param [in] fifo_id FIFO id.
+ * @param [in] release Release the FIFO automatically after copying data out
+ * @param [out] id Returned message ID. Mandatory.
+ * @param [out] ext Returned type of the message ID (true if extended). Mandatory.
+ * @param [out] rtr Returnes flag if request of transmission was requested. Optional.
+ * @param [out] fmi Returned ID of the filter which matched this frame. Optional.
+ * @param [out] length Length of message payload in bytes. Mandatory.
+ * @param [out] data Buffer for storage of message payload data. Mandatory.
+ * @param [out] timestamp Returned timestamp of received frame. Optional.
+ * @returns Operation error status. See @ref fdcan_error.
+ */
+int fdcan_receive(uint32_t canport, uint8_t fifo_id, bool release, uint32_t *id,
+ bool *ext, bool *rtr, uint8_t *fmi, uint8_t *length,
+ uint8_t *data, uint16_t *timestamp)
+{
+ const struct fdcan_message_ram *ram = fdcan_get_msgram_addr(canport);
+
+ const struct fdcan_rx_fifo_element *fifo;
+
+ unsigned pending_frames, get_index, dlc, len;
+
+ fdcan_get_fill_rxfifo(canport, fifo_id, &get_index, &pending_frames);
+
+ fifo = ram->rx_fifo[fifo_id];
+
+ if (pending_frames == 0) {
+ return FDCAN_E_NOTAVAIL;
+ }
+
+ dlc = (fifo[get_index].filt_fmt_dlc_ts >> FDCAN_FIFO_DLC_SHIFT)
+ & FDCAN_FIFO_DLC_MASK;
+
+ len = fdcan_dlc_to_length(dlc);
+
+ *length = len;
+ if ((fifo[get_index].identifier_flags & FDCAN_FIFO_XTD) == FDCAN_FIFO_XTD) {
+ *ext = true;
+ *id = (fifo[get_index].identifier_flags >> FDCAN_FIFO_EID_SHIFT)
+ & FDCAN_FIFO_EID_MASK;
+ } else {
+ *ext = false;
+ *id = (fifo[get_index].identifier_flags >> FDCAN_FIFO_SID_SHIFT)
+ & FDCAN_FIFO_SID_MASK;
+ }
+
+ if (timestamp) {
+ *timestamp = (uint16_t) (fifo[get_index].filt_fmt_dlc_ts >> FDCAN_FIFO_RXTS_SHIFT)
+ & FDCAN_FIFO_RXTS_MASK;
+ }
+
+ if (fmi) {
+ *fmi = (uint8_t) (fifo[get_index].filt_fmt_dlc_ts >> FDCAN_FIFO_MM_SHIFT)
+ & FDCAN_FIFO_MM_MASK;
+ }
+
+ if (rtr) {
+ *rtr = ((fifo[get_index].identifier_flags & FDCAN_FIFO_RTR) == FDCAN_FIFO_RTR);
+ }
+
+ for (unsigned int q = 0; q < len; q += 4) {
+ *((uint32_t *) &data[q]) = fifo[get_index].data[q / 4];
+ }
+
+ if (release) {
+ FDCAN_RXFIA(canport, fifo_id) |= get_index << FDCAN_RXFIFO_AI_SHIFT;
+ }
+
+ return FDCAN_E_OK;
+}
+
+/** Release receive oldest FIFO entry.
+ *
+ * This function will mask oldest entry in FIFO as released making
+ * space for another received frame. This function can be used if
+ * fdcan_receive was called using release = false. If used in other
+ * case, then messages can get lost.
+ *
+ * @param [in] canport FDCAN block base address. See @ref fdcan_block.
+ * @param [in] fifo_id ID of FIFO where release should be performed (0 or 1)
+ */
+void fdcan_release_fifo(uint32_t canport, uint8_t fifo_id)
+{
+ unsigned pending_frames, get_index;
+
+ get_index = (FDCAN_RXFIS(canport, fifo_id) >> FDCAN_RXFIFO_GI_SHIFT)
+ & FDCAN_RXFIFO_GI_SHIFT;
+
+ pending_frames = (FDCAN_RXFIS(canport, fifo_id) >> FDCAN_RXFIFO_FL_SHIFT)
+ & FDCAN_RXFIFO_FL_SHIFT;
+
+ if (pending_frames) {
+ FDCAN_RXFIA(canport, fifo_id) |= get_index << FDCAN_RXFIFO_AI_SHIFT;
+ }
+}
+
+/** Enable IRQ from FDCAN block.
+ *
+ * This routine configures FDCAN to enable certain IRQ.
+ * Each FDCAN block supports two IRQs.
+ *
+ * @param [in] canport FDCAN block base address. See @ref fdcan_block.
+ * @param [in] irq number of IRQ to be enabled (currently 0 or 1)
+ */
+void fdcan_enable_irq(uint32_t canport, uint32_t irq)
+{
+ FDCAN_ILE(canport) |= irq & (FDCAN_ILE_INT0 | FDCAN_ILE_INT1);
+}
+
+/** Disable IRQ from FDCAN block.
+ *
+ * This routine configures FDCAN to disable certain IRQ.
+ * Each FDCAN block supports two IRQs.
+ *
+ * @param [in] canport FDCAN block base address. See @ref fdcan_block.
+ * @param [in] irq number of IRQ to be enabled (currently 0 or 1)
+ */
+void fdcan_disable_irq(uint32_t canport, uint32_t irq)
+{
+ FDCAN_ILE(canport) &= ~(irq & (FDCAN_ILE_INT0 | FDCAN_ILE_INT1));
+}
+
+/** Check if there is free transmit buffer.
+ *
+ * @param [in] canport FDCAN port. See @ref fdcan_block.
+ * @returns true if there is at least one free transmit buffer for new message
+ * to be sent, false otherwise.
+ */
+bool fdcan_available_tx(uint32_t canport)
+{
+ return (fdcan_get_free_txbuf(canport) != FDCAN_E_BUSY);
+}
+
+/** Tell if there is message waiting in receive FIFO.
+ *
+ * @param [in] canport FDCAN port. See @ref fdcan_block.
+ * @param [in] fifo Rx FIFO number, 0 or 1
+ * @returns true if there is at least one message waiting in given receive FIFO,
+ * false otherwise.
+ */
+bool fdcan_available_rx(uint32_t canport, uint8_t fifo)
+{
+ unsigned pending_frames;
+
+ pending_frames = (FDCAN_RXFIS(canport, fifo) >> FDCAN_RXFIFO_FL_SHIFT)
+ & FDCAN_RXFIFO_FL_MASK;
+
+ return (pending_frames != 0);
+}
+
+/**@}*/
+
+
diff --git a/lib/stm32/g4/Makefile b/lib/stm32/g4/Makefile
index ddaddb73..63a13e81 100644
--- a/lib/stm32/g4/Makefile
+++ b/lib/stm32/g4/Makefile
@@ -40,6 +40,7 @@ OBJS += crs_common_all.o
OBJS += dac_common_all.o dac_common_v2.o
OBJS += dma_common_l1f013.o
OBJS += dmamux.o
+OBJS += fdcan.o
OBJS += flash.o flash_common_all.o flash_common_f.o flash_common_idcache.o
OBJS += gpio_common_all.o gpio_common_f0234.o
OBJS += opamp_common_all.o opamp_common_v2.o