nuttx/arch/arm/src/stm32/stm32_fdcan_sock.c
Xiang Xiao fcb3e84c24 can: Merge netpacket/can.h into nuttx/can.h
To align with the layout of Linux can header file.

Signed-off-by: Xiang Xiao <xiaoxiang@xiaomi.com>
2024-08-26 05:05:31 -04:00

3324 lines
93 KiB
C

/****************************************************************************
* arch/arm/src/stm32/stm32_fdcan_sock.c
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*s
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <inttypes.h>
#include <stdio.h>
#include <sys/types.h>
#include <stdint.h>
#include <stdbool.h>
#include <assert.h>
#include <errno.h>
#include <debug.h>
#include <arch/board/board.h>
#include <nuttx/irq.h>
#include <nuttx/arch.h>
#include <nuttx/wqueue.h>
#include <nuttx/net/netdev.h>
#include <nuttx/net/can.h>
#include "arm_internal.h"
#include "stm32_fdcan.h"
#include "hardware/stm32_pinmap.h"
#include "stm32_gpio.h"
#include "stm32_rcc.h"
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
/* Pool configuration *******************************************************/
#define POOL_SIZE (1)
/* Work queue support is required. */
#if !defined(CONFIG_SCHED_WORKQUEUE)
# error Work queue support is required
#endif
/* The low priority work queue is preferred. If it is not enabled, LPWORK
* will be the same as HPWORK.
*
* NOTE: However, the network should NEVER run on the high priority work
* queue! That queue is intended only to service short back end interrupt
* processing that never suspends. Suspending the high priority work queue
* may bring the system to its knees!
*/
#define CANWORK LPWORK
/* Clock source *************************************************************/
#define FDCANCLK_PDIV (0)
#if FDCANCLK_PDIV == 0
# define STM32_FDCANCLK_FREQUENCY (STM32_FDCAN_FREQUENCY / (1))
#else
# define STM32_FDCANCLK_FREQUENCY (STM32_FDCAN_FREQUENCY / (2 * FDCANCLK_PDIV))
#endif
/* General Configuration ****************************************************/
#if defined(CONFIG_STM32_STM32G4XXX)
/* FDCAN Message RAM */
# define FDCAN_MSGRAM_WORDS (212)
# define STM32_CANRAM1_BASE (STM32_CANRAM_BASE + 0x0000)
# define STM32_CANRAM2_BASE (STM32_CANRAM_BASE + 1*(FDCAN_MSGRAM_WORDS * 4) + 4)
# define STM32_CANRAM3_BASE (STM32_CANRAM_BASE + 2*(FDCAN_MSGRAM_WORDS * 4) + 4)
# ifdef CONFIG_STM32_FDCAN1
# define FDCAN1_STDFILTER_SIZE (28)
# define FDCAN1_EXTFILTER_SIZE (8)
# define FDCAN1_RXFIFO0_SIZE (3)
# define FDCAN1_RXFIFO1_SIZE (3)
# define FDCAN1_TXEVENTFIFO_SIZE (3)
# define FDCAN1_TXFIFIOQ_SIZE (3)
# define FDCAN1_STDFILTER_WORDS (28)
# define FDCAN1_EXTFILTER_WORDS (16)
# define FDCAN1_RXFIFO0_WORDS (54)
# define FDCAN1_RXFIFO1_WORDS (54)
# define FDCAN1_TXEVENTFIFO_WORDS (6)
# define FDCAN1_TXFIFIOQ_WORDS (54)
# endif
# ifdef CONFIG_STM32_FDCAN2
# define FDCAN2_STDFILTER_SIZE (28)
# define FDCAN2_EXTFILTER_SIZE (8)
# define FDCAN2_RXFIFO0_SIZE (3)
# define FDCAN2_RXFIFO1_SIZE (3)
# define FDCAN2_TXEVENTFIFO_SIZE (3)
# define FDCAN2_TXFIFIOQ_SIZE (3)
# define FDCAN2_STDFILTER_WORDS (28)
# define FDCAN2_EXTFILTER_WORDS (16)
# define FDCAN2_RXFIFO0_WORDS (54)
# define FDCAN2_RXFIFO1_WORDS (54)
# define FDCAN2_TXEVENTFIFO_WORDS (6)
# define FDCAN2_TXFIFIOQ_WORDS (54)
# endif
# ifdef CONFIG_STM32_FDCAN3
# define FDCAN3_STDFILTER_SIZE (28)
# define FDCAN3_EXTFILTER_SIZE (8)
# define FDCAN3_RXFIFO0_SIZE (3)
# define FDCAN3_RXFIFO1_SIZE (3)
# define FDCAN3_TXEVENTFIFO_SIZE (3)
# define FDCAN3_TXFIFIOQ_SIZE (3)
# define FDCAN3_STDFILTER_WORDS (28)
# define FDCAN3_EXTFILTER_WORDS (16)
# define FDCAN3_RXFIFO0_WORDS (54)
# define FDCAN3_RXFIFO1_WORDS (54)
# define FDCAN3_TXEVENTFIFO_WORDS (6)
# define FDCAN3_TXFIFIOQ_WORDS (54)
# endif
#else
# error
#endif
/* FDCAN1 Configuration *****************************************************/
#ifdef CONFIG_STM32_FDCAN1
/* Bit timing */
# define FDCAN1_NTSEG1 (CONFIG_STM32_FDCAN1_NTSEG1 - 1)
# define FDCAN1_NTSEG2 (CONFIG_STM32_FDCAN1_NTSEG2 - 1)
# define FDCAN1_NBRP ((STM32_FDCANCLK_FREQUENCY / \
((FDCAN1_NTSEG1 + FDCAN1_NTSEG2 + 3) * \
CONFIG_STM32_FDCAN1_BITRATE)) - 1)
# define FDCAN1_NSJW (CONFIG_STM32_FDCAN1_NSJW - 1)
# if FDCAN1_NTSEG1 > FDCAN_NBTP_NTSEG1_MAX
# error Invalid FDCAN1 NTSEG1
# endif
# if FDCAN1_NTSEG2 > FDCAN_NBTP_NTSEG2_MAX
# error Invalid FDCAN1 NTSEG2
# endif
# if FDCAN1_NSJW > FDCAN_NBTP_NSJW_MAX
# error Invalid FDCAN1 NSJW
# endif
# if FDCAN1_NBRP > FDCAN_NBTP_NBRP_MAX
# error Invalid FDCAN1 NBRP
# endif
# ifdef CONFIG_STM32_FDCAN1_FD_BRS
# define FDCAN1_DTSEG1 (CONFIG_STM32_FDCAN1_DTSEG1 - 1)
# define FDCAN1_DTSEG2 (CONFIG_STM32_FDCAN1_DTSEG2 - 1)
# define FDCAN1_DBRP ((STM32_FDCANCLK_FREQUENCY / \
((FDCAN1_DTSEG1 + FDCAN1_DTSEG2 + 3) * \
CONFIG_STM32_FDCAN1_DBITRATE)) - 1)
# define FDCAN1_DSJW (CONFIG_STM32_FDCAN1_DSJW - 1)
# else
# define FDCAN1_DTSEG1 1
# define FDCAN1_DTSEG2 1
# define FDCAN1_DBRP 1
# define FDCAN1_DSJW 1
# endif /* CONFIG_STM32_FDCAN1_FD_BRS */
# if FDCAN1_DTSEG1 > FDCAN_DBTP_DTSEG1_MAX
# error Invalid FDCAN1 DTSEG1
# endif
# if FDCAN1_DTSEG2 > FDCAN_DBTP_DTSEG2_MAX
# error Invalid FDCAN1 DTSEG2
# endif
# if FDCAN1_DBRP > FDCAN_DBTP_DBRP_MAX
# error Invalid FDCAN1 DBRP
# endif
# if FDCAN1_DSJW > FDCAN_DBTP_DSJW_MAX
# error Invalid FDCAN1 DSJW
# endif
/* FDCAN1 Message RAM Configuration *****************************************/
/* FDCAN1 Message RAM Layout */
# define FDCAN1_STDFILTER_INDEX 0
# define FDCAN1_EXTFILTERS_INDEX (FDCAN1_STDFILTER_INDEX + FDCAN1_STDFILTER_WORDS)
# define FDCAN1_RXFIFO0_INDEX (FDCAN1_EXTFILTERS_INDEX + FDCAN1_EXTFILTER_WORDS)
# define FDCAN1_RXFIFO1_INDEX (FDCAN1_RXFIFO0_INDEX + FDCAN1_RXFIFO0_WORDS)
# define FDCAN1_TXEVENTFIFO_INDEX (FDCAN1_RXFIFO1_INDEX + FDCAN1_RXFIFO1_WORDS)
# define FDCAN1_TXFIFOQ_INDEX (FDCAN1_TXEVENTFIFO_INDEX + FDCAN1_TXEVENTFIFO_WORDS)
# define FDCAN1_MSGRAM_WORDS (FDCAN1_TXFIFOQ_INDEX + FDCAN1_TXFIFIOQ_WORDS)
#endif /* CONFIG_STM32_FDCAN1 */
/* FDCAN2 Configuration *****************************************************/
#ifdef CONFIG_STM32_FDCAN2
/* Bit timing */
# define FDCAN2_NTSEG1 (CONFIG_STM32_FDCAN2_NTSEG1 - 1)
# define FDCAN2_NTSEG2 (CONFIG_STM32_FDCAN2_NTSEG2 - 1)
# define FDCAN2_NBRP (((STM32_FDCANCLK_FREQUENCY / \
((FDCAN2_NTSEG1 + FDCAN2_NTSEG2 + 3) * \
CONFIG_STM32_FDCAN2_BITRATE)) - 1))
# define FDCAN2_NSJW (CONFIG_STM32_FDCAN2_NSJW - 1)
# if FDCAN2_NTSEG1 > FDCAN_NBTP_NTSEG1_MAX
# error Invalid FDCAN2 NTSEG1
# endif
# if FDCAN2_NTSEG2 > FDCAN_NBTP_NTSEG2_MAX
# error Invalid FDCAN2 NTSEG2
# endif
# if FDCAN2_NSJW > FDCAN_NBTP_NSJW_MAX
# error Invalid FDCAN2 NSJW
# endif
# if FDCAN2_NBRP > FDCAN_NBTP_NBRP_MAX
# error Invalid FDCAN1 NBRP
# endif
# ifdef CONFIG_STM32_FDCAN2_FD_BRS
# define FDCAN2_DTSEG1 (CONFIG_STM32_FDCAN2_DTSEG1 - 1)
# define FDCAN2_DTSEG2 (CONFIG_STM32_FDCAN2_DTSEG2 - 1)
# define FDCAN2_DBRP (((STM32_FDCANCLK_FREQUENCY / \
((FDCAN2_DTSEG1 + FDCAN2_DTSEG2 + 3) * \
CONFIG_STM32_FDCAN2_DBITRATE)) - 1))
# define FDCAN2_DSJW (CONFIG_STM32_FDCAN2_DSJW - 1)
# else
# define FDCAN2_DTSEG1 1
# define FDCAN2_DTSEG2 1
# define FDCAN2_DBRP 1
# define FDCAN2_DSJW 1
# endif /* CONFIG_STM32_FDCAN2_FD_BRS */
# if FDCAN2_DTSEG1 > FDCAN_DBTP_DTSEG1_MAX
# error Invalid FDCAN2 DTSEG1
# endif
# if FDCAN2_DTSEG2 > FDCAN_DBTP_DTSEG2_MAX
# error Invalid FDCAN2 DTSEG2
# endif
# if FDCAN2_DBRP > FDCAN_DBTP_DBRP_MAX
# error Invalid FDCAN2 DBRP
# endif
# if FDCAN2_DSJW > FDCAN_DBTP_DSJW_MAX
# error Invalid FDCAN2 DSJW
# endif
/* FDCAN2 Message RAM Configuration *****************************************/
/* FDCAN2 Message RAM Layout */
# define FDCAN2_STDFILTER_INDEX 0
# define FDCAN2_EXTFILTERS_INDEX (FDCAN2_STDFILTER_INDEX + FDCAN2_STDFILTER_WORDS)
# define FDCAN2_RXFIFO0_INDEX (FDCAN2_EXTFILTERS_INDEX + FDCAN2_EXTFILTER_WORDS)
# define FDCAN2_RXFIFO1_INDEX (FDCAN2_RXFIFO0_INDEX + FDCAN2_RXFIFO0_WORDS)
# define FDCAN2_TXEVENTFIFO_INDEX (FDCAN2_RXFIFO1_INDEX + FDCAN2_RXFIFO1_WORDS)
# define FDCAN2_TXFIFOQ_INDEX (FDCAN2_TXEVENTFIFO_INDEX + FDCAN2_TXEVENTFIFO_WORDS)
# define FDCAN2_MSGRAM_WORDS (FDCAN2_TXFIFOQ_INDEX + FDCAN2_TXFIFIOQ_WORDS)
#endif /* CONFIG_STM32_FDCAN2 */
/* FDCAN3 Configuration *****************************************************/
#ifdef CONFIG_STM32_FDCAN3
/* Bit timing */
# define FDCAN3_NTSEG1 (CONFIG_STM32_FDCAN3_NTSEG1 - 1)
# define FDCAN3_NTSEG2 (CONFIG_STM32_FDCAN3_NTSEG2 - 1)
# define FDCAN3_NBRP (((STM32_FDCANCLK_FREQUENCY / \
((FDCAN3_NTSEG1 + FDCAN3_NTSEG2 + 3) * \
CONFIG_STM32_FDCAN3_BITRATE)) - 1))
# define FDCAN3_NSJW (CONFIG_STM32_FDCAN3_NSJW - 1)
# if FDCAN3_NTSEG1 > FDCAN_NBTP_NTSEG1_MAX
# error Invalid FDCAN3 NTSEG1
# endif
# if FDCAN3_NTSEG2 > FDCAN_NBTP_NTSEG2_MAX
# error Invalid FDCAN3 NTSEG2
# endif
# if FDCAN3_NSJW > FDCAN_NBTP_NSJW_MAX
# error Invalid FDCAN3 NSJW
# endif
# if FDCAN3_NBRP > FDCAN_NBTP_NBRP_MAX
# error Invalid FDCAN1 NBRP
# endif
# ifdef CONFIG_STM32_FDCAN3_FD_BRS
# define FDCAN3_DTSEG1 (CONFIG_STM32_FDCAN3_DTSEG1 - 1)
# define FDCAN3_DTSEG2 (CONFIG_STM32_FDCAN3_DTSEG2 - 1)
# define FDCAN3_DBRP (((STM32_FDCANCLK_FREQUENCY / \
((FDCAN3_DTSEG1 + FDCAN3_DTSEG2 + 3) * \
CONFIG_STM32_FDCAN3_DBITRATE)) - 1))
# define FDCAN3_DSJW (CONFIG_STM32_FDCAN3_DSJW - 1)
# else
# define FDCAN3_DTSEG1 1
# define FDCAN3_DTSEG2 1
# define FDCAN3_DBRP 1
# define FDCAN3_DSJW 1
# endif /* CONFIG_STM32_FDCAN3_FD_BRS */
# if FDCAN3_DTSEG1 > FDCAN_DBTP_DTSEG1_MAX
# error Invalid FDCAN3 DTSEG1
# endif
# if FDCAN3_DTSEG2 > FDCAN_DBTP_DTSEG2_MAX
# error Invalid FDCAN3 DTSEG2
# endif
# if FDCAN3_DBRP > FDCAN_DBTP_DBRP_MAX
# error Invalid FDCAN3 DBRP
# endif
# if FDCAN3_DSJW > FDCAN_DBTP_DSJW_MAX
# error Invalid FDCAN3 DSJW
# endif
/* FDCAN3 Message RAM Configuration *****************************************/
/* FDCAN3 Message RAM Layout */
# define FDCAN3_STDFILTER_INDEX 0
# define FDCAN3_EXTFILTERS_INDEX (FDCAN3_STDFILTER_INDEX + FDCAN3_STDFILTER_WORDS)
# define FDCAN3_RXFIFO0_INDEX (FDCAN3_EXTFILTERS_INDEX + FDCAN3_EXTFILTER_WORDS)
# define FDCAN3_RXFIFO1_INDEX (FDCAN3_RXFIFO0_INDEX + FDCAN3_RXFIFO0_WORDS)
# define FDCAN3_TXEVENTFIFO_INDEX (FDCAN3_RXFIFO1_INDEX + FDCAN3_RXFIFO1_WORDS)
# define FDCAN3_TXFIFOQ_INDEX (FDCAN3_TXEVENTFIFO_INDEX + FDCAN3_TXEVENTFIFO_WORDS)
# define FDCAN3_MSGRAM_WORDS (FDCAN3_TXFIFOQ_INDEX + FDCAN3_TXFIFIOQ_WORDS)
#endif /* CONFIG_STM32_FDCAN3 */
/* Loopback mode */
#undef STM32_FDCAN_LOOPBACK
#if defined(CONFIG_STM32_FDCAN1_LOOPBACK) || \
defined(CONFIG_STM32_FDCAN2_LOOPBACK) || \
defined(CONFIG_STM32_FDCAN3_LOOPBACK)
# define STM32_FDCAN_LOOPBACK 1
#endif
/* Interrupts ***************************************************************/
/* Common interrupts
*
* FDCAN_INT_TSW - Timestamp Wraparound
* FDCAN_INT_MRAF - Message RAM Access Failure
* FDCAN_INT_TOO - Timeout Occurred
* FDCAN_INT_ELO - Error Logging Overflow
* FDCAN_INT_EP - Error Passive
* FDCAN_INT_EW - Warning Status
* FDCAN_INT_BO - Bus_Off Status
* FDCAN_INT_WDI - Watchdog Interrupt
* FDCAN_INT_PEA - Protocol Error in Arbritration Phase
* FDCAN_INT_PED - Protocol Error in Data Phase
*/
#define FDCAN_CMNERR_INTS (FDCAN_INT_MRAF | FDCAN_INT_TOO | FDCAN_INT_EP | \
FDCAN_INT_BO | FDCAN_INT_WDI | FDCAN_INT_PEA | \
FDCAN_INT_PED)
/* RXFIFO mode interrupts
*
* FDCAN_INT_RF0N - Receive FIFO 0 New Message
* FDCAN_INT_RF0F - Receive FIFO 0 Full
* FDCAN_INT_RF0L - Receive FIFO 0 Message Lost
* FDCAN_INT_RF1N - Receive FIFO 1 New Message
* FDCAN_INT_RF1F - Receive FIFO 1 Full
* FDCAN_INT_RF1L - Receive FIFO 1 Message Lost
* FDCAN_INT_HPM - High Priority Message Received
*
*/
#define FDCAN_RXFIFO0_INTS (FDCAN_INT_RF0N | FDCAN_INT_RF0L)
#define FDCAN_RXFIFO1_INTS (FDCAN_INT_RF1N | FDCAN_INT_RF1L)
#define FDCAN_RXERR_INTS (FDCAN_INT_RF0L | FDCAN_INT_RF1L)
/* TX FIFOQ mode interrupts
*
* FDCAN_INT_TFE - Tx FIFO Empty
*
* TX Event FIFO interrupts
*
* FDCAN_INT_TEFN - Tx Event FIFO New Entry
* FDCAN_INT_TEFF - Tx Event FIFO Full
* FDCAN_INT_TEFL - Tx Event FIFO Element Lost
*
* Mode-independent TX-related interrupts
*
* FDCAN_INT_TC - Transmission Completed
* FDCAN_INT_TCF - Transmission Cancellation Finished
*/
#define FDCAN_TXCOMMON_INTS (FDCAN_INT_TC | FDCAN_INT_TCF)
#define FDCAN_TXFIFOQ_INTS (FDCAN_INT_TFE | FDCAN_TXCOMMON_INTS)
#define FDCAN_TXEVFIFO_INTS (FDCAN_INT_TEFN | FDCAN_INT_TEFF | \
FDCAN_INT_TEFL)
#define FDCAN_TXERR_INTS (FDCAN_INT_TEFL | FDCAN_INT_PEA | FDCAN_INT_PED)
/* Common-, TX- and RX-Error-Mask */
#define FDCAN_ANYERR_INTS (FDCAN_CMNERR_INTS | FDCAN_RXERR_INTS | FDCAN_TXERR_INTS)
/* Convenience macro for clearing all interrupts */
#define FDCAN_INT_ALL 0x3fcfffff
/* Debug ********************************************************************/
/* Debug configurations that may be enabled just for testing FDCAN */
#ifndef CONFIG_DEBUG_NET_INFO
# undef CONFIG_STM32_FDCAN_REGDEBUG
#endif
/****************************************************************************
* Private Types
****************************************************************************/
/* CAN frame format */
enum stm32_frameformat_e
{
FDCAN_ISO11898_1_FORMAT = 0, /* Frame format according to ISO11898-1 */
FDCAN_NONISO_BOSCH_V1_FORMAT = 1 /* Frame format according to Bosch CAN FD V1.0 */
};
/* CAN mode of operation */
enum stm32_canmode_e
{
FDCAN_CLASSIC_MODE = 0, /* Classic CAN operation */
#ifdef CONFIG_NET_CAN_CANFD
FDCAN_FD_MODE = 1, /* CAN FD operation */
FDCAN_FD_BRS_MODE = 2 /* CAN FD operation with bit rate switching */
#endif
};
/* CAN driver state */
enum can_state_s
{
FDCAN_STATE_UNINIT = 0, /* Not yet initialized */
FDCAN_STATE_RESET, /* Initialized, reset state */
FDCAN_STATE_SETUP, /* fdcan_setup() has been called */
FDCAN_STATE_DISABLED /* Disabled by a fdcan_shutdown() */
};
/* This structure describes the FDCAN message RAM layout */
struct stm32_msgram_s
{
volatile uint32_t *stdfilters; /* Standard filters */
volatile uint32_t *extfilters; /* Extended filters */
volatile uint32_t *rxfifo0; /* RX FIFO0 */
volatile uint32_t *rxfifo1; /* RX FIFO1 */
volatile uint32_t *txeventfifo; /* TX event FIFO */
volatile uint32_t *txfifoq; /* TX FIFO queue */
};
/* This structure provides the constant configuration of a FDCAN peripheral */
struct stm32_config_s
{
uint32_t rxpinset; /* RX pin configuration */
uint32_t txpinset; /* TX pin configuration */
uintptr_t base; /* Base address of the FDCAN registers */
uint32_t baud; /* Configured baud */
uint32_t nbtp; /* Nominal bit timing/prescaler register setting */
uint32_t dbtp; /* Data bit timing/prescaler register setting */
uint8_t port; /* FDCAN port number (1 or 2) */
uint8_t irq0; /* FDCAN peripheral IRQ number for interrupt line 0 */
uint8_t irq1; /* FDCAN peripheral IRQ number for interrupt line 1 */
uint8_t mode; /* See enum stm32_canmode_e */
uint8_t format; /* See enum stm32_frameformat_e */
uint8_t nstdfilters; /* Number of standard filters */
uint8_t nextfilters; /* Number of extended filters */
uint8_t nrxfifo0; /* Number of RX FIFO0 elements */
uint8_t nrxfifo1; /* Number of RX FIFO1 elements */
uint8_t ntxeventfifo; /* Number of TXevent FIFO elements */
uint8_t ntxfifoq; /* Number of TX FIFO queue elements */
uint8_t rxfifo0esize; /* RX FIFO0 element size (words) */
uint8_t rxfifo1esize; /* RX FIFO1 element size (words) */
uint8_t txeventesize; /* TXevent element size (words) */
uint8_t txbufferesize; /* TX buffer element size (words) */
#ifdef STM32_FDCAN_LOOPBACK
bool loopback; /* True: Loopback mode */
#endif
/* FDCAN message RAM layout */
struct stm32_msgram_s msgram;
};
/* This structure provides the current state of a FDCAN peripheral */
struct stm32_fdcan_s
{
/* The constant configuration */
const struct stm32_config_s *config;
uint8_t state; /* See enum can_state_s */
#ifdef CONFIG_NET_CAN_EXTID
uint8_t nextalloc; /* Number of allocated extended filters */
#endif
uint8_t nstdalloc; /* Number of allocated standard filters */
uint32_t nbtp; /* Current nominal bit timing */
uint32_t dbtp; /* Current data bit timing */
#ifdef CONFIG_NET_CAN_EXTID
uint32_t extfilters[2]; /* Extended filter bit allocator. 2*32=64 */
#endif
uint32_t stdfilters[4]; /* Standard filter bit allocator. 4*32=128 */
#ifdef CONFIG_STM32_FDCAN_REGDEBUG
uintptr_t regaddr; /* Last register address read */
uint32_t regval; /* Last value read from the register */
unsigned int count; /* Number of times that the value was read */
#endif
bool bifup; /* true:ifup false:ifdown */
struct net_driver_s dev; /* Interface understood by the network */
struct work_s irqwork; /* For deferring interrupt work to the wq */
struct work_s pollwork; /* For deferring poll work to the work wq */
/* A pointers to the list of TX/RX descriptors */
struct can_frame *txdesc;
struct can_frame *rxdesc;
/* TX/RX pool */
#ifdef CONFIG_NET_CAN_CANFD
uint8_t tx_pool[sizeof(struct canfd_frame)*POOL_SIZE];
uint8_t rx_pool[sizeof(struct canfd_frame)*POOL_SIZE];
#else
uint8_t tx_pool[sizeof(struct can_frame)*POOL_SIZE];
uint8_t rx_pool[sizeof(struct can_frame)*POOL_SIZE];
#endif
};
/****************************************************************************
* Private Function Prototypes
****************************************************************************/
/* FDCAN Register access */
static uint32_t fdcan_getreg(struct stm32_fdcan_s *priv, int offset);
static void fdcan_putreg(struct stm32_fdcan_s *priv, int offset,
uint32_t regval);
#ifdef CONFIG_STM32_FDCAN_REGDEBUG
static void fdcan_dumpregs(struct stm32_fdcan_s *priv,
const char *msg);
static void fdcan_dumprxregs(struct stm32_fdcan_s *priv,
const char *msg);
static void fdcan_dumptxregs(struct stm32_fdcan_s *priv,
const char *msg);
static void fdcan_dumpramlayout(struct stm32_fdcan_s *priv);
#else
# define fdcan_dumpregs(priv,msg)
# define fdcan_dumprxregs(priv,msg)
# define fdcan_dumptxregs(priv,msg)
# define fdcan_dumpramlayout(priv)
#endif
/* CAN interrupt enable functions */
static void fdcan_rx0int(struct stm32_fdcan_s *priv, bool enable);
static void fdcan_rx1int(struct stm32_fdcan_s *priv, bool enable);
static void fdcan_txint(struct stm32_fdcan_s *priv, bool enable);
#ifdef CONFIG_NET_CAN_ERRORS
static void fdcan_errint(struct stm32_fdcan_s *priv, bool enable);
#endif
/* Common TX logic */
static int fdcan_send(struct stm32_fdcan_s *priv);
static bool fdcan_txready(struct stm32_fdcan_s *priv);
static int fdcan_txpoll(struct net_driver_s *dev);
/* CAN RX interrupt handling */
static void fdcan_rx0interrupt_work(void *arg);
static void fdcan_rx1interrupt_work(void *arg);
/* CAN TX interrupt handling */
static void fdcan_txdone_work(void *arg);
static void fdcan_txdone(struct stm32_fdcan_s *priv);
#ifdef CONFIG_NET_CAN_ERRORS
/* CAN errors interrupt handling */
static void fdcan_error_work(void *arg);
#endif
/* FDCAN interrupt handling */
#ifdef CONFIG_NET_CAN_ERRORS
static void fdcan_error(struct stm32_fdcan_s *priv, uint32_t status);
#endif
static void fdcan_receive(struct stm32_fdcan_s *priv,
volatile uint32_t *rxbuffer,
unsigned long nwords);
static int fdcan_interrupt(int irq, void *context, void *arg);
/* Initialization */
static void fdcan_reset(struct stm32_fdcan_s *priv);
static int fdcan_setup(struct stm32_fdcan_s *priv);
static void fdcan_shutdown(struct stm32_fdcan_s *priv);
/* FDCAN helpers */
#if 0 /* not used for now */
static int
fdcan_start_busoff_recovery_sequence(struct stm32_fdcan_s *priv);
#endif
/* Hardware initialization */
static int fdcan_hw_initialize(struct stm32_fdcan_s *priv);
/* NuttX callback functions */
static int fdcan_ifup(struct net_driver_s *dev);
static int fdcan_ifdown(struct net_driver_s *dev);
static void fdcan_txavail_work(void *arg);
static int fdcan_txavail(struct net_driver_s *dev);
#ifdef CONFIG_NETDEV_IOCTL
static int fdcan_netdev_ioctl(struct net_driver_s *dev, int cmd,
unsigned long arg);
#endif
/****************************************************************************
* Private Data
****************************************************************************/
#ifdef CONFIG_STM32_FDCAN1
/* Message RAM allocation */
/* Constant configuration */
static const struct stm32_config_s g_fdcan1const =
{
.rxpinset = GPIO_FDCAN1_RX,
.txpinset = GPIO_FDCAN1_TX,
.base = STM32_FDCAN1_BASE,
.baud = CONFIG_STM32_FDCAN1_BITRATE,
.nbtp = FDCAN_NBTP_NBRP(FDCAN1_NBRP) |
FDCAN_NBTP_NTSEG1(FDCAN1_NTSEG1) |
FDCAN_NBTP_NTSEG2(FDCAN1_NTSEG2) |
FDCAN_NBTP_NSJW(FDCAN1_NSJW),
.dbtp = FDCAN_DBTP_DBRP(FDCAN1_DBRP) |
FDCAN_DBTP_DTSEG1(FDCAN1_DTSEG1) |
FDCAN_DBTP_DTSEG2(FDCAN1_DTSEG2) |
FDCAN_DBTP_DSJW(FDCAN1_DSJW),
.port = 1,
.irq0 = STM32_IRQ_FDCAN1_0,
.irq1 = STM32_IRQ_FDCAN1_1,
#if defined(CONFIG_STM32_FDCAN1_CLASSIC)
.mode = FDCAN_CLASSIC_MODE,
#elif defined(CONFIG_STM32_FDCAN1_FD)
.mode = FDCAN_FD_MODE,
#else
.mode = FDCAN_FD_BRS_MODE,
#endif
#if defined(CONFIG_STM32_FDCAN1_NONISO_FORMAT)
.format = FDCAN_NONISO_BOSCH_V1_FORMAT,
#else
.format = FDCAN_ISO11898_1_FORMAT,
#endif
.nstdfilters = FDCAN1_STDFILTER_SIZE,
.nextfilters = FDCAN1_EXTFILTER_SIZE,
.nrxfifo0 = FDCAN1_RXFIFO0_SIZE,
.nrxfifo1 = FDCAN1_RXFIFO1_SIZE,
.ntxeventfifo = FDCAN1_TXEVENTFIFO_SIZE,
.ntxfifoq = FDCAN1_TXFIFIOQ_SIZE,
.rxfifo0esize = (FDCAN1_RXFIFO0_WORDS / FDCAN1_RXFIFO0_SIZE),
.rxfifo1esize = (FDCAN1_RXFIFO1_WORDS / FDCAN1_RXFIFO1_SIZE),
.txeventesize = (FDCAN1_TXEVENTFIFO_WORDS / FDCAN1_TXEVENTFIFO_SIZE),
.txbufferesize = (FDCAN1_TXFIFIOQ_WORDS / FDCAN1_TXFIFIOQ_SIZE),
#ifdef CONFIG_STM32_FDCAN1_LOOPBACK
.loopback = true,
#endif
/* FDCAN1 Message RAM */
.msgram =
{
(uint32_t *)(STM32_CANRAM1_BASE + (FDCAN1_STDFILTER_INDEX << 2)),
(uint32_t *)(STM32_CANRAM1_BASE + (FDCAN1_EXTFILTERS_INDEX << 2)),
(uint32_t *)(STM32_CANRAM1_BASE + (FDCAN1_RXFIFO0_INDEX << 2)),
(uint32_t *)(STM32_CANRAM1_BASE + (FDCAN1_RXFIFO1_INDEX << 2)),
(uint32_t *)(STM32_CANRAM1_BASE + (FDCAN1_TXEVENTFIFO_INDEX << 2)),
(uint32_t *)(STM32_CANRAM1_BASE + (FDCAN1_TXFIFOQ_INDEX << 2))
}
};
/* FDCAN1 variable driver state */
static struct stm32_fdcan_s g_fdcan1priv;
#endif /* CONFIG_STM32_FDCAN1 */
#ifdef CONFIG_STM32_FDCAN2
/* FDCAN2 message RAM allocation */
/* FDCAN2 constant configuration */
static const struct stm32_config_s g_fdcan2const =
{
.rxpinset = GPIO_FDCAN2_RX,
.txpinset = GPIO_FDCAN2_TX,
.base = STM32_FDCAN2_BASE,
.baud = CONFIG_STM32_FDCAN2_BITRATE,
.nbtp = FDCAN_NBTP_NBRP(FDCAN2_NBRP) |
FDCAN_NBTP_NTSEG1(FDCAN2_NTSEG1) |
FDCAN_NBTP_NTSEG2(FDCAN2_NTSEG2) |
FDCAN_NBTP_NSJW(FDCAN2_NSJW),
.dbtp = FDCAN_DBTP_DBRP(FDCAN2_DBRP) |
FDCAN_DBTP_DTSEG1(FDCAN2_DTSEG1) |
FDCAN_DBTP_DTSEG2(FDCAN2_DTSEG2) |
FDCAN_DBTP_DSJW(FDCAN2_DSJW),
.port = 2,
.irq0 = STM32_IRQ_FDCAN2_0,
.irq1 = STM32_IRQ_FDCAN2_1,
#if defined(CONFIG_STM32_FDCAN2_CLASSIC)
.mode = FDCAN_CLASSIC_MODE,
#elif defined(CONFIG_STM32_FDCAN2_FD)
.mode = FDCAN_FD_MODE,
#else
.mode = FDCAN_FD_BRS_MODE,
#endif
#if defined(CONFIG_STM32_FDCAN2_NONISO_FORMAT)
.format = FDCAN_NONISO_BOSCH_V1_FORMAT,
#else
.format = FDCAN_ISO11898_1_FORMAT,
#endif
.nstdfilters = FDCAN2_STDFILTER_SIZE,
.nextfilters = FDCAN2_EXTFILTER_SIZE,
.nrxfifo0 = FDCAN2_RXFIFO0_SIZE,
.nrxfifo1 = FDCAN2_RXFIFO1_SIZE,
.ntxeventfifo = FDCAN2_TXEVENTFIFO_SIZE,
.ntxfifoq = FDCAN2_TXFIFIOQ_SIZE,
.rxfifo0esize = (FDCAN2_RXFIFO0_WORDS / FDCAN2_RXFIFO0_SIZE),
.rxfifo1esize = (FDCAN2_RXFIFO1_WORDS / FDCAN2_RXFIFO1_SIZE),
.txeventesize = (FDCAN2_TXEVENTFIFO_WORDS / FDCAN2_TXEVENTFIFO_SIZE),
.txbufferesize = (FDCAN2_TXFIFIOQ_WORDS / FDCAN2_TXFIFIOQ_SIZE),
#ifdef CONFIG_STM32_FDCAN2_LOOPBACK
.loopback = true,
#endif
/* FDCAN2 Message RAM */
.msgram =
{
(uint32_t *)(STM32_CANRAM2_BASE + (FDCAN2_STDFILTER_INDEX << 2)),
(uint32_t *)(STM32_CANRAM2_BASE + (FDCAN2_EXTFILTERS_INDEX << 2)),
(uint32_t *)(STM32_CANRAM2_BASE + (FDCAN2_RXFIFO0_INDEX << 2)),
(uint32_t *)(STM32_CANRAM2_BASE + (FDCAN2_RXFIFO1_INDEX << 2)),
(uint32_t *)(STM32_CANRAM2_BASE + (FDCAN2_TXEVENTFIFO_INDEX << 2)),
(uint32_t *)(STM32_CANRAM2_BASE + (FDCAN2_TXFIFOQ_INDEX << 2))
}
};
/* FDCAN2 variable driver state */
static struct stm32_fdcan_s g_fdcan2priv;
#endif /* CONFIG_STM32_FDCAN2 */
#ifdef CONFIG_STM32_FDCAN3
/* FDCAN3 message RAM allocation */
/* FDCAN3 constant configuration */
static const struct stm32_config_s g_fdcan3const =
{
.rxpinset = GPIO_FDCAN3_RX,
.txpinset = GPIO_FDCAN3_TX,
.base = STM32_FDCAN3_BASE,
.baud = CONFIG_STM32_FDCAN3_BITRATE,
.nbtp = FDCAN_NBTP_NBRP(FDCAN3_NBRP) |
FDCAN_NBTP_NTSEG1(FDCAN3_NTSEG1) |
FDCAN_NBTP_NTSEG2(FDCAN3_NTSEG2) |
FDCAN_NBTP_NSJW(FDCAN3_NSJW),
.dbtp = FDCAN_DBTP_DBRP(FDCAN3_DBRP) |
FDCAN_DBTP_DTSEG1(FDCAN3_DTSEG1) |
FDCAN_DBTP_DTSEG2(FDCAN3_DTSEG2) |
FDCAN_DBTP_DSJW(FDCAN3_DSJW),
.port = 3,
.irq0 = STM32_IRQ_FDCAN3_0,
.irq1 = STM32_IRQ_FDCAN3_1,
#if defined(CONFIG_STM32_FDCAN3_CLASSIC)
.mode = FDCAN_CLASSIC_MODE,
#elif defined(CONFIG_STM32_FDCAN3_FD)
.mode = FDCAN_FD_MODE,
#else
.mode = FDCAN_FD_BRS_MODE,
#endif
#if defined(CONFIG_STM32_FDCAN3_NONISO_FORMAT)
.format = FDCAN_NONISO_BOSCH_V1_FORMAT,
#else
.format = FDCAN_ISO11898_1_FORMAT,
#endif
.nstdfilters = FDCAN3_STDFILTER_SIZE,
.nextfilters = FDCAN3_EXTFILTER_SIZE,
.nrxfifo0 = FDCAN3_RXFIFO0_SIZE,
.nrxfifo1 = FDCAN3_RXFIFO1_SIZE,
.ntxeventfifo = FDCAN3_TXEVENTFIFO_SIZE,
.ntxfifoq = FDCAN3_TXFIFIOQ_SIZE,
.rxfifo0esize = (FDCAN3_RXFIFO0_WORDS / FDCAN3_RXFIFO0_SIZE),
.rxfifo1esize = (FDCAN3_RXFIFO1_WORDS / FDCAN3_RXFIFO1_SIZE),
.txeventesize = (FDCAN3_TXEVENTFIFO_WORDS / FDCAN3_TXEVENTFIFO_SIZE),
.txbufferesize = (FDCAN3_TXFIFIOQ_WORDS / FDCAN3_TXFIFIOQ_SIZE),
#ifdef CONFIG_STM32_FDCAN3_LOOPBACK
.loopback = true,
#endif
/* FDCAN3 Message RAM */
.msgram =
{
(uint32_t *)(STM32_CANRAM3_BASE + (FDCAN3_STDFILTER_INDEX << 2)),
(uint32_t *)(STM32_CANRAM3_BASE + (FDCAN3_EXTFILTERS_INDEX << 2)),
(uint32_t *)(STM32_CANRAM3_BASE + (FDCAN3_RXFIFO0_INDEX << 2)),
(uint32_t *)(STM32_CANRAM3_BASE + (FDCAN3_RXFIFO1_INDEX << 2)),
(uint32_t *)(STM32_CANRAM3_BASE + (FDCAN3_TXEVENTFIFO_INDEX << 2)),
(uint32_t *)(STM32_CANRAM3_BASE + (FDCAN3_TXFIFOQ_INDEX << 2))
}
};
/* FDCAN3 variable driver state */
static struct stm32_fdcan_s g_fdcan3priv;
#endif /* CONFIG_STM32_FDCAN3 */
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: fdcan_getreg
*
* Description:
* Read the value of a FDCAN register.
*
* Input Parameters:
* priv - A reference to the FDCAN peripheral state
* offset - The offset to the register to read
*
* Returned Value:
*
****************************************************************************/
#ifdef CONFIG_STM32_FDCAN_REGDEBUG
static uint32_t fdcan_getreg(struct stm32_fdcan_s *priv, int offset)
{
const struct stm32_config_s *config = priv->config;
uintptr_t regaddr = 0;
uint32_t regval = 0;
/* Read the value from the register */
regaddr = config->base + offset;
regval = getreg32(regaddr);
/* Is this the same value that we read from the same register last time?
* Are we polling the register? If so, suppress some of the output.
*/
if (regaddr == priv->regaddr && regval == priv->regval)
{
if (priv->count == 0xffffffff || ++priv->count > 3)
{
if (priv->count == 4)
{
ninfo("...\n");
}
return regval;
}
}
/* No this is a new address or value */
else
{
/* Did we print "..." for the previous value? */
if (priv->count > 3)
{
/* Yes.. then show how many times the value repeated */
ninfo("[repeats %d more times]\n", priv->count - 3);
}
/* Save the new address, value, and count */
priv->regaddr = regaddr;
priv->regval = regval;
priv->count = 1;
}
/* Show the register value read */
ninfo("%08" PRIx32 "->%08" PRIx32 "\n", regaddr, regval);
return regval;
}
#else
static uint32_t fdcan_getreg(struct stm32_fdcan_s *priv, int offset)
{
const struct stm32_config_s *config = priv->config;
return getreg32(config->base + offset);
}
#endif
/****************************************************************************
* Name: fdcan_putreg
*
* Description:
* Set the value of a FDCAN register.
*
* Input Parameters:
* priv - A reference to the FDCAN peripheral state
* offset - The offset to the register to write
* regval - The value to write to the register
*
* Returned Value:
* None
*
****************************************************************************/
#ifdef CONFIG_STM32_FDCAN_REGDEBUG
static void fdcan_putreg(struct stm32_fdcan_s *priv, int offset,
uint32_t regval)
{
const struct stm32_config_s *config = priv->config;
uintptr_t regaddr = config->base + offset;
/* Show the register value being written */
ninfo("%08" PRIx32 "->%08" PRIx32 "\n", regaddr, regval);
/* Write the value */
putreg32(regval, regaddr);
}
#else
static void fdcan_putreg(struct stm32_fdcan_s *priv, int offset,
uint32_t regval)
{
const struct stm32_config_s *config = priv->config;
putreg32(regval, config->base + offset);
}
#endif
/****************************************************************************
* Name: fdcan_dumpctrlregs
*
* Description:
* Dump the contents of all CAN control registers
*
* Input Parameters:
* priv - A reference to the CAN block status
*
* Returned Value:
* None
*
****************************************************************************/
#ifdef CONFIG_STM32_FDCAN_REGDEBUG
static void fdcan_dumpregs(struct stm32_fdcan_s *priv,
const char *msg)
{
const struct stm32_config_s *config = priv->config;
ninfo("CAN%d Control and Status Registers: %s\n", config->port, msg);
ninfo(" Base: %08" PRIx32 "\n", config->base);
/* CAN control and status registers */
ninfo(" CCCR: %08" PRIx32 " TEST: %08" PRIx32 "\n",
getreg32(config->base + STM32_FDCAN_CCCR_OFFSET),
getreg32(config->base + STM32_FDCAN_TEST_OFFSET));
ninfo(" NBTP: %08" PRIx32 " DBTP: %08" PRIx32 "\n",
getreg32(config->base + STM32_FDCAN_NBTP_OFFSET),
getreg32(config->base + STM32_FDCAN_DBTP_OFFSET));
ninfo(" IE: %08" PRIx32 " TIE: %08" PRIx32 "\n",
getreg32(config->base + STM32_FDCAN_IE_OFFSET),
getreg32(config->base + STM32_FDCAN_TXBTIE_OFFSET));
ninfo(" ILE: %08" PRIx32 " ILS: %08" PRIx32 "\n",
getreg32(config->base + STM32_FDCAN_ILE_OFFSET),
getreg32(config->base + STM32_FDCAN_ILS_OFFSET));
ninfo(" TXBC: %08" PRIx32 "\n",
getreg32(config->base + STM32_FDCAN_TXBC_OFFSET));
}
#endif
/****************************************************************************
* Name: fdcan_dumprxregs
*
* Description:
* Dump the contents of all Rx status registers
*
* Input Parameters:
* priv - A reference to the CAN block status
*
* Returned Value:
* None
*
****************************************************************************/
#ifdef CONFIG_STM32_FDCAN_REGDEBUG
static void fdcan_dumprxregs(struct stm32_fdcan_s *priv,
const char *msg)
{
const struct stm32_config_s *config = priv->config;
ninfo("CAN%d Rx Registers: %s\n", config->port, msg);
ninfo(" Base: %08" PRIx32 "\n", config->base);
ninfo(" PSR: %08" PRIx32 " ECR: %08" PRIx32
" HPMS: %08" PRIx32 "\n",
getreg32(config->base + STM32_FDCAN_PSR_OFFSET),
getreg32(config->base + STM32_FDCAN_ECR_OFFSET),
getreg32(config->base + STM32_FDCAN_HPMS_OFFSET));
ninfo(" RXF0S: %08" PRIx32 " RXF0A: %08" PRIx32 "\n",
getreg32(config->base + STM32_FDCAN_RXF0S_OFFSET),
getreg32(config->base + STM32_FDCAN_RXF0A_OFFSET));
ninfo(" RXF1S: %08" PRIx32 " RXF1A: %08" PRIx32 "\n",
getreg32(config->base + STM32_FDCAN_RXF1S_OFFSET),
getreg32(config->base + STM32_FDCAN_RXF1A_OFFSET));
ninfo(" IR: %08" PRIx32 " IE: %08" PRIx32 "\n",
getreg32(config->base + STM32_FDCAN_IR_OFFSET),
getreg32(config->base + STM32_FDCAN_IE_OFFSET));
}
#endif
/****************************************************************************
* Name: fdcan_dumptxregs
*
* Description:
* Dump the contents of all Tx buffer registers
*
* Input Parameters:
* priv - A reference to the CAN block status
*
* Returned Value:
* None
*
****************************************************************************/
#ifdef CONFIG_STM32_FDCAN_REGDEBUG
static void fdcan_dumptxregs(struct stm32_fdcan_s *priv,
const char *msg)
{
const struct stm32_config_s *config = priv->config;
ninfo("CAN%d Tx Registers: %s\n", config->port, msg);
ninfo(" Base: %08" PRIx32 "\n", config->base);
ninfo(" PSR: %08" PRIx32 " ECR: %08" PRIx32 "\n",
getreg32(config->base + STM32_FDCAN_PSR_OFFSET),
getreg32(config->base + STM32_FDCAN_ECR_OFFSET));
ninfo(" TXQFS: %08" PRIx32 " TXBAR: %08" PRIx32
" TXBRP: %08" PRIx32 "\n",
getreg32(config->base + STM32_FDCAN_TXFQS_OFFSET),
getreg32(config->base + STM32_FDCAN_TXBAR_OFFSET),
getreg32(config->base + STM32_FDCAN_TXBRP_OFFSET));
ninfo(" TXBTO: %08" PRIx32 " TXBCR: %08" PRIx32 "\n",
getreg32(config->base + STM32_FDCAN_TXBTO_OFFSET),
getreg32(config->base + STM32_FDCAN_TXBCR_OFFSET));
ninfo(" TXEFS: %08" PRIx32 " TXEFA: %08" PRIx32 "\n",
getreg32(config->base + STM32_FDCAN_TXEFS_OFFSET),
getreg32(config->base + STM32_FDCAN_TXEFA_OFFSET));
ninfo(" IR: %08" PRIx32 " IE: %08" PRIx32
" TIE: %08" PRIx32 "\n",
getreg32(config->base + STM32_FDCAN_IR_OFFSET),
getreg32(config->base + STM32_FDCAN_IE_OFFSET),
getreg32(config->base + STM32_FDCAN_TXBTIE_OFFSET));
}
#endif
/****************************************************************************
* Name: fdcan_dumpramlayout
*
* Description:
* Print the layout of the message RAM
*
* Input Parameters:
* priv - A reference to the CAN block status
*
* Returned Value:
* None
*
****************************************************************************/
#ifdef CONFIG_STM32_FDCAN_REGDEBUG
static void fdcan_dumpramlayout(struct stm32_fdcan_s *priv)
{
const struct stm32_config_s *config = priv->config;
ninfo(" ******* FDCAN%d Message RAM layout *******\n", config->port);
ninfo(" Start # Elmnt Elmnt size (words)\n");
if (config->nstdfilters > 0)
{
ninfo("STD filters %p %4d %2d\n",
config->msgram.stdfilters,
config->nstdfilters,
1);
}
if (config->nextfilters > 0)
{
ninfo("EXT filters %p %4d %2d\n",
config->msgram.extfilters,
config->nextfilters,
2);
}
if (config->nrxfifo0 > 0)
{
ninfo("RX FIFO 0 %p %4d %2d\n",
config->msgram.rxfifo0,
config->nrxfifo0,
config->rxfifo0esize);
}
if (config->nrxfifo1 > 0)
{
ninfo("RX FIFO 1 %p %4d %2d\n",
config->msgram.rxfifo1,
config->nrxfifo1,
config->rxfifo1esize);
}
if (config->ntxeventfifo > 0)
{
ninfo("TX EVENT %p %4d %2d\n",
config->msgram.txeventfifo,
config->ntxeventfifo,
config->txeventesize);
}
if (config->ntxfifoq > 0)
{
ninfo("TX FIFO %p %4d %2d\n",
config->msgram.txfifoq,
config->ntxfifoq,
config->txbufferesize);
}
}
#endif
/****************************************************************************
* Name: fdcan_start_busoff_recovery_sequence
*
* Description:
* This function initiates the BUS-OFF recovery sequence.
* CAN Specification Rev. 2.0 or ISO11898-1:2015.
* According the STM32G4 datasheet section 44.3.2 Software initialziation.
*
* Input Parameters:
* priv - An instance of the FDCAN driver state structure.
*
* Returned Value:
* Zero (OK) is returned on success. Otherwise a negated errno value is
* returned to indicate the nature of the error.
*
****************************************************************************/
#if 0 /* not used for now */
static int
fdcan_start_busoff_recovery_sequence(struct stm32_fdcan_s *priv)
{
uint32_t regval = 0;
DEBUGASSERT(priv);
/* Only start BUS-OFF recovery if we are in BUS-OFF state */
regval = fdcan_getreg(priv, STM32_FDCAN_PSR_OFFSET);
if ((regval & FDCAN_PSR_BO) == 0)
{
return -EPERM;
}
/* Disable initialization mode to issue the recovery sequence */
regval = fdcan_getreg(priv, STM32_FDCAN_CCCR_OFFSET);
regval &= ~FDCAN_CCCR_INIT;
fdcan_putreg(priv, STM32_FDCAN_CCCR_OFFSET, regval);
return OK;
}
#endif
/****************************************************************************
* Name: fdcan_reset
*
* Description:
* Reset the FDCAN device. Called early to initialize the hardware. This
* function is called, before fdcan_setup() and on error conditions.
*
* Input Parameters:
* dev - An instance of the "upper half" can driver state structure.
*
* Returned Value:
* None
*
****************************************************************************/
static void fdcan_reset(struct stm32_fdcan_s *priv)
{
const struct stm32_config_s *config = NULL;
uint32_t regval = 0;
irqstate_t flags;
DEBUGASSERT(priv);
config = priv->config;
DEBUGASSERT(config);
ninfo("FDCAN%d\n", config->port);
UNUSED(config);
/* Disable all interrupts */
fdcan_putreg(priv, STM32_FDCAN_IE_OFFSET, 0);
fdcan_putreg(priv, STM32_FDCAN_TXBTIE_OFFSET, 0);
/* Make sure that all buffers are released.
*
* REVISIT: What if a thread is waiting for a buffer? The following
* will not wake up any waiting threads.
*/
/* Disable the FDCAN controller.
* REVISIT: Should fdcan_shutdown() be called here?
*/
/* Reset the FD CAN.
* REVISIT: Since there is only a single reset for both FDCAN
* controllers, do we really want to use the RCC reset here?
* This will nuke operation of the second controller if another
* device is registered.
*/
flags = enter_critical_section();
regval = getreg32(STM32_RCC_APB1RSTR1);
regval |= RCC_APB1RSTR1_FDCANRST;
putreg32(regval, STM32_RCC_APB1RSTR1);
regval &= ~RCC_APB1RSTR1_FDCANRST;
putreg32(regval, STM32_RCC_APB1RSTR1);
leave_critical_section(flags);
priv->state = FDCAN_STATE_RESET;
}
/****************************************************************************
* Name: fdcan_setup
*
* Description:
* Configure the FDCAN. This method is called the first time that the FDCAN
* device is opened. This will occur when the port is first opened.
* This setup includes configuring and attaching FDCAN interrupts.
* All FDCAN interrupts are disabled upon return.
*
* Input Parameters:
* dev - An instance of the "upper half" can driver state structure.
*
* Returned Value:
* Zero on success; a negated errno on failure
*
****************************************************************************/
static int fdcan_setup(struct stm32_fdcan_s *priv)
{
const struct stm32_config_s *config = NULL;
int ret = 0;
DEBUGASSERT(priv);
config = priv->config;
DEBUGASSERT(config);
ninfo("FDCAN%d\n", config->port);
/* FDCAN hardware initialization */
ret = fdcan_hw_initialize(priv);
if (ret < 0)
{
nerr("ERROR: FDCAN%d H/W initialization failed: %d\n",
config->port, ret);
return ret;
}
fdcan_dumpregs(priv, "After hardware initialization");
/* Attach the FDCAN interrupt handlers */
ret = irq_attach(config->irq0, fdcan_interrupt, priv);
if (ret < 0)
{
nerr("ERROR: Failed to attach FDCAN%d line 0 IRQ (%d)",
config->port, config->irq0);
return ret;
}
ret = irq_attach(config->irq1, fdcan_interrupt, priv);
if (ret < 0)
{
nerr("ERROR: Failed to attach FDCAN%d line 1 IRQ (%d)",
config->port, config->irq1);
return ret;
}
priv->state = FDCAN_STATE_SETUP;
/* Enable the interrupts at the NVIC (they are still disabled at the FDCAN
* peripheral).
*/
up_enable_irq(config->irq0);
up_enable_irq(config->irq1);
return OK;
}
/****************************************************************************
* Name: fdcan_shutdown
*
* Description:
* Disable the FDCAN. This method is called when the FDCAN device
* is closed. This method reverses the operation the setup method.
*
* Input Parameters:
* dev - An instance of the "upper half" can driver state structure.
*
* Returned Value:
* None
*
****************************************************************************/
static void fdcan_shutdown(struct stm32_fdcan_s *priv)
{
const struct stm32_config_s *config = NULL;
uint32_t regval = 0;
DEBUGASSERT(priv);
config = priv->config;
DEBUGASSERT(config);
ninfo("FDCAN%d\n", config->port);
/* Disable FDCAN interrupts at the NVIC */
up_disable_irq(config->irq0);
up_disable_irq(config->irq1);
/* Disable all interrupts from the FDCAN peripheral */
fdcan_putreg(priv, STM32_FDCAN_IE_OFFSET, 0);
fdcan_putreg(priv, STM32_FDCAN_TXBTIE_OFFSET, 0);
/* Detach the FDCAN interrupt handler */
irq_detach(config->irq0);
irq_detach(config->irq1);
/* Disable device by setting the Clock Stop Request bit */
regval = fdcan_getreg(priv, STM32_FDCAN_CCCR_OFFSET);
regval |= FDCAN_CCCR_CSR;
fdcan_putreg(priv, STM32_FDCAN_CCCR_OFFSET, regval);
/* Wait for Init and Clock Stop Acknowledge bits to verify
* device is in the powered down state
*/
while ((fdcan_getreg(priv, STM32_FDCAN_CCCR_OFFSET) & FDCAN_CCCR_INIT)
== 0);
while ((fdcan_getreg(priv, STM32_FDCAN_CCCR_OFFSET) & FDCAN_CCCR_CSA)
== 0);
priv->state = FDCAN_STATE_DISABLED;
}
/****************************************************************************
* Name: fdcan_rx0int
*
* Description:
* Call to enable or disable RX0 interrupts.
*
* Input Parameters:
* priv - reference to the private CAN driver state structure
*
* Returned Value:
* None
*
****************************************************************************/
static void fdcan_rx0int(struct stm32_fdcan_s *priv, bool enable)
{
const struct stm32_config_s *config = NULL;
uint32_t regval = 0;
DEBUGASSERT(priv);
config = priv->config;
DEBUGASSERT(config);
ninfo("CAN%" PRIu8 "RX0 enable: %d\n", config->port, enable);
/* Enable/disable the FIFO 0 message pending interrupt */
regval = fdcan_getreg(priv, STM32_FDCAN_IE_OFFSET);
if (enable)
{
regval |= FDCAN_RXFIFO0_INTS;
}
else
{
regval &= ~FDCAN_RXFIFO0_INTS;
}
fdcan_putreg(priv, STM32_FDCAN_IE_OFFSET, regval);
}
/****************************************************************************
* Name: fdcan_rx1int
*
* Description:
* Call to enable or disable RX1 interrupts.
*
* Input Parameters:
* priv - reference to the private CAN driver state structure
*
* Returned Value:
* None
*
****************************************************************************/
static void fdcan_rx1int(struct stm32_fdcan_s *priv, bool enable)
{
const struct stm32_config_s *config = NULL;
uint32_t regval = 0;
DEBUGASSERT(priv);
config = priv->config;
DEBUGASSERT(config);
ninfo("CAN%" PRIu8 "RX1 enable: %d\n", config->port, enable);
/* Enable/disable the FIFO 1 message pending interrupt */
regval = fdcan_getreg(priv, STM32_FDCAN_IE_OFFSET);
if (enable)
{
regval |= FDCAN_RXFIFO1_INTS;
}
else
{
regval &= ~FDCAN_RXFIFO1_INTS;
}
fdcan_putreg(priv, STM32_FDCAN_IE_OFFSET, regval);
}
/****************************************************************************
* Name: fdcan_txint
*
* Description:
* Call to enable or disable TX interrupts.
*
* Input Parameters:
* dev - An instance of the "upper half" can driver state structure.
*
* Returned Value:
* None
*
****************************************************************************/
static void fdcan_txint(struct stm32_fdcan_s *priv, bool enable)
{
const struct stm32_config_s *config = NULL;
uint32_t regval = 0;
DEBUGASSERT(priv);
config = priv->config;
DEBUGASSERT(config);
ninfo("CAN%" PRIu8 "TX enable: %d\n", config->port, enable);
/* Enable/disable the receive interrupts */
regval = fdcan_getreg(priv, STM32_FDCAN_IE_OFFSET);
if (enable)
{
regval |= FDCAN_TXFIFOQ_INTS;
}
else
{
regval &= ~FDCAN_TXFIFOQ_INTS;
}
fdcan_putreg(priv, STM32_FDCAN_IE_OFFSET, regval);
}
#ifdef CONFIG_NET_CAN_ERRORS
/****************************************************************************
* Name: fdcan_txint
*
* Description:
* Call to enable or disable CAN SCE interrupts.
*
* Input Parameters:
* priv - reference to the private CAN driver state structure
*
* Returned Value:
* None
*
****************************************************************************/
static void fdcan_errint(struct stm32_fdcan_s *priv, bool enable)
{
const struct stm32_config_s *config = NULL;
uint32_t regval = 0;
DEBUGASSERT(priv);
config = priv->config;
DEBUGASSERT(config);
ninfo("CAN%" PRIu8 "ERR enable: %d\n", config->port, enable);
/* Enable/disable the transmit mailbox interrupt */
regval = fdcan_getreg(priv, STM32_FDCAN_IE_OFFSET);
if (enable)
{
regval |= FDCAN_ANYERR_INTS;
}
else
{
regval &= ~FDCAN_ANYERR_INTS;
}
fdcan_putreg(priv, STM32_FDCAN_IE_OFFSET, regval);
}
#endif
/****************************************************************************
* Name: fdcan_send
*
* Description:
* Send one can message.
*
* One CAN-message consists of a maximum of 10 bytes. A message is
* composed of at least the first 2 bytes (when there are no data bytes).
*
* Byte 0: Bits 0-7: Bits 3-10 of the 11-bit CAN identifier
* Byte 1: Bits 5-7: Bits 0-2 of the 11-bit CAN identifier
* Bit 4: Remote Transmission Request (RTR)
* Bits 0-3: Data Length Code (DLC)
* Bytes 2-10: CAN data
*
* Input Parameters:
* dev - An instance of the "upper half" can driver state structure.
*
* Returned Value:
* Zero on success; a negated errno on failure
*
****************************************************************************/
static int fdcan_send(struct stm32_fdcan_s *priv)
{
const struct stm32_config_s *config = NULL;
volatile uint32_t *txbuffer = NULL;
const uint8_t *src = NULL;
uint32_t *dest = NULL;
uint32_t regval = 0;
unsigned int ndx = 0;
unsigned int nbytes = 0;
uint32_t wordbuffer = 0;
unsigned int i = 0;
DEBUGASSERT(priv);
config = priv->config;
DEBUGASSERT(config);
fdcan_dumptxregs(priv, "Before send");
/* That that FIFO elements were configured */
DEBUGASSERT(config->ntxfifoq > 0);
/* Get our reserved Tx FIFO/queue put index */
regval = fdcan_getreg(priv, STM32_FDCAN_TXFQS_OFFSET);
DEBUGASSERT((regval & FDCAN_TXFQS_TFQF) == 0);
ndx = (regval & FDCAN_TXFQS_TFQPI_MASK) >> FDCAN_TXFQS_TFQPI_SHIFT;
/* And the TX buffer corresponding to this index */
txbuffer = (config->msgram.txfifoq + ndx * config->txbufferesize);
/* Format the TX FIFOQ entry
*
* Format word T0:
* Transfer message ID (ID) - Value from message structure
* Remote Transmission Request (RTR) - Value from message structure
* Extended Identifier (XTD) - Depends on configuration.
* Error state indicator (ESI) - ESI bit in CAN FD
*
* Format word T1:
* Data Length Code (DLC) - Value from message structure
* Bit Rate Switch (BRS) - Bit rate switching for CAN FD
* FD format (FDF) - Frame transmited in CAN FD format
* Event FIFO Control (EFC) - Do not store events.
* Message Marker (MM) - Always zero
*/
txbuffer[0] = 0;
txbuffer[1] = 0;
/* CAN 2.0 or CAN FD */
if (priv->dev.d_len == sizeof(struct can_frame))
{
struct can_frame *frame = NULL;
frame = (struct can_frame *)priv->dev.d_buf;
ninfo("CAN%" PRIu8 " 2.0 ID: %" PRIu32 " DLC: %" PRIu8 "\n",
config->port, (uint32_t)frame->can_id, frame->can_dlc);
/* Extended or standard ID */
#ifdef CONFIG_NET_CAN_EXTID
if ((frame->can_id & CAN_EFF_FLAG) != 0)
{
DEBUGASSERT(frame->can_id < (1 << 29));
txbuffer[0] |= BUFFER_R0_EXTID(frame->can_id) | BUFFER_R0_XTD;
}
else
#endif
{
DEBUGASSERT(frame->can_id < (1 << 11));
txbuffer[0] |= BUFFER_R0_STDID(frame->can_id);
}
/* Set DLC */
txbuffer[1] |= BUFFER_R1_DLC(frame->can_dlc);
/* Set flags */
if ((frame->can_id & CAN_RTR_FLAG) != 0)
{
txbuffer[0] |= BUFFER_R0_RTR;
}
/* Reset CAN FD bits */
txbuffer[0] &= ~BUFFER_R0_ESI;
txbuffer[1] &= ~BUFFER_R1_FDF;
txbuffer[1] &= ~BUFFER_R1_BRS;
/* Followed by the amount of data corresponding to the DLC (T2..) */
src = frame->data;
nbytes = frame->can_dlc;
}
#ifdef CONFIG_NET_CAN_CANFD
else /* CAN FD frame */
{
struct canfd_frame *frame = (struct canfd_frame *)priv->dev.d_buf;
frame = (struct canfd_frame *)priv->dev.d_buf;
ninfo("CAN%" PRIu8 " FD ID: %" PRIu32 " len: %" PRIu8 "\n",
config->port, (uint32_t)frame->can_id, frame->len);
/* Extended or standard ID */
#ifdef CONFIG_NET_CAN_EXTID
if ((frame->can_id & CAN_EFF_FLAG) != 0)
{
DEBUGASSERT(frame->can_id < (1 << 29));
txbuffer[0] |= BUFFER_R0_EXTID(frame->can_id) | BUFFER_R0_XTD;
}
else
#endif
{
DEBUGASSERT(frame->can_id < (1 << 11));
txbuffer[0] |= BUFFER_R0_STDID(frame->can_id);
}
/* CANFD frame */
txbuffer[1] |= BUFFER_R1_FDF;
/* Set DLC */
txbuffer[1] |= BUFFER_R1_DLC(len_to_can_dlc[frame->len]);
/* Set flags */
if ((frame->can_id & CAN_RTR_FLAG) != 0)
{
txbuffer[0] |= BUFFER_R0_RTR;
}
if ((frame->flags & CANFD_BRS) != 0)
{
txbuffer[1] |= BUFFER_R1_BRS;
}
if ((frame->flags & CANFD_ESI) != 0)
{
txbuffer[0] |= BUFFER_R0_ESI;
}
/* Followed by the amount of data corresponding to the DLC (T2..) */
src = frame->data;
nbytes = frame->len;
}
#endif
dest = (uint32_t *)&txbuffer[2];
/* Writes must be word length */
for (i = 0; i < nbytes; i += 4)
{
/* Little endian is assumed */
wordbuffer = src[0] |
(src[1] << 8) |
(src[2] << 16) |
(src[3] << 24);
src += 4;
*dest++ = wordbuffer;
}
/* Enable transmit interrupts from the TX FIFOQ buffer by setting TC
* interrupt bit in IR (also requires that the TC interrupt is enabled)
*/
fdcan_putreg(priv, STM32_FDCAN_TXBTIE_OFFSET, (1 << ndx));
/* And request to send the packet */
fdcan_putreg(priv, STM32_FDCAN_TXBAR_OFFSET, (1 << ndx));
return OK;
}
/****************************************************************************
* Name: fdcan_txready
*
* Description:
* Return true if the FDCAN hardware can accept another TX message.
*
* Input Parameters:
* dev - An instance of the "upper half" can driver state structure.
*
* Returned Value:
* True if the FDCAN hardware is ready to accept another TX message.
*
****************************************************************************/
static bool fdcan_txready(struct stm32_fdcan_s *priv)
{
uint32_t regval = 0;
bool notfull = false;
/* Return the state of the TX FIFOQ. Return TRUE if the TX FIFO/Queue is
* not full.
*/
regval = fdcan_getreg(priv, STM32_FDCAN_TXFQS_OFFSET);
notfull = ((regval & FDCAN_TXFQS_TFQF) == 0);
return notfull;
}
/****************************************************************************
* Name: fdcan_rx0interrupt_work
*
* Description:
* CAN RX FIFO 0 worker
*
****************************************************************************/
static void fdcan_rx0interrupt_work(void *arg)
{
struct stm32_fdcan_s *priv = (struct stm32_fdcan_s *)arg;
const struct stm32_config_s *config = NULL;
uint32_t regval = 0;
unsigned int nelem = 0;
unsigned int ndx = 0;
DEBUGASSERT(priv);
config = priv->config;
DEBUGASSERT(config);
/* Clear the RX FIFO0 new message interrupt */
fdcan_putreg(priv, STM32_FDCAN_IR_OFFSET, FDCAN_INT_RF0N);
regval = fdcan_getreg(priv, STM32_FDCAN_RXF0S_OFFSET);
nelem = (regval & FDCAN_RXFS_FFL_MASK) >> FDCAN_RXFS_FFL_SHIFT;
if (nelem > 0)
{
/* Handle the newly received message in FIFO0 */
ndx = (regval & FDCAN_RXFS_FGI_MASK) >> FDCAN_RXFS_FGI_SHIFT;
if ((regval & FDCAN_RXFS_RFL) != 0)
{
nerr("ERROR: Message lost: %08" PRIx32 "\n", regval);
}
else
{
fdcan_receive(priv,
config->msgram.rxfifo0 +
(ndx * priv->config->rxfifo0esize),
priv->config->rxfifo0esize);
#ifdef CONFIG_NET_CAN_ERRORS
/* Turning back on all configured RX error interrupts */
regval = fdcan_getreg(priv, STM32_FDCAN_IE_OFFSET);
regval |= FDCAN_RXERR_INTS;
fdcan_putreg(priv, STM32_FDCAN_IE_OFFSET, regval);
#endif
}
/* Acknowledge reading the FIFO entry */
fdcan_putreg(priv, STM32_FDCAN_RXF0A_OFFSET, ndx);
}
/* Re-enable CAN RX interrupts */
fdcan_rx0int(priv, true);
}
/****************************************************************************
* Name: fdcan_rx1interrupt_work
*
* Description:
* CAN RX FIFO 1 worker
*
****************************************************************************/
static void fdcan_rx1interrupt_work(void *arg)
{
struct stm32_fdcan_s *priv = (struct stm32_fdcan_s *)arg;
const struct stm32_config_s *config = NULL;
uint32_t regval = 0;
unsigned int nelem = 0;
unsigned int ndx = 0;
DEBUGASSERT(priv);
config = priv->config;
DEBUGASSERT(config);
/* Clear the RX FIFO1 new message interrupt */
fdcan_putreg(priv, STM32_FDCAN_IR_OFFSET, FDCAN_INT_RF1N);
/* Check if there is anything in RX FIFO1 */
regval = fdcan_getreg(priv, STM32_FDCAN_RXF1S_OFFSET);
nelem = (regval & FDCAN_RXFS_FFL_MASK) >> FDCAN_RXFS_FFL_SHIFT;
if (nelem == 0)
{
/* Clear the RX FIFO1 interrupt (and all other FIFO1-related
* interrupts)
*/
/* Handle the newly received message in FIFO1 */
ndx = (regval & FDCAN_RXFS_FGI_MASK) >> FDCAN_RXFS_FGI_SHIFT;
if ((regval & FDCAN_RXFS_RFL) != 0)
{
nerr("ERROR: Message lost: %08" PRIx32 "\n", regval);
}
else
{
fdcan_receive(priv,
config->msgram.rxfifo1 +
(ndx * priv->config->rxfifo1esize),
priv->config->rxfifo1esize);
#ifdef CONFIG_NET_CAN_ERRORS
/* Turning back on all configured RX error interrupts */
regval = fdcan_getreg(priv, STM32_FDCAN_IE_OFFSET);
regval |= FDCAN_RXERR_INTS;
fdcan_putreg(priv, STM32_FDCAN_IE_OFFSET, regval);
#endif
}
/* Acknowledge reading the FIFO entry */
fdcan_putreg(priv, STM32_FDCAN_RXF1A_OFFSET, ndx);
}
/* Re-enable CAN RX interrupts */
fdcan_rx1int(priv, true);
}
/****************************************************************************
* Name: fdcan_txdone_work
****************************************************************************/
static void fdcan_txdone_work(void *arg)
{
struct stm32_fdcan_s *priv = (struct stm32_fdcan_s *)arg;
fdcan_txdone(priv);
/* There should be space for a new TX in any event. Poll the network for
* new XMIT data
*/
net_lock();
devif_poll(&priv->dev, fdcan_txpoll);
net_unlock();
}
/****************************************************************************
* Name: fdcan_txdone
****************************************************************************/
static void fdcan_txdone(struct stm32_fdcan_s *priv)
{
const struct stm32_config_s *config = NULL;
unsigned int ndx = 0;
uint32_t regval = 0;
DEBUGASSERT(priv);
config = priv->config;
DEBUGASSERT(config);
/* Clear the pending TX completion interrupt (and all
* other TX-related interrupts)
*/
fdcan_putreg(priv, STM32_FDCAN_IR_OFFSET, FDCAN_TXFIFOQ_INTS);
/* Check all TX buffers */
regval = fdcan_getreg(priv, STM32_FDCAN_TXBTO_OFFSET);
for (ndx = 0; ndx < config->ntxfifoq; ndx++)
{
if ((regval & (1 << ndx)) != 0)
{
/* Tell the upper half that the transfer is finished. */
NETDEV_TXDONE(&priv->dev);
}
}
#ifdef CONFIG_NET_CAN_ERRORS
/* Turning back on PEA and PED error interrupts */
regval = fdcan_getreg(priv, STM32_FDCAN_IE_OFFSET);
regval |= (FDCAN_INT_PEA | FDCAN_INT_PED);
fdcan_putreg(priv, STM32_FDCAN_IE_OFFSET, regval);
#endif
/* Re-enable TX interrupts */
fdcan_txint(priv, true);
}
#ifdef CONFIG_NET_CAN_ERRORS
/****************************************************************************
* Name: fdcan_error_work
****************************************************************************/
static void fdcan_error_work(void *arg)
{
struct stm32_fdcan_s *priv = (struct stm32_fdcan_s *)arg;
uint32_t pending = 0;
uint32_t ir = 0;
uint32_t ie = 0;
uint32_t psr = 0;
/* Get the set of pending interrupts. */
ir = fdcan_getreg(priv, STM32_FDCAN_IR_OFFSET);
ie = fdcan_getreg(priv, STM32_FDCAN_IE_OFFSET);
pending = (ir & ie);
/* Check for common errors */
if ((pending & FDCAN_CMNERR_INTS) != 0)
{
/* When a protocol error ocurrs, the problem is recorded in
* the LEC/DLEC fields of the PSR register. In lieu of
* seprate interrupt flags for each error, the hardware
* groups procotol errors under a single interrupt each for
* arbitration and data phases.
*
* These errors have a tendency to flood the system with
* interrupts, so they are disabled here until we get a
* successful transfer/receive on the hardware
*/
psr = fdcan_getreg(priv, STM32_FDCAN_PSR_OFFSET);
if ((psr & FDCAN_PSR_LEC_MASK) != 0)
{
ie &= ~(FDCAN_INT_PEA | FDCAN_INT_PED);
fdcan_putreg(priv, STM32_FDCAN_IE_OFFSET, ie);
}
/* Clear the error indications */
fdcan_putreg(priv, STM32_FDCAN_IR_OFFSET, FDCAN_CMNERR_INTS);
}
/* Check for transmission errors */
if ((pending & FDCAN_TXERR_INTS) != 0)
{
/* An Acknowledge-Error will occur if for example the device
* is not connected to the bus.
*
* The CAN-Standard states that the Chip has to retry the
* message forever, which will produce an ACKE every time.
* To prevent this Interrupt-Flooding and the high CPU-Load
* we disable the ACKE here as long we didn't transfer at
* least one message successfully (see FDCAN_INT_TC below).
*/
/* Clear the error indications */
fdcan_putreg(priv, STM32_FDCAN_IR_OFFSET, FDCAN_TXERR_INTS);
}
/* Check for reception errors */
if ((pending & FDCAN_RXERR_INTS) != 0)
{
/* To prevent Interrupt-Flooding the current active
* RX error interrupts are disabled. After successfully
* receiving at least one CAN packet all RX error interrupts
* are turned back on.
*
* The Interrupt-Flooding can for example occur if the
* configured CAN speed does not match the speed of the other
* CAN nodes in the network.
*/
ie &= ~(pending & FDCAN_RXERR_INTS);
fdcan_putreg(priv, STM32_FDCAN_IE_OFFSET, ie);
/* Clear the error indications */
fdcan_putreg(priv, STM32_FDCAN_IR_OFFSET, FDCAN_RXERR_INTS);
}
/* Report errors */
net_lock();
fdcan_error(priv, pending & FDCAN_ANYERR_INTS);
net_unlock();
/* Re-enable ERROR interrupts */
fdcan_errint(priv, true);
}
/****************************************************************************
* Name: fdcan_error
*
* Description:
* Report a CAN error
*
* Input Parameters:
* dev - CAN-common state data
* status - Interrupt status with error bits set
*
* Returned Value:
* None
*
****************************************************************************/
static void fdcan_error(struct stm32_fdcan_s *priv, uint32_t status)
{
struct can_frame *frame = (struct can_frame *)priv->rxdesc;
uint32_t psr = 0;
uint16_t errbits = 0;
uint8_t data[CAN_ERR_DLC];
DEBUGASSERT(priv != NULL);
/* Encode error bits */
errbits = 0;
memset(data, 0, sizeof(data));
/* Always fill in "static" error conditions, but set the signaling bit
* only if the condition has changed (see IRQ-Flags below)
* They have to be filled in every time CAN_ERROR_CONTROLLER is set.
*/
psr = fdcan_getreg(priv, STM32_FDCAN_PSR_OFFSET);
if ((psr & FDCAN_PSR_EP) != 0)
{
data[1] |= (CAN_ERR_CRTL_RX_PASSIVE | CAN_ERR_CRTL_TX_PASSIVE);
}
if ((psr & FDCAN_PSR_EW) != 0)
{
data[1] |= (CAN_ERR_CRTL_RX_WARNING | CAN_ERR_CRTL_TX_WARNING);
}
if ((status & (FDCAN_INT_EP | FDCAN_INT_EW)) != 0)
{
/* "Error Passive" or "Error Warning" status changed */
errbits |= CAN_ERR_CRTL;
}
if ((status & FDCAN_INT_PEA) != 0)
{
/* Protocol Error in Arbitration Phase */
if ((psr & FDCAN_PSR_LEC_MASK) != 0)
{
/* Error code present */
if ((psr & FDCAN_PSR_LEC(FDCAN_PSR_EC_STUFF_ERROR)) != 0)
{
/* Stuff Error */
errbits |= CAN_ERR_PROT;
data[2] |= CAN_ERR_PROT_STUFF;
}
if ((psr & FDCAN_PSR_LEC(FDCAN_PSR_EC_FORM_ERROR)) != 0)
{
/* Format Error */
errbits |= CAN_ERR_PROT;
data[2] |= CAN_ERR_PROT_FORM;
}
if ((psr & FDCAN_PSR_LEC(FDCAN_PSR_EC_ACK_ERROR)) != 0)
{
/* Acknowledge Error */
errbits |= CAN_ERR_ACK;
}
if ((psr & FDCAN_PSR_LEC(FDCAN_PSR_EC_BIT0_ERROR)) != 0)
{
/* Bit0 Error */
errbits |= CAN_ERR_PROT;
data[2] |= CAN_ERR_PROT_BIT0;
}
if ((psr & FDCAN_PSR_LEC(FDCAN_PSR_EC_BIT1_ERROR)) != 0)
{
/* Bit1 Error */
errbits |= CAN_ERR_PROT;
data[2] |= CAN_ERR_PROT_BIT1;
}
if ((psr & FDCAN_PSR_LEC(FDCAN_PSR_EC_CRC_ERROR)) != 0)
{
/* Receive CRC Error */
errbits |= CAN_ERR_PROT;
data[3] |= (CAN_ERR_PROT_LOC_CRC_SEQ |
CAN_ERR_PROT_LOC_CRC_DEL);
}
if ((psr & FDCAN_PSR_LEC(FDCAN_PSR_EC_NO_CHANGE)) != 0)
{
/* No Change in Error */
errbits |= CAN_ERR_PROT;
data[2] |= CAN_ERR_PROT_UNSPEC;
}
}
}
if ((status & FDCAN_INT_PED) != 0)
{
/* Protocol Error in Data Phase */
if ((psr & FDCAN_PSR_DLEC_MASK) != 0)
{
/* Error code present */
if ((psr & FDCAN_PSR_DLEC(FDCAN_PSR_EC_STUFF_ERROR)) != 0)
{
/* Stuff Error */
errbits |= CAN_ERR_PROT;
data[2] |= CAN_ERR_PROT_STUFF;
}
if ((psr & FDCAN_PSR_DLEC(FDCAN_PSR_EC_FORM_ERROR)) != 0)
{
/* Format Error */
errbits |= CAN_ERR_PROT;
data[2] |= CAN_ERR_PROT_FORM;
}
if ((psr & FDCAN_PSR_DLEC(FDCAN_PSR_EC_ACK_ERROR)) != 0)
{
/* Acknowledge Error */
errbits |= CAN_ERR_ACK;
}
if ((psr & FDCAN_PSR_DLEC(FDCAN_PSR_EC_BIT0_ERROR)) != 0)
{
/* Bit0 Error */
errbits |= CAN_ERR_PROT;
data[2] |= CAN_ERR_PROT_BIT0;
}
if ((psr & FDCAN_PSR_DLEC(FDCAN_PSR_EC_BIT1_ERROR)) != 0)
{
/* Bit1 Error */
errbits |= CAN_ERR_PROT;
data[2] |= CAN_ERR_PROT_BIT1;
}
if ((psr & FDCAN_PSR_DLEC(FDCAN_PSR_EC_CRC_ERROR)) != 0)
{
/* Receive CRC Error */
errbits |= CAN_ERR_PROT;
data[3] |= (CAN_ERR_PROT_LOC_CRC_SEQ |
CAN_ERR_PROT_LOC_CRC_DEL);
}
if ((psr & FDCAN_PSR_DLEC(FDCAN_PSR_EC_NO_CHANGE)) != 0)
{
/* No Change in Error */
errbits |= CAN_ERR_PROT;
data[2] |= CAN_ERR_PROT_UNSPEC;
}
}
}
if ((status & FDCAN_INT_BO) != 0)
{
/* Bus_Off Status changed */
if ((psr & FDCAN_PSR_BO) != 0)
{
errbits |= CAN_ERR_BUSOFF;
}
else
{
errbits |= CAN_ERR_RESTARTED;
}
}
if ((status & (FDCAN_INT_RF0L | FDCAN_INT_RF1L)) != 0)
{
/* Receive FIFO 0/1 Message Lost
* Receive FIFO 1 Message Lost
*/
errbits |= CAN_ERR_CRTL;
data[1] |= CAN_ERR_CRTL_RX_OVERFLOW;
}
if ((status & FDCAN_INT_TEFL) != 0)
{
/* Tx Event FIFO Element Lost */
errbits |= CAN_ERR_CRTL;
data[1] |= CAN_ERR_CRTL_TX_OVERFLOW;
}
if ((status & FDCAN_INT_TOO) != 0)
{
/* Timeout Occurred */
errbits |= CAN_ERR_TX_TIMEOUT;
}
if ((status & (FDCAN_INT_MRAF | FDCAN_INT_ELO)) != 0)
{
/* Message RAM Access Failure
* Error Logging Overflow
*/
errbits |= CAN_ERR_CRTL;
data[1] |= CAN_ERR_CRTL_UNSPEC;
}
if (errbits != 0)
{
nerr("ERROR: errbits = %08" PRIx16 "\n", errbits);
/* Copy frame */
frame->can_id = errbits;
frame->can_dlc = CAN_ERR_DLC;
memcpy(frame->data, data, CAN_ERR_DLC);
/* Copy the buffer pointer to priv->dev.. Set amount of data
* in priv->dev.d_len
*/
priv->dev.d_len = sizeof(struct can_frame);
priv->dev.d_buf = (uint8_t *)frame;
/* Send to socket interface */
NETDEV_ERRORS(&priv->dev);
can_input(&priv->dev);
/* Point the packet buffer back to the next Tx buffer that will be
* used during the next write. If the write queue is full, then
* this will point at an active buffer, which must not be written
* to. This is OK because devif_poll won't be called unless the
* queue is not full.
*/
priv->dev.d_buf = (uint8_t *)priv->txdesc;
}
}
#endif /* CONFIG_NET_CAN_ERRORS */
/****************************************************************************
* Name: fdcan_receive
*
* Description:
* Receive an FDCAN messages
*
* Input Parameters:
* dev - CAN-common state data
* rxbuffer - The RX buffer containing the received messages
* nwords - The length of the RX buffer (element size in words).
*
* Returned Value:
* None
*
****************************************************************************/
static void fdcan_receive(struct stm32_fdcan_s *priv,
volatile uint32_t *rxbuffer,
unsigned long nwords)
{
fdcan_dumprxregs(dev->cd_priv, "Before receive");
/* CAN 2.0 or CAN FD */
#ifdef CONFIG_NET_CAN_CANFD
if ((rxbuffer[1] & BUFFER_R1_FDF) != 0)
{
struct canfd_frame *frame = (struct canfd_frame *)priv->rxdesc;
/* Format the CAN FD header */
/* Extract the RTR bit */
if ((rxbuffer[0] & BUFFER_R0_RTR) != 0)
{
frame->can_id |= CAN_RTR_FLAG;
}
#ifdef CONFIG_NET_CAN_EXTID
if ((rxbuffer[0] & BUFFER_R0_XTD) != 0)
{
/* Save the extended ID of the newly received message */
frame->can_id = ((rxbuffer[0] & BUFFER_R0_EXTID_MASK) >>
BUFFER_R0_EXTID_SHIFT);
frame->can_id |= CAN_EFF_FLAG;
}
else
{
frame->can_id = ((rxbuffer[0] & BUFFER_R0_STDID_MASK) >>
BUFFER_R0_STDID_SHIFT);
frame->can_id &= ~CAN_EFF_FLAG;
}
#else
if ((rxbuffer[0] & BUFFER_R0_XTD) != 0)
{
/* Drop any messages with extended IDs */
return;
}
/* Save the standard ID of the newly received message */
frame->can_id = ((rxbuffer[0] & BUFFER_R0_STDID_MASK) >>
BUFFER_R0_STDID_SHIFT);
#endif
/* Word R1 contains the DLC and timestamp */
frame->len = can_dlc_to_len[((rxbuffer[1] & BUFFER_R1_DLC_MASK) >>
BUFFER_R1_DLC_SHIFT)];
/* Get CANFD flags */
frame->flags = 0;
if ((rxbuffer[0] & BUFFER_R0_ESI) != 0)
{
frame->flags |= CANFD_ESI;
}
if ((rxbuffer[1] & BUFFER_R1_BRS) != 0)
{
frame->flags |= CANFD_BRS;
}
/* Save the message data */
memcpy(frame->data, (void *)&rxbuffer[2], frame->len);
/* Copy the buffer pointer to priv->dev.. Set amount of data
* in priv->dev.d_len
*/
priv->dev.d_len = sizeof(struct canfd_frame);
priv->dev.d_buf = (uint8_t *)frame;
}
else
#endif
{
struct can_frame *frame = (struct can_frame *)priv->rxdesc;
/* Format the CAN header */
/* Extract the RTR bit */
if ((rxbuffer[0] & BUFFER_R0_RTR) != 0)
{
frame->can_id |= CAN_RTR_FLAG;
}
#ifdef CONFIG_NET_CAN_EXTID
if ((rxbuffer[0] & BUFFER_R0_XTD) != 0)
{
/* Save the extended ID of the newly received message */
frame->can_id = ((rxbuffer[0] & BUFFER_R0_EXTID_MASK) >>
BUFFER_R0_EXTID_SHIFT);
frame->can_id |= CAN_EFF_FLAG;
}
else
{
frame->can_id = ((rxbuffer[0] & BUFFER_R0_STDID_MASK) >>
BUFFER_R0_STDID_SHIFT);
frame->can_id &= ~CAN_EFF_FLAG;
}
#else
if ((rxbuffer[0] & BUFFER_R0_XTD) != 0)
{
/* Drop any messages with extended IDs */
return;
}
/* Save the standard ID of the newly received message */
frame->can_id = ((rxbuffer[0] & BUFFER_R0_STDID_MASK) >>
BUFFER_R0_STDID_SHIFT);
#endif
/* Word R1 contains the DLC and timestamp */
frame->can_dlc = ((rxbuffer[1] & BUFFER_R1_DLC_MASK) >>
BUFFER_R1_DLC_SHIFT);
/* Save the message data */
memcpy(frame->data, (void *)&rxbuffer[2], frame->can_dlc);
/* Copy the buffer pointer to priv->dev.. Set amount of data
* in priv->dev.d_len
*/
priv->dev.d_len = sizeof(struct can_frame);
priv->dev.d_buf = (uint8_t *)frame;
}
/* Send to socket interface */
NETDEV_RXPACKETS(&priv->dev);
can_input(&priv->dev);
/* Point the packet buffer back to the next Tx buffer that will be
* used during the next write. If the write queue is full, then
* this will point at an active buffer, which must not be written
* to. This is OK because devif_poll won't be called unless the
* queue is not full.
*/
priv->dev.d_buf = (uint8_t *)priv->txdesc;
}
/****************************************************************************
* Name: fdcan_interrupt
*
* Description:
* Common FDCAN interrupt handler
*
* irq - The IRQ number of the interrupt.
* context - The register state save array at the time of the interrupt.
*
* Returned Value:
* Zero on success; a negated errno on failure
*
****************************************************************************/
static int fdcan_interrupt(int irq, void *context, void *arg)
{
struct stm32_fdcan_s *priv = (struct stm32_fdcan_s *)arg;
uint32_t pending = 0;
DEBUGASSERT(priv != NULL);
/* Get the set of pending interrupts. */
pending = fdcan_getreg(priv, STM32_FDCAN_IR_OFFSET);
#ifdef CONFIG_NET_CAN_ERRORS
/* Check for any errors */
if ((pending & FDCAN_ANYERR_INTS) != 0)
{
/* Disable further CAN ERROR interrupts and schedule to perform the
* interrupt processing on the worker thread
*/
fdcan_errint(priv, false);
work_queue(CANWORK, &priv->irqwork, fdcan_error_work, priv, 0);
}
#endif
/* Check for successful completion of a transmission */
if ((pending & FDCAN_INT_TC) != 0)
{
/* Disable further TX CAN interrupts. here can be no race
* condition here.
*/
fdcan_txint(priv, false);
work_queue(CANWORK, &priv->irqwork, fdcan_txdone_work, priv, 0);
}
else if ((pending & FDCAN_TXFIFOQ_INTS) != 0)
{
/* Clear unhandled TX events */
fdcan_putreg(priv, STM32_FDCAN_IR_OFFSET, FDCAN_TXFIFOQ_INTS);
}
if (pending & FDCAN_INT_RF1N)
{
/* Disable further CAN RX interrupts and schedule to perform the
* interrupt processing on the worker thread
*/
fdcan_rx1int(priv, false);
work_queue(CANWORK, &priv->irqwork,
fdcan_rx1interrupt_work, priv, 0);
}
/* Clear the RX FIFO0 new message interrupt */
if (pending & FDCAN_INT_RF0N)
{
/* Disable further CAN RX interrupts and schedule to perform the
* interrupt processing on the worker thread
*/
fdcan_rx0int(priv, false);
work_queue(CANWORK, &priv->irqwork,
fdcan_rx0interrupt_work, priv, 0);
}
return OK;
}
/****************************************************************************
* Name: fdcan_hw_initialize
*
* Description:
* FDCAN hardware initialization
*
* Input Parameters:
* priv - A pointer to the private data structure for this FDCAN peripheral
*
* Returned Value:
* Zero on success; a negated errno value on failure.
*
****************************************************************************/
static int fdcan_hw_initialize(struct stm32_fdcan_s *priv)
{
const struct stm32_config_s *config = NULL;
volatile uint32_t *msgram = NULL;
uint32_t regval = 0;
uint32_t cntr = 0;
DEBUGASSERT(priv);
config = priv->config;
DEBUGASSERT(config);
ninfo("FDCAN%d\n", config->port);
/* Clean message RAM */
msgram = config->msgram.stdfilters;
cntr = (FDCAN_MSGRAM_WORDS + 1);
while (cntr > 0)
{
*msgram++ = 0;
cntr--;
}
/* Configure FDCAN pins */
stm32_configgpio(config->rxpinset);
stm32_configgpio(config->txpinset);
/* Renable device if previosuly disabled in fdcan_shutdown() */
if (priv->state == FDCAN_STATE_DISABLED)
{
/* Reset Clock Stop Request bit */
regval = fdcan_getreg(priv, STM32_FDCAN_CCCR_OFFSET);
regval &= ~FDCAN_CCCR_CSR;
fdcan_putreg(priv, STM32_FDCAN_CCCR_OFFSET, regval);
/* Wait for Clock Stop Acknowledge bit reset to indicate
* device is operational
*/
while ((fdcan_getreg(priv, STM32_FDCAN_CCCR_OFFSET) & FDCAN_CCCR_CSA)
!= 0);
}
/* Enable the Initialization state */
regval = fdcan_getreg(priv, STM32_FDCAN_CCCR_OFFSET);
regval |= FDCAN_CCCR_INIT;
fdcan_putreg(priv, STM32_FDCAN_CCCR_OFFSET, regval);
/* Wait for initialization mode to take effect */
while ((fdcan_getreg(priv, STM32_FDCAN_CCCR_OFFSET) & FDCAN_CCCR_INIT)
== 0);
/* Enable writing to configuration registers */
regval = fdcan_getreg(priv, STM32_FDCAN_CCCR_OFFSET);
regval |= FDCAN_CCCR_CCE;
fdcan_putreg(priv, STM32_FDCAN_CCCR_OFFSET, regval);
/* Global Filter Configuration:
*
* ANFS=0: Store all non matching standard frame in RX FIFO0
* ANFE=0: Store all non matching extended frame in RX FIFO0
*/
regval = FDCAN_RXGFC_ANFE_RX_FIFO0 | FDCAN_RXGFC_ANFS_RX_FIFO0;
fdcan_putreg(priv, STM32_FDCAN_RXGFC_OFFSET, regval);
/* Extended ID Filter AND mask */
fdcan_putreg(priv, STM32_FDCAN_XIDAM_OFFSET, 0x1fffffff);
/* Disable all interrupts */
fdcan_putreg(priv, STM32_FDCAN_IE_OFFSET, 0);
fdcan_putreg(priv, STM32_FDCAN_TXBTIE_OFFSET, 0);
/* All interrupts directed to Line 0. But disable both interrupt lines 0
* and 1 for now.
*
* REVISIT: Only interrupt line 0 is used by this driver.
*/
fdcan_putreg(priv, STM32_FDCAN_ILS_OFFSET, 0);
fdcan_putreg(priv, STM32_FDCAN_ILE_OFFSET, 0);
/* Clear all pending interrupts. */
fdcan_putreg(priv, STM32_FDCAN_IR_OFFSET, FDCAN_INT_ALL);
/* Configure FDCAN bit timing */
fdcan_putreg(priv, STM32_FDCAN_NBTP_OFFSET, priv->nbtp);
fdcan_putreg(priv, STM32_FDCAN_DBTP_OFFSET, priv->dbtp);
/* Configure message RAM starting addresses and sizes. */
regval = FDCAN_RXGFC_LSS(config->nstdfilters);
regval |= FDCAN_RXGFC_LSE(config->nextfilters);
fdcan_putreg(priv, STM32_FDCAN_RXGFC_OFFSET, regval);
/* Dump RAM layout */
fdcan_dumpramlayout(priv);
/* Configure Message Filters */
/* Disable all standard filters */
msgram = config->msgram.stdfilters;
cntr = config->nstdfilters;
while (cntr > 0)
{
*msgram++ = STDFILTER_S0_SFEC_DISABLE;
cntr--;
}
/* Disable all extended filters */
msgram = config->msgram.extfilters;
cntr = config->nextfilters;
while (cntr > 0)
{
*msgram = EXTFILTER_F0_EFEC_DISABLE;
msgram = msgram + 2;
cntr--;
}
/* Input clock divider configuration */
regval = FDCANCLK_PDIV;
fdcan_putreg(priv, STM32_FDCAN_CKDIV_OFFSET, regval);
/* CC control register */
regval = fdcan_getreg(priv, STM32_FDCAN_CCCR_OFFSET);
regval &= ~(FDCAN_CCCR_NISO | FDCAN_CCCR_FDOE | FDCAN_CCCR_BRSE);
/* Select ISO11898-1 or Non ISO Bosch CAN FD Specification V1.0 */
switch (config->format)
{
case FDCAN_ISO11898_1_FORMAT:
{
break;
}
case FDCAN_NONISO_BOSCH_V1_FORMAT:
{
regval |= FDCAN_CCCR_NISO;
break;
}
default:
{
return -EINVAL;
}
}
/* Select Classic CAN mode or FD mode with or without fast bit rate
* switching
*/
switch (config->mode)
{
case FDCAN_CLASSIC_MODE:
{
break;
}
#ifdef CONFIG_NET_CAN_CANFD
case FDCAN_FD_MODE:
{
regval |= FDCAN_CCCR_FDOE;
break;
}
case FDCAN_FD_BRS_MODE:
{
regval |= (FDCAN_CCCR_FDOE | FDCAN_CCCR_BRSE);
break;
}
#endif
default:
{
return -EINVAL;
}
}
/* Set the initial CAN mode */
fdcan_putreg(priv, STM32_FDCAN_CCCR_OFFSET, regval);
/* Enable FIFO/Queue mode */
regval = fdcan_getreg(priv, STM32_FDCAN_TXBC_OFFSET);
#ifdef CONFIG_STM32_FDCAN_QUEUE_MODE
regval |= FDCAN_TXBC_TFQM;
#else
regval &= ~FDCAN_TXBC_TFQM;
#endif
fdcan_putreg(priv, STM32_FDCAN_TXBC_OFFSET, regval);
#ifdef STM32_FDCAN_LOOPBACK
/* Is loopback mode selected for this peripheral? */
if (config->loopback)
{
/* FDCAN_CCCR_TEST - Test mode enable
* FDCAN_CCCR_MON - Bus monitoring mode (for internal loopback)
* FDCAN_TEST_LBCK - Loopback mode
*/
regval = fdcan_getreg(priv, STM32_FDCAN_CCCR_OFFSET);
regval |= (FDCAN_CCCR_TEST | FDCAN_CCCR_MON);
fdcan_putreg(priv, STM32_FDCAN_CCCR_OFFSET, regval);
regval = fdcan_getreg(priv, STM32_FDCAN_TEST_OFFSET);
regval |= FDCAN_TEST_LBCK;
fdcan_putreg(priv, STM32_FDCAN_TEST_OFFSET, regval);
}
#endif
/* Configure interrupt lines */
/* Direct all interrupts to Line 0.
*
* Bits in the ILS register correspond to each FDCAN interrupt; A bit
* set to '1' is directed to interrupt line 1; a bit cleared to '0'
* is directed interrupt line 0.
*
* REVISIT: Nothing is done here. Only interrupt line 0 is used by
* this driver and ILS was already cleared above.
*/
/* Enable only interrupt line 0. */
fdcan_putreg(priv, STM32_FDCAN_ILE_OFFSET, FDCAN_ILE_EINT0);
/* Disable initialization mode to enable normal operation */
regval = fdcan_getreg(priv, STM32_FDCAN_CCCR_OFFSET);
regval &= ~FDCAN_CCCR_INIT;
fdcan_putreg(priv, STM32_FDCAN_CCCR_OFFSET, regval);
return OK;
}
/****************************************************************************
* Function: fdcan_ifup
*
* Description:
* NuttX Callback: Bring up the Ethernet interface when an IP address is
* provided
*
* Input Parameters:
* dev - Reference to the NuttX driver state structure
*
* Returned Value:
* None
*
* Assumptions:
*
****************************************************************************/
static int fdcan_ifup(struct net_driver_s *dev)
{
struct stm32_fdcan_s *priv =
(struct stm32_fdcan_s *)dev->d_private;
const struct stm32_config_s *config = NULL;
DEBUGASSERT(priv);
config = priv->config;
DEBUGASSERT(config);
/* Setup CAN */
fdcan_setup(priv);
/* Enable interrupts */
fdcan_rx0int(priv, true);
fdcan_rx1int(priv, true);
fdcan_txint(priv, true);
#ifdef CONFIG_NET_CAN_ERRORS
fdcan_errint(priv, true);
#endif
/* Enable the interrupts at the NVIC */
up_enable_irq(config->irq0);
up_enable_irq(config->irq1);
priv->bifup = true;
priv->txdesc = (struct can_frame *)priv->tx_pool;
priv->rxdesc = (struct can_frame *)priv->rx_pool;
priv->dev.d_buf = (uint8_t *)priv->txdesc;
return OK;
}
/****************************************************************************
* Function: fdcan_ifdown
*
* Description:
* NuttX Callback: Stop the interface.
*
* Input Parameters:
* dev - Reference to the NuttX driver state structure
*
* Returned Value:
* None
*
* Assumptions:
*
****************************************************************************/
static int fdcan_ifdown(struct net_driver_s *dev)
{
struct stm32_fdcan_s *priv =
(struct stm32_fdcan_s *)dev->d_private;
/* Disable CAN interrupts */
fdcan_shutdown(priv);
/* Reset CAN */
fdcan_reset(priv);
return OK;
}
/****************************************************************************
* Function: fdcan_txpoll
*
* Description:
* The transmitter is available, check if the network has any outgoing
* packets ready to send. This is a callback from devif_poll().
* devif_poll() may be called:
*
* 1. When the preceding TX packet send is complete,
* 2. When the preceding TX packet send timesout and the interface is reset
* 3. During normal TX polling
*
* Input Parameters:
* dev - Reference to the NuttX driver state structure
*
* Returned Value:
* OK on success; a negated errno on failure
*
* Assumptions:
* May or may not be called from an interrupt handler. In either case,
* global interrupts are disabled, either explicitly or indirectly through
* interrupt handling logic.
*
****************************************************************************/
static int fdcan_txpoll(struct net_driver_s *dev)
{
struct stm32_fdcan_s *priv =
(struct stm32_fdcan_s *)dev->d_private;
/* If the polling resulted in data that should be sent out on the network,
* the field d_len is set to a value > 0.
*/
if (priv->dev.d_len > 0)
{
fdcan_txdone(priv);
/* Send the packet */
fdcan_send(priv);
/* Check if there is room in the device to hold another packet. If
* not, return a non-zero value to terminate the poll.
*/
if (fdcan_txready(priv) == false)
{
return -EBUSY;
}
}
/* If zero is returned, the polling will continue until all connections
* have been examined.
*/
return 0;
}
/****************************************************************************
* Function: fdcan_txavail_work
*
* Description:
* Perform an out-of-cycle poll on the worker thread.
*
* Input Parameters:
* arg - Reference to the NuttX driver state structure (cast to void*)
*
* Returned Value:
* None
*
* Assumptions:
* Called on the higher priority worker thread.
*
****************************************************************************/
static void fdcan_txavail_work(void *arg)
{
struct stm32_fdcan_s *priv = (struct stm32_fdcan_s *)arg;
/* Ignore the notification if the interface is not yet up */
net_lock();
if (priv->bifup)
{
/* Check if there is room in the hardware to hold another outgoing
* packet.
*/
if (fdcan_txready(priv))
{
/* No, there is space for another transfer. Poll the network for
* new XMIT data.
*/
devif_poll(&priv->dev, fdcan_txpoll);
}
}
net_unlock();
}
/****************************************************************************
* Function: fdcan_txavail
*
* Description:
* Driver callback invoked when new TX data is available. This is a
* stimulus perform an out-of-cycle poll and, thereby, reduce the TX
* latency.
*
* Input Parameters:
* dev - Reference to the NuttX driver state structure
*
* Returned Value:
* None
*
* Assumptions:
* Called in normal user mode
*
****************************************************************************/
static int fdcan_txavail(struct net_driver_s *dev)
{
struct stm32_fdcan_s *priv =
(struct stm32_fdcan_s *)dev->d_private;
/* Is our single work structure available? It may not be if there are
* pending interrupt actions and we will have to ignore the Tx
* availability action.
*/
if (work_available(&priv->pollwork))
{
/* Schedule to serialize the poll on the worker thread. */
fdcan_txavail_work(priv);
}
return OK;
}
/****************************************************************************
* Function: fdcan_ioctl
*
* Description:
* PHY ioctl command handler
*
* Input Parameters:
* dev - Reference to the NuttX driver state structure
* cmd - ioctl command
* arg - Argument accompanying the command
*
* Returned Value:
* Zero (OK) on success; a negated errno value on failure.
*
* Assumptions:
*
****************************************************************************/
#ifdef CONFIG_NETDEV_IOCTL
static int fdcan_netdev_ioctl(struct net_driver_s *dev, int cmd,
unsigned long arg);
{
struct stm32_fdcan_s *priv =
(struct stm32_fdcan_s *)dev->d_private;
int ret = OK;
DEBUGASSERT(priv);
switch (cmd)
{
/* TODO */
default:
ret = -ENOTTY;
break;
}
return ret;
}
#endif /* CONFIG_NETDEV_IOCTL */
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: stm32_cansockinitialize
*
* Description:
* Initialize the selected FDCAN port as CAN socket interface
*
* Input Parameters:
* Port number (for hardware that has multiple FDCAN interfaces)
*
* Returned Value:
* OK on success; Negated errno on failure.
*
****************************************************************************/
int stm32_fdcansockinitialize(int port)
{
struct stm32_fdcan_s *priv = NULL;
const struct stm32_config_s *config = NULL;
int ret = OK;
ninfo("FDCAN%d\n", port);
/* Select FDCAN peripheral to be initialized */
#ifdef CONFIG_STM32_FDCAN1
if (port == FDCAN1)
{
/* Select the FDCAN1 device structure */
priv = &g_fdcan1priv;
config = &g_fdcan1const;
}
else
#endif
#ifdef CONFIG_STM32_FDCAN2
if (port == FDCAN2)
{
/* Select the FDCAN2 device structure */
priv = &g_fdcan2priv;
config = &g_fdcan2const;
}
else
#endif
#ifdef CONFIG_STM32_FDCAN3
if (port == FDCAN3)
{
/* Select the FDCAN3 device structure */
priv = &g_fdcan3priv;
config = &g_fdcan3const;
}
else
#endif
{
nerr("ERROR: Unsupported port %d\n", port);
ret = -EINVAL;
goto errout;
}
/* Perform one time data initialization */
memset(priv, 0, sizeof(struct stm32_fdcan_s));
priv->config = config;
/* Set the initial bit timing. This might change subsequently
* due to IOCTL command processing.
*/
priv->nbtp = config->nbtp;
priv->dbtp = config->dbtp;
/* Initialize the driver structure */
priv->dev.d_ifup = fdcan_ifup;
priv->dev.d_ifdown = fdcan_ifdown;
priv->dev.d_txavail = fdcan_txavail;
#ifdef CONFIG_NETDEV_IOCTL
priv->dev.d_ioctl = fdcan_netdev_ioctl;
#endif
priv->dev.d_private = priv;
/* Put the interface in the down state. This usually amounts to resetting
* the device and/or calling fdcan_ifdown().
*/
ninfo("callbacks done\n");
fdcan_ifdown(&priv->dev);
/* Register the device with the OS so that socket IOCTLs can be performed */
ret = netdev_register(&priv->dev, NET_LL_CAN);
errout:
return ret;
}
/****************************************************************************
* Name: arm_netinitialize
*
* Description:
* Initialize the CAN device interfaces. If there is more than one device
* interface in the chip, then board-specific logic will have to provide
* this function to determine which, if any, CAN interfaces should be
* initialized.
*
****************************************************************************/
#if !defined(CONFIG_NETDEV_LATEINIT)
void arm_netinitialize(void)
{
#ifdef CONFIG_STM32_CAN1
stm32_fdcansockinitialize(FDCAN1);
#endif
#ifdef CONFIG_STM32_CAN2
stm32_fdcansockinitialize(FDCAN2);
#endif
#ifdef CONFIG_STM32_CAN3
stm32_fdcansockinitialize(FDCAN3);
#endif
}
#endif