diff --git a/include/libopencm3/stm32/fdcan.h b/include/libopencm3/stm32/fdcan.h index 61ac87d0..f59e9d1b 100644 --- a/include/libopencm3/stm32/fdcan.h +++ b/include/libopencm3/stm32/fdcan.h @@ -1,17 +1,7 @@ -/** @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 + * 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 @@ -27,26 +17,20 @@ LGPL License Terms @ref lgpl_license * along with this library. If not, see . */ - - -#ifndef LIBOPENCM3_FDCAN_H -#define LIBOPENCM3_FDCAN_H +#pragma once #include #include -/** @{ */ +#if defined(STM32G4) +# include +#elif defined(STM32H7) +# include +#endif -/* FDCAN block base addresses. Used in functions to identify FDCAN block being manipulated. */ - -/** @defgroup fdcan_block FDCAN block base addresses +/** @addtogroup fdcan_defines * @{ */ -#define CAN1 FDCAN1_BASE -#define CAN2 FDCAN2_BASE -#define CAN3 FDCAN3_BASE -/**@}*/ - /** @defgroup fdcan_fifo Named constants for FIFOs * @{ @@ -55,6 +39,7 @@ LGPL License Terms @ref lgpl_license #define FDCAN_FIFO1 1 /**@}*/ +#define FDCAN_BLOCK_ID(can_base) (((can_base) - CAN1)/(CAN2 - CAN1)) /** @defgroup FDCAN registers file in each FDCAN block. */ @@ -76,38 +61,29 @@ LGPL License Terms @ref lgpl_license #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_RXFIS(can_base, fifo_id) \ + MMIO32(can_base + FDCAN_RXFIS_BASE + (FDCAN_RXFI_OFFSET * 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_RXFIA(can_base, fifo_id) MMIO32(can_base + 0x0094 + (FDCAN_RXFI_OFFSET * 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 @@ -360,26 +336,6 @@ LGPL License Terms @ref lgpl_license #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 @@ -402,15 +358,12 @@ LGPL License Terms @ref lgpl_license /* 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) @@ -432,7 +385,6 @@ LGPL License Terms @ref lgpl_license /* 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 @@ -461,17 +413,14 @@ LGPL License Terms @ref lgpl_license /* 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 +#define FDCAN_TXFQS_TFGI_SHIFT 8 /* TFQPI[1:0]: Tx FIFO put index */ -#define FDCAN_TXFQS_TFQPI_SHIFT 0 -#define FDCAN_TXFQS_TFQPI_MASK 0x3 +#define FDCAN_TXFQS_TFQPI_SHIFT 16 -#define FDCAN_TXFQS_TFQF (1 << 0) +#define FDCAN_TXFQS_TFQF (1 << 21) /** @defgroup fdcan_txbrp FDCAN_TXBRP Transmit request pending bits * @{ @@ -527,7 +476,7 @@ LGPL License Terms @ref lgpl_license /** @defgroup fdcan_txbcie FDCAN_TXBCIE Transmit cancelled interrupt enable bits * * Each bit enables or disables transmit cancelled interrupt for transmit buffer - * slot. + * slot. * @{ */ #define FDCAN_TXBCIE_CFIE0 (1 << 0) @@ -537,15 +486,12 @@ LGPL License Terms @ref lgpl_license /* 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) @@ -555,10 +501,6 @@ LGPL License Terms @ref lgpl_license #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. @@ -626,12 +568,6 @@ struct fdcan_standard_filter { #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 @@ -706,7 +642,7 @@ struct fdcan_extended_filter { * using id2. */ #define FDCAN_EFT_ID_MASK 0x2 -/** Similar to @ref FDCAN_EFT_RANGE except of ignoring global mask +/** Similar to @ref FDCAN_EFT_RANGE except of ignoring global mask * set using @ref FDCAN_XIDAM register. */ #define FDCAN_EFT_RANGE_NOXIDAM 0x3 @@ -715,12 +651,6 @@ struct fdcan_extended_filter { #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. @@ -792,29 +722,6 @@ struct fdcan_tx_buffer_element { #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 @@ -886,7 +793,18 @@ 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); +int fdcan_cccr_init_cfg(uint32_t canport, bool set, uint32_t timeout); +struct fdcan_standard_filter *fdcan_get_flssa_addr(uint32_t canport); +struct fdcan_extended_filter *fdcan_get_flesa_addr(uint32_t canport); + +struct fdcan_rx_fifo_element *fdcan_get_rxfifo_addr(uint32_t canport, + unsigned fifo_id, unsigned element_id); + +struct fdcan_tx_event_element *fdcan_get_txevt_addr(uint32_t canport); +struct fdcan_tx_buffer_element *fdcan_get_txbuf_addr(uint32_t canport, unsigned element_id); +void fdcan_set_fifo_locked_mode(uint32_t canport, bool locked); +uint32_t fdcan_length_to_dlc(uint8_t length); +uint8_t fdcan_dlc_to_length(uint32_t dlc); + END_DECLS - -#endif diff --git a/include/libopencm3/stm32/g4/fdcan.h b/include/libopencm3/stm32/g4/fdcan.h new file mode 100644 index 00000000..4b94625b --- /dev/null +++ b/include/libopencm3/stm32/g4/fdcan.h @@ -0,0 +1,132 @@ +/** @defgroup fdcan_defines FDCAN Defines + +@ingroup STM32G4xx_defines + +@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 . + */ + +#pragma once + +/** @{ */ + +/* 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 +/**@}*/ + +#define CAN_MSG_BASE FDCAN1_RAM_BASE + +#define FDCAN_RXFIS_BASE 0x0090 +#define FDCAN_RXFIA_BASE 0x0094 +#define FDCAN_RXFI_OFFSET 0x0008 + +#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) + +#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) + +#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 + +#define FDCAN_RXFIFO_FL_MASK 0xF +#define FDCAN_RXFIFO_GI_MASK 0x3 +#define FDCAN_RXFIFO_PI_MASK 0x3 +#define FDCAN_RXFIFO_AI_MASK 0x3 + +#define FDCAN_TXFQS_TFFL_MASK 0x7 +#define FDCAN_TXFQS_TFGI_MASK 0x3 +#define FDCAN_TXFQS_TFQPI_MASK 0x3 + +#define FDCAN_TXEFS_EFFL_MASK 0x7 +#define FDCAN_TXEFS_EFGI_MASK 0x3 +#define FDCAN_TXEFS_EFPI_MASK 0x3 + +/* PDIV[3:0]: Input clock divider */ +#define FDCAN_CKDIV_PDIV_SHIFT 0 +#define FDCAN_CKDIV_PDIV_MASK 0xF + +/** 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 + +/** 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 + +#define FDCAN_LFSSA_OFFSET(can_base) ((FDCAN_BLOCK_ID(can_base) * 0x0350) + 0x0000) +#define FDCAN_LFESA_OFFSET(can_base) ((FDCAN_BLOCK_ID(can_base) * 0x0350) + 0x0070) + +#define FDCAN_RXFIFOS_OFFSET(can_base) ((FDCAN_BLOCK_ID(can_base) * 0x0350) + 0x00B0) + +#define FDCAN_RXFIFO_OFFSET(can_base, fifo_id) \ + (FDCAN_RXFIFOS_OFFSET(can_base) + (0x00D8 * (fifo_id))) + +#define FDCAN_TXEVT_OFFSET(can_base) ((FDCAN_BLOCK_ID(can_base) * 0x0350) + 0x0260) + +#define FDCAN_TXBUF_OFFSET(can_base) ((FDCAN_BLOCK_ID(can_base) * 0x0350) + 0x0278) + +BEGIN_DECLS + +unsigned fdcan_get_fifo_element_size(uint32_t canport, unsigned fifo_id); +unsigned fdcan_get_txbuf_element_size(uint32_t canport); + +END_DECLS + + diff --git a/include/libopencm3/stm32/h7/fdcan.h b/include/libopencm3/stm32/h7/fdcan.h new file mode 100644 index 00000000..0a5cbe7a --- /dev/null +++ b/include/libopencm3/stm32/h7/fdcan.h @@ -0,0 +1,257 @@ +/** @defgroup fdcan_defines FDCAN Defines + +@ingroup STM32H7xx_defines + + +@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 . + */ + +#pragma once + +/** @{ */ + +/* 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 +/**@}*/ + +/* Size of FDCAN peripheral message RAM in bytes */ +#define CAN_MSG_SIZE 0x2800 + +#define FDCAN_GFC(can_base) MMIO32(can_base + 0x0080) + +#define FDCAN_SIDFC(can_base) MMIO32(can_base + 0x0084) +#define FDCAN_XIDFC(can_base) MMIO32(can_base + 0x0088) +#define FDCAN_XIDAM(can_base) MMIO32(can_base + 0x0090) + +#define FDCAN_HPMS(can_base) MMIO32(can_base + 0x0094) +#define FDCAN_NDAT1(can_base) MMIO32(can_base + 0x0098) +#define FDCAN_HDAT2(can_base) MMIO32(can_base + 0x009C) + +#define FDCAN_RXFIC_BASE 0x00A0 +#define FDCAN_RXFI_OFFSET 0x0010 + +#define FDCAN_RXFIC(can_base, fifo_id) \ + MMIO32((can_base) + FDCAN_RXFIC_BASE + (FDCAN_RXFI_OFFSET * (fifo_id))) + +#define FDCAN_RXF0C(can_base) FDCAN_RXFIC(can_base, 0) +#define FDCAN_RXF1C(can_base) FDCAN_RXFIC(can_base, 1) + +#define FDCAN_RXFIS_BASE 0x00A4 +#define FDCAN_RXFIA_BASE 0x00A8 + +#define FDCAN_RXBC(can_base) MMIO32(can_base + 0x00AC) + +#define FDCAN_RXESC(can_base) MMIO32(can_base + 0x00BC) +#define FDCAN_TXESC(can_base) MMIO32(can_base + 0x00C8) +#define FDCAN_TXBRP(can_base) MMIO32(can_base + 0x00CC) +#define FDCAN_TXBAR(can_base) MMIO32(can_base + 0x00D0) +#define FDCAN_TXBCR(can_base) MMIO32(can_base + 0x00D4) +#define FDCAN_TXBTO(can_base) MMIO32(can_base + 0x00D8) +#define FDCAN_TXBCF(can_base) MMIO32(can_base + 0x00DC) +#define FDCAN_TXBTIE(can_base) MMIO32(can_base + 0x00E0) +#define FDCAN_TXBCIE(can_base) MMIO32(can_base + 0x00E4) +#define FDCAN_TXEFC(can_base) MMIO32(can_base + 0x00F0) +#define FDCAN_TXEFS(can_base) MMIO32(can_base + 0x00F4) +#define FDCAN_TXEFA(can_base) MMIO32(can_base + 0x00F8) + +#define FDCAN_TTTMC(can_base) MMIO32(can_base + 0x0100) +#define FDCAN_TTRMC(can_base) MMIO32(can_base + 0x0104) +#define FDCAN_TTOCF(can_base) MMIO32(can_base + 0x0108) +#define FDCAN_TTMLM(can_base) MMIO32(can_base + 0x010C) +#define FDCAN_TURCF(can_base) MMIO32(can_base + 0x0110) +#define FDCAN_TTOCN(can_base) MMIO32(can_base + 0x0114) +#define FDCAN_TTGTP(can_base) MMIO32(can_base + 0x0118) +#define FDCAN_TTTMK(can_base) MMIO32(can_base + 0x011C) +#define FDCAN_TTTIR(can_base) MMIO32(can_base + 0x0120) +#define FDCAN_TTIE(can_base) MMIO32(can_base + 0x0124) +#define FDCAN_TTILS(can_base) MMIO32(can_base + 0x0128) +#define FDCAN_TTOST(can_base) MMIO32(can_base + 0x012C) +#define FDCAN_TURNA(can_base) MMIO32(can_base + 0x0130) +#define FDCAN_TTLGT(can_base) MMIO32(can_base + 0x0134) +#define FDCAN_TTCTC(can_base) MMIO32(can_base + 0x0138) +#define FDCAN_TTCPT(can_base) MMIO32(can_base + 0x013C) +#define FDCAN_TTCSM(can_base) MMIO32(can_base + 0x0140) +#define FDCAN_TTTS(can_base) MMIO32(can_base + 0x0300) + +#define FDCAN_CCU_CCFG MMIO32(CAN_CCU_BASE + 0x0004) +#define FDCAN_CCU_CREL MMIO32(CAN_CCU_BASE + 0x0000) + +#define FDCAN_GFC_RRFE (1 << 0) +#define FDCAN_GFC_RRFS (1 << 1) + +/* ANFE[1:0]: Accept non-matching frames w/ extended ID */ +#define FDCAN_GFC_ANFE_SHIFT 2 +#define FDCAN_GFC_ANFE_MASK 0x3 + +/* ANFS[1:0]: Accept non-matching frames w/ standard ID */ +#define FDCAN_GFC_ANFS_SHIFT 4 +#define FDCAN_GFC_ANFS_MASK 0x3 + +#define FDCAN_FXS_MASK 0xFF +#define FDCAN_FXS_SHIFT 16 + +/* Position of start address of relocatable object within register */ +#define FDCAN_FXSA_MASK 0x3FFF +#define FDCAN_FXSA_SHIFT 2 + +/* LSS[7:0]: List size of standard ID filters */ +#define FDCAN_SIDFC_LSS_MASK FDCAN_FXS_MASK +#define FDCAN_SIDFC_LSS_SHIFT FDCAN_FXS_SHIFT + +/* LFSSA[13:0]: Filter List standard start address */ +#define FDCAN_SIDFC_FLSSA_MASK FDCAN_FXSA_MASK +#define FDCAN_SIDFC_FLSSA_SHIFT FDCAN_FXSA_SHIFT + +/* LSE[7:0]: List size of extended ID filters */ +#define FDCAN_XIDFC_LSE_MASK FDCAN_FXS_MASK +#define FDCAN_XIDFC_LSE_SHIFT FDCAN_FXS_SHIFT + +/* LFSSA[7:0]: Filter List extended start address */ +#define FDCAN_XIDFC_FLESA_MASK FDCAN_FXSA_MASK +#define FDCAN_XIDFC_FLESA_SHIFT FDCAN_FXSA_SHIFT + +/* TFQS[5:0]: Tx FIFO/Queue size */ +#define FDCAN_TXBC_TFQS_MASK 0x3F +#define FDCAN_TXBC_TFQS_SHIFT 24 + +/* TBSA[7:0]: Transmit buffer start address */ +#define FDCAN_TXBC_TBSA_MASK FDCAN_FXSA_MASK +#define FDCAN_TXBC_TBSA_SHIFT FDCAN_FXSA_SHIFT + +#define FDCAN_TXEFC_EFS_MASK 0x3F +#define FDCAN_TXEFC_EFS_SHIFT 16 + +/* EFSA[7:0]: (Transmit) event FIFO start address */ +#define FDCAN_TXEFC_EFSA_MASK FDCAN_FXSA_MASK +#define FDCAN_TXEFC_EFSA_SHIFT FDCAN_FXSA_SHIFT + +#define FDCAN_RXFIC_FIOM (1 << 31) + +#define FDCAN_RXFIC_FIWM_MASK 0x7F +#define FDCAN_RXFIC_FIWM_SHIFT 24 + +#define FDCAN_RXFIC_FIS_MASK 0x7F +#define FDCAN_RXFIC_FIS_SHIFT 16 + + +#define FDCAN_RXFIC_FISA_MASK FDCAN_FXSA_MASK +#define FDCAN_RXFIC_FISA_SHIFT FDCAN_FXSA_SHIFT + +#define FDCAN_RXF0C_F0OM FDCAN_RXFIC_FIOM + +/* F0WM[6:0]: FIFO0 watermark mode */ +#define FDCAN_RXF0C_F0WM_MASK FDCAN_RXFIC_FIWM_MASK +#define FDCAN_RXF0C_F0WM_SHIFT FDCAN_RXFIC_FIWM_SHIFT + +/* F0S[6:0]: FIFO0 size */ +#define FDCAN_RXF0C_F0S_MASK FDCAN_RXFIC_FIS_MASK +#define FDCAN_RXF0C_F0S_SHIFT FDCAN_RXFIC_FIS_SHIFT + +/* F0SA[13:0]: FIFO0 start address */ +#define FDCAN_RXF0C_F0SA_MASK FDCAN_RXFIC_FISA_MASK +#define FDCAN_RXF0C_F0SA_SHIFT FDCAN_RXFIC_FISA_SHIFT + +#define FDCAN_RXF1C_F1OM FDCAN_RXFIC_FIOM + +/* F1WM[6:0]: FIFO1 watermark mode */ +#define FDCAN_RXF1C_F1WM_MASK FDCAN_RXFIC_FIWM_MASK +#define FDCAN_RXF1C_F1WM_SHIFT FDCAN_RXFIC_FIWM_SHIFT + +/* F1S[6:0]: FIFO1 size */ +#define FDCAN_RXF1C_F1S_MASK FDCAN_RXFIC_FIS_MASK +#define FDCAN_RXF1C_F1S_SHIFT FDCAN_RXFIC_FIS_SHIFT + +/* F1SA[13:0]: FIFO1 start address */ +#define FDCAN_RXF1C_F1SA_MASK FDCAN_RXFIC_FISA_MASK +#define FDCAN_RXF1C_F1SA_SHIFT FDCAN_RXFIC_FISA_SHIFT + +/* RBDS[3:0]: RX buffer data field size */ +#define FDCAN_RXESC_RBDS_MASK 0x7 +#define FDCAN_RXESC_RBDS_SHIFT 8 + +/* F0DS[3:0]: FIFO0 data field size */ +#define FDCAN_RXESC_F0DS_MASK 0x7 +#define FDCAN_RXESC_F0DS_SHIFT 0 + +/* F1DS[3:0]: FIFO1 data field size */ +#define FDCAN_RXESC_F1DS_MASK 0x7 +#define FDCAN_RXESC_F1DS_SHIFT 4 + +/* TBDS[3:0]: TX buffer data field size */ +#define FDCAN_TXESC_TBDS_MASK 0x7 +#define FDCAN_TXESC_TBDS_SHIFT 0 + +#define FDCAN_RXFIFO_FL_MASK 0x7F +#define FDCAN_RXFIFO_GI_MASK 0x3F +#define FDCAN_RXFIFO_PI_MASK 0x3F + +#define FDCAN_RXFIFO_AI_MASK 0x3F + +#define FDCAN_TXFQS_TFFL_MASK 0x3F +#define FDCAN_TXFQS_TFGI_MASK 0x1F +#define FDCAN_TXFQS_TFQPI_MASK 0x1F + +#define FDCAN_TXEFS_EFFL_MASK 0x3F +#define FDCAN_TXEFS_EFGI_MASK 0x1F +#define FDCAN_TXEFS_EFPI_MASK 0x1F + +/* PDIV[3:0]: Input clock divider */ +#define FDCAN_CCU_CCFG_CDIV_SHIFT 16 +#define FDCAN_CCU_CCFG_CDIV_MASK 0xF + + + +#define FDCAN_LFSSA_OFFSET(can_base) \ + (FDCAN_SIDFC(can_base) & (FDCAN_SIDFC_FLSSA_MASK << FDCAN_SIDFC_FLSSA_SHIFT)) + +#define FDCAN_LFESA_OFFSET(can_base) \ + (FDCAN_XIDFC(can_base) & (FDCAN_XIDFC_FLESA_MASK << FDCAN_XIDFC_FLESA_SHIFT)) + +#define FDCAN_RXFIFO_OFFSET(can_base, fifo_id) \ + (FDCAN_RXFIC(can_base, fifo_id) & (FDCAN_FXSA_MASK << FDCAN_FXSA_SHIFT)) + +#define FDCAN_TXBUF_OFFSET(can_base) \ + (FDCAN_TXBC(can_base) & (FDCAN_TXBC_TBSA_MASK << FDCAN_TXBC_TBSA_SHIFT)) + +#define FDCAN_TXEVT_OFFSET(can_base) \ + (FDCAN_TXEFC(can_base) & (FDCAN_TXEFC_EFSA_MASK << FDCAN_TXEFC_EFSA_SHIFT)) + +BEGIN_DECLS + +void fdcan_init_std_filter_ram(uint32_t canport, uint32_t flssa, uint8_t lss); +void fdcan_init_ext_filter_ram(uint32_t canport, uint32_t flesa, uint8_t lse); +void fdcan_init_fifo_ram(uint32_t canport, unsigned fifo_id, uint32_t fxsa, uint8_t fxs); +void fdcan_init_tx_event_ram(uint32_t canport, uint32_t tesa, uint8_t tes); +void fdcan_init_tx_buffer_ram(uint32_t canport, uint32_t tbsa, uint8_t tbs); +unsigned fdcan_get_fifo_element_size(uint32_t canport, unsigned fifo_id); +unsigned fdcan_get_txbuf_element_size(uint32_t canport); +int fdcan_set_rx_element_size(uint32_t canport, uint8_t rxbuf, uint8_t rxfifo0, uint8_t rxfifo1); +int fdcan_set_tx_element_size(uint32_t canport, uint8_t txbuf); + +END_DECLS + diff --git a/lib/stm32/common/fdcan.c b/lib/stm32/common/fdcan_common.c similarity index 73% rename from lib/stm32/common/fdcan.c rename to lib/stm32/common/fdcan_common.c index 30e6956a..ef7eeab9 100644 --- a/lib/stm32/common/fdcan.c +++ b/lib/stm32/common/fdcan_common.c @@ -1,22 +1,3 @@ -/** @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. * @@ -53,11 +34,11 @@ * @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. + * 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) +int fdcan_cccr_init_cfg(uint32_t canport, bool set, uint32_t timeout) { uint32_t expected; uint32_t wait_ack; @@ -135,43 +116,71 @@ static void fdcan_get_fill_rxfifo(uint32_t canport, uint8_t fifo_id, unsigned *g & FDCAN_RXFIFO_FL_MASK; } -/** Obtain address of FDCAN Message RAM for certain FDCAN block. +/** Returns standard filter start address in message RAM * - * @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. + * @param [in] canport FDCAN block base address. See @ref fdcan_block. + * @returns Base address of standard filter configuration block. */ -static struct fdcan_message_ram *fdcan_get_msgram_addr(uint32_t canport) +struct fdcan_standard_filter *fdcan_get_flssa_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))); - } + struct fdcan_standard_filter *lfssa = (struct fdcan_standard_filter *) + (CAN_MSG_BASE + FDCAN_LFSSA_OFFSET(canport)); + return lfssa; +} - return NULL; +/** Returns extended filter start address in message RAM + * + * @param [in] canport FDCAN block base address. See @ref fdcan_block. + * @returns Base address of extended filter configuration block. + */ +struct fdcan_extended_filter *fdcan_get_flesa_addr(uint32_t canport) +{ + struct fdcan_extended_filter *lfesa = (struct fdcan_extended_filter *) + (CAN_MSG_BASE + FDCAN_LFESA_OFFSET(canport)); + return lfesa; +} + +/** Returns FIFO start address in message RAM + * + * @param [in] canport FDCAN block base address. See @ref fdcan_block. + * @param [in] fifo_id ID of FIFO whose address is requested + * @returns Base address of FIFO block. + */ +struct fdcan_rx_fifo_element *fdcan_get_rxfifo_addr(uint32_t canport, + unsigned fifo_id, unsigned element_id) +{ + struct fdcan_rx_fifo_element *rxfifo = (struct fdcan_rx_fifo_element *) + (CAN_MSG_BASE + FDCAN_RXFIFO_OFFSET(canport, fifo_id) + + (element_id * fdcan_get_fifo_element_size(canport, fifo_id)) + ); + return rxfifo; +} + +/** Returns transmit event start address in message RAM + * + * @param [in] canport FDCAN block base address. See @ref fdcan_block. + * @returns Base address of transmit event block. + */ +struct fdcan_tx_event_element *fdcan_get_txevt_addr(uint32_t canport) +{ + struct fdcan_tx_event_element *rxfifo = (struct fdcan_tx_event_element *) + (CAN_MSG_BASE + FDCAN_TXEVT_OFFSET(canport)); + return rxfifo; +} + +/** Returns transmit buffer start address in message RAM + * + * @param [in] canport FDCAN block base address. See @ref fdcan_block. + * @returns Base address of transmit buffer block. + */ +struct fdcan_tx_buffer_element *fdcan_get_txbuf_addr(uint32_t canport, unsigned element_id) +{ + struct fdcan_tx_buffer_element *rxfifo = (struct fdcan_tx_buffer_element *) + (CAN_MSG_BASE + FDCAN_TXBUF_OFFSET(canport) + + (element_id * fdcan_get_txbuf_element_size(canport)) + ); + + return rxfifo; } /** Converts frame length to DLC value. @@ -183,7 +192,7 @@ static struct fdcan_message_ram *fdcan_get_msgram_addr(uint32_t canport) * @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) +uint32_t fdcan_length_to_dlc(uint8_t length) { if (length <= 8) { return length; @@ -207,7 +216,7 @@ static uint32_t fdcan_length_to_dlc(uint8_t length) * @param [in] dlc DLC value * @returns data payload length in bytes */ -static uint8_t fdcan_dlc_to_length(uint32_t dlc) +uint8_t fdcan_dlc_to_length(uint32_t dlc) { if (dlc <= 8) { return dlc; @@ -237,13 +246,13 @@ static uint8_t fdcan_dlc_to_length(uint32_t dlc) * * @ref fdcan_init_filter * * @ref fdcan_set_test * - * You can check if FDCAN block is in INIT mode or it is started using + * 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. + * 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) @@ -309,16 +318,11 @@ void fdcan_set_can(uint32_t canport, bool auto_retry_disable, bool rx_fifo_locke 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; - } - + fdcan_set_fifo_locked_mode(canport, rx_fifo_locked); } /** 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. @@ -381,36 +385,6 @@ void fdcan_set_test(uint32_t canport, bool testing, bool loopback) } } -/** 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. @@ -422,59 +396,6 @@ 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 @@ -483,20 +404,20 @@ void fdcan_init_filter(uint32_t canport, uint8_t std_filt, uint8_t ext_filt) * * @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] 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. + * @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); + struct fdcan_standard_filter *lfssa = fdcan_get_flssa_addr(canport); /* id_list_mode and action are passed unguarded. Simply use * defines and it will be OK. id1 and id2 are masked for @@ -506,7 +427,7 @@ void fdcan_set_std_filter(uint32_t canport, uint32_t nr, * overflow into flags. This tends to be extremely time * consuming to debug. */ - ram->lfssa[nr].type_id1_conf_id2 = + lfssa[nr].type_id1_conf_id2 = (id_list_mode << FDCAN_SFT_SHIFT) | (action << FDCAN_SFEC_SHIFT) | ((id1 & FDCAN_SFID1_MASK) << FDCAN_SFID1_SHIFT) @@ -524,25 +445,25 @@ void fdcan_set_std_filter(uint32_t canport, uint32_t nr, * @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. + * 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. + * 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); + struct fdcan_extended_filter *lfesa = fdcan_get_flesa_addr(canport); - ram->lfesa[nr].conf_id1 = + lfesa[nr].conf_id1 = (action << FDCAN_EFEC_SHIFT) | ((id1 & FDCAN_EFID1_MASK) << FDCAN_EFID1_SHIFT); - ram->lfesa[nr].type_id2 = + lfesa[nr].type_id2 = (id_list_mode << FDCAN_EFT_SHIFT) | ((id2 & FDCAN_EFID2_MASK) << FDCAN_EFID2_SHIFT); } @@ -572,7 +493,7 @@ int fdcan_transmit(uint32_t canport, uint32_t id, bool ext, bool rtr, return mailbox; } - struct fdcan_message_ram *ram = fdcan_get_msgram_addr(canport); + struct fdcan_tx_buffer_element *tx_buffer = fdcan_get_txbuf_addr(canport, mailbox); /* Early check: if FDCAN message lentgh is > 8, it must be * a multiple of 4 *and* fdcan format must be enabled. @@ -584,15 +505,15 @@ int fdcan_transmit(uint32_t canport, uint32_t id, bool ext, bool rtr, } if (ext) { - ram->tx_buffer[mailbox].identifier_flags = FDCAN_FIFO_XTD + tx_buffer->identifier_flags = FDCAN_FIFO_XTD | ((id & FDCAN_FIFO_EID_MASK) << FDCAN_FIFO_EID_SHIFT); } else { - ram->tx_buffer[mailbox].identifier_flags = + tx_buffer->identifier_flags = (id & FDCAN_FIFO_SID_MASK) << FDCAN_FIFO_SID_SHIFT; } if (rtr) { - ram->tx_buffer[mailbox].identifier_flags |= FDCAN_FIFO_RTR; + tx_buffer->identifier_flags |= FDCAN_FIFO_RTR; } if (fdcan_fmt) { @@ -603,11 +524,11 @@ int fdcan_transmit(uint32_t canport, uint32_t id, bool ext, bool rtr, flags |= FDCAN_FIFO_BRS; } - ram->tx_buffer[mailbox].evt_fmt_dlc_res = + tx_buffer->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]); + tx_buffer->data[q / 4] = *((uint32_t *) &data[q]); } FDCAN_TXBAR(canport) |= 1 << mailbox; @@ -640,52 +561,49 @@ 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) + const struct fdcan_rx_fifo_element *fifo = fdcan_get_rxfifo_addr(canport, + fifo_id, get_index); + + dlc = (fifo->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) { + if ((fifo->identifier_flags & FDCAN_FIFO_XTD) == FDCAN_FIFO_XTD) { *ext = true; - *id = (fifo[get_index].identifier_flags >> FDCAN_FIFO_EID_SHIFT) + *id = (fifo->identifier_flags >> FDCAN_FIFO_EID_SHIFT) & FDCAN_FIFO_EID_MASK; } else { *ext = false; - *id = (fifo[get_index].identifier_flags >> FDCAN_FIFO_SID_SHIFT) + *id = (fifo->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) + *timestamp = (uint16_t) (fifo->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) + *fmi = (uint8_t) (fifo->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); + *rtr = ((fifo->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]; + *((uint32_t *) &data[q]) = fifo->data[q / 4]; } if (release) { diff --git a/lib/stm32/g4/Makefile b/lib/stm32/g4/Makefile index 63a13e81..70318c6f 100644 --- a/lib/stm32/g4/Makefile +++ b/lib/stm32/g4/Makefile @@ -40,7 +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 += fdcan.o fdcan_common.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 diff --git a/lib/stm32/g4/fdcan.c b/lib/stm32/g4/fdcan.c new file mode 100644 index 00000000..b2193587 --- /dev/null +++ b/lib/stm32/g4/fdcan.c @@ -0,0 +1,187 @@ +/** @addtogroup 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 functions ----------------------------------------------------- */ + +/** @ingroup fdcan_file */ +/**@{ + * */ + +/** Returns actual size of FIFO entry in FIFO for given CAN port and FIFO. + * + * Obtains value of FIFO entry length. For G4 it returns constant value as + * G4 has FIFO element length hardcoded. + * @param [in] canport FDCAN block base address. See @ref fdcan_block. Unused. + * @param [in] fifo_id ID of FIFO whole length is queried. Unused. + * @returns Length of FIFO entry length covering frame header and frame payload. + */ +unsigned fdcan_get_fifo_element_size(uint32_t canport, unsigned fifo_id) +{ + /* Silences compiler. Variables are present for API compatibility + * with STM32H7 + */ + (void) (canport); + (void) (fifo_id); + return sizeof(struct fdcan_rx_fifo_element); +} + +/** Returns actual size of transmit entry in transmit queue/FIFO for given CAN port. + * + * Obtains value of entry length in transmit queue/FIFO. For G4 it returns constant value + * as G4 has transmit buffer entries of fixed length. + * + * @param [in] canport FDCAN block base address. See @ref fdcan_block. Unused. + * @param [in] fifo_id ID of FIFO whole length is queried. Unused. + * @returns Length of FIFO entry length covering frame header and frame payload. + */ +unsigned fdcan_get_txbuf_element_size(uint32_t canport) +{ + /* Silences compiler. Variables are present for API compatibility + * with STM32H7 + */ + (void) (canport); + return sizeof(struct fdcan_tx_buffer_element); +} + +/** 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_standard_filter *lfssa = fdcan_get_flssa_addr(canport); + struct fdcan_extended_filter *lfesa = fdcan_get_flesa_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) { + 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) { + lfesa[q].conf_id1 = 0; + 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)); + } +} + +/** 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; +} + +/** Configure FDCAN FIFO lock mode + * + * This function allows to choose between locked and overewrite mode of FIFOs. In locked mode, + * whenever FIFO is full and new frame arrives, which would normally been stored into given + * FIFO, then frame is dropped. If overwrite mode is active, then most recent message in FIFO + * is rewritten by frame just received. + * @param [in] canport FDCAN block base address. See @ref fdcan_block. + * @param [in] locked true activates locked mode, false activates overwrite mode + */ +void fdcan_set_fifo_locked_mode(uint32_t canport, bool locked) +{ + if (locked) { + FDCAN_RXGFC(canport) &= ~(FDCAN_RXGFC_F1OM | FDCAN_RXGFC_F0OM); + } else { + FDCAN_RXGFC(canport) |= FDCAN_RXGFC_F1OM | FDCAN_RXGFC_F0OM; + } +} + +/** @} */ diff --git a/lib/stm32/h7/Makefile b/lib/stm32/h7/Makefile index 5a133ec7..2c7cbb18 100644 --- a/lib/stm32/h7/Makefile +++ b/lib/stm32/h7/Makefile @@ -39,6 +39,7 @@ ARFLAGS = rcs OBJS += dac_common_all.o dac_common_v2.o OBJS += exti_common_all.o +OBJS += fdcan.o fdcan_common.o OBJS += flash_common_all.o flash_common_f.o flash_common_f24.o OBJS += fmc_common_f47.o OBJS += gpio_common_all.o gpio_common_f0234.o diff --git a/lib/stm32/h7/fdcan.c b/lib/stm32/h7/fdcan.c new file mode 100644 index 00000000..fd0cad0e --- /dev/null +++ b/lib/stm32/h7/fdcan.c @@ -0,0 +1,473 @@ +/** @addtogroup fdcan_file FDCAN peripheral API + * + * @ingroup peripheral_apis + * + * @brief libopencm3 STM32 FDCAN + * + * @version 1.0.0 + * + * @author @htmlonly © @endhtmlonly 2021 Eduard Drusa + * + * Device is equipped with two 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 transmit buffer all of configurable amount of + * entries. For transmitted messages it is possible to opt for event notification once message + * is transmitted. + * + * The FDCAN peripheral present in STM32 H7 is a superset of FDCAN peripheral found in other MCUs + * such as STM32 G4. It allows more fine-grade control over buffer and filter allocation and + * supports TTCAN on CAN1, etc. This driver provides source-level backwards compatible + * implementation of driver, which allows build of unmodified software originally written for + * STM32 G4 on STM32 H7. + * + * 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 + +#define FDCAN_LSS_COUNT(can_base) \ + ((FDCAN_SIDFC(can_base) >> FDCAN_SIDFC_LSS_SHIFT) & FDCAN_SIDFC_LSS_MASK) + +#define FDCAN_LSE_COUNT(can_base) \ + ((FDCAN_XIDFC(can_base) >> FDCAN_XIDFC_LSE_SHIFT) & FDCAN_XIDFC_LSE_MASK) + +/* --- FD-CAN functions ----------------------------------------------------- */ + +/** @ingroup fdcan_file */ +/**@{ + * */ + +/** Returns actual size of FIFO entry in FIFO for given CAN port and FIFO. + * + * Obtains value of FIFO entry length. This value covers both fixed-size frame + * header block and payload buffer. User can configure payload buffer size individually + * for each FIFO and designated RX buffer. + * + * @param [in] canport FDCAN block base address. See @ref fdcan_block. + * @param [in] fifo_id ID of FIFO whole length is queried. + * @returns Length of FIFO entry length covering frame header and frame payload. + */ +unsigned fdcan_get_fifo_element_size(uint32_t canport, unsigned fifo_id) +{ + unsigned element_size; + if (fifo_id == 0) { + element_size = FDCAN_RXESC(canport) >> FDCAN_RXESC_F0DS_SHIFT; + } else { + element_size = FDCAN_RXESC(canport) >> FDCAN_RXESC_F1DS_SHIFT; + } + + /* Mask is unshifted and at this point, element_size is unshifted too */ + return 8 + fdcan_dlc_to_length((element_size & FDCAN_RXESC_F0DS_MASK) | 0x7); +} + +/** Returns actual size of transmit entry in transmit queue/FIFO for given CAN port. + * + * Obtains value of entry length in transmit queue/FIFO. This value covers both + * fixed-sized frame header block and payload buffer. User can configure payload buffer + * size. + * + * @param [in] canport FDCAN block base address. See @ref fdcan_block. + * @param [in] fifo_id ID of FIFO whole length is queried. + * @returns Length of FIFO entry length covering frame header and frame payload. + */ +unsigned fdcan_get_txbuf_element_size(uint32_t canport) +{ + unsigned element_size; + element_size = (FDCAN_TXESC(canport) >> FDCAN_TXESC_TBDS_SHIFT) & FDCAN_TXESC_TBDS_MASK; + return 8 + fdcan_dlc_to_length((element_size & FDCAN_TXESC_TBDS_MASK) | 0x7); +} + +/** Initialize allocation of standard filter block in CAN message RAM. + * + * Allows specifying size of standard filtering block (in term of available filtering + * rules and filter base address within CAN message RAM. Note, that there are no limitations + * nor checking on address provided. It is possible to share whole filtering block or + * portion of it between multiple CAN interfaces. + * @param [in] canport FDCAN block base address. See @ref fdcan_block. + * @param [in] flssa standard filtering block start address offset in message RAM + * @param [in] lss amount of standard filters + */ +void fdcan_init_std_filter_ram(uint32_t canport, uint32_t flssa, uint8_t lss) +{ + FDCAN_SIDFC(canport) = flssa << FDCAN_SIDFC_FLSSA_SHIFT + | lss << FDCAN_SIDFC_LSS_SHIFT; +} + +/** Initialize allocation of extended filter block in CAN message RAM. + * + * Allows specifying size of extended filtering block (in term of available filtering + * rules and filter base address within CAN message RAM. Note, that there are no limitations + * nor checking on address provided. It is possible to share whole filtering block or + * portion of it between multiple CAN interfaces. + * @param [in] canport FDCAN block base address. See @ref fdcan_block. + * @param [in] flesa extended filtering block start address offset in message RAM + * @param [in] lse amount of extended filters + */ +void fdcan_init_ext_filter_ram(uint32_t canport, uint32_t flesa, uint8_t lse) +{ + FDCAN_XIDFC(canport) = flesa << FDCAN_XIDFC_FLESA_SHIFT + | lse << FDCAN_XIDFC_LSE_SHIFT; +} + +/** Initialize allocation of FIFO block in CAN message RAM. + * + * Allows specifying size of FIFO block (in term of available messages in FIFO + * and FIFO base address within CAN message RAM. Note, that there are no limitations + * nor checking on address provided. + * @param [in] canport FDCAN block base address. See @ref fdcan_block. + * @param [in] fifo_id ID of fifo being configured + * @param [in] fxsa FIFO block start address offset in message RAM + * @param [in] fxs amount of entries allocated in FIFO + */ +void fdcan_init_fifo_ram(uint32_t canport, unsigned fifo_id, uint32_t fxsa, uint8_t fxs) +{ + FDCAN_RXFIC(canport, fifo_id) = (FDCAN_RXFIC(canport, fifo_id) + & ~( + (FDCAN_RXFIC_FIS_MASK << FDCAN_RXFIC_FIS_SHIFT) + | (FDCAN_RXFIC_FISA_MASK << FDCAN_RXFIC_FISA_SHIFT) + )) + | (fxs << FDCAN_RXFIC_FIS_SHIFT) + | (fxsa & (FDCAN_RXFIC_FISA_MASK << FDCAN_RXFIC_FISA_SHIFT)); +} + +/** Initialize allocation of transmit event block in CAN message RAM. + * + * Allows specifying size of transmit event block (in term of allocated events and block + * base address within CAN message RAM. Note, that there are no limitations + * nor checking on address provided. + * @param [in] canport FDCAN block base address. See @ref fdcan_block. + * @param [in] fifo_id ID of fifo being configured + * @param [in] fxsa FIFO block start address offset in message RAM + * @param [in] fxs amount of entries allocated in FIFO + */ +void fdcan_init_tx_event_ram(uint32_t canport, uint32_t tesa, uint8_t tes) +{ + FDCAN_TXEFC(canport) = (FDCAN_TXEFC(canport) + & ~( + (FDCAN_TXEFC_EFS_MASK << FDCAN_TXEFC_EFS_SHIFT) + | (FDCAN_TXEFC_EFSA_MASK << FDCAN_TXEFC_EFSA_SHIFT) + )) + | (tes << FDCAN_TXEFC_EFS_SHIFT) + | (tesa & (FDCAN_TXEFC_EFSA_MASK << FDCAN_TXEFC_EFSA_SHIFT)); +} + +/** Initialize allocation of transmit queue block in CAN message RAM. + * + * Allows specifying size of transmit queue block (in term of allocated buffers and block + * base address within CAN message RAM. Note, that there are no limitations + * nor checking on address provided. + * @param [in] canport FDCAN block base address. See @ref fdcan_block. + * @param [in] fifo_id ID of fifo being configured + * @param [in] fxsa FIFO block start address offset in message RAM + * @param [in] fxs amount of entries allocated in FIFO + */ +void fdcan_init_tx_buffer_ram(uint32_t canport, uint32_t tbsa, uint8_t tbs) +{ + FDCAN_TXBC(canport) = (FDCAN_TXBC(canport) + & ~( + (FDCAN_TXBC_TFQS_MASK << FDCAN_TXBC_TFQS_SHIFT) + | (FDCAN_TXBC_TBSA_MASK << FDCAN_TXBC_TBSA_SHIFT) + )) + | (tbs << FDCAN_TXBC_TFQS_SHIFT) + | (tbsa & (FDCAN_TXBC_TBSA_MASK << FDCAN_TXBC_TBSA_SHIFT)); +} + +/** Initialize size of data fields in reception buffers. + * + * Configures maximum size of message payload, which can be stored in different + * reception buffers. Each buffer can have maximum payload size configured independently. + * Buffers can only be configured to sizes which are valid sizes of FDCAN payload. Sizes smaller + * than 8 are automatically padded to 8. + * @note If you change these values, then content of reception buffers is invalidated. You may + * also want to recalculate base addresses of objects in message RAM as their size might have + * changed. + * @param [in] canport FDCAN block base address. See @ref fdcan_block. + * @param [in] rxbuf maximum storable payload size of dedicated RX buffer + * @param [in] rxfifo0 maximum storable payload size of RX FIFO 0 + * @param [in] rxfifo1 maximum storable payload size of RX FIFO 1 + * @returns operation return status. See @ref fdcan_error. + */ +int fdcan_set_rx_element_size(uint32_t canport, uint8_t rxbuf, uint8_t rxfifo0, uint8_t rxfifo1) +{ + unsigned rxbufdlc = fdcan_length_to_dlc(rxbuf); + unsigned rxfifo0dlc = fdcan_length_to_dlc(rxfifo0); + unsigned rxfifo1dlc = fdcan_length_to_dlc(rxfifo1); + + if (rxbufdlc == 0xFF || rxfifo0dlc == 0xFF || rxfifo1dlc == 0xFF) { + return FDCAN_E_INVALID; + } + + /* RXESC fields use format of FDCAN DLC, albeit using only + * three LSBs. DLC < 8 is always allocated as 8 bytes and is always + * encoded as 0. + */ + if (rxbufdlc <= 8) { + rxbufdlc = 0; + } else { + rxbufdlc &= ~(1 << 4); + } + + if (rxfifo0dlc <= 8) { + rxfifo0dlc = 0; + } else { + rxfifo0dlc &= ~(1 << 4); + } + + if (rxfifo1dlc <= 8) { + rxfifo1dlc = 0; + } else { + rxfifo1dlc &= ~(1 << 4); + } + + FDCAN_RXESC(canport) = rxbufdlc << FDCAN_RXESC_RBDS_SHIFT + | rxfifo1dlc << FDCAN_RXESC_F1DS_SHIFT + | rxfifo0dlc << FDCAN_RXESC_F0DS_SHIFT; + + return FDCAN_E_OK; +} + +/** Initialize size of data fields in transmit buffers. + * + * Configures maximum size of message payload, which can be stored either in dedicated + * transmit buffer or into transmit queue/FIFO. One size is applied both to transmit buffer + * and transmit queue / FIFO. Buffers can only be configured to sizes which are valid sizes + * of FDCAN payload. Sizes smaller than 8 are automatically padded to 8 bytes. + * @note If you change these values, then content of transmission buffers is invalidated. You may + * also want to recalculate base addresses of objects in message RAM as their size might have + * changed. + * @param [in] canport FDCAN block base address. See @ref fdcan_block. + * @param [in] txbuf maximum storable payload size of TX buffer / FIFO / queue + * @returns operation return status. See @ref fdcan_error. + */ +int fdcan_set_tx_element_size(uint32_t canport, uint8_t txbuf) +{ + unsigned txbufdlc = fdcan_length_to_dlc(txbuf); + + if (txbufdlc == 0xFF) { + return FDCAN_E_INVALID; + } + + if (txbufdlc <= 8) { + txbufdlc = 0; + } else { + txbufdlc &= ~(1 << 4); + } + + FDCAN_TXESC(canport) = txbufdlc << FDCAN_TXESC_TBDS_SHIFT; + + return FDCAN_E_OK; +} + +/** 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. + * + * This function is provided for source level compatibility with code written for + * STM32G4. As an alternative, you can use @ref fdcan_init_std_filter_ram() and + * @ref fdcan_init_ext_filter_ram() to have more control over filtering configuration. + * Note that if you do so, your code won't be compatible with STM32G4 controllers. + * + * @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_standard_filter *lfssa; + struct fdcan_extended_filter *lfesa; + + int can_id = FDCAN_BLOCK_ID(canport); + + int lfsofs = 0; + int lfeofs = 0; + int lfssize = std_filt * sizeof(struct fdcan_standard_filter); + int lfesize = ext_filt * sizeof(struct fdcan_extended_filter); + + if (can_id == 0) { + lfsofs = 0; + lfeofs = lfssize; + } else { + lfsofs = CAN_MSG_BASE + CAN_MSG_SIZE - lfssize; + lfeofs = lfsofs - lfesize; + } + + fdcan_init_std_filter_ram(canport, lfsofs, 28); + fdcan_init_ext_filter_ram(canport, lfeofs, 8); + + lfssa = fdcan_get_flssa_addr(canport); + lfesa = fdcan_get_flesa_addr(canport); + + /* Only perform initialization of message RAM if there are + * any filters required + */ + if (std_filt > 0) { + for (int q = 0; q < 28; ++q) { + lfssa[q].type_id1_conf_id2 = 0; + } + } + + if (ext_filt > 0) { + for (int q = 0; q < 8; ++q) { + lfesa[q].conf_id1 = 0; + lfesa[q].type_id2 = 0; + } + } +} + +/** 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) +{ + /* This block detects obviously invalid configuration of + * RX FIFOs and TX buffers. Such situation may happen + * if code originally targeted for STM32G4 is compiled for + * STM32H7. This code is not aware of H7 abilities and + * leaves this stuff unconfigured. We pick up here and + * assume, that the code wants the FDCAN to behave as if + * it was STM32G4. Therefore we will configure two three entry + * long RX FIFOs, three entry long TX event buffer and three + * entry long TX FIFO/queue here. + */ + if (FDCAN_RXFIFO_OFFSET(canport, 0) == 0 + && FDCAN_RXFIFO_OFFSET(canport, 1) == 0 + && FDCAN_TXBUF_OFFSET(canport) == 0 + && FDCAN_TXEVT_OFFSET(canport) == 0 + && FDCAN_RXESC(canport) == 0 + && FDCAN_TXESC(canport) == 0 + ) { + /* These sizes are fixed based on what G4 contains. */ + int fifo0_size = 3 * sizeof(struct fdcan_rx_fifo_element); + int fifo1_size = fifo0_size; + int txevt_size = 3 * sizeof(struct fdcan_tx_event_element); + int txbuf_size = 3 * sizeof(struct fdcan_tx_buffer_element); + + fdcan_set_rx_element_size(canport, 0, 64, 64); + fdcan_set_tx_element_size(canport, 64); + + /* At this point we simply assume that FLSSA and FLESA were + * set up by calling fdcan_init_filter(). It they weren't, + * there just won't be allocated any space for them. + * That's not a problem as after fdcan_start returns, it is + * not possible to configure filter amount anymore. + * + * Default approach is to configure CAN1 to occupy bottom + * of message RAM and CAN1 to occupy top of message RAM keeping + * large unused space in between. This ensures that even if someone + * starts playing with CAN1, "implicit" configuration of CAN2 done + * here won't break things magically. + */ + if (FDCAN_BLOCK_ID(canport) == 0) { + /* CAN1 will use bottom-up layout with + * FLSSA < FLESA < F0SA < F1SA < TXESA < TXBSA + * Rx buffer is not used. + */ + fdcan_init_fifo_ram(canport, 0, + FDCAN_LFESA_OFFSET(canport) + + FDCAN_LSE_COUNT(canport) * sizeof(struct fdcan_extended_filter), + 3); + fdcan_init_fifo_ram(canport, 1, + FDCAN_RXFIFO_OFFSET(canport, 0) + fifo0_size, 3); + fdcan_init_tx_event_ram(canport, + FDCAN_RXFIFO_OFFSET(canport, 1) + fifo1_size, 3); + fdcan_init_tx_buffer_ram(canport, + FDCAN_TXEVT_OFFSET(canport) + txevt_size, 3); + } else { + /* LFESA might be uninitialized. In such case + * we forge it's address at the end of MSG RAM + */ + int lfesa_offs = FDCAN_LFESA_OFFSET(canport); + if (lfesa_offs == 0) { + lfesa_offs = CAN_MSG_SIZE; + } + /* CAN2 will use top-down layout with + * TXBSA < TXESA < F1SA < F0SA < FLESA < FLSSA + * Rx buffer is not used. + * This arrangement should ensure that even if + * CAN1 was configured manually, CAN2 default + * configuration should not break CAN1. + */ + fdcan_init_fifo_ram(canport, 0, lfesa_offs - fifo0_size, 3); + fdcan_init_fifo_ram(canport, 1, + FDCAN_RXFIFO_OFFSET(canport, 0) - fifo1_size, 3); + fdcan_init_tx_event_ram(canport, + FDCAN_RXFIFO_OFFSET(canport, 1) - txevt_size, 3); + fdcan_init_tx_buffer_ram(canport, + FDCAN_TXEVT_OFFSET(canport) - txbuf_size, 3); + } + + } + /* 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; +} + +/** Configure FDCAN FIFO lock mode + * + * This function allows to choose between locked and overewrite mode of FIFOs. In locked mode, + * whenever FIFO is full and new frame arrives, which would normally been stored into given + * FIFO, then frame is dropped. If overwrite mode is active, then most recent message in FIFO + * is rewritten by frame just received. + * @param [in] canport FDCAN block base address. See @ref fdcan_block. + * @param [in] locked true activates locked mode, false activates overwrite mode + */ +void fdcan_set_fifo_locked_mode(uint32_t canport, bool locked) +{ + if (locked) { + FDCAN_RXF0C(canport) &= ~(FDCAN_RXF0C_F0OM); + FDCAN_RXF1C(canport) &= ~(FDCAN_RXF1C_F1OM); + } else { + FDCAN_RXF0C(canport) |= FDCAN_RXF0C_F0OM; + FDCAN_RXF1C(canport) |= FDCAN_RXF1C_F1OM; + } +} +