mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2025-12-18 11:03:50 +01:00
1713 lines
56 KiB
C
1713 lines
56 KiB
C
/*
|
|
* SPDX-FileCopyrightText: 2024 COGIP Robotics association
|
|
* SPDX-License-Identifier: LGPL-2.1-only
|
|
*/
|
|
|
|
/**
|
|
* @ingroup cpu_stm32
|
|
* @{
|
|
*
|
|
* @file
|
|
* @brief Implementation of the CAN FD controller driver
|
|
*
|
|
* @author Gilles DOFFE <g.doffe@gmail.com>
|
|
* @}
|
|
*/
|
|
|
|
#include <assert.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
#include <limits.h>
|
|
#include <cpu_conf.h>
|
|
|
|
#include "can/device.h"
|
|
#include "periph/can.h"
|
|
#include "periph/gpio.h"
|
|
#include "pm_layered.h"
|
|
|
|
#define ENABLE_DEBUG 0
|
|
#include "debug.h"
|
|
|
|
#define CAN_MAX_WAIT_CHANGE (10000U)
|
|
|
|
typedef enum {
|
|
MODE_NORMAL,
|
|
MODE_SLEEP,
|
|
MODE_INIT,
|
|
} can_mode_t;
|
|
|
|
/* Driver functions */
|
|
static int _init(candev_t *candev);
|
|
static void _isr(candev_t *candev);
|
|
static int _send(candev_t *candev, const can_frame_t *frame);
|
|
static int _abort(candev_t *candev, const can_frame_t *frame);
|
|
static int _set(candev_t *candev, canopt_t opt, void *value, size_t value_len);
|
|
static int _get(candev_t *candev, canopt_t opt, void *value, size_t max_len);
|
|
static int _set_filter(candev_t *candev, const struct can_filter *filter);
|
|
static int _remove_filter(candev_t *candev, const struct can_filter *filter);
|
|
|
|
/* Interrupts handler */
|
|
static void tx_irq_handler(can_t *dev);
|
|
static void tx_isr(can_t *dev);
|
|
static void tx_conf(can_t *dev, int mailbox);
|
|
static void rx_irq_handler(can_t *dev);
|
|
static void rx_new_message_irq_handler(can_t *dev, uint8_t message_ram_rx_fifo);
|
|
static void rx_isr(can_t *dev);
|
|
|
|
/* Bittiming configuration */
|
|
static inline void set_bit_timing(can_t *dev);
|
|
|
|
static inline can_mode_t get_mode(FDCAN_GlobalTypeDef *can);
|
|
static int set_mode(FDCAN_GlobalTypeDef *can, can_mode_t mode);
|
|
|
|
static const candev_driver_t candev_stm32_driver = {
|
|
.send = _send,
|
|
.init = _init,
|
|
.isr = _isr,
|
|
.get = _get,
|
|
.set = _set,
|
|
.abort = _abort,
|
|
.set_filter = _set_filter,
|
|
.remove_filter = _remove_filter,
|
|
};
|
|
|
|
/* Classic CAN bittiming */
|
|
static const struct can_bittiming_const bittiming_const = {
|
|
.tseg1_min = 1,
|
|
.tseg1_max = 256,
|
|
.tseg2_min = 1,
|
|
.tseg2_max = 128,
|
|
.sjw_max = 1,
|
|
.brp_min = 1,
|
|
.brp_max = 1024,
|
|
.brp_inc = 1,
|
|
};
|
|
|
|
/* FDCAN data bittiming */
|
|
static const struct can_bittiming_const fd_data_bittiming_const = {
|
|
.tseg1_min = 1,
|
|
.tseg1_max = 16,
|
|
.tseg2_min = 1,
|
|
.tseg2_max = 8,
|
|
.sjw_max = 1,
|
|
.brp_min = 1,
|
|
.brp_max = 32,
|
|
.brp_inc = 1,
|
|
};
|
|
|
|
enum {
|
|
STATUS_ON,
|
|
STATUS_SLEEP,
|
|
};
|
|
|
|
/* FDCAN channels */
|
|
static can_t *_can[FDCANDEV_STM32_CHAN_NUMOF];
|
|
/* FDCAN channels status */
|
|
static uint8_t _status[FDCANDEV_STM32_CHAN_NUMOF];
|
|
|
|
/* Return channel for a given FDCAN device */
|
|
static inline uint8_t get_channel(FDCAN_GlobalTypeDef *can)
|
|
{
|
|
return (int) (((uint32_t)can - (uint32_t)FDCAN1) >> 10);
|
|
}
|
|
|
|
/* Return FDCAN STM32 datasheet channel name */
|
|
static inline uint8_t get_channel_id(FDCAN_GlobalTypeDef *can)
|
|
{
|
|
return get_channel(can) + 1;
|
|
}
|
|
|
|
/* Get current FDCAN channel mode */
|
|
static inline can_mode_t get_mode(FDCAN_GlobalTypeDef *can)
|
|
{
|
|
if ((can->CCCR & FDCAN_CCCR_CSR) == FDCAN_CCCR_CSR) {
|
|
DEBUG("%s: Current FDCAN%u mode: SLEEP\n", __func__, get_channel_id(can));
|
|
return MODE_SLEEP;
|
|
}
|
|
else if ((can->CCCR & FDCAN_CCCR_INIT) == FDCAN_CCCR_INIT) {
|
|
DEBUG("%s: Current FDCAN%u mode: INIT\n", __func__, get_channel_id(can));
|
|
return MODE_INIT;
|
|
}
|
|
else {
|
|
DEBUG("%s: Current FDCAN%u mode: NORMAL\n", __func__, get_channel_id(can));
|
|
return MODE_NORMAL;
|
|
}
|
|
}
|
|
|
|
/* Change FDCAN channel mode */
|
|
static int set_mode(FDCAN_GlobalTypeDef *can, can_mode_t mode)
|
|
{
|
|
int max_loop = CAN_MAX_WAIT_CHANGE;
|
|
int res = 0;
|
|
|
|
switch (mode) {
|
|
case MODE_NORMAL:
|
|
DEBUG("%s: Set FDCAN%u NORMAL mode\n", __func__, get_channel_id(can));
|
|
/* Disable sleep mode */
|
|
can->CCCR &= ~FDCAN_CCCR_CSR;
|
|
while ((can->CCCR & FDCAN_CCCR_CSA) && max_loop != 0) {
|
|
max_loop--;
|
|
}
|
|
if (max_loop) {
|
|
max_loop = CAN_MAX_WAIT_CHANGE;
|
|
DEBUG("%s: FDCAN%u FDCAN_CCCR_CSA reset\n", __func__, get_channel_id(can));
|
|
}
|
|
can->CCCR &= ~FDCAN_CCCR_INIT;
|
|
/* CCE (Configuration Change Enable) bit is automatically
|
|
cleared when INIT bit is cleared. So to ensure the FDCAN
|
|
channel is no more in INIT mode, wait for CCE and INIT bits
|
|
to be cleared. */
|
|
while ((can->CCCR & FDCAN_CCCR_CCE) && max_loop != 0) {
|
|
max_loop--;
|
|
}
|
|
if (max_loop) {
|
|
max_loop = CAN_MAX_WAIT_CHANGE;
|
|
DEBUG("%s: FDCAN%u FDCAN_CCCR_CCE reset\n", __func__, get_channel_id(can));
|
|
}
|
|
while ((can->CCCR & FDCAN_CCCR_INIT) && max_loop != 0) {
|
|
max_loop--;
|
|
}
|
|
if (max_loop) {
|
|
max_loop = CAN_MAX_WAIT_CHANGE;
|
|
DEBUG("%s: FDCAN%u_CCCR_INIT reset\n", __func__, get_channel_id(can));
|
|
}
|
|
break;
|
|
case MODE_SLEEP:
|
|
DEBUG("%s: Set FDCAN%u SLEEP mode\n", __func__, get_channel_id(can));
|
|
/* Enable sleep mode */
|
|
can->CCCR |= FDCAN_CCCR_CSR;
|
|
while (!(can->CCCR & FDCAN_CCCR_CSA) && max_loop != 0) {
|
|
max_loop--;
|
|
}
|
|
break;
|
|
case MODE_INIT:
|
|
DEBUG("%s: Set FDCAN%u INIT mode\n", __func__, get_channel_id(can));
|
|
/* Disable sleep mode */
|
|
can->CCCR &= ~FDCAN_CCCR_CSR;
|
|
while ((can->CCCR & FDCAN_CCCR_CSA) && max_loop != 0) {
|
|
max_loop--;
|
|
}
|
|
if (max_loop) {
|
|
max_loop = CAN_MAX_WAIT_CHANGE;
|
|
}
|
|
/* Enable INIT mode */
|
|
can->CCCR |= FDCAN_CCCR_INIT;
|
|
while (!(can->CCCR & FDCAN_CCCR_INIT) && max_loop != 0) {
|
|
max_loop--;
|
|
}
|
|
/* Enable Configuration changes */
|
|
can->CCCR |= FDCAN_CCCR_CCE;
|
|
break;
|
|
default:
|
|
DEBUG("%s: FDCAN%u Unsupported mode\n", __func__, get_channel_id(can));
|
|
res = -EINVAL;
|
|
break;
|
|
}
|
|
|
|
DEBUG("%s: FDCAN%u CCCR = %lx\n", __func__, get_channel_id(can), can->CCCR);
|
|
|
|
if (max_loop == 0) {
|
|
DEBUG("%s: FDCAN%u didn't switch mode %d\n", __func__, get_channel_id(can), mode);
|
|
res = -ETIMEDOUT;
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
void can_init(can_t *dev, const can_conf_t *conf)
|
|
{
|
|
/* Set CAN device callbacks */
|
|
dev->candev.driver = &candev_stm32_driver;
|
|
|
|
/* Use the PCLK (APB1 peripheral) clock by default as this clock is always available */
|
|
RCC->CCIPR &= ~RCC_CCIPR_FDCANSEL;
|
|
RCC->CCIPR |= RCC_CCIPR_FDCANSEL_1;
|
|
|
|
/* Compute bittiming values for classic CAN */
|
|
struct can_bittiming timing = { .bitrate = FDCANDEV_STM32_DEFAULT_BITRATE,
|
|
.sample_point = FDCANDEV_STM32_DEFAULT_SPT };
|
|
can_device_calc_bittiming(CLOCK_APB1, &bittiming_const, &timing);
|
|
timing.tq = (timing.brp * NS_PER_SEC) / CLOCK_APB1;
|
|
|
|
/* Compute bittiming values for FDCAN data stream */
|
|
struct can_bittiming fd_data_timing = { .bitrate = FDCANDEV_STM32_DEFAULT_FD_DATA_BITRATE,
|
|
.sample_point = FDCANDEV_STM32_DEFAULT_SPT };
|
|
can_device_calc_bittiming(CLOCK_APB1, &fd_data_bittiming_const, &fd_data_timing);
|
|
fd_data_timing.tq = (fd_data_timing.brp * NS_PER_SEC) / CLOCK_APB1;
|
|
|
|
/* Save the calculated bittimings */
|
|
memcpy(&dev->candev.bittiming, &timing, sizeof(timing));
|
|
memcpy(&dev->candev.fd_data_bittiming, &fd_data_timing, sizeof(fd_data_timing));
|
|
DEBUG("%s: &dev->rx_mailbox = %p, sizeof(dev->rx_mailbox) = %u\n",
|
|
__func__, &dev->rx_mailbox, sizeof(dev->rx_mailbox));
|
|
|
|
/* Save configuration */
|
|
dev->conf = conf;
|
|
|
|
/* Reset rx_pin/tx_pin */
|
|
dev->rx_pin = GPIO_UNDEF;
|
|
dev->tx_pin = GPIO_UNDEF;
|
|
}
|
|
|
|
/* Get FDCAN message RAM address */
|
|
static uint32_t* get_message_ram(FDCAN_GlobalTypeDef *can) {
|
|
/* In case of multiple instances the RAM start address for the FDCANn is computed by end
|
|
of FDCANn-1 address + 4, and the FDCANn end address is computed by FDCANn start
|
|
address + 0x0350 - 4. */
|
|
DEBUG("%s: FDCAN%u message RAM address is %p\n",
|
|
__func__, get_channel_id(can),
|
|
(uint32_t *)(SRAMCAN_BASE
|
|
+ (((uint32_t)can - FDCAN1_BASE) >> 10) * FDCAN_SRAM_MESSAGE_RAM_SIZE)
|
|
);
|
|
|
|
return (uint32_t*)(
|
|
SRAMCAN_BASE
|
|
+ (((uint32_t)can - FDCAN1_BASE) >> 10) * FDCAN_SRAM_MESSAGE_RAM_SIZE
|
|
);
|
|
}
|
|
|
|
/* Get FDCAN channel extended filters list message RAM address */
|
|
static uint32_t* get_flesa_message_ram(FDCAN_GlobalTypeDef *can) {
|
|
DEBUG("%s: FDCAN%u FLESA message RAM address is %p\n",
|
|
__func__, get_channel_id(can),
|
|
(void*)get_message_ram(can) + FDCAN_SRAM_FLESA
|
|
);
|
|
|
|
return get_message_ram(can) + FDCAN_SRAM_FLESA;
|
|
}
|
|
|
|
/* Get FDCAN channel Rx FIFO message RAM address */
|
|
static uint32_t* get_message_ram_rx_fifo_address(FDCAN_GlobalTypeDef *can,
|
|
uint8_t message_ram_rx_fifo) {
|
|
DEBUG("%s: FDCAN%u Rx FIFO message RAM address is %p\n",
|
|
__func__, get_channel_id(can),
|
|
get_message_ram(can) + FDCAN_SRAM_F0SA + message_ram_rx_fifo * FDCAN_SRAM_RXFIFO_SIZE
|
|
);
|
|
|
|
return get_message_ram(can) + FDCAN_SRAM_F0SA + message_ram_rx_fifo * FDCAN_SRAM_RXFIFO_SIZE;
|
|
}
|
|
|
|
/* Get FDCAN channel Tx buffers message RAM address */
|
|
static uint32_t* get_message_ram_tx_buffer_address(FDCAN_GlobalTypeDef *can) {
|
|
DEBUG("%s: FDCAN%u Tx FIFO message RAM address is %p\n",
|
|
__func__, get_channel_id(can),
|
|
get_message_ram(can) + FDCAN_SRAM_TBSA
|
|
);
|
|
|
|
return get_message_ram(can) + FDCAN_SRAM_TBSA;
|
|
}
|
|
|
|
/** Check if filter is set according to filter ID
|
|
* filter ID < FDCAN_STM32_NB_STD_FILTER: it is a standard filter (message ID is 11 bits long)
|
|
* filter ID >= FDCAN_STM32_NB_STD_FILTER: it is an extended filter (message ID is 29 bits long)
|
|
*/
|
|
static int filter_is_set(FDCAN_GlobalTypeDef *can, uint8_t filter_id)
|
|
{
|
|
int ret = false;
|
|
|
|
if (filter_id < FDCAN_STM32_NB_STD_FILTER) {
|
|
DEBUG("%s: FDCAN%u filter %u is a standard filter\n",
|
|
__func__, get_channel_id(can), filter_id);
|
|
if (filter_id < (can->RXGFC & FDCAN_RXGFC_LSS) >> FDCAN_RXGFC_LSS_Pos) {
|
|
DEBUG("%s: Filter %u is lesser than standard filter list size (%lx)\n",
|
|
__func__,
|
|
filter_id,
|
|
(can->RXGFC & FDCAN_RXGFC_LSS) >> FDCAN_RXGFC_LSS_Pos);
|
|
|
|
/* Filter List Standard start at
|
|
RAM address + FLSSA (0x00) + standard filter offset (4 Bytes * filter_id) */
|
|
uint32_t *fls_ram_address = get_message_ram(can)
|
|
+ FDCAN_SRAM_FLS_FILTER_SIZE * filter_id;
|
|
|
|
DEBUG("%s: FDCAN%u ftandard filter message RAM address is %p\n",
|
|
__func__, get_channel_id(can),
|
|
fls_ram_address
|
|
);
|
|
|
|
if (((*fls_ram_address & FDCAN_SRAM_FLS_SFT) != FDCAN_SRAM_FLS_SFT_DISABLED)
|
|
|| ((*fls_ram_address & FDCAN_SRAM_FLS_SFEC) != FDCAN_SRAM_FLS_SFEC_DISABLED)) {
|
|
DEBUG("%s: FDCAN%u filter %u is enabled by SFT(%lx) and SFEC(%lx)\n",
|
|
__func__, get_channel_id(can),
|
|
filter_id,
|
|
(*fls_ram_address & FDCAN_SRAM_FLS_SFT),
|
|
(*fls_ram_address & FDCAN_SRAM_FLS_SFEC));
|
|
|
|
ret = true;
|
|
}
|
|
else {
|
|
DEBUG("%s: FDCAN%u filter %u is disabled by SFT(%lx) and/or SFEC(%lx)\n",
|
|
__func__, get_channel_id(can),
|
|
filter_id,
|
|
(*fls_ram_address & FDCAN_SRAM_FLS_SFT),
|
|
(*fls_ram_address & FDCAN_SRAM_FLS_SFEC));
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
DEBUG("%s: Filter %u is an extended filter\n", __func__, filter_id);
|
|
/* Real extended filter index */
|
|
filter_id -= FDCAN_STM32_NB_STD_FILTER;
|
|
if (filter_id < (can->RXGFC & FDCAN_RXGFC_LSE) >> FDCAN_RXGFC_LSE_Pos) {
|
|
DEBUG("%s: FDCAN%u filter %u is lesser than extended filter list size (%lx)\n",
|
|
__func__, get_channel_id(can),
|
|
filter_id,
|
|
(can->RXGFC & FDCAN_RXGFC_LSE) >> FDCAN_RXGFC_LSE_Pos);
|
|
|
|
/* Filter List Extended start at
|
|
RAM address + FLESA (0x70) + extended filter offset (8 Bytes * filter_id) */
|
|
uint32_t *fle_ram_address_f0 = get_flesa_message_ram(can)
|
|
+ FDCAN_SRAM_FLE_FILTER_SIZE * filter_id;
|
|
uint32_t *fle_ram_address_f1 = fle_ram_address_f0
|
|
+ FDCAN_SRAM_FLE_FILTER_SIZE / 2;
|
|
|
|
DEBUG("%s: FDCAN%u extended filter message RAM F0 address is %p\n",
|
|
__func__, get_channel_id(can),
|
|
fle_ram_address_f0
|
|
);
|
|
DEBUG("%s: FDCAN%u extended filter message RAM F1 address is %p\n",
|
|
__func__, get_channel_id(can),
|
|
fle_ram_address_f1
|
|
);
|
|
|
|
if ((*fle_ram_address_f0 & FDCAN_SRAM_FLE_F0_EFEC) != FDCAN_SRAM_FLE_F0_EFEC_DISABLED) {
|
|
|
|
DEBUG("%s: FDCAN%u filter %u is enabled by EFEC(%lx)\n",
|
|
__func__, get_channel_id(can),
|
|
filter_id,
|
|
(*fle_ram_address_f0 & FDCAN_SRAM_FLE_F0_EFEC));
|
|
ret = true;
|
|
}
|
|
else {
|
|
DEBUG("%s: FDCAN%u filter %u is disabled by EFEC(%lx)\n",
|
|
__func__, get_channel_id(can),
|
|
filter_id,
|
|
(*fle_ram_address_f0 & FDCAN_SRAM_FLE_F0_EFEC));
|
|
}
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* Set standard filter for 11 bits message ID */
|
|
static int set_standard_filter(FDCAN_GlobalTypeDef *can, uint32_t fr1, uint32_t fr2,
|
|
uint8_t filter_id, uint8_t fifo)
|
|
{
|
|
/* Check filter ID */
|
|
if (filter_id >= FDCAN_STM32_NB_STD_FILTER) {
|
|
DEBUG("ERROR: invalid standard filter ID %u\n", filter_id);
|
|
return -ENXIO;
|
|
}
|
|
|
|
/* Filter List Standard start at
|
|
RAM address + FLSSA (0x00) + standard filter offset (4 Bytes * filter_id) */
|
|
uint32_t *fls_ram_address = get_message_ram(can) + FDCAN_SRAM_FLS_FILTER_SIZE * filter_id;
|
|
DEBUG("%s: FDCAN%u standard filter message RAM address is %p\n",
|
|
__func__, get_channel_id(can),
|
|
fls_ram_address
|
|
);
|
|
|
|
DEBUG("%s: FDCAN%u standard filter %u value is %lx\n",
|
|
__func__, get_channel_id(can),
|
|
filter_id,
|
|
*fls_ram_address
|
|
);
|
|
|
|
/* Set filter/mask */
|
|
if ((fr1 & ~CAN_SFF_MASK) || (fr2 & ~CAN_SFF_MASK)) {
|
|
DEBUG("%s: FDCAN%u standard filter %u ID(%lx) or mask(%lx) are not valid\n",
|
|
__func__, get_channel_id(can),
|
|
filter_id,
|
|
fr1,
|
|
fr2);
|
|
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* Set filter mask and ID */
|
|
*fls_ram_address &= ~FDCAN_SRAM_FLS_SFID1;
|
|
*fls_ram_address |= fr1 << FDCAN_SRAM_FLS_SFID1_Pos;
|
|
|
|
*fls_ram_address &= ~FDCAN_SRAM_FLS_SFID2;
|
|
*fls_ram_address |= fr2;
|
|
|
|
/* Set filter type */
|
|
*fls_ram_address &= ~FDCAN_SRAM_FLS_SFT;
|
|
*fls_ram_address |= FDCAN_SRAM_FLS_SFT_CLASSIC;
|
|
|
|
/* Filter FIFO configuration */
|
|
*fls_ram_address &= ~FDCAN_SRAM_FLS_SFEC;
|
|
*fls_ram_address |= (fifo ? FDCAN_SRAM_FLS_SFEC_FIFO1 : FDCAN_SRAM_FLS_SFEC_FIFO0);
|
|
|
|
DEBUG("%s: FDCAN%u standard filter %u value is %lx\n",
|
|
__func__, get_channel_id(can),
|
|
filter_id,
|
|
*fls_ram_address
|
|
);
|
|
|
|
/* Set LSS (List Size of Standard filters) according to new filter ID */
|
|
if (filter_id >= (can->RXGFC & FDCAN_RXGFC_LSS) >> FDCAN_RXGFC_LSS_Pos) {
|
|
can->RXGFC &= ~FDCAN_RXGFC_LSS;
|
|
can->RXGFC |= ((filter_id) + 1) << FDCAN_RXGFC_LSS_Pos;
|
|
|
|
DEBUG("%s: FDCAN%u new LSS value is %u\n",
|
|
__func__, get_channel_id(can), filter_id + 1);
|
|
}
|
|
|
|
DEBUG("%s: FDCAN%u new RXGFC value is %lx\n",
|
|
__func__, get_channel_id(can), can->RXGFC);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Set extended filter for 29 bits message ID */
|
|
static int set_extended_filter(FDCAN_GlobalTypeDef *can, uint32_t fr1, uint32_t fr2,
|
|
uint8_t filter_id, uint8_t fifo)
|
|
{
|
|
/* Check filter ID */
|
|
if (filter_id >= FDCAN_STM32_NB_EXT_FILTER) {
|
|
DEBUG("ERROR: invalid extended filter ID %u\n", filter_id);
|
|
return -ENXIO;
|
|
}
|
|
|
|
/* Filter List Extended start at
|
|
RAM address + FLESA (0x70) + extended filter offset (8 Bytes * filter_id) */
|
|
uint32_t *fle_ram_address_f0 = get_flesa_message_ram(can)
|
|
+ FDCAN_SRAM_FLE_FILTER_SIZE * filter_id;
|
|
uint32_t *fle_ram_address_f1 = fle_ram_address_f0 + FDCAN_SRAM_FLE_FILTER_SIZE / 2;
|
|
|
|
/* Reset filter */
|
|
*fle_ram_address_f0 = 0;
|
|
*fle_ram_address_f1 = 0;
|
|
|
|
*fle_ram_address_f0 |= (fr1 & CAN_EFF_MASK) /* Filter ID */
|
|
| (fifo ? FDCAN_SRAM_FLE_F0_EFEC_FIFO1
|
|
: FDCAN_SRAM_FLE_F0_EFEC_FIFO0); /* Filter FIFO configuration */
|
|
*fle_ram_address_f1 |= (fr2 & CAN_EFF_MASK) /* Filter mask */
|
|
| FDCAN_SRAM_FLE_F1_EFT_CLASSIC; /* Set classic filter type */
|
|
|
|
DEBUG("%s: FDCAN%u extended filter %u F0 value is %lx\n",
|
|
__func__, get_channel_id(can),
|
|
filter_id,
|
|
*fle_ram_address_f0
|
|
);
|
|
DEBUG("%s: FDCAN%u extended filter %u F1 value is %lx\n",
|
|
__func__, get_channel_id(can),
|
|
filter_id,
|
|
*fle_ram_address_f1
|
|
);
|
|
|
|
/* Set LSS (List Size of Extended filters) according to new filter ID */
|
|
if (filter_id >= (can->RXGFC & FDCAN_RXGFC_LSE) >> FDCAN_RXGFC_LSE_Pos) {
|
|
can->RXGFC &= ~FDCAN_RXGFC_LSE;
|
|
can->RXGFC |= (filter_id + 1) << FDCAN_RXGFC_LSE_Pos;
|
|
|
|
DEBUG("%s: FDCAN%u new LSE value is %u\n",
|
|
__func__, get_channel_id(can), filter_id + 1);
|
|
}
|
|
|
|
DEBUG("%s: FDCAN%u new RXGFC value is %lx\n",
|
|
__func__, get_channel_id(can), can->RXGFC);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/** Select set function for standard or extended filter.
|
|
* Extended filters can filter standard messages
|
|
*/
|
|
static int set_filter(FDCAN_GlobalTypeDef *can, uint32_t fr1, uint32_t fr2,
|
|
uint8_t filter_id, uint8_t fifo)
|
|
{
|
|
/* Check ID and mask, to use an extended filter if needed */
|
|
if (filter_id < FDCAN_STM32_NB_STD_FILTER) {
|
|
return set_standard_filter(can, fr1, fr2, filter_id, fifo);
|
|
}
|
|
else {
|
|
return set_extended_filter(can, fr1, fr2, filter_id - FDCAN_STM32_NB_STD_FILTER, fifo);
|
|
}
|
|
}
|
|
|
|
static int get_can_filter(FDCAN_GlobalTypeDef *can, uint8_t filter_id,
|
|
uint32_t *filter, uint32_t *mask)
|
|
{
|
|
if (filter_id < FDCAN_STM32_NB_STD_FILTER) {
|
|
/* Filter List Standard start at
|
|
RAM address + FLSSA (0x00) + standard filter offset (4 Bytes * filter_id) */
|
|
uint32_t *fls_ram_address = get_message_ram(can) + FDCAN_SRAM_FLS_FILTER_SIZE * filter_id;
|
|
*filter = (*fls_ram_address & FDCAN_SRAM_FLS_SFID1) >> FDCAN_SRAM_FLS_SFID1_Pos;
|
|
*mask = (*fls_ram_address & FDCAN_SRAM_FLS_SFID2);
|
|
}
|
|
else {
|
|
filter_id -= FDCAN_STM32_NB_STD_FILTER;
|
|
/* Filter List Extended start at
|
|
RAM address + FLESA (0x70) + extended filter offset (8 Bytes * filter_id) */
|
|
uint32_t *fle_ram_address_f0 = get_flesa_message_ram(can)
|
|
+ FDCAN_SRAM_FLE_FILTER_SIZE * filter_id;
|
|
uint32_t *fle_ram_address_f1 = fle_ram_address_f0 + FDCAN_SRAM_FLE_FILTER_SIZE / 2;
|
|
*filter = (*fle_ram_address_f0 & FDCAN_SRAM_FLE_F0_EFID1);
|
|
*mask = (*fle_ram_address_f1 & FDCAN_SRAM_FLE_F1_EFID2);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void unset_standard_filter(FDCAN_GlobalTypeDef *can, uint8_t filter_id)
|
|
{
|
|
/* Filter List Standard start at
|
|
RAM address + FLSSA (0x00) + standard filter offset (4 Bytes * filter_id) */
|
|
uint32_t *fls_ram_address = get_message_ram(can) + FDCAN_SRAM_FLS_FILTER_SIZE * filter_id;
|
|
/* Disable filter */
|
|
*fls_ram_address &= ~FDCAN_SRAM_FLS_SFEC;
|
|
|
|
DEBUG("%s: FDCAN%u standard filter %u value is %lx\n",
|
|
__func__, get_channel_id(can),
|
|
filter_id,
|
|
*fls_ram_address
|
|
);
|
|
|
|
/* Update LSS value if necessary */
|
|
uint8_t lss_value = (can->RXGFC & FDCAN_RXGFC_LSS) >> FDCAN_RXGFC_LSS_Pos;
|
|
can->RXGFC &= ~FDCAN_RXGFC_LSS;
|
|
can->RXGFC |= lss_value > 0 ? lss_value-- : 0;
|
|
|
|
DEBUG("%s: FDCAN%u new LSS value is %u\n",
|
|
__func__, get_channel_id(can), filter_id);
|
|
}
|
|
|
|
static void unset_extended_filter(FDCAN_GlobalTypeDef *can, uint8_t filter_id)
|
|
{
|
|
/* Filter List Extended start at
|
|
RAM address + FLESA (0x70) + extended filter offset (8 Bytes * filter_id) */
|
|
uint32_t *fle_ram_address_f0 = get_flesa_message_ram(can)
|
|
+ FDCAN_SRAM_FLE_FILTER_SIZE * filter_id;
|
|
/* Disable filter */
|
|
*fle_ram_address_f0 &= ~FDCAN_SRAM_FLE_F0_EFEC;
|
|
|
|
DEBUG("%s: FDCAN%u extended filter %u F0 value is %lx\n",
|
|
__func__, get_channel_id(can),
|
|
filter_id,
|
|
*fle_ram_address_f0
|
|
);
|
|
|
|
/* Update LSE value */
|
|
uint8_t lse_value = (can->RXGFC & FDCAN_RXGFC_LSE) >> FDCAN_RXGFC_LSE_Pos;
|
|
can->RXGFC &= ~FDCAN_RXGFC_LSE;
|
|
can->RXGFC |= lse_value > 0 ? lse_value-- : 0;
|
|
|
|
DEBUG("%s: FDCAN%u new LSE value is %u\n",
|
|
__func__, get_channel_id(can), filter_id);
|
|
}
|
|
|
|
/**
|
|
* Select unset function for standard or extended filter.
|
|
* Extended filters can filter standard messages
|
|
*/
|
|
static void unset_filter(FDCAN_GlobalTypeDef *can, uint8_t filter_id)
|
|
{
|
|
/* Check ID and mask, to use an extended filter if needed */
|
|
if (filter_id < FDCAN_STM32_NB_STD_FILTER) {
|
|
unset_standard_filter(can, filter_id);
|
|
}
|
|
else {
|
|
unset_extended_filter(can, filter_id - FDCAN_STM32_NB_STD_FILTER);
|
|
}
|
|
}
|
|
|
|
void candev_stm32_set_pins(can_t *dev, gpio_t tx_pin, gpio_t rx_pin,
|
|
gpio_af_t af)
|
|
{
|
|
FDCAN_GlobalTypeDef *can = dev->conf->can;
|
|
|
|
if (dev->tx_pin != GPIO_UNDEF) {
|
|
gpio_init(dev->tx_pin, GPIO_IN);
|
|
gpio_init_analog(dev->tx_pin);
|
|
}
|
|
if (dev->rx_pin != GPIO_UNDEF) {
|
|
gpio_init(dev->rx_pin, GPIO_IN);
|
|
gpio_init_analog(dev->rx_pin);
|
|
}
|
|
dev->tx_pin = tx_pin;
|
|
dev->rx_pin = rx_pin;
|
|
|
|
DEBUG("%s: FDCAN%u GPIO RX = %lu\n",
|
|
__func__, get_channel_id(can), rx_pin);
|
|
DEBUG("%s: FDCAN%u GPIO TX = %lu\n",
|
|
__func__, get_channel_id(can), tx_pin);
|
|
|
|
dev->af = af;
|
|
gpio_init_af(rx_pin, af);
|
|
gpio_init_af(tx_pin, af);
|
|
}
|
|
|
|
static int _init(candev_t *candev)
|
|
{
|
|
can_t *dev = container_of(candev, can_t, candev);
|
|
FDCAN_GlobalTypeDef *can = dev->conf->can;
|
|
|
|
_can[get_channel(can)] = dev;
|
|
|
|
/* Erase TX mailbox and RX FIFO */
|
|
memset(dev->tx_mailbox, 0, sizeof(dev->tx_mailbox));
|
|
memset(&dev->rx_mailbox, 0, sizeof(dev->rx_mailbox));
|
|
|
|
dev->isr_flags.isr_tx = 0;
|
|
dev->isr_flags.isr_rx = 0;
|
|
|
|
/* Enable device clock */
|
|
periph_clk_en(APB1, dev->conf->rcc_mask);
|
|
|
|
DEBUG("%s: FDCAN%u RCC->CCIPR = %lx\n",
|
|
__func__, get_channel_id(can), RCC->CCIPR);
|
|
|
|
_status[get_channel(can)] = STATUS_ON;
|
|
|
|
/* configure pins */
|
|
candev_stm32_set_pins(dev, dev->conf->tx_pin, dev->conf->rx_pin, dev->conf->af);
|
|
|
|
set_mode(can, MODE_INIT);
|
|
|
|
set_bit_timing(dev);
|
|
|
|
/* Enable interrupts */
|
|
NVIC_EnableIRQ(dev->conf->it0_irqn);
|
|
NVIC_EnableIRQ(dev->conf->it1_irqn);
|
|
|
|
can->CCCR &= ~(FDCAN_CCCR_DAR /* Enable auto retransmission on failure */
|
|
| FDCAN_CCCR_PXHD); /* Enable protocol exception handling */
|
|
can->CCCR |= (FDCAN_CCCR_FDOE /* Enable FD mode */
|
|
| FDCAN_CCCR_BRSE /* Enable bitrate switching */
|
|
| FDCAN_CCCR_TXP); /* Enable transmit pause */
|
|
|
|
#ifdef STM32_PM_STOP
|
|
pm_block(STM32_PM_STOP);
|
|
#endif
|
|
|
|
/* Clear interrupt flags */
|
|
can->IR |= 0xFFFFFFU;
|
|
/* Enable all interrupts and both interrupt lines */
|
|
can->IE |= 0xFFFFFFU;
|
|
|
|
can->TXBTIE = FDCAN_TXBTIE_TIE;
|
|
|
|
can->ILS = FDCAN_ILS_PERR | FDCAN_ILS_SMSG;
|
|
can->ILE |= FDCAN_ILE_EINT0 | FDCAN_ILE_EINT1;
|
|
|
|
DEBUG("%s: FDCAN%u->CCCR = %lx\n",
|
|
__func__, get_channel_id(can), can->CCCR);
|
|
|
|
int res = set_mode(can, MODE_NORMAL);
|
|
|
|
DEBUG("%s: FDCAN%u init done, return %d\n",
|
|
__func__, get_channel_id(can), res);
|
|
|
|
/* Reject non matching standard IDs */
|
|
can->RXGFC |= FDCAN_RXGFC_ANFS;
|
|
/* Reject non matching extended IDs */
|
|
can->RXGFC |= FDCAN_RXGFC_ANFE;
|
|
|
|
return res;
|
|
}
|
|
|
|
static inline void set_bit_timing(can_t *dev)
|
|
{
|
|
FDCAN_GlobalTypeDef *can = dev->conf->can;
|
|
|
|
DEBUG("%s: FDCAN%u setup bittiming\n",
|
|
__func__, get_channel_id(can));
|
|
/* Normal CAN bittiming */
|
|
can->NBTP = 0;
|
|
can->NBTP = (dev->candev.bittiming.sjw - 1)
|
|
<< FDCAN_NBTP_NSJW_Pos /* SJW */
|
|
| (dev->candev.bittiming.phase_seg2 - 1)
|
|
<< FDCAN_NBTP_NTSEG2_Pos /* Phase Seg 2 */
|
|
| (dev->candev.bittiming.phase_seg1
|
|
+ dev->candev.bittiming.prop_seg - 1)
|
|
<< FDCAN_NBTP_NTSEG1_Pos /* Phase Seg 1 */
|
|
| (dev->candev.bittiming.brp - 1)
|
|
<< FDCAN_NBTP_NBRP_Pos; /* BRP */
|
|
|
|
DEBUG("%s: FDCAN%u->NBTP = %lx\n",
|
|
__func__, get_channel_id(can), can->NBTP);
|
|
|
|
/* FD CAN bittiming */
|
|
can->DBTP = 0;
|
|
can->DBTP = (dev->candev.fd_data_bittiming.sjw - 1)
|
|
<< FDCAN_DBTP_DSJW_Pos /* SJW */
|
|
| (dev->candev.fd_data_bittiming.phase_seg2 - 1)
|
|
<< FDCAN_DBTP_DTSEG2_Pos /* Phase Seg 2 */
|
|
| (dev->candev.fd_data_bittiming.phase_seg1
|
|
+ dev->candev.fd_data_bittiming.prop_seg - 1)
|
|
<< FDCAN_DBTP_DTSEG1_Pos /* Phase Seg 1 */
|
|
| (dev->candev.fd_data_bittiming.brp - 1)
|
|
<< FDCAN_DBTP_DBRP_Pos /* BRP */
|
|
| FDCAN_DBTP_TDC; /* Transceiver delay compensation */
|
|
|
|
DEBUG("%s: FDCAN%u->DBTP = %lx\n",
|
|
__func__, get_channel_id(can), can->DBTP);
|
|
|
|
can->TDCR &= ~FDCAN_TDCR_TDCO;
|
|
can->TDCR |= (dev->candev.loop_delay / dev->candev.bittiming.tq) << FDCAN_TDCR_TDCO_Pos;
|
|
|
|
DEBUG("%s: FDCAN%u->TDCR = %lx\n",
|
|
__func__, get_channel_id(can), can->TDCR);
|
|
}
|
|
|
|
static uint32_t dlc_to_len(__uint32_t dlc) {
|
|
if (dlc <= 8) {
|
|
return dlc;
|
|
}
|
|
switch (dlc)
|
|
{
|
|
case 9:
|
|
return 12;
|
|
case 10:
|
|
return 16;
|
|
case 11:
|
|
return 20;
|
|
case 12:
|
|
return 24;
|
|
case 13:
|
|
return 32;
|
|
case 14:
|
|
return 48;
|
|
case 15:
|
|
return 64;
|
|
default:
|
|
DEBUG("%s: FDCAN DLC greater than 15, should never happens, consider it as 15",
|
|
__func__);
|
|
return 64;
|
|
}
|
|
}
|
|
|
|
static uint32_t len_to_dlc(uint32_t len) {
|
|
if (len <= 8) {
|
|
return len;
|
|
}
|
|
else if (len <= 12) {
|
|
return 9;
|
|
}
|
|
else if (len <= 16) {
|
|
return 10;
|
|
}
|
|
else if (len <= 20) {
|
|
return 11;
|
|
}
|
|
else if (len <= 24) {
|
|
return 12;
|
|
}
|
|
else if (len <= 32) {
|
|
return 13;
|
|
}
|
|
else if (len <= 48) {
|
|
return 14;
|
|
}
|
|
else if (len <= 64) {
|
|
return 15;
|
|
}
|
|
else {
|
|
DEBUG("%s: FDCAN payload size greater than 64, should never happens, consider it as 64",
|
|
__func__);
|
|
return 15;
|
|
}
|
|
}
|
|
|
|
static int _send(candev_t *candev, const can_frame_t *frame)
|
|
{
|
|
can_t *dev = container_of(candev, can_t, candev);
|
|
FDCAN_GlobalTypeDef *can = dev->conf->can;
|
|
|
|
DEBUG("%s: FDCAN%u candev=%p, frame=%p\n",
|
|
__func__, get_channel_id(can),
|
|
(void *) candev, (void *) frame);
|
|
|
|
/* Check Tx FIFO is not full */
|
|
if (can->TXFQS & FDCAN_TXFQS_TFQF) {
|
|
DEBUG("%s: FDCAN%u Tx FIFO is full\n",
|
|
__func__, get_channel_id(can));
|
|
return -ENOMEM;
|
|
}
|
|
|
|
/* Get free Tx buffer element */
|
|
uint32_t put_index = (can->TXFQS & FDCAN_TXFQS_TFQPI) >> FDCAN_TXFQS_TFQPI_Pos;
|
|
|
|
dev->tx_mailbox[put_index] = frame;
|
|
|
|
uint32_t* tx_buffer_element_t0 = get_message_ram_tx_buffer_address(can)
|
|
+ (put_index * FDCAN_SRAM_TXBUFFER_SIZE);
|
|
memset(tx_buffer_element_t0, 0, FDCAN_SRAM_TXBUFFER_SIZE * 4);
|
|
uint32_t* tx_buffer_element_t1 = tx_buffer_element_t0 + 1;
|
|
uint32_t* tx_buffer_element_data = tx_buffer_element_t1 + 1;
|
|
DEBUG("%s: FDCAN%u tx_buffer_element_t0 = %p\n",
|
|
__func__, get_channel_id(can), tx_buffer_element_t0);
|
|
DEBUG("%s: FDCAN%u tx_buffer_element_t1 = %p\n",
|
|
__func__, get_channel_id(can), tx_buffer_element_t1);
|
|
DEBUG("%s: FDCAN%u tx_buffer_element_data = %p\n",
|
|
__func__, get_channel_id(can), tx_buffer_element_data);
|
|
|
|
/* Check identifier */
|
|
if ((frame->can_id & ~CAN_EFF_FLAG) > CAN_EFF_MASK) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* Determine if it a standard or extended identifier */
|
|
uint32_t xtd_bit = ((((frame->can_id & CAN_EFF_FLAG) == CAN_EFF_FLAG)
|
|
|| (frame->flags & CANFD_FDF)) ? 1 : 0) << FDCAN_SRAM_TXBUFFER_T0_XTD_Pos;
|
|
DEBUG("%s: FDCAN%u xtd_bit = %lx\n",
|
|
__func__, get_channel_id(can), xtd_bit);
|
|
|
|
/* Shift standard ID of 18 bits */
|
|
uint32_t id = (xtd_bit ? (frame->can_id & CAN_EFF_MASK)
|
|
: ((frame->can_id & CAN_SFF_MASK) << FDCAN_SRAM_TXBUFFER_T0_ID_Pos));
|
|
DEBUG("%s: FDCAN%u id = %lx\n",
|
|
__func__, get_channel_id(can), id);
|
|
|
|
uint32_t rtr_bit = ((frame->can_id & CAN_RTR_FLAG) ? 1 : 0)
|
|
<< FDCAN_SRAM_TXBUFFER_T0_RTR_Pos;
|
|
DEBUG("%s: FDCAN%u rtr_bit = %lx\n",
|
|
__func__, get_channel_id(can), rtr_bit);
|
|
|
|
*tx_buffer_element_t0 =
|
|
((((frame->flags & CANFD_ESI) ? 1 : 0) << FDCAN_SRAM_TXBUFFER_T0_ESI_Pos)
|
|
/* The Error State Indicator (ESI) depends of error passive flag */
|
|
| xtd_bit /* Message ID identifier type (11/29 bits) */
|
|
| rtr_bit /* Always a data frame */
|
|
| id); /* Message ID */
|
|
|
|
/* Bit rate switching is specific to FDCAN. */
|
|
if (!(frame->flags & CANFD_FDF) && (frame->flags & CANFD_BRS)) {
|
|
DEBUG("%s: FDCAN%u frame %lu BRS is enabled on a non-fd frame\n",
|
|
__func__, get_channel_id(can), frame->can_id);
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* Check length */
|
|
if ((frame->flags & CANFD_FDF) && (frame->len > CANFD_MAX_DLEN)) {
|
|
DEBUG("%s: FDCAN%u frame %lu len is upper than 64 bytes (%d)\n",
|
|
__func__, get_channel_id(can), frame->can_id, frame->len);
|
|
return -EINVAL;
|
|
}
|
|
if (!(frame->flags & CANFD_FDF) && (frame->len > CAN_MAX_DLEN)) {
|
|
DEBUG("%s: FDCAN%u frame %lu len is upper than 8 bytes (%d)\n",
|
|
__func__, get_channel_id(can), frame->can_id, frame->len);
|
|
return -EINVAL;
|
|
}
|
|
|
|
*tx_buffer_element_t1 = 0;
|
|
*tx_buffer_element_t1 = FDCAN_SRAM_TXBUFFER_T1_EFC_DISABLE /* ESI bit */
|
|
| ((frame->flags & CANFD_BRS)
|
|
? FDCAN_SRAM_TXBUFFER_T1_BRS_ON
|
|
: FDCAN_SRAM_TXBUFFER_T1_BRS_OFF) /* Bit Rate Switching */
|
|
| ((frame->flags & CANFD_FDF)
|
|
? FDCAN_SRAM_TXBUFFER_T1_FDF_FD
|
|
: FDCAN_SRAM_TXBUFFER_T1_FDF_CLASSIC) /* CAN FD frame */
|
|
| (len_to_dlc(frame->len)
|
|
<< FDCAN_SRAM_TXBUFFER_T1_DLC_Pos); /* Data Length Code*/
|
|
|
|
DEBUG("%s: FDCAN%u *tx_buffer_element_t0 = %lx\n",
|
|
__func__, get_channel_id(can), *tx_buffer_element_t0);
|
|
DEBUG("%s: FDCAN%u *tx_buffer_element_t1 = %lx\n",
|
|
__func__, get_channel_id(can), *tx_buffer_element_t1);
|
|
|
|
/* Set data */
|
|
for (uint8_t byte = 0; byte < frame->len && !rtr_bit; byte++) {
|
|
*tx_buffer_element_data |= (uint32_t)frame->data[byte] << ((byte % 4) * 8);
|
|
DEBUG("%s: FDCAN%u *tx_buffer_element_data[%u] = %lx\n",
|
|
__func__, get_channel_id(can),
|
|
byte / 4,
|
|
*tx_buffer_element_data);
|
|
|
|
if (byte % 4 == 3) {
|
|
/* Increment buffer pointer */
|
|
++tx_buffer_element_data;
|
|
|
|
DEBUG("%s: FDCAN%u tx_buffer_element_data = %p\n",
|
|
__func__, get_channel_id(can), tx_buffer_element_data);
|
|
}
|
|
}
|
|
|
|
/* Request transmission */
|
|
can->TXBAR = 1 << put_index;
|
|
|
|
return put_index;
|
|
}
|
|
|
|
static int _abort(candev_t *candev, const can_frame_t *frame)
|
|
{
|
|
can_t *dev = container_of(candev, can_t, candev);
|
|
FDCAN_GlobalTypeDef *can = dev->conf->can;
|
|
int mailbox = 0;
|
|
|
|
for (mailbox = 0; mailbox < FDCAN_STM32_TX_MAILBOXES; mailbox++) {
|
|
if (dev->tx_mailbox[mailbox] == frame) {
|
|
break;
|
|
}
|
|
}
|
|
if (mailbox == FDCAN_STM32_TX_MAILBOXES) {
|
|
return -EOVERFLOW;
|
|
}
|
|
|
|
can->TXBCR = 1 << (uint32_t)mailbox;
|
|
dev->tx_mailbox[mailbox] = NULL;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int read_frame(can_t *dev, can_frame_t *frame, int message_ram_rx_fifo)
|
|
{
|
|
FDCAN_GlobalTypeDef *can = dev->conf->can;
|
|
|
|
DEBUG("%s: FDCAN%u Rx FIFO%u is used\n",
|
|
__func__, get_channel_id(can),
|
|
message_ram_rx_fifo);
|
|
|
|
uint32_t get_index = 0;
|
|
|
|
if (message_ram_rx_fifo) {
|
|
get_index = (can->RXF1S & FDCAN_RXF1S_F1GI) >> FDCAN_RXF1S_F1GI_Pos;
|
|
}
|
|
else {
|
|
get_index = (can->RXF0S & FDCAN_RXF0S_F0GI) >> FDCAN_RXF0S_F0GI_Pos;
|
|
}
|
|
|
|
uint32_t* rx_fifo_element_r0 = get_message_ram_rx_fifo_address(can, message_ram_rx_fifo)
|
|
+ get_index * FDCAN_SRAM_RXFIFO_ELEMENT_SIZE;
|
|
uint32_t* rx_fifo_element_r1 = rx_fifo_element_r0 + 1;
|
|
uint8_t* rx_fifo_element_data = (uint8_t*)rx_fifo_element_r1 + 4;
|
|
|
|
DEBUG("%s: FDCAN%u rx_fifo_element_r0 = %p\n",
|
|
__func__, get_channel_id(can), rx_fifo_element_r0);
|
|
DEBUG("%s: FDCAN%u rx_fifo_element_r1 = %p\n",
|
|
__func__, get_channel_id(can), rx_fifo_element_r1);
|
|
DEBUG("%s: FDCAN%u *rx_fifo_element_r0 = %lx\n",
|
|
__func__, get_channel_id(can), *rx_fifo_element_r0);
|
|
DEBUG("%s: FDCAN%u *rx_fifo_element_r1 = %lx\n",
|
|
__func__, get_channel_id(can), *rx_fifo_element_r1);
|
|
|
|
/* Check identifier */
|
|
if ((frame->can_id & ~CAN_EFF_FLAG) > CAN_EFF_MASK) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* Determine if it is a standard or extended identifier */
|
|
if (*rx_fifo_element_r0 & FDCAN_SRAM_RXFIFO_R0_XTD) {
|
|
frame->can_id = *rx_fifo_element_r0 & FDCAN_SRAM_RXFIFO_R0_ID;
|
|
frame->can_id |= CAN_EFF_FLAG;
|
|
}
|
|
else {
|
|
frame->can_id = (*rx_fifo_element_r0 & FDCAN_SRAM_RXFIFO_R0_ID)
|
|
>> FDCAN_SRAM_RXFIFO_R0_ID_Pos;
|
|
}
|
|
|
|
/* Check if it is a RTR frame */
|
|
if (*rx_fifo_element_r0 & FDCAN_SRAM_RXFIFO_R0_RTR) {
|
|
frame->can_id |= CAN_RTR_FLAG;
|
|
}
|
|
|
|
/* Check if it is a CAN FD frame */
|
|
if (*rx_fifo_element_r1 & FDCAN_SRAM_RXFIFO_R1_FDF) {
|
|
frame->flags |= CANFD_FDF;
|
|
}
|
|
|
|
/* Check if Bit Rate Switching is on */
|
|
if (*rx_fifo_element_r1 & FDCAN_SRAM_RXFIFO_R1_BRS) {
|
|
frame->flags |= CANFD_BRS;
|
|
}
|
|
|
|
frame->len = dlc_to_len((*rx_fifo_element_r1 & FDCAN_SRAM_RXFIFO_R1_DLC)
|
|
>> FDCAN_SRAM_RXFIFO_R1_DLC_Pos);
|
|
|
|
/* Get Data */
|
|
for (uint8_t byte = 0; byte < frame->len && !(frame->can_id & CAN_RTR_FLAG); byte++) {
|
|
DEBUG("%s: FDCAN%u rx_fifo_element_data = %p\n",
|
|
__func__, get_channel_id(can), rx_fifo_element_data);
|
|
DEBUG("%s: FDCAN%u *rx_fifo_element_data = %x\n",
|
|
__func__, get_channel_id(can), *rx_fifo_element_data);
|
|
frame->data[byte] = *rx_fifo_element_data;
|
|
rx_fifo_element_data++;
|
|
}
|
|
|
|
if (message_ram_rx_fifo) {
|
|
can->RXF1A = get_index;
|
|
}
|
|
else {
|
|
can->RXF0A = get_index;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void _isr(candev_t *candev)
|
|
{
|
|
can_t *dev = container_of(candev, can_t, candev);
|
|
|
|
if (dev->isr_flags.isr_tx) {
|
|
tx_isr(dev);
|
|
}
|
|
|
|
if (dev->isr_flags.isr_wkup) {
|
|
if (dev->candev.event_callback) {
|
|
dev->candev.event_callback(&dev->candev, CANDEV_EVENT_WAKE_UP, NULL);
|
|
}
|
|
}
|
|
|
|
unsigned int irq;
|
|
|
|
irq = irq_disable();
|
|
if (dev->isr_flags.isr_rx & 1) {
|
|
dev->isr_flags.isr_rx &= ~1;
|
|
irq_restore(irq);
|
|
rx_isr(dev);
|
|
}
|
|
else {
|
|
irq_restore(irq);
|
|
}
|
|
|
|
irq = irq_disable();
|
|
if (dev->isr_flags.isr_rx & 2) {
|
|
dev->isr_flags.isr_rx &= ~2;
|
|
irq_restore(irq);
|
|
rx_isr(dev);
|
|
}
|
|
else {
|
|
irq_restore(irq);
|
|
}
|
|
}
|
|
|
|
static void _wkup_cb(void *arg)
|
|
{
|
|
can_t *dev = arg;
|
|
FDCAN_GlobalTypeDef *can = dev->conf->can;
|
|
|
|
gpio_irq_disable(dev->rx_pin);
|
|
|
|
DEBUG("%s: FDCAN%u int wkup: %p\n", __func__, get_channel_id(can), arg);
|
|
|
|
dev->isr_flags.isr_wkup = 1;
|
|
|
|
if (dev->candev.event_callback) {
|
|
dev->candev.event_callback(&dev->candev, CANDEV_EVENT_ISR, NULL);
|
|
}
|
|
}
|
|
|
|
/* Disable GPIO Rx interrupt on wake up */
|
|
static void disable_gpio_int(can_t *dev)
|
|
{
|
|
FDCAN_GlobalTypeDef *can = dev->conf->can;
|
|
|
|
DEBUG("%s: FDCAN%u disable Rx GPIO interrupt\n",
|
|
__func__, get_channel_id(can));
|
|
|
|
gpio_irq_disable(dev->rx_pin);
|
|
candev_stm32_set_pins(dev, dev->tx_pin, dev->rx_pin, dev->af);
|
|
}
|
|
|
|
/* Enable GPIO Rx interrupt on sleep */
|
|
static void enable_gpio_int(can_t *dev)
|
|
{
|
|
FDCAN_GlobalTypeDef *can = dev->conf->can;
|
|
|
|
DEBUG("%s: FDCAN%u enable Rx GPIO interrupt\n",
|
|
__func__, get_channel_id(can));
|
|
|
|
gpio_init_int(dev->rx_pin, GPIO_IN, GPIO_FALLING, _wkup_cb, dev);
|
|
}
|
|
|
|
static void turn_off(can_t *dev)
|
|
{
|
|
FDCAN_GlobalTypeDef *can = dev->conf->can;
|
|
|
|
DEBUG("%s: FDCAN%u turn off (%p)\n", __func__, get_channel_id(can), (void *)dev);
|
|
|
|
unsigned irq = irq_disable();
|
|
|
|
if (_status[get_channel(dev->conf->can)] != STATUS_SLEEP) {
|
|
#ifdef STM32_PM_STOP
|
|
pm_unblock(STM32_PM_STOP);
|
|
#endif
|
|
}
|
|
|
|
_status[get_channel(dev->conf->can)] = STATUS_SLEEP;
|
|
|
|
if (dev->conf->en_deep_sleep_wake_up) {
|
|
periph_clk_dis(APB1, dev->conf->rcc_mask);
|
|
enable_gpio_int(dev);
|
|
}
|
|
|
|
irq_restore(irq);
|
|
}
|
|
|
|
static void turn_on(can_t *dev)
|
|
{
|
|
FDCAN_GlobalTypeDef *can = dev->conf->can;
|
|
|
|
DEBUG("%s: FDCAN%u turn on (%p)\n",
|
|
__func__, get_channel_id(can), dev);
|
|
|
|
unsigned irq = irq_disable();
|
|
|
|
if (_status[get_channel(dev->conf->can)] == STATUS_SLEEP) {
|
|
#ifdef STM32_PM_STOP
|
|
pm_block(STM32_PM_STOP);
|
|
#endif
|
|
if (dev->conf->en_deep_sleep_wake_up) {
|
|
disable_gpio_int(dev);
|
|
}
|
|
periph_clk_en(APB1, dev->conf->rcc_mask);
|
|
}
|
|
|
|
_status[get_channel(dev->conf->can)] = STATUS_ON;
|
|
|
|
irq_restore(irq);
|
|
}
|
|
|
|
static int _wake_up(can_t *dev)
|
|
{
|
|
turn_on(dev);
|
|
return set_mode(dev->conf->can, MODE_NORMAL);
|
|
}
|
|
|
|
static int _sleep(can_t *dev)
|
|
{
|
|
int res = set_mode(dev->conf->can, MODE_SLEEP);
|
|
turn_off(dev);
|
|
return res;
|
|
}
|
|
|
|
static int _set(candev_t *candev, canopt_t opt, void *value, size_t value_len)
|
|
{
|
|
can_t *dev = container_of(candev, can_t, candev);
|
|
FDCAN_GlobalTypeDef *can = dev->conf->can;
|
|
int res = 0;
|
|
can_mode_t mode;
|
|
|
|
switch (opt) {
|
|
case CANOPT_BITTIMING:
|
|
DEBUG("%s: FDCAN%u set bittiming\n", __func__, get_channel_id(can));
|
|
if (value_len < sizeof(dev->candev.bittiming)) {
|
|
res = -EOVERFLOW;
|
|
}
|
|
else {
|
|
memcpy(&dev->candev.bittiming, value, sizeof(dev->candev.bittiming));
|
|
mode = get_mode(can);
|
|
if (mode == MODE_SLEEP) {
|
|
res = _wake_up(dev);
|
|
if (res != 0) {
|
|
res = -EBUSY;
|
|
break;
|
|
}
|
|
}
|
|
res = set_mode(can, MODE_INIT);
|
|
if (res == 0) {
|
|
set_bit_timing(dev);
|
|
res = sizeof(dev->candev.bittiming);
|
|
}
|
|
if (mode == MODE_SLEEP) {
|
|
if (_sleep(dev) < 0) {
|
|
res = -EBUSY;
|
|
}
|
|
}
|
|
else if (set_mode(can, mode) < 0) {
|
|
res = -EBUSY;
|
|
}
|
|
}
|
|
break;
|
|
case CANOPT_FD_BITTIMING:
|
|
DEBUG("%s: FDCAN%u set FDCAN data bittiming\n", __func__, get_channel_id(can));
|
|
if (value_len < sizeof(dev->candev.fd_data_bittiming)) {
|
|
res = -EOVERFLOW;
|
|
}
|
|
else {
|
|
memcpy(&dev->candev.fd_data_bittiming, value,
|
|
sizeof(dev->candev.fd_data_bittiming));
|
|
mode = get_mode(can);
|
|
if (mode == MODE_SLEEP) {
|
|
res = _wake_up(dev);
|
|
if (res != 0) {
|
|
res = -EBUSY;
|
|
break;
|
|
}
|
|
}
|
|
res = set_mode(can, MODE_INIT);
|
|
if (res == 0) {
|
|
set_bit_timing(dev);
|
|
res = sizeof(dev->candev.fd_data_bittiming);
|
|
}
|
|
if (mode == MODE_SLEEP) {
|
|
if (_sleep(dev) < 0) {
|
|
res = -EBUSY;
|
|
}
|
|
}
|
|
else if (set_mode(can, mode) < 0) {
|
|
res = -EBUSY;
|
|
}
|
|
}
|
|
break;
|
|
case CANOPT_STATE:
|
|
if (value_len < sizeof(canopt_state_t)) {
|
|
res = -EOVERFLOW;
|
|
}
|
|
else {
|
|
switch (*((canopt_state_t *)value)) {
|
|
case CANOPT_STATE_OFF:
|
|
case CANOPT_STATE_SLEEP:
|
|
DEBUG("%s: FDCAN%u power down\n", __func__, get_channel_id(can));
|
|
res = _sleep(dev);
|
|
break;
|
|
case CANOPT_STATE_ON:
|
|
DEBUG("%s: FDCAN%u power up\n", __func__, get_channel_id(can));
|
|
res = _wake_up(dev);
|
|
break;
|
|
case CANOPT_STATE_LISTEN_ONLY:
|
|
DEBUG("%s: FDCAN%u listen only\n", __func__, get_channel_id(can));
|
|
mode = get_mode(can);
|
|
res = set_mode(can, MODE_INIT);
|
|
can->CCCR |= FDCAN_CCCR_MON;
|
|
res += set_mode(can, mode);
|
|
break;
|
|
case CANOPT_STATE_LOOPBACK:
|
|
DEBUG("%s: FDCAN%u loopback\n", __func__, get_channel_id(can));
|
|
res = set_mode(can, MODE_INIT);
|
|
can->TEST |= FDCAN_TEST_LBCK;
|
|
can->CCCR |= FDCAN_CCCR_MON;
|
|
res += set_mode(can, MODE_NORMAL);
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
res = -ENOTSUP;
|
|
break;
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
static int _get(candev_t *candev, canopt_t opt, void *value, size_t max_len)
|
|
{
|
|
can_t *dev = container_of(candev, can_t, candev);
|
|
FDCAN_GlobalTypeDef *can = dev->conf->can;
|
|
int res = 0;
|
|
|
|
switch (opt) {
|
|
case CANOPT_BITTIMING:
|
|
if (max_len < sizeof(dev->candev.bittiming)) {
|
|
res = -EOVERFLOW;
|
|
}
|
|
else {
|
|
memcpy(value, &dev->candev.bittiming, sizeof(dev->candev.bittiming));
|
|
res = sizeof(dev->candev.bittiming);
|
|
}
|
|
break;
|
|
case CANOPT_BITTIMING_CONST:
|
|
if (max_len < sizeof(bittiming_const)) {
|
|
res = -EOVERFLOW;
|
|
}
|
|
else {
|
|
memcpy(value, &bittiming_const, sizeof(bittiming_const));
|
|
res = sizeof(bittiming_const);
|
|
}
|
|
break;
|
|
case CANOPT_FD_BITTIMING:
|
|
if (max_len < sizeof(dev->candev.fd_data_bittiming)) {
|
|
res = -EOVERFLOW;
|
|
}
|
|
else {
|
|
memcpy(value, &dev->candev.fd_data_bittiming,
|
|
sizeof(dev->candev.fd_data_bittiming));
|
|
res = sizeof(dev->candev.fd_data_bittiming);
|
|
}
|
|
break;
|
|
case CANOPT_FD_BITTIMING_CONST:
|
|
if (max_len < sizeof(fd_data_bittiming_const)) {
|
|
res = -EOVERFLOW;
|
|
}
|
|
else {
|
|
memcpy(value, &fd_data_bittiming_const, sizeof(fd_data_bittiming_const));
|
|
res = sizeof(fd_data_bittiming_const);
|
|
}
|
|
break;
|
|
case CANOPT_CLOCK:
|
|
if (max_len < sizeof(uint32_t)) {
|
|
res = -EOVERFLOW;
|
|
}
|
|
else {
|
|
*((uint32_t *)value) = CLOCK_APB1;
|
|
res = sizeof(uint32_t);
|
|
}
|
|
break;
|
|
case CANOPT_RX_FILTERS:
|
|
if (max_len % sizeof(struct can_filter) != 0) {
|
|
res = -EOVERFLOW;
|
|
}
|
|
else {
|
|
uint8_t nb_enabled_filter = 0;
|
|
struct can_filter *filter_list = (struct can_filter *)value;
|
|
size_t filter_list_size = max_len / sizeof(struct can_filter);
|
|
for (unsigned i = 0; i < FDCAN_STM32_NB_FILTER; i++) {
|
|
struct can_filter *filter = filter_list + nb_enabled_filter;
|
|
if (filter_is_set(can, i)) {
|
|
nb_enabled_filter++;
|
|
get_can_filter(can, i, &filter->can_id, &filter->can_mask);
|
|
}
|
|
if (filter_list_size <= nb_enabled_filter) {
|
|
res = -EOVERFLOW;
|
|
break;
|
|
}
|
|
}
|
|
if (!res) {
|
|
res = nb_enabled_filter * sizeof(struct can_filter);
|
|
}
|
|
}
|
|
break;
|
|
case CANOPT_TEC:
|
|
if (max_len != sizeof(uint16_t)) {
|
|
res = -EOVERFLOW;
|
|
}
|
|
else {
|
|
uint16_t *tec = (uint16_t *)value;
|
|
*tec = (can->ECR & FDCAN_ECR_TEC) >> FDCAN_ECR_TEC_Pos;
|
|
res = sizeof(uint16_t);
|
|
}
|
|
break;
|
|
case CANOPT_REC:
|
|
if (max_len != sizeof(uint16_t)) {
|
|
res = -EOVERFLOW;
|
|
}
|
|
else {
|
|
uint16_t *rec = (uint16_t *)value;
|
|
*rec = (can->ECR & FDCAN_ECR_REC) >> FDCAN_ECR_REC_Pos;
|
|
res = sizeof(uint16_t);
|
|
}
|
|
break;
|
|
default:
|
|
res = -ENOTSUP;
|
|
break;
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
static int _set_filter(candev_t *candev, const struct can_filter *filter)
|
|
{
|
|
can_t *dev = container_of(candev, can_t, candev);
|
|
FDCAN_GlobalTypeDef *can = dev->conf->can;
|
|
int res = 0;
|
|
|
|
DEBUG("%s: FDCAN%u dev=%p, filter=0x%" PRIx32 ", mask=0x%" PRIx32 "\n", __func__,
|
|
get_channel_id(can), (void *)candev, filter->can_id, filter->can_mask);
|
|
|
|
uint8_t i;
|
|
for (i = 0; i < FDCAN_STM32_NB_FILTER; i++) {
|
|
if (!filter_is_set(can, i)
|
|
&& ((i >= FDCAN_STM32_NB_STD_FILTER) /* First free filter slot is an extended
|
|
filter */
|
|
|| (!(filter->can_id & ~CAN_SFF_MASK)
|
|
&& !(filter->can_mask & ~CAN_SFF_MASK)))) { /* or the filter is standard */
|
|
can_mode_t mode = get_mode(can);
|
|
set_mode(can, MODE_INIT);
|
|
res = set_filter(can, filter->can_id, filter->can_mask, i, i % FDCAN_STM32_RX_MAILBOXES);
|
|
set_mode(can, mode);
|
|
if (res) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (i == FDCAN_STM32_NB_FILTER) {
|
|
return -EOVERFLOW;
|
|
}
|
|
|
|
return i;
|
|
}
|
|
|
|
static int _remove_filter(candev_t *candev, const struct can_filter *filter)
|
|
{
|
|
can_t *dev = container_of(candev, can_t, candev);
|
|
FDCAN_GlobalTypeDef *can = dev->conf->can;
|
|
|
|
uint8_t i;
|
|
|
|
for (i = 0; i < FDCAN_STM32_NB_FILTER; i++) {
|
|
if (filter_is_set(can, i)) {
|
|
uint32_t filt, mask;
|
|
get_can_filter(can, i, &filt, &mask);
|
|
DEBUG("%s: FDCAN%u filter=0x%" PRIx32 ",0x%" PRIx32 ", nb=%d, "
|
|
"dev_filter=0x%" PRIx32 ",0x%" PRIx32 "\n", __func__, get_channel_id(can),
|
|
filter->can_id, filter->can_mask, (int)i, filt, mask);
|
|
if ((filt == filter->can_id) /* ID match */
|
|
/* Filter match (extended case) */
|
|
&& (((filt & CAN_EFF_FLAG)
|
|
&& ((mask & CAN_EFF_MASK) == (filter->can_mask & CAN_EFF_MASK)))
|
|
/* Filter match (standard case) */
|
|
|| (!(filt & CAN_EFF_FLAG)
|
|
&& ((mask & CAN_SFF_MASK) == (filter->can_mask & CAN_SFF_MASK))))) {
|
|
can_mode_t mode = get_mode(can);
|
|
set_mode(can, MODE_INIT);
|
|
unset_filter(can, i);
|
|
set_mode(can, mode);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void tx_conf(can_t *dev, int mailbox)
|
|
{
|
|
candev_t *candev = (candev_t *) dev;
|
|
FDCAN_GlobalTypeDef *can = dev->conf->can;
|
|
const can_frame_t *frame = dev->tx_mailbox[mailbox];
|
|
|
|
dev->tx_mailbox[mailbox] = NULL;
|
|
|
|
DEBUG("%s: FDCAN%u, mailbox=%d\n",
|
|
__func__, get_channel_id(can),
|
|
mailbox);
|
|
|
|
if (frame && dev->candev.event_callback) {
|
|
dev->candev.event_callback(candev, CANDEV_EVENT_TX_CONFIRMATION,
|
|
(void *) frame);
|
|
}
|
|
}
|
|
|
|
static void tx_irq_handler(can_t *dev)
|
|
{
|
|
FDCAN_GlobalTypeDef *can = dev->conf->can;
|
|
int flags = dev->isr_flags.isr_tx;
|
|
|
|
//DEBUG("%s: FDCAN%u tx irq\n", __func__, get_channel_id(can));
|
|
|
|
if (can->IR & FDCAN_IR_TC) {
|
|
dev->isr_flags.isr_tx |= can->TXBTO;
|
|
can->IR |= FDCAN_IR_TC;
|
|
}
|
|
|
|
if (dev->candev.event_callback && flags != dev->isr_flags.isr_tx) {
|
|
dev->candev.event_callback(&(dev->candev), CANDEV_EVENT_ISR, NULL);
|
|
}
|
|
}
|
|
|
|
static void tx_isr(can_t *dev)
|
|
{
|
|
unsigned int irq;
|
|
|
|
irq = irq_disable();
|
|
if (dev->isr_flags.isr_tx & 1) {
|
|
dev->isr_flags.isr_tx &= ~1;
|
|
irq_restore(irq);
|
|
tx_conf(dev, 0);
|
|
}
|
|
else {
|
|
irq_restore(irq);
|
|
}
|
|
|
|
irq = irq_disable();
|
|
if (dev->isr_flags.isr_tx & 2) {
|
|
dev->isr_flags.isr_tx &= ~2;
|
|
irq_restore(irq);
|
|
tx_conf(dev, 1);
|
|
}
|
|
else {
|
|
irq_restore(irq);
|
|
}
|
|
|
|
irq = irq_disable();
|
|
if (dev->isr_flags.isr_tx & 4) {
|
|
dev->isr_flags.isr_tx &= ~4;
|
|
irq_restore(irq);
|
|
tx_conf(dev, 2);
|
|
}
|
|
else {
|
|
irq_restore(irq);
|
|
}
|
|
}
|
|
|
|
static void rx_new_message_irq_handler(can_t *dev, uint8_t message_ram_rx_fifo)
|
|
{
|
|
candev_t *candev = (candev_t *) dev;
|
|
FDCAN_GlobalTypeDef *can = dev->conf->can;
|
|
|
|
DEBUG("%s: FDCAN%u rx new message irq\n", __func__, get_channel_id(can));
|
|
|
|
if (!dev->rx_mailbox.is_full) {
|
|
int i = dev->rx_mailbox.write_idx;
|
|
read_frame(dev, &(dev->rx_mailbox.frame[i]), message_ram_rx_fifo);
|
|
|
|
if (!dev->isr_flags.isr_rx) {
|
|
dev->isr_flags.isr_rx |= message_ram_rx_fifo + 1;
|
|
if (dev->candev.event_callback) {
|
|
dev->candev.event_callback(candev, CANDEV_EVENT_ISR, NULL);
|
|
}
|
|
}
|
|
|
|
dev->rx_mailbox.write_idx++;
|
|
if (dev->rx_mailbox.write_idx == FDCAN_STM32_RX_MAILBOXES) {
|
|
dev->rx_mailbox.write_idx = 0;
|
|
}
|
|
if (dev->rx_mailbox.write_idx == dev->rx_mailbox.read_idx) {
|
|
dev->rx_mailbox.is_full = 1;
|
|
}
|
|
}
|
|
else {
|
|
DEBUG("%s: FDCAN%u RX fifo is full!\n", __func__, get_channel_id(can));
|
|
if (dev->candev.event_callback) {
|
|
dev->candev.event_callback(candev, CANDEV_EVENT_RX_ERROR, NULL);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void rx_isr(can_t *dev)
|
|
{
|
|
DEBUG("_rx_isr: device=%p\n", (void *)dev);
|
|
|
|
while (dev->rx_mailbox.is_full || dev->rx_mailbox.read_idx != dev->rx_mailbox.write_idx) {
|
|
int i = dev->rx_mailbox.read_idx;
|
|
|
|
if (dev->candev.event_callback) {
|
|
dev->candev.event_callback(&dev->candev,
|
|
CANDEV_EVENT_RX_INDICATION,
|
|
&dev->rx_mailbox.frame[i]);
|
|
}
|
|
|
|
dev->rx_mailbox.read_idx++;
|
|
if (dev->rx_mailbox.read_idx == FDCAN_STM32_RX_MAILBOXES) {
|
|
dev->rx_mailbox.read_idx = 0;
|
|
}
|
|
|
|
dev->rx_mailbox.is_full = 0;
|
|
}
|
|
}
|
|
|
|
static void rx_irq_handler(can_t *dev)
|
|
{
|
|
FDCAN_GlobalTypeDef *can = dev->conf->can;
|
|
candev_t *candev = (candev_t *) dev;
|
|
|
|
DEBUG("%s: FDCAN%u rx irq\n", __func__, get_channel_id(can));
|
|
|
|
/* FIFO 0 */
|
|
if ((can->IR & FDCAN_IR_RF0L) == FDCAN_IR_RF0L) {
|
|
DEBUG("%s: FDCAN%u RF0L: Rx FIFO 0 message lost\n", __func__, get_channel_id(can));
|
|
can->IR |= FDCAN_IR_RF0L;
|
|
if (dev->candev.event_callback) {
|
|
dev->candev.event_callback(candev, CANDEV_EVENT_RX_ERROR, NULL);
|
|
}
|
|
}
|
|
if ((can->IR & FDCAN_IR_RF0F) == FDCAN_IR_RF0F) {
|
|
DEBUG("%s: FDCAN%u RF0F: Rx FIFO 0 full\n", __func__, get_channel_id(can));
|
|
can->IR |= FDCAN_IR_RF0F;
|
|
if (dev->candev.event_callback) {
|
|
dev->candev.event_callback(candev, CANDEV_EVENT_RX_ERROR, NULL);
|
|
}
|
|
}
|
|
if ((can->IR & FDCAN_IR_RF0N) == FDCAN_IR_RF0N) {
|
|
DEBUG("%s: FDCAN%u RF0N: Rx FIFO 0 new message\n", __func__, get_channel_id(can));
|
|
can->IR |= FDCAN_IR_RF0N;
|
|
rx_new_message_irq_handler(dev, 0);
|
|
}
|
|
|
|
/* FIFO 1 */
|
|
if ((can->IR & FDCAN_IR_RF1L) == FDCAN_IR_RF1L) {
|
|
DEBUG("%s: FDCAN%u RF1L: Rx FIFO 1 message lost\n", __func__, get_channel_id(can));
|
|
can->IR |= FDCAN_IR_RF0L;
|
|
if (dev->candev.event_callback) {
|
|
dev->candev.event_callback(candev, CANDEV_EVENT_RX_ERROR, NULL);
|
|
}
|
|
}
|
|
if ((can->IR & FDCAN_IR_RF1F) == FDCAN_IR_RF1F) {
|
|
DEBUG("%s: FDCAN%u RF1F: Rx FIFO 1 full\n", __func__, get_channel_id(can));
|
|
can->IR |= FDCAN_IR_RF1F;
|
|
if (dev->candev.event_callback) {
|
|
dev->candev.event_callback(candev, CANDEV_EVENT_RX_ERROR, NULL);
|
|
}
|
|
}
|
|
if ((can->IR & FDCAN_IR_RF1N) == FDCAN_IR_RF1N) {
|
|
DEBUG("%s: FDCAN%u RF1N: Rx FIFO 1 new message\n", __func__, get_channel_id(can));
|
|
can->IR |= FDCAN_IR_RF1N;
|
|
rx_new_message_irq_handler(dev, 1);
|
|
}
|
|
}
|
|
|
|
static void irq_handler(can_t *dev)
|
|
{
|
|
FDCAN_GlobalTypeDef *can = dev->conf->can;
|
|
|
|
DEBUG("%s: FDCAN%u Got interrupts, can->IR = %lx\n", __func__,
|
|
get_channel_id(can), dev->conf->can->IR);
|
|
|
|
if (dev->conf->can->IR & 0x3F) {
|
|
rx_irq_handler(dev);
|
|
}
|
|
if (dev->conf->can->IR & 0xFC0) {
|
|
tx_irq_handler(dev);
|
|
}
|
|
if (can->IR & FDCAN_IR_PEA) {
|
|
DEBUG("%s: FDCAN%u PSR.LEC = %lx\n", __func__, get_channel_id(can),
|
|
dev->conf->can->PSR & FDCAN_PSR_LEC);
|
|
DEBUG("%s: FDCAN%u ECR = %lx\n", __func__, get_channel_id(can),
|
|
dev->conf->can->ECR);
|
|
can->IR |= FDCAN_IR_PEA;
|
|
}
|
|
if (can->IR & FDCAN_IR_PED) {
|
|
DEBUG("%s: FDCAN%u PSR.DLEC = %lx\n", __func__, get_channel_id(can),
|
|
dev->conf->can->PSR & FDCAN_PSR_DLEC);
|
|
DEBUG("%s: FDCAN%u ECR = %lx\n", __func__, get_channel_id(can), dev->conf->can->ECR);
|
|
can->IR |= FDCAN_IR_PED;
|
|
}
|
|
|
|
if (dev->conf->can->IR) {
|
|
DEBUG("%s: FDCAN%u Unhandled interrupts, can->IR = %lx\n", __func__,
|
|
get_channel_id(can), dev->conf->can->IR);
|
|
can->IR |= can->IR;
|
|
}
|
|
}
|
|
|
|
void ISR_FDCAN1_IT0(void)
|
|
{
|
|
irq_handler(_can[0]);
|
|
|
|
cortexm_isr_end();
|
|
}
|
|
|
|
void ISR_FDCAN1_IT1(void)
|
|
{
|
|
irq_handler(_can[0]);
|
|
|
|
cortexm_isr_end();
|
|
}
|
|
|
|
#if defined(FDCAN2)
|
|
void ISR_FDCAN2_IT0(void)
|
|
{
|
|
irq_handler(_can[1]);
|
|
|
|
cortexm_isr_end();
|
|
}
|
|
|
|
void ISR_FDCAN2_IT1(void)
|
|
{
|
|
irq_handler(_can[1]);
|
|
|
|
cortexm_isr_end();
|
|
}
|
|
#endif /* FDCAN2 */
|
|
|
|
#if defined(FDCAN3)
|
|
void ISR_FDCAN3_IT0(void)
|
|
{
|
|
irq_handler(_can[2]);
|
|
|
|
cortexm_isr_end();
|
|
}
|
|
|
|
void ISR_FDCAN3_IT1(void)
|
|
{
|
|
irq_handler(_can[2]);
|
|
|
|
cortexm_isr_end();
|
|
}
|
|
#endif /* FDCAN3 */
|