8850dee746
Signed-off-by: chao an <anchao@xiaomi.com>
2527 lines
70 KiB
C
2527 lines
70 KiB
C
/****************************************************************************
|
|
* arch/arm/src/stm32h7/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
|
|
*
|
|
* 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 <stdbool.h>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <time.h>
|
|
#include <unistd.h>
|
|
#include <debug.h>
|
|
#include <errno.h>
|
|
|
|
#include <nuttx/can.h>
|
|
#include <nuttx/wdog.h>
|
|
#include <nuttx/irq.h>
|
|
#include <nuttx/arch.h>
|
|
#include <nuttx/wqueue.h>
|
|
#include <nuttx/signal.h>
|
|
#include <nuttx/net/netdev.h>
|
|
#include <nuttx/net/can.h>
|
|
#include <netpacket/can.h>
|
|
|
|
#if defined(CONFIG_NET_CAN_RAW_TX_DEADLINE) || defined(CONFIG_NET_TIMESTAMP)
|
|
#include <sys/time.h>
|
|
#endif
|
|
|
|
#include <arch/board/board.h>
|
|
|
|
#include "arm_internal.h"
|
|
#include "chip.h"
|
|
#include "stm32.h"
|
|
|
|
/****************************************************************************
|
|
* Pre-processor Definitions
|
|
****************************************************************************/
|
|
|
|
/* General Configuration ****************************************************/
|
|
|
|
#if !defined(CONFIG_SCHED_WORKQUEUE)
|
|
# error Work queue support is required; HPWORK is recommended
|
|
#else
|
|
|
|
/* If processing is not done at the interrupt level, then work queue support
|
|
* is required.
|
|
*
|
|
* The high-priority work queue is suggested in order to minimize latency of
|
|
* critical Rx/Tx transactions on the CAN bus.
|
|
*/
|
|
|
|
# if defined(CONFIG_STM32H7_FDCAN_HPWORK)
|
|
# define CANWORK HPWORK
|
|
# elif defined(CONFIG_STM32H7_FDCAN_LPWORK)
|
|
# define CANWORK LPWORK
|
|
# else
|
|
# define CANWORK LPWORK
|
|
# endif
|
|
#endif
|
|
|
|
#ifdef CONFIG_NET_CAN_RAW_TX_DEADLINE
|
|
# define TX_TIMEOUT_WQ
|
|
#endif
|
|
|
|
/* Message RAM Configuration ************************************************/
|
|
|
|
#define WORD_LENGTH 4U
|
|
|
|
/* Define number of Rx / Tx elements in message RAM; note that elements are
|
|
* given sizes in number of words (4-byte chunks)
|
|
*
|
|
* Up to 64 Rx elements and 32 Tx elements may be configured per interface
|
|
*
|
|
* Note there are a total of 2560 words available shared between all FDCAN
|
|
* interfaces for Rx, Tx, and filter storage
|
|
*/
|
|
|
|
#ifdef CONFIG_NET_CAN_CANFD
|
|
# define FIFO_ELEMENT_SIZE 18 /* size (in Words) of a FIFO element in message RAM (CANFD_MTU / 4) */
|
|
# define NUM_RX_FIFO0 14 /* 14 elements max for RX FIFO0 */
|
|
# define NUM_RX_FIFO1 0 /* No elements for RX FIFO1 */
|
|
# define NUM_TX_FIFO 7 /* 7 elements max for TX FIFO */
|
|
#else
|
|
# define FIFO_ELEMENT_SIZE 4 /* size (in Words) of a FIFO element in message RAM (CAN_MTU / 4) */
|
|
# define NUM_RX_FIFO0 64 /* 64 elements max for RX FIFO0 */
|
|
# define NUM_RX_FIFO1 0 /* No elements for RX FIFO1 */
|
|
# define NUM_TX_FIFO 32 /* 32 elements max for TX FIFO */
|
|
#endif
|
|
|
|
/* Intermediate message buffering *******************************************/
|
|
|
|
#define POOL_SIZE 1
|
|
|
|
#if defined(CONFIG_NET_CAN_RAW_TX_DEADLINE) || defined(CONFIG_NET_TIMESTAMP)
|
|
#define MSG_DATA sizeof(struct timeval)
|
|
#else
|
|
#define MSG_DATA 0
|
|
#endif
|
|
|
|
#ifdef CONFIG_NET_CAN_CANFD
|
|
# define FRAME_TYPE struct canfd_frame
|
|
#else
|
|
# define FRAME_TYPE struct can_frame
|
|
#endif
|
|
|
|
/* CAN Clock Configuration **************************************************/
|
|
|
|
#define STM32_FDCANCLK STM32_HSE_FREQUENCY
|
|
#define CLK_FREQ STM32_FDCANCLK
|
|
#define PRESDIV_MAX 256
|
|
|
|
/* Interrupts ***************************************************************/
|
|
|
|
#define FDCAN_IR_MASK 0x3fcfffff /* Mask of all non-reserved bits in FDCAN_IR */
|
|
|
|
/****************************************************************************
|
|
* Private Types
|
|
****************************************************************************/
|
|
|
|
/* CAN ID word, as defined by FDCAN device (Note xtd/rtr/esi bit positions) */
|
|
|
|
union can_id_u
|
|
{
|
|
volatile uint32_t can_id;
|
|
struct
|
|
{
|
|
volatile uint32_t extid : 29;
|
|
volatile uint32_t resex : 3;
|
|
};
|
|
struct
|
|
{
|
|
volatile uint32_t res : 18;
|
|
volatile uint32_t stdid : 11;
|
|
volatile uint32_t rtr : 1;
|
|
volatile uint32_t xtd : 1;
|
|
volatile uint32_t esi : 1;
|
|
};
|
|
};
|
|
|
|
/* Union of 4 bytes as 1 register */
|
|
|
|
union payload_u
|
|
{
|
|
volatile uint32_t word;
|
|
struct
|
|
{
|
|
volatile uint32_t b00 : 8;
|
|
volatile uint32_t b01 : 8;
|
|
volatile uint32_t b02 : 8;
|
|
volatile uint32_t b03 : 8;
|
|
};
|
|
};
|
|
|
|
/* Message RAM Structures ***************************************************/
|
|
|
|
/* Rx FIFO Element Header -- RM0433 pg 2536 */
|
|
|
|
union rx_fifo_header_u
|
|
{
|
|
struct
|
|
{
|
|
volatile uint32_t w0;
|
|
volatile uint32_t w1;
|
|
};
|
|
|
|
struct
|
|
{
|
|
/* First word */
|
|
|
|
union can_id_u id;
|
|
|
|
/* Second word */
|
|
|
|
volatile uint32_t rxts : 16; /* Rx timestamp */
|
|
volatile uint32_t dlc : 4; /* Data length code */
|
|
volatile uint32_t brs : 1; /* Bitrate switching */
|
|
volatile uint32_t fdf : 1; /* FD frame */
|
|
volatile uint32_t res : 2; /* Reserved for Tx Event */
|
|
volatile uint32_t fidx : 7; /* Filter index */
|
|
volatile uint32_t anmf : 1; /* Accepted non-matching frame */
|
|
};
|
|
};
|
|
|
|
/* Tx FIFO Element Header -- RM0433 pg 2538 */
|
|
|
|
union tx_fifo_header_u
|
|
{
|
|
struct
|
|
{
|
|
volatile uint32_t w0;
|
|
volatile uint32_t w1;
|
|
};
|
|
|
|
struct
|
|
{
|
|
/* First word */
|
|
|
|
union can_id_u id;
|
|
|
|
/* Second word */
|
|
|
|
volatile uint32_t res1 : 16; /* Reserved for Tx Event timestamp */
|
|
volatile uint32_t dlc : 4; /* Data length code */
|
|
volatile uint32_t brs : 1; /* Bitrate switching */
|
|
volatile uint32_t fdf : 1; /* FD frame */
|
|
volatile uint32_t res2 : 1; /* Reserved for Tx Event */
|
|
volatile uint32_t efc : 1; /* Event FIFO control */
|
|
volatile uint32_t mm : 8; /* Message marker (user data; copied to Tx Event) */
|
|
};
|
|
};
|
|
|
|
/* Rx FIFO Element */
|
|
|
|
struct rx_fifo_s
|
|
{
|
|
union rx_fifo_header_u header;
|
|
#ifdef CONFIG_NET_CAN_CANFD
|
|
union payload_u data[16]; /* 64-byte FD payload */
|
|
#else
|
|
union payload_u data[2]; /* 8-byte Classic payload */
|
|
#endif
|
|
};
|
|
|
|
/* Tx FIFO Element */
|
|
|
|
struct tx_fifo_s
|
|
{
|
|
union tx_fifo_header_u header;
|
|
#ifdef CONFIG_NET_CAN_CANFD
|
|
union payload_u data[16]; /* 64-byte FD payload */
|
|
#else
|
|
union payload_u data[2]; /* 8-byte Classic payload */
|
|
#endif
|
|
};
|
|
|
|
/* Tx Mailbox Status Tracking */
|
|
|
|
#define TX_ABORT -1
|
|
#define TX_FREE 0
|
|
#define TX_BUSY 1
|
|
|
|
struct txmbstats
|
|
{
|
|
#ifdef CONFIG_NET_CAN_RAW_TX_DEADLINE
|
|
struct timeval deadline;
|
|
struct wdog_s txtimeout;
|
|
#endif
|
|
int8_t pending;
|
|
};
|
|
|
|
/* FDCAN Device hardware configuration **************************************/
|
|
|
|
struct fdcan_config_s
|
|
{
|
|
uint32_t tx_pin; /* GPIO configuration for TX */
|
|
uint32_t rx_pin; /* GPIO configuration for RX */
|
|
uint32_t mb_irq[2]; /* FDCAN Interrupt 0, 1 (Rx, Tx) */
|
|
};
|
|
|
|
struct fdcan_bitseg
|
|
{
|
|
uint32_t bitrate;
|
|
uint8_t sjw;
|
|
uint8_t bs1;
|
|
uint8_t bs2;
|
|
uint8_t prescaler;
|
|
};
|
|
|
|
struct fdcan_message_ram
|
|
{
|
|
uint32_t filt_stdid_addr;
|
|
uint32_t filt_extid_addr;
|
|
uint32_t rxfifo0_addr;
|
|
uint32_t rxfifo1_addr;
|
|
uint32_t txfifo_addr;
|
|
uint8_t n_stdfilt;
|
|
uint8_t n_extfilt;
|
|
uint8_t n_rxfifo0;
|
|
uint8_t n_rxfifo1;
|
|
uint8_t n_txfifo;
|
|
};
|
|
|
|
/* FDCAN device structures **************************************************/
|
|
|
|
#ifdef CONFIG_STM32H7_FDCAN1
|
|
static const struct fdcan_config_s stm32_fdcan0_config =
|
|
{
|
|
.tx_pin = GPIO_CAN1_TX,
|
|
.rx_pin = GPIO_CAN1_RX,
|
|
.mb_irq =
|
|
{
|
|
STM32_IRQ_FDCAN1_0,
|
|
STM32_IRQ_FDCAN1_1,
|
|
},
|
|
};
|
|
#endif
|
|
|
|
#ifdef CONFIG_STM32H7_FDCAN2
|
|
static const struct fdcan_config_s stm32_fdcan1_config =
|
|
{
|
|
.tx_pin = GPIO_CAN2_TX,
|
|
.rx_pin = GPIO_CAN2_RX,
|
|
.mb_irq =
|
|
{
|
|
STM32_IRQ_FDCAN2_0,
|
|
STM32_IRQ_FDCAN2_1 ,
|
|
},
|
|
};
|
|
#endif
|
|
|
|
#ifdef CONFIG_STM32H7_FDCAN3
|
|
# error "FDCAN3 support not yet added to stm32h7x3xx header files (pinmap, irq, etc.)"
|
|
static const struct fdcan_config_s stm32_fdcan2_config =
|
|
{
|
|
.tx_pin = GPIO_CAN3_TX,
|
|
.rx_pin = GPIO_CAN3_RX,
|
|
.mb_irq =
|
|
{
|
|
STM32_IRQ_FDCAN3_0,
|
|
STM32_IRQ_FDCAN3_1 ,
|
|
},
|
|
};
|
|
#endif
|
|
|
|
/* The fdcan_driver_s encapsulates all state information for a single
|
|
* hardware interface
|
|
*/
|
|
|
|
struct fdcan_driver_s
|
|
{
|
|
const struct fdcan_config_s *config; /* Pin config */
|
|
uint8_t iface_idx; /* FDCAN interface index (0 or 1) */
|
|
uint32_t base; /* FDCAN base address */
|
|
|
|
struct fdcan_bitseg arbi_timing; /* Timing for arbitration phase */
|
|
#ifdef CONFIG_NET_CAN_CANFD
|
|
struct fdcan_bitseg data_timing; /* Timing for data phase */
|
|
#endif
|
|
|
|
struct fdcan_message_ram message_ram; /* Start addresses for each reagion of Message RAM */
|
|
struct rx_fifo_s *rx; /* Pointer to Rx FIFO0 in Message RAM */
|
|
struct tx_fifo_s *tx; /* Pointer to Tx mailboxes in Message RAM */
|
|
|
|
/* Work queue configs for deferring interrupt and poll work */
|
|
|
|
struct work_s rxwork;
|
|
struct work_s txcwork;
|
|
struct work_s txdwork;
|
|
struct work_s pollwork;
|
|
|
|
uint32_t irflags; /* Used to copy IR flags from IRQ context to work_queue */
|
|
|
|
/* Intermediate storage of Tx / Rx frames outside of Message RAM */
|
|
|
|
uint8_t tx_pool[(sizeof(FRAME_TYPE)+MSG_DATA)*POOL_SIZE];
|
|
uint8_t rx_pool[(sizeof(FRAME_TYPE)+MSG_DATA)*POOL_SIZE];
|
|
|
|
struct net_driver_s dev; /* Interface understood by the network */
|
|
bool bifup; /* true:ifup false:ifdown */
|
|
|
|
struct txmbstats txmb[NUM_TX_FIFO]; /* Track deadline and status of every Tx entry */
|
|
};
|
|
|
|
/****************************************************************************
|
|
* Private Data
|
|
****************************************************************************/
|
|
|
|
#ifdef CONFIG_STM32H7_FDCAN1
|
|
static struct fdcan_driver_s g_fdcan0;
|
|
#endif
|
|
|
|
#ifdef CONFIG_STM32H7_FDCAN2
|
|
static struct fdcan_driver_s g_fdcan1;
|
|
#endif
|
|
|
|
#ifdef CONFIG_STM32H7_FDCAN3
|
|
static struct fdcan_driver_s g_fdcan2;
|
|
#endif
|
|
|
|
static bool g_apb1h_init = false;
|
|
|
|
/****************************************************************************
|
|
* Private Function Prototypes
|
|
****************************************************************************/
|
|
|
|
/* Common TX logic */
|
|
|
|
static bool fdcan_txringfull(struct fdcan_driver_s *priv);
|
|
static int fdcan_transmit(struct fdcan_driver_s *priv);
|
|
static int fdcan_txpoll(struct net_driver_s *dev);
|
|
|
|
/* Helper functions */
|
|
|
|
#ifdef CONFIG_STM32H7_FDCAN_REGDEBUG
|
|
static void fdcan_dumpregs(struct fdcan_driver_s *priv);
|
|
#endif
|
|
|
|
int32_t fdcan_bittiming(struct fdcan_bitseg *timing);
|
|
|
|
static void fdcan_apb1hreset(void);
|
|
static void fdcan_setinit(uint32_t base, uint32_t init);
|
|
static void fdcan_setenable(uint32_t base, uint32_t enable);
|
|
static void fdcan_setconfig(uint32_t base, uint32_t config_enable);
|
|
static bool fdcan_waitccr_change(uint32_t base,
|
|
uint32_t mask,
|
|
uint32_t target_state);
|
|
|
|
static void fdcan_enable_interrupts(struct fdcan_driver_s *priv);
|
|
static void fdcan_disable_interrupts(struct fdcan_driver_s *priv);
|
|
|
|
/* Interrupt handling */
|
|
|
|
static void fdcan_receive(struct fdcan_driver_s *priv);
|
|
static void fdcan_receive_work(void *arg);
|
|
static void fdcan_txdone(struct fdcan_driver_s *priv);
|
|
static void fdcan_txdone_work(void *arg);
|
|
|
|
static int fdcan_interrupt(int irq, void *context,
|
|
void *arg);
|
|
|
|
static void fdcan_check_errors(struct fdcan_driver_s *priv);
|
|
|
|
/* Watchdog timer expirations */
|
|
|
|
#ifdef TX_TIMEOUT_WQ
|
|
static void fdcan_txtimeout_work(void *arg);
|
|
static void fdcan_txtimeout_expiry(wdparm_t arg);
|
|
#endif
|
|
|
|
/* NuttX networking stack 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
|
|
|
|
/* Initialization and Reset */
|
|
|
|
static int fdcan_initialize(struct fdcan_driver_s *priv);
|
|
static void fdcan_reset(struct fdcan_driver_s *priv);
|
|
|
|
/****************************************************************************
|
|
* Private Functions
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Name: fdcan_dumpregs
|
|
*
|
|
* Dump common register values to the console for debugging purposes.
|
|
****************************************************************************/
|
|
|
|
#ifdef CONFIG_STM32H7_FDCAN_REGDEBUG
|
|
static void fdcan_dumpregs(struct fdcan_driver_s *priv)
|
|
{
|
|
printf("-------------- FDCAN Reg Dump ----------------\n");
|
|
printf("CAN%d Base: 0x%lx\n", priv->iface_idx, priv->base);
|
|
|
|
uint32_t regval;
|
|
regval = getreg32(priv->base + STM32_FDCAN_CCCR_OFFSET);
|
|
printf("CCCR = 0x%lx\n", regval);
|
|
regval = getreg32(priv->base + STM32_FDCAN_ECR_OFFSET);
|
|
printf("ECR = 0x%lx\n", regval);
|
|
|
|
regval = getreg32(priv->base + STM32_FDCAN_NBTP_OFFSET);
|
|
printf("NBTP = 0x%lx\n", regval);
|
|
regval = getreg32(priv->base + STM32_FDCAN_DBTP_OFFSET);
|
|
printf("DBTP = 0x%lx\n", regval);
|
|
|
|
regval = getreg32(priv->base + STM32_FDCAN_TXBC_OFFSET);
|
|
printf("TXBC = 0x%lx\n", regval);
|
|
regval = getreg32(priv->base + STM32_FDCAN_RXF0C_OFFSET);
|
|
printf("RXF0C = 0x%lx\n", regval);
|
|
|
|
regval = getreg32(priv->base + STM32_FDCAN_TXESC_OFFSET);
|
|
printf("TXESC = 0x%lx\n", regval);
|
|
regval = getreg32(priv->base + STM32_FDCAN_RXESC_OFFSET);
|
|
printf("RXESC = 0x%lx\n", regval);
|
|
|
|
regval = getreg32(priv->base + STM32_FDCAN_IE_OFFSET);
|
|
printf("IE = 0x%lx\n", regval);
|
|
regval = getreg32(priv->base + STM32_FDCAN_ILE_OFFSET);
|
|
printf("ILE = 0x%lx\n", regval);
|
|
regval = getreg32(priv->base + STM32_FDCAN_ILS_OFFSET);
|
|
printf("ILS = 0x%lx\n", regval);
|
|
|
|
/* Print out some possibly interesting unhandled interrupts */
|
|
|
|
regval = getreg32(priv->base + STM32_FDCAN_IR_OFFSET);
|
|
printf("IR = 0x%lx\n", regval);
|
|
|
|
if (regval & FDCAN_IR_PEA || regval & FDCAN_IR_PED)
|
|
{
|
|
/* Protocol error -- check protocol status register for details */
|
|
|
|
regval = getreg32(priv->base + STM32_FDCAN_PSR_OFFSET);
|
|
printf("--PSR.LEC = %d\n", regval & FDCAN_PSR_LEC);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/****************************************************************************
|
|
* Name: fdcan_bittiming
|
|
*
|
|
* Description:
|
|
* Convert desired bitrate to FDCAN bit segment values
|
|
* The computed values apply to both data and arbitration phases
|
|
*
|
|
* Input Parameters:
|
|
* timing - structure to store bit timing
|
|
*
|
|
* Returned Value:
|
|
* OK on success; >0 on failure.
|
|
****************************************************************************/
|
|
|
|
int32_t fdcan_bittiming(struct fdcan_bitseg *timing)
|
|
{
|
|
/* Implementation ported from PX4's uavcan_drivers/stm32[h7]
|
|
*
|
|
* Ref. "Automatic Baudrate Detection in CANopen Networks", U. Koppe
|
|
* MicroControl GmbH & Co. KG
|
|
* CAN in Automation, 2003
|
|
*
|
|
* According to the source, optimal quanta per bit are:
|
|
* Bitrate Optimal Maximum
|
|
* 1000 kbps 8 10
|
|
* 500 kbps 16 17
|
|
* 250 kbps 16 17
|
|
* 125 kbps 16 17
|
|
*/
|
|
|
|
const uint32_t target_bitrate = timing->bitrate;
|
|
static const int32_t max_bs1 = 16;
|
|
static const int32_t max_bs2 = 8;
|
|
const uint8_t max_quanta_per_bit = (timing->bitrate >= 1000000) ? 10 : 17;
|
|
static const int max_sp_location = 900;
|
|
|
|
/* Computing (prescaler * BS):
|
|
* BITRATE = 1 / (PRESCALER * (1 / PCLK) * (1 + BS1 + BS2))
|
|
* BITRATE = PCLK / (PRESCALER * (1 + BS1 + BS2))
|
|
* let:
|
|
* BS = 1 + BS1 + BS2
|
|
* (BS == total number of time quanta per bit)
|
|
* PRESCALER_BS = PRESCALER * BS
|
|
* ==>
|
|
* PRESCALER_BS = PCLK / BITRATE
|
|
*/
|
|
|
|
const uint32_t prescaler_bs = CLK_FREQ / target_bitrate;
|
|
|
|
/* Find prescaler value such that the number of quanta per bit is highest */
|
|
|
|
uint8_t bs1_bs2_sum = max_quanta_per_bit - 1;
|
|
|
|
while ((prescaler_bs % (1 + bs1_bs2_sum)) != 0)
|
|
{
|
|
if (bs1_bs2_sum <= 2)
|
|
{
|
|
nerr("Target bitrate too high - no solution possible.");
|
|
return 1; /* No solution */
|
|
}
|
|
|
|
bs1_bs2_sum--;
|
|
}
|
|
|
|
const uint32_t prescaler = prescaler_bs / (1 + bs1_bs2_sum);
|
|
|
|
if ((prescaler < 1U) || (prescaler > 1024U))
|
|
{
|
|
nerr("Target bitrate invalid - bad prescaler.");
|
|
return 2; /* No solution */
|
|
}
|
|
|
|
/* Now we have a constraint: (BS1 + BS2) == bs1_bs2_sum.
|
|
* We need to find the values so that the sample point is as close as
|
|
* possible to the optimal value.
|
|
*
|
|
* Solve[(1 + bs1)/(1 + bs1 + bs2) == 7/8, bs2]
|
|
* (Where 7/8 is 0.875, the recommended sample point location)
|
|
* {{bs2 -> (1 + bs1)/7}}
|
|
*
|
|
* Hence:
|
|
* bs2 = (1 + bs1) / 7
|
|
* bs1 = (7 * bs1_bs2_sum - 1) / 8
|
|
*
|
|
* Sample point location can be computed as follows:
|
|
* Sample point location = (1 + bs1) / (1 + bs1 + bs2)
|
|
*
|
|
* Since the optimal solution is so close to the maximum, we prepare two
|
|
* solutions, and then pick the best one:
|
|
* - With rounding to nearest
|
|
* - With rounding to zero
|
|
*/
|
|
|
|
/* First attempt with rounding to nearest */
|
|
|
|
uint8_t bs1 = (uint8_t)((7 * bs1_bs2_sum - 1) + 4) / 8;
|
|
uint8_t bs2 = (uint8_t)(bs1_bs2_sum - bs1);
|
|
uint16_t sample_point_permill =
|
|
(uint16_t)(1000 * (1 + bs1) / (1 + bs1 + bs2));
|
|
|
|
if (sample_point_permill > max_sp_location)
|
|
{
|
|
/* Second attempt with rounding to zero */
|
|
|
|
bs1 = (7 * bs1_bs2_sum - 1) / 8;
|
|
bs2 = bs1_bs2_sum - bs1;
|
|
}
|
|
|
|
bool valid = (bs1 >= 1) && (bs1 <= max_bs1) && (bs2 >= 1) &&
|
|
(bs2 <= max_bs2);
|
|
|
|
/* Final validation
|
|
* Helpful Python:
|
|
* def sample_point_from_btr(x):
|
|
* assert 0b0011110010000000111111000000000 & x == 0
|
|
* ts2,ts1,brp = (x>>20)&7, (x>>16)&15, x&511
|
|
* return (1+ts1+1)/(1+ts1+1+ts2+1)
|
|
*/
|
|
|
|
if (target_bitrate != (CLK_FREQ / (prescaler * (1 + bs1 + bs2))) || !valid)
|
|
{
|
|
nerr("Target bitrate invalid - solution does not match.");
|
|
return 3; /* Solution not found */
|
|
}
|
|
|
|
#ifdef CONFIG_STM32H7_FDCAN_REGDEBUG
|
|
ninfo("[fdcan] CLK_FREQ %lu, target_bitrate %lu, prescaler %lu, bs1 %d"
|
|
", bs2 %d\n", CLK_FREQ, target_bitrate, prescaler_bs, bs1 - 1,
|
|
bs2 - 1);
|
|
#endif
|
|
|
|
timing->bs1 = (uint8_t)(bs1 - 1);
|
|
timing->bs2 = (uint8_t)(bs2 - 1);
|
|
timing->prescaler = (uint16_t)(prescaler - 1);
|
|
timing->sjw = 0; /* Which means one */
|
|
|
|
return 0;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Function: fdcan_txringfull
|
|
*
|
|
* Description:
|
|
* Check if all of the TX descriptors are in use.
|
|
*
|
|
* Input Parameters:
|
|
* priv - Reference to the driver state structure
|
|
*
|
|
* Returned Value:
|
|
* true is the TX ring is full; false if there are free slots at the
|
|
* head index.
|
|
*
|
|
****************************************************************************/
|
|
|
|
static bool fdcan_txringfull(struct fdcan_driver_s *priv)
|
|
{
|
|
/* TODO: Decide if this needs to be checked every time, or just during init
|
|
* Check that we even _have_ a Tx FIFO allocated
|
|
*/
|
|
|
|
uint32_t regval = getreg32(priv->base + STM32_FDCAN_TXBC_OFFSET);
|
|
if ((regval & FDCAN_TXBC_TFQS) == 0)
|
|
{
|
|
nerr("No Tx FIFO buffers assigned? Check your message RAM config\n");
|
|
return true;
|
|
}
|
|
|
|
/* Check if the Tx queue is full */
|
|
|
|
regval = getreg32(priv->base + STM32_FDCAN_TXFQS_OFFSET);
|
|
if ((regval & FDCAN_TXFQS_TFQF) == FDCAN_TXFQS_TFQF)
|
|
{
|
|
return true; /* Sorry, out of room, try back later */
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Function: fdcan_transmit
|
|
*
|
|
* Description:
|
|
* Start hardware transmission of the data contained in priv->d_buf. Called
|
|
* either from the txdone interrupt handling or from watchdog based polling
|
|
*
|
|
* Input Parameters:
|
|
* priv - Reference to the 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_transmit(struct fdcan_driver_s *priv)
|
|
{
|
|
irqstate_t flags = enter_critical_section();
|
|
|
|
/* First, check if there are any slots available in the queue */
|
|
|
|
uint32_t regval = getreg32(priv->base + STM32_FDCAN_TXFQS_OFFSET);
|
|
if ((regval & FDCAN_TXFQS_TFQF) == FDCAN_TXFQS_TFQF)
|
|
{
|
|
/* Tx FIFO / Queue is full */
|
|
|
|
leave_critical_section(flags);
|
|
return -EBUSY;
|
|
}
|
|
|
|
/* Next, get the next available FIFO index from the controller */
|
|
|
|
regval = getreg32(priv->base + STM32_FDCAN_TXFQS_OFFSET);
|
|
const uint8_t mbi = (regval & FDCAN_TXFQS_TFQPI) >>
|
|
FDCAN_TXFQS_TFQPI_SHIFT;
|
|
|
|
/* Now, we can copy the CAN frame to the FIFO (in message RAM) */
|
|
|
|
if (mbi >= NUM_TX_FIFO)
|
|
{
|
|
nerr("Invalid Tx mailbox index encountered in transmit\n");
|
|
leave_critical_section(flags);
|
|
return -EIO;
|
|
}
|
|
|
|
struct tx_fifo_s *mb = &priv->tx[mbi];
|
|
|
|
/* Setup timeout deadline if enabled */
|
|
|
|
#ifdef CONFIG_NET_CAN_RAW_TX_DEADLINE
|
|
int32_t timeout = 0;
|
|
struct timespec ts;
|
|
clock_systime_timespec(&ts);
|
|
|
|
if (priv->dev.d_sndlen > priv->dev.d_len)
|
|
{
|
|
/* Tx deadline is stored in d_buf after frame data */
|
|
|
|
struct timeval *tv =
|
|
(struct timeval *)(priv->dev.d_buf + priv->dev.d_len);
|
|
priv->txmb[mbi].deadline = *tv;
|
|
timeout = (tv->tv_sec - ts.tv_sec)*CLK_TCK
|
|
+ ((tv->tv_usec - ts.tv_nsec / 1000)*CLK_TCK) / 1000000;
|
|
if (timeout < 0)
|
|
{
|
|
leave_critical_section(flags);
|
|
return 0; /* No transmission for you! */
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Default TX deadline defined in NET_CAN_RAW_DEFAULT_TX_DEADLINE */
|
|
|
|
if (CONFIG_NET_CAN_RAW_DEFAULT_TX_DEADLINE > 0)
|
|
{
|
|
timeout = ((CONFIG_NET_CAN_RAW_DEFAULT_TX_DEADLINE / 1000000)
|
|
*CLK_TCK);
|
|
priv->txmb[mbi].deadline.tv_sec = ts.tv_sec +
|
|
CONFIG_NET_CAN_RAW_DEFAULT_TX_DEADLINE / 1000000;
|
|
priv->txmb[mbi].deadline.tv_usec = (ts.tv_nsec / 1000) +
|
|
CONFIG_NET_CAN_RAW_DEFAULT_TX_DEADLINE % 1000000;
|
|
}
|
|
else
|
|
{
|
|
priv->txmb[mbi].deadline.tv_sec = 0;
|
|
priv->txmb[mbi].deadline.tv_usec = 0;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/* Attempt to write frame */
|
|
|
|
union tx_fifo_header_u header;
|
|
|
|
if (priv->dev.d_len == sizeof(struct can_frame))
|
|
{
|
|
struct can_frame *frame = (struct can_frame *)priv->dev.d_buf;
|
|
|
|
if (frame->can_id & CAN_EFF_FLAG)
|
|
{
|
|
header.id.xtd = 1;
|
|
header.id.extid = frame->can_id & CAN_EFF_MASK;
|
|
}
|
|
else
|
|
{
|
|
header.id.xtd = 0;
|
|
header.id.stdid = frame->can_id & CAN_SFF_MASK;
|
|
}
|
|
|
|
header.id.esi = frame->can_id & CAN_ERR_FLAG ? 1 : 0;
|
|
header.id.rtr = frame->can_id & CAN_RTR_FLAG ? 1 : 0;
|
|
header.dlc = frame->can_dlc;
|
|
header.brs = 0; /* No bitrate switching */
|
|
header.fdf = 0; /* Classic CAN frame, not CAN-FD */
|
|
header.efc = 0; /* Don't store Tx events */
|
|
header.mm = mbi; /* Mailbox Marker for our own use; just store FIFO index */
|
|
|
|
/* Store into message RAM */
|
|
|
|
mb->header.w0 = header.w0;
|
|
mb->header.w1 = header.w1;
|
|
mb->data[0].word = *(uint32_t *)&frame->data[0];
|
|
mb->data[1].word = *(uint32_t *)&frame->data[4];
|
|
}
|
|
#ifdef CONFIG_NET_CAN_CANFD
|
|
else /* CAN FD frame */
|
|
{
|
|
struct canfd_frame *frame = (struct canfd_frame *)priv->dev.d_buf;
|
|
|
|
if (frame->can_id & CAN_EFF_FLAG)
|
|
{
|
|
header.id.xtd = 1;
|
|
header.id.extid = frame->can_id & CAN_EFF_MASK;
|
|
}
|
|
else
|
|
{
|
|
header.id.xtd = 0;
|
|
header.id.stdid = frame->can_id & CAN_SFF_MASK;
|
|
}
|
|
|
|
const bool brs =
|
|
(priv->arbi_timing.bitrate == priv->data_timing.bitrate) ? 0 : 1;
|
|
|
|
header.id.esi = (frame->can_id & CAN_ERR_FLAG) ? 1 : 0;
|
|
header.id.rtr = (frame->can_id & CAN_RTR_FLAG) ? 1 : 0;
|
|
header.dlc = len_to_can_dlc[frame->len];
|
|
header.brs = brs; /* Bitrate switching */
|
|
header.fdf = 1; /* CAN-FD frame */
|
|
header.efc = 0; /* Don't store Tx events */
|
|
header.mm = mbi; /* Mailbox Marker for our own use; just store FIFO index */
|
|
|
|
/* Store into message RAM */
|
|
|
|
mb->header.w1 = header.w1;
|
|
mb->header.w0 = header.w0;
|
|
|
|
uint32_t *frame_data_word = (uint32_t *)&frame->data[0];
|
|
|
|
for (int i = 0; i < (frame->len + 4 - 1) / 4; i++)
|
|
{
|
|
mb->data[i].word = frame_data_word[i];
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/* GO - Submit the transmission request for this element */
|
|
|
|
putreg32(1 << mbi, priv->base + STM32_FDCAN_TXBAR_OFFSET);
|
|
|
|
/* Increment statistics */
|
|
|
|
NETDEV_TXPACKETS(&priv->dev);
|
|
|
|
priv->txmb[mbi].pending = TX_BUSY;
|
|
|
|
#ifdef TX_TIMEOUT_WQ
|
|
/* Setup the TX timeout watchdog (perhaps restarting the timer) */
|
|
|
|
if (timeout > 0)
|
|
{
|
|
wd_start(&priv->txmb[mbi].txtimeout, timeout + 1,
|
|
fdcan_txtimeout_expiry, (wdparm_t)priv);
|
|
}
|
|
#endif
|
|
|
|
leave_critical_section(flags);
|
|
|
|
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 fdcan_driver_s *priv =
|
|
(struct fdcan_driver_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)
|
|
{
|
|
/* Send the packet */
|
|
|
|
fdcan_transmit(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_txringfull(priv))
|
|
{
|
|
return -EBUSY;
|
|
}
|
|
}
|
|
|
|
/* If zero is returned, the polling will continue until all connections
|
|
* have been examined.
|
|
*/
|
|
|
|
return 0;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Function: fdcan_receive
|
|
*
|
|
* Description:
|
|
* An interrupt was received indicating the availability of a new RX packet
|
|
* Schedule the message receipt and socket notification
|
|
*
|
|
* Input Parameters:
|
|
* priv - Reference to the driver state structure
|
|
*
|
|
****************************************************************************/
|
|
|
|
static void fdcan_receive(struct fdcan_driver_s *priv)
|
|
{
|
|
/* Check the interrupt value to determine which FIFO to read */
|
|
|
|
uint32_t regval = getreg32(priv->base + STM32_FDCAN_IR_OFFSET);
|
|
|
|
const uint32_t ir_fifo0 = FDCAN_IR_RF0N | FDCAN_IR_RF0F;
|
|
const uint32_t ir_fifo1 = FDCAN_IR_RF1N | FDCAN_IR_RF1F;
|
|
|
|
if (regval & ir_fifo0)
|
|
{
|
|
regval = ir_fifo0;
|
|
}
|
|
else if (regval & ir_fifo1)
|
|
{
|
|
regval = ir_fifo1;
|
|
}
|
|
else
|
|
{
|
|
nerr("ERROR: Bad RX IR flags");
|
|
return;
|
|
}
|
|
|
|
/* Store the Rx FIFO IR flags for use in the deferred work function */
|
|
|
|
priv->irflags = regval;
|
|
|
|
/* Write the corresponding interrupt bits to reset these interrupts */
|
|
|
|
putreg32(regval, priv->base + STM32_FDCAN_IR_OFFSET);
|
|
|
|
/* Schedule the actual Rx work immediately from HPWORK context */
|
|
|
|
work_queue(CANWORK, &priv->rxwork, fdcan_receive_work, priv, 0);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Function: fdcan_receive_work
|
|
*
|
|
* Description:
|
|
* An frame was received; read the frame into the intermediate rx_pool and
|
|
* notify the upper-half driver.
|
|
* While we're here, also check for errors and timeouts.
|
|
*
|
|
* Input Parameters:
|
|
* priv - Reference to the driver state structure
|
|
*
|
|
* Returned Value:
|
|
* None
|
|
*
|
|
* Assumptions:
|
|
* Scheduled in worker thread (HPWORK / LPWORK)
|
|
*
|
|
****************************************************************************/
|
|
|
|
static void fdcan_receive_work(void *arg)
|
|
{
|
|
irqstate_t flags = enter_critical_section();
|
|
|
|
struct fdcan_driver_s *priv = (struct fdcan_driver_s *)arg;
|
|
|
|
/* Check which FIFO triggered this work */
|
|
|
|
uint32_t irflags = priv->irflags;
|
|
|
|
const uint32_t ir_fifo0 = FDCAN_IR_RF0N | FDCAN_IR_RF0F;
|
|
const uint32_t ir_fifo1 = FDCAN_IR_RF1N | FDCAN_IR_RF1F;
|
|
uint8_t fifo_id;
|
|
|
|
if (irflags & ir_fifo0)
|
|
{
|
|
fifo_id = 0;
|
|
}
|
|
else if (irflags & ir_fifo1)
|
|
{
|
|
fifo_id = 1;
|
|
}
|
|
else
|
|
{
|
|
nerr("ERROR: Bad RX IR flags");
|
|
leave_critical_section(flags);
|
|
return;
|
|
}
|
|
|
|
/* Bitwise register definitions are the same for FIFO 0/1
|
|
*
|
|
* FDCAN_RXFnC_F0S: Rx FIFO Size
|
|
* FDCAN_RXFnS_RF0L: Rx Message Lost
|
|
* FDCAN_RXFnS_F0FL: Rx FIFO Fill Level
|
|
* FDCAN_RXFnS_F0GI: Rx FIFO Get Index
|
|
*
|
|
* So we will use only the RX FIFO0 register definitions for simplicity
|
|
*/
|
|
|
|
uint32_t offset_rxfnc = (fifo_id == 0) ? STM32_FDCAN_RXF0C_OFFSET
|
|
: STM32_FDCAN_RXF1C_OFFSET;
|
|
uint32_t offset_rxfns = (fifo_id == 0) ? STM32_FDCAN_RXF0S_OFFSET
|
|
: STM32_FDCAN_RXF1S_OFFSET;
|
|
uint32_t offset_rxfna = (fifo_id == 0) ? STM32_FDCAN_RXF0A_OFFSET
|
|
: STM32_FDCAN_RXF1A_OFFSET;
|
|
|
|
volatile uint32_t *const rxfnc = (uint32_t *)(priv->base + offset_rxfnc);
|
|
volatile uint32_t *const rxfns = (uint32_t *)(priv->base + offset_rxfns);
|
|
volatile uint32_t *const rxfna = (uint32_t *)(priv->base + offset_rxfna);
|
|
|
|
/* Check number of elements in message RAM allocated to this FIFO */
|
|
|
|
if ((*rxfnc & FDCAN_RXF0C_F0S) == 0)
|
|
{
|
|
nerr("ERROR: No RX FIFO elements allocated");
|
|
leave_critical_section(flags);
|
|
return;
|
|
}
|
|
|
|
/* Check for message lost; count an error */
|
|
|
|
if ((*rxfns & FDCAN_RXF0S_RF0L) != 0)
|
|
{
|
|
NETDEV_RXERRORS(&priv->dev);
|
|
}
|
|
|
|
/* Check number of elements available (fill level) */
|
|
|
|
const uint8_t n_elem = (*rxfns & FDCAN_RXF0S_F0FL);
|
|
|
|
if (n_elem == 0)
|
|
{
|
|
nerr("RX interrupt but 0 frames available");
|
|
leave_critical_section(flags);
|
|
return;
|
|
}
|
|
|
|
struct rx_fifo_s *rf = NULL;
|
|
|
|
while ((*rxfns & FDCAN_RXF0S_F0FL) > 0)
|
|
{
|
|
/* Copy the frame from message RAM */
|
|
|
|
const uint8_t index = (*rxfns & FDCAN_RXF0S_F0GI) >>
|
|
FDCAN_RXF0S_F0GI_SHIFT;
|
|
|
|
rf = &priv->rx[index];
|
|
|
|
/* Read the frame contents */
|
|
|
|
#ifdef CONFIG_NET_CAN_CANFD
|
|
if (rf->header.fdf) /* CAN FD frame */
|
|
{
|
|
struct canfd_frame *frame = (struct canfd_frame *)priv->rx_pool;
|
|
|
|
if (rf->header.id.xtd)
|
|
{
|
|
frame->can_id = CAN_EFF_MASK & rf->header.id.extid;
|
|
frame->can_id |= CAN_EFF_FLAG;
|
|
}
|
|
else
|
|
{
|
|
frame->can_id = CAN_SFF_MASK & rf->header.id.stdid;
|
|
}
|
|
|
|
if (rf->header.id.rtr)
|
|
{
|
|
frame->can_id |= CAN_RTR_FLAG;
|
|
}
|
|
|
|
frame->len = can_dlc_to_len[rf->header.dlc];
|
|
|
|
uint32_t *frame_data_word = (uint32_t *)&frame->data[0];
|
|
|
|
for (int i = 0; i < (frame->len + 4 - 1) / 4; i++)
|
|
{
|
|
frame_data_word[i] = rf->data[i].word;
|
|
}
|
|
|
|
/* Acknowledge receipt of this FIFO element */
|
|
|
|
putreg32(index, rxfna);
|
|
|
|
/* 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 /* CAN 2.0 Frame */
|
|
#endif
|
|
{
|
|
struct can_frame *frame = (struct can_frame *)priv->rx_pool;
|
|
|
|
if (rf->header.id.xtd)
|
|
{
|
|
frame->can_id = CAN_EFF_MASK & rf->header.id.extid;
|
|
frame->can_id |= CAN_EFF_FLAG;
|
|
}
|
|
else
|
|
{
|
|
frame->can_id = CAN_SFF_MASK & rf->header.id.stdid;
|
|
}
|
|
|
|
if (rf->header.id.rtr)
|
|
{
|
|
frame->can_id |= CAN_RTR_FLAG;
|
|
}
|
|
|
|
frame->can_dlc = rf->header.dlc;
|
|
|
|
*(uint32_t *)&frame->data[0] = rf->data[0].word;
|
|
*(uint32_t *)&frame->data[4] = rf->data[1].word;
|
|
|
|
/* Acknowledge receipt of this FIFO element */
|
|
|
|
putreg32(index, rxfna);
|
|
|
|
/* 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 */
|
|
|
|
can_input(&priv->dev);
|
|
|
|
/* Update iface statistics */
|
|
|
|
NETDEV_RXPACKETS(&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 = priv->tx_pool;
|
|
}
|
|
|
|
/* Check for errors and abort-transmission requests */
|
|
|
|
fdcan_check_errors(priv);
|
|
|
|
leave_critical_section(flags);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Function: fdcan_txdone
|
|
*
|
|
* Description:
|
|
* An interrupt was received indicating that the last TX packet(s) is done
|
|
*
|
|
* Input Parameters:
|
|
* priv - Reference to the driver state structure
|
|
*
|
|
* Returned Value:
|
|
* None
|
|
*
|
|
* Assumptions:
|
|
* Called from interrupt context
|
|
*
|
|
****************************************************************************/
|
|
|
|
static void fdcan_txdone(struct fdcan_driver_s *priv)
|
|
{
|
|
/* Read and reset the interrupt flag */
|
|
|
|
uint32_t ir = getreg32(priv->base + STM32_FDCAN_IR_OFFSET);
|
|
if (ir & FDCAN_IR_TC)
|
|
{
|
|
putreg32(FDCAN_IR_TC, priv->base + STM32_FDCAN_IR_OFFSET);
|
|
}
|
|
else
|
|
{
|
|
nerr("Unexpected FCAN interrupt on line 1\n");
|
|
return;
|
|
}
|
|
|
|
/* Schedule to perform the TX timeout processing on the worker thread */
|
|
|
|
work_queue(CANWORK, &priv->txdwork, fdcan_txdone_work, priv, 0);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Function: fdcan_txdone_work
|
|
*
|
|
* Description:
|
|
* Process completed transmissions, including canceling their watchdog
|
|
* timers if applicable
|
|
*
|
|
* Input Parameters:
|
|
* priv - Reference to the driver state structure
|
|
*
|
|
* Returned Value:
|
|
* None
|
|
*
|
|
* Assumptions:
|
|
* Scheduled in worker thread (HPWORK / LPWORK)
|
|
*
|
|
****************************************************************************/
|
|
|
|
static void fdcan_txdone_work(void *arg)
|
|
{
|
|
irqstate_t flags = enter_critical_section();
|
|
|
|
struct fdcan_driver_s *priv = (struct fdcan_driver_s *)arg;
|
|
|
|
/* Update counters for successful transmissions */
|
|
|
|
for (uint8_t i = 0; i < NUM_TX_FIFO; i++)
|
|
{
|
|
if ((getreg32(priv->base + STM32_FDCAN_TXBTO_OFFSET) & (1 << i)) > 0)
|
|
{
|
|
/* Transmission Occurred in buffer i
|
|
* (Not necessarily a 'new' transmission, however)
|
|
* Check that it's a new transmission, not a previously handled
|
|
* transmission
|
|
*/
|
|
|
|
struct txmbstats *txi = &priv->txmb[i];
|
|
|
|
if (txi->pending == TX_BUSY)
|
|
{
|
|
/* This is a transmission that just now completed */
|
|
|
|
NETDEV_TXDONE(&priv->dev);
|
|
|
|
txi->pending = TX_FREE;
|
|
|
|
#ifdef TX_TIMEOUT_WQ
|
|
/* We are here because a transmission completed, so the
|
|
* corresponding watchdog can be canceled.
|
|
*/
|
|
|
|
wd_cancel(&priv->txmb[i].txtimeout);
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Check for errors and abort-transmission requests */
|
|
|
|
fdcan_check_errors(priv);
|
|
|
|
/* There should be space for a new TX in any event
|
|
* Poll the network for new data to transmit
|
|
*/
|
|
|
|
devif_poll(&priv->dev, fdcan_txpoll);
|
|
|
|
leave_critical_section(flags);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Function: fdcan_interrupt
|
|
*
|
|
* Description:
|
|
* Common handler for all enabled FDCAN interrupts
|
|
*
|
|
* Input Parameters:
|
|
* irq - Number of the IRQ that generated the interrupt
|
|
* context - Interrupt register state save info (architecture-specific)
|
|
* arg - Unused
|
|
*
|
|
* Returned Value:
|
|
* Zero on success; a negated errno on failure
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int fdcan_interrupt(int irq, void *context,
|
|
void *arg)
|
|
{
|
|
switch (irq)
|
|
{
|
|
#ifdef CONFIG_STM32H7_FDCAN1
|
|
case STM32_IRQ_FDCAN1_0:
|
|
fdcan_receive(&g_fdcan0);
|
|
break;
|
|
|
|
case STM32_IRQ_FDCAN1_1:
|
|
fdcan_txdone(&g_fdcan0);
|
|
break;
|
|
#endif
|
|
|
|
#ifdef CONFIG_STM32H7_FDCAN2
|
|
case STM32_IRQ_FDCAN2_0:
|
|
fdcan_receive(&g_fdcan1);
|
|
break;
|
|
|
|
case STM32_IRQ_FDCAN2_1:
|
|
fdcan_txdone(&g_fdcan1);
|
|
break;
|
|
#endif
|
|
|
|
#ifdef CONFIG_STM32H7_FDCAN3
|
|
case STM32_IRQ_FDCAN3_0:
|
|
fdcan_receive(&g_fdcan2);
|
|
break;
|
|
|
|
case STM32_IRQ_FDCAN3_1:
|
|
fdcan_txdone(&g_fdcan2);
|
|
break;
|
|
#endif
|
|
|
|
default:
|
|
nerr("Unexpected IRQ [%d]\n", irq);
|
|
return -EINVAL;
|
|
}
|
|
|
|
return OK;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Function: fdcan_check_errors
|
|
*
|
|
* Description:
|
|
* Check error flags and cancel any timed out transmissions
|
|
*
|
|
* Input Parameters:
|
|
* priv - Reference to the driver state structure
|
|
*
|
|
* 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 void fdcan_check_errors(struct fdcan_driver_s *priv)
|
|
{
|
|
/* Read CAN Error Logging counter (This also resets the error counter) */
|
|
|
|
uint32_t regval = getreg32(priv->base + STM32_FDCAN_ECR_OFFSET)
|
|
& FDCAN_ECR_CEL;
|
|
const uint8_t cel = (uint8_t)(regval >> FDCAN_ECR_CEL_SHIFT);
|
|
|
|
if (cel > 0)
|
|
{
|
|
/* We've had some errors; check the status of the device */
|
|
|
|
regval = getreg32(priv->base + STM32_FDCAN_CCCR_OFFSET);
|
|
bool restricted_op_mode = (regval & FDCAN_CCCR_ASM) > 0;
|
|
|
|
if (restricted_op_mode)
|
|
{
|
|
nerr("Tx handler message RAM error -- resctricted mode enabled\n");
|
|
|
|
/* Reset the CCCR.ASM register to exit restricted op mode */
|
|
|
|
putreg32(regval & ~FDCAN_CCCR_ASM,
|
|
priv->base + STM32_FDCAN_CCCR_OFFSET);
|
|
}
|
|
}
|
|
|
|
/* Serve abort requests */
|
|
|
|
for (uint8_t i = 0; i < NUM_TX_FIFO; i++)
|
|
{
|
|
struct txmbstats *txi = &priv->txmb[i];
|
|
|
|
regval = getreg32(priv->base + STM32_FDCAN_TXBRP_OFFSET);
|
|
if (txi->pending == TX_ABORT && ((1 << i) & regval))
|
|
{
|
|
/* Request to Cancel Tx item */
|
|
|
|
putreg32(1 << i, priv->base + STM32_FDCAN_TXBCR_OFFSET);
|
|
txi->pending = TX_FREE;
|
|
NETDEV_TXERRORS(&priv->dev);
|
|
#ifdef TX_TIMEOUT_WQ
|
|
wd_cancel(&priv->txmb[i].txtimeout);
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef TX_TIMEOUT_WQ
|
|
/****************************************************************************
|
|
* Function: fdcan_txtimeout_work
|
|
*
|
|
* Description:
|
|
* Perform TX timeout related work from the worker thread
|
|
*
|
|
* Input Parameters:
|
|
* arg - The argument passed when work_queue() as called.
|
|
*
|
|
* Assumptions:
|
|
*
|
|
****************************************************************************/
|
|
|
|
static void fdcan_txtimeout_work(void *arg)
|
|
{
|
|
struct fdcan_driver_s *priv = (struct fdcan_driver_s *)arg;
|
|
|
|
struct timespec ts;
|
|
struct timeval *now = (struct timeval *)&ts;
|
|
clock_systime_timespec(&ts);
|
|
now->tv_usec = ts.tv_nsec / 1000; /* timespec to timeval conversion */
|
|
|
|
/* The watchdog timed out, yet we still check mailboxes in case the
|
|
* transmit function transmitted a new frame
|
|
*/
|
|
|
|
for (int mbi = 0; mbi < NUM_TX_FIFO; mbi++)
|
|
{
|
|
if (priv->txmb[mbi].deadline.tv_sec != 0
|
|
&& (now->tv_sec > priv->txmb[mbi].deadline.tv_sec
|
|
|| now->tv_usec > priv->txmb[mbi].deadline.tv_usec))
|
|
{
|
|
NETDEV_TXTIMEOUTS(&priv->dev);
|
|
priv->txmb[mbi].pending = TX_ABORT;
|
|
}
|
|
}
|
|
|
|
fdcan_check_errors(priv);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Function: fdcan_txtimeout_expiry
|
|
*
|
|
* Description:
|
|
* Our TX watchdog timed out. Called from the timer interrupt handler.
|
|
*
|
|
* Input Parameters:
|
|
* arg - Pointer to the private FDCAN driver state structure
|
|
*
|
|
* Assumptions:
|
|
* Global interrupts are disabled by the watchdog logic.
|
|
*
|
|
****************************************************************************/
|
|
|
|
static void fdcan_txtimeout_expiry(wdparm_t arg)
|
|
{
|
|
struct fdcan_driver_s *priv = (struct fdcan_driver_s *)arg;
|
|
|
|
/* Schedule to perform the TX timeout processing on the worker thread */
|
|
|
|
work_queue(CANWORK, &priv->txcwork, fdcan_txtimeout_work, priv, 0);
|
|
}
|
|
#endif
|
|
|
|
/****************************************************************************
|
|
* Function: fdcan_apb1hreset
|
|
*
|
|
* Description:
|
|
* Reset the periheral bus clock used by FDCAN
|
|
* Note that this will reset all configuration of all FDCAN peripherals
|
|
*
|
|
****************************************************************************/
|
|
|
|
static void fdcan_apb1hreset(void)
|
|
{
|
|
/* Reset the FDCAN's peripheral bus clock */
|
|
|
|
modifyreg32(STM32_RCC_APB1HRSTR, 0, RCC_APB1HRSTR_FDCANRST);
|
|
modifyreg32(STM32_RCC_APB1HRSTR, RCC_APB1HRSTR_FDCANRST, 0);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Function: fdcan_setinit
|
|
*
|
|
* Description:
|
|
* Enter / Exit initialization mode
|
|
*
|
|
* Input Parameters:
|
|
* base - The base pointer of the FDCAN peripheral
|
|
* init - true: Enter init mode; false: Exit init mode
|
|
*
|
|
****************************************************************************/
|
|
|
|
static void fdcan_setinit(uint32_t base, uint32_t init)
|
|
{
|
|
if (init)
|
|
{
|
|
/* Enter hardware initialization mode */
|
|
|
|
modifyreg32(base + STM32_FDCAN_CCCR_OFFSET, 0, FDCAN_CCCR_INIT);
|
|
fdcan_waitccr_change(base, FDCAN_CCCR_INIT, FDCAN_CCCR_INIT);
|
|
}
|
|
else
|
|
{
|
|
/* Exit hardware initialization mode */
|
|
|
|
modifyreg32(base + STM32_FDCAN_CCCR_OFFSET, FDCAN_CCCR_INIT, 0);
|
|
fdcan_waitccr_change(base, FDCAN_CCCR_INIT, 0);
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Function: fdcan_setenable
|
|
*
|
|
* Description:
|
|
* Power On / Power Off the device with the Clock Stop Request bit
|
|
*
|
|
* Input Parameters:
|
|
* base - The base pointer of the FDCAN peripheral
|
|
* init - true: Power on the device; false: Power off the device
|
|
*
|
|
****************************************************************************/
|
|
|
|
static void fdcan_setenable(uint32_t base, uint32_t enable)
|
|
{
|
|
if (enable)
|
|
{
|
|
/* Clear CSR bit */
|
|
|
|
modifyreg32(base + STM32_FDCAN_CCCR_OFFSET, FDCAN_CCCR_CSR, 0);
|
|
fdcan_waitccr_change(base, FDCAN_CCCR_CSA, 0);
|
|
}
|
|
else
|
|
{
|
|
/* Set CSR bit */
|
|
|
|
modifyreg32(base + STM32_FDCAN_CCCR_OFFSET, 0, FDCAN_CCCR_CSR);
|
|
fdcan_waitccr_change(base, FDCAN_CCCR_CSA, 1);
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Function: fdcan_setconfig
|
|
*
|
|
* Description:
|
|
* Enter / Exit Configuration Changes Enabled mode
|
|
*
|
|
* Input Parameters:
|
|
* base - The base pointer of the FDCAN peripheral
|
|
* init - true: Enter config mode; false: Exit config mode
|
|
*
|
|
****************************************************************************/
|
|
|
|
static void fdcan_setconfig(uint32_t base, uint32_t config_enable)
|
|
{
|
|
if (config_enable)
|
|
{
|
|
/* Configuration Changes Enabled (CCE) mode */
|
|
|
|
modifyreg32(base + STM32_FDCAN_CCCR_OFFSET, 0, FDCAN_CCCR_CCE);
|
|
fdcan_waitccr_change(base, FDCAN_CCCR_CCE, 1);
|
|
}
|
|
else
|
|
{
|
|
/* Exit CCE mode */
|
|
|
|
modifyreg32(base + STM32_FDCAN_CCCR_OFFSET, FDCAN_CCCR_CCE, 0);
|
|
fdcan_waitccr_change(base, FDCAN_CCCR_CCE, 0);
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Function: fdcan_waitccr_change
|
|
*
|
|
* Description:
|
|
* Wait for the CCR register to accept a requested change.
|
|
* Timeout after ~10ms.
|
|
*
|
|
* Input Parameters:
|
|
* base - The base pointer of the FDCAN peripheral
|
|
* mask - Mask to apply to the CCR register value
|
|
* target_state - Target masked value to wait for
|
|
*
|
|
* Returned Value:
|
|
* true on success; false on timeout
|
|
*
|
|
****************************************************************************/
|
|
|
|
static bool fdcan_waitccr_change(uint32_t base, uint32_t mask,
|
|
uint32_t target_state)
|
|
{
|
|
const unsigned timeout = 1000;
|
|
for (unsigned wait_ack = 0; wait_ack < timeout; wait_ack++)
|
|
{
|
|
const bool state = (getreg32(base + STM32_FDCAN_CCCR_OFFSET) & mask);
|
|
if (state == target_state)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
up_udelay(10);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Function: fdcan_enable_interrupts
|
|
*
|
|
* Description:
|
|
* Enable all interrupts used by this driver
|
|
*
|
|
* Input Parameters:
|
|
* priv - Pointer to the private FDCAN driver state structure
|
|
*
|
|
* Assumptions:
|
|
* The peripheral is in Configuration Changes Enabled (CCE) mode
|
|
*
|
|
****************************************************************************/
|
|
|
|
static void fdcan_enable_interrupts(struct fdcan_driver_s *priv)
|
|
{
|
|
/* Enable both interrupt lines at the device level */
|
|
|
|
const uint32_t ile = FDCAN_ILE_EINT0 | FDCAN_ILE_EINT1;
|
|
modifyreg32(priv->base + STM32_FDCAN_ILE_OFFSET, 0, ile);
|
|
|
|
/* Enable both lines at the NVIC level */
|
|
|
|
up_enable_irq(priv->config->mb_irq[0]);
|
|
up_enable_irq(priv->config->mb_irq[1]);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Function: fdcan_disable_interrupts
|
|
*
|
|
* Description:
|
|
* Disable all interrupts used by this driver
|
|
*
|
|
* Input Parameters:
|
|
* priv - Pointer to the private FDCAN driver state structure
|
|
*
|
|
* Assumptions:
|
|
* The peripheral is in Configuration Changes Enabled (CCE) mode
|
|
*
|
|
****************************************************************************/
|
|
|
|
static void fdcan_disable_interrupts(struct fdcan_driver_s *priv)
|
|
{
|
|
/* Disable both lines at the NVIC level */
|
|
|
|
up_disable_irq(priv->config->mb_irq[0]);
|
|
up_disable_irq(priv->config->mb_irq[1]);
|
|
|
|
/* Disable both interrupt lines at the device level */
|
|
|
|
const uint32_t ile = FDCAN_ILE_EINT0 | FDCAN_ILE_EINT1;
|
|
modifyreg32(priv->base + STM32_FDCAN_ILE_OFFSET, ile, 0);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Function: fdcan_ifup
|
|
*
|
|
* Description:
|
|
* NuttX Callback: Bring up the CAN interface when a socket is opened
|
|
*
|
|
* Input Parameters:
|
|
* dev - Reference to the NuttX driver state structure
|
|
*
|
|
* Returned Value:
|
|
* None
|
|
*
|
|
* Assumptions:
|
|
* The device is initialized and waiting to be brought online
|
|
****************************************************************************/
|
|
|
|
static int fdcan_ifup(struct net_driver_s *dev)
|
|
{
|
|
struct fdcan_driver_s *priv =
|
|
(struct fdcan_driver_s *)dev->d_private;
|
|
|
|
/* Wake up the device and perform all initialization */
|
|
|
|
irqstate_t flags = enter_critical_section();
|
|
|
|
fdcan_initialize(priv);
|
|
|
|
fdcan_setinit(priv->base, 1);
|
|
fdcan_setconfig(priv->base, 1);
|
|
|
|
/* Enable interrupts (at both device and NVIC level) */
|
|
|
|
fdcan_enable_interrupts(priv);
|
|
|
|
/* Leave init mode */
|
|
|
|
fdcan_setinit(priv->base, 0);
|
|
|
|
#ifdef CONFIG_STM32H7_FDCAN_REGDEBUG
|
|
fdcan_dumpregs(priv);
|
|
#endif
|
|
|
|
leave_critical_section(flags);
|
|
|
|
priv->bifup = true;
|
|
|
|
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 fdcan_driver_s *priv =
|
|
(struct fdcan_driver_s *)dev->d_private;
|
|
|
|
fdcan_reset(priv);
|
|
|
|
priv->bifup = false;
|
|
|
|
return OK;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* 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 fdcan_driver_s *priv = (struct fdcan_driver_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_txringfull(priv))
|
|
{
|
|
/* 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 to 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 fdcan_driver_s *priv =
|
|
(struct fdcan_driver_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_netdev_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 fdcan_driver_s *priv = dev->d_private;
|
|
|
|
int ret;
|
|
|
|
switch (cmd)
|
|
{
|
|
#ifdef CONFIG_NETDEV_CAN_BITRATE_IOCTL
|
|
case SIOCGCANBITRATE: /* Get bitrate from a CAN controller */
|
|
{
|
|
struct can_ioctl_data_s *req =
|
|
(struct can_ioctl_data_s *)((uintptr_t)arg);
|
|
req->arbi_bitrate = priv->arbi_timing.bitrate / 1000; /* kbit/s */
|
|
#ifdef CONFIG_NET_CAN_CANFD
|
|
req->data_bitrate = priv->data_timing.bitrate / 1000; /* kbit/s */
|
|
#else
|
|
req->data_bitrate = 0;
|
|
#endif
|
|
ret = OK;
|
|
}
|
|
break;
|
|
|
|
case SIOCSCANBITRATE: /* Set bitrate of a CAN controller */
|
|
{
|
|
struct can_ioctl_data_s *req =
|
|
(struct can_ioctl_data_s *)((uintptr_t)arg);
|
|
|
|
priv->arbi_timing.bitrate = req->arbi_bitrate * 1000;
|
|
#ifdef CONFIG_NET_CAN_CANFD
|
|
priv->data_timing.bitrate = req->data_bitrate * 1000;
|
|
#endif
|
|
|
|
/* Reset CAN controller and start with new timings */
|
|
|
|
ret = fdcan_initialize(priv);
|
|
|
|
if (ret == OK)
|
|
{
|
|
ret = fdcan_ifup(dev);
|
|
}
|
|
}
|
|
break;
|
|
#endif /* CONFIG_NETDEV_CAN_BITRATE_IOCTL */
|
|
|
|
#ifdef CONFIG_NETDEV_CAN_FILTER_IOCTL
|
|
case SIOCACANEXTFILTER:
|
|
{
|
|
/* TODO: Add hardware-level filter... */
|
|
|
|
stm32_addextfilter(priv, (struct canioc_extfilter_s *)arg);
|
|
}
|
|
break;
|
|
|
|
case SIOCDCANEXTFILTER:
|
|
{
|
|
/* TODO: Delete hardware-level filter... */
|
|
|
|
stm32_delextfilter(priv, (struct canioc_extfilter_s *)arg);
|
|
}
|
|
break;
|
|
|
|
case SIOCACANSTDFILTER:
|
|
{
|
|
/* TODO: Add hardware-level filter... */
|
|
|
|
stm32_addstdfilter(priv, (struct canioc_stdfilter_s *)arg);
|
|
}
|
|
break;
|
|
|
|
case SIOCDCANSTDFILTER:
|
|
{
|
|
/* TODO: Delete hardware-level filter... */
|
|
|
|
stm32_delstdfilter(priv, (struct canioc_stdfilter_s *)arg);
|
|
}
|
|
break;
|
|
#endif
|
|
|
|
default:
|
|
ret = -ENOTSUP;
|
|
break;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
#endif /* CONFIG_NETDEV_IOCTL */
|
|
|
|
/****************************************************************************
|
|
* Function: fdcan_initialize
|
|
*
|
|
* Description:
|
|
* Initialize FDCAN device
|
|
*
|
|
* Input Parameters:
|
|
* priv - Pointer to the private FDCAN driver state structure
|
|
*
|
|
* Returned Value:
|
|
* OK on success; Negated errno on failure.
|
|
*
|
|
* Assumptions:
|
|
*
|
|
****************************************************************************/
|
|
|
|
int fdcan_initialize(struct fdcan_driver_s *priv)
|
|
{
|
|
uint32_t regval;
|
|
|
|
irqstate_t flags = enter_critical_section();
|
|
|
|
/* Reset the peripheral clock bus (only do this once) */
|
|
|
|
if (!g_apb1h_init)
|
|
{
|
|
fdcan_apb1hreset();
|
|
g_apb1h_init = true;
|
|
}
|
|
|
|
/* Exit Power-down / Sleep mode */
|
|
|
|
fdcan_setenable(priv->base, 1);
|
|
|
|
/* Enter Initialization mode */
|
|
|
|
fdcan_setinit(priv->base, 1);
|
|
|
|
/* Enter Configuration Changes Enabled mode */
|
|
|
|
fdcan_setconfig(priv->base, 1);
|
|
|
|
/* Disable interrupts while we configure the hardware */
|
|
|
|
putreg32(0, priv->base + STM32_FDCAN_IE_OFFSET);
|
|
|
|
/* Compute CAN bit timings for this bitrate */
|
|
|
|
/* Nominal / arbitration phase bitrate */
|
|
|
|
if (fdcan_bittiming(&priv->arbi_timing) != OK)
|
|
{
|
|
fdcan_setinit(priv->base, 0);
|
|
leave_critical_section(flags);
|
|
return -EIO;
|
|
}
|
|
|
|
#ifdef CONFIG_STM32H7_FDCAN_REGDEBUG
|
|
const fdcan_bitseg *tim = &priv->arbi_timing;
|
|
ninfo("[fdcan][arbi] Timings: presc=%u sjw=%u bs1=%u bs2=%u\r\n",
|
|
tim->prescaler, tim->sjw, tim->bs1, tim->bs2);
|
|
#endif
|
|
|
|
/* Set bit timings and prescalers (Nominal bitrate) */
|
|
|
|
regval = ((priv->arbi_timing.sjw << FDCAN_NBTP_NSJW_SHIFT) |
|
|
(priv->arbi_timing.bs1 << FDCAN_NBTP_NTSEG1_SHIFT) |
|
|
(priv->arbi_timing.bs2 << FDCAN_NBTP_TSEG2_SHIFT) |
|
|
(priv->arbi_timing.prescaler << FDCAN_NBTP_NBRP_SHIFT));
|
|
putreg32(regval, priv->base + STM32_FDCAN_NBTP_OFFSET);
|
|
|
|
#ifdef CONFIG_NET_CAN_CANFD
|
|
/* CAN-FD Data phase bitrate */
|
|
|
|
if (fdcan_bittiming(&priv->data_timing) != OK)
|
|
{
|
|
fdcan_setinit(priv->base, 0);
|
|
leave_critical_section(flags);
|
|
return -EIO;
|
|
}
|
|
|
|
#ifdef CONFIG_STM32H7_FDCAN_REGDEBUG
|
|
const fdcan_bitseg *tim = &priv->data_timing;
|
|
ninfo("[fdcan][data] Timings: presc=%u sjw=%u bs1=%u bs2=%u\r\n",
|
|
tim->prescaler, tim->sjw, tim->bs1, tim->bs2);
|
|
#endif
|
|
|
|
/* Set bit timings and prescalers (Data bitrate) */
|
|
|
|
regval = ((priv->data_timing.sjw << FDCAN_DBTP_DSJW_SHIFT) |
|
|
(priv->data_timing.bs1 << FDCAN_DBTP_DTSEG1_SHIFT) |
|
|
(priv->data_timing.bs2 << FDCAN_DBTP_DTSEG2_SHIFT) |
|
|
(priv->data_timing.prescaler << FDCAN_DBTP_DBRP_SHIFT));
|
|
#endif /* CONFIG_NET_CAN_CANFD */
|
|
|
|
/* Be sure to fill data-phase register even if we're not using CAN FD */
|
|
|
|
putreg32(regval, priv->base + STM32_FDCAN_DBTP_OFFSET);
|
|
|
|
/* Operation Configuration */
|
|
|
|
#ifdef CONFIG_STM32H7_FDCAN_LOOPBACK
|
|
/* Enable External Loopback Mode (Rx pin disconnected) (RM0433 pg 2494) */
|
|
|
|
modifyreg32(priv->base + STM32_FDCAN_CCCR_OFFSET, 0, FDCAN_CCCR_TEST);
|
|
modifyreg32(priv->base + STM32_FDCAN_TEST_OFFSET, 0, FDCAN_TEST_LBCK);
|
|
#endif
|
|
|
|
#ifdef CONFIG_STM32H7_FDCAN_LOOPBACK_INTERNAL
|
|
/* Enable Bus Monitoring / Restricted Op Mode (RM0433 pg 2492, 2494) */
|
|
|
|
modifyreg32(priv->base + STM32_FDCAN_CCCR_OFFSET, 0, FDCAN_CCCR_MON);
|
|
#endif
|
|
|
|
#ifdef CONFIG_NET_CAN_CANFD
|
|
/* Enable CAN-FD frames, including bitrate switching if needed */
|
|
|
|
modifyreg32(priv->base + STM32_FDCAN_CCCR_OFFSET, 0, FDCAN_CCCR_FDOE);
|
|
if (priv->arbi_timing.bitrate != priv->data_timing.bitrate)
|
|
{
|
|
modifyreg32(priv->base + STM32_FDCAN_CCCR_OFFSET, 0, FDCAN_CCCR_BRSE);
|
|
}
|
|
#else
|
|
/* Disable CAN-FD communications ("classic" CAN only) */
|
|
|
|
modifyreg32(priv->base + STM32_FDCAN_CCCR_OFFSET, FDCAN_CCCR_FDOE, 0);
|
|
#endif
|
|
|
|
#if 0
|
|
/* Disable Automatic Retransmission of frames upon error
|
|
* NOTE: This will even disable automatic retry due to lost arbitration!!
|
|
*/
|
|
|
|
modifyreg32(priv->base + STM32_FDCAN_CCCR_OFFSET, 0, FDCAN_CCCR_DAR);
|
|
#endif
|
|
|
|
/* Configure Interrupts */
|
|
|
|
/* Clear all interrupt flags
|
|
* Note: A flag is cleared by writing a 1 to the corresponding bit position
|
|
*/
|
|
|
|
putreg32(FDCAN_IR_MASK, priv->base + STM32_FDCAN_IR_OFFSET);
|
|
|
|
/* Enable relevant interrupts */
|
|
|
|
regval = FDCAN_IE_TCE /* Transmit Complete */
|
|
| FDCAN_IE_RF0NE /* Rx FIFO 0 new message */
|
|
| FDCAN_IE_RF0FE /* Rx FIFO 0 FIFO full */
|
|
| FDCAN_IE_RF1NE /* Rx FIFO 1 new message */
|
|
| FDCAN_IE_RF1FE; /* Rx FIFO 1 FIFO full */
|
|
putreg32(regval, priv->base + STM32_FDCAN_IE_OFFSET);
|
|
|
|
/* Keep Rx interrupts on Line 0; move Tx to Line 1
|
|
* TC (Tx Complete) interrupt on line 1
|
|
*/
|
|
|
|
regval = getreg32(priv->base + STM32_FDCAN_ILS_OFFSET);
|
|
regval |= FDCAN_ILS_TCL;
|
|
putreg32(FDCAN_ILS_TCL, priv->base + STM32_FDCAN_ILS_OFFSET);
|
|
|
|
/* Enable Tx buffer transmission interrupts
|
|
* Note: Still need fdcan_enable_interrupts() to set ILE (IR line enable)
|
|
*/
|
|
|
|
putreg32(FDCAN_TXBTIE_TIE, priv->base + STM32_FDCAN_TXBTIE_OFFSET);
|
|
|
|
/* Configure Message RAM
|
|
*
|
|
* The available 2560 words (10 kiB) of RAM are shared between both FDCAN
|
|
* interfaces. It is up to us to ensure each interface has its own non-
|
|
* overlapping region of RAM assigned to it by properly assigning the start
|
|
* and end addresses for all regions of RAM.
|
|
*
|
|
* We will give each interface half of the available RAM.
|
|
*
|
|
* Rx buffers are only used in conjunction with acceptance filters; we
|
|
* don't have any specific need for this, so we will only use Rx FIFOs.
|
|
*
|
|
* Each FIFO can hold up to 64 elements, where each element (for a classic
|
|
* CAN 2.0B frame) is up to 4 words long (8 bytes data + header bits)
|
|
*
|
|
* Let's make use of the full 64 FIFO elements for FIFO0. We have no need
|
|
* to separate messages between FIFO0 and FIFO1, so ignore FIFO1 for
|
|
* simplicity.
|
|
*
|
|
* Note that the start addresses given to FDCAN are in terms of _words_,
|
|
* not bytes, so when we go to read/write to/from the message RAM, there
|
|
* will be a factor of 4 necessary in the address relative to the SA
|
|
* register values.
|
|
*/
|
|
|
|
/* Location of this interface's message RAM - address in CPU memory address
|
|
* and relative address (in words) used for configuration
|
|
*/
|
|
|
|
const uint32_t iface_ram_base = (2560 / 2) * priv->iface_idx;
|
|
const uint32_t gl_ram_base = STM32_CANRAM_BASE;
|
|
uint32_t ram_offset = iface_ram_base;
|
|
|
|
/* Standard ID Filters: Allow space for 128 filters (128 words) */
|
|
|
|
const uint8_t n_stdid = 128;
|
|
priv->message_ram.filt_stdid_addr = gl_ram_base + ram_offset * WORD_LENGTH;
|
|
|
|
regval = (n_stdid << FDCAN_SIDFC_LSS_SHIFT) & FDCAN_SIDFC_LSS_MASK;
|
|
regval |= ram_offset << FDCAN_SIDFC_FLSSA_SHIFT;
|
|
putreg32(regval, priv->base + STM32_FDCAN_SIDFC_OFFSET);
|
|
ram_offset += n_stdid;
|
|
|
|
/* Extended ID Filters: Allow space for 128 filters (128 words) */
|
|
|
|
const uint8_t n_extid = 128;
|
|
priv->message_ram.filt_extid_addr = gl_ram_base + ram_offset * WORD_LENGTH;
|
|
|
|
regval = (n_extid << FDCAN_XIDFC_LSE_SHIFT) & FDCAN_XIDFC_LSE_MASK;
|
|
regval |= ram_offset << FDCAN_XIDFC_FLESA_SHIFT;
|
|
putreg32(regval, priv->base + STM32_FDCAN_XIDFC_OFFSET);
|
|
ram_offset += n_extid;
|
|
|
|
/* Set size of each element in the Rx/Tx buffers and FIFOs */
|
|
|
|
#ifdef CONFIG_NET_CAN_CANFD
|
|
/* Set full 64 byte space for every Rx/Tx FIFO element */
|
|
|
|
modifyreg32(priv->base + STM32_FDCAN_RXESC_OFFSET, 0, FDCAN_RXESC_RBDS); /* Rx Buffer */
|
|
modifyreg32(priv->base + STM32_FDCAN_RXESC_OFFSET, 0, FDCAN_RXESC_F0DS); /* Rx FIFO 0 */
|
|
modifyreg32(priv->base + STM32_FDCAN_RXESC_OFFSET, 0, FDCAN_RXESC_F1DS); /* Rx FIFO 1 */
|
|
modifyreg32(priv->base + STM32_FDCAN_TXESC_OFFSET, 0, FDCAN_TXESC_TBDS); /* Tx Buffer */
|
|
#else
|
|
putreg32(0, priv->base + STM32_FDCAN_RXESC_OFFSET); /* 8 byte space for every element (Rx buffer, FIFO1, FIFO0) */
|
|
putreg32(0, priv->base + STM32_FDCAN_TXESC_OFFSET); /* 8 byte space for every element (Tx buffer) */
|
|
#endif
|
|
|
|
priv->message_ram.n_rxfifo0 = NUM_RX_FIFO0;
|
|
priv->message_ram.n_rxfifo1 = NUM_RX_FIFO1;
|
|
priv->message_ram.n_txfifo = NUM_TX_FIFO;
|
|
|
|
/* Assign Rx Mailbox pointer in the driver structure */
|
|
|
|
priv->message_ram.rxfifo0_addr = gl_ram_base + ram_offset * WORD_LENGTH;
|
|
priv->rx = (struct rx_fifo_s *)(priv->message_ram.rxfifo0_addr);
|
|
|
|
/* Set Rx FIFO0 size (64 elements max) */
|
|
|
|
regval = (ram_offset << FDCAN_RXF0C_F0SA_SHIFT) & FDCAN_RXF0C_F0SA_MASK;
|
|
regval |= (NUM_RX_FIFO0 << FDCAN_RXF0C_F0S_SHIFT) & FDCAN_RXF0C_F0S_MASK;
|
|
putreg32(regval, priv->base + STM32_FDCAN_RXF0C_OFFSET);
|
|
ram_offset += NUM_RX_FIFO0 * FIFO_ELEMENT_SIZE;
|
|
|
|
/* Not using Rx FIFO1 */
|
|
|
|
/* Assign Tx Mailbox pointer in the driver structure */
|
|
|
|
priv->message_ram.txfifo_addr = gl_ram_base + ram_offset * WORD_LENGTH;
|
|
priv->tx = (struct tx_fifo_s *)(priv->message_ram.txfifo_addr);
|
|
|
|
/* Set Tx FIFO size (32 elements max) */
|
|
|
|
regval = (NUM_TX_FIFO << FDCAN_TXBC_TFQS_SHIFT) & FDCAN_TXBC_TFQS_MASK;
|
|
regval &= ~FDCAN_TXBC_TFQM; /* Use FIFO */
|
|
regval |= (ram_offset << FDCAN_TXBC_TBSA_SHIFT) & FDCAN_TXBC_TBSA_MASK;
|
|
putreg32(regval, priv->base + STM32_FDCAN_TXBC_OFFSET);
|
|
|
|
/* Default filter configuration - Accept all messages into Rx FIFO0 */
|
|
|
|
regval = getreg32(priv->base + STM32_FDCAN_GFC_OFFSET);
|
|
regval &= ~FDCAN_GFC_ANFS; /* Accept non-matching stdid frames into FIFO0 */
|
|
regval &= ~FDCAN_GFC_ANFE; /* Accept non-matching extid frames into FIFO0 */
|
|
putreg32(regval, priv->base + STM32_FDCAN_GFC_OFFSET);
|
|
|
|
#ifdef CONFIG_STM32H7_FDCAN_REGDEBUG
|
|
fdcan_dumpregs(priv);
|
|
#endif
|
|
|
|
/* Exit Initialization mode */
|
|
|
|
fdcan_setinit(priv->base, 0);
|
|
|
|
#ifdef CONFIG_STM32H7_FDCAN_REGDEBUG
|
|
fdcan_dumpregs(priv);
|
|
#endif
|
|
|
|
leave_critical_section(flags);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Function: fdcan_reset
|
|
*
|
|
* Description:
|
|
* Put the device in the non-operational, reset state
|
|
*
|
|
* Input Parameters:
|
|
* priv - Pointer to the private FDCAN driver state structure
|
|
*
|
|
* Returned Value:
|
|
* None
|
|
*
|
|
* Assumptions:
|
|
* The device has previously been initialized, including message RAM
|
|
****************************************************************************/
|
|
|
|
static void fdcan_reset(struct fdcan_driver_s *priv)
|
|
{
|
|
/* Request Init Mode */
|
|
|
|
irqstate_t flags = enter_critical_section();
|
|
|
|
fdcan_setenable(priv->base, 1);
|
|
fdcan_setinit(priv->base, 1);
|
|
|
|
/* Enable Configuration Change Mode */
|
|
|
|
fdcan_setconfig(priv->base, 1);
|
|
|
|
/* Disable interrupts and clear all interrupt flags */
|
|
|
|
fdcan_disable_interrupts(priv);
|
|
|
|
putreg32(FDCAN_IR_MASK, priv->base + STM32_FDCAN_IR_OFFSET);
|
|
|
|
/* Clear all message RAM mailboxes if initialized */
|
|
|
|
#ifdef CONFIG_NET_CAN_CANFD
|
|
const uint8_t n_data_words = 16;
|
|
#else
|
|
const uint8_t n_data_words = 2;
|
|
#endif
|
|
|
|
if (priv->rx)
|
|
{
|
|
for (uint32_t i = 0; i < NUM_RX_FIFO0; i++)
|
|
{
|
|
#ifdef CONFIG_STM32H7_FDCAN_REGDEBUG
|
|
ninfo("[fdcan] MB RX %i %p\r\n", i, &priv->rx[i]);
|
|
#endif
|
|
priv->rx[i].header.w1 = 0x0;
|
|
priv->rx[i].header.w0 = 0x0;
|
|
for (uint8_t j = 0; j < n_data_words; j++)
|
|
{
|
|
priv->rx[i].data[j].word = 0x0;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (priv->tx)
|
|
{
|
|
for (uint32_t i = 0; i < NUM_TX_FIFO; i++)
|
|
{
|
|
#ifdef CONFIG_STM32H7_FDCAN_REGDEBUG
|
|
ninfo("[fdcan] MB TX %i %p\r\n", i, &priv->tx[i]);
|
|
#endif
|
|
priv->tx[i].header.w1 = 0x0;
|
|
priv->tx[i].header.w0 = 0x0;
|
|
for (uint8_t j = 0; j < n_data_words; j++)
|
|
{
|
|
priv->tx[i].data[j].word = 0x0;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Power off the device -- See RM0433 pg 2493 */
|
|
|
|
fdcan_setinit(priv->base, 0);
|
|
fdcan_setenable(priv->base, 0);
|
|
|
|
leave_critical_section(flags);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Public Functions
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Function: stm32_fdcansockinitialize
|
|
*
|
|
* Description:
|
|
* Initialize the selected CAN peripheral and network (socket) interface
|
|
*
|
|
* Input Parameters:
|
|
* intf - In the case where there are multiple interfaces, this value
|
|
* identifies which interface is to be initialized.
|
|
*
|
|
* Returned Value:
|
|
* OK on success; Negated errno on failure.
|
|
*
|
|
* Assumptions:
|
|
*
|
|
****************************************************************************/
|
|
|
|
int stm32_fdcansockinitialize(int intf)
|
|
{
|
|
struct fdcan_driver_s *priv;
|
|
|
|
switch (intf)
|
|
{
|
|
#ifdef CONFIG_STM32H7_FDCAN1
|
|
case 0:
|
|
priv = &g_fdcan0;
|
|
memset(priv, 0, sizeof(struct fdcan_driver_s));
|
|
priv->base = STM32_FDCAN1_BASE;
|
|
priv->iface_idx = 0;
|
|
priv->config = &stm32_fdcan0_config;
|
|
|
|
/* Default bitrate configuration */
|
|
|
|
# ifdef CONFIG_NET_CAN_CANFD
|
|
priv->arbi_timing.bitrate = CONFIG_FDCAN1_ARBI_BITRATE;
|
|
priv->data_timing.bitrate = CONFIG_FDCAN1_DATA_BITRATE;
|
|
# else
|
|
priv->arbi_timing.bitrate = CONFIG_FDCAN1_BITRATE;
|
|
# endif
|
|
break;
|
|
#endif
|
|
|
|
#ifdef CONFIG_STM32H7_FDCAN2
|
|
case 1:
|
|
priv = &g_fdcan1;
|
|
memset(priv, 0, sizeof(struct fdcan_driver_s));
|
|
priv->base = STM32_FDCAN2_BASE;
|
|
priv->iface_idx = 1;
|
|
priv->config = &stm32_fdcan1_config;
|
|
|
|
/* Default bitrate configuration */
|
|
|
|
# ifdef CONFIG_NET_CAN_CANFD
|
|
priv->arbi_timing.bitrate = CONFIG_FDCAN2_ARBI_BITRATE;
|
|
priv->data_timing.bitrate = CONFIG_FDCAN2_DATA_BITRATE;
|
|
# else
|
|
priv->arbi_timing.bitrate = CONFIG_FDCAN2_BITRATE;
|
|
# endif
|
|
break;
|
|
#endif
|
|
|
|
#ifdef CONFIG_STM32H7_FDCAN3
|
|
case 2:
|
|
priv = &g_fdcan2
|
|
memset(priv, 0, sizeof(struct fdcan_driver_s));
|
|
priv->base = STM32_FDCAN3_BASE;
|
|
priv->iface_idx = 2;
|
|
priv->config = &stm32_fdcan2_config;
|
|
|
|
/* Default bitrate configuration */
|
|
|
|
# ifdef CONFIG_NET_CAN_CANFD
|
|
priv->arbi_timing.bitrate = CONFIG_FDCAN3_ARBI_BITRATE;
|
|
priv->data_timing.bitrate = CONFIG_FDCAN3_DATA_BITRATE;
|
|
# else
|
|
priv->arbi_timing.bitrate = CONFIG_FDCAN3_BITRATE;
|
|
# endif
|
|
break;
|
|
#endif
|
|
|
|
default:
|
|
return -ENODEV;
|
|
}
|
|
|
|
if (fdcan_bittiming(&priv->arbi_timing) != OK)
|
|
{
|
|
printf("ERROR: Invalid CAN timings\n");
|
|
return -1;
|
|
}
|
|
|
|
#ifdef CONFIG_NET_CAN_CANFD
|
|
if (fdcan_bittiming(&priv->data_timing) != OK)
|
|
{
|
|
printf("ERROR: Invalid CAN data phase timings\n");
|
|
return -1;
|
|
}
|
|
#endif
|
|
|
|
/* Configure the pins we're using to interface to the controller */
|
|
|
|
stm32_configgpio(priv->config->tx_pin);
|
|
stm32_configgpio(priv->config->rx_pin);
|
|
|
|
/* Attach the fdcan interrupt handlers */
|
|
|
|
if (irq_attach(priv->config->mb_irq[0], fdcan_interrupt, NULL))
|
|
{
|
|
/* We could not attach the ISR to the interrupt */
|
|
|
|
printf("ERROR: Failed to attach CAN RX IRQ\n");
|
|
return -EAGAIN;
|
|
}
|
|
|
|
if (irq_attach(priv->config->mb_irq[1], fdcan_interrupt, NULL))
|
|
{
|
|
/* We could not attach the ISR to the interrupt */
|
|
|
|
printf("ERROR: Failed to attach CAN TX IRQ\n");
|
|
return -EAGAIN;
|
|
}
|
|
|
|
/* Initialize the driver structure */
|
|
|
|
priv->dev.d_ifup = fdcan_ifup; /* I/F up callback */
|
|
priv->dev.d_ifdown = fdcan_ifdown; /* I/F down callback */
|
|
priv->dev.d_txavail = fdcan_txavail; /* New TX data callback */
|
|
#ifdef CONFIG_NETDEV_IOCTL
|
|
priv->dev.d_ioctl = fdcan_netdev_ioctl; /* Support CAN ioctl() calls */
|
|
#endif
|
|
priv->dev.d_private = (void *)priv; /* Used to recover private state from dev */
|
|
|
|
priv->dev.d_buf = priv->tx_pool;
|
|
|
|
priv->rx = NULL;
|
|
priv->tx = NULL;
|
|
|
|
/* Put the interface in the down state (disable interrupts, power off) */
|
|
|
|
fdcan_ifdown(&priv->dev);
|
|
|
|
/* Register the device with the OS so that socket IOCTLs can be performed */
|
|
|
|
netdev_register(&priv->dev, NET_LL_CAN);
|
|
|
|
#ifdef CONFIG_STM32H7_FDCAN_REGDEBUG
|
|
fdcan_dumpregs(priv);
|
|
#endif
|
|
|
|
return OK;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* 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_STM32H7_FDCAN1
|
|
stm32_fdcansockinitialize(0);
|
|
#endif
|
|
|
|
#ifdef CONFIG_STM32H7_FDCAN2
|
|
stm32_fdcansockinitialize(1);
|
|
#endif
|
|
|
|
#ifdef CONFIG_STM32H7_FDCAN3
|
|
stm32_fdcansockinitialize(2);
|
|
#endif
|
|
}
|
|
#endif
|