diff --git a/arch/arm/src/stm32f7/Kconfig b/arch/arm/src/stm32f7/Kconfig index 2031bf5fb48..6bb06ac3440 100644 --- a/arch/arm/src/stm32f7/Kconfig +++ b/arch/arm/src/stm32f7/Kconfig @@ -4769,6 +4769,52 @@ config STM32F7_TIM14_CAP endmenu # Timer Configuration +menu "CAN driver configuration" + depends on STM32F7_CAN + +config STM32F7_CAN1_BAUD + int "CAN1 BAUD" + default 250000 + depends on STM32F7_CAN1 + ---help--- + CAN1 BAUD rate. Required if CONFIG_STM32F7_CAN1 is defined. + +config STM32F7_CAN2_BAUD + int "CAN2 BAUD" + default 250000 + depends on STM32F7_CAN2 + ---help--- + CAN2 BAUD rate. Required if CONFIG_STM32F7_CAN2 is defined. + +config STM32F7_CAN3_BAUD + int "CAN3 BAUD" + default 250000 + depends on STM32F7_CAN3 + ---help--- + CAN2 BAUD rate. Required if CONFIG_STM32F7_CAN2 is defined. + +config STM32F7_CAN_TSEG1 + int "TSEG1 quanta" + default 6 + ---help--- + The number of CAN time quanta in segment 1. Default: 6 + +config STM32F7_CAN_TSEG2 + int "TSEG2 quanta" + default 7 + ---help--- + The number of CAN time quanta in segment 2. Default: 7 + +config STM32F7_CAN_REGDEBUG + bool "CAN Register level debug" + depends on DEBUG_CAN_INFO + default n + ---help--- + Output detailed register-level CAN device debug information. + Requires also CONFIG_DEBUG_CAN_INFO. + +endmenu + menu "ADC Configuration" depends on STM32F7_ADC diff --git a/arch/arm/src/stm32f7/Make.defs b/arch/arm/src/stm32f7/Make.defs index 9e2704d7f7e..e00d08239e8 100644 --- a/arch/arm/src/stm32f7/Make.defs +++ b/arch/arm/src/stm32f7/Make.defs @@ -213,4 +213,8 @@ endif ifeq ($(CONFIG_SENSORS_QENCODER),y) CHIP_CSRCS += stm32_qencoder.c -endif \ No newline at end of file +endif + +ifeq ($(CONFIG_STM32F7_CAN),y) +CHIP_CSRCS += stm32_can.c +endif diff --git a/arch/arm/src/stm32f7/chip/stm32_can.h b/arch/arm/src/stm32f7/chip/stm32_can.h new file mode 100644 index 00000000000..8de35e9f19c --- /dev/null +++ b/arch/arm/src/stm32f7/chip/stm32_can.h @@ -0,0 +1,563 @@ +/************************************************************************************ + * arch/arm/src/stm32/chip/stm32_can.h + * + * Copyright (C) 2009, 2011, 2013, 2019 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ************************************************************************************/ + +#ifndef __ARCH_ARM_SRC_STM32_CHIP_STM32F7_CAN_H +#define __ARCH_ARM_SRC_STM32_CHIP_STM32F7_CAN_H + +/************************************************************************************ + * Included Files + ************************************************************************************/ + +#include + +#include "chip.h" + +/************************************************************************************ + * Pre-processor Definitions + ************************************************************************************/ + +/* 3 TX mailboxes */ + +#define CAN_TXMBOX1 0 +#define CAN_TXMBOX2 1 +#define CAN_TXMBOX3 2 + +/* 2 RX mailboxes */ + +#define CAN_RXMBOX1 0 +#define CAN_RXMBOX2 1 + +/* Number of filters depends on silicon */ + +#if defined(CONFIG_STM32_CONNECTIVITYLINE) || defined(CONFIG_STM32_STM32F20XX) || \ + defined(CONFIG_STM32_STM32F30XX) || defined(CONFIG_STM32_STM32F4XXX) +# define CAN_NFILTERS 28 +#else +# define CAN_NFILTERS 14 +#endif + +/* Register Offsets *****************************************************************/ + +/* CAN control and status registers */ + +#define STM32_CAN_MCR_OFFSET 0x0000 /* CAN master control register */ +#define STM32_CAN_MSR_OFFSET 0x0004 /* CAN master status register */ +#define STM32_CAN_TSR_OFFSET 0x0008 /* CAN transmit status register */ + +#define STM32_CAN_RFR_OFFSET(m) (0x000c+((m)<<2)) +#define STM32_CAN_RF0R_OFFSET 0x000c /* CAN receive FIFO 0 register */ +#define STM32_CAN_RF1R_OFFSET 0x0010 /* CAN receive FIFO 1 register */ + +#define STM32_CAN_IER_OFFSET 0x0014 /* CAN interrupt enable register */ +#define STM32_CAN_ESR_OFFSET 0x0018 /* CAN error status register */ +#define STM32_CAN_BTR_OFFSET 0x001c /* CAN bit timing register */ + +/* CAN mailbox registers (3 TX and 2 RX) */ + +#define STM32_CAN_TIR_OFFSET(m) (0x0180+((m)<<4)) +#define STM32_CAN_TI0R_OFFSET 0x0180 /* TX mailbox identifier register 0 */ +#define STM32_CAN_TI1R_OFFSET 0x0190 /* TX mailbox identifier register 1 */ +#define STM32_CAN_TI2R_OFFSET 0x01a0 /* TX mailbox identifier register 2 */ + +#define STM32_CAN_TDTR_OFFSET(m) (0x0184+((m)<<4)) +#define STM32_CAN_TDT0R_OFFSET 0x0184 /* Mailbox data length control and time stamp register 0 */ +#define STM32_CAN_TDT1R_OFFSET 0x0194 /* Mailbox data length control and time stamp register 1 */ +#define STM32_CAN_TDT2R_OFFSET 0x01a4 /* Mailbox data length control and time stamp register 2 */ + +#define STM32_CAN_TDLR_OFFSET(m) (0x0188+((m)<<4)) +#define STM32_CAN_TDL0R_OFFSET 0x0188 /* Mailbox data low register 0 */ +#define STM32_CAN_TDL1R_OFFSET 0x0198 /* Mailbox data low register 1 */ +#define STM32_CAN_TDL2R_OFFSET 0x01a8 /* Mailbox data low register 2 */ + +#define STM32_CAN_TDHR_OFFSET(m) (0x018c+((m)<<4)) +#define STM32_CAN_TDH0R_OFFSET 0x018c /* Mailbox data high register 0 */ +#define STM32_CAN_TDH1R_OFFSET 0x019c /* Mailbox data high register 1 */ +#define STM32_CAN_TDH2R_OFFSET 0x01ac /* Mailbox data high register 2 */ + +#define STM32_CAN_RIR_OFFSET(m) (0x01b0+((m)<<4)) +#define STM32_CAN_RI0R_OFFSET 0x01b0 /* Rx FIFO mailbox identifier register 0 */ +#define STM32_CAN_RI1R_OFFSET 0x01c0 /* Rx FIFO mailbox identifier register 1 */ + +#define STM32_CAN_RDTR_OFFSET(m) (0x01b4+((m)<<4)) +#define STM32_CAN_RDT0R_OFFSET 0x01b4 /* Rx FIFO mailbox data length control and time stamp register 0 */ +#define STM32_CAN_RDT1R_OFFSET 0x01c4 /* Rx FIFO mailbox data length control and time stamp register 1 */ + +#define STM32_CAN_RDLR_OFFSET(m) (0x01b8+((m)<<4)) +#define STM32_CAN_RDL0R_OFFSET 0x01b8 /* Receive FIFO mailbox data low register 0 */ +#define STM32_CAN_RDL1R_OFFSET 0x01c8 /* Receive FIFO mailbox data low register 1 */ + +#define STM32_CAN_RDHR_OFFSET(m) (0x01bc+((m)<<4)) +#define STM32_CAN_RDH0R_OFFSET 0x01bc /* Receive FIFO mailbox data high register 0 */ +#define STM32_CAN_RDH1R_OFFSET 0x01cc /* Receive FIFO mailbox data high register 1 */ + +/* CAN filter registers */ + +#define STM32_CAN_FMR_OFFSET 0x0200 /* CAN filter master register */ +#define STM32_CAN_FM1R_OFFSET 0x0204 /* CAN filter mode register */ +#define STM32_CAN_FS1R_OFFSET 0x020c /* CAN filter scale register */ +#define STM32_CAN_FFA1R_OFFSET 0x0214 /* CAN filter FIFO assignment register */ +#define STM32_CAN_FA1R_OFFSET 0x021c /* CAN filter activation register */ + +/* There are 14 or 28 filter banks (depending) on the device. + * Each filter bank is composed of two 32-bit registers, CAN_FiR: + * F0R1 Offset 0x240 + * F0R2 Offset 0x244 + * F1R1 Offset 0x248 + * F1R2 Offset 0x24c + * ... + */ + +#define STM32_CAN_FIR_OFFSET(f,i) (0x240+((f)<<3)+(((i)-1)<<2)) + +/* Register Addresses ***************************************************************/ + +#if defined(CONFIG_STM32F7_CAN1) +# define STM32_CAN1_MCR (STM32_CAN1_BASE+STM32_CAN_MCR_OFFSET) +# define STM32_CAN1_MSR (STM32_CAN1_BASE+STM32_CAN_MSR_OFFSET) +# define STM32_CAN1_TSR (STM32_CAN1_BASE+STM32_CAN_TSR_OFFSET) +# define STM32_CAN1_RF0R (STM32_CAN1_BASE+STM32_CAN_RF0R_OFFSET) +# define STM32_CAN1_RF1R (STM32_CAN1_BASE+STM32_CAN_RF1R_OFFSET) +# define STM32_CAN1_IER (STM32_CAN1_BASE+STM32_CAN_IER_OFFSET) +# define STM32_CAN1_ESR (STM32_CAN1_BASE+STM32_CAN_ESR_OFFSET) +# define STM32_CAN1_BTR (STM32_CAN1_BASE+STM32_CAN_BTR_OFFSET) + +# define STM32_CAN1_TIR(m) (STM32_CAN1_BASE+STM32_CAN_TIR_OFFSET(m)) +# define STM32_CAN1_TI0R (STM32_CAN1_BASE+STM32_CAN_TI0R_OFFSET) +# define STM32_CAN1_TI1R (STM32_CAN1_BASE+STM32_CAN_TI1R_OFFSET) +# define STM32_CAN1_TI2R (STM32_CAN1_BASE+STM32_CAN_TI2R_OFFSET) + +# define STM32_CAN1_TDTR(m) (STM32_CAN1_BASE+STM32_CAN_TDTR_OFFSET(m)) +# define STM32_CAN1_TDT0R (STM32_CAN1_BASE+STM32_CAN_TDT0R_OFFSET) +# define STM32_CAN1_TDT1R (STM32_CAN1_BASE+STM32_CAN_TDT1R_OFFSET) +# define STM32_CAN1_TDT2R (STM32_CAN1_BASE+STM32_CAN_TDT2R_OFFSET) + +# define STM32_CAN1_TDLR(m) (STM32_CAN1_BASE+STM32_CAN_TDLR_OFFSET(m)) +# define STM32_CAN1_TDL0R (STM32_CAN1_BASE+STM32_CAN_TDL0R_OFFSET) +# define STM32_CAN1_TDL1R (STM32_CAN1_BASE+STM32_CAN_TDL1R_OFFSET) +# define STM32_CAN1_TDL2R (STM32_CAN1_BASE+STM32_CAN_TDL2R_OFFSET) + +# define STM32_CAN1_TDHR(m) (STM32_CAN1_BASE+STM32_CAN_TDHR_OFFSET(m)) +# define STM32_CAN1_TDH0R (STM32_CAN1_BASE+STM32_CAN_TDH0R_OFFSET) +# define STM32_CAN1_TDH1R (STM32_CAN1_BASE+STM32_CAN_TDH1R_OFFSET) +# define STM32_CAN1_TDH2R (STM32_CAN1_BASE+STM32_CAN_TDH2R_OFFSET) + +# define STM32_CAN1_RIR(m) (STM32_CAN1_BASE+STM32_CAN_RIR_OFFSET(m)) +# define STM32_CAN1_RI0R (STM32_CAN1_BASE+STM32_CAN_RI0R_OFFSET) +# define STM32_CAN1_RI1R (STM32_CAN1_BASE+STM32_CAN_RI1R_OFFSET) + +# define STM32_CAN1_RDTR(m) (STM32_CAN1_BASE+STM32_CAN_RDTR_OFFSET(m)) +# define STM32_CAN1_RDT0R (STM32_CAN1_BASE+STM32_CAN_RDT0R_OFFSET) +# define STM32_CAN1_RDT1R (STM32_CAN1_BASE+STM32_CAN_RDT1R_OFFSET) + +# define STM32_CAN1_RDLR(m) (STM32_CAN1_BASE+STM32_CAN_RDLR_OFFSET(m)) +# define STM32_CAN1_RDL0R (STM32_CAN1_BASE+STM32_CAN_RDL0R_OFFSET) +# define STM32_CAN1_RDL1R (STM32_CAN1_BASE+STM32_CAN_RDL1R_OFFSET) + +# define STM32_CAN1_RDHR(m) (STM32_CAN1_BASE+STM32_CAN_RDHR_OFFSET(m)) +# define STM32_CAN1_RDH0R (STM32_CAN1_BASE+STM32_CAN_RDH0R_OFFSET) +# define STM32_CAN1_RDH1R (STM32_CAN1_BASE+STM32_CAN_RDH1R_OFFSET) + +# define STM32_CAN1_FMR (STM32_CAN1_BASE+STM32_CAN_FMR_OFFSET) +# define STM32_CAN1_FM1R (STM32_CAN1_BASE+STM32_CAN_FM1R_OFFSET) +# define STM32_CAN1_FS1R (STM32_CAN1_BASE+STM32_CAN_FS1R_OFFSET) +# define STM32_CAN1_FFA1R (STM32_CAN1_BASE+STM32_CAN_FFA1R_OFFSET) +# define STM32_CAN1_FA1R (STM32_CAN1_BASE+STM32_CAN_FA1R_OFFSET) +# define STM32_CAN1_FIR(b,i) (STM32_CAN1_BASE+STM32_CAN_FIR_OFFSET(b,i)) +#endif + +#if defined(CONFIG_STM32F7_CAN2) +# define STM32_CAN2_MCR (STM32_CAN2_BASE+STM32_CAN_MCR_OFFSET) +# define STM32_CAN2_MSR (STM32_CAN2_BASE+STM32_CAN_MSR_OFFSET) +# define STM32_CAN2_TSR (STM32_CAN2_BASE+STM32_CAN_TSR_OFFSET) +# define STM32_CAN2_RF0R (STM32_CAN2_BASE+STM32_CAN_RF0R_OFFSET) +# define STM32_CAN2_RF1R (STM32_CAN2_BASE+STM32_CAN_RF1R_OFFSET) +# define STM32_CAN2_IER (STM32_CAN2_BASE+STM32_CAN_IER_OFFSET) +# define STM32_CAN2_ESR (STM32_CAN2_BASE+STM32_CAN_ESR_OFFSET) +# define STM32_CAN2_BTR (STM32_CAN2_BASE+STM32_CAN_BTR_OFFSET) + +# define STM32_CAN2_TIR(m) (STM32_CAN2_BASE+STM32_CAN_TIR_OFFSET(m)) +# define STM32_CAN2_TI0R (STM32_CAN2_BASE+STM32_CAN_TI0R_OFFSET) +# define STM32_CAN2_TI1R (STM32_CAN2_BASE+STM32_CAN_TI1R_OFFSET) +# define STM32_CAN2_TI2R (STM32_CAN2_BASE+STM32_CAN_TI2R_OFFSET) + +# define STM32_CAN2_TDTR(m) (STM32_CAN2_BASE+STM32_CAN_TDTR_OFFSET(m)) +# define STM32_CAN2_TDT0R (STM32_CAN2_BASE+STM32_CAN_TDT0R_OFFSET) +# define STM32_CAN2_TDT1R (STM32_CAN2_BASE+STM32_CAN_TDT1R_OFFSET) +# define STM32_CAN2_TDT2R (STM32_CAN2_BASE+STM32_CAN_TDT2R_OFFSET) + +# define STM32_CAN2_TDLR(m) (STM32_CAN2_BASE+STM32_CAN_TDLR_OFFSET(m)) +# define STM32_CAN2_TDL0R (STM32_CAN2_BASE+STM32_CAN_TDL0R_OFFSET) +# define STM32_CAN2_TDL1R (STM32_CAN2_BASE+STM32_CAN_TDL1R_OFFSET) +# define STM32_CAN2_TDL2R (STM32_CAN2_BASE+STM32_CAN_TDL2R_OFFSET) + +# define STM32_CAN2_TDHR(m) (STM32_CAN2_BASE+STM32_CAN_TDHR_OFFSET(m)) +# define STM32_CAN2_TDH0R (STM32_CAN2_BASE+STM32_CAN_TDH0R_OFFSET) +# define STM32_CAN2_TDH1R (STM32_CAN2_BASE+STM32_CAN_TDH1R_OFFSET) +# define STM32_CAN2_TDH2R (STM32_CAN2_BASE+STM32_CAN_TDH2R_OFFSET) + +# define STM32_CAN2_RIR(m) (STM32_CAN2_BASE+STM32_CAN_RIR_OFFSET(m)) +# define STM32_CAN2_RI0R (STM32_CAN2_BASE+STM32_CAN_RI0R_OFFSET) +# define STM32_CAN2_RI1R (STM32_CAN2_BASE+STM32_CAN_RI1R_OFFSET) + +# define STM32_CAN2_RDTR(m) (STM32_CAN2_BASE+STM32_CAN_RDTR_OFFSET(m)) +# define STM32_CAN2_RDT0R (STM32_CAN2_BASE+STM32_CAN_RDT0R_OFFSET) +# define STM32_CAN2_RDT1R (STM32_CAN2_BASE+STM32_CAN_RDT1R_OFFSET) + +# define STM32_CAN2_RDLR(m) (STM32_CAN2_BASE+STM32_CAN_RDLR_OFFSET(m)) +# define STM32_CAN2_RDL0R (STM32_CAN2_BASE+STM32_CAN_RDL0R_OFFSET) +# define STM32_CAN2_RDL1R (STM32_CAN2_BASE+STM32_CAN_RDL1R_OFFSET) + +# define STM32_CAN2_RDHR(m) (STM32_CAN2_BASE+STM32_CAN_RDHR_OFFSET(m)) +# define STM32_CAN2_RDH0R (STM32_CAN2_BASE+STM32_CAN_RDH0R_OFFSET) +# define STM32_CAN2_RDH1R (STM32_CAN2_BASE+STM32_CAN_RDH1R_OFFSET) + +# define STM32_CAN2_FMR (STM32_CAN2_BASE+STM32_CAN_FMR_OFFSET) +# define STM32_CAN2_FM1R (STM32_CAN2_BASE+STM32_CAN_FM1R_OFFSET) +# define STM32_CAN2_FS1R (STM32_CAN2_BASE+STM32_CAN_FS1R_OFFSET) +# define STM32_CAN2_FFA1R (STM32_CAN2_BASE+STM32_CAN_FFA1R_OFFSET) +# define STM32_CAN2_FA1R (STM32_CAN2_BASE+STM32_CAN_FA1R_OFFSET) +# define STM32_CAN2_FIR(b,i) (STM32_CAN2_BASE+STM32_CAN_FIR_OFFSET(b,i)) +#endif + +#if defined(CONFIG_STM32F7_CAN3) +# define STM32_CAN3_MCR (STM32_CAN3_BASE+STM32_CAN_MCR_OFFSET) +# define STM32_CAN3_MSR (STM32_CAN3_BASE+STM32_CAN_MSR_OFFSET) +# define STM32_CAN3_TSR (STM32_CAN3_BASE+STM32_CAN_TSR_OFFSET) +# define STM32_CAN3_RF0R (STM32_CAN3_BASE+STM32_CAN_RF0R_OFFSET) +# define STM32_CAN3_RF1R (STM32_CAN3_BASE+STM32_CAN_RF1R_OFFSET) +# define STM32_CAN3_IER (STM32_CAN3_BASE+STM32_CAN_IER_OFFSET) +# define STM32_CAN3_ESR (STM32_CAN3_BASE+STM32_CAN_ESR_OFFSET) +# define STM32_CAN3_BTR (STM32_CAN3_BASE+STM32_CAN_BTR_OFFSET) + +# define STM32_CAN3_TIR(m) (STM32_CAN3_BASE+STM32_CAN_TIR_OFFSET(m)) +# define STM32_CAN3_TI0R (STM32_CAN3_BASE+STM32_CAN_TI0R_OFFSET) +# define STM32_CAN3_TI1R (STM32_CAN3_BASE+STM32_CAN_TI1R_OFFSET) +# define STM32_CAN3_TI2R (STM32_CAN3_BASE+STM32_CAN_TI2R_OFFSET) + +# define STM32_CAN3_TDTR(m) (STM32_CAN3_BASE+STM32_CAN_TDTR_OFFSET(m)) +# define STM32_CAN3_TDT0R (STM32_CAN3_BASE+STM32_CAN_TDT0R_OFFSET) +# define STM32_CAN3_TDT1R (STM32_CAN3_BASE+STM32_CAN_TDT1R_OFFSET) +# define STM32_CAN3_TDT2R (STM32_CAN3_BASE+STM32_CAN_TDT2R_OFFSET) + +# define STM32_CAN3_TDLR(m) (STM32_CAN3_BASE+STM32_CAN_TDLR_OFFSET(m)) +# define STM32_CAN3_TDL0R (STM32_CAN3_BASE+STM32_CAN_TDL0R_OFFSET) +# define STM32_CAN3_TDL1R (STM32_CAN3_BASE+STM32_CAN_TDL1R_OFFSET) +# define STM32_CAN3_TDL2R (STM32_CAN3_BASE+STM32_CAN_TDL2R_OFFSET) + +# define STM32_CAN3_TDHR(m) (STM32_CAN3_BASE+STM32_CAN_TDHR_OFFSET(m)) +# define STM32_CAN3_TDH0R (STM32_CAN3_BASE+STM32_CAN_TDH0R_OFFSET) +# define STM32_CAN3_TDH1R (STM32_CAN3_BASE+STM32_CAN_TDH1R_OFFSET) +# define STM32_CAN3_TDH2R (STM32_CAN3_BASE+STM32_CAN_TDH2R_OFFSET) + +# define STM32_CAN3_RIR(m) (STM32_CAN3_BASE+STM32_CAN_RIR_OFFSET(m)) +# define STM32_CAN3_RI0R (STM32_CAN3_BASE+STM32_CAN_RI0R_OFFSET) +# define STM32_CAN3_RI1R (STM32_CAN3_BASE+STM32_CAN_RI1R_OFFSET) + +# define STM32_CAN3_RDTR(m) (STM32_CAN3_BASE+STM32_CAN_RDTR_OFFSET(m)) +# define STM32_CAN3_RDT0R (STM32_CAN3_BASE+STM32_CAN_RDT0R_OFFSET) +# define STM32_CAN3_RDT1R (STM32_CAN3_BASE+STM32_CAN_RDT1R_OFFSET) + +# define STM32_CAN3_RDLR(m) (STM32_CAN3_BASE+STM32_CAN_RDLR_OFFSET(m)) +# define STM32_CAN3_RDL0R (STM32_CAN3_BASE+STM32_CAN_RDL0R_OFFSET) +# define STM32_CAN3_RDL1R (STM32_CAN3_BASE+STM32_CAN_RDL1R_OFFSET) + +# define STM32_CAN3_RDHR(m) (STM32_CAN3_BASE+STM32_CAN_RDHR_OFFSET(m)) +# define STM32_CAN3_RDH0R (STM32_CAN3_BASE+STM32_CAN_RDH0R_OFFSET) +# define STM32_CAN3_RDH1R (STM32_CAN3_BASE+STM32_CAN_RDH1R_OFFSET) + +# define STM32_CAN3_FMR (STM32_CAN3_BASE+STM32_CAN_FMR_OFFSET) +# define STM32_CAN3_FM1R (STM32_CAN3_BASE+STM32_CAN_FM1R_OFFSET) +# define STM32_CAN3_FS1R (STM32_CAN3_BASE+STM32_CAN_FS1R_OFFSET) +# define STM32_CAN3_FFA1R (STM32_CAN3_BASE+STM32_CAN_FFA1R_OFFSET) +# define STM32_CAN3_FA1R (STM32_CAN3_BASE+STM32_CAN_FA1R_OFFSET) +# define STM32_CAN3_FIR(b,i) (STM32_CAN3_BASE+STM32_CAN_FIR_OFFSET(b,i)) +#endif + +/* Register Bitfield Definitions ****************************************************/ + +/* CAN master control register */ + +#define CAN_MCR_INRQ (1 << 0) /* Bit 0: Initialization Request */ +#define CAN_MCR_SLEEP (1 << 1) /* Bit 1: Sleep Mode Request */ +#define CAN_MCR_TXFP (1 << 2) /* Bit 2: Transmit FIFO Priority */ +#define CAN_MCR_RFLM (1 << 3) /* Bit 3: Receive FIFO Locked Mode */ +#define CAN_MCR_NART (1 << 4) /* Bit 4: No Automatic Retransmission */ +#define CAN_MCR_AWUM (1 << 5) /* Bit 5: Automatic Wakeup Mode */ +#define CAN_MCR_ABOM (1 << 6) /* Bit 6: Automatic Bus-Off Management */ +#define CAN_MCR_TTCM (1 << 7) /* Bit 7: Time Triggered Communication Mode Enable */ +#define CAN_MCR_RESET (1 << 15) /* Bit 15: bxCAN software master reset */ +#define CAN_MCR_DBF (1 << 16) /* Bit 16: Debug freeze */ + +/* CAN master status register */ + +#define CAN_MSR_INAK (1 << 0) /* Bit 0: Initialization Acknowledge */ +#define CAN_MSR_SLAK (1 << 1) /* Bit 1: Sleep Acknowledge */ +#define CAN_MSR_ERRI (1 << 2) /* Bit 2: Error Interrupt */ +#define CAN_MSR_WKUI (1 << 3) /* Bit 3: Wakeup Interrupt */ +#define CAN_MSR_SLAKI (1 << 4) /* Bit 4: Sleep acknowledge interrupt */ +#define CAN_MSR_TXM (1 << 8) /* Bit 8: Transmit Mode */ +#define CAN_MSR_RXM (1 << 9) /* Bit 9: Receive Mode */ +#define CAN_MSR_SAMP (1 << 10) /* Bit 10: Last Sample Point */ +#define CAN_MSR_RX (1 << 11) /* Bit 11: CAN Rx Signal */ + +/* CAN transmit status register */ + +#define CAN_TSR_RQCP0 (1 << 0) /* Bit 0: Request Completed Mailbox 0 */ +#define CAN_TSR_TXOK0 (1 << 1) /* Bit 1 : Transmission OK of Mailbox 0 */ +#define CAN_TSR_ALST0 (1 << 2) /* Bit 2 : Arbitration Lost for Mailbox 0 */ +#define CAN_TSR_TERR0 (1 << 3) /* Bit 3 : Transmission Error of Mailbox 0 */ +#define CAN_TSR_ABRQ0 (1 << 7) /* Bit 7 : Abort Request for Mailbox 0 */ +#define CAN_TSR_RQCP1 (1 << 8) /* Bit 8 : Request Completed Mailbox 1 */ +#define CAN_TSR_TXOK1 (1 << 9) /* Bit 9 : Transmission OK of Mailbox 1 */ +#define CAN_TSR_ALST1 (1 << 10) /* Bit 10 : Arbitration Lost for Mailbox 1 */ +#define CAN_TSR_TERR1 (1 << 11) /* Bit 11 : Transmission Error of Mailbox 1 */ +#define CAN_TSR_ABRQ1 (1 << 15) /* Bit 15 : Abort Request for Mailbox 1 */ +#define CAN_TSR_RQCP2 (1 << 16) /* Bit 16 : Request Completed Mailbox 2 */ +#define CAN_TSR_TXOK2 (1 << 17) /* Bit 17 : Transmission OK of Mailbox 2 */ +#define CAN_TSR_ALST2 (1 << 18) /* Bit 18: Arbitration Lost for Mailbox 2 */ +#define CAN_TSR_TERR2 (1 << 19) /* Bit 19: Transmission Error of Mailbox 2 */ +#define CAN_TSR_ABRQ2 (1 << 23) /* Bit 23: Abort Request for Mailbox 2 */ +#define CAN_TSR_CODE_SHIFT (24) /* Bits 25-24: Mailbox Code */ +#define CAN_TSR_CODE_MASK (3 << CAN_TSR_CODE_SHIFT) +#define CAN_TSR_TME0 (1 << 26) /* Bit 26: Transmit Mailbox 0 Empty */ +#define CAN_TSR_TME1 (1 << 27) /* Bit 27: Transmit Mailbox 1 Empty */ +#define CAN_TSR_TME2 (1 << 28) /* Bit 28: Transmit Mailbox 2 Empty */ +#define CAN_TSR_LOW0 (1 << 29) /* Bit 29: Lowest Priority Flag for Mailbox 0 */ +#define CAN_TSR_LOW1 (1 << 30) /* Bit 30: Lowest Priority Flag for Mailbox 1 */ +#define CAN_TSR_LOW2 (1 << 31) /* Bit 31: Lowest Priority Flag for Mailbox 2 */ + +/* CAN receive FIFO 0/1 registers */ + +#define CAN_RFR_FMP_SHIFT (0) /* Bits 1-0: FIFO Message Pending */ +#define CAN_RFR_FMP_MASK (3 << CAN_RFR_FMP_SHIFT) +#define CAN_RFR_FULL (1 << 3) /* Bit 3: FIFO 0 Full */ +#define CAN_RFR_FOVR (1 << 4) /* Bit 4: FIFO 0 Overrun */ +#define CAN_RFR_RFOM (1 << 5) /* Bit 5: Release FIFO 0 Output Mailbox */ + +/* CAN interrupt enable register */ + +#define CAN_IER_TMEIE (1 << 0) /* Bit 0: Transmit Mailbox Empty Interrupt Enable */ +#define CAN_IER_FMPIE0 (1 << 1) /* Bit 1: FIFO Message Pending Interrupt Enable */ +#define CAN_IER_FFIE0 (1 << 2) /* Bit 2: FIFO Full Interrupt Enable */ +#define CAN_IER_FOVIE0 (1 << 3) /* Bit 3: FIFO Overrun Interrupt Enable */ +#define CAN_IER_FMPIE1 (1 << 4) /* Bit 4: FIFO Message Pending Interrupt Enable */ +#define CAN_IER_FFIE1 (1 << 5) /* Bit 5: FIFO Full Interrupt Enable */ +#define CAN_IER_FOVIE1 (1 << 6) /* Bit 6: FIFO Overrun Interrupt Enable */ +#define CAN_IER_EWGIE (1 << 8) /* Bit 8: Error Warning Interrupt Enable */ +#define CAN_IER_EPVIE (1 << 9) /* Bit 9: Error Passive Interrupt Enable */ +#define CAN_IER_BOFIE (1 << 10) /* Bit 10: Bus-Off Interrupt Enable */ +#define CAN_IER_LECIE (1 << 11) /* Bit 11: Last Error Code Interrupt Enable */ +#define CAN_IER_ERRIE (1 << 15) /* Bit 15: Error Interrupt Enable */ +#define CAN_IER_WKUIE (1 << 16) /* Bit 16: Wakeup Interrupt Enable */ +#define CAN_IER_SLKIE (1 << 17) /* Bit 17: Sleep Interrupt Enable */ + +/* CAN error status register */ + +#define CAN_ESR_EWGF (1 << 0) /* Bit 0: Error Warning Flag */ +#define CAN_ESR_EPVF (1 << 1) /* Bit 1: Error Passive Flag */ +#define CAN_ESR_BOFF (1 << 2) /* Bit 2: Bus-Off Flag */ +#define CAN_ESR_LEC_SHIFT (4) /* Bits 6-4: Last Error Code */ +#define CAN_ESR_LEC_MASK (7 << CAN_ESR_LEC_SHIFT) +# define CAN_ESR_NOERROR (0 << CAN_ESR_LEC_SHIFT) /* 000: No Error */ +# define CAN_ESR_STUFFERROR (1 << CAN_ESR_LEC_SHIFT) /* 001: Stuff Error */ +# define CAN_ESR_FORMERROR (2 << CAN_ESR_LEC_SHIFT) /* 010: Form Error */ +# define CAN_ESR_ACKERROR (3 << CAN_ESR_LEC_SHIFT) /* 011: Acknowledgment Error */ +# define CAN_ESR_BRECERROR (4 << CAN_ESR_LEC_SHIFT) /* 100: Bit recessive Error */ +# define CAN_ESR_BDOMERROR (5 << CAN_ESR_LEC_SHIFT) /* 101: Bit dominant Error */ +# define CAN_ESR_CRCERRPR (6 << CAN_ESR_LEC_SHIFT) /* 110: CRC Error */ +# define CAN_ESR_SWERROR (7 << CAN_ESR_LEC_SHIFT) /* 111: Set by software */ +#define CAN_ESR_TEC_SHIFT (16) /* Bits 23-16: LS byte of the 9-bit Transmit Error Counter */ +#define CAN_ESR_TEC_MASK (0xff << CAN_ESR_TEC_SHIF) +#define CAN_ESR_REC_SHIFT (24) /* Bits 31-24: Receive Error Counter */ +#define CAN_ESR_REC_MASK (0xff << CAN_ESR_REC_SHIFT) + +/* CAN bit timing register */ + +#define CAN_BTR_BRP_SHIFT (0) /* Bits 9-0: Baud Rate Prescaler */ +#define CAN_BTR_BRP_MASK (0x03ff << CAN_BTR_BRP_SHIFT) +#define CAN_BTR_TS1_SHIFT (16) /* Bits 19-16: Time Segment 1 */ +#define CAN_BTR_TS1_MASK (0x0f << CAN_BTR_TS1_SHIFT) +#define CAN_BTR_TS2_SHIFT (20) /* Bits 22-20: Time Segment 2 */ +#define CAN_BTR_TS2_MASK (7 << CAN_BTR_TS2_SHIFT) +#define CAN_BTR_SJW_SHIFT (24) /* Bits 25-24: Resynchronization Jump Width */ +#define CAN_BTR_SJW_MASK (3 << CAN_BTR_SJW_SHIFT) +#define CAN_BTR_LBKM (1 << 30) /* Bit 30: Loop Back Mode (Debug) */ +#define CAN_BTR_SILM (1ul << 31) /* Bit 31: Silent Mode (Debug) */ + +#define CAN_BTR_BRP_MAX (1024) /* Maximum BTR value (without decrement) */ +#define CAN_BTR_TSEG1_MAX (16) /* Maximum TSEG1 value (without decrement) */ +#define CAN_BTR_TSEG2_MAX (8) /* Maximum TSEG2 value (without decrement) */ + +/* TX mailbox identifier register */ + +#define CAN_TIR_TXRQ (1 << 0) /* Bit 0: Transmit Mailbox Request */ +#define CAN_TIR_RTR (1 << 1) /* Bit 1: Remote Transmission Request */ +#define CAN_TIR_IDE (1 << 2) /* Bit 2: Identifier Extension */ +#define CAN_TIR_EXID_SHIFT (3) /* Bit 3-31: Extended Identifier */ +#define CAN_TIR_EXID_MASK (0x1fffffff << CAN_TIR_EXID_SHIFT) +#define CAN_TIR_STID_SHIFT (21) /* Bits 21-31: Standard Identifier */ +#define CAN_TIR_STID_MASK (0x07ff << CAN_TIR_STID_SHIFT) + +/* Mailbox data length control and time stamp register */ + +#define CAN_TDTR_DLC_SHIFT (0) /* Bits 3:0: Data Length Code */ +#define CAN_TDTR_DLC_MASK (0x0f << CAN_TDTR_DLC_SHIFT) +#define CAN_TDTR_TGT (1 << 8) /* Bit 8: Transmit Global Time */ +#define CAN_TDTR_TIME_SHIFT (16) /* Bits 31:16: Message Time Stamp */ +#define CAN_TDTR_TIME_MASK (0xffff << CAN_TDTR_TIME_SHIFT) + +/* Mailbox data low register */ + +#define CAN_TDLR_DATA0_SHIFT (0) /* Bits 7-0: Data Byte 0 */ +#define CAN_TDLR_DATA0_MASK (0xff << CAN_TDLR_DATA0_SHIFT) +#define CAN_TDLR_DATA1_SHIFT (8) /* Bits 15-8: Data Byte 1 */ +#define CAN_TDLR_DATA1_MASK (0xff << CAN_TDLR_DATA1_SHIFT) +#define CAN_TDLR_DATA2_SHIFT (16) /* Bits 23-16: Data Byte 2 */ +#define CAN_TDLR_DATA2_MASK (0xff << CAN_TDLR_DATA2_SHIFT) +#define CAN_TDLR_DATA3_SHIFT (24) /* Bits 31-24: Data Byte 3 */ +#define CAN_TDLR_DATA3_MASK (0xff << CAN_TDLR_DATA3_SHIFT) + +/* Mailbox data high register */ + +#define CAN_TDHR_DATA4_SHIFT (0) /* Bits 7-0: Data Byte 4 */ +#define CAN_TDHR_DATA4_MASK (0xff << CAN_TDHR_DATA4_SHIFT) +#define CAN_TDHR_DATA5_SHIFT (8) /* Bits 15-8: Data Byte 5 */ +#define CAN_TDHR_DATA5_MASK (0xff << CAN_TDHR_DATA5_SHIFT) +#define CAN_TDHR_DATA6_SHIFT (16) /* Bits 23-16: Data Byte 6 */ +#define CAN_TDHR_DATA6_MASK (0xff << CAN_TDHR_DATA6_SHIFT) +#define CAN_TDHR_DATA7_SHIFT (24) /* Bits 31-24: Data Byte 7 */ +#define CAN_TDHR_DATA7_MASK (0xff << CAN_TDHR_DATA7_SHIFT) + +/* Rx FIFO mailbox identifier register */ + +#define CAN_RIR_RTR (1 << 1) /* Bit 1: Remote Transmission Request */ +#define CAN_RIR_IDE (1 << 2) /* Bit 2: Identifier Extension */ +#define CAN_RIR_EXID_SHIFT (3) /* Bit 3-31: Extended Identifier */ +#define CAN_RIR_EXID_MASK (0x1fffffff << CAN_RIR_EXID_SHIFT) +#define CAN_RIR_STID_SHIFT (21) /* Bits 21-31: Standard Identifier */ +#define CAN_RIR_STID_MASK (0x07ff << CAN_RIR_STID_SHIFT) + +/* Receive FIFO mailbox data length control and time stamp register */ + +#define CAN_RDTR_DLC_SHIFT (0) /* Bits 3:0: Data Length Code */ +#define CAN_RDTR_DLC_MASK (0x0f << CAN_RDTR_DLC_SHIFT) +#define CAN_RDTR_FM_SHIFT (8) /* Bits 15-8: Filter Match Index */ +#define CAN_RDTR_FM_MASK (0xff << CAN_RDTR_FM_SHIFT) +#define CAN_RDTR_TIME_SHIFT (16) /* Bits 31:16: Message Time Stamp */ +#define CAN_RDTR_TIME_MASK (0xffff << CAN_RDTR_TIME_SHIFT) + +/* Receive FIFO mailbox data low register */ + +#define CAN_RDLR_DATA0_SHIFT (0) /* Bits 7-0: Data Byte 0 */ +#define CAN_RDLR_DATA0_MASK (0xff << CAN_RDLR_DATA0_SHIFT) +#define CAN_RDLR_DATA1_SHIFT (8) /* Bits 15-8: Data Byte 1 */ +#define CAN_RDLR_DATA1_MASK (0xff << CAN_RDLR_DATA1_SHIFT) +#define CAN_RDLR_DATA2_SHIFT (16) /* Bits 23-16: Data Byte 2 */ +#define CAN_RDLR_DATA2_MASK (0xff << CAN_RDLR_DATA2_SHIFT) +#define CAN_RDLR_DATA3_SHIFT (24) /* Bits 31-24: Data Byte 3 */ +#define CAN_RDLR_DATA3_MASK (0xff << CAN_RDLR_DATA3_SHIFT) + +/* Receive FIFO mailbox data high register */ + +#define CAN_RDHR_DATA4_SHIFT (0) /* Bits 7-0: Data Byte 4 */ +#define CAN_RDHR_DATA4_MASK (0xff << CAN_RDHR_DATA4_SHIFT) +#define CAN_RDHR_DATA5_SHIFT (8) /* Bits 15-8: Data Byte 5 */ +#define CAN_RDHR_DATA5_MASK (0xff << CAN_RDHR_DATA5_SHIFT) +#define CAN_RDHR_DATA6_SHIFT (16) /* Bits 23-16: Data Byte 6 */ +#define CAN_RDHR_DATA6_MASK (0xff << CAN_RDHR_DATA6_SHIFT) +#define CAN_RDHR_DATA7_SHIFT (24) /* Bits 31-24: Data Byte 7 */ +#define CAN_RDHR_DATA7_MASK (0xff << CAN_RDHR_DATA7_SHIFT) + +/* CAN filter master register */ + +#define CAN_FMR_FINIT (1 << 0) /* Bit 0: Filter Init Mode */ +#if defined(CONFIG_STM32_CONNECTIVITYLINE) || defined(CONFIG_STM32_STM32F20XX) || defined(CONFIG_STM32_STM32F4XXX) +# define CAN_FMR_CAN2SB_SHIFT (8) /* Bits 13-8: CAN2 start bank */ +# define CAN_FMR_CAN2SB_MASK (0x3f << CAN_FMR_CAN2SB_SHIFT) +#endif + +/* CAN filter mode register */ + +#if defined(CONFIG_STM32_CONNECTIVITYLINE) || defined(CONFIG_STM32_STM32F20XX) || defined(CONFIG_STM32_STM32F4XXX) +# define CAN_FM1R_FBM_SHIFT (0) /* Bits 13:0: Filter Mode */ +# define CAN_FM1R_FBM_MASK (0x3fff << CAN_FM1R_FBM_SHIFT) +#else +# define CAN_FM1R_FBM_SHIFT (0) /* Bits 27:0: Filter Mode */ +# define CAN_FM1R_FBM_MASK (0x0fffffff << CAN_FM1R_FBM_SHIFT) +#endif + +/* CAN filter scale register */ + +#if defined(CONFIG_STM32_CONNECTIVITYLINE) || defined(CONFIG_STM32_STM32F20XX) || defined(CONFIG_STM32_STM32F4XXX) +# define CAN_FS1R_FSC_SHIFT (0) /* Bits 13:0: Filter Scale Configuration */ +# define CAN_FS1R_FSC_MASK (0x3fff << CAN_FS1R_FSC_SHIFT) +#else +# define CAN_FS1R_FSC_SHIFT (0) /* Bits 27:0: Filter Scale Configuration */ +# define CAN_FS1R_FSC_MASK (0x0fffffff << CAN_FS1R_FSC_SHIFT) +#endif + +/* CAN filter FIFO assignment register */ + +#if defined(CONFIG_STM32_CONNECTIVITYLINE) || defined(CONFIG_STM32_STM32F20XX) || defined(CONFIG_STM32_STM32F4XXX) +# define CAN_FFA1R_FFA_SHIFT (0) /* Bits 13:0: Filter FIFO Assignment */ +# define CAN_FFA1R_FFA_MASK (0x3fff << CAN_FFA1R_FFA_SHIFT) +#else +# define CAN_FFA1R_FFA_SHIFT (0) /* Bits 27:0: Filter FIFO Assignment */ +# define CAN_FFA1R_FFA_MASK (0x0fffffff << CAN_FFA1R_FFA_SHIFT) +#endif + +/* CAN filter activation register */ + +#if defined(CONFIG_STM32_CONNECTIVITYLINE) || defined(CONFIG_STM32_STM32F20XX) || defined(CONFIG_STM32_STM32F4XXX) +# define CAN_FA1R_FACT_SHIFT (0) /* Bits 13:0: Filter Active */ +# define CAN_FA1R_FACT_MASK (0x3fff << CAN_FA1R_FACT_SHIFT) +#else +# define CAN_FA1R_FACT_SHIFT (0) /* Bits 27:0: Filter Active */ +# define CAN_FA1R_FACT_MASK (0x0fffffff << CAN_FA1R_FACT_SHIFT) +#endif + +/************************************************************************************ + * Public Types + ************************************************************************************/ + +/************************************************************************************ + * Public Data + ************************************************************************************/ + +/************************************************************************************ + * Public Functions + ************************************************************************************/ + +#endif /* __ARCH_ARM_SRC_STM32_CHIP_STM32_CAN_H */ diff --git a/arch/arm/src/stm32f7/stm32_can.c b/arch/arm/src/stm32f7/stm32_can.c new file mode 100644 index 00000000000..3d4adb2c22a --- /dev/null +++ b/arch/arm/src/stm32f7/stm32_can.c @@ -0,0 +1,2274 @@ +/**************************************************************************** + * arch/arm/src/stm32/stm32_can.c + * + * Copyright (C) 2011, 2016-2017, 2019 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * Copyright (C) 2016 Omni Hoverboards Inc. All rights reserved. + * Author: Paul Alexander Patience + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "up_internal.h" +#include "up_arch.h" + +#include "chip.h" +#include "stm32_can.h" +#include "stm32_gpio.h" + +#if defined(CONFIG_CAN) && \ + (defined(CONFIG_STM32F7_CAN1) || defined(CONFIG_STM32F7_CAN2)) + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Delays *******************************************************************/ + +/* Time out for INAK bit */ + +#define INAK_TIMEOUT 65535 + +/* Bit timing ***************************************************************/ + +#define CAN_BIT_QUANTA (CONFIG_STM32F7_CAN_TSEG1 + CONFIG_STM32F7_CAN_TSEG2 + 1) + +#ifndef CONFIG_DEBUG_CAN_INFO +# undef CONFIG_STM32_CAN_REGDEBUG +#endif + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct stm32_can_s +{ + uint8_t port; /* CAN port number (1 or 2) */ + uint8_t canrx[2]; /* CAN RX FIFO 0/1 IRQ number */ + uint8_t cantx; /* CAN TX IRQ number */ + uint8_t filter; /* Filter number */ + uint32_t base; /* Base address of the CAN control registers */ + uint32_t fbase; /* Base address of the CAN filter registers */ + uint32_t baud; /* Configured baud */ +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +/* CAN Register access */ + +static uint32_t stm32can_getreg(FAR struct stm32_can_s *priv, + int offset); +static uint32_t stm32can_getfreg(FAR struct stm32_can_s *priv, + int offset); +static void stm32can_putreg(FAR struct stm32_can_s *priv, int offset, + uint32_t value); +static void stm32can_putfreg(FAR struct stm32_can_s *priv, int offset, + uint32_t value); +#ifdef CONFIG_STM32_CAN_REGDEBUG +static void stm32can_dumpctrlregs(FAR struct stm32_can_s *priv, + FAR const char *msg); +static void stm32can_dumpmbregs(FAR struct stm32_can_s *priv, + FAR const char *msg); +static void stm32can_dumpfiltregs(FAR struct stm32_can_s *priv, + FAR const char *msg); +#else +# define stm32can_dumpctrlregs(priv,msg) +# define stm32can_dumpmbregs(priv,msg) +# define stm32can_dumpfiltregs(priv,msg) +#endif + +/* Filtering (todo) */ + +#ifdef CONFIG_CAN_EXTID +static int stm32can_addextfilter(FAR struct stm32_can_s *priv, + FAR struct canioc_extfilter_s *arg); +static int stm32can_delextfilter(FAR struct stm32_can_s *priv, + int arg); +#endif +static int stm32can_addstdfilter(FAR struct stm32_can_s *priv, + FAR struct canioc_stdfilter_s *arg); +static int stm32can_delstdfilter(FAR struct stm32_can_s *priv, + int arg); + +/* CAN driver methods */ + +static void stm32can_reset(FAR struct can_dev_s *dev); +static int stm32can_setup(FAR struct can_dev_s *dev); +static void stm32can_shutdown(FAR struct can_dev_s *dev); +static void stm32can_rxint(FAR struct can_dev_s *dev, bool enable); +static void stm32can_txint(FAR struct can_dev_s *dev, bool enable); +static int stm32can_ioctl(FAR struct can_dev_s *dev, int cmd, + unsigned long arg); +static int stm32can_remoterequest(FAR struct can_dev_s *dev, + uint16_t id); +static int stm32can_send(FAR struct can_dev_s *dev, + FAR struct can_msg_s *msg); +static bool stm32can_txready(FAR struct can_dev_s *dev); +static bool stm32can_txempty(FAR struct can_dev_s *dev); + +/* CAN interrupt handling */ + +static int stm32can_rxinterrupt(FAR struct can_dev_s *dev, int rxmb); +static int stm32can_rx0interrupt(int irq, FAR void *context, FAR void *arg); +static int stm32can_rx1interrupt(int irq, FAR void *context, FAR void *arg); +static int stm32can_txinterrupt(int irq, FAR void *context, FAR void *arg); + +/* Initialization */ + +static int stm32can_enterinitmode(FAR struct stm32_can_s *priv); +static int stm32can_exitinitmode(FAR struct stm32_can_s *priv); +static int stm32can_bittiming(FAR struct stm32_can_s *priv); +static int stm32can_cellinit(FAR struct stm32_can_s *priv); +static int stm32can_filterinit(FAR struct stm32_can_s *priv); + +/* TX mailbox status */ + +static bool stm32can_txmb0empty(uint32_t tsr_regval); +static bool stm32can_txmb1empty(uint32_t tsr_regval); +static bool stm32can_txmb2empty(uint32_t tsr_regval); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static const struct can_ops_s g_canops = +{ + .co_reset = stm32can_reset, + .co_setup = stm32can_setup, + .co_shutdown = stm32can_shutdown, + .co_rxint = stm32can_rxint, + .co_txint = stm32can_txint, + .co_ioctl = stm32can_ioctl, + .co_remoterequest = stm32can_remoterequest, + .co_send = stm32can_send, + .co_txready = stm32can_txready, + .co_txempty = stm32can_txempty, +}; + +#ifdef CONFIG_STM32F7_CAN1 +static struct stm32_can_s g_can1priv = +{ + .port = 1, + .canrx = + { + STM32_IRQ_CAN1RX0, + STM32_IRQ_CAN1RX1, + }, + .cantx = STM32_IRQ_CAN1TX, + .filter = 0, + .base = STM32_CAN1_BASE, + .fbase = STM32_CAN1_BASE, + .baud = CONFIG_STM32F7_CAN1_BAUD, +}; + +static struct can_dev_s g_can1dev = +{ + .cd_ops = &g_canops, + .cd_priv = &g_can1priv, +}; +#endif + +#ifdef CONFIG_STM32F7_CAN2 +static struct stm32_can_s g_can2priv = +{ + .port = 2, + .canrx = + { + STM32_IRQ_CAN2RX0, + STM32_IRQ_CAN2RX1, + }, + .cantx = STM32_IRQ_CAN2TX, + .filter = CAN_NFILTERS / 2, + .base = STM32_CAN2_BASE, + .fbase = STM32_CAN1_BASE, + .baud = CONFIG_STM32F7_CAN2_BAUD, +}; + +static struct can_dev_s g_can2dev = +{ + .cd_ops = &g_canops, + .cd_priv = &g_can2priv, +}; +#endif + +#ifdef CONFIG_STM32F7_CAN3 +static struct stm32_can_s g_can3priv = +{ + .port = 3, + .canrx = + { + STM32_IRQ_CAN3RX0, + STM32_IRQ_CAN3RX1, + }, + .cantx = STM32_IRQ_CAN3TX, + .filter = CAN_NFILTERS / 2, + .base = STM32_CAN3_BASE, + .fbase = STM32_CAN3_BASE, + .baud = CONFIG_STM32F7_CAN3_BAUD, +}; + +static struct can_dev_s g_can3dev = +{ + .cd_ops = &g_canops, + .cd_priv = &g_can3priv, +}; +#endif + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: stm32can_getreg + * Name: stm32can_getfreg + * + * Description: + * Read the value of a CAN register or filter block register. + * + * Input Parameters: + * priv - A reference to the CAN block status + * offset - The offset to the register to read + * + * Returned Value: + * + ****************************************************************************/ + +#ifdef CONFIG_STM32_CAN_REGDEBUG +static uint32_t stm32can_vgetreg(uint32_t addr) +{ + static uint32_t prevaddr = 0; + static uint32_t preval = 0; + static uint32_t count = 0; + + /* Read the value from the register */ + + uint32_t val = getreg32(addr); + + /* Is this the same value that we read from the same register last time? + * Are we polling the register? If so, suppress some of the output. + */ + + if (addr == prevaddr && val == preval) + { + if (count == 0xffffffff || ++count > 3) + { + if (count == 4) + { + caninfo("...\n"); + } + + return val; + } + } + + /* No this is a new address or value */ + + else + { + /* Did we print "..." for the previous value? */ + + if (count > 3) + { + /* Yes.. then show how many times the value repeated */ + + caninfo("[repeats %d more times]\n", count - 3); + } + + /* Save the new address, value, and count */ + + prevaddr = addr; + preval = val; + count = 1; + } + + /* Show the register value read */ + + caninfo("%08x->%08x\n", addr, val); + return val; +} + +static uint32_t stm32can_getreg(FAR struct stm32_can_s *priv, int offset) +{ + return stm32can_vgetreg(priv->base + offset); +} + +static uint32_t stm32can_getfreg(FAR struct stm32_can_s *priv, int offset) +{ + return stm32can_vgetreg(priv->fbase + offset); +} + +#else +static uint32_t stm32can_getreg(FAR struct stm32_can_s *priv, int offset) +{ + return getreg32(priv->base + offset); +} + +static uint32_t stm32can_getfreg(FAR struct stm32_can_s *priv, int offset) +{ + return getreg32(priv->fbase + offset); +} + +#endif + +/**************************************************************************** + * Name: stm32can_putreg + * Name: stm32can_putfreg + * + * Description: + * Set the value of a CAN register or filter block register. + * + * Input Parameters: + * priv - A reference to the CAN block status + * offset - The offset to the register to write + * value - The value to write to the register + * + * Returned Value: + * None + * + ****************************************************************************/ + +#ifdef CONFIG_STM32_CAN_REGDEBUG +static void stm32can_vputreg(uint32_t addr, uint32_t value) +{ + /* Show the register value being written */ + + caninfo("%08x<-%08x\n", addr, value); + + /* Write the value */ + + putreg32(value, addr); +} + +static void stm32can_putreg(FAR struct stm32_can_s *priv, int offset, + uint32_t value) +{ + stm32can_vputreg(priv->base + offset, value); +} + +static void stm32can_putfreg(FAR struct stm32_can_s *priv, int offset, + uint32_t value) +{ + stm32can_vputreg(priv->fbase + offset, value); +} + +#else +static void stm32can_putreg(FAR struct stm32_can_s *priv, int offset, + uint32_t value) +{ + putreg32(value, priv->base + offset); +} + +static void stm32can_putfreg(FAR struct stm32_can_s *priv, int offset, + uint32_t value) +{ + putreg32(value, priv->fbase + offset); +} +#endif + +/**************************************************************************** + * Name: stm32can_dumpctrlregs + * + * Description: + * Dump the contents of all CAN control registers + * + * Input Parameters: + * priv - A reference to the CAN block status + * + * Returned Value: + * None + * + ****************************************************************************/ + +#ifdef CONFIG_STM32_CAN_REGDEBUG +static void stm32can_dumpctrlregs(FAR struct stm32_can_s *priv, + FAR const char *msg) +{ + if (msg) + { + caninfo("Control Registers: %s\n", msg); + } + else + { + caninfo("Control Registers:\n"); + } + + /* CAN control and status registers */ + + caninfo(" MCR: %08x MSR: %08x TSR: %08x\n", + getreg32(priv->base + STM32_CAN_MCR_OFFSET), + getreg32(priv->base + STM32_CAN_MSR_OFFSET), + getreg32(priv->base + STM32_CAN_TSR_OFFSET)); + + caninfo(" RF0R: %08x RF1R: %08x\n", + getreg32(priv->base + STM32_CAN_RF0R_OFFSET), + getreg32(priv->base + STM32_CAN_RF1R_OFFSET)); + + caninfo(" IER: %08x ESR: %08x BTR: %08x\n", + getreg32(priv->base + STM32_CAN_IER_OFFSET), + getreg32(priv->base + STM32_CAN_ESR_OFFSET), + getreg32(priv->base + STM32_CAN_BTR_OFFSET)); +} +#endif + +/**************************************************************************** + * Name: stm32can_dumpmbregs + * + * Description: + * Dump the contents of all CAN mailbox registers + * + * Input Parameters: + * priv - A reference to the CAN block status + * + * Returned Value: + * None + * + ****************************************************************************/ + +#ifdef CONFIG_STM32_CAN_REGDEBUG +static void stm32can_dumpmbregs(FAR struct stm32_can_s *priv, + FAR const char *msg) +{ + if (msg) + { + caninfo("Mailbox Registers: %s\n", msg); + } + else + { + caninfo("Mailbox Registers:\n"); + } + + /* CAN mailbox registers (3 TX and 2 RX) */ + + caninfo(" TI0R: %08x TDT0R: %08x TDL0R: %08x TDH0R: %08x\n", + getreg32(priv->base + STM32_CAN_TI0R_OFFSET), + getreg32(priv->base + STM32_CAN_TDT0R_OFFSET), + getreg32(priv->base + STM32_CAN_TDL0R_OFFSET), + getreg32(priv->base + STM32_CAN_TDH0R_OFFSET)); + + caninfo(" TI1R: %08x TDT1R: %08x TDL1R: %08x TDH1R: %08x\n", + getreg32(priv->base + STM32_CAN_TI1R_OFFSET), + getreg32(priv->base + STM32_CAN_TDT1R_OFFSET), + getreg32(priv->base + STM32_CAN_TDL1R_OFFSET), + getreg32(priv->base + STM32_CAN_TDH1R_OFFSET)); + + caninfo(" TI2R: %08x TDT2R: %08x TDL2R: %08x TDH2R: %08x\n", + getreg32(priv->base + STM32_CAN_TI2R_OFFSET), + getreg32(priv->base + STM32_CAN_TDT2R_OFFSET), + getreg32(priv->base + STM32_CAN_TDL2R_OFFSET), + getreg32(priv->base + STM32_CAN_TDH2R_OFFSET)); + + caninfo(" RI0R: %08x RDT0R: %08x RDL0R: %08x RDH0R: %08x\n", + getreg32(priv->base + STM32_CAN_RI0R_OFFSET), + getreg32(priv->base + STM32_CAN_RDT0R_OFFSET), + getreg32(priv->base + STM32_CAN_RDL0R_OFFSET), + getreg32(priv->base + STM32_CAN_RDH0R_OFFSET)); + + caninfo(" RI1R: %08x RDT1R: %08x RDL1R: %08x RDH1R: %08x\n", + getreg32(priv->base + STM32_CAN_RI1R_OFFSET), + getreg32(priv->base + STM32_CAN_RDT1R_OFFSET), + getreg32(priv->base + STM32_CAN_RDL1R_OFFSET), + getreg32(priv->base + STM32_CAN_RDH1R_OFFSET)); +} +#endif + +/**************************************************************************** + * Name: stm32can_dumpfiltregs + * + * Description: + * Dump the contents of all CAN filter registers + * + * Input Parameters: + * priv - A reference to the CAN block status + * + * Returned Value: + * None + * + ****************************************************************************/ + +#ifdef CONFIG_STM32_CAN_REGDEBUG +static void stm32can_dumpfiltregs(FAR struct stm32_can_s *priv, + FAR const char *msg) +{ + int i; + + if (msg) + { + caninfo("Filter Registers: %s\n", msg); + } + else + { + caninfo("Filter Registers:\n"); + } + + caninfo(" FMR: %08x FM1R: %08x FS1R: %08x FFA1R: %08x FA1R: %08x\n", + getreg32(priv->base + STM32_CAN_FMR_OFFSET), + getreg32(priv->base + STM32_CAN_FM1R_OFFSET), + getreg32(priv->base + STM32_CAN_FS1R_OFFSET), + getreg32(priv->base + STM32_CAN_FFA1R_OFFSET), + getreg32(priv->base + STM32_CAN_FA1R_OFFSET)); + + for (i = 0; i < CAN_NFILTERS; i++) + { + caninfo(" F%dR1: %08x F%dR2: %08x\n", + i, getreg32(priv->base + STM32_CAN_FIR_OFFSET(i, 1)), + i, getreg32(priv->base + STM32_CAN_FIR_OFFSET(i, 2))); + } +} +#endif + +/**************************************************************************** + * Name: stm32can_reset + * + * Description: + * Reset the CAN device. Called early to initialize the hardware. This + * function is called, before stm32can_setup() and on error conditions. + * + * Input Parameters: + * dev - An instance of the "upper half" can driver state structure. + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void stm32can_reset(FAR struct can_dev_s *dev) +{ + FAR struct stm32_can_s *priv = dev->cd_priv; + uint32_t regval; + uint32_t regbit = 0; + irqstate_t flags; + + caninfo("CAN%d\n", priv->port); + + /* Get the bits in the AHB1RSTR register needed to reset this CAN device */ + +#ifdef CONFIG_STM32F7_CAN1 + if (priv->port == 1) + { + regbit = RCC_APB1RSTR_CAN1RST; + } + else +#endif +#ifdef CONFIG_STM32F7_CAN2 + if (priv->port == 2) + { + regbit = RCC_APB1RSTR_CAN2RST; + } + else +#endif +#ifdef CONFIG_STM32F7_CAN3 + if (priv->port == 3) + { + regbit = RCC_APB1RSTR_CAN3RST; + } + else +#endif + { + canerr("ERROR: Unsupported port %d\n", priv->port); + return; + } + + /* Disable interrupts momentarily to stop any ongoing CAN event processing + * and to prevent any concurrent access to the AHB1RSTR register. + */ + + flags = enter_critical_section(); + + /* Reset the CAN */ + + regval = getreg32(STM32_RCC_APB1RSTR); + regval |= regbit; + putreg32(regval, STM32_RCC_APB1RSTR); + + regval &= ~regbit; + putreg32(regval, STM32_RCC_APB1RSTR); + leave_critical_section(flags); +} + +/**************************************************************************** + * Name: stm32can_setup + * + * Description: + * Configure the CAN. This method is called the first time that the CAN + * device is opened. This will occur when the port is first opened. + * This setup includes configuring and attaching CAN interrupts. + * All CAN interrupts are disabled upon return. + * + * Input Parameters: + * dev - An instance of the "upper half" can driver state structure. + * + * Returned Value: + * Zero on success; a negated errno on failure + * + ****************************************************************************/ + +static int stm32can_setup(FAR struct can_dev_s *dev) +{ + FAR struct stm32_can_s *priv = dev->cd_priv; + int ret; + + caninfo("CAN%d RX0 irq: %d RX1 irq: %d TX irq: %d\n", + priv->port, priv->canrx[0], priv->canrx[1], priv->cantx); + + /* CAN cell initialization */ + + ret = stm32can_cellinit(priv); + if (ret < 0) + { + canerr("ERROR: CAN%d cell initialization failed: %d\n", + priv->port, ret); + return ret; + } + + stm32can_dumpctrlregs(priv, "After cell initialization"); + stm32can_dumpmbregs(priv, NULL); + + /* CAN filter initialization */ + + ret = stm32can_filterinit(priv); + if (ret < 0) + { + canerr("ERROR: CAN%d filter initialization failed: %d\n", + priv->port, ret); + return ret; + } + + stm32can_dumpfiltregs(priv, "After filter initialization"); + + /* Attach the CAN RX FIFO 0/1 interrupts and TX interrupts. + * The others are not used. + */ + + ret = irq_attach(priv->canrx[0], stm32can_rx0interrupt, dev); + if (ret < 0) + { + canerr("ERROR: Failed to attach CAN%d RX0 IRQ (%d)", + priv->port, priv->canrx[0]); + return ret; + } + + ret = irq_attach(priv->canrx[1], stm32can_rx1interrupt, dev); + if (ret < 0) + { + canerr("ERROR: Failed to attach CAN%d RX1 IRQ (%d)", + priv->port, priv->canrx[1]); + return ret; + } + + ret = irq_attach(priv->cantx, stm32can_txinterrupt, dev); + if (ret < 0) + { + canerr("ERROR: Failed to attach CAN%d TX IRQ (%d)", + priv->port, priv->cantx); + return ret; + } + + /* Enable the interrupts at the NVIC. Interrupts are still disabled in + * the CAN module. Since we coming out of reset here, there should be + * no pending interrupts. + */ + + up_enable_irq(priv->canrx[0]); + up_enable_irq(priv->canrx[1]); + up_enable_irq(priv->cantx); + return OK; +} + +/**************************************************************************** + * Name: stm32can_shutdown + * + * Description: + * Disable the CAN. This method is called when the CAN device is closed. + * This method reverses the operation the setup method. + * + * Input Parameters: + * dev - An instance of the "upper half" can driver state structure. + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void stm32can_shutdown(FAR struct can_dev_s *dev) +{ + FAR struct stm32_can_s *priv = dev->cd_priv; + + caninfo("CAN%d\n", priv->port); + + /* Disable the RX FIFO 0/1 and TX interrupts */ + + up_disable_irq(priv->canrx[0]); + up_disable_irq(priv->canrx[1]); + up_disable_irq(priv->cantx); + + /* Detach the RX FIFO 0/1 and TX interrupts */ + + irq_detach(priv->canrx[0]); + irq_detach(priv->canrx[1]); + irq_detach(priv->cantx); + + /* And reset the hardware */ + + stm32can_reset(dev); +} + +/**************************************************************************** + * Name: stm32can_rxint + * + * Description: + * Call to enable or disable RX interrupts. + * + * Input Parameters: + * dev - An instance of the "upper half" can driver state structure. + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void stm32can_rxint(FAR struct can_dev_s *dev, bool enable) +{ + FAR struct stm32_can_s *priv = dev->cd_priv; + uint32_t regval; + + caninfo("CAN%d enable: %d\n", priv->port, enable); + + /* Enable/disable the FIFO 0/1 message pending interrupt */ + + regval = stm32can_getreg(priv, STM32_CAN_IER_OFFSET); + if (enable) + { + regval |= CAN_IER_FMPIE0 | CAN_IER_FMPIE1; + } + else + { + regval &= ~(CAN_IER_FMPIE0 | CAN_IER_FMPIE1); + } + + stm32can_putreg(priv, STM32_CAN_IER_OFFSET, regval); +} + +/**************************************************************************** + * Name: stm32can_txint + * + * Description: + * Call to enable or disable TX interrupts. + * + * Input Parameters: + * dev - An instance of the "upper half" can driver state structure. + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void stm32can_txint(FAR struct can_dev_s *dev, bool enable) +{ + FAR struct stm32_can_s *priv = dev->cd_priv; + uint32_t regval; + + caninfo("CAN%d enable: %d\n", priv->port, enable); + + /* Support only disabling the transmit mailbox interrupt */ + + if (!enable) + { + regval = stm32can_getreg(priv, STM32_CAN_IER_OFFSET); + regval &= ~CAN_IER_TMEIE; + stm32can_putreg(priv, STM32_CAN_IER_OFFSET, regval); + } +} + +/**************************************************************************** + * Name: stm32can_ioctl + * + * Description: + * All ioctl calls will be routed through this method + * + * Input Parameters: + * dev - An instance of the "upper half" can driver state structure. + * + * Returned Value: + * Zero on success; a negated errno on failure + * + ****************************************************************************/ + +static int stm32can_ioctl(FAR struct can_dev_s *dev, int cmd, + unsigned long arg) +{ + FAR struct stm32_can_s *priv; + int ret = -ENOTTY; + + caninfo("cmd=%04x arg=%lu\n", cmd, arg); + + DEBUGASSERT(dev && dev->cd_priv); + priv = dev->cd_priv; + + /* Handle the command */ + + switch (cmd) + { + /* CANIOC_GET_BITTIMING: + * Description: Return the current bit timing settings + * Argument: A pointer to a write-able instance of struct + * canioc_bittiming_s in which current bit timing + * values will be returned. + * Returned Value: Zero (OK) is returned on success. Otherwise -1 + * (ERROR) is returned with the errno variable set + * to indicate the nature of the error. + * Dependencies: None + */ + + case CANIOC_GET_BITTIMING: + { + FAR struct canioc_bittiming_s *bt = + (FAR struct canioc_bittiming_s *)arg; + uint32_t regval; + uint32_t brp; + + DEBUGASSERT(bt != NULL); + + regval = stm32can_getreg(priv, STM32_CAN_BTR_OFFSET); + bt->bt_sjw = ((regval & CAN_BTR_SJW_MASK) >> CAN_BTR_SJW_SHIFT) + 1; + bt->bt_tseg1 = ((regval & CAN_BTR_TS1_MASK) >> CAN_BTR_TS1_SHIFT) + 1; + bt->bt_tseg2 = ((regval & CAN_BTR_TS2_MASK) >> CAN_BTR_TS2_SHIFT) + 1; + + brp = ((regval & CAN_BTR_BRP_MASK) >> CAN_BTR_BRP_SHIFT) + 1; + bt->bt_baud = STM32_PCLK1_FREQUENCY / + (brp * (bt->bt_tseg1 + bt->bt_tseg2 + 1)); + ret = OK; + } + break; + + /* CANIOC_SET_BITTIMING: + * Description: Set new current bit timing values + * Argument: A pointer to a read-able instance of struct + * canioc_bittiming_s in which the new bit timing + * values are provided. + * Returned Value: Zero (OK) is returned on success. Otherwise -1 + * (ERROR)is returned with the errno variable set + * to indicate thenature of the error. + * Dependencies: None + * + * REVISIT: There is probably a limitation here: If there are multiple + * threads trying to send CAN packets, when one of these threads + * reconfigures the bitrate, the MCAN hardware will be reset and the + * context of operation will be lost. Hence, this IOCTL can only safely + * be executed in quiescent time periods. + */ + + case CANIOC_SET_BITTIMING: + { + FAR const struct canioc_bittiming_s *bt = + (FAR const struct canioc_bittiming_s *)arg; + uint32_t brp; + uint32_t can_bit_quanta; + uint32_t tmp; + uint32_t regval; + + DEBUGASSERT(bt != NULL); + DEBUGASSERT(bt->bt_baud < STM32_PCLK1_FREQUENCY); + DEBUGASSERT(bt->bt_sjw > 0 && bt->bt_sjw <= 4); + DEBUGASSERT(bt->bt_tseg1 > 0 && bt->bt_tseg1 <= 16); + DEBUGASSERT(bt->bt_tseg2 > 0 && bt->bt_tseg2 <= 8); + + regval = stm32can_getreg(priv, STM32_CAN_BTR_OFFSET); + + /* Extract bit timing data. tmp is in clocks per bit time */ + + tmp = STM32_PCLK1_FREQUENCY / bt->bt_baud; + + /* This value is dynamic as requested by user */ + + can_bit_quanta = bt->bt_tseg1 + bt->bt_tseg2 + 1; + + if (tmp < can_bit_quanta) + { + /* This timing is not possible */ + + ret = -EINVAL; + break; + } + + /* Otherwise, nquanta is can_bit_quanta, ts1 and ts2 are + * provided by the user and we calculate brp to achieve + * can_bit_quanta quanta in the bit times + */ + + else + { + brp = (tmp + (can_bit_quanta / 2)) / can_bit_quanta; + DEBUGASSERT(brp >= 1 && brp <= CAN_BTR_BRP_MAX); + } + + caninfo("TS1: %d TS2: %d BRP: %d\n", + bt->bt_tseg1, bt->bt_tseg2, brp); + + /* Configure bit timing. */ + + regval &= ~(CAN_BTR_BRP_MASK | CAN_BTR_TS1_MASK | + CAN_BTR_TS2_MASK | CAN_BTR_SJW_MASK); + regval |= ((brp - 1) << CAN_BTR_BRP_SHIFT) | + ((bt->bt_tseg1 - 1) << CAN_BTR_TS1_SHIFT) | + ((bt->bt_tseg2 - 1) << CAN_BTR_TS2_SHIFT) | + ((bt->bt_sjw - 1) << CAN_BTR_SJW_SHIFT); + + /* Bit timing can only be configured in init mode. */ + + ret = stm32can_enterinitmode(priv); + if (ret < 0) + { + break; + } + + stm32can_putreg(priv, STM32_CAN_BTR_OFFSET, regval); + + ret = stm32can_exitinitmode(priv); + if (ret >= 0) + { + priv->baud = STM32_PCLK1_FREQUENCY / + (brp * (bt->bt_tseg1 + bt->bt_tseg2 + 1)); + } + } + break; + + /* CANIOC_GET_CONNMODES: + * Description: Get the current bus connection modes + * Argument: A pointer to a write-able instance of struct + * canioc_connmodes_s in which the new bus modes will + * be returned. + * Returned Value: Zero (OK) is returned on success. Otherwise -1 + * (ERROR)is returned with the errno variable set + * to indicate the nature of the error. + * Dependencies: None + */ + + case CANIOC_GET_CONNMODES: + { + FAR struct canioc_connmodes_s *bm = + (FAR struct canioc_connmodes_s *)arg; + uint32_t regval; + + DEBUGASSERT(bm != NULL); + + regval = stm32can_getreg(priv, STM32_CAN_BTR_OFFSET); + + bm->bm_loopback = ((regval & CAN_BTR_LBKM) == CAN_BTR_LBKM); + bm->bm_silent = ((regval & CAN_BTR_SILM) == CAN_BTR_SILM); + ret = OK; + break; + } + + /* CANIOC_SET_CONNMODES: + * Description: Set new bus connection modes values + * Argument: A pointer to a read-able instance of struct + * canioc_connmodes_s in which the new bus modes + * are provided. + * Returned Value: Zero (OK) is returned on success. Otherwise -1 + * (ERROR) is returned with the errno variable set + * to indicate the nature of the error. + * Dependencies: None + */ + + case CANIOC_SET_CONNMODES: + { + FAR struct canioc_connmodes_s *bm = + (FAR struct canioc_connmodes_s *)arg; + uint32_t regval; + + DEBUGASSERT(bm != NULL); + + regval = stm32can_getreg(priv, STM32_CAN_BTR_OFFSET); + + if (bm->bm_loopback) + { + regval |= CAN_BTR_LBKM; + } + else + { + regval &= ~CAN_BTR_LBKM; + } + + if (bm->bm_silent) + { + regval |= CAN_BTR_SILM; + } + else + { + regval &= ~CAN_BTR_SILM; + } + + /* This register can only be configured in init mode. */ + + ret = stm32can_enterinitmode(priv); + if (ret < 0) + { + break; + } + + stm32can_putreg(priv, STM32_CAN_BTR_OFFSET, regval); + + ret = stm32can_exitinitmode(priv); + } + break; + +#ifdef CONFIG_CAN_EXTID + /* CANIOC_ADD_EXTFILTER: + * Description: Add an address filter for a extended 29 bit + * address. + * Argument: A reference to struct canioc_extfilter_s + * Returned Value: A non-negative filter ID is returned on success. + * Otherwise -1 (ERROR) is returned with the errno + * variable set to indicate the nature of the error. + */ + + case CANIOC_ADD_EXTFILTER: + { + DEBUGASSERT(arg != 0); + ret = stm32can_addextfilter(priv, + (FAR struct canioc_extfilter_s *)arg); + } + break; + + /* CANIOC_DEL_EXTFILTER: + * Description: Remove an address filter for a standard 29 bit + * address. + * Argument: The filter index previously returned by the + * CANIOC_ADD_EXTFILTER command + * Returned Value: Zero (OK) is returned on success. Otherwise -1 + * (ERROR)is returned with the errno variable set + * to indicate the nature of the error. + */ + + case CANIOC_DEL_EXTFILTER: + { +#if 0 /* Unimplemented */ + DEBUGASSERT(arg <= priv->config->nextfilters); +#endif + ret = stm32can_delextfilter(priv, (int)arg); + } + break; +#endif + + /* CANIOC_ADD_STDFILTER: + * Description: Add an address filter for a standard 11 bit + * address. + * Argument: A reference to struct canioc_stdfilter_s + * Returned Value: A non-negative filter ID is returned on success. + * Otherwise -1 (ERROR) is returned with the errno + * variable set to indicate the nature of the error. + */ + + case CANIOC_ADD_STDFILTER: + { + DEBUGASSERT(arg != 0); + ret = stm32can_addstdfilter(priv, + (FAR struct canioc_stdfilter_s *)arg); + } + break; + + /* CANIOC_DEL_STDFILTER: + * Description: Remove an address filter for a standard 11 bit + * address. + * Argument: The filter index previously returned by the + * CANIOC_ADD_STDFILTER command + * Returned Value: Zero (OK) is returned on success. Otherwise -1 + * (ERROR) is returned with the errno variable set + * to indicate the nature of the error. + */ + + case CANIOC_DEL_STDFILTER: + { +#if 0 /* Unimplemented */ + DEBUGASSERT(arg <= priv->config->nstdfilters); +#endif + ret = stm32can_delstdfilter(priv, (int)arg); + } + break; + + /* Unsupported/unrecognized command */ + + default: + canerr("ERROR: Unrecognized command: %04x\n", cmd); + break; + } + + return ret; +} + +/**************************************************************************** + * Name: stm32can_remoterequest + * + * Description: + * Send a remote request + * + * Input Parameters: + * dev - An instance of the "upper half" can driver state structure. + * + * Returned Value: + * Zero on success; a negated errno on failure + * + ****************************************************************************/ + +static int stm32can_remoterequest(FAR struct can_dev_s *dev, uint16_t id) +{ +#warning "Remote request not implemented" + return -ENOSYS; +} + +/**************************************************************************** + * Name: stm32can_send + * + * Description: + * Send one can message. + * + * One CAN-message consists of a maximum of 10 bytes. A message is + * composed of at least the first 2 bytes (when there are no data bytes). + * + * Byte 0: Bits 0-7: Bits 3-10 of the 11-bit CAN identifier + * Byte 1: Bits 5-7: Bits 0-2 of the 11-bit CAN identifier + * Bit 4: Remote Tranmission Request (RTR) + * Bits 0-3: Data Length Code (DLC) + * Bytes 2-10: CAN data + * + * Input Parameters: + * dev - An instance of the "upper half" can driver state structure. + * + * Returned Value: + * Zero on success; a negated errno on failure + * + ****************************************************************************/ + +static int stm32can_send(FAR struct can_dev_s *dev, + FAR struct can_msg_s *msg) +{ + FAR struct stm32_can_s *priv = dev->cd_priv; + FAR uint8_t *ptr; + uint32_t regval; + uint32_t tmp; + int dlc; + int txmb; + + caninfo("CAN%d ID: %d DLC: %d\n", + priv->port, msg->cm_hdr.ch_id, msg->cm_hdr.ch_dlc); + + /* Select one empty transmit mailbox */ + + regval = stm32can_getreg(priv, STM32_CAN_TSR_OFFSET); + if (stm32can_txmb0empty(regval)) + { + txmb = 0; + } + else if (stm32can_txmb1empty(regval)) + { + txmb = 1; + } + else if (stm32can_txmb2empty(regval)) + { + txmb = 2; + } + else + { + canerr("ERROR: No available mailbox\n"); + return -EBUSY; + } + + /* Clear TXRQ, RTR, IDE, EXID, and STID fields */ + + regval = stm32can_getreg(priv, STM32_CAN_TIR_OFFSET(txmb)); + regval &= ~(CAN_TIR_TXRQ | CAN_TIR_RTR | CAN_TIR_IDE | + CAN_TIR_EXID_MASK | CAN_TIR_STID_MASK); + stm32can_putreg(priv, STM32_CAN_TIR_OFFSET(txmb), regval); + + /* Set up the ID, standard 11-bit or extended 29-bit. */ + +#ifdef CONFIG_CAN_EXTID + regval &= ~CAN_TIR_EXID_MASK; + if (msg->cm_hdr.ch_extid) + { + DEBUGASSERT(msg->cm_hdr.ch_id < (1 << 29)); + regval |= (msg->cm_hdr.ch_id << CAN_TIR_EXID_SHIFT) | CAN_TIR_IDE; + } + else + { + DEBUGASSERT(msg->cm_hdr.ch_id < (1 << 11)); + regval |= msg->cm_hdr.ch_id << CAN_TIR_STID_SHIFT; + } + +#else + regval &= ~CAN_TIR_STID_MASK; + regval |= (uint32_t)msg->cm_hdr.ch_id << CAN_TIR_STID_SHIFT; +#endif + + stm32can_putreg(priv, STM32_CAN_TIR_OFFSET(txmb), regval); + + /* Set up the DLC */ + + dlc = msg->cm_hdr.ch_dlc; + regval = stm32can_getreg(priv, STM32_CAN_TDTR_OFFSET(txmb)); + regval &= ~(CAN_TDTR_DLC_MASK | CAN_TDTR_TGT); + regval |= (uint32_t)dlc << CAN_TDTR_DLC_SHIFT; + stm32can_putreg(priv, STM32_CAN_TDTR_OFFSET(txmb), regval); + + /* Set up the data fields */ + + ptr = msg->cm_data; + regval = 0; + + if (dlc > 0) + { + tmp = (uint32_t)*ptr++; + regval = tmp << CAN_TDLR_DATA0_SHIFT; + + if (dlc > 1) + { + tmp = (uint32_t)*ptr++; + regval |= tmp << CAN_TDLR_DATA1_SHIFT; + + if (dlc > 2) + { + tmp = (uint32_t)*ptr++; + regval |= tmp << CAN_TDLR_DATA2_SHIFT; + + if (dlc > 3) + { + tmp = (uint32_t)*ptr++; + regval |= tmp << CAN_TDLR_DATA3_SHIFT; + } + } + } + } + + stm32can_putreg(priv, STM32_CAN_TDLR_OFFSET(txmb), regval); + + regval = 0; + if (dlc > 4) + { + tmp = (uint32_t)*ptr++; + regval = tmp << CAN_TDHR_DATA4_SHIFT; + + if (dlc > 5) + { + tmp = (uint32_t)*ptr++; + regval |= tmp << CAN_TDHR_DATA5_SHIFT; + + if (dlc > 6) + { + tmp = (uint32_t)*ptr++; + regval |= tmp << CAN_TDHR_DATA6_SHIFT; + + if (dlc > 7) + { + tmp = (uint32_t)*ptr++; + regval |= tmp << CAN_TDHR_DATA7_SHIFT; + } + } + } + } + + stm32can_putreg(priv, STM32_CAN_TDHR_OFFSET(txmb), regval); + + /* Enable the transmit mailbox empty interrupt (may already be enabled) */ + + regval = stm32can_getreg(priv, STM32_CAN_IER_OFFSET); + regval |= CAN_IER_TMEIE; + stm32can_putreg(priv, STM32_CAN_IER_OFFSET, regval); + + /* Request transmission */ + + regval = stm32can_getreg(priv, STM32_CAN_TIR_OFFSET(txmb)); + regval |= CAN_TIR_TXRQ; /* Transmit Mailbox Request */ + stm32can_putreg(priv, STM32_CAN_TIR_OFFSET(txmb), regval); + + stm32can_dumpmbregs(priv, "After send"); + return OK; +} + +/**************************************************************************** + * Name: stm32can_txready + * + * Description: + * Return true if the CAN hardware can accept another TX message. + * + * Input Parameters: + * dev - An instance of the "upper half" can driver state structure. + * + * Returned Value: + * True if the CAN hardware is ready to accept another TX message. + * + ****************************************************************************/ + +static bool stm32can_txready(FAR struct can_dev_s *dev) +{ + FAR struct stm32_can_s *priv = dev->cd_priv; + uint32_t regval; + + /* Return true if any mailbox is available */ + + regval = stm32can_getreg(priv, STM32_CAN_TSR_OFFSET); + caninfo("CAN%d TSR: %08x\n", priv->port, regval); + + return stm32can_txmb0empty(regval) || stm32can_txmb1empty(regval) || + stm32can_txmb2empty(regval); +} + +/**************************************************************************** + * Name: stm32can_txempty + * + * Description: + * Return true if all message have been sent. If for example, the CAN + * hardware implements FIFOs, then this would mean the transmit FIFO is + * empty. This method is called when the driver needs to make sure that + * all characters are "drained" from the TX hardware before calling + * co_shutdown(). + * + * Input Parameters: + * dev - An instance of the "upper half" can driver state structure. + * + * Returned Value: + * True if there are no pending TX transfers in the CAN hardware. + * + ****************************************************************************/ + +static bool stm32can_txempty(FAR struct can_dev_s *dev) +{ + FAR struct stm32_can_s *priv = dev->cd_priv; + uint32_t regval; + + /* Return true if all mailboxes are available */ + + regval = stm32can_getreg(priv, STM32_CAN_TSR_OFFSET); + caninfo("CAN%d TSR: %08x\n", priv->port, regval); + + return stm32can_txmb0empty(regval) && stm32can_txmb1empty(regval) && + stm32can_txmb2empty(regval); +} + +/**************************************************************************** + * Name: stm32can_rxinterrupt + * + * Description: + * CAN RX FIFO 0/1 interrupt handler + * + * Input Parameters: + * irq - The IRQ number of the interrupt. + * context - The register state save array at the time of the interrupt. + * rxmb - The RX mailbox number. + * + * Returned Value: + * Zero on success; a negated errno on failure + * + ****************************************************************************/ + +static int stm32can_rxinterrupt(FAR struct can_dev_s *dev, int rxmb) +{ + FAR struct stm32_can_s *priv; + struct can_hdr_s hdr; + uint8_t data[CAN_MAXDATALEN]; + uint32_t regval; + int npending; + int ret; + + DEBUGASSERT(dev != NULL && dev->cd_priv != NULL); + priv = dev->cd_priv; + + /* Verify that a message is pending in the FIFO */ + + regval = stm32can_getreg(priv, STM32_CAN_RFR_OFFSET(rxmb)); + npending = (regval & CAN_RFR_FMP_MASK) >> CAN_RFR_FMP_SHIFT; + if (npending < 1) + { + canwarn("WARNING: No messages pending\n"); + return OK; + } + + if (rxmb == 0) + { + stm32can_dumpmbregs(priv, "RX0 interrupt"); + } + else + { + stm32can_dumpmbregs(priv, "RX1 interrupt"); + } + + /* Get the CAN identifier. */ + + regval = stm32can_getreg(priv, STM32_CAN_RIR_OFFSET(rxmb)); + +#ifdef CONFIG_CAN_EXTID + if ((regval & CAN_RIR_IDE) != 0) + { + hdr.ch_id = (regval & CAN_RIR_EXID_MASK) >> CAN_RIR_EXID_SHIFT; + hdr.ch_extid = true; + } + else + { + hdr.ch_id = (regval & CAN_RIR_STID_MASK) >> CAN_RIR_STID_SHIFT; + hdr.ch_extid = false; + } +#else + if ((regval & CAN_RIR_IDE) != 0) + { + canerr("ERROR: Received message with extended identifier. Dropped\n"); + ret = -ENOSYS; + goto errout; + } + + hdr.ch_id = (regval & CAN_RIR_STID_MASK) >> CAN_RIR_STID_SHIFT; +#endif + + /* Clear the error indication and unused bits */ + +#ifdef CONFIG_CAN_ERRORS + hdr.ch_error = 0; /* Error reporting not supported */ +#endif + hdr.ch_unused = 0; + + /* Extract the RTR bit */ + + hdr.ch_rtr = (regval & CAN_RIR_RTR) != 0; + + /* Get the DLC */ + + regval = stm32can_getreg(priv, STM32_CAN_RDTR_OFFSET(rxmb)); + hdr.ch_dlc = (regval & CAN_RDTR_DLC_MASK) >> CAN_RDTR_DLC_SHIFT; + + /* Save the message data */ + + regval = stm32can_getreg(priv, STM32_CAN_RDLR_OFFSET(rxmb)); + data[0] = (regval & CAN_RDLR_DATA0_MASK) >> CAN_RDLR_DATA0_SHIFT; + data[1] = (regval & CAN_RDLR_DATA1_MASK) >> CAN_RDLR_DATA1_SHIFT; + data[2] = (regval & CAN_RDLR_DATA2_MASK) >> CAN_RDLR_DATA2_SHIFT; + data[3] = (regval & CAN_RDLR_DATA3_MASK) >> CAN_RDLR_DATA3_SHIFT; + + regval = stm32can_getreg(priv, STM32_CAN_RDHR_OFFSET(rxmb)); + data[4] = (regval & CAN_RDHR_DATA4_MASK) >> CAN_RDHR_DATA4_SHIFT; + data[5] = (regval & CAN_RDHR_DATA5_MASK) >> CAN_RDHR_DATA5_SHIFT; + data[6] = (regval & CAN_RDHR_DATA6_MASK) >> CAN_RDHR_DATA6_SHIFT; + data[7] = (regval & CAN_RDHR_DATA7_MASK) >> CAN_RDHR_DATA7_SHIFT; + + /* Provide the data to the upper half driver */ + + ret = can_receive(dev, &hdr, data); + + /* Release the FIFO */ + +#ifndef CONFIG_CAN_EXTID +errout: +#endif + regval = stm32can_getreg(priv, STM32_CAN_RFR_OFFSET(rxmb)); + regval |= CAN_RFR_RFOM; + stm32can_putreg(priv, STM32_CAN_RFR_OFFSET(rxmb), regval); + return ret; +} + +/**************************************************************************** + * Name: stm32can_rx0interrupt + * + * Description: + * CAN RX FIFO 0 interrupt handler + * + * Input Parameters: + * irq - The IRQ number of the interrupt. + * context - The register state save array at the time of the interrupt. + * + * Returned Value: + * Zero on success; a negated errno on failure + * + ****************************************************************************/ + +static int stm32can_rx0interrupt(int irq, FAR void *context, FAR void *arg) +{ + FAR struct can_dev_s *dev = (FAR struct can_dev_s *)arg; + return stm32can_rxinterrupt(dev, 0); +} + +/**************************************************************************** + * Name: stm32can_rx1interrupt + * + * Description: + * CAN RX FIFO 1 interrupt handler + * + * Input Parameters: + * irq - The IRQ number of the interrupt. + * context - The register state save array at the time of the interrupt. + * + * Returned Value: + * Zero on success; a negated errno on failure + * + ****************************************************************************/ + +static int stm32can_rx1interrupt(int irq, FAR void *context, FAR void *arg) +{ + FAR struct can_dev_s *dev = (FAR struct can_dev_s *)arg; + return stm32can_rxinterrupt(dev, 1); +} + +/**************************************************************************** + * Name: stm32can_txinterrupt + * + * Description: + * CAN TX mailbox complete interrupt handler + * + * Input Parameters: + * irq - The IRQ number of the interrupt. + * context - The register state save array at the time of the interrupt. + * + * Returned Value: + * Zero on success; a negated errno on failure + * + ****************************************************************************/ + +static int stm32can_txinterrupt(int irq, FAR void *context, FAR void *arg) +{ + FAR struct can_dev_s *dev = (FAR struct can_dev_s *)arg; + FAR struct stm32_can_s *priv; + uint32_t regval; + + DEBUGASSERT(dev != NULL && dev->cd_priv != NULL); + priv = dev->cd_priv; + + /* Get the transmit status */ + + regval = stm32can_getreg(priv, STM32_CAN_TSR_OFFSET); + + /* Check for RQCP0: Request completed mailbox 0 */ + + if ((regval & CAN_TSR_RQCP0) != 0) + { + /* Writing '1' to RCP0 clears RCP0 and all the status bits (TXOK0, + * ALST0 and TERR0) for Mailbox 0. + */ + + stm32can_putreg(priv, STM32_CAN_TSR_OFFSET, CAN_TSR_RQCP0); + + /* Tell the upper half that the transfer is finished. */ + + (void)can_txdone(dev); + } + + /* Check for RQCP1: Request completed mailbox 1 */ + + if ((regval & CAN_TSR_RQCP1) != 0) + { + /* Writing '1' to RCP1 clears RCP1 and all the status bits (TXOK1, + * ALST1 and TERR1) for Mailbox 1. + */ + + stm32can_putreg(priv, STM32_CAN_TSR_OFFSET, CAN_TSR_RQCP1); + + /* Tell the upper half that the transfer is finished. */ + + (void)can_txdone(dev); + } + + /* Check for RQCP2: Request completed mailbox 2 */ + + if ((regval & CAN_TSR_RQCP2) != 0) + { + /* Writing '1' to RCP2 clears RCP2 and all the status bits (TXOK2, + * ALST2 and TERR2) for Mailbox 2. + */ + + stm32can_putreg(priv, STM32_CAN_TSR_OFFSET, CAN_TSR_RQCP2); + + /* Tell the upper half that the transfer is finished. */ + + (void)can_txdone(dev); + } + + return OK; +} + +/**************************************************************************** + * Name: stm32can_bittiming + * + * Description: + * Set the CAN bit timing register (BTR) based on the configured BAUD. + * + * "The bit timing logic monitors the serial bus-line and performs sampling + * and adjustment of the sample point by synchronizing on the start-bit edge + * and resynchronizing on the following edges. + * + * "Its operation may be explained simply by splitting nominal bit time into + * three segments as follows: + * + * 1. "Synchronization segment (SYNC_SEG): a bit change is expected to occur + * within this time segment. It has a fixed length of one time quantum + * (1 x tCAN). + * 2. "Bit segment 1 (BS1): defines the location of the sample point. It + * includes the PROP_SEG and PHASE_SEG1 of the CAN standard. Its duration + * is programmable between 1 and 16 time quanta but may be automatically + * lengthened to compensate for positive phase drifts due to differences + * in the frequency of the various nodes of the network. + * 3. "Bit segment 2 (BS2): defines the location of the transmit point. It + * represents the PHASE_SEG2 of the CAN standard. Its duration is + * programmable between 1 and 8 time quanta but may also be automatically + * shortened to compensate for negative phase drifts." + * + * Pictorially: + * + * |<----------------- NOMINAL BIT TIME ----------------->| + * |<- SYNC_SEG ->|<------ BS1 ------>|<------ BS2 ------>| + * |<---- Tq ---->|<----- Tbs1 ------>|<----- Tbs2 ------>| + * + * Where + * Tbs1 is the duration of the BS1 segment + * Tbs2 is the duration of the BS2 segment + * Tq is the "Time Quantum" + * + * Relationships: + * + * baud = 1 / bit_time + * bit_time = Tq + Tbs1 + Tbs2 + * Tbs1 = Tq * ts1 + * Tbs2 = Tq * ts2 + * Tq = brp * Tpclk1 + * baud = Fpclk1 / (brp * (1 + ts1 + ts2)) + * + * Where: + * Tpclk1 is the period of the APB1 clock (PCLK1). + * + * Input Parameters: + * priv - A reference to the CAN block status + * + * Returned Value: + * Zero on success; a negated errno on failure + * + ****************************************************************************/ + +static int stm32can_bittiming(FAR struct stm32_can_s *priv) +{ + uint32_t tmp; + uint32_t brp; + uint32_t ts1; + uint32_t ts2; + + caninfo("CAN%d PCLK1: %d baud: %d\n", + priv->port, STM32_PCLK1_FREQUENCY, priv->baud); + + /* Try to get CAN_BIT_QUANTA quanta in one bit_time. + * + * bit_time = Tq*(ts1 + ts2 + 1) + * nquanta = bit_time / Tq + * nquanta = (ts1 + ts2 + 1) + * + * bit_time = brp * Tpclk1 * (ts1 + ts2 + 1) + * nquanta = bit_time / brp / Tpclk1 + * = PCLK1 / baud / brp + * brp = PCLK1 / baud / nquanta; + * + * Example: + * PCLK1 = 42,000,000 baud = 1,000,000 nquanta = 14 : brp = 3 + * PCLK1 = 42,000,000 baud = 700,000 nquanta = 14 : brp = 4 + */ + + tmp = STM32_PCLK1_FREQUENCY / priv->baud; + if (tmp < CAN_BIT_QUANTA) + { + /* At the smallest brp value (1), there are already too few bit times + * (PCLCK1 / baud) to meet our goal. brp must be one and we need + * make some reasonable guesses about ts1 and ts2. + */ + + brp = 1; + + /* In this case, we have to guess a good value for ts1 and ts2 */ + + ts1 = (tmp - 1) >> 1; + ts2 = tmp - ts1 - 1; + if (ts1 == ts2 && ts1 > 1 && ts2 < CAN_BTR_TSEG2_MAX) + { + ts1--; + ts2++; + } + } + + /* Otherwise, nquanta is CAN_BIT_QUANTA, ts1 is CONFIG_STM32_CAN_TSEG1, + * ts2 is CONFIG_STM32_CAN_TSEG2 and we calculate brp to achieve + * CAN_BIT_QUANTA quanta in the bit time + */ + + else + { + ts1 = CONFIG_STM32_CAN_TSEG1; + ts2 = CONFIG_STM32_CAN_TSEG2; + brp = (tmp + (CAN_BIT_QUANTA / 2)) / CAN_BIT_QUANTA; + DEBUGASSERT(brp >= 1 && brp <= CAN_BTR_BRP_MAX); + } + + caninfo("TS1: %d TS2: %d BRP: %d\n", ts1, ts2, brp); + + /* Configure bit timing. This also does the following, less obvious + * things. Unless loopback mode is enabled, it: + * + * - Disables silent mode. + * - Disables loopback mode. + * + * NOTE that for the time being, SJW is set to 1 just because I don't + * know any better. + */ + + tmp = ((brp - 1) << CAN_BTR_BRP_SHIFT) | ((ts1 - 1) << CAN_BTR_TS1_SHIFT) | + ((ts2 - 1) << CAN_BTR_TS2_SHIFT) | ((1 - 1) << CAN_BTR_SJW_SHIFT); +#ifdef CONFIG_CAN_LOOPBACK + tmp |= CAN_BTR_LBKM; +#endif + + stm32can_putreg(priv, STM32_CAN_BTR_OFFSET, tmp); + return OK; +} + +/**************************************************************************** + * Name: stm32can_enterinitmode + * + * Description: + * Put the CAN cell in Initialization mode. This only disconnects the CAN + * peripheral, no registers are changed. The initialization mode is + * required to change the baud rate. + * + * Input Parameters: + * priv - A pointer to the private data structure for this CAN block + * + * Returned Value: + * Zero on success; a negated errno value on failure. + * + ****************************************************************************/ + +static int stm32can_enterinitmode(FAR struct stm32_can_s *priv) +{ + uint32_t regval; + volatile uint32_t timeout; + + caninfo("CAN%d\n", priv->port); + + /* Enter initialization mode */ + + regval = stm32can_getreg(priv, STM32_CAN_MCR_OFFSET); + regval |= CAN_MCR_INRQ; + stm32can_putreg(priv, STM32_CAN_MCR_OFFSET, regval); + + /* Wait until initialization mode is acknowledged */ + + for (timeout = INAK_TIMEOUT; timeout > 0; timeout--) + { + regval = stm32can_getreg(priv, STM32_CAN_MSR_OFFSET); + if ((regval & CAN_MSR_INAK) != 0) + { + /* We are in initialization mode */ + + break; + } + } + + /* Check for a timeout */ + + if (timeout < 1) + { + canerr("ERROR: Timed out waiting to enter initialization mode\n"); + return -ETIMEDOUT; + } + + return OK; +} + +/**************************************************************************** + * Name: stm32can_exitinitmode + * + * Description: + * Put the CAN cell out of the Initialization mode (to Normal mode) + * + * Input Parameters: + * priv - A pointer to the private data structure for this CAN block + * + * Returned Value: + * Zero on success; a negated errno value on failure. + * + ****************************************************************************/ + +static int stm32can_exitinitmode(FAR struct stm32_can_s *priv) +{ + uint32_t regval; + volatile uint32_t timeout; + + /* Exit Initialization mode, enter Normal mode */ + + regval = stm32can_getreg(priv, STM32_CAN_MCR_OFFSET); + regval &= ~CAN_MCR_INRQ; + stm32can_putreg(priv, STM32_CAN_MCR_OFFSET, regval); + + /* Wait until the initialization mode exit is acknowledged */ + + for (timeout = INAK_TIMEOUT; timeout > 0; timeout--) + { + regval = stm32can_getreg(priv, STM32_CAN_MSR_OFFSET); + if ((regval & CAN_MSR_INAK) == 0) + { + /* We are out of initialization mode */ + + break; + } + } + + /* Check for a timeout */ + + if (timeout < 1) + { + canerr("ERROR: Timed out waiting to exit initialization mode: %08x\n", + regval); + return -ETIMEDOUT; + } + + return OK; +} + +/**************************************************************************** + * Name: stm32can_cellinit + * + * Description: + * CAN cell initialization + * + * Input Parameters: + * priv - A pointer to the private data structure for this CAN block + * + * Returned Value: + * Zero on success; a negated errno value on failure. + * + ****************************************************************************/ + +static int stm32can_cellinit(FAR struct stm32_can_s *priv) +{ + uint32_t regval; + int ret; + + caninfo("CAN%d\n", priv->port); + + /* Exit from sleep mode */ + + regval = stm32can_getreg(priv, STM32_CAN_MCR_OFFSET); + regval &= ~CAN_MCR_SLEEP; + stm32can_putreg(priv, STM32_CAN_MCR_OFFSET, regval); + + ret = stm32can_enterinitmode(priv); + if (ret != 0) + { + return ret; + } + + /* Disable the following modes: + * + * - Time triggered communication mode + * - Automatic bus-off management + * - Automatic wake-up mode + * - No automatic retransmission + * - Receive FIFO locked mode + * + * Enable: + * + * - Transmit FIFO priority + */ + + regval = stm32can_getreg(priv, STM32_CAN_MCR_OFFSET); + regval &= ~(CAN_MCR_RFLM | CAN_MCR_NART | CAN_MCR_AWUM | + CAN_MCR_ABOM | CAN_MCR_TTCM); + regval |= CAN_MCR_TXFP; + stm32can_putreg(priv, STM32_CAN_MCR_OFFSET, regval); + + /* Configure bit timing. */ + + ret = stm32can_bittiming(priv); + if (ret < 0) + { + canerr("ERROR: Failed to set bit timing: %d\n", ret); + return ret; + } + + return stm32can_exitinitmode(priv); +} + +/**************************************************************************** + * Name: stm32can_filterinit + * + * Description: + * CAN filter initialization. CAN filters are not currently used by this + * driver. The CAN filters can be configured in a different way: + * + * 1. As a match of specific IDs in a list (IdList mode), or as + * 2. And ID and a mask (IdMask mode). + * + * Filters can also be configured as: + * + * 3. 16- or 32-bit. The advantage of 16-bit filters is that you get + * more filters; The advantage of 32-bit filters is that you get + * finer control of the filtering. + * + * One filter is set up for each CAN. The filter resources are shared + * between the two CAN modules: CAN1 uses only filter 0 (but reserves + * 0 through CAN_NFILTERS/2-1); CAN2 uses only filter CAN_NFILTERS/2 + * (but reserves CAN_NFILTERS/2 through CAN_NFILTERS-1). + * + * 32-bit IdMask mode is configured. However, both the ID and the MASK + * are set to zero thus supressing all filtering because anything masked + * with zero matches zero. + * + * Input Parameters: + * priv - A pointer to the private data structure for this CAN block + * + * Returned Value: + * Zero on success; a negated errno value on failure. + * + ****************************************************************************/ + +static int stm32can_filterinit(FAR struct stm32_can_s *priv) +{ + uint32_t regval; + uint32_t bitmask; + + caninfo("CAN%d filter: %d\n", priv->port, priv->filter); + + /* Get the bitmask associated with the filter used by this CAN block */ + + bitmask = (uint32_t)1 << priv->filter; + + /* Enter filter initialization mode */ + + regval = stm32can_getfreg(priv, STM32_CAN_FMR_OFFSET); + regval |= CAN_FMR_FINIT; + stm32can_putfreg(priv, STM32_CAN_FMR_OFFSET, regval); + + /* Assign half the filters to CAN1, half to CAN2 */ + +#if defined(CONFIG_STM32_CONNECTIVITYLINE) || \ + defined(CONFIG_STM32_STM32F20XX) || \ + defined(CONFIG_STM32_STM32F4XXX) + regval = stm32can_getfreg(priv, STM32_CAN_FMR_OFFSET); + regval &= CAN_FMR_CAN2SB_MASK; + regval |= (CAN_NFILTERS / 2) << CAN_FMR_CAN2SB_SHIFT; + stm32can_putfreg(priv, STM32_CAN_FMR_OFFSET, regval); +#endif + + /* Disable the filter */ + + regval = stm32can_getfreg(priv, STM32_CAN_FA1R_OFFSET); + regval &= ~bitmask; + stm32can_putfreg(priv, STM32_CAN_FA1R_OFFSET, regval); + + /* Select the 32-bit scale for the filter */ + + regval = stm32can_getfreg(priv, STM32_CAN_FS1R_OFFSET); + regval |= bitmask; + stm32can_putfreg(priv, STM32_CAN_FS1R_OFFSET, regval); + + /* There are 14 or 28 filter banks (depending) on the device. + * Each filter bank is composed of two 32-bit registers, CAN_FiR: + */ + + stm32can_putfreg(priv, STM32_CAN_FIR_OFFSET(priv->filter, 1), 0); + stm32can_putfreg(priv, STM32_CAN_FIR_OFFSET(priv->filter, 2), 0); + + /* Set Id/Mask mode for the filter */ + + regval = stm32can_getfreg(priv, STM32_CAN_FM1R_OFFSET); + regval &= ~bitmask; + stm32can_putfreg(priv, STM32_CAN_FM1R_OFFSET, regval); + + /* Assign FIFO 0 for the filter */ + + regval = stm32can_getfreg(priv, STM32_CAN_FFA1R_OFFSET); + regval &= ~bitmask; + stm32can_putfreg(priv, STM32_CAN_FFA1R_OFFSET, regval); + + /* Enable the filter */ + + regval = stm32can_getfreg(priv, STM32_CAN_FA1R_OFFSET); + regval |= bitmask; + stm32can_putfreg(priv, STM32_CAN_FA1R_OFFSET, regval); + + /* Exit filter initialization mode */ + + regval = stm32can_getfreg(priv, STM32_CAN_FMR_OFFSET); + regval &= ~CAN_FMR_FINIT; + stm32can_putfreg(priv, STM32_CAN_FMR_OFFSET, regval); + return OK; +} + +/**************************************************************************** + * Name: stm32can_addextfilter + * + * Description: + * Add a filter for extended CAN IDs + * + * Input Parameters: + * priv - A pointer to the private data structure for this CAN block + * arg - A pointer to a structure describing the filter + * + * Returned Value: + * A non-negative filter ID is returned on success. + * Otherwise -1 (ERROR) is returned with the errno + * set to indicate the nature of the error. + * + ****************************************************************************/ + +#ifdef CONFIG_CAN_EXTID +static int stm32can_addextfilter(FAR struct stm32_can_s *priv, + FAR struct canioc_extfilter_s *arg) +{ + return -ENOTTY; +} +#endif + +/**************************************************************************** + * Name: stm32can_delextfilter + * + * Description: + * Remove a filter for extended CAN IDs + * + * Input Parameters: + * priv - A pointer to the private data structure for this CAN block + * arg - The filter index previously returned by the + * CANIOC_ADD_EXTFILTER command + * + * Returned Value: + * Zero (OK) is returned on success. Otherwise -1 (ERROR) + * returned with the errno variable set to indicate the + * of the error. + * + ****************************************************************************/ + +#ifdef CONFIG_CAN_EXTID +static int stm32can_delextfilter(FAR struct stm32_can_s *priv, int arg) +{ + return -ENOTTY; +} +#endif + +/**************************************************************************** + * Name: stm32can_addstdfilter + * + * Description: + * Add a filter for standard CAN IDs + * + * Input Parameters: + * priv - A pointer to the private data structure for this CAN block + * arg - A pointer to a structure describing the filter + * + * Returned Value: + * A non-negative filter ID is returned on success. + * Otherwise -1 (ERROR) is returned with the errno + * set to indicate the nature of the error. + * + ****************************************************************************/ + +static int stm32can_addstdfilter(FAR struct stm32_can_s *priv, + FAR struct canioc_stdfilter_s *arg) +{ + return -ENOTTY; +} + +/**************************************************************************** + * Name: stm32can_delstdfilter + * + * Description: + * Remove a filter for standard CAN IDs + * + * Input Parameters: + * priv - A pointer to the private data structure for this CAN block + * arg - The filter index previously returned by the + * CANIOC_ADD_STDFILTER command + * + * Returned Value: + * Zero (OK) is returned on success. Otherwise -1 (ERROR) + * returned with the errno variable set to indicate the + * of the error. + * + ****************************************************************************/ + +static int stm32can_delstdfilter(FAR struct stm32_can_s *priv, int arg) +{ + return -ENOTTY; +} + +/**************************************************************************** + * Name: stm32can_txmb0empty + * + * Input Parameters: + * tsr_regval - value of CAN transmit status register + * + * Returned Value: + * Returns true if mailbox 0 is empty and can be used for sending. + * + ****************************************************************************/ + +static bool stm32can_txmb0empty(uint32_t tsr_regval) +{ + return (tsr_regval & CAN_TSR_TME0) != 0 && + (tsr_regval & CAN_TSR_RQCP0) == 0; +} + +/**************************************************************************** + * Name: stm32can_txmb1empty + * + * Input Parameters: + * tsr_regval - value of CAN transmit status register + * + * Returned Value: + * Returns true if mailbox 1 is empty and can be used for sending. + * + ****************************************************************************/ + +static bool stm32can_txmb1empty(uint32_t tsr_regval) +{ + return (tsr_regval & CAN_TSR_TME1) != 0 && + (tsr_regval & CAN_TSR_RQCP1) == 0; +} + +/**************************************************************************** + * Name: stm32can_txmb2empty + * + * Input Parameters: + * tsr_regval - value of CAN transmit status register + * + * Returned Value: + * Returns true if mailbox 2 is empty and can be used for sending. + * + ****************************************************************************/ + +static bool stm32can_txmb2empty(uint32_t tsr_regval) +{ + return (tsr_regval & CAN_TSR_TME2) != 0 && + (tsr_regval & CAN_TSR_RQCP2) == 0; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: stm32_caninitialize + * + * Description: + * Initialize the selected CAN port + * + * Input Parameters: + * Port number (for hardware that has mutiple CAN interfaces) + * + * Returned Value: + * Valid CAN device structure reference on succcess; a NULL on failure + * + ****************************************************************************/ + +FAR struct can_dev_s *stm32_caninitialize(int port) +{ + FAR struct can_dev_s *dev = NULL; + + caninfo("CAN%d\n", port); + + /* NOTE: Peripherical clocking for CAN1 and/or CAN2 was already provided + * by stm32_clockconfig() early in the reset sequence. + */ + +#ifdef CONFIG_STM32F7_CAN1 + if (port == 1) + { + /* Select the CAN1 device structure */ + + dev = &g_can1dev; + + /* Configure CAN1 pins. The ambiguous settings in the stm32*_pinmap.h + * file must have been disambiguated in the board.h file. + */ + + stm32_configgpio(GPIO_CAN1_RX); + stm32_configgpio(GPIO_CAN1_TX); + } + else +#endif +#ifdef CONFIG_STM32F7_CAN2 + if (port == 2) + { + /* Select the CAN2 device structure */ + + dev = &g_can2dev; + + /* Configure CAN2 pins. The ambiguous settings in the stm32*_pinmap.h + * file must have been disambiguated in the board.h file. + */ + + stm32_configgpio(GPIO_CAN2_RX); + stm32_configgpio(GPIO_CAN2_TX); + } + else +#endif +#ifdef CONFIG_STM32F7_CAN3 + if (port == 3) + { + /* Select the CAN3 device structure */ + + dev = &g_can3dev; + + /* Configure CANZ pins. The ambiguous settings in the stm32*_pinmap.h + * file must have been disambiguated in the board.h file. + */ + + stm32_configgpio(GPIO_CAN3_RX); + stm32_configgpio(GPIO_CAN3_TX); + } + else +#endif + { + canerr("ERROR: Unsupported port %d\n", port); + return NULL; + } + + return dev; +} + +#endif /* CONFIG_CAN && (CONFIG_STM32_CAN1 || CONFIG_STM32_CAN2) */ diff --git a/arch/arm/src/stm32f7/stm32_can.h b/arch/arm/src/stm32f7/stm32_can.h new file mode 100644 index 00000000000..d622ee3cb66 --- /dev/null +++ b/arch/arm/src/stm32f7/stm32_can.h @@ -0,0 +1,143 @@ +/************************************************************************************ + * arch/arm/src/stm32/stm32_can.h + * + * Copyright (C) 2009, 2011, 2015 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ************************************************************************************/ + +#ifndef __ARCH_ARM_SRC_STM32_STM32F7_CAN_H +#define __ARCH_ARM_SRC_STM32_STM32F7_CAN_H + +/************************************************************************************ + * Included Files + ************************************************************************************/ + +#include + +#include "chip.h" +#include "chip/stm32_can.h" + +#include + +/************************************************************************************ + * Pre-processor Definitions + ************************************************************************************/ + +/* Configuration ********************************************************************/ + +#if defined(CONFIG_CAN) && (defined(CONFIG_STM32F7_CAN1) || \ + defined(CONFIG_STM32F7_CAN2) || defined(CONFIG_STM32F7_CAN3)) + +/* CAN BAUD */ + +#if defined(CONFIG_STM32F7_CAN1) && !defined(CONFIG_STM32F7_CAN1_BAUD) +# error "CONFIG_STM32F7_CAN1_BAUD is not defined" +#endif + +#if defined(CONFIG_STM32F7_CAN2) && !defined(CONFIG_STM32F7_CAN2_BAUD) +# error "CONFIG_STM32F7_CAN2_BAUD is not defined" +#endif + +#if defined(CONFIG_STM32F7_CAN3) && !defined(CONFIG_STM32F7_CAN3_BAUD) +# error "CONFIG_STM32F7_CAN3_BAUD is not defined" +#endif + +/* User-defined TSEG1 and TSEG2 settings may be used. + * + * CONFIG_STM32_CAN_TSEG1 = the number of CAN time quanta in segment 1 + * CONFIG_STM32_CAN_TSEG2 = the number of CAN time quanta in segment 2 + * CAN_BIT_QUANTA = The number of CAN time quanta in on bit time + */ + +#ifndef CONFIG_STM32_CAN_TSEG1 +# define CONFIG_STM32_CAN_TSEG1 6 +#endif + +#if CONFIG_STM32_CAN_TSEG1 < 1 || CONFIG_STM32_CAN_TSEG1 > CAN_BTR_TSEG1_MAX +# errror "CONFIG_STM32_CAN_TSEG1 is out of range" +#endif + +#ifndef CONFIG_STM32_CAN_TSEG2 +# define CONFIG_STM32_CAN_TSEG2 7 +#endif + +#if CONFIG_STM32_CAN_TSEG2 < 1 || CONFIG_STM32_CAN_TSEG2 > CAN_BTR_TSEG2_MAX +# errror "CONFIG_STM32_CAN_TSEG2 is out of range" +#endif + +/************************************************************************************ + * Public Types + ************************************************************************************/ + +#ifndef __ASSEMBLY__ + +/************************************************************************************ + * Public Data + ************************************************************************************/ + +#undef EXTERN +#if defined(__cplusplus) +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + +/************************************************************************************ + * Public Functions + ************************************************************************************/ + +/**************************************************************************** + * Name: stm32_caninitialize + * + * Description: + * Initialize the selected CAN port + * + * Input Parameters: + * Port number (for hardware that has mutiple CAN interfaces) + * + * Returned Value: + * Valid CAN device structure reference on succcess; a NULL on failure + * + ****************************************************************************/ + +struct can_dev_s; +FAR struct can_dev_s *stm32_caninitialize(int port); + +#undef EXTERN +#if defined(__cplusplus) +} +#endif + +#endif /* __ASSEMBLY__ */ +#endif /* CONFIG_CAN && (CONFIG_STM32_CAN1 || CONFIG_STM32_CAN2) */ +#endif /* __ARCH_ARM_SRC_STM32_STM32_CAN_H */ diff --git a/arch/arm/src/stm32f7/stm32f76xx77xx_rcc.c b/arch/arm/src/stm32f7/stm32f76xx77xx_rcc.c index aa31490f169..742774b48ce 100644 --- a/arch/arm/src/stm32f7/stm32f76xx77xx_rcc.c +++ b/arch/arm/src/stm32f7/stm32f76xx77xx_rcc.c @@ -496,6 +496,13 @@ static inline void rcc_enableapb1(void) regval |= (RCC_APB1ENR_CAN1EN | RCC_APB1ENR_CAN2EN); #endif +#ifdef CONFIG_STM32F7_CAN3 + /* CAN3 clock enable. */ + + regval |= (RCC_APB1ENR_CAN3EN); +#endif + + #ifdef CONFIG_STM32F7_CEC /* CEC clock enable. */